[
  {
    "path": ".editorconfig",
    "content": "# EditorConfig for Spring Security\n# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.adoc#mind-the-whitespace\n\nroot = true\n\n[*]\nend_of_line = lf\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nmax_line_length = 120\n\n[*.{java,xml}]\nindent_style = tab\nindent_size = 4\ncharset = utf-8\ncontinuation_indent_size = 8\n\nij_smart_tabs = false\nij_java_align_multiline_parameters = false\n\n[*.gradle]\nindent_style = tab\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Normalize line endings to auto.\n* text auto\n\n# Ensure that line endings for DOS batch files are not modified.\n*.bat -text\n\n# Ensure the following are treated as binary.\n*.cer      binary\n*.graffle  binary\n*.jar      binary\n*.jpeg     binary\n*.jpg      binary\n*.keystore binary\n*.odg      binary\n*.otg      binary\n*.png      binary\n*.hsx      binary\n*.serialized binary\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.md",
    "content": "---\nname: Bug\nabout: Create a bug report to help us improve\ntitle: ''\nlabels: 'status: waiting-for-triage, type: bug'\nassignees: ''\n\n---\n\n<!--\nDo NOT report Security Vulnerabilities here. Please use https://github.com/spring-projects/spring-security/security/policy\n-->\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior.\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Sample**\n\nA link to a GitHub repository with a [minimal, reproducible sample](https://stackoverflow.com/help/minimal-reproducible-example).\n\nReports that include a sample will take priority over reports that do not.\nAt times, we may require a sample, so it is good to try and include a sample up front.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Community Support\n    url: https://stackoverflow.com/questions/tagged/spring-security\n    about: Please ask and answer questions on StackOverflow with the tag `spring-security`.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/enhancement.md",
    "content": "---\nname: Enhancement\nabout: Suggest an enhancement for this project\ntitle: ''\nlabels: 'status: waiting-for-triage, type: enhancement'\nassignees: ''\n\n---\n\n**Expected Behavior**\n\n<!--- Tell us how it should work -->\n\n**Current Behavior**\n\n<!--- Explain the difference from current behavior -->\n\n**Context**\n\n<!--- \nHow has this issue affected you?\nWhat are you trying to accomplish?\nWhat other alternatives have you considered?\nAre you aware of any workarounds?\n-->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--\nFor Security Vulnerabilities, please use https://spring.io/security-policy\n-->\n\n### Summary\n\n<!-- \nPlease provide a high level summary of the issue you are having\n-->\n\n### Actual Behavior\n\n<!-- \nPlease describe step by step the behavior you are observing\n-->\n\n### Expected Behavior\n\n<!--\nPlease describe step by step the behavior you expect\n-->\n\n### Configuration\n\n<!--\nPlease provide any configuration you have.\n-->\n\n### Version\n\n<!--\nPlease describe what version you are using. Does the problem occur in other versions?\n-->\n\n### Sample\n\n<!--\nProviding a complete sample (i.e. link to a github repository) will give this issue higher\npriority than issues that do not have a complete sample\n-->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nFor Security Vulnerabilities, please use https://pivotal.io/security#reporting\n-->\n\n<!--\nBefore creating new features, we recommend creating an issue to discuss the feature. This ensures that everyone is on the same page before extensive work is done.\n\nThanks for contributing to Spring Security. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with gh-).\n-->\n"
  },
  {
    "path": ".github/dco.yml",
    "content": "require:\n  members: false\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nregistries:\n  shibboleth:\n    type: maven-repository\n    url: https://build.shibboleth.net/maven/releases\nupdates:\n  # 6.5.x\n  - package-ecosystem: gradle\n    target-branch: 6.5.x\n    directory: /\n    schedule:\n      interval: daily\n      time: '03:00'\n      timezone: Etc/UTC\n    labels:\n      - 'type: dependency-upgrade'\n    registries:\n      - shibboleth\n    ignore:\n      - dependency-name: com.nimbusds:nimbus-jose-jwt\n      - dependency-name: org.python:jython\n      - dependency-name: org.apache.directory.server:*\n      - dependency-name: org.apache.directory.shared:*\n      - dependency-name: org.junit:junit-bom\n        update-types:\n          - version-update:semver-major\n      - dependency-name: org.mockito:mockito-bom\n        update-types:\n          - version-update:semver-major\n      - dependency-name: '*'\n        update-types:\n          - version-update:semver-major\n          - version-update:semver-minor\n  - package-ecosystem: npm\n    target-branch: 6.5.x\n    directory: /docs\n    schedule:\n      interval: weekly\n    labels:\n      - 'type: task'\n      - 'type: dependency-upgrade'\n      - 'in: build'\n  - package-ecosystem: github-actions\n    target-branch: 6.5.x\n    directory: /\n    schedule:\n      interval: weekly\n    labels:\n      - 'type: task'\n      - 'type: dependency-upgrade'\n      - 'in: build'\n\n  # 7.0.x\n  - package-ecosystem: gradle\n    target-branch: 7.0.x\n    directory: /\n    schedule:\n      interval: daily\n      time: '03:00'\n      timezone: Etc/UTC\n    labels:\n      - 'type: dependency-upgrade'\n    registries:\n      - shibboleth\n    ignore:\n      - dependency-name: com.nimbusds:nimbus-jose-jwt\n      - dependency-name: io.spring.nullability:*\n      - dependency-name: org.python:jython\n      - dependency-name: org.apache.directory.server:*\n      - dependency-name: org.apache.directory.shared:*\n      - dependency-name: org.junit:junit-bom\n        update-types:\n          - version-update:semver-major\n      - dependency-name: org.mockito:mockito-bom\n        update-types:\n          - version-update:semver-major\n      - dependency-name: com.gradle.enterprise\n        update-types:\n          - version-update:semver-major\n          - version-update:semver-minor\n      - dependency-name: '*'\n        update-types:\n          - version-update:semver-major\n          - version-update:semver-minor\n  - package-ecosystem: npm\n    target-branch: 7.0.x\n    directory: /docs\n    schedule:\n      interval: weekly\n    labels:\n      - 'type: task'\n      - 'type: dependency-upgrade'\n      - 'in: build'\n  - package-ecosystem: github-actions\n    target-branch: 7.0.x\n    directory: /\n    schedule:\n      interval: weekly\n    labels:\n      - 'type: task'\n      - 'type: dependency-upgrade'\n      - 'in: build'\n\n  # main\n  - package-ecosystem: gradle\n    target-branch: main\n    directory: /\n    schedule:\n      interval: daily\n      time: '03:00'\n      timezone: Etc/UTC\n    labels:\n      - 'type: dependency-upgrade'\n    registries:\n      - shibboleth\n    ignore:\n      - dependency-name: com.nimbusds:nimbus-jose-jwt\n      - dependency-name: org.python:jython\n      - dependency-name: org.apache.directory.server:*\n      - dependency-name: org.apache.directory.shared:*\n      - dependency-name: org.junit:junit-bom\n        update-types:\n          - version-update:semver-major\n      - dependency-name: org.mockito:mockito-bom\n        update-types:\n          - version-update:semver-major\n      - dependency-name: com.gradle.enterprise\n        update-types:\n          - version-update:semver-major\n          - version-update:semver-minor\n      - dependency-name: '*'\n        update-types:\n          - version-update:semver-major\n  - package-ecosystem: npm\n    target-branch: main\n    directory: /docs\n    schedule:\n      interval: weekly\n    labels:\n      - 'type: task'\n      - 'type: dependency-upgrade'\n      - 'in: build'\n  - package-ecosystem: github-actions\n    target-branch: main\n    directory: /\n    schedule:\n      interval: weekly\n    labels:\n      - 'type: task'\n      - 'type: dependency-upgrade'\n      - 'in: build'\n\n  # docs-build\n  - package-ecosystem: gradle\n    target-branch: docs-build\n    directory: /\n    schedule:\n      interval: daily\n      time: '03:00'\n      timezone: Etc/UTC\n    labels:\n      - 'type: dependency-upgrade'\n    registries:\n      - shibboleth\n    ignore:\n      - dependency-name: com.nimbusds:nimbus-jose-jwt\n      - dependency-name: org.python:jython\n      - dependency-name: org.apache.directory.server:*\n      - dependency-name: org.apache.directory.shared:*\n      - dependency-name: org.junit:junit-bom\n        update-types:\n          - version-update:semver-major\n      - dependency-name: org.mockito:mockito-bom\n        update-types:\n          - version-update:semver-major\n      - dependency-name: com.gradle.enterprise\n        update-types:\n          - version-update:semver-major\n          - version-update:semver-minor\n      - dependency-name: '*'\n        update-types:\n          - version-update:semver-major\n  - package-ecosystem: npm\n    target-branch: docs-build\n    directory: /\n    schedule:\n      interval: weekly\n    labels:\n      - 'type: task'\n      - 'type: dependency-upgrade'\n      - 'in: build'\n  - package-ecosystem: github-actions\n    target-branch: docs-build\n    directory: /\n    schedule:\n      interval: weekly\n    labels:\n      - 'type: task'\n      - 'type: dependency-upgrade'\n      - 'in: build'\n"
  },
  {
    "path": ".github/workflows/auto-merge-dependabot.yml",
    "content": "name: Merge Dependabot PR\n\non:\n  pull_request:\n    branches:\n      - main\n      - '*.x'\n\nrun-name: Merge Dependabot PR ${{ github.ref_name }}\n\njobs:\n  merge-dependabot-pr:\n    permissions: write-all\n    uses: spring-io/spring-github-workflows/.github/workflows/spring-merge-dependabot-pr.yml@v7\n    with:\n      mergeArguments: --auto --rebase"
  },
  {
    "path": ".github/workflows/check-snapshots.yml",
    "content": "name: CI\n\non:\n  schedule:\n    - cron: '0 10 * * *' # Once per day at 10am UTC\n  workflow_dispatch: # Manual trigger\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n\npermissions:\n  contents: read\n\njobs:\n  snapshot-test:\n    name: Test Against Snapshots\n    uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n    strategy:\n      matrix:\n        include:\n          - java-version: 25\n            toolchain: 25\n    with:\n      java-version: ${{ matrix.java-version }}\n      test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace\n    secrets: inherit\n  send-notification:\n    name: Send Notification\n    needs: [ snapshot-test ]\n    if: ${{ !success() }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Send Notification\n        uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n        with:\n          webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}\n"
  },
  {
    "path": ".github/workflows/clean_build_artifacts.yml",
    "content": "name: Clean build artifacts\non:\n  schedule:\n    - cron: '0 10 * * *' # Once per day at 10am UTC\n\npermissions:\n  contents: read\n\njobs:\n  main:\n    runs-on: ubuntu-latest\n    if: ${{ github.repository == 'spring-projects/spring-security' }}\n    permissions:\n      contents: none\n    steps:\n      - name: Delete artifacts in cron job\n        env:\n          GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}\n        run: |\n          echo \"Running clean build artifacts logic\"\n          output=$(curl -X GET -H \"Authorization: token $GH_ACTIONS_REPO_TOKEN\" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts | grep '\"id\"' | cut -d : -f2 | sed 's/,*$//g')\n          echo Output is $output\n          for id in $output; do curl -X DELETE -H \"Authorization: token $GH_ACTIONS_REPO_TOKEN\" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts/$id; done;\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL Advanced\"\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n  schedule:\n    # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule\n    - cron: '0 5 * * *'\npermissions: read-all\njobs:\n  codeql-analysis-call:\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n    uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@1\n"
  },
  {
    "path": ".github/workflows/continuous-integration-workflow.yml",
    "content": "name: CI\n\non:\n  push:\n    branches-ignore:\n      - \"dependabot/**\"\n  schedule:\n    - cron: '0 10 * * *' # Once per day at 10am UTC\n  workflow_dispatch: # Manual trigger\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    name: Build\n    uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n    strategy:\n      matrix:\n        os: [ ubuntu-latest, windows-latest ]\n        jdk: [ 25 ]\n    with:\n      runs-on: ${{ matrix.os }}\n      java-version: ${{ matrix.jdk }}\n      distribution: temurin\n    secrets: inherit\n  deploy-artifacts:\n    name: Deploy Artifacts\n    needs: [ build]\n    uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n    with:\n      should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}\n      default-publish-milestones-central: true\n      java-version: 25\n    secrets: inherit\n  deploy-schema:\n    name: Deploy Schema\n    needs: [ build ]\n    uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n    with:\n      should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}\n      java-version: 25\n    secrets: inherit\n  perform-release:\n    name: Perform Release\n    needs: [ deploy-artifacts, deploy-schema ]\n    uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n    with:\n      should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }}\n      project-version: ${{ needs.deploy-artifacts.outputs.project-version }}\n      milestone-repo-url: https://repo1.maven.org/maven2\n      release-repo-url: https://repo1.maven.org/maven2\n      artifact-path: org/springframework/security/spring-security-core\n      slack-announcing-id: spring-security-announcing\n      java-version: 25\n    secrets: inherit\n  send-notification:\n    name: Send Notification\n    needs: [ perform-release ]\n    if: ${{ !success() }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Send Notification\n        uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n        with:\n          webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}\n"
  },
  {
    "path": ".github/workflows/defer-issues.yml",
    "content": "name: Defer Issues\n\non:\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  defer-issues:\n    name: Defer Issues\n    runs-on: ubuntu-latest\n    if: github.repository_owner == 'spring-projects'\n    permissions:\n      issues: write\n    steps:\n    - name: Checkout\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n    - name: Compute Version\n      id: compute-version\n      uses: spring-io/spring-release-actions/compute-version@0.0.3\n    - name: Get Today's Release Version\n      id: todays-release\n      uses: spring-io/spring-release-actions/get-todays-release-version@0.0.3\n      with:\n        snapshot-version: ${{ steps.compute-version.outputs.version }}\n        milestone-repository: ${{ github.repository }}\n        milestone-token: ${{ secrets.GITHUB_TOKEN }}\n    - name: Compute Next Version\n      id: next-version\n      uses: spring-io/spring-release-actions/compute-next-version@0.0.3\n      with:\n        version: ${{ steps.todays-release.outputs.release-version }}\n    - name: Schedule Next Milestone\n      uses: spring-io/spring-release-actions/schedule-milestone@0.0.3\n      with:\n        version: ${{ steps.next-version.outputs.version }}\n        version-date: ${{ steps.next-version.outputs.version-date }}\n        repository: ${{ github.repository }}\n        token: ${{ secrets.GITHUB_TOKEN }}\n    - name: Move Open Issues to Next Milestone\n      env:\n        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        CURRENT_MILESTONE: ${{ steps.todays-release.outputs.release-version }}\n        NEXT_MILESTONE: ${{ steps.next-version.outputs.version }}\n      run: |\n        current_milestone_number=$(gh api repos/${{ github.repository }}/milestones \\\n          --jq \".[] | select(.title == \\\"$CURRENT_MILESTONE\\\") | .number\")\n        if [ -z \"$current_milestone_number\" ]; then\n          echo \"No milestone found for $CURRENT_MILESTONE\"\n          exit 0\n        fi\n        next_milestone_number=$(gh api repos/${{ github.repository }}/milestones \\\n          --jq \".[] | select(.title == \\\"$NEXT_MILESTONE\\\") | .number\")\n        if [ -z \"$next_milestone_number\" ]; then\n          echo \"No milestone found for $NEXT_MILESTONE\"\n          exit 1\n        fi\n        echo \"Moving open issues from milestone '$CURRENT_MILESTONE' (#$current_milestone_number) to '$NEXT_MILESTONE' (#$next_milestone_number)\"\n        page=1\n        while true; do\n          issues=$(gh api \"repos/${{ github.repository }}/issues?milestone=$current_milestone_number&state=open&per_page=100&page=$page\" \\\n            --jq '.[].number')\n          if [ -z \"$issues\" ]; then\n            break\n          fi\n          for issue in $issues; do\n            echo \"Moving issue/PR #$issue to milestone $NEXT_MILESTONE\"\n            gh api repos/${{ github.repository }}/issues/$issue \\\n              --method PATCH \\\n              --field milestone=$next_milestone_number \\\n              --silent\n          done\n          page=$((page + 1))\n        done\n        echo \"Done.\"\n"
  },
  {
    "path": ".github/workflows/deploy-docs.yml",
    "content": "name: Deploy Docs\non:\n  push:\n    branches-ignore:\n      - \"gh-pages\"\n      - \"dependabot/**\"\n    tags: '**'\n  repository_dispatch:\n    types: request-build-reference # legacy\n  #schedule:\n  #- cron: '0 10 * * *' # Once per day at 10am UTC\n  workflow_dispatch:\npermissions: read-all\njobs:\n  build:\n    runs-on: ubuntu-latest\n    if: github.repository_owner == 'spring-projects'\n    steps:\n    - name: Checkout\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        ref: docs-build\n        fetch-depth: 1\n    - name: Dispatch (partial build)\n      if: github.ref_type == 'branch'\n      env:\n        GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}\n      run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}\n    - name: Dispatch (full build)\n      if: github.ref_type == 'tag'\n      env:\n        GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}\n      run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)\n"
  },
  {
    "path": ".github/workflows/finalize-release.yml",
    "content": "name: Finalize Release\n\non:\n  workflow_dispatch: # Manual trigger\n    inputs:\n      version:\n        description: The Spring Security release to finalize (e.g. 7.0.0-RC2)\n        required: true\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n\npermissions:\n  contents: read\n\njobs:\n  perform-release:\n    name: Perform Release\n    uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n    with:\n      should-perform-release: true\n      project-version: ${{ inputs.version }}\n      milestone-repo-url: https://repo1.maven.org/maven2\n      release-repo-url: https://repo1.maven.org/maven2\n      artifact-path: org/springframework/security/spring-security-core\n      slack-announcing-id: spring-security-announcing\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/gradle-wrapper-upgrade-execution.yml",
    "content": "name: Execute Gradle Wrapper Upgrade\n\non:\n  schedule:\n    - cron:  '0 2 * * *' # 2am UTC\n  workflow_dispatch:\npermissions:\n  pull-requests: write\njobs:\n  upgrade_wrapper:\n    name: Execution\n    if: ${{ github.repository == 'spring-projects/spring-security' }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Set up Git configuration\n        env:\n          TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          git config --global url.\"https://unused-username:${TOKEN}@github.com/\".insteadOf \"https://github.com/\"\n          git config --global user.name 'github-actions[bot]'\n          git config --global user.email 'github-actions[bot]@users.noreply.github.com'\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - name: Set up JDK 25\n        uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n        with:\n          java-version: '25'\n          distribution: 'temurin'\n      - name: Set up Gradle\n        uses: gradle/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1\n      - name: Upgrade Wrappers\n        run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false\n        env:\n          WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/milestone-spring-releasetrain.yml",
    "content": "name: Check Milestone\non:\n  milestone:\n    types: [created, opened, edited]\nenv:\n  DUE_ON: ${{ github.event.milestone.due_on }}\n  TITLE: ${{ github.event.milestone.title }}\npermissions:\n  contents: read\njobs:\n  spring-releasetrain-checks:    \n    name: Check DueOn is on a Release Date\n    runs-on: ubuntu-latest\n    if: ${{ github.repository == 'spring-projects/spring-security' }}\n    permissions:\n      contents: none\n    steps:\n    - name: Print Milestone Being Checked\n      run: echo \"Validating DueOn '$DUE_ON' for milestone '$TITLE'\"\n    - name: Validate DueOn\n      if: env.DUE_ON != ''\n      run: |\n        export TOOL_VERSION=0.1.1\n        wget \"https://repo.maven.apache.org/maven2/io/spring/releasetrain/spring-release-train-tools/$TOOL_VERSION/spring-release-train-tools-$TOOL_VERSION.jar\"\n        java -cp \"spring-release-train-tools-$TOOL_VERSION.jar\" io.spring.releasetrain.CheckMilestoneDueOnMain --dueOn \"$DUE_ON\" --expectedDayOfWeek MONDAY --expectedMondayCount 3\n  send-notification:\n    name: Send Notification\n    needs: [ spring-releasetrain-checks ]\n    if: ${{ failure() || cancelled() }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Send Notification\n        uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n        with:\n          webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}\n"
  },
  {
    "path": ".github/workflows/pr-build-workflow.yml",
    "content": "name: PR Build\n\non: pull_request\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    if: ${{ github.repository == 'spring-projects/spring-security' }}\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - name: Set up gradle\n        uses: spring-io/spring-gradle-build-action@efc55f07f4dfa22f2afd97f9ea1be4212eeed737 # v2.0.5\n        with:\n          java-version: '25'\n          distribution: 'temurin'\n      - name: Build with Gradle\n        run: ./gradlew clean build -PskipCheckExpectedBranchVersion --continue --scan\n  generate-docs:\n    name: Generate Docs\n    runs-on: ubuntu-latest\n    if: ${{ github.repository == 'spring-projects/spring-security' }}\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      - name: Set up gradle\n        uses: spring-io/spring-gradle-build-action@efc55f07f4dfa22f2afd97f9ea1be4212eeed737 # v2.0.5\n        with:\n          java-version: '25'\n          distribution: 'temurin'\n      - name: Run Antora\n        run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora\n      - name: Upload Docs\n        id: upload\n        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0\n        with:\n          name: docs\n          path: docs/build/site\n          overwrite: true\n  send-notification:\n    name: Send Notification\n    needs: [ build, generate-docs ]\n    if: ${{ failure() && github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Send Notification\n        uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n        with:\n          webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}\n"
  },
  {
    "path": ".github/workflows/release-scheduler.yml",
    "content": "name: Release Scheduler\non:\n  schedule:\n  - cron: '15 15 * * MON' # Every Monday at 3:15pm UTC\n  workflow_dispatch:\npermissions: read-all\njobs:\n  dispatch_scheduled_releases:\n    name: Dispatch scheduled releases\n    if: github.repository_owner == 'spring-projects'\n    strategy:\n      matrix:\n        # List of active maintenance branches.\n        branch: [ main, 7.0.x, 6.5.x, 6.4.x, 6.3.x ]\n    runs-on: ubuntu-latest\n    steps:\n    - name: Checkout\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 1\n    - name: Dispatch\n      env:\n        GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}\n      run: gh workflow run update-scheduled-release-version.yml -r ${{ matrix.branch }}\n"
  },
  {
    "path": ".github/workflows/update-antora-ui-spring.yml",
    "content": "name: Update Antora UI Spring\n\non:\n  schedule:\n    - cron: '0 10 * * *' # Once per day at 10am UTC\n  workflow_dispatch:\n\npermissions:\n  pull-requests: write\n  issues: write\n  contents: write\n\njobs:\n  update-antora-ui-spring:\n    name: Update on Supported Branches\n    if: ${{ github.repository == 'spring-projects/spring-security' }}\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        branch: [ '6.5.x', '7.0.x', 'main' ]\n    steps:\n      - uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf\n        name: Update\n        with:\n          docs-branch: ${{ matrix.branch }}\n          token: ${{ secrets.GITHUB_TOKEN }}\n          antora-file-path: 'docs/antora-playbook.yml'\n  update-antora-ui-spring-docs-build:\n    name: Update on docs-build\n    if: ${{ github.repository == 'spring-projects/spring-security' }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf\n        name: Update\n        with:\n          docs-branch: 'docs-build'\n          token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/update-scheduled-release-version.yml",
    "content": "name: Update Scheduled Release Version\n\non:\n  workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main.\n\npermissions:\n  contents: read\n\njobs:\n  update-scheduled-release-version:\n    name: Update Scheduled Release Version\n    uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n    secrets: inherit\n  send-notification:\n    name: Send Notification\n    needs: [ update-scheduled-release-version ]\n    if: ${{ failure() || cancelled() }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Send Notification\n        uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15\n        with:\n          webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}"
  },
  {
    "path": ".gitignore",
    "content": "classes/\ntarget/\n*/src/*/java/META-INF\n*/src/META-INF/\n*/src/*/java/META-INF/\n.classpath\n.springBeans\n.project\n.DS_Store\n.settings/\n.idea/*\nout/\nbin/\nintellij/\nbuild/\n*.log\n*.log.*\n*.iml\n*.ipr\n*.iws\n.gradle/\natlassian-ide-plugin.xml\n!etc/eclipse/.checkstyle\n.checkstyle\ns101plugin.state\n.attach_pid*\n.~lock.*#\n.kotlin/\n\n!.idea/checkstyle-idea.xml\n!.idea/externalDependencies.xml\n\nnode_modules\n"
  },
  {
    "path": ".idea/checkstyle-idea.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CheckStyle-IDEA\">\n    <option name=\"configuration\">\n      <map>\n        <entry key=\"checkstyle-version\" value=\"8.14\" />\n        <entry key=\"copy-libs\" value=\"false\" />\n        <entry key=\"location-0\" value=\"BUNDLED:(bundled):Sun Checks\" />\n        <entry key=\"location-1\" value=\"BUNDLED:(bundled):Google Checks\" />\n        <entry key=\"scan-before-checkin\" value=\"false\" />\n        <entry key=\"scanscope\" value=\"JavaOnlyWithTests\" />\n        <entry key=\"suppress-errors\" value=\"false\" />\n      </map>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/externalDependencies.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ExternalDependencies\">\n    <plugin id=\"CheckStyle-IDEA\" />\n    <plugin id=\"org.jetbrains.plugins.gradle\" />\n  </component>\n</project>"
  },
  {
    "path": ".sdkmanrc",
    "content": "# Use sdkman to run \"sdk env\" to initialize with correct JDK version\n# Enable auto-env through the sdkman_auto_env config\n# See https://sdkman.io/usage#config\n# A summary is to add the following to ~/.sdkman/etc/config\n# sdkman_auto_env=true\njava=25-librca\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"java.gradle.buildServer.enabled\": \"off\"\n}\n"
  },
  {
    "path": "CONTRIBUTING.adoc",
    "content": "= Contributing to Spring Security\n\nFirst off, thank you for taking the time to contribute! :+1: :tada:\n\n== Table of Contents\n\n* <<code-of-conduct>>\n* <<how-to-contribute>>\n* <<ask-questions>>\n* <<find-an-issue>>\n* <<create-an-issue>>\n* <<issue-lifecycle>>\n* <<submit-a-pull-request>>\n* <<build-from-source>>\n* <<code-style>>\n\n[[code-of-conduct]]\n== Code of Conduct\n\nThis project is governed by the https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[Spring code of conduct].\nBy participating you are expected to uphold this code.\nPlease report unacceptable behavior to spring-code-of-conduct@pivotal.io.\n\n[[how-to-contribute]]\n== How to Contribute\n\n[[ask-questions]]\n=== Ask Questions\n\nIf you have a question, check Stack Overflow using\nhttps://stackoverflow.com/questions/tagged/spring-security+or+spring-ldap+or+spring-authorization-server+or+spring-session?tab=Newest[this list of tags].\nFind an existing discussion, or start a new one if necessary.\n\nIf you believe there is an issue, search through https://github.com/spring-projects/spring-security/issues[existing issues] trying a few different ways to find discussions, past or current, that are related to the issue.\nReading those discussions helps you to learn about the issue, and helps us to make a decision.\n\n[[find-an-issue]]\n=== Find an Existing Issue\n\nThere are many issues in Spring Security with the labels https://github.com/spring-projects/spring-security/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+ideal-for-contribution%22[`ideal-for-contribution`] or https://github.com/spring-projects/spring-security/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+first-timers-only%22[`first-timers-only`] that are a great way to contribute to a discussion or <<submit-a-pull-request,to a PR>>.\nYou can volunteer by commenting on these tickets, and we will assign them to you.\n\n[[create-an-issue]]\n=== Create an Issue\n\nReporting an issue or making a feature request is a great way to contribute.\nYour feedback and the conversations that result from it provide a continuous flow of ideas.\nHowever, before creating a ticket, please take the time to <<ask-questions,ask and research>> first.\n\nIf you create an issue after a discussion on Stack Overflow, please provide a description in the issue instead of simply referring to Stack Overflow.\nThe issue tracker is an important place of record for design discussions and should be self-sufficient.\n\nOnce you're ready, create an issue on https://github.com/spring-projects/spring-security/issues[GitHub].\n\nMany issues are caused by subtle behavior, typos, and unintended configuration.\nCreating a https://stackoverflow.com/help/minimal-reproducible-example[Minimal Reproducible Example] (starting with https://start.spring.io for example) of the problem helps the team quickly triage your issue and get to the core of the problem.\n\nWe love contributors, and we may ask you to <<submit-a-pull-request,submit a PR with a fix>>.\n\n[[issue-lifecycle]]\n=== Issue Lifecycle\n\nWhen an issue is first created, it is flagged `waiting-for-triage` waiting for a team member to triage it.\nOnce the issue has been reviewed, the team may ask for further information if needed, and based on the findings, the issue is either assigned a target branch (or no branch if a feature) or is closed with a specific status.\nThe target branch is https://spring.io/projects/spring-security#support[the earliest supported branch] where <<choose-a-branch,the change will be applied>>.\n\nWhen a fix is ready, the issue is closed and may still be re-opened until the fix is released.\nAfter that the issue will typically no longer be reopened.\nIn rare cases if the issue was not at all fixed, the issue may be re-opened.\nIn most cases however any follow-up reports will need to be created as new issues with a fresh description.\n\n[[build-from-source]]\n=== Build from Source\n\nSee https://github.com/spring-projects/spring-security/tree/main#building-from-source[Build from Source] for instructions on how to check out, build, and import the Spring Security source code into your IDE.\n\n[[code-style]]\n=== Source Code Style\n\nThe wiki pages https://github.com/spring-projects/spring-framework/wiki/Code-Style[Code Style] and https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings[IntelliJ IDEA Editor Settings] define the source file coding standards we use along with some IDEA editor settings we customize.\n\nAdditionally, since Streams are https://github.com/spring-projects/spring-security/issues/7154[much slower] than `for` loops, please use them judiciously.\nThe team may ask you to change to a `for` loop if the given code is along a hot path.\n\nTo format the code as well as check the style, run `./gradlew format && ./gradlew check`.\n\n[[submit-a-pull-request]]\n=== Submit a Pull Request\n\nWe are excited for your pull request! :heart:\n\nPlease do your best to follow these steps.\nDon't worry if you don't get them all correct the first time, we will help you.\n\n1. [[sign-cla]] All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin.\nFor additional details, please refer to the blog post https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring[Hello DCO, Goodbye CLA: Simplifying Contributions to Spring].\n2. [[create-an-issue-list]] Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier to discuss with the team first to determine the right fix or enhancement.\nFor typos and straightforward bug fixes, starting with a pull request is encouraged.\nPlease include a description for context and motivation.\nNote that the team may close your pull request if it's not a fit for the project.\n3. [[choose-a-branch]] Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).\nIf there is no milestone, choose `main`.\nOnce merged, the fix will be forwarded-ported to applicable branches including `main`.\n4. [[create-a-local-branch]] Create a local branch\nIf this is for an issue, consider a branch name with the issue number, like `gh-22276`.\n5. [[write-tests]] Add documentation and JUnit Tests for your changes.\n6. [[update-copyright]] In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.\n7. [[add-since]] If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds\n8. [[change-rnc]] If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.\n9. [[format-code]] For each commit, build the code using `./gradlew format && ./gradlew check`.\nThis command ensures the code meets most of <<code-style,the style guide>>; a notable exception is import order.\n10. [[commit-atomically]] Choose the granularity of your commits consciously and squash commits that represent\nmultiple edits or corrections of the same logical change.\nSee https://git-scm.com/book/en/Git-Tools-Rewriting-History[Rewriting History section of Pro Git] for an overview of streamlining the commit history.\n11. [[format-commit-messages]] Format commit messages using 55 characters for the subject line, 72 characters per line\nfor the description, followed by the issue fixed, for example, `Closes gh-22276`.\nSee the https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines[Commit Guidelines section of Pro Git] for best practices around commit messages, and use `git log` to see some examples.\nFavor imperative tense over present tense (use \"Fix\" instead of \"Fixes\"); avoid past tense (use \"Fix\" instead of \"Fixed\").\n+\n[indent=0]\n----\nAddress NullPointerException\n\nCloses gh-22276\n----\n[[reference-issue]]\n1. If there is a prior issue, reference the GitHub issue number in the description of the pull request.\n+\n[indent=0]\n----\nCloses gh-22276\n----\n\nIf accepted, your contribution may be heavily modified as needed prior to merging.\nYou will likely retain author attribution for your Git commits granted that the bulk of your changes remain intact.\nYou may also be asked to rework the submission.\n\nIf asked to make corrections, simply push the changes against the same branch, and your pull request will be updated.\nIn other words, you do not need to create a new pull request when asked to make changes.\nWhen it is time to merge, you'll be asked to squash your commits.\n\n==== Participate in Reviews\n\nHelping to review pull requests is another great way to contribute.\nYour feedback can help to shape the implementation of new features.\nWhen reviewing pull requests, however, please refrain from approving or rejecting a PR unless you are a core committer for Spring Security.\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        https://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       https://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "README.adoc",
    "content": "image::https://badges.gitter.im/Join%20Chat.svg[Gitter,link=https://gitter.im/spring-projects/spring-security?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge]\n\nimage:https://github.com/spring-projects/spring-security/actions/workflows/continuous-integration-workflow.yml/badge.svg?branch=main[\"Build Status\", link=\"https://github.com/spring-projects/spring-security/actions/workflows/continuous-integration-workflow.yml\"]\n\nimage:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A[\"Revved up by Develocity\", link=\"https://ge.spring.io/scans?search.rootProjectNames=spring-security\"]\n\n= Spring Security\n\nSpring Security provides security services for the https://docs.spring.io[Spring IO Platform]. Spring Security 6.0 requires Spring 6.0 as\na minimum and also requires Java 17.\n\nFor a detailed list of features and access to the latest release, please visit https://spring.io/projects[Spring projects].\n\n== Code of Conduct\nPlease see our https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[code of conduct]\n\n== Downloading Artifacts\nSee https://docs.spring.io/spring-security/reference/getting-spring-security.html[Getting Spring Security] for how to obtain Spring Security.\n\n== Documentation\nBe sure to read the https://docs.spring.io/spring-security/reference/[Spring Security Reference].\nExtensive JavaDoc for the Spring Security code is also available in the https://docs.spring.io/spring-security/site/docs/current/api/[Spring Security API Documentation].\n\nYou may also want to check out https://docs.spring.io/spring-security/reference/whats-new.html[what's new in the latest release].\n\n== Quick Start\nSee https://docs.spring.io/spring-security/reference/servlet/getting-started.html[Hello Spring Security] to get started with a \"Hello, World\" application.\n\n== Building from Source\nSpring Security uses a https://gradle.org[Gradle]-based build system.\nIn the instructions below, https://vimeo.com/34436402[`./gradlew`] is invoked from the root of the source tree and serves as\na cross-platform, self-contained bootstrap mechanism for the build.\n\n=== Prerequisites\nhttps://docs.github.com/en/get-started/quickstart/set-up-git[Git] and the https://www.oracle.com/java/technologies/downloads/#java17[JDK17 build].\n\nBe sure that your `JAVA_HOME` environment variable points to the `jdk-17` folder extracted from the JDK download.\n\n=== Check out sources\n[indent=0]\n----\ngit clone git@github.com:spring-projects/spring-security.git\n----\n\n=== Install all `spring-*.jar` into your local Maven repository.\n\n[indent=0]\n----\n./gradlew publishToMavenLocal\n----\n\n=== Compile and test; build all JARs, distribution zips, and docs\n\n[indent=0]\n----\n./gradlew build\n----\n\nThe reference docs are not currently included in the distribution zip.\nYou can build the reference docs for this branch by running the following command:\n\n----\n./gradlew :spring-security-docs:antora\n----\n\nThat command publishes the docs site to the `_docs/build/site_` directory.\nThe https://github.com/spring-projects/spring-security/tree/docs-build[playbook branch] describes how to build the reference docs in detail.\n\nDiscover more commands with `./gradlew tasks`.\n\n=== IDE setup (IntelliJ)\n\nNo special steps are needed to open Spring Security in IntelliJ.\n\n=== IDE setup (Eclipse and VS Code)\n\nTo work in Eclipse or VS Code, first generate Eclipse metadata so you can import the project into Eclipse or VS Code:\n\n[indent=0]\n----\n./gradlew cleanEclipse eclipse\n----\n\nIf you have not built the project yet, run `./gradlew publishToMavenLocal` first so dependencies are resolved.\n\n*VS Code:* Open the repository root as a folder. The repository includes `.vscode/settings.json` which disables automatic Gradle import so that the generated Eclipse metadata (`.classpath`, `.project`) is used. Do not use the Gradle for Java extension to import the project.\n\n*Eclipse:* File → Import → General → Existing Projects into Workspace, then select the repository root.\n\nThe build uses a custom Eclipse plugin to work around Gradle dependency cycles that confuse IDE metadata generation. You may see Eclipse warnings about `xml-apis` from some test dependencies; those are excluded in the build and can be ignored.\n\n== Getting Support\nCheck out the https://stackoverflow.com/questions/tagged/spring-security[Spring Security tags on Stack Overflow].\nhttps://spring.io/support[Commercial support] is available too.\n\n== Contributing\nhttps://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request[Pull requests] are welcome; see the https://github.com/spring-projects/spring-security/blob/main/CONTRIBUTING.adoc[contributor guidelines] for details.\n\n== License\nSpring Security is Open Source software released under the\nhttps://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].\n"
  },
  {
    "path": "RELEASE.adoc",
    "content": "= Release Process\n\nThe release process for Spring Security is entirely automated via the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc[Spring Security Release Plugin] and https://github.com/spring-io/spring-security-release-tools/tree/main/.github/workflows[reusable workflows].\nThe following table outlines the steps that are taken by the automation.\n\nWARNING: The `5.8.x` branch does not have all of the improvements from the `6.x.x` branches. See \"Status (5.8.x)\" for which steps are still manual.\n\nIn case of a failure, you can follow the links below to read about each step, which includes instructions for performing the step manually if applicable.\nSee <<frequently-asked-questions,FAQ>> for troubleshooting tips.\n\n[cols=\"1,1,1\"]\n|===\n| Step | Status (5.8.x) | Status (6.0.x+)\n\n| <<update-dependencies>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<check-all-issues-are-closed>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<update-release-version>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<tag-release>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<push-release-commit>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<build-locally>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<update-release-notes-on-github>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<update-version-on-project-page>>\n| :x: manual\n| :white_check_mark: automated\n\n| <<close-create-milestone,Close milestone>>\n| :x: manual\n| :white_check_mark: automated\n\n| <<announce-release-on-slack>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<update-to-next-development-version>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<close-create-milestone,Create milestone>>\n| :white_check_mark: automated\n| :white_check_mark: automated\n\n| <<announce-release-on-other-channels>>\n| :x: manual\n| :x: manual\n|===\n\n[#update-dependencies]\n== Update dependencies\n\nDependency versions are managed in the file xref:./gradle/libs.versions.toml[libs.versions.toml] and are automatically updated by xref:./.github/dependabot.yml[dependabot].\n\n[#check-all-issues-are-closed]\n== Check all issues are closed\n\nThe first step of a release is to check if there are any open issues remaining in a milestone.\n\nNOTE: A scheduled release will not proceed if there are any open issues.\n\nTIP: If you need to prevent a release from occurring automatically, the easiest way to block a release is to add an unresolved issue to the milestone.\n\nThe https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#checkMilestoneHasNoOpenIssues[`checkMilestoneHasOpenIssues`] command will check if there are any open issues for the release.\nBefore running the command manually, replace the following values:\n\n* `<next-version>` - Replace with the title of the milestone you are releasing now (i.e. 5.5.0-RC1)\n* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `public_repo`. This is optional since you are unlikely to reach the rate limit for such a simple check.\n\n[source,bash]\n----\n./gradlew checkMilestoneHasOpenIssues -PnextVersion=<next-version> -PgitHubAccessToken=<github-personal-access-token>\n----\n\nAlternatively, you can manually check using the https://github.com/spring-projects/spring-security/milestones[milestones] page.\n\n[#update-release-version]\n== Update release version\n\nIf all issues for the release are <<check-all-issues-are-closed,closed>>, the version number is automatically updated using the milestone title.\nWhen performing this step manually, update the version number in `gradle.properties` for the release (for example `5.5.0`) and commit the change using the message \"Release x.y.z\".\n\n[#tag-release]\n== Tag release\n\nThe release will automatically be tagged using the milestone title.\nIt is not required to tag manually.\nHowever, you can perform this step manually by running the following command:\n\n[source,bash]\n----\ngit tag 5.5.0\n----\n\n[#push-release-commit]\n== Push release commit\n\nDuring a scheduled release, the release commit will automatically be pushed to trigger a build.\nIf performing this step manually, you can push the commit and tag and GitHub actions will build and deploy the artifacts with the following command:\n\n[source,bash]\n----\ngit push --atomic origin main 5.5.0\n----\n\nThe build will automatically wait for artifacts to be released to Maven Central.\nYou can get notified manually when uploading is complete by running the following:\n\n[source,bash]\n----\n./scripts/release/wait-for-done.sh 5.5.0\n----\n\n[#build-locally]\n== Build\n\nAll checks will automatically be performed by the build prior to uploading the artifacts to Maven Central.\nIf something goes wrong, you can run the build locally using:\n\n[source,bash]\n----\n./gradlew check\n----\n\n[#update-release-notes-on-github]\n== Update release notes on GitHub\n\nOnce the release has been uploaded to Maven Central, release notes will automatically be generated and a GitHub release will be created.\nTo do this manually, you can use the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#generateChangelog[`generateChangelog`] command to generate the release notes by replacing:\n\n* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)\n\n[source,bash]\n----\n./gradlew generateChangelog -PnextVersion=<next-version>\n----\n\nThen copy the release notes to your clipboard (your mileage may vary with the following command):\n\n[source,bash]\n----\ncat build/changelog/release-notes.md | xclip -selection clipboard\n----\n\nFinally, create the\nhttps://github.com/spring-projects/spring-security/releases[release on\nGitHub], associate it with the tag, and paste the generated notes.\n\nAlternatively, you can run the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#createGitHubRelease[`createGitHubRelease`] command to perform these steps automatically, replacing:\n\n* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)\n* `<branch>` - The name of the branch to be tagged (if the release commit has not already been tagged)\n* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `write:org`\n\n[source,bash]\n----\n./gradlew createGitHubRelease -PnextVersion=<next-version> -Pbranch=<branch> -PcreateRelease=true -PgitHubAccessToken=<github-personal-access-token>\n----\n\n[#update-version-on-project-page]\n== Update version on project page\n\nThe build will automatically update the project versions on https://spring.io/projects/spring-security#learn.\nTo do this manually, you can use the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#createSaganRelease[`createSaganRelease`] and https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#deleteSaganRelease[`deleteSaganRelease`] commands using the following parameters:\n\n* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)\n* `<previous-version>` - Replace with the previous release which will be removed from the listed versions (i.e. 5.5.0-RC1)\n* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `read:org` as https://spring.io/restdocs/index.html#authentication[documented for spring.io api]\n\n[source,bash]\n----\n./gradlew createSaganRelease deleteSaganRelease -PnextVersion=<next-version> -PpreviousVersion=<previous-version> -PgitHubAccessToken=<github-personal-access-token>\n----\n\nAlternatively, you can log into Contentful and update the versions manually on the Spring Security project page.\n\n[#close-create-milestone]\n== Close / Create milestone\n\nThe release milestone will be automatically closed once the release is complete.\nTo proceed manually, perform the following steps:\n\n1. Visit https://github.com/spring-projects/spring-security/milestones[GitHub\nMilestones] and create a new milestone for the next release version\n2. Move any open issues from the existing milestone you just released to the new milestone\n3. Close the milestone for the release\n\nNOTE: Remember that scheduled releases <<check-all-issues-are-closed,will not proceed>> if there are still open issues in the milestone.\n\n[#announce-release-on-slack]\n== Announce release on Slack\n\nThe release will automatically be announced on Slack.\nIf proceeding manually, announce the release on Slack in the channel https://pivotal.slack.com/messages/spring-release[#spring-release], including the keyword `+spring-security-announcing+` in the message.\nSomething like:\n\n....\nspring-security-announcing `5.5.0` is available now\n....\n\n[#update-to-next-development-version]\n== Update to next development version\n\nAfter the release is complete and artifacts have been uploaded to Maven Central, the build will automatically update to the next development version, commit and push.\nIf proceeding manually, update the version in `gradle.properties` to the next `+SNAPSHOT+` version with the commit message \"Next development version\" and then push.\n\n[#announce-release-on-other-channels]\n== Announce release on other channels\n\n* Create a blog post on Contentful\n* Tweet from https://twitter.com/springsecurity[@SpringSecurity]\n\n[[frequently-asked-questions]]\n== Frequently Asked Questions\n\n*When should I update dependencies manually?* Dependencies should be updated at the latest the end of the week prior to the release. This is usually the Friday following the 2nd Monday of the month (counting from the first week with a Monday). When in doubt, check the https://github.com/spring-projects/spring-security/milestones[milestones] page for release due dates.\n\n*When do scheduled releases occur?* Automated releases are scheduled to occur at *3:15 PM UTC* on the *3rd Monday of the month* (counting from the first week with a Monday).\n\n[NOTE]\nThe scheduled release process currently runs every Monday but only releases when a release is due. See the performed checks below for more information.\n\nThe automated release process occurs on the following branches:\n\n* `main`\n* `6.2.x`\n* `6.1.x`\n* `6.0.x` (commercial only)\n* `5.8.x`\n\nFor each of the above branches, the automated process performs the following checks before proceeding with the release:\n\n1. _Check if the milestone is due today._ This check compares the current (SNAPSHOT) version of the branch with available milestones and chooses the first match (sorted alphabetically). If the due date on the matched milestone is *not* today, the process stops.\n2. _Check if all issues are closed._ This check uses the milestone from the previous step and looks for open issues. If any open issues are found, the process stops.\n\n[IMPORTANT]\nYou should ensure all issues are closed or moved to another milestone prior to a scheduled release.\n\nIf the above checks pass, the version number is updated (in `gradle.properties`) and a commit is pushed to trigger the CI process.\n\n*How do I trigger a release manually?* You can trigger a release manually in two ways:\n\n1. Trigger a release for a particular branch via https://github.com/spring-projects/spring-security/actions/workflows/update-scheduled-release-version.yml[`update-scheduled-release-version.yml`] on the desired branch. The above checks are performed for that branch, and the release will proceed if all checks pass. _This is the recommended way to trigger a release that did not pass the above checks during a regularly scheduled release._\n2. Trigger releases for all branches via https://github.com/spring-projects/spring-security/actions/workflows/release-scheduler.yml[`release-scheduler.yml`] on the `main` branch. The above checks are performed for each branch, and only releases that pass all checks will proceed.\n\n*When should additional manual steps be performed?* All other automated steps listed above occur during the normal CI process. Additional manual steps can be performed at any time once the builds pass and releases are finished.\n\n*What if something goes wrong?* If the normal CI process fails, you can retry by re-running the failed jobs with the \"Re-run failed jobs\" option in GitHub Actions. If changes are required, you should revert the \"Release x.y.z\" commit, delete the tag, and proceed manually.\n"
  },
  {
    "path": "access/spring-security-access.gradle",
    "content": "plugins {\n\tid 'compile-warnings-error'\n\tid 'javadoc-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-crypto')\n\tapi project(':spring-security-core')\n\tapi 'org.springframework:spring-aop'\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-expression'\n\tapi 'io.micrometer:micrometer-observation'\n\n\toptional project(':spring-security-acl')\n\toptional project(':spring-security-messaging')\n\toptional project(':spring-security-web')\n\toptional 'org.springframework:spring-websocket'\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\toptional 'io.micrometer:context-propagation'\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'jakarta.annotation:jakarta.annotation-api'\n\toptional 'org.aspectj:aspectjrt'\n\toptional 'org.springframework:spring-jdbc'\n\toptional 'org.springframework:spring-tx'\n\toptional 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor'\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(path : ':spring-security-web', configuration : 'tests')\n\ttestImplementation 'commons-collections:commons-collections'\n\ttestImplementation 'io.projectreactor:reactor-test'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-core-test\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation 'org.skyscreamer:jsonassert'\n\ttestImplementation 'org.springframework:spring-test'\n\ttestImplementation 'org.jetbrains.kotlin:kotlin-reflect'\n\ttestImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'\n\ttestImplementation 'io.mockk:mockk'\n\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/AccessDecisionManager.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.util.Collection;\n\nimport org.springframework.security.authentication.InsufficientAuthenticationException;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Makes a final access control (authorization) decision.\n *\n * @author Ben Alex\n * @deprecated Use {@link AuthorizationManager} instead\n */\n@Deprecated\npublic interface AccessDecisionManager {\n\n\t/**\n\t * Resolves an access control decision for the passed parameters.\n\t * @param authentication the caller invoking the method (not null)\n\t * @param object the secured object being called\n\t * @param configAttributes the configuration attributes associated with the secured\n\t * object being invoked\n\t * @throws AccessDeniedException if access is denied as the authentication does not\n\t * hold a required authority or ACL privilege\n\t * @throws InsufficientAuthenticationException if access is denied as the\n\t * authentication does not provide a sufficient level of trust\n\t */\n\tvoid decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)\n\t\t\tthrows AccessDeniedException, InsufficientAuthenticationException;\n\n\t/**\n\t * Indicates whether this <code>AccessDecisionManager</code> is able to process\n\t * authorization requests presented with the passed <code>ConfigAttribute</code>.\n\t * <p>\n\t * This allows the <code>AbstractSecurityInterceptor</code> to check every\n\t * configuration attribute can be consumed by the configured\n\t * <code>AccessDecisionManager</code> and/or <code>RunAsManager</code> and/or\n\t * <code>AfterInvocationManager</code>.\n\t * </p>\n\t * @param attribute a configuration attribute that has been configured against the\n\t * <code>AbstractSecurityInterceptor</code>\n\t * @return true if this <code>AccessDecisionManager</code> can support the passed\n\t * configuration attribute\n\t */\n\tboolean supports(ConfigAttribute attribute);\n\n\t/**\n\t * Indicates whether the <code>AccessDecisionManager</code> implementation is able to\n\t * provide access control decisions for the indicated secured object type.\n\t * @param clazz the class that is being queried\n\t * @return <code>true</code> if the implementation can process the indicated class\n\t */\n\tboolean supports(Class<?> clazz);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/AccessDecisionVoter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.util.Collection;\n\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Indicates a class is responsible for voting on authorization decisions.\n * <p>\n * The coordination of voting (ie polling {@code AccessDecisionVoter}s, tallying their\n * responses, and making the final authorization decision) is performed by an\n * {@link org.springframework.security.access.AccessDecisionManager}.\n *\n * @author Ben Alex\n * @deprecated Use {@link AuthorizationManager} instead\n */\n@Deprecated\npublic interface AccessDecisionVoter<S> {\n\n\tint ACCESS_GRANTED = 1;\n\n\tint ACCESS_ABSTAIN = 0;\n\n\tint ACCESS_DENIED = -1;\n\n\t/**\n\t * Indicates whether this {@code AccessDecisionVoter} is able to vote on the passed\n\t * {@code ConfigAttribute}.\n\t * <p>\n\t * This allows the {@code AbstractSecurityInterceptor} to check every configuration\n\t * attribute can be consumed by the configured {@code AccessDecisionManager} and/or\n\t * {@code RunAsManager} and/or {@code AfterInvocationManager}.\n\t * @param attribute a configuration attribute that has been configured against the\n\t * {@code AbstractSecurityInterceptor}\n\t * @return true if this {@code AccessDecisionVoter} can support the passed\n\t * configuration attribute\n\t */\n\tboolean supports(ConfigAttribute attribute);\n\n\t/**\n\t * Indicates whether the {@code AccessDecisionVoter} implementation is able to provide\n\t * access control votes for the indicated secured object type.\n\t * @param clazz the class that is being queried\n\t * @return true if the implementation can process the indicated class\n\t */\n\tboolean supports(Class<?> clazz);\n\n\t/**\n\t * Indicates whether or not access is granted.\n\t * <p>\n\t * The decision must be affirmative ({@code ACCESS_GRANTED}), negative (\n\t * {@code ACCESS_DENIED}) or the {@code AccessDecisionVoter} can abstain (\n\t * {@code ACCESS_ABSTAIN}) from voting. Under no circumstances should implementing\n\t * classes return any other value. If a weighting of results is desired, this should\n\t * be handled in a custom\n\t * {@link org.springframework.security.access.AccessDecisionManager} instead.\n\t * <p>\n\t * Unless an {@code AccessDecisionVoter} is specifically intended to vote on an access\n\t * control decision due to a passed method invocation or configuration attribute\n\t * parameter, it must return {@code ACCESS_ABSTAIN}. This prevents the coordinating\n\t * {@code AccessDecisionManager} from counting votes from those\n\t * {@code AccessDecisionVoter}s without a legitimate interest in the access control\n\t * decision.\n\t * <p>\n\t * Whilst the secured object (such as a {@code MethodInvocation}) is passed as a\n\t * parameter to maximise flexibility in making access control decisions, implementing\n\t * classes should not modify it or cause the represented invocation to take place (for\n\t * example, by calling {@code MethodInvocation.proceed()}).\n\t * @param authentication the caller making the invocation\n\t * @param object the secured object being invoked\n\t * @param attributes the configuration attributes associated with the secured object\n\t * @return either {@link #ACCESS_GRANTED}, {@link #ACCESS_ABSTAIN} or\n\t * {@link #ACCESS_DENIED}\n\t */\n\tint vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/AfterInvocationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.intercept.AfterInvocationProviderManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Indicates a class is responsible for participating in an\n * {@link AfterInvocationProviderManager} decision.\n *\n * @author Ben Alex\n * @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor\n * @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor\n * @deprecated Use delegation with {@link AuthorizationManager}\n */\n@Deprecated\npublic interface AfterInvocationProvider {\n\n\tObject decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,\n\t\t\tObject returnedObject) throws AccessDeniedException;\n\n\t/**\n\t * Indicates whether this <code>AfterInvocationProvider</code> is able to participate\n\t * in a decision involving the passed <code>ConfigAttribute</code>.\n\t * <p>\n\t * This allows the <code>AbstractSecurityInterceptor</code> to check every\n\t * configuration attribute can be consumed by the configured\n\t * <code>AccessDecisionManager</code> and/or <code>RunAsManager</code> and/or\n\t * <code>AccessDecisionManager</code>.\n\t * </p>\n\t * @param attribute a configuration attribute that has been configured against the\n\t * <code>AbstractSecurityInterceptor</code>\n\t * @return true if this <code>AfterInvocationProvider</code> can support the passed\n\t * configuration attribute\n\t */\n\tboolean supports(ConfigAttribute attribute);\n\n\t/**\n\t * Indicates whether the <code>AfterInvocationProvider</code> is able to provide\n\t * \"after invocation\" processing for the indicated secured object type.\n\t * @param clazz the class of secure object that is being queried\n\t * @return true if the implementation can process the indicated class\n\t */\n\tboolean supports(Class<?> clazz);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/ConfigAttribute.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.access.intercept.RunAsManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\n\n/**\n * Stores a security system related configuration attribute.\n *\n * <p>\n * When an\n * {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor} is\n * set up, a list of configuration attributes is defined for secure object patterns. These\n * configuration attributes have special meaning to a {@link RunAsManager},\n * {@link AccessDecisionManager} or <code>AccessDecisionManager</code> delegate.\n *\n * <p>\n * Stored at runtime with other <code>ConfigAttribute</code>s for the same secure object\n * target.\n *\n * @author Ben Alex\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. In the case of\n * method security, please see {@link SecurityAnnotationScanner} and\n * {@link AuthorizationManager}. In the case of channel security, please see\n * {@code HttpsRedirectFilter}. In the case of web security, please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\n@NullUnmarked\npublic interface ConfigAttribute extends Serializable {\n\n\t/**\n\t * If the <code>ConfigAttribute</code> can be represented as a <code>String</code> and\n\t * that <code>String</code> is sufficient in precision to be relied upon as a\n\t * configuration parameter by a {@link RunAsManager}, {@link AccessDecisionManager} or\n\t * <code>AccessDecisionManager</code> delegate, this method should return such a\n\t * <code>String</code>.\n\t * <p>\n\t * If the <code>ConfigAttribute</code> cannot be expressed with sufficient precision\n\t * as a <code>String</code>, <code>null</code> should be returned. Returning\n\t * <code>null</code> will require any relying classes to specifically support the\n\t * <code>ConfigAttribute</code> implementation, so returning <code>null</code> should\n\t * be avoided unless actually required.\n\t * @return a representation of the configuration attribute (or <code>null</code> if\n\t * the configuration attribute cannot be expressed as a <code>String</code> with\n\t * sufficient precision).\n\t */\n\tString getAttribute();\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/SecurityConfig.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.io.Serial;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Stores a {@link ConfigAttribute} as a <code>String</code>.\n *\n * @author Ben Alex\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. In the case of\n * method security, please see {@link SecurityAnnotationScanner} and\n * {@link AuthorizationManager}. In the case of channel security, please see\n * {@code HttpsRedirectFilter}. In the case of web security, please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\npublic class SecurityConfig implements ConfigAttribute {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7138084564199804304L;\n\n\tprivate final String attrib;\n\n\tpublic SecurityConfig(String config) {\n\t\tAssert.hasText(config, \"You must provide a configuration attribute\");\n\t\tthis.attrib = config;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof ConfigAttribute attr) {\n\t\t\treturn this.attrib.equals(attr.getAttribute());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getAttribute() {\n\t\treturn this.attrib;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.attrib.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.attrib;\n\t}\n\n\tpublic static List<ConfigAttribute> createListFromCommaDelimitedString(String access) {\n\t\treturn createList(StringUtils.commaDelimitedListToStringArray(access));\n\t}\n\n\tpublic static List<ConfigAttribute> createList(String... attributeNames) {\n\t\tAssert.notNull(attributeNames, \"You must supply an array of attribute names\");\n\t\tList<ConfigAttribute> attributes = new ArrayList<>(attributeNames.length);\n\t\tfor (String attribute : attributeNames) {\n\t\t\tattributes.add(new SecurityConfig(attribute.trim()));\n\t\t}\n\t\treturn attributes;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/SecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.util.Collection;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.security.access.intercept.AbstractSecurityInterceptor;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\n\n/**\n * Implemented by classes that store and can identify the {@link ConfigAttribute}s that\n * applies to a given secure object invocation.\n *\n * @author Ben Alex\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. In the case of\n * method security, please see {@link SecurityAnnotationScanner} and\n * {@link AuthorizationManager}. In the case of channel security, please see\n * {@code HttpsRedirectFilter}. In the case of web security, please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\npublic interface SecurityMetadataSource extends AopInfrastructureBean {\n\n\t/**\n\t * Accesses the {@code ConfigAttribute}s that apply to a given secure object.\n\t * @param object the object being secured\n\t * @return the attributes that apply to the passed in secured object. Should return an\n\t * empty collection if there are no applicable attributes.\n\t * @throws IllegalArgumentException if the passed object is not of a type supported by\n\t * the <code>SecurityMetadataSource</code> implementation\n\t */\n\tCollection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException;\n\n\t/**\n\t * If available, returns all of the {@code ConfigAttribute}s defined by the\n\t * implementing class.\n\t * <p>\n\t * This is used by the {@link AbstractSecurityInterceptor} to perform startup time\n\t * validation of each {@code ConfigAttribute} configured against it.\n\t * @return the {@code ConfigAttribute}s or {@code null} if unsupported\n\t */\n\tCollection<ConfigAttribute> getAllConfigAttributes();\n\n\t/**\n\t * Indicates whether the {@code SecurityMetadataSource} implementation is able to\n\t * provide {@code ConfigAttribute}s for the indicated secure object type.\n\t * @param clazz the class that is being queried\n\t * @return true if the implementation can process the indicated class\n\t */\n\tboolean supports(Class<?> clazz);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/annotation/AnnotationMetadataExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.lang.annotation.Annotation;\nimport java.util.Collection;\n\nimport org.springframework.security.access.ConfigAttribute;\n\n/**\n * Strategy to process a custom security annotation to extract the relevant\n * {@code ConfigAttribute}s for securing a method.\n * <p>\n * Used by {@code SecuredAnnotationSecurityMetadataSource}.\n *\n * @author Luke Taylor\n * @deprecated Used only by now-deprecated classes. Consider\n * {@link org.springframework.security.authorization.method.SecuredAuthorizationManager}\n * for `@Secured` methods.\n */\n@Deprecated\npublic interface AnnotationMetadataExtractor<A extends Annotation> {\n\n\tCollection<? extends ConfigAttribute> extractAttributes(A securityAnnotation);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/annotation/Jsr250MethodSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport jakarta.annotation.security.DenyAll;\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;\nimport org.springframework.util.StringUtils;\n\n/**\n * Sources method security metadata from major JSR 250 security annotations.\n *\n * @author Ben Alex\n * @since 2.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.Jsr250AuthorizationManager}\n * instead\n */\n@NullUnmarked\n@Deprecated\npublic class Jsr250MethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {\n\n\tprivate String defaultRolePrefix = \"ROLE_\";\n\n\t/**\n\t * <p>\n\t * Sets the default prefix to be added to {@link RolesAllowed}. For example, if\n\t * {@code @RolesAllowed(\"ADMIN\")} or {@code @RolesAllowed(\"ADMIN\")} is used, then the\n\t * role ROLE_ADMIN will be used when the defaultRolePrefix is \"ROLE_\" (default).\n\t * </p>\n\t *\n\t * <p>\n\t * If null or empty, then no default role prefix is used.\n\t * </p>\n\t * @param defaultRolePrefix the default prefix to add to roles. Default \"ROLE_\".\n\t */\n\tpublic void setDefaultRolePrefix(String defaultRolePrefix) {\n\t\tthis.defaultRolePrefix = defaultRolePrefix;\n\t}\n\n\t@Override\n\tprotected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {\n\t\treturn processAnnotations(clazz.getAnnotations());\n\t}\n\n\t@Override\n\tprotected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {\n\t\treturn processAnnotations(AnnotationUtils.getAnnotations(method));\n\t}\n\n\t@Override\n\tpublic @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\treturn null;\n\t}\n\n\tprivate @Nullable List<ConfigAttribute> processAnnotations(Annotation @Nullable [] annotations) {\n\t\tif (annotations == null || annotations.length == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tList<ConfigAttribute> attributes = new ArrayList<>();\n\t\tfor (Annotation annotation : annotations) {\n\t\t\tif (annotation instanceof DenyAll) {\n\t\t\t\tattributes.add(Jsr250SecurityConfig.DENY_ALL_ATTRIBUTE);\n\t\t\t\treturn attributes;\n\t\t\t}\n\t\t\tif (annotation instanceof PermitAll) {\n\t\t\t\tattributes.add(Jsr250SecurityConfig.PERMIT_ALL_ATTRIBUTE);\n\t\t\t\treturn attributes;\n\t\t\t}\n\t\t\tif (annotation instanceof RolesAllowed ra) {\n\n\t\t\t\tfor (String allowed : ra.value()) {\n\t\t\t\t\tString defaultedAllowed = getRoleWithDefaultPrefix(allowed);\n\t\t\t\t\tattributes.add(new Jsr250SecurityConfig(defaultedAllowed));\n\t\t\t\t}\n\t\t\t\treturn attributes;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String getRoleWithDefaultPrefix(String role) {\n\t\tif (role == null) {\n\t\t\treturn role;\n\t\t}\n\t\tif (!StringUtils.hasLength(this.defaultRolePrefix)) {\n\t\t\treturn role;\n\t\t}\n\t\tif (role.startsWith(this.defaultRolePrefix)) {\n\t\t\treturn role;\n\t\t}\n\t\treturn this.defaultRolePrefix + role;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/annotation/Jsr250SecurityConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport jakarta.annotation.security.DenyAll;\nimport jakarta.annotation.security.PermitAll;\n\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\n\n/**\n * Security config applicable as a JSR 250 annotation attribute.\n *\n * @author Ryan Heaton\n * @since 2.0\n * @deprecated Use {@link AuthorizationManagerBeforeMethodInterceptor#jsr250()} instead\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\npublic class Jsr250SecurityConfig extends SecurityConfig {\n\n\tpublic static final Jsr250SecurityConfig PERMIT_ALL_ATTRIBUTE = new Jsr250SecurityConfig(PermitAll.class.getName());\n\n\tpublic static final Jsr250SecurityConfig DENY_ALL_ATTRIBUTE = new Jsr250SecurityConfig(DenyAll.class.getName());\n\n\tpublic Jsr250SecurityConfig(String role) {\n\t\tsuper(role);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/annotation/Jsr250Voter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Voter on JSR-250 configuration attributes.\n *\n * @author Ryan Heaton\n * @since 2.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.Jsr250AuthorizationManager}\n * instead\n */\n@Deprecated\npublic class Jsr250Voter implements AccessDecisionVoter<Object> {\n\n\t/**\n\t * The specified config attribute is supported if its an instance of a\n\t * {@link Jsr250SecurityConfig}.\n\t * @param configAttribute The config attribute.\n\t * @return whether the config attribute is supported.\n\t */\n\t@Override\n\tpublic boolean supports(ConfigAttribute configAttribute) {\n\t\treturn configAttribute instanceof Jsr250SecurityConfig;\n\t}\n\n\t/**\n\t * All classes are supported.\n\t * @param clazz the class.\n\t * @return true\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Votes according to JSR 250.\n\t * <p>\n\t * If no JSR-250 attributes are found, it will abstain, otherwise it will grant or\n\t * deny access based on the attributes that are found.\n\t * @param authentication The authentication object.\n\t * @param object The access object.\n\t * @param definition The configuration definition.\n\t * @return The vote.\n\t */\n\t@Override\n\tpublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> definition) {\n\t\tboolean jsr250AttributeFound = false;\n\t\tfor (ConfigAttribute attribute : definition) {\n\t\t\tif (Jsr250SecurityConfig.PERMIT_ALL_ATTRIBUTE.equals(attribute)) {\n\t\t\t\treturn ACCESS_GRANTED;\n\t\t\t}\n\t\t\tif (Jsr250SecurityConfig.DENY_ALL_ATTRIBUTE.equals(attribute)) {\n\t\t\t\treturn ACCESS_DENIED;\n\t\t\t}\n\t\t\tif (supports(attribute)) {\n\t\t\t\tjsr250AttributeFound = true;\n\t\t\t\t// Attempt to find a matching granted authority\n\t\t\t\tfor (GrantedAuthority authority : authentication.getAuthorities()) {\n\t\t\t\t\tif (attribute.getAttribute().equals(authority.getAuthority())) {\n\t\t\t\t\t\treturn ACCESS_GRANTED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn jsr250AttributeFound ? ACCESS_DENIED : ACCESS_ABSTAIN;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.GenericTypeResolver;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;\nimport org.springframework.util.Assert;\n\n/**\n * Sources method security metadata from Spring Security's {@link Secured} annotation.\n * <p>\n * Can also be used with custom security annotations by injecting an\n * {@link AnnotationMetadataExtractor}. The annotation type will then be obtained from the\n * generic parameter type supplied to this interface\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor#secured}\n */\n@NullUnmarked\n@Deprecated\n@SuppressWarnings({ \"unchecked\" })\npublic class SecuredAnnotationSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {\n\n\tprivate AnnotationMetadataExtractor annotationExtractor;\n\n\tprivate @Nullable Class<? extends Annotation> annotationType;\n\n\tpublic SecuredAnnotationSecurityMetadataSource() {\n\t\tthis(new SecuredAnnotationMetadataExtractor());\n\t}\n\n\tpublic SecuredAnnotationSecurityMetadataSource(AnnotationMetadataExtractor annotationMetadataExtractor) {\n\t\tAssert.notNull(annotationMetadataExtractor, \"annotationMetadataExtractor cannot be null\");\n\t\tthis.annotationExtractor = annotationMetadataExtractor;\n\t\tthis.annotationType = (Class<? extends Annotation>) GenericTypeResolver\n\t\t\t.resolveTypeArgument(this.annotationExtractor.getClass(), AnnotationMetadataExtractor.class);\n\t\tAssert.notNull(this.annotationType, () -> this.annotationExtractor.getClass().getName()\n\t\t\t\t+ \" must supply a generic parameter for AnnotationMetadataExtractor\");\n\t}\n\n\t@Override\n\tprotected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {\n\t\treturn processAnnotation(AnnotationUtils.findAnnotation(clazz, this.annotationType));\n\t}\n\n\t@Override\n\tprotected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {\n\t\treturn processAnnotation(AnnotationUtils.findAnnotation(method, this.annotationType));\n\t}\n\n\t@Override\n\tpublic @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\treturn null;\n\t}\n\n\tprivate @Nullable Collection<ConfigAttribute> processAnnotation(@Nullable Annotation annotation) {\n\t\treturn (annotation != null) ? this.annotationExtractor.extractAttributes(annotation) : null;\n\t}\n\n\tstatic class SecuredAnnotationMetadataExtractor implements AnnotationMetadataExtractor<Secured> {\n\n\t\t@Override\n\t\tpublic Collection<ConfigAttribute> extractAttributes(Secured secured) {\n\t\t\tString[] attributeTokens = secured.value();\n\t\t\tList<ConfigAttribute> attributes = new ArrayList<>(attributeTokens.length);\n\t\t\tfor (String token : attributeTokens) {\n\t\t\t\tattributes.add(new SecurityConfig(token));\n\t\t\t}\n\t\t\treturn attributes;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/event/AbstractAuthorizationEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.event;\n\nimport org.springframework.context.ApplicationEvent;\n\n/**\n * Abstract superclass for all security interception related events.\n *\n * @author Ben Alex\n * @deprecated Authorization events have moved. Consider\n * {@link org.springframework.security.authorization.event.AuthorizationGrantedEvent} and\n * {@link org.springframework.security.authorization.event.AuthorizationDeniedEvent}\n */\n@Deprecated\npublic abstract class AbstractAuthorizationEvent extends ApplicationEvent {\n\n\t/**\n\t * Construct the event, passing in the secure object being intercepted.\n\t * @param secureObject the secure object\n\t */\n\tpublic AbstractAuthorizationEvent(Object secureObject) {\n\t\tsuper(secureObject);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/event/AuthenticationCredentialsNotFoundEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.event;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.util.Assert;\n\n/**\n * Indicates a secure object invocation failed because the <code>Authentication</code>\n * could not be obtained from the <code>SecurityContextHolder</code>.\n *\n * @author Ben Alex\n * @deprecated Authentication is now separated from authorization. Consider\n * {@link org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent}\n * instead.\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\npublic class AuthenticationCredentialsNotFoundEvent extends AbstractAuthorizationEvent {\n\n\tprivate final AuthenticationCredentialsNotFoundException credentialsNotFoundException;\n\n\tprivate final Collection<ConfigAttribute> configAttribs;\n\n\t/**\n\t * Construct the event.\n\t * @param secureObject the secure object\n\t * @param attributes that apply to the secure object\n\t * @param credentialsNotFoundException exception returned to the caller (contains\n\t * reason)\n\t *\n\t */\n\tpublic AuthenticationCredentialsNotFoundEvent(Object secureObject, Collection<ConfigAttribute> attributes,\n\t\t\tAuthenticationCredentialsNotFoundException credentialsNotFoundException) {\n\t\tsuper(secureObject);\n\t\tAssert.isTrue(attributes != null && credentialsNotFoundException != null,\n\t\t\t\t\"All parameters are required and cannot be null\");\n\t\tthis.configAttribs = attributes;\n\t\tthis.credentialsNotFoundException = credentialsNotFoundException;\n\t}\n\n\tpublic Collection<ConfigAttribute> getConfigAttributes() {\n\t\treturn this.configAttribs;\n\t}\n\n\tpublic AuthenticationCredentialsNotFoundException getCredentialsNotFoundException() {\n\t\treturn this.credentialsNotFoundException;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/event/AuthorizationFailureEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.event;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Indicates a secure object invocation failed because the principal could not be\n * authorized for the request.\n *\n * <p>\n * This event might be thrown as a result of either an\n * {@link org.springframework.security.access.AccessDecisionManager AccessDecisionManager}\n * or an {@link org.springframework.security.access.intercept.AfterInvocationManager\n * AfterInvocationManager}.\n *\n * @author Ben Alex\n * @deprecated Use\n * {@link org.springframework.security.authorization.event.AuthorizationDeniedEvent}\n * instead\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\npublic class AuthorizationFailureEvent extends AbstractAuthorizationEvent {\n\n\tprivate final AccessDeniedException accessDeniedException;\n\n\tprivate final Authentication authentication;\n\n\tprivate final Collection<ConfigAttribute> configAttributes;\n\n\t/**\n\t * Construct the event.\n\t * @param secureObject the secure object\n\t * @param attributes that apply to the secure object\n\t * @param authentication that was found in the <code>SecurityContextHolder</code>\n\t * @param accessDeniedException that was returned by the\n\t * <code>AccessDecisionManager</code>\n\t * @throws IllegalArgumentException if any null arguments are presented.\n\t */\n\tpublic AuthorizationFailureEvent(Object secureObject, Collection<ConfigAttribute> attributes,\n\t\t\tAuthentication authentication, AccessDeniedException accessDeniedException) {\n\t\tsuper(secureObject);\n\t\tAssert.isTrue(attributes != null && authentication != null && accessDeniedException != null,\n\t\t\t\t\"All parameters are required and cannot be null\");\n\t\tthis.configAttributes = attributes;\n\t\tthis.authentication = authentication;\n\t\tthis.accessDeniedException = accessDeniedException;\n\t}\n\n\tpublic AccessDeniedException getAccessDeniedException() {\n\t\treturn this.accessDeniedException;\n\t}\n\n\tpublic Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n\tpublic Collection<ConfigAttribute> getConfigAttributes() {\n\t\treturn this.configAttributes;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/event/AuthorizedEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.event;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Event indicating a secure object was invoked successfully.\n * <P>\n * Published just before the secure object attempts to proceed.\n * </p>\n *\n * @author Ben Alex\n * @deprecated Use\n * {@link org.springframework.security.authorization.event.AuthorizationGrantedEvent}\n * instead\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\npublic class AuthorizedEvent extends AbstractAuthorizationEvent {\n\n\tprivate final Authentication authentication;\n\n\tprivate final Collection<ConfigAttribute> configAttributes;\n\n\t/**\n\t * Construct the event.\n\t * @param secureObject the secure object\n\t * @param attributes that apply to the secure object\n\t * @param authentication that successfully called the secure object\n\t *\n\t */\n\tpublic AuthorizedEvent(Object secureObject, Collection<ConfigAttribute> attributes, Authentication authentication) {\n\t\tsuper(secureObject);\n\t\tAssert.isTrue(attributes != null && authentication != null, \"All parameters are required and cannot be null\");\n\t\tthis.configAttributes = attributes;\n\t\tthis.authentication = authentication;\n\t}\n\n\tpublic Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n\tpublic Collection<ConfigAttribute> getConfigAttributes() {\n\t\treturn this.configAttributes;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/event/LoggerListener.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.event;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.log.LogMessage;\n\n/**\n * Outputs interceptor-related application events to Commons Logging.\n * <p>\n * All failures are logged at the warning level, with success events logged at the\n * information level, and public invocation events logged at the debug level.\n * </p>\n *\n * @author Ben Alex\n * @deprecated Logging is now embedded in Spring Security components. If you need further\n * logging, please consider using your own {@link ApplicationListener}\n */\n@Deprecated\npublic class LoggerListener implements ApplicationListener<AbstractAuthorizationEvent> {\n\n\tprivate static final Log logger = LogFactory.getLog(LoggerListener.class);\n\n\t@Override\n\tpublic void onApplicationEvent(AbstractAuthorizationEvent event) {\n\t\tif (event instanceof AuthenticationCredentialsNotFoundEvent) {\n\t\t\tonAuthenticationCredentialsNotFoundEvent((AuthenticationCredentialsNotFoundEvent) event);\n\t\t}\n\t\tif (event instanceof AuthorizationFailureEvent) {\n\t\t\tonAuthorizationFailureEvent((AuthorizationFailureEvent) event);\n\t\t}\n\t\tif (event instanceof AuthorizedEvent) {\n\t\t\tonAuthorizedEvent((AuthorizedEvent) event);\n\t\t}\n\t\tif (event instanceof PublicInvocationEvent) {\n\t\t\tonPublicInvocationEvent((PublicInvocationEvent) event);\n\t\t}\n\t}\n\n\tprivate void onAuthenticationCredentialsNotFoundEvent(AuthenticationCredentialsNotFoundEvent authEvent) {\n\t\tlogger.warn(LogMessage.format(\n\t\t\t\t\"Security interception failed due to: %s; secure object: %s; configuration attributes: %s\",\n\t\t\t\tauthEvent.getCredentialsNotFoundException(), authEvent.getSource(), authEvent.getConfigAttributes()));\n\t}\n\n\tprivate void onPublicInvocationEvent(PublicInvocationEvent event) {\n\t\tlogger.info(LogMessage.format(\"Security interception not required for public secure object: %s\",\n\t\t\t\tevent.getSource()));\n\t}\n\n\tprivate void onAuthorizedEvent(AuthorizedEvent authEvent) {\n\t\tlogger.info(LogMessage.format(\n\t\t\t\t\"Security authorized for authenticated principal: %s; secure object: %s; configuration attributes: %s\",\n\t\t\t\tauthEvent.getAuthentication(), authEvent.getSource(), authEvent.getConfigAttributes()));\n\t}\n\n\tprivate void onAuthorizationFailureEvent(AuthorizationFailureEvent authEvent) {\n\t\tlogger.warn(LogMessage.format(\n\t\t\t\t\"Security authorization failed due to: %s; authenticated principal: %s; secure object: %s; configuration attributes: %s\",\n\t\t\t\tauthEvent.getAccessDeniedException(), authEvent.getAuthentication(), authEvent.getSource(),\n\t\t\t\tauthEvent.getConfigAttributes()));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/event/PublicInvocationEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.event;\n\nimport org.springframework.security.authorization.event.AuthorizationGrantedEvent;\n\n/**\n * Event that is generated whenever a public secure object is invoked.\n * <p>\n * A public secure object is a secure object that has no <code>ConfigAttribute</code>s\n * defined. A public secure object will not cause the <code>SecurityContextHolder</code>\n * to be inspected or authenticated, and no authorization will take place.\n * </p>\n * <p>\n * Published just before the secure object attempts to proceed.\n * </p>\n *\n * @author Ben Alex\n * @deprecated Only used by now-deprecated classes. Consider\n * {@link AuthorizationGrantedEvent#getSource()} to deduce public invocations.\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\npublic class PublicInvocationEvent extends AbstractAuthorizationEvent {\n\n\t/**\n\t * Construct the event, passing in the public secure object.\n\t * @param secureObject the public secure object\n\t */\n\tpublic PublicInvocationEvent(Object secureObject) {\n\t\tsuper(secureObject);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/event/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Authorization event and listener classes.\n */\n@NullMarked\npackage org.springframework.security.access.event;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/expression/method/AbstractExpressionBasedMethodConfigAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParseException;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.util.Assert;\n\n/**\n * Contains both filtering and authorization expression meta-data for Spring-EL based\n * access control.\n * <p>\n * Base class for pre or post-invocation phases of a method invocation.\n * <p>\n * Either filter or authorization expressions may be null, but not both.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}\n * interceptors instead\n */\n@NullUnmarked\n@Deprecated\nabstract class AbstractExpressionBasedMethodConfigAttribute implements ConfigAttribute {\n\n\tprivate final @Nullable Expression filterExpression;\n\n\tprivate final @Nullable Expression authorizeExpression;\n\n\t/**\n\t * Parses the supplied expressions as Spring-EL.\n\t */\n\tAbstractExpressionBasedMethodConfigAttribute(String filterExpression, String authorizeExpression)\n\t\t\tthrows ParseException {\n\t\tAssert.isTrue(filterExpression != null || authorizeExpression != null,\n\t\t\t\t\"Filter and authorization Expressions cannot both be null\");\n\t\tSpelExpressionParser parser = new SpelExpressionParser();\n\t\tthis.filterExpression = (filterExpression != null) ? parser.parseExpression(filterExpression) : null;\n\t\tthis.authorizeExpression = (authorizeExpression != null) ? parser.parseExpression(authorizeExpression) : null;\n\t}\n\n\tAbstractExpressionBasedMethodConfigAttribute(Expression filterExpression, Expression authorizeExpression)\n\t\t\tthrows ParseException {\n\t\tAssert.isTrue(filterExpression != null || authorizeExpression != null,\n\t\t\t\t\"Filter and authorization Expressions cannot both be null\");\n\t\tthis.filterExpression = (filterExpression != null) ? filterExpression : null;\n\t\tthis.authorizeExpression = (authorizeExpression != null) ? authorizeExpression : null;\n\t}\n\n\tExpression getFilterExpression() {\n\t\treturn this.filterExpression;\n\t}\n\n\tExpression getAuthorizeExpression() {\n\t\treturn this.authorizeExpression;\n\t}\n\n\t@Override\n\tpublic @Nullable String getAttribute() {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/expression/method/ExpressionBasedAnnotationAttributeFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.ParseException;\nimport org.springframework.security.access.prepost.PostInvocationAttribute;\nimport org.springframework.security.access.prepost.PreInvocationAttribute;\nimport org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;\nimport org.springframework.util.Assert;\n\n/**\n * {@link PrePostInvocationAttributeFactory} which interprets the annotation value as an\n * expression to be evaluated at runtime.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.0\n * @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}\n * interceptors instead\n */\n@NullUnmarked\n@Deprecated\npublic class ExpressionBasedAnnotationAttributeFactory implements PrePostInvocationAttributeFactory {\n\n\tprivate final Object parserLock = new Object();\n\n\tprivate @Nullable ExpressionParser parser;\n\n\tprivate MethodSecurityExpressionHandler handler;\n\n\tpublic ExpressionBasedAnnotationAttributeFactory(MethodSecurityExpressionHandler handler) {\n\t\tAssert.notNull(handler, \"handler cannot be null\");\n\t\tthis.handler = handler;\n\t}\n\n\t@Override\n\tpublic PreInvocationAttribute createPreInvocationAttribute(String preFilterAttribute, String filterObject,\n\t\t\tString preAuthorizeAttribute) {\n\t\ttry {\n\t\t\t// TODO: Optimization of permitAll\n\t\t\tExpressionParser parser = getParser();\n\t\t\tExpression preAuthorizeExpression = (preAuthorizeAttribute != null)\n\t\t\t\t\t? parser.parseExpression(preAuthorizeAttribute) : parser.parseExpression(\"permitAll\");\n\t\t\tExpression preFilterExpression = (preFilterAttribute != null) ? parser.parseExpression(preFilterAttribute)\n\t\t\t\t\t: null;\n\t\t\treturn new PreInvocationExpressionAttribute(preFilterExpression, filterObject, preAuthorizeExpression);\n\t\t}\n\t\tcatch (ParseException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to parse expression '\" + ex.getExpressionString() + \"'\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable PostInvocationAttribute createPostInvocationAttribute(String postFilterAttribute,\n\t\t\tString postAuthorizeAttribute) {\n\t\ttry {\n\t\t\tExpressionParser parser = getParser();\n\t\t\tExpression postAuthorizeExpression = (postAuthorizeAttribute != null)\n\t\t\t\t\t? parser.parseExpression(postAuthorizeAttribute) : null;\n\t\t\tExpression postFilterExpression = (postFilterAttribute != null)\n\t\t\t\t\t? parser.parseExpression(postFilterAttribute) : null;\n\t\t\tif (postFilterExpression != null || postAuthorizeExpression != null) {\n\t\t\t\treturn new PostInvocationExpressionAttribute(postFilterExpression, postAuthorizeExpression);\n\t\t\t}\n\t\t}\n\t\tcatch (ParseException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to parse expression '\" + ex.getExpressionString() + \"'\", ex);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Delay the lookup of the {@link ExpressionParser} to prevent SEC-2136\n\t * @return\n\t */\n\tprivate ExpressionParser getParser() {\n\t\tif (this.parser != null) {\n\t\t\treturn this.parser;\n\t\t}\n\t\tsynchronized (this.parserLock) {\n\t\t\tthis.parser = this.handler.getExpressionParser();\n\t\t\tthis.handler = null;\n\t\t}\n\t\treturn this.parser;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/expression/method/ExpressionBasedPostInvocationAdvice.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.prepost.PostInvocationAttribute;\nimport org.springframework.security.access.prepost.PostInvocationAuthorizationAdvice;\nimport org.springframework.security.core.Authentication;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}\n * instead\n */\n@Deprecated\npublic class ExpressionBasedPostInvocationAdvice implements PostInvocationAuthorizationAdvice {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final MethodSecurityExpressionHandler expressionHandler;\n\n\tpublic ExpressionBasedPostInvocationAdvice(MethodSecurityExpressionHandler expressionHandler) {\n\t\tthis.expressionHandler = expressionHandler;\n\t}\n\n\t@Override\n\tpublic Object after(Authentication authentication, MethodInvocation mi, PostInvocationAttribute postAttr,\n\t\t\tObject returnedObject) throws AccessDeniedException {\n\t\tPostInvocationExpressionAttribute pia = (PostInvocationExpressionAttribute) postAttr;\n\t\tEvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);\n\t\tExpression postFilter = pia.getFilterExpression();\n\t\tExpression postAuthorize = pia.getAuthorizeExpression();\n\t\tif (postFilter != null) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Applying PostFilter expression %s\", postFilter));\n\t\t\tif (returnedObject != null) {\n\t\t\t\treturnedObject = this.expressionHandler.filter(returnedObject, postFilter, ctx);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.logger.debug(\"Return object is null, filtering will be skipped\");\n\t\t\t}\n\t\t}\n\t\tthis.expressionHandler.setReturnObject(returnedObject, ctx);\n\t\tif (postAuthorize != null && !ExpressionUtils.evaluateAsBoolean(postAuthorize, ctx)) {\n\t\t\tthis.logger.debug(\"PostAuthorize expression rejected access\");\n\t\t\tthrow new AccessDeniedException(\"Access is denied\");\n\t\t}\n\t\treturn returnedObject;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/expression/method/ExpressionBasedPreInvocationAdvice.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.util.Collection;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.prepost.PreInvocationAttribute;\nimport org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Method pre-invocation handling based on expressions.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}\n * instead\n */\n@NullUnmarked\n@Deprecated\npublic class ExpressionBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice {\n\n\tprivate MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\n\t@Override\n\tpublic boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute attr) {\n\t\tPreInvocationExpressionAttribute preAttr = (PreInvocationExpressionAttribute) attr;\n\t\tEvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);\n\t\tExpression preFilter = preAttr.getFilterExpression();\n\t\tExpression preAuthorize = preAttr.getAuthorizeExpression();\n\t\tif (preFilter != null) {\n\t\t\tObject filterTarget = findFilterTarget(preAttr.getFilterTarget(), ctx, mi);\n\t\t\tthis.expressionHandler.filter(filterTarget, preFilter, ctx);\n\t\t}\n\t\treturn (preAuthorize != null) ? ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx) : true;\n\t}\n\n\tprivate Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation invocation) {\n\t\tObject filterTarget = null;\n\t\tif (filterTargetName.length() > 0) {\n\t\t\tfilterTarget = ctx.lookupVariable(filterTargetName);\n\t\t\tAssert.notNull(filterTarget,\n\t\t\t\t\t() -> \"Filter target was null, or no argument with name \" + filterTargetName + \" found in method\");\n\t\t}\n\t\telse if (invocation.getArguments().length == 1) {\n\t\t\tObject arg = invocation.getArguments()[0];\n\t\t\tif (arg.getClass().isArray() || arg instanceof Collection<?>) {\n\t\t\t\tfilterTarget = arg;\n\t\t\t}\n\t\t\tAssert.notNull(filterTarget, () -> \"A PreFilter expression was set but the method argument type\"\n\t\t\t\t\t+ arg.getClass() + \" is not filterable\");\n\t\t}\n\t\telse if (invocation.getArguments().length > 1) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Unable to determine the method argument for filtering. Specify the filter target.\");\n\t\t}\n\t\tAssert.isTrue(!filterTarget.getClass().isArray(),\n\t\t\t\t\"Pre-filtering on array types is not supported. Using a Collection will solve this problem\");\n\t\treturn filterTarget;\n\t}\n\n\tpublic void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\tthis.expressionHandler = expressionHandler;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/expression/method/PostInvocationExpressionAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParseException;\nimport org.springframework.security.access.prepost.PostInvocationAttribute;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}\n * instead\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\nclass PostInvocationExpressionAttribute extends AbstractExpressionBasedMethodConfigAttribute\n\t\timplements PostInvocationAttribute {\n\n\tPostInvocationExpressionAttribute(String filterExpression, String authorizeExpression) throws ParseException {\n\t\tsuper(filterExpression, authorizeExpression);\n\t}\n\n\tPostInvocationExpressionAttribute(@Nullable Expression filterExpression, @Nullable Expression authorizeExpression)\n\t\t\tthrows ParseException {\n\t\tsuper(filterExpression, authorizeExpression);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tExpression authorize = getAuthorizeExpression();\n\t\tExpression filter = getFilterExpression();\n\t\tsb.append(\"[authorize: '\").append((authorize != null) ? authorize.getExpressionString() : \"null\");\n\t\tsb.append(\"', filter: '\").append((filter != null) ? filter.getExpressionString() : \"null\").append(\"']\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/expression/method/PreInvocationExpressionAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParseException;\nimport org.springframework.security.access.prepost.PreInvocationAttribute;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}\n * instead\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\nclass PreInvocationExpressionAttribute extends AbstractExpressionBasedMethodConfigAttribute\n\t\timplements PreInvocationAttribute {\n\n\tprivate final String filterTarget;\n\n\tPreInvocationExpressionAttribute(String filterExpression, String filterTarget, String authorizeExpression)\n\t\t\tthrows ParseException {\n\t\tsuper(filterExpression, authorizeExpression);\n\t\tthis.filterTarget = filterTarget;\n\t}\n\n\tPreInvocationExpressionAttribute(@Nullable Expression filterExpression, String filterTarget,\n\t\t\tExpression authorizeExpression) throws ParseException {\n\t\tsuper(filterExpression, authorizeExpression);\n\t\tthis.filterTarget = filterTarget;\n\t}\n\n\t/**\n\t * The parameter name of the target argument (must be a Collection) to which filtering\n\t * will be applied.\n\t * @return the method parameter name\n\t */\n\tString getFilterTarget() {\n\t\treturn this.filterTarget;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tExpression authorize = getAuthorizeExpression();\n\t\tExpression filter = getFilterExpression();\n\t\tsb.append(\"[authorize: '\").append((authorize != null) ? authorize.getExpressionString() : \"null\");\n\t\tsb.append(\"', filter: '\").append((filter != null) ? filter.getExpressionString() : \"null\");\n\t\tsb.append(\"', filterTarget: '\").append(this.filterTarget).append(\"']\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityMetadataSource;\nimport org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent;\nimport org.springframework.security.access.event.AuthorizationFailureEvent;\nimport org.springframework.security.access.event.AuthorizedEvent;\nimport org.springframework.security.access.event.PublicInvocationEvent;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Abstract class that implements security interception for secure objects.\n * <p>\n * The <code>AbstractSecurityInterceptor</code> will ensure the proper startup\n * configuration of the security interceptor. It will also implement the proper handling\n * of secure object invocations, namely:\n * <ol>\n * <li>Obtain the {@link Authentication} object from the\n * {@link SecurityContextHolder}.</li>\n * <li>Determine if the request relates to a secured or public invocation by looking up\n * the secure object request against the {@link SecurityMetadataSource}.</li>\n * <li>For an invocation that is secured (there is a list of <code>ConfigAttribute</code>s\n * for the secure object invocation):\n * <ol type=\"a\">\n * <li>If either the\n * {@link org.springframework.security.core.Authentication#isAuthenticated()} returns\n * <code>false</code>, or the {@link #alwaysReauthenticate} is <code>true</code>,\n * authenticate the request against the configured {@link AuthenticationManager}. When\n * authenticated, replace the <code>Authentication</code> object on the\n * <code>SecurityContextHolder</code> with the returned value.</li>\n * <li>Authorize the request against the configured {@link AccessDecisionManager}.</li>\n * <li>Perform any run-as replacement via the configured {@link RunAsManager}.</li>\n * <li>Pass control back to the concrete subclass, which will actually proceed with\n * executing the object. A {@link InterceptorStatusToken} is returned so that after the\n * subclass has finished proceeding with execution of the object, its finally clause can\n * ensure the <code>AbstractSecurityInterceptor</code> is re-called and tidies up\n * correctly using {@link #finallyInvocation(InterceptorStatusToken)}.</li>\n * <li>The concrete subclass will re-call the <code>AbstractSecurityInterceptor</code> via\n * the {@link #afterInvocation(InterceptorStatusToken, Object)} method.</li>\n * <li>If the <code>RunAsManager</code> replaced the <code>Authentication</code> object,\n * return the <code>SecurityContextHolder</code> to the object that existed after the call\n * to <code>AuthenticationManager</code>.</li>\n * <li>If an <code>AfterInvocationManager</code> is defined, invoke the invocation manager\n * and allow it to replace the object due to be returned to the caller.</li>\n * </ol>\n * </li>\n * <li>For an invocation that is public (there are no <code>ConfigAttribute</code>s for\n * the secure object invocation):\n * <ol type=\"a\">\n * <li>As described above, the concrete subclass will be returned an\n * <code>InterceptorStatusToken</code> which is subsequently re-presented to the\n * <code>AbstractSecurityInterceptor</code> after the secure object has been executed. The\n * <code>AbstractSecurityInterceptor</code> will take no further action when its\n * {@link #afterInvocation(InterceptorStatusToken, Object)} is called.</li>\n * </ol>\n * </li>\n * <li>Control again returns to the concrete subclass, along with the <code>Object</code>\n * that should be returned to the caller. The subclass will then return that result or\n * exception to the original caller.</li>\n * </ol>\n *\n * @author Ben Alex\n * @author Rob Winch\n * @deprecated Use\n * {@link org.springframework.security.web.access.intercept.AuthorizationFilter} instead\n * for filter security,\n * {@link org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor}\n * for messaging security, or\n * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}\n * and\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}\n * for method security.\n */\n@NullUnmarked\n@Deprecated\npublic abstract class AbstractSecurityInterceptor\n\t\timplements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable ApplicationEventPublisher eventPublisher;\n\n\tprivate @Nullable AccessDecisionManager accessDecisionManager;\n\n\tprivate @Nullable AfterInvocationManager afterInvocationManager;\n\n\tprivate AuthenticationManager authenticationManager = new NoOpAuthenticationManager();\n\n\tprivate RunAsManager runAsManager = new NullRunAsManager();\n\n\tprivate boolean alwaysReauthenticate = false;\n\n\tprivate boolean rejectPublicInvocations = false;\n\n\tprivate boolean validateConfigAttributes = true;\n\n\tprivate boolean publishAuthorizationSuccess = false;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(getSecureObjectClass(), \"Subclass must provide a non-null response to getSecureObjectClass()\");\n\t\tAssert.notNull(this.messages, \"A message source must be set\");\n\t\tAssert.notNull(this.authenticationManager, \"An AuthenticationManager is required\");\n\t\tAssert.notNull(this.accessDecisionManager, \"An AccessDecisionManager is required\");\n\t\tAssert.notNull(this.runAsManager, \"A RunAsManager is required\");\n\t\tAssert.notNull(this.obtainSecurityMetadataSource(), \"An SecurityMetadataSource is required\");\n\t\tAssert.isTrue(this.obtainSecurityMetadataSource().supports(getSecureObjectClass()),\n\t\t\t\t() -> \"SecurityMetadataSource does not support secure object class: \" + getSecureObjectClass());\n\t\tAssert.isTrue(this.runAsManager.supports(getSecureObjectClass()),\n\t\t\t\t() -> \"RunAsManager does not support secure object class: \" + getSecureObjectClass());\n\t\tAssert.isTrue(this.accessDecisionManager.supports(getSecureObjectClass()),\n\t\t\t\t() -> \"AccessDecisionManager does not support secure object class: \" + getSecureObjectClass());\n\t\tif (this.afterInvocationManager != null) {\n\t\t\tAssert.isTrue(this.afterInvocationManager.supports(getSecureObjectClass()),\n\t\t\t\t\t() -> \"AfterInvocationManager does not support secure object class: \" + getSecureObjectClass());\n\t\t}\n\t\tif (this.validateConfigAttributes) {\n\t\t\tCollection<ConfigAttribute> attributeDefs = this.obtainSecurityMetadataSource().getAllConfigAttributes();\n\t\t\tif (attributeDefs == null) {\n\t\t\t\tthis.logger.warn(\"Could not validate configuration attributes as the \"\n\t\t\t\t\t\t+ \"SecurityMetadataSource did not return any attributes from getAllConfigAttributes()\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvalidateAttributeDefs(attributeDefs);\n\t\t}\n\t}\n\n\tprivate void validateAttributeDefs(Collection<ConfigAttribute> attributeDefs) {\n\t\tSet<ConfigAttribute> unsupportedAttrs = new HashSet<>();\n\t\tfor (ConfigAttribute attr : attributeDefs) {\n\t\t\tif (!this.runAsManager.supports(attr) && !this.accessDecisionManager.supports(attr)\n\t\t\t\t\t&& ((this.afterInvocationManager == null) || !this.afterInvocationManager.supports(attr))) {\n\t\t\t\tunsupportedAttrs.add(attr);\n\t\t\t}\n\t\t}\n\t\tif (unsupportedAttrs.size() != 0) {\n\t\t\tthis.logger\n\t\t\t\t.trace(\"Did not validate configuration attributes since validateConfigurationAttributes is false\");\n\t\t\tthrow new IllegalArgumentException(\"Unsupported configuration attributes: \" + unsupportedAttrs);\n\t\t}\n\t\telse {\n\t\t\tthis.logger.trace(\"Validated configuration attributes\");\n\t\t}\n\t}\n\n\tprotected @Nullable InterceptorStatusToken beforeInvocation(Object object) {\n\t\tAssert.notNull(object, \"Object was null\");\n\t\tif (!getSecureObjectClass().isAssignableFrom(object.getClass())) {\n\t\t\tthrow new IllegalArgumentException(\"Security invocation attempted for object \" + object.getClass().getName()\n\t\t\t\t\t+ \" but AbstractSecurityInterceptor only configured to support secure objects of type: \"\n\t\t\t\t\t+ getSecureObjectClass());\n\t\t}\n\t\tCollection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);\n\t\tif (CollectionUtils.isEmpty(attributes)) {\n\t\t\tAssert.isTrue(!this.rejectPublicInvocations,\n\t\t\t\t\t() -> \"Secure object invocation \" + object\n\t\t\t\t\t\t\t+ \" was denied as public invocations are not allowed via this interceptor. \"\n\t\t\t\t\t\t\t+ \"This indicates a configuration error because the \"\n\t\t\t\t\t\t\t+ \"rejectPublicInvocations property is set to 'true'\");\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Authorized public object %s\", object));\n\t\t\t}\n\t\t\tpublishEvent(new PublicInvocationEvent(object));\n\t\t\treturn null; // no further work post-invocation\n\t\t}\n\t\tif (this.securityContextHolderStrategy.getContext().getAuthentication() == null) {\n\t\t\tcredentialsNotFound(this.messages.getMessage(\"AbstractSecurityInterceptor.authenticationNotFound\",\n\t\t\t\t\t\"An Authentication object was not found in the SecurityContext\"), object, attributes);\n\t\t}\n\t\tAuthentication authenticated = authenticateIfRequired();\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(LogMessage.format(\"Authorizing %s with attributes %s\", object, attributes));\n\t\t}\n\t\t// Attempt authorization\n\t\tattemptAuthorization(object, attributes, authenticated);\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Authorized %s with attributes %s\", object, attributes));\n\t\t}\n\t\tif (this.publishAuthorizationSuccess) {\n\t\t\tpublishEvent(new AuthorizedEvent(object, attributes, authenticated));\n\t\t}\n\n\t\t// Attempt to run as a different user\n\t\tAuthentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);\n\t\tif (runAs != null) {\n\t\t\tSecurityContext origCtx = this.securityContextHolderStrategy.getContext();\n\t\t\tSecurityContext newCtx = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\tnewCtx.setAuthentication(runAs);\n\t\t\tthis.securityContextHolderStrategy.setContext(newCtx);\n\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Switched to RunAs authentication %s\", runAs));\n\t\t\t}\n\t\t\t// need to revert to token.Authenticated post-invocation\n\t\t\treturn new InterceptorStatusToken(origCtx, true, attributes, object);\n\t\t}\n\t\tthis.logger.trace(\"Did not switch RunAs authentication since RunAsManager returned null\");\n\t\t// no further work post-invocation\n\t\treturn new InterceptorStatusToken(this.securityContextHolderStrategy.getContext(), false, attributes, object);\n\n\t}\n\n\tprivate void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,\n\t\t\tAuthentication authenticated) {\n\t\ttry {\n\t\t\tthis.accessDecisionManager.decide(authenticated, object, attributes);\n\t\t}\n\t\tcatch (AccessDeniedException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Failed to authorize %s with attributes %s using %s\", object,\n\t\t\t\t\t\tattributes, this.accessDecisionManager));\n\t\t\t}\n\t\t\telse if (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Failed to authorize %s with attributes %s\", object, attributes));\n\t\t\t}\n\t\t\tpublishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/**\n\t * Cleans up the work of the <tt>AbstractSecurityInterceptor</tt> after the secure\n\t * object invocation has been completed. This method should be invoked after the\n\t * secure object invocation and before afterInvocation regardless of the secure object\n\t * invocation returning successfully (i.e. it should be done in a finally block).\n\t * @param token as returned by the {@link #beforeInvocation(Object)} method\n\t */\n\tprotected void finallyInvocation(InterceptorStatusToken token) {\n\t\tif (token != null && token.isContextHolderRefreshRequired()) {\n\t\t\tthis.securityContextHolderStrategy.setContext(token.getSecurityContext());\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage\n\t\t\t\t\t.of(() -> \"Reverted to original authentication \" + token.getSecurityContext().getAuthentication()));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Completes the work of the <tt>AbstractSecurityInterceptor</tt> after the secure\n\t * object invocation has been completed.\n\t * @param token as returned by the {@link #beforeInvocation(Object)} method\n\t * @param returnedObject any object returned from the secure object invocation (may be\n\t * <tt>null</tt>)\n\t * @return the object the secure object invocation should ultimately return to its\n\t * caller (may be <tt>null</tt>)\n\t */\n\tprotected Object afterInvocation(InterceptorStatusToken token, @Nullable Object returnedObject) {\n\t\tif (token == null) {\n\t\t\t// public object\n\t\t\treturn returnedObject;\n\t\t}\n\t\tfinallyInvocation(token); // continue to clean in this method for passivity\n\t\tif (this.afterInvocationManager != null) {\n\t\t\t// Attempt after invocation handling\n\t\t\ttry {\n\t\t\t\treturnedObject = this.afterInvocationManager.decide(token.getSecurityContext().getAuthentication(),\n\t\t\t\t\t\ttoken.getSecureObject(), token.getAttributes(), returnedObject);\n\t\t\t}\n\t\t\tcatch (AccessDeniedException ex) {\n\t\t\t\tpublishEvent(new AuthorizationFailureEvent(token.getSecureObject(), token.getAttributes(),\n\t\t\t\t\t\ttoken.getSecurityContext().getAuthentication(), ex));\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t}\n\t\treturn returnedObject;\n\t}\n\n\t/**\n\t * Checks the current authentication token and passes it to the AuthenticationManager\n\t * if {@link org.springframework.security.core.Authentication#isAuthenticated()}\n\t * returns false or the property <tt>alwaysReauthenticate</tt> has been set to true.\n\t * @return an authenticated <tt>Authentication</tt> object.\n\t */\n\tprivate Authentication authenticateIfRequired() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication.isAuthenticated() && !this.alwaysReauthenticate) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Did not re-authenticate %s before authorizing\", authentication));\n\t\t\t}\n\t\t\treturn authentication;\n\t\t}\n\t\tauthentication = this.authenticationManager.authenticate(authentication);\n\t\t// Don't authenticated.setAuthentication(true) because each provider does that\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Re-authenticated %s before authorizing\", authentication));\n\t\t}\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\treturn authentication;\n\t}\n\n\t/**\n\t * Helper method which generates an exception containing the passed reason, and\n\t * publishes an event to the application context.\n\t * <p>\n\t * Always throws an exception.\n\t * @param reason to be provided in the exception detail\n\t * @param secureObject that was being called\n\t * @param configAttribs that were defined for the secureObject\n\t */\n\tprivate void credentialsNotFound(String reason, Object secureObject, Collection<ConfigAttribute> configAttribs) {\n\t\tAuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException(reason);\n\t\tAuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(secureObject,\n\t\t\t\tconfigAttribs, exception);\n\t\tpublishEvent(event);\n\t\tthrow exception;\n\t}\n\n\tpublic AccessDecisionManager getAccessDecisionManager() {\n\t\treturn this.accessDecisionManager;\n\t}\n\n\tpublic AfterInvocationManager getAfterInvocationManager() {\n\t\treturn this.afterInvocationManager;\n\t}\n\n\tpublic AuthenticationManager getAuthenticationManager() {\n\t\treturn this.authenticationManager;\n\t}\n\n\tpublic RunAsManager getRunAsManager() {\n\t\treturn this.runAsManager;\n\t}\n\n\t/**\n\t * Indicates the type of secure objects the subclass will be presenting to the\n\t * abstract parent for processing. This is used to ensure collaborators wired to the\n\t * {@code AbstractSecurityInterceptor} all support the indicated secure object class.\n\t * @return the type of secure object the subclass provides services for\n\t */\n\tpublic abstract Class<?> getSecureObjectClass();\n\n\tpublic boolean isAlwaysReauthenticate() {\n\t\treturn this.alwaysReauthenticate;\n\t}\n\n\tpublic boolean isRejectPublicInvocations() {\n\t\treturn this.rejectPublicInvocations;\n\t}\n\n\tpublic boolean isValidateConfigAttributes() {\n\t\treturn this.validateConfigAttributes;\n\t}\n\n\tpublic abstract SecurityMetadataSource obtainSecurityMetadataSource();\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tpublic void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {\n\t\tthis.accessDecisionManager = accessDecisionManager;\n\t}\n\n\tpublic void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {\n\t\tthis.afterInvocationManager = afterInvocationManager;\n\t}\n\n\t/**\n\t * Indicates whether the <code>AbstractSecurityInterceptor</code> should ignore the\n\t * {@link Authentication#isAuthenticated()} property. Defaults to <code>false</code>,\n\t * meaning by default the <code>Authentication.isAuthenticated()</code> property is\n\t * trusted and re-authentication will not occur if the principal has already been\n\t * authenticated.\n\t * @param alwaysReauthenticate <code>true</code> to force\n\t * <code>AbstractSecurityInterceptor</code> to disregard the value of\n\t * <code>Authentication.isAuthenticated()</code> and always re-authenticate the\n\t * request (defaults to <code>false</code>).\n\t */\n\tpublic void setAlwaysReauthenticate(boolean alwaysReauthenticate) {\n\t\tthis.alwaysReauthenticate = alwaysReauthenticate;\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tthis.eventPublisher = applicationEventPublisher;\n\t}\n\n\tpublic void setAuthenticationManager(AuthenticationManager newManager) {\n\t\tthis.authenticationManager = newManager;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t/**\n\t * Only {@code AuthorizationFailureEvent} will be published. If you set this property\n\t * to {@code true}, {@code AuthorizedEvent}s will also be published.\n\t * @param publishAuthorizationSuccess default value is {@code false}\n\t */\n\tpublic void setPublishAuthorizationSuccess(boolean publishAuthorizationSuccess) {\n\t\tthis.publishAuthorizationSuccess = publishAuthorizationSuccess;\n\t}\n\n\t/**\n\t * By rejecting public invocations (and setting this property to <tt>true</tt>),\n\t * essentially you are ensuring that every secure object invocation advised by\n\t * <code>AbstractSecurityInterceptor</code> has a configuration attribute defined.\n\t * This is useful to ensure a \"fail safe\" mode where undeclared secure objects will be\n\t * rejected and configuration omissions detected early. An\n\t * <tt>IllegalArgumentException</tt> will be thrown by the\n\t * <tt>AbstractSecurityInterceptor</tt> if you set this property to <tt>true</tt> and\n\t * an attempt is made to invoke a secure object that has no configuration attributes.\n\t * @param rejectPublicInvocations set to <code>true</code> to reject invocations of\n\t * secure objects that have no configuration attributes (by default it is\n\t * <code>false</code> which treats undeclared secure objects as \"public\" or\n\t * unauthorized).\n\t */\n\tpublic void setRejectPublicInvocations(boolean rejectPublicInvocations) {\n\t\tthis.rejectPublicInvocations = rejectPublicInvocations;\n\t}\n\n\tpublic void setRunAsManager(RunAsManager runAsManager) {\n\t\tthis.runAsManager = runAsManager;\n\t}\n\n\tpublic void setValidateConfigAttributes(boolean validateConfigAttributes) {\n\t\tthis.validateConfigAttributes = validateConfigAttributes;\n\t}\n\n\tprivate void publishEvent(ApplicationEvent event) {\n\t\tif (this.eventPublisher != null) {\n\t\t\tthis.eventPublisher.publishEvent(event);\n\t\t}\n\t}\n\n\tprivate static class NoOpAuthenticationManager implements AuthenticationManager {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\tthrow new AuthenticationServiceException(\"Cannot authenticate \" + authentication);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/AfterInvocationManager.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Reviews the <code>Object</code> returned from a secure object invocation, being able to\n * modify the <code>Object</code> or throw an {@link AccessDeniedException}.\n * <p>\n * Typically used to ensure the principal is permitted to access the domain object\n * instance returned by a service layer bean. Can also be used to mutate the domain object\n * instance so the principal is only able to access authorised bean properties or\n * <code>Collection</code> elements.\n * <p>\n * Special consideration should be given to using an <code>AfterInvocationManager</code>\n * on bean methods that modify a database. Typically an\n * <code>AfterInvocationManager</code> is used with read-only methods, such as\n * <code>public DomainObject getById(id)</code>. If used with methods that modify a\n * database, a transaction manager should be used to ensure any\n * <code>AccessDeniedException</code> will cause a rollback of the changes made by the\n * transaction.\n * </p>\n *\n * @author Ben Alex\n * @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor\n * @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor\n * @deprecated Use delegation with {@link AuthorizationManager}\n */\n@Deprecated\npublic interface AfterInvocationManager {\n\n\t/**\n\t * Given the details of a secure object invocation including its returned\n\t * <code>Object</code>, make an access control decision or optionally modify the\n\t * returned <code>Object</code>.\n\t * @param authentication the caller that invoked the method\n\t * @param object the secured object that was called\n\t * @param attributes the configuration attributes associated with the secured object\n\t * that was invoked\n\t * @param returnedObject the <code>Object</code> that was returned from the secure\n\t * object invocation\n\t * @return the <code>Object</code> that will ultimately be returned to the caller (if\n\t * an implementation does not wish to modify the object to be returned to the caller,\n\t * the implementation should simply return the same object it was passed by the\n\t * <code>returnedObject</code> method argument)\n\t * @throws AccessDeniedException if access is denied\n\t */\n\tObject decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,\n\t\t\tObject returnedObject) throws AccessDeniedException;\n\n\t/**\n\t * Indicates whether this <code>AfterInvocationManager</code> is able to process\n\t * \"after invocation\" requests presented with the passed <code>ConfigAttribute</code>.\n\t * <p>\n\t * This allows the <code>AbstractSecurityInterceptor</code> to check every\n\t * configuration attribute can be consumed by the configured\n\t * <code>AccessDecisionManager</code> and/or <code>RunAsManager</code> and/or\n\t * <code>AfterInvocationManager</code>.\n\t * </p>\n\t * @param attribute a configuration attribute that has been configured against the\n\t * <code>AbstractSecurityInterceptor</code>\n\t * @return true if this <code>AfterInvocationManager</code> can support the passed\n\t * configuration attribute\n\t */\n\tboolean supports(ConfigAttribute attribute);\n\n\t/**\n\t * Indicates whether the <code>AfterInvocationManager</code> implementation is able to\n\t * provide access control decisions for the indicated secured object type.\n\t * @param clazz the class that is being queried\n\t * @return <code>true</code> if the implementation can process the indicated class\n\t */\n\tboolean supports(Class<?> clazz);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/AfterInvocationProviderManager.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.AfterInvocationProvider;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Provider-based implementation of {@link AfterInvocationManager}.\n * <p>\n * Handles configuration of a bean context defined list of {@link AfterInvocationProvider}\n * s.\n * <p>\n * Every <code>AfterInvocationProvider</code> will be polled when the\n * {@link #decide(Authentication, Object, Collection, Object)} method is called. The\n * <code>Object</code> returned from each provider will be presented to the successive\n * provider for processing. This means each provider <b>must</b> ensure they return the\n * <code>Object</code>, even if they are not interested in the \"after invocation\" decision\n * (perhaps as the secure object invocation did not include a configuration attribute a\n * given provider is configured to respond to).\n *\n * @author Ben Alex\n * @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor\n * @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor\n * @deprecated Use delegation with {@link AuthorizationManager}\n */\n@NullUnmarked\n@Deprecated\npublic class AfterInvocationProviderManager implements AfterInvocationManager, InitializingBean {\n\n\tprotected static final Log logger = LogFactory.getLog(AfterInvocationProviderManager.class);\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable List<AfterInvocationProvider> providers;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tcheckIfValidList(this.providers);\n\t}\n\n\t@Override\n\tpublic Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,\n\t\t\tObject returnedObject) throws AccessDeniedException {\n\t\tObject result = returnedObject;\n\t\tfor (AfterInvocationProvider provider : this.providers) {\n\t\t\tresult = provider.decide(authentication, object, config, result);\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic List<AfterInvocationProvider> getProviders() {\n\t\treturn this.providers;\n\t}\n\n\tpublic void setProviders(List<?> newList) {\n\t\tcheckIfValidList(newList);\n\t\tthis.providers = new ArrayList<>(newList.size());\n\t\tfor (Object currentObject : newList) {\n\t\t\tAssert.isInstanceOf(AfterInvocationProvider.class, currentObject, () -> \"AfterInvocationProvider \"\n\t\t\t\t\t+ currentObject.getClass().getName() + \" must implement AfterInvocationProvider\");\n\t\t\tthis.providers.add((AfterInvocationProvider) currentObject);\n\t\t}\n\t}\n\n\tprivate void checkIfValidList(List<?> listToCheck) {\n\t\tAssert.isTrue(!CollectionUtils.isEmpty(listToCheck), \"A list of AfterInvocationProviders is required\");\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\tfor (AfterInvocationProvider provider : this.providers) {\n\t\t\tlogger.debug(LogMessage.format(\"Evaluating %s against %s\", attribute, provider));\n\t\t\tif (provider.supports(attribute)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Iterates through all <code>AfterInvocationProvider</code>s and ensures each can\n\t * support the presented class.\n\t * <p>\n\t * If one or more providers cannot support the presented class, <code>false</code> is\n\t * returned.\n\t * @param clazz the secure object class being queries\n\t * @return if the <code>AfterInvocationProviderManager</code> can support the secure\n\t * object class, which requires every one of its <code>AfterInvocationProvider</code>s\n\t * to support the secure object class\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\tfor (AfterInvocationProvider provider : this.providers) {\n\t\t\tif (!provider.supports(clazz)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/InterceptorStatusToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * A return object received by {@link AbstractSecurityInterceptor} subclasses.\n * <p>\n * This class reflects the status of the security interception, so that the final call to\n * {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor#afterInvocation(InterceptorStatusToken, Object)}\n * can tidy up correctly.\n *\n * @author Ben Alex\n * @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor\n * @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor\n * @deprecated Use delegation with {@link AuthorizationManager}\n */\n@Deprecated\npublic class InterceptorStatusToken {\n\n\tprivate SecurityContext securityContext;\n\n\tprivate Collection<ConfigAttribute> attr;\n\n\tprivate Object secureObject;\n\n\tprivate boolean contextHolderRefreshRequired;\n\n\tpublic InterceptorStatusToken(SecurityContext securityContext, boolean contextHolderRefreshRequired,\n\t\t\tCollection<ConfigAttribute> attributes, Object secureObject) {\n\t\tthis.securityContext = securityContext;\n\t\tthis.contextHolderRefreshRequired = contextHolderRefreshRequired;\n\t\tthis.attr = attributes;\n\t\tthis.secureObject = secureObject;\n\t}\n\n\tpublic Collection<ConfigAttribute> getAttributes() {\n\t\treturn this.attr;\n\t}\n\n\tpublic SecurityContext getSecurityContext() {\n\t\treturn this.securityContext;\n\t}\n\n\tpublic Object getSecureObject() {\n\t\treturn this.secureObject;\n\t}\n\n\tpublic boolean isContextHolderRefreshRequired() {\n\t\treturn this.contextHolderRefreshRequired;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/MethodInvocationPrivilegeEvaluator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Collection;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Allows users to determine whether they have \"before invocation\" privileges for a given\n * method invocation.\n * <p>\n * Of course, if an\n * {@link org.springframework.security.access.intercept.AfterInvocationManager} is used to\n * authorize the <em>result</em> of a method invocation, this class cannot assist\n * determine whether or not the <code>AfterInvocationManager</code> will enable access.\n * Instead this class aims to allow applications to determine whether or not the current\n * principal would be allowed to at least attempt to invoke the method, irrespective of\n * the \"after\" invocation handling.\n * </p>\n *\n * @author Ben Alex\n * @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}\n * instead\n */\n@NullUnmarked\n@Deprecated\npublic class MethodInvocationPrivilegeEvaluator implements InitializingBean {\n\n\tprotected static final Log logger = LogFactory.getLog(MethodInvocationPrivilegeEvaluator.class);\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable AbstractSecurityInterceptor securityInterceptor;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.securityInterceptor, \"SecurityInterceptor required\");\n\t}\n\n\tpublic boolean isAllowed(MethodInvocation invocation, Authentication authentication) {\n\t\tAssert.notNull(invocation, \"MethodInvocation required\");\n\t\tAssert.notNull(invocation.getMethod(), \"MethodInvocation must provide a non-null getMethod()\");\n\t\tCollection<ConfigAttribute> attrs = this.securityInterceptor.obtainSecurityMetadataSource()\n\t\t\t.getAttributes(invocation);\n\t\tif (attrs == null) {\n\t\t\treturn !this.securityInterceptor.isRejectPublicInvocations();\n\t\t}\n\t\tif (authentication == null || authentication.getAuthorities().isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tthis.securityInterceptor.getAccessDecisionManager().decide(authentication, invocation, attrs);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (AccessDeniedException unauthorized) {\n\t\t\tlogger.debug(LogMessage.format(\"%s denied for %s\", invocation, authentication), unauthorized);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic void setSecurityInterceptor(AbstractSecurityInterceptor securityInterceptor) {\n\t\tAssert.notNull(securityInterceptor, \"AbstractSecurityInterceptor cannot be null\");\n\t\tAssert.isTrue(MethodInvocation.class.equals(securityInterceptor.getSecureObjectClass()),\n\t\t\t\t\"AbstractSecurityInterceptor does not support MethodInvocations\");\n\t\tAssert.notNull(securityInterceptor.getAccessDecisionManager(),\n\t\t\t\t\"AbstractSecurityInterceptor must provide a non-null AccessDecisionManager\");\n\t\tthis.securityInterceptor = securityInterceptor;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/NullRunAsManager.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Implementation of a {@link RunAsManager} that does nothing.\n * <p>\n * This class should be used if you do not require run-as authentication replacement\n * functionality.\n *\n * @author Ben Alex\n * @deprecated please see {@link RunAsManager} deprecation notice\n */\n@NullUnmarked\n@Deprecated\nfinal class NullRunAsManager implements RunAsManager {\n\n\t@Override\n\tpublic @Nullable Authentication buildRunAs(Authentication authentication, Object object,\n\t\t\tCollection<ConfigAttribute> config) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation that can authenticate a\n * {@link RunAsUserToken}.\n * <P>\n * Configured in the bean context with a key that should match the key used by adapters to\n * generate the <code>RunAsUserToken</code>. It treats as valid any\n * <code>RunAsUserToken</code> instance presenting a hash code that matches the\n * <code>RunAsImplAuthenticationProvider</code>-configured key.\n * </p>\n * <P>\n * If the key does not match, a <code>BadCredentialsException</code> is thrown.\n * </p>\n *\n * @deprecated Authentication is now separated from authorization in Spring Security. This\n * class is only used by now-deprecated components. There is not yet an equivalent\n * replacement in Spring Security.\n */\n@NullUnmarked\n@Deprecated\npublic class RunAsImplAuthenticationProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware {\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable String key;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.key, \"A Key is required and should match that configured for the RunAsManagerImpl\");\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tRunAsUserToken token = (RunAsUserToken) authentication;\n\t\tif (token.getKeyHash() != this.key.hashCode()) {\n\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"RunAsImplAuthenticationProvider.incorrectKey\",\n\t\t\t\t\t\"The presented RunAsUserToken does not contain the expected key\"));\n\t\t}\n\t\treturn authentication;\n\t}\n\n\tpublic String getKey() {\n\t\treturn this.key;\n\t}\n\n\tpublic void setKey(String key) {\n\t\tthis.key = key;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn RunAsUserToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/RunAsManager.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Creates a new temporary {@link Authentication} object for the current secure object\n * invocation only.\n *\n * <p>\n * This interface permits implementations to replace the <code>Authentication</code>\n * object that applies to the current secure object invocation only. The\n * {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor} will\n * replace the <code>Authentication</code> object held in the\n * {@link org.springframework.security.core.context.SecurityContext SecurityContext} for\n * the duration of the secure object callback only, returning it to the original\n * <code>Authentication</code> object when the callback ends.\n * </p>\n *\n * <p>\n * This is provided so that systems with two layers of objects can be established. One\n * layer is public facing and has normal secure methods with the granted authorities\n * expected to be held by external callers. The other layer is private, and is only\n * expected to be called by objects within the public facing layer. The objects in this\n * private layer still need security (otherwise they would be public methods) and they\n * also need security in such a manner that prevents them being called directly by\n * external callers. The objects in the private layer would be configured to require\n * granted authorities never granted to external callers. The <code>RunAsManager</code>\n * interface provides a mechanism to elevate security in this manner.\n * </p>\n *\n * <p>\n * It is expected implementations will provide a corresponding concrete\n * <code>Authentication</code> and <code>AuthenticationProvider</code> so that the\n * replacement <code>Authentication</code> object can be authenticated. Some form of\n * security will need to be implemented to ensure the <code>AuthenticationProvider</code>\n * only accepts <code>Authentication</code> objects created by an authorized concrete\n * implementation of <code>RunAsManager</code>.\n * </p>\n *\n * @author Ben Alex\n * @deprecated Authentication is now separated from authorization in Spring Security. This\n * class is only used by now-deprecated components. There is not yet an equivalent\n * replacement in Spring Security.\n */\n@Deprecated\npublic interface RunAsManager {\n\n\t/**\n\t * Returns a replacement <code>Authentication</code> object for the current secure\n\t * object invocation, or <code>null</code> if replacement not required.\n\t * @param authentication the caller invoking the secure object\n\t * @param object the secured object being called\n\t * @param attributes the configuration attributes associated with the secure object\n\t * being invoked\n\t * @return a replacement object to be used for duration of the secure object\n\t * invocation, or <code>null</code> if the <code>Authentication</code> should be left\n\t * as is\n\t */\n\tAuthentication buildRunAs(Authentication authentication, Object object, Collection<ConfigAttribute> attributes);\n\n\t/**\n\t * Indicates whether this <code>RunAsManager</code> is able to process the passed\n\t * <code>ConfigAttribute</code>.\n\t * <p>\n\t * This allows the <code>AbstractSecurityInterceptor</code> to check every\n\t * configuration attribute can be consumed by the configured\n\t * <code>AccessDecisionManager</code> and/or <code>RunAsManager</code> and/or\n\t * <code>AfterInvocationManager</code>.\n\t * </p>\n\t * @param attribute a configuration attribute that has been configured against the\n\t * <code>AbstractSecurityInterceptor</code>\n\t * @return <code>true</code> if this <code>RunAsManager</code> can support the passed\n\t * configuration attribute\n\t */\n\tboolean supports(ConfigAttribute attribute);\n\n\t/**\n\t * Indicates whether the <code>RunAsManager</code> implementation is able to provide\n\t * run-as replacement for the indicated secure object type.\n\t * @param clazz the class that is being queried\n\t * @return true if the implementation can process the indicated class\n\t */\n\tboolean supports(Class<?> clazz);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/RunAsManagerImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Basic concrete implementation of a {@link RunAsManager}.\n * <p>\n * Is activated if any {@link ConfigAttribute#getAttribute()} is prefixed with\n * <Code>RUN_AS_</code>. If found, it generates a new {@link RunAsUserToken} containing\n * the same principal, credentials and granted authorities as the original\n * {@link Authentication} object, along with {@link SimpleGrantedAuthority}s for each\n * <code>RUN_AS_</code> indicated. The created <code>SimpleGrantedAuthority</code>s will\n * be prefixed with a special prefix indicating that it is a role (default prefix value is\n * <code>ROLE_</code>), and then the remainder of the <code>RUN_AS_</code> keyword. For\n * example, <code>RUN_AS_FOO</code> will result in the creation of a granted authority of\n * <code>ROLE_RUN_AS_FOO</code>.\n * <p>\n * The role prefix may be overridden from the default, to match that used elsewhere, for\n * example when using an existing role database with another prefix. An empty role prefix\n * may also be specified. Note however that there are potential issues with using an empty\n * role prefix since different categories of {@link ConfigAttribute} can not be properly\n * discerned based on the prefix, with possible consequences when performing voting and\n * other actions. However, this option may be of some use when using pre-existing role\n * names without a prefix, and no ability exists to prefix them with a role prefix on\n * reading them in, such as provided for example in\n * {@link org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl}.\n *\n * @author Ben Alex\n * @author colin sampaleanu\n * @deprecated Authentication is now separated from authorization in Spring Security. This\n * class is only used by now-deprecated components. There is not yet an equivalent\n * replacement in Spring Security.\n */\n@NullUnmarked\n@Deprecated\npublic class RunAsManagerImpl implements RunAsManager, InitializingBean {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable String key;\n\n\tprivate String rolePrefix = \"ROLE_\";\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.key,\n\t\t\t\t\"A Key is required and should match that configured for the RunAsImplAuthenticationProvider\");\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication buildRunAs(Authentication authentication, Object object,\n\t\t\tCollection<ConfigAttribute> attributes) {\n\t\tList<GrantedAuthority> newAuthorities = new ArrayList<>();\n\t\tfor (ConfigAttribute attribute : attributes) {\n\t\t\tif (this.supports(attribute)) {\n\t\t\t\tGrantedAuthority extraAuthority = new SimpleGrantedAuthority(\n\t\t\t\t\t\tgetRolePrefix() + attribute.getAttribute());\n\t\t\t\tnewAuthorities.add(extraAuthority);\n\t\t\t}\n\t\t}\n\t\tif (newAuthorities.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\t// Add existing authorities\n\t\tnewAuthorities.addAll(authentication.getAuthorities());\n\t\treturn new RunAsUserToken(this.key, authentication.getPrincipal(), authentication.getCredentials(),\n\t\t\t\tnewAuthorities, authentication.getClass());\n\t}\n\n\tpublic String getKey() {\n\t\treturn this.key;\n\t}\n\n\tpublic String getRolePrefix() {\n\t\treturn this.rolePrefix;\n\t}\n\n\tpublic void setKey(String key) {\n\t\tthis.key = key;\n\t}\n\n\t/**\n\t * Allows the default role prefix of <code>ROLE_</code> to be overridden. May be set\n\t * to an empty value, although this is usually not desirable.\n\t * @param rolePrefix the new prefix\n\t */\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn attribute.getAttribute() != null && attribute.getAttribute().startsWith(\"RUN_AS_\");\n\t}\n\n\t/**\n\t * This implementation supports any type of class, because it does not query the\n\t * presented secure object.\n\t * @param clazz the secure object\n\t * @return always <code>true</code>\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/RunAsUserToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Collection;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * An immutable {@link org.springframework.security.core.Authentication} implementation\n * that supports {@link RunAsManagerImpl}.\n *\n * @author Ben Alex\n * @deprecated Authentication is now separated from authorization in Spring Security. This\n * class is only used by now-deprecated components. There is not yet an equivalent\n * replacement in Spring Security.\n */\n@Deprecated\npublic class RunAsUserToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final Class<? extends Authentication> originalAuthentication;\n\n\tprivate final Object credentials;\n\n\tprivate final Object principal;\n\n\tprivate final int keyHash;\n\n\tpublic RunAsUserToken(String key, Object principal, Object credentials,\n\t\t\tCollection<? extends GrantedAuthority> authorities,\n\t\t\tClass<? extends Authentication> originalAuthentication) {\n\t\tsuper(authorities);\n\t\tthis.keyHash = key.hashCode();\n\t\tthis.principal = principal;\n\t\tthis.credentials = credentials;\n\t\tthis.originalAuthentication = originalAuthentication;\n\t\tsetAuthenticated(true);\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\tpublic int getKeyHash() {\n\t\treturn this.keyHash;\n\t}\n\n\tpublic Class<? extends Authentication> getOriginalAuthentication() {\n\t\treturn this.originalAuthentication;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder(super.toString());\n\t\tString className = (this.originalAuthentication != null) ? this.originalAuthentication.getName() : null;\n\t\tsb.append(\"; Original Class: \").append(className);\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/aopalliance/MethodSecurityInterceptor.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aopalliance;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.SecurityMetadataSource;\nimport org.springframework.security.access.intercept.AbstractSecurityInterceptor;\nimport org.springframework.security.access.intercept.InterceptorStatusToken;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\n\n/**\n * Provides security interception of AOP Alliance based method invocations.\n * <p>\n * The <code>SecurityMetadataSource</code> required by this security interceptor is of\n * type {@link MethodSecurityMetadataSource}. This is shared with the AspectJ based\n * security interceptor (<code>AspectJSecurityInterceptor</code>), since both work with\n * Java <code>Method</code>s.\n * <p>\n * Refer to {@link AbstractSecurityInterceptor} for details on the workflow.\n *\n * @author Ben Alex\n * @author Rob Winch\n * @deprecated Please use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}\n * and\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}\n * instead\n */\n@NullUnmarked\n@Deprecated\npublic class MethodSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {\n\n\tprivate @Nullable MethodSecurityMetadataSource securityMetadataSource;\n\n\t@Override\n\tpublic Class<?> getSecureObjectClass() {\n\t\treturn MethodInvocation.class;\n\t}\n\n\t/**\n\t * This method should be used to enforce security on a <code>MethodInvocation</code>.\n\t * @param mi The method being invoked which requires a security decision\n\t * @return The returned value from the method invocation (possibly modified by the\n\t * {@code AfterInvocationManager}).\n\t * @throws Throwable if any error occurs\n\t */\n\t@Override\n\tpublic Object invoke(MethodInvocation mi) throws Throwable {\n\t\tInterceptorStatusToken token = super.beforeInvocation(mi);\n\t\tObject result;\n\t\ttry {\n\t\t\tresult = mi.proceed();\n\t\t}\n\t\tfinally {\n\t\t\tsuper.finallyInvocation(token);\n\t\t}\n\t\treturn super.afterInvocation(token, result);\n\t}\n\n\tpublic MethodSecurityMetadataSource getSecurityMetadataSource() {\n\t\treturn this.securityMetadataSource;\n\t}\n\n\t@Override\n\tpublic SecurityMetadataSource obtainSecurityMetadataSource() {\n\t\treturn this.securityMetadataSource;\n\t}\n\n\tpublic void setSecurityMetadataSource(MethodSecurityMetadataSource newSource) {\n\t\tthis.securityMetadataSource = newSource;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/aopalliance/MethodSecurityMetadataSourceAdvisor.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aopalliance;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.support.AbstractPointcutAdvisor;\nimport org.springframework.aop.support.StaticMethodMatcherPointcut;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Advisor driven by a {@link MethodSecurityMetadataSource}, used to exclude a\n * {@link MethodInterceptor} from public (non-secure) methods.\n * <p>\n * Because the AOP framework caches advice calculations, this is normally faster than just\n * letting the <code>MethodInterceptor</code> run and find out itself that it has no work\n * to do.\n * <p>\n * This class also allows the use of Spring's {@code DefaultAdvisorAutoProxyCreator},\n * which makes configuration easier than setup a <code>ProxyFactoryBean</code> for each\n * object requiring security. Note that autoproxying is not supported for BeanFactory\n * implementations, as post-processing is automatic only for application contexts.\n * <p>\n * Based on Spring's TransactionAttributeSourceAdvisor.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @deprecated Use\n * <code>org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity</code>\n * or publish interceptors directly\n */\n@NullUnmarked\n@Deprecated\n@SuppressWarnings(\"serial\")\npublic class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {\n\n\tprivate transient MethodSecurityMetadataSource attributeSource;\n\n\tprivate transient @Nullable MethodInterceptor interceptor;\n\n\tprivate final Pointcut pointcut = new MethodSecurityMetadataSourcePointcut();\n\n\tprivate @Nullable BeanFactory beanFactory;\n\n\tprivate final String adviceBeanName;\n\n\tprivate final String metadataSourceBeanName;\n\n\tprivate transient volatile Object adviceMonitor = new Object();\n\n\t/**\n\t * Alternative constructor for situations where we want the advisor decoupled from the\n\t * advice. Instead the advice bean name should be set. This prevents eager\n\t * instantiation of the interceptor (and hence the AuthenticationManager). See\n\t * SEC-773, for example. The metadataSourceBeanName is used rather than a direct\n\t * reference to support serialization via a bean factory lookup.\n\t * @param adviceBeanName name of the MethodSecurityInterceptor bean\n\t * @param attributeSource the SecurityMetadataSource (should be the same as the one\n\t * used on the interceptor)\n\t * @param attributeSourceBeanName the bean name of the attributeSource (required for\n\t * serialization)\n\t */\n\tpublic MethodSecurityMetadataSourceAdvisor(String adviceBeanName, MethodSecurityMetadataSource attributeSource,\n\t\t\tString attributeSourceBeanName) {\n\t\tAssert.notNull(adviceBeanName, \"The adviceBeanName cannot be null\");\n\t\tAssert.notNull(attributeSource, \"The attributeSource cannot be null\");\n\t\tAssert.notNull(attributeSourceBeanName, \"The attributeSourceBeanName cannot be null\");\n\t\tthis.adviceBeanName = adviceBeanName;\n\t\tthis.attributeSource = attributeSource;\n\t\tthis.metadataSourceBeanName = attributeSourceBeanName;\n\t}\n\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\tsynchronized (this.adviceMonitor) {\n\t\t\tif (this.interceptor == null) {\n\t\t\t\tAssert.notNull(this.adviceBeanName, \"'adviceBeanName' must be set for use with bean factory lookup.\");\n\t\t\t\tAssert.state(this.beanFactory != null, \"BeanFactory must be set to resolve 'adviceBeanName'\");\n\t\t\t\tthis.interceptor = this.beanFactory.getBean(this.adviceBeanName, MethodInterceptor.class);\n\t\t\t}\n\t\t\treturn this.interceptor;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\tthis.beanFactory = beanFactory;\n\t}\n\n\tprivate void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {\n\t\tois.defaultReadObject();\n\t\tthis.adviceMonitor = new Object();\n\t\tthis.attributeSource = this.beanFactory.getBean(this.metadataSourceBeanName,\n\t\t\t\tMethodSecurityMetadataSource.class);\n\t}\n\n\tclass MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {\n\n\t\t@Override\n\t\tpublic boolean matches(Method m, Class<?> targetClass) {\n\t\t\tMethodSecurityMetadataSource source = MethodSecurityMetadataSourceAdvisor.this.attributeSource;\n\t\t\treturn !CollectionUtils.isEmpty(source.getAttributes(m, targetClass));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/aopalliance/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Enforces security for AOP Alliance <code>MethodInvocation</code>s, such as via Spring\n * AOP.\n */\n@NullMarked\npackage org.springframework.security.access.intercept.aopalliance;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/aspectj/AspectJCallback.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aspectj;\n\n/**\n * Called by the {@link AspectJMethodSecurityInterceptor} when it wishes for the AspectJ\n * processing to continue. Typically implemented in the <code>around()</code> advice as a\n * simple <code>return proceed();</code> statement.\n *\n * @author Ben Alex\n * @deprecated This class will be removed from the public API. Please either use\n * `spring-security-aspects`, Spring Security's method security support or create your own\n * class that uses Spring AOP annotations.\n */\n@Deprecated\npublic interface AspectJCallback {\n\n\tObject proceedWithObject();\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/aspectj/AspectJMethodSecurityInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aspectj;\n\nimport org.aspectj.lang.JoinPoint;\n\nimport org.springframework.security.access.intercept.InterceptorStatusToken;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;\n\n/**\n * AspectJ {@code JoinPoint} security interceptor which wraps the {@code JoinPoint} in a\n * {@code MethodInvocation} adapter to make it compatible with security infrastructure\n * classes which only support {@code MethodInvocation}s.\n * <p>\n * One of the {@code invoke} methods should be called from the {@code around()} advice in\n * your aspect. Alternatively you can use one of the pre-defined aspects from the aspects\n * module.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.0.3\n * @deprecated This class will be removed from the public API. Please either use\n * `spring-security-aspects`, Spring Security's method security support or create your own\n * class that uses Spring AOP annotations.\n */\n@Deprecated\npublic final class AspectJMethodSecurityInterceptor extends MethodSecurityInterceptor {\n\n\t/**\n\t * Method that is suitable for user with @Aspect notation.\n\t * @param jp The AspectJ joint point being invoked which requires a security decision\n\t * @return The returned value from the method invocation\n\t * @throws Throwable if the invocation throws one\n\t */\n\tpublic Object invoke(JoinPoint jp) throws Throwable {\n\t\treturn super.invoke(new MethodInvocationAdapter(jp));\n\t}\n\n\t/**\n\t * Method that is suitable for user with traditional AspectJ-code aspects.\n\t * @param jp The AspectJ joint point being invoked which requires a security decision\n\t * @param advisorProceed the advice-defined anonymous class that implements\n\t * {@code AspectJCallback} containing a simple {@code return proceed();} statement\n\t * @return The returned value from the method invocation\n\t */\n\tpublic Object invoke(JoinPoint jp, AspectJCallback advisorProceed) {\n\t\tInterceptorStatusToken token = super.beforeInvocation(new MethodInvocationAdapter(jp));\n\t\tObject result;\n\t\ttry {\n\t\t\tresult = advisorProceed.proceedWithObject();\n\t\t}\n\t\tfinally {\n\t\t\tsuper.finallyInvocation(token);\n\t\t}\n\t\treturn super.afterInvocation(token, result);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/aspectj/MethodInvocationAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aspectj;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.reflect.CodeSignature;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.util.Assert;\n\n/**\n * Decorates a JoinPoint to allow it to be used with method-security infrastructure\n * classes which support {@code MethodInvocation} instances.\n *\n * @author Luke Taylor\n * @since 3.0.3\n * @deprecated This class will be removed from the public API. See\n * `JoinPointMethodInvocation` in `spring-security-aspects` for its replacement\n */\n@NullUnmarked\n@Deprecated\npublic final class MethodInvocationAdapter implements MethodInvocation {\n\n\tprivate final ProceedingJoinPoint jp;\n\n\tprivate final Method method;\n\n\tprivate final Object target;\n\n\tMethodInvocationAdapter(JoinPoint jp) {\n\t\tthis.jp = (ProceedingJoinPoint) jp;\n\t\tif (jp.getTarget() != null) {\n\t\t\tthis.target = jp.getTarget();\n\t\t}\n\t\telse {\n\t\t\t// SEC-1295: target may be null if an ITD is in use\n\t\t\tthis.target = jp.getSignature().getDeclaringType();\n\t\t}\n\t\tString targetMethodName = jp.getStaticPart().getSignature().getName();\n\t\tClass<?>[] types = ((CodeSignature) jp.getStaticPart().getSignature()).getParameterTypes();\n\t\tClass<?> declaringType = jp.getStaticPart().getSignature().getDeclaringType();\n\t\tthis.method = findMethod(targetMethodName, declaringType, types);\n\t\tAssert.notNull(this.method, () -> \"Could not obtain target method from JoinPoint: '\" + jp + \"'\");\n\t}\n\n\tprivate Method findMethod(String name, Class<?> declaringType, Class<?>[] params) {\n\t\tMethod method = null;\n\t\ttry {\n\t\t\tmethod = declaringType.getMethod(name, params);\n\t\t}\n\t\tcatch (NoSuchMethodException ignored) {\n\t\t}\n\t\tif (method == null) {\n\t\t\ttry {\n\t\t\t\tmethod = declaringType.getDeclaredMethod(name, params);\n\t\t\t}\n\t\t\tcatch (NoSuchMethodException ignored) {\n\t\t\t}\n\t\t}\n\t\treturn method;\n\t}\n\n\t@Override\n\tpublic Method getMethod() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic Object[] getArguments() {\n\t\treturn this.jp.getArgs();\n\t}\n\n\t@Override\n\tpublic AccessibleObject getStaticPart() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic Object getThis() {\n\t\treturn this.target;\n\t}\n\n\t@Override\n\tpublic Object proceed() throws Throwable {\n\t\treturn this.jp.proceed();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/aspectj/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Enforces security for AspectJ <code>JointPoint</code>s, delegating secure object\n * callbacks to the calling aspect.\n */\n@NullMarked\npackage org.springframework.security.access.intercept.aspectj;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/intercept/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Abstract level security interception classes which are responsible for enforcing the\n * configured security constraints for a secure object.\n * <p>\n * A <i>secure object</i> is a term frequently used throughout the security system. It\n * does <b>not</b> refer to a business object that is being secured, but instead refers to\n * some infrastructure object that can have security facilities provided for it by Spring\n * Security. For example, one secure object would be <code>MethodInvocation</code>, whilst\n * another would be HTTP {@code org.springframework.security.web.FilterInvocation}. Note\n * these are infrastructure objects and their design allows them to represent a large\n * variety of actual resources that might need to be secured, such as business objects or\n * HTTP request URLs.\n * <p>\n * Each secure object typically has its own interceptor package. Each package usually\n * includes a concrete security interceptor (which subclasses\n * {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor}) and\n * an appropriate {@link org.springframework.security.access.SecurityMetadataSource} for\n * the type of resources the secure object represents.\n */\n@NullMarked\npackage org.springframework.security.access.intercept;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/method/AbstractFallbackMethodSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.method;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\n\n/**\n * Abstract implementation of {@link MethodSecurityMetadataSource} that supports both\n * Spring AOP and AspectJ and performs attribute resolution from: 1. specific target\n * method; 2. target class; 3. declaring method; 4. declaring class/interface. Use with\n * {@link DelegatingMethodSecurityMetadataSource} for caching support.\n * <p>\n * This class mimics the behaviour of Spring's\n * <tt>AbstractFallbackTransactionAttributeSource</tt> class.\n * <p>\n * Note that this class cannot extract security metadata where that metadata is expressed\n * by way of a target method/class (i.e. #1 and #2 above) AND the target method/class is\n * encapsulated in another proxy object. Spring Security does not walk a proxy chain to\n * locate the concrete/final target object. Consider making Spring Security your final\n * advisor (so it advises the final target, as opposed to another proxy), move the\n * metadata to declared methods or interfaces the proxy implements, or provide your own\n * replacement <tt>MethodSecurityMetadataSource</tt>.\n *\n * @author Ben Alex\n * @author Luke taylor\n * @since 2.0\n * @deprecated Use the {@code use-authorization-manager} attribute for\n * {@code <method-security>} and {@code <intercept-methods>} instead or use\n * annotation-based or {@link AuthorizationManager}-based authorization\n */\n@Deprecated\npublic abstract class AbstractFallbackMethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {\n\n\t@Override\n\tpublic Collection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass) {\n\t\t// The method may be on an interface, but we need attributes from the target\n\t\t// class.\n\t\t// If the target class is null, the method will be unchanged.\n\t\tMethod specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);\n\t\t// First try is the method in the target class.\n\t\tCollection<ConfigAttribute> attr = findAttributes(specificMethod, targetClass);\n\t\tif (attr != null) {\n\t\t\treturn attr;\n\t\t}\n\t\t// Second try is the config attribute on the target class.\n\t\tattr = findAttributes(specificMethod.getDeclaringClass());\n\t\tif (attr != null) {\n\t\t\treturn attr;\n\t\t}\n\t\tif (specificMethod != method || targetClass == null) {\n\t\t\t// Fallback is to look at the original method.\n\t\t\tattr = findAttributes(method, method.getDeclaringClass());\n\t\t\tif (attr != null) {\n\t\t\t\treturn attr;\n\t\t\t}\n\t\t\t// Last fallback is the class of the original method.\n\t\t\treturn findAttributes(method.getDeclaringClass());\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t/**\n\t * Obtains the security metadata applicable to the specified method invocation.\n\t *\n\t * <p>\n\t * Note that the {@link Method#getDeclaringClass()} may not equal the\n\t * <code>targetClass</code>. Both parameters are provided to assist subclasses which\n\t * may wish to provide advanced capabilities related to method metadata being\n\t * \"registered\" against a method even if the target class does not declare the method\n\t * (i.e. the subclass may only inherit the method).\n\t * @param method the method for the current invocation (never <code>null</code>)\n\t * @param targetClass the target class for the invocation (may be <code>null</code>)\n\t * @return the security metadata (or null if no metadata applies)\n\t */\n\tprotected abstract Collection<ConfigAttribute> findAttributes(Method method, @Nullable Class<?> targetClass);\n\n\t/**\n\t * Obtains the security metadata registered against the specified class.\n\t *\n\t * <p>\n\t * Subclasses should only return metadata expressed at a class level. Subclasses\n\t * should NOT aggregate metadata for each method registered against a class, as the\n\t * abstract superclass will separate invoke {@link #findAttributes(Method, Class)} for\n\t * individual methods as appropriate.\n\t * @param clazz the target class for the invocation (never <code>null</code>)\n\t * @return the security metadata (or null if no metadata applies)\n\t */\n\tprotected abstract Collection<ConfigAttribute> findAttributes(Class<?> clazz);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/method/AbstractMethodSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.method;\n\nimport java.util.Collection;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.aop.framework.AopProxyUtils;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\n\n/**\n * Abstract implementation of <tt>MethodSecurityMetadataSource</tt> which resolves the\n * secured object type to a MethodInvocation.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @deprecated Use the {@code use-authorization-manager} attribute for\n * {@code <method-security>} and {@code <intercept-methods>} instead or use\n * annotation-based or {@link AuthorizationManager}-based authorization\n */\n@NullUnmarked\n@Deprecated\npublic abstract class AbstractMethodSecurityMetadataSource implements MethodSecurityMetadataSource {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\t@Override\n\tpublic final Collection<ConfigAttribute> getAttributes(Object object) {\n\t\tif (object instanceof MethodInvocation mi) {\n\t\t\tObject target = mi.getThis();\n\t\t\tClass<?> targetClass = null;\n\t\t\tif (target != null) {\n\t\t\t\ttargetClass = (target instanceof Class<?>) ? (Class<?>) target\n\t\t\t\t\t\t: AopProxyUtils.ultimateTargetClass(target);\n\t\t\t}\n\t\t\tCollection<ConfigAttribute> attrs = getAttributes(mi.getMethod(), targetClass);\n\t\t\tif (attrs != null && !attrs.isEmpty()) {\n\t\t\t\treturn attrs;\n\t\t\t}\n\t\t\tif (target != null && !(target instanceof Class<?>)) {\n\t\t\t\tattrs = getAttributes(mi.getMethod(), target.getClass());\n\t\t\t}\n\t\t\treturn attrs;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Object must be a non-null MethodInvocation\");\n\t}\n\n\t@Override\n\tpublic final boolean supports(Class<?> clazz) {\n\t\treturn (MethodInvocation.class.isAssignableFrom(clazz));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/method/DelegatingMethodSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.method;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * Automatically tries a series of method definition sources, relying on the first source\n * of metadata that provides a non-null/non-empty response. Provides automatic caching of\n * the retrieved metadata.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @deprecated Use the {@code use-authorization-manager} attribute for\n * {@code <method-security>} and {@code <intercept-methods>} instead or use\n * annotation-based or {@link AuthorizationManager}-based authorization\n */\n@Deprecated\npublic final class DelegatingMethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {\n\n\tprivate static final List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = Collections.emptyList();\n\n\tprivate final List<MethodSecurityMetadataSource> methodSecurityMetadataSources;\n\n\tprivate final Map<DefaultCacheKey, Collection<ConfigAttribute>> attributeCache = new HashMap<>();\n\n\tpublic DelegatingMethodSecurityMetadataSource(List<MethodSecurityMetadataSource> methodSecurityMetadataSources) {\n\t\tAssert.notNull(methodSecurityMetadataSources, \"MethodSecurityMetadataSources cannot be null\");\n\t\tthis.methodSecurityMetadataSources = methodSecurityMetadataSources;\n\t}\n\n\t@Override\n\tpublic Collection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass) {\n\t\tDefaultCacheKey cacheKey = new DefaultCacheKey(method, targetClass);\n\t\tsynchronized (this.attributeCache) {\n\t\t\tCollection<ConfigAttribute> cached = this.attributeCache.get(cacheKey);\n\t\t\t// Check for canonical value indicating there is no config attribute,\n\t\t\tif (cached != null) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t\t// No cached value, so query the sources to find a result\n\t\t\tCollection<ConfigAttribute> attributes = null;\n\t\t\tfor (MethodSecurityMetadataSource s : this.methodSecurityMetadataSources) {\n\t\t\t\tattributes = s.getAttributes(method, targetClass);\n\t\t\t\tif (attributes != null && !attributes.isEmpty()) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Put it in the cache.\n\t\t\tif (attributes == null || attributes.isEmpty()) {\n\t\t\t\tthis.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);\n\t\t\t\treturn NULL_CONFIG_ATTRIBUTE;\n\t\t\t}\n\t\t\tthis.logger.debug(LogMessage.format(\"Caching method [%s] with attributes %s\", cacheKey, attributes));\n\t\t\tthis.attributeCache.put(cacheKey, attributes);\n\t\t\treturn attributes;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\tSet<ConfigAttribute> set = new HashSet<>();\n\t\tfor (MethodSecurityMetadataSource s : this.methodSecurityMetadataSources) {\n\t\t\tCollection<ConfigAttribute> attrs = s.getAllConfigAttributes();\n\t\t\tif (attrs != null) {\n\t\t\t\tset.addAll(attrs);\n\t\t\t}\n\t\t}\n\t\treturn set;\n\t}\n\n\tpublic List<MethodSecurityMetadataSource> getMethodSecurityMetadataSources() {\n\t\treturn this.methodSecurityMetadataSources;\n\t}\n\n\tprivate static class DefaultCacheKey {\n\n\t\tprivate final Method method;\n\n\t\tprivate final @Nullable Class<?> targetClass;\n\n\t\tDefaultCacheKey(Method method, @Nullable Class<?> targetClass) {\n\t\t\tthis.method = method;\n\t\t\tthis.targetClass = targetClass;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object other) {\n\t\t\tDefaultCacheKey otherKey = (DefaultCacheKey) other;\n\t\t\treturn (this.method.equals(otherKey.method)\n\t\t\t\t\t&& ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn this.method.hashCode() * 21 + ((this.targetClass != null) ? this.targetClass.hashCode() : 0);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tString targetClassName = (this.targetClass != null) ? this.targetClass.getName() : \"-\";\n\t\t\treturn \"CacheKey[\" + targetClassName + \"; \" + this.method + \"]\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/method/MapBasedMethodSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.method;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.BeanClassLoaderAware;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Stores a list of <tt>ConfigAttribute</tt>s for a method or class signature.\n *\n * <p>\n * This class is the preferred implementation of {@link MethodSecurityMetadataSource} for\n * XML-based definition of method security metadata. To assist in XML-based definition,\n * wildcard support is provided.\n * </p>\n *\n * @author Ben Alex\n * @since 2.0\n * @deprecated Use the {@code use-authorization-manager} attribute for\n * {@code <method-security>} and {@code <intercept-methods>} instead or use\n * annotation-based or {@link AuthorizationManager}-based authorization\n */\n@NullUnmarked\n@Deprecated\npublic class MapBasedMethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource\n\t\timplements BeanClassLoaderAware {\n\n\t@SuppressWarnings(\"NullAway\")\n\tprivate @Nullable ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();\n\n\t/**\n\t * Map from RegisteredMethod to ConfigAttribute list\n\t */\n\tprotected final Map<RegisteredMethod, List<ConfigAttribute>> methodMap = new HashMap<>();\n\n\t/**\n\t * Map from RegisteredMethod to name pattern used for registration\n\t */\n\tprivate final Map<RegisteredMethod, String> nameMap = new HashMap<>();\n\n\tpublic MapBasedMethodSecurityMetadataSource() {\n\t}\n\n\t/**\n\t * Creates the <tt>MapBasedMethodSecurityMetadataSource</tt> from a\n\t * @param methodMap map of method names to <tt>ConfigAttribute</tt>s.\n\t */\n\tpublic MapBasedMethodSecurityMetadataSource(Map<String, List<ConfigAttribute>> methodMap) {\n\t\tfor (Map.Entry<String, List<ConfigAttribute>> entry : methodMap.entrySet()) {\n\t\t\taddSecureMethod(entry.getKey(), entry.getValue());\n\t\t}\n\t}\n\n\t/**\n\t * Implementation does not support class-level attributes.\n\t */\n\t@Override\n\tprotected @Nullable Collection<ConfigAttribute> findAttributes(Class<?> clazz) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Will walk the method inheritance tree to find the most specific declaration\n\t * applicable.\n\t */\n\t@Override\n\tprotected @Nullable Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {\n\t\tif (targetClass == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn findAttributesSpecifiedAgainst(method, targetClass);\n\t}\n\n\tprivate @Nullable List<ConfigAttribute> findAttributesSpecifiedAgainst(Method method, Class<?> clazz) {\n\t\tRegisteredMethod registeredMethod = new RegisteredMethod(method, clazz);\n\t\tif (this.methodMap.containsKey(registeredMethod)) {\n\t\t\treturn this.methodMap.get(registeredMethod);\n\t\t}\n\t\t// Search superclass\n\t\tif (clazz.getSuperclass() != null) {\n\t\t\treturn findAttributesSpecifiedAgainst(method, clazz.getSuperclass());\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Add configuration attributes for a secure method. Method names can end or start\n\t * with <code>*</code> for matching multiple methods.\n\t * @param name type and method name, separated by a dot\n\t * @param attr the security attributes associated with the method\n\t */\n\tprivate void addSecureMethod(String name, List<ConfigAttribute> attr) {\n\t\tint lastDotIndex = name.lastIndexOf(\".\");\n\t\tAssert.isTrue(lastDotIndex != -1, () -> \"'\" + name + \"' is not a valid method name: format is FQN.methodName\");\n\t\tString methodName = name.substring(lastDotIndex + 1);\n\t\tAssert.hasText(methodName, () -> \"Method not found for '\" + name + \"'\");\n\t\tString typeName = name.substring(0, lastDotIndex);\n\t\tClass<?> type = ClassUtils.resolveClassName(typeName, this.beanClassLoader);\n\t\taddSecureMethod(type, methodName, attr);\n\t}\n\n\t/**\n\t * Add configuration attributes for a secure method. Mapped method names can end or\n\t * start with <code>*</code> for matching multiple methods.\n\t * @param javaType target interface or class the security configuration attribute\n\t * applies to\n\t * @param mappedName mapped method name, which the javaType has declared or inherited\n\t * @param attr required authorities associated with the method\n\t */\n\tpublic void addSecureMethod(Class<?> javaType, String mappedName, List<ConfigAttribute> attr) {\n\t\tString name = javaType.getName() + '.' + mappedName;\n\t\tthis.logger.debug(LogMessage.format(\"Request to add secure method [%s] with attributes [%s]\", name, attr));\n\t\tMethod[] methods = javaType.getMethods();\n\t\tList<Method> matchingMethods = new ArrayList<>();\n\t\tfor (Method method : methods) {\n\t\t\tif (method.getName().equals(mappedName) || isMatch(method.getName(), mappedName)) {\n\t\t\t\tmatchingMethods.add(method);\n\t\t\t}\n\t\t}\n\t\tAssert.notEmpty(matchingMethods, () -> \"Couldn't find method '\" + mappedName + \"' on '\" + javaType + \"'\");\n\t\tregisterAllMatchingMethods(javaType, attr, name, matchingMethods);\n\t}\n\n\tprivate void registerAllMatchingMethods(Class<?> javaType, List<ConfigAttribute> attr, String name,\n\t\t\tList<Method> matchingMethods) {\n\t\tfor (Method method : matchingMethods) {\n\t\t\tRegisteredMethod registeredMethod = new RegisteredMethod(method, javaType);\n\t\t\tString regMethodName = this.nameMap.get(registeredMethod);\n\t\t\tif ((regMethodName == null) || (!regMethodName.equals(name) && (regMethodName.length() <= name.length()))) {\n\t\t\t\t// no already registered method name, or more specific\n\t\t\t\t// method name specification (now) -> (re-)register method\n\t\t\t\tif (regMethodName != null) {\n\t\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\t\"Replacing attributes for secure method [%s]: current name [%s] is more specific than [%s]\",\n\t\t\t\t\t\t\tmethod, name, regMethodName));\n\t\t\t\t}\n\t\t\t\tthis.nameMap.put(registeredMethod, name);\n\t\t\t\taddSecureMethod(registeredMethod, attr);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"Keeping attributes for secure method [%s]: current name [%s] is not more specific than [%s]\",\n\t\t\t\t\t\tmethod, name, regMethodName));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Adds configuration attributes for a specific method, for example where the method\n\t * has been matched using a pointcut expression. If a match already exists in the map\n\t * for the method, then the existing match will be retained, so that if this method is\n\t * called for a more general pointcut it will not override a more specific one which\n\t * has already been added.\n\t * <p>\n\t * This method should only be called during initialization of the {@code BeanFactory}.\n\t */\n\tpublic void addSecureMethod(Class<?> javaType, Method method, List<ConfigAttribute> attr) {\n\t\tRegisteredMethod key = new RegisteredMethod(method, javaType);\n\t\tif (this.methodMap.containsKey(key)) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Method [%s] is already registered with attributes [%s]\", method,\n\t\t\t\t\tthis.methodMap.get(key)));\n\t\t\treturn;\n\t\t}\n\t\tthis.methodMap.put(key, attr);\n\t}\n\n\t/**\n\t * Add configuration attributes for a secure method.\n\t * @param method the method to be secured\n\t * @param attr required authorities associated with the method\n\t */\n\tprivate void addSecureMethod(RegisteredMethod method, List<ConfigAttribute> attr) {\n\t\tAssert.notNull(method, \"RegisteredMethod required\");\n\t\tAssert.notNull(attr, \"Configuration attribute required\");\n\t\tthis.logger.info(LogMessage.format(\"Adding secure method [%s] with attributes [%s]\", method, attr));\n\t\tthis.methodMap.put(method, attr);\n\t}\n\n\t/**\n\t * Obtains the configuration attributes explicitly defined against this bean.\n\t * @return the attributes explicitly defined against this bean\n\t */\n\t@Override\n\tpublic Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\tSet<ConfigAttribute> allAttributes = new HashSet<>();\n\t\tthis.methodMap.values().forEach(allAttributes::addAll);\n\t\treturn allAttributes;\n\t}\n\n\t/**\n\t * Return if the given method name matches the mapped name. The default implementation\n\t * checks for \"xxx\" and \"xxx\" matches.\n\t * @param methodName the method name of the class\n\t * @param mappedName the name in the descriptor\n\t * @return if the names match\n\t */\n\tprivate boolean isMatch(String methodName, String mappedName) {\n\t\treturn (mappedName.endsWith(\"*\") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1)))\n\t\t\t\t|| (mappedName.startsWith(\"*\") && methodName.endsWith(mappedName.substring(1, mappedName.length())));\n\t}\n\n\t@Override\n\tpublic void setBeanClassLoader(ClassLoader beanClassLoader) {\n\t\tAssert.notNull(beanClassLoader, \"Bean class loader required\");\n\t\tthis.beanClassLoader = beanClassLoader;\n\t}\n\n\t/**\n\t * @return map size (for unit tests and diagnostics)\n\t */\n\tpublic int getMethodMapSize() {\n\t\treturn this.methodMap.size();\n\t}\n\n\t/**\n\t * Stores both the Java Method as well as the Class we obtained the Method from. This\n\t * is necessary because Method only provides us access to the declaring class. It\n\t * doesn't provide a way for us to introspect which Class the Method was registered\n\t * against. If a given Class inherits and redeclares a method (i.e. calls super();)\n\t * the registered Class and declaring Class are the same. If a given class merely\n\t * inherits but does not redeclare a method, the registered Class will be the Class\n\t * we're invoking against and the Method will provide details of the declared class.\n\t */\n\tprivate static class RegisteredMethod {\n\n\t\tprivate final Method method;\n\n\t\tprivate final Class<?> registeredJavaType;\n\n\t\tRegisteredMethod(Method method, Class<?> registeredJavaType) {\n\t\t\tAssert.notNull(method, \"Method required\");\n\t\t\tAssert.notNull(registeredJavaType, \"Registered Java Type required\");\n\t\t\tthis.method = method;\n\t\t\tthis.registeredJavaType = registeredJavaType;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (this == obj) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (obj instanceof RegisteredMethod rhs) {\n\t\t\t\treturn this.method.equals(rhs.method) && this.registeredJavaType.equals(rhs.registeredJavaType);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn this.method.hashCode() * this.registeredJavaType.hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"RegisteredMethod[\" + this.registeredJavaType.getName() + \"; \" + this.method + \"]\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/method/MethodSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.method;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityMetadataSource;\nimport org.springframework.security.authorization.AuthorizationManager;\n\n/**\n * Interface for <code>SecurityMetadataSource</code> implementations that are designed to\n * perform lookups keyed on <code>Method</code>s.\n *\n * @author Ben Alex\n * @see org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager\n * @see org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager\n * @deprecated Use the {@code use-authorization-manager} attribute for\n * {@code <method-security>} and {@code <intercept-methods>} instead or use\n * annotation-based or {@link AuthorizationManager}-based authorization\n */\n@Deprecated\npublic interface MethodSecurityMetadataSource extends SecurityMetadataSource {\n\n\tCollection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/method/P.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.method;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.security.core.parameters.AnnotationParameterNameDiscoverer;\n\n/**\n * An annotation that can be used along with {@link AnnotationParameterNameDiscoverer} to\n * specify parameter names. This is useful for interfaces prior to JDK 8 which cannot\n * contain the parameter names.\n *\n * @see AnnotationParameterNameDiscoverer\n * @author Rob Winch\n * @since 3.2\n * @deprecated use @{code org.springframework.security.core.parameters.P}\n */\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Deprecated\npublic @interface P {\n\n\t/**\n\t * The parameter name\n\t * @return\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/method/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Provides {@code SecurityMetadataSource} implementations for securing Java method\n * invocations via different AOP libraries.\n */\n@NullMarked\npackage org.springframework.security.access.method;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PostInvocationAdviceProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport java.util.Collection;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.AfterInvocationProvider;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\n\n/**\n * <tt>AfterInvocationProvider</tt> which delegates to a\n * {@link PostInvocationAuthorizationAdvice} instance passing it the\n * <tt>PostInvocationAttribute</tt> created from @PostAuthorize and @PostFilter\n * annotations.\n *\n * @author Luke Taylor\n * @author Alexander Furer\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}\n * instead\n */\n@NullUnmarked\n@Deprecated\npublic class PostInvocationAdviceProvider implements AfterInvocationProvider {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final PostInvocationAuthorizationAdvice postAdvice;\n\n\tpublic PostInvocationAdviceProvider(PostInvocationAuthorizationAdvice postAdvice) {\n\t\tthis.postAdvice = postAdvice;\n\t}\n\n\t@Override\n\tpublic Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,\n\t\t\tObject returnedObject) throws AccessDeniedException {\n\t\tPostInvocationAttribute postInvocationAttribute = findPostInvocationAttribute(config);\n\t\tif (postInvocationAttribute == null) {\n\t\t\treturn returnedObject;\n\t\t}\n\t\treturn this.postAdvice.after(authentication, (MethodInvocation) object, postInvocationAttribute,\n\t\t\t\treturnedObject);\n\t}\n\n\tprivate @Nullable PostInvocationAttribute findPostInvocationAttribute(Collection<ConfigAttribute> config) {\n\t\tfor (ConfigAttribute attribute : config) {\n\t\t\tif (attribute instanceof PostInvocationAttribute) {\n\t\t\t\treturn (PostInvocationAttribute) attribute;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn attribute instanceof PostInvocationAttribute;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn MethodInvocation.class.isAssignableFrom(clazz);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PostInvocationAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport org.springframework.security.access.ConfigAttribute;\n\n/**\n * Marker interface for attributes which are created from combined @PostFilter\n * and @PostAuthorize annotations.\n * <p>\n * Consumed by a {@link PostInvocationAuthorizationAdvice}.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}\n * instead\n */\n@Deprecated\npublic interface PostInvocationAttribute extends ConfigAttribute {\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PostInvocationAuthorizationAdvice.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Performs filtering and authorization logic after a method is invoked.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}\n * instead\n */\n@Deprecated\npublic interface PostInvocationAuthorizationAdvice extends AopInfrastructureBean {\n\n\tObject after(Authentication authentication, MethodInvocation mi, PostInvocationAttribute pia, Object returnedObject)\n\t\t\tthrows AccessDeniedException;\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PreInvocationAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport org.springframework.security.access.ConfigAttribute;\n\n/**\n * Marker interface for attributes which are created from combined @PreFilter\n * and @PreAuthorize annotations.\n * <p>\n * Consumed by a {@link PreInvocationAuthorizationAdvice}.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}\n * instead\n */\n@Deprecated\npublic interface PreInvocationAttribute extends ConfigAttribute {\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PreInvocationAuthorizationAdvice.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Performs argument filtering and authorization logic before a method is invoked.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}\n * instead\n */\n@Deprecated\npublic interface PreInvocationAuthorizationAdvice extends AopInfrastructureBean {\n\n\t/**\n\t * The \"before\" advice which should be executed to perform any filtering necessary and\n\t * to decide whether the method call is authorised.\n\t * @param authentication the information on the principal on whose account the\n\t * decision should be made\n\t * @param mi the method invocation being attempted\n\t * @param preInvocationAttribute the attribute built from the @PreFilter\n\t * and @PostFilter annotations.\n\t * @return true if authorised, false otherwise\n\t */\n\tboolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute preInvocationAttribute);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PreInvocationAuthorizationAdviceVoter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport java.util.Collection;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Voter which performs the actions using a PreInvocationAuthorizationAdvice\n * implementation generated from @PreFilter and @PreAuthorize annotations.\n * <p>\n * In practice, if these annotations are being used, they will normally contain all the\n * necessary access control logic, so a voter-based system is not really necessary and a\n * single <tt>AccessDecisionManager</tt> which contained the same logic would suffice.\n * However, this class fits in readily with the traditional voter-based\n * <tt>AccessDecisionManager</tt> implementations used by Spring Security.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}\n * instead\n */\n@NullUnmarked\n@Deprecated\npublic class PreInvocationAuthorizationAdviceVoter implements AccessDecisionVoter<MethodInvocation> {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final PreInvocationAuthorizationAdvice preAdvice;\n\n\tpublic PreInvocationAuthorizationAdviceVoter(PreInvocationAuthorizationAdvice pre) {\n\t\tthis.preAdvice = pre;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn attribute instanceof PreInvocationAttribute;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn MethodInvocation.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\tpublic int vote(Authentication authentication, MethodInvocation method, Collection<ConfigAttribute> attributes) {\n\t\t// Find prefilter and preauth (or combined) attributes\n\t\t// if both null, abstain else call advice with them\n\t\tPreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);\n\t\tif (preAttr == null) {\n\t\t\t// No expression based metadata, so abstain\n\t\t\treturn ACCESS_ABSTAIN;\n\t\t}\n\t\treturn this.preAdvice.before(authentication, method, preAttr) ? ACCESS_GRANTED : ACCESS_DENIED;\n\t}\n\n\tprivate @Nullable PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {\n\t\tfor (ConfigAttribute attribute : config) {\n\t\t\tif (attribute instanceof PreInvocationAttribute) {\n\t\t\t\treturn (PreInvocationAttribute) attribute;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\n\nimport kotlinx.coroutines.reactive.ReactiveFlowKt;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.Exceptions;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.KotlinDetector;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link MethodInterceptor} that supports {@link PreAuthorize} and\n * {@link PostAuthorize} for methods that return {@link Mono} or {@link Flux} and Kotlin\n * coroutine functions.\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n * @since 5.0\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor}\n * or\n * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor}\n */\n@NullUnmarked\n@Deprecated\npublic class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor {\n\n\tprivate Authentication anonymous = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tprivate final MethodSecurityMetadataSource attributeSource;\n\n\tprivate final PreInvocationAuthorizationAdvice preInvocationAdvice;\n\n\tprivate final PostInvocationAuthorizationAdvice postAdvice;\n\n\tprivate static final String COROUTINES_FLOW_CLASS_NAME = \"kotlinx.coroutines.flow.Flow\";\n\n\tprivate static final int RETURN_TYPE_METHOD_PARAMETER_INDEX = -1;\n\n\t/**\n\t * Creates a new instance\n\t * @param attributeSource the {@link MethodSecurityMetadataSource} to use\n\t * @param preInvocationAdvice the {@link PreInvocationAuthorizationAdvice} to use\n\t * @param postInvocationAdvice the {@link PostInvocationAuthorizationAdvice} to use\n\t */\n\tpublic PrePostAdviceReactiveMethodInterceptor(MethodSecurityMetadataSource attributeSource,\n\t\t\tPreInvocationAuthorizationAdvice preInvocationAdvice,\n\t\t\tPostInvocationAuthorizationAdvice postInvocationAdvice) {\n\t\tAssert.notNull(attributeSource, \"attributeSource cannot be null\");\n\t\tAssert.notNull(preInvocationAdvice, \"preInvocationAdvice cannot be null\");\n\t\tAssert.notNull(postInvocationAdvice, \"postInvocationAdvice cannot be null\");\n\t\tthis.attributeSource = attributeSource;\n\t\tthis.preInvocationAdvice = preInvocationAdvice;\n\t\tthis.postAdvice = postInvocationAdvice;\n\t}\n\n\t@Override\n\tpublic Object invoke(final MethodInvocation invocation) {\n\t\tMethod method = invocation.getMethod();\n\t\tClass<?> returnType = method.getReturnType();\n\n\t\tboolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);\n\t\tboolean hasFlowReturnType = COROUTINES_FLOW_CLASS_NAME\n\t\t\t.equals(new MethodParameter(method, RETURN_TYPE_METHOD_PARAMETER_INDEX).getParameterType().getName());\n\t\tboolean hasReactiveReturnType = Publisher.class.isAssignableFrom(returnType) || isSuspendingFunction\n\t\t\t\t|| hasFlowReturnType;\n\n\t\tAssert.state(hasReactiveReturnType,\n\t\t\t\t() -> \"The returnType \" + returnType + \" on \" + method\n\t\t\t\t\t\t+ \" must return an instance of org.reactivestreams.Publisher \"\n\t\t\t\t\t\t+ \"(i.e. Mono / Flux) or the function must be a Kotlin coroutine \"\n\t\t\t\t\t\t+ \"function in order to support Reactor Context\");\n\t\tClass<?> targetClass = invocation.getThis().getClass();\n\t\tCollection<ConfigAttribute> attributes = this.attributeSource.getAttributes(method, targetClass);\n\t\tPreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);\n\t\t// @formatter:off\n\t\tMono<Authentication> toInvoke = ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.defaultIfEmpty(this.anonymous)\n\t\t\t\t.filter((auth) -> this.preInvocationAdvice.before(auth, invocation, preAttr))\n\t\t\t\t.switchIfEmpty(Mono.defer(() -> Mono.error(new AccessDeniedException(\"Denied\"))));\n\t\t// @formatter:on\n\t\tPostInvocationAttribute attr = findPostInvocationAttribute(attributes);\n\t\tif (Mono.class.isAssignableFrom(returnType)) {\n\t\t\treturn toInvoke.flatMap((auth) -> PrePostAdviceReactiveMethodInterceptor.<Mono<?>>proceed(invocation)\n\t\t\t\t.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));\n\t\t}\n\t\tif (Flux.class.isAssignableFrom(returnType)) {\n\t\t\treturn toInvoke.flatMapMany((auth) -> PrePostAdviceReactiveMethodInterceptor.<Flux<?>>proceed(invocation)\n\t\t\t\t.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));\n\t\t}\n\t\tif (hasFlowReturnType) {\n\t\t\tif (isSuspendingFunction) {\n\t\t\t\treturn toInvoke\n\t\t\t\t\t.flatMapMany((auth) -> Flux.from(PrePostAdviceReactiveMethodInterceptor.proceed(invocation))\n\t\t\t\t\t\t.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(returnType);\n\t\t\t\tAssert.state(adapter != null, () -> \"The returnType \" + returnType + \" on \" + method\n\t\t\t\t\t\t+ \" must have a org.springframework.core.ReactiveAdapter registered\");\n\t\t\t\tFlux<?> response = toInvoke.flatMapMany((auth) -> Flux\n\t\t\t\t\t.from(adapter.toPublisher(PrePostAdviceReactiveMethodInterceptor.flowProceed(invocation)))\n\t\t\t\t\t.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));\n\t\t\t\treturn KotlinDelegate.asFlow(response);\n\t\t\t}\n\t\t}\n\t\treturn toInvoke.flatMap((auth) -> Mono.from(PrePostAdviceReactiveMethodInterceptor.proceed(invocation))\n\t\t\t.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T extends Publisher<?>> @Nullable T proceed(final MethodInvocation invocation) {\n\t\ttry {\n\t\t\treturn (T) invocation.proceed();\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tthrow Exceptions.propagate(throwable);\n\t\t}\n\t}\n\n\tprivate static @Nullable Object flowProceed(final MethodInvocation invocation) {\n\t\ttry {\n\t\t\treturn invocation.proceed();\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tthrow Exceptions.propagate(throwable);\n\t\t}\n\t}\n\n\tprivate static @Nullable PostInvocationAttribute findPostInvocationAttribute(Collection<ConfigAttribute> config) {\n\t\tfor (ConfigAttribute attribute : config) {\n\t\t\tif (attribute instanceof PostInvocationAttribute) {\n\t\t\t\treturn (PostInvocationAttribute) attribute;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static @Nullable PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {\n\t\tfor (ConfigAttribute attribute : config) {\n\t\t\tif (attribute instanceof PreInvocationAttribute) {\n\t\t\t\treturn (PreInvocationAttribute) attribute;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Inner class to avoid a hard dependency on Kotlin at runtime.\n\t */\n\tprivate static class KotlinDelegate {\n\n\t\tprivate static Object asFlow(Publisher<?> publisher) {\n\t\t\treturn ReactiveFlowKt.asFlow(publisher);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PrePostAnnotationSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;\nimport org.springframework.util.ClassUtils;\n\n/**\n * <tt>MethodSecurityMetadataSource</tt> which extracts metadata from the @PreFilter\n * and @PreAuthorize annotations placed on a method. This class is merely responsible for\n * locating the relevant annotations (if any). It delegates the actual\n * <tt>ConfigAttribute</tt> creation to its {@link PrePostInvocationAttributeFactory},\n * thus decoupling itself from the mechanism which will enforce the annotations'\n * behaviour.\n * <p>\n * Annotations may be specified on classes or methods, and method-specific annotations\n * will take precedence. If you use any annotation and do not specify a pre-authorization\n * condition, then the method will be allowed as if a @PreAuthorize(\"permitAll\") were\n * present.\n * <p>\n * Since we are handling multiple annotations here, it's possible that we may have to\n * combine annotations defined in multiple locations for a single method - they may be\n * defined on the method itself, or at interface or class level.\n *\n * @author Luke Taylor\n * @since 3.0\n * @see PreInvocationAuthorizationAdviceVoter\n * @deprecated Use\n * {@link org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager}\n * and\n * {@link org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager}\n * instead\n */\n@NullUnmarked\n@Deprecated\npublic class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {\n\n\tprivate final PrePostInvocationAttributeFactory attributeFactory;\n\n\tpublic PrePostAnnotationSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory) {\n\t\tthis.attributeFactory = attributeFactory;\n\t}\n\n\t@Override\n\tpublic Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {\n\t\tif (method.getDeclaringClass() == Object.class) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tPreFilter preFilter = findAnnotation(method, targetClass, PreFilter.class);\n\t\tPreAuthorize preAuthorize = findAnnotation(method, targetClass, PreAuthorize.class);\n\t\tPostFilter postFilter = findAnnotation(method, targetClass, PostFilter.class);\n\t\t// TODO: Can we check for void methods and throw an exception here?\n\t\tPostAuthorize postAuthorize = findAnnotation(method, targetClass, PostAuthorize.class);\n\t\tif (preFilter == null && preAuthorize == null && postFilter == null && postAuthorize == null) {\n\t\t\t// There is no meta-data so return\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tString preFilterAttribute = (preFilter != null) ? preFilter.value() : null;\n\t\tString filterObject = (preFilter != null) ? preFilter.filterTarget() : null;\n\t\tString preAuthorizeAttribute = (preAuthorize != null) ? preAuthorize.value() : null;\n\t\tString postFilterAttribute = (postFilter != null) ? postFilter.value() : null;\n\t\tString postAuthorizeAttribute = (postAuthorize != null) ? postAuthorize.value() : null;\n\t\tArrayList<ConfigAttribute> attrs = new ArrayList<>(2);\n\t\tPreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(preFilterAttribute,\n\t\t\t\tfilterObject, preAuthorizeAttribute);\n\t\tif (pre != null) {\n\t\t\tattrs.add(pre);\n\t\t}\n\t\tPostInvocationAttribute post = this.attributeFactory.createPostInvocationAttribute(postFilterAttribute,\n\t\t\t\tpostAuthorizeAttribute);\n\t\tif (post != null) {\n\t\t\tattrs.add(post);\n\t\t}\n\t\tattrs.trimToSize();\n\t\treturn attrs;\n\t}\n\n\t@Override\n\tpublic @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * See\n\t * {@link org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource#getAttributes(Method, Class)}\n\t * for the logic of this method. The ordering here is slightly different in that we\n\t * consider method-specific annotations on an interface before class-level ones.\n\t */\n\tprivate <A extends Annotation> @Nullable A findAnnotation(Method method, Class<?> targetClass,\n\t\t\tClass<A> annotationClass) {\n\t\t// The method may be on an interface, but we need attributes from the target\n\t\t// class.\n\t\t// If the target class is null, the method will be unchanged.\n\t\tMethod specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);\n\t\tA annotation = AnnotationUtils.findAnnotation(specificMethod, annotationClass);\n\t\tif (annotation != null) {\n\t\t\tthis.logger.debug(LogMessage.format(\"%s found on specific method: %s\", annotation, specificMethod));\n\t\t\treturn annotation;\n\t\t}\n\t\t// Check the original (e.g. interface) method\n\t\tif (specificMethod != method) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(method, annotationClass);\n\t\t\tif (annotation != null) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"%s found on: %s\", annotation, method));\n\t\t\t\treturn annotation;\n\t\t\t}\n\t\t}\n\t\t// Check the class-level (note declaringClass, not targetClass, which may not\n\t\t// actually implement the method)\n\t\tannotation = AnnotationUtils.findAnnotation(specificMethod.getDeclaringClass(), annotationClass);\n\t\tif (annotation != null) {\n\t\t\tthis.logger\n\t\t\t\t.debug(LogMessage.format(\"%s found on: %s\", annotation, specificMethod.getDeclaringClass().getName()));\n\t\t\treturn annotation;\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/prepost/PrePostInvocationAttributeFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.security.authorization.AuthorizationManager;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n * @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor\n * @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor\n * @deprecated Use delegation with {@link AuthorizationManager}\n */\n@Deprecated\npublic interface PrePostInvocationAttributeFactory extends AopInfrastructureBean {\n\n\tPreInvocationAttribute createPreInvocationAttribute(@Nullable String preFilterAttribute,\n\t\t\t@Nullable String filterObject, @Nullable String preAuthorizeAttribute);\n\n\tPostInvocationAttribute createPostInvocationAttribute(@Nullable String postFilterAttribute,\n\t\t\t@Nullable String postAuthorizeAttribute);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/AbstractAccessDecisionManager.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.util.Assert;\n\n/**\n * Abstract implementation of {@link AccessDecisionManager}.\n *\n * <p>\n * Handles configuration of a bean context defined list of {@link AccessDecisionVoter}s\n * and the access control behaviour if all voters abstain from voting (defaults to deny\n * access).\n *\n * @deprecated Use {@link AuthorizationManager} instead\n */\n@Deprecated\npublic abstract class AbstractAccessDecisionManager\n\t\timplements AccessDecisionManager, InitializingBean, MessageSourceAware {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate List<AccessDecisionVoter<?>> decisionVoters;\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate boolean allowIfAllAbstainDecisions = false;\n\n\tprotected AbstractAccessDecisionManager(List<AccessDecisionVoter<?>> decisionVoters) {\n\t\tAssert.notEmpty(decisionVoters, \"A list of AccessDecisionVoters is required\");\n\t\tthis.decisionVoters = decisionVoters;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notEmpty(this.decisionVoters, \"A list of AccessDecisionVoters is required\");\n\t\tAssert.notNull(this.messages, \"A message source must be set\");\n\t}\n\n\tprotected final void checkAllowIfAllAbstainDecisions() {\n\t\tif (!this.isAllowIfAllAbstainDecisions()) {\n\t\t\tthrow new AccessDeniedException(\n\t\t\t\t\tthis.messages.getMessage(\"AbstractAccessDecisionManager.accessDenied\", \"Access is denied\"));\n\t\t}\n\t}\n\n\tpublic List<AccessDecisionVoter<?>> getDecisionVoters() {\n\t\treturn this.decisionVoters;\n\t}\n\n\tpublic boolean isAllowIfAllAbstainDecisions() {\n\t\treturn this.allowIfAllAbstainDecisions;\n\t}\n\n\tpublic void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) {\n\t\tthis.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\tfor (AccessDecisionVoter<?> voter : this.decisionVoters) {\n\t\t\tif (voter.supports(attribute)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Iterates through all <code>AccessDecisionVoter</code>s and ensures each can support\n\t * the presented class.\n\t * <p>\n\t * If one or more voters cannot support the presented class, <code>false</code> is\n\t * returned.\n\t * @param clazz the type of secured object being presented\n\t * @return true if this type is supported\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\tfor (AccessDecisionVoter<?> voter : this.decisionVoters) {\n\t\t\tif (!voter.supports(clazz)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.getClass().getSimpleName() + \" [DecisionVoters=\" + this.decisionVoters\n\t\t\t\t+ \", AllowIfAllAbstainDecisions=\" + this.allowIfAllAbstainDecisions + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/AbstractAclVoter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AuthorizationServiceException;\nimport org.springframework.util.Assert;\n\n/**\n * Provides helper methods for writing domain object ACL voters. Not bound to any\n * particular ACL system.\n *\n * @author Ben Alex\n * @deprecated Now used by only-deprecated classes. Generally speaking, in-memory ACL is\n * no longer advised, so no replacement is planned at this point.\n */\n@NullUnmarked\n@Deprecated\npublic abstract class AbstractAclVoter implements AccessDecisionVoter<MethodInvocation> {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable Class<?> processDomainObjectClass;\n\n\tprotected Object getDomainObjectInstance(MethodInvocation invocation) {\n\t\tObject[] args = invocation.getArguments();\n\t\tClass<?>[] params = invocation.getMethod().getParameterTypes();\n\t\tfor (int i = 0; i < params.length; i++) {\n\t\t\tif (this.processDomainObjectClass.isAssignableFrom(params[i])) {\n\t\t\t\treturn args[i];\n\t\t\t}\n\t\t}\n\t\tthrow new AuthorizationServiceException(\"MethodInvocation: \" + invocation\n\t\t\t\t+ \" did not provide any argument of type: \" + this.processDomainObjectClass);\n\t}\n\n\tpublic Class<?> getProcessDomainObjectClass() {\n\t\treturn this.processDomainObjectClass;\n\t}\n\n\tpublic void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {\n\t\tAssert.notNull(processDomainObjectClass, \"processDomainObjectClass cannot be set to null\");\n\t\tthis.processDomainObjectClass = processDomainObjectClass;\n\t}\n\n\t/**\n\t * This implementation supports only <code>MethodSecurityInterceptor</code>, because\n\t * it queries the presented <code>MethodInvocation</code>.\n\t * @param clazz the secure object\n\t * @return <code>true</code> if the secure object is <code>MethodInvocation</code>,\n\t * <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn (MethodInvocation.class.isAssignableFrom(clazz));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/AffirmativeBased.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Simple concrete implementation of\n * {@link org.springframework.security.access.AccessDecisionManager} that grants access if\n * any <code>AccessDecisionVoter</code> returns an affirmative response.\n *\n * @deprecated Use {@link AuthorizationManager} instead\n */\n@Deprecated\npublic class AffirmativeBased extends AbstractAccessDecisionManager {\n\n\tpublic AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {\n\t\tsuper(decisionVoters);\n\t}\n\n\t/**\n\t * This concrete implementation simply polls all configured\n\t * {@link AccessDecisionVoter}s and grants access if any\n\t * <code>AccessDecisionVoter</code> voted affirmatively. Denies access only if there\n\t * was a deny vote AND no affirmative votes.\n\t * <p>\n\t * If every <code>AccessDecisionVoter</code> abstained from voting, the decision will\n\t * be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to\n\t * false).\n\t * </p>\n\t * @param authentication the caller invoking the method\n\t * @param object the secured object\n\t * @param configAttributes the configuration attributes associated with the method\n\t * being invoked\n\t * @throws AccessDeniedException if access is denied\n\t */\n\t@Override\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)\n\t\t\tthrows AccessDeniedException {\n\t\tint deny = 0;\n\t\tfor (AccessDecisionVoter voter : getDecisionVoters()) {\n\t\t\tint result = voter.vote(authentication, object, configAttributes);\n\t\t\tswitch (result) {\n\t\t\t\tcase AccessDecisionVoter.ACCESS_GRANTED:\n\t\t\t\t\treturn;\n\t\t\t\tcase AccessDecisionVoter.ACCESS_DENIED:\n\t\t\t\t\tdeny++;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (deny > 0) {\n\t\t\tthrow new AccessDeniedException(\n\t\t\t\t\tthis.messages.getMessage(\"AbstractAccessDecisionManager.accessDenied\", \"Access is denied\"));\n\t\t}\n\t\t// To get this far, every AccessDecisionVoter abstained\n\t\tcheckAllowIfAllAbstainDecisions();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/AuthenticatedVoter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Votes if a {@link ConfigAttribute#getAttribute()} of\n * <code>IS_AUTHENTICATED_FULLY</code> or <code>IS_AUTHENTICATED_REMEMBERED</code> or\n * <code>IS_AUTHENTICATED_ANONYMOUSLY</code> is present. This list is in order of most\n * strict checking to least strict checking.\n * <p>\n * The current <code>Authentication</code> will be inspected to determine if the principal\n * has a particular level of authentication. The \"FULLY\" authenticated option means the\n * user is authenticated fully (i.e.\n * {@link org.springframework.security.authentication.AuthenticationTrustResolver#isAnonymous(Authentication)}\n * is false and\n * {@link org.springframework.security.authentication.AuthenticationTrustResolver#isRememberMe(Authentication)}\n * is false). The \"REMEMBERED\" will grant access if the principal was either authenticated\n * via remember-me OR is fully authenticated. The \"ANONYMOUSLY\" will grant access if the\n * principal was authenticated via remember-me, OR anonymously, OR via full\n * authentication.\n * <p>\n * All comparisons and prefixes are case sensitive.\n *\n * @author Ben Alex\n * @deprecated Use\n * {@link org.springframework.security.authorization.AuthorityAuthorizationManager}\n * instead\n */\n@Deprecated\npublic class AuthenticatedVoter implements AccessDecisionVoter<Object> {\n\n\tpublic static final String IS_AUTHENTICATED_FULLY = \"IS_AUTHENTICATED_FULLY\";\n\n\tpublic static final String IS_AUTHENTICATED_REMEMBERED = \"IS_AUTHENTICATED_REMEMBERED\";\n\n\tpublic static final String IS_AUTHENTICATED_ANONYMOUSLY = \"IS_AUTHENTICATED_ANONYMOUSLY\";\n\n\tprivate AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate boolean isFullyAuthenticated(Authentication authentication) {\n\t\treturn this.authenticationTrustResolver.isFullyAuthenticated(authentication);\n\t}\n\n\tpublic void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {\n\t\tAssert.notNull(authenticationTrustResolver, \"AuthenticationTrustResolver cannot be set to null\");\n\t\tthis.authenticationTrustResolver = authenticationTrustResolver;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn (attribute.getAttribute() != null) && (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())\n\t\t\t\t|| IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())\n\t\t\t\t|| IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute()));\n\t}\n\n\t/**\n\t * This implementation supports any type of class, because it does not query the\n\t * presented secure object.\n\t * @param clazz the secure object type\n\t * @return always {@code true}\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {\n\t\tint result = ACCESS_ABSTAIN;\n\t\tfor (ConfigAttribute attribute : attributes) {\n\t\t\tif (this.supports(attribute)) {\n\t\t\t\tresult = ACCESS_DENIED;\n\t\t\t\tif (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) {\n\t\t\t\t\tif (isFullyAuthenticated(authentication)) {\n\t\t\t\t\t\treturn ACCESS_GRANTED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) {\n\t\t\t\t\tif (this.authenticationTrustResolver.isRememberMe(authentication)\n\t\t\t\t\t\t\t|| isFullyAuthenticated(authentication)) {\n\t\t\t\t\t\treturn ACCESS_GRANTED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {\n\t\t\t\t\tif (this.authenticationTrustResolver.isAnonymous(authentication)\n\t\t\t\t\t\t\t|| isFullyAuthenticated(authentication)\n\t\t\t\t\t\t\t|| this.authenticationTrustResolver.isRememberMe(authentication)) {\n\t\t\t\t\t\treturn ACCESS_GRANTED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/ConsensusBased.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Simple concrete implementation of\n * {@link org.springframework.security.access.AccessDecisionManager} that uses a\n * consensus-based approach.\n * <p>\n * \"Consensus\" here means majority-rule (ignoring abstains) rather than unanimous\n * agreement (ignoring abstains). If you require unanimity, please see\n * {@link UnanimousBased}.\n *\n * @deprecated Use {@link AuthorizationManager} instead\n */\n@Deprecated\npublic class ConsensusBased extends AbstractAccessDecisionManager {\n\n\tprivate boolean allowIfEqualGrantedDeniedDecisions = true;\n\n\tpublic ConsensusBased(List<AccessDecisionVoter<?>> decisionVoters) {\n\t\tsuper(decisionVoters);\n\t}\n\n\t/**\n\t * This concrete implementation simply polls all configured\n\t * {@link AccessDecisionVoter}s and upon completion determines the consensus of\n\t * granted against denied responses.\n\t * <p>\n\t * If there were an equal number of grant and deny votes, the decision will be based\n\t * on the {@link #isAllowIfEqualGrantedDeniedDecisions()} property (defaults to true).\n\t * <p>\n\t * If every <code>AccessDecisionVoter</code> abstained from voting, the decision will\n\t * be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to\n\t * false).\n\t * @param authentication the caller invoking the method\n\t * @param object the secured object\n\t * @param configAttributes the configuration attributes associated with the method\n\t * being invoked\n\t * @throws AccessDeniedException if access is denied\n\t */\n\t@Override\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)\n\t\t\tthrows AccessDeniedException {\n\t\tint grant = 0;\n\t\tint deny = 0;\n\t\tfor (AccessDecisionVoter voter : getDecisionVoters()) {\n\t\t\tint result = voter.vote(authentication, object, configAttributes);\n\t\t\tswitch (result) {\n\t\t\t\tcase AccessDecisionVoter.ACCESS_GRANTED -> grant++;\n\t\t\t\tcase AccessDecisionVoter.ACCESS_DENIED -> deny++;\n\t\t\t\tdefault -> {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (grant > deny) {\n\t\t\treturn;\n\t\t}\n\t\tif (deny > grant) {\n\t\t\tthrow new AccessDeniedException(\n\t\t\t\t\tthis.messages.getMessage(\"AbstractAccessDecisionManager.accessDenied\", \"Access is denied\"));\n\t\t}\n\t\tif ((grant == deny) && (grant != 0)) {\n\t\t\tif (this.allowIfEqualGrantedDeniedDecisions) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new AccessDeniedException(\n\t\t\t\t\tthis.messages.getMessage(\"AbstractAccessDecisionManager.accessDenied\", \"Access is denied\"));\n\t\t}\n\t\t// To get this far, every AccessDecisionVoter abstained\n\t\tcheckAllowIfAllAbstainDecisions();\n\t}\n\n\tpublic boolean isAllowIfEqualGrantedDeniedDecisions() {\n\t\treturn this.allowIfEqualGrantedDeniedDecisions;\n\t}\n\n\tpublic void setAllowIfEqualGrantedDeniedDecisions(boolean allowIfEqualGrantedDeniedDecisions) {\n\t\tthis.allowIfEqualGrantedDeniedDecisions = allowIfEqualGrantedDeniedDecisions;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/RoleHierarchyVoter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Extended RoleVoter which uses a {@link RoleHierarchy} definition to determine the roles\n * allocated to the current user before voting.\n *\n * @author Luke Taylor\n * @since 2.0.4\n * @deprecated Use\n * {@link org.springframework.security.authorization.AuthorityAuthorizationManager#setRoleHierarchy}\n * instead\n */\n@NullUnmarked\n@Deprecated\npublic class RoleHierarchyVoter extends RoleVoter {\n\n\t@SuppressWarnings(\"NullAway\")\n\tprivate @Nullable RoleHierarchy roleHierarchy = null;\n\n\tpublic RoleHierarchyVoter(RoleHierarchy roleHierarchy) {\n\t\tAssert.notNull(roleHierarchy, \"RoleHierarchy must not be null\");\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n\t/**\n\t * Calls the <tt>RoleHierarchy</tt> to obtain the complete set of user authorities.\n\t */\n\t@Override\n\tCollection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {\n\t\treturn this.roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/RoleVoter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix indicating\n * that it is a role. The default prefix string is <Code>ROLE_</code>, but this may be\n * overridden to any value. It may also be set to empty, which means that essentially any\n * attribute will be voted on. As described further below, the effect of an empty prefix\n * may not be quite desirable.\n * <p>\n * Abstains from voting if no configuration attribute commences with the role prefix.\n * Votes to grant access if there is an exact matching\n * {@link org.springframework.security.core.GrantedAuthority} to a\n * <code>ConfigAttribute</code> starting with the role prefix. Votes to deny access if\n * there is no exact matching <code>GrantedAuthority</code> to a\n * <code>ConfigAttribute</code> starting with the role prefix.\n * <p>\n * An empty role prefix means that the voter will vote for every ConfigAttribute. When\n * there are different categories of ConfigAttributes used, this will not be optimal since\n * the voter will be voting for attributes which do not represent roles. However, this\n * option may be of some use when using pre-existing role names without a prefix, and no\n * ability exists to prefix them with a role prefix on reading them in, such as provided\n * for example in {@link org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl}.\n * <p>\n * All comparisons and prefixes are case sensitive.\n *\n * @author Ben Alex\n * @author colin sampaleanu\n * @deprecated Use\n * {@link org.springframework.security.authorization.AuthorityAuthorizationManager}\n * instead\n */\n@Deprecated\n@NullUnmarked\npublic class RoleVoter implements AccessDecisionVoter<Object> {\n\n\tprivate String rolePrefix = \"ROLE_\";\n\n\tpublic String getRolePrefix() {\n\t\treturn this.rolePrefix;\n\t}\n\n\t/**\n\t * Allows the default role prefix of <code>ROLE_</code> to be overridden. May be set\n\t * to an empty value, although this is usually not desirable.\n\t * @param rolePrefix the new prefix\n\t */\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn (attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix());\n\t}\n\n\t/**\n\t * This implementation supports any type of class, because it does not query the\n\t * presented secure object.\n\t * @param clazz the secure object\n\t * @return always <code>true</code>\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {\n\t\tif (authentication == null) {\n\t\t\treturn ACCESS_DENIED;\n\t\t}\n\t\tint result = ACCESS_ABSTAIN;\n\t\tCollection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);\n\t\tfor (ConfigAttribute attribute : attributes) {\n\t\t\tif (this.supports(attribute)) {\n\t\t\t\tresult = ACCESS_DENIED;\n\t\t\t\t// Attempt to find a matching granted authority\n\t\t\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\t\t\tif (attribute.getAttribute().equals(authority.getAuthority())) {\n\t\t\t\t\t\treturn ACCESS_GRANTED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tCollection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {\n\t\treturn authentication.getAuthorities();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/UnanimousBased.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Simple concrete implementation of\n * {@link org.springframework.security.access.AccessDecisionManager} that requires all\n * voters to abstain or grant access.\n *\n * @deprecated Use {@link AuthorizationManager} instead\n */\n@Deprecated\npublic class UnanimousBased extends AbstractAccessDecisionManager {\n\n\tpublic UnanimousBased(List<AccessDecisionVoter<?>> decisionVoters) {\n\t\tsuper(decisionVoters);\n\t}\n\n\t/**\n\t * This concrete implementation polls all configured {@link AccessDecisionVoter}s for\n\t * each {@link ConfigAttribute} and grants access if <b>only</b> grant (or abstain)\n\t * votes were received.\n\t * <p>\n\t * Other voting implementations usually pass the entire list of\n\t * <tt>ConfigAttribute</tt>s to the <code>AccessDecisionVoter</code>. This\n\t * implementation differs in that each <code>AccessDecisionVoter</code> knows only\n\t * about a single <code>ConfigAttribute</code> at a time.\n\t * <p>\n\t * If every <code>AccessDecisionVoter</code> abstained from voting, the decision will\n\t * be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to\n\t * false).\n\t * @param authentication the caller invoking the method\n\t * @param object the secured object\n\t * @param attributes the configuration attributes associated with the method being\n\t * invoked\n\t * @throws AccessDeniedException if access is denied\n\t */\n\t@Override\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic void decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)\n\t\t\tthrows AccessDeniedException {\n\t\tint grant = 0;\n\t\tList<ConfigAttribute> singleAttributeList = new ArrayList<>(1);\n\t\tsingleAttributeList.add(null);\n\t\tfor (ConfigAttribute attribute : attributes) {\n\t\t\tsingleAttributeList.set(0, attribute);\n\t\t\tfor (AccessDecisionVoter voter : getDecisionVoters()) {\n\t\t\t\tint result = voter.vote(authentication, object, singleAttributeList);\n\t\t\t\tswitch (result) {\n\t\t\t\t\tcase AccessDecisionVoter.ACCESS_GRANTED:\n\t\t\t\t\t\tgrant++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AccessDecisionVoter.ACCESS_DENIED:\n\t\t\t\t\t\tthrow new AccessDeniedException(this.messages\n\t\t\t\t\t\t\t.getMessage(\"AbstractAccessDecisionManager.accessDenied\", \"Access is denied\"));\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// To get this far, there were no deny votes\n\t\tif (grant > 0) {\n\t\t\treturn;\n\t\t}\n\t\t// To get this far, every AccessDecisionVoter abstained\n\t\tcheckAllowIfAllAbstainDecisions();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/access/vote/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Implements a vote-based approach to authorization decisions.\n */\n@NullMarked\npackage org.springframework.security.access.vote;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/acls/AclEntryVoter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.access.AuthorizationServiceException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.vote.AbstractAclVoter;\nimport org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;\nimport org.springframework.security.acls.domain.SidRetrievalStrategyImpl;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * <p>\n * Given a domain object instance passed as a method argument, ensures the principal has\n * appropriate permission as indicated by the {@link AclService}.\n * <p>\n * The <tt>AclService</tt> is used to retrieve the access control list (ACL) permissions\n * associated with a domain object instance for the current <tt>Authentication</tt>\n * object.\n * <p>\n * The voter will vote if any {@link ConfigAttribute#getAttribute()} matches the\n * {@link #processConfigAttribute}. The provider will then locate the first method\n * argument of type {@link #processDomainObjectClass}. Assuming that method argument is\n * non-null, the provider will then lookup the ACLs from the <code>AclManager</code> and\n * ensure the principal is {@link Acl#isGranted(List, List, boolean)} when presenting the\n * {@link #requirePermission} array to that method.\n * <p>\n * If the method argument is <tt>null</tt>, the voter will abstain from voting. If the\n * method argument could not be found, an {@link AuthorizationServiceException} will be\n * thrown.\n * <p>\n * In practical terms users will typically setup a number of <tt>AclEntryVoter</tt>s. Each\n * will have a different {@link #setProcessDomainObjectClass processDomainObjectClass},\n * {@link #processConfigAttribute} and {@link #requirePermission} combination. For\n * example, a small application might employ the following instances of\n * <tt>AclEntryVoter</tt>:\n * <ul>\n * <li>Process domain object class <code>BankAccount</code>, configuration attribute\n * <code>VOTE_ACL_BANK_ACCOUNT_READ</code>, require permission\n * <code>BasePermission.READ</code></li>\n * <li>Process domain object class <code>BankAccount</code>, configuration attribute\n * <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list\n * <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code> (allowing the\n * principal to have <b>either</b> of these two permissions)</li>\n * <li>Process domain object class <code>Customer</code>, configuration attribute\n * <code>VOTE_ACL_CUSTOMER_READ</code>, require permission\n * <code>BasePermission.READ</code></li>\n * <li>Process domain object class <code>Customer</code>, configuration attribute\n * <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list\n * <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code></li>\n * </ul>\n * Alternatively, you could have used a common superclass or interface for the\n * {@link #processDomainObjectClass} if both <code>BankAccount</code> and\n * <code>Customer</code> had common parents.\n *\n * <p>\n * If the principal does not have sufficient permissions, the voter will vote to deny\n * access.\n *\n * <p>\n * All comparisons and prefixes are case sensitive.\n *\n * @author Ben Alex\n * @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security\n * annotations may also prove useful, for example\n * {@code @PreAuthorize(\"hasPermission(#id, ObjectsReturnType.class, read)\")}\n */\n@Deprecated\npublic class AclEntryVoter extends AbstractAclVoter {\n\n\tprivate static final Log logger = LogFactory.getLog(AclEntryVoter.class);\n\n\tprivate final AclService aclService;\n\n\tprivate final String processConfigAttribute;\n\n\tprivate final List<Permission> requirePermission;\n\n\tprivate ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();\n\n\tprivate SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();\n\n\tprivate String internalMethod;\n\n\tpublic AclEntryVoter(AclService aclService, String processConfigAttribute, Permission[] requirePermission) {\n\t\tAssert.notNull(processConfigAttribute, \"A processConfigAttribute is mandatory\");\n\t\tAssert.notNull(aclService, \"An AclService is mandatory\");\n\t\tAssert.isTrue(!ObjectUtils.isEmpty(requirePermission), \"One or more requirePermission entries is mandatory\");\n\t\tthis.aclService = aclService;\n\t\tthis.processConfigAttribute = processConfigAttribute;\n\t\tthis.requirePermission = Arrays.asList(requirePermission);\n\t}\n\n\t/**\n\t * Optionally specifies a method of the domain object that will be used to obtain a\n\t * contained domain object. That contained domain object will be used for the ACL\n\t * evaluation. This is useful if a domain object contains a parent that an ACL\n\t * evaluation should be targeted for, instead of the child domain object (which\n\t * perhaps is being created and as such does not yet have any ACL permissions)\n\t * @return <code>null</code> to use the domain object, or the name of a method (that\n\t * requires no arguments) that should be invoked to obtain an <code>Object</code>\n\t * which will be the domain object used for ACL evaluation\n\t */\n\tprotected String getInternalMethod() {\n\t\treturn this.internalMethod;\n\t}\n\n\tpublic void setInternalMethod(String internalMethod) {\n\t\tthis.internalMethod = internalMethod;\n\t}\n\n\tprotected String getProcessConfigAttribute() {\n\t\treturn this.processConfigAttribute;\n\t}\n\n\tpublic void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {\n\t\tAssert.notNull(objectIdentityRetrievalStrategy, \"ObjectIdentityRetrievalStrategy required\");\n\t\tthis.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;\n\t}\n\n\tpublic void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {\n\t\tAssert.notNull(sidRetrievalStrategy, \"SidRetrievalStrategy required\");\n\t\tthis.sidRetrievalStrategy = sidRetrievalStrategy;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn (attribute.getAttribute() != null) && attribute.getAttribute().equals(getProcessConfigAttribute());\n\t}\n\n\t@Override\n\tpublic int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {\n\t\tfor (ConfigAttribute attr : attributes) {\n\t\t\tif (!supports(attr)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Need to make an access decision on this invocation\n\t\t\t// Attempt to locate the domain object instance to process\n\t\t\tObject domainObject = getDomainObjectInstance(object);\n\n\t\t\t// If domain object is null, vote to abstain\n\t\t\tif (domainObject == null) {\n\t\t\t\tlogger.debug(\"Voting to abstain - domainObject is null\");\n\t\t\t\treturn ACCESS_ABSTAIN;\n\t\t\t}\n\n\t\t\t// Evaluate if we are required to use an inner domain object\n\t\t\tif (StringUtils.hasText(this.internalMethod)) {\n\t\t\t\tdomainObject = invokeInternalMethod(domainObject);\n\t\t\t}\n\n\t\t\t// Obtain the OID applicable to the domain object\n\t\t\tObjectIdentity objectIdentity = this.objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);\n\n\t\t\t// Obtain the SIDs applicable to the principal\n\t\t\tList<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);\n\n\t\t\tAcl acl;\n\n\t\t\ttry {\n\t\t\t\t// Lookup only ACLs for SIDs we're interested in\n\t\t\t\tacl = this.aclService.readAclById(objectIdentity, sids);\n\t\t\t}\n\t\t\tcatch (NotFoundException ex) {\n\t\t\t\tlogger.debug(\"Voting to deny access - no ACLs apply for this principal\");\n\t\t\t\treturn ACCESS_DENIED;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (acl.isGranted(this.requirePermission, sids, false)) {\n\t\t\t\t\tlogger.debug(\"Voting to grant access\");\n\t\t\t\t\treturn ACCESS_GRANTED;\n\t\t\t\t}\n\t\t\t\tlogger.debug(\"Voting to deny access - ACLs returned, but insufficient permissions for this principal\");\n\t\t\t\treturn ACCESS_DENIED;\n\t\t\t}\n\t\t\tcatch (NotFoundException ex) {\n\t\t\t\tlogger.debug(\"Voting to deny access - no ACLs apply for this principal\");\n\t\t\t\treturn ACCESS_DENIED;\n\t\t\t}\n\t\t}\n\n\t\t// No configuration attribute matched, so abstain\n\t\treturn ACCESS_ABSTAIN;\n\t}\n\n\tprivate Object invokeInternalMethod(Object domainObject) {\n\t\ttry {\n\t\t\tClass<?> domainObjectType = domainObject.getClass();\n\t\t\tMethod method = domainObjectType.getMethod(this.internalMethod, new Class[0]);\n\t\t\treturn method.invoke(domainObject);\n\t\t}\n\t\tcatch (NoSuchMethodException ex) {\n\t\t\tthrow new AuthorizationServiceException(\"Object of class '\" + domainObject.getClass()\n\t\t\t\t\t+ \"' does not provide the requested internalMethod: \" + this.internalMethod);\n\t\t}\n\t\tcatch (IllegalAccessException ex) {\n\t\t\tlogger.debug(\"IllegalAccessException\", ex);\n\t\t\tthrow new AuthorizationServiceException(\n\t\t\t\t\t\"Problem invoking internalMethod: \" + this.internalMethod + \" for object: \" + domainObject);\n\t\t}\n\t\tcatch (InvocationTargetException ex) {\n\t\t\tlogger.debug(\"InvocationTargetException\", ex);\n\t\t\tthrow new AuthorizationServiceException(\n\t\t\t\t\t\"Problem invoking internalMethod: \" + this.internalMethod + \" for object: \" + domainObject);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/acls/afterinvocation/AbstractAclProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.afterinvocation;\n\nimport java.util.List;\n\nimport org.springframework.security.access.AfterInvocationProvider;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.acls.AclPermissionEvaluator;\nimport org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;\nimport org.springframework.security.acls.domain.SidRetrievalStrategyImpl;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * Abstract {@link AfterInvocationProvider} which provides commonly-used ACL-related\n * services.\n *\n * @author Ben Alex\n * @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security\n * annotations may also prove useful, for example\n * {@code @PostAuthorize(\"hasPermission(filterObject, read)\")}\n */\n@Deprecated\npublic abstract class AbstractAclProvider implements AfterInvocationProvider {\n\n\tprotected final AclService aclService;\n\n\tprotected String processConfigAttribute;\n\n\tprotected Class<?> processDomainObjectClass = Object.class;\n\n\tprotected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();\n\n\tprotected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();\n\n\tprotected final List<Permission> requirePermission;\n\n\tpublic AbstractAclProvider(AclService aclService, String processConfigAttribute,\n\t\t\tList<Permission> requirePermission) {\n\t\tAssert.hasText(processConfigAttribute, \"A processConfigAttribute is mandatory\");\n\t\tAssert.notNull(aclService, \"An AclService is mandatory\");\n\t\tAssert.isTrue(!ObjectUtils.isEmpty(requirePermission), \"One or more requirePermission entries is mandatory\");\n\t\tthis.aclService = aclService;\n\t\tthis.processConfigAttribute = processConfigAttribute;\n\t\tthis.requirePermission = requirePermission;\n\t}\n\n\tprotected Class<?> getProcessDomainObjectClass() {\n\t\treturn this.processDomainObjectClass;\n\t}\n\n\tprotected boolean hasPermission(Authentication authentication, Object domainObject) {\n\t\t// Obtain the OID applicable to the domain object\n\t\tObjectIdentity objectIdentity = this.objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);\n\n\t\t// Obtain the SIDs applicable to the principal\n\t\tList<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);\n\n\t\ttry {\n\t\t\t// Lookup only ACLs for SIDs we're interested in\n\t\t\tAcl acl = this.aclService.readAclById(objectIdentity, sids);\n\t\t\treturn acl.isGranted(this.requirePermission, sids, false);\n\t\t}\n\t\tcatch (NotFoundException ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {\n\t\tAssert.notNull(objectIdentityRetrievalStrategy, \"ObjectIdentityRetrievalStrategy required\");\n\t\tthis.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;\n\t}\n\n\tprotected void setProcessConfigAttribute(String processConfigAttribute) {\n\t\tAssert.hasText(processConfigAttribute, \"A processConfigAttribute is mandatory\");\n\t\tthis.processConfigAttribute = processConfigAttribute;\n\t}\n\n\tpublic void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {\n\t\tAssert.notNull(processDomainObjectClass, \"processDomainObjectClass cannot be set to null\");\n\t\tthis.processDomainObjectClass = processDomainObjectClass;\n\t}\n\n\tpublic void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {\n\t\tAssert.notNull(sidRetrievalStrategy, \"SidRetrievalStrategy required\");\n\t\tthis.sidRetrievalStrategy = sidRetrievalStrategy;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn this.processConfigAttribute.equals(attribute.getAttribute());\n\t}\n\n\t/**\n\t * This implementation supports any type of class, because it does not query the\n\t * presented secure object.\n\t * @param clazz the secure object\n\t * @return always <code>true</code>\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.afterinvocation;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.AuthorizationServiceException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.acls.AclPermissionEvaluator;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.core.Authentication;\n\n/**\n * <p>\n * Given a <code>Collection</code> of domain object instances returned from a secure\n * object invocation, remove any <code>Collection</code> elements the principal does not\n * have appropriate permission to access as defined by the {@link AclService}.\n * <p>\n * The <code>AclService</code> is used to retrieve the access control list (ACL)\n * permissions associated with each <code>Collection</code> domain object instance element\n * for the current <code>Authentication</code> object.\n * <p>\n * This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}\n * matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs\n * from the <code>AclService</code> and ensure the principal is\n * {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)\n * Acl.isGranted()} when presenting the {@link #requirePermission} array to that method.\n * <p>\n * If the principal does not have permission, that element will not be included in the\n * returned <code>Collection</code>.\n * <p>\n * Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a\n * {@link #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a\n * {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the\n * defaults.\n * <p>\n * If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code>\n * <code>Collection</code> will be returned. If the provided <code>returnObject</code> is\n * not a <code>Collection</code>, an {@link AuthorizationServiceException} will be thrown.\n * <p>\n * All comparisons and prefixes are case sensitive.\n *\n * @author Ben Alex\n * @author Paulo Neves\n * @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security\n * annotations may also prove useful, for example\n * {@code @PostFilter(\"hasPermission(filterObject, read)\")}\n */\n@Deprecated\npublic class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {\n\n\tprotected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);\n\n\tpublic AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService,\n\t\t\tList<Permission> requirePermission) {\n\t\tsuper(aclService, \"AFTER_ACL_COLLECTION_READ\", requirePermission);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,\n\t\t\tObject returnedObject) throws AccessDeniedException {\n\t\tif (returnedObject == null) {\n\t\t\tlogger.debug(\"Return object is null, skipping\");\n\t\t\treturn null;\n\t\t}\n\n\t\tfor (ConfigAttribute attr : config) {\n\t\t\tif (!this.supports(attr)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Need to process the Collection for this invocation\n\t\t\tFilterer filterer = getFilterer(returnedObject);\n\n\t\t\t// Locate unauthorised Collection elements\n\t\t\tfor (Object domainObject : filterer) {\n\t\t\t\t// Ignore nulls or entries which aren't instances of the configured domain\n\t\t\t\t// object class\n\t\t\t\tif (domainObject == null || !getProcessDomainObjectClass().isAssignableFrom(domainObject.getClass())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!hasPermission(authentication, domainObject)) {\n\t\t\t\t\tfilterer.remove(domainObject);\n\t\t\t\t\tlogger.debug(LogMessage.of(() -> \"Principal is NOT authorised for element: \" + domainObject));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn filterer.getFilteredObject();\n\t\t}\n\t\treturn returnedObject;\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprivate Filterer getFilterer(Object returnedObject) {\n\t\tif (returnedObject instanceof Collection) {\n\t\t\treturn new CollectionFilterer((Collection) returnedObject);\n\t\t}\n\t\tif (returnedObject.getClass().isArray()) {\n\t\t\treturn new ArrayFilterer((Object[]) returnedObject);\n\t\t}\n\t\tthrow new AuthorizationServiceException(\"A Collection or an array (or null) was required as the \"\n\t\t\t\t+ \"returnedObject, but the returnedObject was: \" + returnedObject);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.afterinvocation;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.acls.AclPermissionEvaluator;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.SpringSecurityMessageSource;\n\n/**\n * Given a domain object instance returned from a secure object invocation, ensures the\n * principal has appropriate permission as defined by the {@link AclService}.\n * <p>\n * The <code>AclService</code> is used to retrieve the access control list (ACL)\n * permissions associated with a domain object instance for the current\n * <code>Authentication</code> object.\n * <p>\n * This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}\n * matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs\n * from the <tt>AclService</tt> and ensure the principal is\n * {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)\n * Acl.isGranted(List, List, boolean)} when presenting the {@link #requirePermission}\n * array to that method.\n * <p>\n * Often users will set up an <code>AclEntryAfterInvocationProvider</code> with a\n * {@link #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a\n * {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the\n * defaults.\n * <p>\n * If the principal does not have sufficient permissions, an\n * <code>AccessDeniedException</code> will be thrown.\n * <p>\n * If the provided <tt>returnedObject</tt> is <code>null</code>, permission will always be\n * granted and <code>null</code> will be returned.\n * <p>\n * All comparisons and prefixes are case sensitive.\n *\n * @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security\n * annotations may also prove useful, for example\n * {@code @PostAuthorize(\"hasPermission(filterObject, read)\")}\n */\n@Deprecated\npublic class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {\n\n\tprotected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tpublic AclEntryAfterInvocationProvider(AclService aclService, List<Permission> requirePermission) {\n\t\tthis(aclService, \"AFTER_ACL_READ\", requirePermission);\n\t}\n\n\tpublic AclEntryAfterInvocationProvider(AclService aclService, String processConfigAttribute,\n\t\t\tList<Permission> requirePermission) {\n\t\tsuper(aclService, processConfigAttribute, requirePermission);\n\t}\n\n\t@Override\n\tpublic Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,\n\t\t\tObject returnedObject) throws AccessDeniedException {\n\n\t\tif (returnedObject == null) {\n\t\t\t// AclManager interface contract prohibits nulls\n\t\t\t// As they have permission to null/nothing, grant access\n\t\t\tlogger.debug(\"Return object is null, skipping\");\n\t\t\treturn null;\n\t\t}\n\n\t\tif (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) {\n\t\t\tlogger.debug(\"Return object is not applicable for this provider, skipping\");\n\t\t\treturn returnedObject;\n\t\t}\n\n\t\tfor (ConfigAttribute attr : config) {\n\t\t\tif (!this.supports(attr)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Need to make an access decision on this invocation\n\t\t\tif (hasPermission(authentication, returnedObject)) {\n\t\t\t\treturn returnedObject;\n\t\t\t}\n\n\t\t\tlogger.debug(\"Denying access\");\n\t\t\tthrow new AccessDeniedException(this.messages.getMessage(\"AclEntryAfterInvocationProvider.noPermission\",\n\t\t\t\t\tnew Object[] { authentication.getName(), returnedObject },\n\t\t\t\t\t\"Authentication {0} has NO permissions to the domain object {1}\"));\n\t\t}\n\n\t\treturn returnedObject;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/acls/afterinvocation/ArrayFilterer.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.afterinvocation;\n\nimport java.lang.reflect.Array;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\n\n/**\n * A filter used to filter arrays.\n *\n * @author Ben Alex\n * @author Paulo Neves\n * @deprecated please see {@code PostFilter}\n */\n@Deprecated\nclass ArrayFilterer<T> implements Filterer<T> {\n\n\tprotected static final Log logger = LogFactory.getLog(ArrayFilterer.class);\n\n\tprivate final Set<T> removeList;\n\n\tprivate final T[] list;\n\n\tArrayFilterer(T[] list) {\n\t\tthis.list = list;\n\t\t// Collect the removed objects to a HashSet so that\n\t\t// it is fast to lookup them when a filtered array\n\t\t// is constructed.\n\t\tthis.removeList = new HashSet<>();\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic T[] getFilteredObject() {\n\t\t// Recreate an array of same type and filter the removed objects.\n\t\tint originalSize = this.list.length;\n\t\tint sizeOfResultingList = originalSize - this.removeList.size();\n\t\tT[] filtered = (T[]) Array.newInstance(this.list.getClass().getComponentType(), sizeOfResultingList);\n\t\tfor (int i = 0, j = 0; i < this.list.length; i++) {\n\t\t\tT object = this.list[i];\n\t\t\tif (!this.removeList.contains(object)) {\n\t\t\t\tfiltered[j] = object;\n\t\t\t\tj++;\n\t\t\t}\n\t\t}\n\t\tlogger.debug(LogMessage.of(() -> \"Original array contained \" + originalSize + \" elements; now contains \"\n\t\t\t\t+ sizeOfResultingList + \" elements\"));\n\t\treturn filtered;\n\t}\n\n\t@Override\n\tpublic Iterator<T> iterator() {\n\t\treturn new ArrayFiltererIterator();\n\t}\n\n\t@Override\n\tpublic void remove(T object) {\n\t\tthis.removeList.add(object);\n\t}\n\n\t/**\n\t * Iterator for {@link ArrayFilterer} elements.\n\t */\n\tprivate class ArrayFiltererIterator implements Iterator<T> {\n\n\t\tprivate int index = 0;\n\n\t\t@Override\n\t\tpublic boolean hasNext() {\n\t\t\treturn this.index < ArrayFilterer.this.list.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic T next() {\n\t\t\tif (hasNext()) {\n\t\t\t\treturn ArrayFilterer.this.list[this.index++];\n\t\t\t}\n\t\t\tthrow new NoSuchElementException();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/acls/afterinvocation/CollectionFilterer.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.afterinvocation;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\n\n/**\n * A filter used to filter Collections.\n *\n * @author Ben Alex\n * @author Paulo Neves\n * @deprecated please see {@code PostFilter}\n */\n@Deprecated\nclass CollectionFilterer<T> implements Filterer<T> {\n\n\tprotected static final Log logger = LogFactory.getLog(CollectionFilterer.class);\n\n\tprivate final Collection<T> collection;\n\n\tprivate final Set<T> removeList;\n\n\tCollectionFilterer(Collection<T> collection) {\n\t\tthis.collection = collection;\n\t\t// We create a Set of objects to be removed from the Collection,\n\t\t// as ConcurrentModificationException prevents removal during\n\t\t// iteration, and making a new Collection to be returned is\n\t\t// problematic as the original Collection implementation passed\n\t\t// to the method may not necessarily be re-constructable (as\n\t\t// the Collection(collection) constructor is not guaranteed and\n\t\t// manually adding may lose sort order or other capabilities)\n\t\tthis.removeList = new HashSet<>();\n\t}\n\n\t@Override\n\tpublic Object getFilteredObject() {\n\t\t// Now the Iterator has ended, remove Objects from Collection\n\t\tIterator<T> removeIter = this.removeList.iterator();\n\t\tint originalSize = this.collection.size();\n\t\twhile (removeIter.hasNext()) {\n\t\t\tthis.collection.remove(removeIter.next());\n\t\t}\n\t\tlogger.debug(LogMessage.of(() -> \"Original collection contained \" + originalSize + \" elements; now contains \"\n\t\t\t\t+ this.collection.size() + \" elements\"));\n\t\treturn this.collection;\n\t}\n\n\t@Override\n\tpublic Iterator<T> iterator() {\n\t\treturn this.collection.iterator();\n\t}\n\n\t@Override\n\tpublic void remove(T object) {\n\t\tthis.removeList.add(object);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/acls/afterinvocation/Filterer.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.afterinvocation;\n\nimport java.util.Iterator;\n\n/**\n * Filterer strategy interface.\n *\n * @author Ben Alex\n * @author Paulo Neves\n * @deprecated please use {@code PreFilter} and {@code @PostFilter} instead\n */\n@Deprecated\ninterface Filterer<T> extends Iterable<T> {\n\n\t/**\n\t * Gets the filtered collection or array.\n\t * @return the filtered collection or array\n\t */\n\tObject getFilteredObject();\n\n\t/**\n\t * Returns an iterator over the filtered collection or array.\n\t * @return an Iterator\n\t */\n\t@Override\n\tIterator<T> iterator();\n\n\t/**\n\t * Removes the given object from the resulting list.\n\t * @param object the object to be removed\n\t */\n\tvoid remove(T object);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/acls/afterinvocation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * After-invocation providers for collection and array filtering. Consider using a\n * {@code PostFilter} annotation in preference.\n */\npackage org.springframework.security.acls.afterinvocation;\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/messaging/access/expression/EvaluationContextPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport org.springframework.expression.EvaluationContext;\n\n/**\n * Allows post processing the {@link EvaluationContext}\n *\n * <p>\n * This API is intentionally kept package scope as it may evolve over time.\n * </p>\n *\n * @author Daniel Bustamante Ospina\n * @since 5.2\n * @deprecated Since {@link MessageExpressionVoter} is deprecated, there is no more need\n * for this class\n */\n@Deprecated\ninterface EvaluationContextPostProcessor<I> {\n\n\t/**\n\t * Allows post processing of the {@link EvaluationContext}. Implementations may return\n\t * a new instance of {@link EvaluationContext} or modify the {@link EvaluationContext}\n\t * that was passed in.\n\t * @param context the original {@link EvaluationContext}\n\t * @param invocation the security invocation object (i.e. Message)\n\t * @return the updated context.\n\t */\n\tEvaluationContext postProcess(EvaluationContext context, I invocation);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/messaging/access/expression/ExpressionBasedMessageSecurityMetadataSourceFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.messaging.access.intercept.DefaultMessageSecurityMetadataSource;\nimport org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\n\n/**\n * A class used to create a {@link MessageSecurityMetadataSource} that uses\n * {@link MessageMatcher} mapped to Spring Expressions.\n *\n * @author Rob Winch\n * @since 4.0\n * @deprecated Use\n * {@link org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager}\n * instead\n */\n@Deprecated\npublic final class ExpressionBasedMessageSecurityMetadataSourceFactory {\n\n\tprivate ExpressionBasedMessageSecurityMetadataSourceFactory() {\n\t}\n\n\t/**\n\t * Create a {@link MessageSecurityMetadataSource} that uses {@link MessageMatcher}\n\t * mapped to Spring Expressions. Each entry is considered in order and only the first\n\t * match is used.\n\t *\n\t * For example:\n\t *\n\t * <pre>\n\t *     LinkedHashMap&lt;MessageMatcher&lt;?&gt;,String&gt; matcherToExpression = new LinkedHashMap&lt;MessageMatcher&lt;Object&gt;,String&gt;();\n\t *     matcherToExpression.put(new SimDestinationMessageMatcher(\"/public/**\"), \"permitAll\");\n\t *     matcherToExpression.put(new SimDestinationMessageMatcher(\"/admin/**\"), \"hasRole('ROLE_ADMIN')\");\n\t *     matcherToExpression.put(new SimDestinationMessageMatcher(\"/topics/{name}/**\"), \"@someBean.customLogic(authentication, #name)\");\n\t *     matcherToExpression.put(new SimDestinationMessageMatcher(\"/**\"), \"authenticated\");\n\t *\n\t *     MessageSecurityMetadataSource metadataSource = createExpressionMessageMetadataSource(matcherToExpression);\n\t * </pre>\n\t *\n\t * <p>\n\t * If our destination is \"/public/hello\", it would match on \"/public/**\" and on \"/**\".\n\t * However, only \"/public/**\" would be used since it is the first entry. That means\n\t * that a destination of \"/public/hello\" will be mapped to \"permitAll\".\n\t *\n\t * <p>\n\t * For a complete listing of expressions see {@link MessageSecurityExpressionRoot}\n\t * @param matcherToExpression an ordered mapping of {@link MessageMatcher} to Strings\n\t * that are turned into an Expression using\n\t * {@link DefaultMessageSecurityExpressionHandler#getExpressionParser()}\n\t * @return the {@link MessageSecurityMetadataSource} to use. Cannot be null.\n\t */\n\tpublic static MessageSecurityMetadataSource createExpressionMessageMetadataSource(\n\t\t\tLinkedHashMap<MessageMatcher<?>, String> matcherToExpression) {\n\t\treturn createExpressionMessageMetadataSource(matcherToExpression,\n\t\t\t\tnew DefaultMessageSecurityExpressionHandler<>());\n\t}\n\n\t/**\n\t * Create a {@link MessageSecurityMetadataSource} that uses {@link MessageMatcher}\n\t * mapped to Spring Expressions. Each entry is considered in order and only the first\n\t * match is used.\n\t *\n\t * For example:\n\t *\n\t * <pre>\n\t *     LinkedHashMap&lt;MessageMatcher&lt;?&gt;,String&gt; matcherToExpression = new LinkedHashMap&lt;MessageMatcher&lt;Object&gt;,String&gt;();\n\t *     matcherToExpression.put(new SimDestinationMessageMatcher(\"/public/**\"), \"permitAll\");\n\t *     matcherToExpression.put(new SimDestinationMessageMatcher(\"/admin/**\"), \"hasRole('ROLE_ADMIN')\");\n\t *     matcherToExpression.put(new SimDestinationMessageMatcher(\"/topics/{name}/**\"), \"@someBean.customLogic(authentication, #name)\");\n\t *     matcherToExpression.put(new SimDestinationMessageMatcher(\"/**\"), \"authenticated\");\n\t *\n\t *     MessageSecurityMetadataSource metadataSource = createExpressionMessageMetadataSource(matcherToExpression);\n\t * </pre>\n\t *\n\t * <p>\n\t * If our destination is \"/public/hello\", it would match on \"/public/**\" and on \"/**\".\n\t * However, only \"/public/**\" would be used since it is the first entry. That means\n\t * that a destination of \"/public/hello\" will be mapped to \"permitAll\".\n\t * </p>\n\t *\n\t * <p>\n\t * For a complete listing of expressions see {@link MessageSecurityExpressionRoot}\n\t * </p>\n\t * @param matcherToExpression an ordered mapping of {@link MessageMatcher} to Strings\n\t * that are turned into an Expression using\n\t * {@link DefaultMessageSecurityExpressionHandler#getExpressionParser()}\n\t * @param handler the {@link SecurityExpressionHandler} to use\n\t * @return the {@link MessageSecurityMetadataSource} to use. Cannot be null.\n\t */\n\tpublic static MessageSecurityMetadataSource createExpressionMessageMetadataSource(\n\t\t\tLinkedHashMap<MessageMatcher<?>, String> matcherToExpression,\n\t\t\tSecurityExpressionHandler<Message<Object>> handler) {\n\t\tLinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>> matcherToAttrs = new LinkedHashMap<>();\n\t\tfor (Map.Entry<MessageMatcher<?>, String> entry : matcherToExpression.entrySet()) {\n\t\t\tMessageMatcher<?> matcher = entry.getKey();\n\t\t\tString rawExpression = entry.getValue();\n\t\t\tExpression expression = handler.getExpressionParser().parseExpression(rawExpression);\n\t\t\tConfigAttribute attribute = new MessageExpressionConfigAttribute(expression, matcher);\n\t\t\tmatcherToAttrs.put(matcher, Arrays.asList(attribute));\n\t\t}\n\t\treturn new DefaultMessageSecurityMetadataSource(matcherToAttrs);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/messaging/access/expression/MessageExpressionConfigAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Simple expression configuration attribute for use in {@link Message} authorizations.\n *\n * @author Rob Winch\n * @author Daniel Bustamante Ospina\n * @since 4.0\n * @deprecated Use\n * {@link org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager}\n * instead\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\nclass MessageExpressionConfigAttribute implements ConfigAttribute, EvaluationContextPostProcessor<Message<?>> {\n\n\tprivate final Expression authorizeExpression;\n\n\tprivate final MessageMatcher<Object> matcher;\n\n\t/**\n\t * Creates a new instance\n\t * @param authorizeExpression the {@link Expression} to use. Cannot be null\n\t * @param matcher the {@link MessageMatcher} used to match the messages.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tMessageExpressionConfigAttribute(Expression authorizeExpression, MessageMatcher<?> matcher) {\n\t\tAssert.notNull(authorizeExpression, \"authorizeExpression cannot be null\");\n\t\tAssert.notNull(matcher, \"matcher cannot be null\");\n\t\tthis.authorizeExpression = authorizeExpression;\n\t\tthis.matcher = (MessageMatcher<Object>) matcher;\n\t}\n\n\tExpression getAuthorizeExpression() {\n\t\treturn this.authorizeExpression;\n\t}\n\n\t@Override\n\tpublic @Nullable String getAttribute() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.authorizeExpression.getExpressionString();\n\t}\n\n\t@Override\n\tpublic EvaluationContext postProcess(EvaluationContext ctx, Message<?> message) {\n\t\tMap<String, String> variables = this.matcher.matcher(message).getVariables();\n\t\tfor (Map.Entry<String, String> entry : variables.entrySet()) {\n\t\t\tctx.setVariable(entry.getKey(), entry.getValue());\n\t\t}\n\t\treturn ctx;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/messaging/access/expression/MessageExpressionVoter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Voter which handles {@link Message} authorisation decisions. If a\n * {@link MessageExpressionConfigAttribute} is found, then its expression is evaluated. If\n * true, {@code ACCESS_GRANTED} is returned. If false, {@code ACCESS_DENIED} is returned.\n * If no {@code MessageExpressionConfigAttribute} is found, then {@code ACCESS_ABSTAIN} is\n * returned.\n *\n * @author Rob Winch\n * @author Daniel Bustamante Ospina\n * @since 4.0\n * @deprecated Use\n * {@link org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager}\n * instead\n */\n@Deprecated\npublic class MessageExpressionVoter<T> implements AccessDecisionVoter<Message<T>> {\n\n\tprivate SecurityExpressionHandler<Message<T>> expressionHandler = new DefaultMessageSecurityExpressionHandler<>();\n\n\t@Override\n\tpublic int vote(Authentication authentication, Message<T> message, Collection<ConfigAttribute> attributes) {\n\t\tAssert.notNull(authentication, \"authentication must not be null\");\n\t\tAssert.notNull(message, \"message must not be null\");\n\t\tAssert.notNull(attributes, \"attributes must not be null\");\n\t\tMessageExpressionConfigAttribute attr = findConfigAttribute(attributes);\n\t\tif (attr == null) {\n\t\t\treturn ACCESS_ABSTAIN;\n\t\t}\n\t\tEvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, message);\n\t\tctx = attr.postProcess(ctx, message);\n\t\treturn ExpressionUtils.evaluateAsBoolean(attr.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED : ACCESS_DENIED;\n\t}\n\n\tprivate @Nullable MessageExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {\n\t\tfor (ConfigAttribute attribute : attributes) {\n\t\t\tif (attribute instanceof MessageExpressionConfigAttribute) {\n\t\t\t\treturn (MessageExpressionConfigAttribute) attribute;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn attribute instanceof MessageExpressionConfigAttribute;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn Message.class.isAssignableFrom(clazz);\n\t}\n\n\tpublic void setExpressionHandler(SecurityExpressionHandler<Message<T>> expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.expressionHandler = expressionHandler;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/messaging/access/intercept/ChannelSecurityInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.support.ChannelInterceptor;\nimport org.springframework.security.access.SecurityMetadataSource;\nimport org.springframework.security.access.intercept.AbstractSecurityInterceptor;\nimport org.springframework.security.access.intercept.InterceptorStatusToken;\nimport org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory;\nimport org.springframework.util.Assert;\n\n/**\n * Performs security handling of Message resources via a ChannelInterceptor\n * implementation.\n * <p>\n * The <code>SecurityMetadataSource</code> required by this security interceptor is of\n * type {@link MessageSecurityMetadataSource}.\n * <p>\n * Refer to {@link AbstractSecurityInterceptor} for details on the workflow.\n *\n * @author Rob Winch\n * @since 4.0\n * @deprecated Use {@code AuthorizationChannelInterceptor} instead\n */\n@Deprecated\npublic final class ChannelSecurityInterceptor extends AbstractSecurityInterceptor implements ChannelInterceptor {\n\n\tprivate static final ThreadLocal<InterceptorStatusToken> tokenHolder = new ThreadLocal<>();\n\n\tprivate final MessageSecurityMetadataSource metadataSource;\n\n\t/**\n\t * Creates a new instance\n\t * @param metadataSource the MessageSecurityMetadataSource to use. Cannot be null.\n\t *\n\t * @see DefaultMessageSecurityMetadataSource\n\t * @see ExpressionBasedMessageSecurityMetadataSourceFactory\n\t */\n\tpublic ChannelSecurityInterceptor(MessageSecurityMetadataSource metadataSource) {\n\t\tAssert.notNull(metadataSource, \"metadataSource cannot be null\");\n\t\tthis.metadataSource = metadataSource;\n\t}\n\n\t@Override\n\tpublic Class<?> getSecureObjectClass() {\n\t\treturn Message.class;\n\t}\n\n\t@Override\n\tpublic SecurityMetadataSource obtainSecurityMetadataSource() {\n\t\treturn this.metadataSource;\n\t}\n\n\t@Override\n\tpublic Message<?> preSend(Message<?> message, MessageChannel channel) {\n\t\tInterceptorStatusToken token = beforeInvocation(message);\n\t\tif (token != null) {\n\t\t\ttokenHolder.set(token);\n\t\t}\n\t\treturn message;\n\t}\n\n\t@Override\n\tpublic void postSend(Message<?> message, MessageChannel channel, boolean sent) {\n\t\tInterceptorStatusToken token = clearToken();\n\t\tafterInvocation(token, null);\n\t}\n\n\t@Override\n\tpublic void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, @Nullable Exception ex) {\n\t\tInterceptorStatusToken token = clearToken();\n\t\tfinallyInvocation(token);\n\t}\n\n\t@Override\n\tpublic boolean preReceive(MessageChannel channel) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic Message<?> postReceive(Message<?> message, MessageChannel channel) {\n\t\treturn message;\n\t}\n\n\t@Override\n\tpublic void afterReceiveCompletion(@Nullable Message<?> message, MessageChannel channel, @Nullable Exception ex) {\n\t}\n\n\tprivate InterceptorStatusToken clearToken() {\n\t\tInterceptorStatusToken token = tokenHolder.get();\n\t\ttokenHolder.remove();\n\t\treturn token;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/messaging/access/intercept/DefaultMessageSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\n\n/**\n * A default implementation of {@link MessageSecurityMetadataSource} that looks up the\n * {@link ConfigAttribute} instances using a {@link MessageMatcher}.\n *\n * <p>\n * Each entry is considered in order. The first entry that matches, the corresponding\n * {@code Collection<ConfigAttribute>} is returned.\n * </p>\n *\n * @author Rob Winch\n * @since 4.0\n * @see ChannelSecurityInterceptor\n * @see ExpressionBasedMessageSecurityMetadataSourceFactory\n * @deprecated Use {@link MessageMatcherDelegatingAuthorizationManager} instead\n */\n@Deprecated\npublic final class DefaultMessageSecurityMetadataSource implements MessageSecurityMetadataSource {\n\n\tprivate final Map<MessageMatcher<?>, Collection<ConfigAttribute>> messageMap;\n\n\tpublic DefaultMessageSecurityMetadataSource(\n\t\t\tLinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>> messageMap) {\n\t\tthis.messageMap = messageMap;\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {\n\t\tfinal Message message = (Message) object;\n\t\tfor (Map.Entry<MessageMatcher<?>, Collection<ConfigAttribute>> entry : this.messageMap.entrySet()) {\n\t\t\tif (entry.getKey().matches(message)) {\n\t\t\t\treturn entry.getValue();\n\t\t\t}\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\tSet<ConfigAttribute> allAttributes = new HashSet<>();\n\t\tfor (Collection<ConfigAttribute> entry : this.messageMap.values()) {\n\t\t\tallAttributes.addAll(entry);\n\t\t}\n\t\treturn allAttributes;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn Message.class.isAssignableFrom(clazz);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/messaging/access/intercept/MessageSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.SecurityMetadataSource;\n\n/**\n * A {@link SecurityMetadataSource} that is used for securing {@link Message}\n *\n * @author Rob Winch\n * @since 4.0\n * @see ChannelSecurityInterceptor\n * @see DefaultMessageSecurityMetadataSource\n * @deprecated Use {@link MessageMatcherDelegatingAuthorizationManager} instead\n */\n@Deprecated\npublic interface MessageSecurityMetadataSource extends SecurityMetadataSource {\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/DefaultWebInvocationPrivilegeEvaluator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.util.Collection;\n\nimport jakarta.servlet.ServletContext;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.intercept.AbstractSecurityInterceptor;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.ServletContextAware;\n\n/**\n * Allows users to determine whether they have privileges for a given web URI.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use {@link AuthorizationManagerWebInvocationPrivilegeEvaluator} instead\n */\n@Deprecated\npublic class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator, ServletContextAware {\n\n\tprotected static final Log logger = LogFactory.getLog(DefaultWebInvocationPrivilegeEvaluator.class);\n\n\tprivate final AbstractSecurityInterceptor securityInterceptor;\n\n\tprivate @Nullable ServletContext servletContext;\n\n\tpublic DefaultWebInvocationPrivilegeEvaluator(AbstractSecurityInterceptor securityInterceptor) {\n\t\tAssert.notNull(securityInterceptor, \"SecurityInterceptor cannot be null\");\n\t\tAssert.isTrue(FilterInvocation.class.equals(securityInterceptor.getSecureObjectClass()),\n\t\t\t\t\"AbstractSecurityInterceptor does not support FilterInvocations\");\n\t\tAssert.notNull(securityInterceptor.getAccessDecisionManager(),\n\t\t\t\t\"AbstractSecurityInterceptor must provide a non-null AccessDecisionManager\");\n\t\tthis.securityInterceptor = securityInterceptor;\n\t}\n\n\t/**\n\t * Determines whether the user represented by the supplied <tt>Authentication</tt>\n\t * object is allowed to invoke the supplied URI.\n\t * @param uri the URI excluding the context path (a default context path setting will\n\t * be used)\n\t */\n\t@Override\n\tpublic boolean isAllowed(String uri, @Nullable Authentication authentication) {\n\t\treturn isAllowed(null, uri, null, authentication);\n\t}\n\n\t/**\n\t * Determines whether the user represented by the supplied <tt>Authentication</tt>\n\t * object is allowed to invoke the supplied URI, with the given .\n\t * <p>\n\t * Note the default implementation of <tt>FilterInvocationSecurityMetadataSource</tt>\n\t * disregards the <code>contextPath</code> when evaluating which secure object\n\t * metadata applies to a given request URI, so generally the <code>contextPath</code>\n\t * is unimportant unless you are using a custom\n\t * <code>FilterInvocationSecurityMetadataSource</code>.\n\t * @param uri the URI excluding the context path\n\t * @param contextPath the context path (may be null, in which case a default value\n\t * will be used).\n\t * @param method the HTTP method (or null, for any method)\n\t * @param authentication the <tt>Authentication</tt> instance whose authorities should\n\t * be used in evaluation whether access should be granted.\n\t * @return true if access is allowed, false if denied\n\t */\n\t@Override\n\tpublic boolean isAllowed(@Nullable String contextPath, String uri, @Nullable String method,\n\t\t\t@Nullable Authentication authentication) {\n\t\tAssert.notNull(uri, \"uri parameter is required\");\n\t\tFilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);\n\t\tCollection<ConfigAttribute> attributes = this.securityInterceptor.obtainSecurityMetadataSource()\n\t\t\t.getAttributes(filterInvocation);\n\t\tif (attributes == null) {\n\t\t\treturn (!this.securityInterceptor.isRejectPublicInvocations());\n\t\t}\n\t\tif (authentication == null) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tthis.securityInterceptor.getAccessDecisionManager().decide(authentication, filterInvocation, attributes);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (AccessDeniedException ex) {\n\t\t\tlogger.debug(LogMessage.format(\"%s denied for %s\", filterInvocation, authentication), ex);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setServletContext(ServletContext servletContext) {\n\t\tthis.servletContext = servletContext;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/AbstractRetryEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * @author Luke Taylor\n * @deprecated please use\n * {@link org.springframework.security.web.transport.HttpsRedirectFilter} and its\n * associated {@link PortMapper}\n */\n@Deprecated\npublic abstract class AbstractRetryEntryPoint implements ChannelEntryPoint {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate PortMapper portMapper = new PortMapperImpl();\n\n\t/**\n\t * The scheme (\"http://\" or \"https://\")\n\t */\n\tprivate final String scheme;\n\n\t/**\n\t * The standard port for the scheme (80 for http, 443 for https)\n\t */\n\tprivate final int standardPort;\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tpublic AbstractRetryEntryPoint(String scheme, int standardPort) {\n\t\tthis.scheme = scheme;\n\t\tthis.standardPort = standardPort;\n\t}\n\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response) throws IOException {\n\t\tString queryString = request.getQueryString();\n\t\tString redirectUrl = request.getRequestURI() + ((queryString != null) ? (\"?\" + queryString) : \"\");\n\t\tInteger currentPort = this.portMapper.getServerPort(request);\n\t\tInteger redirectPort = getMappedPort(currentPort);\n\t\tif (redirectPort != null) {\n\t\t\tboolean includePort = redirectPort != this.standardPort;\n\t\t\tString port = (includePort) ? (\":\" + redirectPort) : \"\";\n\t\t\tredirectUrl = this.scheme + request.getServerName() + port + redirectUrl;\n\t\t}\n\t\tthis.logger.debug(LogMessage.format(\"Redirecting to: %s\", redirectUrl));\n\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUrl);\n\t}\n\n\tprotected abstract @Nullable Integer getMappedPort(Integer mapFromPort);\n\n\tprotected final PortMapper getPortMapper() {\n\t\treturn this.portMapper;\n\t}\n\n\tpublic void setPortMapper(PortMapper portMapper) {\n\t\tAssert.notNull(portMapper, \"portMapper cannot be null\");\n\t\tthis.portMapper = portMapper;\n\t}\n\n\t/**\n\t * Sets the strategy to be used for redirecting to the required channel URL. A\n\t * {@code DefaultRedirectStrategy} instance will be used if not set.\n\t * @param redirectStrategy the strategy instance to which the URL will be passed.\n\t */\n\tpublic void setRedirectStrategy(RedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n\tprotected final RedirectStrategy getRedirectStrategy() {\n\t\treturn this.redirectStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/ChannelDecisionManager.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\nimport java.util.Collection;\n\nimport jakarta.servlet.ServletException;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Decides whether a web channel provides sufficient security.\n *\n * @author Ben Alex\n * @deprecated no replacement is planned, though consider using a custom\n * {@link RequestMatcher} for any sophisticated decision-making\n */\n@Deprecated\npublic interface ChannelDecisionManager {\n\n\t/**\n\t * Decided whether the presented {@link FilterInvocation} provides the appropriate\n\t * level of channel security based on the requested list of <tt>ConfigAttribute</tt>s.\n\t *\n\t */\n\tvoid decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException;\n\n\t/**\n\t * Indicates whether this <code>ChannelDecisionManager</code> is able to process the\n\t * passed <code>ConfigAttribute</code>.\n\t * <p>\n\t * This allows the <code>ChannelProcessingFilter</code> to check every configuration\n\t * attribute can be consumed by the configured <code>ChannelDecisionManager</code>.\n\t * </p>\n\t * @param attribute a configuration attribute that has been configured against the\n\t * <code>ChannelProcessingFilter</code>\n\t * @return true if this <code>ChannelDecisionManager</code> can support the passed\n\t * configuration attribute\n\t */\n\tboolean supports(ConfigAttribute attribute);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/ChannelDecisionManagerImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport jakarta.servlet.ServletException;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of {@link ChannelDecisionManager}.\n * <p>\n * Iterates through each configured {@link ChannelProcessor}. If a\n * <code>ChannelProcessor</code> has any issue with the security of the request, it should\n * cause a redirect, exception or whatever other action is appropriate for the\n * <code>ChannelProcessor</code> implementation.\n * <p>\n * Once any response is committed (ie a redirect is written to the response object), the\n * <code>ChannelDecisionManagerImpl</code> will not iterate through any further\n * <code>ChannelProcessor</code>s.\n * <p>\n * The attribute \"ANY_CHANNEL\" if applied to a particular URL, the iteration through the\n * channel processors will be skipped (see SEC-494, SEC-335).\n *\n * @author Ben Alex\n * @deprecated no replacement is planned, though consider using a custom\n * {@link RequestMatcher} for any sophisticated decision-making\n */\n@Deprecated\n@NullUnmarked\npublic class ChannelDecisionManagerImpl implements ChannelDecisionManager, InitializingBean {\n\n\tpublic static final String ANY_CHANNEL = \"ANY_CHANNEL\";\n\n\tprivate List<ChannelProcessor> channelProcessors;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notEmpty(this.channelProcessors, \"A list of ChannelProcessors is required\");\n\t}\n\n\t@Override\n\tpublic void decide(FilterInvocation invocation, Collection<ConfigAttribute> config)\n\t\t\tthrows IOException, ServletException {\n\t\tfor (ConfigAttribute attribute : config) {\n\t\t\tif (ANY_CHANNEL.equals(attribute.getAttribute())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tfor (ChannelProcessor processor : this.channelProcessors) {\n\t\t\tprocessor.decide(invocation, config);\n\t\t\tif (invocation.getResponse().isCommitted()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected @Nullable List<ChannelProcessor> getChannelProcessors() {\n\t\treturn this.channelProcessors;\n\t}\n\n\t@SuppressWarnings(\"cast\")\n\tpublic void setChannelProcessors(List<?> channelProcessors) {\n\t\tAssert.notEmpty(channelProcessors, \"A list of ChannelProcessors is required\");\n\t\tthis.channelProcessors = new ArrayList<>(channelProcessors.size());\n\t\tfor (Object currentObject : channelProcessors) {\n\t\t\tAssert.isInstanceOf(ChannelProcessor.class, currentObject, () -> \"ChannelProcessor \"\n\t\t\t\t\t+ currentObject.getClass().getName() + \" must implement ChannelProcessor\");\n\t\t\tthis.channelProcessors.add((ChannelProcessor) currentObject);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\tif (ANY_CHANNEL.equals(attribute.getAttribute())) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (ChannelProcessor processor : this.channelProcessors) {\n\t\t\tif (processor.supports(attribute)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/ChannelEntryPoint.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.PortMapper;\n\n/**\n * May be used by a {@link ChannelProcessor} to launch a web channel.\n *\n * <p>\n * <code>ChannelProcessor</code>s can elect to launch a new web channel directly, or they\n * can delegate to another class. The <code>ChannelEntryPoint</code> is a pluggable\n * interface to assist <code>ChannelProcessor</code>s in performing this delegation.\n *\n * @author Ben Alex\n * @deprecated please use\n * {@link org.springframework.security.web.transport.HttpsRedirectFilter} and its\n * associated {@link PortMapper}\n */\n@Deprecated\npublic interface ChannelEntryPoint {\n\n\t/**\n\t * Commences a secure channel.\n\t * <p>\n\t * Implementations should modify the headers on the <code>ServletResponse</code> as\n\t * necessary to commence the user agent using the implementation's supported channel\n\t * type.\n\t * @param request that a <code>ChannelProcessor</code> has rejected\n\t * @param response so that the user agent can begin using a new channel\n\t *\n\t */\n\tvoid commence(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/ChannelProcessingFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Ensures a web request is delivered over the required channel.\n * <p>\n * Internally uses a {@link FilterInvocation} to represent the request, allowing a\n * {@code FilterInvocationSecurityMetadataSource} to be used to lookup the attributes\n * which apply.\n * <p>\n * Delegates the actual channel security decisions and necessary actions to the configured\n * {@link ChannelDecisionManager}. If a response is committed by the\n * {@code ChannelDecisionManager}, the filter chain will not proceed.\n * <p>\n * The most common usage is to ensure that a request takes place over HTTPS, where the\n * {@link ChannelDecisionManagerImpl} is configured with a {@link SecureChannelProcessor}\n * and an {@link InsecureChannelProcessor}. A typical configuration would be\n *\n * <pre>\n *\n * &lt;bean id=\"channelProcessingFilter\" class=\"org.springframework.security.web.access.channel.ChannelProcessingFilter\"&gt;\n *   &lt;property name=\"channelDecisionManager\" ref=\"channelDecisionManager\"/&gt;\n *   &lt;property name=\"securityMetadataSource\"&gt;\n *     &lt;security:filter-security-metadata-source request-matcher=\"regex\"&gt;\n *       &lt;security:intercept-url pattern=\"\\A/secure/.*\\Z\" access=\"REQUIRES_SECURE_CHANNEL\"/&gt;\n *       &lt;security:intercept-url pattern=\"\\A/login.jsp.*\\Z\" access=\"REQUIRES_SECURE_CHANNEL\"/&gt;\n *       &lt;security:intercept-url pattern=\"\\A/.*\\Z\" access=\"ANY_CHANNEL\"/&gt;\n *     &lt;/security:filter-security-metadata-source&gt;\n *   &lt;/property&gt;\n * &lt;/bean&gt;\n *\n * &lt;bean id=\"channelDecisionManager\" class=\"org.springframework.security.web.access.channel.ChannelDecisionManagerImpl\"&gt;\n *   &lt;property name=\"channelProcessors\"&gt;\n *     &lt;list&gt;\n *     &lt;ref bean=\"secureChannelProcessor\"/&gt;\n *     &lt;ref bean=\"insecureChannelProcessor\"/&gt;\n *     &lt;/list&gt;\n *   &lt;/property&gt;\n * &lt;/bean&gt;\n *\n * &lt;bean id=\"secureChannelProcessor\"\n *   class=\"org.springframework.security.web.access.channel.SecureChannelProcessor\"/&gt;\n * &lt;bean id=\"insecureChannelProcessor\"\n *   class=\"org.springframework.security.web.access.channel.InsecureChannelProcessor\"/&gt;\n *\n * </pre>\n *\n * which would force the login form and any access to the {@code /secure} path to be made\n * over HTTPS.\n *\n * @author Ben Alex\n * @deprecated see {@link org.springframework.security.web.transport.HttpsRedirectFilter}\n */\n@Deprecated\npublic class ChannelProcessingFilter extends GenericFilterBean {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate ChannelDecisionManager channelDecisionManager;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate FilterInvocationSecurityMetadataSource securityMetadataSource;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.securityMetadataSource, \"securityMetadataSource must be specified\");\n\t\tAssert.notNull(this.channelDecisionManager, \"channelDecisionManager must be specified\");\n\t\tCollection<ConfigAttribute> attributes = this.securityMetadataSource.getAllConfigAttributes();\n\t\tif (attributes == null) {\n\t\t\tthis.logger.warn(\"Could not validate configuration attributes as the \"\n\t\t\t\t\t+ \"FilterInvocationSecurityMetadataSource did not return any attributes\");\n\t\t\treturn;\n\t\t}\n\t\tSet<ConfigAttribute> unsupportedAttributes = getUnsupportedAttributes(attributes);\n\t\tAssert.isTrue(unsupportedAttributes.isEmpty(),\n\t\t\t\t() -> \"Unsupported configuration attributes: \" + unsupportedAttributes);\n\t\tthis.logger.info(\"Validated configuration attributes\");\n\t}\n\n\tprivate Set<ConfigAttribute> getUnsupportedAttributes(Collection<ConfigAttribute> attrDefs) {\n\t\tSet<ConfigAttribute> unsupportedAttributes = new HashSet<>();\n\t\tfor (ConfigAttribute attr : attrDefs) {\n\t\t\tif (!this.channelDecisionManager.supports(attr)) {\n\t\t\t\tunsupportedAttributes.add(attr);\n\t\t\t}\n\t\t}\n\t\treturn unsupportedAttributes;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tHttpServletRequest request = (HttpServletRequest) req;\n\t\tHttpServletResponse response = (HttpServletResponse) res;\n\t\tFilterInvocation filterInvocation = new FilterInvocation(request, response, chain);\n\t\tCollection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(filterInvocation);\n\t\tif (attributes != null) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Request: %s; ConfigAttributes: %s\", filterInvocation, attributes));\n\t\t\tthis.channelDecisionManager.decide(filterInvocation, attributes);\n\t\t\t@Nullable HttpServletResponse channelResponse = filterInvocation.getResponse();\n\t\t\tAssert.notNull(channelResponse, \"HttpServletResponse is required\");\n\t\t\tif (channelResponse.isCommitted()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tchain.doFilter(request, response);\n\t}\n\n\tprotected @Nullable ChannelDecisionManager getChannelDecisionManager() {\n\t\treturn this.channelDecisionManager;\n\t}\n\n\tprotected FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {\n\t\treturn this.securityMetadataSource;\n\t}\n\n\tpublic void setChannelDecisionManager(ChannelDecisionManager channelDecisionManager) {\n\t\tthis.channelDecisionManager = channelDecisionManager;\n\t}\n\n\tpublic void setSecurityMetadataSource(\n\t\t\tFilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) {\n\t\tthis.securityMetadataSource = filterInvocationSecurityMetadataSource;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/ChannelProcessor.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\nimport java.util.Collection;\n\nimport jakarta.servlet.ServletException;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Decides whether a web channel meets a specific security condition.\n * <p>\n * <code>ChannelProcessor</code> implementations are iterated by the\n * {@link ChannelDecisionManagerImpl}.\n * <p>\n * If an implementation has an issue with the channel security, they should take action\n * themselves. The callers of the implementation do not take any action.\n *\n * @author Ben Alex\n * @deprecated no replacement is planned, though consider using a custom\n * {@link RequestMatcher} for any sophisticated decision-making\n */\n@Deprecated\npublic interface ChannelProcessor {\n\n\t/**\n\t * Decided whether the presented {@link FilterInvocation} provides the appropriate\n\t * level of channel security based on the requested list of <tt>ConfigAttribute</tt>s.\n\t */\n\tvoid decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException;\n\n\t/**\n\t * Indicates whether this <code>ChannelProcessor</code> is able to process the passed\n\t * <code>ConfigAttribute</code>.\n\t * <p>\n\t * This allows the <code>ChannelProcessingFilter</code> to check every configuration\n\t * attribute can be consumed by the configured <code>ChannelDecisionManager</code>.\n\t * @param attribute a configuration attribute that has been configured against the\n\t * <tt>ChannelProcessingFilter</tt>.\n\t * @return true if this <code>ChannelProcessor</code> can support the passed\n\t * configuration attribute\n\t */\n\tboolean supports(ConfigAttribute attribute);\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/InsecureChannelProcessor.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\nimport java.util.Collection;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Ensures channel security is inactive by review of\n * <code>HttpServletRequest.isSecure()</code> responses.\n * <p>\n * The class responds to one case-sensitive keyword, {@link #getInsecureKeyword}. If this\n * keyword is detected, <code>HttpServletRequest.isSecure()</code> is used to determine\n * the channel security offered. If channel security is present, the configured\n * <code>ChannelEntryPoint</code> is called. By default the entry point is\n * {@link RetryWithHttpEntryPoint}.\n * <p>\n * The default <code>insecureKeyword</code> is <code>REQUIRES_INSECURE_CHANNEL</code>.\n *\n * @author Ben Alex\n * @deprecated no replacement is planned, though consider using a custom\n * {@link RequestMatcher} for any sophisticated decision-making\n */\n@Deprecated\npublic class InsecureChannelProcessor implements InitializingBean, ChannelProcessor {\n\n\tprivate ChannelEntryPoint entryPoint = new RetryWithHttpEntryPoint();\n\n\tprivate String insecureKeyword = \"REQUIRES_INSECURE_CHANNEL\";\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasLength(this.insecureKeyword, \"insecureKeyword required\");\n\t\tAssert.notNull(this.entryPoint, \"entryPoint required\");\n\t}\n\n\t@Override\n\tpublic void decide(FilterInvocation invocation, Collection<ConfigAttribute> config)\n\t\t\tthrows IOException, ServletException {\n\t\tAssert.isTrue(invocation != null && config != null, \"Nulls cannot be provided\");\n\t\tfor (ConfigAttribute attribute : config) {\n\t\t\tif (supports(attribute)) {\n\t\t\t\tif (invocation.getHttpRequest().isSecure()) {\n\t\t\t\t\t@Nullable HttpServletResponse response = invocation.getResponse();\n\t\t\t\t\tAssert.notNull(response, \"HttpServletResponse required\");\n\t\t\t\t\tthis.entryPoint.commence(invocation.getRequest(), response);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic ChannelEntryPoint getEntryPoint() {\n\t\treturn this.entryPoint;\n\t}\n\n\tpublic String getInsecureKeyword() {\n\t\treturn this.insecureKeyword;\n\t}\n\n\tpublic void setEntryPoint(ChannelEntryPoint entryPoint) {\n\t\tthis.entryPoint = entryPoint;\n\t}\n\n\tpublic void setInsecureKeyword(String secureKeyword) {\n\t\tthis.insecureKeyword = secureKeyword;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn (attribute != null) && (attribute.getAttribute() != null)\n\t\t\t\t&& attribute.getAttribute().equals(getInsecureKeyword());\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/RetryWithHttpEntryPoint.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.PortMapper;\n\n/**\n * Commences an insecure channel by retrying the original request using HTTP.\n * <p>\n * This entry point should suffice in most circumstances. However, it is not intended to\n * properly handle HTTP POSTs or other usage where a standard redirect would cause an\n * issue.\n *\n * @author Ben Alex\n * @deprecated please use\n * {@link org.springframework.security.web.transport.HttpsRedirectFilter} and its\n * associated {@link PortMapper}\n */\n@Deprecated(since = \"6.5\")\npublic class RetryWithHttpEntryPoint extends AbstractRetryEntryPoint {\n\n\tpublic RetryWithHttpEntryPoint() {\n\t\tsuper(\"http://\", 80);\n\t}\n\n\t@Override\n\tprotected @Nullable Integer getMappedPort(Integer mapFromPort) {\n\t\treturn getPortMapper().lookupHttpPort(mapFromPort);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/RetryWithHttpsEntryPoint.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.PortMapper;\n\n/**\n * Commences a secure channel by retrying the original request using HTTPS.\n * <p>\n * This entry point should suffice in most circumstances. However, it is not intended to\n * properly handle HTTP POSTs or other usage where a standard redirect would cause an\n * issue.\n * </p>\n *\n * @author Ben Alex\n * @deprecated please use\n * {@link org.springframework.security.web.transport.HttpsRedirectFilter} and its\n * associated {@link PortMapper}\n */\n@Deprecated(since = \"6.5\")\npublic class RetryWithHttpsEntryPoint extends AbstractRetryEntryPoint {\n\n\tpublic RetryWithHttpsEntryPoint() {\n\t\tsuper(\"https://\", 443);\n\t}\n\n\t@Override\n\tprotected @Nullable Integer getMappedPort(Integer mapFromPort) {\n\t\treturn getPortMapper().lookupHttpsPort(mapFromPort);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/SecureChannelProcessor.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\nimport java.util.Collection;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Ensures channel security is active by review of\n * <code>HttpServletRequest.isSecure()</code> responses.\n * <p>\n * The class responds to one case-sensitive keyword, {@link #getSecureKeyword}. If this\n * keyword is detected, <code>HttpServletRequest.isSecure()</code> is used to determine\n * the channel security offered. If channel security is not present, the configured\n * <code>ChannelEntryPoint</code> is called. By default the entry point is\n * {@link RetryWithHttpsEntryPoint}.\n * <p>\n * The default <code>secureKeyword</code> is <code>REQUIRES_SECURE_CHANNEL</code>.\n *\n * @author Ben Alex\n * @deprecated no replacement is planned, though consider using a custom\n * {@link RequestMatcher} for any sophisticated decision-making\n */\n@Deprecated\npublic class SecureChannelProcessor implements InitializingBean, ChannelProcessor {\n\n\tprivate ChannelEntryPoint entryPoint = new RetryWithHttpsEntryPoint();\n\n\tprivate String secureKeyword = \"REQUIRES_SECURE_CHANNEL\";\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasLength(this.secureKeyword, \"secureKeyword required\");\n\t\tAssert.notNull(this.entryPoint, \"entryPoint required\");\n\t}\n\n\t@Override\n\tpublic void decide(FilterInvocation invocation, Collection<ConfigAttribute> config)\n\t\t\tthrows IOException, ServletException {\n\t\tAssert.isTrue((invocation != null) && (config != null), \"Nulls cannot be provided\");\n\t\tfor (ConfigAttribute attribute : config) {\n\t\t\tif (supports(attribute)) {\n\t\t\t\tif (!invocation.getHttpRequest().isSecure()) {\n\t\t\t\t\tHttpServletResponse response = invocation.getResponse();\n\t\t\t\t\tAssert.notNull(response, \"HttpServletResponse is required\");\n\t\t\t\t\tthis.entryPoint.commence(invocation.getRequest(), response);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic ChannelEntryPoint getEntryPoint() {\n\t\treturn this.entryPoint;\n\t}\n\n\tpublic String getSecureKeyword() {\n\t\treturn this.secureKeyword;\n\t}\n\n\tpublic void setEntryPoint(ChannelEntryPoint entryPoint) {\n\t\tthis.entryPoint = entryPoint;\n\t}\n\n\tpublic void setSecureKeyword(String secureKeyword) {\n\t\tthis.secureKeyword = secureKeyword;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn (attribute != null) && (attribute.getAttribute() != null)\n\t\t\t\t&& attribute.getAttribute().equals(getSecureKeyword());\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/channel/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes that ensure web requests are received over required transport channels.\n * <p>\n * Most commonly used to enforce that requests are submitted over HTTP or HTTPS.\n */\n@NullMarked\npackage org.springframework.security.web.access.channel;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.expression.AbstractSecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionOperations;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @author Steve Riesenberg\n * @since 3.0\n */\npublic class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpressionHandler<FilterInvocation>\n\t\timplements SecurityExpressionHandler<FilterInvocation> {\n\n\tprivate static final String DEFAULT_ROLE_PREFIX = \"ROLE_\";\n\n\tprivate String defaultRolePrefix = DEFAULT_ROLE_PREFIX;\n\n\t@Override\n\t@SuppressWarnings(\"deprecation\")\n\tprotected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,\n\t\t\tFilterInvocation fi) {\n\t\tFilterInvocationExpressionRoot root = new FilterInvocationExpressionRoot(() -> authentication, fi);\n\t\troot.setAuthorizationManagerFactory(getAuthorizationManagerFactory());\n\t\troot.setPermissionEvaluator(getPermissionEvaluator());\n\t\tif (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {\n\t\t\t// Ensure SecurityExpressionRoot can strip the custom role prefix\n\t\t\troot.setDefaultRolePrefix(this.defaultRolePrefix);\n\t\t}\n\t\treturn root;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. The default is\n\t * {@link AuthenticationTrustResolverImpl}.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be\n\t * null.\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tgetDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the default prefix to be added to\n\t * {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasAnyRole(String...)}\n\t * or\n\t * {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasRole(String)}.\n\t * For example, if hasRole(\"ADMIN\") or hasRole(\"ROLE_ADMIN\") is passed in, then the\n\t * role ROLE_ADMIN will be used when the defaultRolePrefix is \"ROLE_\" (default).\n\t * </p>\n\t *\n\t * <p>\n\t * If null or empty, then no default role prefix is used.\n\t * </p>\n\t * @param defaultRolePrefix the default prefix to add to roles. Default \"ROLE_\".\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setDefaultRolePrefix(@Nullable String defaultRolePrefix) {\n\t\tif (defaultRolePrefix == null) {\n\t\t\tdefaultRolePrefix = \"\";\n\t\t}\n\t\tgetDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);\n\t\tthis.defaultRolePrefix = defaultRolePrefix;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/expression/ExpressionBasedFilterInvocationSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.ParseException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Expression-based {@code FilterInvocationSecurityMetadataSource}.\n *\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @since 3.0\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. In the case of\n * method security, please see {@link SecurityAnnotationScanner} and\n * {@link AuthorizationManager}. In the case of channel security, please see\n * {@code HttpsRedirectFilter}. In the case of web security, please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\npublic final class ExpressionBasedFilterInvocationSecurityMetadataSource\n\t\textends DefaultFilterInvocationSecurityMetadataSource {\n\n\tprivate static final Log logger = LogFactory.getLog(ExpressionBasedFilterInvocationSecurityMetadataSource.class);\n\n\tpublic ExpressionBasedFilterInvocationSecurityMetadataSource(\n\t\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap,\n\t\t\tSecurityExpressionHandler<FilterInvocation> expressionHandler) {\n\t\tsuper(processMap(requestMap, expressionHandler.getExpressionParser()));\n\t\tAssert.notNull(expressionHandler, \"A non-null SecurityExpressionHandler is required\");\n\t}\n\n\tprivate static LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> processMap(\n\t\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap, ExpressionParser parser) {\n\t\tAssert.notNull(parser, \"SecurityExpressionHandler returned a null parser object\");\n\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> processed = new LinkedHashMap<>(requestMap);\n\t\trequestMap.forEach((request, value) -> process(parser, request, value, processed::put));\n\t\treturn processed;\n\t}\n\n\tprivate static void process(ExpressionParser parser, RequestMatcher request, Collection<ConfigAttribute> value,\n\t\t\tBiConsumer<RequestMatcher, Collection<ConfigAttribute>> consumer) {\n\t\tString expression = getExpression(request, value);\n\t\tif (logger.isDebugEnabled()) {\n\t\t\tlogger.debug(LogMessage.format(\"Adding web access control expression [%s] for %s\", expression, request));\n\t\t}\n\t\tAbstractVariableEvaluationContextPostProcessor postProcessor = createPostProcessor(request);\n\t\tArrayList<ConfigAttribute> processed = new ArrayList<>(1);\n\t\ttry {\n\t\t\tprocessed.add(new WebExpressionConfigAttribute(parser.parseExpression(expression), postProcessor));\n\t\t}\n\t\tcatch (ParseException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to parse expression '\" + expression + \"'\");\n\t\t}\n\t\tconsumer.accept(request, processed);\n\t}\n\n\tprivate static String getExpression(RequestMatcher request, Collection<ConfigAttribute> value) {\n\t\tAssert.isTrue(value.size() == 1, () -> \"Expected a single expression attribute for \" + request);\n\t\treturn value.toArray(new ConfigAttribute[1])[0].getAttribute();\n\t}\n\n\tprivate static AbstractVariableEvaluationContextPostProcessor createPostProcessor(RequestMatcher request) {\n\t\treturn new RequestVariablesExtractorEvaluationContextPostProcessor(request);\n\t}\n\n\tstatic class RequestVariablesExtractorEvaluationContextPostProcessor\n\t\t\textends AbstractVariableEvaluationContextPostProcessor {\n\n\t\tprivate final RequestMatcher matcher;\n\n\t\tRequestVariablesExtractorEvaluationContextPostProcessor(RequestMatcher matcher) {\n\t\t\tthis.matcher = matcher;\n\t\t}\n\n\t\t@Override\n\t\tMap<String, String> extractVariables(HttpServletRequest request) {\n\t\t\treturn this.matcher.matcher(request).getVariables();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/expression/WebExpressionConfigAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.web.FilterInvocation;\n\n/**\n * Simple expression configuration attribute for use in web request authorizations.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. Please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\n@NullUnmarked\nclass WebExpressionConfigAttribute implements ConfigAttribute, EvaluationContextPostProcessor<FilterInvocation> {\n\n\tprivate final Expression authorizeExpression;\n\n\tprivate final EvaluationContextPostProcessor<FilterInvocation> postProcessor;\n\n\tWebExpressionConfigAttribute(Expression authorizeExpression,\n\t\t\tEvaluationContextPostProcessor<FilterInvocation> postProcessor) {\n\t\tthis.authorizeExpression = authorizeExpression;\n\t\tthis.postProcessor = postProcessor;\n\t}\n\n\tExpression getAuthorizeExpression() {\n\t\treturn this.authorizeExpression;\n\t}\n\n\t@Override\n\tpublic EvaluationContext postProcess(EvaluationContext context, FilterInvocation fi) {\n\t\treturn (this.postProcessor != null) ? this.postProcessor.postProcess(context, fi) : context;\n\t}\n\n\t@Override\n\tpublic String getAttribute() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.authorizeExpression.getExpressionString();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/expression/WebExpressionVoter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.Collection;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.util.Assert;\n\n/**\n * Voter which handles web authorisation decisions.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use {@link WebExpressionAuthorizationManager} instead\n */\n@Deprecated\npublic class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();\n\n\t@Override\n\tpublic int vote(Authentication authentication, FilterInvocation filterInvocation,\n\t\t\tCollection<ConfigAttribute> attributes) {\n\t\tAssert.notNull(authentication, \"authentication must not be null\");\n\t\tAssert.notNull(filterInvocation, \"filterInvocation must not be null\");\n\t\tAssert.notNull(attributes, \"attributes must not be null\");\n\t\tWebExpressionConfigAttribute webExpressionConfigAttribute = findConfigAttribute(attributes);\n\t\tif (webExpressionConfigAttribute == null) {\n\t\t\tthis.logger\n\t\t\t\t.trace(\"Abstained since did not find a config attribute of instance WebExpressionConfigAttribute\");\n\t\t\treturn ACCESS_ABSTAIN;\n\t\t}\n\t\tEvaluationContext ctx = webExpressionConfigAttribute.postProcess(\n\t\t\t\tthis.expressionHandler.createEvaluationContext(authentication, filterInvocation), filterInvocation);\n\t\tboolean granted = ExpressionUtils.evaluateAsBoolean(webExpressionConfigAttribute.getAuthorizeExpression(), ctx);\n\t\tif (granted) {\n\t\t\treturn ACCESS_GRANTED;\n\t\t}\n\t\tthis.logger.trace(\"Voted to deny authorization\");\n\t\treturn ACCESS_DENIED;\n\t}\n\n\tprivate @Nullable WebExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {\n\t\tfor (ConfigAttribute attribute : attributes) {\n\t\t\tif (attribute instanceof WebExpressionConfigAttribute) {\n\t\t\t\treturn (WebExpressionConfigAttribute) attribute;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn attribute instanceof WebExpressionConfigAttribute;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn FilterInvocation.class.isAssignableFrom(clazz);\n\t}\n\n\tpublic void setExpressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {\n\t\tthis.expressionHandler = expressionHandler;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/intercept/DefaultFilterInvocationSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Default implementation of <tt>FilterInvocationDefinitionSource</tt>.\n * <p>\n * Stores an ordered map of {@link RequestMatcher}s to <tt>ConfigAttribute</tt>\n * collections and provides matching of {@code FilterInvocation}s against the items stored\n * in the map.\n * <p>\n * The order of the {@link RequestMatcher}s in the map is very important. The <b>first</b>\n * one which matches the request will be used. Later matchers in the map will not be\n * invoked if a match has already been found. Accordingly, the most specific matchers\n * should be registered first, with the most general matches registered last.\n * <p>\n * The most common method creating an instance is using the Spring Security namespace. For\n * example, the {@code pattern} and {@code access} attributes of the\n * {@code <intercept-url>} elements defined as children of the {@code <http>} element are\n * combined to build the instance used by the {@code FilterSecurityInterceptor}.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. In the case of\n * method security, please see {@link SecurityAnnotationScanner} and\n * {@link AuthorizationManager}. In the case of channel security, please see\n * {@code HttpsRedirectFilter}. In the case of web security, please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\npublic class DefaultFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;\n\n\t/**\n\t * Sets the internal request map from the supplied map. The key elements should be of\n\t * type {@link RequestMatcher}, which. The path stored in the key will depend on the\n\t * type of the supplied UrlMatcher.\n\t * @param requestMap order-preserving map of request definitions to attribute lists\n\t */\n\tpublic DefaultFilterInvocationSecurityMetadataSource(\n\t\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap) {\n\t\tthis.requestMap = requestMap;\n\t}\n\n\t@Override\n\tpublic Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\tSet<ConfigAttribute> allAttributes = new HashSet<>();\n\t\tthis.requestMap.values().forEach(allAttributes::addAll);\n\t\treturn allAttributes;\n\t}\n\n\t@Override\n\tpublic Collection<ConfigAttribute> getAttributes(Object object) {\n\t\tfinal HttpServletRequest request = getHttpServletRequest(object);\n\t\tint count = 0;\n\t\tfor (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : this.requestMap.entrySet()) {\n\t\t\tif (entry.getKey().matches(request)) {\n\t\t\t\treturn entry.getValue();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(LogMessage.format(\"Did not match request to %s - %s (%d/%d)\", entry.getKey(),\n\t\t\t\t\t\t\tentry.getValue(), ++count, this.requestMap.size()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn FilterInvocation.class.isAssignableFrom(clazz);\n\t}\n\n\tprivate HttpServletRequest getHttpServletRequest(Object object) {\n\t\tif (object instanceof FilterInvocation invocation) {\n\t\t\treturn invocation.getHttpRequest();\n\t\t}\n\t\tif (object instanceof HttpServletRequest request) {\n\t\t\treturn request;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"object must be of type FilterInvocation or HttpServletRequest\");\n\t}\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/intercept/FilterInvocationSecurityMetadataSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport org.springframework.security.access.SecurityMetadataSource;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.web.FilterInvocation;\n\n/**\n * Marker interface for <code>SecurityMetadataSource</code> implementations that are\n * designed to perform lookups keyed on {@link FilterInvocation}s.\n *\n * @author Ben Alex\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. In the case of\n * method security, please see {@link SecurityAnnotationScanner} and\n * {@link AuthorizationManager}. In the case of channel security, please see\n * {@code HttpsRedirectFilter}. In the case of web security, please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\npublic interface FilterInvocationSecurityMetadataSource extends SecurityMetadataSource {\n\n}\n"
  },
  {
    "path": "access/src/main/java/org/springframework/security/web/access/intercept/FilterSecurityInterceptor.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.FilterConfig;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.SecurityMetadataSource;\nimport org.springframework.security.access.intercept.AbstractSecurityInterceptor;\nimport org.springframework.security.access.intercept.InterceptorStatusToken;\nimport org.springframework.security.web.FilterInvocation;\n\n/**\n * Performs security handling of HTTP resources via a filter implementation.\n * <p>\n * The <code>SecurityMetadataSource</code> required by this security interceptor is of\n * type {@link FilterInvocationSecurityMetadataSource}.\n * <p>\n * Refer to {@link AbstractSecurityInterceptor} for details on the workflow.\n * </p>\n *\n * @author Ben Alex\n * @author Rob Winch\n * @deprecated Use {@link AuthorizationFilter} instead\n */\n@Deprecated\npublic class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {\n\n\tprivate static final String FILTER_APPLIED = \"__spring_security_filterSecurityInterceptor_filterApplied\";\n\n\tprivate @Nullable FilterInvocationSecurityMetadataSource securityMetadataSource;\n\n\tprivate boolean observeOncePerRequest = false;\n\n\t/**\n\t * Not used (we rely on IoC container lifecycle services instead)\n\t * @param arg0 ignored\n\t *\n\t */\n\t@Override\n\tpublic void init(FilterConfig arg0) {\n\t}\n\n\t/**\n\t * Not used (we rely on IoC container lifecycle services instead)\n\t */\n\t@Override\n\tpublic void destroy() {\n\t}\n\n\t/**\n\t * Method that is actually called by the filter chain. Simply delegates to the\n\t * {@link #invoke(FilterInvocation)} method.\n\t * @param request the servlet request\n\t * @param response the servlet response\n\t * @param chain the filter chain\n\t * @throws IOException if the filter chain fails\n\t * @throws ServletException if the filter chain fails\n\t */\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tinvoke(new FilterInvocation(request, response, chain));\n\t}\n\n\tpublic @Nullable FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {\n\t\treturn this.securityMetadataSource;\n\t}\n\n\t@Override\n\tpublic @Nullable SecurityMetadataSource obtainSecurityMetadataSource() {\n\t\treturn this.securityMetadataSource;\n\t}\n\n\tpublic void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {\n\t\tthis.securityMetadataSource = newSource;\n\t}\n\n\t@Override\n\tpublic Class<?> getSecureObjectClass() {\n\t\treturn FilterInvocation.class;\n\t}\n\n\tpublic void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {\n\t\tif (isApplied(filterInvocation) && this.observeOncePerRequest) {\n\t\t\t// filter already applied to this request and user wants us to observe\n\t\t\t// once-per-request handling, so don't re-do security checking\n\t\t\tfilterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());\n\t\t\treturn;\n\t\t}\n\t\t// first time this request being called, so perform security checking\n\t\tif (filterInvocation.getRequest() != null && this.observeOncePerRequest) {\n\t\t\tfilterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);\n\t\t}\n\t\tInterceptorStatusToken token = super.beforeInvocation(filterInvocation);\n\t\ttry {\n\t\t\tfilterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());\n\t\t}\n\t\tfinally {\n\t\t\tsuper.finallyInvocation(token);\n\t\t}\n\t\tsuper.afterInvocation(token, null);\n\t}\n\n\tprivate boolean isApplied(FilterInvocation filterInvocation) {\n\t\treturn (filterInvocation.getRequest() != null)\n\t\t\t\t&& (filterInvocation.getRequest().getAttribute(FILTER_APPLIED) != null);\n\t}\n\n\t/**\n\t * Indicates whether once-per-request handling will be observed. By default this is\n\t * <code>true</code>, meaning the <code>FilterSecurityInterceptor</code> will only\n\t * execute once-per-request. Sometimes users may wish it to execute more than once per\n\t * request, such as when JSP forwards are being used and filter security is desired on\n\t * each included fragment of the HTTP request.\n\t * @return <code>true</code> (the default) if once-per-request is honoured, otherwise\n\t * <code>false</code> if <code>FilterSecurityInterceptor</code> will enforce\n\t * authorizations for each and every fragment of the HTTP request.\n\t */\n\tpublic boolean isObserveOncePerRequest() {\n\t\treturn this.observeOncePerRequest;\n\t}\n\n\tpublic void setObserveOncePerRequest(boolean observeOncePerRequest) {\n\t\tthis.observeOncePerRequest = observeOncePerRequest;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/AuthenticationCredentialsNotFoundEventTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AuthenticationCredentialsNotFoundEvent}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class AuthenticationCredentialsNotFoundEventTests {\n\n\t@Test\n\tpublic void testRejectsNulls() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AuthenticationCredentialsNotFoundEvent(null,\n\t\t\t\tSecurityConfig.createList(\"TEST\"), new AuthenticationCredentialsNotFoundException(\"test\")));\n\t}\n\n\t@Test\n\tpublic void testRejectsNulls2() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthenticationCredentialsNotFoundEvent(new SimpleMethodInvocation(), null,\n\t\t\t\t\tnew AuthenticationCredentialsNotFoundException(\"test\")));\n\t}\n\n\t@Test\n\tpublic void testRejectsNulls3() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthenticationCredentialsNotFoundEvent(new SimpleMethodInvocation(),\n\t\t\t\t\tSecurityConfig.createList(\"TEST\"), null));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/AuthorizationFailureEventTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.event.AuthorizationFailureEvent;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AuthorizationFailureEvent}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class AuthorizationFailureEventTests {\n\n\tprivate final UsernamePasswordAuthenticationToken foo = UsernamePasswordAuthenticationToken.unauthenticated(\"foo\",\n\t\t\t\"bar\");\n\n\tprivate List<ConfigAttribute> attributes = SecurityConfig.createList(\"TEST\");\n\n\tprivate AccessDeniedException exception = new AuthorizationServiceException(\"error\", new Throwable());\n\n\t@Test\n\tpublic void rejectsNullSecureObject() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationFailureEvent(null, this.attributes, this.foo, this.exception));\n\t}\n\n\t@Test\n\tpublic void rejectsNullAttributesList() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new AuthorizationFailureEvent(new SimpleMethodInvocation(), null, this.foo, this.exception));\n\t}\n\n\t@Test\n\tpublic void rejectsNullAuthentication() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationFailureEvent(new SimpleMethodInvocation(), this.attributes, null,\n\t\t\t\t\tthis.exception));\n\t}\n\n\t@Test\n\tpublic void rejectsNullException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new AuthorizationFailureEvent(new SimpleMethodInvocation(), this.attributes, this.foo, null));\n\t}\n\n\t@Test\n\tpublic void gettersReturnCtorSuppliedData() {\n\t\tAuthorizationFailureEvent event = new AuthorizationFailureEvent(new Object(), this.attributes, this.foo,\n\t\t\t\tthis.exception);\n\t\tassertThat(event.getConfigAttributes()).isSameAs(this.attributes);\n\t\tassertThat(event.getAccessDeniedException()).isSameAs(this.exception);\n\t\tassertThat(event.getAuthentication()).isSameAs(this.foo);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/AuthorizedEventTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.event.AuthorizedEvent;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AuthorizedEvent}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class AuthorizedEventTests {\n\n\t@Test\n\tpublic void testRejectsNulls() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AuthorizedEvent(null,\n\t\t\t\tSecurityConfig.createList(\"TEST\"), UsernamePasswordAuthenticationToken.unauthenticated(\"foo\", \"bar\")));\n\t}\n\n\t@Test\n\tpublic void testRejectsNulls2() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AuthorizedEvent(new SimpleMethodInvocation(), null,\n\t\t\t\tUsernamePasswordAuthenticationToken.unauthenticated(\"foo\", \"bar\")));\n\t}\n\n\t@Test\n\tpublic void testRejectsNulls3() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new AuthorizedEvent(new SimpleMethodInvocation(), SecurityConfig.createList(\"TEST\"), null));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/ITargetObject.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\n/**\n * Represents the interface of a secured object.\n *\n * @author Ben Alex\n */\npublic interface ITargetObject {\n\n\tInteger computeHashCode(String input);\n\n\tint countLength(String input);\n\n\tString makeLowerCase(String input);\n\n\tString makeUpperCase(String input);\n\n\tString publicMakeLowerCase(String input);\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/OtherTargetObject.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\n/**\n * Simply extends {@link TargetObject} so we have a different object to put configuration\n * attributes against.\n * <P>\n * There is no different behaviour. We have to define each method so that\n * <code>Class.getMethod(methodName, args)</code> returns a <code>Method</code>\n * referencing this class rather than the parent class.\n * </p>\n * <P>\n * We need to implement <code>ITargetObject</code> again because the\n * <code>MethodDefinitionAttributes</code> only locates attributes on interfaces\n * explicitly defined by the intercepted class (not the interfaces defined by its parent\n * class or classes).\n * </p>\n *\n * @author Ben Alex\n */\npublic class OtherTargetObject extends TargetObject implements ITargetObject {\n\n\t@Override\n\tpublic String makeLowerCase(String input) {\n\t\treturn super.makeLowerCase(input);\n\t}\n\n\t@Override\n\tpublic String makeUpperCase(String input) {\n\t\treturn super.makeUpperCase(input);\n\t}\n\n\t@Override\n\tpublic String publicMakeLowerCase(String input) {\n\t\treturn super.publicMakeLowerCase(input);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/SecurityConfigTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link SecurityConfig}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class SecurityConfigTests {\n\n\t@Test\n\tpublic void testHashCode() {\n\t\tSecurityConfig config = new SecurityConfig(\"TEST\");\n\t\tassertThat(config.hashCode()).isEqualTo(\"TEST\".hashCode());\n\t}\n\n\t@Test\n\tpublic void testCannotConstructWithNullAttribute() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SecurityConfig(null)); // SEC-727\n\t}\n\n\t@Test\n\tpublic void testCannotConstructWithEmptyAttribute() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SecurityConfig(\"\")); // SEC-727\n\t}\n\n\t@Test\n\tpublic void testNoArgConstructorDoesntExist() throws Exception {\n\t\tassertThatExceptionOfType(NoSuchMethodException.class)\n\t\t\t.isThrownBy(() -> SecurityConfig.class.getDeclaredConstructor((Class[]) null));\n\t}\n\n\t@Test\n\tpublic void testObjectEquals() {\n\t\tSecurityConfig security1 = new SecurityConfig(\"TEST\");\n\t\tSecurityConfig security2 = new SecurityConfig(\"TEST\");\n\t\tassertThat(security2).isEqualTo(security1);\n\t\t// SEC-311: Must observe symmetry requirement of Object.equals(Object) contract\n\t\tString securityString1 = \"TEST\";\n\t\tassertThat(securityString1).isNotSameAs(security1);\n\t\tString securityString2 = \"NOT_EQUAL\";\n\t\tassertThat(!security1.equals(securityString2)).isTrue();\n\t\tSecurityConfig security3 = new SecurityConfig(\"NOT_EQUAL\");\n\t\tassertThat(!security1.equals(security3)).isTrue();\n\t\tMockConfigAttribute mock1 = new MockConfigAttribute(\"TEST\");\n\t\tassertThat(security1).isEqualTo(mock1);\n\t\tMockConfigAttribute mock2 = new MockConfigAttribute(\"NOT_EQUAL\");\n\t\tassertThat(security1).isNotEqualTo(mock2);\n\t\tInteger int1 = 987;\n\t\tassertThat(security1).isNotEqualTo(int1);\n\t}\n\n\t@Test\n\tpublic void testToString() {\n\t\tSecurityConfig config = new SecurityConfig(\"TEST\");\n\t\tassertThat(config.toString()).isEqualTo(\"TEST\");\n\t}\n\n\tprivate class MockConfigAttribute implements ConfigAttribute {\n\n\t\tprivate String attribute;\n\n\t\tMockConfigAttribute(String configuration) {\n\t\t\tthis.attribute = configuration;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getAttribute() {\n\t\t\treturn this.attribute;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/TargetObject.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * Represents a secured object.\n *\n * @author Ben Alex\n */\npublic class TargetObject implements ITargetObject {\n\n\t@Override\n\tpublic Integer computeHashCode(String input) {\n\t\treturn input.hashCode();\n\t}\n\n\t@Override\n\tpublic int countLength(String input) {\n\t\treturn input.length();\n\t}\n\n\t/**\n\t * Returns the lowercase string, followed by security environment information.\n\t * @param input the message to make lowercase\n\t * @return the lowercase message, a space, the <code>Authentication</code> class that\n\t * was on the <code>SecurityContext</code> at the time of method invocation, and a\n\t * boolean indicating if the <code>Authentication</code> object is authenticated or\n\t * not\n\t */\n\t@Override\n\tpublic String makeLowerCase(String input) {\n\t\tAuthentication auth = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (auth == null) {\n\t\t\treturn input.toLowerCase() + \" Authentication empty\";\n\t\t}\n\t\telse {\n\t\t\treturn input.toLowerCase() + \" \" + auth.getClass().getName() + \" \" + auth.isAuthenticated();\n\t\t}\n\t}\n\n\t/**\n\t * Returns the uppercase string, followed by security environment information.\n\t * @param input the message to make uppercase\n\t * @return the uppercase message, a space, the <code>Authentication</code> class that\n\t * was on the <code>SecurityContext</code> at the time of method invocation, and a\n\t * boolean indicating if the <code>Authentication</code> object is authenticated or\n\t * not\n\t */\n\t@Override\n\tpublic String makeUpperCase(String input) {\n\t\tAuthentication auth = SecurityContextHolder.getContext().getAuthentication();\n\t\treturn input.toUpperCase() + \" \" + auth.getClass().getName() + \" \" + auth.isAuthenticated();\n\t}\n\n\t/**\n\t * Delegates through to the {@link #makeLowerCase(String)} method.\n\t * @param input the message to be made lower-case\n\t */\n\t@Override\n\tpublic String publicMakeLowerCase(String input) {\n\t\treturn this.makeLowerCase(input);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/BusinessService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n */\n@Secured({ \"ROLE_USER\" })\n@PermitAll\npublic interface BusinessService extends Serializable {\n\n\t@Secured({ \"ROLE_ADMIN\" })\n\t@RolesAllowed({ \"ROLE_ADMIN\" })\n\t@PreAuthorize(\"hasRole('ROLE_ADMIN')\")\n\tvoid someAdminMethod();\n\n\t@Secured({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\t@RolesAllowed({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\tvoid someUserAndAdminMethod();\n\n\t@Secured({ \"ROLE_USER\" })\n\t@RolesAllowed({ \"ROLE_USER\" })\n\tvoid someUserMethod1();\n\n\t@Secured({ \"ROLE_USER\" })\n\t@RolesAllowed({ \"ROLE_USER\" })\n\tvoid someUserMethod2();\n\n\t@RolesAllowed({ \"USER\" })\n\tvoid rolesAllowedUser();\n\n\tint someOther(String s);\n\n\tint someOther(int input);\n\n\tList<?> methodReturningAList(List<?> someList);\n\n\tObject[] methodReturningAnArray(Object[] someArray);\n\n\tList<?> methodReturningAList(String userName, String extraParam);\n\n\t@RequireAdminRole\n\t@RequireUserRole\n\tdefault void repeatedAnnotations() {\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/BusinessServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Joe Scalise\n */\n@SuppressWarnings(\"serial\")\npublic class BusinessServiceImpl<E extends Entity> implements BusinessService {\n\n\t@Override\n\t@Secured({ \"ROLE_USER\" })\n\tpublic void someUserMethod1() {\n\t}\n\n\t@Override\n\t@Secured({ \"ROLE_USER\" })\n\tpublic void someUserMethod2() {\n\t}\n\n\t@Override\n\t@Secured({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\tpublic void someUserAndAdminMethod() {\n\t}\n\n\t@Override\n\t@Secured({ \"ROLE_ADMIN\" })\n\tpublic void someAdminMethod() {\n\t}\n\n\tpublic E someUserMethod3(final E entity) {\n\t\treturn entity;\n\t}\n\n\t@Override\n\tpublic int someOther(String s) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int someOther(int input) {\n\t\treturn input;\n\t}\n\n\t@Override\n\tpublic List<?> methodReturningAList(List<?> someList) {\n\t\treturn someList;\n\t}\n\n\t@Override\n\tpublic List<Object> methodReturningAList(String userName, String arg2) {\n\t\treturn new ArrayList<>();\n\t}\n\n\t@Override\n\tpublic Object[] methodReturningAnArray(Object[] someArray) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void rolesAllowedUser() {\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/Entity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\n/**\n * Class to act as a superclass for annotations testing.\n *\n * @author Ben Alex\n *\n */\npublic class Entity {\n\n\tpublic Entity(String someParameter) {\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/ExpressionProtectedBusinessServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\n\n@SuppressWarnings(\"serial\")\npublic class ExpressionProtectedBusinessServiceImpl implements BusinessService {\n\n\t@Override\n\tpublic void someAdminMethod() {\n\t}\n\n\t@Override\n\tpublic int someOther(String s) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int someOther(int input) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void someUserAndAdminMethod() {\n\t}\n\n\t@Override\n\tpublic void someUserMethod1() {\n\t}\n\n\t@Override\n\tpublic void someUserMethod2() {\n\t}\n\n\t@Override\n\t@PreFilter(filterTarget = \"someList\", value = \"filterObject == authentication.name or filterObject == 'sam'\")\n\t@PostFilter(\"filterObject == 'bob'\")\n\tpublic List<?> methodReturningAList(List<?> someList) {\n\t\treturn someList;\n\t}\n\n\t@Override\n\tpublic List<Object> methodReturningAList(String userName, String arg2) {\n\t\treturn new ArrayList<>();\n\t}\n\n\t@Override\n\t@PostFilter(\"filterObject == 'bob'\")\n\tpublic Object[] methodReturningAnArray(Object[] someArray) {\n\t\treturn someArray;\n\t}\n\n\t@PreAuthorize(\"#x == 'x' and @number.intValue() == 1294 \")\n\tpublic void methodWithBeanNamePropertyAccessExpression(String x) {\n\t}\n\n\t@Override\n\tpublic void rolesAllowedUser() {\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/Jsr250BusinessServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\n\n/**\n * @author Luke Taylor\n */\n@PermitAll\n@SuppressWarnings(\"serial\")\npublic class Jsr250BusinessServiceImpl implements BusinessService {\n\n\t@Override\n\t@RolesAllowed(\"ROLE_USER\")\n\tpublic void someUserMethod1() {\n\t}\n\n\t@Override\n\t@RolesAllowed(\"ROLE_USER\")\n\tpublic void someUserMethod2() {\n\t}\n\n\t@Override\n\t@RolesAllowed({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\tpublic void someUserAndAdminMethod() {\n\t}\n\n\t@Override\n\t@RolesAllowed(\"ROLE_ADMIN\")\n\tpublic void someAdminMethod() {\n\t}\n\n\t@Override\n\tpublic int someOther(String input) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int someOther(int input) {\n\t\treturn input;\n\t}\n\n\t@Override\n\tpublic List<?> methodReturningAList(List<?> someList) {\n\t\treturn someList;\n\t}\n\n\t@Override\n\tpublic List<?> methodReturningAList(String userName, String arg2) {\n\t\treturn new ArrayList<>();\n\t}\n\n\t@Override\n\tpublic Object[] methodReturningAnArray(Object[] someArray) {\n\t\treturn null;\n\t}\n\n\t@Override\n\t@RolesAllowed({ \"USER\" })\n\tpublic void rolesAllowedUser() {\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/Jsr250MethodSecurityMetadataSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.util.Collection;\n\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.intercept.method.MockMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class Jsr250MethodSecurityMetadataSourceTests {\n\n\tJsr250MethodSecurityMetadataSource mds;\n\n\tA a;\n\n\tUserAllowedClass userAllowed;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mds = new Jsr250MethodSecurityMetadataSource();\n\t\tthis.a = new A();\n\t\tthis.userAllowed = new UserAllowedClass();\n\t}\n\n\tprivate ConfigAttribute[] findAttributes(String methodName) throws Exception {\n\t\treturn this.mds.findAttributes(this.a.getClass().getMethod(methodName), null).toArray(new ConfigAttribute[0]);\n\t}\n\n\t@Test\n\tpublic void methodWithRolesAllowedHasCorrectAttribute() throws Exception {\n\t\tConfigAttribute[] accessAttributes = findAttributes(\"adminMethod\");\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes[0].toString()).isEqualTo(\"ROLE_ADMIN\");\n\t}\n\n\t@Test\n\tpublic void permitAllMethodHasPermitAllAttribute() throws Exception {\n\t\tConfigAttribute[] accessAttributes = findAttributes(\"permitAllMethod\");\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes[0].toString()).isEqualTo(\"jakarta.annotation.security.PermitAll\");\n\t}\n\n\t@Test\n\tpublic void noRoleMethodHasNoAttributes() throws Exception {\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds\n\t\t\t.findAttributes(this.a.getClass().getMethod(\"noRoleMethod\"), null);\n\t\tassertThat(accessAttributes).isNull();\n\t}\n\n\t@Test\n\tpublic void classRoleIsAppliedToNoRoleMethod() throws Exception {\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds\n\t\t\t.findAttributes(this.userAllowed.getClass().getMethod(\"noRoleMethod\"), null);\n\t\tassertThat(accessAttributes).isNull();\n\t}\n\n\t@Test\n\tpublic void methodRoleOverridesClassRole() throws Exception {\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds\n\t\t\t.findAttributes(this.userAllowed.getClass().getMethod(\"adminMethod\"), null);\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes.toArray()[0].toString()).isEqualTo(\"ROLE_ADMIN\");\n\t}\n\n\t@Test\n\tpublic void customDefaultRolePrefix() throws Exception {\n\t\tthis.mds.setDefaultRolePrefix(\"CUSTOMPREFIX_\");\n\t\tConfigAttribute[] accessAttributes = findAttributes(\"adminMethod\");\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes[0].toString()).isEqualTo(\"CUSTOMPREFIX_ADMIN\");\n\t}\n\n\t@Test\n\tpublic void emptyDefaultRolePrefix() throws Exception {\n\t\tthis.mds.setDefaultRolePrefix(\"\");\n\t\tConfigAttribute[] accessAttributes = findAttributes(\"adminMethod\");\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes[0].toString()).isEqualTo(\"ADMIN\");\n\t}\n\n\t@Test\n\tpublic void nullDefaultRolePrefix() throws Exception {\n\t\tthis.mds.setDefaultRolePrefix(null);\n\t\tConfigAttribute[] accessAttributes = findAttributes(\"adminMethod\");\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes[0].toString()).isEqualTo(\"ADMIN\");\n\t}\n\n\t@Test\n\tpublic void alreadyHasDefaultPrefix() throws Exception {\n\t\tConfigAttribute[] accessAttributes = findAttributes(\"roleAdminMethod\");\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes[0].toString()).isEqualTo(\"ROLE_ADMIN\");\n\t}\n\n\t// JSR-250 Spec Tests\n\t/**\n\t * Class-level annotations only affect the class they annotate and their members, that\n\t * is, its methods and fields. They never affect a member declared by a superclass,\n\t * even if it is not hidden or overridden by the class in question.\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void classLevelAnnotationsOnlyAffectTheClassTheyAnnotateAndTheirMembers() throws Exception {\n\t\tChild target = new Child();\n\t\tMockMethodInvocation mi = new MockMethodInvocation(target, target.getClass(), \"notOverriden\");\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds.getAttributes(mi);\n\t\tassertThat(accessAttributes).isNull();\n\t}\n\n\t@Test\n\tpublic void classLevelAnnotationsOnlyAffectTheClassTheyAnnotateAndTheirMembersOverriden() throws Exception {\n\t\tChild target = new Child();\n\t\tMockMethodInvocation mi = new MockMethodInvocation(target, target.getClass(), \"overriden\");\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds.getAttributes(mi);\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes.toArray()[0].toString()).isEqualTo(\"ROLE_DERIVED\");\n\t}\n\n\t@Test\n\tpublic void classLevelAnnotationsImpactMemberLevel() throws Exception {\n\t\tChild target = new Child();\n\t\tMockMethodInvocation mi = new MockMethodInvocation(target, target.getClass(), \"defaults\");\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds.getAttributes(mi);\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes.toArray()[0].toString()).isEqualTo(\"ROLE_DERIVED\");\n\t}\n\n\t@Test\n\tpublic void classLevelAnnotationsIgnoredByExplicitMemberAnnotation() throws Exception {\n\t\tChild target = new Child();\n\t\tMockMethodInvocation mi = new MockMethodInvocation(target, target.getClass(), \"explicitMethod\");\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds.getAttributes(mi);\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes.toArray()[0].toString()).isEqualTo(\"ROLE_EXPLICIT\");\n\t}\n\n\t/**\n\t * The interfaces implemented by a class never contribute annotations to the class\n\t * itself or any of its members.\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void interfacesNeverContributeAnnotationsMethodLevel() throws Exception {\n\t\tParent target = new Parent();\n\t\tMockMethodInvocation mi = new MockMethodInvocation(target, target.getClass(), \"interfaceMethod\");\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds.getAttributes(mi);\n\t\tassertThat(accessAttributes).isEmpty();\n\t}\n\n\t@Test\n\tpublic void interfacesNeverContributeAnnotationsClassLevel() throws Exception {\n\t\tParent target = new Parent();\n\t\tMockMethodInvocation mi = new MockMethodInvocation(target, target.getClass(), \"notOverriden\");\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds.getAttributes(mi);\n\t\tassertThat(accessAttributes).isEmpty();\n\t}\n\n\t@Test\n\tpublic void annotationsOnOverriddenMemberIgnored() throws Exception {\n\t\tChild target = new Child();\n\t\tMockMethodInvocation mi = new MockMethodInvocation(target, target.getClass(), \"overridenIgnored\");\n\t\tCollection<ConfigAttribute> accessAttributes = this.mds.getAttributes(mi);\n\t\tassertThat(accessAttributes).hasSize(1);\n\t\tassertThat(accessAttributes.toArray()[0].toString()).isEqualTo(\"ROLE_DERIVED\");\n\t}\n\n\tpublic static class A {\n\n\t\tpublic void noRoleMethod() {\n\t\t}\n\n\t\t@RolesAllowed(\"ADMIN\")\n\t\tpublic void adminMethod() {\n\t\t}\n\n\t\t@RolesAllowed(\"ROLE_ADMIN\")\n\t\tpublic void roleAdminMethod() {\n\t\t}\n\n\t\t@PermitAll\n\t\tpublic void permitAllMethod() {\n\t\t}\n\n\t}\n\n\t@RolesAllowed(\"USER\")\n\tpublic static class UserAllowedClass {\n\n\t\tpublic void noRoleMethod() {\n\t\t}\n\n\t\t@RolesAllowed(\"ADMIN\")\n\t\tpublic void adminMethod() {\n\t\t}\n\n\t}\n\n\t// JSR-250 Spec\n\t@RolesAllowed(\"IPARENT\")\n\tinterface IParent {\n\n\t\t@RolesAllowed(\"INTERFACEMETHOD\")\n\t\tvoid interfaceMethod();\n\n\t}\n\n\tstatic class Parent implements IParent {\n\n\t\t@Override\n\t\tpublic void interfaceMethod() {\n\t\t}\n\n\t\tpublic void notOverriden() {\n\t\t}\n\n\t\tpublic void overriden() {\n\t\t}\n\n\t\t@RolesAllowed(\"OVERRIDENIGNORED\")\n\t\tpublic void overridenIgnored() {\n\t\t}\n\n\t}\n\n\t@RolesAllowed(\"DERIVED\")\n\tclass Child extends Parent {\n\n\t\t@Override\n\t\tpublic void overriden() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void overridenIgnored() {\n\t\t}\n\n\t\tpublic void defaults() {\n\t\t}\n\n\t\t@RolesAllowed(\"EXPLICIT\")\n\t\tpublic void explicitMethod() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/Jsr250VoterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings(\"deprecation\")\npublic class Jsr250VoterTests {\n\n\t// SEC-1443\n\t@Test\n\tpublic void supportsMultipleRolesCorrectly() {\n\t\tList<ConfigAttribute> attrs = new ArrayList<>();\n\t\tJsr250Voter voter = new Jsr250Voter();\n\t\tattrs.add(new Jsr250SecurityConfig(\"A\"));\n\t\tattrs.add(new Jsr250SecurityConfig(\"B\"));\n\t\tattrs.add(new Jsr250SecurityConfig(\"C\"));\n\t\tassertThat(voter.vote(new TestingAuthenticationToken(\"user\", \"pwd\", \"A\"), new Object(), attrs))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t\tassertThat(voter.vote(new TestingAuthenticationToken(\"user\", \"pwd\", \"B\"), new Object(), attrs))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t\tassertThat(voter.vote(new TestingAuthenticationToken(\"user\", \"pwd\", \"C\"), new Object(), attrs))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t\tassertThat(voter.vote(new TestingAuthenticationToken(\"user\", \"pwd\", \"NONE\"), new Object(), attrs))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_DENIED);\n\t\tassertThat(voter.vote(new TestingAuthenticationToken(\"user\", \"pwd\", \"A\"), new Object(),\n\t\t\t\tSecurityConfig.createList(\"A\", \"B\", \"C\")))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_ABSTAIN);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/RequireAdminRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('ADMIN')\")\n@RolesAllowed(\"ADMIN\")\n@Secured(\"ADMIN\")\npublic @interface RequireAdminRole {\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/RequireUserRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('USER')\")\n@RolesAllowed(\"USER\")\n@Secured(\"USER\")\npublic @interface RequireUserRole {\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataSourceTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.annotation.sec2150.MethodInvocationFactory;\nimport org.springframework.security.access.intercept.method.MockMethodInvocation;\nimport org.springframework.security.core.GrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\n/**\n * Tests for {@link SecuredAnnotationSecurityMetadataSource}\n *\n * @author Mark St.Godard\n * @author Joe Scalise\n * @author Ben Alex\n * @author Luke Taylor\n */\n@SuppressWarnings(\"deprecation\")\npublic class SecuredAnnotationSecurityMetadataSourceTests {\n\n\tprivate SecuredAnnotationSecurityMetadataSource mds = new SecuredAnnotationSecurityMetadataSource();\n\n\t@Test\n\tpublic void genericsSuperclassDeclarationsAreIncludedWhenSubclassesOverride() {\n\t\tMethod method = null;\n\t\ttry {\n\t\t\tmethod = DepartmentServiceImpl.class.getMethod(\"someUserMethod3\", new Class[] { Department.class });\n\t\t}\n\t\tcatch (NoSuchMethodException unexpected) {\n\t\t\tfail(\"Should be a superMethod called 'someUserMethod3' on class!\");\n\t\t}\n\t\tCollection<ConfigAttribute> attrs = this.mds.findAttributes(method, DepartmentServiceImpl.class);\n\t\tassertThat(attrs).isNotNull();\n\t\t// expect 1 attribute\n\t\tassertThat(attrs.size() == 1).as(\"Did not find 1 attribute\").isTrue();\n\t\t// should have 1 SecurityConfig\n\t\tfor (ConfigAttribute sc : attrs) {\n\t\t\tassertThat(sc.getAttribute()).as(\"Found an incorrect role\").isEqualTo(\"ROLE_ADMIN\");\n\t\t}\n\t\tMethod superMethod = null;\n\t\ttry {\n\t\t\tsuperMethod = DepartmentServiceImpl.class.getMethod(\"someUserMethod3\", new Class[] { Entity.class });\n\t\t}\n\t\tcatch (NoSuchMethodException unexpected) {\n\t\t\tfail(\"Should be a superMethod called 'someUserMethod3' on class!\");\n\t\t}\n\t\tCollection<ConfigAttribute> superAttrs = this.mds.findAttributes(superMethod, DepartmentServiceImpl.class);\n\t\tassertThat(superAttrs).isNotNull();\n\t\t// This part of the test relates to SEC-274\n\t\t// expect 1 attribute\n\t\tassertThat(superAttrs).as(\"Did not find 1 attribute\").hasSize(1);\n\t\t// should have 1 SecurityConfig\n\t\tfor (ConfigAttribute sc : superAttrs) {\n\t\t\tassertThat(sc.getAttribute()).as(\"Found an incorrect role\").isEqualTo(\"ROLE_ADMIN\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void classLevelAttributesAreFound() {\n\t\tCollection<ConfigAttribute> attrs = this.mds.findAttributes(BusinessService.class);\n\t\tassertThat(attrs).isNotNull();\n\t\t// expect 1 annotation\n\t\tassertThat(attrs).hasSize(1);\n\t\t// should have 1 SecurityConfig\n\t\tSecurityConfig sc = (SecurityConfig) attrs.toArray()[0];\n\t\tassertThat(sc.getAttribute()).isEqualTo(\"ROLE_USER\");\n\t}\n\n\t@Test\n\tpublic void methodLevelAttributesAreFound() {\n\t\tMethod method = null;\n\t\ttry {\n\t\t\tmethod = BusinessService.class.getMethod(\"someUserAndAdminMethod\", new Class[] {});\n\t\t}\n\t\tcatch (NoSuchMethodException unexpected) {\n\t\t\tfail(\"Should be a method called 'someUserAndAdminMethod' on class!\");\n\t\t}\n\t\tCollection<ConfigAttribute> attrs = this.mds.findAttributes(method, BusinessService.class);\n\t\t// expect 2 attributes\n\t\tassertThat(attrs).hasSize(2);\n\t\tboolean user = false;\n\t\tboolean admin = false;\n\t\t// should have 2 SecurityConfigs\n\t\tfor (ConfigAttribute sc : attrs) {\n\t\t\tassertThat(sc).isInstanceOf(SecurityConfig.class);\n\t\t\tif (sc.getAttribute().equals(\"ROLE_USER\")) {\n\t\t\t\tuser = true;\n\t\t\t}\n\t\t\telse if (sc.getAttribute().equals(\"ROLE_ADMIN\")) {\n\t\t\t\tadmin = true;\n\t\t\t}\n\t\t}\n\t\t// expect to have ROLE_USER and ROLE_ADMIN\n\t\tassertThat(user).isEqualTo(admin).isTrue();\n\t}\n\n\t// SEC-1491\n\t@Test\n\tpublic void customAnnotationAttributesAreFound() {\n\t\tSecuredAnnotationSecurityMetadataSource mds = new SecuredAnnotationSecurityMetadataSource(\n\t\t\t\tnew CustomSecurityAnnotationMetadataExtractor());\n\t\tCollection<ConfigAttribute> attrs = mds.findAttributes(CustomAnnotatedService.class);\n\t\tassertThat(attrs).containsOnly(SecurityEnum.ADMIN);\n\t}\n\n\t@Test\n\tpublic void annotatedAnnotationAtClassLevelIsDetected() throws Exception {\n\t\tMockMethodInvocation annotatedAtClassLevel = new MockMethodInvocation(new AnnotatedAnnotationAtClassLevel(),\n\t\t\t\tReturnVoid.class, \"doSomething\", List.class);\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(annotatedAtClassLevel).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t\tassertThat(attrs).extracting(\"attribute\").containsOnly(\"CUSTOM\");\n\t}\n\n\t@Test\n\tpublic void annotatedAnnotationAtInterfaceLevelIsDetected() throws Exception {\n\t\tMockMethodInvocation annotatedAtInterfaceLevel = new MockMethodInvocation(\n\t\t\t\tnew AnnotatedAnnotationAtInterfaceLevel(), ReturnVoid2.class, \"doSomething\", List.class);\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(annotatedAtInterfaceLevel).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t\tassertThat(attrs).extracting(\"attribute\").containsOnly(\"CUSTOM\");\n\t}\n\n\t@Test\n\tpublic void annotatedAnnotationAtMethodLevelIsDetected() throws Exception {\n\t\tMockMethodInvocation annotatedAtMethodLevel = new MockMethodInvocation(new AnnotatedAnnotationAtMethodLevel(),\n\t\t\t\tReturnVoid.class, \"doSomething\", List.class);\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(annotatedAtMethodLevel).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t\tassertThat(attrs).extracting(\"attribute\").containsOnly(\"CUSTOM\");\n\t}\n\n\t@Test\n\tpublic void proxyFactoryInterfaceAttributesFound() throws Exception {\n\t\tMockMethodInvocation mi = MethodInvocationFactory.createSec2150MethodInvocation();\n\t\tCollection<ConfigAttribute> attributes = this.mds.getAttributes(mi);\n\t\tassertThat(attributes).hasSize(1);\n\t\tassertThat(attributes).extracting(\"attribute\").containsOnly(\"ROLE_PERSON\");\n\t}\n\n\t// Inner classes\n\tclass Department extends Entity {\n\n\t\tDepartment(String name) {\n\t\t\tsuper(name);\n\t\t}\n\n\t}\n\n\tinterface DepartmentService extends BusinessService {\n\n\t\t@Secured({ \"ROLE_USER\" })\n\t\tDepartment someUserMethod3(Department dept);\n\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tclass DepartmentServiceImpl extends BusinessServiceImpl<Department> implements DepartmentService {\n\n\t\t@Override\n\t\t@Secured({ \"ROLE_ADMIN\" })\n\t\tpublic Department someUserMethod3(final Department dept) {\n\t\t\treturn super.someUserMethod3(dept);\n\t\t}\n\n\t}\n\n\t// SEC-1491 Related classes. PoC for custom annotation with enum value.\n\t@CustomSecurityAnnotation(SecurityEnum.ADMIN)\n\tinterface CustomAnnotatedService {\n\n\t}\n\n\tclass CustomAnnotatedServiceImpl implements CustomAnnotatedService {\n\n\t}\n\n\tenum SecurityEnum implements ConfigAttribute, GrantedAuthority {\n\n\t\tADMIN, USER;\n\n\t\t@Override\n\t\tpublic String getAttribute() {\n\t\t\treturn toString();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getAuthority() {\n\t\t\treturn toString();\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface CustomSecurityAnnotation {\n\n\t\tSecurityEnum[] value();\n\n\t}\n\n\tclass CustomSecurityAnnotationMetadataExtractor implements AnnotationMetadataExtractor<CustomSecurityAnnotation> {\n\n\t\t@Override\n\t\tpublic Collection<? extends ConfigAttribute> extractAttributes(CustomSecurityAnnotation securityAnnotation) {\n\t\t\tSecurityEnum[] values = securityAnnotation.value();\n\t\t\treturn EnumSet.copyOf(Arrays.asList(values));\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@Secured(\"CUSTOM\")\n\tpublic @interface AnnotatedAnnotation {\n\n\t}\n\n\tpublic interface ReturnVoid {\n\n\t\tvoid doSomething(List<?> param);\n\n\t}\n\n\t@AnnotatedAnnotation\n\tpublic interface ReturnVoid2 {\n\n\t\tvoid doSomething(List<?> param);\n\n\t}\n\n\t@AnnotatedAnnotation\n\tpublic static class AnnotatedAnnotationAtClassLevel implements ReturnVoid {\n\n\t\t@Override\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n\tpublic static class AnnotatedAnnotationAtInterfaceLevel implements ReturnVoid2 {\n\n\t\t@Override\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n\tpublic static class AnnotatedAnnotationAtMethodLevel implements ReturnVoid {\n\n\t\t@Override\n\t\t@AnnotatedAnnotation\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/sec2150/CrudRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation.sec2150;\n\npublic interface CrudRepository {\n\n\tIterable<Object> findAll();\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/sec2150/MethodInvocationFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation.sec2150;\n\nimport org.springframework.aop.framework.ProxyFactory;\nimport org.springframework.security.access.intercept.method.MockMethodInvocation;\n\npublic final class MethodInvocationFactory {\n\n\tprivate MethodInvocationFactory() {\n\t}\n\n\t/**\n\t * In order to reproduce the bug for SEC-2150, we must have a proxy object that\n\t * implements TargetSourceAware and implements our annotated interface.\n\t * @return the mock method invocation\n\t * @throws NoSuchMethodException\n\t */\n\tpublic static MockMethodInvocation createSec2150MethodInvocation() throws NoSuchMethodException {\n\t\tProxyFactory factory = new ProxyFactory(new Class[] { PersonRepository.class });\n\t\tfactory.setTargetClass(CrudRepository.class);\n\t\tPersonRepository repository = (PersonRepository) factory.getProxy();\n\t\treturn new MockMethodInvocation(repository, PersonRepository.class, \"findAll\");\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/annotation/sec2150/PersonRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation.sec2150;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * Note that JSR-256 states that annotations have no impact when placed on interfaces, so\n * SEC-2150 is not impacted by JSR-256 support.\n *\n * @author Rob Winch\n *\n */\n@Secured(\"ROLE_PERSON\")\n@PreAuthorize(\"hasRole('ROLE_PERSON')\")\npublic interface PersonRepository extends CrudRepository {\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.TypedValue;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n@ExtendWith(MockitoExtension.class)\npublic class DefaultMethodSecurityExpressionHandlerTests {\n\n\tprivate DefaultMethodSecurityExpressionHandler handler;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\t@Mock\n\tprivate MethodInvocation methodInvocation;\n\n\t@Mock\n\tprivate AuthenticationTrustResolver trustResolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.handler = new DefaultMethodSecurityExpressionHandler();\n\t}\n\n\tprivate void setupMocks() {\n\t\tgiven(this.methodInvocation.getThis()).willReturn(new Foo());\n\t\tgiven(this.methodInvocation.getMethod()).willReturn(Foo.class.getMethods()[0]);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setTrustResolverNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setTrustResolver(null));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void createEvaluationContextCustomTrustResolver() {\n\t\tsetupMocks();\n\t\tthis.handler.setTrustResolver(this.trustResolver);\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"anonymous\");\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);\n\t\texpression.getValue(context, Boolean.class);\n\t\tverify(this.trustResolver).isAnonymous(this.authentication);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void filterByKeyWhenUsingMapThenFiltersMap() {\n\t\tsetupMocks();\n\t\tfinal Map<String, String> map = new HashMap<>();\n\t\tmap.put(\"key1\", \"value1\");\n\t\tmap.put(\"key2\", \"value2\");\n\t\tmap.put(\"key3\", \"value3\");\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"filterObject.key eq 'key2'\");\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);\n\t\tObject filtered = this.handler.filter(map, expression, context);\n\t\tassertThat(filtered == map);\n\t\tMap<String, String> result = ((Map<String, String>) filtered);\n\t\tassertThat(result.size() == 1);\n\t\tassertThat(result).containsKey(\"key2\");\n\t\tassertThat(result).containsValue(\"value2\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void filterByValueWhenUsingMapThenFiltersMap() {\n\t\tsetupMocks();\n\t\tfinal Map<String, String> map = new HashMap<>();\n\t\tmap.put(\"key1\", \"value1\");\n\t\tmap.put(\"key2\", \"value2\");\n\t\tmap.put(\"key3\", \"value3\");\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"filterObject.value eq 'value3'\");\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);\n\t\tObject filtered = this.handler.filter(map, expression, context);\n\t\tassertThat(filtered == map);\n\t\tMap<String, String> result = ((Map<String, String>) filtered);\n\t\tassertThat(result.size() == 1);\n\t\tassertThat(result).containsKey(\"key3\");\n\t\tassertThat(result).containsValue(\"value3\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void filterByKeyAndValueWhenUsingMapThenFiltersMap() {\n\t\tsetupMocks();\n\t\tfinal Map<String, String> map = new HashMap<>();\n\t\tmap.put(\"key1\", \"value1\");\n\t\tmap.put(\"key2\", \"value2\");\n\t\tmap.put(\"key3\", \"value3\");\n\t\tExpression expression = this.handler.getExpressionParser()\n\t\t\t.parseExpression(\"(filterObject.key eq 'key1') or (filterObject.value eq 'value2')\");\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);\n\t\tObject filtered = this.handler.filter(map, expression, context);\n\t\tassertThat(filtered == map);\n\t\tMap<String, String> result = ((Map<String, String>) filtered);\n\t\tassertThat(result.size() == 2);\n\t\tassertThat(result).containsKeys(\"key1\", \"key2\");\n\t\tassertThat(result).containsValues(\"value1\", \"value2\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void filterWhenUsingStreamThenFiltersStream() {\n\t\tsetupMocks();\n\t\tfinal Stream<String> stream = Stream.of(\"1\", \"2\", \"3\");\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"filterObject ne '2'\");\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);\n\t\tObject filtered = this.handler.filter(stream, expression, context);\n\t\tassertThat(filtered).isInstanceOf(Stream.class);\n\t\tList<String> list = ((Stream<String>) filtered).collect(Collectors.toList());\n\t\tassertThat(list).containsExactly(\"1\", \"3\");\n\t}\n\n\t@Test\n\tpublic void filterStreamWhenClosedThenUpstreamGetsClosed() {\n\t\tsetupMocks();\n\t\tfinal Stream<?> upstream = mock(Stream.class);\n\t\tdoReturn(Stream.<String>empty()).when(upstream).filter(any());\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"true\");\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);\n\t\t((Stream) this.handler.filter(upstream, expression, context)).close();\n\t\tverify(upstream).close();\n\t}\n\n\t@Test\n\tpublic void createEvaluationContextSupplierAuthentication() {\n\t\tsetupMocks();\n\t\tSupplier<Authentication> mockAuthenticationSupplier = mock();\n\t\tgiven(mockAuthenticationSupplier.get()).willReturn(this.authentication);\n\t\tEvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier,\n\t\t\t\tthis.methodInvocation);\n\t\tverifyNoInteractions(mockAuthenticationSupplier);\n\t\tassertThat(context.getRootObject()).extracting(TypedValue::getValue)\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(MethodSecurityExpressionRoot.class))\n\t\t\t.extracting(SecurityExpressionRoot::getAuthentication)\n\t\t\t.isEqualTo(this.authentication);\n\t\tverify(mockAuthenticationSupplier).get();\n\t}\n\n\tstatic class Foo {\n\n\t\tvoid bar() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/expression/method/ExpressionBasedPreInvocationAdviceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.access.intercept.method.MockMethodInvocation;\nimport org.springframework.security.access.prepost.PreInvocationAttribute;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link ExpressionBasedPreInvocationAdvice}\n *\n * @author Maksim Vinogradov\n * @since 5.2\n */\n@ExtendWith(MockitoExtension.class)\n@SuppressWarnings(\"deprecation\")\npublic class ExpressionBasedPreInvocationAdviceTests {\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\tprivate ExpressionBasedPreInvocationAdvice expressionBasedPreInvocationAdvice;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.expressionBasedPreInvocationAdvice = new ExpressionBasedPreInvocationAdvice();\n\t}\n\n\t@Test\n\tpublic void findFilterTargetNameProvidedButNotMatch() throws Exception {\n\t\tPreInvocationAttribute attribute = new PreInvocationExpressionAttribute(\"true\", \"filterTargetDoesNotMatch\",\n\t\t\t\tnull);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingCollection\", new Class[] { List.class }, new Object[] { new ArrayList<>() });\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.expressionBasedPreInvocationAdvice.before(this.authentication, methodInvocation, attribute));\n\t}\n\n\t@Test\n\tpublic void findFilterTargetNameProvidedArrayUnsupported() throws Exception {\n\t\tPreInvocationAttribute attribute = new PreInvocationExpressionAttribute(\"true\", \"param\", null);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingArray\", new Class[] { String[].class }, new Object[] { new String[0] });\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.expressionBasedPreInvocationAdvice.before(this.authentication, methodInvocation, attribute));\n\t}\n\n\t@Test\n\tpublic void findFilterTargetNameProvided() throws Exception {\n\t\tPreInvocationAttribute attribute = new PreInvocationExpressionAttribute(\"true\", \"param\", null);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingCollection\", new Class[] { List.class }, new Object[] { new ArrayList<>() });\n\t\tboolean result = this.expressionBasedPreInvocationAdvice.before(this.authentication, methodInvocation,\n\t\t\t\tattribute);\n\t\tassertThat(result).isTrue();\n\t}\n\n\t@Test\n\tpublic void findFilterTargetNameNotProvidedArrayUnsupported() throws Exception {\n\t\tPreInvocationAttribute attribute = new PreInvocationExpressionAttribute(\"true\", \"\", null);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingArray\", new Class[] { String[].class }, new Object[] { new String[0] });\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.expressionBasedPreInvocationAdvice.before(this.authentication, methodInvocation, attribute));\n\t}\n\n\t@Test\n\tpublic void findFilterTargetNameNotProvided() throws Exception {\n\t\tPreInvocationAttribute attribute = new PreInvocationExpressionAttribute(\"true\", \"\", null);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingCollection\", new Class[] { List.class }, new Object[] { new ArrayList<>() });\n\t\tboolean result = this.expressionBasedPreInvocationAdvice.before(this.authentication, methodInvocation,\n\t\t\t\tattribute);\n\t\tassertThat(result).isTrue();\n\t}\n\n\t@Test\n\tpublic void findFilterTargetNameNotProvidedTypeNotSupported() throws Exception {\n\t\tPreInvocationAttribute attribute = new PreInvocationExpressionAttribute(\"true\", \"\", null);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"param\" });\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.expressionBasedPreInvocationAdvice.before(this.authentication, methodInvocation, attribute));\n\t}\n\n\t@Test\n\tpublic void findFilterTargetNameNotProvidedMethodAcceptMoreThenOneArgument() throws Exception {\n\t\tPreInvocationAttribute attribute = new PreInvocationExpressionAttribute(\"true\", \"\", null);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingTwoArgs\", new Class[] { String.class, List.class },\n\t\t\t\tnew Object[] { \"param\", new ArrayList<>() });\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.expressionBasedPreInvocationAdvice.before(this.authentication, methodInvocation, attribute));\n\t}\n\n\tprivate class TestClass {\n\n\t\tpublic Boolean doSomethingCollection(List<?> param) {\n\t\t\treturn Boolean.TRUE;\n\t\t}\n\n\t\tpublic Boolean doSomethingArray(String[] param) {\n\t\t\treturn Boolean.TRUE;\n\t\t}\n\n\t\tpublic Boolean doSomethingString(String param) {\n\t\t\treturn Boolean.TRUE;\n\t\t}\n\n\t\tpublic Boolean doSomethingTwoArgs(String param, List<?> list) {\n\t\t\treturn Boolean.TRUE;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/expression/method/MethodExpressionVoterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class MethodExpressionVoterTests {\n\n\tprivate TestingAuthenticationToken joe = new TestingAuthenticationToken(\"joe\", \"joespass\", \"ROLE_blah\");\n\n\tprivate PreInvocationAuthorizationAdviceVoter am = new PreInvocationAuthorizationAdviceVoter(\n\t\t\tnew ExpressionBasedPreInvocationAdvice());\n\n\t@Test\n\tpublic void hasRoleExpressionAllowsUserWithRole() throws Exception {\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingAnArray());\n\t\tassertThat(this.am.vote(this.joe, mi,\n\t\t\t\tcreateAttributes(new PreInvocationExpressionAttribute(null, null, \"hasRole('blah')\"))))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t}\n\n\t@Test\n\tpublic void hasRoleExpressionDeniesUserWithoutRole() throws Exception {\n\t\tList<ConfigAttribute> cad = new ArrayList<>(1);\n\t\tcad.add(new PreInvocationExpressionAttribute(null, null, \"hasRole('joedoesnt')\"));\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingAnArray());\n\t\tassertThat(this.am.vote(this.joe, mi, cad)).isEqualTo(AccessDecisionVoter.ACCESS_DENIED);\n\t}\n\n\t@Test\n\tpublic void matchingArgAgainstAuthenticationNameIsSuccessful() throws Exception {\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingAString(), \"joe\");\n\t\tassertThat(this.am.vote(this.joe, mi,\n\t\t\t\tcreateAttributes(new PreInvocationExpressionAttribute(null, null,\n\t\t\t\t\t\t\"(#argument == principal) and (principal == 'joe')\"))))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t}\n\n\t@Test\n\tpublic void accessIsGrantedIfNoPreAuthorizeAttributeIsUsed() throws Exception {\n\t\tCollection arg = createCollectionArg(\"joe\", \"bob\", \"sam\");\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingACollection(), arg);\n\t\tassertThat(this.am.vote(this.joe, mi,\n\t\t\t\tcreateAttributes(new PreInvocationExpressionAttribute(\"(filterObject == 'jim')\", \"collection\", null))))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t\t// All objects should have been removed, because the expression is always false\n\t\tassertThat(arg).isEmpty();\n\t}\n\n\t@Test\n\tpublic void collectionPreFilteringIsSuccessful() throws Exception {\n\t\tList arg = createCollectionArg(\"joe\", \"bob\", \"sam\");\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingACollection(), arg);\n\t\tthis.am.vote(this.joe, mi, createAttributes(new PreInvocationExpressionAttribute(\n\t\t\t\t\"(filterObject == 'joe' or filterObject == 'sam')\", \"collection\", \"permitAll\")));\n\t\tassertThat(arg).containsExactly(\"joe\", \"sam\");\n\t}\n\n\t@Test\n\tpublic void arraysCannotBePrefiltered() throws Exception {\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingAnArray(),\n\t\t\t\tcreateArrayArg(\"sam\", \"joe\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.am.vote(this.joe, mi,\n\t\t\t\tcreateAttributes(new PreInvocationExpressionAttribute(\"(filterObject == 'jim')\", \"someArray\", null))));\n\t}\n\n\t@Test\n\tpublic void incorrectFilterTargetNameIsRejected() throws Exception {\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingACollection(),\n\t\t\t\tcreateCollectionArg(\"joe\", \"bob\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.am.vote(this.joe, mi,\n\t\t\t\tcreateAttributes(new PreInvocationExpressionAttribute(\"(filterObject == 'joe')\", \"collcetion\", null))));\n\t}\n\n\t@Test\n\tpublic void nullNamedFilterTargetIsRejected() throws Exception {\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingACollection(),\n\t\t\t\tnew Object[] { null });\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.am.vote(this.joe, mi,\n\t\t\t\tcreateAttributes(new PreInvocationExpressionAttribute(\"(filterObject == 'joe')\", \"collection\", null))));\n\t}\n\n\t@Test\n\tpublic void ruleDefinedInAClassMethodIsApplied() throws Exception {\n\t\tMethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(), methodTakingAString(), \"joe\");\n\t\tassertThat(this.am.vote(this.joe, mi,\n\t\t\t\tcreateAttributes(new PreInvocationExpressionAttribute(null, null,\n\t\t\t\t\t\t\"T(org.springframework.security.access.expression.method.SecurityRules).isJoe(#argument)\"))))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t}\n\n\tprivate List<ConfigAttribute> createAttributes(ConfigAttribute... attributes) {\n\t\treturn Arrays.asList(attributes);\n\t}\n\n\tprivate List createCollectionArg(Object... elts) {\n\t\tArrayList result = new ArrayList(elts.length);\n\t\tresult.addAll(Arrays.asList(elts));\n\t\treturn result;\n\t}\n\n\tprivate Object createArrayArg(Object... elts) {\n\t\tArrayList result = new ArrayList(elts.length);\n\t\tresult.addAll(Arrays.asList(elts));\n\t\treturn result.toArray(new Object[0]);\n\t}\n\n\tprivate Method methodTakingAnArray() throws Exception {\n\t\treturn Target.class.getMethod(\"methodTakingAnArray\", Object[].class);\n\t}\n\n\tprivate Method methodTakingAString() throws Exception {\n\t\treturn Target.class.getMethod(\"methodTakingAString\", String.class);\n\t}\n\n\tprivate Method methodTakingACollection() throws Exception {\n\t\treturn Target.class.getMethod(\"methodTakingACollection\", Collection.class);\n\t}\n\n\tprivate interface Target {\n\n\t\tvoid methodTakingAnArray(Object[] args);\n\n\t\tvoid methodTakingAString(String argument);\n\n\t\tCollection methodTakingACollection(Collection collection);\n\n\t}\n\n\tprivate static class TargetImpl implements Target {\n\n\t\t@Override\n\t\tpublic void methodTakingAnArray(Object[] args) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void methodTakingAString(String argument) {\n\t\t};\n\n\t\t@Override\n\t\tpublic Collection methodTakingACollection(Collection collection) {\n\t\t\treturn collection;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/expression/method/MethodSecurityEvaluationContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.mockito.Mockito.doReturn;\n\n/**\n * @author shabarijonnalagadda\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class MethodSecurityEvaluationContextTests {\n\n\t@Mock\n\tprivate ParameterNameDiscoverer paramNameDiscoverer;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\t@Mock\n\tprivate MethodInvocation methodInvocation;\n\n\t@Test\n\tpublic void lookupVariableWhenParameterNameNullThenNotSet() {\n\t\tClass<String> type = String.class;\n\t\tMethod method = ReflectionUtils.findMethod(String.class, \"contains\", CharSequence.class);\n\t\tdoReturn(new String[] { null }).when(this.paramNameDiscoverer).getParameterNames(method);\n\t\tdoReturn(new Object[] { null }).when(this.methodInvocation).getArguments();\n\t\tdoReturn(type).when(this.methodInvocation).getThis();\n\t\tdoReturn(method).when(this.methodInvocation).getMethod();\n\t\tNotNullVariableMethodSecurityEvaluationContext context = new NotNullVariableMethodSecurityEvaluationContext(\n\t\t\t\tthis.authentication, this.methodInvocation, this.paramNameDiscoverer);\n\t\tcontext.lookupVariable(\"testVariable\");\n\t}\n\n\tprivate static class NotNullVariableMethodSecurityEvaluationContext extends MethodSecurityEvaluationContext {\n\n\t\tNotNullVariableMethodSecurityEvaluationContext(Authentication auth, MethodInvocation mi,\n\t\t\t\tParameterNameDiscoverer parameterNameDiscoverer) {\n\t\t\tsuper(auth, mi, parameterNameDiscoverer);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setVariable(String name, @Nullable Object value) {\n\t\t\tif (name == null) {\n\t\t\t\tthrow new IllegalArgumentException(\"name  should not be null\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsuper.setVariable(name, value);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/expression/method/MethodSecurityExpressionRootTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link MethodSecurityExpressionRoot}\n *\n * @author Luke Taylor\n */\npublic class MethodSecurityExpressionRootTests {\n\n\tSpelExpressionParser parser = new SpelExpressionParser();\n\n\tMethodSecurityExpressionRoot root;\n\n\tStandardEvaluationContext ctx;\n\n\tprivate AuthenticationTrustResolver trustResolver;\n\n\tprivate Authentication user;\n\n\t@BeforeEach\n\tpublic void createContext() {\n\t\tthis.user = mock(Authentication.class);\n\t\tthis.root = new MethodSecurityExpressionRoot(() -> this.user, mock(MethodInvocation.class));\n\t\tthis.ctx = new StandardEvaluationContext();\n\t\tthis.ctx.setRootObject(this.root);\n\t\tthis.trustResolver = mock(AuthenticationTrustResolver.class);\n\t\tDefaultAuthorizationManagerFactory<MethodInvocation> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();\n\t\tauthorizationManagerFactory.setTrustResolver(this.trustResolver);\n\t\tthis.root.setAuthorizationManagerFactory(authorizationManagerFactory);\n\t}\n\n\t@Test\n\tpublic void canCallMethodsOnVariables() {\n\t\tthis.ctx.setVariable(\"var\", \"somestring\");\n\t\tExpression e = this.parser.parseExpression(\"#var.length() == 10\");\n\t\tAssertions.assertThat(ExpressionUtils.evaluateAsBoolean(e, this.ctx)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isAnonymousReturnsTrueIfTrustResolverReportsAnonymous() {\n\t\tgiven(this.trustResolver.isAnonymous(this.user)).willReturn(true);\n\t\tAssertions.assertThat(this.root.isAnonymous()).isTrue();\n\t}\n\n\t@Test\n\tpublic void isAnonymousReturnsFalseIfTrustResolverReportsNonAnonymous() {\n\t\tgiven(this.trustResolver.isAnonymous(this.user)).willReturn(false);\n\t\tAssertions.assertThat(this.root.isAnonymous()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasPermissionOnDomainObjectReturnsFalseIfPermissionEvaluatorDoes() {\n\t\tfinal Object dummyDomainObject = new Object();\n\t\tfinal PermissionEvaluator pe = mock(PermissionEvaluator.class);\n\t\tthis.ctx.setVariable(\"domainObject\", dummyDomainObject);\n\t\tthis.root.setPermissionEvaluator(pe);\n\t\tgiven(pe.hasPermission(this.user, dummyDomainObject, \"ignored\")).willReturn(false);\n\t\tAssertions.assertThat(this.root.hasPermission(dummyDomainObject, \"ignored\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasPermissionOnDomainObjectReturnsTrueIfPermissionEvaluatorDoes() {\n\t\tfinal Object dummyDomainObject = new Object();\n\t\tfinal PermissionEvaluator pe = mock(PermissionEvaluator.class);\n\t\tthis.ctx.setVariable(\"domainObject\", dummyDomainObject);\n\t\tthis.root.setPermissionEvaluator(pe);\n\t\tgiven(pe.hasPermission(this.user, dummyDomainObject, \"ignored\")).willReturn(true);\n\t\tAssertions.assertThat(this.root.hasPermission(dummyDomainObject, \"ignored\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasPermissionOnDomainObjectWorksWithIntegerExpressions() {\n\t\tfinal Object dummyDomainObject = new Object();\n\t\tthis.ctx.setVariable(\"domainObject\", dummyDomainObject);\n\t\tfinal PermissionEvaluator pe = mock(PermissionEvaluator.class);\n\t\tthis.root.setPermissionEvaluator(pe);\n\t\tgiven(pe.hasPermission(eq(this.user), eq(dummyDomainObject), any(Integer.class))).willReturn(true, true, false);\n\t\tExpression e = this.parser.parseExpression(\"hasPermission(#domainObject, 0xA)\");\n\t\t// evaluator returns true\n\t\tAssertions.assertThat(ExpressionUtils.evaluateAsBoolean(e, this.ctx)).isTrue();\n\t\te = this.parser.parseExpression(\"hasPermission(#domainObject, 10)\");\n\t\t// evaluator returns true\n\t\tAssertions.assertThat(ExpressionUtils.evaluateAsBoolean(e, this.ctx)).isTrue();\n\t\te = this.parser.parseExpression(\"hasPermission(#domainObject, 0xFF)\");\n\t\t// evaluator returns false, make sure return value matches\n\t\tAssertions.assertThat(ExpressionUtils.evaluateAsBoolean(e, this.ctx)).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasPermissionWorksWithThisObject() {\n\t\tObject targetObject = new Object() {\n\t\t\tpublic String getX() {\n\t\t\t\treturn \"x\";\n\t\t\t}\n\t\t};\n\t\tthis.root.setThis(targetObject);\n\t\tInteger i = 2;\n\t\tPermissionEvaluator pe = mock(PermissionEvaluator.class);\n\t\tthis.root.setPermissionEvaluator(pe);\n\t\tgiven(pe.hasPermission(this.user, targetObject, i)).willReturn(true, false);\n\t\tgiven(pe.hasPermission(this.user, \"x\", i)).willReturn(true);\n\t\tExpression e = this.parser.parseExpression(\"hasPermission(this, 2)\");\n\t\tAssertions.assertThat(ExpressionUtils.evaluateAsBoolean(e, this.ctx)).isTrue();\n\t\te = this.parser.parseExpression(\"hasPermission(this, 2)\");\n\t\tAssertions.assertThat(ExpressionUtils.evaluateAsBoolean(e, this.ctx)).isFalse();\n\t\te = this.parser.parseExpression(\"hasPermission(this.x, 2)\");\n\t\tAssertions.assertThat(ExpressionUtils.evaluateAsBoolean(e, this.ctx)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/expression/method/PrePostAnnotationSecurityMetadataSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.annotation.sec2150.MethodInvocationFactory;\nimport org.springframework.security.access.intercept.method.MockMethodInvocation;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\n@SuppressWarnings(\"deprecation\")\npublic class PrePostAnnotationSecurityMetadataSourceTests {\n\n\tprivate PrePostAnnotationSecurityMetadataSource mds = new PrePostAnnotationSecurityMetadataSource(\n\t\t\tnew ExpressionBasedAnnotationAttributeFactory(new DefaultMethodSecurityExpressionHandler()));\n\n\tprivate MockMethodInvocation voidImpl1;\n\n\tprivate MockMethodInvocation voidImpl2;\n\n\tprivate MockMethodInvocation voidImpl3;\n\n\tprivate MockMethodInvocation listImpl1;\n\n\tprivate MockMethodInvocation notherListImpl1;\n\n\tprivate MockMethodInvocation notherListImpl2;\n\n\tprivate MockMethodInvocation annotatedAtClassLevel;\n\n\tprivate MockMethodInvocation annotatedAtInterfaceLevel;\n\n\tprivate MockMethodInvocation annotatedAtMethodLevel;\n\n\t@BeforeEach\n\tpublic void setUpData() throws Exception {\n\t\tthis.voidImpl1 = new MockMethodInvocation(new ReturnVoidImpl1(), ReturnVoid.class, \"doSomething\", List.class);\n\t\tthis.voidImpl2 = new MockMethodInvocation(new ReturnVoidImpl2(), ReturnVoid.class, \"doSomething\", List.class);\n\t\tthis.voidImpl3 = new MockMethodInvocation(new ReturnVoidImpl3(), ReturnVoid.class, \"doSomething\", List.class);\n\t\tthis.listImpl1 = new MockMethodInvocation(new ReturnAListImpl1(), ReturnAList.class, \"doSomething\", List.class);\n\t\tthis.notherListImpl1 = new MockMethodInvocation(new ReturnAnotherListImpl1(), ReturnAnotherList.class,\n\t\t\t\t\"doSomething\", List.class);\n\t\tthis.notherListImpl2 = new MockMethodInvocation(new ReturnAnotherListImpl2(), ReturnAnotherList.class,\n\t\t\t\t\"doSomething\", List.class);\n\t\tthis.annotatedAtClassLevel = new MockMethodInvocation(new CustomAnnotationAtClassLevel(), ReturnVoid.class,\n\t\t\t\t\"doSomething\", List.class);\n\t\tthis.annotatedAtInterfaceLevel = new MockMethodInvocation(new CustomAnnotationAtInterfaceLevel(),\n\t\t\t\tReturnVoid2.class, \"doSomething\", List.class);\n\t\tthis.annotatedAtMethodLevel = new MockMethodInvocation(new CustomAnnotationAtMethodLevel(), ReturnVoid.class,\n\t\t\t\t\"doSomething\", List.class);\n\t}\n\n\t@Test\n\tpublic void classLevelPreAnnotationIsPickedUpWhenNoMethodLevelExists() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.voidImpl1).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t\tassertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();\n\t\tPreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];\n\t\tassertThat(pre.getAuthorizeExpression()).isNotNull();\n\t\tassertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo(\"someExpression\");\n\t\tassertThat(pre.getFilterExpression()).isNull();\n\t}\n\n\t@Test\n\tpublic void mixedClassAndMethodPreAnnotationsAreBothIncluded() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.voidImpl2).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t\tassertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();\n\t\tPreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];\n\t\tassertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo(\"someExpression\");\n\t\tassertThat(pre.getFilterExpression()).isNotNull();\n\t\tassertThat(pre.getFilterExpression().getExpressionString()).isEqualTo(\"somePreFilterExpression\");\n\t}\n\n\t@Test\n\tpublic void methodWithPreFilterOnlyIsAllowed() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.voidImpl3).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t\tassertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();\n\t\tPreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];\n\t\tassertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo(\"permitAll\");\n\t\tassertThat(pre.getFilterExpression()).isNotNull();\n\t\tassertThat(pre.getFilterExpression().getExpressionString()).isEqualTo(\"somePreFilterExpression\");\n\t}\n\n\t@Test\n\tpublic void methodWithPostFilterOnlyIsAllowed() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.listImpl1).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(2);\n\t\tassertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();\n\t\tassertThat(attrs[1] instanceof PostInvocationExpressionAttribute).isTrue();\n\t\tPreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];\n\t\tPostInvocationExpressionAttribute post = (PostInvocationExpressionAttribute) attrs[1];\n\t\tassertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo(\"permitAll\");\n\t\tassertThat(post.getFilterExpression()).isNotNull();\n\t\tassertThat(post.getFilterExpression().getExpressionString()).isEqualTo(\"somePostFilterExpression\");\n\t}\n\n\t@Test\n\tpublic void interfaceAttributesAreIncluded() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.notherListImpl1).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t\tassertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();\n\t\tPreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];\n\t\tassertThat(pre.getFilterExpression()).isNotNull();\n\t\tassertThat(pre.getAuthorizeExpression()).isNotNull();\n\t\tassertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo(\"interfaceMethodAuthzExpression\");\n\t\tassertThat(pre.getFilterExpression().getExpressionString()).isEqualTo(\"interfacePreFilterExpression\");\n\t}\n\n\t@Test\n\tpublic void classAttributesTakesPrecedeceOverInterfaceAttributes() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.notherListImpl2).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t\tassertThat(attrs[0] instanceof PreInvocationExpressionAttribute).isTrue();\n\t\tPreInvocationExpressionAttribute pre = (PreInvocationExpressionAttribute) attrs[0];\n\t\tassertThat(pre.getFilterExpression()).isNotNull();\n\t\tassertThat(pre.getAuthorizeExpression()).isNotNull();\n\t\tassertThat(pre.getAuthorizeExpression().getExpressionString()).isEqualTo(\"interfaceMethodAuthzExpression\");\n\t\tassertThat(pre.getFilterExpression().getExpressionString()).isEqualTo(\"classMethodPreFilterExpression\");\n\t}\n\n\t@Test\n\tpublic void customAnnotationAtClassLevelIsDetected() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.annotatedAtClassLevel).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void customAnnotationAtInterfaceLevelIsDetected() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.annotatedAtInterfaceLevel)\n\t\t\t.toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void customAnnotationAtMethodLevelIsDetected() {\n\t\tConfigAttribute[] attrs = this.mds.getAttributes(this.annotatedAtMethodLevel).toArray(new ConfigAttribute[0]);\n\t\tassertThat(attrs).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void proxyFactoryInterfaceAttributesFound() throws Exception {\n\t\tMockMethodInvocation mi = MethodInvocationFactory.createSec2150MethodInvocation();\n\t\tCollection<ConfigAttribute> attributes = this.mds.getAttributes(mi);\n\t\tassertThat(attributes).hasSize(1);\n\t\tExpression expression = (Expression) ReflectionTestUtils.getField(attributes.iterator().next(),\n\t\t\t\t\"authorizeExpression\");\n\t\tassertThat(expression.getExpressionString()).isEqualTo(\"hasRole('ROLE_PERSON')\");\n\t}\n\n\tpublic interface ReturnVoid {\n\n\t\tvoid doSomething(List<?> param);\n\n\t}\n\n\tpublic interface ReturnAList {\n\n\t\tList<?> doSomething(List<?> param);\n\n\t}\n\n\t@PreAuthorize(\"interfaceAuthzExpression\")\n\tpublic interface ReturnAnotherList {\n\n\t\t@PreAuthorize(\"interfaceMethodAuthzExpression\")\n\t\t@PreFilter(filterTarget = \"param\", value = \"interfacePreFilterExpression\")\n\t\tList<?> doSomething(List<?> param);\n\n\t}\n\n\t@PreAuthorize(\"someExpression\")\n\tpublic static class ReturnVoidImpl1 implements ReturnVoid {\n\n\t\t@Override\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"someExpression\")\n\tpublic static class ReturnVoidImpl2 implements ReturnVoid {\n\n\t\t@Override\n\t\t@PreFilter(filterTarget = \"param\", value = \"somePreFilterExpression\")\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n\tpublic static class ReturnVoidImpl3 implements ReturnVoid {\n\n\t\t@Override\n\t\t@PreFilter(filterTarget = \"param\", value = \"somePreFilterExpression\")\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n\tpublic static class ReturnAListImpl1 implements ReturnAList {\n\n\t\t@Override\n\t\t@PostFilter(\"somePostFilterExpression\")\n\t\tpublic List<?> doSomething(List<?> param) {\n\t\t\treturn param;\n\t\t}\n\n\t}\n\n\tpublic static class ReturnAListImpl2 implements ReturnAList {\n\n\t\t@Override\n\t\t@PreAuthorize(\"someExpression\")\n\t\t@PreFilter(filterTarget = \"param\", value = \"somePreFilterExpression\")\n\t\t@PostFilter(\"somePostFilterExpression\")\n\t\t@PostAuthorize(\"somePostAuthorizeExpression\")\n\t\tpublic List<?> doSomething(List<?> param) {\n\t\t\treturn param;\n\t\t}\n\n\t}\n\n\tpublic static class ReturnAnotherListImpl1 implements ReturnAnotherList {\n\n\t\t@Override\n\t\tpublic List<?> doSomething(List<?> param) {\n\t\t\treturn param;\n\t\t}\n\n\t}\n\n\tpublic static class ReturnAnotherListImpl2 implements ReturnAnotherList {\n\n\t\t@Override\n\t\t@PreFilter(filterTarget = \"param\", value = \"classMethodPreFilterExpression\")\n\t\tpublic List<?> doSomething(List<?> param) {\n\t\t\treturn param;\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@PreAuthorize(\"customAnnotationExpression\")\n\tpublic @interface CustomAnnotation {\n\n\t}\n\n\t@CustomAnnotation\n\tpublic interface ReturnVoid2 {\n\n\t\tvoid doSomething(List<?> param);\n\n\t}\n\n\t@CustomAnnotation\n\tpublic static class CustomAnnotationAtClassLevel implements ReturnVoid {\n\n\t\t@Override\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n\tpublic static class CustomAnnotationAtInterfaceLevel implements ReturnVoid2 {\n\n\t\t@Override\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n\tpublic static class CustomAnnotationAtMethodLevel implements ReturnVoid {\n\n\t\t@Override\n\t\t@CustomAnnotation\n\t\tpublic void doSomething(List<?> param) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/expression/method/SecurityRules.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\npublic final class SecurityRules {\n\n\tprivate SecurityRules() {\n\t}\n\n\tpublic static boolean disallow() {\n\t\treturn false;\n\t}\n\n\tpublic static boolean allow() {\n\t\treturn false;\n\t}\n\n\tpublic static boolean isJoe(String s) {\n\t\treturn \"joe\".equals(s);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/AbstractSecurityInterceptorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.SecurityMetadataSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests some {@link AbstractSecurityInterceptor} methods. Most of the testing for this\n * class is found in the {@code MethodSecurityInterceptorTests} class.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class AbstractSecurityInterceptorTests {\n\n\t@Test\n\tpublic void detectsIfInvocationPassedIncompatibleSecureObject() {\n\t\tMockSecurityInterceptorWhichOnlySupportsStrings si = new MockSecurityInterceptorWhichOnlySupportsStrings();\n\t\tsi.setRunAsManager(mock(RunAsManager.class));\n\t\tsi.setAuthenticationManager(mock(AuthenticationManager.class));\n\t\tsi.setAfterInvocationManager(mock(AfterInvocationManager.class));\n\t\tsi.setAccessDecisionManager(mock(AccessDecisionManager.class));\n\t\tsi.setSecurityMetadataSource(mock(SecurityMetadataSource.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> si.beforeInvocation(new SimpleMethodInvocation()));\n\t}\n\n\t@Test\n\tpublic void detectsViolationOfGetSecureObjectClassMethod() throws Exception {\n\t\tMockSecurityInterceptorReturnsNull si = new MockSecurityInterceptorReturnsNull();\n\t\tsi.setRunAsManager(mock(RunAsManager.class));\n\t\tsi.setAuthenticationManager(mock(AuthenticationManager.class));\n\t\tsi.setAfterInvocationManager(mock(AfterInvocationManager.class));\n\t\tsi.setAccessDecisionManager(mock(AccessDecisionManager.class));\n\t\tsi.setSecurityMetadataSource(mock(SecurityMetadataSource.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(si::afterPropertiesSet);\n\t}\n\n\tprivate class MockSecurityInterceptorReturnsNull extends AbstractSecurityInterceptor {\n\n\t\tprivate SecurityMetadataSource securityMetadataSource;\n\n\t\t@Override\n\t\tpublic Class<?> getSecureObjectClass() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic SecurityMetadataSource obtainSecurityMetadataSource() {\n\t\t\treturn this.securityMetadataSource;\n\t\t}\n\n\t\tvoid setSecurityMetadataSource(SecurityMetadataSource securityMetadataSource) {\n\t\t\tthis.securityMetadataSource = securityMetadataSource;\n\t\t}\n\n\t}\n\n\tprivate class MockSecurityInterceptorWhichOnlySupportsStrings extends AbstractSecurityInterceptor {\n\n\t\tprivate SecurityMetadataSource securityMetadataSource;\n\n\t\t@Override\n\t\tpublic Class<?> getSecureObjectClass() {\n\t\t\treturn String.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic SecurityMetadataSource obtainSecurityMetadataSource() {\n\t\t\treturn this.securityMetadataSource;\n\t\t}\n\n\t\tvoid setSecurityMetadataSource(SecurityMetadataSource securityMetadataSource) {\n\t\t\tthis.securityMetadataSource = securityMetadataSource;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/AfterInvocationProviderManagerTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Vector;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.AfterInvocationProvider;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AfterInvocationProviderManager}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class AfterInvocationProviderManagerTests {\n\n\t@Test\n\tpublic void testCorrectOperation() throws Exception {\n\t\tAfterInvocationProviderManager manager = new AfterInvocationProviderManager();\n\t\tList list = new Vector();\n\t\tlist.add(new MockAfterInvocationProvider(\"swap1\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP1\")));\n\t\tlist.add(new MockAfterInvocationProvider(\"swap2\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP2\")));\n\t\tlist.add(new MockAfterInvocationProvider(\"swap3\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP3\")));\n\t\tmanager.setProviders(list);\n\t\tassertThat(manager.getProviders()).isEqualTo(list);\n\t\tmanager.afterPropertiesSet();\n\t\tList<ConfigAttribute> attr1 = SecurityConfig.createList(new String[] { \"GIVE_ME_SWAP1\" });\n\t\tList<ConfigAttribute> attr2 = SecurityConfig.createList(new String[] { \"GIVE_ME_SWAP2\" });\n\t\tList<ConfigAttribute> attr3 = SecurityConfig.createList(new String[] { \"GIVE_ME_SWAP3\" });\n\t\tList<ConfigAttribute> attr2and3 = SecurityConfig.createList(new String[] { \"GIVE_ME_SWAP2\", \"GIVE_ME_SWAP3\" });\n\t\tList<ConfigAttribute> attr4 = SecurityConfig.createList(new String[] { \"NEVER_CAUSES_SWAP\" });\n\t\tassertThat(manager.decide(null, new SimpleMethodInvocation(), attr1, \"content-before-swapping\"))\n\t\t\t.isEqualTo(\"swap1\");\n\t\tassertThat(manager.decide(null, new SimpleMethodInvocation(), attr2, \"content-before-swapping\"))\n\t\t\t.isEqualTo(\"swap2\");\n\t\tassertThat(manager.decide(null, new SimpleMethodInvocation(), attr3, \"content-before-swapping\"))\n\t\t\t.isEqualTo(\"swap3\");\n\t\tassertThat(manager.decide(null, new SimpleMethodInvocation(), attr4, \"content-before-swapping\"))\n\t\t\t.isEqualTo(\"content-before-swapping\");\n\t\tassertThat(manager.decide(null, new SimpleMethodInvocation(), attr2and3, \"content-before-swapping\"))\n\t\t\t.isEqualTo(\"swap3\");\n\t}\n\n\t@Test\n\tpublic void testRejectsEmptyProvidersList() {\n\t\tAfterInvocationProviderManager manager = new AfterInvocationProviderManager();\n\t\tList list = new Vector();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setProviders(list));\n\t}\n\n\t@Test\n\tpublic void testRejectsNonAfterInvocationProviders() {\n\t\tAfterInvocationProviderManager manager = new AfterInvocationProviderManager();\n\t\tList list = new Vector();\n\t\tlist.add(new MockAfterInvocationProvider(\"swap1\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP1\")));\n\t\tlist.add(45);\n\t\tlist.add(new MockAfterInvocationProvider(\"swap3\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP3\")));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setProviders(list));\n\t}\n\n\t@Test\n\tpublic void testRejectsNullProvidersList() throws Exception {\n\t\tAfterInvocationProviderManager manager = new AfterInvocationProviderManager();\n\t\tassertThatIllegalArgumentException().isThrownBy(manager::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testSupportsConfigAttributeIteration() throws Exception {\n\t\tAfterInvocationProviderManager manager = new AfterInvocationProviderManager();\n\t\tList list = new Vector();\n\t\tlist.add(new MockAfterInvocationProvider(\"swap1\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP1\")));\n\t\tlist.add(new MockAfterInvocationProvider(\"swap2\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP2\")));\n\t\tlist.add(new MockAfterInvocationProvider(\"swap3\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP3\")));\n\t\tmanager.setProviders(list);\n\t\tmanager.afterPropertiesSet();\n\t\tassertThat(manager.supports(new SecurityConfig(\"UNKNOWN_ATTRIB\"))).isFalse();\n\t\tassertThat(manager.supports(new SecurityConfig(\"GIVE_ME_SWAP2\"))).isTrue();\n\t}\n\n\t@Test\n\tpublic void testSupportsSecureObjectIteration() throws Exception {\n\t\tAfterInvocationProviderManager manager = new AfterInvocationProviderManager();\n\t\tList list = new Vector();\n\t\tlist.add(new MockAfterInvocationProvider(\"swap1\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP1\")));\n\t\tlist.add(new MockAfterInvocationProvider(\"swap2\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP2\")));\n\t\tlist.add(new MockAfterInvocationProvider(\"swap3\", MethodInvocation.class, new SecurityConfig(\"GIVE_ME_SWAP3\")));\n\t\tmanager.setProviders(list);\n\t\tmanager.afterPropertiesSet();\n\t\t// assertFalse(manager.supports(FilterInvocation.class));\n\t\tassertThat(manager.supports(MethodInvocation.class)).isTrue();\n\t}\n\n\t/**\n\t * Always returns the constructor-defined <code>forceReturnObject</code>, provided the\n\t * same configuration attribute was provided. Also stores the secure object it\n\t * supports.\n\t */\n\tprivate class MockAfterInvocationProvider implements AfterInvocationProvider {\n\n\t\tprivate Class secureObject;\n\n\t\tprivate ConfigAttribute configAttribute;\n\n\t\tprivate Object forceReturnObject;\n\n\t\tMockAfterInvocationProvider(Object forceReturnObject, Class secureObject, ConfigAttribute configAttribute) {\n\t\t\tthis.forceReturnObject = forceReturnObject;\n\t\t\tthis.secureObject = secureObject;\n\t\t\tthis.configAttribute = configAttribute;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,\n\t\t\t\tObject returnedObject) throws AccessDeniedException {\n\t\t\tif (config.contains(this.configAttribute)) {\n\t\t\t\treturn this.forceReturnObject;\n\t\t\t}\n\t\t\treturn returnedObject;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> clazz) {\n\t\t\treturn this.secureObject.isAssignableFrom(clazz);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(ConfigAttribute attribute) {\n\t\t\treturn attribute.equals(this.configAttribute);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/InterceptorStatusTokenTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link InterceptorStatusToken}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class InterceptorStatusTokenTests {\n\n\t@Test\n\tpublic void testOperation() {\n\t\tList<ConfigAttribute> attr = SecurityConfig.createList(\"FOO\");\n\t\tMethodInvocation mi = new SimpleMethodInvocation();\n\t\tSecurityContext ctx = SecurityContextHolder.createEmptyContext();\n\t\tInterceptorStatusToken token = new InterceptorStatusToken(ctx, true, attr, mi);\n\t\tassertThat(token.isContextHolderRefreshRequired()).isTrue();\n\t\tassertThat(token.getAttributes()).isEqualTo(attr);\n\t\tassertThat(token.getSecureObject()).isEqualTo(mi);\n\t\tassertThat(token.getSecurityContext()).isSameAs(ctx);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/NullRunAsManagerTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.SecurityConfig;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link NullRunAsManager}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class NullRunAsManagerTests {\n\n\t@Test\n\tpublic void testAlwaysReturnsNull() {\n\t\tNullRunAsManager runAs = new NullRunAsManager();\n\t\tassertThat(runAs.buildRunAs(null, null, null)).isNull();\n\t}\n\n\t@Test\n\tpublic void testAlwaysSupportsClass() {\n\t\tNullRunAsManager runAs = new NullRunAsManager();\n\t\tassertThat(runAs.supports(String.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNeverSupportsAttribute() {\n\t\tNullRunAsManager runAs = new NullRunAsManager();\n\t\tassertThat(runAs.supports(new SecurityConfig(\"X\"))).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link RunAsImplAuthenticationProvider}.\n */\n@SuppressWarnings(\"deprecation\")\npublic class RunAsImplAuthenticationProviderTests {\n\n\t@Test\n\tpublic void testAuthenticationFailDueToWrongKey() {\n\t\tRunAsUserToken token = new RunAsUserToken(\"wrong_key\", \"Test\", \"Password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"), UsernamePasswordAuthenticationToken.class);\n\t\tRunAsImplAuthenticationProvider provider = new RunAsImplAuthenticationProvider();\n\t\tprovider.setKey(\"hello_world\");\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticationSuccess() {\n\t\tRunAsUserToken token = new RunAsUserToken(\"my_password\", \"Test\", \"Password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"), UsernamePasswordAuthenticationToken.class);\n\t\tRunAsImplAuthenticationProvider provider = new RunAsImplAuthenticationProvider();\n\t\tprovider.setKey(\"my_password\");\n\t\tAuthentication result = provider.authenticate(token);\n\t\tassertThat(result instanceof RunAsUserToken).as(\"Should have returned RunAsUserToken\").isTrue();\n\t\tRunAsUserToken resultCast = (RunAsUserToken) result;\n\t\tassertThat(resultCast.getKeyHash()).isEqualTo(\"my_password\".hashCode());\n\t}\n\n\t@Test\n\tpublic void testStartupFailsIfNoKey() throws Exception {\n\t\tRunAsImplAuthenticationProvider provider = new RunAsImplAuthenticationProvider();\n\t\tassertThatIllegalArgumentException().isThrownBy(provider::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testStartupSuccess() throws Exception {\n\t\tRunAsImplAuthenticationProvider provider = new RunAsImplAuthenticationProvider();\n\t\tprovider.setKey(\"hello_world\");\n\t\tassertThat(provider.getKey()).isEqualTo(\"hello_world\");\n\t\tprovider.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tRunAsImplAuthenticationProvider provider = new RunAsImplAuthenticationProvider();\n\t\tassertThat(provider.supports(RunAsUserToken.class)).isTrue();\n\t\tassertThat(!provider.supports(TestingAuthenticationToken.class)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/RunAsManagerImplTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.fail;\n\n/**\n * Tests {@link RunAsManagerImpl}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class RunAsManagerImplTests {\n\n\t@Test\n\tpublic void testAlwaysSupportsClass() {\n\t\tRunAsManagerImpl runAs = new RunAsManagerImpl();\n\t\tassertThat(runAs.supports(String.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testDoesNotReturnAdditionalAuthoritiesIfCalledWithoutARunAsSetting() {\n\t\tUsernamePasswordAuthenticationToken inputToken = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tRunAsManagerImpl runAs = new RunAsManagerImpl();\n\t\trunAs.setKey(\"my_password\");\n\t\tAuthentication resultingToken = runAs.buildRunAs(inputToken, new Object(),\n\t\t\t\tSecurityConfig.createList(\"SOMETHING_WE_IGNORE\"));\n\t\tassertThat(resultingToken).isNull();\n\t}\n\n\t@Test\n\tpublic void testRespectsRolePrefix() {\n\t\tUsernamePasswordAuthenticationToken inputToken = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ONE\", \"TWO\"));\n\t\tRunAsManagerImpl runAs = new RunAsManagerImpl();\n\t\trunAs.setKey(\"my_password\");\n\t\trunAs.setRolePrefix(\"FOOBAR_\");\n\t\tAuthentication result = runAs.buildRunAs(inputToken, new Object(),\n\t\t\t\tSecurityConfig.createList(\"RUN_AS_SOMETHING\"));\n\t\tassertThat(result instanceof RunAsUserToken).withFailMessage(\"Should have returned a RunAsUserToken\").isTrue();\n\t\tassertThat(result.getPrincipal()).isEqualTo(inputToken.getPrincipal());\n\t\tassertThat(result.getCredentials()).isEqualTo(inputToken.getCredentials());\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(authorities).contains(\"FOOBAR_RUN_AS_SOMETHING\");\n\t\tassertThat(authorities).contains(\"ONE\");\n\t\tassertThat(authorities).contains(\"TWO\");\n\t\tRunAsUserToken resultCast = (RunAsUserToken) result;\n\t\tassertThat(resultCast.getKeyHash()).isEqualTo(\"my_password\".hashCode());\n\t}\n\n\t@Test\n\tpublic void testReturnsAdditionalGrantedAuthorities() {\n\t\tUsernamePasswordAuthenticationToken inputToken = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tRunAsManagerImpl runAs = new RunAsManagerImpl();\n\t\trunAs.setKey(\"my_password\");\n\t\tAuthentication result = runAs.buildRunAs(inputToken, new Object(),\n\t\t\t\tSecurityConfig.createList(\"RUN_AS_SOMETHING\"));\n\t\tif (!(result instanceof RunAsUserToken)) {\n\t\t\tfail(\"Should have returned a RunAsUserToken\");\n\t\t}\n\t\tassertThat(result.getPrincipal()).isEqualTo(inputToken.getPrincipal());\n\t\tassertThat(result.getCredentials()).isEqualTo(inputToken.getCredentials());\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(authorities).contains(\"ROLE_RUN_AS_SOMETHING\");\n\t\tassertThat(authorities).contains(\"ROLE_ONE\");\n\t\tassertThat(authorities).contains(\"ROLE_TWO\");\n\t\tRunAsUserToken resultCast = (RunAsUserToken) result;\n\t\tassertThat(resultCast.getKeyHash()).isEqualTo(\"my_password\".hashCode());\n\t}\n\n\t@Test\n\tpublic void testStartupDetectsMissingKey() throws Exception {\n\t\tRunAsManagerImpl runAs = new RunAsManagerImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(runAs::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testStartupSuccessfulWithKey() throws Exception {\n\t\tRunAsManagerImpl runAs = new RunAsManagerImpl();\n\t\trunAs.setKey(\"hello_world\");\n\t\trunAs.afterPropertiesSet();\n\t\tassertThat(runAs.getKey()).isEqualTo(\"hello_world\");\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tRunAsManager runAs = new RunAsManagerImpl();\n\t\tassertThat(runAs.supports(new SecurityConfig(\"RUN_AS_SOMETHING\"))).isTrue();\n\t\tassertThat(!runAs.supports(new SecurityConfig(\"ROLE_WHICH_IS_IGNORED\"))).isTrue();\n\t\tassertThat(!runAs.supports(new SecurityConfig(\"role_LOWER_CASE_FAILS\"))).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/RunAsUserTokenTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests {@link RunAsUserToken}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class RunAsUserTokenTests {\n\n\t@Test\n\tpublic void testAuthenticationSetting() {\n\t\tRunAsUserToken token = new RunAsUserToken(\"my_password\", \"Test\", \"Password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"), UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t\ttoken.setAuthenticated(false);\n\t\tassertThat(!token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testGetters() {\n\t\tRunAsUserToken token = new RunAsUserToken(\"my_password\", \"Test\", \"Password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"), UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(\"Test\").isEqualTo(token.getPrincipal());\n\t\tassertThat(\"Password\").isEqualTo(token.getCredentials());\n\t\tassertThat(\"my_password\".hashCode()).isEqualTo(token.getKeyHash());\n\t\tassertThat(UsernamePasswordAuthenticationToken.class).isEqualTo(token.getOriginalAuthentication());\n\t}\n\n\t@Test\n\tpublic void testNoArgConstructorDoesntExist() {\n\t\tassertThatExceptionOfType(NoSuchMethodException.class)\n\t\t\t.isThrownBy(() -> RunAsUserToken.class.getDeclaredConstructor((Class[]) null));\n\t}\n\n\t@Test\n\tpublic void testToString() {\n\t\tRunAsUserToken token = new RunAsUserToken(\"my_password\", \"Test\", \"Password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"), UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token.toString()\n\t\t\t.lastIndexOf(\"Original Class: \" + UsernamePasswordAuthenticationToken.class.getName().toString()) != -1)\n\t\t\t.isTrue();\n\t}\n\n\t// SEC-1792\n\t@Test\n\tpublic void testToStringNullOriginalAuthentication() {\n\t\tRunAsUserToken token = new RunAsUserToken(\"my_password\", \"Test\", \"Password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"), null);\n\t\tassertThat(token.toString().lastIndexOf(\"Original Class: null\") != -1).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/aopalliance/MethodSecurityInterceptorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aopalliance;\n\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.framework.ProxyFactory;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.ITargetObject;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.TargetObject;\nimport org.springframework.security.access.event.AuthorizationFailureEvent;\nimport org.springframework.security.access.event.AuthorizedEvent;\nimport org.springframework.security.access.intercept.AfterInvocationManager;\nimport org.springframework.security.access.intercept.RunAsManager;\nimport org.springframework.security.access.intercept.RunAsUserToken;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link MethodSecurityInterceptor}.\n *\n * @author Ben Alex\n * @author Rob Winch\n */\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class MethodSecurityInterceptorTests {\n\n\tprivate TestingAuthenticationToken token;\n\n\tprivate MethodSecurityInterceptor interceptor;\n\n\tprivate ITargetObject realTarget;\n\n\tprivate ITargetObject advisedTarget;\n\n\tprivate AccessDecisionManager adm;\n\n\tprivate MethodSecurityMetadataSource mds;\n\n\tprivate AuthenticationManager authman;\n\n\tprivate ApplicationEventPublisher eventPublisher;\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.token = new TestingAuthenticationToken(\"Test\", \"Password\");\n\t\tthis.interceptor = new MethodSecurityInterceptor();\n\t\tthis.adm = mock(AccessDecisionManager.class);\n\t\tthis.authman = mock(AuthenticationManager.class);\n\t\tthis.mds = mock(MethodSecurityMetadataSource.class);\n\t\tthis.eventPublisher = mock(ApplicationEventPublisher.class);\n\t\tthis.interceptor.setAccessDecisionManager(this.adm);\n\t\tthis.interceptor.setAuthenticationManager(this.authman);\n\t\tthis.interceptor.setSecurityMetadataSource(this.mds);\n\t\tthis.interceptor.setApplicationEventPublisher(this.eventPublisher);\n\t\tcreateTarget(false);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\tprivate void createTarget(boolean useMock) {\n\t\tthis.realTarget = useMock ? mock(ITargetObject.class) : new TargetObject();\n\t\tProxyFactory pf = new ProxyFactory(this.realTarget);\n\t\tpf.addAdvice(this.interceptor);\n\t\tthis.advisedTarget = (ITargetObject) pf.getProxy();\n\t}\n\n\t@Test\n\tpublic void gettersReturnExpectedData() {\n\t\tRunAsManager runAs = mock(RunAsManager.class);\n\t\tAfterInvocationManager aim = mock(AfterInvocationManager.class);\n\t\tthis.interceptor.setRunAsManager(runAs);\n\t\tthis.interceptor.setAfterInvocationManager(aim);\n\t\tassertThat(this.interceptor.getAccessDecisionManager()).isEqualTo(this.adm);\n\t\tassertThat(this.interceptor.getRunAsManager()).isEqualTo(runAs);\n\t\tassertThat(this.interceptor.getAuthenticationManager()).isEqualTo(this.authman);\n\t\tassertThat(this.interceptor.getSecurityMetadataSource()).isEqualTo(this.mds);\n\t\tassertThat(this.interceptor.getAfterInvocationManager()).isEqualTo(aim);\n\t}\n\n\t@Test\n\tpublic void missingAccessDecisionManagerIsDetected() throws Exception {\n\t\tthis.interceptor.setAccessDecisionManager(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void missingAuthenticationManagerIsDetected() throws Exception {\n\t\tthis.interceptor.setAuthenticationManager(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void missingMethodSecurityMetadataSourceIsRejected() throws Exception {\n\t\tthis.interceptor.setSecurityMetadataSource(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void missingRunAsManagerIsRejected() throws Exception {\n\t\tthis.interceptor.setRunAsManager(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void initializationRejectsSecurityMetadataSourceThatDoesNotSupportMethodInvocation() throws Throwable {\n\t\tgiven(this.mds.supports(MethodInvocation.class)).willReturn(false);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void initializationRejectsAccessDecisionManagerThatDoesNotSupportMethodInvocation() throws Exception {\n\t\tgiven(this.mds.supports(MethodInvocation.class)).willReturn(true);\n\t\tgiven(this.adm.supports(MethodInvocation.class)).willReturn(false);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void intitalizationRejectsRunAsManagerThatDoesNotSupportMethodInvocation() throws Exception {\n\t\tfinal RunAsManager ram = mock(RunAsManager.class);\n\t\tgiven(ram.supports(MethodInvocation.class)).willReturn(false);\n\t\tthis.interceptor.setRunAsManager(ram);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void intitalizationRejectsAfterInvocationManagerThatDoesNotSupportMethodInvocation() throws Exception {\n\t\tfinal AfterInvocationManager aim = mock(AfterInvocationManager.class);\n\t\tgiven(aim.supports(MethodInvocation.class)).willReturn(false);\n\t\tthis.interceptor.setAfterInvocationManager(aim);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void initializationFailsIfAccessDecisionManagerRejectsConfigAttributes() throws Exception {\n\t\tgiven(this.adm.supports(any(ConfigAttribute.class))).willReturn(false);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void validationNotAttemptedIfIsValidateConfigAttributesSetToFalse() throws Exception {\n\t\tgiven(this.adm.supports(MethodInvocation.class)).willReturn(true);\n\t\tgiven(this.mds.supports(MethodInvocation.class)).willReturn(true);\n\t\tthis.interceptor.setValidateConfigAttributes(false);\n\t\tthis.interceptor.afterPropertiesSet();\n\t\tverify(this.mds, never()).getAllConfigAttributes();\n\t\tverify(this.adm, never()).supports(any(ConfigAttribute.class));\n\t}\n\n\t@Test\n\tpublic void validationNotAttemptedIfMethodSecurityMetadataSourceReturnsNullForAttributes() throws Exception {\n\t\tgiven(this.adm.supports(MethodInvocation.class)).willReturn(true);\n\t\tgiven(this.mds.supports(MethodInvocation.class)).willReturn(true);\n\t\tgiven(this.mds.getAllConfigAttributes()).willReturn(null);\n\t\tthis.interceptor.setValidateConfigAttributes(true);\n\t\tthis.interceptor.afterPropertiesSet();\n\t\tverify(this.adm, never()).supports(any(ConfigAttribute.class));\n\t}\n\n\t@Test\n\tpublic void callingAPublicMethodFacadeWillNotRepeatSecurityChecksWhenPassedToTheSecuredMethodItFronts() {\n\t\tmdsReturnsNull();\n\t\tString result = this.advisedTarget.publicMakeLowerCase(\"HELLO\");\n\t\tassertThat(result).isEqualTo(\"hello Authentication empty\");\n\t}\n\n\t@Test\n\tpublic void callingAPublicMethodWhenPresentingAnAuthenticationObjectDoesntChangeItsAuthenticatedProperty() {\n\t\tmdsReturnsNull();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tassertThat(this.advisedTarget.publicMakeLowerCase(\"HELLO\"))\n\t\t\t.isEqualTo(\"hello org.springframework.security.authentication.TestingAuthenticationToken false\");\n\t\tassertThat(!this.token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void callIsntMadeWhenAuthenticationManagerRejectsAuthentication() {\n\t\tfinal TestingAuthenticationToken token = new TestingAuthenticationToken(\"Test\", \"Password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tmdsReturnsUserRole();\n\t\tgiven(this.authman.authenticate(token)).willThrow(new BadCredentialsException(\"rejected\"));\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.advisedTarget.makeLowerCase(\"HELLO\"));\n\t}\n\n\t@Test\n\tpublic void callSucceedsIfAccessDecisionManagerGrantsAccess() {\n\t\tthis.token.setAuthenticated(true);\n\t\tthis.interceptor.setPublishAuthorizationSuccess(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tmdsReturnsUserRole();\n\t\tString result = this.advisedTarget.makeLowerCase(\"HELLO\");\n\t\t// Note we check the isAuthenticated remained true in following line\n\t\tassertThat(result)\n\t\t\t.isEqualTo(\"hello org.springframework.security.authentication.TestingAuthenticationToken true\");\n\t\tverify(this.eventPublisher).publishEvent(any(AuthorizedEvent.class));\n\t}\n\n\t@Test\n\tpublic void callIsntMadeWhenAccessDecisionManagerRejectsAccess() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\t// Use mocked target to make sure invocation doesn't happen (not in expectations\n\t\t// so test would fail)\n\t\tcreateTarget(true);\n\t\tmdsReturnsUserRole();\n\t\tgiven(this.authman.authenticate(this.token)).willReturn(this.token);\n\t\twillThrow(new AccessDeniedException(\"rejected\")).given(this.adm)\n\t\t\t.decide(any(Authentication.class), any(MethodInvocation.class), any(List.class));\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.advisedTarget.makeUpperCase(\"HELLO\"));\n\t\tverify(this.eventPublisher).publishEvent(any(AuthorizationFailureEvent.class));\n\t}\n\n\t@Test\n\tpublic void rejectsNullSecuredObjects() throws Throwable {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.invoke(null));\n\t}\n\n\t@Test\n\tpublic void runAsReplacementIsCorrectlySet() {\n\t\tSecurityContext ctx = SecurityContextHolder.getContext();\n\t\tctx.setAuthentication(this.token);\n\t\tthis.token.setAuthenticated(true);\n\t\tfinal RunAsManager runAs = mock(RunAsManager.class);\n\t\tfinal RunAsUserToken runAsToken = new RunAsUserToken(\"key\", \"someone\", \"creds\", this.token.getAuthorities(),\n\t\t\t\tTestingAuthenticationToken.class);\n\t\tthis.interceptor.setRunAsManager(runAs);\n\t\tmdsReturnsUserRole();\n\t\tgiven(runAs.buildRunAs(eq(this.token), any(MethodInvocation.class), any(List.class))).willReturn(runAsToken);\n\t\tString result = this.advisedTarget.makeUpperCase(\"hello\");\n\t\tassertThat(result).isEqualTo(\"HELLO org.springframework.security.access.intercept.RunAsUserToken true\");\n\t\t// Check we've changed back\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(ctx);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.token);\n\t}\n\n\t// SEC-1967\n\t@Test\n\tpublic void runAsReplacementCleansAfterException() {\n\t\tcreateTarget(true);\n\t\tgiven(this.realTarget.makeUpperCase(anyString())).willThrow(new RuntimeException());\n\t\tSecurityContext ctx = SecurityContextHolder.getContext();\n\t\tctx.setAuthentication(this.token);\n\t\tthis.token.setAuthenticated(true);\n\t\tfinal RunAsManager runAs = mock(RunAsManager.class);\n\t\tfinal RunAsUserToken runAsToken = new RunAsUserToken(\"key\", \"someone\", \"creds\", this.token.getAuthorities(),\n\t\t\t\tTestingAuthenticationToken.class);\n\t\tthis.interceptor.setRunAsManager(runAs);\n\t\tmdsReturnsUserRole();\n\t\tgiven(runAs.buildRunAs(eq(this.token), any(MethodInvocation.class), any(List.class))).willReturn(runAsToken);\n\t\tassertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.advisedTarget.makeUpperCase(\"hello\"));\n\t\t// Check we've changed back\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(ctx);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.token);\n\t}\n\n\t@Test\n\tpublic void emptySecurityContextIsRejected() {\n\t\tmdsReturnsUserRole();\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.advisedTarget.makeUpperCase(\"hello\"));\n\t}\n\n\t@Test\n\tpublic void afterInvocationManagerIsNotInvokedIfExceptionIsRaised() throws Throwable {\n\t\tMethodInvocation mi = mock(MethodInvocation.class);\n\t\tthis.token.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tmdsReturnsUserRole();\n\t\tAfterInvocationManager aim = mock(AfterInvocationManager.class);\n\t\tthis.interceptor.setAfterInvocationManager(aim);\n\t\tgiven(mi.proceed()).willThrow(new Throwable());\n\t\tassertThatExceptionOfType(Throwable.class).isThrownBy(() -> this.interceptor.invoke(mi));\n\t\tverifyNoMoreInteractions(aim);\n\t}\n\n\tvoid mdsReturnsNull() {\n\t\tgiven(this.mds.getAttributes(any(MethodInvocation.class))).willReturn(null);\n\t}\n\n\tvoid mdsReturnsUserRole() {\n\t\tgiven(this.mds.getAttributes(any(MethodInvocation.class))).willReturn(SecurityConfig.createList(\"ROLE_USER\"));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/aopalliance/MethodSecurityMetadataSourceAdvisorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aopalliance;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.TargetObject;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link MethodSecurityMetadataSourceAdvisor}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class MethodSecurityMetadataSourceAdvisorTests {\n\n\t@Test\n\tpublic void testAdvisorReturnsFalseWhenMethodInvocationNotDefined() throws Exception {\n\t\tClass<TargetObject> clazz = TargetObject.class;\n\t\tMethod method = clazz.getMethod(\"makeLowerCase\", new Class[] { String.class });\n\t\tMethodSecurityMetadataSource mds = mock(MethodSecurityMetadataSource.class);\n\t\tgiven(mds.getAttributes(method, clazz)).willReturn(null);\n\t\tMethodSecurityMetadataSourceAdvisor advisor = new MethodSecurityMetadataSourceAdvisor(\"\", mds, \"\");\n\t\tassertThat(advisor.getPointcut().getMethodMatcher().matches(method, clazz)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testAdvisorReturnsTrueWhenMethodInvocationIsDefined() throws Exception {\n\t\tClass<TargetObject> clazz = TargetObject.class;\n\t\tMethod method = clazz.getMethod(\"countLength\", new Class[] { String.class });\n\t\tMethodSecurityMetadataSource mds = mock(MethodSecurityMetadataSource.class);\n\t\tgiven(mds.getAttributes(method, clazz)).willReturn(SecurityConfig.createList(\"ROLE_A\"));\n\t\tMethodSecurityMetadataSourceAdvisor advisor = new MethodSecurityMetadataSourceAdvisor(\"\", mds, \"\");\n\t\tassertThat(advisor.getPointcut().getMethodMatcher().matches(method, clazz)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/aspectj/AspectJMethodSecurityInterceptorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aspectj;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.Signature;\nimport org.aspectj.lang.reflect.CodeSignature;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.TargetObject;\nimport org.springframework.security.access.intercept.AfterInvocationManager;\nimport org.springframework.security.access.intercept.RunAsManager;\nimport org.springframework.security.access.intercept.RunAsUserToken;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link AspectJMethodSecurityInterceptor}.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @author Rob Winch\n */\n@SuppressWarnings(\"deprecation\")\npublic class AspectJMethodSecurityInterceptorTests {\n\n\tprivate TestingAuthenticationToken token;\n\n\tprivate AspectJMethodSecurityInterceptor interceptor;\n\n\t@Mock\n\tprivate AccessDecisionManager adm;\n\n\t@Mock\n\tprivate MethodSecurityMetadataSource mds;\n\n\t@Mock\n\tprivate AuthenticationManager authman;\n\n\t@Mock\n\tprivate AspectJCallback aspectJCallback;\n\n\tprivate ProceedingJoinPoint joinPoint;\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.token = new TestingAuthenticationToken(\"Test\", \"Password\");\n\t\tthis.interceptor = new AspectJMethodSecurityInterceptor();\n\t\tthis.interceptor.setAccessDecisionManager(this.adm);\n\t\tthis.interceptor.setAuthenticationManager(this.authman);\n\t\tthis.interceptor.setSecurityMetadataSource(this.mds);\n\t\t// Set up joinpoint information for the countLength method on TargetObject\n\t\tthis.joinPoint = mock(ProceedingJoinPoint.class); // new MockJoinPoint(new\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// TargetObject(), method);\n\t\tSignature sig = mock(Signature.class);\n\t\tgiven(sig.getDeclaringType()).willReturn(TargetObject.class);\n\t\tJoinPoint.StaticPart staticPart = mock(JoinPoint.StaticPart.class);\n\t\tgiven(this.joinPoint.getSignature()).willReturn(sig);\n\t\tgiven(this.joinPoint.getStaticPart()).willReturn(staticPart);\n\t\tCodeSignature codeSig = mock(CodeSignature.class);\n\t\tgiven(codeSig.getName()).willReturn(\"countLength\");\n\t\tgiven(codeSig.getDeclaringType()).willReturn(TargetObject.class);\n\t\tgiven(codeSig.getParameterTypes()).willReturn(new Class[] { String.class });\n\t\tgiven(staticPart.getSignature()).willReturn(codeSig);\n\t\tgiven(this.mds.getAttributes(any())).willReturn(SecurityConfig.createList(\"ROLE_USER\"));\n\t\tgiven(this.authman.authenticate(this.token)).willReturn(this.token);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void callbackIsInvokedWhenPermissionGranted() throws Throwable {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tthis.interceptor.invoke(this.joinPoint, this.aspectJCallback);\n\t\tverify(this.aspectJCallback).proceedWithObject();\n\t\t// Just try the other method too\n\t\tthis.interceptor.invoke(this.joinPoint);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void callbackIsNotInvokedWhenPermissionDenied() {\n\t\twillThrow(new AccessDeniedException(\"denied\")).given(this.adm).decide(any(), any(), any());\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.invoke(this.joinPoint, this.aspectJCallback));\n\t\tverify(this.aspectJCallback, never()).proceedWithObject();\n\t}\n\n\t@Test\n\tpublic void adapterHoldsCorrectData() {\n\t\tTargetObject to = new TargetObject();\n\t\tMethod m = ClassUtils.getMethodIfAvailable(TargetObject.class, \"countLength\", new Class[] { String.class });\n\t\tgiven(this.joinPoint.getTarget()).willReturn(to);\n\t\tgiven(this.joinPoint.getArgs()).willReturn(new Object[] { \"Hi\" });\n\t\tMethodInvocationAdapter mia = new MethodInvocationAdapter(this.joinPoint);\n\t\tassertThat(mia.getArguments()[0]).isEqualTo(\"Hi\");\n\t\tassertThat(mia.getStaticPart()).isEqualTo(m);\n\t\tassertThat(mia.getMethod()).isEqualTo(m);\n\t\tassertThat(mia.getThis()).isSameAs(to);\n\t}\n\n\t@Test\n\tpublic void afterInvocationManagerIsNotInvokedIfExceptionIsRaised() {\n\t\tthis.token.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tAfterInvocationManager aim = mock(AfterInvocationManager.class);\n\t\tthis.interceptor.setAfterInvocationManager(aim);\n\t\tgiven(this.aspectJCallback.proceedWithObject()).willThrow(new RuntimeException());\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.invoke(this.joinPoint, this.aspectJCallback));\n\t\tverifyNoMoreInteractions(aim);\n\t}\n\n\t// SEC-1967\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void invokeWithAspectJCallbackRunAsReplacementCleansAfterException() {\n\t\tSecurityContext ctx = SecurityContextHolder.getContext();\n\t\tctx.setAuthentication(this.token);\n\t\tthis.token.setAuthenticated(true);\n\t\tfinal RunAsManager runAs = mock(RunAsManager.class);\n\t\tfinal RunAsUserToken runAsToken = new RunAsUserToken(\"key\", \"someone\", \"creds\", this.token.getAuthorities(),\n\t\t\t\tTestingAuthenticationToken.class);\n\t\tthis.interceptor.setRunAsManager(runAs);\n\t\tgiven(runAs.buildRunAs(eq(this.token), any(MethodInvocation.class), any(List.class))).willReturn(runAsToken);\n\t\tgiven(this.aspectJCallback.proceedWithObject()).willThrow(new RuntimeException());\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.invoke(this.joinPoint, this.aspectJCallback));\n\t\t// Check we've changed back\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(ctx);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.token);\n\t}\n\n\t// SEC-1967\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void invokeRunAsReplacementCleansAfterException() throws Throwable {\n\t\tSecurityContext ctx = SecurityContextHolder.getContext();\n\t\tctx.setAuthentication(this.token);\n\t\tthis.token.setAuthenticated(true);\n\t\tfinal RunAsManager runAs = mock(RunAsManager.class);\n\t\tfinal RunAsUserToken runAsToken = new RunAsUserToken(\"key\", \"someone\", \"creds\", this.token.getAuthorities(),\n\t\t\t\tTestingAuthenticationToken.class);\n\t\tthis.interceptor.setRunAsManager(runAs);\n\t\tgiven(runAs.buildRunAs(eq(this.token), any(MethodInvocation.class), any(List.class))).willReturn(runAsToken);\n\t\tgiven(this.joinPoint.proceed()).willThrow(new RuntimeException());\n\t\tassertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.interceptor.invoke(this.joinPoint));\n\t\t// Check we've changed back\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(ctx);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.token);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/method/MapBasedMethodSecurityMetadataSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.method;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link MapBasedMethodSecurityMetadataSource}.\n *\n * @author Luke Taylor\n * @since 2.0.4\n */\n@SuppressWarnings(\"deprecation\")\npublic class MapBasedMethodSecurityMetadataSourceTests {\n\n\tprivate final List<ConfigAttribute> ROLE_A = SecurityConfig.createList(\"ROLE_A\");\n\n\tprivate final List<ConfigAttribute> ROLE_B = SecurityConfig.createList(\"ROLE_B\");\n\n\tprivate MapBasedMethodSecurityMetadataSource mds;\n\n\tprivate Method someMethodString;\n\n\tprivate Method someMethodInteger;\n\n\t@BeforeEach\n\tpublic void initialize() throws Exception {\n\t\tthis.mds = new MapBasedMethodSecurityMetadataSource();\n\t\tthis.someMethodString = MockService.class.getMethod(\"someMethod\", String.class);\n\t\tthis.someMethodInteger = MockService.class.getMethod(\"someMethod\", Integer.class);\n\t}\n\n\t@Test\n\tpublic void wildcardedMatchIsOverwrittenByMoreSpecificMatch() {\n\t\tthis.mds.addSecureMethod(MockService.class, \"some*\", this.ROLE_A);\n\t\tthis.mds.addSecureMethod(MockService.class, \"someMethod*\", this.ROLE_B);\n\t\tassertThat(this.mds.getAttributes(this.someMethodInteger, MockService.class)).isEqualTo(this.ROLE_B);\n\t}\n\n\t@Test\n\tpublic void methodsWithDifferentArgumentsAreMatchedCorrectly() {\n\t\tthis.mds.addSecureMethod(MockService.class, this.someMethodInteger, this.ROLE_A);\n\t\tthis.mds.addSecureMethod(MockService.class, this.someMethodString, this.ROLE_B);\n\t\tassertThat(this.mds.getAttributes(this.someMethodInteger, MockService.class)).isEqualTo(this.ROLE_A);\n\t\tassertThat(this.mds.getAttributes(this.someMethodString, MockService.class)).isEqualTo(this.ROLE_B);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate class MockService {\n\n\t\tpublic void someMethod(String s) {\n\t\t}\n\n\t\tpublic void someMethod(Integer i) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/method/MethodInvocationPrivilegeEvaluatorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.method;\n\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.ITargetObject;\nimport org.springframework.security.access.OtherTargetObject;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.TargetObject;\nimport org.springframework.security.access.intercept.MethodInvocationPrivilegeEvaluator;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.util.MethodInvocationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests\n * {@link org.springframework.security.access.intercept.MethodInvocationPrivilegeEvaluator}\n * .\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class MethodInvocationPrivilegeEvaluatorTests {\n\n\tprivate TestingAuthenticationToken token;\n\n\tprivate MethodSecurityInterceptor interceptor;\n\n\tprivate AccessDecisionManager adm;\n\n\tprivate MethodSecurityMetadataSource mds;\n\n\tprivate final List<ConfigAttribute> role = SecurityConfig.createList(\"ROLE_IGNORED\");\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.interceptor = new MethodSecurityInterceptor();\n\t\tthis.token = new TestingAuthenticationToken(\"Test\", \"Password\", \"ROLE_SOMETHING\");\n\t\tthis.adm = mock(AccessDecisionManager.class);\n\t\tAuthenticationManager authman = mock(AuthenticationManager.class);\n\t\tthis.mds = mock(MethodSecurityMetadataSource.class);\n\t\tthis.interceptor.setAccessDecisionManager(this.adm);\n\t\tthis.interceptor.setAuthenticationManager(authman);\n\t\tthis.interceptor.setSecurityMetadataSource(this.mds);\n\t}\n\n\t@Test\n\tpublic void allowsAccessUsingCreate() throws Exception {\n\t\tObject object = new TargetObject();\n\t\tfinal MethodInvocation mi = MethodInvocationUtils.create(object, \"makeLowerCase\", \"foobar\");\n\t\tMethodInvocationPrivilegeEvaluator mipe = new MethodInvocationPrivilegeEvaluator();\n\t\tgiven(this.mds.getAttributes(mi)).willReturn(this.role);\n\t\tmipe.setSecurityInterceptor(this.interceptor);\n\t\tmipe.afterPropertiesSet();\n\t\tassertThat(mipe.isAllowed(mi, this.token)).isTrue();\n\t}\n\n\t@Test\n\tpublic void allowsAccessUsingCreateFromClass() {\n\t\tfinal MethodInvocation mi = MethodInvocationUtils.createFromClass(new OtherTargetObject(), ITargetObject.class,\n\t\t\t\t\"makeLowerCase\", new Class[] { String.class }, new Object[] { \"Hello world\" });\n\t\tMethodInvocationPrivilegeEvaluator mipe = new MethodInvocationPrivilegeEvaluator();\n\t\tmipe.setSecurityInterceptor(this.interceptor);\n\t\tgiven(this.mds.getAttributes(mi)).willReturn(this.role);\n\t\tassertThat(mipe.isAllowed(mi, this.token)).isTrue();\n\t}\n\n\t@Test\n\tpublic void declinesAccessUsingCreate() {\n\t\tObject object = new TargetObject();\n\t\tfinal MethodInvocation mi = MethodInvocationUtils.create(object, \"makeLowerCase\", \"foobar\");\n\t\tMethodInvocationPrivilegeEvaluator mipe = new MethodInvocationPrivilegeEvaluator();\n\t\tmipe.setSecurityInterceptor(this.interceptor);\n\t\tgiven(this.mds.getAttributes(mi)).willReturn(this.role);\n\t\twillThrow(new AccessDeniedException(\"rejected\")).given(this.adm).decide(this.token, mi, this.role);\n\t\tassertThat(mipe.isAllowed(mi, this.token)).isFalse();\n\t}\n\n\t@Test\n\tpublic void declinesAccessUsingCreateFromClass() {\n\t\tfinal MethodInvocation mi = MethodInvocationUtils.createFromClass(new OtherTargetObject(), ITargetObject.class,\n\t\t\t\t\"makeLowerCase\", new Class[] { String.class }, new Object[] { \"helloWorld\" });\n\t\tMethodInvocationPrivilegeEvaluator mipe = new MethodInvocationPrivilegeEvaluator();\n\t\tmipe.setSecurityInterceptor(this.interceptor);\n\t\tgiven(this.mds.getAttributes(mi)).willReturn(this.role);\n\t\twillThrow(new AccessDeniedException(\"rejected\")).given(this.adm).decide(this.token, mi, this.role);\n\t\tassertThat(mipe.isAllowed(mi, this.token)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/intercept/method/MockMethodInvocation.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.method;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.intercept.MethodInvocation;\n\n@SuppressWarnings(\"unchecked\")\npublic class MockMethodInvocation implements MethodInvocation {\n\n\tprivate Method method;\n\n\tprivate Object targetObject;\n\n\tprivate Object[] arguments = new Object[0];\n\n\tpublic MockMethodInvocation(Object targetObject, Class clazz, String methodName, Class[] parameterTypes,\n\t\t\tObject[] arguments) throws NoSuchMethodException {\n\t\tthis(targetObject, clazz, methodName, parameterTypes);\n\t\tthis.arguments = arguments;\n\t}\n\n\tpublic MockMethodInvocation(Object targetObject, Class clazz, String methodName, Class... parameterTypes)\n\t\t\tthrows NoSuchMethodException {\n\t\tthis(targetObject, clazz.getMethod(methodName, parameterTypes));\n\t\tthis.targetObject = targetObject;\n\t}\n\n\tpublic MockMethodInvocation(Object targetObject, Method method) {\n\t\tthis.targetObject = targetObject;\n\t\tthis.method = method;\n\t}\n\n\t@Override\n\tpublic Object[] getArguments() {\n\t\treturn this.arguments;\n\t}\n\n\t@Override\n\tpublic Method getMethod() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic AccessibleObject getStaticPart() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Object getThis() {\n\t\treturn this.targetObject;\n\t}\n\n\t@Override\n\tpublic Object proceed() {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/method/DelegatingMethodSecurityMetadataSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.method;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentMatchers;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class DelegatingMethodSecurityMetadataSourceTests {\n\n\tDelegatingMethodSecurityMetadataSource mds;\n\n\t@Test\n\tpublic void returnsEmptyListIfDelegateReturnsNull() throws Exception {\n\t\tList sources = new ArrayList();\n\t\tMethodSecurityMetadataSource delegate = mock(MethodSecurityMetadataSource.class);\n\t\tgiven(delegate.getAttributes(ArgumentMatchers.<Method>any(), ArgumentMatchers.any(Class.class)))\n\t\t\t.willReturn(null);\n\t\tsources.add(delegate);\n\t\tthis.mds = new DelegatingMethodSecurityMetadataSource(sources);\n\t\tassertThat(this.mds.getMethodSecurityMetadataSources()).isSameAs(sources);\n\t\tassertThat(this.mds.getAllConfigAttributes()).isEmpty();\n\t\tMethodInvocation mi = new SimpleMethodInvocation(null, String.class.getMethod(\"toString\"));\n\t\tassertThat(this.mds.getAttributes(mi)).isEqualTo(Collections.emptyList());\n\t\t// Exercise the cached case\n\t\tassertThat(this.mds.getAttributes(mi)).isEqualTo(Collections.emptyList());\n\t}\n\n\t@Test\n\tpublic void returnsDelegateAttributes() throws Exception {\n\t\tList sources = new ArrayList();\n\t\tMethodSecurityMetadataSource delegate = mock(MethodSecurityMetadataSource.class);\n\t\tConfigAttribute ca = mock(ConfigAttribute.class);\n\t\tList attributes = Arrays.asList(ca);\n\t\tMethod toString = String.class.getMethod(\"toString\");\n\t\tgiven(delegate.getAttributes(toString, String.class)).willReturn(attributes);\n\t\tsources.add(delegate);\n\t\tthis.mds = new DelegatingMethodSecurityMetadataSource(sources);\n\t\tassertThat(this.mds.getMethodSecurityMetadataSources()).isSameAs(sources);\n\t\tassertThat(this.mds.getAllConfigAttributes()).isEmpty();\n\t\tMethodInvocation mi = new SimpleMethodInvocation(\"\", toString);\n\t\tassertThat(this.mds.getAttributes(mi)).isSameAs(attributes);\n\t\t// Exercise the cached case\n\t\tassertThat(this.mds.getAttributes(mi)).isSameAs(attributes);\n\t\tassertThat(this.mds.getAttributes(new SimpleMethodInvocation(null, String.class.getMethod(\"length\"))))\n\t\t\t.isEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/prepost/PostInvocationAdviceProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.aop.ProxyMethodInvocation;\nimport org.springframework.security.access.intercept.aspectj.MethodInvocationAdapter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(MockitoExtension.class)\n@SuppressWarnings(\"deprecation\")\npublic class PostInvocationAdviceProviderTests {\n\n\t@Mock\n\tprivate PostInvocationAuthorizationAdvice authorizationAdvice;\n\n\tprivate PostInvocationAdviceProvider postInvocationAdviceProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.postInvocationAdviceProvider = new PostInvocationAdviceProvider(this.authorizationAdvice);\n\t}\n\n\t@Test\n\tpublic void supportsMethodInvocation() {\n\t\tassertThat(this.postInvocationAdviceProvider.supports(MethodInvocation.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsProxyMethodInvocation() {\n\t\tassertThat(this.postInvocationAdviceProvider.supports(ProxyMethodInvocation.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsMethodInvocationAdapter() {\n\t\tassertThat(this.postInvocationAdviceProvider.supports(MethodInvocationAdapter.class)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/prepost/PreInvocationAuthorizationAdviceVoterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.aop.ProxyMethodInvocation;\nimport org.springframework.security.access.intercept.aspectj.MethodInvocationAdapter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(MockitoExtension.class)\n@SuppressWarnings(\"deprecation\")\npublic class PreInvocationAuthorizationAdviceVoterTests {\n\n\t@Mock\n\tprivate PreInvocationAuthorizationAdvice authorizationAdvice;\n\n\tprivate PreInvocationAuthorizationAdviceVoter voter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.voter = new PreInvocationAuthorizationAdviceVoter(this.authorizationAdvice);\n\t}\n\n\t@Test\n\tpublic void supportsMethodInvocation() {\n\t\tassertThat(this.voter.supports(MethodInvocation.class)).isTrue();\n\t}\n\n\t// SEC-2031\n\t@Test\n\tpublic void supportsProxyMethodInvocation() {\n\t\tassertThat(this.voter.supports(ProxyMethodInvocation.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsMethodInvocationAdapter() {\n\t\tassertThat(this.voter.supports(MethodInvocationAdapter.class)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/AbstractAccessDecisionManagerTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Vector;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AbstractAccessDecisionManager}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class AbstractAccessDecisionManagerTests {\n\n\t@Test\n\tpublic void testAllowIfAccessDecisionManagerDefaults() {\n\t\tList list = new Vector();\n\t\tDenyAgainVoter denyVoter = new DenyAgainVoter();\n\t\tlist.add(denyVoter);\n\t\tMockDecisionManagerImpl mock = new MockDecisionManagerImpl(list);\n\t\tassertThat(!mock.isAllowIfAllAbstainDecisions()).isTrue(); // default\n\t\tmock.setAllowIfAllAbstainDecisions(true);\n\t\tassertThat(mock.isAllowIfAllAbstainDecisions()).isTrue(); // changed\n\t}\n\n\t@Test\n\tpublic void testDelegatesSupportsClassRequests() {\n\t\tList list = new Vector();\n\t\tlist.add(new DenyVoter());\n\t\tlist.add(new MockStringOnlyVoter());\n\t\tMockDecisionManagerImpl mock = new MockDecisionManagerImpl(list);\n\t\tassertThat(mock.supports(String.class)).isTrue();\n\t\tassertThat(!mock.supports(Integer.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testDelegatesSupportsRequests() {\n\t\tList list = new Vector();\n\t\tDenyVoter voter = new DenyVoter();\n\t\tDenyAgainVoter denyVoter = new DenyAgainVoter();\n\t\tlist.add(voter);\n\t\tlist.add(denyVoter);\n\t\tMockDecisionManagerImpl mock = new MockDecisionManagerImpl(list);\n\t\tConfigAttribute attr = new SecurityConfig(\"DENY_AGAIN_FOR_SURE\");\n\t\tassertThat(mock.supports(attr)).isTrue();\n\t\tConfigAttribute badAttr = new SecurityConfig(\"WE_DONT_SUPPORT_THIS\");\n\t\tassertThat(!mock.supports(badAttr)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testProperlyStoresListOfVoters() {\n\t\tList list = new Vector();\n\t\tDenyVoter voter = new DenyVoter();\n\t\tDenyAgainVoter denyVoter = new DenyAgainVoter();\n\t\tlist.add(voter);\n\t\tlist.add(denyVoter);\n\t\tMockDecisionManagerImpl mock = new MockDecisionManagerImpl(list);\n\t\tassertThat(mock.getDecisionVoters()).hasSize(list.size());\n\t}\n\n\t@Test\n\tpublic void testRejectsEmptyList() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MockDecisionManagerImpl(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void testRejectsNullVotersList() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MockDecisionManagerImpl(null));\n\t}\n\n\t@Test\n\tpublic void testRoleVoterAlwaysReturnsTrueToSupports() {\n\t\tRoleVoter rv = new RoleVoter();\n\t\tassertThat(rv.supports(String.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testWillNotStartIfDecisionVotersNotSet() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MockDecisionManagerImpl(null));\n\t}\n\n\tprivate class MockDecisionManagerImpl extends AbstractAccessDecisionManager {\n\n\t\tprotected MockDecisionManagerImpl(List<AccessDecisionVoter<? extends Object>> decisionVoters) {\n\t\t\tsuper(decisionVoters);\n\t\t}\n\n\t\t@Override\n\t\tpublic void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) {\n\t\t}\n\n\t}\n\n\tprivate class MockStringOnlyVoter implements AccessDecisionVoter<Object> {\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> clazz) {\n\t\t\treturn String.class.isAssignableFrom(clazz);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(ConfigAttribute attribute) {\n\t\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {\n\t\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/AbstractAclVoterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.util.MethodInvocationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings(\"deprecation\")\npublic class AbstractAclVoterTests {\n\n\tprivate AbstractAclVoter voter = new AbstractAclVoter() {\n\t\t@Override\n\t\tpublic boolean supports(ConfigAttribute attribute) {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic int vote(Authentication authentication, MethodInvocation object,\n\t\t\t\tCollection<ConfigAttribute> attributes) {\n\t\t\treturn 0;\n\t\t}\n\t};\n\n\t@Test\n\tpublic void supportsMethodInvocations() {\n\t\tassertThat(this.voter.supports(MethodInvocation.class)).isTrue();\n\t\tassertThat(this.voter.supports(String.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic void expectedDomainObjectArgumentIsReturnedFromMethodInvocation() {\n\t\tthis.voter.setProcessDomainObjectClass(String.class);\n\t\tMethodInvocation mi = MethodInvocationUtils.create(new TestClass(), \"methodTakingAString\", \"The Argument\");\n\t\tassertThat(this.voter.getDomainObjectInstance(mi)).isEqualTo(\"The Argument\");\n\t}\n\n\t@Test\n\tpublic void correctArgumentIsSelectedFromMultipleArgs() {\n\t\tthis.voter.setProcessDomainObjectClass(String.class);\n\t\tMethodInvocation mi = MethodInvocationUtils.create(new TestClass(), \"methodTakingAListAndAString\",\n\t\t\t\tnew ArrayList<>(), \"The Argument\");\n\t\tassertThat(this.voter.getDomainObjectInstance(mi)).isEqualTo(\"The Argument\");\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static class TestClass {\n\n\t\tpublic void methodTakingAString(String arg) {\n\t\t}\n\n\t\tpublic void methodTaking2Strings(String arg1, String arg2) {\n\t\t}\n\n\t\tpublic void methodTakingAListAndAString(ArrayList<Object> arg1, String arg2) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/AffirmativeBasedTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link AffirmativeBased}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class AffirmativeBasedTests {\n\n\tprivate final List<ConfigAttribute> attrs = new ArrayList<>();\n\n\tprivate final Authentication user = new TestingAuthenticationToken(\"somebody\", \"password\", \"ROLE_1\", \"ROLE_2\");\n\n\tprivate AffirmativeBased mgr;\n\n\tprivate AccessDecisionVoter grant;\n\n\tprivate AccessDecisionVoter abstain;\n\n\tprivate AccessDecisionVoter deny;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setup() {\n\t\tthis.grant = mock(AccessDecisionVoter.class);\n\t\tthis.abstain = mock(AccessDecisionVoter.class);\n\t\tthis.deny = mock(AccessDecisionVoter.class);\n\t\tgiven(this.grant.vote(any(Authentication.class), any(Object.class), any(List.class)))\n\t\t\t.willReturn(AccessDecisionVoter.ACCESS_GRANTED);\n\t\tgiven(this.abstain.vote(any(Authentication.class), any(Object.class), any(List.class)))\n\t\t\t.willReturn(AccessDecisionVoter.ACCESS_ABSTAIN);\n\t\tgiven(this.deny.vote(any(Authentication.class), any(Object.class), any(List.class)))\n\t\t\t.willReturn(AccessDecisionVoter.ACCESS_DENIED);\n\t}\n\n\t@Test\n\tpublic void oneAffirmativeVoteOneDenyVoteOneAbstainVoteGrantsAccess() throws Exception {\n\t\tthis.mgr = new AffirmativeBased(\n\t\t\t\tArrays.<AccessDecisionVoter<? extends Object>>asList(this.grant, this.deny, this.abstain));\n\t\tthis.mgr.afterPropertiesSet();\n\t\tthis.mgr.decide(this.user, new Object(), this.attrs);\n\t}\n\n\t@Test\n\tpublic void oneDenyVoteOneAbstainVoteOneAffirmativeVoteGrantsAccess() {\n\t\tthis.mgr = new AffirmativeBased(\n\t\t\t\tArrays.<AccessDecisionVoter<? extends Object>>asList(this.deny, this.abstain, this.grant));\n\t\tthis.mgr.decide(this.user, new Object(), this.attrs);\n\t}\n\n\t@Test\n\tpublic void oneAffirmativeVoteTwoAbstainVotesGrantsAccess() {\n\t\tthis.mgr = new AffirmativeBased(\n\t\t\t\tArrays.<AccessDecisionVoter<? extends Object>>asList(this.grant, this.abstain, this.abstain));\n\t\tthis.mgr.decide(this.user, new Object(), this.attrs);\n\t}\n\n\t@Test\n\tpublic void oneDenyVoteTwoAbstainVotesDeniesAccess() {\n\t\tthis.mgr = new AffirmativeBased(\n\t\t\t\tArrays.<AccessDecisionVoter<? extends Object>>asList(this.deny, this.abstain, this.abstain));\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.mgr.decide(this.user, new Object(), this.attrs));\n\t}\n\n\t@Test\n\tpublic void onlyAbstainVotesDeniesAccessWithDefault() {\n\t\tthis.mgr = new AffirmativeBased(\n\t\t\t\tArrays.<AccessDecisionVoter<? extends Object>>asList(this.abstain, this.abstain, this.abstain));\n\t\tassertThat(!this.mgr.isAllowIfAllAbstainDecisions()).isTrue(); // check default\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.mgr.decide(this.user, new Object(), this.attrs));\n\t}\n\n\t@Test\n\tpublic void testThreeAbstainVotesGrantsAccessIfAllowIfAllAbstainDecisionsIsSet() {\n\t\tthis.mgr = new AffirmativeBased(\n\t\t\t\tArrays.<AccessDecisionVoter<? extends Object>>asList(this.abstain, this.abstain, this.abstain));\n\t\tthis.mgr.setAllowIfAllAbstainDecisions(true);\n\t\tassertThat(this.mgr.isAllowIfAllAbstainDecisions()).isTrue(); // check changed\n\t\tthis.mgr.decide(this.user, new Object(), this.attrs);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/AuthenticatedVoterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AuthenticatedVoter}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class AuthenticatedVoterTests {\n\n\tprivate Authentication createAnonymous() {\n\t\treturn new AnonymousAuthenticationToken(\"ignored\", \"ignored\", AuthorityUtils.createAuthorityList(\"ignored\"));\n\t}\n\n\tprivate Authentication createFullyAuthenticated() {\n\t\treturn UsernamePasswordAuthenticationToken.authenticated(\"ignored\", \"ignored\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ignored\"));\n\t}\n\n\tprivate Authentication createRememberMe() {\n\t\treturn new RememberMeAuthenticationToken(\"ignored\", \"ignored\", AuthorityUtils.createAuthorityList(\"ignored\"));\n\t}\n\n\t@Test\n\tpublic void testAnonymousWorks() {\n\t\tAuthenticatedVoter voter = new AuthenticatedVoter();\n\t\tList<ConfigAttribute> def = SecurityConfig.createList(AuthenticatedVoter.IS_AUTHENTICATED_ANONYMOUSLY);\n\t\tassertThat(AccessDecisionVoter.ACCESS_GRANTED).isEqualTo(voter.vote(createAnonymous(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_GRANTED).isEqualTo(voter.vote(createRememberMe(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_GRANTED).isEqualTo(voter.vote(createFullyAuthenticated(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_DENIED).isEqualTo(voter.vote(null, null, def));\n\t}\n\n\t@Test\n\tpublic void testFullyWorks() {\n\t\tAuthenticatedVoter voter = new AuthenticatedVoter();\n\t\tList<ConfigAttribute> def = SecurityConfig.createList(AuthenticatedVoter.IS_AUTHENTICATED_FULLY);\n\t\tassertThat(AccessDecisionVoter.ACCESS_DENIED).isEqualTo(voter.vote(createAnonymous(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_DENIED).isEqualTo(voter.vote(createRememberMe(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_GRANTED).isEqualTo(voter.vote(createFullyAuthenticated(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_DENIED).isEqualTo(voter.vote(null, null, def));\n\t}\n\n\t@Test\n\tpublic void testRememberMeWorks() {\n\t\tAuthenticatedVoter voter = new AuthenticatedVoter();\n\t\tList<ConfigAttribute> def = SecurityConfig.createList(AuthenticatedVoter.IS_AUTHENTICATED_REMEMBERED);\n\t\tassertThat(AccessDecisionVoter.ACCESS_DENIED).isEqualTo(voter.vote(createAnonymous(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_GRANTED).isEqualTo(voter.vote(createRememberMe(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_GRANTED).isEqualTo(voter.vote(createFullyAuthenticated(), null, def));\n\t\tassertThat(AccessDecisionVoter.ACCESS_DENIED).isEqualTo(voter.vote(null, null, def));\n\t}\n\n\t@Test\n\tpublic void testSetterRejectsNull() {\n\t\tAuthenticatedVoter voter = new AuthenticatedVoter();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> voter.setAuthenticationTrustResolver(null));\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tAuthenticatedVoter voter = new AuthenticatedVoter();\n\t\tassertThat(voter.supports(String.class)).isTrue();\n\t\tassertThat(voter.supports(new SecurityConfig(AuthenticatedVoter.IS_AUTHENTICATED_ANONYMOUSLY))).isTrue();\n\t\tassertThat(voter.supports(new SecurityConfig(AuthenticatedVoter.IS_AUTHENTICATED_FULLY))).isTrue();\n\t\tassertThat(voter.supports(new SecurityConfig(AuthenticatedVoter.IS_AUTHENTICATED_REMEMBERED))).isTrue();\n\t\tassertThat(voter.supports(new SecurityConfig(\"FOO\"))).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/ConsensusBasedTests.java",
    "content": "/*\n * Copyright 2004 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.List;\nimport java.util.Vector;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests {@link ConsensusBased}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class ConsensusBasedTests {\n\n\t@Test\n\tpublic void testOneAffirmativeVoteOneDenyVoteOneAbstainVoteDeniesAccessWithoutDefault() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tConsensusBased mgr = makeDecisionManager();\n\t\tmgr.setAllowIfEqualGrantedDeniedDecisions(false);\n\t\tassertThat(!mgr.isAllowIfEqualGrantedDeniedDecisions()).isTrue(); // check changed\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(\"ROLE_1\", \"DENY_FOR_SURE\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> mgr.decide(auth, new Object(), config));\n\t}\n\n\t@Test\n\tpublic void testOneAffirmativeVoteOneDenyVoteOneAbstainVoteGrantsAccessWithDefault() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tConsensusBased mgr = makeDecisionManager();\n\t\tassertThat(mgr.isAllowIfEqualGrantedDeniedDecisions()).isTrue(); // check default\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(\"ROLE_1\", \"DENY_FOR_SURE\");\n\t\tmgr.decide(auth, new Object(), config);\n\t}\n\n\t@Test\n\tpublic void testOneAffirmativeVoteTwoAbstainVotesGrantsAccess() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tConsensusBased mgr = makeDecisionManager();\n\t\tmgr.decide(auth, new Object(), SecurityConfig.createList(\"ROLE_2\"));\n\t}\n\n\t@Test\n\tpublic void testOneDenyVoteTwoAbstainVotesDeniesAccess() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tConsensusBased mgr = makeDecisionManager();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> mgr.decide(auth, new Object(), SecurityConfig.createList(\"ROLE_WE_DO_NOT_HAVE\")));\n\t}\n\n\t@Test\n\tpublic void testThreeAbstainVotesDeniesAccessWithDefault() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tConsensusBased mgr = makeDecisionManager();\n\t\tassertThat(!mgr.isAllowIfAllAbstainDecisions()).isTrue(); // check default\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> mgr.decide(auth, new Object(), SecurityConfig.createList(\"IGNORED_BY_ALL\")));\n\t}\n\n\t@Test\n\tpublic void testThreeAbstainVotesGrantsAccessWithoutDefault() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tConsensusBased mgr = makeDecisionManager();\n\t\tmgr.setAllowIfAllAbstainDecisions(true);\n\t\tassertThat(mgr.isAllowIfAllAbstainDecisions()).isTrue(); // check changed\n\t\tmgr.decide(auth, new Object(), SecurityConfig.createList(\"IGNORED_BY_ALL\"));\n\t}\n\n\t@Test\n\tpublic void testTwoAffirmativeVotesTwoAbstainVotesGrantsAccess() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tConsensusBased mgr = makeDecisionManager();\n\t\tmgr.decide(auth, new Object(), SecurityConfig.createList(\"ROLE_1\", \"ROLE_2\"));\n\t}\n\n\tprivate ConsensusBased makeDecisionManager() {\n\t\tRoleVoter roleVoter = new RoleVoter();\n\t\tDenyVoter denyForSureVoter = new DenyVoter();\n\t\tDenyAgainVoter denyAgainForSureVoter = new DenyAgainVoter();\n\t\tList<AccessDecisionVoter<? extends Object>> voters = new Vector<>();\n\t\tvoters.add(roleVoter);\n\t\tvoters.add(denyForSureVoter);\n\t\tvoters.add(denyAgainForSureVoter);\n\t\treturn new ConsensusBased(voters);\n\t}\n\n\tprivate TestingAuthenticationToken makeTestToken() {\n\t\treturn new TestingAuthenticationToken(\"somebody\", \"password\", \"ROLE_1\", \"ROLE_2\");\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/DenyAgainVoter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Implementation of an {@link AccessDecisionVoter} for unit testing.\n * <p>\n * If the {@link ConfigAttribute#getAttribute()} has a value of\n * <code>DENY_AGAIN_FOR_SURE</code>, the voter will vote to deny access.\n * <p>\n * All comparisons are case sensitive.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class DenyAgainVoter implements AccessDecisionVoter<Object> {\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn \"DENY_AGAIN_FOR_SURE\".equals(attribute.getAttribute());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {\n\t\tfor (ConfigAttribute attribute : attributes) {\n\t\t\tif (this.supports(attribute)) {\n\t\t\t\treturn ACCESS_DENIED;\n\t\t\t}\n\t\t}\n\t\treturn ACCESS_ABSTAIN;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/DenyVoter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Implementation of an {@link AccessDecisionVoter} for unit testing.\n * <p>\n * If the {@link ConfigAttribute#getAttribute()} has a value of <code>DENY_FOR_SURE</code>\n * , the voter will vote to deny access.\n * </p>\n * <p>\n * All comparisons are case sensitive.\n * </p>\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class DenyVoter implements AccessDecisionVoter<Object> {\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn \"DENY_FOR_SURE\".equals(attribute.getAttribute());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {\n\t\tfor (ConfigAttribute attribute : attributes) {\n\t\t\tif (this.supports(attribute)) {\n\t\t\t\treturn ACCESS_DENIED;\n\t\t\t}\n\t\t}\n\t\treturn ACCESS_ABSTAIN;\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/RoleHierarchyVoterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"deprecation\")\npublic class RoleHierarchyVoterTests {\n\n\t@Test\n\tpublic void hierarchicalRoleIsIncludedInDecision() {\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\");\n\t\t// User has role A, role B is required\n\t\tTestingAuthenticationToken auth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_A\");\n\t\tRoleHierarchyVoter voter = new RoleHierarchyVoter(roleHierarchyImpl);\n\t\tassertThat(voter.vote(auth, new Object(), SecurityConfig.createList(\"ROLE_B\")))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/RoleVoterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings(\"deprecation\")\npublic class RoleVoterTests {\n\n\t@Test\n\tpublic void oneMatchingAttributeGrantsAccess() {\n\t\tRoleVoter voter = new RoleVoter();\n\t\tvoter.setRolePrefix(\"\");\n\t\tAuthentication userAB = new TestingAuthenticationToken(\"user\", \"pass\", \"A\", \"B\");\n\t\t// Vote on attribute list that has two attributes A and C (i.e. only one matching)\n\t\tassertThat(voter.vote(userAB, this, SecurityConfig.createList(\"A\", \"C\")))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t}\n\n\t// SEC-3128\n\t@Test\n\tpublic void nullAuthenticationDenies() {\n\t\tRoleVoter voter = new RoleVoter();\n\t\tvoter.setRolePrefix(\"\");\n\t\tAuthentication notAuthenitcated = null;\n\t\tassertThat(voter.vote(notAuthenitcated, this, SecurityConfig.createList(\"A\")))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_DENIED);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/access/vote/UnanimousBasedTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.vote;\n\nimport java.util.List;\nimport java.util.Vector;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests {@link UnanimousBased}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class UnanimousBasedTests {\n\n\tprivate UnanimousBased makeDecisionManager() {\n\t\tRoleVoter roleVoter = new RoleVoter();\n\t\tDenyVoter denyForSureVoter = new DenyVoter();\n\t\tDenyAgainVoter denyAgainForSureVoter = new DenyAgainVoter();\n\t\tList<AccessDecisionVoter<? extends Object>> voters = new Vector<>();\n\t\tvoters.add(roleVoter);\n\t\tvoters.add(denyForSureVoter);\n\t\tvoters.add(denyAgainForSureVoter);\n\t\treturn new UnanimousBased(voters);\n\t}\n\n\tprivate UnanimousBased makeDecisionManagerWithFooBarPrefix() {\n\t\tRoleVoter roleVoter = new RoleVoter();\n\t\troleVoter.setRolePrefix(\"FOOBAR_\");\n\t\tDenyVoter denyForSureVoter = new DenyVoter();\n\t\tDenyAgainVoter denyAgainForSureVoter = new DenyAgainVoter();\n\t\tList<AccessDecisionVoter<? extends Object>> voters = new Vector<>();\n\t\tvoters.add(roleVoter);\n\t\tvoters.add(denyForSureVoter);\n\t\tvoters.add(denyAgainForSureVoter);\n\t\treturn new UnanimousBased(voters);\n\t}\n\n\tprivate TestingAuthenticationToken makeTestToken() {\n\t\treturn new TestingAuthenticationToken(\"somebody\", \"password\", \"ROLE_1\", \"ROLE_2\");\n\t}\n\n\tprivate TestingAuthenticationToken makeTestTokenWithFooBarPrefix() {\n\t\treturn new TestingAuthenticationToken(\"somebody\", \"password\", \"FOOBAR_1\", \"FOOBAR_2\");\n\t}\n\n\t@Test\n\tpublic void testOneAffirmativeVoteOneDenyVoteOneAbstainVoteDeniesAccess() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tUnanimousBased mgr = makeDecisionManager();\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(new String[] { \"ROLE_1\", \"DENY_FOR_SURE\" });\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> mgr.decide(auth, new Object(), config));\n\t}\n\n\t@Test\n\tpublic void testOneAffirmativeVoteTwoAbstainVotesGrantsAccess() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tUnanimousBased mgr = makeDecisionManager();\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(\"ROLE_2\");\n\t\tmgr.decide(auth, new Object(), config);\n\t}\n\n\t@Test\n\tpublic void testOneDenyVoteTwoAbstainVotesDeniesAccess() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tUnanimousBased mgr = makeDecisionManager();\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(\"ROLE_WE_DO_NOT_HAVE\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> mgr.decide(auth, new Object(), config));\n\t}\n\n\t@Test\n\tpublic void testRoleVoterPrefixObserved() {\n\t\tTestingAuthenticationToken auth = makeTestTokenWithFooBarPrefix();\n\t\tUnanimousBased mgr = makeDecisionManagerWithFooBarPrefix();\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(new String[] { \"FOOBAR_1\", \"FOOBAR_2\" });\n\t\tmgr.decide(auth, new Object(), config);\n\t}\n\n\t@Test\n\tpublic void testThreeAbstainVotesDeniesAccessWithDefault() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tUnanimousBased mgr = makeDecisionManager();\n\t\tassertThat(!mgr.isAllowIfAllAbstainDecisions()).isTrue(); // check default\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(\"IGNORED_BY_ALL\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> mgr.decide(auth, new Object(), config));\n\t}\n\n\t@Test\n\tpublic void testThreeAbstainVotesGrantsAccessWithoutDefault() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tUnanimousBased mgr = makeDecisionManager();\n\t\tmgr.setAllowIfAllAbstainDecisions(true);\n\t\tassertThat(mgr.isAllowIfAllAbstainDecisions()).isTrue(); // check changed\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(\"IGNORED_BY_ALL\");\n\t\tmgr.decide(auth, new Object(), config);\n\t}\n\n\t@Test\n\tpublic void testTwoAffirmativeVotesTwoAbstainVotesGrantsAccess() {\n\t\tTestingAuthenticationToken auth = makeTestToken();\n\t\tUnanimousBased mgr = makeDecisionManager();\n\t\tList<ConfigAttribute> config = SecurityConfig.createList(new String[] { \"ROLE_1\", \"ROLE_2\" });\n\t\tmgr.decide(auth, new Object(), config);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationCollectionFilteringProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.afterinvocation;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class AclEntryAfterInvocationCollectionFilteringProviderTests {\n\n\t@Test\n\tpublic void objectsAreRemovedIfPermissionDenied() {\n\t\tAclService service = mock(AclService.class);\n\t\tAcl acl = mock(Acl.class);\n\t\tgiven(acl.isGranted(any(), any(), anyBoolean())).willReturn(false);\n\t\tgiven(service.readAclById(any(), any())).willReturn(acl);\n\t\tAclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(\n\t\t\t\tservice, Arrays.asList(mock(Permission.class)));\n\t\tprovider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));\n\t\tprovider.setProcessDomainObjectClass(Object.class);\n\t\tprovider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));\n\t\tObject returned = provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\tSecurityConfig.createList(\"AFTER_ACL_COLLECTION_READ\"),\n\t\t\t\tnew ArrayList(Arrays.asList(new Object(), new Object())));\n\t\tassertThat(returned).isInstanceOf(List.class);\n\t\tassertThat(((List) returned)).isEmpty();\n\t\treturned = provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\tSecurityConfig.createList(\"UNSUPPORTED\", \"AFTER_ACL_COLLECTION_READ\"),\n\t\t\t\tnew Object[] { new Object(), new Object() });\n\t\tassertThat(returned instanceof Object[]).isTrue();\n\t\tassertThat(((Object[]) returned).length == 0).isTrue();\n\t}\n\n\t@Test\n\tpublic void accessIsGrantedIfNoAttributesDefined() {\n\t\tAclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(\n\t\t\t\tmock(AclService.class), Arrays.asList(mock(Permission.class)));\n\t\tObject returned = new Object();\n\t\tassertThat(returned).isSameAs(provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\tCollections.<ConfigAttribute>emptyList(), returned));\n\t}\n\n\t@Test\n\tpublic void nullReturnObjectIsIgnored() {\n\t\tAclService service = mock(AclService.class);\n\t\tAclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(\n\t\t\t\tservice, Arrays.asList(mock(Permission.class)));\n\t\tassertThat(provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\tSecurityConfig.createList(\"AFTER_ACL_COLLECTION_READ\"), null))\n\t\t\t.isNull();\n\t\tverify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/acls/afterinvocation/AclEntryAfterInvocationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.afterinvocation;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.SpringSecurityMessageSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class AclEntryAfterInvocationProviderTests {\n\n\t@Test\n\tpublic void rejectsMissingPermissions() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AclEntryAfterInvocationProvider(mock(AclService.class), null));\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new AclEntryAfterInvocationProvider(mock(AclService.class), Collections.<Permission>emptyList()));\n\t}\n\n\t@Test\n\tpublic void accessIsAllowedIfPermissionIsGranted() {\n\t\tAclService service = mock(AclService.class);\n\t\tAcl acl = mock(Acl.class);\n\t\tgiven(acl.isGranted(any(List.class), any(List.class), anyBoolean())).willReturn(true);\n\t\tgiven(service.readAclById(any(), any())).willReturn(acl);\n\t\tAclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service,\n\t\t\t\tArrays.asList(mock(Permission.class)));\n\t\tprovider.setMessageSource(new SpringSecurityMessageSource());\n\t\tprovider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));\n\t\tprovider.setProcessDomainObjectClass(Object.class);\n\t\tprovider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));\n\t\tObject returned = new Object();\n\t\tassertThat(returned).isSameAs(provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\tSecurityConfig.createList(\"AFTER_ACL_READ\"), returned));\n\t}\n\n\t@Test\n\tpublic void accessIsGrantedIfNoAttributesDefined() {\n\t\tAclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(mock(AclService.class),\n\t\t\t\tArrays.asList(mock(Permission.class)));\n\t\tObject returned = new Object();\n\t\tassertThat(returned).isSameAs(provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\tCollections.<ConfigAttribute>emptyList(), returned));\n\t}\n\n\t@Test\n\tpublic void accessIsGrantedIfObjectTypeNotSupported() {\n\t\tAclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(mock(AclService.class),\n\t\t\t\tArrays.asList(mock(Permission.class)));\n\t\tprovider.setProcessDomainObjectClass(String.class);\n\t\t// Not a String\n\t\tObject returned = new Object();\n\t\tassertThat(returned).isSameAs(provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\tSecurityConfig.createList(\"AFTER_ACL_READ\"), returned));\n\t}\n\n\t@Test\n\tpublic void accessIsDeniedIfPermissionIsNotGranted() {\n\t\tAclService service = mock(AclService.class);\n\t\tAcl acl = mock(Acl.class);\n\t\tgiven(acl.isGranted(any(List.class), any(List.class), anyBoolean())).willReturn(false);\n\t\t// Try a second time with no permissions found\n\t\tgiven(acl.isGranted(any(), any(List.class), anyBoolean())).willThrow(new NotFoundException(\"\"));\n\t\tgiven(service.readAclById(any(), any())).willReturn(acl);\n\t\tAclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service,\n\t\t\t\tArrays.asList(mock(Permission.class)));\n\t\tprovider.setProcessConfigAttribute(\"MY_ATTRIBUTE\");\n\t\tprovider.setMessageSource(new SpringSecurityMessageSource());\n\t\tprovider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));\n\t\tprovider.setProcessDomainObjectClass(Object.class);\n\t\tprovider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\t\tSecurityConfig.createList(\"UNSUPPORTED\", \"MY_ATTRIBUTE\"), new Object()));\n\t\t// Second scenario with no acls found\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\t\tSecurityConfig.createList(\"UNSUPPORTED\", \"MY_ATTRIBUTE\"), new Object()));\n\t}\n\n\t@Test\n\tpublic void nullReturnObjectIsIgnored() {\n\t\tAclService service = mock(AclService.class);\n\t\tAclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service,\n\t\t\t\tArrays.asList(mock(Permission.class)));\n\t\tassertThat(provider.decide(mock(Authentication.class), new Object(),\n\t\t\t\tSecurityConfig.createList(\"AFTER_ACL_COLLECTION_READ\"), null))\n\t\t\t.isNull();\n\t\tverify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/messaging/access/expression/ExpressionBasedMessageSecurityMetadataSourceFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.messaging.access.intercept.MessageSecurityMetadataSource;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\n@ExtendWith(MockitoExtension.class)\n@SuppressWarnings(\"deprecation\")\npublic class ExpressionBasedMessageSecurityMetadataSourceFactoryTests {\n\n\t@Mock\n\tMessageMatcher<Object> matcher1;\n\n\t@Mock\n\tMessageMatcher<Object> matcher2;\n\n\t@Mock\n\tMessage<Object> message;\n\n\t@Mock\n\tAuthentication authentication;\n\n\tString expression1;\n\n\tString expression2;\n\n\tLinkedHashMap<MessageMatcher<?>, String> matcherToExpression;\n\n\tMessageSecurityMetadataSource source;\n\n\tMessageSecurityExpressionRoot<Object> rootObject;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.expression1 = \"permitAll\";\n\t\tthis.expression2 = \"denyAll\";\n\t\tthis.matcherToExpression = new LinkedHashMap<>();\n\t\tthis.matcherToExpression.put(this.matcher1, this.expression1);\n\t\tthis.matcherToExpression.put(this.matcher2, this.expression2);\n\t\tthis.source = ExpressionBasedMessageSecurityMetadataSourceFactory\n\t\t\t.createExpressionMessageMetadataSource(this.matcherToExpression);\n\t\tthis.rootObject = new MessageSecurityExpressionRoot<>(this.authentication, this.message);\n\t}\n\n\t@Test\n\tpublic void createExpressionMessageMetadataSourceNoMatch() {\n\t\tCollection<ConfigAttribute> attrs = this.source.getAttributes(this.message);\n\t\tassertThat(attrs).isEmpty();\n\t}\n\n\t@Test\n\tpublic void createExpressionMessageMetadataSourceMatchFirst() {\n\t\tgiven(this.matcher1.matches(this.message)).willReturn(true);\n\t\tCollection<ConfigAttribute> attrs = this.source.getAttributes(this.message);\n\t\tassertThat(attrs).hasSize(1);\n\t\tConfigAttribute attr = attrs.iterator().next();\n\t\tassertThat(attr).isInstanceOf(MessageExpressionConfigAttribute.class);\n\t\tassertThat(((MessageExpressionConfigAttribute) attr).getAuthorizeExpression().getValue(this.rootObject))\n\t\t\t.isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void createExpressionMessageMetadataSourceMatchSecond() {\n\t\tgiven(this.matcher2.matches(this.message)).willReturn(true);\n\t\tCollection<ConfigAttribute> attrs = this.source.getAttributes(this.message);\n\t\tassertThat(attrs).hasSize(1);\n\t\tConfigAttribute attr = attrs.iterator().next();\n\t\tassertThat(attr).isInstanceOf(MessageExpressionConfigAttribute.class);\n\t\tassertThat(((MessageExpressionConfigAttribute) attr).getAuthorizeExpression().getValue(this.rootObject))\n\t\t\t.isEqualTo(false);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/messaging/access/expression/MessageExpressionConfigAttributeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\nimport org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\n@SuppressWarnings(\"deprecation\")\npublic class MessageExpressionConfigAttributeTests {\n\n\t@Mock\n\tExpression expression;\n\n\t@Mock\n\tMessageMatcher<?> matcher;\n\n\tMessageExpressionConfigAttribute attribute;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.attribute = new MessageExpressionConfigAttribute(this.expression, this.matcher);\n\t}\n\n\t@Test\n\tpublic void constructorNullExpression() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MessageExpressionConfigAttribute(null, this.matcher));\n\t}\n\n\t@Test\n\tpublic void constructorNullMatcher() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new MessageExpressionConfigAttribute(this.expression, null));\n\t}\n\n\t@Test\n\tpublic void getAuthorizeExpression() {\n\t\tassertThat(this.attribute.getAuthorizeExpression()).isSameAs(this.expression);\n\t}\n\n\t@Test\n\tpublic void getAttribute() {\n\t\tassertThat(this.attribute.getAttribute()).isNull();\n\t}\n\n\t@Test\n\tpublic void toStringUsesExpressionString() {\n\t\tgiven(this.expression.getExpressionString()).willReturn(\"toString\");\n\t\tassertThat(this.attribute.toString()).isEqualTo(this.expression.getExpressionString());\n\t}\n\n\t@Test\n\tpublic void postProcessContext() {\n\t\tPathPatternMessageMatcher matcher = PathPatternMessageMatcher.withDefaults().matcher(\"/topics/{topic}/**\");\n\t\t// @formatter:off\n\t\tMessage<?> message = MessageBuilder.withPayload(\"M\")\n\t\t\t\t.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/topics/someTopic/sub1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tEvaluationContext context = mock(EvaluationContext.class);\n\t\tthis.attribute = new MessageExpressionConfigAttribute(this.expression, matcher);\n\t\tthis.attribute.postProcess(context, message);\n\t\tverify(context).setVariable(\"topic\", \"someTopic\");\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/messaging/access/expression/MessageExpressionVoterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\n@SuppressWarnings(\"deprecation\")\npublic class MessageExpressionVoterTests {\n\n\t@Mock\n\tAuthentication authentication;\n\n\t@Mock\n\tMessage<Object> message;\n\n\tCollection<ConfigAttribute> attributes;\n\n\t@Mock\n\tExpression expression;\n\n\t@Mock\n\tMessageMatcher<?> matcher;\n\n\t@Mock\n\tSecurityExpressionHandler<Message> expressionHandler;\n\n\t@Mock\n\tEvaluationContext evaluationContext;\n\n\tMessageExpressionVoter voter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.attributes = Arrays\n\t\t\t.<ConfigAttribute>asList(new MessageExpressionConfigAttribute(this.expression, this.matcher));\n\t\tthis.voter = new MessageExpressionVoter();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void voteGranted() {\n\t\tgiven(this.expression.getValue(any(EvaluationContext.class), eq(Boolean.class))).willReturn(true);\n\t\tgiven(this.matcher.matcher(any())).willCallRealMethod();\n\t\tassertThat(this.voter.vote(this.authentication, this.message, this.attributes))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void voteDenied() {\n\t\tgiven(this.expression.getValue(any(EvaluationContext.class), eq(Boolean.class))).willReturn(false);\n\t\tgiven(this.matcher.matcher(any())).willCallRealMethod();\n\t\tassertThat(this.voter.vote(this.authentication, this.message, this.attributes))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_DENIED);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void voteAbstain() {\n\t\tthis.attributes = Arrays.<ConfigAttribute>asList(new SecurityConfig(\"ROLE_USER\"));\n\t\tassertThat(this.voter.vote(this.authentication, this.message, this.attributes))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_ABSTAIN);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void supportsObjectClassFalse() {\n\t\tassertThat(this.voter.supports(Object.class)).isFalse();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void supportsMessageClassTrue() {\n\t\tassertThat(this.voter.supports(Message.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsSecurityConfigFalse() {\n\t\tassertThat(this.voter.supports(new SecurityConfig(\"ROLE_USER\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsMessageExpressionConfigAttributeTrue() {\n\t\tassertThat(this.voter.supports(new MessageExpressionConfigAttribute(this.expression, this.matcher))).isTrue();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setExpressionHandlerNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.voter.setExpressionHandler(null));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void customExpressionHandler() {\n\t\tthis.voter.setExpressionHandler(this.expressionHandler);\n\t\tgiven(this.expressionHandler.createEvaluationContext(this.authentication, this.message))\n\t\t\t.willReturn(this.evaluationContext);\n\t\tgiven(this.expression.getValue(this.evaluationContext, Boolean.class)).willReturn(true);\n\t\tgiven(this.matcher.matcher(any())).willCallRealMethod();\n\t\tassertThat(this.voter.vote(this.authentication, this.message, this.attributes))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t\tverify(this.expressionHandler).createEvaluationContext(this.authentication, this.message);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void postProcessEvaluationContext() {\n\t\tfinal MessageExpressionConfigAttribute configAttribute = mock(MessageExpressionConfigAttribute.class);\n\t\tthis.voter.setExpressionHandler(this.expressionHandler);\n\t\tgiven(this.expressionHandler.createEvaluationContext(this.authentication, this.message))\n\t\t\t.willReturn(this.evaluationContext);\n\t\tgiven(configAttribute.getAuthorizeExpression()).willReturn(this.expression);\n\t\tthis.attributes = Arrays.<ConfigAttribute>asList(configAttribute);\n\t\tgiven(configAttribute.postProcess(this.evaluationContext, this.message)).willReturn(this.evaluationContext);\n\t\tgiven(this.expression.getValue(any(EvaluationContext.class), eq(Boolean.class))).willReturn(true);\n\t\tassertThat(this.voter.vote(this.authentication, this.message, this.attributes))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t\tverify(configAttribute).postProcess(this.evaluationContext, this.message);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/messaging/access/intercept/ChannelSecurityInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.intercept.RunAsManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\n\n@ExtendWith(MockitoExtension.class)\n@SuppressWarnings(\"deprecation\")\npublic class ChannelSecurityInterceptorTests {\n\n\t@Mock\n\tMessage<Object> message;\n\n\t@Mock\n\tMessageChannel channel;\n\n\t@Mock\n\tMessageSecurityMetadataSource source;\n\n\t@Mock\n\tAccessDecisionManager accessDecisionManager;\n\n\t@Mock\n\tRunAsManager runAsManager;\n\n\t@Mock\n\tAuthentication runAs;\n\n\tAuthentication originalAuth;\n\n\tList<ConfigAttribute> attrs;\n\n\tChannelSecurityInterceptor interceptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.attrs = Arrays.<ConfigAttribute>asList(new SecurityConfig(\"ROLE_USER\"));\n\t\tthis.interceptor = new ChannelSecurityInterceptor(this.source);\n\t\tthis.interceptor.setAccessDecisionManager(this.accessDecisionManager);\n\t\tthis.interceptor.setRunAsManager(this.runAsManager);\n\t\tthis.originalAuth = new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(this.originalAuth);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorMessageSecurityMetadataSourceNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ChannelSecurityInterceptor(null));\n\t}\n\n\t@Test\n\tpublic void getSecureObjectClass() {\n\t\tassertThat(this.interceptor.getSecureObjectClass()).isEqualTo(Message.class);\n\t}\n\n\t@Test\n\tpublic void obtainSecurityMetadataSource() {\n\t\tassertThat(this.interceptor.obtainSecurityMetadataSource()).isEqualTo(this.source);\n\t}\n\n\t@Test\n\tpublic void preSendNullAttributes() {\n\t\tassertThat(this.interceptor.preSend(this.message, this.channel)).isSameAs(this.message);\n\t}\n\n\t@Test\n\tpublic void preSendGrant() {\n\t\tgiven(this.source.getAttributes(this.message)).willReturn(this.attrs);\n\t\tMessage<?> result = this.interceptor.preSend(this.message, this.channel);\n\t\tassertThat(result).isSameAs(this.message);\n\t}\n\n\t@Test\n\tpublic void preSendDeny() {\n\t\tgiven(this.source.getAttributes(this.message)).willReturn(this.attrs);\n\t\twillThrow(new AccessDeniedException(\"\")).given(this.accessDecisionManager)\n\t\t\t.decide(any(Authentication.class), eq(this.message), eq(this.attrs));\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.preSend(this.message, this.channel));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void preSendPostSendRunAs() {\n\t\tgiven(this.source.getAttributes(this.message)).willReturn(this.attrs);\n\t\tgiven(this.runAsManager.buildRunAs(any(Authentication.class), any(), any(Collection.class)))\n\t\t\t.willReturn(this.runAs);\n\t\tMessage<?> preSend = this.interceptor.preSend(this.message, this.channel);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.runAs);\n\t\tthis.interceptor.postSend(preSend, this.channel, true);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.originalAuth);\n\t}\n\n\t@Test\n\tpublic void afterSendCompletionNotTokenMessageNoExceptionThrown() {\n\t\tthis.interceptor.afterSendCompletion(this.message, this.channel, true, null);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void preSendFinallySendRunAs() {\n\t\tgiven(this.source.getAttributes(this.message)).willReturn(this.attrs);\n\t\tgiven(this.runAsManager.buildRunAs(any(Authentication.class), any(), any(Collection.class)))\n\t\t\t.willReturn(this.runAs);\n\t\tMessage<?> preSend = this.interceptor.preSend(this.message, this.channel);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.runAs);\n\t\tthis.interceptor.afterSendCompletion(preSend, this.channel, true, new RuntimeException());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.originalAuth);\n\t}\n\n\t@Test\n\tpublic void preReceive() {\n\t\tassertThat(this.interceptor.preReceive(this.channel)).isTrue();\n\t}\n\n\t@Test\n\tpublic void postReceive() {\n\t\tassertThat(this.interceptor.postReceive(this.message, this.channel)).isSameAs(this.message);\n\t}\n\n\t@Test\n\tpublic void afterReceiveCompletionNullExceptionNoExceptionThrown() {\n\t\tthis.interceptor.afterReceiveCompletion(this.message, this.channel, null);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/messaging/access/intercept/DefaultMessageSecurityMetadataSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\n@ExtendWith(MockitoExtension.class)\n@SuppressWarnings(\"deprecation\")\npublic class DefaultMessageSecurityMetadataSourceTests {\n\n\t@Mock\n\tMessageMatcher<Object> matcher1;\n\n\t@Mock\n\tMessageMatcher<Object> matcher2;\n\n\t@Mock\n\tMessage<?> message;\n\n\t@Mock\n\tAuthentication authentication;\n\n\tSecurityConfig config1;\n\n\tSecurityConfig config2;\n\n\tLinkedHashMap<MessageMatcher<?>, Collection<ConfigAttribute>> messageMap;\n\n\tMessageSecurityMetadataSource source;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.messageMap = new LinkedHashMap<>();\n\t\tthis.messageMap.put(this.matcher1, Arrays.<ConfigAttribute>asList(this.config1));\n\t\tthis.messageMap.put(this.matcher2, Arrays.<ConfigAttribute>asList(this.config2));\n\t\tthis.source = new DefaultMessageSecurityMetadataSource(this.messageMap);\n\t}\n\n\t@Test\n\tpublic void getAttributesEmpty() {\n\t\tassertThat(this.source.getAttributes(this.message)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getAttributesFirst() {\n\t\tgiven(this.matcher1.matches(this.message)).willReturn(true);\n\t\tassertThat(this.source.getAttributes(this.message)).containsOnly(this.config1);\n\t}\n\n\t@Test\n\tpublic void getAttributesSecond() {\n\t\tgiven(this.matcher1.matches(this.message)).willReturn(true);\n\t\tassertThat(this.source.getAttributes(this.message)).containsOnly(this.config2);\n\t}\n\n\t@Test\n\tpublic void getAllConfigAttributes() {\n\t\tassertThat(this.source.getAllConfigAttributes()).containsOnly(this.config1, this.config2);\n\t}\n\n\t@Test\n\tpublic void supportsFalse() {\n\t\tassertThat(this.source.supports(Object.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsTrue() {\n\t\tassertThat(this.source.supports(Message.class)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/DefaultWebInvocationPrivilegeEvaluatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.BDDMockito;\nimport org.mockito.Mockito;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.intercept.RunAsManager;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\n\n/**\n * Tests\n * {@link org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class DefaultWebInvocationPrivilegeEvaluatorTests {\n\n\tprivate AccessDecisionManager adm;\n\n\tprivate FilterInvocationSecurityMetadataSource ods;\n\n\tprivate RunAsManager ram;\n\n\tprivate FilterSecurityInterceptor interceptor;\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tthis.interceptor = new FilterSecurityInterceptor();\n\t\tthis.ods = Mockito.mock(FilterInvocationSecurityMetadataSource.class);\n\t\tthis.adm = Mockito.mock(AccessDecisionManager.class);\n\t\tthis.ram = Mockito.mock(RunAsManager.class);\n\t\tthis.interceptor.setAuthenticationManager(Mockito.mock(AuthenticationManager.class));\n\t\tthis.interceptor.setSecurityMetadataSource(this.ods);\n\t\tthis.interceptor.setAccessDecisionManager(this.adm);\n\t\tthis.interceptor.setRunAsManager(this.ram);\n\t\tthis.interceptor.setApplicationEventPublisher(Mockito.mock(ApplicationEventPublisher.class));\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void permitsAccessIfNoMatchingAttributesAndPublicInvocationsAllowed() {\n\t\tDefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);\n\t\tBDDMockito.given(this.ods.getAttributes(ArgumentMatchers.any())).willReturn(null);\n\t\tAssertions.assertThat(wipe.isAllowed(\"/context\", \"/foo/index.jsp\", \"GET\", Mockito.mock(Authentication.class)))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void deniesAccessIfNoMatchingAttributesAndPublicInvocationsNotAllowed() {\n\t\tDefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);\n\t\tBDDMockito.given(this.ods.getAttributes(ArgumentMatchers.any())).willReturn(null);\n\t\tthis.interceptor.setRejectPublicInvocations(true);\n\t\tAssertions.assertThat(wipe.isAllowed(\"/context\", \"/foo/index.jsp\", \"GET\", Mockito.mock(Authentication.class)))\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void deniesAccessIfAuthenticationIsNull() {\n\t\tDefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);\n\t\tAssertions.assertThat(wipe.isAllowed(\"/foo/index.jsp\", null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void allowsAccessIfAccessDecisionManagerDoes() {\n\t\tAuthentication token = new TestingAuthenticationToken(\"test\", \"Password\", \"MOCK_INDEX\");\n\t\tDefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);\n\t\tAssertions.assertThat(wipe.isAllowed(\"/foo/index.jsp\", token)).isTrue();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void deniesAccessIfAccessDecisionManagerDoes() {\n\t\tAuthentication token = new TestingAuthenticationToken(\"test\", \"Password\", \"MOCK_INDEX\");\n\t\tDefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);\n\t\tBDDMockito.willThrow(new AccessDeniedException(\"\"))\n\t\t\t.given(this.adm)\n\t\t\t.decide(ArgumentMatchers.any(Authentication.class), ArgumentMatchers.any(), ArgumentMatchers.anyList());\n\t\tAssertions.assertThat(wipe.isAllowed(\"/foo/index.jsp\", token)).isFalse();\n\t}\n\n\t@Test\n\tpublic void isAllowedWhenServletContextIsSetThenPassedFilterInvocationHasServletContext() {\n\t\tAuthentication token = new TestingAuthenticationToken(\"test\", \"Password\", \"MOCK_INDEX\");\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tArgumentCaptor<FilterInvocation> filterInvocationArgumentCaptor = ArgumentCaptor\n\t\t\t.forClass(FilterInvocation.class);\n\t\tDefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);\n\t\twipe.setServletContext(servletContext);\n\t\twipe.isAllowed(\"/foo/index.jsp\", token);\n\t\tMockito.verify(this.adm)\n\t\t\t.decide(ArgumentMatchers.eq(token), filterInvocationArgumentCaptor.capture(), ArgumentMatchers.any());\n\t\tAssertions.assertThat(filterInvocationArgumentCaptor.getValue().getRequest().getServletContext()).isNotNull();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/channel/ChannelDecisionManagerImplTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Vector;\n\nimport jakarta.servlet.FilterChain;\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.web.FilterInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link ChannelDecisionManagerImpl}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class ChannelDecisionManagerImplTests {\n\n\t@Test\n\tpublic void testCannotSetEmptyChannelProcessorsList() throws Exception {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tcdm.setChannelProcessors(new Vector());\n\t\t\tcdm.afterPropertiesSet();\n\t\t}).withMessage(\"A list of ChannelProcessors is required\");\n\t}\n\n\t@Test\n\tpublic void testCannotSetIncorrectObjectTypesIntoChannelProcessorsList() {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tList list = new Vector();\n\t\tlist.add(\"THIS IS NOT A CHANNELPROCESSOR\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> cdm.setChannelProcessors(list));\n\t}\n\n\t@Test\n\tpublic void testCannotSetNullChannelProcessorsList() throws Exception {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tcdm.setChannelProcessors(null);\n\t\t\tcdm.afterPropertiesSet();\n\t\t}).withMessage(\"A list of ChannelProcessors is required\");\n\t}\n\n\t@Test\n\tpublic void testDecideIsOperational() throws Exception {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tMockChannelProcessor cpXyz = new MockChannelProcessor(\"xyz\", false);\n\t\tMockChannelProcessor cpAbc = new MockChannelProcessor(\"abc\", true);\n\t\tList list = new Vector();\n\t\tlist.add(cpXyz);\n\t\tlist.add(cpAbc);\n\t\tcdm.setChannelProcessors(list);\n\t\tcdm.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tList<ConfigAttribute> cad = SecurityConfig.createList(\"xyz\");\n\t\tcdm.decide(fi, cad);\n\t\tAssertions.assertThat(fi.getResponse().isCommitted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testAnyChannelAttributeCausesProcessorsToBeSkipped() throws Exception {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tMockChannelProcessor cpAbc = new MockChannelProcessor(\"abc\", true);\n\t\tList list = new Vector();\n\t\tlist.add(cpAbc);\n\t\tcdm.setChannelProcessors(list);\n\t\tcdm.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tcdm.decide(fi, SecurityConfig.createList(new String[] { \"abc\", \"ANY_CHANNEL\" }));\n\t\tAssertions.assertThat(fi.getResponse().isCommitted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testDecideIteratesAllProcessorsIfNoneCommitAResponse() throws Exception {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tMockChannelProcessor cpXyz = new MockChannelProcessor(\"xyz\", false);\n\t\tMockChannelProcessor cpAbc = new MockChannelProcessor(\"abc\", false);\n\t\tList list = new Vector();\n\t\tlist.add(cpXyz);\n\t\tlist.add(cpAbc);\n\t\tcdm.setChannelProcessors(list);\n\t\tcdm.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tcdm.decide(fi, SecurityConfig.createList(\"SOME_ATTRIBUTE_NO_PROCESSORS_SUPPORT\"));\n\t\tAssertions.assertThat(fi.getResponse().isCommitted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testDelegatesSupports() throws Exception {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tMockChannelProcessor cpXyz = new MockChannelProcessor(\"xyz\", false);\n\t\tMockChannelProcessor cpAbc = new MockChannelProcessor(\"abc\", false);\n\t\tList list = new Vector();\n\t\tlist.add(cpXyz);\n\t\tlist.add(cpAbc);\n\t\tcdm.setChannelProcessors(list);\n\t\tcdm.afterPropertiesSet();\n\t\tassertThat(cdm.supports(new SecurityConfig(\"xyz\"))).isTrue();\n\t\tassertThat(cdm.supports(new SecurityConfig(\"abc\"))).isTrue();\n\t\tassertThat(cdm.supports(new SecurityConfig(\"UNSUPPORTED\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tassertThat(cdm.getChannelProcessors()).isNull();\n\t\tMockChannelProcessor cpXyz = new MockChannelProcessor(\"xyz\", false);\n\t\tMockChannelProcessor cpAbc = new MockChannelProcessor(\"abc\", false);\n\t\tList list = new Vector();\n\t\tlist.add(cpXyz);\n\t\tlist.add(cpAbc);\n\t\tcdm.setChannelProcessors(list);\n\t\tassertThat(cdm.getChannelProcessors()).isEqualTo(list);\n\t}\n\n\t@Test\n\tpublic void testStartupFailsWithEmptyChannelProcessorsList() throws Exception {\n\t\tChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(cdm::afterPropertiesSet)\n\t\t\t.withMessage(\"A list of ChannelProcessors is required\");\n\t}\n\n\tprivate class MockChannelProcessor implements ChannelProcessor {\n\n\t\tprivate String configAttribute;\n\n\t\tprivate boolean failIfCalled;\n\n\t\tMockChannelProcessor(String configAttribute, boolean failIfCalled) {\n\t\t\tthis.configAttribute = configAttribute;\n\t\t\tthis.failIfCalled = failIfCalled;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException {\n\t\t\tIterator iter = config.iterator();\n\t\t\tif (this.failIfCalled) {\n\t\t\t\tfail(\"Should not have called this channel processor: \" + this.configAttribute);\n\t\t\t}\n\t\t\twhile (iter.hasNext()) {\n\t\t\t\tConfigAttribute attr = (ConfigAttribute) iter.next();\n\t\t\t\tif (attr.getAttribute().equals(this.configAttribute)) {\n\t\t\t\t\tinvocation.getHttpResponse().sendRedirect(\"/redirected\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(ConfigAttribute attribute) {\n\t\t\treturn attribute.getAttribute().equals(this.configAttribute);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/channel/ChannelProcessingFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.io.IOException;\nimport java.util.Collection;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link ChannelProcessingFilter}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class ChannelProcessingFilterTests {\n\n\t@Test\n\tpublic void testDetectsMissingChannelDecisionManager() {\n\t\tChannelProcessingFilter filter = new ChannelProcessingFilter();\n\t\tMockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap(\"/path\", true, \"MOCK\");\n\t\tfilter.setSecurityMetadataSource(fids);\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testDetectsMissingFilterInvocationSecurityMetadataSource() {\n\t\tChannelProcessingFilter filter = new ChannelProcessingFilter();\n\t\tfilter.setChannelDecisionManager(new MockChannelDecisionManager(false, \"MOCK\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testDetectsSupportedConfigAttribute() {\n\t\tChannelProcessingFilter filter = new ChannelProcessingFilter();\n\t\tfilter.setChannelDecisionManager(new MockChannelDecisionManager(false, \"SUPPORTS_MOCK_ONLY\"));\n\t\tMockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap(\"/path\", true,\n\t\t\t\t\"SUPPORTS_MOCK_ONLY\");\n\t\tfilter.setSecurityMetadataSource(fids);\n\t\tfilter.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void testDetectsUnsupportedConfigAttribute() {\n\t\tChannelProcessingFilter filter = new ChannelProcessingFilter();\n\t\tfilter.setChannelDecisionManager(new MockChannelDecisionManager(false, \"SUPPORTS_MOCK_ONLY\"));\n\t\tMockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap(\"/path\", true,\n\t\t\t\t\"SUPPORTS_MOCK_ONLY\", \"INVALID_ATTRIBUTE\");\n\t\tfilter.setSecurityMetadataSource(fids);\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testDoFilterWhenManagerDoesCommitResponse() throws Exception {\n\t\tChannelProcessingFilter filter = new ChannelProcessingFilter();\n\t\tfilter.setChannelDecisionManager(new MockChannelDecisionManager(true, \"SOME_ATTRIBUTE\"));\n\t\tMockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap(\"/path\", true, \"SOME_ATTRIBUTE\");\n\t\tfilter.setSecurityMetadataSource(fids);\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"/path\").build();\n\t\trequest.setQueryString(\"info=now\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, mock(FilterChain.class));\n\t}\n\n\t@Test\n\tpublic void testDoFilterWhenManagerDoesNotCommitResponse() throws Exception {\n\t\tChannelProcessingFilter filter = new ChannelProcessingFilter();\n\t\tfilter.setChannelDecisionManager(new MockChannelDecisionManager(false, \"SOME_ATTRIBUTE\"));\n\t\tMockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap(\"/path\", true, \"SOME_ATTRIBUTE\");\n\t\tfilter.setSecurityMetadataSource(fids);\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"/path\").build();\n\t\trequest.setQueryString(\"info=now\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, mock(FilterChain.class));\n\t}\n\n\t@Test\n\tpublic void testDoFilterWhenNullConfigAttributeReturned() throws Exception {\n\t\tChannelProcessingFilter filter = new ChannelProcessingFilter();\n\t\tfilter.setChannelDecisionManager(new MockChannelDecisionManager(false, \"NOT_USED\"));\n\t\tMockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap(\"/path\", true, \"NOT_USED\");\n\t\tfilter.setSecurityMetadataSource(fids);\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"/PATH_NOT_MATCHING_CONFIG_ATTRIBUTE\").build();\n\t\trequest.setQueryString(\"info=now\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, mock(FilterChain.class));\n\t}\n\n\t@Test\n\tpublic void testGetterSetters() {\n\t\tChannelProcessingFilter filter = new ChannelProcessingFilter();\n\t\tfilter.setChannelDecisionManager(new MockChannelDecisionManager(false, \"MOCK\"));\n\t\tassertThat(filter.getChannelDecisionManager() != null).isTrue();\n\t\tMockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap(\"/path\", false, \"MOCK\");\n\t\tfilter.setSecurityMetadataSource(fids);\n\t\tassertThat(filter.getSecurityMetadataSource()).isSameAs(fids);\n\t\tfilter.afterPropertiesSet();\n\t}\n\n\tprivate class MockChannelDecisionManager implements ChannelDecisionManager {\n\n\t\tprivate String supportAttribute;\n\n\t\tprivate boolean commitAResponse;\n\n\t\tMockChannelDecisionManager(boolean commitAResponse, String supportAttribute) {\n\t\t\tthis.commitAResponse = commitAResponse;\n\t\t\tthis.supportAttribute = supportAttribute;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException {\n\t\t\tif (this.commitAResponse) {\n\t\t\t\tinvocation.getHttpResponse().sendRedirect(\"/redirected\");\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(ConfigAttribute attribute) {\n\t\t\treturn attribute.getAttribute().equals(this.supportAttribute);\n\t\t}\n\n\t}\n\n\tprivate class MockFilterInvocationDefinitionMap implements FilterInvocationSecurityMetadataSource {\n\n\t\tprivate Collection<ConfigAttribute> toReturn;\n\n\t\tprivate String servletPath;\n\n\t\tprivate boolean provideIterator;\n\n\t\tMockFilterInvocationDefinitionMap(String servletPath, boolean provideIterator, String... toReturn) {\n\t\t\tthis.servletPath = servletPath;\n\t\t\tthis.toReturn = SecurityConfig.createList(toReturn);\n\t\t\tthis.provideIterator = provideIterator;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {\n\t\t\tFilterInvocation fi = (FilterInvocation) object;\n\t\t\tif (this.servletPath.equals(fi.getHttpRequest().getServletPath())) {\n\t\t\t\treturn this.toReturn;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\t\tif (!this.provideIterator) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn this.toReturn;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> clazz) {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/channel/InsecureChannelProcessorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport jakarta.servlet.FilterChain;\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link InsecureChannelProcessor}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class InsecureChannelProcessorTests {\n\n\t@Test\n\tpublic void testDecideDetectsAcceptableChannel() throws Exception {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"http://localhost:8080\")\n\t\t\t.requestUri(\"/bigapp\", \"/servlet\", null)\n\t\t\t.queryString(\"info=true\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tInsecureChannelProcessor processor = new InsecureChannelProcessor();\n\t\tprocessor.decide(fi, SecurityConfig.createList(\"SOME_IGNORED_ATTRIBUTE\", \"REQUIRES_INSECURE_CHANNEL\"));\n\t\tAssertions.assertThat(fi.getResponse().isCommitted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testDecideDetectsUnacceptableChannel() throws Exception {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"https://localhost:8443\")\n\t\t\t.requestUri(\"/bigapp\", \"/servlet\", null)\n\t\t\t.queryString(\"info=true\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tInsecureChannelProcessor processor = new InsecureChannelProcessor();\n\t\tprocessor.decide(fi,\n\t\t\t\tSecurityConfig.createList(new String[] { \"SOME_IGNORED_ATTRIBUTE\", \"REQUIRES_INSECURE_CHANNEL\" }));\n\t\tAssertions.assertThat(fi.getResponse().isCommitted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testDecideRejectsNulls() throws Exception {\n\t\tInsecureChannelProcessor processor = new InsecureChannelProcessor();\n\t\tprocessor.afterPropertiesSet();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> processor.decide(null, null));\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tInsecureChannelProcessor processor = new InsecureChannelProcessor();\n\t\tassertThat(processor.getInsecureKeyword()).isEqualTo(\"REQUIRES_INSECURE_CHANNEL\");\n\t\tprocessor.setInsecureKeyword(\"X\");\n\t\tassertThat(processor.getInsecureKeyword()).isEqualTo(\"X\");\n\t\tassertThat(processor.getEntryPoint() != null).isTrue();\n\t\tprocessor.setEntryPoint(null);\n\t\tassertThat(processor.getEntryPoint() == null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testMissingEntryPoint() throws Exception {\n\t\tInsecureChannelProcessor processor = new InsecureChannelProcessor();\n\t\tprocessor.setEntryPoint(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)\n\t\t\t.withMessage(\"entryPoint required\");\n\t}\n\n\t@Test\n\tpublic void testMissingSecureChannelKeyword() throws Exception {\n\t\tInsecureChannelProcessor processor = new InsecureChannelProcessor();\n\t\tprocessor.setInsecureKeyword(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)\n\t\t\t.withMessage(\"insecureKeyword required\");\n\t\tprocessor.setInsecureKeyword(\"\");\n\t\tassertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)\n\t\t\t.withMessage(\"insecureKeyword required\");\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tInsecureChannelProcessor processor = new InsecureChannelProcessor();\n\t\tassertThat(processor.supports(new SecurityConfig(\"REQUIRES_INSECURE_CHANNEL\"))).isTrue();\n\t\tassertThat(processor.supports(null)).isFalse();\n\t\tassertThat(processor.supports(new SecurityConfig(\"NOT_SUPPORTED\"))).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/channel/RetryWithHttpEntryPointTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.security.web.RedirectStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link RetryWithHttpEntryPoint}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class RetryWithHttpEntryPointTests {\n\n\t@Test\n\tpublic void testDetectsMissingPortMapper() {\n\t\tRetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ep.setPortMapper(null));\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tRetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();\n\t\tPortMapper portMapper = mock(PortMapper.class);\n\t\tRedirectStrategy redirector = mock(RedirectStrategy.class);\n\t\tep.setPortMapper(portMapper);\n\t\tep.setRedirectStrategy(redirector);\n\t\tassertThat(ep.getPortMapper()).isSameAs(portMapper);\n\t\tassertThat(ep.getRedirectStrategy()).isSameAs(redirector);\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/bigWebApp/hello/pathInfo.html\");\n\t\trequest.setQueryString(\"open=true\");\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"localhost\");\n\t\trequest.setServerPort(443);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.commence(request, response);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"http://localhost/bigWebApp/hello/pathInfo.html?open=true\");\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWithNullQueryString() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/bigWebApp/hello\");\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"localhost\");\n\t\trequest.setServerPort(443);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.commence(request, response);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"http://localhost/bigWebApp/hello\");\n\t}\n\n\t@Test\n\tpublic void testOperationWhenTargetPortIsUnknown() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/bigWebApp\");\n\t\trequest.setQueryString(\"open=true\");\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"www.example.com\");\n\t\trequest.setServerPort(8768);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.commence(request, response);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/bigWebApp?open=true\");\n\t}\n\n\t@Test\n\tpublic void testOperationWithNonStandardPort() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/bigWebApp/hello/pathInfo.html\");\n\t\trequest.setQueryString(\"open=true\");\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"localhost\");\n\t\trequest.setServerPort(9999);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tMap<String, String> map = new HashMap<>();\n\t\tmap.put(\"8888\", \"9999\");\n\t\tportMapper.setPortMappings(map);\n\t\tRetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();\n\t\tep.setPortMapper(portMapper);\n\t\tep.commence(request, response);\n\t\tassertThat(response.getRedirectedUrl())\n\t\t\t.isEqualTo(\"http://localhost:8888/bigWebApp/hello/pathInfo.html?open=true\");\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/channel/RetryWithHttpsEntryPointTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.PortMapperImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link RetryWithHttpsEntryPoint}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class RetryWithHttpsEntryPointTests {\n\n\t@Test\n\tpublic void testDetectsMissingPortMapper() {\n\t\tRetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ep.setPortMapper(null));\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tRetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tassertThat(ep.getPortMapper() != null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/bigWebApp/hello/pathInfo.html\");\n\t\trequest.setQueryString(\"open=true\");\n\t\trequest.setScheme(\"http\");\n\t\trequest.setServerName(\"www.example.com\");\n\t\trequest.setServerPort(80);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.commence(request, response);\n\t\tassertThat(response.getRedirectedUrl())\n\t\t\t.isEqualTo(\"https://www.example.com/bigWebApp/hello/pathInfo.html?open=true\");\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWithNullQueryString() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/bigWebApp/hello\");\n\t\trequest.setScheme(\"http\");\n\t\trequest.setServerName(\"www.example.com\");\n\t\trequest.setServerPort(80);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.commence(request, response);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://www.example.com/bigWebApp/hello\");\n\t}\n\n\t@Test\n\tpublic void testOperationWhenTargetPortIsUnknown() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/bigWebApp\");\n\t\trequest.setQueryString(\"open=true\");\n\t\trequest.setScheme(\"http\");\n\t\trequest.setServerName(\"www.example.com\");\n\t\trequest.setServerPort(8768);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.commence(request, response);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/bigWebApp?open=true\");\n\t}\n\n\t@Test\n\tpublic void testOperationWithNonStandardPort() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/bigWebApp/hello/pathInfo.html\");\n\t\trequest.setQueryString(\"open=true\");\n\t\trequest.setScheme(\"http\");\n\t\trequest.setServerName(\"www.example.com\");\n\t\trequest.setServerPort(8888);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tMap<String, String> map = new HashMap<>();\n\t\tmap.put(\"8888\", \"9999\");\n\t\tportMapper.setPortMappings(map);\n\t\tRetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();\n\t\tep.setPortMapper(portMapper);\n\t\tep.commence(request, response);\n\t\tassertThat(response.getRedirectedUrl())\n\t\t\t.isEqualTo(\"https://www.example.com:9999/bigWebApp/hello/pathInfo.html?open=true\");\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/channel/SecureChannelProcessorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.channel;\n\nimport jakarta.servlet.FilterChain;\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link SecureChannelProcessor}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class SecureChannelProcessorTests {\n\n\t@Test\n\tpublic void testDecideDetectsAcceptableChannel() throws Exception {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"https://localhost:8443\")\n\t\t\t.requestUri(\"/bigapp\", \"/servlet\", null)\n\t\t\t.queryString(\"info=true\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tSecureChannelProcessor processor = new SecureChannelProcessor();\n\t\tprocessor.decide(fi, SecurityConfig.createList(\"SOME_IGNORED_ATTRIBUTE\", \"REQUIRES_SECURE_CHANNEL\"));\n\t\tAssertions.assertThat(fi.getResponse().isCommitted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testDecideDetectsUnacceptableChannel() throws Exception {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"http://localhost:8080\")\n\t\t\t.requestUri(\"/bigapp\", \"/servlet\", null)\n\t\t\t.queryString(\"info=true\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tSecureChannelProcessor processor = new SecureChannelProcessor();\n\t\tprocessor.decide(fi,\n\t\t\t\tSecurityConfig.createList(new String[] { \"SOME_IGNORED_ATTRIBUTE\", \"REQUIRES_SECURE_CHANNEL\" }));\n\t\tAssertions.assertThat(fi.getResponse().isCommitted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testDecideRejectsNulls() throws Exception {\n\t\tSecureChannelProcessor processor = new SecureChannelProcessor();\n\t\tprocessor.afterPropertiesSet();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> processor.decide(null, null));\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tSecureChannelProcessor processor = new SecureChannelProcessor();\n\t\tassertThat(processor.getSecureKeyword()).isEqualTo(\"REQUIRES_SECURE_CHANNEL\");\n\t\tprocessor.setSecureKeyword(\"X\");\n\t\tassertThat(processor.getSecureKeyword()).isEqualTo(\"X\");\n\t\tassertThat(processor.getEntryPoint() != null).isTrue();\n\t\tprocessor.setEntryPoint(null);\n\t\tassertThat(processor.getEntryPoint() == null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testMissingEntryPoint() throws Exception {\n\t\tSecureChannelProcessor processor = new SecureChannelProcessor();\n\t\tprocessor.setEntryPoint(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)\n\t\t\t.withMessage(\"entryPoint required\");\n\t}\n\n\t@Test\n\tpublic void testMissingSecureChannelKeyword() throws Exception {\n\t\tSecureChannelProcessor processor = new SecureChannelProcessor();\n\t\tprocessor.setSecureKeyword(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(processor::afterPropertiesSet)\n\t\t\t.withMessage(\"secureKeyword required\");\n\t\tprocessor.setSecureKeyword(\"\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> processor.afterPropertiesSet())\n\t\t\t.withMessage(\"secureKeyword required\");\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tSecureChannelProcessor processor = new SecureChannelProcessor();\n\t\tassertThat(processor.supports(new SecurityConfig(\"REQUIRES_SECURE_CHANNEL\"))).isTrue();\n\t\tassertThat(processor.supports(null)).isFalse();\n\t\tassertThat(processor.supports(new SecurityConfig(\"NOT_SUPPORTED\"))).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.context.support.StaticApplicationContext;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.FilterInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class DefaultWebSecurityExpressionHandlerTests {\n\n\t@Mock\n\tprivate AuthenticationTrustResolver trustResolver;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\t@Mock\n\tprivate FilterInvocation invocation;\n\n\tprivate DefaultWebSecurityExpressionHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.handler = new DefaultWebSecurityExpressionHandler();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void expressionPropertiesAreResolvedAgainstAppContextBeans() {\n\t\tStaticApplicationContext appContext = new StaticApplicationContext();\n\t\tRootBeanDefinition bean = new RootBeanDefinition(SecurityConfig.class);\n\t\tbean.getConstructorArgumentValues().addGenericArgumentValue(\"ROLE_A\");\n\t\tappContext.registerBeanDefinition(\"role\", bean);\n\t\tthis.handler.setApplicationContext(appContext);\n\t\tEvaluationContext ctx = this.handler.createEvaluationContext(mock(Authentication.class),\n\t\t\t\tmock(FilterInvocation.class));\n\t\tExpressionParser parser = this.handler.getExpressionParser();\n\t\tassertThat(parser.parseExpression(\"@role.getAttribute() == 'ROLE_A'\").getValue(ctx, Boolean.class)).isTrue();\n\t\tassertThat(parser.parseExpression(\"@role.attribute == 'ROLE_A'\").getValue(ctx, Boolean.class)).isTrue();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setTrustResolverNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setTrustResolver(null));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void createEvaluationContextCustomTrustResolver() {\n\t\tthis.handler.setTrustResolver(this.trustResolver);\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"anonymous\");\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.invocation);\n\t\tassertThat(expression.getValue(context, Boolean.class)).isFalse();\n\t\tverify(this.trustResolver).isAnonymous(this.authentication);\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/expression/ExpressionBasedFilterInvocationSecurityMetadataSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings(\"deprecation\")\npublic class ExpressionBasedFilterInvocationSecurityMetadataSourceTests {\n\n\t@Test\n\tpublic void expectedAttributeIsReturned() {\n\t\tfinal String expression = \"hasRole('X')\";\n\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();\n\t\trequestMap.put(AnyRequestMatcher.INSTANCE, SecurityConfig.createList(expression));\n\t\tExpressionBasedFilterInvocationSecurityMetadataSource mds = new ExpressionBasedFilterInvocationSecurityMetadataSource(\n\t\t\t\trequestMap, new DefaultWebSecurityExpressionHandler());\n\t\tassertThat(mds.getAllConfigAttributes()).hasSize(1);\n\t\tCollection<ConfigAttribute> attrs = mds.getAttributes(new FilterInvocation(\"/path\", \"GET\"));\n\t\tassertThat(attrs).hasSize(1);\n\t\tWebExpressionConfigAttribute attribute = (WebExpressionConfigAttribute) attrs.toArray()[0];\n\t\tassertThat(attribute.getAttribute()).isNull();\n\t\tassertThat(attribute.getAuthorizeExpression().getExpressionString()).isEqualTo(expression);\n\t\tassertThat(attribute.toString()).isEqualTo(expression);\n\t}\n\n\t@Test\n\tpublic void invalidExpressionIsRejected() {\n\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();\n\t\trequestMap.put(AnyRequestMatcher.INSTANCE, SecurityConfig.createList(\"hasRole('X'\"));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap,\n\t\t\t\t\tnew DefaultWebSecurityExpressionHandler()));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/expression/WebExpressionVoterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.ArrayList;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings({ \"unchecked\", \"deprecation\" })\npublic class WebExpressionVoterTests {\n\n\tprivate Authentication user = new TestingAuthenticationToken(\"user\", \"pass\", \"X\");\n\n\t@Test\n\tpublic void supportsWebConfigAttributeAndFilterInvocation() {\n\t\tWebExpressionVoter voter = new WebExpressionVoter();\n\t\tassertThat(voter.supports(\n\t\t\t\tnew WebExpressionConfigAttribute(mock(Expression.class), mock(EvaluationContextPostProcessor.class))))\n\t\t\t.isTrue();\n\t\tassertThat(voter.supports(FilterInvocation.class)).isTrue();\n\t\tassertThat(voter.supports(MethodInvocation.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic void abstainsIfNoAttributeFound() {\n\t\tWebExpressionVoter voter = new WebExpressionVoter();\n\t\tassertThat(\n\t\t\t\tvoter.vote(this.user, new FilterInvocation(\"/path\", \"GET\"), SecurityConfig.createList(\"A\", \"B\", \"C\")))\n\t\t\t.isEqualTo(AccessDecisionVoter.ACCESS_ABSTAIN);\n\t}\n\n\t@Test\n\tpublic void grantsAccessIfExpressionIsTrueDeniesIfFalse() {\n\t\tWebExpressionVoter voter = new WebExpressionVoter();\n\t\tExpression ex = mock(Expression.class);\n\t\tEvaluationContextPostProcessor postProcessor = mock(EvaluationContextPostProcessor.class);\n\t\tgiven(postProcessor.postProcess(any(EvaluationContext.class), any(FilterInvocation.class)))\n\t\t\t.willAnswer((invocation) -> invocation.getArgument(0));\n\t\tWebExpressionConfigAttribute weca = new WebExpressionConfigAttribute(ex, postProcessor);\n\t\tEvaluationContext ctx = mock(EvaluationContext.class);\n\t\tSecurityExpressionHandler eh = mock(SecurityExpressionHandler.class);\n\t\tFilterInvocation fi = new FilterInvocation(\"/path\", \"GET\");\n\t\tvoter.setExpressionHandler(eh);\n\t\tgiven(eh.createEvaluationContext(this.user, fi)).willReturn(ctx);\n\t\tgiven(ex.getValue(ctx, Boolean.class)).willReturn(Boolean.TRUE, Boolean.FALSE);\n\t\tArrayList attributes = new ArrayList();\n\t\tattributes.addAll(SecurityConfig.createList(\"A\", \"B\", \"C\"));\n\t\tattributes.add(weca);\n\t\tassertThat(voter.vote(this.user, fi, attributes)).isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);\n\t\t// Second time false\n\t\tassertThat(voter.vote(this.user, fi, attributes)).isEqualTo(AccessDecisionVoter.ACCESS_DENIED);\n\t}\n\n\t// SEC-2507\n\t@Test\n\tpublic void supportFilterInvocationSubClass() {\n\t\tWebExpressionVoter voter = new WebExpressionVoter();\n\t\tassertThat(voter.supports(FilterInvocationChild.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportFilterInvocation() {\n\t\tWebExpressionVoter voter = new WebExpressionVoter();\n\t\tassertThat(voter.supports(FilterInvocation.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsObjectIsFalse() {\n\t\tWebExpressionVoter voter = new WebExpressionVoter();\n\t\tassertThat(voter.supports(Object.class)).isFalse();\n\t}\n\n\tprivate static class FilterInvocationChild extends FilterInvocation {\n\n\t\tFilterInvocationChild(ServletRequest request, ServletResponse response, FilterChain chain) {\n\t\t\tsuper(request, response, chain);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/intercept/DefaultFilterInvocationSecurityMetadataSourceTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link DefaultFilterInvocationSecurityMetadataSource}.\n *\n * @author Ben Alex\n */\n@SuppressWarnings(\"deprecation\")\npublic class DefaultFilterInvocationSecurityMetadataSourceTests {\n\n\tprivate DefaultFilterInvocationSecurityMetadataSource fids;\n\n\tprivate Collection<ConfigAttribute> def = SecurityConfig.createList(\"ROLE_ONE\");\n\n\tprivate void createFids(String pattern, HttpMethod method) {\n\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();\n\t\trequestMap.put(PathPatternRequestMatcher.pathPattern(method, pattern), this.def);\n\t\tthis.fids = new DefaultFilterInvocationSecurityMetadataSource(requestMap);\n\t}\n\n\t@Test\n\tpublic void lookupNotRequiringExactMatchSucceedsIfNotMatching() {\n\t\tcreateFids(\"/secure/super/**\", null);\n\t\tFilterInvocation fi = createFilterInvocation(\"/secure/super/somefile.html\", null, null, \"GET\");\n\t\tassertThat(this.fids.getAttributes(fi)).isEqualTo(this.def);\n\t}\n\n\t/**\n\t * SEC-501. Note that as of 2.0, lower case comparisons are the default for this\n\t * class.\n\t */\n\t@Test\n\tpublic void lookupNotRequiringExactMatchSucceedsIfSecureUrlPathContainsUpperCase() {\n\t\tcreateFids(\"/secure/super/**\", null);\n\t\tFilterInvocation fi = createFilterInvocation(\"/secure\", \"/super/somefile.html\", null, \"GET\");\n\t\tCollection<ConfigAttribute> response = this.fids.getAttributes(fi);\n\t\tassertThat(response).isEqualTo(this.def);\n\t}\n\n\t@Test\n\tpublic void lookupRequiringExactMatchIsSuccessful() {\n\t\tcreateFids(\"/SeCurE/super/**\", null);\n\t\tFilterInvocation fi = createFilterInvocation(\"/SeCurE/super/somefile.html\", null, null, \"GET\");\n\t\tCollection<ConfigAttribute> response = this.fids.getAttributes(fi);\n\t\tassertThat(response).isEqualTo(this.def);\n\t}\n\n\t@Test\n\tpublic void lookupRequiringExactMatchWithAdditionalSlashesIsSuccessful() {\n\t\tcreateFids(\"/someAdminPage.html**\", null);\n\t\tFilterInvocation fi = createFilterInvocation(\"/someAdminPage.html\", null, \"a=/test\", \"GET\");\n\t\tCollection<ConfigAttribute> response = this.fids.getAttributes(fi);\n\t\tassertThat(response); // see SEC-161 (it should truncate after ?\n\t\t\t\t\t\t\t\t// sign).isEqualTo(def)\n\t}\n\n\t@Test\n\tpublic void httpMethodLookupSucceeds() {\n\t\tcreateFids(\"/somepage**\", HttpMethod.GET);\n\t\tFilterInvocation fi = createFilterInvocation(\"/somepage\", null, null, \"GET\");\n\t\tCollection<ConfigAttribute> attrs = this.fids.getAttributes(fi);\n\t\tassertThat(attrs).isEqualTo(this.def);\n\t}\n\n\t@Test\n\tpublic void generalMatchIsUsedIfNoMethodSpecificMatchExists() {\n\t\tcreateFids(\"/somepage**\", null);\n\t\tFilterInvocation fi = createFilterInvocation(\"/somepage\", null, null, \"GET\");\n\t\tCollection<ConfigAttribute> attrs = this.fids.getAttributes(fi);\n\t\tassertThat(attrs).isEqualTo(this.def);\n\t}\n\n\t@Test\n\tpublic void requestWithDifferentHttpMethodDoesntMatch() {\n\t\tcreateFids(\"/somepage**\", HttpMethod.GET);\n\t\tFilterInvocation fi = createFilterInvocation(\"/somepage\", null, null, \"POST\");\n\t\tCollection<ConfigAttribute> attrs = this.fids.getAttributes(fi);\n\t\tassertThat(attrs).isEmpty();\n\t}\n\n\t// SEC-1236\n\t@Test\n\tpublic void mixingPatternsWithAndWithoutHttpMethodsIsSupported() {\n\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();\n\t\tCollection<ConfigAttribute> userAttrs = SecurityConfig.createList(\"A\");\n\t\trequestMap.put(PathPatternRequestMatcher.pathPattern(\"/user/**\"), userAttrs);\n\t\trequestMap.put(PathPatternRequestMatcher.pathPattern(HttpMethod.GET, \"/teller/**\"),\n\t\t\t\tSecurityConfig.createList(\"B\"));\n\t\tthis.fids = new DefaultFilterInvocationSecurityMetadataSource(requestMap);\n\t\tFilterInvocation fi = createFilterInvocation(\"/user\", null, null, \"GET\");\n\t\tCollection<ConfigAttribute> attrs = this.fids.getAttributes(fi);\n\t\tassertThat(attrs).isEqualTo(userAttrs);\n\t}\n\n\t/**\n\t * Check fixes for SEC-321\n\t */\n\t@Test\n\tpublic void extraQuestionMarkStillMatches() {\n\t\tcreateFids(\"/someAdminPage.html*\", null);\n\t\tFilterInvocation fi = createFilterInvocation(\"/someAdminPage.html\", null, null, \"GET\");\n\t\tCollection<ConfigAttribute> response = this.fids.getAttributes(fi);\n\t\tassertThat(response).isEqualTo(this.def);\n\t\tfi = createFilterInvocation(\"/someAdminPage.html\", null, \"?\", \"GET\");\n\t\tresponse = this.fids.getAttributes(fi);\n\t\tassertThat(response).isEqualTo(this.def);\n\t}\n\n\tprivate FilterInvocation createFilterInvocation(String servletPath, String pathInfo, String queryString,\n\t\t\tString method) {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.request(method)\n\t\t\t.requestUri(null, servletPath, pathInfo)\n\t\t\t.queryString(queryString)\n\t\t\t.build();\n\t\treturn new FilterInvocation(request, new MockHttpServletResponse(), mock(FilterChain.class));\n\t}\n\n}\n"
  },
  {
    "path": "access/src/test/java/org/springframework/security/web/access/intercept/FilterSecurityInterceptorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.event.AuthorizedEvent;\nimport org.springframework.security.access.intercept.AfterInvocationManager;\nimport org.springframework.security.access.intercept.RunAsManager;\nimport org.springframework.security.access.intercept.RunAsUserToken;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyCollection;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link FilterSecurityInterceptor}.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @author Rob Winch\n */\n@SuppressWarnings(\"deprecation\")\npublic class FilterSecurityInterceptorTests {\n\n\tprivate AuthenticationManager am;\n\n\tprivate AccessDecisionManager adm;\n\n\tprivate FilterInvocationSecurityMetadataSource ods;\n\n\tprivate RunAsManager ram;\n\n\tprivate FilterSecurityInterceptor interceptor;\n\n\tprivate ApplicationEventPublisher publisher;\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tthis.interceptor = new FilterSecurityInterceptor();\n\t\tthis.am = mock(AuthenticationManager.class);\n\t\tthis.ods = mock(FilterInvocationSecurityMetadataSource.class);\n\t\tthis.adm = mock(AccessDecisionManager.class);\n\t\tthis.ram = mock(RunAsManager.class);\n\t\tthis.publisher = mock(ApplicationEventPublisher.class);\n\t\tthis.interceptor.setAuthenticationManager(this.am);\n\t\tthis.interceptor.setSecurityMetadataSource(this.ods);\n\t\tthis.interceptor.setAccessDecisionManager(this.adm);\n\t\tthis.interceptor.setRunAsManager(this.ram);\n\t\tthis.interceptor.setApplicationEventPublisher(this.publisher);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testEnsuresAccessDecisionManagerSupportsFilterInvocationClass() throws Exception {\n\t\tgiven(this.adm.supports(FilterInvocation.class)).willReturn(true);\n\t\tassertThatIllegalArgumentException().isThrownBy(this.interceptor::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testEnsuresRunAsManagerSupportsFilterInvocationClass() throws Exception {\n\t\tgiven(this.adm.supports(FilterInvocation.class)).willReturn(false);\n\t\tassertThatIllegalArgumentException().isThrownBy(this.interceptor::afterPropertiesSet);\n\t}\n\n\t/**\n\t * We just test invocation works in a success event. There is no need to test access\n\t * denied events as the abstract parent enforces that logic, which is extensively\n\t * tested separately.\n\t */\n\t@Test\n\tpublic void testSuccessfulInvocation() throws Throwable {\n\t\t// Setup a Context\n\t\tAuthentication token = new TestingAuthenticationToken(\"Test\", \"Password\", \"NOT_USED\");\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tFilterInvocation fi = createinvocation();\n\t\tgiven(this.ods.getAttributes(fi)).willReturn(SecurityConfig.createList(\"MOCK_OK\"));\n\t\tthis.interceptor.invoke(fi);\n\t\t// SEC-1697\n\t\tverify(this.publisher, never()).publishEvent(any(AuthorizedEvent.class));\n\t}\n\n\t@Test\n\tpublic void afterInvocationIsNotInvokedIfExceptionThrown() throws Exception {\n\t\tAuthentication token = new TestingAuthenticationToken(\"Test\", \"Password\", \"NOT_USED\");\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tFilterInvocation fi = createinvocation();\n\t\tFilterChain chain = fi.getChain();\n\t\twillThrow(new RuntimeException()).given(chain)\n\t\t\t.doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tgiven(this.ods.getAttributes(fi)).willReturn(SecurityConfig.createList(\"MOCK_OK\"));\n\t\tAfterInvocationManager aim = mock(AfterInvocationManager.class);\n\t\tthis.interceptor.setAfterInvocationManager(aim);\n\t\tassertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.interceptor.invoke(fi));\n\t\tverifyNoMoreInteractions(aim);\n\t}\n\n\t// SEC-1967\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void finallyInvocationIsInvokedIfExceptionThrown() throws Exception {\n\t\tSecurityContext ctx = SecurityContextHolder.getContext();\n\t\tAuthentication token = new TestingAuthenticationToken(\"Test\", \"Password\", \"NOT_USED\");\n\t\ttoken.setAuthenticated(true);\n\t\tctx.setAuthentication(token);\n\t\tRunAsManager runAsManager = mock(RunAsManager.class);\n\t\tgiven(runAsManager.buildRunAs(eq(token), any(), anyCollection()))\n\t\t\t.willReturn(new RunAsUserToken(\"key\", \"someone\", \"creds\", token.getAuthorities(), token.getClass()));\n\t\tthis.interceptor.setRunAsManager(runAsManager);\n\t\tFilterInvocation fi = createinvocation();\n\t\tFilterChain chain = fi.getChain();\n\t\twillThrow(new RuntimeException()).given(chain)\n\t\t\t.doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tgiven(this.ods.getAttributes(fi)).willReturn(SecurityConfig.createList(\"MOCK_OK\"));\n\t\tAfterInvocationManager aim = mock(AfterInvocationManager.class);\n\t\tthis.interceptor.setAfterInvocationManager(aim);\n\t\tassertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.interceptor.invoke(fi));\n\t\t// Check we've changed back\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(ctx);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(token);\n\t}\n\n\t@Test\n\t// gh-4997\n\tpublic void doFilterWhenObserveOncePerRequestThenAttributeNotSet() throws Exception {\n\t\tthis.interceptor.setObserveOncePerRequest(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tthis.interceptor.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(request.getAttributeNames().hasMoreElements()).isFalse();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenObserveOncePerRequestFalseAndInvokedTwiceThenObserveTwice() throws Throwable {\n\t\tAuthentication token = new TestingAuthenticationToken(\"Test\", \"Password\", \"NOT_USED\");\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tFilterInvocation fi = createinvocation();\n\t\tgiven(this.ods.getAttributes(fi)).willReturn(SecurityConfig.createList(\"MOCK_OK\"));\n\t\tthis.interceptor.invoke(fi);\n\t\tthis.interceptor.invoke(fi);\n\t\tverify(this.adm, times(2)).decide(any(), any(), any());\n\t}\n\n\tprivate FilterInvocation createinvocation() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"/secure/page.html\").build();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tFilterInvocation fi = new FilterInvocation(request, response, chain);\n\t\treturn fi;\n\t}\n\n}\n"
  },
  {
    "path": "acl/spring-security-acl.gradle",
    "content": "plugins {\n\tid 'compile-warnings-error'\n\tid 'javadoc-warnings-error'\n\tid 'security-nullability'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi 'org.springframework:spring-aop'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-jdbc'\n\tapi 'org.springframework:spring-tx'\n\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation 'org.springframework:spring-beans'\n\ttestImplementation 'org.springframework:spring-context-support'\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/AclPermissionCacheOptimizer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.PermissionCacheOptimizer;\nimport org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;\nimport org.springframework.security.acls.domain.SidRetrievalStrategyImpl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Batch loads ACLs for collections of objects to allow optimised filtering.\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic class AclPermissionCacheOptimizer implements PermissionCacheOptimizer {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final AclService aclService;\n\n\tprivate SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();\n\n\tprivate ObjectIdentityRetrievalStrategy oidRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();\n\n\tpublic AclPermissionCacheOptimizer(AclService aclService) {\n\t\tthis.aclService = aclService;\n\t}\n\n\t@Override\n\tpublic void cachePermissionsFor(Authentication authentication, Collection<?> objects) {\n\t\tif (objects.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<ObjectIdentity> oidsToCache = new ArrayList<>(objects.size());\n\t\tfor (Object domainObject : objects) {\n\t\t\tif (domainObject != null) {\n\t\t\t\tObjectIdentity oid = this.oidRetrievalStrategy.getObjectIdentity(domainObject);\n\t\t\t\toidsToCache.add(oid);\n\t\t\t}\n\t\t}\n\t\tList<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);\n\t\tthis.logger.debug(LogMessage.of(() -> \"Eagerly loading Acls for \" + oidsToCache.size() + \" objects\"));\n\t\tthis.aclService.readAclsById(oidsToCache, sids);\n\t}\n\n\tpublic void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {\n\t\tthis.oidRetrievalStrategy = objectIdentityRetrievalStrategy;\n\t}\n\n\tpublic void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {\n\t\tthis.sidRetrievalStrategy = sidRetrievalStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/AclPermissionEvaluator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.acls.domain.DefaultPermissionFactory;\nimport org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;\nimport org.springframework.security.acls.domain.PermissionFactory;\nimport org.springframework.security.acls.domain.SidRetrievalStrategyImpl;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityGenerator;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Used by Spring Security's expression-based access control implementation to evaluate\n * permissions for a particular object using the ACL module. Similar in behaviour to\n * <code> org.springframework.security.acls.AclEntryVoter AclEntryVoter </code>\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class AclPermissionEvaluator implements PermissionEvaluator {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final AclService aclService;\n\n\tprivate ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();\n\n\tprivate ObjectIdentityGenerator objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();\n\n\tprivate SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();\n\n\tprivate PermissionFactory permissionFactory = new DefaultPermissionFactory();\n\n\tpublic AclPermissionEvaluator(AclService aclService) {\n\t\tthis.aclService = aclService;\n\t}\n\n\t/**\n\t * Determines whether the user has the given permission(s) on the domain object using\n\t * the ACL configuration. If the domain object is null, returns false (this can always\n\t * be overridden using a null check in the expression itself).\n\t */\n\t@Override\n\tpublic boolean hasPermission(Authentication authentication, @Nullable Object domainObject, Object permission) {\n\t\tif (domainObject == null) {\n\t\t\treturn false;\n\t\t}\n\t\tObjectIdentity objectIdentity = this.objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);\n\t\treturn checkPermission(authentication, objectIdentity, permission);\n\t}\n\n\t@Override\n\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\tObject permission) {\n\t\tObjectIdentity objectIdentity = this.objectIdentityGenerator.createObjectIdentity(targetId, targetType);\n\t\treturn checkPermission(authentication, objectIdentity, permission);\n\t}\n\n\tprivate boolean checkPermission(Authentication authentication, ObjectIdentity oid, Object permission) {\n\t\t// Obtain the SIDs applicable to the principal\n\t\tList<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);\n\t\tList<Permission> requiredPermission = resolvePermission(permission);\n\t\tthis.logger.debug(LogMessage.of(() -> \"Checking permission '\" + permission + \"' for object '\" + oid + \"'\"));\n\t\ttry {\n\t\t\t// Lookup only ACLs for SIDs we're interested in\n\t\t\tAcl acl = this.aclService.readAclById(oid, sids);\n\t\t\tif (acl.isGranted(requiredPermission, sids, false)) {\n\t\t\t\tthis.logger.debug(\"Access is granted\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tthis.logger.debug(\"Returning false - ACLs returned, but insufficient permissions for this principal\");\n\t\t}\n\t\tcatch (NotFoundException nfe) {\n\t\t\tthis.logger.debug(\"Returning false - no ACLs apply for this principal\");\n\t\t}\n\t\treturn false;\n\t}\n\n\tList<Permission> resolvePermission(Object permission) {\n\t\tif (permission instanceof Integer) {\n\t\t\treturn Arrays.asList(this.permissionFactory.buildFromMask((Integer) permission));\n\t\t}\n\t\tif (permission instanceof Permission) {\n\t\t\treturn Arrays.asList((Permission) permission);\n\t\t}\n\t\tif (permission instanceof Permission[]) {\n\t\t\treturn Arrays.asList((Permission[]) permission);\n\t\t}\n\t\tif (permission instanceof String permString) {\n\t\t\tPermission p = buildPermission(permString);\n\t\t\tif (p != null) {\n\t\t\t\treturn Arrays.asList(p);\n\t\t\t}\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Unsupported permission: \" + permission);\n\t}\n\n\tprivate Permission buildPermission(String permString) {\n\t\ttry {\n\t\t\treturn this.permissionFactory.buildFromName(permString);\n\t\t}\n\t\tcatch (IllegalArgumentException notfound) {\n\t\t\treturn this.permissionFactory.buildFromName(permString.toUpperCase(Locale.ENGLISH));\n\t\t}\n\t}\n\n\tpublic void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {\n\t\tthis.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;\n\t}\n\n\tpublic void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {\n\t\tthis.objectIdentityGenerator = objectIdentityGenerator;\n\t}\n\n\tpublic void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {\n\t\tthis.sidRetrievalStrategy = sidRetrievalStrategy;\n\t}\n\n\tpublic void setPermissionFactory(PermissionFactory permissionFactory) {\n\t\tthis.permissionFactory = permissionFactory;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/aot/hint/AclRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.aot.hint;\n\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.security.acls.domain.AclImpl;\nimport org.springframework.security.acls.domain.AuditLogger;\nimport org.springframework.security.acls.domain.BasePermission;\nimport org.springframework.security.acls.domain.GrantedAuthoritySid;\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.domain.PrincipalSid;\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AuditableAccessControlEntry;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.Sid;\n\n/**\n * {@link RuntimeHintsRegistrar} for ACL (Access Control List) classes.\n *\n * @author Josh Long\n */\nclass AclRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\tregisterAclDomainHints(hints);\n\t\tregisterJdbcSchemaHints(hints);\n\t}\n\n\tprivate void registerAclDomainHints(RuntimeHints hints) {\n\t\t// Register core ACL domain types\n\t\tStream\n\t\t\t.of(Acl.class, AccessControlEntry.class, AuditableAccessControlEntry.class, ObjectIdentity.class, Sid.class,\n\t\t\t\t\tAclImpl.class, AccessControlEntry.class, AuditLogger.class, ObjectIdentityImpl.class,\n\t\t\t\t\tPrincipalSid.class, GrantedAuthoritySid.class, BasePermission.class)\n\t\t\t.forEach((c) -> hints.reflection()\n\t\t\t\t.registerType(TypeReference.of(c),\n\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS)));\n\n\t}\n\n\tprivate void registerJdbcSchemaHints(RuntimeHints hints) {\n\t\tString[] sqlFiles = new String[] { \"createAclSchema.sql\", \"createAclSchemaMySQL.sql\",\n\t\t\t\t\"createAclSchemaOracle.sql\", \"createAclSchemaPostgres.sql\", \"createAclSchemaSqlServer.sql\",\n\t\t\t\t\"createAclSchemaWithAclClassIdType.sql\", \"select.sql\" };\n\t\tfor (String sqlFile : sqlFiles) {\n\t\t\tResource sqlResource = new ClassPathResource(sqlFile);\n\t\t\tif (sqlResource.exists()) {\n\t\t\t\thints.resources().registerResource(sqlResource);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * AOT and native image hint support for ACLs.\n */\n@NullMarked\npackage org.springframework.security.acls.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/AbstractPermission.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.Permission;\n\n/**\n * Provides an abstract superclass for {@link Permission} implementations.\n *\n * @author Ben Alex\n * @since 2.0.3\n */\npublic abstract class AbstractPermission implements Permission {\n\n\tprotected final char code;\n\n\tprotected int mask;\n\n\t/**\n\t * Sets the permission mask and uses the '*' character to represent active bits when\n\t * represented as a bit pattern string.\n\t * @param mask the integer bit mask for the permission\n\t */\n\tprotected AbstractPermission(int mask) {\n\t\tthis.mask = mask;\n\t\tthis.code = '*';\n\t}\n\n\t/**\n\t * Sets the permission mask and uses the specified character for active bits.\n\t * @param mask the integer bit mask for the permission\n\t * @param code the character to print for each active bit in the mask (see\n\t * {@link Permission#getPattern()})\n\t */\n\tprotected AbstractPermission(int mask, char code) {\n\t\tthis.mask = mask;\n\t\tthis.code = code;\n\t}\n\n\t@Override\n\tpublic final boolean equals(Object obj) {\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!(obj instanceof Permission other)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn (this.mask == other.getMask());\n\t}\n\n\t@Override\n\tpublic final int hashCode() {\n\t\treturn this.mask;\n\t}\n\n\t@Override\n\tpublic final String toString() {\n\t\treturn this.getClass().getSimpleName() + \"[\" + getPattern() + \"=\" + this.mask + \"]\";\n\t}\n\n\t@Override\n\tpublic final int getMask() {\n\t\treturn this.mask;\n\t}\n\n\t@Override\n\tpublic String getPattern() {\n\t\treturn AclFormattingUtils.printBinary(this.mask, this.code);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/AccessControlEntryImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AuditableAccessControlEntry;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.util.Assert;\n\n/**\n * An immutable default implementation of <code>AccessControlEntry</code>.\n *\n * @author Ben Alex\n */\npublic class AccessControlEntryImpl implements AccessControlEntry, AuditableAccessControlEntry {\n\n\tprivate final Acl acl;\n\n\tprivate Permission permission;\n\n\tprivate final @Nullable Serializable id;\n\n\tprivate final Sid sid;\n\n\tprivate boolean auditFailure = false;\n\n\tprivate boolean auditSuccess = false;\n\n\tprivate final boolean granting;\n\n\tpublic AccessControlEntryImpl(@Nullable Serializable id, Acl acl, Sid sid, Permission permission, boolean granting,\n\t\t\tboolean auditSuccess, boolean auditFailure) {\n\t\tAssert.notNull(acl, \"Acl required\");\n\t\tAssert.notNull(sid, \"Sid required\");\n\t\tAssert.notNull(permission, \"Permission required\");\n\t\tthis.id = id;\n\t\tthis.acl = acl; // can be null\n\t\tthis.sid = sid;\n\t\tthis.permission = permission;\n\t\tthis.granting = granting;\n\t\tthis.auditSuccess = auditSuccess;\n\t\tthis.auditFailure = auditFailure;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object arg0) {\n\t\tif (!(arg0 instanceof AccessControlEntryImpl)) {\n\t\t\treturn false;\n\t\t}\n\t\tAccessControlEntryImpl other = (AccessControlEntryImpl) arg0;\n\t\tif (this.acl == null) {\n\t\t\tif (other.getAcl() != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// Both this.acl and rhs.acl are null and thus equal\n\t\t}\n\t\telse {\n\t\t\t// this.acl is non-null\n\t\t\tif (other.getAcl() == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Both this.acl and rhs.acl are non-null, so do a comparison\n\t\t\tif (this.acl.getObjectIdentity() == null) {\n\t\t\t\tif (other.acl.getObjectIdentity() != null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// Both this.acl and rhs.acl are null and thus equal\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Both this.acl.objectIdentity and rhs.acl.objectIdentity are non-null\n\t\t\t\tif (!this.acl.getObjectIdentity().equals(other.getAcl().getObjectIdentity())) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (this.id == null) {\n\t\t\tif (other.id != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// Both this.id and rhs.id are null and thus equal\n\t\t}\n\t\telse {\n\t\t\t// this.id is non-null\n\t\t\tif (other.id == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// Both this.id and rhs.id are non-null\n\t\t\tif (!this.id.equals(other.id)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif ((this.auditFailure != other.isAuditFailure()) || (this.auditSuccess != other.isAuditSuccess())\n\t\t\t\t|| (this.granting != other.isGranting()) || !this.permission.equals(other.getPermission())\n\t\t\t\t|| !this.sid.equals(other.getSid())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.permission.hashCode();\n\t\tresult = 31 * result + ((this.id != null) ? this.id.hashCode() : 0);\n\t\tresult = 31 * result + (this.sid.hashCode());\n\t\tresult = 31 * result + (this.auditFailure ? 1 : 0);\n\t\tresult = 31 * result + (this.auditSuccess ? 1 : 0);\n\t\tresult = 31 * result + (this.granting ? 1 : 0);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic Acl getAcl() {\n\t\treturn this.acl;\n\t}\n\n\t@Override\n\tpublic @Nullable Serializable getId() {\n\t\treturn this.id;\n\t}\n\n\t@Override\n\tpublic Permission getPermission() {\n\t\treturn this.permission;\n\t}\n\n\t@Override\n\tpublic Sid getSid() {\n\t\treturn this.sid;\n\t}\n\n\t@Override\n\tpublic boolean isAuditFailure() {\n\t\treturn this.auditFailure;\n\t}\n\n\t@Override\n\tpublic boolean isAuditSuccess() {\n\t\treturn this.auditSuccess;\n\t}\n\n\t@Override\n\tpublic boolean isGranting() {\n\t\treturn this.granting;\n\t}\n\n\tvoid setAuditFailure(boolean auditFailure) {\n\t\tthis.auditFailure = auditFailure;\n\t}\n\n\tvoid setAuditSuccess(boolean auditSuccess) {\n\t\tthis.auditSuccess = auditSuccess;\n\t}\n\n\tvoid setPermission(Permission permission) {\n\t\tAssert.notNull(permission, \"Permission required\");\n\t\tthis.permission = permission;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"AccessControlEntryImpl[\");\n\t\tsb.append(\"id: \").append(this.id).append(\"; \");\n\t\tsb.append(\"granting: \").append(this.granting).append(\"; \");\n\t\tsb.append(\"sid: \").append(this.sid).append(\"; \");\n\t\tsb.append(\"permission: \").append(this.permission).append(\"; \");\n\t\tsb.append(\"auditSuccess: \").append(this.auditSuccess).append(\"; \");\n\t\tsb.append(\"auditFailure: \").append(this.auditFailure);\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.Acl;\n\n/**\n * Strategy used by {@link AclImpl} to determine whether a principal is permitted to call\n * administrative methods on the <code>AclImpl</code>.\n *\n * @author Ben Alex\n */\npublic interface AclAuthorizationStrategy {\n\n\tint CHANGE_OWNERSHIP = 0;\n\n\tint CHANGE_AUDITING = 1;\n\n\tint CHANGE_GENERAL = 2;\n\n\tvoid securityCheck(Acl acl, int changeType);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * Default implementation of {@link AclAuthorizationStrategy}.\n * <p>\n * Permission will be granted if at least one of the following conditions is true for the\n * current principal.\n * <ul>\n * <li>is the owner (as defined by the ACL).</li>\n * <li>holds the relevant system-wide {@link GrantedAuthority} injected into the\n * constructor.</li>\n * <li>has {@link BasePermission#ADMINISTRATION} permission (as defined by the ACL).</li>\n * </ul>\n *\n * @author Ben Alex\n */\npublic class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final GrantedAuthority gaGeneralChanges;\n\n\tprivate final GrantedAuthority gaModifyAuditing;\n\n\tprivate final GrantedAuthority gaTakeOwnership;\n\n\tprivate SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();\n\n\tprivate RoleHierarchy roleHierarchy = new NullRoleHierarchy();\n\n\t/**\n\t * Constructor. The only mandatory parameter relates to the system-wide\n\t * {@link GrantedAuthority} instances that can be held to always permit ACL changes.\n\t * @param auths the <code>GrantedAuthority</code>s that have special permissions\n\t * (index 0 is the authority needed to change ownership, index 1 is the authority\n\t * needed to modify auditing details, index 2 is the authority needed to change other\n\t * ACL and ACE details) (required)\n\t * <p>\n\t * Alternatively, a single value can be supplied for all three permissions.\n\t */\n\tpublic AclAuthorizationStrategyImpl(GrantedAuthority... auths) {\n\t\tAssert.isTrue(auths != null && (auths.length == 3 || auths.length == 1),\n\t\t\t\t\"One or three GrantedAuthority instances required\");\n\t\tif (auths.length == 3) {\n\t\t\tthis.gaTakeOwnership = auths[0];\n\t\t\tthis.gaModifyAuditing = auths[1];\n\t\t\tthis.gaGeneralChanges = auths[2];\n\t\t}\n\t\telse {\n\t\t\tthis.gaTakeOwnership = auths[0];\n\t\t\tthis.gaModifyAuditing = auths[0];\n\t\t\tthis.gaGeneralChanges = auths[0];\n\t\t}\n\t}\n\n\t@Override\n\tpublic void securityCheck(Acl acl, int changeType) {\n\t\tSecurityContext context = this.securityContextHolderStrategy.getContext();\n\t\tif ((context == null) || (context.getAuthentication() == null)\n\t\t\t\t|| !context.getAuthentication().isAuthenticated()) {\n\t\t\tthrow new AccessDeniedException(\"Authenticated principal required to operate with ACLs\");\n\t\t}\n\t\tAuthentication authentication = context.getAuthentication();\n\t\t// Check if authorized by virtue of ACL ownership\n\t\tSid currentUser = createCurrentUser(authentication);\n\t\tSid owner = acl.getOwner();\n\t\tif (owner != null && currentUser.equals(owner)\n\t\t\t\t&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Iterate this principal's authorities to determine right\n\t\tCollection<? extends GrantedAuthority> reachableGrantedAuthorities = this.roleHierarchy\n\t\t\t.getReachableGrantedAuthorities(authentication.getAuthorities());\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(reachableGrantedAuthorities);\n\t\tif (owner instanceof GrantedAuthoritySid\n\t\t\t\t&& authorities.contains(((GrantedAuthoritySid) owner).getGrantedAuthority())) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Not authorized by ACL ownership; try via adminstrative permissions\n\t\tGrantedAuthority requiredAuthority = getRequiredAuthority(changeType);\n\n\t\tif (authorities.contains(requiredAuthority.getAuthority())) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Try to get permission via ACEs within the ACL\n\t\tList<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);\n\t\tif (acl.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), sids, false)) {\n\t\t\treturn;\n\t\t}\n\n\t\tthrow new AccessDeniedException(\n\t\t\t\t\"Principal does not have required ACL permissions to perform requested operation\");\n\t}\n\n\tprivate GrantedAuthority getRequiredAuthority(int changeType) {\n\t\tif (changeType == CHANGE_AUDITING) {\n\t\t\treturn this.gaModifyAuditing;\n\t\t}\n\t\tif (changeType == CHANGE_GENERAL) {\n\t\t\treturn this.gaGeneralChanges;\n\t\t}\n\t\tif (changeType == CHANGE_OWNERSHIP) {\n\t\t\treturn this.gaTakeOwnership;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Unknown change type\");\n\t}\n\n\t/**\n\t * Creates a principal-like sid from the authentication information.\n\t * @param authentication the authentication information that can provide principal and\n\t * thus the sid's id will be dependant on the value inside\n\t * @return a sid with the ID taken from the authentication information\n\t */\n\tprotected Sid createCurrentUser(Authentication authentication) {\n\t\treturn new PrincipalSid(authentication);\n\t}\n\n\tpublic void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {\n\t\tAssert.notNull(sidRetrievalStrategy, \"SidRetrievalStrategy required\");\n\t\tthis.sidRetrievalStrategy = sidRetrievalStrategy;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Sets the {@link RoleHierarchy} to use. The default is to use a\n\t * {@link NullRoleHierarchy}\n\t * @since 6.4\n\t */\n\tpublic void setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tAssert.notNull(roleHierarchy, \"roleHierarchy cannot be null\");\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/AclFormattingUtils.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.util.Assert;\n\n/**\n * Utility methods for displaying ACL information.\n *\n * @author Ben Alex\n */\npublic abstract class AclFormattingUtils {\n\n\tpublic static String demergePatterns(String original, String removeBits) {\n\t\tAssert.notNull(original, \"Original string required\");\n\t\tAssert.notNull(removeBits, \"Bits To Remove string required\");\n\t\tAssert.isTrue(original.length() == removeBits.length(),\n\t\t\t\t\"Original and Bits To Remove strings must be identical length\");\n\t\tchar[] replacement = new char[original.length()];\n\t\tfor (int i = 0; i < original.length(); i++) {\n\t\t\tif (removeBits.charAt(i) == Permission.RESERVED_OFF) {\n\t\t\t\treplacement[i] = original.charAt(i);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treplacement[i] = Permission.RESERVED_OFF;\n\t\t\t}\n\t\t}\n\t\treturn new String(replacement);\n\t}\n\n\tpublic static String mergePatterns(String original, String extraBits) {\n\t\tAssert.notNull(original, \"Original string required\");\n\t\tAssert.notNull(extraBits, \"Extra Bits string required\");\n\t\tAssert.isTrue(original.length() == extraBits.length(),\n\t\t\t\t\"Original and Extra Bits strings must be identical length\");\n\t\tchar[] replacement = new char[extraBits.length()];\n\t\tfor (int i = 0; i < extraBits.length(); i++) {\n\t\t\tif (extraBits.charAt(i) == Permission.RESERVED_OFF) {\n\t\t\t\treplacement[i] = original.charAt(i);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treplacement[i] = extraBits.charAt(i);\n\t\t\t}\n\t\t}\n\t\treturn new String(replacement);\n\t}\n\n\t/**\n\t * Returns a representation of the active bits in the presented mask, with each active\n\t * bit being denoted by character '*'.\n\t * <p>\n\t * Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.\n\t * @param i the integer bit mask to print the active bits for\n\t * @return a 32-character representation of the bit mask\n\t */\n\tpublic static String printBinary(int i) {\n\t\treturn printBinary(i, '*', Permission.RESERVED_OFF);\n\t}\n\n\t/**\n\t * Returns a representation of the active bits in the presented mask, with each active\n\t * bit being denoted by the passed character.\n\t * <p>\n\t * Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.\n\t * @param mask the integer bit mask to print the active bits for\n\t * @param code the character to print when an active bit is detected\n\t * @return a 32-character representation of the bit mask\n\t */\n\tpublic static String printBinary(int mask, char code) {\n\t\tAssert.doesNotContain(Character.toString(code), Character.toString(Permission.RESERVED_ON),\n\t\t\t\t() -> Permission.RESERVED_ON + \" is a reserved character code\");\n\t\tAssert.doesNotContain(Character.toString(code), Character.toString(Permission.RESERVED_OFF),\n\t\t\t\t() -> Permission.RESERVED_OFF + \" is a reserved character code\");\n\t\treturn printBinary(mask, Permission.RESERVED_ON, Permission.RESERVED_OFF).replace(Permission.RESERVED_ON, code);\n\t}\n\n\tprivate static String printBinary(int i, char on, char off) {\n\t\tString s = Integer.toBinaryString(i);\n\t\tString pattern = Permission.THIRTY_TWO_RESERVED_OFF;\n\t\tString temp2 = pattern.substring(0, pattern.length() - s.length()) + s;\n\t\treturn temp2.replace('0', off).replace('1', on);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/AclImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AuditableAcl;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.OwnershipAcl;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.PermissionGrantingStrategy;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.UnloadedSidException;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * Base implementation of <code>Acl</code>.\n *\n * @author Ben Alex\n */\npublic class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {\n\n\tprivate @Nullable Acl parentAcl;\n\n\tprivate transient AclAuthorizationStrategy aclAuthorizationStrategy;\n\n\tprivate transient PermissionGrantingStrategy permissionGrantingStrategy;\n\n\tprivate final List<AccessControlEntry> aces = new ArrayList<>();\n\n\tprivate ObjectIdentity objectIdentity;\n\n\tprivate Serializable id;\n\n\t// OwnershipAcl\n\tprivate @Nullable Sid owner;\n\n\t// includes all SIDs the WHERE clause covered, even if there was no ACE for a SID\n\tprivate @Nullable List<Sid> loadedSids = null;\n\n\tprivate boolean entriesInheriting = true;\n\n\t/**\n\t * Minimal constructor, which should be used\n\t * {@link org.springframework.security.acls.model.MutableAclService#createAcl(ObjectIdentity)}\n\t * .\n\t * @param objectIdentity the object identity this ACL relates to (required)\n\t * @param id the primary key assigned to this ACL (required)\n\t * @param aclAuthorizationStrategy authorization strategy (required)\n\t * @param auditLogger audit logger (required)\n\t */\n\tpublic AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,\n\t\t\tAuditLogger auditLogger) {\n\t\tAssert.notNull(objectIdentity, \"Object Identity required\");\n\t\tAssert.notNull(id, \"Id required\");\n\t\tAssert.notNull(aclAuthorizationStrategy, \"AclAuthorizationStrategy required\");\n\t\tAssert.notNull(auditLogger, \"AuditLogger required\");\n\t\tthis.objectIdentity = objectIdentity;\n\t\tthis.id = id;\n\t\tthis.aclAuthorizationStrategy = aclAuthorizationStrategy;\n\t\tthis.permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);\n\t}\n\n\t/**\n\t * Full constructor, which should be used by persistence tools that do not provide\n\t * field-level access features.\n\t * @param objectIdentity the object identity this ACL relates to\n\t * @param id the primary key assigned to this ACL\n\t * @param aclAuthorizationStrategy authorization strategy\n\t * @param grantingStrategy the {@code PermissionGrantingStrategy} which will be used\n\t * by the {@code isGranted()} method\n\t * @param parentAcl the parent (may be may be {@code null})\n\t * @param loadedSids the loaded SIDs if only a subset were loaded (may be {@code null}\n\t * )\n\t * @param entriesInheriting if ACEs from the parent should inherit into this ACL\n\t * @param owner the owner (required)\n\t */\n\tpublic AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,\n\t\t\tPermissionGrantingStrategy grantingStrategy, @Nullable Acl parentAcl, @Nullable List<Sid> loadedSids,\n\t\t\tboolean entriesInheriting, Sid owner) {\n\t\tAssert.notNull(objectIdentity, \"Object Identity required\");\n\t\tAssert.notNull(id, \"Id required\");\n\t\tAssert.notNull(aclAuthorizationStrategy, \"AclAuthorizationStrategy required\");\n\t\tAssert.notNull(owner, \"Owner required\");\n\t\tthis.objectIdentity = objectIdentity;\n\t\tthis.id = id;\n\t\tthis.aclAuthorizationStrategy = aclAuthorizationStrategy;\n\t\tthis.parentAcl = parentAcl; // may be null\n\t\tthis.loadedSids = loadedSids; // may be null\n\t\tthis.entriesInheriting = entriesInheriting;\n\t\tthis.owner = owner;\n\t\tthis.permissionGrantingStrategy = grantingStrategy;\n\t}\n\n\t/**\n\t * Private no-argument constructor for use by reflection-based persistence tools along\n\t * with field-level access.\n\t */\n\t@SuppressWarnings({ \"unused\", \"NullAway.Init\" })\n\tprivate AclImpl() {\n\t}\n\n\t@Override\n\tpublic void deleteAce(int aceIndex) throws NotFoundException {\n\t\tthis.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t\tverifyAceIndexExists(aceIndex);\n\t\tsynchronized (this.aces) {\n\t\t\tthis.aces.remove(aceIndex);\n\t\t}\n\t}\n\n\tprivate void verifyAceIndexExists(int aceIndex) {\n\t\tif (aceIndex < 0) {\n\t\t\tthrow new NotFoundException(\"aceIndex must be greater than or equal to zero\");\n\t\t}\n\t\tif (aceIndex >= this.aces.size()) {\n\t\t\tthrow new NotFoundException(\"aceIndex must refer to an index of the AccessControlEntry list. \"\n\t\t\t\t\t+ \"List size is \" + this.aces.size() + \", index was \" + aceIndex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)\n\t\t\tthrows NotFoundException {\n\t\tthis.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t\tAssert.notNull(permission, \"Permission required\");\n\t\tAssert.notNull(sid, \"Sid required\");\n\t\tif (atIndexLocation < 0) {\n\t\t\tthrow new NotFoundException(\"atIndexLocation must be greater than or equal to zero\");\n\t\t}\n\t\tif (atIndexLocation > this.aces.size()) {\n\t\t\tthrow new NotFoundException(\n\t\t\t\t\t\"atIndexLocation must be less than or equal to the size of the AccessControlEntry collection\");\n\t\t}\n\t\tAccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid, permission, granting, false, false);\n\t\tsynchronized (this.aces) {\n\t\t\tthis.aces.add(atIndexLocation, ace);\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<AccessControlEntry> getEntries() {\n\t\t// Can safely return AccessControlEntry directly, as they're immutable outside the\n\t\t// ACL package\n\t\treturn new ArrayList<>(this.aces);\n\t}\n\n\t@Override\n\tpublic Serializable getId() {\n\t\treturn this.id;\n\t}\n\n\t@Override\n\tpublic ObjectIdentity getObjectIdentity() {\n\t\treturn this.objectIdentity;\n\t}\n\n\t@Override\n\tpublic boolean isEntriesInheriting() {\n\t\treturn this.entriesInheriting;\n\t}\n\n\t/**\n\t * Delegates to the {@link PermissionGrantingStrategy}.\n\t * @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the\n\t * ACL was only loaded for a subset of SIDs\n\t * @see DefaultPermissionGrantingStrategy\n\t */\n\t@Override\n\tpublic boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)\n\t\t\tthrows NotFoundException, UnloadedSidException {\n\t\tAssert.notEmpty(permission, \"Permissions required\");\n\t\tAssert.notEmpty(sids, \"SIDs required\");\n\t\tif (!this.isSidLoaded(sids)) {\n\t\t\tthrow new UnloadedSidException(\"ACL was not loaded for one or more SID\");\n\t\t}\n\t\treturn this.permissionGrantingStrategy.isGranted(this, permission, sids, administrativeMode);\n\t}\n\n\t@Override\n\tpublic boolean isSidLoaded(@Nullable List<Sid> sids) {\n\t\t// If loadedSides is null, this indicates all SIDs were loaded\n\t\t// Also return true if the caller didn't specify a SID to find\n\t\tif ((this.loadedSids == null) || (sids == null) || sids.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// This ACL applies to a SID subset only. Iterate to check it applies.\n\t\tfor (Sid sid : sids) {\n\t\t\tboolean found = false;\n\t\t\tfor (Sid loadedSid : this.loadedSids) {\n\t\t\t\tif (sid.equals(loadedSid)) {\n\t\t\t\t\t// this SID is OK\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak; // out of loadedSids for loop\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!found) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void setEntriesInheriting(boolean entriesInheriting) {\n\t\tthis.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t\tthis.entriesInheriting = entriesInheriting;\n\t}\n\n\t@Override\n\tpublic void setOwner(Sid newOwner) {\n\t\tthis.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_OWNERSHIP);\n\t\tAssert.notNull(newOwner, \"Owner required\");\n\t\tthis.owner = newOwner;\n\t}\n\n\t@Override\n\tpublic @Nullable Sid getOwner() {\n\t\treturn this.owner;\n\t}\n\n\t@Override\n\tpublic void setParent(@Nullable Acl newParent) {\n\t\tthis.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t\tAssert.isTrue(newParent == null || !newParent.equals(this), \"Cannot be the parent of yourself\");\n\t\tthis.parentAcl = newParent;\n\t}\n\n\t@Override\n\tpublic @Nullable Acl getParentAcl() {\n\t\treturn this.parentAcl;\n\t}\n\n\t@Override\n\tpublic void updateAce(int aceIndex, Permission permission) throws NotFoundException {\n\t\tthis.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t\tverifyAceIndexExists(aceIndex);\n\t\tsynchronized (this.aces) {\n\t\t\tAccessControlEntryImpl ace = (AccessControlEntryImpl) this.aces.get(aceIndex);\n\t\t\tace.setPermission(permission);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure) {\n\t\tthis.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_AUDITING);\n\t\tverifyAceIndexExists(aceIndex);\n\t\tsynchronized (this.aces) {\n\t\t\tAccessControlEntryImpl ace = (AccessControlEntryImpl) this.aces.get(aceIndex);\n\t\t\tace.setAuditSuccess(auditSuccess);\n\t\t\tace.setAuditFailure(auditFailure);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj == this) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || !(obj instanceof AclImpl)) {\n\t\t\treturn false;\n\t\t}\n\t\tAclImpl other = (AclImpl) obj;\n\t\tboolean result = true;\n\t\tresult = result && this.aces.equals(other.aces);\n\t\tresult = result && ObjectUtils.nullSafeEquals(this.parentAcl, other.parentAcl);\n\t\tresult = result && ObjectUtils.nullSafeEquals(this.objectIdentity, other.objectIdentity);\n\t\tresult = result && ObjectUtils.nullSafeEquals(this.id, other.id);\n\t\tresult = result && ObjectUtils.nullSafeEquals(this.owner, other.owner);\n\t\tresult = result && this.entriesInheriting == other.entriesInheriting;\n\t\tresult = result && ObjectUtils.nullSafeEquals(this.loadedSids, other.loadedSids);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = (this.parentAcl != null) ? this.parentAcl.hashCode() : 0;\n\t\tresult = 31 * result + this.aclAuthorizationStrategy.hashCode();\n\t\tresult = 31 * result\n\t\t\t\t+ ((this.permissionGrantingStrategy != null) ? this.permissionGrantingStrategy.hashCode() : 0);\n\t\tresult = 31 * result + ((this.aces != null) ? this.aces.hashCode() : 0);\n\t\tresult = 31 * result + this.objectIdentity.hashCode();\n\t\tresult = 31 * result + this.id.hashCode();\n\t\tresult = 31 * result + ((this.owner != null) ? this.owner.hashCode() : 0);\n\t\tresult = 31 * result + ((this.loadedSids != null) ? this.loadedSids.hashCode() : 0);\n\t\tresult = 31 * result + (this.entriesInheriting ? 1 : 0);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"AclImpl[\");\n\t\tsb.append(\"id: \").append(this.id).append(\"; \");\n\t\tsb.append(\"objectIdentity: \").append(this.objectIdentity).append(\"; \");\n\t\tsb.append(\"owner: \").append(this.owner).append(\"; \");\n\t\tint count = 0;\n\t\tfor (AccessControlEntry ace : this.aces) {\n\t\t\tcount++;\n\t\t\tif (count == 1) {\n\t\t\t\tsb.append(\"\\n\");\n\t\t\t}\n\t\t\tsb.append(ace).append(\"\\n\");\n\t\t}\n\t\tif (count == 0) {\n\t\t\tsb.append(\"no ACEs; \");\n\t\t}\n\t\tsb.append(\"inheriting: \").append(this.entriesInheriting).append(\"; \");\n\t\tsb.append(\"parent: \").append((this.parentAcl == null) ? \"Null\" : this.parentAcl.getObjectIdentity().toString());\n\t\tsb.append(\"; \");\n\t\tsb.append(\"aclAuthorizationStrategy: \").append(this.aclAuthorizationStrategy).append(\"; \");\n\t\tsb.append(\"permissionGrantingStrategy: \").append(this.permissionGrantingStrategy);\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/AuditLogger.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.AccessControlEntry;\n\n/**\n * Used by <code>AclImpl</code> to log audit events.\n *\n * @author Ben Alex\n */\npublic interface AuditLogger {\n\n\tvoid logIfNeeded(boolean granted, AccessControlEntry ace);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/BasePermission.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.Permission;\n\n/**\n * A set of standard permissions.\n *\n * <p>\n * You may subclass this class to add additional permissions, or use this class as a guide\n * for creating your own permission classes.\n * </p>\n *\n * @author Ben Alex\n */\npublic class BasePermission extends AbstractPermission {\n\n\tpublic static final Permission READ = new BasePermission(1 << 0, 'R'); // 1\n\n\tpublic static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2\n\n\tpublic static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4\n\n\tpublic static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8\n\n\tpublic static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16\n\n\tprotected BasePermission(int mask) {\n\t\tsuper(mask);\n\t}\n\n\tprotected BasePermission(int mask, char code) {\n\t\tsuper(mask, code);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/ConsoleAuditLogger.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.AuditableAccessControlEntry;\nimport org.springframework.util.Assert;\n\n/**\n * A basic implementation of {@link AuditLogger}.\n *\n * @author Ben Alex\n */\npublic class ConsoleAuditLogger implements AuditLogger {\n\n\t@Override\n\tpublic void logIfNeeded(boolean granted, AccessControlEntry ace) {\n\t\tAssert.notNull(ace, \"AccessControlEntry required\");\n\t\tif (ace instanceof AuditableAccessControlEntry) {\n\t\t\tAuditableAccessControlEntry auditableAce = (AuditableAccessControlEntry) ace;\n\t\t\tif (granted && auditableAce.isAuditSuccess()) {\n\t\t\t\tSystem.out.println(\"GRANTED due to ACE: \" + ace);\n\t\t\t}\n\t\t\telse if (!granted && auditableAce.isAuditFailure()) {\n\t\t\t\tSystem.out.println(\"DENIED due to ACE: \" + ace);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/CumulativePermission.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.Permission;\n\n/**\n * Represents a <code>Permission</code> that is constructed at runtime from other\n * permissions.\n *\n * <p>\n * Methods return <code>this</code>, in order to facilitate method chaining.\n * </p>\n *\n * @author Ben Alex\n */\npublic class CumulativePermission extends AbstractPermission {\n\n\tprivate String pattern = THIRTY_TWO_RESERVED_OFF;\n\n\tpublic CumulativePermission() {\n\t\tsuper(0, ' ');\n\t}\n\n\tpublic CumulativePermission clear(Permission permission) {\n\t\tthis.mask &= ~permission.getMask();\n\t\tthis.pattern = AclFormattingUtils.demergePatterns(this.pattern, permission.getPattern());\n\t\treturn this;\n\t}\n\n\tpublic CumulativePermission clear() {\n\t\tthis.mask = 0;\n\t\tthis.pattern = THIRTY_TWO_RESERVED_OFF;\n\t\treturn this;\n\t}\n\n\tpublic CumulativePermission set(Permission permission) {\n\t\tthis.mask |= permission.getMask();\n\t\tthis.pattern = AclFormattingUtils.mergePatterns(this.pattern, permission.getPattern());\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic String getPattern() {\n\t\treturn this.pattern;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/DefaultPermissionFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.util.Assert;\n\n/**\n * Default implementation of {@link PermissionFactory}.\n * <p>\n * Used as a strategy by classes which wish to map integer masks and permission names to\n * <tt>Permission</tt> instances for use with the ACL implementation.\n * <p>\n * Maintains a registry of permission names and masks to <tt>Permission</tt> instances.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @since 2.0.3\n */\npublic class DefaultPermissionFactory implements PermissionFactory {\n\n\tprivate final Map<Integer, Permission> registeredPermissionsByInteger = new HashMap<>();\n\n\tprivate final Map<String, Permission> registeredPermissionsByName = new HashMap<>();\n\n\t/**\n\t * Registers the <tt>Permission</tt> fields from the <tt>BasePermission</tt> class.\n\t */\n\tpublic DefaultPermissionFactory() {\n\t\tregisterPublicPermissions(BasePermission.class);\n\t}\n\n\t/**\n\t * Registers the <tt>Permission</tt> fields from the supplied class.\n\t */\n\tpublic DefaultPermissionFactory(Class<? extends Permission> permissionClass) {\n\t\tregisterPublicPermissions(permissionClass);\n\t}\n\n\t/**\n\t * Registers a map of named <tt>Permission</tt> instances.\n\t * @param namedPermissions the map of <tt>Permission</tt>s, keyed by name.\n\t */\n\tpublic DefaultPermissionFactory(Map<String, ? extends Permission> namedPermissions) {\n\t\tfor (String name : namedPermissions.keySet()) {\n\t\t\tregisterPermission(namedPermissions.get(name), name);\n\t\t}\n\t}\n\n\t/**\n\t * Registers the public static fields of type {@link Permission} for a give class.\n\t * <p>\n\t * These permissions will be registered under the name of the field. See\n\t * {@link BasePermission} for an example.\n\t * @param clazz a {@link Permission} class with public static fields to register\n\t */\n\tprotected void registerPublicPermissions(Class<? extends Permission> clazz) {\n\t\tAssert.notNull(clazz, \"Class required\");\n\t\tField[] fields = clazz.getFields();\n\t\tfor (Field field : fields) {\n\t\t\ttry {\n\t\t\t\tObject fieldValue = field.get(null);\n\t\t\t\tif (Permission.class.isAssignableFrom(fieldValue.getClass())) {\n\t\t\t\t\t// Found a Permission static field\n\t\t\t\t\tPermission perm = (Permission) fieldValue;\n\t\t\t\t\tString permissionName = field.getName();\n\t\t\t\t\tregisterPermission(perm, permissionName);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void registerPermission(Permission perm, String permissionName) {\n\t\tAssert.notNull(perm, \"Permission required\");\n\t\tAssert.hasText(permissionName, \"Permission name required\");\n\t\tInteger mask = perm.getMask();\n\n\t\t// Ensure no existing Permission uses this integer or code\n\t\tAssert.isTrue(!this.registeredPermissionsByInteger.containsKey(mask),\n\t\t\t\t() -> \"An existing Permission already provides mask \" + mask);\n\t\tAssert.isTrue(!this.registeredPermissionsByName.containsKey(permissionName),\n\t\t\t\t() -> \"An existing Permission already provides name '\" + permissionName + \"'\");\n\n\t\t// Register the new Permission\n\t\tthis.registeredPermissionsByInteger.put(mask, perm);\n\t\tthis.registeredPermissionsByName.put(permissionName, perm);\n\t}\n\n\t@Override\n\tpublic Permission buildFromMask(int mask) {\n\t\tif (this.registeredPermissionsByInteger.containsKey(mask)) {\n\t\t\t// The requested mask has an exact match against a statically-defined\n\t\t\t// Permission, so return it\n\t\t\treturn this.registeredPermissionsByInteger.get(mask);\n\t\t}\n\n\t\t// To get this far, we have to use a CumulativePermission\n\t\tCumulativePermission permission = new CumulativePermission();\n\t\tfor (int i = 0; i < 32; i++) {\n\t\t\tint permissionToCheck = 1 << i;\n\t\t\tif ((mask & permissionToCheck) == permissionToCheck) {\n\t\t\t\tPermission p = this.registeredPermissionsByInteger.get(permissionToCheck);\n\t\t\t\tAssert.state(p != null,\n\t\t\t\t\t\t() -> \"Mask '\" + permissionToCheck + \"' does not have a corresponding static Permission\");\n\t\t\t\tpermission.set(p);\n\t\t\t}\n\t\t}\n\t\treturn permission;\n\t}\n\n\t@Override\n\tpublic Permission buildFromName(String name) {\n\t\tPermission p = this.registeredPermissionsByName.get(name);\n\t\tAssert.notNull(p, \"Unknown permission '\" + name + \"'\");\n\t\treturn p;\n\t}\n\n\t@Override\n\tpublic List<Permission> buildFromNames(List<String> names) {\n\t\tif ((names == null) || names.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<Permission> permissions = new ArrayList<>(names.size());\n\t\tfor (String name : names) {\n\t\t\tpermissions.add(buildFromName(name));\n\t\t}\n\t\treturn permissions;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/DefaultPermissionGrantingStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.util.List;\n\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.PermissionGrantingStrategy;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.util.Assert;\n\npublic class DefaultPermissionGrantingStrategy implements PermissionGrantingStrategy {\n\n\tprivate final transient AuditLogger auditLogger;\n\n\t/**\n\t * Creates an instance with the logger which will be used to record granting and\n\t * denial of requested permissions.\n\t */\n\tpublic DefaultPermissionGrantingStrategy(AuditLogger auditLogger) {\n\t\tAssert.notNull(auditLogger, \"auditLogger cannot be null\");\n\t\tthis.auditLogger = auditLogger;\n\t}\n\n\t/**\n\t * Determines authorization. The order of the <code>permission</code> and\n\t * <code>sid</code> arguments is <em>extremely important</em>! The method will iterate\n\t * through each of the <code>permission</code>s in the order specified. For each\n\t * iteration, all of the <code>sid</code>s will be considered, again in the order they\n\t * are presented. A search will then be performed for the first\n\t * {@link AccessControlEntry} object that directly matches that\n\t * <code>permission:sid</code> combination. When the <em>first full match</em> is\n\t * found (ie an ACE that has the SID currently being searched for and the exact\n\t * permission bit mask being search for), the grant or deny flag for that ACE will\n\t * prevail. If the ACE specifies to grant access, the method will return\n\t * <code>true</code>. If the ACE specifies to deny access, the loop will stop and the\n\t * next <code>permission</code> iteration will be performed. If each permission\n\t * indicates to deny access, the first deny ACE found will be considered the reason\n\t * for the failure (as it was the first match found, and is therefore the one most\n\t * logically requiring changes - although not always). If absolutely no matching ACE\n\t * was found at all for any permission, the parent ACL will be tried (provided that\n\t * there is a parent and {@link Acl#isEntriesInheriting()} is <code>true</code>. The\n\t * parent ACL will also scan its parent and so on. If ultimately no matching ACE is\n\t * found, a <code>NotFoundException</code> will be thrown and the caller will need to\n\t * decide how to handle the permission check. Similarly, if any of the SID arguments\n\t * presented to the method were not loaded by the ACL,\n\t * <code>UnloadedSidException</code> will be thrown.\n\t * @param permission the exact permissions to scan for (order is important)\n\t * @param sids the exact SIDs to scan for (order is important)\n\t * @param administrativeMode if <code>true</code> denotes the query is for\n\t * administrative purposes and no auditing will be undertaken\n\t * @return <code>true</code> if one of the permissions has been granted,\n\t * <code>false</code> if one of the permissions has been specifically revoked\n\t * @throws NotFoundException if an exact ACE for one of the permission bit masks and\n\t * SID combination could not be found\n\t */\n\t@Override\n\tpublic boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids, boolean administrativeMode)\n\t\t\tthrows NotFoundException {\n\t\tList<AccessControlEntry> aces = acl.getEntries();\n\t\tAccessControlEntry firstRejection = null;\n\t\tfor (Permission p : permission) {\n\t\t\tfor (Sid sid : sids) {\n\t\t\t\t// Attempt to find exact match for this permission mask and SID\n\t\t\t\tboolean scanNextSid = true;\n\t\t\t\tfor (AccessControlEntry ace : aces) {\n\t\t\t\t\tif (isGranted(ace, p) && ace.getSid().equals(sid)) {\n\t\t\t\t\t\t// Found a matching ACE, so its authorization decision will\n\t\t\t\t\t\t// prevail\n\t\t\t\t\t\tif (ace.isGranting()) {\n\t\t\t\t\t\t\t// Success\n\t\t\t\t\t\t\tif (!administrativeMode) {\n\t\t\t\t\t\t\t\tthis.auditLogger.logIfNeeded(true, ace);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Failure for this permission, so stop search\n\t\t\t\t\t\t// We will see if they have a different permission\n\t\t\t\t\t\t// (this permission is 100% rejected for this SID)\n\t\t\t\t\t\tif (firstRejection == null) {\n\t\t\t\t\t\t\t// Store first rejection for auditing reasons\n\t\t\t\t\t\t\tfirstRejection = ace;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tscanNextSid = false; // helps break the loop\n\n\t\t\t\t\t\tbreak; // exit aces loop\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!scanNextSid) {\n\t\t\t\t\tbreak; // exit SID for loop (now try next permission)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (firstRejection != null) {\n\t\t\t// We found an ACE to reject the request at this point, as no\n\t\t\t// other ACEs were found that granted a different permission\n\t\t\tif (!administrativeMode) {\n\t\t\t\tthis.auditLogger.logIfNeeded(false, firstRejection);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// No matches have been found so far\n\t\tif (acl.isEntriesInheriting() && (acl.getParentAcl() != null)) {\n\t\t\t// We have a parent, so let them try to find a matching ACE\n\t\t\treturn acl.getParentAcl().isGranted(permission, sids, false);\n\t\t}\n\n\t\t// We either have no parent, or we're the uppermost parent\n\t\tthrow new NotFoundException(\"Unable to locate a matching ACE for passed permissions and SIDs\");\n\t}\n\n\t/**\n\t * Compares an ACE Permission to the given Permission. By default, we compare the\n\t * Permission masks for exact match. Subclasses of this strategy can override this\n\t * behavior and implement more sophisticated comparisons, e.g. a bitwise comparison\n\t * for ACEs that grant access. <pre>{@code\n\t * if (ace.isGranting() && p.getMask() != 0) {\n\t *    return (ace.getPermission().getMask() & p.getMask()) != 0;\n\t * } else {\n\t *    return ace.getPermission().getMask() == p.getMask();\n\t * }\n\t * }</pre>\n\t * @param ace the ACE from the Acl holding the mask.\n\t * @param p the Permission we are checking against.\n\t * @return true, if the respective masks are considered to be equal.\n\t */\n\tprotected boolean isGranted(AccessControlEntry ace, Permission p) {\n\t\treturn ace.getPermission().getMask() == p.getMask();\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/GrantedAuthoritySid.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Represents a <code>GrantedAuthority</code> as a <code>Sid</code>.\n * <p>\n * This is a basic implementation that simply uses the <code>String</code>-based principal\n * for <code>Sid</code> comparison. More complex principal objects may wish to provide an\n * alternative <code>Sid</code> implementation that uses some other identifier.\n * </p>\n *\n * @author Ben Alex\n */\npublic class GrantedAuthoritySid implements Sid {\n\n\tprivate final String grantedAuthority;\n\n\tpublic GrantedAuthoritySid(String grantedAuthority) {\n\t\tAssert.hasText(grantedAuthority, \"GrantedAuthority required\");\n\t\tthis.grantedAuthority = grantedAuthority;\n\t}\n\n\tpublic GrantedAuthoritySid(GrantedAuthority grantedAuthority) {\n\t\tAssert.notNull(grantedAuthority, \"GrantedAuthority required\");\n\t\tAssert.notNull(grantedAuthority.getAuthority(),\n\t\t\t\t\"This Sid is only compatible with GrantedAuthority that provide a non-null getAuthority()\");\n\t\tthis.grantedAuthority = grantedAuthority.getAuthority();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object object) {\n\t\tif ((object == null) || !(object instanceof GrantedAuthoritySid)) {\n\t\t\treturn false;\n\t\t}\n\t\t// Delegate to getGrantedAuthority() to perform actual comparison (both should be\n\t\t// identical)\n\t\treturn ((GrantedAuthoritySid) object).getGrantedAuthority().equals(this.getGrantedAuthority());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.getGrantedAuthority().hashCode();\n\t}\n\n\tpublic String getGrantedAuthority() {\n\t\treturn this.grantedAuthority;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"GrantedAuthoritySid[\" + this.grantedAuthority + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/IdentityUnavailableException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\n/**\n * Thrown if an ACL identity could not be extracted from an object.\n *\n * @author Ben Alex\n */\npublic class IdentityUnavailableException extends RuntimeException {\n\n\t/**\n\t * Constructs an <code>IdentityUnavailableException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic IdentityUnavailableException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>IdentityUnavailableException</code> with the specified message\n\t * and root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic IdentityUnavailableException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/ObjectIdentityImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\n\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Simple implementation of {@link ObjectIdentity}.\n * <p>\n * Uses <code>String</code>s to store the identity of the domain object instance. Also\n * offers a constructor that uses reflection to build the identity information.\n *\n * @author Ben Alex\n */\npublic class ObjectIdentityImpl implements ObjectIdentity {\n\n\tprivate final String type;\n\n\tprivate Serializable identifier;\n\n\tpublic ObjectIdentityImpl(String type, Serializable identifier) {\n\t\tAssert.hasText(type, \"Type required\");\n\t\tAssert.notNull(identifier, \"identifier required\");\n\t\tthis.identifier = identifier;\n\t\tthis.type = type;\n\t}\n\n\t/**\n\t * Constructor which uses the name of the supplied class as the <tt>type</tt>\n\t * property.\n\t */\n\tpublic ObjectIdentityImpl(Class<?> javaType, Serializable identifier) {\n\t\tAssert.notNull(javaType, \"Java Type required\");\n\t\tAssert.notNull(identifier, \"identifier required\");\n\t\tthis.type = javaType.getName();\n\t\tthis.identifier = identifier;\n\t}\n\n\t/**\n\t * Creates the <code>ObjectIdentityImpl</code> based on the passed object instance.\n\t * The passed object must provide a <code>getId()</code> method, otherwise an\n\t * exception will be thrown.\n\t * <p>\n\t * The class name of the object passed will be considered the {@link #type}, so if\n\t * more control is required, a different constructor should be used.\n\t * @param object the domain object instance to create an identity for.\n\t * @throws IdentityUnavailableException if identity could not be extracted\n\t */\n\tpublic ObjectIdentityImpl(Object object) throws IdentityUnavailableException {\n\t\tAssert.notNull(object, \"object cannot be null\");\n\t\tClass<?> typeClass = ClassUtils.getUserClass(object.getClass());\n\t\tthis.type = typeClass.getName();\n\t\tObject result = invokeGetIdMethod(object, typeClass);\n\t\tAssert.notNull(result, \"getId() is required to return a non-null value\");\n\t\tAssert.isInstanceOf(Serializable.class, result, \"Getter must provide a return value of type Serializable\");\n\t\tthis.identifier = (Serializable) result;\n\t}\n\n\tprivate Object invokeGetIdMethod(Object object, Class<?> typeClass) {\n\t\ttry {\n\t\t\tMethod method = typeClass.getMethod(\"getId\", new Class[] {});\n\t\t\treturn method.invoke(object);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IdentityUnavailableException(\"Could not extract identity from object \" + object, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Important so caching operates properly.\n\t * <p>\n\t * Considers an object of the same class equal if it has the same\n\t * <code>classname</code> and <code>id</code> properties.\n\t * <p>\n\t * Numeric identities (Integer and Long values) are considered equal if they are\n\t * numerically equal. Other serializable types are evaluated using a simple equality.\n\t * @param obj object to compare\n\t * @return <code>true</code> if the presented object matches this object\n\t */\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj == null || !(obj instanceof ObjectIdentityImpl)) {\n\t\t\treturn false;\n\t\t}\n\t\tObjectIdentityImpl other = (ObjectIdentityImpl) obj;\n\t\tif (this.identifier instanceof Number && other.identifier instanceof Number) {\n\t\t\t// Integers and Longs with same value should be considered equal\n\t\t\tif (((Number) this.identifier).longValue() != ((Number) other.identifier).longValue()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// Use plain equality for other serializable types\n\t\t\tif (!this.identifier.equals(other.identifier)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn this.type.equals(other.type);\n\t}\n\n\t@Override\n\tpublic Serializable getIdentifier() {\n\t\treturn this.identifier;\n\t}\n\n\t@Override\n\tpublic String getType() {\n\t\treturn this.type;\n\t}\n\n\t/**\n\t * Important so caching operates properly.\n\t * @return the hash\n\t */\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.type.hashCode();\n\t\tresult = 31 * result + this.identifier.hashCode();\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(this.getClass().getName()).append(\"[\");\n\t\tsb.append(\"Type: \").append(this.type);\n\t\tsb.append(\"; Identifier: \").append(this.identifier).append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/ObjectIdentityRetrievalStrategyImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.io.Serializable;\n\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityGenerator;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\n\n/**\n * Basic implementation of {@link ObjectIdentityRetrievalStrategy} and\n * <tt>ObjectIdentityGenerator</tt> that uses the constructors of\n * {@link ObjectIdentityImpl} to create the {@link ObjectIdentity}.\n *\n * @author Ben Alex\n */\npublic class ObjectIdentityRetrievalStrategyImpl implements ObjectIdentityRetrievalStrategy, ObjectIdentityGenerator {\n\n\t@Override\n\tpublic ObjectIdentity getObjectIdentity(Object domainObject) {\n\t\treturn new ObjectIdentityImpl(domainObject);\n\t}\n\n\t@Override\n\tpublic ObjectIdentity createObjectIdentity(Serializable id, String type) {\n\t\treturn new ObjectIdentityImpl(type, id);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/PermissionFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.util.List;\n\nimport org.springframework.security.acls.model.Permission;\n\n/**\n * Provides a simple mechanism to retrieve {@link Permission} instances from integer\n * masks.\n *\n * @author Ben Alex\n * @since 2.0.3\n */\npublic interface PermissionFactory {\n\n\t/**\n\t * Dynamically creates a <code>CumulativePermission</code> or\n\t * <code>BasePermission</code> representing the active bits in the passed mask.\n\t * @param mask to build\n\t * @return a Permission representing the requested object\n\t */\n\tPermission buildFromMask(int mask);\n\n\tPermission buildFromName(String name);\n\n\tList<Permission> buildFromNames(List<String> names);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/PrincipalSid.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Represents an <code>Authentication.getPrincipal()</code> as a <code>Sid</code>.\n * <p>\n * This is a basic implementation that simply uses the <code>String</code>-based principal\n * for <code>Sid</code> comparison. More complex principal objects may wish to provide an\n * alternative <code>Sid</code> implementation that uses some other identifier.\n * </p>\n *\n * @author Ben Alex\n */\npublic class PrincipalSid implements Sid {\n\n\tprivate final String principal;\n\n\tpublic PrincipalSid(String principal) {\n\t\tAssert.hasText(principal, \"Principal required\");\n\t\tthis.principal = principal;\n\t}\n\n\tpublic PrincipalSid(Authentication authentication) {\n\t\tAssert.notNull(authentication, \"Authentication required\");\n\t\tAssert.notNull(authentication.getPrincipal(), \"Principal required\");\n\t\tthis.principal = authentication.getName();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object object) {\n\t\tif ((object == null) || !(object instanceof PrincipalSid)) {\n\t\t\treturn false;\n\t\t}\n\t\t// Delegate to getPrincipal() to perform actual comparison (both should be\n\t\t// identical)\n\t\treturn ((PrincipalSid) object).getPrincipal().equals(this.getPrincipal());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.getPrincipal().hashCode();\n\t}\n\n\tpublic String getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"PrincipalSid[\" + this.principal + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/SidRetrievalStrategyImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Basic implementation of {@link SidRetrievalStrategy} that creates a {@link Sid} for the\n * principal, as well as every granted authority the principal holds. Can optionally have\n * a <tt>RoleHierarchy</tt> injected in order to determine the extended list of\n * authorities that the principal is assigned.\n * <p>\n * The returned array will always contain the {@link PrincipalSid} before any\n * {@link GrantedAuthoritySid} elements.\n *\n * @author Ben Alex\n */\npublic class SidRetrievalStrategyImpl implements SidRetrievalStrategy {\n\n\tprivate RoleHierarchy roleHierarchy = new NullRoleHierarchy();\n\n\tpublic SidRetrievalStrategyImpl() {\n\t}\n\n\tpublic SidRetrievalStrategyImpl(RoleHierarchy roleHierarchy) {\n\t\tAssert.notNull(roleHierarchy, \"RoleHierarchy must not be null\");\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n\t@Override\n\tpublic List<Sid> getSids(Authentication authentication) {\n\t\tCollection<? extends GrantedAuthority> authorities = this.roleHierarchy\n\t\t\t.getReachableGrantedAuthorities(authentication.getAuthorities());\n\t\tList<Sid> sids = new ArrayList<>(authorities.size() + 1);\n\t\tsids.add(new PrincipalSid(authentication));\n\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\tsids.add(new GrantedAuthoritySid(authority));\n\t\t}\n\t\treturn sids;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/SpringCacheBasedAclCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.security.acls.model.AclCache;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.PermissionGrantingStrategy;\nimport org.springframework.security.util.FieldUtils;\nimport org.springframework.util.Assert;\n\n/**\n * Simple implementation of {@link org.springframework.security.acls.model.AclCache} that\n * delegates to {@link Cache} implementation.\n * <p>\n * Designed to handle the transient fields in\n * {@link org.springframework.security.acls.domain.AclImpl}. Note that this implementation\n * assumes all {@link org.springframework.security.acls.domain.AclImpl} instances share\n * the same {@link org.springframework.security.acls.model.PermissionGrantingStrategy} and\n * {@link org.springframework.security.acls.domain.AclAuthorizationStrategy} instances.\n *\n * @author Marten Deinum\n * @since 3.2\n */\npublic class SpringCacheBasedAclCache implements AclCache {\n\n\tprivate final Cache cache;\n\n\tprivate PermissionGrantingStrategy permissionGrantingStrategy;\n\n\tprivate AclAuthorizationStrategy aclAuthorizationStrategy;\n\n\tpublic SpringCacheBasedAclCache(Cache cache, PermissionGrantingStrategy permissionGrantingStrategy,\n\t\t\tAclAuthorizationStrategy aclAuthorizationStrategy) {\n\t\tAssert.notNull(cache, \"Cache required\");\n\t\tAssert.notNull(permissionGrantingStrategy, \"PermissionGrantingStrategy required\");\n\t\tAssert.notNull(aclAuthorizationStrategy, \"AclAuthorizationStrategy required\");\n\t\tthis.cache = cache;\n\t\tthis.permissionGrantingStrategy = permissionGrantingStrategy;\n\t\tthis.aclAuthorizationStrategy = aclAuthorizationStrategy;\n\t}\n\n\t@Override\n\tpublic void evictFromCache(Serializable pk) {\n\t\tAssert.notNull(pk, \"Primary key (identifier) required\");\n\t\tMutableAcl acl = getFromCache(pk);\n\t\tif (acl != null) {\n\t\t\tthis.cache.evict(acl.getId());\n\t\t\tthis.cache.evict(acl.getObjectIdentity());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void evictFromCache(ObjectIdentity objectIdentity) {\n\t\tAssert.notNull(objectIdentity, \"ObjectIdentity required\");\n\t\tMutableAcl acl = getFromCache(objectIdentity);\n\t\tif (acl != null) {\n\t\t\tthis.cache.evict(acl.getId());\n\t\t\tthis.cache.evict(acl.getObjectIdentity());\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable MutableAcl getFromCache(ObjectIdentity objectIdentity) {\n\t\tAssert.notNull(objectIdentity, \"ObjectIdentity required\");\n\t\treturn getFromCache((Object) objectIdentity);\n\t}\n\n\t@Override\n\tpublic @Nullable MutableAcl getFromCache(Serializable pk) {\n\t\tAssert.notNull(pk, \"Primary key (identifier) required\");\n\t\treturn getFromCache((Object) pk);\n\t}\n\n\t@Override\n\tpublic void putInCache(MutableAcl acl) {\n\t\tAssert.notNull(acl, \"Acl required\");\n\t\tAssert.notNull(acl.getObjectIdentity(), \"ObjectIdentity required\");\n\t\tAssert.notNull(acl.getId(), \"ID required\");\n\t\tif ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {\n\t\t\tputInCache((MutableAcl) acl.getParentAcl());\n\t\t}\n\t\tthis.cache.put(acl.getObjectIdentity(), acl);\n\t\tthis.cache.put(acl.getId(), acl);\n\t}\n\n\tprivate @Nullable MutableAcl getFromCache(Object key) {\n\t\tCache.ValueWrapper element = this.cache.get(key);\n\t\tif (element == null) {\n\t\t\treturn null;\n\t\t}\n\t\tObject value = element.get();\n\t\tif (value == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn initializeTransientFields((MutableAcl) value);\n\t}\n\n\tprivate MutableAcl initializeTransientFields(MutableAcl value) {\n\t\tif (value instanceof AclImpl) {\n\t\t\tFieldUtils.setProtectedFieldValue(\"aclAuthorizationStrategy\", value, this.aclAuthorizationStrategy);\n\t\t\tFieldUtils.setProtectedFieldValue(\"permissionGrantingStrategy\", value, this.permissionGrantingStrategy);\n\t\t}\n\t\tif (value.getParentAcl() != null) {\n\t\t\tinitializeTransientFields((MutableAcl) value.getParentAcl());\n\t\t}\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic void clearCache() {\n\t\tthis.cache.clear();\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/domain/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Basic implementation of access control lists (ACLs) interfaces.\n */\n@NullMarked\npackage org.springframework.security.acls.domain;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/jdbc/AclClassIdUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.io.Serializable;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.UUID;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.ConversionFailedException;\nimport org.springframework.core.convert.ConversionService;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.convert.support.GenericConversionService;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.util.Assert;\n\n/**\n * Utility class for helping convert database representations of\n * {@link ObjectIdentity#getIdentifier()} into the correct Java type as specified by\n * <code>acl_class.class_id_type</code>.\n *\n * @author paulwheeler\n */\nclass AclClassIdUtils {\n\n\tprivate static final String DEFAULT_CLASS_ID_TYPE_COLUMN_NAME = \"class_id_type\";\n\n\tprivate static final Log log = LogFactory.getLog(AclClassIdUtils.class);\n\n\tprivate ConversionService conversionService;\n\n\tAclClassIdUtils() {\n\t\tGenericConversionService genericConversionService = new GenericConversionService();\n\t\tgenericConversionService.addConverter(String.class, Long.class, new StringToLongConverter());\n\t\tgenericConversionService.addConverter(String.class, UUID.class, new StringToUUIDConverter());\n\t\tthis.conversionService = genericConversionService;\n\t}\n\n\tAclClassIdUtils(ConversionService conversionService) {\n\t\tAssert.notNull(conversionService, \"conversionService must not be null\");\n\t\tthis.conversionService = conversionService;\n\t}\n\n\t/**\n\t * Converts the raw type from the database into the right Java type. For most\n\t * applications the 'raw type' will be Long, for some applications it could be String.\n\t * @param identifier The identifier from the database\n\t * @param resultSet Result set of the query\n\t * @return The identifier in the appropriate target Java type. Typically Long or UUID.\n\t * @throws SQLException\n\t */\n\t@Nullable Serializable identifierFrom(Serializable identifier, ResultSet resultSet) throws SQLException {\n\t\tClass<? extends Serializable> classIdType = classIdTypeFrom(resultSet);\n\t\tif (isString(identifier) && classIdType != null && canConvertFromStringTo(classIdType)) {\n\t\t\treturn convertFromStringTo((String) identifier, classIdType);\n\t\t}\n\t\t// Assume it should be a Long type\n\t\treturn convertToLong(identifier);\n\t}\n\n\tprivate boolean hasValidClassIdType(ResultSet resultSet) {\n\t\ttry {\n\t\t\treturn classIdTypeFrom(resultSet) != null;\n\t\t}\n\t\tcatch (SQLException ex) {\n\t\t\tlog.debug(\"Unable to obtain the class id type\", ex);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate @Nullable Class<? extends Serializable> classIdTypeFrom(ResultSet resultSet) throws SQLException {\n\t\ttry {\n\t\t\treturn classIdTypeFrom(resultSet.getString(DEFAULT_CLASS_ID_TYPE_COLUMN_NAME));\n\t\t}\n\t\tcatch (SQLException ex) {\n\t\t\tlog.debug(\"Unable to obtain the class id type\", ex);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate @Nullable Class<? extends Serializable> classIdTypeFrom(String className) {\n\t\tif (className == null) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\treturn Class.forName(className).asSubclass(Serializable.class);\n\t\t}\n\t\tcatch (ClassNotFoundException ex) {\n\t\t\tlog.debug(\"Unable to find class id type on classpath\", ex);\n\t\t\treturn null;\n\t\t}\n\t\tcatch (ClassCastException ex) {\n\t\t\tlog.debug(\"Class id type is not a Serializable type\", ex);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate <T> boolean canConvertFromStringTo(Class<T> targetType) {\n\t\treturn this.conversionService.canConvert(String.class, targetType);\n\t}\n\n\tprivate <T extends Serializable> @Nullable T convertFromStringTo(String identifier, Class<T> targetType) {\n\t\treturn this.conversionService.convert(identifier, targetType);\n\t}\n\n\t/**\n\t * Converts to a {@link Long}, attempting to use the {@link ConversionService} if\n\t * available.\n\t * @param identifier The identifier\n\t * @return Long version of the identifier\n\t * @throws NumberFormatException if the string cannot be parsed to a long.\n\t * @throws org.springframework.core.convert.ConversionException if a conversion\n\t * exception occurred\n\t * @throws IllegalArgumentException if targetType is null\n\t */\n\tprivate @Nullable Long convertToLong(Serializable identifier) {\n\t\tif (this.conversionService.canConvert(identifier.getClass(), Long.class)) {\n\t\t\treturn this.conversionService.convert(identifier, Long.class);\n\t\t}\n\t\treturn Long.valueOf(identifier.toString());\n\t}\n\n\tprivate boolean isString(Serializable object) {\n\t\treturn object.getClass().isAssignableFrom(String.class);\n\t}\n\n\tvoid setConversionService(ConversionService conversionService) {\n\t\tAssert.notNull(conversionService, \"conversionService must not be null\");\n\t\tthis.conversionService = conversionService;\n\t}\n\n\tprivate static class StringToLongConverter implements Converter<String, Long> {\n\n\t\t@Override\n\t\tpublic Long convert(@Nullable String identifierAsString) {\n\t\t\tif (identifierAsString == null) {\n\t\t\t\tthrow new ConversionFailedException(TypeDescriptor.valueOf(String.class),\n\t\t\t\t\t\tTypeDescriptor.valueOf(Long.class), identifierAsString, new NullPointerException());\n\n\t\t\t}\n\t\t\treturn Long.parseLong(identifierAsString);\n\t\t}\n\n\t}\n\n\tprivate static class StringToUUIDConverter implements Converter<String, UUID> {\n\n\t\t@Override\n\t\tpublic UUID convert(@Nullable String identifierAsString) {\n\t\t\tif (identifierAsString == null) {\n\t\t\t\tthrow new ConversionFailedException(TypeDescriptor.valueOf(String.class),\n\t\t\t\t\t\tTypeDescriptor.valueOf(UUID.class), identifierAsString, new NullPointerException());\n\n\t\t\t}\n\t\t\treturn UUID.fromString(identifierAsString);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006, 2017 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Field;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.ConversionException;\nimport org.springframework.core.convert.ConversionService;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.ResultSetExtractor;\nimport org.springframework.security.acls.domain.AccessControlEntryImpl;\nimport org.springframework.security.acls.domain.AclAuthorizationStrategy;\nimport org.springframework.security.acls.domain.AclImpl;\nimport org.springframework.security.acls.domain.AuditLogger;\nimport org.springframework.security.acls.domain.DefaultPermissionFactory;\nimport org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;\nimport org.springframework.security.acls.domain.GrantedAuthoritySid;\nimport org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;\nimport org.springframework.security.acls.domain.PermissionFactory;\nimport org.springframework.security.acls.domain.PrincipalSid;\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclCache;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityGenerator;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.PermissionGrantingStrategy;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.UnloadedSidException;\nimport org.springframework.security.util.FieldUtils;\nimport org.springframework.util.Assert;\n\n/**\n * Performs lookups in a manner that is compatible with ANSI SQL.\n * <p>\n * NB: This implementation does attempt to provide reasonably optimised lookups - within\n * the constraints of a normalised database and standard ANSI SQL features. If you are\n * willing to sacrifice either of these constraints (e.g. use a particular database\n * feature such as hierarchical queries or materialized views, or reduce normalisation)\n * you are likely to achieve better performance. In such situations you will need to\n * provide your own custom <code>LookupStrategy</code>. This class does not support\n * subclassing, as it is likely to change in future releases and therefore subclassing is\n * unsupported.\n * <p>\n * There are two SQL queries executed, one in the <tt>lookupPrimaryKeys</tt> method and\n * one in <tt>lookupObjectIdentities</tt>. These are built from the same select and \"order\n * by\" clause, using a different where clause in each case. In order to use custom schema\n * or column names, each of these SQL clauses can be customized, but they must be\n * consistent with each other and with the expected result set generated by the default\n * values.\n *\n * @author Ben Alex\n */\npublic class BasicLookupStrategy implements LookupStrategy {\n\n\tprivate static final String DEFAULT_SELECT_CLAUSE_COLUMNS = \"select acl_object_identity.object_id_identity, \"\n\t\t\t+ \"acl_entry.ace_order,  \" + \"acl_object_identity.id as acl_id, \" + \"acl_object_identity.parent_object, \"\n\t\t\t+ \"acl_object_identity.entries_inheriting, \" + \"acl_entry.id as ace_id, \" + \"acl_entry.mask,  \"\n\t\t\t+ \"acl_entry.granting,  \" + \"acl_entry.audit_success, \" + \"acl_entry.audit_failure,  \"\n\t\t\t+ \"acl_sid.principal as ace_principal, \" + \"acl_sid.sid as ace_sid,  \"\n\t\t\t+ \"acli_sid.principal as acl_principal, \" + \"acli_sid.sid as acl_sid, \" + \"acl_class.class \";\n\n\tprivate static final String DEFAULT_SELECT_CLAUSE_ACL_CLASS_ID_TYPE_COLUMN = \", acl_class.class_id_type  \";\n\n\tprivate static final String DEFAULT_SELECT_CLAUSE_FROM = \"from acl_object_identity \"\n\t\t\t+ \"left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid \"\n\t\t\t+ \"left join acl_class on acl_class.id = acl_object_identity.object_id_class   \"\n\t\t\t+ \"left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity \"\n\t\t\t+ \"left join acl_sid on acl_entry.sid = acl_sid.id  \" + \"where ( \";\n\n\tpublic static final String DEFAULT_SELECT_CLAUSE = DEFAULT_SELECT_CLAUSE_COLUMNS + DEFAULT_SELECT_CLAUSE_FROM;\n\n\tpublic static final String DEFAULT_ACL_CLASS_ID_SELECT_CLAUSE = DEFAULT_SELECT_CLAUSE_COLUMNS\n\t\t\t+ DEFAULT_SELECT_CLAUSE_ACL_CLASS_ID_TYPE_COLUMN + DEFAULT_SELECT_CLAUSE_FROM;\n\n\tprivate static final String DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE = \"(acl_object_identity.id = ?)\";\n\n\tprivate static final String DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE = \"(acl_object_identity.object_id_identity = ? and acl_class.class = ?)\";\n\n\tpublic static final String DEFAULT_ORDER_BY_CLAUSE = \") order by acl_object_identity.object_id_identity\"\n\t\t\t+ \" asc, acl_entry.ace_order asc\";\n\n\tprivate final AclAuthorizationStrategy aclAuthorizationStrategy;\n\n\tprivate ObjectIdentityGenerator objectIdentityGenerator;\n\n\tprivate PermissionFactory permissionFactory = new DefaultPermissionFactory();\n\n\tprivate final AclCache aclCache;\n\n\tprivate final PermissionGrantingStrategy grantingStrategy;\n\n\tprivate final JdbcTemplate jdbcTemplate;\n\n\tprivate int batchSize = 50;\n\n\tprivate final Field fieldAces = FieldUtils.getField(AclImpl.class, \"aces\");\n\n\tprivate final Field fieldAcl = FieldUtils.getField(AccessControlEntryImpl.class, \"acl\");\n\n\t// SQL Customization fields\n\tprivate String selectClause = DEFAULT_SELECT_CLAUSE;\n\n\tprivate String lookupPrimaryKeysWhereClause = DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE;\n\n\tprivate String lookupObjectIdentitiesWhereClause = DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE;\n\n\tprivate String orderByClause = DEFAULT_ORDER_BY_CLAUSE;\n\n\tprivate AclClassIdUtils aclClassIdUtils;\n\n\t/**\n\t * Constructor accepting mandatory arguments\n\t * @param dataSource to access the database\n\t * @param aclCache the cache where fully-loaded elements can be stored\n\t * @param aclAuthorizationStrategy authorization strategy (required)\n\t */\n\tpublic BasicLookupStrategy(DataSource dataSource, AclCache aclCache,\n\t\t\tAclAuthorizationStrategy aclAuthorizationStrategy, AuditLogger auditLogger) {\n\t\tthis(dataSource, aclCache, aclAuthorizationStrategy, new DefaultPermissionGrantingStrategy(auditLogger));\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param dataSource to access the database\n\t * @param aclCache the cache where fully-loaded elements can be stored\n\t * @param aclAuthorizationStrategy authorization strategy (required)\n\t * @param grantingStrategy the PermissionGrantingStrategy\n\t */\n\tpublic BasicLookupStrategy(DataSource dataSource, AclCache aclCache,\n\t\t\tAclAuthorizationStrategy aclAuthorizationStrategy, PermissionGrantingStrategy grantingStrategy) {\n\t\tAssert.notNull(dataSource, \"DataSource required\");\n\t\tAssert.notNull(aclCache, \"AclCache required\");\n\t\tAssert.notNull(aclAuthorizationStrategy, \"AclAuthorizationStrategy required\");\n\t\tAssert.notNull(grantingStrategy, \"grantingStrategy required\");\n\t\tthis.jdbcTemplate = new JdbcTemplate(dataSource);\n\t\tthis.aclCache = aclCache;\n\t\tthis.aclAuthorizationStrategy = aclAuthorizationStrategy;\n\t\tthis.grantingStrategy = grantingStrategy;\n\t\tthis.objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();\n\t\tthis.aclClassIdUtils = new AclClassIdUtils();\n\t\tthis.fieldAces.setAccessible(true);\n\t\tthis.fieldAcl.setAccessible(true);\n\t}\n\n\tprivate String computeRepeatingSql(String repeatingSql, int requiredRepetitions) {\n\t\tAssert.isTrue(requiredRepetitions > 0, \"requiredRepetitions must be > 0\");\n\t\tString startSql = this.selectClause;\n\t\tString endSql = this.orderByClause;\n\t\tStringBuilder sqlStringBldr = new StringBuilder(\n\t\t\t\tstartSql.length() + endSql.length() + requiredRepetitions * (repeatingSql.length() + 4));\n\t\tsqlStringBldr.append(startSql);\n\t\tfor (int i = 1; i <= requiredRepetitions; i++) {\n\t\t\tsqlStringBldr.append(repeatingSql);\n\t\t\tif (i != requiredRepetitions) {\n\t\t\t\tsqlStringBldr.append(\" or \");\n\t\t\t}\n\t\t}\n\t\tsqlStringBldr.append(endSql);\n\t\treturn sqlStringBldr.toString();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate List<AccessControlEntryImpl> readAces(AclImpl acl) {\n\t\ttry {\n\t\t\treturn (List<AccessControlEntryImpl>) this.fieldAces.get(acl);\n\t\t}\n\t\tcatch (IllegalAccessException ex) {\n\t\t\tthrow new IllegalStateException(\"Could not obtain AclImpl.aces field\", ex);\n\t\t}\n\t}\n\n\tprivate void setAclOnAce(AccessControlEntryImpl ace, AclImpl acl) {\n\t\ttry {\n\t\t\tthis.fieldAcl.set(ace, acl);\n\t\t}\n\t\tcatch (IllegalAccessException ex) {\n\t\t\tthrow new IllegalStateException(\"Could not or set AclImpl on AccessControlEntryImpl fields\", ex);\n\t\t}\n\t}\n\n\tprivate void setAces(AclImpl acl, List<AccessControlEntryImpl> aces) {\n\t\ttry {\n\t\t\tthis.fieldAces.set(acl, aces);\n\t\t}\n\t\tcatch (IllegalAccessException ex) {\n\t\t\tthrow new IllegalStateException(\"Could not set AclImpl entries\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Locates the primary key IDs specified in \"findNow\", adding AclImpl instances with\n\t * StubAclParents to the \"acls\" Map.\n\t * @param acls the AclImpls (with StubAclParents)\n\t * @param findNow Long-based primary keys to retrieve\n\t * @param sids\n\t */\n\tprivate void lookupPrimaryKeys(final Map<Serializable, Acl> acls, final Set<Long> findNow,\n\t\t\tfinal @Nullable List<Sid> sids) {\n\t\tAssert.notNull(acls, \"ACLs are required\");\n\t\tAssert.notEmpty(findNow, \"Items to find now required\");\n\t\tString sql = computeRepeatingSql(this.lookupPrimaryKeysWhereClause, findNow.size());\n\t\tSet<Long> parentsToLookup = this.jdbcTemplate.query(sql, (ps) -> setKeys(ps, findNow),\n\t\t\t\tnew ProcessResultSet(acls, sids));\n\t\t// Lookup the parents, now that our JdbcTemplate has released the database\n\t\t// connection (SEC-547)\n\t\tif (parentsToLookup.size() > 0) {\n\t\t\tlookupPrimaryKeys(acls, parentsToLookup, sids);\n\t\t}\n\t}\n\n\tprivate void setKeys(PreparedStatement ps, Set<Long> findNow) throws SQLException {\n\t\tint i = 0;\n\t\tfor (Long toFind : findNow) {\n\t\t\ti++;\n\t\t\tps.setLong(i, toFind);\n\t\t}\n\t}\n\n\t/**\n\t * The main method.\n\t * <p>\n\t * WARNING: This implementation completely disregards the \"sids\" argument! Every item\n\t * in the cache is expected to contain all SIDs. If you have serious performance needs\n\t * (e.g. a very large number of SIDs per object identity), you'll probably want to\n\t * develop a custom {@link LookupStrategy} implementation instead.\n\t * <p>\n\t * The implementation works in batch sizes specified by {@link #batchSize}.\n\t * @param objects the identities to lookup (required)\n\t * @param sids the SIDs for which identities are required (ignored by this\n\t * implementation)\n\t * @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the\n\t * located {@link Acl} and values are the located {@link Acl} (never <tt>null</tt>\n\t * although some entries may be missing; this method should not throw\n\t * {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used to\n\t * automatically create entries if required)\n\t */\n\t@Override\n\tpublic final Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids) {\n\t\tAssert.isTrue(this.batchSize >= 1, \"BatchSize must be >= 1\");\n\t\tAssert.notEmpty(objects, \"Objects to lookup required\");\n\t\t// Map<ObjectIdentity,Acl>\n\t\t// contains FULLY loaded Acl objects\n\t\tMap<ObjectIdentity, Acl> result = new HashMap<>();\n\t\tSet<ObjectIdentity> currentBatchToLoad = new HashSet<>();\n\t\tfor (int i = 0; i < objects.size(); i++) {\n\t\t\tfinal ObjectIdentity oid = objects.get(i);\n\t\t\tboolean aclFound = false;\n\t\t\t// Check we don't already have this ACL in the results\n\t\t\tif (result.containsKey(oid)) {\n\t\t\t\taclFound = true;\n\t\t\t}\n\t\t\t// Check cache for the present ACL entry\n\t\t\tif (!aclFound) {\n\t\t\t\tAcl acl = this.aclCache.getFromCache(oid);\n\t\t\t\t// Ensure any cached element supports all the requested SIDs\n\t\t\t\t// (they should always, as our base impl doesn't filter on SID)\n\t\t\t\tif (acl != null) {\n\t\t\t\t\tAssert.state(acl.isSidLoaded(sids),\n\t\t\t\t\t\t\t\"Error: SID-filtered element detected when implementation does not perform SID filtering \"\n\t\t\t\t\t\t\t\t\t+ \"- have you added something to the cache manually?\");\n\t\t\t\t\tresult.put(acl.getObjectIdentity(), acl);\n\t\t\t\t\taclFound = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Load the ACL from the database\n\t\t\tif (!aclFound) {\n\t\t\t\tcurrentBatchToLoad.add(oid);\n\t\t\t}\n\t\t\t// Is it time to load from JDBC the currentBatchToLoad?\n\t\t\tif ((currentBatchToLoad.size() == this.batchSize) || ((i + 1) == objects.size())) {\n\t\t\t\tif (currentBatchToLoad.size() > 0) {\n\t\t\t\t\tMap<ObjectIdentity, Acl> loadedBatch = lookupObjectIdentities(currentBatchToLoad, sids);\n\t\t\t\t\t// Add loaded batch (all elements 100% initialized) to results\n\t\t\t\t\tresult.putAll(loadedBatch);\n\t\t\t\t\t// Add the loaded batch to the cache\n\t\t\t\t\tfor (Acl loadedAcl : loadedBatch.values()) {\n\t\t\t\t\t\tthis.aclCache.putInCache((AclImpl) loadedAcl);\n\t\t\t\t\t}\n\t\t\t\t\tcurrentBatchToLoad.clear();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Looks up a batch of <code>ObjectIdentity</code>s directly from the database.\n\t * <p>\n\t * The caller is responsible for optimization issues, such as selecting the identities\n\t * to lookup, ensuring the cache doesn't contain them already, and adding the returned\n\t * elements to the cache etc.\n\t * <p>\n\t * This subclass is required to return fully valid <code>Acl</code>s, including\n\t * properly-configured parent ACLs.\n\t */\n\tprivate Map<ObjectIdentity, Acl> lookupObjectIdentities(final Collection<ObjectIdentity> objectIdentities,\n\t\t\t@Nullable List<Sid> sids) {\n\t\tAssert.notEmpty(objectIdentities, \"Must provide identities to lookup\");\n\n\t\t// contains Acls with StubAclParents\n\t\tMap<Serializable, Acl> acls = new HashMap<>();\n\n\t\t// Make the \"acls\" map contain all requested objectIdentities\n\t\t// (including markers to each parent in the hierarchy)\n\t\tString sql = computeRepeatingSql(this.lookupObjectIdentitiesWhereClause, objectIdentities.size());\n\n\t\tSet<Long> parentsToLookup = this.jdbcTemplate.query(sql,\n\t\t\t\t(ps) -> setupLookupObjectIdentitiesStatement(ps, objectIdentities), new ProcessResultSet(acls, sids));\n\n\t\t// Lookup the parents, now that our JdbcTemplate has released the database\n\t\t// connection (SEC-547)\n\t\tif (parentsToLookup.size() > 0) {\n\t\t\tlookupPrimaryKeys(acls, parentsToLookup, sids);\n\t\t}\n\n\t\t// Finally, convert our \"acls\" containing StubAclParents into true Acls\n\t\tMap<ObjectIdentity, Acl> resultMap = new HashMap<>();\n\t\tfor (Acl inputAcl : acls.values()) {\n\t\t\tAssert.isInstanceOf(AclImpl.class, inputAcl, \"Map should have contained an AclImpl\");\n\t\t\tAssert.isInstanceOf(Long.class, ((AclImpl) inputAcl).getId(), \"Acl.getId() must be Long\");\n\t\t\tAcl result = convert(acls, (Long) ((AclImpl) inputAcl).getId());\n\t\t\tresultMap.put(result.getObjectIdentity(), result);\n\t\t}\n\n\t\treturn resultMap;\n\t}\n\n\tprivate void setupLookupObjectIdentitiesStatement(PreparedStatement ps, Collection<ObjectIdentity> objectIdentities)\n\t\t\tthrows SQLException {\n\t\tint i = 0;\n\t\tfor (ObjectIdentity oid : objectIdentities) {\n\t\t\t// Determine prepared statement values for this iteration\n\t\t\tString type = oid.getType();\n\n\t\t\t// No need to check for nulls, as guaranteed non-null by\n\t\t\t// ObjectIdentity.getIdentifier() interface contract\n\t\t\tString identifier = oid.getIdentifier().toString();\n\n\t\t\t// Inject values\n\t\t\tps.setString((2 * i) + 1, identifier);\n\t\t\tps.setString((2 * i) + 2, type);\n\t\t\ti++;\n\t\t}\n\t}\n\n\t/**\n\t * The final phase of converting the <code>Map</code> of <code>AclImpl</code>\n\t * instances which contain <code>StubAclParent</code>s into proper, valid\n\t * <code>AclImpl</code>s with correct ACL parents.\n\t * @param inputMap the unconverted <code>AclImpl</code>s\n\t * @param currentIdentity the current<code>Acl</code> that we wish to convert (this\n\t * may be\n\t */\n\tprivate AclImpl convert(Map<Serializable, Acl> inputMap, Long currentIdentity) {\n\t\tAssert.notEmpty(inputMap, \"InputMap required\");\n\t\tAssert.notNull(currentIdentity, \"CurrentIdentity required\");\n\n\t\t// Retrieve this Acl from the InputMap\n\t\tAcl uncastAcl = inputMap.get(currentIdentity);\n\t\tAssert.isInstanceOf(AclImpl.class, uncastAcl, \"The inputMap contained a non-AclImpl\");\n\n\t\tAclImpl inputAcl = (AclImpl) uncastAcl;\n\n\t\tAcl parent = inputAcl.getParentAcl();\n\n\t\tif ((parent != null) && parent instanceof StubAclParent) {\n\t\t\t// Lookup the parent\n\t\t\tStubAclParent stubAclParent = (StubAclParent) parent;\n\t\t\tparent = convert(inputMap, stubAclParent.getId());\n\t\t}\n\n\t\t// Now we have the parent (if there is one), create the true AclImpl\n\t\tSid owner = inputAcl.getOwner();\n\t\tAssert.isTrue(owner != null, \"Owner is required\");\n\t\tAclImpl result = new AclImpl(inputAcl.getObjectIdentity(), inputAcl.getId(), this.aclAuthorizationStrategy,\n\t\t\t\tthis.grantingStrategy, parent, null, inputAcl.isEntriesInheriting(), owner);\n\n\t\t// Copy the \"aces\" from the input to the destination\n\n\t\t// Obtain the \"aces\" from the input ACL\n\t\tList<AccessControlEntryImpl> aces = readAces(inputAcl);\n\n\t\t// Create a list in which to store the \"aces\" for the \"result\" AclImpl instance\n\t\tList<AccessControlEntryImpl> acesNew = new ArrayList<>();\n\n\t\t// Iterate over the \"aces\" input and replace each nested\n\t\t// AccessControlEntryImpl.getAcl() with the new \"result\" AclImpl instance\n\t\t// This ensures StubAclParent instances are removed, as per SEC-951\n\t\tfor (AccessControlEntryImpl ace : aces) {\n\t\t\tsetAclOnAce(ace, result);\n\t\t\tacesNew.add(ace);\n\t\t}\n\n\t\t// Finally, now that the \"aces\" have been converted to have the \"result\" AclImpl\n\t\t// instance, modify the \"result\" AclImpl instance\n\t\tsetAces(result, acesNew);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Creates a particular implementation of {@link Sid} depending on the arguments.\n\t * @param sid the name of the sid representing its unique identifier. In typical ACL\n\t * database schema it's located in table {@code acl_sid} table, {@code sid} column.\n\t * @param isPrincipal whether it's a user or granted authority like role\n\t * @return the instance of Sid with the {@code sidName} as an identifier\n\t */\n\tprotected Sid createSid(boolean isPrincipal, String sid) {\n\t\tif (isPrincipal) {\n\t\t\treturn new PrincipalSid(sid);\n\t\t}\n\t\treturn new GrantedAuthoritySid(sid);\n\t}\n\n\t/**\n\t * Sets the {@code PermissionFactory} instance which will be used to convert loaded\n\t * permission data values to {@code Permission}s. A {@code DefaultPermissionFactory}\n\t * will be used by default.\n\t * @param permissionFactory\n\t */\n\tpublic final void setPermissionFactory(PermissionFactory permissionFactory) {\n\t\tthis.permissionFactory = permissionFactory;\n\t}\n\n\tpublic final void setBatchSize(int batchSize) {\n\t\tthis.batchSize = batchSize;\n\t}\n\n\t/**\n\t * The SQL for the select clause. If customizing in order to modify column names,\n\t * schema etc, the other SQL customization fields must also be set to match.\n\t * @param selectClause the select clause, which defaults to\n\t * {@link #DEFAULT_SELECT_CLAUSE}.\n\t */\n\tpublic final void setSelectClause(String selectClause) {\n\t\tthis.selectClause = selectClause;\n\t}\n\n\t/**\n\t * The SQL for the where clause used in the <tt>lookupPrimaryKey</tt> method.\n\t */\n\tpublic final void setLookupPrimaryKeysWhereClause(String lookupPrimaryKeysWhereClause) {\n\t\tthis.lookupPrimaryKeysWhereClause = lookupPrimaryKeysWhereClause;\n\t}\n\n\t/**\n\t * The SQL for the where clause used in the <tt>lookupObjectIdentities</tt> method.\n\t */\n\tpublic final void setLookupObjectIdentitiesWhereClause(String lookupObjectIdentitiesWhereClause) {\n\t\tthis.lookupObjectIdentitiesWhereClause = lookupObjectIdentitiesWhereClause;\n\t}\n\n\t/**\n\t * The SQL for the \"order by\" clause used in both queries.\n\t */\n\tpublic final void setOrderByClause(String orderByClause) {\n\t\tthis.orderByClause = orderByClause;\n\t}\n\n\tpublic final void setAclClassIdSupported(boolean aclClassIdSupported) {\n\t\tif (aclClassIdSupported) {\n\t\t\tAssert.isTrue(this.selectClause.equals(DEFAULT_SELECT_CLAUSE),\n\t\t\t\t\t\"Cannot set aclClassIdSupported and override the select clause; \"\n\t\t\t\t\t\t\t+ \"just override the select clause\");\n\t\t\tthis.selectClause = DEFAULT_ACL_CLASS_ID_SELECT_CLAUSE;\n\t\t}\n\t}\n\n\tpublic final void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {\n\t\tAssert.notNull(objectIdentityGenerator, \"objectIdentityGenerator cannot be null\");\n\t\tthis.objectIdentityGenerator = objectIdentityGenerator;\n\t}\n\n\tpublic final void setConversionService(ConversionService conversionService) {\n\t\tthis.aclClassIdUtils = new AclClassIdUtils(conversionService);\n\t}\n\n\tprivate class ProcessResultSet implements ResultSetExtractor<Set<Long>> {\n\n\t\tprivate final Map<Serializable, Acl> acls;\n\n\t\tprivate final @Nullable List<Sid> sids;\n\n\t\tProcessResultSet(Map<Serializable, Acl> acls, @Nullable List<Sid> sids) {\n\t\t\tAssert.notNull(acls, \"ACLs cannot be null\");\n\t\t\tthis.acls = acls;\n\t\t\tthis.sids = sids; // can be null\n\t\t}\n\n\t\t/**\n\t\t * Implementation of {@link ResultSetExtractor#extractData(ResultSet)}. Creates an\n\t\t * {@link Acl} for each row in the {@link ResultSet} and ensures it is in member\n\t\t * field <tt>acls</tt>. Any {@link Acl} with a parent will have the parents id\n\t\t * returned in a set. The returned set of ids may requires further processing.\n\t\t * @param rs The {@link ResultSet} to be processed\n\t\t * @return a list of parent IDs remaining to be looked up (may be empty, but never\n\t\t * <tt>null</tt>)\n\t\t * @throws SQLException\n\t\t */\n\t\t@Override\n\t\tpublic Set<Long> extractData(ResultSet rs) throws SQLException {\n\t\t\tSet<Long> parentIdsToLookup = new HashSet<>(); // Set of parent_id Longs\n\n\t\t\twhile (rs.next()) {\n\t\t\t\t// Convert current row into an Acl (albeit with a StubAclParent)\n\t\t\t\tconvertCurrentResultIntoObject(this.acls, rs);\n\n\t\t\t\t// Figure out if this row means we need to lookup another parent\n\t\t\t\tlong parentId = rs.getLong(\"parent_object\");\n\n\t\t\t\tif (parentId != 0) {\n\t\t\t\t\t// See if it's already in the \"acls\"\n\t\t\t\t\tif (this.acls.containsKey(parentId)) {\n\t\t\t\t\t\tcontinue; // skip this while iteration\n\t\t\t\t\t}\n\n\t\t\t\t\t// Now try to find it in the cache\n\t\t\t\t\tMutableAcl cached = BasicLookupStrategy.this.aclCache.getFromCache(parentId);\n\t\t\t\t\tif ((cached == null) || !cached.isSidLoaded(this.sids)) {\n\t\t\t\t\t\tparentIdsToLookup.add(parentId);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Pop into the acls map, so our convert method doesn't\n\t\t\t\t\t\t// need to deal with an unsynchronized AclCache\n\t\t\t\t\t\tthis.acls.put(cached.getId(), cached);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Return the parents left to lookup to the caller\n\t\t\treturn parentIdsToLookup;\n\t\t}\n\n\t\t/**\n\t\t * Accepts the current <code>ResultSet</code> row, and converts it into an\n\t\t * <code>AclImpl</code> that contains a <code>StubAclParent</code>\n\t\t * @param acls the Map we should add the converted Acl to\n\t\t * @param rs the ResultSet focused on a current row\n\t\t * @throws SQLException if something goes wrong converting values\n\t\t * @throws ConversionException if can't convert to the desired Java type\n\t\t */\n\t\tprivate void convertCurrentResultIntoObject(Map<Serializable, Acl> acls, ResultSet rs) throws SQLException {\n\t\t\tLong id = rs.getLong(\"acl_id\");\n\n\t\t\t// If we already have an ACL for this ID, just create the ACE\n\t\t\tAcl acl = acls.get(id);\n\n\t\t\tif (acl == null) {\n\t\t\t\t// Make an AclImpl and pop it into the Map\n\n\t\t\t\t// If the Java type is a String, check to see if we can convert it to the\n\t\t\t\t// target id type, e.g. UUID.\n\t\t\t\tSerializable identifier = (Serializable) rs.getObject(\"object_id_identity\");\n\t\t\t\tidentifier = BasicLookupStrategy.this.aclClassIdUtils.identifierFrom(identifier, rs);\n\t\t\t\tif (identifier == null) {\n\t\t\t\t\tthrow new IllegalStateException(\"Identifier cannot be null\");\n\t\t\t\t}\n\t\t\t\tObjectIdentity objectIdentity = BasicLookupStrategy.this.objectIdentityGenerator\n\t\t\t\t\t.createObjectIdentity(identifier, rs.getString(\"class\"));\n\n\t\t\t\tAcl parentAcl = null;\n\t\t\t\tlong parentAclId = rs.getLong(\"parent_object\");\n\n\t\t\t\tif (parentAclId != 0) {\n\t\t\t\t\tparentAcl = new StubAclParent(parentAclId);\n\t\t\t\t}\n\n\t\t\t\tboolean entriesInheriting = rs.getBoolean(\"entries_inheriting\");\n\t\t\t\tSid owner = createSid(rs.getBoolean(\"acl_principal\"), rs.getString(\"acl_sid\"));\n\n\t\t\t\tacl = new AclImpl(objectIdentity, id, BasicLookupStrategy.this.aclAuthorizationStrategy,\n\t\t\t\t\t\tBasicLookupStrategy.this.grantingStrategy, parentAcl, null, entriesInheriting, owner);\n\n\t\t\t\tacls.put(id, acl);\n\t\t\t}\n\n\t\t\t// Add an extra ACE to the ACL (ORDER BY maintains the ACE list order)\n\t\t\t// It is permissible to have no ACEs in an ACL (which is detected by a null\n\t\t\t// ACE_SID)\n\t\t\tif (rs.getString(\"ace_sid\") != null) {\n\t\t\t\tLong aceId = rs.getLong(\"ace_id\");\n\t\t\t\tSid recipient = createSid(rs.getBoolean(\"ace_principal\"), rs.getString(\"ace_sid\"));\n\n\t\t\t\tint mask = rs.getInt(\"mask\");\n\t\t\t\tPermission permission = BasicLookupStrategy.this.permissionFactory.buildFromMask(mask);\n\t\t\t\tboolean granting = rs.getBoolean(\"granting\");\n\t\t\t\tboolean auditSuccess = rs.getBoolean(\"audit_success\");\n\t\t\t\tboolean auditFailure = rs.getBoolean(\"audit_failure\");\n\n\t\t\t\tAccessControlEntryImpl ace = new AccessControlEntryImpl(aceId, acl, recipient, permission, granting,\n\t\t\t\t\t\tauditSuccess, auditFailure);\n\n\t\t\t\t// Field acesField = FieldUtils.getField(AclImpl.class, \"aces\");\n\t\t\t\tList<AccessControlEntryImpl> aces = readAces((AclImpl) acl);\n\n\t\t\t\t// Add the ACE if it doesn't already exist in the ACL.aces field\n\t\t\t\tif (!aces.contains(ace)) {\n\t\t\t\t\taces.add(ace);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate static class StubAclParent implements Acl {\n\n\t\tprivate final Long id;\n\n\t\tStubAclParent(Long id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tLong getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\t@Override\n\t\tpublic List<AccessControlEntry> getEntries() {\n\t\t\tthrow new UnsupportedOperationException(\"Stub only\");\n\t\t}\n\n\t\t@Override\n\t\tpublic ObjectIdentity getObjectIdentity() {\n\t\t\tthrow new UnsupportedOperationException(\"Stub only\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Sid getOwner() {\n\t\t\tthrow new UnsupportedOperationException(\"Stub only\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Acl getParentAcl() {\n\t\t\tthrow new UnsupportedOperationException(\"Stub only\");\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isEntriesInheriting() {\n\t\t\tthrow new UnsupportedOperationException(\"Stub only\");\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)\n\t\t\t\tthrows NotFoundException, UnloadedSidException {\n\t\t\tthrow new UnsupportedOperationException(\"Stub only\");\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isSidLoaded(@Nullable List<Sid> sids) {\n\t\t\tthrow new UnsupportedOperationException(\"Stub only\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/jdbc/JdbcAclService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006, 2017, 2018 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.io.Serializable;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.ConversionService;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityGenerator;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.util.Assert;\n\n/**\n * Simple JDBC-based implementation of <code>AclService</code>.\n * <p>\n * Requires the \"dirty\" flags in {@link org.springframework.security.acls.domain.AclImpl}\n * and {@link org.springframework.security.acls.domain.AccessControlEntryImpl} to be set,\n * so that the implementation can detect changed parameters easily.\n *\n * @author Ben Alex\n */\npublic class JdbcAclService implements AclService {\n\n\tprotected static final Log log = LogFactory.getLog(JdbcAclService.class);\n\n\tprivate static final String DEFAULT_SELECT_ACL_CLASS_COLUMNS = \"class.class as class\";\n\n\tprivate static final String DEFAULT_SELECT_ACL_CLASS_COLUMNS_WITH_ID_TYPE = DEFAULT_SELECT_ACL_CLASS_COLUMNS\n\t\t\t+ \", class.class_id_type as class_id_type\";\n\n\tprivate static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL = \"select obj.object_id_identity as obj_id, \"\n\t\t\t+ DEFAULT_SELECT_ACL_CLASS_COLUMNS\n\t\t\t+ \" from acl_object_identity obj, acl_object_identity parent, acl_class class \"\n\t\t\t+ \"where obj.parent_object = parent.id and obj.object_id_class = class.id \"\n\t\t\t+ \"and parent.object_id_identity = ? and parent.object_id_class = (\"\n\t\t\t+ \"select id FROM acl_class where acl_class.class = ?)\";\n\n\tprivate static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL_WITH_CLASS_ID_TYPE = \"select obj.object_id_identity as obj_id, \"\n\t\t\t+ DEFAULT_SELECT_ACL_CLASS_COLUMNS_WITH_ID_TYPE\n\t\t\t+ \" from acl_object_identity obj, acl_object_identity parent, acl_class class \"\n\t\t\t+ \"where obj.parent_object = parent.id and obj.object_id_class = class.id \"\n\t\t\t+ \"and parent.object_id_identity = ? and parent.object_id_class = (\"\n\t\t\t+ \"select id FROM acl_class where acl_class.class = ?)\";\n\n\tprotected final JdbcOperations jdbcOperations;\n\n\tprivate final LookupStrategy lookupStrategy;\n\n\tprivate boolean aclClassIdSupported;\n\n\tprivate String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;\n\n\tprivate AclClassIdUtils aclClassIdUtils;\n\n\tprivate ObjectIdentityGenerator objectIdentityGenerator;\n\n\tpublic JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {\n\t\tthis(new JdbcTemplate(dataSource), lookupStrategy);\n\t}\n\n\tpublic JdbcAclService(JdbcOperations jdbcOperations, LookupStrategy lookupStrategy) {\n\t\tAssert.notNull(jdbcOperations, \"JdbcOperations required\");\n\t\tAssert.notNull(lookupStrategy, \"LookupStrategy required\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t\tthis.lookupStrategy = lookupStrategy;\n\t\tthis.aclClassIdUtils = new AclClassIdUtils();\n\t\tthis.objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();\n\t}\n\n\t@Override\n\tpublic @Nullable List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {\n\t\tObject[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };\n\t\tList<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql,\n\t\t\t\t(rs, rowNum) -> mapObjectIdentityRow(rs), args);\n\t\treturn (!objects.isEmpty()) ? objects : null;\n\t}\n\n\tprivate ObjectIdentity mapObjectIdentityRow(ResultSet rs) throws SQLException {\n\t\tString javaType = rs.getString(\"class\");\n\t\tSerializable identifier = (Serializable) rs.getObject(\"obj_id\");\n\t\tidentifier = this.aclClassIdUtils.identifierFrom(identifier, rs);\n\t\tif (identifier == null) {\n\t\t\tthrow new IllegalStateException(\"Identifier cannot be null\");\n\t\t}\n\t\treturn this.objectIdentityGenerator.createObjectIdentity(identifier, javaType);\n\t}\n\n\t@Override\n\tpublic Acl readAclById(ObjectIdentity object, @Nullable List<Sid> sids) throws NotFoundException {\n\t\tMap<ObjectIdentity, Acl> map = readAclsById(Collections.singletonList(object), sids);\n\t\tAssert.isTrue(map.containsKey(object),\n\t\t\t\t() -> \"There should have been an Acl entry for ObjectIdentity \" + object);\n\t\treturn map.get(object);\n\t}\n\n\t@Override\n\tpublic Acl readAclById(ObjectIdentity object) throws NotFoundException {\n\t\treturn readAclById(object, null);\n\t}\n\n\t@Override\n\tpublic Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException {\n\t\treturn readAclsById(objects, null);\n\t}\n\n\t@Override\n\tpublic Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids)\n\t\t\tthrows NotFoundException {\n\t\tMap<ObjectIdentity, Acl> result = this.lookupStrategy.readAclsById(objects, sids);\n\t\t// Check every requested object identity was found (throw NotFoundException if\n\t\t// needed)\n\t\tfor (ObjectIdentity oid : objects) {\n\t\t\tif (!result.containsKey(oid)) {\n\t\t\t\tthrow new NotFoundException(\"Unable to find ACL information for object identity '\" + oid + \"'\");\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Allows customization of the SQL query used to find child object identities.\n\t * @param findChildrenSql\n\t */\n\tpublic void setFindChildrenQuery(String findChildrenSql) {\n\t\tthis.findChildrenSql = findChildrenSql;\n\t}\n\n\tpublic void setAclClassIdSupported(boolean aclClassIdSupported) {\n\t\tthis.aclClassIdSupported = aclClassIdSupported;\n\t\tif (aclClassIdSupported) {\n\t\t\t// Change the default children select if it hasn't been overridden\n\t\t\tif (this.findChildrenSql.equals(DEFAULT_SELECT_ACL_WITH_PARENT_SQL)) {\n\t\t\t\tthis.findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL_WITH_CLASS_ID_TYPE;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlog.debug(\"Find children statement has already been overridden, so not overriding the default\");\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setConversionService(ConversionService conversionService) {\n\t\tthis.aclClassIdUtils = new AclClassIdUtils(conversionService);\n\t}\n\n\tpublic void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {\n\t\tAssert.notNull(objectIdentityGenerator, \"objectIdentityGenerator cannot be null\");\n\t\tthis.objectIdentityGenerator = objectIdentityGenerator;\n\t}\n\n\tprotected boolean isAclClassIdSupported() {\n\t\treturn this.aclClassIdSupported;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006, 2017, 2018 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\nimport org.springframework.security.acls.domain.AccessControlEntryImpl;\nimport org.springframework.security.acls.domain.GrantedAuthoritySid;\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.domain.PrincipalSid;\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclCache;\nimport org.springframework.security.acls.model.AlreadyExistsException;\nimport org.springframework.security.acls.model.ChildrenExistException;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.MutableAclService;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.transaction.support.TransactionSynchronizationManager;\nimport org.springframework.util.Assert;\n\n/**\n * Provides a base JDBC implementation of {@link MutableAclService}.\n * <p>\n * The default settings are for HSQLDB. If you are using a different database you will\n * probably need to set the {@link #setSidIdentityQuery(String) sidIdentityQuery} and\n * {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. The\n * other queries, SQL inserts and updates can also be customized to accommodate schema\n * variations, but must produce results consistent with those expected by the defaults.\n * <p>\n * See the appendix of the Spring Security reference manual for more information on the\n * expected schema and how it is used. Information on using PostgreSQL is also included.\n *\n * @author Ben Alex\n * @author Johannes Zlattinger\n */\npublic class JdbcMutableAclService extends JdbcAclService implements MutableAclService {\n\n\tprivate static final String DEFAULT_INSERT_INTO_ACL_CLASS = \"insert into acl_class (class) values (?)\";\n\n\tprivate static final String DEFAULT_INSERT_INTO_ACL_CLASS_WITH_ID = \"insert into acl_class (class, class_id_type) values (?, ?)\";\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate boolean foreignKeysInDatabase = true;\n\n\tprivate final AclCache aclCache;\n\n\tprivate String deleteEntryByObjectIdentityForeignKey = \"delete from acl_entry where acl_object_identity=?\";\n\n\tprivate String deleteObjectIdentityByPrimaryKey = \"delete from acl_object_identity where id=?\";\n\n\tprivate String classIdentityQuery = \"call identity()\";\n\n\tprivate String sidIdentityQuery = \"call identity()\";\n\n\tprivate String insertClass = DEFAULT_INSERT_INTO_ACL_CLASS;\n\n\tprivate String insertEntry = \"insert into acl_entry \"\n\t\t\t+ \"(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)\"\n\t\t\t+ \"values (?, ?, ?, ?, ?, ?, ?)\";\n\n\tprivate String insertObjectIdentity = \"insert into acl_object_identity \"\n\t\t\t+ \"(object_id_class, object_id_identity, owner_sid, entries_inheriting) \" + \"values (?, ?, ?, ?)\";\n\n\tprivate String insertSid = \"insert into acl_sid (principal, sid) values (?, ?)\";\n\n\tprivate String selectClassPrimaryKey = \"select id from acl_class where class=?\";\n\n\tprivate String selectObjectIdentityPrimaryKey = \"select acl_object_identity.id from acl_object_identity, acl_class \"\n\t\t\t+ \"where acl_object_identity.object_id_class = acl_class.id and acl_class.class=? \"\n\t\t\t+ \"and acl_object_identity.object_id_identity = ?\";\n\n\tprivate String selectSidPrimaryKey = \"select id from acl_sid where principal=? and sid=?\";\n\n\tprivate String updateObjectIdentity = \"update acl_object_identity set \"\n\t\t\t+ \"parent_object = ?, owner_sid = ?, entries_inheriting = ?\" + \" where id = ?\";\n\n\tpublic JdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {\n\t\tsuper(dataSource, lookupStrategy);\n\t\tAssert.notNull(aclCache, \"AclCache required\");\n\t\tthis.aclCache = aclCache;\n\t}\n\n\t@Override\n\tpublic MutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsException {\n\t\tAssert.notNull(objectIdentity, \"Object Identity required\");\n\n\t\t// Check this object identity hasn't already been persisted\n\t\tif (retrieveObjectIdentityPrimaryKey(objectIdentity) != null) {\n\t\t\tthrow new AlreadyExistsException(\"Object identity '\" + objectIdentity + \"' already exists\");\n\t\t}\n\n\t\t// Need to retrieve the current principal, in order to know who \"owns\" this ACL\n\t\t// (can be changed later on)\n\t\tAuthentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tAssert.isTrue(auth != null, \"Authentication required\");\n\t\tPrincipalSid sid = new PrincipalSid(auth);\n\n\t\t// Create the acl_object_identity row\n\t\tcreateObjectIdentity(objectIdentity, sid);\n\n\t\t// Retrieve the ACL via superclass (ensures cache registration, proper retrieval\n\t\t// etc)\n\t\tAcl acl = readAclById(objectIdentity);\n\t\tAssert.isInstanceOf(MutableAcl.class, acl, \"MutableAcl should be been returned\");\n\n\t\treturn (MutableAcl) acl;\n\t}\n\n\t/**\n\t * Creates a new row in acl_entry for every ACE defined in the passed MutableAcl\n\t * object.\n\t * @param acl containing the ACEs to insert\n\t */\n\tprotected void createEntries(final MutableAcl acl) {\n\t\tif (acl.getEntries().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.jdbcOperations.batchUpdate(this.insertEntry, new BatchPreparedStatementSetter() {\n\n\t\t\t@Override\n\t\t\tpublic int getBatchSize() {\n\t\t\t\treturn acl.getEntries().size();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void setValues(PreparedStatement stmt, int i) throws SQLException {\n\t\t\t\tAccessControlEntry entry_ = acl.getEntries().get(i);\n\t\t\t\tAssert.isTrue(entry_ instanceof AccessControlEntryImpl, \"Unknown ACE class\");\n\t\t\t\tAccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;\n\n\t\t\t\tAssert.state(acl.getId() != null, \"ACL ID cannot be null\");\n\t\t\t\tstmt.setLong(1, (Long) acl.getId());\n\t\t\t\tstmt.setInt(2, i);\n\t\t\t\tLong sidPrimaryKey = createOrRetrieveSidPrimaryKey(entry.getSid(), true);\n\t\t\t\tAssert.state(sidPrimaryKey != null, \"SID primary key cannot be null\");\n\t\t\t\tstmt.setLong(3, sidPrimaryKey);\n\t\t\t\tstmt.setInt(4, entry.getPermission().getMask());\n\t\t\t\tstmt.setBoolean(5, entry.isGranting());\n\t\t\t\tstmt.setBoolean(6, entry.isAuditSuccess());\n\t\t\t\tstmt.setBoolean(7, entry.isAuditFailure());\n\t\t\t}\n\n\t\t});\n\t}\n\n\t/**\n\t * Creates an entry in the acl_object_identity table for the passed ObjectIdentity.\n\t * The Sid is also necessary, as acl_object_identity has defined the sid column as\n\t * non-null.\n\t * @param object to represent an acl_object_identity for\n\t * @param owner for the SID column (will be created if there is no acl_sid entry for\n\t * this particular Sid already)\n\t */\n\tprotected void createObjectIdentity(ObjectIdentity object, Sid owner) {\n\t\tLong sidId = createOrRetrieveSidPrimaryKey(owner, true);\n\t\tLong classId = createOrRetrieveClassPrimaryKey(object.getType(), true, object.getIdentifier().getClass());\n\t\tthis.jdbcOperations.update(this.insertObjectIdentity, classId, object.getIdentifier().toString(), sidId,\n\t\t\t\tBoolean.TRUE);\n\t}\n\n\t/**\n\t * Retrieves the primary key from {@code acl_class}, creating a new row if needed and\n\t * the {@code allowCreate} property is {@code true}.\n\t * @param type to find or create an entry for (often the fully-qualified class name)\n\t * @param allowCreate true if creation is permitted if not found\n\t * @return the primary key or null if not found\n\t */\n\tprotected @Nullable Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {\n\t\tList<@Nullable Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);\n\n\t\tif (!classIds.isEmpty()) {\n\t\t\tLong result = classIds.get(0);\n\t\t\tif (result != null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\tif (allowCreate) {\n\t\t\tif (!isAclClassIdSupported()) {\n\t\t\t\tthis.jdbcOperations.update(this.insertClass, type);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.jdbcOperations.update(this.insertClass, type, idType.getCanonicalName());\n\t\t\t}\n\t\t\tAssert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), \"Transaction must be running\");\n\t\t\tLong result = this.jdbcOperations.queryForObject(this.classIdentityQuery, Long.class);\n\t\t\tAssert.state(result != null, \"Failed to retrieve class primary key\");\n\t\t\treturn result;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Retrieves the primary key from acl_sid, creating a new row if needed and the\n\t * allowCreate property is true.\n\t * @param sid to find or create\n\t * @param allowCreate true if creation is permitted if not found\n\t * @return the primary key or null if not found\n\t * @throws IllegalArgumentException if the <tt>Sid</tt> is not a recognized\n\t * implementation.\n\t */\n\tprotected @Nullable Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {\n\t\tAssert.notNull(sid, \"Sid required\");\n\t\tif (sid instanceof PrincipalSid) {\n\t\t\tString sidName = ((PrincipalSid) sid).getPrincipal();\n\t\t\treturn createOrRetrieveSidPrimaryKey(sidName, true, allowCreate);\n\t\t}\n\t\tif (sid instanceof GrantedAuthoritySid) {\n\t\t\tString sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();\n\t\t\treturn createOrRetrieveSidPrimaryKey(sidName, false, allowCreate);\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Unsupported implementation of Sid\");\n\t}\n\n\t/**\n\t * Retrieves the primary key from acl_sid, creating a new row if needed and the\n\t * allowCreate property is true.\n\t * @param sidName name of Sid to find or to create\n\t * @param sidIsPrincipal whether it's a user or granted authority like role\n\t * @param allowCreate true if creation is permitted if not found\n\t * @return the primary key or null if not found\n\t */\n\tprotected @Nullable Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal,\n\t\t\tboolean allowCreate) {\n\t\tList<@Nullable Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class,\n\t\t\t\tsidIsPrincipal, sidName);\n\t\tif (!sidIds.isEmpty()) {\n\t\t\tLong result = sidIds.get(0);\n\t\t\tif (result != null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\tif (allowCreate) {\n\t\t\tthis.jdbcOperations.update(this.insertSid, sidIsPrincipal, sidName);\n\t\t\tAssert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), \"Transaction must be running\");\n\t\t\tLong result = this.jdbcOperations.queryForObject(this.sidIdentityQuery, Long.class);\n\t\t\tAssert.state(result != null, \"Failed to retrieve sid primary key\");\n\t\t\treturn result;\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren) throws ChildrenExistException {\n\t\tAssert.notNull(objectIdentity, \"Object Identity required\");\n\t\tAssert.notNull(objectIdentity.getIdentifier(), \"Object Identity doesn't provide an identifier\");\n\t\tif (deleteChildren) {\n\t\t\tList<ObjectIdentity> children = findChildren(objectIdentity);\n\t\t\tif (children != null) {\n\t\t\t\tfor (ObjectIdentity child : children) {\n\t\t\t\t\tdeleteAcl(child, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (!this.foreignKeysInDatabase) {\n\t\t\t\t// We need to perform a manual verification for what a FK would normally\n\t\t\t\t// do. We generally don't do this, in the interests of deadlock management\n\t\t\t\tList<ObjectIdentity> children = findChildren(objectIdentity);\n\t\t\t\tif (children != null) {\n\t\t\t\t\tthrow new ChildrenExistException(\n\t\t\t\t\t\t\t\"Cannot delete '\" + objectIdentity + \"' (has \" + children.size() + \" children)\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tLong oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);\n\t\tif (oidPrimaryKey == null) {\n\t\t\tthrow new NotFoundException(\"Object identity not found: \" + objectIdentity);\n\t\t}\n\n\t\t// Delete this ACL's ACEs in the acl_entry table\n\t\tdeleteEntries(oidPrimaryKey);\n\n\t\t// Delete this ACL's acl_object_identity row\n\t\tdeleteObjectIdentity(oidPrimaryKey);\n\n\t\t// Clear the cache\n\t\tthis.aclCache.evictFromCache(objectIdentity);\n\t}\n\n\t/**\n\t * Deletes all ACEs defined in the acl_entry table belonging to the presented\n\t * ObjectIdentity primary key.\n\t * @param oidPrimaryKey the rows in acl_entry to delete\n\t */\n\tprotected void deleteEntries(Long oidPrimaryKey) {\n\t\tthis.jdbcOperations.update(this.deleteEntryByObjectIdentityForeignKey, oidPrimaryKey);\n\t}\n\n\t/**\n\t * Deletes a single row from acl_object_identity that is associated with the presented\n\t * ObjectIdentity primary key.\n\t * <p>\n\t * We do not delete any entries from acl_class, even if no classes are using that\n\t * class any longer. This is a deadlock avoidance approach.\n\t * @param oidPrimaryKey to delete the acl_object_identity\n\t */\n\tprotected void deleteObjectIdentity(Long oidPrimaryKey) {\n\t\t// Delete the acl_object_identity row\n\t\tthis.jdbcOperations.update(this.deleteObjectIdentityByPrimaryKey, oidPrimaryKey);\n\t}\n\n\t/**\n\t * Retrieves the primary key from the acl_object_identity table for the passed\n\t * ObjectIdentity. Unlike some other methods in this implementation, this method will\n\t * NOT create a row (use {@link #createObjectIdentity(ObjectIdentity, Sid)} instead).\n\t * @param oid to find\n\t * @return the object identity or null if not found\n\t */\n\tprotected @Nullable Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {\n\t\ttry {\n\t\t\tLong result = this.jdbcOperations.queryForObject(this.selectObjectIdentityPrimaryKey, Long.class,\n\t\t\t\t\toid.getType(), oid.getIdentifier().toString());\n\t\t\treturn result;\n\t\t}\n\t\tcatch (DataAccessException notFound) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * This implementation will simply delete all ACEs in the database and recreate them\n\t * on each invocation of this method. A more comprehensive implementation might use\n\t * dirty state checking, or more likely use ORM capabilities for create, update and\n\t * delete operations of {@link MutableAcl}.\n\t */\n\t@Override\n\tpublic MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {\n\t\tAssert.notNull(acl.getId(), \"Object Identity doesn't provide an identifier\");\n\n\t\t// Delete this ACL's ACEs in the acl_entry table\n\t\tLong oidPrimaryKey = retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity());\n\t\tif (oidPrimaryKey == null) {\n\t\t\tthrow new NotFoundException(\"Object identity not found for ACL: \" + acl.getObjectIdentity());\n\t\t}\n\t\tdeleteEntries(oidPrimaryKey);\n\n\t\t// Create this ACL's ACEs in the acl_entry table\n\t\tcreateEntries(acl);\n\n\t\t// Change the mutable columns in acl_object_identity\n\t\tupdateObjectIdentity(acl);\n\n\t\t// Clear the cache, including children\n\t\tclearCacheIncludingChildren(acl.getObjectIdentity());\n\n\t\t// Retrieve the ACL via superclass (ensures cache registration, proper retrieval\n\t\t// etc)\n\t\treturn (MutableAcl) super.readAclById(acl.getObjectIdentity());\n\t}\n\n\tprivate void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {\n\t\tAssert.notNull(objectIdentity, \"ObjectIdentity required\");\n\t\tList<ObjectIdentity> children = findChildren(objectIdentity);\n\t\tif (children != null) {\n\t\t\tfor (ObjectIdentity child : children) {\n\t\t\t\tclearCacheIncludingChildren(child);\n\t\t\t}\n\t\t}\n\t\tthis.aclCache.evictFromCache(objectIdentity);\n\t}\n\n\t/**\n\t * Updates an existing acl_object_identity row, with new information presented in the\n\t * passed MutableAcl object. Also will create an acl_sid entry if needed for the Sid\n\t * that owns the MutableAcl.\n\t * @param acl to modify (a row must already exist in acl_object_identity)\n\t * @throws NotFoundException if the ACL could not be found to update.\n\t */\n\tprotected void updateObjectIdentity(MutableAcl acl) {\n\t\tLong parentId = null;\n\t\tif (acl.getParentAcl() != null) {\n\t\t\tAssert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl().getObjectIdentity(),\n\t\t\t\t\t\"Implementation only supports ObjectIdentityImpl\");\n\t\t\tObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl().getObjectIdentity();\n\t\t\tparentId = retrieveObjectIdentityPrimaryKey(oii);\n\t\t}\n\t\tAssert.notNull(acl.getOwner(), \"Owner is required in this implementation\");\n\t\tLong ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);\n\t\tint count = this.jdbcOperations.update(this.updateObjectIdentity, parentId, ownerSid, acl.isEntriesInheriting(),\n\t\t\t\tacl.getId());\n\t\tif (count != 1) {\n\t\t\tthrow new NotFoundException(\"Unable to locate ACL to update\");\n\t\t}\n\t}\n\n\t/**\n\t * Sets the query that will be used to retrieve the identity of a newly created row in\n\t * the <tt>acl_class</tt> table.\n\t * @param classIdentityQuery the query, which should return the identifier. Defaults\n\t * to <tt>call identity()</tt>\n\t */\n\tpublic void setClassIdentityQuery(String classIdentityQuery) {\n\t\tAssert.hasText(classIdentityQuery, \"New classIdentityQuery query is required\");\n\t\tthis.classIdentityQuery = classIdentityQuery;\n\t}\n\n\t/**\n\t * Sets the query that will be used to retrieve the identity of a newly created row in\n\t * the <tt>acl_sid</tt> table.\n\t * @param sidIdentityQuery the query, which should return the identifier. Defaults to\n\t * <tt>call identity()</tt>\n\t */\n\tpublic void setSidIdentityQuery(String sidIdentityQuery) {\n\t\tAssert.hasText(sidIdentityQuery, \"New sidIdentityQuery query is required\");\n\t\tthis.sidIdentityQuery = sidIdentityQuery;\n\t}\n\n\tpublic void setDeleteEntryByObjectIdentityForeignKeySql(String deleteEntryByObjectIdentityForeignKey) {\n\t\tthis.deleteEntryByObjectIdentityForeignKey = deleteEntryByObjectIdentityForeignKey;\n\t}\n\n\tpublic void setDeleteObjectIdentityByPrimaryKeySql(String deleteObjectIdentityByPrimaryKey) {\n\t\tthis.deleteObjectIdentityByPrimaryKey = deleteObjectIdentityByPrimaryKey;\n\t}\n\n\tpublic void setInsertClassSql(String insertClass) {\n\t\tthis.insertClass = insertClass;\n\t}\n\n\tpublic void setInsertEntrySql(String insertEntry) {\n\t\tthis.insertEntry = insertEntry;\n\t}\n\n\tpublic void setInsertObjectIdentitySql(String insertObjectIdentity) {\n\t\tthis.insertObjectIdentity = insertObjectIdentity;\n\t}\n\n\tpublic void setInsertSidSql(String insertSid) {\n\t\tthis.insertSid = insertSid;\n\t}\n\n\tpublic void setClassPrimaryKeyQuery(String selectClassPrimaryKey) {\n\t\tthis.selectClassPrimaryKey = selectClassPrimaryKey;\n\t}\n\n\tpublic void setObjectIdentityPrimaryKeyQuery(String selectObjectIdentityPrimaryKey) {\n\t\tthis.selectObjectIdentityPrimaryKey = selectObjectIdentityPrimaryKey;\n\t}\n\n\tpublic void setSidPrimaryKeyQuery(String selectSidPrimaryKey) {\n\t\tthis.selectSidPrimaryKey = selectSidPrimaryKey;\n\t}\n\n\tpublic void setUpdateObjectIdentity(String updateObjectIdentity) {\n\t\tthis.updateObjectIdentity = updateObjectIdentity;\n\t}\n\n\t/**\n\t * @param foreignKeysInDatabase if false this class will perform additional FK\n\t * constrain checking, which may cause deadlocks (the default is true, so deadlocks\n\t * are avoided but the database is expected to enforce FKs)\n\t */\n\tpublic void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {\n\t\tthis.foreignKeysInDatabase = foreignKeysInDatabase;\n\t}\n\n\t@Override\n\tpublic void setAclClassIdSupported(boolean aclClassIdSupported) {\n\t\tsuper.setAclClassIdSupported(aclClassIdSupported);\n\t\tif (aclClassIdSupported) {\n\t\t\t// Change the default insert if it hasn't been overridden\n\t\t\tif (this.insertClass.equals(DEFAULT_INSERT_INTO_ACL_CLASS)) {\n\t\t\t\tthis.insertClass = DEFAULT_INSERT_INTO_ACL_CLASS_WITH_ID;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlog.debug(\"Insert class statement has already been overridden, so not overriding the default\");\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/jdbc/LookupStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.Sid;\n\n/**\n * Performs lookups for {@link org.springframework.security.acls.model.AclService}.\n *\n * @author Ben Alex\n */\npublic interface LookupStrategy {\n\n\t/**\n\t * Perform database-specific optimized lookup.\n\t * @param objects the identities to lookup (required)\n\t * @param sids the SIDs for which identities are required (may be <tt>null</tt> -\n\t * implementations may elect not to provide SID optimisations)\n\t * @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the\n\t * located {@link Acl} and values are the located {@link Acl} (never <tt>null</tt>\n\t * although some entries may be missing; this method should not throw\n\t * {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used to\n\t * automatically create entries if required)\n\t */\n\tMap<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/jdbc/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * JDBC-based persistence of ACL information\n */\n@NullMarked\npackage org.springframework.security.acls.jdbc;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/AccessControlEntry.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Represents an individual permission assignment within an {@link Acl}.\n *\n * <p>\n * Instances MUST be immutable, as they are returned by <code>Acl</code> and should not\n * allow client modification.\n * </p>\n *\n * @author Ben Alex\n */\npublic interface AccessControlEntry extends Serializable {\n\n\tAcl getAcl();\n\n\t/**\n\t * Obtains an identifier that represents this ACE.\n\t * @return the identifier, or <code>null</code> if unsaved\n\t */\n\t@Nullable Serializable getId();\n\n\tPermission getPermission();\n\n\tSid getSid();\n\n\t/**\n\t * Indicates the permission is being granted to the relevant Sid. If false, indicates\n\t * the permission is being revoked/blocked.\n\t * @return true if being granted, false otherwise\n\t */\n\tboolean isGranting();\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/Acl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Represents an access control list (ACL) for a domain object.\n *\n * <p>\n * An <tt>Acl</tt> represents all ACL entries for a given domain object. In order to avoid\n * needing references to the domain object itself, this interface handles indirection\n * between a domain object and an ACL object identity via the\n * {@link org.springframework.security.acls.model.ObjectIdentity} interface.\n * </p>\n *\n * <p>\n * Implementing classes may elect to return instances that represent\n * {@link org.springframework.security.acls.model.Permission} information for either some\n * OR all {@link org.springframework.security.acls.model.Sid} instances. Therefore, an\n * instance may NOT necessarily contain ALL <tt>Sid</tt>s for a given domain object.\n * </p>\n *\n * @author Ben Alex\n */\npublic interface Acl extends Serializable {\n\n\t/**\n\t * Returns all of the entries represented by the present <tt>Acl</tt>. Entries\n\t * associated with the <tt>Acl</tt> parents are not returned.\n\t *\n\t * <p>\n\t * This method is typically used for administrative purposes.\n\t * </p>\n\t *\n\t * <p>\n\t * The order that entries appear in the array is important for methods declared in the\n\t * {@link MutableAcl} interface. Furthermore, some implementations MAY use ordering as\n\t * part of advanced permission checking.\n\t * </p>\n\t *\n\t * <p>\n\t * Do <em>NOT</em> use this method for making authorization decisions. Instead use\n\t * {@link #isGranted(List, List, boolean)}.\n\t * </p>\n\t *\n\t * <p>\n\t * This method must operate correctly even if the <tt>Acl</tt> only represents a\n\t * subset of <tt>Sid</tt>s. The caller is responsible for correctly handling the\n\t * result if only a subset of <tt>Sid</tt>s is represented.\n\t * </p>\n\t * @return the list of entries represented by the <tt>Acl</tt>, or <tt>null</tt> if\n\t * there are no entries presently associated with this <tt>Acl</tt>.\n\t */\n\tList<AccessControlEntry> getEntries();\n\n\t/**\n\t * Obtains the domain object this <tt>Acl</tt> provides entries for. This is immutable\n\t * once an <tt>Acl</tt> is created.\n\t * @return the object identity (never <tt>null</tt>)\n\t */\n\tObjectIdentity getObjectIdentity();\n\n\t/**\n\t * Determines the owner of the <tt>Acl</tt>. The meaning of ownership varies by\n\t * implementation and is unspecified.\n\t * @return the owner (may be <tt>null</tt> if the implementation does not use\n\t * ownership concepts)\n\t */\n\t@Nullable Sid getOwner();\n\n\t/**\n\t * A domain object may have a parent for the purpose of ACL inheritance. If there is a\n\t * parent, its ACL can be accessed via this method. In turn, the parent's parent\n\t * (grandparent) can be accessed and so on.\n\t *\n\t * <p>\n\t * This method solely represents the presence of a navigation hierarchy between the\n\t * parent <tt>Acl</tt> and this <tt>Acl</tt>. For actual inheritance to take place,\n\t * the {@link #isEntriesInheriting()} must also be <tt>true</tt>.\n\t * </p>\n\t *\n\t * <p>\n\t * This method must operate correctly even if the <tt>Acl</tt> only represents a\n\t * subset of <tt>Sid</tt>s. The caller is responsible for correctly handling the\n\t * result if only a subset of <tt>Sid</tt>s is represented.\n\t * </p>\n\t * @return the parent <tt>Acl</tt> (may be <tt>null</tt> if this <tt>Acl</tt> does not\n\t * have a parent)\n\t */\n\t@Nullable Acl getParentAcl();\n\n\t/**\n\t * Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down\n\t * into the current <tt>Acl</tt>.\n\t * <p>\n\t * The mere link between an <tt>Acl</tt> and a parent <tt>Acl</tt> on its own is\n\t * insufficient to cause ACL entries to inherit down. This is because a domain object\n\t * may wish to have entirely independent entries, but maintain the link with the\n\t * parent for navigation purposes. Thus, this method denotes whether or not the\n\t * navigation relationship also extends to the actual inheritance of entries.\n\t * </p>\n\t * @return <tt>true</tt> if parent ACL entries inherit into the current <tt>Acl</tt>\n\t */\n\tboolean isEntriesInheriting();\n\n\t/**\n\t * This is the actual authorization logic method, and must be used whenever ACL\n\t * authorization decisions are required.\n\t *\n\t * <p>\n\t * An array of <tt>Sid</tt>s are presented, representing security identifies of the\n\t * current principal. In addition, an array of <tt>Permission</tt>s is presented which\n\t * will have one or more bits set in order to indicate the permissions needed for an\n\t * affirmative authorization decision. An array is presented because holding\n\t * <em>any</em> of the <tt>Permission</tt>s inside the array will be sufficient for an\n\t * affirmative authorization.\n\t * </p>\n\t *\n\t * <p>\n\t * The actual approach used to make authorization decisions is left to the\n\t * implementation and is not specified by this interface. For example, an\n\t * implementation <em>MAY</em> search the current ACL in the order the ACL entries\n\t * have been stored. If a single entry is found that has the same active bits as are\n\t * shown in a passed <tt>Permission</tt>, that entry's grant or deny state may\n\t * determine the authorization decision. If the case of a deny state, the deny\n\t * decision will only be relevant if all other <tt>Permission</tt>s passed in the\n\t * array have also been unsuccessfully searched. If no entry is found that match the\n\t * bits in the current ACL, provided that {@link #isEntriesInheriting()} is\n\t * <tt>true</tt>, the authorization decision may be passed to the parent ACL. If there\n\t * is no matching entry, the implementation MAY throw an exception, or make a\n\t * predefined authorization decision.\n\t * </p>\n\t *\n\t * <p>\n\t * This method must operate correctly even if the <tt>Acl</tt> only represents a\n\t * subset of <tt>Sid</tt>s, although the implementation is permitted to throw one of\n\t * the signature-defined exceptions if the method is called requesting an\n\t * authorization decision for a {@link Sid} that was never loaded in this <tt>Acl</tt>\n\t * .\n\t * </p>\n\t * @param permission the permission or permissions required (at least one entry\n\t * required)\n\t * @param sids the security identities held by the principal (at least one entry\n\t * required)\n\t * @param administrativeMode if <tt>true</tt> denotes the query is for administrative\n\t * purposes and no logging or auditing (if supported by the implementation) should be\n\t * undertaken\n\t * @return <tt>true</tt> if authorization is granted\n\t * @throws NotFoundException MUST be thrown if an implementation cannot make an\n\t * authoritative authorization decision, usually because there is no ACL information\n\t * for this particular permission and/or SID\n\t * @throws UnloadedSidException thrown if the <tt>Acl</tt> does not have details for\n\t * one or more of the <tt>Sid</tt>s passed as arguments\n\t */\n\tboolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)\n\t\t\tthrows NotFoundException, UnloadedSidException;\n\n\t/**\n\t * For efficiency reasons an <tt>Acl</tt> may be loaded and <em>not</em> contain\n\t * entries for every <tt>Sid</tt> in the system. If an <tt>Acl</tt> has been loaded\n\t * and does not represent every <tt>Sid</tt>, all methods of the <tt>Acl</tt> can only\n\t * be used within the limited scope of the <tt>Sid</tt> instances it actually\n\t * represents.\n\t * <p>\n\t * It is normal to load an <tt>Acl</tt> for only particular <tt>Sid</tt>s if read-only\n\t * authorization decisions are being made. However, if user interface reporting or\n\t * modification of <tt>Acl</tt>s are desired, an <tt>Acl</tt> should be loaded with\n\t * all <tt>Sid</tt>s. This method denotes whether or not the specified <tt>Sid</tt>s\n\t * have been loaded or not.\n\t * </p>\n\t * @param sids one or more security identities the caller is interest in knowing\n\t * whether this <tt>Sid</tt> supports\n\t * @return <tt>true</tt> if every passed <tt>Sid</tt> is represented by this\n\t * <tt>Acl</tt> instance\n\t */\n\tboolean isSidLoaded(@Nullable List<Sid> sids);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/AclCache.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.acls.jdbc.JdbcAclService;\n\n/**\n * A caching layer for {@link JdbcAclService}.\n *\n * @author Ben Alex\n */\npublic interface AclCache {\n\n\tvoid evictFromCache(Serializable pk);\n\n\tvoid evictFromCache(ObjectIdentity objectIdentity);\n\n\t@Nullable MutableAcl getFromCache(ObjectIdentity objectIdentity);\n\n\t@Nullable MutableAcl getFromCache(Serializable pk);\n\n\tvoid putInCache(MutableAcl acl);\n\n\tvoid clearCache();\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/AclDataAccessException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * Abstract base class for Acl data operations.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic abstract class AclDataAccessException extends RuntimeException {\n\n\t/**\n\t * Constructs an <code>AclDataAccessException</code> with the specified message and\n\t * root cause.\n\t * @param msg the detail message\n\t * @param cause the root cause\n\t */\n\tpublic AclDataAccessException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\t/**\n\t * Constructs an <code>AclDataAccessException</code> with the specified message and no\n\t * root cause.\n\t * @param msg the detail message\n\t */\n\tpublic AclDataAccessException(String msg) {\n\t\tsuper(msg);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/AclService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Provides retrieval of {@link Acl} instances.\n *\n * @author Ben Alex\n */\npublic interface AclService {\n\n\t/**\n\t * Locates all object identities that use the specified parent. This is useful for\n\t * administration tools.\n\t * @param parentIdentity to locate children of\n\t * @return the children (or <tt>null</tt> if none were found)\n\t */\n\t@Nullable List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);\n\n\t/**\n\t * Same as {@link #readAclsById(List)} except it returns only a single Acl.\n\t * <p>\n\t * This method should not be called as it does not leverage the underlying\n\t * implementation's potential ability to filter <tt>Acl</tt> entries based on a\n\t * {@link Sid} parameter.\n\t * </p>\n\t * @param object to locate an {@link Acl} for\n\t * @return the {@link Acl} for the requested {@link ObjectIdentity} (never\n\t * <tt>null</tt>)\n\t * @throws NotFoundException if an {@link Acl} was not found for the requested\n\t * {@link ObjectIdentity}\n\t */\n\tAcl readAclById(ObjectIdentity object) throws NotFoundException;\n\n\t/**\n\t * Same as {@link #readAclsById(List, List)} except it returns only a single Acl.\n\t * @param object to locate an {@link Acl} for\n\t * @param sids the security identities for which {@link Acl} information is required\n\t * (may be <tt>null</tt> to denote all entries)\n\t * @return the {@link Acl} for the requested {@link ObjectIdentity} (never\n\t * <tt>null</tt>)\n\t * @throws NotFoundException if an {@link Acl} was not found for the requested\n\t * {@link ObjectIdentity}\n\t */\n\tAcl readAclById(ObjectIdentity object, @Nullable List<Sid> sids) throws NotFoundException;\n\n\t/**\n\t * Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s.\n\t * <p>\n\t * The returned map is keyed on the passed objects, with the values being the\n\t * <tt>Acl</tt> instances. Any unknown objects will not have a map key.\n\t * </p>\n\t * @param objects the objects to find {@link Acl} information for\n\t * @return a map with exactly one element for each {@link ObjectIdentity} passed as an\n\t * argument (never <tt>null</tt>)\n\t * @throws NotFoundException if an {@link Acl} was not found for each requested\n\t * {@link ObjectIdentity}\n\t */\n\tMap<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException;\n\n\t/**\n\t * Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s, but only\n\t * for the security identifies passed.\n\t * <p>\n\t * Implementations <em>MAY</em> provide a subset of the ACLs via this method although\n\t * this is NOT a requirement. This is intended to allow performance optimisations\n\t * within implementations. Callers should therefore use this method in preference to\n\t * the alternative overloaded version which does not have performance optimisation\n\t * opportunities.\n\t * </p>\n\t * <p>\n\t * The returned map is keyed on the passed objects, with the values being the\n\t * <tt>Acl</tt> instances. Any unknown objects (or objects for which the interested\n\t * <tt>Sid</tt>s do not have entries) will not have a map key.\n\t * </p>\n\t * @param objects the objects to find {@link Acl} information for\n\t * @param sids the security identities for which {@link Acl} information is required\n\t * (may be <tt>null</tt> to denote all entries)\n\t * @return a map with exactly one element for each {@link ObjectIdentity} passed as an\n\t * argument (never <tt>null</tt>)\n\t * @throws NotFoundException if an {@link Acl} was not found for each requested\n\t * {@link ObjectIdentity}\n\t */\n\tMap<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids)\n\t\t\tthrows NotFoundException;\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/AlreadyExistsException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * Thrown if an <code>Acl</code> entry already exists for the object.\n *\n * @author Ben Alex\n */\npublic class AlreadyExistsException extends AclDataAccessException {\n\n\t/**\n\t * Constructs an <code>AlreadyExistsException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic AlreadyExistsException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>AlreadyExistsException</code> with the specified message and\n\t * root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic AlreadyExistsException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/AuditableAccessControlEntry.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * Represents an ACE that provides auditing information.\n *\n * @author Ben Alex\n */\npublic interface AuditableAccessControlEntry extends AccessControlEntry {\n\n\tboolean isAuditFailure();\n\n\tboolean isAuditSuccess();\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/AuditableAcl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * A mutable ACL that provides audit capabilities.\n *\n * @author Ben Alex\n */\npublic interface AuditableAcl extends MutableAcl {\n\n\tvoid updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/ChildrenExistException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * Thrown if an {@link Acl} cannot be deleted because children <code>Acl</code>s exist.\n *\n * @author Ben Alex\n */\npublic class ChildrenExistException extends AclDataAccessException {\n\n\t/**\n\t * Constructs an <code>ChildrenExistException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic ChildrenExistException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>ChildrenExistException</code> with the specified message and\n\t * root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic ChildrenExistException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/MutableAcl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.io.Serializable;\n\n/**\n * A mutable <tt>Acl</tt>.\n * <p>\n * A mutable ACL must ensure that appropriate security checks are performed before\n * allowing access to its methods.\n *\n * @author Ben Alex\n */\npublic interface MutableAcl extends Acl {\n\n\tvoid deleteAce(int aceIndex) throws NotFoundException;\n\n\t/**\n\t * Obtains an identifier that represents this <tt>MutableAcl</tt>.\n\t * @return the identifier, or <tt>null</tt> if unsaved\n\t */\n\tSerializable getId();\n\n\tvoid insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting) throws NotFoundException;\n\n\t/**\n\t * Changes the present owner to a different owner.\n\t * @param newOwner the new owner (mandatory; cannot be null)\n\t */\n\tvoid setOwner(Sid newOwner);\n\n\t/**\n\t * Change the value returned by {@link Acl#isEntriesInheriting()}.\n\t * @param entriesInheriting the new value\n\t */\n\tvoid setEntriesInheriting(boolean entriesInheriting);\n\n\t/**\n\t * Changes the parent of this ACL.\n\t * @param newParent the new parent\n\t */\n\tvoid setParent(Acl newParent);\n\n\tvoid updateAce(int aceIndex, Permission permission) throws NotFoundException;\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/MutableAclService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * Provides support for creating and storing <code>Acl</code> instances.\n *\n * @author Ben Alex\n */\npublic interface MutableAclService extends AclService {\n\n\t/**\n\t * Creates an empty <code>Acl</code> object in the database. It will have no entries.\n\t * The returned object will then be used to add entries.\n\t * @param objectIdentity the object identity to create\n\t * @return an ACL object with its ID set\n\t * @throws AlreadyExistsException if the passed object identity already has a record\n\t */\n\tMutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsException;\n\n\t/**\n\t * Removes the specified entry from the database.\n\t * @param objectIdentity the object identity to remove\n\t * @param deleteChildren whether to cascade the delete to children\n\t * @throws ChildrenExistException if the deleteChildren argument was\n\t * <code>false</code> but children exist\n\t */\n\tvoid deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren) throws ChildrenExistException;\n\n\t/**\n\t * Changes an existing <code>Acl</code> in the database.\n\t * @param acl to modify\n\t * @throws NotFoundException if the relevant record could not be found (did you\n\t * remember to use {@link #createAcl(ObjectIdentity)} to create the object, rather\n\t * than creating it with the <code>new</code> keyword?)\n\t */\n\tMutableAcl updateAcl(MutableAcl acl) throws NotFoundException;\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/NotFoundException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * Thrown if an ACL-related object cannot be found.\n *\n * @author Ben Alex\n */\npublic class NotFoundException extends AclDataAccessException {\n\n\t/**\n\t * Constructs an <code>NotFoundException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic NotFoundException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>NotFoundException</code> with the specified message and root\n\t * cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic NotFoundException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/ObjectIdentity.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.io.Serializable;\n\n/**\n * Represents the identity of an individual domain object instance.\n *\n * <p>\n * As implementations of <tt>ObjectIdentity</tt> are used as the key to represent domain\n * objects in the ACL subsystem, it is essential that implementations provide methods so\n * that object-equality rather than reference-equality can be relied upon reliably. In\n * other words, the ACL subsystem can consider two <tt>ObjectIdentity</tt>s equal if\n * <tt>identity1.equals(identity2)</tt>, rather than reference-equality of\n * <tt>identity1==identity2</tt>.\n * </p>\n *\n * @author Ben Alex\n */\npublic interface ObjectIdentity extends Serializable {\n\n\t/**\n\t * @param obj to be compared\n\t * @return <tt>true</tt> if the objects are equal, <tt>false</tt> otherwise\n\t * @see Object#equals(Object)\n\t */\n\t@Override\n\tboolean equals(Object obj);\n\n\t/**\n\t * Obtains the actual identifier. This identifier must not be reused to represent\n\t * other domain objects with the same <tt>javaType</tt>.\n\t *\n\t * <p>\n\t * Because ACLs are largely immutable, it is strongly recommended to use a synthetic\n\t * identifier (such as a database sequence number for the primary key). Do not use an\n\t * identifier with business meaning, as that business meaning may change in the future\n\t * such change will cascade to the ACL subsystem data.\n\t * </p>\n\t * @return the identifier (unique within this <tt>type</tt>; never <tt>null</tt>)\n\t */\n\tSerializable getIdentifier();\n\n\t/**\n\t * Obtains the \"type\" metadata for the domain object. This will often be a Java type\n\t * name (an interface or a class) &ndash; traditionally it is the name of the domain\n\t * object implementation class.\n\t * @return the \"type\" of the domain object (never <tt>null</tt>).\n\t */\n\tString getType();\n\n\t/**\n\t * @return a hash code representation of the <tt>ObjectIdentity</tt>\n\t * @see Object#hashCode()\n\t */\n\t@Override\n\tint hashCode();\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/ObjectIdentityGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.io.Serializable;\n\n/**\n * Strategy which creates an {@link ObjectIdentity} from an object identifier (such as a\n * primary key) and type information.\n * <p>\n * Differs from {@link ObjectIdentityRetrievalStrategy} in that it is used in situations\n * when the actual object instance isn't available.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface ObjectIdentityGenerator {\n\n\t/**\n\t * @param id the identifier of the domain object, not null\n\t * @param type the type of the object (often a class name), not null\n\t * @return the identity constructed using the supplied identifier and type\n\t * information.\n\t */\n\tObjectIdentity createObjectIdentity(Serializable id, String type);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/ObjectIdentityRetrievalStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * Strategy interface that provides the ability to determine which {@link ObjectIdentity}\n * will be returned for a particular domain object\n *\n * @author Ben Alex\n */\npublic interface ObjectIdentityRetrievalStrategy {\n\n\tObjectIdentity getObjectIdentity(Object domainObject);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/OwnershipAcl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * A mutable ACL that provides ownership capabilities.\n *\n * <p>\n * Generally the owner of an ACL is able to call any ACL mutator method, as well as assign\n * a new owner.\n *\n * @author Ben Alex\n */\npublic interface OwnershipAcl extends MutableAcl {\n\n\t@Override\n\tvoid setOwner(Sid newOwner);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/Permission.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.io.Serializable;\n\n/**\n * Represents a permission granted to a <tt>Sid</tt> for a given domain object.\n *\n * @author Ben Alex\n */\npublic interface Permission extends Serializable {\n\n\tchar RESERVED_ON = '~';\n\n\tchar RESERVED_OFF = '.';\n\n\tString THIRTY_TWO_RESERVED_OFF = \"................................\";\n\n\t/**\n\t * Returns the bits that represents the permission.\n\t * @return the bits that represent the permission\n\t */\n\tint getMask();\n\n\t/**\n\t * Returns a 32-character long bit pattern <code>String</code> representing this\n\t * permission.\n\t * <p>\n\t * Implementations are free to format the pattern as they see fit, although under no\n\t * circumstances may {@link #RESERVED_OFF} or {@link #RESERVED_ON} be used within the\n\t * pattern. An exemption is in the case of {@link #RESERVED_OFF} which is used to\n\t * denote a bit that is off (clear). Implementations may also elect to use\n\t * {@link #RESERVED_ON} internally for computation purposes, although this method may\n\t * not return any <code>String</code> containing {@link #RESERVED_ON}.\n\t * <p>\n\t * The returned String must be 32 characters in length.\n\t * <p>\n\t * This method is only used for user interface and logging purposes. It is not used in\n\t * any permission calculations. Therefore, duplication of characters within the output\n\t * is permitted.\n\t * @return a 32-character bit pattern\n\t */\n\tString getPattern();\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/PermissionGrantingStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.util.List;\n\n/**\n * Allow customization of the logic for determining whether a permission or permissions\n * are granted to a particular sid or sids by an {@link Acl}.\n *\n * @author Luke Taylor\n * @since 3.0.2\n */\npublic interface PermissionGrantingStrategy {\n\n\t/**\n\t * Returns true if the supplied strategy decides that the supplied {@code Acl} grants\n\t * access based on the supplied list of permissions and sids.\n\t */\n\tboolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids, boolean administrativeMode);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/Sid.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.io.Serializable;\n\n/**\n * A security identity recognised by the ACL system.\n *\n * <p>\n * This interface provides indirection between actual security objects (eg principals,\n * roles, groups etc) and what is stored inside an <code>Acl</code>. This is because an\n * <code>Acl</code> will not store an entire security object, but only an abstraction of\n * it. This interface therefore provides a simple way to compare these abstracted security\n * identities with other security identities and actual security objects.\n * </p>\n *\n * @author Ben Alex\n */\npublic interface Sid extends Serializable {\n\n\t/**\n\t * Refer to the <code>java.lang.Object</code> documentation for the interface\n\t * contract.\n\t * @param obj to be compared\n\t * @return <code>true</code> if the objects are equal, <code>false</code> otherwise\n\t */\n\t@Override\n\tboolean equals(Object obj);\n\n\t/**\n\t * Refer to the <code>java.lang.Object</code> documentation for the interface\n\t * contract.\n\t * @return a hash code representation of this object\n\t */\n\t@Override\n\tint hashCode();\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/SidRetrievalStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\nimport java.util.List;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Strategy interface that provides an ability to determine the {@link Sid} instances\n * applicable for an {@link Authentication}.\n *\n * @author Ben Alex\n */\npublic interface SidRetrievalStrategy {\n\n\tList<Sid> getSids(Authentication authentication);\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/UnloadedSidException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.model;\n\n/**\n * Thrown if an {@link Acl} cannot perform an operation because it only loaded a subset of\n * <code>Sid</code>s and the caller has requested details for an unloaded <code>Sid</code>\n * .\n *\n * @author Ben Alex\n */\npublic class UnloadedSidException extends AclDataAccessException {\n\n\t/**\n\t * Constructs an <code>NotFoundException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic UnloadedSidException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>NotFoundException</code> with the specified message and root\n\t * cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic UnloadedSidException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/model/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Interfaces and shared classes to manage access control lists (ACLs) for domain object\n * instances.\n */\n@NullMarked\npackage org.springframework.security.acls.model;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "acl/src/main/java/org/springframework/security/acls/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The Spring Security ACL package which implements instance-based security for domain\n * objects.\n * <p>\n * Consider using the annotation based approach ({@code @PreAuthorize},\n * {@code @PostFilter} annotations) combined with a\n * {@link org.springframework.security.acls.AclPermissionEvaluator} in preference to the\n * older and more verbose attribute/voter/after-invocation approach from versions before\n * Spring Security 3.0.\n */\n@NullMarked\npackage org.springframework.security.acls;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "acl/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.acls.aot.hint.AclRuntimeHints\n"
  },
  {
    "path": "acl/src/main/resources/createAclSchema.sql",
    "content": "-- ACL schema sql used in HSQLDB\n\n-- drop table acl_entry;\n-- drop table acl_object_identity;\n-- drop table acl_class;\n-- drop table acl_sid;\n\ncreate table acl_sid(\n    id bigint generated by default as identity(start with 100) not null primary key,\n    principal boolean not null,\n    sid varchar_ignorecase(100) not null,\n    constraint unique_uk_1 unique(sid,principal)\n);\n\ncreate table acl_class(\n    id bigint generated by default as identity(start with 100) not null primary key,\n    class varchar_ignorecase(100) not null,\n    constraint unique_uk_2 unique(class)\n);\n\ncreate table acl_object_identity(\n    id bigint generated by default as identity(start with 100) not null primary key,\n    object_id_class bigint not null,\n    object_id_identity bigint not null,\n    parent_object bigint,\n    owner_sid bigint,\n    entries_inheriting boolean not null,\n    constraint unique_uk_3 unique(object_id_class,object_id_identity),\n    constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),\n    constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),\n    constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)\n);\n\ncreate table acl_entry(\n    id bigint generated by default as identity(start with 100) not null primary key,\n    acl_object_identity bigint not null,\n    ace_order int not null,\n    sid bigint not null,\n    mask integer not null,\n    granting boolean not null,\n    audit_success boolean not null,\n    audit_failure boolean not null,\n    constraint unique_uk_4 unique(acl_object_identity,ace_order),\n    constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),\n    constraint foreign_fk_5 foreign key(sid) references acl_sid(id)\n);\n"
  },
  {
    "path": "acl/src/main/resources/createAclSchemaMySQL.sql",
    "content": "-- ACL Schema SQL for MySQL 5.5+ / MariaDB equivalent\n\n-- drop table acl_entry;\n-- drop table acl_object_identity;\n-- drop table acl_class;\n-- drop table acl_sid;\n\nCREATE TABLE acl_sid (\n    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n    principal BOOLEAN NOT NULL,\n    sid VARCHAR(100) NOT NULL,\n    UNIQUE KEY unique_acl_sid (sid, principal)\n) ENGINE=InnoDB;\n\nCREATE TABLE acl_class (\n    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n    class VARCHAR(100) NOT NULL,\n    UNIQUE KEY uk_acl_class (class)\n) ENGINE=InnoDB;\n\nCREATE TABLE acl_object_identity (\n    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n    object_id_class BIGINT UNSIGNED NOT NULL,\n    object_id_identity VARCHAR(36) NOT NULL,\n    parent_object BIGINT UNSIGNED,\n    owner_sid BIGINT UNSIGNED,\n    entries_inheriting BOOLEAN NOT NULL,\n    UNIQUE KEY uk_acl_object_identity (object_id_class, object_id_identity),\n    CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),\n    CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),\n    CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)\n) ENGINE=InnoDB;\n\nCREATE TABLE acl_entry (\n    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n    acl_object_identity BIGINT UNSIGNED NOT NULL,\n    ace_order INTEGER NOT NULL,\n    sid BIGINT UNSIGNED NOT NULL,\n    mask INTEGER UNSIGNED NOT NULL,\n    granting BOOLEAN NOT NULL,\n    audit_success BOOLEAN NOT NULL,\n    audit_failure BOOLEAN NOT NULL,\n    UNIQUE KEY unique_acl_entry (acl_object_identity, ace_order),\n    CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),\n    CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)\n) ENGINE=InnoDB;\n"
  },
  {
    "path": "acl/src/main/resources/createAclSchemaOracle.sql",
    "content": "-- ACL Schema SQL for Oracle Database 10g+\n\n-- drop trigger acl_sid_id_trigger;\n-- drop trigger acl_class_id_trigger;\n-- drop trigger acl_object_identity_id_trigger;\n-- drop trigger acl_entry_id_trigger;\n-- drop sequence acl_sid_sequence;\n-- drop sequence acl_class_sequence;\n-- drop sequence acl_object_identity_sequence;\n-- drop sequence acl_entry_sequence;\n-- drop table acl_entry;\n-- drop table acl_object_identity;\n-- drop table acl_class;\n-- drop table acl_sid;\n\nCREATE TABLE acl_sid (\n    id NUMBER(38) NOT NULL PRIMARY KEY,\n    principal NUMBER(1) NOT NULL CHECK (principal in (0, 1)),\n    sid NVARCHAR2(100) NOT NULL,\n    CONSTRAINT unique_acl_sid UNIQUE (sid, principal)\n);\nCREATE SEQUENCE acl_sid_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;\nCREATE OR REPLACE TRIGGER acl_sid_id_trigger\n    BEFORE INSERT ON acl_sid\n    FOR EACH ROW\nBEGIN\n    SELECT acl_sid_sequence.nextval INTO :new.id FROM dual;\nEND;\n\nCREATE TABLE acl_class (\n    id NUMBER(38) NOT NULL PRIMARY KEY,\n    class NVARCHAR2(100) NOT NULL,\n    CONSTRAINT uk_acl_class UNIQUE (class)\n);\nCREATE SEQUENCE acl_class_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;\nCREATE OR REPLACE TRIGGER acl_class_id_trigger\n    BEFORE INSERT ON acl_class\n    FOR EACH ROW\nBEGIN\n    SELECT acl_class_sequence.nextval INTO :new.id FROM dual;\nEND;\n\nCREATE TABLE acl_object_identity (\n    id NUMBER(38) NOT NULL PRIMARY KEY,\n    object_id_class NUMBER(38) NOT NULL,\n    object_id_identity NVARCHAR2(36) NOT NULL,\n    parent_object NUMBER(38),\n    owner_sid NUMBER(38),\n    entries_inheriting NUMBER(1) NOT NULL CHECK (entries_inheriting in (0, 1)),\n    CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),\n    CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),\n    CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),\n    CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)\n);\nCREATE SEQUENCE acl_object_identity_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;\nCREATE OR REPLACE TRIGGER acl_object_identity_id_trigger\n    BEFORE INSERT ON acl_object_identity\n    FOR EACH ROW\nBEGIN\n    SELECT acl_object_identity_sequence.nextval INTO :new.id FROM dual;\nEND;\n\nCREATE TABLE acl_entry (\n    id NUMBER(38) NOT NULL PRIMARY KEY,\n    acl_object_identity NUMBER(38) NOT NULL,\n    ace_order INTEGER NOT NULL,\n    sid NUMBER(38) NOT NULL,\n    mask INTEGER NOT NULL,\n    granting NUMBER(1) NOT NULL CHECK (granting in (0, 1)),\n    audit_success NUMBER(1) NOT NULL CHECK (audit_success in (0, 1)),\n    audit_failure NUMBER(1) NOT NULL CHECK (audit_failure in (0, 1)),\n    CONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),\n    CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),\n    CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)\n);\nCREATE SEQUENCE acl_entry_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;\nCREATE OR REPLACE TRIGGER acl_entry_id_trigger\n    BEFORE INSERT ON acl_entry\n    FOR EACH ROW\nBEGIN\n    SELECT acl_entry_sequence.nextval INTO :new.id FROM dual;\nEND;\n"
  },
  {
    "path": "acl/src/main/resources/createAclSchemaPostgres.sql",
    "content": "-- ACL Schema SQL for PostgreSQL\n\n-- drop table acl_entry;\n-- drop table acl_object_identity;\n-- drop table acl_class;\n-- drop table acl_sid;\n\ncreate table acl_sid(\n    id bigserial not null primary key,\n    principal boolean not null,\n    sid varchar(100) not null,\n    constraint unique_uk_1 unique(sid,principal)\n);\n\ncreate table acl_class(\n    id bigserial not null primary key,\n    class varchar(100) not null,\n    class_id_type varchar(100),\n    constraint unique_uk_2 unique(class)\n);\n\ncreate table acl_object_identity(\n    id bigserial primary key,\n    object_id_class bigint not null,\n    object_id_identity varchar(36) not null,\n    parent_object bigint,\n    owner_sid bigint,\n    entries_inheriting boolean not null,\n    constraint unique_uk_3 unique(object_id_class,object_id_identity),\n    constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),\n    constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),\n    constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)\n);\n\ncreate table acl_entry(\n    id bigserial primary key,\n    acl_object_identity bigint not null,\n    ace_order int not null,\n    sid bigint not null,\n    mask integer not null,\n    granting boolean not null,\n    audit_success boolean not null,\n    audit_failure boolean not null,\n    constraint unique_uk_4 unique(acl_object_identity,ace_order),\n    constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),\n    constraint foreign_fk_5 foreign key(sid) references acl_sid(id)\n);\n"
  },
  {
    "path": "acl/src/main/resources/createAclSchemaSqlServer.sql",
    "content": "-- ACL Schema SQL for Microsoft SQL Server 2008+\n\n-- drop table acl_entry;\n-- drop table acl_object_identity;\n-- drop table acl_class;\n-- drop table acl_sid;\n\nCREATE TABLE acl_sid (\n    id BIGINT NOT NULL IDENTITY PRIMARY KEY,\n    principal BIT NOT NULL,\n    sid VARCHAR(100) NOT NULL,\n    CONSTRAINT unique_acl_sid UNIQUE (sid, principal)\n);\n\nCREATE TABLE acl_class (\n    id BIGINT NOT NULL IDENTITY PRIMARY KEY,\n    class VARCHAR(100) NOT NULL,\n    CONSTRAINT uk_acl_class UNIQUE (class)\n);\n\nCREATE TABLE acl_object_identity (\n    id BIGINT NOT NULL IDENTITY PRIMARY KEY,\n    object_id_class BIGINT NOT NULL,\n    object_id_identity VARCHAR(36) NOT NULL,\n    parent_object BIGINT,\n    owner_sid BIGINT,\n    entries_inheriting BIT NOT NULL,\n    CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),\n    CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),\n    CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),\n    CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)\n);\n\nCREATE TABLE acl_entry (\n    id BIGINT NOT NULL IDENTITY PRIMARY KEY,\n    acl_object_identity BIGINT NOT NULL,\n    ace_order INTEGER NOT NULL,\n    sid BIGINT NOT NULL,\n    mask INTEGER NOT NULL,\n    granting BIT NOT NULL,\n    audit_success BIT NOT NULL,\n    audit_failure BIT NOT NULL,\n    CONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),\n    CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),\n    CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)\n);\n"
  },
  {
    "path": "acl/src/main/resources/createAclSchemaWithAclClassIdType.sql",
    "content": "-- ACL schema sql used in HSQLDB\n\n-- drop table acl_entry;\n-- drop table acl_object_identity;\n-- drop table acl_class;\n-- drop table acl_sid;\n\ncreate table acl_sid(\n    id bigint generated by default as identity(start with 100) not null primary key,\n    principal boolean not null,\n    sid varchar_ignorecase(100) not null,\n    constraint unique_uk_1 unique(sid,principal)\n);\n\ncreate table acl_class(\n    id bigint generated by default as identity(start with 100) not null primary key,\n    class varchar_ignorecase(100) not null,\n    class_id_type varchar_ignorecase(100),\n    constraint unique_uk_2 unique(class)\n);\n\ncreate table acl_object_identity(\n    id bigint generated by default as identity(start with 100) not null primary key,\n    object_id_class bigint not null,\n    object_id_identity varchar_ignorecase(36) not null,\n    parent_object bigint,\n    owner_sid bigint,\n    entries_inheriting boolean not null,\n    constraint unique_uk_3 unique(object_id_class,object_id_identity),\n    constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),\n    constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),\n    constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)\n);\n\ncreate table acl_entry(\n    id bigint generated by default as identity(start with 100) not null primary key,\n    acl_object_identity bigint not null,\n    ace_order int not null,\n    sid bigint not null,\n    mask integer not null,\n    granting boolean not null,\n    audit_success boolean not null,\n    audit_failure boolean not null,\n    constraint unique_uk_4 unique(acl_object_identity,ace_order),\n    constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),\n    constraint foreign_fk_5 foreign key(sid) references acl_sid(id)\n);\n"
  },
  {
    "path": "acl/src/main/resources/select.sql",
    "content": "-- Not required. Just shows the sort of queries being sent to DB.\n\n\nselect  acl_object_identity.object_id_identity,\n        acl_entry.ace_order,\n        acl_object_identity.id as acl_id,\n        acl_object_identity.parent_object,\n        acl_object_identity,\n        entries_inheriting,\n        acl_entry.id as ace_id,\n        acl_entry.mask,\n        acl_entry.granting,\n        acl_entry.audit_success,\n        acl_entry.audit_failure,\n        acl_sid.principal as ace_principal,\n        acl_sid.sid as ace_sid,\n        acli_sid.principal as acl_principal,\n        acli_sid.sid as acl_sid,\n        acl_class.class\n\nfrom    acl_object_identity,\n        acl_sid acli_sid,\n        acl_class\n\nleft join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity\nleft join acl_sid on acl_entry.sid = acl_sid.id\n\nwhere\n    acli_sid.id = acl_object_identity.owner_sid\n\nand acl_class.id = acl_object_identity.object_id_class\n\nand (\n\n    (acl_object_identity.object_id_identity = 1 and acl_class.class = 'sample.contact.contact')\nor\n    (acl_object_identity.object_id_identity = 2000 and acl_class.class = 'sample.contact.contact')\n\n) order by acl_object_identity.object_id_identity asc, acl_entry.ace_order asc\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/AclFormattingUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.domain.AclFormattingUtils;\nimport org.springframework.security.acls.model.Permission;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Tests for {@link AclFormattingUtils}.\n *\n * @author Andrei Stefan\n */\npublic class AclFormattingUtilsTests {\n\n\t@Test\n\tpublic final void testDemergePatternsParametersConstraints() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AclFormattingUtils.demergePatterns(null, \"SOME STRING\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AclFormattingUtils.demergePatterns(\"SOME STRING\", null));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AclFormattingUtils.demergePatterns(\"SOME STRING\", \"LONGER SOME STRING\"));\n\t\tassertThatNoException().isThrownBy(() -> AclFormattingUtils.demergePatterns(\"SOME STRING\", \"SAME LENGTH\"));\n\t}\n\n\t@Test\n\tpublic final void testDemergePatterns() {\n\t\tString original = \"...........................A...R\";\n\t\tString removeBits = \"...............................R\";\n\t\tassertThat(AclFormattingUtils.demergePatterns(original, removeBits))\n\t\t\t.isEqualTo(\"...........................A....\");\n\t\tassertThat(AclFormattingUtils.demergePatterns(\"ABCDEF\", \"......\")).isEqualTo(\"ABCDEF\");\n\t\tassertThat(AclFormattingUtils.demergePatterns(\"ABCDEF\", \"GHIJKL\")).isEqualTo(\"......\");\n\t}\n\n\t@Test\n\tpublic final void testMergePatternsParametersConstraints() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AclFormattingUtils.mergePatterns(null, \"SOME STRING\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AclFormattingUtils.mergePatterns(\"SOME STRING\", null));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AclFormattingUtils.mergePatterns(\"SOME STRING\", \"LONGER SOME STRING\"));\n\t\tassertThatNoException().isThrownBy(() -> AclFormattingUtils.mergePatterns(\"SOME STRING\", \"SAME LENGTH\"));\n\t}\n\n\t@Test\n\tpublic final void testMergePatterns() {\n\t\tString original = \"...............................R\";\n\t\tString extraBits = \"...........................A....\";\n\t\tassertThat(AclFormattingUtils.mergePatterns(original, extraBits)).isEqualTo(\"...........................A...R\");\n\t\tassertThat(AclFormattingUtils.mergePatterns(\"ABCDEF\", \"......\")).isEqualTo(\"ABCDEF\");\n\t\tassertThat(AclFormattingUtils.mergePatterns(\"ABCDEF\", \"GHIJKL\")).isEqualTo(\"GHIJKL\");\n\t}\n\n\t@Test\n\tpublic final void testBinaryPrints() {\n\t\tassertThat(AclFormattingUtils.printBinary(15)).isEqualTo(\"............................****\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AclFormattingUtils.printBinary(15, Permission.RESERVED_ON));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AclFormattingUtils.printBinary(15, Permission.RESERVED_OFF));\n\t\tassertThat(AclFormattingUtils.printBinary(15, 'x')).isEqualTo(\"............................xxxx\");\n\t}\n\n\t@Test\n\tpublic void testPrintBinaryNegative() {\n\t\tassertThat(AclFormattingUtils.printBinary(0x80000000)).isEqualTo(\"*...............................\");\n\t}\n\n\t@Test\n\tpublic void testPrintBinaryMinusOne() {\n\t\tassertThat(AclFormattingUtils.printBinary(0xffffffff)).isEqualTo(\"********************************\");\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/AclPermissionCacheOptimizerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings({ \"unchecked\" })\npublic class AclPermissionCacheOptimizerTests {\n\n\t@Test\n\tpublic void eagerlyLoadsRequiredAcls() {\n\t\tAclService service = mock(AclService.class);\n\t\tAclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);\n\t\tObjectIdentityRetrievalStrategy oidStrat = mock(ObjectIdentityRetrievalStrategy.class);\n\t\tSidRetrievalStrategy sidStrat = mock(SidRetrievalStrategy.class);\n\t\tpco.setObjectIdentityRetrievalStrategy(oidStrat);\n\t\tpco.setSidRetrievalStrategy(sidStrat);\n\t\tObject[] dos = { new Object(), null, new Object() };\n\t\tObjectIdentity[] oids = { new ObjectIdentityImpl(\"A\", \"1\"), new ObjectIdentityImpl(\"A\", \"2\") };\n\t\tgiven(oidStrat.getObjectIdentity(dos[0])).willReturn(oids[0]);\n\t\tgiven(oidStrat.getObjectIdentity(dos[2])).willReturn(oids[1]);\n\t\tpco.cachePermissionsFor(mock(Authentication.class), Arrays.asList(dos));\n\t\t// AclService should be invoked with the list of required Oids\n\t\tverify(service).readAclsById(eq(Arrays.asList(oids)), any(List.class));\n\t}\n\n\t@Test\n\tpublic void ignoresEmptyCollection() {\n\t\tAclService service = mock(AclService.class);\n\t\tAclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);\n\t\tObjectIdentityRetrievalStrategy oids = mock(ObjectIdentityRetrievalStrategy.class);\n\t\tSidRetrievalStrategy sids = mock(SidRetrievalStrategy.class);\n\t\tpco.setObjectIdentityRetrievalStrategy(oids);\n\t\tpco.setSidRetrievalStrategy(sids);\n\t\tpco.cachePermissionsFor(mock(Authentication.class), Collections.emptyList());\n\t\tverifyNoMoreInteractions(service, sids, oids);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/AclPermissionEvaluatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls;\n\nimport java.util.Locale;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclService;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\npublic class AclPermissionEvaluatorTests {\n\n\t@Test\n\tpublic void hasPermissionReturnsTrueIfAclGrantsPermission() {\n\t\tAclService service = mock(AclService.class);\n\t\tAclPermissionEvaluator pe = new AclPermissionEvaluator(service);\n\t\tObjectIdentity oid = mock(ObjectIdentity.class);\n\t\tObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);\n\t\tgiven(oidStrategy.getObjectIdentity(any(Object.class))).willReturn(oid);\n\t\tpe.setObjectIdentityRetrievalStrategy(oidStrategy);\n\t\tpe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));\n\t\tAcl acl = mock(Acl.class);\n\t\tgiven(service.readAclById(any(ObjectIdentity.class), anyList())).willReturn(acl);\n\t\tgiven(acl.isGranted(anyList(), anyList(), eq(false))).willReturn(true);\n\t\tassertThat(pe.hasPermission(mock(Authentication.class), new Object(), \"READ\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolvePermissionNonEnglishLocale() {\n\t\tLocale systemLocale = Locale.getDefault();\n\t\tLocale.setDefault(new Locale(\"tr\"));\n\t\tAclService service = mock(AclService.class);\n\t\tAclPermissionEvaluator pe = new AclPermissionEvaluator(service);\n\t\tObjectIdentity oid = mock(ObjectIdentity.class);\n\t\tObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);\n\t\tgiven(oidStrategy.getObjectIdentity(any(Object.class))).willReturn(oid);\n\t\tpe.setObjectIdentityRetrievalStrategy(oidStrategy);\n\t\tpe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));\n\t\tAcl acl = mock(Acl.class);\n\t\tgiven(service.readAclById(any(ObjectIdentity.class), anyList())).willReturn(acl);\n\t\tgiven(acl.isGranted(anyList(), anyList(), eq(false))).willReturn(true);\n\t\tassertThat(pe.hasPermission(mock(Authentication.class), new Object(), \"write\")).isTrue();\n\t\tLocale.setDefault(systemLocale);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/TargetObject.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls;\n\n/**\n * Dummy domain object class\n *\n * @author Luke Taylor\n */\npublic final class TargetObject {\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/TargetObjectWithUUID.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls;\n\nimport java.util.UUID;\n\n/**\n * Dummy domain object class with a {@link UUID} for the Id.\n *\n * @author Luke Taylor\n */\npublic final class TargetObjectWithUUID {\n\n\tprivate UUID id;\n\n\tpublic UUID getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic void setId(UUID id) {\n\t\tthis.id = id;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/AccessControlImplEntryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AuditableAccessControlEntry;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.Sid;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link AccessControlEntryImpl}.\n *\n * @author Andrei Stefan\n */\npublic class AccessControlImplEntryTests {\n\n\t@Test\n\tpublic void testConstructorRequiredFields() {\n\t\t// Check Acl field is present\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AccessControlEntryImpl(null, null,\n\t\t\t\tnew PrincipalSid(\"johndoe\"), BasePermission.ADMINISTRATION, true, true, true));\n\t\t// Check Sid field is present\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AccessControlEntryImpl(null, mock(Acl.class), null,\n\t\t\t\tBasePermission.ADMINISTRATION, true, true, true));\n\t\t// Check Permission field is present\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AccessControlEntryImpl(null, mock(Acl.class),\n\t\t\t\tnew PrincipalSid(\"johndoe\"), null, true, true, true));\n\t}\n\n\t@Test\n\tpublic void testAccessControlEntryImplGetters() {\n\t\tAcl mockAcl = mock(Acl.class);\n\t\tSid sid = new PrincipalSid(\"johndoe\");\n\t\t// Create a sample entry\n\t\tAccessControlEntry ace = new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, true, true,\n\t\t\t\ttrue);\n\t\t// and check every get() method\n\t\tassertThat(ace.getId()).isEqualTo(1L);\n\t\tassertThat(ace.getAcl()).isEqualTo(mockAcl);\n\t\tassertThat(ace.getSid()).isEqualTo(sid);\n\t\tassertThat(ace.isGranting()).isTrue();\n\t\tassertThat(ace.getPermission()).isEqualTo(BasePermission.ADMINISTRATION);\n\t\tassertThat(((AuditableAccessControlEntry) ace).isAuditFailure()).isTrue();\n\t\tassertThat(((AuditableAccessControlEntry) ace).isAuditSuccess()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testEquals() {\n\t\tfinal Acl mockAcl = mock(Acl.class);\n\t\tfinal ObjectIdentity oid = mock(ObjectIdentity.class);\n\t\tgiven(mockAcl.getObjectIdentity()).willReturn(oid);\n\t\tSid sid = new PrincipalSid(\"johndoe\");\n\t\tAccessControlEntry ace = new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, true, true,\n\t\t\t\ttrue);\n\t\tassertThat(ace).isNotNull();\n\t\tassertThat(ace).isNotEqualTo(100L);\n\t\tassertThat(ace).isEqualTo(ace);\n\t\tassertThat(ace)\n\t\t\t.isEqualTo(new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, true, true, true));\n\t\tassertThat(ace).isNotEqualTo(\n\t\t\t\tnew AccessControlEntryImpl(2L, mockAcl, sid, BasePermission.ADMINISTRATION, true, true, true));\n\t\tassertThat(ace).isNotEqualTo(new AccessControlEntryImpl(1L, mockAcl, new PrincipalSid(\"scott\"),\n\t\t\t\tBasePermission.ADMINISTRATION, true, true, true));\n\t\tassertThat(ace)\n\t\t\t.isNotEqualTo(new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.WRITE, true, true, true));\n\t\tassertThat(ace).isNotEqualTo(\n\t\t\t\tnew AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, false, true, true));\n\t\tassertThat(ace).isNotEqualTo(\n\t\t\t\tnew AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, true, false, true));\n\t\tassertThat(ace).isNotEqualTo(\n\t\t\t\tnew AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, true, true, false));\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImplTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class AclAuthorizationStrategyImplTests {\n\n\tSecurityContext context;\n\n\t@Mock\n\tAcl acl;\n\n\t@Mock\n\tSecurityContextHolderStrategy securityContextHolderStrategy;\n\n\tGrantedAuthority authority;\n\n\tAclAuthorizationStrategyImpl strategy;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authority = new SimpleGrantedAuthority(\"ROLE_AUTH\");\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"foo\", \"bar\",\n\t\t\t\tArrays.asList(this.authority));\n\t\tauthentication.setAuthenticated(true);\n\t\tthis.context = new SecurityContextImpl(authentication);\n\t\tSecurityContextHolder.setContext(this.context);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t// gh-4085\n\t@Test\n\tpublic void securityCheckWhenCustomAuthorityThenNameIsUsed() {\n\t\tthis.strategy = new AclAuthorizationStrategyImpl(new CustomAuthority());\n\t\tthis.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t}\n\n\t// gh-9425\n\t@Test\n\tpublic void securityCheckWhenAclOwnedByGrantedAuthority() {\n\t\tgiven(this.acl.getOwner()).willReturn(new GrantedAuthoritySid(\"ROLE_AUTH\"));\n\t\tthis.strategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority(\"ROLE_SYSTEM_ADMIN\"));\n\t\tthis.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t}\n\n\t@Test\n\tpublic void securityCheckWhenRoleReachableByHierarchyThenAuthorized() {\n\t\tgiven(this.acl.getOwner()).willReturn(new GrantedAuthoritySid(\"ROLE_AUTH_B\"));\n\t\tthis.strategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority(\"ROLE_SYSTEM_ADMIN\"));\n\t\tthis.strategy.setRoleHierarchy(RoleHierarchyImpl.fromHierarchy(\"ROLE_AUTH > ROLE_AUTH_B\"));\n\t\tassertThatNoException()\n\t\t\t.isThrownBy(() -> this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL));\n\t}\n\n\t@Test\n\tpublic void securityCheckWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(this.context);\n\t\tgiven(this.acl.getOwner()).willReturn(new GrantedAuthoritySid(\"ROLE_AUTH\"));\n\t\tthis.strategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority(\"ROLE_SYSTEM_ADMIN\"));\n\t\tthis.strategy.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tthis.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tclass CustomAuthority implements GrantedAuthority {\n\n\t\t@Override\n\t\tpublic String getAuthority() {\n\t\t\treturn AclAuthorizationStrategyImplTests.this.authority.getAuthority();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/AclImplTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AlreadyExistsException;\nimport org.springframework.security.acls.model.AuditableAccessControlEntry;\nimport org.springframework.security.acls.model.AuditableAcl;\nimport org.springframework.security.acls.model.ChildrenExistException;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.MutableAclService;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.PermissionGrantingStrategy;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.util.FieldUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link AclImpl}.\n *\n * @author Andrei Stefan\n */\npublic class AclImplTests {\n\n\tprivate static final String TARGET_CLASS = \"org.springframework.security.acls.TargetObject\";\n\n\tprivate static final List<Permission> READ = Arrays.asList(BasePermission.READ);\n\n\tprivate static final List<Permission> WRITE = Arrays.asList(BasePermission.WRITE);\n\n\tprivate static final List<Permission> CREATE = Arrays.asList(BasePermission.CREATE);\n\n\tprivate static final List<Permission> DELETE = Arrays.asList(BasePermission.DELETE);\n\n\tprivate static final List<Sid> SCOTT = Arrays.asList((Sid) new PrincipalSid(\"scott\"));\n\n\tprivate static final List<Sid> BEN = Arrays.asList((Sid) new PrincipalSid(\"ben\"));\n\n\tAuthentication auth = new TestingAuthenticationToken(\"joe\", \"ignored\", \"ROLE_ADMINISTRATOR\");\n\n\tAclAuthorizationStrategy authzStrategy;\n\n\tPermissionGrantingStrategy pgs;\n\n\tAuditLogger mockAuditLogger;\n\n\tObjectIdentity objectIdentity = new ObjectIdentityImpl(TARGET_CLASS, 100);\n\n\tprivate DefaultPermissionFactory permissionFactory;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tthis.authzStrategy = mock(AclAuthorizationStrategy.class);\n\t\tthis.mockAuditLogger = mock(AuditLogger.class);\n\t\tthis.pgs = new DefaultPermissionGrantingStrategy(this.mockAuditLogger);\n\t\tthis.auth.setAuthenticated(true);\n\t\tthis.permissionFactory = new DefaultPermissionFactory();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorsRejectNullObjectIdentity() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new AclImpl(null, 1, this.authzStrategy, this.pgs, null, null, true, new PrincipalSid(\"joe\")));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AclImpl(null, 1, this.authzStrategy, this.mockAuditLogger));\n\t}\n\n\t@Test\n\tpublic void constructorsRejectNullId() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AclImpl(this.objectIdentity, null, this.authzStrategy,\n\t\t\t\tthis.pgs, null, null, true, new PrincipalSid(\"joe\")));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AclImpl(this.objectIdentity, null, this.authzStrategy, this.mockAuditLogger));\n\t}\n\n\t@Test\n\tpublic void constructorsRejectNullAclAuthzStrategy() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AclImpl(this.objectIdentity, 1, null,\n\t\t\t\tnew DefaultPermissionGrantingStrategy(this.mockAuditLogger), null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\")));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AclImpl(this.objectIdentity, 1, null, this.mockAuditLogger));\n\t}\n\n\t@Test\n\tpublic void insertAceRejectsNullParameters() {\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> acl.insertAce(0, null, new GrantedAuthoritySid(\"ROLE_IGNORED\"), true));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> acl.insertAce(0, BasePermission.READ, null, true));\n\t}\n\n\t@Test\n\tpublic void insertAceAddsElementAtCorrectIndex() {\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tMockAclService service = new MockAclService();\n\t\t// Insert one permission\n\t\tacl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_TEST1\"), true);\n\t\tservice.updateAcl(acl);\n\t\t// Check it was successfully added\n\t\tassertThat(acl.getEntries()).hasSize(1);\n\t\tassertThat(acl).isEqualTo(acl.getEntries().get(0).getAcl());\n\t\tassertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(0).getPermission());\n\t\tassertThat(acl.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST1\"));\n\t\t// Add a second permission\n\t\tacl.insertAce(1, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_TEST2\"), true);\n\t\tservice.updateAcl(acl);\n\t\t// Check it was added on the last position\n\t\tassertThat(acl.getEntries()).hasSize(2);\n\t\tassertThat(acl).isEqualTo(acl.getEntries().get(1).getAcl());\n\t\tassertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(1).getPermission());\n\t\tassertThat(acl.getEntries().get(1).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST2\"));\n\t\t// Add a third permission, after the first one\n\t\tacl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid(\"ROLE_TEST3\"), false);\n\t\tservice.updateAcl(acl);\n\t\tassertThat(acl.getEntries()).hasSize(3);\n\t\t// Check the third entry was added between the two existent ones\n\t\tassertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(0).getPermission());\n\t\tassertThat(acl.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST1\"));\n\t\tassertThat(BasePermission.WRITE).isEqualTo(acl.getEntries().get(1).getPermission());\n\t\tassertThat(acl.getEntries().get(1).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST3\"));\n\t\tassertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(2).getPermission());\n\t\tassertThat(acl.getEntries().get(2).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST2\"));\n\t}\n\n\t@Test\n\tpublic void insertAceFailsForNonExistentElement() {\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tMockAclService service = new MockAclService();\n\t\t// Insert one permission\n\t\tacl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_TEST1\"), true);\n\t\tservice.updateAcl(acl);\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> acl.insertAce(55, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_TEST2\"), true));\n\t}\n\n\t@Test\n\tpublic void deleteAceKeepsInitialOrdering() {\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tMockAclService service = new MockAclService();\n\t\t// Add several permissions\n\t\tacl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_TEST1\"), true);\n\t\tacl.insertAce(1, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_TEST2\"), true);\n\t\tacl.insertAce(2, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_TEST3\"), true);\n\t\tservice.updateAcl(acl);\n\t\t// Delete first permission and check the order of the remaining permissions is\n\t\t// kept\n\t\tacl.deleteAce(0);\n\t\tassertThat(acl.getEntries()).hasSize(2);\n\t\tassertThat(acl.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST2\"));\n\t\tassertThat(acl.getEntries().get(1).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST3\"));\n\t\t// Add one more permission and remove the permission in the middle\n\t\tacl.insertAce(2, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_TEST4\"), true);\n\t\tservice.updateAcl(acl);\n\t\tacl.deleteAce(1);\n\t\tassertThat(acl.getEntries()).hasSize(2);\n\t\tassertThat(acl.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST2\"));\n\t\tassertThat(acl.getEntries().get(1).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST4\"));\n\t\t// Remove remaining permissions\n\t\tacl.deleteAce(1);\n\t\tacl.deleteAce(0);\n\t\tassertThat(acl.getEntries()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void deleteAceFailsForNonExistentElement() {\n\t\tAclAuthorizationStrategyImpl strategy = new AclAuthorizationStrategyImpl(\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_OWNERSHIP\"), new SimpleGrantedAuthority(\"ROLE_AUDITING\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_GENERAL\"));\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, (1), strategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tassertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> acl.deleteAce(99));\n\t}\n\n\t@Test\n\tpublic void isGrantingRejectsEmptyParameters() {\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tSid ben = new PrincipalSid(\"ben\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> acl.isGranted(new ArrayList<>(0), Arrays.asList(ben), false));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> acl.isGranted(READ, new ArrayList<>(0), false));\n\t}\n\n\t@Test\n\tpublic void isGrantingGrantsAccessForAclWithNoParent() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"ben\", \"ignored\", \"ROLE_GENERAL\", \"ROLE_GUEST\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity rootOid = new ObjectIdentityImpl(TARGET_CLASS, 100);\n\t\t// Create an ACL which owner is not the authenticated principal\n\t\tMutableAcl rootAcl = new AclImpl(rootOid, 1, this.authzStrategy, this.pgs, null, null, false,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\t// Grant some permissions\n\t\trootAcl.insertAce(0, BasePermission.READ, new PrincipalSid(\"ben\"), false);\n\t\trootAcl.insertAce(1, BasePermission.WRITE, new PrincipalSid(\"scott\"), true);\n\t\trootAcl.insertAce(2, BasePermission.WRITE, new PrincipalSid(\"rod\"), false);\n\t\trootAcl.insertAce(3, BasePermission.WRITE, new GrantedAuthoritySid(\"WRITE_ACCESS_ROLE\"), true);\n\t\t// Check permissions granting\n\t\tList<Permission> permissions = Arrays.asList(BasePermission.READ, BasePermission.CREATE);\n\t\tList<Sid> sids = Arrays.asList(new PrincipalSid(\"ben\"), new GrantedAuthoritySid(\"ROLE_GUEST\"));\n\t\tassertThat(rootAcl.isGranted(permissions, sids, false)).isFalse();\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> rootAcl.isGranted(permissions, SCOTT, false));\n\t\tassertThat(rootAcl.isGranted(WRITE, SCOTT, false)).isTrue();\n\t\tassertThat(rootAcl.isGranted(WRITE,\n\t\t\t\tArrays.asList(new PrincipalSid(\"rod\"), new GrantedAuthoritySid(\"WRITE_ACCESS_ROLE\")), false))\n\t\t\t.isFalse();\n\t\tassertThat(rootAcl.isGranted(WRITE,\n\t\t\t\tArrays.asList(new GrantedAuthoritySid(\"WRITE_ACCESS_ROLE\"), new PrincipalSid(\"rod\")), false))\n\t\t\t.isTrue();\n\t\t// Change the type of the Sid and check the granting process\n\t\tassertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> rootAcl.isGranted(WRITE,\n\t\t\t\tArrays.asList(new GrantedAuthoritySid(\"rod\"), new PrincipalSid(\"WRITE_ACCESS_ROLE\")), false));\n\t}\n\n\t@Test\n\tpublic void isGrantingGrantsAccessForInheritableAcls() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"ben\", \"ignored\", \"ROLE_GENERAL\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, 100);\n\t\tObjectIdentity parentOid1 = new ObjectIdentityImpl(TARGET_CLASS, 101);\n\t\tObjectIdentity parentOid2 = new ObjectIdentityImpl(TARGET_CLASS, 102);\n\t\tObjectIdentity childOid1 = new ObjectIdentityImpl(TARGET_CLASS, 103);\n\t\tObjectIdentity childOid2 = new ObjectIdentityImpl(TARGET_CLASS, 104);\n\t\t// Create ACLs\n\t\tPrincipalSid joe = new PrincipalSid(\"joe\");\n\t\tMutableAcl grandParentAcl = new AclImpl(grandParentOid, 1, this.authzStrategy, this.pgs, null, null, false,\n\t\t\t\tjoe);\n\t\tMutableAcl parentAcl1 = new AclImpl(parentOid1, 2, this.authzStrategy, this.pgs, null, null, true, joe);\n\t\tMutableAcl parentAcl2 = new AclImpl(parentOid2, 3, this.authzStrategy, this.pgs, null, null, true, joe);\n\t\tMutableAcl childAcl1 = new AclImpl(childOid1, 4, this.authzStrategy, this.pgs, null, null, true, joe);\n\t\tMutableAcl childAcl2 = new AclImpl(childOid2, 4, this.authzStrategy, this.pgs, null, null, false, joe);\n\t\t// Create hierarchies\n\t\tchildAcl2.setParent(childAcl1);\n\t\tchildAcl1.setParent(parentAcl1);\n\t\tparentAcl2.setParent(grandParentAcl);\n\t\tparentAcl1.setParent(grandParentAcl);\n\t\t// Add some permissions\n\t\tgrandParentAcl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_USER_READ\"), true);\n\t\tgrandParentAcl.insertAce(1, BasePermission.WRITE, new PrincipalSid(\"ben\"), true);\n\t\tgrandParentAcl.insertAce(2, BasePermission.DELETE, new PrincipalSid(\"ben\"), false);\n\t\tgrandParentAcl.insertAce(3, BasePermission.DELETE, new PrincipalSid(\"scott\"), true);\n\t\tparentAcl1.insertAce(0, BasePermission.READ, new PrincipalSid(\"scott\"), true);\n\t\tparentAcl1.insertAce(1, BasePermission.DELETE, new PrincipalSid(\"scott\"), false);\n\t\tparentAcl2.insertAce(0, BasePermission.CREATE, new PrincipalSid(\"ben\"), true);\n\t\tchildAcl1.insertAce(0, BasePermission.CREATE, new PrincipalSid(\"scott\"), true);\n\t\t// Check granting process for parent1\n\t\tassertThat(parentAcl1.isGranted(READ, SCOTT, false)).isTrue();\n\t\tassertThat(parentAcl1.isGranted(READ, Arrays.asList((Sid) new GrantedAuthoritySid(\"ROLE_USER_READ\")), false))\n\t\t\t.isTrue();\n\t\tassertThat(parentAcl1.isGranted(WRITE, BEN, false)).isTrue();\n\t\tassertThat(parentAcl1.isGranted(DELETE, BEN, false)).isFalse();\n\t\tassertThat(parentAcl1.isGranted(DELETE, SCOTT, false)).isFalse();\n\t\t// Check granting process for parent2\n\t\tassertThat(parentAcl2.isGranted(CREATE, BEN, false)).isTrue();\n\t\tassertThat(parentAcl2.isGranted(WRITE, BEN, false)).isTrue();\n\t\tassertThat(parentAcl2.isGranted(DELETE, BEN, false)).isFalse();\n\t\t// Check granting process for child1\n\t\tassertThat(childAcl1.isGranted(CREATE, SCOTT, false)).isTrue();\n\t\tassertThat(childAcl1.isGranted(READ, Arrays.asList((Sid) new GrantedAuthoritySid(\"ROLE_USER_READ\")), false))\n\t\t\t.isTrue();\n\t\tassertThat(childAcl1.isGranted(DELETE, BEN, false)).isFalse();\n\t\t// Check granting process for child2 (doesn't inherit the permissions from its\n\t\t// parent)\n\t\tassertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> childAcl2.isGranted(CREATE, SCOTT, false));\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> childAcl2.isGranted(CREATE, Arrays.asList((Sid) new PrincipalSid(\"joe\")), false));\n\t}\n\n\t@Test\n\tpublic void updatedAceValuesAreCorrectlyReflectedInAcl() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"ben\", \"ignored\", \"ROLE_GENERAL\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, false,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tMockAclService service = new MockAclService();\n\t\tacl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_USER_READ\"), true);\n\t\tacl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid(\"ROLE_USER_READ\"), true);\n\t\tacl.insertAce(2, BasePermission.CREATE, new PrincipalSid(\"ben\"), true);\n\t\tservice.updateAcl(acl);\n\t\tassertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(0).getPermission());\n\t\tassertThat(BasePermission.WRITE).isEqualTo(acl.getEntries().get(1).getPermission());\n\t\tassertThat(BasePermission.CREATE).isEqualTo(acl.getEntries().get(2).getPermission());\n\t\t// Change each permission\n\t\tacl.updateAce(0, BasePermission.CREATE);\n\t\tacl.updateAce(1, BasePermission.DELETE);\n\t\tacl.updateAce(2, BasePermission.READ);\n\t\t// Check the change was successfully made\n\t\tassertThat(BasePermission.CREATE).isEqualTo(acl.getEntries().get(0).getPermission());\n\t\tassertThat(BasePermission.DELETE).isEqualTo(acl.getEntries().get(1).getPermission());\n\t\tassertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(2).getPermission());\n\t}\n\n\t@Test\n\tpublic void auditableEntryFlagsAreUpdatedCorrectly() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"ben\", \"ignored\", \"ROLE_AUDITING\", \"ROLE_GENERAL\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, false,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tMockAclService service = new MockAclService();\n\t\tacl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_USER_READ\"), true);\n\t\tacl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid(\"ROLE_USER_READ\"), true);\n\t\tservice.updateAcl(acl);\n\t\tassertThat(((AuditableAccessControlEntry) acl.getEntries().get(0)).isAuditFailure()).isFalse();\n\t\tassertThat(((AuditableAccessControlEntry) acl.getEntries().get(1)).isAuditFailure()).isFalse();\n\t\tassertThat(((AuditableAccessControlEntry) acl.getEntries().get(0)).isAuditSuccess()).isFalse();\n\t\tassertThat(((AuditableAccessControlEntry) acl.getEntries().get(1)).isAuditSuccess()).isFalse();\n\t\t// Change each permission\n\t\t((AuditableAcl) acl).updateAuditing(0, true, true);\n\t\t((AuditableAcl) acl).updateAuditing(1, true, true);\n\t\t// Check the change was successfuly made\n\t\tassertThat(acl.getEntries()).extracting(\"auditSuccess\").containsOnly(true, true);\n\t\tassertThat(acl.getEntries()).extracting(\"auditFailure\").containsOnly(true, true);\n\t}\n\n\t@Test\n\tpublic void gettersAndSettersAreConsistent() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"ben\", \"ignored\", \"ROLE_GENERAL\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, (100));\n\t\tObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, (101));\n\t\tMutableAcl acl = new AclImpl(identity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tMutableAcl parentAcl = new AclImpl(identity2, 2, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tMockAclService service = new MockAclService();\n\t\tacl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid(\"ROLE_USER_READ\"), true);\n\t\tacl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid(\"ROLE_USER_READ\"), true);\n\t\tservice.updateAcl(acl);\n\t\tassertThat(1).isEqualTo(acl.getId());\n\t\tassertThat(identity).isEqualTo(acl.getObjectIdentity());\n\t\tassertThat(new PrincipalSid(\"joe\")).isEqualTo(acl.getOwner());\n\t\tassertThat(acl.getParentAcl()).isNull();\n\t\tassertThat(acl.isEntriesInheriting()).isTrue();\n\t\tassertThat(acl.getEntries()).hasSize(2);\n\t\tacl.setParent(parentAcl);\n\t\tassertThat(parentAcl).isEqualTo(acl.getParentAcl());\n\t\tacl.setEntriesInheriting(false);\n\t\tassertThat(acl.isEntriesInheriting()).isFalse();\n\t\tacl.setOwner(new PrincipalSid(\"ben\"));\n\t\tassertThat(new PrincipalSid(\"ben\")).isEqualTo(acl.getOwner());\n\t}\n\n\t@Test\n\tpublic void isSidLoadedBehavesAsExpected() {\n\t\tList<Sid> loadedSids = Arrays.asList(new PrincipalSid(\"ben\"), new GrantedAuthoritySid(\"ROLE_IGNORED\"));\n\t\tMutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, loadedSids, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tassertThat(acl.isSidLoaded(loadedSids)).isTrue();\n\t\tassertThat(acl.isSidLoaded(Arrays.asList(new GrantedAuthoritySid(\"ROLE_IGNORED\"), new PrincipalSid(\"ben\"))))\n\t\t\t.isTrue();\n\t\tassertThat(acl.isSidLoaded(Arrays.asList((Sid) new GrantedAuthoritySid(\"ROLE_IGNORED\")))).isTrue();\n\t\tassertThat(acl.isSidLoaded(BEN)).isTrue();\n\t\tassertThat(acl.isSidLoaded(null)).isTrue();\n\t\tassertThat(acl.isSidLoaded(new ArrayList<>(0))).isTrue();\n\t\tassertThat(acl.isSidLoaded(\n\t\t\t\tArrays.asList(new GrantedAuthoritySid(\"ROLE_IGNORED\"), new GrantedAuthoritySid(\"ROLE_IGNORED\"))))\n\t\t\t.isTrue();\n\t\tassertThat(acl.isSidLoaded(\n\t\t\t\tArrays.asList(new GrantedAuthoritySid(\"ROLE_GENERAL\"), new GrantedAuthoritySid(\"ROLE_IGNORED\"))))\n\t\t\t.isFalse();\n\t\tassertThat(acl.isSidLoaded(\n\t\t\t\tArrays.asList(new GrantedAuthoritySid(\"ROLE_IGNORED\"), new GrantedAuthoritySid(\"ROLE_GENERAL\"))))\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void insertAceRaisesNotFoundExceptionForIndexLessThanZero() {\n\t\tAclImpl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> acl.insertAce(-1, mock(Permission.class), mock(Sid.class), true));\n\t}\n\n\t@Test\n\tpublic void deleteAceRaisesNotFoundExceptionForIndexLessThanZero() {\n\t\tAclImpl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tassertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> acl.deleteAce(-1));\n\t}\n\n\t@Test\n\tpublic void insertAceRaisesNotFoundExceptionForIndexGreaterThanSize() {\n\t\tAclImpl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\t// Insert at zero, OK.\n\t\tacl.insertAce(0, mock(Permission.class), mock(Sid.class), true);\n\t\t// Size is now 1\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> acl.insertAce(2, mock(Permission.class), mock(Sid.class), true));\n\t}\n\n\t// SEC-1151\n\t@Test\n\tpublic void deleteAceRaisesNotFoundExceptionForIndexEqualToSize() {\n\t\tAclImpl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tacl.insertAce(0, mock(Permission.class), mock(Sid.class), true);\n\t\t// Size is now 1\n\t\tassertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> acl.deleteAce(1));\n\t}\n\n\t// SEC-1795\n\t@Test\n\tpublic void changingParentIsSuccessful() {\n\t\tAclImpl parentAcl = new AclImpl(this.objectIdentity, 1L, this.authzStrategy, this.mockAuditLogger);\n\t\tAclImpl childAcl = new AclImpl(this.objectIdentity, 2L, this.authzStrategy, this.mockAuditLogger);\n\t\tAclImpl changeParentAcl = new AclImpl(this.objectIdentity, 3L, this.authzStrategy, this.mockAuditLogger);\n\t\tchildAcl.setParent(parentAcl);\n\t\tchildAcl.setParent(changeParentAcl);\n\t}\n\n\t// SEC-2342\n\t@Test\n\tpublic void maskPermissionGrantingStrategy() {\n\t\tDefaultPermissionGrantingStrategy maskPgs = new MaskPermissionGrantingStrategy(this.mockAuditLogger);\n\t\tMockAclService service = new MockAclService();\n\t\tAclImpl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, maskPgs, null, null, true,\n\t\t\t\tnew PrincipalSid(\"joe\"));\n\t\tPermission permission = this.permissionFactory\n\t\t\t.buildFromMask(BasePermission.READ.getMask() | BasePermission.WRITE.getMask());\n\t\tSid sid = new PrincipalSid(\"ben\");\n\t\tacl.insertAce(0, permission, sid, true);\n\t\tservice.updateAcl(acl);\n\t\tList<Permission> permissions = Arrays.asList(BasePermission.READ);\n\t\tList<Sid> sids = Arrays.asList(sid);\n\t\tassertThat(acl.isGranted(permissions, sids, false)).isTrue();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void hashCodeWithoutStackOverFlow() throws Exception {\n\t\tSid sid = new PrincipalSid(\"pSid\");\n\t\tObjectIdentity oid = new ObjectIdentityImpl(\"type\", 1);\n\t\tAclAuthorizationStrategy authStrategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority(\"role\"));\n\t\tPermissionGrantingStrategy grantingStrategy = new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());\n\t\tAclImpl acl = new AclImpl(oid, 1L, authStrategy, grantingStrategy, null, null, false, sid);\n\t\tAccessControlEntryImpl ace = new AccessControlEntryImpl(1L, acl, sid, BasePermission.READ, true, true, true);\n\t\tField fieldAces = FieldUtils.getField(AclImpl.class, \"aces\");\n\t\tfieldAces.setAccessible(true);\n\t\tList<AccessControlEntryImpl> aces = (List<AccessControlEntryImpl>) fieldAces.get(acl);\n\t\taces.add(ace);\n\t\tace.hashCode();\n\t}\n\n\tprivate static class MaskPermissionGrantingStrategy extends DefaultPermissionGrantingStrategy {\n\n\t\tMaskPermissionGrantingStrategy(AuditLogger auditLogger) {\n\t\t\tsuper(auditLogger);\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean isGranted(AccessControlEntry ace, Permission p) {\n\t\t\tif (p.getMask() != 0) {\n\t\t\t\treturn (p.getMask() & ace.getPermission().getMask()) != 0;\n\t\t\t}\n\t\t\treturn super.isGranted(ace, p);\n\t\t}\n\n\t}\n\n\tprivate class MockAclService implements MutableAclService {\n\n\t\t@Override\n\t\tpublic MutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsException {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren) throws ChildrenExistException {\n\t\t}\n\n\t\t/*\n\t\t * Mock implementation that populates the aces list with fully initialized\n\t\t * AccessControlEntries\n\t\t *\n\t\t * @see org.springframework.security.acls.MutableAclService#updateAcl(org.\n\t\t * springframework .security.acls.MutableAcl)\n\t\t */\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {\n\t\t\tList<AccessControlEntry> oldAces = acl.getEntries();\n\t\t\tField acesField = FieldUtils.getField(AclImpl.class, \"aces\");\n\t\t\tacesField.setAccessible(true);\n\t\t\tList newAces;\n\t\t\ttry {\n\t\t\t\tnewAces = (List) acesField.get(acl);\n\t\t\t\tnewAces.clear();\n\t\t\t\tfor (int i = 0; i < oldAces.size(); i++) {\n\t\t\t\t\tAccessControlEntry ac = oldAces.get(i);\n\t\t\t\t\t// Just give an ID to all this acl's aces, rest of the fields are just\n\t\t\t\t\t// copied\n\t\t\t\t\tnewAces.add(new AccessControlEntryImpl((i + 1), ac.getAcl(), ac.getSid(), ac.getPermission(),\n\t\t\t\t\t\t\tac.isGranting(), ((AuditableAccessControlEntry) ac).isAuditSuccess(),\n\t\t\t\t\t\t\t((AuditableAccessControlEntry) ac).isAuditFailure()));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (IllegalAccessException ex) {\n\t\t\t\tex.printStackTrace();\n\t\t\t}\n\t\t\treturn acl;\n\t\t}\n\n\t\t@Override\n\t\tpublic List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Acl readAclById(ObjectIdentity object) throws NotFoundException {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)\n\t\t\t\tthrows NotFoundException {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/AclImplementationSecurityCheckTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Test class for {@link AclAuthorizationStrategyImpl} and {@link AclImpl} security\n * checks.\n *\n * @author Andrei Stefan\n */\npublic class AclImplementationSecurityCheckTests {\n\n\tprivate static final String TARGET_CLASS = \"org.springframework.security.acls.TargetObject\";\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testSecurityCheckNoACEs() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_GENERAL\", \"ROLE_AUDITING\",\n\t\t\t\t\"ROLE_OWNERSHIP\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\t\tAclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_OWNERSHIP\"), new SimpleGrantedAuthority(\"ROLE_AUDITING\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_GENERAL\"));\n\t\tAcl acl = new AclImpl(identity, 1L, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\taclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t\taclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING);\n\t\taclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);\n\t\t// Create another authorization strategy\n\t\tAclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl(\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_ONE\"), new SimpleGrantedAuthority(\"ROLE_TWO\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_THREE\"));\n\t\tAcl acl2 = new AclImpl(identity, 1L, aclAuthorizationStrategy2, new ConsoleAuditLogger());\n\t\t// Check access in case the principal has no authorization rights\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_GENERAL));\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_AUDITING));\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_OWNERSHIP));\n\t}\n\n\t@Test\n\tpublic void testSecurityCheckWithMultipleACEs() {\n\t\t// Create a simple authentication with ROLE_GENERAL\n\t\tAuthentication auth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_GENERAL\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\t\t// Authorization strategy will require a different role for each access\n\t\tAclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_OWNERSHIP\"), new SimpleGrantedAuthority(\"ROLE_AUDITING\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_GENERAL\"));\n\t\t// Let's give the principal the ADMINISTRATION permission, without\n\t\t// granting access\n\t\tMutableAcl aclFirstDeny = new AclImpl(identity, 1L, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\taclFirstDeny.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);\n\t\t// The CHANGE_GENERAL test should pass as the principal has ROLE_GENERAL\n\t\taclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_GENERAL);\n\t\t// The CHANGE_AUDITING and CHANGE_OWNERSHIP should fail since the\n\t\t// principal doesn't have these authorities,\n\t\t// nor granting access\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING));\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_OWNERSHIP));\n\t\t// Add granting access to this principal\n\t\taclFirstDeny.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);\n\t\t// and try again for CHANGE_AUDITING - the first ACE's granting flag\n\t\t// (false) will deny this access\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING));\n\t\t// Create another ACL and give the principal the ADMINISTRATION\n\t\t// permission, with granting access\n\t\tMutableAcl aclFirstAllow = new AclImpl(identity, 1L, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\taclFirstAllow.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);\n\t\t// The CHANGE_AUDITING test should pass as there is one ACE with\n\t\t// granting access\n\t\taclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);\n\t\t// Add a deny ACE and test again for CHANGE_AUDITING\n\t\taclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);\n\t\tassertThatNoException().isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING));\n\t\t// Create an ACL with no ACE\n\t\tMutableAcl aclNoACE = new AclImpl(identity, 1L, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\tassertThatExceptionOfType(NotFoundException.class).isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_AUDITING));\n\t\t// and still grant access for CHANGE_GENERAL\n\t\tassertThatNoException().isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_GENERAL));\n\t}\n\n\t@Test\n\tpublic void testSecurityCheckWithInheritableACEs() {\n\t\t// Create a simple authentication with ROLE_GENERAL\n\t\tAuthentication auth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_GENERAL\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);\n\t\t// Authorization strategy will require a different role for each access\n\t\tAclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_ONE\"), new SimpleGrantedAuthority(\"ROLE_TWO\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_GENERAL\"));\n\t\t// Let's give the principal an ADMINISTRATION permission, with granting\n\t\t// access\n\t\tMutableAcl parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\tparentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);\n\t\tMutableAcl childAcl = new AclImpl(identity, 2, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\t// Check against the 'child' acl, which doesn't offer any authorization\n\t\t// rights on CHANGE_OWNERSHIP\n\t\tassertThatExceptionOfType(NotFoundException.class).isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP));\n\t\t// Link the child with its parent and test again against the\n\t\t// CHANGE_OWNERSHIP right\n\t\tchildAcl.setParent(parentAcl);\n\t\tchildAcl.setEntriesInheriting(true);\n\t\tassertThatNoException().isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP));\n\t\t// Create a root parent and link it to the middle parent\n\t\tMutableAcl rootParentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\tparentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\trootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);\n\t\tparentAcl.setEntriesInheriting(true);\n\t\tparentAcl.setParent(rootParentAcl);\n\t\tchildAcl.setParent(parentAcl);\n\t\tassertThatNoException().isThrownBy(\n\t\t\t\t() -> aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP));\n\t}\n\n\t@Test\n\tpublic void testSecurityCheckPrincipalOwner() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ONE\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);\n\t\tAclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_OWNERSHIP\"), new SimpleGrantedAuthority(\"ROLE_AUDITING\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_GENERAL\"));\n\t\tAcl acl = new AclImpl(identity, 1, aclAuthorizationStrategy,\n\t\t\t\tnew DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), null, null, false,\n\t\t\t\tnew PrincipalSid(auth));\n\t\tassertThatNoException()\n\t\t\t.isThrownBy(() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL));\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING));\n\t\tassertThatNoException()\n\t\t\t.isThrownBy(() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP));\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/AuditLoggerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.AuditableAccessControlEntry;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Test class for {@link ConsoleAuditLogger}.\n *\n * @author Andrei Stefan\n */\npublic class AuditLoggerTests {\n\n\tprivate PrintStream console;\n\n\tprivate ByteArrayOutputStream bytes = new ByteArrayOutputStream();\n\n\tprivate ConsoleAuditLogger logger;\n\n\tprivate AuditableAccessControlEntry ace;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.logger = new ConsoleAuditLogger();\n\t\tthis.ace = mock(AuditableAccessControlEntry.class);\n\t\tthis.console = System.out;\n\t\tSystem.setOut(new PrintStream(this.bytes));\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSystem.setOut(this.console);\n\t\tthis.bytes.reset();\n\t}\n\n\t@Test\n\tpublic void nonAuditableAceIsIgnored() {\n\t\tAccessControlEntry ace = mock(AccessControlEntry.class);\n\t\tthis.logger.logIfNeeded(true, ace);\n\t\tassertThat(this.bytes.size()).isZero();\n\t}\n\n\t@Test\n\tpublic void successIsNotLoggedIfAceDoesntRequireSuccessAudit() {\n\t\tgiven(this.ace.isAuditSuccess()).willReturn(false);\n\t\tthis.logger.logIfNeeded(true, this.ace);\n\t\tassertThat(this.bytes.size()).isZero();\n\t}\n\n\t@Test\n\tpublic void successIsLoggedIfAceRequiresSuccessAudit() {\n\t\tgiven(this.ace.isAuditSuccess()).willReturn(true);\n\t\tthis.logger.logIfNeeded(true, this.ace);\n\t\tassertThat(this.bytes.toString()).startsWith(\"GRANTED due to ACE\");\n\t}\n\n\t@Test\n\tpublic void failureIsntLoggedIfAceDoesntRequireFailureAudit() {\n\t\tgiven(this.ace.isAuditFailure()).willReturn(false);\n\t\tthis.logger.logIfNeeded(false, this.ace);\n\t\tassertThat(this.bytes.size()).isZero();\n\t}\n\n\t@Test\n\tpublic void failureIsLoggedIfAceRequiresFailureAudit() {\n\t\tgiven(this.ace.isAuditFailure()).willReturn(true);\n\t\tthis.logger.logIfNeeded(false, this.ace);\n\t\tassertThat(this.bytes.toString()).startsWith(\"DENIED due to ACE\");\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/ObjectIdentityImplTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.model.ObjectIdentity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Tests for {@link ObjectIdentityImpl}.\n *\n * @author Andrei Stefan\n */\n@SuppressWarnings(\"unused\")\npublic class ObjectIdentityImplTests {\n\n\tprivate static final String DOMAIN_CLASS = \"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockIdDomainObject\";\n\n\t@Test\n\tpublic void constructorsRespectRequiredFields() {\n\t\t// Check one-argument constructor required field\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ObjectIdentityImpl(null));\n\t\t// Check String-Serializable constructor required field\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ObjectIdentityImpl(\"\", 1L));\n\t\t// Check Serializable parameter is not null\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ObjectIdentityImpl(DOMAIN_CLASS, null));\n\t\t// The correct way of using String-Serializable constructor\n\t\tassertThatNoException().isThrownBy(() -> new ObjectIdentityImpl(DOMAIN_CLASS, 1L));\n\t\t// Check the Class-Serializable constructor\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ObjectIdentityImpl(MockIdDomainObject.class, null));\n\t}\n\n\t@Test\n\tpublic void gettersReturnExpectedValues() {\n\t\tObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, 1L);\n\t\tassertThat(obj.getIdentifier()).isEqualTo(1L);\n\t\tassertThat(obj.getType()).isEqualTo(MockIdDomainObject.class.getName());\n\t}\n\n\t@Test\n\tpublic void testGetIdMethodConstraints() {\n\t\t// Check the getId() method is present\n\t\tassertThatExceptionOfType(IdentityUnavailableException.class)\n\t\t\t.isThrownBy(() -> new ObjectIdentityImpl(\"A_STRING_OBJECT\"));\n\t\t// getId() should return a non-null value\n\t\tMockIdDomainObject mockId = new MockIdDomainObject();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ObjectIdentityImpl(mockId));\n\t\t// getId() should return a Serializable object\n\t\tmockId.setId(new MockIdDomainObject());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ObjectIdentityImpl(mockId));\n\t\t// getId() should return a Serializable object\n\t\tmockId.setId(100L);\n\t\tassertThatNoException().isThrownBy(() -> new ObjectIdentityImpl(mockId));\n\t}\n\n\t@Test\n\tpublic void constructorRejectsInvalidTypeParameter() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ObjectIdentityImpl(\"\", 1L));\n\t}\n\n\t@Test\n\tpublic void testEquals() {\n\t\tObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, 1L);\n\t\tMockIdDomainObject mockObj = new MockIdDomainObject();\n\t\tmockObj.setId(1L);\n\t\tString string = \"SOME_STRING\";\n\t\tassertThat(string).isNotSameAs(obj);\n\t\tassertThat(obj).isNotNull();\n\t\tassertThat(obj).isNotEqualTo(\"DIFFERENT_OBJECT_TYPE\");\n\t\tassertThat(obj).isNotEqualTo(new ObjectIdentityImpl(DOMAIN_CLASS, 2L));\n\t\tassertThat(obj).isNotEqualTo(new ObjectIdentityImpl(\n\t\t\t\t\"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockOtherIdDomainObject\", 1L));\n\t\tassertThat(new ObjectIdentityImpl(DOMAIN_CLASS, 1L)).isEqualTo(obj);\n\t\tassertThat(new ObjectIdentityImpl(mockObj)).isEqualTo(obj);\n\t}\n\n\t@Test\n\tpublic void hashcodeIsDifferentForDifferentJavaTypes() {\n\t\tObjectIdentity obj = new ObjectIdentityImpl(Object.class, 1L);\n\t\tObjectIdentity obj2 = new ObjectIdentityImpl(String.class, 1L);\n\t\tassertThat(obj.hashCode()).isNotEqualTo(obj2.hashCode());\n\t}\n\n\t@Test\n\tpublic void longAndIntegerIdsWithSameValueAreEqualAndHaveSameHashcode() {\n\t\tObjectIdentity obj = new ObjectIdentityImpl(Object.class, 5L);\n\t\tObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, 5);\n\t\tassertThat(obj2).isEqualTo(obj);\n\t\tassertThat(obj2.hashCode()).isEqualTo(obj.hashCode());\n\t}\n\n\t@Test\n\tpublic void equalStringIdsAreEqualAndHaveSameHashcode() {\n\t\tObjectIdentity obj = new ObjectIdentityImpl(Object.class, \"1000\");\n\t\tObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, \"1000\");\n\t\tassertThat(obj2).isEqualTo(obj);\n\t\tassertThat(obj2.hashCode()).isEqualTo(obj.hashCode());\n\t}\n\n\t@Test\n\tpublic void stringAndNumericIdsAreNotEqual() {\n\t\tObjectIdentity obj = new ObjectIdentityImpl(Object.class, \"1000\");\n\t\tObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, 1000L);\n\t\tassertThat(obj).isNotEqualTo(obj2);\n\t}\n\n\tprivate class MockIdDomainObject {\n\n\t\tprivate Object id;\n\n\t\tpublic Object getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic void setId(Object id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t}\n\n\tprivate class MockOtherIdDomainObject {\n\n\t\tprivate Object id;\n\n\t\tpublic Object getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic void setId(Object id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/ObjectIdentityRetrievalStrategyImplTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ObjectIdentityRetrievalStrategyImpl}\n *\n * @author Andrei Stefan\n */\npublic class ObjectIdentityRetrievalStrategyImplTests {\n\n\t@Test\n\tpublic void testObjectIdentityCreation() {\n\t\tMockIdDomainObject domain = new MockIdDomainObject();\n\t\tdomain.setId(1);\n\t\tObjectIdentityRetrievalStrategy retStrategy = new ObjectIdentityRetrievalStrategyImpl();\n\t\tObjectIdentity identity = retStrategy.getObjectIdentity(domain);\n\t\tassertThat(identity).isNotNull();\n\t\tassertThat(new ObjectIdentityImpl(domain)).isEqualTo(identity);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate class MockIdDomainObject {\n\n\t\tprivate Object id;\n\n\t\tpublic Object getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tpublic void setId(Object id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/PermissionTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.model.Permission;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests classes associated with Permission.\n *\n * @author Ben Alex\n */\npublic class PermissionTests {\n\n\tprivate DefaultPermissionFactory permissionFactory;\n\n\t@BeforeEach\n\tpublic void createPermissionfactory() {\n\t\tthis.permissionFactory = new DefaultPermissionFactory();\n\t}\n\n\t@Test\n\tpublic void basePermissionTest() {\n\t\tPermission p = this.permissionFactory.buildFromName(\"WRITE\");\n\t\tassertThat(p).isNotNull();\n\t}\n\n\t@Test\n\tpublic void expectedIntegerValues() {\n\t\tassertThat(BasePermission.READ.getMask()).isEqualTo(1);\n\t\tassertThat(BasePermission.ADMINISTRATION.getMask()).isEqualTo(16);\n\t\tassertThat(new CumulativePermission().set(BasePermission.READ)\n\t\t\t.set(BasePermission.WRITE)\n\t\t\t.set(BasePermission.CREATE)\n\t\t\t.getMask()).isEqualTo(7);\n\t\tassertThat(new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask())\n\t\t\t.isEqualTo(17);\n\t}\n\n\t@Test\n\tpublic void fromInteger() {\n\t\tPermission permission = this.permissionFactory.buildFromMask(7);\n\t\tpermission = this.permissionFactory.buildFromMask(4);\n\t}\n\n\t@Test\n\tpublic void stringConversion() {\n\t\tthis.permissionFactory.registerPublicPermissions(SpecialPermission.class);\n\t\tassertThat(BasePermission.READ.toString()).isEqualTo(\"BasePermission[...............................R=1]\");\n\t\tassertThat(BasePermission.ADMINISTRATION.toString())\n\t\t\t.isEqualTo(\"BasePermission[...........................A....=16]\");\n\t\tassertThat(new CumulativePermission().set(BasePermission.READ).toString())\n\t\t\t.isEqualTo(\"CumulativePermission[...............................R=1]\");\n\t\tassertThat(\n\t\t\t\tnew CumulativePermission().set(SpecialPermission.ENTER).set(BasePermission.ADMINISTRATION).toString())\n\t\t\t.isEqualTo(\"CumulativePermission[..........................EA....=48]\");\n\t\tassertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString())\n\t\t\t.isEqualTo(\"CumulativePermission[...........................A...R=17]\");\n\t\tassertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION)\n\t\t\t.set(BasePermission.READ)\n\t\t\t.clear(BasePermission.ADMINISTRATION)\n\t\t\t.toString()).isEqualTo(\"CumulativePermission[...............................R=1]\");\n\t\tassertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION)\n\t\t\t.set(BasePermission.READ)\n\t\t\t.clear(BasePermission.ADMINISTRATION)\n\t\t\t.clear(BasePermission.READ)\n\t\t\t.toString()).isEqualTo(\"CumulativePermission[................................=0]\");\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/domain/SpecialPermission.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.domain;\n\nimport org.springframework.security.acls.model.Permission;\n\n/**\n * A test permission.\n *\n * @author Ben Alex\n */\npublic class SpecialPermission extends BasePermission {\n\n\tpublic static final Permission ENTER = new SpecialPermission(1 << 5, 'E'); // 32\n\n\tpublic static final Permission LEAVE = new SpecialPermission(1 << 6, 'L');\n\n\tprotected SpecialPermission(int mask, char code) {\n\t\tsuper(mask, code);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/AbstractBasicLookupStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport javax.sql.DataSource;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.concurrent.ConcurrentMapCache;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.security.acls.TargetObject;\nimport org.springframework.security.acls.TargetObjectWithUUID;\nimport org.springframework.security.acls.domain.AclAuthorizationStrategy;\nimport org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;\nimport org.springframework.security.acls.domain.BasePermission;\nimport org.springframework.security.acls.domain.ConsoleAuditLogger;\nimport org.springframework.security.acls.domain.DefaultPermissionFactory;\nimport org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;\nimport org.springframework.security.acls.domain.GrantedAuthoritySid;\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.domain.PrincipalSid;\nimport org.springframework.security.acls.domain.SpringCacheBasedAclCache;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AuditableAccessControlEntry;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link BasicLookupStrategy}\n *\n * @author Andrei Stefan\n */\npublic abstract class AbstractBasicLookupStrategyTests {\n\n\tprotected static final Sid BEN_SID = new PrincipalSid(\"ben\");\n\n\tprotected static final String TARGET_CLASS = TargetObject.class.getName();\n\n\tprotected static final String TARGET_CLASS_WITH_UUID = TargetObjectWithUUID.class.getName();\n\n\tprotected static final UUID OBJECT_IDENTITY_UUID = UUID.randomUUID();\n\n\tprotected static final Long OBJECT_IDENTITY_LONG_AS_UUID = 110L;\n\n\tprivate BasicLookupStrategy strategy;\n\n\tprivate static CacheManagerMock cacheManager;\n\n\tpublic abstract JdbcTemplate getJdbcTemplate();\n\n\tpublic abstract DataSource getDataSource();\n\n\t@BeforeAll\n\tpublic static void initCacheManaer() {\n\t\tcacheManager = new CacheManagerMock();\n\t\tcacheManager.addCache(\"basiclookuptestcache\");\n\t}\n\n\t@AfterAll\n\tpublic static void shutdownCacheManager() {\n\t\tcacheManager.clear();\n\t}\n\n\t@BeforeEach\n\tpublic void populateDatabase() {\n\t\tString query = \"INSERT INTO acl_sid(ID,PRINCIPAL,SID) VALUES (1,1,'ben');\"\n\t\t\t\t+ \"INSERT INTO acl_class(ID,CLASS) VALUES (2,'\" + TARGET_CLASS + \"');\"\n\t\t\t\t+ \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (1,2,100,null,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (2,2,101,1,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (3,2,102,2,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (1,1,0,1,1,1,0,0);\"\n\t\t\t\t+ \"INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (2,1,1,1,2,0,0,0);\"\n\t\t\t\t+ \"INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (3,2,0,1,8,1,0,0);\"\n\t\t\t\t+ \"INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (4,3,0,1,8,0,0,0);\";\n\t\tgetJdbcTemplate().execute(query);\n\t}\n\n\t@BeforeEach\n\tpublic void initializeBeans() {\n\t\tthis.strategy = new BasicLookupStrategy(getDataSource(), aclCache(), aclAuthStrategy(),\n\t\t\t\tnew DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()));\n\t\tthis.strategy.setPermissionFactory(new DefaultPermissionFactory());\n\t}\n\n\tprotected AclAuthorizationStrategy aclAuthStrategy() {\n\t\treturn new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority(\"ROLE_ADMINISTRATOR\"));\n\t}\n\n\tprotected SpringCacheBasedAclCache aclCache() {\n\t\treturn new SpringCacheBasedAclCache(getCache(), new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()),\n\t\t\t\tnew AclAuthorizationStrategyImpl(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t}\n\n\tprotected Cache getCache() {\n\t\tCache cache = cacheManager.getCacheManager().getCache(\"basiclookuptestcache\");\n\t\tcache.clear();\n\t\treturn cache;\n\t}\n\n\t@AfterEach\n\tpublic void emptyDatabase() {\n\t\tString query = \"DELETE FROM acl_entry;\" + \"DELETE FROM acl_object_identity WHERE ID = 9;\"\n\t\t\t\t+ \"DELETE FROM acl_object_identity WHERE ID = 8;\" + \"DELETE FROM acl_object_identity WHERE ID = 7;\"\n\t\t\t\t+ \"DELETE FROM acl_object_identity WHERE ID = 6;\" + \"DELETE FROM acl_object_identity WHERE ID = 5;\"\n\t\t\t\t+ \"DELETE FROM acl_object_identity WHERE ID = 4;\" + \"DELETE FROM acl_object_identity WHERE ID = 3;\"\n\t\t\t\t+ \"DELETE FROM acl_object_identity WHERE ID = 2;\" + \"DELETE FROM acl_object_identity WHERE ID = 1;\"\n\t\t\t\t+ \"DELETE FROM acl_class;\" + \"DELETE FROM acl_sid;\";\n\t\tgetJdbcTemplate().execute(query);\n\t}\n\n\t@Test\n\tpublic void testAclsRetrievalWithDefaultBatchSize() throws Exception {\n\t\tObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\t\tObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, 101L);\n\t\t// Deliberately use an integer for the child, to reproduce bug report in SEC-819\n\t\tObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, 102);\n\t\tMap<ObjectIdentity, Acl> map = this.strategy\n\t\t\t.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);\n\t\tcheckEntries(topParentOid, middleParentOid, childOid, map);\n\t}\n\n\t@Test\n\tpublic void testAclsRetrievalFromCacheOnly() throws Exception {\n\t\tObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, 100);\n\t\tObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, 101L);\n\t\tObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, 102L);\n\t\t// Objects were put in cache\n\t\tthis.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);\n\t\t// Let's empty the database to force acls retrieval from cache\n\t\temptyDatabase();\n\t\tMap<ObjectIdentity, Acl> map = this.strategy\n\t\t\t.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);\n\t\tcheckEntries(topParentOid, middleParentOid, childOid, map);\n\t}\n\n\t@Test\n\tpublic void testAclsRetrievalWithCustomBatchSize() throws Exception {\n\t\tObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\t\tObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, 101);\n\t\tObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, 102L);\n\t\t// Set a batch size to allow multiple database queries in order to retrieve all\n\t\t// acls\n\t\tthis.strategy.setBatchSize(1);\n\t\tMap<ObjectIdentity, Acl> map = this.strategy\n\t\t\t.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);\n\t\tcheckEntries(topParentOid, middleParentOid, childOid, map);\n\t}\n\n\tprivate void checkEntries(ObjectIdentity topParentOid, ObjectIdentity middleParentOid, ObjectIdentity childOid,\n\t\t\tMap<ObjectIdentity, Acl> map) {\n\t\tassertThat(map).hasSize(3);\n\t\tMutableAcl topParent = (MutableAcl) map.get(topParentOid);\n\t\tMutableAcl middleParent = (MutableAcl) map.get(middleParentOid);\n\t\tMutableAcl child = (MutableAcl) map.get(childOid);\n\t\t// Check the retrieved versions has IDs\n\t\tassertThat(topParent.getId()).isNotNull();\n\t\tassertThat(middleParent.getId()).isNotNull();\n\t\tassertThat(child.getId()).isNotNull();\n\t\t// Check their parents were correctly retrieved\n\t\tassertThat(topParent.getParentAcl()).isNull();\n\t\tassertThat(middleParent.getParentAcl().getObjectIdentity()).isEqualTo(topParentOid);\n\t\tassertThat(child.getParentAcl().getObjectIdentity()).isEqualTo(middleParentOid);\n\t\t// Check their ACEs were correctly retrieved\n\t\tassertThat(topParent.getEntries()).hasSize(2);\n\t\tassertThat(middleParent.getEntries()).hasSize(1);\n\t\tassertThat(child.getEntries()).hasSize(1);\n\t\t// Check object identities were correctly retrieved\n\t\tassertThat(topParent.getObjectIdentity()).isEqualTo(topParentOid);\n\t\tassertThat(middleParent.getObjectIdentity()).isEqualTo(middleParentOid);\n\t\tassertThat(child.getObjectIdentity()).isEqualTo(childOid);\n\t\t// Check each entry\n\t\tassertThat(topParent.isEntriesInheriting()).isTrue();\n\t\tassertThat(Long.valueOf(1)).isEqualTo(topParent.getId());\n\t\tassertThat(new PrincipalSid(\"ben\")).isEqualTo(topParent.getOwner());\n\t\tassertThat(Long.valueOf(1)).isEqualTo(topParent.getEntries().get(0).getId());\n\t\tassertThat(topParent.getEntries().get(0).getPermission()).isEqualTo(BasePermission.READ);\n\t\tassertThat(topParent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid(\"ben\"));\n\t\tassertThat(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditFailure()).isFalse();\n\t\tassertThat(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditSuccess()).isFalse();\n\t\tassertThat((topParent.getEntries().get(0)).isGranting()).isTrue();\n\t\tassertThat(Long.valueOf(2)).isEqualTo(topParent.getEntries().get(1).getId());\n\t\tassertThat(topParent.getEntries().get(1).getPermission()).isEqualTo(BasePermission.WRITE);\n\t\tassertThat(topParent.getEntries().get(1).getSid()).isEqualTo(new PrincipalSid(\"ben\"));\n\t\tassertThat(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditFailure()).isFalse();\n\t\tassertThat(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditSuccess()).isFalse();\n\t\tassertThat(topParent.getEntries().get(1).isGranting()).isFalse();\n\t\tassertThat(middleParent.isEntriesInheriting()).isTrue();\n\t\tassertThat(Long.valueOf(2)).isEqualTo(middleParent.getId());\n\t\tassertThat(new PrincipalSid(\"ben\")).isEqualTo(middleParent.getOwner());\n\t\tassertThat(Long.valueOf(3)).isEqualTo(middleParent.getEntries().get(0).getId());\n\t\tassertThat(middleParent.getEntries().get(0).getPermission()).isEqualTo(BasePermission.DELETE);\n\t\tassertThat(middleParent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid(\"ben\"));\n\t\tassertThat(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditFailure()).isFalse();\n\t\tassertThat(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditSuccess()).isFalse();\n\t\tassertThat(middleParent.getEntries().get(0).isGranting()).isTrue();\n\t\tassertThat(child.isEntriesInheriting()).isTrue();\n\t\tassertThat(Long.valueOf(3)).isEqualTo(child.getId());\n\t\tassertThat(new PrincipalSid(\"ben\")).isEqualTo(child.getOwner());\n\t\tassertThat(Long.valueOf(4)).isEqualTo(child.getEntries().get(0).getId());\n\t\tassertThat(child.getEntries().get(0).getPermission()).isEqualTo(BasePermission.DELETE);\n\t\tassertThat(new PrincipalSid(\"ben\")).isEqualTo(child.getEntries().get(0).getSid());\n\t\tassertThat(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditFailure()).isFalse();\n\t\tassertThat(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditSuccess()).isFalse();\n\t\tassertThat((child.getEntries().get(0)).isGranting()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testAllParentsAreRetrievedWhenChildIsLoaded() {\n\t\tString query = \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,103,1,1,1);\";\n\t\tgetJdbcTemplate().execute(query);\n\t\tObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\t\tObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, 101L);\n\t\tObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, 102L);\n\t\tObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS, 103L);\n\t\t// Retrieve the child\n\t\tMap<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(childOid), null);\n\t\t// Check that the child and all its parents were retrieved\n\t\tassertThat(map.get(childOid)).isNotNull();\n\t\tassertThat(map.get(childOid).getObjectIdentity()).isEqualTo(childOid);\n\t\tassertThat(map.get(middleParentOid)).isNotNull();\n\t\tassertThat(map.get(middleParentOid).getObjectIdentity()).isEqualTo(middleParentOid);\n\t\tassertThat(map.get(topParentOid)).isNotNull();\n\t\tassertThat(map.get(topParentOid).getObjectIdentity()).isEqualTo(topParentOid);\n\t\t// The second parent shouldn't have been retrieved\n\t\tassertThat(map.get(middleParent2Oid)).isNull();\n\t}\n\n\t/**\n\t * Test created from SEC-590.\n\t */\n\t@Test\n\tpublic void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached() {\n\t\tString query = \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,105,null,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (7,2,106,6,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (8,2,107,6,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (9,2,108,7,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (7,6,0,1,1,1,0,0)\";\n\t\tgetJdbcTemplate().execute(query);\n\t\tObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, 104L);\n\t\tObjectIdentity parent1Oid = new ObjectIdentityImpl(TARGET_CLASS, 105L);\n\t\tObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS, 106);\n\t\tObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, 107);\n\t\t// First lookup only child, thus populating the cache with grandParent,\n\t\t// parent1\n\t\t// and child\n\t\tList<Permission> checkPermission = Arrays.asList(BasePermission.READ);\n\t\tList<Sid> sids = Arrays.asList(BEN_SID);\n\t\tList<ObjectIdentity> childOids = Arrays.asList(childOid);\n\t\tthis.strategy.setBatchSize(6);\n\t\tMap<ObjectIdentity, Acl> foundAcls = this.strategy.readAclsById(childOids, sids);\n\t\tAcl foundChildAcl = foundAcls.get(childOid);\n\t\tassertThat(foundChildAcl).isNotNull();\n\t\tassertThat(foundChildAcl.isGranted(checkPermission, sids, false)).isTrue();\n\t\t// Search for object identities has to be done in the following order:\n\t\t// last\n\t\t// element have to be one which\n\t\t// is already in cache and the element before it must not be stored in\n\t\t// cache\n\t\tList<ObjectIdentity> allOids = Arrays.asList(grandParentOid, parent1Oid, parent2Oid, childOid);\n\t\tfoundAcls = this.strategy.readAclsById(allOids, sids);\n\t\tAcl foundParent2Acl = foundAcls.get(parent2Oid);\n\t\tassertThat(foundParent2Acl).isNotNull();\n\t\tassertThat(foundParent2Acl.isGranted(checkPermission, sids, false)).isTrue();\n\t}\n\n\t@Test\n\tpublic void nullOwnerIsNotSupported() {\n\t\tString query = \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,104,null,null,1);\";\n\t\tgetJdbcTemplate().execute(query);\n\t\tObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, 104L);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)));\n\t}\n\n\t@Test\n\tpublic void testCreatePrincipalSid() {\n\t\tSid result = this.strategy.createSid(true, \"sid\");\n\t\tassertThat(result.getClass()).isEqualTo(PrincipalSid.class);\n\t\tassertThat(((PrincipalSid) result).getPrincipal()).isEqualTo(\"sid\");\n\t}\n\n\t@Test\n\tpublic void testCreateGrantedAuthority() {\n\t\tSid result = this.strategy.createSid(false, \"sid\");\n\t\tassertThat(result.getClass()).isEqualTo(GrantedAuthoritySid.class);\n\t\tassertThat(((GrantedAuthoritySid) result).getGrantedAuthority()).isEqualTo(\"sid\");\n\t}\n\n\t@Test\n\tpublic void setObjectIdentityGeneratorWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.strategy.setObjectIdentityGenerator(null))\n\t\t\t\t.withMessage(\"objectIdentityGenerator cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\tprivate static final class CacheManagerMock {\n\n\t\tprivate final List<String> cacheNames;\n\n\t\tprivate final CacheManager cacheManager;\n\n\t\tprivate CacheManagerMock() {\n\t\t\tthis.cacheNames = new ArrayList<>();\n\t\t\tthis.cacheManager = mock(CacheManager.class);\n\t\t\tgiven(this.cacheManager.getCacheNames()).willReturn(this.cacheNames);\n\t\t}\n\n\t\tprivate CacheManager getCacheManager() {\n\t\t\treturn this.cacheManager;\n\t\t}\n\n\t\tprivate void addCache(String name) {\n\t\t\tthis.cacheNames.add(name);\n\t\t\tCache cache = new ConcurrentMapCache(name);\n\t\t\tgiven(this.cacheManager.getCache(name)).willReturn(cache);\n\t\t}\n\n\t\tprivate void clear() {\n\t\t\tthis.cacheNames.clear();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/AclClassIdUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.io.Serializable;\nimport java.math.BigInteger;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.UUID;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.convert.ConversionService;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * Tests for {@link AclClassIdUtils}.\n *\n * @author paulwheeler\n */\n@ExtendWith(MockitoExtension.class)\npublic class AclClassIdUtilsTests {\n\n\tprivate static final Long DEFAULT_IDENTIFIER = 999L;\n\n\tprivate static final BigInteger BIGINT_IDENTIFIER = new BigInteger(\"999\");\n\n\tprivate static final String DEFAULT_IDENTIFIER_AS_STRING = DEFAULT_IDENTIFIER.toString();\n\n\t@Mock\n\tprivate ResultSet resultSet;\n\n\t@Mock\n\tprivate ConversionService conversionService;\n\n\tprivate AclClassIdUtils aclClassIdUtils;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.aclClassIdUtils = new AclClassIdUtils();\n\t}\n\n\t@Test\n\tpublic void shouldReturnLongIfIdentifierIsLong() throws SQLException {\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER, this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER);\n\t}\n\n\t@Test\n\tpublic void shouldReturnLongIfIdentifierIsBigInteger() throws SQLException {\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(BIGINT_IDENTIFIER, this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER);\n\t}\n\n\t@Test\n\tpublic void shouldReturnLongIfClassIdTypeIsNull() throws SQLException {\n\t\tgiven(this.resultSet.getString(\"class_id_type\")).willReturn(null);\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER);\n\t}\n\n\t@Test\n\tpublic void shouldReturnLongIfNoClassIdTypeColumn() throws SQLException {\n\t\tgiven(this.resultSet.getString(\"class_id_type\")).willThrow(SQLException.class);\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER);\n\t}\n\n\t@Test\n\tpublic void shouldReturnLongIfTypeClassNotFound() throws SQLException {\n\t\tgiven(this.resultSet.getString(\"class_id_type\")).willReturn(\"com.example.UnknownType\");\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER);\n\t}\n\n\t@Test\n\tpublic void shouldReturnLongEvenIfCustomConversionServiceDoesNotSupportLongConversion() throws SQLException {\n\t\tgiven(this.resultSet.getString(\"class_id_type\")).willReturn(\"java.lang.Long\");\n\t\tgiven(this.conversionService.canConvert(String.class, Long.class)).willReturn(false);\n\t\tthis.aclClassIdUtils.setConversionService(this.conversionService);\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER);\n\t}\n\n\t@Test\n\tpublic void shouldReturnLongWhenLongClassIdType() throws SQLException {\n\t\tgiven(this.resultSet.getString(\"class_id_type\")).willReturn(\"java.lang.Long\");\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(DEFAULT_IDENTIFIER_AS_STRING, this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(DEFAULT_IDENTIFIER);\n\t}\n\n\t@Test\n\tpublic void shouldReturnUUIDWhenUUIDClassIdType() throws SQLException {\n\t\tUUID identifier = UUID.randomUUID();\n\t\tgiven(this.resultSet.getString(\"class_id_type\")).willReturn(\"java.util.UUID\");\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(identifier.toString(), this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(identifier);\n\t}\n\n\t@Test\n\tpublic void shouldReturnStringWhenStringClassIdType() throws SQLException {\n\t\tString identifier = \"MY_STRING_IDENTIFIER\";\n\t\tgiven(this.resultSet.getString(\"class_id_type\")).willReturn(\"java.lang.String\");\n\t\tSerializable newIdentifier = this.aclClassIdUtils.identifierFrom(identifier, this.resultSet);\n\t\tassertThat(newIdentifier).isEqualTo(identifier);\n\t}\n\n\t@Test\n\tpublic void shouldNotAcceptNullConversionServiceInConstruction() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AclClassIdUtils(null));\n\t}\n\n\t@Test\n\tpublic void shouldNotAcceptNullConversionServiceInSetter() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.aclClassIdUtils.setConversionService(null));\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport javax.sql.DataSource;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\n\nimport org.springframework.jdbc.core.JdbcTemplate;\n\n/**\n * Tests {@link BasicLookupStrategy} with Acl Class type id not specified.\n *\n * @author Andrei Stefan\n * @author Paul Wheeler\n */\npublic class BasicLookupStrategyTests extends AbstractBasicLookupStrategyTests {\n\n\tprivate static final BasicLookupStrategyTestsDbHelper DATABASE_HELPER = new BasicLookupStrategyTestsDbHelper();\n\n\t@BeforeAll\n\tpublic static void createDatabase() throws Exception {\n\t\tDATABASE_HELPER.createDatabase();\n\t}\n\n\t@AfterAll\n\tpublic static void dropDatabase() {\n\t\tDATABASE_HELPER.getDataSource().destroy();\n\t}\n\n\t@Override\n\tpublic JdbcTemplate getJdbcTemplate() {\n\t\treturn DATABASE_HELPER.getJdbcTemplate();\n\t}\n\n\t@Override\n\tpublic DataSource getDataSource() {\n\t\treturn DATABASE_HELPER.getDataSource();\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyTestsDbHelper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.SingleConnectionDataSource;\nimport org.springframework.util.FileCopyUtils;\n\n/**\n * Helper class to initialize the database for BasicLookupStrategyTests.\n *\n * @author Andrei Stefan\n * @author Paul Wheeler\n */\npublic class BasicLookupStrategyTestsDbHelper {\n\n\tprivate static final String ACL_SCHEMA_SQL_FILE = \"createAclSchema.sql\";\n\n\tprivate static final String ACL_SCHEMA_SQL_FILE_WITH_ACL_CLASS_ID = \"createAclSchemaWithAclClassIdType.sql\";\n\n\tprivate SingleConnectionDataSource dataSource;\n\n\tprivate JdbcTemplate jdbcTemplate;\n\n\tprivate boolean withAclClassIdType;\n\n\tpublic BasicLookupStrategyTestsDbHelper() {\n\t}\n\n\tpublic BasicLookupStrategyTestsDbHelper(boolean withAclClassIdType) {\n\t\tthis.withAclClassIdType = withAclClassIdType;\n\t}\n\n\tpublic void createDatabase() throws Exception {\n\t\t// Use a different connection url so the tests can run in parallel\n\t\tString connectionUrl;\n\t\tString sqlClassPathResource;\n\t\tif (!this.withAclClassIdType) {\n\t\t\tconnectionUrl = \"jdbc:hsqldb:mem:lookupstrategytest\";\n\t\t\tsqlClassPathResource = ACL_SCHEMA_SQL_FILE;\n\t\t}\n\t\telse {\n\t\t\tconnectionUrl = \"jdbc:hsqldb:mem:lookupstrategytestWithAclClassIdType\";\n\t\t\tsqlClassPathResource = ACL_SCHEMA_SQL_FILE_WITH_ACL_CLASS_ID;\n\t\t}\n\t\tthis.dataSource = new SingleConnectionDataSource(connectionUrl, \"sa\", \"\", true);\n\t\tthis.dataSource.setDriverClassName(\"org.hsqldb.jdbcDriver\");\n\t\tthis.jdbcTemplate = new JdbcTemplate(this.dataSource);\n\t\tResource resource = new ClassPathResource(sqlClassPathResource);\n\t\tString sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));\n\t\tthis.jdbcTemplate.execute(sql);\n\t}\n\n\tpublic JdbcTemplate getJdbcTemplate() {\n\t\treturn this.jdbcTemplate;\n\t}\n\n\tpublic SingleConnectionDataSource getDataSource() {\n\t\treturn this.dataSource;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/BasicLookupStrategyWithAclClassTypeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.util.Arrays;\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.ConversionFailedException;\nimport org.springframework.core.convert.support.DefaultConversionService;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.security.acls.domain.ConsoleAuditLogger;\nimport org.springframework.security.acls.domain.DefaultPermissionFactory;\nimport org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.ObjectIdentity;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests {@link BasicLookupStrategy} with Acl Class type id set to UUID.\n *\n * @author Paul Wheeler\n */\npublic class BasicLookupStrategyWithAclClassTypeTests extends AbstractBasicLookupStrategyTests {\n\n\tprivate static final BasicLookupStrategyTestsDbHelper DATABASE_HELPER = new BasicLookupStrategyTestsDbHelper(true);\n\n\tprivate BasicLookupStrategy uuidEnabledStrategy;\n\n\t@Override\n\tpublic JdbcTemplate getJdbcTemplate() {\n\t\treturn DATABASE_HELPER.getJdbcTemplate();\n\t}\n\n\t@Override\n\tpublic DataSource getDataSource() {\n\t\treturn DATABASE_HELPER.getDataSource();\n\t}\n\n\t@BeforeAll\n\tpublic static void createDatabase() throws Exception {\n\t\tDATABASE_HELPER.createDatabase();\n\t}\n\n\t@AfterAll\n\tpublic static void dropDatabase() {\n\t\tDATABASE_HELPER.getDataSource().destroy();\n\t}\n\n\t@Override\n\t@BeforeEach\n\tpublic void initializeBeans() {\n\t\tsuper.initializeBeans();\n\t\tthis.uuidEnabledStrategy = new BasicLookupStrategy(getDataSource(), aclCache(), aclAuthStrategy(),\n\t\t\t\tnew DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()));\n\t\tthis.uuidEnabledStrategy.setPermissionFactory(new DefaultPermissionFactory());\n\t\tthis.uuidEnabledStrategy.setAclClassIdSupported(true);\n\t\tthis.uuidEnabledStrategy.setConversionService(new DefaultConversionService());\n\t}\n\n\t@BeforeEach\n\tpublic void populateDatabaseForAclClassTypeTests() {\n\t\tString query = \"INSERT INTO acl_class(ID,CLASS,CLASS_ID_TYPE) VALUES (3,'\" + TARGET_CLASS_WITH_UUID\n\t\t\t\t+ \"', 'java.util.UUID');\"\n\t\t\t\t+ \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,3,'\"\n\t\t\t\t+ OBJECT_IDENTITY_UUID.toString() + \"',null,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,3,'\"\n\t\t\t\t+ OBJECT_IDENTITY_LONG_AS_UUID + \"',null,1,1);\"\n\t\t\t\t+ \"INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,8,0,0,0);\"\n\t\t\t\t+ \"INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (6,5,0,1,8,0,0,0);\";\n\t\tDATABASE_HELPER.getJdbcTemplate().execute(query);\n\t}\n\n\t@Test\n\tpublic void testReadObjectIdentityUsingUuidType() {\n\t\tObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, OBJECT_IDENTITY_UUID);\n\t\tMap<ObjectIdentity, Acl> foundAcls = this.uuidEnabledStrategy.readAclsById(Arrays.asList(oid),\n\t\t\t\tArrays.asList(BEN_SID));\n\t\tassertThat(foundAcls).hasSize(1);\n\t\tassertThat(foundAcls.get(oid)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testReadObjectIdentityUsingLongTypeWithConversionServiceEnabled() {\n\t\tObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\t\tMap<ObjectIdentity, Acl> foundAcls = this.uuidEnabledStrategy.readAclsById(Arrays.asList(oid),\n\t\t\t\tArrays.asList(BEN_SID));\n\t\tassertThat(foundAcls).hasSize(1);\n\t\tassertThat(foundAcls.get(oid)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testReadObjectIdentityUsingNonUuidInDatabase() {\n\t\tObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, OBJECT_IDENTITY_LONG_AS_UUID);\n\t\tassertThatExceptionOfType(ConversionFailedException.class)\n\t\t\t.isThrownBy(() -> this.uuidEnabledStrategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)));\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/DatabaseSeeder.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.io.IOException;\n\nimport javax.sql.DataSource;\n\nimport org.springframework.core.io.Resource;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.util.Assert;\nimport org.springframework.util.FileCopyUtils;\n\n/**\n * Seeds the database for {@link JdbcMutableAclServiceTests}.\n *\n * @author Ben Alex\n */\npublic class DatabaseSeeder {\n\n\tpublic DatabaseSeeder(DataSource dataSource, Resource resource) throws IOException {\n\t\tAssert.notNull(dataSource, \"dataSource required\");\n\t\tAssert.notNull(resource, \"resource required\");\n\t\tJdbcTemplate template = new JdbcTemplate(dataSource);\n\t\tString sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));\n\t\ttemplate.execute(sql);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/JdbcAclServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport javax.sql.DataSource;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.domain.PrincipalSid;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.Sid;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.anyList;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * Unit and Integration tests the ACL JdbcAclService using an in-memory database.\n *\n * @author Nena Raab\n */\n@ExtendWith(MockitoExtension.class)\npublic class JdbcAclServiceTests {\n\n\tprivate EmbeddedDatabase embeddedDatabase;\n\n\t@Mock\n\tprivate DataSource dataSource;\n\n\t@Mock\n\tprivate LookupStrategy lookupStrategy;\n\n\t@Mock\n\tJdbcOperations jdbcOperations;\n\n\tprivate JdbcAclService aclServiceIntegration;\n\n\tprivate JdbcAclService aclService;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\t// @formatter:off\n\t\tthis.embeddedDatabase = new EmbeddedDatabaseBuilder()\n\t\t\t.addScript(\"createAclSchemaWithAclClassIdType.sql\")\n\t\t\t.addScript(\"db/sql/test_data_hierarchy.sql\")\n\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tthis.aclService = new JdbcAclService(this.jdbcOperations, this.lookupStrategy);\n\t\tthis.aclServiceIntegration = new JdbcAclService(this.embeddedDatabase, this.lookupStrategy);\n\t}\n\n\t@AfterEach\n\tpublic void tearDownEmbeddedDatabase() {\n\t\tthis.embeddedDatabase.shutdown();\n\t}\n\n\t// SEC-1898\n\t@Test\n\tpublic void readAclByIdMissingAcl() {\n\t\tMap<ObjectIdentity, Acl> result = new HashMap<>();\n\t\tgiven(this.lookupStrategy.readAclsById(anyList(), anyList())).willReturn(result);\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(Object.class, 1);\n\t\tList<Sid> sids = Arrays.<Sid>asList(new PrincipalSid(\"user\"));\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> this.aclService.readAclById(objectIdentity, sids));\n\t}\n\n\t@Test\n\tpublic void findOneChildren() {\n\t\tList<ObjectIdentity> result = new ArrayList<>();\n\t\tresult.add(new ObjectIdentityImpl(Object.class, \"5577\"));\n\t\tObject[] args = { \"1\", \"org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockLongIdDomainObject\" };\n\t\tgiven(this.jdbcOperations.query(anyString(), ArgumentMatchers.<RowMapper<ObjectIdentity>>any(), eq(args)))\n\t\t\t.willReturn(result);\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L);\n\t\tList<ObjectIdentity> objectIdentities = this.aclService.findChildren(objectIdentity);\n\t\tassertThat(objectIdentities).hasSize(1);\n\t\tassertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(\"5577\");\n\t}\n\n\t@Test\n\tpublic void findNoChildren() {\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L);\n\t\tList<ObjectIdentity> objectIdentities = this.aclService.findChildren(objectIdentity);\n\t\tassertThat(objectIdentities).isNull();\n\t}\n\n\t@Test\n\tpublic void findChildrenWithoutIdType() {\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 4711L);\n\t\tList<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);\n\t\tassertThat(objectIdentities).hasSize(1);\n\t\tassertThat(objectIdentities.get(0).getType()).isEqualTo(MockUntypedIdDomainObject.class.getName());\n\t\tassertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(5000L);\n\t}\n\n\t@Test\n\tpublic void findChildrenForUnknownObject() {\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(Object.class, 33);\n\t\tList<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);\n\t\tassertThat(objectIdentities).isNull();\n\t}\n\n\t@Test\n\tpublic void findChildrenOfIdTypeLong() {\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(\"location\", \"US-PAL\");\n\t\tList<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);\n\t\tassertThat(objectIdentities).hasSize(2);\n\t\tassertThat(objectIdentities.get(0).getType()).isEqualTo(MockLongIdDomainObject.class.getName());\n\t\tassertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(4711L);\n\t\tassertThat(objectIdentities.get(1).getType()).isEqualTo(MockLongIdDomainObject.class.getName());\n\t\tassertThat(objectIdentities.get(1).getIdentifier()).isEqualTo(4712L);\n\t}\n\n\t@Test\n\tpublic void findChildrenOfIdTypeString() {\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(\"location\", \"US\");\n\t\tthis.aclServiceIntegration.setAclClassIdSupported(true);\n\t\tList<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);\n\t\tassertThat(objectIdentities).hasSize(1);\n\t\tassertThat(objectIdentities.get(0).getType()).isEqualTo(\"location\");\n\t\tassertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(\"US-PAL\");\n\t}\n\n\t@Test\n\tpublic void findChildrenOfIdTypeUUID() {\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(MockUntypedIdDomainObject.class, 5000L);\n\t\tthis.aclServiceIntegration.setAclClassIdSupported(true);\n\t\tList<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);\n\t\tassertThat(objectIdentities).hasSize(1);\n\t\tassertThat(objectIdentities.get(0).getType()).isEqualTo(\"costcenter\");\n\t\tassertThat(objectIdentities.get(0).getIdentifier())\n\t\t\t.isEqualTo(UUID.fromString(\"25d93b3f-c3aa-4814-9d5e-c7c96ced7762\"));\n\t}\n\n\t@Test\n\tpublic void setObjectIdentityGeneratorWhenNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.aclServiceIntegration.setObjectIdentityGenerator(null))\n\t\t\t.withMessage(\"objectIdentityGenerator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void findChildrenWhenObjectIdentityGeneratorSetThenUsed() {\n\t\tthis.aclServiceIntegration\n\t\t\t.setObjectIdentityGenerator((id, type) -> new ObjectIdentityImpl(type, \"prefix:\" + id));\n\n\t\tObjectIdentity objectIdentity = new ObjectIdentityImpl(\"location\", \"US\");\n\t\tthis.aclServiceIntegration.setAclClassIdSupported(true);\n\t\tList<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);\n\t\tassertThat(objectIdentities).hasSize(1);\n\t\tassertThat(objectIdentities.get(0).getType()).isEqualTo(\"location\");\n\t\tassertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(\"prefix:US-PAL\");\n\t}\n\n\tclass MockLongIdDomainObject {\n\n\t\tprivate Object id;\n\n\t\tObject getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tvoid setId(Object id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t}\n\n\tclass MockUntypedIdDomainObject {\n\n\t\tprivate Object id;\n\n\t\tObject getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\tvoid setId(Object id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006, 2017 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.security.acls.TargetObject;\nimport org.springframework.security.acls.domain.AclImpl;\nimport org.springframework.security.acls.domain.BasePermission;\nimport org.springframework.security.acls.domain.CumulativePermission;\nimport org.springframework.security.acls.domain.GrantedAuthoritySid;\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.domain.PrincipalSid;\nimport org.springframework.security.acls.model.AccessControlEntry;\nimport org.springframework.security.acls.model.Acl;\nimport org.springframework.security.acls.model.AclCache;\nimport org.springframework.security.acls.model.AlreadyExistsException;\nimport org.springframework.security.acls.model.ChildrenExistException;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.NotFoundException;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.Permission;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.sid.CustomSid;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.transaction.AfterTransaction;\nimport org.springframework.test.context.transaction.BeforeTransaction;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Integration tests the ACL system using an in-memory database.\n *\n * @author Ben Alex\n * @author Andrei Stefan\n */\n@Transactional\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = { \"/jdbcMutableAclServiceTests-context.xml\" })\npublic class JdbcMutableAclServiceTests {\n\n\tprivate static final String TARGET_CLASS = TargetObject.class.getName();\n\n\tprivate final Authentication auth = new TestingAuthenticationToken(\"ben\", \"ignored\", \"ROLE_ADMINISTRATOR\");\n\n\tpublic static final String SELECT_ALL_CLASSES = \"SELECT * FROM acl_class WHERE class = ?\";\n\n\tprivate final ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\n\tprivate final ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, 101L);\n\n\tprivate final ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, 102L);\n\n\t@Autowired\n\tprivate JdbcMutableAclService jdbcMutableAclService;\n\n\t@Autowired\n\tprivate AclCache aclCache;\n\n\t@Autowired\n\tprivate LookupStrategy lookupStrategy;\n\n\t@Autowired\n\tprivate DataSource dataSource;\n\n\t@Autowired\n\tprivate JdbcTemplate jdbcTemplate;\n\n\tprotected String getSqlClassPathResource() {\n\t\treturn \"createAclSchema.sql\";\n\t}\n\n\tprotected ObjectIdentity getTopParentOid() {\n\t\treturn this.topParentOid;\n\t}\n\n\tprotected ObjectIdentity getMiddleParentOid() {\n\t\treturn this.middleParentOid;\n\t}\n\n\tprotected ObjectIdentity getChildOid() {\n\t\treturn this.childOid;\n\t}\n\n\tprotected String getTargetClass() {\n\t\treturn TARGET_CLASS;\n\t}\n\n\t@BeforeTransaction\n\tpublic void createTables() throws Exception {\n\t\ttry {\n\t\t\tnew DatabaseSeeder(this.dataSource, new ClassPathResource(getSqlClassPathResource()));\n\t\t\t// new DatabaseSeeder(dataSource, new\n\t\t\t// ClassPathResource(\"createAclSchemaPostgres.sql\"));\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@AfterTransaction\n\tpublic void clearContextAndData() {\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.jdbcTemplate.execute(\"drop table acl_entry\");\n\t\tthis.jdbcTemplate.execute(\"drop table acl_object_identity\");\n\t\tthis.jdbcTemplate.execute(\"drop table acl_class\");\n\t\tthis.jdbcTemplate.execute(\"drop table acl_sid\");\n\t\tthis.aclCache.clearCache();\n\t}\n\n\t@Test\n\t@Transactional\n\tpublic void testLifecycle() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tMutableAcl topParent = this.jdbcMutableAclService.createAcl(getTopParentOid());\n\t\tMutableAcl middleParent = this.jdbcMutableAclService.createAcl(getMiddleParentOid());\n\t\tMutableAcl child = this.jdbcMutableAclService.createAcl(getChildOid());\n\t\t// Specify the inheritance hierarchy\n\t\tmiddleParent.setParent(topParent);\n\t\tchild.setParent(middleParent);\n\t\t// Now let's add a couple of permissions\n\t\ttopParent.insertAce(0, BasePermission.READ, new PrincipalSid(this.auth), true);\n\t\ttopParent.insertAce(1, BasePermission.WRITE, new PrincipalSid(this.auth), false);\n\t\tmiddleParent.insertAce(0, BasePermission.DELETE, new PrincipalSid(this.auth), true);\n\t\tchild.insertAce(0, BasePermission.DELETE, new PrincipalSid(this.auth), false);\n\t\t// Explicitly save the changed ACL\n\t\tthis.jdbcMutableAclService.updateAcl(topParent);\n\t\tthis.jdbcMutableAclService.updateAcl(middleParent);\n\t\tthis.jdbcMutableAclService.updateAcl(child);\n\t\t// Let's check if we can read them back correctly\n\t\tMap<ObjectIdentity, Acl> map = this.jdbcMutableAclService\n\t\t\t.readAclsById(Arrays.asList(getTopParentOid(), getMiddleParentOid(), getChildOid()));\n\t\tassertThat(map).hasSize(3);\n\t\t// Get the retrieved versions\n\t\tMutableAcl retrievedTopParent = (MutableAcl) map.get(getTopParentOid());\n\t\tMutableAcl retrievedMiddleParent = (MutableAcl) map.get(getMiddleParentOid());\n\t\tMutableAcl retrievedChild = (MutableAcl) map.get(getChildOid());\n\t\t// Check the retrieved versions has IDs\n\t\tassertThat(retrievedTopParent.getId()).isNotNull();\n\t\tassertThat(retrievedMiddleParent.getId()).isNotNull();\n\t\tassertThat(retrievedChild.getId()).isNotNull();\n\t\t// Check their parents were correctly persisted\n\t\tassertThat(retrievedTopParent.getParentAcl()).isNull();\n\t\tassertThat(retrievedMiddleParent.getParentAcl().getObjectIdentity()).isEqualTo(getTopParentOid());\n\t\tassertThat(retrievedChild.getParentAcl().getObjectIdentity()).isEqualTo(getMiddleParentOid());\n\t\t// Check their ACEs were correctly persisted\n\t\tassertThat(retrievedTopParent.getEntries()).hasSize(2);\n\t\tassertThat(retrievedMiddleParent.getEntries()).hasSize(1);\n\t\tassertThat(retrievedChild.getEntries()).hasSize(1);\n\t\t// Check the retrieved rights are correct\n\t\tList<Permission> read = Arrays.asList(BasePermission.READ);\n\t\tList<Permission> write = Arrays.asList(BasePermission.WRITE);\n\t\tList<Permission> delete = Arrays.asList(BasePermission.DELETE);\n\t\tList<Sid> pSid = Arrays.asList((Sid) new PrincipalSid(this.auth));\n\t\tassertThat(retrievedTopParent.isGranted(read, pSid, false)).isTrue();\n\t\tassertThat(retrievedTopParent.isGranted(write, pSid, false)).isFalse();\n\t\tassertThat(retrievedMiddleParent.isGranted(delete, pSid, false)).isTrue();\n\t\tassertThat(retrievedChild.isGranted(delete, pSid, false)).isFalse();\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> retrievedChild.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), pSid, false));\n\t\t// Now check the inherited rights (when not explicitly overridden) also look OK\n\t\tassertThat(retrievedChild.isGranted(read, pSid, false)).isTrue();\n\t\tassertThat(retrievedChild.isGranted(write, pSid, false)).isFalse();\n\t\tassertThat(retrievedChild.isGranted(delete, pSid, false)).isFalse();\n\t\t// Next change the child so it doesn't inherit permissions from above\n\t\tretrievedChild.setEntriesInheriting(false);\n\t\tthis.jdbcMutableAclService.updateAcl(retrievedChild);\n\t\tMutableAcl nonInheritingChild = (MutableAcl) this.jdbcMutableAclService.readAclById(getChildOid());\n\t\tassertThat(nonInheritingChild.isEntriesInheriting()).isFalse();\n\t\t// Check the child permissions no longer inherit\n\t\tassertThat(nonInheritingChild.isGranted(delete, pSid, true)).isFalse();\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> nonInheritingChild.isGranted(read, pSid, true));\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> nonInheritingChild.isGranted(write, pSid, true));\n\t\t// Let's add an identical permission to the child, but it'll appear AFTER the\n\t\t// current permission, so has no impact\n\t\tnonInheritingChild.insertAce(1, BasePermission.DELETE, new PrincipalSid(this.auth), true);\n\t\t// Let's also add another permission to the child\n\t\tnonInheritingChild.insertAce(2, BasePermission.CREATE, new PrincipalSid(this.auth), true);\n\t\t// Save the changed child\n\t\tthis.jdbcMutableAclService.updateAcl(nonInheritingChild);\n\t\tMutableAcl retrievedNonInheritingChild = (MutableAcl) this.jdbcMutableAclService.readAclById(getChildOid());\n\t\tassertThat(retrievedNonInheritingChild.getEntries()).hasSize(3);\n\t\t// Output permissions\n\t\tfor (int i = 0; i < retrievedNonInheritingChild.getEntries().size(); i++) {\n\t\t\tSystem.out.println(retrievedNonInheritingChild.getEntries().get(i));\n\t\t}\n\t\t// Check the permissions are as they should be\n\t\tassertThat(retrievedNonInheritingChild.isGranted(delete, pSid, true)).isFalse(); // as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// earlier\n\t\t// permission\n\t\t// overrode\n\t\tassertThat(retrievedNonInheritingChild.isGranted(Arrays.asList(BasePermission.CREATE), pSid, true)).isTrue();\n\t\t// Now check the first ACE (index 0) really is DELETE for our Sid and is\n\t\t// non-granting\n\t\tAccessControlEntry entry = retrievedNonInheritingChild.getEntries().get(0);\n\t\tassertThat(entry.getPermission().getMask()).isEqualTo(BasePermission.DELETE.getMask());\n\t\tassertThat(entry.getSid()).isEqualTo(new PrincipalSid(this.auth));\n\t\tassertThat(entry.isGranting()).isFalse();\n\t\tassertThat(entry.getId()).isNotNull();\n\t\t// Now delete that first ACE\n\t\tretrievedNonInheritingChild.deleteAce(0);\n\t\t// Save and check it worked\n\t\tMutableAcl savedChild = this.jdbcMutableAclService.updateAcl(retrievedNonInheritingChild);\n\t\tassertThat(savedChild.getEntries()).hasSize(2);\n\t\tassertThat(savedChild.isGranted(delete, pSid, false)).isTrue();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t/**\n\t * Test method that demonstrates eviction failure from cache - SEC-676\n\t */\n\t@Test\n\t@Transactional\n\tpublic void deleteAclAlsoDeletesChildren() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tthis.jdbcMutableAclService.createAcl(getTopParentOid());\n\t\tMutableAcl middleParent = this.jdbcMutableAclService.createAcl(getMiddleParentOid());\n\t\tMutableAcl child = this.jdbcMutableAclService.createAcl(getChildOid());\n\t\tchild.setParent(middleParent);\n\t\tthis.jdbcMutableAclService.updateAcl(middleParent);\n\t\tthis.jdbcMutableAclService.updateAcl(child);\n\t\t// Check the childOid really is a child of middleParentOid\n\t\tAcl childAcl = this.jdbcMutableAclService.readAclById(getChildOid());\n\t\tassertThat(childAcl.getParentAcl().getObjectIdentity()).isEqualTo(getMiddleParentOid());\n\t\t// Delete the mid-parent and test if the child was deleted, as well\n\t\tthis.jdbcMutableAclService.deleteAcl(getMiddleParentOid(), true);\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> this.jdbcMutableAclService.readAclById(getMiddleParentOid()));\n\t\tassertThatExceptionOfType(NotFoundException.class)\n\t\t\t.isThrownBy(() -> this.jdbcMutableAclService.readAclById(getChildOid()));\n\t\tAcl acl = this.jdbcMutableAclService.readAclById(getTopParentOid());\n\t\tassertThat(acl).isNotNull();\n\t\tassertThat(getTopParentOid()).isEqualTo(acl.getObjectIdentity());\n\t}\n\n\t@Test\n\tpublic void constructorRejectsNullParameters() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new JdbcMutableAclService(null, this.lookupStrategy, this.aclCache));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new JdbcMutableAclService(this.dataSource, null, this.aclCache));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new JdbcMutableAclService(this.dataSource, this.lookupStrategy, null));\n\t}\n\n\t@Test\n\tpublic void createAclRejectsNullParameter() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.jdbcMutableAclService.createAcl(null));\n\t}\n\n\t@Test\n\t@Transactional\n\tpublic void createAclForADuplicateDomainObject() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tObjectIdentity duplicateOid = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\t\tthis.jdbcMutableAclService.createAcl(duplicateOid);\n\t\t// Try to add the same object second time\n\t\tassertThatExceptionOfType(AlreadyExistsException.class)\n\t\t\t.isThrownBy(() -> this.jdbcMutableAclService.createAcl(duplicateOid));\n\t}\n\n\t@Test\n\t@Transactional\n\tpublic void deleteAclRejectsNullParameters() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.jdbcMutableAclService.deleteAcl(null, true));\n\t}\n\n\t@Test\n\t@Transactional\n\tpublic void deleteAclWithChildrenThrowsException() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tMutableAcl parent = this.jdbcMutableAclService.createAcl(getTopParentOid());\n\t\tMutableAcl child = this.jdbcMutableAclService.createAcl(getMiddleParentOid());\n\t\t// Specify the inheritance hierarchy\n\t\tchild.setParent(parent);\n\t\tthis.jdbcMutableAclService.updateAcl(child);\n\t\t// switch on FK\n\t\tthis.jdbcMutableAclService.setForeignKeysInDatabase(false);\n\t\ttry {\n\t\t\t// checking in the class, not database\n\t\t\tassertThatExceptionOfType(ChildrenExistException.class)\n\t\t\t\t.isThrownBy(() -> this.jdbcMutableAclService.deleteAcl(getTopParentOid(), false));\n\t\t}\n\t\tfinally {\n\t\t\t// restore to the default\n\t\t\tthis.jdbcMutableAclService.setForeignKeysInDatabase(true);\n\t\t}\n\t}\n\n\t@Test\n\t@Transactional\n\tpublic void deleteAclRemovesRowsFromDatabase() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tMutableAcl child = this.jdbcMutableAclService.createAcl(getChildOid());\n\t\tchild.insertAce(0, BasePermission.DELETE, new PrincipalSid(this.auth), false);\n\t\tthis.jdbcMutableAclService.updateAcl(child);\n\t\t// Remove the child and check all related database rows were removed accordingly\n\t\tthis.jdbcMutableAclService.deleteAcl(getChildOid(), false);\n\t\tassertThat(this.jdbcTemplate.queryForList(SELECT_ALL_CLASSES, new Object[] { getTargetClass() })).hasSize(1);\n\t\tassertThat(this.jdbcTemplate.queryForList(\"select * from acl_object_identity\")).isEmpty();\n\t\tassertThat(this.jdbcTemplate.queryForList(\"select * from acl_entry\")).isEmpty();\n\t\t// Check the cache\n\t\tassertThat(this.aclCache.getFromCache(getChildOid())).isNull();\n\t\tassertThat(this.aclCache.getFromCache(102L)).isNull();\n\t}\n\n\t/** SEC-1107 */\n\t@Test\n\t@Transactional\n\tpublic void identityWithIntegerIdIsSupportedByCreateAcl() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, 101);\n\t\tthis.jdbcMutableAclService.createAcl(oid);\n\t\tassertThat(this.jdbcMutableAclService.readAclById(new ObjectIdentityImpl(TARGET_CLASS, 101L))).isNotNull();\n\t}\n\n\t@Test\n\t@Transactional\n\tpublic void createAclWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = mock(SecurityContextHolderStrategy.class);\n\t\tSecurityContext context = new SecurityContextImpl(this.auth);\n\t\tgiven(securityContextHolderStrategy.getContext()).willReturn(context);\n\t\tJdbcMutableAclService service = new JdbcMutableAclService(this.dataSource, this.lookupStrategy, this.aclCache);\n\t\tservice.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, 101);\n\t\tservice.createAcl(oid);\n\t\tverify(securityContextHolderStrategy).getContext();\n\t}\n\n\t/**\n\t * SEC-655\n\t */\n\t@Test\n\t@Transactional\n\tpublic void childrenAreClearedFromCacheWhenParentIsUpdated() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"ben\", \"ignored\", \"ROLE_ADMINISTRATOR\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity parentOid = new ObjectIdentityImpl(TARGET_CLASS, 104L);\n\t\tObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, 105L);\n\t\tMutableAcl parent = this.jdbcMutableAclService.createAcl(parentOid);\n\t\tMutableAcl child = this.jdbcMutableAclService.createAcl(childOid);\n\t\tchild.setParent(parent);\n\t\tthis.jdbcMutableAclService.updateAcl(child);\n\t\tparent = (AclImpl) this.jdbcMutableAclService.readAclById(parentOid);\n\t\tparent.insertAce(0, BasePermission.READ, new PrincipalSid(\"ben\"), true);\n\t\tthis.jdbcMutableAclService.updateAcl(parent);\n\t\tparent = (AclImpl) this.jdbcMutableAclService.readAclById(parentOid);\n\t\tparent.insertAce(1, BasePermission.READ, new PrincipalSid(\"scott\"), true);\n\t\tthis.jdbcMutableAclService.updateAcl(parent);\n\t\tchild = (MutableAcl) this.jdbcMutableAclService.readAclById(childOid);\n\t\tparent = (MutableAcl) child.getParentAcl();\n\t\tassertThat(parent.getEntries()).hasSize(2)\n\t\t\t.withFailMessage(\"Fails because child has a stale reference to its parent\");\n\t\tassertThat(parent.getEntries().get(0).getPermission().getMask()).isEqualTo(1);\n\t\tassertThat(parent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid(\"ben\"));\n\t\tassertThat(parent.getEntries().get(1).getPermission().getMask()).isEqualTo(1);\n\t\tassertThat(parent.getEntries().get(1).getSid()).isEqualTo(new PrincipalSid(\"scott\"));\n\t}\n\n\t/**\n\t * SEC-655\n\t */\n\t@Test\n\t@Transactional\n\tpublic void childrenAreClearedFromCacheWhenParentisUpdated2() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"system\", \"secret\", \"ROLE_IGNORED\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentityImpl rootObject = new ObjectIdentityImpl(TARGET_CLASS, 1L);\n\t\tMutableAcl parent = this.jdbcMutableAclService.createAcl(rootObject);\n\t\tMutableAcl child = this.jdbcMutableAclService.createAcl(new ObjectIdentityImpl(TARGET_CLASS, 2L));\n\t\tchild.setParent(parent);\n\t\tthis.jdbcMutableAclService.updateAcl(child);\n\t\tparent.insertAce(0, BasePermission.ADMINISTRATION, new GrantedAuthoritySid(\"ROLE_ADMINISTRATOR\"), true);\n\t\tthis.jdbcMutableAclService.updateAcl(parent);\n\t\tparent.insertAce(1, BasePermission.DELETE, new PrincipalSid(\"terry\"), true);\n\t\tthis.jdbcMutableAclService.updateAcl(parent);\n\t\tchild = (MutableAcl) this.jdbcMutableAclService.readAclById(new ObjectIdentityImpl(TARGET_CLASS, 2L));\n\t\tparent = (MutableAcl) child.getParentAcl();\n\t\tassertThat(parent.getEntries()).hasSize(2);\n\t\tassertThat(parent.getEntries().get(0).getPermission().getMask()).isEqualTo(16);\n\t\tassertThat(parent.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(\"ROLE_ADMINISTRATOR\"));\n\t\tassertThat(parent.getEntries().get(1).getPermission().getMask()).isEqualTo(8);\n\t\tassertThat(parent.getEntries().get(1).getSid()).isEqualTo(new PrincipalSid(\"terry\"));\n\t}\n\n\t@Test\n\t@Transactional\n\tpublic void cumulativePermissions() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"ben\", \"ignored\", \"ROLE_ADMINISTRATOR\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, 110L);\n\t\tMutableAcl topParent = this.jdbcMutableAclService.createAcl(topParentOid);\n\t\t// Add an ACE permission entry\n\t\tPermission cm = new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION);\n\t\tassertThat(cm.getMask()).isEqualTo(17);\n\t\tSid benSid = new PrincipalSid(auth);\n\t\ttopParent.insertAce(0, cm, benSid, true);\n\t\tassertThat(topParent.getEntries()).hasSize(1);\n\t\t// Explicitly save the changed ACL\n\t\ttopParent = this.jdbcMutableAclService.updateAcl(topParent);\n\t\t// Check the mask was retrieved correctly\n\t\tassertThat(topParent.getEntries().get(0).getPermission().getMask()).isEqualTo(17);\n\t\tassertThat(topParent.isGranted(Arrays.asList(cm), Arrays.asList(benSid), true)).isTrue();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testProcessingCustomSid() {\n\t\tCustomJdbcMutableAclService customJdbcMutableAclService = spy(\n\t\t\t\tnew CustomJdbcMutableAclService(this.dataSource, this.lookupStrategy, this.aclCache));\n\t\tCustomSid customSid = new CustomSid(\"Custom sid\");\n\t\tgiven(customJdbcMutableAclService.createOrRetrieveSidPrimaryKey(\"Custom sid\", false, false)).willReturn(1L);\n\t\tLong result = customJdbcMutableAclService.createOrRetrieveSidPrimaryKey(customSid, false);\n\t\tassertThat(Long.valueOf(1L)).isEqualTo(result);\n\t}\n\n\tprotected Authentication getAuth() {\n\t\treturn this.auth;\n\t}\n\n\tprotected JdbcMutableAclService getJdbcMutableAclService() {\n\t\treturn this.jdbcMutableAclService;\n\t}\n\n\t/**\n\t * This class needed to show how to extend {@link JdbcMutableAclService} for\n\t * processing custom {@link Sid} implementations\n\t */\n\tprivate class CustomJdbcMutableAclService extends JdbcMutableAclService {\n\n\t\tCustomJdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {\n\t\t\tsuper(dataSource, lookupStrategy, aclCache);\n\t\t}\n\n\t\t@Override\n\t\tprotected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {\n\t\t\tString sidName;\n\t\t\tboolean isPrincipal = false;\n\t\t\tif (sid instanceof CustomSid) {\n\t\t\t\tsidName = ((CustomSid) sid).getSid();\n\t\t\t}\n\t\t\telse if (sid instanceof GrantedAuthoritySid) {\n\t\t\t\tsidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsidName = ((PrincipalSid) sid).getPrincipal();\n\t\t\t\tisPrincipal = true;\n\t\t\t}\n\t\t\treturn createOrRetrieveSidPrimaryKey(sidName, isPrincipal, allowCreate);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTestsWithAclClassId.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.util.UUID;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.TargetObjectWithUUID;\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Integration tests the ACL system using ACL class id type of UUID and using an in-memory\n * database.\n *\n * @author Paul Wheeler\n */\n@ContextConfiguration(locations = { \"/jdbcMutableAclServiceTestsWithAclClass-context.xml\" })\npublic class JdbcMutableAclServiceTestsWithAclClassId extends JdbcMutableAclServiceTests {\n\n\tprivate static final String TARGET_CLASS_WITH_UUID = TargetObjectWithUUID.class.getName();\n\n\tprivate final ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, UUID.randomUUID());\n\n\tprivate final ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, UUID.randomUUID());\n\n\tprivate final ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, UUID.randomUUID());\n\n\t@Override\n\tprotected String getSqlClassPathResource() {\n\t\treturn \"createAclSchemaWithAclClassIdType.sql\";\n\t}\n\n\t@Override\n\tprotected ObjectIdentity getTopParentOid() {\n\t\treturn this.topParentOid;\n\t}\n\n\t@Override\n\tprotected ObjectIdentity getMiddleParentOid() {\n\t\treturn this.middleParentOid;\n\t}\n\n\t@Override\n\tprotected ObjectIdentity getChildOid() {\n\t\treturn this.childOid;\n\t}\n\n\t@Override\n\tprotected String getTargetClass() {\n\t\treturn TARGET_CLASS_WITH_UUID;\n\t}\n\n\t@Test\n\t@Transactional\n\tpublic void identityWithUuidIdIsSupportedByCreateAcl() {\n\t\tSecurityContextHolder.getContext().setAuthentication(getAuth());\n\t\tUUID id = UUID.randomUUID();\n\t\tObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, id);\n\t\tgetJdbcMutableAclService().createAcl(oid);\n\t\tassertThat(getJdbcMutableAclService().readAclById(new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, id)))\n\t\t\t.isNotNull();\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/jdbc/SpringCacheBasedAclCacheTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.jdbc;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.concurrent.ConcurrentMapCacheManager;\nimport org.springframework.security.acls.domain.AclAuthorizationStrategy;\nimport org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;\nimport org.springframework.security.acls.domain.AclImpl;\nimport org.springframework.security.acls.domain.AuditLogger;\nimport org.springframework.security.acls.domain.ConsoleAuditLogger;\nimport org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;\nimport org.springframework.security.acls.domain.ObjectIdentityImpl;\nimport org.springframework.security.acls.domain.SpringCacheBasedAclCache;\nimport org.springframework.security.acls.model.MutableAcl;\nimport org.springframework.security.acls.model.ObjectIdentity;\nimport org.springframework.security.acls.model.PermissionGrantingStrategy;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.util.FieldUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link org.springframework.security.acls.domain.SpringCacheBasedAclCache}\n *\n * @author Marten Deinum\n */\npublic class SpringCacheBasedAclCacheTests {\n\n\tprivate static final String TARGET_CLASS = \"org.springframework.security.acls.TargetObject\";\n\n\tprivate static CacheManager cacheManager;\n\n\t@BeforeAll\n\tpublic static void initCacheManaer() {\n\t\tcacheManager = new ConcurrentMapCacheManager();\n\t\t// Use disk caching immediately (to test for serialization issue reported in\n\t\t// SEC-527)\n\t\tcacheManager.getCache(\"springcasebasedacltests\");\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\tprivate Cache getCache() {\n\t\tCache cache = cacheManager.getCache(\"springcasebasedacltests\");\n\t\tcache.clear();\n\t\treturn cache;\n\t}\n\n\t@Test\n\tpublic void constructorRejectsNullParameters() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SpringCacheBasedAclCache(null, null, null));\n\t}\n\n\t@Test\n\tpublic void cacheOperationsAclWithoutParent() {\n\t\tCache cache = getCache();\n\t\tMap<?, ?> realCache = (Map<?, ?>) cache.getNativeCache();\n\t\tObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100L);\n\t\tAclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_OWNERSHIP\"), new SimpleGrantedAuthority(\"ROLE_AUDITING\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_GENERAL\"));\n\t\tAuditLogger auditLogger = new ConsoleAuditLogger();\n\t\tPermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);\n\t\tSpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy,\n\t\t\t\taclAuthorizationStrategy);\n\t\tMutableAcl acl = new AclImpl(identity, 1L, aclAuthorizationStrategy, auditLogger);\n\t\tassertThat(realCache).isEmpty();\n\t\tmyCache.putInCache(acl);\n\t\t// Check we can get from cache the same objects we put in\n\t\tassertThat(acl).isEqualTo(myCache.getFromCache(1L));\n\t\tassertThat(acl).isEqualTo(myCache.getFromCache(identity));\n\t\t// Put another object in cache\n\t\tObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, 101L);\n\t\tMutableAcl acl2 = new AclImpl(identity2, 2L, aclAuthorizationStrategy, new ConsoleAuditLogger());\n\t\tmyCache.putInCache(acl2);\n\t\t// Try to evict an entry that doesn't exist\n\t\tmyCache.evictFromCache(3L);\n\t\tmyCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, 102L));\n\t\tassertThat(realCache).hasSize(4);\n\t\tmyCache.evictFromCache(1L);\n\t\tassertThat(realCache).hasSize(2);\n\t\t// Check the second object inserted\n\t\tassertThat(acl2).isEqualTo(myCache.getFromCache(2L));\n\t\tassertThat(acl2).isEqualTo(myCache.getFromCache(identity2));\n\t\tmyCache.evictFromCache(identity2);\n\t\tassertThat(realCache).isEmpty();\n\t}\n\n\t@Test\n\tpublic void cacheOperationsAclWithParent() throws Exception {\n\t\tCache cache = getCache();\n\t\tMap<?, ?> realCache = (Map<?, ?>) cache.getNativeCache();\n\t\tAuthentication auth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_GENERAL\");\n\t\tauth.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 1L);\n\t\tObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, 2L);\n\t\tAclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_OWNERSHIP\"), new SimpleGrantedAuthority(\"ROLE_AUDITING\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_GENERAL\"));\n\t\tAuditLogger auditLogger = new ConsoleAuditLogger();\n\t\tPermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);\n\t\tSpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy,\n\t\t\t\taclAuthorizationStrategy);\n\t\tMutableAcl acl = new AclImpl(identity, 1L, aclAuthorizationStrategy, auditLogger);\n\t\tMutableAcl parentAcl = new AclImpl(identityParent, 2L, aclAuthorizationStrategy, auditLogger);\n\t\tacl.setParent(parentAcl);\n\t\tassertThat(realCache).isEmpty();\n\t\tmyCache.putInCache(acl);\n\t\tassertThat(4).isEqualTo(realCache.size());\n\t\t// Check we can get from cache the same objects we put in\n\t\tAclImpl aclFromCache = (AclImpl) myCache.getFromCache(1L);\n\t\tassertThat(aclFromCache).isEqualTo(acl);\n\t\t// SEC-951 check transient fields are set on parent\n\t\tassertThat(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), \"aclAuthorizationStrategy\")).isNotNull();\n\t\tassertThat(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), \"permissionGrantingStrategy\")).isNotNull();\n\t\tassertThat(myCache.getFromCache(identity)).isEqualTo(acl);\n\t\tassertThat(FieldUtils.getFieldValue(aclFromCache, \"aclAuthorizationStrategy\")).isNotNull();\n\t\tAclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(2L);\n\t\tassertThat(parentAclFromCache).isEqualTo(parentAcl);\n\t\tassertThat(FieldUtils.getFieldValue(parentAclFromCache, \"aclAuthorizationStrategy\")).isNotNull();\n\t\tassertThat(myCache.getFromCache(identityParent)).isEqualTo(parentAcl);\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/sid/CustomSid.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.sid;\n\nimport org.springframework.security.acls.model.Sid;\n\n/**\n * This class is example of custom {@link Sid} implementation\n *\n * @author Mikhail Stryzhonok\n */\npublic class CustomSid implements Sid {\n\n\tprivate String sid;\n\n\tpublic CustomSid(String sid) {\n\t\tthis.sid = sid;\n\t}\n\n\tpublic String getSid() {\n\t\treturn this.sid;\n\t}\n\n\tpublic void setSid(String sid) {\n\t\tthis.sid = sid;\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/sid/SidRetrievalStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.sid;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.acls.domain.GrantedAuthoritySid;\nimport org.springframework.security.acls.domain.PrincipalSid;\nimport org.springframework.security.acls.domain.SidRetrievalStrategyImpl;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.acls.model.SidRetrievalStrategy;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyCollection;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link SidRetrievalStrategyImpl}\n *\n * @author Andrei Stefan\n * @author Luke Taylor\n */\n@SuppressWarnings(\"unchecked\")\npublic class SidRetrievalStrategyTests {\n\n\tAuthentication authentication = new TestingAuthenticationToken(\"scott\", \"password\", \"A\", \"B\", \"C\");\n\n\t@Test\n\tpublic void correctSidsAreRetrieved() {\n\t\tSidRetrievalStrategy retrStrategy = new SidRetrievalStrategyImpl();\n\t\tList<Sid> sids = retrStrategy.getSids(this.authentication);\n\t\tassertThat(sids).isNotNull();\n\t\tassertThat(sids).hasSize(4);\n\t\tassertThat(sids.get(0)).isNotNull();\n\t\tassertThat(sids.get(0) instanceof PrincipalSid).isTrue();\n\t\tfor (int i = 1; i < sids.size(); i++) {\n\t\t\tassertThat(sids.get(i) instanceof GrantedAuthoritySid).isTrue();\n\t\t}\n\t\tassertThat(((PrincipalSid) sids.get(0)).getPrincipal()).isEqualTo(\"scott\");\n\t\tassertThat(((GrantedAuthoritySid) sids.get(1)).getGrantedAuthority()).isEqualTo(\"A\");\n\t\tassertThat(((GrantedAuthoritySid) sids.get(2)).getGrantedAuthority()).isEqualTo(\"B\");\n\t\tassertThat(((GrantedAuthoritySid) sids.get(3)).getGrantedAuthority()).isEqualTo(\"C\");\n\t}\n\n\t@Test\n\tpublic void roleHierarchyIsUsedWhenSet() {\n\t\tRoleHierarchy rh = mock(RoleHierarchy.class);\n\t\tList rhAuthorities = AuthorityUtils.createAuthorityList(\"D\");\n\t\tgiven(rh.getReachableGrantedAuthorities(anyCollection())).willReturn(rhAuthorities);\n\t\tSidRetrievalStrategy strat = new SidRetrievalStrategyImpl(rh);\n\t\tList<Sid> sids = strat.getSids(this.authentication);\n\t\tassertThat(sids).hasSize(2);\n\t\tassertThat(sids.get(0)).isNotNull();\n\t\tassertThat(sids.get(0) instanceof PrincipalSid).isTrue();\n\t\tassertThat(((GrantedAuthoritySid) sids.get(1)).getGrantedAuthority()).isEqualTo(\"D\");\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/java/org/springframework/security/acls/sid/SidTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.acls.sid;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.acls.domain.GrantedAuthoritySid;\nimport org.springframework.security.acls.domain.PrincipalSid;\nimport org.springframework.security.acls.model.Sid;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\npublic class SidTests {\n\n\t@Test\n\tpublic void testPrincipalSidConstructorsRequiredFields() {\n\t\t// Check one String-argument constructor\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PrincipalSid((String) null));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PrincipalSid(\"\"));\n\t\tassertThatNoException().isThrownBy(() -> new PrincipalSid(\"johndoe\"));\n\t\t// Check one Authentication-argument constructor\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PrincipalSid((Authentication) null));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new PrincipalSid(new TestingAuthenticationToken(null, \"password\")));\n\t\tassertThatNoException()\n\t\t\t.isThrownBy(() -> new PrincipalSid(new TestingAuthenticationToken(\"johndoe\", \"password\")));\n\t}\n\n\t@Test\n\tpublic void testGrantedAuthoritySidConstructorsRequiredFields() {\n\t\t// Check one String-argument constructor\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new GrantedAuthoritySid((String) null));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new GrantedAuthoritySid(\"\"));\n\t\tassertThatNoException().isThrownBy(() -> new GrantedAuthoritySid(\"ROLE_TEST\"));\n\t\t// Check one GrantedAuthority-argument constructor\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new GrantedAuthoritySid((GrantedAuthority) null));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new GrantedAuthoritySid(new SimpleGrantedAuthority(null)));\n\t\tassertThatNoException().isThrownBy(() -> new GrantedAuthoritySid(new SimpleGrantedAuthority(\"ROLE_TEST\")));\n\t}\n\n\t@Test\n\tpublic void testPrincipalSidEquals() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"johndoe\", \"password\");\n\t\tSid principalSid = new PrincipalSid(authentication);\n\t\tassertThat(principalSid.equals(null)).isFalse();\n\t\tassertThat(principalSid.equals(\"DIFFERENT_TYPE_OBJECT\")).isFalse();\n\t\tassertThat(principalSid.equals(principalSid)).isTrue();\n\t\tassertThat(principalSid.equals(new PrincipalSid(authentication))).isTrue();\n\t\tassertThat(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken(\"johndoe\", null)))).isTrue();\n\t\tassertThat(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken(\"scott\", null)))).isFalse();\n\t\tassertThat(principalSid.equals(new PrincipalSid(\"johndoe\"))).isTrue();\n\t\tassertThat(principalSid.equals(new PrincipalSid(\"scott\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void testGrantedAuthoritySidEquals() {\n\t\tGrantedAuthority ga = new SimpleGrantedAuthority(\"ROLE_TEST\");\n\t\tSid gaSid = new GrantedAuthoritySid(ga);\n\t\tassertThat(gaSid.equals(null)).isFalse();\n\t\tassertThat(gaSid.equals(\"DIFFERENT_TYPE_OBJECT\")).isFalse();\n\t\tassertThat(gaSid.equals(gaSid)).isTrue();\n\t\tassertThat(gaSid.equals(new GrantedAuthoritySid(ga))).isTrue();\n\t\tassertThat(gaSid.equals(new GrantedAuthoritySid(new SimpleGrantedAuthority(\"ROLE_TEST\")))).isTrue();\n\t\tassertThat(gaSid.equals(new GrantedAuthoritySid(new SimpleGrantedAuthority(\"ROLE_NOT_EQUAL\")))).isFalse();\n\t\tassertThat(gaSid.equals(new GrantedAuthoritySid(\"ROLE_TEST\"))).isTrue();\n\t\tassertThat(gaSid.equals(new GrantedAuthoritySid(\"ROLE_NOT_EQUAL\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void testPrincipalSidHashCode() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"johndoe\", \"password\");\n\t\tSid principalSid = new PrincipalSid(authentication);\n\t\tassertThat(principalSid.hashCode()).isEqualTo(\"johndoe\".hashCode());\n\t\tassertThat(principalSid.hashCode()).isEqualTo(new PrincipalSid(\"johndoe\").hashCode());\n\t\tassertThat(principalSid.hashCode()).isNotEqualTo(new PrincipalSid(\"scott\").hashCode());\n\t\tassertThat(principalSid.hashCode())\n\t\t\t.isNotEqualTo(new PrincipalSid(new TestingAuthenticationToken(\"scott\", \"password\")).hashCode());\n\t}\n\n\t@Test\n\tpublic void testGrantedAuthoritySidHashCode() {\n\t\tGrantedAuthority ga = new SimpleGrantedAuthority(\"ROLE_TEST\");\n\t\tSid gaSid = new GrantedAuthoritySid(ga);\n\t\tassertThat(gaSid.hashCode()).isEqualTo(\"ROLE_TEST\".hashCode());\n\t\tassertThat(gaSid.hashCode()).isEqualTo(new GrantedAuthoritySid(\"ROLE_TEST\").hashCode());\n\t\tassertThat(gaSid.hashCode()).isNotEqualTo(new GrantedAuthoritySid(\"ROLE_TEST_2\").hashCode());\n\t\tassertThat(gaSid.hashCode())\n\t\t\t.isNotEqualTo(new GrantedAuthoritySid(new SimpleGrantedAuthority(\"ROLE_TEST_2\")).hashCode());\n\t}\n\n\t@Test\n\tpublic void testGetters() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"johndoe\", \"password\");\n\t\tPrincipalSid principalSid = new PrincipalSid(authentication);\n\t\tGrantedAuthority ga = new SimpleGrantedAuthority(\"ROLE_TEST\");\n\t\tGrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga);\n\t\tassertThat(\"johndoe\").isEqualTo(principalSid.getPrincipal());\n\t\tassertThat(\"scott\".equals(principalSid.getPrincipal())).isFalse();\n\t\tassertThat(\"ROLE_TEST\").isEqualTo(gaSid.getGrantedAuthority());\n\t\tassertThat(\"ROLE_TEST2\".equals(gaSid.getGrantedAuthority())).isFalse();\n\t}\n\n\t@Test\n\tpublic void getPrincipalWhenPrincipalInstanceOfUserDetailsThenReturnsUsername() {\n\t\tUser user = new User(\"user\", \"password\", Collections.singletonList(new SimpleGrantedAuthority(\"ROLE_TEST\")));\n\t\tAuthentication authentication = new TestingAuthenticationToken(user, \"password\");\n\t\tPrincipalSid principalSid = new PrincipalSid(authentication);\n\t\tassertThat(\"user\").isEqualTo(principalSid.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void getPrincipalWhenPrincipalNotInstanceOfUserDetailsThenReturnsPrincipalName() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"token\", \"password\");\n\t\tPrincipalSid principalSid = new PrincipalSid(authentication);\n\t\tassertThat(\"token\").isEqualTo(principalSid.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void getPrincipalWhenCustomAuthenticationPrincipalThenReturnsPrincipalName() {\n\t\tAuthentication authentication = new CustomAuthenticationToken(new CustomToken(\"token\"), null);\n\t\tPrincipalSid principalSid = new PrincipalSid(authentication);\n\t\tassertThat(\"token\").isEqualTo(principalSid.getPrincipal());\n\t}\n\n\tstatic class CustomAuthenticationToken extends AbstractAuthenticationToken {\n\n\t\tprivate CustomToken principal;\n\n\t\tCustomAuthenticationToken(CustomToken principal, Collection<GrantedAuthority> authorities) {\n\t\t\tsuper(authorities);\n\t\t\tthis.principal = principal;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getCredentials() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic CustomToken getPrincipal() {\n\t\t\treturn this.principal;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn this.principal.getName();\n\t\t}\n\n\t}\n\n\tstatic class CustomToken {\n\n\t\tprivate String name;\n\n\t\tCustomToken(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tString getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "acl/src/test/resources/db/sql/test_data_hierarchy.sql",
    "content": "--- insert ACL data\nINSERT INTO ACL_SID (ID, PRINCIPAL, SID) VALUES\n    (10, true, 'user');\n\nINSERT INTO acl_class (id, class, class_id_type) VALUES\n    (20,'location','java.lang.String'),\n    (21,'org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockLongIdDomainObject','java.lang.Long'),\n    (22,'org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockUntypedIdDomainObject',''),\n    (23,'costcenter','java.util.UUID');\n\nINSERT INTO acl_object_identity (id, object_id_class, object_id_identity, parent_object, owner_sid, entries_inheriting) VALUES\n    (1,20,'US',NULL,10,false),\n    (2,20,'US-PAL',1,10,true),\n    (3,21,'4711',2,10,true),\n    (4,21,'4712',2,10,true),\n\t(5,22,'5000',3,10,true),\n    (6,23,'25d93b3f-c3aa-4814-9d5e-c7c96ced7762',5,10,true);\n"
  },
  {
    "path": "acl/src/test/resources/jdbcMutableAclServiceTests-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN//EN\" \"https://www.springframework.org/dtd/spring-beans.dtd\">\n\n<!--\n  - Application context containing business beans.\n  -\n  - Used by all artifacts.\n  -\n  -->\n\n<beans>\n\t<bean id=\"transactionManager\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n\t\t<property name=\"dataSource\" ref=\"dataSource\"/>\n\t</bean>\n\n\t<bean id=\"aclCache\" class=\"org.springframework.security.acls.domain.SpringCacheBasedAclCache\">\n\t\t<constructor-arg>\n\t\t   <bean class=\"org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean\">\n\t\t\t  <property name=\"name\" value=\"aclCache\"/>\n\t\t   </bean>\n\t\t</constructor-arg>\n\t\t<constructor-arg>\n\t\t\t<bean class=\"org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy\">\n\t\t\t\t<constructor-arg>\n\t\t\t\t\t<bean class=\"org.springframework.security.acls.domain.ConsoleAuditLogger\"/>\n\t\t\t\t</constructor-arg>\n\t\t\t</bean>\n\t\t</constructor-arg>\n\t\t<constructor-arg>\n\t\t\t<bean class=\"org.springframework.security.acls.domain.AclAuthorizationStrategyImpl\">\n\t\t\t\t<constructor-arg>\n\t\t\t\t\t<list>\n\t\t\t\t\t\t<bean class=\"org.springframework.security.core.authority.SimpleGrantedAuthority\">\n\t\t\t\t\t\t\t<constructor-arg value=\"ROLE_USER\"/>\n\t\t\t\t\t\t</bean>\n\t\t\t\t\t</list>\n\t\t\t\t</constructor-arg>\n\t\t\t</bean>\n\t\t</constructor-arg>\n\n\t</bean>\n\n\t<bean id=\"lookupStrategy\" class=\"org.springframework.security.acls.jdbc.BasicLookupStrategy\">\n\t\t<constructor-arg ref=\"dataSource\"/>\n\t\t<constructor-arg ref=\"aclCache\"/>\n\t\t<constructor-arg ref=\"aclAuthorizationStrategy\"/>\n\t\t<constructor-arg>\n\t\t\t<bean class=\"org.springframework.security.acls.domain.ConsoleAuditLogger\"/>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<bean id=\"aclAuthorizationStrategy\" class=\"org.springframework.security.acls.domain.AclAuthorizationStrategyImpl\">\n\t\t<constructor-arg>\n\t\t\t<list>\n\t\t\t\t<bean class=\"org.springframework.security.core.authority.SimpleGrantedAuthority\">\n\t\t\t\t\t<constructor-arg value=\"ROLE_ADMINISTRATOR\"/>\n\t\t\t\t</bean>\n\t\t\t\t<bean class=\"org.springframework.security.core.authority.SimpleGrantedAuthority\">\n\t\t\t\t\t<constructor-arg value=\"ROLE_ADMINISTRATOR\"/>\n\t\t\t\t</bean>\n\t\t\t\t<bean class=\"org.springframework.security.core.authority.SimpleGrantedAuthority\">\n\t\t\t\t\t<constructor-arg value=\"ROLE_ADMINISTRATOR\"/>\n\t\t\t\t</bean>\n\t\t\t</list>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<bean id=\"aclService\" class=\"org.springframework.security.acls.jdbc.JdbcMutableAclService\">\n\t\t<constructor-arg ref=\"dataSource\"/>\n\t\t<constructor-arg ref=\"lookupStrategy\"/>\n\t\t<constructor-arg ref=\"aclCache\"/>\n\n<!-- Uncomment to use PostgreSQL\n\t\t<property name=\"classIdentityQuery\" value=\"select currval(pg_get_serial_sequence('acl_class', 'id'))\"/>\n\t\t<property name=\"sidIdentityQuery\" value=\"select currval(pg_get_serial_sequence('acl_sid', 'id'))\"/>\n -->\n\t</bean>\n\n<!-- PostgreSQL DataSource configuration\n\n\t<bean id=\"dataSource\" class=\"org.springframework.jdbc.datasource.DriverManagerDataSource\">\n\t\t<property name=\"driverClassName\" value=\"org.postgresql.Driver\"/>\n\t\t<property name=\"url\" value=\"jdbc:postgresql://localhost:5432/acltest\"/>\n\t\t<property name=\"username\" value=\"acltest\"/>\n\t\t<property name=\"password\" value=\"acltest\"/>\n\t</bean>\n -->\n\t<bean id=\"dataSource\" class=\"org.springframework.jdbc.datasource.DriverManagerDataSource\">\n\t\t<property name=\"driverClassName\" value=\"org.hsqldb.jdbcDriver\"/>\n\t\t<property name=\"url\" value=\"jdbc:hsqldb:mem:acltest\"/>\n\t\t<property name=\"username\" value=\"sa\"/>\n\t\t<property name=\"password\" value=\"\"/>\n\t</bean>\n\n\t<bean id=\"jdbcTemplate\" class=\"org.springframework.jdbc.core.JdbcTemplate\">\n\t\t<property name=\"dataSource\" ref=\"dataSource\"/>\n\t</bean>\n\n</beans>\n"
  },
  {
    "path": "acl/src/test/resources/jdbcMutableAclServiceTestsWithAclClass-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n  <import resource=\"jdbcMutableAclServiceTests-context.xml\"/>\n\n  <bean id=\"conversionService\" class=\"org.springframework.core.convert.support.DefaultConversionService\"/>\n\n  <!-- Overridden bean definitions -->\n\n  <bean id=\"aclService\" class=\"org.springframework.security.acls.jdbc.JdbcMutableAclService\">\n    <constructor-arg ref=\"dataSource\"/>\n    <constructor-arg ref=\"lookupStrategy\"/>\n    <constructor-arg ref=\"aclCache\"/>\n    <property name=\"aclClassIdSupported\" value=\"true\"/>\n    <property name=\"conversionService\" ref=\"conversionService\"/>\n  </bean>\n\n  <bean id=\"lookupStrategy\" class=\"org.springframework.security.acls.jdbc.BasicLookupStrategy\">\n    <constructor-arg ref=\"dataSource\"/>\n    <constructor-arg ref=\"aclCache\"/>\n    <constructor-arg ref=\"aclAuthorizationStrategy\"/>\n    <constructor-arg>\n      <bean class=\"org.springframework.security.acls.domain.ConsoleAuditLogger\"/>\n    </constructor-arg>\n    <property name=\"aclClassIdSupported\" value=\"true\"/>\n    <property name=\"conversionService\" ref=\"conversionService\"/>\n  </bean>\n</beans>\n"
  },
  {
    "path": "acl/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "aspects/spring-security-aspects.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-module'\napply plugin: 'io.freefair.aspectj'\napply plugin: 'javadoc-warnings-error'\napply plugin: 'compile-warnings-error'\n\ncompileAspectj {\n\tsourceCompatibility = \"17\"\n\ttargetCompatibility = \"17\"\n\tajcOptions.compilerArgs += ['-Xlint:ignore']\n}\ncompileTestAspectj {\n\tsourceCompatibility = \"17\"\n\ttargetCompatibility = \"17\"\n\tajcOptions.compilerArgs += ['-Xlint:ignore']\n}\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi \"org.aspectj:aspectjrt\"\n\tapi project(':spring-security-core')\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\n\toptional project(':spring-security-access')\n\n\ttestImplementation 'org.springframework:spring-aop'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestAspect sourceSets.main.output\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\ncompileAspectj.ajcOptions.outxmlfile = \"META-INF/aop.xml\"\n"
  },
  {
    "path": "aspects/src/main/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspect.aj",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.access.intercept.aspectj.aspect;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.intercept.aspectj.AspectJCallback;\nimport org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\n\n/**\n * Concrete AspectJ aspect using Spring Security @Secured annotation\n * for JDK 1.5+.\n *\n * <p>\n * When using this aspect, you <i>must</i> annotate the implementation class\n * (and/or methods within that class), <i>not</i> the interface (if any) that\n * the class implements. AspectJ follows Java's rule that annotations on\n * interfaces are <i>not</i> inherited. This will vary from Spring AOP.\n *\n * @author Mike Wiesner\n * @author Luke Taylor\n * @since 3.1\n * @deprecated Use aspects in {@link org.springframework.security.authorization.method.aspectj} instead\n */\n@Deprecated\npublic aspect AnnotationSecurityAspect implements InitializingBean {\n\n\t\t/**\n\t\t * Matches the execution of any public method in a type with the Secured\n\t\t * annotation, or any subtype of a type with the Secured annotation.\n\t\t */\n\t\tprivate pointcut executionOfAnyPublicMethodInAtSecuredType() :\n\t\t\t\texecution(public * ((@Secured *)+).*(..)) && @this(Secured);\n\n\t\t/**\n\t\t * Matches the execution of any method with the Secured annotation.\n\t\t */\n\t\tprivate pointcut executionOfSecuredMethod() :\n\t\t\t\texecution(* *(..)) && @annotation(Secured);\n\n\t\t/**\n\t\t * Matches the execution of any method with Pre/Post annotations.\n\t\t */\n\t\tprivate pointcut executionOfPrePostAnnotatedMethod() :\n\t\t\t\texecution(* *(..)) && (@annotation(PreAuthorize) || @annotation(PreFilter)\n\t\t\t\t\t\t\t\t|| @annotation(PostAuthorize) || @annotation(PostFilter));\n\n\t\tprivate pointcut securedMethodExecution() :\n\t\t\t\texecutionOfAnyPublicMethodInAtSecuredType() ||\n\t\t\t\texecutionOfSecuredMethod() ||\n\t\t\t\texecutionOfPrePostAnnotatedMethod();\n\n\t\tprivate AspectJMethodSecurityInterceptor securityInterceptor;\n\n\t\tObject around(): securedMethodExecution() {\n\t\t\t\tif (this.securityInterceptor == null) {\n\t\t\t\t\t\treturn proceed();\n\t\t\t\t}\n\n\t\t\t\tAspectJCallback callback = () -> proceed();\n\n\t\t\t\treturn this.securityInterceptor.invoke(thisJoinPoint, callback);\n\t\t}\n\n\t\tpublic void setSecurityInterceptor(AspectJMethodSecurityInterceptor securityInterceptor) {\n\t\t\t\tthis.securityInterceptor = securityInterceptor;\n\t\t}\n\n\t\tpublic void afterPropertiesSet() {\n\t\t\t\tif (this.securityInterceptor == null) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"securityInterceptor required\");\n\t\t\t\t}\n\t\t}\n\n}\n"
  },
  {
    "path": "aspects/src/main/java/org/springframework/security/authorization/method/aspectj/AbstractMethodInterceptorAspect.aj",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.prepost.PostAuthorize;\n\n/**\n * Abstract AspectJ aspect for adapting a {@link MethodInvocation}\n *\n * @author Josh Cummings\n * @since 5.8\n */\nabstract aspect AbstractMethodInterceptorAspect {\n\n\tprotected abstract pointcut executionOfAnnotatedMethod();\n\n\tprivate MethodInterceptor securityInterceptor;\n\n\tObject around(): executionOfAnnotatedMethod() {\n\t\tif (this.securityInterceptor == null) {\n\t\t\treturn proceed();\n\t\t}\n\t\tMethodInvocation invocation = new JoinPointMethodInvocation(thisJoinPoint, () -> proceed());\n\t\ttry {\n\t\t\treturn this.securityInterceptor.invoke(invocation);\n\t\t} catch (Throwable t) {\n\t\t\tthrowUnchecked(t);\n\t\t\tthrow new IllegalStateException(\"Code unexpectedly reached\", t);\n\t\t}\n\t}\n\n\tpublic void setSecurityInterceptor(MethodInterceptor securityInterceptor) {\n\t\tthis.securityInterceptor = securityInterceptor;\n\t}\n\n\tprivate static void throwUnchecked(Throwable ex) {\n\t\tAbstractMethodInterceptorAspect.<RuntimeException>throwAny(ex);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <E extends Throwable> void throwAny(Throwable ex) throws E {\n\t\tthrow (E) ex;\n\t}\n}\n"
  },
  {
    "path": "aspects/src/main/java/org/springframework/security/authorization/method/aspectj/JoinPointMethodInvocation.aj",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method.aspectj;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Method;\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.reflect.CodeSignature;\n\nimport org.springframework.util.Assert;\n\nclass JoinPointMethodInvocation implements MethodInvocation {\n\n\tprivate final JoinPoint jp;\n\n\tprivate final Method method;\n\n\tprivate final Object target;\n\n\tprivate final Supplier<Object> proceed;\n\n\tJoinPointMethodInvocation(JoinPoint jp, Supplier<Object> proceed) {\n\t\tthis.jp = jp;\n\t\tif (jp.getTarget() != null) {\n\t\t\tthis.target = jp.getTarget();\n\t\t}\n\t\telse {\n\t\t\t// SEC-1295: target may be null if an ITD is in use\n\t\t\tthis.target = jp.getSignature().getDeclaringType();\n\t\t}\n\t\tString targetMethodName = jp.getStaticPart().getSignature().getName();\n\t\tClass<?>[] types = ((CodeSignature) jp.getStaticPart().getSignature()).getParameterTypes();\n\t\tClass<?> declaringType = jp.getStaticPart().getSignature().getDeclaringType();\n\t\tthis.method = findMethod(targetMethodName, declaringType, types);\n\t\tAssert.notNull(this.method, () -> \"Could not obtain target method from JoinPoint: '\" + jp + \"'\");\n\t\tthis.proceed = proceed;\n\t}\n\n\tprivate Method findMethod(String name, Class<?> declaringType, Class<?>[] params) {\n\t\tMethod method = null;\n\t\ttry {\n\t\t\tmethod = declaringType.getMethod(name, params);\n\t\t}\n\t\tcatch (NoSuchMethodException ignored) {\n\t\t}\n\t\tif (method == null) {\n\t\t\ttry {\n\t\t\t\tmethod = declaringType.getDeclaredMethod(name, params);\n\t\t\t}\n\t\t\tcatch (NoSuchMethodException ignored) {\n\t\t\t}\n\t\t}\n\t\treturn method;\n\t}\n\n\t@Override\n\tpublic Method getMethod() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic Object[] getArguments() {\n\t\treturn this.jp.getArgs();\n\t}\n\n\t@Override\n\tpublic AccessibleObject getStaticPart() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic Object getThis() {\n\t\treturn this.target;\n\t}\n\n\t@Override\n\tpublic Object proceed() throws Throwable {\n\t\treturn this.proceed.get();\n\t}\n\n}\n"
  },
  {
    "path": "aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PostAuthorizeAspect.aj",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.prepost.PostAuthorize;\n\n/**\n * Concrete AspectJ aspect using Spring Security @PostAuthorize annotation.\n *\n * <p>\n * When using this aspect, you <i>must</i> annotate the implementation class\n * (and/or methods within that class), <i>not</i> the interface (if any) that\n * the class implements. AspectJ follows Java's rule that annotations on\n * interfaces are <i>not</i> inherited. This will vary from Spring AOP.\n *\n * @author Mike Wiesner\n * @author Luke Taylor\n * @author Josh Cummings\n * @since 5.8\n */\npublic aspect PostAuthorizeAspect extends AbstractMethodInterceptorAspect {\n\n\t/**\n\t * Matches the execution of any method with a PostAuthorize annotation.\n\t */\n\tprotected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PostAuthorize);\n}\n"
  },
  {
    "path": "aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PostFilterAspect.aj",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.prepost.PostFilter;\n\n/**\n * Concrete AspectJ aspect using Spring Security @PostFilter annotation.\n *\n * <p>\n * When using this aspect, you <i>must</i> annotate the implementation class\n * (and/or methods within that class), <i>not</i> the interface (if any) that\n * the class implements. AspectJ follows Java's rule that annotations on\n * interfaces are <i>not</i> inherited. This will vary from Spring AOP.\n *\n * @author Mike Wiesner\n * @author Luke Taylor\n * @author Josh Cummings\n * @since 5.8\n */\npublic aspect PostFilterAspect extends AbstractMethodInterceptorAspect {\n\n\t/**\n\t * Matches the execution of any method with a PostFilter annotation.\n\t */\n\tprotected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PostFilter);\n\n}\n"
  },
  {
    "path": "aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PreAuthorizeAspect.aj",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * Concrete AspectJ aspect using Spring Security @PreAuthorize annotation.\n *\n * <p>\n * When using this aspect, you <i>must</i> annotate the implementation class\n * (and/or methods within that class), <i>not</i> the interface (if any) that\n * the class implements. AspectJ follows Java's rule that annotations on\n * interfaces are <i>not</i> inherited. This will vary from Spring AOP.\n *\n * @author Mike Wiesner\n * @author Luke Taylor\n * @author Josh Cummings\n * @since 5.8\n */\npublic aspect PreAuthorizeAspect extends AbstractMethodInterceptorAspect {\n\n\t/**\n\t * Matches the execution of any method with a PreAuthorize annotation.\n\t */\n\tprotected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PreAuthorize);\n}\n"
  },
  {
    "path": "aspects/src/main/java/org/springframework/security/authorization/method/aspectj/PreFilterAspect.aj",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.prepost.PreFilter;\n\n/**\n * Concrete AspectJ aspect using Spring Security @PreFilter annotation.\n *\n * <p>\n * When using this aspect, you <i>must</i> annotate the implementation class\n * (and/or methods within that class), <i>not</i> the interface (if any) that\n * the class implements. AspectJ follows Java's rule that annotations on\n * interfaces are <i>not</i> inherited. This will vary from Spring AOP.\n *\n * @author Mike Wiesner\n * @author Luke Taylor\n * @author Josh Cummings\n * @since 5.8\n */\npublic aspect PreFilterAspect extends AbstractMethodInterceptorAspect {\n\n\t/**\n\t * Matches the execution of any method with a PreFilter annotation.\n\t */\n\tprotected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PreFilter);\n\n}\n"
  },
  {
    "path": "aspects/src/main/java/org/springframework/security/authorization/method/aspectj/SecuredAspect.aj",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.access.annotation.Secured;\n\n/**\n * Concrete AspectJ aspect using Spring Security @Secured annotation.\n *\n * <p>\n * When using this aspect, you <i>must</i> annotate the implementation class\n * (and/or methods within that class), <i>not</i> the interface (if any) that\n * the class implements. AspectJ follows Java's rule that annotations on\n * interfaces are <i>not</i> inherited. This will vary from Spring AOP.\n *\n * @author Mike Wiesner\n * @author Luke Taylor\n * @author Josh Cummings\n * @since 5.8\n */\npublic aspect SecuredAspect extends AbstractMethodInterceptorAspect {\n\n\t/**\n\t * Matches the execution of any public method in a type with the Secured\n\t * annotation, or any subtype of a type with the Secured annotation.\n\t */\n\tprivate pointcut executionOfAnyPublicMethodInAtSecuredType() :\n\t\t\texecution(public * ((@Secured *)+).*(..)) && @this(Secured);\n\n\t/**\n\t * Matches the execution of any method with the Secured annotation.\n\t */\n\tprivate pointcut executionOfSecuredMethod() :\n\t\t\texecution(* *(..)) && @annotation(Secured);\n\n\tprotected pointcut executionOfAnnotatedMethod() :\n\t\t\texecutionOfAnyPublicMethodInAtSecuredType() ||\n\t\t\texecutionOfSecuredMethod();\n}\n"
  },
  {
    "path": "aspects/src/test/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspectTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.intercept.aspectj.aspect;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;\nimport org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;\nimport org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;\nimport org.springframework.security.access.intercept.AfterInvocationProviderManager;\nimport org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PostInvocationAdviceProvider;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;\nimport org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;\nimport org.springframework.security.access.vote.AffirmativeBased;\nimport org.springframework.security.access.vote.RoleVoter;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n * @since 3.0.3\n */\npublic class AnnotationSecurityAspectTests {\n\n\tprivate AffirmativeBased adm;\n\n\t@Mock\n\tprivate AuthenticationManager authman;\n\n\tprivate TestingAuthenticationToken anne = new TestingAuthenticationToken(\"anne\", \"\", \"ROLE_A\");\n\n\t// private TestingAuthenticationToken bob = new TestingAuthenticationToken(\"bob\", \"\",\n\t// \"ROLE_B\");\n\tprivate AspectJMethodSecurityInterceptor interceptor;\n\n\tprivate SecuredImpl secured = new SecuredImpl();\n\n\tprivate SecuredImplSubclass securedSub = new SecuredImplSubclass();\n\n\tprivate PrePostSecured prePostSecured = new PrePostSecured();\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tthis.interceptor = new AspectJMethodSecurityInterceptor();\n\t\tAccessDecisionVoter[] voters = new AccessDecisionVoter[] { new RoleVoter(),\n\t\t\t\tnew PreInvocationAuthorizationAdviceVoter(new ExpressionBasedPreInvocationAdvice()) };\n\t\tthis.adm = new AffirmativeBased(Arrays.<AccessDecisionVoter<? extends Object>>asList(voters));\n\t\tthis.interceptor.setAccessDecisionManager(this.adm);\n\t\tthis.interceptor.setAuthenticationManager(this.authman);\n\t\tthis.interceptor.setSecurityMetadataSource(new SecuredAnnotationSecurityMetadataSource());\n\t\tAnnotationSecurityAspect secAspect = AnnotationSecurityAspect.aspectOf();\n\t\tsecAspect.setSecurityInterceptor(this.interceptor);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void securedInterfaceMethodAllowsAllAccess() {\n\t\tthis.secured.securedMethod();\n\t}\n\n\t@Test\n\tpublic void securedClassMethodDeniesUnauthenticatedAccess() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.secured.securedClassMethod());\n\t}\n\n\t@Test\n\tpublic void securedClassMethodAllowsAccessToRoleA() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tthis.secured.securedClassMethod();\n\t}\n\n\t@Test\n\tpublic void internalPrivateCallIsIntercepted() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());\n\t}\n\n\t@Test\n\tpublic void protectedMethodIsIntercepted() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());\n\t}\n\n\t@Test\n\tpublic void overriddenProtectedMethodIsNotIntercepted() {\n\t\t// AspectJ doesn't inherit annotations\n\t\tthis.securedSub.protectedMethod();\n\t}\n\n\t// SEC-1262\n\t@Test\n\tpublic void denyAllPreAuthorizeDeniesAccess() {\n\t\tconfigureForElAnnotations();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);\n\t}\n\n\t@Test\n\tpublic void postFilterIsApplied() {\n\t\tconfigureForElAnnotations();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tList<String> objects = this.prePostSecured.postFilterMethod();\n\t\tassertThat(objects).hasSize(2);\n\t\tassertThat(objects).contains(\"apple\");\n\t\tassertThat(objects).contains(\"aubergine\");\n\t}\n\n\tprivate void configureForElAnnotations() {\n\t\tDefaultMethodSecurityExpressionHandler eh = new DefaultMethodSecurityExpressionHandler();\n\t\tthis.interceptor.setSecurityMetadataSource(\n\t\t\t\tnew PrePostAnnotationSecurityMetadataSource(new ExpressionBasedAnnotationAttributeFactory(eh)));\n\t\tthis.interceptor.setAccessDecisionManager(this.adm);\n\t\tAfterInvocationProviderManager aim = new AfterInvocationProviderManager();\n\t\taim.setProviders(Arrays.asList(new PostInvocationAdviceProvider(new ExpressionBasedPostInvocationAdvice(eh))));\n\t\tthis.interceptor.setAfterInvocationManager(aim);\n\t}\n\n\tinterface SecuredInterface {\n\n\t\t@Secured(\"ROLE_X\")\n\t\tvoid securedMethod();\n\n\t}\n\n\tstatic class SecuredImpl implements SecuredInterface {\n\n\t\t// Not really secured because AspectJ doesn't inherit annotations from interfaces\n\t\t@Override\n\t\tpublic void securedMethod() {\n\t\t}\n\n\t\t@Secured(\"ROLE_A\")\n\t\tpublic void securedClassMethod() {\n\t\t}\n\n\t\t@Secured(\"ROLE_X\")\n\t\tprivate void privateMethod() {\n\t\t}\n\n\t\t@Secured(\"ROLE_X\")\n\t\tprotected void protectedMethod() {\n\t\t}\n\n\t\t@Secured(\"ROLE_X\")\n\t\tpublic void publicCallsPrivate() {\n\t\t\tprivateMethod();\n\t\t}\n\n\t}\n\n\tstatic class SecuredImplSubclass extends SecuredImpl {\n\n\t\t@Override\n\t\tprotected void protectedMethod() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void publicCallsPrivate() {\n\t\t\tsuper.publicCallsPrivate();\n\t\t}\n\n\t}\n\n\tstatic class PrePostSecured {\n\n\t\t@PreAuthorize(\"denyAll\")\n\t\tpublic void denyAllMethod() {\n\t\t}\n\n\t\t@PostFilter(\"filterObject.startsWith('a')\")\n\t\tpublic List<String> postFilterMethod() {\n\t\t\tArrayList<String> objects = new ArrayList<>();\n\t\t\tobjects.addAll(Arrays.asList(new String[] { \"apple\", \"banana\", \"aubergine\", \"orange\" }));\n\t\t\treturn objects;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PostAuthorizeAspectTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.MockitoAnnotations;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n * @since 3.0.3\n */\npublic class PostAuthorizeAspectTests {\n\n\tprivate TestingAuthenticationToken anne = new TestingAuthenticationToken(\"anne\", \"\", \"ROLE_A\");\n\n\tprivate MethodInterceptor interceptor;\n\n\tprivate SecuredImpl secured = new SecuredImpl();\n\n\tprivate SecuredImplSubclass securedSub = new SecuredImplSubclass();\n\n\tprivate PrePostSecured prePostSecured = new PrePostSecured();\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tthis.interceptor = AuthorizationManagerAfterMethodInterceptor.postAuthorize();\n\t\tPostAuthorizeAspect secAspect = PostAuthorizeAspect.aspectOf();\n\t\tsecAspect.setSecurityInterceptor(this.interceptor);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void securedInterfaceMethodAllowsAllAccess() {\n\t\tthis.secured.securedMethod();\n\t}\n\n\t@Test\n\tpublic void securedClassMethodDeniesUnauthenticatedAccess() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.secured.securedClassMethod());\n\t}\n\n\t@Test\n\tpublic void securedClassMethodAllowsAccessToRoleA() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tthis.secured.securedClassMethod();\n\t}\n\n\t@Test\n\tpublic void internalPrivateCallIsIntercepted() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());\n\t}\n\n\t@Test\n\tpublic void protectedMethodIsIntercepted() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());\n\t}\n\n\t@Test\n\tpublic void overriddenProtectedMethodIsNotIntercepted() {\n\t\t// AspectJ doesn't inherit annotations\n\t\tthis.securedSub.protectedMethod();\n\t}\n\n\t// SEC-1262\n\t@Test\n\tpublic void denyAllPreAuthorizeDeniesAccess() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);\n\t}\n\n\t@Test\n\tpublic void nestedDenyAllPostAuthorizeDeniesAccess() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.secured.myObject().denyAllMethod());\n\t}\n\n\tinterface SecuredInterface {\n\n\t\t@PostAuthorize(\"hasRole('X')\")\n\t\tvoid securedMethod();\n\n\t}\n\n\tstatic class SecuredImpl implements SecuredInterface {\n\n\t\t// Not really secured because AspectJ doesn't inherit annotations from interfaces\n\t\t@Override\n\t\tpublic void securedMethod() {\n\t\t}\n\n\t\t@PostAuthorize(\"hasRole('A')\")\n\t\tvoid securedClassMethod() {\n\t\t}\n\n\t\t@PostAuthorize(\"hasRole('X')\")\n\t\tprivate void privateMethod() {\n\t\t}\n\n\t\t@PostAuthorize(\"hasRole('X')\")\n\t\tprotected void protectedMethod() {\n\t\t}\n\n\t\t@PostAuthorize(\"hasRole('X')\")\n\t\tvoid publicCallsPrivate() {\n\t\t\tprivateMethod();\n\t\t}\n\n\t\tNestedObject myObject() {\n\t\t\treturn new NestedObject();\n\t\t}\n\n\t}\n\n\tstatic class SecuredImplSubclass extends SecuredImpl {\n\n\t\t@Override\n\t\tprotected void protectedMethod() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void publicCallsPrivate() {\n\t\t\tsuper.publicCallsPrivate();\n\t\t}\n\n\t}\n\n\tstatic class PrePostSecured {\n\n\t\t@PostAuthorize(\"denyAll\")\n\t\tvoid denyAllMethod() {\n\t\t}\n\n\t}\n\n\tstatic class NestedObject {\n\n\t\t@PostAuthorize(\"denyAll\")\n\t\tvoid denyAllMethod() {\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PostFilterAspectTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method.aspectj;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.MockitoAnnotations;\n\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n * @since 3.0.3\n */\npublic class PostFilterAspectTests {\n\n\tprivate MethodInterceptor interceptor;\n\n\tprivate PrePostSecured prePostSecured = new PrePostSecured();\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tthis.interceptor = new PostFilterAuthorizationMethodInterceptor();\n\t\tPostFilterAspect secAspect = PostFilterAspect.aspectOf();\n\t\tsecAspect.setSecurityInterceptor(this.interceptor);\n\t}\n\n\t@Test\n\tpublic void preFilterMethodWhenListThenFilters() {\n\t\tList<String> objects = new ArrayList<>(Arrays.asList(\"apple\", \"banana\", \"aubergine\", \"orange\"));\n\t\tassertThat(this.prePostSecured.postFilterMethod(objects)).containsExactly(\"apple\", \"aubergine\");\n\t}\n\n\t@Test\n\tpublic void nestedDenyAllPostFilterDeniesAccess() {\n\t\tassertThat(this.prePostSecured.myObject().denyAllMethod()).isEmpty();\n\t}\n\n\tstatic class PrePostSecured {\n\n\t\t@PostFilter(\"filterObject.startsWith('a')\")\n\t\tList<String> postFilterMethod(List<String> objects) {\n\t\t\treturn objects;\n\t\t}\n\n\t\tNestedObject myObject() {\n\t\t\treturn new NestedObject();\n\t\t}\n\n\t}\n\n\tstatic class NestedObject {\n\n\t\t@PostFilter(\"filterObject == null\")\n\t\tList<String> denyAllMethod() {\n\t\t\treturn new ArrayList<>(List.of(\"deny\"));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PreAuthorizeAspectTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.MockitoAnnotations;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n * @since 3.0.3\n */\npublic class PreAuthorizeAspectTests {\n\n\tprivate TestingAuthenticationToken anne = new TestingAuthenticationToken(\"anne\", \"\", \"ROLE_A\");\n\n\tprivate MethodInterceptor interceptor;\n\n\tprivate SecuredImpl secured = new SecuredImpl();\n\n\tprivate SecuredImplSubclass securedSub = new SecuredImplSubclass();\n\n\tprivate PrePostSecured prePostSecured = new PrePostSecured();\n\n\tprivate MultipleInterfaces multiple = new MultipleInterfaces();\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tthis.interceptor = AuthorizationManagerBeforeMethodInterceptor.preAuthorize();\n\t\tPreAuthorizeAspect secAspect = PreAuthorizeAspect.aspectOf();\n\t\tsecAspect.setSecurityInterceptor(this.interceptor);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void securedInterfaceMethodAllowsAllAccess() {\n\t\tthis.secured.securedMethod();\n\t}\n\n\t@Test\n\tpublic void securedClassMethodDeniesUnauthenticatedAccess() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.secured.securedClassMethod());\n\t}\n\n\t@Test\n\tpublic void securedClassMethodAllowsAccessToRoleA() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tthis.secured.securedClassMethod();\n\t}\n\n\t@Test\n\tpublic void internalPrivateCallIsIntercepted() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());\n\t}\n\n\t@Test\n\tpublic void protectedMethodIsIntercepted() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());\n\t}\n\n\t@Test\n\tpublic void overriddenProtectedMethodIsNotIntercepted() {\n\t\t// AspectJ doesn't inherit annotations\n\t\tthis.securedSub.protectedMethod();\n\t}\n\n\t// SEC-1262\n\t@Test\n\tpublic void denyAllPreAuthorizeDeniesAccess() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);\n\t}\n\n\t@Test\n\tpublic void nestedDenyAllPreAuthorizeDeniesAccess() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.secured.myObject().denyAllMethod());\n\t}\n\n\t@Test\n\tpublic void multipleInterfacesPreAuthorizeAllows() {\n\t\t// aspectj doesn't inherit annotations\n\t\tthis.multiple.securedMethod();\n\t}\n\n\tinterface SecuredInterface {\n\n\t\t@PreAuthorize(\"hasRole('X')\")\n\t\tvoid securedMethod();\n\n\t}\n\n\tstatic class SecuredImpl implements SecuredInterface {\n\n\t\t// Not really secured because AspectJ doesn't inherit annotations from interfaces\n\t\t@Override\n\t\tpublic void securedMethod() {\n\t\t}\n\n\t\t@PreAuthorize(\"hasRole('A')\")\n\t\tvoid securedClassMethod() {\n\t\t}\n\n\t\t@PreAuthorize(\"hasRole('X')\")\n\t\tprivate void privateMethod() {\n\t\t}\n\n\t\t@PreAuthorize(\"hasRole('X')\")\n\t\tprotected void protectedMethod() {\n\t\t}\n\n\t\t@PreAuthorize(\"hasRole('A')\")\n\t\tvoid publicCallsPrivate() {\n\t\t\tprivateMethod();\n\t\t}\n\n\t\tNestedObject myObject() {\n\t\t\treturn new NestedObject();\n\t\t}\n\n\t}\n\n\tstatic class SecuredImplSubclass extends SecuredImpl {\n\n\t\t@Override\n\t\tprotected void protectedMethod() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void publicCallsPrivate() {\n\t\t\tsuper.publicCallsPrivate();\n\t\t}\n\n\t}\n\n\tstatic class PrePostSecured {\n\n\t\t@PreAuthorize(\"denyAll\")\n\t\tvoid denyAllMethod() {\n\t\t}\n\n\t}\n\n\tstatic class NestedObject {\n\n\t\t@PreAuthorize(\"denyAll\")\n\t\tvoid denyAllMethod() {\n\n\t\t}\n\n\t}\n\n\tinterface AnotherSecuredInterface {\n\n\t\t@PreAuthorize(\"hasRole('Y')\")\n\t\tvoid securedMethod();\n\n\t}\n\n\tstatic class MultipleInterfaces implements SecuredInterface, AnotherSecuredInterface {\n\n\t\t@Override\n\t\tpublic void securedMethod() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "aspects/src/test/java/org/springframework/security/authorization/method/aspectj/PreFilterAspectTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method.aspectj;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.MockitoAnnotations;\n\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n * @since 3.0.3\n */\npublic class PreFilterAspectTests {\n\n\tprivate MethodInterceptor interceptor;\n\n\tprivate PrePostSecured prePostSecured = new PrePostSecured();\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tthis.interceptor = new PreFilterAuthorizationMethodInterceptor();\n\t\tPreFilterAspect secAspect = PreFilterAspect.aspectOf();\n\t\tsecAspect.setSecurityInterceptor(this.interceptor);\n\t}\n\n\t@Test\n\tpublic void preFilterMethodWhenListThenFilters() {\n\t\tList<String> objects = new ArrayList<>(Arrays.asList(\"apple\", \"banana\", \"aubergine\", \"orange\"));\n\t\tassertThat(this.prePostSecured.preFilterMethod(objects)).containsExactly(\"apple\", \"aubergine\");\n\t}\n\n\t@Test\n\tpublic void nestedDenyAllPreFilterDeniesAccess() {\n\t\tassertThat(this.prePostSecured.myObject().denyAllMethod(new ArrayList<>(List.of(\"deny\")))).isEmpty();\n\t}\n\n\tstatic class PrePostSecured {\n\n\t\t@PreFilter(\"filterObject.startsWith('a')\")\n\t\tList<String> preFilterMethod(List<String> objects) {\n\t\t\treturn objects;\n\t\t}\n\n\t\tNestedObject myObject() {\n\t\t\treturn new NestedObject();\n\t\t}\n\n\t}\n\n\tstatic class NestedObject {\n\n\t\t@PreFilter(\"filterObject == null\")\n\t\tList<String> denyAllMethod(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "aspects/src/test/java/org/springframework/security/authorization/method/aspectj/SecuredAspectTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method.aspectj;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.MockitoAnnotations;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n * @since 3.0.3\n */\npublic class SecuredAspectTests {\n\n\tprivate TestingAuthenticationToken anne = new TestingAuthenticationToken(\"anne\", \"\", \"ROLE_A\");\n\n\tprivate MethodInterceptor interceptor;\n\n\tprivate SecuredImpl secured = new SecuredImpl();\n\n\tprivate SecuredImplSubclass securedSub = new SecuredImplSubclass();\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tthis.interceptor = AuthorizationManagerBeforeMethodInterceptor.secured();\n\t\tSecuredAspect secAspect = SecuredAspect.aspectOf();\n\t\tsecAspect.setSecurityInterceptor(this.interceptor);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void securedInterfaceMethodAllowsAllAccess() {\n\t\tthis.secured.securedMethod();\n\t}\n\n\t@Test\n\tpublic void securedClassMethodDeniesUnauthenticatedAccess() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.secured.securedClassMethod());\n\t}\n\n\t@Test\n\tpublic void securedClassMethodAllowsAccessToRoleA() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tthis.secured.securedClassMethod();\n\t}\n\n\t@Test\n\tpublic void internalPrivateCallIsIntercepted() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());\n\t}\n\n\t@Test\n\tpublic void protectedMethodIsIntercepted() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.anne);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());\n\t}\n\n\t@Test\n\tpublic void overriddenProtectedMethodIsNotIntercepted() {\n\t\t// AspectJ doesn't inherit annotations\n\t\tthis.securedSub.protectedMethod();\n\t}\n\n\tinterface SecuredInterface {\n\n\t\t@Secured(\"ROLE_X\")\n\t\tvoid securedMethod();\n\n\t}\n\n\tstatic class SecuredImpl implements SecuredInterface {\n\n\t\t// Not really secured because AspectJ doesn't inherit annotations from interfaces\n\t\t@Override\n\t\tpublic void securedMethod() {\n\t\t}\n\n\t\t@Secured(\"ROLE_A\")\n\t\tvoid securedClassMethod() {\n\t\t}\n\n\t\t@Secured(\"ROLE_X\")\n\t\tprivate void privateMethod() {\n\t\t}\n\n\t\t@Secured(\"ROLE_X\")\n\t\tprotected void protectedMethod() {\n\t\t}\n\n\t\t@Secured(\"ROLE_X\")\n\t\tvoid publicCallsPrivate() {\n\t\t\tprivateMethod();\n\t\t}\n\n\t}\n\n\tstatic class SecuredImplSubclass extends SecuredImpl {\n\n\t\t@Override\n\t\tprotected void protectedMethod() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void publicCallsPrivate() {\n\t\t\tsuper.publicCallsPrivate();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "aspects/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "bom/spring-security-bom.gradle",
    "content": "import io.spring.gradle.convention.SpringModulePlugin\n\napply plugin: 'io.spring.convention.bom'\napply plugin: 'compile-warnings-error'\n\ndependencies {\n\tconstraints {\n\t\tproject.rootProject.allprojects { p ->\n\t\t\tp.plugins.withType(SpringModulePlugin) {\n\t\t\t\tapi p\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "build.gradle",
    "content": "import io.spring.gradle.IncludeRepoTask\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport trang.RncToXsd\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nbuildscript {\n\tdependencies {\n\t\tclasspath libs.io.spring.javaformat.spring.javaformat.gradle.plugin\n\t\tclasspath libs.io.spring.nohttp.nohttp.gradle\n\t\tclasspath libs.io.freefair.gradle.aspectj.plugin\n\t\tclasspath libs.org.jetbrains.kotlin.kotlin.gradle.plugin\n\t\tclasspath libs.com.netflix.nebula.nebula.project.plugin\n\t}\n\trepositories {\n\t\tmaven { url='https://plugins.gradle.org/m2/' }\n\t}\n}\n\nplugins {\n\talias(libs.plugins.org.gradle.wrapper.upgrade)\n}\n\napply plugin: 'io.spring.nohttp'\napply plugin: 'locks'\napply plugin: 'io.spring.convention.root'\napply plugin: 'org.jetbrains.kotlin.jvm'\napply plugin: 'org.springframework.security.versions.verify-dependencies-versions'\napply plugin: 'org.springframework.security.check-expected-branch-version'\napply plugin: 'io.spring.security.release'\n\ngroup = 'org.springframework.security'\ndescription = 'Spring Security'\n\next.snapshotBuild = version.contains(\"SNAPSHOT\")\next.releaseBuild = version.contains(\"SNAPSHOT\")\next.milestoneBuild = !(snapshotBuild || releaseBuild)\n\nrepositories {\n\tmavenCentral()\n\tmaven { url = \"https://repo.spring.io/milestone\" }\n}\n\nspringRelease {\n\tweekOfMonth = 3\n\tdayOfWeek = 1\n\treferenceDocUrl = \"https://docs.spring.io/spring-security/reference/{version}/index.html\"\n\tapiDocUrl = \"https://docs.spring.io/spring-security/reference/{version}/api/java/index.html\"\n\treplaceSnapshotVersionInReferenceDocUrl = true\n}\n\nallprojects {\n\tif (!['spring-security-bom', 'spring-security-docs'].contains(project.name)) {\n\t\tapply plugin: 'io.spring.javaformat'\n\t\tapply plugin: 'checkstyle'\n\n\t\tpluginManager.withPlugin(\"io.spring.convention.checkstyle\") {\n\t\t\tdependencies {\n\t\t\t\tcheckstyle libs.io.spring.javaformat.spring.javaformat.checkstyle\n\t\t\t}\n\t\t\tcheckstyle {\n\t\t\t\ttoolVersion = '8.34'\n\t\t\t}\n\t\t}\n\n\t\tif (project.name.contains('sample')) {\n\t\t\ttasks.whenTaskAdded { task ->\n\t\t\t\tif (task.name.contains('format') || task.name.contains('checkFormat') || task.name.contains(\"checkstyle\")) {\n\t\t\t\t\ttask.enabled = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\ndevelocity {\n\tbuildScan {\n\t\ttermsOfUseUrl = 'https://gradle.com/help/legal-terms-of-use'\n\t\ttermsOfUseAgree = 'yes'\n\t}\n}\n\nnohttp {\n\tsource.exclude \"buildSrc/build/**\", \"**/build/**\", \"**/target/**\", \"javascript/.gradle/**\", \"javascript/package-lock.json\", \"javascript/node_modules/**\", \"javascript/build/**\", \"javascript/dist/**\"\n\tsource.builtBy(project(':spring-security-config').tasks.withType(RncToXsd))\n}\n\ntasks.named('checkstyleNohttp') {\n\tmaxHeapSize = '1g'\n}\n\ntasks.register('cloneRepository', IncludeRepoTask) {\n\trepository = project.getProperties().get(\"repositoryName\")\n\tref = project.getProperties().get(\"ref\")\n\tvar defaultDirectory = project.file(\"build/tmp/clone\")\n\toutputDirectory = project.hasProperty(\"cloneOutputDirectory\") ? project.file(\"$cloneOutputDirectory\") : defaultDirectory\n}\n\nwrapperUpgrade {\n\tgradle {\n\t\t'spring-security' {\n\t\t\trepo = 'spring-projects/spring-security'\n\t\t\tbaseBranch = '6.3.x' // runs only on 6.3.x and the update is merged forward to main\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/.idea/compiler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <bytecodeTargetLevel target=\"1.8\" />\n  </component>\n</project>"
  },
  {
    "path": "buildSrc/.idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleMigrationSettings\" migrationVersion=\"1\" />\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"gradleHome\" value=\"$USER_HOME$/.sdkman/candidates/gradle/current\" />\n        <option name=\"gradleJvm\" value=\"11.0.9.hs-adpt\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n          </set>\n        </option>\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": "buildSrc/.idea/jarRepositories.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RemoteRepositoriesConfiguration\">\n    <remote-repository>\n      <option name=\"id\" value=\"central\" />\n      <option name=\"name\" value=\"Maven Central repository\" />\n      <option name=\"url\" value=\"https://repo1.maven.org/maven2\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"jboss.community\" />\n      <option name=\"name\" value=\"JBoss Community repository\" />\n      <option name=\"url\" value=\"https://repository.jboss.org/nexus/content/repositories/public/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"MavenRepo\" />\n      <option name=\"name\" value=\"MavenRepo\" />\n      <option name=\"url\" value=\"https://repo.maven.apache.org/maven2/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"Gradle Central Plugin Repository\" />\n      <option name=\"name\" value=\"Gradle Central Plugin Repository\" />\n      <option name=\"url\" value=\"https://plugins.gradle.org/m2\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"BintrayJCenter\" />\n      <option name=\"name\" value=\"BintrayJCenter\" />\n      <option name=\"url\" value=\"https://jcenter.bintray.com/\" />\n    </remote-repository>\n    <remote-repository>\n      <option name=\"id\" value=\"maven\" />\n      <option name=\"name\" value=\"maven\" />\n      <option name=\"url\" value=\"https://repo.spring.io/plugins-release/\" />\n    </remote-repository>\n  </component>\n</project>"
  },
  {
    "path": "buildSrc/.idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ExternalStorageConfigurationManager\" enabled=\"true\" />\n  <component name=\"FrameworkDetectionExcludesConfiguration\">\n    <file type=\"web\" url=\"file://$PROJECT_DIR$\" />\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_8\" project-jdk-name=\"11\" project-jdk-type=\"JavaSDK\" />\n</project>"
  },
  {
    "path": "buildSrc/.idea/uiDesigner.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Palette2\">\n    <group name=\"Swing\">\n      <item class=\"com.intellij.uiDesigner.HSpacer\" tooltip-text=\"Horizontal Spacer\" icon=\"/com/intellij/uiDesigner/icons/hspacer.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" />\n      </item>\n      <item class=\"com.intellij.uiDesigner.VSpacer\" tooltip-text=\"Vertical Spacer\" icon=\"/com/intellij/uiDesigner/icons/vspacer.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" />\n      </item>\n      <item class=\"javax.swing.JPanel\" icon=\"/com/intellij/uiDesigner/icons/panel.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" />\n      </item>\n      <item class=\"javax.swing.JScrollPane\" icon=\"/com/intellij/uiDesigner/icons/scrollPane.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"7\" hsize-policy=\"7\" anchor=\"0\" fill=\"3\" />\n      </item>\n      <item class=\"javax.swing.JButton\" icon=\"/com/intellij/uiDesigner/icons/button.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" />\n        <initial-values>\n          <property name=\"text\" value=\"Button\" />\n        </initial-values>\n      </item>\n      <item class=\"javax.swing.JRadioButton\" icon=\"/com/intellij/uiDesigner/icons/radioButton.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" />\n        <initial-values>\n          <property name=\"text\" value=\"RadioButton\" />\n        </initial-values>\n      </item>\n      <item class=\"javax.swing.JCheckBox\" icon=\"/com/intellij/uiDesigner/icons/checkBox.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" />\n        <initial-values>\n          <property name=\"text\" value=\"CheckBox\" />\n        </initial-values>\n      </item>\n      <item class=\"javax.swing.JLabel\" icon=\"/com/intellij/uiDesigner/icons/label.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" />\n        <initial-values>\n          <property name=\"text\" value=\"Label\" />\n        </initial-values>\n      </item>\n      <item class=\"javax.swing.JTextField\" icon=\"/com/intellij/uiDesigner/icons/textField.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\">\n          <preferred-size width=\"150\" height=\"-1\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JPasswordField\" icon=\"/com/intellij/uiDesigner/icons/passwordField.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\">\n          <preferred-size width=\"150\" height=\"-1\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JFormattedTextField\" icon=\"/com/intellij/uiDesigner/icons/formattedTextField.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\">\n          <preferred-size width=\"150\" height=\"-1\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JTextArea\" icon=\"/com/intellij/uiDesigner/icons/textArea.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JTextPane\" icon=\"/com/intellij/uiDesigner/icons/textPane.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JEditorPane\" icon=\"/com/intellij/uiDesigner/icons/editorPane.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JComboBox\" icon=\"/com/intellij/uiDesigner/icons/comboBox.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"2\" anchor=\"8\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JTable\" icon=\"/com/intellij/uiDesigner/icons/table.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JList\" icon=\"/com/intellij/uiDesigner/icons/list.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"2\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JTree\" icon=\"/com/intellij/uiDesigner/icons/tree.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JTabbedPane\" icon=\"/com/intellij/uiDesigner/icons/tabbedPane.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"200\" height=\"200\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JSplitPane\" icon=\"/com/intellij/uiDesigner/icons/splitPane.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"200\" height=\"200\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JSpinner\" icon=\"/com/intellij/uiDesigner/icons/spinner.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JSlider\" icon=\"/com/intellij/uiDesigner/icons/slider.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JSeparator\" icon=\"/com/intellij/uiDesigner/icons/separator.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\" />\n      </item>\n      <item class=\"javax.swing.JProgressBar\" icon=\"/com/intellij/uiDesigner/icons/progressbar.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JToolBar\" icon=\"/com/intellij/uiDesigner/icons/toolbar.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\">\n          <preferred-size width=\"-1\" height=\"20\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JToolBar$Separator\" icon=\"/com/intellij/uiDesigner/icons/toolbarSeparator.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JScrollBar\" icon=\"/com/intellij/uiDesigner/icons/scrollbar.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"0\" anchor=\"0\" fill=\"2\" />\n      </item>\n    </group>\n  </component>\n</project>"
  },
  {
    "path": "buildSrc/.idea/workspace.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"AutoImportSettings\">\n    <option name=\"autoReloadType\" value=\"SELECTIVE\" />\n  </component>\n  <component name=\"ChangeListManager\">\n    <list default=\"true\" id=\"1650039e-90b3-4603-bb34-47f415f4e67a\" name=\"Default Changelist\" comment=\"\">\n      <change beforePath=\"$PROJECT_DIR$/gradlew\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/gradlew\" afterDir=\"false\" />\n      <change beforePath=\"$PROJECT_DIR$/gradlew.bat\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/gradlew.bat\" afterDir=\"false\" />\n      <change beforePath=\"$PROJECT_DIR$/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesExtension.java\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesExtension.java\" afterDir=\"false\" />\n      <change beforePath=\"$PROJECT_DIR$/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesPlugin.java\" beforeDir=\"false\" afterPath=\"$PROJECT_DIR$/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesPlugin.java\" afterDir=\"false\" />\n    </list>\n    <option name=\"SHOW_DIALOG\" value=\"false\" />\n    <option name=\"HIGHLIGHT_CONFLICTS\" value=\"true\" />\n    <option name=\"HIGHLIGHT_NON_ACTIVE_CHANGELIST\" value=\"false\" />\n    <option name=\"LAST_RESOLUTION\" value=\"IGNORE\" />\n  </component>\n  <component name=\"ExternalProjectsData\">\n    <projectState path=\"$PROJECT_DIR$\">\n      <ProjectState />\n    </projectState>\n  </component>\n  <component name=\"ExternalProjectsManager\">\n    <system id=\"GRADLE\">\n      <state>\n        <task path=\"$PROJECT_DIR$\">\n          <activation />\n        </task>\n        <projects_view>\n          <tree_state>\n            <expand>\n              <path>\n                <item name=\"\" type=\"6a2764b6:ExternalProjectsStructure$RootNode\" />\n                <item name=\"buildSrc\" type=\"f1a62948:ProjectNode\" />\n              </path>\n              <path>\n                <item name=\"\" type=\"6a2764b6:ExternalProjectsStructure$RootNode\" />\n                <item name=\"buildSrc\" type=\"f1a62948:ProjectNode\" />\n                <item name=\"Run Configurations\" type=\"7b0102dc:RunConfigurationsNode\" />\n              </path>\n            </expand>\n            <select />\n          </tree_state>\n        </projects_view>\n      </state>\n    </system>\n  </component>\n  <component name=\"FileTemplateManagerImpl\">\n    <option name=\"RECENT_TEMPLATES\">\n      <list>\n        <option value=\"Class\" />\n      </list>\n    </option>\n  </component>\n  <component name=\"Git.Settings\">\n    <option name=\"RECENT_GIT_ROOT_PATH\" value=\"$PROJECT_DIR$/..\" />\n  </component>\n  <component name=\"GitSEFilterConfiguration\">\n    <file-type-list>\n      <filtered-out-file-type name=\"LOCAL_BRANCH\" />\n      <filtered-out-file-type name=\"REMOTE_BRANCH\" />\n      <filtered-out-file-type name=\"TAG\" />\n      <filtered-out-file-type name=\"COMMIT_BY_MESSAGE\" />\n    </file-type-list>\n  </component>\n  <component name=\"ProjectId\" id=\"1qXvSePBfcQC8Y4nOwtetSNoy4Q\" />\n  <component name=\"ProjectLevelVcsManager\" settingsEditedManually=\"true\" />\n  <component name=\"ProjectViewState\">\n    <option name=\"hideEmptyMiddlePackages\" value=\"true\" />\n    <option name=\"showLibraryContents\" value=\"true\" />\n  </component>\n  <component name=\"PropertiesComponent\">\n    <property name=\"RunOnceActivity.OpenProjectViewOnStart\" value=\"true\" />\n    <property name=\"RunOnceActivity.ShowReadmeOnStart\" value=\"true\" />\n    <property name=\"WebServerToolWindowFactoryState\" value=\"false\" />\n    <property name=\"aspect.path.notification.shown\" value=\"true\" />\n    <property name=\"last_opened_file_path\" value=\"$PROJECT_DIR$\" />\n    <property name=\"node.js.detected.package.eslint\" value=\"true\" />\n    <property name=\"node.js.detected.package.tslint\" value=\"true\" />\n    <property name=\"node.js.path.for.package.eslint\" value=\"project\" />\n    <property name=\"node.js.path.for.package.tslint\" value=\"project\" />\n    <property name=\"node.js.selected.package.eslint\" value=\"(autodetect)\" />\n    <property name=\"node.js.selected.package.tslint\" value=\"(autodetect)\" />\n  </component>\n  <component name=\"ReactorSettings\">\n    <option name=\"notificationShown\" value=\"true\" />\n  </component>\n  <component name=\"RecentsManager\">\n    <key name=\"MoveClassesOrPackagesDialog.RECENTS_KEY\">\n      <recent name=\"org.springframework.security.convention\" />\n    </key>\n  </component>\n  <component name=\"RunManager\" selected=\"Gradle.DocsPluginITest.build triggers docs\">\n    <configuration default=\"true\" type=\"ArquillianJUnit\" factoryName=\"\" nameIsGenerated=\"true\">\n      <option name=\"arquillianRunConfiguration\">\n        <value>\n          <option name=\"containerStateName\" value=\"\" />\n        </value>\n      </option>\n      <option name=\"TEST_OBJECT\" value=\"class\" />\n      <method v=\"2\">\n        <option name=\"Make\" enabled=\"true\" />\n      </method>\n    </configuration>\n    <configuration name=\"DocsPluginITest.build triggers docs\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\" temporary=\"true\">\n      <ExternalSystemSettings>\n        <option name=\"executionName\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n        <option name=\"scriptParameters\" value=\"--tests &quot;io.spring.gradle.convention.DocsPluginITest.build triggers docs&quot;\" />\n        <option name=\"taskDescriptions\">\n          <list />\n        </option>\n        <option name=\"taskNames\">\n          <list>\n            <option value=\":test\" />\n          </list>\n        </option>\n        <option name=\"vmOptions\" />\n      </ExternalSystemSettings>\n      <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>\n      <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n      <DebugAllEnabled>false</DebugAllEnabled>\n      <method v=\"2\" />\n    </configuration>\n    <configuration name=\"GitHubApiTests.findCreateIssueInput\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\" temporary=\"true\">\n      <ExternalSystemSettings>\n        <option name=\"executionName\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n        <option name=\"scriptParameters\" value=\"--tests &quot;org.springframework.security.convention.versions.GitHubApiTests.findCreateIssueInput&quot;\" />\n        <option name=\"taskDescriptions\">\n          <list />\n        </option>\n        <option name=\"taskNames\">\n          <list>\n            <option value=\":test\" />\n          </list>\n        </option>\n        <option name=\"vmOptions\" />\n      </ExternalSystemSettings>\n      <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>\n      <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n      <DebugAllEnabled>false</DebugAllEnabled>\n      <method v=\"2\" />\n    </configuration>\n    <configuration name=\"GitHubApiTests.findRepositoryId\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\" temporary=\"true\">\n      <ExternalSystemSettings>\n        <option name=\"executionName\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n        <option name=\"scriptParameters\" value=\"--tests &quot;org.springframework.security.convention.versions.GitHubApiTests.findRepositoryId&quot;\" />\n        <option name=\"taskDescriptions\">\n          <list />\n        </option>\n        <option name=\"taskNames\">\n          <list>\n            <option value=\":test\" />\n          </list>\n        </option>\n        <option name=\"vmOptions\" />\n      </ExternalSystemSettings>\n      <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>\n      <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n      <DebugAllEnabled>false</DebugAllEnabled>\n      <method v=\"2\" />\n    </configuration>\n    <recent_temporary>\n      <list>\n        <item itemvalue=\"Gradle.DocsPluginITest.build triggers docs\" />\n        <item itemvalue=\"Gradle.GitHubApiTests.findCreateIssueInput\" />\n        <item itemvalue=\"Gradle.GitHubApiTests.findRepositoryId\" />\n      </list>\n    </recent_temporary>\n  </component>\n  <component name=\"SpellCheckerSettings\" RuntimeDictionaries=\"0\" Folders=\"0\" CustomDictionaries=\"0\" DefaultDictionary=\"application-level\" UseSingleDictionary=\"true\" transferred=\"true\" />\n  <component name=\"TaskManager\">\n    <task active=\"true\" id=\"Default\" summary=\"Default task\">\n      <changelist id=\"1650039e-90b3-4603-bb34-47f415f4e67a\" name=\"Default Changelist\" comment=\"\" />\n      <created>1617238749852</created>\n      <option name=\"number\" value=\"Default\" />\n      <option name=\"presentableId\" value=\"Default\" />\n      <updated>1617238749852</updated>\n      <workItem from=\"1617238751049\" duration=\"982000\" />\n      <workItem from=\"1617239768987\" duration=\"22510000\" />\n      <workItem from=\"1617314253813\" duration=\"9753000\" />\n      <workItem from=\"1617372923830\" duration=\"9278000\" />\n    </task>\n    <servers />\n  </component>\n  <component name=\"TypeScriptGeneratedFilesManager\">\n    <option name=\"version\" value=\"3\" />\n  </component>\n  <component name=\"Vcs.Log.Tabs.Properties\">\n    <option name=\"TAB_STATES\">\n      <map>\n        <entry key=\"MAIN\">\n          <value>\n            <State />\n          </value>\n        </entry>\n      </map>\n    </option>\n    <option name=\"oldMeFiltersMigrated\" value=\"true\" />\n  </component>\n</project>"
  },
  {
    "path": "buildSrc/build.gradle",
    "content": "plugins {\n\tid \"java-gradle-plugin\"\n\tid \"groovy-gradle-plugin\"\n\tid \"java\"\n\tid \"groovy\"\n}\n\njava {\n\tsourceCompatibility = JavaVersion.VERSION_17\n}\n\nrepositories {\n\tgradlePluginPortal()\n\tmavenCentral()\n\tmaven { url = 'https://repo.spring.io/snapshot' }\n}\n\nsourceSets {\n\tmain {\n\t\tjava {\n\t\t\tsrcDirs = []\n\t\t}\n\t\tgroovy {\n\t\t\tsrcDirs += [\"src/main/java\"]\n\t\t}\n\t}\n}\n\ngradlePlugin {\n\tplugins {\n\t\ttrang {\n\t\t\tid = \"trang\"\n\t\t\timplementationClass = \"trang.TrangPlugin\"\n\t\t}\n\t\tlocks {\n\t\t\tid = \"locks\"\n\t\t\timplementationClass = \"lock.GlobalLockPlugin\"\n\t\t}\n\t\tmanagementConfiguration {\n\t\t\tid = \"io.spring.convention.management-configuration\"\n\t\t\timplementationClass = \"io.spring.gradle.convention.ManagementConfigurationPlugin\"\n\t\t}\n\t\ts101 {\n\t\t\tid = \"s101\"\n\t\t\timplementationClass = \"s101.S101Plugin\"\n\t\t}\n\t\tverifyDependenciesVersions {\n\t\t\tid = \"org.springframework.security.versions.verify-dependencies-versions\"\n\t\t\timplementationClass = \"org.springframework.security.convention.versions.VerifyDependenciesVersionsPlugin\"\n\t\t}\n\t\tcheckExpectedBranchVersion {\n\t\t\tid = \"org.springframework.security.check-expected-branch-version\"\n\t\t\timplementationClass = \"org.springframework.security.CheckExpectedBranchVersionPlugin\"\n\t\t}\n\t}\n}\n\nconfigurations {\n\timplementation {\n\t\texclude module: 'groovy-all'\n\t}\n}\n\ndependencies {\n\timplementation platform(libs.io.projectreactor.reactor.bom)\n\n\timplementation libs.spring.nullability\n\timplementation libs.com.google.code.gson.gson\n\timplementation libs.com.thaiopensource.trag\n\timplementation libs.net.sourceforge.saxon.saxon\n\timplementation libs.org.yaml.snakeyaml\n\timplementation localGroovy()\n\n\timplementation libs.io.github.gradle.nexus.publish.plugin\n\timplementation 'io.projectreactor:reactor-core'\n\timplementation libs.org.gretty.gretty\n\timplementation libs.com.github.ben.manes.gradle.versions.plugin\n\timplementation libs.com.github.spullara.mustache.java.compiler\n\timplementation libs.io.spring.javaformat.spring.javaformat.gradle.plugin\n\timplementation libs.io.spring.nohttp.nohttp.gradle\n\timplementation libs.org.jetbrains.kotlin.kotlin.gradle.plugin\n\timplementation (libs.net.sourceforge.htmlunit) {\n\t\texclude group: 'org.eclipse.jetty.websocket', module: 'websocket-client'\n\t}\n\timplementation libs.org.hidetake.gradle.ssh.plugin\n\timplementation libs.org.jfrog.buildinfo.build.info.extractor.gradle\n\timplementation libs.org.sonarsource.scanner.gradle.sonarqube.gradle.plugin\n\timplementation libs.com.squareup.okhttp3.okhttp\n\timplementation libs.io.spring.security.release.plugin\n\n\ttestImplementation platform(libs.org.junit.junit.bom)\n\ttestImplementation platform(libs.org.mockito.mockito.bom)\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation libs.org.apache.commons.commons.io\n\ttestImplementation libs.org.assertj.assertj.core\n\ttestImplementation 'org.mockito:mockito-core'\n\ttestImplementation 'org.mockito:mockito-junit-jupiter'\n\ttestImplementation libs.com.squareup.okhttp3.mockwebserver\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\n\ntasks.named('test', Test).configure {\n\tonlyIf { !project.hasProperty(\"buildSrc.skipTests\") }\n\tuseJUnitPlatform()\n\tjvmArgs(\n\t\t\t'--add-opens', 'java.base/java.lang=ALL-UNNAMED',\n\t\t\t'--add-opens', 'java.base/java.util=ALL-UNNAMED'\n\t)\n}\n"
  },
  {
    "path": "buildSrc/settings.gradle",
    "content": "dependencyResolutionManagement {\n\tversionCatalogs {\n\t\tlibs {\n\t\t\tfrom(files(\"../gradle/libs.versions.toml\"))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/graphql/org/springframework/security/convention/versions/CreateIssue.graphql",
    "content": "mutation CreateIssueInput($assigneeId: ID!, $labelIds: [ID!], $milestoneId: ID!, $repositoryId: ID!, $title: String!) {\n    createIssue(input: {assigneeIds: [$assigneeId], labelIds: $labelIds, milestoneId: $milestoneId, projectIds: [], repositoryId: $repositoryId, title: $title}) {\n        issue {\n            number\n        }\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/graphql/org/springframework/security/convention/versions/FindCreateIssueInput.graphql",
    "content": "query FindCreateIssueInput($owner: String!, $name: String!, $labelQuery: String, $milestoneName: String) {\n  repository(owner: $owner, name: $name) {\n      id\n      labels(query: $labelQuery, first: 1) {\n          nodes {\n              id\n              name\n          }\n      }\n      milestones(query: $milestoneName, states: [OPEN], first: 1) {\n          nodes {\n              id\n              title\n          }\n      }\n  }\n  viewer {\n      id\n  }\n}\n"
  },
  {
    "path": "buildSrc/src/main/graphql/org/springframework/security/convention/versions/RateLimit.graphql",
    "content": "query RateLimit {\n    rateLimit {\n        limit\n        cost\n        remaining\n        resetAt\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/graphql/org/springframework/security/convention/versions/schema.json",
    "content": "{\"data\":{\"__schema\":{\"queryType\":{\"name\":\"Query\"},\"mutationType\":{\"name\":\"Mutation\"},\"subscriptionType\":null,\"types\":[{\"kind\":\"INPUT_OBJECT\",\"name\":\"AcceptEnterpriseAdministratorInvitationInput\",\"description\":\"Autogenerated input type of AcceptEnterpriseAdministratorInvitation\",\"fields\":null,\"inputFields\":[{\"name\":\"invitationId\",\"description\":\"The id of the invitation being accepted\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AcceptEnterpriseAdministratorInvitationPayload\",\"description\":\"Autogenerated return type of AcceptEnterpriseAdministratorInvitation\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitation\",\"description\":\"The invitation that was accepted.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of accepting an administrator invitation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AcceptTopicSuggestionInput\",\"description\":\"Autogenerated input type of AcceptTopicSuggestion\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the suggested topic.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AcceptTopicSuggestionPayload\",\"description\":\"Autogenerated return type of AcceptTopicSuggestion\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topic\",\"description\":\"The accepted topic.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"description\":\"Represents an object which can take actions on GitHub. Typically a User or Bot.\",\"fields\":[{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the actor's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"The username of the actor.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this actor.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this actor.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Bot\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccount\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Mannequin\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"description\":\"Location information for an actor\",\"fields\":[{\"name\":\"city\",\"description\":\"City\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"country\",\"description\":\"Country name\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"countryCode\",\"description\":\"Country code\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"region\",\"description\":\"Region name\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"regionCode\",\"description\":\"Region or state code\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddAssigneesToAssignableInput\",\"description\":\"Autogenerated input type of AddAssigneesToAssignable\",\"fields\":null,\"inputFields\":[{\"name\":\"assignableId\",\"description\":\"The id of the assignable object to add assignees to.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"assigneeIds\",\"description\":\"The id of users to add as assignees.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddAssigneesToAssignablePayload\",\"description\":\"Autogenerated return type of AddAssigneesToAssignable\",\"fields\":[{\"name\":\"assignable\",\"description\":\"The item that was assigned.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Assignable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddCommentInput\",\"description\":\"Autogenerated input type of AddComment\",\"fields\":null,\"inputFields\":[{\"name\":\"subjectId\",\"description\":\"The Node ID of the subject to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The contents of the comment.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddCommentPayload\",\"description\":\"Autogenerated return type of AddComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commentEdge\",\"description\":\"The edge from the subject's comment connection.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"IssueCommentEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"The subject\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"timelineEdge\",\"description\":\"The edge from the subject's timeline connection.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineItemEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddEnterpriseSupportEntitlementInput\",\"description\":\"Autogenerated input type of AddEnterpriseSupportEntitlement\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the Enterprise which the admin belongs to.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"login\",\"description\":\"The login of a member who will receive the support entitlement.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddEnterpriseSupportEntitlementPayload\",\"description\":\"Autogenerated return type of AddEnterpriseSupportEntitlement\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of adding the support entitlement.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddLabelsToLabelableInput\",\"description\":\"Autogenerated input type of AddLabelsToLabelable\",\"fields\":null,\"inputFields\":[{\"name\":\"labelableId\",\"description\":\"The id of the labelable object to add labels to.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"labelIds\",\"description\":\"The ids of the labels to add.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddLabelsToLabelablePayload\",\"description\":\"Autogenerated return type of AddLabelsToLabelable\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"labelable\",\"description\":\"The item that was labeled.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Labelable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddProjectCardInput\",\"description\":\"Autogenerated input type of AddProjectCard\",\"fields\":null,\"inputFields\":[{\"name\":\"projectColumnId\",\"description\":\"The Node ID of the ProjectColumn.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"contentId\",\"description\":\"The content of the card. Must be a member of the ProjectCardItem union\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"note\",\"description\":\"The note on the card.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddProjectCardPayload\",\"description\":\"Autogenerated return type of AddProjectCard\",\"fields\":[{\"name\":\"cardEdge\",\"description\":\"The edge from the ProjectColumn's card connection.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCardEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectColumn\",\"description\":\"The ProjectColumn\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumn\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddProjectColumnInput\",\"description\":\"Autogenerated input type of AddProjectColumn\",\"fields\":null,\"inputFields\":[{\"name\":\"projectId\",\"description\":\"The Node ID of the project.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the column.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddProjectColumnPayload\",\"description\":\"Autogenerated return type of AddProjectColumn\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"columnEdge\",\"description\":\"The edge from the project's column connection.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumnEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The project\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddPullRequestReviewCommentInput\",\"description\":\"Autogenerated input type of AddPullRequestReviewComment\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"The node ID of the pull request reviewing\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pullRequestReviewId\",\"description\":\"The Node ID of the review to modify.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"commitOID\",\"description\":\"The SHA of the commit to comment on.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The text of the comment.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"path\",\"description\":\"The relative path of the file to comment on.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"position\",\"description\":\"The line index in the diff to comment on.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"inReplyTo\",\"description\":\"The comment id to reply to.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddPullRequestReviewCommentPayload\",\"description\":\"Autogenerated return type of AddPullRequestReviewComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"comment\",\"description\":\"The newly created comment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commentEdge\",\"description\":\"The edge from the review's comment connection.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewCommentEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddPullRequestReviewInput\",\"description\":\"Autogenerated input type of AddPullRequestReview\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"The Node ID of the pull request to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"commitOID\",\"description\":\"The commit OID the review pertains to.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The contents of the review body comment.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"event\",\"description\":\"The event to perform on the pull request review.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewEvent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"comments\",\"description\":\"The review line comments.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DraftPullRequestReviewComment\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"threads\",\"description\":\"The review line comment threads.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DraftPullRequestReviewThread\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddPullRequestReviewPayload\",\"description\":\"Autogenerated return type of AddPullRequestReview\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReview\",\"description\":\"The newly created pull request review.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reviewEdge\",\"description\":\"The edge from the pull request's review connection.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddPullRequestReviewThreadInput\",\"description\":\"Autogenerated input type of AddPullRequestReviewThread\",\"fields\":null,\"inputFields\":[{\"name\":\"path\",\"description\":\"Path to the file being commented on.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"Body of the thread's first comment.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"pullRequestId\",\"description\":\"The node ID of the pull request reviewing\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pullRequestReviewId\",\"description\":\"The Node ID of the review to modify.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"line\",\"description\":\"The line of the blob to which the thread refers. The end of the line range for multi-line comments.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"side\",\"description\":\"The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"DiffSide\",\"ofType\":null},\"defaultValue\":\"RIGHT\"},{\"name\":\"startLine\",\"description\":\"The first line of the range to which the comment refers.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"startSide\",\"description\":\"The side of the diff on which the start line resides.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"DiffSide\",\"ofType\":null},\"defaultValue\":\"RIGHT\"},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddPullRequestReviewThreadPayload\",\"description\":\"Autogenerated return type of AddPullRequestReviewThread\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"thread\",\"description\":\"The newly created thread.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddReactionInput\",\"description\":\"Autogenerated input type of AddReaction\",\"fields\":null,\"inputFields\":[{\"name\":\"subjectId\",\"description\":\"The Node ID of the subject to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"The name of the emoji to react with.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddReactionPayload\",\"description\":\"Autogenerated return type of AddReaction\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reaction\",\"description\":\"The reaction object.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Reaction\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"The reactable subject.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddStarInput\",\"description\":\"Autogenerated input type of AddStar\",\"fields\":null,\"inputFields\":[{\"name\":\"starrableId\",\"description\":\"The Starrable ID to star.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddStarPayload\",\"description\":\"Autogenerated return type of AddStar\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"starrable\",\"description\":\"The starrable.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Starrable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddVerifiableDomainInput\",\"description\":\"Autogenerated input type of AddVerifiableDomain\",\"fields\":null,\"inputFields\":[{\"name\":\"ownerId\",\"description\":\"The ID of the owner to add the domain to\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"domain\",\"description\":\"The URL of the domain\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddVerifiableDomainPayload\",\"description\":\"Autogenerated return type of AddVerifiableDomain\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"domain\",\"description\":\"The verifiable domain that was added.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomain\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AddedToProjectEvent\",\"description\":\"Represents a 'added_to_project' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"App\",\"description\":\"A GitHub App.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The description of the app.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"logoBackgroundColor\",\"description\":\"The hex color code, without the leading '#', for the logo background.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"logoUrl\",\"description\":\"A URL pointing to the app's logo.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the app.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"slug\",\"description\":\"A slug based on the name of the app for use in URLs.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The URL to the app's homepage.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ApproveVerifiableDomainInput\",\"description\":\"Autogenerated input type of ApproveVerifiableDomain\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the verifiable domain to approve.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ApproveVerifiableDomainPayload\",\"description\":\"Autogenerated return type of ApproveVerifiableDomain\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"domain\",\"description\":\"The verifiable domain that was approved.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomain\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ArchiveRepositoryInput\",\"description\":\"Autogenerated input type of ArchiveRepository\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The ID of the repository to mark as archived.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ArchiveRepositoryPayload\",\"description\":\"Autogenerated return type of ArchiveRepository\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository that was marked as archived.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Assignable\",\"description\":\"An object that can have users assigned to it.\",\"fields\":[{\"name\":\"assignees\",\"description\":\"A list of Users assigned to this object.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"AssignedEvent\",\"description\":\"Represents an 'assigned' event on any assignable object.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"assignable\",\"description\":\"Identifies the assignable associated with the event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Assignable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"assignee\",\"description\":\"Identifies the user or mannequin that was assigned.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"Assignee\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"Identifies the user who was assigned.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":true,\"deprecationReason\":\"Assignees can now be mannequins. Use the `assignee` field instead. Removal on 2020-01-01 UTC.\"}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"Assignee\",\"description\":\"Types that can be assigned to issues.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Bot\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Mannequin\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"description\":\"An entry in the audit log.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposClearAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OauthApplicationCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddBillingManagerAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgBlockUserAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableOauthAppRestrictionsAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableSamlAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableTwoFactorRequirementAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableOauthAppRestrictionsAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableSamlAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableTwoFactorRequirementAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteToBusinessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessApprovedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessDeniedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessRequestedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveBillingManagerAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUnblockUserAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateDefaultRepositoryPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryCreationPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoArchivedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoChangeMergeSettingAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigLockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigUnlockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoDestroyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddRepositoryAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamChangeParentTeamAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveRepositoryAuditEntry\",\"ofType\":null}]},{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"description\":\"Types that can initiate an audit log event.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Bot\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"AuditLogOrder\",\"description\":\"Ordering options for Audit Log connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order Audit Logs by.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"AuditLogOrderField\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"AuditLogOrderField\",\"description\":\"Properties by which Audit Log connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order audit log entries by timestamp\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoMergeDisabledEvent\",\"description\":\"Represents a 'auto_merge_disabled' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"disabler\",\"description\":\"The user who disabled auto-merge for this Pull Request\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reason\",\"description\":\"The reason auto-merge was disabled\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reasonCode\",\"description\":\"The reason_code relating to why auto-merge was disabled\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoMergeEnabledEvent\",\"description\":\"Represents a 'auto_merge_enabled' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enabler\",\"description\":\"The user who enabled auto-merge for this Pull Request\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoMergeRequest\",\"description\":\"Represents an auto-merge request for a pull request\",\"fields\":[{\"name\":\"authorEmail\",\"description\":\"The email address of the author of this auto-merge request.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitBody\",\"description\":\"The commit message of the auto-merge request.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitHeadline\",\"description\":\"The commit title of the auto-merge request.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enabledAt\",\"description\":\"When was this auto-merge request was enabled.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enabledBy\",\"description\":\"The actor who created the auto-merge request.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeMethod\",\"description\":\"The merge method of the auto-merge request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestMergeMethod\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request that this auto-merge request is set against.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoRebaseEnabledEvent\",\"description\":\"Represents a 'auto_rebase_enabled' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enabler\",\"description\":\"The user who enabled auto-merge (rebase) for this Pull Request\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoSquashEnabledEvent\",\"description\":\"Represents a 'auto_squash_enabled' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enabler\",\"description\":\"The user who enabled auto-merge (squash) for this Pull Request\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AutomaticBaseChangeFailedEvent\",\"description\":\"Represents a 'automatic_base_change_failed' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"newBase\",\"description\":\"The new base for this PR\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oldBase\",\"description\":\"The old base for this PR\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"AutomaticBaseChangeSucceededEvent\",\"description\":\"Represents a 'automatic_base_change_succeeded' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"newBase\",\"description\":\"The new base for this PR\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oldBase\",\"description\":\"The old base for this PR\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefChangedEvent\",\"description\":\"Represents a 'base_ref_changed' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"currentRefName\",\"description\":\"Identifies the name of the base ref for the pull request after it was changed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"previousRefName\",\"description\":\"Identifies the name of the base ref for the pull request before it was changed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefDeletedEvent\",\"description\":\"Represents a 'base_ref_deleted' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"baseRefName\",\"description\":\"Identifies the name of the Ref associated with the `base_ref_deleted` event.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefForcePushedEvent\",\"description\":\"Represents a 'base_ref_force_pushed' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"afterCommit\",\"description\":\"Identifies the after commit SHA for the 'base_ref_force_pushed' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"beforeCommit\",\"description\":\"Identifies the before commit SHA for the 'base_ref_force_pushed' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ref\",\"description\":\"Identifies the fully qualified ref name for the 'base_ref_force_pushed' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Blame\",\"description\":\"Represents a Git blame.\",\"fields\":[{\"name\":\"ranges\",\"description\":\"The list of ranges from a Git blame.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"BlameRange\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BlameRange\",\"description\":\"Represents a range of information from a Git blame.\",\"fields\":[{\"name\":\"age\",\"description\":\"Identifies the recency of the change, from 1 (new) to 10 (old). This is calculated as a 2-quantile and determines the length of distance between the median age of all the changes in the file and the recency of the current range's change.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"Identifies the line author\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"endingLine\",\"description\":\"The ending line for the range\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"startingLine\",\"description\":\"The starting line for the range\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Blob\",\"description\":\"Represents a Git blob.\",\"fields\":[{\"name\":\"abbreviatedOid\",\"description\":\"An abbreviated version of the Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"byteSize\",\"description\":\"Byte size of Blob object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitResourcePath\",\"description\":\"The HTTP path for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitUrl\",\"description\":\"The HTTP URL for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isBinary\",\"description\":\"Indicates whether the Blob is binary or text. Returns null if unable to determine the encoding.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isTruncated\",\"description\":\"Indicates whether the contents is truncated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oid\",\"description\":\"The Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The Repository the Git object belongs to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"text\",\"description\":\"UTF8 text data or null if the Blob is binary\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"description\":\"Represents `true` or `false` values.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Bot\",\"description\":\"A special type of user which takes actions on behalf of GitHub Apps.\",\"fields\":[{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the GitHub App's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"The username of the actor.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this bot\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this bot\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"description\":\"A branch protection rule.\",\"fields\":[{\"name\":\"allowsDeletions\",\"description\":\"Can this branch be deleted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"allowsForcePushes\",\"description\":\"Are force pushes allowed on this branch.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"branchProtectionRuleConflicts\",\"description\":\"A list of conflicts matching branches protection rule and other branch protection rules\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConflictConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"creator\",\"description\":\"The actor who created this branch protection rule.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dismissesStaleReviews\",\"description\":\"Will new commits pushed to matching branches dismiss pull request review approvals.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isAdminEnforced\",\"description\":\"Can admins overwrite branch protection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"matchingRefs\",\"description\":\"Repository refs that are protected by this rule\",\"args\":[{\"name\":\"query\",\"description\":\"Filters refs with query on name\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RefConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pattern\",\"description\":\"Identifies the protection rule pattern.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pushAllowances\",\"description\":\"A list push allowances for this branch protection rule.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PushAllowanceConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this branch protection rule.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiredApprovingReviewCount\",\"description\":\"Number of approving reviews required to update matching branches.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiredStatusCheckContexts\",\"description\":\"List of required status check contexts that must pass for commits to be accepted to matching branches.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresApprovingReviews\",\"description\":\"Are approving reviews required to update matching branches.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresCodeOwnerReviews\",\"description\":\"Are reviews from code owners required to update matching branches.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresCommitSignatures\",\"description\":\"Are commits required to be signed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresLinearHistory\",\"description\":\"Are merge commits prohibited from being pushed to this branch.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresStatusChecks\",\"description\":\"Are status checks required to update matching branches.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresStrictStatusChecks\",\"description\":\"Are branches required to be up to date before merging.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restrictsPushes\",\"description\":\"Is pushing to matching branches restricted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restrictsReviewDismissals\",\"description\":\"Is dismissal of pull request reviews restricted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reviewDismissalAllowances\",\"description\":\"A list review dismissal allowances for this branch protection rule.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissalAllowanceConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConflict\",\"description\":\"A conflict between two branch protection rules.\",\"fields\":[{\"name\":\"branchProtectionRule\",\"description\":\"Identifies the branch protection rule.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"conflictingBranchProtectionRule\",\"description\":\"Identifies the conflicting branch protection rule.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ref\",\"description\":\"Identifies the branch ref that has conflicting rules\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConflictConnection\",\"description\":\"The connection type for BranchProtectionRuleConflict.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConflictEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConflict\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConflictEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConflict\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConnection\",\"description\":\"The connection type for BranchProtectionRule.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CVSS\",\"description\":\"The Common Vulnerability Scoring System\",\"fields\":[{\"name\":\"score\",\"description\":\"The CVSS score associated with this advisory\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"vectorString\",\"description\":\"The CVSS vector string associated with this advisory\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CWE\",\"description\":\"A common weakness enumeration\",\"fields\":[{\"name\":\"cweId\",\"description\":\"The id of the CWE\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"A detailed description of this CWE\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":\"ID of the object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of this CWE\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CWEConnection\",\"description\":\"The connection type for CWE.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CWEEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CWE\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CWEEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CWE\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CancelEnterpriseAdminInvitationInput\",\"description\":\"Autogenerated input type of CancelEnterpriseAdminInvitation\",\"fields\":null,\"inputFields\":[{\"name\":\"invitationId\",\"description\":\"The Node ID of the pending enterprise administrator invitation.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CancelEnterpriseAdminInvitationPayload\",\"description\":\"Autogenerated return type of CancelEnterpriseAdminInvitation\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitation\",\"description\":\"The invitation that was canceled.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of canceling an administrator invitation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ChangeUserStatusInput\",\"description\":\"Autogenerated input type of ChangeUserStatus\",\"fields\":null,\"inputFields\":[{\"name\":\"emoji\",\"description\":\"The emoji to represent your status. Can either be a native Unicode emoji or an emoji name with colons, e.g., :grinning:.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"message\",\"description\":\"A short description of your current status.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"organizationId\",\"description\":\"The ID of the organization whose members will be allowed to see the status. If omitted, the status will be publicly visible.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"limitedAvailability\",\"description\":\"Whether this status should indicate you are not fully available on GitHub, e.g., you are away.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"expiresAt\",\"description\":\"If set, the user status will not be shown after this date.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ChangeUserStatusPayload\",\"description\":\"Autogenerated return type of ChangeUserStatus\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"status\",\"description\":\"Your updated status.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserStatus\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotation\",\"description\":\"A single check annotation.\",\"fields\":[{\"name\":\"annotationLevel\",\"description\":\"The annotation's severity level.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"CheckAnnotationLevel\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blobUrl\",\"description\":\"The path to the file that this annotation was made on.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"location\",\"description\":\"The position of this annotation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationSpan\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"The annotation's message.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"The path that this annotation was made on.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"rawDetails\",\"description\":\"Additional information about the annotation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"The annotation's title\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationConnection\",\"description\":\"The connection type for CheckAnnotation.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckAnnotationData\",\"description\":\"Information from a check run analysis to specific lines of code.\",\"fields\":null,\"inputFields\":[{\"name\":\"path\",\"description\":\"The path of the file to add an annotation to.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"location\",\"description\":\"The location of the annotation\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckAnnotationRange\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"annotationLevel\",\"description\":\"Represents an annotation's information level\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CheckAnnotationLevel\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"message\",\"description\":\"A short description of the feedback for these lines of code.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"title\",\"description\":\"The title that represents the annotation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"rawDetails\",\"description\":\"Details about this annotation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"CheckAnnotationLevel\",\"description\":\"Represents an annotation's information level.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"FAILURE\",\"description\":\"An annotation indicating an inescapable error.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NOTICE\",\"description\":\"An annotation indicating some information.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WARNING\",\"description\":\"An annotation indicating an ignorable error.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationPosition\",\"description\":\"A character position in a check annotation.\",\"fields\":[{\"name\":\"column\",\"description\":\"Column number (1 indexed).\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"line\",\"description\":\"Line number (1 indexed).\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckAnnotationRange\",\"description\":\"Information from a check run analysis to specific lines of code.\",\"fields\":null,\"inputFields\":[{\"name\":\"startLine\",\"description\":\"The starting line of the range.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"startColumn\",\"description\":\"The starting column of the range.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"endLine\",\"description\":\"The ending line of the range.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"endColumn\",\"description\":\"The ending column of the range.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationSpan\",\"description\":\"An inclusive pair of positions for a check annotation.\",\"fields\":[{\"name\":\"end\",\"description\":\"End position (inclusive).\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationPosition\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"start\",\"description\":\"Start position (inclusive).\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationPosition\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"CheckConclusionState\",\"description\":\"The possible states for a check suite or run conclusion.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ACTION_REQUIRED\",\"description\":\"The check suite or run requires action.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TIMED_OUT\",\"description\":\"The check suite or run has timed out.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CANCELLED\",\"description\":\"The check suite or run has been cancelled.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FAILURE\",\"description\":\"The check suite or run has failed.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUCCESS\",\"description\":\"The check suite or run has succeeded.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NEUTRAL\",\"description\":\"The check suite or run was neutral.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SKIPPED\",\"description\":\"The check suite or run was skipped.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"STARTUP_FAILURE\",\"description\":\"The check suite or run has failed at startup.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"STALE\",\"description\":\"The check suite or run was marked stale by GitHub. Only GitHub can use this conclusion.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"description\":\"A check run.\",\"fields\":[{\"name\":\"annotations\",\"description\":\"The check run's annotations\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckAnnotationConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"checkSuite\",\"description\":\"The check suite that this run is a part of.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckSuite\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"completedAt\",\"description\":\"Identifies the date and time when the check run was completed.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"conclusion\",\"description\":\"The conclusion of the check run.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"CheckConclusionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"detailsUrl\",\"description\":\"The URL from which to find full details of the check run on the integrator's site.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"externalId\",\"description\":\"A reference for the check run on the integrator's system.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isRequired\",\"description\":\"Whether this is required to pass before merging for a specific pull request.\",\"args\":[{\"name\":\"pullRequestId\",\"description\":\"The id of the pull request this is required for\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pullRequestNumber\",\"description\":\"The number of the pull request this is required for\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the check for this check run.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permalink\",\"description\":\"The permalink to the check run summary.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this check run.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this check run.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"startedAt\",\"description\":\"Identifies the date and time when the check run was started.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"status\",\"description\":\"The current status of the check run.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CheckStatusState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"summary\",\"description\":\"A string representing the check run's summary\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"text\",\"description\":\"A string representing the check run's text\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"A string representing the check run\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this check run.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RequirableByPullRequest\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunAction\",\"description\":\"Possible further actions the integrator can perform.\",\"fields\":null,\"inputFields\":[{\"name\":\"label\",\"description\":\"The text to be displayed on a button in the web UI.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"description\",\"description\":\"A short explanation of what this action would do.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"identifier\",\"description\":\"A reference for the action on the integrator's system. \",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckRunConnection\",\"description\":\"The connection type for CheckRun.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckRunEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckRunEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunFilter\",\"description\":\"The filters that are available when fetching check runs.\",\"fields\":null,\"inputFields\":[{\"name\":\"checkType\",\"description\":\"Filters the check runs by this type.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"CheckRunType\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"appId\",\"description\":\"Filters the check runs created by this application ID.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"checkName\",\"description\":\"Filters the check runs by this name.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"status\",\"description\":\"Filters the check runs by this status.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"CheckStatusState\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunOutput\",\"description\":\"Descriptive details about the check run.\",\"fields\":null,\"inputFields\":[{\"name\":\"title\",\"description\":\"A title to provide for this check run.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"summary\",\"description\":\"The summary of the check run (supports Commonmark).\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"text\",\"description\":\"The details of the check run (supports Commonmark).\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"annotations\",\"description\":\"The annotations that are made as part of the check run.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckAnnotationData\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"images\",\"description\":\"Images attached to the check run output displayed in the GitHub pull request UI.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunOutputImage\",\"ofType\":null}}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunOutputImage\",\"description\":\"Images attached to the check run output displayed in the GitHub pull request UI.\",\"fields\":null,\"inputFields\":[{\"name\":\"alt\",\"description\":\"The alternative text for the image.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"imageUrl\",\"description\":\"The full URL of the image.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"caption\",\"description\":\"A short image description.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"CheckRunType\",\"description\":\"The possible types of check runs.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ALL\",\"description\":\"Every check run available.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LATEST\",\"description\":\"The latest check run.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"CheckStatusState\",\"description\":\"The possible states for a check suite or run status.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"QUEUED\",\"description\":\"The check suite or run has been queued.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"IN_PROGRESS\",\"description\":\"The check suite or run is in progress.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMPLETED\",\"description\":\"The check suite or run has been completed.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WAITING\",\"description\":\"The check suite or run is in waiting state.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REQUESTED\",\"description\":\"The check suite or run has been requested.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckSuite\",\"description\":\"A check suite.\",\"fields\":[{\"name\":\"app\",\"description\":\"The GitHub App which created this check suite.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"App\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"branch\",\"description\":\"The name of the branch for this check suite.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"checkRuns\",\"description\":\"The check runs associated with a check suite.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"filterBy\",\"description\":\"Filters the check runs by this type.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunFilter\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckRunConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"The commit for this check suite\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"conclusion\",\"description\":\"The conclusion of this check suite.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"CheckConclusionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"matchingPullRequests\",\"description\":\"A list of open pull requests matching the check suite.\",\"args\":[{\"name\":\"states\",\"description\":\"A list of states to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"headRefName\",\"description\":\"The head ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"baseRefName\",\"description\":\"The base ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pull requests returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"push\",\"description\":\"The push that triggered this check suite.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Push\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this check suite.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this check suite\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"status\",\"description\":\"The status of this check suite.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CheckStatusState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this check suite\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckSuiteAutoTriggerPreference\",\"description\":\"The auto-trigger preferences that are available for check suites.\",\"fields\":null,\"inputFields\":[{\"name\":\"appId\",\"description\":\"The node ID of the application that owns the check suite.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"setting\",\"description\":\"Set to `true` to enable automatic creation of CheckSuite events upon pushes to the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckSuiteConnection\",\"description\":\"The connection type for CheckSuite.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckSuiteEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CheckSuite\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckSuiteEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckSuite\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckSuiteFilter\",\"description\":\"The filters that are available when fetching check suites.\",\"fields\":null,\"inputFields\":[{\"name\":\"appId\",\"description\":\"Filters the check suites created by this application ID.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"checkName\",\"description\":\"Filters the check suites by this name.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ClearLabelsFromLabelableInput\",\"description\":\"Autogenerated input type of ClearLabelsFromLabelable\",\"fields\":null,\"inputFields\":[{\"name\":\"labelableId\",\"description\":\"The id of the labelable object to clear the labels from.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ClearLabelsFromLabelablePayload\",\"description\":\"Autogenerated return type of ClearLabelsFromLabelable\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"labelable\",\"description\":\"The item that was unlabeled.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Labelable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CloneProjectInput\",\"description\":\"Autogenerated input type of CloneProject\",\"fields\":null,\"inputFields\":[{\"name\":\"targetOwnerId\",\"description\":\"The owner ID to create the project under.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"sourceId\",\"description\":\"The source project to clone.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"includeWorkflows\",\"description\":\"Whether or not to clone the source project's workflows.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the project.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The description of the project.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"public\",\"description\":\"The visibility of the project, defaults to false (private).\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CloneProjectPayload\",\"description\":\"Autogenerated return type of CloneProject\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"jobStatusId\",\"description\":\"The id of the JobStatus for populating cloned fields.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The new cloned project.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CloneTemplateRepositoryInput\",\"description\":\"Autogenerated input type of CloneTemplateRepository\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the template repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the new repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"ownerId\",\"description\":\"The ID of the owner for the new repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"description\",\"description\":\"A short description of the new repository.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"visibility\",\"description\":\"Indicates the repository's visibility level.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryVisibility\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"includeAllBranches\",\"description\":\"Whether to copy all branches from the template to the new repository. Defaults to copying only the default branch of the template.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CloneTemplateRepositoryPayload\",\"description\":\"Autogenerated return type of CloneTemplateRepository\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The new repository.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Closable\",\"description\":\"An object that can be closed\",\"fields\":[{\"name\":\"closed\",\"description\":\"`true` if the object is closed (definition of closed may depend on type)\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closedAt\",\"description\":\"Identifies the date and time when the object was closed.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CloseIssueInput\",\"description\":\"Autogenerated input type of CloseIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"issueId\",\"description\":\"ID of the issue to be closed.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CloseIssuePayload\",\"description\":\"Autogenerated return type of CloseIssue\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The issue that was closed.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ClosePullRequestInput\",\"description\":\"Autogenerated input type of ClosePullRequest\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"ID of the pull request to be closed.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ClosePullRequestPayload\",\"description\":\"Autogenerated return type of ClosePullRequest\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request that was closed.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ClosedEvent\",\"description\":\"Represents a 'closed' event on any `Closable`.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closable\",\"description\":\"Object that was closed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Closable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closer\",\"description\":\"Object which triggered the creation of this event.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"Closer\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this closed event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this closed event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"Closer\",\"description\":\"The object which triggered a `ClosedEvent`.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"CodeOfConduct\",\"description\":\"The Code of Conduct for a repository\",\"fields\":[{\"name\":\"body\",\"description\":\"The body of the Code of Conduct\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"key\",\"description\":\"The key for the Code of Conduct\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The formal name of the Code of Conduct\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this Code of Conduct\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this Code of Conduct\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"CollaboratorAffiliation\",\"description\":\"Collaborators affiliation level with a subject.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OUTSIDE\",\"description\":\"All outside collaborators of an organization-owned subject.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DIRECT\",\"description\":\"All collaborators with permissions to an organization-owned subject, regardless of organization membership status.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ALL\",\"description\":\"All collaborators the authenticated user can see.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"description\":\"Represents a comment.\",\"fields\":[{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the subject of the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"The body as Markdown.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The body rendered to text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null}]},{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"description\":\"A comment author association with repository.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"MEMBER\",\"description\":\"Author is a member of the organization that owns the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OWNER\",\"description\":\"Author is the owner of the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MANNEQUIN\",\"description\":\"Author is a placeholder for an unclaimed user.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COLLABORATOR\",\"description\":\"Author has been invited to collaborate on the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONTRIBUTOR\",\"description\":\"Author has previously committed to the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FIRST_TIME_CONTRIBUTOR\",\"description\":\"Author has not previously committed to the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FIRST_TIMER\",\"description\":\"Author has not previously committed to GitHub.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NONE\",\"description\":\"Author has no association with the repository.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"description\":\"The possible errors that will prevent a user from updating a comment.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ARCHIVED\",\"description\":\"Unable to create comment because repository is archived.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INSUFFICIENT_ACCESS\",\"description\":\"You must be the author or have write access to this repository to update this comment.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LOCKED\",\"description\":\"Unable to create comment because issue is locked.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LOGIN_REQUIRED\",\"description\":\"You must be logged in to update this comment.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MAINTENANCE\",\"description\":\"Repository is under maintenance.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"VERIFIED_EMAIL_REQUIRED\",\"description\":\"At least one email address must be verified to update this comment.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DENIED\",\"description\":\"You cannot update this comment\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommentDeletedEvent\",\"description\":\"Represents a 'comment_deleted' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletedCommentAuthor\",\"description\":\"The user who authored the deleted comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"description\":\"Represents a Git commit.\",\"fields\":[{\"name\":\"abbreviatedOid\",\"description\":\"An abbreviated version of the Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"additions\",\"description\":\"The number of additions in this commit.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"associatedPullRequests\",\"description\":\"The pull requests associated with a commit\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pull requests.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"PullRequestOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: ASC}\"}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"author\",\"description\":\"Authorship details of the commit.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"GitActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authoredByCommitter\",\"description\":\"Check if the committer and the author match.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authoredDate\",\"description\":\"The datetime when this commit was authored.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authors\",\"description\":\"The list of authors for this commit based on the git author and the Co-authored-by\\nmessage trailer. The git author will always be first.\\n\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GitActorConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blame\",\"description\":\"Fetches `git blame` information.\",\"args\":[{\"name\":\"path\",\"description\":\"The file whose Git blame information you want.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Blame\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"changedFiles\",\"description\":\"The number of changed files in this commit.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"checkSuites\",\"description\":\"The check suites associated with a commit.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"filterBy\",\"description\":\"Filters the check suites by this type.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckSuiteFilter\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckSuiteConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"comments\",\"description\":\"Comments made on the commit.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitResourcePath\",\"description\":\"The HTTP path for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitUrl\",\"description\":\"The HTTP URL for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"committedDate\",\"description\":\"The datetime when this commit was committed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"committedViaWeb\",\"description\":\"Check if committed via GitHub web UI.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"committer\",\"description\":\"Committer details of the commit.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"GitActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletions\",\"description\":\"The number of deletions in this commit.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deployments\",\"description\":\"The deployments associated with a commit.\",\"args\":[{\"name\":\"environments\",\"description\":\"Environments to list deployments for\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for deployments returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeploymentOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"file\",\"description\":\"The tree entry representing the file located at the given path.\",\"args\":[{\"name\":\"path\",\"description\":\"The path for the file\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TreeEntry\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"history\",\"description\":\"The linear commit history starting from (and including) this commit, in the same order as `git log`.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"path\",\"description\":\"If non-null, filters history to only show commits touching files under this path.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"author\",\"description\":\"If non-null, filters history to only show commits with matching authorship.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CommitAuthor\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"since\",\"description\":\"Allows specifying a beginning time or date for fetching commits.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitTimestamp\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"until\",\"description\":\"Allows specifying an ending time or date for fetching commits.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitTimestamp\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitHistoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"The Git commit message\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"messageBody\",\"description\":\"The Git commit message body\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"messageBodyHTML\",\"description\":\"The commit message body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"messageHeadline\",\"description\":\"The Git commit message headline\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"messageHeadlineHTML\",\"description\":\"The commit message headline rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oid\",\"description\":\"The Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"onBehalfOf\",\"description\":\"The organization this commit was made on behalf of.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parents\",\"description\":\"The parents of a commit.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pushedDate\",\"description\":\"The datetime when this commit was pushed.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The Repository this commit belongs to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this commit\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signature\",\"description\":\"Commit signing information, if present.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"GitSignature\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"status\",\"description\":\"Status information for this commit\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Status\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"statusCheckRollup\",\"description\":\"Check and Status rollup information for this commit.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"StatusCheckRollup\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"submodules\",\"description\":\"Returns a list of all submodules in this repository as of this Commit parsed from the .gitmodules file.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SubmoduleConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tarballUrl\",\"description\":\"Returns a URL to download a tarball archive for a repository.\\nNote: For private repositories, these links are temporary and expire after five minutes.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tree\",\"description\":\"Commit's root Tree\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Tree\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"treeResourcePath\",\"description\":\"The HTTP path for the tree of this commit\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"treeUrl\",\"description\":\"The HTTP URL for the tree of this commit\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this commit\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSubscribe\",\"description\":\"Check if the viewer is able to change their subscription status for the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerSubscription\",\"description\":\"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"zipballUrl\",\"description\":\"Returns a URL to download a zipball archive for a repository.\\nNote: For private repositories, these links are temporary and expire after five minutes.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CommitAuthor\",\"description\":\"Specifies an author for filtering Git commits.\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"ID of a User to filter by. If non-null, only commits authored by this user will be returned. This field takes precedence over emails.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"emails\",\"description\":\"Email addresses to filter by. Commits authored by any of the specified email addresses will be returned.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"description\":\"Represents a comment on a given Commit.\",\"fields\":[{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the subject of the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"Identifies the comment body.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The body rendered to text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"Identifies the commit associated with the comment, if the commit exists.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isMinimized\",\"description\":\"Returns whether or not a comment has been minimized.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"minimizedReason\",\"description\":\"Returns why the comment was minimized.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"Identifies the file path associated with the comment.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"position\",\"description\":\"Identifies the line position associated with the comment.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path permalink for this commit comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL permalink for this commit comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDelete\",\"description\":\"Check if the current viewer can delete this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanMinimize\",\"description\":\"Check if the current viewer can minimize this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Deletable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Minimizable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitCommentConnection\",\"description\":\"The connection type for CommitComment.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitCommentEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitCommentEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitCommentThread\",\"description\":\"A thread of comments on a commit.\",\"fields\":[{\"name\":\"comments\",\"description\":\"The comments that exist in this thread.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"The commit the comments were made on.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"The file the comments were made on.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"position\",\"description\":\"The position in the diff for the commit that the comment was made on.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitConnection\",\"description\":\"The connection type for Commit.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CommitContributionOrder\",\"description\":\"Ordering options for commit contribution connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field by which to order commit contributions.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommitContributionOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"CommitContributionOrderField\",\"description\":\"Properties by which commit contribution connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OCCURRED_AT\",\"description\":\"Order commit contributions by when they were made.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMMIT_COUNT\",\"description\":\"Order commit contributions by how many commits they represent.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitContributionsByRepository\",\"description\":\"This aggregates commits made by a user within one repository.\",\"fields\":[{\"name\":\"contributions\",\"description\":\"The commit contributions, each representing a day.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for commit contributions returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CommitContributionOrder\",\"ofType\":null},\"defaultValue\":\"{field: OCCURRED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedCommitContributionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository in which the commits were made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for the user's commits to the repository in this time range.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for the user's commits to the repository in this time range.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitHistoryConnection\",\"description\":\"The connection type for Commit.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ConnectedEvent\",\"description\":\"Represents a 'connected' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCrossRepository\",\"description\":\"Reference originated in a different repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"source\",\"description\":\"Issue or pull request that made the reference.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"ReferencedSubject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"Issue or pull request which was connected.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"ReferencedSubject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Contribution\",\"description\":\"Represents a contribution a user made on GitHub, such as opening an issue.\",\"fields\":[{\"name\":\"isRestricted\",\"description\":\"Whether this contribution is associated with a record you do not have access to. For\\nexample, your own 'first issue' contribution may have been made on a repository you can no\\nlonger access.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"occurredAt\",\"description\":\"When this contribution was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made this contribution.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CreatedCommitContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"JoinedGitHubContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RestrictedContribution\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"ContributionCalendar\",\"description\":\"A calendar of contributions made on GitHub by a user.\",\"fields\":[{\"name\":\"colors\",\"description\":\"A list of hex color codes used in this calendar. The darker the color, the more contributions it represents.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isHalloween\",\"description\":\"Determine if the color set was chosen because it's currently Halloween.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"months\",\"description\":\"A list of the months of contributions in this calendar.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ContributionCalendarMonth\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalContributions\",\"description\":\"The count of total contributions in the calendar.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"weeks\",\"description\":\"A list of the weeks of contributions in this calendar.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ContributionCalendarWeek\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ContributionCalendarDay\",\"description\":\"Represents a single day of contributions on GitHub by a user.\",\"fields\":[{\"name\":\"color\",\"description\":\"The hex color code that represents how many contributions were made on this day compared to others in the calendar.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"contributionCount\",\"description\":\"How many contributions were made by the user on this day.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"contributionLevel\",\"description\":\"Indication of contributions, relative to other days. Can be used to indicate which color to represent this day on a calendar.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ContributionLevel\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"date\",\"description\":\"The day this square represents.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Date\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"weekday\",\"description\":\"A number representing which day of the week this square represents, e.g., 1 is Monday.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ContributionCalendarMonth\",\"description\":\"A month of contributions in a user's contribution graph.\",\"fields\":[{\"name\":\"firstDay\",\"description\":\"The date of the first day of this month.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Date\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the month.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalWeeks\",\"description\":\"How many weeks started in this month.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"year\",\"description\":\"The year the month occurred in.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ContributionCalendarWeek\",\"description\":\"A week of contributions in a user's contribution graph.\",\"fields\":[{\"name\":\"contributionDays\",\"description\":\"The days of contributions in this week.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ContributionCalendarDay\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"firstDay\",\"description\":\"The date of the earliest square in this week.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Date\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ContributionLevel\",\"description\":\"Varying levels of contributions from none to many.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NONE\",\"description\":\"No contributions occurred.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FIRST_QUARTILE\",\"description\":\"Lowest 25% of days of contributions.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SECOND_QUARTILE\",\"description\":\"Second lowest 25% of days of contributions. More contributions than the first quartile.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"THIRD_QUARTILE\",\"description\":\"Second highest 25% of days of contributions. More contributions than second quartile, less than the fourth quartile.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FOURTH_QUARTILE\",\"description\":\"Highest 25% of days of contributions. More contributions than the third quartile.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ContributionOrder\",\"description\":\"Ordering options for contribution connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ContributionsCollection\",\"description\":\"A contributions collection aggregates contributions such as opened issues and commits created by a user.\",\"fields\":[{\"name\":\"commitContributionsByRepository\",\"description\":\"Commit contributions made by the user, grouped by repository.\",\"args\":[{\"name\":\"maxRepositories\",\"description\":\"How many repositories should be included.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"25\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitContributionsByRepository\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"contributionCalendar\",\"description\":\"A calendar of this user's contributions on GitHub.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ContributionCalendar\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"contributionYears\",\"description\":\"The years the user has been making contributions with the most recent year first.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"doesEndInCurrentMonth\",\"description\":\"Determine if this collection's time span ends in the current month.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"earliestRestrictedContributionDate\",\"description\":\"The date of the first restricted contribution the user made in this time period. Can only be non-null when the user has enabled private contribution counts.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Date\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"endedAt\",\"description\":\"The ending date and time of this collection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"firstIssueContribution\",\"description\":\"The first issue the user opened on GitHub. This will be null if that issue was opened outside the collection's time range and ignoreTimeRange is false. If the issue is not visible but the user has opted to show private contributions, a RestrictedContribution will be returned.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"CreatedIssueOrRestrictedContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"firstPullRequestContribution\",\"description\":\"The first pull request the user opened on GitHub. This will be null if that pull request was opened outside the collection's time range and ignoreTimeRange is not true. If the pull request is not visible but the user has opted to show private contributions, a RestrictedContribution will be returned.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"CreatedPullRequestOrRestrictedContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"firstRepositoryContribution\",\"description\":\"The first repository the user created on GitHub. This will be null if that first repository was created outside the collection's time range and ignoreTimeRange is false. If the repository is not visible, then a RestrictedContribution is returned.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"CreatedRepositoryOrRestrictedContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasActivityInThePast\",\"description\":\"Does the user have any more activity in the timeline that occurred prior to the collection's time range?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasAnyContributions\",\"description\":\"Determine if there are any contributions in this collection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasAnyRestrictedContributions\",\"description\":\"Determine if the user made any contributions in this time frame whose details are not visible because they were made in a private repository. Can only be true if the user enabled private contribution counts.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSingleDay\",\"description\":\"Whether or not the collector's time span is all within the same day.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issueContributions\",\"description\":\"A list of issues the user opened.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"excludeFirst\",\"description\":\"Should the user's first issue ever be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"excludePopular\",\"description\":\"Should the user's most commented issue be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for contributions returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ContributionOrder\",\"ofType\":null},\"defaultValue\":\"{direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContributionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issueContributionsByRepository\",\"description\":\"Issue contributions made by the user, grouped by repository.\",\"args\":[{\"name\":\"maxRepositories\",\"description\":\"How many repositories should be included.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"25\"},{\"name\":\"excludeFirst\",\"description\":\"Should the user's first issue ever be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"excludePopular\",\"description\":\"Should the user's most commented issue be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueContributionsByRepository\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"joinedGitHubContribution\",\"description\":\"When the user signed up for GitHub. This will be null if that sign up date falls outside the collection's time range and ignoreTimeRange is false.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"JoinedGitHubContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"latestRestrictedContributionDate\",\"description\":\"The date of the most recent restricted contribution the user made in this time period. Can only be non-null when the user has enabled private contribution counts.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Date\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mostRecentCollectionWithActivity\",\"description\":\"When this collection's time range does not include any activity from the user, use this\\nto get a different collection from an earlier time range that does have activity.\\n\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ContributionsCollection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mostRecentCollectionWithoutActivity\",\"description\":\"Returns a different contributions collection from an earlier time range than this one\\nthat does not have any contributions.\\n\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ContributionsCollection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"popularIssueContribution\",\"description\":\"The issue the user opened on GitHub that received the most comments in the specified\\ntime frame.\\n\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"popularPullRequestContribution\",\"description\":\"The pull request the user opened on GitHub that received the most comments in the\\nspecified time frame.\\n\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestContributions\",\"description\":\"Pull request contributions made by the user.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"excludeFirst\",\"description\":\"Should the user's first pull request ever be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"excludePopular\",\"description\":\"Should the user's most commented pull request be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for contributions returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ContributionOrder\",\"ofType\":null},\"defaultValue\":\"{direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContributionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestContributionsByRepository\",\"description\":\"Pull request contributions made by the user, grouped by repository.\",\"args\":[{\"name\":\"maxRepositories\",\"description\":\"How many repositories should be included.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"25\"},{\"name\":\"excludeFirst\",\"description\":\"Should the user's first pull request ever be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"excludePopular\",\"description\":\"Should the user's most commented pull request be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestContributionsByRepository\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReviewContributions\",\"description\":\"Pull request review contributions made by the user.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for contributions returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ContributionOrder\",\"ofType\":null},\"defaultValue\":\"{direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContributionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReviewContributionsByRepository\",\"description\":\"Pull request review contributions made by the user, grouped by repository.\",\"args\":[{\"name\":\"maxRepositories\",\"description\":\"How many repositories should be included.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"25\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewContributionsByRepository\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryContributions\",\"description\":\"A list of repositories owned by the user that the user created in this time range.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"excludeFirst\",\"description\":\"Should the user's first repository ever be excluded from the result.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for contributions returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ContributionOrder\",\"ofType\":null},\"defaultValue\":\"{direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContributionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restrictedContributionsCount\",\"description\":\"A count of contributions made by the user that the viewer cannot access. Only non-zero when the user has chosen to share their private contribution counts.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"startedAt\",\"description\":\"The beginning date and time of this collection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCommitContributions\",\"description\":\"How many commits were made by the user in this time span.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalIssueContributions\",\"description\":\"How many issues the user opened.\",\"args\":[{\"name\":\"excludeFirst\",\"description\":\"Should the user's first issue ever be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"excludePopular\",\"description\":\"Should the user's most commented issue be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalPullRequestContributions\",\"description\":\"How many pull requests the user opened.\",\"args\":[{\"name\":\"excludeFirst\",\"description\":\"Should the user's first pull request ever be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"excludePopular\",\"description\":\"Should the user's most commented pull request be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalPullRequestReviewContributions\",\"description\":\"How many pull request reviews the user left.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalRepositoriesWithContributedCommits\",\"description\":\"How many different repositories the user committed to.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalRepositoriesWithContributedIssues\",\"description\":\"How many different repositories the user opened issues in.\",\"args\":[{\"name\":\"excludeFirst\",\"description\":\"Should the user's first issue ever be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"excludePopular\",\"description\":\"Should the user's most commented issue be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalRepositoriesWithContributedPullRequestReviews\",\"description\":\"How many different repositories the user left pull request reviews in.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalRepositoriesWithContributedPullRequests\",\"description\":\"How many different repositories the user opened pull requests in.\",\"args\":[{\"name\":\"excludeFirst\",\"description\":\"Should the user's first pull request ever be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"excludePopular\",\"description\":\"Should the user's most commented pull request be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalRepositoryContributions\",\"description\":\"How many repositories the user created.\",\"args\":[{\"name\":\"excludeFirst\",\"description\":\"Should the user's first repository ever be excluded from this count.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made the contributions in this collection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ConvertProjectCardNoteToIssueInput\",\"description\":\"Autogenerated input type of ConvertProjectCardNoteToIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"projectCardId\",\"description\":\"The ProjectCard ID to convert.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"repositoryId\",\"description\":\"The ID of the repository to create the issue in.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"title\",\"description\":\"The title of the newly created issue. Defaults to the card's note text.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The body of the newly created issue.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertProjectCardNoteToIssuePayload\",\"description\":\"Autogenerated return type of ConvertProjectCardNoteToIssue\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectCard\",\"description\":\"The updated ProjectCard.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCard\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertToDraftEvent\",\"description\":\"Represents a 'convert_to_draft' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this convert to draft event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this convert to draft event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertedNoteToIssueEvent\",\"description\":\"Represents a 'converted_note_to_issue' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateBranchProtectionRuleInput\",\"description\":\"Autogenerated input type of CreateBranchProtectionRule\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The global relay id of the repository in which a new branch protection rule should be created in.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"pattern\",\"description\":\"The glob-like pattern used to determine matching branches.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"requiresApprovingReviews\",\"description\":\"Are approving reviews required to update matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiredApprovingReviewCount\",\"description\":\"Number of approving reviews required to update matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresCommitSignatures\",\"description\":\"Are commits required to be signed.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresLinearHistory\",\"description\":\"Are merge commits prohibited from being pushed to this branch.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"allowsForcePushes\",\"description\":\"Are force pushes allowed on this branch.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"allowsDeletions\",\"description\":\"Can this branch be deleted.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isAdminEnforced\",\"description\":\"Can admins overwrite branch protection.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresStatusChecks\",\"description\":\"Are status checks required to update matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresStrictStatusChecks\",\"description\":\"Are branches required to be up to date before merging.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresCodeOwnerReviews\",\"description\":\"Are reviews from code owners required to update matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"dismissesStaleReviews\",\"description\":\"Will new commits pushed to matching branches dismiss pull request review approvals.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"restrictsReviewDismissals\",\"description\":\"Is dismissal of pull request reviews restricted.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"reviewDismissalActorIds\",\"description\":\"A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"restrictsPushes\",\"description\":\"Is pushing to matching branches restricted.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pushActorIds\",\"description\":\"A list of User, Team or App IDs allowed to push to matching branches.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"requiredStatusCheckContexts\",\"description\":\"List of required status check contexts that must pass for commits to be accepted to matching branches.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateBranchProtectionRulePayload\",\"description\":\"Autogenerated return type of CreateBranchProtectionRule\",\"fields\":[{\"name\":\"branchProtectionRule\",\"description\":\"The newly created BranchProtectionRule.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateCheckRunInput\",\"description\":\"Autogenerated input type of CreateCheckRun\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the check.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"headSha\",\"description\":\"The SHA of the head commit.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"detailsUrl\",\"description\":\"The URL of the integrator's site that has the full details of the check.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"externalId\",\"description\":\"A reference for the run on the integrator's system.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"status\",\"description\":\"The current status.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RequestableCheckStatusState\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"startedAt\",\"description\":\"The time that the check run began.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"conclusion\",\"description\":\"The final conclusion of the check.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"CheckConclusionState\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"completedAt\",\"description\":\"The time that the check run finished.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"output\",\"description\":\"Descriptive details about the run.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunOutput\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"actions\",\"description\":\"Possible further actions the integrator can perform, which a user may trigger.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunAction\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateCheckRunPayload\",\"description\":\"Autogenerated return type of CreateCheckRun\",\"fields\":[{\"name\":\"checkRun\",\"description\":\"The newly created check run.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateCheckSuiteInput\",\"description\":\"Autogenerated input type of CreateCheckSuite\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"headSha\",\"description\":\"The SHA of the head commit.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateCheckSuitePayload\",\"description\":\"Autogenerated return type of CreateCheckSuite\",\"fields\":[{\"name\":\"checkSuite\",\"description\":\"The newly created check suite.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckSuite\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateEnterpriseOrganizationInput\",\"description\":\"Autogenerated input type of CreateEnterpriseOrganization\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise owning the new organization.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"login\",\"description\":\"The login of the new organization.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"profileName\",\"description\":\"The profile name of the new organization.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"billingEmail\",\"description\":\"The email used for sending billing receipts.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"adminLogins\",\"description\":\"The logins for the administrators of the new organization.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateEnterpriseOrganizationPayload\",\"description\":\"Autogenerated return type of CreateEnterpriseOrganization\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise that owns the created organization.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The organization that was created.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateIpAllowListEntryInput\",\"description\":\"Autogenerated input type of CreateIpAllowListEntry\",\"fields\":null,\"inputFields\":[{\"name\":\"ownerId\",\"description\":\"The ID of the owner for which to create the new IP allow list entry.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"allowListValue\",\"description\":\"An IP address or range of addresses in CIDR notation.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"An optional name for the IP allow list entry.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isActive\",\"description\":\"Whether the IP allow list entry is active when an IP allow list is enabled.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateIpAllowListEntryPayload\",\"description\":\"Autogenerated return type of CreateIpAllowListEntry\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ipAllowListEntry\",\"description\":\"The IP allow list entry that was created.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntry\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateIssueInput\",\"description\":\"Autogenerated input type of CreateIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"title\",\"description\":\"The title for the issue.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The body for the issue description.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"assigneeIds\",\"description\":\"The Node ID for the user assignee for this issue.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"milestoneId\",\"description\":\"The Node ID of the milestone for this issue.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"labelIds\",\"description\":\"An array of Node IDs of labels for this issue.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"projectIds\",\"description\":\"An array of Node IDs for projects associated with this issue.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"issueTemplate\",\"description\":\"The name of an issue template in the repository, assigns labels and assignees from the template to the issue\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateIssuePayload\",\"description\":\"Autogenerated return type of CreateIssue\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The new issue.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateProjectInput\",\"description\":\"Autogenerated input type of CreateProject\",\"fields\":null,\"inputFields\":[{\"name\":\"ownerId\",\"description\":\"The owner ID to create the project under.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of project.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The description of project.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"template\",\"description\":\"The name of the GitHub-provided template.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ProjectTemplate\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"repositoryIds\",\"description\":\"A list of repository IDs to create as linked repositories for the project\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateProjectPayload\",\"description\":\"Autogenerated return type of CreateProject\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The new project.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreatePullRequestInput\",\"description\":\"Autogenerated input type of CreatePullRequest\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"baseRefName\",\"description\":\"The name of the branch you want your changes pulled into. This should be an existing branch\\non the current repository. You cannot update the base branch on a pull request to point\\nto another repository.\\n\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"headRefName\",\"description\":\"The name of the branch where your changes are implemented. For cross-repository pull requests\\nin the same network, namespace `head_ref_name` with a user like this: `username:branch`.\\n\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"title\",\"description\":\"The title of the pull request.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The contents of the pull request.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"maintainerCanModify\",\"description\":\"Indicates whether maintainers can modify the pull request.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"true\"},{\"name\":\"draft\",\"description\":\"Indicates whether this pull request should be a draft.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatePullRequestPayload\",\"description\":\"Autogenerated return type of CreatePullRequest\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The new pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateRefInput\",\"description\":\"Autogenerated input type of CreateRef\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the Repository to create the Ref in.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The fully qualified name of the new Ref (ie: `refs/heads/my_new_branch`).\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"oid\",\"description\":\"The GitObjectID that the new Ref shall target. Must point to a commit.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateRefPayload\",\"description\":\"Autogenerated return type of CreateRef\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ref\",\"description\":\"The newly created ref.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateRepositoryInput\",\"description\":\"Autogenerated input type of CreateRepository\",\"fields\":null,\"inputFields\":[{\"name\":\"name\",\"description\":\"The name of the new repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"ownerId\",\"description\":\"The ID of the owner for the new repository.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"description\",\"description\":\"A short description of the new repository.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"visibility\",\"description\":\"Indicates the repository's visibility level.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryVisibility\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"template\",\"description\":\"Whether this repository should be marked as a template such that anyone who can access it can create new repositories with the same files and directory structure.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"homepageUrl\",\"description\":\"The URL for a web page about this repository.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"hasWikiEnabled\",\"description\":\"Indicates if the repository should have the wiki feature enabled.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"hasIssuesEnabled\",\"description\":\"Indicates if the repository should have the issues feature enabled.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"true\"},{\"name\":\"teamId\",\"description\":\"When an organization is specified as the owner, this ID identifies the team that should be granted access to the new repository.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateRepositoryPayload\",\"description\":\"Autogenerated return type of CreateRepository\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The new repository.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateTeamDiscussionCommentInput\",\"description\":\"Autogenerated input type of CreateTeamDiscussionComment\",\"fields\":null,\"inputFields\":[{\"name\":\"discussionId\",\"description\":\"The ID of the discussion to which the comment belongs.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The content of the comment.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateTeamDiscussionCommentPayload\",\"description\":\"Autogenerated return type of CreateTeamDiscussionComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamDiscussionComment\",\"description\":\"The new comment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateTeamDiscussionInput\",\"description\":\"Autogenerated input type of CreateTeamDiscussion\",\"fields\":null,\"inputFields\":[{\"name\":\"teamId\",\"description\":\"The ID of the team to which the discussion belongs.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"title\",\"description\":\"The title of the discussion.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The content of the discussion.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"private\",\"description\":\"If true, restricts the visibility of this discussion to team members and organization admins. If false or not specified, allows any organization member to view this discussion.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreateTeamDiscussionPayload\",\"description\":\"Autogenerated return type of CreateTeamDiscussion\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamDiscussion\",\"description\":\"The new discussion.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedCommitContribution\",\"description\":\"Represents the contribution a user made by committing to a repository.\",\"fields\":[{\"name\":\"commitCount\",\"description\":\"How many commits were made on this day to this repository by the user.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isRestricted\",\"description\":\"Whether this contribution is associated with a record you do not have access to. For\\nexample, your own 'first issue' contribution may have been made on a repository you can no\\nlonger access.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"occurredAt\",\"description\":\"When this contribution was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository the user made a commit in.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made this contribution.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Contribution\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedCommitContributionConnection\",\"description\":\"The connection type for CreatedCommitContribution.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedCommitContributionEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedCommitContribution\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of commits across days and repositories in the connection.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedCommitContributionEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreatedCommitContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContribution\",\"description\":\"Represents the contribution a user made on GitHub by opening an issue.\",\"fields\":[{\"name\":\"isRestricted\",\"description\":\"Whether this contribution is associated with a record you do not have access to. For\\nexample, your own 'first issue' contribution may have been made on a repository you can no\\nlonger access.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The issue that was opened.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"occurredAt\",\"description\":\"When this contribution was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made this contribution.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Contribution\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContributionConnection\",\"description\":\"The connection type for CreatedIssueContribution.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContributionEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContribution\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContributionEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"CreatedIssueOrRestrictedContribution\",\"description\":\"Represents either a issue the viewer can access or a restricted contribution.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RestrictedContribution\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContribution\",\"description\":\"Represents the contribution a user made on GitHub by opening a pull request.\",\"fields\":[{\"name\":\"isRestricted\",\"description\":\"Whether this contribution is associated with a record you do not have access to. For\\nexample, your own 'first issue' contribution may have been made on a repository you can no\\nlonger access.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"occurredAt\",\"description\":\"When this contribution was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request that was opened.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made this contribution.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Contribution\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContributionConnection\",\"description\":\"The connection type for CreatedPullRequestContribution.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContributionEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContribution\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContributionEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"CreatedPullRequestOrRestrictedContribution\",\"description\":\"Represents either a pull request the viewer can access or a restricted contribution.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RestrictedContribution\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContribution\",\"description\":\"Represents the contribution a user made by leaving a review on a pull request.\",\"fields\":[{\"name\":\"isRestricted\",\"description\":\"Whether this contribution is associated with a record you do not have access to. For\\nexample, your own 'first issue' contribution may have been made on a repository you can no\\nlonger access.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"occurredAt\",\"description\":\"When this contribution was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request the user reviewed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReview\",\"description\":\"The review the user left on the pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository containing the pull request that the user reviewed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made this contribution.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Contribution\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContributionConnection\",\"description\":\"The connection type for CreatedPullRequestReviewContribution.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContributionEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContribution\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContributionEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContribution\",\"description\":\"Represents the contribution a user made on GitHub by creating a repository.\",\"fields\":[{\"name\":\"isRestricted\",\"description\":\"Whether this contribution is associated with a record you do not have access to. For\\nexample, your own 'first issue' contribution may have been made on a repository you can no\\nlonger access.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"occurredAt\",\"description\":\"When this contribution was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository that was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made this contribution.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Contribution\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContributionConnection\",\"description\":\"The connection type for CreatedRepositoryContribution.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContributionEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContribution\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContributionEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContribution\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"CreatedRepositoryOrRestrictedContribution\",\"description\":\"Represents either a repository the viewer can access or a restricted contribution.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CreatedRepositoryContribution\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RestrictedContribution\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"CrossReferencedEvent\",\"description\":\"Represents a mention made by one issue or pull request to another.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCrossRepository\",\"description\":\"Reference originated in a different repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"referencedAt\",\"description\":\"Identifies when the reference was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"source\",\"description\":\"Issue or pull request that made the reference.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"ReferencedSubject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"target\",\"description\":\"Issue or pull request to which the reference was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"ReferencedSubject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"willCloseTarget\",\"description\":\"Checks if the target will be closed when the source is merged.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"Date\",\"description\":\"An ISO-8601 encoded date string.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"description\":\"An ISO-8601 encoded UTC date string.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeclineTopicSuggestionInput\",\"description\":\"Autogenerated input type of DeclineTopicSuggestion\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the suggested topic.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"reason\",\"description\":\"The reason why the suggested topic is declined.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"TopicSuggestionDeclineReason\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeclineTopicSuggestionPayload\",\"description\":\"Autogenerated return type of DeclineTopicSuggestion\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topic\",\"description\":\"The declined topic.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"DefaultRepositoryPermissionField\",\"description\":\"The possible default permissions for repositories.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NONE\",\"description\":\"No access\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"READ\",\"description\":\"Can read repos by default\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WRITE\",\"description\":\"Can read and write repos by default\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADMIN\",\"description\":\"Can read, write, and administrate repos by default\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Deletable\",\"description\":\"Entities that can be deleted.\",\"fields\":[{\"name\":\"viewerCanDelete\",\"description\":\"Check if the current viewer can delete this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteBranchProtectionRuleInput\",\"description\":\"Autogenerated input type of DeleteBranchProtectionRule\",\"fields\":null,\"inputFields\":[{\"name\":\"branchProtectionRuleId\",\"description\":\"The global relay id of the branch protection rule to be deleted.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteBranchProtectionRulePayload\",\"description\":\"Autogenerated return type of DeleteBranchProtectionRule\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteDeploymentInput\",\"description\":\"Autogenerated input type of DeleteDeployment\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The Node ID of the deployment to be deleted.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteDeploymentPayload\",\"description\":\"Autogenerated return type of DeleteDeployment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteIpAllowListEntryInput\",\"description\":\"Autogenerated input type of DeleteIpAllowListEntry\",\"fields\":null,\"inputFields\":[{\"name\":\"ipAllowListEntryId\",\"description\":\"The ID of the IP allow list entry to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteIpAllowListEntryPayload\",\"description\":\"Autogenerated return type of DeleteIpAllowListEntry\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ipAllowListEntry\",\"description\":\"The IP allow list entry that was deleted.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntry\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteIssueCommentInput\",\"description\":\"Autogenerated input type of DeleteIssueComment\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the comment to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteIssueCommentPayload\",\"description\":\"Autogenerated return type of DeleteIssueComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteIssueInput\",\"description\":\"Autogenerated input type of DeleteIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"issueId\",\"description\":\"The ID of the issue to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteIssuePayload\",\"description\":\"Autogenerated return type of DeleteIssue\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository the issue belonged to\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteProjectCardInput\",\"description\":\"Autogenerated input type of DeleteProjectCard\",\"fields\":null,\"inputFields\":[{\"name\":\"cardId\",\"description\":\"The id of the card to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteProjectCardPayload\",\"description\":\"Autogenerated return type of DeleteProjectCard\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"column\",\"description\":\"The column the deleted card was in.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumn\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletedCardId\",\"description\":\"The deleted card ID.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteProjectColumnInput\",\"description\":\"Autogenerated input type of DeleteProjectColumn\",\"fields\":null,\"inputFields\":[{\"name\":\"columnId\",\"description\":\"The id of the column to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteProjectColumnPayload\",\"description\":\"Autogenerated return type of DeleteProjectColumn\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletedColumnId\",\"description\":\"The deleted column ID.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The project the deleted column was in.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteProjectInput\",\"description\":\"Autogenerated input type of DeleteProject\",\"fields\":null,\"inputFields\":[{\"name\":\"projectId\",\"description\":\"The Project ID to update.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteProjectPayload\",\"description\":\"Autogenerated return type of DeleteProject\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The repository or organization the project was removed from.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"ProjectOwner\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeletePullRequestReviewCommentInput\",\"description\":\"Autogenerated input type of DeletePullRequestReviewComment\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the comment to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeletePullRequestReviewCommentPayload\",\"description\":\"Autogenerated return type of DeletePullRequestReviewComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReview\",\"description\":\"The pull request review the deleted comment belonged to.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeletePullRequestReviewInput\",\"description\":\"Autogenerated input type of DeletePullRequestReview\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestReviewId\",\"description\":\"The Node ID of the pull request review to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeletePullRequestReviewPayload\",\"description\":\"Autogenerated return type of DeletePullRequestReview\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReview\",\"description\":\"The deleted pull request review.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteRefInput\",\"description\":\"Autogenerated input type of DeleteRef\",\"fields\":null,\"inputFields\":[{\"name\":\"refId\",\"description\":\"The Node ID of the Ref to be deleted.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteRefPayload\",\"description\":\"Autogenerated return type of DeleteRef\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteTeamDiscussionCommentInput\",\"description\":\"Autogenerated input type of DeleteTeamDiscussionComment\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the comment to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteTeamDiscussionCommentPayload\",\"description\":\"Autogenerated return type of DeleteTeamDiscussionComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteTeamDiscussionInput\",\"description\":\"Autogenerated input type of DeleteTeamDiscussion\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The discussion ID to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteTeamDiscussionPayload\",\"description\":\"Autogenerated return type of DeleteTeamDiscussion\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteVerifiableDomainInput\",\"description\":\"Autogenerated input type of DeleteVerifiableDomain\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the verifiable domain to delete.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeleteVerifiableDomainPayload\",\"description\":\"Autogenerated return type of DeleteVerifiableDomain\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The owning account from which the domain was deleted.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"VerifiableDomainOwner\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DemilestonedEvent\",\"description\":\"Represents a 'demilestoned' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"milestoneTitle\",\"description\":\"Identifies the milestone title associated with the 'demilestoned' event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"Object referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"MilestoneItem\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeployKey\",\"description\":\"A repository deploy key.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"key\",\"description\":\"The deploy key.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"readOnly\",\"description\":\"Whether or not the deploy key is read only.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"The deploy key title.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"verified\",\"description\":\"Whether or not the deploy key has been verified.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeployKeyConnection\",\"description\":\"The connection type for DeployKey.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"DeployKeyEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"DeployKey\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeployKeyEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeployKey\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeployedEvent\",\"description\":\"Represents a 'deployed' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deployment\",\"description\":\"The deployment associated with the 'deployed' event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Deployment\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ref\",\"description\":\"The ref associated with the 'deployed' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Deployment\",\"description\":\"Represents triggered deployment instance.\",\"fields\":[{\"name\":\"commit\",\"description\":\"Identifies the commit sha of the deployment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitOid\",\"description\":\"Identifies the oid of the deployment commit, even if the commit has been deleted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"creator\",\"description\":\"Identifies the actor who triggered the deployment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The deployment description.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"environment\",\"description\":\"The latest environment to which this deployment was made.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"latestEnvironment\",\"description\":\"The latest environment to which this deployment was made.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"latestStatus\",\"description\":\"The latest status of this deployment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatus\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"originalEnvironment\",\"description\":\"The original environment to which this deployment was made.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"payload\",\"description\":\"Extra information that a deployment system might need.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ref\",\"description\":\"Identifies the Ref of the deployment, if the deployment was created by ref.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"Identifies the repository associated with the deployment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The current state of the deployment.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"DeploymentState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"statuses\",\"description\":\"A list of statuses associated with the deployment.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatusConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"task\",\"description\":\"The deployment task.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentConnection\",\"description\":\"The connection type for Deployment.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Deployment\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Deployment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentEnvironmentChangedEvent\",\"description\":\"Represents a 'deployment_environment_changed' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deploymentStatus\",\"description\":\"The deployment status that updated the deployment environment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatus\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeploymentOrder\",\"description\":\"Ordering options for deployment connections\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order deployments by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"DeploymentOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"DeploymentOrderField\",\"description\":\"Properties by which deployment connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order collection by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"DeploymentState\",\"description\":\"The possible states in which a deployment can be.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ABANDONED\",\"description\":\"The pending deployment was not updated after 30 minutes.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ACTIVE\",\"description\":\"The deployment is currently active.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DESTROYED\",\"description\":\"An inactive transient deployment.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ERROR\",\"description\":\"The deployment experienced an error.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FAILURE\",\"description\":\"The deployment has failed.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INACTIVE\",\"description\":\"The deployment is inactive.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PENDING\",\"description\":\"The deployment is pending.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"QUEUED\",\"description\":\"The deployment has queued\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"IN_PROGRESS\",\"description\":\"The deployment is in progress.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WAITING\",\"description\":\"The deployment is waiting.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatus\",\"description\":\"Describes the status of a given deployment attempt.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"creator\",\"description\":\"Identifies the actor who triggered the deployment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deployment\",\"description\":\"Identifies the deployment associated with status.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Deployment\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"Identifies the description of the deployment.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"environmentUrl\",\"description\":\"Identifies the environment URL of the deployment.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"logUrl\",\"description\":\"Identifies the log URL of the deployment.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"Identifies the current state of the deployment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"DeploymentStatusState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatusConnection\",\"description\":\"The connection type for DeploymentStatus.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatusEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatus\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatusEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatus\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"DeploymentStatusState\",\"description\":\"The possible states for a deployment status.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PENDING\",\"description\":\"The deployment is pending.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUCCESS\",\"description\":\"The deployment was successful.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FAILURE\",\"description\":\"The deployment has failed.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INACTIVE\",\"description\":\"The deployment is inactive.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ERROR\",\"description\":\"The deployment experienced an error.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"QUEUED\",\"description\":\"The deployment is queued\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"IN_PROGRESS\",\"description\":\"The deployment is in progress.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WAITING\",\"description\":\"The deployment is waiting.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"DiffSide\",\"description\":\"The possible sides of a diff.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"LEFT\",\"description\":\"The left side of the diff.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RIGHT\",\"description\":\"The right side of the diff.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DisablePullRequestAutoMergeInput\",\"description\":\"Autogenerated input type of DisablePullRequestAutoMerge\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"ID of the pull request to disable auto merge on.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DisablePullRequestAutoMergePayload\",\"description\":\"Autogenerated return type of DisablePullRequestAutoMerge\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request auto merge was disabled on.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DisconnectedEvent\",\"description\":\"Represents a 'disconnected' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCrossRepository\",\"description\":\"Reference originated in a different repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"source\",\"description\":\"Issue or pull request from which the issue was disconnected.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"ReferencedSubject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"Issue or pull request which was disconnected.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"ReferencedSubject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DismissPullRequestReviewInput\",\"description\":\"Autogenerated input type of DismissPullRequestReview\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestReviewId\",\"description\":\"The Node ID of the pull request review to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"message\",\"description\":\"The contents of the pull request review dismissal message.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"DismissPullRequestReviewPayload\",\"description\":\"Autogenerated return type of DismissPullRequestReview\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReview\",\"description\":\"The dismissed pull request review.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DraftPullRequestReviewComment\",\"description\":\"Specifies a review comment to be left with a Pull Request Review.\",\"fields\":null,\"inputFields\":[{\"name\":\"path\",\"description\":\"Path to the file being commented on.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"position\",\"description\":\"Position in the file to leave a comment on.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"Body of the comment to leave.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"DraftPullRequestReviewThread\",\"description\":\"Specifies a review comment thread to be left with a Pull Request Review.\",\"fields\":null,\"inputFields\":[{\"name\":\"path\",\"description\":\"Path to the file being commented on.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"line\",\"description\":\"The line of the blob to which the thread refers. The end of the line range for multi-line comments.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"side\",\"description\":\"The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"DiffSide\",\"ofType\":null},\"defaultValue\":\"RIGHT\"},{\"name\":\"startLine\",\"description\":\"The first line of the range to which the comment refers.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"startSide\",\"description\":\"The side of the diff on which the start line resides.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"DiffSide\",\"ofType\":null},\"defaultValue\":\"RIGHT\"},{\"name\":\"body\",\"description\":\"Body of the comment to leave.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnablePullRequestAutoMergeInput\",\"description\":\"Autogenerated input type of EnablePullRequestAutoMerge\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"ID of the pull request to enable auto-merge on.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"commitHeadline\",\"description\":\"Commit headline to use for the commit when the PR is mergable; if omitted, a default message will be used.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"commitBody\",\"description\":\"Commit body to use for the commit when the PR is mergable; if omitted, a default message will be used.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"mergeMethod\",\"description\":\"The merge method to use. If omitted, defaults to 'MERGE'\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PullRequestMergeMethod\",\"ofType\":null},\"defaultValue\":\"MERGE\"},{\"name\":\"authorEmail\",\"description\":\"The email address to associate with this merge.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnablePullRequestAutoMergePayload\",\"description\":\"Autogenerated return type of EnablePullRequestAutoMerge\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request auto-merge was enabled on.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"description\":\"An account to manage multiple organizations with consolidated policy and billing.\",\"fields\":[{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the enterprise's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"billingInfo\",\"description\":\"Enterprise billing information visible to enterprise billing managers.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseBillingInfo\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The description of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"descriptionHTML\",\"description\":\"The description of the enterprise as HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"location\",\"description\":\"The location of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"members\",\"description\":\"A list of users who are members of this enterprise.\",\"args\":[{\"name\":\"organizationLogins\",\"description\":\"Only return members within the organizations with these logins\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for members returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseMemberOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"},{\"name\":\"role\",\"description\":\"The role of the user in the enterprise organization or server.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseUserAccountMembershipRole\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"deployment\",\"description\":\"Only return members within the selected GitHub Enterprise deployment\",\"type\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseUserDeployment\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseMemberConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizations\",\"description\":\"A list of organizations that belong to this enterprise.\",\"args\":[{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ownerInfo\",\"description\":\"Enterprise information only visible to enterprise owners.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOwnerInfo\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"slug\",\"description\":\"The URL-friendly identifier for the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userAccounts\",\"description\":\"A list of user accounts on this enterprise.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccountConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerIsAdmin\",\"description\":\"Is the current viewer an admin of this enterprise?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"websiteUrl\",\"description\":\"The URL of the enterprise website.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorEdge\",\"description\":\"A User who is an administrator of an enterprise.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"role\",\"description\":\"The role of the administrator.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorRole\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"description\":\"An invitation for a user to become an owner or billing manager of an enterprise.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The email of the person who was invited to the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise the invitation is for.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitee\",\"description\":\"The user who was invited to the enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"inviter\",\"description\":\"The user who created the invitation.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"role\",\"description\":\"The invitee's pending role in the enterprise (owner or billing_manager).\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorRole\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitationConnection\",\"description\":\"The connection type for EnterpriseAdministratorInvitation.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitationEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitationEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseAdministratorInvitationOrder\",\"description\":\"Ordering options for enterprise administrator invitation connections\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order enterprise administrator invitations by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorInvitationOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorInvitationOrderField\",\"description\":\"Properties by which enterprise administrator invitation connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order enterprise administrator member invitations by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorRole\",\"description\":\"The possible administrator roles in an enterprise account.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OWNER\",\"description\":\"Represents an owner of the enterprise account.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BILLING_MANAGER\",\"description\":\"Represents a billing manager of the enterprise account.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"description\":\"Metadata for an audit entry containing enterprise account information.\",\"fields\":[{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposClearAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteToBusinessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeEnableAuditEntry\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseBillingInfo\",\"description\":\"Enterprise billing information visible to enterprise billing managers and owners.\",\"fields\":[{\"name\":\"allLicensableUsersCount\",\"description\":\"The number of licenseable users/emails across the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"assetPacks\",\"description\":\"The number of data packs used by all organizations owned by the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"availableSeats\",\"description\":\"The number of available seats across all owned organizations based on the unique number of billable users.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"`availableSeats` will be replaced with `totalAvailableLicenses` to provide more clarity on the value being returned Use EnterpriseBillingInfo.totalAvailableLicenses instead. Removal on 2020-01-01 UTC.\"},{\"name\":\"bandwidthQuota\",\"description\":\"The bandwidth quota in GB for all organizations owned by the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bandwidthUsage\",\"description\":\"The bandwidth usage in GB for all organizations owned by the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bandwidthUsagePercentage\",\"description\":\"The bandwidth usage as a percentage of the bandwidth quota.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"seats\",\"description\":\"The total seats across all organizations owned by the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"`seats` will be replaced with `totalLicenses` to provide more clarity on the value being returned Use EnterpriseBillingInfo.totalLicenses instead. Removal on 2020-01-01 UTC.\"},{\"name\":\"storageQuota\",\"description\":\"The storage quota in GB for all organizations owned by the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"storageUsage\",\"description\":\"The storage usage in GB for all organizations owned by the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"storageUsagePercentage\",\"description\":\"The storage usage as a percentage of the storage quota.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalAvailableLicenses\",\"description\":\"The number of available licenses across all owned organizations based on the unique number of billable users.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalLicenses\",\"description\":\"The total number of licenses allocated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseDefaultRepositoryPermissionSettingValue\",\"description\":\"The possible values for the enterprise default repository permission setting.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NO_POLICY\",\"description\":\"Organizations in the enterprise choose default repository permissions for their members.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADMIN\",\"description\":\"Organization members will be able to clone, pull, push, and add new collaborators to all organization repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WRITE\",\"description\":\"Organization members will be able to clone, pull, and push all organization repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"READ\",\"description\":\"Organization members will be able to clone and pull all organization repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NONE\",\"description\":\"Organization members will only be able to clone and pull public repositories.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"description\":\"The possible values for an enabled/disabled enterprise setting.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ENABLED\",\"description\":\"The setting is enabled for organizations in the enterprise.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISABLED\",\"description\":\"The setting is disabled for organizations in the enterprise.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NO_POLICY\",\"description\":\"There is no policy set for organizations in the enterprise.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledSettingValue\",\"description\":\"The possible values for an enabled/no policy enterprise setting.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ENABLED\",\"description\":\"The setting is enabled for organizations in the enterprise.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NO_POLICY\",\"description\":\"There is no policy set for organizations in the enterprise.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseIdentityProvider\",\"description\":\"An identity provider configured to provision identities for an enterprise.\",\"fields\":[{\"name\":\"digestMethod\",\"description\":\"The digest algorithm used to sign SAML requests for the identity provider.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SamlDigestAlgorithm\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise this identity provider belongs to.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"externalIdentities\",\"description\":\"ExternalIdentities provisioned by this identity provider.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentityConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"idpCertificate\",\"description\":\"The x509 certificate used by the identity provider to sign assertions and responses.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"X509Certificate\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issuer\",\"description\":\"The Issuer Entity ID for the SAML identity provider.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"recoveryCodes\",\"description\":\"Recovery codes that can be used by admins to access the enterprise if the identity provider is unavailable.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signatureMethod\",\"description\":\"The signature algorithm used to sign SAML requests for the identity provider.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SamlSignatureAlgorithm\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ssoUrl\",\"description\":\"The URL endpoint for the identity provider's SAML SSO.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"EnterpriseMember\",\"description\":\"An object that is a member of an enterprise.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccount\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseMemberConnection\",\"description\":\"The connection type for EnterpriseMember.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseMemberEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"EnterpriseMember\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseMemberEdge\",\"description\":\"A User who is a member of an enterprise through one or more organizations.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUnlicensed\",\"description\":\"Whether the user does not have a license for the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"All members consume a license Removal on 2021-01-01 UTC.\"},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"EnterpriseMember\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseMemberOrder\",\"description\":\"Ordering options for enterprise member connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order enterprise members by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseMemberOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseMemberOrderField\",\"description\":\"Properties by which enterprise member connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"LOGIN\",\"description\":\"Order enterprise members by login\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CREATED_AT\",\"description\":\"Order enterprise members by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseMembersCanCreateRepositoriesSettingValue\",\"description\":\"The possible values for the enterprise members can create repositories setting.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NO_POLICY\",\"description\":\"Organization administrators choose whether to allow members to create repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ALL\",\"description\":\"Members will be able to create public and private repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"Members will be able to create only public repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"Members will be able to create only private repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISABLED\",\"description\":\"Members will not be able to create public or private repositories.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseMembersCanMakePurchasesSettingValue\",\"description\":\"The possible values for the members can make purchases setting.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ENABLED\",\"description\":\"The setting is enabled for organizations in the enterprise.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISABLED\",\"description\":\"The setting is disabled for organizations in the enterprise.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOrganizationMembershipConnection\",\"description\":\"The connection type for Organization.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOrganizationMembershipEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOrganizationMembershipEdge\",\"description\":\"An enterprise organization that a user is a member of.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"role\",\"description\":\"The role of the user in the enterprise membership.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseUserAccountMembershipRole\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOutsideCollaboratorConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOutsideCollaboratorEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOutsideCollaboratorEdge\",\"description\":\"A User who is an outside collaborator of an enterprise through one or more organizations.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUnlicensed\",\"description\":\"Whether the outside collaborator does not have a license for the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"All outside collaborators consume a license Removal on 2021-01-01 UTC.\"},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositories\",\"description\":\"The enterprise organization repositories this user is a member of.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null},\"defaultValue\":\"{field: NAME, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfoConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOwnerInfo\",\"description\":\"Enterprise information only visible to enterprise owners.\",\"fields\":[{\"name\":\"admins\",\"description\":\"A list of all of the administrators for this enterprise.\",\"args\":[{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"role\",\"description\":\"The role to filter by.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorRole\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for administrators returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseMemberOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"affiliatedUsersWithTwoFactorDisabled\",\"description\":\"A list of users in the enterprise who currently have two-factor authentication disabled.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"affiliatedUsersWithTwoFactorDisabledExist\",\"description\":\"Whether or not affiliated users with two-factor authentication disabled exist in the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"allowPrivateRepositoryForkingSetting\",\"description\":\"The setting value for whether private repository forking is enabled for repositories in organizations in this enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"allowPrivateRepositoryForkingSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided private repository forking setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"defaultRepositoryPermissionSetting\",\"description\":\"The setting value for base repository permissions for organizations in this enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseDefaultRepositoryPermissionSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"defaultRepositoryPermissionSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided default repository permission.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The permission to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"DefaultRepositoryPermissionField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"domains\",\"description\":\"A list of domains owned by the enterprise.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isVerified\",\"description\":\"Filter whether or not the domain is verified.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"null\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for verifiable domains returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"VerifiableDomainOrder\",\"ofType\":null},\"defaultValue\":\"{field: DOMAIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomainConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseServerInstallations\",\"description\":\"Enterprise Server installations owned by the enterprise.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"connectedOnly\",\"description\":\"Whether or not to only return installations discovered via GitHub Connect.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for Enterprise Server installations returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseServerInstallationOrder\",\"ofType\":null},\"defaultValue\":\"{field: HOST_NAME, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ipAllowListEnabledSetting\",\"description\":\"The setting value for whether the enterprise has an IP allow list enabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IpAllowListEnabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ipAllowListEntries\",\"description\":\"The IP addresses that are allowed to access resources owned by the enterprise.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for IP allow list entries returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IpAllowListEntryOrder\",\"ofType\":null},\"defaultValue\":\"{field: ALLOW_LIST_VALUE, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUpdatingDefaultRepositoryPermission\",\"description\":\"Whether or not the default repository permission is currently being updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUpdatingTwoFactorRequirement\",\"description\":\"Whether the two-factor authentication requirement is currently being enforced.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanChangeRepositoryVisibilitySetting\",\"description\":\"The setting value for whether organization members with admin permissions on a repository can change repository visibility.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanChangeRepositoryVisibilitySettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided can change repository visibility setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanCreateInternalRepositoriesSetting\",\"description\":\"The setting value for whether members of organizations in the enterprise can create internal repositories.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanCreatePrivateRepositoriesSetting\",\"description\":\"The setting value for whether members of organizations in the enterprise can create private repositories.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanCreatePublicRepositoriesSetting\",\"description\":\"The setting value for whether members of organizations in the enterprise can create public repositories.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanCreateRepositoriesSetting\",\"description\":\"The setting value for whether members of organizations in the enterprise can create repositories.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseMembersCanCreateRepositoriesSettingValue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanCreateRepositoriesSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided repository creation setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrganizationMembersCanCreateRepositoriesSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanDeleteIssuesSetting\",\"description\":\"The setting value for whether members with admin permissions for repositories can delete issues.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanDeleteIssuesSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided members can delete issues setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanDeleteRepositoriesSetting\",\"description\":\"The setting value for whether members with admin permissions for repositories can delete or transfer repositories.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanDeleteRepositoriesSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided members can delete repositories setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanInviteCollaboratorsSetting\",\"description\":\"The setting value for whether members of organizations in the enterprise can invite outside collaborators.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanInviteCollaboratorsSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided members can invite collaborators setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanMakePurchasesSetting\",\"description\":\"Indicates whether members of this enterprise's organizations can purchase additional services for those organizations.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseMembersCanMakePurchasesSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanUpdateProtectedBranchesSetting\",\"description\":\"The setting value for whether members with admin permissions for repositories can update protected branches.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanUpdateProtectedBranchesSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided members can update protected branches setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanViewDependencyInsightsSetting\",\"description\":\"The setting value for whether members can view dependency insights.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersCanViewDependencyInsightsSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided members can view dependency insights setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"notificationDeliveryRestrictionEnabledSetting\",\"description\":\"Indicates if email notification delivery for this enterprise is restricted to verified domains.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"NotificationRestrictionSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationProjectsSetting\",\"description\":\"The setting value for whether organization projects are enabled for organizations in this enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationProjectsSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided organization projects setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"outsideCollaborators\",\"description\":\"A list of outside collaborators across the repositories in the enterprise.\",\"args\":[{\"name\":\"login\",\"description\":\"The login of one specific outside collaborator.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for outside collaborators returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseMemberOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"},{\"name\":\"visibility\",\"description\":\"Only return outside collaborators on repositories with this visibility.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryVisibility\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOutsideCollaboratorConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pendingAdminInvitations\",\"description\":\"A list of pending administrator invitations for the enterprise.\",\"args\":[{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pending enterprise administrator invitations returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseAdministratorInvitationOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"},{\"name\":\"role\",\"description\":\"The role to filter by.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorRole\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pendingCollaboratorInvitations\",\"description\":\"A list of pending collaborator invitations across the repositories in the enterprise.\",\"args\":[{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pending repository collaborator invitations returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryInvitationOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryInvitationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pendingCollaborators\",\"description\":\"A list of pending collaborators across the repositories in the enterprise.\",\"args\":[{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pending repository collaborator invitations returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryInvitationOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterprisePendingCollaboratorConnection\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"Repository invitations can now be associated with an email, not only an invitee. Use the `pendingCollaboratorInvitations` field instead. Removal on 2020-10-01 UTC.\"},{\"name\":\"pendingMemberInvitations\",\"description\":\"A list of pending member invitations for organizations in the enterprise.\",\"args\":[{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterprisePendingMemberInvitationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryProjectsSetting\",\"description\":\"The setting value for whether repository projects are enabled in this enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryProjectsSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided repository projects setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"samlIdentityProvider\",\"description\":\"The SAML Identity Provider for the enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseIdentityProvider\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"samlIdentityProviderSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the SAML single sign-on setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IdentityProviderConfigurationState\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"supportEntitlements\",\"description\":\"A list of members with a support entitlement.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for support entitlement users returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseMemberOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseMemberConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamDiscussionsSetting\",\"description\":\"The setting value for whether team discussions are enabled for organizations in this enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamDiscussionsSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the provided team discussions setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"twoFactorRequiredSetting\",\"description\":\"The setting value for whether the enterprise requires two-factor authentication for its organizations and users.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"twoFactorRequiredSettingOrganizations\",\"description\":\"A list of enterprise organizations configured with the two-factor authentication setting value.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The setting value to find organizations for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations with this setting.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterprisePendingCollaboratorConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterprisePendingCollaboratorEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterprisePendingCollaboratorEdge\",\"description\":\"A user with an invitation to be a collaborator on a repository owned by an organization in an enterprise.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUnlicensed\",\"description\":\"Whether the invited collaborator does not have a license for the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"All pending collaborators consume a license Removal on 2021-01-01 UTC.\"},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositories\",\"description\":\"The enterprise organization repositories this user is a member of.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null},\"defaultValue\":\"{field: NAME, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfoConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterprisePendingMemberInvitationConnection\",\"description\":\"The connection type for OrganizationInvitation.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterprisePendingMemberInvitationEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalUniqueUserCount\",\"description\":\"Identifies the total count of unique users in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterprisePendingMemberInvitationEdge\",\"description\":\"An invitation to be a member in an enterprise organization.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUnlicensed\",\"description\":\"Whether the invitation has a license for the enterprise.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"All pending members consume a license Removal on 2020-07-01 UTC.\"},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfo\",\"description\":\"A subset of repository information queryable from an enterprise.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPrivate\",\"description\":\"Identifies if the repository is private.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The repository's name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nameWithOwner\",\"description\":\"The repository's name with owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfoConnection\",\"description\":\"The connection type for EnterpriseRepositoryInfo.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfoEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfoEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfo\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallation\",\"description\":\"An Enterprise Server installation.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"customerName\",\"description\":\"The customer name to which the Enterprise Server installation belongs.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hostName\",\"description\":\"The host name of the Enterprise Server installation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isConnected\",\"description\":\"Whether or not the installation is connected to an Enterprise Server installation via GitHub Connect.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userAccounts\",\"description\":\"User accounts on this Enterprise Server installation.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for Enterprise Server user accounts returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseServerUserAccountOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userAccountsUploads\",\"description\":\"User accounts uploads for the Enterprise Server installation.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for Enterprise Server user accounts uploads returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseServerUserAccountsUploadOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountsUploadConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallationConnection\",\"description\":\"The connection type for EnterpriseServerInstallation.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallationEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallationEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseServerInstallationOrder\",\"description\":\"Ordering options for Enterprise Server installation connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order Enterprise Server installations by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerInstallationOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerInstallationOrderField\",\"description\":\"Properties by which Enterprise Server installation connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"HOST_NAME\",\"description\":\"Order Enterprise Server installations by host name\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CUSTOMER_NAME\",\"description\":\"Order Enterprise Server installations by customer name\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CREATED_AT\",\"description\":\"Order Enterprise Server installations by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccount\",\"description\":\"A user account on an Enterprise Server installation.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"emails\",\"description\":\"User emails belonging to this user account.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for Enterprise Server user account emails returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseServerUserAccountEmailOrder\",\"ofType\":null},\"defaultValue\":\"{field: EMAIL, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEmailConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseServerInstallation\",\"description\":\"The Enterprise Server installation on which this user account exists.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSiteAdmin\",\"description\":\"Whether the user account is a site administrator on the Enterprise Server installation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"The login of the user account on the Enterprise Server installation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"profileName\",\"description\":\"The profile name of the user account on the Enterprise Server installation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"remoteCreatedAt\",\"description\":\"The date and time when the user account was created on the Enterprise Server installation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"remoteUserId\",\"description\":\"The ID of the user account on the Enterprise Server installation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountConnection\",\"description\":\"The connection type for EnterpriseServerUserAccount.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccount\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccount\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEmail\",\"description\":\"An email belonging to a user account on an Enterprise Server installation.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The email address.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPrimary\",\"description\":\"Indicates whether this is the primary email of the associated user account.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userAccount\",\"description\":\"The user account to which the email belongs.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccount\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEmailConnection\",\"description\":\"The connection type for EnterpriseServerUserAccountEmail.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEmailEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEmail\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEmailEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEmail\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseServerUserAccountEmailOrder\",\"description\":\"Ordering options for Enterprise Server user account email connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order emails by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerUserAccountEmailOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerUserAccountEmailOrderField\",\"description\":\"Properties by which Enterprise Server user account email connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"EMAIL\",\"description\":\"Order emails by email\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseServerUserAccountOrder\",\"description\":\"Ordering options for Enterprise Server user account connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order user accounts by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerUserAccountOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerUserAccountOrderField\",\"description\":\"Properties by which Enterprise Server user account connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"LOGIN\",\"description\":\"Order user accounts by login\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REMOTE_CREATED_AT\",\"description\":\"Order user accounts by creation time on the Enterprise Server installation\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountsUpload\",\"description\":\"A user accounts upload from an Enterprise Server installation.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise to which this upload belongs.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseServerInstallation\",\"description\":\"The Enterprise Server installation for which this upload was generated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the file uploaded.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"syncState\",\"description\":\"The synchronization state of the upload\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerUserAccountsUploadSyncState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountsUploadConnection\",\"description\":\"The connection type for EnterpriseServerUserAccountsUpload.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountsUploadEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountsUpload\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountsUploadEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountsUpload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnterpriseServerUserAccountsUploadOrder\",\"description\":\"Ordering options for Enterprise Server user accounts upload connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order user accounts uploads by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerUserAccountsUploadOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerUserAccountsUploadOrderField\",\"description\":\"Properties by which Enterprise Server user accounts upload connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order user accounts uploads by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseServerUserAccountsUploadSyncState\",\"description\":\"Synchronization state of the Enterprise Server user accounts upload\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PENDING\",\"description\":\"The synchronization of the upload is pending.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUCCESS\",\"description\":\"The synchronization of the upload succeeded.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FAILURE\",\"description\":\"The synchronization of the upload failed.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccount\",\"description\":\"An account for a user who is an admin of an enterprise or a member of an enterprise through one or more organizations.\",\"fields\":[{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the enterprise user account's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise in which this user account exists.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"An identifier for the enterprise user account, a login or email address\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the enterprise user account\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizations\",\"description\":\"A list of enterprise organizations this user is a member of.\",\"args\":[{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for organizations returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"},{\"name\":\"role\",\"description\":\"The role of the user in the enterprise organization.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseUserAccountMembershipRole\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseOrganizationMembershipConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this user.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this user.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user within the enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccountConnection\",\"description\":\"The connection type for EnterpriseUserAccount.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccountEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccount\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccountEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccount\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseUserAccountMembershipRole\",\"description\":\"The possible roles for enterprise membership.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"MEMBER\",\"description\":\"The user is a member of the enterprise membership.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OWNER\",\"description\":\"The user is an owner of the enterprise membership.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"EnterpriseUserDeployment\",\"description\":\"The possible GitHub Enterprise deployments where this user can exist.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CLOUD\",\"description\":\"The user is part of a GitHub Enterprise Cloud deployment.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SERVER\",\"description\":\"The user is part of a GitHub Enterprise Server deployment.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentity\",\"description\":\"An external identity provisioned by SAML SSO or SCIM.\",\"fields\":[{\"name\":\"guid\",\"description\":\"The GUID for this identity\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationInvitation\",\"description\":\"Organization invitation for this SCIM-provisioned external identity\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"samlIdentity\",\"description\":\"SAML Identity attributes\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentitySamlAttributes\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"scimIdentity\",\"description\":\"SCIM Identity attributes\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentityScimAttributes\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"User linked to this external identity. Will be NULL if this identity has not been claimed by an organization member.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentityConnection\",\"description\":\"The connection type for ExternalIdentity.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentityEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentity\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentityEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentity\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentitySamlAttributes\",\"description\":\"SAML attributes for the External Identity\",\"fields\":[{\"name\":\"emails\",\"description\":\"The emails associated with the SAML identity\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserEmailMetadata\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"familyName\",\"description\":\"Family name of the SAML identity\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"givenName\",\"description\":\"Given name of the SAML identity\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"groups\",\"description\":\"The groups linked to this identity in IDP\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nameId\",\"description\":\"The NameID of the SAML identity\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"username\",\"description\":\"The userName of the SAML identity\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentityScimAttributes\",\"description\":\"SCIM attributes for the External Identity\",\"fields\":[{\"name\":\"emails\",\"description\":\"The emails associated with the SCIM identity\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserEmailMetadata\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"familyName\",\"description\":\"Family name of the SCIM identity\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"givenName\",\"description\":\"Given name of the SCIM identity\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"groups\",\"description\":\"The groups linked to this identity in IDP\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"username\",\"description\":\"The userName of the SCIM identity\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"FileViewedState\",\"description\":\"The possible viewed states of a file .\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"DISMISSED\",\"description\":\"The file has new changes since last viewed.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"VIEWED\",\"description\":\"The file has been marked as viewed.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNVIEWED\",\"description\":\"The file has not been marked as viewed.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"Float\",\"description\":\"Represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"FollowUserInput\",\"description\":\"Autogenerated input type of FollowUser\",\"fields\":null,\"inputFields\":[{\"name\":\"userId\",\"description\":\"ID of the user to follow.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"FollowUserPayload\",\"description\":\"Autogenerated return type of FollowUser\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user that was followed.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"FollowerConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"FollowingConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"FundingLink\",\"description\":\"A funding platform link for a repository.\",\"fields\":[{\"name\":\"platform\",\"description\":\"The funding platform this link is for.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"FundingPlatform\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The configured URL for this funding link.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"FundingPlatform\",\"description\":\"The possible funding platforms for repository funding links.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"GITHUB\",\"description\":\"GitHub funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PATREON\",\"description\":\"Patreon funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OPEN_COLLECTIVE\",\"description\":\"Open Collective funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"KO_FI\",\"description\":\"Ko-fi funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TIDELIFT\",\"description\":\"Tidelift funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMMUNITY_BRIDGE\",\"description\":\"Community Bridge funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LIBERAPAY\",\"description\":\"Liberapay funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ISSUEHUNT\",\"description\":\"IssueHunt funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OTECHIE\",\"description\":\"Otechie funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CUSTOM\",\"description\":\"Custom funding platform.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GenericHovercardContext\",\"description\":\"A generic hovercard context with a message and icon\",\"fields\":[{\"name\":\"message\",\"description\":\"A string describing this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"octicon\",\"description\":\"An octicon to accompany this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"HovercardContext\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"description\":\"A Gist.\",\"fields\":[{\"name\":\"comments\",\"description\":\"A list of comments associated with the gist\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GistCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The gist description.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"files\",\"description\":\"The files in this gist.\",\"args\":[{\"name\":\"limit\",\"description\":\"The maximum number of files to return.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"10\"},{\"name\":\"oid\",\"description\":\"The oid of the files to return\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GistFile\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"forks\",\"description\":\"A list of forks associated with the gist\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for gists returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"GistOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GistConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isFork\",\"description\":\"Identifies if the gist is a fork.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPublic\",\"description\":\"Whether the gist is public or not.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The gist name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The gist owner.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"RepositoryOwner\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pushedAt\",\"description\":\"Identifies when the gist was last pushed to.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTML path to this resource.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"stargazerCount\",\"description\":\"Returns a count of how many stargazers there are on this object\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"stargazers\",\"description\":\"A list of users who have starred this starrable.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"StarOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StargazerConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this Gist.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerHasStarred\",\"description\":\"Returns a boolean indicating whether the viewing user has starred this starrable.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Starrable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"description\":\"Represents a comment on an Gist.\",\"fields\":[{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the gist.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"Identifies the comment body.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The body rendered to text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"gist\",\"description\":\"The associated gist.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isMinimized\",\"description\":\"Returns whether or not a comment has been minimized.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"minimizedReason\",\"description\":\"Returns why the comment was minimized.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDelete\",\"description\":\"Check if the current viewer can delete this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanMinimize\",\"description\":\"Check if the current viewer can minimize this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Deletable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Minimizable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GistCommentConnection\",\"description\":\"The connection type for GistComment.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GistCommentEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GistCommentEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GistConnection\",\"description\":\"The connection type for Gist.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GistEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GistEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GistFile\",\"description\":\"A file in a gist.\",\"fields\":[{\"name\":\"encodedName\",\"description\":\"The file name encoded to remove characters that are invalid in URL paths.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"encoding\",\"description\":\"The gist file encoding.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"extension\",\"description\":\"The file extension from the file name.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isImage\",\"description\":\"Indicates if this file is an image.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isTruncated\",\"description\":\"Whether the file's contents were truncated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"language\",\"description\":\"The programming language this file is written in.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Language\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The gist file name.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"size\",\"description\":\"The gist file size in bytes.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"text\",\"description\":\"UTF8 text data or null if the file is binary\",\"args\":[{\"name\":\"truncate\",\"description\":\"Optionally truncate the returned file to this length.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"GistOrder\",\"description\":\"Ordering options for gist connections\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order repositories by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"GistOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"GistOrderField\",\"description\":\"Properties by which gist connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order gists by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UPDATED_AT\",\"description\":\"Order gists by update time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUSHED_AT\",\"description\":\"Order gists by push time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"GistPrivacy\",\"description\":\"The privacy of a Gist\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PUBLIC\",\"description\":\"Public\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SECRET\",\"description\":\"Secret\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ALL\",\"description\":\"Gists that are public and secret\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GitActor\",\"description\":\"Represents an actor in a Git commit (ie. an author or committer).\",\"fields\":[{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the author's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"date\",\"description\":\"The timestamp of the Git action (authoring or committing).\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitTimestamp\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The email in the Git commit.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name in the Git commit.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The GitHub user corresponding to the email field. Null if no such user exists.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GitActorConnection\",\"description\":\"The connection type for GitActor.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GitActorEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GitActor\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GitActorEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"GitActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GitHubMetadata\",\"description\":\"Represents information about the GitHub instance.\",\"fields\":[{\"name\":\"gitHubServicesSha\",\"description\":\"Returns a String that's a SHA of `github-services`\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"gitIpAddresses\",\"description\":\"IP addresses that users connect to for git operations\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hookIpAddresses\",\"description\":\"IP addresses that service hooks are sent from\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"importerIpAddresses\",\"description\":\"IP addresses that the importer connects from\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPasswordAuthenticationVerifiable\",\"description\":\"Whether or not users are verified\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pagesIpAddresses\",\"description\":\"IP addresses for GitHub Pages' A records\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"description\":\"Represents a Git object.\",\"fields\":[{\"name\":\"abbreviatedOid\",\"description\":\"An abbreviated version of the Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitResourcePath\",\"description\":\"The HTTP path for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitUrl\",\"description\":\"The HTTP URL for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oid\",\"description\":\"The Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The Repository the Git object belongs to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Blob\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Tag\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Tree\",\"ofType\":null}]},{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"description\":\"A Git object ID.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"GitSSHRemote\",\"description\":\"Git SSH string\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"GitSignature\",\"description\":\"Information about a signature (GPG or S/MIME) on a Commit or Tag.\",\"fields\":[{\"name\":\"email\",\"description\":\"Email used to sign this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isValid\",\"description\":\"True if the signature is valid and verified by GitHub.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"payload\",\"description\":\"Payload for GPG signing object. Raw ODB object without the signature header.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signature\",\"description\":\"ASCII-armored signature header from object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signer\",\"description\":\"GitHub user corresponding to the email signing this commit.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"GitSignatureState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"wasSignedByGitHub\",\"description\":\"True if the signature was made with GitHub's signing key.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"GpgSignature\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SmimeSignature\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnknownSignature\",\"ofType\":null}]},{\"kind\":\"ENUM\",\"name\":\"GitSignatureState\",\"description\":\"The state of a Git signature.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"VALID\",\"description\":\"Valid signature and verified by GitHub\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INVALID\",\"description\":\"Invalid signature\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MALFORMED_SIG\",\"description\":\"Malformed signature\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNKNOWN_KEY\",\"description\":\"Key used for signing not known to GitHub\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BAD_EMAIL\",\"description\":\"Invalid email used for signing\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNVERIFIED_EMAIL\",\"description\":\"Email used for signing unverified on GitHub\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NO_USER\",\"description\":\"Email used for signing not known to GitHub\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNKNOWN_SIG_TYPE\",\"description\":\"Unknown signature type\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNSIGNED\",\"description\":\"Unsigned\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"GPGVERIFY_UNAVAILABLE\",\"description\":\"Internal error - the GPG verification service is unavailable at the moment\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"GPGVERIFY_ERROR\",\"description\":\"Internal error - the GPG verification service misbehaved\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NOT_SIGNING_KEY\",\"description\":\"The usage flags for the key that signed this don't allow signing\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"EXPIRED_KEY\",\"description\":\"Signing key expired\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OCSP_PENDING\",\"description\":\"Valid signature, pending certificate revocation checking\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OCSP_ERROR\",\"description\":\"Valid signature, though certificate revocation check failed\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BAD_CERT\",\"description\":\"The signing certificate or its chain could not be verified\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OCSP_REVOKED\",\"description\":\"One or more certificates in chain has been revoked\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"GitTimestamp\",\"description\":\"An ISO-8601 encoded date string. Unlike the DateTime type, GitTimestamp is not converted in UTC.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"GpgSignature\",\"description\":\"Represents a GPG signature on a Commit or Tag.\",\"fields\":[{\"name\":\"email\",\"description\":\"Email used to sign this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isValid\",\"description\":\"True if the signature is valid and verified by GitHub.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"keyId\",\"description\":\"Hex-encoded ID of the key that signed this object.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"payload\",\"description\":\"Payload for GPG signing object. Raw ODB object without the signature header.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signature\",\"description\":\"ASCII-armored signature header from object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signer\",\"description\":\"GitHub user corresponding to the email signing this commit.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"GitSignatureState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"wasSignedByGitHub\",\"description\":\"True if the signature was made with GitHub's signing key.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"GitSignature\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"description\":\"A string containing HTML code.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefDeletedEvent\",\"description\":\"Represents a 'head_ref_deleted' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"headRef\",\"description\":\"Identifies the Ref associated with the `head_ref_deleted` event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"headRefName\",\"description\":\"Identifies the name of the Ref associated with the `head_ref_deleted` event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefForcePushedEvent\",\"description\":\"Represents a 'head_ref_force_pushed' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"afterCommit\",\"description\":\"Identifies the after commit SHA for the 'head_ref_force_pushed' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"beforeCommit\",\"description\":\"Identifies the before commit SHA for the 'head_ref_force_pushed' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ref\",\"description\":\"Identifies the fully qualified ref name for the 'head_ref_force_pushed' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefRestoredEvent\",\"description\":\"Represents a 'head_ref_restored' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Hovercard\",\"description\":\"Detail needed to display a hovercard for a user\",\"fields\":[{\"name\":\"contexts\",\"description\":\"Each of the contexts for this hovercard\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"HovercardContext\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"HovercardContext\",\"description\":\"An individual line of a hovercard\",\"fields\":[{\"name\":\"message\",\"description\":\"A string describing this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"octicon\",\"description\":\"An octicon to accompany this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"GenericHovercardContext\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationTeamsHovercardContext\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationsHovercardContext\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewStatusHovercardContext\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ViewerHovercardContext\",\"ofType\":null}]},{\"kind\":\"SCALAR\",\"name\":\"ID\",\"description\":\"Represents a unique identifier that is Base64 obfuscated. It is often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\\\"VXNlci0xMA==\\\"`) or integer (such as `4`) input value will be accepted as an ID.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"IdentityProviderConfigurationState\",\"description\":\"The possible states in which authentication can be configured with an identity provider.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ENFORCED\",\"description\":\"Authentication with an identity provider is configured and enforced.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONFIGURED\",\"description\":\"Authentication with an identity provider is configured but not enforced.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNCONFIGURED\",\"description\":\"Authentication with an identity provider is not configured.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"Int\",\"description\":\"Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"InviteEnterpriseAdminInput\",\"description\":\"Autogenerated input type of InviteEnterpriseAdmin\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise to which you want to invite an administrator.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"invitee\",\"description\":\"The login of a user to invite as an administrator.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"email\",\"description\":\"The email of the person to invite as an administrator.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"role\",\"description\":\"The role of the administrator.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorRole\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"InviteEnterpriseAdminPayload\",\"description\":\"Autogenerated return type of InviteEnterpriseAdmin\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitation\",\"description\":\"The created enterprise administrator invitation.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"IpAllowListEnabledSettingValue\",\"description\":\"The possible values for the IP allow list enabled setting.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ENABLED\",\"description\":\"The setting is enabled for the owner.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISABLED\",\"description\":\"The setting is disabled for the owner.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntry\",\"description\":\"An IP address or range of addresses that is allowed to access an owner's resources.\",\"fields\":[{\"name\":\"allowListValue\",\"description\":\"A single IP address or range of IP addresses in CIDR notation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isActive\",\"description\":\"Whether the entry is currently active.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the IP allow list entry.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The owner of the IP allow list entry.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"IpAllowListOwner\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntryConnection\",\"description\":\"The connection type for IpAllowListEntry.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntryEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntry\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntryEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntry\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"IpAllowListEntryOrder\",\"description\":\"Ordering options for IP allow list entry connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order IP allow list entries by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IpAllowListEntryOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"IpAllowListEntryOrderField\",\"description\":\"Properties by which IP allow list entry connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order IP allow list entries by creation time.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ALLOW_LIST_VALUE\",\"description\":\"Order IP allow list entries by the allow list value.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"IpAllowListOwner\",\"description\":\"Types that can own an IP allow list.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"description\":\"An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project.\",\"fields\":[{\"name\":\"activeLockReason\",\"description\":\"Reason that the conversation was locked.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"LockReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"assignees\",\"description\":\"A list of Users assigned to this object.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the subject of the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"Identifies the body of the issue.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyResourcePath\",\"description\":\"The http path for this issue body\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"Identifies the body of the issue rendered to text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyUrl\",\"description\":\"The http URL for this issue body\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closed\",\"description\":\"`true` if the object is closed (definition of closed may depend on type)\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closedAt\",\"description\":\"Identifies the date and time when the object was closed.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"comments\",\"description\":\"A list of comments associated with the Issue.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for issue comments returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueCommentOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hovercard\",\"description\":\"The hovercard information for this issue\",\"args\":[{\"name\":\"includeNotificationContexts\",\"description\":\"Whether or not to include notification contexts\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"true\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Hovercard\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPinned\",\"description\":\"Indicates whether or not this issue is currently pinned to the repository issues list\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isReadByViewer\",\"description\":\"Is this issue read by the viewer\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"labels\",\"description\":\"A list of labels associated with the object.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for labels returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"LabelOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"LabelConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"locked\",\"description\":\"`true` if the object is locked\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"milestone\",\"description\":\"Identifies the milestone associated with the issue.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"number\",\"description\":\"Identifies the issue number.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"participants\",\"description\":\"A list of Users that are participating in the Issue conversation.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectCards\",\"description\":\"List of project cards associated with this issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"archivedStates\",\"description\":\"A list of archived states to filter the cards by\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectCardArchivedState\",\"ofType\":null}},\"defaultValue\":\"[ARCHIVED, NOT_ARCHIVED]\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCardConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this issue\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"Identifies the state of the issue.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"timeline\",\"description\":\"A list of events, comments, commits, etc. associated with the issue.\",\"args\":[{\"name\":\"since\",\"description\":\"Allows filtering timeline events by a `since` timestamp.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineConnection\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"`timeline` will be removed Use Issue.timelineItems instead. Removal on 2020-10-01 UTC.\"},{\"name\":\"timelineItems\",\"description\":\"A list of events, comments, commits, etc. associated with the issue.\",\"args\":[{\"name\":\"since\",\"description\":\"Filter timeline items by a `since` timestamp.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"skip\",\"description\":\"Skips the first _n_ elements in the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"itemTypes\",\"description\":\"Filter timeline items by type.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueTimelineItemsItemType\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineItemsConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"Identifies the issue title.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this issue\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSubscribe\",\"description\":\"Check if the viewer is able to change their subscription status for the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerSubscription\",\"description\":\"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Assignable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Closable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Labelable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Lockable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"description\":\"Represents a comment on an Issue.\",\"fields\":[{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the subject of the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"The body as Markdown.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The body rendered to text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isMinimized\",\"description\":\"Returns whether or not a comment has been minimized.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"Identifies the issue associated with the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"minimizedReason\",\"description\":\"Returns why the comment was minimized.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"Returns the pull request associated with the comment, if this comment was made on a\\npull request.\\n\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this issue comment\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this issue comment\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDelete\",\"description\":\"Check if the current viewer can delete this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanMinimize\",\"description\":\"Check if the current viewer can minimize this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Deletable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Minimizable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueCommentConnection\",\"description\":\"The connection type for IssueComment.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueCommentEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueCommentEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueCommentOrder\",\"description\":\"Ways in which lists of issue comments can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order issue comments by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueCommentOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order issue comments by the specified field.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"IssueCommentOrderField\",\"description\":\"Properties by which issue comment connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"UPDATED_AT\",\"description\":\"Order issue comments by update time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueConnection\",\"description\":\"The connection type for Issue.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueContributionsByRepository\",\"description\":\"This aggregates issues opened by a user within one repository.\",\"fields\":[{\"name\":\"contributions\",\"description\":\"The issue contributions.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for contributions returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ContributionOrder\",\"ofType\":null},\"defaultValue\":\"{direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedIssueContributionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository in which the issues were opened.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueFilters\",\"description\":\"Ways in which to filter lists of issues.\",\"fields\":null,\"inputFields\":[{\"name\":\"assignee\",\"description\":\"List issues assigned to given name. Pass in `null` for issues with no assigned user, and `*` for issues assigned to any user.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"createdBy\",\"description\":\"List issues created by given name.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"List issues where the list of label names exist on the issue.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"mentioned\",\"description\":\"List issues where the given name is mentioned in the issue.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"milestone\",\"description\":\"List issues by given milestone argument. If an string representation of an integer is passed, it should refer to a milestone by its number field. Pass in `null` for issues with no milestone, and `*` for issues that are assigned to any milestone.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"since\",\"description\":\"List issues that have been updated at or after the given date.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"List issues filtered by the list of states given.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"viewerSubscribed\",\"description\":\"List issues subscribed to by viewer.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"IssueOrPullRequest\",\"description\":\"Used for return value of Repository.issueOrPullRequest.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"description\":\"Ways in which lists of issues can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order issues by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order issues by the specified field.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"IssueOrderField\",\"description\":\"Properties by which issue connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order issues by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UPDATED_AT\",\"description\":\"Order issues by update time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMMENTS\",\"description\":\"Order issues by comment count\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"IssueState\",\"description\":\"The possible states of an issue.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OPEN\",\"description\":\"An issue that is still open\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CLOSED\",\"description\":\"An issue that has been closed\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueTemplate\",\"description\":\"A repository issue template.\",\"fields\":[{\"name\":\"about\",\"description\":\"The template purpose.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"The suggested issue body.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The template name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"The suggested issue title.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineConnection\",\"description\":\"The connection type for IssueTimelineItem.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineItemEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"IssueTimelineItem\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"IssueTimelineItem\",\"description\":\"An item in an issue timeline\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"AssignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ClosedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CrossReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DemilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RenamedTitleEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReopenedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TransferredEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnassignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnsubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UserBlockedEvent\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineItemEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"IssueTimelineItem\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"IssueTimelineItems\",\"description\":\"An item in an issue timeline\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"AddedToProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AssignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ClosedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CommentDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConnectedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertedNoteToIssueEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CrossReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DemilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DisconnectedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MarkedAsDuplicateEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MentionedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MovedColumnsInProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RemovedFromProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RenamedTitleEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReopenedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TransferredEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnassignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnmarkedAsDuplicateEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnpinnedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnsubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UserBlockedEvent\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineItemsConnection\",\"description\":\"The connection type for IssueTimelineItems.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineItemsEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"filteredCount\",\"description\":\"Identifies the count of items after applying `before` and `after` filters.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"IssueTimelineItems\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageCount\",\"description\":\"Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the timeline was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueTimelineItemsEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"IssueTimelineItems\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"IssueTimelineItemsItemType\",\"description\":\"The possible item types found in a timeline.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ISSUE_COMMENT\",\"description\":\"Represents a comment on an Issue.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CROSS_REFERENCED_EVENT\",\"description\":\"Represents a mention made by one issue or pull request to another.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADDED_TO_PROJECT_EVENT\",\"description\":\"Represents a 'added_to_project' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ASSIGNED_EVENT\",\"description\":\"Represents an 'assigned' event on any assignable object.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CLOSED_EVENT\",\"description\":\"Represents a 'closed' event on any `Closable`.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMMENT_DELETED_EVENT\",\"description\":\"Represents a 'comment_deleted' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONNECTED_EVENT\",\"description\":\"Represents a 'connected' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONVERTED_NOTE_TO_ISSUE_EVENT\",\"description\":\"Represents a 'converted_note_to_issue' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DEMILESTONED_EVENT\",\"description\":\"Represents a 'demilestoned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISCONNECTED_EVENT\",\"description\":\"Represents a 'disconnected' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LABELED_EVENT\",\"description\":\"Represents a 'labeled' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LOCKED_EVENT\",\"description\":\"Represents a 'locked' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MARKED_AS_DUPLICATE_EVENT\",\"description\":\"Represents a 'marked_as_duplicate' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MENTIONED_EVENT\",\"description\":\"Represents a 'mentioned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MILESTONED_EVENT\",\"description\":\"Represents a 'milestoned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MOVED_COLUMNS_IN_PROJECT_EVENT\",\"description\":\"Represents a 'moved_columns_in_project' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PINNED_EVENT\",\"description\":\"Represents a 'pinned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REFERENCED_EVENT\",\"description\":\"Represents a 'referenced' event on a given `ReferencedSubject`.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REMOVED_FROM_PROJECT_EVENT\",\"description\":\"Represents a 'removed_from_project' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RENAMED_TITLE_EVENT\",\"description\":\"Represents a 'renamed' event on a given issue or pull request\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REOPENED_EVENT\",\"description\":\"Represents a 'reopened' event on any `Closable`.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUBSCRIBED_EVENT\",\"description\":\"Represents a 'subscribed' event on a given `Subscribable`.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TRANSFERRED_EVENT\",\"description\":\"Represents a 'transferred' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNASSIGNED_EVENT\",\"description\":\"Represents an 'unassigned' event on any assignable object.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNLABELED_EVENT\",\"description\":\"Represents an 'unlabeled' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNLOCKED_EVENT\",\"description\":\"Represents an 'unlocked' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"USER_BLOCKED_EVENT\",\"description\":\"Represents a 'user_blocked' event on a given user.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNMARKED_AS_DUPLICATE_EVENT\",\"description\":\"Represents an 'unmarked_as_duplicate' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNPINNED_EVENT\",\"description\":\"Represents an 'unpinned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNSUBSCRIBED_EVENT\",\"description\":\"Represents an 'unsubscribed' event on a given `Subscribable`.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"JoinedGitHubContribution\",\"description\":\"Represents a user signing up for a GitHub account.\",\"fields\":[{\"name\":\"isRestricted\",\"description\":\"Whether this contribution is associated with a record you do not have access to. For\\nexample, your own 'first issue' contribution may have been made on a repository you can no\\nlonger access.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"occurredAt\",\"description\":\"When this contribution was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made this contribution.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Contribution\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Label\",\"description\":\"A label for categorizing Issues or Milestones with a given Repository.\",\"fields\":[{\"name\":\"color\",\"description\":\"Identifies the label color.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the label was created.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"A brief description of this label.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDefault\",\"description\":\"Indicates whether or not this is a default label.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issues\",\"description\":\"A list of issues associated with this label.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for issues returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the issues by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"filterBy\",\"description\":\"Filtering options for issues returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueFilters\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"Identifies the label name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequests\",\"description\":\"A list of pull requests associated with this label.\",\"args\":[{\"name\":\"states\",\"description\":\"A list of states to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"headRefName\",\"description\":\"The head ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"baseRefName\",\"description\":\"The base ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pull requests returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this label.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this label.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the label was last updated.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this label.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"LabelConnection\",\"description\":\"The connection type for Label.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"LabelEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Label\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"LabelEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Label\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"LabelOrder\",\"description\":\"Ways in which lists of labels can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order labels by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"LabelOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order labels by the specified field.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"LabelOrderField\",\"description\":\"Properties by which label connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NAME\",\"description\":\"Order labels by name \",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CREATED_AT\",\"description\":\"Order labels by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Labelable\",\"description\":\"An object that can have labels assigned to it.\",\"fields\":[{\"name\":\"labels\",\"description\":\"A list of labels associated with the object.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for labels returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"LabelOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"LabelConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"LabeledEvent\",\"description\":\"Represents a 'labeled' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"label\",\"description\":\"Identifies the label associated with the 'labeled' event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Label\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"labelable\",\"description\":\"Identifies the `Labelable` associated with the event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Labelable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Language\",\"description\":\"Represents a given language found in repositories.\",\"fields\":[{\"name\":\"color\",\"description\":\"The color defined for the current language.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the current language.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"LanguageConnection\",\"description\":\"A list of languages associated with the parent.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"LanguageEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Language\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalSize\",\"description\":\"The total size in bytes of files written in that language.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"LanguageEdge\",\"description\":\"Represents the language of a repository.\",\"fields\":[{\"name\":\"cursor\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Language\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"size\",\"description\":\"The number of bytes of code written in the language.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"LanguageOrder\",\"description\":\"Ordering options for language connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order languages by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"LanguageOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"LanguageOrderField\",\"description\":\"Properties by which language connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"SIZE\",\"description\":\"Order languages by the size of all files containing the language\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"License\",\"description\":\"A repository's open source license\",\"fields\":[{\"name\":\"body\",\"description\":\"The full text of the license\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"conditions\",\"description\":\"The conditions set by the license\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"LicenseRule\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"A human-readable description of the license\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"featured\",\"description\":\"Whether the license should be featured\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hidden\",\"description\":\"Whether the license should be displayed in license pickers\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"implementation\",\"description\":\"Instructions on how to implement the license\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"key\",\"description\":\"The lowercased SPDX ID of the license\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"limitations\",\"description\":\"The limitations set by the license\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"LicenseRule\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The license full name specified by <https://spdx.org/licenses>\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nickname\",\"description\":\"Customary short name if applicable (e.g, GPLv3)\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permissions\",\"description\":\"The permissions set by the license\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"LicenseRule\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pseudoLicense\",\"description\":\"Whether the license is a pseudo-license placeholder (e.g., other, no-license)\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"spdxId\",\"description\":\"Short identifier specified by <https://spdx.org/licenses>\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"URL to the license on <https://choosealicense.com>\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"LicenseRule\",\"description\":\"Describes a License's conditions, permissions, and limitations\",\"fields\":[{\"name\":\"description\",\"description\":\"A description of the rule\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"key\",\"description\":\"The machine-readable rule key\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"label\",\"description\":\"The human-readable rule label\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"LinkRepositoryToProjectInput\",\"description\":\"Autogenerated input type of LinkRepositoryToProject\",\"fields\":null,\"inputFields\":[{\"name\":\"projectId\",\"description\":\"The ID of the Project to link to a Repository\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"repositoryId\",\"description\":\"The ID of the Repository to link to a Project.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"LinkRepositoryToProjectPayload\",\"description\":\"Autogenerated return type of LinkRepositoryToProject\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The linked Project.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The linked Repository.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"LockLockableInput\",\"description\":\"Autogenerated input type of LockLockable\",\"fields\":null,\"inputFields\":[{\"name\":\"lockableId\",\"description\":\"ID of the item to be locked.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"lockReason\",\"description\":\"A reason for why the item will be locked.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"LockReason\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"LockLockablePayload\",\"description\":\"Autogenerated return type of LockLockable\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lockedRecord\",\"description\":\"The item that was locked.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Lockable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"LockReason\",\"description\":\"The possible reasons that an issue or pull request was locked.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OFF_TOPIC\",\"description\":\"The issue or pull request was locked because the conversation was off-topic.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TOO_HEATED\",\"description\":\"The issue or pull request was locked because the conversation was too heated.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RESOLVED\",\"description\":\"The issue or pull request was locked because the conversation was resolved.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SPAM\",\"description\":\"The issue or pull request was locked because the conversation was spam.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Lockable\",\"description\":\"An object that can be locked.\",\"fields\":[{\"name\":\"activeLockReason\",\"description\":\"Reason that the conversation was locked.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"LockReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"locked\",\"description\":\"`true` if the object is locked\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"LockedEvent\",\"description\":\"Represents a 'locked' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lockReason\",\"description\":\"Reason that the conversation was locked (optional).\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"LockReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lockable\",\"description\":\"Object that was locked.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Lockable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Mannequin\",\"description\":\"A placeholder user for attribution of imported data on GitHub.\",\"fields\":[{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the GitHub App's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The mannequin's email on the source instance.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"The username of the actor.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTML path to this resource.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The URL to this resource.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"MarkFileAsViewedInput\",\"description\":\"Autogenerated input type of MarkFileAsViewed\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"The Node ID of the pull request.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"path\",\"description\":\"The path of the file to mark as viewed\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MarkFileAsViewedPayload\",\"description\":\"Autogenerated return type of MarkFileAsViewed\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The updated pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"MarkPullRequestReadyForReviewInput\",\"description\":\"Autogenerated input type of MarkPullRequestReadyForReview\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"ID of the pull request to be marked as ready for review.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MarkPullRequestReadyForReviewPayload\",\"description\":\"Autogenerated return type of MarkPullRequestReadyForReview\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request that is ready for review.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MarkedAsDuplicateEvent\",\"description\":\"Represents a 'marked_as_duplicate' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"canonical\",\"description\":\"The authoritative issue or pull request which has been duplicated by another.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"IssueOrPullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"duplicate\",\"description\":\"The issue or pull request which has been marked as a duplicate of another.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"IssueOrPullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCrossRepository\",\"description\":\"Canonical and duplicate belong to different repositories.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MarketplaceCategory\",\"description\":\"A public description of a Marketplace category.\",\"fields\":[{\"name\":\"description\",\"description\":\"The category's description.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"howItWorks\",\"description\":\"The technical description of how apps listed in this category work with GitHub.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The category's name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"primaryListingCount\",\"description\":\"How many Marketplace listings have this as their primary category.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this Marketplace category.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"secondaryListingCount\",\"description\":\"How many Marketplace listings have this as their secondary category.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"slug\",\"description\":\"The short name of the category used in its URL.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this Marketplace category.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListing\",\"description\":\"A listing in the GitHub integration marketplace.\",\"fields\":[{\"name\":\"app\",\"description\":\"The GitHub App this listing represents.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"App\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"companyUrl\",\"description\":\"URL to the listing owner's company site.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"configurationResourcePath\",\"description\":\"The HTTP path for configuring access to the listing's integration or OAuth app\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"configurationUrl\",\"description\":\"The HTTP URL for configuring access to the listing's integration or OAuth app\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"documentationUrl\",\"description\":\"URL to the listing's documentation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"extendedDescription\",\"description\":\"The listing's detailed description.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"extendedDescriptionHTML\",\"description\":\"The listing's detailed description rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"fullDescription\",\"description\":\"The listing's introductory description.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"fullDescriptionHTML\",\"description\":\"The listing's introductory description rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasPublishedFreeTrialPlans\",\"description\":\"Does this listing have any plans with a free trial?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasTermsOfService\",\"description\":\"Does this listing have a terms of service link?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasVerifiedOwner\",\"description\":\"Whether the creator of the app is a verified org\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"howItWorks\",\"description\":\"A technical description of how this app works with GitHub.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"howItWorksHTML\",\"description\":\"The listing's technical description rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"installationUrl\",\"description\":\"URL to install the product to the viewer's account or organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"installedForViewer\",\"description\":\"Whether this listing's app has been installed for the current viewer\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isArchived\",\"description\":\"Whether this listing has been removed from the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDraft\",\"description\":\"Whether this listing is still an editable draft that has not been submitted for review and is not publicly visible in the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPaid\",\"description\":\"Whether the product this listing represents is available as part of a paid plan.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPublic\",\"description\":\"Whether this listing has been approved for display in the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isRejected\",\"description\":\"Whether this listing has been rejected by GitHub for display in the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUnverified\",\"description\":\"Whether this listing has been approved for unverified display in the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUnverifiedPending\",\"description\":\"Whether this draft listing has been submitted for review for approval to be unverified in the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isVerificationPendingFromDraft\",\"description\":\"Whether this draft listing has been submitted for review from GitHub for approval to be verified in the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isVerificationPendingFromUnverified\",\"description\":\"Whether this unverified listing has been submitted for review from GitHub for approval to be verified in the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isVerified\",\"description\":\"Whether this listing has been approved for verified display in the Marketplace.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"logoBackgroundColor\",\"description\":\"The hex color code, without the leading '#', for the logo background.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"logoUrl\",\"description\":\"URL for the listing's logo image.\",\"args\":[{\"name\":\"size\",\"description\":\"The size in pixels of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"400\"}],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The listing's full name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"normalizedShortDescription\",\"description\":\"The listing's very short description without a trailing period or ampersands.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pricingUrl\",\"description\":\"URL to the listing's detailed pricing.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"primaryCategory\",\"description\":\"The category that best describes the listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceCategory\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"privacyPolicyUrl\",\"description\":\"URL to the listing's privacy policy, may return an empty string for listings that do not require a privacy policy URL.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for the Marketplace listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"screenshotUrls\",\"description\":\"The URLs for the listing's screenshots.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"secondaryCategory\",\"description\":\"An alternate category that describes the listing.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceCategory\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"shortDescription\",\"description\":\"The listing's very short description.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"slug\",\"description\":\"The short name of the listing used in its URL.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"statusUrl\",\"description\":\"URL to the listing's status page.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"supportEmail\",\"description\":\"An email address for support for this listing's app.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"supportUrl\",\"description\":\"Either a URL or an email address for support for this listing's app, may return an empty string for listings that do not require a support URL.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"termsOfServiceUrl\",\"description\":\"URL to the listing's terms of service.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for the Marketplace listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanAddPlans\",\"description\":\"Can the current viewer add plans for this Marketplace listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanApprove\",\"description\":\"Can the current viewer approve this Marketplace listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDelist\",\"description\":\"Can the current viewer delist this Marketplace listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanEdit\",\"description\":\"Can the current viewer edit this Marketplace listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanEditCategories\",\"description\":\"Can the current viewer edit the primary and secondary category of this\\nMarketplace listing.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanEditPlans\",\"description\":\"Can the current viewer edit the plans for this Marketplace listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanRedraft\",\"description\":\"Can the current viewer return this Marketplace listing to draft state\\nso it becomes editable again.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReject\",\"description\":\"Can the current viewer reject this Marketplace listing by returning it to\\nan editable draft state or rejecting it entirely.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanRequestApproval\",\"description\":\"Can the current viewer request this listing be reviewed for display in\\nthe Marketplace as verified.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerHasPurchased\",\"description\":\"Indicates whether the current user has an active subscription to this Marketplace listing.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerHasPurchasedForAllOrganizations\",\"description\":\"Indicates if the current user has purchased a subscription to this Marketplace listing\\nfor all of the organizations the user owns.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerIsListingAdmin\",\"description\":\"Does the current viewer role allow them to administer this Marketplace listing.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListingConnection\",\"description\":\"Look up Marketplace Listings\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListingEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListing\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListingEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListing\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"MemberStatusable\",\"description\":\"Entities that have members who can set status messages.\",\"fields\":[{\"name\":\"memberStatuses\",\"description\":\"Get the status messages members of this entity have set that are either public or visible only to the organization.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for user statuses returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UserStatusOrder\",\"ofType\":null},\"defaultValue\":\"{field: UPDATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserStatusConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposClearAuditEntry\",\"description\":\"Audit log entry for a members_can_delete_repos.clear event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposDisableAuditEntry\",\"description\":\"Audit log entry for a members_can_delete_repos.disable event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposEnableAuditEntry\",\"description\":\"Audit log entry for a members_can_delete_repos.enable event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MentionedEvent\",\"description\":\"Represents a 'mentioned' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"MergeBranchInput\",\"description\":\"Autogenerated input type of MergeBranch\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the Repository containing the base branch that will be modified.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"base\",\"description\":\"The name of the base branch that the provided head will be merged into.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"head\",\"description\":\"The head to merge into the base branch. This can be a branch name or a commit GitObjectID.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"commitMessage\",\"description\":\"Message to use for the merge commit. If omitted, a default will be used.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"authorEmail\",\"description\":\"The email address to associate with this commit.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MergeBranchPayload\",\"description\":\"Autogenerated return type of MergeBranch\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeCommit\",\"description\":\"The resulting merge Commit.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"MergePullRequestInput\",\"description\":\"Autogenerated input type of MergePullRequest\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"ID of the pull request to be merged.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"commitHeadline\",\"description\":\"Commit headline to use for the merge commit; if omitted, a default message will be used.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"commitBody\",\"description\":\"Commit body to use for the merge commit; if omitted, a default message will be used\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"expectedHeadOid\",\"description\":\"OID that the pull request head ref must match to allow merge; if omitted, no check is performed.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"mergeMethod\",\"description\":\"The merge method to use. If omitted, defaults to 'MERGE'\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PullRequestMergeMethod\",\"ofType\":null},\"defaultValue\":\"MERGE\"},{\"name\":\"authorEmail\",\"description\":\"The email address to associate with this merge.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MergePullRequestPayload\",\"description\":\"Autogenerated return type of MergePullRequest\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request that was merged.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"MergeableState\",\"description\":\"Whether or not a PullRequest can be merged.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"MERGEABLE\",\"description\":\"The pull request can be merged.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONFLICTING\",\"description\":\"The pull request cannot be merged due to merge conflicts.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNKNOWN\",\"description\":\"The mergeability of the pull request is still being calculated.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MergedEvent\",\"description\":\"Represents a 'merged' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"Identifies the commit associated with the `merge` event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeRef\",\"description\":\"Identifies the Ref associated with the `merge` event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeRefName\",\"description\":\"Identifies the name of the Ref associated with the `merge` event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this merged event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this merged event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"description\":\"Represents a Milestone object on a given repository.\",\"fields\":[{\"name\":\"closed\",\"description\":\"`true` if the object is closed (definition of closed may depend on type)\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closedAt\",\"description\":\"Identifies the date and time when the object was closed.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"creator\",\"description\":\"Identifies the actor who created the milestone.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"Identifies the description of the milestone.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dueOn\",\"description\":\"Identifies the due date of the milestone.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issues\",\"description\":\"A list of issues associated with the milestone.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for issues returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the issues by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"filterBy\",\"description\":\"Filtering options for issues returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueFilters\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"number\",\"description\":\"Identifies the number of the milestone.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"progressPercentage\",\"description\":\"Identifies the percentage complete for the milestone\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequests\",\"description\":\"A list of pull requests associated with the milestone.\",\"args\":[{\"name\":\"states\",\"description\":\"A list of states to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"headRefName\",\"description\":\"The head ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"baseRefName\",\"description\":\"The base ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pull requests returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this milestone.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this milestone\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"Identifies the state of the milestone.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"MilestoneState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"Identifies the title of the milestone.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this milestone\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Closable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MilestoneConnection\",\"description\":\"The connection type for Milestone.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"MilestoneEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MilestoneEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"MilestoneItem\",\"description\":\"Types that can be inside a Milestone.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"MilestoneOrder\",\"description\":\"Ordering options for milestone connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order milestones by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"MilestoneOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"MilestoneOrderField\",\"description\":\"Properties by which milestone connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"DUE_DATE\",\"description\":\"Order milestones by when they are due.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CREATED_AT\",\"description\":\"Order milestones by when they were created.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UPDATED_AT\",\"description\":\"Order milestones by when they were last updated.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NUMBER\",\"description\":\"Order milestones by their number.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"MilestoneState\",\"description\":\"The possible states of a milestone.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OPEN\",\"description\":\"A milestone that is still open.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CLOSED\",\"description\":\"A milestone that has been closed.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MilestonedEvent\",\"description\":\"Represents a 'milestoned' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"milestoneTitle\",\"description\":\"Identifies the milestone title associated with the 'milestoned' event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"Object referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"MilestoneItem\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Minimizable\",\"description\":\"Entities that can be minimized.\",\"fields\":[{\"name\":\"isMinimized\",\"description\":\"Returns whether or not a comment has been minimized.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"minimizedReason\",\"description\":\"Returns why the comment was minimized.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanMinimize\",\"description\":\"Check if the current viewer can minimize this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"MinimizeCommentInput\",\"description\":\"Autogenerated input type of MinimizeComment\",\"fields\":null,\"inputFields\":[{\"name\":\"subjectId\",\"description\":\"The Node ID of the subject to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"classifier\",\"description\":\"The classification of comment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ReportedContentClassifiers\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MinimizeCommentPayload\",\"description\":\"Autogenerated return type of MinimizeComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"minimizedComment\",\"description\":\"The comment that was minimized.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Minimizable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"MoveProjectCardInput\",\"description\":\"Autogenerated input type of MoveProjectCard\",\"fields\":null,\"inputFields\":[{\"name\":\"cardId\",\"description\":\"The id of the card to move.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"columnId\",\"description\":\"The id of the column to move it into.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"afterCardId\",\"description\":\"Place the new card after the card with this id. Pass null to place it at the top.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MoveProjectCardPayload\",\"description\":\"Autogenerated return type of MoveProjectCard\",\"fields\":[{\"name\":\"cardEdge\",\"description\":\"The new edge of the moved card.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCardEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"MoveProjectColumnInput\",\"description\":\"Autogenerated input type of MoveProjectColumn\",\"fields\":null,\"inputFields\":[{\"name\":\"columnId\",\"description\":\"The id of the column to move.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"afterColumnId\",\"description\":\"Place the new column after the column with this id. Pass null to place it at the front.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MoveProjectColumnPayload\",\"description\":\"Autogenerated return type of MoveProjectColumn\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"columnEdge\",\"description\":\"The new edge of the moved column.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumnEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"MovedColumnsInProjectEvent\",\"description\":\"Represents a 'moved_columns_in_project' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Mutation\",\"description\":\"The root query for implementing GraphQL mutations.\",\"fields\":[{\"name\":\"acceptEnterpriseAdministratorInvitation\",\"description\":\"Accepts a pending invitation for a user to become an administrator of an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AcceptEnterpriseAdministratorInvitation\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AcceptEnterpriseAdministratorInvitationInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AcceptEnterpriseAdministratorInvitationPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"acceptTopicSuggestion\",\"description\":\"Applies a suggested topic to the repository.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AcceptTopicSuggestion\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AcceptTopicSuggestionInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AcceptTopicSuggestionPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addAssigneesToAssignable\",\"description\":\"Adds assignees to an assignable object.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddAssigneesToAssignable\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddAssigneesToAssignableInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddAssigneesToAssignablePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addComment\",\"description\":\"Adds a comment to an Issue or Pull Request.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addEnterpriseSupportEntitlement\",\"description\":\"Adds a support entitlement to an enterprise member.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddEnterpriseSupportEntitlement\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddEnterpriseSupportEntitlementInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddEnterpriseSupportEntitlementPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addLabelsToLabelable\",\"description\":\"Adds labels to a labelable object.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddLabelsToLabelable\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddLabelsToLabelableInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddLabelsToLabelablePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addProjectCard\",\"description\":\"Adds a card to a ProjectColumn. Either `contentId` or `note` must be provided but **not** both.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddProjectCard\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddProjectCardInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddProjectCardPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addProjectColumn\",\"description\":\"Adds a column to a Project.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddProjectColumn\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddProjectColumnInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddProjectColumnPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addPullRequestReview\",\"description\":\"Adds a review to a Pull Request.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddPullRequestReview\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddPullRequestReviewInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddPullRequestReviewPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addPullRequestReviewComment\",\"description\":\"Adds a comment to a review.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddPullRequestReviewComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddPullRequestReviewCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddPullRequestReviewCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addPullRequestReviewThread\",\"description\":\"Adds a new thread to a pending Pull Request Review.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddPullRequestReviewThread\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddPullRequestReviewThreadInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddPullRequestReviewThreadPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addReaction\",\"description\":\"Adds a reaction to a subject.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddReaction\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddReactionInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddReactionPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addStar\",\"description\":\"Adds a star to a Starrable.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddStar\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddStarInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddStarPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"addVerifiableDomain\",\"description\":\"Adds a verifiable domain to an owning account.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for AddVerifiableDomain\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AddVerifiableDomainInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AddVerifiableDomainPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"approveVerifiableDomain\",\"description\":\"Approve a verifiable domain for notification delivery.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ApproveVerifiableDomain\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ApproveVerifiableDomainInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ApproveVerifiableDomainPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"archiveRepository\",\"description\":\"Marks a repository as archived.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ArchiveRepository\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ArchiveRepositoryInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ArchiveRepositoryPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"cancelEnterpriseAdminInvitation\",\"description\":\"Cancels a pending invitation for an administrator to join an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CancelEnterpriseAdminInvitation\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CancelEnterpriseAdminInvitationInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CancelEnterpriseAdminInvitationPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"changeUserStatus\",\"description\":\"Update your status on GitHub.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ChangeUserStatus\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ChangeUserStatusInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ChangeUserStatusPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clearLabelsFromLabelable\",\"description\":\"Clears all labels from a labelable object.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ClearLabelsFromLabelable\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ClearLabelsFromLabelableInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ClearLabelsFromLabelablePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"cloneProject\",\"description\":\"Creates a new project by cloning configuration from an existing project.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CloneProject\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CloneProjectInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CloneProjectPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"cloneTemplateRepository\",\"description\":\"Create a new repository with the same files and directory structure as a template repository.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CloneTemplateRepository\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CloneTemplateRepositoryInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CloneTemplateRepositoryPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closeIssue\",\"description\":\"Close an issue.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CloseIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CloseIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CloseIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closePullRequest\",\"description\":\"Close a pull request.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ClosePullRequest\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ClosePullRequestInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ClosePullRequestPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"convertProjectCardNoteToIssue\",\"description\":\"Convert a project note card to one associated with a newly created issue.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ConvertProjectCardNoteToIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ConvertProjectCardNoteToIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ConvertProjectCardNoteToIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createBranchProtectionRule\",\"description\":\"Create a new branch protection rule\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateBranchProtectionRule\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateBranchProtectionRuleInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateBranchProtectionRulePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createCheckRun\",\"description\":\"Create a check run.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateCheckRun\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateCheckRunInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateCheckRunPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createCheckSuite\",\"description\":\"Create a check suite\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateCheckSuite\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateCheckSuiteInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateCheckSuitePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createEnterpriseOrganization\",\"description\":\"Creates an organization as part of an enterprise account.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateEnterpriseOrganization\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateEnterpriseOrganizationInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateEnterpriseOrganizationPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createIpAllowListEntry\",\"description\":\"Creates a new IP allow list entry.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateIpAllowListEntry\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateIpAllowListEntryInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateIpAllowListEntryPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createIssue\",\"description\":\"Creates a new issue.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createProject\",\"description\":\"Creates a new project.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateProject\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateProjectInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateProjectPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createPullRequest\",\"description\":\"Create a new pull request\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreatePullRequest\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreatePullRequestInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreatePullRequestPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createRef\",\"description\":\"Create a new Git Ref.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateRef\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateRefInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateRefPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createRepository\",\"description\":\"Create a new repository.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateRepository\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateRepositoryInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateRepositoryPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createTeamDiscussion\",\"description\":\"Creates a new team discussion.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateTeamDiscussion\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateTeamDiscussionInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateTeamDiscussionPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createTeamDiscussionComment\",\"description\":\"Creates a new team discussion comment.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for CreateTeamDiscussionComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CreateTeamDiscussionCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CreateTeamDiscussionCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"declineTopicSuggestion\",\"description\":\"Rejects a suggested topic for the repository.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeclineTopicSuggestion\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeclineTopicSuggestionInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeclineTopicSuggestionPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteBranchProtectionRule\",\"description\":\"Delete a branch protection rule\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteBranchProtectionRule\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteBranchProtectionRuleInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteBranchProtectionRulePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteDeployment\",\"description\":\"Deletes a deployment.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteDeployment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteDeploymentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteDeploymentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteIpAllowListEntry\",\"description\":\"Deletes an IP allow list entry.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteIpAllowListEntry\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteIpAllowListEntryInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteIpAllowListEntryPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteIssue\",\"description\":\"Deletes an Issue object.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteIssueComment\",\"description\":\"Deletes an IssueComment object.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteIssueComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteIssueCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteIssueCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteProject\",\"description\":\"Deletes a project.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteProject\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteProjectInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteProjectPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteProjectCard\",\"description\":\"Deletes a project card.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteProjectCard\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteProjectCardInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteProjectCardPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteProjectColumn\",\"description\":\"Deletes a project column.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteProjectColumn\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteProjectColumnInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteProjectColumnPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletePullRequestReview\",\"description\":\"Deletes a pull request review.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeletePullRequestReview\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeletePullRequestReviewInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeletePullRequestReviewPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletePullRequestReviewComment\",\"description\":\"Deletes a pull request review comment.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeletePullRequestReviewComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeletePullRequestReviewCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeletePullRequestReviewCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteRef\",\"description\":\"Delete a Git Ref.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteRef\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteRefInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteRefPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteTeamDiscussion\",\"description\":\"Deletes a team discussion.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteTeamDiscussion\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteTeamDiscussionInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteTeamDiscussionPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteTeamDiscussionComment\",\"description\":\"Deletes a team discussion comment.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteTeamDiscussionComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteTeamDiscussionCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteTeamDiscussionCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteVerifiableDomain\",\"description\":\"Deletes a verifiable domain.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DeleteVerifiableDomain\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeleteVerifiableDomainInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DeleteVerifiableDomainPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"disablePullRequestAutoMerge\",\"description\":\"Disable auto merge on the given pull request\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DisablePullRequestAutoMerge\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DisablePullRequestAutoMergeInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DisablePullRequestAutoMergePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dismissPullRequestReview\",\"description\":\"Dismisses an approved or rejected pull request review.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for DismissPullRequestReview\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DismissPullRequestReviewInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"DismissPullRequestReviewPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enablePullRequestAutoMerge\",\"description\":\"Enable the default auto-merge on a pull request.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for EnablePullRequestAutoMerge\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"EnablePullRequestAutoMergeInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnablePullRequestAutoMergePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"followUser\",\"description\":\"Follow a user.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for FollowUser\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"FollowUserInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"FollowUserPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"inviteEnterpriseAdmin\",\"description\":\"Invite someone to become an administrator of the enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for InviteEnterpriseAdmin\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"InviteEnterpriseAdminInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"InviteEnterpriseAdminPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"linkRepositoryToProject\",\"description\":\"Creates a repository link for a project.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for LinkRepositoryToProject\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"LinkRepositoryToProjectInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"LinkRepositoryToProjectPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lockLockable\",\"description\":\"Lock a lockable object\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for LockLockable\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"LockLockableInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"LockLockablePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"markFileAsViewed\",\"description\":\"Mark a pull request file as viewed\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for MarkFileAsViewed\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"MarkFileAsViewedInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MarkFileAsViewedPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"markPullRequestReadyForReview\",\"description\":\"Marks a pull request ready for review.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for MarkPullRequestReadyForReview\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"MarkPullRequestReadyForReviewInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MarkPullRequestReadyForReviewPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeBranch\",\"description\":\"Merge a head into a branch.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for MergeBranch\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"MergeBranchInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MergeBranchPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergePullRequest\",\"description\":\"Merge a pull request.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for MergePullRequest\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"MergePullRequestInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MergePullRequestPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"minimizeComment\",\"description\":\"Minimizes a comment on an Issue, Commit, Pull Request, or Gist\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for MinimizeComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"MinimizeCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MinimizeCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"moveProjectCard\",\"description\":\"Moves a project card to another place.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for MoveProjectCard\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"MoveProjectCardInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MoveProjectCardPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"moveProjectColumn\",\"description\":\"Moves a project column to another place.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for MoveProjectColumn\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"MoveProjectColumnInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MoveProjectColumnPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinIssue\",\"description\":\"Pin an issue to a repository\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for PinIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"PinIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PinIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"regenerateEnterpriseIdentityProviderRecoveryCodes\",\"description\":\"Regenerates the identity provider recovery codes for an enterprise\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RegenerateEnterpriseIdentityProviderRecoveryCodes\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RegenerateEnterpriseIdentityProviderRecoveryCodesInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RegenerateEnterpriseIdentityProviderRecoveryCodesPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"regenerateVerifiableDomainToken\",\"description\":\"Regenerates a verifiable domain's verification token.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RegenerateVerifiableDomainToken\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RegenerateVerifiableDomainTokenInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RegenerateVerifiableDomainTokenPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeAssigneesFromAssignable\",\"description\":\"Removes assignees from an assignable object.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveAssigneesFromAssignable\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveAssigneesFromAssignableInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveAssigneesFromAssignablePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeEnterpriseAdmin\",\"description\":\"Removes an administrator from the enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveEnterpriseAdmin\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveEnterpriseAdminInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveEnterpriseAdminPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeEnterpriseIdentityProvider\",\"description\":\"Removes the identity provider from an enterprise\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveEnterpriseIdentityProvider\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveEnterpriseIdentityProviderInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveEnterpriseIdentityProviderPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeEnterpriseOrganization\",\"description\":\"Removes an organization from the enterprise\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveEnterpriseOrganization\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveEnterpriseOrganizationInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveEnterpriseOrganizationPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeEnterpriseSupportEntitlement\",\"description\":\"Removes a support entitlement from an enterprise member.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveEnterpriseSupportEntitlement\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveEnterpriseSupportEntitlementInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveEnterpriseSupportEntitlementPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeLabelsFromLabelable\",\"description\":\"Removes labels from a Labelable object.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveLabelsFromLabelable\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveLabelsFromLabelableInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveLabelsFromLabelablePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeOutsideCollaborator\",\"description\":\"Removes outside collaborator from all repositories in an organization.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveOutsideCollaborator\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveOutsideCollaboratorInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveOutsideCollaboratorPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeReaction\",\"description\":\"Removes a reaction from a subject.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveReaction\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveReactionInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveReactionPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removeStar\",\"description\":\"Removes a star from a Starrable.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RemoveStar\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveStarInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RemoveStarPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reopenIssue\",\"description\":\"Reopen a issue.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ReopenIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReopenIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ReopenIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reopenPullRequest\",\"description\":\"Reopen a pull request.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ReopenPullRequest\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReopenPullRequestInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ReopenPullRequestPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requestReviews\",\"description\":\"Set review requests on a pull request.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RequestReviews\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RequestReviewsInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RequestReviewsPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"rerequestCheckSuite\",\"description\":\"Rerequests an existing check suite.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for RerequestCheckSuite\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RerequestCheckSuiteInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RerequestCheckSuitePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resolveReviewThread\",\"description\":\"Marks a review thread as resolved.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for ResolveReviewThread\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ResolveReviewThreadInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ResolveReviewThreadPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"setEnterpriseIdentityProvider\",\"description\":\"Creates or updates the identity provider for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for SetEnterpriseIdentityProvider\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SetEnterpriseIdentityProviderInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SetEnterpriseIdentityProviderPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"setOrganizationInteractionLimit\",\"description\":\"Set an organization level interaction limit for an organization's public repositories.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for SetOrganizationInteractionLimit\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SetOrganizationInteractionLimitInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SetOrganizationInteractionLimitPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"setRepositoryInteractionLimit\",\"description\":\"Sets an interaction limit setting for a repository.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for SetRepositoryInteractionLimit\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SetRepositoryInteractionLimitInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SetRepositoryInteractionLimitPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"setUserInteractionLimit\",\"description\":\"Set a user level interaction limit for an user's public repositories.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for SetUserInteractionLimit\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SetUserInteractionLimitInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SetUserInteractionLimitPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"submitPullRequestReview\",\"description\":\"Submits a pending pull request review.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for SubmitPullRequestReview\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SubmitPullRequestReviewInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SubmitPullRequestReviewPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"transferIssue\",\"description\":\"Transfer an issue to a different repository\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for TransferIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"TransferIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TransferIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unarchiveRepository\",\"description\":\"Unarchives a repository.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnarchiveRepository\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnarchiveRepositoryInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnarchiveRepositoryPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unfollowUser\",\"description\":\"Unfollow a user.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnfollowUser\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnfollowUserInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnfollowUserPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unlinkRepositoryFromProject\",\"description\":\"Deletes a repository link from a project.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnlinkRepositoryFromProject\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnlinkRepositoryFromProjectInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnlinkRepositoryFromProjectPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unlockLockable\",\"description\":\"Unlock a lockable object\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnlockLockable\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnlockLockableInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnlockLockablePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unmarkFileAsViewed\",\"description\":\"Unmark a pull request file as viewed\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnmarkFileAsViewed\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnmarkFileAsViewedInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnmarkFileAsViewedPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unmarkIssueAsDuplicate\",\"description\":\"Unmark an issue as a duplicate of another issue.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnmarkIssueAsDuplicate\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnmarkIssueAsDuplicateInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnmarkIssueAsDuplicatePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unminimizeComment\",\"description\":\"Unminimizes a comment on an Issue, Commit, Pull Request, or Gist\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnminimizeComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnminimizeCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnminimizeCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unpinIssue\",\"description\":\"Unpin a pinned issue from a repository\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnpinIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnpinIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnpinIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unresolveReviewThread\",\"description\":\"Marks a review thread as unresolved.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UnresolveReviewThread\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnresolveReviewThreadInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UnresolveReviewThreadPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateBranchProtectionRule\",\"description\":\"Create a new branch protection rule\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateBranchProtectionRule\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateBranchProtectionRuleInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateBranchProtectionRulePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateCheckRun\",\"description\":\"Update a check run\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateCheckRun\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateCheckRunInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateCheckRunPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateCheckSuitePreferences\",\"description\":\"Modifies the settings of an existing check suite\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateCheckSuitePreferences\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateCheckSuitePreferencesInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateCheckSuitePreferencesPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseAdministratorRole\",\"description\":\"Updates the role of an enterprise administrator.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseAdministratorRole\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseAdministratorRoleInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseAdministratorRolePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseAllowPrivateRepositoryForkingSetting\",\"description\":\"Sets whether private repository forks are enabled for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseAllowPrivateRepositoryForkingSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseAllowPrivateRepositoryForkingSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseAllowPrivateRepositoryForkingSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseDefaultRepositoryPermissionSetting\",\"description\":\"Sets the default repository permission for organizations in an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseDefaultRepositoryPermissionSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseDefaultRepositoryPermissionSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseDefaultRepositoryPermissionSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseMembersCanChangeRepositoryVisibilitySetting\",\"description\":\"Sets whether organization members with admin permissions on a repository can change repository visibility.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseMembersCanCreateRepositoriesSetting\",\"description\":\"Sets the members can create repositories setting for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseMembersCanCreateRepositoriesSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanCreateRepositoriesSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanCreateRepositoriesSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseMembersCanDeleteIssuesSetting\",\"description\":\"Sets the members can delete issues setting for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseMembersCanDeleteIssuesSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanDeleteIssuesSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanDeleteIssuesSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseMembersCanDeleteRepositoriesSetting\",\"description\":\"Sets the members can delete repositories setting for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseMembersCanDeleteRepositoriesSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanDeleteRepositoriesSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanDeleteRepositoriesSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseMembersCanInviteCollaboratorsSetting\",\"description\":\"Sets whether members can invite collaborators are enabled for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseMembersCanInviteCollaboratorsSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanInviteCollaboratorsSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanInviteCollaboratorsSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseMembersCanMakePurchasesSetting\",\"description\":\"Sets whether or not an organization admin can make purchases.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseMembersCanMakePurchasesSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanMakePurchasesSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanMakePurchasesSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseMembersCanUpdateProtectedBranchesSetting\",\"description\":\"Sets the members can update protected branches setting for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseMembersCanUpdateProtectedBranchesSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseMembersCanViewDependencyInsightsSetting\",\"description\":\"Sets the members can view dependency insights for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseMembersCanViewDependencyInsightsSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanViewDependencyInsightsSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanViewDependencyInsightsSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseOrganizationProjectsSetting\",\"description\":\"Sets whether organization projects are enabled for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseOrganizationProjectsSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseOrganizationProjectsSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseOrganizationProjectsSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseProfile\",\"description\":\"Updates an enterprise's profile.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseProfile\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseProfileInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseProfilePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseRepositoryProjectsSetting\",\"description\":\"Sets whether repository projects are enabled for a enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseRepositoryProjectsSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseRepositoryProjectsSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseRepositoryProjectsSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseTeamDiscussionsSetting\",\"description\":\"Sets whether team discussions are enabled for an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseTeamDiscussionsSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseTeamDiscussionsSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseTeamDiscussionsSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateEnterpriseTwoFactorAuthenticationRequiredSetting\",\"description\":\"Sets whether two factor authentication is required for all users in an enterprise.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateEnterpriseTwoFactorAuthenticationRequiredSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseTwoFactorAuthenticationRequiredSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseTwoFactorAuthenticationRequiredSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateIpAllowListEnabledSetting\",\"description\":\"Sets whether an IP allow list is enabled on an owner.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateIpAllowListEnabledSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateIpAllowListEnabledSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateIpAllowListEnabledSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateIpAllowListEntry\",\"description\":\"Updates an IP allow list entry.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateIpAllowListEntry\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateIpAllowListEntryInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateIpAllowListEntryPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateIssue\",\"description\":\"Updates an Issue.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateIssue\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateIssueInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateIssuePayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateIssueComment\",\"description\":\"Updates an IssueComment object.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateIssueComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateIssueCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateIssueCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateNotificationRestrictionSetting\",\"description\":\"Update the setting to restrict notifications to only verified domains available to an owner.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateNotificationRestrictionSetting\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateNotificationRestrictionSettingInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateNotificationRestrictionSettingPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateProject\",\"description\":\"Updates an existing project.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateProject\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateProjectInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateProjectPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateProjectCard\",\"description\":\"Updates an existing project card.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateProjectCard\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateProjectCardInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateProjectCardPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateProjectColumn\",\"description\":\"Updates an existing project column.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateProjectColumn\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateProjectColumnInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateProjectColumnPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatePullRequest\",\"description\":\"Update a pull request\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdatePullRequest\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdatePullRequestInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdatePullRequestPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatePullRequestReview\",\"description\":\"Updates the body of a pull request review.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdatePullRequestReview\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdatePullRequestReviewInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdatePullRequestReviewPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatePullRequestReviewComment\",\"description\":\"Updates a pull request review comment.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdatePullRequestReviewComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdatePullRequestReviewCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdatePullRequestReviewCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateRef\",\"description\":\"Update a Git Ref.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateRef\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateRefInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateRefPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateRepository\",\"description\":\"Update information about a repository.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateRepository\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateRepositoryInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateRepositoryPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateSubscription\",\"description\":\"Updates the state for subscribable subjects.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateSubscription\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateSubscriptionInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateSubscriptionPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateTeamDiscussion\",\"description\":\"Updates a team discussion.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateTeamDiscussion\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateTeamDiscussionInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateTeamDiscussionPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateTeamDiscussionComment\",\"description\":\"Updates a discussion comment.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateTeamDiscussionComment\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateTeamDiscussionCommentInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateTeamDiscussionCommentPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updateTopics\",\"description\":\"Replaces the repository's topics with the given topics.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for UpdateTopics\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateTopicsInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UpdateTopicsPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"verifyVerifiableDomain\",\"description\":\"Verify that a verifiable domain has the expected DNS record.\",\"args\":[{\"name\":\"input\",\"description\":\"Parameters for VerifyVerifiableDomain\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"VerifyVerifiableDomainInput\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"VerifyVerifiableDomainPayload\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"description\":\"An object with an ID.\",\"fields\":[{\"name\":\"id\",\"description\":\"ID of the object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"AddedToProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"App\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AssignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoMergeDisabledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoMergeEnabledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoRebaseEnabledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoSquashEnabledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutomaticBaseChangeFailedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutomaticBaseChangeSucceededEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefChangedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefForcePushedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Blob\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Bot\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CWE\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckSuite\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ClosedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CodeOfConduct\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CommentDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitCommentThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConnectedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertToDraftEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertedNoteToIssueEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CrossReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DemilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DeployKey\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DeployedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Deployment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentEnvironmentChangedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentStatus\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DisconnectedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseIdentityProvider\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseRepositoryInfo\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerInstallation\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccount\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountEmail\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseServerUserAccountsUpload\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"EnterpriseUserAccount\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentity\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefForcePushedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefRestoredEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Label\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Language\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"License\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Mannequin\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MarkedAsDuplicateEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MarketplaceCategory\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListing\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposClearAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MentionedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MergedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MovedColumnsInProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OauthApplicationCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddBillingManagerAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgBlockUserAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableOauthAppRestrictionsAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableSamlAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableTwoFactorRequirementAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableOauthAppRestrictionsAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableSamlAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableTwoFactorRequirementAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteToBusinessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessApprovedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessDeniedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessRequestedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveBillingManagerAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUnblockUserAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateDefaultRepositoryPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryCreationPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationIdentityProvider\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitation\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Package\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageFile\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageTag\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageVersion\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnedIssue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectCard\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectColumn\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PublicKey\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommitCommentThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Push\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PushAllowance\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Reaction\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReadyForReviewEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Release\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReleaseAsset\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RemovedFromProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RenamedTitleEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReopenedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoArchivedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoChangeMergeSettingAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigLockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigUnlockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoDestroyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryInvitation\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopic\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlert\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissalAllowance\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestRemovedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SavedReply\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisory\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorsListing\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorsTier\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Sponsorship\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Status\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"StatusCheckRollup\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"StatusContext\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Tag\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddRepositoryAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamChangeParentTeamAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveRepositoryAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TransferredEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Tree\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnassignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnmarkedAsDuplicateEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnpinnedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnsubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UserBlockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UserContentEdit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UserStatus\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomain\",\"ofType\":null}]},{\"kind\":\"ENUM\",\"name\":\"NotificationRestrictionSettingValue\",\"description\":\"The possible values for the notification restriction setting.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ENABLED\",\"description\":\"The setting is enabled for the owner.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISABLED\",\"description\":\"The setting is disabled for the owner.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"OauthApplicationAuditEntryData\",\"description\":\"Metadata for an audit entry with action oauth_application.*\",\"fields\":[{\"name\":\"oauthApplicationName\",\"description\":\"The name of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationResourcePath\",\"description\":\"The HTTP path for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationUrl\",\"description\":\"The HTTP URL for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"OauthApplicationCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessApprovedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessDeniedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessRequestedAuditEntry\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"OauthApplicationCreateAuditEntry\",\"description\":\"Audit log entry for a oauth_application.create event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"applicationUrl\",\"description\":\"The application URL of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"callbackUrl\",\"description\":\"The callback URL of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationName\",\"description\":\"The name of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationResourcePath\",\"description\":\"The HTTP path for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationUrl\",\"description\":\"The HTTP URL for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"rateLimit\",\"description\":\"The rate limit of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The state of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OauthApplicationCreateAuditEntryState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OauthApplicationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OauthApplicationCreateAuditEntryState\",\"description\":\"The state of an OAuth Application when it was created.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ACTIVE\",\"description\":\"The OAuth Application was active and allowed to have OAuth Accesses.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUSPENDED\",\"description\":\"The OAuth Application was suspended from generating OAuth Accesses due to abuse or security concerns.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PENDING_DELETION\",\"description\":\"The OAuth Application was in the process of being deleted.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"description\":\"The corresponding operation type for the action\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ACCESS\",\"description\":\"An existing resource was accessed\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTHENTICATION\",\"description\":\"A resource performed an authentication event\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CREATE\",\"description\":\"A new resource was created\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MODIFY\",\"description\":\"An existing resource was modified\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REMOVE\",\"description\":\"An existing resource was removed\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RESTORE\",\"description\":\"An existing resource was restored\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TRANSFER\",\"description\":\"An existing resource was transferred between multiple resources\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"description\":\"Possible directions in which to order a list of items when provided an `orderBy` argument.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ASC\",\"description\":\"Specifies an ascending order for a given `orderBy` argument.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DESC\",\"description\":\"Specifies a descending order for a given `orderBy` argument.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddBillingManagerAuditEntry\",\"description\":\"Audit log entry for a org.add_billing_manager\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitationEmail\",\"description\":\"The email address used to invite a billing manager for the organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddMemberAuditEntry\",\"description\":\"Audit log entry for a org.add_member\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permission\",\"description\":\"The permission level of the member added to the organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgAddMemberAuditEntryPermission\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgAddMemberAuditEntryPermission\",\"description\":\"The permissions available to members on an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"READ\",\"description\":\"Can read and clone repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADMIN\",\"description\":\"Can read, clone, push, and add collaborators to repositories.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgBlockUserAuditEntry\",\"description\":\"Audit log entry for a org.block_user\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockedUser\",\"description\":\"The blocked user.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockedUserName\",\"description\":\"The username of the blocked user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockedUserResourcePath\",\"description\":\"The HTTP path for the blocked user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockedUserUrl\",\"description\":\"The HTTP URL for the blocked user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigDisableCollaboratorsOnlyAuditEntry\",\"description\":\"Audit log entry for a org.config.disable_collaborators_only event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigEnableCollaboratorsOnlyAuditEntry\",\"description\":\"Audit log entry for a org.config.enable_collaborators_only event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgCreateAuditEntry\",\"description\":\"Audit log entry for a org.create event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"billingPlan\",\"description\":\"The billing plan for the Organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgCreateAuditEntryBillingPlan\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgCreateAuditEntryBillingPlan\",\"description\":\"The billing plans available for organizations.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"FREE\",\"description\":\"Free Plan\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BUSINESS\",\"description\":\"Team Plan\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BUSINESS_PLUS\",\"description\":\"Enterprise Cloud Plan\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNLIMITED\",\"description\":\"Legacy Unlimited Plan\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TIERED_PER_SEAT\",\"description\":\"Tiered Per Seat Plan\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableOauthAppRestrictionsAuditEntry\",\"description\":\"Audit log entry for a org.disable_oauth_app_restrictions event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableSamlAuditEntry\",\"description\":\"Audit log entry for a org.disable_saml event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"digestMethodUrl\",\"description\":\"The SAML provider's digest algorithm URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issuerUrl\",\"description\":\"The SAML provider's issuer URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signatureMethodUrl\",\"description\":\"The SAML provider's signature algorithm URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"singleSignOnUrl\",\"description\":\"The SAML provider's single sign-on URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableTwoFactorRequirementAuditEntry\",\"description\":\"Audit log entry for a org.disable_two_factor_requirement event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableOauthAppRestrictionsAuditEntry\",\"description\":\"Audit log entry for a org.enable_oauth_app_restrictions event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableSamlAuditEntry\",\"description\":\"Audit log entry for a org.enable_saml event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"digestMethodUrl\",\"description\":\"The SAML provider's digest algorithm URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issuerUrl\",\"description\":\"The SAML provider's issuer URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signatureMethodUrl\",\"description\":\"The SAML provider's signature algorithm URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"singleSignOnUrl\",\"description\":\"The SAML provider's single sign-on URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableTwoFactorRequirementAuditEntry\",\"description\":\"Audit log entry for a org.enable_two_factor_requirement event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteMemberAuditEntry\",\"description\":\"Audit log entry for a org.invite_member event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The email address of the organization invitation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationInvitation\",\"description\":\"The organization invitation.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteToBusinessAuditEntry\",\"description\":\"Audit log entry for a org.invite_to_business event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessApprovedAuditEntry\",\"description\":\"Audit log entry for a org.oauth_app_access_approved event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationName\",\"description\":\"The name of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationResourcePath\",\"description\":\"The HTTP path for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationUrl\",\"description\":\"The HTTP URL for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OauthApplicationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessDeniedAuditEntry\",\"description\":\"Audit log entry for a org.oauth_app_access_denied event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationName\",\"description\":\"The name of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationResourcePath\",\"description\":\"The HTTP path for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationUrl\",\"description\":\"The HTTP URL for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OauthApplicationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessRequestedAuditEntry\",\"description\":\"Audit log entry for a org.oauth_app_access_requested event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationName\",\"description\":\"The name of the OAuth Application.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationResourcePath\",\"description\":\"The HTTP path for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oauthApplicationUrl\",\"description\":\"The HTTP URL for the OAuth Application\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OauthApplicationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveBillingManagerAuditEntry\",\"description\":\"Audit log entry for a org.remove_billing_manager event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reason\",\"description\":\"The reason for the billing manager being removed.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgRemoveBillingManagerAuditEntryReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgRemoveBillingManagerAuditEntryReason\",\"description\":\"The reason a billing manager was removed from an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE\",\"description\":\"The organization required 2FA of its billing managers and this user did not have 2FA enabled.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SAML_EXTERNAL_IDENTITY_MISSING\",\"description\":\"SAML external identity missing\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY\",\"description\":\"SAML SSO enforcement requires an external identity\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveMemberAuditEntry\",\"description\":\"Audit log entry for a org.remove_member event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membershipTypes\",\"description\":\"The types of membership the member has with the organization.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrgRemoveMemberAuditEntryMembershipType\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reason\",\"description\":\"The reason for the member being removed.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgRemoveMemberAuditEntryReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgRemoveMemberAuditEntryMembershipType\",\"description\":\"The type of membership a user has with an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"DIRECT_MEMBER\",\"description\":\"A direct member is a user that is a member of the Organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADMIN\",\"description\":\"Organization administrators have full access and can change several settings, including the names of repositories that belong to the Organization and Owners team membership. In addition, organization admins can delete the organization and all of its repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BILLING_MANAGER\",\"description\":\"A billing manager is a user who manages the billing settings for the Organization, such as updating payment information.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNAFFILIATED\",\"description\":\"An unaffiliated collaborator is a person who is not a member of the Organization and does not have access to any repositories in the Organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OUTSIDE_COLLABORATOR\",\"description\":\"An outside collaborator is a person who isn't explicitly a member of the Organization, but who has Read, Write, or Admin permissions to one or more repositories in the organization.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgRemoveMemberAuditEntryReason\",\"description\":\"The reason a member was removed from an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE\",\"description\":\"The organization required 2FA of its billing managers and this user did not have 2FA enabled.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SAML_EXTERNAL_IDENTITY_MISSING\",\"description\":\"SAML external identity missing\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY\",\"description\":\"SAML SSO enforcement requires an external identity\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"USER_ACCOUNT_DELETED\",\"description\":\"User account has been deleted\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TWO_FACTOR_ACCOUNT_RECOVERY\",\"description\":\"User was removed from organization during account recovery\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntry\",\"description\":\"Audit log entry for a org.remove_outside_collaborator event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membershipTypes\",\"description\":\"The types of membership the outside collaborator has with the organization.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntryMembershipType\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reason\",\"description\":\"The reason for the outside collaborator being removed from the Organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntryReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntryMembershipType\",\"description\":\"The type of membership a user has with an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OUTSIDE_COLLABORATOR\",\"description\":\"An outside collaborator is a person who isn't explicitly a member of the Organization, but who has Read, Write, or Admin permissions to one or more repositories in the organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNAFFILIATED\",\"description\":\"An unaffiliated collaborator is a person who is not a member of the Organization and does not have access to any repositories in the organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BILLING_MANAGER\",\"description\":\"A billing manager is a user who manages the billing settings for the Organization, such as updating payment information.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntryReason\",\"description\":\"The reason an outside collaborator was removed from an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE\",\"description\":\"The organization required 2FA of its billing managers and this user did not have 2FA enabled.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SAML_EXTERNAL_IDENTITY_MISSING\",\"description\":\"SAML external identity missing\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberAuditEntry\",\"description\":\"Audit log entry for a org.restore_member event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restoredCustomEmailRoutingsCount\",\"description\":\"The number of custom email routings for the restored member.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restoredIssueAssignmentsCount\",\"description\":\"The number of issue assignments for the restored member.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restoredMemberships\",\"description\":\"Restored organization membership objects.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"OrgRestoreMemberAuditEntryMembership\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restoredMembershipsCount\",\"description\":\"The number of restored memberships.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restoredRepositoriesCount\",\"description\":\"The number of repositories of the restored member.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restoredRepositoryStarsCount\",\"description\":\"The number of starred repositories for the restored member.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"restoredRepositoryWatchesCount\",\"description\":\"The number of watched repositories for the restored member.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"OrgRestoreMemberAuditEntryMembership\",\"description\":\"Types of memberships that can be restored for an Organization member.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipOrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipRepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipTeamAuditEntryData\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipOrganizationAuditEntryData\",\"description\":\"Metadata for an organization membership for org.restore_member actions\",\"fields\":[{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipRepositoryAuditEntryData\",\"description\":\"Metadata for a repository membership for org.restore_member actions\",\"fields\":[{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipTeamAuditEntryData\",\"description\":\"Metadata for a team membership for org.restore_member actions\",\"fields\":[{\"name\":\"team\",\"description\":\"The team associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamName\",\"description\":\"The name of the team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamResourcePath\",\"description\":\"The HTTP path for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamUrl\",\"description\":\"The HTTP URL for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"TeamAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUnblockUserAuditEntry\",\"description\":\"Audit log entry for a org.unblock_user\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockedUser\",\"description\":\"The user being unblocked by the organization.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockedUserName\",\"description\":\"The username of the blocked user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockedUserResourcePath\",\"description\":\"The HTTP path for the blocked user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockedUserUrl\",\"description\":\"The HTTP URL for the blocked user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateDefaultRepositoryPermissionAuditEntry\",\"description\":\"Audit log entry for a org.update_default_repository_permission\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permission\",\"description\":\"The new default repository permission level for the organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgUpdateDefaultRepositoryPermissionAuditEntryPermission\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permissionWas\",\"description\":\"The former default repository permission level for the organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgUpdateDefaultRepositoryPermissionAuditEntryPermission\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgUpdateDefaultRepositoryPermissionAuditEntryPermission\",\"description\":\"The default permission a repository can have in an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"READ\",\"description\":\"Can read and clone repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WRITE\",\"description\":\"Can read, clone and push to repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADMIN\",\"description\":\"Can read, clone, push, and add collaborators to repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NONE\",\"description\":\"No default permission value.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberAuditEntry\",\"description\":\"Audit log entry for a org.update_member event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permission\",\"description\":\"The new member permission level for the organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgUpdateMemberAuditEntryPermission\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permissionWas\",\"description\":\"The former member permission level for the organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgUpdateMemberAuditEntryPermission\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgUpdateMemberAuditEntryPermission\",\"description\":\"The permissions available to members on an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"READ\",\"description\":\"Can read and clone repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADMIN\",\"description\":\"Can read, clone, push, and add collaborators to repositories.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryCreationPermissionAuditEntry\",\"description\":\"Audit log entry for a org.update_member_repository_creation_permission event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"canCreateRepositories\",\"description\":\"Can members create repositories in the organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"visibility\",\"description\":\"The permission for visibility level of repositories for this organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrgUpdateMemberRepositoryCreationPermissionAuditEntryVisibility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrgUpdateMemberRepositoryCreationPermissionAuditEntryVisibility\",\"description\":\"The permissions available for repository creation on an Organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ALL\",\"description\":\"All organization members are restricted from creating any repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"All organization members are restricted from creating public repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NONE\",\"description\":\"All organization members are allowed to create any repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"All organization members are restricted from creating private repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INTERNAL\",\"description\":\"All organization members are restricted from creating internal repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC_INTERNAL\",\"description\":\"All organization members are restricted from creating public or internal repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE_INTERNAL\",\"description\":\"All organization members are restricted from creating private or internal repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC_PRIVATE\",\"description\":\"All organization members are restricted from creating public or private repositories.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry\",\"description\":\"Audit log entry for a org.update_member_repository_invitation_permission event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"canInviteOutsideCollaboratorsToRepositories\",\"description\":\"Can outside collaborators be invited to repositories in the organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"description\":\"An account on GitHub, with one or more owners, that has repositories, members and teams.\",\"fields\":[{\"name\":\"anyPinnableItems\",\"description\":\"Determine if this repository owner has any items that can be pinned to their profile.\",\"args\":[{\"name\":\"type\",\"description\":\"Filter to only a particular kind of pinnable item.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"auditLog\",\"description\":\"Audit log entries of the organization\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"The query string to filter audit entries\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for the returned audit log entries.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"AuditLogOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationAuditEntryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the organization's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The organization's public profile description.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"descriptionHTML\",\"description\":\"The organization's public profile description rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"domains\",\"description\":\"A list of domains owned by the organization.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isVerified\",\"description\":\"Filter by if the domain is verified.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"null\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for verifiable domains returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"VerifiableDomainOrder\",\"ofType\":null},\"defaultValue\":\"{field: DOMAIN, direction: ASC}\"}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomainConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The organization's public email.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasSponsorsListing\",\"description\":\"True if this user/organization has a GitHub Sponsors listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"interactionAbility\",\"description\":\"The interaction ability settings for this organization.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryInteractionAbility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ipAllowListEnabledSetting\",\"description\":\"The setting value for whether the organization has an IP allow list enabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IpAllowListEnabledSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ipAllowListEntries\",\"description\":\"The IP addresses that are allowed to access resources owned by the organization.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for IP allow list entries returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IpAllowListEntryOrder\",\"ofType\":null},\"defaultValue\":\"{field: ALLOW_LIST_VALUE, direction: ASC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSponsoredBy\",\"description\":\"Check if the given account is sponsoring this user/organization.\",\"args\":[{\"name\":\"accountLogin\",\"description\":\"The target account's login.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSponsoringViewer\",\"description\":\"True if the viewer is sponsored by this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isVerified\",\"description\":\"Whether the organization has verified its profile email and website.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"itemShowcase\",\"description\":\"Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProfileItemShowcase\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"location\",\"description\":\"The organization's public profile location.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"The organization's login name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"memberStatuses\",\"description\":\"Get the status messages members of this entity have set that are either public or visible only to the organization.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for user statuses returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UserStatusOrder\",\"ofType\":null},\"defaultValue\":\"{field: UPDATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserStatusConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersWithRole\",\"description\":\"A list of users who are members of this organization.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationMemberConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The organization's public profile name.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"newTeamResourcePath\",\"description\":\"The HTTP path creating a new team\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"newTeamUrl\",\"description\":\"The HTTP URL creating a new team\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"notificationDeliveryRestrictionEnabledSetting\",\"description\":\"Indicates if email notification delivery for this organization is restricted to verified domains.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"NotificationRestrictionSettingValue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationBillingEmail\",\"description\":\"The billing email for the organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"packages\",\"description\":\"A list of packages under the owner.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"names\",\"description\":\"Find packages by their names.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"repositoryId\",\"description\":\"Find packages in a repository by ID.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"packageType\",\"description\":\"Filter registry package by type.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PackageType\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering of the returned packages.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pendingMembers\",\"description\":\"A list of users who have been invited to join this organization.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnableItems\",\"description\":\"A list of repositories and gists this profile owner can pin to their profile.\",\"args\":[{\"name\":\"types\",\"description\":\"Filter the types of pinnable items that are returned.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnableItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnedItems\",\"description\":\"A list of repositories and gists this profile owner has pinned to their profile\",\"args\":[{\"name\":\"types\",\"description\":\"Filter the types of pinned items that are returned.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnableItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnedItemsRemaining\",\"description\":\"Returns how many more items this profile owner can pin to their profile.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"Find project by number.\",\"args\":[{\"name\":\"number\",\"description\":\"The project number to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projects\",\"description\":\"A list of projects under the owner.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for projects returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ProjectOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"search\",\"description\":\"Query to search projects by, currently only searching by name.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the projects by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectsResourcePath\",\"description\":\"The HTTP path listing organization's projects\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectsUrl\",\"description\":\"The HTTP URL listing organization's projects\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositories\",\"description\":\"A list of repositories that the user owns.\",\"args\":[{\"name\":\"privacy\",\"description\":\"If non-null, filters repositories according to privacy\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPrivacy\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"affiliations\",\"description\":\"Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"ownerAffiliations\",\"description\":\"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":\"[OWNER, COLLABORATOR]\"},{\"name\":\"isLocked\",\"description\":\"If non-null, filters repositories according to whether they have been locked\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isFork\",\"description\":\"If non-null, filters repositories according to whether they are forks of another repository\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"Find Repository.\",\"args\":[{\"name\":\"name\",\"description\":\"Name of Repository to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresTwoFactorAuthentication\",\"description\":\"When true the organization requires all members, billing managers, and outside collaborators to enable two-factor authentication.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"samlIdentityProvider\",\"description\":\"The Organization's SAML identity providers\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationIdentityProvider\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorsListing\",\"description\":\"The GitHub Sponsors listing for this user or organization.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsListing\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipForViewerAsSponsor\",\"description\":\"The viewer's sponsorship of this entity.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Sponsorship\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipsAsMaintainer\",\"description\":\"This object's sponsorships as the maintainer.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"includePrivate\",\"description\":\"Whether or not to include private sponsorships in the result set\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorshipOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorshipConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipsAsSponsor\",\"description\":\"This object's sponsorships as the sponsor.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorshipOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorshipConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"team\",\"description\":\"Find an organization's team by its slug.\",\"args\":[{\"name\":\"slug\",\"description\":\"The name or slug of the team to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teams\",\"description\":\"A list of teams in this organization.\",\"args\":[{\"name\":\"privacy\",\"description\":\"If non-null, filters teams according to privacy\",\"type\":{\"kind\":\"ENUM\",\"name\":\"TeamPrivacy\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"role\",\"description\":\"If non-null, filters teams according to whether the viewer is an admin or member on team\",\"type\":{\"kind\":\"ENUM\",\"name\":\"TeamRole\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"If non-null, filters teams with query on team name and team slug\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"userLogins\",\"description\":\"User logins to filter by\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for teams returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"ldapMapped\",\"description\":\"If true, filters teams that are mapped to an LDAP Group (Enterprise only)\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"rootTeamsOnly\",\"description\":\"If true, restrict to only root teams\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamsResourcePath\",\"description\":\"The HTTP path listing organization's teams\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamsUrl\",\"description\":\"The HTTP URL listing organization's teams\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"twitterUsername\",\"description\":\"The organization's Twitter username.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanAdminister\",\"description\":\"Organization is adminable by the viewer.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanChangePinnedItems\",\"description\":\"Can the viewer pin repositories and gists to the profile?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanCreateProjects\",\"description\":\"Can the current viewer create new projects on this owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanCreateRepositories\",\"description\":\"Viewer can create repositories on this organization\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanCreateTeams\",\"description\":\"Viewer can create teams on this organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSponsor\",\"description\":\"Whether or not the viewer is able to sponsor this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerIsAMember\",\"description\":\"Viewer is an active member of this organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerIsSponsoring\",\"description\":\"True if the viewer is sponsoring this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"websiteUrl\",\"description\":\"The organization's public profile URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"PackageOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"ProjectOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"MemberStatusable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"ProfileOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Sponsorable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"OrganizationAuditEntry\",\"description\":\"An audit entry in an organization audit log.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposClearAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OauthApplicationCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddBillingManagerAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgBlockUserAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableOauthAppRestrictionsAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableSamlAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableTwoFactorRequirementAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableOauthAppRestrictionsAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableSamlAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableTwoFactorRequirementAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteToBusinessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessApprovedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessDeniedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessRequestedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveBillingManagerAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUnblockUserAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateDefaultRepositoryPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryCreationPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoArchivedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoChangeMergeSettingAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigLockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigUnlockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoDestroyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddRepositoryAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamChangeParentTeamAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveRepositoryAuditEntry\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"OrganizationAuditEntryConnection\",\"description\":\"The connection type for OrganizationAuditEntry.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationAuditEntryEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"OrganizationAuditEntry\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"description\":\"Metadata for an audit entry with action org.*\",\"fields\":[{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposClearAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MembersCanDeleteReposEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OauthApplicationCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddBillingManagerAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgBlockUserAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableOauthAppRestrictionsAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableSamlAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgDisableTwoFactorRequirementAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableOauthAppRestrictionsAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableSamlAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgEnableTwoFactorRequirementAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgInviteToBusinessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessApprovedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessDeniedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgOauthAppAccessRequestedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveBillingManagerAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRemoveOutsideCollaboratorAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipOrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUnblockUserAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateDefaultRepositoryPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryCreationPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoArchivedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoChangeMergeSettingAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigLockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigUnlockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoDestroyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddRepositoryAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamChangeParentTeamAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveRepositoryAuditEntry\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"OrganizationAuditEntryEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"OrganizationAuditEntry\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"description\":\"The connection type for Organization.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationIdentityProvider\",\"description\":\"An Identity Provider configured to provision SAML and SCIM identities for Organizations\",\"fields\":[{\"name\":\"digestMethod\",\"description\":\"The digest algorithm used to sign SAML requests for the Identity Provider.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"externalIdentities\",\"description\":\"External Identities provisioned by this Identity Provider\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ExternalIdentityConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"idpCertificate\",\"description\":\"The x509 certificate used by the Identity Provider to sign assertions and responses.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"X509Certificate\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issuer\",\"description\":\"The Issuer Entity ID for the SAML Identity Provider\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"Organization this Identity Provider belongs to\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signatureMethod\",\"description\":\"The signature algorithm used to sign SAML requests for the Identity Provider.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ssoUrl\",\"description\":\"The URL endpoint for the Identity Provider's SAML SSO.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitation\",\"description\":\"An Invitation for a user to an organization.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The email address of the user invited to the organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitationType\",\"description\":\"The type of invitation that was sent (e.g. email, user).\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrganizationInvitationType\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitee\",\"description\":\"The user who was invited to the organization.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"inviter\",\"description\":\"The user who created the invitation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The organization the invite is for\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"role\",\"description\":\"The user's pending role in the organization (e.g. member, owner).\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrganizationInvitationRole\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitationConnection\",\"description\":\"The connection type for OrganizationInvitation.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitationEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitationEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrganizationInvitationRole\",\"description\":\"The possible organization invitation roles.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"DIRECT_MEMBER\",\"description\":\"The user is invited to be a direct member of the organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADMIN\",\"description\":\"The user is invited to be an admin of the organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BILLING_MANAGER\",\"description\":\"The user is invited to be a billing manager of the organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REINSTATE\",\"description\":\"The user's previous role will be reinstated.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrganizationInvitationType\",\"description\":\"The possible organization invitation types.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"USER\",\"description\":\"The invitation was to an existing user.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"EMAIL\",\"description\":\"The invitation was to an email address.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationMemberConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationMemberEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationMemberEdge\",\"description\":\"Represents a user within an organization.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasTwoFactorEnabled\",\"description\":\"Whether the organization member has two factor enabled or not. Returns null if information is not available to viewer.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"role\",\"description\":\"The role this user has in the organization.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OrganizationMemberRole\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrganizationMemberRole\",\"description\":\"The possible roles within an organization for its members.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"MEMBER\",\"description\":\"The user is a member of the organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADMIN\",\"description\":\"The user is an administrator of the organization.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrganizationMembersCanCreateRepositoriesSettingValue\",\"description\":\"The possible values for the members can create repositories setting on an organization.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ALL\",\"description\":\"Members will be able to create public and private repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"Members will be able to create only private repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISABLED\",\"description\":\"Members will not be able to create public or private repositories.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"OrganizationOrder\",\"description\":\"Ordering options for organization connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order organizations by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrganizationOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"OrganizationOrderField\",\"description\":\"Properties by which organization connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order organizations by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LOGIN\",\"description\":\"Order organizations by login\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationTeamsHovercardContext\",\"description\":\"An organization teams hovercard context\",\"fields\":[{\"name\":\"message\",\"description\":\"A string describing this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"octicon\",\"description\":\"An octicon to accompany this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"relevantTeams\",\"description\":\"Teams in this organization the user is a member of that are relevant\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamsResourcePath\",\"description\":\"The path for the full team list for this user\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamsUrl\",\"description\":\"The URL for the full team list for this user\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalTeamCount\",\"description\":\"The total number of teams the user is on in the organization\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"HovercardContext\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"OrganizationsHovercardContext\",\"description\":\"An organization list hovercard context\",\"fields\":[{\"name\":\"message\",\"description\":\"A string describing this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"octicon\",\"description\":\"An octicon to accompany this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"relevantOrganizations\",\"description\":\"Organizations this user is a member of that are relevant\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalOrganizationCount\",\"description\":\"The total number of organizations this user is in\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"HovercardContext\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Package\",\"description\":\"Information for an uploaded package.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"latestVersion\",\"description\":\"Find the latest version for the package.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"Identifies the name of the package.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"packageType\",\"description\":\"Identifies the type of the package.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PackageType\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository this package belongs to.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"statistics\",\"description\":\"Statistics about package activity.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PackageStatistics\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"version\",\"description\":\"Find package version by version string.\",\"args\":[{\"name\":\"version\",\"description\":\"The package version.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"versions\",\"description\":\"list of versions for this package\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering of the returned packages.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageVersionOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageConnection\",\"description\":\"The connection type for Package.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Package\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Package\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageFile\",\"description\":\"A file in a package version.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"md5\",\"description\":\"MD5 hash of the file.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"Name of the file.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"packageVersion\",\"description\":\"The package version this file belongs to.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sha1\",\"description\":\"SHA1 hash of the file.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sha256\",\"description\":\"SHA256 hash of the file.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"size\",\"description\":\"Size of the file in bytes.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"URL to download the asset.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageFileConnection\",\"description\":\"The connection type for PackageFile.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageFileEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageFile\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageFileEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PackageFile\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageFileOrder\",\"description\":\"Ways in which lists of package files can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order package files by.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PackageFileOrderField\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order package files by the specified field.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PackageFileOrderField\",\"description\":\"Properties by which package file connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order package files by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageOrder\",\"description\":\"Ways in which lists of packages can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order packages by.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PackageOrderField\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order packages by the specified field.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PackageOrderField\",\"description\":\"Properties by which package connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order packages by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"PackageOwner\",\"description\":\"Represents an owner of a package.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"packages\",\"description\":\"A list of packages under the owner.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"names\",\"description\":\"Find packages by their names.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"repositoryId\",\"description\":\"Find packages in a repository by ID.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"packageType\",\"description\":\"Filter registry package by type.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PackageType\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering of the returned packages.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"PackageStatistics\",\"description\":\"Represents a object that contains package activity statistics such as downloads.\",\"fields\":[{\"name\":\"downloadsTotalCount\",\"description\":\"Number of times the package was downloaded since it was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageTag\",\"description\":\"A version tag contains the mapping between a tag name and a version.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"Identifies the tag name of the version.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"version\",\"description\":\"Version that the tag is associated with.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PackageType\",\"description\":\"The possible types of a package.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NPM\",\"description\":\"An npm package.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RUBYGEMS\",\"description\":\"A rubygems package.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MAVEN\",\"description\":\"A maven package.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DOCKER\",\"description\":\"A docker image.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DEBIAN\",\"description\":\"A debian package.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NUGET\",\"description\":\"A nuget package.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PYPI\",\"description\":\"A python package.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageVersion\",\"description\":\"Information about a specific package version.\",\"fields\":[{\"name\":\"files\",\"description\":\"List of files associated with this package version\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering of the returned package files.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageFileOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageFileConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"package\",\"description\":\"The package associated with this version.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Package\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"platform\",\"description\":\"The platform this version was built for.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"preRelease\",\"description\":\"Whether or not this version is a pre-release.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"readme\",\"description\":\"The README of this package version.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"release\",\"description\":\"The release associated with this package version.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Release\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"statistics\",\"description\":\"Statistics about package activity.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersionStatistics\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"summary\",\"description\":\"The package version summary.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"version\",\"description\":\"The version string.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageVersionConnection\",\"description\":\"The connection type for PackageVersion.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersionEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersion\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageVersionEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PackageVersion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageVersionOrder\",\"description\":\"Ways in which lists of package versions can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order package versions by.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PackageVersionOrderField\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order package versions by the specified field.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PackageVersionOrderField\",\"description\":\"Properties by which package version connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order package versions by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PackageVersionStatistics\",\"description\":\"Represents a object that contains package version activity statistics such as downloads.\",\"fields\":[{\"name\":\"downloadsTotalCount\",\"description\":\"Number of times the package was downloaded since it was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"description\":\"Information about pagination in a connection.\",\"fields\":[{\"name\":\"endCursor\",\"description\":\"When paginating forwards, the cursor to continue.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasNextPage\",\"description\":\"When paginating forwards, are there more items?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasPreviousPage\",\"description\":\"When paginating backwards, are there more items?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"startCursor\",\"description\":\"When paginating backwards, the cursor to continue.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"PermissionGranter\",\"description\":\"Types that can grant permissions on a repository to a user\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"PermissionSource\",\"description\":\"A level of permission and source for a user's access to a repository.\",\"fields\":[{\"name\":\"organization\",\"description\":\"The organization the repository belongs to.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permission\",\"description\":\"The level of access this source has granted to the user.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"DefaultRepositoryPermissionField\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"source\",\"description\":\"The source of this permission.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"PermissionGranter\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"PinIssueInput\",\"description\":\"Autogenerated input type of PinIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"issueId\",\"description\":\"The ID of the issue to be pinned\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PinIssuePayload\",\"description\":\"Autogenerated return type of PinIssue\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The issue that was pinned\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"PinnableItem\",\"description\":\"Types that can be pinned to a profile page.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"PinnableItemConnection\",\"description\":\"The connection type for PinnableItem.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnableItemEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"PinnableItem\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnableItemEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"PinnableItem\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"description\":\"Represents items that can be pinned to a profile page or dashboard.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"REPOSITORY\",\"description\":\"A repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"GIST\",\"description\":\"A gist.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ISSUE\",\"description\":\"An issue.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PROJECT\",\"description\":\"A project.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PULL_REQUEST\",\"description\":\"A pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"USER\",\"description\":\"A user.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ORGANIZATION\",\"description\":\"An organization.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TEAM\",\"description\":\"A team.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnedEvent\",\"description\":\"Represents a 'pinned' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"Identifies the issue associated with the event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnedIssue\",\"description\":\"A Pinned Issue is a issue pinned to a repository's index page.\",\"fields\":[{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The issue that was pinned.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnedBy\",\"description\":\"The actor that pinned this issue.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository that this issue was pinned to.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnedIssueConnection\",\"description\":\"The connection type for PinnedIssue.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnedIssueEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnedIssue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnedIssueEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PinnedIssue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"description\":\"An ISO-8601 encoded UTC date string with millisecond precision.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingDisableAuditEntry\",\"description\":\"Audit log entry for a private_repository_forking.disable event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingEnableAuditEntry\",\"description\":\"Audit log entry for a private_repository_forking.enable event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProfileItemShowcase\",\"description\":\"A curatable list of repositories relating to a repository owner, which defaults to showing the most popular repositories they own.\",\"fields\":[{\"name\":\"hasPinnedItems\",\"description\":\"Whether or not the owner has pinned any repositories or gists.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"items\",\"description\":\"The repositories and gists in the showcase. If the profile owner has any pinned items, those will be returned. Otherwise, the profile owner's popular repositories will be returned.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnableItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"ProfileOwner\",\"description\":\"Represents any entity on GitHub that has a profile page.\",\"fields\":[{\"name\":\"anyPinnableItems\",\"description\":\"Determine if this repository owner has any items that can be pinned to their profile.\",\"args\":[{\"name\":\"type\",\"description\":\"Filter to only a particular kind of pinnable item.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The public profile email.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"itemShowcase\",\"description\":\"Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProfileItemShowcase\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"location\",\"description\":\"The public profile location.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"The username used to login.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The public profile name.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnableItems\",\"description\":\"A list of repositories and gists this profile owner can pin to their profile.\",\"args\":[{\"name\":\"types\",\"description\":\"Filter the types of pinnable items that are returned.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnableItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnedItems\",\"description\":\"A list of repositories and gists this profile owner has pinned to their profile\",\"args\":[{\"name\":\"types\",\"description\":\"Filter the types of pinned items that are returned.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnableItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnedItemsRemaining\",\"description\":\"Returns how many more items this profile owner can pin to their profile.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanChangePinnedItems\",\"description\":\"Can the viewer pin repositories and gists to the profile?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"websiteUrl\",\"description\":\"The public profile website URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"Project\",\"description\":\"Projects manage issues, pull requests and notes within a project owner.\",\"fields\":[{\"name\":\"body\",\"description\":\"The project's description body.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The projects description body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closed\",\"description\":\"`true` if the object is closed (definition of closed may depend on type)\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closedAt\",\"description\":\"Identifies the date and time when the object was closed.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"columns\",\"description\":\"List of columns in the project\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumnConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"creator\",\"description\":\"The actor who originally created the project.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The project's name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"number\",\"description\":\"The project's number.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The project's owner. Currently limited to repositories, organizations, and users.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"ProjectOwner\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pendingCards\",\"description\":\"List of pending cards in this project\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"archivedStates\",\"description\":\"A list of archived states to filter the cards by\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectCardArchivedState\",\"ofType\":null}},\"defaultValue\":\"[ARCHIVED, NOT_ARCHIVED]\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCardConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"progress\",\"description\":\"Project progress details.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectProgress\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this project\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"Whether the project is open or closed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this project\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Closable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectCard\",\"description\":\"A card in a project.\",\"fields\":[{\"name\":\"column\",\"description\":\"The project column this card is associated under. A card may only belong to one\\nproject column at a time. The column field will be null if the card is created\\nin a pending state and has yet to be associated with a column. Once cards are\\nassociated with a column, they will not become pending in the future.\\n\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumn\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"content\",\"description\":\"The card content item\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"ProjectCardItem\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"creator\",\"description\":\"The actor who created this card\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isArchived\",\"description\":\"Whether the card is archived\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"note\",\"description\":\"The card note\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The project that contains this card.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this card\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The state of ProjectCard\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"ProjectCardState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this card\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ProjectCardArchivedState\",\"description\":\"The possible archived states of a project card.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ARCHIVED\",\"description\":\"A project card that is archived\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NOT_ARCHIVED\",\"description\":\"A project card that is not archived\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectCardConnection\",\"description\":\"The connection type for ProjectCard.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCardEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCard\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectCardEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCard\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"ProjectCardItem\",\"description\":\"Types that can be inside Project Cards.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"ENUM\",\"name\":\"ProjectCardState\",\"description\":\"Various content states of a ProjectCard\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CONTENT_ONLY\",\"description\":\"The card has content only.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NOTE_ONLY\",\"description\":\"The card has a note only.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REDACTED\",\"description\":\"The card is redacted.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectColumn\",\"description\":\"A column inside a project.\",\"fields\":[{\"name\":\"cards\",\"description\":\"List of cards in the column\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"archivedStates\",\"description\":\"A list of archived states to filter the cards by\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectCardArchivedState\",\"ofType\":null}},\"defaultValue\":\"[ARCHIVED, NOT_ARCHIVED]\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCardConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The project column's name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The project that contains this column.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"purpose\",\"description\":\"The semantic purpose of the column\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"ProjectColumnPurpose\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this project column\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this project column\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectColumnConnection\",\"description\":\"The connection type for ProjectColumn.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumnEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumn\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectColumnEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumn\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ProjectColumnPurpose\",\"description\":\"The semantic purpose of the column - todo, in progress, or done.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"TODO\",\"description\":\"The column contains cards still to be worked on\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"IN_PROGRESS\",\"description\":\"The column contains cards which are currently being worked on\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DONE\",\"description\":\"The column contains cards which are complete\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectConnection\",\"description\":\"A list of projects associated with the owner.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ProjectEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ProjectOrder\",\"description\":\"Ways in which lists of projects can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order projects by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order projects by the specified field.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ProjectOrderField\",\"description\":\"Properties by which project connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order projects by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UPDATED_AT\",\"description\":\"Order projects by update time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NAME\",\"description\":\"Order projects by name\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"ProjectOwner\",\"description\":\"Represents an owner of a Project.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"Find project by number.\",\"args\":[{\"name\":\"number\",\"description\":\"The project number to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projects\",\"description\":\"A list of projects under the owner.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for projects returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ProjectOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"search\",\"description\":\"Query to search projects by, currently only searching by name.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the projects by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectsResourcePath\",\"description\":\"The HTTP path listing owners projects\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectsUrl\",\"description\":\"The HTTP URL listing owners projects\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanCreateProjects\",\"description\":\"Can the current viewer create new projects on this owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"ProjectProgress\",\"description\":\"Project progress stats.\",\"fields\":[{\"name\":\"doneCount\",\"description\":\"The number of done cards.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"donePercentage\",\"description\":\"The percentage of done cards.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enabled\",\"description\":\"Whether progress tracking is enabled and cards with purpose exist for this project\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"inProgressCount\",\"description\":\"The number of in-progress cards.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"inProgressPercentage\",\"description\":\"The percentage of in-progress cards.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"todoCount\",\"description\":\"The number of to do cards.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"todoPercentage\",\"description\":\"The percentage of to do cards.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Float\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ProjectState\",\"description\":\"State of the project; either 'open' or 'closed'\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OPEN\",\"description\":\"The project is open.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CLOSED\",\"description\":\"The project is closed.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ProjectTemplate\",\"description\":\"GitHub-provided templates for Projects\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"BASIC_KANBAN\",\"description\":\"Create a board with columns for To do, In progress and Done.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTOMATED_KANBAN_V2\",\"description\":\"Create a board with v2 triggers to automatically move cards across To do, In progress and Done columns.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTOMATED_REVIEWS_KANBAN\",\"description\":\"Create a board with triggers to automatically move cards across columns with review automation.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BUG_TRIAGE\",\"description\":\"Create a board to triage and prioritize bugs with To do, priority, and Done columns.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PublicKey\",\"description\":\"A user's public key.\",\"fields\":[{\"name\":\"accessedAt\",\"description\":\"The last time this authorization was used to perform an action. Values will be null for keys not owned by the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the key was created. Keys created before March 5th, 2014 have inaccurate values. Values will be null for keys not owned by the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"fingerprint\",\"description\":\"The fingerprint for this PublicKey.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isReadOnly\",\"description\":\"Whether this PublicKey is read-only or not. Values will be null for keys not owned by the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"key\",\"description\":\"The public key string.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the key was updated. Keys created before March 5th, 2014 may have inaccurate values. Values will be null for keys not owned by the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PublicKeyConnection\",\"description\":\"The connection type for PublicKey.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PublicKeyEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PublicKey\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PublicKeyEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PublicKey\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"description\":\"A repository pull request.\",\"fields\":[{\"name\":\"activeLockReason\",\"description\":\"Reason that the conversation was locked.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"LockReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"additions\",\"description\":\"The number of additions in this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"assignees\",\"description\":\"A list of Users assigned to this object.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the subject of the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"autoMergeRequest\",\"description\":\"Returns the auto-merge request object if one exists for this pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"AutoMergeRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"baseRef\",\"description\":\"Identifies the base Ref associated with the pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"baseRefName\",\"description\":\"Identifies the name of the base Ref associated with the pull request, even if the ref has been deleted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"baseRefOid\",\"description\":\"Identifies the oid of the base ref associated with the pull request, even if the ref has been deleted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"baseRepository\",\"description\":\"The repository associated with this pull request's base Ref.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"The body as Markdown.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The body rendered to text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"changedFiles\",\"description\":\"The number of changed files in this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"checksResourcePath\",\"description\":\"The HTTP path for the checks of this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"checksUrl\",\"description\":\"The HTTP URL for the checks of this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closed\",\"description\":\"`true` if the pull request is closed\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closedAt\",\"description\":\"Identifies the date and time when the object was closed.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"comments\",\"description\":\"A list of comments associated with the pull request.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for issue comments returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueCommentOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commits\",\"description\":\"A list of commits present in this pull request's head branch not present in the base branch.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommitConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletions\",\"description\":\"The number of deletions in this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited this pull request's body.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"files\",\"description\":\"Lists the files changed within this pull request.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestChangedFileConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"headRef\",\"description\":\"Identifies the head Ref associated with the pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"headRefName\",\"description\":\"Identifies the name of the head Ref associated with the pull request, even if the ref has been deleted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"headRefOid\",\"description\":\"Identifies the oid of the head ref associated with the pull request, even if the ref has been deleted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"headRepository\",\"description\":\"The repository associated with this pull request's head Ref.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"headRepositoryOwner\",\"description\":\"The owner of the repository associated with this pull request's head Ref.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"RepositoryOwner\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hovercard\",\"description\":\"The hovercard information for this issue\",\"args\":[{\"name\":\"includeNotificationContexts\",\"description\":\"Whether or not to include notification contexts\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"true\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Hovercard\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCrossRepository\",\"description\":\"The head and base repositories are different.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDraft\",\"description\":\"Identifies if the pull request is a draft.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isReadByViewer\",\"description\":\"Is this pull request read by the viewer\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"labels\",\"description\":\"A list of labels associated with the object.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for labels returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"LabelOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"LabelConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"latestOpinionatedReviews\",\"description\":\"A list of latest reviews per user associated with the pull request.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"writersOnly\",\"description\":\"Only return reviews from user who have write access to the repository\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"latestReviews\",\"description\":\"A list of latest reviews per user associated with the pull request that are not also pending review.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"locked\",\"description\":\"`true` if the pull request is locked\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"maintainerCanModify\",\"description\":\"Indicates whether maintainers can modify the pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeCommit\",\"description\":\"The commit that was created when this pull request was merged.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeable\",\"description\":\"Whether or not the pull request can be merged based on the existence of merge conflicts.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"MergeableState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"merged\",\"description\":\"Whether or not the pull request was merged.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergedAt\",\"description\":\"The date and time that the pull request was merged.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergedBy\",\"description\":\"The actor who merged the pull request.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"milestone\",\"description\":\"Identifies the milestone associated with the pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"number\",\"description\":\"Identifies the pull request number.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"participants\",\"description\":\"A list of Users that are participating in the Pull Request conversation.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permalink\",\"description\":\"The permalink to the pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"potentialMergeCommit\",\"description\":\"The commit that GitHub automatically generated to test if this pull request could be merged. This field will not return a value if the pull request is merged, or if the test merge commit is still being generated. See the `mergeable` field for more details on the mergeability of the pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectCards\",\"description\":\"List of project cards associated with this pull request.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"archivedStates\",\"description\":\"A list of archived states to filter the cards by\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectCardArchivedState\",\"ofType\":null}},\"defaultValue\":\"[ARCHIVED, NOT_ARCHIVED]\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCardConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"revertResourcePath\",\"description\":\"The HTTP path for reverting this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"revertUrl\",\"description\":\"The HTTP URL for reverting this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reviewDecision\",\"description\":\"The current status of this pull request with respect to code review.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewDecision\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reviewRequests\",\"description\":\"A list of review requests associated with the pull request.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reviewThreads\",\"description\":\"The list of all review threads for this pull request.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThreadConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reviews\",\"description\":\"A list of reviews associated with the pull request.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the reviews.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"author\",\"description\":\"Filter by author of the review.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"Identifies the state of the pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"suggestedReviewers\",\"description\":\"A list of reviewer suggestions based on commit history and past review comments.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SuggestedReviewer\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"timeline\",\"description\":\"A list of events, comments, commits, etc. associated with the pull request.\",\"args\":[{\"name\":\"since\",\"description\":\"Allows filtering timeline events by a `since` timestamp.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestTimelineConnection\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"`timeline` will be removed Use PullRequest.timelineItems instead. Removal on 2020-10-01 UTC.\"},{\"name\":\"timelineItems\",\"description\":\"A list of events, comments, commits, etc. associated with the pull request.\",\"args\":[{\"name\":\"since\",\"description\":\"Filter timeline items by a `since` timestamp.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"skip\",\"description\":\"Skips the first _n_ elements in the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"itemTypes\",\"description\":\"Filter timeline items by type.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestTimelineItemsItemType\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestTimelineItemsConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"Identifies the pull request title.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanApplySuggestion\",\"description\":\"Whether or not the viewer can apply suggestion.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDeleteHeadRef\",\"description\":\"Check if the viewer can restore the deleted head ref.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDisableAutoMerge\",\"description\":\"Whether or not the viewer can disable auto-merge\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanEnableAutoMerge\",\"description\":\"Whether or not the viewer can enable auto-merge\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSubscribe\",\"description\":\"Check if the viewer is able to change their subscription status for the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerLatestReview\",\"description\":\"The latest review given from the viewer.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerLatestReviewRequest\",\"description\":\"The person who has requested the viewer for review on this pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ReviewRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerMergeBodyText\",\"description\":\"The merge body text for the viewer and method.\",\"args\":[{\"name\":\"mergeType\",\"description\":\"The merge method for the message.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PullRequestMergeMethod\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerMergeHeadlineText\",\"description\":\"The merge headline text for the viewer and method.\",\"args\":[{\"name\":\"mergeType\",\"description\":\"The merge method for the message.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PullRequestMergeMethod\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerSubscription\",\"description\":\"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Assignable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Closable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Labelable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Lockable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestChangedFile\",\"description\":\"A file changed in a pull request.\",\"fields\":[{\"name\":\"additions\",\"description\":\"The number of additions to the file.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletions\",\"description\":\"The number of deletions to the file.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"The path of the file.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerViewedState\",\"description\":\"The state of the file for the viewer.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"FileViewedState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestChangedFileConnection\",\"description\":\"The connection type for PullRequestChangedFile.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestChangedFileEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestChangedFile\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestChangedFileEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestChangedFile\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommit\",\"description\":\"Represents a Git commit part of a pull request.\",\"fields\":[{\"name\":\"commit\",\"description\":\"The Git commit object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request this commit belongs to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this pull request commit\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this pull request commit\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommitCommentThread\",\"description\":\"Represents a commit comment thread part of a pull request.\",\"fields\":[{\"name\":\"comments\",\"description\":\"The comments that exist in this thread.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"The commit the comments were made on.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"The file the comments were made on.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"position\",\"description\":\"The position in the diff for the commit that the comment was made on.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request this commit comment thread belongs to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommitConnection\",\"description\":\"The connection type for PullRequestCommit.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommitEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommitEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestConnection\",\"description\":\"The connection type for PullRequest.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestContributionsByRepository\",\"description\":\"This aggregates pull requests opened by a user within one repository.\",\"fields\":[{\"name\":\"contributions\",\"description\":\"The pull request contributions.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for contributions returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ContributionOrder\",\"ofType\":null},\"defaultValue\":\"{direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestContributionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository in which the pull requests were opened.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestMergeMethod\",\"description\":\"Represents available types of methods to use when merging a pull request.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"MERGE\",\"description\":\"Add all commits from the head branch to the base branch with a merge commit.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SQUASH\",\"description\":\"Combine all commits from the head branch into a single commit in the base branch.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REBASE\",\"description\":\"Add all commits from the head branch onto the base branch individually.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"PullRequestOrder\",\"description\":\"Ways in which lists of issues can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order pull requests by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order pull requests by the specified field.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestOrderField\",\"description\":\"Properties by which pull_requests connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order pull_requests by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UPDATED_AT\",\"description\":\"Order pull_requests by update time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"description\":\"A review object for a given pull request.\",\"fields\":[{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the subject of the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorCanPushToRepository\",\"description\":\"Indicates whether the author of this review has push access to the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"Identifies the pull request review body.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The body of this review rendered as plain text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"comments\",\"description\":\"A list of review comments for the current pull request review.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"Identifies the commit associated with this pull request review.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"onBehalfOf\",\"description\":\"A list of teams that this review was made on behalf of.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"Identifies the pull request associated with this pull request review.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path permalink for this PullRequestReview.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"Identifies the current state of the pull request review.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"submittedAt\",\"description\":\"Identifies when the Pull Request Review was submitted\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL permalink for this PullRequestReview.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDelete\",\"description\":\"Check if the current viewer can delete this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Deletable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"description\":\"A review comment associated with a given repository pull request.\",\"fields\":[{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the subject of the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"The comment body of this review comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The comment body of this review comment rendered as plain text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"Identifies the commit associated with the comment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies when the comment was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"diffHunk\",\"description\":\"The diff hunk to which the comment applies.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"draftedAt\",\"description\":\"Identifies when the comment was created in a draft state.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isMinimized\",\"description\":\"Returns whether or not a comment has been minimized.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"minimizedReason\",\"description\":\"Returns why the comment was minimized.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"originalCommit\",\"description\":\"Identifies the original commit associated with the comment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"originalPosition\",\"description\":\"The original line index in the diff to which the comment applies.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"outdated\",\"description\":\"Identifies when the comment body is outdated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"The path to which the comment applies.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"position\",\"description\":\"The line index in the diff to which the comment applies.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request associated with this review comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReview\",\"description\":\"The pull request review associated with this review comment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"replyTo\",\"description\":\"The comment this is a reply to.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path permalink for this review comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"Identifies the state of the comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewCommentState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies when the comment was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL permalink for this review comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDelete\",\"description\":\"Check if the current viewer can delete this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanMinimize\",\"description\":\"Check if the current viewer can minimize this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Deletable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Minimizable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewCommentConnection\",\"description\":\"The connection type for PullRequestReviewComment.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewCommentEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewCommentEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewCommentState\",\"description\":\"The possible states of a pull request review comment.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PENDING\",\"description\":\"A comment that is part of a pending review\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUBMITTED\",\"description\":\"A comment that is part of a submitted review\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewConnection\",\"description\":\"The connection type for PullRequestReview.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewContributionsByRepository\",\"description\":\"This aggregates pull request reviews made by a user within one repository.\",\"fields\":[{\"name\":\"contributions\",\"description\":\"The pull request review contributions.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for contributions returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ContributionOrder\",\"ofType\":null},\"defaultValue\":\"{direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CreatedPullRequestReviewContributionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository in which the pull request reviews were made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewDecision\",\"description\":\"The review status of a pull request.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CHANGES_REQUESTED\",\"description\":\"Changes have been requested on the pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"APPROVED\",\"description\":\"The pull request has received an approving review.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REVIEW_REQUIRED\",\"description\":\"A review is required before the pull request can be merged.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewEvent\",\"description\":\"The possible events to perform on a pull request review.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"COMMENT\",\"description\":\"Submit general feedback without explicit approval.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"APPROVE\",\"description\":\"Submit feedback and approve merging these changes.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REQUEST_CHANGES\",\"description\":\"Submit feedback that must be addressed before merging.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISMISS\",\"description\":\"Dismiss review so it now longer effects merging.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewState\",\"description\":\"The possible states of a pull request review.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PENDING\",\"description\":\"A review that has not yet been submitted.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMMENTED\",\"description\":\"An informational review.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"APPROVED\",\"description\":\"A review allowing the pull request to merge.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CHANGES_REQUESTED\",\"description\":\"A review blocking the pull request from merging.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISMISSED\",\"description\":\"A review that has been dismissed.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"description\":\"A threaded list of comments for a given pull request.\",\"fields\":[{\"name\":\"comments\",\"description\":\"A list of pull request comments associated with the thread.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"skip\",\"description\":\"Skips the first _n_ elements in the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"diffSide\",\"description\":\"The side of the diff on which this thread was placed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"DiffSide\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCollapsed\",\"description\":\"Whether or not the thread has been collapsed (outdated or resolved)\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isOutdated\",\"description\":\"Indicates whether this thread was outdated by newer changes.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isResolved\",\"description\":\"Whether this thread has been resolved\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"line\",\"description\":\"The line in the file to which this thread refers\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"originalLine\",\"description\":\"The original line in the file to which this thread refers.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"originalStartLine\",\"description\":\"The original start line in the file to which this thread refers (multi-line only).\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"Identifies the file path of this thread.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"Identifies the pull request associated with this thread.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"Identifies the repository associated with this thread.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resolvedBy\",\"description\":\"The user who resolved this thread\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"startDiffSide\",\"description\":\"The side of the diff that the first line of the thread starts on (multi-line only)\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"DiffSide\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"startLine\",\"description\":\"The start line in the file to which this thread refers (multi-line only)\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReply\",\"description\":\"Indicates whether the current viewer can reply to this thread.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanResolve\",\"description\":\"Whether or not the viewer can resolve this thread\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUnresolve\",\"description\":\"Whether or not the viewer can unresolve this thread\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThreadConnection\",\"description\":\"Review comment threads for a pull request review.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThreadEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThreadEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestRevisionMarker\",\"description\":\"Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastSeenCommit\",\"description\":\"The last commit the viewer has seen.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request to which the marker belongs.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestState\",\"description\":\"The possible states of a pull request.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OPEN\",\"description\":\"A pull request that is still open.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CLOSED\",\"description\":\"A pull request that has been closed without being merged.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MERGED\",\"description\":\"A pull request that has been closed by being merged.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestTimelineConnection\",\"description\":\"The connection type for PullRequestTimelineItem.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestTimelineItemEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"PullRequestTimelineItem\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"PullRequestTimelineItem\",\"description\":\"An item in a pull request timeline\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"AssignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefForcePushedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ClosedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitCommentThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CrossReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DemilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DeployedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentEnvironmentChangedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefForcePushedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefRestoredEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MergedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RenamedTitleEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReopenedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestRemovedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnassignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnsubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UserBlockedEvent\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"PullRequestTimelineItemEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"PullRequestTimelineItem\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"PullRequestTimelineItems\",\"description\":\"An item in a pull request timeline\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"AddedToProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AssignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoMergeDisabledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoMergeEnabledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoRebaseEnabledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutoSquashEnabledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutomaticBaseChangeFailedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"AutomaticBaseChangeSucceededEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefChangedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"BaseRefForcePushedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ClosedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CommentDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConnectedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertToDraftEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertedNoteToIssueEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CrossReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DemilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DeployedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DeploymentEnvironmentChangedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"DisconnectedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefDeletedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefForcePushedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"HeadRefRestoredEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"LockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MarkedAsDuplicateEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MentionedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MergedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MilestonedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MovedColumnsInProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PinnedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommitCommentThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestRevisionMarker\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReadyForReviewEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RemovedFromProjectEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RenamedTitleEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReopenedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestRemovedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"SubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TransferredEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnassignedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlabeledEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlockedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnmarkedAsDuplicateEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnpinnedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UnsubscribedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"UserBlockedEvent\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"PullRequestTimelineItemsConnection\",\"description\":\"The connection type for PullRequestTimelineItems.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestTimelineItemsEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"filteredCount\",\"description\":\"Identifies the count of items after applying `before` and `after` filters.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"PullRequestTimelineItems\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageCount\",\"description\":\"Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the timeline was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestTimelineItemsEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"PullRequestTimelineItems\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestTimelineItemsItemType\",\"description\":\"The possible item types found in a timeline.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PULL_REQUEST_COMMIT\",\"description\":\"Represents a Git commit part of a pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PULL_REQUEST_COMMIT_COMMENT_THREAD\",\"description\":\"Represents a commit comment thread part of a pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PULL_REQUEST_REVIEW\",\"description\":\"A review object for a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PULL_REQUEST_REVIEW_THREAD\",\"description\":\"A threaded list of comments for a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PULL_REQUEST_REVISION_MARKER\",\"description\":\"Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTOMATIC_BASE_CHANGE_FAILED_EVENT\",\"description\":\"Represents a 'automatic_base_change_failed' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTOMATIC_BASE_CHANGE_SUCCEEDED_EVENT\",\"description\":\"Represents a 'automatic_base_change_succeeded' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTO_MERGE_DISABLED_EVENT\",\"description\":\"Represents a 'auto_merge_disabled' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTO_MERGE_ENABLED_EVENT\",\"description\":\"Represents a 'auto_merge_enabled' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTO_REBASE_ENABLED_EVENT\",\"description\":\"Represents a 'auto_rebase_enabled' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"AUTO_SQUASH_ENABLED_EVENT\",\"description\":\"Represents a 'auto_squash_enabled' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BASE_REF_CHANGED_EVENT\",\"description\":\"Represents a 'base_ref_changed' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BASE_REF_FORCE_PUSHED_EVENT\",\"description\":\"Represents a 'base_ref_force_pushed' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BASE_REF_DELETED_EVENT\",\"description\":\"Represents a 'base_ref_deleted' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DEPLOYED_EVENT\",\"description\":\"Represents a 'deployed' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DEPLOYMENT_ENVIRONMENT_CHANGED_EVENT\",\"description\":\"Represents a 'deployment_environment_changed' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"HEAD_REF_DELETED_EVENT\",\"description\":\"Represents a 'head_ref_deleted' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"HEAD_REF_FORCE_PUSHED_EVENT\",\"description\":\"Represents a 'head_ref_force_pushed' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"HEAD_REF_RESTORED_EVENT\",\"description\":\"Represents a 'head_ref_restored' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MERGED_EVENT\",\"description\":\"Represents a 'merged' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REVIEW_DISMISSED_EVENT\",\"description\":\"Represents a 'review_dismissed' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REVIEW_REQUESTED_EVENT\",\"description\":\"Represents an 'review_requested' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REVIEW_REQUEST_REMOVED_EVENT\",\"description\":\"Represents an 'review_request_removed' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"READY_FOR_REVIEW_EVENT\",\"description\":\"Represents a 'ready_for_review' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONVERT_TO_DRAFT_EVENT\",\"description\":\"Represents a 'convert_to_draft' event on a given pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ISSUE_COMMENT\",\"description\":\"Represents a comment on an Issue.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CROSS_REFERENCED_EVENT\",\"description\":\"Represents a mention made by one issue or pull request to another.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ADDED_TO_PROJECT_EVENT\",\"description\":\"Represents a 'added_to_project' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ASSIGNED_EVENT\",\"description\":\"Represents an 'assigned' event on any assignable object.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CLOSED_EVENT\",\"description\":\"Represents a 'closed' event on any `Closable`.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMMENT_DELETED_EVENT\",\"description\":\"Represents a 'comment_deleted' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONNECTED_EVENT\",\"description\":\"Represents a 'connected' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONVERTED_NOTE_TO_ISSUE_EVENT\",\"description\":\"Represents a 'converted_note_to_issue' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DEMILESTONED_EVENT\",\"description\":\"Represents a 'demilestoned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DISCONNECTED_EVENT\",\"description\":\"Represents a 'disconnected' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LABELED_EVENT\",\"description\":\"Represents a 'labeled' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LOCKED_EVENT\",\"description\":\"Represents a 'locked' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MARKED_AS_DUPLICATE_EVENT\",\"description\":\"Represents a 'marked_as_duplicate' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MENTIONED_EVENT\",\"description\":\"Represents a 'mentioned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MILESTONED_EVENT\",\"description\":\"Represents a 'milestoned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MOVED_COLUMNS_IN_PROJECT_EVENT\",\"description\":\"Represents a 'moved_columns_in_project' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PINNED_EVENT\",\"description\":\"Represents a 'pinned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REFERENCED_EVENT\",\"description\":\"Represents a 'referenced' event on a given `ReferencedSubject`.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REMOVED_FROM_PROJECT_EVENT\",\"description\":\"Represents a 'removed_from_project' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RENAMED_TITLE_EVENT\",\"description\":\"Represents a 'renamed' event on a given issue or pull request\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REOPENED_EVENT\",\"description\":\"Represents a 'reopened' event on any `Closable`.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUBSCRIBED_EVENT\",\"description\":\"Represents a 'subscribed' event on a given `Subscribable`.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TRANSFERRED_EVENT\",\"description\":\"Represents a 'transferred' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNASSIGNED_EVENT\",\"description\":\"Represents an 'unassigned' event on any assignable object.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNLABELED_EVENT\",\"description\":\"Represents an 'unlabeled' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNLOCKED_EVENT\",\"description\":\"Represents an 'unlocked' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"USER_BLOCKED_EVENT\",\"description\":\"Represents a 'user_blocked' event on a given user.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNMARKED_AS_DUPLICATE_EVENT\",\"description\":\"Represents an 'unmarked_as_duplicate' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNPINNED_EVENT\",\"description\":\"Represents an 'unpinned' event on a given issue or pull request.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNSUBSCRIBED_EVENT\",\"description\":\"Represents an 'unsubscribed' event on a given `Subscribable`.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"PullRequestUpdateState\",\"description\":\"The possible target states when updating a pull request.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OPEN\",\"description\":\"A pull request that is still open.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CLOSED\",\"description\":\"A pull request that has been closed without being merged.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Push\",\"description\":\"A Git push.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nextSha\",\"description\":\"The SHA after the push\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permalink\",\"description\":\"The permalink for this push.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"previousSha\",\"description\":\"The SHA before the push\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pusher\",\"description\":\"The user who pushed\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository that was pushed to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PushAllowance\",\"description\":\"A team, user or app who has the ability to push to a protected branch.\",\"fields\":[{\"name\":\"actor\",\"description\":\"The actor that can push.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"PushAllowanceActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"branchProtectionRule\",\"description\":\"Identifies the branch protection rule associated with the allowed user or team.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"PushAllowanceActor\",\"description\":\"Types that can be an actor.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"App\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"PushAllowanceConnection\",\"description\":\"The connection type for PushAllowance.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PushAllowanceEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PushAllowance\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"PushAllowanceEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PushAllowance\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Query\",\"description\":\"The query root of GitHub's GraphQL interface.\",\"fields\":[{\"name\":\"codeOfConduct\",\"description\":\"Look up a code of conduct by its key\",\"args\":[{\"name\":\"key\",\"description\":\"The code of conduct's key\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CodeOfConduct\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"codesOfConduct\",\"description\":\"Look up a code of conduct by its key\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CodeOfConduct\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"Look up an enterprise by URL slug.\",\"args\":[{\"name\":\"slug\",\"description\":\"The enterprise URL slug.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"invitationToken\",\"description\":\"The enterprise invitation token.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseAdministratorInvitation\",\"description\":\"Look up a pending enterprise administrator invitation by invitee, enterprise and role.\",\"args\":[{\"name\":\"userLogin\",\"description\":\"The login of the user invited to join the business.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise the user was invited to join.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"role\",\"description\":\"The role for the business member invitation.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorRole\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseAdministratorInvitationByToken\",\"description\":\"Look up a pending enterprise administrator invitation by invitation token.\",\"args\":[{\"name\":\"invitationToken\",\"description\":\"The invitation token sent with the invitation email.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseAdministratorInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"license\",\"description\":\"Look up an open source license by its key\",\"args\":[{\"name\":\"key\",\"description\":\"The license's downcased SPDX ID\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"License\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"licenses\",\"description\":\"Return a list of known open source licenses\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"License\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"marketplaceCategories\",\"description\":\"Get alphabetically sorted list of Marketplace categories\",\"args\":[{\"name\":\"includeCategories\",\"description\":\"Return only the specified categories.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"excludeEmpty\",\"description\":\"Exclude categories with no listings.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"excludeSubcategories\",\"description\":\"Returns top level categories only, excluding any subcategories.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceCategory\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"marketplaceCategory\",\"description\":\"Look up a Marketplace category by its slug.\",\"args\":[{\"name\":\"slug\",\"description\":\"The URL slug of the category.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"useTopicAliases\",\"description\":\"Also check topic aliases for the category slug\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceCategory\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"marketplaceListing\",\"description\":\"Look up a single Marketplace listing\",\"args\":[{\"name\":\"slug\",\"description\":\"Select the listing that matches this slug. It's the short name of the listing used in its URL.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListing\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"marketplaceListings\",\"description\":\"Look up Marketplace listings\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"categorySlug\",\"description\":\"Select only listings with the given category.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"useTopicAliases\",\"description\":\"Also check topic aliases for the category slug\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"viewerCanAdmin\",\"description\":\"Select listings to which user has admin access. If omitted, listings visible to the\\nviewer are returned.\\n\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"adminId\",\"description\":\"Select listings that can be administered by the specified user.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"organizationId\",\"description\":\"Select listings for products owned by the specified organization.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"allStates\",\"description\":\"Select listings visible to the viewer even if they are not approved. If omitted or\\nfalse, only approved listings will be returned.\\n\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"slugs\",\"description\":\"Select the listings with these slugs, if they are visible to the viewer.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"primaryCategoryOnly\",\"description\":\"Select only listings where the primary category matches the given category slug.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"withFreeTrialsOnly\",\"description\":\"Select only listings that offer a free trial.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListingConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"meta\",\"description\":\"Return information about the GitHub instance\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GitHubMetadata\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"Fetches an object given its ID.\",\"args\":[{\"name\":\"id\",\"description\":\"ID of the object.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"Lookup nodes by a list of IDs.\",\"args\":[{\"name\":\"ids\",\"description\":\"The list of node IDs.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}}},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"Lookup a organization by login.\",\"args\":[{\"name\":\"login\",\"description\":\"The organization's login.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"rateLimit\",\"description\":\"The client's rate limit information.\",\"args\":[{\"name\":\"dryRun\",\"description\":\"If true, calculate the cost for the query without evaluating it\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RateLimit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"relay\",\"description\":\"Hack to workaround https://github.com/facebook/relay/issues/112 re-exposing the root query object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Query\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"Lookup a given repository by the owner and repository name.\",\"args\":[{\"name\":\"owner\",\"description\":\"The login field of a user or organization\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the repository\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryOwner\",\"description\":\"Lookup a repository owner (ie. either a User or an Organization) by login.\",\"args\":[{\"name\":\"login\",\"description\":\"The username to lookup the owner by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"RepositoryOwner\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resource\",\"description\":\"Lookup resource by a URL.\",\"args\":[{\"name\":\"url\",\"description\":\"The URL.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"search\",\"description\":\"Perform a search across resources.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"type\",\"description\":\"The types of search items to search within.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SearchType\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SearchResultItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"securityAdvisories\",\"description\":\"GitHub Security Advisories\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for the returned topics.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SecurityAdvisoryOrder\",\"ofType\":null},\"defaultValue\":\"{field: UPDATED_AT, direction: DESC}\"},{\"name\":\"identifier\",\"description\":\"Filter advisories by identifier, e.g. GHSA or CVE.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SecurityAdvisoryIdentifierFilter\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"publishedSince\",\"description\":\"Filter advisories to those published since a time in the past.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"updatedSince\",\"description\":\"Filter advisories to those updated since a time in the past.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"securityAdvisory\",\"description\":\"Fetch a Security Advisory by its GHSA ID\",\"args\":[{\"name\":\"ghsaId\",\"description\":\"GitHub Security Advisory ID.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisory\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"securityVulnerabilities\",\"description\":\"Software Vulnerabilities documented by GitHub Security Advisories\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for the returned topics.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SecurityVulnerabilityOrder\",\"ofType\":null},\"defaultValue\":\"{field: UPDATED_AT, direction: DESC}\"},{\"name\":\"ecosystem\",\"description\":\"An ecosystem to filter vulnerabilities by.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryEcosystem\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"package\",\"description\":\"A package name to filter vulnerabilities by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"severities\",\"description\":\"A list of severities to filter vulnerabilities by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisorySeverity\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerabilityConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorables\",\"description\":\"Users and organizations who can be sponsored via GitHub Sponsors.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for users and organizations returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorableOrder\",\"ofType\":null},\"defaultValue\":\"{field: LOGIN, direction: ASC}\"},{\"name\":\"onlyDependencies\",\"description\":\"Whether only sponsorables who own the viewer's dependencies will be returned. Must be authenticated to use. Can check an organization instead for their dependencies owned by sponsorables by passing orgLoginForDependencies.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orgLoginForDependencies\",\"description\":\"Optional organization username for whose dependencies should be checked. Used when onlyDependencies = true. Omit to check your own dependencies. If you are not an administrator of the organization, only dependencies from its public repositories will be considered.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"dependencyEcosystem\",\"description\":\"Optional filter for which dependencies should be checked for sponsorable owners. Only sponsorable owners of dependencies in this ecosystem will be included. Used when onlyDependencies = true.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryEcosystem\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorableItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorsListing\",\"description\":\"Look up a single Sponsors Listing\",\"args\":[{\"name\":\"slug\",\"description\":\"Select the Sponsors listing which matches this slug\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsListing\",\"ofType\":null},\"isDeprecated\":true,\"deprecationReason\":\"`Query.sponsorsListing` will be removed. Use `Sponsorable.sponsorsListing` instead. Removal on 2020-04-01 UTC.\"},{\"name\":\"topic\",\"description\":\"Look up a topic by name.\",\"args\":[{\"name\":\"name\",\"description\":\"The topic's name.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"Lookup a user by login.\",\"args\":[{\"name\":\"login\",\"description\":\"The user's login.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewer\",\"description\":\"The currently authenticated user.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RateLimit\",\"description\":\"Represents the client's rate limit.\",\"fields\":[{\"name\":\"cost\",\"description\":\"The point cost for the current query counting against the rate limit.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"limit\",\"description\":\"The maximum number of points the client is permitted to consume in a 60 minute window.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodeCount\",\"description\":\"The maximum number of nodes this query may return\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"remaining\",\"description\":\"The number of points remaining in the current rate limit window.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resetAt\",\"description\":\"The time at which the current rate limit window resets in UTC epoch seconds.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"used\",\"description\":\"The number of points used in the current rate limit window.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"description\":\"Represents a subject that can be reacted on.\",\"fields\":[{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"ReactingUserConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactingUserEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReactingUserEdge\",\"description\":\"Represents a user that's made a reaction.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactedAt\",\"description\":\"The moment when the user made the reaction.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Reaction\",\"description\":\"An emoji reaction to a particular piece of content.\",\"fields\":[{\"name\":\"content\",\"description\":\"Identifies the emoji reaction.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactable\",\"description\":\"The reactable piece of content\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"Identifies the user who created this reaction.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"description\":\"A list of reactions that have been left on the subject.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Reaction\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerHasReacted\",\"description\":\"Whether or not the authenticated user has left a reaction on the subject.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"description\":\"Emojis that can be attached to Issues, Pull Requests and Comments.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"THUMBS_UP\",\"description\":\"Represents the `:+1:` emoji.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"THUMBS_DOWN\",\"description\":\"Represents the `:-1:` emoji.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LAUGH\",\"description\":\"Represents the `:laugh:` emoji.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"HOORAY\",\"description\":\"Represents the `:hooray:` emoji.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONFUSED\",\"description\":\"Represents the `:confused:` emoji.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"HEART\",\"description\":\"Represents the `:heart:` emoji.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ROCKET\",\"description\":\"Represents the `:rocket:` emoji.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"EYES\",\"description\":\"Represents the `:eyes:` emoji.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReactionEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Reaction\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"description\":\"A group of emoji reactions to a particular piece of content.\",\"fields\":[{\"name\":\"content\",\"description\":\"Identifies the emoji reaction.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies when the reaction was created.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"The subject that was reacted to.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"users\",\"description\":\"Users who have reacted to the reaction subject with the emotion represented by this reaction group\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactingUserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerHasReacted\",\"description\":\"Whether or not the authenticated user has left a reaction on the subject.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"description\":\"Ways in which lists of reactions can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order reactions by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ReactionOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order reactions by the specified field.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ReactionOrderField\",\"description\":\"A list of fields that reactions can be ordered by.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Allows ordering a list of reactions by when they were created.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReadyForReviewEvent\",\"description\":\"Represents a 'ready_for_review' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this ready for review event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this ready for review event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"description\":\"Represents a Git reference.\",\"fields\":[{\"name\":\"associatedPullRequests\",\"description\":\"A list of pull requests with this ref as the head ref.\",\"args\":[{\"name\":\"states\",\"description\":\"A list of states to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"headRefName\",\"description\":\"The head ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"baseRefName\",\"description\":\"The base ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pull requests returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"branchProtectionRule\",\"description\":\"Branch protection rules for this ref\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The ref name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"prefix\",\"description\":\"The ref's prefix, such as `refs/heads/` or `refs/tags/`.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"refUpdateRule\",\"description\":\"Branch protection rules that are viewable by non-admins\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RefUpdateRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository the ref belongs to.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"target\",\"description\":\"The object the ref points to. Returns null when object does not exist.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RefConnection\",\"description\":\"The connection type for Ref.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RefEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RefEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RefOrder\",\"description\":\"Ways in which lists of git refs can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order refs by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RefOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order refs by the specified field.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RefOrderField\",\"description\":\"Properties by which ref connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"TAG_COMMIT_DATE\",\"description\":\"Order refs by underlying commit date if the ref prefix is refs/tags/\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ALPHABETICAL\",\"description\":\"Order refs by their alphanumeric name\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RefUpdateRule\",\"description\":\"A ref update rules for a viewer.\",\"fields\":[{\"name\":\"allowsDeletions\",\"description\":\"Can this branch be deleted.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"allowsForcePushes\",\"description\":\"Are force pushes allowed on this branch.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pattern\",\"description\":\"Identifies the protection rule pattern.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiredApprovingReviewCount\",\"description\":\"Number of approving reviews required to update matching branches.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiredStatusCheckContexts\",\"description\":\"List of required status check contexts that must pass for commits to be accepted to matching branches.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresCodeOwnerReviews\",\"description\":\"Are reviews from code owners required to update matching branches.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresLinearHistory\",\"description\":\"Are merge commits prohibited from being pushed to this branch.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requiresSignatures\",\"description\":\"Are commits required to be signed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerAllowedToDismissReviews\",\"description\":\"Is the viewer allowed to dismiss reviews.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanPush\",\"description\":\"Can the viewer push to the branch\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReferencedEvent\",\"description\":\"Represents a 'referenced' event on a given `ReferencedSubject`.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"Identifies the commit associated with the 'referenced' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitRepository\",\"description\":\"Identifies the repository associated with the 'referenced' event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCrossRepository\",\"description\":\"Reference originated in a different repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDirectReference\",\"description\":\"Checks if the commit message itself references the subject. Can be false in the case of a commit comment reference.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"Object referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"ReferencedSubject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"ReferencedSubject\",\"description\":\"Any referencable object\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RegenerateEnterpriseIdentityProviderRecoveryCodesInput\",\"description\":\"Autogenerated input type of RegenerateEnterpriseIdentityProviderRecoveryCodes\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set an identity provider.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RegenerateEnterpriseIdentityProviderRecoveryCodesPayload\",\"description\":\"Autogenerated return type of RegenerateEnterpriseIdentityProviderRecoveryCodes\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"identityProvider\",\"description\":\"The identity provider for the enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseIdentityProvider\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RegenerateVerifiableDomainTokenInput\",\"description\":\"Autogenerated input type of RegenerateVerifiableDomainToken\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the verifiable domain to regenerate the verification token of.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RegenerateVerifiableDomainTokenPayload\",\"description\":\"Autogenerated return type of RegenerateVerifiableDomainToken\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"verificationToken\",\"description\":\"The verification token that was generated.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Release\",\"description\":\"A release contains the content for a release.\",\"fields\":[{\"name\":\"author\",\"description\":\"The author of the release\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The description of the release.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"descriptionHTML\",\"description\":\"The description of this release rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDraft\",\"description\":\"Whether or not the release is a draft\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isLatest\",\"description\":\"Whether or not the release is the latest releast\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPrerelease\",\"description\":\"Whether or not the release is a prerelease\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The title of the release.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies the date and time when the release was created.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"releaseAssets\",\"description\":\"List of releases assets which are dependent on this release.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"A list of names to filter the assets by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReleaseAssetConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository that the release belongs to.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this issue\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"shortDescriptionHTML\",\"description\":\"A description of the release, rendered to HTML without any links in it.\",\"args\":[{\"name\":\"limit\",\"description\":\"How many characters to return.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"200\"}],\"type\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tag\",\"description\":\"The Git tag the release points to\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tagCommit\",\"description\":\"The tag commit for this release.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tagName\",\"description\":\"The name of the release's Git tag\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this issue\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReleaseAsset\",\"description\":\"A release asset contains the content for a release asset.\",\"fields\":[{\"name\":\"contentType\",\"description\":\"The asset's content-type\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"downloadCount\",\"description\":\"The number of times this asset was downloaded\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"downloadUrl\",\"description\":\"Identifies the URL where you can download the release asset via the browser.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"Identifies the title of the release asset.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"release\",\"description\":\"Release that the asset is associated with\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Release\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"size\",\"description\":\"The size (in bytes) of the asset\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"uploadedBy\",\"description\":\"The user that performed the upload\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"Identifies the URL of the release asset.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReleaseAssetConnection\",\"description\":\"The connection type for ReleaseAsset.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReleaseAssetEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReleaseAsset\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReleaseAssetEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ReleaseAsset\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReleaseConnection\",\"description\":\"The connection type for Release.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReleaseEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Release\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReleaseEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Release\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReleaseOrder\",\"description\":\"Ways in which lists of releases can be ordered upon return.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order releases by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ReleaseOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order releases by the specified field.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ReleaseOrderField\",\"description\":\"Properties by which release connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order releases by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NAME\",\"description\":\"Order releases alphabetically by name\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveAssigneesFromAssignableInput\",\"description\":\"Autogenerated input type of RemoveAssigneesFromAssignable\",\"fields\":null,\"inputFields\":[{\"name\":\"assignableId\",\"description\":\"The id of the assignable object to remove assignees from.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"assigneeIds\",\"description\":\"The id of users to remove as assignees.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveAssigneesFromAssignablePayload\",\"description\":\"Autogenerated return type of RemoveAssigneesFromAssignable\",\"fields\":[{\"name\":\"assignable\",\"description\":\"The item that was unassigned.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Assignable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveEnterpriseAdminInput\",\"description\":\"Autogenerated input type of RemoveEnterpriseAdmin\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The Enterprise ID from which to remove the administrator.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"login\",\"description\":\"The login of the user to remove as an administrator.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveEnterpriseAdminPayload\",\"description\":\"Autogenerated return type of RemoveEnterpriseAdmin\",\"fields\":[{\"name\":\"admin\",\"description\":\"The user who was removed as an administrator.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The updated enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of removing an administrator.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewer\",\"description\":\"The viewer performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveEnterpriseIdentityProviderInput\",\"description\":\"Autogenerated input type of RemoveEnterpriseIdentityProvider\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise from which to remove the identity provider.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveEnterpriseIdentityProviderPayload\",\"description\":\"Autogenerated return type of RemoveEnterpriseIdentityProvider\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"identityProvider\",\"description\":\"The identity provider that was removed from the enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseIdentityProvider\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveEnterpriseOrganizationInput\",\"description\":\"Autogenerated input type of RemoveEnterpriseOrganization\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise from which the organization should be removed.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"organizationId\",\"description\":\"The ID of the organization to remove from the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveEnterpriseOrganizationPayload\",\"description\":\"Autogenerated return type of RemoveEnterpriseOrganization\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The updated enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The organization that was removed from the enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewer\",\"description\":\"The viewer performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveEnterpriseSupportEntitlementInput\",\"description\":\"Autogenerated input type of RemoveEnterpriseSupportEntitlement\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the Enterprise which the admin belongs to.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"login\",\"description\":\"The login of a member who will lose the support entitlement.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveEnterpriseSupportEntitlementPayload\",\"description\":\"Autogenerated return type of RemoveEnterpriseSupportEntitlement\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of removing the support entitlement.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveLabelsFromLabelableInput\",\"description\":\"Autogenerated input type of RemoveLabelsFromLabelable\",\"fields\":null,\"inputFields\":[{\"name\":\"labelableId\",\"description\":\"The id of the Labelable to remove labels from.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"labelIds\",\"description\":\"The ids of labels to remove.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveLabelsFromLabelablePayload\",\"description\":\"Autogenerated return type of RemoveLabelsFromLabelable\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"labelable\",\"description\":\"The Labelable the labels were removed from.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Labelable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveOutsideCollaboratorInput\",\"description\":\"Autogenerated input type of RemoveOutsideCollaborator\",\"fields\":null,\"inputFields\":[{\"name\":\"userId\",\"description\":\"The ID of the outside collaborator to remove.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"organizationId\",\"description\":\"The ID of the organization to remove the outside collaborator from.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveOutsideCollaboratorPayload\",\"description\":\"Autogenerated return type of RemoveOutsideCollaborator\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"removedUser\",\"description\":\"The user that was removed as an outside collaborator.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveReactionInput\",\"description\":\"Autogenerated input type of RemoveReaction\",\"fields\":null,\"inputFields\":[{\"name\":\"subjectId\",\"description\":\"The Node ID of the subject to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"The name of the emoji reaction to remove.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveReactionPayload\",\"description\":\"Autogenerated return type of RemoveReaction\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reaction\",\"description\":\"The reaction object.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Reaction\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"The reactable subject.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RemoveStarInput\",\"description\":\"Autogenerated input type of RemoveStar\",\"fields\":null,\"inputFields\":[{\"name\":\"starrableId\",\"description\":\"The Starrable ID to unstar.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemoveStarPayload\",\"description\":\"Autogenerated return type of RemoveStar\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"starrable\",\"description\":\"The starrable.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Starrable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RemovedFromProjectEvent\",\"description\":\"Represents a 'removed_from_project' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RenamedTitleEvent\",\"description\":\"Represents a 'renamed' event on a given issue or pull request\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"currentTitle\",\"description\":\"Identifies the current title of the issue or pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"previousTitle\",\"description\":\"Identifies the previous title of the issue or pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"Subject that was renamed.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"RenamedTitleSubject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"RenamedTitleSubject\",\"description\":\"An object which has a renamable title\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReopenIssueInput\",\"description\":\"Autogenerated input type of ReopenIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"issueId\",\"description\":\"ID of the issue to be opened.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReopenIssuePayload\",\"description\":\"Autogenerated return type of ReopenIssue\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The issue that was opened.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReopenPullRequestInput\",\"description\":\"Autogenerated input type of ReopenPullRequest\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"ID of the pull request to be reopened.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReopenPullRequestPayload\",\"description\":\"Autogenerated return type of ReopenPullRequest\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request that was reopened.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReopenedEvent\",\"description\":\"Represents a 'reopened' event on any `Closable`.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"closable\",\"description\":\"Object that was reopened.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Closable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAccessAuditEntry\",\"description\":\"Audit log entry for a repo.access event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"visibility\",\"description\":\"The visibility of the repository\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepoAccessAuditEntryVisibility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepoAccessAuditEntryVisibility\",\"description\":\"The privacy of a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"INTERNAL\",\"description\":\"The repository is visible only to users in the same business.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"The repository is visible only to those with explicit access.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"The repository is visible to everyone.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddMemberAuditEntry\",\"description\":\"Audit log entry for a repo.add_member event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"visibility\",\"description\":\"The visibility of the repository\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepoAddMemberAuditEntryVisibility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepoAddMemberAuditEntryVisibility\",\"description\":\"The privacy of a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"INTERNAL\",\"description\":\"The repository is visible only to users in the same business.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"The repository is visible only to those with explicit access.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"The repository is visible to everyone.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddTopicAuditEntry\",\"description\":\"Audit log entry for a repo.add_topic event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topic\",\"description\":\"The name of the topic added to the repository\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topicName\",\"description\":\"The name of the topic added to the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"TopicAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoArchivedAuditEntry\",\"description\":\"Audit log entry for a repo.archived event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"visibility\",\"description\":\"The visibility of the repository\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepoArchivedAuditEntryVisibility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepoArchivedAuditEntryVisibility\",\"description\":\"The privacy of a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"INTERNAL\",\"description\":\"The repository is visible only to users in the same business.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"The repository is visible only to those with explicit access.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"The repository is visible to everyone.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoChangeMergeSettingAuditEntry\",\"description\":\"Audit log entry for a repo.change_merge_setting event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isEnabled\",\"description\":\"Whether the change was to enable (true) or disable (false) the merge type\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeType\",\"description\":\"The merge method affected by the change\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepoChangeMergeSettingAuditEntryMergeType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepoChangeMergeSettingAuditEntryMergeType\",\"description\":\"The merge options available for pull requests to this repository.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"MERGE\",\"description\":\"The pull request is added to the base branch in a merge commit.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REBASE\",\"description\":\"Commits from the pull request are added onto the base branch individually without a merge commit.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SQUASH\",\"description\":\"The pull request's commits are squashed into a single commit before they are merged to the base branch.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableAnonymousGitAccessAuditEntry\",\"description\":\"Audit log entry for a repo.config.disable_anonymous_git_access event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableCollaboratorsOnlyAuditEntry\",\"description\":\"Audit log entry for a repo.config.disable_collaborators_only event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableContributorsOnlyAuditEntry\",\"description\":\"Audit log entry for a repo.config.disable_contributors_only event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableSockpuppetDisallowedAuditEntry\",\"description\":\"Audit log entry for a repo.config.disable_sockpuppet_disallowed event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableAnonymousGitAccessAuditEntry\",\"description\":\"Audit log entry for a repo.config.enable_anonymous_git_access event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableCollaboratorsOnlyAuditEntry\",\"description\":\"Audit log entry for a repo.config.enable_collaborators_only event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableContributorsOnlyAuditEntry\",\"description\":\"Audit log entry for a repo.config.enable_contributors_only event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableSockpuppetDisallowedAuditEntry\",\"description\":\"Audit log entry for a repo.config.enable_sockpuppet_disallowed event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigLockAnonymousGitAccessAuditEntry\",\"description\":\"Audit log entry for a repo.config.lock_anonymous_git_access event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigUnlockAnonymousGitAccessAuditEntry\",\"description\":\"Audit log entry for a repo.config.unlock_anonymous_git_access event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoCreateAuditEntry\",\"description\":\"Audit log entry for a repo.create event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"forkParentName\",\"description\":\"The name of the parent repository for this forked repository.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"forkSourceName\",\"description\":\"The name of the root repository for this network.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"visibility\",\"description\":\"The visibility of the repository\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepoCreateAuditEntryVisibility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepoCreateAuditEntryVisibility\",\"description\":\"The privacy of a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"INTERNAL\",\"description\":\"The repository is visible only to users in the same business.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"The repository is visible only to those with explicit access.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"The repository is visible to everyone.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoDestroyAuditEntry\",\"description\":\"Audit log entry for a repo.destroy event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"visibility\",\"description\":\"The visibility of the repository\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepoDestroyAuditEntryVisibility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepoDestroyAuditEntryVisibility\",\"description\":\"The privacy of a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"INTERNAL\",\"description\":\"The repository is visible only to users in the same business.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"The repository is visible only to those with explicit access.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"The repository is visible to everyone.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveMemberAuditEntry\",\"description\":\"Audit log entry for a repo.remove_member event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"visibility\",\"description\":\"The visibility of the repository\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepoRemoveMemberAuditEntryVisibility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepoRemoveMemberAuditEntryVisibility\",\"description\":\"The privacy of a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"INTERNAL\",\"description\":\"The repository is visible only to users in the same business.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"The repository is visible only to those with explicit access.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"The repository is visible to everyone.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveTopicAuditEntry\",\"description\":\"Audit log entry for a repo.remove_topic event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topic\",\"description\":\"The name of the topic added to the repository\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topicName\",\"description\":\"The name of the topic added to the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"TopicAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"ReportedContentClassifiers\",\"description\":\"The reasons a piece of content can be reported or minimized.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"SPAM\",\"description\":\"A spammy piece of content\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ABUSE\",\"description\":\"An abusive or harassing piece of content\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OFF_TOPIC\",\"description\":\"An irrelevant piece of content\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OUTDATED\",\"description\":\"An outdated piece of content\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"DUPLICATE\",\"description\":\"A duplicated piece of content\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RESOLVED\",\"description\":\"The content has been resolved\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"description\":\"A repository contains the content for a project.\",\"fields\":[{\"name\":\"assignableUsers\",\"description\":\"A list of users that can be assigned to issues in this repository.\",\"args\":[{\"name\":\"query\",\"description\":\"Filters users with query on user name and login\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"branchProtectionRules\",\"description\":\"A list of branch protection rules for this repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRuleConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"codeOfConduct\",\"description\":\"Returns the code of conduct for this repository\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CodeOfConduct\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"collaborators\",\"description\":\"A list of collaborators associated with the repository.\",\"args\":[{\"name\":\"affiliation\",\"description\":\"Collaborators affiliation level with a repository.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"CollaboratorAffiliation\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"Filters users with query on user name and login\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryCollaboratorConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitComments\",\"description\":\"A list of commit comments associated with the repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"contactLinks\",\"description\":\"Returns a list of contact links associated to the repository\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryContactLink\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"defaultBranchRef\",\"description\":\"The Ref associated with the repository's default branch.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deleteBranchOnMerge\",\"description\":\"Whether or not branches are automatically deleted when merged in this repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deployKeys\",\"description\":\"A list of deploy keys that are on this repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"DeployKeyConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deployments\",\"description\":\"Deployments associated with the repository\",\"args\":[{\"name\":\"environments\",\"description\":\"Environments to list deployments for\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for deployments returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"DeploymentOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"DeploymentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The description of the repository.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"descriptionHTML\",\"description\":\"The description of the repository rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"diskUsage\",\"description\":\"The number of kilobytes this repository occupies on disk.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"forkCount\",\"description\":\"Returns how many forks there are of this repository in the whole network.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"forks\",\"description\":\"A list of direct forked repositories.\",\"args\":[{\"name\":\"privacy\",\"description\":\"If non-null, filters repositories according to privacy\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPrivacy\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"affiliations\",\"description\":\"Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"ownerAffiliations\",\"description\":\"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":\"[OWNER, COLLABORATOR]\"},{\"name\":\"isLocked\",\"description\":\"If non-null, filters repositories according to whether they have been locked\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"fundingLinks\",\"description\":\"The funding links for this repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"FundingLink\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasIssuesEnabled\",\"description\":\"Indicates if the repository has issues feature enabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasProjectsEnabled\",\"description\":\"Indicates if the repository has the Projects feature enabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasWikiEnabled\",\"description\":\"Indicates if the repository has wiki feature enabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"homepageUrl\",\"description\":\"The repository's URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"interactionAbility\",\"description\":\"The interaction ability settings for this repository.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryInteractionAbility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isArchived\",\"description\":\"Indicates if the repository is unmaintained.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isBlankIssuesEnabled\",\"description\":\"Returns true if blank issue creation is allowed\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDisabled\",\"description\":\"Returns whether or not this repository disabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isEmpty\",\"description\":\"Returns whether or not this repository is empty.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isFork\",\"description\":\"Identifies if the repository is a fork.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isInOrganization\",\"description\":\"Indicates if a repository is either owned by an organization, or is a private fork of an organization repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isLocked\",\"description\":\"Indicates if the repository has been locked or not.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isMirror\",\"description\":\"Identifies if the repository is a mirror.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPrivate\",\"description\":\"Identifies if the repository is private.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSecurityPolicyEnabled\",\"description\":\"Returns true if this repository has a security policy\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isTemplate\",\"description\":\"Identifies if the repository is a template that can be used to generate new repositories.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isUserConfigurationRepository\",\"description\":\"Is this repository a user configuration repository?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"Returns a single issue from the current repository by number.\",\"args\":[{\"name\":\"number\",\"description\":\"The number for the issue to be returned.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issueOrPullRequest\",\"description\":\"Returns a single issue-like object from the current repository by number.\",\"args\":[{\"name\":\"number\",\"description\":\"The number for the issue to be returned.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"UNION\",\"name\":\"IssueOrPullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issueTemplates\",\"description\":\"Returns a list of issue templates associated to the repository\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueTemplate\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issues\",\"description\":\"A list of issues that have been opened in the repository.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for issues returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the issues by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"filterBy\",\"description\":\"Filtering options for issues returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueFilters\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"label\",\"description\":\"Returns a single label by name\",\"args\":[{\"name\":\"name\",\"description\":\"Label name\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Label\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"labels\",\"description\":\"A list of labels associated with the repository.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for labels returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"LabelOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: ASC}\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"If provided, searches labels by name and description.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"LabelConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"languages\",\"description\":\"A list containing a breakdown of the language composition of the repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"LanguageOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"LanguageConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"latestRelease\",\"description\":\"Get the latest release for the repository if one exists.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Release\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"licenseInfo\",\"description\":\"The license associated with the repository\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"License\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lockReason\",\"description\":\"The reason the repository has been locked.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryLockReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mentionableUsers\",\"description\":\"A list of Users that can be mentioned in the context of the repository.\",\"args\":[{\"name\":\"query\",\"description\":\"Filters users with query on user name and login\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mergeCommitAllowed\",\"description\":\"Whether or not PRs are merged with a merge commit on this repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"milestone\",\"description\":\"Returns a single milestone from the current repository by number.\",\"args\":[{\"name\":\"number\",\"description\":\"The number for the milestone to be returned.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"milestones\",\"description\":\"A list of milestones associated with the repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"Filter by the state of the milestones.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"MilestoneState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for milestones.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"MilestoneOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"Filters milestones with a query on the title\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"MilestoneConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mirrorUrl\",\"description\":\"The repository's original mirror URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nameWithOwner\",\"description\":\"The repository's name with owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"object\",\"description\":\"A Git object in the repository\",\"args\":[{\"name\":\"oid\",\"description\":\"The Git object ID\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"expression\",\"description\":\"A Git revision expression suitable for rev-parse\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"openGraphImageUrl\",\"description\":\"The image used to represent this repository in Open Graph data.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The User owner of the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"RepositoryOwner\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"packages\",\"description\":\"A list of packages under the owner.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"names\",\"description\":\"Find packages by their names.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"repositoryId\",\"description\":\"Find packages in a repository by ID.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"packageType\",\"description\":\"Filter registry package by type.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PackageType\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering of the returned packages.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parent\",\"description\":\"The repository parent, if this is a fork.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnedIssues\",\"description\":\"A list of pinned issues for this repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PinnedIssueConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"primaryLanguage\",\"description\":\"The primary language of the repository's code.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Language\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"Find project by number.\",\"args\":[{\"name\":\"number\",\"description\":\"The project number to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projects\",\"description\":\"A list of projects under the owner.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for projects returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ProjectOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"search\",\"description\":\"Query to search projects by, currently only searching by name.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the projects by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectsResourcePath\",\"description\":\"The HTTP path listing the repository's projects\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectsUrl\",\"description\":\"The HTTP URL listing the repository's projects\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"Returns a single pull request from the current repository by number.\",\"args\":[{\"name\":\"number\",\"description\":\"The number for the pull request to be returned.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequests\",\"description\":\"A list of pull requests that have been opened in the repository.\",\"args\":[{\"name\":\"states\",\"description\":\"A list of states to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"headRefName\",\"description\":\"The head ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"baseRefName\",\"description\":\"The base ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pull requests returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pushedAt\",\"description\":\"Identifies when the repository was last pushed to.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"rebaseMergeAllowed\",\"description\":\"Whether or not rebase-merging is enabled on this repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ref\",\"description\":\"Fetch a given ref from the repository\",\"args\":[{\"name\":\"qualifiedName\",\"description\":\"The ref to retrieve. Fully qualified matches are checked in order (`refs/heads/master`) before falling back onto checks for short name matches (`master`).\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"refs\",\"description\":\"Fetch a list of refs from the repository\",\"args\":[{\"name\":\"query\",\"description\":\"Filters refs with query on name\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"refPrefix\",\"description\":\"A ref name prefix like `refs/heads/`, `refs/tags/`, etc.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"DEPRECATED: use orderBy. The ordering direction.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for refs returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RefOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RefConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"release\",\"description\":\"Lookup a single release given various criteria.\",\"args\":[{\"name\":\"tagName\",\"description\":\"The name of the Tag the Release was created from\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Release\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"releases\",\"description\":\"List of releases which are dependent on this repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReleaseOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReleaseConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryTopics\",\"description\":\"A list of applied repository-topic associations for this repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopicConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"securityPolicyUrl\",\"description\":\"The security policy URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"shortDescriptionHTML\",\"description\":\"A description of the repository, rendered to HTML without any links in it.\",\"args\":[{\"name\":\"limit\",\"description\":\"How many characters to return.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"200\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"squashMergeAllowed\",\"description\":\"Whether or not squash-merging is enabled on this repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sshUrl\",\"description\":\"The SSH URL to clone this repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitSSHRemote\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"stargazerCount\",\"description\":\"Returns a count of how many stargazers there are on this object\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"stargazers\",\"description\":\"A list of users who have starred this starrable.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"StarOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StargazerConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"submodules\",\"description\":\"Returns a list of all submodules in this repository parsed from the .gitmodules file as of the default branch's HEAD commit.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SubmoduleConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tempCloneToken\",\"description\":\"Temporary authentication token for cloning this repository.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"templateRepository\",\"description\":\"The repository from which this repository was generated, if any.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"usesCustomOpenGraphImage\",\"description\":\"Whether this repository has a custom image to use with Open Graph as opposed to being represented by the owner's avatar.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanAdminister\",\"description\":\"Indicates whether the viewer has admin permissions on this repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanCreateProjects\",\"description\":\"Can the current viewer create new projects on this owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSubscribe\",\"description\":\"Check if the viewer is able to change their subscription status for the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdateTopics\",\"description\":\"Indicates whether the viewer can update the topics of this repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDefaultCommitEmail\",\"description\":\"The last commit email for the viewer.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDefaultMergeMethod\",\"description\":\"The last used merge method by the viewer or the default for the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestMergeMethod\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerHasStarred\",\"description\":\"Returns a boolean indicating whether the viewing user has starred this starrable.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerPermission\",\"description\":\"The users permission level on the repository. Will return null if authenticated as an GitHub App.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPermission\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerPossibleCommitEmails\",\"description\":\"A list of emails this viewer can commit with.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerSubscription\",\"description\":\"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"vulnerabilityAlerts\",\"description\":\"A list of vulnerability alerts that are on this repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlertConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"watchers\",\"description\":\"A list of users watching the repository.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"ProjectOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"PackageOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Starrable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryInfo\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"description\":\"The affiliation of a user to a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"OWNER\",\"description\":\"Repositories that are owned by the authenticated user.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COLLABORATOR\",\"description\":\"Repositories that the user has been added to as a collaborator.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ORGANIZATION_MEMBER\",\"description\":\"Repositories that the user has access to through being a member of an organization. This includes every repository on every team that the user is on.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"description\":\"Metadata for an audit entry with action repo.*\",\"fields\":[{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipRepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingDisableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PrivateRepositoryForkingEnableAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoAddTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoArchivedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoChangeMergeSettingAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigDisableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableCollaboratorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableContributorsOnlyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigEnableSockpuppetDisallowedAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigLockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoConfigUnlockAnonymousGitAccessAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoCreateAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoDestroyAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddRepositoryAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveRepositoryAuditEntry\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"RepositoryCollaboratorConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryCollaboratorEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryCollaboratorEdge\",\"description\":\"Represents a user who is a collaborator of a repository.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permission\",\"description\":\"The permission the user has on the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPermission\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permissionSources\",\"description\":\"A list of sources for the user's access to the repository.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PermissionSource\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryConnection\",\"description\":\"A list of repositories owned by the subject.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalDiskUsage\",\"description\":\"The total size in kilobytes of all repositories in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryContactLink\",\"description\":\"A repository contact link.\",\"fields\":[{\"name\":\"about\",\"description\":\"The contact link purpose.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The contact link name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The contact link URL.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryContributionType\",\"description\":\"The reason a repository is listed as 'contributed'.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"COMMIT\",\"description\":\"Created a commit\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ISSUE\",\"description\":\"Created an issue\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PULL_REQUEST\",\"description\":\"Created a pull request\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REPOSITORY\",\"description\":\"Created the repository\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PULL_REQUEST_REVIEW\",\"description\":\"Reviewed a pull request\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryInfo\",\"description\":\"A subset of repository info.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The description of the repository.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"descriptionHTML\",\"description\":\"The description of the repository rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"forkCount\",\"description\":\"Returns how many forks there are of this repository in the whole network.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasIssuesEnabled\",\"description\":\"Indicates if the repository has issues feature enabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasProjectsEnabled\",\"description\":\"Indicates if the repository has the Projects feature enabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasWikiEnabled\",\"description\":\"Indicates if the repository has wiki feature enabled.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"homepageUrl\",\"description\":\"The repository's URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isArchived\",\"description\":\"Indicates if the repository is unmaintained.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isFork\",\"description\":\"Identifies if the repository is a fork.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isInOrganization\",\"description\":\"Indicates if a repository is either owned by an organization, or is a private fork of an organization repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isLocked\",\"description\":\"Indicates if the repository has been locked or not.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isMirror\",\"description\":\"Identifies if the repository is a mirror.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPrivate\",\"description\":\"Identifies if the repository is private.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isTemplate\",\"description\":\"Identifies if the repository is a template that can be used to generate new repositories.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"licenseInfo\",\"description\":\"The license associated with the repository\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"License\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lockReason\",\"description\":\"The reason the repository has been locked.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryLockReason\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mirrorUrl\",\"description\":\"The repository's original mirror URL.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nameWithOwner\",\"description\":\"The repository's name with owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"openGraphImageUrl\",\"description\":\"The image used to represent this repository in Open Graph data.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The User owner of the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"RepositoryOwner\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pushedAt\",\"description\":\"Identifies when the repository was last pushed to.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"shortDescriptionHTML\",\"description\":\"A description of the repository, rendered to HTML without any links in it.\",\"args\":[{\"name\":\"limit\",\"description\":\"How many characters to return.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"200\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"usesCustomOpenGraphImage\",\"description\":\"Whether this repository has a custom image to use with Open Graph as opposed to being represented by the owner's avatar.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"RepositoryInteractionAbility\",\"description\":\"Repository interaction limit that applies to this object.\",\"fields\":[{\"name\":\"expiresAt\",\"description\":\"The time the currently active limit expires.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"limit\",\"description\":\"The current limit that is enabled on this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"origin\",\"description\":\"The origin of the currently active interaction limit.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimitOrigin\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimit\",\"description\":\"A repository interaction limit.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"EXISTING_USERS\",\"description\":\"Users that have recently created their account will be unable to interact with the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CONTRIBUTORS_ONLY\",\"description\":\"Users that have not previously committed to a repository’s default branch will be unable to interact with the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COLLABORATORS_ONLY\",\"description\":\"Users that are not collaborators will not be able to interact with the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NO_LIMIT\",\"description\":\"No interaction limits are enabled.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimitExpiry\",\"description\":\"The length for a repository interaction limit to be enabled for.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ONE_DAY\",\"description\":\"The interaction limit will expire after 1 day.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"THREE_DAYS\",\"description\":\"The interaction limit will expire after 3 days.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ONE_WEEK\",\"description\":\"The interaction limit will expire after 1 week.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ONE_MONTH\",\"description\":\"The interaction limit will expire after 1 month.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SIX_MONTHS\",\"description\":\"The interaction limit will expire after 6 months.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimitOrigin\",\"description\":\"Indicates where an interaction limit is configured.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"REPOSITORY\",\"description\":\"A limit that is configured at the repository level.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ORGANIZATION\",\"description\":\"A limit that is configured at the organization level.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"USER\",\"description\":\"A limit that is configured at the user-wide level.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryInvitation\",\"description\":\"An invitation for a user to be added to a repository.\",\"fields\":[{\"name\":\"email\",\"description\":\"The email address that received the invitation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitee\",\"description\":\"The user who received the invitation.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"inviter\",\"description\":\"The user who created the invitation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permalink\",\"description\":\"The permalink for this repository invitation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permission\",\"description\":\"The permission granted on this repository by this invitation.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPermission\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The Repository the user is invited to.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"RepositoryInfo\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryInvitationConnection\",\"description\":\"The connection type for RepositoryInvitation.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryInvitationEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryInvitation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryInvitationEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryInvitation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryInvitationOrder\",\"description\":\"Ordering options for repository invitation connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order repository invitations by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInvitationOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryInvitationOrderField\",\"description\":\"Properties by which repository invitation connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order repository invitations by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INVITEE_LOGIN\",\"description\":\"Order repository invitations by invitee login\",\"isDeprecated\":true,\"deprecationReason\":\"`INVITEE_LOGIN` is no longer a valid field value. Repository invitations can now be associated with an email, not only an invitee. Removal on 2020-10-01 UTC.\"}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryLockReason\",\"description\":\"The possible reasons a given repository could be in a locked state.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"MOVING\",\"description\":\"The repository is locked due to a move.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"BILLING\",\"description\":\"The repository is locked due to a billing related reason.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RENAME\",\"description\":\"The repository is locked due to a rename.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MIGRATING\",\"description\":\"The repository is locked due to a migration.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"description\":\"Represents a object that belongs to a repository.\",\"fields\":[{\"name\":\"repository\",\"description\":\"The repository associated with this node.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CommitCommentThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommitCommentThread\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlert\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"description\":\"Ordering options for repository connections\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order repositories by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryOrderField\",\"description\":\"Properties by which repository connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order repositories by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UPDATED_AT\",\"description\":\"Order repositories by update time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUSHED_AT\",\"description\":\"Order repositories by push time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NAME\",\"description\":\"Order repositories by name\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"STARGAZERS\",\"description\":\"Order repositories by number of stargazers\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryOwner\",\"description\":\"Represents an owner of a Repository.\",\"fields\":[{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the owner's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"The username used to login.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositories\",\"description\":\"A list of repositories that the user owns.\",\"args\":[{\"name\":\"privacy\",\"description\":\"If non-null, filters repositories according to privacy\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPrivacy\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"affiliations\",\"description\":\"Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"ownerAffiliations\",\"description\":\"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":\"[OWNER, COLLABORATOR]\"},{\"name\":\"isLocked\",\"description\":\"If non-null, filters repositories according to whether they have been locked\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isFork\",\"description\":\"If non-null, filters repositories according to whether they are forks of another repository\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"Find Repository.\",\"args\":[{\"name\":\"name\",\"description\":\"Name of Repository to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP URL for the owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for the owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"ENUM\",\"name\":\"RepositoryPermission\",\"description\":\"The access level to a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ADMIN\",\"description\":\"Can read, clone, and push to this repository. Can also manage issues, pull requests, and repository settings, including adding collaborators\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MAINTAIN\",\"description\":\"Can read, clone, and push to this repository. They can also manage issues, pull requests, and some repository settings\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WRITE\",\"description\":\"Can read, clone, and push to this repository. Can also manage issues and pull requests\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TRIAGE\",\"description\":\"Can read and clone this repository. Can also manage issues and pull requests\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"READ\",\"description\":\"Can read and clone this repository. Can also open and comment on issues and pull requests\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryPrivacy\",\"description\":\"The privacy of a repository\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PUBLIC\",\"description\":\"Public\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"Private\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopic\",\"description\":\"A repository-topic connects a repository to a topic.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this repository-topic.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topic\",\"description\":\"The topic.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this repository-topic.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopicConnection\",\"description\":\"The connection type for RepositoryTopic.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopicEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopic\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopicEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopic\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RepositoryVisibility\",\"description\":\"The repository's visibility level.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PRIVATE\",\"description\":\"The repository is visible only to those with explicit access.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUBLIC\",\"description\":\"The repository is visible to everyone.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INTERNAL\",\"description\":\"The repository is visible only to users in the same business.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeDisableAuditEntry\",\"description\":\"Audit log entry for a repository_visibility_change.disable event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVisibilityChangeEnableAuditEntry\",\"description\":\"Audit log entry for a repository_visibility_change.enable event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseResourcePath\",\"description\":\"The HTTP path for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseSlug\",\"description\":\"The slug of the enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterpriseUrl\",\"description\":\"The HTTP URL for this enterprise.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"EnterpriseAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlert\",\"description\":\"A alert for a repository with an affected vulnerability.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"When was the alert created?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dismissReason\",\"description\":\"The reason the alert was dismissed\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dismissedAt\",\"description\":\"When was the alert dismissed?\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dismisser\",\"description\":\"The user who dismissed the alert\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The associated repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"securityAdvisory\",\"description\":\"The associated security advisory\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisory\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"securityVulnerability\",\"description\":\"The associated security vulnerability\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerability\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"vulnerableManifestFilename\",\"description\":\"The vulnerable manifest filename\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"vulnerableManifestPath\",\"description\":\"The vulnerable manifest path\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"vulnerableRequirements\",\"description\":\"The vulnerable requirements\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryNode\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlertConnection\",\"description\":\"The connection type for RepositoryVulnerabilityAlert.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlertEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlert\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlertEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryVulnerabilityAlert\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RequestReviewsInput\",\"description\":\"Autogenerated input type of RequestReviews\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"The Node ID of the pull request to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"userIds\",\"description\":\"The Node IDs of the user to request.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"teamIds\",\"description\":\"The Node IDs of the team to request.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"union\",\"description\":\"Add users to the set rather than replace.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RequestReviewsPayload\",\"description\":\"Autogenerated return type of RequestReviews\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The pull request that is getting requests.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requestedReviewersEdge\",\"description\":\"The edge from the pull request to the requested reviewers.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserEdge\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"RequestableCheckStatusState\",\"description\":\"The possible states that can be requested when creating a check run.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"QUEUED\",\"description\":\"The check suite or run has been queued.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"IN_PROGRESS\",\"description\":\"The check suite or run is in progress.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMPLETED\",\"description\":\"The check suite or run has been completed.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"WAITING\",\"description\":\"The check suite or run is in waiting state.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"RequestedReviewer\",\"description\":\"Types that can be requested reviewers.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Mannequin\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"INTERFACE\",\"name\":\"RequirableByPullRequest\",\"description\":\"Represents a type that can be required by a pull request for merging.\",\"fields\":[{\"name\":\"isRequired\",\"description\":\"Whether this is required to pass before merging for a specific pull request.\",\"args\":[{\"name\":\"pullRequestId\",\"description\":\"The id of the pull request this is required for\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pullRequestNumber\",\"description\":\"The number of the pull request this is required for\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"StatusContext\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"RerequestCheckSuiteInput\",\"description\":\"Autogenerated input type of RerequestCheckSuite\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"checkSuiteId\",\"description\":\"The Node ID of the check suite.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RerequestCheckSuitePayload\",\"description\":\"Autogenerated return type of RerequestCheckSuite\",\"fields\":[{\"name\":\"checkSuite\",\"description\":\"The requested check suite.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckSuite\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"ResolveReviewThreadInput\",\"description\":\"Autogenerated input type of ResolveReviewThread\",\"fields\":null,\"inputFields\":[{\"name\":\"threadId\",\"description\":\"The ID of the thread to resolve\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ResolveReviewThreadPayload\",\"description\":\"Autogenerated return type of ResolveReviewThread\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"thread\",\"description\":\"The thread to resolve.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"RestrictedContribution\",\"description\":\"Represents a private contribution a user made on GitHub.\",\"fields\":[{\"name\":\"isRestricted\",\"description\":\"Whether this contribution is associated with a record you do not have access to. For\\nexample, your own 'first issue' contribution may have been made on a repository you can no\\nlonger access.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"occurredAt\",\"description\":\"When this contribution was made.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this contribution.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who made this contribution.\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Contribution\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissalAllowance\",\"description\":\"A team or user who has the ability to dismiss a review on a protected branch.\",\"fields\":[{\"name\":\"actor\",\"description\":\"The actor that can dismiss.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"ReviewDismissalAllowanceActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"branchProtectionRule\",\"description\":\"Identifies the branch protection rule associated with the allowed user or team.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"ReviewDismissalAllowanceActor\",\"description\":\"Types that can be an actor.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissalAllowanceConnection\",\"description\":\"The connection type for ReviewDismissalAllowance.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissalAllowanceEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissalAllowance\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissalAllowanceEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissalAllowance\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissedEvent\",\"description\":\"Represents a 'review_dismissed' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dismissalMessage\",\"description\":\"Identifies the optional message associated with the 'review_dismissed' event.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dismissalMessageHTML\",\"description\":\"Identifies the optional message associated with the event, rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"previousReviewState\",\"description\":\"Identifies the previous state of the review with the 'review_dismissed' event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestCommit\",\"description\":\"Identifies the commit which caused the review to become stale.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this review dismissed event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"review\",\"description\":\"Identifies the review associated with the 'review_dismissed' event.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this review dismissed event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequest\",\"description\":\"A request for a user to review a pull request.\",\"fields\":[{\"name\":\"asCodeOwner\",\"description\":\"Whether this request was created for a code owner\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"Identifies the pull request associated with this review request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requestedReviewer\",\"description\":\"The reviewer that is requested.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"RequestedReviewer\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestConnection\",\"description\":\"The connection type for ReviewRequest.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReviewRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ReviewRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestRemovedEvent\",\"description\":\"Represents an 'review_request_removed' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requestedReviewer\",\"description\":\"Identifies the reviewer whose review request was removed.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"RequestedReviewer\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewRequestedEvent\",\"description\":\"Represents an 'review_requested' event on a given pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"PullRequest referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"requestedReviewer\",\"description\":\"Identifies the reviewer whose review was requested.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"RequestedReviewer\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewStatusHovercardContext\",\"description\":\"A hovercard context with a message describing the current code review state of the pull\\nrequest.\\n\",\"fields\":[{\"name\":\"message\",\"description\":\"A string describing this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"octicon\",\"description\":\"An octicon to accompany this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reviewDecision\",\"description\":\"The current status of the pull request with respect to code review.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewDecision\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"HovercardContext\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SamlDigestAlgorithm\",\"description\":\"The possible digest algorithms used to sign SAML requests for an identity provider.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"SHA1\",\"description\":\"SHA1\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SHA256\",\"description\":\"SHA256\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SHA384\",\"description\":\"SHA384\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SHA512\",\"description\":\"SHA512\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SamlSignatureAlgorithm\",\"description\":\"The possible signature algorithms used to sign SAML requests for a Identity Provider.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"RSA_SHA1\",\"description\":\"RSA-SHA1\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RSA_SHA256\",\"description\":\"RSA-SHA256\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RSA_SHA384\",\"description\":\"RSA-SHA384\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"RSA_SHA512\",\"description\":\"RSA-SHA512\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SavedReply\",\"description\":\"A Saved Reply is text a user can use to reply quickly.\",\"fields\":[{\"name\":\"body\",\"description\":\"The body of the saved reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The saved reply body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"The title of the saved reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user that saved this reply.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SavedReplyConnection\",\"description\":\"The connection type for SavedReply.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SavedReplyEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SavedReply\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SavedReplyEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SavedReply\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SavedReplyOrder\",\"description\":\"Ordering options for saved reply connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order saved replies by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SavedReplyOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SavedReplyOrderField\",\"description\":\"Properties by which saved reply connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"UPDATED_AT\",\"description\":\"Order saved reply by when they were updated.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"SearchResultItem\",\"description\":\"The results of a search.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"App\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MarketplaceListing\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"SearchResultItemConnection\",\"description\":\"A list of results that matched against a search query.\",\"fields\":[{\"name\":\"codeCount\",\"description\":\"The number of pieces of code that matched the search query.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SearchResultItemEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issueCount\",\"description\":\"The number of issues that matched the search query.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"SearchResultItem\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryCount\",\"description\":\"The number of repositories that matched the search query.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userCount\",\"description\":\"The number of users that matched the search query.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"wikiCount\",\"description\":\"The number of wiki pages that matched the search query.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SearchResultItemEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"SearchResultItem\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"textMatches\",\"description\":\"Text matches on the result found.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TextMatch\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SearchType\",\"description\":\"Represents the individual results of a search.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ISSUE\",\"description\":\"Returns results matching issues in repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"REPOSITORY\",\"description\":\"Returns results matching repositories.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"USER\",\"description\":\"Returns results matching users and organizations on GitHub.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisory\",\"description\":\"A GitHub Security Advisory\",\"fields\":[{\"name\":\"cvss\",\"description\":\"The CVSS associated with this advisory\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CVSS\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"cwes\",\"description\":\"CWEs associated with this Advisory\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CWEConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"This is a long plaintext description of the advisory\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ghsaId\",\"description\":\"The GitHub Security Advisory ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"identifiers\",\"description\":\"A list of identifiers for this advisory\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryIdentifier\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"notificationsPermalink\",\"description\":\"The permalink for the advisory's dependabot alerts page\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"origin\",\"description\":\"The organization that originated the advisory\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permalink\",\"description\":\"The permalink for the advisory\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"When the advisory was published\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"references\",\"description\":\"A list of references for this advisory\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryReference\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"severity\",\"description\":\"The severity of the advisory\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisorySeverity\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"summary\",\"description\":\"A short plaintext summary of the advisory\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"When the advisory was last updated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"vulnerabilities\",\"description\":\"Vulnerabilities associated with this Advisory\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for the returned topics.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SecurityVulnerabilityOrder\",\"ofType\":null},\"defaultValue\":\"{field: UPDATED_AT, direction: DESC}\"},{\"name\":\"ecosystem\",\"description\":\"An ecosystem to filter vulnerabilities by.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryEcosystem\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"package\",\"description\":\"A package name to filter vulnerabilities by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"severities\",\"description\":\"A list of severities to filter vulnerabilities by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisorySeverity\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerabilityConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"withdrawnAt\",\"description\":\"When the advisory was withdrawn, if it has been withdrawn\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryConnection\",\"description\":\"The connection type for SecurityAdvisory.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisory\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryEcosystem\",\"description\":\"The possible ecosystems of a security vulnerability's package.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"RUBYGEMS\",\"description\":\"Ruby gems hosted at RubyGems.org\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NPM\",\"description\":\"JavaScript packages hosted at npmjs.com\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PIP\",\"description\":\"Python packages hosted at PyPI.org\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MAVEN\",\"description\":\"Java artifacts hosted at the Maven central repository\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NUGET\",\"description\":\".NET packages hosted at the NuGet Gallery\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"COMPOSER\",\"description\":\"PHP packages hosted at packagist.org\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisory\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryIdentifier\",\"description\":\"A GitHub Security Advisory Identifier\",\"fields\":[{\"name\":\"type\",\"description\":\"The identifier type, e.g. GHSA, CVE\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"value\",\"description\":\"The identifier\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SecurityAdvisoryIdentifierFilter\",\"description\":\"An advisory identifier to filter results on.\",\"fields\":null,\"inputFields\":[{\"name\":\"type\",\"description\":\"The identifier type.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryIdentifierType\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"value\",\"description\":\"The identifier string. Supports exact or partial matching.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryIdentifierType\",\"description\":\"Identifier formats available for advisories.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CVE\",\"description\":\"Common Vulnerabilities and Exposures Identifier.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"GHSA\",\"description\":\"GitHub Security Advisory ID.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SecurityAdvisoryOrder\",\"description\":\"Ordering options for security advisory connections\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order security advisories by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryOrderField\",\"description\":\"Properties by which security advisory connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PUBLISHED_AT\",\"description\":\"Order advisories by publication time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UPDATED_AT\",\"description\":\"Order advisories by update time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryPackage\",\"description\":\"An individual package\",\"fields\":[{\"name\":\"ecosystem\",\"description\":\"The ecosystem the package belongs to, e.g. RUBYGEMS, NPM\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisoryEcosystem\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The package name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryPackageVersion\",\"description\":\"An individual package version\",\"fields\":[{\"name\":\"identifier\",\"description\":\"The package name or version\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryReference\",\"description\":\"A GitHub Security Advisory Reference\",\"fields\":[{\"name\":\"url\",\"description\":\"A publicly accessible reference\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisorySeverity\",\"description\":\"Severity of the vulnerability.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"LOW\",\"description\":\"Low.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MODERATE\",\"description\":\"Moderate.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"HIGH\",\"description\":\"High.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CRITICAL\",\"description\":\"Critical.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerability\",\"description\":\"An individual vulnerability within an Advisory\",\"fields\":[{\"name\":\"advisory\",\"description\":\"The Advisory associated with this Vulnerability\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisory\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"firstPatchedVersion\",\"description\":\"The first version containing a fix for the vulnerability\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryPackageVersion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"package\",\"description\":\"A description of the vulnerable package\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityAdvisoryPackage\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"severity\",\"description\":\"The severity of the vulnerability within this package\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SecurityAdvisorySeverity\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"When the vulnerability was last updated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"vulnerableVersionRange\",\"description\":\"A string that describes the vulnerable package versions.\\nThis string follows a basic syntax with a few forms.\\n+ `= 0.2.0` denotes a single vulnerable version.\\n+ `<= 1.0.8` denotes a version range up to and including the specified version\\n+ `< 0.1.11` denotes a version range up to, but excluding, the specified version\\n+ `>= 4.3.0, < 4.3.5` denotes a version range with a known minimum and maximum version.\\n+ `>= 0.0.1` denotes a version range with a known minimum, but no known maximum\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerabilityConnection\",\"description\":\"The connection type for SecurityVulnerability.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerabilityEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerability\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerabilityEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SecurityVulnerability\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SecurityVulnerabilityOrder\",\"description\":\"Ordering options for security vulnerability connections\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order security vulnerabilities by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SecurityVulnerabilityOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SecurityVulnerabilityOrderField\",\"description\":\"Properties by which security vulnerability connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"UPDATED_AT\",\"description\":\"Order vulnerability by update time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SetEnterpriseIdentityProviderInput\",\"description\":\"Autogenerated input type of SetEnterpriseIdentityProvider\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set an identity provider.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"ssoUrl\",\"description\":\"The URL endpoint for the identity provider's SAML SSO.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"issuer\",\"description\":\"The Issuer Entity ID for the SAML identity provider\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"idpCertificate\",\"description\":\"The x509 certificate used by the identity provider to sign assertions and responses.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"signatureMethod\",\"description\":\"The signature algorithm used to sign SAML requests for the identity provider.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SamlSignatureAlgorithm\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"digestMethod\",\"description\":\"The digest algorithm used to sign SAML requests for the identity provider.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SamlDigestAlgorithm\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SetEnterpriseIdentityProviderPayload\",\"description\":\"Autogenerated return type of SetEnterpriseIdentityProvider\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"identityProvider\",\"description\":\"The identity provider for the enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"EnterpriseIdentityProvider\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SetOrganizationInteractionLimitInput\",\"description\":\"Autogenerated input type of SetOrganizationInteractionLimit\",\"fields\":null,\"inputFields\":[{\"name\":\"organizationId\",\"description\":\"The ID of the organization to set a limit for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"limit\",\"description\":\"The limit to set.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimit\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"expiry\",\"description\":\"When this limit should expire.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimitExpiry\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SetOrganizationInteractionLimitPayload\",\"description\":\"Autogenerated return type of SetOrganizationInteractionLimit\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The organization that the interaction limit was set for.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SetRepositoryInteractionLimitInput\",\"description\":\"Autogenerated input type of SetRepositoryInteractionLimit\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The ID of the repository to set a limit for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"limit\",\"description\":\"The limit to set.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimit\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"expiry\",\"description\":\"When this limit should expire.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimitExpiry\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SetRepositoryInteractionLimitPayload\",\"description\":\"Autogenerated return type of SetRepositoryInteractionLimit\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository that the interaction limit was set for.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SetUserInteractionLimitInput\",\"description\":\"Autogenerated input type of SetUserInteractionLimit\",\"fields\":null,\"inputFields\":[{\"name\":\"userId\",\"description\":\"The ID of the user to set a limit for.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"limit\",\"description\":\"The limit to set.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimit\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"expiry\",\"description\":\"When this limit should expire.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryInteractionLimitExpiry\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SetUserInteractionLimitPayload\",\"description\":\"Autogenerated return type of SetUserInteractionLimit\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user that the interaction limit was set for.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SmimeSignature\",\"description\":\"Represents an S/MIME signature on a Commit or Tag.\",\"fields\":[{\"name\":\"email\",\"description\":\"Email used to sign this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isValid\",\"description\":\"True if the signature is valid and verified by GitHub.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"payload\",\"description\":\"Payload for GPG signing object. Raw ODB object without the signature header.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signature\",\"description\":\"ASCII-armored signature header from object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signer\",\"description\":\"GitHub user corresponding to the email signing this commit.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"GitSignatureState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"wasSignedByGitHub\",\"description\":\"True if the signature was made with GitHub's signing key.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"GitSignature\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"Sponsor\",\"description\":\"Entities that can sponsor others via GitHub Sponsors\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"INTERFACE\",\"name\":\"Sponsorable\",\"description\":\"Entities that can be sponsored through GitHub Sponsors\",\"fields\":[{\"name\":\"hasSponsorsListing\",\"description\":\"True if this user/organization has a GitHub Sponsors listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSponsoredBy\",\"description\":\"Check if the given account is sponsoring this user/organization.\",\"args\":[{\"name\":\"accountLogin\",\"description\":\"The target account's login.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSponsoringViewer\",\"description\":\"True if the viewer is sponsored by this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorsListing\",\"description\":\"The GitHub Sponsors listing for this user or organization.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsListing\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipForViewerAsSponsor\",\"description\":\"The viewer's sponsorship of this entity.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Sponsorship\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipsAsMaintainer\",\"description\":\"This object's sponsorships as the maintainer.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"includePrivate\",\"description\":\"Whether or not to include private sponsorships in the result set\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorshipOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorshipConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipsAsSponsor\",\"description\":\"This object's sponsorships as the sponsor.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorshipOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorshipConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSponsor\",\"description\":\"Whether or not the viewer is able to sponsor this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerIsSponsoring\",\"description\":\"True if the viewer is sponsoring this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"UNION\",\"name\":\"SponsorableItem\",\"description\":\"Entities that can be sponsored via GitHub Sponsors\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"SponsorableItemConnection\",\"description\":\"The connection type for SponsorableItem.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorableItemEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"SponsorableItem\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorableItemEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"SponsorableItem\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorableOrder\",\"description\":\"Ordering options for connections to get sponsorable entities for GitHub Sponsors.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order sponsorable entities by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SponsorableOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SponsorableOrderField\",\"description\":\"Properties by which sponsorable connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"LOGIN\",\"description\":\"Order sponsorable entities by login (username).\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorsGoal\",\"description\":\"A goal associated with a GitHub Sponsors listing, representing a target the sponsored maintainer would like to attain.\",\"fields\":[{\"name\":\"description\",\"description\":\"A description of the goal from the maintainer.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"kind\",\"description\":\"What the objective of this goal is.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SponsorsGoalKind\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"percentComplete\",\"description\":\"The percentage representing how complete this goal is, between 0-100.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"targetValue\",\"description\":\"What the goal amount is. Represents a dollar amount for monthly sponsorship amount goals. Represents a count of unique sponsors for total sponsors count goals.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"A brief summary of the kind and target value of this goal.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SponsorsGoalKind\",\"description\":\"The different kinds of goals a GitHub Sponsors member can have.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"TOTAL_SPONSORS_COUNT\",\"description\":\"The goal is about reaching a certain number of sponsors.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MONTHLY_SPONSORSHIP_AMOUNT\",\"description\":\"The goal is about getting a certain dollar amount from sponsorships each month.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorsListing\",\"description\":\"A GitHub Sponsors listing.\",\"fields\":[{\"name\":\"activeGoal\",\"description\":\"The current goal the maintainer is trying to reach with GitHub Sponsors, if any.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsGoal\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"fullDescription\",\"description\":\"The full description of the listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"fullDescriptionHTML\",\"description\":\"The full description of the listing rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The listing's full name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"shortDescription\",\"description\":\"The short description of the listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"slug\",\"description\":\"The short name of the listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tiers\",\"description\":\"The published tiers for this GitHub Sponsors listing.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for Sponsors tiers returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorsTierOrder\",\"ofType\":null},\"defaultValue\":\"{field: MONTHLY_PRICE_IN_CENTS, direction: ASC}\"}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsTierConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorsTier\",\"description\":\"A GitHub Sponsors tier associated with a GitHub Sponsors listing.\",\"fields\":[{\"name\":\"adminInfo\",\"description\":\"SponsorsTier information only visible to users that can administer the associated Sponsors listing.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsTierAdminInfo\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The description of the tier.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"descriptionHTML\",\"description\":\"The tier description rendered to HTML\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"monthlyPriceInCents\",\"description\":\"How much this tier costs per month in cents.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"monthlyPriceInDollars\",\"description\":\"How much this tier costs per month in dollars.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the tier.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorsListing\",\"description\":\"The sponsors listing that this tier belongs to.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsListing\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorsTierAdminInfo\",\"description\":\"SponsorsTier information only visible to users that can administer the associated Sponsors listing.\",\"fields\":[{\"name\":\"sponsorships\",\"description\":\"The sponsorships associated with this tier.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"includePrivate\",\"description\":\"Whether or not to include private sponsorships in the result set\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorshipOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorshipConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorsTierConnection\",\"description\":\"The connection type for SponsorsTier.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsTierEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsTier\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorsTierEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsTier\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorsTierOrder\",\"description\":\"Ordering options for Sponsors tiers connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order tiers by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SponsorsTierOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SponsorsTierOrderField\",\"description\":\"Properties by which Sponsors tiers connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order tiers by creation time.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MONTHLY_PRICE_IN_CENTS\",\"description\":\"Order tiers by their monthly price in cents\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Sponsorship\",\"description\":\"A sponsorship relationship between a sponsor and a maintainer\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"maintainer\",\"description\":\"The entity that is being sponsored\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"`Sponsorship.maintainer` will be removed. Use `Sponsorship.sponsorable` instead. Removal on 2020-04-01 UTC.\"},{\"name\":\"privacyLevel\",\"description\":\"The privacy level for this sponsorship.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SponsorshipPrivacy\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsor\",\"description\":\"The user that is sponsoring. Returns null if the sponsorship is private or if sponsor is not a user.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":true,\"deprecationReason\":\"`Sponsorship.sponsor` will be removed. Use `Sponsorship.sponsorEntity` instead. Removal on 2020-10-01 UTC.\"},{\"name\":\"sponsorEntity\",\"description\":\"The user or organization that is sponsoring, if you have permission to view them.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"Sponsor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorable\",\"description\":\"The entity that is being sponsored\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Sponsorable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tier\",\"description\":\"The associated sponsorship tier\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsTier\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorshipConnection\",\"description\":\"The connection type for Sponsorship.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorshipEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Sponsorship\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SponsorshipEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Sponsorship\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorshipOrder\",\"description\":\"Ordering options for sponsorship connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order sponsorship by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SponsorshipOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SponsorshipOrderField\",\"description\":\"Properties by which sponsorship connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order sponsorship by creation time.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SponsorshipPrivacy\",\"description\":\"The privacy of a sponsorship\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"PUBLIC\",\"description\":\"Public\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PRIVATE\",\"description\":\"Private\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"StarOrder\",\"description\":\"Ways in which star connections can be ordered.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order nodes by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"StarOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order nodes.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"StarOrderField\",\"description\":\"Properties by which star connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"STARRED_AT\",\"description\":\"Allows ordering a list of stars by when they were created.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"StargazerConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StargazerEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"StargazerEdge\",\"description\":\"Represents a user that's starred a repository.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"starredAt\",\"description\":\"Identifies when the item was starred.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Starrable\",\"description\":\"Things that can be starred.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"stargazerCount\",\"description\":\"Returns a count of how many stargazers there are on this object\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"stargazers\",\"description\":\"A list of users who have starred this starrable.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"StarOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StargazerConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerHasStarred\",\"description\":\"Returns a boolean indicating whether the viewing user has starred this starrable.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"StarredRepositoryConnection\",\"description\":\"The connection type for Repository.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StarredRepositoryEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isOverLimit\",\"description\":\"Is the list of stars for this user truncated? This is true for users that have many stars.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"StarredRepositoryEdge\",\"description\":\"Represents a starred repository.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"starredAt\",\"description\":\"Identifies when the item was starred.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Status\",\"description\":\"Represents a commit status.\",\"fields\":[{\"name\":\"combinedContexts\",\"description\":\"A list of status contexts and check runs for this commit.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StatusCheckRollupContextConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"The commit this status is attached to.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"context\",\"description\":\"Looks up an individual status context by context name.\",\"args\":[{\"name\":\"name\",\"description\":\"The context name.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"StatusContext\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"contexts\",\"description\":\"The individual status contexts for this commit.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StatusContext\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The combined commit status.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"StatusState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"StatusCheckRollup\",\"description\":\"Represents the rollup for both the check runs and status for a commit.\",\"fields\":[{\"name\":\"commit\",\"description\":\"The commit the status and check runs are attached to.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"contexts\",\"description\":\"A list of status contexts and check runs for this commit.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StatusCheckRollupContextConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The combined status for the commit.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"StatusState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"StatusCheckRollupContext\",\"description\":\"Types that can be inside a StatusCheckRollup context.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"StatusContext\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"StatusCheckRollupContextConnection\",\"description\":\"The connection type for StatusCheckRollupContext.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StatusCheckRollupContextEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"StatusCheckRollupContext\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"StatusCheckRollupContextEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"StatusCheckRollupContext\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"StatusContext\",\"description\":\"Represents an individual commit status context\",\"fields\":[{\"name\":\"avatarUrl\",\"description\":\"The avatar of the OAuth application or the user that created the status\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"40\"}],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commit\",\"description\":\"This commit this status context is attached to.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"context\",\"description\":\"The name of this status context.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"creator\",\"description\":\"The actor who created this status context.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The description for this status context.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isRequired\",\"description\":\"Whether this is required to pass before merging for a specific pull request.\",\"args\":[{\"name\":\"pullRequestId\",\"description\":\"The id of the pull request this is required for\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pullRequestNumber\",\"description\":\"The number of the pull request this is required for\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The state of this status context.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"StatusState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"targetUrl\",\"description\":\"The URL for this status context.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RequirableByPullRequest\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"StatusState\",\"description\":\"The possible commit status states.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"EXPECTED\",\"description\":\"Status is expected.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ERROR\",\"description\":\"Status is errored.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FAILURE\",\"description\":\"Status is failing.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PENDING\",\"description\":\"Status is pending.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUCCESS\",\"description\":\"Status is successful.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"String\",\"description\":\"Represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"SubmitPullRequestReviewInput\",\"description\":\"Autogenerated input type of SubmitPullRequestReview\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"The Pull Request ID to submit any pending reviews.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pullRequestReviewId\",\"description\":\"The Pull Request Review ID to submit.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"event\",\"description\":\"The event to send to the Pull Request Review.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestReviewEvent\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The text field to set on the Pull Request Review.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SubmitPullRequestReviewPayload\",\"description\":\"Autogenerated return type of SubmitPullRequestReview\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReview\",\"description\":\"The submitted pull request review.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Submodule\",\"description\":\"A pointer to a repository at a specific revision embedded inside another repository.\",\"fields\":[{\"name\":\"branch\",\"description\":\"The branch of the upstream submodule for tracking updates\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"gitUrl\",\"description\":\"The git URL of the submodule repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the submodule in .gitmodules\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"The path in the superproject that this submodule is located in\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subprojectCommitOid\",\"description\":\"The commit revision of the subproject repository being tracked by the submodule\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SubmoduleConnection\",\"description\":\"The connection type for Submodule.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SubmoduleEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Submodule\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SubmoduleEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Submodule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"description\":\"Entities that can be subscribed to for web and email notifications.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSubscribe\",\"description\":\"Check if the viewer is able to change their subscription status for the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerSubscription\",\"description\":\"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"SubscribedEvent\",\"description\":\"Represents a 'subscribed' event on a given `Subscribable`.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subscribable\",\"description\":\"Object referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"description\":\"The possible states of a subscription.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"UNSUBSCRIBED\",\"description\":\"The User is only notified when participating or @mentioned.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUBSCRIBED\",\"description\":\"The User is notified of all conversations.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"IGNORED\",\"description\":\"The User is never notified.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"SuggestedReviewer\",\"description\":\"A suggestion to review a pull request based on a user's commit history and review comments.\",\"fields\":[{\"name\":\"isAuthor\",\"description\":\"Is this suggestion based on past commits?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCommenter\",\"description\":\"Is this suggestion based on past review comments?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reviewer\",\"description\":\"Identifies the user suggested to review the pull request.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Tag\",\"description\":\"Represents a Git tag.\",\"fields\":[{\"name\":\"abbreviatedOid\",\"description\":\"An abbreviated version of the Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitResourcePath\",\"description\":\"The HTTP path for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitUrl\",\"description\":\"The HTTP URL for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"The Git tag message.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The Git tag name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oid\",\"description\":\"The Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The Repository the Git object belongs to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tagger\",\"description\":\"Details about the tag author.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"GitActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"target\",\"description\":\"The Git object the tag points to.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Team\",\"description\":\"A team of users in an organization.\",\"fields\":[{\"name\":\"ancestors\",\"description\":\"A list of teams that are ancestors of this team.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the team's avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size in pixels of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"400\"}],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"childTeams\",\"description\":\"List of child teams belonging to this team\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"userLogins\",\"description\":\"User logins to filter by\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"immediateOnly\",\"description\":\"Whether to list immediate child teams or all descendant child teams.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"true\"},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"combinedSlug\",\"description\":\"The slug corresponding to the organization and team.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":\"The description of the team.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"discussion\",\"description\":\"Find a team discussion by its number.\",\"args\":[{\"name\":\"number\",\"description\":\"The sequence number of the discussion to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"discussions\",\"description\":\"A list of team discussions.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isPinned\",\"description\":\"If provided, filters discussions according to whether or not they are pinned.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamDiscussionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"discussionsResourcePath\",\"description\":\"The HTTP path for team discussions\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"discussionsUrl\",\"description\":\"The HTTP URL for team discussions\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editTeamResourcePath\",\"description\":\"The HTTP path for editing this team\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editTeamUrl\",\"description\":\"The HTTP URL for editing this team\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invitations\",\"description\":\"A list of pending invitations for users to this team\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationInvitationConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"memberStatuses\",\"description\":\"Get the status messages members of this entity have set that are either public or visible only to the organization.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for user statuses returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"UserStatusOrder\",\"ofType\":null},\"defaultValue\":\"{field: UPDATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserStatusConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"members\",\"description\":\"A list of users who are members of this team.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"membership\",\"description\":\"Filter by membership type\",\"type\":{\"kind\":\"ENUM\",\"name\":\"TeamMembershipType\",\"ofType\":null},\"defaultValue\":\"ALL\"},{\"name\":\"role\",\"description\":\"Filter by team member role\",\"type\":{\"kind\":\"ENUM\",\"name\":\"TeamMemberRole\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamMemberOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamMemberConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersResourcePath\",\"description\":\"The HTTP path for the team' members\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"membersUrl\",\"description\":\"The HTTP URL for the team' members\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The name of the team.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"newTeamResourcePath\",\"description\":\"The HTTP path creating a new team\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"newTeamUrl\",\"description\":\"The HTTP URL creating a new team\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The organization that owns this team.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeam\",\"description\":\"The parent team of the team.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"privacy\",\"description\":\"The level of privacy the team has.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"TeamPrivacy\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositories\",\"description\":\"A list of repositories this team has access to.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"query\",\"description\":\"The search string to look for.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamRepositoryOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamRepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoriesResourcePath\",\"description\":\"The HTTP path for this team's repositories\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoriesUrl\",\"description\":\"The HTTP URL for this team's repositories\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this team\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"slug\",\"description\":\"The slug corresponding to the team.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamsResourcePath\",\"description\":\"The HTTP path for this team's teams\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamsUrl\",\"description\":\"The HTTP URL for this team's teams\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this team\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanAdminister\",\"description\":\"Team is adminable by the viewer.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSubscribe\",\"description\":\"Check if the viewer is able to change their subscription status for the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerSubscription\",\"description\":\"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"MemberStatusable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddMemberAuditEntry\",\"description\":\"Audit log entry for a team.add_member event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isLdapMapped\",\"description\":\"Whether the team was mapped to an LDAP Group.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"team\",\"description\":\"The team associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamName\",\"description\":\"The name of the team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamResourcePath\",\"description\":\"The HTTP path for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamUrl\",\"description\":\"The HTTP URL for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"TeamAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddRepositoryAuditEntry\",\"description\":\"Audit log entry for a team.add_repository event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isLdapMapped\",\"description\":\"Whether the team was mapped to an LDAP Group.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"team\",\"description\":\"The team associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamName\",\"description\":\"The name of the team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamResourcePath\",\"description\":\"The HTTP path for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamUrl\",\"description\":\"The HTTP URL for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"TeamAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"TeamAuditEntryData\",\"description\":\"Metadata for an audit entry with action team.*\",\"fields\":[{\"name\":\"team\",\"description\":\"The team associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamName\",\"description\":\"The name of the team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamResourcePath\",\"description\":\"The HTTP path for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamUrl\",\"description\":\"The HTTP URL for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"OrgRestoreMemberMembershipTeamAuditEntryData\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamAddRepositoryAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamChangeParentTeamAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveMemberAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveRepositoryAuditEntry\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"TeamChangeParentTeamAuditEntry\",\"description\":\"Audit log entry for a team.change_parent_team event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isLdapMapped\",\"description\":\"Whether the team was mapped to an LDAP Group.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeam\",\"description\":\"The new parent team.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeamName\",\"description\":\"The name of the new parent team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeamNameWas\",\"description\":\"The name of the former parent team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeamResourcePath\",\"description\":\"The HTTP path for the parent team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeamUrl\",\"description\":\"The HTTP URL for the parent team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeamWas\",\"description\":\"The former parent team.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeamWasResourcePath\",\"description\":\"The HTTP path for the previous parent team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"parentTeamWasUrl\",\"description\":\"The HTTP URL for the previous parent team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"team\",\"description\":\"The team associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamName\",\"description\":\"The name of the team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamResourcePath\",\"description\":\"The HTTP path for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamUrl\",\"description\":\"The HTTP URL for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"TeamAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamConnection\",\"description\":\"The connection type for Team.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"description\":\"A team discussion.\",\"fields\":[{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the discussion's team.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"The body as Markdown.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The body rendered to text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyVersion\",\"description\":\"Identifies the discussion body hash.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"comments\",\"description\":\"A list of comments on this discussion.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamDiscussionCommentOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"fromComment\",\"description\":\"When provided, filters the connection such that results begin with the comment with this number.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commentsResourcePath\",\"description\":\"The HTTP path for discussion comments\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commentsUrl\",\"description\":\"The HTTP URL for discussion comments\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPinned\",\"description\":\"Whether or not the discussion is pinned.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isPrivate\",\"description\":\"Whether or not the discussion is only visible to team members and org admins.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"number\",\"description\":\"Identifies the discussion within its team.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this discussion\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"team\",\"description\":\"The team that defines the context of this discussion.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"title\",\"description\":\"The title of the discussion\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this discussion\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDelete\",\"description\":\"Check if the current viewer can delete this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanPin\",\"description\":\"Whether or not the current viewer can pin this discussion.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSubscribe\",\"description\":\"Check if the viewer is able to change their subscription status for the repository.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerSubscription\",\"description\":\"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Deletable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"description\":\"A comment on a team discussion.\",\"fields\":[{\"name\":\"author\",\"description\":\"The actor who authored the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"authorAssociation\",\"description\":\"Author's association with the comment's team.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentAuthorAssociation\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"body\",\"description\":\"The body as Markdown.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyHTML\",\"description\":\"The body rendered to HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyText\",\"description\":\"The body rendered to text.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bodyVersion\",\"description\":\"The current version of the body content.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdViaEmail\",\"description\":\"Check if this comment was created via an email reply.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"discussion\",\"description\":\"The discussion this comment is about.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited the comment.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"includesCreatedEdit\",\"description\":\"Check if this comment was edited and includes an edit with the creation data\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lastEditedAt\",\"description\":\"The moment the editor made the last edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"number\",\"description\":\"Identifies the comment number.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publishedAt\",\"description\":\"Identifies when the comment was published at.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactionGroups\",\"description\":\"A list of reactions grouped by content left on the subject.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionGroup\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"reactions\",\"description\":\"A list of Reactions left on the Issue.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"content\",\"description\":\"Allows filtering Reactions by emoji.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ReactionContent\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Allows specifying the order in which reactions are returned.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ReactionOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ReactionConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this comment\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this comment\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userContentEdits\",\"description\":\"A list of edits to this content.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanDelete\",\"description\":\"Check if the current viewer can delete this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanReact\",\"description\":\"Can user react to this subject\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerDidAuthor\",\"description\":\"Did the viewer author this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Comment\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Deletable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Reactable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionCommentConnection\",\"description\":\"The connection type for TeamDiscussionComment.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionCommentEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionCommentEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamDiscussionCommentOrder\",\"description\":\"Ways in which team discussion comment connections can be ordered.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field by which to order nodes.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"TeamDiscussionCommentOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order nodes.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamDiscussionCommentOrderField\",\"description\":\"Properties by which team discussion comment connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NUMBER\",\"description\":\"Allows sequential ordering of team discussion comments (which is equivalent to chronological ordering).\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionConnection\",\"description\":\"The connection type for TeamDiscussion.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamDiscussionOrder\",\"description\":\"Ways in which team discussion connections can be ordered.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field by which to order nodes.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"TeamDiscussionOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order nodes.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamDiscussionOrderField\",\"description\":\"Properties by which team discussion connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Allows chronological ordering of team discussions.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamMemberConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamMemberEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamMemberEdge\",\"description\":\"Represents a user who is a member of a team.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"memberAccessResourcePath\",\"description\":\"The HTTP path to the organization's member access page.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"memberAccessUrl\",\"description\":\"The HTTP URL to the organization's member access page.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"role\",\"description\":\"The role the member has on the team.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"TeamMemberRole\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamMemberOrder\",\"description\":\"Ordering options for team member connections\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order team members by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"TeamMemberOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamMemberOrderField\",\"description\":\"Properties by which team member connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"LOGIN\",\"description\":\"Order team members by login\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CREATED_AT\",\"description\":\"Order team members by creation time\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamMemberRole\",\"description\":\"The possible team member roles; either 'maintainer' or 'member'.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"MAINTAINER\",\"description\":\"A team maintainer has permission to add and remove team members.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MEMBER\",\"description\":\"A team member has no administrative permissions on the team.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamMembershipType\",\"description\":\"Defines which types of team members are included in the returned list. Can be one of IMMEDIATE, CHILD_TEAM or ALL.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"IMMEDIATE\",\"description\":\"Includes only immediate members of the team.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CHILD_TEAM\",\"description\":\"Includes only child team members for the team.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ALL\",\"description\":\"Includes immediate and child team members for the team.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamOrder\",\"description\":\"Ways in which team connections can be ordered.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field in which to order nodes by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"TeamOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The direction in which to order nodes.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamOrderField\",\"description\":\"Properties by which team connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NAME\",\"description\":\"Allows ordering a list of teams by name.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamPrivacy\",\"description\":\"The possible team privacy values.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"SECRET\",\"description\":\"A secret team can only be seen by its members.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"VISIBLE\",\"description\":\"A visible team can be seen and @mentioned by every member of the organization.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveMemberAuditEntry\",\"description\":\"Audit log entry for a team.remove_member event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isLdapMapped\",\"description\":\"Whether the team was mapped to an LDAP Group.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"team\",\"description\":\"The team associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamName\",\"description\":\"The name of the team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamResourcePath\",\"description\":\"The HTTP path for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamUrl\",\"description\":\"The HTTP URL for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"TeamAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRemoveRepositoryAuditEntry\",\"description\":\"Audit log entry for a team.remove_repository event.\",\"fields\":[{\"name\":\"action\",\"description\":\"The action name\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actor\",\"description\":\"The user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"AuditEntryActor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorIp\",\"description\":\"The IP address of the actor\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLocation\",\"description\":\"A readable representation of the actor's location\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ActorLocation\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorLogin\",\"description\":\"The username of the user who initiated the action\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorResourcePath\",\"description\":\"The HTTP path for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"actorUrl\",\"description\":\"The HTTP URL for the actor.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"The time the action was initiated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"PreciseDateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isLdapMapped\",\"description\":\"Whether the team was mapped to an LDAP Group.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"operationType\",\"description\":\"The corresponding operation type for the action\",\"args\":[],\"type\":{\"kind\":\"ENUM\",\"name\":\"OperationType\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The Organization associated with the Audit Entry.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationName\",\"description\":\"The name of the Organization.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationResourcePath\",\"description\":\"The HTTP path for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationUrl\",\"description\":\"The HTTP URL for the organization\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryName\",\"description\":\"The name of the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryResourcePath\",\"description\":\"The HTTP path for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoryUrl\",\"description\":\"The HTTP URL for the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"team\",\"description\":\"The team associated with the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Team\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamName\",\"description\":\"The name of the team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamResourcePath\",\"description\":\"The HTTP path for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamUrl\",\"description\":\"The HTTP URL for this team\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user affected by the action\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userLogin\",\"description\":\"For actions involving two users, the actor is the initiator and the user is the affected user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userResourcePath\",\"description\":\"The HTTP path for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"userUrl\",\"description\":\"The HTTP URL for the user.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"AuditEntry\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"OrganizationAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryAuditEntryData\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"TeamAuditEntryData\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRepositoryConnection\",\"description\":\"The connection type for Repository.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TeamRepositoryEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamRepositoryEdge\",\"description\":\"Represents a team repository.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"permission\",\"description\":\"The permission level the team has on the repository\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPermission\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"TeamRepositoryOrder\",\"description\":\"Ordering options for team repository connections\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order repositories by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"TeamRepositoryOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamRepositoryOrderField\",\"description\":\"Properties by which team repository connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"CREATED_AT\",\"description\":\"Order repositories by creation time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UPDATED_AT\",\"description\":\"Order repositories by update time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PUSHED_AT\",\"description\":\"Order repositories by push time\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NAME\",\"description\":\"Order repositories by name\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PERMISSION\",\"description\":\"Order repositories by permission\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"STARGAZERS\",\"description\":\"Order repositories by number of stargazers\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"TeamRole\",\"description\":\"The role of a user on a team.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ADMIN\",\"description\":\"User has admin rights on the team.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MEMBER\",\"description\":\"User is a member of the team.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TextMatch\",\"description\":\"A text match within a search result.\",\"fields\":[{\"name\":\"fragment\",\"description\":\"The specific text fragment within the property matched on.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"highlights\",\"description\":\"Highlights within the matched fragment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TextMatchHighlight\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"property\",\"description\":\"The property matched on.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TextMatchHighlight\",\"description\":\"Represents a single highlight in a search result match.\",\"fields\":[{\"name\":\"beginIndice\",\"description\":\"The indice in the fragment where the matched text begins.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"endIndice\",\"description\":\"The indice in the fragment where the matched text ends.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"text\",\"description\":\"The text matched.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"description\":\"A topic aggregates entities that are related to a subject.\",\"fields\":[{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The topic's name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"relatedTopics\",\"description\":\"A list of related topics, including aliases of this topic, sorted with the most relevant\\nfirst. Returns up to 10 Topics.\\n\",\"args\":[{\"name\":\"first\",\"description\":\"How many topics to return.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":\"3\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"stargazerCount\",\"description\":\"Returns a count of how many stargazers there are on this object\\n\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"stargazers\",\"description\":\"A list of users who have starred this starrable.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"StarOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StargazerConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerHasStarred\",\"description\":\"Returns a boolean indicating whether the viewing user has starred this starrable.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Starrable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"TopicAuditEntryData\",\"description\":\"Metadata for an audit entry with a topic.\",\"fields\":[{\"name\":\"topic\",\"description\":\"The name of the topic added to the repository\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Topic\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topicName\",\"description\":\"The name of the topic added to the repository\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"RepoAddTopicAuditEntry\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepoRemoveTopicAuditEntry\",\"ofType\":null}]},{\"kind\":\"ENUM\",\"name\":\"TopicSuggestionDeclineReason\",\"description\":\"Reason that the suggested topic is declined.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"NOT_RELEVANT\",\"description\":\"The suggested topic is not relevant to the repository.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TOO_SPECIFIC\",\"description\":\"The suggested topic is too specific for the repository (e.g. #ruby-on-rails-version-4-2-1).\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PERSONAL_PREFERENCE\",\"description\":\"The viewer does not like the suggested topic.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"TOO_GENERAL\",\"description\":\"The suggested topic is too general for the repository.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"TransferIssueInput\",\"description\":\"Autogenerated input type of TransferIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"issueId\",\"description\":\"The Node ID of the issue to be transferred\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository the issue should be transferred to\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TransferIssuePayload\",\"description\":\"Autogenerated return type of TransferIssue\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The issue that was transferred\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TransferredEvent\",\"description\":\"Represents a 'transferred' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"fromRepository\",\"description\":\"The repository this came from\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"Identifies the issue associated with the event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"Tree\",\"description\":\"Represents a Git tree.\",\"fields\":[{\"name\":\"abbreviatedOid\",\"description\":\"An abbreviated version of the Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitResourcePath\",\"description\":\"The HTTP path for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitUrl\",\"description\":\"The HTTP URL for this Git object\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"entries\",\"description\":\"A list of tree entries.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"TreeEntry\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oid\",\"description\":\"The Git object ID\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The Repository the Git object belongs to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"TreeEntry\",\"description\":\"Represents a Git tree entry.\",\"fields\":[{\"name\":\"extension\",\"description\":\"The extension of the file\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isGenerated\",\"description\":\"Whether or not this tree entry is generated\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mode\",\"description\":\"Entry file mode.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"Entry file name.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"object\",\"description\":\"Entry file object.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"GitObject\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"oid\",\"description\":\"Entry file Git object ID.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"path\",\"description\":\"The full path of the file.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The Repository the tree entry belongs to\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"submodule\",\"description\":\"If the TreeEntry is for a directory occupied by a submodule project, this returns the corresponding submodule\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Submodule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"type\",\"description\":\"Entry file type.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"URI\",\"description\":\"An RFC 3986, RFC 3987, and RFC 6570 (level 4) compliant URI string.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnarchiveRepositoryInput\",\"description\":\"Autogenerated input type of UnarchiveRepository\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The ID of the repository to unarchive.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnarchiveRepositoryPayload\",\"description\":\"Autogenerated return type of UnarchiveRepository\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The repository that was unarchived.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnassignedEvent\",\"description\":\"Represents an 'unassigned' event on any assignable object.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"assignable\",\"description\":\"Identifies the assignable associated with the event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Assignable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"assignee\",\"description\":\"Identifies the user or mannequin that was unassigned.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"Assignee\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"Identifies the subject (user) who was unassigned.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":true,\"deprecationReason\":\"Assignees can now be mannequins. Use the `assignee` field instead. Removal on 2020-01-01 UTC.\"}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnfollowUserInput\",\"description\":\"Autogenerated input type of UnfollowUser\",\"fields\":null,\"inputFields\":[{\"name\":\"userId\",\"description\":\"ID of the user to unfollow.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnfollowUserPayload\",\"description\":\"Autogenerated return type of UnfollowUser\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user that was unfollowed.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"description\":\"Represents a type that can be retrieved by a URL.\",\"fields\":[{\"name\":\"resourcePath\",\"description\":\"The HTML path to this resource.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The URL to this resource.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Bot\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ClosedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Commit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ConvertToDraftEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"CrossReferencedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Mannequin\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"MergedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Milestone\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestCommit\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReadyForReviewEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Release\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"RepositoryTopic\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"ReviewDismissedEvent\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}]},{\"kind\":\"OBJECT\",\"name\":\"UnknownSignature\",\"description\":\"Represents an unknown signature on a Commit or Tag.\",\"fields\":[{\"name\":\"email\",\"description\":\"Email used to sign this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isValid\",\"description\":\"True if the signature is valid and verified by GitHub.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"payload\",\"description\":\"Payload for GPG signing object. Raw ODB object without the signature header.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signature\",\"description\":\"ASCII-armored signature header from object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"signer\",\"description\":\"GitHub user corresponding to the email signing this commit.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"state\",\"description\":\"The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"GitSignatureState\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"wasSignedByGitHub\",\"description\":\"True if the signature was made with GitHub's signing key.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"GitSignature\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlabeledEvent\",\"description\":\"Represents an 'unlabeled' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"label\",\"description\":\"Identifies the label associated with the 'unlabeled' event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Label\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"labelable\",\"description\":\"Identifies the `Labelable` associated with the event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Labelable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnlinkRepositoryFromProjectInput\",\"description\":\"Autogenerated input type of UnlinkRepositoryFromProject\",\"fields\":null,\"inputFields\":[{\"name\":\"projectId\",\"description\":\"The ID of the Project linked to the Repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"repositoryId\",\"description\":\"The ID of the Repository linked to the Project.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlinkRepositoryFromProjectPayload\",\"description\":\"Autogenerated return type of UnlinkRepositoryFromProject\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The linked Project.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The linked Repository.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnlockLockableInput\",\"description\":\"Autogenerated input type of UnlockLockable\",\"fields\":null,\"inputFields\":[{\"name\":\"lockableId\",\"description\":\"ID of the item to be unlocked.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlockLockablePayload\",\"description\":\"Autogenerated return type of UnlockLockable\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unlockedRecord\",\"description\":\"The item that was unlocked.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Lockable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnlockedEvent\",\"description\":\"Represents an 'unlocked' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"lockable\",\"description\":\"Object that was unlocked.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Lockable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnmarkFileAsViewedInput\",\"description\":\"Autogenerated input type of UnmarkFileAsViewed\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"The Node ID of the pull request.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"path\",\"description\":\"The path of the file to mark as unviewed\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnmarkFileAsViewedPayload\",\"description\":\"Autogenerated return type of UnmarkFileAsViewed\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The updated pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnmarkIssueAsDuplicateInput\",\"description\":\"Autogenerated input type of UnmarkIssueAsDuplicate\",\"fields\":null,\"inputFields\":[{\"name\":\"duplicateId\",\"description\":\"ID of the issue or pull request currently marked as a duplicate.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"canonicalId\",\"description\":\"ID of the issue or pull request currently considered canonical/authoritative/original.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnmarkIssueAsDuplicatePayload\",\"description\":\"Autogenerated return type of UnmarkIssueAsDuplicate\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"duplicate\",\"description\":\"The issue or pull request that was marked as a duplicate.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"IssueOrPullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnmarkedAsDuplicateEvent\",\"description\":\"Represents an 'unmarked_as_duplicate' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"canonical\",\"description\":\"The authoritative issue or pull request which has been duplicated by another.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"IssueOrPullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"duplicate\",\"description\":\"The issue or pull request which has been marked as a duplicate of another.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"IssueOrPullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCrossRepository\",\"description\":\"Canonical and duplicate belong to different repositories.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnminimizeCommentInput\",\"description\":\"Autogenerated input type of UnminimizeComment\",\"fields\":null,\"inputFields\":[{\"name\":\"subjectId\",\"description\":\"The Node ID of the subject to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnminimizeCommentPayload\",\"description\":\"Autogenerated return type of UnminimizeComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"unminimizedComment\",\"description\":\"The comment that was unminimized.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Minimizable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnpinIssueInput\",\"description\":\"Autogenerated input type of UnpinIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"issueId\",\"description\":\"The ID of the issue to be unpinned\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnpinIssuePayload\",\"description\":\"Autogenerated return type of UnpinIssue\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The issue that was unpinned\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnpinnedEvent\",\"description\":\"Represents an 'unpinned' event on a given issue or pull request.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"Identifies the issue associated with the event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UnresolveReviewThreadInput\",\"description\":\"Autogenerated input type of UnresolveReviewThread\",\"fields\":null,\"inputFields\":[{\"name\":\"threadId\",\"description\":\"The ID of the thread to unresolve\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnresolveReviewThreadPayload\",\"description\":\"Autogenerated return type of UnresolveReviewThread\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"thread\",\"description\":\"The thread to resolve.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewThread\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UnsubscribedEvent\",\"description\":\"Represents an 'unsubscribed' event on a given `Subscribable`.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subscribable\",\"description\":\"Object referenced by event.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INTERFACE\",\"name\":\"Updatable\",\"description\":\"Entities that can be updated.\",\"fields\":[{\"name\":\"viewerCanUpdate\",\"description\":\"Check if the current viewer can update this object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null}]},{\"kind\":\"INTERFACE\",\"name\":\"UpdatableComment\",\"description\":\"Comments that can be updated.\",\"fields\":[{\"name\":\"viewerCannotUpdateReasons\",\"description\":\"Reasons why the current viewer can not update this comment.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"CommentCannotUpdateReason\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"CommitComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"GistComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateBranchProtectionRuleInput\",\"description\":\"Autogenerated input type of UpdateBranchProtectionRule\",\"fields\":null,\"inputFields\":[{\"name\":\"branchProtectionRuleId\",\"description\":\"The global relay id of the branch protection rule to be updated.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"pattern\",\"description\":\"The glob-like pattern used to determine matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresApprovingReviews\",\"description\":\"Are approving reviews required to update matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiredApprovingReviewCount\",\"description\":\"Number of approving reviews required to update matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresCommitSignatures\",\"description\":\"Are commits required to be signed.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresLinearHistory\",\"description\":\"Are merge commits prohibited from being pushed to this branch.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"allowsForcePushes\",\"description\":\"Are force pushes allowed on this branch.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"allowsDeletions\",\"description\":\"Can this branch be deleted.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isAdminEnforced\",\"description\":\"Can admins overwrite branch protection.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresStatusChecks\",\"description\":\"Are status checks required to update matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresStrictStatusChecks\",\"description\":\"Are branches required to be up to date before merging.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"requiresCodeOwnerReviews\",\"description\":\"Are reviews from code owners required to update matching branches.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"dismissesStaleReviews\",\"description\":\"Will new commits pushed to matching branches dismiss pull request review approvals.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"restrictsReviewDismissals\",\"description\":\"Is dismissal of pull request reviews restricted.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"reviewDismissalActorIds\",\"description\":\"A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"restrictsPushes\",\"description\":\"Is pushing to matching branches restricted.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pushActorIds\",\"description\":\"A list of User, Team or App IDs allowed to push to matching branches.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"requiredStatusCheckContexts\",\"description\":\"List of required status check contexts that must pass for commits to be accepted to matching branches.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateBranchProtectionRulePayload\",\"description\":\"Autogenerated return type of UpdateBranchProtectionRule\",\"fields\":[{\"name\":\"branchProtectionRule\",\"description\":\"The newly created BranchProtectionRule.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"BranchProtectionRule\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateCheckRunInput\",\"description\":\"Autogenerated input type of UpdateCheckRun\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"checkRunId\",\"description\":\"The node of the check.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the check.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"detailsUrl\",\"description\":\"The URL of the integrator's site that has the full details of the check.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"externalId\",\"description\":\"A reference for the run on the integrator's system.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"status\",\"description\":\"The current status.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RequestableCheckStatusState\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"startedAt\",\"description\":\"The time that the check run began.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"conclusion\",\"description\":\"The final conclusion of the check.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"CheckConclusionState\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"completedAt\",\"description\":\"The time that the check run finished.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"output\",\"description\":\"Descriptive details about the run.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunOutput\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"actions\",\"description\":\"Possible further actions the integrator can perform, which a user may trigger.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckRunAction\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateCheckRunPayload\",\"description\":\"Autogenerated return type of UpdateCheckRun\",\"fields\":[{\"name\":\"checkRun\",\"description\":\"The updated check run.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"CheckRun\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateCheckSuitePreferencesInput\",\"description\":\"Autogenerated input type of UpdateCheckSuitePreferences\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"autoTriggerPreferences\",\"description\":\"The check suite preferences to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"CheckSuiteAutoTriggerPreference\",\"ofType\":null}}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateCheckSuitePreferencesPayload\",\"description\":\"Autogenerated return type of UpdateCheckSuitePreferences\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The updated repository.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseAdministratorRoleInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseAdministratorRole\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the Enterprise which the admin belongs to.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"login\",\"description\":\"The login of a administrator whose role is being changed.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"role\",\"description\":\"The new role for the Enterprise administrator.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseAdministratorRole\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseAdministratorRolePayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseAdministratorRole\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of changing the administrator's role.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseAllowPrivateRepositoryForkingSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseAllowPrivateRepositoryForkingSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the allow private repository forking setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the allow private repository forking setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseAllowPrivateRepositoryForkingSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseAllowPrivateRepositoryForkingSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated allow private repository forking setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the allow private repository forking setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseDefaultRepositoryPermissionSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseDefaultRepositoryPermissionSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the default repository permission setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the default repository permission setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseDefaultRepositoryPermissionSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseDefaultRepositoryPermissionSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseDefaultRepositoryPermissionSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated default repository permission setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the default repository permission setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the members can change repository visibility setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the members can change repository visibility setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated members can change repository visibility setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the members can change repository visibility setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanCreateRepositoriesSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseMembersCanCreateRepositoriesSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the members can create repositories setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"Value for the members can create repositories setting on the enterprise. This or the granular public/private/internal allowed fields (but not both) must be provided.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseMembersCanCreateRepositoriesSettingValue\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"membersCanCreateRepositoriesPolicyEnabled\",\"description\":\"When false, allow member organizations to set their own repository creation member privileges.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"membersCanCreatePublicRepositories\",\"description\":\"Allow members to create public repositories. Defaults to current value.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"membersCanCreatePrivateRepositories\",\"description\":\"Allow members to create private repositories. Defaults to current value.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"membersCanCreateInternalRepositories\",\"description\":\"Allow members to create internal repositories. Defaults to current value.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanCreateRepositoriesSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseMembersCanCreateRepositoriesSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated members can create repositories setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the members can create repositories setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanDeleteIssuesSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseMembersCanDeleteIssuesSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the members can delete issues setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the members can delete issues setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanDeleteIssuesSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseMembersCanDeleteIssuesSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated members can delete issues setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the members can delete issues setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanDeleteRepositoriesSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseMembersCanDeleteRepositoriesSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the members can delete repositories setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the members can delete repositories setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanDeleteRepositoriesSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseMembersCanDeleteRepositoriesSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated members can delete repositories setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the members can delete repositories setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanInviteCollaboratorsSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseMembersCanInviteCollaboratorsSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the members can invite collaborators setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the members can invite collaborators setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanInviteCollaboratorsSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseMembersCanInviteCollaboratorsSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated members can invite collaborators setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the members can invite collaborators setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanMakePurchasesSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseMembersCanMakePurchasesSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the members can make purchases setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the members can make purchases setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseMembersCanMakePurchasesSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanMakePurchasesSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseMembersCanMakePurchasesSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated members can make purchases setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the members can make purchases setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseMembersCanUpdateProtectedBranchesSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the members can update protected branches setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the members can update protected branches setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseMembersCanUpdateProtectedBranchesSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated members can update protected branches setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the members can update protected branches setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseMembersCanViewDependencyInsightsSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseMembersCanViewDependencyInsightsSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the members can view dependency insights setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the members can view dependency insights setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseMembersCanViewDependencyInsightsSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseMembersCanViewDependencyInsightsSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated members can view dependency insights setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the members can view dependency insights setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseOrganizationProjectsSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseOrganizationProjectsSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the organization projects setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the organization projects setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseOrganizationProjectsSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseOrganizationProjectsSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated organization projects setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the organization projects setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseProfileInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseProfile\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The Enterprise ID to update.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of the enterprise.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"description\",\"description\":\"The description of the enterprise.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"websiteUrl\",\"description\":\"The URL of the enterprise's website.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"location\",\"description\":\"The location of the enterprise.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseProfilePayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseProfile\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The updated enterprise.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseRepositoryProjectsSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseRepositoryProjectsSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the repository projects setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the repository projects setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseRepositoryProjectsSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseRepositoryProjectsSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated repository projects setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the repository projects setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseTeamDiscussionsSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseTeamDiscussionsSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the team discussions setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the team discussions setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledDisabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseTeamDiscussionsSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseTeamDiscussionsSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated team discussions setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the team discussions setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateEnterpriseTwoFactorAuthenticationRequiredSettingInput\",\"description\":\"Autogenerated input type of UpdateEnterpriseTwoFactorAuthenticationRequiredSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"enterpriseId\",\"description\":\"The ID of the enterprise on which to set the two factor authentication required setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the two factor authentication required setting on the enterprise.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"EnterpriseEnabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateEnterpriseTwoFactorAuthenticationRequiredSettingPayload\",\"description\":\"Autogenerated return type of UpdateEnterpriseTwoFactorAuthenticationRequiredSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enterprise\",\"description\":\"The enterprise with the updated two factor authentication required setting.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A message confirming the result of updating the two factor authentication required setting.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateIpAllowListEnabledSettingInput\",\"description\":\"Autogenerated input type of UpdateIpAllowListEnabledSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"ownerId\",\"description\":\"The ID of the owner on which to set the IP allow list enabled setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the IP allow list enabled setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IpAllowListEnabledSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateIpAllowListEnabledSettingPayload\",\"description\":\"Autogenerated return type of UpdateIpAllowListEnabledSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The IP allow list owner on which the setting was updated.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"IpAllowListOwner\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateIpAllowListEntryInput\",\"description\":\"Autogenerated input type of UpdateIpAllowListEntry\",\"fields\":null,\"inputFields\":[{\"name\":\"ipAllowListEntryId\",\"description\":\"The ID of the IP allow list entry to update.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"allowListValue\",\"description\":\"An IP address or range of addresses in CIDR notation.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"An optional name for the IP allow list entry.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isActive\",\"description\":\"Whether the IP allow list entry is active when an IP allow list is enabled.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateIpAllowListEntryPayload\",\"description\":\"Autogenerated return type of UpdateIpAllowListEntry\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ipAllowListEntry\",\"description\":\"The IP allow list entry that was updated.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"IpAllowListEntry\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateIssueCommentInput\",\"description\":\"Autogenerated input type of UpdateIssueComment\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the IssueComment to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The updated text of the comment.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateIssueCommentPayload\",\"description\":\"Autogenerated return type of UpdateIssueComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issueComment\",\"description\":\"The updated comment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"IssueComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateIssueInput\",\"description\":\"Autogenerated input type of UpdateIssue\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the Issue to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"title\",\"description\":\"The title for the issue.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The body for the issue description.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"assigneeIds\",\"description\":\"An array of Node IDs of users for this issue.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"milestoneId\",\"description\":\"The Node ID of the milestone for this issue.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"labelIds\",\"description\":\"An array of Node IDs of labels for this issue.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"state\",\"description\":\"The desired issue state.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"IssueState\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"projectIds\",\"description\":\"An array of Node IDs for projects associated with this issue.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateIssuePayload\",\"description\":\"Autogenerated return type of UpdateIssue\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issue\",\"description\":\"The issue.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Issue\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateNotificationRestrictionSettingInput\",\"description\":\"Autogenerated input type of UpdateNotificationRestrictionSetting\",\"fields\":null,\"inputFields\":[{\"name\":\"ownerId\",\"description\":\"The ID of the owner on which to set the restrict notifications setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"settingValue\",\"description\":\"The value for the restrict notifications setting.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"NotificationRestrictionSettingValue\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateNotificationRestrictionSettingPayload\",\"description\":\"Autogenerated return type of UpdateNotificationRestrictionSetting\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The owner on which the setting was updated.\",\"args\":[],\"type\":{\"kind\":\"UNION\",\"name\":\"VerifiableDomainOwner\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateProjectCardInput\",\"description\":\"Autogenerated input type of UpdateProjectCard\",\"fields\":null,\"inputFields\":[{\"name\":\"projectCardId\",\"description\":\"The ProjectCard ID to update.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"isArchived\",\"description\":\"Whether or not the ProjectCard should be archived\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"note\",\"description\":\"The note of ProjectCard.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateProjectCardPayload\",\"description\":\"Autogenerated return type of UpdateProjectCard\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectCard\",\"description\":\"The updated ProjectCard.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectCard\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateProjectColumnInput\",\"description\":\"Autogenerated input type of UpdateProjectColumn\",\"fields\":null,\"inputFields\":[{\"name\":\"projectColumnId\",\"description\":\"The ProjectColumn ID to update.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of project column.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateProjectColumnPayload\",\"description\":\"Autogenerated return type of UpdateProjectColumn\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectColumn\",\"description\":\"The updated project column.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"ProjectColumn\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateProjectInput\",\"description\":\"Autogenerated input type of UpdateProject\",\"fields\":null,\"inputFields\":[{\"name\":\"projectId\",\"description\":\"The Project ID to update.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The name of project.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The description of project.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"state\",\"description\":\"Whether the project is open or closed.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"ProjectState\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"public\",\"description\":\"Whether the project is public or not.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateProjectPayload\",\"description\":\"Autogenerated return type of UpdateProject\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"The updated project.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdatePullRequestInput\",\"description\":\"Autogenerated input type of UpdatePullRequest\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestId\",\"description\":\"The Node ID of the pull request.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"baseRefName\",\"description\":\"The name of the branch you want your changes pulled into. This should be an existing branch\\non the current repository.\\n\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"title\",\"description\":\"The title of the pull request.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The contents of the pull request.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"state\",\"description\":\"The target state of the pull request.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PullRequestUpdateState\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"maintainerCanModify\",\"description\":\"Indicates whether maintainers can modify the pull request.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"assigneeIds\",\"description\":\"An array of Node IDs of users for this pull request.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"milestoneId\",\"description\":\"The Node ID of the milestone for this pull request.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"labelIds\",\"description\":\"An array of Node IDs of labels for this pull request.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"projectIds\",\"description\":\"An array of Node IDs for projects associated with this pull request.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdatePullRequestPayload\",\"description\":\"Autogenerated return type of UpdatePullRequest\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequest\",\"description\":\"The updated pull request.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequest\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdatePullRequestReviewCommentInput\",\"description\":\"Autogenerated input type of UpdatePullRequestReviewComment\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestReviewCommentId\",\"description\":\"The Node ID of the comment to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The text of the comment.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdatePullRequestReviewCommentPayload\",\"description\":\"Autogenerated return type of UpdatePullRequestReviewComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReviewComment\",\"description\":\"The updated comment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReviewComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdatePullRequestReviewInput\",\"description\":\"Autogenerated input type of UpdatePullRequestReview\",\"fields\":null,\"inputFields\":[{\"name\":\"pullRequestReviewId\",\"description\":\"The Node ID of the pull request review to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The contents of the pull request review body.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdatePullRequestReviewPayload\",\"description\":\"Autogenerated return type of UpdatePullRequestReview\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequestReview\",\"description\":\"The updated pull request review.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestReview\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateRefInput\",\"description\":\"Autogenerated input type of UpdateRef\",\"fields\":null,\"inputFields\":[{\"name\":\"refId\",\"description\":\"The Node ID of the Ref to be updated.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"oid\",\"description\":\"The GitObjectID that the Ref shall be updated to target.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"GitObjectID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"force\",\"description\":\"Permit updates of branch Refs that are not fast-forwards?\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateRefPayload\",\"description\":\"Autogenerated return type of UpdateRef\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ref\",\"description\":\"The updated Ref.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Ref\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateRepositoryInput\",\"description\":\"Autogenerated input type of UpdateRepository\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The ID of the repository to update.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"name\",\"description\":\"The new name of the repository.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"description\",\"description\":\"A new description for the repository. Pass an empty string to erase the existing description.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"template\",\"description\":\"Whether this repository should be marked as a template such that anyone who can access it can create new repositories with the same files and directory structure.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"homepageUrl\",\"description\":\"The URL for a web page about this repository. Pass an empty string to erase the existing URL.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"hasWikiEnabled\",\"description\":\"Indicates if the repository should have the wiki feature enabled.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"hasIssuesEnabled\",\"description\":\"Indicates if the repository should have the issues feature enabled.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"hasProjectsEnabled\",\"description\":\"Indicates if the repository should have the project boards feature enabled.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateRepositoryPayload\",\"description\":\"Autogenerated return type of UpdateRepository\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The updated repository.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateSubscriptionInput\",\"description\":\"Autogenerated input type of UpdateSubscription\",\"fields\":null,\"inputFields\":[{\"name\":\"subscribableId\",\"description\":\"The Node ID of the subscribable object to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"state\",\"description\":\"The new state of the subscription.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"SubscriptionState\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateSubscriptionPayload\",\"description\":\"Autogenerated return type of UpdateSubscription\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subscribable\",\"description\":\"The input subscribable entity.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Subscribable\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateTeamDiscussionCommentInput\",\"description\":\"Autogenerated input type of UpdateTeamDiscussionComment\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the comment to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The updated text of the comment.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"bodyVersion\",\"description\":\"The current version of the body content.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateTeamDiscussionCommentPayload\",\"description\":\"Autogenerated return type of UpdateTeamDiscussionComment\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamDiscussionComment\",\"description\":\"The updated comment.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussionComment\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateTeamDiscussionInput\",\"description\":\"Autogenerated input type of UpdateTeamDiscussion\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The Node ID of the discussion to modify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"title\",\"description\":\"The updated title of the discussion.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"body\",\"description\":\"The updated text of the discussion.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"bodyVersion\",\"description\":\"The current version of the body content. If provided, this update operation will be rejected if the given version does not match the latest version on the server.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"pinned\",\"description\":\"If provided, sets the pinned state of the updated discussion.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateTeamDiscussionPayload\",\"description\":\"Autogenerated return type of UpdateTeamDiscussion\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"teamDiscussion\",\"description\":\"The updated discussion.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"TeamDiscussion\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UpdateTopicsInput\",\"description\":\"Autogenerated input type of UpdateTopics\",\"fields\":null,\"inputFields\":[{\"name\":\"repositoryId\",\"description\":\"The Node ID of the repository.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"topicNames\",\"description\":\"An array of topic names.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UpdateTopicsPayload\",\"description\":\"Autogenerated return type of UpdateTopics\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"invalidTopicNames\",\"description\":\"Names of the provided topics that are not valid.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"The updated repository.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"User\",\"description\":\"A user is an individual's account on GitHub that owns repositories and can make new content.\",\"fields\":[{\"name\":\"anyPinnableItems\",\"description\":\"Determine if this repository owner has any items that can be pinned to their profile.\",\"args\":[{\"name\":\"type\",\"description\":\"Filter to only a particular kind of pinnable item.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"avatarUrl\",\"description\":\"A URL pointing to the user's public avatar.\",\"args\":[{\"name\":\"size\",\"description\":\"The size of the resulting square image.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bio\",\"description\":\"The user's public profile bio.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"bioHTML\",\"description\":\"The user's public profile bio as HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"commitComments\",\"description\":\"A list of commit comments made by this user.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"CommitCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"company\",\"description\":\"The user's public profile company.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"companyHTML\",\"description\":\"The user's public profile company as HTML.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"contributionsCollection\",\"description\":\"The collection of contributions this user has made to different repositories.\",\"args\":[{\"name\":\"organizationID\",\"description\":\"The ID of the organization used to filter contributions.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"from\",\"description\":\"Only contributions made at this time or later will be counted. If omitted, defaults to a year ago.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"to\",\"description\":\"Only contributions made before and up to and including this time will be counted. If omitted, defaults to the current time.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ContributionsCollection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"email\",\"description\":\"The user's publicly visible profile email.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"followers\",\"description\":\"A list of users the given user is followed by.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"FollowerConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"following\",\"description\":\"A list of users the given user is following.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"FollowingConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"gist\",\"description\":\"Find gist by repo name.\",\"args\":[{\"name\":\"name\",\"description\":\"The gist name to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Gist\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"gistComments\",\"description\":\"A list of gist comments made by this user.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GistCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"gists\",\"description\":\"A list of the Gists the user has created.\",\"args\":[{\"name\":\"privacy\",\"description\":\"Filters Gists according to privacy.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"GistPrivacy\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for gists returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"GistOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"GistConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasSponsorsListing\",\"description\":\"True if this user/organization has a GitHub Sponsors listing.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hovercard\",\"description\":\"The hovercard information for this user in a given context\",\"args\":[{\"name\":\"primarySubjectId\",\"description\":\"The ID of the subject to get the hovercard in the context of\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"Hovercard\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"interactionAbility\",\"description\":\"The interaction ability settings for this user.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryInteractionAbility\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isBountyHunter\",\"description\":\"Whether or not this user is a participant in the GitHub Security Bug Bounty.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isCampusExpert\",\"description\":\"Whether or not this user is a participant in the GitHub Campus Experts Program.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDeveloperProgramMember\",\"description\":\"Whether or not this user is a GitHub Developer Program member.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isEmployee\",\"description\":\"Whether or not this user is a GitHub employee.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isGitHubStar\",\"description\":\"Whether or not this user is a member of the GitHub Stars Program.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isHireable\",\"description\":\"Whether or not the user has marked themselves as for hire.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSiteAdmin\",\"description\":\"Whether or not this user is a site administrator.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSponsoredBy\",\"description\":\"Check if the given account is sponsoring this user/organization.\",\"args\":[{\"name\":\"accountLogin\",\"description\":\"The target account's login.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isSponsoringViewer\",\"description\":\"True if the viewer is sponsored by this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isViewer\",\"description\":\"Whether or not this user is the viewing user.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issueComments\",\"description\":\"A list of issue comments made by this user.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for issue comments returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueCommentOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueCommentConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"issues\",\"description\":\"A list of issues associated with this user.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for issues returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the issues by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"IssueState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"filterBy\",\"description\":\"Filtering options for issues returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueFilters\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"IssueConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"itemShowcase\",\"description\":\"Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProfileItemShowcase\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"location\",\"description\":\"The user's public profile location.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"login\",\"description\":\"The username used to login.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":\"The user's public profile name.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"Find an organization by its login that the user belongs to.\",\"args\":[{\"name\":\"login\",\"description\":\"The login of the organization to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizationVerifiedDomainEmails\",\"description\":\"Verified email addresses that match verified domains for a specified organization the user is a member of.\",\"args\":[{\"name\":\"login\",\"description\":\"The login of the organization to match verified domains from.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organizations\",\"description\":\"A list of organizations the user belongs to.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"OrganizationConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"packages\",\"description\":\"A list of packages under the owner.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"names\",\"description\":\"Find packages by their names.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"repositoryId\",\"description\":\"Find packages in a repository by ID.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"packageType\",\"description\":\"Filter registry package by type.\",\"type\":{\"kind\":\"ENUM\",\"name\":\"PackageType\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering of the returned packages.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"PackageOrder\",\"ofType\":null},\"defaultValue\":\"{field: CREATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PackageConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnableItems\",\"description\":\"A list of repositories and gists this profile owner can pin to their profile.\",\"args\":[{\"name\":\"types\",\"description\":\"Filter the types of pinnable items that are returned.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnableItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnedItems\",\"description\":\"A list of repositories and gists this profile owner has pinned to their profile\",\"args\":[{\"name\":\"types\",\"description\":\"Filter the types of pinned items that are returned.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PinnableItemType\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PinnableItemConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pinnedItemsRemaining\",\"description\":\"Returns how many more items this profile owner can pin to their profile.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"project\",\"description\":\"Find project by number.\",\"args\":[{\"name\":\"number\",\"description\":\"The project number to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Project\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projects\",\"description\":\"A list of projects under the owner.\",\"args\":[{\"name\":\"orderBy\",\"description\":\"Ordering options for projects returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"ProjectOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"search\",\"description\":\"Query to search projects by, currently only searching by name.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"states\",\"description\":\"A list of states to filter the projects by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"ProjectState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"ProjectConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectsResourcePath\",\"description\":\"The HTTP path listing user's projects\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"projectsUrl\",\"description\":\"The HTTP URL listing user's projects\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"publicKeys\",\"description\":\"A list of public keys associated with this user.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PublicKeyConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pullRequests\",\"description\":\"A list of pull requests associated with this user.\",\"args\":[{\"name\":\"states\",\"description\":\"A list of states to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"PullRequestState\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"labels\",\"description\":\"A list of label names to filter the pull requests by.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}}},\"defaultValue\":null},{\"name\":\"headRefName\",\"description\":\"The head ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"baseRefName\",\"description\":\"The base ref name to filter the pull requests by.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for pull requests returned from the connection.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"IssueOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PullRequestConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositories\",\"description\":\"A list of repositories that the user owns.\",\"args\":[{\"name\":\"privacy\",\"description\":\"If non-null, filters repositories according to privacy\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPrivacy\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"affiliations\",\"description\":\"Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"ownerAffiliations\",\"description\":\"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":\"[OWNER, COLLABORATOR]\"},{\"name\":\"isLocked\",\"description\":\"If non-null, filters repositories according to whether they have been locked\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isFork\",\"description\":\"If non-null, filters repositories according to whether they are forks of another repository\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repositoriesContributedTo\",\"description\":\"A list of repositories that the user recently contributed to.\",\"args\":[{\"name\":\"privacy\",\"description\":\"If non-null, filters repositories according to privacy\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPrivacy\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"isLocked\",\"description\":\"If non-null, filters repositories according to whether they have been locked\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"includeUserRepositories\",\"description\":\"If true, include user repositories\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"contributionTypes\",\"description\":\"If non-null, include only the specified types of contributions. The GitHub.com UI uses [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryContributionType\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"repository\",\"description\":\"Find Repository.\",\"args\":[{\"name\":\"name\",\"description\":\"Name of Repository to find.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"defaultValue\":null}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Repository\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"resourcePath\",\"description\":\"The HTTP path for this user\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"savedReplies\",\"description\":\"Replies this user has saved\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"The field to order saved replies by.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SavedReplyOrder\",\"ofType\":null},\"defaultValue\":\"{field: UPDATED_AT, direction: DESC}\"}],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SavedReplyConnection\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorsListing\",\"description\":\"The GitHub Sponsors listing for this user or organization.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"SponsorsListing\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipForViewerAsSponsor\",\"description\":\"The viewer's sponsorship of this entity.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Sponsorship\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipsAsMaintainer\",\"description\":\"This object's sponsorships as the maintainer.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"includePrivate\",\"description\":\"Whether or not to include private sponsorships in the result set\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"},{\"name\":\"orderBy\",\"description\":\"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorshipOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorshipConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"sponsorshipsAsSponsor\",\"description\":\"This object's sponsorships as the sponsor.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"SponsorshipOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"SponsorshipConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"starredRepositories\",\"description\":\"Repositories the user has starred.\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"ownedByViewer\",\"description\":\"Filters starred repositories to only return repositories owned by the viewer.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Order for connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"StarOrder\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"StarredRepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"status\",\"description\":\"The user's description of what they're currently doing.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserStatus\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"topRepositories\",\"description\":\"Repositories the user has contributed to, ordered by contribution rank, plus repositories the user has created\\n\",\"args\":[{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories returned from the connection\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"since\",\"description\":\"How far back in time to fetch contributed repositories\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"twitterUsername\",\"description\":\"The user's Twitter username.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"url\",\"description\":\"The HTTP URL for this user\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanChangePinnedItems\",\"description\":\"Can the viewer pin repositories and gists to the profile?\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanCreateProjects\",\"description\":\"Can the current viewer create new projects on this owner.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanFollow\",\"description\":\"Whether or not the viewer is able to follow the user.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerCanSponsor\",\"description\":\"Whether or not the viewer is able to sponsor this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerIsFollowing\",\"description\":\"Whether or not this user is followed by the viewer.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewerIsSponsoring\",\"description\":\"True if the viewer is sponsoring this user/organization.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"watching\",\"description\":\"A list of repositories the given user is watching.\",\"args\":[{\"name\":\"privacy\",\"description\":\"If non-null, filters repositories according to privacy\",\"type\":{\"kind\":\"ENUM\",\"name\":\"RepositoryPrivacy\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"orderBy\",\"description\":\"Ordering options for repositories returned from the connection\",\"type\":{\"kind\":\"INPUT_OBJECT\",\"name\":\"RepositoryOrder\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"affiliations\",\"description\":\"Affiliation options for repositories returned from the connection. If none specified, the results will include repositories for which the current viewer is an owner or collaborator, or member.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"ownerAffiliations\",\"description\":\"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.\",\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"RepositoryAffiliation\",\"ofType\":null}},\"defaultValue\":\"[OWNER, COLLABORATOR]\"},{\"name\":\"isLocked\",\"description\":\"If non-null, filters repositories according to whether they have been locked\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"after\",\"description\":\"Returns the elements in the list that come after the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"before\",\"description\":\"Returns the elements in the list that come before the specified cursor.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"first\",\"description\":\"Returns the first _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null},{\"name\":\"last\",\"description\":\"Returns the last _n_ elements from the list.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"defaultValue\":null}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"RepositoryConnection\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"websiteUrl\",\"description\":\"A URL pointing to the user's public website/blog.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"PackageOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"ProjectOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"RepositoryOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"UniformResourceLocatable\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"ProfileOwner\",\"ofType\":null},{\"kind\":\"INTERFACE\",\"name\":\"Sponsorable\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"UserBlockDuration\",\"description\":\"The possible durations that a user can be blocked for.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"ONE_DAY\",\"description\":\"The user was blocked for 1 day\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"THREE_DAYS\",\"description\":\"The user was blocked for 3 days\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ONE_WEEK\",\"description\":\"The user was blocked for 7 days\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ONE_MONTH\",\"description\":\"The user was blocked for 30 days\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"PERMANENT\",\"description\":\"The user was blocked permanently\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserBlockedEvent\",\"description\":\"Represents a 'user_blocked' event on a given user.\",\"fields\":[{\"name\":\"actor\",\"description\":\"Identifies the actor who performed the event.\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"blockDuration\",\"description\":\"Number of days that the user was blocked for.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"UserBlockDuration\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subject\",\"description\":\"The user who was blocked.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserConnection\",\"description\":\"The connection type for User.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserContentEdit\",\"description\":\"An edit on user content\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletedAt\",\"description\":\"Identifies the date and time when the object was deleted.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deletedBy\",\"description\":\"The actor who deleted this content\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"diff\",\"description\":\"A summary of the changes for this edit\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editedAt\",\"description\":\"When this content was edited\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"editor\",\"description\":\"The actor who edited this content\",\"args\":[],\"type\":{\"kind\":\"INTERFACE\",\"name\":\"Actor\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserContentEditConnection\",\"description\":\"A list of edits to content.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEditEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEdit\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserContentEditEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserContentEdit\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserEdge\",\"description\":\"Represents a user.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserEmailMetadata\",\"description\":\"Email attributes from External Identity\",\"fields\":[{\"name\":\"primary\",\"description\":\"Boolean to identify primary emails\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"type\",\"description\":\"Type of email\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"value\",\"description\":\"Email id\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserStatus\",\"description\":\"The user's description of what they're currently doing.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"emoji\",\"description\":\"An emoji summarizing the user's status.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"emojiHTML\",\"description\":\"The status emoji as HTML.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"HTML\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"expiresAt\",\"description\":\"If set, the status will not be shown after this date.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":\"ID of the object.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"indicatesLimitedAvailability\",\"description\":\"Whether this status indicates the user is not fully available on GitHub.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"message\",\"description\":\"A brief message describing what the user is doing.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"organization\",\"description\":\"The organization whose members can see this status. If null, this status is publicly visible.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"user\",\"description\":\"The user who has this status.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserStatusConnection\",\"description\":\"The connection type for UserStatus.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserStatusEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"UserStatus\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"UserStatusEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"UserStatus\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"UserStatusOrder\",\"description\":\"Ordering options for user status connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order user statuses by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"UserStatusOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"UserStatusOrderField\",\"description\":\"Properties by which user status connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"UPDATED_AT\",\"description\":\"Order user statuses by when they were updated.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomain\",\"description\":\"A domain that can be verified for an organization or an enterprise.\",\"fields\":[{\"name\":\"createdAt\",\"description\":\"Identifies the date and time when the object was created.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"databaseId\",\"description\":\"Identifies the primary key from the database.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"dnsHostName\",\"description\":\"The DNS host name that should be used for verification.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"domain\",\"description\":\"The unicode encoded domain.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasFoundHostName\",\"description\":\"Whether a TXT record for verification with the expected host name was found.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"hasFoundVerificationToken\",\"description\":\"Whether a TXT record for verification with the expected verification token was found.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"id\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isRequiredForPolicyEnforcement\",\"description\":\"Whether this domain is required to exist for an organization or enterprise policy to be enforced.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isVerified\",\"description\":\"Whether or not the domain is verified.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"owner\",\"description\":\"The owner of the domain.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"UNION\",\"name\":\"VerifiableDomainOwner\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"punycodeEncodedDomain\",\"description\":\"The punycode encoded domain.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"URI\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"tokenExpirationTime\",\"description\":\"The time that the current verification token will expire.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"updatedAt\",\"description\":\"Identifies the date and time when the object was last updated.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"DateTime\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"verificationToken\",\"description\":\"The current verification token for the domain.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"Node\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomainConnection\",\"description\":\"The connection type for VerifiableDomain.\",\"fields\":[{\"name\":\"edges\",\"description\":\"A list of edges.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomainEdge\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"nodes\",\"description\":\"A list of nodes.\",\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomain\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"pageInfo\",\"description\":\"Information to aid in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"PageInfo\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"totalCount\",\"description\":\"Identifies the total count of items in the connection.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Int\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomainEdge\",\"description\":\"An edge in a connection.\",\"fields\":[{\"name\":\"cursor\",\"description\":\"A cursor for use in pagination.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"node\",\"description\":\"The item at the end of the edge.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomain\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"INPUT_OBJECT\",\"name\":\"VerifiableDomainOrder\",\"description\":\"Ordering options for verifiable domain connections.\",\"fields\":null,\"inputFields\":[{\"name\":\"field\",\"description\":\"The field to order verifiable domains by.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"VerifiableDomainOrderField\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"direction\",\"description\":\"The ordering direction.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"OrderDirection\",\"ofType\":null}},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"VerifiableDomainOrderField\",\"description\":\"Properties by which verifiable domain connections can be ordered.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"DOMAIN\",\"description\":\"Order verifiable domains by the domain name.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"CREATED_AT\",\"description\":\"Order verifiable domains by their creation date.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"UNION\",\"name\":\"VerifiableDomainOwner\",\"description\":\"Types that can own a verifiable domain.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":[{\"kind\":\"OBJECT\",\"name\":\"Enterprise\",\"ofType\":null},{\"kind\":\"OBJECT\",\"name\":\"Organization\",\"ofType\":null}]},{\"kind\":\"INPUT_OBJECT\",\"name\":\"VerifyVerifiableDomainInput\",\"description\":\"Autogenerated input type of VerifyVerifiableDomain\",\"fields\":null,\"inputFields\":[{\"name\":\"id\",\"description\":\"The ID of the verifiable domain to verify.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"ID\",\"ofType\":null}},\"defaultValue\":null},{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":null}],\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"VerifyVerifiableDomainPayload\",\"description\":\"Autogenerated return type of VerifyVerifiableDomain\",\"fields\":[{\"name\":\"clientMutationId\",\"description\":\"A unique identifier for the client performing the mutation.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"domain\",\"description\":\"The verifiable domain that was verified.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"VerifiableDomain\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"ViewerHovercardContext\",\"description\":\"A hovercard context with a message describing how the viewer is related.\",\"fields\":[{\"name\":\"message\",\"description\":\"A string describing this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"octicon\",\"description\":\"An octicon to accompany this context\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"viewer\",\"description\":\"Identifies the user who is related to this context.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"User\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[{\"kind\":\"INTERFACE\",\"name\":\"HovercardContext\",\"ofType\":null}],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"SCALAR\",\"name\":\"X509Certificate\",\"description\":\"A valid x509 certificate string\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"__Directive\",\"description\":\"A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\\n\\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.\",\"fields\":[{\"name\":\"args\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__InputValue\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"locations\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"__DirectiveLocation\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"onField\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"Use `locations`.\"},{\"name\":\"onFragment\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"Use `locations`.\"},{\"name\":\"onOperation\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":true,\"deprecationReason\":\"Use `locations`.\"}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"__DirectiveLocation\",\"description\":\"A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"QUERY\",\"description\":\"Location adjacent to a query operation.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"MUTATION\",\"description\":\"Location adjacent to a mutation operation.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SUBSCRIPTION\",\"description\":\"Location adjacent to a subscription operation.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FIELD\",\"description\":\"Location adjacent to a field.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FRAGMENT_DEFINITION\",\"description\":\"Location adjacent to a fragment definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FRAGMENT_SPREAD\",\"description\":\"Location adjacent to a fragment spread.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INLINE_FRAGMENT\",\"description\":\"Location adjacent to an inline fragment.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SCHEMA\",\"description\":\"Location adjacent to a schema definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"SCALAR\",\"description\":\"Location adjacent to a scalar definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OBJECT\",\"description\":\"Location adjacent to an object type definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"FIELD_DEFINITION\",\"description\":\"Location adjacent to a field definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ARGUMENT_DEFINITION\",\"description\":\"Location adjacent to an argument definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INTERFACE\",\"description\":\"Location adjacent to an interface definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNION\",\"description\":\"Location adjacent to a union definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ENUM\",\"description\":\"Location adjacent to an enum definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ENUM_VALUE\",\"description\":\"Location adjacent to an enum value definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INPUT_OBJECT\",\"description\":\"Location adjacent to an input object type definition.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INPUT_FIELD_DEFINITION\",\"description\":\"Location adjacent to an input object field definition.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"__EnumValue\",\"description\":\"One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.\",\"fields\":[{\"name\":\"deprecationReason\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDeprecated\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"__Field\",\"description\":\"Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.\",\"fields\":[{\"name\":\"args\",\"description\":null,\"args\":[{\"name\":\"includeDeprecated\",\"description\":null,\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__InputValue\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deprecationReason\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDeprecated\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"type\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"__InputValue\",\"description\":\"Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.\",\"fields\":[{\"name\":\"defaultValue\",\"description\":\"A GraphQL-formatted string representing the default value for this input value.\",\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"deprecationReason\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"description\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"isDeprecated\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"type\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"__Schema\",\"description\":\"A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.\",\"fields\":[{\"name\":\"directives\",\"description\":\"A list of all directives supported by this server.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__Directive\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"mutationType\",\"description\":\"If this server supports mutation, the type that mutation operations will be rooted at.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"queryType\",\"description\":\"The type that query operations will be rooted at.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"subscriptionType\",\"description\":\"If this server support subscription, the type that subscription operations will be rooted at.\",\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"types\",\"description\":\"A list of all types supported by this server.\",\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null}}}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"description\":\"The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\\n\\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.\",\"fields\":[{\"name\":\"description\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"enumValues\",\"description\":null,\"args\":[{\"name\":\"includeDeprecated\",\"description\":null,\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__EnumValue\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"fields\",\"description\":null,\"args\":[{\"name\":\"includeDeprecated\",\"description\":null,\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__Field\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"inputFields\",\"description\":null,\"args\":[{\"name\":\"includeDeprecated\",\"description\":null,\"type\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null},\"defaultValue\":\"false\"}],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__InputValue\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"interfaces\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"kind\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"ENUM\",\"name\":\"__TypeKind\",\"ofType\":null}},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"name\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ofType\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null},\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"possibleTypes\",\"description\":null,\"args\":[],\"type\":{\"kind\":\"LIST\",\"name\":null,\"ofType\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"OBJECT\",\"name\":\"__Type\",\"ofType\":null}}},\"isDeprecated\":false,\"deprecationReason\":null}],\"inputFields\":null,\"interfaces\":[],\"enumValues\":null,\"possibleTypes\":null},{\"kind\":\"ENUM\",\"name\":\"__TypeKind\",\"description\":\"An enum describing what kind of type a given `__Type` is.\",\"fields\":null,\"inputFields\":null,\"interfaces\":null,\"enumValues\":[{\"name\":\"SCALAR\",\"description\":\"Indicates this type is a scalar.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"OBJECT\",\"description\":\"Indicates this type is an object. `fields` and `interfaces` are valid fields.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INTERFACE\",\"description\":\"Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"UNION\",\"description\":\"Indicates this type is a union. `possibleTypes` is a valid field.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"ENUM\",\"description\":\"Indicates this type is an enum. `enumValues` is a valid field.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"INPUT_OBJECT\",\"description\":\"Indicates this type is an input object. `inputFields` is a valid field.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"LIST\",\"description\":\"Indicates this type is a list. `ofType` is a valid field.\",\"isDeprecated\":false,\"deprecationReason\":null},{\"name\":\"NON_NULL\",\"description\":\"Indicates this type is a non-null. `ofType` is a valid field.\",\"isDeprecated\":false,\"deprecationReason\":null}],\"possibleTypes\":null}],\"directives\":[{\"name\":\"include\",\"description\":\"Directs the executor to include this field or fragment only when the `if` argument is true.\",\"locations\":[\"FIELD\",\"FRAGMENT_SPREAD\",\"INLINE_FRAGMENT\"],\"args\":[{\"name\":\"if\",\"description\":\"Included when true.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null}]},{\"name\":\"skip\",\"description\":\"Directs the executor to skip this field or fragment when the `if` argument is true.\",\"locations\":[\"FIELD\",\"FRAGMENT_SPREAD\",\"INLINE_FRAGMENT\"],\"args\":[{\"name\":\"if\",\"description\":\"Skipped when true.\",\"type\":{\"kind\":\"NON_NULL\",\"name\":null,\"ofType\":{\"kind\":\"SCALAR\",\"name\":\"Boolean\",\"ofType\":null}},\"defaultValue\":null}]},{\"name\":\"deprecated\",\"description\":\"Marks an element of a GraphQL schema as no longer supported.\",\"locations\":[\"FIELD_DEFINITION\",\"ENUM_VALUE\",\"ARGUMENT_DEFINITION\",\"INPUT_FIELD_DEFINITION\"],\"args\":[{\"name\":\"reason\",\"description\":\"Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).\",\"type\":{\"kind\":\"SCALAR\",\"name\":\"String\",\"ofType\":null},\"defaultValue\":\"\\\"No longer supported\\\"\"}]}]}}}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/compile-warnings-error.gradle",
    "content": "import org.gradle.api.tasks.compile.JavaCompile\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\ntasks.withType(JavaCompile) {\n    options.compilerArgs += \"-Werror\"\n}\n\ntasks.withType(KotlinCompile) {\n    kotlinOptions.allWarningsAsErrors = true\n}\n\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/IncludeRepoTask.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\n\npackage io.spring.gradle\n\nimport groovy.transform.CompileStatic\nimport groovy.transform.TypeChecked\nimport groovy.transform.TypeCheckingMode\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.Task\nimport org.gradle.api.provider.Property\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.OutputDirectory\nimport org.gradle.api.tasks.TaskAction\n\n/**\n * Checkout a project template from a git repository.\n *\n * @author Marcus Da Coregio\n */\n@CompileStatic\nabstract class IncludeRepoTask extends DefaultTask {\n\n\tprivate static final String DEFAULT_URI_PREFIX = 'https://github.com/'\n\n\t/**\n\t * Git repository to use. Will be prefixed with {@link #DEFAULT_URI_PREFIX} if it isn't already\n\t * @return\n\t */\n\t@Input\n\tabstract Property<String> getRepository();\n\n\t/**\n\t * Git reference to use.\n\t */\n\t@Input\n\tabstract Property<String> getRef()\n\n\t/**\n\t * Directory where the project template should be copied.\n\t */\n\t@OutputDirectory\n\tFile outputDirectory = project.file(\"$project.buildDir/$name\")\n\n\t@TaskAction\n\tvoid checkoutAndCopy() {\n\t\toutputDirectory.deleteDir()\n\t\tFile checkoutDir = checkout(this, getRemoteUri(), ref.get())\n\t\tmoveToOutputDir(checkoutDir, outputDirectory)\n\t}\n\n\tprivate static File cleanTemporaryDir(Task task, File tmpDir) {\n\t\tif (tmpDir.exists()) {\n\t\t\ttask.project.delete(tmpDir)\n\t\t}\n\t\treturn tmpDir\n\t}\n\n\tstatic File checkout(Task task, String remoteUri, String ref) {\n\t\tcheckout(task, remoteUri, ref, task.getTemporaryDir())\n\t}\n\n\t@TypeChecked(TypeCheckingMode.SKIP)\n\tstatic File checkout(Task task, String remoteUri, String ref, File checkoutDir) {\n\t\tcleanTemporaryDir(task, checkoutDir)\n\t\ttask.project.exec {\n\t\t\tcommandLine = [\"git\", \"clone\", \"--no-checkout\", remoteUri, checkoutDir.absolutePath]\n\t\t\terrorOutput = System.err\n\t\t}\n\t\ttask.project.exec {\n\t\t\tcommandLine = [\"git\", \"checkout\", ref]\n\t\t\tworkingDir = checkoutDir\n\t\t\terrorOutput = System.err\n\t\t}\n\t\treturn checkoutDir\n\t}\n\n\tprivate static void moveToOutputDir(File tmpDir, File outputDirectory) {\n\t\tFile baseDir = tmpDir\n\t\tbaseDir.renameTo(outputDirectory)\n\t}\n\n\tprivate String getRemoteUri() {\n\t\tString remoteUri = this.repository.get()\n\t\tif (remoteUri.startsWith(DEFAULT_URI_PREFIX)) {\n\t\t\treturn remoteUri\n\t\t}\n\t\treturn DEFAULT_URI_PREFIX + remoteUri\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.plugins.GroovyPlugin;\nimport org.gradle.api.plugins.JavaPlugin\nimport org.gradle.api.plugins.PluginManager;\nimport org.gradle.plugins.ide.eclipse.EclipseWtpPlugin;\nimport org.gradle.plugins.ide.idea.IdeaPlugin;\nimport org.springframework.gradle.CopyPropertiesPlugin\nimport org.springframework.gradle.propdeps.PropDepsEclipsePlugin\nimport org.springframework.gradle.propdeps.PropDepsIdeaPlugin\nimport org.springframework.gradle.propdeps.PropDepsPlugin;\n\n/**\n * @author Rob Winch\n */\npublic abstract class AbstractSpringJavaPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic final void apply(Project project) {\n\t\tPluginManager pluginManager = project.getPluginManager();\n\t\tpluginManager.apply(JavaPlugin.class);\n\t\tpluginManager.apply(ManagementConfigurationPlugin.class)\n\t\tif (project.file(\"src/main/groovy\").exists()\n\t\t\t\t|| project.file(\"src/test/groovy\").exists()\n\t\t\t\t|| project.file(\"src/integration-test/groovy\").exists()) {\n\t\t\tpluginManager.apply(GroovyPlugin.class);\n\t\t}\n\t\tpluginManager.apply(\"io.spring.convention.repository\");\n\t\tpluginManager.apply(EclipseWtpPlugin);\n\t\tpluginManager.apply(IdeaPlugin);\n\t\tpluginManager.apply(PropDepsPlugin);\n\t\tpluginManager.apply(PropDepsEclipsePlugin);\n\t\tpluginManager.apply(PropDepsIdeaPlugin);\n\t\tpluginManager.apply(\"io.spring.convention.tests-configuration\");\n\t\tpluginManager.apply(\"io.spring.convention.integration-test\");\n\t\tpluginManager.apply(\"io.spring.convention.javadoc-options\");\n\t\tpluginManager.apply(\"io.spring.convention.checkstyle\");\n\t\tpluginManager.apply(CopyPropertiesPlugin);\n\t\tpluginManager.apply(\"io.spring.convention.eclipse\");\n\n\t\tproject.jar {\n\t\t\tmanifest.attributes[\"Created-By\"] =\n\t\t\t\t\t\"${System.getProperty(\"java.version\")} (${System.getProperty(\"java.specification.vendor\")})\"\n\t\t\tmanifest.attributes[\"Implementation-Title\"] = project.name\n\t\t\tmanifest.attributes[\"Implementation-Version\"] = project.version\n\t\t\tmanifest.attributes[\"Automatic-Module-Name\"] = project.name.replace('-', '.')\n\t\t}\n        project.test {\n            useJUnitPlatform()\n        }\n\t\tadditionalPlugins(project);\n\t}\n\n\tprotected abstract void additionalPlugins(Project project);\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/ArtifactoryPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\npackage io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.publish.maven.plugins.MavenPublishPlugin\n\nclass ArtifactoryPlugin implements Plugin<Project> {\n\n\tprivate static final String ARTIFACTORY_URL_NAME = \"ARTIFACTORY_URL\"\n\n\tprivate static final String ARTIFACTORY_SNAPSHOT_REPOSITORY = \"ARTIFACTORY_SNAPSHOT_REPOSITORY\"\n\n\tprivate static final String ARTIFACTORY_MILESTONE_REPOSITORY = \"ARTIFACTORY_MILESTONE_REPOSITORY\"\n\n\tprivate static final String ARTIFACTORY_RELEASE_REPOSITORY = \"ARTIFACTORY_RELEASE_REPOSITORY\"\n\n\tprivate static final String ARTIFACTORY_PROJECT_KEY = \"ARTIFACTORY_PROJECT_KEY\"\n\n\tprivate static final String ARTIFACTORY_BUILD_NAME = \"ARTIFACTORY_BUILD_NAME\"\n\n\tprivate static final String ARTIFACTORY_BUILD_NUMBER = \"ARTIFACTORY_BUILD_NUMBER\"\n\n\tprivate static final String ARTIFACTORY_BUILD_URL = \"ARTIFACTORY_BUILD_URL\"\n\n\tprivate static final String ARTIFACTORY_BUILD_AGENT_NAME = \"ARTIFACTORY_BUILD_AGENT_NAME\"\n\n\tprivate static final String ARTIFACTORY_BUILD_AGENT_VERSION = \"ARTIFACTORY_BUILD_AGENT_VERSION\"\n\n\tprivate static final String ARTIFACTORY_USER_AGENT_NAME = \"ARTIFACTORY_USER_AGENT_NAME\"\n\n\tprivate static final String ARTIFACTORY_USER_AGENT_VERSION = \"ARTIFACTORY_USER_AGENT_VERSION\"\n\n\tprivate static final String ARTIFACTORY_VCS_REVISION = \"ARTIFACTORY_VCS_REVISION\"\n\n\tprivate static final String DEFAULT_ARTIFACTORY_URL = \"https://repo.spring.io\"\n\n\tprivate static final String DEFAULT_ARTIFACTORY_SNAPSHOT_REPOSITORY = \"libs-snapshot-local\"\n\n\tprivate static final String DEFAULT_ARTIFACTORY_MILESTONE_REPOSITORY = \"libs-milestone-local\"\n\n\tprivate static final String DEFAULT_ARTIFACTORY_RELEASE_REPOSITORY = \"libs-release-local\"\n\n\t@Override\n\tvoid apply(Project project) {\n\t\tproject.plugins.apply('com.jfrog.artifactory')\n\t\tString name = Utils.getProjectName(project);\n\t\tboolean isSnapshot = Utils.isSnapshot(project);\n\t\tboolean isMilestone = Utils.isMilestone(project);\n\t\tMap<String, String> env = System.getenv()\n\t\tString artifactoryUrl = env.getOrDefault(ARTIFACTORY_URL_NAME, DEFAULT_ARTIFACTORY_URL)\n\t\tString snapshotRepository = env.getOrDefault(ARTIFACTORY_SNAPSHOT_REPOSITORY, DEFAULT_ARTIFACTORY_SNAPSHOT_REPOSITORY)\n\t\tString milestoneRepository = env.getOrDefault(ARTIFACTORY_MILESTONE_REPOSITORY, DEFAULT_ARTIFACTORY_MILESTONE_REPOSITORY)\n\t\tString releaseRepository = env.getOrDefault(ARTIFACTORY_RELEASE_REPOSITORY, DEFAULT_ARTIFACTORY_RELEASE_REPOSITORY)\n\t\tString projectKey = env.get(ARTIFACTORY_PROJECT_KEY)\n\t\tString buildName = env.get(ARTIFACTORY_BUILD_NAME)\n\t\tString buildNumber = env.get(ARTIFACTORY_BUILD_NUMBER)\n\t\tString buildUrl = env.get(ARTIFACTORY_BUILD_URL)\n\t\tString buildAgentName = env.get(ARTIFACTORY_BUILD_AGENT_NAME)\n\t\tString buildAgentVersion = env.get(ARTIFACTORY_BUILD_AGENT_VERSION)\n\t\tString userAgentName = env.get(ARTIFACTORY_USER_AGENT_NAME)\n\t\tString userAgentVersion = env.get(ARTIFACTORY_USER_AGENT_VERSION)\n\t\tString vcsRevision = env.get(ARTIFACTORY_VCS_REVISION)\n\t\tproject.artifactory {\n\t\t\tcontextUrl = artifactoryUrl\n\t\t\tpublish {\n\t\t\t\trepository {\n\t\t\t\t\trepoKey = isSnapshot ? snapshotRepository : isMilestone ? milestoneRepository : releaseRepository\n\t\t\t\t\tif(project.hasProperty('artifactoryUsername')) {\n\t\t\t\t\t\tusername = project.artifactoryUsername\n\t\t\t\t\t\tpassword = project.artifactoryPassword\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdef buildInfo = clientConfig.info\n\t\t\tif (projectKey != null) {\n\t\t\t\tbuildInfo.setProject(projectKey)\n\t\t\t}\n\t\t\tif (buildName != null) {\n\t\t\t\tbuildInfo.setBuildName(buildName)\n\t\t\t}\n\t\t\tif (buildNumber != null) {\n\t\t\t\tbuildInfo.setBuildNumber(buildNumber)\n\t\t\t}\n\t\t\tif (buildUrl != null) {\n\t\t\t\tbuildInfo.setBuildUrl(buildUrl)\n\t\t\t}\n\t\t\tif (buildAgentName != null) {\n\t\t\t\tbuildInfo.setBuildAgentName(buildAgentName)\n\t\t\t}\n\t\t\tif (buildAgentVersion != null) {\n\t\t\t\tbuildInfo.setBuildAgentVersion(buildAgentVersion)\n\t\t\t}\n\t\t\tif (userAgentName != null) {\n\t\t\t\tbuildInfo.setAgentName(userAgentName)\n\t\t\t}\n\t\t\tif (userAgentVersion != null) {\n\t\t\t\tbuildInfo.setAgentVersion(userAgentVersion)\n\t\t\t}\n\t\t\tif (vcsRevision != null) {\n\t\t\t\tbuildInfo.setVcsRevision(vcsRevision)\n\t\t\t}\n\t\t}\n\t\tproject.plugins.withType(MavenPublishPlugin) {\n\t\t\tproject.artifactory {\n\t\t\t\tpublish {\n\t\t\t\t\tdefaults {\n\t\t\t\t\t\tpublications('mavenJava')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport org.gradle.api.plugins.JavaPlugin\n\n/**\n * Adds and configures Checkstyle plugin.\n *\n * @author Vedran Pavic\n */\nclass CheckstylePlugin implements Plugin<Project> {\n\n\tfinal CHECKSTYLE_DIR = 'etc/checkstyle'\n\n\t@Override\n\tvoid apply(Project project) {\n\t\tdef versionCatalog = project.rootProject.extensions.getByType(VersionCatalogsExtension.class)\n\t\t\t\t.named(\"libs\")\n\t\tproject.plugins.withType(JavaPlugin) {\n\t\t\tdef checkstyleDir = project.rootProject.file(CHECKSTYLE_DIR)\n\t\t\tif (checkstyleDir.exists() && checkstyleDir.directory) {\n\t\t\t\tproject.getPluginManager().apply('checkstyle')\n\t\t\t\tproject.dependencies.add('checkstyle', versionCatalog.findLibrary('io-spring-javaformat-spring-javaformat-checkstyle').get())\n\t\t\t\tproject.dependencies.add('checkstyle', versionCatalog.findLibrary('io-spring-nohttp-nohttp-checkstyle').get())\n\n\t\t\t\tproject.checkstyle {\n\t\t\t\t\tconfigDirectory = checkstyleDir\n\t\t\t\t\ttoolVersion = '8.21'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy",
    "content": "package io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.Task\nimport org.gradle.api.plugins.BasePlugin\nimport org.gradle.api.plugins.PluginManager\nimport org.gradle.api.tasks.bundling.Zip\n\n/**\n * Aggregates asciidoc, javadoc, and deploying of the docs into a single plugin\n */\npublic class DocsPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\n\t\tPluginManager pluginManager = project.getPluginManager();\n\t\tpluginManager.apply(BasePlugin);\n\t\tpluginManager.apply(JavadocApiPlugin);\n\n\t\tTask docsZip = project.tasks.create('docsZip', Zip) {\n\t\t\tdependsOn 'api'\n\t\t\tgroup = 'Distribution'\n\t\t\tarchiveBaseName = project.rootProject.name\n\t\t\tarchiveClassifier = 'docs'\n\t\t\tdescription = \"Builds -${archiveClassifier.get()} archive containing all \" +\n\t\t\t\t\"Docs for deployment at docs.spring.io\"\n\n\t\t\tfrom(project.tasks.api.outputs) {\n\t\t\t\tinto 'api'\n\t\t\t}\n\t\t\tinto 'docs'\n\t\t\tduplicatesStrategy = 'exclude'\n\t\t}\n\n\t\tTask docs = project.tasks.create(\"docs\") {\n\t\t\tgroup = 'Documentation'\n\t\t\tdescription = 'An aggregator task to generate all the documentation'\n\t\t\tdependsOn docsZip\n\t\t}\n\t\tproject.tasks.assemble.dependsOn docs\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/EclipsePlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.plugins.ide.eclipse.EclipsePlugin\n\n/**\n * Plugin to tweak .classpath settings so that eclipse/sts/vscode can\n * be imported cleanly.\n *\n * @author Janne Valkealahti\n */\nclass EclipsePlugin implements Plugin<Project> {\n\n\t@Override\n\tvoid apply(Project project) {\n\t\tproject.plugins.apply(EclipsePlugin)\n\n\t\tproject.eclipse {\n\t\t\tclasspath {\n\t\t\t\tfile {\n\t\t\t\t\twhenMerged {\n\t\t\t\t\t\t// for aspects gradle eclipse integration wrongly creates lib entries for\n\t\t\t\t\t\t// aspects/build/classes/java/main and aspects/build/resources/main which\n\t\t\t\t\t\t// never has anything. eclipse/sts don't care, vscode language server\n\t\t\t\t\t\t// complains about missing folders. So remove those from a .classpath\n\t\t\t\t\t\tentries.removeAll {\n\t\t\t\t\t\t\tentry -> entry.kind == 'lib' && (entry.path.contains('aspects/build/classes/java/main') || entry.path.contains('aspects/build/resources/main'))\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// there are cycles and then dependencies between projects main sources and\n\t\t\t\t\t\t// test sources. Looks like gradle eclipse integration is getting confused.\n\t\t\t\t\t\t// thus just relax rules so that projects always sees everything from\n\t\t\t\t\t\t// dependant project.\n\t\t\t\t\t\tentries\n\t\t\t\t\t\t\t.findAll { entry -> entry instanceof org.gradle.plugins.ide.eclipse.model.ProjectDependency }\n\t\t\t\t\t\t\t.each {\n\t\t\t\t\t\t\t\tit.entryAttributes['without_test_code'] = 'false'\n\t\t\t\t\t\t\t\tit.entryAttributes['test'] = 'false'\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tjdt {\n\t\t\t\tfile {\n\t\t\t\t\twithProperties { properties ->\n\t\t\t\t\t\t// there are cycles and then dependencies between projects main sources and\n\t\t\t\t\t\t// test sources. Relax those from error to warning\n\t\t\t\t\t\tproperties['org.eclipse.jdt.core.circularClasspath'] = 'warning'\n\t\t\t\t\t\tproperties['org.eclipse.jdt.core.incompleteClasspath'] = 'warning'\n\t\t\t\t\t\tproperties['org.eclipse.jdt.core.compiler.codegen.methodParameters'] = 'generate'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\npackage io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.Task\nimport org.gradle.api.plugins.GroovyPlugin\nimport org.gradle.api.plugins.JavaPlugin\nimport org.gradle.api.tasks.testing.Test\nimport org.gradle.plugins.ide.eclipse.EclipsePlugin\nimport org.gradle.plugins.ide.idea.IdeaPlugin\nimport org.springframework.gradle.propdeps.PropDepsPlugin\n\n/**\n *\n * Adds support for integration tests to java projects.\n *\n * <ul>\n * <li>Adds integrationTestCompile and integrationTestRuntime configurations</li>\n * <li>A new source test folder of src/integration-test/java has been added</li>\n * <li>A task to run integration tests named integrationTest is added</li>\n * <li>If Groovy plugin is added a new source test folder src/integration-test/groovy is added</li>\n * </ul>\n *\n * @author Rob Winch\n */\npublic class IntegrationTestPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.plugins.withType(JavaPlugin.class) {\n\t\t\tapplyJava(project)\n\t\t}\n\t}\n\n\tprivate applyJava(Project project) {\n\t\tif(!project.file('src/integration-test/').exists()) {\n\t\t\t// ensure we don't add if no tests to avoid adding Gretty\n\t\t\treturn\n\t\t}\n\t\tproject.sourceSets {\n\t\t\tintegrationTest {\n\t\t\t\tjava.srcDir project.file('src/integration-test/java')\n\t\t\t\tresources.srcDir project.file('src/integration-test/resources')\n\t\t\t\tcompileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompileClasspath\n\t\t\t\truntimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntimeClasspath\n\t\t\t}\n\t\t}\n\n\t\tproject.configurations {\n\t\t\tintegrationTestCompile {\n\t\t\t\textendsFrom testImplementation\n\t\t\t}\n\t\t\tintegrationTestRuntime {\n\t\t\t\textendsFrom integrationTestCompile, testRuntime, testRuntimeOnly\n\t\t\t}\n\t\t\tintegrationTestCompileClasspath {\n\t\t\t\textendsFrom integrationTestCompile\n\t\t\t}\n\t\t\tintegrationTestRuntimeClasspath {\n\t\t\t\textendsFrom integrationTestRuntime\n\t\t\t}\n\t\t}\n\n\t\tTask integrationTestTask = project.tasks.create(\"integrationTest\", Test) {\n\t\t\tgroup = 'Verification'\n\t\t\tdescription = 'Runs the integration tests.'\n\t\t\tdependsOn 'jar'\n\t\t\ttestClassesDirs = project.sourceSets.integrationTest.output.classesDirs\n\t\t\tclasspath = project.sourceSets.integrationTest.runtimeClasspath\n\t\t\tshouldRunAfter project.tasks.test\n\t\t\tuseJUnitPlatform()\n\t\t}\n\t\tproject.tasks.check.dependsOn integrationTestTask\n\n\t\tproject.plugins.withType(IdeaPlugin) {\n\t\t\tproject.idea {\n\t\t\t\tmodule {\n\t\t\t\t\ttestSources.from(project.file('src/integration-test/java'))\n\t\t\t\t\tscopes.TEST.plus += [ project.configurations.integrationTestCompileClasspath ]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tproject.plugins.withType(GroovyPlugin) {\n\t\t\tproject.sourceSets {\n\t\t\t\tintegrationTest {\n\t\t\t\t\tgroovy.srcDirs project.file('src/integration-test/groovy')\n\t\t\t\t}\n\t\t\t}\n\t\t\tproject.plugins.withType(IdeaPlugin) {\n\t\t\t\tproject.idea {\n\t\t\t\t\tmodule {\n\t\t\t\t\t\ttestSources.from(project.file('src/integration-test/groovy'))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tproject.plugins.withType(PropDepsPlugin) {\n\t\t\tproject.configurations {\n\t\t\t\tintegrationTestCompile {\n\t\t\t\t\textendsFrom optional, provided\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tproject.plugins.withType(EclipsePlugin) {\n\t\t\tproject.eclipse.classpath {\n\t\t\t\tplusConfigurations += [ project.configurations.integrationTestCompileClasspath ]\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/JacocoPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPlugin\n\n/**\n * Adds a version of jacoco to use and makes check depend on jacocoTestReport.\n *\n * @author Rob Winch\n */\nclass JacocoPlugin implements Plugin<Project> {\n\n\t@Override\n\tvoid apply(Project project) {\n\t\tproject.plugins.withType(JavaPlugin) {\n\t\t\tproject.getPluginManager().apply(\"jacoco\")\n\t\t\tproject.tasks.check.dependsOn project.tasks.jacocoTestReport\n\n\t\t\tproject.jacoco {\n\t\t\t\ttoolVersion = '0.8.14'\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocApiPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\nimport org.gradle.api.Action;\nimport org.gradle.api.JavaVersion\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.plugins.JavaPluginExtension;\nimport org.gradle.api.tasks.SourceSet;\nimport org.gradle.api.tasks.javadoc.Javadoc;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author Rob Winch\n */\npublic class JavadocApiPlugin implements Plugin<Project> {\n\tLogger logger = LoggerFactory.getLogger(getClass());\n\tSet<Pattern> excludes = Collections.singleton(Pattern.compile(\"test\"));\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tlogger.info(\"Applied\");\n\t\tProject rootProject = project.getRootProject();\n\n\n\t\t//Task docs = project.getTasks().findByPath(\"docs\") ?: project.getTasks().create(\"docs\");\n\t\tJavadoc api = project.getTasks().create(\"api\", Javadoc);\n\n\t\tapi.setGroup(\"Documentation\");\n\t\tapi.setDescription(\"Generates aggregated Javadoc API documentation.\");\n\t\tapi.doLast {\n\t\t\tif (JavaVersion.current().isJava11Compatible()) {\n\t\t\t\tproject.copy({ copy -> copy\n\t\t\t\t\t.from(api.destinationDir)\n\t\t\t\t\t.into(api.destinationDir)\n\t\t\t\t\t.include(\"element-list\")\n\t\t\t\t\t.rename(\"element-list\", \"package-list\")\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tSet<Project> subprojects = rootProject.getSubprojects();\n\t\tfor (Project subproject : subprojects) {\n\t\t\taddProject(api, subproject);\n\t\t}\n\n\t\tif (subprojects.isEmpty()) {\n\t\t\taddProject(api, project);\n\t\t}\n\n\t\tapi.setMaxMemory(\"1024m\");\n\t\tapi.setDestinationDir(project.layout.getBuildDirectory().dir(\"api\").get().getAsFile());\n\n\t\tproject.getPluginManager().apply(\"io.spring.convention.javadoc-options\");\n\t}\n\n\tpublic void setExcludes(String... excludes) {\n\t\tif(excludes == null) {\n\t\t\tthis.excludes = Collections.emptySet();\n\t\t}\n\t\tthis.excludes = new HashSet<Pattern>(excludes.length);\n\t\tfor(String exclude : excludes) {\n\t\t\tthis.excludes.add(Pattern.compile(exclude));\n\t\t}\n\t}\n\n\tprivate void addProject(final Javadoc api, final Project project) {\n\t\tfor(Pattern exclude : excludes) {\n\t\t\tif(exclude.matcher(project.getName()).matches()) {\n\t\t\t\tlogger.info(\"Skipping {} because it is excluded by {}\", project, exclude);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tlogger.info(\"Try add sources for {}\", project);\n\t\tproject.getPlugins().withType(SpringModulePlugin.class).all(new Action<SpringModulePlugin>() {\n\t\t\t@Override\n\t\t\tpublic void execute(SpringModulePlugin plugin) {\n\t\t\t\tlogger.info(\"Added sources for {}\", project);\n\n\t\t\t\tJavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class);\n\t\t\t\tSourceSet mainSourceSet = java.getSourceSets().getByName(\"main\");\n\n\t\t\t\tapi.setSource(api.getSource().plus(mainSourceSet.getAllJava()));\n\t\t\t\tproject.getTasks().withType(Javadoc.class).all(new Action<Javadoc>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void execute(Javadoc projectJavadoc) {\n\t\t\t\t\t\tapi.setClasspath(api.getClasspath().plus(projectJavadoc.getClasspath()));\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n}\n\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocOptionsPlugin.groovy",
    "content": "package io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.tasks.javadoc.Javadoc\n\npublic class JavadocOptionsPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getTasks().withType(Javadoc).all { t->\n\t\t\tt.options.addStringOption('Xdoclint:none', '-quiet')\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/ManagementConfigurationPlugin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.artifacts.ConfigurationContainer;\nimport org.gradle.api.plugins.JavaPlugin;\nimport org.gradle.api.plugins.JavaTestFixturesPlugin;\nimport org.gradle.api.plugins.PluginContainer;\nimport org.gradle.api.publish.PublishingExtension;\nimport org.gradle.api.publish.maven.MavenPublication;\nimport org.gradle.api.publish.maven.plugins.MavenPublishPlugin;\n\nimport org.springframework.gradle.propdeps.PropDepsPlugin;\n\n/**\n * Creates a Management configuration that is appropriate for adding a platform to that is not exposed externally. If\n * the JavaPlugin is applied, the compileClasspath, runtimeClasspath, testCompileClasspath, and testRuntimeClasspath\n * will extend from it.\n * @author Rob Winch\n */\npublic class ManagementConfigurationPlugin implements Plugin<Project> {\n\n\tpublic static final String MANAGEMENT_CONFIGURATION_NAME = \"management\";\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tConfigurationContainer configurations = project.getConfigurations();\n\t\tconfigurations.create(MANAGEMENT_CONFIGURATION_NAME, (management) -> {\n\t\t\tmanagement.setVisible(false);\n\t\t\tmanagement.setCanBeConsumed(false);\n\t\t\tmanagement.setCanBeResolved(false);\n\n\t\t\tPluginContainer plugins = project.getPlugins();\n\t\t\tplugins.withType(JavaPlugin.class, (javaPlugin) -> {\n\t\t\t\tconfigurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management);\n\t\t\t\tconfigurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management);\n\t\t\t\tconfigurations.getByName(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management);\n\t\t\t\tconfigurations.getByName(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management);\n\t\t\t});\n\t\t\tplugins.withType(JavaTestFixturesPlugin.class, (javaTestFixturesPlugin) -> {\n\t\t\t\tconfigurations.getByName(\"testFixturesCompileClasspath\").extendsFrom(management);\n\t\t\t\tconfigurations.getByName(\"testFixturesRuntimeClasspath\").extendsFrom(management);\n\t\t\t});\n\t\t\tplugins.withType(MavenPublishPlugin.class, (mavenPublish) -> {\n\t\t\t\tPublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);\n\t\t\t\tpublishing.getPublications().withType(MavenPublication.class, (mavenPublication -> {\n\t\t\t\t\tmavenPublication.versionMapping((versions) ->\n\t\t\t\t\t\t\tversions.allVariants((versionMapping) -> versionMapping.fromResolutionResult())\n\t\t\t\t\t);\n\t\t\t\t}));\n\t\t\t});\n\t\t\tplugins.withType(PropDepsPlugin.class, (propDepsPlugin -> {\n\t\t\t\tconfigurations.getByName(\"optional\").extendsFrom(management);\n\t\t\t\tconfigurations.getByName(\"provided\").extendsFrom(management);\n\t\t\t}));\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomPlugin.groovy",
    "content": "package io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPlatformPlugin\nimport org.sonarqube.gradle.SonarQubePlugin\nimport org.springframework.gradle.CopyPropertiesPlugin\nimport org.springframework.gradle.maven.SpringMavenPlugin\n\npublic class MavenBomPlugin implements Plugin<Project> {\n\tstatic String MAVEN_BOM_TASK_NAME = \"mavenBom\"\n\n\tpublic void apply(Project project) {\n\t\tproject.plugins.apply(JavaPlatformPlugin)\n\t\tproject.plugins.apply(SpringMavenPlugin)\n\t\tproject.plugins.apply(CopyPropertiesPlugin)\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\nclass RepositoryConventionPlugin implements Plugin<Project> {\n\n\t@Override\n\tvoid apply(Project project) {\n\t\tString[] forceMavenRepositories = ((String) project.findProperty(\"forceMavenRepositories\"))?.split(',')\n\t\tboolean isImplicitSnapshotRepository = forceMavenRepositories == null && Utils.isSnapshot(project)\n\t\tboolean isImplicitMilestoneRepository = forceMavenRepositories == null && Utils.isMilestone(project)\n\n\t\tboolean isSnapshot = isImplicitSnapshotRepository || forceMavenRepositories?.contains('snapshot')\n\t\tboolean isMilestone = isImplicitMilestoneRepository || forceMavenRepositories?.contains('milestone')\n\n\t\tproject.repositories {\n\t\t\tif (forceMavenRepositories?.contains('local')) {\n\t\t\t\tmavenLocal()\n\t\t\t}\n\t\t\tmaven {\n\t\t\t\tname = 'shibboleth'\n\t\t\t\turl = 'https://build.shibboleth.net/nexus/content/repositories/releases/'\n\t\t\t\tcontent {\n\t\t\t\t\tincludeGroupByRegex('org\\\\.opensaml.*')\n\t\t\t\t\tincludeGroupByRegex('net\\\\.shibboleth.*')\n\t\t\t\t}\n\t\t\t}\n\t\t\tmavenCentral()\n\t\t\tif (isSnapshot) {\n\t\t\t\tmaven {\n\t\t\t\t\tname = 'artifactory-snapshot'\n\t\t\t\t\tif (project.hasProperty('artifactoryUsername')) {\n\t\t\t\t\t\tcredentials {\n\t\t\t\t\t\t\tusername project.artifactoryUsername\n\t\t\t\t\t\t\tpassword project.artifactoryPassword\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\turl = 'https://repo.spring.io/snapshot/'\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isSnapshot || isMilestone) {\n\t\t\t\tmaven {\n\t\t\t\t\tname = 'artifactory-milestone'\n\t\t\t\t\tif (project.hasProperty('artifactoryUsername')) {\n\t\t\t\t\t\tcredentials {\n\t\t\t\t\t\t\tusername project.artifactoryUsername\n\t\t\t\t\t\t\tpassword project.artifactoryPassword\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\turl = 'https://repo.spring.io/milestone/'\n\t\t\t\t}\n\t\t\t}\n\t\t\tmaven {\n\t\t\t\tname = 'artifactory-release'\n\t\t\t\tif (project.hasProperty('artifactoryUsername')) {\n\t\t\t\t\tcredentials {\n\t\t\t\t\t\tusername project.artifactoryUsername\n\t\t\t\t\t\tpassword project.artifactoryPassword\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontent {\n\t\t\t\t\texcludeGroup('net.minidev')\n\t\t\t\t}\n\t\t\t\turl = 'https://repo.spring.io/release/'\n\t\t\t}\n\t\t\tforceMavenRepositories.findAll { it.startsWith('https://') || it.startsWith('file://') }.each { mavenUrl ->\n\t\t\t\tmaven {\n\t\t\t\t\turl mavenUrl\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention\n\nimport io.spring.nohttp.gradle.NoHttpPlugin\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.BasePlugin\nimport org.gradle.api.plugins.PluginManager\nimport org.springframework.gradle.classpath.CheckProhibitedDependenciesLifecyclePlugin\nimport org.springframework.gradle.maven.SpringNexusPublishPlugin\n\nclass RootProjectPlugin implements Plugin<Project> {\n\n\t@Override\n\tvoid apply(Project project) {\n\t\tPluginManager pluginManager = project.getPluginManager()\n\t\tpluginManager.apply(BasePlugin)\n\t\tpluginManager.apply(SchemaPlugin)\n\t\tpluginManager.apply(NoHttpPlugin)\n\t\tpluginManager.apply(SpringNexusPublishPlugin)\n\t\tpluginManager.apply(CheckProhibitedDependenciesLifecyclePlugin)\n\t\tpluginManager.apply(ArtifactoryPlugin)\n\t\tpluginManager.apply(\"org.sonarqube\")\n\n\t\tproject.repositories.mavenCentral()\n\n\t\tString projectName = Utils.getProjectName(project)\n\t\tproject.sonarqube {\n\t\t\tproperties {\n\t\t\t\tproperty \"sonar.java.coveragePlugin\", \"jacoco\"\n\t\t\t\tproperty \"sonar.projectName\", projectName\n\t\t\t\tproperty \"sonar.jacoco.reportPath\", \"${project.buildDir.name}/jacoco.exec\"\n\t\t\t\tproperty \"sonar.links.homepage\", \"https://spring.io/${projectName}\"\n\t\t\t\tproperty \"sonar.links.ci\", \"https://jenkins.spring.io/job/${projectName}/\"\n\t\t\t\tproperty \"sonar.links.issue\", \"https://github.com/spring-projects/${projectName}/issues\"\n\t\t\t\tproperty \"sonar.links.scm\", \"https://github.com/spring-projects/${projectName}\"\n\t\t\t\tproperty \"sonar.links.scm_dev\", \"https://github.com/spring-projects/${projectName}.git\"\n\t\t\t}\n\t\t}\n\n\t\tdef finalizeDeployArtifacts = project.task(\"finalizeDeployArtifacts\")\n\t\tif (Utils.isRelease(project) && project.hasProperty(\"ossrhUsername\")) {\n\t\t\tfinalizeDeployArtifacts.dependsOn project.tasks.closeAndReleaseOssrhStagingRepository\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaDeployPlugin.groovy",
    "content": "package io.spring.gradle.convention\n\nimport org.gradle.api.plugins.JavaPlugin\nimport org.gradle.api.tasks.bundling.Zip\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\npublic class SchemaDeployPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getPluginManager().apply('org.hidetake.ssh')\n\n\t\tproject.ssh.settings {\n\t\t\tknownHosts = allowAnyHosts\n\t\t}\n\t\tproject.remotes {\n\t\t\tdocs {\n\t\t\t\trole 'docs'\n\t\t\t\tif (project.hasProperty('deployDocsHost')) {\n\t\t\t\t\thost = project.findProperty('deployDocsHost')\n\t\t\t\t} else {\n\t\t\t\t\thost = 'docs.af.pivotal.io'\n\t\t\t\t}\n\t\t\t\tretryCount = 5 // retry 5 times (default is 0)\n\t\t\t\tretryWaitSec = 10 // wait 10 seconds between retries (default is 0)\n\t\t\t\tuser = project.findProperty('deployDocsSshUsername')\n\t\t\t\tif(project.hasProperty('deployDocsSshKeyPath')) {\n\t\t\t\t\tidentity = project.file(project.findProperty('deployDocsSshKeyPath'))\n\t\t\t\t} else if (project.hasProperty('deployDocsSshKey')) {\n\t\t\t\t\tidentity = project.findProperty('deployDocsSshKey')\n\t\t\t\t}\n\t\t\t\tif(project.hasProperty('deployDocsSshPassphrase')) {\n\t\t\t\t\tpassphrase = project.findProperty('deployDocsSshPassphrase')\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tproject.task('deploySchema') {\n\t\t\tdependsOn 'schemaZip'\n\t\t\tdoFirst {\n\t\t\t\tproject.ssh.run {\n\t\t\t\t\tsession(project.remotes.docs) {\n\t\t\t\t\t\tdef now = System.currentTimeMillis()\n\t\t\t\t\t\tdef name = project.rootProject.name\n\t\t\t\t\t\tdef version = project.rootProject.version\n\t\t\t\t\t\tdef tempPath = \"/tmp/${name}-${now}-schema/\".replaceAll(' ', '_')\n\n\t\t\t\t\t\texecute \"mkdir -p $tempPath\"\n\n\t\t\t\t\t\tproject.tasks.schemaZip.outputs.each { o ->\n\t\t\t\t\t\t\tprintln \"Putting $o.files\"\n\t\t\t\t\t\t\tput from: o.files, into: tempPath\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\texecute \"unzip $tempPath*.zip -d $tempPath\"\n\n\t\t\t\t\t\tdef extractPath = \"/var/www/domains/spring.io/docs/htdocs/autorepo/schema/${name}/${version}/\"\n\n\t\t\t\t\t\texecute \"rm -rf $extractPath\"\n\t\t\t\t\t\texecute \"mkdir -p $extractPath\"\n\t\t\t\t\t\texecute \"rm -f $tempPath*.zip\"\n\t\t\t\t\t\texecute \"rm -rf $extractPath*\"\n\t\t\t\t\t\texecute \"mv $tempPath/* $extractPath\"\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Copy spring-security-oauth schemas so that legacy projects using the \"http\" scheme can\n\t\t\t\t\t\t * resolve the following schema locations:\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * - http://www.springframework.org/schema/security/spring-security-oauth-1.0.xsd\n\t\t\t\t\t\t * - http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd\n\t\t\t\t\t\t * - http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * Note: The directory:\n\t\t\t\t\t\t *   https://www.springframework.org/schema/security/\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * is a symbolic link pointing to:\n\t\t\t\t\t\t *   https://docs.spring.io/autorepo/schema/spring-security/current/security/\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * which in turn points to the latest:\n\t\t\t\t\t\t *   https://docs.spring.io/autorepo/schema/spring-security/$version/security/\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * with the help of the autoln command which is run regularly via a cron job.\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * See https://github.com/spring-io/autoln for more info.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (name == \"spring-security\") {\n\t\t\t\t\t\t\tdef springSecurityOauthPath = \"/var/www/domains/spring.io/docs/htdocs/autorepo/schema/spring-security-oauth/current/security\"\n\t\t\t\t\t\t\texecute \"cp $springSecurityOauthPath/* ${extractPath}security\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\texecute \"chmod -R g+w $extractPath\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaPlugin.groovy",
    "content": "package io.spring.gradle.convention\n\nimport org.gradle.api.plugins.JavaPlugin\nimport org.gradle.api.tasks.bundling.Zip\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\npublic class SchemaPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getPluginManager().apply(SchemaZipPlugin)\n\t\tproject.getPluginManager().apply(SchemaDeployPlugin)\n\t}\n}"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaZipPlugin.groovy",
    "content": "package io.spring.gradle.convention\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPlugin\nimport org.gradle.api.tasks.bundling.Zip\nimport org.springframework.gradle.xsd.CreateVersionlessXsdTask\n\npublic class SchemaZipPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tZip schemaZip = project.tasks.create('schemaZip', Zip)\n\t\tschemaZip.group = 'Distribution'\n\t\tschemaZip.archiveBaseName = project.rootProject.name\n\t\tschemaZip.archiveClassifier = 'schema'\n\t\tschemaZip.description = \"Builds -${schemaZip.archiveClassifier} archive containing all \" +\n\t\t\t\"XSDs for deployment at static.springframework.org/schema.\"\n\t\tdef versionlessXsd = project.tasks.create(\"versionlessXsd\", CreateVersionlessXsdTask) {\n\t\t\tdescription = \"Generates spring-security.xsd\"\n\t\t\tversionlessXsdFile = project.layout.buildDirectory.file(\"versionlessXsd/spring-security.xsd\")\n\t\t}\n\t\tproject.rootProject.subprojects.each { module ->\n\n\t\t\tmodule.getPlugins().withType(JavaPlugin.class).all {\n\t\t\t\tdef Properties schemas = new Properties();\n\n\t\t\t\tmodule.sourceSets.main.resources.find {\n\t\t\t\t\tit.path.endsWith('META-INF/spring.schemas')\n\t\t\t\t}?.withInputStream { schemas.load(it) }\n\n\t\t\t\tfor (def key : schemas.keySet()) {\n\t\t\t\t\tdef shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')\n\t\t\t\t\tassert shortName != key\n\t\t\t\t\tdef schemaResourceName = schemas.get(key)\n\t\t\t\t\tFile xsdFile = module.sourceSets.main.resources.find {\n\t\t\t\t\t\tit.path.endsWith(schemaResourceName)\n\t\t\t\t\t}\n\t\t\t\t\tif (xsdFile == null) {\n\t\t\t\t\t\tthrow new IllegalStateException(\"Could not find schema file for resource name \" + schemaResourceName + \" in src/main/resources\")\n\t\t\t\t\t}\n\t\t\t\t\tschemaZip.into (shortName) {\n\t\t\t\t\t\tduplicatesStrategy = 'exclude'\n\t\t\t\t\t\tfrom xsdFile.path\n\t\t\t\t\t}\n\t\t\t\t\tversionlessXsd.getInputFiles().from(xsdFile.path)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n        schemaZip.into(\"security\") {\n            from(versionlessXsd.getOutputs())\n        }\n\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/SortedProperties.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.spring.gradle.convention;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Properties;\n\n/**\n * A Properties which sorts they keys so that they can be written to a File with\n * the keys sorted.\n *\n * @author Rob Winch\n *\n */\nclass SortedProperties extends Properties {\n\tprivate static final long serialVersionUID = -6199017589626540836L;\n\n\tpublic Enumeration<Object> keys() {\n\t\tEnumeration<Object> keysEnum = super.keys();\n\t\tList<Object> keyList = new ArrayList<Object>();\n\n\t\twhile (keysEnum.hasMoreElements()) {\n\t\t\tkeyList.add(keysEnum.nextElement());\n\t\t}\n\n\t\tCollections.sort(keyList, new Comparator<Object>() {\n\t\t\t@Override\n\t\t\tpublic int compare(Object o1, Object o2) {\n\t\t\t\treturn o1.toString().compareTo(o2.toString());\n\t\t\t}\n\t\t});\n\n\t\treturn Collections.enumeration(keyList);\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaLibraryPlugin;\nimport org.gradle.api.plugins.PluginManager\nimport org.gradle.api.tasks.bundling.Jar\nimport org.springframework.gradle.classpath.CheckClasspathForProhibitedDependenciesPlugin;\nimport org.springframework.gradle.maven.SpringMavenPlugin;\n\n/**\n * @author Rob Winch\n */\nclass SpringModulePlugin extends AbstractSpringJavaPlugin {\n\n\t@Override\n\tvoid additionalPlugins(Project project) {\n\t\tPluginManager pluginManager = project.getPluginManager();\n\t\tpluginManager.apply(JavaLibraryPlugin.class)\n\t\tpluginManager.apply(SpringMavenPlugin.class);\n\t\tpluginManager.apply(CheckClasspathForProhibitedDependenciesPlugin.class);\n\t\tpluginManager.apply(\"io.spring.convention.jacoco\");\n\t\tpluginManager.apply(\"java-toolchain\");\n\n\t\tdef deployArtifacts = project.task(\"deployArtifacts\")\n\t\tdeployArtifacts.group = 'Deploy tasks'\n\t\tdeployArtifacts.description = \"Deploys the artifacts to either Artifactory or Maven Central\"\n\t\tif (!Utils.isRelease(project)) {\n\t\t\tdeployArtifacts.dependsOn project.tasks.artifactoryPublish\n\t\t}\n\t\tproject.tasks.withType(Jar) {\n\t\t\tfrom(project.rootProject.files('LICENSE.txt')) {\n\t\t\t\tinto('META-INF')\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport org.gradle.api.Project;\n\n/**\n * @author Rob Winch\n */\npublic class SpringTestPlugin extends AbstractSpringJavaPlugin {\n\n\t@Override\n\tpublic void additionalPlugins(Project project) {\n\t\tproject.sonarqube.skipProject = true\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\npackage io.spring.gradle.convention;\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPlugin\nimport org.gradle.jvm.tasks.Jar\n\n/**\n * Adds the ability to depends on the test jar within other projects using:\n *\n * <code>\n * testCompile project(path: ':foo', configuration: 'tests')\n * </code>\n *\n * @author Rob Winch\n */\npublic class TestsConfigurationPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.plugins.withType(JavaPlugin) {\n\t\t\tapplyJavaProject(project)\n\t\t}\n\t}\n\n\tprivate void applyJavaProject(Project project) {\n\t\tproject.configurations {\n\t\t\ttests.extendsFrom testRuntime, testRuntimeClasspath\n\t\t}\n\n\t\tproject.tasks.create('testJar', Jar) {\n\t\t\tarchiveClassifier = 'test'\n\t\t\tfrom project.sourceSets.test.output\n\t\t}\n\n\t\tproject.artifacts {\n\t\t\ttests project.testJar\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy",
    "content": "package io.spring.gradle.convention;\n\nimport org.gradle.api.Project;\n\npublic class Utils {\n\n\tstatic String getProjectName(Project project) {\n\t\tString projectName = project.getRootProject().getName();\n\t\tif(projectName.endsWith(\"-build\")) {\n\t\t\tprojectName = projectName.substring(0, projectName.length() - \"-build\".length());\n\t\t}\n\t\treturn projectName;\n\t}\n\n\tstatic boolean isSnapshot(Project project) {\n\t\tString projectVersion = projectVersion(project)\n\t\treturn projectVersion.matches('^.*([.-]BUILD)?-SNAPSHOT$')\n\t}\n\n\tstatic boolean isMilestone(Project project) {\n\t\tString projectVersion = projectVersion(project)\n\t\treturn projectVersion.matches('^.*[.-]M\\\\d+$') || projectVersion.matches('^.*[.-]RC\\\\d+$')\n\t}\n\n\tstatic boolean isRelease(Project project) {\n\t\treturn !(isSnapshot(project) || isMilestone(project))\n\t}\n\n\tprivate static String projectVersion(Project project) {\n\t\treturn String.valueOf(project.getVersion());\n\t}\n\n\tprivate Utils() {}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/java-toolchain.gradle",
    "content": "import org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\ndef toolchainVersion() {\n\tif (project.hasProperty('testToolchain')) {\n\t\treturn project.property('testToolchain').toString().toInteger()\n\t}\n\treturn 25\n}\n\njava {\n\ttoolchain {\n\t\tlanguageVersion = JavaLanguageVersion.of(toolchainVersion())\n\t}\n}\n\ntasks.withType(JavaCompile).configureEach {\n\toptions.encoding = \"UTF-8\"\n\toptions.compilerArgs.add(\"-parameters\")\n\toptions.release = 17\n}\n\npluginManager.withPlugin(\"org.jetbrains.kotlin.jvm\") {\n\tkotlin {\n\t\tjvmToolchain {\n\t\t\tlanguageVersion = JavaLanguageVersion.of(toolchainVersion())\n\t\t}\n\t}\n\n\ttasks.withType(KotlinCompile).configureEach {\n\t\tcompilerOptions {\n\t\t\tjavaParameters = true\n\t\t\tjvmTarget.set(JvmTarget.JVM_17)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/javadoc-warnings-error.gradle",
    "content": "import org.gradle.api.tasks.javadoc.Javadoc\n\nproject.tasks.withType(Javadoc).configureEach {\n\toptions.addBooleanOption('Werror', true)\n\t// temporarily disable missing to get build to pass with JDK 25\n\toptions.addStringOption('Xdoclint:all,-missing')\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/security-kotlin.gradle",
    "content": "import org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nplugins {\n    id 'kotlin'\n}\n\nproject.plugins.withId(\"org.jetbrains.kotlin.jvm\", (kotlinProject) -> {\n    project.tasks.withType(KotlinCompile).configureEach {\n        kotlinOptions {\n            languageVersion = '2.2'\n            apiVersion = '2.2'\n            freeCompilerArgs = [\"-Xjsr305=strict\", \"-Xsuppress-version-warnings\"]\n            jvmTarget = '17'\n\n        }\n    }\n})\n"
  },
  {
    "path": "buildSrc/src/main/groovy/security-nullability.gradle",
    "content": "plugins {\n    id 'io.spring.nullability'\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/test-compile-target-jdk25.gradle",
    "content": "import org.gradle.api.tasks.compile.JavaCompile\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\n/**\n * We need to compile with JDK 25 for nullability support, but using JDK 25 means that our tests will fail due to the\n * <a href=\"https://docs.oracle.com/en/java/javase/25/security/security-manager-is-permanently-disabled.html\">removal\n * of the Java Security Manager</a>. For example, in JDK 25 {@code Subject.getSubject(AccessControlContext)} throws an\n * {@code UnsupportedOperationException}.\n *\n * To resolve this, we must migrate tests to use the new APIs (e.g. {@code Subject.current()}) but those APIs are not\n * available in the JDK 17 source, so compiling with JDK 25 and release 17 fails. The plugin overrides the test\n * compilation to use release 25.\n *\n * @see <a href=\"https://docs.oracle.com/en/java/javase/25/security/security-manager-is-permanently-disabled.html\">The\n * Security Manager Is Permanently Disabled</a>\n * @see <a href=\"https://inside.java/2024/07/08/quality-heads-up/\">Quality Outreach Heads-up - JDK 23: Re-Specified\n * Subject.getSubject API</a>\n */\n\ntasks.withType(JavaCompile).configureEach { task ->\n\tif (task.name == 'compileTestJava' || task.name == 'compileIntegrationTestJava') {\n\t\ttask.options.release.set(25)\n\t}\n}\n\ntasks.withType(KotlinCompile).configureEach { task ->\n\tif (task.name == 'compileTestKotlin' || task.name == 'compileIntegrationTestKotlin') {\n\t\ttask.kotlinOptions.jvmTarget = '25'\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/lock/GlobalLockPlugin.java",
    "content": "package lock;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\n\n/**\n * @author Rob Winch\n */\npublic class GlobalLockPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getTasks().register(\"writeLocks\", GlobalLockTask.class, (writeAll) -> {\n\t\t\twriteAll.setDescription(\"Writes the locks for all projects\");\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/lock/GlobalLockTask.java",
    "content": "package lock;\n\nimport org.gradle.api.Action;\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.Project;\nimport org.gradle.api.artifacts.Configuration;\nimport org.gradle.api.tasks.TaskAction;\n\nimport java.util.function.Consumer;\n\n/**\n * @author Rob Winch\n */\npublic class GlobalLockTask extends DefaultTask {\n\t@TaskAction\n\tpublic void lock() {\n\t\tProject taskProject = getProject();\n\t\tif (!taskProject.getGradle().getStartParameter().isWriteDependencyLocks()) {\n\t\t\tthrow new IllegalStateException(\"You just specify --write-locks argument\");\n\t\t}\n\t\twriteLocksFor(taskProject);\n\t\ttaskProject.getSubprojects().forEach(new Consumer<Project>() {\n\t\t\t@Override\n\t\t\tpublic void accept(Project subproject) {\n\t\t\t\twriteLocksFor(subproject);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void writeLocksFor(Project project) {\n\t\tproject.getConfigurations().configureEach(new Action<Configuration>() {\n\t\t\t@Override\n\t\t\tpublic void execute(Configuration configuration) {\n\t\t\t\tif (configuration.isCanBeResolved()) {\n\t\t\t\t\tconfiguration.resolve();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/CopyPropertiesPlugin.java",
    "content": "package org.springframework.gradle;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\n\npublic class CopyPropertiesPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tcopyPropertyFromRootProjectTo(\"group\", project);\n\t\tcopyPropertyFromRootProjectTo(\"version\", project);\n\t\tcopyPropertyFromRootProjectTo(\"description\", project);\n\t}\n\n\n\tprivate void copyPropertyFromRootProjectTo(String propertyName, Project project) {\n\t\tProject rootProject = project.getRootProject();\n\t\tObject property = rootProject.findProperty(propertyName);\n\t\tif(property != null) {\n\t\t\tproject.setProperty(propertyName, property);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/classpath/CheckClasspathForProhibitedDependencies.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.gradle.classpath;\n\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.GradleException;\nimport org.gradle.api.Task;\nimport org.gradle.api.artifacts.Configuration;\nimport org.gradle.api.artifacts.ModuleVersionIdentifier;\nimport org.gradle.api.artifacts.ResolvedConfiguration;\nimport org.gradle.api.file.FileCollection;\nimport org.gradle.api.tasks.Classpath;\nimport org.gradle.api.tasks.TaskAction;\n\nimport java.io.IOException;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\n/**\n * A {@link Task} for checking the classpath for prohibited dependencies.\n *\n * @author Andy Wilkinson\n */\npublic class CheckClasspathForProhibitedDependencies extends DefaultTask {\n\n\tprivate Configuration classpath;\n\n\tpublic CheckClasspathForProhibitedDependencies() {\n\t\tgetOutputs().upToDateWhen((task) -> true);\n\t}\n\n\tpublic void setClasspath(Configuration classpath) {\n\t\tthis.classpath = classpath;\n\t}\n\n\t@Classpath\n\tpublic FileCollection getClasspath() {\n\t\treturn this.classpath;\n\t}\n\n\t@TaskAction\n\tpublic void checkForProhibitedDependencies() throws IOException {\n\t\tResolvedConfiguration resolvedConfiguration = this.classpath.getResolvedConfiguration();\n\t\tTreeSet<String> prohibited = resolvedConfiguration.getResolvedArtifacts().stream()\n\t\t\t\t.map((artifact) -> artifact.getModuleVersion().getId()).filter(this::prohibited)\n\t\t\t\t.map((id) -> id.getGroup() + \":\" + id.getName()).collect(Collectors.toCollection(TreeSet::new));\n\t\tif (!prohibited.isEmpty()) {\n\t\t\tStringBuilder message = new StringBuilder(String.format(\"Found prohibited dependencies in '%s':%n\", this.classpath.getName()));\n\t\t\tfor (String dependency : prohibited) {\n\t\t\t\tmessage.append(String.format(\"    %s%n\", dependency));\n\t\t\t}\n\t\t\tthrow new GradleException(message.toString());\n\t\t}\n\t}\n\n\tprivate boolean prohibited(ModuleVersionIdentifier id) {\n\t\tString group = id.getGroup();\n\t\tif (group.equals(\"javax.batch\")) {\n\t\t\treturn false;\n\t\t}\n\t\tif (group.equals(\"javax.cache\")) {\n\t\t\treturn false;\n\t\t}\n\t\tif (group.equals(\"javax.money\")) {\n\t\t\treturn false;\n\t\t}\n\t\tif (group.startsWith(\"javax\")) {\n\t\t\treturn true;\n\t\t}\n\t\tif (group.equals(\"org.slf4j\") && id.getName().equals(\"jcl-over-slf4j\")) {\n\t\t\treturn true;\n\t\t}\n\t\tif (group.startsWith(\"org.jboss.spec\")) {\n\t\t\treturn true;\n\t\t}\n\t\tif (group.equals(\"org.apache.geronimo.specs\")) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/classpath/CheckClasspathForProhibitedDependenciesPlugin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.gradle.classpath;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.artifacts.Configuration;\nimport org.gradle.api.artifacts.ConfigurationContainer;\nimport org.gradle.api.plugins.JavaBasePlugin;\nimport org.gradle.api.tasks.SourceSetContainer;\nimport org.gradle.api.tasks.TaskProvider;\nimport org.gradle.language.base.plugins.LifecycleBasePlugin;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Andy Wilkinson\n * @author Rob Winch\n */\npublic class CheckClasspathForProhibitedDependenciesPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getPlugins().apply(CheckProhibitedDependenciesLifecyclePlugin.class);\n\t\tproject.getPlugins().withType(JavaBasePlugin.class, javaBasePlugin -> configureProhibitedDependencyChecks(project));\n\t}\n\n\tprivate void configureProhibitedDependencyChecks(Project project) {\n\t\tSourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);\n\t\tsourceSets.all((sourceSet) -> createProhibitedDependenciesChecks(project,\n\t\t\t\tsourceSet.getCompileClasspathConfigurationName(), sourceSet.getRuntimeClasspathConfigurationName()));\n\t}\n\n\tprivate void createProhibitedDependenciesChecks(Project project, String... configurationNames) {\n\t\tConfigurationContainer configurations = project.getConfigurations();\n\t\tfor (String configurationName : configurationNames) {\n\t\t\tConfiguration configuration = configurations.getByName(configurationName);\n\t\t\tcreateProhibitedDependenciesCheck(configuration, project);\n\t\t}\n\t}\n\n\tprivate void createProhibitedDependenciesCheck(Configuration classpath, Project project) {\n\t\tString taskName = \"check\" + StringUtils.capitalize(classpath.getName() + \"ForProhibitedDependencies\");\n\t\tTaskProvider<CheckClasspathForProhibitedDependencies> checkClasspathTask = project.getTasks().register(taskName,\n\t\t\t\tCheckClasspathForProhibitedDependencies.class, checkClasspath -> {\n\t\t\t\t\tcheckClasspath.setGroup(LifecycleBasePlugin.CHECK_TASK_NAME);\n\t\t\t\t\tcheckClasspath.setDescription(\"Checks \" + classpath.getName() + \" for prohibited dependencies\");\n\t\t\t\t\tcheckClasspath.setClasspath(classpath);\n\t\t\t\t});\n\t\tproject.getTasks().named(CheckProhibitedDependenciesLifecyclePlugin.CHECK_PROHIBITED_DEPENDENCIES_TASK_NAME, checkProhibitedTask -> checkProhibitedTask.dependsOn(checkClasspathTask));\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/classpath/CheckProhibitedDependenciesLifecyclePlugin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.gradle.classpath;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.Task;\nimport org.gradle.api.plugins.JavaBasePlugin;\nimport org.gradle.api.tasks.TaskProvider;\n\n/**\n * @author Rob Winch\n */\npublic class CheckProhibitedDependenciesLifecyclePlugin implements Plugin<Project> {\n\tpublic static final String CHECK_PROHIBITED_DEPENDENCIES_TASK_NAME = \"checkForProhibitedDependencies\";\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tTaskProvider<Task> checkProhibitedDependencies = project.getTasks().register(CheckProhibitedDependenciesLifecyclePlugin.CHECK_PROHIBITED_DEPENDENCIES_TASK_NAME, task -> {\n\t\t\ttask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);\n\t\t\ttask.setDescription(\"Checks both the compile/runtime classpath of every SourceSet for prohibited dependencies\");\n\t\t});\n\t\tproject.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(checkProhibitedDependencies));\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/maven/MavenPublishingConventionsPlugin.java",
    "content": "package org.springframework.gradle.maven;\n\nimport org.gradle.api.Action;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.plugins.JavaPlugin;\nimport org.gradle.api.plugins.JavaPluginExtension;\nimport org.gradle.api.publish.PublishingExtension;\nimport org.gradle.api.publish.maven.MavenPom;\nimport org.gradle.api.publish.maven.MavenPomDeveloperSpec;\nimport org.gradle.api.publish.maven.MavenPomIssueManagement;\nimport org.gradle.api.publish.maven.MavenPomLicenseSpec;\nimport org.gradle.api.publish.maven.MavenPomOrganization;\nimport org.gradle.api.publish.maven.MavenPomScm;\nimport org.gradle.api.publish.maven.MavenPublication;\nimport org.gradle.api.publish.maven.plugins.MavenPublishPlugin;\n\npublic class MavenPublishingConventionsPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getPlugins().withType(MavenPublishPlugin.class).all(new Action<MavenPublishPlugin>() {\n\t\t\t@Override\n\t\t\tpublic void execute(MavenPublishPlugin mavenPublish) {\n\t\t\t\tPublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);\n\t\t\t\tpublishing.getPublications().withType(MavenPublication.class)\n\t\t\t\t\t\t.all((mavenPublication) -> MavenPublishingConventionsPlugin.this.customizePom(mavenPublication.getPom(), project));\n\t\t\t\tMavenPublishingConventionsPlugin.this.customizeJavaPlugin(project);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void customizePom(MavenPom pom, Project project) {\n\t\tpom.getUrl().set(\"https://spring.io/projects/spring-security\");\n\t\tpom.getName().set(project.provider(project::getName));\n\t\tpom.getDescription().set(project.provider(project::getDescription));\n\t\tpom.organization(this::customizeOrganization);\n\t\tpom.licenses(this::customizeLicences);\n\t\tpom.developers(this::customizeDevelopers);\n\t\tpom.scm(this::customizeScm);\n\t\tpom.issueManagement(this::customizeIssueManagement);\n\t}\n\n\tprivate void customizeOrganization(MavenPomOrganization organization) {\n\t\torganization.getName().set(\"Pivotal Software, Inc.\");\n\t\torganization.getUrl().set(\"https://spring.io\");\n\t}\n\n\tprivate void customizeLicences(MavenPomLicenseSpec licences) {\n\t\tlicences.license((licence) -> {\n\t\t\tlicence.getName().set(\"Apache License, Version 2.0\");\n\t\t\tlicence.getUrl().set(\"https://www.apache.org/licenses/LICENSE-2.0\");\n\t\t});\n\t}\n\n\tprivate void customizeDevelopers(MavenPomDeveloperSpec developers) {\n\t\tdevelopers.developer((developer) -> {\n\t\t\tdeveloper.getName().set(\"Pivotal\");\n\t\t\tdeveloper.getEmail().set(\"info@pivotal.io\");\n\t\t\tdeveloper.getOrganization().set(\"Pivotal Software, Inc.\");\n\t\t\tdeveloper.getOrganizationUrl().set(\"https://www.spring.io\");\n\t\t});\n\t}\n\n\tprivate void customizeScm(MavenPomScm scm) {\n\t\tscm.getConnection().set(\"scm:git:git://github.com/spring-projects/spring-security.git\");\n\t\tscm.getDeveloperConnection().set(\"scm:git:ssh://git@github.com/spring-projects/spring-security.git\");\n\t\tscm.getUrl().set(\"https://github.com/spring-projects/spring-security\");\n\t}\n\n\tprivate void customizeIssueManagement(MavenPomIssueManagement issueManagement) {\n\t\tissueManagement.getSystem().set(\"GitHub\");\n\t\tissueManagement.getUrl().set(\"https://github.com/spring-projects/spring-security/issues\");\n\t}\n\n\tprivate void customizeJavaPlugin(Project project) {\n\t\tproject.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> {\n\t\t\tJavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class);\n\t\t\textension.withJavadocJar();\n\t\t\textension.withSourcesJar();\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/maven/PublishAllJavaComponentsPlugin.java",
    "content": "package org.springframework.gradle.maven;\n\n\nimport org.gradle.api.Action;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.plugins.JavaPlatformPlugin;\nimport org.gradle.api.plugins.JavaPlugin;\nimport org.gradle.api.publish.PublishingExtension;\nimport org.gradle.api.publish.maven.MavenPublication;\nimport org.gradle.api.publish.maven.plugins.MavenPublishPlugin;\n\npublic class PublishAllJavaComponentsPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getPlugins().withType(MavenPublishPlugin.class).all((mavenPublish) -> {\n\t\t\tPublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);\n\t\t\tpublishing.getPublications().create(\"mavenJava\", MavenPublication.class, new Action<MavenPublication>() {\n\t\t\t\t@Override\n\t\t\t\tpublic void execute(MavenPublication maven) {\n\t\t\t\t\tproject.getPlugins().withType(JavaPlugin.class, (plugin) -> {\n\t\t\t\t\t\tmaven.from(project.getComponents().getByName(\"java\"));\n\t\t\t\t\t});\n\t\t\t\t\tproject.getPlugins().withType(JavaPlatformPlugin.class, (plugin) -> {\n\t\t\t\t\t\tmaven.from(project.getComponents().getByName(\"javaPlatform\"));\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/maven/PublishArtifactsPlugin.java",
    "content": "package org.springframework.gradle.maven;\n\nimport io.spring.gradle.convention.Utils;\nimport org.gradle.api.Action;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.Task;\n\npublic class PublishArtifactsPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getTasks().register(\"publishArtifacts\", new Action<Task>() {\n\t\t\t@Override\n\t\t\tpublic void execute(Task publishArtifacts) {\n\t\t\t\tpublishArtifacts.setGroup(\"Publishing\");\n\t\t\t\tpublishArtifacts.setDescription(\"Publish the artifacts to either Artifactory or Maven Central based on the version\");\n\t\t\t\tif (Utils.isRelease(project)) {\n\t\t\t\t\tpublishArtifacts.dependsOn(\"publishToOssrh\");\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tpublishArtifacts.dependsOn(\"artifactoryPublish\");\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/maven/PublishLocalPlugin.java",
    "content": "package org.springframework.gradle.maven;\n\nimport org.gradle.api.Action;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.artifacts.repositories.MavenArtifactRepository;\nimport org.gradle.api.publish.PublishingExtension;\nimport org.gradle.api.publish.maven.plugins.MavenPublishPlugin;\n\nimport java.io.File;\n\npublic class PublishLocalPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getPlugins().withType(MavenPublishPlugin.class).all(new Action<MavenPublishPlugin>() {\n\t\t\t@Override\n\t\t\tpublic void execute(MavenPublishPlugin mavenPublish) {\n\t\t\t\tPublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);\n\t\t\t\tpublishing.getRepositories().maven(new Action<MavenArtifactRepository>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void execute(MavenArtifactRepository maven) {\n\t\t\t\t\t\tmaven.setName(\"local\");\n\t\t\t\t\t\tmaven.setUrl(new File(project.getRootProject().getBuildDir(), \"publications/repos\"));\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/maven/SpringMavenPlugin.java",
    "content": "package org.springframework.gradle.maven;\n\nimport io.spring.gradle.convention.ArtifactoryPlugin;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.plugins.PluginManager;\nimport org.gradle.api.publish.maven.plugins.MavenPublishPlugin;\n\npublic class SpringMavenPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tPluginManager pluginManager = project.getPluginManager();\n\t\tpluginManager.apply(MavenPublishPlugin.class);\n\t\tpluginManager.apply(SpringSigningPlugin.class);\n\t\tpluginManager.apply(MavenPublishingConventionsPlugin.class);\n\t\tpluginManager.apply(PublishAllJavaComponentsPlugin.class);\n\t\tpluginManager.apply(PublishLocalPlugin.class);\n\t\tpluginManager.apply(PublishArtifactsPlugin.class);\n\t\tpluginManager.apply(ArtifactoryPlugin.class);\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/maven/SpringNexusPublishPlugin.java",
    "content": "package org.springframework.gradle.maven;\n\nimport io.github.gradlenexus.publishplugin.NexusPublishExtension;\nimport io.github.gradlenexus.publishplugin.NexusPublishPlugin;\nimport io.github.gradlenexus.publishplugin.NexusRepository;\nimport org.gradle.api.Action;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\n\nimport java.net.URI;\nimport java.time.Duration;\n\npublic class SpringNexusPublishPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getPlugins().apply(NexusPublishPlugin.class);\n\t\tNexusPublishExtension nexusPublishing = project.getExtensions().findByType(NexusPublishExtension.class);\n\t\tnexusPublishing.getRepositories().create(\"ossrh\", new Action<NexusRepository>() {\n\t\t\t@Override\n\t\t\tpublic void execute(NexusRepository nexusRepository) {\n\t\t\t\t\t\tnexusRepository.getNexusUrl().set(URI.create(\"https://s01.oss.sonatype.org/service/local/\"));\n\t\t\t\t\t\tnexusRepository.getSnapshotRepositoryUrl().set(URI.create(\"https://s01.oss.sonatype.org/content/repositories/snapshots/\"));\n\t\t\t\t\t}\n\t\t});\n\t\tnexusPublishing.getConnectTimeout().set(Duration.ofMinutes(3));\n\t\tnexusPublishing.getClientTimeout().set(Duration.ofMinutes(3));\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/maven/SpringSigningPlugin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage org.springframework.gradle.maven;\n\nimport org.gradle.api.Action;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.publish.Publication;\nimport org.gradle.api.publish.PublishingExtension;\nimport org.gradle.plugins.signing.SigningExtension;\nimport org.gradle.plugins.signing.SigningPlugin;\n\nimport java.util.concurrent.Callable;\n\npublic class SpringSigningPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getPluginManager().apply(SigningPlugin.class);\n\t\tproject.getPlugins().withType(SigningPlugin.class).all(new Action<SigningPlugin>() {\n\t\t\t@Override\n\t\t\tpublic void execute(SigningPlugin signingPlugin) {\n\t\t\t\tboolean hasSigningKey = project.hasProperty(\"signing.keyId\") || project.hasProperty(\"signingKey\");\n\t\t\t\tif (hasSigningKey) {\n\t\t\t\t\tsign(project);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void sign(Project project) {\n\t\tSigningExtension signing = project.getExtensions().findByType(SigningExtension.class);\n\t\tsigning.setRequired((Callable<Boolean>) () -> project.getGradle().getTaskGraph().hasTask(\"publishArtifacts\"));\n\t\tString signingKeyId = (String) project.findProperty(\"signingKeyId\");\n\t\tString signingKey = (String) project.findProperty(\"signingKey\");\n\t\tString signingPassword = (String) project.findProperty(\"signingPassword\");\n\t\tif (signingKeyId != null) {\n\t\t\tsigning.useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword);\n\t\t} else {\n\t\t\tsigning.useInMemoryPgpKeys(signingKey, signingPassword);\n\t\t}\n\t\tproject.getPlugins().withType(PublishAllJavaComponentsPlugin.class).all(new Action<PublishAllJavaComponentsPlugin>() {\n\t\t\t@Override\n\t\t\tpublic void execute(PublishAllJavaComponentsPlugin publishingPlugin) {\n\t\t\t\tPublishingExtension publishing = project.getExtensions().findByType(PublishingExtension.class);\n\t\t\t\tPublication maven = publishing.getPublications().getByName(\"mavenJava\");\n\t\t\t\tsigning.sign(maven);\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsEclipsePlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.gradle.propdeps\n\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.plugins.ide.eclipse.EclipsePlugin\n\n/**\n * Plugin to allow optional and provided dependency configurations to work with the\n * standard gradle 'eclipse' plugin\n *\n * @author Phillip Webb\n */\nclass PropDepsEclipsePlugin implements Plugin<Project> {\n\n\tpublic void apply(Project project) {\n\t\tproject.plugins.apply(PropDepsPlugin)\n\t\tproject.plugins.apply(EclipsePlugin)\n\n\t\tproject.eclipse {\n\t\t\tclasspath {\n\t\t\t\tplusConfigurations += [project.configurations.provided, project.configurations.optional]\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsIdeaPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.gradle.propdeps\n\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.plugins.ide.idea.IdeaPlugin\n\n/**\n * Plugin to allow optional and provided dependency configurations to work with the\n * standard gradle 'idea' plugin\n *\n * @author Phillip Webb\n * @author Brian Clozel\n * @link https://youtrack.jetbrains.com/issue/IDEA-107046\n * @link https://youtrack.jetbrains.com/issue/IDEA-117668\n */\nclass PropDepsIdeaPlugin implements Plugin<Project> {\n\n\tpublic void apply(Project project) {\n\t\tproject.plugins.apply(PropDepsPlugin)\n\t\tproject.plugins.apply(IdeaPlugin)\n\t\tproject.idea.module {\n\t\t\t// IDEA internally deals with 4 scopes : COMPILE, TEST, PROVIDED, RUNTIME\n\t\t\t// but only PROVIDED seems to be picked up\n\t\t\tscopes.PROVIDED.plus += [project.configurations.provided]\n\t\t\tscopes.PROVIDED.plus += [project.configurations.optional]\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/propdeps/PropDepsPlugin.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.gradle.propdeps\n\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.Configuration\nimport org.gradle.api.plugins.JavaLibraryPlugin\nimport org.gradle.api.plugins.JavaPlugin\nimport org.gradle.api.tasks.javadoc.Javadoc\n\n/**\n * Plugin to allow 'optional' and 'provided' dependency configurations\n *\n * As stated in the maven documentation, provided scope \"is only available on the compilation and test classpath,\n * and is not transitive\".\n *\n * This plugin creates two new configurations, and each one:\n * <ul>\n * <li>is a parent of the compile configuration</li>\n * <li>is not visible, not transitive</li>\n * <li>all dependencies are excluded from the default configuration</li>\n * </ul>\n *\n * @author Phillip Webb\n * @author Brian Clozel\n * @author Rob Winch\n *\n * @see <a href=\"https://www.gradle.org/docs/current/userguide/java_plugin.html#N121CF\">Maven documentation</a>\n * @see <a href=\"https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope\">Gradle configurations</a>\n * @see PropDepsEclipsePlugin\n * @see PropDepsIdeaPlugin\n */\nclass PropDepsPlugin implements Plugin<Project> {\n\n\tpublic void apply(Project project) {\n\t\tproject.plugins.apply(JavaPlugin)\n\n\t\tConfiguration provided = addConfiguration(project, \"provided\")\n\t\tConfiguration optional = addConfiguration(project, \"optional\")\n\n\t\tJavadoc javadoc = project.tasks.getByName(JavaPlugin.JAVADOC_TASK_NAME)\n\t\tjavadoc.classpath = javadoc.classpath.plus(provided).plus(optional)\n\t}\n\n\tprivate Configuration addConfiguration(Project project, String name) {\n\t\tConfiguration configuration = project.configurations.create(name)\n\t\tconfiguration.extendsFrom(project.configurations.implementation)\n\t\tproject.plugins.withType(JavaLibraryPlugin, {\n\t\t\tconfiguration.extendsFrom(project.configurations.api)\n\t\t})\n\n\t\tproject.sourceSets.all {\n\t\t\tcompileClasspath += configuration\n\t\t\truntimeClasspath += configuration\n\t\t}\n\n\t\treturn configuration\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/gradle/xsd/CreateVersionlessXsdTask.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.gradle.xsd;\n\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.file.ConfigurableFileCollection;\nimport org.gradle.api.file.RegularFileProperty;\nimport org.gradle.api.tasks.*;\nimport org.gradle.work.DisableCachingByDefault;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Creates the spring-security.xsd automatically\n *\n * @author Rob Winch\n */\n@DisableCachingByDefault(because = \"not worth it\")\npublic abstract class CreateVersionlessXsdTask extends DefaultTask {\n\n\t@InputFiles\n\tpublic abstract ConfigurableFileCollection getInputFiles();\n\n\t@OutputFile\n\tabstract RegularFileProperty getVersionlessXsdFile();\n\n\t@TaskAction\n\tvoid createVersionlessXsd() throws IOException {\n\t\tXsdFileMajorMinorVersion largest = null;\n\t\tConfigurableFileCollection inputFiles = getInputFiles();\n\t\tif (inputFiles.isEmpty()) {\n\t\t\tthrow new IllegalStateException(\"No Inputs configured\");\n\t\t}\n\t\tfor (File file : inputFiles) {\n\t\t\tXsdFileMajorMinorVersion current = XsdFileMajorMinorVersion.create(file);\n\t\t\tif (current == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (largest == null) {\n\t\t\t\tlargest = current;\n\t\t\t}\n\t\t\telse if (current.getVersion().isGreaterThan(largest.getVersion())) {\n\t\t\t\tlargest = current;\n\t\t\t}\n\t\t}\n\t\tif (largest == null) {\n\t\t\tthrow new IllegalStateException(\"Could not create versionless xsd file because no files matching spring-security-<digit>.xsd were found in \" + inputFiles.getFiles());\n\t\t}\n\t\tPath to = getVersionlessXsdFile().getAsFile().get().toPath();\n\t\tPath from = largest.getFile().toPath();\n\t\tFiles.copy(from, to, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);\n\t}\n\n\tstatic class XsdFileMajorMinorVersion {\n\t\tprivate final File file;\n\n\t\tprivate final MajorMinorVersion version;\n\n\t\tprivate XsdFileMajorMinorVersion(File file, MajorMinorVersion version) {\n\t\t\tthis.file = file;\n\t\t\tthis.version = version;\n\t\t}\n\n\t\tprivate static final Pattern FILE_MAJOR_MINOR_VERSION_PATTERN = Pattern.compile(\"^spring-security-(\\\\d+)\\\\.(\\\\d+)\\\\.xsd$\");\n\n\t\t/**\n\t\t * If matches xsd with major minor version (e.g. spring-security-5.1.xsd returns it, otherwise null\n\t\t * @param file\n\t\t * @return\n\t\t */\n\t\tstatic XsdFileMajorMinorVersion create(File file) {\n\t\t\tString fileName = file.getName();\n\t\t\tMatcher matcher = FILE_MAJOR_MINOR_VERSION_PATTERN.matcher(fileName);\n\t\t\tif (!matcher.find()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tint major = Integer.parseInt(matcher.group(1));\n\t\t\tint minor = Integer.parseInt(matcher.group(2));\n\t\t\tMajorMinorVersion version = new MajorMinorVersion(major, minor);\n\t\t\treturn new XsdFileMajorMinorVersion(file, version);\n\t\t}\n\n\t\tpublic File getFile() {\n\t\t\treturn file;\n\t\t}\n\n\t\tpublic MajorMinorVersion getVersion() {\n\t\t\treturn version;\n\t\t}\n\t}\n\n\tstatic class MajorMinorVersion {\n\t\tprivate final int major;\n\n\t\tprivate final int minor;\n\n\t\tMajorMinorVersion(int major, int minor) {\n\t\t\tthis.major = major;\n\t\t\tthis.minor = minor;\n\t\t}\n\n\t\tpublic int getMajor() {\n\t\t\treturn major;\n\t\t}\n\n\t\tpublic int getMinor() {\n\t\t\treturn minor;\n\t\t}\n\n\t\tpublic boolean isGreaterThan(MajorMinorVersion version) {\n\t\t\tif (getMajor() > version.getMajor()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (getMajor() < version.getMajor()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (getMinor() > version.getMinor()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (getMinor() < version.getMinor()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// they are equal\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/security/CheckExpectedBranchVersionPlugin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.Task;\nimport org.gradle.api.file.RegularFileProperty;\nimport org.gradle.api.plugins.JavaBasePlugin;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.tasks.CacheableTask;\nimport org.gradle.api.tasks.Input;\nimport org.gradle.api.tasks.OutputFile;\nimport org.gradle.api.tasks.TaskAction;\nimport org.gradle.api.tasks.TaskExecutionException;\nimport org.gradle.api.tasks.TaskProvider;\nimport org.gradle.api.tasks.VerificationException;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\n\n/**\n * @author Marcus da Coregio\n */\npublic class CheckExpectedBranchVersionPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tTaskProvider<CheckExpectedBranchVersionTask> checkExpectedBranchVersionTask = project.getTasks().register(\"checkExpectedBranchVersion\", CheckExpectedBranchVersionTask.class, (task) -> {\n\t\t\ttask.setGroup(\"Build\");\n\t\t\ttask.setDescription(\"Check if the project version matches the branch version\");\n\t\t\ttask.onlyIf(\"skipCheckExpectedBranchVersion property is false or not present\", CheckExpectedBranchVersionPlugin::skipPropertyFalseOrNotPresent);\n\t\t\ttask.getVersion().convention(project.provider(() -> project.getVersion().toString()));\n\t\t\ttask.getBranchName().convention(project.getProviders().exec((execSpec) -> execSpec.setCommandLine(\"git\", \"symbolic-ref\", \"--short\", \"HEAD\")).getStandardOutput().getAsText());\n\t\t\ttask.getOutputFile().convention(project.getLayout().getBuildDirectory().file(\"check-expected-branch-version\"));\n\t\t});\n\t\tproject.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(checkExpectedBranchVersionTask));\n\t}\n\n\tprivate static boolean skipPropertyFalseOrNotPresent(Task task) {\n\t\treturn task.getProject()\n\t\t\t\t.getProviders()\n\t\t\t\t.gradleProperty(\"skipCheckExpectedBranchVersion\")\n\t\t\t\t.orElse(\"false\")\n\t\t\t\t.map(\"false\"::equalsIgnoreCase)\n\t\t\t\t.get();\n\t}\n\n\t@CacheableTask\n\tpublic static abstract class CheckExpectedBranchVersionTask extends DefaultTask {\n\n\t\t@Input\n\t\tabstract Property<String> getVersion();\n\n\t\t@Input\n\t\tabstract Property<String> getBranchName();\n\n\t\t@OutputFile\n\t\tabstract RegularFileProperty getOutputFile();\n\n\t\t@TaskAction\n\t\tpublic void run() {\n\t\t\tString version = getVersion().get();\n\t\t\tString branchVersion = getBranchName().map(String::trim).get();\n\t\t\tif (!branchVersion.matches(\"^[0-9]+\\\\.[0-9]+\\\\.x$\")) {\n\t\t\t\tString msg = String.format(\"Branch version [%s] does not match *.x, ignoring\", branchVersion);\n\t\t\t\tgetLogger().warn(msg);\n\t\t\t\twriteExpectedVersionOutput(msg);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!versionsMatch(version, branchVersion)) {\n\t\t\t\tString msg = String.format(\"Project version [%s] does not match branch version [%s]. \" +\n\t\t\t\t\t\t\"Please verify that the branch contains the right version.\", version, branchVersion);\n\t\t\t\twriteExpectedVersionOutput(msg);\n\t\t\t\tthrow new VerificationException(msg);\n\t\t\t}\n\n\t\t\twriteExpectedVersionOutput(version);\n\t\t}\n\n\t\tprivate void writeExpectedVersionOutput(String fileContent) {\n\t\t\ttry {\n\t\t\t\tFiles.writeString(getOutputFile().get().getAsFile().toPath(), fileContent);\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new TaskExecutionException(this, e);\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean versionsMatch(String projectVersion, String branchVersion) {\n\t\t\tString[] projectVersionParts = projectVersion.split(\"\\\\.\");\n\t\t\tString[] branchVersionParts = branchVersion.split(\"\\\\.\");\n\t\t\tif (projectVersionParts.length < 2 || branchVersionParts.length < 2) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn projectVersionParts[0].equals(branchVersionParts[0]) && projectVersionParts[1].equals(branchVersionParts[1]);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/security/convention/versions/FileUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.convention.versions;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.function.Function;\n\nclass FileUtils {\n\tstatic void replaceFileText(File file, Function<String, String> replaceText) {\n\t\tString buildFileText = readString(file);\n\t\tString updatedBuildFileText = replaceText.apply(buildFileText);\n\t\twriteString(file, updatedBuildFileText);\n\t}\n\n\tstatic String readString(File file) {\n\t\ttry {\n\t\t\tbyte[] bytes = Files.readAllBytes(file.toPath());\n\t\t\treturn new String(bytes);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate static void writeString(File file, String text) {\n\t\ttry {\n\t\t\tFiles.write(file.toPath(), text.getBytes());\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/security/convention/versions/TransitiveDependencyLookupUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.convention.versions;\n\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\nimport org.w3c.dom.Document;\nimport org.xml.sax.SAXException;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.xpath.XPath;\nimport javax.xml.xpath.XPathExpressionException;\nimport javax.xml.xpath.XPathFactory;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nclass TransitiveDependencyLookupUtils {\n\tstatic String OIDC_SDK_NAME = \"oauth2-oidc-sdk\";\n\tstatic String NIMBUS_JOSE_JWT_NAME = \"nimbus-jose-jwt\";\n\n\tprivate static OkHttpClient client = new OkHttpClient();\n\n\tstatic String lookupJwtVersion(String oauthSdcVersion) {\n\t\tRequest request = new Request.Builder()\n\t\t\t\t.get()\n\t\t\t\t.url(\"https://repo.maven.apache.org/maven2/com/nimbusds/\" + OIDC_SDK_NAME + \"/\" + oauthSdcVersion + \"/\" + OIDC_SDK_NAME + \"-\" + oauthSdcVersion + \".pom\")\n\t\t\t\t.build();\n\t\ttry (Response response = client.newCall(request).execute()) {\n\t\t\tif (!response.isSuccessful()) {\n\t\t\t\tthrow new IOException(\"Unexpected code \" + response);\n\t\t\t}\n\t\t\tInputStream inputStream = response.body().byteStream();\n\t\t\treturn getVersion(inputStream);\n\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate static String getVersion(InputStream inputStream) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException {\n\t\tDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\n\t\tdbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);\n\t\tDocumentBuilder db = dbf.newDocumentBuilder();\n\n\t\tDocument doc = db.parse(inputStream);\n\n\t\tdoc.getDocumentElement().normalize();\n\n\t\tXPath xPath = XPathFactory.newInstance().newXPath();\n\t\treturn xPath.evaluate(\"/project/dependencies/dependency/version[../artifactId/text() = \\\"\" + NIMBUS_JOSE_JWT_NAME + \"\\\"]\", doc);\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/org/springframework/security/convention/versions/VerifyDependenciesVersionsPlugin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.convention.versions;\n\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.artifacts.Dependency;\nimport org.gradle.api.artifacts.MinimalExternalModuleDependency;\nimport org.gradle.api.artifacts.VersionCatalog;\nimport org.gradle.api.artifacts.VersionCatalogsExtension;\nimport org.gradle.api.file.RegularFileProperty;\nimport org.gradle.api.plugins.JavaBasePlugin;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.provider.Provider;\nimport org.gradle.api.tasks.CacheableTask;\nimport org.gradle.api.tasks.Input;\nimport org.gradle.api.tasks.OutputFile;\nimport org.gradle.api.tasks.TaskAction;\nimport org.gradle.api.tasks.TaskExecutionException;\nimport org.gradle.api.tasks.TaskProvider;\nimport org.gradle.api.tasks.VerificationException;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Optional;\n\npublic class VerifyDependenciesVersionsPlugin implements Plugin<Project> {\n\n\t@Override\n\tpublic void apply(Project project) {\n\t\tVersionCatalog versionCatalog = project.getExtensions().getByType(VersionCatalogsExtension.class).named(\"libs\");\n\t\tOptional<Provider<MinimalExternalModuleDependency>> oauth2OidcSdk = versionCatalog.findLibrary(\"com-nimbusds-oauth2-oidc-sdk\");\n\t\tOptional<Provider<MinimalExternalModuleDependency>> nimbusJoseJwt = versionCatalog.findLibrary(\"com-nimbusds-nimbus-jose-jwt\");\n\n\t\tif (oauth2OidcSdk.isEmpty()) {\n\t\t\tthrow new VerificationException(\"Library [com-nimbusds-oauth2-oidc-sdk] does not exist in the version catalog named libs.\");\n\t\t}\n\n\t\tif (nimbusJoseJwt.isEmpty()) {\n\t\t\tthrow new VerificationException(\"Library [com-nimbusds-nimbus-jose-jwt] does not exist in the version catalog named libs.\");\n\t\t}\n\n\t\tTaskProvider<VerifyDependenciesVersionsTask> verifyDependenciesVersionsTaskProvider = project.getTasks().register(\"verifyDependenciesVersions\", VerifyDependenciesVersionsTask.class, (task) -> {\n\t\t\ttask.setGroup(\"Verification\");\n\t\t\ttask.setDescription(\"Verify that specific dependencies are using the same version\");\n\t\t\ttask.getOauth2OidcSdkVersion().convention(oauth2OidcSdk.get().map(Dependency::getVersion));\n\t\t\ttask.getExpectedNimbusJoseJwtVersion().convention(nimbusJoseJwt.get().map(Dependency::getVersion));\n\t\t\ttask.getOutputFile().convention(project.getLayout().getBuildDirectory().file(\"verify-dependencies-versions\"));\n\t\t});\n\t\tproject.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(verifyDependenciesVersionsTaskProvider));\n\t}\n\n\t@CacheableTask\n\tpublic abstract static class VerifyDependenciesVersionsTask extends DefaultTask {\n\n\t\t@Input\n\t\tabstract Property<String> getOauth2OidcSdkVersion();\n\n\t\t@Input\n\t\tabstract Property<String> getExpectedNimbusJoseJwtVersion();\n\n\t\t@OutputFile\n\t\tabstract RegularFileProperty getOutputFile();\n\n\t\t@TaskAction\n\t\tpublic void verify()  {\n\t\t\tString oauth2OidcSdkVersion = this.getOauth2OidcSdkVersion().get();\n\t\t\tString transitiveNimbusJoseJwtVersion = TransitiveDependencyLookupUtils.lookupJwtVersion(oauth2OidcSdkVersion);\n\t\t\tString expectedNimbusJoseJwtVersion = this.getExpectedNimbusJoseJwtVersion().get();\n\t\t\tif (!transitiveNimbusJoseJwtVersion.equals(expectedNimbusJoseJwtVersion)) {\n\t\t\t\tString message = String.format(\"Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, but the project contains a different version of nimbus-jose-jwt [%s]. Please align the versions.\", transitiveNimbusJoseJwtVersion, oauth2OidcSdkVersion, expectedNimbusJoseJwtVersion);\n\t\t\t\tthrow new VerificationException(message);\n\t\t\t}\n\t\t\tString message = String.format(\"Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, the project contains expected version of nimbus-jose-jwt [%s]. Verified all versions align.\", transitiveNimbusJoseJwtVersion, oauth2OidcSdkVersion, expectedNimbusJoseJwtVersion);\n\t\t\ttry {\n\t\t\t\tFiles.writeString(getOutputFile().get().getAsFile().toPath(), message);\n\t\t\t} catch (IOException e) {\n\t\t\t\tthrow new TaskExecutionException(this, e);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/s101/S101Configure.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage s101;\n\nimport java.io.File;\n\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.tasks.TaskAction;\n\npublic class S101Configure extends DefaultTask {\n\t@TaskAction\n\tpublic void configure() throws Exception {\n\t\tS101PluginExtension extension = getProject().getExtensions().getByType(S101PluginExtension.class);\n\t\tFile buildDirectory = extension.getInstallationDirectory().get();\n\t\tFile projectDirectory = extension.getConfigurationDirectory().get();\n\t\tS101Configurer configurer = new S101Configurer(getProject());\n\t\tconfigurer.configure(buildDirectory, projectDirectory);\n\t}\n\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/s101/S101Configurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage s101;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\nimport java.io.UncheckedIOException;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport com.github.mustachejava.DefaultMustacheFactory;\nimport com.github.mustachejava.Mustache;\nimport com.github.mustachejava.MustacheFactory;\nimport org.apache.commons.io.IOUtils;\nimport org.gradle.api.Project;\nimport org.gradle.api.logging.Logger;\nimport org.gradle.api.tasks.SourceSet;\nimport org.gradle.api.tasks.SourceSetContainer;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\n\npublic class S101Configurer {\n\tprivate static final Pattern VERSION = Pattern.compile(\"<local-project .* version=\\\"(.*?)\\\"\");\n\n\tprivate static final int BUFFER = 1024;\n\tprivate static final long TOOBIG = 0x10000000; // ~268M\n\tprivate static final int TOOMANY = 200;\n\n\tprivate final MustacheFactory mustache = new DefaultMustacheFactory();\n\tprivate final Mustache hspTemplate;\n\tprivate final Mustache repositoryTemplate;\n\n\tprivate final Path licenseDirectory;\n\n\tprivate final String repository;\n\n\tprivate final String version;\n\n\tprivate final Project project;\n\tprivate final Logger logger;\n\n\tpublic S101Configurer(Project project) {\n\t\tthis.project = project;\n\t\tthis.logger = project.getLogger();\n\t\tResource template = new ClassPathResource(\"s101/project.java.hsp\");\n\t\ttry (InputStream is = template.getInputStream()) {\n\t\t\tthis.hspTemplate = this.mustache.compile(new InputStreamReader(is), \"project\");\n\t\t} catch (IOException ex) {\n\t\t\tthrow new UncheckedIOException(ex);\n\t\t}\n\t\ttemplate = new ClassPathResource(\"s101/repository.xml\");\n\t\ttry (InputStream is = template.getInputStream()) {\n\t\t\tthis.repositoryTemplate = this.mustache.compile(new InputStreamReader(is), \"repository\");\n\t\t} catch (IOException ex) {\n\t\t\tthrow new UncheckedIOException(ex);\n\t\t}\n\t\tthis.licenseDirectory = new File(System.getProperty(\"user.home\") + \"/.Structure101/java\").toPath();\n\t\tS101PluginExtension extension = project.getExtensions().getByType(S101PluginExtension.class);\n\t\tthis.repository = extension.getRepository().get();\n\t\tthis.version = extension.getVersion().get();\n\t}\n\n\tpublic void license(String licenseId) {\n\t\tPath licenseFile = this.licenseDirectory.resolve(\".structure101license.properties\");\n\t\tif (needsLicense(licenseFile, licenseId)) {\n\t\t\twriteLicense(licenseFile, licenseId);\n\t\t}\n\t}\n\n\tprivate boolean needsLicense(Path licenseFile, String licenseId) {\n\t\tif (!licenseFile.toFile().exists()) {\n\t\t\treturn true;\n\t\t}\n\t\ttry {\n\t\t\tString license = new String(Files.readAllBytes(licenseFile));\n\t\t\treturn !license.contains(licenseId);\n\t\t} catch (IOException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprivate void writeLicense(Path licenseFile, String licenseId) {\n\t\tif (!this.licenseDirectory.toFile().mkdirs()) {\n\t\t\tthis.licenseDirectory.forEach((path) -> path.toFile().delete());\n\t\t}\n\t\ttry (PrintWriter pw = new PrintWriter(licenseFile.toFile())) {\n\t\t\tpw.println(\"licensecode=\" + licenseId);\n\t\t} catch (IOException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tpublic void install(File installationDirectory, File configurationDirectory) {\n\t\tdeleteDirectory(installationDirectory);\n\t\tinstallBuildTool(installationDirectory, configurationDirectory);\n\t}\n\n\tpublic void configure(File installationDirectory, File configurationDirectory) {\n\t\tdeleteDirectory(configurationDirectory);\n\t\tconfigureProject(this.version, configurationDirectory);\n\t}\n\n\tprivate boolean deleteDirectory(File directoryToBeDeleted) {\n\t\tFile[] allContents = directoryToBeDeleted.listFiles();\n\t\tif (allContents != null) {\n\t\t\tfor (File file : allContents) {\n\t\t\t\tdeleteDirectory(file);\n\t\t\t}\n\t\t}\n\t\treturn directoryToBeDeleted.delete();\n\t}\n\n\tprivate String installBuildTool(File installationDirectory, File configurationDirectory) {\n\t\tcopyZipToFilesystem(this.repository, installationDirectory, \"structure101-build-java-all-\" + this.version);\n\t\treturn this.version;\n\t}\n\n\tprivate void copyZipToFilesystem(String source, File destination, String name) {\n\t\ttry (ZipInputStream in = new ZipInputStream(new URL(source + \"/\" + name + \".zip\").openStream())) {\n\t\t\tZipEntry entry;\n\t\t\tString build = destination.getName();\n\t\t\tint entries = 0;\n\t\t\tlong size = 0;\n\t\t\twhile ((entry = in.getNextEntry()) != null) {\n\t\t\t\tif (entry.getName().equals(name + \"/\")) {\n\t\t\t\t\tdestination.mkdirs();\n\t\t\t\t} else if (entry.getName().startsWith(name)) {\n\t\t\t\t\tif (entries++ > TOOMANY) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"Zip file has more entries than expected\");\n\t\t\t\t\t}\n\t\t\t\t\tif (size + BUFFER > TOOBIG) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"Zip file is larger than expected\");\n\t\t\t\t\t}\n\t\t\t\t\tString filename = entry.getName().replace(name, build);\n\t\t\t\t\tif (filename.contains(\"maven\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (filename.contains(\"jxbrowser\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (filename.contains(\"jetty\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (filename.contains(\"jfreechart\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (filename.contains(\"piccolo2d\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (filename.contains(\"plexus\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (filename.contains(\"websocket\")) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tvalidateFilename(filename, build);\n\t\t\t\t\tthis.logger.info(\"Downloading \" + filename);\n\t\t\t\t\ttry (OutputStream out = new FileOutputStream(new File(destination.getParentFile(), filename))) {\n\t\t\t\t\t\tbyte[] data = new byte[BUFFER];\n\t\t\t\t\t\tint read;\n\t\t\t\t\t\twhile ((read = in.read(data, 0, BUFFER)) != -1 && TOOBIG - size >= read) {\n\t\t\t\t\t\t\tout.write(data, 0, read);\n\t\t\t\t\t\t\tsize += read;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate String validateFilename(String filename, String intendedDir)\n\t\t\tthrows java.io.IOException {\n\t\tFile f = new File(filename);\n\t\tString canonicalPath = f.getCanonicalPath();\n\n\t\tFile iD = new File(intendedDir);\n\t\tString canonicalID = iD.getCanonicalPath();\n\n\t\tif (canonicalPath.startsWith(canonicalID)) {\n\t\t\treturn canonicalPath;\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\"File is outside extraction target directory.\");\n\t\t}\n\t}\n\n\tprivate void configureProject(String version, File configurationDirectory) {\n\t\tconfigurationDirectory.mkdirs();\n\t\tMap<String, Object> model = hspTemplateValues(version, configurationDirectory);\n\t\tcopyToProject(this.hspTemplate, model, new File(configurationDirectory, \"project.java.hsp\"));\n\t\tcopyToProject(\"s101/config.xml\", new File(configurationDirectory, \"config.xml\"));\n\t\tFile repository = new File(configurationDirectory, \"repository\");\n\t\tFile snapshots = new File(repository, \"snapshots\");\n\t\tif (!snapshots.exists() && !snapshots.mkdirs()) {\n\t\t\tthrow new IllegalStateException(\"Unable to create snapshots directory\");\n\t\t}\n\t\tcopyToProject(this.repositoryTemplate, model, new File(repository, \"repository.xml\"));\n\t}\n\n\tprivate void copyToProject(String location, File destination) {\n\t\tResource resource = new ClassPathResource(location);\n\t\ttry (InputStream is = resource.getInputStream();\n\t\t\tOutputStream os = new FileOutputStream(destination)) {\n\t\t\tIOUtils.copy(is, os);\n\t\t} catch (IOException ex) {\n\t\t\tthrow new UncheckedIOException(ex);\n\t\t}\n\t}\n\n\tprivate void copyToProject(Mustache view, Map<String, Object> model, File destination) {\n\t\ttry (OutputStream os = new FileOutputStream(destination)) {\n\t\t\tview.execute(new OutputStreamWriter(os), model).flush();\n\t\t} catch (IOException ex) {\n\t\t\tthrow new UncheckedIOException(ex);\n\t\t}\n\t}\n\n\tprivate Map<String, Object> hspTemplateValues(String version, File configurationDirectory) {\n\t\tMap<String, Object> values = new LinkedHashMap<>();\n\t\tvalues.put(\"version\", version);\n\t\tvalues.put(\"patchVersion\", version.split(\"\\\\.\")[2]);\n\t\tvalues.put(\"relativeTo\", \"const(THIS_FILE)/\" + configurationDirectory.toPath().relativize(this.project.getProjectDir().toPath()));\n\n\t\tList<Map<String, Object>> entries = new ArrayList<>();\n\t\tSet<Project> projects = this.project.getAllprojects();\n\t\tfor (Project p : projects) {\n\t\t\tSourceSetContainer sourceSets = (SourceSetContainer) p.getExtensions().findByName(\"sourceSets\");\n\t\t\tif (sourceSets == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (SourceSet source : sourceSets) {\n\t\t\t\tSet<File> classDirs = source.getOutput().getClassesDirs().getFiles();\n\t\t\t\tfor (File directory : classDirs) {\n\t\t\t\t\tMap<String, Object> entry = new HashMap<>();\n\t\t\t\t\tentry.put(\"path\", this.project.getProjectDir().toPath().relativize(directory.toPath()));\n\t\t\t\t\tentry.put(\"module\", p.getName());\n\t\t\t\t\tentries.add(entry);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvalues.put(\"entries\", entries);\n\t\treturn values;\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/s101/S101Install.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage s101;\n\nimport java.io.File;\n\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.tasks.TaskAction;\n\npublic class S101Install extends DefaultTask {\n\t@TaskAction\n\tpublic void install() throws Exception {\n\t\tS101PluginExtension extension = getProject().getExtensions().getByType(S101PluginExtension.class);\n\t\tFile installationDirectory = extension.getInstallationDirectory().get();\n\t\tFile configurationDirectory = extension.getConfigurationDirectory().get();\n\t\tS101Configurer configurer = new S101Configurer(getProject());\n\t\tconfigurer.install(installationDirectory, configurationDirectory);\n\t}\n\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/s101/S101Plugin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage s101;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.tasks.JavaExec;\n\npublic class S101Plugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getExtensions().add(\"s101\", new S101PluginExtension(project));\n\t\tproject.getTasks().register(\"s101Install\", S101Install.class, this::configure);\n\t\tproject.getTasks().register(\"s101Configure\", S101Configure.class, this::configure);\n\t\tproject.getTasks().register(\"s101\", JavaExec.class, this::configure);\n\t}\n\n\tprivate void configure(S101Install install) {\n\t\tinstall.setDescription(\"Installs Structure101 to your filesystem\");\n\t}\n\n\tprivate void configure(S101Configure configure) {\n\t\tconfigure.setDescription(\"Applies a default Structure101 configuration to the project\");\n\t}\n\n\tprivate void configure(JavaExec exec) {\n\t\texec.setDescription(\"Runs Structure101 headless analysis, installing and configuring if necessary\");\n\t\texec.dependsOn(\"assemble\");\n\t\tProject project = exec.getProject();\n\t\tS101PluginExtension extension = project.getExtensions().getByType(S101PluginExtension.class);\n\t\texec\n\t\t\t\t.workingDir(extension.getInstallationDirectory())\n\t\t\t\t.classpath(new File(extension.getInstallationDirectory().get(), \"structure101-java-build.jar\"))\n\t\t\t\t.args(new File(new File(project.getBuildDir(), \"s101\"), \"config.xml\"))\n\t\t\t\t.systemProperty(\"s101.label\", computeLabel(extension).get())\n\t\t\t\t.doFirst((task) -> {\n\t\t\t\t\tinstallAndConfigureIfNeeded(project);\n\t\t\t\t\tcopyConfigurationToBuildDirectory(extension, project);\n\t\t\t\t})\n\t\t\t\t.doLast((task) -> {\n\t\t\t\t\tcopyResultsBackToConfigurationDirectory(extension, project);\n\t\t\t\t});\n\t}\n\n\tprivate Property<String> computeLabel(S101PluginExtension extension) {\n\t\tboolean hasBaseline = extension.getConfigurationDirectory().get().toPath()\n\t\t\t\t.resolve(\"repository\").resolve(\"snapshots\").resolve(\"baseline\").toFile().exists();\n\t\tif (!hasBaseline) {\n\t\t\treturn extension.getLabel().convention(\"baseline\");\n\t\t}\n\t\treturn extension.getLabel().convention(\"recent\");\n\t}\n\n\tprivate void installAndConfigureIfNeeded(Project project) {\n\t\tS101Configurer configurer = new S101Configurer(project);\n\t\tS101PluginExtension extension = project.getExtensions().getByType(S101PluginExtension.class);\n\t\tString licenseId = extension.getLicenseId().getOrNull();\n\t\tif (licenseId != null) {\n\t\t\tconfigurer.license(licenseId);\n\t\t}\n\t\tFile installationDirectory = extension.getInstallationDirectory().get();\n\t\tFile configurationDirectory = extension.getConfigurationDirectory().get();\n\t\tif (!installationDirectory.exists()) {\n\t\t\tconfigurer.install(installationDirectory, configurationDirectory);\n\t\t}\n\t\tif (!configurationDirectory.exists()) {\n\t\t\tconfigurer.configure(installationDirectory, configurationDirectory);\n\t\t}\n\t}\n\n\tprivate void copyConfigurationToBuildDirectory(S101PluginExtension extension, Project project) {\n\t\tPath configurationDirectory = extension.getConfigurationDirectory().get().toPath();\n\t\tPath buildDirectory = project.getBuildDir().toPath();\n\t\tcopyDirectory(project, configurationDirectory, buildDirectory);\n\t}\n\n\tprivate void copyResultsBackToConfigurationDirectory(S101PluginExtension extension, Project project) {\n\t\tPath buildConfigurationDirectory = project.getBuildDir().toPath().resolve(\"s101\");\n\t\tString label = extension.getLabel().get();\n\t\tif (\"baseline\".equals(label)) { // a new baseline was created\n\t\t\tcopyDirectory(project, buildConfigurationDirectory.resolve(\"repository\").resolve(\"snapshots\"),\n\t\t\t\t\textension.getConfigurationDirectory().get().toPath().resolve(\"repository\"));\n\t\t\tcopyDirectory(project, buildConfigurationDirectory.resolve(\"repository\"),\n\t\t\t\t\textension.getConfigurationDirectory().get().toPath());\n\t\t}\n\t}\n\n\tprivate void copyDirectory(Project project, Path source, Path destination) {\n\t\ttry {\n\t\t\tFiles.walk(source)\n\t\t\t\t\t.forEach(each -> {\n\t\t\t\t\t\tPath relativeToSource = source.getParent().relativize(each);\n\t\t\t\t\t\tPath resolvedDestination = destination.resolve(relativeToSource);\n\t\t\t\t\t\tif (each.toFile().isDirectory()) {\n\t\t\t\t\t\t\tresolvedDestination.toFile().mkdirs();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tInputStream input;\n\t\t\t\t\t\tif (\"project.java.hsp\".equals(each.toFile().getName())) {\n\t\t\t\t\t\t\tPath relativeTo = project.getBuildDir().toPath().resolve(\"s101\").relativize(project.getProjectDir().toPath());\n\t\t\t\t\t\t\tString value = \"const(THIS_FILE)/\" + relativeTo;\n\t\t\t\t\t\t\tinput = replace(each, \"<property name=\\\"relative-to\\\" value=\\\"(.*)\\\" />\", \"<property name=\\\"relative-to\\\" value=\\\"\" + value + \"\\\" />\");\n\t\t\t\t\t\t} else if (each.toFile().toString().endsWith(\".xml\")) {\n\t\t\t\t\t\t\tinput = replace(each, \"\\\\r\\\\n\", \"\\n\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tinput = input(each);\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tFiles.copy(input, resolvedDestination, StandardCopyOption.REPLACE_EXISTING);\n\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate InputStream replace(Path file, String search, String replace) {\n\t\ttry {\n\t\t\tbyte[] b = Files.readAllBytes(file);\n\t\t\tString contents = new String(b).replaceAll(search, replace);\n\t\t\treturn new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate InputStream input(Path file) {\n\t\ttry {\n\t\t\treturn new FileInputStream(file.toFile());\n\t\t} catch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/s101/S101PluginExtension.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage s101;\n\nimport java.io.File;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport com.gargoylesoftware.htmlunit.WebClient;\nimport com.gargoylesoftware.htmlunit.html.HtmlAnchor;\nimport com.gargoylesoftware.htmlunit.html.HtmlPage;\nimport org.gradle.api.Project;\nimport org.gradle.api.provider.Property;\nimport org.gradle.api.tasks.Input;\nimport org.gradle.api.tasks.InputDirectory;\n\npublic class S101PluginExtension {\n\tprivate final Property<String> licenseId;\n\n\tprivate final Property<String> repository;\n\n\tprivate final Property<String> version;\n\n\tprivate final Property<File> installationDirectory;\n\tprivate final Property<File> configurationDirectory;\n\tprivate final Property<String> label;\n\n\t@Input\n\tpublic Property<String> getLicenseId() {\n\t\treturn this.licenseId;\n\t}\n\n\tpublic void setLicenseId(String licenseId) {\n\t\tthis.licenseId.set(licenseId);\n\t}\n\n\t@InputDirectory\n\tpublic Property<File> getInstallationDirectory() {\n\t\treturn this.installationDirectory;\n\t}\n\n\tpublic void setInstallationDirectory(String installationDirectory) {\n\t\tthis.installationDirectory.set(new File(installationDirectory));\n\t}\n\n\t@InputDirectory\n\tpublic Property<File> getConfigurationDirectory() {\n\t\treturn this.configurationDirectory;\n\t}\n\n\tpublic void setConfigurationDirectory(String configurationDirectory) {\n\t\tthis.configurationDirectory.set(new File(configurationDirectory));\n\t}\n\n\t@Input\n\tpublic Property<String> getLabel() {\n\t\treturn this.label;\n\t}\n\n\tpublic void setLabel(String label) {\n\t\tthis.label.set(label);\n\t}\n\n\t@Input\n\tpublic Property<String> getRepository() {\n\t\treturn repository;\n\t}\n\n\tpublic void setRepository(String repository) {\n\t\tthis.repository.set(repository);\n\t}\n\n\t@Input\n\tpublic Property<String> getVersion() {\n\t\treturn this.version;\n\t}\n\n\tpublic void setVersion(String version) {\n\t\tthis.version.set(version);\n\t}\n\n\tpublic S101PluginExtension(Project project) {\n\t\tthis.licenseId = project.getObjects().property(String.class);\n\t\tif (project.hasProperty(\"s101.licenseId\")) {\n\t\t\tsetLicenseId((String) project.findProperty(\"s101.licenseId\"));\n\t\t}\n\t\tthis.installationDirectory = project.getObjects().property(File.class)\n\t\t\t\t.convention(new File(project.getBuildDir(), \"s101\"));\n\t\tthis.configurationDirectory = project.getObjects().property(File.class)\n\t\t\t\t.convention(new File(project.getProjectDir(), \"s101\"));\n\t\tthis.label = project.getObjects().property(String.class);\n\t\tif (project.hasProperty(\"s101.label\")) {\n\t\t\tsetLabel((String) project.findProperty(\"s101.label\"));\n\t\t}\n\t\tthis.repository = project.getObjects().property(String.class);\n\t\tif (project.hasProperty(\"s101.repository\")) {\n\t\t\tsetRepository((String) project.findProperty(\"s101.repository\"));\n\t\t} else {\n\t\t\tsetRepository(\"https://structure101.com/binaries/v6\");\n\t\t}\n\t\tthis.version = project.getObjects().property(String.class);\n\t\tif (project.hasProperty(\"s101.version\")) {\n\t\t\tsetVersion((String) project.findProperty(\"s101.version\"));\n\t\t} else {\n\t\t\ttry (final WebClient webClient = new WebClient()) {\n\t\t\t\tHtmlPage page = webClient.getPage(getRepository().get());\n\t\t\t\tMatcher matcher = null;\n\t\t\t\tfor (HtmlAnchor anchor : page.getAnchors()) {\n\t\t\t\t\tMatcher candidate = Pattern.compile(\"(structure101-build-java-all-)(.*).zip\").matcher(anchor.getHrefAttribute());\n\t\t\t\t\tif (candidate.find()) {\n\t\t\t\t\t\tmatcher = candidate;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (matcher != null) {\n\t\t\t\t\tsetVersion(matcher.group(2));\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/trang/RncToXsd.java",
    "content": "package trang;\n\nimport com.thaiopensource.relaxng.translate.Driver;\nimport net.sf.saxon.TransformerFactoryImpl;\nimport org.gradle.api.DefaultTask;\nimport org.gradle.api.tasks.InputDirectory;\nimport org.gradle.api.tasks.InputFile;\nimport org.gradle.api.tasks.OutputDirectory;\nimport org.gradle.api.tasks.TaskAction;\n\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerException;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.stream.StreamResult;\nimport javax.xml.transform.stream.StreamSource;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.StandardCopyOption;\n\n/**\n * Converts .rnc files to .xsd files using trang and then applies an xsl file to cleanup the results.\n */\npublic class RncToXsd extends DefaultTask {\n\n\tprivate File rncDir;\n\n\tprivate File xslFile;\n\n\tprivate File xsdDir;\n\n\t@InputDirectory\n\tpublic File getRncDir() {\n\t\treturn rncDir;\n\t}\n\n\tpublic void setRncDir(File rncDir) {\n\t\tthis.rncDir = rncDir;\n\t}\n\n\t@InputFile\n\tpublic File getXslFile() {\n\t\treturn xslFile;\n\t}\n\n\tpublic void setXslFile(File xslFile) {\n\t\tthis.xslFile = xslFile;\n\t}\n\n\t@OutputDirectory\n\tpublic File getXsdDir() {\n\t\treturn xsdDir;\n\t}\n\n\tpublic void setXsdDir(File xsdDir) {\n\t\tthis.xsdDir = xsdDir;\n\t}\n\n\t@TaskAction\n\tpublic final void transform() throws IOException, TransformerException {\n\t\tString xslPath = xslFile.getAbsolutePath();\n\n\t\tFile[] files = rncDir.listFiles((dir, file) -> file.endsWith(\".rnc\"));\n\t\tif(files != null) {\n\t\t\tfor (File rncFile : files) {\n\t\t\t\tFile xsdFile = new File(xsdDir, rncFile.getName().replace(\".rnc\", \".xsd\"));\n\t\t\t\tString xsdOutputPath = xsdFile.getAbsolutePath();\n\n\t\t\t\tnew Driver().run(new String[]{rncFile.getAbsolutePath(), xsdOutputPath});\n\n\t\t\t\tTransformerFactory tFactory = new TransformerFactoryImpl();\n\t\t\t\tTransformer transformer = tFactory.newTransformer(new StreamSource(xslPath));\n\n\t\t\t\tFile temp = File.createTempFile(\"gradle-trang-\" + xsdFile.getName(), \".xsd\");\n\n\t\t\t\tFiles.copy(xsdFile.toPath(), temp.toPath(), StandardCopyOption.REPLACE_EXISTING);\n\t\t\t\tStreamSource xmlSource = new StreamSource(temp);\n\t\t\t\ttransformer.transform(xmlSource, new StreamResult(xsdFile));\n\t\t\t\ttemp.delete();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/trang/TrangPlugin.java",
    "content": "package trang;\n\nimport org.gradle.api.Plugin;\nimport org.gradle.api.Project;\n\n/**\n * Used for converting .rnc files to .xsd files.\n * @author Rob Winch\n */\npublic class TrangPlugin implements Plugin<Project> {\n\t@Override\n\tpublic void apply(Project project) {\n\t\tproject.getTasks().register(\"rncToXsd\", RncToXsd.class, (rncToXsd) -> {\n\t\t\trncToXsd.setDescription(\"Converts .rnc to .xsd\");\n\t\t\trncToXsd.setGroup(\"Build\");\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.artifactory.properties",
    "content": "implementation-class=io.spring.gradle.convention.ArtifactoryPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.bom.properties",
    "content": "implementation-class=io.spring.gradle.convention.MavenBomPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.checkstyle.properties",
    "content": "implementation-class=io.spring.gradle.convention.CheckstylePlugin\n"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.docs.properties",
    "content": "implementation-class=io.spring.gradle.convention.DocsPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.eclipse.properties",
    "content": "implementation-class=io.spring.gradle.convention.EclipsePlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.integration-test.properties",
    "content": "implementation-class=io.spring.gradle.convention.IntegrationTestPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.jacoco.properties",
    "content": "implementation-class=io.spring.gradle.convention.JacocoPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-api.properties",
    "content": "implementation-class=io.spring.gradle.convention.JavadocApiPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-options.properties",
    "content": "implementation-class=io.spring.gradle.convention.JavadocOptionsPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.repository.properties",
    "content": "implementation-class=io.spring.gradle.convention.RepositoryConventionPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.root.properties",
    "content": "implementation-class=io.spring.gradle.convention.RootProjectPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-module.properties",
    "content": "implementation-class=io.spring.gradle.convention.SpringModulePlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-test.properties",
    "content": "implementation-class=io.spring.gradle.convention.SpringTestPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.tests-configuration.properties",
    "content": "implementation-class=io.spring.gradle.convention.TestsConfigurationPlugin"
  },
  {
    "path": "buildSrc/src/main/resources/s101/config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<headless version=\"1.0\">\n\t<operations>\n\t\t<operation type=\"publish\">\n\t\t\t<argument name=\"overwrite\" value=\"true\"/>\n\t\t\t<argument name=\"diagrams\" value=\"true\"/>\n\t\t</operation>\n\t\t<operation type=\"check-key-measures\">\n\t\t\t<argument name=\"baseline\" value=\"baseline\"/>\n\t\t\t<argument name=\"useProjectFileSpec\" value=\"true\"/>\n\t\t\t<argument name=\"useProjectFileDiagrams\" value=\"true\"/>\n\t\t\t<argument name=\"fail-on-architecture-violations\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-fat-package\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-fat-class\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-fat-method\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-feedback-dependencies\" value=\"true\"/>\n\t\t\t<argument name=\"fail-on-spec-violation-dependencies\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-total-problem-dependencies\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-spec-item-violations\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-biggest-class-tangle\" value=\"true\"/>\n\t\t\t<argument name=\"fail-on-tangled-package\" value=\"true\"/>\n\t\t\t<argument name=\"fail-on-architecture-violations\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-total-problem-dependencies\" value=\"true\"/>\n\t\t\t<argument name=\"identifier-on-violation\" value=\"S101 key measure violation\"/>\n\t\t</operation>\n\t</operations>\n\t<arguments>\n\t\t<argument name=\"local-project\" value=\"const(THIS_FILE)/project.java.hsp\"/>\n\t\t<argument name=\"repository\" value=\"const(THIS_FILE)/repository\"/>\n\t\t<argument name=\"project\" value=\"snapshots\"/>\n\t</arguments>\n</headless>\n"
  },
  {
    "path": "buildSrc/src/main/resources/s101/project.java.hsp",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<local-project language=\"java\" version=\"{{version}}\" xml-version=\"3\" flavor=\"j2se\">\n  <property name=\"show-as-module\" value=\"false\" />\n  <property name=\"publish-architecture-artifacts\" value=\"true\" />\n  <property name=\"force-classpath\" value=\"false\" />\n  <property name=\"project-type\" value=\"classpath\" />\n  <property name=\"hide-externals\" value=\"true\" />\n  <property name=\"parse-archive-in-archive\" value=\"false\" />\n  <property name=\"include-injected-dependency\" value=\"false\" />\n  <property name=\"relative-to\" value=\"{{relativeTo}}\" />\n  <property name=\"action-set-mod\" value=\"1\" />\n  <property name=\"detail-mode\" value=\"true\" />\n  <property name=\"hide-deprecated\" value=\"false\" />\n  <property name=\"resolve-name-clashes\" value=\"true\" />\n  <property name=\"project-excluded\" />\n  <property name=\"show-needs-to-compile\" value=\"false\" />\n  <classpath>\n    {{#entries}}\n    <classpathentry kind=\"lib\" path=\"{{path}}\" module=\"{{module}}\" />\n    {{/entries}}\n  </classpath>\n  <pom-root-files />\n  <modules-in-scope />\n  <restructuring>\n    <set version=\"3\" name=\"Action list 1\" hiview=\"Codemap\" active=\"true\" todo=\"false\" list=\"0\" />\n  </restructuring>\n  <grid-set sep=\".\" version=\"{{version}}\" />\n</local-project>\n"
  },
  {
    "path": "buildSrc/src/main/resources/s101/repository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<structure101-repository language=\"java\" version=\"{{patchVersion}}\">\n  <xs-configuration>\n    <entry metric=\"Tangled\" scope=\"design\" threshold=\"0\" color=\"153,53,0\" />\n    <entry metric=\"Fat\" scope=\"design\" threshold=\"120\" color=\"255,153,0\" />\n    <entry metric=\"Fat\" scope=\"leaf package\" threshold=\"120\" color=\"0,153,153\" />\n    <entry metric=\"Fat\" scope=\"class\" threshold=\"120\" color=\"255,153,153\" />\n    <entry metric=\"Fat\" scope=\"method\" threshold=\"15\" color=\"51,255,51\" />\n  </xs-configuration>\n  <!--Note: All date strings are stored in short US format e.g. 2/1/06 for 1st Feb 2006-->\n  <project name=\"snapshots\" dir=\"snapshots\" baselineSnapshot=\"default\" version=\"{{patchVersion}}\">\n    <snapshot label=\"baseline\" location=\"baseline\" timestamp=\"3/16/21, 4:42 PM\" version=\"{{patchVersion}}\" detail=\"true\" good=\"true\" size=\"20\" />\n  </project>\n</structure101-repository>\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/TestKit.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\npackage io.spring.gradle;\n\nimport org.apache.commons.io.FileUtils;\nimport org.gradle.testkit.runner.GradleRunner;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.nio.file.Paths;\nimport java.util.Enumeration;\n\npublic class TestKit {\n\tfinal File buildDir;\n\n\tpublic TestKit(File buildDir) {\n\t\tthis.buildDir = buildDir;\n\t}\n\n\tpublic File getRootDir() {\n\t\treturn buildDir;\n\t}\n\n\tpublic GradleRunner withProjectDir(File projectDir) throws IOException {\n\t\tFileUtils.copyDirectory(projectDir, buildDir);\n\t\treturn GradleRunner.create()\n\t\t\t.withProjectDir(buildDir)\n\t\t\t.withPluginClasspath();\n\t}\n\n\tpublic GradleRunner withProjectResource(String projectResourceName) throws IOException, URISyntaxException {\n\t\tClassLoader classLoader = getClass().getClassLoader();\n\t\tEnumeration<URL> resources = classLoader.getResources(projectResourceName);\n\t\tif(!resources.hasMoreElements()) {\n\t\t\tthrow new IOException(\"Cannot find resource \" + projectResourceName + \" with \" + classLoader);\n\t\t}\n\t\tURL resourceUrl = resources.nextElement();\n\t\tFile projectDir = Paths.get(resourceUrl.toURI()).toFile();\n\t\treturn withProjectDir(projectDir);\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/IntegrationPluginTest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport org.apache.commons.io.FileUtils;\nimport org.gradle.api.Project;\nimport org.gradle.api.plugins.JavaPlugin;\nimport org.gradle.testfixtures.ProjectBuilder;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class IntegrationPluginTest {\n\tProject rootProject;\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tif (rootProject != null) {\n\t\t\tFileUtils.deleteDirectory(rootProject.getProjectDir());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void applyWhenNoSourceThenIntegrationTestTaskNull() {\n\t\trootProject = ProjectBuilder.builder().build();\n\t\trootProject.getPlugins().apply(JavaPlugin.class);\n\t\trootProject.getPlugins().apply(IntegrationTestPlugin.class);\n\n\t\tassertThat(rootProject.getTasks().findByPath(\"integrationTest\")).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/IntegrationTestPluginITest.java",
    "content": "package io.spring.gradle.convention;\n\nimport io.spring.gradle.TestKit;\nimport org.gradle.testkit.runner.BuildResult;\nimport org.gradle.testkit.runner.TaskOutcome;\nimport org.junit.Test;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport java.io.File;\nimport java.nio.file.Path;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class IntegrationTestPluginITest {\n\tprivate io.spring.gradle.TestKit testKit;\n\n\t@BeforeEach\n\tvoid setup(@TempDir Path tempDir) {\n\t\tthis.testKit = new TestKit(tempDir.toFile());\n\t}\n\n    @Test\n\tpublic void checkWithJavaPlugin() throws Exception {\n        BuildResult result = this.testKit.withProjectResource(\"samples/integrationtest/withjava/\")\n\t\t\t\t.withArguments(\"check\")\n\t\t\t\t.build();\n\t\tassertThat(result.task(\":check\").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);\n\t\tassertThat(new File(testKit.getRootDir(), \"build/test-results/integrationTest/\")).exists();\n\t\tassertThat(new File(testKit.getRootDir(), \"build/reports/tests/integrationTest/\")).exists();\n\t}\n\n\t@Test\n    public void checkWithPropdeps() throws Exception {\n        BuildResult result = this.testKit.withProjectResource(\"samples/integrationtest/withpropdeps/\")\n\t\t\t\t.withArguments(\"check\")\n\t\t\t\t.build();\n\t\tassertThat(result.task(\":check\").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);\n\t\tassertThat(new File(testKit.getRootDir(), \"build/test-results/integrationTest/\")).exists();\n\t\tassertThat(new File(testKit.getRootDir(), \"build/reports/tests/integrationTest/\")).exists();\n\t}\n\n\t@Test\n    public void checkWithGroovy() throws Exception {\n\t\tBuildResult result = this.testKit.withProjectResource(\"samples/integrationtest/withgroovy/\")\n\t\t\t\t.withArguments(\"check\")\n\t\t\t\t.build();\n\t\tassertThat(result.task(\":check\").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);\n\t\tassertThat(new File(testKit.getRootDir(), \"build/test-results/integrationTest/\")).exists();\n\t\tassertThat(new File(testKit.getRootDir(), \"build/reports/tests/integrationTest/\")).exists();\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/JacocoPluginITest.java",
    "content": "package io.spring.gradle.convention;\n\nimport org.gradle.testkit.runner.BuildResult;\nimport org.gradle.testkit.runner.TaskOutcome;\nimport org.junit.Test;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport java.io.File;\nimport java.nio.file.Path;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class JacocoPluginITest{\n\tprivate io.spring.gradle.TestKit testKit;\n\n\t@BeforeEach\n\tvoid setup(@TempDir Path tempDir) {\n\t\tthis.testKit = new io.spring.gradle.TestKit(tempDir.toFile());\n\t}\n\n    @Test\n\tpublic void checkWithJavaPlugin() throws Exception {\n        BuildResult result = this.testKit.withProjectResource(\"samples/jacoco/java/\")\n\t\t\t\t.withArguments(\"check\")\n\t\t\t\t.build();\n\t\tassertThat(result.task(\":check\").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);\n\t\tassertThat(new File(testKit.getRootDir(), \"build/jacoco\")).exists();\n\t\tassertThat(new File(testKit.getRootDir(), \"build/reports/jacoco/test/html/\")).exists();\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginITest.java",
    "content": "package io.spring.gradle.convention;\n\nimport io.spring.gradle.TestKit;\nimport org.apache.commons.io.FileUtils;\nimport org.gradle.testkit.runner.BuildResult;\nimport org.gradle.testkit.runner.TaskOutcome;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport java.io.File;\nimport java.nio.file.Path;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class JavadocApiPluginITest {\n\tprivate TestKit testKit;\n\n\t@BeforeEach\n\tvoid setup(@TempDir Path tempDir) {\n\t\tthis.testKit = new TestKit(tempDir.toFile());\n\t}\n\n\t@Test\n    public void multiModuleApi() throws Exception {\n        BuildResult result = this.testKit.withProjectResource(\"samples/javadocapi/multimodule/\")\n\t\t\t\t.withArguments(\"api\")\n\t\t\t\t.build();\n\t\tassertThat(result.task(\":api\").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);\n        File allClasses = new File(testKit.getRootDir(), \"build/api/allclasses-noframe.html\");\n\t\tFile index = new File(testKit.getRootDir(), \"build/api/allclasses-index.html\");\n\t\tFile listing = allClasses.exists() ? allClasses : index;\n\t\tString listingText = FileUtils.readFileToString(listing);\n\t\tassertThat(listingText).contains(\"sample/Api.html\");\n        assertThat(listingText).contains(\"sample/Impl.html\");\n\t\tassertThat(listingText).doesNotContain(\"sample/Sample.html\");\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginTest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport java.io.File;\n\nimport org.apache.commons.io.FileUtils;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport org.gradle.api.Project;\nimport org.gradle.api.tasks.javadoc.Javadoc;\nimport org.gradle.testfixtures.ProjectBuilder;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author Rob Winch\n */\npublic class JavadocApiPluginTest {\n\tProject rootProject;\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tif (rootProject != null) {\n\t\t\tFileUtils.deleteDirectory(rootProject.getProjectDir());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void applyWhenNotOverrideThenPropertiesDefaulted() {\n\t\trootProject = ProjectBuilder.builder().build();\n\t\trootProject.getPlugins().apply(JavadocApiPlugin.class);\n\n\t\tJavadoc apiTask = (Javadoc) rootProject.getTasks().getByPath(\"api\");\n\n\t\tassertThat(apiTask).isNotNull();\n\t\tassertThat(apiTask.getGroup()).isEqualTo(\"Documentation\");\n\t\tassertThat(apiTask.getDescription()).isEqualTo(\"Generates aggregated Javadoc API documentation.\");\n\t\tassertThat(apiTask.getMaxMemory()).isEqualTo(\"1024m\");\n\t\tassertThat(apiTask.getDestinationDir()).isEqualTo(new File(rootProject.getBuildDir(), \"api\"));\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/RepositoryConventionPluginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage io.spring.gradle.convention;\n\nimport org.gradle.api.Project;\nimport org.gradle.api.artifacts.dsl.RepositoryHandler;\nimport org.gradle.api.artifacts.repositories.ArtifactRepository;\nimport org.gradle.api.artifacts.repositories.MavenArtifactRepository;\nimport org.gradle.api.plugins.ExtraPropertiesExtension;\nimport org.gradle.testfixtures.ProjectBuilder;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link RepositoryConventionPlugin}.\n */\npublic class RepositoryConventionPluginTests {\n\n\tprivate Project project = ProjectBuilder.builder().build();\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.project.getProperties().clear();\n\t}\n\n\t@Test\n\tpublic void applyWhenIsReleaseThenShouldIncludeReleaseRepo() {\n\t\tthis.project.setVersion(\"1.0.0.RELEASE\");\n\t\tthis.project.getPluginManager().apply(RepositoryConventionPlugin.class);\n\n\t\tRepositoryHandler repositories = this.project.getRepositories();\n\t\tassertReleaseRepository(repositories);\n\t}\n\n\t@Test\n\tpublic void applyWhenIsMilestoneThenShouldIncludeMilestoneRepo() {\n\t\tthis.project.setVersion(\"1.0.0.M1\");\n\t\tthis.project.getPluginManager().apply(RepositoryConventionPlugin.class);\n\n\t\tRepositoryHandler repositories = this.project.getRepositories();\n\t\tassertMilestoneRepository(repositories); // milestone\n\t}\n\n\t@Test\n\tpublic void applyWhenIsSnapshotThenShouldIncludeSnapshotRepo() {\n\t\tthis.project.setVersion(\"1.0.0.BUILD-SNAPSHOT\");\n\t\tthis.project.getPluginManager().apply(RepositoryConventionPlugin.class);\n\n\t\tRepositoryHandler repositories = this.project.getRepositories();\n\t\tassertSnapshotRepository(repositories);\n\t}\n\n\t@Test\n\tpublic void applyWhenIsSnapshotWithForceReleaseThenShouldOnlyIncludeReleaseRepo() {\n\t\tthis.project.getExtensions().getByType(ExtraPropertiesExtension.class)\n\t\t\t\t.set(\"forceMavenRepositories\", \"release\");\n\t\tthis.project.setVersion(\"1.0.0.RELEASE\");\n\t\tthis.project.getPluginManager().apply(RepositoryConventionPlugin.class);\n\n\t\tRepositoryHandler repositories = this.project.getRepositories();\n\t\tassertReleaseRepository(repositories);\n\t}\n\n\t@Test\n\tpublic void applyWhenIsReleaseWithForceMilestoneThenShouldIncludeMilestoneRepo() {\n\t\tthis.project.getExtensions().getByType(ExtraPropertiesExtension.class)\n\t\t\t\t.set(\"forceMavenRepositories\", \"milestone\");\n\t\tthis.project.setVersion(\"1.0.0.RELEASE\");\n\t\tthis.project.getPluginManager().apply(RepositoryConventionPlugin.class);\n\n\t\tRepositoryHandler repositories = this.project.getRepositories();\n\t\tassertMilestoneRepository(repositories);\n\t}\n\n\t@Test\n\tpublic void applyWhenIsReleaseWithForceSnapshotThenShouldIncludeSnapshotRepo() {\n\t\tthis.project.getExtensions().getByType(ExtraPropertiesExtension.class)\n\t\t\t\t.set(\"forceMavenRepositories\", \"snapshot\");\n\t\tthis.project.setVersion(\"1.0.0.RELEASE\");\n\t\tthis.project.getPluginManager().apply(RepositoryConventionPlugin.class);\n\n\t\tRepositoryHandler repositories = this.project.getRepositories();\n\t\tassertSnapshotRepository(repositories);\n\t}\n\n\t@Test\n\tpublic void applyWhenIsReleaseWithForceLocalThenShouldIncludeReleaseAndLocalRepos() {\n\t\tthis.project.getExtensions().getByType(ExtraPropertiesExtension.class)\n\t\t\t\t.set(\"forceMavenRepositories\", \"local\");\n\t\tthis.project.setVersion(\"1.0.0.RELEASE\");\n\t\tthis.project.getPluginManager().apply(RepositoryConventionPlugin.class);\n\n\t\tRepositoryHandler repositories = this.project.getRepositories();\n\t\tassertThat(repositories).hasSize(4);\n\t\tassertThat((repositories.get(0)).getName()).isEqualTo(\"MavenLocal\");\n\t}\n\n\t@Test\n\tpublic void applyWhenIsReleaseWithForceMilestoneAndLocalThenShouldIncludeMilestoneAndLocalRepos() {\n\t\tthis.project.getExtensions().getByType(ExtraPropertiesExtension.class)\n\t\t\t\t.set(\"forceMavenRepositories\", \"milestone,local\");\n\t\tthis.project.setVersion(\"1.0.0.RELEASE\");\n\t\tthis.project.getPluginManager().apply(RepositoryConventionPlugin.class);\n\n\t\tRepositoryHandler repositories = this.project.getRepositories();\n\t\tassertThat(repositories).hasSize(5);\n\t\tassertThat((repositories.get(0)).getName()).isEqualTo(\"MavenLocal\");\n\t}\n\n\tprivate void assertSnapshotRepository(RepositoryHandler repositories) {\n\t\tassertThat(repositories).extracting(ArtifactRepository::getName).hasSize(5);\n\t\tassertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())\n\t\t\t\t.isEqualTo(\"https://repo.maven.apache.org/maven2/\");\n\t\tassertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())\n\t\t\t\t.isEqualTo(\"https://repo.spring.io/snapshot/\");\n\t\tassertThat(((MavenArtifactRepository) repositories.get(3)).getUrl().toString())\n\t\t\t\t.isEqualTo(\"https://repo.spring.io/milestone/\");\n\t}\n\n\tprivate void assertMilestoneRepository(RepositoryHandler repositories) {\n\t\tassertThat(repositories).extracting(ArtifactRepository::getName).hasSize(4);\n\t\tassertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())\n\t\t\t\t.isEqualTo(\"https://repo.maven.apache.org/maven2/\");\n\t\tassertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())\n\t\t\t\t.isEqualTo(\"https://repo.spring.io/milestone/\");\n\t}\n\n\tprivate void assertReleaseRepository(RepositoryHandler repositories) {\n\t\tassertThat(repositories).extracting(ArtifactRepository::getName).hasSize(3);\n\t\tassertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())\n\t\t\t\t.isEqualTo(\"https://repo.maven.apache.org/maven2/\");\n\t\tassertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())\n\t\t\t\t.isEqualTo(\"https://repo.spring.io/release/\");\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/ShowcaseITest.java",
    "content": "package io.spring.gradle.convention;\n\nimport io.spring.gradle.TestKit;\nimport org.gradle.testkit.runner.BuildResult;\nimport org.gradle.testkit.runner.TaskOutcome;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class ShowcaseITest {\n\tprivate TestKit testKit;\n\n\t@BeforeEach\n\tvoid setup(@TempDir Path tempDir) {\n\t\tthis.testKit = new TestKit(tempDir.toFile());\n\t}\n\n    @Test\n\tpublic void build() throws Exception {\n        BuildResult result = this.testKit.withProjectResource(\"samples/showcase/\")\n\t\t\t\t.withArguments(\"build\", \"--stacktrace\")\n\t\t\t\t.forwardOutput()\n\t\t\t\t.build();\n\t\tassertThat(result.getOutput()).contains(\"BUILD SUCCESSFUL\");\n\t}\n\n\t@Test\n\t@Disabled\n    public void install() throws Exception {\n\t\tBuildResult result = this.testKit\n\t\t\t.withProjectResource(\"samples/showcase/\")\n\t\t\t.withArguments(\"install\", \"--stacktrace\")\n\t\t\t.build();\n\n        assertThat(result.getOutput()).contains(\"SUCCESS\");\n\n\t\tFile pom = new File(testKit.getRootDir(), \"sgbcs-core/build/poms/pom-default.xml\");\n\t\tassertThat(pom).exists();\n\n\t\tString pomText = new String(Files.readAllBytes(pom.toPath()));\n\t\tString pomTextNoSpace = pomText.replaceAll(\"\\\\s\", \"\");\n\n\t\tassertThat(pomText).doesNotContain(\"<dependencyManagement>\");\n\n\t\tassertThat(pomTextNoSpace).contains(\"<dependency>\\n\t\t\t<groupId>org.springframework</groupId>\\n\t\t\t<artifactId>spring-test</artifactId>\\n\t\t\t<scope>test</scope>\\n\t\t\t<version>4.3.6.RELEASE</version>\\n\t\t</dependency>\".replaceAll(\"\\\\s\", \"\"));\n\t\tassertThat(pomTextNoSpace).contains(\"<developers>\\n\t\t\t<developer>\\n\t\t\t\t<id>rwinch</id>\\n\t\t\t\t<name>Rob Winch</name>\\n\t\t\t\t<email>rwinch@pivotal.io</email>\\n\t\t\t</developer>\\n\t\t\t<developer>\\n\t\t\t\t<id>jgrandja</id>\\n\t\t\t\t<name>Joe Grandja</name>\\n\t\t\t\t<email>jgrandja@pivotal.io</email>\\n\t\t\t</developer>\\n\t\t</developers>\".replaceAll(\"\\\\s\", \"\"));\n\t\tassertThat(pomTextNoSpace).contains(\"<scm>\\n\t\t\t<connection>scm:git:git://github.com/spring-projects/spring-security</connection>\\n\t\t\t<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>\\n\t\t\t<url>https://github.com/spring-projects/spring-security</url>\\n\t\t</scm>\".replaceAll(\"\\\\s\", \"\"));\n\t\tassertThat(pomTextNoSpace).contains(\"<description>sgbcs-core</description>\");\n\t\tassertThat(pomTextNoSpace).contains(\"<url>https://spring.io/spring-security</url>\");\n\t\tassertThat(pomTextNoSpace).contains(\"<organization>\\n\t\t\t<name>spring.io</name>\\n\t\t\t<url>https://spring.io/</url>\\n\t\t</organization>\".replaceAll(\"\\\\s\", \"\"));\n\t\tassertThat(pomTextNoSpace).contains(\"\t<licenses>\\n\t\t\t<license>\\n\t\t\t\t<name>The Apache Software License, Version 2.0</name>\\n\t\t\t\t<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>\\n\t\t\t\t<distribution>repo</distribution>\\n\t\t\t</license>\\n\t\t</licenses>\".replaceAll(\"\\\\s\", \"\"));\n\t\tassertThat(pomTextNoSpace).contains(\"<scm>\\n\t\t\t<connection>scm:git:git://github.com/spring-projects/spring-security</connection>\\n\t\t\t<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>\\n\t\t\t<url>https://github.com/spring-projects/spring-security</url>\\n\t\t</scm>\".replaceAll(\"\\\\s\", \"\"));\n\n\t\tFile bom = new File(testKit.getRootDir(), \"bom/build/poms/pom-default.xml\");\n\t\tassertThat(bom).exists();\n\t\tassertThat(bom).hasContent(\"<artifactId>sgbcs-core</artifactId>\");\n\n\t\tBuildResult secondBuild = this.testKit.withProjectResource(\"samples/showcase/\").withArguments(\"mavenBom\", \"--stacktrace\").build();\n\t\t// mavenBom is not up to date since install is never up to date\n\t\tassertThat(result.task(\":bom:mavenBom\").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/SpringMavenPluginITest.java",
    "content": "package io.spring.gradle.convention;\n\nimport io.spring.gradle.TestKit;\nimport org.apache.commons.io.IOUtils;\nimport org.gradle.testkit.runner.BuildResult;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.LinkedHashMap;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class SpringMavenPluginITest {\n\n\tprivate TestKit testKit;\n\n\t@BeforeEach\n\tvoid setup(@TempDir Path tempDir) {\n\t\tthis.testKit = new TestKit(tempDir.toFile());\n\t}\n\n    @Disabled\n\t@Test\n    public void install()  throws Exception {\n        BuildResult result = this.testKit.withProjectResource(\"samples/maven/install\")\n\t\t\t\t.withArguments(\"install\")\n\t\t\t\t.build();\n\t\tassertThat(result.getOutput()).contains(\"SUCCESS\");\n\t\tFile pom = new File(testKit.getRootDir(), \"build/poms/pom-default.xml\");\n\t\tassertThat(pom).exists();\n\t\tString pomText = new String(Files.readAllBytes(pom.toPath()));\n\t\tassertThat(pomText.replaceAll(\"\\\\s\", \"\")).contains(\"<dependency>\\n\t\t\t<groupId>aopalliance</groupId>\\n\t\t\t<artifactId>aopalliance</artifactId>\\n\t\t\t<version>1.0</version>\\n\t\t\t<scope>compile</scope>\\n\t\t\t<optional>true</optional>\\n\t\t</dependency>\".replaceAll(\"\\\\s\", \"\"));\n\t}\n\n    @Disabled\n\t@Test\n    public void signArchivesWhenInMemory() throws Exception {\n        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(2);\n        map.put(\"ORG_GRADLE_PROJECT_signingKey\", getSigningKey());\n        map.put(\"ORG_GRADLE_PROJECT_signingPassword\", \"password\");\n        BuildResult result = this.testKit.withProjectResource(\"samples/maven/signing\")\n\t\t\t\t.withArguments(\"signArchives\")\n\t\t\t\t.withEnvironment(map)\n\t\t\t\t.forwardOutput()\n\t\t\t\t.build();\n\t\tassertThat(result.getOutput()).contains(\"SUCCESS\");\n\t\tfinal File jar = new File(testKit.getRootDir(), \"build/libs/signing-1.0.0.RELEASE.jar\");\n\t\tassertThat(jar).exists();\n\t\tFile signature = new File(jar.getAbsolutePath() + \".asc\");\n\t\tassertThat(signature).exists();\n\t}\n\n    public String getSigningKey() throws Exception {\n\t\treturn IOUtils.toString(getClass().getResource(\"/test-private.pgp\"));\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/TestsConfigurationPluginITest.java",
    "content": "package io.spring.gradle.convention;\n\nimport io.spring.gradle.TestKit;\nimport org.gradle.testkit.runner.BuildResult;\nimport org.gradle.testkit.runner.TaskOutcome;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport java.nio.file.Path;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestsConfigurationPluginITest {\n\n\tprivate TestKit testKit;\n\n\t@BeforeEach\n\tvoid setup(@TempDir Path tempDir) {\n\t\tthis.testKit = new TestKit(tempDir.toFile());\n\t}\n\n\t@Test\n    public void canFindDepencency() throws Exception {\n        BuildResult result = this.testKit.withProjectResource(\"samples/testsconfiguration\")\n\t\t\t\t.withArguments(\"check\")\n\t\t\t\t.build();\n\t\tassertThat(result.task(\":web:check\").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/io/spring/gradle/convention/UtilsTest.java",
    "content": "package io.spring.gradle.convention;\n\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.when;\n\nimport org.gradle.api.Project;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n@ExtendWith(MockitoExtension.class)\npublic class UtilsTest {\n\t@Mock\n\tProject project;\n\t@Mock\n\tProject rootProject;\n\n\t@Test\n\tpublic void getProjectName() {\n\t\twhen(project.getRootProject()).thenReturn(rootProject);\n\t\twhen(rootProject.getName()).thenReturn(\"spring-security\");\n\n\t\tassertThat(Utils.getProjectName(project)).isEqualTo(\"spring-security\");\n\t}\n\n\t@Test\n\tpublic void getProjectNameWhenEndsWithBuildThenStrippedOut() {\n\t\twhen(project.getRootProject()).thenReturn(rootProject);\n\t\twhen(rootProject.getName()).thenReturn(\"spring-security-build\");\n\n\t\tassertThat(Utils.getProjectName(project)).isEqualTo(\"spring-security\");\n\t}\n\n\t@Test\n\tpublic void isSnapshotValidWithDot() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0.BUILD-SNAPSHOT\");\n\n\t\tassertThat(Utils.isSnapshot(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isSnapshotValidWithNoBuild() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0-SNAPSHOT\");\n\n\t\tassertThat(Utils.isSnapshot(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isSnapshotValidWithDash() {\n\t\twhen(project.getVersion()).thenReturn(\"Theme-BUILD-SNAPSHOT\");\n\n\t\tassertThat(Utils.isSnapshot(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isSnapshotInvalid() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0.SNAPSHOT\");\n\n\t\tassertThat(Utils.isSnapshot(project)).isFalse();\n\t}\n\n\t@Test\n\tpublic void isMilestoneValidWithDot() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0.M1\");\n\n\t\tassertThat(Utils.isMilestone(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isMilestoneValidWithDash() {\n\t\twhen(project.getVersion()).thenReturn(\"Theme-M1\");\n\n\t\tassertThat(Utils.isMilestone(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isMilestoneValidWithNumberDash() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0-M1\");\n\n\t\tassertThat(Utils.isMilestone(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isMilestoneInvalid() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0.M\");\n\n\t\tassertThat(Utils.isMilestone(project)).isFalse();\n\t}\n\n\t@Test\n\tpublic void isReleaseCandidateValidWithDot() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0.RC1\");\n\n\t\tassertThat(Utils.isMilestone(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isReleaseCandidateValidWithNumberDash() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0-RC1\");\n\n\t\tassertThat(Utils.isMilestone(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isReleaseCandidateValidWithDash() {\n\t\twhen(project.getVersion()).thenReturn(\"Theme-RC1\");\n\n\t\tassertThat(Utils.isMilestone(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isReleaseCandidateInvalid() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0.RC\");\n\n\t\tassertThat(Utils.isMilestone(project)).isFalse();\n\t}\n\n\t@Test\n\tpublic void isReleaseValidWithDot() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0.RELEASE\");\n\n\t\tassertThat(Utils.isRelease(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isReleaseValidWithNoRelease() {\n\t\twhen(project.getVersion()).thenReturn(\"1.0.0\");\n\n\t\tassertThat(Utils.isRelease(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isReleaseValidWithDash() {\n\t\twhen(project.getVersion()).thenReturn(\"Theme-RELEASE\");\n\n\t\tassertThat(Utils.isRelease(project)).isTrue();\n\t}\n\n\t@Test\n\tpublic void isServiceReleaseValid() {\n\t\twhen(project.getVersion()).thenReturn(\"Theme-SR1\");\n\n\t\tassertThat(Utils.isRelease(project)).isTrue();\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/java/org/springframework/gradle/xsd/CreateVersionlessXsdTaskTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.gradle.xsd;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.gradle.xsd.CreateVersionlessXsdTask.MajorMinorVersion;\nimport org.springframework.gradle.xsd.CreateVersionlessXsdTask.XsdFileMajorMinorVersion;\n\nimport java.io.File;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\n\n/**\n * @author Rob Winch\n */\nclass CreateVersionlessXsdTaskTests {\n\n\t@Test\n\tvoid xsdCreateWhenValid() {\n\t\tFile file = new File(\"spring-security-2.0.xsd\");\n\t\tXsdFileMajorMinorVersion xsdFile = XsdFileMajorMinorVersion.create(file);\n\t\tassertThat(xsdFile).isNotNull();\n\t\tassertThat(xsdFile.getFile()).isEqualTo(file);\n\t\tassertThat(xsdFile.getVersion().getMajor()).isEqualTo(2);\n\t\tassertThat(xsdFile.getVersion().getMinor()).isEqualTo(0);\n\t}\n\n\t@Test\n\tvoid xsdCreateWhenPatchReleaseThenNull() {\n\t\tFile file = new File(\"spring-security-2.0.1.xsd\");\n\t\tXsdFileMajorMinorVersion xsdFile = XsdFileMajorMinorVersion.create(file);\n\t\tassertThat(xsdFile).isNull();\n\t}\n\n\t@Test\n\tvoid xsdCreateWhenNotXsdFileThenNull() {\n\t\tFile file = new File(\"spring-security-2.0.txt\");\n\t\tXsdFileMajorMinorVersion xsdFile = XsdFileMajorMinorVersion.create(file);\n\t\tassertThat(xsdFile).isNull();\n\t}\n\n\t@Test\n\tvoid xsdCreateWhenNotStartWithSpringSecurityThenNull() {\n\t\tFile file = new File(\"spring-securityNO-2.0.xsd\");\n\t\tXsdFileMajorMinorVersion xsdFile = XsdFileMajorMinorVersion.create(file);\n\t\tassertThat(xsdFile).isNull();\n\t}\n\n\t@Test\n\tvoid isGreaterWhenMajorLarger() {\n\t\tMajorMinorVersion larger = new MajorMinorVersion(2,0);\n\t\tMajorMinorVersion smaller = new MajorMinorVersion(1,0);\n\t\tassertThat(larger.isGreaterThan(smaller)).isTrue();\n\t\tassertThat(smaller.isGreaterThan(larger)).isFalse();\n\t}\n\n\t@Test\n\tvoid isGreaterWhenMinorLarger() {\n\t\tMajorMinorVersion larger = new MajorMinorVersion(1,1);\n\t\tMajorMinorVersion smaller = new MajorMinorVersion(1,0);\n\t\tassertThat(larger.isGreaterThan(smaller)).isTrue();\n\t\tassertThat(smaller.isGreaterThan(larger)).isFalse();\n\t}\n\n\t@Test\n\tvoid isGreaterWhenMajorAndMinorLarger() {\n\t\tMajorMinorVersion larger = new MajorMinorVersion(2,1);\n\t\tMajorMinorVersion smaller = new MajorMinorVersion(1,0);\n\t\tassertThat(larger.isGreaterThan(smaller)).isTrue();\n\t\tassertThat(smaller.isGreaterThan(larger)).isFalse();\n\t}\n\n\t@Test\n\tvoid isGreaterWhenSame() {\n\t\tMajorMinorVersion first = new MajorMinorVersion(1,0);\n\t\tMajorMinorVersion second = new MajorMinorVersion(1,0);\n\t\tassertThat(first.isGreaterThan(second)).isFalse();\n\t\tassertThat(second.isGreaterThan(first)).isFalse();\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/integrationtest/withgroovy/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.integration-test'\n}\n\napply plugin: 'java'\napply plugin: 'groovy'\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestCompile 'junit:junit:4.12'\n\ttestCompile 'org.spockframework:spock-core:1.0-groovy-2.4'\n\tintegrationTestCompile 'org.springframework:spring-core:4.3.7.RELEASE'\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/integrationtest/withgroovy/src/integration-test/groovy/sample/TheTest.groovy",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\npackage sample;\n\nimport org.springframework.core.Ordered;\nimport spock.lang.Specification;\n\nclass TheTest extends Specification {\n\tdef \"has Ordered\"() {\n\t\texpect: 'Loads Ordered fine'\n\t\tOrdered ordered = new Ordered() {\n\t\t\t@Override\n\t\t\tint getOrder() {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/integrationtest/withjava/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.integration-test'\n}\n\napply plugin: 'java'\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestCompile 'junit:junit:4.12'\n\tintegrationTestCompile 'org.springframework:spring-core:4.3.7.RELEASE'\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/integrationtest/withjava/src/integration-test/java/sample/TheTest.java",
    "content": "package sample;\n\nimport org.junit.Test;\nimport org.springframework.core.Ordered;\n\npublic class TheTest {\n\t@Test\n\tpublic void compilesAndRuns() {\n\t\tOrdered ordered = new Ordered() {\n\t\t\t@Override\n\t\t\tpublic int getOrder() {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t};\n\t}\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/integrationtest/withpropdeps/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.integration-test'\n}\n\napply plugin: 'java'\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\toptional 'jakarta.servlet:jakarta.servlet-api:5.0.0'\n\ttestCompile 'junit:junit:4.12'\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/integrationtest/withpropdeps/src/integration-test/java/sample/TheTest.java",
    "content": "package sample;\n\nimport org.junit.Test;\nimport jakarta.servlet.http.HttpServletRequest;\n\npublic class TheTest {\n\t@Test\n\tpublic void compilesAndRuns() {\n\t\tHttpServletRequest request = null;\n\t}\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/jacoco/java/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.jacoco'\n}\n\napply plugin: 'java'\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestCompile 'junit:junit:4.12'\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/jacoco/java/src/main/java/sample/TheClass.java",
    "content": "package sample;\n\npublic class TheClass {\n\tpublic boolean doStuff(boolean b) {\n\t\tif(b) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/jacoco/java/src/test/java/sample/TheClassTest.java",
    "content": "package sample;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\npublic class TheClassTest {\n\tTheClass theClass = new TheClass();\n\n\t@Test\n\tpublic void doStuffWhenTrueThenTrue() {\n\t\tassertTrue(theClass.doStuff(true));\n\t}\n\n\t@Test\n\tpublic void doStuffWhenTrueThenFalse() {\n\t\tassertFalse(theClass.doStuff(false));\n\t}\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/javadocapi/multimodule/api/build.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-module'"
  },
  {
    "path": "buildSrc/src/test/resources/samples/javadocapi/multimodule/api/src/main/java/sample/Api.java",
    "content": "package sample;\n\n/**\n * Testing this\n * @author Rob Winch\n *\n */\npublic class Api {\n\n\t/**\n\t * This does stuff\n\t */\n\tpublic void doStuff() {}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/javadocapi/multimodule/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.javadoc-api'\n\tid 'io.spring.convention.spring-module' apply false\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/javadocapi/multimodule/impl/build.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-module'"
  },
  {
    "path": "buildSrc/src/test/resources/samples/javadocapi/multimodule/impl/src/main/java/sample/Impl.java",
    "content": "package sample;\n\n/**\n * Testing this\n * @author Rob Winch\n *\n */\npublic class Impl {\n\n\t/**\n\t * This does stuff\n\t */\n\tpublic void otherThings() {}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/javadocapi/multimodule/sample/build.gradle",
    "content": "apply plugin: 'java'\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/javadocapi/multimodule/sample/src/main/java/sample/Sample.java",
    "content": "package sample;\n\n/**\n * Testing this\n * @author Rob Winch\n *\n */\npublic class Sample {\n\n\t/**\n\t * This does stuff\n\t */\n\tpublic void doSample() {}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/javadocapi/multimodule/settings.gradle",
    "content": "include ':api'\ninclude ':impl'\ninclude ':sample'"
  },
  {
    "path": "buildSrc/src/test/resources/samples/maven/install/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.root'\n}\n\napply plugin: 'io.spring.convention.maven'\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestCompile 'junit:junit:4.12'\n\toptional 'aopalliance:aopalliance:1.0'\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/maven/install-with-springio/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.spring-module'\n}\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestCompile 'junit:junit:4.12'\n\tcompile 'org.springframework:spring-core'\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/maven/install-with-springio/gradle/dependency-management.gradle",
    "content": "dependencyManagement {\n\tdependencies {\n\t\tdependency 'org.springframework:spring-core:3.0.0.RELEASE'\n\t}\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/maven/signing/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.root'\n}\n\nversion = \"1.0.0.RELEASE\"\n\napply plugin: 'io.spring.convention.maven'\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestCompile 'junit:junit:4.12'\n\toptional 'aopalliance:aopalliance:1.0'\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/maven/signing/settings.gradle",
    "content": "rootProject.name = 'signing'"
  },
  {
    "path": "buildSrc/src/test/resources/samples/maven/upload/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.root'\n}\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestCompile 'junit:junit:4.12'\n\toptional 'aopalliance:aopalliance:1.0'\n}\n\nuploadArchives {\n\trepositories {\n\t\tmavenDeployer {\n\t\t\trepository(url: \"file:$buildDir/repo\")\n\t\t}\n\t}\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/Jenkinsfile",
    "content": "parallel check: {\n\tstage('Check') {\n\t\tnode {\n\t\t\tcheckout scm\n\t\t\tsh \"./gradlew check  --refresh-dependencies --no-daemon\"\n\t\t}\n\t}\n},\nsonar: {\n\tstage('Sonar') {\n\t\tnode {\n\t\t\tcheckout scm\n\t\t\twithCredentials([string(credentialsId: 'spring-sonar.login', variable: 'SONAR_LOGIN')]) {\n\t\t\t\tsh \"./gradlew sonarqube -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon\"\n\t\t\t}\n\t\t}\n\t}\n},\nossrh: {\n\tstage('OSSRH Deploy') {\n\t\tnode {\n\t\t\tcheckout scm\n\t\t\twithCredentials([file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')]) {\n\t\t\t\twithCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) {\n\t\t\t\t\twithCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) {\n\t\t\t\t\t\tsh \"./gradlew uploadArchives -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password=$SIGNING_PASSWORD -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD  --refresh-dependencies --no-daemon\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n},\nschema: {\n\tstage('Deploy Schema') {\n\t\tnode {\n\t\t\tcheckout scm\n\t\t\twithCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {\n\t\t\t\tsh \"./gradlew deploySchema -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace\"\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/bom/bom.gradle",
    "content": "apply plugin: 'io.spring.convention.bom'\n\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.root'\n}\n\ngroup = \"org.springframework.build.test\"\nversion = \"1.0.0.BUILD-SNAPSHOT\"\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/etc/checkstyle/checkstyle.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE module PUBLIC \"-//Puppy Crawl//DTD Check Configuration 1.3//EN\"\n\t\t\"https://www.puppycrawl.com/dtds/configuration_1_3.dtd\">\n<module name=\"Checker\">\n</module>\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/settings.gradle",
    "content": "import java.util.regex.Matcher\n\nrootProject.name = 'spring-gradle-build-conventions-sample'\n\n\nFileTree projects = fileTree(rootDir) {\n\tinclude '**/*.gradle'\n\texclude '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*'\n}\n\nString rootDirPath = rootDir.absolutePath + File.separator\nprojects.each { File buildFile ->\n\tString buildFilePath = buildFile.parentFile.absolutePath\n\n\tString projectPath = buildFilePath.replace(rootDirPath, '').replaceAll(Matcher.quoteReplacement(File.separator), ':')\n\n\tinclude projectPath\n\n\tdef project = findProject(\":${projectPath}\")\n\tif(!'build.gradle'.equals(buildFile.name)) {\n\t\tproject.name = buildFile.name.replace('.gradle','')\n\t\tproject.buildFileName = buildFile.name\n\t}\n\tproject.projectDir = buildFile.parentFile\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-api/sgbcs-api.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tapi platform('org.springframework.boot:spring-boot-dependencies:2.5.2')\n\timplementation 'org.springframework:spring-web'\n\timplementation 'org.springframework:spring-core'\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-api/src/main/java/api/Api.java",
    "content": "package api;\n\n/**\n *\n * @author Rob Winch\n *\n */\npublic class Api {\n\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-api/src/test/java/api/ApiTest.java",
    "content": "package api;\n\nimport org.junit.jupiter.api.Test;\n\npublic class ApiTest {\n\n\t@Test\n\tpublic void api() {}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/sgbcs-core.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tapi platform('org.springframework.boot:spring-boot-dependencies:2.5.2')\n\toptional 'ch.qos.logback:logback-classic'\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/java/core/CoreClass.java",
    "content": "package core;\n\n/**\n *\n * @author Rob Winch\n *\n */\npublic class CoreClass {\n\n\tpublic void run() {\n\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/java/core/HasOptional.java",
    "content": "package core;\n\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n\npublic class HasOptional {\n\n\tpublic static void doStuffWithOptionalDependency() {\n\t\tLogger logger = LoggerFactory.getLogger(HasOptional.class);\n\t\tlogger.debug(\"This is optional\");\n\t}\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/META-INF/spring.handlers",
    "content": "http\\://www.springframework.org/schema/springgradlebuildsample=org.springframework.ldap.config.LdapNamespaceHandler"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/META-INF/spring.schemas",
    "content": "http\\://www.springframework.org/schema/springgradlebuildsample/spring-springgradlebuildsample.xsd=org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.2.xsd\nhttp\\://www.springframework.org/schema/springgradlebuildsample/spring-springgradlebuildsample-2.0.xsd=org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.0.xsd\nhttp\\://www.springframework.org/schema/springgradlebuildsample/spring-springgradlebuildsample-2.1.xsd=org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.1.xsd\nhttp\\://www.springframework.org/schema/springgradlebuildsample/spring-springgradlebuildsample-2.2.xsd=org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.2.xsd"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.0.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:ldap=\"http://www.springframework.org/schema/ldap\"\n           xmlns:repository=\"http://www.springframework.org/schema/data/repository\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/springgradlebuildsample\">\n\n    <xs:import namespace=\"http://www.springframework.org/schema/data/repository\"\n                schemaLocation=\"https://www.springframework.org/schema/data/repository/spring-repository.xsd\" />\n\n    <xs:attributeGroup name=\"context-source.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    A bean identifier, used for referring to the bean elsewhere in the context.\n                    &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"anonymous-read-only\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Defines whether read-only operations will be performed using an anonymous (unauthenticated) context.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"authentication-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the AuthenticationSource instance to use. If not specified, a SimpleAuthenticationSource will\n                    be used.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"authentication-strategy-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the DirContextAuthenticationStrategy instance to use. If not specified, a SimpleDirContextAuthenticationStrategy\n                    will be used.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The base DN. If configured, all LDAP operations on contexts retrieved from this ContextSource will\n                    be relative to this DN. Default is an empty distinguished name (i.e. all operations will be\n                    relative to the directory root).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"password\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The password to use for authentication.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"native-pooling\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specify whether native Java LDAP connection pooling should be used. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"referral\">\n            <xs:annotation>\n                <xs:documentation>\n                    Defines the strategy to handle referrals, as described on https://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html.\n                    Default is null.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"ignore\" />\n                    <xs:enumeration value=\"follow\" />\n                    <xs:enumeration value=\"throw\" />\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"url\" type=\"xs:string\" use=\"required\">\n            <xs:annotation>\n                <xs:documentation>\n                    URL of the LDAP server to use. If fail-over functionality is desired, more than one URL can\n                    be specified, separated using comma (,).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"username\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The username (principal) to use for authentication. This will normally be the distinguished name\n                    of an admin user.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"base-env-props-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Reference to a Map of custom environment properties that should supplied with the environment\n                    sent to the DirContext on construction.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:attributeGroup name=\"pooling.attlist\">\n        <xs:attribute name=\"max-active\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of active connections of each type (read-only|read-write)\n                    that can be allocated from the pool at the same time, or non-positive for no limit.\n                    Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-total\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The overall maximum number of active connections (for all types) that can be allocated from\n                    this pool at the same time, or non-positive for no limit. Default is -1 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-idle\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool,\n                    without extra ones being released, or non-positive for no limit. Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-idle\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum number of active connections of each type (read-only|read-write) that can remain\n                    idle in the pool, without extra ones being created, or zero to create none. Default is 0.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-wait\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of milliseconds that the pool will wait (when there are no available connections)\n                    for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.\n                    Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"when-exhausted\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies the behaviour when the pool is exhausted.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"FAIL\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Throw a NoSuchElementException when the pool is exhausted\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                    <xs:enumeration value=\"BLOCK\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Wait until a new object is available. If max-wait is positive a NoSuchElementException\n                                is thrown if no new object is available after the maxWait time expires.\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                    <xs:enumeration value=\"GROW\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Create and return a new object (essentially making maxActive meaningless).\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-borrow\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being borrowed from the pool.\n                    If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-return\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being returned to the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-while-idle\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated by the idle object evictor (if any).\n                    If an object fails to validate, it will be dropped from the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"eviction-run-interval-millis\" type=\"xs:int\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,\n                    no idle object evictor thread will be run. Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"tests-per-eviction-run\" type=\"xs:int\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of objects to examine during each run of the idle object evictor thread (if any).\n                    Default is 3.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-evictable-time-millis\" type=\"xs:int\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum amount of time an object may sit idle in the pool before it is eligible\n                    for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The base dn to use for validation searches. Default is LdapUtils.emptyPath().\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-filter\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The filter to use for validation queries. Default is (objectclass=*).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-search-controls-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"non-transient-exceptions\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"context-source\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates a ContextSource instance to be used to get LdapContexts for communicating with an LDAP server.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:sequence minOccurs=\"0\" maxOccurs=\"1\">\n                <xs:element name=\"pooling\">\n                    <xs:annotation>\n                        <xs:documentation>\n                            Defines the settings to use for the Spring LDAP connection pooling support.\n                        </xs:documentation>\n                    </xs:annotation>\n                    <xs:complexType>\n                        <xs:attributeGroup ref=\"ldap:pooling.attlist\" />\n                    </xs:complexType>\n                </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"ldap:context-source.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:attributeGroup name=\"ldap-template.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    A bean identifier, used for referring to the bean elsewhere in the context.\n                    Default is &quot;ldapTemplate&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"context-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ContextSource instance to use. Default is &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"count-limit\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default count limit for searches. Default is 0 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"time-limit\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default time limit for searches. Default is 0 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"search-scope\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default search scope for searches. Default is SUBTREE.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"OBJECT\" />\n                    <xs:enumeration value=\"ONELEVEL\" />\n                    <xs:enumeration value=\"SUBTREE\" />\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"ignore-name-not-found\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies whether NameNotFoundException should be ignored in searches. Setting this\n                    attribute to true will cause errors caused by invalid search base to be silently swallowed.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"ignore-partial-result\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies whether PartialResultException should be ignored in searches. Some LDAP servers\n                    have problems with referrals; these should normally be followed automatically, but if this\n                    doesn't work it will manifest itself with a PartialResultException. Setting this attribute\n                    to true presents a work-around to this problem. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"odm-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"ldap-template\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates an LdapTemplate instance.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:attributeGroup ref=\"ldap:ldap-template.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:attributeGroup name=\"transaction-manager.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of this instance. Default is &quot;transactionManager&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"context-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ContextSource instance to use. &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the DataSource instance to use.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"session-factory-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the Hibernate SessionFactory instance to use.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"transaction-manager\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates an ContextSourceTransactionManager. If data-source-ref or session-factory-ref is specified,\n                a DataSourceAndContextSourceTransactionManager/HibernateAndContextSourceTransactionManager will be\n                created.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:choice minOccurs=\"1\" maxOccurs=\"1\">\n                <xs:element name=\"default-renaming-strategy\">\n                    <xs:annotation>\n                        <xs:documentation>\n                            The default (simplistic) TempEntryRenamingStrategy. Please note that this\n                            strategy will not work for more advanced scenarios. See reference documentation\n                            for details.\n                        </xs:documentation>\n                    </xs:annotation>\n                    <xs:complexType>\n                        <xs:attribute name=\"temp-suffix\" type=\"xs:string\">\n                            <xs:annotation>\n                                <xs:documentation>\n                                    The default suffix that will be added to modified entries.\n                                    Default is &quot;_temp&quot;.\n                                </xs:documentation>\n                            </xs:annotation>\n                        </xs:attribute>\n                    </xs:complexType>\n                </xs:element>\n                <xs:element name=\"different-subtree-renaming-strategy\">\n                    <xs:annotation>\n                        <xs:documentation>\n                            TempEntryRenamingStrategy that moves the entry to a different subtree than\n                            the original entry.\n                        </xs:documentation>\n                    </xs:annotation>\n                    <xs:complexType>\n                        <xs:attribute name=\"subtree-node\" type=\"xs:string\" use=\"required\">\n                            <xs:annotation>\n                                <xs:documentation>\n                                    The subtree base where changed entries should be moved.\n                                </xs:documentation>\n                            </xs:annotation>\n                        </xs:attribute>\n                    </xs:complexType>\n                </xs:element>\n            </xs:choice>\n            <xs:attributeGroup ref=\"ldap:transaction-manager.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"repositories\">\n        <xs:complexType>\n            <xs:complexContent>\n                <xs:extension base=\"repository:repositories\">\n                    <xs:attribute name=\"ldap-template-ref\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                The reference to an LdapTemplate. Will default to 'ldapTemplate'.\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:attribute>\n                </xs:extension>\n            </xs:complexContent>\n        </xs:complexType>\n    </xs:element>\n</xs:schema>"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.1.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:ldap=\"http://www.springframework.org/schema/ldap\"\n           xmlns:repository=\"http://www.springframework.org/schema/data/repository\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/springgradlebuildsample\">\n\n    <xs:import namespace=\"http://www.springframework.org/schema/data/repository\"\n                schemaLocation=\"https://www.springframework.org/schema/data/repository/spring-repository.xsd\" />\n\n    <xs:attributeGroup name=\"context-source.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    A bean identifier, used for referring to the bean elsewhere in the context.\n                    &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"anonymous-read-only\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Defines whether read-only operations will be performed using an anonymous (unauthenticated) context.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"authentication-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the AuthenticationSource instance to use. If not specified, a SimpleAuthenticationSource will\n                    be used.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"authentication-strategy-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the DirContextAuthenticationStrategy instance to use. If not specified, a SimpleDirContextAuthenticationStrategy\n                    will be used.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The base DN. If configured, all LDAP operations on contexts retrieved from this ContextSource will\n                    be relative to this DN. Default is an empty distinguished name (i.e. all operations will be\n                    relative to the directory root).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"password\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The password to use for authentication.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"native-pooling\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specify whether native Java LDAP connection pooling should be used. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"referral\">\n            <xs:annotation>\n                <xs:documentation>\n                    Defines the strategy to handle referrals, as described on https://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html.\n                    Default is null.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"ignore\" />\n                    <xs:enumeration value=\"follow\" />\n                    <xs:enumeration value=\"throw\" />\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"url\" type=\"xs:string\" use=\"required\">\n            <xs:annotation>\n                <xs:documentation>\n                    URL of the LDAP server to use. If fail-over functionality is desired, more than one URL can\n                    be specified, separated using comma (,).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"username\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The username (principal) to use for authentication. This will normally be the distinguished name\n                    of an admin user.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"base-env-props-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Reference to a Map of custom environment properties that should supplied with the environment\n                    sent to the DirContext on construction.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:attributeGroup name=\"pooling.attlist\">\n        <xs:attribute name=\"max-active\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of active connections of each type (read-only|read-write)\n                    that can be allocated from the pool at the same time, or non-positive for no limit.\n                    Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-total\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The overall maximum number of active connections (for all types) that can be allocated from\n                    this pool at the same time, or non-positive for no limit. Default is -1 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-idle\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool,\n                    without extra ones being released, or non-positive for no limit. Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-idle\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum number of active connections of each type (read-only|read-write) that can remain\n                    idle in the pool, without extra ones being created, or zero to create none. Default is 0.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-wait\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of milliseconds that the pool will wait (when there are no available connections)\n                    for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.\n                    Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"when-exhausted\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies the behaviour when the pool is exhausted.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"FAIL\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Throw a NoSuchElementException when the pool is exhausted\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                    <xs:enumeration value=\"BLOCK\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Wait until a new object is available. If max-wait is positive a NoSuchElementException\n                                is thrown if no new object is available after the maxWait time expires.\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                    <xs:enumeration value=\"GROW\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Create and return a new object (essentially making maxActive meaningless).\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-borrow\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being borrowed from the pool.\n                    If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-return\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being returned to the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-while-idle\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated by the idle object evictor (if any).\n                    If an object fails to validate, it will be dropped from the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"eviction-run-interval-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,\n                    no idle object evictor thread will be run. Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"tests-per-eviction-run\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of objects to examine during each run of the idle object evictor thread (if any).\n                    Default is 3.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-evictable-time-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum amount of time an object may sit idle in the pool before it is eligible\n                    for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The base dn to use for validation searches. Default is LdapUtils.emptyPath().\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-filter\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The filter to use for validation queries. Default is (objectclass=*).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-search-controls-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"non-transient-exceptions\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:attributeGroup name=\"pooling2.attlist\">\n        <xs:attribute name=\"max-total\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The overall maximum number of active connections (for all types) that can be allocated from\n                    this pool at the same time, or non-positive for no limit. Default is -1 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-total-per-key\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The limit on the number of object instances allocated by the pool (checked out or idle),\n                    per key. When the limit is reached, the sub-pool is said to be exhausted. A negative value\n                    indicates no limit. Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-idle-per-key\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of active connections per type (read-only|read-write) that can remain idle in the pool,\n                    without extra ones being released, or non-positive for no limit. Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-idle-per-key\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum number of active connections per type (read-only|read-write) that can remain\n                    idle in the pool, without extra ones being created, or zero to create none. Default is 0.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-wait\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of milliseconds that the pool will wait (when there are no available connections)\n                    for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.\n                    Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"block-when-exhausted\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets to wait until a new object is available. If max-wait is positive a NoSuchElementException\n                    is thrown if no new object is available after the maxWait time expires..\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-create\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets whether objects created for the pool will be validated before borrowing. If the object\n                    fails to validate, then borrowing will fail. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-borrow\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being borrowed from the pool.\n                    If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-return\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being returned to the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-while-idle\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated by the idle object evictor (if any).\n                    If an object fails to validate, it will be dropped from the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"eviction-run-interval-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,\n                    no idle object evictor thread will be run. Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"tests-per-eviction-run\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of objects to examine during each run of the idle object evictor thread (if any).\n                    Default is 3.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-evictable-time-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum amount of time an object may sit idle in the pool before it is eligible\n                    for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"soft-min-evictable-idle-time-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum amount of time an object may sit idle in the pool before it is eligible for\n                    eviction by the idle object evictor, with the extra condition that at least minimum number\n                    of object instances per key remain in the pool. This settings is overridden by min-evictable-time-millis if\n                    it is set to a positive value. Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"eviction-policy-class\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The name of the eviction policy implementation that is used by this pool. The Pool will\n                    attempt to load the class using the thread context class loader. If that fails, the Pool\n                    will attempt to load the class using the class loader that loaded this class. Default is\n                    org.apache.commons.pool2.impl.DefaultEvictionPolicy.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"fairness\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets whether or not the pool serves threads waiting to borrow connections fairly.\n                    True means that waiting threads are served as if waiting in a FIFO queue. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"jmx-enable\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets whether JMX will be enabled with the platform MBean server for the pool. Default\n                    is true.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"jmx-name-base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The value of the JMX name base that will be used as part of the name assigned\n                    to JMX enabled pools. Default is null.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"jmx-name-prefix\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The value of the JMX name prefix that will be used as part of the name assigned\n                    to JMX enabled pools. Default value is pool.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"lifo\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets whether the pool has LIFO (last in, first out) behaviour with\n                    respect to idle objects - always returning the most recently used object\n                    from the pool, or as a FIFO (first in, first out) queue, where the pool\n                    always returns the oldest object in the idle object pool. Default is true.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The base dn to use for validation searches. Default is LdapUtils.emptyPath().\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-filter\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The filter to use for validation queries. Default is (objectclass=*).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-search-controls-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"non-transient-exceptions\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"context-source\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates a ContextSource instance to be used to get LdapContexts for communicating with an LDAP server.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:choice minOccurs=\"0\" maxOccurs=\"1\">\n                <xs:sequence>\n                    <xs:element name=\"pooling\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Defines the settings to use for the Spring LDAP connection pooling support.\n                            </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                            <xs:attributeGroup ref=\"ldap:pooling.attlist\" />\n                        </xs:complexType>\n                    </xs:element>\n                </xs:sequence>\n                <xs:sequence>\n                    <xs:element name=\"pooling2\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Defines the settings to use for the Spring LDAP connection pooling support based on commons-pool2 library.\n                            </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                            <xs:attributeGroup ref=\"ldap:pooling2.attlist\" />\n                        </xs:complexType>\n                    </xs:element>\n                </xs:sequence>\n            </xs:choice>\n            <xs:attributeGroup ref=\"ldap:context-source.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:attributeGroup name=\"ldap-template.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    A bean identifier, used for referring to the bean elsewhere in the context.\n                    Default is &quot;ldapTemplate&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"context-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ContextSource instance to use. Default is &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"count-limit\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default count limit for searches. Default is 0 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"time-limit\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default time limit for searches. Default is 0 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"search-scope\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default search scope for searches. Default is SUBTREE.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"OBJECT\" />\n                    <xs:enumeration value=\"ONELEVEL\" />\n                    <xs:enumeration value=\"SUBTREE\" />\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"ignore-name-not-found\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies whether NameNotFoundException should be ignored in searches. Setting this\n                    attribute to true will cause errors caused by invalid search base to be silently swallowed.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"ignore-partial-result\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies whether PartialResultException should be ignored in searches. Some LDAP servers\n                    have problems with referrals; these should normally be followed automatically, but if this\n                    doesn't work it will manifest itself with a PartialResultException. Setting this attribute\n                    to true presents a work-around to this problem. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"odm-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"ldap-template\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates an LdapTemplate instance.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:attributeGroup ref=\"ldap:ldap-template.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:attributeGroup name=\"transaction-manager.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of this instance. Default is &quot;transactionManager&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"context-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ContextSource instance to use. &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the DataSource instance to use.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"session-factory-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the Hibernate SessionFactory instance to use.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"transaction-manager\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates an ContextSourceTransactionManager. If data-source-ref or session-factory-ref is specified,\n                a DataSourceAndContextSourceTransactionManager/HibernateAndContextSourceTransactionManager will be\n                created.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:choice minOccurs=\"1\" maxOccurs=\"1\">\n                <xs:element name=\"default-renaming-strategy\">\n                    <xs:annotation>\n                        <xs:documentation>\n                            The default (simplistic) TempEntryRenamingStrategy. Please note that this\n                            strategy will not work for more advanced scenarios. See reference documentation\n                            for details.\n                        </xs:documentation>\n                    </xs:annotation>\n                    <xs:complexType>\n                        <xs:attribute name=\"temp-suffix\" type=\"xs:string\">\n                            <xs:annotation>\n                                <xs:documentation>\n                                    The default suffix that will be added to modified entries.\n                                    Default is &quot;_temp&quot;.\n                                </xs:documentation>\n                            </xs:annotation>\n                        </xs:attribute>\n                    </xs:complexType>\n                </xs:element>\n                <xs:element name=\"different-subtree-renaming-strategy\">\n                    <xs:annotation>\n                        <xs:documentation>\n                            TempEntryRenamingStrategy that moves the entry to a different subtree than\n                            the original entry.\n                        </xs:documentation>\n                    </xs:annotation>\n                    <xs:complexType>\n                        <xs:attribute name=\"subtree-node\" type=\"xs:string\" use=\"required\">\n                            <xs:annotation>\n                                <xs:documentation>\n                                    The subtree base where changed entries should be moved.\n                                </xs:documentation>\n                            </xs:annotation>\n                        </xs:attribute>\n                    </xs:complexType>\n                </xs:element>\n            </xs:choice>\n            <xs:attributeGroup ref=\"ldap:transaction-manager.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"repositories\">\n        <xs:complexType>\n            <xs:complexContent>\n                <xs:extension base=\"repository:repositories\">\n                    <xs:attribute name=\"ldap-template-ref\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                The reference to an LdapTemplate. Will default to 'ldapTemplate'.\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:attribute>\n                </xs:extension>\n            </xs:complexContent>\n        </xs:complexType>\n    </xs:element>\n</xs:schema>\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.2.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:ldap=\"http://www.springframework.org/schema/ldap\"\n           xmlns:repository=\"http://www.springframework.org/schema/data/repository\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/springgradlebuildsample\">\n\n    <xs:import namespace=\"http://www.springframework.org/schema/data/repository\"\n                schemaLocation=\"https://www.springframework.org/schema/data/repository/spring-repository.xsd\" />\n\n    <xs:attributeGroup name=\"context-source.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    A bean identifier, used for referring to the bean elsewhere in the context.\n                    &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"anonymous-read-only\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Defines whether read-only operations will be performed using an anonymous (unauthenticated) context.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"authentication-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the AuthenticationSource instance to use. If not specified, a SimpleAuthenticationSource will\n                    be used.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"authentication-strategy-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the DirContextAuthenticationStrategy instance to use. If not specified, a SimpleDirContextAuthenticationStrategy\n                    will be used.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The base DN. If configured, all LDAP operations on contexts retrieved from this ContextSource will\n                    be relative to this DN. Default is an empty distinguished name (i.e. all operations will be\n                    relative to the directory root).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"password\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The password to use for authentication.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"native-pooling\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specify whether native Java LDAP connection pooling should be used. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"referral\">\n            <xs:annotation>\n                <xs:documentation>\n                    Defines the strategy to handle referrals, as described on https://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html.\n                    Default is null.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"ignore\" />\n                    <xs:enumeration value=\"follow\" />\n                    <xs:enumeration value=\"throw\" />\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"url\" type=\"xs:string\" use=\"required\">\n            <xs:annotation>\n                <xs:documentation>\n                    URL of the LDAP server to use. If fail-over functionality is desired, more than one URL can\n                    be specified, separated using comma (,).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"username\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The username (principal) to use for authentication. This will normally be the distinguished name\n                    of an admin user.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"base-env-props-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Reference to a Map of custom environment properties that should supplied with the environment\n                    sent to the DirContext on construction.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:attributeGroup name=\"pooling.attlist\">\n        <xs:attribute name=\"max-active\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of active connections of each type (read-only|read-write)\n                    that can be allocated from the pool at the same time, or non-positive for no limit.\n                    Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-total\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The overall maximum number of active connections (for all types) that can be allocated from\n                    this pool at the same time, or non-positive for no limit. Default is -1 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-idle\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool,\n                    without extra ones being released, or non-positive for no limit. Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-idle\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum number of active connections of each type (read-only|read-write) that can remain\n                    idle in the pool, without extra ones being created, or zero to create none. Default is 0.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-wait\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of milliseconds that the pool will wait (when there are no available connections)\n                    for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.\n                    Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"when-exhausted\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies the behaviour when the pool is exhausted.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"FAIL\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Throw a NoSuchElementException when the pool is exhausted\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                    <xs:enumeration value=\"BLOCK\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Wait until a new object is available. If max-wait is positive a NoSuchElementException\n                                is thrown if no new object is available after the maxWait time expires.\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                    <xs:enumeration value=\"GROW\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Create and return a new object (essentially making maxActive meaningless).\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:enumeration>\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-borrow\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being borrowed from the pool.\n                    If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-return\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being returned to the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-while-idle\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated by the idle object evictor (if any).\n                    If an object fails to validate, it will be dropped from the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"eviction-run-interval-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,\n                    no idle object evictor thread will be run. Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"tests-per-eviction-run\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of objects to examine during each run of the idle object evictor thread (if any).\n                    Default is 3.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-evictable-time-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum amount of time an object may sit idle in the pool before it is eligible\n                    for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The base dn to use for validation searches. Default is LdapUtils.emptyPath().\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-filter\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The filter to use for validation queries. Default is (objectclass=*).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-search-controls-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"non-transient-exceptions\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:attributeGroup name=\"pooling2.attlist\">\n        <xs:attribute name=\"max-total\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The overall maximum number of active connections (for all types) that can be allocated from\n                    this pool at the same time, or non-positive for no limit. Default is -1 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-total-per-key\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The limit on the number of object instances allocated by the pool (checked out or idle),\n                    per key. When the limit is reached, the sub-pool is said to be exhausted. A negative value\n                    indicates no limit. Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-idle-per-key\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of active connections per type (read-only|read-write) that can remain idle in the pool,\n                    without extra ones being released, or non-positive for no limit. Default is 8.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-idle-per-key\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum number of active connections per type (read-only|read-write) that can remain\n                    idle in the pool, without extra ones being created, or zero to create none. Default is 0.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"max-wait\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The maximum number of milliseconds that the pool will wait (when there are no available connections)\n                    for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.\n                    Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"block-when-exhausted\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets to wait until a new object is available. If max-wait is positive a NoSuchElementException\n                    is thrown if no new object is available after the maxWait time expires. Default is true.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-create\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets whether objects created for the pool will be validated before borrowing. If the object\n                    fails to validate, then borrowing will fail. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-borrow\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being borrowed from the pool.\n                    If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-on-return\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated before being returned to the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"test-while-idle\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    The indication of whether objects will be validated by the idle object evictor (if any).\n                    If an object fails to validate, it will be dropped from the pool.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"eviction-run-interval-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,\n                    no idle object evictor thread will be run. Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"tests-per-eviction-run\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The number of objects to examine during each run of the idle object evictor thread (if any).\n                    Default is 3.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"min-evictable-time-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum amount of time an object may sit idle in the pool before it is eligible\n                    for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"soft-min-evictable-idle-time-millis\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The minimum amount of time an object may sit idle in the pool before it is eligible for\n                    eviction by the idle object evictor, with the extra condition that at least minimum number\n                    of object instances per key remain in the pool. This settings is overridden by min-evictable-time-millis if\n                    it is set to a positive value. Default is -1.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"eviction-policy-class\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The name of the eviction policy implementation that is used by this pool. The Pool will\n                    attempt to load the class using the thread context class loader. If that fails, the Pool\n                    will attempt to load the class using the class loader that loaded this class. Default is\n                    org.apache.commons.pool2.impl.DefaultEvictionPolicy.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"fairness\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets whether or not the pool serves threads waiting to borrow connections fairly.\n                    True means that waiting threads are served as if waiting in a FIFO queue. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"jmx-enable\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets whether JMX will be enabled with the platform MBean server for the pool. Default\n                    is true.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"jmx-name-base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The value of the JMX name base that will be used as part of the name assigned\n                    to JMX enabled pools. Default is null.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"jmx-name-prefix\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The value of the JMX name prefix that will be used as part of the name assigned\n                    to JMX enabled pools. Default value is pool.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"lifo\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Sets whether the pool has LIFO (last in, first out) behaviour with\n                    respect to idle objects - always returning the most recently used object\n                    from the pool, or as a FIFO (first in, first out) queue, where the pool\n                    always returns the oldest object in the idle object pool. Default is true.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-base\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The base dn to use for validation searches. Default is LdapUtils.emptyPath().\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-filter\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    The filter to use for validation queries. Default is (objectclass=*).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"validation-query-search-controls-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"non-transient-exceptions\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;\n                    countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"context-source\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates a ContextSource instance to be used to get LdapContexts for communicating with an LDAP server.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:choice minOccurs=\"0\" maxOccurs=\"1\">\n                <xs:sequence>\n                    <xs:element name=\"pooling\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Defines the settings to use for the Spring LDAP connection pooling support.\n                            </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                            <xs:attributeGroup ref=\"ldap:pooling.attlist\" />\n                        </xs:complexType>\n                    </xs:element>\n                </xs:sequence>\n                <xs:sequence>\n                    <xs:element name=\"pooling2\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                Defines the settings to use for the Spring LDAP connection pooling support based on commons-pool2 library.\n                            </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                            <xs:attributeGroup ref=\"ldap:pooling2.attlist\" />\n                        </xs:complexType>\n                    </xs:element>\n                </xs:sequence>\n            </xs:choice>\n            <xs:attributeGroup ref=\"ldap:context-source.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:attributeGroup name=\"ldap-template.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    A bean identifier, used for referring to the bean elsewhere in the context.\n                    Default is &quot;ldapTemplate&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"context-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ContextSource instance to use. Default is &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"count-limit\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default count limit for searches. Default is 0 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"time-limit\" type=\"xs:integer\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default time limit for searches. Default is 0 (no limit).\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"search-scope\">\n            <xs:annotation>\n                <xs:documentation>\n                    The default search scope for searches. Default is SUBTREE.\n                </xs:documentation>\n            </xs:annotation>\n            <xs:simpleType>\n                <xs:restriction base=\"xs:token\">\n                    <xs:enumeration value=\"OBJECT\" />\n                    <xs:enumeration value=\"ONELEVEL\" />\n                    <xs:enumeration value=\"SUBTREE\" />\n                </xs:restriction>\n            </xs:simpleType>\n        </xs:attribute>\n        <xs:attribute name=\"ignore-name-not-found\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies whether NameNotFoundException should be ignored in searches. Setting this\n                    attribute to true will cause errors caused by invalid search base to be silently swallowed.\n                    Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"ignore-partial-result\" type=\"xs:boolean\">\n            <xs:annotation>\n                <xs:documentation>\n                    Specifies whether PartialResultException should be ignored in searches. Some LDAP servers\n                    have problems with referrals; these should normally be followed automatically, but if this\n                    doesn't work it will manifest itself with a PartialResultException. Setting this attribute\n                    to true presents a work-around to this problem. Default is false.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"odm-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"ldap-template\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates an LdapTemplate instance.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:attributeGroup ref=\"ldap:ldap-template.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:attributeGroup name=\"transaction-manager.attlist\">\n        <xs:attribute name=\"id\" type=\"xs:string\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of this instance. Default is &quot;transactionManager&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"context-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the ContextSource instance to use. &quot;contextSource&quot;.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the DataSource instance to use.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n        <xs:attribute name=\"session-factory-ref\" type=\"xs:token\">\n            <xs:annotation>\n                <xs:documentation>\n                    Id of the Hibernate SessionFactory instance to use.\n                </xs:documentation>\n            </xs:annotation>\n        </xs:attribute>\n    </xs:attributeGroup>\n\n    <xs:element name=\"transaction-manager\">\n        <xs:annotation>\n            <xs:documentation>\n                Creates an ContextSourceTransactionManager. If data-source-ref or session-factory-ref is specified,\n                a DataSourceAndContextSourceTransactionManager/HibernateAndContextSourceTransactionManager will be\n                created.\n            </xs:documentation>\n        </xs:annotation>\n        <xs:complexType>\n            <xs:choice minOccurs=\"1\" maxOccurs=\"1\">\n                <xs:element name=\"default-renaming-strategy\">\n                    <xs:annotation>\n                        <xs:documentation>\n                            The default (simplistic) TempEntryRenamingStrategy. Please note that this\n                            strategy will not work for more advanced scenarios. See reference documentation\n                            for details.\n                        </xs:documentation>\n                    </xs:annotation>\n                    <xs:complexType>\n                        <xs:attribute name=\"temp-suffix\" type=\"xs:string\">\n                            <xs:annotation>\n                                <xs:documentation>\n                                    The default suffix that will be added to modified entries.\n                                    Default is &quot;_temp&quot;.\n                                </xs:documentation>\n                            </xs:annotation>\n                        </xs:attribute>\n                    </xs:complexType>\n                </xs:element>\n                <xs:element name=\"different-subtree-renaming-strategy\">\n                    <xs:annotation>\n                        <xs:documentation>\n                            TempEntryRenamingStrategy that moves the entry to a different subtree than\n                            the original entry.\n                        </xs:documentation>\n                    </xs:annotation>\n                    <xs:complexType>\n                        <xs:attribute name=\"subtree-node\" type=\"xs:string\" use=\"required\">\n                            <xs:annotation>\n                                <xs:documentation>\n                                    The subtree base where changed entries should be moved.\n                                </xs:documentation>\n                            </xs:annotation>\n                        </xs:attribute>\n                    </xs:complexType>\n                </xs:element>\n            </xs:choice>\n            <xs:attributeGroup ref=\"ldap:transaction-manager.attlist\" />\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"repositories\">\n        <xs:complexType>\n            <xs:complexContent>\n                <xs:extension base=\"repository:repositories\">\n                    <xs:attribute name=\"ldap-template-ref\">\n                        <xs:annotation>\n                            <xs:documentation>\n                                The reference to an LdapTemplate. Will default to 'ldapTemplate'.\n                            </xs:documentation>\n                        </xs:annotation>\n                    </xs:attribute>\n                </xs:extension>\n            </xs:complexContent>\n        </xs:complexType>\n    </xs:element>\n</xs:schema>\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/test/java/core/CoreClassTest.java",
    "content": "package core;\n\nimport org.junit.jupiter.api.Test;\n\npublic class CoreClassTest {\n\n\t@Test\n\tpublic void test() {\n\t\tnew CoreClass().run();\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/test/java/core/HasOptionalTest.java",
    "content": "package core;\n\nimport org.junit.jupiter.api.Test;\n\npublic class HasOptionalTest {\n\n\t@Test\n\tpublic void test() {\n\t\tHasOptional.doStuffWithOptionalDependency();\n\t}\n\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-docs/sgbcs-docs.gradle",
    "content": "apply plugin: 'java'\napply plugin: 'io.spring.convention.docs'\n\nversion = \"1.0.0.BUILD-SNAPSHOT\"\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/docinfo.html",
    "content": "<script src=\"//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.js\"></script>\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/index.adoc",
    "content": "= Example Manual\nDoc Writer <doc.writer@example.org>\n2014-09-09\n:example-caption!:\nifndef::imagesdir[:imagesdir: images]\nifndef::sourcedir[:sourcedir: ../java]\n\nThis is a user manual for an example project.\n\n== Introduction\n\nThis project does something.\nWe just haven't decided what that is yet.\n\n== Source Code\n\n[source,java]\n.Java code from project\n----\ninclude::{sourcedir}/example/StringUtils.java[tags=contains,indent=0]\n----\n\nThis page was built by the following command:\n\n $ ./gradlew asciidoctor\n\n== Images\n\n[.thumb]\nimage::sunset.jpg[scaledwidth=75%]\n\n== Attributes\n\n.Built-in\nasciidoctor-version:: {asciidoctor-version}\nsafe-mode-name:: {safe-mode-name}\ndocdir:: {docdir}\ndocfile:: {docfile}\nimagesdir:: {imagesdir}\nrevnumber:: {revnumber}\n\n.Custom\nsourcedir:: {sourcedir}\nendpoint-url:: {endpoint-url}\n\n== Includes\n\n.include::subdir/_b.adoc[]\n====\ninclude::subdir/_b.adoc[]\n====\n\nWARNING: Includes can be tricky!\n\n== build.gradle\n\n[source,groovy]\n----\ninclude::{build-gradle}[]\n----\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/subdir/_b.adoc",
    "content": "content from _src/docs/asciidoc/subdir/_b.adoc_.\n\n.include::_c.adoc[]\n[example]\n--\ninclude::_c.adoc[]\n--\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/subdir/_c.adoc",
    "content": "content from _src/docs/asciidoc/subdir/c.adoc_.\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/main/java/example/StringUtils.java",
    "content": "package example;\n\npublic class StringUtils {\n    // tag::contains[]\n    public boolean contains(String haystack, String needle) {\n        return haystack.contains(needle);\n    }\n    // end::contains[]\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/testsconfiguration/build.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.tests-configuration'\n\tid 'java'\n}\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/testsconfiguration/core/build.gradle",
    "content": "apply plugin: 'io.spring.convention.tests-configuration'\napply plugin: 'java' // second to test ordering\n"
  },
  {
    "path": "buildSrc/src/test/resources/samples/testsconfiguration/core/src/test/java/sample/Dependency.java",
    "content": "package sample;\n\npublic class Dependency {}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/testsconfiguration/settings.gradle",
    "content": "include ':core'\ninclude ':web'"
  },
  {
    "path": "buildSrc/src/test/resources/samples/testsconfiguration/web/build.gradle",
    "content": "apply plugin: 'java'\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestImplementation project(path: ':core', configuration: 'tests')\n\ttestImplementation 'junit:junit:4.12'\n}"
  },
  {
    "path": "buildSrc/src/test/resources/samples/testsconfiguration/web/src/test/java/sample/DependencyTest.java",
    "content": "package sample;\n\nimport org.junit.*;\n\npublic class DependencyTest {\n\t@Test\n\tpublic void findsDependencyOnClasspath() {\n\t\tnew Dependency();\n\t}\n}"
  },
  {
    "path": "buildSrc/src/test/resources/test-private.pgp",
    "content": "-----BEGIN PGP PRIVATE KEY BLOCK-----\n\nlQWGBF7sxWEBDADekNzn2KDfbb8QGPTHZLzqvNgfYVGXIWLFfEWsA0wTPn3YQh78\nvhsK+nq598R5Rjt2s4lm/L8y5eQ4GWpok9wu5gna4s+nHbdwDjJKoXA/GVN8Y/oi\ng37CafIqWACdzGpN5fjvblsfrNjVmwLdgq2kYoYqduOtiFeeQDivRJdZp3417e3C\nxAdarksMkWOuDKD7JQU46ofxoMX5+WsvZ0DYuKybXAiVhNTpn3rl/4MIvu6XlMBS\nEdLCdQkrdiSs6dBt8iQWkSDmwl4qaFyOmtlwjtzpJ+mPYpTWWWN3ukBRCM/AJRqm\nIgLXnYnrvTmEmuZwfcZrLGwPDa30aaCK5Jjq4WLD1lwpFBuZMT46saLA+y8CvcSW\n8vPAvQFzSrbBaEGu4ZIpU2aWBRW4JYpN+l90RFoWfdPyxrQ4rmPp4BIoEG25+zxI\n8XMJNwr4T/t8C4I6YQfCKqnyeeR94ZF8JjGJh3Ts6nLHDN84IZbeJRypvYpcTPAD\nHZiWnk4QREuSyhEAEQEAAf4HAwLsRiB8Oo4Nef+LrgBEtQ86fdfs5mTbxPv+XNGb\nWugl7HtIbgjhcmDw1zaWt9B7PKhSn2FQwJQduijE/Ae7OmNkFBQoeUeN0QADlarA\nXb5dlANIbfEJk/9KR769YL9HTy6y25LxrfgH3mrV2848dA/ilgv/WGmAz0SGER1t\nOKvgOfGrmQECKbw7+U9EyEPl7nWRgJrkRK8/zgMnAA1TuXG3Rr3FvuUlw51MnBxx\nsYOj6A7Xk0ijvh1szMwcUwVtZWDAiRFVhMcgopf7jCKs1UlrWJ98sa7WNXje25Mo\nWXZ8VRA1/FbM/ifzuICOmKc7C+rNff7H3U7PBwRCGJrG6m0EJ/Cwrko5uHVq+fG5\ns2Vl7817ztM3/rkmNkC9jmE/sKHlkPlVc9hZGPTbqBC6BThpbBzNLUoTPmAgzbqU\nhgW2ES938D7ipt7IzhRxTitQN8a1JRk8v710g/D2skLhTx9T3SkJyJkXbmOVJJXc\nIN/mtlzRykR/c/TGOsMQR6v+nz42jZ1AY406AKMjd1R15wamscg4d457qVxn89T0\nwne+eShON8vPNpu1bAaN8soHxC5a4eVnLNVguxs+Abc0x/qwpwvd0VOMd86Aukte\nYsS+fzGSSpX9PqHVNThK9Z1dU13J7RONeO+2xZnThhiMyGvpqL5PQ3lE3P4zkccB\njlQuuHW/6jD0/W1tlPPcsTcWDS4Ku7xxXg+ZSR3LfC4ukBWQLGiYNBWA2Zi1zW0A\nxXB9kdP9MJcxdBlsUa+xVrI71LcWaREGA/O5KGMmTXQaJJNvp/2bCauAIE8AJEN9\nMrGLe+SBu3n0PJxpCB3iqLXNe9nebf0I4E8uisKB6zcQMFMEWxI5R3oRweVKYIBo\n8Le5tlJ9DSwvwTI0tyDMW7Y9KCrAcIpz7tx0cIn4RzMGzCtDaLpwDk973jUQcFeE\nOQBLCWPm2pIuoKO4RkIHaqhYXc9SYG3E+qQ481DohAoB/Gr3Vf80wQyIGAorg4wM\nOhYmFp5pl8XDDpa7Sth8w/0flA08IRA466IYaBAhuP0ug+rhMjNfLwy+anurUd9f\nM3UD21WqIUEscN5SkPI2OhGCXf5rbAQ+PkyrbgHXZpW29fGeUrAEP7FBWnCxylvL\nijP/camoJLlOzr5CKi1TTkhitbfSMH1jQosw6JeiHtbnvCyrqNbPYw57gkpRH4FD\nIql0TGAiHRe8l4s0xW79oiqN6vWKICCkekwcuc3NdNFsyxjtPdWI3ni0FulkHX0Y\nO+R0Ge+uwoPKiwCT6POzn1sPP6q8kbcvP0QmkNAx7b/JKRPEoe4u9oUNc5qS9gEC\nGxzvbXqq7Zv7UmYcTfNSjEWluIy0SYkVB7QkU3ByaW5nICh0ZXN0aW5nKSA8bm9y\nZXBseUBzcHJpbmcuaW8+iQHOBBMBCgA4FiEEkkystQ8WdKVRsEMQjGaXsjMvWqAF\nAl7sxWECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQjGaXsjMvWqCNTAwA\npg4/XCj0vqdeVNepw4p4mU/62yu2pKXh5usU2BcEvD8dEidUpGRJkyt5n1vWzvC6\n8pbmn+Qa/6QKyL300RHeb3lpKQXbrtx3WWAMIO/JXh5lLtNaytw/N1IpqIVpg85G\nL27sjeGwInQ4MleLzKykxygIsqZYUE3KWCb75JmgtEQiyaivAmDxlFClaav35LQp\naAyX2jrRPxPcD3qEL8JwDRpIlYwi1rcJgun9HIPAdma4AIarBBlEmEcXUus4rNAl\n5vVgzB+v6dH3V+TiBL2QpepSoj4snA0iVyxGmLhhuuLf5gdb5sQgBY+BVxmqEji/\nis9/Steis5d25JG5G2qaflv5H00aZBJeCsARKkhBMKqBILHAAkBdr611gGLcHusq\nT4GdhYx4BJK1mHjgY08pDV+My4xnwz1wcsS3iAl7efCu8X2lfe9ju+sV1EhXAtj0\nXAIaHz1k8bN4dPZDZG8Lk+2kH6WDLr7EOIsJCCsBp4HUFrxUUEdYaUsNDFxKxmmA\nnQWGBF7sxWEBDADRHc69S/XH4yDv7Msg7OWW2eEBKkFV7i6lMHCp+lNBqNtxx83Z\nww8BRzqJZqvPRw219hwsVw7XN9YB1c8k5bw14mPx/VMK84oChPKRF58K9Ak8hyV5\n3BSrd4N+DKPYut0l+WhHTfkPIguFGeHp8Kg/GphAlK2eieE8vzrwrUZdNqWCBbuF\n+JqU84g9XIMUtfPSIwbnaRh6DkU59cbEaHYl4ltr5+ZExhHypMKt4G0sJu3Vo1iq\nnA4NiB1rUwzsvB5NUvTErLDKHscdeKSfbf7VeMe/Oqaa5EHWQuDVWBMDhyqRt4bV\nkdHnHogjtNT8Ose2eYgqY+8rzJ1vs84DUuo6Osd/ATc0K6jodZpueYN1NQEP9l2s\nVsjJzT4rFb6RUglnEyp4LANrz4ogkx9Y9wnvti0Z3vtLQyAf/DmXBKXzos5lRLBw\nJu+zqUUe6fww11LaftKdSI9yzhAP2ZnZFZ1FtVvuypLBrhZVsiYyRnDN6XhPxJgq\n2A6yIoTNaq/xIA0AEQEAAf4HAwJBDiLQbcpQ8v9Dq9OdYRBZQxhMbhpVB45r8BeR\nobnNmZWHRrZcQ7xqaqqWLEb8dtqvAR6lo7c6uZNSpzW59s72HCCvNLm8W8J6iMIb\noPD5Idbk1a0YXETUQOO5MP4NGzb/g6MInXhMM3TeC66YeZecXHiWwdDYk9nSiH7Q\n207vKvDYCxNC1wIV8UqPI7ck0ekAczJB9PlpFDXD2W0JX/JB0hnBAIKKEsVshPEv\nFKlDXOrjx2fKV1kzrB9fSewfaKW5MrqWEEElikPwoo5o+mv57ClkhE6TmGqPSA+t\n4kuTOlQwtVwlnLn0n0uyagRDs1eFUFuNXhFlAPAHbSMQeEVboYn7m9h7MYlVJwIT\n4N/8w2cEnjAd/xX/O1maTxI/7MXTboCCE6j9NmLHWTX3MM/xhO248zhXbttwUkxf\n2amL6QhsQiGtoyFKjhQtVTH8VVik05caEkWBOzKfldEVBrky8bacemW2EQp0uNAt\n7IOwzv9uUuWOUd8yAyDb7rI4+JrYsUwFLFk49zFZrYwrn30WvaWTEkRRgL+xGfjC\nW4ZLa8OmOO8O6C2sbTZyyCN0noG2IUGdIsIEhVFPIoqyqGlZ8IxF6EovZniz4sf8\nLu0Fo8YN+5LMiS9hCXs2Guv0jaSQ3vJtxU00/hT1zCc7/x9P1G6ks79r4aKJYCwS\nc/nAnT87YrXfQS3Zqa50HkbGqey7TwzwyrexC+eXgYvNhvXElG78hpPDhs/cyMcT\nApQNy3jQcXbdXeVL9AfEl6DmDR/XWtWOpz9Cpxz321IT8t6j8ZOdan1F5rgVfNbv\nz7lx6AmI5GVZlTA2DBQGjmSHcfHbvo9EPcodALdSvAqwsSmuF72bJBYnkdyiO/6A\n/ElYpcHv3X0NRyC93hkipnyUkoBkyWEeDJb69w9tUCleqA4Rd6TuicEIep010lMT\ntbzy8wHAciN716PzfJPsnZBB2Mv9sXCzpbbZjT8TNmz6/O0d/a+LNaSCiTvjwqNd\nERvWTgp6f6kcBQZFDSzZIMN9SHNaQAHAFOFVBA0IQfelcSz/bFz21Z9f0ZQ8b3ML\nFUMhx59D5Fcrvqn+5D6m8bAXw9gElRmUzi8BKyQay7JkHsQauS66yUrj2EEp94ch\n8WdbE/zTNxbUWkIJxYg36EIYgrH8zf7/M00+tXk6HMZRy0wLbbJqQEh5tDV9Ht8z\n+Eu3x6/VKGIFjhtIhVY7ZDCM9wFZjsbq/kQDT8PB9X9wt5o/quDo8wg8Zm0qnJkD\nmsPMXLh2ZVvKXiFM7WkhUYTOpxApOt+/jMGSqP2peCBqVIwQTdtQQ+wJZjx5sBmz\n2eXKwEu+pmsRAsM3dtPuqXpJWUzrrcuI+okBtgQYAQoAIBYhBJJMrLUPFnSlUbBD\nEIxml7IzL1qgBQJe7MVhAhsMAAoJEIxml7IzL1qg9YsMAJXUf1+CJd5mVkOZ551+\nINV3eIf+r5wXO7KoiK8CBUEnAqSNMrQ7QHTXwo9pSjuWR1O5JcRJumvZg/dj4Vox\ntb/l26Y5gdyYVkzwjKA6OnuHmICB3Y6xZVNrq1FUMiDThytHbuJz7mZZugJ69lMV\nITA0iKJV+nFNP7slthTSpfP0XkQ74hnteWf+HadXU11MHFEw2Doi2xANMxzoQgy9\n8uY6tp1/07Ll/Te5Y6YB1dlXrHiuJcX7/nUvmNa13y1cq28W2fqVsNVmZPZB7/SK\nDNyv6OT0iSIOBH/6AwoTzWo+Rcwr9PDKnII2fxizc3Jq75zjA+7F/Ol8fyaVrIbn\n8oxh88rujnORKevIextgcrAlu+Q8dnspZ9oACqoKQM/W+mVb5ISr9Xf+qnicXWem\nuGi6ofVHUZzzJsVRdrASSA6B2+Aup+PP+SuxFSok02/DkxkD2zgT8Xt/T+/usBsy\nc9LeCkYBwNlcZZc7jWAJZ6Tt514F4wmJgKFgiuZ6MqBrRQ==\n=1LV7\n-----END PGP PRIVATE KEY BLOCK-----"
  },
  {
    "path": "cas/spring-security-cas.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n\tid 'compile-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi project(':spring-security-web')\n\tapi 'org.apereo.cas.client:cas-client-core'\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-web'\n\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\toptional 'tools.jackson.core:jackson-databind'\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(path : ':spring-security-web', configuration : 'tests')\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation 'org.skyscreamer:jsonassert'\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/SamlServiceProperties.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas;\n\n/**\n * Sets the appropriate parameters for CAS's implementation of SAML (which is not\n * guaranteed to be actually SAML compliant).\n *\n * @author Scott Battaglia\n * @since 3.0\n */\npublic final class SamlServiceProperties extends ServiceProperties {\n\n\tpublic static final String DEFAULT_SAML_ARTIFACT_PARAMETER = \"SAMLart\";\n\n\tpublic static final String DEFAULT_SAML_SERVICE_PARAMETER = \"TARGET\";\n\n\tpublic SamlServiceProperties() {\n\t\tsuper.setArtifactParameter(DEFAULT_SAML_ARTIFACT_PARAMETER);\n\t\tsuper.setServiceParameter(DEFAULT_SAML_SERVICE_PARAMETER);\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/ServiceProperties.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.util.Assert;\n\n/**\n * Stores properties related to this CAS service.\n * <p>\n * Each web application capable of processing CAS tickets is known as a service. This\n * class stores the properties that are relevant to the local CAS service, being the\n * application that is being secured by Spring Security.\n *\n * @author Ben Alex\n */\npublic class ServiceProperties implements InitializingBean {\n\n\tpublic static final String DEFAULT_CAS_ARTIFACT_PARAMETER = \"ticket\";\n\n\tpublic static final String DEFAULT_CAS_SERVICE_PARAMETER = \"service\";\n\n\tprivate @Nullable String service;\n\n\tprivate boolean authenticateAllArtifacts;\n\n\tprivate boolean sendRenew = false;\n\n\tprivate String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;\n\n\tprivate String serviceParameter = DEFAULT_CAS_SERVICE_PARAMETER;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasLength(this.service, \"service cannot be empty.\");\n\t\tAssert.hasLength(this.artifactParameter, \"artifactParameter cannot be empty.\");\n\t\tAssert.hasLength(this.serviceParameter, \"serviceParameter cannot be empty.\");\n\t}\n\n\t/**\n\t * Represents the service the user is authenticating to.\n\t * <p>\n\t * This service is the callback URL belonging to the local Spring Security System for\n\t * Spring secured application. For example,\n\t *\n\t * <pre>\n\t * https://www.mycompany.com/application/login/cas\n\t * </pre>\n\t * @return the URL of the service the user is authenticating to\n\t */\n\tpublic final @Nullable String getService() {\n\t\treturn this.service;\n\t}\n\n\t/**\n\t * Indicates whether the <code>renew</code> parameter should be sent to the CAS login\n\t * URL and CAS validation URL.\n\t * <p>\n\t * If <code>true</code>, it will force CAS to authenticate the user again (even if the\n\t * user has previously authenticated). During ticket validation it will require the\n\t * ticket was generated as a consequence of an explicit login. High security\n\t * applications would probably set this to <code>true</code>. Defaults to\n\t * <code>false</code>, providing automated single sign on.\n\t * @return whether to send the <code>renew</code> parameter to CAS\n\t */\n\tpublic final boolean isSendRenew() {\n\t\treturn this.sendRenew;\n\t}\n\n\tpublic final void setSendRenew(final boolean sendRenew) {\n\t\tthis.sendRenew = sendRenew;\n\t}\n\n\tpublic final void setService(final String service) {\n\t\tthis.service = service;\n\t}\n\n\tpublic final String getArtifactParameter() {\n\t\treturn this.artifactParameter;\n\t}\n\n\t/**\n\t * Configures the Request Parameter to look for when attempting to see if a CAS ticket\n\t * was sent from the server.\n\t * @param artifactParameter the id to use. Default is \"ticket\".\n\t */\n\tpublic final void setArtifactParameter(final String artifactParameter) {\n\t\tthis.artifactParameter = artifactParameter;\n\t}\n\n\t/**\n\t * Configures the Request parameter to look for when attempting to send a request to\n\t * CAS.\n\t * @return the service parameter to use. Default is \"service\".\n\t */\n\tpublic final String getServiceParameter() {\n\t\treturn this.serviceParameter;\n\t}\n\n\tpublic final void setServiceParameter(final String serviceParameter) {\n\t\tthis.serviceParameter = serviceParameter;\n\t}\n\n\tpublic final boolean isAuthenticateAllArtifacts() {\n\t\treturn this.authenticateAllArtifacts;\n\t}\n\n\t/**\n\t * If true, then any non-null artifact (ticket) should be authenticated. Additionally,\n\t * the service will be determined dynamically in order to ensure the service matches\n\t * the expected value for this artifact.\n\t * @param authenticateAllArtifacts\n\t */\n\tpublic final void setAuthenticateAllArtifacts(final boolean authenticateAllArtifacts) {\n\t\tthis.authenticateAllArtifacts = authenticateAllArtifacts;\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/CasAssertionAuthenticationToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport java.util.ArrayList;\n\nimport org.apereo.cas.client.validation.Assertion;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\n\n/**\n * Temporary authentication object needed to load the user details service.\n *\n * @author Scott Battaglia\n * @since 3.0\n */\npublic final class CasAssertionAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final Assertion assertion;\n\n\tprivate final String ticket;\n\n\tpublic CasAssertionAuthenticationToken(final Assertion assertion, final String ticket) {\n\t\tsuper(new ArrayList<>());\n\t\tthis.assertion = assertion;\n\t\tthis.ticket = ticket;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.assertion.getPrincipal().getName();\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn this.ticket;\n\t}\n\n\tpublic Assertion getAssertion() {\n\t\treturn this.assertion;\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.apereo.cas.client.validation.Assertion;\nimport org.apereo.cas.client.validation.TicketValidationException;\nimport org.apereo.cas.client.validation.TicketValidator;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation that integrates with JA-SIG Central\n * Authentication Service (CAS).\n * <p>\n * This <code>AuthenticationProvider</code> is capable of validating\n * {@link CasServiceTicketAuthenticationToken} requests which contain a\n * <code>principal</code> name equal to either\n * {@link CasServiceTicketAuthenticationToken#CAS_STATEFUL_IDENTIFIER} or\n * {@link CasServiceTicketAuthenticationToken#CAS_STATELESS_IDENTIFIER}. It can also\n * validate a previously created {@link CasAuthenticationToken}.\n *\n * @author Ben Alex\n * @author Scott Battaglia\n * @author Kim Youngwoong\n */\npublic class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {\n\n\tprivate static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.CAS_AUTHORITY;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;\n\n\tprivate UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate String key;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate TicketValidator ticketValidator;\n\n\tprivate @Nullable ServiceProperties serviceProperties;\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.authenticationUserDetailsService, \"An authenticationUserDetailsService must be set\");\n\t\tAssert.notNull(this.ticketValidator, \"A ticketValidator must be set\");\n\t\tAssert.notNull(this.statelessTicketCache, \"A statelessTicketCache must be set\");\n\t\tAssert.hasText(this.key,\n\t\t\t\t\"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated\");\n\t\tAssert.notNull(this.messages, \"A message source must be set\");\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (!supports(authentication.getClass())) {\n\t\t\treturn null;\n\t\t}\n\t\t// If an existing CasAuthenticationToken, just check we created it\n\t\tif (authentication instanceof CasAuthenticationToken) {\n\t\t\tif (this.key.hashCode() != ((CasAuthenticationToken) authentication).getKeyHash()) {\n\t\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"CasAuthenticationProvider.incorrectKey\",\n\t\t\t\t\t\t\"The presented CasAuthenticationToken does not contain the expected key\"));\n\t\t\t}\n\t\t\treturn authentication;\n\t\t}\n\n\t\t// Ensure credentials are presented\n\t\tif ((authentication.getCredentials() == null) || \"\".equals(authentication.getCredentials())) {\n\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"CasAuthenticationProvider.noServiceTicket\",\n\t\t\t\t\t\"Failed to provide a CAS service ticket to validate\"));\n\t\t}\n\n\t\tboolean stateless = (authentication instanceof CasServiceTicketAuthenticationToken token\n\t\t\t\t&& token.isStateless());\n\t\tCasAuthenticationToken result = null;\n\n\t\tif (stateless) {\n\t\t\t// Try to obtain from cache\n\t\t\tresult = this.statelessTicketCache.getByTicketId(authentication.getCredentials().toString());\n\t\t}\n\t\tif (result == null) {\n\t\t\tresult = this.authenticateNow(authentication);\n\t\t\tresult.setDetails(authentication.getDetails());\n\t\t}\n\t\tif (stateless) {\n\t\t\t// Add to cache\n\t\t\tthis.statelessTicketCache.putTicketInCache(result);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {\n\t\ttry {\n\t\t\tObject credentials = authentication.getCredentials();\n\t\t\tif (credentials == null) {\n\t\t\t\tthrow new BadCredentialsException(\"Authentication.getCredentials() cannot be null\");\n\t\t\t}\n\t\t\tAssertion assertion = this.ticketValidator.validate(credentials.toString(), getServiceUrl(authentication));\n\t\t\tUserDetails userDetails = loadUserByAssertion(assertion);\n\t\t\tthis.userDetailsChecker.check(userDetails);\n\t\t\tCollection<GrantedAuthority> authorities = new ArrayList<>(\n\t\t\t\t\tthis.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()));\n\t\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\t\treturn new CasAuthenticationToken(this.key, userDetails, credentials, authorities, userDetails, assertion);\n\t\t}\n\t\tcatch (TicketValidationException ex) {\n\t\t\tthrow new BadCredentialsException(ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Gets the serviceUrl. If the {@link Authentication#getDetails()} is an instance of\n\t * {@link ServiceAuthenticationDetails}, then\n\t * {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise, the\n\t * {@link ServiceProperties#getService()} is used.\n\t * @param authentication\n\t * @return\n\t */\n\tprivate @Nullable String getServiceUrl(Authentication authentication) {\n\t\tString serviceUrl;\n\t\tif (authentication.getDetails() instanceof ServiceAuthenticationDetails) {\n\t\t\treturn ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();\n\t\t}\n\t\tAssert.state(this.serviceProperties != null,\n\t\t\t\t\"serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.\");\n\t\tAssert.state(this.serviceProperties.getService() != null,\n\t\t\t\t\"serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.\");\n\t\tserviceUrl = this.serviceProperties.getService();\n\t\tlogger.debug(LogMessage.format(\"serviceUrl = %s\", serviceUrl));\n\t\treturn serviceUrl;\n\t}\n\n\t/**\n\t * Template method for retrieving the UserDetails based on the assertion. Default is\n\t * to call configured userDetailsService and pass the username. Deployers can override\n\t * this method and retrieve the user based on any criteria they desire.\n\t * @param assertion The CAS Assertion.\n\t * @return the UserDetails.\n\t */\n\tprotected UserDetails loadUserByAssertion(final Assertion assertion) {\n\t\tfinal CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, \"\");\n\t\treturn this.authenticationUserDetailsService.loadUserDetails(token);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t/**\n\t * Sets the UserDetailsService to use. This is a convenience method to invoke\n\t */\n\tpublic void setUserDetailsService(final UserDetailsService userDetailsService) {\n\t\tthis.authenticationUserDetailsService = new UserDetailsByNameServiceWrapper(userDetailsService);\n\t}\n\n\tpublic void setAuthenticationUserDetailsService(\n\t\t\tfinal AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService) {\n\t\tthis.authenticationUserDetailsService = authenticationUserDetailsService;\n\t}\n\n\t/**\n\t * Sets the UserDetailsChecker to be used for checking the status of retrieved user\n\t * details. This allows customization of the UserDetailsChecker implementation.\n\t * @param userDetailsChecker the UserDetailsChecker to be set\n\t * @since 6.4\n\t */\n\tpublic void setUserDetailsChecker(final UserDetailsChecker userDetailsChecker) {\n\t\tAssert.notNull(userDetailsChecker, \"userDetailsChecker cannot be null\");\n\t\tthis.userDetailsChecker = userDetailsChecker;\n\t}\n\n\tpublic void setServiceProperties(final ServiceProperties serviceProperties) {\n\t\tthis.serviceProperties = serviceProperties;\n\t}\n\n\tprotected String getKey() {\n\t\treturn this.key;\n\t}\n\n\tpublic void setKey(String key) {\n\t\tthis.key = key;\n\t}\n\n\tpublic StatelessTicketCache getStatelessTicketCache() {\n\t\treturn this.statelessTicketCache;\n\t}\n\n\tprotected @Nullable TicketValidator getTicketValidator() {\n\t\treturn this.ticketValidator;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(final MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\tpublic void setStatelessTicketCache(final StatelessTicketCache statelessTicketCache) {\n\t\tthis.statelessTicketCache = statelessTicketCache;\n\t}\n\n\tpublic void setTicketValidator(final TicketValidator ticketValidator) {\n\t\tthis.ticketValidator = ticketValidator;\n\t}\n\n\tpublic void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\t@Override\n\tpublic boolean supports(final Class<?> authentication) {\n\t\treturn (CasServiceTicketAuthenticationToken.class.isAssignableFrom(authentication))\n\t\t\t\t|| (CasAuthenticationToken.class.isAssignableFrom(authentication))\n\t\t\t\t|| (CasAssertionAuthenticationToken.class.isAssignableFrom(authentication));\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport java.io.Serializable;\nimport java.util.Collection;\n\nimport org.apereo.cas.client.validation.Assertion;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * Represents a successful CAS <code>Authentication</code>.\n *\n * @author Ben Alex\n * @author Scott Battaglia\n */\npublic class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final Object credentials;\n\n\tprivate final Object principal;\n\n\tprivate final UserDetails userDetails;\n\n\tprivate final int keyHash;\n\n\tprivate final Assertion assertion;\n\n\t/**\n\t * Constructor.\n\t * @param key to identify if this object made by a given\n\t * {@link CasAuthenticationProvider}\n\t * @param principal typically the UserDetails object (cannot be <code>null</code>)\n\t * @param credentials the service/proxy ticket ID from CAS (cannot be\n\t * <code>null</code>)\n\t * @param authorities the authorities granted to the user (from the\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot\n\t * be <code>null</code>)\n\t * @param userDetails the user details (from the\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot\n\t * be <code>null</code>)\n\t * @param assertion the assertion returned from the CAS servers. It contains the\n\t * principal and how to obtain a proxy ticket for the user.\n\t * @throws IllegalArgumentException if a <code>null</code> was passed\n\t */\n\tpublic CasAuthenticationToken(final String key, final Object principal, final Object credentials,\n\t\t\tfinal Collection<? extends GrantedAuthority> authorities, final UserDetails userDetails,\n\t\t\tfinal Assertion assertion) {\n\t\tthis(extractKeyHash(key), principal, credentials, authorities, userDetails, assertion);\n\t}\n\n\t/**\n\t * Private constructor for Jackson Deserialization support\n\t * @param keyHash hashCode of provided key to identify if this object made by a given\n\t * {@link CasAuthenticationProvider}\n\t * @param principal typically the UserDetails object (cannot be <code>null</code>)\n\t * @param credentials the service/proxy ticket ID from CAS (cannot be\n\t * <code>null</code>)\n\t * @param authorities the authorities granted to the user (from the\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot\n\t * be <code>null</code>)\n\t * @param userDetails the user details (from the\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot\n\t * be <code>null</code>)\n\t * @param assertion the assertion returned from the CAS servers. It contains the\n\t * principal and how to obtain a proxy ticket for the user.\n\t * @throws IllegalArgumentException if a <code>null</code> was passed\n\t * @since 4.2\n\t */\n\tprivate CasAuthenticationToken(final Integer keyHash, final Object principal, final Object credentials,\n\t\t\tfinal Collection<? extends GrantedAuthority> authorities, final UserDetails userDetails,\n\t\t\tfinal Assertion assertion) {\n\t\tsuper(authorities);\n\t\tif ((principal == null) || \"\".equals(principal) || (credentials == null) || \"\".equals(credentials)\n\t\t\t\t|| (authorities == null) || (userDetails == null) || (assertion == null)) {\n\t\t\tthrow new IllegalArgumentException(\"Cannot pass null or empty values to constructor\");\n\t\t}\n\t\tthis.keyHash = keyHash;\n\t\tthis.principal = principal;\n\t\tthis.credentials = credentials;\n\t\tthis.userDetails = userDetails;\n\t\tthis.assertion = assertion;\n\t\tsetAuthenticated(true);\n\t}\n\n\tprotected CasAuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tAssert.isTrue(!\"\".equals(builder.principal), \"principal cannot be null or empty\");\n\t\tAssert.notNull(!\"\".equals(builder.credentials), \"credentials cannot be null or empty\");\n\t\tAssert.notNull(builder.userDetails, \"userDetails cannot be null\");\n\t\tAssert.notNull(builder.assertion, \"assertion cannot be null\");\n\t\tthis.keyHash = builder.keyHash;\n\t\tthis.principal = builder.principal;\n\t\tthis.credentials = builder.credentials;\n\t\tthis.userDetails = builder.userDetails;\n\t\tthis.assertion = builder.assertion;\n\t}\n\n\tprivate static Integer extractKeyHash(String key) {\n\t\tAssert.hasLength(key, \"key cannot be null or empty\");\n\t\treturn key.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(final Object obj) {\n\t\tif (!super.equals(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (obj instanceof CasAuthenticationToken test) {\n\t\t\treturn this.assertion.equals(test.getAssertion()) && this.getKeyHash() == test.getKeyHash();\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = super.hashCode();\n\t\tresult = 31 * result + this.credentials.hashCode();\n\t\tresult = 31 * result + this.principal.hashCode();\n\t\tresult = 31 * result + this.userDetails.hashCode();\n\t\tresult = 31 * result + this.keyHash;\n\t\tresult = 31 * result + ObjectUtils.nullSafeHashCode(this.assertion);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\tpublic int getKeyHash() {\n\t\treturn this.keyHash;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\tpublic Assertion getAssertion() {\n\t\treturn this.assertion;\n\t}\n\n\tpublic UserDetails getUserDetails() {\n\t\treturn this.userDetails;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(super.toString());\n\t\tsb.append(\" Assertion: \").append(this.assertion);\n\t\tsb.append(\" Credentials (Service/Proxy Ticket): \").append(this.credentials);\n\t\treturn (sb.toString());\n\t}\n\n\t/**\n\t * A builder of {@link CasAuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate Integer keyHash;\n\n\t\tprivate Object principal;\n\n\t\tprivate Object credentials;\n\n\t\tprivate UserDetails userDetails;\n\n\t\tprivate Assertion assertion;\n\n\t\tprotected Builder(CasAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.keyHash = token.keyHash;\n\t\t\tthis.principal = token.principal;\n\t\t\tthis.credentials = token.credentials;\n\t\t\tthis.userDetails = token.userDetails;\n\t\t\tthis.assertion = token.assertion;\n\t\t}\n\n\t\t/**\n\t\t * Use this key\n\t\t * @param key the key to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic B key(String key) {\n\t\t\tthis.keyHash = key.hashCode();\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object credentials) {\n\t\t\tAssert.notNull(credentials, \"credentials cannot be null\");\n\t\t\tthis.credentials = credentials;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link UserDetails}\n\t\t * @param userDetails the {@link UserDetails} to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic B userDetails(UserDetails userDetails) {\n\t\t\tthis.userDetails = userDetails;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link Assertion}\n\t\t * @param assertion the {@link Assertion} to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic B assertion(Assertion assertion) {\n\t\t\tthis.assertion = assertion;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic CasAuthenticationToken build() {\n\t\t\treturn new CasAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/CasServiceTicketAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport java.io.Serial;\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link org.springframework.security.core.Authentication} implementation that is\n * designed to process CAS service ticket.\n *\n * @author Hal Deadman\n * @since 6.1\n */\npublic class CasServiceTicketAuthenticationToken extends AbstractAuthenticationToken {\n\n\tstatic final String CAS_STATELESS_IDENTIFIER = \"_cas_stateless_\";\n\n\tstatic final String CAS_STATEFUL_IDENTIFIER = \"_cas_stateful_\";\n\n\t@Serial\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String identifier;\n\n\tprivate @Nullable Object credentials;\n\n\t/**\n\t * This constructor can be safely used by any code that wishes to create a\n\t * <code>CasServiceTicketAuthenticationToken</code>, as the {@link #isAuthenticated()}\n\t * will return <code>false</code>.\n\t *\n\t */\n\tpublic CasServiceTicketAuthenticationToken(String identifier, Object credentials) {\n\t\tsuper((Collection<? extends GrantedAuthority>) null);\n\t\tthis.identifier = identifier;\n\t\tthis.credentials = credentials;\n\t\tsetAuthenticated(false);\n\t}\n\n\t/**\n\t * This constructor should only be used by <code>AuthenticationManager</code> or\n\t * <code>AuthenticationProvider</code> implementations that are satisfied with\n\t * producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)\n\t * authentication token.\n\t * @param identifier\n\t * @param credentials\n\t * @param authorities\n\t */\n\tpublic CasServiceTicketAuthenticationToken(String identifier, Object credentials,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tthis.identifier = identifier;\n\t\tthis.credentials = credentials;\n\t\tsuper.setAuthenticated(true);\n\t}\n\n\tprotected CasServiceTicketAuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.identifier = builder.principal;\n\t\tthis.credentials = builder.credentials;\n\t}\n\n\tpublic static CasServiceTicketAuthenticationToken stateful(Object credentials) {\n\t\treturn new CasServiceTicketAuthenticationToken(CAS_STATEFUL_IDENTIFIER, credentials);\n\t}\n\n\tpublic static CasServiceTicketAuthenticationToken stateless(Object credentials) {\n\t\treturn new CasServiceTicketAuthenticationToken(CAS_STATELESS_IDENTIFIER, credentials);\n\t}\n\n\tpublic boolean isStateless() {\n\t\treturn CAS_STATELESS_IDENTIFIER.equals(this.identifier);\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.identifier;\n\t}\n\n\t@Override\n\tpublic void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n\t\tAssert.isTrue(!isAuthenticated,\n\t\t\t\t\"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead\");\n\t\tsuper.setAuthenticated(false);\n\t}\n\n\t@Override\n\tpublic void eraseCredentials() {\n\t\tsuper.eraseCredentials();\n\t\tthis.credentials = null;\n\t}\n\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link CasServiceTicketAuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate String principal;\n\n\t\tprivate @Nullable Object credentials;\n\n\t\tprotected Builder(CasServiceTicketAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.identifier;\n\t\t\tthis.credentials = token.credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.isInstanceOf(String.class, principal, \"principal must be of type String\");\n\t\t\tthis.principal = (String) principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object credentials) {\n\t\t\tAssert.notNull(credentials, \"credentials cannot be null\");\n\t\t\tthis.credentials = credentials;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic CasServiceTicketAuthenticationToken build() {\n\t\t\treturn new CasServiceTicketAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/NullStatelessTicketCache.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful\n * in instances where storing of tickets for stateless session management is not required.\n * <p>\n * This is the default StatelessTicketCache of the @link {@link CasAuthenticationProvider}\n * to eliminate the unnecessary dependency on EhCache that applications have even if they\n * are not using the stateless session management.\n *\n * @author Scott Battaglia\n * @see CasAuthenticationProvider\n */\npublic final class NullStatelessTicketCache implements StatelessTicketCache {\n\n\t/**\n\t * @return null since we are not storing any tickets.\n\t */\n\t@Override\n\tpublic @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * This is a no-op since we are not storing tickets.\n\t */\n\t@Override\n\tpublic void putTicketInCache(final CasAuthenticationToken token) {\n\t\t// nothing to do\n\t}\n\n\t/**\n\t * This is a no-op since we are not storing tickets.\n\t */\n\t@Override\n\tpublic void removeTicketFromCache(final CasAuthenticationToken token) {\n\t\t// nothing to do\n\t}\n\n\t/**\n\t * This is a no-op since we are not storing tickets.\n\t */\n\t@Override\n\tpublic void removeTicketFromCache(final String serviceTicket) {\n\t\t// nothing to do\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/ServiceAuthenticationDetails.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport java.io.Serializable;\n\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.core.Authentication;\n\n/**\n * In order for the {@link CasAuthenticationProvider} to provide the correct service url\n * to authenticate the ticket, the returned value of {@link Authentication#getDetails()}\n * should implement this interface when tickets can be sent to any URL rather than only\n * {@link ServiceProperties#getService()}.\n *\n * @author Rob Winch\n * @see org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource\n */\npublic interface ServiceAuthenticationDetails extends Serializable {\n\n\t/**\n\t * Gets the absolute service url (i.e. https://example.com/service/).\n\t * @return the service url. Cannot be <code>null</code>.\n\t */\n\tString getServiceUrl();\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\n\n/**\n * Caches tickets using a Spring IoC defined {@link Cache}.\n *\n * @author Marten Deinum\n * @since 3.2\n *\n */\npublic class SpringCacheBasedTicketCache implements StatelessTicketCache {\n\n\tprivate static final Log logger = LogFactory.getLog(SpringCacheBasedTicketCache.class);\n\n\tprivate final Cache cache;\n\n\tpublic SpringCacheBasedTicketCache(Cache cache) {\n\t\tAssert.notNull(cache, \"cache mandatory\");\n\t\tthis.cache = cache;\n\t}\n\n\t@Override\n\tpublic @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {\n\t\tfinal Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null;\n\t\tlogger.debug(LogMessage.of(() -> \"Cache hit: \" + (element != null) + \"; service ticket: \" + serviceTicket));\n\t\treturn (element != null) ? (CasAuthenticationToken) element.get() : null;\n\t}\n\n\t@Override\n\tpublic void putTicketInCache(final CasAuthenticationToken token) {\n\t\tString key = token.getCredentials().toString();\n\t\tlogger.debug(LogMessage.of(() -> \"Cache put: \" + key));\n\t\tthis.cache.put(key, token);\n\t}\n\n\t@Override\n\tpublic void removeTicketFromCache(final CasAuthenticationToken token) {\n\t\tlogger.debug(LogMessage.of(() -> \"Cache remove: \" + token.getCredentials().toString()));\n\t\tthis.removeTicketFromCache(token.getCredentials().toString());\n\t}\n\n\t@Override\n\tpublic void removeTicketFromCache(final String serviceTicket) {\n\t\tthis.cache.evict(serviceTicket);\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/StatelessTicketCache.java",
    "content": "/*\n * Copyright 2004 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Caches CAS service tickets and CAS proxy tickets for stateless connections.\n *\n * <p>\n * When a service ticket or proxy ticket is validated against the CAS server, it is unable\n * to be used again. Most types of callers are stateful and are associated with a given\n * <code>HttpSession</code>. This allows the affirmative CAS validation outcome to be\n * stored in the <code>HttpSession</code>, meaning the removal of the ticket from the CAS\n * server is not an issue.\n * </p>\n *\n * <P>\n * Stateless callers, such as remoting protocols, cannot take advantage of\n * <code>HttpSession</code>. If the stateless caller is located a significant network\n * distance from the CAS server, acquiring a fresh service ticket or proxy ticket for each\n * invocation would be expensive.\n * </p>\n *\n * <P>\n * To avoid this issue with stateless callers, it is expected stateless callers will\n * obtain a single service ticket or proxy ticket, and then present this same ticket to\n * the Spring Security secured application on each occasion. As no\n * <code>HttpSession</code> is available for such callers, the affirmative CAS validation\n * outcome cannot be stored in this location.\n * </p>\n *\n * <P>\n * The <code>StatelessTicketCache</code> enables the service tickets and proxy tickets\n * belonging to stateless callers to be placed in a cache. This in-memory cache stores the\n * <code>CasAuthenticationToken</code>, effectively providing the same capability as a\n * <code>HttpSession</code> with the ticket identifier being the key rather than a session\n * identifier.\n * </p>\n *\n * <P>\n * Implementations should provide a reasonable timeout on stored entries, such that the\n * stateless caller are not required to unnecessarily acquire fresh CAS service tickets or\n * proxy tickets.\n * </p>\n *\n * @author Ben Alex\n */\npublic interface StatelessTicketCache {\n\n\t/**\n\t * Retrieves the <code>CasAuthenticationToken</code> associated with the specified\n\t * ticket.\n\t *\n\t * <P>\n\t * If not found, returns a <code>null</code><code>CasAuthenticationToken</code>.\n\t * </p>\n\t * @return the fully populated authentication token\n\t */\n\t@Nullable CasAuthenticationToken getByTicketId(String serviceTicket);\n\n\t/**\n\t * Adds the specified <code>CasAuthenticationToken</code> to the cache.\n\t *\n\t * <P>\n\t * The {@link CasAuthenticationToken#getCredentials()} method is used to retrieve the\n\t * service ticket number.\n\t * </p>\n\t * @param token to be added to the cache\n\t */\n\tvoid putTicketInCache(CasAuthenticationToken token);\n\n\t/**\n\t * Removes the specified ticket from the cache, as per\n\t * {@link #removeTicketFromCache(String)}.\n\t *\n\t * <P>\n\t * Implementations should use {@link CasAuthenticationToken#getCredentials()} to\n\t * obtain the ticket and then delegate to the {@link #removeTicketFromCache(String)}\n\t * method.\n\t * </p>\n\t * @param token to be removed\n\t */\n\tvoid removeTicketFromCache(CasAuthenticationToken token);\n\n\t/**\n\t * Removes the specified ticket from the cache, meaning that future calls will require\n\t * a new service ticket.\n\t *\n\t * <P>\n\t * This is in case applications wish to provide a session termination capability for\n\t * their stateless clients.\n\t * </p>\n\t * @param serviceTicket to be removed\n\t */\n\tvoid removeTicketFromCache(String serviceTicket);\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * An {@code AuthenticationProvider} that can process CAS service tickets and proxy\n * tickets.\n */\n@NullMarked\npackage org.springframework.security.cas.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson/AssertionImplMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson;\n\nimport java.util.Date;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.apereo.cas.client.authentication.AttributePrincipal;\n\n/**\n * Helps in jackson deserialization of class\n * {@link org.apereo.cas.client.validation.AssertionImpl}, which is used with\n * {@link org.springframework.security.cas.authentication.CasAuthenticationToken}.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CasJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nclass AssertionImplMixin {\n\n\t/**\n\t * Mixin Constructor helps in deserialize\n\t * {@link org.apereo.cas.client.validation.AssertionImpl}\n\t * @param principal the Principal to associate with the Assertion.\n\t * @param validFromDate when the assertion is valid from.\n\t * @param validUntilDate when the assertion is valid to.\n\t * @param authenticationDate when the assertion is authenticated.\n\t * @param attributes the key/value pairs for this attribute.\n\t */\n\t@JsonCreator\n\tAssertionImplMixin(@JsonProperty(\"principal\") AttributePrincipal principal,\n\t\t\t@JsonProperty(\"validFromDate\") Date validFromDate, @JsonProperty(\"validUntilDate\") Date validUntilDate,\n\t\t\t@JsonProperty(\"authenticationDate\") Date authenticationDate,\n\t\t\t@JsonProperty(\"attributes\") Map<String, Object> attributes) {\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson/AttributePrincipalImplMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.apereo.cas.client.proxy.ProxyRetriever;\n\n/**\n * Helps in deserialize\n * {@link org.apereo.cas.client.authentication.AttributePrincipalImpl} which is used with\n * {@link org.springframework.security.cas.authentication.CasAuthenticationToken}.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CasJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nclass AttributePrincipalImplMixin {\n\n\t/**\n\t * Mixin Constructor helps in deserialize\n\t * {@link org.apereo.cas.client.authentication.AttributePrincipalImpl}\n\t * @param name the unique identifier for the principal.\n\t * @param attributes the key/value pairs for this principal.\n\t * @param proxyGrantingTicket the ticket associated with this principal.\n\t * @param proxyRetriever the ProxyRetriever implementation to call back to the CAS\n\t * server.\n\t */\n\t@JsonCreator\n\tAttributePrincipalImplMixin(@JsonProperty(\"name\") String name,\n\t\t\t@JsonProperty(\"attributes\") Map<String, Object> attributes,\n\t\t\t@JsonProperty(\"proxyGrantingTicket\") String proxyGrantingTicket,\n\t\t\t@JsonProperty(\"proxyRetriever\") ProxyRetriever proxyRetriever) {\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson/CasAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.apereo.cas.client.validation.Assertion;\n\nimport org.springframework.security.cas.authentication.CasAuthenticationProvider;\nimport org.springframework.security.cas.authentication.CasAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * Mixin class which helps in deserialize {@link CasAuthenticationToken} using jackson.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CasJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\nclass CasAuthenticationTokenMixin {\n\n\t/**\n\t * Mixin Constructor helps in deserialize {@link CasAuthenticationToken}\n\t * @param keyHash hashCode of provided key to identify if this object made by a given\n\t * {@link CasAuthenticationProvider}\n\t * @param principal typically the UserDetails object (cannot be <code>null</code>)\n\t * @param credentials the service/proxy ticket ID from CAS (cannot be\n\t * <code>null</code>)\n\t * @param authorities the authorities granted to the user (from the\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot\n\t * be <code>null</code>)\n\t * @param userDetails the user details (from the\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot\n\t * be <code>null</code>)\n\t * @param assertion the assertion returned from the CAS servers. It contains the\n\t * principal and how to obtain a proxy ticket for the user.\n\t */\n\t@JsonCreator\n\tCasAuthenticationTokenMixin(@JsonProperty(\"keyHash\") Integer keyHash, @JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"credentials\") Object credentials,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"userDetails\") UserDetails userDetails, @JsonProperty(\"assertion\") Assertion assertion) {\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson/CasJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson;\n\nimport org.apereo.cas.client.authentication.AttributePrincipalImpl;\nimport org.apereo.cas.client.validation.AssertionImpl;\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.cas.authentication.CasAuthenticationToken;\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.jackson.SecurityJacksonModules;\n\n/**\n * Jackson module for spring-security-cas. This module register\n * {@link AssertionImplMixin}, {@link AttributePrincipalImplMixin} and\n * {@link CasAuthenticationTokenMixin}. If no default typing enabled by default then it'll\n * enable it because typing info is needed to properly serialize/deserialize objects. In\n * order to use this module just add this module into your JsonMapper configuration.\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see SecurityJacksonModules\n */\npublic class CasJacksonModule extends SecurityJacksonModule {\n\n\tpublic CasJacksonModule() {\n\t\tsuper(CasJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(AssertionImpl.class)\n\t\t\t.allowIfSubType(AttributePrincipalImpl.class)\n\t\t\t.allowIfSubType(CasAuthenticationToken.class);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(AssertionImpl.class, AssertionImplMixin.class);\n\t\tcontext.setMixIn(AttributePrincipalImpl.class, AttributePrincipalImplMixin.class);\n\t\tcontext.setMixIn(CasAuthenticationToken.class, CasAuthenticationTokenMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 3+ serialization support for CAS.\n */\n@NullMarked\npackage org.springframework.security.cas.jackson;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson2/AssertionImplMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson2;\n\nimport java.util.Date;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.apereo.cas.client.authentication.AttributePrincipal;\n\n/**\n * Helps in jackson deserialization of class\n * {@link org.apereo.cas.client.validation.AssertionImpl}, which is used with\n * {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. To use\n * this class we need to register with\n * {@link com.fasterxml.jackson.databind.ObjectMapper}. Type information will be stored\n * in @class property.\n *\n * <p>\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CasJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see CasJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.cas.jackson.AssertionImplMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nclass AssertionImplMixin {\n\n\t/**\n\t * Mixin Constructor helps in deserialize\n\t * {@link org.apereo.cas.client.validation.AssertionImpl}\n\t * @param principal the Principal to associate with the Assertion.\n\t * @param validFromDate when the assertion is valid from.\n\t * @param validUntilDate when the assertion is valid to.\n\t * @param authenticationDate when the assertion is authenticated.\n\t * @param attributes the key/value pairs for this attribute.\n\t */\n\t@JsonCreator\n\tAssertionImplMixin(@JsonProperty(\"principal\") AttributePrincipal principal,\n\t\t\t@JsonProperty(\"validFromDate\") Date validFromDate, @JsonProperty(\"validUntilDate\") Date validUntilDate,\n\t\t\t@JsonProperty(\"authenticationDate\") Date authenticationDate,\n\t\t\t@JsonProperty(\"attributes\") Map<String, Object> attributes) {\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson2/AttributePrincipalImplMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson2;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.apereo.cas.client.proxy.ProxyRetriever;\n\n/**\n * Helps in deserialize\n * {@link org.apereo.cas.client.authentication.AttributePrincipalImpl} which is used with\n * {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. Type\n * information will be stored in property named @class.\n *\n * <p>\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CasJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see CasJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.cas.jackson.AttributePrincipalImplMixin} based on\n * Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nclass AttributePrincipalImplMixin {\n\n\t/**\n\t * Mixin Constructor helps in deserialize\n\t * {@link org.apereo.cas.client.authentication.AttributePrincipalImpl}\n\t * @param name the unique identifier for the principal.\n\t * @param attributes the key/value pairs for this principal.\n\t * @param proxyGrantingTicket the ticket associated with this principal.\n\t * @param proxyRetriever the ProxyRetriever implementation to call back to the CAS\n\t * server.\n\t */\n\t@JsonCreator\n\tAttributePrincipalImplMixin(@JsonProperty(\"name\") String name,\n\t\t\t@JsonProperty(\"attributes\") Map<String, Object> attributes,\n\t\t\t@JsonProperty(\"proxyGrantingTicket\") String proxyGrantingTicket,\n\t\t\t@JsonProperty(\"proxyRetriever\") ProxyRetriever proxyRetriever) {\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson2;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.apereo.cas.client.validation.Assertion;\n\nimport org.springframework.security.cas.authentication.CasAuthenticationProvider;\nimport org.springframework.security.cas.authentication.CasAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * Mixin class which helps in deserialize\n * {@link org.springframework.security.cas.authentication.CasAuthenticationToken} using\n * jackson. Two more dependent classes needs to register along with this mixin class.\n * <ol>\n * <li>{@link org.springframework.security.cas.jackson2.AssertionImplMixin}</li>\n * <li>{@link org.springframework.security.cas.jackson2.AttributePrincipalImplMixin}</li>\n * </ol>\n *\n * <p>\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CasJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see CasJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.cas.jackson.CasAuthenticationTokenMixin} based on\n * Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\n@JsonIgnoreProperties(ignoreUnknown = true)\nclass CasAuthenticationTokenMixin {\n\n\t/**\n\t * Mixin Constructor helps in deserialize {@link CasAuthenticationToken}\n\t * @param keyHash hashCode of provided key to identify if this object made by a given\n\t * {@link CasAuthenticationProvider}\n\t * @param principal typically the UserDetails object (cannot be <code>null</code>)\n\t * @param credentials the service/proxy ticket ID from CAS (cannot be\n\t * <code>null</code>)\n\t * @param authorities the authorities granted to the user (from the\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot\n\t * be <code>null</code>)\n\t * @param userDetails the user details (from the\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot\n\t * be <code>null</code>)\n\t * @param assertion the assertion returned from the CAS servers. It contains the\n\t * principal and how to obtain a proxy ticket for the user.\n\t */\n\t@JsonCreator\n\tCasAuthenticationTokenMixin(@JsonProperty(\"keyHash\") Integer keyHash, @JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"credentials\") Object credentials,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"userDetails\") UserDetails userDetails, @JsonProperty(\"assertion\") Assertion assertion) {\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson2/CasJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson2;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport org.apereo.cas.client.authentication.AttributePrincipalImpl;\nimport org.apereo.cas.client.validation.AssertionImpl;\n\nimport org.springframework.security.cas.authentication.CasAuthenticationToken;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\n\n/**\n * Jackson module for spring-security-cas. This module register\n * {@link AssertionImplMixin}, {@link AttributePrincipalImplMixin} and\n * {@link CasAuthenticationTokenMixin}. If no default typing enabled by default then it'll\n * enable it because typing info is needed to properly serialize/deserialize objects. In\n * order to use this module just add this module into your ObjectMapper configuration.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CasJackson2Module());\n * </pre> <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list\n * of all security modules on the classpath.</b>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.cas.jackson.CasJacksonModule} based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings({ \"serial\", \"removal\" })\npublic class CasJackson2Module extends SimpleModule {\n\n\tpublic CasJackson2Module() {\n\t\tsuper(CasJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tSecurityJackson2Modules.enableDefaultTyping(context.getOwner());\n\t\tcontext.setMixInAnnotations(AssertionImpl.class, AssertionImplMixin.class);\n\t\tcontext.setMixInAnnotations(AttributePrincipalImpl.class, AttributePrincipalImplMixin.class);\n\t\tcontext.setMixInAnnotations(CasAuthenticationToken.class, CasAuthenticationTokenMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/jackson2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 2 support for CAS.\n */\n@NullMarked\npackage org.springframework.security.cas.jackson2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security support for Apereo's Central Authentication Service\n * (<a href=\"https://github.com/apereo/cas\">CAS</a>).\n */\n@NullMarked\npackage org.springframework.security.cas;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/userdetails/AbstractCasAssertionUserDetailsService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.userdetails;\n\nimport org.apereo.cas.client.validation.Assertion;\n\nimport org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * Abstract class for using the provided CAS assertion to construct a new User object.\n * This generally is most useful when combined with a SAML-based response from the CAS\n * Server/client.\n *\n * @author Scott Battaglia\n * @since 3.0\n */\npublic abstract class AbstractCasAssertionUserDetailsService\n\t\timplements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {\n\n\t@Override\n\tpublic final UserDetails loadUserDetails(final CasAssertionAuthenticationToken token) {\n\t\treturn loadUserDetails(token.getAssertion());\n\t}\n\n\t/**\n\t * Protected template method for construct a\n\t * {@link org.springframework.security.core.userdetails.UserDetails} via the supplied\n\t * CAS assertion.\n\t * @param assertion the assertion to use to construct the new UserDetails. CANNOT be\n\t * NULL.\n\t * @return the newly constructed UserDetails.\n\t */\n\tprotected abstract UserDetails loadUserDetails(Assertion assertion);\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.userdetails;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.apereo.cas.client.validation.Assertion;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * Populates the {@link org.springframework.security.core.GrantedAuthority}s for a user by\n * reading a list of attributes that were returned as part of the CAS response. Each\n * attribute is read and each value of the attribute is turned into a GrantedAuthority. If\n * the attribute has no value then its not added.\n *\n * @author Scott Battaglia\n * @since 3.0\n */\npublic final class GrantedAuthorityFromAssertionAttributesUserDetailsService\n\t\textends AbstractCasAssertionUserDetailsService {\n\n\tprivate static final String NON_EXISTENT_PASSWORD_VALUE = \"NO_PASSWORD\";\n\n\tprivate final String[] attributes;\n\n\tprivate boolean convertToUpperCase = true;\n\n\tpublic GrantedAuthorityFromAssertionAttributesUserDetailsService(final String[] attributes) {\n\t\tAssert.notNull(attributes, \"attributes cannot be null.\");\n\t\tAssert.isTrue(attributes.length > 0, \"At least one attribute is required to retrieve roles from.\");\n\t\tthis.attributes = attributes;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tprotected UserDetails loadUserDetails(final Assertion assertion) {\n\t\tList<GrantedAuthority> grantedAuthorities = new ArrayList<>();\n\t\tfor (String attribute : this.attributes) {\n\t\t\tObject value = assertion.getPrincipal().getAttributes().get(attribute);\n\t\t\tif (value != null) {\n\t\t\t\tif (value instanceof List) {\n\t\t\t\t\tfor (Object o : (List<?>) value) {\n\t\t\t\t\t\tgrantedAuthorities.add(createSimpleGrantedAuthority(o));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tgrantedAuthorities.add(createSimpleGrantedAuthority(value));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn new User(assertion.getPrincipal().getName(), NON_EXISTENT_PASSWORD_VALUE, true, true, true, true,\n\t\t\t\tgrantedAuthorities);\n\t}\n\n\tprivate SimpleGrantedAuthority createSimpleGrantedAuthority(Object o) {\n\t\treturn new SimpleGrantedAuthority(\n\t\t\t\tthis.convertToUpperCase ? o.toString().toUpperCase(Locale.ROOT) : o.toString());\n\t}\n\n\t/**\n\t * Converts the returned attribute values to uppercase values.\n\t * @param convertToUpperCase true if it should convert, false otherwise.\n\t */\n\tpublic void setConvertToUpperCase(final boolean convertToUpperCase) {\n\t\tthis.convertToUpperCase = convertToUpperCase;\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/userdetails/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * {@link org.springframework.security.core.userdetails.UserDetails} abstractions for CAS.\n */\n@NullMarked\npackage org.springframework.security.cas.userdetails;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apereo.cas.client.util.CommonUtils;\nimport org.apereo.cas.client.util.WebUtils;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the\n * JA-SIG Central Authentication Service (CAS).\n * <p>\n * The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page.\n * This page is specified by the <code>loginUrl</code> property. Once login is complete,\n * the CAS login page will redirect to the page indicated by the <code>service</code>\n * property. The <code>service</code> is a HTTP URL belonging to the current application.\n * The <code>service</code> URL is monitored by the {@link CasAuthenticationFilter}, which\n * will validate the CAS login was successful.\n *\n * @author Ben Alex\n * @author Scott Battaglia\n */\npublic class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate ServiceProperties serviceProperties;\n\n\tprivate @Nullable String loginUrl;\n\n\t/**\n\t * Determines whether the Service URL should include the session id for the specific\n\t * user. As of CAS 3.0.5, the session id will automatically be stripped. However,\n\t * older versions of CAS (i.e. CAS 2), do not automatically strip the session\n\t * identifier (this is a bug on the part of the older server implementations), so an\n\t * option to disable the session encoding is provided for backwards compatibility.\n\t *\n\t * By default, encoding is enabled.\n\t */\n\tprivate boolean encodeServiceUrlWithSessionId = true;\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasLength(this.loginUrl, \"loginUrl must be specified\");\n\t\tAssert.notNull(this.serviceProperties, \"serviceProperties must be specified\");\n\t\tAssert.notNull(this.serviceProperties.getService(), \"serviceProperties.getService() cannot be null.\");\n\t}\n\n\t@Override\n\tpublic final void commence(final HttpServletRequest servletRequest, HttpServletResponse response,\n\t\t\tAuthenticationException authenticationException) throws IOException {\n\t\tString urlEncodedService = createServiceUrl(servletRequest, response);\n\t\tString redirectUrl = createRedirectUrl(urlEncodedService);\n\t\tpreCommence(servletRequest, response);\n\t\tthis.redirectStrategy.sendRedirect(servletRequest, response, redirectUrl);\n\t}\n\n\t/**\n\t * Constructs a new Service Url. The default implementation relies on the CAS client\n\t * to do the bulk of the work.\n\t * @param request the HttpServletRequest\n\t * @param response the HttpServlet Response\n\t * @return the constructed service url. CANNOT be NULL.\n\t */\n\tprotected String createServiceUrl(HttpServletRequest request, HttpServletResponse response) {\n\t\treturn WebUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null,\n\t\t\t\tthis.serviceProperties.getServiceParameter(), this.serviceProperties.getArtifactParameter(),\n\t\t\t\tthis.encodeServiceUrlWithSessionId);\n\t}\n\n\t/**\n\t * Constructs the Url for Redirection to the CAS server. Default implementation relies\n\t * on the CAS client to do the bulk of the work.\n\t * @param serviceUrl the service url that should be included.\n\t * @return the redirect url. CANNOT be NULL.\n\t */\n\tprotected String createRedirectUrl(String serviceUrl) {\n\t\treturn CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl,\n\t\t\t\tthis.serviceProperties.isSendRenew(), false);\n\t}\n\n\t/**\n\t * Template method for you to do your own pre-processing before the redirect occurs.\n\t * @param request the HttpServletRequest\n\t * @param response the HttpServletResponse\n\t */\n\tprotected void preCommence(HttpServletRequest request, HttpServletResponse response) {\n\n\t}\n\n\t/**\n\t * The enterprise-wide CAS login URL. Usually something like\n\t * <code>https://www.mycompany.com/cas/login</code>.\n\t * @return the enterprise-wide CAS login URL\n\t */\n\tpublic final @Nullable String getLoginUrl() {\n\t\treturn this.loginUrl;\n\t}\n\n\tpublic final ServiceProperties getServiceProperties() {\n\t\treturn this.serviceProperties;\n\t}\n\n\tpublic final void setLoginUrl(String loginUrl) {\n\t\tthis.loginUrl = loginUrl;\n\t}\n\n\tpublic final void setServiceProperties(ServiceProperties serviceProperties) {\n\t\tthis.serviceProperties = serviceProperties;\n\t}\n\n\t/**\n\t * Sets whether to encode the service url with the session id or not.\n\t * @param encodeServiceUrlWithSessionId whether to encode the service url with the\n\t * session id or not.\n\t */\n\tpublic final void setEncodeServiceUrlWithSessionId(boolean encodeServiceUrlWithSessionId) {\n\t\tthis.encodeServiceUrlWithSessionId = encodeServiceUrlWithSessionId;\n\t}\n\n\t/**\n\t * Sets whether to encode the service url with the session id or not.\n\t * @return whether to encode the service url with the session id or not.\n\t *\n\t */\n\tprotected boolean getEncodeServiceUrlWithSessionId() {\n\t\treturn this.encodeServiceUrlWithSessionId;\n\t}\n\n\t/**\n\t * Sets the {@link RedirectStrategy} to use\n\t * @param redirectStrategy the {@link RedirectStrategy} to use\n\t * @since 6.3\n\t */\n\tpublic void setRedirectStrategy(RedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;\nimport org.apereo.cas.client.util.WebUtils;\nimport org.apereo.cas.client.validation.TicketValidator;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.cas.authentication.CasServiceTicketAuthenticationToken;\nimport org.springframework.security.cas.authentication.ServiceAuthenticationDetails;\nimport org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy\n * tickets.\n * <h2>Service Tickets</h2>\n * <p>\n * A service ticket consists of an opaque ticket string. It arrives at this filter by the\n * user's browser successfully authenticating using CAS, and then receiving an HTTP\n * redirect to a <code>service</code>. The opaque ticket string is presented in the\n * <code>ticket</code> request parameter.\n * <p>\n * This filter monitors the <code>service</code> URL so that it can receive the service\n * ticket and process it. By default, this filter processes the URL <tt>/login/cas</tt>.\n * When processing this URL, the value of {@link ServiceProperties#getService()} is used\n * as the <tt>service</tt> when validating the <code>ticket</code>. This means that it is\n * important that {@link ServiceProperties#getService()} specifies the same value as the\n * <tt>filterProcessesUrl</tt>.\n * <p>\n * Processing the service ticket involves creating a\n * <code>CasServiceTicketAuthenticationToken</code> which uses\n * {@link CasServiceTicketAuthenticationToken#CAS_STATEFUL_IDENTIFIER} for the\n * <code>principal</code> and the opaque ticket string as the <code>credentials</code>.\n * <h2>Obtaining Proxy Granting Tickets</h2>\n * <p>\n * If specified, the filter can also monitor the <code>proxyReceptorUrl</code>. The filter\n * will respond to the requests matching this url so that the CAS Server can provide a PGT\n * to the filter. Note that in addition to the <code>proxyReceptorUrl</code> a non-null\n * <code>proxyGrantingTicketStorage</code> must be provided in order for the filter to\n * respond to proxy receptor requests. By configuring a shared\n * {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the\n * <code>CasAuthenticationFilter</code>, one can have the\n * <code>CasAuthenticationFilter</code> handling the proxying requirements for CAS.\n * <h2>Proxy Tickets</h2>\n * <p>\n * The filter can process tickets present on any url. This is useful when one wants to\n * process proxy tickets. In order for proxy tickets to get processed,\n * {@link ServiceProperties#isAuthenticateAllArtifacts()} must return <code>true</code>.\n * Additionally, if the request is already authenticated, authentication will <b>not</b>\n * occur. Last, {@link AuthenticationDetailsSource#buildDetails(Object)} must return a\n * {@link ServiceAuthenticationDetails}. This can be accomplished using the\n * {@link ServiceAuthenticationDetailsSource}. In this case,\n * {@link ServiceAuthenticationDetails#getServiceUrl()} will be used for the service url.\n * <p>\n * Processing the proxy ticket involves creating a\n * <code>CasServiceTicketAuthenticationToken</code> which uses\n * {@link CasServiceTicketAuthenticationToken#CAS_STATELESS_IDENTIFIER} for the\n * <code>principal</code> and the opaque ticket string as the <code>credentials</code>.\n * When a proxy ticket is successfully authenticated, the FilterChain continues and the\n * <code>authenticationSuccessHandler</code> is not used.\n * <h2>Notes about the <code>AuthenticationManager</code></h2>\n * <p>\n * The configured <code>AuthenticationManager</code> is expected to provide a provider\n * that can recognise <code>CasServiceTicketAuthenticationToken</code>s containing this\n * special <code>principal</code> name, and process them accordingly by validation with\n * the CAS server. Additionally, it should be capable of using the result of\n * {@link ServiceAuthenticationDetails#getServiceUrl()} as the service when validating the\n * ticket.\n * <h2>Example Configuration</h2>\n * <p>\n * An example configuration that supports service tickets, obtaining proxy granting\n * tickets, and proxy tickets is illustrated below:\n *\n * <pre>\n * &lt;b:bean id=&quot;serviceProperties&quot;\n *     class=&quot;org.springframework.security.cas.ServiceProperties&quot;\n *     p:service=&quot;https://service.example.com/cas-sample/login/cas&quot;\n *     p:authenticateAllArtifacts=&quot;true&quot;/&gt;\n * &lt;b:bean id=&quot;casEntryPoint&quot;\n *     class=&quot;org.springframework.security.cas.web.CasAuthenticationEntryPoint&quot;\n *     p:serviceProperties-ref=&quot;serviceProperties&quot; p:loginUrl=&quot;https://login.example.org/cas/login&quot; /&gt;\n * &lt;b:bean id=&quot;casFilter&quot;\n *     class=&quot;org.springframework.security.cas.web.CasAuthenticationFilter&quot;\n *     p:authenticationManager-ref=&quot;authManager&quot;\n *     p:serviceProperties-ref=&quot;serviceProperties&quot;\n *     p:proxyGrantingTicketStorage-ref=&quot;pgtStorage&quot;\n *     p:proxyReceptorUrl=&quot;/login/cas/proxyreceptor&quot;&gt;\n *     &lt;b:property name=&quot;authenticationDetailsSource&quot;&gt;\n *         &lt;b:bean class=&quot;org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource&quot;/&gt;\n *     &lt;/b:property&gt;\n *     &lt;b:property name=&quot;authenticationFailureHandler&quot;&gt;\n *         &lt;b:bean class=&quot;org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler&quot;\n *             p:defaultFailureUrl=&quot;/casfailed.jsp&quot;/&gt;\n *     &lt;/b:property&gt;\n * &lt;/b:bean&gt;\n * &lt;!--\n *     NOTE: In a real application you should not use an in memory implementation. You will also want\n *           to ensure to clean up expired tickets by calling ProxyGrantingTicketStorage.cleanup()\n *  --&gt;\n * &lt;b:bean id=&quot;pgtStorage&quot; class=&quot;org.apereo.cas.client.proxy.ProxyGrantingTicketStorageImpl&quot;/&gt;\n * &lt;b:bean id=&quot;casAuthProvider&quot; class=&quot;org.springframework.security.cas.authentication.CasAuthenticationProvider&quot;\n *     p:serviceProperties-ref=&quot;serviceProperties&quot;\n *     p:key=&quot;casAuthProviderKey&quot;&gt;\n *     &lt;b:property name=&quot;authenticationUserDetailsService&quot;&gt;\n *         &lt;b:bean\n *             class=&quot;org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper&quot;&gt;\n *             &lt;b:constructor-arg ref=&quot;userService&quot; /&gt;\n *         &lt;/b:bean&gt;\n *     &lt;/b:property&gt;\n *     &lt;b:property name=&quot;ticketValidator&quot;&gt;\n *         &lt;b:bean\n *             class=&quot;org.apereo.cas.client.validation.Cas20ProxyTicketValidator&quot;\n *             p:acceptAnyProxy=&quot;true&quot;\n *             p:proxyCallbackUrl=&quot;https://service.example.com/cas-sample/login/cas/proxyreceptor&quot;\n *             p:proxyGrantingTicketStorage-ref=&quot;pgtStorage&quot;&gt;\n *             &lt;b:constructor-arg value=&quot;https://login.example.org/cas&quot; /&gt;\n *         &lt;/b:bean&gt;\n *     &lt;/b:property&gt;\n *     &lt;b:property name=&quot;statelessTicketCache&quot;&gt;\n *         &lt;b:bean class=&quot;org.springframework.security.cas.authentication.EhCacheBasedTicketCache&quot;&gt;\n *             &lt;b:property name=&quot;cache&quot;&gt;\n *                 &lt;b:bean class=&quot;net.sf.ehcache.Cache&quot;\n *                   init-method=&quot;initialise&quot;\n *                   destroy-method=&quot;dispose&quot;&gt;\n *                     &lt;b:constructor-arg value=&quot;casTickets&quot;/&gt;\n *                     &lt;b:constructor-arg value=&quot;50&quot;/&gt;\n *                     &lt;b:constructor-arg value=&quot;true&quot;/&gt;\n *                     &lt;b:constructor-arg value=&quot;false&quot;/&gt;\n *                     &lt;b:constructor-arg value=&quot;3600&quot;/&gt;\n *                     &lt;b:constructor-arg value=&quot;900&quot;/&gt;\n *                 &lt;/b:bean&gt;\n *             &lt;/b:property&gt;\n *         &lt;/b:bean&gt;\n *     &lt;/b:property&gt;\n * &lt;/b:bean&gt;\n * </pre>\n *\n * @author Ben Alex\n * @author Rob Winch\n */\npublic class CasAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n\t/**\n\t * The last portion of the receptor url, i.e. /proxy/receptor\n\t */\n\tprivate @Nullable RequestMatcher proxyReceptorMatcher;\n\n\t/**\n\t * The backing storage to store ProxyGrantingTicket requests.\n\t */\n\tprivate @Nullable ProxyGrantingTicketStorage proxyGrantingTicketStorage;\n\n\tprivate String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;\n\n\tprivate boolean authenticateAllArtifacts;\n\n\tprivate AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();\n\n\tprivate SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate RequestCache requestCache = new HttpSessionRequestCache();\n\n\tprivate final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\tpublic CasAuthenticationFilter() {\n\t\tsuper(\"/login/cas\");\n\t\tRequestMatcher processUri = pathPattern(\"/login/cas\");\n\t\tsetRequiresAuthenticationRequestMatcher(processUri);\n\t\tsetAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());\n\t\tsetSecurityContextRepository(this.securityContextRepository);\n\t}\n\n\t@Override\n\tprotected final void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tFilterChain chain, Authentication authResult) throws IOException, ServletException {\n\t\tboolean continueFilterChain = proxyTicketRequest(serviceTicketRequest(request, response), request);\n\t\tif (!continueFilterChain) {\n\t\t\tsuper.successfulAuthentication(request, response, chain, authResult);\n\t\t\treturn;\n\t\t}\n\t\tthis.logger.debug(\n\t\t\t\tLogMessage.format(\"Authentication success. Updating SecurityContextHolder to contain: %s\", authResult));\n\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authResult);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\tif (this.eventPublisher != null) {\n\t\t\tthis.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));\n\t\t}\n\t\tchain.doFilter(request, response);\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows AuthenticationException, IOException {\n\t\t// if the request is a proxy request process it and return null to indicate the\n\t\t// request has been processed\n\t\tif (proxyReceptorRequest(request)) {\n\t\t\tthis.logger.debug(\"Responding to proxy receptor request\");\n\t\t\tWebUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);\n\t\t\treturn null;\n\t\t}\n\t\tString serviceTicket = obtainArtifact(request);\n\t\tif (!StringUtils.hasText(serviceTicket)) {\n\t\t\tHttpSession session = request.getSession(false);\n\t\t\tif (session != null && session\n\t\t\t\t.getAttribute(CasGatewayAuthenticationRedirectFilter.CAS_GATEWAY_AUTHENTICATION_ATTR) != null) {\n\t\t\t\tthis.logger.debug(\"Failed authentication response from CAS gateway request\");\n\t\t\t\tsession.removeAttribute(CasGatewayAuthenticationRedirectFilter.CAS_GATEWAY_AUTHENTICATION_ATTR);\n\t\t\t\tSavedRequest savedRequest = this.requestCache.getRequest(request, response);\n\t\t\t\tif (savedRequest != null) {\n\t\t\t\t\tString redirectUrl = savedRequest.getRedirectUrl();\n\t\t\t\t\tthis.logger.debug(LogMessage.format(\"Redirecting to: %s\", redirectUrl));\n\t\t\t\t\tthis.requestCache.removeRequest(request, response);\n\t\t\t\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUrl);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.logger.debug(\"Failed to obtain an artifact (cas ticket)\");\n\t\t\tserviceTicket = \"\";\n\t\t}\n\t\tboolean serviceTicketRequest = serviceTicketRequest(request, response);\n\t\tCasServiceTicketAuthenticationToken authRequest = serviceTicketRequest\n\t\t\t\t? CasServiceTicketAuthenticationToken.stateful(serviceTicket)\n\t\t\t\t: CasServiceTicketAuthenticationToken.stateless(serviceTicket);\n\t\tauthRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\treturn this.getAuthenticationManager().authenticate(authRequest);\n\t}\n\n\t/**\n\t * If present, gets the artifact (CAS ticket) from the {@link HttpServletRequest}.\n\t * @param request\n\t * @return if present the artifact from the {@link HttpServletRequest}, else null\n\t */\n\tprotected String obtainArtifact(HttpServletRequest request) {\n\t\treturn request.getParameter(this.artifactParameter);\n\t}\n\n\t/**\n\t * Overridden to provide proxying capabilities.\n\t */\n\t@Override\n\tprotected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {\n\t\tfinal boolean serviceTicketRequest = serviceTicketRequest(request, response);\n\t\tfinal boolean result = serviceTicketRequest || proxyReceptorRequest(request)\n\t\t\t\t|| (proxyTicketRequest(serviceTicketRequest, request));\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(\"requiresAuthentication = \" + result);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} for proxy requests.\n\t * @param proxyFailureHandler\n\t */\n\tpublic final void setProxyAuthenticationFailureHandler(AuthenticationFailureHandler proxyFailureHandler) {\n\t\tAssert.notNull(proxyFailureHandler, \"proxyFailureHandler cannot be null\");\n\t\tthis.proxyFailureHandler = proxyFailureHandler;\n\t}\n\n\t/**\n\t * Wraps the {@link AuthenticationFailureHandler} to distinguish between handling\n\t * proxy ticket authentication failures and service ticket failures.\n\t */\n\t@Override\n\tpublic final void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {\n\t\tsuper.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));\n\t}\n\n\t/**\n\t * Use this {@code RequestMatcher} to match proxy receptor requests. Without setting\n\t * this matcher, {@link CasAuthenticationFilter} will not capture any proxy receptor\n\t * requests.\n\t * @param proxyReceptorMatcher the {@link RequestMatcher} to use\n\t * @since 6.5\n\t */\n\tpublic final void setProxyReceptorMatcher(RequestMatcher proxyReceptorMatcher) {\n\t\tAssert.notNull(proxyReceptorMatcher, \"proxyReceptorMatcher cannot be null\");\n\t\tthis.proxyReceptorMatcher = proxyReceptorMatcher;\n\t}\n\n\tpublic final void setProxyReceptorUrl(final String proxyReceptorUrl) {\n\t\tthis.proxyReceptorMatcher = pathPattern(proxyReceptorUrl);\n\t}\n\n\tpublic final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {\n\t\tthis.proxyGrantingTicketStorage = proxyGrantingTicketStorage;\n\t}\n\n\tpublic final void setServiceProperties(final ServiceProperties serviceProperties) {\n\t\tthis.artifactParameter = serviceProperties.getArtifactParameter();\n\t\tthis.authenticateAllArtifacts = serviceProperties.isAuthenticateAllArtifacts();\n\t}\n\n\t@Override\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tsuper.setSecurityContextRepository(securityContextRepository);\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t@Override\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tsuper.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Set the {@link RedirectStrategy} used to redirect to the saved request if there is\n\t * one saved. Defaults to {@link DefaultRedirectStrategy}.\n\t * @param redirectStrategy the redirect strategy to use\n\t * @since 6.3\n\t */\n\tpublic final void setRedirectStrategy(RedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n\t/**\n\t * The {@link RequestCache} used to retrieve the saved request in failed gateway\n\t * authentication scenarios.\n\t * @param requestCache the request cache to use\n\t * @since 6.3\n\t */\n\tpublic final void setRequestCache(RequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t/**\n\t * Indicates if the request is eligible to process a service ticket. This method\n\t * exists for readability.\n\t * @param request\n\t * @param response\n\t * @return\n\t */\n\tprivate boolean serviceTicketRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\tboolean result = super.requiresAuthentication(request, response);\n\t\tthis.logger.debug(LogMessage.format(\"serviceTicketRequest = %s\", result));\n\t\treturn result;\n\t}\n\n\t/**\n\t * Indicates if the request is eligible to process a proxy ticket.\n\t * @param request\n\t * @return\n\t */\n\tprivate boolean proxyTicketRequest(boolean serviceTicketRequest, HttpServletRequest request) {\n\t\tif (serviceTicketRequest) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean result = this.authenticateAllArtifacts && obtainArtifact(request) != null && !authenticated();\n\t\tthis.logger.debug(LogMessage.format(\"proxyTicketRequest = %s\", result));\n\t\treturn result;\n\t}\n\n\t/**\n\t * Determines if a user is already authenticated.\n\t * @return\n\t */\n\tprivate boolean authenticated() {\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\treturn this.trustResolver.isAuthenticated(authentication);\n\t}\n\n\t/**\n\t * Indicates if the request is eligible to be processed as the proxy receptor.\n\t * @param request\n\t * @return\n\t */\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tprivate boolean proxyReceptorRequest(HttpServletRequest request) {\n\t\tfinal boolean result = proxyReceptorConfigured() && this.proxyReceptorMatcher.matches(request);\n\t\tthis.logger.debug(LogMessage.format(\"proxyReceptorRequest = %s\", result));\n\t\treturn result;\n\t}\n\n\t/**\n\t * Determines if the {@link CasAuthenticationFilter} is configured to handle the proxy\n\t * receptor requests.\n\t * @return\n\t */\n\tprivate boolean proxyReceptorConfigured() {\n\t\tfinal boolean result = this.proxyGrantingTicketStorage != null && this.proxyReceptorMatcher != null;\n\t\tthis.logger.debug(LogMessage.format(\"proxyReceptorConfigured = %s\", result));\n\t\treturn result;\n\t}\n\n\t/**\n\t * A wrapper for the AuthenticationFailureHandler that will flex the\n\t * {@link AuthenticationFailureHandler} that is used. The value\n\t * {@link CasAuthenticationFilter#setProxyAuthenticationFailureHandler(AuthenticationFailureHandler)}\n\t * will be used for proxy requests that fail. The value\n\t * {@link CasAuthenticationFilter#setAuthenticationFailureHandler(AuthenticationFailureHandler)}\n\t * will be used for service tickets that fail.\n\t */\n\tprivate class CasAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n\t\tprivate final AuthenticationFailureHandler serviceTicketFailureHandler;\n\n\t\tCasAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {\n\t\t\tAssert.notNull(failureHandler, \"failureHandler\");\n\t\t\tthis.serviceTicketFailureHandler = failureHandler;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthenticationException exception) throws IOException, ServletException {\n\t\t\tif (serviceTicketRequest(request, response)) {\n\t\t\t\tthis.serviceTicketFailureHandler.onAuthenticationFailure(request, response, exception);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tCasAuthenticationFilter.this.proxyFailureHandler.onAuthenticationFailure(request, response, exception);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/web/CasGatewayAuthenticationRedirectFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apereo.cas.client.util.CommonUtils;\nimport org.apereo.cas.client.util.WebUtils;\n\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Redirects the request to the CAS server appending {@code gateway=true} to the URL. Upon\n * redirection, the {@link ServiceProperties#isSendRenew()} is ignored and considered as\n * {@code false} to align with the specification says that the {@code sendRenew} parameter\n * is not compatible with the {@code gateway} parameter. See the <a href=\n * \"https://apereo.github.io/cas/6.6.x/protocol/CAS-Protocol-V2-Specification.html#:~:text=This%20parameter%20is%20not%20compatible%20with%20the%20%E2%80%9Crenew%E2%80%9D%20parameter.%20Behavior%20is%20undefined%20if%20both%20are%20set.\">CAS\n * Protocol Specification</a> for more details. To allow other filters to know if the\n * request is a gateway request, this filter creates a session and add an attribute with\n * name {@link #CAS_GATEWAY_AUTHENTICATION_ATTR} which can be checked by other filters if\n * needed. It is recommended that this filter is placed after\n * {@link CasAuthenticationFilter} if it is defined.\n *\n * @author Michael Remond\n * @author Jerome LELEU\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic final class CasGatewayAuthenticationRedirectFilter extends GenericFilterBean {\n\n\tpublic static final String CAS_GATEWAY_AUTHENTICATION_ATTR = \"CAS_GATEWAY_AUTHENTICATION\";\n\n\tprivate final String casLoginUrl;\n\n\tprivate final ServiceProperties serviceProperties;\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate RequestCache requestCache = new HttpSessionRequestCache();\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\t/**\n\t * Constructs a new instance of this class\n\t * @param serviceProperties the {@link ServiceProperties}\n\t */\n\tpublic CasGatewayAuthenticationRedirectFilter(String casLoginUrl, ServiceProperties serviceProperties) {\n\t\tAssert.hasText(casLoginUrl, \"casLoginUrl cannot be null or empty\");\n\t\tAssert.notNull(serviceProperties, \"serviceProperties cannot be null\");\n\t\tthis.casLoginUrl = casLoginUrl;\n\t\tthis.serviceProperties = serviceProperties;\n\t\tthis.requestMatcher = new CasGatewayResolverRequestMatcher(this.serviceProperties);\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\n\t\tHttpServletRequest request = (HttpServletRequest) req;\n\t\tHttpServletResponse response = (HttpServletResponse) res;\n\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.requestCache.saveRequest(request, response);\n\t\tHttpSession session = request.getSession(true);\n\t\tsession.setAttribute(CAS_GATEWAY_AUTHENTICATION_ATTR, true);\n\t\tString urlEncodedService = WebUtils.constructServiceUrl(request, response, this.serviceProperties.getService(),\n\t\t\t\tnull, this.serviceProperties.getServiceParameter(), this.serviceProperties.getArtifactParameter(),\n\t\t\t\ttrue);\n\t\tString redirectUrl = CommonUtils.constructRedirectUrl(this.casLoginUrl,\n\t\t\t\tthis.serviceProperties.getServiceParameter(), urlEncodedService, false, true);\n\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUrl);\n\t}\n\n\t/**\n\t * Sets the {@link RequestMatcher} used to trigger this filter. Defaults to\n\t * {@link CasGatewayResolverRequestMatcher}.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\t/**\n\t * Sets the {@link RequestCache} used to store the current request to be replayed\n\t * after redirect from the CAS server. Defaults to {@link HttpSessionRequestCache}.\n\t * @param requestCache the {@link RequestCache} to use\n\t */\n\tpublic void setRequestCache(RequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/web/CasGatewayResolverRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apereo.cas.client.authentication.DefaultGatewayResolverImpl;\nimport org.apereo.cas.client.authentication.GatewayResolver;\n\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link RequestMatcher} implementation that delegates the check to an instance of\n * {@link GatewayResolver}. The request is marked as \"gatewayed\" using the configured\n * {@link GatewayResolver} to avoid infinite loop.\n *\n * @author Michael Remond\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic final class CasGatewayResolverRequestMatcher implements RequestMatcher {\n\n\tprivate final ServiceProperties serviceProperties;\n\n\tprivate GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();\n\n\tpublic CasGatewayResolverRequestMatcher(ServiceProperties serviceProperties) {\n\t\tAssert.notNull(serviceProperties, \"serviceProperties cannot be null\");\n\t\tthis.serviceProperties = serviceProperties;\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\tboolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, this.serviceProperties.getService());\n\t\tif (!wasGatewayed) {\n\t\t\tthis.gatewayStorage.storeGatewayInformation(request, this.serviceProperties.getService());\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Sets the {@link GatewayResolver} to check if the request was already gatewayed.\n\t * Defaults to {@link DefaultGatewayResolverImpl}\n\t * @param gatewayStorage the {@link GatewayResolver} to use. Cannot be null.\n\t */\n\tpublic void setGatewayStorage(GatewayResolver gatewayStorage) {\n\t\tAssert.notNull(gatewayStorage, \"gatewayStorage cannot be null\");\n\t\tthis.gatewayStorage = gatewayStorage;\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetails.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web.authentication;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.regex.Pattern;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.cas.authentication.ServiceAuthenticationDetails;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * A default implementation of {@link ServiceAuthenticationDetails} that figures out the\n * value for {@link #getServiceUrl()} by inspecting the current {@link HttpServletRequest}\n * and using the current URL minus the artifact and the corresponding value.\n *\n * @author Rob Winch\n * @author Ngoc Nhan\n */\nfinal class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails\n\t\timplements ServiceAuthenticationDetails {\n\n\tprivate static final long serialVersionUID = 6192409090610517700L;\n\n\tprivate final String serviceUrl;\n\n\t/**\n\t * Creates a new instance\n\t * @param request the current {@link HttpServletRequest} to obtain the\n\t * {@link #getServiceUrl()} from.\n\t * @param artifactPattern the {@link Pattern} that will be used to clean up the query\n\t * string from containing the artifact name and value. This can be created using\n\t * {@link #createArtifactPattern(String)}.\n\t */\n\tDefaultServiceAuthenticationDetails(@Nullable String casService, HttpServletRequest request,\n\t\t\tPattern artifactPattern) throws MalformedURLException {\n\t\tsuper(request);\n\t\tURL casServiceUrl = new URL(casService);\n\t\tint port = getServicePort(casServiceUrl);\n\t\tfinal String query = getQueryString(request, artifactPattern);\n\t\tthis.serviceUrl = UrlUtils.buildFullRequestUrl(casServiceUrl.getProtocol(), casServiceUrl.getHost(), port,\n\t\t\t\trequest.getRequestURI(), query);\n\t}\n\n\t/**\n\t * Returns the current URL minus the artifact parameter and its value, if present.\n\t * @see org.springframework.security.cas.authentication.ServiceAuthenticationDetails#getServiceUrl()\n\t */\n\t@Override\n\tpublic String getServiceUrl() {\n\t\treturn this.serviceUrl;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails that)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.serviceUrl.equals(that.getServiceUrl());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = super.hashCode();\n\t\tresult = prime * result + this.serviceUrl.hashCode();\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder result = new StringBuilder();\n\t\tresult.append(super.toString());\n\t\tresult.append(\"ServiceUrl: \");\n\t\tresult.append(this.serviceUrl);\n\t\treturn result.toString();\n\t}\n\n\t/**\n\t * If present, removes the artifactParameterName and the corresponding value from the\n\t * query String.\n\t * @param request the current {@link HttpServletRequest} to obtain the\n\t * {@link #getServiceUrl()} from.\n\t * @param artifactPattern the {@link Pattern} that will be used to clean up the query\n\t * string from containing the artifact name and value. This can be created using\n\t * {@link #createArtifactPattern(String)}.\n\t * @return the query String minus the artifactParameterName and the corresponding\n\t * value.\n\t */\n\tprivate @Nullable String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {\n\t\tfinal String query = request.getQueryString();\n\t\tif (query == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString result = artifactPattern.matcher(query).replaceFirst(\"\");\n\t\tif (result.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\t// strip off the trailing & only if the artifact was the first query param\n\t\treturn result.startsWith(\"&\") ? result.substring(1) : result;\n\t}\n\n\t/**\n\t * Creates a {@link Pattern} that can be passed into the constructor. This allows the\n\t * {@link Pattern} to be reused for every instance of\n\t * {@link DefaultServiceAuthenticationDetails}.\n\t * @param artifactParameterName the artifactParameterName that is removed from the\n\t * current URL. The result becomes the service url. Cannot be null or an empty String.\n\t * @return a {@link Pattern}\n\t */\n\tstatic Pattern createArtifactPattern(String artifactParameterName) {\n\t\tAssert.hasLength(artifactParameterName, \"artifactParameterName is expected to have a length\");\n\t\treturn Pattern.compile(\"&?\" + Pattern.quote(artifactParameterName) + \"=[^&]*\");\n\t}\n\n\t/**\n\t * Gets the port from the casServiceURL ensuring to return the proper value if the\n\t * default port is being used.\n\t * @param casServiceUrl the casServerUrl to be used (i.e.\n\t * \"https://example.com/context/login/cas\")\n\t * @return the port that is configured for the casServerUrl\n\t */\n\tprivate static int getServicePort(URL casServiceUrl) {\n\t\tint port = casServiceUrl.getPort();\n\t\tif (port == -1) {\n\t\t\tport = casServiceUrl.getDefaultPort();\n\t\t}\n\t\treturn port;\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/web/authentication/ServiceAuthenticationDetailsSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web.authentication;\n\nimport java.net.MalformedURLException;\nimport java.util.regex.Pattern;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.cas.authentication.ServiceAuthenticationDetails;\nimport org.springframework.util.Assert;\n\n/**\n * The {@code AuthenticationDetailsSource} that is set on the\n * {@code CasAuthenticationFilter} should return a value that implements\n * {@code ServiceAuthenticationDetails} if the application needs to authenticate dynamic\n * service urls. The\n * {@code ServiceAuthenticationDetailsSource#buildDetails(HttpServletRequest)} creates a\n * default {@code ServiceAuthenticationDetails}.\n *\n * @author Rob Winch\n */\npublic class ServiceAuthenticationDetailsSource\n\t\timplements AuthenticationDetailsSource<HttpServletRequest, ServiceAuthenticationDetails> {\n\n\tprivate final Pattern artifactPattern;\n\n\tprivate ServiceProperties serviceProperties;\n\n\t/**\n\t * Creates an implementation that uses the specified ServiceProperties and the default\n\t * CAS artifactParameterName.\n\t * @param serviceProperties The ServiceProperties to use to construct the serviceUrl.\n\t */\n\tpublic ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties) {\n\t\tthis(serviceProperties, ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);\n\t}\n\n\t/**\n\t * Creates an implementation that uses the specified artifactParameterName\n\t * @param serviceProperties The ServiceProperties to use to construct the serviceUrl.\n\t * @param artifactParameterName the artifactParameterName that is removed from the\n\t * current URL. The result becomes the service url. Cannot be null and cannot be an\n\t * empty String.\n\t */\n\tpublic ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties, String artifactParameterName) {\n\t\tAssert.notNull(serviceProperties, \"serviceProperties cannot be null\");\n\t\tthis.serviceProperties = serviceProperties;\n\t\tthis.artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(artifactParameterName);\n\t}\n\n\t/**\n\t * @param context the {@code HttpServletRequest} object.\n\t * @return the {@code ServiceAuthenticationDetails} containing information about the\n\t * current request\n\t */\n\t@Override\n\tpublic ServiceAuthenticationDetails buildDetails(HttpServletRequest context) {\n\t\ttry {\n\t\t\treturn new DefaultServiceAuthenticationDetails(this.serviceProperties.getService(), context,\n\t\t\t\t\tthis.artifactPattern);\n\t\t}\n\t\tcatch (MalformedURLException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/web/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Authentication processing mechanisms which respond to the submission of authentication\n * credentials using CAS.\n */\n@NullMarked\npackage org.springframework.security.cas.web.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "cas/src/main/java/org/springframework/security/cas/web/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Authenticates standard web browser users via CAS.\n */\n@NullMarked\npackage org.springframework.security.cas.web;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apereo.cas.client.validation.Assertion;\nimport org.apereo.cas.client.validation.AssertionImpl;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\n\n/**\n * @author Scott Battaglia\n * @since 2.0\n *\n */\npublic abstract class AbstractStatelessTicketCacheTests {\n\n\tprotected CasAuthenticationToken getToken() {\n\t\tList<String> proxyList = new ArrayList<>();\n\t\tproxyList.add(\"https://localhost/newPortal/login/cas\");\n\t\tUser user = new User(\"rod\", \"password\", true, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tfinal Assertion assertion = new AssertionImpl(\"rod\");\n\t\treturn new CasAuthenticationToken(\"key\", user, \"ST-0-ER94xMJmn6pha35CQRoZ\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"), user, assertion);\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apereo.cas.client.validation.Assertion;\nimport org.apereo.cas.client.validation.AssertionImpl;\nimport org.apereo.cas.client.validation.TicketValidator;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link CasAuthenticationProvider}.\n *\n * @author Ben Alex\n * @author Scott Battaglia\n * @author Kim Youngwoong\n */\n@SuppressWarnings(\"unchecked\")\npublic class CasAuthenticationProviderTests {\n\n\tprivate UserDetails makeUserDetails() {\n\t\treturn new User(\"user\", \"password\", true, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t}\n\n\tprivate UserDetails makeUserDetailsFromAuthoritiesPopulator() {\n\t\treturn new User(\"user\", \"password\", true, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\"));\n\t}\n\n\tprivate ServiceProperties makeServiceProperties() {\n\t\tfinal ServiceProperties serviceProperties = new ServiceProperties();\n\t\tserviceProperties.setSendRenew(false);\n\t\tserviceProperties.setService(\"http://test.com\");\n\t\treturn serviceProperties;\n\t}\n\n\t@Test\n\tpublic void statefulAuthenticationIsSuccessful() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tStatelessTicketCache cache = new MockStatelessTicketCache();\n\t\tcap.setStatelessTicketCache(cache);\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.afterPropertiesSet();\n\t\tCasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful(\"ST-123\");\n\t\ttoken.setDetails(\"details\");\n\t\tAuthentication result = cap.authenticate(token);\n\t\t// Confirm ST-123 was NOT added to the cache\n\t\tassertThat(cache.getByTicketId(\"ST-456\") == null).isTrue();\n\t\tif (!(result instanceof CasAuthenticationToken)) {\n\t\t\tfail(\"Should have returned a CasAuthenticationToken\");\n\t\t}\n\t\tCasAuthenticationToken casResult = (CasAuthenticationToken) result;\n\t\tassertThat(casResult.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator());\n\t\tassertThat(casResult.getCredentials()).isEqualTo(\"ST-123\");\n\t\tassertThat(casResult.getAuthorities()).contains(new SimpleGrantedAuthority(\"ROLE_A\"));\n\t\tassertThat(casResult.getAuthorities()).contains(new SimpleGrantedAuthority(\"ROLE_B\"));\n\t\tassertThat(casResult.getKeyHash()).isEqualTo(cap.getKey().hashCode());\n\t\tassertThat(casResult.getDetails()).isEqualTo(\"details\");\n\t\t// Now confirm the CasAuthenticationToken is automatically re-accepted.\n\t\t// To ensure TicketValidator not called again, set it to deliver an exception...\n\t\tcap.setTicketValidator(new MockTicketValidator(false));\n\t\tAuthentication laterResult = cap.authenticate(result);\n\t\tassertThat(laterResult).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void statelessAuthenticationIsSuccessful() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tStatelessTicketCache cache = new MockStatelessTicketCache();\n\t\tcap.setStatelessTicketCache(cache);\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.afterPropertiesSet();\n\t\tCasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateless(\"ST-456\");\n\t\ttoken.setDetails(\"details\");\n\t\tAuthentication result = cap.authenticate(token);\n\t\t// Confirm ST-456 was added to the cache\n\t\tassertThat(cache.getByTicketId(\"ST-456\") != null).isTrue();\n\t\tif (!(result instanceof CasAuthenticationToken)) {\n\t\t\tfail(\"Should have returned a CasAuthenticationToken\");\n\t\t}\n\t\tassertThat(result.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator());\n\t\tassertThat(result.getCredentials()).isEqualTo(\"ST-456\");\n\t\tassertThat(result.getDetails()).isEqualTo(\"details\");\n\t\t// Now try to authenticate again. To ensure TicketValidator not\n\t\t// called again, set it to deliver an exception...\n\t\tcap.setTicketValidator(new MockTicketValidator(false));\n\t\t// Previously created CasServiceTicketAuthenticationToken is OK\n\t\tAuthentication newResult = cap.authenticate(token);\n\t\tassertThat(newResult.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator());\n\t\tassertThat(newResult.getCredentials()).isEqualTo(\"ST-456\");\n\t}\n\n\t@Test\n\tpublic void authenticateAllNullService() throws Exception {\n\t\tString serviceUrl = \"https://service/context\";\n\t\tServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);\n\t\tgiven(details.getServiceUrl()).willReturn(serviceUrl);\n\t\tTicketValidator validator = mock(TicketValidator.class);\n\t\tgiven(validator.validate(any(String.class), any(String.class))).willReturn(new AssertionImpl(\"rod\"));\n\t\tServiceProperties serviceProperties = makeServiceProperties();\n\t\tserviceProperties.setAuthenticateAllArtifacts(true);\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setTicketValidator(validator);\n\t\tcap.setServiceProperties(serviceProperties);\n\t\tcap.afterPropertiesSet();\n\t\tString ticket = \"ST-456\";\n\t\tCasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateless(ticket);\n\t\tAuthentication result = cap.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void authenticateAllAuthenticationIsSuccessful() throws Exception {\n\t\tString serviceUrl = \"https://service/context\";\n\t\tServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);\n\t\tgiven(details.getServiceUrl()).willReturn(serviceUrl);\n\t\tTicketValidator validator = mock(TicketValidator.class);\n\t\tgiven(validator.validate(any(String.class), any(String.class))).willReturn(new AssertionImpl(\"rod\"));\n\t\tServiceProperties serviceProperties = makeServiceProperties();\n\t\tserviceProperties.setAuthenticateAllArtifacts(true);\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setTicketValidator(validator);\n\t\tcap.setServiceProperties(serviceProperties);\n\t\tcap.afterPropertiesSet();\n\t\tString ticket = \"ST-456\";\n\t\tCasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateless(ticket);\n\t\tAuthentication result = cap.authenticate(token);\n\t\tverify(validator).validate(ticket, serviceProperties.getService());\n\t\tserviceProperties.setAuthenticateAllArtifacts(true);\n\t\tresult = cap.authenticate(token);\n\t\tverify(validator, times(2)).validate(ticket, serviceProperties.getService());\n\t\ttoken.setDetails(details);\n\t\tresult = cap.authenticate(token);\n\t\tverify(validator).validate(ticket, serviceUrl);\n\t\tserviceProperties.setAuthenticateAllArtifacts(false);\n\t\tserviceProperties.setService(null);\n\t\tcap.setServiceProperties(serviceProperties);\n\t\tcap.afterPropertiesSet();\n\t\tresult = cap.authenticate(token);\n\t\tverify(validator, times(2)).validate(ticket, serviceUrl);\n\t\ttoken.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));\n\t\tassertThatIllegalStateException().isThrownBy(() -> cap.authenticate(token));\n\t\tcap.setServiceProperties(null);\n\t\tcap.afterPropertiesSet();\n\t\tassertThatIllegalStateException().isThrownBy(() -> cap.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void missingTicketIdIsDetected() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tStatelessTicketCache cache = new MockStatelessTicketCache();\n\t\tcap.setStatelessTicketCache(cache);\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.afterPropertiesSet();\n\t\tCasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful(\"\");\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> cap.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void invalidKeyIsDetected() throws Exception {\n\t\tfinal Assertion assertion = new AssertionImpl(\"test\");\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tStatelessTicketCache cache = new MockStatelessTicketCache();\n\t\tcap.setStatelessTicketCache(cache);\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.afterPropertiesSet();\n\t\tCasAuthenticationToken token = new CasAuthenticationToken(\"WRONG_KEY\", makeUserDetails(), \"credentials\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"XX\"), makeUserDetails(), assertion);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> cap.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void detectsMissingAuthoritiesPopulator() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setStatelessTicketCache(new MockStatelessTicketCache());\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void detectsMissingKey() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setStatelessTicketCache(new MockStatelessTicketCache());\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void detectsMissingStatelessTicketCache() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\t// set this explicitly to null to test failure\n\t\tcap.setStatelessTicketCache(null);\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void detectsMissingTicketValidator() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setStatelessTicketCache(new MockStatelessTicketCache());\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet());\n\t}\n\n\t@Test\n\tpublic void gettersAndSettersMatch() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setStatelessTicketCache(new MockStatelessTicketCache());\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.afterPropertiesSet();\n\t\t// TODO disabled because why do we need to expose this?\n\t\t// assertThat(cap.getUserDetailsService() != null).isTrue();\n\t\tassertThat(cap.getKey()).isEqualTo(\"qwerty\");\n\t\tassertThat(cap.getStatelessTicketCache() != null).isTrue();\n\t\tassertThat(cap.getTicketValidator() != null).isTrue();\n\t}\n\n\t@Test\n\tpublic void ignoresClassesItDoesNotSupport() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setStatelessTicketCache(new MockStatelessTicketCache());\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.afterPropertiesSet();\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_A\");\n\t\tassertThat(cap.supports(TestingAuthenticationToken.class)).isFalse();\n\t\t// Try it anyway\n\t\tassertThat(cap.authenticate(token)).isNull();\n\t}\n\n\t@Test\n\tpublic void ignoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPrincipal() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setStatelessTicketCache(new MockStatelessTicketCache());\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.afterPropertiesSet();\n\t\tUsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(\"some_normal_user\",\n\t\t\t\t\"password\", AuthorityUtils.createAuthorityList(\"ROLE_A\"));\n\t\tassertThat(cap.authenticate(token)).isNull();\n\t}\n\n\t@Test\n\tpublic void supportsRequiredTokens() {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tassertThat(cap.supports(CasServiceTicketAuthenticationToken.class)).isTrue();\n\t\tassertThat(cap.supports(CasAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testSetUserDetailsChecker() throws AuthenticationException {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.afterPropertiesSet();\n\t\tCasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful(\"ST-123\");\n\n\t\tAtomicInteger checkCount = new AtomicInteger(0);\n\t\tUserDetailsChecker userDetailsChecker = new UserDetailsChecker() {\n\t\t\t@Override\n\t\t\tpublic void check(UserDetails user) {\n\t\t\t\tcheckCount.incrementAndGet();\n\t\t\t}\n\t\t};\n\t\tcap.setUserDetailsChecker(userDetailsChecker);\n\t\tcap.authenticate(token);\n\n\t\tassertThat(checkCount.get()).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSuccessfulThenIssuesFactor() throws Exception {\n\t\tCasAuthenticationProvider cap = new CasAuthenticationProvider();\n\t\tcap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());\n\t\tcap.setKey(\"qwerty\");\n\t\tStatelessTicketCache cache = new MockStatelessTicketCache();\n\t\tcap.setStatelessTicketCache(cache);\n\t\tcap.setServiceProperties(makeServiceProperties());\n\t\tcap.setTicketValidator(new MockTicketValidator(true));\n\t\tcap.afterPropertiesSet();\n\t\tCasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful(\"ST-123\");\n\t\ttoken.setDetails(\"details\");\n\t\tAuthentication result = cap.authenticate(token);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.CAS_AUTHORITY);\n\t}\n\n\tprivate class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserDetails(final Authentication token) throws UsernameNotFoundException {\n\t\t\treturn makeUserDetailsFromAuthoritiesPopulator();\n\t\t}\n\n\t}\n\n\tprivate class MockStatelessTicketCache implements StatelessTicketCache {\n\n\t\tprivate Map<String, CasAuthenticationToken> cache = new HashMap<>();\n\n\t\t@Override\n\t\tpublic CasAuthenticationToken getByTicketId(String serviceTicket) {\n\t\t\treturn this.cache.get(serviceTicket);\n\t\t}\n\n\t\t@Override\n\t\tpublic void putTicketInCache(CasAuthenticationToken token) {\n\t\t\tthis.cache.put(token.getCredentials().toString(), token);\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeTicketFromCache(CasAuthenticationToken token) {\n\t\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeTicketFromCache(String serviceTicket) {\n\t\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t\t}\n\n\t}\n\n\tprivate class MockTicketValidator implements TicketValidator {\n\n\t\tprivate boolean returnTicket;\n\n\t\tMockTicketValidator(boolean returnTicket) {\n\t\t\tthis.returnTicket = returnTicket;\n\t\t}\n\n\t\t@Override\n\t\tpublic Assertion validate(final String ticket, final String service) {\n\t\t\tif (this.returnTicket) {\n\t\t\t\treturn new AssertionImpl(\"rod\");\n\t\t\t}\n\t\t\tthrow new BadCredentialsException(\"As requested from mock\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apereo.cas.client.validation.Assertion;\nimport org.apereo.cas.client.validation.AssertionImpl;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link CasAuthenticationToken}.\n *\n * @author Ben Alex\n */\npublic class CasAuthenticationTokenTests {\n\n\tprivate final List<GrantedAuthority> ROLES = AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\");\n\n\tprivate UserDetails makeUserDetails() {\n\t\treturn makeUserDetails(\"user\");\n\t}\n\n\tprivate UserDetails makeUserDetails(final String name) {\n\t\treturn new User(name, \"password\", true, true, true, true, this.ROLES);\n\t}\n\n\t@Test\n\tpublic void testConstructorRejectsNulls() {\n\t\tAssertion assertion = new AssertionImpl(\"test\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken(null, makeUserDetails(),\n\t\t\t\t\"Password\", this.ROLES, makeUserDetails(), assertion));\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new CasAuthenticationToken(\"key\", null, \"Password\", this.ROLES, makeUserDetails(), assertion));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken(\"key\", makeUserDetails(), null,\n\t\t\t\tthis.ROLES, makeUserDetails(), assertion));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken(\"key\", makeUserDetails(),\n\t\t\t\t\"Password\", this.ROLES, makeUserDetails(), null));\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES, null, assertion));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken(\"key\", makeUserDetails(),\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_1\", null), makeUserDetails(), assertion));\n\t}\n\n\t@Test\n\tpublic void constructorWhenEmptyKeyThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new CasAuthenticationToken(\"\", \"user\", \"password\", Collections.<GrantedAuthority>emptyList(),\n\t\t\t\t\t\tnew User(\"user\", \"password\", Collections.<GrantedAuthority>emptyList()), null));\n\t}\n\n\t@Test\n\tpublic void testEqualsWhenEqual() {\n\t\tfinal Assertion assertion = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken token1 = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion);\n\t\tCasAuthenticationToken token2 = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion);\n\t\tassertThat(token2).isEqualTo(token1);\n\t}\n\n\t@Test\n\tpublic void testGetters() {\n\t\t// Build the proxy list returned in the ticket from CAS\n\t\tfinal Assertion assertion = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken token = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion);\n\t\tassertThat(token.getKeyHash()).isEqualTo(\"key\".hashCode());\n\t\tassertThat(token.getPrincipal()).isEqualTo(makeUserDetails());\n\t\tassertThat(token.getCredentials()).isEqualTo(\"Password\");\n\t\tassertThat(token.getAuthorities()).contains(new SimpleGrantedAuthority(\"ROLE_ONE\"));\n\t\tassertThat(token.getAuthorities()).contains(new SimpleGrantedAuthority(\"ROLE_TWO\"));\n\t\tassertThat(token.getAssertion()).isEqualTo(assertion);\n\t\tassertThat(token.getUserDetails().getUsername()).isEqualTo(makeUserDetails().getUsername());\n\t}\n\n\t@Test\n\tpublic void testNoArgConstructorDoesntExist() {\n\t\tassertThatExceptionOfType(NoSuchMethodException.class)\n\t\t\t.isThrownBy(() -> CasAuthenticationToken.class.getDeclaredConstructor((Class[]) null));\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToAbstractParentEqualsCheck() {\n\t\tfinal Assertion assertion = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken token1 = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion);\n\t\tCasAuthenticationToken token2 = new CasAuthenticationToken(\"key\", makeUserDetails(\"OTHER_NAME\"), \"Password\",\n\t\t\t\tthis.ROLES, makeUserDetails(), assertion);\n\t\tassertThat(!token1.equals(token2)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToKey() {\n\t\tfinal Assertion assertion = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken token1 = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion);\n\t\tCasAuthenticationToken token2 = new CasAuthenticationToken(\"DIFFERENT_KEY\", makeUserDetails(), \"Password\",\n\t\t\t\tthis.ROLES, makeUserDetails(), assertion);\n\t\tassertThat(!token1.equals(token2)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToAssertion() {\n\t\tfinal Assertion assertion = new AssertionImpl(\"test\");\n\t\tfinal Assertion assertion2 = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken token1 = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion);\n\t\tCasAuthenticationToken token2 = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion2);\n\t\tassertThat(!token1.equals(token2)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testSetAuthenticated() {\n\t\tfinal Assertion assertion = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken token = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion);\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t\ttoken.setAuthenticated(false);\n\t\tassertThat(!token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testToString() {\n\t\tfinal Assertion assertion = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken token = new CasAuthenticationToken(\"key\", makeUserDetails(), \"Password\", this.ROLES,\n\t\t\t\tmakeUserDetails(), assertion);\n\t\tString result = token.toString();\n\t\tassertThat(result.lastIndexOf(\"Credentials (Service/Proxy Ticket):\") != -1).isTrue();\n\t}\n\n\t@Test\n\tpublic void toBuilderWhenApplyThenCopies() {\n\t\tAssertion assertionOne = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken factorOne = new CasAuthenticationToken(\"key\", \"alice\", \"pass\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"), PasswordEncodedUser.user(), assertionOne);\n\t\tAssertion assertionTwo = new AssertionImpl(\"test\");\n\t\tCasAuthenticationToken factorTwo = new CasAuthenticationToken(\"yek\", \"bob\", \"ssap\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"), PasswordEncodedUser.admin(), assertionTwo);\n\t\tCasAuthenticationToken authentication = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.key(\"yek\")\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.credentials(factorTwo.getCredentials())\n\t\t\t.userDetails(factorTwo.getUserDetails())\n\t\t\t.assertion(factorTwo.getAssertion())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());\n\t\tassertThat(authentication.getKeyHash()).isEqualTo(factorTwo.getKeyHash());\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(factorTwo.getPrincipal());\n\t\tassertThat(authentication.getCredentials()).isEqualTo(factorTwo.getCredentials());\n\t\tassertThat(authentication.getUserDetails()).isEqualTo(factorTwo.getUserDetails());\n\t\tassertThat(authentication.getAssertion()).isEqualTo(factorTwo.getAssertion());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/authentication/NullStatelessTicketCacheTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Test cases for the @link {@link NullStatelessTicketCache}\n *\n * @author Scott Battaglia\n *\n */\npublic class NullStatelessTicketCacheTests extends AbstractStatelessTicketCacheTests {\n\n\tprivate StatelessTicketCache cache = new NullStatelessTicketCache();\n\n\t@Test\n\tpublic void testGetter() {\n\t\tassertThat(this.cache.getByTicketId(null)).isNull();\n\t\tassertThat(this.cache.getByTicketId(\"test\")).isNull();\n\t}\n\n\t@Test\n\tpublic void testInsertAndGet() {\n\t\tfinal CasAuthenticationToken token = getToken();\n\t\tthis.cache.putTicketInCache(token);\n\t\tassertThat(this.cache.getByTicketId((String) token.getCredentials())).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCacheTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.authentication;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.concurrent.ConcurrentMapCacheManager;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests\n * {@link org.springframework.security.cas.authentication.SpringCacheBasedTicketCache}.\n *\n * @author Marten Deinum\n * @since 3.2\n */\npublic class SpringCacheBasedTicketCacheTests extends AbstractStatelessTicketCacheTests {\n\n\tprivate static CacheManager cacheManager;\n\n\t@BeforeAll\n\tpublic static void initCacheManaer() {\n\t\tcacheManager = new ConcurrentMapCacheManager();\n\t\tcacheManager.getCache(\"castickets\");\n\t}\n\n\t@Test\n\tpublic void testCacheOperation() throws Exception {\n\t\tSpringCacheBasedTicketCache cache = new SpringCacheBasedTicketCache(cacheManager.getCache(\"castickets\"));\n\t\tfinal CasAuthenticationToken token = getToken();\n\t\t// Check it gets stored in the cache\n\t\tcache.putTicketInCache(token);\n\t\tassertThat(cache.getByTicketId(\"ST-0-ER94xMJmn6pha35CQRoZ\")).isEqualTo(token);\n\t\t// Check it gets removed from the cache\n\t\tcache.removeTicketFromCache(getToken());\n\t\tassertThat(cache.getByTicketId(\"ST-0-ER94xMJmn6pha35CQRoZ\")).isNull();\n\t\t// Check it doesn't return values for null or unknown service tickets\n\t\tassertThat(cache.getByTicketId(null)).isNull();\n\t\tassertThat(cache.getByTicketId(\"UNKNOWN_SERVICE_TICKET\")).isNull();\n\t}\n\n\t@Test\n\tpublic void testStartupDetectsMissingCache() throws Exception {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SpringCacheBasedTicketCache(null));\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/jackson/CasAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\n\nimport org.apereo.cas.client.authentication.AttributePrincipalImpl;\nimport org.apereo.cas.client.validation.Assertion;\nimport org.apereo.cas.client.validation.AssertionImpl;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.cas.authentication.CasAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.jackson.SecurityJacksonModules;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class CasAuthenticationTokenMixinTests {\n\n\tprivate static final String KEY = \"casKey\";\n\n\tprivate static final String PASSWORD = \"\\\"1234\\\"\";\n\n\tprivate static final Date START_DATE = new Date();\n\n\tprivate static final Date END_DATE = new Date();\n\n\tpublic static final String AUTHORITY_JSON = \"{\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\", \\\"authority\\\": \\\"ROLE_USER\\\"}\";\n\n\tpublic static final String AUTHORITIES_SET_JSON = \"[\\\"java.util.Collections$UnmodifiableSet\\\", [\" + AUTHORITY_JSON\n\t\t\t+ \"]]\";\n\n\tpublic static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", [\"\n\t\t\t+ AUTHORITY_JSON + \"]]\";\n\n\t// @formatter:off\n\tpublic static final String USER_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.core.userdetails.User\\\", \"\n\t\t+ \"\\\"username\\\": \\\"admin\\\",\"\n\t\t+ \" \\\"password\\\": \" + PASSWORD + \", \"\n\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t+ \"\\\"enabled\\\": true, \"\n\t\t+ \"\\\"authorities\\\": \" + AUTHORITIES_SET_JSON\n\t+ \"}\";\n\t// @formatter:on\n\tprivate static final String CAS_TOKEN_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.cas.authentication.CasAuthenticationToken\\\", \"\n\t\t\t+ \"\\\"keyHash\\\": \" + KEY.hashCode() + \",\" + \"\\\"principal\\\": \" + USER_JSON + \", \" + \"\\\"credentials\\\": \"\n\t\t\t+ PASSWORD + \", \" + \"\\\"authorities\\\": \" + AUTHORITIES_ARRAYLIST_JSON + \",\" + \"\\\"userDetails\\\": \" + USER_JSON\n\t\t\t+ \",\" + \"\\\"authenticated\\\": true, \" + \"\\\"details\\\": null,\" + \"\\\"assertion\\\": {\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.apereo.cas.client.validation.AssertionImpl\\\", \" + \"\\\"principal\\\": {\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.apereo.cas.client.authentication.AttributePrincipalImpl\\\", \"\n\t\t\t+ \"\\\"name\\\": \\\"assertName\\\", \" + \"\\\"attributes\\\": {\\\"@class\\\": \\\"java.util.Collections$EmptyMap\\\"}, \"\n\t\t\t+ \"\\\"proxyGrantingTicket\\\": null, \" + \"\\\"proxyRetriever\\\": null\" + \"}, \"\n\t\t\t+ \"\\\"validFromDate\\\": [\\\"java.util.Date\\\", \" + START_DATE.getTime() + \"], \"\n\t\t\t+ \"\\\"validUntilDate\\\": [\\\"java.util.Date\\\", \" + END_DATE.getTime() + \"],\"\n\t\t\t+ \"\\\"authenticationDate\\\": [\\\"java.util.Date\\\", \" + START_DATE.getTime() + \"], \"\n\t\t\t+ \"\\\"attributes\\\": {\\\"@class\\\": \\\"java.util.Collections$EmptyMap\\\"},\"\n\t\t\t+ \"\\\"context\\\": {\\\"@class\\\":\\\"java.util.HashMap\\\"}\" + \"}\" + \"}\";\n\n\tprivate static final String CAS_TOKEN_CLEARED_JSON = CAS_TOKEN_JSON.replaceFirst(PASSWORD, \"null\");\n\n\tprotected JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tpublic void serializeCasAuthenticationTest() throws JSONException {\n\t\tCasAuthenticationToken token = createCasAuthenticationToken();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(CAS_TOKEN_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeCasAuthenticationTestAfterEraseCredentialInvoked() throws JSONException {\n\t\tCasAuthenticationToken token = createCasAuthenticationToken();\n\t\ttoken.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(CAS_TOKEN_CLEARED_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeCasAuthenticationTestAfterEraseCredentialInvoked() {\n\t\tCasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_CLEARED_JSON, CasAuthenticationToken.class);\n\t\tassertThat(((UserDetails) token.getPrincipal()).getPassword()).isNull();\n\t}\n\n\t@Test\n\tpublic void deserializeCasAuthenticationTest() throws IOException {\n\t\tCasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_JSON, CasAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(((User) token.getPrincipal()).getUsername()).isEqualTo(\"admin\");\n\t\tassertThat(((User) token.getPrincipal()).getPassword()).isEqualTo(\"1234\");\n\t\tassertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class);\n\t\tassertThat(token.getKeyHash()).isEqualTo(KEY.hashCode());\n\t\tassertThat(token.getUserDetails().getAuthorities()).extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsOnly(\"ROLE_USER\");\n\t\tassertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE);\n\t\tassertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE);\n\t\tassertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE);\n\t\tassertThat(token.getAssertion().getPrincipal().getName()).isEqualTo(\"assertName\");\n\t\tassertThat(token.getAssertion().getAttributes()).hasSize(0);\n\t}\n\n\tprivate CasAuthenticationToken createCasAuthenticationToken() {\n\t\tUser principal = new User(\"admin\", \"1234\", Collections.singletonList(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t\tCollection<? extends GrantedAuthority> authorities = Collections\n\t\t\t.singletonList(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tAssertion assertion = new AssertionImpl(new AttributePrincipalImpl(\"assertName\"), START_DATE, END_DATE,\n\t\t\t\tSTART_DATE, Collections.<String, Object>emptyMap());\n\t\treturn new CasAuthenticationToken(KEY, principal, principal.getPassword(), authorities,\n\t\t\t\tnew User(\"admin\", \"1234\", authorities), assertion);\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/jackson2/CasAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.jackson2;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.apereo.cas.client.authentication.AttributePrincipalImpl;\nimport org.apereo.cas.client.validation.Assertion;\nimport org.apereo.cas.client.validation.AssertionImpl;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.cas.authentication.CasAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\n@SuppressWarnings(\"removal\")\npublic class CasAuthenticationTokenMixinTests {\n\n\tprivate static final String KEY = \"casKey\";\n\n\tprivate static final String PASSWORD = \"\\\"1234\\\"\";\n\n\tprivate static final Date START_DATE = new Date();\n\n\tprivate static final Date END_DATE = new Date();\n\n\tpublic static final String AUTHORITY_JSON = \"{\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\", \\\"authority\\\": \\\"ROLE_USER\\\"}\";\n\n\tpublic static final String AUTHORITIES_SET_JSON = \"[\\\"java.util.Collections$UnmodifiableSet\\\", [\" + AUTHORITY_JSON\n\t\t\t+ \"]]\";\n\n\tpublic static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", [\"\n\t\t\t+ AUTHORITY_JSON + \"]]\";\n\n\t// @formatter:off\n\tpublic static final String USER_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.core.userdetails.User\\\", \"\n\t\t+ \"\\\"username\\\": \\\"admin\\\",\"\n\t\t+ \" \\\"password\\\": \" + PASSWORD + \", \"\n\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t+ \"\\\"enabled\\\": true, \"\n\t\t+ \"\\\"authorities\\\": \" + AUTHORITIES_SET_JSON\n\t+ \"}\";\n\t// @formatter:on\n\tprivate static final String CAS_TOKEN_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.cas.authentication.CasAuthenticationToken\\\", \"\n\t\t\t+ \"\\\"keyHash\\\": \" + KEY.hashCode() + \",\" + \"\\\"principal\\\": \" + USER_JSON + \", \" + \"\\\"credentials\\\": \"\n\t\t\t+ PASSWORD + \", \" + \"\\\"authorities\\\": \" + AUTHORITIES_ARRAYLIST_JSON + \",\" + \"\\\"userDetails\\\": \" + USER_JSON\n\t\t\t+ \",\" + \"\\\"authenticated\\\": true, \" + \"\\\"details\\\": null,\" + \"\\\"assertion\\\": {\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.apereo.cas.client.validation.AssertionImpl\\\", \" + \"\\\"principal\\\": {\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.apereo.cas.client.authentication.AttributePrincipalImpl\\\", \"\n\t\t\t+ \"\\\"name\\\": \\\"assertName\\\", \" + \"\\\"attributes\\\": {\\\"@class\\\": \\\"java.util.Collections$EmptyMap\\\"}, \"\n\t\t\t+ \"\\\"proxyGrantingTicket\\\": null, \" + \"\\\"proxyRetriever\\\": null\" + \"}, \"\n\t\t\t+ \"\\\"validFromDate\\\": [\\\"java.util.Date\\\", \" + START_DATE.getTime() + \"], \"\n\t\t\t+ \"\\\"validUntilDate\\\": [\\\"java.util.Date\\\", \" + END_DATE.getTime() + \"],\"\n\t\t\t+ \"\\\"authenticationDate\\\": [\\\"java.util.Date\\\", \" + START_DATE.getTime() + \"], \"\n\t\t\t+ \"\\\"attributes\\\": {\\\"@class\\\": \\\"java.util.Collections$EmptyMap\\\"},\"\n\t\t\t+ \"\\\"context\\\": {\\\"@class\\\":\\\"java.util.HashMap\\\"}\" + \"}\" + \"}\";\n\n\tprivate static final String CAS_TOKEN_CLEARED_JSON = CAS_TOKEN_JSON.replaceFirst(PASSWORD, \"null\");\n\n\tprotected ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tpublic void serializeCasAuthenticationTest() throws JsonProcessingException, JSONException {\n\t\tCasAuthenticationToken token = createCasAuthenticationToken();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(CAS_TOKEN_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeCasAuthenticationTestAfterEraseCredentialInvoked()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tCasAuthenticationToken token = createCasAuthenticationToken();\n\t\ttoken.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(CAS_TOKEN_CLEARED_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeCasAuthenticationTestAfterEraseCredentialInvoked() throws Exception {\n\t\tCasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_CLEARED_JSON, CasAuthenticationToken.class);\n\t\tassertThat(((UserDetails) token.getPrincipal()).getPassword()).isNull();\n\t}\n\n\t@Test\n\tpublic void deserializeCasAuthenticationTest() throws IOException {\n\t\tCasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_JSON, CasAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(((User) token.getPrincipal()).getUsername()).isEqualTo(\"admin\");\n\t\tassertThat(((User) token.getPrincipal()).getPassword()).isEqualTo(\"1234\");\n\t\tassertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class);\n\t\tassertThat(token.getKeyHash()).isEqualTo(KEY.hashCode());\n\t\tassertThat(token.getUserDetails().getAuthorities()).extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsOnly(\"ROLE_USER\");\n\t\tassertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE);\n\t\tassertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE);\n\t\tassertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE);\n\t\tassertThat(token.getAssertion().getPrincipal().getName()).isEqualTo(\"assertName\");\n\t\tassertThat(token.getAssertion().getAttributes()).hasSize(0);\n\t}\n\n\tprivate CasAuthenticationToken createCasAuthenticationToken() {\n\t\tUser principal = new User(\"admin\", \"1234\", Collections.singletonList(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t\tCollection<? extends GrantedAuthority> authorities = Collections\n\t\t\t.singletonList(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tAssertion assertion = new AssertionImpl(new AttributePrincipalImpl(\"assertName\"), START_DATE, END_DATE,\n\t\t\t\tSTART_DATE, Collections.<String, Object>emptyMap());\n\t\treturn new CasAuthenticationToken(KEY, principal, principal.getPassword(), authorities,\n\t\t\t\tnew User(\"admin\", \"1234\", authorities), assertion);\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.userdetails;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apereo.cas.client.authentication.AttributePrincipal;\nimport org.apereo.cas.client.validation.Assertion;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests {\n\n\t@Test\n\tpublic void correctlyExtractsNamedAttributesFromAssertionAndConvertsThemToAuthorities() {\n\t\tGrantedAuthorityFromAssertionAttributesUserDetailsService uds = new GrantedAuthorityFromAssertionAttributesUserDetailsService(\n\t\t\t\tnew String[] { \"a\", \"b\", \"c\", \"d\" });\n\t\tuds.setConvertToUpperCase(false);\n\t\tAssertion assertion = mock(Assertion.class);\n\t\tAttributePrincipal principal = mock(AttributePrincipal.class);\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(\"a\", Arrays.asList(\"role_a1\", \"role_a2\"));\n\t\tattributes.put(\"b\", \"role_b\");\n\t\tattributes.put(\"c\", \"role_c\");\n\t\tattributes.put(\"d\", null);\n\t\tattributes.put(\"someother\", \"unused\");\n\t\tgiven(assertion.getPrincipal()).willReturn(principal);\n\t\tgiven(principal.getAttributes()).willReturn(attributes);\n\t\tgiven(principal.getName()).willReturn(\"somebody\");\n\t\tCasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, \"ticket\");\n\t\tUserDetails user = uds.loadUserDetails(token);\n\t\tSet<String> roles = AuthorityUtils.authorityListToSet(user.getAuthorities());\n\t\tassertThat(roles).containsExactlyInAnyOrder(\"role_a1\", \"role_a2\", \"role_b\", \"role_c\");\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.web.RedirectStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link CasAuthenticationEntryPoint}.\n *\n * @author Ben Alex\n */\npublic class CasAuthenticationEntryPointTests {\n\n\t@Test\n\tpublic void testDetectsMissingLoginFormUrl() throws Exception {\n\t\tCasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();\n\t\tep.setServiceProperties(new ServiceProperties());\n\t\tassertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet)\n\t\t\t.withMessage(\"loginUrl must be specified\");\n\t}\n\n\t@Test\n\tpublic void testDetectsMissingServiceProperties() throws Exception {\n\t\tCasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();\n\t\tep.setLoginUrl(\"https://cas/login\");\n\t\tassertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet)\n\t\t\t.withMessage(\"serviceProperties must be specified\");\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tCasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();\n\t\tep.setLoginUrl(\"https://cas/login\");\n\t\tassertThat(ep.getLoginUrl()).isEqualTo(\"https://cas/login\");\n\t\tep.setServiceProperties(new ServiceProperties());\n\t\tassertThat(ep.getServiceProperties() != null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWithRenewFalse() throws Exception {\n\t\tServiceProperties sp = new ServiceProperties();\n\t\tsp.setSendRenew(false);\n\t\tsp.setService(\"https://mycompany.com/bigWebApp/login/cas\");\n\t\tCasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();\n\t\tep.setLoginUrl(\"https://cas/login\");\n\t\tep.setServiceProperties(sp);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/some_path\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.afterPropertiesSet();\n\t\tep.commence(request, response, null);\n\t\tassertThat(\n\t\t\t\t\"https://cas/login?service=\" + URLEncoder.encode(\"https://mycompany.com/bigWebApp/login/cas\", \"UTF-8\"))\n\t\t\t.isEqualTo(response.getRedirectedUrl());\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWithRenewTrue() throws Exception {\n\t\tServiceProperties sp = new ServiceProperties();\n\t\tsp.setSendRenew(true);\n\t\tsp.setService(\"https://mycompany.com/bigWebApp/login/cas\");\n\t\tCasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();\n\t\tep.setLoginUrl(\"https://cas/login\");\n\t\tep.setServiceProperties(sp);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/some_path\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.afterPropertiesSet();\n\t\tep.commence(request, response, null);\n\t\tassertThat(\"https://cas/login?service=\"\n\t\t\t\t+ URLEncoder.encode(\"https://mycompany.com/bigWebApp/login/cas\", \"UTF-8\") + \"&renew=true\")\n\t\t\t.isEqualTo(response.getRedirectedUrl());\n\t}\n\n\t@Test\n\tvoid setRedirectStrategyThenUses() throws IOException {\n\t\tCasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();\n\t\tServiceProperties sp = new ServiceProperties();\n\n\t\tsp.setService(\"https://mycompany.com/login/cas\");\n\t\tep.setServiceProperties(sp);\n\t\tep.setLoginUrl(\"https://cas/login\");\n\n\t\tRedirectStrategy redirectStrategy = mock();\n\n\t\tep.setRedirectStrategy(redirectStrategy);\n\t\tMockHttpServletRequest req = new MockHttpServletRequest();\n\t\tMockHttpServletResponse res = new MockHttpServletResponse();\n\n\t\tep.commence(req, res, new BadCredentialsException(\"bad credentials\"));\n\n\t\tverify(redirectStrategy).sendRedirect(eq(req), eq(res),\n\t\t\t\teq(\"https://cas/login?service=https%3A%2F%2Fmycompany.com%2Flogin%2Fcas\"));\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpSession;\nimport org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests {@link CasAuthenticationFilter}.\n *\n * @author Ben Alex\n * @author Rob Winch\n */\npublic class CasAuthenticationFilterTests {\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tfilter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));\n\t\tfilter.setProxyReceptorUrl(\"/someurl\");\n\t\tfilter.setServiceProperties(new ServiceProperties());\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() throws Exception {\n\t\tMockHttpServletRequest request = post(\"/login/cas\").param(\"ticket\", \"ST-0-ER94xMJmn6pha35CQRoZ\").build();\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tfilter.setAuthenticationManager((a) -> a);\n\t\tassertThat(filter.requiresAuthentication(request, new MockHttpServletResponse())).isTrue();\n\t\tAuthentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());\n\t\tassertThat(result != null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNullServiceTicketHandledGracefully() throws Exception {\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tfilter.setAuthenticationManager((a) -> {\n\t\t\tthrow new BadCredentialsException(\"Rejected\");\n\t\t});\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(\n\t\t\t\t() -> filter.attemptAuthentication(new MockHttpServletRequest(), new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void testRequiresAuthenticationFilterProcessUrl() {\n\t\tString url = \"/login/cas\";\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tfilter.setFilterProcessesUrl(url);\n\t\tMockHttpServletRequest request = post(url).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(filter.requiresAuthentication(request, response)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testRequiresAuthenticationProxyRequest() {\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tMockHttpServletRequest request = get(\"/pgtCallback\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(filter.requiresAuthentication(request, response)).isFalse();\n\t\tfilter.setProxyReceptorUrl(request.getServletPath());\n\t\tassertThat(filter.requiresAuthentication(request, response)).isFalse();\n\t\tfilter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));\n\t\tassertThat(filter.requiresAuthentication(request, response)).isTrue();\n\t\trequest = get(\"/other\").build();\n\t\tassertThat(filter.requiresAuthentication(request, response)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testRequiresAuthenticationAuthAll() {\n\t\tServiceProperties properties = new ServiceProperties();\n\t\tproperties.setAuthenticateAllArtifacts(true);\n\t\tString url = \"/login/cas\";\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tfilter.setFilterProcessesUrl(url);\n\t\tfilter.setServiceProperties(properties);\n\t\tMockHttpServletRequest request = post(url).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(filter.requiresAuthentication(request, response)).isTrue();\n\t\trequest = post(\"/other\").build();\n\t\tassertThat(filter.requiresAuthentication(request, response)).isFalse();\n\t\trequest.setParameter(properties.getArtifactParameter(), \"value\");\n\t\tassertThat(filter.requiresAuthentication(request, response)).isTrue();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new AnonymousAuthenticationToken(\"key\", \"principal\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\")));\n\t\tassertThat(filter.requiresAuthentication(request, response)).isTrue();\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(\"un\", \"principal\"));\n\t\tassertThat(filter.requiresAuthentication(request, response)).isTrue();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"un\", \"principal\", \"ROLE_ANONYMOUS\"));\n\t\tassertThat(filter.requiresAuthentication(request, response)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testAuthenticateProxyUrl() throws Exception {\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tMockHttpServletRequest request = get(\"/pgtCallback\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));\n\t\tfilter.setProxyReceptorUrl(request.getServletPath());\n\t\tassertThat(filter.attemptAuthentication(request, response)).isNull();\n\t}\n\n\t@Test\n\tpublic void testDoFilterAuthenticateAll() throws Exception {\n\t\tAuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);\n\t\tAuthenticationManager manager = mock(AuthenticationManager.class);\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"un\", \"pwd\", \"ROLE_USER\");\n\t\tgiven(manager.authenticate(any(Authentication.class))).willReturn(authentication);\n\t\tServiceProperties serviceProperties = new ServiceProperties();\n\t\tserviceProperties.setAuthenticateAllArtifacts(true);\n\t\tMockHttpServletRequest request = post(\"/authenticate\").param(\"ticket\", \"ST-1-123\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tfilter.setServiceProperties(serviceProperties);\n\t\tfilter.setAuthenticationSuccessHandler(successHandler);\n\t\tfilter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));\n\t\tfilter.setAuthenticationManager(manager);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull()\n\t\t\t.withFailMessage(\"Authentication should not be null\");\n\t\tverify(chain).doFilter(request, response);\n\t\tverifyNoInteractions(successHandler);\n\t\t// validate for when the filterProcessUrl matches\n\t\tfilter.setFilterProcessesUrl(request.getServletPath());\n\t\tSecurityContextHolder.clearContext();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(chain);\n\t\tverify(successHandler).onAuthenticationSuccess(request, response, authentication);\n\t}\n\n\t// SEC-1592\n\t@Test\n\tpublic void testChainNotInvokedForProxyReceptor() throws Exception {\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tMockHttpServletRequest request = get(\"/pgtCallback\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));\n\t\tfilter.setProxyReceptorUrl(request.getServletPath());\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoInteractions(chain);\n\t}\n\n\t@Test\n\tpublic void successfulAuthenticationWhenProxyRequestThenSavesSecurityContext() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER, \"ticket\");\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tServiceProperties serviceProperties = new ServiceProperties();\n\t\tserviceProperties.setAuthenticateAllArtifacts(true);\n\t\tfilter.setServiceProperties(serviceProperties);\n\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tReflectionTestUtils.setField(filter, \"securityContextRepository\", securityContextRepository);\n\n\t\tfilter.successfulAuthentication(request, response, new MockFilterChain(), mock(Authentication.class));\n\t\tverify(securityContextRepository).saveContext(any(SecurityContext.class), eq(request), eq(response));\n\t}\n\n\t@Test\n\tpublic void attemptAuthenticationWhenNoServiceTicketAndIsGatewayRequestThenRedirectToSavedRequestAndClearAttribute()\n\t\t\tthrows Exception {\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpSession session = request.getSession(true);\n\t\tsession.setAttribute(CasGatewayAuthenticationRedirectFilter.CAS_GATEWAY_AUTHENTICATION_ATTR, true);\n\n\t\tnew HttpSessionRequestCache().saveRequest(request, response);\n\n\t\tAuthentication authn = filter.attemptAuthentication(request, response);\n\t\tassertThat(authn).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(302);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"http://localhost?continue\");\n\t\tassertThat(session.getAttribute(CasGatewayAuthenticationRedirectFilter.CAS_GATEWAY_AUTHENTICATION_ATTR))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tvoid successfulAuthenticationWhenSecurityContextRepositorySetThenUses() throws ServletException, IOException {\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tfilter.setSecurityContextRepository(securityContextRepository);\n\t\tfilter.successfulAuthentication(new MockHttpServletRequest(), new MockHttpServletResponse(),\n\t\t\t\tnew MockFilterChain(), mock(Authentication.class));\n\t\tverify(securityContextRepository).saveContext(any(SecurityContext.class), any(), any());\n\t}\n\n\t@Test\n\tvoid successfulAuthenticationWhenSecurityContextHolderStrategySetThenUses() throws ServletException, IOException {\n\t\tSecurityContextHolderStrategy securityContextRepository = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(securityContextRepository.createEmptyContext()).willReturn(new SecurityContextImpl());\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tfilter.setSecurityContextHolderStrategy(securityContextRepository);\n\t\tfilter.successfulAuthentication(new MockHttpServletRequest(), new MockHttpServletResponse(),\n\t\t\t\tnew MockFilterChain(), mock(Authentication.class));\n\t\tverify(securityContextRepository).setContext(any(SecurityContext.class));\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationWhenProxyRequestMatcherThenMatches() {\n\t\tCasAuthenticationFilter filter = new CasAuthenticationFilter();\n\t\tMockHttpServletRequest request = get(\"/pgtCallback\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(filter.requiresAuthentication(request, response)).isFalse();\n\t\tfilter.setProxyReceptorMatcher(pathPattern(request.getServletPath()));\n\t\tassertThat(filter.requiresAuthentication(request, response)).isFalse();\n\t\tfilter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));\n\t\tassertThat(filter.requiresAuthentication(request, response)).isTrue();\n\t\trequest = get(\"/other\").build();\n\t\tassertThat(filter.requiresAuthentication(request, response)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/web/CasGatewayAuthenticationRedirectFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.web.savedrequest.RequestCache;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link CasGatewayAuthenticationRedirectFilter}.\n *\n * @author Jerome LELEU\n * @author Marcus da Coregio\n */\npublic class CasGatewayAuthenticationRedirectFilterTests {\n\n\tprivate static final String CAS_LOGIN_URL = \"http://mycasserver/login\";\n\n\tCasGatewayAuthenticationRedirectFilter filter = new CasGatewayAuthenticationRedirectFilter(CAS_LOGIN_URL,\n\t\t\tserviceProperties());\n\n\t@Test\n\tvoid doFilterWhenMatchesThenSavesRequestAndSavesAttributeAndSendRedirect() throws IOException, ServletException {\n\t\tRequestCache requestCache = mock();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter.setRequestMatcher((req) -> true);\n\t\tthis.filter.setRequestCache(requestCache);\n\t\tthis.filter.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getHeader(\"Location\"))\n\t\t\t.isEqualTo(\"http://mycasserver/login?service=http%3A%2F%2Flocalhost%2Flogin%2Fcas&gateway=true\");\n\t\tverify(requestCache).saveRequest(request, response);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotMatchThenContinueFilter() throws ServletException, IOException {\n\t\tthis.filter.setRequestMatcher((req) -> false);\n\t\tFilterChain chain = mock();\n\t\tMockHttpServletResponse response = mock();\n\t\tthis.filter.doFilter(new MockHttpServletRequest(), response, chain);\n\t\tverify(chain).doFilter(any(), any());\n\t\tverifyNoInteractions(response);\n\t}\n\n\t@Test\n\tvoid doFilterWhenSendRenewTrueThenIgnores() throws ServletException, IOException {\n\t\tServiceProperties serviceProperties = serviceProperties();\n\t\tserviceProperties.setSendRenew(true);\n\t\tthis.filter = new CasGatewayAuthenticationRedirectFilter(CAS_LOGIN_URL, serviceProperties);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter.setRequestMatcher((req) -> true);\n\t\tthis.filter.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getHeader(\"Location\"))\n\t\t\t.isEqualTo(\"http://mycasserver/login?service=http%3A%2F%2Flocalhost%2Flogin%2Fcas&gateway=true\");\n\t}\n\n\tprivate static ServiceProperties serviceProperties() {\n\t\tServiceProperties serviceProperties = new ServiceProperties();\n\t\tserviceProperties.setService(\"http://localhost/login/cas\");\n\t\treturn serviceProperties;\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/web/CasGatewayResolverRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.cas.ServiceProperties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link CasGatewayResolverRequestMatcher}.\n *\n * @author Marcus da Coregio\n */\nclass CasGatewayResolverRequestMatcherTests {\n\n\tCasGatewayResolverRequestMatcher matcher = new CasGatewayResolverRequestMatcher(new ServiceProperties());\n\n\t@Test\n\tvoid constructorWhenServicePropertiesNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CasGatewayResolverRequestMatcher(null))\n\t\t\t.withMessage(\"serviceProperties cannot be null\");\n\t}\n\n\t@Test\n\tvoid matchesWhenAlreadyGatewayedThenReturnsFalse() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.getSession().setAttribute(\"_const_cas_gateway_\", \"yes\");\n\t\tboolean matches = this.matcher.matches(request);\n\t\tassertThat(matches).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenNotGatewayedThenReturnsTrue() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tboolean matches = this.matcher.matches(request);\n\t\tassertThat(matches).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenNoSessionThenReturnsTrue() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(null);\n\t\tboolean matches = this.matcher.matches(request);\n\t\tassertThat(matches).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenNotGatewayedAndCheckedAgainThenSavesAsGatewayedAndReturnsFalse() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tboolean matches = this.matcher.matches(request);\n\t\tboolean secondMatch = this.matcher.matches(request);\n\t\tassertThat(matches).isTrue();\n\t\tassertThat(secondMatch).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/web/ServicePropertiesTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.cas.SamlServiceProperties;\nimport org.springframework.security.cas.ServiceProperties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link ServiceProperties}.\n *\n * @author Ben Alex\n */\npublic class ServicePropertiesTests {\n\n\t@Test\n\tpublic void detectsMissingService() throws Exception {\n\t\tServiceProperties sp = new ServiceProperties();\n\t\tassertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void nullServiceWhenAuthenticateAllTokens() throws Exception {\n\t\tServiceProperties sp = new ServiceProperties();\n\t\tsp.setAuthenticateAllArtifacts(true);\n\t\tassertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet);\n\t\tsp.setAuthenticateAllArtifacts(false);\n\t\tassertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() throws Exception {\n\t\tServiceProperties[] sps = { new ServiceProperties(), new SamlServiceProperties() };\n\t\tfor (ServiceProperties sp : sps) {\n\t\t\tsp.setSendRenew(false);\n\t\t\tassertThat(sp.isSendRenew()).isFalse();\n\t\t\tsp.setSendRenew(true);\n\t\t\tassertThat(sp.isSendRenew()).isTrue();\n\t\t\tsp.setArtifactParameter(\"notticket\");\n\t\t\tassertThat(sp.getArtifactParameter()).isEqualTo(\"notticket\");\n\t\t\tsp.setServiceParameter(\"notservice\");\n\t\t\tassertThat(sp.getServiceParameter()).isEqualTo(\"notservice\");\n\t\t\tsp.setService(\"https://mycompany.com/service\");\n\t\t\tassertThat(sp.getService()).isEqualTo(\"https://mycompany.com/service\");\n\t\t\tsp.afterPropertiesSet();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/java/org/springframework/security/cas/web/authentication/DefaultServiceAuthenticationDetailsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.cas.web.authentication;\n\nimport java.util.regex.Pattern;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.support.GenericXmlApplicationContext;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.cas.ServiceProperties;\nimport org.springframework.security.cas.authentication.ServiceAuthenticationDetails;\nimport org.springframework.security.web.util.UrlUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class DefaultServiceAuthenticationDetailsTests {\n\n\tprivate DefaultServiceAuthenticationDetails details;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate Pattern artifactPattern;\n\n\tprivate String casServiceUrl;\n\n\tprivate ConfigurableApplicationContext context;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.casServiceUrl = \"https://localhost:8443/j_spring_security_cas\";\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.request.setScheme(\"https\");\n\t\tthis.request.setServerName(\"localhost\");\n\t\tthis.request.setServerPort(8443);\n\t\tthis.request.setRequestURI(\"/cas-sample/secure/\");\n\t\tthis.artifactPattern = DefaultServiceAuthenticationDetails\n\t\t\t.createArtifactPattern(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getServiceUrlNullQuery() throws Exception {\n\t\tthis.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);\n\t\tassertThat(this.details.getServiceUrl()).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));\n\t}\n\n\t@Test\n\tpublic void getServiceUrlTicketOnlyParam() throws Exception {\n\t\tthis.request.setQueryString(\"ticket=123\");\n\t\tthis.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);\n\t\tString serviceUrl = this.details.getServiceUrl();\n\t\tthis.request.setQueryString(null);\n\t\tassertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));\n\t}\n\n\t@Test\n\tpublic void getServiceUrlTicketFirstMultiParam() throws Exception {\n\t\tthis.request.setQueryString(\"ticket=123&other=value\");\n\t\tthis.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);\n\t\tString serviceUrl = this.details.getServiceUrl();\n\t\tthis.request.setQueryString(\"other=value\");\n\t\tassertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));\n\t}\n\n\t@Test\n\tpublic void getServiceUrlTicketLastMultiParam() throws Exception {\n\t\tthis.request.setQueryString(\"other=value&ticket=123\");\n\t\tthis.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);\n\t\tString serviceUrl = this.details.getServiceUrl();\n\t\tthis.request.setQueryString(\"other=value\");\n\t\tassertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));\n\t}\n\n\t@Test\n\tpublic void getServiceUrlTicketMiddleMultiParam() throws Exception {\n\t\tthis.request.setQueryString(\"other=value&ticket=123&last=this\");\n\t\tthis.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);\n\t\tString serviceUrl = this.details.getServiceUrl();\n\t\tthis.request.setQueryString(\"other=value&last=this\");\n\t\tassertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));\n\t}\n\n\t@Test\n\tpublic void getServiceUrlDoesNotUseHostHeader() throws Exception {\n\t\tthis.casServiceUrl = \"https://example.com/j_spring_security_cas\";\n\t\tthis.request.setServerName(\"evil.com\");\n\t\tthis.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);\n\t\tassertThat(this.details.getServiceUrl()).isEqualTo(\"https://example.com/cas-sample/secure/\");\n\t}\n\n\t@Test\n\tpublic void getServiceUrlDoesNotUseHostHeaderExplicit() {\n\t\tthis.casServiceUrl = \"https://example.com/j_spring_security_cas\";\n\t\tthis.request.setServerName(\"evil.com\");\n\t\tServiceAuthenticationDetails details = loadServiceAuthenticationDetails(\n\t\t\t\t\"defaultserviceauthenticationdetails-explicit.xml\");\n\t\tassertThat(details.getServiceUrl()).isEqualTo(\"https://example.com/cas-sample/secure/\");\n\t}\n\n\tprivate ServiceAuthenticationDetails loadServiceAuthenticationDetails(String resourceName) {\n\t\tthis.context = new GenericXmlApplicationContext(getClass(), resourceName);\n\t\tServiceAuthenticationDetailsSource source = this.context.getBean(ServiceAuthenticationDetailsSource.class);\n\t\treturn source.buildDetails(this.request);\n\t}\n\n}\n"
  },
  {
    "path": "cas/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-explicit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\t   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t   xmlns:p=\"http://www.springframework.org/schema/p\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n\t   xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd\">\n\n\t<bean id=\"serviceProperties\"\n\t\t  class=\"org.springframework.security.cas.ServiceProperties\">\n\t\t<property name=\"service\"\n\t\t\t\t  value=\"https://example.com/j_spring_security_cas\"/>\n\t\t<property name=\"sendRenew\" value=\"false\"/>\n\t</bean>\n\t<bean id=\"serviceProperties2\"\n\t\t  class=\"org.springframework.security.cas.ServiceProperties\">\n\t\t<property name=\"service\"\n\t\t\t\t  value=\"https://example2.com/j_spring_security_cas\"/>\n\t\t<property name=\"sendRenew\" value=\"false\"/>\n\t</bean>\n\n\t<bean class=\"org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource\">\n\t\t<constructor-arg ref=\"serviceProperties\"/>\n\t</bean>\n</beans>"
  },
  {
    "path": "cas/src/test/resources/org/springframework/security/cas/web/authentication/defaultserviceauthenticationdetails-passivity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\t   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t   xmlns:p=\"http://www.springframework.org/schema/p\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n\t   xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd\">\n\n\t<bean id=\"serviceProperties\"\n\t\t  class=\"org.springframework.security.cas.ServiceProperties\">\n\t\t<property name=\"service\"\n\t\t\t\t  value=\"https://example.com/j_spring_security_cas\"/>\n\t\t<property name=\"sendRenew\" value=\"false\"/>\n\t</bean>\n\n\t<bean class=\"org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource\"/>\n</beans>"
  },
  {
    "path": "class_mapping_from_2.0.x.txt",
    "content": "Class Mapping from 2.0.x to 3.0.x\n------------------------------------\n\nApproximate mapping of classes which have new names, or new implementations in 3.0. These may not be a \nstraightforward replacement, but the listed classes and interfaces from 3.0 will give some indication of where to\nlook in the APIs when upgrading.\n\nAbstractProcessingFilter, AbstractAuthenticationProcessingFilter\nAbstractFallbackMethodDefinitionSource, AbstractFallbackMethodSecurityMetadataSource\nAnonymousProcessingFilter, AnonymousAuthenticationFilter\nAuthenticationFailureConcurrentLoginEvent\nAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter\nAuthenticationProcessingFilterEntryPoint, LoginUrlAuthenticationEntryPoint\n\nBasicProcessingFilter, BasicAuthenticationFilter\nBasicProcessingFilterEntryPoint, BasicAuthenticationEntryPoint\n\nCasProcessingFilter, CasAuthenticationFilter\nCasProcessingFilterEntryPoint, CasAuthenticationEntryPoint\nConcurrentSessionController, ConcurrentSessionControlStrategy (Now implemented through the SessionManagementFilter)\nConfigAttributeDefinition, Collection<ConfigAttribute>\n\nDefaultFilterInvocationDefinitionSource, DefaultFilterInvocationSecurityMetadataSource\nDigestProcessingFilter, DigestAuthenticationFilter\nDigestProcessingFilterEntryPoint, DigestAuthenticationEntryPoint\n\nFilterInvocationDefinitionSource, FilterInvocationSecurityMetadataSource\n\nHttpSessionContextIntegrationFilter, SecurityContextPersistenceFilter (see also SecurityContextRepository)\n\nJsr250MethodDefinitionSource, Jsr250MethodSecurityMetadataSource\n\nMapBasedMethodDefinitionSource, MapBasedMethodSecurityMetadataSource\nMethodDefinitionAttributes\nMethodDefinitionSource, MethodSecurityMetadataSource\nMethodDefinitionSourceAdvisor, MethodSecurityMetadataSourceAdvisor\nMethodDefinitionSourceEditor, MethodSecurityMetadataSourceEditor\n\nObjectDefinitionSource SecurityMetadataSource\nOpenIDAuthenticationProcessingFilter, OpenIDAuthenticationFilter\n\nRedirectUtils, DefaultRedirectStrategy\nRememberMeProcessingFilter, RememberMeAuthenticationFilter\nRequestHeaderPreAuthenticatedProcessingFilter, RequestHeaderAuthenticationFilter\n\nSecuredMethodDefinitionSource, SecuredAnnotationSecurityMetadataSource\nSessionFixationProtectionFilter, SessionManagementFilter (See also SessionAuthenticationStrategy, SessionFixationProtectionStrategy)\nSpringSecurityContextSource, LdapContextSource (from Spring LDAP 1.3, which introduced the ability to bind as a specific user)\nSwitchUserFilter, SwitchUserProcessingFilter\n\nTargetUrlResolver, AuthenticationSuccessHandler (see also AuthenticationFailureHandler)\nTargetUrlResolverImpl, SavedRequestAwareAuthenticationSuccessHandler (see also SimpleUrlAuthenticationSuccessHandler)\n\nWASSecurityHelper, DefaultWASUsernameAndGroupsExtractor\n\nX509PreAuthenticatedProcessingFilter, X509AuthenticationFilter\n\n"
  },
  {
    "path": "config/spring-security-config.gradle",
    "content": "import org.springframework.gradle.xsd.CreateVersionlessXsdTask\nimport trang.RncToXsd\n\napply plugin: 'io.spring.convention.spring-module'\napply plugin: 'trang'\napply plugin: 'security-kotlin'\napply plugin: 'test-compile-target-jdk25'\napply plugin: 'compile-warnings-error'\napply plugin:  'javadoc-warnings-error'\n\nconfigurations {\n\topensaml5 {\n\t\textendsFrom(optional, tests)\n\t}\n}\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\t// NB: Don't add other compile time dependencies to the config module as this breaks tooling\n\tapi project(':spring-security-core')\n\tapi 'org.springframework:spring-aop'\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\n\toptional project(':spring-security-access')\n\toptional project(':spring-security-data')\n\toptional project(':spring-security-ldap')\n\toptional project(':spring-security-messaging')\n\toptional project(path: ':spring-security-saml2-service-provider')\n\toptional project(':spring-security-oauth2-client')\n\toptional project(':spring-security-oauth2-jose')\n\toptional project(':spring-security-oauth2-resource-server')\n\toptional project(':spring-security-oauth2-authorization-server')\n\toptional project(':spring-security-rsocket')\n\toptional project(':spring-security-web')\n\toptional project(':spring-security-webauthn')\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'org.aspectj:aspectjweaver'\n\toptional 'org.springframework:spring-jdbc'\n\toptional 'org.springframework:spring-messaging'\n\toptional 'org.springframework:spring-tx'\n\toptional 'org.springframework:spring-webmvc'\n\toptional'org.springframework:spring-web'\n\toptional'org.springframework:spring-webflux'\n\toptional'org.springframework:spring-websocket'\n\toptional 'org.jetbrains.kotlin:kotlin-reflect'\n\toptional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'\n\toptional 'jakarta.annotation:jakarta.annotation-api'\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(':spring-security-aspects')\n\ttestImplementation project(':spring-security-cas')\n\ttestImplementation project(':spring-security-test')\n\ttestImplementation project(path : ':spring-security-access', configuration : 'tests')\n\ttestImplementation project(path : ':spring-security-core', configuration : 'tests')\n\ttestImplementation project(path : ':spring-security-ldap', configuration : 'tests')\n\ttestImplementation project(path : ':spring-security-oauth2-client', configuration : 'tests')\n\ttestImplementation project(path : ':spring-security-oauth2-resource-server', configuration : 'tests')\n\ttestImplementation project(path : ':spring-security-oauth2-authorization-server', configuration : 'tests')\n\ttestImplementation project(':spring-security-saml2-service-provider')\n\ttestImplementation project(path : ':spring-security-saml2-service-provider', configuration : 'tests')\n\ttestImplementation project(path : ':spring-security-web', configuration : 'tests')\n\ttestImplementation project(path : ':spring-security-webauthn', configuration : 'tests')\n\ttestImplementation \"jakarta.inject:jakarta.inject-api\"\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\ttestImplementation 'ch.qos.logback:logback-classic'\n\ttestImplementation 'io.projectreactor.netty:reactor-netty'\n\ttestImplementation 'io.rsocket:rsocket-transport-netty'\n\ttestImplementation 'jakarta.annotation:jakarta.annotation-api'\n\ttestImplementation 'jakarta.xml.bind:jakarta.xml.bind-api'\n\ttestImplementation 'jakarta.websocket:jakarta.websocket-api'\n\ttestImplementation 'jakarta.websocket:jakarta.websocket-client-api'\n\ttestImplementation 'ldapsdk:ldapsdk:4.1'\n\ttestImplementation('org.htmlunit:htmlunit') {\n\t\texclude group: 'commons-logging', module: 'commons-logging'\n\t\texclude group: 'xml-apis', module: 'xml-apis'\n\t}\n\ttestImplementation \"com.unboundid:unboundid-ldapsdk\"\n\ttestImplementation 'jakarta.persistence:jakarta.persistence-api'\n\ttestImplementation \"org.hibernate.orm:hibernate-core\"\n\ttestImplementation 'org.hsqldb:hsqldb'\n\ttestImplementation 'org.mockito:mockito-core'\n\ttestImplementation('org.seleniumhq.selenium:htmlunit3-driver') {\n\t\texclude group: 'commons-logging', module: 'commons-logging'\n\t\texclude group: 'xml-apis', module: 'xml-apis'\n\t}\n\ttestImplementation('org.seleniumhq.selenium:selenium-java') {\n\t\texclude group: 'commons-logging', module: 'commons-logging'\n\t\texclude group: 'io.netty', module: 'netty'\n\t}\n\ttestImplementation 'org.springframework.ldap:spring-ldap-core'\n\ttestImplementation 'org.springframework:spring-expression'\n\ttestImplementation 'org.springframework:spring-jdbc'\n\ttestImplementation 'org.springframework:spring-orm'\n\ttestImplementation 'org.springframework:spring-tx'\n\ttestImplementation 'org.springframework:spring-core-test'\n\ttestImplementation ('org.springframework.data:spring-data-jpa') {\n\t\texclude group: 'org.aspectj', module: 'aspectjrt'\n\t}\n\ttestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core'\n\ttestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor'\n\ttestImplementation 'io.mockk:mockk'\n\ttestImplementation 'org.junit.platform:junit-platform-launcher'\n\ttestImplementation 'org.apache.maven.resolver:maven-resolver-connector-basic'\n\ttestImplementation ('org.apache.maven.resolver:maven-resolver-impl') {\n\t\texclude(group: \"javax.annotation\", module: \"javax.annotation-api\")\n\t}\n\ttestImplementation ('org.apache.maven:maven-resolver-provider') {\n\t\texclude(group: \"javax.inject\", module: \"javax.inject\")\n\t\texclude(group: \"javax.annotation\", module: \"javax.annotation-api\")\n\t}\n\ttestImplementation ('org.apache.maven.resolver:maven-resolver-transport-http') {\n\t\texclude group: \"org.slf4j\", module: \"jcl-over-slf4j\"\n\t}\n\ttestImplementation libs.org.instancio.instancio.junit\n\ttestImplementation libs.org.eclipse.jetty.jetty.server\n\ttestImplementation libs.org.eclipse.jetty.jetty.servlet\n\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n\ttestRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'\n}\n\ndef rncToXsd = tasks.named('rncToXsd', RncToXsd)\nrncToXsd.configure {\n\trncDir = file('src/main/resources/org/springframework/security/config/')\n\txsdDir = rncDir\n\txslFile = new File(rncDir, 'spring-security.xsl')\n}\n\ndef versionlessXsd = tasks.register(\"versionlessXsd\", CreateVersionlessXsdTask) {\n\tinputFiles.from(rncToXsd.map { task -> project.fileTree(task.xsdDir) })\n\tversionlessXsdFile = project.layout.buildDirectory.file(\"versionlessXsd/spring-security.xsd\")\n}\n\ntasks.named('processResources', ProcessResources).configure {\n\tfrom(versionlessXsd) {\n\t\tinto 'org/springframework/security/config/'\n\t}\n\tfrom(rncToXsd) {\n\t\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n\t\tinto 'org/springframework/security/config/'\n\t}\n}\n\ntasks.named('sourcesJar', Jar).configure {\n\tfrom(rncToXsd) {\n\t\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n\t\tinto 'org/springframework/security/config/'\n\t}\n}\n\nconfigure(project.tasks.withType(Test)) {\n\tdoFirst {\n\t\tsystemProperties['springSecurityVersion'] = version\n\t}\n}\n\ntest {\n\tonOutput { descriptor, event ->\n\t\tif (!project.hasProperty('serialization')) {\n\t\t\treturn\n\t\t}\n\t\tif (descriptor.name=='listClassesMissingSerialVersion()') {\n\t\t\tlogger.lifecycle(event.message)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/LdapAuthenticationProviderBuilderSecurityBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.ldap;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\nimport java.util.List;\n\nimport javax.naming.directory.SearchControls;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;\nimport org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;\nimport org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.authentication.LdapAuthenticationProvider;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class LdapAuthenticationProviderBuilderSecurityBuilderTests {\n\n\tstatic Integer port;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Autowired\n\tprivate AuthenticationManager authenticationManager;\n\n\t@Test\n\tpublic void defaultConfiguration() {\n\t\tthis.spring.register(DefaultLdapConfig.class).autowire();\n\t\tLdapAuthenticationProvider provider = ldapProvider();\n\t\tLdapAuthoritiesPopulator authoritiesPopulator = getAuthoritiesPopulator(provider);\n\t\tassertThat(authoritiesPopulator).hasFieldOrPropertyWithValue(\"groupRoleAttribute\", \"cn\");\n\t\tassertThat(authoritiesPopulator).hasFieldOrPropertyWithValue(\"groupSearchBase\", \"\");\n\t\tassertThat(authoritiesPopulator).hasFieldOrPropertyWithValue(\"groupSearchFilter\", \"(uniqueMember={0})\");\n\t\tassertThat(authoritiesPopulator).extracting(\"searchControls\")\n\t\t\t.hasFieldOrPropertyWithValue(\"searchScope\", SearchControls.ONELEVEL_SCOPE);\n\t\tassertThat(ReflectionTestUtils.getField(getAuthoritiesMapper(provider), \"prefix\")).isEqualTo(\"ROLE_\");\n\t}\n\n\t@Test\n\tpublic void groupRolesCustom() {\n\t\tthis.spring.register(GroupRolesConfig.class).autowire();\n\t\tLdapAuthenticationProvider provider = ldapProvider();\n\n\t\tassertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), \"groupRoleAttribute\"))\n\t\t\t.isEqualTo(\"group\");\n\t}\n\n\t@Test\n\tpublic void groupSearchCustom() {\n\t\tthis.spring.register(GroupSearchConfig.class).autowire();\n\t\tLdapAuthenticationProvider provider = ldapProvider();\n\n\t\tassertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), \"groupSearchFilter\"))\n\t\t\t.isEqualTo(\"ou=groupName\");\n\t}\n\n\t@Test\n\tpublic void groupSubtreeSearchCustom() {\n\t\tthis.spring.register(GroupSubtreeSearchConfig.class).autowire();\n\t\tLdapAuthenticationProvider provider = ldapProvider();\n\n\t\tassertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), \"searchControls\"))\n\t\t\t.extracting(\"searchScope\")\n\t\t\t.isEqualTo(SearchControls.SUBTREE_SCOPE);\n\t}\n\n\t@Test\n\tpublic void rolePrefixCustom() {\n\t\tthis.spring.register(RolePrefixConfig.class).autowire();\n\t\tLdapAuthenticationProvider provider = ldapProvider();\n\n\t\tassertThat(ReflectionTestUtils.getField(getAuthoritiesMapper(provider), \"prefix\")).isEqualTo(\"role_\");\n\t}\n\n\t@Test\n\tpublic void bindAuthentication() throws Exception {\n\t\tthis.spring.register(BindAuthenticationConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bob\").withRoles(\"DEVELOPERS\"));\n\t}\n\n\t// SEC-2472\n\t@Test\n\tpublic void canUseCryptoPasswordEncoder() throws Exception {\n\t\tthis.spring.register(PasswordEncoderConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bcrypt\").password(\"password\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bcrypt\").withRoles(\"DEVELOPERS\"));\n\t}\n\n\tprivate LdapAuthenticationProvider ldapProvider() {\n\t\treturn ((List<LdapAuthenticationProvider>) ReflectionTestUtils.getField(this.authenticationManager,\n\t\t\t\t\"providers\"))\n\t\t\t.get(0);\n\t}\n\n\tprivate LdapAuthoritiesPopulator getAuthoritiesPopulator(LdapAuthenticationProvider provider) {\n\t\treturn (LdapAuthoritiesPopulator) ReflectionTestUtils.getField(provider, \"authoritiesPopulator\");\n\t}\n\n\tprivate GrantedAuthoritiesMapper getAuthoritiesMapper(LdapAuthenticationProvider provider) {\n\t\treturn (GrantedAuthoritiesMapper) ReflectionTestUtils.getField(provider, \"authoritiesMapper\");\n\t}\n\n\tstatic int getPort() throws IOException {\n\t\tif (port == null) {\n\t\t\tServerSocket socket = new ServerSocket(0);\n\t\t\tport = socket.getLocalPort();\n\t\t\tsocket.close();\n\t\t}\n\t\treturn port;\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultLdapConfig extends BaseLdapProviderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.contextSource(contextSource())\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)\n\t\t\t\tthrows Exception {\n\t\t\treturn authenticationConfiguration.getAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class GroupRolesConfig extends BaseLdapProviderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.contextSource(contextSource())\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\")\n\t\t\t\t\t.groupRoleAttribute(\"group\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)\n\t\t\t\tthrows Exception {\n\t\t\treturn authenticationConfiguration.getAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class GroupSearchConfig extends BaseLdapProviderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.contextSource(contextSource())\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\")\n\t\t\t\t\t.groupSearchFilter(\"ou=groupName\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)\n\t\t\t\tthrows Exception {\n\t\t\treturn authenticationConfiguration.getAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class GroupSubtreeSearchConfig extends BaseLdapProviderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.contextSource(contextSource())\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\")\n\t\t\t\t\t.groupSearchFilter(\"ou=groupName\")\n\t\t\t\t\t.groupSearchSubtree(true);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)\n\t\t\t\tthrows Exception {\n\t\t\treturn authenticationConfiguration.getAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RolePrefixConfig extends BaseLdapProviderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.contextSource(contextSource())\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\")\n\t\t\t\t\t.rolePrefix(\"role_\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)\n\t\t\t\tthrows Exception {\n\t\t\treturn authenticationConfiguration.getAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class BindAuthenticationConfig extends BaseLdapServerConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.contextSource(contextSource())\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\")\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)\n\t\t\t\tthrows Exception {\n\t\t\treturn authenticationConfiguration.getAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordEncoderConfig extends BaseLdapServerConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.contextSource(contextSource())\n\t\t\t\t\t.passwordEncoder(new BCryptPasswordEncoder())\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\")\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)\n\t\t\t\tthrows Exception {\n\t\t\treturn authenticationConfiguration.getAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tabstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapServer() throws Exception {\n\t\t\tUnboundIdContainer unboundIdContainer = new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\t\"classpath:/test-server.ldif\");\n\t\t\tunboundIdContainer.setPort(getPort());\n\t\t\treturn unboundIdContainer;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableGlobalAuthentication\n\t@Import(ObjectPostProcessorConfiguration.class)\n\tabstract static class BaseLdapProviderConfig {\n\n\t\t@Bean\n\t\tBaseLdapPathContextSource contextSource() throws Exception {\n\t\t\tDefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(\n\t\t\t\t\t\"ldap://127.0.0.1:\" + getPort() + \"/dc=springframework,dc=org\");\n\t\t\tcontextSource.setUserDn(\"uid=admin,ou=system\");\n\t\t\tcontextSource.setPassword(\"secret\");\n\t\t\tcontextSource.afterPropertiesSet();\n\t\t\treturn contextSource;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/LdapAuthenticationProviderConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.ldap;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.ldap.LdapAuthenticationProviderBuilderSecurityBuilderTests.BaseLdapProviderConfig;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class LdapAuthenticationProviderConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void authenticationManagerSupportMultipleDefaultLdapContextsWithPortsDynamicallyAllocated()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(MultiLdapAuthenticationProvidersConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bob\"));\n\t}\n\n\t@Test\n\tpublic void authenticationManagerSupportMultipleLdapContextWithDefaultRolePrefix() throws Exception {\n\t\tthis.spring.register(MultiLdapAuthenticationProvidersConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()\n\t\t\t\t.user(\"bob\")\n\t\t\t\t.password(\"bobspassword\");\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()\n\t\t\t\t.withUsername(\"bob\")\n\t\t\t\t.withRoles(\"DEVELOPERS\");\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(request).andExpect(expectedUser);\n\t}\n\n\t@Test\n\tpublic void authenticationManagerSupportMultipleLdapContextWithCustomRolePrefix() throws Exception {\n\t\tthis.spring.register(MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()\n\t\t\t\t.user(\"bob\")\n\t\t\t\t.password(\"bobspassword\");\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()\n\t\t\t\t.withUsername(\"bob\")\n\t\t\t\t.withRoles(\"ROL_\", new String[] { \"DEVELOPERS\" });\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(request).andExpect(expectedUser);\n\t}\n\n\t@Test\n\tpublic void authenticationManagerWhenPortZeroThenAuthenticates() throws Exception {\n\t\tthis.spring.register(LdapWithRandomPortConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()\n\t\t\t\t.user(\"bob\")\n\t\t\t\t.password(\"bobspassword\");\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()\n\t\t\t\t.withUsername(\"bob\");\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(request).andExpect(expectedUser);\n\t}\n\n\t@Test\n\tpublic void authenticationManagerWhenSearchSubtreeThenNestedGroupFound() throws Exception {\n\t\tthis.spring.register(GroupSubtreeSearchConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()\n\t\t\t\t.user(\"otherben\")\n\t\t\t\t.password(\"otherbenspassword\");\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()\n\t\t\t\t.withUsername(\"otherben\")\n\t\t\t\t.withRoles(\"SUBMANAGERS\", \"MANAGERS\", \"DEVELOPERS\");\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(request).andExpect(expectedUser);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MultiLdapAuthenticationProvidersConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\")\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\")\n\t\t\t\t\t.and()\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\")\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\")\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\")\n\t\t\t\t\t.rolePrefix(\"ROL_\")\n\t\t\t\t\t.and()\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\")\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\")\n\t\t\t\t\t.rolePrefix(\"RUOLO_\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class LdapWithRandomPortConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\")\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\")\n\t\t\t\t\t.contextSource()\n\t\t\t\t\t.port(0);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class GroupSubtreeSearchConfig extends BaseLdapProviderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\")\n\t\t\t\t\t.groupSearchSubtree(true)\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.ldap;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.support.LdapContextSource;\nimport org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.CustomAuthoritiesPopulatorConfig;\nimport org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.CustomLdapAuthenticationProviderConfig;\nimport org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig;\nimport org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.PasswordCompareLdapConfig;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceLdapAuthenticationProviderTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Autowired\n\tprivate FilterChainProxy filterChainProxy;\n\n\t@Test\n\tpublic void ldapAuthenticationProvider() throws Exception {\n\t\tthis.spring.register(LdapAuthenticationProviderConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()\n\t\t\t\t.user(\"bob\")\n\t\t\t\t.password(\"bobspassword\");\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()\n\t\t\t\t.withUsername(\"bob\");\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(request).andExpect(user);\n\t}\n\n\t@Test\n\tpublic void ldapAuthenticationProviderCustom() throws Exception {\n\t\tthis.spring.register(CustomLdapAuthenticationProviderConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()\n\t\t\t\t.user(\"bob\")\n\t\t\t\t.password(\"bobspassword\");\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()\n\t\t\t\t.withRoles(\"PREFIX_\", new String[] { \"DEVELOPERS\" });\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(request).andExpect(user);\n\t}\n\n\t// SEC-2490\n\t@Test\n\tpublic void ldapAuthenticationProviderCustomLdapAuthoritiesPopulator() throws Exception {\n\t\tLdapContextSource contextSource = new DefaultSpringSecurityContextSource(\n\t\t\t\t\"ldap://blah.example.com:789/dc=springframework,dc=org\");\n\t\tCustomAuthoritiesPopulatorConfig.LAP = new DefaultLdapAuthoritiesPopulator(contextSource, null) {\n\t\t\t@Override\n\t\t\tprotected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {\n\t\t\t\treturn new HashSet<>(AuthorityUtils.createAuthorityList(\"ROLE_EXTRA\"));\n\t\t\t}\n\t\t};\n\n\t\tthis.spring.register(CustomAuthoritiesPopulatorConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()\n\t\t\t\t.user(\"bob\")\n\t\t\t\t.password(\"bobspassword\");\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()\n\t\t\t\t.withRoles(\"EXTRA\");\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(request).andExpect(user);\n\t}\n\n\t@Test\n\tpublic void ldapAuthenticationProviderPasswordCompare() throws Exception {\n\t\tthis.spring.register(PasswordCompareLdapConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()\n\t\t\t\t.user(\"bcrypt\")\n\t\t\t\t.password(\"password\");\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated().withUsername(\"bcrypt\");\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(request).andExpect(user);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTestsConfigs.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.ldap;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.PersonContextMapper;\n\n/**\n * @author Rob Winch\n *\n */\npublic class NamespaceLdapAuthenticationProviderTestsConfigs {\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class LdapAuthenticationProviderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\"); // ldap-server@user-dn-pattern\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomLdapAuthenticationProviderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupRoleAttribute(\"cn\") // ldap-authentication-provider@group-role-attribute\n\t\t\t\t\t.groupSearchBase(\"ou=groups\") // ldap-authentication-provider@group-search-base\n\t\t\t\t\t.groupSearchFilter(\"(member={0})\") // ldap-authentication-provider@group-search-filter\n\t\t\t\t\t.rolePrefix(\"PREFIX_\") // ldap-authentication-provider@group-search-filter\n\t\t\t\t\t.userDetailsContextMapper(new PersonContextMapper()) // ldap-authentication-provider@user-context-mapper-ref / ldap-authentication-provider@user-details-class\n\t\t\t\t\t.userDnPatterns(\"uid={0},ou=people\") // ldap-authentication-provider@user-dn-pattern\n\t\t\t\t\t.userSearchBase(\"ou=users\") // ldap-authentication-provider@user-dn-pattern\n\t\t\t\t\t.userSearchFilter(\"(uid={0})\") // ldap-authentication-provider@user-search-filter\n\t\t\t\t\t// .contextSource(contextSource) // ldap-authentication-provider@server-ref\n\t\t\t\t\t.contextSource()\n\t\t\t\t\t\t.ldif(\"classpath:users.xldif\") // ldap-server@ldif\n\t\t\t\t\t\t.managerDn(\"uid=admin,ou=system\") // ldap-server@manager-dn\n\t\t\t\t\t\t.managerPassword(\"secret\") // ldap-server@manager-password\n\t\t\t\t\t\t.port(0) // ldap-server@port\n\t\t\t\t\t\t.root(\"dc=springframework,dc=org\"); // ldap-server@root\n\t\t\t// .url(\"ldap://localhost:33389/dc-springframework,dc=org\") this overrides root and port and is used for external\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthoritiesPopulatorConfig {\n\n\t\tstatic LdapAuthoritiesPopulator LAP;\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.userSearchFilter(\"(uid={0})\")\n\t\t\t\t\t.ldapAuthoritiesPopulator(LAP);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordCompareLdapConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.ldapAuthentication()\n\t\t\t\t\t.groupSearchBase(\"ou=groups\")\n\t\t\t\t\t.userSearchFilter(\"(uid={0})\")\n\t\t\t\t\t.passwordCompare()\n\t\t\t\t\t\t.passwordEncoder(new BCryptPasswordEncoder()) // ldap-authentication-provider/password-compare/password-encoder@ref\n\t\t\t\t\t\t.passwordAttribute(\"userPassword\"); // ldap-authentication-provider/password-compare@password-attribute\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/configurers/WebAuthnWebDriverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.configurers;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.assertj.core.api.AbstractStringAssert;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.server.ServerConnector;\nimport org.eclipse.jetty.servlet.FilterHolder;\nimport org.eclipse.jetty.servlet.ServletContextHandler;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.openqa.selenium.By;\nimport org.openqa.selenium.WebDriverException;\nimport org.openqa.selenium.WebElement;\nimport org.openqa.selenium.chrome.ChromeDriverService;\nimport org.openqa.selenium.chrome.ChromeOptions;\nimport org.openqa.selenium.chromium.HasCdp;\nimport org.openqa.selenium.devtools.HasDevTools;\nimport org.openqa.selenium.remote.Augmenter;\nimport org.openqa.selenium.remote.RemoteWebDriver;\nimport org.openqa.selenium.support.ui.FluentWait;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.env.Environment;\nimport org.springframework.mock.env.MockPropertySource;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.filter.DelegatingFilterProxy;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Webdriver-based tests for the WebAuthnConfigurer. This uses a full browser because\n * these features require Javascript and browser APIs to be available.\n *\n * @author Daniel Garnier-Moiroux\n */\n@Disabled\nclass WebAuthnWebDriverTests {\n\n\tprivate String baseUrl;\n\n\tprivate static ChromeDriverService driverService;\n\n\tprivate Server server;\n\n\tprivate RemoteWebDriver driver;\n\n\tprivate static final String USERNAME = \"user\";\n\n\tprivate static final String PASSWORD = \"password\";\n\n\tprivate String authenticatorId = null;\n\n\t@BeforeAll\n\tstatic void startChromeDriverService() throws Exception {\n\t\tdriverService = new ChromeDriverService.Builder().usingAnyFreePort().build();\n\t\tdriverService.start();\n\t}\n\n\t@AfterAll\n\tstatic void stopChromeDriverService() {\n\t\tdriverService.stop();\n\t}\n\n\t@BeforeEach\n\tvoid startServer() throws Exception {\n\t\t// Create the server on port 8080\n\t\tthis.server = new Server(0);\n\n\t\t// Set up the ServletContextHandler\n\t\tServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);\n\t\tcontextHandler.setContextPath(\"/\");\n\t\tthis.server.setHandler(contextHandler);\n\t\tthis.server.start();\n\t\tint serverPort = ((ServerConnector) this.server.getConnectors()[0]).getLocalPort();\n\t\tthis.baseUrl = \"http://localhost:\" + serverPort;\n\n\t\t// Set up Spring application context\n\t\tAnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();\n\t\tapplicationContext.register(WebAuthnConfiguration.class);\n\t\tapplicationContext.setServletContext(contextHandler.getServletContext());\n\n\t\t// Add the server port\n\t\tMockPropertySource propertySource = new MockPropertySource().withProperty(\"server.port\", serverPort);\n\t\tapplicationContext.getEnvironment().getPropertySources().addFirst(propertySource);\n\n\t\t// Register the filter chain\n\t\tDelegatingFilterProxy filterProxy = new DelegatingFilterProxy(\"securityFilterChain\", applicationContext);\n\t\tFilterHolder filterHolder = new FilterHolder(filterProxy);\n\t\tcontextHandler.addFilter(filterHolder, \"/*\", null);\n\t}\n\n\t@AfterEach\n\tvoid stopServer() throws Exception {\n\t\tthis.server.stop();\n\t}\n\n\t@BeforeEach\n\tvoid setupDriver() {\n\t\tChromeOptions options = new ChromeOptions();\n\t\toptions.addArguments(\"--headless=new\");\n\t\tRemoteWebDriver baseDriver = new RemoteWebDriver(driverService.getUrl(), options);\n\t\t// Enable dev tools\n\t\tthis.driver = (RemoteWebDriver) new Augmenter().augment(baseDriver);\n\t\tthis.driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(1));\n\t}\n\n\t@AfterEach\n\tvoid cleanupDriver() {\n\t\tthis.driver.quit();\n\t}\n\n\t@Test\n\tvoid loginWhenNoValidAuthenticatorCredentialsThenRejects() {\n\t\tcreateVirtualAuthenticator(true);\n\t\tthis.getAndWait(\"/\", \"/login\");\n\t\tthis.driver.findElement(signinWithPasskeyButton()).click();\n\t\tawait(() -> assertThat(this.driver.getCurrentUrl()).endsWith(\"/login?error\"));\n\t}\n\n\t@Test\n\tvoid registerWhenNoLabelThenRejects() {\n\t\tlogin();\n\n\t\tthis.getAndWait(\"/webauthn/register\");\n\n\t\tthis.driver.findElement(registerPasskeyButton()).click();\n\t\tassertHasAlertStartingWith(\"error\", \"Error: Passkey Label is required\");\n\t}\n\n\t@Test\n\tvoid registerWhenAuthenticatorNoUserVerificationThenRejects() {\n\t\tcreateVirtualAuthenticator(false);\n\t\tlogin();\n\t\tthis.getAndWait(\"/webauthn/register\");\n\t\tthis.driver.findElement(passkeyLabel()).sendKeys(\"Virtual authenticator\");\n\t\tthis.driver.findElement(registerPasskeyButton()).click();\n\n\t\tawait(() -> assertHasAlertStartingWith(\"error\",\n\t\t\t\t\"Registration failed. Call to navigator.credentials.create failed:\"));\n\t}\n\n\t/**\n\t * Test in 4 steps to verify the end-to-end flow of registering an authenticator and\n\t * using it to register.\n\t * <ul>\n\t * <li>Step 1: Log in with username / password</li>\n\t * <li>Step 2: Register a credential from the virtual authenticator</li>\n\t * <li>Step 3: Log out</li>\n\t * <li>Step 4: Log in with the authenticator (no allowCredentials)</li>\n\t * <li>Step 5: Log in again with the same authenticator (with allowCredentials)</li>\n\t * </ul>\n\t */\n\t@Test\n\tvoid loginWhenAuthenticatorRegisteredThenSuccess() {\n\t\t// Setup\n\t\tcreateVirtualAuthenticator(true);\n\n\t\t// Step 1: log in with username / password\n\t\tlogin();\n\n\t\t// Step 2: register a credential from the virtual authenticator\n\t\tthis.getAndWait(\"/webauthn/register\");\n\t\tthis.driver.findElement(passkeyLabel()).sendKeys(\"Virtual authenticator\");\n\t\tthis.driver.findElement(registerPasskeyButton()).click();\n\n\t\t// Ensure the page location has changed before performing further assertions.\n\t\t// This is required because the location change is asynchronously performed in\n\t\t// javascript, and performing assertions based on this.driver.findElement(...)\n\t\t// may result in a StaleElementReferenceException.\n\t\tawait(() -> assertThat(this.driver.getCurrentUrl()).endsWith(\"/webauthn/register?success\"));\n\t\tawait(() -> assertHasAlertStartingWith(\"success\", \"Success!\"));\n\n\t\tList<WebElement> passkeyRows = this.driver.findElements(passkeyTableRows());\n\t\tassertThat(passkeyRows).hasSize(1)\n\t\t\t.first()\n\t\t\t.extracting((row) -> row.findElement(firstCell()))\n\t\t\t.extracting(WebElement::getText)\n\t\t\t.isEqualTo(\"Virtual authenticator\");\n\n\t\t// Step 3: log out\n\t\tlogout();\n\n\t\t// Step 4: log in with the virtual authenticator\n\t\tthis.getAndWait(\"/webauthn/register\", \"/login\");\n\t\tthis.driver.findElement(signinWithPasskeyButton()).click();\n\t\tawait(() -> assertThat(this.driver.getCurrentUrl()).endsWith(\"/webauthn/register?continue\"));\n\n\t\t// Step 5: authenticate while being already logged in\n\t\t// This simulates some use-cases with MFA. Since the user is already logged in,\n\t\t// the \"allowCredentials\" property is populated\n\t\tthis.getAndWait(\"/login\");\n\t\tthis.driver.findElement(signinWithPasskeyButton()).click();\n\t\tawait(() -> assertThat(this.driver.getCurrentUrl()).endsWith(\"/\"));\n\t}\n\n\t@Test\n\tvoid registerWhenAuthenticatorAlreadyRegisteredThenRejects() {\n\t\tcreateVirtualAuthenticator(true);\n\t\tlogin();\n\t\tregisterAuthenticator(\"Virtual authenticator\");\n\n\t\t// Cannot re-register the same authenticator because excludeCredentials\n\t\t// is not empty and contains the given authenticator\n\t\tthis.driver.findElement(passkeyLabel()).sendKeys(\"Same authenticator\");\n\t\tthis.driver.findElement(registerPasskeyButton()).click();\n\n\t\tawait(() -> assertHasAlertStartingWith(\"error\", \"Registration failed\"));\n\t}\n\n\t@Test\n\tvoid registerSecondAuthenticatorThenSucceeds() {\n\t\tcreateVirtualAuthenticator(true);\n\t\tlogin();\n\n\t\tregisterAuthenticator(\"Virtual authenticator\");\n\t\tthis.getAndWait(\"/webauthn/register\");\n\t\tList<WebElement> passkeyRows = this.driver.findElements(passkeyTableRows());\n\t\tassertThat(passkeyRows).hasSize(1)\n\t\t\t.first()\n\t\t\t.extracting((row) -> row.findElement(firstCell()))\n\t\t\t.extracting(WebElement::getText)\n\t\t\t.isEqualTo(\"Virtual authenticator\");\n\n\t\t// Create second authenticator and register\n\t\tremoveAuthenticator();\n\t\tcreateVirtualAuthenticator(true);\n\t\tregisterAuthenticator(\"Second virtual authenticator\");\n\n\t\tthis.getAndWait(\"/webauthn/register\");\n\n\t\tpasskeyRows = this.driver.findElements(passkeyTableRows());\n\t\tassertThat(passkeyRows).hasSize(2)\n\t\t\t.extracting((row) -> row.findElement(firstCell()))\n\t\t\t.extracting(WebElement::getText)\n\t\t\t.contains(\"Second virtual authenticator\");\n\t}\n\n\t/**\n\t * Add a virtual authenticator.\n\t * <p>\n\t * Note that Selenium docs for {@link HasCdp} strongly encourage to use\n\t * {@link HasDevTools} instead. However, devtools require more dependencies and\n\t * boilerplate, notably to sync the Devtools-CDP version with the current browser\n\t * version, whereas CDP runs out of the box.\n\t * <p>\n\t * @param userIsVerified whether the authenticator simulates user verification.\n\t * Setting it to false will make the ceremonies fail.\n\t * @see <a href=\n\t * \"https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/\">https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/</a>\n\t */\n\tprivate void createVirtualAuthenticator(boolean userIsVerified) {\n\t\tif (StringUtils.hasText(this.authenticatorId)) {\n\t\t\tthrow new IllegalStateException(\"Authenticator already exists, please remove it before re-creating one\");\n\t\t}\n\t\tHasCdp cdpDriver = (HasCdp) this.driver;\n\t\tcdpDriver.executeCdpCommand(\"WebAuthn.enable\", Map.of(\"enableUI\", false));\n\t\t// this.driver.addVirtualAuthenticator(createVirtualAuthenticatorOptions());\n\t\t//@formatter:off\n\t\tMap<String, Object> cmdResponse = cdpDriver.executeCdpCommand(\"WebAuthn.addVirtualAuthenticator\",\n\t\t\t\tMap.of(\n\t\t\t\t\t\t\"options\",\n\t\t\t\t\t\tMap.of(\n\t\t\t\t\t\t\t\t\"protocol\", \"ctap2\",\n\t\t\t\t\t\t\t\t\"transport\", \"usb\",\n\t\t\t\t\t\t\t\t\"hasUserVerification\", true,\n\t\t\t\t\t\t\t\t\"hasResidentKey\", true,\n\t\t\t\t\t\t\t\t\"isUserVerified\", userIsVerified,\n\t\t\t\t\t\t\t\t\"automaticPresenceSimulation\", true\n\t\t\t\t\t\t)\n\t\t\t\t));\n\t\t//@formatter:on\n\t\tthis.authenticatorId = cmdResponse.get(\"authenticatorId\").toString();\n\t}\n\n\tprivate void removeAuthenticator() {\n\t\tHasCdp cdpDriver = (HasCdp) this.driver;\n\t\tcdpDriver.executeCdpCommand(\"WebAuthn.removeVirtualAuthenticator\",\n\t\t\t\tMap.of(\"authenticatorId\", this.authenticatorId));\n\t\tthis.authenticatorId = null;\n\t}\n\n\tprivate void login() {\n\t\tthis.getAndWait(\"/\", \"/login\");\n\t\tthis.driver.findElement(usernameField()).sendKeys(USERNAME);\n\t\tthis.driver.findElement(passwordField()).sendKeys(PASSWORD);\n\t\tthis.driver.findElement(signinWithUsernamePasswordButton()).click();\n\t\t// Ensure login has completed\n\t\tawait(() -> assertThat(this.driver.getCurrentUrl()).doesNotContain(\"/login\"));\n\t}\n\n\tprivate void logout() {\n\t\tthis.getAndWait(\"/logout\");\n\t\tthis.driver.findElement(logoutButton()).click();\n\t\tawait(() -> assertThat(this.driver.getCurrentUrl()).endsWith(\"/login?logout\"));\n\t}\n\n\tprivate void registerAuthenticator(String passkeyName) {\n\t\tthis.getAndWait(\"/webauthn/register\");\n\t\tthis.driver.findElement(passkeyLabel()).sendKeys(passkeyName);\n\t\tthis.driver.findElement(registerPasskeyButton()).click();\n\t\tawait(() -> assertThat(this.driver.getCurrentUrl()).endsWith(\"/webauthn/register?success\"));\n\t}\n\n\tprivate AbstractStringAssert<?> assertHasAlertStartingWith(String alertType, String alertMessage) {\n\t\tWebElement alert = this.driver.findElement(new By.ById(alertType));\n\t\tassertThat(alert.isDisplayed())\n\t\t\t.withFailMessage(\n\t\t\t\t\t() -> alertType + \" alert was not displayed. Full page source:\\n\\n\" + this.driver.getPageSource())\n\t\t\t.isTrue();\n\n\t\treturn assertThat(alert.getText()).startsWith(alertMessage);\n\t}\n\n\t/**\n\t * Await until the assertion passes. If the assertion fails, it will display the\n\t * assertion error in stdout. WebDriver-related exceptions are ignored, so that\n\t * {@code assertion}s can interact with the page and be retried on error, e.g.\n\t * {@code assertThat(this.driver.findElement(By.Id(\"some-id\")).isNotNull()}.\n\t */\n\tprivate void await(Supplier<AbstractAssert<?, ?>> assertion) {\n\t\tnew FluentWait<>(this.driver).withTimeout(Duration.ofSeconds(2))\n\t\t\t.pollingEvery(Duration.ofMillis(100))\n\t\t\t.ignoring(AssertionError.class, WebDriverException.class)\n\t\t\t.until((d) -> {\n\t\t\t\tassertion.get();\n\t\t\t\treturn true;\n\t\t\t});\n\t}\n\n\tprivate void getAndWait(String endpoint) {\n\t\tthis.getAndWait(endpoint, endpoint);\n\t}\n\n\tprivate void getAndWait(String endpoint, String redirectUrl) {\n\t\tthis.driver.get(this.baseUrl + endpoint);\n\t\tthis.await(() -> assertThat(this.driver.getCurrentUrl()).endsWith(redirectUrl));\n\t}\n\n\tprivate static By.ById passkeyLabel() {\n\t\treturn new By.ById(\"label\");\n\t}\n\n\tprivate static By.ById registerPasskeyButton() {\n\t\treturn new By.ById(\"register\");\n\t}\n\n\tprivate static By.ByCssSelector passkeyTableRows() {\n\t\treturn new By.ByCssSelector(\"table > tbody > tr\");\n\t}\n\n\tprivate static By.ByCssSelector firstCell() {\n\t\treturn new By.ByCssSelector(\"td:first-child\");\n\t}\n\n\tprivate static By.ById passwordField() {\n\t\treturn new By.ById(PASSWORD);\n\t}\n\n\tprivate static By.ById usernameField() {\n\t\treturn new By.ById(\"username\");\n\t}\n\n\tprivate static By.ByCssSelector signinWithUsernamePasswordButton() {\n\t\treturn new By.ByCssSelector(\"form > button[type=\\\"submit\\\"]\");\n\t}\n\n\tprivate static By.ById signinWithPasskeyButton() {\n\t\treturn new By.ById(\"passkey-signin\");\n\t}\n\n\tprivate static By.ByCssSelector logoutButton() {\n\t\treturn new By.ByCssSelector(\"button\");\n\t}\n\n\tprivate static By.ByCssSelector deletePasskeyButton() {\n\t\treturn new By.ByCssSelector(\"table > tbody > tr > button\");\n\t}\n\n\t/**\n\t * The configuration for WebAuthN tests. It accesses the Server's current port, so we\n\t * can configurer WebAuthnConfigurer#allowedOrigin\n\t */\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class WebAuthnConfiguration {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\t\tUser.withDefaultPasswordEncoder().username(USERNAME).password(PASSWORD).build());\n\t\t}\n\n\t\t@Bean\n\t\tFilterChainProxy securityFilterChain(HttpSecurity http, Environment environment) throws Exception {\n\t\t\tSecurityFilterChain securityFilterChain = http\n\t\t\t\t.authorizeHttpRequests((auth) -> auth.anyRequest().authenticated())\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.webAuthn((passkeys) -> passkeys.rpId(\"localhost\")\n\t\t\t\t\t.rpName(\"Spring Security WebAuthN tests\")\n\t\t\t\t\t.allowedOrigins(\"http://localhost:\" + environment.getProperty(\"server.port\")))\n\t\t\t\t.build();\n\t\t\treturn new FilterChainProxy(securityFilterChain);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/AnonymousAuthenticationITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.rsocket.core.RSocketServer;\nimport io.rsocket.exceptions.RejectedSetupException;\nimport io.rsocket.frame.decoder.PayloadDecoder;\nimport io.rsocket.transport.netty.server.CloseableChannel;\nimport io.rsocket.transport.netty.server.TcpServerTransport;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.rsocket.RSocketRequester;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Andrey Litvitski\n */\n@ContextConfiguration\n@ExtendWith(SpringExtension.class)\npublic class AnonymousAuthenticationITests {\n\n\t@Autowired\n\tRSocketMessageHandler handler;\n\n\t@Autowired\n\tSecuritySocketAcceptorInterceptor interceptor;\n\n\t@Autowired\n\tServerController controller;\n\n\tprivate CloseableChannel server;\n\n\tprivate RSocketRequester requester;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.server = RSocketServer.create()\n\t\t\t\t.payloadDecoder(PayloadDecoder.ZERO_COPY)\n\t\t\t\t.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)\n\t\t\t\t)\n\t\t\t\t.acceptor(this.handler.responder())\n\t\t\t\t.bind(TcpServerTransport.create(\"localhost\", 0))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void dispose() {\n\t\tthis.requester.rsocket().dispose();\n\t\tthis.server.dispose();\n\t\tthis.controller.payloads.clear();\n\t}\n\n\t@Test\n\tpublic void requestWhenAnonymousDisabledThenRespondsWithForbidden() {\n\t\tthis.requester = RSocketRequester.builder()\n\t\t\t.rsocketStrategies(this.handler.getRSocketStrategies())\n\t\t\t.connectTcp(\"localhost\", this.server.address().getPort())\n\t\t\t.block();\n\t\tString data = \"andrew\";\n\t\tassertThatExceptionOfType(RejectedSetupException.class).isThrownBy(\n\t\t\t\t() -> this.requester.route(\"secure.retrieve-mono\").data(data).retrieveMono(String.class).block());\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Configuration\n\t@EnableRSocketSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tServerController controller() {\n\t\t\treturn new ServerController();\n\t\t}\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\treturn new RSocketMessageHandler();\n\t\t}\n\n\t\t@Bean\n\t\tPayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n\t\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\t\t\tReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> anonymous = (authentication,\n\t\t\t\t\texchange) -> authentication.map(trustResolver::isAnonymous).map(AuthorizationDecision::new);\n\t\t\trsocket.authorizePayload((authorize) -> authorize.anyExchange().access(anonymous));\n\t\t\trsocket.anonymous((anonymousAuthentication) -> anonymousAuthentication.disable());\n\t\t\treturn rsocket.build();\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class ServerController {\n\n\t\tprivate List<String> payloads = new ArrayList<>();\n\n\t\t@MessageMapping(\"**\")\n\t\tString retrieveMono(String payload) {\n\t\t\tadd(payload);\n\t\t\treturn \"Hi \" + payload;\n\t\t}\n\n\t\tprivate void add(String p) {\n\t\t\tthis.payloads.add(p);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/HelloHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport io.rsocket.ConnectionSetupPayload;\nimport io.rsocket.Payload;\nimport io.rsocket.RSocket;\nimport io.rsocket.SocketAcceptor;\nimport io.rsocket.util.ByteBufPayload;\nimport reactor.core.publisher.Mono;\n\npublic class HelloHandler implements SocketAcceptor {\n\n\t@Override\n\tpublic Mono<RSocket> accept(ConnectionSetupPayload setup, RSocket sendingSocket) {\n\t\treturn Mono.just(new RSocket() {\n\t\t\t@Override\n\t\t\tpublic Mono<Payload> requestResponse(Payload payload) {\n\t\t\t\tString data = payload.getDataUtf8();\n\t\t\t\tpayload.release();\n\t\t\t\tSystem.out.println(\"Got \" + data);\n\t\t\t\treturn Mono.just(ByteBufPayload.create(\"Hello \" + data));\n\t\t\t}\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/HelloRSocketITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.rsocket.core.RSocketServer;\nimport io.rsocket.exceptions.RejectedSetupException;\nimport io.rsocket.frame.decoder.PayloadDecoder;\nimport io.rsocket.transport.netty.server.CloseableChannel;\nimport io.rsocket.transport.netty.server.TcpServerTransport;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.rsocket.RSocketRequester;\nimport org.springframework.messaging.rsocket.RSocketStrategies;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder;\nimport org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n */\n@ContextConfiguration\n@ExtendWith(SpringExtension.class)\npublic class HelloRSocketITests {\n\n\t@Autowired\n\tRSocketMessageHandler handler;\n\n\t@Autowired\n\tSecuritySocketAcceptorInterceptor interceptor;\n\n\t@Autowired\n\tServerController controller;\n\n\tprivate CloseableChannel server;\n\n\tprivate RSocketRequester requester;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.server = RSocketServer.create()\n\t\t\t\t.payloadDecoder(PayloadDecoder.ZERO_COPY)\n\t\t\t\t.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)\n\t\t\t\t)\n\t\t\t\t.acceptor(this.handler.responder())\n\t\t\t\t.bind(TcpServerTransport.create(\"localhost\", 0))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void dispose() {\n\t\tthis.requester.rsocket().dispose();\n\t\tthis.server.dispose();\n\t\tthis.controller.payloads.clear();\n\t}\n\n\t@Test\n\tpublic void retrieveMonoWhenSecureThenDenied() throws Exception {\n\t\t// @formatter:off\n\t\tthis.requester = RSocketRequester.builder()\n\t\t\t.rsocketStrategies(this.handler.getRSocketStrategies())\n\t\t\t.connectTcp(\"localhost\", this.server.address().getPort())\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tString data = \"rob\";\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(\n\t\t\t\t() -> this.requester.route(\"secure.retrieve-mono\")\n\t\t\t\t\t\t.data(data)\n\t\t\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.matches((ex) -> ex instanceof RejectedSetupException\n\t\t\t\t\t\t|| ex.getClass().toString().contains(\"ReactiveException\"));\n\t\t// @formatter:on\n\t\t// FIXME: https://github.com/rsocket/rsocket-java/issues/686\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Test\n\tpublic void retrieveMonoWhenAuthorizedThenGranted() throws Exception {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"rob\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = RSocketRequester.builder()\n\t\t\t.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.rsocketStrategies(this.handler.getRSocketStrategies())\n\t\t\t.connectTcp(\"localhost\", this.server.address().getPort())\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tString data = \"rob\";\n\t\t// @formatter:off\n\t\tString hiRob = this.requester.route(\"secure.retrieve-mono\")\n\t\t\t.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.data(data)\n\t\t\t.retrieveMono(String.class)\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t\tassertThat(this.controller.payloads).containsOnly(data);\n\t}\n\n\t@Configuration\n\t@EnableRSocketSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tServerController controller() {\n\t\t\treturn new ServerController();\n\t\t}\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\tRSocketMessageHandler handler = new RSocketMessageHandler();\n\t\t\thandler.setRSocketStrategies(rsocketStrategies());\n\t\t\treturn handler;\n\t\t}\n\n\t\t@Bean\n\t\tRSocketStrategies rsocketStrategies() {\n\t\t\treturn RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();\n\t\t}\n\n\t\t@Bean\n\t\tMapReactiveUserDetailsService uds() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails rob = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"rob\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new MapReactiveUserDetailsService(rob);\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class ServerController {\n\n\t\tprivate List<String> payloads = new ArrayList<>();\n\n\t\t@MessageMapping(\"**\")\n\t\tString retrieveMono(String payload) {\n\t\t\tadd(payload);\n\t\t\treturn \"Hi \" + payload;\n\t\t}\n\n\t\tprivate void add(String p) {\n\t\t\tthis.payloads.add(p);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/HelloRSocketObservationITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.rsocket.core.RSocketServer;\nimport io.rsocket.frame.decoder.PayloadDecoder;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport io.rsocket.transport.netty.server.CloseableChannel;\nimport io.rsocket.transport.netty.server.TcpServerTransport;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.rsocket.RSocketRequester;\nimport org.springframework.messaging.rsocket.RSocketStrategies;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.metadata.SimpleAuthenticationEncoder;\nimport org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.util.MimeTypeUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ContextConfiguration\n@ExtendWith(SpringExtension.class)\npublic class HelloRSocketObservationITests {\n\n\t@Autowired\n\tRSocketMessageHandler handler;\n\n\t@Autowired\n\tSecuritySocketAcceptorInterceptor interceptor;\n\n\t@Autowired\n\tServerController controller;\n\n\t@Autowired\n\tObservationHandler<Observation.Context> observationHandler;\n\n\tprivate CloseableChannel server;\n\n\tprivate RSocketRequester requester;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.server = RSocketServer.create()\n\t\t\t\t.payloadDecoder(PayloadDecoder.ZERO_COPY)\n\t\t\t\t.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)\n\t\t\t\t)\n\t\t\t\t.acceptor(this.handler.responder())\n\t\t\t\t.bind(TcpServerTransport.create(\"localhost\", 0))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void dispose() {\n\t\tthis.requester.rsocket().dispose();\n\t\tthis.server.dispose();\n\t\tthis.controller.payloads.clear();\n\t}\n\n\t@Test\n\tpublic void getWhenUsingObservationRegistryThenObservesRequest() {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"rob\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = RSocketRequester.builder()\n\t\t\t\t.setupMetadata(credentials, MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString()))\n\t\t\t\t.rsocketStrategies(this.handler.getRSocketStrategies())\n\t\t\t\t.connectTcp(\"localhost\", this.server.address().getPort())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tString data = \"rob\";\n\t\t// @formatter:off\n\t\tthis.requester.route(\"secure.retrieve-mono\")\n\t\t\t\t.metadata(credentials, MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString()))\n\t\t\t\t.data(data)\n\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.observationHandler, times(2)).onStart(captor.capture());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\t// once for setup\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authentications\");\n\t\t// once for request\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authentications\");\n\t}\n\n\t@Configuration\n\t@EnableRSocketSecurity\n\tstatic class Config {\n\n\t\tprivate ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\n\t\t@Bean\n\t\tServerController controller() {\n\t\t\treturn new ServerController();\n\t\t}\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\tRSocketMessageHandler handler = new RSocketMessageHandler();\n\t\t\thandler.setRSocketStrategies(rsocketStrategies());\n\t\t\treturn handler;\n\t\t}\n\n\t\t@Bean\n\t\tRSocketStrategies rsocketStrategies() {\n\t\t\treturn RSocketStrategies.builder().encoder(new SimpleAuthenticationEncoder()).build();\n\t\t}\n\n\t\t@Bean\n\t\tMapReactiveUserDetailsService uds() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails rob = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"rob\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new MapReactiveUserDetailsService(rob);\n\t\t}\n\n\t\t@Bean\n\t\tObservationHandler<Observation.Context> observationHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\t\tregistry.observationConfig().observationHandler(this.handler);\n\t\t\treturn registry;\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class ServerController {\n\n\t\tprivate List<String> payloads = new ArrayList<>();\n\n\t\t@MessageMapping(\"**\")\n\t\tString retrieveMono(String payload) {\n\t\t\tadd(payload);\n\t\t\treturn \"Hi \" + payload;\n\t\t}\n\n\t\tprivate void add(String p) {\n\t\t\tthis.payloads.add(p);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/HelloRSocketWithWebFluxITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.rsocket.core.RSocketServer;\nimport io.rsocket.exceptions.RejectedSetupException;\nimport io.rsocket.frame.decoder.PayloadDecoder;\nimport io.rsocket.transport.netty.server.CloseableChannel;\nimport io.rsocket.transport.netty.server.TcpServerTransport;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.rsocket.RSocketRequester;\nimport org.springframework.messaging.rsocket.RSocketStrategies;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n */\n@ContextConfiguration\n@ExtendWith(SpringExtension.class)\npublic class HelloRSocketWithWebFluxITests {\n\n\t@Autowired\n\tRSocketMessageHandler handler;\n\n\t@Autowired\n\tSecuritySocketAcceptorInterceptor interceptor;\n\n\t@Autowired\n\tServerController controller;\n\n\tprivate CloseableChannel server;\n\n\tprivate RSocketRequester requester;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.server = RSocketServer.create()\n\t\t\t\t.payloadDecoder(PayloadDecoder.ZERO_COPY)\n\t\t\t\t.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)\n\t\t\t\t)\n\t\t\t\t.acceptor(this.handler.responder())\n\t\t\t\t.bind(TcpServerTransport.create(\"localhost\", 0))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void dispose() {\n\t\tthis.requester.rsocket().dispose();\n\t\tthis.server.dispose();\n\t\tthis.controller.payloads.clear();\n\t}\n\n\t// gh-16161\n\t@Test\n\tpublic void retrieveMonoWhenSecureThenDenied() {\n\t\t// @formatter:off\n\t\tthis.requester = RSocketRequester.builder()\n\t\t\t.rsocketStrategies(this.handler.getRSocketStrategies())\n\t\t\t.connectTcp(\"localhost\", this.server.address().getPort())\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tString data = \"rob\";\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(\n\t\t\t\t() -> this.requester.route(\"secure.retrieve-mono\")\n\t\t\t\t\t\t.data(data)\n\t\t\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.matches((ex) -> ex instanceof RejectedSetupException\n\t\t\t\t\t\t|| ex.getClass().toString().contains(\"ReactiveException\"));\n\t\t// @formatter:on\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Configuration\n\t@EnableRSocketSecurity\n\t@EnableWebFluxSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tServerController controller() {\n\t\t\treturn new ServerController();\n\t\t}\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\tRSocketMessageHandler handler = new RSocketMessageHandler();\n\t\t\thandler.setRSocketStrategies(rsocketStrategies());\n\t\t\treturn handler;\n\t\t}\n\n\t\t@Bean\n\t\tRSocketStrategies rsocketStrategies() {\n\t\t\treturn RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();\n\t\t}\n\n\t\t@Bean\n\t\tMapReactiveUserDetailsService uds() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails rob = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"rob\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new MapReactiveUserDetailsService(rob);\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class ServerController {\n\n\t\tprivate List<String> payloads = new ArrayList<>();\n\n\t\t@MessageMapping(\"**\")\n\t\tString retrieveMono(String payload) {\n\t\t\tadd(payload);\n\t\t\treturn \"Hi \" + payload;\n\t\t}\n\n\t\tprivate void add(String p) {\n\t\t\tthis.payloads.add(p);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/JwtITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport io.rsocket.core.RSocketServer;\nimport io.rsocket.frame.decoder.PayloadDecoder;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport io.rsocket.transport.netty.server.CloseableChannel;\nimport io.rsocket.transport.netty.server.TcpServerTransport;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.rsocket.RSocketRequester;\nimport org.springframework.messaging.rsocket.RSocketStrategies;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.metadata.BearerTokenAuthenticationEncoder;\nimport org.springframework.security.rsocket.metadata.BearerTokenMetadata;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n */\n@ContextConfiguration\n@ExtendWith(SpringExtension.class)\npublic class JwtITests {\n\n\t@Autowired\n\tRSocketMessageHandler handler;\n\n\t@Autowired\n\tSecuritySocketAcceptorInterceptor interceptor;\n\n\t@Autowired\n\tServerController controller;\n\n\t@Autowired\n\tReactiveJwtDecoder decoder;\n\n\tprivate CloseableChannel server;\n\n\tprivate RSocketRequester requester;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.server = RSocketServer.create()\n\t\t\t.payloadDecoder(PayloadDecoder.ZERO_COPY)\n\t\t\t.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)\n\t\t\t)\n\t\t\t.acceptor(this.handler.responder())\n\t\t\t.bind(TcpServerTransport.create(\"localhost\", 0))\n\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void dispose() {\n\t\tthis.requester.rsocket().dispose();\n\t\tthis.server.dispose();\n\t\tthis.controller.payloads.clear();\n\t}\n\n\t@Test\n\tpublic void routeWhenBearerThenAuthorized() {\n\t\tBearerTokenMetadata credentials = new BearerTokenMetadata(\"token\");\n\t\tgiven(this.decoder.decode(any())).willReturn(Mono.just(jwt()));\n\t\t// @formatter:off\n\t\tthis.requester = requester()\n\t\t\t.setupMetadata(credentials.getToken(), BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE)\n\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t.block();\n\t\tString hiRob = this.requester.route(\"secure.retrieve-mono\")\n\t\t\t.data(\"rob\")\n\t\t\t.retrieveMono(String.class)\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t}\n\n\t@Test\n\tpublic void routeWhenAuthenticationBearerThenAuthorized() {\n\t\tMimeType authenticationMimeType = MimeTypeUtils\n\t\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());\n\t\tBearerTokenMetadata credentials = new BearerTokenMetadata(\"token\");\n\t\tgiven(this.decoder.decode(any())).willReturn(Mono.just(jwt()));\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(credentials, authenticationMimeType)\n\t\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t\t.block();\n\t\tString hiRob = this.requester.route(\"secure.retrieve-mono\")\n\t\t\t\t.data(\"rob\")\n\t\t\t\t.retrieveMono(String.class).block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t}\n\n\tprivate Jwt jwt() {\n\t\treturn TestJwts.jwt()\n\t\t\t.claim(IdTokenClaimNames.ISS, \"https://issuer.example.com\")\n\t\t\t.claim(IdTokenClaimNames.SUB, \"rob\")\n\t\t\t.claim(IdTokenClaimNames.AUD, Arrays.asList(\"client-id\"))\n\t\t\t.build();\n\t}\n\n\tprivate RSocketRequester.Builder requester() {\n\t\treturn RSocketRequester.builder().rsocketStrategies(this.handler.getRSocketStrategies());\n\t}\n\n\t@Configuration\n\t@EnableRSocketSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tServerController controller() {\n\t\t\treturn new ServerController();\n\t\t}\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\tRSocketMessageHandler handler = new RSocketMessageHandler();\n\t\t\thandler.setRSocketStrategies(rsocketStrategies());\n\t\t\treturn handler;\n\t\t}\n\n\t\t@Bean\n\t\tRSocketStrategies rsocketStrategies() {\n\t\t\treturn RSocketStrategies.builder().encoder(new BearerTokenAuthenticationEncoder()).build();\n\t\t}\n\n\t\t@Bean\n\t\tPayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n\t\t\trsocket.authorizePayload((authorize) -> authorize.anyRequest().authenticated().anyExchange().permitAll())\n\t\t\t\t.jwt(Customizer.withDefaults());\n\t\t\treturn rsocket.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveJwtDecoder jwtDecoder() {\n\t\t\treturn mock(ReactiveJwtDecoder.class);\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class ServerController {\n\n\t\tprivate List<String> payloads = new ArrayList<>();\n\n\t\t@MessageMapping(\"**\")\n\t\tString connect(String payload) {\n\t\t\treturn \"Hi \" + payload;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/RSocketMessageHandlerConnectionITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.rsocket.core.RSocketServer;\nimport io.rsocket.exceptions.ApplicationErrorException;\nimport io.rsocket.exceptions.RejectedSetupException;\nimport io.rsocket.frame.decoder.PayloadDecoder;\nimport io.rsocket.transport.netty.server.CloseableChannel;\nimport io.rsocket.transport.netty.server.TcpServerTransport;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.rsocket.RSocketRequester;\nimport org.springframework.messaging.rsocket.RSocketStrategies;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder;\nimport org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n * @author Luis Felipe Vega\n * @author Jesús Ascama Arias\n * @author Manuel Tejeda\n * @author Ebert Toribio\n */\n@ContextConfiguration\n@ExtendWith(SpringExtension.class)\npublic class RSocketMessageHandlerConnectionITests {\n\n\t@Autowired\n\tRSocketMessageHandler handler;\n\n\t@Autowired\n\tSecuritySocketAcceptorInterceptor interceptor;\n\n\t@Autowired\n\tServerController controller;\n\n\tprivate CloseableChannel server;\n\n\tprivate RSocketRequester requester;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.server = RSocketServer.create()\n\t\t\t\t.payloadDecoder(PayloadDecoder.ZERO_COPY)\n\t\t\t\t.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)\n\t\t\t\t)\n\t\t\t\t.acceptor(this.handler.responder())\n\t\t\t\t.bind(TcpServerTransport.create(\"localhost\", 0))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void dispose() {\n\t\tthis.requester.rsocket().dispose();\n\t\tthis.server.dispose();\n\t\tthis.controller.payloads.clear();\n\t}\n\n\t@Test\n\tpublic void routeWhenAuthorized() {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t.block();\n\t\tString hiRob = this.requester.route(\"secure.retrieve-mono\")\n\t\t\t.data(\"rob\")\n\t\t\t.retrieveMono(String.class)\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t}\n\n\t@Test\n\tpublic void routeWhenNotAuthorized() {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t.block();\n\t\tassertThatExceptionOfType(ApplicationErrorException.class).isThrownBy(() -> this.requester\n\t\t\t\t.route(\"secure.admin.retrieve-mono\")\n\t\t\t\t.data(\"data\")\n\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t.block()\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void routeWhenStreamCredentialsAuthorized() {\n\t\tUsernamePasswordMetadata connectCredentials = new UsernamePasswordMetadata(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(connectCredentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t.block();\n\t\tString hiRob = this.requester.route(\"secure.admin.retrieve-mono\")\n\t\t\t.metadata(new UsernamePasswordMetadata(\"admin\", \"password\"),\n\t\t\t\t\tUsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.data(\"rob\")\n\t\t\t.retrieveMono(String.class)\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t}\n\n\t@Test\n\tpublic void routeWhenStreamCredentialsHaveAuthority() {\n\t\tUsernamePasswordMetadata connectCredentials = new UsernamePasswordMetadata(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(connectCredentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t.block();\n\t\tString hiUser = this.requester.route(\"secure.authority.retrieve-mono\")\n\t\t\t.metadata(new UsernamePasswordMetadata(\"admin\", \"password\"),\n\t\t\t\t\tUsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.data(\"Felipe\")\n\t\t\t.retrieveMono(String.class)\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiUser).isEqualTo(\"Hi Felipe\");\n\t}\n\n\t@Test\n\tpublic void connectWhenNotAuthenticated() {\n\t\t// @formatter:off\n\t\tthis.requester = requester().connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t\t.block();\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t\t.isThrownBy(() -> this.requester.route(\"retrieve-mono\")\n\t\t\t\t\t\t.data(\"data\")\n\t\t\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.matches((ex) -> ex instanceof RejectedSetupException\n\t\t\t\t\t\t|| ex.getClass().toString().contains(\"ReactiveException\"));\n\t\t// @formatter:on\n\t\t// FIXME: https://github.com/rsocket/rsocket-java/issues/686\n\t}\n\n\t@Test\n\tpublic void connectWhenNotAuthorized() {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"evil\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t\t.block();\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t\t.isThrownBy(() -> this.requester.route(\"retrieve-mono\")\n\t\t\t\t\t\t.data(\"data\")\n\t\t\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.matches((ex) -> ex instanceof RejectedSetupException\n\t\t\t\t\t\t|| ex.getClass().toString().contains(\"ReactiveException\"));\n\t\t// @formatter:on\n\t\t// FIXME: https://github.com/rsocket/rsocket-java/issues/686\n\t}\n\n\t@Test\n\tpublic void connectionDenied() {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t\t.block();\n\t\tassertThatExceptionOfType(ApplicationErrorException.class)\n\t\t\t\t.isThrownBy(() -> this.requester.route(\"prohibit\")\n\t\t\t\t\t\t.data(\"data\")\n\t\t\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t\t\t.block()\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void connectWithAnyRole() {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t\t.block();\n\t\tString hiRob = this.requester.route(\"anyroute\")\n\t\t\t\t.data(\"rob\")\n\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t}\n\n\t@Test\n\tpublic void connectWithAnyAuthority() {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"admin\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t\t.connectTcp(this.server.address().getHostName(), this.server.address().getPort())\n\t\t\t\t.block();\n\t\tString hiEbert = this.requester.route(\"management.users\")\n\t\t\t\t.data(\"admin\")\n\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiEbert).isEqualTo(\"Hi admin\");\n\t}\n\n\tprivate RSocketRequester.Builder requester() {\n\t\treturn RSocketRequester.builder().rsocketStrategies(this.handler.getRSocketStrategies());\n\t}\n\n\t@Configuration\n\t@EnableRSocketSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tServerController controller() {\n\t\t\treturn new ServerController();\n\t\t}\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\tRSocketMessageHandler handler = new RSocketMessageHandler();\n\t\t\thandler.setRSocketStrategies(rsocketStrategies());\n\t\t\treturn handler;\n\t\t}\n\n\t\t@Bean\n\t\tRSocketStrategies rsocketStrategies() {\n\t\t\treturn RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();\n\t\t}\n\n\t\t@Bean\n\t\tMapReactiveUserDetailsService uds() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails admin = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"admin\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\", \"ADMIN\", \"SETUP\")\n\t\t\t\t\t.build();\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\", \"SETUP\")\n\t\t\t\t\t.build();\n\t\t\tUserDetails evil = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"evil\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"EVIL\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new MapReactiveUserDetailsService(admin, user, evil);\n\t\t}\n\n\t\t@Bean\n\t\tPayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n\t\t\t// @formatter:off\n\t\t\trsocket.authorizePayload((authorize) -> authorize\n\t\t\t\t\t.setup().hasRole(\"SETUP\")\n\t\t\t\t\t.route(\"secure.admin.*\").hasRole(\"ADMIN\")\n\t\t\t\t\t.route(\"secure.**\").hasRole(\"USER\")\n\t\t\t\t\t.route(\"secure.authority.*\").hasAuthority(\"ROLE_USER\")\n\t\t\t\t\t.route(\"management.*\").hasAnyAuthority(\"ROLE_ADMIN\")\n\t\t\t\t\t.route(\"prohibit\").denyAll()\n\t\t\t\t\t.anyRequest().permitAll()\n\t\t\t)\n\t\t\t.basicAuthentication(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn rsocket.build();\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class ServerController {\n\n\t\tprivate List<String> payloads = new ArrayList<>();\n\n\t\t@MessageMapping(\"**\")\n\t\tString connect(String payload) {\n\t\t\treturn \"Hi \" + payload;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/RSocketMessageHandlerITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport io.rsocket.core.RSocketServer;\nimport io.rsocket.exceptions.ApplicationErrorException;\nimport io.rsocket.frame.decoder.PayloadDecoder;\nimport io.rsocket.transport.netty.server.CloseableChannel;\nimport io.rsocket.transport.netty.server.TcpServerTransport;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.rsocket.RSocketRequester;\nimport org.springframework.messaging.rsocket.RSocketStrategies;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder;\nimport org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n */\n@ContextConfiguration\n@ExtendWith(SpringExtension.class)\npublic class RSocketMessageHandlerITests {\n\n\t@Autowired\n\tRSocketMessageHandler handler;\n\n\t@Autowired\n\tSecuritySocketAcceptorInterceptor interceptor;\n\n\t@Autowired\n\tServerController controller;\n\n\tprivate CloseableChannel server;\n\n\tprivate RSocketRequester requester;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.server = RSocketServer.create()\n\t\t\t\t.payloadDecoder(PayloadDecoder.ZERO_COPY)\n\t\t\t\t.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)\n\t\t\t\t)\n\t\t\t\t.acceptor(this.handler.responder())\n\t\t\t\t.bind(TcpServerTransport.create(\"localhost\", 0))\n\t\t\t\t.block();\n\t\tthis.requester = RSocketRequester.builder()\n\t\t\t\t// .rsocketFactory((factory) ->\n\t\t\t\t// factory.addRequesterPlugin(payloadInterceptor))\n\t\t\t\t.rsocketStrategies(this.handler.getRSocketStrategies())\n\t\t\t\t.connectTcp(\"localhost\", this.server.address().getPort())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void dispose() {\n\t\tthis.requester.rsocket().dispose();\n\t\tthis.server.dispose();\n\t\tthis.controller.payloads.clear();\n\t}\n\n\t@Test\n\tpublic void retrieveMonoWhenSecureThenDenied() throws Exception {\n\t\tString data = \"rob\";\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(ApplicationErrorException.class).isThrownBy(\n\t\t\t\t() -> this.requester.route(\"secure.retrieve-mono\")\n\t\t\t\t\t.data(data)\n\t\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t\t.block()\n\t\t\t)\n\t\t\t.withMessageContaining(\"Access Denied\");\n\t\t// @formatter:on\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Test\n\tpublic void retrieveMonoWhenAuthenticationFailedThenException() throws Exception {\n\t\tString data = \"rob\";\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"invalid\", \"password\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(ApplicationErrorException.class)\n\t\t\t.isThrownBy(() -> this.requester.route(\"secure.retrieve-mono\")\n\t\t\t\t.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE).data(data)\n\t\t\t\t.retrieveMono(String.class)\n\t\t\t\t.block()\n\t\t\t)\n\t\t\t.withMessageContaining(\"Invalid Credentials\");\n\t\t// @formatter:on\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Test\n\tpublic void retrieveMonoWhenAuthorizedThenGranted() throws Exception {\n\t\tString data = \"rob\";\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"rob\", \"password\");\n\t\t// @formatter:off\n\t\tString hiRob = this.requester.route(\"secure.retrieve-mono\")\n\t\t\t.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)\n\t\t\t.data(data)\n\t\t\t.retrieveMono(String.class)\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t\tassertThat(this.controller.payloads).containsOnly(data);\n\t}\n\n\t@Test\n\tpublic void retrieveMonoWhenPublicThenGranted() throws Exception {\n\t\tString data = \"rob\";\n\t\t// @formatter:off\n\t\tString hiRob = this.requester.route(\"retrieve-mono\")\n\t\t\t.data(data)\n\t\t\t.retrieveMono(String.class)\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t\tassertThat(this.controller.payloads).containsOnly(data);\n\t}\n\n\t@Test\n\tpublic void retrieveFluxWhenDataFluxAndSecureThenDenied() throws Exception {\n\t\tFlux<String> data = Flux.just(\"a\", \"b\", \"c\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(ApplicationErrorException.class)\n\t\t\t.isThrownBy(() -> this.requester.route(\"secure.retrieve-flux\")\n\t\t\t\t.data(data, String.class)\n\t\t\t\t.retrieveFlux(String.class)\n\t\t\t\t.collectList()\n\t\t\t\t.block()\n\t\t\t)\n\t\t\t.withMessageContaining(\"Access Denied\");\n\t\t// @formatter:on\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Test\n\tpublic void retrieveFluxWhenDataFluxAndPublicThenGranted() throws Exception {\n\t\tFlux<String> data = Flux.just(\"a\", \"b\", \"c\");\n\t\t// @formatter:off\n\t\tList<String> hi = this.requester.route(\"retrieve-flux\")\n\t\t\t.data(data, String.class)\n\t\t\t.retrieveFlux(String.class)\n\t\t\t.collectList()\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hi).containsOnly(\"hello a\", \"hello b\", \"hello c\");\n\t\tassertThat(this.controller.payloads).containsOnlyElementsOf(data.collectList().block());\n\t}\n\n\t@Test\n\tpublic void retrieveFluxWhenDataStringAndSecureThenDenied() throws Exception {\n\t\tString data = \"a\";\n\t\tassertThatExceptionOfType(ApplicationErrorException.class).isThrownBy(\n\t\t\t\t() -> this.requester.route(\"secure.hello\").data(data).retrieveFlux(String.class).collectList().block())\n\t\t\t.withMessageContaining(\"Access Denied\");\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Test\n\tpublic void sendWhenSecureThenDenied() throws Exception {\n\t\tString data = \"hi\";\n\t\t// @formatter:off\n\t\tthis.requester.route(\"secure.send\")\n\t\t\t.data(data)\n\t\t\t.send()\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Test\n\tpublic void sendWhenPublicThenGranted() throws Exception {\n\t\tString data = \"hi\";\n\t\t// @formatter:off\n\t\tthis.requester.route(\"send\")\n\t\t\t.data(data)\n\t\t\t.send()\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(this.controller.awaitPayloads()).containsOnly(\"hi\");\n\t}\n\n\t@Configuration\n\t@EnableRSocketSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tServerController controller() {\n\t\t\treturn new ServerController();\n\t\t}\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\tRSocketMessageHandler handler = new RSocketMessageHandler();\n\t\t\thandler.setRSocketStrategies(rsocketStrategies());\n\t\t\treturn handler;\n\t\t}\n\n\t\t@Bean\n\t\tRSocketStrategies rsocketStrategies() {\n\t\t\treturn RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();\n\t\t}\n\n\t\t@Bean\n\t\tMapReactiveUserDetailsService uds() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails rob = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"rob\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t\t.build();\n\t\t\tUserDetails rossen = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"rossen\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new MapReactiveUserDetailsService(rob, rossen);\n\t\t}\n\n\t\t@Bean\n\t\tPayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n\t\t\t// @formatter:off\n\t\t\trsocket.authorizePayload(\n\t\t\t\t(authorize) -> authorize\n\t\t\t\t\t.route(\"secure.*\").authenticated()\n\t\t\t\t\t.anyExchange().permitAll()\n\t\t\t\t)\n\t\t\t\t.basicAuthentication(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn rsocket.build();\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class ServerController {\n\n\t\tprivate List<String> payloads = new ArrayList<>();\n\n\t\t@MessageMapping({ \"secure.retrieve-mono\", \"retrieve-mono\" })\n\t\tString retrieveMono(String payload) {\n\t\t\tadd(payload);\n\t\t\treturn \"Hi \" + payload;\n\t\t}\n\n\t\t@MessageMapping({ \"secure.retrieve-flux\", \"retrieve-flux\" })\n\t\tFlux<String> retrieveFlux(Flux<String> payload) {\n\t\t\treturn payload.doOnNext(this::add).map((p) -> \"hello \" + p);\n\t\t}\n\n\t\t@MessageMapping({ \"secure.send\", \"send\" })\n\t\tMono<Void> send(Mono<String> payload) {\n\t\t\treturn payload.doOnNext(this::add).then(Mono.fromRunnable(this::doNotifyAll));\n\t\t}\n\n\t\tprivate synchronized void doNotifyAll() {\n\t\t\tthis.notifyAll();\n\t\t}\n\n\t\tprivate synchronized List<String> awaitPayloads() throws InterruptedException {\n\t\t\tthis.wait(TimeUnit.SECONDS.toMillis(1));\n\t\t\treturn this.payloads;\n\t\t}\n\n\t\tprivate void add(String p) {\n\t\t\tthis.payloads.add(p);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/SimpleAuthenticationITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.rsocket.core.RSocketServer;\nimport io.rsocket.exceptions.ApplicationErrorException;\nimport io.rsocket.frame.decoder.PayloadDecoder;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport io.rsocket.transport.netty.server.CloseableChannel;\nimport io.rsocket.transport.netty.server.TcpServerTransport;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.rsocket.RSocketRequester;\nimport org.springframework.messaging.rsocket.RSocketStrategies;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.metadata.SimpleAuthenticationEncoder;\nimport org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n */\n@ContextConfiguration\n@ExtendWith(SpringExtension.class)\npublic class SimpleAuthenticationITests {\n\n\t@Autowired\n\tRSocketMessageHandler handler;\n\n\t@Autowired\n\tSecuritySocketAcceptorInterceptor interceptor;\n\n\t@Autowired\n\tServerController controller;\n\n\tprivate CloseableChannel server;\n\n\tprivate RSocketRequester requester;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.server = RSocketServer.create()\n\t\t\t\t.payloadDecoder(PayloadDecoder.ZERO_COPY)\n\t\t\t\t.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)\n\t\t\t\t)\n\t\t\t\t.acceptor(this.handler.responder())\n\t\t\t\t.bind(TcpServerTransport.create(\"localhost\", 0))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void dispose() {\n\t\tthis.requester.rsocket().dispose();\n\t\tthis.server.dispose();\n\t\tthis.controller.payloads.clear();\n\t}\n\n\t@Test\n\tpublic void retrieveMonoWhenSecureThenDenied() throws Exception {\n\t\t// @formatter:off\n\t\tthis.requester = RSocketRequester.builder()\n\t\t\t.rsocketStrategies(this.handler.getRSocketStrategies())\n\t\t\t.connectTcp(\"localhost\", this.server.address().getPort())\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tString data = \"rob\";\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(ApplicationErrorException.class)\n\t\t\t.isThrownBy(() -> this.requester.route(\"secure.retrieve-mono\")\n\t\t\t\t.data(data).retrieveMono(String.class)\n\t\t\t\t.block()\n\t\t\t);\n\t\t// @formatter:on\n\t\tassertThat(this.controller.payloads).isEmpty();\n\t}\n\n\t@Test\n\tpublic void retrieveMonoWhenAuthorizedThenGranted() {\n\t\tMimeType authenticationMimeType = MimeTypeUtils\n\t\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"rob\", \"password\");\n\t\t// @formatter:off\n\t\tthis.requester = RSocketRequester.builder()\n\t\t\t.setupMetadata(credentials, authenticationMimeType)\n\t\t\t.rsocketStrategies(this.handler.getRSocketStrategies())\n\t\t\t.connectTcp(\"localhost\", this.server.address().getPort())\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tString data = \"rob\";\n\t\t// @formatter:off\n\t\tString hiRob = this.requester.route(\"secure.retrieve-mono\")\n\t\t\t.metadata(credentials, authenticationMimeType)\n\t\t\t.data(data).retrieveMono(String.class)\n\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(hiRob).isEqualTo(\"Hi rob\");\n\t\tassertThat(this.controller.payloads).containsOnly(data);\n\t}\n\n\t@Configuration\n\t@EnableRSocketSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tServerController controller() {\n\t\t\treturn new ServerController();\n\t\t}\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\tRSocketMessageHandler handler = new RSocketMessageHandler();\n\t\t\thandler.setRSocketStrategies(rsocketStrategies());\n\t\t\treturn handler;\n\t\t}\n\n\t\t@Bean\n\t\tRSocketStrategies rsocketStrategies() {\n\t\t\treturn RSocketStrategies.builder().encoder(new SimpleAuthenticationEncoder()).build();\n\t\t}\n\n\t\t@Bean\n\t\tPayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n\t\t\trsocket.authorizePayload((authorize) -> authorize.anyRequest().authenticated().anyExchange().permitAll())\n\t\t\t\t.simpleAuthentication(Customizer.withDefaults());\n\t\t\treturn rsocket.build();\n\t\t}\n\n\t\t@Bean\n\t\tMapReactiveUserDetailsService uds() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails rob = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"rob\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new MapReactiveUserDetailsService(rob);\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class ServerController {\n\n\t\tprivate List<String> payloads = new ArrayList<>();\n\n\t\t@MessageMapping(\"**\")\n\t\tString retrieveMono(String payload) {\n\t\t\tadd(payload);\n\t\t\treturn \"Hi \" + payload;\n\t\t}\n\n\t\tprivate void add(String p) {\n\t\t\tthis.payloads.add(p);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/ldap/EmbeddedLdapServerContextSourceFactoryBeanITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.UnsatisfiedDependencyException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.ldap.core.support.LdapContextSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class EmbeddedLdapServerContextSourceFactoryBeanITests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void contextSourceFactoryBeanWhenEmbeddedServerThenAuthenticates() throws Exception {\n\t\tthis.spring.register(FromEmbeddedLdapServerConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bob\"));\n\t}\n\n\t@Test\n\tpublic void contextSourceFactoryBeanWhenPortZeroThenAuthenticates() throws Exception {\n\t\tthis.spring.register(PortZeroConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bob\"));\n\t}\n\n\t@Test\n\tpublic void contextSourceFactoryBeanWhenCustomLdifAndRootThenAuthenticates() throws Exception {\n\t\tthis.spring.register(CustomLdifAndRootConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"pg\").password(\"password\")).andExpect(authenticated().withUsername(\"pg\"));\n\t}\n\n\t@Test\n\tpublic void contextSourceFactoryBeanWhenCustomManagerDnThenAuthenticates() throws Exception {\n\t\tthis.spring.register(CustomManagerDnConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bob\"));\n\t}\n\n\t@Test\n\tpublic void contextSourceFactoryBeanWhenManagerDnAndNoPasswordThenException() {\n\t\tassertThatExceptionOfType(UnsatisfiedDependencyException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(CustomManagerDnNoPasswordConfig.class).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.isInstanceOf(IllegalStateException.class)\n\t\t\t.withMessageContaining(\"managerPassword is required if managerDn is supplied\");\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FromEmbeddedLdapServerConfig {\n\n\t\t@Bean\n\t\tEmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {\n\t\t\treturn EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(LdapContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PortZeroConfig {\n\n\t\t@Bean\n\t\tEmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {\n\t\t\tEmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean\n\t\t\t\t.fromEmbeddedLdapServer();\n\t\t\tfactoryBean.setPort(0);\n\t\t\treturn factoryBean;\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(LdapContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomLdifAndRootConfig {\n\n\t\t@Bean\n\t\tEmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {\n\t\t\tEmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean\n\t\t\t\t.fromEmbeddedLdapServer();\n\t\t\tfactoryBean.setLdif(\"classpath*:test-server2.xldif\");\n\t\t\tfactoryBean.setRoot(\"dc=monkeymachine,dc=co,dc=uk\");\n\t\t\treturn factoryBean;\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(LdapContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=gorillas\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomManagerDnConfig {\n\n\t\t@Bean\n\t\tEmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {\n\t\t\tEmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean\n\t\t\t\t.fromEmbeddedLdapServer();\n\t\t\tfactoryBean.setManagerDn(\"uid=admin,ou=system\");\n\t\t\tfactoryBean.setManagerPassword(\"secret\");\n\t\t\treturn factoryBean;\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(LdapContextSource contextSource) {\n\t\t\tLdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(\n\t\t\t\t\tcontextSource, NoOpPasswordEncoder.getInstance());\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomManagerDnNoPasswordConfig {\n\n\t\t@Bean\n\t\tEmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {\n\t\t\tEmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean\n\t\t\t\t.fromEmbeddedLdapServer();\n\t\t\tfactoryBean.setManagerDn(\"uid=admin,ou=system\");\n\t\t\treturn factoryBean;\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(LdapContextSource contextSource) {\n\t\t\tLdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(\n\t\t\t\t\tcontextSource, NoOpPasswordEncoder.getInstance());\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/ldap/Ldap247ITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport javax.naming.Name;\n\nimport org.junit.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.ldap.core.DistinguishedName;\nimport org.springframework.ldap.core.support.BaseLdapPathAware;\nimport org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor;\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class Ldap247ITests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate LdapGroupDao ldapGroupDao;\n\n\t@Test\n\tpublic void verifyThatBasePathIsProperlyPopulated() {\n\t\tthis.spring.register(FromContextSourceConfig.class).autowire();\n\t\tassertThat(this.ldapGroupDao).isNotNull();\n\t\tassertThat(this.ldapGroupDao.getBasePath()).isNotNull();\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\t@Import(BaseLdapServerConfig.class)\n\tstatic class FromContextSourceConfig {\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t\t@Bean\n\t\tstatic MethodSecurityExpressionHandler securityExpressionHandler(LdapGroupDao ldap) {\n\t\t\treturn new MethodSecurityExpressionHandler(ldap);\n\t\t}\n\n\t\t@Bean\n\t\tstatic LdapGroupDao ldapGroupDao() {\n\t\t\treturn new LdapGroupDao();\n\t\t}\n\n\t\t@Bean\n\t\tstatic BaseLdapPathBeanPostProcessor baseLdapPathBeanPostProcessor() {\n\t\t\treturn new BaseLdapPathBeanPostProcessor();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class BaseLdapServerConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container;\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapServer() {\n\t\t\tthis.container = new UnboundIdContainer(\"dc=springframework,dc=org\", \"classpath:/test-server.ldif\");\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Bean\n\t\tBaseLdapPathContextSource contextSource(UnboundIdContainer container) {\n\t\t\tint port = container.getPort();\n\t\t\treturn new DefaultSpringSecurityContextSource(\"ldap://localhost:\" + port + \"/dc=springframework,dc=org\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n\tstatic class MethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {\n\n\t\tprivate final LdapGroupDao groupDao;\n\n\t\tMethodSecurityExpressionHandler(LdapGroupDao groupDao) {\n\t\t\tthis.groupDao = groupDao;\n\t\t}\n\n\t}\n\n\tstatic class LdapGroupDao implements BaseLdapPathAware {\n\n\t\tprivate Name basePath;\n\n\t\tLdapGroupDao() {\n\t\t\tsuper();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setBaseLdapPath(DistinguishedName baseLdapPath) {\n\t\t\tthis.basePath = baseLdapPath;\n\t\t}\n\n\t\tName getBasePath() {\n\t\t\treturn this.basePath;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/ldap/LdapBindAuthenticationManagerFactoryITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.ldap.core.support.LdapContextSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.UserDetailsContextMapper;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class LdapBindAuthenticationManagerFactoryITests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void authenticationManagerFactoryWhenFromContextSourceThenAuthenticates() throws Exception {\n\t\tthis.spring.register(FromContextSourceConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bob\"));\n\t}\n\n\t@Test\n\tpublic void ldapAuthenticationProviderCustomLdapAuthoritiesPopulator() throws Exception {\n\t\tCustomAuthoritiesPopulatorConfig.LAP = new DefaultLdapAuthoritiesPopulator(mock(LdapContextSource.class),\n\t\t\t\tnull) {\n\t\t\t@Override\n\t\t\tprotected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {\n\t\t\t\treturn new HashSet<>(AuthorityUtils.createAuthorityList(\"ROLE_EXTRA\"));\n\t\t\t}\n\t\t};\n\n\t\tthis.spring.register(CustomAuthoritiesPopulatorConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withRoles(\"EXTRA\"));\n\t}\n\n\t@Test\n\tpublic void authenticationManagerFactoryWhenCustomAuthoritiesMapperThenUsed() throws Exception {\n\t\tCustomAuthoritiesMapperConfig.AUTHORITIES_MAPPER = ((authorities) -> AuthorityUtils\n\t\t\t.createAuthorityList(\"ROLE_CUSTOM\"));\n\n\t\tthis.spring.register(CustomAuthoritiesMapperConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withRoles(\"CUSTOM\"));\n\t}\n\n\t@Test\n\tpublic void authenticationManagerFactoryWhenCustomUserDetailsContextMapperThenUsed() throws Exception {\n\t\tCustomUserDetailsContextMapperConfig.CONTEXT_MAPPER = new UserDetailsContextMapper() {\n\t\t\t@Override\n\t\t\t@NullMarked\n\t\t\tpublic UserDetails mapUserFromContext(DirContextOperations ctx, String username,\n\t\t\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\t\t\treturn User.withUsername(\"other\").password(\"password\").roles(\"USER\").build();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\t@NullMarked\n\t\t\tpublic void mapUserToContext(UserDetails user, DirContextAdapter ctx) {\n\t\t\t}\n\t\t};\n\n\t\tthis.spring.register(CustomUserDetailsContextMapperConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"other\"));\n\t}\n\n\t@Test\n\tpublic void authenticationManagerFactoryWhenCustomUserDnPatternsThenUsed() throws Exception {\n\t\tthis.spring.register(CustomUserDnPatternsConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bob\"));\n\t}\n\n\t@Test\n\tpublic void authenticationManagerFactoryWhenCustomUserSearchThenUsed() throws Exception {\n\t\tthis.spring.register(CustomUserSearchConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bobspassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bob\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FromContextSourceConfig extends BaseLdapServerConfig {\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthoritiesMapperConfig extends BaseLdapServerConfig {\n\n\t\tstatic GrantedAuthoritiesMapper AUTHORITIES_MAPPER;\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\tfactory.setAuthoritiesMapper(AUTHORITIES_MAPPER);\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthoritiesPopulatorConfig extends BaseLdapServerConfig {\n\n\t\tstatic LdapAuthoritiesPopulator LAP;\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\tfactory.setLdapAuthoritiesPopulator(LAP);\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomUserDetailsContextMapperConfig extends BaseLdapServerConfig {\n\n\t\tstatic UserDetailsContextMapper CONTEXT_MAPPER;\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\tfactory.setUserDetailsContextMapper(CONTEXT_MAPPER);\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomUserDnPatternsConfig extends BaseLdapServerConfig {\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomUserSearchConfig extends BaseLdapServerConfig {\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\t\t\tfactory.setUserSearchFilter(\"uid={0}\");\n\t\t\tfactory.setUserSearchBase(\"ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tabstract static class BaseLdapServerConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container;\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapServer() {\n\t\t\tthis.container = new UnboundIdContainer(\"dc=springframework,dc=org\", \"classpath:/test-server.ldif\");\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Bean\n\t\tBaseLdapPathContextSource contextSource(UnboundIdContainer container) {\n\t\t\tint port = container.getPort();\n\t\t\treturn new DefaultSpringSecurityContextSource(\"ldap://localhost:\" + port + \"/dc=springframework,dc=org\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/ldap/LdapPasswordComparisonAuthenticationManagerFactoryITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class LdapPasswordComparisonAuthenticationManagerFactoryITests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void authenticationManagerFactoryWhenCustomPasswordEncoderThenUsed() throws Exception {\n\t\tthis.spring.register(CustomPasswordEncoderConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bcrypt\").password(\"password\"))\n\t\t\t.andExpect(authenticated().withUsername(\"bcrypt\"));\n\t}\n\n\t@Test\n\tpublic void authenticationManagerFactoryWhenCustomPasswordAttributeThenUsed() throws Exception {\n\t\tthis.spring.register(CustomPasswordAttributeConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(formLogin().user(\"bob\").password(\"bob\")).andExpect(authenticated().withUsername(\"bob\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomPasswordEncoderConfig extends BaseLdapServerConfig {\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(\n\t\t\t\t\tcontextSource, new BCryptPasswordEncoder());\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomPasswordAttributeConfig extends BaseLdapServerConfig {\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\t\t\tLdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(\n\t\t\t\t\tcontextSource, NoOpPasswordEncoder.getInstance());\n\t\t\tfactory.setPasswordAttribute(\"uid\");\n\t\t\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\t\t\treturn factory.createAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tabstract static class BaseLdapServerConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container;\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapServer() {\n\t\t\tthis.container = new UnboundIdContainer(\"dc=springframework,dc=org\", \"classpath:/test-server.ldif\");\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Bean\n\t\tBaseLdapPathContextSource contextSource(UnboundIdContainer container) {\n\t\t\tint port = container.getPort();\n\t\t\treturn new DefaultSpringSecurityContextSource(\"ldap://localhost:\" + port + \"/dc=springframework,dc=org\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/ldap/LdapProviderBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport java.text.MessageFormat;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ApplicationContextException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\npublic class LdapProviderBeanDefinitionParserTests {\n\n\tInMemoryXmlApplicationContext appCtx;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appCtx != null) {\n\t\t\tthis.appCtx.close();\n\t\t\tthis.appCtx = null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void simpleProviderAuthenticatesCorrectly() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server ldif='classpath:test-server.ldif' port='0'/>\"\n\t\t\t\t+ \"<authentication-manager>\" + \"  <ldap-authentication-provider group-search-filter='member={0}' />\"\n\t\t\t\t+ \"</authentication-manager>\");\n\n\t\tAuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,\n\t\t\t\tAuthenticationManager.class);\n\t\tAuthentication auth = authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"otherben\", \"otherbenspassword\"));\n\t\tUserDetails ben = (UserDetails) auth.getPrincipal();\n\t\tassertThat(ben.getAuthorities()).hasSize(3);\n\t}\n\n\t@Test\n\tpublic void multipleProvidersAreSupported() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server ldif='classpath:test-server.ldif' port='0'/>\"\n\t\t\t\t+ \"<authentication-manager>\" + \"  <ldap-authentication-provider group-search-filter='member={0}' />\"\n\t\t\t\t+ \"  <ldap-authentication-provider group-search-filter='uniqueMember={0}' />\"\n\t\t\t\t+ \"</authentication-manager>\");\n\n\t\tProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);\n\t\tassertThat(providerManager.getProviders()).hasSize(2);\n\t\tassertThat(providerManager.getProviders()).extracting(\"authoritiesPopulator.groupSearchFilter\")\n\t\t\t.containsExactly(\"member={0}\", \"uniqueMember={0}\");\n\t}\n\n\t@Test\n\tpublic void missingServerEltCausesConfigException() {\n\t\tassertThatExceptionOfType(ApplicationContextException.class).isThrownBy(() -> new InMemoryXmlApplicationContext(\n\t\t\t\t\"<authentication-manager>\" + \"  <ldap-authentication-provider />\" + \"</authentication-manager>\"));\n\t}\n\n\t@Test\n\tpublic void supportsPasswordComparisonAuthentication() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server ldif='classpath:test-server.ldif' port='0'/>\"\n\t\t\t\t+ \"<authentication-manager>\" + \"  <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>\"\n\t\t\t\t+ \"    <password-compare />\" + \"  </ldap-authentication-provider>\" + \"</authentication-manager>\");\n\n\t\tAuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,\n\t\t\t\tAuthenticationManager.class);\n\t\tAuthentication auth = authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"ben\", \"benspassword\"));\n\n\t\tassertThat(auth).isNotNull();\n\t}\n\n\t@Test\n\tpublic void supportsPasswordComparisonAuthenticationWithPasswordEncoder() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server ldif='classpath:test-server.ldif' port='0'/>\"\n\t\t\t\t+ \"<authentication-manager>\" + \"  <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>\"\n\t\t\t\t+ \"    <password-compare password-attribute='uid'>\" + \"      <password-encoder ref='passwordEncoder' />\"\n\t\t\t\t+ \"    </password-compare>\" + \"  </ldap-authentication-provider>\" + \"</authentication-manager>\"\n\t\t\t\t+ \"<b:bean id='passwordEncoder' class='org.springframework.security.crypto.password.NoOpPasswordEncoder' factory-method='getInstance' />\");\n\n\t\tAuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,\n\t\t\t\tAuthenticationManager.class);\n\t\tAuthentication auth = authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"ben\", \"ben\"));\n\n\t\tassertThat(auth).isNotNull();\n\t}\n\n\t// SEC-2472\n\t@Test\n\tpublic void supportsCryptoPasswordEncoder() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server ldif='classpath:test-server.ldif' port='0'/>\"\n\t\t\t\t+ \"<authentication-manager>\" + \"  <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>\"\n\t\t\t\t+ \"    <password-compare>\" + \"      <password-encoder ref='pe' />\" + \"    </password-compare>\"\n\t\t\t\t+ \"  </ldap-authentication-provider>\" + \"</authentication-manager>\"\n\t\t\t\t+ \"<b:bean id='pe' class='org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' />\");\n\n\t\tAuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,\n\t\t\t\tAuthenticationManager.class);\n\t\tAuthentication auth = authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"bcrypt\", \"password\"));\n\n\t\tassertThat(auth).isNotNull();\n\t}\n\n\t@Test\n\tpublic void supportsShaPasswordEncoder() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"\"\"\n\t\t\t\t<ldap-server ldif='classpath:test-server.ldif' port='0'/>\n\t\t\t\t<authentication-manager>\n\t\t\t\t\t<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>\n\t\t\t\t\t\t<password-compare>\n\t\t\t\t\t\t\t<password-encoder ref='pe' />\n\t\t\t\t\t\t</password-compare>\n\t\t\t\t\t</ldap-authentication-provider>\n\t\t\t\t</authentication-manager>\n\t\t\t\t<b:bean id='pe' class='org.springframework.security.crypto.password.LdapShaPasswordEncoder' />\n\t\t\t\t\"\"\");\n\t\tAuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,\n\t\t\t\tAuthenticationManager.class);\n\t\tAuthentication auth = authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"ben\", \"benspassword\"));\n\n\t\tassertThat(auth).isNotNull();\n\t}\n\n\t@Test\n\tpublic void inetOrgContextMapperIsSupported() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\n\t\t\t\t\"<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' port='0'/>\"\n\t\t\t\t\t\t+ \"<authentication-manager>\"\n\t\t\t\t\t\t+ \"  <ldap-authentication-provider user-details-class='inetOrgPerson' />\"\n\t\t\t\t\t\t+ \"</authentication-manager>\");\n\n\t\tProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);\n\t\tassertThat(providerManager.getProviders()).hasSize(1);\n\t\tassertThat(providerManager.getProviders()).extracting(\"userDetailsContextMapper\")\n\t\t\t.allSatisfy((contextMapper) -> assertThat(contextMapper).isInstanceOf(InetOrgPersonContextMapper.class));\n\t}\n\n\t@Test\n\tpublic void ldapAuthenticationProviderWorksWithPlaceholders() {\n\t\tSystem.setProperty(\"udp\", \"people\");\n\t\tSystem.setProperty(\"gsf\", \"member\");\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server />\" + \"<authentication-manager>\"\n\t\t\t\t+ \"  <ldap-authentication-provider user-dn-pattern='uid={0},ou=${udp}' group-search-filter='${gsf}={0}' />\"\n\t\t\t\t+ \"</authentication-manager>\"\n\t\t\t\t+ \"<b:bean id='org.springframework.context.support.PropertySourcesPlaceholderConfigurer' class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer' />\");\n\n\t\tProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);\n\t\tassertThat(providerManager.getProviders()).hasSize(1);\n\n\t\tAuthenticationProvider authenticationProvider = providerManager.getProviders().get(0);\n\t\tassertThat(authenticationProvider).extracting(\"authenticator.userDnFormat\")\n\t\t\t.satisfies((messageFormats) -> assertThat(messageFormats)\n\t\t\t\t.isEqualTo(new MessageFormat[] { new MessageFormat(\"uid={0},ou=people\") }));\n\t\tassertThat(authenticationProvider).extracting(\"authoritiesPopulator.groupSearchFilter\")\n\t\t\t.satisfies((searchFilter) -> assertThat(searchFilter).isEqualTo(\"member={0}\"));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.core.LdapTemplate;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n */\npublic class LdapServerBeanDefinitionParserTests {\n\n\tInMemoryXmlApplicationContext appCtx;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appCtx != null) {\n\t\t\tthis.appCtx.close();\n\t\t\tthis.appCtx = null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void embeddedServerCreationContainsExpectedContextSourceAndData() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server ldif='classpath:test-server.ldif' port='0'/>\");\n\n\t\tDefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) this.appCtx\n\t\t\t.getBean(BeanIds.CONTEXT_SOURCE);\n\n\t\t// Check data is loaded\n\t\tLdapTemplate template = new LdapTemplate(contextSource);\n\t\ttemplate.lookup(\"uid=ben,ou=people\");\n\t}\n\n\t@Test\n\tpublic void useOfUrlAttributeCreatesCorrectContextSource() throws Exception {\n\t\tint port = getDefaultPort();\n\t\t// Create second \"server\" with a url pointing at embedded one\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server ldif='classpath:test-server.ldif' port='\" + port\n\t\t\t\t+ \"'/>\" + \"<ldap-server ldif='classpath:test-server.ldif' id='blah' url='ldap://127.0.0.1:\" + port\n\t\t\t\t+ \"/dc=springframework,dc=org' />\");\n\n\t\t// Check the default context source is still there.\n\t\tthis.appCtx.getBean(BeanIds.CONTEXT_SOURCE);\n\n\t\tDefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) this.appCtx\n\t\t\t.getBean(\"blah\");\n\n\t\t// Check data is loaded as before\n\t\tLdapTemplate template = new LdapTemplate(contextSource);\n\t\ttemplate.lookup(\"uid=ben,ou=people\");\n\t}\n\n\t@Test\n\tpublic void loadingSpecificLdifFileIsSuccessful() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\n\t\t\t\t\"<ldap-server ldif='classpath*:test-server2.xldif' root='dc=monkeymachine,dc=co,dc=uk' port='0'/>\");\n\t\tDefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) this.appCtx\n\t\t\t.getBean(BeanIds.CONTEXT_SOURCE);\n\n\t\tLdapTemplate template = new LdapTemplate(contextSource);\n\t\ttemplate.lookup(\"uid=pg,ou=gorillas\");\n\t}\n\n\t@Test\n\tpublic void defaultLdifFileIsSuccessful() {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(\"<ldap-server/>\");\n\t\tUnboundIdContainer dsContainer = this.appCtx.getBean(UnboundIdContainer.class);\n\n\t\tassertThat(ReflectionTestUtils.getField(dsContainer, \"ldif\")).isEqualTo(\"classpath*:*.ldif\");\n\t}\n\n\tprivate int getDefaultPort() throws IOException {\n\t\ttry (ServerSocket server = new ServerSocket(0)) {\n\t\t\treturn server.getLocalPort();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.ldap.search.FilterBasedLdapUserSearch;\nimport org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.InetOrgPerson;\nimport org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsService;\nimport org.springframework.security.ldap.userdetails.Person;\nimport org.springframework.security.ldap.userdetails.PersonContextMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n * @author Eddú Meléndez\n */\npublic class LdapUserServiceBeanDefinitionParserTests {\n\n\tprivate InMemoryXmlApplicationContext appCtx;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appCtx != null) {\n\t\t\tthis.appCtx.close();\n\t\t\tthis.appCtx = null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void beanClassNamesAreCorrect() {\n\t\tassertThat(FilterBasedLdapUserSearch.class.getName())\n\t\t\t.isEqualTo(LdapUserServiceBeanDefinitionParser.LDAP_SEARCH_CLASS);\n\t\tassertThat(PersonContextMapper.class.getName())\n\t\t\t.isEqualTo(LdapUserServiceBeanDefinitionParser.PERSON_MAPPER_CLASS);\n\t\tassertThat(InetOrgPersonContextMapper.class.getName())\n\t\t\t.isEqualTo(LdapUserServiceBeanDefinitionParser.INET_ORG_PERSON_MAPPER_CLASS);\n\t\tassertThat(LdapUserDetailsMapper.class.getName())\n\t\t\t.isEqualTo(LdapUserServiceBeanDefinitionParser.LDAP_USER_MAPPER_CLASS);\n\t\tassertThat(DefaultLdapAuthoritiesPopulator.class.getName())\n\t\t\t.isEqualTo(LdapUserServiceBeanDefinitionParser.LDAP_AUTHORITIES_POPULATOR_CLASS);\n\t\tassertThat(new LdapUserServiceBeanDefinitionParser().getBeanClassName(mock(Element.class)))\n\t\t\t.isEqualTo(LdapUserDetailsService.class.getName());\n\t}\n\n\t@Test\n\tpublic void minimalConfigurationIsParsedOk() {\n\t\tsetContext(\n\t\t\t\t\"<ldap-user-service user-search-filter='(uid={0})' /><ldap-server ldif='classpath:test-server.ldif' url='ldap://127.0.0.1:343/dc=springframework,dc=org' />\");\n\t}\n\n\t@Test\n\tpublic void userServiceReturnsExpectedData() {\n\t\tsetContext(\n\t\t\t\t\"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-search-filter='member={0}' /><ldap-server ldif='classpath:test-server.ldif'/>\");\n\n\t\tUserDetailsService uds = (UserDetailsService) this.appCtx.getBean(\"ldapUDS\");\n\t\tUserDetails ben = uds.loadUserByUsername(\"ben\");\n\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities());\n\t\tassertThat(authorities).hasSize(3);\n\t\tassertThat(authorities).contains(\"ROLE_DEVELOPERS\");\n\t}\n\n\t@Test\n\tpublic void differentUserSearchBaseWorksAsExpected() {\n\t\tsetContext(\"<ldap-user-service id='ldapUDS' \" + \"       user-search-base='ou=otherpeople' \"\n\t\t\t\t+ \"       user-search-filter='(cn={0})' \"\n\t\t\t\t+ \"       group-search-filter='member={0}' /><ldap-server ldif='classpath:test-server.ldif'/>\");\n\n\t\tUserDetailsService uds = (UserDetailsService) this.appCtx.getBean(\"ldapUDS\");\n\t\tUserDetails joe = uds.loadUserByUsername(\"Joe Smeth\");\n\n\t\tassertThat(joe.getUsername()).isEqualTo(\"Joe Smeth\");\n\t}\n\n\t@Test\n\tpublic void rolePrefixIsSupported() {\n\t\tsetContext(\"<ldap-user-service id='ldapUDS' \" + \"     user-search-filter='(uid={0})' \"\n\t\t\t\t+ \"     group-search-filter='member={0}' role-prefix='PREFIX_'/>\"\n\t\t\t\t+ \"<ldap-user-service id='ldapUDSNoPrefix' \" + \"     user-search-filter='(uid={0})' \"\n\t\t\t\t+ \"     group-search-filter='member={0}' role-prefix='none'/><ldap-server ldif='classpath:test-server.ldif'/>\");\n\n\t\tUserDetailsService uds = (UserDetailsService) this.appCtx.getBean(\"ldapUDS\");\n\t\tUserDetails ben = uds.loadUserByUsername(\"ben\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(ben.getAuthorities())).contains(\"PREFIX_DEVELOPERS\");\n\n\t\tuds = (UserDetailsService) this.appCtx.getBean(\"ldapUDSNoPrefix\");\n\t\tben = uds.loadUserByUsername(\"ben\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(ben.getAuthorities())).contains(\"DEVELOPERS\");\n\t}\n\n\t@Test\n\tpublic void differentGroupRoleAttributeWorksAsExpected() {\n\t\tsetContext(\n\t\t\t\t\"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-role-attribute='ou' group-search-filter='member={0}' /><ldap-server ldif='classpath:test-server.ldif'/>\");\n\n\t\tUserDetailsService uds = (UserDetailsService) this.appCtx.getBean(\"ldapUDS\");\n\t\tUserDetails ben = uds.loadUserByUsername(\"ben\");\n\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities());\n\t\tassertThat(authorities).hasSize(3);\n\t\tassertThat(authorities).contains(\"ROLE_DEVELOPER\");\n\n\t}\n\n\t@Test\n\tpublic void isSupportedByAuthenticationProviderElement() {\n\t\tsetContext(\n\t\t\t\t\"<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' ldif='classpath:test-server.ldif'/>\"\n\t\t\t\t\t\t+ \"<authentication-manager>\" + \"  <authentication-provider>\"\n\t\t\t\t\t\t+ \"    <ldap-user-service user-search-filter='(uid={0})' />\" + \"  </authentication-provider>\"\n\t\t\t\t\t\t+ \"</authentication-manager>\");\n\t}\n\n\t@Test\n\tpublic void personContextMapperIsSupported() {\n\t\tsetContext(\"<ldap-server ldif='classpath:test-server.ldif'/>\"\n\t\t\t\t+ \"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-details-class='person'/>\");\n\t\tUserDetailsService uds = (UserDetailsService) this.appCtx.getBean(\"ldapUDS\");\n\t\tUserDetails ben = uds.loadUserByUsername(\"ben\");\n\t\tassertThat(ben instanceof Person).isTrue();\n\t}\n\n\t@Test\n\tpublic void inetOrgContextMapperIsSupported() {\n\t\tsetContext(\"<ldap-server id='someServer' ldif='classpath:test-server.ldif'/>\"\n\t\t\t\t+ \"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-details-class='inetOrgPerson'/>\");\n\t\tUserDetailsService uds = (UserDetailsService) this.appCtx.getBean(\"ldapUDS\");\n\t\tUserDetails ben = uds.loadUserByUsername(\"ben\");\n\t\tassertThat(ben instanceof InetOrgPerson).isTrue();\n\t}\n\n\t@Test\n\tpublic void externalContextMapperIsSupported() {\n\t\tsetContext(\"<ldap-server id='someServer' ldif='classpath:test-server.ldif'/>\"\n\t\t\t\t+ \"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-context-mapper-ref='mapper'/>\"\n\t\t\t\t+ \"<b:bean id='mapper' class='\" + InetOrgPersonContextMapper.class.getName() + \"'/>\");\n\n\t\tUserDetailsService uds = (UserDetailsService) this.appCtx.getBean(\"ldapUDS\");\n\t\tUserDetails ben = uds.loadUserByUsername(\"ben\");\n\t\tassertThat(ben instanceof InetOrgPerson).isTrue();\n\t}\n\n\tprivate void setContext(String context) {\n\t\tthis.appCtx = new InMemoryXmlApplicationContext(context);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/integration-test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\t<logger name=\"JdbmTable\" level=\"INFO\"/>\n\t<logger name=\"JdbmIndex\" level=\"INFO\"/>\n\t<logger name=\"org.apache.mina\" level=\"WARN\"/>\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "config/src/integration-test/resources/test-server.ldif",
    "content": "dn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: ou=subgroups,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: subgroups\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: ou=otherpeople,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: otherpeople\n\ndn: uid=ben,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Ben Alex\nsn: Alex\nuid: ben\nuserPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=\n\ndn: uid=otherben,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Other Ben Alex\nsn: Alex\nuid: otherben\nuserPassword: otherbenspassword\n\ndn: uid=bcrypt,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: BCrypt user\nsn: BCrypt\nuid: bcrypt\nuserPassword: $2a$10$FBAKClV1zBIOOC9XMXf3AO8RoGXYVYsfvUdoLxGkd/BnXEn4tqT3u\n\ndn: uid=bob,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Bob Hamilton\nsn: Hamilton\nuid: bob\nuserPassword: bobspassword\n\ndn: uid=joe,ou=otherpeople,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Joe Smeth\nsn: Smeth\nuid: joe\nuserPassword: joespassword\n\ndn: cn=mouse\\, jerry,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Mouse, Jerry\nsn: Mouse\nuid: jerry\nuserPassword: jerryspassword\n\ndn: cn=developers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: developers\nou: developer\nmember: uid=bcrypt,ou=people,dc=springframework,dc=org\nmember: uid=ben,ou=people,dc=springframework,dc=org\nmember: uid=otherben,ou=people,dc=springframework,dc=org\nmember: uid=bob,ou=people,dc=springframework,dc=org\n\ndn: cn=managers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: managers\nou: manager\nmember: uid=ben,ou=people,dc=springframework,dc=org\nmember: uid=otherben,ou=people,dc=springframework,dc=org\nmember: cn=mouse\\, jerry,ou=people,dc=springframework,dc=org\n\ndn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: submanagers\nou: submanager\nmember: uid=ben,ou=people,dc=springframework,dc=org\nmember: uid=otherben,ou=people,dc=springframework,dc=org\n"
  },
  {
    "path": "config/src/integration-test/resources/test-server2.xldif",
    "content": "dn: ou=gorillas,dc=monkeymachine,dc=co,dc=uk\nobjectclass: top\nobjectclass: organizationalUnit\nou: gorillas\n\ndn: uid=pg,ou=gorillas,dc=monkeymachine,dc=co,dc=uk\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Pierre\nsn: Gorille\nuid: pg\nuserPassword: password\n"
  },
  {
    "path": "config/src/integration-test/resources/users.xldif",
    "content": "dn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: ou=subgroups,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: subgroups\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: ou=otherpeople,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: otherpeople\n\ndn: uid=ben,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Ben Alex\nsn: Alex\nuid: ben\nuserPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=\n\ndn: uid=bcrypt,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: BCrypt user\nsn: BCrypt\nuid: bcrypt\nuserPassword: $2a$10$lDa0YFNHAt63MjIzK/wUqeM0qjIhzPhp3RNI/MLUQEAUbzhB/SnnS\n\ndn: uid=bob,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Bob Hamilton\nsn: Hamilton\nuid: bob\nuserPassword: bobspassword\n\ndn: uid=joe,ou=otherpeople,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Joe Smeth\nsn: Smeth\nuid: joe\nuserPassword: joespassword\n\ndn: cn=mouse\\, jerry,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Mouse, Jerry\nsn: Mouse\nuid: jerry\nuserPassword: jerryspassword\n\ndn: cn=developers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: developers\nou: developer\nmember: uid=ben,ou=people,dc=springframework,dc=org\nmember: uid=bob,ou=people,dc=springframework,dc=org\n\ndn: cn=managers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: managers\nou: manager\nmember: uid=ben,ou=people,dc=springframework,dc=org\nmember: cn=mouse\\, jerry,ou=people,dc=springframework,dc=org\n\ndn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: submanagers\nou: submanager\nmember: uid=ben,ou=people,dc=springframework,dc=org\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/BeanIds.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\n/**\n * Contains globally used default Bean IDs for beans created by the namespace support in\n * Spring Security 2.\n * <p>\n * These are intended for internal use.\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic abstract class BeanIds {\n\n\tprivate static final String PREFIX = \"org.springframework.security.\";\n\n\t/**\n\t * The \"global\" AuthenticationManager instance, registered by the\n\t * &lt;authentication-manager&gt; element\n\t */\n\tpublic static final String AUTHENTICATION_MANAGER = PREFIX + \"authenticationManager\";\n\n\t/**\n\t * External alias for FilterChainProxy bean, for use in web.xml files\n\t */\n\tpublic static final String SPRING_SECURITY_FILTER_CHAIN = \"springSecurityFilterChain\";\n\n\tpublic static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = PREFIX + \"contextSettingPostProcessor\";\n\n\tpublic static final String USER_DETAILS_SERVICE = PREFIX + \"userDetailsService\";\n\n\tpublic static final String USER_DETAILS_SERVICE_FACTORY = PREFIX + \"userDetailsServiceFactory\";\n\n\tpublic static final String METHOD_ACCESS_MANAGER = PREFIX + \"defaultMethodAccessManager\";\n\n\tpublic static final String FILTER_CHAIN_PROXY = PREFIX + \"filterChainProxy\";\n\n\tpublic static final String FILTER_CHAINS = PREFIX + \"filterChains\";\n\n\tpublic static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + \"methodSecurityMetadataSourceAdvisor\";\n\n\tpublic static final String EMBEDDED_UNBOUNDID = PREFIX + \"unboundidServerContainer\";\n\n\tpublic static final String CONTEXT_SOURCE = PREFIX + \"securityContextSource\";\n\n\tpublic static final String DEBUG_FILTER = PREFIX + \"debugFilter\";\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/Customizer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\n/**\n * Callback interface that accepts a single input argument and returns no result.\n *\n * @param <T> the type of the input to the operation\n * @author Eleftheria Stein\n * @since 5.2\n */\n@FunctionalInterface\npublic interface Customizer<T> {\n\n\t/**\n\t * Performs the customizations on the input argument.\n\t * @param t the input argument\n\t */\n\tvoid customize(T t);\n\n\t/**\n\t * Returns a {@link Customizer} that does not alter the input argument.\n\t * @return a {@link Customizer} that does not alter the input argument.\n\t */\n\tstatic <T> Customizer<T> withDefaults() {\n\t\treturn (t) -> {\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/DebugBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.debug.SecurityDebugBeanFactoryPostProcessor;\n\n/**\n * @author Luke Taylor\n */\npublic class DebugBeanDefinitionParser implements BeanDefinitionParser {\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tRootBeanDefinition debugPP = new RootBeanDefinition(SecurityDebugBeanFactoryPostProcessor.class);\n\t\tparserContext.getReaderContext().registerWithGeneratedName(debugPP);\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/Elements.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\n/**\n * Contains all the element names used by Spring Security 3 namespace support.\n *\n * @author Ben Alex\n * @author Evgeniy Cheban\n */\npublic abstract class Elements {\n\n\tpublic static final String ACCESS_DENIED_HANDLER = \"access-denied-handler\";\n\n\tpublic static final String AUTHENTICATION_MANAGER = \"authentication-manager\";\n\n\tpublic static final String AFTER_INVOCATION_PROVIDER = \"after-invocation-provider\";\n\n\tpublic static final String USER_SERVICE = \"user-service\";\n\n\tpublic static final String JDBC_USER_SERVICE = \"jdbc-user-service\";\n\n\tpublic static final String FILTER_CHAIN_MAP = \"filter-chain-map\";\n\n\tpublic static final String INTERCEPT_METHODS = \"intercept-methods\";\n\n\tpublic static final String INTERCEPT_URL = \"intercept-url\";\n\n\tpublic static final String AUTHENTICATION_PROVIDER = \"authentication-provider\";\n\n\tpublic static final String HTTP = \"http\";\n\n\tpublic static final String LDAP_PROVIDER = \"ldap-authentication-provider\";\n\n\tpublic static final String LDAP_SERVER = \"ldap-server\";\n\n\tpublic static final String LDAP_USER_SERVICE = \"ldap-user-service\";\n\n\tpublic static final String PROTECT_POINTCUT = \"protect-pointcut\";\n\n\tpublic static final String EXPRESSION_HANDLER = \"expression-handler\";\n\n\tpublic static final String INVOCATION_HANDLING = \"pre-post-annotation-handling\";\n\n\tpublic static final String INVOCATION_ATTRIBUTE_FACTORY = \"invocation-attribute-factory\";\n\n\tpublic static final String PRE_INVOCATION_ADVICE = \"pre-invocation-advice\";\n\n\tpublic static final String POST_INVOCATION_ADVICE = \"post-invocation-advice\";\n\n\tpublic static final String PROTECT = \"protect\";\n\n\tpublic static final String SESSION_MANAGEMENT = \"session-management\";\n\n\tpublic static final String CONCURRENT_SESSIONS = \"concurrency-control\";\n\n\tpublic static final String LOGOUT = \"logout\";\n\n\tpublic static final String FORM_LOGIN = \"form-login\";\n\n\tpublic static final String BASIC_AUTH = \"http-basic\";\n\n\tpublic static final String REMEMBER_ME = \"remember-me\";\n\n\tpublic static final String ANONYMOUS = \"anonymous\";\n\n\tpublic static final String FILTER_CHAIN = \"filter-chain\";\n\n\tpublic static final String GLOBAL_METHOD_SECURITY = \"global-method-security\";\n\n\tpublic static final String METHOD_SECURITY = \"method-security\";\n\n\tpublic static final String PASSWORD_ENCODER = \"password-encoder\";\n\n\tpublic static final String PORT_MAPPINGS = \"port-mappings\";\n\n\tpublic static final String PORT_MAPPING = \"port-mapping\";\n\n\tpublic static final String CUSTOM_FILTER = \"custom-filter\";\n\n\tpublic static final String REQUEST_CACHE = \"request-cache\";\n\n\tpublic static final String X509 = \"x509\";\n\n\tpublic static final String JEE = \"jee\";\n\n\tpublic static final String FILTER_SECURITY_METADATA_SOURCE = \"filter-security-metadata-source\";\n\n\tpublic static final String METHOD_SECURITY_METADATA_SOURCE = \"method-security-metadata-source\";\n\n\tpublic static final String LDAP_PASSWORD_COMPARE = \"password-compare\";\n\n\tpublic static final String DEBUG = \"debug\";\n\n\tpublic static final String HTTP_FIREWALL = \"http-firewall\";\n\n\tpublic static final String HEADERS = \"headers\";\n\n\tpublic static final String CORS = \"cors\";\n\n\tpublic static final String CSRF = \"csrf\";\n\n\tpublic static final String OAUTH2_RESOURCE_SERVER = \"oauth2-resource-server\";\n\n\tpublic static final String JWT = \"jwt\";\n\n\tpublic static final String OPAQUE_TOKEN = \"opaque-token\";\n\n\tpublic static final String WEBSOCKET_MESSAGE_BROKER = \"websocket-message-broker\";\n\n\tpublic static final String INTERCEPT_MESSAGE = \"intercept-message\";\n\n\tpublic static final String OAUTH2_LOGIN = \"oauth2-login\";\n\n\tpublic static final String OAUTH2_CLIENT = \"oauth2-client\";\n\n\tpublic static final String CLIENT_REGISTRATIONS = \"client-registrations\";\n\n\tpublic static final String PASSWORD_MANAGEMENT = \"password-management\";\n\n\tpublic static final String RELYING_PARTY_REGISTRATIONS = \"relying-party-registrations\";\n\n\tpublic static final String SAML2_LOGIN = \"saml2-login\";\n\n\tpublic static final String SAML2_LOGOUT = \"saml2-logout\";\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ObjectPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.springframework.beans.factory.Aware;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * Allows initialization of Objects. Typically this is used to call the {@link Aware}\n * methods, {@link InitializingBean#afterPropertiesSet()}, and ensure that\n * {@link DisposableBean#destroy()} has been invoked.\n *\n * @param <T> the bound of the types of Objects this {@link ObjectPostProcessor} supports.\n * @author Rob Winch\n * @since 3.2\n */\npublic interface ObjectPostProcessor<T> {\n\n\tstatic <S> ObjectPostProcessor<S> identity() {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic <O extends S> O postProcess(O object) {\n\t\t\t\treturn object;\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Initialize the object possibly returning a modified instance that should be used\n\t * instead.\n\t * @param object the object to initialize\n\t * @return the initialized version of the object\n\t */\n\t<O extends T> O postProcess(O object);\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanDefinitionHolder;\nimport org.springframework.beans.factory.xml.BeanDefinitionDecorator;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.NamespaceHandler;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser;\nimport org.springframework.security.config.authentication.AuthenticationProviderBeanDefinitionParser;\nimport org.springframework.security.config.authentication.JdbcUserServiceBeanDefinitionParser;\nimport org.springframework.security.config.authentication.UserServiceBeanDefinitionParser;\nimport org.springframework.security.config.http.FilterChainBeanDefinitionParser;\nimport org.springframework.security.config.http.FilterChainMapBeanDefinitionDecorator;\nimport org.springframework.security.config.http.FilterInvocationSecurityMetadataSourceParser;\nimport org.springframework.security.config.http.HttpFirewallBeanDefinitionParser;\nimport org.springframework.security.config.http.HttpSecurityBeanDefinitionParser;\nimport org.springframework.security.config.ldap.LdapProviderBeanDefinitionParser;\nimport org.springframework.security.config.ldap.LdapServerBeanDefinitionParser;\nimport org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser;\nimport org.springframework.security.config.method.GlobalMethodSecurityBeanDefinitionParser;\nimport org.springframework.security.config.method.InterceptMethodsBeanDefinitionDecorator;\nimport org.springframework.security.config.method.MethodSecurityBeanDefinitionParser;\nimport org.springframework.security.config.method.MethodSecurityMetadataSourceBeanDefinitionParser;\nimport org.springframework.security.config.oauth2.client.ClientRegistrationsBeanDefinitionParser;\nimport org.springframework.security.config.saml2.RelyingPartyRegistrationsBeanDefinitionParser;\nimport org.springframework.security.config.websocket.WebSocketMessageBrokerSecurityBeanDefinitionParser;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Parses elements from the \"security\" namespace\n * (http://www.springframework.org/schema/security).\n *\n * @author Luke Taylor\n * @author Ben Alex\n * @author Rob Winch\n * @since 2.0\n */\npublic final class SecurityNamespaceHandler implements NamespaceHandler {\n\n\tprivate static final String FILTER_CHAIN_PROXY_CLASSNAME = \"org.springframework.security.web.FilterChainProxy\";\n\n\tprivate static final String MESSAGE_CLASSNAME = \"org.springframework.messaging.Message\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final Map<String, BeanDefinitionParser> parsers = new HashMap<>();\n\n\tprivate final BeanDefinitionDecorator interceptMethodsBDD = new InterceptMethodsBeanDefinitionDecorator();\n\n\tprivate BeanDefinitionDecorator filterChainMapBDD;\n\n\tpublic SecurityNamespaceHandler() {\n\t\tString coreVersion = SpringSecurityCoreVersion.getVersion();\n\t\tString configVersion = configVersion();\n\t\tif (!Objects.equals(coreVersion, configVersion)) {\n\t\t\tString message = \"You are attempting to run spring-security-core:%s with spring-security-config:%s\";\n\t\t\tthis.logger.error(String.format(message, coreVersion, configVersion));\n\t\t}\n\t}\n\n\tprivate static String configVersion() {\n\t\tPackage pkg = SpringSecurityCoreVersion.class.getPackage();\n\t\treturn (pkg != null) ? pkg.getImplementationVersion() : null;\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tif (!namespaceMatchesVersion(element)) {\n\t\t\tpc.getReaderContext()\n\t\t\t\t.fatal(\"You cannot use any XSD older than spring-security-7.1.xsd. Either change to spring-security.xsd or spring-security-7.1.xsd\",\n\t\t\t\t\t\telement);\n\t\t}\n\t\tString name = pc.getDelegate().getLocalName(element);\n\t\tBeanDefinitionParser parser = this.parsers.get(name);\n\t\tif (parser == null) {\n\t\t\t// SEC-1455. Load parsers when required, not just on init().\n\t\t\tloadParsers();\n\t\t\tparser = this.parsers.get(name);\n\t\t}\n\t\tif (parser != null) {\n\t\t\treturn parser.parse(element, pc);\n\t\t}\n\t\tif (Elements.HTTP.equals(name) || Elements.FILTER_SECURITY_METADATA_SOURCE.equals(name)\n\t\t\t\t|| Elements.FILTER_CHAIN_MAP.equals(name) || Elements.FILTER_CHAIN.equals(name)) {\n\t\t\treportMissingWebClasses(name, pc, element);\n\t\t}\n\t\telse {\n\t\t\treportUnsupportedNodeType(name, pc, element);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext pc) {\n\t\tString name = pc.getDelegate().getLocalName(node);\n\t\tif (node instanceof Element) {\n\t\t\t// We only handle elements\n\t\t\tif (Elements.INTERCEPT_METHODS.equals(name)) {\n\t\t\t\treturn this.interceptMethodsBDD.decorate(node, definition, pc);\n\t\t\t}\n\t\t\tif (Elements.FILTER_CHAIN_MAP.equals(name)) {\n\t\t\t\tif (this.filterChainMapBDD == null) {\n\t\t\t\t\tloadParsers();\n\t\t\t\t}\n\t\t\t\tif (this.filterChainMapBDD == null) {\n\t\t\t\t\treportMissingWebClasses(name, pc, node);\n\t\t\t\t}\n\t\t\t\treturn this.filterChainMapBDD.decorate(node, definition, pc);\n\t\t\t}\n\t\t}\n\t\treportUnsupportedNodeType(name, pc, node);\n\t\treturn null;\n\t}\n\n\tprivate void reportUnsupportedNodeType(String name, ParserContext pc, Node node) {\n\t\tpc.getReaderContext()\n\t\t\t.fatal(\"Security namespace does not support decoration of \"\n\t\t\t\t\t+ ((node instanceof Element) ? \"element\" : \"attribute\") + \" [\" + name + \"]\", node);\n\t}\n\n\tprivate void reportMissingWebClasses(String nodeName, ParserContext pc, Node node) {\n\t\tString errorMessage = \"The classes from the spring-security-web jar \"\n\t\t\t\t+ \"(or one of its dependencies) are not available. You need these to use <\" + nodeName + \">\";\n\t\ttry {\n\t\t\tClassUtils.forName(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader());\n\t\t\t// no details available\n\t\t\tpc.getReaderContext().fatal(errorMessage, node);\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\t// provide details on why it could not be loaded\n\t\t\tpc.getReaderContext().fatal(errorMessage, node, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void init() {\n\t\tloadParsers();\n\t}\n\n\tprivate void loadParsers() {\n\t\t// Parsers\n\t\tthis.parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.METHOD_SECURITY, new MethodSecurityBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE,\n\t\t\t\tnew MethodSecurityMetadataSourceBeanDefinitionParser());\n\t\tif (ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader())) {\n\t\t\tloadWebParsers();\n\t\t}\n\t\tif (ClassUtils.isPresent(MESSAGE_CLASSNAME, getClass().getClassLoader())) {\n\t\t\tloadWebSocketParsers();\n\t\t}\n\t}\n\n\tprivate void loadWebParsers() {\n\t\tthis.parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());\n\t\tthis.parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());\n\t\tthis.filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();\n\t\tthis.parsers.put(Elements.CLIENT_REGISTRATIONS, new ClientRegistrationsBeanDefinitionParser());\n\t\tthis.parsers.put(Elements.RELYING_PARTY_REGISTRATIONS, new RelyingPartyRegistrationsBeanDefinitionParser());\n\t}\n\n\tprivate void loadWebSocketParsers() {\n\t\tthis.parsers.put(Elements.WEBSOCKET_MESSAGE_BROKER, new WebSocketMessageBrokerSecurityBeanDefinitionParser());\n\t}\n\n\t/**\n\t * Check that the schema location declared in the source file being parsed matches the\n\t * Spring Security version. The old 2.0 schema is not compatible with the 3.1 parser,\n\t * so it is an error to explicitly use 2.0.\n\t * <p>\n\t * There are also differences between 3.0 and 3.1 which are sufficient that we report\n\t * using 3.0 as an error too. It might be an error to declare spring-security.xsd as\n\t * an alias, but you are only going to find that out when one of the sub parsers\n\t * breaks.\n\t * @param element the element that is to be parsed next\n\t * @return true if we find a schema declaration that matches\n\t */\n\tprivate boolean namespaceMatchesVersion(Element element) {\n\t\treturn matchesVersionInternal(element)\n\t\t\t\t&& matchesVersionInternal(element.getOwnerDocument().getDocumentElement());\n\t}\n\n\tprivate boolean matchesVersionInternal(Element element) {\n\t\tString schemaLocation = element.getAttributeNS(\"http://www.w3.org/2001/XMLSchema-instance\", \"schemaLocation\");\n\t\treturn schemaLocation.matches(\"(?m).*spring-security-7\\\\.1.*.xsd.*\")\n\t\t\t\t|| schemaLocation.matches(\"(?m).*spring-security.xsd.*\")\n\t\t\t\t|| !schemaLocation.matches(\"(?m).*spring-security.*\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ThrowingCustomizer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\n/**\n * A {@link Customizer} that allows invocation of code that throws a checked exception.\n *\n * @param <T> The type of input.\n */\n@FunctionalInterface\npublic interface ThrowingCustomizer<T> extends Customizer<T> {\n\n\t/**\n\t * Default {@link Customizer#customize(Object)} that wraps any thrown checked\n\t * exceptions (by default in a {@link RuntimeException}).\n\t * @param t the object to customize\n\t */\n\tdefault void customize(T t) {\n\t\ttry {\n\t\t\tcustomizeWithException(t);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Performs the customization on the given object, possibly throwing a checked\n\t * exception.\n\t * @param t the object to customize\n\t * @throws Exception on error\n\t */\n\tvoid customizeWithException(T t) throws Exception;\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.WebSecurity;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.DelegatingFilterProxy;\n\n/**\n * <p>\n * A base {@link SecurityBuilder} that allows {@link SecurityConfigurer} to be applied to\n * it. This makes modifying the {@link SecurityBuilder} a strategy that can be customized\n * and broken up into a number of {@link SecurityConfigurer} objects that have more\n * specific goals than that of the {@link SecurityBuilder}.\n * </p>\n *\n * <p>\n * For example, a {@link SecurityBuilder} may build an {@link DelegatingFilterProxy}, but\n * a {@link SecurityConfigurer} might populate the {@link SecurityBuilder} with the\n * filters necessary for session management, form based login, authorization, etc.\n * </p>\n *\n * @param <O> The object that this builder returns\n * @param <B> The type of this builder (that is returned by the base class)\n * @author Rob Winch\n * @author DingHao\n * @see WebSecurity\n */\npublic abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>\n\t\textends AbstractSecurityBuilder<O> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();\n\n\tprivate List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();\n\n\tprivate final Map<Class<?>, Object> sharedObjects = new HashMap<>();\n\n\tprivate final boolean allowConfigurersOfSameType;\n\n\tprivate BuildState buildState = BuildState.UNBUILT;\n\n\tprivate ObjectPostProcessor<Object> objectPostProcessor;\n\n\t/***\n\t * Creates a new instance with the provided {@link ObjectPostProcessor}. This post\n\t * processor must support Object since there are many types of objects that may be\n\t * post processed.\n\t * @param objectPostProcessor the {@link ObjectPostProcessor} to use\n\t */\n\tprotected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tthis(objectPostProcessor, false);\n\t}\n\n\t/***\n\t * Creates a new instance with the provided {@link ObjectPostProcessor}. This post\n\t * processor must support Object since there are many types of objects that may be\n\t * post processed.\n\t * @param objectPostProcessor the {@link ObjectPostProcessor} to use\n\t * @param allowConfigurersOfSameType if true, will not override other\n\t * {@link SecurityConfigurer}'s when performing apply\n\t */\n\tprotected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor,\n\t\t\tboolean allowConfigurersOfSameType) {\n\t\tAssert.notNull(objectPostProcessor, \"objectPostProcessor cannot be null\");\n\t\tthis.objectPostProcessor = objectPostProcessor;\n\t\tthis.allowConfigurersOfSameType = allowConfigurersOfSameType;\n\t}\n\n\t/**\n\t * Similar to {@link SecurityBuilder#build()} and {@link #getObject()} but checks the\n\t * state to determine if {@link SecurityBuilder#build()} needs to be called first.\n\t * @return the result of {@link SecurityBuilder#build()} or {@link #getObject()}. If\n\t * an error occurs while building, returns null.\n\t */\n\tpublic O getOrBuild() {\n\t\tif (!isUnbuilt()) {\n\t\t\treturn getObject();\n\t\t}\n\t\ttry {\n\t\t\treturn build();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthis.logger.debug(\"Failed to perform build. Returning null\", ex);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any\n\t * {@link SecurityConfigurer} of the exact same class. Note that object hierarchies\n\t * are not considered.\n\t * @param configurer\n\t * @return the {@link SecurityConfigurerAdapter} for further customizations\n\t * @throws Exception\n\t */\n\tpublic <C extends SecurityConfigurer<O, B>> C apply(C configurer) {\n\t\tadd(configurer);\n\t\treturn configurer;\n\t}\n\n\t/**\n\t * Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and\n\t * invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.\n\t *\n\t * <p>\n\t * A shortcut for applying a configurer as-is, or in other words: <code>\n\t *     .with(new MyConfigurer())\n\t * </code>\n\t *\n\t * <p>\n\t * Is identical to: <code>\n\t *     .with(new MyConfigurer(), Customizer.withDefaults())\n\t * </code>\n\t * @param configurer\n\t * @return the {@link SecurityBuilder} for further customizations\n\t * @throws Exception\n\t * @since 7.0\n\t */\n\tpublic <C extends SecurityConfigurerAdapter<O, B>> B with(C configurer) {\n\t\treturn with(configurer, Customizer.withDefaults());\n\t}\n\n\t/**\n\t * Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and\n\t * invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.\n\t * @param configurer\n\t * @return the {@link SecurityBuilder} for further customizations\n\t * @throws Exception\n\t * @since 6.2\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <C extends SecurityConfigurerAdapter<O, B>> B with(C configurer, Customizer<C> customizer) {\n\t\tconfigurer.addObjectPostProcessor(this.objectPostProcessor);\n\t\tconfigurer.setBuilder((B) this);\n\t\tadd(configurer);\n\t\tcustomizer.customize(configurer);\n\t\treturn (B) this;\n\t}\n\n\t/**\n\t * Sets an object that is shared by multiple {@link SecurityConfigurer}.\n\t * @param sharedType the Class to key the shared object by.\n\t * @param object the Object to store\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <C> void setSharedObject(Class<C> sharedType, C object) {\n\t\tthis.sharedObjects.put(sharedType, object);\n\t}\n\n\t/**\n\t * Gets a shared Object. Note that object hierarchies are not considered.\n\t * @param sharedType the type of the shared Object\n\t * @return the shared Object or null if it is not found\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <C> C getSharedObject(Class<C> sharedType) {\n\t\treturn (C) this.sharedObjects.get(sharedType);\n\t}\n\n\t/**\n\t * Gets the shared objects\n\t * @return the shared Objects\n\t */\n\tpublic Map<Class<?>, Object> getSharedObjects() {\n\t\treturn Collections.unmodifiableMap(this.sharedObjects);\n\t}\n\n\t/**\n\t * Adds {@link SecurityConfigurer} ensuring that it is allowed and invoking\n\t * {@link SecurityConfigurer#init(SecurityBuilder)} immediately if necessary.\n\t * @param configurer the {@link SecurityConfigurer} to add\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <C extends SecurityConfigurer<O, B>> void add(C configurer) {\n\t\tAssert.notNull(configurer, \"configurer cannot be null\");\n\t\tClass<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer\n\t\t\t.getClass();\n\t\tsynchronized (this.configurers) {\n\t\t\tif (this.buildState.isConfigured()) {\n\t\t\t\tthrow new IllegalStateException(\"Cannot apply \" + configurer + \" to already built object\");\n\t\t\t}\n\t\t\tList<SecurityConfigurer<O, B>> configs = null;\n\t\t\tif (this.allowConfigurersOfSameType) {\n\t\t\t\tconfigs = this.configurers.get(clazz);\n\t\t\t}\n\t\t\tconfigs = (configs != null) ? configs : new ArrayList<>(1);\n\t\t\tconfigs.add(configurer);\n\t\t\tthis.configurers.put(clazz, configs);\n\t\t\tif (this.buildState.isInitializing()) {\n\t\t\t\tthis.configurersAddedInInitializing.add(configurer);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets all the {@link SecurityConfigurer} instances by its class name or an empty\n\t * List if not found. Note that object hierarchies are not considered.\n\t * @param clazz the {@link SecurityConfigurer} class to look for\n\t * @return a list of {@link SecurityConfigurer}s for further customization\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) {\n\t\tList<C> configs = (List<C>) this.configurers.get(clazz);\n\t\tif (configs == null) {\n\t\t\treturn new ArrayList<>();\n\t\t}\n\t\treturn new ArrayList<>(configs);\n\t}\n\n\t/**\n\t * Removes all the {@link SecurityConfigurer} instances by its class name or an empty\n\t * List if not found. Note that object hierarchies are not considered.\n\t * @param clazz the {@link SecurityConfigurer} class to look for\n\t * @return a list of {@link SecurityConfigurer}s for further customization\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {\n\t\tList<C> configs = (List<C>) this.configurers.remove(clazz);\n\t\tif (configs == null) {\n\t\t\treturn new ArrayList<>();\n\t\t}\n\t\tremoveFromConfigurersAddedInInitializing(clazz);\n\t\treturn new ArrayList<>(configs);\n\t}\n\n\t/**\n\t * Gets the {@link SecurityConfigurer} by its class name or <code>null</code> if not\n\t * found. Note that object hierarchies are not considered.\n\t * @param clazz\n\t * @return the {@link SecurityConfigurer} for further customizations\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <C extends SecurityConfigurer<O, B>> C getConfigurer(Class<C> clazz) {\n\t\tList<SecurityConfigurer<O, B>> configs = this.configurers.get(clazz);\n\t\tif (configs == null) {\n\t\t\treturn null;\n\t\t}\n\t\tAssert.state(configs.size() == 1,\n\t\t\t\t() -> \"Only one configurer expected for type \" + clazz + \", but got \" + configs);\n\t\treturn (C) configs.get(0);\n\t}\n\n\t/**\n\t * Removes and returns the {@link SecurityConfigurer} by its class name or\n\t * <code>null</code> if not found. Note that object hierarchies are not considered.\n\t * @param clazz\n\t * @return\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <C extends SecurityConfigurer<O, B>> C removeConfigurer(Class<C> clazz) {\n\t\tList<SecurityConfigurer<O, B>> configs = this.configurers.remove(clazz);\n\t\tif (configs == null) {\n\t\t\treturn null;\n\t\t}\n\t\tremoveFromConfigurersAddedInInitializing(clazz);\n\t\tAssert.state(configs.size() == 1,\n\t\t\t\t() -> \"Only one configurer expected for type \" + clazz + \", but got \" + configs);\n\t\treturn (C) configs.get(0);\n\t}\n\n\tprivate <C extends SecurityConfigurer<O, B>> void removeFromConfigurersAddedInInitializing(Class<C> clazz) {\n\t\tthis.configurersAddedInInitializing.removeIf(clazz::isInstance);\n\t}\n\n\t/**\n\t * Specifies the {@link ObjectPostProcessor} to use.\n\t * @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null\n\t * @return the {@link SecurityBuilder} for further customizations\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic B objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tAssert.notNull(objectPostProcessor, \"objectPostProcessor cannot be null\");\n\t\tthis.objectPostProcessor = objectPostProcessor;\n\t\treturn (B) this;\n\t}\n\n\t/**\n\t * Performs post processing of an object. The default is to delegate to the\n\t * {@link ObjectPostProcessor}.\n\t * @param object the Object to post process\n\t * @return the possibly modified Object to use\n\t */\n\tprotected <P> P postProcess(P object) {\n\t\treturn this.objectPostProcessor.postProcess(object);\n\t}\n\n\t/**\n\t * Executes the build using the {@link SecurityConfigurer}'s that have been applied\n\t * using the following steps:\n\t *\n\t * <ul>\n\t * <li>Invokes {@link #beforeInit()} for any subclass to hook into</li>\n\t * <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any\n\t * {@link SecurityConfigurer} that was applied to this builder.</li>\n\t * <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li>\n\t * <li>Invokes {@link #performBuild()} which actually builds the Object</li>\n\t * </ul>\n\t */\n\t@Override\n\tprotected final O doBuild() {\n\t\tsynchronized (this.configurers) {\n\t\t\tthis.buildState = BuildState.INITIALIZING;\n\t\t\tbeforeInit();\n\t\t\tinit();\n\t\t\tthis.buildState = BuildState.CONFIGURING;\n\t\t\tbeforeConfigure();\n\t\t\tconfigure();\n\t\t\tthis.buildState = BuildState.BUILDING;\n\t\t\tO result = performBuild();\n\t\t\tthis.buildState = BuildState.BUILT;\n\t\t\treturn result;\n\t\t}\n\t}\n\n\t/**\n\t * Invoked prior to invoking each {@link SecurityConfigurer#init(SecurityBuilder)}\n\t * method. Subclasses may override this method to hook into the lifecycle without\n\t * using a {@link SecurityConfigurer}.\n\t */\n\tprotected void beforeInit() {\n\t}\n\n\t/**\n\t * Invoked prior to invoking each\n\t * {@link SecurityConfigurer#configure(SecurityBuilder)} method. Subclasses may\n\t * override this method to hook into the lifecycle without using a\n\t * {@link SecurityConfigurer}.\n\t */\n\tprotected void beforeConfigure() {\n\t}\n\n\t/**\n\t * Subclasses must implement this method to build the object that is being returned.\n\t * @return the Object to be built or null if the implementation allows it\n\t */\n\tprotected abstract O performBuild();\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate void init() {\n\t\tCollection<SecurityConfigurer<O, B>> configurers = getConfigurers();\n\t\tfor (SecurityConfigurer<O, B> configurer : configurers) {\n\t\t\tconfigurer.init((B) this);\n\t\t}\n\t\twhile (!this.configurersAddedInInitializing.isEmpty()) {\n\t\t\tList<SecurityConfigurer<O, B>> toInit = this.configurersAddedInInitializing;\n\t\t\tthis.configurersAddedInInitializing = new ArrayList<>();\n\t\t\tfor (SecurityConfigurer<O, B> configurer : toInit) {\n\t\t\t\tconfigurer.init((B) this);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate void configure() {\n\t\tCollection<SecurityConfigurer<O, B>> configurers = getConfigurers();\n\t\tfor (SecurityConfigurer<O, B> configurer : configurers) {\n\t\t\tconfigurer.configure((B) this);\n\t\t}\n\t}\n\n\tprivate Collection<SecurityConfigurer<O, B>> getConfigurers() {\n\t\tList<SecurityConfigurer<O, B>> result = new ArrayList<>();\n\t\tfor (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {\n\t\t\tresult.addAll(configs);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Determines if the object is unbuilt.\n\t * @return true, if unbuilt else false\n\t */\n\tprivate boolean isUnbuilt() {\n\t\tsynchronized (this.configurers) {\n\t\t\treturn this.buildState == BuildState.UNBUILT;\n\t\t}\n\t}\n\n\t/**\n\t * The build state for the application\n\t *\n\t * @author Rob Winch\n\t * @since 3.2\n\t */\n\tprivate enum BuildState {\n\n\t\t/**\n\t\t * This is the state before the {@link SecurityBuilder#build()} is invoked\n\t\t */\n\t\tUNBUILT(0),\n\n\t\t/**\n\t\t * The state from when {@link SecurityBuilder#build()} is first invoked until all\n\t\t * the {@link SecurityConfigurer#init(SecurityBuilder)} methods have been invoked.\n\t\t */\n\t\tINITIALIZING(1),\n\n\t\t/**\n\t\t * The state from after all {@link SecurityConfigurer#init(SecurityBuilder)} have\n\t\t * been invoked until after all the\n\t\t * {@link SecurityConfigurer#configure(SecurityBuilder)} methods have been\n\t\t * invoked.\n\t\t */\n\t\tCONFIGURING(2),\n\n\t\t/**\n\t\t * From the point after all the\n\t\t * {@link SecurityConfigurer#configure(SecurityBuilder)} have completed to just\n\t\t * after {@link AbstractConfiguredSecurityBuilder#performBuild()}.\n\t\t */\n\t\tBUILDING(3),\n\n\t\t/**\n\t\t * After the object has been completely built.\n\t\t */\n\t\tBUILT(4);\n\n\t\tprivate final int order;\n\n\t\tBuildState(int order) {\n\t\t\tthis.order = order;\n\t\t}\n\n\t\tpublic boolean isInitializing() {\n\t\t\treturn INITIALIZING.order == this.order;\n\t\t}\n\n\t\t/**\n\t\t * Determines if the state is CONFIGURING or later\n\t\t * @return\n\t\t */\n\t\tpublic boolean isConfigured() {\n\t\t\treturn this.order >= CONFIGURING.order;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/AbstractSecurityBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * A base {@link SecurityBuilder} that ensures the object being built is only built one\n * time.\n *\n * @param <O> the type of Object that is being built\n * @author Rob Winch\n *\n */\npublic abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {\n\n\tprivate AtomicBoolean building = new AtomicBoolean();\n\n\tprivate O object;\n\n\t@Override\n\tpublic final O build() {\n\t\tif (this.building.compareAndSet(false, true)) {\n\t\t\tthis.object = doBuild();\n\t\t\treturn this.object;\n\t\t}\n\t\tthrow new AlreadyBuiltException(\"This object has already been built\");\n\t}\n\n\t/**\n\t * Gets the object that was built. If it has not been built yet an Exception is\n\t * thrown.\n\t * @return the Object that was built\n\t */\n\tpublic final O getObject() {\n\t\tif (!this.building.get()) {\n\t\t\tthrow new IllegalStateException(\"This object has not been built\");\n\t\t}\n\t\treturn this.object;\n\t}\n\n\t/**\n\t * Subclasses should implement this to perform the build.\n\t * @return the object that should be returned by {@link SecurityBuilder#build()}.\n\t * @throws Exception if an error occurs\n\t */\n\tprotected abstract O doBuild();\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/AlreadyBuiltException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\n/**\n * Thrown when {@link SecurityBuilder#build()} is two or more times.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class AlreadyBuiltException extends IllegalStateException {\n\n\tpublic AlreadyBuiltException(String message) {\n\t\tsuper(message);\n\t}\n\n\tprivate static final long serialVersionUID = -5891004752785553015L;\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/SecurityBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\n/**\n * Interface for building an Object\n *\n * @param <O> The type of the Object being built\n * @author Rob Winch\n * @since 3.2\n */\npublic interface SecurityBuilder<O> {\n\n\t/**\n\t * Builds the object and returns it or null.\n\t * @return the Object to be built or null if the implementation allows it.\n\t * @throws Exception if an error occurred when building the Object\n\t */\n\tO build();\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/SecurityConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\n/**\n * Allows for configuring a {@link SecurityBuilder}. All {@link SecurityConfigurer} first\n * have their {@link #init(SecurityBuilder)} method invoked. After all\n * {@link #init(SecurityBuilder)} methods have been invoked, each\n * {@link #configure(SecurityBuilder)} method is invoked.\n *\n * @param <O> The object being built by the {@link SecurityBuilder} B\n * @param <B> The {@link SecurityBuilder} that builds objects of type O. This is also the\n * {@link SecurityBuilder} that is being configured.\n * @author Rob Winch\n * @see AbstractConfiguredSecurityBuilder\n */\npublic interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {\n\n\t/**\n\t * Initialize the {@link SecurityBuilder}. Here only shared state should be created\n\t * and modified, but not properties on the {@link SecurityBuilder} used for building\n\t * the object. This ensures that the {@link #configure(SecurityBuilder)} method uses\n\t * the correct shared objects when building. Configurers should be applied here.\n\t * @param builder\n\t * @throws Exception\n\t */\n\tvoid init(B builder);\n\n\t/**\n\t * Configure the {@link SecurityBuilder} by setting the necessary properties on the\n\t * {@link SecurityBuilder}.\n\t * @param builder\n\t * @throws Exception\n\t */\n\tvoid configure(B builder);\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/SecurityConfigurerAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.core.GenericTypeResolver;\nimport org.springframework.core.annotation.AnnotationAwareOrderComparator;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.util.Assert;\n\n/**\n * A base class for {@link SecurityConfigurer} that allows subclasses to only implement\n * the methods they are interested in. It also provides a mechanism for using the\n * {@link SecurityConfigurer} and when done gaining access to the {@link SecurityBuilder}\n * that is being configured.\n *\n * @param <O> The Object being built by B\n * @param <B> The Builder that is building O and is configured by\n * {@link SecurityConfigurerAdapter}\n * @author Rob Winch\n * @author Wallace Wadge\n */\npublic abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> {\n\n\tprivate B securityBuilder;\n\n\tprivate CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();\n\n\t@Override\n\tpublic void init(B builder) {\n\t}\n\n\t@Override\n\tpublic void configure(B builder) {\n\t}\n\n\t/**\n\t * Gets the {@link SecurityBuilder}. Cannot be null.\n\t * @return the {@link SecurityBuilder}\n\t * @throws IllegalStateException if {@link SecurityBuilder} is null\n\t */\n\tprotected final B getBuilder() {\n\t\tAssert.state(this.securityBuilder != null, \"securityBuilder cannot be null\");\n\t\treturn this.securityBuilder;\n\t}\n\n\t/**\n\t * Performs post processing of an object. The default is to delegate to the\n\t * {@link ObjectPostProcessor}.\n\t * @param object the Object to post process\n\t * @return the possibly modified Object to use\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprotected <T> T postProcess(T object) {\n\t\treturn (T) this.objectPostProcessor.postProcess(object);\n\t}\n\n\t/**\n\t * Adds an {@link ObjectPostProcessor} to be used for this\n\t * {@link SecurityConfigurerAdapter}. The default implementation does nothing to the\n\t * object.\n\t * @param objectPostProcessor the {@link ObjectPostProcessor} to use\n\t */\n\tpublic void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {\n\t\tthis.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityBuilder} to be used. This is automatically set when using\n\t * {@link AbstractConfiguredSecurityBuilder#with(SecurityConfigurerAdapter, Customizer)}\n\t * @param builder the {@link SecurityBuilder} to set\n\t */\n\tpublic void setBuilder(B builder) {\n\t\tthis.securityBuilder = builder;\n\t}\n\n\t/**\n\t * An {@link ObjectPostProcessor} that delegates work to numerous\n\t * {@link ObjectPostProcessor} implementations.\n\t *\n\t * @author Rob Winch\n\t */\n\tprivate static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\tprivate List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();\n\n\t\t@Override\n\t\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\t\tpublic Object postProcess(Object object) {\n\t\t\tfor (ObjectPostProcessor opp : this.postProcessors) {\n\t\t\t\tClass<?> oppClass = opp.getClass();\n\t\t\t\tClass<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass, ObjectPostProcessor.class);\n\t\t\t\tif (oppType == null || oppType.isAssignableFrom(object.getClass())) {\n\t\t\t\t\tobject = opp.postProcess(object);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t/**\n\t\t * Adds an {@link ObjectPostProcessor} to use\n\t\t * @param objectPostProcessor the {@link ObjectPostProcessor} to add\n\t\t * @return true if the {@link ObjectPostProcessor} was added, else false\n\t\t */\n\t\tprivate boolean addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {\n\t\t\tboolean result = this.postProcessors.add(objectPostProcessor);\n\t\t\tthis.postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/ProviderManagerBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.config.annotation.SecurityBuilder;\n\n/**\n * Interface for operating on a SecurityBuilder that creates a {@link ProviderManager}\n *\n * @param <B> the type of the {@link SecurityBuilder}\n * @author Rob Winch\n */\npublic interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>>\n\t\textends SecurityBuilder<AuthenticationManager> {\n\n\t/**\n\t * Add authentication based upon the custom {@link AuthenticationProvider} that is\n\t * passed in. Since the {@link AuthenticationProvider} implementation is unknown, all\n\t * customizations must be done externally and the {@link ProviderManagerBuilder} is\n\t * returned immediately.\n\t *\n\t * Note that an Exception is thrown if an error occurs when adding the\n\t * {@link AuthenticationProvider}.\n\t * @return a {@link ProviderManagerBuilder} to allow further authentication to be\n\t * provided to the {@link ProviderManagerBuilder}\n\t */\n\tB authenticationProvider(AuthenticationProvider authenticationProvider);\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.builders;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;\nimport org.springframework.security.config.annotation.SecurityBuilder;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;\nimport org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;\nimport org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;\nimport org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;\nimport org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.util.Assert;\n\n/**\n * {@link SecurityBuilder} used to create an {@link AuthenticationManager}. Allows for\n * easily building in memory authentication, LDAP authentication, JDBC based\n * authentication, adding {@link UserDetailsService}, and adding\n * {@link AuthenticationProvider}'s.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class AuthenticationManagerBuilder\n\t\textends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>\n\t\timplements ProviderManagerBuilder<AuthenticationManagerBuilder> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate AuthenticationManager parentAuthenticationManager;\n\n\tprivate List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate UserDetailsService defaultUserDetailsService;\n\n\tprivate Boolean eraseCredentials;\n\n\tprivate AuthenticationEventPublisher eventPublisher;\n\n\t/**\n\t * Creates a new instance\n\t * @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.\n\t */\n\tpublic AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor, true);\n\t}\n\n\t/**\n\t * Allows providing a parent {@link AuthenticationManager} that will be tried if this\n\t * {@link AuthenticationManager} was unable to attempt to authenticate the provided\n\t * {@link Authentication}.\n\t * @param authenticationManager the {@link AuthenticationManager} that should be used\n\t * if the current {@link AuthenticationManager} was unable to attempt to authenticate\n\t * the provided {@link Authentication}.\n\t * @return the {@link AuthenticationManagerBuilder} for further adding types of\n\t * authentication\n\t */\n\tpublic AuthenticationManagerBuilder parentAuthenticationManager(AuthenticationManager authenticationManager) {\n\t\tif (authenticationManager instanceof ProviderManager) {\n\t\t\teraseCredentials(((ProviderManager) authenticationManager).isEraseCredentialsAfterAuthentication());\n\t\t}\n\t\tthis.parentAuthenticationManager = authenticationManager;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationEventPublisher}\n\t * @param eventPublisher the {@link AuthenticationEventPublisher} to use\n\t * @return the {@link AuthenticationManagerBuilder} for further customizations\n\t */\n\tpublic AuthenticationManagerBuilder authenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {\n\t\tAssert.notNull(eventPublisher, \"AuthenticationEventPublisher cannot be null\");\n\t\tthis.eventPublisher = eventPublisher;\n\t\treturn this;\n\t}\n\n\t/**\n\t * @param eraseCredentials true if {@link AuthenticationManager} should clear the\n\t * credentials from the {@link Authentication} object after authenticating\n\t * @return the {@link AuthenticationManagerBuilder} for further customizations\n\t */\n\tpublic AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {\n\t\tthis.eraseCredentials = eraseCredentials;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add in memory authentication to the {@link AuthenticationManagerBuilder} and return\n\t * a {@link InMemoryUserDetailsManagerConfigurer} to allow customization of the in\n\t * memory authentication.\n\t *\n\t * <p>\n\t * This method also ensure that a {@link UserDetailsService} is available for the\n\t * {@link #getDefaultUserDetailsService()} method. Note that additional\n\t * {@link UserDetailsService}'s may override this {@link UserDetailsService} as the\n\t * default.\n\t * </p>\n\t * @return a {@link InMemoryUserDetailsManagerConfigurer} to allow customization of\n\t * the in memory authentication\n\t * @throws Exception if an error occurs when adding the in memory authentication\n\t */\n\tpublic InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication() {\n\t\treturn apply(new InMemoryUserDetailsManagerConfigurer<>());\n\t}\n\n\t/**\n\t * Add JDBC authentication to the {@link AuthenticationManagerBuilder} and return a\n\t * {@link JdbcUserDetailsManagerConfigurer} to allow customization of the JDBC\n\t * authentication.\n\t *\n\t * <p>\n\t * When using with a persistent data store, it is best to add users external of\n\t * configuration using something like <a href=\"https://flywaydb.org/\">Flyway</a> or\n\t * <a href=\"https://www.liquibase.org/\">Liquibase</a> to create the schema and adding\n\t * users to ensure these steps are only done once and that the optimal SQL is used.\n\t * </p>\n\t *\n\t * <p>\n\t * This method also ensure that a {@link UserDetailsService} is available for the\n\t * {@link #getDefaultUserDetailsService()} method. Note that additional\n\t * {@link UserDetailsService}'s may override this {@link UserDetailsService} as the\n\t * default. See the <a href=\n\t * \"https://docs.spring.io/spring-security/reference/servlet/appendix/database-schema.html\"\n\t * >User Schema</a> section of the reference for the default schema.\n\t * </p>\n\t * @return a {@link JdbcUserDetailsManagerConfigurer} to allow customization of the\n\t * JDBC authentication\n\t * @throws Exception if an error occurs when adding the JDBC authentication\n\t */\n\tpublic JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() {\n\t\treturn apply(new JdbcUserDetailsManagerConfigurer<>());\n\t}\n\n\t/**\n\t * Add authentication based upon the custom {@link UserDetailsService} that is passed\n\t * in. It then returns a {@link DaoAuthenticationConfigurer} to allow customization of\n\t * the authentication.\n\t *\n\t * <p>\n\t * This method also ensure that the {@link UserDetailsService} is available for the\n\t * {@link #getDefaultUserDetailsService()} method. Note that additional\n\t * {@link UserDetailsService}'s may override this {@link UserDetailsService} as the\n\t * default.\n\t * </p>\n\t * @return a {@link DaoAuthenticationConfigurer} to allow customization of the DAO\n\t * authentication\n\t * @throws Exception if an error occurs when adding the {@link UserDetailsService}\n\t * based authentication\n\t */\n\tpublic <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(\n\t\t\tT userDetailsService) {\n\t\tthis.defaultUserDetailsService = userDetailsService;\n\t\treturn apply(new DaoAuthenticationConfigurer<>(userDetailsService));\n\t}\n\n\t/**\n\t * Add LDAP authentication to the {@link AuthenticationManagerBuilder} and return a\n\t * {@link LdapAuthenticationProviderConfigurer} to allow customization of the LDAP\n\t * authentication.\n\t *\n\t * <p>\n\t * This method <b>does NOT</b> ensure that a {@link UserDetailsService} is available\n\t * for the {@link #getDefaultUserDetailsService()} method.\n\t * @return a {@link LdapAuthenticationProviderConfigurer} to allow customization of\n\t * the LDAP authentication\n\t * @throws Exception if an error occurs when adding the LDAP authentication\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthentication() throws Exception {\n\t\tLdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldap = new LdapAuthenticationProviderConfigurer<>();\n\t\twith(ldap);\n\t\treturn ldap;\n\t}\n\n\t/**\n\t * Add authentication based upon the custom {@link AuthenticationProvider} that is\n\t * passed in. Since the {@link AuthenticationProvider} implementation is unknown, all\n\t * customizations must be done externally and the {@link AuthenticationManagerBuilder}\n\t * is returned immediately.\n\t *\n\t * <p>\n\t * This method <b>does NOT</b> ensure that the {@link UserDetailsService} is available\n\t * for the {@link #getDefaultUserDetailsService()} method.\n\t *\n\t * Note that an {@link Exception} might be thrown if an error occurs when adding the\n\t * {@link AuthenticationProvider}.\n\t * @return a {@link AuthenticationManagerBuilder} to allow further authentication to\n\t * be provided to the {@link AuthenticationManagerBuilder}\n\t */\n\t@Override\n\tpublic AuthenticationManagerBuilder authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t@Override\n\tprotected ProviderManager performBuild() {\n\t\tif (!isConfigured()) {\n\t\t\tthis.logger.debug(\"No authenticationProviders and no parentAuthenticationManager defined. Returning null.\");\n\t\t\treturn null;\n\t\t}\n\t\tProviderManager providerManager = new ProviderManager(this.authenticationProviders,\n\t\t\t\tthis.parentAuthenticationManager);\n\t\tif (this.eraseCredentials != null) {\n\t\t\tproviderManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);\n\t\t}\n\t\tif (this.eventPublisher != null) {\n\t\t\tproviderManager.setAuthenticationEventPublisher(this.eventPublisher);\n\t\t}\n\t\tproviderManager = postProcess(providerManager);\n\t\treturn providerManager;\n\t}\n\n\t/**\n\t * Determines if the {@link AuthenticationManagerBuilder} is configured to build a non\n\t * null {@link AuthenticationManager}. This means that either a non-null parent is\n\t * specified or at least one {@link AuthenticationProvider} has been specified.\n\t *\n\t * <p>\n\t * When using {@link SecurityConfigurer} instances, the\n\t * {@link AuthenticationManagerBuilder} will not be configured until the\n\t * {@link SecurityConfigurer#configure(SecurityBuilder)} methods. This means a\n\t * {@link SecurityConfigurer} that is last could check this method and provide a\n\t * default configuration in the {@link SecurityConfigurer#configure(SecurityBuilder)}\n\t * method.\n\t * @return true, if {@link AuthenticationManagerBuilder} is configured, otherwise\n\t * false\n\t */\n\tpublic boolean isConfigured() {\n\t\treturn !this.authenticationProviders.isEmpty() || this.parentAuthenticationManager != null;\n\t}\n\n\t/**\n\t * Gets the default {@link UserDetailsService} for the\n\t * {@link AuthenticationManagerBuilder}. The result may be null in some circumstances.\n\t * @return the default {@link UserDetailsService} for the\n\t * {@link AuthenticationManagerBuilder}\n\t */\n\tpublic UserDetailsService getDefaultUserDetailsService() {\n\t\treturn this.defaultUserDetailsService;\n\t}\n\n\t/**\n\t * Captures the {@link UserDetailsService} from any {@link UserDetailsAwareConfigurer}\n\t * .\n\t * @param configurer the {@link UserDetailsAwareConfigurer} to capture the\n\t * {@link UserDetailsService} from.\n\t * @return the {@link UserDetailsAwareConfigurer} for further customizations\n\t * @throws Exception if an error occurs\n\t */\n\tprivate <C extends UserDetailsAwareConfigurer<AuthenticationManagerBuilder, ? extends UserDetailsService>> C apply(\n\t\t\tC configurer) {\n\t\tthis.defaultUserDetailsService = configurer.getUserDetailsService();\n\t\twith(configurer);\n\t\treturn configurer;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.aop.framework.ProxyFactoryBean;\nimport org.springframework.aop.target.LazyInitTargetSource;\nimport org.springframework.beans.factory.BeanFactoryUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.annotation.AnnotationAwareOrderComparator;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.lang.Contract;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;\nimport org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;\nimport org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;\nimport org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.util.Assert;\n\n/**\n * Exports the authentication {@link Configuration}\n *\n * @author Rob Winch\n * @author Ngoc Nhan\n * @since 3.2\n *\n */\n@Configuration(proxyBeanMethods = false)\n@Import(ObjectPostProcessorConfiguration.class)\npublic class AuthenticationConfiguration {\n\n\tprivate AtomicBoolean buildingAuthenticationManager = new AtomicBoolean();\n\n\tprivate ApplicationContext applicationContext;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate boolean authenticationManagerInitialized;\n\n\tprivate List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections.emptyList();\n\n\tprivate ObjectPostProcessor<Object> objectPostProcessor;\n\n\t@Bean\n\tpublic AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,\n\t\t\tApplicationContext context) {\n\t\tLazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);\n\t\tAuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);\n\t\tDefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(\n\t\t\t\tobjectPostProcessor, defaultPasswordEncoder);\n\t\tif (authenticationEventPublisher != null) {\n\t\t\tresult.authenticationEventPublisher(authenticationEventPublisher);\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Bean\n\tpublic static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(\n\t\t\tApplicationContext context) {\n\t\treturn new EnableGlobalAuthenticationAutowiredConfigurer(context);\n\t}\n\n\t@Bean\n\tpublic static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(\n\t\t\tApplicationContext context) {\n\t\treturn new InitializeUserDetailsBeanManagerConfigurer(context);\n\t}\n\n\t@Bean\n\tpublic static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(\n\t\t\tApplicationContext context) {\n\t\treturn new InitializeAuthenticationProviderBeanManagerConfigurer(context);\n\t}\n\n\tpublic AuthenticationManager getAuthenticationManager() {\n\t\tif (this.authenticationManagerInitialized) {\n\t\t\treturn this.authenticationManager;\n\t\t}\n\t\tAuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);\n\t\tif (this.buildingAuthenticationManager.getAndSet(true)) {\n\t\t\treturn new AuthenticationManagerDelegator(authBuilder);\n\t\t}\n\t\tfor (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {\n\t\t\tauthBuilder.apply(config);\n\t\t}\n\t\tthis.authenticationManager = authBuilder.build();\n\t\tif (this.authenticationManager == null) {\n\t\t\tthis.authenticationManager = getAuthenticationManagerBean();\n\t\t}\n\t\tthis.authenticationManagerInitialized = true;\n\t\treturn this.authenticationManager;\n\t}\n\n\t@Autowired(required = false)\n\tpublic void setGlobalAuthenticationConfigurers(List<GlobalAuthenticationConfigurerAdapter> configurers) {\n\t\tconfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);\n\t\tthis.globalAuthConfigurers = configurers;\n\t}\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext applicationContext) {\n\t\tthis.applicationContext = applicationContext;\n\t}\n\n\t@Autowired\n\tpublic void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tthis.objectPostProcessor = objectPostProcessor;\n\t}\n\n\tprivate AuthenticationEventPublisher getAuthenticationEventPublisher(ApplicationContext context) {\n\t\tif (context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) {\n\t\t\treturn context.getBean(AuthenticationEventPublisher.class);\n\t\t}\n\t\treturn this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T> T lazyBean(Class<T> interfaceName) {\n\t\tLazyInitTargetSource lazyTargetSource = new LazyInitTargetSource();\n\t\tString[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.applicationContext,\n\t\t\t\tinterfaceName);\n\t\tif (beanNamesForType.length == 0) {\n\t\t\treturn null;\n\t\t}\n\t\tString beanName = getBeanName(interfaceName, beanNamesForType);\n\t\tlazyTargetSource.setTargetBeanName(beanName);\n\t\tlazyTargetSource.setBeanFactory(this.applicationContext);\n\t\tProxyFactoryBean proxyFactory = new ProxyFactoryBean();\n\t\tproxyFactory = this.objectPostProcessor.postProcess(proxyFactory);\n\t\tproxyFactory.setTargetSource(lazyTargetSource);\n\t\treturn (T) proxyFactory.getObject();\n\t}\n\n\tprivate <T> String getBeanName(Class<T> interfaceName, String[] beanNamesForType) {\n\t\tif (beanNamesForType.length == 1) {\n\t\t\treturn beanNamesForType[0];\n\t\t}\n\t\tList<String> primaryBeanNames = getPrimaryBeanNames(beanNamesForType);\n\t\tAssert.isTrue(primaryBeanNames.size() != 0, () -> \"Found \" + beanNamesForType.length + \" beans for type \"\n\t\t\t\t+ interfaceName + \", but none marked as primary\");\n\t\tAssert.isTrue(primaryBeanNames.size() == 1,\n\t\t\t\t() -> \"Found \" + primaryBeanNames.size() + \" beans for type \" + interfaceName + \" marked as primary\");\n\t\treturn primaryBeanNames.get(0);\n\t}\n\n\tprivate List<String> getPrimaryBeanNames(String[] beanNamesForType) {\n\t\tList<String> list = new ArrayList<>();\n\t\tif (!(this.applicationContext instanceof ConfigurableApplicationContext)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tfor (String beanName : beanNamesForType) {\n\t\t\tif (((ConfigurableApplicationContext) this.applicationContext).getBeanFactory()\n\t\t\t\t.getBeanDefinition(beanName)\n\t\t\t\t.isPrimary()) {\n\t\t\t\tlist.add(beanName);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate AuthenticationManager getAuthenticationManagerBean() {\n\t\treturn lazyBean(AuthenticationManager.class);\n\t}\n\n\tprivate static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {\n\n\t\tprivate final ApplicationContext context;\n\n\t\tprivate static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);\n\n\t\tEnableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {\n\t\t\tthis.context = context;\n\t\t}\n\n\t\t@Override\n\t\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\t\tMap<String, Object> beansWithAnnotation = this.context\n\t\t\t\t.getBeansWithAnnotation(EnableGlobalAuthentication.class);\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(LogMessage.format(\"Eagerly initializing %s\", beansWithAnnotation));\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Prevents infinite recursion in the event that initializing the\n\t * AuthenticationManager.\n\t *\n\t * @author Rob Winch\n\t * @since 4.1.1\n\t */\n\tstatic final class AuthenticationManagerDelegator implements AuthenticationManager {\n\n\t\tprivate AuthenticationManagerBuilder delegateBuilder;\n\n\t\tprivate AuthenticationManager delegate;\n\n\t\tprivate final Object delegateMonitor = new Object();\n\n\t\tAuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {\n\t\t\tAssert.notNull(delegateBuilder, \"delegateBuilder cannot be null\");\n\t\t\tthis.delegateBuilder = delegateBuilder;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\tif (this.delegate != null) {\n\t\t\t\treturn this.delegate.authenticate(authentication);\n\t\t\t}\n\t\t\tsynchronized (this.delegateMonitor) {\n\t\t\t\tif (this.delegate == null) {\n\t\t\t\t\tthis.delegate = this.delegateBuilder.getObject();\n\t\t\t\t\tthis.delegateBuilder = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this.delegate.authenticate(authentication);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"AuthenticationManagerDelegator [delegate=\" + this.delegate + \"]\";\n\t\t}\n\n\t}\n\n\tstatic class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {\n\n\t\tprivate PasswordEncoder defaultPasswordEncoder;\n\n\t\t/**\n\t\t * Creates a new instance\n\t\t * @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.\n\t\t */\n\t\tDefaultPasswordEncoderAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,\n\t\t\t\tPasswordEncoder defaultPasswordEncoder) {\n\t\t\tsuper(objectPostProcessor);\n\t\t\tthis.defaultPasswordEncoder = defaultPasswordEncoder;\n\t\t}\n\n\t\t@Override\n\t\tpublic InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication() {\n\t\t\treturn super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);\n\t\t}\n\n\t\t@Override\n\t\tpublic JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() {\n\t\t\treturn super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);\n\t\t}\n\n\t\t@Override\n\t\tpublic <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(\n\t\t\t\tT userDetailsService) {\n\t\t\treturn super.userDetailsService(userDetailsService).passwordEncoder(this.defaultPasswordEncoder);\n\t\t}\n\n\t}\n\n\tstatic class LazyPasswordEncoder implements PasswordEncoder {\n\n\t\tprivate ApplicationContext applicationContext;\n\n\t\tprivate PasswordEncoder passwordEncoder;\n\n\t\tLazyPasswordEncoder(ApplicationContext applicationContext) {\n\t\t\tthis.applicationContext = applicationContext;\n\t\t}\n\n\t\t@Override\n\t\t@Contract(\"!null -> !null; null -> null\")\n\t\tpublic String encode(CharSequence rawPassword) {\n\t\t\treturn getPasswordEncoder().encode(rawPassword);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(CharSequence rawPassword, String encodedPassword) {\n\t\t\treturn getPasswordEncoder().matches(rawPassword, encodedPassword);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean upgradeEncoding(String encodedPassword) {\n\t\t\treturn getPasswordEncoder().upgradeEncoding(encodedPassword);\n\t\t}\n\n\t\tprivate PasswordEncoder getPasswordEncoder() {\n\t\t\tif (this.passwordEncoder != null) {\n\t\t\t\treturn this.passwordEncoder;\n\t\t\t}\n\t\t\tthis.passwordEncoder = this.applicationContext.getBeanProvider(PasswordEncoder.class)\n\t\t\t\t.getIfUnique(PasswordEncoderFactories::createDelegatingPasswordEncoder);\n\t\t\treturn this.passwordEncoder;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn getPasswordEncoder().toString();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationManagerBeanRegistrationAotProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport java.util.Set;\n\nimport org.springframework.aop.framework.AopProxyUtils;\nimport org.springframework.aot.generate.GenerationContext;\nimport org.springframework.aot.hint.ProxyHints;\nimport org.springframework.beans.factory.aot.BeanRegistrationAotContribution;\nimport org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;\nimport org.springframework.beans.factory.aot.BeanRegistrationCode;\nimport org.springframework.beans.factory.support.RegisteredBean;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.util.ClassUtils;\n\n/**\n * AOT {@code BeanRegistrationAotProcessor} that detects beans that implement\n * {@link AuthenticationManager} creates the required proxy hints.\n *\n * @author Marcus da Coregio\n * @since 6.0.1\n * @see AuthenticationConfiguration#getAuthenticationManager()\n */\nclass AuthenticationManagerBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {\n\n\t@Override\n\tpublic BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {\n\t\tClass<?> beanClass = registeredBean.getBeanClass();\n\t\tSet<Class<?>> allInterfacesForClass = ClassUtils.getAllInterfacesForClassAsSet(beanClass);\n\t\tif (allInterfacesForClass.contains(AuthenticationManager.class)) {\n\t\t\treturn new AuthenticationManagerBeanRegistrationAotContribution();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static class AuthenticationManagerBeanRegistrationAotContribution\n\t\t\timplements BeanRegistrationAotContribution {\n\n\t\t@Override\n\t\tpublic void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {\n\t\t\tProxyHints proxyHints = generationContext.getRuntimeHints().proxies();\n\t\t\tproxyHints.registerJdkProxy(AopProxyUtils.completeJdkProxyInterfaces(AuthenticationManager.class));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/EnableGlobalAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\n\n/**\n * The {@link EnableGlobalAuthentication} annotation signals that the annotated class can\n * be used to configure a global instance of {@link AuthenticationManagerBuilder}. For\n * example:\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableGlobalAuthentication\n * public class MyGlobalAuthenticationConfiguration {\n *\n * \t&#064;Bean\n * \tpublic UserDetailsService userDetailsService() {\n * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n * \t\t\t.username(&quot;user&quot;)\n * \t\t\t.password(&quot;password&quot;)\n * \t\t\t.roles(&quot;USER&quot;)\n * \t\t\t.build();\n * \t\tUserDetails admin = User.withDefaultPasswordEncoder()\n * \t\t\t.username(&quot;admin&quot;)\n * \t\t\t.password(&quot;password&quot;)\n * \t\t\t.roles(&quot;ADMIN&quot;, &quot;USER&quot;)\n * \t\t\t.build();\n * \t\treturn new InMemoryUserDetailsManager(user, admin);\n * \t}\n * }\n * </pre>\n *\n * Annotations that are annotated with {@link EnableGlobalAuthentication} also signal that\n * the annotated class can be used to configure a global instance of\n * {@link AuthenticationManagerBuilder}. For example:\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableWebSecurity\n * public class MyWebSecurityConfiguration {\n *\n * \t&#064;Bean\n * \tpublic UserDetailsService userDetailsService() {\n * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n * \t\t\t.username(&quot;user&quot;)\n * \t\t\t.password(&quot;password&quot;)\n * \t\t\t.roles(&quot;USER&quot;)\n * \t\t\t.build();\n * \t\tUserDetails admin = User.withDefaultPasswordEncoder()\n * \t\t\t.username(&quot;admin&quot;)\n * \t\t\t.password(&quot;password&quot;)\n * \t\t\t.roles(&quot;ADMIN&quot;, &quot;USER&quot;)\n * \t\t\t.build();\n * \t\treturn new InMemoryUserDetailsManager(user, admin);\n * \t}\n *\n * \t// Possibly more bean methods ...\n * }\n * </pre>\n *\n * The following annotations are annotated with {@link EnableGlobalAuthentication}\n *\n * <ul>\n * <li>{@link EnableWebSecurity}</li>\n * <li>{@link EnableGlobalMethodSecurity}</li>\n * </ul>\n *\n * Configuring {@link AuthenticationManagerBuilder} in a class without the\n * {@link EnableGlobalAuthentication} annotation has unpredictable results.\n *\n * @see EnableWebSecurity\n * @see EnableGlobalMethodSecurity\n * @author Rob Winch\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import(AuthenticationConfiguration.class)\npublic @interface EnableGlobalAuthentication {\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/GlobalAuthenticationConfigurerAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\n\n/**\n * A {@link SecurityConfigurer} that can be exposed as a bean to configure the global\n * {@link AuthenticationManagerBuilder}. Beans of this type are automatically used by\n * {@link AuthenticationConfiguration} to configure the global\n * {@link AuthenticationManagerBuilder}.\n *\n * @author Rob Winch\n * @since 5.0\n */\n@Order(100)\npublic abstract class GlobalAuthenticationConfigurerAdapter\n\t\timplements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {\n\n\t@Override\n\tpublic void init(AuthenticationManagerBuilder auth) {\n\t}\n\n\t@Override\n\tpublic void configure(AuthenticationManagerBuilder auth) {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/InitializeAuthenticationProviderBeanManagerConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport java.util.Arrays;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\n\n/**\n * Lazily initializes the global authentication with an {@link AuthenticationProvider} if\n * it is not yet configured and there is only a single Bean of that type.\n *\n * @author Rob Winch\n * @since 4.1\n */\n@Order(InitializeAuthenticationProviderBeanManagerConfigurer.DEFAULT_ORDER)\nclass InitializeAuthenticationProviderBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {\n\n\tstatic final int DEFAULT_ORDER = InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER - 100;\n\n\tprivate final ApplicationContext context;\n\n\t/**\n\t * @param context the ApplicationContext to look up beans.\n\t */\n\tInitializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {\n\t\tthis.context = context;\n\t}\n\n\t@Override\n\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\tauth.apply(new InitializeAuthenticationProviderManagerConfigurer());\n\t}\n\n\tclass InitializeAuthenticationProviderManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {\n\n\t\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t\t@Override\n\t\tpublic void configure(AuthenticationManagerBuilder auth) {\n\t\t\tif (auth.isConfigured()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString[] beanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context\n\t\t\t\t.getBeanNamesForType(AuthenticationProvider.class);\n\t\t\tif (beanNames.length == 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (beanNames.length > 1) {\n\t\t\t\tthis.logger.info(LogMessage.format(\"Found %s AuthenticationProvider beans, with names %s. \"\n\t\t\t\t\t\t+ \"Global Authentication Manager will not be configured with AuthenticationProviders. \"\n\t\t\t\t\t\t+ \"Consider publishing a single AuthenticationProvider bean, or wiring your Providers directly \"\n\t\t\t\t\t\t+ \"using the DSL.\", beanNames.length, Arrays.toString(beanNames)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tAuthenticationProvider authenticationProvider = InitializeAuthenticationProviderBeanManagerConfigurer.this.context\n\t\t\t\t.getBean(beanNames[0], AuthenticationProvider.class);\n\t\t\tauth.authenticationProvider(authenticationProvider);\n\t\t\tthis.logger.info(LogMessage.format(\n\t\t\t\t\t\"Global AuthenticationManager configured with AuthenticationProvider bean with name %s\",\n\t\t\t\t\tbeanNames[0]));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/InitializeUserDetailsBeanManagerConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport java.util.Arrays;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.authentication.password.CompromisedPasswordChecker;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.core.userdetails.UserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n/**\n * Lazily initializes the global authentication with a {@link UserDetailsService} if it is\n * not yet configured and there is only a single Bean of that type. Optionally, if a\n * {@link PasswordEncoder} is defined will wire this up too.\n *\n * @author Rob Winch\n * @author Ngoc Nhan\n * @since 4.1\n */\n@Order(InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER)\nclass InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {\n\n\tstatic final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;\n\n\tprivate final ApplicationContext context;\n\n\t/**\n\t * @param context\n\t */\n\tInitializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {\n\t\tthis.context = context;\n\t}\n\n\t@Override\n\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\tauth.apply(new InitializeUserDetailsManagerConfigurer());\n\t}\n\n\tclass InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {\n\n\t\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t\t@Override\n\t\tpublic void configure(AuthenticationManagerBuilder auth) {\n\t\t\tString[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context\n\t\t\t\t.getBeanNamesForType(UserDetailsService.class);\n\t\t\tif (auth.isConfigured()) {\n\t\t\t\tif (beanNames.length > 0) {\n\t\t\t\t\tthis.logger.warn(\"Global AuthenticationManager configured with an AuthenticationProvider bean. \"\n\t\t\t\t\t\t\t+ \"UserDetailsService beans will not be used by Spring Security for automatically configuring username/password login. \"\n\t\t\t\t\t\t\t+ \"Consider removing the AuthenticationProvider bean. \"\n\t\t\t\t\t\t\t+ \"Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. \"\n\t\t\t\t\t\t\t+ \"If the current configuration is intentional, to turn off this warning, \"\n\t\t\t\t\t\t\t+ \"increase the logging level of 'org.springframework.security.config.annotation.authentication.configuration.InitializeUserDetailsBeanManagerConfigurer' to ERROR\");\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (beanNames.length == 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (beanNames.length > 1) {\n\t\t\t\tthis.logger.warn(LogMessage.format(\"Found %s UserDetailsService beans, with names %s. \"\n\t\t\t\t\t\t+ \"Global Authentication Manager will not use a UserDetailsService for username/password login. \"\n\t\t\t\t\t\t+ \"Consider publishing a single UserDetailsService bean.\", beanNames.length,\n\t\t\t\t\t\tArrays.toString(beanNames)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tUserDetailsService userDetailsService = InitializeUserDetailsBeanManagerConfigurer.this.context\n\t\t\t\t.getBean(beanNames[0], UserDetailsService.class);\n\t\t\tPasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);\n\t\t\tUserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);\n\t\t\tCompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);\n\t\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);\n\t\t\tif (passwordEncoder != null) {\n\t\t\t\tprovider.setPasswordEncoder(passwordEncoder);\n\t\t\t}\n\t\t\tif (passwordManager != null) {\n\t\t\t\tprovider.setUserDetailsPasswordService(passwordManager);\n\t\t\t}\n\t\t\tif (passwordChecker != null) {\n\t\t\t\tprovider.setCompromisedPasswordChecker(passwordChecker);\n\t\t\t}\n\t\t\tprovider.afterPropertiesSet();\n\t\t\tauth.authenticationProvider(provider);\n\t\t\tthis.logger.info(LogMessage.format(\n\t\t\t\t\t\"Global AuthenticationManager configured with UserDetailsService bean with name %s\", beanNames[0]));\n\t\t}\n\n\t\t/**\n\t\t * @return a bean of the requested class if there's just a single registered\n\t\t * component, null otherwise.\n\t\t */\n\t\tprivate <T> T getBeanOrNull(Class<T> type) {\n\t\t\treturn InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanProvider(type).getIfUnique();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/ldap/LdapAuthenticationProviderConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.ldap;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\n\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;\nimport org.springframework.security.ldap.authentication.BindAuthenticator;\nimport org.springframework.security.ldap.authentication.LdapAuthenticationProvider;\nimport org.springframework.security.ldap.authentication.LdapAuthenticator;\nimport org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;\nimport org.springframework.security.ldap.search.FilterBasedLdapUserSearch;\nimport org.springframework.security.ldap.search.LdapUserSearch;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;\nimport org.springframework.security.ldap.userdetails.PersonContextMapper;\nimport org.springframework.security.ldap.userdetails.UserDetailsContextMapper;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Configures LDAP {@link AuthenticationProvider} in the {@link ProviderManagerBuilder}.\n *\n * @param <B> the {@link ProviderManagerBuilder} type that this is configuring.\n * @author Rob Winch\n * @author Eddú Meléndez\n * @author Tony Dalbrekt\n * @since 3.2\n */\npublic class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>\n\t\textends SecurityConfigurerAdapter<AuthenticationManager, B> {\n\n\tprivate static final String UNBOUNDID_CLASSNAME = \"com.unboundid.ldap.listener.InMemoryDirectoryServer\";\n\n\tprivate static final boolean unboundIdPresent;\n\n\tprivate String groupRoleAttribute = \"cn\";\n\n\tprivate String groupSearchBase = \"\";\n\n\tprivate boolean groupSearchSubtree = false;\n\n\tprivate String groupSearchFilter = \"(uniqueMember={0})\";\n\n\tprivate String rolePrefix = \"ROLE_\";\n\n\tprivate String userSearchBase = \"\"; // only for search\n\n\tprivate String userSearchFilter = null; // \"uid={0}\"; // only for search\n\n\tprivate String[] userDnPatterns;\n\n\tprivate BaseLdapPathContextSource contextSource;\n\n\tprivate ContextSourceBuilder contextSourceBuilder = new ContextSourceBuilder();\n\n\tprivate UserDetailsContextMapper userDetailsContextMapper;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\tprivate String passwordAttribute;\n\n\tprivate LdapAuthoritiesPopulator ldapAuthoritiesPopulator;\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper;\n\n\tstatic {\n\t\tClassLoader classLoader = LdapAuthenticationProviderConfigurer.class.getClassLoader();\n\t\tunboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader);\n\t}\n\n\tprivate LdapAuthenticationProvider build() {\n\t\tBaseLdapPathContextSource contextSource = getContextSource();\n\t\tLdapAuthenticator ldapAuthenticator = createLdapAuthenticator(contextSource);\n\t\tLdapAuthoritiesPopulator authoritiesPopulator = getLdapAuthoritiesPopulator();\n\t\tLdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(ldapAuthenticator,\n\t\t\t\tauthoritiesPopulator);\n\t\tldapAuthenticationProvider.setAuthoritiesMapper(getAuthoritiesMapper());\n\t\tif (this.userDetailsContextMapper != null) {\n\t\t\tldapAuthenticationProvider.setUserDetailsContextMapper(this.userDetailsContextMapper);\n\t\t}\n\t\treturn ldapAuthenticationProvider;\n\t}\n\n\t/**\n\t * Specifies the {@link LdapAuthoritiesPopulator}.\n\t * @param ldapAuthoritiesPopulator the {@link LdapAuthoritiesPopulator} the default is\n\t * {@link DefaultLdapAuthoritiesPopulator}\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> ldapAuthoritiesPopulator(\n\t\t\tLdapAuthoritiesPopulator ldapAuthoritiesPopulator) {\n\t\tthis.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link ObjectPostProcessor} for this class.\n\t * @param objectPostProcessor\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {\n\t\taddObjectPostProcessor(objectPostProcessor);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the {@link LdapAuthoritiesPopulator} and defaults to\n\t * {@link DefaultLdapAuthoritiesPopulator}\n\t * @return the {@link LdapAuthoritiesPopulator}\n\t */\n\tprivate LdapAuthoritiesPopulator getLdapAuthoritiesPopulator() {\n\t\tif (this.ldapAuthoritiesPopulator != null) {\n\t\t\treturn this.ldapAuthoritiesPopulator;\n\t\t}\n\t\tDefaultLdapAuthoritiesPopulator defaultAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator(\n\t\t\t\tthis.contextSource, this.groupSearchBase);\n\t\tdefaultAuthoritiesPopulator.setGroupRoleAttribute(this.groupRoleAttribute);\n\t\tdefaultAuthoritiesPopulator.setGroupSearchFilter(this.groupSearchFilter);\n\t\tdefaultAuthoritiesPopulator.setSearchSubtree(this.groupSearchSubtree);\n\t\tdefaultAuthoritiesPopulator.setRolePrefix(this.rolePrefix);\n\t\tthis.ldapAuthoritiesPopulator = postProcess(defaultAuthoritiesPopulator);\n\t\treturn defaultAuthoritiesPopulator;\n\t}\n\n\t/**\n\t * Specifies the {@link GrantedAuthoritiesMapper}.\n\t * @param grantedAuthoritiesMapper the {@link GrantedAuthoritiesMapper} the default is\n\t * {@link SimpleAuthorityMapper}\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t *\n\t * @since 4.1.1\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> authoritiesMapper(\n\t\t\tGrantedAuthoritiesMapper grantedAuthoritiesMapper) {\n\t\tthis.authoritiesMapper = grantedAuthoritiesMapper;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the {@link GrantedAuthoritiesMapper} and defaults to\n\t * {@link SimpleAuthorityMapper}.\n\t * @return the {@link GrantedAuthoritiesMapper}\n\t * @throws Exception if errors in {@link SimpleAuthorityMapper#afterPropertiesSet()}\n\t */\n\tprotected GrantedAuthoritiesMapper getAuthoritiesMapper() {\n\t\tif (this.authoritiesMapper != null) {\n\t\t\treturn this.authoritiesMapper;\n\t\t}\n\t\tSimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();\n\t\tsimpleAuthorityMapper.setPrefix(this.rolePrefix);\n\t\tsimpleAuthorityMapper.afterPropertiesSet();\n\t\tthis.authoritiesMapper = simpleAuthorityMapper;\n\t\treturn simpleAuthorityMapper;\n\t}\n\n\t/**\n\t * Creates the {@link LdapAuthenticator} to use\n\t * @param contextSource the {@link BaseLdapPathContextSource} to use\n\t * @return the {@link LdapAuthenticator} to use\n\t */\n\tprivate LdapAuthenticator createLdapAuthenticator(BaseLdapPathContextSource contextSource) {\n\t\tAbstractLdapAuthenticator ldapAuthenticator = (this.passwordEncoder != null)\n\t\t\t\t? createPasswordCompareAuthenticator(contextSource) : createBindAuthenticator(contextSource);\n\t\tLdapUserSearch userSearch = createUserSearch();\n\t\tif (userSearch != null) {\n\t\t\tldapAuthenticator.setUserSearch(userSearch);\n\t\t}\n\t\tif (this.userDnPatterns != null && this.userDnPatterns.length > 0) {\n\t\t\tldapAuthenticator.setUserDnPatterns(this.userDnPatterns);\n\t\t}\n\t\treturn postProcess(ldapAuthenticator);\n\t}\n\n\t/**\n\t * Creates {@link PasswordComparisonAuthenticator}\n\t * @param contextSource the {@link BaseLdapPathContextSource} to use\n\t * @return\n\t */\n\tprivate PasswordComparisonAuthenticator createPasswordCompareAuthenticator(\n\t\t\tBaseLdapPathContextSource contextSource) {\n\t\tPasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(contextSource);\n\t\tif (this.passwordAttribute != null) {\n\t\t\tldapAuthenticator.setPasswordAttributeName(this.passwordAttribute);\n\t\t}\n\t\tldapAuthenticator.setPasswordEncoder(this.passwordEncoder);\n\t\treturn ldapAuthenticator;\n\t}\n\n\t/**\n\t * Creates a {@link BindAuthenticator}\n\t * @param contextSource the {@link BaseLdapPathContextSource} to use\n\t * @return the {@link BindAuthenticator} to use\n\t */\n\tprivate BindAuthenticator createBindAuthenticator(BaseLdapPathContextSource contextSource) {\n\t\treturn new BindAuthenticator(contextSource);\n\t}\n\n\tprivate LdapUserSearch createUserSearch() {\n\t\tif (this.userSearchFilter == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new FilterBasedLdapUserSearch(this.userSearchBase, this.userSearchFilter, this.contextSource);\n\t}\n\n\t/**\n\t * Specifies the {@link BaseLdapPathContextSource} to be used. If not specified, an\n\t * embedded LDAP server will be created using {@link #contextSource()}.\n\t * @param contextSource the {@link BaseLdapPathContextSource} to use\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t * @see #contextSource()\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> contextSource(BaseLdapPathContextSource contextSource) {\n\t\tthis.contextSource = contextSource;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows easily configuring of a {@link BaseLdapPathContextSource} with defaults\n\t * pointing to an embedded LDAP server that is created.\n\t * @return the {@link ContextSourceBuilder} for further customizations\n\t */\n\tpublic ContextSourceBuilder contextSource() {\n\t\treturn this.contextSourceBuilder;\n\t}\n\n\t/**\n\t * Specifies the {@link org.springframework.security.crypto.password.PasswordEncoder}\n\t * to be used when authenticating with password comparison.\n\t * @param passwordEncoder the\n\t * {@link org.springframework.security.crypto.password.PasswordEncoder} to use\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customization\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> passwordEncoder(\n\t\t\tfinal org.springframework.security.crypto.password.PasswordEncoder passwordEncoder) {\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder must not be null.\");\n\t\tthis.passwordEncoder = passwordEncoder;\n\t\treturn this;\n\t}\n\n\t/**\n\t * If your users are at a fixed location in the directory (i.e. you can work out the\n\t * DN directly from the username without doing a directory search), you can use this\n\t * attribute to map directly to the DN. It maps directly to the userDnPatterns\n\t * property of AbstractLdapAuthenticator. The value is a specific pattern used to\n\t * build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present\n\t * and will be substituted with the username.\n\t * @param userDnPatterns the LDAP patterns for finding the usernames\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> userDnPatterns(String... userDnPatterns) {\n\t\tthis.userDnPatterns = userDnPatterns;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows explicit customization of the loaded user object by specifying a\n\t * UserDetailsContextMapper bean which will be called with the context information\n\t * from the user's directory entry.\n\t * @param userDetailsContextMapper the {@link UserDetailsContextMapper} to use\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t *\n\t * @see PersonContextMapper\n\t * @see InetOrgPersonContextMapper\n\t * @see LdapUserDetailsMapper\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> userDetailsContextMapper(\n\t\t\tUserDetailsContextMapper userDetailsContextMapper) {\n\t\tthis.userDetailsContextMapper = userDetailsContextMapper;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the attribute name which contains the role name. Default is \"cn\".\n\t * @param groupRoleAttribute the attribute name that maps a group to a role.\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> groupRoleAttribute(String groupRoleAttribute) {\n\t\tthis.groupRoleAttribute = groupRoleAttribute;\n\t\treturn this;\n\t}\n\n\t/**\n\t * The search base for group membership searches. Defaults to \"\".\n\t * @param groupSearchBase\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> groupSearchBase(String groupSearchBase) {\n\t\tthis.groupSearchBase = groupSearchBase;\n\t\treturn this;\n\t}\n\n\t/**\n\t * If set to true, a subtree scope search will be performed for group membership. If\n\t * false a single-level search is used.\n\t * @param groupSearchSubtree set to true to enable searching of the entire tree below\n\t * the <tt>groupSearchBase</tt>.\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> groupSearchSubtree(boolean groupSearchSubtree) {\n\t\tthis.groupSearchSubtree = groupSearchSubtree;\n\t\treturn this;\n\t}\n\n\t/**\n\t * The LDAP filter to search for groups. Defaults to \"(uniqueMember={0})\". The\n\t * substituted parameter is the DN of the user.\n\t * @param groupSearchFilter the LDAP filter to search for groups\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> groupSearchFilter(String groupSearchFilter) {\n\t\tthis.groupSearchFilter = groupSearchFilter;\n\t\treturn this;\n\t}\n\n\t/**\n\t * A non-empty string prefix that will be added as a prefix to the existing roles. The\n\t * default is \"ROLE_\".\n\t * @param rolePrefix the prefix to be added to the roles that are loaded.\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t * @see SimpleAuthorityMapper#setPrefix(String)\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> rolePrefix(String rolePrefix) {\n\t\tthis.rolePrefix = rolePrefix;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Search base for user searches. Defaults to \"\". Only used with\n\t * {@link #userSearchFilter(String)}.\n\t * @param userSearchBase search base for user searches\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> userSearchBase(String userSearchBase) {\n\t\tthis.userSearchBase = userSearchBase;\n\t\treturn this;\n\t}\n\n\t/**\n\t * The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n\t * substituted parameter is the user's login name.\n\t * @param userSearchFilter the LDAP filter used to search for users\n\t * @return the {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t */\n\tpublic LdapAuthenticationProviderConfigurer<B> userSearchFilter(String userSearchFilter) {\n\t\tthis.userSearchFilter = userSearchFilter;\n\t\treturn this;\n\t}\n\n\tpublic B and() {\n\t\treturn getBuilder();\n\t}\n\n\t@Override\n\tpublic void configure(B builder) {\n\t\tLdapAuthenticationProvider provider = postProcess(build());\n\t\tbuilder.authenticationProvider(provider);\n\t}\n\n\tprivate BaseLdapPathContextSource getContextSource() {\n\t\tif (this.contextSource == null) {\n\t\t\tthis.contextSource = this.contextSourceBuilder.build();\n\t\t}\n\t\treturn this.contextSource;\n\t}\n\n\t/**\n\t * @return the {@link PasswordCompareConfigurer} for further customizations\n\t */\n\tpublic PasswordCompareConfigurer passwordCompare() {\n\t\treturn new PasswordCompareConfigurer().passwordAttribute(\"password\")\n\t\t\t.passwordEncoder(NoOpPasswordEncoder.getInstance());\n\t}\n\n\t/**\n\t * Sets up Password based comparison\n\t *\n\t * @author Rob Winch\n\t */\n\tpublic final class PasswordCompareConfigurer {\n\n\t\t/**\n\t\t * Allows specifying the {@link PasswordEncoder} to use. The default is\n\t\t * {@link org.springframework.security.crypto.password.NoOpPasswordEncoder}.\n\t\t * @param passwordEncoder the {@link PasswordEncoder} to use\n\t\t * @return the {@link PasswordCompareConfigurer} for further customizations\n\t\t */\n\t\tpublic PasswordCompareConfigurer passwordEncoder(PasswordEncoder passwordEncoder) {\n\t\t\tLdapAuthenticationProviderConfigurer.this.passwordEncoder = passwordEncoder;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The attribute in the directory which contains the user password. Defaults to\n\t\t * \"userPassword\".\n\t\t * @param passwordAttribute the attribute in the directory which contains the user\n\t\t * password\n\t\t * @return the {@link PasswordCompareConfigurer} for further customizations\n\t\t */\n\t\tpublic PasswordCompareConfigurer passwordAttribute(String passwordAttribute) {\n\t\t\tLdapAuthenticationProviderConfigurer.this.passwordAttribute = passwordAttribute;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows obtaining a reference to the\n\t\t * {@link LdapAuthenticationProviderConfigurer} for further customizations\n\t\t * @return attribute in the directory which contains the user password\n\t\t */\n\t\tpublic LdapAuthenticationProviderConfigurer<B> and() {\n\t\t\treturn LdapAuthenticationProviderConfigurer.this;\n\t\t}\n\n\t\tprivate PasswordCompareConfigurer() {\n\t\t}\n\n\t}\n\n\t/**\n\t * Allows building a {@link BaseLdapPathContextSource} and optionally creating an\n\t * embedded LDAP instance.\n\t *\n\t * @author Rob Winch\n\t * @author Evgeniy Cheban\n\t * @since 3.2\n\t */\n\tpublic final class ContextSourceBuilder {\n\n\t\tprivate static final String UNBOUNDID_CLASSNAME = \"com.unboundid.ldap.listener.InMemoryDirectoryServer\";\n\n\t\tprivate static final int DEFAULT_PORT = 33389;\n\n\t\tprivate static final int RANDOM_PORT = 0;\n\n\t\tprivate String ldif = \"classpath*:*.ldif\";\n\n\t\tprivate String managerPassword;\n\n\t\tprivate String managerDn;\n\n\t\tprivate Integer port;\n\n\t\tprivate String root = \"dc=springframework,dc=org\";\n\n\t\tprivate String url;\n\n\t\t/**\n\t\t * Specifies an ldif to load at startup for an embedded LDAP server. This only\n\t\t * loads if using an embedded instance. The default is \"classpath*:*.ldif\".\n\t\t * @param ldif the ldif to load at startup for an embedded LDAP server.\n\t\t * @return the {@link ContextSourceBuilder} for further customization\n\t\t */\n\t\tpublic ContextSourceBuilder ldif(String ldif) {\n\t\t\tthis.ldif = ldif;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Username (DN) of the \"manager\" user identity (i.e. \"uid=admin,ou=system\") which\n\t\t * will be used to authenticate to a (non-embedded) LDAP server. If omitted,\n\t\t * anonymous access will be used.\n\t\t * @param managerDn the username (DN) of the \"manager\" user identity used to\n\t\t * authenticate to a LDAP server.\n\t\t * @return the {@link ContextSourceBuilder} for further customization\n\t\t */\n\t\tpublic ContextSourceBuilder managerDn(String managerDn) {\n\t\t\tthis.managerDn = managerDn;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The password for the manager DN. This is required if the manager-dn is\n\t\t * specified.\n\t\t * @param managerPassword password for the manager DN\n\t\t * @return the {@link ContextSourceBuilder} for further customization\n\t\t */\n\t\tpublic ContextSourceBuilder managerPassword(String managerPassword) {\n\t\t\tthis.managerPassword = managerPassword;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The port to connect to LDAP to (the default is 33389 or random available port\n\t\t * if unavailable).\n\t\t *\n\t\t * Supplying 0 as the port indicates that a random available port should be\n\t\t * selected.\n\t\t * @param port the port to connect to\n\t\t * @return the {@link ContextSourceBuilder} for further customization\n\t\t */\n\t\tpublic ContextSourceBuilder port(int port) {\n\t\t\tthis.port = port;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Optional root suffix for the embedded LDAP server. Default is\n\t\t * \"dc=springframework,dc=org\"\n\t\t * @param root root suffix for the embedded LDAP server\n\t\t * @return the {@link ContextSourceBuilder} for further customization\n\t\t */\n\t\tpublic ContextSourceBuilder root(String root) {\n\t\t\tthis.root = root;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the ldap server URL when not using the embedded LDAP server. For\n\t\t * example, \"ldaps://ldap.example.com:33389/dc=myco,dc=org\".\n\t\t * @param url the ldap server URL\n\t\t * @return the {@link ContextSourceBuilder} for further customization\n\t\t */\n\t\tpublic ContextSourceBuilder url(String url) {\n\t\t\tthis.url = url;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Gets the {@link LdapAuthenticationProviderConfigurer} for further\n\t\t * customizations\n\t\t * @return the {@link LdapAuthenticationProviderConfigurer} for further\n\t\t * customizations\n\t\t */\n\t\tpublic LdapAuthenticationProviderConfigurer<B> and() {\n\t\t\treturn LdapAuthenticationProviderConfigurer.this;\n\t\t}\n\n\t\tprivate DefaultSpringSecurityContextSource build() {\n\t\t\tif (this.url == null) {\n\t\t\t\tstartEmbeddedLdapServer();\n\t\t\t}\n\t\t\tDefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(getProviderUrl());\n\t\t\tif (this.managerDn != null) {\n\t\t\t\tcontextSource.setUserDn(this.managerDn);\n\t\t\t\tif (this.managerPassword == null) {\n\t\t\t\t\tthrow new IllegalStateException(\"managerPassword is required if managerDn is supplied\");\n\t\t\t\t}\n\t\t\t\tcontextSource.setPassword(this.managerPassword);\n\t\t\t}\n\t\t\tcontextSource = postProcess(contextSource);\n\t\t\treturn contextSource;\n\t\t}\n\n\t\tprivate void startEmbeddedLdapServer() {\n\t\t\tif (unboundIdPresent) {\n\t\t\t\tUnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);\n\t\t\t\tunboundIdContainer.setPort(getPort());\n\t\t\t\tpostProcess(unboundIdContainer);\n\t\t\t\tthis.port = unboundIdContainer.getPort();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new IllegalStateException(\"Embedded LDAP server is not provided\");\n\t\t\t}\n\t\t}\n\n\t\tprivate int getPort() {\n\t\t\tif (this.port == null) {\n\t\t\t\tthis.port = getDefaultPort();\n\t\t\t}\n\t\t\treturn this.port;\n\t\t}\n\n\t\tprivate int getDefaultPort() {\n\t\t\ttry (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {\n\t\t\t\treturn serverSocket.getLocalPort();\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\treturn RANDOM_PORT;\n\t\t\t}\n\t\t}\n\n\t\tprivate String getProviderUrl() {\n\t\t\tif (this.url == null) {\n\t\t\t\treturn \"ldap://127.0.0.1:\" + getPort() + \"/\" + this.root;\n\t\t\t}\n\t\t\treturn this.url;\n\t\t}\n\n\t\tprivate ContextSourceBuilder() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/provisioning/InMemoryUserDetailsManagerConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.provisioning;\n\nimport java.util.ArrayList;\n\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\n\n/**\n * Configures an\n * {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}\n * to have in memory authentication. It also allows easily adding users to the in memory\n * authentication.\n *\n * @param <B> the type of the {@link ProviderManagerBuilder} that is being configured\n * @author Rob Winch\n * @since 3.2\n */\npublic class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>\n\t\textends UserDetailsManagerConfigurer<B, InMemoryUserDetailsManagerConfigurer<B>> {\n\n\t/**\n\t * Creates a new instance\n\t */\n\tpublic InMemoryUserDetailsManagerConfigurer() {\n\t\tsuper(new InMemoryUserDetailsManager(new ArrayList<>()));\n\t}\n\n\tpublic B and() {\n\t\treturn getBuilder();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/provisioning/JdbcUserDetailsManagerConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.provisioning;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.jdbc.datasource.init.DataSourceInitializer;\nimport org.springframework.jdbc.datasource.init.DatabasePopulator;\nimport org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.provisioning.JdbcUserDetailsManager;\n\n/**\n * Configures an\n * {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}\n * to have JDBC authentication. It also allows easily adding users to the database used\n * for authentication and setting up the schema.\n *\n * <p>\n * The only required method is the {@link #dataSource(javax.sql.DataSource)} all other\n * methods have reasonable defaults.\n *\n * @param <B> the type of the {@link ProviderManagerBuilder} that is being configured\n * @author Rob Winch\n * @since 3.2\n */\npublic class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>\n\t\textends UserDetailsManagerConfigurer<B, JdbcUserDetailsManagerConfigurer<B>> {\n\n\tprivate DataSource dataSource;\n\n\tprivate List<Resource> initScripts = new ArrayList<>();\n\n\tpublic JdbcUserDetailsManagerConfigurer(JdbcUserDetailsManager manager) {\n\t\tsuper(manager);\n\t}\n\n\tpublic JdbcUserDetailsManagerConfigurer() {\n\t\tthis(new JdbcUserDetailsManager());\n\t}\n\n\t/**\n\t * Populates the {@link DataSource} to be used. This is the only required attribute.\n\t * @param dataSource the {@link DataSource} to be used. Cannot be null.\n\t * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional\n\t * customizations\n\t */\n\tpublic JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource) {\n\t\tthis.dataSource = dataSource;\n\t\tgetUserDetailsService().setDataSource(dataSource);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the query to be used for finding a user by their username. For example:\n\t *\n\t * <code>\n\t *     select username,password,enabled from users where username = ?\n\t * </code>\n\t * @param query The query to use for selecting the username, password, and if the user\n\t * is enabled by username. Must contain a single parameter for the username.\n\t * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional\n\t * customizations\n\t */\n\tpublic JdbcUserDetailsManagerConfigurer<B> usersByUsernameQuery(String query) {\n\t\tgetUserDetailsService().setUsersByUsernameQuery(query);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the query to be used for finding a user's authorities by their username. For\n\t * example:\n\t *\n\t * <code>\n\t *     select username,authority from authorities where username = ?\n\t * </code>\n\t * @param query The query to use for selecting the username, authority by username.\n\t * Must contain a single parameter for the username.\n\t * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional\n\t * customizations\n\t */\n\tpublic JdbcUserDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query) {\n\t\tgetUserDetailsService().setAuthoritiesByUsernameQuery(query);\n\t\treturn this;\n\t}\n\n\t/**\n\t * An SQL statement to query user's group authorities given a username. For example:\n\t *\n\t * <code>\n\t *     select\n\t *         g.id, g.group_name, ga.authority\n\t *     from\n\t *         groups g, group_members gm, group_authorities ga\n\t *     where\n\t *         gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\n\t * </code>\n\t * @param query The query to use for selecting the authorities by group. Must contain\n\t * a single parameter for the username.\n\t * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional\n\t * customizations\n\t */\n\tpublic JdbcUserDetailsManagerConfigurer<B> groupAuthoritiesByUsername(String query) {\n\t\tJdbcUserDetailsManager userDetailsService = getUserDetailsService();\n\t\tuserDetailsService.setEnableGroups(true);\n\t\tuserDetailsService.setGroupAuthoritiesByUsernameQuery(query);\n\t\treturn this;\n\t}\n\n\t/**\n\t * A non-empty string prefix that will be added to role strings loaded from persistent\n\t * storage (default is \"\").\n\t * @param rolePrefix\n\t * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional\n\t * customizations\n\t */\n\tpublic JdbcUserDetailsManagerConfigurer<B> rolePrefix(String rolePrefix) {\n\t\tgetUserDetailsService().setRolePrefix(rolePrefix);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Defines the {@link UserCache} to use\n\t * @param userCache the {@link UserCache} to use\n\t * @return the {@link JdbcUserDetailsManagerConfigurer} for further customizations\n\t */\n\tpublic JdbcUserDetailsManagerConfigurer<B> userCache(UserCache userCache) {\n\t\tgetUserDetailsService().setUserCache(userCache);\n\t\treturn this;\n\t}\n\n\t@Override\n\tprotected void initUserDetailsService() {\n\t\tif (!this.initScripts.isEmpty()) {\n\t\t\tgetDataSourceInit().afterPropertiesSet();\n\t\t}\n\t\tsuper.initUserDetailsService();\n\t}\n\n\t@Override\n\tpublic JdbcUserDetailsManager getUserDetailsService() {\n\t\treturn (JdbcUserDetailsManager) super.getUserDetailsService();\n\t}\n\n\t/**\n\t * Populates the default schema that allows users and authorities to be stored.\n\t * @return The {@link JdbcUserDetailsManagerConfigurer} used for additional\n\t * customizations\n\t */\n\tpublic JdbcUserDetailsManagerConfigurer<B> withDefaultSchema() {\n\t\tthis.initScripts.add(new ClassPathResource(\"org/springframework/security/core/userdetails/jdbc/users.ddl\"));\n\t\treturn this;\n\t}\n\n\tprotected DatabasePopulator getDatabasePopulator() {\n\t\tResourceDatabasePopulator dbp = new ResourceDatabasePopulator();\n\t\tdbp.setScripts(this.initScripts.toArray(new Resource[0]));\n\t\treturn dbp;\n\t}\n\n\tprivate DataSourceInitializer getDataSourceInit() {\n\t\tDataSourceInitializer dsi = new DataSourceInitializer();\n\t\tdsi.setDatabasePopulator(getDatabasePopulator());\n\t\tdsi.setDataSource(this.dataSource);\n\t\treturn dsi;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/provisioning/UserDetailsManagerConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.provisioning;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.security.config.annotation.SecurityBuilder;\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsServiceConfigurer;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.User.UserBuilder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.provisioning.UserDetailsManager;\n\n/**\n * Base class for populating an\n * {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}\n * with a {@link UserDetailsManager}.\n *\n * @param <B> the type of the {@link SecurityBuilder} that is being configured\n * @param <C> the type of {@link UserDetailsManagerConfigurer}\n * @author Rob Winch\n * @since 3.2\n */\npublic class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsManagerConfigurer<B, C>>\n\t\textends UserDetailsServiceConfigurer<B, C, UserDetailsManager> {\n\n\tprivate final List<UserDetailsBuilder> userBuilders = new ArrayList<>();\n\n\tprivate final List<UserDetails> users = new ArrayList<>();\n\n\tprotected UserDetailsManagerConfigurer(UserDetailsManager userDetailsManager) {\n\t\tsuper(userDetailsManager);\n\t}\n\n\t/**\n\t * Populates the users that have been added.\n\t * @throws Exception\n\t */\n\t@Override\n\tprotected void initUserDetailsService() {\n\t\tfor (UserDetailsBuilder userBuilder : this.userBuilders) {\n\t\t\tgetUserDetailsService().createUser(userBuilder.build());\n\t\t}\n\t\tfor (UserDetails userDetails : this.users) {\n\t\t\tgetUserDetailsService().createUser(userDetails);\n\t\t}\n\t}\n\n\t/**\n\t * Allows adding a user to the {@link UserDetailsManager} that is being created. This\n\t * method can be invoked multiple times to add multiple users.\n\t * @param userDetails the user to add. Cannot be null.\n\t * @return the {@link UserDetailsBuilder} for further customizations\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic final C withUser(UserDetails userDetails) {\n\t\tthis.users.add(userDetails);\n\t\treturn (C) this;\n\t}\n\n\t/**\n\t * Allows adding a user to the {@link UserDetailsManager} that is being created. This\n\t * method can be invoked multiple times to add multiple users.\n\t * @param userBuilder the user to add. Cannot be null.\n\t * @return the {@link UserDetailsBuilder} for further customizations\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic final C withUser(User.UserBuilder userBuilder) {\n\t\tthis.users.add(userBuilder.build());\n\t\treturn (C) this;\n\t}\n\n\t/**\n\t * Allows adding a user to the {@link UserDetailsManager} that is being created. This\n\t * method can be invoked multiple times to add multiple users.\n\t * @param username the username for the user being added. Cannot be null.\n\t * @return the {@link UserDetailsBuilder} for further customizations\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic final UserDetailsBuilder withUser(String username) {\n\t\tUserDetailsBuilder userBuilder = new UserDetailsBuilder((C) this);\n\t\tuserBuilder.username(username);\n\t\tthis.userBuilders.add(userBuilder);\n\t\treturn userBuilder;\n\t}\n\n\t/**\n\t * Builds the user to be added. At minimum the username, password, and authorities\n\t * should provided. The remaining attributes have reasonable defaults.\n\t */\n\tpublic final class UserDetailsBuilder {\n\n\t\tprivate UserBuilder user;\n\n\t\tprivate final C builder;\n\n\t\t/**\n\t\t * Creates a new instance\n\t\t * @param builder the builder to return\n\t\t */\n\t\tprivate UserDetailsBuilder(C builder) {\n\t\t\tthis.builder = builder;\n\t\t}\n\n\t\t/**\n\t\t * Returns the {@link UserDetailsManagerConfigurer} for method chaining (i.e. to\n\t\t * add another user)\n\t\t * @return the {@link UserDetailsManagerConfigurer} for method chaining\n\t\t */\n\t\tpublic C and() {\n\t\t\treturn this.builder;\n\t\t}\n\n\t\t/**\n\t\t * Populates the username. This attribute is required.\n\t\t * @param username the username. Cannot be null.\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tprivate UserDetailsBuilder username(String username) {\n\t\t\tthis.user = User.withUsername(username);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the password. This attribute is required.\n\t\t * @param password the password. Cannot be null.\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserDetailsBuilder password(String password) {\n\t\t\tthis.user.password(password);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the roles. This method is a shortcut for calling\n\t\t * {@link #authorities(String...)}, but automatically prefixes each entry with\n\t\t * \"ROLE_\". This means the following:\n\t\t *\n\t\t * <code>\n\t\t *     builder.roles(\"USER\",\"ADMIN\");\n\t\t * </code>\n\t\t *\n\t\t * is equivalent to\n\t\t *\n\t\t * <code>\n\t\t *     builder.authorities(\"ROLE_USER\",\"ROLE_ADMIN\");\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * This attribute is required, but can also be populated with\n\t\t * {@link #authorities(String...)}.\n\t\t * </p>\n\t\t * @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null,\n\t\t * contain null values or start with \"ROLE_\"\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserDetailsBuilder roles(String... roles) {\n\t\t\tthis.user.roles(roles);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the authorities. This attribute is required.\n\t\t * @param authorities the authorities for this user. Cannot be null, or contain\n\t\t * null values\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t * @see #roles(String...)\n\t\t */\n\t\tpublic UserDetailsBuilder authorities(GrantedAuthority... authorities) {\n\t\t\tthis.user.authorities(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the authorities. This attribute is required.\n\t\t * @param authorities the authorities for this user. Cannot be null, or contain\n\t\t * null values\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t * @see #roles(String...)\n\t\t */\n\t\tpublic UserDetailsBuilder authorities(List<? extends GrantedAuthority> authorities) {\n\t\t\tthis.user.authorities(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the authorities. This attribute is required.\n\t\t * @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN,\n\t\t * etc). Cannot be null, or contain null values\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t * @see #roles(String...)\n\t\t */\n\t\tpublic UserDetailsBuilder authorities(String... authorities) {\n\t\t\tthis.user.authorities(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Defines if the account is expired or not. Default is false.\n\t\t * @param accountExpired true if the account is expired, false otherwise\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserDetailsBuilder accountExpired(boolean accountExpired) {\n\t\t\tthis.user.accountExpired(accountExpired);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Defines if the account is locked or not. Default is false.\n\t\t * @param accountLocked true if the account is locked, false otherwise\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserDetailsBuilder accountLocked(boolean accountLocked) {\n\t\t\tthis.user.accountLocked(accountLocked);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Defines if the credentials are expired or not. Default is false.\n\t\t * @param credentialsExpired true if the credentials are expired, false otherwise\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserDetailsBuilder credentialsExpired(boolean credentialsExpired) {\n\t\t\tthis.user.credentialsExpired(credentialsExpired);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Defines if the account is disabled or not. Default is false.\n\t\t * @param disabled true if the account is disabled, false otherwise\n\t\t * @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserDetailsBuilder disabled(boolean disabled) {\n\t\t\tthis.user.disabled(disabled);\n\t\t\treturn this;\n\t\t}\n\n\t\tUserDetails build() {\n\t\t\treturn this.user.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/userdetails/AbstractDaoAuthenticationConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.userdetails;\n\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityBuilder;\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.core.userdetails.UserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n/**\n * Allows configuring a {@link DaoAuthenticationProvider}\n *\n * @param <B> the type of the {@link SecurityBuilder}\n * @param <C> the type of {@link AbstractDaoAuthenticationConfigurer} this is\n * @param <U> The type of {@link UserDetailsService} that is being used\n * @author Rob Winch\n * @since 3.2\n */\npublic abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>\n\t\textends UserDetailsAwareConfigurer<B, U> {\n\n\tprivate DaoAuthenticationProvider provider;\n\n\tprivate final U userDetailsService;\n\n\t/**\n\t * Creates a new instance\n\t * @param userDetailsService\n\t */\n\tAbstractDaoAuthenticationConfigurer(U userDetailsService) {\n\t\tthis.userDetailsService = userDetailsService;\n\t\tthis.provider = new DaoAuthenticationProvider(userDetailsService);\n\t\tif (userDetailsService instanceof UserDetailsPasswordService) {\n\t\t\tthis.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);\n\t\t}\n\t}\n\n\t/**\n\t * Adds an {@link ObjectPostProcessor} for this class.\n\t * @param objectPostProcessor\n\t * @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic C withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {\n\t\taddObjectPostProcessor(objectPostProcessor);\n\t\treturn (C) this;\n\t}\n\n\t/**\n\t * Allows specifying the {@link PasswordEncoder} to use with the\n\t * {@link DaoAuthenticationProvider}. The default is to use plain text.\n\t * @param passwordEncoder The {@link PasswordEncoder} to use.\n\t * @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic C passwordEncoder(PasswordEncoder passwordEncoder) {\n\t\tthis.provider.setPasswordEncoder(passwordEncoder);\n\t\treturn (C) this;\n\t}\n\n\tpublic C userDetailsPasswordManager(UserDetailsPasswordService passwordManager) {\n\t\tthis.provider.setUserDetailsPasswordService(passwordManager);\n\t\treturn (C) this;\n\t}\n\n\t@Override\n\tpublic void configure(B builder) {\n\t\tthis.provider = postProcess(this.provider);\n\t\tbuilder.authenticationProvider(this.provider);\n\t}\n\n\t/**\n\t * Gets the {@link UserDetailsService} that is used with the\n\t * {@link DaoAuthenticationProvider}\n\t * @return the {@link UserDetailsService} that is used with the\n\t * {@link DaoAuthenticationProvider}\n\t */\n\t@Override\n\tpublic U getUserDetailsService() {\n\t\treturn this.userDetailsService;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/userdetails/DaoAuthenticationConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.userdetails;\n\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\n/**\n * Allows configuring a {@link DaoAuthenticationProvider}\n *\n * @param <B> The type of {@link ProviderManagerBuilder} this is\n * @param <U> The type of {@link UserDetailsService} that is being used\n * @author Rob Winch\n * @since 3.2\n */\npublic class DaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>\n\t\textends AbstractDaoAuthenticationConfigurer<B, DaoAuthenticationConfigurer<B, U>, U> {\n\n\t/**\n\t * Creates a new instance\n\t * @param userDetailsService\n\t */\n\tpublic DaoAuthenticationConfigurer(U userDetailsService) {\n\t\tsuper(userDetailsService);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/userdetails/UserDetailsAwareConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.userdetails;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\n/**\n * Base class that allows access to the {@link UserDetailsService} for using as a default\n * value with {@link AuthenticationManagerBuilder}.\n *\n * @param <B> the type of the {@link ProviderManagerBuilder}\n * @param <U> the type of {@link UserDetailsService}\n * @author Rob Winch\n */\npublic abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>\n\t\textends SecurityConfigurerAdapter<AuthenticationManager, B> {\n\n\t/**\n\t * Gets the {@link UserDetailsService} or null if it is not available\n\t * @return the {@link UserDetailsService} or null if it is not available\n\t */\n\tpublic abstract U getUserDetailsService();\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/userdetails/UserDetailsServiceConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.userdetails;\n\nimport org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\n/**\n * Allows configuring a {@link UserDetailsService} within a\n * {@link AuthenticationManagerBuilder}.\n *\n * @param <B> the type of the {@link ProviderManagerBuilder}\n * @param <C> the {@link UserDetailsServiceConfigurer} (or this)\n * @param <U> the type of UserDetailsService being used to allow for returning the\n * concrete UserDetailsService.\n * @author Rob Winch\n * @since 3.2\n */\npublic class UserDetailsServiceConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsServiceConfigurer<B, C, U>, U extends UserDetailsService>\n\t\textends AbstractDaoAuthenticationConfigurer<B, C, U> {\n\n\t/**\n\t * Creates a new instance\n\t * @param userDetailsService the {@link UserDetailsService} that should be used\n\t */\n\tpublic UserDetailsServiceConfigurer(U userDetailsService) {\n\t\tsuper(userDetailsService);\n\t}\n\n\t@Override\n\tpublic void configure(B builder) {\n\t\tinitUserDetailsService();\n\t\tsuper.configure(builder);\n\t}\n\n\t/**\n\t * Allows subclasses to initialize the {@link UserDetailsService}. For example, it\n\t * might add users, initialize schema, etc.\n\t */\n\tprotected void initUserDetailsService() {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authorization/AuthorizationManagerFactoryConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ImportAware;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.authorization.AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.config.Customizer;\n\n/**\n * Uses {@link EnableMultiFactorAuthentication} to configure a\n * {@link DefaultAuthorizationManagerFactory}.\n *\n * @author Rob Winch\n * @since 7.0\n * @see EnableMultiFactorAuthentication\n */\nclass AuthorizationManagerFactoryConfiguration implements ImportAware {\n\n\tprivate String[] authorities;\n\n\t@Bean\n\tDefaultAuthorizationManagerFactory authorizationManagerFactory(\n\t\t\tList<Customizer<AdditionalRequiredFactorsBuilder<Object>>> additionalRequiredFactorsCustomizers) {\n\t\tAdditionalRequiredFactorsBuilder<Object> builder = AuthorizationManagerFactories.multiFactor()\n\t\t\t.requireFactors(this.authorities);\n\t\tfor (Customizer<AdditionalRequiredFactorsBuilder<Object>> customizer : additionalRequiredFactorsCustomizers) {\n\t\t\tcustomizer.customize(builder);\n\t\t}\n\t\treturn builder.build();\n\t}\n\n\t@Override\n\tpublic void setImportMetadata(AnnotationMetadata importMetadata) {\n\t\tMap<String, Object> multiFactorAuthenticationAttrs = importMetadata\n\t\t\t.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName());\n\n\t\tthis.authorities = (String[]) multiFactorAuthenticationAttrs.getOrDefault(\"authorities\", new String[0]);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authorization/EnableMfaFiltersConfiguration.java",
    "content": "/*\n * Copyright 2002-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.authentication.AuthenticationFilter;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\n\n@Configuration(proxyBeanMethods = false)\nclass EnableMfaFiltersConfiguration {\n\n\t@Bean\n\tBeanPostProcessor mfaBeanPostProcessor() {\n\t\treturn new EnableMfaFiltersPostProcessor();\n\t}\n\n\t/**\n\t * A {@link BeanPostProcessor} that enables MFA on authentication filters.\n\t *\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tprivate static class EnableMfaFiltersPostProcessor implements BeanPostProcessor {\n\n\t\t@Override\n\t\tpublic @Nullable Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tif (bean instanceof AbstractAuthenticationProcessingFilter filter) {\n\t\t\t\tfilter.setMfaEnabled(true);\n\t\t\t}\n\t\t\tif (bean instanceof AuthenticationFilter filter) {\n\t\t\t\tfilter.setMfaEnabled(true);\n\t\t\t}\n\t\t\tif (bean instanceof AbstractPreAuthenticatedProcessingFilter filter) {\n\t\t\t\tfilter.setMfaEnabled(true);\n\t\t\t}\n\t\t\tif (bean instanceof BasicAuthenticationFilter filter) {\n\t\t\t\tfilter.setMfaEnabled(true);\n\t\t\t}\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authorization/EnableMultiFactorAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\n\n/**\n * Enables Multi-Factor Authentication (MFA) support within Spring Security.\n *\n * When {@link #authorities()} is specified creates a\n * {@link DefaultAuthorizationManagerFactory} as a Bean with the {@link #authorities()}\n * specified as additional required authorities. When {@link #when()} is\n * {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, {@link #authorities()} must include\n * {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY};\n * otherwise an {@link IllegalArgumentException} is thrown during configuration\n * processing. When {@link #when()} is not specified (default is an empty array), no such\n * requirement applies. The configuration will be picked up by both\n * {@link org.springframework.security.config.annotation.web.configuration.EnableWebSecurity}\n * and\n * {@link org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity}.\n *\n * <pre>\n\n * &#64;Configuration\n * &#64;EnableMultiFactorAuthentication(authorities = { GrantedAuthorities.FACTOR_OTT, GrantedAuthorities.FACTOR_PASSWORD })\n * public class MyConfiguration {\n *     // ...\n * }\n * </pre>\n *\n * <p>\n * You can also publish one or more\n * {@code Customizer<AdditionalRequiredFactorsBuilder<Object>>} beans to further customize\n * the {@link DefaultAuthorizationManagerFactory}. For example, conditionally applying MFA\n * for specific users:\n *\n * <pre>\n * &#64;Bean\n * Customizer&lt;AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder&lt;Object&gt;&gt; additionalRequiredFactorsCustomizer() {\n *     return (builder) -&gt; builder.when((auth) -&gt; \"admin\".equals(auth.getName()));\n * }\n * </pre>\n *\n * NOTE: At this time reactive applications do not support MFA and thus are not impacted.\n * This will likely be enhanced in the future.\n *\n * @author Rob Winch\n * @since 7.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import(MultiFactorAuthenticationSelector.class)\npublic @interface EnableMultiFactorAuthentication {\n\n\t/**\n\t * The additional authorities that are required.\n\t * @return the additional authorities that are required (e.g. {\n\t * FactorGrantedAuthority.FACTOR_OTT, FactorGrantedAuthority.FACTOR_PASSWORD }). Can\n\t * be null or an empty array if no additional authorities are required (if\n\t * authorization rules are not globally requiring MFA).\n\t * @see org.springframework.security.core.authority.FactorGrantedAuthority\n\t */\n\tString[] authorities();\n\n\t/**\n\t * The conditions under which multi-factor authentication is required.\n\t * <p>\n\t * When multiple conditions are specified, they are applied as an AND (all conditions\n\t * must be met).\n\t * @return the conditions (default is an empty array, which requires MFA\n\t * unconditionally)\n\t * @since 7.1\n\t */\n\tMultiFactorCondition[] when() default {};\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authorization/MultiFactorAuthenticationSelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\n/**\n * Uses {@link EnableMultiFactorAuthentication} to configure a\n * {@link DefaultAuthorizationManagerFactory}.\n * <p>\n * When {@link EnableMultiFactorAuthentication#when()} includes\n * {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, validates that\n * {@link EnableMultiFactorAuthentication#authorities()} includes\n * {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY}\n * and throws an {@link IllegalArgumentException} if not.\n *\n * @author Rob Winch\n * @since 7.0\n * @see EnableMultiFactorAuthentication\n */\nclass MultiFactorAuthenticationSelector implements ImportSelector {\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata metadata) {\n\t\tMap<String, Object> multiFactorAuthenticationAttrs = metadata\n\t\t\t.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName());\n\t\tString[] authorities = (String[]) multiFactorAuthenticationAttrs.getOrDefault(\"authorities\", new String[0]);\n\t\tMultiFactorCondition[] when = (MultiFactorCondition[]) multiFactorAuthenticationAttrs.getOrDefault(\"when\",\n\t\t\t\tnew MultiFactorCondition[0]);\n\t\tboolean hasWebAuthn = Arrays.asList(when).contains(MultiFactorCondition.WEBAUTHN_REGISTERED);\n\t\tif (hasWebAuthn && !Arrays.asList(authorities).contains(FactorGrantedAuthority.WEBAUTHN_AUTHORITY)) {\n\t\t\tthrow new IllegalArgumentException(\"When when() includes \" + MultiFactorCondition.WEBAUTHN_REGISTERED\n\t\t\t\t\t+ \", authorities() must include \" + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t\t}\n\t\tList<String> imports = new ArrayList<>(3);\n\t\tif (authorities.length > 0) {\n\t\t\timports.add(AuthorizationManagerFactoryConfiguration.class.getName());\n\t\t\tif (hasWebAuthn) {\n\t\t\t\timports.add(WhenWebAuthnRegisteredMfaConfiguration.class.getName());\n\t\t\t}\n\t\t}\n\t\timports.add(EnableMfaFiltersConfiguration.class.getName());\n\t\treturn imports.toArray(new String[imports.size()]);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authorization/MultiFactorCondition.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\n/**\n * Condition under which multi-factor authentication is required.\n *\n * @author Rob Winch\n * @since 7.1\n * @see EnableMultiFactorAuthentication#when()\n */\npublic enum MultiFactorCondition {\n\n\t/**\n\t * Require multi-factor authentication only when the user has a WebAuthn credential\n\t * record registered.\n\t * <p>\n\t * When this condition is specified,\n\t * {@link EnableMultiFactorAuthentication#authorities()} must include\n\t * {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY}.\n\t * Failing to include it results in an {@link IllegalArgumentException} when the\n\t * configuration is processed.\n\t * <p>\n\t * Using this condition also requires both a\n\t * {@link org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository}\n\t * Bean and a\n\t * {@link org.springframework.security.web.webauthn.management.UserCredentialRepository}\n\t * Bean to be published.\n\t *\n\t * <pre>\n\t * &#64;Bean\n\t * public PublicKeyCredentialUserEntityRepository userEntityRepository() {\n\t *     return new InMemoryPublicKeyCredentialUserEntityRepository();\n\t * }\n\t *\n\t * &#64;Bean\n\t * public UserCredentialRepository userCredentialRepository() {\n\t *     return new InMemoryUserCredentialRepository();\n\t * }\n\t * </pre>\n\t */\n\tWEBAUTHN_REGISTERED\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/authorization/WhenWebAuthnRegisteredMfaConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport java.util.function.Predicate;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository;\n\n/**\n * Configuration that provides a\n * {@link Customizer}&lt;{@link AdditionalRequiredFactorsBuilder}&gt; for\n * {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, requiring multi-factor authentication\n * only when the user has a WebAuthn credential record.\n *\n * @author Rob Winch\n * @since 7.1\n * @see EnableMultiFactorAuthentication#when()\n * @see MultiFactorCondition#WEBAUTHN_REGISTERED\n */\n@Configuration(proxyBeanMethods = false)\nclass WhenWebAuthnRegisteredMfaConfiguration {\n\n\t@Bean\n\tCustomizer<AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer(\n\t\t\tPublicKeyCredentialUserEntityRepository userEntityRepository,\n\t\t\tUserCredentialRepository userCredentialRepository) {\n\t\treturn (builder) -> builder.withWhen((current) -> {\n\t\t\tPredicate<Authentication> webAuthnRegisteredPredicate = new WebAuthnRegisteredPredicate(\n\t\t\t\t\tuserEntityRepository, userCredentialRepository);\n\t\t\tif (current == null) {\n\t\t\t\treturn webAuthnRegisteredPredicate;\n\t\t\t}\n\t\t\treturn current.and(webAuthnRegisteredPredicate);\n\t\t});\n\t}\n\n\tprivate static final class WebAuthnRegisteredPredicate implements Predicate<Authentication> {\n\n\t\tprivate final PublicKeyCredentialUserEntityRepository userEntityRepository;\n\n\t\tprivate final UserCredentialRepository userCredentialRepository;\n\n\t\tprivate WebAuthnRegisteredPredicate(PublicKeyCredentialUserEntityRepository userEntityRepository,\n\t\t\t\tUserCredentialRepository userCredentialRepository) {\n\t\t\tthis.userEntityRepository = userEntityRepository;\n\t\t\tthis.userCredentialRepository = userCredentialRepository;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean test(Authentication authentication) {\n\t\t\tif (authentication == null || authentication.getName() == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tPublicKeyCredentialUserEntity userEntity = this.userEntityRepository\n\t\t\t\t.findByUsername(authentication.getName());\n\t\t\tif (userEntity == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn !this.userCredentialRepository.findByUserId(userEntity.getId()).isEmpty();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"WEBAUTHN_REGISTERED\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/configuration/AutowireBeanFactoryObjectPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.beans.factory.Aware;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.SmartInitializingSingleton;\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.springframework.core.NativeDetector;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.util.Assert;\n\n/**\n * Allows registering Objects to participate with an {@link AutowireCapableBeanFactory}'s\n * post processing of {@link Aware} methods, {@link InitializingBean#afterPropertiesSet()}\n * , and {@link DisposableBean#destroy()}.\n *\n * @author Rob Winch\n * @since 3.2\n */\nfinal class AutowireBeanFactoryObjectPostProcessor\n\t\timplements ObjectPostProcessor<Object>, DisposableBean, SmartInitializingSingleton {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final AutowireCapableBeanFactory autowireBeanFactory;\n\n\tprivate final List<DisposableBean> disposableBeans = new ArrayList<>();\n\n\tprivate final List<SmartInitializingSingleton> smartSingletons = new ArrayList<>();\n\n\tAutowireBeanFactoryObjectPostProcessor(AutowireCapableBeanFactory autowireBeanFactory) {\n\t\tAssert.notNull(autowireBeanFactory, \"autowireBeanFactory cannot be null\");\n\t\tthis.autowireBeanFactory = autowireBeanFactory;\n\t}\n\n\t@Override\n\tpublic <T> T postProcess(T object) {\n\t\tif (object == null) {\n\t\t\treturn null;\n\t\t}\n\t\tT result = null;\n\t\ttry {\n\t\t\tresult = initializeBeanIfNeeded(object);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tClass<?> type = object.getClass();\n\t\t\tthrow new RuntimeException(\"Could not postProcess \" + object + \" of type \" + type, ex);\n\t\t}\n\t\tthis.autowireBeanFactory.autowireBean(object);\n\t\tif (result instanceof DisposableBean) {\n\t\t\tthis.disposableBeans.add((DisposableBean) result);\n\t\t}\n\t\tif (result instanceof SmartInitializingSingleton) {\n\t\t\tthis.smartSingletons.add((SmartInitializingSingleton) result);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Invokes {@link AutowireCapableBeanFactory#initializeBean(Object, String)} only if\n\t * needed, i.e when the application is not a native image or the object is not a CGLIB\n\t * proxy.\n\t * @param object the object to initialize\n\t * @param <T> the type of the object\n\t * @return the initialized bean or an existing bean if the object is a CGLIB proxy and\n\t * the application is a native image\n\t * @see <a href=\n\t * \"https://github.com/spring-projects/spring-security/issues/14825\">Issue\n\t * gh-14825</a>\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T> T initializeBeanIfNeeded(T object) {\n\t\tif (!NativeDetector.inNativeImage() || !AopUtils.isCglibProxy(object)) {\n\t\t\treturn (T) this.autowireBeanFactory.initializeBean(object, object.toString());\n\t\t}\n\t\tObjectProvider<?> provider = this.autowireBeanFactory.getBeanProvider(object.getClass());\n\t\tObject bean = provider.getIfUnique();\n\t\tif (bean == null) {\n\t\t\tString msg = \"\"\"\n\t\t\t\t\tFailed to resolve an unique bean (single or primary) of type [%s] from the BeanFactory.\n\t\t\t\t\tBecause the object is a CGLIB Proxy, a raw bean cannot be initialized during runtime in a native image.\n\t\t\t\t\t\"\"\"\n\t\t\t\t.formatted(object.getClass());\n\t\t\tthrow new IllegalStateException(msg);\n\t\t}\n\t\treturn (T) bean;\n\t}\n\n\t@Override\n\tpublic void afterSingletonsInstantiated() {\n\t\tfor (SmartInitializingSingleton singleton : this.smartSingletons) {\n\t\t\tsingleton.afterSingletonsInstantiated();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tfor (DisposableBean disposable : this.disposableBeans) {\n\t\t\ttry {\n\t\t\t\tdisposable.destroy();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthis.logger.error(ex);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/configuration/ObjectPostProcessorConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.configuration;\n\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\n\n/**\n * Spring {@link Configuration} that exports the default {@link ObjectPostProcessor}. This\n * class is not intended to be imported manually rather it is imported automatically when\n * using {@link EnableWebSecurity} or {@link EnableGlobalMethodSecurity}.\n *\n * @author Rob Winch\n * @since 3.2\n * @see EnableWebSecurity\n * @see EnableGlobalMethodSecurity\n */\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\npublic class ObjectPostProcessorConfiguration {\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tpublic ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {\n\t\treturn new AutowireBeanFactoryObjectPostProcessor(beanFactory);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/AuthorizationProxyConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInterceptor;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.aot.hint.AuthorizeReturnObjectCoreHintsRegistrar;\nimport org.springframework.security.aot.hint.SecurityHintsRegistrar;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisor;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;\nimport org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;\nimport org.springframework.security.config.Customizer;\n\n@Configuration(proxyBeanMethods = false)\nfinal class AuthorizationProxyConfiguration implements AopInfrastructureBean {\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic AuthorizationAdvisorProxyFactory authorizationProxyFactory(\n\t\t\tObjectProvider<AuthorizationAdvisor> authorizationAdvisors, ObjectProvider<TargetVisitor> targetVisitors,\n\t\t\tObjectProvider<Customizer<AuthorizationAdvisorProxyFactory>> customizers) {\n\t\tList<AuthorizationAdvisor> advisors = new ArrayList<>();\n\t\tauthorizationAdvisors.forEach(advisors::add);\n\t\tList<TargetVisitor> visitors = new ArrayList<>();\n\t\ttargetVisitors.orderedStream().forEach(visitors::add);\n\t\tvisitors.add(TargetVisitor.defaults());\n\t\tAuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(advisors);\n\t\tfactory.setTargetVisitor(TargetVisitor.of(visitors.toArray(TargetVisitor[]::new)));\n\t\tcustomizers.forEach((c) -> c.customize(factory));\n\t\treturn factory;\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor authorizeReturnObjectMethodInterceptor() {\n\t\treturn new AuthorizeReturnObjectMethodInterceptor();\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic SecurityHintsRegistrar authorizeReturnObjectHintsRegistrar(AuthorizationProxyFactory proxyFactory) {\n\t\treturn new AuthorizeReturnObjectCoreHintsRegistrar(proxyFactory);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/AuthorizationProxyDataConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.List;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.core.Ordered;\nimport org.springframework.data.domain.PageImpl;\nimport org.springframework.data.domain.SliceImpl;\nimport org.springframework.data.geo.GeoPage;\nimport org.springframework.data.geo.GeoResult;\nimport org.springframework.data.geo.GeoResults;\nimport org.springframework.security.aot.hint.SecurityHintsRegistrar;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar;\n\n@Configuration(proxyBeanMethods = false)\nfinal class AuthorizationProxyDataConfiguration implements AopInfrastructureBean {\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic SecurityHintsRegistrar authorizeReturnObjectDataHintsRegistrar(AuthorizationProxyFactory proxyFactory) {\n\t\treturn new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tDataTargetVisitor dataTargetVisitor() {\n\t\treturn new DataTargetVisitor();\n\t}\n\n\tstatic final class DataTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {\n\n\t\tprivate static final int DEFAULT_ORDER = 200;\n\n\t\t@Override\n\t\tpublic Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {\n\t\t\tif (target instanceof GeoResults<?> geoResults) {\n\t\t\t\treturn new GeoResults<>(proxyFactory.proxy(geoResults.getContent()), geoResults.getAverageDistance());\n\t\t\t}\n\t\t\tif (target instanceof GeoResult<?> geoResult) {\n\t\t\t\treturn new GeoResult<>(proxyFactory.proxy(geoResult.getContent()), geoResult.getDistance());\n\t\t\t}\n\t\t\tif (target instanceof GeoPage<?> geoPage) {\n\t\t\t\tGeoResults<?> results = new GeoResults<>(proxyFactory.proxy(geoPage.getContent()),\n\t\t\t\t\t\tgeoPage.getAverageDistance());\n\t\t\t\treturn new GeoPage<>(results, geoPage.getPageable(), geoPage.getTotalElements());\n\t\t\t}\n\t\t\tif (target instanceof PageImpl<?> page) {\n\t\t\t\tList<?> content = proxyFactory.proxy(page.getContent());\n\t\t\t\treturn new PageImpl<>(content, page.getPageable(), page.getTotalElements());\n\t\t\t}\n\t\t\tif (target instanceof SliceImpl<?> slice) {\n\t\t\t\tList<?> content = proxyFactory.proxy(slice.getContent());\n\t\t\t\treturn new SliceImpl<>(content, slice.getPageable(), slice.hasNext());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getOrder() {\n\t\t\treturn DEFAULT_ORDER;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/AuthorizationProxyWebConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.core.Ordered;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.web.util.ThrowableAnalyzer;\nimport org.springframework.web.servlet.HandlerExceptionResolver;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.View;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\nimport org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;\n\n@Configuration\nclass AuthorizationProxyWebConfiguration implements WebMvcConfigurer {\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tAuthorizationAdvisorProxyFactory.TargetVisitor webTargetVisitor() {\n\t\treturn new WebTargetVisitor();\n\t}\n\n\t@Override\n\tpublic void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {\n\t\tfor (int i = 0; i < resolvers.size(); i++) {\n\t\t\tHandlerExceptionResolver resolver = resolvers.get(i);\n\t\t\tif (resolver instanceof DefaultHandlerExceptionResolver) {\n\t\t\t\tresolvers.add(i, new AccessDeniedExceptionResolver());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tresolvers.add(new AccessDeniedExceptionResolver());\n\t}\n\n\tstatic class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {\n\n\t\tprivate static final int DEFAULT_ORDER = 100;\n\n\t\t@Override\n\t\tpublic Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {\n\t\t\tif (target instanceof ResponseEntity<?> entity) {\n\t\t\t\treturn new ResponseEntity<>(proxyFactory.proxy(entity.getBody()), entity.getHeaders(),\n\t\t\t\t\t\tentity.getStatusCode());\n\t\t\t}\n\t\t\tif (target instanceof HttpEntity<?> entity) {\n\t\t\t\treturn new HttpEntity<>(proxyFactory.proxy(entity.getBody()), entity.getHeaders());\n\t\t\t}\n\t\t\tif (target instanceof ModelAndView mav) {\n\t\t\t\tView view = mav.getView();\n\t\t\t\tString viewName = mav.getViewName();\n\t\t\t\tMap<String, Object> model = proxyFactory.proxy(mav.getModel());\n\t\t\t\tModelAndView proxied = (view != null) ? new ModelAndView(view, model)\n\t\t\t\t\t\t: new ModelAndView(viewName, model);\n\t\t\t\tproxied.setStatus(mav.getStatus());\n\t\t\t\treturn proxied;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getOrder() {\n\t\t\treturn DEFAULT_ORDER;\n\t\t}\n\n\t}\n\n\tstatic class AccessDeniedExceptionResolver implements HandlerExceptionResolver {\n\n\t\tfinal ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();\n\n\t\t@Override\n\t\tpublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,\n\t\t\t\tException ex) {\n\t\t\tThrowable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);\n\t\t\tThrowable accessDeniedException = this.throwableAnalyzer\n\t\t\t\t.getFirstThrowableOfType(AccessDeniedException.class, causeChain);\n\t\t\tif (accessDeniedException != null) {\n\t\t\t\tthrow (AccessDeniedException) accessDeniedException;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/DeferringMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.lang.NonNull;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authorization.method.AuthorizationAdvisor;\nimport org.springframework.util.function.SingletonSupplier;\n\nfinal class DeferringMethodInterceptor<M extends AuthorizationAdvisor> implements AuthorizationAdvisor {\n\n\tprivate final Pointcut pointcut;\n\n\tprivate final Supplier<M> delegate;\n\n\tDeferringMethodInterceptor(Pointcut pointcut, Supplier<M> delegate) {\n\t\tthis.pointcut = pointcut;\n\t\tthis.delegate = SingletonSupplier.of(delegate);\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic Object invoke(@NonNull MethodInvocation invocation) throws Throwable {\n\t\treturn this.delegate.get().invoke(invocation);\n\t}\n\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.delegate.get().getOrder();\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/EnableGlobalMethodSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.Ordered;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;\n\n/**\n * <p>\n * Enables Spring Security global method security similar to the\n * &lt;global-method-security&gt; xml support.\n *\n * <p>\n * More advanced configurations may wish to extend\n * {@link GlobalMethodSecurityConfiguration} and override the protected methods to provide\n * custom implementations. Note that {@link EnableGlobalMethodSecurity} still must be\n * included on the class extending {@link GlobalMethodSecurityConfiguration} to determine\n * the settings.\n *\n * @author Rob Winch\n * @since 3.2\n * @deprecated Use {@link EnableMethodSecurity} instead\n */\n@Deprecated\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import({ GlobalMethodSecuritySelector.class })\n@EnableGlobalAuthentication\npublic @interface EnableGlobalMethodSecurity {\n\n\t/**\n\t * Determines if Spring Security's pre post annotations should be enabled. Default is\n\t * false.\n\t * @return true if pre post annotations should be enabled false otherwise.\n\t */\n\tboolean prePostEnabled() default false;\n\n\t/**\n\t * Determines if Spring Security's {@link Secured} annotations should be enabled.\n\t * @return true if {@link Secured} annotations should be enabled false otherwise.\n\t * Default is false.\n\t */\n\tboolean securedEnabled() default false;\n\n\t/**\n\t * Determines if JSR-250 annotations should be enabled. Default is false.\n\t * @return true if JSR-250 should be enabled false otherwise.\n\t */\n\tboolean jsr250Enabled() default false;\n\n\t/**\n\t * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as\n\t * opposed to standard Java interface-based proxies ({@code false}). The default is\n\t * {@code false}. <strong>Applicable only if {@link #mode()} is set to\n\t * {@link AdviceMode#PROXY}</strong>.\n\t *\n\t * <p>\n\t * Note that setting this attribute to {@code true} will affect <em>all</em>\n\t * Spring-managed beans requiring proxying, not just those marked with the Security\n\t * annotations. For example, other beans marked with Spring's {@code @Transactional}\n\t * annotation will be upgraded to subclass proxying at the same time. This approach\n\t * has no negative impact in practice unless one is explicitly expecting one type of\n\t * proxy vs another, e.g. in tests.\n\t * @return true if CGILIB proxies should be created instead of interface based\n\t * proxies, else false\n\t */\n\tboolean proxyTargetClass() default false;\n\n\t/**\n\t * Indicate how security advice should be applied. The default is\n\t * {@link AdviceMode#PROXY}.\n\t * @see AdviceMode\n\t * @return the {@link AdviceMode} to use\n\t */\n\tAdviceMode mode() default AdviceMode.PROXY;\n\n\t/**\n\t * Indicate the ordering of the execution of the security advisor when multiple\n\t * advices are applied at a specific joinpoint. The default is\n\t * {@link Ordered#LOWEST_PRECEDENCE}.\n\t * @return the order the security advisor should be applied\n\t */\n\tint order() default Ordered.LOWEST_PRECEDENCE;\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\n\n/**\n * Enables Spring Security Method Security.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import(MethodSecuritySelector.class)\npublic @interface EnableMethodSecurity {\n\n\t/**\n\t * Determines if Spring Security's {@link PreAuthorize}, {@link PostAuthorize},\n\t * {@link PreFilter}, and {@link PostFilter} annotations should be enabled. Default is\n\t * true.\n\t * @return true if pre/post annotations should be enabled false otherwise\n\t */\n\tboolean prePostEnabled() default true;\n\n\t/**\n\t * Determines if Spring Security's {@link Secured} annotation should be enabled.\n\t * Default is false.\n\t * @return true if {@link Secured} annotation should be enabled false otherwise\n\t */\n\tboolean securedEnabled() default false;\n\n\t/**\n\t * Determines if JSR-250 annotations should be enabled. Default is false.\n\t * @return true if JSR-250 should be enabled false otherwise\n\t */\n\tboolean jsr250Enabled() default false;\n\n\t/**\n\t * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to\n\t * standard Java interface-based proxies. The default is {@code false}. <strong>\n\t * Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.\n\t * <p>\n\t * Note that setting this attribute to {@code true} will affect <em>all</em>\n\t * Spring-managed beans requiring proxying, not just those marked with\n\t * {@code @Cacheable}. For example, other beans marked with Spring's\n\t * {@code @Transactional} annotation will be upgraded to subclass proxying at the same\n\t * time. This approach has no negative impact in practice unless one is explicitly\n\t * expecting one type of proxy vs another, e.g. in tests.\n\t * @return true if subclass-based (CGLIB) proxies are to be created\n\t */\n\tboolean proxyTargetClass() default false;\n\n\t/**\n\t * Indicate how security advice should be applied. The default is\n\t * {@link AdviceMode#PROXY}.\n\t * @see AdviceMode\n\t * @return the {@link AdviceMode} to use\n\t */\n\tAdviceMode mode() default AdviceMode.PROXY;\n\n\t/**\n\t * Indicate additional offset in the ordering of the execution of the security\n\t * interceptors when multiple advices are applied at a specific joinpoint. I.e.,\n\t * precedence of each security interceptor enabled by this annotation will be\n\t * calculated as sum of its default precedence and offset. The default is 0.\n\t * @return the offset in the order the security advisor should be applied\n\t * @since 6.3\n\t */\n\tint offset() default 0;\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/EnableReactiveMethodSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.Ordered;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import(ReactiveMethodSecuritySelector.class)\npublic @interface EnableReactiveMethodSecurity {\n\n\t/**\n\t * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to\n\t * standard Java interface-based proxies. The default is {@code false}. <strong>\n\t * Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.\n\t * <p>\n\t * Note that setting this attribute to {@code true} will affect <em>all</em>\n\t * Spring-managed beans requiring proxying, not just those marked with\n\t * {@code @Cacheable}. For example, other beans marked with Spring's\n\t * {@code @Transactional} annotation will be upgraded to subclass proxying at the same\n\t * time. This approach has no negative impact in practice unless one is explicitly\n\t * expecting one type of proxy vs another, e.g. in tests.\n\t */\n\tboolean proxyTargetClass() default false;\n\n\t/**\n\t * Indicate how security advice should be applied. The default is\n\t * {@link AdviceMode#PROXY}.\n\t * @see AdviceMode\n\t * @return the {@link AdviceMode} to use\n\t */\n\tAdviceMode mode() default AdviceMode.PROXY;\n\n\t/**\n\t * Indicate the ordering of the execution of the security advisor when multiple\n\t * advices are applied at a specific joinpoint. The default is\n\t * {@link Ordered#LOWEST_PRECEDENCE}.\n\t * @return the order the security advisor should be applied\n\t */\n\tint order() default Ordered.LOWEST_PRECEDENCE;\n\n\t/**\n\t * Indicate whether {@link ReactiveAuthorizationManager} based Method Security to be\n\t * used.\n\t * @since 5.8\n\t */\n\tboolean useAuthorizationManager() default true;\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityAspectJAutoProxyRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.type.AnnotationMetadata;\n\n/**\n * Registers an\n * {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator\n * AnnotationAwareAspectJAutoProxyCreator} against the current\n * {@link BeanDefinitionRegistry} as appropriate based on a given @\n * {@link EnableGlobalMethodSecurity} annotation.\n *\n * <p>\n * Note: This class is necessary because AspectJAutoProxyRegistrar only supports\n * EnableAspectJAutoProxy.\n * </p>\n *\n * @author Rob Winch\n * @since 3.2\n * @deprecated Use {@link EnableMethodSecurity} instead\n */\n@Deprecated\nclass GlobalMethodSecurityAspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {\n\n\t/**\n\t * Register, escalate, and configure the AspectJ auto proxy creator based on the value\n\t * of the @{@link EnableGlobalMethodSecurity#proxyTargetClass()} attribute on the\n\t * importing {@code @Configuration} class.\n\t */\n\t@Override\n\tpublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {\n\t\tBeanDefinition interceptor = registry.getBeanDefinition(\"methodSecurityInterceptor\");\n\t\tBeanDefinitionBuilder aspect = BeanDefinitionBuilder.rootBeanDefinition(\n\t\t\t\t\"org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect\");\n\t\taspect.setFactoryMethod(\"aspectOf\");\n\t\taspect.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\taspect.addPropertyValue(\"securityInterceptor\", interceptor);\n\t\tregistry.registerBeanDefinition(\"annotationSecurityAspect$0\", aspect.getBeanDefinition());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.SmartInitializingSingleton;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.ImportAware;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.core.annotation.AnnotationAttributes;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.AfterInvocationProvider;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;\nimport org.springframework.security.access.annotation.Jsr250Voter;\nimport org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;\nimport org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;\nimport org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.intercept.AfterInvocationManager;\nimport org.springframework.security.access.intercept.AfterInvocationProviderManager;\nimport org.springframework.security.access.intercept.RunAsManager;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;\nimport org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;\nimport org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\nimport org.springframework.security.access.prepost.PostInvocationAdviceProvider;\nimport org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;\nimport org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;\nimport org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;\nimport org.springframework.security.access.vote.AffirmativeBased;\nimport org.springframework.security.access.vote.AuthenticatedVoter;\nimport org.springframework.security.access.vote.RoleVoter;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * Base {@link Configuration} for enabling global method security. Classes may extend this\n * class to customize the defaults, but must be sure to specify the\n * {@link EnableGlobalMethodSecurity} annotation on the subclass.\n *\n * @author Rob Winch\n * @author Eddú Meléndez\n * @author Ngoc Nhan\n * @since 3.2\n * @see EnableGlobalMethodSecurity\n * @deprecated Use {@link PrePostMethodSecurityConfiguration},\n * {@link SecuredMethodSecurityConfiguration}, or\n * {@link Jsr250MethodSecurityConfiguration} instead\n */\n@Deprecated\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\npublic class GlobalMethodSecurityConfiguration implements ImportAware, SmartInitializingSingleton, BeanFactoryAware {\n\n\tprivate static final Log logger = LogFactory.getLog(GlobalMethodSecurityConfiguration.class);\n\n\tprivate ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<>() {\n\n\t\t@Override\n\t\tpublic <T> T postProcess(T object) {\n\t\t\tthrow new IllegalStateException(ObjectPostProcessor.class.getName()\n\t\t\t\t\t+ \" is a required bean. Ensure you have used @\" + EnableGlobalMethodSecurity.class.getName());\n\t\t}\n\n\t};\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate DefaultMethodSecurityExpressionHandler defaultMethodExpressionHandler = new DefaultMethodSecurityExpressionHandler();\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate AuthenticationManagerBuilder auth;\n\n\tprivate boolean disableAuthenticationRegistry;\n\n\tprivate AnnotationAttributes enableMethodSecurity;\n\n\tprivate BeanFactory context;\n\n\tprivate MethodSecurityExpressionHandler expressionHandler;\n\n\tprivate MethodSecurityInterceptor methodSecurityInterceptor;\n\n\t/**\n\t * Creates the default MethodInterceptor which is a MethodSecurityInterceptor using\n\t * the following methods to construct it.\n\t * <ul>\n\t * <li>{@link #accessDecisionManager()}</li>\n\t * <li>{@link #afterInvocationManager()}</li>\n\t * <li>{@link #authenticationManager()}</li>\n\t * <li>{@link #runAsManager()}</li>\n\t *\n\t * </ul>\n\t *\n\t * <p>\n\t * Subclasses can override this method to provide a different\n\t * {@link MethodInterceptor}.\n\t * </p>\n\t * @param methodSecurityMetadataSource the default\n\t * {@link MethodSecurityMetadataSource}.\n\t * @return the {@link MethodInterceptor}.\n\t */\n\t@Bean\n\tpublic MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {\n\t\tthis.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor()\n\t\t\t\t: new MethodSecurityInterceptor();\n\t\tthis.methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());\n\t\tthis.methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());\n\t\tthis.methodSecurityInterceptor.setSecurityMetadataSource(methodSecurityMetadataSource);\n\t\tthis.methodSecurityInterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tRunAsManager runAsManager = runAsManager();\n\t\tif (runAsManager != null) {\n\t\t\tthis.methodSecurityInterceptor.setRunAsManager(runAsManager);\n\t\t}\n\t\treturn this.methodSecurityInterceptor;\n\t}\n\n\t@Override\n\tpublic void afterSingletonsInstantiated() {\n\t\ttry {\n\t\t\tinitializeMethodSecurityInterceptor();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t\tPermissionEvaluator permissionEvaluator = getBeanOrNull(PermissionEvaluator.class);\n\t\tif (permissionEvaluator != null) {\n\t\t\tthis.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluator);\n\t\t}\n\t\tRoleHierarchy roleHierarchy = getBeanOrNull(RoleHierarchy.class);\n\t\tif (roleHierarchy != null) {\n\t\t\tthis.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);\n\t\t}\n\t\tAuthenticationTrustResolver trustResolver = getBeanOrNull(AuthenticationTrustResolver.class);\n\t\tif (trustResolver != null) {\n\t\t\tthis.defaultMethodExpressionHandler.setTrustResolver(trustResolver);\n\t\t}\n\t\tGrantedAuthorityDefaults grantedAuthorityDefaults = getBeanOrNull(GrantedAuthorityDefaults.class);\n\t\tif (grantedAuthorityDefaults != null) {\n\t\t\tthis.defaultMethodExpressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());\n\t\t}\n\n\t\tthis.defaultMethodExpressionHandler = this.objectPostProcessor.postProcess(this.defaultMethodExpressionHandler);\n\t}\n\n\tprivate <T> T getBeanOrNull(Class<T> type) {\n\t\treturn this.context.getBeanProvider(type).getIfUnique();\n\t}\n\n\tprivate void initializeMethodSecurityInterceptor() throws Exception {\n\t\tif (this.methodSecurityInterceptor == null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.methodSecurityInterceptor.setAuthenticationManager(authenticationManager());\n\t}\n\n\t/**\n\t * Provide a custom {@link AfterInvocationManager} for the default implementation of\n\t * {@link #methodSecurityInterceptor(MethodSecurityMetadataSource)}. The default is\n\t * null if pre post is not enabled. Otherwise, it returns a\n\t * {@link AfterInvocationProviderManager}.\n\t *\n\t * <p>\n\t * Subclasses should override this method to provide a custom\n\t * {@link AfterInvocationManager}\n\t * </p>\n\t * @return the {@link AfterInvocationManager} to use\n\t */\n\tprotected AfterInvocationManager afterInvocationManager() {\n\t\tif (prePostEnabled()) {\n\t\t\tAfterInvocationProviderManager invocationProviderManager = new AfterInvocationProviderManager();\n\t\t\tExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(\n\t\t\t\t\tgetExpressionHandler());\n\t\t\tPostInvocationAdviceProvider postInvocationAdviceProvider = new PostInvocationAdviceProvider(postAdvice);\n\t\t\tList<AfterInvocationProvider> afterInvocationProviders = new ArrayList<>();\n\t\t\tafterInvocationProviders.add(postInvocationAdviceProvider);\n\t\t\tinvocationProviderManager.setProviders(afterInvocationProviders);\n\t\t\treturn invocationProviderManager;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Provide a custom {@link RunAsManager} for the default implementation of\n\t * {@link #methodSecurityInterceptor(MethodSecurityMetadataSource)}. The default is\n\t * null.\n\t * @return the {@link RunAsManager} to use\n\t */\n\tprotected RunAsManager runAsManager() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Allows subclasses to provide a custom {@link AccessDecisionManager}. The default is\n\t * a {@link AffirmativeBased} with the following voters:\n\t *\n\t * <ul>\n\t * <li>{@link PreInvocationAuthorizationAdviceVoter}</li>\n\t * <li>{@link RoleVoter}</li>\n\t * <li>{@link AuthenticatedVoter}</li>\n\t * </ul>\n\t * @return the {@link AccessDecisionManager} to use\n\t */\n\tprotected AccessDecisionManager accessDecisionManager() {\n\t\tList<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();\n\t\tif (prePostEnabled()) {\n\t\t\tExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();\n\t\t\texpressionAdvice.setExpressionHandler(getExpressionHandler());\n\t\t\tdecisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));\n\t\t}\n\t\tif (jsr250Enabled()) {\n\t\t\tdecisionVoters.add(new Jsr250Voter());\n\t\t}\n\t\tRoleVoter roleVoter = new RoleVoter();\n\t\tGrantedAuthorityDefaults grantedAuthorityDefaults = getBeanOrNull(GrantedAuthorityDefaults.class);\n\t\tif (grantedAuthorityDefaults != null) {\n\t\t\troleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());\n\t\t}\n\t\tdecisionVoters.add(roleVoter);\n\t\tdecisionVoters.add(new AuthenticatedVoter());\n\t\treturn new AffirmativeBased(decisionVoters);\n\t}\n\n\t/**\n\t * Provide a {@link MethodSecurityExpressionHandler} that is registered with the\n\t * {@link ExpressionBasedPreInvocationAdvice}. The default is\n\t * {@link DefaultMethodSecurityExpressionHandler} which optionally will Autowire an\n\t * {@link AuthenticationTrustResolver}.\n\t *\n\t * <p>\n\t * Subclasses may override this method to provide a custom\n\t * {@link MethodSecurityExpressionHandler}\n\t * </p>\n\t * @return the {@link MethodSecurityExpressionHandler} to use\n\t */\n\tprotected MethodSecurityExpressionHandler createExpressionHandler() {\n\t\treturn this.defaultMethodExpressionHandler;\n\t}\n\n\t/**\n\t * Gets the {@link MethodSecurityExpressionHandler} or creates it using\n\t * {@link #expressionHandler}.\n\t * @return a non {@code null} {@link MethodSecurityExpressionHandler}\n\t */\n\tprotected final MethodSecurityExpressionHandler getExpressionHandler() {\n\t\tif (this.expressionHandler == null) {\n\t\t\tthis.expressionHandler = createExpressionHandler();\n\t\t}\n\t\treturn this.expressionHandler;\n\t}\n\n\t/**\n\t * Provides a custom {@link MethodSecurityMetadataSource} that is registered with the\n\t * {@link #methodSecurityMetadataSource()}. Default is null.\n\t * @return a custom {@link MethodSecurityMetadataSource} that is registered with the\n\t * {@link #methodSecurityMetadataSource()}\n\t */\n\tprotected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Allows providing a custom {@link AuthenticationManager}. The default is to use any\n\t * authentication mechanisms registered by\n\t * {@link #configure(AuthenticationManagerBuilder)}. If\n\t * {@link #configure(AuthenticationManagerBuilder)} was not overridden, then an\n\t * {@link AuthenticationManager} is attempted to be autowired by type.\n\t * @return the {@link AuthenticationManager} to use\n\t */\n\tprotected AuthenticationManager authenticationManager() throws Exception {\n\t\tif (this.authenticationManager == null) {\n\t\t\tDefaultAuthenticationEventPublisher eventPublisher = this.objectPostProcessor\n\t\t\t\t.postProcess(new DefaultAuthenticationEventPublisher());\n\t\t\tthis.auth = new AuthenticationManagerBuilder(this.objectPostProcessor);\n\t\t\tthis.auth.authenticationEventPublisher(eventPublisher);\n\t\t\tconfigure(this.auth);\n\t\t\tthis.authenticationManager = (this.disableAuthenticationRegistry)\n\t\t\t\t\t? getAuthenticationConfiguration().getAuthenticationManager() : this.auth.build();\n\t\t}\n\t\treturn this.authenticationManager;\n\t}\n\n\t/**\n\t * Sub classes can override this method to register different types of authentication.\n\t * If not overridden, {@link #configure(AuthenticationManagerBuilder)} will attempt to\n\t * autowire by type.\n\t * @param auth the {@link AuthenticationManagerBuilder} used to register different\n\t * authentication mechanisms for the global method security.\n\t * @throws Exception\n\t */\n\tprotected void configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\tthis.disableAuthenticationRegistry = true;\n\t}\n\n\t/**\n\t * Provides the default {@link MethodSecurityMetadataSource} that will be used. It\n\t * creates a {@link DelegatingMethodSecurityMetadataSource} based upon\n\t * {@link #customMethodSecurityMetadataSource()} and the attributes on\n\t * {@link EnableGlobalMethodSecurity}.\n\t * @return the {@link MethodSecurityMetadataSource}\n\t */\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tpublic MethodSecurityMetadataSource methodSecurityMetadataSource() {\n\t\tList<MethodSecurityMetadataSource> sources = new ArrayList<>();\n\t\tExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(\n\t\t\t\tgetExpressionHandler());\n\t\tMethodSecurityMetadataSource customMethodSecurityMetadataSource = customMethodSecurityMetadataSource();\n\t\tif (customMethodSecurityMetadataSource != null) {\n\t\t\tsources.add(customMethodSecurityMetadataSource);\n\t\t}\n\t\tboolean hasCustom = customMethodSecurityMetadataSource != null;\n\t\tboolean isPrePostEnabled = prePostEnabled();\n\t\tboolean isSecuredEnabled = securedEnabled();\n\t\tboolean isJsr250Enabled = jsr250Enabled();\n\t\tAssert.state(isPrePostEnabled || isSecuredEnabled || isJsr250Enabled || hasCustom,\n\t\t\t\t\"In the composition of all global method configuration, \"\n\t\t\t\t\t\t+ \"no annotation support was actually activated\");\n\t\tif (isPrePostEnabled) {\n\t\t\tsources.add(new PrePostAnnotationSecurityMetadataSource(attributeFactory));\n\t\t}\n\t\tif (isSecuredEnabled) {\n\t\t\tsources.add(new SecuredAnnotationSecurityMetadataSource());\n\t\t}\n\t\tif (isJsr250Enabled) {\n\t\t\tGrantedAuthorityDefaults grantedAuthorityDefaults = getBeanOrNull(GrantedAuthorityDefaults.class);\n\t\t\tJsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource = this.context\n\t\t\t\t.getBean(Jsr250MethodSecurityMetadataSource.class);\n\t\t\tif (grantedAuthorityDefaults != null) {\n\t\t\t\tjsr250MethodSecurityMetadataSource.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());\n\t\t\t}\n\t\t\tsources.add(jsr250MethodSecurityMetadataSource);\n\t\t}\n\t\treturn new DelegatingMethodSecurityMetadataSource(sources);\n\t}\n\n\t/**\n\t * Creates the {@link PreInvocationAuthorizationAdvice} to be used. The default is\n\t * {@link ExpressionBasedPreInvocationAdvice}.\n\t * @return the {@link PreInvocationAuthorizationAdvice}\n\t */\n\t@Bean\n\tpublic PreInvocationAuthorizationAdvice preInvocationAuthorizationAdvice() {\n\t\tExpressionBasedPreInvocationAdvice preInvocationAdvice = new ExpressionBasedPreInvocationAdvice();\n\t\tpreInvocationAdvice.setExpressionHandler(getExpressionHandler());\n\t\treturn preInvocationAdvice;\n\t}\n\n\t/**\n\t * Obtains the attributes from {@link EnableGlobalMethodSecurity} if this class was\n\t * imported using the {@link EnableGlobalMethodSecurity} annotation.\n\t */\n\t@Override\n\tpublic final void setImportMetadata(AnnotationMetadata importMetadata) {\n\t\tMap<String, Object> annotationAttributes = importMetadata\n\t\t\t.getAnnotationAttributes(EnableGlobalMethodSecurity.class.getName());\n\t\tthis.enableMethodSecurity = AnnotationAttributes.fromMap(annotationAttributes);\n\t}\n\n\t@Autowired(required = false)\n\tpublic void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tthis.objectPostProcessor = objectPostProcessor;\n\t}\n\n\t@Autowired(required = false)\n\tpublic void setMethodSecurityExpressionHandler(List<MethodSecurityExpressionHandler> handlers) {\n\t\tif (handlers.size() != 1) {\n\t\t\tlogger.debug(\"Not autowiring MethodSecurityExpressionHandler since size != 1. Got \" + handlers);\n\t\t\treturn;\n\t\t}\n\t\tthis.expressionHandler = handlers.get(0);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Override\n\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\tthis.context = beanFactory;\n\t}\n\n\tprivate AuthenticationConfiguration getAuthenticationConfiguration() {\n\t\treturn this.context.getBean(AuthenticationConfiguration.class);\n\t}\n\n\tprivate boolean prePostEnabled() {\n\t\treturn enableMethodSecurity().getBoolean(\"prePostEnabled\");\n\t}\n\n\tprivate boolean securedEnabled() {\n\t\treturn enableMethodSecurity().getBoolean(\"securedEnabled\");\n\t}\n\n\tprivate boolean jsr250Enabled() {\n\t\treturn enableMethodSecurity().getBoolean(\"jsr250Enabled\");\n\t}\n\n\tprivate boolean isAspectJ() {\n\t\treturn enableMethodSecurity().getEnum(\"mode\") == AdviceMode.ASPECTJ;\n\t}\n\n\tprivate AnnotationAttributes enableMethodSecurity() {\n\t\tif (this.enableMethodSecurity == null) {\n\t\t\t// if it is null look at this instance (i.e. a subclass was used)\n\t\t\tEnableGlobalMethodSecurity methodSecurityAnnotation = AnnotationUtils.findAnnotation(getClass(),\n\t\t\t\t\tEnableGlobalMethodSecurity.class);\n\t\t\tAssert.notNull(methodSecurityAnnotation, () -> EnableGlobalMethodSecurity.class.getName() + \" is required\");\n\t\t\tMap<String, Object> methodSecurityAttrs = AnnotationUtils.getAnnotationAttributes(methodSecurityAnnotation);\n\t\t\tthis.enableMethodSecurity = AnnotationAttributes.fromMap(methodSecurityAttrs);\n\t\t}\n\t\treturn this.enableMethodSecurity;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecuritySelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.AutoProxyRegistrar;\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.annotation.AnnotationAttributes;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Dynamically determines which imports to include using the\n * {@link EnableGlobalMethodSecurity} annotation.\n *\n * @author Rob Winch\n * @since 3.2\n * @deprecated Use {@link MethodSecuritySelector} instead\n */\n@Deprecated\nfinal class GlobalMethodSecuritySelector implements ImportSelector {\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\tClass<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class;\n\t\tMap<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(annoType.getName(),\n\t\t\t\tfalse);\n\t\tAnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationAttributes);\n\t\tAssert.notNull(attributes, () -> String.format(\"@%s is not present on importing class '%s' as expected\",\n\t\t\t\tannoType.getSimpleName(), importingClassMetadata.getClassName()));\n\t\t// TODO would be nice if could use BeanClassLoaderAware (does not work)\n\t\tClass<?> importingClass = ClassUtils.resolveClassName(importingClassMetadata.getClassName(),\n\t\t\t\tClassUtils.getDefaultClassLoader());\n\t\tboolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class\n\t\t\t.isAssignableFrom(importingClass);\n\t\tAdviceMode mode = attributes.getEnum(\"mode\");\n\t\tboolean isProxy = AdviceMode.PROXY == mode;\n\t\tString autoProxyClassName = isProxy ? AutoProxyRegistrar.class.getName()\n\t\t\t\t: GlobalMethodSecurityAspectJAutoProxyRegistrar.class.getName();\n\t\tboolean jsr250Enabled = attributes.getBoolean(\"jsr250Enabled\");\n\t\tList<String> classNames = new ArrayList<>(4);\n\t\tif (isProxy) {\n\t\t\tclassNames.add(MethodSecurityMetadataSourceAdvisorRegistrar.class.getName());\n\t\t}\n\t\tclassNames.add(autoProxyClassName);\n\t\tif (!skipMethodSecurityConfiguration) {\n\t\t\tclassNames.add(GlobalMethodSecurityConfiguration.class.getName());\n\t\t}\n\t\tif (jsr250Enabled) {\n\t\t\tclassNames.add(Jsr250MetadataSourceConfiguration.class.getName());\n\t\t}\n\t\treturn classNames.toArray(new String[0]);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MetadataSourceConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;\n\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n@Deprecated\nclass Jsr250MetadataSourceConfiguration {\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tJsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource() {\n\t\treturn new Jsr250MethodSecurityMetadataSource();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.ImportAware;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authorization.AuthoritiesAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\nimport org.springframework.security.authorization.method.Jsr250AuthorizationManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\n/**\n * {@link Configuration} for enabling JSR-250 Spring Security Method Security.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n * @see EnableMethodSecurity\n */\n@Configuration(value = \"_jsr250MethodSecurityConfiguration\", proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nfinal class Jsr250MethodSecurityConfiguration implements ImportAware, AopInfrastructureBean {\n\n\tprivate static final Pointcut pointcut = AuthorizationManagerBeforeMethodInterceptor.jsr250().getPointcut();\n\n\tprivate final Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();\n\n\tprivate final AuthorizationManagerBeforeMethodInterceptor methodInterceptor;\n\n\tJsr250MethodSecurityConfiguration(\n\t\t\tObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> postProcessors) {\n\t\tObjectPostProcessor<AuthorizationManager<MethodInvocation>> postProcessor = postProcessors\n\t\t\t.getIfUnique(ObjectPostProcessor::identity);\n\t\tAuthorizationManager<MethodInvocation> manager = postProcessor.postProcess(this.authorizationManager);\n\t\tthis.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.jsr250(manager);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor jsr250AuthorizationMethodInterceptor(\n\t\t\tObjectProvider<Jsr250MethodSecurityConfiguration> _jsr250MethodSecurityConfiguration) {\n\t\tSupplier<AuthorizationManagerBeforeMethodInterceptor> supplier = () -> {\n\t\t\tJsr250MethodSecurityConfiguration configuration = _jsr250MethodSecurityConfiguration.getObject();\n\t\t\treturn configuration.methodInterceptor;\n\t\t};\n\t\treturn new DeferringMethodInterceptor<>(pointcut, supplier);\n\t}\n\n\t@Override\n\tpublic void setImportMetadata(AnnotationMetadata importMetadata) {\n\t\tEnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize();\n\t\tthis.methodInterceptor.setOrder(this.methodInterceptor.getOrder() + annotation.offset());\n\t}\n\n\t@Autowired(required = false)\n\tvoid setGrantedAuthorityDefaults(GrantedAuthorityDefaults defaults) {\n\t\tthis.authorizationManager.setRolePrefix(defaults.getRolePrefix());\n\t}\n\n\t@Autowired(required = false)\n\tvoid setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tAuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();\n\t\tauthoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy);\n\t\tthis.authorizationManager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setEventPublisher(AuthorizationEventPublisher eventPublisher) {\n\t\tthis.methodInterceptor.setAuthorizationEventPublisher(eventPublisher);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodObservationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.ObservationAuthorizationManager;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\n\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nclass MethodObservationConfiguration {\n\n\tprivate static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()\n\t\t.shouldObserveRequests(true)\n\t\t.shouldObserveAuthentications(true)\n\t\t.shouldObserveAuthorizations(true)\n\t\t.build();\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<AuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic AuthorizationManager postProcess(AuthorizationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();\n\t\t\t\treturn active ? new ObservationAuthorizationManager<>(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic AuthorizationManager postProcess(AuthorizationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();\n\t\t\t\treturn active ? new ObservationAuthorizationManager<>(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityAdvisorRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.aop.Advisor;\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.PointcutAdvisor;\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.lang.NonNull;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authorization.method.AuthorizationAdvisor;\n\nclass MethodSecurityAdvisorRegistrar implements ImportBeanDefinitionRegistrar {\n\n\t@Override\n\tpublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {\n\t\tregisterAsAdvisor(\"preFilterAuthorization\", registry);\n\t\tregisterAsAdvisor(\"preAuthorizeAuthorization\", registry);\n\t\tregisterAsAdvisor(\"postFilterAuthorization\", registry);\n\t\tregisterAsAdvisor(\"postAuthorizeAuthorization\", registry);\n\t\tregisterAsAdvisor(\"securedAuthorization\", registry);\n\t\tregisterAsAdvisor(\"jsr250Authorization\", registry);\n\t\tregisterAsAdvisor(\"authorizeReturnObject\", registry);\n\t}\n\n\tprivate void registerAsAdvisor(String prefix, BeanDefinitionRegistry registry) {\n\t\tString advisorName = prefix + \"Advisor\";\n\t\tif (registry.containsBeanDefinition(advisorName)) {\n\t\t\treturn;\n\t\t}\n\t\tString interceptorName = prefix + \"MethodInterceptor\";\n\t\tif (!registry.containsBeanDefinition(interceptorName)) {\n\t\t\treturn;\n\t\t}\n\t\tBeanDefinition definition = registry.getBeanDefinition(interceptorName);\n\t\tif (!(definition instanceof RootBeanDefinition)) {\n\t\t\treturn;\n\t\t}\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(AdvisorWrapper.class);\n\t\tbuilder.setFactoryMethod(\"of\");\n\t\tbuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\tbuilder.addConstructorArgReference(interceptorName);\n\t\tRootBeanDefinition advisor = (RootBeanDefinition) builder.getBeanDefinition();\n\t\tadvisor.setTargetType(Advisor.class);\n\t\tregistry.registerBeanDefinition(advisorName, advisor);\n\t}\n\n\tpublic static final class AdvisorWrapper\n\t\t\timplements PointcutAdvisor, MethodInterceptor, Ordered, AopInfrastructureBean {\n\n\t\tprivate final AuthorizationAdvisor advisor;\n\n\t\tprivate AdvisorWrapper(AuthorizationAdvisor advisor) {\n\t\t\tthis.advisor = advisor;\n\t\t}\n\n\t\tpublic static AdvisorWrapper of(AuthorizationAdvisor advisor) {\n\t\t\treturn new AdvisorWrapper(advisor);\n\t\t}\n\n\t\t@Override\n\t\tpublic Advice getAdvice() {\n\t\t\treturn this.advisor.getAdvice();\n\t\t}\n\n\t\t@Override\n\t\tpublic Pointcut getPointcut() {\n\t\t\treturn this.advisor.getPointcut();\n\t\t}\n\n\t\t@Override\n\t\tpublic int getOrder() {\n\t\t\treturn this.advisor.getOrder();\n\t\t}\n\n\t\t@Nullable\n\t\t@Override\n\t\tpublic Object invoke(@NonNull MethodInvocation invocation) throws Throwable {\n\t\t\treturn this.advisor.invoke(invocation);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityAspectJAutoProxyRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.type.AnnotationMetadata;\n\n/**\n * Registers an\n * {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator\n * AnnotationAwareAspectJAutoProxyCreator} against the current\n * {@link BeanDefinitionRegistry} as appropriate based on a given @\n * {@link EnableMethodSecurity} annotation.\n *\n * <p>\n * Note: This class is necessary because AspectJAutoProxyRegistrar only supports\n * EnableAspectJAutoProxy.\n * </p>\n *\n * @author Josh Cummings\n * @since 5.8\n */\nclass MethodSecurityAspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {\n\n\t/**\n\t * Register, escalate, and configure the AspectJ auto proxy creator based on the value\n\t * of the @{@link EnableMethodSecurity#proxyTargetClass()} attribute on the importing\n\t * {@code @Configuration} class.\n\t */\n\t@Override\n\tpublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {\n\t\tregisterBeanDefinition(\"preFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.PreFilterAspect\", \"preFilterAspect$0\",\n\t\t\t\tregistry);\n\t\tregisterBeanDefinition(\"postFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.PostFilterAspect\", \"postFilterAspect$0\",\n\t\t\t\tregistry);\n\t\tregisterBeanDefinition(\"preAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.PreAuthorizeAspect\", \"preAuthorizeAspect$0\",\n\t\t\t\tregistry);\n\t\tregisterBeanDefinition(\"postAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.PostAuthorizeAspect\",\n\t\t\t\t\"postAuthorizeAspect$0\", registry);\n\t\tregisterBeanDefinition(\"securedAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.SecuredAspect\", \"securedAspect$0\", registry);\n\t}\n\n\tprivate void registerBeanDefinition(String beanName, String aspectClassName, String aspectBeanName,\n\t\t\tBeanDefinitionRegistry registry) {\n\t\tif (!registry.containsBeanDefinition(beanName)) {\n\t\t\treturn;\n\t\t}\n\t\tBeanDefinition interceptor = registry.getBeanDefinition(beanName);\n\t\tBeanDefinitionBuilder aspect = BeanDefinitionBuilder.rootBeanDefinition(aspectClassName);\n\t\taspect.setFactoryMethod(\"aspectOf\");\n\t\taspect.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\taspect.addPropertyValue(\"securityInterceptor\", interceptor);\n\t\tregistry.registerBeanDefinition(aspectBeanName, aspect.getBeanDefinition());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityMetadataSourceAdvisorRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;\nimport org.springframework.util.MultiValueMap;\n\n/**\n * Creates Spring Security's MethodSecurityMetadataSourceAdvisor only when using proxy\n * based method security (i.e. do not do it when using ASPECTJ). The conditional logic is\n * controlled through {@link GlobalMethodSecuritySelector}.\n *\n * @author Rob Winch\n * @since 4.0.2\n * @see GlobalMethodSecuritySelector\n * @deprecated Use {@link MethodSecuritySelector} instead\n */\n@Deprecated\nclass MethodSecurityMetadataSourceAdvisorRegistrar implements ImportBeanDefinitionRegistrar {\n\n\t/**\n\t * Register, escalate, and configure the AspectJ auto proxy creator based on the value\n\t * of the @{@link EnableGlobalMethodSecurity#proxyTargetClass()} attribute on the\n\t * importing {@code @Configuration} class.\n\t */\n\t@Override\n\tpublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {\n\t\tBeanDefinitionBuilder advisor = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(MethodSecurityMetadataSourceAdvisor.class);\n\t\tadvisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\tadvisor.addConstructorArgValue(\"methodSecurityInterceptor\");\n\t\tadvisor.addConstructorArgReference(\"methodSecurityMetadataSource\");\n\t\tadvisor.addConstructorArgValue(\"methodSecurityMetadataSource\");\n\t\tMultiValueMap<String, Object> attributes = importingClassMetadata\n\t\t\t.getAllAnnotationAttributes(EnableGlobalMethodSecurity.class.getName());\n\t\tInteger order = (Integer) attributes.getFirst(\"order\");\n\t\tif (order != null) {\n\t\t\tadvisor.addPropertyValue(\"order\", order);\n\t\t}\n\t\tregistry.registerBeanDefinition(\"metaDataSourceAdvisor\", advisor.getBeanDefinition());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.AdviceModeImportSelector;\nimport org.springframework.context.annotation.AutoProxyRegistrar;\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.lang.NonNull;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Dynamically determines which imports to include using the {@link EnableMethodSecurity}\n * annotation.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n */\nfinal class MethodSecuritySelector implements ImportSelector {\n\n\tprivate static final boolean isDataPresent = ClassUtils\n\t\t.isPresent(\"org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar\", null);\n\n\tprivate static final boolean isWebPresent = ClassUtils\n\t\t.isPresent(\"org.springframework.web.servlet.DispatcherServlet\", null)\n\t\t\t&& ClassUtils.isPresent(\"org.springframework.security.web.util.ThrowableAnalyzer\", null);\n\n\tprivate static final boolean isObservabilityPresent = ClassUtils\n\t\t.isPresent(\"io.micrometer.observation.ObservationRegistry\", null);\n\n\tprivate final ImportSelector autoProxy = new AutoProxyRegistrarSelector();\n\n\t@Override\n\tpublic String[] selectImports(@NonNull AnnotationMetadata importMetadata) {\n\t\tif (!importMetadata.hasAnnotation(EnableMethodSecurity.class.getName())\n\t\t\t\t&& !importMetadata.hasMetaAnnotation(EnableMethodSecurity.class.getName())) {\n\t\t\treturn new String[0];\n\t\t}\n\t\tEnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize();\n\t\tList<String> imports = new ArrayList<>(Arrays.asList(this.autoProxy.selectImports(importMetadata)));\n\t\tif (annotation.prePostEnabled()) {\n\t\t\timports.add(PrePostMethodSecurityConfiguration.class.getName());\n\t\t}\n\t\tif (annotation.securedEnabled()) {\n\t\t\timports.add(SecuredMethodSecurityConfiguration.class.getName());\n\t\t}\n\t\tif (annotation.jsr250Enabled()) {\n\t\t\timports.add(Jsr250MethodSecurityConfiguration.class.getName());\n\t\t}\n\t\timports.add(AuthorizationProxyConfiguration.class.getName());\n\t\tif (isDataPresent) {\n\t\t\timports.add(AuthorizationProxyDataConfiguration.class.getName());\n\t\t}\n\t\tif (isWebPresent) {\n\t\t\timports.add(AuthorizationProxyWebConfiguration.class.getName());\n\t\t}\n\t\tif (isObservabilityPresent) {\n\t\t\timports.add(MethodObservationConfiguration.class.getName());\n\t\t}\n\t\treturn imports.toArray(new String[0]);\n\t}\n\n\tprivate static final class AutoProxyRegistrarSelector extends AdviceModeImportSelector<EnableMethodSecurity> {\n\n\t\tprivate static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName(),\n\t\t\t\tMethodSecurityAdvisorRegistrar.class.getName() };\n\n\t\tprivate static final String[] ASPECTJ_IMPORTS = new String[] {\n\t\t\t\tMethodSecurityAspectJAutoProxyRegistrar.class.getName() };\n\n\t\t@Override\n\t\tprotected String[] selectImports(@NonNull AdviceMode adviceMode) {\n\t\t\tif (adviceMode == AdviceMode.PROXY) {\n\t\t\t\treturn IMPORTS;\n\t\t\t}\n\t\t\tif (adviceMode == AdviceMode.ASPECTJ) {\n\t\t\t\treturn ASPECTJ_IMPORTS;\n\t\t\t}\n\t\t\tthrow new IllegalStateException(\"AdviceMode '\" + adviceMode + \"' is not supported\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.ImportAware;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.aot.hint.PrePostAuthorizeHintsRegistrar;\nimport org.springframework.security.aot.hint.SecurityHintsRegistrar;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;\nimport org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;\nimport org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;\nimport org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\n/**\n * Base {@link Configuration} for enabling Spring Security Method Security.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n * @see EnableMethodSecurity\n */\n@Configuration(value = \"_prePostMethodSecurityConfiguration\", proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nfinal class PrePostMethodSecurityConfiguration implements ImportAware, ApplicationContextAware, AopInfrastructureBean {\n\n\tprivate static final Pointcut preFilterPointcut = new PreFilterAuthorizationMethodInterceptor().getPointcut();\n\n\tprivate static final Pointcut preAuthorizePointcut = AuthorizationManagerBeforeMethodInterceptor.preAuthorize()\n\t\t.getPointcut();\n\n\tprivate static final Pointcut postAuthorizePointcut = AuthorizationManagerAfterMethodInterceptor.postAuthorize()\n\t\t.getPointcut();\n\n\tprivate static final Pointcut postFilterPointcut = new PostFilterAuthorizationMethodInterceptor().getPointcut();\n\n\tprivate final PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();\n\n\tprivate final PostAuthorizeAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeAuthorizationManager();\n\n\tprivate final PreFilterAuthorizationMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();\n\n\tprivate final AuthorizationManagerBeforeMethodInterceptor preAuthorizeMethodInterceptor;\n\n\tprivate final AuthorizationManagerAfterMethodInterceptor postAuthorizeMethodInterceptor;\n\n\tprivate final PostFilterAuthorizationMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();\n\n\tprivate final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\n\tPrePostMethodSecurityConfiguration(\n\t\t\tObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> preAuthorizeProcessor,\n\t\t\tObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>>> postAuthorizeProcessor) {\n\t\tthis.preFilterMethodInterceptor.setExpressionHandler(this.expressionHandler);\n\t\tthis.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);\n\t\tthis.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);\n\t\tthis.postFilterMethodInterceptor.setExpressionHandler(this.expressionHandler);\n\t\tAuthorizationManager<MethodInvocation> preAuthorize = preAuthorizeProcessor\n\t\t\t.getIfUnique(ObjectPostProcessor::identity)\n\t\t\t.postProcess(this.preAuthorizeAuthorizationManager);\n\t\tthis.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor.preAuthorize(preAuthorize);\n\t\tAuthorizationManager<MethodInvocationResult> postAuthorize = postAuthorizeProcessor\n\t\t\t.getIfUnique(ObjectPostProcessor::identity)\n\t\t\t.postProcess(this.postAuthorizeAuthorizationManager);\n\t\tthis.postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor.postAuthorize(postAuthorize);\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\tthis.expressionHandler.setApplicationContext(context);\n\t\tthis.preAuthorizeAuthorizationManager.setApplicationContext(context);\n\t\tthis.postAuthorizeAuthorizationManager.setApplicationContext(context);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {\n\t\tthis.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());\n\t}\n\n\t@Autowired(required = false)\n\tvoid setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tthis.expressionHandler.setRoleHierarchy(roleHierarchy);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setAuthorizationManagerFactory(AuthorizationManagerFactory<MethodInvocation> authorizationManagerFactory) {\n\t\tthis.expressionHandler.setAuthorizationManagerFactory(authorizationManagerFactory);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);\n\t\tthis.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);\n\t\tthis.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);\n\t\tthis.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\tthis.preFilterMethodInterceptor.setExpressionHandler(expressionHandler);\n\t\tthis.preAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler);\n\t\tthis.postAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler);\n\t\tthis.postFilterMethodInterceptor.setExpressionHandler(expressionHandler);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.preFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tthis.preAuthorizeMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tthis.postAuthorizeMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tthis.postFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setAuthorizationEventPublisher(AuthorizationEventPublisher publisher) {\n\t\tthis.preAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher);\n\t\tthis.postAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor preFilterAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<PrePostMethodSecurityConfiguration> _prePostMethodSecurityConfiguration) {\n\t\treturn new DeferringMethodInterceptor<>(preFilterPointcut,\n\t\t\t\t() -> _prePostMethodSecurityConfiguration.getObject().preFilterMethodInterceptor);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<PrePostMethodSecurityConfiguration> _prePostMethodSecurityConfiguration) {\n\t\treturn new DeferringMethodInterceptor<>(preAuthorizePointcut,\n\t\t\t\t() -> _prePostMethodSecurityConfiguration.getObject().preAuthorizeMethodInterceptor);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<PrePostMethodSecurityConfiguration> _prePostMethodSecurityConfiguration) {\n\t\treturn new DeferringMethodInterceptor<>(postAuthorizePointcut,\n\t\t\t\t() -> _prePostMethodSecurityConfiguration.getObject().postAuthorizeMethodInterceptor);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor postFilterAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<PrePostMethodSecurityConfiguration> _prePostMethodSecurityConfiguration) {\n\t\treturn new DeferringMethodInterceptor<>(postFilterPointcut,\n\t\t\t\t() -> _prePostMethodSecurityConfiguration.getObject().postFilterMethodInterceptor);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic SecurityHintsRegistrar prePostAuthorizeExpressionHintsRegistrar() {\n\t\treturn new PrePostAuthorizeHintsRegistrar();\n\t}\n\n\t@Override\n\tpublic void setImportMetadata(AnnotationMetadata importMetadata) {\n\t\tEnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize();\n\t\tthis.preFilterMethodInterceptor.setOrder(this.preFilterMethodInterceptor.getOrder() + annotation.offset());\n\t\tthis.preAuthorizeMethodInterceptor\n\t\t\t.setOrder(this.preAuthorizeMethodInterceptor.getOrder() + annotation.offset());\n\t\tthis.postAuthorizeMethodInterceptor\n\t\t\t.setOrder(this.postAuthorizeMethodInterceptor.getOrder() + annotation.offset());\n\t\tthis.postFilterMethodInterceptor.setOrder(this.postFilterMethodInterceptor.getOrder() + annotation.offset());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveAuthorizationManagerMethodSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Fallback;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.authorization.method.PostAuthorizeReactiveAuthorizationManager;\nimport org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor;\nimport org.springframework.security.authorization.method.PreAuthorizeReactiveAuthorizationManager;\nimport org.springframework.security.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\n\n/**\n * Configuration for a {@link ReactiveAuthenticationManager} based Method Security.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\n@Configuration(value = \"_reactiveMethodSecurityConfiguration\", proxyBeanMethods = false)\nfinal class ReactiveAuthorizationManagerMethodSecurityConfiguration\n\t\timplements AopInfrastructureBean, ApplicationContextAware {\n\n\tprivate static final Pointcut preFilterPointcut = new PreFilterAuthorizationReactiveMethodInterceptor()\n\t\t.getPointcut();\n\n\tprivate static final Pointcut preAuthorizePointcut = AuthorizationManagerBeforeReactiveMethodInterceptor\n\t\t.preAuthorize()\n\t\t.getPointcut();\n\n\tprivate static final Pointcut postAuthorizePointcut = AuthorizationManagerAfterReactiveMethodInterceptor\n\t\t.postAuthorize()\n\t\t.getPointcut();\n\n\tprivate static final Pointcut postFilterPointcut = new PostFilterAuthorizationReactiveMethodInterceptor()\n\t\t.getPointcut();\n\n\tprivate PreFilterAuthorizationReactiveMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\n\tprivate PreAuthorizeReactiveAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager();\n\n\tprivate PostAuthorizeReactiveAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager();\n\n\tprivate PostFilterAuthorizationReactiveMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor();\n\n\tprivate final AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeMethodInterceptor;\n\n\tprivate final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;\n\n\tReactiveAuthorizationManagerMethodSecurityConfiguration(\n\t\t\tObjectProvider<MethodSecurityExpressionHandler> expressionHandlers,\n\t\t\tObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>>> preAuthorizePostProcessor,\n\t\t\tObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>>> postAuthorizePostProcessor) {\n\t\tMethodSecurityExpressionHandler expressionHandler = expressionHandlers.getIfUnique();\n\t\tif (expressionHandler != null) {\n\t\t\tthis.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);\n\t\t\tthis.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler);\n\t\t\tthis.postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler);\n\t\t\tthis.postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager(expressionHandler);\n\t\t}\n\t\tReactiveAuthorizationManager<MethodInvocation> preAuthorize = preAuthorizePostProcessor\n\t\t\t.getIfUnique(ObjectPostProcessor::identity)\n\t\t\t.postProcess(this.preAuthorizeAuthorizationManager);\n\t\tthis.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor\n\t\t\t.preAuthorize(preAuthorize);\n\t\tReactiveAuthorizationManager<MethodInvocationResult> postAuthorize = postAuthorizePostProcessor\n\t\t\t.getIfAvailable(ObjectPostProcessor::identity)\n\t\t\t.postProcess(this.postAuthorizeAuthorizationManager);\n\t\tthis.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor\n\t\t\t.postAuthorize(postAuthorize);\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\tthis.preAuthorizeAuthorizationManager.setApplicationContext(context);\n\t\tthis.postAuthorizeAuthorizationManager.setApplicationContext(context);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);\n\t\tthis.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);\n\t\tthis.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);\n\t\tthis.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor preFilterAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {\n\t\treturn new DeferringMethodInterceptor<>(preFilterPointcut,\n\t\t\t\t() -> _reactiveMethodSecurityConfiguration.getObject().preFilterMethodInterceptor);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {\n\t\treturn new DeferringMethodInterceptor<>(preAuthorizePointcut,\n\t\t\t\t() -> _reactiveMethodSecurityConfiguration.getObject().preAuthorizeMethodInterceptor);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor postFilterAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {\n\t\treturn new DeferringMethodInterceptor<>(postFilterPointcut,\n\t\t\t\t() -> _reactiveMethodSecurityConfiguration.getObject().postFilterMethodInterceptor);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {\n\t\treturn new DeferringMethodInterceptor<>(postAuthorizePointcut,\n\t\t\t\t() -> _reactiveMethodSecurityConfiguration.getObject().postAuthorizeMethodInterceptor);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t@Fallback\n\tstatic DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(\n\t\t\t@Autowired(required = false) GrantedAuthorityDefaults grantedAuthorityDefaults) {\n\t\tDefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();\n\t\tif (grantedAuthorityDefaults != null) {\n\t\t\thandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());\n\t\t}\n\t\treturn handler;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodObservationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.authorization.ObservationReactiveAuthorizationManager;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\n\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nclass ReactiveMethodObservationConfiguration {\n\n\tprivate static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()\n\t\t.shouldObserveRequests(true)\n\t\t.shouldObserveAuthentications(true)\n\t\t.shouldObserveAuthorizations(true)\n\t\t.build();\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();\n\t\t\t\treturn active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();\n\t\t\t\treturn active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.Arrays;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Fallback;\nimport org.springframework.context.annotation.ImportAware;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;\nimport org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;\nimport org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;\nimport org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;\nimport org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;\nimport org.springframework.security.access.prepost.PrePostAdviceReactiveMethodInterceptor;\nimport org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\n\n/**\n * @author Rob Winch\n * @author Tadaya Tsuyukubo\n * @since 5.0\n */\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nclass ReactiveMethodSecurityConfiguration implements ImportAware {\n\n\tprivate int advisorOrder;\n\n\tprivate GrantedAuthorityDefaults grantedAuthorityDefaults;\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodSecurityMetadataSourceAdvisor methodSecurityInterceptor(AbstractMethodSecurityMetadataSource source,\n\t\t\tReactiveMethodSecurityConfiguration configuration) {\n\t\tMethodSecurityMetadataSourceAdvisor advisor = new MethodSecurityMetadataSourceAdvisor(\n\t\t\t\t\"securityMethodInterceptor\", source, \"methodMetadataSource\");\n\t\tadvisor.setOrder(configuration.advisorOrder);\n\t\treturn advisor;\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic DelegatingMethodSecurityMetadataSource methodMetadataSource(\n\t\t\tMethodSecurityExpressionHandler methodSecurityExpressionHandler) {\n\t\tExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(\n\t\t\t\tmethodSecurityExpressionHandler);\n\t\tPrePostAnnotationSecurityMetadataSource prePostSource = new PrePostAnnotationSecurityMetadataSource(\n\t\t\t\tattributeFactory);\n\t\treturn new DelegatingMethodSecurityMetadataSource(Arrays.asList(prePostSource));\n\t}\n\n\t@Bean\n\tstatic PrePostAdviceReactiveMethodInterceptor securityMethodInterceptor(AbstractMethodSecurityMetadataSource source,\n\t\t\tMethodSecurityExpressionHandler handler) {\n\t\tExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(handler);\n\t\tExpressionBasedPreInvocationAdvice preAdvice = new ExpressionBasedPreInvocationAdvice();\n\t\tpreAdvice.setExpressionHandler(handler);\n\t\treturn new PrePostAdviceReactiveMethodInterceptor(source, preAdvice, postAdvice);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t@Fallback\n\tstatic DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(\n\t\t\tReactiveMethodSecurityConfiguration configuration) {\n\t\tDefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();\n\t\tif (configuration.grantedAuthorityDefaults != null) {\n\t\t\thandler.setDefaultRolePrefix(configuration.grantedAuthorityDefaults.getRolePrefix());\n\t\t}\n\t\treturn handler;\n\t}\n\n\t@Override\n\tpublic void setImportMetadata(AnnotationMetadata importMetadata) {\n\t\tthis.advisorOrder = (int) importMetadata.getAnnotationAttributes(EnableReactiveMethodSecurity.class.getName())\n\t\t\t.get(\"order\");\n\t}\n\n\t@Autowired(required = false)\n\tvoid setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {\n\t\tthis.grantedAuthorityDefaults = grantedAuthorityDefaults;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecuritySelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.AdviceModeImportSelector;\nimport org.springframework.context.annotation.AutoProxyRegistrar;\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.lang.NonNull;\nimport org.springframework.util.ClassUtils;\n\n/**\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 5.0\n */\nclass ReactiveMethodSecuritySelector implements ImportSelector {\n\n\tprivate static final boolean isDataPresent = ClassUtils\n\t\t.isPresent(\"org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar\", null);\n\n\tprivate static final boolean isObservabilityPresent = ClassUtils\n\t\t.isPresent(\"io.micrometer.observation.ObservationRegistry\", null);\n\n\tprivate final ImportSelector autoProxy = new AutoProxyRegistrarSelector();\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importMetadata) {\n\t\tif (!importMetadata.hasAnnotation(EnableReactiveMethodSecurity.class.getName())) {\n\t\t\treturn new String[0];\n\t\t}\n\t\tEnableReactiveMethodSecurity annotation = importMetadata.getAnnotations()\n\t\t\t.get(EnableReactiveMethodSecurity.class)\n\t\t\t.synthesize();\n\t\tList<String> imports = new ArrayList<>(Arrays.asList(this.autoProxy.selectImports(importMetadata)));\n\t\tif (annotation.useAuthorizationManager()) {\n\t\t\timports.add(ReactiveAuthorizationManagerMethodSecurityConfiguration.class.getName());\n\t\t}\n\t\telse {\n\t\t\timports.add(ReactiveMethodSecurityConfiguration.class.getName());\n\t\t}\n\t\tif (isDataPresent) {\n\t\t\timports.add(AuthorizationProxyDataConfiguration.class.getName());\n\t\t}\n\t\tif (isObservabilityPresent) {\n\t\t\timports.add(ReactiveMethodObservationConfiguration.class.getName());\n\t\t}\n\t\timports.add(AuthorizationProxyConfiguration.class.getName());\n\t\treturn imports.toArray(new String[0]);\n\t}\n\n\tprivate static final class AutoProxyRegistrarSelector\n\t\t\textends AdviceModeImportSelector<EnableReactiveMethodSecurity> {\n\n\t\tprivate static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName(),\n\t\t\t\tMethodSecurityAdvisorRegistrar.class.getName() };\n\n\t\t@Override\n\t\tprotected String[] selectImports(@NonNull AdviceMode adviceMode) {\n\t\t\tif (adviceMode == AdviceMode.PROXY) {\n\t\t\t\treturn IMPORTS;\n\t\t\t}\n\t\t\tthrow new IllegalStateException(\"AdviceMode \" + adviceMode + \" is not supported\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.ImportAware;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authorization.AuthoritiesAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\nimport org.springframework.security.authorization.method.SecuredAuthorizationManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\n/**\n * {@link Configuration} for enabling {@link Secured} Spring Security Method Security.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n * @see EnableMethodSecurity\n */\n@Configuration(value = \"_securedMethodSecurityConfiguration\", proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nfinal class SecuredMethodSecurityConfiguration implements ImportAware, AopInfrastructureBean {\n\n\tprivate static final Pointcut pointcut = AuthorizationManagerBeforeMethodInterceptor.secured().getPointcut();\n\n\tprivate final SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();\n\n\tprivate final AuthorizationManagerBeforeMethodInterceptor methodInterceptor;\n\n\tSecuredMethodSecurityConfiguration(\n\t\t\tObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> postProcessors) {\n\t\tObjectPostProcessor<AuthorizationManager<MethodInvocation>> postProcessor = postProcessors\n\t\t\t.getIfUnique(ObjectPostProcessor::identity);\n\t\tAuthorizationManager<MethodInvocation> manager = postProcessor.postProcess(this.authorizationManager);\n\t\tthis.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(manager);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic MethodInterceptor securedAuthorizationMethodInterceptor(\n\t\t\tObjectProvider<SecuredMethodSecurityConfiguration> securedMethodSecurityConfiguration) {\n\t\tSupplier<AuthorizationManagerBeforeMethodInterceptor> supplier = () -> {\n\t\t\tSecuredMethodSecurityConfiguration configuration = securedMethodSecurityConfiguration.getObject();\n\t\t\treturn configuration.methodInterceptor;\n\t\t};\n\t\treturn new DeferringMethodInterceptor<>(pointcut, supplier);\n\t}\n\n\t@Override\n\tpublic void setImportMetadata(AnnotationMetadata importMetadata) {\n\t\tEnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize();\n\t\tthis.methodInterceptor.setOrder(this.methodInterceptor.getOrder() + annotation.offset());\n\t}\n\n\t@Autowired(required = false)\n\tvoid setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tAuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();\n\t\tauthoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy);\n\t\tthis.authorizationManager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setEventPublisher(AuthorizationEventPublisher eventPublisher) {\n\t\tthis.methodInterceptor.setAuthorizationEventPublisher(eventPublisher);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/rsocket/EnableRSocketSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.Import;\n\n/**\n * Add this annotation to a {@code Configuration} class to have Spring Security\n * {@link RSocketSecurity} support added.\n *\n * @author Rob Winch\n * @since 5.2\n * @see RSocketSecurity\n */\n@Documented\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Import({ RSocketSecurityConfiguration.class, SecuritySocketAcceptorInterceptorConfiguration.class,\n\t\tReactiveObservationImportSelector.class })\npublic @interface EnableRSocketSecurity {\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/rsocket/PayloadInterceptorOrder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport org.springframework.core.Ordered;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\n\n/**\n * The standard order for {@link PayloadInterceptor} to be sorted. The actual values might\n * change, so users should use the {@link #getOrder()} method to calculate the position\n * dynamically rather than copy values.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic enum PayloadInterceptorOrder implements Ordered {\n\n\t/**\n\t * Where basic authentication is placed.\n\t * @see RSocketSecurity#basicAuthentication(Customizer)\n\t * @deprecated please see {@link PayloadInterceptorOrder#AUTHENTICATION}\n\t */\n\t@Deprecated\n\tBASIC_AUTHENTICATION,\n\t/**\n\t * Where JWT based authentication is performed.\n\t * @see RSocketSecurity#jwt(Customizer)\n\t * @deprecated please see {@link PayloadInterceptorOrder#AUTHENTICATION}\n\t */\n\t@Deprecated\n\tJWT_AUTHENTICATION,\n\t/**\n\t * A generic placeholder for other types of authentication.\n\t * @see org.springframework.security.rsocket.authentication.AuthenticationPayloadInterceptor\n\t */\n\tAUTHENTICATION,\n\t/**\n\t * Where anonymous authentication is placed.\n\t */\n\tANONYMOUS,\n\t/**\n\t * Where authorization is placed.\n\t * @see org.springframework.security.rsocket.authorization.AuthorizationPayloadInterceptor\n\t */\n\tAUTHORIZATION;\n\n\tprivate static final int INTERVAL = 100;\n\n\tprivate int order;\n\n\tPayloadInterceptorOrder() {\n\t\tthis.order = ordinal() * INTERVAL;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/rsocket/RSocketSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotationAwareOrderComparator;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityReactiveAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.security.rsocket.authentication.AnonymousPayloadInterceptor;\nimport org.springframework.security.rsocket.authentication.AuthenticationPayloadExchangeConverter;\nimport org.springframework.security.rsocket.authentication.AuthenticationPayloadInterceptor;\nimport org.springframework.security.rsocket.authentication.BearerPayloadExchangeConverter;\nimport org.springframework.security.rsocket.authorization.AuthorizationPayloadInterceptor;\nimport org.springframework.security.rsocket.authorization.PayloadExchangeMatcherReactiveAuthorizationManager;\nimport org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcher;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcherEntry;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatchers;\nimport org.springframework.security.rsocket.util.matcher.RoutePayloadExchangeMatcher;\n\n/**\n * Allows configuring RSocket based security.\n *\n * A minimal example can be found below:\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableRSocketSecurity\n * public class SecurityConfig {\n *     &#064;Bean\n *     PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n *         rsocket\n *             .authorizePayload((authorize) -&gt;\n *                 authorize\n *                     .anyRequest().authenticated()\n *             );\n *         return rsocket.build();\n *     }\n *\n *     &#064;Bean\n *     public MapReactiveUserDetailsService userDetailsService() {\n *          UserDetails user = User.withDefaultPasswordEncoder()\n *               .username(\"user\")\n *               .password(\"password\")\n *               .roles(\"USER\")\n *               .build();\n *          return new MapReactiveUserDetailsService(user);\n *     }\n * }\n * </pre>\n *\n * A more advanced configuration can be seen below:\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableRSocketSecurity\n * public class SecurityConfig {\n *     &#064;Bean\n *     PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n *         rsocket\n *             .authorizePayload((authorize) -&gt;\n *                 authorize\n *                     // must have ROLE_SETUP to make connection\n *                     .setup().hasRole(\"SETUP\")\n *                      // must have ROLE_ADMIN for routes starting with \"admin.\"\n *                     .route(\"admin.*\").hasRole(\"ADMIN\")\n *                     // any other request must be authenticated for\n *                     .anyRequest().authenticated()\n *             );\n *         return rsocket.build();\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @author Jesús Ascama Arias\n * @author Luis Felipe Vega\n * @author Manuel Tejeda\n * @author Ebert Toribio\n * @author Ngoc Nhan\n * @author Andrey Litvitski\n * @since 5.2\n */\npublic class RSocketSecurity {\n\n\tprivate List<PayloadInterceptor> payloadInterceptors = new ArrayList<>();\n\n\tprivate BasicAuthenticationSpec basicAuthSpec;\n\n\tprivate SimpleAuthenticationSpec simpleAuthSpec;\n\n\tprivate AnonymousAuthenticationSpec anonymousAuthSpec = new AnonymousAuthenticationSpec(this);\n\n\tprivate JwtSpec jwtSpec;\n\n\tprivate AuthorizePayloadsSpec authorizePayload;\n\n\tprivate ApplicationContext context;\n\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t/**\n\t * Adds a {@link PayloadInterceptor} to be used. This is typically only used when\n\t * using the DSL does not meet a users needs. In order to ensure the\n\t * {@link PayloadInterceptor} is done in the proper order the\n\t * {@link PayloadInterceptor} should either implement\n\t * {@link org.springframework.core.Ordered} or be annotated with\n\t * {@link org.springframework.core.annotation.Order}.\n\t * @param interceptor\n\t * @return the builder for additional customizations\n\t * @see PayloadInterceptorOrder\n\t */\n\tpublic RSocketSecurity addPayloadInterceptor(PayloadInterceptor interceptor) {\n\t\tthis.payloadInterceptors.add(interceptor);\n\t\treturn this;\n\t}\n\n\tpublic RSocketSecurity authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds support for validating a username and password using <a href=\n\t * \"https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Simple.md\">Simple\n\t * Authentication</a>\n\t * @param simple a customizer\n\t * @return RSocketSecurity for additional configuration\n\t * @since 5.3\n\t */\n\tpublic RSocketSecurity simpleAuthentication(Customizer<SimpleAuthenticationSpec> simple) {\n\t\tif (this.simpleAuthSpec == null) {\n\t\t\tthis.simpleAuthSpec = new SimpleAuthenticationSpec();\n\t\t}\n\t\tsimple.customize(this.simpleAuthSpec);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds anonymous authentication\n\t * @param anonymous a customizer\n\t * @return this instance\n\t * @since 7.0\n\t */\n\tpublic RSocketSecurity anonymous(Customizer<AnonymousAuthenticationSpec> anonymous) {\n\t\tif (this.anonymousAuthSpec == null) {\n\t\t\tthis.anonymousAuthSpec = new AnonymousAuthenticationSpec(this);\n\t\t}\n\t\tanonymous.customize(this.anonymousAuthSpec);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds authentication with BasicAuthenticationPayloadExchangeConverter.\n\t * @param basic\n\t * @return this instance\n\t * @deprecated Use {@link #simpleAuthentication(Customizer)}\n\t */\n\t@Deprecated\n\tpublic RSocketSecurity basicAuthentication(Customizer<BasicAuthenticationSpec> basic) {\n\t\tif (this.basicAuthSpec == null) {\n\t\t\tthis.basicAuthSpec = new BasicAuthenticationSpec();\n\t\t}\n\t\tbasic.customize(this.basicAuthSpec);\n\t\treturn this;\n\t}\n\n\tpublic RSocketSecurity jwt(Customizer<JwtSpec> jwt) {\n\t\tif (this.jwtSpec == null) {\n\t\t\tthis.jwtSpec = new JwtSpec();\n\t\t}\n\t\tjwt.customize(this.jwtSpec);\n\t\treturn this;\n\t}\n\n\tpublic RSocketSecurity authorizePayload(Customizer<AuthorizePayloadsSpec> authorize) {\n\t\tif (this.authorizePayload == null) {\n\t\t\tthis.authorizePayload = new AuthorizePayloadsSpec();\n\t\t}\n\t\tauthorize.customize(this.authorizePayload);\n\t\treturn this;\n\t}\n\n\tpublic PayloadSocketAcceptorInterceptor build() {\n\t\tPayloadSocketAcceptorInterceptor interceptor = new PayloadSocketAcceptorInterceptor(payloadInterceptors());\n\t\tRSocketMessageHandler handler = getBean(RSocketMessageHandler.class);\n\t\tinterceptor.setDefaultDataMimeType(handler.getDefaultDataMimeType());\n\t\tinterceptor.setDefaultMetadataMimeType(handler.getDefaultMetadataMimeType());\n\t\treturn interceptor;\n\t}\n\n\tprivate List<PayloadInterceptor> payloadInterceptors() {\n\t\tList<PayloadInterceptor> result = new ArrayList<>(this.payloadInterceptors);\n\t\tif (this.basicAuthSpec != null) {\n\t\t\tresult.add(this.basicAuthSpec.build());\n\t\t}\n\t\tif (this.simpleAuthSpec != null) {\n\t\t\tresult.add(this.simpleAuthSpec.build());\n\t\t}\n\t\tif (this.jwtSpec != null) {\n\t\t\tresult.addAll(this.jwtSpec.build());\n\t\t}\n\t\tif (this.anonymousAuthSpec != null) {\n\t\t\tresult.add(this.anonymousAuthSpec.build());\n\t\t}\n\t\tif (this.authorizePayload != null) {\n\t\t\tresult.add(this.authorizePayload.build());\n\t\t}\n\t\tAnnotationAwareOrderComparator.sort(result);\n\t\treturn result;\n\t}\n\n\tprivate <T> T getBean(Class<T> beanClass) {\n\t\tif (this.context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.context.getBean(beanClass);\n\t}\n\n\tprivate <T> T getBeanOrNull(Class<T> beanClass) {\n\t\treturn getBeanOrNull(ResolvableType.forClass(beanClass));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T> T getBeanOrNull(ResolvableType type) {\n\t\tif (this.context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (T) this.context.getBeanProvider(type).getIfUnique();\n\t}\n\n\tprotected void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.context = applicationContext;\n\t}\n\n\t/**\n\t * @since 5.3\n\t */\n\tpublic final class SimpleAuthenticationSpec {\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate SimpleAuthenticationSpec() {\n\t\t}\n\n\t\tpublic SimpleAuthenticationSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\tif (this.authenticationManager == null) {\n\t\t\t\treturn RSocketSecurity.this.authenticationManager;\n\t\t\t}\n\t\t\treturn this.authenticationManager;\n\t\t}\n\n\t\tprotected AuthenticationPayloadInterceptor build() {\n\t\t\tReactiveAuthenticationManager manager = getAuthenticationManager();\n\t\t\tAuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager);\n\t\t\tresult.setAuthenticationConverter(new AuthenticationPayloadExchangeConverter());\n\t\t\tresult.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n\tpublic final class AnonymousAuthenticationSpec {\n\n\t\tprivate RSocketSecurity parent;\n\n\t\tprivate AnonymousAuthenticationSpec(RSocketSecurity parent) {\n\t\t\tthis.parent = parent;\n\t\t}\n\n\t\tprotected AnonymousPayloadInterceptor build() {\n\t\t\tAnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor(\"anonymousUser\");\n\t\t\tresult.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic void disable() {\n\t\t\tthis.parent.anonymousAuthSpec = null;\n\t\t}\n\n\t}\n\n\tpublic final class BasicAuthenticationSpec {\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate BasicAuthenticationSpec() {\n\t\t}\n\n\t\tpublic BasicAuthenticationSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\tif (this.authenticationManager == null) {\n\t\t\t\treturn RSocketSecurity.this.authenticationManager;\n\t\t\t}\n\t\t\treturn this.authenticationManager;\n\t\t}\n\n\t\tprotected AuthenticationPayloadInterceptor build() {\n\t\t\tReactiveAuthenticationManager manager = getAuthenticationManager();\n\t\t\tAuthenticationPayloadInterceptor result = new AuthenticationPayloadInterceptor(manager);\n\t\t\tresult.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n\tpublic final class JwtSpec {\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate JwtSpec() {\n\t\t}\n\n\t\tpublic JwtSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\tif (this.authenticationManager != null) {\n\t\t\t\treturn this.authenticationManager;\n\t\t\t}\n\t\t\tReactiveJwtDecoder jwtDecoder = getBeanOrNull(ReactiveJwtDecoder.class);\n\t\t\tif (jwtDecoder != null) {\n\t\t\t\tthis.authenticationManager = new JwtReactiveAuthenticationManager(jwtDecoder);\n\t\t\t\treturn this.authenticationManager;\n\t\t\t}\n\t\t\treturn RSocketSecurity.this.authenticationManager;\n\t\t}\n\n\t\tprotected List<AuthenticationPayloadInterceptor> build() {\n\t\t\tReactiveAuthenticationManager manager = getAuthenticationManager();\n\t\t\tAuthenticationPayloadInterceptor legacy = new AuthenticationPayloadInterceptor(manager);\n\t\t\tlegacy.setAuthenticationConverter(new BearerPayloadExchangeConverter());\n\t\t\tlegacy.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());\n\t\t\tAuthenticationPayloadInterceptor standard = new AuthenticationPayloadInterceptor(manager);\n\t\t\tstandard.setAuthenticationConverter(new AuthenticationPayloadExchangeConverter());\n\t\t\tstandard.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());\n\t\t\treturn Arrays.asList(standard, legacy);\n\t\t}\n\n\t}\n\n\tpublic class AuthorizePayloadsSpec {\n\n\t\tprivate PayloadExchangeMatcherReactiveAuthorizationManager.Builder authzBuilder = PayloadExchangeMatcherReactiveAuthorizationManager\n\t\t\t.builder();\n\n\t\tpublic Access setup() {\n\t\t\treturn matcher(PayloadExchangeMatchers.setup());\n\t\t}\n\n\t\t/**\n\t\t * Matches if\n\t\t * {@link org.springframework.security.rsocket.api.PayloadExchangeType#isRequest()}\n\t\t * is true, else not a match\n\t\t * @return the Access to set up the authorization rule.\n\t\t */\n\t\tpublic Access anyRequest() {\n\t\t\treturn matcher(PayloadExchangeMatchers.anyRequest());\n\t\t}\n\n\t\t/**\n\t\t * Always matches\n\t\t * @return the Access to set up the authorization rule.\n\t\t */\n\t\tpublic Access anyExchange() {\n\t\t\treturn matcher(PayloadExchangeMatchers.anyExchange());\n\t\t}\n\n\t\tprotected AuthorizationPayloadInterceptor build() {\n\t\t\tAuthorizationPayloadInterceptor result = new AuthorizationPayloadInterceptor(this.authzBuilder.build());\n\t\t\tresult.setOrder(PayloadInterceptorOrder.AUTHORIZATION.getOrder());\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic Access route(String pattern) {\n\t\t\tRSocketMessageHandler handler = getBean(RSocketMessageHandler.class);\n\t\t\tPayloadExchangeMatcher matcher = new RoutePayloadExchangeMatcher(handler.getMetadataExtractor(),\n\t\t\t\t\thandler.getRouteMatcher(), pattern);\n\t\t\treturn matcher(matcher);\n\t\t}\n\n\t\tpublic Access matcher(PayloadExchangeMatcher matcher) {\n\t\t\treturn new Access(matcher);\n\t\t}\n\n\t\tpublic final class Access {\n\n\t\t\tprivate final PayloadExchangeMatcher matcher;\n\n\t\t\tprivate Access(PayloadExchangeMatcher matcher) {\n\t\t\t\tthis.matcher = matcher;\n\t\t\t}\n\n\t\t\tpublic AuthorizePayloadsSpec authenticated() {\n\t\t\t\treturn access(AuthenticatedReactiveAuthorizationManager.authenticated());\n\t\t\t}\n\n\t\t\tpublic AuthorizePayloadsSpec hasAuthority(String authority) {\n\t\t\t\treturn access(AuthorityReactiveAuthorizationManager.hasAuthority(authority));\n\t\t\t}\n\n\t\t\tpublic AuthorizePayloadsSpec hasRole(String role) {\n\t\t\t\treturn access(AuthorityReactiveAuthorizationManager.hasRole(role));\n\t\t\t}\n\n\t\t\tpublic AuthorizePayloadsSpec hasAnyRole(String... roles) {\n\t\t\t\treturn access(AuthorityReactiveAuthorizationManager.hasAnyRole(roles));\n\t\t\t}\n\n\t\t\tpublic AuthorizePayloadsSpec permitAll() {\n\t\t\t\treturn access((a, ctx) -> Mono.just(new AuthorizationDecision(true)));\n\t\t\t}\n\n\t\t\tpublic AuthorizePayloadsSpec hasAnyAuthority(String... authorities) {\n\t\t\t\treturn access(AuthorityReactiveAuthorizationManager.hasAnyAuthority(authorities));\n\t\t\t}\n\n\t\t\tpublic AuthorizePayloadsSpec access(\n\t\t\t\t\tReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> authorization) {\n\t\t\t\tAuthorizePayloadsSpec.this.authzBuilder\n\t\t\t\t\t.add(new PayloadExchangeMatcherEntry<>(this.matcher, authorization));\n\t\t\t\treturn AuthorizePayloadsSpec.this;\n\t\t\t}\n\n\t\t\tpublic AuthorizePayloadsSpec denyAll() {\n\t\t\t\treturn access((a, ctx) -> Mono.just(new AuthorizationDecision(false)));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/rsocket/RSocketSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n/**\n * @author Rob Winch\n * @since 5.2\n */\n@Configuration(proxyBeanMethods = false)\nclass RSocketSecurityConfiguration {\n\n\tprivate static final String BEAN_NAME_PREFIX = \"org.springframework.security.config.annotation.rsocket.RSocketSecurityConfiguration.\";\n\n\tprivate static final String RSOCKET_SECURITY_BEAN_NAME = BEAN_NAME_PREFIX + \"rsocketSecurity\";\n\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\tprivate ReactiveUserDetailsService reactiveUserDetailsService;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\tprivate ObjectPostProcessor<ReactiveAuthenticationManager> postProcessor = ObjectPostProcessor.identity();\n\n\t@Autowired(required = false)\n\tvoid setAuthenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setUserDetailsService(ReactiveUserDetailsService userDetailsService) {\n\t\tthis.reactiveUserDetailsService = userDetailsService;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tthis.passwordEncoder = passwordEncoder;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setAuthenticationManagerPostProcessor(\n\t\t\tMap<String, ObjectPostProcessor<ReactiveAuthenticationManager>> postProcessors) {\n\t\tif (postProcessors.size() == 1) {\n\t\t\tthis.postProcessor = postProcessors.values().iterator().next();\n\t\t}\n\t\tthis.postProcessor = postProcessors.get(\"rSocketAuthenticationManagerPostProcessor\");\n\t}\n\n\t@Bean(name = RSOCKET_SECURITY_BEAN_NAME)\n\t@Scope(\"prototype\")\n\tRSocketSecurity rsocketSecurity(ApplicationContext context) {\n\t\tRSocketSecurity security = new RSocketSecurity().authenticationManager(authenticationManager());\n\t\tsecurity.setApplicationContext(context);\n\t\treturn security;\n\t}\n\n\tprivate ReactiveAuthenticationManager authenticationManager() {\n\t\tif (this.authenticationManager != null) {\n\t\t\treturn this.authenticationManager;\n\t\t}\n\t\tif (this.reactiveUserDetailsService != null) {\n\t\t\tUserDetailsRepositoryReactiveAuthenticationManager manager = new UserDetailsRepositoryReactiveAuthenticationManager(\n\t\t\t\t\tthis.reactiveUserDetailsService);\n\t\t\tif (this.passwordEncoder != null) {\n\t\t\t\tmanager.setPasswordEncoder(this.passwordEncoder);\n\t\t\t}\n\t\t\treturn this.postProcessor.postProcess(manager);\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/rsocket/ReactiveObservationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.authentication.ObservationReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authorization.ObservationReactiveAuthorizationManager;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\nimport org.springframework.security.rsocket.api.PayloadExchange;\n\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nclass ReactiveObservationConfiguration {\n\n\tprivate static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()\n\t\t.shouldObserveRequests(true)\n\t\t.shouldObserveAuthentications(true)\n\t\t.shouldObserveAuthorizations(true)\n\t\t.build();\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<ReactiveAuthorizationManager<PayloadExchange>> rSocketAuthorizationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();\n\t\t\t\treturn active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<ReactiveAuthenticationManager> rSocketAuthenticationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic ReactiveAuthenticationManager postProcess(ReactiveAuthenticationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthentications();\n\t\t\t\treturn active ? new ObservationReactiveAuthenticationManager(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/rsocket/ReactiveObservationImportSelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Used by {@link EnableWebFluxSecurity} to conditionally import observation configuration\n * when {@link ObservationRegistry} is present.\n *\n * @author Josh Cummings\n * @since 6.4\n */\nclass ReactiveObservationImportSelector implements ImportSelector {\n\n\tprivate static final boolean observabilityPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = ReactiveObservationImportSelector.class.getClassLoader();\n\t\tobservabilityPresent = ClassUtils.isPresent(\"io.micrometer.observation.ObservationRegistry\", classLoader);\n\t}\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\tif (!observabilityPresent) {\n\t\t\treturn new String[0];\n\t\t}\n\t\treturn new String[] { ReactiveObservationConfiguration.class.getName() };\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/rsocket/SecuritySocketAcceptorInterceptorConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.rsocket;\n\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcher.MatchResult;\n\n/**\n * @author Rob Winch\n * @since 5.2\n */\n@Configuration(proxyBeanMethods = false)\nclass SecuritySocketAcceptorInterceptorConfiguration {\n\n\t@Bean\n\tSecuritySocketAcceptorInterceptor securitySocketAcceptorInterceptor(\n\t\t\tObjectProvider<PayloadSocketAcceptorInterceptor> rsocketInterceptor,\n\t\t\tObjectProvider<RSocketSecurity> rsocketSecurity) {\n\t\tPayloadSocketAcceptorInterceptor delegate = rsocketInterceptor\n\t\t\t.getIfAvailable(() -> defaultInterceptor(rsocketSecurity));\n\t\treturn new SecuritySocketAcceptorInterceptor(delegate);\n\t}\n\n\tprivate PayloadSocketAcceptorInterceptor defaultInterceptor(ObjectProvider<RSocketSecurity> rsocketSecurity) {\n\t\tRSocketSecurity rsocket = rsocketSecurity.getIfAvailable();\n\t\tif (rsocket == null) {\n\t\t\tthrow new NoSuchBeanDefinitionException(\"No RSocketSecurity defined\");\n\t\t}\n\t\t// @formatter:off\n\t\trsocket.basicAuthentication(Customizer.withDefaults())\n\t\t\t.simpleAuthentication(Customizer.withDefaults())\n\t\t\t.authorizePayload((authz) -> authz\n\t\t\t\t.setup().authenticated()\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t.matcher((e) -> MatchResult.match()).permitAll()\n\t\t\t);\n\t\t// @formatter:on\n\t\treturn rsocket.build();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.DispatcherType;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.function.ThrowingSupplier;\n\n/**\n * A base class for registering {@link RequestMatcher}'s. For example, it might allow for\n * specifying which {@link RequestMatcher} require a certain level of authorization.\n *\n * @param <C> The object that is returned or Chained after creating the RequestMatcher\n * @author Rob Winch\n * @author Ankur Pathak\n * @since 3.2\n */\npublic abstract class AbstractRequestMatcherRegistry<C> {\n\n\tprivate static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;\n\n\tprivate ApplicationContext context;\n\n\tprivate boolean anyRequestConfigured = false;\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate PathPatternRequestMatcher.Builder requestMatcherBuilder;\n\n\tprotected final void setApplicationContext(ApplicationContext context) {\n\t\tthis.context = context;\n\t}\n\n\t/**\n\t * Gets the {@link ApplicationContext}\n\t * @return the {@link ApplicationContext}\n\t */\n\tprotected final ApplicationContext getApplicationContext() {\n\t\treturn this.context;\n\t}\n\n\t/**\n\t * Maps any request.\n\t * @return the object that is chained after creating the {@link RequestMatcher}\n\t */\n\tpublic C anyRequest() {\n\t\tAssert.state(!this.anyRequestConfigured, \"Can't configure anyRequest after itself\");\n\t\tC configurer = requestMatchers(ANY_REQUEST);\n\t\tthis.anyRequestConfigured = true;\n\t\treturn configurer;\n\t}\n\n\t/**\n\t * Maps a {@link List} of\n\t * {@link org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher}\n\t * instances.\n\t * @param method the {@link HttpMethod} to use or {@code null} for any\n\t * {@link HttpMethod}.\n\t * @param dispatcherTypes the dispatcher types to match against\n\t * @return the object that is chained after creating the {@link RequestMatcher}\n\t */\n\tpublic C dispatcherTypeMatchers(@Nullable HttpMethod method, DispatcherType... dispatcherTypes) {\n\t\tAssert.state(!this.anyRequestConfigured, \"Can't configure dispatcherTypeMatchers after anyRequest\");\n\t\tList<RequestMatcher> matchers = new ArrayList<>();\n\t\tfor (DispatcherType dispatcherType : dispatcherTypes) {\n\t\t\tmatchers.add(new DispatcherTypeRequestMatcher(dispatcherType, method));\n\t\t}\n\t\treturn chainRequestMatchers(matchers);\n\t}\n\n\t/**\n\t * Create a {@link List} of\n\t * {@link org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher}\n\t * instances that do not specify an {@link HttpMethod}.\n\t * @param dispatcherTypes the dispatcher types to match against\n\t * @return the object that is chained after creating the {@link RequestMatcher}\n\t */\n\tpublic C dispatcherTypeMatchers(DispatcherType... dispatcherTypes) {\n\t\tAssert.state(!this.anyRequestConfigured, \"Can't configure dispatcherTypeMatchers after anyRequest\");\n\t\treturn dispatcherTypeMatchers(null, dispatcherTypes);\n\t}\n\n\t/**\n\t * Associates a list of {@link RequestMatcher} instances with the\n\t * {@link AbstractRequestMatcherRegistry}\n\t * @param requestMatchers the {@link RequestMatcher} instances\n\t * @return the object that is chained after creating the {@link RequestMatcher}\n\t */\n\tpublic C requestMatchers(RequestMatcher... requestMatchers) {\n\t\tAssert.state(!this.anyRequestConfigured, \"Can't configure requestMatchers after anyRequest\");\n\t\treturn chainRequestMatchers(Arrays.asList(requestMatchers));\n\t}\n\n\t/**\n\t * <p>\n\t * Match when the {@link HttpMethod} is {@code method} and when the request URI\n\t * matches one of {@code patterns}. See\n\t * {@link org.springframework.web.util.pattern.PathPattern} for matching rules.\n\t * </p>\n\t * <p>\n\t * If a specific {@link RequestMatcher} must be specified, use\n\t * {@link #requestMatchers(RequestMatcher...)} instead\n\t * </p>\n\t * @param method the {@link HttpMethod} to use or {@code null} for any\n\t * {@link HttpMethod}.\n\t * @param patterns the patterns to match on\n\t * @return the object that is chained after creating the {@link RequestMatcher}.\n\t * @since 5.8\n\t */\n\tpublic C requestMatchers(HttpMethod method, String... patterns) {\n\t\tif (anyPathsDontStartWithLeadingSlash(patterns)) {\n\t\t\tthis.logger.warn(\"One of the patterns in \" + Arrays.toString(patterns)\n\t\t\t\t\t+ \" is missing a leading slash. This is discouraged; please include the \"\n\t\t\t\t\t+ \"leading slash in all your request matcher patterns. In future versions of \"\n\t\t\t\t\t+ \"Spring Security, leaving out the leading slash will result in an exception.\");\n\t\t}\n\t\tAssert.state(!this.anyRequestConfigured, \"Can't configure requestMatchers after anyRequest\");\n\t\tPathPatternRequestMatcher.Builder builder = getRequestMatcherBuilder();\n\t\tList<RequestMatcher> matchers = new ArrayList<>();\n\t\tfor (String pattern : patterns) {\n\t\t\tmatchers.add(builder.matcher(method, pattern));\n\t\t}\n\t\treturn requestMatchers(matchers.toArray(new RequestMatcher[0]));\n\t}\n\n\tprivate PathPatternRequestMatcher.Builder getRequestMatcherBuilder() {\n\t\tif (this.requestMatcherBuilder != null) {\n\t\t\treturn this.requestMatcherBuilder;\n\t\t}\n\t\tthis.requestMatcherBuilder = this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class)\n\t\t\t.getIfUnique(() -> constructRequestMatcherBuilder(this.context));\n\t\treturn this.requestMatcherBuilder;\n\t}\n\n\tprivate PathPatternRequestMatcher.Builder constructRequestMatcherBuilder(ApplicationContext context) {\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder = new PathPatternRequestMatcherBuilderFactoryBean();\n\t\trequestMatcherBuilder.setApplicationContext(context);\n\t\trequestMatcherBuilder.setBeanFactory(context.getAutowireCapableBeanFactory());\n\t\trequestMatcherBuilder.setBeanName(requestMatcherBuilder.toString());\n\t\treturn ThrowingSupplier.of(requestMatcherBuilder::getObject).get();\n\t}\n\n\tprivate boolean anyPathsDontStartWithLeadingSlash(String... patterns) {\n\t\tfor (String pattern : patterns) {\n\t\t\tif (!pattern.startsWith(\"/\")) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * <p>\n\t * Match when the request URI matches one of {@code patterns}. See\n\t * {@link org.springframework.web.util.pattern.PathPattern} for matching rules.\n\t * </p>\n\t * <p>\n\t * If a specific {@link RequestMatcher} must be specified, use\n\t * {@link #requestMatchers(RequestMatcher...)} instead\n\t * </p>\n\t * @param patterns the patterns to match on\n\t * @return the object that is chained after creating the {@link RequestMatcher}.\n\t * @since 5.8\n\t */\n\tpublic C requestMatchers(String... patterns) {\n\t\treturn requestMatchers(null, patterns);\n\t}\n\n\t/**\n\t * <p>\n\t * Match when the {@link HttpMethod} is {@code method}\n\t * </p>\n\t * <p>\n\t * If a specific {@link RequestMatcher} must be specified, use\n\t * {@link #requestMatchers(RequestMatcher...)} instead\n\t * </p>\n\t * @param method the {@link HttpMethod} to use or {@code null} for any\n\t * {@link HttpMethod}.\n\t * @return the object that is chained after creating the {@link RequestMatcher}.\n\t * @since 5.8\n\t */\n\tpublic C requestMatchers(HttpMethod method) {\n\t\treturn requestMatchers(method, \"/**\");\n\t}\n\n\t/**\n\t * Subclasses should implement this method for returning the object that is chained to\n\t * the creation of the {@link RequestMatcher} instances.\n\t * @param requestMatchers the {@link RequestMatcher} instances that were created\n\t * @return the chained Object for the subclass which allows association of something\n\t * else to the {@link RequestMatcher}\n\t */\n\tprotected abstract C chainRequestMatchers(List<RequestMatcher> requestMatchers);\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport jakarta.servlet.Filter;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.annotation.SecurityBuilder;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.channel.ChannelProcessingFilter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;\nimport org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserFilter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.authentication.www.DigestAuthenticationFilter;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.header.HeaderWriterFilter;\nimport org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;\nimport org.springframework.security.web.savedrequest.RequestCacheAwareFilter;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\nimport org.springframework.security.web.session.ConcurrentSessionFilter;\nimport org.springframework.security.web.session.DisableEncodeUrlFilter;\nimport org.springframework.security.web.session.ForceEagerSessionCreationFilter;\nimport org.springframework.security.web.session.SessionManagementFilter;\nimport org.springframework.web.filter.CorsFilter;\n\n/**\n * @param <H>\n * @author Rob Winch\n */\npublic interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>\n\t\textends SecurityBuilder<DefaultSecurityFilterChain> {\n\n\t/**\n\t * Gets the {@link SecurityConfigurer} by its class name or <code>null</code> if not\n\t * found. Note that object hierarchies are not considered.\n\t * @param clazz the Class of the {@link SecurityConfigurer} to attempt to get.\n\t */\n\t<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz);\n\n\t/**\n\t * Removes the {@link SecurityConfigurer} by its class name or <code>null</code> if\n\t * not found. Note that object hierarchies are not considered.\n\t * @param clazz the Class of the {@link SecurityConfigurer} to attempt to remove.\n\t * @return the {@link SecurityConfigurer} that was removed or null if not found\n\t */\n\t<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz);\n\n\t/**\n\t * Sets an object that is shared by multiple {@link SecurityConfigurer}.\n\t * @param sharedType the Class to key the shared object by.\n\t * @param object the Object to store\n\t */\n\t<C> void setSharedObject(Class<C> sharedType, C object);\n\n\t/**\n\t * Gets a shared Object. Note that object hierarchies are not considered.\n\t * @param sharedType the type of the shared Object\n\t * @return the shared Object or null if it is not found\n\t */\n\t<C> C getSharedObject(Class<C> sharedType);\n\n\t/**\n\t * Allows adding an additional {@link AuthenticationProvider} to be used\n\t * @param authenticationProvider the {@link AuthenticationProvider} to be added\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tH authenticationProvider(AuthenticationProvider authenticationProvider);\n\n\t/**\n\t * Allows adding an additional {@link UserDetailsService} to be used\n\t * @param userDetailsService the {@link UserDetailsService} to be added\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tH userDetailsService(UserDetailsService userDetailsService) throws Exception;\n\n\t/**\n\t * Allows adding a {@link Filter} after one of the known {@link Filter} classes. The\n\t * known {@link Filter} instances are either a {@link Filter} listed in\n\t * {@link #addFilter(Filter)} or a {@link Filter} that has already been added using\n\t * {@link #addFilterAfter(Filter, Class)} or {@link #addFilterBefore(Filter, Class)}.\n\t * @param filter the {@link Filter} to register after the type {@code afterFilter}\n\t * @param afterFilter the Class of the known {@link Filter}.\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tH addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);\n\n\t/**\n\t * Allows adding a {@link Filter} before one of the known {@link Filter} classes. The\n\t * known {@link Filter} instances are either a {@link Filter} listed in\n\t * {@link #addFilter(Filter)} or a {@link Filter} that has already been added using\n\t * {@link #addFilterAfter(Filter, Class)} or {@link #addFilterBefore(Filter, Class)}.\n\t * @param filter the {@link Filter} to register before the type {@code beforeFilter}\n\t * @param beforeFilter the Class of the known {@link Filter}.\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tH addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);\n\n\t/**\n\t * Adds a {@link Filter} that must be an instance of or extend one of the Filters\n\t * provided within the Security framework. The method ensures that the ordering of the\n\t * Filters is automatically taken care of.\n\t *\n\t * The ordering of the Filters is:\n\t *\n\t * <ul>\n\t * <li>{@link DisableEncodeUrlFilter}</li>\n\t * <li>{@link ForceEagerSessionCreationFilter}</li>\n\t * <li>{@link ChannelProcessingFilter}</li>\n\t * <li>{@link org.springframework.security.web.transport.HttpsRedirectFilter}</li>\n\t * <li>{@link WebAsyncManagerIntegrationFilter}</li>\n\t * <li>{@link SecurityContextHolderFilter}</li>\n\t * <li>{@link SecurityContextPersistenceFilter}</li>\n\t * <li>{@link HeaderWriterFilter}</li>\n\t * <li>{@link CorsFilter}</li>\n\t * <li>{@link CsrfFilter}</li>\n\t * <li>{@link LogoutFilter}</li>\n\t * <li>{@link org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter}</li>\n\t * <li>{@link org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter}</li>\n\t * <li>{@link X509AuthenticationFilter}</li>\n\t * <li>{@link AbstractPreAuthenticatedProcessingFilter}</li>\n\t * <li><a href=\"\n\t * {@docRoot}/org/springframework/security/cas/web/CasAuthenticationFilter.html\">CasAuthenticationFilter</a></li>\n\t * <li>{@link org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter}</li>\n\t * <li>{@link org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter}</li>\n\t * <li>{@link UsernamePasswordAuthenticationFilter}</li>\n\t * <li>{@link org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter}</li>\n\t * <li>{@link org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter}</li>\n\t * <li>{@link ConcurrentSessionFilter}</li>\n\t * <li>{@link DigestAuthenticationFilter}</li>\n\t * <li>{@link BearerTokenAuthenticationFilter}</li>\n\t * <li>{@link BasicAuthenticationFilter}</li>\n\t * <li>{@link org.springframework.security.web.authentication.AuthenticationFilter}</li>\n\t * <li>{@link RequestCacheAwareFilter}</li>\n\t * <li>{@link SecurityContextHolderAwareRequestFilter}</li>\n\t * <li>{@link JaasApiIntegrationFilter}</li>\n\t * <li>{@link RememberMeAuthenticationFilter}</li>\n\t * <li>{@link AnonymousAuthenticationFilter}</li>\n\t * <li>{@link org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter}</li>\n\t * <li>{@link SessionManagementFilter}</li>\n\t * <li>{@link ExceptionTranslationFilter}</li>\n\t * <li>{@link FilterSecurityInterceptor}</li>\n\t * <li>{@link AuthorizationFilter}</li>\n\t * <li>{@link SwitchUserFilter}</li>\n\t * </ul>\n\t * @param filter the {@link Filter} to add\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tH addFilter(Filter filter);\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/ServletRegistrationsSupport.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletRegistration;\n\nimport org.springframework.util.ClassUtils;\n\nclass ServletRegistrationsSupport {\n\n\tprivate final Collection<RegistrationMapping> registrations;\n\n\tServletRegistrationsSupport(ServletContext servletContext) {\n\t\tMap<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();\n\t\tCollection<RegistrationMapping> mappings = new ArrayList<>();\n\t\tfor (Map.Entry<String, ? extends ServletRegistration> entry : registrations.entrySet()) {\n\t\t\tif (!entry.getValue().getMappings().isEmpty()) {\n\t\t\t\tfor (String mapping : entry.getValue().getMappings()) {\n\t\t\t\t\tmappings.add(new RegistrationMapping(entry.getValue(), mapping));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.registrations = mappings;\n\t}\n\n\tCollection<RegistrationMapping> dispatcherServletMappings() {\n\t\tCollection<RegistrationMapping> mappings = new ArrayList<>();\n\t\tfor (RegistrationMapping registration : this.registrations) {\n\t\t\tif (registration.isDispatcherServlet()) {\n\t\t\t\tmappings.add(registration);\n\t\t\t}\n\t\t}\n\t\treturn mappings;\n\t}\n\n\tCollection<RegistrationMapping> mappings() {\n\t\treturn this.registrations;\n\t}\n\n\trecord RegistrationMapping(ServletRegistration registration, String mapping) {\n\t\tboolean isDispatcherServlet() {\n\t\t\tClass<?> dispatcherServlet = ClassUtils\n\t\t\t\t.resolveClassName(\"org.springframework.web.servlet.DispatcherServlet\", null);\n\t\t\ttry {\n\t\t\t\tClass<?> clazz = Class.forName(this.registration.getClassName());\n\t\t\t\treturn dispatcherServlet.isAssignableFrom(clazz);\n\t\t\t}\n\t\t\tcatch (ClassNotFoundException ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tboolean isDefault() {\n\t\t\treturn \"/\".equals(this.mapping);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/WebSecurityConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport jakarta.servlet.Filter;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.SecurityBuilder;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.web.builders.WebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\n\n/**\n * Allows customization to the {@link WebSecurity}. In most instances users will use\n * {@link EnableWebSecurity} and create a {@link Configuration} that exposes a\n * {@link SecurityFilterChain} bean. This will automatically be applied to the\n * {@link WebSecurity} by the {@link EnableWebSecurity} annotation.\n *\n * @author Rob Winch\n * @since 3.2\n * @see SecurityFilterChain\n */\npublic interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.Filter;\n\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.security.web.authentication.AuthenticationFilter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationFilter;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;\nimport org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.authentication.www.DigestAuthenticationFilter;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.header.HeaderWriterFilter;\nimport org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;\nimport org.springframework.security.web.savedrequest.RequestCacheAwareFilter;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\nimport org.springframework.security.web.session.ConcurrentSessionFilter;\nimport org.springframework.security.web.session.DisableEncodeUrlFilter;\nimport org.springframework.security.web.session.ForceEagerSessionCreationFilter;\nimport org.springframework.security.web.session.SessionManagementFilter;\nimport org.springframework.security.web.transport.HttpsRedirectFilter;\nimport org.springframework.web.filter.CorsFilter;\n\n/**\n * An internal use only {@link Comparator} that sorts the Security {@link Filter}\n * instances to ensure they are in the correct order.\n *\n * @author Rob Winch\n * @since 3.2\n */\n\n@SuppressWarnings(\"serial\")\nfinal class FilterOrderRegistration {\n\n\tprivate static final int INITIAL_ORDER = 100;\n\n\tprivate static final int ORDER_STEP = 100;\n\n\tprivate final Map<String, Integer> filterToOrder = new HashMap<>();\n\n\tFilterOrderRegistration() {\n\t\tStep order = new Step(INITIAL_ORDER, ORDER_STEP);\n\t\tput(DisableEncodeUrlFilter.class, order.next());\n\t\tput(ForceEagerSessionCreationFilter.class, order.next());\n\t\tthis.filterToOrder.put(\"org.springframework.security.web.access.channel.ChannelProcessingFilter\", order.next());\n\t\tput(HttpsRedirectFilter.class, order.next());\n\t\torder.next(); // gh-8105\n\t\tput(WebAsyncManagerIntegrationFilter.class, order.next());\n\t\tput(SecurityContextHolderFilter.class, order.next());\n\t\tput(SecurityContextPersistenceFilter.class, order.next());\n\t\tput(HeaderWriterFilter.class, order.next());\n\t\tput(CorsFilter.class, order.next());\n\t\tput(CsrfFilter.class, order.next());\n\t\tput(LogoutFilter.class, order.next());\n\t\tthis.filterToOrder.put(\n\t\t\t\t\"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter\",\n\t\t\t\torder.next());\n\t\tthis.filterToOrder.put(\n\t\t\t\t\"org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter\",\n\t\t\t\torder.next());\n\t\tput(GenerateOneTimeTokenFilter.class, order.next());\n\t\tput(X509AuthenticationFilter.class, order.next());\n\t\tput(AbstractPreAuthenticatedProcessingFilter.class, order.next());\n\t\tthis.filterToOrder.put(\"org.springframework.security.cas.web.CasAuthenticationFilter\", order.next());\n\t\tthis.filterToOrder.put(\"org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter\",\n\t\t\t\torder.next());\n\t\tthis.filterToOrder.put(\n\t\t\t\t\"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter\",\n\t\t\t\torder.next());\n\t\tput(UsernamePasswordAuthenticationFilter.class, order.next());\n\t\tput(OneTimeTokenAuthenticationFilter.class, order.next());\n\t\torder.next(); // gh-8105\n\t\tput(DefaultResourcesFilter.class, order.next());\n\t\tput(DefaultLoginPageGeneratingFilter.class, order.next());\n\t\tput(DefaultLogoutPageGeneratingFilter.class, order.next());\n\t\tput(DefaultOneTimeTokenSubmitPageGeneratingFilter.class, order.next());\n\t\tput(ConcurrentSessionFilter.class, order.next());\n\t\tput(DigestAuthenticationFilter.class, order.next());\n\t\tthis.filterToOrder.put(\n\t\t\t\t\"org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter\",\n\t\t\t\torder.next());\n\t\tput(BasicAuthenticationFilter.class, order.next());\n\t\tput(AuthenticationFilter.class, order.next());\n\t\tput(RequestCacheAwareFilter.class, order.next());\n\t\tput(SecurityContextHolderAwareRequestFilter.class, order.next());\n\t\tput(JaasApiIntegrationFilter.class, order.next());\n\t\tput(RememberMeAuthenticationFilter.class, order.next());\n\t\tput(AnonymousAuthenticationFilter.class, order.next());\n\t\tthis.filterToOrder.put(\"org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter\",\n\t\t\t\torder.next());\n\t\tput(SessionManagementFilter.class, order.next());\n\t\tput(ExceptionTranslationFilter.class, order.next());\n\t\tthis.filterToOrder.put(\"org.springframework.security.web.access.intercept.FilterSecurityInterceptor\",\n\t\t\t\torder.next());\n\t\tput(AuthorizationFilter.class, order.next());\n\t\tput(SwitchUserFilter.class, order.next());\n\t}\n\n\t/**\n\t * Register a {@link Filter} with its specific position. If the {@link Filter} was\n\t * already registered before, the position previously defined is not going to be\n\t * overridden\n\t * @param filter the {@link Filter} to register\n\t * @param position the position to associate with the {@link Filter}\n\t */\n\tvoid put(Class<? extends Filter> filter, int position) {\n\t\tthis.filterToOrder.putIfAbsent(filter.getName(), position);\n\t}\n\n\t/**\n\t * Gets the order of a particular {@link Filter} class taking into consideration\n\t * superclasses.\n\t * @param clazz the {@link Filter} class to determine the sort order\n\t * @return the sort order or null if not defined\n\t */\n\tInteger getOrder(Class<?> clazz) {\n\t\twhile (clazz != null) {\n\t\t\tInteger result = this.filterToOrder.get(clazz.getName());\n\t\t\tif (result != null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tclazz = clazz.getSuperclass();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static class Step {\n\n\t\tprivate int value;\n\n\t\tprivate final int stepSize;\n\n\t\tStep(int initialValue, int stepSize) {\n\t\t\tthis.value = initialValue;\n\t\t\tthis.stepSize = stepSize;\n\t\t}\n\n\t\tint next() {\n\t\t\tint value = this.value;\n\t\t\tthis.value += this.stepSize;\n\t\t\treturn value;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.OrderComparator;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;\nimport org.springframework.security.config.annotation.SecurityBuilder;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;\nimport org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry;\nimport org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.CorsConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.JeeConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.PasswordManagementConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.PortMapperConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.ServletApiConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.WebAuthnConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.X509Configurer;\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.ott.OneTimeTokenLoginConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.saml2.Saml2MetadataConfigurer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.session.HttpSessionEventPublisher;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.filter.CorsFilter;\n\n/**\n * A {@link HttpSecurity} is similar to Spring Security's XML &lt;http&gt; element in the\n * namespace configuration. It allows configuring web based security for specific http\n * requests. By default it will be applied to all requests, but can be restricted using\n * {@link #authorizeHttpRequests(Customizer)} or other similar methods.\n *\n * <h2>Example Usage</h2>\n *\n * The most basic form based configuration can be seen below. The configuration will\n * require that any URL that is requested will require a User with the role \"ROLE_USER\".\n * It also defines an in memory authentication scheme with a user that has the username\n * \"user\", the password \"password\", and the role \"ROLE_USER\". For additional examples,\n * refer to the Java Doc of individual methods on {@link HttpSecurity}.\n *\n * <pre>\n * &#064;Configuration\n * &#064;EnableWebSecurity\n * public class FormLoginSecurityConfig {\n *\n * \t&#064;Bean\n * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n * \t\thttp\n * \t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n * \t\t\t)\n * \t\t\t.formLogin(withDefaults());\n * \t\treturn http.build();\n * \t}\n *\n * \t&#064;Bean\n * \tpublic UserDetailsService userDetailsService() {\n * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n * \t\t\t.username(&quot;user&quot;)\n * \t\t\t.password(&quot;password&quot;)\n * \t\t\t.roles(&quot;USER&quot;)\n * \t\t\t.build();\n * \t\treturn new InMemoryUserDetailsManager(user);\n * \t}\n * }\n * </pre>\n *\n * @author Rob Winch\n * @author Joe Grandja\n * @author Ngoc Nhan\n * @since 3.2\n * @see EnableWebSecurity\n */\npublic final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>\n\t\timplements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {\n\n\tprivate final RequestMatcherConfigurer requestMatcherConfigurer;\n\n\tprivate List<OrderedFilter> filters = new ArrayList<>();\n\n\tprivate RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;\n\n\tprivate FilterOrderRegistration filterOrders = new FilterOrderRegistration();\n\n\tprivate AuthenticationManager authenticationManager;\n\n\t/**\n\t * Creates a new instance\n\t * @param objectPostProcessor the {@link ObjectPostProcessor} that should be used\n\t * @param authenticationBuilder the {@link AuthenticationManagerBuilder} to use for\n\t * additional updates\n\t * @param sharedObjects the shared Objects to initialize the {@link HttpSecurity} with\n\t * @see WebSecurityConfiguration\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor,\n\t\t\tAuthenticationManagerBuilder authenticationBuilder, Map<Class<?>, Object> sharedObjects) {\n\t\tsuper(objectPostProcessor);\n\t\tAssert.notNull(authenticationBuilder, \"authenticationBuilder cannot be null\");\n\t\tsetSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);\n\t\tfor (Map.Entry<Class<?>, Object> entry : sharedObjects.entrySet()) {\n\t\t\tsetSharedObject((Class<Object>) entry.getKey(), entry.getValue());\n\t\t}\n\t\tApplicationContext context = (ApplicationContext) sharedObjects.get(ApplicationContext.class);\n\t\tthis.requestMatcherConfigurer = new RequestMatcherConfigurer(context);\n\t}\n\n\tprivate ApplicationContext getContext() {\n\t\treturn getSharedObject(ApplicationContext.class);\n\t}\n\n\t/**\n\t * Adds the Security headers to the response. This is activated by default when using\n\t * {@link EnableWebSecurity}.\n\t *\n\t * <h2>Example Configurations</h2>\n\t *\n\t * Accepting the default provided by {@link EnableWebSecurity} or only invoking\n\t * {@link #headers(Customizer)} without invoking additional methods on it, is the\n\t * equivalent of:\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class CsrfSecurityConfig {\n\t *\n\t *\t&#064;Bean\n\t *\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t *\t\thttp\n\t *\t\t\t.headers((headers) -&gt;\n\t *\t\t\t\theaders\n\t *\t\t\t\t\t.contentTypeOptions(withDefaults())\n\t *\t\t\t\t\t.xssProtection(withDefaults())\n\t *\t\t\t\t\t.cacheControl(withDefaults())\n\t *\t\t\t\t\t.httpStrictTransportSecurity(withDefaults())\n\t *\t\t\t\t\t.frameOptions(withDefaults()\n\t *\t\t\t);\n\t *\t\treturn http.build();\n\t *\t}\n\t * }\n\t * </pre>\n\t *\n\t * You can disable the headers using the following:\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class CsrfSecurityConfig {\n\t *\n\t *\t&#064;Bean\n\t *\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.headers((headers) -&gt; headers.disable());\n\t *\t\treturn http.build();\n\t *\t}\n\t * }\n\t * </pre>\n\t *\n\t * You can enable only a few of the headers by first invoking\n\t * {@link HeadersConfigurer#defaultsDisabled()} and then invoking the appropriate\n\t * methods on the {@link #headers(Customizer)} result. For example, the following will\n\t * enable {@link HeadersConfigurer#cacheControl(Customizer)} and\n\t * {@link HeadersConfigurer#frameOptions(Customizer)} only.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class CsrfSecurityConfig {\n\t *\n\t *\t&#064;Bean\n\t *\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t *\t\thttp\n\t *\t\t\t.headers((headers) -&gt;\n\t *\t\t\t\theaders\n\t *\t\t\t \t\t.defaultsDisabled()\n\t *\t\t\t \t\t.cacheControl(withDefaults())\n\t *\t\t\t \t\t.frameOptions(withDefaults())\n\t *\t\t\t);\n\t *\t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * You can also choose to keep the defaults but explicitly disable a subset of\n\t * headers. For example, the following will enable all the default headers except\n\t * {@link HeadersConfigurer#frameOptions(Customizer)}.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class CsrfSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t *  \thttp\n\t *  \t\t.headers((headers) -&gt;\n\t *  \t\t\theaders\n\t *  \t\t\t\t.frameOptions((frameOptions) -&gt; frameOptions.disable())\n\t *  \t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param headersCustomizer the {@link Customizer} to provide more options for the\n\t * {@link HeadersConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity headers(Customizer<HeadersConfigurer<HttpSecurity>> headersCustomizer) {\n\t\theadersCustomizer.customize(getOrApply(new HeadersConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Adds a {@link CorsFilter} to be used. If a bean by the name of corsFilter is\n\t * provided, that {@link CorsFilter} is used. Else if corsConfigurationSource is\n\t * defined, then that {@link CorsConfiguration} is used. You can enable CORS using:\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class CorsSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.cors(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param corsCustomizer the {@link Customizer} to provide more options for the\n\t * {@link CorsConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity cors(Customizer<CorsConfigurer<HttpSecurity>> corsCustomizer) {\n\t\tcorsCustomizer.customize(getOrApply(new CorsConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Allows configuring of Session Management.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following configuration demonstrates how to enforce that only a single instance\n\t * of a user is authenticated at a time. If a user authenticates with the username\n\t * \"user\" without logging out and an attempt to authenticate with \"user\" is made the\n\t * first session will be forcibly terminated and sent to the \"/login?expired\" URL.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class SessionManagementSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.anyRequest().hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin((formLogin) -&gt;\n\t * \t\t\t\tformLogin\n\t * \t\t\t\t\t.permitAll()\n\t * \t\t\t)\n\t * \t\t\t.sessionManagement((sessionManagement) -&gt;\n\t * \t\t\t\tsessionManagement\n\t * \t\t\t\t\t.sessionConcurrency((sessionConcurrency) -&gt;\n\t * \t\t\t\t\t\tsessionConcurrency\n\t * \t\t\t\t\t\t\t.maximumSessions(1)\n\t * \t\t\t\t\t\t\t.expiredUrl(&quot;/login?expired&quot;)\n\t * \t\t\t\t\t)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * When using {@link SessionManagementConfigurer#maximumSessions(int)}, do not forget\n\t * to configure {@link HttpSessionEventPublisher} for the application to ensure that\n\t * expired sessions are cleaned up.\n\t *\n\t * In a web.xml this can be configured using the following:\n\t *\n\t * <pre>\n\t * &lt;listener&gt;\n\t *      &lt;listener-class&gt;org.springframework.security.web.session.HttpSessionEventPublisher&lt;/listener-class&gt;\n\t * &lt;/listener&gt;\n\t * </pre>\n\t *\n\t * Alternatively,\n\t * {@link AbstractSecurityWebApplicationInitializer#enableHttpSessionEventPublisher()}\n\t * could return true.\n\t * @param sessionManagementCustomizer the {@link Customizer} to provide more options\n\t * for the {@link SessionManagementConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity sessionManagement(\n\t\t\tCustomizer<SessionManagementConfigurer<HttpSecurity>> sessionManagementCustomizer) {\n\t\tsessionManagementCustomizer.customize(getOrApply(new SessionManagementConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Allows configuring a {@link PortMapper} that is available from\n\t * {@link HttpSecurity#getSharedObject(Class)}. Other provided\n\t * {@link SecurityConfigurer} objects use this configured {@link PortMapper} as a\n\t * default {@link PortMapper} when redirecting from HTTP to HTTPS or from HTTPS to\n\t * HTTP (for example when used in combination with\n\t * {@link #requiresChannel(Customizer)} )}. By default Spring Security uses a\n\t * {@link PortMapperImpl} which maps the HTTP port 8080 to the HTTPS port 8443 and the\n\t * HTTP port of 80 to the HTTPS port of 443.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following configuration will ensure that redirects within Spring Security from\n\t * HTTP of a port of 9090 will redirect to HTTPS port of 9443 and the HTTP port of 80\n\t * to the HTTPS port of 443.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class PortMapperSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.requiresChannel((requiresChannel) -&gt;\n\t * \t\t\t\trequiresChannel\n\t * \t\t\t\t\t.anyRequest().requiresSecure()\n\t * \t\t\t)\n\t * \t\t\t.portMapper((portMapper) -&gt;\n\t * \t\t\t\tportMapper\n\t * \t\t\t\t\t.http(9090).mapsTo(9443)\n\t * \t\t\t\t\t.http(80).mapsTo(443)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param portMapperCustomizer the {@link Customizer} to provide more options for the\n\t * {@link PortMapperConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @see #requiresChannel(Customizer)\n\t */\n\tpublic HttpSecurity portMapper(Customizer<PortMapperConfigurer<HttpSecurity>> portMapperCustomizer) {\n\t\tportMapperCustomizer.customize(getOrApply(new PortMapperConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures container based pre authentication. In this case, authentication is\n\t * managed by the Servlet Container.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following configuration will use the principal found on the\n\t * {@link HttpServletRequest} and if the user is in the role \"ROLE_USER\" or\n\t * \"ROLE_ADMIN\" will add that to the resulting {@link Authentication}.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class JeeSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.jee((jee) -&gt;\n\t * \t\t\t\tjee\n\t * \t\t\t\t\t.mappableRoles(&quot;USER&quot;, &quot;ADMIN&quot;)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * Developers wishing to use pre authentication with the container will need to ensure\n\t * their web.xml configures the security constraints. For example, the web.xml (there\n\t * is no equivalent Java based configuration supported by the Servlet specification)\n\t * might look like:\n\t *\n\t * <pre>\n\t * &lt;login-config&gt;\n\t *     &lt;auth-method&gt;FORM&lt;/auth-method&gt;\n\t *     &lt;form-login-config&gt;\n\t *         &lt;form-login-page&gt;/login&lt;/form-login-page&gt;\n\t *         &lt;form-error-page&gt;/login?error&lt;/form-error-page&gt;\n\t *     &lt;/form-login-config&gt;\n\t * &lt;/login-config&gt;\n\t *\n\t * &lt;security-role&gt;\n\t *     &lt;role-name&gt;ROLE_USER&lt;/role-name&gt;\n\t * &lt;/security-role&gt;\n\t * &lt;security-constraint&gt;\n\t *     &lt;web-resource-collection&gt;\n\t *     &lt;web-resource-name&gt;Public&lt;/web-resource-name&gt;\n\t *         &lt;description&gt;Matches unconstrained pages&lt;/description&gt;\n\t *         &lt;url-pattern&gt;/login&lt;/url-pattern&gt;\n\t *         &lt;url-pattern&gt;/logout&lt;/url-pattern&gt;\n\t *         &lt;url-pattern&gt;/resources/*&lt;/url-pattern&gt;\n\t *     &lt;/web-resource-collection&gt;\n\t * &lt;/security-constraint&gt;\n\t * &lt;security-constraint&gt;\n\t *     &lt;web-resource-collection&gt;\n\t *         &lt;web-resource-name&gt;Secured Areas&lt;/web-resource-name&gt;\n\t *         &lt;url-pattern&gt;/*&lt;/url-pattern&gt;\n\t *     &lt;/web-resource-collection&gt;\n\t *     &lt;auth-constraint&gt;\n\t *         &lt;role-name&gt;ROLE_USER&lt;/role-name&gt;\n\t *     &lt;/auth-constraint&gt;\n\t * &lt;/security-constraint&gt;\n\t * </pre>\n\t *\n\t * Last you will need to configure your container to contain the user with the correct\n\t * roles. This configuration is specific to the Servlet Container, so consult your\n\t * Servlet Container's documentation.\n\t * @param jeeCustomizer the {@link Customizer} to provide more options for the\n\t * {@link JeeConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity jee(Customizer<JeeConfigurer<HttpSecurity>> jeeCustomizer) {\n\t\tjeeCustomizer.customize(getOrApply(new JeeConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures X509 based pre authentication.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following configuration will attempt to extract the username from the X509\n\t * certificate. Remember that the Servlet Container will need to be configured to\n\t * request client certificates in order for this to work.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class X509SecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.x509(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param x509Customizer the {@link Customizer} to provide more options for the\n\t * {@link X509Configurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity x509(Customizer<X509Configurer<HttpSecurity>> x509Customizer) {\n\t\tx509Customizer.customize(getOrApply(new X509Configurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Allows configuring of Remember Me authentication.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following configuration demonstrates how to allow token based remember me\n\t * authentication. Upon authenticating if the HTTP parameter named \"remember-me\"\n\t * exists, then the user will be remembered even after their\n\t * {@link jakarta.servlet.http.HttpSession} expires.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class RememberMeSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults())\n\t * \t\t\t.rememberMe(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param rememberMeCustomizer the {@link Customizer} to provide more options for the\n\t * {@link RememberMeConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity rememberMe(Customizer<RememberMeConfigurer<HttpSecurity>> rememberMeCustomizer) {\n\t\trememberMeCustomizer.customize(getOrApply(new RememberMeConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Allows restricting access based upon the {@link HttpServletRequest} using\n\t * {@link RequestMatcher} implementations (i.e. via URL patterns).\n\t *\n\t * <h2>Example Configurations</h2>\n\t *\n\t * The most basic example is to configure all URLs to require the role \"ROLE_USER\".\n\t * The configuration below requires authentication to every URL and will grant access\n\t * to both the user \"admin\" and \"user\".\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class AuthorizeUrlsSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\tUserDetails admin = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;admin&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;ADMIN&quot;, &quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user, admin);\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * We can also configure multiple URLs. The configuration below requires\n\t * authentication to every URL and will grant access to URLs starting with /admin/ to\n\t * only the \"admin\" user. All other URLs either user can access.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class AuthorizeUrlsSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n\t * \t\t\t\t\t.requestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\tUserDetails admin = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;admin&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;ADMIN&quot;, &quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user, admin);\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * Note that the matchers are considered in order. Therefore, the following is invalid\n\t * because the first matcher matches every request and will never get to the second\n\t * mapping:\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class AuthorizeUrlsSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t \t.authorizeHttpRequests((authorize) -&gt; authorize\n\t * \t\t\t \t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t \t\t.requestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)\n\t * \t\t \t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param authorizeHttpRequestsCustomizer the {@link Customizer} to provide more\n\t * options for the {@link AuthorizationManagerRequestMatcherRegistry}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @since 5.5\n\t */\n\tpublic HttpSecurity authorizeHttpRequests(\n\t\t\tCustomizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer) {\n\t\tApplicationContext context = getContext();\n\t\tauthorizeHttpRequestsCustomizer\n\t\t\t.customize(getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry());\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Allows configuring the Request Cache. For example, a protected page (/protected)\n\t * may be requested prior to authentication. The application will redirect the user to\n\t * a login page. After authentication, Spring Security will redirect the user to the\n\t * originally requested protected page (/protected). This is automatically applied\n\t * when using {@link EnableWebSecurity}.\n\t *\n\t * <h2>Example Custom Configuration</h2>\n\t *\n\t * The following example demonstrates how to disable request caching.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class RequestCacheDisabledSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.requestCache((requestCache) -&gt;\n\t * \t\t\t\trequestCache.disable()\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param requestCacheCustomizer the {@link Customizer} to provide more options for\n\t * the {@link RequestCacheConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity requestCache(Customizer<RequestCacheConfigurer<HttpSecurity>> requestCacheCustomizer) {\n\t\trequestCacheCustomizer.customize(getOrApply(new RequestCacheConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Allows configuring exception handling. This is automatically applied when using\n\t * {@link EnableWebSecurity}.\n\t *\n\t * <h2>Example Custom Configuration</h2>\n\t *\n\t * The following customization will ensure that users who are denied access are\n\t * forwarded to the page \"/errors/access-denied\".\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class ExceptionHandlingSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t// sample exception handling customization\n\t * \t\t\t.exceptionHandling((exceptionHandling) -&gt;\n\t * \t\t\t\texceptionHandling\n\t * \t\t\t\t\t.accessDeniedPage(&quot;/errors/access-denied&quot;)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param exceptionHandlingCustomizer the {@link Customizer} to provide more options\n\t * for the {@link ExceptionHandlingConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity exceptionHandling(\n\t\t\tCustomizer<ExceptionHandlingConfigurer<HttpSecurity>> exceptionHandlingCustomizer) {\n\t\texceptionHandlingCustomizer.customize(getOrApply(new ExceptionHandlingConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Sets up management of the {@link SecurityContext} on the\n\t * {@link SecurityContextHolder} between {@link HttpServletRequest}'s. This is\n\t * automatically applied when using {@link EnableWebSecurity}.\n\t *\n\t * The following customization specifies the shared {@link SecurityContextRepository}\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class SecurityContextSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.securityContext((securityContext) -&gt;\n\t * \t\t\t\tsecurityContext\n\t * \t\t\t\t\t.securityContextRepository(SCR)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param securityContextCustomizer the {@link Customizer} to provide more options for\n\t * the {@link SecurityContextConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity securityContext(Customizer<SecurityContextConfigurer<HttpSecurity>> securityContextCustomizer) {\n\t\tsecurityContextCustomizer.customize(getOrApply(new SecurityContextConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Integrates the {@link HttpServletRequest} methods with the values found on the\n\t * {@link SecurityContext}. This is automatically applied when using\n\t * {@link EnableWebSecurity}. You can disable it using:\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class ServletApiSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.servletApi((servletApi) -&gt;\n\t * \t\t\t\tservletApi.disable()\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param servletApiCustomizer the {@link Customizer} to provide more options for the\n\t * {@link ServletApiConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity servletApi(Customizer<ServletApiConfigurer<HttpSecurity>> servletApiCustomizer) {\n\t\tservletApiCustomizer.customize(getOrApply(new ServletApiConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Enables CSRF protection. This is activated by default when using\n\t * {@link EnableWebSecurity}. You can disable it using:\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class CsrfSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.csrf((csrf) -&gt; csrf.disable());\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param csrfCustomizer the {@link Customizer} to provide more options for the\n\t * {@link CsrfConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity csrf(Customizer<CsrfConfigurer<HttpSecurity>> csrfCustomizer) {\n\t\tApplicationContext context = getContext();\n\t\tcsrfCustomizer.customize(getOrApply(new CsrfConfigurer<>(context)));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Provides logout support. This is automatically applied when using\n\t * {@link EnableWebSecurity}. The default is that accessing the URL \"/logout\" will log\n\t * the user out by invalidating the HTTP Session, cleaning up any\n\t * {@link #rememberMe(Customizer)} authentication that was configured, clearing the\n\t * {@link SecurityContextHolder}, and then redirect to \"/login?success\".\n\t *\n\t * <h2>Example Custom Configuration</h2>\n\t *\n\t * The following customization to log out when the URL \"/custom-logout\" is invoked.\n\t * Log out will remove the cookie named \"remove\", not invalidate the HttpSession,\n\t * clear the SecurityContextHolder, and upon completion redirect to \"/logout-success\".\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class LogoutSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults())\n\t * \t\t\t// sample logout customization\n\t * \t\t\t.logout((logout) -&gt;\n\t * \t\t\t\tlogout.deleteCookies(&quot;remove&quot;)\n\t * \t\t\t\t\t.invalidateHttpSession(false)\n\t * \t\t\t\t\t.logoutUrl(&quot;/custom-logout&quot;)\n\t * \t\t\t\t\t.logoutSuccessUrl(&quot;/logout-success&quot;)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param logoutCustomizer the {@link Customizer} to provide more options for the\n\t * {@link LogoutConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity logout(Customizer<LogoutConfigurer<HttpSecurity>> logoutCustomizer) {\n\t\tlogoutCustomizer.customize(getOrApply(new LogoutConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Allows configuring how an anonymous user is represented. This is automatically\n\t * applied when used in conjunction with {@link EnableWebSecurity}. By default\n\t * anonymous users will be represented with an\n\t * {@link org.springframework.security.authentication.AnonymousAuthenticationToken}\n\t * and contain the role \"ROLE_ANONYMOUS\".\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following configuration demonstrates how to specify that anonymous users should\n\t * contain the role \"ROLE_ANON\" instead.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class AnonymousSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults())\n\t * \t\t\t// sample anonymous customization\n\t * \t\t\t.anonymous((anonymous) -&gt;\n\t * \t\t\t\tanonymous\n\t * \t\t\t\t\t.authorities(&quot;ROLE_ANON&quot;)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * The following demonstrates how to represent anonymous users as null. Note that this\n\t * can cause {@link NullPointerException} in code that assumes anonymous\n\t * authentication is enabled.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class AnonymousSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults())\n\t * \t\t\t// sample anonymous customization\n\t * \t\t\t.anonymous((anonymous) -&gt;\n\t * \t\t\t\tanonymous.disable()\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param anonymousCustomizer the {@link Customizer} to provide more options for the\n\t * {@link AnonymousConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity anonymous(Customizer<AnonymousConfigurer<HttpSecurity>> anonymousCustomizer) {\n\t\tanonymousCustomizer.customize(getOrApply(new AnonymousConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Specifies to support form based authentication. If\n\t * {@link FormLoginConfigurer#loginPage(String)} is not specified a default login page\n\t * will be generated.\n\t *\n\t * <h2>Example Configurations</h2>\n\t *\n\t * The most basic configuration defaults to automatically generating a login page at\n\t * the URL \"/login\", redirecting to \"/login?error\" for authentication failure. The\n\t * details of the login page can be found on\n\t * {@link FormLoginConfigurer#loginPage(String)}\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class FormLoginSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * The configuration below demonstrates customizing the defaults.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class FormLoginSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin((formLogin) -&gt;\n\t * \t\t\t\tformLogin\n\t * \t\t\t\t\t.usernameParameter(&quot;username&quot;)\n\t * \t\t\t\t\t.passwordParameter(&quot;password&quot;)\n\t * \t\t\t\t\t.loginPage(&quot;/authentication/login&quot;)\n\t * \t\t\t\t\t.failureUrl(&quot;/authentication/login?failed&quot;)\n\t * \t\t\t\t\t.loginProcessingUrl(&quot;/authentication/login/process&quot;)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param formLoginCustomizer the {@link Customizer} to provide more options for the\n\t * {@link FormLoginConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @see FormLoginConfigurer#loginPage(String)\n\t */\n\tpublic HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) {\n\t\tformLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures authentication support using an SAML 2.0 Service Provider. <br>\n\t * <br>\n\t *\n\t * The &quot;authentication flow&quot; is implemented using the <b>Web Browser SSO\n\t * Profile, using POST and REDIRECT bindings</b>, as documented in the\n\t * <a target=\"_blank\" href=\"https://docs.oasis-open.org/security/saml/\">SAML V2.0\n\t * Core,Profiles and Bindings</a> specifications. <br>\n\t * <br>\n\t *\n\t * As a prerequisite to using this feature, is that you have a SAML v2.0 Identity\n\t * Provider to provide an assertion. The representation of the Service Provider, the\n\t * relying party, and the remote Identity Provider, the asserting party is contained\n\t * within {@link RelyingPartyRegistration}. <br>\n\t * <br>\n\t *\n\t * {@link RelyingPartyRegistration}(s) are composed within a\n\t * {@link RelyingPartyRegistrationRepository}, which is <b>required</b> and must be\n\t * registered with the {@link ApplicationContext} or configured via\n\t * <code>saml2Login().relyingPartyRegistrationRepository(..)</code>. <br>\n\t * <br>\n\t *\n\t * The default configuration provides an auto-generated login page at\n\t * <code>&quot;/login&quot;</code> and redirects to\n\t * <code>&quot;/login?error&quot;</code> when an authentication error occurs. The\n\t * login page will display each of the identity providers with a link that is capable\n\t * of initiating the &quot;authentication flow&quot;. <br>\n\t * <br>\n\t *\n\t * <p>\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following example shows the minimal configuration required, using SimpleSamlPhp\n\t * as the Authentication Provider.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class Saml2LoginSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.anyRequest().authenticated()\n\t * \t\t\t)\n\t * \t\t\t.saml2Login(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t *\t&#064;Bean\n\t *\tpublic RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {\n\t *\t\treturn new InMemoryRelyingPartyRegistrationRepository(this.getSaml2RelyingPartyRegistration());\n\t *\t}\n\t *\n\t * \tprivate RelyingPartyRegistration getSaml2RelyingPartyRegistration() {\n\t * \t\t//remote IDP entity ID\n\t * \t\tString idpEntityId = \"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\";\n\t * \t\t//remote WebSSO Endpoint - Where to Send AuthNRequests to\n\t * \t\tString webSsoEndpoint = \"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\";\n\t * \t\t//local registration ID\n\t * \t\tString registrationId = \"simplesamlphp\";\n\t * \t\t//local entity ID - autogenerated based on URL\n\t * \t\tString localEntityIdTemplate = \"{baseUrl}/saml2/service-provider-metadata/{registrationId}\";\n\t * \t\t//local signing (and decryption key)\n\t * \t\tSaml2X509Credential signingCredential = getSigningCredential();\n\t * \t\t//IDP certificate for verification of incoming messages\n\t * \t\tSaml2X509Credential idpVerificationCertificate = getVerificationCertificate();\n\t * \t\treturn RelyingPartyRegistration.withRegistrationId(registrationId)\n\t * \t\t\t\t.remoteIdpEntityId(idpEntityId)\n\t * \t\t\t\t.idpWebSsoUrl(webSsoEndpoint)\n\t * \t\t\t\t.credential(signingCredential)\n\t * \t\t\t\t.credential(idpVerificationCertificate)\n\t * \t\t\t\t.localEntityIdTemplate(localEntityIdTemplate)\n\t * \t\t\t\t.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * <p>\n\t * @param saml2LoginCustomizer the {@link Customizer} to provide more options for the\n\t * {@link Saml2LoginConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @since 5.2\n\t */\n\tpublic HttpSecurity saml2Login(Customizer<Saml2LoginConfigurer<HttpSecurity>> saml2LoginCustomizer) {\n\t\tsaml2LoginCustomizer.customize(getOrApply(new Saml2LoginConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures logout support for an SAML 2.0 Relying Party. <br>\n\t * <br>\n\t *\n\t * Implements the <b>Single Logout Profile, using POST and REDIRECT bindings</b>, as\n\t * documented in the\n\t * <a target=\"_blank\" href=\"https://docs.oasis-open.org/security/saml/\">SAML V2.0\n\t * Core, Profiles and Bindings</a> specifications. <br>\n\t * <br>\n\t *\n\t * As a prerequisite to using this feature, is that you have a SAML v2.0 Asserting\n\t * Party to sent a logout request to. The representation of the relying party and the\n\t * asserting party is contained within {@link RelyingPartyRegistration}. <br>\n\t * <br>\n\t *\n\t * {@link RelyingPartyRegistration}(s) are composed within a\n\t * {@link RelyingPartyRegistrationRepository}, which is <b>required</b> and must be\n\t * registered with the {@link ApplicationContext} or configured via\n\t * {@link #saml2Login(Customizer)}.<br>\n\t * <br>\n\t *\n\t * The default configuration provides an auto-generated logout endpoint at\n\t * <code>&quot;/logout&quot;</code> and redirects to <code>/login?logout</code> when\n\t * logout completes. <br>\n\t * <br>\n\t *\n\t * <p>\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following example shows the minimal configuration required, using a\n\t * hypothetical asserting party.\n\t *\n\t * <pre>\n\t *\t&#064;EnableWebSecurity\n\t *\t&#064;Configuration\n\t *\tpublic class Saml2LogoutSecurityConfig {\n\t *\t\t&#064;Bean\n\t *\t\tpublic SecurityFilterChain web(HttpSecurity http)  {\n\t *\t\t\thttp\n\t *\t\t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n\t *\t\t\t\t\t.anyRequest().authenticated()\n\t *\t\t\t\t)\n\t *\t\t\t\t.saml2Login(withDefaults())\n\t *\t\t\t\t.saml2Logout(withDefaults());\n\t *\t\t\treturn http.build();\n\t *\t\t}\n\t *\n\t *\t\t&#064;Bean\n\t *\t\tpublic RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {\n\t *\t\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t *\t\t\t\t\t.withMetadataLocation(\"https://ap.example.org/metadata\")\n\t *\t\t\t\t\t.registrationId(\"simple\")\n\t *\t\t\t\t\t.build();\n\t *\t\t\treturn new InMemoryRelyingPartyRegistrationRepository(registration);\n\t *\t\t}\n\t *\t}\n\t * </pre>\n\t *\n\t * <p>\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @since 5.6\n\t */\n\tpublic HttpSecurity saml2Logout(Customizer<Saml2LogoutConfigurer<HttpSecurity>> saml2LogoutCustomizer) {\n\t\tsaml2LogoutCustomizer.customize(getOrApply(new Saml2LogoutConfigurer<>(getContext())));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures a SAML 2.0 metadata endpoint that presents relying party configurations\n\t * in an {@code <md:EntityDescriptor>} payload.\n\t *\n\t * <p>\n\t * By default, the endpoints are {@code /saml2/metadata} and\n\t * {@code /saml2/metadata/{registrationId}} though note that also\n\t * {@code /saml2/service-provider-metadata/{registrationId}} is recognized for\n\t * backward compatibility purposes.\n\t *\n\t * <p>\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following example shows the minimal configuration required, using a\n\t * hypothetical asserting party.\n\t *\n\t * <pre>\n\t *\t&#064;EnableWebSecurity\n\t *\t&#064;Configuration\n\t *\tpublic class Saml2LogoutSecurityConfig {\n\t *\t\t&#064;Bean\n\t *\t\tpublic SecurityFilterChain web(HttpSecurity http)  {\n\t *\t\t\thttp\n\t *\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t *\t\t\t\t.saml2Metadata(Customizer.withDefaults());\n\t *\t\t\treturn http.build();\n\t *\t\t}\n\t *\n\t *\t\t&#064;Bean\n\t *\t\tpublic RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {\n\t *\t\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t *\t\t\t\t\t.withMetadataLocation(\"https://ap.example.org/metadata\")\n\t *\t\t\t\t\t.registrationId(\"simple\")\n\t *\t\t\t\t\t.build();\n\t *\t\t\treturn new InMemoryRelyingPartyRegistrationRepository(registration);\n\t *\t\t}\n\t *\t}\n\t * </pre>\n\t * @param saml2MetadataConfigurer the {@link Customizer} to provide more options for\n\t * the {@link Saml2MetadataConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @since 6.1\n\t */\n\tpublic HttpSecurity saml2Metadata(Customizer<Saml2MetadataConfigurer<HttpSecurity>> saml2MetadataConfigurer) {\n\t\tsaml2MetadataConfigurer.customize(getOrApply(new Saml2MetadataConfigurer<>(getContext())));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0\n\t * Provider. <br>\n\t * <br>\n\t *\n\t * The &quot;authentication flow&quot; is implemented using the <b>Authorization Code\n\t * Grant</b>, as specified in the\n\t * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">OAuth 2.0\n\t * Authorization Framework</a> and <a target=\"_blank\" href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth\">OpenID Connect\n\t * Core 1.0</a> specification. <br>\n\t * <br>\n\t *\n\t * As a prerequisite to using this feature, you must register a client with a\n\t * provider. The client registration information may than be used for configuring a\n\t * {@link org.springframework.security.oauth2.client.registration.ClientRegistration}\n\t * using a\n\t * {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder}.\n\t * <br>\n\t * <br>\n\t *\n\t * {@link org.springframework.security.oauth2.client.registration.ClientRegistration}(s)\n\t * are composed within a\n\t * {@link org.springframework.security.oauth2.client.registration.ClientRegistrationRepository},\n\t * which is <b>required</b> and must be registered with the {@link ApplicationContext}\n\t * or configured via <code>oauth2Login().clientRegistrationRepository(..)</code>. <br>\n\t * <br>\n\t *\n\t * The default configuration provides an auto-generated login page at\n\t * <code>&quot;/login&quot;</code> and redirects to\n\t * <code>&quot;/login?error&quot;</code> when an authentication error occurs. The\n\t * login page will display each of the clients with a link that is capable of\n\t * initiating the &quot;authentication flow&quot;. <br>\n\t * <br>\n\t *\n\t * <p>\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following example shows the minimal configuration required, using Google as the\n\t * Authentication Provider.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class OAuth2LoginSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.anyRequest().authenticated()\n\t * \t\t\t)\n\t * \t\t\t.oauth2Login(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t *\t&#064;Bean\n\t *\tpublic ClientRegistrationRepository clientRegistrationRepository() {\n\t *\t\treturn new InMemoryClientRegistrationRepository(this.googleClientRegistration());\n\t *\t}\n\t *\n\t * \tprivate ClientRegistration googleClientRegistration() {\n\t * \t\treturn ClientRegistration.withRegistrationId(\"google\")\n\t * \t\t\t.clientId(\"google-client-id\")\n\t * \t\t\t.clientSecret(\"google-client-secret\")\n\t * \t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t * \t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t * \t\t\t.redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n\t * \t\t\t.scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n\t * \t\t\t.authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n\t * \t\t\t.tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n\t * \t\t\t.userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n\t * \t\t\t.userNameAttributeName(IdTokenClaimNames.SUB)\n\t * \t\t\t.jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n\t * \t\t\t.clientName(\"Google\")\n\t * \t\t\t.build();\n\t *\t}\n\t * }\n\t * </pre>\n\t *\n\t * <p>\n\t * For more advanced configuration, see {@link OAuth2LoginConfigurer} for available\n\t * options to customize the defaults.\n\t * @param oauth2LoginCustomizer the {@link Customizer} to provide more options for the\n\t * {@link OAuth2LoginConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @see\n\t * <a target=\"_blank\" href= \"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n\t * 4.1 Authorization Code Grant</a>\n\t * @see <a target=\"_blank\" href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth\">Section 3.1\n\t * Authorization Code Flow</a>\n\t * @see org.springframework.security.oauth2.client.registration.ClientRegistration\n\t * @see org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\n\t */\n\tpublic HttpSecurity oauth2Login(Customizer<OAuth2LoginConfigurer<HttpSecurity>> oauth2LoginCustomizer) {\n\t\toauth2LoginCustomizer.customize(getOrApply(new OAuth2LoginConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\tpublic OidcLogoutConfigurer<HttpSecurity> oidcLogout() {\n\t\treturn getOrApply(new OidcLogoutConfigurer<>());\n\t}\n\n\tpublic HttpSecurity oidcLogout(Customizer<OidcLogoutConfigurer<HttpSecurity>> oidcLogoutCustomizer) {\n\t\toidcLogoutCustomizer.customize(getOrApply(new OidcLogoutConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures OAuth 2.0 Client support.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following example demonstrates how to enable OAuth 2.0 Client support for all\n\t * endpoints.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class OAuth2ClientSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.anyRequest().authenticated()\n\t * \t\t\t)\n\t * \t\t\t.oauth2Client(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param oauth2ClientCustomizer the {@link Customizer} to provide more options for\n\t * the {@link OAuth2ClientConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @see\n\t * <a target=\"_blank\" href= \"https://tools.ietf.org/html/rfc6749#section-1.1\">OAuth\n\t * 2.0 Authorization Framework</a>\n\t */\n\tpublic HttpSecurity oauth2Client(Customizer<OAuth2ClientConfigurer<HttpSecurity>> oauth2ClientCustomizer) {\n\t\toauth2ClientCustomizer.customize(getOrApply(new OAuth2ClientConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures OAuth 2.0 Resource Server support.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The following example demonstrates how to configure a custom JWT authentication\n\t * converter.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class OAuth2ResourceServerSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.anyRequest().authenticated()\n\t * \t\t\t)\n\t * \t\t\t.oauth2ResourceServer((oauth2ResourceServer) -&gt;\n\t * \t\t\t\toauth2ResourceServer\n\t * \t\t\t\t\t.jwt((jwt) -&gt;\n\t * \t\t\t\t\t\tjwt\n\t * \t\t\t\t\t\t\t.decoder(jwtDecoder())\n\t * \t\t\t\t\t)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic JwtDecoder jwtDecoder() {\n\t * \t\treturn NimbusJwtDecoder.withPublicKey(this.key).build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param oauth2ResourceServerCustomizer the {@link Customizer} to provide more\n\t * options for the {@link OAuth2ResourceServerConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @see\n\t * <a target=\"_blank\" href= \"https://tools.ietf.org/html/rfc6749#section-1.1\">OAuth\n\t * 2.0 Authorization Framework</a>\n\t */\n\tpublic HttpSecurity oauth2ResourceServer(\n\t\t\tCustomizer<OAuth2ResourceServerConfigurer<HttpSecurity>> oauth2ResourceServerCustomizer) {\n\t\tOAuth2ResourceServerConfigurer<HttpSecurity> configurer = getOrApply(\n\t\t\t\tnew OAuth2ResourceServerConfigurer<>(getContext()));\n\t\tthis.postProcess(configurer);\n\t\toauth2ResourceServerCustomizer.customize(configurer);\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures OAuth 2.1 Authorization Server support.\n\t * @param oauth2AuthorizationServerCustomizer the {@link Customizer} providing access\n\t * to the {@link OAuth2AuthorizationServerConfigurer} for further customizations\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @since 7.0\n\t * @see <a target=\"_blank\" href=\n\t * \"https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-13.html\">OAuth 2.1\n\t * Authorization Framework</a>\n\t */\n\tpublic HttpSecurity oauth2AuthorizationServer(\n\t\t\tCustomizer<OAuth2AuthorizationServerConfigurer> oauth2AuthorizationServerCustomizer) {\n\t\toauth2AuthorizationServerCustomizer.customize(getOrApply(new OAuth2AuthorizationServerConfigurer()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures One-Time Token Login Support.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class SecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n\t * \t\t\t\t\t.anyRequest().authenticated()\n\t * \t\t\t)\n\t * \t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic OneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler() {\n\t * \t\treturn new MyMagicLinkOneTimeTokenGenerationSuccessHandler();\n\t * \t}\n\t *\n\t * }\n\t * </pre>\n\t * @param oneTimeTokenLoginConfigurerCustomizer the {@link Customizer} to provide more\n\t * options for the {@link OneTimeTokenLoginConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity oneTimeTokenLogin(\n\t\t\tCustomizer<OneTimeTokenLoginConfigurer<HttpSecurity>> oneTimeTokenLoginConfigurerCustomizer) {\n\t\toneTimeTokenLoginConfigurerCustomizer.customize(getOrApply(new OneTimeTokenLoginConfigurer<>(getContext())));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures channel security. In order for this configuration to be useful at least\n\t * one mapping to a required channel must be provided.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The example below demonstrates how to require HTTPs for every request. Only\n\t * requiring HTTPS for some requests is supported, but not recommended since an\n\t * application that allows for HTTP introduces many security vulnerabilities. For one\n\t * such example, read about\n\t * <a href=\"https://en.wikipedia.org/wiki/Firesheep\">Firesheep</a>.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class ChannelSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults())\n\t * \t\t\t.requiresChannel((requiresChannel) -&gt;\n\t * \t\t\t\trequiresChannel\n\t * \t\t\t\t\t.anyRequest().requiresSecure()\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param requiresChannelCustomizer the {@link Customizer} to provide more options for\n\t * the {@link ChannelSecurityConfigurer.ChannelRequestMatcherRegistry}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @deprecated Use {@link #redirectToHttps}\n\t */\n\t@Deprecated\n\tpublic HttpSecurity requiresChannel(\n\t\t\tCustomizer<ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry> requiresChannelCustomizer) {\n\t\tApplicationContext context = getContext();\n\t\trequiresChannelCustomizer.customize(getOrApply(new ChannelSecurityConfigurer<>(context)).getRegistry());\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures channel security. In order for this configuration to be useful at least\n\t * one mapping to a required channel must be provided.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The example below demonstrates how to require HTTPS for every request. Only\n\t * requiring HTTPS for some requests is supported, for example if you need to\n\t * differentiate between local and production deployments.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class RequireHttpsConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n\t * \t\t\t\tanyRequest().authenticated()\n\t * \t\t\t)\n\t * \t\t\t.formLogin(withDefaults())\n\t * \t\t\t.redirectToHttps(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param httpsRedirectConfigurerCustomizer the {@link Customizer} to provide more\n\t * options for the {@link HttpsRedirectConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tpublic HttpSecurity redirectToHttps(\n\t\t\tCustomizer<HttpsRedirectConfigurer<HttpSecurity>> httpsRedirectConfigurerCustomizer) {\n\t\thttpsRedirectConfigurerCustomizer.customize(getOrApply(new HttpsRedirectConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configures HTTP Basic authentication.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * The example below demonstrates how to configure HTTP Basic authentication for an\n\t * application. The default realm is \"Realm\", but can be customized using\n\t * {@link HttpBasicConfigurer#realmName(String)}.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class HttpBasicSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests((authorizeHttpRequests) -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.httpBasic(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param httpBasicCustomizer the {@link Customizer} to provide more options for the\n\t * {@link HttpBasicConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity httpBasic(Customizer<HttpBasicConfigurer<HttpSecurity>> httpBasicCustomizer) {\n\t\thttpBasicCustomizer.customize(getOrApply(new HttpBasicConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Adds support for the password management.\n\t *\n\t * <h2>Example Configuration</h2> The example below demonstrates how to configure\n\t * password management for an application. The default change password page is\n\t * \"/change-password\", but can be customized using\n\t * {@link PasswordManagementConfigurer#changePasswordPage(String)}.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class PasswordManagementSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.authorizeHttpRequests(authorizeHttpRequests -&gt;\n\t * \t\t\t\tauthorizeHttpRequests\n\t * \t\t\t\t\t.requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.passwordManagement(passwordManagement -&gt;\n\t * \t\t\t\tpasswordManagement\n\t * \t\t\t\t\t.changePasswordPage(&quot;/custom-change-password-page&quot;)\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param passwordManagementCustomizer the {@link Customizer} to provide more options\n\t * for the {@link PasswordManagementConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @ @since 5.6\n\t */\n\tpublic HttpSecurity passwordManagement(\n\t\t\tCustomizer<PasswordManagementConfigurer<HttpSecurity>> passwordManagementCustomizer) {\n\t\tpasswordManagementCustomizer.customize(getOrApply(new PasswordManagementConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Configure the default {@link AuthenticationManager}.\n\t * @param authenticationManager the {@link AuthenticationManager} to use\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @since 5.6\n\t */\n\tpublic HttpSecurity authenticationManager(AuthenticationManager authenticationManager) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\treturn HttpSecurity.this;\n\t}\n\n\t@Override\n\tpublic <C> void setSharedObject(Class<C> sharedType, C object) {\n\t\tsuper.setSharedObject(sharedType, object);\n\t}\n\n\t@Override\n\tprotected void beforeConfigure() {\n\t\tif (this.authenticationManager != null) {\n\t\t\tsetSharedObject(AuthenticationManager.class, this.authenticationManager);\n\t\t}\n\t\telse {\n\t\t\tObjectPostProcessor<AuthenticationManager> postProcessor = getAuthenticationManagerPostProcessor();\n\t\t\tAuthenticationManager manager = getAuthenticationRegistry().build();\n\t\t\tif (manager != null) {\n\t\t\t\tsetSharedObject(AuthenticationManager.class, postProcessor.postProcess(manager));\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tprotected DefaultSecurityFilterChain performBuild() {\n\t\tthis.filters.sort(OrderComparator.INSTANCE);\n\t\tList<Filter> sortedFilters = new ArrayList<>(this.filters.size());\n\t\tfor (Filter filter : this.filters) {\n\t\t\tsortedFilters.add(((OrderedFilter) filter).filter);\n\t\t}\n\t\treturn new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);\n\t}\n\n\t@Override\n\tpublic HttpSecurity authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tgetAuthenticationRegistry().authenticationProvider(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic HttpSecurity userDetailsService(UserDetailsService userDetailsService) {\n\t\tgetAuthenticationRegistry().userDetailsService(userDetailsService);\n\t\treturn this;\n\t}\n\n\tprivate AuthenticationManagerBuilder getAuthenticationRegistry() {\n\t\treturn getSharedObject(AuthenticationManagerBuilder.class);\n\t}\n\n\t@Override\n\tpublic HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {\n\t\treturn addFilterAtOffsetOf(filter, 1, afterFilter);\n\t}\n\n\t@Override\n\tpublic HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) {\n\t\treturn addFilterAtOffsetOf(filter, -1, beforeFilter);\n\t}\n\n\tprivate HttpSecurity addFilterAtOffsetOf(Filter filter, int offset, Class<? extends Filter> registeredFilter) {\n\t\tInteger registeredFilterOrder = this.filterOrders.getOrder(registeredFilter);\n\t\tif (registeredFilterOrder == null) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"The Filter class \" + registeredFilter.getName() + \" does not have a registered order\");\n\t\t}\n\t\tint order = registeredFilterOrder + offset;\n\t\tthis.filters.add(new OrderedFilter(filter, order));\n\t\tthis.filterOrders.put(filter.getClass(), order);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic HttpSecurity addFilter(Filter filter) {\n\t\tInteger order = this.filterOrders.getOrder(filter.getClass());\n\t\tif (order == null) {\n\t\t\tthrow new IllegalArgumentException(\"The Filter class \" + filter.getClass().getName()\n\t\t\t\t\t+ \" does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.\");\n\t\t}\n\t\tthis.filters.add(new OrderedFilter(filter, order));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds the Filter at the location of the specified Filter class. For example, if you\n\t * want the filter CustomFilter to be registered in the same position as\n\t * {@link UsernamePasswordAuthenticationFilter}, you can invoke:\n\t *\n\t * <pre>\n\t * addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)\n\t * </pre>\n\t *\n\t * Registration of multiple Filters in the same location means their ordering is not\n\t * deterministic. More concretely, registering multiple Filters in the same location\n\t * does not override existing Filters. Instead, do not register Filters you do not\n\t * want to use.\n\t * @param filter the Filter to register\n\t * @param atFilter the location of another {@link Filter} that is already registered\n\t * (i.e. known) with Spring Security.\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tpublic HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {\n\t\treturn addFilterAtOffsetOf(filter, 0, atFilter);\n\t}\n\n\t/**\n\t * Allows specifying which {@link HttpServletRequest} instances this\n\t * {@link HttpSecurity} will be invoked on. This method allows for easily invoking the\n\t * {@link HttpSecurity} for multiple different {@link RequestMatcher} instances. If\n\t * only a single {@link RequestMatcher} is necessary consider using\n\t * {@link #securityMatcher(String...)}, or {@link #securityMatcher(RequestMatcher)}.\n\t *\n\t * <p>\n\t * Invoking {@link #securityMatchers(Customizer)} will not override previous\n\t * invocations of {@link #securityMatchers(Customizer)}\n\t * {@link #securityMatcher(String...)} and {@link #securityMatcher(RequestMatcher)}\n\t * </p>\n\t *\n\t * <h3>Example Configurations</h3>\n\t *\n\t * The following configuration enables the {@link HttpSecurity} for URLs that begin\n\t * with \"/api/\" or \"/oauth/\".\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class RequestMatchersSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.securityMatchers((matchers) -&gt; matchers\n\t * \t\t\t\t.requestMatchers(&quot;/api/**&quot;, &quot;/oauth/**&quot;)\n\t * \t\t\t)\n\t * \t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n\t * \t\t\t\t.anyRequest().hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.httpBasic(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * The configuration below is the same as the previous configuration.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class RequestMatchersSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.securityMatchers((matchers) -&gt; matchers\n\t * \t\t\t\t.requestMatchers(&quot;/api/**&quot;)\n\t * \t\t\t\t.requestMatchers(&quot;/oauth/**&quot;)\n\t * \t\t\t)\n\t * \t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n\t * \t\t\t\t.anyRequest().hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.httpBasic(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t *\n\t * The configuration below is also the same as the above configuration.\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebSecurity\n\t * public class RequestMatchersSecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t.securityMatchers((matchers) -&gt; matchers\n\t * \t\t\t\t.requestMatchers(&quot;/api/**&quot;)\n\t * \t\t\t)\n\t *\t\t\t.securityMatchers((matchers) -&gt; matchers\n\t *\t\t\t\t.requestMatchers(&quot;/oauth/**&quot;)\n\t * \t\t\t)\n\t * \t\t\t.authorizeHttpRequests((authorize) -&gt; authorize\n\t * \t\t\t\t.anyRequest().hasRole(&quot;USER&quot;)\n\t * \t\t\t)\n\t * \t\t\t.httpBasic(withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic UserDetailsService userDetailsService() {\n\t * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t * \t\t\t.username(&quot;user&quot;)\n\t * \t\t\t.password(&quot;password&quot;)\n\t * \t\t\t.roles(&quot;USER&quot;)\n\t * \t\t\t.build();\n\t * \t\treturn new InMemoryUserDetailsManager(user);\n\t * \t}\n\t * }\n\t * </pre>\n\t * @param requestMatcherCustomizer the {@link Customizer} to provide more options for\n\t * the {@link RequestMatcherConfigurer}\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tpublic HttpSecurity securityMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) {\n\t\trequestMatcherCustomizer.customize(this.requestMatcherConfigurer);\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * Allows configuring the {@link HttpSecurity} to only be invoked when matching the\n\t * provided {@link RequestMatcher}. If more advanced configuration is necessary,\n\t * consider using {@link #securityMatchers(Customizer)} ()}.\n\t *\n\t * <p>\n\t * Invoking {@link #securityMatcher(RequestMatcher)} will override previous\n\t * invocations of {@link #securityMatcher(RequestMatcher)},\n\t * {@link #securityMatcher(String...)} and {@link #securityMatchers(Customizer)}\n\t * </p>\n\t * @param requestMatcher the {@link RequestMatcher} to use, for example,\n\t * {@code PathPatternRequestMatcher.pathPattern(HttpMethod.GET, \"/admin/**\")}\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @see #securityMatcher(String...)\n\t */\n\tpublic HttpSecurity securityMatcher(RequestMatcher requestMatcher) {\n\t\tthis.requestMatcher = requestMatcher;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows configuring the {@link HttpSecurity} to only be invoked when matching the\n\t * provided set of {@code patterns}. See\n\t * {@link org.springframework.web.util.pattern.PathPattern} for matching rules\n\t *\n\t * <p>\n\t * Invoking {@link #securityMatcher(String...)} will override previous invocations of\n\t * {@link #securityMatcher(String...)}, {@link #securityMatcher(RequestMatcher)} and\n\t * {@link #securityMatchers(Customizer)}.\n\t * </p>\n\t * @param patterns the pattern to match on (i.e. \"/admin/**\")\n\t * @return the {@link HttpSecurity} for further customizations\n\t * @see org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher\n\t * @see org.springframework.web.util.pattern.PathPattern\n\t */\n\tpublic HttpSecurity securityMatcher(String... patterns) {\n\t\tList<RequestMatcher> matchers = new ArrayList<>();\n\t\tPathPatternRequestMatcher.Builder builder = getSharedObject(PathPatternRequestMatcher.Builder.class);\n\t\tfor (String pattern : patterns) {\n\t\t\tmatchers.add(builder.matcher(pattern));\n\t\t}\n\t\tthis.requestMatcher = new OrRequestMatcher(matchers);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies webAuthn/passkeys based authentication.\n\t *\n\t * <pre>\n\t * \t&#064;Bean\n\t * \tSecurityFilterChain securityFilterChain(HttpSecurity http)  {\n\t * \t\thttp\n\t * \t\t\t// ...\n\t * \t\t\t.webAuthn((webAuthn) -&gt; webAuthn\n\t * \t\t\t\t.rpId(\"example.com\")\n\t * \t\t\t\t.allowedOrigins(\"https://example.com\")\n\t * \t\t\t);\n\t * \t\treturn http.build();\n\t * \t}\n\t * </pre>\n\t * @param webAuthn the customizer to apply\n\t * @return the {@link HttpSecurity} for further customizations @\n\t */\n\tpublic HttpSecurity webAuthn(Customizer<WebAuthnConfigurer<HttpSecurity>> webAuthn) {\n\t\twebAuthn.customize(getOrApply(new WebAuthnConfigurer<>()));\n\t\treturn HttpSecurity.this;\n\t}\n\n\t/**\n\t * If the {@link SecurityConfigurer} has already been specified get the original,\n\t * otherwise apply the new {@link SecurityConfigurerAdapter}.\n\t * @param configurer the {@link SecurityConfigurer} to apply if one is not found for\n\t * this {@link SecurityConfigurer} class.\n\t * @return the current {@link SecurityConfigurer} for the configurer passed in @\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer) {\n\t\tC existingConfig = (C) getConfigurer(configurer.getClass());\n\t\tif (existingConfig != null) {\n\t\t\treturn existingConfig;\n\t\t}\n\t\twith(configurer);\n\t\treturn configurer;\n\t}\n\n\tprivate ObjectPostProcessor<AuthenticationManager> getAuthenticationManagerPostProcessor() {\n\t\tApplicationContext context = getContext();\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,\n\t\t\t\tAuthenticationManager.class);\n\t\tObjectProvider<ObjectPostProcessor<AuthenticationManager>> manager = context.getBeanProvider(type);\n\t\treturn manager.getIfUnique(ObjectPostProcessor::identity);\n\t}\n\n\t/**\n\t * Allows mapping HTTP requests that this {@link HttpSecurity} will be used for\n\t *\n\t * @author Rob Winch\n\t * @since 3.2\n\t */\n\tpublic class RequestMatcherConfigurer extends AbstractRequestMatcherRegistry<RequestMatcherConfigurer> {\n\n\t\tprotected List<RequestMatcher> matchers = new ArrayList<>();\n\n\t\tRequestMatcherConfigurer(ApplicationContext context) {\n\t\t\tsetApplicationContext(context);\n\t\t}\n\n\t\t@Override\n\t\tprotected RequestMatcherConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {\n\t\t\tsetMatchers(requestMatchers);\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate void setMatchers(List<? extends RequestMatcher> requestMatchers) {\n\t\t\tthis.matchers.addAll(requestMatchers);\n\t\t\tsecurityMatcher(new OrRequestMatcher(this.matchers));\n\t\t}\n\n\t}\n\n\t/**\n\t * A Filter that implements Ordered to be sorted. After sorting occurs, the original\n\t * filter is what is used by FilterChainProxy\n\t */\n\tprivate static final class OrderedFilter implements Ordered, Filter {\n\n\t\tprivate final Filter filter;\n\n\t\tprivate final int order;\n\n\t\tprivate OrderedFilter(Filter filter, int order) {\n\t\t\tthis.filter = filter;\n\t\t\tthis.order = order;\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tthis.filter.doFilter(servletRequest, servletResponse, filterChain);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getOrder() {\n\t\t\treturn this.order;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"OrderedFilter{\" + \"filter=\" + this.filter + \", order=\" + this.order + '}';\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.expression.AbstractSecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionOperations;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.SingleResultAuthorizationManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;\nimport org.springframework.security.config.annotation.SecurityBuilder;\nimport org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;\nimport org.springframework.security.config.annotation.web.WebSecurityConfigurer;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.FilterChainProxy.FilterChainDecorator;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer;\nimport org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.PathPatternRequestTransformer;\nimport org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.web.debug.DebugFilter;\nimport org.springframework.security.web.firewall.CompositeRequestRejectedHandler;\nimport org.springframework.security.web.firewall.HttpFirewall;\nimport org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler;\nimport org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler;\nimport org.springframework.security.web.firewall.RequestRejectedHandler;\nimport org.springframework.security.web.firewall.StrictHttpFirewall;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.web.context.ServletContextAware;\nimport org.springframework.web.filter.DelegatingFilterProxy;\n\n/**\n * <p>\n * The {@link WebSecurity} is created by {@link WebSecurityConfiguration} to create the\n * {@link FilterChainProxy} known as the Spring Security Filter Chain\n * (springSecurityFilterChain). The springSecurityFilterChain is the {@link Filter} that\n * the {@link DelegatingFilterProxy} delegates to.\n * </p>\n *\n * <p>\n * Customizations to the {@link WebSecurity} can be made by creating a\n * {@link WebSecurityConfigurer} or exposing a {@link WebSecurityCustomizer} bean.\n * </p>\n *\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 3.2\n * @see EnableWebSecurity\n * @see WebSecurityConfiguration\n */\npublic final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>\n\t\timplements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {\n\n\tprivate static final boolean USING_ACCESS = ClassUtils\n\t\t.isPresent(\"org.springframework.security.access.SecurityConfig\", null);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final List<RequestMatcher> ignoredRequests = new ArrayList<>();\n\n\tprivate final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<>();\n\n\tprivate IgnoredRequestConfigurer ignoredRequestRegistry;\n\n\tprivate HttpFirewall httpFirewall;\n\n\tprivate RequestRejectedHandler requestRejectedHandler;\n\n\tprivate boolean debugEnabled;\n\n\tprivate WebInvocationPrivilegeEvaluator privilegeEvaluator;\n\n\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\tprivate ObjectPostProcessor<FilterChainDecorator> filterChainDecoratorPostProcessor = ObjectPostProcessor\n\t\t.identity();\n\n\tprivate HttpServletRequestTransformer privilegeEvaluatorRequestTransformer;\n\n\tprivate DefaultHttpSecurityExpressionHandler defaultExpressionHandler = new DefaultHttpSecurityExpressionHandler();\n\n\tprivate SecurityExpressionHandler<FilterInvocation> expressionHandler = new SecurityExpressionHandlerAdapter(\n\t\t\tthis.defaultExpressionHandler);\n\n\tprivate Runnable postBuildAction = () -> {\n\t};\n\n\tprivate ServletContext servletContext;\n\n\t/**\n\t * Creates a new instance\n\t * @param objectPostProcessor the {@link ObjectPostProcessor} to use\n\t * @see WebSecurityConfiguration\n\t */\n\tpublic WebSecurity(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * <p>\n\t * Allows adding {@link RequestMatcher} instances that Spring Security should ignore.\n\t * Web Security provided by Spring Security (including the {@link SecurityContext})\n\t * will not be available on {@link HttpServletRequest} that match. Typically the\n\t * requests that are registered should be that of only static resources. For requests\n\t * that are dynamic, consider mapping the request to allow all users instead.\n\t * </p>\n\t *\n\t * Example Usage:\n\t *\n\t * <pre>\n\t * webSecurityBuilder.ignoring()\n\t * // ignore all URLs that start with /resources/ or /static/\n\t * \t\t.requestMatchers(&quot;/resources/**&quot;, &quot;/static/**&quot;);\n\t * </pre>\n\t *\n\t * Alternatively this will accomplish the same result:\n\t *\n\t * <pre>\n\t * webSecurityBuilder.ignoring()\n\t * // ignore all URLs that start with /resources/ or /static/\n\t * \t\t.requestMatchers(&quot;/resources/**&quot;).requestMatchers(&quot;/static/**&quot;);\n\t * </pre>\n\t *\n\t * Multiple invocations of ignoring() are also additive, so the following is also\n\t * equivalent to the previous two examples:\n\t *\n\t * <pre>\n\t * webSecurityBuilder.ignoring()\n\t * // ignore all URLs that start with /resources/\n\t * \t\t.requestMatchers(&quot;/resources/**&quot;);\n\t * webSecurityBuilder.ignoring()\n\t * // ignore all URLs that start with /static/\n\t * \t\t.requestMatchers(&quot;/static/**&quot;);\n\t * // now both URLs that start with /resources/ and /static/ will be ignored\n\t * </pre>\n\t * @return the {@link IgnoredRequestConfigurer} to use for registering request that\n\t * should be ignored\n\t */\n\tpublic IgnoredRequestConfigurer ignoring() {\n\t\treturn this.ignoredRequestRegistry;\n\t}\n\n\t/**\n\t * Allows customizing the {@link HttpFirewall}. The default is\n\t * {@link StrictHttpFirewall}.\n\t * @param httpFirewall the custom {@link HttpFirewall}\n\t * @return the {@link WebSecurity} for further customizations\n\t */\n\tpublic WebSecurity httpFirewall(HttpFirewall httpFirewall) {\n\t\tthis.httpFirewall = httpFirewall;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Controls debugging support for Spring Security.\n\t * @param debugEnabled if true, enables debug support with Spring Security. Default is\n\t * false.\n\t * @return the {@link WebSecurity} for further customization.\n\t * @see EnableWebSecurity#debug()\n\t */\n\tpublic WebSecurity debug(boolean debugEnabled) {\n\t\tthis.debugEnabled = debugEnabled;\n\t\treturn this;\n\t}\n\n\t/**\n\t * <p>\n\t * Adds builders to create {@link SecurityFilterChain} instances.\n\t * </p>\n\t *\n\t * <p>\n\t * Typically this method is invoked automatically within the framework from\n\t * {@link WebSecurityConfiguration#springSecurityFilterChain(ObjectProvider)}\n\t * </p>\n\t * @param securityFilterChainBuilder the builder to use to create the\n\t * {@link SecurityFilterChain} instances\n\t * @return the {@link WebSecurity} for further customizations\n\t */\n\tpublic WebSecurity addSecurityFilterChainBuilder(\n\t\t\tSecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {\n\t\tthis.securityFilterChainBuilders.add(securityFilterChainBuilder);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not\n\t * specified, then a {@link AuthorizationManagerWebInvocationPrivilegeEvaluator} will\n\t * be created based on the list of {@link SecurityFilterChain}.\n\t * @param privilegeEvaluator the {@link WebInvocationPrivilegeEvaluator} to use\n\t * @return the {@link WebSecurity} for further customizations\n\t */\n\tpublic WebSecurity privilegeEvaluator(WebInvocationPrivilegeEvaluator privilegeEvaluator) {\n\t\tthis.privilegeEvaluator = privilegeEvaluator;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the {@link SecurityExpressionHandler} to be used. If this is not specified,\n\t * then a {@link DefaultHttpSecurityExpressionHandler} will be used.\n\t * @param expressionHandler the {@link SecurityExpressionHandler} to use\n\t * @return the {@link WebSecurity} for further customizations\n\t */\n\tpublic WebSecurity expressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.expressionHandler = expressionHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the {@link SecurityExpressionHandler} to be used.\n\t * @return the {@link SecurityExpressionHandler} for further customizations\n\t */\n\tpublic SecurityExpressionHandler<FilterInvocation> getExpressionHandler() {\n\t\treturn this.expressionHandler;\n\t}\n\n\t/**\n\t * Gets the {@link WebInvocationPrivilegeEvaluator} to be used.\n\t * @return the {@link WebInvocationPrivilegeEvaluator} for further customizations\n\t */\n\tpublic WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() {\n\t\treturn this.privilegeEvaluator;\n\t}\n\n\t/**\n\t * Executes the Runnable immediately after the build takes place\n\t * @param postBuildAction\n\t * @return the {@link WebSecurity} for further customizations\n\t */\n\tpublic WebSecurity postBuildAction(Runnable postBuildAction) {\n\t\tthis.postBuildAction = postBuildAction;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the handler to handle\n\t * {@link org.springframework.security.web.firewall.RequestRejectedException}\n\t * @param requestRejectedHandler\n\t * @return the {@link WebSecurity} for further customizations\n\t * @since 5.7\n\t */\n\tpublic WebSecurity requestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {\n\t\tAssert.notNull(requestRejectedHandler, \"requestRejectedHandler cannot be null\");\n\t\tthis.requestRejectedHandler = requestRejectedHandler;\n\t\treturn this;\n\t}\n\n\t@Override\n\tprotected Filter performBuild() {\n\t\tAssert.state(!this.securityFilterChainBuilders.isEmpty(),\n\t\t\t\t() -> \"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. \"\n\t\t\t\t\t\t+ \"Typically this is done by exposing a SecurityFilterChain bean. \"\n\t\t\t\t\t\t+ \"More advanced users can invoke \" + WebSecurity.class.getSimpleName()\n\t\t\t\t\t\t+ \".addSecurityFilterChainBuilder directly\");\n\t\tint chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();\n\t\tList<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);\n\t\tRequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager\n\t\t\t.builder();\n\t\tboolean mappings = false;\n\t\tfor (RequestMatcher ignoredRequest : this.ignoredRequests) {\n\t\t\tWebSecurity.this.logger.warn(\"You are asking Spring Security to ignore \" + ignoredRequest\n\t\t\t\t\t+ \". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.\");\n\t\t\tSecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);\n\t\t\tsecurityFilterChains.add(securityFilterChain);\n\t\t\tbuilder.add(ignoredRequest, SingleResultAuthorizationManager.permitAll());\n\t\t\tmappings = true;\n\t\t}\n\t\tfor (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {\n\t\t\tSecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();\n\t\t\tsecurityFilterChains.add(securityFilterChain);\n\t\t\tmappings = addAuthorizationManager(securityFilterChain, builder) || mappings;\n\t\t}\n\t\tif (this.privilegeEvaluator == null) {\n\t\t\tAuthorizationManager<HttpServletRequest> authorizationManager = mappings ? builder.build()\n\t\t\t\t\t: SingleResultAuthorizationManager.permitAll();\n\t\t\tAuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(\n\t\t\t\t\tauthorizationManager);\n\t\t\tprivilegeEvaluator.setServletContext(this.servletContext);\n\t\t\tif (this.privilegeEvaluatorRequestTransformer != null) {\n\t\t\t\tprivilegeEvaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);\n\t\t\t}\n\t\t\tthis.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(\n\t\t\t\t\tList.of(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE, List.of(privilegeEvaluator))));\n\t\t}\n\t\tFilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);\n\t\tif (this.httpFirewall != null) {\n\t\t\tfilterChainProxy.setFirewall(this.httpFirewall);\n\t\t}\n\t\tif (this.requestRejectedHandler != null) {\n\t\t\tfilterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);\n\t\t}\n\t\telse if (!this.observationRegistry.isNoop()) {\n\t\t\tCompositeRequestRejectedHandler requestRejectedHandler = new CompositeRequestRejectedHandler(\n\t\t\t\t\tnew ObservationMarkingRequestRejectedHandler(this.observationRegistry),\n\t\t\t\t\tnew HttpStatusRequestRejectedHandler());\n\t\t\tfilterChainProxy.setRequestRejectedHandler(requestRejectedHandler);\n\t\t}\n\t\tfilterChainProxy.setFilterChainValidator(new WebSecurityFilterChainValidator());\n\t\tfilterChainProxy.setFilterChainDecorator(getFilterChainDecorator());\n\t\tfilterChainProxy.afterPropertiesSet();\n\n\t\tFilter result = filterChainProxy;\n\t\tif (this.debugEnabled) {\n\t\t\tthis.logger.warn(\"\\n\\n\" + \"********************************************************************\\n\"\n\t\t\t\t\t+ \"**********        Security debugging is enabled.       *************\\n\"\n\t\t\t\t\t+ \"**********    This may include sensitive information.  *************\\n\"\n\t\t\t\t\t+ \"**********      Do not use in a production system!     *************\\n\"\n\t\t\t\t\t+ \"********************************************************************\\n\\n\");\n\t\t\tresult = new DebugFilter(filterChainProxy);\n\t\t}\n\n\t\tthis.postBuildAction.run();\n\t\treturn result;\n\t}\n\n\tprivate boolean addAuthorizationManager(SecurityFilterChain securityFilterChain,\n\t\t\tRequestMatcherDelegatingAuthorizationManager.Builder builder) {\n\t\tboolean mappings = false;\n\t\tfor (Filter filter : securityFilterChain.getFilters()) {\n\t\t\tif (USING_ACCESS) {\n\t\t\t\tmappings = AccessComponents.addAuthorizationManager(filter, this.servletContext, builder,\n\t\t\t\t\t\tsecurityFilterChain);\n\t\t\t}\n\t\t\tif (filter instanceof AuthorizationFilter authorization) {\n\t\t\t\tAuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();\n\t\t\t\tbuilder.add(securityFilterChain::matches,\n\t\t\t\t\t\t(authentication, context) -> (AuthorizationDecision) authorizationManager\n\t\t\t\t\t\t\t.authorize(authentication, context.getRequest()));\n\t\t\t\tmappings = true;\n\t\t\t}\n\t\t}\n\t\treturn mappings;\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.defaultExpressionHandler.setApplicationContext(applicationContext);\n\t\ttry {\n\t\t\tthis.defaultExpressionHandler.setRoleHierarchy(applicationContext.getBean(RoleHierarchy.class));\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t}\n\t\ttry {\n\t\t\tthis.defaultExpressionHandler.setPermissionEvaluator(applicationContext.getBean(PermissionEvaluator.class));\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t}\n\t\tthis.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);\n\t\ttry {\n\t\t\tthis.httpFirewall = applicationContext.getBean(HttpFirewall.class);\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t}\n\t\ttry {\n\t\t\tthis.requestRejectedHandler = applicationContext.getBean(RequestRejectedHandler.class);\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t}\n\t\ttry {\n\t\t\tthis.observationRegistry = applicationContext.getBean(ObservationRegistry.class);\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t}\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,\n\t\t\t\tFilterChainDecorator.class);\n\t\tObjectProvider<ObjectPostProcessor<FilterChainDecorator>> postProcessor = applicationContext\n\t\t\t.getBeanProvider(type);\n\t\tthis.filterChainDecoratorPostProcessor = postProcessor.getIfUnique(ObjectPostProcessor::identity);\n\t\tClass<HttpServletRequestTransformer> requestTransformerClass = HttpServletRequestTransformer.class;\n\t\tthis.privilegeEvaluatorRequestTransformer = applicationContext.getBeanProvider(requestTransformerClass)\n\t\t\t.getIfUnique(PathPatternRequestTransformer::new);\n\t}\n\n\t@Override\n\tpublic void setServletContext(ServletContext servletContext) {\n\t\tthis.servletContext = servletContext;\n\t}\n\n\tFilterChainDecorator getFilterChainDecorator() {\n\t\treturn this.filterChainDecoratorPostProcessor.postProcess(new FilterChainProxy.VirtualFilterChainDecorator());\n\t}\n\n\t/**\n\t * Allows registering {@link RequestMatcher} instances that should be ignored by\n\t * Spring Security.\n\t *\n\t * @author Rob Winch\n\t * @since 3.2\n\t */\n\tpublic class IgnoredRequestConfigurer extends AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {\n\n\t\tIgnoredRequestConfigurer(ApplicationContext context) {\n\t\t\tsetApplicationContext(context);\n\t\t}\n\n\t\t@Override\n\t\tprotected IgnoredRequestConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {\n\t\t\tWebSecurity.this.ignoredRequests.addAll(requestMatchers);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Returns the {@link WebSecurity} to be returned for chaining.\n\t\t */\n\t\tpublic WebSecurity and() {\n\t\t\treturn WebSecurity.this;\n\t\t}\n\n\t}\n\n\t@NullMarked\n\tprivate static final class SecurityExpressionHandlerAdapter\n\t\t\textends AbstractSecurityExpressionHandler<FilterInvocation> {\n\n\t\tprivate final AbstractSecurityExpressionHandler<RequestAuthorizationContext> delegate;\n\n\t\tprivate SecurityExpressionHandlerAdapter(\n\t\t\t\tAbstractSecurityExpressionHandler<RequestAuthorizationContext> delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\t\tFilterInvocation invocation) {\n\t\t\tRequestAuthorizationContext context = new RequestAuthorizationContext(invocation.getRequest());\n\t\t\treturn this.delegate.createEvaluationContext(authentication, context);\n\t\t}\n\n\t\t@Override\n\t\tprotected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,\n\t\t\t\tFilterInvocation invocation) {\n\t\t\tRequestAuthorizationContext context = new RequestAuthorizationContext(invocation.getRequest());\n\t\t\tObject operations = this.delegate.createEvaluationContext(authentication, context)\n\t\t\t\t.getRootObject()\n\t\t\t\t.getValue();\n\t\t\tAssert.isInstanceOf(SecurityExpressionOperations.class, operations,\n\t\t\t\t\t\"createEvaluationContext must have a SecurityExpressionOperations instance as its root\");\n\t\t\treturn (SecurityExpressionOperations) operations;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext context) {\n\t\t\tthis.delegate.setApplicationContext(context);\n\t\t\tsuper.setApplicationContext(context);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {\n\t\t\tthis.delegate.setPermissionEvaluator(permissionEvaluator);\n\t\t\tsuper.setPermissionEvaluator(permissionEvaluator);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {\n\t\t\tthis.delegate.setRoleHierarchy(roleHierarchy);\n\t\t\tsuper.setRoleHierarchy(roleHierarchy);\n\t\t}\n\n\t}\n\n\tprivate static final class AccessComponents {\n\n\t\tprivate static boolean addAuthorizationManager(Filter filter, ServletContext servletContext,\n\t\t\t\tRequestMatcherDelegatingAuthorizationManager.Builder builder, SecurityFilterChain securityFilterChain) {\n\t\t\tif (filter instanceof FilterSecurityInterceptor securityInterceptor) {\n\t\t\t\tDefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(\n\t\t\t\t\t\tsecurityInterceptor);\n\t\t\t\tprivilegeEvaluator.setServletContext(servletContext);\n\t\t\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {\n\t\t\t\t\tHttpServletRequest request = context.getRequest();\n\t\t\t\t\tboolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),\n\t\t\t\t\t\t\trequest.getMethod(), authentication.get());\n\t\t\t\t\treturn new AuthorizationDecision(result);\n\t\t\t\t};\n\t\t\t\tbuilder.add(securityFilterChain::matches, authorizationManager);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurityFilterChainValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.UnreachableFilterChainException;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.util.ClassUtils;\n\n/**\n * A filter chain validator for filter chains built by {@link WebSecurity}\n *\n * @author Josh Cummings\n * @author Max Batischev\n * @since 6.5\n */\nfinal class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {\n\n\tprivate static final boolean USING_ACCESS = ClassUtils\n\t\t.isPresent(\"org.springframework.security.access.SecurityConfig\", null);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t@Override\n\tpublic void validate(FilterChainProxy filterChainProxy) {\n\t\tList<SecurityFilterChain> chains = filterChainProxy.getFilterChains();\n\t\tcheckForAnyRequestRequestMatcher(chains);\n\t\tcheckForDuplicateMatchers(chains);\n\t\tcheckAuthorizationFilters(chains);\n\t}\n\n\tprivate void checkForAnyRequestRequestMatcher(List<SecurityFilterChain> chains) {\n\t\tDefaultSecurityFilterChain anyRequestFilterChain = null;\n\t\tfor (SecurityFilterChain chain : chains) {\n\t\t\tif (anyRequestFilterChain != null) {\n\t\t\t\tString message = \"A filter chain that matches any request [\" + anyRequestFilterChain\n\t\t\t\t\t\t+ \"] has already been configured, which means that this filter chain [\" + chain\n\t\t\t\t\t\t+ \"] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.\";\n\t\t\t\tthrow new UnreachableFilterChainException(message, anyRequestFilterChain, chain);\n\t\t\t}\n\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\tif (defaultChain.getRequestMatcher() instanceof AnyRequestMatcher) {\n\t\t\t\t\tanyRequestFilterChain = defaultChain;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {\n\t\tDefaultSecurityFilterChain filterChain = null;\n\t\tfor (SecurityFilterChain chain : chains) {\n\t\t\tif (filterChain != null) {\n\t\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\t\tif (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {\n\t\t\t\t\t\tthrow new UnreachableFilterChainException(\n\t\t\t\t\t\t\t\t\"The FilterChainProxy contains two filter chains using the\" + \" matcher \"\n\t\t\t\t\t\t\t\t\t\t+ defaultChain.getRequestMatcher(),\n\t\t\t\t\t\t\t\tfilterChain, defaultChain);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\tfilterChain = defaultChain;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkAuthorizationFilters(List<SecurityFilterChain> chains) {\n\t\tFilter authorizationFilter = null;\n\t\tFilter filterSecurityInterceptor = null;\n\t\tfor (SecurityFilterChain chain : chains) {\n\t\t\tfor (Filter filter : chain.getFilters()) {\n\t\t\t\tif (filter instanceof AuthorizationFilter) {\n\t\t\t\t\tauthorizationFilter = filter;\n\t\t\t\t}\n\t\t\t\tif (USING_ACCESS && AccessComponents.isFilterSecurityInterceptor(filter)) {\n\t\t\t\t\tfilterSecurityInterceptor = filter;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (authorizationFilter != null && filterSecurityInterceptor != null) {\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\t\"It is not recommended to use authorizeRequests or FilterSecurityInterceptor in the configuration. Please only use authorizeHttpRequests\");\n\t\t\t}\n\t\t\tif (filterSecurityInterceptor != null) {\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\t\"Usage of authorizeRequests and FilterSecurityInterceptor are deprecated. Please use authorizeHttpRequests in the configuration\");\n\t\t\t}\n\t\t\tauthorizationFilter = null;\n\t\t\tfilterSecurityInterceptor = null;\n\t\t}\n\t}\n\n\tprivate static final class AccessComponents {\n\n\t\tprivate static boolean isFilterSecurityInterceptor(Filter filter) {\n\t\t\treturn filter instanceof FilterSecurityInterceptor;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/AutowiredWebSecurityConfigurersIgnoreParents.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport jakarta.servlet.Filter;\n\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.web.WebSecurityConfigurer;\nimport org.springframework.security.config.annotation.web.builders.WebSecurity;\nimport org.springframework.util.Assert;\n\n/**\n * A class used to get all the {@link WebSecurityConfigurer} instances from the current\n * {@link ApplicationContext} but ignoring the parent.\n *\n * @author Rob Winch\n */\npublic final class AutowiredWebSecurityConfigurersIgnoreParents {\n\n\tprivate final ConfigurableListableBeanFactory beanFactory;\n\n\tAutowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) {\n\t\tAssert.notNull(beanFactory, \"beanFactory cannot be null\");\n\t\tthis.beanFactory = beanFactory;\n\t}\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {\n\t\tList<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();\n\t\tMap<String, WebSecurityConfigurer> beansOfType = this.beanFactory.getBeansOfType(WebSecurityConfigurer.class);\n\t\tfor (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {\n\t\t\twebSecurityConfigurers.add(entry.getValue());\n\t\t}\n\t\treturn webSecurityConfigurers;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/EnableWebSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;\nimport org.springframework.security.config.annotation.web.WebSecurityConfigurer;\nimport org.springframework.security.web.SecurityFilterChain;\n\n/**\n * Add this annotation to an {@code @Configuration} class to have the Spring Security\n * configuration defined in any {@link WebSecurityConfigurer} or more likely by exposing a\n * {@link SecurityFilterChain} bean:\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableWebSecurity\n * public class MyWebSecurityConfiguration {\n *\n * \t&#064;Bean\n * \tpublic WebSecurityCustomizer webSecurityCustomizer() {\n * \t\treturn (web) -> web.ignoring()\n * \t\t// Spring Security should completely ignore URLs starting with /resources/\n * \t\t\t\t.requestMatchers(&quot;/resources/**&quot;);\n * \t}\n *\n * \t&#064;Bean\n * \tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n * \t\thttp.authorizeHttpRequests((authorize) -&gt; authorize\n * \t\t\t.requestMatchers(&quot;/public/**&quot;).permitAll()\n * \t\t\t.anyRequest().hasRole(&quot;USER&quot;))\n * \t\t\t\t// Possibly more configuration ...\n * \t\t\t\t.formLogin() // enable form based log in\n * \t\t\t\t// set permitAll for all URLs associated with Form Login\n * \t\t\t\t.permitAll();\n * \t\treturn http.build();\n * \t}\n *\n * \t&#064;Bean\n * \tpublic UserDetailsService userDetailsService() {\n * \t\tUserDetails user = User.withDefaultPasswordEncoder()\n * \t\t\t.username(&quot;user&quot;)\n * \t\t\t.password(&quot;password&quot;)\n * \t\t\t.roles(&quot;USER&quot;)\n * \t\t\t.build();\n * \t\tUserDetails admin = User.withDefaultPasswordEncoder()\n * \t\t\t.username(&quot;admin&quot;)\n * \t\t\t.password(&quot;password&quot;)\n * \t\t\t.roles(&quot;ADMIN&quot;, &quot;USER&quot;)\n * \t\t\t.build();\n * \t\treturn new InMemoryUserDetailsManager(user, admin);\n * \t}\n *\n * \t// Possibly more bean methods ...\n * }\n * </pre>\n *\n * @see WebSecurityConfigurer\n * @author Rob Winch\n * @since 3.2\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,\n\t\tHttpSecurityConfiguration.class, ObservationImportSelector.class })\n@EnableGlobalAuthentication\npublic @interface EnableWebSecurity {\n\n\t/**\n\t * Controls debugging support for Spring Security. Default is false.\n\t * @return if true, enables debug support with Spring Security\n\t */\n\tboolean debug() default false;\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.lang.reflect.Modifier;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.lang.Contract;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;\nimport org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;\nimport org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;\nimport org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;\nimport org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.util.function.ThrowingSupplier;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.accept.HeaderContentNegotiationStrategy;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * {@link Configuration} that exposes the {@link HttpSecurity} bean.\n *\n * @author Eleftheria Stein\n * @author Jinwoo Bae\n * @author Ngoc Nhan\n * @since 5.4\n */\n@Configuration(proxyBeanMethods = false)\nclass HttpSecurityConfiguration {\n\n\tprivate static final String BEAN_NAME_PREFIX = \"org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.\";\n\n\tprivate static final String HTTPSECURITY_BEAN_NAME = BEAN_NAME_PREFIX + \"httpSecurity\";\n\n\tprivate ObjectPostProcessor<Object> objectPostProcessor;\n\n\tprivate AuthenticationConfiguration authenticationConfiguration;\n\n\tprivate ApplicationContext context;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();\n\n\t@Autowired\n\tvoid setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tthis.objectPostProcessor = objectPostProcessor;\n\t}\n\n\t@Autowired\n\tvoid setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {\n\t\tthis.authenticationConfiguration = authenticationConfiguration;\n\t}\n\n\t@Autowired\n\tvoid setApplicationContext(ApplicationContext context) {\n\t\tthis.context = context;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setContentNegotiationStrategy(ContentNegotiationStrategy contentNegotiationStrategy) {\n\t\tthis.contentNegotiationStrategy = contentNegotiationStrategy;\n\t}\n\n\t@Bean(HTTPSECURITY_BEAN_NAME)\n\t@Scope(\"prototype\")\n\tHttpSecurity httpSecurity() {\n\t\tLazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);\n\t\tAuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(\n\t\t\t\tthis.objectPostProcessor, passwordEncoder);\n\t\tauthenticationBuilder.parentAuthenticationManager(authenticationManager());\n\t\tauthenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());\n\t\tHttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());\n\t\tWebAsyncManagerIntegrationFilter webAsyncManagerIntegrationFilter = new WebAsyncManagerIntegrationFilter();\n\t\twebAsyncManagerIntegrationFilter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.csrf(withDefaults())\n\t\t\t.addFilter(webAsyncManagerIntegrationFilter)\n\t\t\t.exceptionHandling(withDefaults())\n\t\t\t.headers(withDefaults())\n\t\t\t.sessionManagement(withDefaults())\n\t\t\t.securityContext(withDefaults())\n\t\t\t.requestCache(withDefaults())\n\t\t\t.anonymous(withDefaults())\n\t\t\t.servletApi(withDefaults())\n\t\t\t.with(new DefaultLoginPageConfigurer<>());\n\t\thttp.logout(withDefaults());\n\t\t// @formatter:on\n\t\tapplyCorsIfAvailable(http);\n\t\tapplyDefaultConfigurers(http);\n\t\tapplyHttpSecurityCustomizers(this.context, http);\n\t\tapplyTopLevelCustomizers(this.context, http);\n\t\treturn http;\n\t}\n\n\tprivate void applyCorsIfAvailable(HttpSecurity http) {\n\t\tif (this.context.getBeanNamesForType(UrlBasedCorsConfigurationSource.class).length > 0) {\n\t\t\thttp.cors(withDefaults());\n\t\t}\n\t}\n\n\tprivate AuthenticationManager authenticationManager() {\n\t\treturn this.authenticationConfiguration.getAuthenticationManager();\n\t}\n\n\tprivate AuthenticationEventPublisher getAuthenticationEventPublisher() {\n\t\tif (this.context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) {\n\t\t\treturn this.context.getBean(AuthenticationEventPublisher.class);\n\t\t}\n\t\treturn this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());\n\t}\n\n\tprivate void applyDefaultConfigurers(HttpSecurity http) {\n\t\tClassLoader classLoader = this.context.getClassLoader();\n\t\tList<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader\n\t\t\t.loadFactories(AbstractHttpConfigurer.class, classLoader);\n\t\tfor (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {\n\t\t\thttp.with(configurer);\n\t\t}\n\t}\n\n\t/**\n\t * Applies all {@code Customizer<HttpSecurity>} Bean instances to the\n\t * {@link HttpSecurity} instance.\n\t * @param applicationContext the {@link ApplicationContext} to lookup Bean instances\n\t * @param http the {@link HttpSecurity} to apply the Beans to.\n\t */\n\tprivate void applyHttpSecurityCustomizers(ApplicationContext applicationContext, HttpSecurity http) {\n\t\tResolvableType httpSecurityCustomizerType = ResolvableType.forClassWithGenerics(Customizer.class,\n\t\t\t\tHttpSecurity.class);\n\t\tObjectProvider<Customizer<HttpSecurity>> customizerProvider = this.context\n\t\t\t.getBeanProvider(httpSecurityCustomizerType);\n\n\t\t// @formatter:off\n\t\tcustomizerProvider.orderedStream().forEach((customizer) ->\n\t\t\t\tcustomizer.customize(http)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Applies all {@link Customizer} Beans to {@link HttpSecurity}. For each public,\n\t * non-static method in HttpSecurity that accepts a Customizer\n\t * <ul>\n\t * <li>Use the {@link MethodParameter} (this preserves generics) to resolve all Beans\n\t * for that type</li>\n\t * <li>For each {@link Customizer} Bean invoke the {@link java.lang.reflect.Method}\n\t * with the {@link Customizer} Bean as the argument</li>\n\t * </ul>\n\t * @param context the {@link ApplicationContext}\n\t * @param http the {@link HttpSecurity} @\n\t */\n\tprivate void applyTopLevelCustomizers(ApplicationContext context, HttpSecurity http) {\n\t\tReflectionUtils.MethodFilter isCustomizerMethod = (method) -> {\n\t\t\tif (Modifier.isStatic(method.getModifiers())) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!Modifier.isPublic(method.getModifiers())) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!method.canAccess(http)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (method.getParameterCount() != 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (method.getParameterTypes()[0] == Customizer.class) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\t\tReflectionUtils.MethodCallback invokeWithEachCustomizerBean = (customizerMethod) -> {\n\n\t\t\tMethodParameter customizerParameter = new MethodParameter(customizerMethod, 0);\n\t\t\tResolvableType customizerType = ResolvableType.forMethodParameter(customizerParameter);\n\t\t\tObjectProvider<?> customizerProvider = context.getBeanProvider(customizerType);\n\n\t\t\t// @formatter:off\n\t\t\tcustomizerProvider.orderedStream().forEach((customizer) ->\n\t\t\t\tReflectionUtils.invokeMethod(customizerMethod, http, customizer)\n\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t};\n\t\tReflectionUtils.doWithMethods(HttpSecurity.class, invokeWithEachCustomizerBean, isCustomizerMethod);\n\t}\n\n\tprivate Map<Class<?>, Object> createSharedObjects() {\n\t\tMap<Class<?>, Object> sharedObjects = new HashMap<>();\n\t\tsharedObjects.put(ApplicationContext.class, this.context);\n\t\tsharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy);\n\t\tsharedObjects.put(PathPatternRequestMatcher.Builder.class, constructRequestMatcherBuilder(this.context));\n\t\treturn sharedObjects;\n\t}\n\n\tprivate PathPatternRequestMatcher.Builder constructRequestMatcherBuilder(ApplicationContext context) {\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder = new PathPatternRequestMatcherBuilderFactoryBean();\n\t\trequestMatcherBuilder.setApplicationContext(context);\n\t\trequestMatcherBuilder.setBeanFactory(context.getAutowireCapableBeanFactory());\n\t\trequestMatcherBuilder.setBeanName(requestMatcherBuilder.toString());\n\t\treturn ThrowingSupplier.of(requestMatcherBuilder::getObject).get();\n\t}\n\n\tstatic class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {\n\n\t\tprivate PasswordEncoder defaultPasswordEncoder;\n\n\t\t/**\n\t\t * Creates a new instance\n\t\t * @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.\n\t\t */\n\t\tDefaultPasswordEncoderAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,\n\t\t\t\tPasswordEncoder defaultPasswordEncoder) {\n\t\t\tsuper(objectPostProcessor);\n\t\t\tthis.defaultPasswordEncoder = defaultPasswordEncoder;\n\t\t}\n\n\t\t@Override\n\t\tpublic InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication() {\n\t\t\treturn super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);\n\t\t}\n\n\t\t@Override\n\t\tpublic JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() {\n\t\t\treturn super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);\n\t\t}\n\n\t\t@Override\n\t\tpublic <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(\n\t\t\t\tT userDetailsService) {\n\t\t\treturn super.userDetailsService(userDetailsService).passwordEncoder(this.defaultPasswordEncoder);\n\t\t}\n\n\t}\n\n\tstatic class LazyPasswordEncoder implements PasswordEncoder {\n\n\t\tprivate ApplicationContext applicationContext;\n\n\t\tprivate PasswordEncoder passwordEncoder;\n\n\t\tLazyPasswordEncoder(ApplicationContext applicationContext) {\n\t\t\tthis.applicationContext = applicationContext;\n\t\t}\n\n\t\t@Override\n\t\t@Contract(\"!null -> !null; null -> null\")\n\t\tpublic String encode(CharSequence rawPassword) {\n\t\t\treturn getPasswordEncoder().encode(rawPassword);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(CharSequence rawPassword, String encodedPassword) {\n\t\t\treturn getPasswordEncoder().matches(rawPassword, encodedPassword);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean upgradeEncoding(String encodedPassword) {\n\t\t\treturn getPasswordEncoder().upgradeEncoding(encodedPassword);\n\t\t}\n\n\t\tprivate PasswordEncoder getPasswordEncoder() {\n\t\t\tif (this.passwordEncoder != null) {\n\t\t\t\treturn this.passwordEncoder;\n\t\t\t}\n\t\t\tthis.passwordEncoder = this.applicationContext.getBeanProvider(PasswordEncoder.class)\n\t\t\t\t.getIfUnique(PasswordEncoderFactories::createDelegatingPasswordEncoder);\n\t\t\treturn this.passwordEncoder;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn getPasswordEncoder().toString();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.JWSKeySelector;\nimport com.nimbusds.jose.proc.JWSVerificationKeySelector;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jwt.proc.ConfigurableJWTProcessor;\nimport com.nimbusds.jwt.proc.DefaultJWTProcessor;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.SecurityFilterChain;\n\n/**\n * {@link Configuration} for OAuth 2.1 Authorization Server support.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer\n */\n@Configuration(proxyBeanMethods = false)\npublic class OAuth2AuthorizationServerConfiguration {\n\n\t@Bean\n\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.oauth2AuthorizationServer(Customizer.withDefaults())\n\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t);\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\n\tpublic static JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\tSet<JWSAlgorithm> jwsAlgs = new HashSet<>();\n\t\tjwsAlgs.addAll(JWSAlgorithm.Family.RSA);\n\t\tjwsAlgs.addAll(JWSAlgorithm.Family.EC);\n\t\tjwsAlgs.addAll(JWSAlgorithm.Family.HMAC_SHA);\n\t\tConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\tJWSKeySelector<SecurityContext> jwsKeySelector = new JWSVerificationKeySelector<>(jwsAlgs, jwkSource);\n\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector);\n\t\t// Override the default Nimbus claims set verifier as NimbusJwtDecoder handles it\n\t\t// instead\n\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t});\n\t\treturn new NimbusJwtDecoder(jwtProcessor);\n\t}\n\n\t@Bean\n\tRegisterMissingBeanPostProcessor registerMissingBeanPostProcessor() {\n\t\tRegisterMissingBeanPostProcessor postProcessor = new RegisterMissingBeanPostProcessor();\n\t\tpostProcessor.addBeanDefinition(AuthorizationServerSettings.class,\n\t\t\t\t() -> AuthorizationServerSettings.builder().build());\n\t\treturn postProcessor;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.BeanFactoryUtils;\nimport org.springframework.beans.factory.BeanInitializationException;\nimport org.springframework.beans.factory.ListableBeanFactory;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.context.annotation.AnnotationBeanNameGenerator;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.DelegatingOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.TokenExchangeOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.method.annotation.OAuth2AuthorizedClientArgumentResolver;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * {@link Configuration} for OAuth 2.0 Client support.\n *\n * <p>\n * This {@code Configuration} is conditionally imported by {@link OAuth2ImportSelector}\n * when the {@code spring-security-oauth2-client} module is present on the classpath.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2ImportSelector\n */\n@Import({ OAuth2ClientConfiguration.OAuth2ClientWebMvcImportSelector.class,\n\t\tOAuth2ClientConfiguration.OAuth2AuthorizedClientManagerConfiguration.class })\nfinal class OAuth2ClientConfiguration {\n\n\tprivate static final boolean webMvcPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = OAuth2ClientConfiguration.class.getClassLoader();\n\t\twebMvcPresent = ClassUtils.isPresent(\"org.springframework.web.servlet.DispatcherServlet\", classLoader);\n\t}\n\n\tstatic class OAuth2ClientWebMvcImportSelector implements ImportSelector {\n\n\t\t@Override\n\t\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\t\tif (!webMvcPresent) {\n\t\t\t\treturn new String[0];\n\t\t\t}\n\t\t\treturn new String[] {\n\t\t\t\t\tOAuth2ClientConfiguration.class.getName() + \".OAuth2ClientWebMvcSecurityConfiguration\" };\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Joe Grandja\n\t * @since 6.2.0\n\t */\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class OAuth2AuthorizedClientManagerConfiguration {\n\n\t\t@Bean(name = OAuth2AuthorizedClientManagerRegistrar.BEAN_NAME)\n\t\tOAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar() {\n\t\t\treturn new OAuth2AuthorizedClientManagerRegistrar();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class OAuth2ClientWebMvcSecurityConfiguration implements WebMvcConfigurer {\n\n\t\tprivate final OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t\tprivate final ObjectProvider<SecurityContextHolderStrategy> securityContextHolderStrategy;\n\n\t\tprivate final OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar;\n\n\t\tOAuth2ClientWebMvcSecurityConfiguration(ObjectProvider<OAuth2AuthorizedClientManager> authorizedClientManager,\n\t\t\t\tObjectProvider<SecurityContextHolderStrategy> securityContextHolderStrategy,\n\t\t\t\tOAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar) {\n\t\t\tthis.authorizedClientManager = authorizedClientManager.getIfUnique();\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t\tthis.authorizedClientManagerRegistrar = authorizedClientManagerRegistrar;\n\t\t}\n\n\t\t@Override\n\t\tpublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {\n\t\t\tOAuth2AuthorizedClientManager authorizedClientManager = getAuthorizedClientManager();\n\t\t\tif (authorizedClientManager != null) {\n\t\t\t\tOAuth2AuthorizedClientArgumentResolver resolver = new OAuth2AuthorizedClientArgumentResolver(\n\t\t\t\t\t\tauthorizedClientManager);\n\t\t\t\tthis.securityContextHolderStrategy.ifAvailable(resolver::setSecurityContextHolderStrategy);\n\t\t\t\targumentResolvers.add(resolver);\n\t\t\t}\n\t\t}\n\n\t\tprivate OAuth2AuthorizedClientManager getAuthorizedClientManager() {\n\t\t\tif (this.authorizedClientManager != null) {\n\t\t\t\treturn this.authorizedClientManager;\n\t\t\t}\n\t\t\treturn this.authorizedClientManagerRegistrar.getAuthorizedClientManagerIfAvailable();\n\t\t}\n\n\t}\n\n\t/**\n\t * A registrar for registering the default {@link OAuth2AuthorizedClientManager} bean\n\t * definition, if not already present.\n\t *\n\t * @author Joe Grandja\n\t * @author Steve Riesenberg\n\t * @since 6.2.0\n\t */\n\tstatic final class OAuth2AuthorizedClientManagerRegistrar\n\t\t\timplements ApplicationEventPublisherAware, BeanDefinitionRegistryPostProcessor, BeanFactoryAware {\n\n\t\tstatic final String BEAN_NAME = \"authorizedClientManagerRegistrar\";\n\n\t\tstatic final String FACTORY_METHOD_NAME = \"getAuthorizedClientManager\";\n\n\t\t// @formatter:off\n\t\tprivate static final Set<Class<?>> KNOWN_AUTHORIZED_CLIENT_PROVIDERS = Set.of(\n\t\t\t\tAuthorizationCodeOAuth2AuthorizedClientProvider.class,\n\t\t\t\tRefreshTokenOAuth2AuthorizedClientProvider.class,\n\t\t\t\tClientCredentialsOAuth2AuthorizedClientProvider.class,\n\t\t\t\tJwtBearerOAuth2AuthorizedClientProvider.class,\n\t\t\t\tTokenExchangeOAuth2AuthorizedClientProvider.class\n\t\t);\n\t\t// @formatter:on\n\n\t\tprivate final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();\n\n\t\tprivate ApplicationEventPublisher applicationEventPublisher;\n\n\t\tprivate ListableBeanFactory beanFactory;\n\n\t\t@Override\n\t\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\t\tif (getBeanNamesForType(OAuth2AuthorizedClientManager.class).length != 0\n\t\t\t\t\t|| getBeanNamesForType(ClientRegistrationRepository.class).length != 1\n\t\t\t\t\t|| getBeanNamesForType(OAuth2AuthorizedClientRepository.class).length != 1) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tBeanDefinition beanDefinition = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(OAuth2AuthorizedClientManager.class)\n\t\t\t\t.setFactoryMethodOnBean(FACTORY_METHOD_NAME, BEAN_NAME)\n\t\t\t\t.getBeanDefinition();\n\n\t\t\tregistry.registerBeanDefinition(this.beanNameGenerator.generateBeanName(beanDefinition, registry),\n\t\t\t\t\tbeanDefinition);\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t\t}\n\n\t\t@Override\n\t\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\t\tthis.beanFactory = (ListableBeanFactory) beanFactory;\n\t\t}\n\n\t\tOAuth2AuthorizedClientManager getAuthorizedClientManagerIfAvailable() {\n\t\t\tif (getBeanNamesForType(ClientRegistrationRepository.class).length != 1\n\t\t\t\t\t|| getBeanNamesForType(OAuth2AuthorizedClientRepository.class).length != 1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getAuthorizedClientManager();\n\t\t}\n\n\t\tOAuth2AuthorizedClientManager getAuthorizedClientManager() {\n\t\t\tClientRegistrationRepository clientRegistrationRepository = BeanFactoryUtils\n\t\t\t\t.beanOfTypeIncludingAncestors(this.beanFactory, ClientRegistrationRepository.class, true, true);\n\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = BeanFactoryUtils\n\t\t\t\t.beanOfTypeIncludingAncestors(this.beanFactory, OAuth2AuthorizedClientRepository.class, true, true);\n\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviderBeans = BeanFactoryUtils\n\t\t\t\t.beansOfTypeIncludingAncestors(this.beanFactory, OAuth2AuthorizedClientProvider.class, true, true)\n\t\t\t\t.values();\n\n\t\t\tOAuth2AuthorizedClientProvider authorizedClientProvider;\n\t\t\tif (hasDelegatingAuthorizedClientProvider(authorizedClientProviderBeans)) {\n\t\t\t\tauthorizedClientProvider = authorizedClientProviderBeans.iterator().next();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tList<OAuth2AuthorizedClientProvider> authorizedClientProviders = new ArrayList<>();\n\t\t\t\tauthorizedClientProviders\n\t\t\t\t\t.add(getAuthorizationCodeAuthorizedClientProvider(authorizedClientProviderBeans));\n\t\t\t\tauthorizedClientProviders.add(getRefreshTokenAuthorizedClientProvider(authorizedClientProviderBeans));\n\t\t\t\tauthorizedClientProviders\n\t\t\t\t\t.add(getClientCredentialsAuthorizedClientProvider(authorizedClientProviderBeans));\n\n\t\t\t\tOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = getJwtBearerAuthorizedClientProvider(\n\t\t\t\t\t\tauthorizedClientProviderBeans);\n\t\t\t\tif (jwtBearerAuthorizedClientProvider != null) {\n\t\t\t\t\tauthorizedClientProviders.add(jwtBearerAuthorizedClientProvider);\n\t\t\t\t}\n\n\t\t\t\tOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = getTokenExchangeAuthorizedClientProvider(\n\t\t\t\t\t\tauthorizedClientProviderBeans);\n\t\t\t\tif (tokenExchangeAuthorizedClientProvider != null) {\n\t\t\t\t\tauthorizedClientProviders.add(tokenExchangeAuthorizedClientProvider);\n\t\t\t\t}\n\n\t\t\t\tauthorizedClientProviders.addAll(getAdditionalAuthorizedClientProviders(authorizedClientProviderBeans));\n\t\t\t\tauthorizedClientProvider = new DelegatingOAuth2AuthorizedClientProvider(authorizedClientProviders);\n\t\t\t}\n\n\t\t\tDefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t\t\tConsumer<DefaultOAuth2AuthorizedClientManager> authorizedClientManagerConsumer = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(Consumer.class, DefaultOAuth2AuthorizedClientManager.class));\n\t\t\tif (authorizedClientManagerConsumer != null) {\n\t\t\t\tauthorizedClientManagerConsumer.accept(authorizedClientManager);\n\t\t\t}\n\n\t\t\treturn authorizedClientManager;\n\t\t}\n\n\t\tprivate boolean hasDelegatingAuthorizedClientProvider(\n\t\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tif (authorizedClientProviders.size() != 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn authorizedClientProviders.iterator().next() instanceof DelegatingOAuth2AuthorizedClientProvider;\n\t\t}\n\n\t\tprivate OAuth2AuthorizedClientProvider getAuthorizationCodeAuthorizedClientProvider(\n\t\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tAuthorizationCodeOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, AuthorizationCodeOAuth2AuthorizedClientProvider.class);\n\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\tauthorizedClientProvider = new AuthorizationCodeOAuth2AuthorizedClientProvider();\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate OAuth2AuthorizedClientProvider getRefreshTokenAuthorizedClientProvider(\n\t\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tRefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, RefreshTokenOAuth2AuthorizedClientProvider.class);\n\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\tauthorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider();\n\t\t\t}\n\n\t\t\tOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\t\tOAuth2RefreshTokenGrantRequest.class));\n\t\t\tif (accessTokenResponseClient != null) {\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t\t}\n\n\t\t\tif (this.applicationEventPublisher != null) {\n\t\t\t\tauthorizedClientProvider.setApplicationEventPublisher(this.applicationEventPublisher);\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate OAuth2AuthorizedClientProvider getClientCredentialsAuthorizedClientProvider(\n\t\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, ClientCredentialsOAuth2AuthorizedClientProvider.class);\n\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\tauthorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();\n\t\t\t}\n\n\t\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\t\tOAuth2ClientCredentialsGrantRequest.class));\n\t\t\tif (accessTokenResponseClient != null) {\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate OAuth2AuthorizedClientProvider getJwtBearerAuthorizedClientProvider(\n\t\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tJwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, JwtBearerOAuth2AuthorizedClientProvider.class);\n\n\t\t\tOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\t\tJwtBearerGrantRequest.class));\n\t\t\tif (accessTokenResponseClient != null) {\n\t\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\t\tauthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();\n\t\t\t\t}\n\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate OAuth2AuthorizedClientProvider getTokenExchangeAuthorizedClientProvider(\n\t\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tTokenExchangeOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, TokenExchangeOAuth2AuthorizedClientProvider.class);\n\n\t\t\tOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\t\tTokenExchangeGrantRequest.class));\n\t\t\tif (accessTokenResponseClient != null) {\n\t\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\t\tauthorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();\n\t\t\t\t}\n\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate List<OAuth2AuthorizedClientProvider> getAdditionalAuthorizedClientProviders(\n\t\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tList<OAuth2AuthorizedClientProvider> additionalAuthorizedClientProviders = new ArrayList<>(\n\t\t\t\t\tauthorizedClientProviders);\n\t\t\tadditionalAuthorizedClientProviders\n\t\t\t\t.removeIf((provider) -> KNOWN_AUTHORIZED_CLIENT_PROVIDERS.contains(provider.getClass()));\n\t\t\treturn additionalAuthorizedClientProviders;\n\t\t}\n\n\t\tprivate <T extends OAuth2AuthorizedClientProvider> T getAuthorizedClientProviderByType(\n\t\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders, Class<T> providerClass) {\n\t\t\tT authorizedClientProvider = null;\n\t\t\tfor (OAuth2AuthorizedClientProvider current : authorizedClientProviders) {\n\t\t\t\tif (providerClass.isInstance(current)) {\n\t\t\t\t\tassertAuthorizedClientProviderIsNull(authorizedClientProvider);\n\t\t\t\t\tauthorizedClientProvider = providerClass.cast(current);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate static void assertAuthorizedClientProviderIsNull(\n\t\t\t\tOAuth2AuthorizedClientProvider authorizedClientProvider) {\n\t\t\tif (authorizedClientProvider != null) {\n\t\t\t\t// @formatter:off\n\t\t\t\tthrow new BeanInitializationException(String.format(\n\t\t\t\t\t\t\"Unable to create an %s bean. Expected one bean of type %s, but found multiple. \" +\n\t\t\t\t\t\t\"Please consider defining only a single bean of this type, or define an %s bean yourself.\",\n\t\t\t\t\t\tOAuth2AuthorizedClientManager.class.getName(),\n\t\t\t\t\t\tauthorizedClientProvider.getClass().getName(),\n\t\t\t\t\t\tOAuth2AuthorizedClientManager.class.getName()));\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t}\n\n\t\tprivate <T> String[] getBeanNamesForType(Class<T> beanClass) {\n\t\t\treturn BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, beanClass, true, true);\n\t\t}\n\n\t\tprivate <T> T getBeanOfType(ResolvableType resolvableType) {\n\t\t\tObjectProvider<T> objectProvider = this.beanFactory.getBeanProvider(resolvableType, true);\n\t\t\treturn objectProvider.getIfAvailable();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\t\tthis.applicationEventPublisher = applicationEventPublisher;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ImportSelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Used by {@link EnableWebSecurity} to conditionally import:\n *\n * <ul>\n * <li>{@link OAuth2ClientConfiguration} when the {@code spring-security-oauth2-client}\n * module is present on the classpath</li>\n * <li>{@link SecurityReactorContextConfiguration} when either the\n * {@code spring-security-oauth2-client} or {@code spring-security-oauth2-resource-server}\n * module as well as the {@code spring-webflux} module are present on the classpath</li>\n * </ul>\n *\n * @author Joe Grandja\n * @author Josh Cummings\n * @since 5.1\n * @see OAuth2ClientConfiguration\n * @see SecurityReactorContextConfiguration\n */\nfinal class OAuth2ImportSelector implements ImportSelector {\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\tSet<String> imports = new LinkedHashSet<>();\n\t\tClassLoader classLoader = getClass().getClassLoader();\n\t\tboolean oauth2ClientPresent = ClassUtils\n\t\t\t.isPresent(\"org.springframework.security.oauth2.client.registration.ClientRegistration\", classLoader);\n\t\tboolean webfluxPresent = ClassUtils\n\t\t\t.isPresent(\"org.springframework.web.reactive.function.client.ExchangeFilterFunction\", classLoader);\n\t\tboolean oauth2ResourceServerPresent = ClassUtils\n\t\t\t.isPresent(\"org.springframework.security.oauth2.server.resource.BearerTokenError\", classLoader);\n\t\tif (oauth2ClientPresent) {\n\t\t\timports.add(\"org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration\");\n\t\t}\n\t\tif (webfluxPresent && oauth2ClientPresent) {\n\t\t\timports.add(\n\t\t\t\t\t\"org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration\");\n\t\t}\n\t\tif (webfluxPresent && oauth2ResourceServerPresent) {\n\t\t\timports.add(\n\t\t\t\t\t\"org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration\");\n\t\t}\n\t\treturn StringUtils.toStringArray(imports);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/ObservationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.ObservationAuthenticationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.ObservationAuthorizationManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\nimport org.springframework.security.web.FilterChainProxy.FilterChainDecorator;\nimport org.springframework.security.web.ObservationFilterChainDecorator;\n\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nclass ObservationConfiguration {\n\n\tprivate static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()\n\t\t.shouldObserveRequests(true)\n\t\t.shouldObserveAuthentications(true)\n\t\t.shouldObserveAuthorizations(true)\n\t\t.build();\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> webAuthorizationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic AuthorizationManager postProcess(AuthorizationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();\n\t\t\t\treturn active ? new ObservationAuthorizationManager<>(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<AuthenticationManager> authenticationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic AuthenticationManager postProcess(AuthenticationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthentications();\n\t\t\t\treturn active ? new ObservationAuthenticationManager(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<FilterChainDecorator> filterChainDecoratorPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic FilterChainDecorator postProcess(FilterChainDecorator object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveRequests();\n\t\t\t\treturn active ? new ObservationFilterChainDecorator(r) : object;\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/ObservationImportSelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Used by {@link EnableWebSecurity} to conditionally import observation configuration\n * when {@link ObservationRegistry} is present.\n *\n * @author Josh Cummings\n * @since 6.4\n */\nclass ObservationImportSelector implements ImportSelector {\n\n\tprivate static final boolean observabilityPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = ObservationImportSelector.class.getClassLoader();\n\t\tobservabilityPresent = ClassUtils.isPresent(\"io.micrometer.observation.ObservationRegistry\", classLoader);\n\t}\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\tif (!observabilityPresent) {\n\t\t\treturn new String[0];\n\t\t}\n\t\treturn new String[] { ObservationConfiguration.class.getName() };\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.BeanFactoryUtils;\nimport org.springframework.beans.factory.ListableBeanFactory;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.support.AbstractBeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.context.annotation.AnnotationBeanNameGenerator;\n\n/**\n * Post processor to register one or more bean definitions on container initialization, if\n * not already present.\n *\n * @author Steve Riesenberg\n * @since 7.0\n */\nfinal class RegisterMissingBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {\n\n\tprivate final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();\n\n\tprivate final List<AbstractBeanDefinition> beanDefinitions = new ArrayList<>();\n\n\tprivate BeanFactory beanFactory;\n\n\t@Override\n\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\tfor (AbstractBeanDefinition beanDefinition : this.beanDefinitions) {\n\t\t\tString[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(\n\t\t\t\t\t(ListableBeanFactory) this.beanFactory, beanDefinition.getBeanClass(), false, false);\n\t\t\tif (beanNames.length == 0) {\n\t\t\t\tString beanName = this.beanNameGenerator.generateBeanName(beanDefinition, registry);\n\t\t\t\tregistry.registerBeanDefinition(beanName, beanDefinition);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t}\n\n\t<T> void addBeanDefinition(Class<T> beanClass, Supplier<T> beanSupplier) {\n\t\tthis.beanDefinitions.add(new RootBeanDefinition(beanClass, beanSupplier));\n\t}\n\n\t@Override\n\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\tthis.beanFactory = beanFactory;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/SecurityReactorContextConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.reactivestreams.Publisher;\nimport org.reactivestreams.Subscription;\nimport reactor.core.CoreSubscriber;\nimport reactor.core.publisher.Hooks;\nimport reactor.core.publisher.Operators;\nimport reactor.util.context.Context;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\n/**\n * {@link Configuration} that (potentially) adds a \"decorating\" {@code Publisher} for the\n * last operator created in every {@code Mono} or {@code Flux}.\n *\n * <p>\n * The {@code Publisher} is solely responsible for adding the current\n * {@code HttpServletRequest}, {@code HttpServletResponse} and {@code Authentication} to\n * the Reactor {@code Context} so that it's accessible in every flow, if required.\n *\n * @author Joe Grandja\n * @author Roman Matiushchenko\n * @since 5.2\n * @see OAuth2ImportSelector\n */\n@Configuration(proxyBeanMethods = false)\nclass SecurityReactorContextConfiguration {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t@Bean\n\tSecurityReactorContextSubscriberRegistrar securityReactorContextSubscriberRegistrar() {\n\t\tSecurityReactorContextSubscriberRegistrar registrar = new SecurityReactorContextSubscriberRegistrar();\n\t\tregistrar.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\treturn registrar;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tstatic class SecurityReactorContextSubscriberRegistrar implements InitializingBean, DisposableBean {\n\n\t\tprivate static final String SECURITY_REACTOR_CONTEXT_OPERATOR_KEY = \"org.springframework.security.SECURITY_REACTOR_CONTEXT_OPERATOR\";\n\n\t\tprivate final Map<Object, Supplier<Object>> CONTEXT_ATTRIBUTE_VALUE_LOADERS = new HashMap<>();\n\n\t\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t\t.getContextHolderStrategy();\n\n\t\tSecurityReactorContextSubscriberRegistrar() {\n\t\t\tthis.CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(HttpServletRequest.class,\n\t\t\t\t\tSecurityReactorContextSubscriberRegistrar::getRequest);\n\t\t\tthis.CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(HttpServletResponse.class,\n\t\t\t\t\tSecurityReactorContextSubscriberRegistrar::getResponse);\n\t\t\tthis.CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(Authentication.class, this::getAuthentication);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterPropertiesSet() throws Exception {\n\t\t\tFunction<? super Publisher<Object>, ? extends Publisher<Object>> lifter = Operators\n\t\t\t\t.liftPublisher((pub, sub) -> createSubscriberIfNecessary(sub));\n\t\t\tHooks.onLastOperator(SECURITY_REACTOR_CONTEXT_OPERATOR_KEY, lifter::apply);\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tHooks.resetOnLastOperator(SECURITY_REACTOR_CONTEXT_OPERATOR_KEY);\n\t\t}\n\n\t\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t}\n\n\t\t<T> CoreSubscriber<T> createSubscriberIfNecessary(CoreSubscriber<T> delegate) {\n\t\t\tif (delegate.currentContext().hasKey(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES)) {\n\t\t\t\t// Already enriched. No need to create Subscriber so return original\n\t\t\t\treturn delegate;\n\t\t\t}\n\t\t\treturn new SecurityReactorContextSubscriber<>(delegate, getContextAttributes());\n\t\t}\n\n\t\tprivate Map<Object, Object> getContextAttributes() {\n\t\t\treturn new LoadingMap<>(this.CONTEXT_ATTRIBUTE_VALUE_LOADERS);\n\t\t}\n\n\t\tprivate static HttpServletRequest getRequest() {\n\t\t\tRequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n\t\t\tif (requestAttributes instanceof ServletRequestAttributes) {\n\t\t\t\tServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;\n\t\t\t\treturn servletRequestAttributes.getRequest();\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate static HttpServletResponse getResponse() {\n\t\t\tRequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n\t\t\tif (requestAttributes instanceof ServletRequestAttributes) {\n\t\t\t\tServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;\n\t\t\t\treturn servletRequestAttributes.getResponse(); // possible null\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate Authentication getAuthentication() {\n\t\t\treturn this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t}\n\n\t}\n\n\tstatic class SecurityReactorContextSubscriber<T> implements CoreSubscriber<T> {\n\n\t\tstatic final String SECURITY_CONTEXT_ATTRIBUTES = \"org.springframework.security.SECURITY_CONTEXT_ATTRIBUTES\";\n\n\t\tprivate final CoreSubscriber<T> delegate;\n\n\t\tprivate final Context context;\n\n\t\tSecurityReactorContextSubscriber(CoreSubscriber<T> delegate, Map<Object, Object> attributes) {\n\t\t\tthis.delegate = delegate;\n\t\t\tContext context = getOrPutContext(attributes, this.delegate.currentContext());\n\t\t\tthis.context = context;\n\t\t}\n\n\t\tprivate Context getOrPutContext(Map<Object, Object> attributes, Context currentContext) {\n\t\t\tif (currentContext.hasKey(SECURITY_CONTEXT_ATTRIBUTES)) {\n\t\t\t\treturn currentContext;\n\t\t\t}\n\t\t\treturn currentContext.put(SECURITY_CONTEXT_ATTRIBUTES, attributes);\n\t\t}\n\n\t\t@Override\n\t\tpublic Context currentContext() {\n\t\t\treturn this.context;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onSubscribe(Subscription s) {\n\t\t\tthis.delegate.onSubscribe(s);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onNext(T t) {\n\t\t\tthis.delegate.onNext(t);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onError(Throwable ex) {\n\t\t\tthis.delegate.onError(ex);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onComplete() {\n\t\t\tthis.delegate.onComplete();\n\t\t}\n\n\t}\n\n\t/**\n\t * A map that computes each value when {@link #get} is invoked\n\t */\n\tstatic class LoadingMap<K, V> implements Map<K, V> {\n\n\t\tprivate final Map<K, V> loaded = new ConcurrentHashMap<>();\n\n\t\tprivate final Map<K, Supplier<V>> loaders;\n\n\t\tLoadingMap(Map<K, Supplier<V>> loaders) {\n\t\t\tthis.loaders = Collections.unmodifiableMap(new HashMap<>(loaders));\n\t\t}\n\n\t\t@Override\n\t\tpublic int size() {\n\t\t\treturn this.loaders.size();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isEmpty() {\n\t\t\treturn this.loaders.isEmpty();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean containsKey(Object key) {\n\t\t\treturn this.loaders.containsKey(key);\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<K> keySet() {\n\t\t\treturn this.loaders.keySet();\n\t\t}\n\n\t\t@Override\n\t\tpublic V get(Object key) {\n\t\t\tif (!this.loaders.containsKey(key)) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"This map only supports the following keys: \" + this.loaders.keySet());\n\t\t\t}\n\t\t\treturn this.loaded.computeIfAbsent((K) key, (k) -> this.loaders.get(k).get());\n\t\t}\n\n\t\t@Override\n\t\tpublic V put(K key, V value) {\n\t\t\tif (!this.loaders.containsKey(key)) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"This map only supports the following keys: \" + this.loaders.keySet());\n\t\t\t}\n\t\t\treturn this.loaded.put(key, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic V remove(Object key) {\n\t\t\tif (!this.loaders.containsKey(key)) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"This map only supports the following keys: \" + this.loaders.keySet());\n\t\t\t}\n\t\t\treturn this.loaded.remove(key);\n\t\t}\n\n\t\t@Override\n\t\tpublic void putAll(Map<? extends K, ? extends V> m) {\n\t\t\tfor (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {\n\t\t\t\tput(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void clear() {\n\t\t\tthis.loaded.clear();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean containsValue(Object value) {\n\t\t\treturn this.loaded.containsValue(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<V> values() {\n\t\t\treturn this.loaded.values();\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<Entry<K, V>> entrySet() {\n\t\t\treturn this.loaded.entrySet();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tLoadingMap<?, ?> that = (LoadingMap<?, ?>) o;\n\n\t\t\treturn this.loaded.equals(that.loaded);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn this.loaded.hashCode();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/SpringWebMvcImportSelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Used by {@link EnableWebSecurity} to conditionally import\n * {@link WebMvcSecurityConfiguration} when the DispatcherServlet is present on the\n * classpath.\n *\n * @author Rob Winch\n * @since 3.2\n */\nclass SpringWebMvcImportSelector implements ImportSelector {\n\n\tprivate static final boolean webMvcPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = SpringWebMvcImportSelector.class.getClassLoader();\n\t\twebMvcPresent = ClassUtils.isPresent(\"org.springframework.web.servlet.DispatcherServlet\", classLoader);\n\t}\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\tif (!webMvcPresent) {\n\t\t\treturn new String[0];\n\t\t}\n\t\treturn new String[] {\n\t\t\t\t\"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration\" };\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.expression.BeanFactoryResolver;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.debug.DebugFilter;\nimport org.springframework.security.web.firewall.HttpFirewall;\nimport org.springframework.security.web.firewall.RequestRejectedHandler;\nimport org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver;\nimport org.springframework.security.web.method.annotation.CsrfTokenArgumentResolver;\nimport org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver;\nimport org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;\nimport org.springframework.web.filter.CompositeFilter;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\nimport org.springframework.web.servlet.support.RequestDataValueProcessor;\n\n/**\n * Used to add a {@link RequestDataValueProcessor} for Spring MVC and Spring Security CSRF\n * integration. This configuration is added whenever {@link EnableWebMvc} is added by\n * <a href=\"\n * {@docRoot}/org/springframework/security/config/annotation/web/configuration/SpringWebMvcImportSelector.html\">SpringWebMvcImportSelector</a> and\n * the DispatcherServlet is present on the classpath. It also adds the\n * {@link AuthenticationPrincipalArgumentResolver} as a\n * {@link HandlerMethodArgumentResolver}.\n *\n * @author Rob Winch\n * @author Dan Zheng\n * @since 3.2\n */\nclass WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {\n\n\tprivate BeanResolver beanResolver;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate AnnotationTemplateExpressionDefaults templateDefaults;\n\n\t@Override\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {\n\t\tAuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();\n\t\tauthenticationPrincipalResolver.setBeanResolver(this.beanResolver);\n\t\tauthenticationPrincipalResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tauthenticationPrincipalResolver.setTemplateDefaults(this.templateDefaults);\n\t\targumentResolvers.add(authenticationPrincipalResolver);\n\t\targumentResolvers\n\t\t\t.add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());\n\t\tCurrentSecurityContextArgumentResolver currentSecurityContextArgumentResolver = new CurrentSecurityContextArgumentResolver();\n\t\tcurrentSecurityContextArgumentResolver.setBeanResolver(this.beanResolver);\n\t\tcurrentSecurityContextArgumentResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tcurrentSecurityContextArgumentResolver.setTemplateDefaults(this.templateDefaults);\n\t\targumentResolvers.add(currentSecurityContextArgumentResolver);\n\t\targumentResolvers.add(new CsrfTokenArgumentResolver());\n\t}\n\n\t@Bean\n\tRequestDataValueProcessor requestDataValueProcessor() {\n\t\treturn new CsrfRequestDataValueProcessor();\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory());\n\t\tif (applicationContext.getBeanNamesForType(SecurityContextHolderStrategy.class).length == 1) {\n\t\t\tthis.securityContextHolderStrategy = applicationContext.getBean(SecurityContextHolderStrategy.class);\n\t\t}\n\t\tif (applicationContext.getBeanNamesForType(AnnotationTemplateExpressionDefaults.class).length == 1) {\n\t\t\tthis.templateDefaults = applicationContext.getBean(AnnotationTemplateExpressionDefaults.class);\n\t\t}\n\t}\n\n\t/**\n\t * Extends {@link FilterChainProxy} to provide as much passivity as possible but\n\t * delegates to {@link CompositeFilter} for\n\t * {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.\n\t *\n\t * @deprecated see {@link WebSecurityConfiguration} for\n\t * {@link org.springframework.web.util.pattern.PathPattern} replacement\n\t */\n\t@Deprecated\n\tstatic class CompositeFilterChainProxy extends FilterChainProxy {\n\n\t\t/**\n\t\t * Used for {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}\n\t\t */\n\t\tprivate final Filter doFilterDelegate;\n\n\t\tprivate final FilterChainProxy springSecurityFilterChain;\n\n\t\t/**\n\t\t * Creates a new instance\n\t\t * @param filters the Filters to delegate to. One of which must be\n\t\t * FilterChainProxy.\n\t\t */\n\t\tCompositeFilterChainProxy(List<? extends Filter> filters) {\n\t\t\tthis.doFilterDelegate = createDoFilterDelegate(filters);\n\t\t\tthis.springSecurityFilterChain = findFilterChainProxy(filters);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterPropertiesSet() {\n\t\t\tthis.springSecurityFilterChain.afterPropertiesSet();\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tthis.doFilterDelegate.doFilter(request, response, chain);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<Filter> getFilters(String url) {\n\t\t\treturn this.springSecurityFilterChain.getFilters(url);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<SecurityFilterChain> getFilterChains() {\n\t\t\treturn this.springSecurityFilterChain.getFilterChains();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.springSecurityFilterChain.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setFilterChainValidator(FilterChainValidator filterChainValidator) {\n\t\t\tthis.springSecurityFilterChain.setFilterChainValidator(filterChainValidator);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setFilterChainDecorator(FilterChainDecorator filterChainDecorator) {\n\t\t\tthis.springSecurityFilterChain.setFilterChainDecorator(filterChainDecorator);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setFirewall(HttpFirewall firewall) {\n\t\t\tthis.springSecurityFilterChain.setFirewall(firewall);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {\n\t\t\tthis.springSecurityFilterChain.setRequestRejectedHandler(requestRejectedHandler);\n\t\t}\n\n\t\t/**\n\t\t * Used through reflection by Spring Security's Test support to lookup the\n\t\t * FilterChainProxy Filters for a specific HttpServletRequest.\n\t\t * @param request\n\t\t * @return\n\t\t */\n\t\tprivate List<? extends Filter> getFilters(HttpServletRequest request) {\n\t\t\tList<SecurityFilterChain> filterChains = this.springSecurityFilterChain.getFilterChains();\n\t\t\tfor (SecurityFilterChain chain : filterChains) {\n\t\t\t\tif (chain.matches(request)) {\n\t\t\t\t\treturn chain.getFilters();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t/**\n\t\t * Creates the Filter to delegate to for doFilter\n\t\t * @param filters the Filters to delegate to.\n\t\t * @return the Filter for doFilter\n\t\t */\n\t\tprivate static Filter createDoFilterDelegate(List<? extends Filter> filters) {\n\t\t\tCompositeFilter delegate = new CompositeFilter();\n\t\t\tdelegate.setFilters(filters);\n\t\t\treturn delegate;\n\t\t}\n\n\t\t/**\n\t\t * Find the FilterChainProxy in a List of Filter\n\t\t * @param filters\n\t\t * @return non-null FilterChainProxy\n\t\t * @throws IllegalStateException if the FilterChainProxy cannot be found\n\t\t */\n\t\tprivate static FilterChainProxy findFilterChainProxy(List<? extends Filter> filters) {\n\t\t\tfor (Filter filter : filters) {\n\t\t\t\tif (filter instanceof FilterChainProxy fcp) {\n\t\t\t\t\treturn fcp;\n\t\t\t\t}\n\t\t\t\tif (filter instanceof DebugFilter debugFilter) {\n\t\t\t\t\treturn debugFilter.getFilterChainProxy();\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new IllegalStateException(\"Couldn't find FilterChainProxy in \" + filters);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.DependsOn;\nimport org.springframework.context.annotation.ImportAware;\nimport org.springframework.core.OrderComparator;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.AnnotationAttributes;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.web.WebSecurityConfigurer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.builders.WebSecurity;\nimport org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;\nimport org.springframework.security.context.DelegatingApplicationListener;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;\nimport org.springframework.security.web.debug.DebugFilter;\nimport org.springframework.security.web.firewall.HttpFirewall;\nimport org.springframework.security.web.firewall.RequestRejectedHandler;\nimport org.springframework.web.filter.CompositeFilter;\nimport org.springframework.web.filter.ServletRequestPathFilter;\n\n/**\n * Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web\n * based security for Spring Security. It then exports the necessary beans. Customizations\n * can be made to {@link WebSecurity} by implementing {@link WebSecurityConfigurer} and\n * exposing it as a {@link Configuration} or exposing a {@link WebSecurityCustomizer}\n * bean. This configuration is imported when using {@link EnableWebSecurity}.\n *\n * @author Rob Winch\n * @author Keesun Baik\n * @author Yanming Zhou\n * @since 3.2\n * @see EnableWebSecurity\n * @see WebSecurity\n */\n@Configuration(proxyBeanMethods = false)\npublic class WebSecurityConfiguration implements ImportAware {\n\n\tprivate WebSecurity webSecurity;\n\n\tprivate Boolean debugEnabled;\n\n\tprivate List<SecurityFilterChain> securityFilterChains = Collections.emptyList();\n\n\tprivate List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();\n\n\t@Bean\n\tpublic static DelegatingApplicationListener delegatingApplicationListener() {\n\t\treturn new DelegatingApplicationListener();\n\t}\n\n\t@Bean\n\t@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)\n\tpublic SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {\n\t\treturn this.webSecurity.getExpressionHandler();\n\t}\n\n\t/**\n\t * Creates the Spring Security Filter Chain\n\t * @return the {@link Filter} that represents the security filter chain\n\t * @throws Exception\n\t */\n\t@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)\n\tpublic Filter springSecurityFilterChain(ObjectProvider<HttpSecurity> provider) throws Exception {\n\t\tboolean hasFilterChain = !this.securityFilterChains.isEmpty();\n\t\tif (!hasFilterChain) {\n\t\t\tthis.webSecurity.addSecurityFilterChainBuilder(() -> {\n\t\t\t\tHttpSecurity httpSecurity = provider.getObject();\n\t\t\t\thttpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());\n\t\t\t\thttpSecurity.formLogin(Customizer.withDefaults());\n\t\t\t\thttpSecurity.httpBasic(Customizer.withDefaults());\n\t\t\t\treturn httpSecurity.build();\n\t\t\t});\n\t\t}\n\t\tfor (SecurityFilterChain securityFilterChain : this.securityFilterChains) {\n\t\t\tthis.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);\n\t\t}\n\t\tfor (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {\n\t\t\tcustomizer.customize(this.webSecurity);\n\t\t}\n\t\treturn this.webSecurity.build();\n\t}\n\n\t/**\n\t * Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary to evaluate\n\t * privileges for a given web URI\n\t * @return the {@link WebInvocationPrivilegeEvaluator}\n\t */\n\t@Bean\n\t@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)\n\tpublic WebInvocationPrivilegeEvaluator privilegeEvaluator() {\n\t\treturn this.webSecurity.getPrivilegeEvaluator();\n\t}\n\n\t/**\n\t * Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>}\n\t * instances used to create the web configuration.\n\t * @param objectPostProcessor the {@link ObjectPostProcessor} used to create a\n\t * {@link WebSecurity} instance\n\t * @param beanFactory the bean factory to use to retrieve the relevant\n\t * {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to\n\t * create the web configuration\n\t * @throws Exception\n\t */\n\t@Autowired(required = false)\n\tpublic void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,\n\t\t\tConfigurableListableBeanFactory beanFactory) throws Exception {\n\t\tthis.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));\n\t\tif (this.debugEnabled != null) {\n\t\t\tthis.webSecurity.debug(this.debugEnabled);\n\t\t}\n\t\tList<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new AutowiredWebSecurityConfigurersIgnoreParents(\n\t\t\t\tbeanFactory)\n\t\t\t.getWebSecurityConfigurers();\n\t\twebSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);\n\t\tInteger previousOrder = null;\n\t\tObject previousConfig = null;\n\t\tfor (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {\n\t\t\tInteger order = AnnotationAwareOrderComparator.lookupOrder(config);\n\t\t\tif (previousOrder != null && previousOrder.equals(order)) {\n\t\t\t\tthrow new IllegalStateException(\"@Order on WebSecurityConfigurers must be unique. Order of \" + order\n\t\t\t\t\t\t+ \" was already used on \" + previousConfig + \", so it cannot be used on \" + config + \" too.\");\n\t\t\t}\n\t\t\tpreviousOrder = order;\n\t\t\tpreviousConfig = config;\n\t\t}\n\t\tfor (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {\n\t\t\tthis.webSecurity.apply(webSecurityConfigurer);\n\t\t}\n\t}\n\n\t@Autowired(required = false)\n\tvoid setFilterChains(List<SecurityFilterChain> securityFilterChains) {\n\t\tthis.securityFilterChains = securityFilterChains;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {\n\t\tthis.webSecurityCustomizers = webSecurityCustomizers;\n\t}\n\n\t@Bean\n\tpublic static RsaKeyConversionServicePostProcessor conversionServicePostProcessor() {\n\t\treturn new RsaKeyConversionServicePostProcessor();\n\t}\n\n\t@Override\n\tpublic void setImportMetadata(AnnotationMetadata importMetadata) {\n\t\tMap<String, Object> enableWebSecurityAttrMap = importMetadata\n\t\t\t.getAnnotationAttributes(EnableWebSecurity.class.getName());\n\t\tAnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);\n\t\tthis.debugEnabled = enableWebSecurityAttrs.getBoolean(\"debug\");\n\t\tif (this.webSecurity != null) {\n\t\t\tthis.webSecurity.debug(this.debugEnabled);\n\t\t}\n\t}\n\n\t/**\n\t * Used to ensure Spring MVC request matching is cached.\n\t *\n\t * Creates a {@link BeanDefinitionRegistryPostProcessor} that moves the\n\t * AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name\n\t * and then adds a {@link CompositeFilter} that contains\n\t * {@link ServletRequestPathFilter} and the original FilterChainProxy under the\n\t * original Bean name.\n\t * @return\n\t */\n\t@Bean\n\tstatic BeanDefinitionRegistryPostProcessor springSecurityPathPatternParserBeanDefinitionRegistryPostProcessor() {\n\t\treturn new BeanDefinitionRegistryPostProcessor() {\n\t\t\t@Override\n\t\t\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\t\t\tBeanDefinition filterChainProxy = registry\n\t\t\t\t\t.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);\n\n\t\t\t\tif (filterChainProxy.getResolvableType().isInstance(CompositeFilterChainProxy.class)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tBeanDefinitionBuilder pppCacheFilterBldr = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(ServletRequestPathFilter.class)\n\t\t\t\t\t.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\n\t\t\t\tManagedList<BeanMetadataElement> filters = new ManagedList<>();\n\t\t\t\tfilters.add(pppCacheFilterBldr.getBeanDefinition());\n\t\t\t\tfilters.add(filterChainProxy);\n\t\t\t\tBeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(CompositeFilterChainProxy.class)\n\t\t\t\t\t.addConstructorArgValue(filters);\n\n\t\t\t\tregistry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);\n\t\t\t\tregistry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,\n\t\t\t\t\t\tcompositeSpringSecurityFilterChainBldr.getBeanDefinition());\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * A custom version of the Spring provided AnnotationAwareOrderComparator that uses\n\t * {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class\n\t * instances for the {@link Order} annotation.\n\t *\n\t * @author Rob Winch\n\t * @since 3.2\n\t */\n\tprivate static class AnnotationAwareOrderComparator extends OrderComparator {\n\n\t\tprivate static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();\n\n\t\t@Override\n\t\tprotected int getOrder(Object obj) {\n\t\t\treturn lookupOrder(obj);\n\t\t}\n\n\t\tprivate static int lookupOrder(Object obj) {\n\t\t\tif (obj instanceof Ordered) {\n\t\t\t\treturn ((Ordered) obj).getOrder();\n\t\t\t}\n\t\t\tif (obj != null) {\n\t\t\t\tClass<?> clazz = ((obj instanceof Class) ? (Class<?>) obj : obj.getClass());\n\t\t\t\tOrder order = AnnotationUtils.findAnnotation(clazz, Order.class);\n\t\t\t\tif (order != null) {\n\t\t\t\t\treturn order.value();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Ordered.LOWEST_PRECEDENCE;\n\t\t}\n\n\t}\n\n\t/**\n\t * Extends {@link FilterChainProxy} to provide as much passivity as possible but\n\t * delegates to {@link CompositeFilter} for\n\t * {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.\n\t */\n\tstatic class CompositeFilterChainProxy extends FilterChainProxy {\n\n\t\t/**\n\t\t * Used for {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}\n\t\t */\n\t\tprivate final Filter doFilterDelegate;\n\n\t\tprivate final FilterChainProxy springSecurityFilterChain;\n\n\t\t/**\n\t\t * Creates a new instance\n\t\t * @param filters the Filters to delegate to. One of which must be\n\t\t * FilterChainProxy.\n\t\t */\n\t\tCompositeFilterChainProxy(List<? extends Filter> filters) {\n\t\t\tthis.doFilterDelegate = createDoFilterDelegate(filters);\n\t\t\tthis.springSecurityFilterChain = findFilterChainProxy(filters);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterPropertiesSet() {\n\t\t\tthis.springSecurityFilterChain.afterPropertiesSet();\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tthis.doFilterDelegate.doFilter(request, response, chain);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<Filter> getFilters(String url) {\n\t\t\treturn this.springSecurityFilterChain.getFilters(url);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<SecurityFilterChain> getFilterChains() {\n\t\t\treturn this.springSecurityFilterChain.getFilterChains();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.springSecurityFilterChain.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setFilterChainValidator(FilterChainValidator filterChainValidator) {\n\t\t\tthis.springSecurityFilterChain.setFilterChainValidator(filterChainValidator);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setFilterChainDecorator(FilterChainDecorator filterChainDecorator) {\n\t\t\tthis.springSecurityFilterChain.setFilterChainDecorator(filterChainDecorator);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setFirewall(HttpFirewall firewall) {\n\t\t\tthis.springSecurityFilterChain.setFirewall(firewall);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {\n\t\t\tthis.springSecurityFilterChain.setRequestRejectedHandler(requestRejectedHandler);\n\t\t}\n\n\t\t/**\n\t\t * Used through reflection by Spring Security's Test support to lookup the\n\t\t * FilterChainProxy Filters for a specific HttpServletRequest.\n\t\t * @param request\n\t\t * @return\n\t\t */\n\t\tprivate List<? extends Filter> getFilters(HttpServletRequest request) {\n\t\t\tList<SecurityFilterChain> filterChains = this.springSecurityFilterChain.getFilterChains();\n\t\t\tfor (SecurityFilterChain chain : filterChains) {\n\t\t\t\tif (chain.matches(request)) {\n\t\t\t\t\treturn chain.getFilters();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t/**\n\t\t * Creates the Filter to delegate to for doFilter\n\t\t * @param filters the Filters to delegate to.\n\t\t * @return the Filter for doFilter\n\t\t */\n\t\tprivate static Filter createDoFilterDelegate(List<? extends Filter> filters) {\n\t\t\tCompositeFilter delegate = new CompositeFilter();\n\t\t\tdelegate.setFilters(filters);\n\t\t\treturn delegate;\n\t\t}\n\n\t\t/**\n\t\t * Find the FilterChainProxy in a List of Filter\n\t\t * @param filters\n\t\t * @return non-null FilterChainProxy\n\t\t * @throws IllegalStateException if the FilterChainProxy cannot be found\n\t\t */\n\t\tprivate static FilterChainProxy findFilterChainProxy(List<? extends Filter> filters) {\n\t\t\tfor (Filter filter : filters) {\n\t\t\t\tif (filter instanceof FilterChainProxy fcp) {\n\t\t\t\t\treturn fcp;\n\t\t\t\t}\n\t\t\t\tif (filter instanceof DebugFilter debugFilter) {\n\t\t\t\t\treturn debugFilter.getFilterChainProxy();\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new IllegalStateException(\"Couldn't find FilterChainProxy in \" + filters);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityCustomizer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport org.springframework.security.config.annotation.web.builders.WebSecurity;\n\n/**\n * Callback interface for customizing {@link WebSecurity}.\n *\n * Beans of this type will automatically be used by {@link WebSecurityConfiguration} to\n * customize {@link WebSecurity}.\n *\n * Example usage:\n *\n * <pre>\n * &#064;Bean\n * public WebSecurityCustomizer ignoringCustomizer() {\n * \treturn (web) -&gt; web.ignoring().requestMatchers(\"/ignore1\", \"/ignore2\");\n * }\n * </pre>\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@FunctionalInterface\npublic interface WebSecurityCustomizer {\n\n\t/**\n\t * Performs the customizations on {@link WebSecurity}.\n\t * @param web the instance of {@link WebSecurity} to apply to customizations to\n\t */\n\tvoid customize(WebSecurity web);\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.accept.HeaderContentNegotiationStrategy;\n\n/**\n * Base class for configuring {@link AbstractAuthenticationFilterConfigurer}. This is\n * intended for internal use only.\n *\n * @param <T> refers to \"this\" for returning the current configurer\n * @param <F> refers to the {@link AbstractAuthenticationProcessingFilter} that is being\n * built\n * @author Rob Winch\n * @since 3.2\n * @see FormLoginConfigurer\n */\npublic abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>\n\t\textends AbstractHttpConfigurer<T, B> {\n\n\tprivate F authFilter;\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;\n\n\tprivate SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();\n\n\tprivate AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;\n\n\tprivate LoginUrlAuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate boolean customLoginPage;\n\n\tprivate String loginPage;\n\n\tprivate String loginProcessingUrl;\n\n\tprivate AuthenticationFailureHandler failureHandler;\n\n\tprivate boolean permitAll;\n\n\tprivate String failureUrl;\n\n\t/**\n\t * Creates a new instance with minimal defaults\n\t */\n\tprotected AbstractAuthenticationFilterConfigurer() {\n\t\tsetLoginPage(\"/login\");\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param authenticationFilter the {@link AbstractAuthenticationProcessingFilter} to\n\t * use\n\t * @param defaultLoginProcessingUrl the default URL to use for\n\t * {@link #loginProcessingUrl(String)}\n\t */\n\tprotected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) {\n\t\tthis();\n\t\tthis.authFilter = authenticationFilter;\n\t\tif (defaultLoginProcessingUrl != null) {\n\t\t\tloginProcessingUrl(defaultLoginProcessingUrl);\n\t\t}\n\t}\n\n\t/**\n\t * Specifies where users will be redirected after authenticating successfully if they\n\t * have not visited a secured page prior to authenticating. This is a shortcut for\n\t * calling {@link #defaultSuccessUrl(String, boolean)}.\n\t * @param defaultSuccessUrl the default success url\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic final T defaultSuccessUrl(String defaultSuccessUrl) {\n\t\treturn defaultSuccessUrl(defaultSuccessUrl, false);\n\t}\n\n\t/**\n\t * Specifies where users will be redirected after authenticating successfully if they\n\t * have not visited a secured page prior to authenticating or {@code alwaysUse} is\n\t * true. This is a shortcut for calling\n\t * {@link #successHandler(AuthenticationSuccessHandler)}.\n\t * @param defaultSuccessUrl the default success url\n\t * @param alwaysUse true if the {@code defaultSuccessUrl} should be used after\n\t * authentication despite if a protected page had been previously visited\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) {\n\t\tSavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();\n\t\thandler.setDefaultTargetUrl(defaultSuccessUrl);\n\t\thandler.setAlwaysUseDefaultTargetUrl(alwaysUse);\n\t\tthis.defaultSuccessHandler = handler;\n\t\treturn successHandler(handler);\n\t}\n\n\t/**\n\t * Specifies the URL to validate the credentials.\n\t * @param loginProcessingUrl the URL to validate username and password\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic T loginProcessingUrl(String loginProcessingUrl) {\n\t\tthis.loginProcessingUrl = loginProcessingUrl;\n\t\tthis.authFilter.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));\n\t\treturn getSelf();\n\t}\n\n\tpublic T securityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tthis.authFilter.setSecurityContextRepository(securityContextRepository);\n\t\treturn getSelf();\n\t}\n\n\t/**\n\t * Create the {@link RequestMatcher} given a loginProcessingUrl\n\t * @param loginProcessingUrl creates the {@link RequestMatcher} based upon the\n\t * loginProcessingUrl\n\t * @return the {@link RequestMatcher} to use based upon the loginProcessingUrl\n\t */\n\tprotected abstract RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl);\n\n\t/**\n\t * Specifies a custom {@link AuthenticationDetailsSource}. The default is\n\t * {@link WebAuthenticationDetailsSource}.\n\t * @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic final T authenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t\treturn getSelf();\n\t}\n\n\t/**\n\t * Specifies the {@link AuthenticationSuccessHandler} to be used. The default is\n\t * {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties\n\t * set.\n\t * @param successHandler the {@link AuthenticationSuccessHandler}.\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic final T successHandler(AuthenticationSuccessHandler successHandler) {\n\t\tthis.successHandler = successHandler;\n\t\treturn getSelf();\n\t}\n\n\t/**\n\t * Equivalent of invoking permitAll(true)\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic final T permitAll() {\n\t\treturn permitAll(true);\n\t}\n\n\t/**\n\t * Ensures the urls for {@link #failureUrl(String)} as well as for the\n\t * {@link HttpSecurityBuilder}, the {@link #getLoginPage} and\n\t * {@link #getLoginProcessingUrl} are granted access to any user.\n\t * @param permitAll true to grant access to the URLs false to skip this step\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic final T permitAll(boolean permitAll) {\n\t\tthis.permitAll = permitAll;\n\t\treturn getSelf();\n\t}\n\n\t/**\n\t * The URL to send users if authentication fails. This is a shortcut for invoking\n\t * {@link #failureHandler(AuthenticationFailureHandler)}. The default is\n\t * \"/login?error\".\n\t * @param authenticationFailureUrl the URL to send users if authentication fails (i.e.\n\t * \"/login?error\").\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic final T failureUrl(String authenticationFailureUrl) {\n\t\tT result = failureHandler(new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl));\n\t\tthis.failureUrl = authenticationFailureUrl;\n\t\treturn result;\n\t}\n\n\t/**\n\t * Specifies the {@link AuthenticationFailureHandler} to use when authentication\n\t * fails. The default is redirecting to \"/login?error\" using\n\t * {@link SimpleUrlAuthenticationFailureHandler}\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use\n\t * when authentication fails.\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic final T failureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tthis.failureUrl = null;\n\t\tthis.failureHandler = authenticationFailureHandler;\n\t\treturn getSelf();\n\t}\n\n\t@Override\n\tpublic void init(B http) {\n\t\tupdateAuthenticationDefaults();\n\t\tupdateAccessDefaults(http);\n\t\tregisterDefaultAuthenticationEntryPoint(http);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprotected final void registerDefaultAuthenticationEntryPoint(B http) {\n\t\tregisterAuthenticationEntryPoint(http, this.authenticationEntryPoint);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprotected final void registerAuthenticationEntryPoint(B http, AuthenticationEntryPoint authenticationEntryPoint) {\n\t\tExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptionHandling == null) {\n\t\t\treturn;\n\t\t}\n\t\texceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint),\n\t\t\t\tgetAuthenticationEntryPointMatcher(http));\n\t}\n\n\tprotected final RequestMatcher getAuthenticationEntryPointMatcher(B http) {\n\t\tContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);\n\t\tif (contentNegotiationStrategy == null) {\n\t\t\tcontentNegotiationStrategy = new HeaderContentNegotiationStrategy();\n\t\t}\n\t\tMediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_XHTML_XML, new MediaType(\"image\", \"*\"), MediaType.TEXT_HTML,\n\t\t\t\tMediaType.TEXT_PLAIN);\n\t\tmediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tRequestMatcher notXRequestedWith = new NegatedRequestMatcher(\n\t\t\t\tnew RequestHeaderRequestMatcher(\"X-Requested-With\", \"XMLHttpRequest\"));\n\t\treturn new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));\n\t}\n\n\t@Override\n\tpublic void configure(B http) {\n\t\tPortMapper portMapper = http.getSharedObject(PortMapper.class);\n\t\tif (portMapper != null) {\n\t\t\tthis.authenticationEntryPoint.setPortMapper(portMapper);\n\t\t}\n\t\tRequestCache requestCache = http.getSharedObject(RequestCache.class);\n\t\tif (requestCache != null) {\n\t\t\tthis.defaultSuccessHandler.setRequestCache(requestCache);\n\t\t}\n\t\tthis.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));\n\t\tthis.authFilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tthis.authFilter.setAuthenticationFailureHandler(this.failureHandler);\n\t\tif (this.authenticationDetailsSource != null) {\n\t\t\tthis.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);\n\t\t}\n\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy = http\n\t\t\t.getSharedObject(SessionAuthenticationStrategy.class);\n\t\tif (sessionAuthenticationStrategy != null) {\n\t\t\tthis.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);\n\t\t}\n\t\tRememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);\n\t\tif (rememberMeServices != null) {\n\t\t\tthis.authFilter.setRememberMeServices(rememberMeServices);\n\t\t}\n\t\tSecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);\n\t\tif (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {\n\t\t\tSecurityContextRepository securityContextRepository = securityContextConfigurer\n\t\t\t\t.getSecurityContextRepository();\n\t\t\tthis.authFilter.setSecurityContextRepository(securityContextRepository);\n\t\t}\n\t\tthis.authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tF filter = postProcess(this.authFilter);\n\t\thttp.addFilter(filter);\n\t}\n\n\t/**\n\t * <p>\n\t * Specifies the URL to send users to if login is required. If used with\n\t * {@link EnableWebSecurity} a default login page will be generated when this\n\t * attribute is not specified.\n\t * </p>\n\t *\n\t * <p>\n\t * If a URL is specified or this is not being used in conjunction with\n\t * {@link EnableWebSecurity}, users are required to process the specified URL to\n\t * generate a login page.\n\t * </p>\n\t */\n\tprotected T loginPage(String loginPage) {\n\t\tsetLoginPage(loginPage);\n\t\tupdateAuthenticationDefaults();\n\t\tthis.customLoginPage = true;\n\t\treturn getSelf();\n\t}\n\n\t/**\n\t * @return true if a custom login page has been specified, else false\n\t */\n\tpublic final boolean isCustomLoginPage() {\n\t\treturn this.customLoginPage;\n\t}\n\n\t/**\n\t * Gets the Authentication Filter\n\t * @return the Authentication Filter\n\t */\n\tprotected final F getAuthenticationFilter() {\n\t\treturn this.authFilter;\n\t}\n\n\t/**\n\t * Sets the Authentication Filter\n\t * @param authFilter the Authentication Filter\n\t */\n\tprotected final void setAuthenticationFilter(F authFilter) {\n\t\tthis.authFilter = authFilter;\n\t}\n\n\t/**\n\t * Gets the login page\n\t * @return the login page\n\t */\n\tprotected final String getLoginPage() {\n\t\treturn this.loginPage;\n\t}\n\n\t/**\n\t * Gets the Authentication Entry Point\n\t * @return the Authentication Entry Point\n\t */\n\tprotected final AuthenticationEntryPoint getAuthenticationEntryPoint() {\n\t\treturn this.authenticationEntryPoint;\n\t}\n\n\t/**\n\t * Gets the URL to submit an authentication request to (i.e. where username/password\n\t * must be submitted)\n\t * @return the URL to submit an authentication request to\n\t */\n\tprotected final String getLoginProcessingUrl() {\n\t\treturn this.loginProcessingUrl;\n\t}\n\n\t/**\n\t * Gets the URL to send users to if authentication fails\n\t * @return the URL to send users if authentication fails (e.g. \"/login?error\").\n\t */\n\tprotected final String getFailureUrl() {\n\t\treturn this.failureUrl;\n\t}\n\n\t/**\n\t * Updates the default values for authentication.\n\t */\n\tprotected final void updateAuthenticationDefaults() {\n\t\tif (this.loginProcessingUrl == null) {\n\t\t\tloginProcessingUrl(this.loginPage);\n\t\t}\n\t\tif (this.failureHandler == null) {\n\t\t\tfailureUrl(this.loginPage + \"?error\");\n\t\t}\n\t\tLogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(LogoutConfigurer.class);\n\t\tif (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {\n\t\t\tlogoutConfigurer.logoutSuccessUrl(this.loginPage + \"?logout\");\n\t\t}\n\t}\n\n\t/**\n\t * Updates the default values for access.\n\t */\n\tprotected final void updateAccessDefaults(B http) {\n\t\tif (this.permitAll) {\n\t\t\tPermitAllSupport.permitAll(http, this.loginPage, this.loginProcessingUrl, this.failureUrl);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the loginPage and updates the {@link AuthenticationEntryPoint}.\n\t * @param loginPage\n\t */\n\tprivate void setLoginPage(String loginPage) {\n\t\tthis.loginPage = loginPage;\n\t\tthis.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);\n\t}\n\n\tprivate <C> C getBeanOrNull(B http, Class<C> clazz) {\n\t\tApplicationContext context = http.getSharedObject(ApplicationContext.class);\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn context.getBeanProvider(clazz).getIfUnique();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate T getSelf() {\n\t\treturn (T) this;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.List;\n\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * A base class for registering {@link RequestMatcher}'s. For example, it might allow for\n * specifying which {@link RequestMatcher} require a certain level of authorization.\n *\n * @param <C> The object that is returned or Chained after creating the RequestMatcher\n * @author Rob Winch\n * @since 3.2\n * @see ChannelSecurityConfigurer\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. In the case of\n * method security, please see {@link SecurityAnnotationScanner} and\n * {@link AuthorizationManager}. In the case of channel security, please see\n * {@code HttpsRedirectFilter}. In the case of web security, please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\npublic abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends AbstractRequestMatcherRegistry<C> {\n\n\tprivate List<UrlMapping> urlMappings = new ArrayList<>();\n\n\tprivate List<RequestMatcher> unmappedMatchers;\n\n\t/**\n\t * Gets the {@link UrlMapping} added by subclasses in\n\t * {@link #chainRequestMatchers(java.util.List)}. May be empty.\n\t * @return the {@link UrlMapping} added by subclasses in\n\t * {@link #chainRequestMatchers(java.util.List)}\n\t */\n\tfinal List<UrlMapping> getUrlMappings() {\n\t\treturn this.urlMappings;\n\t}\n\n\t/**\n\t * Adds a {@link UrlMapping} added by subclasses in\n\t * {@link #chainRequestMatchers(java.util.List)} and resets the unmapped\n\t * {@link RequestMatcher}'s.\n\t * @param urlMapping {@link UrlMapping} the mapping to add\n\t */\n\tfinal void addMapping(UrlMapping urlMapping) {\n\t\tthis.unmappedMatchers = null;\n\t\tthis.urlMappings.add(urlMapping);\n\t}\n\n\t/**\n\t * Marks the {@link RequestMatcher}'s as unmapped and then calls\n\t * {@link #chainRequestMatchersInternal(List)}.\n\t * @param requestMatchers the {@link RequestMatcher} instances that were created\n\t * @return the chained Object for the subclass which allows association of something\n\t * else to the {@link RequestMatcher}\n\t */\n\t@Override\n\tprotected final C chainRequestMatchers(List<RequestMatcher> requestMatchers) {\n\t\tthis.unmappedMatchers = requestMatchers;\n\t\treturn chainRequestMatchersInternal(requestMatchers);\n\t}\n\n\t/**\n\t * Subclasses should implement this method for returning the object that is chained to\n\t * the creation of the {@link RequestMatcher} instances.\n\t * @param requestMatchers the {@link RequestMatcher} instances that were created\n\t * @return the chained Object for the subclass which allows association of something\n\t * else to the {@link RequestMatcher}\n\t */\n\tprotected abstract C chainRequestMatchersInternal(List<RequestMatcher> requestMatchers);\n\n\t/**\n\t * Adds a {@link UrlMapping} added by subclasses in\n\t * {@link #chainRequestMatchers(java.util.List)} at a particular index.\n\t * @param index the index to add a {@link UrlMapping}\n\t * @param urlMapping {@link UrlMapping} the mapping to add\n\t */\n\tfinal void addMapping(int index, UrlMapping urlMapping) {\n\t\tthis.urlMappings.add(index, urlMapping);\n\t}\n\n\t/**\n\t * Creates the mapping of {@link RequestMatcher} to {@link Collection} of\n\t * {@link ConfigAttribute} instances\n\t * @return the mapping of {@link RequestMatcher} to {@link Collection} of\n\t * {@link ConfigAttribute} instances. Cannot be null.\n\t */\n\tfinal LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> createRequestMap() {\n\t\tAssert.state(this.unmappedMatchers == null, () -> \"An incomplete mapping was found for \" + this.unmappedMatchers\n\t\t\t\t+ \". Try completing it with something like requestUrls().<something>.hasRole('USER')\");\n\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();\n\t\tfor (UrlMapping mapping : getUrlMappings()) {\n\t\t\tRequestMatcher matcher = mapping.getRequestMatcher();\n\t\t\tCollection<ConfigAttribute> configAttrs = mapping.getConfigAttrs();\n\t\t\trequestMap.put(matcher, configAttrs);\n\t\t}\n\t\treturn requestMap;\n\t}\n\n\t/**\n\t * A mapping of {@link RequestMatcher} to {@link Collection} of\n\t * {@link ConfigAttribute} instances\n\t */\n\tstatic final class UrlMapping {\n\n\t\tprivate final RequestMatcher requestMatcher;\n\n\t\tprivate final Collection<ConfigAttribute> configAttrs;\n\n\t\tUrlMapping(RequestMatcher requestMatcher, Collection<ConfigAttribute> configAttrs) {\n\t\t\tthis.requestMatcher = requestMatcher;\n\t\t\tthis.configAttrs = configAttrs;\n\t\t}\n\n\t\tRequestMatcher getRequestMatcher() {\n\t\t\treturn this.requestMatcher;\n\t\t}\n\n\t\tCollection<ConfigAttribute> getConfigAttrs() {\n\t\t\treturn this.configAttrs;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\n\n/**\n * Adds a convenient base class for {@link SecurityConfigurer} instances that operate on\n * {@link HttpSecurity}.\n *\n * @author Rob Winch\n * @author Ding Hao\n */\npublic abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>\n\t\textends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy;\n\n\t/**\n\t * Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh\n\t * version of the configuration can be applied.\n\t * @return the {@link HttpSecurityBuilder} for additional customizations\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic B disable() {\n\t\tgetBuilder().removeConfigurer(getClass());\n\t\treturn getBuilder();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {\n\t\taddObjectPostProcessor(objectPostProcessor);\n\t\treturn (T) this;\n\t}\n\n\tprotected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {\n\t\tif (this.securityContextHolderStrategy != null) {\n\t\t\treturn this.securityContextHolderStrategy;\n\t\t}\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\tthis.securityContextHolderStrategy = context.getBeanProvider(SecurityContextHolderStrategy.class)\n\t\t\t.getIfUnique(SecurityContextHolder::getContextHolderStrategy);\n\t\treturn this.securityContextHolderStrategy;\n\t}\n\n\tprotected PathPatternRequestMatcher.Builder getRequestMatcherBuilder() {\n\t\treturn getBuilder().getSharedObject(PathPatternRequestMatcher.Builder.class);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.List;\nimport java.util.UUID;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationProvider;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\n\n/**\n * Configures Anonymous authentication (i.e. populate an {@link Authentication} that\n * represents an anonymous user instead of having a null value) for an\n * {@link HttpSecurity}. Specifically this will configure an\n * {@link AnonymousAuthenticationFilter} and an {@link AnonymousAuthenticationProvider}.\n * All properties have reasonable defaults, so no additional configuration is required\n * other than applying this {@link SecurityConfigurer}.\n *\n * @author Rob Winch\n * @author DingHao\n * @since 3.2\n */\npublic final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<AnonymousConfigurer<H>, H> {\n\n\tprivate String key;\n\n\tprivate AuthenticationProvider authenticationProvider;\n\n\tprivate AnonymousAuthenticationFilter authenticationFilter;\n\n\tprivate Object principal = \"anonymousUser\";\n\n\tprivate List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\");\n\n\tprivate String computedKey;\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#anonymous(Customizer)\n\t */\n\tpublic AnonymousConfigurer() {\n\t}\n\n\t/**\n\t * Sets the key to identify tokens created for anonymous authentication. Default is a\n\t * secure randomly generated key.\n\t * @param key the key to identify tokens created for anonymous authentication. Default\n\t * is a secure randomly generated key.\n\t * @return the {@link AnonymousConfigurer} for further customization of anonymous\n\t * authentication\n\t */\n\tpublic AnonymousConfigurer<H> key(String key) {\n\t\tthis.key = key;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the principal for {@link Authentication} objects of anonymous users\n\t * @param principal used for the {@link Authentication} object of anonymous users\n\t * @return the {@link AnonymousConfigurer} for further customization of anonymous\n\t * authentication\n\t */\n\tpublic AnonymousConfigurer<H> principal(Object principal) {\n\t\tthis.principal = principal;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link org.springframework.security.core.Authentication#getAuthorities()}\n\t * for anonymous users\n\t * @param authorities Sets the\n\t * {@link org.springframework.security.core.Authentication#getAuthorities()} for\n\t * anonymous users\n\t * @return the {@link AnonymousConfigurer} for further customization of anonymous\n\t * authentication\n\t */\n\tpublic AnonymousConfigurer<H> authorities(List<GrantedAuthority> authorities) {\n\t\tthis.authorities = authorities;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link org.springframework.security.core.Authentication#getAuthorities()}\n\t * for anonymous users\n\t * @param authorities Sets the\n\t * {@link org.springframework.security.core.Authentication#getAuthorities()} for\n\t * anonymous users (i.e. \"ROLE_ANONYMOUS\")\n\t * @return the {@link AnonymousConfigurer} for further customization of anonymous\n\t * authentication\n\t */\n\tpublic AnonymousConfigurer<H> authorities(String... authorities) {\n\t\treturn authorities(AuthorityUtils.createAuthorityList(authorities));\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationProvider} used to validate an anonymous user. If this\n\t * is set, no attributes on the {@link AnonymousConfigurer} will be set on the\n\t * {@link AuthenticationProvider}.\n\t * @param authenticationProvider the {@link AuthenticationProvider} used to validate\n\t * an anonymous user. Default is {@link AnonymousAuthenticationProvider}\n\t * @return the {@link AnonymousConfigurer} for further customization of anonymous\n\t * authentication\n\t */\n\tpublic AnonymousConfigurer<H> authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tthis.authenticationProvider = authenticationProvider;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AnonymousAuthenticationFilter} used to populate an anonymous user.\n\t * If this is set, no attributes on the {@link AnonymousConfigurer} will be set on the\n\t * {@link AnonymousAuthenticationFilter}.\n\t * @param authenticationFilter the {@link AnonymousAuthenticationFilter} used to\n\t * populate an anonymous user.\n\t * @return the {@link AnonymousConfigurer} for further customization of anonymous\n\t * authentication\n\t */\n\tpublic AnonymousConfigurer<H> authenticationFilter(AnonymousAuthenticationFilter authenticationFilter) {\n\t\tthis.authenticationFilter = authenticationFilter;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\tif (this.authenticationProvider == null) {\n\t\t\tthis.authenticationProvider = new AnonymousAuthenticationProvider(getKey());\n\t\t}\n\t\tthis.authenticationProvider = postProcess(this.authenticationProvider);\n\t\thttp.authenticationProvider(this.authenticationProvider);\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tif (this.authenticationFilter == null) {\n\t\t\tthis.authenticationFilter = new AnonymousAuthenticationFilter(getKey(), this.principal, this.authorities);\n\t\t}\n\t\tthis.authenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tthis.authenticationFilter.afterPropertiesSet();\n\t\thttp.addFilter(postProcess(this.authenticationFilter));\n\t}\n\n\tprivate String getKey() {\n\t\tif (this.computedKey != null) {\n\t\t\treturn this.computedKey;\n\t\t}\n\t\tif (this.key == null) {\n\t\t\tthis.computedKey = UUID.randomUUID().toString();\n\t\t}\n\t\telse {\n\t\t\tthis.computedKey = this.key;\n\t\t}\n\t\treturn this.computedKey;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.List;\nimport java.util.function.Function;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.AuthorizationManagers;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.authorization.SpringAuthorizationEventPublisher;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\nimport org.springframework.util.Assert;\n\n/**\n * Adds a URL based authorization using {@link AuthorizationManager}.\n *\n * @param <H> the type of {@link HttpSecurityBuilder} that is being configured.\n * @author Evgeniy Cheban\n * @author Steve Riesenberg\n * @since 5.5\n */\npublic final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {\n\n\tprivate final AuthorizationManagerRequestMatcherRegistry registry;\n\n\tprivate final AuthorizationEventPublisher publisher;\n\n\tprivate final AuthorizationManagerFactory<? super RequestAuthorizationContext> authorizationManagerFactory;\n\n\tprivate ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> postProcessor = ObjectPostProcessor\n\t\t.identity();\n\n\t/**\n\t * Creates an instance.\n\t * @param context the {@link ApplicationContext} to use\n\t */\n\tpublic AuthorizeHttpRequestsConfigurer(ApplicationContext context) {\n\t\tthis.registry = new AuthorizationManagerRequestMatcherRegistry(context);\n\t\tif (context.getBeanNamesForType(AuthorizationEventPublisher.class).length > 0) {\n\t\t\tthis.publisher = context.getBean(AuthorizationEventPublisher.class);\n\t\t}\n\t\telse {\n\t\t\tthis.publisher = new SpringAuthorizationEventPublisher(context);\n\t\t}\n\t\tthis.authorizationManagerFactory = getAuthorizationManagerFactory(context);\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,\n\t\t\t\tResolvableType.forClassWithGenerics(AuthorizationManager.class, HttpServletRequest.class));\n\t\tObjectProvider<ObjectPostProcessor<AuthorizationManager<HttpServletRequest>>> provider = context\n\t\t\t.getBeanProvider(type);\n\t\tprovider.ifUnique((postProcessor) -> this.postProcessor = postProcessor);\n\t}\n\n\tprivate AuthorizationManagerFactory<? super RequestAuthorizationContext> getAuthorizationManagerFactory(\n\t\t\tApplicationContext context) {\n\t\tResolvableType authorizationManagerFactoryType = ResolvableType\n\t\t\t.forClassWithGenerics(AuthorizationManagerFactory.class, RequestAuthorizationContext.class);\n\n\t\t// Handle fallback to generic type\n\t\tif (context.getBeanNamesForType(authorizationManagerFactoryType).length == 0) {\n\t\t\tauthorizationManagerFactoryType = ResolvableType.forClassWithGenerics(AuthorizationManagerFactory.class,\n\t\t\t\t\tObject.class);\n\t\t}\n\n\t\tObjectProvider<AuthorizationManagerFactory<RequestAuthorizationContext>> authorizationManagerFactoryProvider = context\n\t\t\t.getBeanProvider(authorizationManagerFactoryType);\n\n\t\treturn authorizationManagerFactoryProvider.getIfAvailable(() -> {\n\t\t\tRoleHierarchy roleHierarchy = context.getBeanProvider(RoleHierarchy.class)\n\t\t\t\t.getIfAvailable(NullRoleHierarchy::new);\n\t\t\tGrantedAuthorityDefaults grantedAuthorityDefaults = context.getBeanProvider(GrantedAuthorityDefaults.class)\n\t\t\t\t.getIfAvailable();\n\t\t\tString rolePrefix = (grantedAuthorityDefaults != null) ? grantedAuthorityDefaults.getRolePrefix() : \"ROLE_\";\n\n\t\t\tDefaultAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();\n\t\t\tauthorizationManagerFactory.setRoleHierarchy(roleHierarchy);\n\t\t\tauthorizationManagerFactory.setRolePrefix(rolePrefix);\n\n\t\t\treturn authorizationManagerFactory;\n\t\t});\n\t}\n\n\t/**\n\t * The {@link AuthorizationManagerRequestMatcherRegistry} is what users will interact\n\t * with after applying the {@link AuthorizeHttpRequestsConfigurer}.\n\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t * customizations\n\t */\n\tpublic AuthorizationManagerRequestMatcherRegistry getRegistry() {\n\t\treturn this.registry;\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tAuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();\n\t\tAuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);\n\t\tauthorizationFilter.setAuthorizationEventPublisher(this.publisher);\n\t\tauthorizationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\thttp.addFilter(postProcess(authorizationFilter));\n\t}\n\n\tprivate AuthorizationManagerRequestMatcherRegistry addMapping(List<? extends RequestMatcher> matchers,\n\t\t\tAuthorizationManager<? super RequestAuthorizationContext> manager) {\n\t\tfor (RequestMatcher matcher : matchers) {\n\t\t\tthis.registry.addMapping(matcher, manager);\n\t\t}\n\t\treturn this.registry;\n\t}\n\n\tAuthorizationManagerRequestMatcherRegistry addFirst(RequestMatcher matcher,\n\t\t\tAuthorizationManager<RequestAuthorizationContext> manager) {\n\t\tthis.registry.addFirst(matcher, manager);\n\t\treturn this.registry;\n\t}\n\n\t/**\n\t * Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.\n\t *\n\t * @author Evgeniy Cheban\n\t */\n\tpublic final class AuthorizationManagerRequestMatcherRegistry\n\t\t\textends AbstractRequestMatcherRegistry<AuthorizedUrl> {\n\n\t\tprivate final RequestMatcherDelegatingAuthorizationManager.Builder managerBuilder = RequestMatcherDelegatingAuthorizationManager\n\t\t\t.builder();\n\n\t\tprivate List<RequestMatcher> unmappedMatchers;\n\n\t\tprivate int mappingCount;\n\n\t\tprivate AuthorizationManagerRequestMatcherRegistry(ApplicationContext context) {\n\t\t\tsetApplicationContext(context);\n\t\t}\n\n\t\tprivate void addMapping(RequestMatcher matcher,\n\t\t\t\tAuthorizationManager<? super RequestAuthorizationContext> manager) {\n\t\t\tthis.unmappedMatchers = null;\n\t\t\tthis.managerBuilder.add(matcher, manager);\n\t\t\tthis.mappingCount++;\n\t\t}\n\n\t\tprivate void addFirst(RequestMatcher matcher,\n\t\t\t\tAuthorizationManager<? super RequestAuthorizationContext> manager) {\n\t\t\tthis.unmappedMatchers = null;\n\t\t\tthis.managerBuilder.mappings((m) -> m.add(0, new RequestMatcherEntry<>(matcher, manager)));\n\t\t\tthis.mappingCount++;\n\t\t}\n\n\t\tprivate AuthorizationManager<HttpServletRequest> createAuthorizationManager() {\n\t\t\tAssert.state(this.unmappedMatchers == null,\n\t\t\t\t\t() -> \"An incomplete mapping was found for \" + this.unmappedMatchers\n\t\t\t\t\t\t\t+ \". Try completing it with something like requestUrls().<something>.hasRole('USER')\");\n\t\t\tAssert.state(this.mappingCount > 0,\n\t\t\t\t\t\"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())\");\n\t\t\tAuthorizationManager<HttpServletRequest> manager = postProcess(\n\t\t\t\t\t(AuthorizationManager<HttpServletRequest>) this.managerBuilder.build());\n\t\t\treturn AuthorizeHttpRequestsConfigurer.this.postProcessor.postProcess(manager);\n\t\t}\n\n\t\t@Override\n\t\tprotected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {\n\t\t\tthis.unmappedMatchers = requestMatchers;\n\t\t\treturn new AuthorizedUrl(requestMatchers, AuthorizeHttpRequestsConfigurer.this.authorizationManagerFactory);\n\t\t}\n\n\t\t/**\n\t\t * Adds an {@link ObjectPostProcessor} for this class.\n\t\t * @param objectPostProcessor the {@link ObjectPostProcessor} to use\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry withObjectPostProcessor(\n\t\t\t\tObjectPostProcessor<?> objectPostProcessor) {\n\t\t\taddObjectPostProcessor(objectPostProcessor);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t/**\n\t * An object that allows configuring the {@link AuthorizationManager} for\n\t * {@link RequestMatcher}s.\n\t *\n\t * @author Evgeniy Cheban\n\t * @author Josh Cummings\n\t */\n\tpublic class AuthorizedUrl {\n\n\t\tprivate final List<? extends RequestMatcher> matchers;\n\n\t\tprivate AuthorizationManagerFactory<? super RequestAuthorizationContext> authorizationManagerFactory;\n\n\t\tprivate boolean not;\n\n\t\t/**\n\t\t * Creates an instance.\n\t\t * @param matchers the {@link RequestMatcher} instances to map\n\t\t * @param authorizationManagerFactory the {@link AuthorizationManagerFactory} for\n\t\t * creating instances of {@link AuthorizationManager}\n\t\t */\n\t\tAuthorizedUrl(List<? extends RequestMatcher> matchers,\n\t\t\t\tAuthorizationManagerFactory<? super RequestAuthorizationContext> authorizationManagerFactory) {\n\t\t\tthis.matchers = matchers;\n\t\t\tthis.authorizationManagerFactory = authorizationManagerFactory;\n\t\t}\n\n\t\tprotected List<? extends RequestMatcher> getMatchers() {\n\t\t\treturn this.matchers;\n\t\t}\n\n\t\tvoid setAuthorizationManagerFactory(\n\t\t\t\tAuthorizationManagerFactory<? super RequestAuthorizationContext> authorizationManagerFactory) {\n\t\t\tthis.authorizationManagerFactory = authorizationManagerFactory;\n\t\t}\n\n\t\t/**\n\t\t * Negates the following authorization rule.\n\t\t * @return the {@link AuthorizedUrl} for further customization\n\t\t * @since 6.3\n\t\t */\n\t\tpublic AuthorizedUrl not() {\n\t\t\tthis.not = true;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specify that URLs are allowed by anyone.\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry permitAll() {\n\t\t\treturn access(this.authorizationManagerFactory.permitAll());\n\t\t}\n\n\t\t/**\n\t\t * Specify that URLs are not allowed by anyone.\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry denyAll() {\n\t\t\treturn access(this.authorizationManagerFactory.denyAll());\n\t\t}\n\n\t\t/**\n\t\t * Specifies a user requires a role.\n\t\t * @param role the role that should be required which is prepended with ROLE_\n\t\t * automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_\n\t\t * @return {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry hasRole(String role) {\n\t\t\treturn access(this.authorizationManagerFactory.hasRole(role));\n\t\t}\n\n\t\t/**\n\t\t * Specifies that a user requires one of many roles.\n\t\t * @param roles the roles that the user should have at least one of (i.e. ADMIN,\n\t\t * USER, etc). Each role should not start with ROLE_ since it is automatically\n\t\t * prepended already\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry hasAnyRole(String... roles) {\n\t\t\treturn access(this.authorizationManagerFactory.hasAnyRole(roles));\n\t\t}\n\n\t\t/**\n\t\t * Specifies that a user requires all the provided roles.\n\t\t * @param roles the roles that the user should have (i.e. ADMIN, USER, etc). Each\n\t\t * role should not start with ROLE_ since it is automatically prepended already\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry hasAllRoles(String... roles) {\n\t\t\treturn access(this.authorizationManagerFactory.hasAllRoles(roles));\n\t\t}\n\n\t\t/**\n\t\t * Specifies a user requires an authority.\n\t\t * @param authority the authority that should be required\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {\n\t\t\treturn access(this.authorizationManagerFactory.hasAuthority(authority));\n\t\t}\n\n\t\t/**\n\t\t * Specifies that a user requires one of many authorities.\n\t\t * @param authorities the authorities that the user should have at least one of\n\t\t * (i.e. ROLE_USER, ROLE_ADMIN, etc)\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {\n\t\t\treturn access(this.authorizationManagerFactory.hasAnyAuthority(authorities));\n\t\t}\n\n\t\t/**\n\t\t * Specifies that a user requires all the provided authorities.\n\t\t * @param authorities the authorities that the user should have (i.e. ROLE_USER,\n\t\t * ROLE_ADMIN, etc)\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry hasAllAuthorities(String... authorities) {\n\t\t\treturn access(this.authorizationManagerFactory.hasAllAuthorities(authorities));\n\t\t}\n\n\t\t/**\n\t\t * Specify that URLs are allowed by any authenticated user.\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry authenticated() {\n\t\t\treturn access(this.authorizationManagerFactory.authenticated());\n\t\t}\n\n\t\t/**\n\t\t * Specify that URLs are allowed by users who have authenticated and were not\n\t\t * \"remembered\".\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customization\n\t\t * @since 5.8\n\t\t * @see RememberMeConfigurer\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry fullyAuthenticated() {\n\t\t\treturn access(this.authorizationManagerFactory.fullyAuthenticated());\n\t\t}\n\n\t\t/**\n\t\t * Specify that URLs are allowed by users that have been remembered.\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customization\n\t\t * @since 5.8\n\t\t * @see RememberMeConfigurer\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry rememberMe() {\n\t\t\treturn access(this.authorizationManagerFactory.rememberMe());\n\t\t}\n\n\t\t/**\n\t\t * Specify that URLs are allowed by anonymous users.\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customization\n\t\t * @since 5.8\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry anonymous() {\n\t\t\treturn access(this.authorizationManagerFactory.anonymous());\n\t\t}\n\n\t\t/**\n\t\t * Specify that a path variable in URL to be compared.\n\t\t *\n\t\t * <p>\n\t\t * For example, <pre>\n\t\t * requestMatchers(\"/user/{username}\").hasVariable(\"username\").equalTo(Authentication::getName)\n\t\t * </pre>\n\t\t * @param variable the variable in URL template to compare.\n\t\t * @return {@link AuthorizedUrlVariable} for further customization.\n\t\t * @since 6.3\n\t\t */\n\t\tpublic AuthorizedUrlVariable hasVariable(String variable) {\n\t\t\treturn new AuthorizedUrlVariable(variable);\n\t\t}\n\n\t\t/**\n\t\t * Allows specifying a custom {@link AuthorizationManager}.\n\t\t * @param manager the {@link AuthorizationManager} to use\n\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t * customizations\n\t\t */\n\t\tpublic AuthorizationManagerRequestMatcherRegistry access(\n\t\t\t\tAuthorizationManager<? super RequestAuthorizationContext> manager) {\n\t\t\tAssert.notNull(manager, \"manager cannot be null\");\n\t\t\treturn (this.not)\n\t\t\t\t\t? AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, AuthorizationManagers.not(manager))\n\t\t\t\t\t: AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);\n\t\t}\n\n\t\t/**\n\t\t * An object that allows configuring {@link RequestMatcher}s with URI path\n\t\t * variables\n\t\t *\n\t\t * @author Taehong Kim\n\t\t * @since 6.3\n\t\t */\n\t\tpublic final class AuthorizedUrlVariable {\n\n\t\t\tprivate final String variable;\n\n\t\t\tprivate AuthorizedUrlVariable(String variable) {\n\t\t\t\tthis.variable = variable;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Compares the value of a path variable in the URI with an `Authentication`\n\t\t\t * attribute\n\t\t\t * <p>\n\t\t\t * For example, <pre>\n\t\t\t * requestMatchers(\"/user/{username}\").hasVariable(\"username\").equalTo(Authentication::getName));\n\t\t\t * </pre>\n\t\t\t * @param function a function to get value from {@link Authentication}.\n\t\t\t * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further\n\t\t\t * customization.\n\t\t\t */\n\t\t\tpublic AuthorizationManagerRequestMatcherRegistry equalTo(Function<Authentication, String> function) {\n\t\t\t\treturn access((auth, requestContext) -> {\n\t\t\t\t\tString value = requestContext.getVariables().get(this.variable);\n\t\t\t\t\treturn new AuthorizationDecision(function.apply(auth.get()).equals(value));\n\t\t\t\t});\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.List;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;\nimport org.springframework.security.web.access.channel.ChannelProcessingFilter;\nimport org.springframework.security.web.access.channel.ChannelProcessor;\nimport org.springframework.security.web.access.channel.InsecureChannelProcessor;\nimport org.springframework.security.web.access.channel.RetryWithHttpEntryPoint;\nimport org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint;\nimport org.springframework.security.web.access.channel.SecureChannelProcessor;\nimport org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Adds channel security (i.e. requires HTTPS or HTTP) to an application. In order for\n * {@link ChannelSecurityConfigurer} to be useful, at least one {@link RequestMatcher}\n * should be mapped to HTTP or HTTPS.\n *\n * <p>\n * By default an {@link InsecureChannelProcessor} and a {@link SecureChannelProcessor}\n * will be registered.\n * </p>\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link ChannelProcessingFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared objects are created.\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link PortMapper} is used to create the default {@link ChannelProcessor} instances\n * </li>\n * </ul>\n *\n * @param <H> the type of {@link HttpSecurityBuilder} that is being configured\n * @author Rob Winch\n * @author Onur Kagan Ozcan\n * @since 3.2\n * @deprecated please use {@link HttpsRedirectConfigurer} instead\n */\n@Deprecated\npublic final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<ChannelSecurityConfigurer<H>, H> {\n\n\tprivate ChannelProcessingFilter channelFilter = new ChannelProcessingFilter();\n\n\tprivate LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();\n\n\tprivate List<ChannelProcessor> channelProcessors;\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate final ChannelRequestMatcherRegistry REGISTRY;\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#requiresChannel(Customizer)\n\t */\n\tpublic ChannelSecurityConfigurer(ApplicationContext context) {\n\t\tthis.REGISTRY = new ChannelRequestMatcherRegistry(context);\n\t}\n\n\tpublic ChannelRequestMatcherRegistry getRegistry() {\n\t\treturn this.REGISTRY;\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tChannelDecisionManagerImpl channelDecisionManager = new ChannelDecisionManagerImpl();\n\t\tchannelDecisionManager.setChannelProcessors(getChannelProcessors(http));\n\t\tchannelDecisionManager = postProcess(channelDecisionManager);\n\t\tthis.channelFilter.setChannelDecisionManager(channelDecisionManager);\n\t\tDefaultFilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource = new DefaultFilterInvocationSecurityMetadataSource(\n\t\t\t\tthis.requestMap);\n\t\tthis.channelFilter.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);\n\t\tthis.channelFilter = postProcess(this.channelFilter);\n\t\thttp.addFilter(this.channelFilter);\n\t}\n\n\tprivate List<ChannelProcessor> getChannelProcessors(H http) {\n\t\tif (this.channelProcessors != null) {\n\t\t\treturn this.channelProcessors;\n\t\t}\n\t\tInsecureChannelProcessor insecureChannelProcessor = new InsecureChannelProcessor();\n\t\tSecureChannelProcessor secureChannelProcessor = new SecureChannelProcessor();\n\t\tPortMapper portMapper = http.getSharedObject(PortMapper.class);\n\t\tif (portMapper != null) {\n\t\t\tRetryWithHttpEntryPoint httpEntryPoint = new RetryWithHttpEntryPoint();\n\t\t\thttpEntryPoint.setPortMapper(portMapper);\n\t\t\thttpEntryPoint.setRedirectStrategy(this.redirectStrategy);\n\t\t\tinsecureChannelProcessor.setEntryPoint(httpEntryPoint);\n\t\t\tRetryWithHttpsEntryPoint httpsEntryPoint = new RetryWithHttpsEntryPoint();\n\t\t\thttpsEntryPoint.setPortMapper(portMapper);\n\t\t\thttpsEntryPoint.setRedirectStrategy(this.redirectStrategy);\n\t\t\tsecureChannelProcessor.setEntryPoint(httpsEntryPoint);\n\t\t}\n\t\tinsecureChannelProcessor = postProcess(insecureChannelProcessor);\n\t\tsecureChannelProcessor = postProcess(secureChannelProcessor);\n\t\treturn Arrays.asList(insecureChannelProcessor, secureChannelProcessor);\n\t}\n\n\tprivate ChannelRequestMatcherRegistry addAttribute(String attribute, List<? extends RequestMatcher> matchers) {\n\t\tfor (RequestMatcher matcher : matchers) {\n\t\t\tCollection<ConfigAttribute> attrs = Arrays.asList(new SecurityConfig(attribute));\n\t\t\tthis.requestMap.put(matcher, attrs);\n\t\t}\n\t\treturn this.REGISTRY;\n\t}\n\n\t/**\n\t * @deprecated no replacement planned\n\t */\n\t@Deprecated\n\tpublic final class ChannelRequestMatcherRegistry\n\t\t\textends AbstractConfigAttributeRequestMatcherRegistry<RequiresChannelUrl> {\n\n\t\tprivate ChannelRequestMatcherRegistry(ApplicationContext context) {\n\t\t\tsetApplicationContext(context);\n\t\t}\n\n\t\t@Override\n\t\tprotected RequiresChannelUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {\n\t\t\treturn new RequiresChannelUrl(requestMatchers);\n\t\t}\n\n\t\t/**\n\t\t * Adds an {@link ObjectPostProcessor} for this class.\n\t\t * @param objectPostProcessor\n\t\t * @return the {@link ChannelSecurityConfigurer} for further customizations\n\t\t */\n\t\tpublic ChannelRequestMatcherRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {\n\t\t\taddObjectPostProcessor(objectPostProcessor);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link ChannelProcessor} instances to use in\n\t\t * {@link ChannelDecisionManagerImpl}\n\t\t * @param channelProcessors\n\t\t * @return the {@link ChannelSecurityConfigurer} for further customizations\n\t\t */\n\t\tpublic ChannelRequestMatcherRegistry channelProcessors(List<ChannelProcessor> channelProcessors) {\n\t\t\tChannelSecurityConfigurer.this.channelProcessors = channelProcessors;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link RedirectStrategy} instances to use in\n\t\t * {@link RetryWithHttpEntryPoint} and {@link RetryWithHttpsEntryPoint}\n\t\t * @param redirectStrategy\n\t\t * @return the {@link ChannelSecurityConfigurer} for further customizations\n\t\t */\n\t\tpublic ChannelRequestMatcherRegistry redirectStrategy(RedirectStrategy redirectStrategy) {\n\t\t\tChannelSecurityConfigurer.this.redirectStrategy = redirectStrategy;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t/**\n\t * @deprecated no replacement planned\n\t */\n\t@Deprecated\n\tpublic class RequiresChannelUrl {\n\n\t\tprotected List<? extends RequestMatcher> requestMatchers;\n\n\t\tRequiresChannelUrl(List<? extends RequestMatcher> requestMatchers) {\n\t\t\tthis.requestMatchers = requestMatchers;\n\t\t}\n\n\t\tpublic ChannelRequestMatcherRegistry requiresSecure() {\n\t\t\treturn requires(\"REQUIRES_SECURE_CHANNEL\");\n\t\t}\n\n\t\tpublic ChannelRequestMatcherRegistry requiresInsecure() {\n\t\t\treturn requires(\"REQUIRES_INSECURE_CHANNEL\");\n\t\t}\n\n\t\tpublic ChannelRequestMatcherRegistry requires(String attribute) {\n\t\t\treturn addAttribute(attribute, this.requestMatchers);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/CorsConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.util.Assert;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.CorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\n\n/**\n * Adds {@link CorsFilter} to the Spring Security filter chain. If a bean by the name of\n * corsFilter is provided, that {@link CorsFilter} is used. Else if\n * corsConfigurationSource is defined, then that {@link CorsConfiguration} is used.\n *\n * @param <H> the builder to return.\n * @author Rob Winch\n * @since 4.1.1\n */\npublic class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CorsConfigurer<H>, H> {\n\n\tprivate static final String CORS_CONFIGURATION_SOURCE_BEAN_NAME = \"corsConfigurationSource\";\n\n\tprivate static final String CORS_FILTER_BEAN_NAME = \"corsFilter\";\n\n\tprivate CorsConfigurationSource configurationSource;\n\n\t/**\n\t * Creates a new instance\n\t *\n\t * @see HttpSecurity#cors(Customizer)\n\t */\n\tpublic CorsConfigurer() {\n\t}\n\n\tpublic CorsConfigurer<H> configurationSource(CorsConfigurationSource configurationSource) {\n\t\tthis.configurationSource = configurationSource;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tApplicationContext context = http.getSharedObject(ApplicationContext.class);\n\t\tCorsFilter corsFilter = getCorsFilter(context);\n\t\tAssert.state(corsFilter != null, () -> \"Please configure either a \" + CORS_FILTER_BEAN_NAME + \" bean or a \"\n\t\t\t\t+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + \"bean.\");\n\t\thttp.addFilter(corsFilter);\n\t}\n\n\tprivate CorsFilter getCorsFilter(ApplicationContext context) {\n\t\tif (this.configurationSource != null) {\n\t\t\treturn new CorsFilter(this.configurationSource);\n\t\t}\n\t\tboolean containsCorsFilter = context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);\n\t\tif (containsCorsFilter) {\n\t\t\treturn context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);\n\t\t}\n\t\tboolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);\n\t\tif (containsCorsSource) {\n\t\t\tCorsConfigurationSource configurationSource = context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME,\n\t\t\t\t\tCorsConfigurationSource.class);\n\t\t\treturn new CorsFilter(configurationSource);\n\t\t}\n\t\treturn MvcCorsFilter.getMvcCorsFilter(context);\n\t}\n\n\tstatic class MvcCorsFilter {\n\n\t\tprivate static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = \"mvcHandlerMappingIntrospector\";\n\n\t\t/**\n\t\t * This needs to be isolated into a separate class as Spring MVC is an optional\n\t\t * dependency and will potentially cause ClassLoading issues\n\t\t * @param context\n\t\t * @return\n\t\t */\n\t\tprivate static CorsFilter getMvcCorsFilter(ApplicationContext context) {\n\t\t\tif (context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {\n\t\t\t\tCorsConfigurationSource corsConfigurationSource = context\n\t\t\t\t\t.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);\n\t\t\t\treturn new CorsFilter(corsConfigurationSource);\n\t\t\t}\n\t\t\tthrow new NoSuchBeanDefinitionException(CorsConfigurationSource.class,\n\t\t\t\t\t\"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using \"\n\t\t\t\t\t\t\t+ \"`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl;\nimport org.springframework.security.web.access.CompositeAccessDeniedHandler;\nimport org.springframework.security.web.access.DelegatingAccessDeniedHandler;\nimport org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.csrf.CookieCsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfAuthenticationStrategy;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.csrf.CsrfLogoutHandler;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\nimport org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;\nimport org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;\nimport org.springframework.security.web.session.InvalidSessionStrategy;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Adds\n * <a href=\"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)\" >CSRF</a>\n * protection for the methods as specified by\n * {@link #requireCsrfProtectionMatcher(RequestMatcher)}.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link CsrfFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared objects are created.\n *\n * <h2>Shared Objects Used</h2>\n *\n * <ul>\n * <li>{@link ExceptionHandlingConfigurer#accessDeniedHandler(AccessDeniedHandler)} is\n * used to determine how to handle CSRF attempts</li>\n * <li>{@link InvalidSessionStrategy}</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Michael Vitz\n * @since 3.2\n */\npublic final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {\n\n\tprivate CsrfTokenRepository csrfTokenRepository = new HttpSessionCsrfTokenRepository();\n\n\tprivate RequestMatcher requireCsrfProtectionMatcher = CsrfFilter.DEFAULT_CSRF_MATCHER;\n\n\tprivate List<RequestMatcher> ignoredCsrfProtectionMatchers = new ArrayList<>();\n\n\tprivate SessionAuthenticationStrategy sessionAuthenticationStrategy;\n\n\tprivate CsrfTokenRequestHandler requestHandler;\n\n\tprivate final ApplicationContext context;\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#csrf(Customizer)\n\t */\n\tpublic CsrfConfigurer(ApplicationContext context) {\n\t\tthis.context = context;\n\t}\n\n\t/**\n\t * Specify the {@link CsrfTokenRepository} to use. The default is an\n\t * {@link HttpSessionCsrfTokenRepository}.\n\t * @param csrfTokenRepository the {@link CsrfTokenRepository} to use\n\t * @return the {@link CsrfConfigurer} for further customizations\n\t */\n\tpublic CsrfConfigurer<H> csrfTokenRepository(CsrfTokenRepository csrfTokenRepository) {\n\t\tAssert.notNull(csrfTokenRepository, \"csrfTokenRepository cannot be null\");\n\t\tthis.csrfTokenRepository = csrfTokenRepository;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the {@link RequestMatcher} to use for determining when CSRF should be\n\t * applied. The default is to ignore GET, HEAD, TRACE, OPTIONS and process all other\n\t * requests.\n\t * @param requireCsrfProtectionMatcher the {@link RequestMatcher} to use\n\t * @return the {@link CsrfConfigurer} for further customizations\n\t */\n\tpublic CsrfConfigurer<H> requireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {\n\t\tAssert.notNull(requireCsrfProtectionMatcher, \"requireCsrfProtectionMatcher cannot be null\");\n\t\tthis.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify a {@link CsrfTokenRequestHandler} to use for making the {@code CsrfToken}\n\t * available as a request attribute.\n\t * @param requestHandler the {@link CsrfTokenRequestHandler} to use\n\t * @return the {@link CsrfConfigurer} for further customizations\n\t * @since 5.8\n\t */\n\tpublic CsrfConfigurer<H> csrfTokenRequestHandler(CsrfTokenRequestHandler requestHandler) {\n\t\tthis.requestHandler = requestHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * <p>\n\t * Allows specifying {@link HttpServletRequest}s that should not use CSRF Protection\n\t * even if they match the {@link #requireCsrfProtectionMatcher(RequestMatcher)}.\n\t * </p>\n\t *\n\t * <p>\n\t * For example, the following configuration will ensure CSRF protection ignores:\n\t * </p>\n\t * <ul>\n\t * <li>Any GET, HEAD, TRACE, OPTIONS (this is the default)</li>\n\t * <li>We also explicitly state to ignore any request that has a \"X-Requested-With:\n\t * XMLHttpRequest\" header</li>\n\t * </ul>\n\t *\n\t * <pre>\n\t * http\n\t *     .csrf((csrf) -&gt; csrf\n\t *         .ignoringRequestMatchers((request) -&gt; \"XMLHttpRequest\".equals(request.getHeader(\"X-Requested-With\"))))\n\t *     ...\n\t * </pre>\n\t *\n\t * @since 5.1\n\t */\n\tpublic CsrfConfigurer<H> ignoringRequestMatchers(RequestMatcher... requestMatchers) {\n\t\tnew IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers);\n\t\treturn this;\n\t}\n\n\t/**\n\t * <p>\n\t * Allows specifying {@link HttpServletRequest} that should not use CSRF Protection\n\t * even if they match the {@link #requireCsrfProtectionMatcher(RequestMatcher)}.\n\t * </p>\n\t *\n\t * <p>\n\t * For example, the following configuration will ensure CSRF protection ignores:\n\t * </p>\n\t * <ul>\n\t * <li>Any GET, HEAD, TRACE, OPTIONS (this is the default)</li>\n\t * <li>We also explicitly state to ignore any request that starts with \"/sockjs/\"</li>\n\t * </ul>\n\t *\n\t * <pre>\n\t * http\n\t *     .csrf((csrf) -&gt; csrf\n\t *         .ignoringRequestMatchers(\"/sockjs/**\"))\n\t *     ...\n\t * </pre>\n\t *\n\t * @since 5.8\n\t * @see AbstractRequestMatcherRegistry#requestMatchers(String...)\n\t */\n\tpublic CsrfConfigurer<H> ignoringRequestMatchers(String... patterns) {\n\t\tnew IgnoreCsrfProtectionRegistry(this.context).requestMatchers(patterns);\n\t\treturn this;\n\t}\n\n\t/**\n\t * <p>\n\t * Specify the {@link SessionAuthenticationStrategy} to use. The default is a\n\t * {@link CsrfAuthenticationStrategy}.\n\t * </p>\n\t * @param sessionAuthenticationStrategy the {@link SessionAuthenticationStrategy} to\n\t * use\n\t * @return the {@link CsrfConfigurer} for further customizations\n\t * @since 5.2\n\t */\n\tpublic CsrfConfigurer<H> sessionAuthenticationStrategy(\n\t\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy) {\n\t\tAssert.notNull(sessionAuthenticationStrategy, \"sessionAuthenticationStrategy cannot be null\");\n\t\tthis.sessionAuthenticationStrategy = sessionAuthenticationStrategy;\n\t\treturn this;\n\t}\n\n\t/**\n\t * <p>\n\t * Sensible CSRF defaults when used in combination with a single page application.\n\t * Creates a cookie-based token repository and a custom request handler to resolve the\n\t * actual token value instead of the encoded token.\n\t * </p>\n\t * @return the {@link CsrfConfigurer} for further customizations\n\t * @since 7.0\n\t */\n\tpublic CsrfConfigurer<H> spa() {\n\t\tthis.csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();\n\t\tthis.requestHandler = new SpaCsrfTokenRequestHandler();\n\t\treturn this;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void configure(H http) {\n\t\tCsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);\n\t\tRequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();\n\t\tif (requireCsrfProtectionMatcher != null) {\n\t\t\tfilter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);\n\t\t}\n\t\tAccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);\n\t\tObservationRegistry registry = getObservationRegistry();\n\t\tif (!registry.isNoop()) {\n\t\t\tObservationMarkingAccessDeniedHandler observable = new ObservationMarkingAccessDeniedHandler(registry);\n\t\t\taccessDeniedHandler = new CompositeAccessDeniedHandler(observable, accessDeniedHandler);\n\t\t}\n\t\tif (accessDeniedHandler != null) {\n\t\t\tfilter.setAccessDeniedHandler(accessDeniedHandler);\n\t\t}\n\t\tLogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);\n\t\tif (logoutConfigurer != null) {\n\t\t\tlogoutConfigurer.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));\n\t\t}\n\t\tSessionManagementConfigurer<H> sessionConfigurer = http.getConfigurer(SessionManagementConfigurer.class);\n\t\tif (sessionConfigurer != null) {\n\t\t\tsessionConfigurer.addSessionAuthenticationStrategy(getSessionAuthenticationStrategy());\n\t\t}\n\t\tif (this.requestHandler != null) {\n\t\t\tfilter.setRequestHandler(this.requestHandler);\n\t\t}\n\t\tfilter = postProcess(filter);\n\t\thttp.addFilter(filter);\n\t}\n\n\t/**\n\t * Gets the final {@link RequestMatcher} to use by combining the\n\t * {@link #requireCsrfProtectionMatcher(RequestMatcher)} and any {@link #ignore()}.\n\t * @return the {@link RequestMatcher} to use\n\t */\n\tprivate RequestMatcher getRequireCsrfProtectionMatcher() {\n\t\tif (this.ignoredCsrfProtectionMatchers.isEmpty()) {\n\t\t\treturn this.requireCsrfProtectionMatcher;\n\t\t}\n\t\treturn new AndRequestMatcher(this.requireCsrfProtectionMatcher,\n\t\t\t\tnew NegatedRequestMatcher(new OrRequestMatcher(this.ignoredCsrfProtectionMatchers)));\n\t}\n\n\t/**\n\t * Gets the default {@link AccessDeniedHandler} from the\n\t * {@link ExceptionHandlingConfigurer#getAccessDeniedHandler(HttpSecurityBuilder)} or\n\t * create a {@link AccessDeniedHandlerImpl} if not available.\n\t * @param http the {@link HttpSecurityBuilder}\n\t * @return the {@link AccessDeniedHandler}\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate AccessDeniedHandler getDefaultAccessDeniedHandler(H http) {\n\t\tExceptionHandlingConfigurer<H> exceptionConfig = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tAccessDeniedHandler handler = null;\n\t\tif (exceptionConfig != null) {\n\t\t\thandler = exceptionConfig.getAccessDeniedHandler(http);\n\t\t}\n\t\tif (handler == null) {\n\t\t\thandler = new AccessDeniedHandlerImpl();\n\t\t}\n\t\treturn handler;\n\t}\n\n\t/**\n\t * Gets the default {@link InvalidSessionStrategy} from the\n\t * {@link SessionManagementConfigurer#getInvalidSessionStrategy()} or null if not\n\t * available.\n\t * @param http the {@link HttpSecurityBuilder}\n\t * @return the {@link InvalidSessionStrategy}\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate InvalidSessionStrategy getInvalidSessionStrategy(H http) {\n\t\tSessionManagementConfigurer<H> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);\n\t\tif (sessionManagement == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn sessionManagement.getInvalidSessionStrategy();\n\t}\n\n\t/**\n\t * Creates the {@link AccessDeniedHandler} from the result of\n\t * {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} and\n\t * {@link #getInvalidSessionStrategy(HttpSecurityBuilder)}. If\n\t * {@link #getInvalidSessionStrategy(HttpSecurityBuilder)} is non-null, then a\n\t * {@link DelegatingAccessDeniedHandler} is used in combination with\n\t * {@link InvalidSessionAccessDeniedHandler} and the\n\t * {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)}. Otherwise, only\n\t * {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} is used.\n\t * @param http the {@link HttpSecurityBuilder}\n\t * @return the {@link AccessDeniedHandler}\n\t */\n\tprivate AccessDeniedHandler createAccessDeniedHandler(H http) {\n\t\tInvalidSessionStrategy invalidSessionStrategy = getInvalidSessionStrategy(http);\n\t\tAccessDeniedHandler defaultAccessDeniedHandler = getDefaultAccessDeniedHandler(http);\n\t\tif (invalidSessionStrategy == null) {\n\t\t\treturn defaultAccessDeniedHandler;\n\t\t}\n\t\tInvalidSessionAccessDeniedHandler invalidSessionDeniedHandler = new InvalidSessionAccessDeniedHandler(\n\t\t\t\tinvalidSessionStrategy);\n\t\tLinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers = new LinkedHashMap<>();\n\t\thandlers.put(MissingCsrfTokenException.class, invalidSessionDeniedHandler);\n\t\treturn new DelegatingAccessDeniedHandler(handlers, defaultAccessDeniedHandler);\n\t}\n\n\t/**\n\t * Gets the {@link SessionAuthenticationStrategy} to use. If none was set by the user\n\t * a {@link CsrfAuthenticationStrategy} is created.\n\t * @return the {@link SessionAuthenticationStrategy}\n\t * @since 5.2\n\t */\n\tprivate SessionAuthenticationStrategy getSessionAuthenticationStrategy() {\n\t\tif (this.sessionAuthenticationStrategy != null) {\n\t\t\treturn this.sessionAuthenticationStrategy;\n\t\t}\n\t\tCsrfAuthenticationStrategy csrfAuthenticationStrategy = new CsrfAuthenticationStrategy(\n\t\t\t\tthis.csrfTokenRepository);\n\t\tif (this.requestHandler != null) {\n\t\t\tcsrfAuthenticationStrategy.setRequestHandler(this.requestHandler);\n\t\t}\n\t\treturn csrfAuthenticationStrategy;\n\t}\n\n\tprivate ObservationRegistry getObservationRegistry() {\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\tString[] names = context.getBeanNamesForType(ObservationRegistry.class);\n\t\tif (names.length == 1) {\n\t\t\treturn context.getBean(ObservationRegistry.class);\n\t\t}\n\t\telse {\n\t\t\treturn ObservationRegistry.NOOP;\n\t\t}\n\t}\n\n\t/**\n\t * Allows registering {@link RequestMatcher} instances that should be ignored (even if\n\t * the {@link HttpServletRequest} matches the\n\t * {@link CsrfConfigurer#requireCsrfProtectionMatcher(RequestMatcher)}.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tprivate class IgnoreCsrfProtectionRegistry extends AbstractRequestMatcherRegistry<IgnoreCsrfProtectionRegistry> {\n\n\t\tIgnoreCsrfProtectionRegistry(ApplicationContext context) {\n\t\t\tsetApplicationContext(context);\n\t\t}\n\n\t\t@Override\n\t\tprotected IgnoreCsrfProtectionRegistry chainRequestMatchers(List<RequestMatcher> requestMatchers) {\n\t\t\tCsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(requestMatchers);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tprivate static final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {\n\n\t\tprivate final CsrfTokenRequestAttributeHandler plain = new CsrfTokenRequestAttributeHandler();\n\n\t\tprivate final CsrfTokenRequestAttributeHandler xor = new XorCsrfTokenRequestAttributeHandler();\n\n\t\tSpaCsrfTokenRequestHandler() {\n\t\t\tthis.xor.setCsrfRequestAttributeName(null);\n\t\t}\n\n\t\t@Override\n\t\tpublic void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {\n\t\t\tthis.xor.handle(request, response, csrfToken);\n\t\t}\n\n\t\t@Override\n\t\tpublic String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {\n\t\t\tString headerValue = request.getHeader(csrfToken.getHeaderName());\n\t\t\treturn (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.csrf.CsrfToken;\n\n/**\n * Adds a Filter that will generate a login page if one is not specified otherwise when\n * using {@link EnableWebSecurity}.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are conditionally populated\n *\n * <ul>\n * <li>{@link DefaultLoginPageGeneratingFilter} if the {@link FormLoginConfigurer} did not\n * have a login page specified</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared objects are created.\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link FormLoginConfigurer} is used to determine if the\n * {@link DefaultLoginPageConfigurer} should be added and how to configure it.</li>\n * </ul>\n *\n * @author Rob Winch\n * @since 3.2\n * @see EnableWebSecurity\n */\npublic final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<DefaultLoginPageConfigurer<H>, H> {\n\n\tprivate DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = new DefaultLoginPageGeneratingFilter();\n\n\tprivate DefaultLogoutPageGeneratingFilter logoutPageGeneratingFilter = new DefaultLogoutPageGeneratingFilter();\n\n\t@Override\n\tpublic void init(H http) {\n\t\tthis.loginPageGeneratingFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tthis.loginPageGeneratingFilter.setResolveHiddenInputs(DefaultLoginPageConfigurer.this::hiddenInputs);\n\t\tthis.logoutPageGeneratingFilter.setResolveHiddenInputs(DefaultLoginPageConfigurer.this::hiddenInputs);\n\t\thttp.setSharedObject(DefaultLoginPageGeneratingFilter.class, this.loginPageGeneratingFilter);\n\t}\n\n\tprivate Map<String, String> hiddenInputs(HttpServletRequest request) {\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\treturn (token != null) ? Collections.singletonMap(token.getParameterName(), token.getToken())\n\t\t\t\t: Collections.emptyMap();\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void configure(H http) {\n\t\tAuthenticationEntryPoint authenticationEntryPoint = null;\n\t\tExceptionHandlingConfigurer<?> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptionConf != null) {\n\t\t\tauthenticationEntryPoint = exceptionConf.getAuthenticationEntryPoint();\n\t\t}\n\t\tif (this.loginPageGeneratingFilter.isEnabled() && authenticationEntryPoint == null) {\n\t\t\tthis.loginPageGeneratingFilter = postProcess(this.loginPageGeneratingFilter);\n\t\t\thttp.addFilter(this.loginPageGeneratingFilter);\n\t\t\thttp.addFilter(DefaultResourcesFilter.css());\n\t\t\tLogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);\n\t\t\tif (logoutConfigurer != null) {\n\t\t\t\thttp.addFilter(this.logoutPageGeneratingFilter);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.LinkedHashMap;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl;\nimport org.springframework.security.web.access.DelegatingMissingAuthorityAccessDeniedHandler;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.RequestMatcherDelegatingAccessDeniedHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Adds exception handling for Spring Security related exceptions to an application. All\n * properties have reasonable defaults, so no additional configuration is required other\n * than applying this\n * {@link org.springframework.security.config.annotation.SecurityConfigurer}.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link ExceptionTranslationFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared objects are created.\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>If no explicit {@link RequestCache}, is provided a {@link RequestCache} shared\n * object is used to replay the request after authentication is successful</li>\n * <li>{@link AuthenticationEntryPoint} - see\n * {@link #authenticationEntryPoint(AuthenticationEntryPoint)}</li>\n * </ul>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<ExceptionHandlingConfigurer<H>, H> {\n\n\tprivate AuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate AccessDeniedHandler accessDeniedHandler;\n\n\tprivate DelegatingAuthenticationEntryPoint.@Nullable Builder defaultEntryPoint;\n\n\tprivate LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings = new LinkedHashMap<>();\n\n\tprivate DelegatingMissingAuthorityAccessDeniedHandler.@Nullable Builder missingAuthoritiesHandlerBuilder;\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#exceptionHandling(Customizer)\n\t */\n\tpublic ExceptionHandlingConfigurer() {\n\t}\n\n\t/**\n\t * Shortcut to specify the {@link AccessDeniedHandler} to be used is a specific error\n\t * page\n\t * @param accessDeniedUrl the URL to the access denied page (i.e. /errors/401)\n\t * @return the {@link ExceptionHandlingConfigurer} for further customization\n\t * @see AccessDeniedHandlerImpl\n\t * @see #accessDeniedHandler(org.springframework.security.web.access.AccessDeniedHandler)\n\t */\n\tpublic ExceptionHandlingConfigurer<H> accessDeniedPage(String accessDeniedUrl) {\n\t\tAccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();\n\t\taccessDeniedHandler.setErrorPage(accessDeniedUrl);\n\t\treturn accessDeniedHandler(accessDeniedHandler);\n\t}\n\n\t/**\n\t * Specifies the {@link AccessDeniedHandler} to be used\n\t * @param accessDeniedHandler the {@link AccessDeniedHandler} to be used\n\t * @return the {@link ExceptionHandlingConfigurer} for further customization\n\t */\n\tpublic ExceptionHandlingConfigurer<H> accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {\n\t\tthis.accessDeniedHandler = accessDeniedHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets a default {@link AccessDeniedHandler} to be used which prefers being invoked\n\t * for the provided {@link RequestMatcher}. If only a single default\n\t * {@link AccessDeniedHandler} is specified, it will be what is used for the default\n\t * {@link AccessDeniedHandler}. If multiple default {@link AccessDeniedHandler}\n\t * instances are configured, then a\n\t * {@link RequestMatcherDelegatingAccessDeniedHandler} will be used.\n\t * @param deniedHandler the {@link AccessDeniedHandler} to use\n\t * @param preferredMatcher the {@link RequestMatcher} for this default\n\t * {@link AccessDeniedHandler}\n\t * @return the {@link ExceptionHandlingConfigurer} for further customizations\n\t * @since 5.1\n\t */\n\tpublic ExceptionHandlingConfigurer<H> defaultAccessDeniedHandlerFor(AccessDeniedHandler deniedHandler,\n\t\t\tRequestMatcher preferredMatcher) {\n\t\tthis.defaultDeniedHandlerMappings.put(preferredMatcher, deniedHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets a default {@link AuthenticationEntryPoint} to be used which prefers being\n\t * invoked for the provided missing {@link GrantedAuthority}.\n\t * @param entryPoint the {@link AuthenticationEntryPoint} to use for the given\n\t * {@code authority}\n\t * @param authority the authority\n\t * @return the {@link ExceptionHandlingConfigurer} for further customizations\n\t * @since 7.0\n\t */\n\tpublic ExceptionHandlingConfigurer<H> defaultDeniedHandlerForMissingAuthority(AuthenticationEntryPoint entryPoint,\n\t\t\tString authority) {\n\t\tif (this.missingAuthoritiesHandlerBuilder == null) {\n\t\t\tthis.missingAuthoritiesHandlerBuilder = DelegatingMissingAuthorityAccessDeniedHandler.builder();\n\t\t}\n\t\tthis.missingAuthoritiesHandlerBuilder.addEntryPointFor(entryPoint, authority);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets a default {@link AuthenticationEntryPoint} to be used which prefers being\n\t * invoked for the provided missing {@link GrantedAuthority}.\n\t * @param entryPoint a consumer of a\n\t * {@link DelegatingAuthenticationEntryPoint.Builder} to use for the given\n\t * {@code authority}\n\t * @param authority the authority\n\t * @return the {@link ExceptionHandlingConfigurer} for further customizations\n\t * @since 7.0\n\t */\n\tpublic ExceptionHandlingConfigurer<H> defaultDeniedHandlerForMissingAuthority(\n\t\t\tConsumer<DelegatingAuthenticationEntryPoint.Builder> entryPoint, String authority) {\n\t\tif (this.missingAuthoritiesHandlerBuilder == null) {\n\t\t\tthis.missingAuthoritiesHandlerBuilder = DelegatingMissingAuthorityAccessDeniedHandler.builder();\n\t\t}\n\t\tthis.missingAuthoritiesHandlerBuilder.addEntryPointFor(entryPoint, authority);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationEntryPoint} to be used.\n\t *\n\t * <p>\n\t * If no {@link #authenticationEntryPoint(AuthenticationEntryPoint)} is specified,\n\t * then\n\t * {@link #defaultAuthenticationEntryPointFor(AuthenticationEntryPoint, RequestMatcher)}\n\t * will be used. The first {@link AuthenticationEntryPoint} will be used as the\n\t * default if no matches were found.\n\t * </p>\n\t *\n\t * <p>\n\t * If that is not provided defaults to {@link Http403ForbiddenEntryPoint}.\n\t * </p>\n\t * @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use\n\t * @return the {@link ExceptionHandlingConfigurer} for further customizations\n\t */\n\tpublic ExceptionHandlingConfigurer<H> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets a default {@link AuthenticationEntryPoint} to be used which prefers being\n\t * invoked for the provided {@link RequestMatcher}. If only a single default\n\t * {@link AuthenticationEntryPoint} is specified, it will be what is used for the\n\t * default {@link AuthenticationEntryPoint}. If multiple default\n\t * {@link AuthenticationEntryPoint} instances are configured, then a\n\t * {@link DelegatingAuthenticationEntryPoint} will be used.\n\t * @param entryPoint the {@link AuthenticationEntryPoint} to use\n\t * @param preferredMatcher the {@link RequestMatcher} for this default\n\t * {@link AuthenticationEntryPoint}\n\t * @return the {@link ExceptionHandlingConfigurer} for further customizations\n\t */\n\tpublic ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint,\n\t\t\tRequestMatcher preferredMatcher) {\n\t\tif (this.defaultEntryPoint == null) {\n\t\t\tthis.defaultEntryPoint = DelegatingAuthenticationEntryPoint.builder();\n\t\t}\n\t\tthis.defaultEntryPoint.addEntryPointFor(entryPoint, preferredMatcher);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets any explicitly configured {@link AuthenticationEntryPoint}\n\t * @return\n\t */\n\tAuthenticationEntryPoint getAuthenticationEntryPoint() {\n\t\treturn this.authenticationEntryPoint;\n\t}\n\n\t/**\n\t * Gets the {@link AccessDeniedHandler} that is configured.\n\t * @return the {@link AccessDeniedHandler}\n\t */\n\tAccessDeniedHandler getAccessDeniedHandler() {\n\t\treturn this.accessDeniedHandler;\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tAuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);\n\t\tExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint,\n\t\t\t\tgetRequestCache(http));\n\t\tAccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);\n\t\texceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);\n\t\texceptionTranslationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\texceptionTranslationFilter = postProcess(exceptionTranslationFilter);\n\t\thttp.addFilter(exceptionTranslationFilter);\n\t}\n\n\t/**\n\t * Gets the {@link AccessDeniedHandler} according to the rules specified by\n\t * {@link #accessDeniedHandler(AccessDeniedHandler)}\n\t * @param http the {@link HttpSecurity} used to look up shared\n\t * {@link AccessDeniedHandler}\n\t * @return the {@link AccessDeniedHandler} to use\n\t */\n\tAccessDeniedHandler getAccessDeniedHandler(H http) {\n\t\tAccessDeniedHandler deniedHandler = this.accessDeniedHandler;\n\t\tif (deniedHandler == null) {\n\t\t\tdeniedHandler = createDefaultDeniedHandler(http);\n\t\t}\n\t\treturn deniedHandler;\n\t}\n\n\t/**\n\t * Gets the {@link AuthenticationEntryPoint} according to the rules specified by\n\t * {@link #authenticationEntryPoint(AuthenticationEntryPoint)}\n\t * @param http the {@link HttpSecurity} used to look up shared\n\t * {@link AuthenticationEntryPoint}\n\t * @return the {@link AuthenticationEntryPoint} to use\n\t */\n\tAuthenticationEntryPoint getAuthenticationEntryPoint(H http) {\n\t\tAuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;\n\t\tif (entryPoint == null) {\n\t\t\tentryPoint = createDefaultEntryPoint(http);\n\t\t}\n\t\treturn entryPoint;\n\t}\n\n\tprivate AccessDeniedHandler createDefaultDeniedHandler(H http) {\n\t\tAccessDeniedHandler defaults = createDefaultAccessDeniedHandler(http);\n\t\tif (this.missingAuthoritiesHandlerBuilder == null) {\n\t\t\treturn defaults;\n\t\t}\n\t\tDelegatingMissingAuthorityAccessDeniedHandler deniedHandler = this.missingAuthoritiesHandlerBuilder.build();\n\t\tdeniedHandler.setRequestCache(getRequestCache(http));\n\t\tdeniedHandler.setDefaultAccessDeniedHandler(defaults);\n\t\treturn deniedHandler;\n\t}\n\n\tprivate AccessDeniedHandler createDefaultAccessDeniedHandler(H http) {\n\t\tif (this.defaultDeniedHandlerMappings.isEmpty()) {\n\t\t\treturn new AccessDeniedHandlerImpl();\n\t\t}\n\t\tif (this.defaultDeniedHandlerMappings.size() == 1) {\n\t\t\treturn this.defaultDeniedHandlerMappings.values().iterator().next();\n\t\t}\n\t\treturn new RequestMatcherDelegatingAccessDeniedHandler(this.defaultDeniedHandlerMappings,\n\t\t\t\tnew AccessDeniedHandlerImpl());\n\t}\n\n\tprivate AuthenticationEntryPoint createDefaultEntryPoint(H http) {\n\t\tif (this.defaultEntryPoint == null) {\n\t\t\treturn new Http403ForbiddenEntryPoint();\n\t\t}\n\t\treturn this.defaultEntryPoint.build();\n\t}\n\n\t/**\n\t * Gets the {@link RequestCache} to use. If one is defined using\n\t * {@link #requestCache(org.springframework.security.web.savedrequest.RequestCache)},\n\t * then it is used. Otherwise, an attempt to find a {@link RequestCache} shared object\n\t * is made. If that fails, an {@link HttpSessionRequestCache} is used\n\t * @param http the {@link HttpSecurity} to attempt to fined the shared object\n\t * @return the {@link RequestCache} to use\n\t */\n\tprivate RequestCache getRequestCache(H http) {\n\t\tRequestCache result = http.getSharedObject(RequestCache.class);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\treturn new HttpSessionRequestCache();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.ForwardAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Adds form based authentication. All attributes have reasonable defaults making all\n * parameters are optional. If no {@link #loginPage(String)} is specified, a default login\n * page will be generated by the framework.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link UsernamePasswordAuthenticationFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * The following shared objects are populated\n *\n * <ul>\n * <li>{@link AuthenticationEntryPoint}</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link org.springframework.security.authentication.AuthenticationManager}</li>\n * <li>{@link RememberMeServices} - is optionally used. See {@link RememberMeConfigurer}\n * </li>\n * <li>{@link SessionAuthenticationStrategy} - is optionally used. See\n * {@link SessionManagementConfigurer}</li>\n * <li>{@link DefaultLoginPageGeneratingFilter} - if present will be populated with\n * information from the configuration</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Shazin Sadakath\n * @since 3.2\n */\npublic final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends\n\t\tAbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#formLogin(Customizer)\n\t */\n\tpublic FormLoginConfigurer() {\n\t\tsuper(new UsernamePasswordAuthenticationFilter(), null);\n\t\tusernameParameter(\"username\");\n\t\tpasswordParameter(\"password\");\n\t}\n\n\t/**\n\t * <p>\n\t * Specifies the URL to send users to if login is required. If used with\n\t * {@link EnableWebSecurity} a default login page will be generated when this\n\t * attribute is not specified.\n\t * </p>\n\t *\n\t * <p>\n\t * If a URL is specified or this is not being used in conjunction with\n\t * {@link EnableWebSecurity}, users are required to process the specified URL to\n\t * generate a login page. In general, the login page should create a form that submits\n\t * a request with the following requirements to work with\n\t * {@link UsernamePasswordAuthenticationFilter}:\n\t * </p>\n\t *\n\t * <ul>\n\t * <li>It must be an HTTP POST</li>\n\t * <li>It must be submitted to {@link #loginProcessingUrl(String)}</li>\n\t * <li>It should include the username as an HTTP parameter by the name of\n\t * {@link #usernameParameter(String)}</li>\n\t * <li>It should include the password as an HTTP parameter by the name of\n\t * {@link #passwordParameter(String)}</li>\n\t * </ul>\n\t *\n\t * <h2>Example login.jsp</h2>\n\t *\n\t * Login pages can be rendered with any technology you choose so long as the rules\n\t * above are followed. Below is an example login.jsp that can be used as a quick start\n\t * when using JSP's or as a baseline to translate into another view technology.\n\t *\n\t * <pre>\n\t * <!-- loginProcessingUrl should correspond to FormLoginConfigurer#loginProcessingUrl. Don't forget to perform a POST -->\n\t * &lt;c:url value=\"/login\" var=\"loginProcessingUrl\"/&gt;\n\t * &lt;form action=\"${loginProcessingUrl}\" method=\"post\"&gt;\n\t *    &lt;fieldset&gt;\n\t *        &lt;legend&gt;Please Login&lt;/legend&gt;\n\t *        &lt;!-- use param.error assuming FormLoginConfigurer#failureUrl contains the query parameter error --&gt;\n\t *        &lt;c:if test=\"${param.error != null}\"&gt;\n\t *            &lt;div&gt;\n\t *                Failed to login.\n\t *                &lt;c:if test=\"${SPRING_SECURITY_LAST_EXCEPTION != null}\"&gt;\n\t *                  Reason: &lt;c:out value=\"${SPRING_SECURITY_LAST_EXCEPTION.message}\" /&gt;\n\t *                &lt;/c:if&gt;\n\t *            &lt;/div&gt;\n\t *        &lt;/c:if&gt;\n\t *        &lt;!-- the configured LogoutConfigurer#logoutSuccessUrl is /login?logout and contains the query param logout --&gt;\n\t *        &lt;c:if test=\"${param.logout != null}\"&gt;\n\t *            &lt;div&gt;\n\t *                You have been logged out.\n\t *            &lt;/div&gt;\n\t *        &lt;/c:if&gt;\n\t *        &lt;p&gt;\n\t *        &lt;label for=\"username\"&gt;Username&lt;/label&gt;\n\t *        &lt;input type=\"text\" id=\"username\" name=\"username\"/&gt;\n\t *        &lt;/p&gt;\n\t *        &lt;p&gt;\n\t *        &lt;label for=\"password\"&gt;Password&lt;/label&gt;\n\t *        &lt;input type=\"password\" id=\"password\" name=\"password\"/&gt;\n\t *        &lt;/p&gt;\n\t *        &lt;!-- if using RememberMeConfigurer make sure remember-me matches RememberMeConfigurer#rememberMeParameter --&gt;\n\t *        &lt;p&gt;\n\t *        &lt;label for=\"remember-me\"&gt;Remember Me?&lt;/label&gt;\n\t *        &lt;input type=\"checkbox\" id=\"remember-me\" name=\"remember-me\"/&gt;\n\t *        &lt;/p&gt;\n\t *        &lt;div&gt;\n\t *            &lt;button type=\"submit\" class=\"btn\"&gt;Log in&lt;/button&gt;\n\t *        &lt;/div&gt;\n\t *    &lt;/fieldset&gt;\n\t * &lt;/form&gt;\n\t * </pre>\n\t *\n\t * <h2>Impact on other defaults</h2>\n\t *\n\t * Updating this value, also impacts a number of other default values. For example,\n\t * the following are the default values when only formLogin() was specified.\n\t *\n\t * <ul>\n\t * <li>/login GET - the login form</li>\n\t * <li>/login POST - process the credentials and if valid authenticate the user</li>\n\t * <li>/login?error GET - redirect here for failed authentication attempts</li>\n\t * <li>/login?logout GET - redirect here after successfully logging out</li>\n\t * </ul>\n\t *\n\t * If \"/authenticate\" was passed to this method it update the defaults as shown below:\n\t *\n\t * <ul>\n\t * <li>/authenticate GET - the login form</li>\n\t * <li>/authenticate POST - process the credentials and if valid authenticate the user\n\t * </li>\n\t * <li>/authenticate?error GET - redirect here for failed authentication attempts</li>\n\t * <li>/authenticate?logout GET - redirect here after successfully logging out</li>\n\t * </ul>\n\t * @param loginPage the login page to redirect to if authentication is required (i.e.\n\t * \"/login\")\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\t@Override\n\tpublic FormLoginConfigurer<H> loginPage(String loginPage) {\n\t\treturn super.loginPage(loginPage);\n\t}\n\n\t/**\n\t * The HTTP parameter to look for the username when performing authentication. Default\n\t * is \"username\".\n\t * @param usernameParameter the HTTP parameter to look for the username when\n\t * performing authentication\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic FormLoginConfigurer<H> usernameParameter(String usernameParameter) {\n\t\tgetAuthenticationFilter().setUsernameParameter(usernameParameter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * The HTTP parameter to look for the password when performing authentication. Default\n\t * is \"password\".\n\t * @param passwordParameter the HTTP parameter to look for the password when\n\t * performing authentication\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic FormLoginConfigurer<H> passwordParameter(String passwordParameter) {\n\t\tgetAuthenticationFilter().setPasswordParameter(passwordParameter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Forward Authentication Failure Handler\n\t * @param forwardUrl the target URL in case of failure\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {\n\t\tfailureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Forward Authentication Success Handler\n\t * @param forwardUrl the target URL in case of success\n\t * @return the {@link FormLoginConfigurer} for additional customization\n\t */\n\tpublic FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {\n\t\tsuccessHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\tsuper.init(http);\n\t\tinitDefaultLoginFilter(http);\n\t\tExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptions != null) {\n\t\t\tAuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint();\n\t\t\tRequestMatcher requestMatcher = getAuthenticationEntryPointMatcher(http);\n\t\t\texceptions.defaultDeniedHandlerForMissingAuthority((ep) -> ep.addEntryPointFor(entryPoint, requestMatcher),\n\t\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t\t}\n\t}\n\n\t@Override\n\tprotected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {\n\t\treturn getRequestMatcherBuilder().matcher(HttpMethod.POST, loginProcessingUrl);\n\t}\n\n\t/**\n\t * Gets the HTTP parameter that is used to submit the username.\n\t * @return the HTTP parameter that is used to submit the username\n\t */\n\tprivate String getUsernameParameter() {\n\t\treturn getAuthenticationFilter().getUsernameParameter();\n\t}\n\n\t/**\n\t * Gets the HTTP parameter that is used to submit the password.\n\t * @return the HTTP parameter that is used to submit the password\n\t */\n\tprivate String getPasswordParameter() {\n\t\treturn getAuthenticationFilter().getPasswordParameter();\n\t}\n\n\t/**\n\t * If available, initializes the {@link DefaultLoginPageGeneratingFilter} shared\n\t * object.\n\t * @param http the {@link HttpSecurityBuilder} to use\n\t */\n\tprivate void initDefaultLoginFilter(H http) {\n\t\tDefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http\n\t\t\t.getSharedObject(DefaultLoginPageGeneratingFilter.class);\n\t\tif (loginPageGeneratingFilter != null && !isCustomLoginPage()) {\n\t\t\tloginPageGeneratingFilter.setFormLoginEnabled(true);\n\t\t\tloginPageGeneratingFilter.setUsernameParameter(getUsernameParameter());\n\t\t\tloginPageGeneratingFilter.setPasswordParameter(getPasswordParameter());\n\t\t\tloginPageGeneratingFilter.setLoginPageUrl(getLoginPage());\n\t\t\tloginPageGeneratingFilter.setFailureUrl(getFailureUrl());\n\t\t\tloginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.security.web.header.HeaderWriterFilter;\nimport org.springframework.security.web.header.writers.CacheControlHeadersWriter;\nimport org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.HpkpHeaderWriter;\nimport org.springframework.security.web.header.writers.HstsHeaderWriter;\nimport org.springframework.security.web.header.writers.PermissionsPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;\nimport org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter;\nimport org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Adds the Security HTTP headers to the response. Security HTTP headers is activated by\n * default when using {@link EnableWebSecurity}'s default constructor.\n * </p>\n *\n * <p>\n * The default headers include are:\n * </p>\n *\n * <pre>\n * Cache-Control: no-cache, no-store, max-age=0, must-revalidate\n * Pragma: no-cache\n * Expires: 0\n * X-Content-Type-Options: nosniff\n * Strict-Transport-Security: max-age=31536000 ; includeSubDomains\n * X-Frame-Options: DENY\n * X-XSS-Protection: 0\n * </pre>\n *\n * @author Rob Winch\n * @author Tim Ysewyn\n * @author Joe Grandja\n * @author Eddú Meléndez\n * @author Vedran Pavic\n * @author Ankur Pathak\n * @author Daniel Garnier-Moiroux\n * @since 3.2\n */\npublic class HeadersConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<HeadersConfigurer<H>, H> {\n\n\tprivate List<HeaderWriter> headerWriters = new ArrayList<>();\n\n\tprivate final ContentTypeOptionsConfig contentTypeOptions = new ContentTypeOptionsConfig();\n\n\tprivate final XXssConfig xssProtection = new XXssConfig();\n\n\tprivate final CacheControlConfig cacheControl = new CacheControlConfig();\n\n\tprivate final HstsConfig hsts = new HstsConfig();\n\n\tprivate final FrameOptionsConfig frameOptions = new FrameOptionsConfig();\n\n\tprivate final HpkpConfig hpkp = new HpkpConfig();\n\n\tprivate final ContentSecurityPolicyConfig contentSecurityPolicy = new ContentSecurityPolicyConfig();\n\n\tprivate final ReferrerPolicyConfig referrerPolicy = new ReferrerPolicyConfig();\n\n\tprivate final FeaturePolicyConfig featurePolicy = new FeaturePolicyConfig();\n\n\tprivate final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig();\n\n\tprivate final CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy = new CrossOriginOpenerPolicyConfig();\n\n\tprivate final CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyConfig();\n\n\tprivate final CrossOriginResourcePolicyConfig crossOriginResourcePolicy = new CrossOriginResourcePolicyConfig();\n\n\t/**\n\t * Creates a new instance\n\t *\n\t * @see HttpSecurity#headers(Customizer)\n\t */\n\tpublic HeadersConfigurer() {\n\t}\n\n\t/**\n\t * Adds a {@link HeaderWriter} instance\n\t * @param headerWriter the {@link HeaderWriter} instance to add\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t */\n\tpublic HeadersConfigurer<H> addHeaderWriter(HeaderWriter headerWriter) {\n\t\tAssert.notNull(headerWriter, \"headerWriter cannot be null\");\n\t\tthis.headerWriters.add(headerWriter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the {@link XContentTypeOptionsHeaderWriter} which inserts the\n\t * <a href= \"https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx\"\n\t * >X-Content-Type-Options</a>:\n\t *\n\t * <pre>\n\t * X-Content-Type-Options: nosniff\n\t * </pre>\n\t * @param contentTypeOptionsCustomizer the {@link Customizer} to provide more options\n\t * for the {@link ContentTypeOptionsConfig}\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t */\n\tpublic HeadersConfigurer<H> contentTypeOptions(Customizer<ContentTypeOptionsConfig> contentTypeOptionsCustomizer) {\n\t\tcontentTypeOptionsCustomizer.customize(this.contentTypeOptions.enable());\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * <strong>Note this is not comprehensive XSS protection!</strong>\n\t *\n\t * <p>\n\t * Allows customizing the {@link XXssProtectionHeaderWriter} which adds the <a href=\n\t * \"https://web.archive.org/web/20160201174302/https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx\"\n\t * >X-XSS-Protection header</a>\n\t * </p>\n\t * @param xssCustomizer the {@link Customizer} to provide more options for the\n\t * {@link XXssConfig}\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t */\n\tpublic HeadersConfigurer<H> xssProtection(Customizer<XXssConfig> xssCustomizer) {\n\t\txssCustomizer.customize(this.xssProtection.enable());\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * Allows customizing the {@link CacheControlHeadersWriter}. Specifically it adds the\n\t * following headers:\n\t * <ul>\n\t * <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>\n\t * <li>Pragma: no-cache</li>\n\t * <li>Expires: 0</li>\n\t * </ul>\n\t * @param cacheControlCustomizer the {@link Customizer} to provide more options for\n\t * the {@link CacheControlConfig}\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t */\n\tpublic HeadersConfigurer<H> cacheControl(Customizer<CacheControlConfig> cacheControlCustomizer) {\n\t\tcacheControlCustomizer.customize(this.cacheControl.enable());\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * Allows customizing the {@link HstsHeaderWriter} which provides support for\n\t * <a href=\"https://tools.ietf.org/html/rfc6797\">HTTP Strict Transport Security\n\t * (HSTS)</a>.\n\t * @param hstsCustomizer the {@link Customizer} to provide more options for the\n\t * {@link HstsConfig}\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t */\n\tpublic HeadersConfigurer<H> httpStrictTransportSecurity(Customizer<HstsConfig> hstsCustomizer) {\n\t\thstsCustomizer.customize(this.hsts.enable());\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * Allows customizing the {@link XFrameOptionsHeaderWriter}.\n\t * @param frameOptionsCustomizer the {@link Customizer} to provide more options for\n\t * the {@link FrameOptionsConfig}\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t */\n\tpublic HeadersConfigurer<H> frameOptions(Customizer<FrameOptionsConfig> frameOptionsCustomizer) {\n\t\tframeOptionsCustomizer.customize(this.frameOptions.enable());\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * Allows customizing the {@link HpkpHeaderWriter} which provides support for\n\t * <a href=\"https://tools.ietf.org/html/rfc7469\">HTTP Public Key Pinning (HPKP)</a>.\n\t * @param hpkpCustomizer the {@link Customizer} to provide more options for the\n\t * {@link HpkpConfig}\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t * @deprecated see <a href=\n\t * \"https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning\">Certificate\n\t * and Public Key Pinning</a> for more context\n\t */\n\t@Deprecated\n\tpublic HeadersConfigurer<H> httpPublicKeyPinning(Customizer<HpkpConfig> hpkpCustomizer) {\n\t\thpkpCustomizer.customize(this.hpkp.enable());\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * <p>\n\t * Allows configuration for <a href=\"https://www.w3.org/TR/CSP2/\">Content Security\n\t * Policy (CSP) Level 2</a>.\n\t * </p>\n\t *\n\t * <p>\n\t * Calling this method automatically enables (includes) the Content-Security-Policy\n\t * header in the response using the supplied security policy directive(s).\n\t * </p>\n\t *\n\t * <p>\n\t * Configuration is provided to the {@link ContentSecurityPolicyHeaderWriter} which\n\t * supports the writing of the two headers as detailed in the W3C Candidate\n\t * Recommendation:\n\t * </p>\n\t * <ul>\n\t * <li>Content-Security-Policy</li>\n\t * <li>Content-Security-Policy-Report-Only</li>\n\t * </ul>\n\t * @param contentSecurityCustomizer the {@link Customizer} to provide more options for\n\t * the {@link ContentSecurityPolicyConfig}\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t * @see ContentSecurityPolicyHeaderWriter\n\t */\n\tpublic HeadersConfigurer<H> contentSecurityPolicy(\n\t\t\tCustomizer<ContentSecurityPolicyConfig> contentSecurityCustomizer) {\n\t\tthis.contentSecurityPolicy.writer = new ContentSecurityPolicyHeaderWriter();\n\t\tcontentSecurityCustomizer.customize(this.contentSecurityPolicy);\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * Clears all of the default headers from the response. After doing so, one can add\n\t * headers back. For example, if you only want to use Spring Security's cache control\n\t * you can use the following:\n\t *\n\t * <pre>\n\t * http.headers().defaultsDisabled().cacheControl();\n\t * </pre>\n\t * @return the {@link HeadersConfigurer} for additional customization\n\t */\n\tpublic HeadersConfigurer<H> defaultsDisabled() {\n\t\tthis.contentTypeOptions.disable();\n\t\tthis.xssProtection.disable();\n\t\tthis.cacheControl.disable();\n\t\tthis.hsts.disable();\n\t\tthis.frameOptions.disable();\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tHeaderWriterFilter headersFilter = createHeaderWriterFilter();\n\t\thttp.addFilter(headersFilter);\n\t}\n\n\t/**\n\t * Creates the {@link HeaderWriter}\n\t * @return the {@link HeaderWriter}\n\t */\n\tprivate HeaderWriterFilter createHeaderWriterFilter() {\n\t\tList<HeaderWriter> writers = getHeaderWriters();\n\t\tif (writers.isEmpty()) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Headers security is enabled, but no headers will be added. Either add headers or disable headers security\");\n\t\t}\n\t\tHeaderWriterFilter headersFilter = new HeaderWriterFilter(writers);\n\t\theadersFilter = postProcess(headersFilter);\n\t\treturn headersFilter;\n\t}\n\n\t/**\n\t * Gets the {@link HeaderWriter} instances and possibly initializes with the defaults.\n\t * @return\n\t */\n\tprivate List<HeaderWriter> getHeaderWriters() {\n\t\tList<HeaderWriter> writers = new ArrayList<>();\n\t\taddIfNotNull(writers, this.contentTypeOptions.writer);\n\t\taddIfNotNull(writers, this.xssProtection.writer);\n\t\taddIfNotNull(writers, this.cacheControl.writer);\n\t\taddIfNotNull(writers, this.hsts.writer);\n\t\taddIfNotNull(writers, this.frameOptions.writer);\n\t\taddIfNotNull(writers, this.hpkp.writer);\n\t\taddIfNotNull(writers, this.contentSecurityPolicy.writer);\n\t\taddIfNotNull(writers, this.referrerPolicy.writer);\n\t\taddIfNotNull(writers, this.featurePolicy.writer);\n\t\taddIfNotNull(writers, this.permissionsPolicy.writer);\n\t\taddIfNotNull(writers, this.crossOriginOpenerPolicy.writer);\n\t\taddIfNotNull(writers, this.crossOriginEmbedderPolicy.writer);\n\t\taddIfNotNull(writers, this.crossOriginResourcePolicy.writer);\n\t\twriters.addAll(this.headerWriters);\n\t\treturn writers;\n\t}\n\n\tprivate <T> void addIfNotNull(List<T> values, T value) {\n\t\tif (value != null) {\n\t\t\tvalues.add(value);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Allows configuration for <a href=\"https://www.w3.org/TR/referrer-policy/\">Referrer\n\t * Policy</a>.\n\t * </p>\n\t *\n\t * <p>\n\t * Configuration is provided to the {@link ReferrerPolicyHeaderWriter} which support\n\t * the writing of the header as detailed in the W3C Technical Report:\n\t * </p>\n\t * <ul>\n\t * <li>Referrer-Policy</li>\n\t * </ul>\n\t * @param referrerPolicyCustomizer the {@link Customizer} to provide more options for\n\t * the {@link ReferrerPolicyConfig}\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t * @see ReferrerPolicyHeaderWriter\n\t */\n\tpublic HeadersConfigurer<H> referrerPolicy(Customizer<ReferrerPolicyConfig> referrerPolicyCustomizer) {\n\t\tthis.referrerPolicy.writer = new ReferrerPolicyHeaderWriter();\n\t\treferrerPolicyCustomizer.customize(this.referrerPolicy);\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * Allows configuration for <a href=\"https://wicg.github.io/feature-policy/\">Feature\n\t * Policy</a>.\n\t * <p>\n\t * Calling this method automatically enables (includes) the {@code Feature-Policy}\n\t * header in the response using the supplied policy directive(s).\n\t * <p>\n\t * Configuration is provided to the {@link FeaturePolicyHeaderWriter} which is\n\t * responsible for writing the header.\n\t * @return the {@link FeaturePolicyConfig} for additional configuration\n\t * @throws IllegalArgumentException if policyDirectives is {@code null} or empty\n\t * @since 5.1\n\t * @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)} or\n\t * {@code permissionsPolicy(Customizer.withDefaults())} to stick with defaults. See\n\t * the <a href=\n\t * \"https://docs.spring.io/spring-security/reference/migration-7/configuration.html#_use_the_lambda_dsl\">documentation</a>\n\t * for more details.\n\t * @see ObjectPostProcessorConfiguration FeaturePolicyHeaderWriter\n\t */\n\t@Deprecated\n\tpublic FeaturePolicyConfig featurePolicy(String policyDirectives) {\n\t\tthis.featurePolicy.writer = new FeaturePolicyHeaderWriter(policyDirectives);\n\t\treturn this.featurePolicy;\n\t}\n\n\t/**\n\t * Allows configuration for\n\t * <a href=\"https://w3c.github.io/webappsec-permissions-policy/\"> Permissions\n\t * Policy</a>.\n\t * <p>\n\t * Calling this method automatically enables (includes) the {@code Permissions-Policy}\n\t * header in the response using the supplied policy directive(s).\n\t * <p>\n\t * Configuration is provided to the {@link PermissionsPolicyHeaderWriter} which is\n\t * responsible for writing the header.\n\t * @return the {@link PermissionsPolicyConfig} for additional configuration\n\t * @throws IllegalArgumentException if policyDirectives is {@code null} or empty\n\t * @since 5.5\n\t * @deprecated For removal in 7.0. Use {@link #permissionsPolicyHeader(Customizer)}\n\t * instead\n\t * @see PermissionsPolicyHeaderWriter\n\t */\n\t@Deprecated(since = \"6.4\", forRemoval = true)\n\tpublic PermissionsPolicyConfig permissionsPolicy(Customizer<PermissionsPolicyConfig> permissionsPolicyCustomizer) {\n\t\tthis.permissionsPolicy.writer = new PermissionsPolicyHeaderWriter();\n\t\tpermissionsPolicyCustomizer.customize(this.permissionsPolicy);\n\t\treturn this.permissionsPolicy;\n\t}\n\n\t/**\n\t * Allows configuration for\n\t * <a href=\"https://w3c.github.io/webappsec-permissions-policy/\"> Permissions\n\t * Policy</a>.\n\t * <p>\n\t * Calling this method automatically enables (includes) the {@code Permissions-Policy}\n\t * header in the response using the supplied policy directive(s).\n\t * <p>\n\t * Configuration is provided to the {@link PermissionsPolicyHeaderWriter} which is\n\t * responsible for writing the header.\n\t * @return the {@link PermissionsPolicyConfig} for additional configuration\n\t * @throws IllegalArgumentException if policyDirectives is {@code null} or empty\n\t * @since 6.4\n\t * @see PermissionsPolicyHeaderWriter\n\t */\n\tpublic HeadersConfigurer<H> permissionsPolicyHeader(\n\t\t\tCustomizer<PermissionsPolicyConfig> permissionsPolicyCustomizer) {\n\t\tthis.permissionsPolicy.writer = new PermissionsPolicyHeaderWriter();\n\t\tpermissionsPolicyCustomizer.customize(this.permissionsPolicy);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows configuration for <a href=\n\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy\">\n\t * Cross-Origin-Opener-Policy</a> header.\n\t * <p>\n\t * Calling this method automatically enables (includes) the\n\t * {@code Cross-Origin-Opener-Policy} header in the response using the supplied\n\t * policy.\n\t * <p>\n\t * <p>\n\t * Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which\n\t * responsible for writing the header.\n\t * </p>\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t * @since 5.7\n\t * @see CrossOriginOpenerPolicyHeaderWriter\n\t */\n\tpublic HeadersConfigurer<H> crossOriginOpenerPolicy(\n\t\t\tCustomizer<CrossOriginOpenerPolicyConfig> crossOriginOpenerPolicyCustomizer) {\n\t\tthis.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();\n\t\tcrossOriginOpenerPolicyCustomizer.customize(this.crossOriginOpenerPolicy);\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * Allows configuration for <a href=\n\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy\">\n\t * Cross-Origin-Embedder-Policy</a> header.\n\t * <p>\n\t * Calling this method automatically enables (includes) the\n\t * {@code Cross-Origin-Embedder-Policy} header in the response using the supplied\n\t * policy.\n\t * <p>\n\t * <p>\n\t * Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}\n\t * which is responsible for writing the header.\n\t * </p>\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t * @since 5.7\n\t * @see CrossOriginEmbedderPolicyHeaderWriter\n\t */\n\tpublic HeadersConfigurer<H> crossOriginEmbedderPolicy(\n\t\t\tCustomizer<CrossOriginEmbedderPolicyConfig> crossOriginEmbedderPolicyCustomizer) {\n\t\tthis.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();\n\t\tcrossOriginEmbedderPolicyCustomizer.customize(this.crossOriginEmbedderPolicy);\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\t/**\n\t * Allows configuration for <a href=\n\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy\">\n\t * Cross-Origin-Resource-Policy</a> header.\n\t * <p>\n\t * Calling this method automatically enables (includes) the\n\t * {@code Cross-Origin-Resource-Policy} header in the response using the supplied\n\t * policy.\n\t * <p>\n\t * <p>\n\t * Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}\n\t * which is responsible for writing the header:\n\t * </p>\n\t * @return the {@link HeadersConfigurer} for additional customizations\n\t * @since 5.7\n\t * @see CrossOriginResourcePolicyHeaderWriter\n\t */\n\tpublic HeadersConfigurer<H> crossOriginResourcePolicy(\n\t\t\tCustomizer<CrossOriginResourcePolicyConfig> crossOriginResourcePolicyCustomizer) {\n\t\tthis.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();\n\t\tcrossOriginResourcePolicyCustomizer.customize(this.crossOriginResourcePolicy);\n\t\treturn HeadersConfigurer.this;\n\t}\n\n\tpublic final class ContentTypeOptionsConfig {\n\n\t\tprivate XContentTypeOptionsHeaderWriter writer;\n\n\t\tprivate ContentTypeOptionsConfig() {\n\t\t\tenable();\n\t\t}\n\n\t\t/**\n\t\t * Removes the X-XSS-Protection header.\n\t\t * @return {@link HeadersConfigurer} for additional customization.\n\t\t */\n\t\tpublic HeadersConfigurer<H> disable() {\n\t\t\tthis.writer = null;\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Ensures that Content Type Options is enabled\n\t\t * @return the {@link ContentTypeOptionsConfig} for additional customization\n\t\t */\n\t\tprivate ContentTypeOptionsConfig enable() {\n\t\t\tif (this.writer == null) {\n\t\t\t\tthis.writer = new XContentTypeOptionsHeaderWriter();\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class XXssConfig {\n\n\t\tprivate XXssProtectionHeaderWriter writer;\n\n\t\tprivate XXssConfig() {\n\t\t\tenable();\n\t\t}\n\n\t\t/**\n\t\t * Sets the value of the X-XSS-PROTECTION header. OWASP recommends using\n\t\t * {@link XXssProtectionHeaderWriter.HeaderValue#DISABLED}.\n\t\t *\n\t\t * If {@link XXssProtectionHeaderWriter.HeaderValue#DISABLED}, will specify that\n\t\t * X-XSS-Protection is disabled. For example:\n\t\t *\n\t\t * <pre>\n\t\t * X-XSS-Protection: 0\n\t\t * </pre>\n\t\t *\n\t\t * If {@link XXssProtectionHeaderWriter.HeaderValue#ENABLED}, will contain a value\n\t\t * of 1, but will not specify the mode as blocked. In this instance, any content\n\t\t * will be attempted to be fixed. For example:\n\t\t *\n\t\t * <pre>\n\t\t * X-XSS-Protection: 1\n\t\t * </pre>\n\t\t *\n\t\t * If {@link XXssProtectionHeaderWriter.HeaderValue#ENABLED_MODE_BLOCK}, will\n\t\t * contain a value of 1 and will specify mode as blocked. The content will be\n\t\t * replaced with \"#\". For example:\n\t\t *\n\t\t * <pre>\n\t\t * X-XSS-Protection: 1; mode=block\n\t\t * </pre>\n\t\t * @param headerValue the new header value\n\t\t * @since 5.8\n\t\t */\n\t\tpublic XXssConfig headerValue(XXssProtectionHeaderWriter.HeaderValue headerValue) {\n\t\t\tthis.writer.setHeaderValue(headerValue);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Disables X-XSS-Protection header (does not include it)\n\t\t * @return the {@link HeadersConfigurer} for additional configuration\n\t\t */\n\t\tpublic HeadersConfigurer<H> disable() {\n\t\t\tthis.writer = null;\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Ensures the X-XSS-Protection header is enabled if it is not already.\n\t\t * @return the {@link XXssConfig} for additional customization\n\t\t */\n\t\tprivate XXssConfig enable() {\n\t\t\tif (this.writer == null) {\n\t\t\t\tthis.writer = new XXssProtectionHeaderWriter();\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class CacheControlConfig {\n\n\t\tprivate CacheControlHeadersWriter writer;\n\n\t\tprivate CacheControlConfig() {\n\t\t\tenable();\n\t\t}\n\n\t\t/**\n\t\t * Disables Cache Control\n\t\t * @return the {@link HeadersConfigurer} for additional configuration\n\t\t */\n\t\tpublic HeadersConfigurer<H> disable() {\n\t\t\tthis.writer = null;\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Ensures the Cache Control headers are enabled if they are not already.\n\t\t * @return the {@link CacheControlConfig} for additional customization\n\t\t */\n\t\tprivate CacheControlConfig enable() {\n\t\t\tif (this.writer == null) {\n\t\t\t\tthis.writer = new CacheControlHeadersWriter();\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class HstsConfig {\n\n\t\tprivate HstsHeaderWriter writer;\n\n\t\tprivate HstsConfig() {\n\t\t\tenable();\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * Sets the value (in seconds) for the max-age directive of the\n\t\t * Strict-Transport-Security header. The default is one year.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * This instructs browsers how long to remember to keep this domain as a known\n\t\t * HSTS Host. See\n\t\t * <a href=\"https://tools.ietf.org/html/rfc6797#section-6.1.1\">Section 6.1.1</a>\n\t\t * for additional details.\n\t\t * </p>\n\t\t * @param maxAgeInSeconds the maximum amount of time (in seconds) to consider this\n\t\t * domain as a known HSTS Host.\n\t\t * @throws IllegalArgumentException if maxAgeInSeconds is negative\n\t\t */\n\t\tpublic HstsConfig maxAgeInSeconds(long maxAgeInSeconds) {\n\t\t\tthis.writer.setMaxAgeInSeconds(maxAgeInSeconds);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link RequestMatcher} used to determine if the\n\t\t * \"Strict-Transport-Security\" should be added. If true the header is added, else\n\t\t * the header is not added. By default the header is added when\n\t\t * {@link HttpServletRequest#isSecure()} returns true.\n\t\t * @param requestMatcher the {@link RequestMatcher} to use.\n\t\t * @throws IllegalArgumentException if {@link RequestMatcher} is null\n\t\t */\n\t\tpublic HstsConfig requestMatcher(RequestMatcher requestMatcher) {\n\t\t\tthis.writer.setRequestMatcher(requestMatcher);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * If true, subdomains should be considered HSTS Hosts too. The default is true.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * See <a href=\"https://tools.ietf.org/html/rfc6797#section-6.1.2\">Section\n\t\t * 6.1.2</a> for additional details.\n\t\t * </p>\n\t\t * @param includeSubDomains true to include subdomains, else false\n\t\t */\n\t\tpublic HstsConfig includeSubDomains(boolean includeSubDomains) {\n\t\t\tthis.writer.setIncludeSubDomains(includeSubDomains);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * If true, preload will be included in HSTS Header. The default is false.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * See <a href=\"https://hstspreload.org/\">Website hstspreload.org</a> for\n\t\t * additional details.\n\t\t * </p>\n\t\t * @param preload true to include preload, else false\n\t\t * @since 5.2.0\n\t\t */\n\t\tpublic HstsConfig preload(boolean preload) {\n\t\t\tthis.writer.setPreload(preload);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Disables Strict Transport Security\n\t\t * @return the {@link HeadersConfigurer} for additional configuration\n\t\t */\n\t\tpublic HeadersConfigurer<H> disable() {\n\t\t\tthis.writer = null;\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Ensures that Strict-Transport-Security is enabled if it is not already\n\t\t * @return the {@link HstsConfig} for additional customization\n\t\t */\n\t\tprivate HstsConfig enable() {\n\t\t\tif (this.writer == null) {\n\t\t\t\tthis.writer = new HstsHeaderWriter();\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class FrameOptionsConfig {\n\n\t\tprivate XFrameOptionsHeaderWriter writer;\n\n\t\tprivate FrameOptionsConfig() {\n\t\t\tenable();\n\t\t}\n\n\t\t/**\n\t\t * Specify to DENY framing any content from this application.\n\t\t * @return the {@link HeadersConfigurer} for additional customization.\n\t\t */\n\t\tpublic HeadersConfigurer<H> deny() {\n\t\t\tthis.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * Specify to allow any request that comes from the same origin to frame this\n\t\t * application. For example, if the application was hosted on example.com, then\n\t\t * example.com could frame the application, but evil.com could not frame the\n\t\t * application.\n\t\t * </p>\n\t\t * @return the {@link HeadersConfigurer} for additional customization.\n\t\t */\n\t\tpublic HeadersConfigurer<H> sameOrigin() {\n\t\t\tthis.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN);\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Prevents the header from being added to the response.\n\t\t * @return the {@link HeadersConfigurer} for additional configuration.\n\t\t */\n\t\tpublic HeadersConfigurer<H> disable() {\n\t\t\tthis.writer = null;\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Enables FrameOptionsConfig if it is not already enabled.\n\t\t * @return the FrameOptionsConfig for additional customization.\n\t\t */\n\t\tprivate FrameOptionsConfig enable() {\n\t\t\tif (this.writer == null) {\n\t\t\t\tthis.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t/**\n\t * @deprecated see <a href=\n\t * \"https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning\">Certificate\n\t * and Public Key Pinning</a> for more context\n\t */\n\t@Deprecated\n\tpublic final class HpkpConfig {\n\n\t\tprivate HpkpHeaderWriter writer;\n\n\t\tprivate HpkpConfig() {\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * Sets the value for the pin- directive of the Public-Key-Pins header.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * The pin directive specifies a way for web host operators to indicate a\n\t\t * cryptographic identity that should be bound to a given web host. See\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.1\">Section 2.1.1</a>\n\t\t * for additional details.\n\t\t * </p>\n\t\t * @param pins the map of base64-encoded SPKI fingerprint &amp; cryptographic hash\n\t\t * algorithm pairs.\n\t\t * @throws IllegalArgumentException if pins is null\n\t\t */\n\t\tpublic HpkpConfig withPins(Map<String, String> pins) {\n\t\t\tthis.writer.setPins(pins);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * Adds a list of SHA256 hashed pins for the pin- directive of the Public-Key-Pins\n\t\t * header.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * The pin directive specifies a way for web host operators to indicate a\n\t\t * cryptographic identity that should be bound to a given web host. See\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.1\">Section 2.1.1</a>\n\t\t * for additional details.\n\t\t * </p>\n\t\t * @param pins a list of base64-encoded SPKI fingerprints.\n\t\t * @throws IllegalArgumentException if a pin is null\n\t\t */\n\t\tpublic HpkpConfig addSha256Pins(String... pins) {\n\t\t\tthis.writer.addSha256Pins(pins);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * Sets the value (in seconds) for the max-age directive of the Public-Key-Pins\n\t\t * header. The default is 60 days.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * This instructs browsers how long they should regard the host (from whom the\n\t\t * message was received) as a known pinned host. See\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.2\">Section 2.1.2</a>\n\t\t * for additional details.\n\t\t * </p>\n\t\t * @param maxAgeInSeconds the maximum amount of time (in seconds) to regard the\n\t\t * host as a known pinned host.\n\t\t * @throws IllegalArgumentException if maxAgeInSeconds is negative\n\t\t */\n\t\tpublic HpkpConfig maxAgeInSeconds(long maxAgeInSeconds) {\n\t\t\tthis.writer.setMaxAgeInSeconds(maxAgeInSeconds);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * If true, the pinning policy applies to this pinned host as well as any\n\t\t * subdomains of the host's domain name. The default is false.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * See <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.3\">Section\n\t\t * 2.1.3</a> for additional details.\n\t\t * </p>\n\t\t * @param includeSubDomains true to include subdomains, else false\n\t\t */\n\t\tpublic HpkpConfig includeSubDomains(boolean includeSubDomains) {\n\t\t\tthis.writer.setIncludeSubDomains(includeSubDomains);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * If true, the browser should not terminate the connection with the server. The\n\t\t * default is true.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * See <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1\">Section 2.1</a>\n\t\t * for additional details.\n\t\t * </p>\n\t\t * @param reportOnly true to report only, else false\n\t\t */\n\t\tpublic HpkpConfig reportOnly(boolean reportOnly) {\n\t\t\tthis.writer.setReportOnly(reportOnly);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * Sets the URI to which the browser should report pin validation failures.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * See <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.4\">Section\n\t\t * 2.1.4</a> for additional details.\n\t\t * </p>\n\t\t * @param reportUri the URI where the browser should send the report to.\n\t\t */\n\t\tpublic HpkpConfig reportUri(URI reportUri) {\n\t\t\tthis.writer.setReportUri(reportUri);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * <p>\n\t\t * Sets the URI to which the browser should report pin validation failures.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * See <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.4\">Section\n\t\t * 2.1.4</a> for additional details.\n\t\t * </p>\n\t\t * @param reportUri the URI where the browser should send the report to.\n\t\t * @throws IllegalArgumentException if the reportUri is not a valid URI\n\t\t */\n\t\tpublic HpkpConfig reportUri(String reportUri) {\n\t\t\tthis.writer.setReportUri(reportUri);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Prevents the header from being added to the response.\n\t\t * @return the {@link HeadersConfigurer} for additional configuration.\n\t\t */\n\t\tpublic HeadersConfigurer<H> disable() {\n\t\t\tthis.writer = null;\n\t\t\treturn and();\n\t\t}\n\n\t\t/**\n\t\t * Allows completing configuration of Public Key Pinning and continuing\n\t\t * configuration of headers.\n\t\t * @return the {@link HeadersConfigurer} for additional configuration\n\t\t */\n\t\tpublic HeadersConfigurer<H> and() {\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Ensures that Public-Key-Pins or Public-Key-Pins-Report-Only is enabled if it is\n\t\t * not already\n\t\t * @return the {@link HstsConfig} for additional customization\n\t\t */\n\t\tprivate HpkpConfig enable() {\n\t\t\tif (this.writer == null) {\n\t\t\t\tthis.writer = new HpkpHeaderWriter();\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class ContentSecurityPolicyConfig {\n\n\t\tprivate ContentSecurityPolicyHeaderWriter writer;\n\n\t\tprivate ContentSecurityPolicyConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the security policy directive(s) to be used in the response header.\n\t\t * @param policyDirectives the security policy directive(s)\n\t\t * @return the {@link ContentSecurityPolicyConfig} for additional configuration\n\t\t * @throws IllegalArgumentException if policyDirectives is null or empty\n\t\t */\n\t\tpublic ContentSecurityPolicyConfig policyDirectives(String policyDirectives) {\n\t\t\tthis.writer.setPolicyDirectives(policyDirectives);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Enables (includes) the Content-Security-Policy-Report-Only header in the\n\t\t * response.\n\t\t * @return the {@link ContentSecurityPolicyConfig} for additional configuration\n\t\t */\n\t\tpublic ContentSecurityPolicyConfig reportOnly() {\n\t\t\tthis.writer.setReportOnly(true);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class ReferrerPolicyConfig {\n\n\t\tprivate ReferrerPolicyHeaderWriter writer;\n\n\t\tprivate ReferrerPolicyConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the policy to be used in the response header.\n\t\t * @param policy a referrer policy\n\t\t * @return the {@link ReferrerPolicyConfig} for additional configuration\n\t\t * @throws IllegalArgumentException if policy is null\n\t\t */\n\t\tpublic ReferrerPolicyConfig policy(ReferrerPolicy policy) {\n\t\t\tthis.writer.setPolicy(policy);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class FeaturePolicyConfig {\n\n\t\tprivate FeaturePolicyHeaderWriter writer;\n\n\t\tprivate FeaturePolicyConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Allows completing configuration of Feature Policy and continuing configuration\n\t\t * of headers.\n\t\t * @return the {@link HeadersConfigurer} for additional configuration\n\t\t */\n\t\tpublic HeadersConfigurer<H> and() {\n\t\t\treturn HeadersConfigurer.this;\n\t\t}\n\n\t}\n\n\tpublic final class PermissionsPolicyConfig {\n\n\t\tprivate PermissionsPolicyHeaderWriter writer;\n\n\t\tprivate PermissionsPolicyConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the policy to be used in the response header.\n\t\t * @param policy a permissions policy\n\t\t * @return the {@link PermissionsPolicyConfig} for additional configuration\n\t\t * @throws IllegalArgumentException if policy is null\n\t\t */\n\t\tpublic PermissionsPolicyConfig policy(String policy) {\n\t\t\tthis.writer.setPolicy(policy);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class CrossOriginOpenerPolicyConfig {\n\n\t\tprivate CrossOriginOpenerPolicyHeaderWriter writer;\n\n\t\tpublic CrossOriginOpenerPolicyConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the policy to be used in the {@code Cross-Origin-Opener-Policy} header\n\t\t * @param openerPolicy a {@code Cross-Origin-Opener-Policy}\n\t\t * @return the {@link CrossOriginOpenerPolicyConfig} for additional configuration\n\t\t * @throws IllegalArgumentException if openerPolicy is null\n\t\t */\n\t\tpublic CrossOriginOpenerPolicyConfig policy(\n\t\t\t\tCrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy openerPolicy) {\n\t\t\tthis.writer.setPolicy(openerPolicy);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class CrossOriginEmbedderPolicyConfig {\n\n\t\tprivate CrossOriginEmbedderPolicyHeaderWriter writer;\n\n\t\tpublic CrossOriginEmbedderPolicyConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the policy to be used in the {@code Cross-Origin-Embedder-Policy} header\n\t\t * @param embedderPolicy a {@code Cross-Origin-Embedder-Policy}\n\t\t * @return the {@link CrossOriginEmbedderPolicyConfig} for additional\n\t\t * configuration\n\t\t * @throws IllegalArgumentException if embedderPolicy is null\n\t\t */\n\t\tpublic CrossOriginEmbedderPolicyConfig policy(\n\t\t\t\tCrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy embedderPolicy) {\n\t\t\tthis.writer.setPolicy(embedderPolicy);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic final class CrossOriginResourcePolicyConfig {\n\n\t\tprivate CrossOriginResourcePolicyHeaderWriter writer;\n\n\t\tpublic CrossOriginResourcePolicyConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the policy to be used in the {@code Cross-Origin-Resource-Policy} header\n\t\t * @param resourcePolicy a {@code Cross-Origin-Resource-Policy}\n\t\t * @return the {@link CrossOriginResourcePolicyConfig} for additional\n\t\t * configuration\n\t\t * @throws IllegalArgumentException if resourcePolicy is null\n\t\t */\n\t\tpublic CrossOriginResourcePolicyConfig policy(\n\t\t\t\tCrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy resourcePolicy) {\n\t\t\tthis.writer.setPolicy(resourcePolicy);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.HttpStatusEntryPoint;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.accept.HeaderContentNegotiationStrategy;\n\n/**\n * Adds HTTP basic based authentication. All attributes have reasonable defaults making\n * all parameters are optional.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link BasicAuthenticationFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * <ul>\n * <li>AuthenticationEntryPoint - populated with the\n * {@link #authenticationEntryPoint(AuthenticationEntryPoint)} (default\n * {@link BasicAuthenticationEntryPoint})</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link AuthenticationManager}</li>\n * <li>{@link RememberMeServices}</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 3.2\n */\npublic final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>\n\t\textends AbstractHttpConfigurer<HttpBasicConfigurer<B>, B> {\n\n\tprivate static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher(\n\t\t\t\"X-Requested-With\", \"XMLHttpRequest\");\n\n\tprivate static final String DEFAULT_REALM = \"Realm\";\n\n\tprivate AuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;\n\n\tprivate BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();\n\n\tprivate SecurityContextRepository securityContextRepository;\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#httpBasic(Customizer)\n\t */\n\tpublic HttpBasicConfigurer() {\n\t\trealmName(DEFAULT_REALM);\n\t\t// @formatter:off\n\t\tthis.authenticationEntryPoint = DelegatingAuthenticationEntryPoint.builder()\n\t\t\t\t.addEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), X_REQUESTED_WITH)\n\t\t\t\t.defaultEntryPoint(this.basicAuthEntryPoint)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Allows easily changing the realm, but leaving the remaining defaults in place. If\n\t * {@link #authenticationEntryPoint(AuthenticationEntryPoint)} has been invoked,\n\t * invoking this method will result in an error.\n\t * @param realmName the HTTP Basic realm to use\n\t * @return {@link HttpBasicConfigurer} for additional customization\n\t */\n\tpublic HttpBasicConfigurer<B> realmName(String realmName) {\n\t\tthis.basicAuthEntryPoint.setRealmName(realmName);\n\t\tthis.basicAuthEntryPoint.afterPropertiesSet();\n\t\treturn this;\n\t}\n\n\t/**\n\t * The {@link AuthenticationEntryPoint} to be populated on\n\t * {@link BasicAuthenticationFilter} in the event that authentication fails. The\n\t * default to use {@link BasicAuthenticationEntryPoint} with the realm \"Realm\".\n\t * @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use\n\t * @return {@link HttpBasicConfigurer} for additional customization\n\t */\n\tpublic HttpBasicConfigurer<B> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies a custom {@link AuthenticationDetailsSource} to use for basic\n\t * authentication. The default is {@link WebAuthenticationDetailsSource}.\n\t * @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}\n\t * to use\n\t * @return {@link HttpBasicConfigurer} for additional customization\n\t */\n\tpublic HttpBasicConfigurer<B> authenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies a custom {@link SecurityContextRepository} to use for basic\n\t * authentication. The default is {@link RequestAttributeSecurityContextRepository}.\n\t * @param securityContextRepository the custom {@link SecurityContextRepository} to\n\t * use\n\t * @return {@link HttpBasicConfigurer} for additional customization\n\t * @since 6.1\n\t */\n\tpublic HttpBasicConfigurer<B> securityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tthis.securityContextRepository = securityContextRepository;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(B http) {\n\t\tregisterDefaults(http);\n\t}\n\n\tprivate void registerDefaults(B http) {\n\t\tContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);\n\t\tif (contentNegotiationStrategy == null) {\n\t\t\tcontentNegotiationStrategy = new HeaderContentNegotiationStrategy();\n\t\t}\n\t\tMediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,\n\t\t\t\tMediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,\n\t\t\t\tMediaType.TEXT_XML);\n\t\trestMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tMediaTypeRequestMatcher allMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.ALL);\n\t\tallMatcher.setUseEquals(true);\n\t\tRequestMatcher notHtmlMatcher = new NegatedRequestMatcher(\n\t\t\t\tnew MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.TEXT_HTML));\n\t\tRequestMatcher restNotHtmlMatcher = new AndRequestMatcher(Arrays.asList(notHtmlMatcher, restMatcher));\n\t\tRequestMatcher preferredMatcher = new OrRequestMatcher(\n\t\t\t\tArrays.asList(X_REQUESTED_WITH, restNotHtmlMatcher, allMatcher));\n\t\tregisterDefaultEntryPoint(http, preferredMatcher);\n\t\tregisterDefaultLogoutSuccessHandler(http, preferredMatcher);\n\t}\n\n\tprivate void registerDefaultEntryPoint(B http, RequestMatcher preferredMatcher) {\n\t\tExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptionHandling == null) {\n\t\t\treturn;\n\t\t}\n\t\tAuthenticationEntryPoint entryPoint = postProcess(this.authenticationEntryPoint);\n\t\texceptionHandling.defaultAuthenticationEntryPointFor(entryPoint, preferredMatcher);\n\t\texceptionHandling.defaultDeniedHandlerForMissingAuthority(\n\t\t\t\t(ep) -> ep.addEntryPointFor(entryPoint, preferredMatcher), FactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t}\n\n\tprivate void registerDefaultLogoutSuccessHandler(B http, RequestMatcher preferredMatcher) {\n\t\tLogoutConfigurer<B> logout = http.getConfigurer(LogoutConfigurer.class);\n\t\tif (logout == null) {\n\t\t\treturn;\n\t\t}\n\t\tLogoutConfigurer<B> handler = logout.defaultLogoutSuccessHandlerFor(\n\t\t\t\tpostProcess(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT)), preferredMatcher);\n\t}\n\n\t@Override\n\tpublic void configure(B http) {\n\t\tAuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);\n\t\tBasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager,\n\t\t\t\tthis.authenticationEntryPoint);\n\t\tif (this.authenticationDetailsSource != null) {\n\t\t\tbasicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);\n\t\t}\n\t\tif (this.securityContextRepository != null) {\n\t\t\tbasicAuthenticationFilter.setSecurityContextRepository(this.securityContextRepository);\n\t\t}\n\t\tRememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);\n\t\tif (rememberMeServices != null) {\n\t\t\tbasicAuthenticationFilter.setRememberMeServices(rememberMeServices);\n\t\t}\n\t\tbasicAuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tbasicAuthenticationFilter = postProcess(basicAuthenticationFilter);\n\t\thttp.addFilter(basicAuthenticationFilter);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/HttpsRedirectConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.transport.HttpsRedirectFilter;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Specifies for what requests the application should redirect to HTTPS. When this\n * configurer is added, it redirects all HTTP requests by default to HTTPS.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link HttpsRedirectFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared objects are created.\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link PortMapper} is used to configure {@link HttpsRedirectFilter}</li>\n * </ul>\n *\n * @param <H> the type of {@link HttpSecurityBuilder} that is being configured\n * @author Josh Cummings\n * @since 6.5\n */\npublic final class HttpsRedirectConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<HeadersConfigurer<H>, H> {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tpublic HttpsRedirectConfigurer<H> requestMatchers(RequestMatcher... matchers) {\n\t\tthis.requestMatcher = new OrRequestMatcher(matchers);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tHttpsRedirectFilter filter = new HttpsRedirectFilter();\n\t\tif (this.requestMatcher != null) {\n\t\t\tfilter.setRequestMatcher(this.requestMatcher);\n\t\t}\n\t\tPortMapper mapper = http.getSharedObject(PortMapper.class);\n\t\tif (mapper != null) {\n\t\t\tfilter.setPortMapper(mapper);\n\t\t}\n\t\thttp.addFilter(filter);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/JeeConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService;\nimport org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;\n\n/**\n * Adds support for J2EE pre authentication.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link J2eePreAuthenticatedProcessingFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * <ul>\n * <li>{@link AuthenticationEntryPoint} is populated with an\n * {@link Http403ForbiddenEntryPoint}</li>\n * <li>A {@link PreAuthenticatedAuthenticationProvider} is populated into\n * {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}\n * </li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link AuthenticationManager}</li>\n * </ul>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<JeeConfigurer<H>, H> {\n\n\tprivate J2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter;\n\n\tprivate AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService;\n\n\tprivate Set<String> mappableRoles = new HashSet<>();\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#jee(Customizer)\n\t */\n\tpublic JeeConfigurer() {\n\t}\n\n\t/**\n\t * Specifies roles to use map from the {@link HttpServletRequest} to the\n\t * {@link UserDetails}. If {@link HttpServletRequest#isUserInRole(String)} returns\n\t * true, the role is added to the {@link UserDetails}. This method is the equivalent\n\t * of invoking {@link #mappableAuthorities(Set)}. Multiple invocations of\n\t * {@link #mappableAuthorities(String...)} will override previous invocations.\n\t *\n\t * <p>\n\t * There are no default roles that are mapped.\n\t * </p>\n\t * @param mappableRoles the roles to attempt to map to the {@link UserDetails} (i.e.\n\t * \"ROLE_USER\", \"ROLE_ADMIN\", etc).\n\t * @return the {@link JeeConfigurer} for further customizations\n\t * @see SimpleMappableAttributesRetriever\n\t * @see #mappableRoles(String...)\n\t */\n\tpublic JeeConfigurer<H> mappableAuthorities(String... mappableRoles) {\n\t\tthis.mappableRoles.clear();\n\t\tfor (String role : mappableRoles) {\n\t\t\tthis.mappableRoles.add(role);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies roles to use map from the {@link HttpServletRequest} to the\n\t * {@link UserDetails} and automatically prefixes it with \"ROLE_\". If\n\t * {@link HttpServletRequest#isUserInRole(String)} returns true, the role is added to\n\t * the {@link UserDetails}. This method is the equivalent of invoking\n\t * {@link #mappableAuthorities(Set)}. Multiple invocations of\n\t * {@link #mappableRoles(String...)} will override previous invocations.\n\t *\n\t * <p>\n\t * There are no default roles that are mapped.\n\t * </p>\n\t * @param mappableRoles the roles to attempt to map to the {@link UserDetails} (i.e.\n\t * \"USER\", \"ADMIN\", etc).\n\t * @return the {@link JeeConfigurer} for further customizations\n\t * @see SimpleMappableAttributesRetriever\n\t * @see #mappableAuthorities(String...)\n\t */\n\tpublic JeeConfigurer<H> mappableRoles(String... mappableRoles) {\n\t\tthis.mappableRoles.clear();\n\t\tfor (String role : mappableRoles) {\n\t\t\tthis.mappableRoles.add(\"ROLE_\" + role);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies roles to use map from the {@link HttpServletRequest} to the\n\t * {@link UserDetails}. If {@link HttpServletRequest#isUserInRole(String)} returns\n\t * true, the role is added to the {@link UserDetails}. This is the equivalent of\n\t * {@link #mappableRoles(String...)}. Multiple invocations of\n\t * {@link #mappableAuthorities(Set)} will override previous invocations.\n\t *\n\t * <p>\n\t * There are no default roles that are mapped.\n\t * </p>\n\t * @param mappableRoles the roles to attempt to map to the {@link UserDetails}.\n\t * @return the {@link JeeConfigurer} for further customizations\n\t * @see SimpleMappableAttributesRetriever\n\t */\n\tpublic JeeConfigurer<H> mappableAuthorities(Set<String> mappableRoles) {\n\t\tthis.mappableRoles = mappableRoles;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the {@link AuthenticationUserDetailsService} that is used with the\n\t * {@link PreAuthenticatedAuthenticationProvider}. The default is a\n\t * {@link PreAuthenticatedGrantedAuthoritiesUserDetailsService}.\n\t * @param authenticatedUserDetailsService the {@link AuthenticationUserDetailsService}\n\t * to use.\n\t * @return the {@link JeeConfigurer} for further configuration\n\t */\n\tpublic JeeConfigurer<H> authenticatedUserDetailsService(\n\t\t\tAuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticatedUserDetailsService) {\n\t\tthis.authenticationUserDetailsService = authenticatedUserDetailsService;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows specifying the {@link J2eePreAuthenticatedProcessingFilter} to use. If\n\t * {@link J2eePreAuthenticatedProcessingFilter} is provided, all of its attributes\n\t * must also be configured manually (i.e. all attributes populated in the\n\t * {@link JeeConfigurer} are not used).\n\t * @param j2eePreAuthenticatedProcessingFilter the\n\t * {@link J2eePreAuthenticatedProcessingFilter} to use.\n\t * @return the {@link JeeConfigurer} for further configuration\n\t */\n\tpublic JeeConfigurer<H> j2eePreAuthenticatedProcessingFilter(\n\t\t\tJ2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter) {\n\t\tthis.j2eePreAuthenticatedProcessingFilter = j2eePreAuthenticatedProcessingFilter;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Populates a {@link PreAuthenticatedAuthenticationProvider} into\n\t * {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}\n\t * and a {@link Http403ForbiddenEntryPoint} into\n\t * {@link HttpSecurityBuilder#setSharedObject(Class, Object)}\n\t *\n\t * @see SecurityConfigurer#init(org.springframework.security.config.annotation.SecurityBuilder)\n\t */\n\t@Override\n\tpublic void init(H http) {\n\t\tPreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();\n\t\tauthenticationProvider.setPreAuthenticatedUserDetailsService(getUserDetailsService());\n\t\tauthenticationProvider = postProcess(authenticationProvider);\n\t\thttp.authenticationProvider(authenticationProvider)\n\t\t\t.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tJ2eePreAuthenticatedProcessingFilter filter = getFilter(http.getSharedObject(AuthenticationManager.class),\n\t\t\t\thttp);\n\t\thttp.addFilter(filter);\n\t}\n\n\t/**\n\t * Gets the {@link J2eePreAuthenticatedProcessingFilter} or creates a default instance\n\t * using the properties provided.\n\t * @param authenticationManager the {@link AuthenticationManager} to use.\n\t * @return the {@link J2eePreAuthenticatedProcessingFilter} to use.\n\t */\n\tprivate J2eePreAuthenticatedProcessingFilter getFilter(AuthenticationManager authenticationManager, H http) {\n\t\tif (this.j2eePreAuthenticatedProcessingFilter == null) {\n\t\t\tthis.j2eePreAuthenticatedProcessingFilter = new J2eePreAuthenticatedProcessingFilter();\n\t\t\tthis.j2eePreAuthenticatedProcessingFilter.setAuthenticationManager(authenticationManager);\n\t\t\tthis.j2eePreAuthenticatedProcessingFilter\n\t\t\t\t.setAuthenticationDetailsSource(createWebAuthenticationDetailsSource());\n\t\t\tthis.j2eePreAuthenticatedProcessingFilter\n\t\t\t\t.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\t\tthis.j2eePreAuthenticatedProcessingFilter = postProcess(this.j2eePreAuthenticatedProcessingFilter);\n\t\t}\n\n\t\treturn this.j2eePreAuthenticatedProcessingFilter;\n\t}\n\n\t/**\n\t * Gets the {@link AuthenticationUserDetailsService} that was specified or defaults to\n\t * {@link PreAuthenticatedGrantedAuthoritiesUserDetailsService}.\n\t * @return the {@link AuthenticationUserDetailsService} to use\n\t */\n\tprivate AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getUserDetailsService() {\n\t\treturn (this.authenticationUserDetailsService != null) ? this.authenticationUserDetailsService\n\t\t\t\t: new PreAuthenticatedGrantedAuthoritiesUserDetailsService();\n\t}\n\n\t/**\n\t * Creates the {@link J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource} to set\n\t * on the {@link J2eePreAuthenticatedProcessingFilter}. It is populated with a\n\t * {@link SimpleMappableAttributesRetriever}.\n\t * @return the {@link J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource} to use.\n\t */\n\tprivate J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource createWebAuthenticationDetailsSource() {\n\t\tJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource detailsSource = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();\n\t\tSimpleMappableAttributesRetriever rolesRetriever = new SimpleMappableAttributesRetriever();\n\t\trolesRetriever.setMappableAttributes(this.mappableRoles);\n\t\tdetailsSource.setMappableRolesRetriever(rolesRetriever);\n\t\tdetailsSource = postProcess(detailsSource);\n\t\treturn detailsSource;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;\nimport org.springframework.security.web.authentication.logout.DelegatingLogoutSuccessHandler;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Adds logout support. Other {@link SecurityConfigurer} instances may invoke\n * {@link #addLogoutHandler(LogoutHandler)} in the\n * {@link SecurityConfigurer#init(org.springframework.security.config.annotation.SecurityBuilder)}\n * phase.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link LogoutFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared Objects are created\n *\n * <h2>Shared Objects Used</h2>\n *\n * No shared objects are used.\n *\n * @author Rob Winch\n * @author Onur Kagan Ozcan\n * @since 3.2\n * @see RememberMeConfigurer\n */\npublic final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<LogoutConfigurer<H>, H> {\n\n\tprivate List<LogoutHandler> logoutHandlers = new ArrayList<>();\n\n\tprivate SecurityContextLogoutHandler contextLogoutHandler = new SecurityContextLogoutHandler();\n\n\tprivate String logoutSuccessUrl = \"/login?logout\";\n\n\tprivate LogoutSuccessHandler logoutSuccessHandler;\n\n\tprivate String logoutUrl = \"/logout\";\n\n\tprivate RequestMatcher logoutRequestMatcher;\n\n\tprivate boolean permitAll;\n\n\tprivate boolean customLogoutSuccess;\n\n\tprivate LinkedHashMap<RequestMatcher, LogoutSuccessHandler> defaultLogoutSuccessHandlerMappings = new LinkedHashMap<>();\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#logout(Customizer)\n\t */\n\tpublic LogoutConfigurer() {\n\t}\n\n\t/**\n\t * Adds a {@link LogoutHandler}. {@link SecurityContextLogoutHandler} and\n\t * {@link LogoutSuccessEventPublishingLogoutHandler} are added as last\n\t * {@link LogoutHandler} instances by default.\n\t * @param logoutHandler the {@link LogoutHandler} to add\n\t * @return the {@link LogoutConfigurer} for further customization\n\t */\n\tpublic LogoutConfigurer<H> addLogoutHandler(LogoutHandler logoutHandler) {\n\t\tAssert.notNull(logoutHandler, \"logoutHandler cannot be null\");\n\t\tthis.logoutHandlers.add(logoutHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies if {@link SecurityContextLogoutHandler} should clear the\n\t * {@link Authentication} at the time of logout.\n\t * @param clearAuthentication true {@link SecurityContextLogoutHandler} should clear\n\t * the {@link Authentication} (default), or false otherwise.\n\t * @return the {@link LogoutConfigurer} for further customization\n\t */\n\tpublic LogoutConfigurer<H> clearAuthentication(boolean clearAuthentication) {\n\t\tthis.contextLogoutHandler.setClearAuthentication(clearAuthentication);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures {@link SecurityContextLogoutHandler} to invalidate the\n\t * {@link HttpSession} at the time of logout.\n\t * @param invalidateHttpSession true if the {@link HttpSession} should be invalidated\n\t * (default), or false otherwise.\n\t * @return the {@link LogoutConfigurer} for further customization\n\t */\n\tpublic LogoutConfigurer<H> invalidateHttpSession(boolean invalidateHttpSession) {\n\t\tthis.contextLogoutHandler.setInvalidateHttpSession(invalidateHttpSession);\n\t\treturn this;\n\t}\n\n\t/**\n\t * The URL that triggers log out to occur (default is \"/logout\"). If CSRF protection\n\t * is enabled (default), then the request must also be a POST. This means that by\n\t * default POST \"/logout\" is required to trigger a log out. If CSRF protection is\n\t * disabled, then any HTTP method is allowed.\n\t *\n\t * <p>\n\t * It is considered best practice to use an HTTP POST on any action that changes state\n\t * (i.e. log out) to protect against\n\t * <a href=\"https://en.wikipedia.org/wiki/Cross-site_request_forgery\">CSRF\n\t * attacks</a>. If you really want to use an HTTP GET, you can use\n\t * <code>logoutRequestMatcher(PathPatternRequestMatcher.pathPattern(HttpMethod.GEt, logoutUrl));</code>\n\t * </p>\n\t * @param logoutUrl the URL that will invoke logout.\n\t * @return the {@link LogoutConfigurer} for further customization\n\t * @see #logoutRequestMatcher(RequestMatcher)\n\t * @see HttpSecurity#csrf(Customizer)\n\t */\n\tpublic LogoutConfigurer<H> logoutUrl(String logoutUrl) {\n\t\tthis.logoutRequestMatcher = null;\n\t\tthis.logoutUrl = logoutUrl;\n\t\treturn this;\n\t}\n\n\t/**\n\t * The RequestMatcher that triggers log out to occur. In most circumstances users will\n\t * use {@link #logoutUrl(String)} which helps enforce good practices.\n\t * @param logoutRequestMatcher the RequestMatcher used to determine if logout should\n\t * occur.\n\t * @return the {@link LogoutConfigurer} for further customization\n\t * @see #logoutUrl(String)\n\t */\n\tpublic LogoutConfigurer<H> logoutRequestMatcher(RequestMatcher logoutRequestMatcher) {\n\t\tthis.logoutRequestMatcher = logoutRequestMatcher;\n\t\treturn this;\n\t}\n\n\t/**\n\t * The URL to redirect to after logout has occurred. The default is \"/login?logout\".\n\t * This is a shortcut for invoking {@link #logoutSuccessHandler(LogoutSuccessHandler)}\n\t * with a {@link SimpleUrlLogoutSuccessHandler}.\n\t * @param logoutSuccessUrl the URL to redirect to after logout occurred\n\t * @return the {@link LogoutConfigurer} for further customization\n\t */\n\tpublic LogoutConfigurer<H> logoutSuccessUrl(String logoutSuccessUrl) {\n\t\tthis.customLogoutSuccess = true;\n\t\tthis.logoutSuccessUrl = logoutSuccessUrl;\n\t\treturn this;\n\t}\n\n\t/**\n\t * A shortcut for {@link #permitAll(boolean)} with <code>true</code> as an argument.\n\t * @return the {@link LogoutConfigurer} for further customizations\n\t */\n\tpublic LogoutConfigurer<H> permitAll() {\n\t\treturn permitAll(true);\n\t}\n\n\t/**\n\t * Allows specifying the names of cookies to be removed on logout success. This is a\n\t * shortcut to easily invoke {@link #addLogoutHandler(LogoutHandler)} with a\n\t * {@link CookieClearingLogoutHandler}.\n\t * @param cookieNamesToClear the names of cookies to be removed on logout success.\n\t * @return the {@link LogoutConfigurer} for further customization\n\t */\n\tpublic LogoutConfigurer<H> deleteCookies(String... cookieNamesToClear) {\n\t\treturn addLogoutHandler(new CookieClearingLogoutHandler(cookieNamesToClear));\n\t}\n\n\t/**\n\t * Sets the {@link LogoutSuccessHandler} to use. If this is specified,\n\t * {@link #logoutSuccessUrl(String)} is ignored.\n\t * @param logoutSuccessHandler the {@link LogoutSuccessHandler} to use after a user\n\t * has been logged out.\n\t * @return the {@link LogoutConfigurer} for further customizations\n\t */\n\tpublic LogoutConfigurer<H> logoutSuccessHandler(LogoutSuccessHandler logoutSuccessHandler) {\n\t\tthis.logoutSuccessUrl = null;\n\t\tthis.customLogoutSuccess = true;\n\t\tthis.logoutSuccessHandler = logoutSuccessHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets a default {@link LogoutSuccessHandler} to be used which prefers being invoked\n\t * for the provided {@link RequestMatcher}. If no {@link LogoutSuccessHandler} is\n\t * specified a {@link SimpleUrlLogoutSuccessHandler} will be used. If any default\n\t * {@link LogoutSuccessHandler} instances are configured, then a\n\t * {@link DelegatingLogoutSuccessHandler} will be used that defaults to a\n\t * {@link SimpleUrlLogoutSuccessHandler}.\n\t * @param handler the {@link LogoutSuccessHandler} to use\n\t * @param preferredMatcher the {@link RequestMatcher} for this default\n\t * {@link LogoutSuccessHandler}\n\t * @return the {@link LogoutConfigurer} for further customizations\n\t */\n\tpublic LogoutConfigurer<H> defaultLogoutSuccessHandlerFor(LogoutSuccessHandler handler,\n\t\t\tRequestMatcher preferredMatcher) {\n\t\tAssert.notNull(handler, \"handler cannot be null\");\n\t\tAssert.notNull(preferredMatcher, \"preferredMatcher cannot be null\");\n\t\tthis.defaultLogoutSuccessHandlerMappings.put(preferredMatcher, handler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Grants access to the {@link #logoutSuccessUrl(String)} and the\n\t * {@link #logoutUrl(String)} for every user.\n\t * @param permitAll if true grants access, else nothing is done\n\t * @return the {@link LogoutConfigurer} for further customization.\n\t */\n\tpublic LogoutConfigurer<H> permitAll(boolean permitAll) {\n\t\tthis.permitAll = permitAll;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the {@link LogoutSuccessHandler} if not null, otherwise creates a new\n\t * {@link SimpleUrlLogoutSuccessHandler} using the {@link #logoutSuccessUrl(String)}.\n\t * @return the {@link LogoutSuccessHandler} to use\n\t */\n\tpublic LogoutSuccessHandler getLogoutSuccessHandler() {\n\t\tLogoutSuccessHandler handler = this.logoutSuccessHandler;\n\t\tif (handler == null) {\n\t\t\thandler = createDefaultSuccessHandler();\n\t\t\tthis.logoutSuccessHandler = handler;\n\t\t}\n\t\treturn handler;\n\t}\n\n\tprivate LogoutSuccessHandler createDefaultSuccessHandler() {\n\t\tSimpleUrlLogoutSuccessHandler urlLogoutHandler = new SimpleUrlLogoutSuccessHandler();\n\t\turlLogoutHandler.setDefaultTargetUrl(this.logoutSuccessUrl);\n\t\tif (this.defaultLogoutSuccessHandlerMappings.isEmpty()) {\n\t\t\treturn urlLogoutHandler;\n\t\t}\n\t\tDelegatingLogoutSuccessHandler successHandler = new DelegatingLogoutSuccessHandler(\n\t\t\t\tthis.defaultLogoutSuccessHandlerMappings);\n\t\tsuccessHandler.setDefaultLogoutSuccessHandler(urlLogoutHandler);\n\t\treturn successHandler;\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\tif (this.permitAll) {\n\t\t\tPermitAllSupport.permitAll(http, this.logoutSuccessUrl);\n\t\t\tPermitAllSupport.permitAll(http, this.getLogoutRequestMatcher(http));\n\t\t}\n\t\tDefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http\n\t\t\t.getSharedObject(DefaultLoginPageGeneratingFilter.class);\n\t\tif (loginPageGeneratingFilter != null && !isCustomLogoutSuccess()) {\n\t\t\tloginPageGeneratingFilter.setLogoutSuccessUrl(getLogoutSuccessUrl());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tLogoutFilter logoutFilter = createLogoutFilter(http);\n\t\thttp.addFilter(logoutFilter);\n\t}\n\n\t/**\n\t * Returns true if the logout success has been customized via\n\t * {@link #logoutSuccessUrl(String)} or\n\t * {@link #logoutSuccessHandler(LogoutSuccessHandler)}.\n\t * @return true if logout success handling has been customized, else false\n\t */\n\tboolean isCustomLogoutSuccess() {\n\t\treturn this.customLogoutSuccess;\n\t}\n\n\t/**\n\t * Gets the logoutSuccessUrl or null if a\n\t * {@link #logoutSuccessHandler(LogoutSuccessHandler)} was configured.\n\t * @return the logoutSuccessUrl\n\t */\n\tprivate String getLogoutSuccessUrl() {\n\t\treturn this.logoutSuccessUrl;\n\t}\n\n\t/**\n\t * Gets the {@link LogoutHandler} instances that will be used.\n\t * @return the {@link LogoutHandler} instances. Cannot be null.\n\t */\n\tpublic List<LogoutHandler> getLogoutHandlers() {\n\t\treturn this.logoutHandlers;\n\t}\n\n\t/**\n\t * Creates the {@link LogoutFilter} using the {@link LogoutHandler} instances, the\n\t * {@link #logoutSuccessHandler(LogoutSuccessHandler)} and the\n\t * {@link #logoutUrl(String)}.\n\t * @param http the builder to use\n\t * @return the {@link LogoutFilter} to use.\n\t */\n\tprivate LogoutFilter createLogoutFilter(H http) {\n\t\tthis.contextLogoutHandler.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tthis.contextLogoutHandler.setSecurityContextRepository(getSecurityContextRepository(http));\n\t\tthis.logoutHandlers.add(this.contextLogoutHandler);\n\t\tthis.logoutHandlers.add(postProcess(new LogoutSuccessEventPublishingLogoutHandler()));\n\t\tLogoutHandler[] handlers = this.logoutHandlers.toArray(new LogoutHandler[0]);\n\t\tLogoutFilter result = new LogoutFilter(getLogoutSuccessHandler(), handlers);\n\t\tresult.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tresult.setLogoutRequestMatcher(getLogoutRequestMatcher(http));\n\t\tresult = postProcess(result);\n\t\treturn result;\n\t}\n\n\tprivate SecurityContextRepository getSecurityContextRepository(H http) {\n\t\tSecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);\n\t\tif (securityContextRepository == null) {\n\t\t\tsecurityContextRepository = new HttpSessionSecurityContextRepository();\n\t\t}\n\t\treturn securityContextRepository;\n\t}\n\n\tprivate RequestMatcher getLogoutRequestMatcher(H http) {\n\t\tif (this.logoutRequestMatcher != null) {\n\t\t\treturn this.logoutRequestMatcher;\n\t\t}\n\t\tthis.logoutRequestMatcher = createLogoutRequestMatcher(http);\n\t\treturn this.logoutRequestMatcher;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate RequestMatcher createLogoutRequestMatcher(H http) {\n\t\tRequestMatcher post = createLogoutRequestMatcher(\"POST\");\n\t\tif (http.getConfigurer(CsrfConfigurer.class) != null) {\n\t\t\treturn post;\n\t\t}\n\t\tRequestMatcher get = createLogoutRequestMatcher(\"GET\");\n\t\tRequestMatcher put = createLogoutRequestMatcher(\"PUT\");\n\t\tRequestMatcher delete = createLogoutRequestMatcher(\"DELETE\");\n\t\treturn new OrRequestMatcher(get, post, put, delete);\n\t}\n\n\tprivate RequestMatcher createLogoutRequestMatcher(String httpMethod) {\n\t\treturn getRequestMatcherBuilder().matcher(HttpMethod.valueOf(httpMethod), this.logoutUrl);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/PasswordManagementConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.web.RequestMatcherRedirectFilter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.util.Assert;\n\n/**\n * Adds password management support.\n *\n * @author Evgeniy Cheban\n * @since 5.6\n */\npublic final class PasswordManagementConfigurer<B extends HttpSecurityBuilder<B>>\n\t\textends AbstractHttpConfigurer<PasswordManagementConfigurer<B>, B> {\n\n\tprivate static final String WELL_KNOWN_CHANGE_PASSWORD_PATTERN = \"/.well-known/change-password\";\n\n\tprivate static final String DEFAULT_CHANGE_PASSWORD_PAGE = \"/change-password\";\n\n\tprivate String changePasswordPage = DEFAULT_CHANGE_PASSWORD_PAGE;\n\n\t/**\n\t * Sets the change password page. Defaults to\n\t * {@link PasswordManagementConfigurer#DEFAULT_CHANGE_PASSWORD_PAGE}.\n\t * @param changePasswordPage the change password page\n\t * @return the {@link PasswordManagementConfigurer} for further customizations\n\t */\n\tpublic PasswordManagementConfigurer<B> changePasswordPage(String changePasswordPage) {\n\t\tAssert.hasText(changePasswordPage, \"changePasswordPage cannot be empty\");\n\t\tthis.changePasswordPage = changePasswordPage;\n\t\treturn this;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void configure(B http) {\n\t\tRequestMatcherRedirectFilter changePasswordFilter = new RequestMatcherRedirectFilter(\n\t\t\t\tgetRequestMatcherBuilder().matcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN), this.changePasswordPage);\n\t\thttp.addFilterBefore(postProcess(changePasswordFilter), UsernamePasswordAuthenticationFilter.class);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/PermitAllSupport.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authorization.SingleResultAuthorizationManager;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configures non-null URL's to grant access to every URL\n *\n * @author Rob Winch\n * @since 3.2\n */\nfinal class PermitAllSupport {\n\n\tprivate PermitAllSupport() {\n\t}\n\n\tstatic void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http, String... urls) {\n\t\tfor (String url : urls) {\n\t\t\tif (url != null) {\n\t\t\t\tpermitAll(http, new ExactUrlRequestMatcher(url));\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tstatic void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http,\n\t\t\tRequestMatcher... requestMatchers) {\n\t\tAuthorizeHttpRequestsConfigurer<?> httpConfigurer = http.getConfigurer(AuthorizeHttpRequestsConfigurer.class);\n\n\t\tAssert.state(httpConfigurer != null,\n\t\t\t\t\"permitAll only works with HttpSecurity.authorizeHttpRequests(). Please define one.\");\n\n\t\tfor (RequestMatcher matcher : requestMatchers) {\n\t\t\tif (matcher != null) {\n\t\t\t\thttpConfigurer.addFirst(matcher, SingleResultAuthorizationManager.permitAll());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static final class ExactUrlRequestMatcher implements RequestMatcher {\n\n\t\tprivate String processUrl;\n\n\t\tprivate ExactUrlRequestMatcher(String processUrl) {\n\t\t\tthis.processUrl = processUrl;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\tString uri = request.getRequestURI();\n\t\t\tString query = request.getQueryString();\n\t\t\tif (query != null) {\n\t\t\t\turi += \"?\" + query;\n\t\t\t}\n\t\t\tif (\"\".equals(request.getContextPath())) {\n\t\t\t\treturn uri.equals(this.processUrl);\n\t\t\t}\n\t\t\treturn uri.equals(request.getContextPath() + this.processUrl);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(\"ExactUrl [processUrl='\").append(this.processUrl).append(\"']\");\n\t\t\treturn sb.toString();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/PortMapperConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.PortMapperImpl;\n\n/**\n * Allows configuring a shared {@link PortMapper} instance used to determine the ports\n * when redirecting between HTTP and HTTPS. The {@link PortMapper} can be obtained from\n * {@link HttpSecurity#getSharedObject(Class)}.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<PortMapperConfigurer<H>, H> {\n\n\tprivate PortMapper portMapper;\n\n\tprivate Map<String, String> httpsPortMappings = new HashMap<>();\n\n\t/**\n\t * Creates a new instance\n\t */\n\tpublic PortMapperConfigurer() {\n\t}\n\n\t/**\n\t * Allows specifying the {@link PortMapper} instance.\n\t * @param portMapper\n\t * @return the {@link PortMapperConfigurer} for further customizations\n\t */\n\tpublic PortMapperConfigurer<H> portMapper(PortMapper portMapper) {\n\t\tthis.portMapper = portMapper;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a port mapping\n\t * @param httpPort the HTTP port that maps to a specific HTTPS port.\n\t * @return {@link HttpPortMapping} to define the HTTPS port\n\t */\n\tpublic HttpPortMapping http(int httpPort) {\n\t\treturn new HttpPortMapping(httpPort);\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\thttp.setSharedObject(PortMapper.class, getPortMapper());\n\t}\n\n\t/**\n\t * Gets the {@link PortMapper} to use. If {@link #portMapper(PortMapper)} was not\n\t * invoked, builds a {@link PortMapperImpl} using the port mappings specified with\n\t * {@link #http(int)}.\n\t * @return the {@link PortMapper} to use\n\t */\n\tprivate PortMapper getPortMapper() {\n\t\tif (this.portMapper == null) {\n\t\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\t\tportMapper.setPortMappings(this.httpsPortMappings);\n\t\t\tthis.portMapper = portMapper;\n\t\t}\n\t\treturn this.portMapper;\n\t}\n\n\t/**\n\t * Allows specifying the HTTPS port for a given HTTP port when redirecting between\n\t * HTTP and HTTPS.\n\t *\n\t * @author Rob Winch\n\t * @since 3.2\n\t */\n\tpublic final class HttpPortMapping {\n\n\t\tprivate final int httpPort;\n\n\t\t/**\n\t\t * Creates a new instance\n\t\t * @param httpPort\n\t\t * @see PortMapperConfigurer#http(int)\n\t\t */\n\t\tprivate HttpPortMapping(int httpPort) {\n\t\t\tthis.httpPort = httpPort;\n\t\t}\n\n\t\t/**\n\t\t * Maps the given HTTP port to the provided HTTPS port and vice versa.\n\t\t * @param httpsPort the HTTPS port to map to\n\t\t * @return the {@link PortMapperConfigurer} for further customization\n\t\t */\n\t\tpublic PortMapperConfigurer<H> mapsTo(int httpsPort) {\n\t\t\tPortMapperConfigurer.this.httpsPortMappings.put(String.valueOf(this.httpPort), String.valueOf(httpsPort));\n\t\t\treturn PortMapperConfigurer.this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.UUID;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.RememberMeAuthenticationProvider;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;\nimport org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\n\n/**\n * Configures Remember Me authentication. This typically involves the user checking a box\n * when they enter their username and password that states to \"Remember Me\".\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link RememberMeAuthenticationFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * The following shared objects are populated\n *\n * <ul>\n * <li>\n * {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}\n * is populated with a {@link RememberMeAuthenticationProvider}</li>\n * <li>{@link RememberMeServices} is populated as a shared object and available on\n * {@link HttpSecurity#getSharedObject(Class)}</li>\n * <li>{@link LogoutConfigurer#addLogoutHandler(LogoutHandler)} is used to add a logout\n * handler to clean up the remember me authentication.</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link AuthenticationManager}</li>\n * <li>{@link UserDetailsService} if no {@link #userDetailsService(UserDetailsService)}\n * was specified.</li>\n * <li>{@link DefaultLoginPageGeneratingFilter} - if present will be populated with\n * information from the configuration</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Eddú Meléndez\n * @author Ngoc Nhan\n * @since 3.2\n */\npublic final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<RememberMeConfigurer<H>, H> {\n\n\t/**\n\t * The default name for remember me parameter name and remember me cookie name\n\t */\n\tprivate static final String DEFAULT_REMEMBER_ME_NAME = \"remember-me\";\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate String key;\n\n\tprivate RememberMeServices rememberMeServices;\n\n\tprivate LogoutHandler logoutHandler;\n\n\tprivate String rememberMeParameter = DEFAULT_REMEMBER_ME_NAME;\n\n\tprivate String rememberMeCookieName = DEFAULT_REMEMBER_ME_NAME;\n\n\tprivate String rememberMeCookieDomain;\n\n\tprivate PersistentTokenRepository tokenRepository;\n\n\tprivate UserDetailsService userDetailsService;\n\n\tprivate Integer tokenValiditySeconds;\n\n\tprivate Boolean useSecureCookie;\n\n\tprivate Boolean alwaysRemember;\n\n\t/**\n\t * Creates a new instance\n\t */\n\tpublic RememberMeConfigurer() {\n\t}\n\n\t/**\n\t * Allows specifying how long (in seconds) a token is valid for\n\t * @param tokenValiditySeconds\n\t * @return {@link RememberMeConfigurer} for further customization\n\t * @see AbstractRememberMeServices#setTokenValiditySeconds(int)\n\t */\n\tpublic RememberMeConfigurer<H> tokenValiditySeconds(int tokenValiditySeconds) {\n\t\tthis.tokenValiditySeconds = tokenValiditySeconds;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Whether the cookie should be flagged as secure or not. Secure cookies can only be\n\t * sent over an HTTPS connection and thus cannot be accidentally submitted over HTTP\n\t * where they could be intercepted.\n\t * <p>\n\t * By default the cookie will be secure if the request is secure. If you only want to\n\t * use remember-me over HTTPS (recommended) you should set this property to\n\t * {@code true}.\n\t * @param useSecureCookie set to {@code true} to always user secure cookies,\n\t * {@code false} to disable their use.\n\t * @return the {@link RememberMeConfigurer} for further customization\n\t * @see AbstractRememberMeServices#setUseSecureCookie(boolean)\n\t */\n\tpublic RememberMeConfigurer<H> useSecureCookie(boolean useSecureCookie) {\n\t\tthis.useSecureCookie = useSecureCookie;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the {@link UserDetailsService} used to look up the {@link UserDetails}\n\t * when a remember me token is valid. When using a\n\t * {@link org.springframework.security.web.SecurityFilterChain} bean, the default is\n\t * to look for a {@link UserDetailsService} bean. Alternatively, one can populate\n\t * {@link #rememberMeServices(RememberMeServices)}.\n\t * @param userDetailsService the {@link UserDetailsService} to configure\n\t * @return the {@link RememberMeConfigurer} for further customization\n\t * @see AbstractRememberMeServices\n\t */\n\tpublic RememberMeConfigurer<H> userDetailsService(UserDetailsService userDetailsService) {\n\t\tthis.userDetailsService = userDetailsService;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the {@link PersistentTokenRepository} to use. The default is to use\n\t * {@link TokenBasedRememberMeServices} instead.\n\t * @param tokenRepository the {@link PersistentTokenRepository} to use\n\t * @return the {@link RememberMeConfigurer} for further customization\n\t */\n\tpublic RememberMeConfigurer<H> tokenRepository(PersistentTokenRepository tokenRepository) {\n\t\tthis.tokenRepository = tokenRepository;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the key to identify tokens created for remember me authentication. Default is\n\t * a secure randomly generated key. If {@link #rememberMeServices(RememberMeServices)}\n\t * is specified and is of type {@link AbstractRememberMeServices}, then the default is\n\t * the key set in {@link AbstractRememberMeServices}.\n\t * @param key the key to identify tokens created for remember me authentication\n\t * @return the {@link RememberMeConfigurer} for further customization\n\t */\n\tpublic RememberMeConfigurer<H> key(String key) {\n\t\tthis.key = key;\n\t\treturn this;\n\t}\n\n\t/**\n\t * The HTTP parameter used to indicate to remember the user at time of login.\n\t * @param rememberMeParameter the HTTP parameter used to indicate to remember the user\n\t * @return the {@link RememberMeConfigurer} for further customization\n\t */\n\tpublic RememberMeConfigurer<H> rememberMeParameter(String rememberMeParameter) {\n\t\tthis.rememberMeParameter = rememberMeParameter;\n\t\treturn this;\n\t}\n\n\t/**\n\t * The name of cookie which store the token for remember me authentication. Defaults\n\t * to 'remember-me'.\n\t * @param rememberMeCookieName the name of cookie which store the token for remember\n\t * me authentication\n\t * @return the {@link RememberMeConfigurer} for further customization\n\t * @since 4.0.1\n\t */\n\tpublic RememberMeConfigurer<H> rememberMeCookieName(String rememberMeCookieName) {\n\t\tthis.rememberMeCookieName = rememberMeCookieName;\n\t\treturn this;\n\t}\n\n\t/**\n\t * The domain name within which the remember me cookie is visible.\n\t * @param rememberMeCookieDomain the domain name within which the remember me cookie\n\t * is visible.\n\t * @return the {@link RememberMeConfigurer} for further customization\n\t * @since 4.1.0\n\t */\n\tpublic RememberMeConfigurer<H> rememberMeCookieDomain(String rememberMeCookieDomain) {\n\t\tthis.rememberMeCookieDomain = rememberMeCookieDomain;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows control over the destination a remembered user is sent to when they are\n\t * successfully authenticated. By default, the filter will just allow the current\n\t * request to proceed, but if an {@code AuthenticationSuccessHandler} is set, it will\n\t * be invoked and the {@code doFilter()} method will return immediately, thus allowing\n\t * the application to redirect the user to a specific URL, regardless of what the\n\t * original request was for.\n\t * @param authenticationSuccessHandler the strategy to invoke immediately before\n\t * returning from {@code doFilter()}.\n\t * @return {@link RememberMeConfigurer} for further customization\n\t * @see RememberMeAuthenticationFilter#setAuthenticationSuccessHandler(AuthenticationSuccessHandler)\n\t */\n\tpublic RememberMeConfigurer<H> authenticationSuccessHandler(\n\t\t\tAuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the {@link RememberMeServices} to use.\n\t * @param rememberMeServices the {@link RememberMeServices} to use\n\t * @return the {@link RememberMeConfigurer} for further customizations\n\t * @see RememberMeServices\n\t */\n\tpublic RememberMeConfigurer<H> rememberMeServices(RememberMeServices rememberMeServices) {\n\t\tthis.rememberMeServices = rememberMeServices;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Whether the cookie should always be created even if the remember-me parameter is\n\t * not set.\n\t * <p>\n\t * By default this will be set to {@code false}.\n\t * @param alwaysRemember set to {@code true} to always trigger remember me,\n\t * {@code false} to use the remember-me parameter.\n\t * @return the {@link RememberMeConfigurer} for further customization\n\t * @see AbstractRememberMeServices#setAlwaysRemember(boolean)\n\t */\n\tpublic RememberMeConfigurer<H> alwaysRemember(boolean alwaysRemember) {\n\t\tthis.alwaysRemember = alwaysRemember;\n\t\treturn this;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void init(H http) {\n\t\tvalidateInput();\n\t\tString key = getKey();\n\t\tRememberMeServices rememberMeServices = getRememberMeServices(http, key);\n\t\thttp.setSharedObject(RememberMeServices.class, rememberMeServices);\n\t\tLogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);\n\t\tif (logoutConfigurer != null && this.logoutHandler != null) {\n\t\t\tlogoutConfigurer.addLogoutHandler(this.logoutHandler);\n\t\t}\n\t\tRememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(key);\n\t\tauthenticationProvider = postProcess(authenticationProvider);\n\t\thttp.authenticationProvider(authenticationProvider);\n\t\tinitDefaultLoginFilter(http);\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tRememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter(\n\t\t\t\thttp.getSharedObject(AuthenticationManager.class), this.rememberMeServices);\n\t\tif (this.authenticationSuccessHandler != null) {\n\t\t\trememberMeFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);\n\t\t}\n\t\tSecurityContextConfigurer<?> securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);\n\t\tif (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {\n\t\t\tSecurityContextRepository securityContextRepository = securityContextConfigurer\n\t\t\t\t.getSecurityContextRepository();\n\t\t\trememberMeFilter.setSecurityContextRepository(securityContextRepository);\n\t\t}\n\t\trememberMeFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\n\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy = http\n\t\t\t.getSharedObject(SessionAuthenticationStrategy.class);\n\t\tif (sessionAuthenticationStrategy != null) {\n\t\t\trememberMeFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);\n\t\t}\n\n\t\trememberMeFilter = postProcess(rememberMeFilter);\n\t\thttp.addFilter(rememberMeFilter);\n\t}\n\n\t/**\n\t * Validate rememberMeServices and rememberMeCookieName have not been set at the same\n\t * time.\n\t */\n\tprivate void validateInput() {\n\t\tif (this.rememberMeServices != null && !DEFAULT_REMEMBER_ME_NAME.equals(this.rememberMeCookieName)) {\n\t\t\tthrow new IllegalArgumentException(\"Can not set rememberMeCookieName and custom rememberMeServices.\");\n\t\t}\n\t}\n\n\t/**\n\t * Returns the HTTP parameter used to indicate to remember the user at time of login.\n\t * @return the HTTP parameter used to indicate to remember the user\n\t */\n\tprivate String getRememberMeParameter() {\n\t\treturn this.rememberMeParameter;\n\t}\n\n\t/**\n\t * If available, initializes the {@link DefaultLoginPageGeneratingFilter} shared\n\t * object.\n\t * @param http the {@link HttpSecurityBuilder} to use\n\t */\n\tprivate void initDefaultLoginFilter(H http) {\n\t\tDefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http\n\t\t\t.getSharedObject(DefaultLoginPageGeneratingFilter.class);\n\t\tif (loginPageGeneratingFilter != null) {\n\t\t\tloginPageGeneratingFilter.setRememberMeParameter(getRememberMeParameter());\n\t\t}\n\t}\n\n\t/**\n\t * Gets the {@link RememberMeServices} or creates the {@link RememberMeServices}.\n\t * @param http the {@link HttpSecurity} to lookup shared objects\n\t * @param key the {@link #key(String)}\n\t * @return the {@link RememberMeServices} to use\n\t * @throws Exception\n\t */\n\tprivate RememberMeServices getRememberMeServices(H http, String key) {\n\t\tif (this.rememberMeServices != null) {\n\t\t\tif (this.rememberMeServices instanceof LogoutHandler && this.logoutHandler == null) {\n\t\t\t\tthis.logoutHandler = (LogoutHandler) this.rememberMeServices;\n\t\t\t}\n\t\t\treturn this.rememberMeServices;\n\t\t}\n\t\tAbstractRememberMeServices tokenRememberMeServices = createRememberMeServices(http, key);\n\t\ttokenRememberMeServices.setParameter(this.rememberMeParameter);\n\t\ttokenRememberMeServices.setCookieName(this.rememberMeCookieName);\n\t\tif (this.rememberMeCookieDomain != null) {\n\t\t\ttokenRememberMeServices.setCookieDomain(this.rememberMeCookieDomain);\n\t\t}\n\t\tif (this.tokenValiditySeconds != null) {\n\t\t\ttokenRememberMeServices.setTokenValiditySeconds(this.tokenValiditySeconds);\n\t\t}\n\t\tif (this.useSecureCookie != null) {\n\t\t\ttokenRememberMeServices.setUseSecureCookie(this.useSecureCookie);\n\t\t}\n\t\tif (this.alwaysRemember != null) {\n\t\t\ttokenRememberMeServices.setAlwaysRemember(this.alwaysRemember);\n\t\t}\n\t\ttokenRememberMeServices.afterPropertiesSet();\n\t\tthis.logoutHandler = tokenRememberMeServices;\n\t\tthis.rememberMeServices = tokenRememberMeServices;\n\t\treturn tokenRememberMeServices;\n\t}\n\n\t/**\n\t * Creates the {@link RememberMeServices} to use when none is provided. The result is\n\t * either {@link PersistentTokenRepository} (if a {@link PersistentTokenRepository} is\n\t * specified, else {@link TokenBasedRememberMeServices}.\n\t * @param http the {@link HttpSecurity} to lookup shared objects\n\t * @param key the {@link #key(String)}\n\t * @return the {@link RememberMeServices} to use\n\t */\n\tprivate AbstractRememberMeServices createRememberMeServices(H http, String key) {\n\t\treturn (this.tokenRepository != null) ? createPersistentRememberMeServices(http, key)\n\t\t\t\t: createTokenBasedRememberMeServices(http, key);\n\t}\n\n\t/**\n\t * Creates {@link TokenBasedRememberMeServices}\n\t * @param http the {@link HttpSecurity} to lookup shared objects\n\t * @param key the {@link #key(String)}\n\t * @return the {@link TokenBasedRememberMeServices}\n\t */\n\tprivate AbstractRememberMeServices createTokenBasedRememberMeServices(H http, String key) {\n\t\tUserDetailsService userDetailsService = getUserDetailsService(http);\n\t\treturn new TokenBasedRememberMeServices(key, userDetailsService);\n\t}\n\n\t/**\n\t * Creates {@link PersistentTokenBasedRememberMeServices}\n\t * @param http the {@link HttpSecurity} to lookup shared objects\n\t * @param key the {@link #key(String)}\n\t * @return the {@link PersistentTokenBasedRememberMeServices}\n\t */\n\tprivate AbstractRememberMeServices createPersistentRememberMeServices(H http, String key) {\n\t\tUserDetailsService userDetailsService = getUserDetailsService(http);\n\t\treturn new PersistentTokenBasedRememberMeServices(key, userDetailsService, this.tokenRepository);\n\t}\n\n\t/**\n\t * Gets the {@link UserDetailsService} to use. Either the explicitly configured\n\t * {@link UserDetailsService} from {@link #userDetailsService(UserDetailsService)}, a\n\t * shared object from {@link HttpSecurity#getSharedObject(Class)} or the\n\t * {@link UserDetailsService} bean.\n\t * @param http {@link HttpSecurity} to get the shared {@link UserDetailsService}\n\t * @return the {@link UserDetailsService} to use\n\t */\n\tprivate UserDetailsService getUserDetailsService(H http) {\n\t\tif (this.userDetailsService == null) {\n\t\t\tthis.userDetailsService = getSharedOrBean(http, UserDetailsService.class);\n\t\t}\n\t\tAssert.state(this.userDetailsService != null,\n\t\t\t\t() -> \"userDetailsService cannot be null. Invoke \" + RememberMeConfigurer.class.getSimpleName()\n\t\t\t\t\t\t+ \"#userDetailsService(UserDetailsService) or see its javadoc for alternative approaches.\");\n\t\treturn this.userDetailsService;\n\t}\n\n\t/**\n\t * Gets the key to use for validating remember me tokens. If a value was passed into\n\t * {@link #key(String)}, then that is returned. Alternatively, if a key was specified\n\t * in the {@link #rememberMeServices(RememberMeServices)}}, then that is returned. If\n\t * no key was specified in either of those cases, then a secure random string is\n\t * generated.\n\t * @return the remember me key to use\n\t */\n\tprivate String getKey() {\n\t\tif (this.key == null) {\n\t\t\tif (this.rememberMeServices instanceof AbstractRememberMeServices) {\n\t\t\t\tthis.key = ((AbstractRememberMeServices) this.rememberMeServices).getKey();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.key = UUID.randomUUID().toString();\n\t\t\t}\n\t\t}\n\t\treturn this.key;\n\t}\n\n\tprivate <C> C getSharedOrBean(H http, Class<C> type) {\n\t\tC shared = http.getSharedObject(type);\n\t\tif (shared != null) {\n\t\t\treturn shared;\n\t\t}\n\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn context.getBeanProvider(type).getIfUnique();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.NullRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.RequestCacheAwareFilter;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.accept.HeaderContentNegotiationStrategy;\n\n/**\n * Adds request cache for Spring Security. Specifically this ensures that requests that\n * are saved (i.e. after authentication is required) are later replayed. All properties\n * have reasonable defaults, so no additional configuration is required other than\n * applying this {@link org.springframework.security.config.annotation.SecurityConfigurer}\n * .\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link RequestCacheAwareFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared objects are created.\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>If no explicit {@link RequestCache}, is provided a {@link RequestCache} shared\n * object is used to replay the request after authentication is successful</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Ngoc Nhan\n * @since 3.2\n * @see RequestCache\n */\npublic final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<RequestCacheConfigurer<H>, H> {\n\n\tpublic RequestCacheConfigurer() {\n\t}\n\n\t/**\n\t * Allows explicit configuration of the {@link RequestCache} to be used. Defaults to\n\t * try finding a {@link RequestCache} as a shared object. Then falls back to a\n\t * {@link HttpSessionRequestCache}.\n\t * @param requestCache the explicit {@link RequestCache} to use\n\t * @return the {@link RequestCacheConfigurer} for further customization\n\t */\n\tpublic RequestCacheConfigurer<H> requestCache(RequestCache requestCache) {\n\t\tgetBuilder().setSharedObject(RequestCache.class, requestCache);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic H disable() {\n\t\tgetBuilder().setSharedObject(RequestCache.class, new NullRequestCache());\n\t\treturn super.disable();\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\thttp.setSharedObject(RequestCache.class, getRequestCache(http));\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tRequestCache requestCache = getRequestCache(http);\n\t\tRequestCacheAwareFilter requestCacheFilter = new RequestCacheAwareFilter(requestCache);\n\t\trequestCacheFilter = postProcess(requestCacheFilter);\n\t\thttp.addFilter(requestCacheFilter);\n\t}\n\n\t/**\n\t * Gets the {@link RequestCache} to use. If one is defined using\n\t * {@link #requestCache(org.springframework.security.web.savedrequest.RequestCache)},\n\t * then it is used. Otherwise, an attempt to find a {@link RequestCache} shared object\n\t * is made. If that fails, an {@link HttpSessionRequestCache} is used\n\t * @param http the {@link HttpSecurity} to attempt to fined the shared object\n\t * @return the {@link RequestCache} to use\n\t */\n\tprivate RequestCache getRequestCache(H http) {\n\t\tRequestCache result = http.getSharedObject(RequestCache.class);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\tresult = getBeanOrNull(RequestCache.class);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\tHttpSessionRequestCache defaultCache = new HttpSessionRequestCache();\n\t\tdefaultCache.setRequestMatcher(createDefaultSavedRequestMatcher(http));\n\t\treturn defaultCache;\n\t}\n\n\tprivate <T> T getBeanOrNull(Class<T> type) {\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn context.getBeanProvider(type).getIfUnique();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate RequestMatcher createDefaultSavedRequestMatcher(H http) {\n\t\tRequestMatcher notFavIcon = new NegatedRequestMatcher(getFaviconRequestMatcher());\n\t\tRequestMatcher notXRequestedWith = new NegatedRequestMatcher(\n\t\t\t\tnew RequestHeaderRequestMatcher(\"X-Requested-With\", \"XMLHttpRequest\"));\n\t\tRequestMatcher notWebSocket = new NegatedRequestMatcher(\n\t\t\t\tnew RequestHeaderRequestMatcher(\"Upgrade\", \"websocket\"));\n\n\t\tboolean isCsrfEnabled = http.getConfigurer(CsrfConfigurer.class) != null;\n\t\tList<RequestMatcher> matchers = new ArrayList<>();\n\t\tif (isCsrfEnabled) {\n\t\t\tRequestMatcher getRequests = getRequestMatcherBuilder().matcher(HttpMethod.GET, \"/**\");\n\t\t\tmatchers.add(0, getRequests);\n\t\t}\n\t\tmatchers.add(notFavIcon);\n\t\tmatchers.add(notMatchingMediaType(http, MediaType.APPLICATION_JSON));\n\t\tmatchers.add(notXRequestedWith);\n\t\tmatchers.add(notMatchingMediaType(http, MediaType.MULTIPART_FORM_DATA));\n\t\tmatchers.add(notMatchingMediaType(http, MediaType.TEXT_EVENT_STREAM));\n\t\tmatchers.add(notWebSocket);\n\t\treturn new AndRequestMatcher(matchers);\n\t}\n\n\tprivate RequestMatcher notMatchingMediaType(H http, MediaType mediaType) {\n\t\tContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);\n\t\tif (contentNegotiationStrategy == null) {\n\t\t\tcontentNegotiationStrategy = new HeaderContentNegotiationStrategy();\n\t\t}\n\t\tMediaTypeRequestMatcher mediaRequest = new MediaTypeRequestMatcher(contentNegotiationStrategy, mediaType);\n\t\tmediaRequest.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\treturn new NegatedRequestMatcher(mediaRequest);\n\t}\n\n\tprivate RequestMatcher getFaviconRequestMatcher() {\n\t\treturn getRequestMatcherBuilder().matcher(\"/favicon.*\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.context.DelegatingSecurityContextRepository;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.session.ForceEagerSessionCreationFilter;\n\n/**\n * Allows persisting and restoring of the {@link SecurityContext} found on the\n * {@link SecurityContextHolder} for each request by configuring the\n * {@link SecurityContextPersistenceFilter}. All properties have reasonable defaults, so\n * no additional configuration is required other than applying this\n * {@link org.springframework.security.config.annotation.SecurityConfigurer}.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link SecurityContextPersistenceFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared objects are created.\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>If {@link SessionManagementConfigurer}, is provided and set to always, then the\n * {@link SecurityContextPersistenceFilter#setForceEagerSessionCreation(boolean)} will be\n * set to true.</li>\n * <li>{@link SecurityContextRepository} must be set and is used on\n * {@link SecurityContextPersistenceFilter}.</li>\n * </ul>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<SecurityContextConfigurer<H>, H> {\n\n\tprivate boolean requireExplicitSave = true;\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#securityContext(Customizer)\n\t */\n\tpublic SecurityContextConfigurer() {\n\t}\n\n\t/**\n\t * Specifies the shared {@link SecurityContextRepository} that is to be used\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use\n\t * @return the {@link HttpSecurity} for further customizations\n\t */\n\tpublic SecurityContextConfigurer<H> securityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tgetBuilder().setSharedObject(SecurityContextRepository.class, securityContextRepository);\n\t\treturn this;\n\t}\n\n\tpublic SecurityContextConfigurer<H> requireExplicitSave(boolean requireExplicitSave) {\n\t\tthis.requireExplicitSave = requireExplicitSave;\n\t\treturn this;\n\t}\n\n\tboolean isRequireExplicitSave() {\n\t\treturn this.requireExplicitSave;\n\t}\n\n\tSecurityContextRepository getSecurityContextRepository() {\n\t\tSecurityContextRepository securityContextRepository = getBuilder()\n\t\t\t.getSharedObject(SecurityContextRepository.class);\n\t\tif (securityContextRepository == null) {\n\t\t\tsecurityContextRepository = new DelegatingSecurityContextRepository(\n\t\t\t\t\tnew RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository());\n\t\t}\n\t\treturn securityContextRepository;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void configure(H http) {\n\t\tSecurityContextRepository securityContextRepository = getSecurityContextRepository();\n\t\tif (this.requireExplicitSave) {\n\t\t\tSecurityContextHolderFilter securityContextHolderFilter = postProcess(\n\t\t\t\t\tnew SecurityContextHolderFilter(securityContextRepository));\n\t\t\tsecurityContextHolderFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\t\thttp.addFilter(securityContextHolderFilter);\n\t\t}\n\t\telse {\n\t\t\tSecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(\n\t\t\t\t\tsecurityContextRepository);\n\t\t\tsecurityContextFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\t\tSessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);\n\t\t\tSessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)\n\t\t\t\t\t? sessionManagement.getSessionCreationPolicy() : null;\n\t\t\tif (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {\n\t\t\t\tsecurityContextFilter.setForceEagerSessionCreation(true);\n\t\t\t\thttp.addFilter(postProcess(new ForceEagerSessionCreationFilter()));\n\t\t\t}\n\t\t\tsecurityContextFilter = postProcess(securityContextFilter);\n\t\t\thttp.addFilter(securityContextFilter);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/ServletApiConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\n\n/**\n * Implements select methods from the {@link HttpServletRequest} using the\n * {@link SecurityContext} from the {@link SecurityContextHolder}.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link SecurityContextHolderAwareRequestFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared objects are created.\n *\n * <h2>Shared Objects Used</h2>\n *\n * <ul>\n * <li>{@link AuthenticationTrustResolver} is optionally used to populate the\n * {@link SecurityContextHolderAwareRequestFilter}</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Ngoc Nhan\n * @since 3.2\n */\npublic final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<ServletApiConfigurer<H>, H> {\n\n\tprivate SecurityContextHolderAwareRequestFilter securityContextRequestFilter = new SecurityContextHolderAwareRequestFilter();\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#servletApi(Customizer)\n\t */\n\tpublic ServletApiConfigurer() {\n\t}\n\n\tpublic ServletApiConfigurer<H> rolePrefix(String rolePrefix) {\n\t\tthis.securityContextRequestFilter.setRolePrefix(rolePrefix);\n\t\treturn this;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void configure(H http) {\n\t\tthis.securityContextRequestFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));\n\t\tExceptionHandlingConfigurer<H> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tAuthenticationEntryPoint authenticationEntryPoint = (exceptionConf != null)\n\t\t\t\t? exceptionConf.getAuthenticationEntryPoint(http) : null;\n\t\tthis.securityContextRequestFilter.setAuthenticationEntryPoint(authenticationEntryPoint);\n\t\tLogoutConfigurer<H> logoutConf = http.getConfigurer(LogoutConfigurer.class);\n\t\tList<LogoutHandler> logoutHandlers = (logoutConf != null) ? logoutConf.getLogoutHandlers() : null;\n\t\tthis.securityContextRequestFilter.setLogoutHandlers(logoutHandlers);\n\t\tAuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);\n\t\tif (trustResolver != null) {\n\t\t\tthis.securityContextRequestFilter.setTrustResolver(trustResolver);\n\t\t}\n\t\tApplicationContext context = http.getSharedObject(ApplicationContext.class);\n\t\tif (context != null) {\n\t\t\tcontext.getBeanProvider(GrantedAuthorityDefaults.class)\n\t\t\t\t.ifUnique((grantedAuthorityDefaults) -> this.securityContextRequestFilter\n\t\t\t\t\t.setRolePrefix(grantedAuthorityDefaults.getRolePrefix()));\n\t\t\tthis.securityContextRequestFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\t}\n\t\tthis.securityContextRequestFilter = postProcess(this.securityContextRequestFilter);\n\t\thttp.addFilter(this.securityContextRequestFilter);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.GenericApplicationListenerAdapter;\nimport org.springframework.context.event.SmartApplicationListener;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.context.DelegatingApplicationListener;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.core.session.SessionRegistryImpl;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;\nimport org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;\nimport org.springframework.security.web.authentication.session.SessionLimit;\nimport org.springframework.security.web.context.DelegatingSecurityContextRepository;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.NullSecurityContextRepository;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.savedrequest.NullRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.session.ConcurrentSessionFilter;\nimport org.springframework.security.web.session.DisableEncodeUrlFilter;\nimport org.springframework.security.web.session.ForceEagerSessionCreationFilter;\nimport org.springframework.security.web.session.InvalidSessionStrategy;\nimport org.springframework.security.web.session.SessionInformationExpiredStrategy;\nimport org.springframework.security.web.session.SessionManagementFilter;\nimport org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;\nimport org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Allows configuring session management.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link SessionManagementFilter}</li>\n * <li>{@link ConcurrentSessionFilter} if there are restrictions on how many concurrent\n * sessions a user can have</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * The following shared objects are created:\n *\n * <ul>\n * <li>{@link RequestCache}</li>\n * <li>{@link SecurityContextRepository}</li>\n * <li>{@link SessionManagementConfigurer}</li>\n * <li>{@link InvalidSessionStrategy}</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * <ul>\n * <li>{@link SecurityContextRepository}</li>\n * <li>{@link AuthenticationTrustResolver} is optionally used to populate the\n * {@link HttpSessionSecurityContextRepository} and {@link SessionManagementFilter}</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Onur Kagan Ozcan\n * @author Ngoc Nhan\n * @since 3.2\n * @see SessionManagementFilter\n * @see ConcurrentSessionFilter\n */\npublic final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<SessionManagementConfigurer<H>, H> {\n\n\tprivate final SessionAuthenticationStrategy DEFAULT_SESSION_FIXATION_STRATEGY = createDefaultSessionFixationProtectionStrategy();\n\n\tprivate SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = this.DEFAULT_SESSION_FIXATION_STRATEGY;\n\n\tprivate SessionAuthenticationStrategy sessionAuthenticationStrategy;\n\n\tprivate SessionAuthenticationStrategy providedSessionAuthenticationStrategy;\n\n\tprivate InvalidSessionStrategy invalidSessionStrategy;\n\n\tprivate SessionInformationExpiredStrategy expiredSessionStrategy;\n\n\tprivate List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<>();\n\n\tprivate SessionRegistry sessionRegistry;\n\n\tprivate SessionLimit sessionLimit;\n\n\tprivate String expiredUrl;\n\n\tprivate boolean maxSessionsPreventsLogin;\n\n\tprivate SessionCreationPolicy sessionPolicy;\n\n\tprivate boolean enableSessionUrlRewriting;\n\n\tprivate String invalidSessionUrl;\n\n\tprivate String sessionAuthenticationErrorUrl;\n\n\tprivate AuthenticationFailureHandler sessionAuthenticationFailureHandler;\n\n\tprivate Set<String> propertiesThatRequireImplicitAuthentication = new HashSet<>();\n\n\tprivate Boolean requireExplicitAuthenticationStrategy;\n\n\t/**\n\t * This should not use RequestAttributeSecurityContextRepository since that is\n\t * stateless and session management is about state management.\n\t */\n\tprivate SecurityContextRepository sessionManagementSecurityContextRepository = new HttpSessionSecurityContextRepository();\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#sessionManagement(Customizer)\n\t */\n\tpublic SessionManagementConfigurer() {\n\t}\n\n\t/**\n\t * Setting this attribute will inject the {@link SessionManagementFilter} with a\n\t * {@link SimpleRedirectInvalidSessionStrategy} configured with the attribute value.\n\t * When an invalid session ID is submitted, the strategy will be invoked, redirecting\n\t * to the configured URL.\n\t * @param invalidSessionUrl the URL to redirect to when an invalid session is detected\n\t * @return the {@link SessionManagementConfigurer} for further customization\n\t */\n\tpublic SessionManagementConfigurer<H> invalidSessionUrl(String invalidSessionUrl) {\n\t\tthis.invalidSessionUrl = invalidSessionUrl;\n\t\tthis.propertiesThatRequireImplicitAuthentication.add(\"invalidSessionUrl = \" + invalidSessionUrl);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Setting this means that explicit invocation of\n\t * {@link SessionAuthenticationStrategy} is required.\n\t * @param requireExplicitAuthenticationStrategy require explicit invocation of\n\t * {@link SessionAuthenticationStrategy}\n\t * @return the {@link SessionManagementConfigurer} for further customization\n\t */\n\tpublic SessionManagementConfigurer<H> requireExplicitAuthenticationStrategy(\n\t\t\tboolean requireExplicitAuthenticationStrategy) {\n\t\tthis.requireExplicitAuthenticationStrategy = requireExplicitAuthenticationStrategy;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Setting this attribute will inject the provided invalidSessionStrategy into the\n\t * {@link SessionManagementFilter}. When an invalid session ID is submitted, the\n\t * strategy will be invoked, redirecting to the configured URL.\n\t * @param invalidSessionStrategy the strategy to use when an invalid session ID is\n\t * submitted.\n\t * @return the {@link SessionManagementConfigurer} for further customization\n\t */\n\tpublic SessionManagementConfigurer<H> invalidSessionStrategy(InvalidSessionStrategy invalidSessionStrategy) {\n\t\tAssert.notNull(invalidSessionStrategy, \"invalidSessionStrategy\");\n\t\tthis.invalidSessionStrategy = invalidSessionStrategy;\n\t\tthis.propertiesThatRequireImplicitAuthentication.add(\"invalidSessionStrategy = \" + invalidSessionStrategy);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Defines the URL of the error page which should be shown when the\n\t * SessionAuthenticationStrategy raises an exception. If not set, an unauthorized\n\t * (402) error code will be returned to the client. Note that this attribute doesn't\n\t * apply if the error occurs during a form-based login, where the URL for\n\t * authentication failure will take precedence.\n\t * @param sessionAuthenticationErrorUrl the URL to redirect to\n\t * @return the {@link SessionManagementConfigurer} for further customization\n\t */\n\tpublic SessionManagementConfigurer<H> sessionAuthenticationErrorUrl(String sessionAuthenticationErrorUrl) {\n\t\tthis.sessionAuthenticationErrorUrl = sessionAuthenticationErrorUrl;\n\t\tthis.propertiesThatRequireImplicitAuthentication\n\t\t\t.add(\"sessionAuthenticationErrorUrl = \" + sessionAuthenticationErrorUrl);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Defines the {@code AuthenticationFailureHandler} which will be used when the\n\t * SessionAuthenticationStrategy raises an exception. If not set, an unauthorized\n\t * (402) error code will be returned to the client. Note that this attribute doesn't\n\t * apply if the error occurs during a form-based login, where the URL for\n\t * authentication failure will take precedence.\n\t * @param sessionAuthenticationFailureHandler the handler to use\n\t * @return the {@link SessionManagementConfigurer} for further customization\n\t */\n\tpublic SessionManagementConfigurer<H> sessionAuthenticationFailureHandler(\n\t\t\tAuthenticationFailureHandler sessionAuthenticationFailureHandler) {\n\t\tthis.sessionAuthenticationFailureHandler = sessionAuthenticationFailureHandler;\n\t\tthis.propertiesThatRequireImplicitAuthentication\n\t\t\t.add(\"sessionAuthenticationFailureHandler = \" + sessionAuthenticationFailureHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * If set to true, allows HTTP sessions to be rewritten in the URLs when using\n\t * {@link HttpServletResponse#encodeRedirectURL(String)} or\n\t * {@link HttpServletResponse#encodeURL(String)}, otherwise disallows HTTP sessions to\n\t * be included in the URL. This prevents leaking information to external domains.\n\t * <p>\n\t * This is achieved by guarding {@link HttpServletResponse#encodeURL} and\n\t * {@link HttpServletResponse#encodeRedirectURL} invocations. Any code that also\n\t * overrides either of these two methods, like\n\t * {@link org.springframework.web.servlet.resource.ResourceUrlEncodingFilter}, needs\n\t * to come after the security filter chain or risk being skipped.\n\t * @param enableSessionUrlRewriting true if should allow the JSESSIONID to be\n\t * rewritten into the URLs, else false (default)\n\t * @return the {@link SessionManagementConfigurer} for further customization\n\t * @see HttpSessionSecurityContextRepository#setDisableUrlRewriting(boolean)\n\t */\n\tpublic SessionManagementConfigurer<H> enableSessionUrlRewriting(boolean enableSessionUrlRewriting) {\n\t\tthis.enableSessionUrlRewriting = enableSessionUrlRewriting;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows specifying the {@link SessionCreationPolicy}\n\t * @param sessionCreationPolicy the {@link SessionCreationPolicy} to use. Cannot be\n\t * null.\n\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t * @throws IllegalArgumentException if {@link SessionCreationPolicy} is null.\n\t * @see SessionCreationPolicy\n\t */\n\tpublic SessionManagementConfigurer<H> sessionCreationPolicy(SessionCreationPolicy sessionCreationPolicy) {\n\t\tAssert.notNull(sessionCreationPolicy, \"sessionCreationPolicy cannot be null\");\n\t\tthis.sessionPolicy = sessionCreationPolicy;\n\t\tthis.propertiesThatRequireImplicitAuthentication.add(\"sessionCreationPolicy = \" + sessionCreationPolicy);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows explicitly specifying the {@link SessionAuthenticationStrategy}. The default\n\t * is to use {@link ChangeSessionIdAuthenticationStrategy}. If restricting the maximum\n\t * number of sessions is configured, then\n\t * {@link CompositeSessionAuthenticationStrategy} delegating to\n\t * {@link ConcurrentSessionControlAuthenticationStrategy}, the default OR supplied\n\t * {@code SessionAuthenticationStrategy} and\n\t * {@link RegisterSessionAuthenticationStrategy}.\n\t *\n\t * <p>\n\t * NOTE: Supplying a custom {@link SessionAuthenticationStrategy} will override the\n\t * default session fixation strategy.\n\t * @param sessionAuthenticationStrategy\n\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t */\n\tpublic SessionManagementConfigurer<H> sessionAuthenticationStrategy(\n\t\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy) {\n\t\tthis.providedSessionAuthenticationStrategy = sessionAuthenticationStrategy;\n\t\tthis.propertiesThatRequireImplicitAuthentication\n\t\t\t.add(\"sessionAuthenticationStrategy = \" + sessionAuthenticationStrategy);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an additional {@link SessionAuthenticationStrategy} to be used within the\n\t * {@link CompositeSessionAuthenticationStrategy}.\n\t * @param sessionAuthenticationStrategy\n\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t */\n\tpublic SessionManagementConfigurer<H> addSessionAuthenticationStrategy(\n\t\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy) {\n\t\tthis.sessionAuthenticationStrategies.add(sessionAuthenticationStrategy);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows changing the default {@link SessionFixationProtectionStrategy}.\n\t * @return the {@link SessionFixationConfigurer} for further customizations\n\t */\n\tpublic SessionFixationConfigurer sessionFixation() {\n\t\treturn new SessionFixationConfigurer();\n\t}\n\n\t/**\n\t * Allows configuring session fixation protection.\n\t * @param sessionFixationCustomizer the {@link Customizer} to provide more options for\n\t * the {@link SessionFixationConfigurer}\n\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t */\n\tpublic SessionManagementConfigurer<H> sessionFixation(\n\t\t\tCustomizer<SessionFixationConfigurer> sessionFixationCustomizer) {\n\t\tsessionFixationCustomizer.customize(new SessionFixationConfigurer());\n\t\treturn this;\n\t}\n\n\t/**\n\t * Controls the maximum number of sessions for a user. The default is to allow any\n\t * number of sessions.\n\t * @param maximumSessions the maximum number of sessions for a user\n\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t */\n\tpublic ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {\n\t\tthis.sessionLimit = SessionLimit.of(maximumSessions);\n\t\tthis.propertiesThatRequireImplicitAuthentication.add(\"maximumSessions = \" + maximumSessions);\n\t\treturn new ConcurrencyControlConfigurer();\n\t}\n\n\t/**\n\t * Controls the maximum number of sessions for a user. The default is to allow any\n\t * number of users.\n\t * @param sessionConcurrencyCustomizer the {@link Customizer} to provide more options\n\t * for the {@link ConcurrencyControlConfigurer}\n\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t */\n\tpublic SessionManagementConfigurer<H> sessionConcurrency(\n\t\t\tCustomizer<ConcurrencyControlConfigurer> sessionConcurrencyCustomizer) {\n\t\tsessionConcurrencyCustomizer.customize(new ConcurrencyControlConfigurer());\n\t\treturn this;\n\t}\n\n\t/**\n\t * Invokes {@link #postProcess(Object)} and sets the\n\t * {@link SessionAuthenticationStrategy} for session fixation.\n\t * @param sessionFixationAuthenticationStrategy\n\t */\n\tprivate void setSessionFixationAuthenticationStrategy(\n\t\t\tSessionAuthenticationStrategy sessionFixationAuthenticationStrategy) {\n\t\tthis.sessionFixationAuthenticationStrategy = postProcess(sessionFixationAuthenticationStrategy);\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\tSecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);\n\t\tboolean stateless = isStateless();\n\t\tif (securityContextRepository == null) {\n\t\t\tif (stateless) {\n\t\t\t\thttp.setSharedObject(SecurityContextRepository.class, new RequestAttributeSecurityContextRepository());\n\t\t\t\tthis.sessionManagementSecurityContextRepository = new NullSecurityContextRepository();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tHttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();\n\t\t\t\thttpSecurityRepository.setDisableUrlRewriting(!this.enableSessionUrlRewriting);\n\t\t\t\thttpSecurityRepository.setAllowSessionCreation(isAllowSessionCreation());\n\t\t\t\tAuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);\n\t\t\t\tif (trustResolver != null) {\n\t\t\t\t\thttpSecurityRepository.setTrustResolver(trustResolver);\n\t\t\t\t}\n\t\t\t\tthis.sessionManagementSecurityContextRepository = httpSecurityRepository;\n\t\t\t\tDelegatingSecurityContextRepository defaultRepository = new DelegatingSecurityContextRepository(\n\t\t\t\t\t\thttpSecurityRepository, new RequestAttributeSecurityContextRepository());\n\t\t\t\thttp.setSharedObject(SecurityContextRepository.class, defaultRepository);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthis.sessionManagementSecurityContextRepository = securityContextRepository;\n\t\t}\n\t\tRequestCache requestCache = http.getSharedObject(RequestCache.class);\n\t\tif (requestCache == null) {\n\t\t\tif (stateless) {\n\t\t\t\thttp.setSharedObject(RequestCache.class, new NullRequestCache());\n\t\t\t}\n\t\t}\n\t\thttp.setSharedObject(SessionAuthenticationStrategy.class, getSessionAuthenticationStrategy(http));\n\t\thttp.setSharedObject(InvalidSessionStrategy.class, getInvalidSessionStrategy());\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tSessionManagementFilter sessionManagementFilter = createSessionManagementFilter(http);\n\t\tif (sessionManagementFilter != null) {\n\t\t\thttp.addFilter(sessionManagementFilter);\n\t\t}\n\t\tif (isConcurrentSessionControlEnabled()) {\n\t\t\tConcurrentSessionFilter concurrentSessionFilter = createConcurrencyFilter(http);\n\n\t\t\tconcurrentSessionFilter = postProcess(concurrentSessionFilter);\n\t\t\thttp.addFilter(concurrentSessionFilter);\n\t\t}\n\t\tif (!this.enableSessionUrlRewriting) {\n\t\t\thttp.addFilter(new DisableEncodeUrlFilter());\n\t\t}\n\t\tif (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {\n\t\t\thttp.addFilter(new ForceEagerSessionCreationFilter());\n\t\t}\n\t}\n\n\tprivate boolean shouldRequireExplicitAuthenticationStrategy() {\n\t\tboolean defaultRequireExplicitAuthenticationStrategy = this.propertiesThatRequireImplicitAuthentication\n\t\t\t.isEmpty();\n\t\tif (this.requireExplicitAuthenticationStrategy == null) {\n\t\t\t// explicit is not set, use default\n\t\t\treturn defaultRequireExplicitAuthenticationStrategy;\n\t\t}\n\t\tif (this.requireExplicitAuthenticationStrategy && !defaultRequireExplicitAuthenticationStrategy) {\n\t\t\t// explicit disabled and implicit requires it\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Invalid configuration that explicitly sets requireExplicitAuthenticationStrategy to \"\n\t\t\t\t\t\t\t+ this.requireExplicitAuthenticationStrategy\n\t\t\t\t\t\t\t+ \" but implicitly requires it due to the following properties being set: \"\n\t\t\t\t\t\t\t+ this.propertiesThatRequireImplicitAuthentication);\n\t\t}\n\t\treturn this.requireExplicitAuthenticationStrategy;\n\t}\n\n\tprivate SessionManagementFilter createSessionManagementFilter(H http) {\n\t\tif (shouldRequireExplicitAuthenticationStrategy()) {\n\t\t\treturn null;\n\t\t}\n\t\tSecurityContextRepository securityContextRepository = this.sessionManagementSecurityContextRepository;\n\t\tSessionManagementFilter sessionManagementFilter = new SessionManagementFilter(securityContextRepository,\n\t\t\t\tgetSessionAuthenticationStrategy(http));\n\t\tif (this.sessionAuthenticationErrorUrl != null) {\n\t\t\tsessionManagementFilter.setAuthenticationFailureHandler(\n\t\t\t\t\tnew SimpleUrlAuthenticationFailureHandler(this.sessionAuthenticationErrorUrl));\n\t\t}\n\t\tInvalidSessionStrategy strategy = getInvalidSessionStrategy();\n\t\tif (strategy != null) {\n\t\t\tsessionManagementFilter.setInvalidSessionStrategy(strategy);\n\t\t}\n\t\tAuthenticationFailureHandler failureHandler = getSessionAuthenticationFailureHandler();\n\t\tif (failureHandler != null) {\n\t\t\tsessionManagementFilter.setAuthenticationFailureHandler(failureHandler);\n\t\t}\n\t\tAuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);\n\t\tif (trustResolver != null) {\n\t\t\tsessionManagementFilter.setTrustResolver(trustResolver);\n\t\t}\n\t\tsessionManagementFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\treturn postProcess(sessionManagementFilter);\n\t}\n\n\tprivate ConcurrentSessionFilter createConcurrencyFilter(H http) {\n\t\tSessionInformationExpiredStrategy expireStrategy = getExpiredSessionStrategy();\n\t\tSessionRegistry sessionRegistry = getSessionRegistry(http);\n\t\tConcurrentSessionFilter concurrentSessionFilter = (expireStrategy != null)\n\t\t\t\t? new ConcurrentSessionFilter(sessionRegistry, expireStrategy)\n\t\t\t\t: new ConcurrentSessionFilter(sessionRegistry);\n\t\tLogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);\n\t\tif (logoutConfigurer != null) {\n\t\t\tList<LogoutHandler> logoutHandlers = logoutConfigurer.getLogoutHandlers();\n\t\t\tif (!CollectionUtils.isEmpty(logoutHandlers)) {\n\t\t\t\tconcurrentSessionFilter.setLogoutHandlers(logoutHandlers);\n\t\t\t}\n\t\t}\n\t\tconcurrentSessionFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\treturn concurrentSessionFilter;\n\t}\n\n\t/**\n\t * Gets the {@link InvalidSessionStrategy} to use. If null and\n\t * {@link #invalidSessionUrl} is not null defaults to\n\t * {@link SimpleRedirectInvalidSessionStrategy}.\n\t * @return the {@link InvalidSessionStrategy} to use\n\t */\n\tInvalidSessionStrategy getInvalidSessionStrategy() {\n\t\tif (this.invalidSessionStrategy != null) {\n\t\t\treturn this.invalidSessionStrategy;\n\t\t}\n\t\tif (this.invalidSessionUrl == null) {\n\t\t\treturn null;\n\t\t}\n\t\tthis.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(this.invalidSessionUrl);\n\t\treturn this.invalidSessionStrategy;\n\t}\n\n\tSessionInformationExpiredStrategy getExpiredSessionStrategy() {\n\t\tif (this.expiredSessionStrategy != null) {\n\t\t\treturn this.expiredSessionStrategy;\n\t\t}\n\t\tif (this.expiredUrl == null) {\n\t\t\treturn null;\n\t\t}\n\t\tthis.expiredSessionStrategy = new SimpleRedirectSessionInformationExpiredStrategy(this.expiredUrl);\n\t\treturn this.expiredSessionStrategy;\n\t}\n\n\tAuthenticationFailureHandler getSessionAuthenticationFailureHandler() {\n\t\tif (this.sessionAuthenticationFailureHandler != null) {\n\t\t\treturn this.sessionAuthenticationFailureHandler;\n\t\t}\n\t\tif (this.sessionAuthenticationErrorUrl == null) {\n\t\t\treturn null;\n\t\t}\n\t\tthis.sessionAuthenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler(\n\t\t\t\tthis.sessionAuthenticationErrorUrl);\n\t\treturn this.sessionAuthenticationFailureHandler;\n\t}\n\n\t/**\n\t * Gets the {@link SessionCreationPolicy}. Can not be null.\n\t * @return the {@link SessionCreationPolicy}\n\t */\n\tSessionCreationPolicy getSessionCreationPolicy() {\n\t\tif (this.sessionPolicy != null) {\n\t\t\treturn this.sessionPolicy;\n\t\t}\n\t\tSessionCreationPolicy sessionPolicy = getBuilder().getSharedObject(SessionCreationPolicy.class);\n\t\treturn (sessionPolicy != null) ? sessionPolicy : SessionCreationPolicy.IF_REQUIRED;\n\t}\n\n\t/**\n\t * Returns true if the {@link SessionCreationPolicy} allows session creation, else\n\t * false\n\t * @return true if the {@link SessionCreationPolicy} allows session creation\n\t */\n\tprivate boolean isAllowSessionCreation() {\n\t\tSessionCreationPolicy sessionPolicy = getSessionCreationPolicy();\n\t\treturn SessionCreationPolicy.ALWAYS == sessionPolicy || SessionCreationPolicy.IF_REQUIRED == sessionPolicy;\n\t}\n\n\t/**\n\t * Returns true if the {@link SessionCreationPolicy} is stateless\n\t * @return\n\t */\n\tprivate boolean isStateless() {\n\t\tSessionCreationPolicy sessionPolicy = getSessionCreationPolicy();\n\t\treturn SessionCreationPolicy.STATELESS == sessionPolicy;\n\t}\n\n\t/**\n\t * Gets the customized {@link SessionAuthenticationStrategy} if\n\t * {@link #sessionAuthenticationStrategy(SessionAuthenticationStrategy)} was\n\t * specified. Otherwise creates a default {@link SessionAuthenticationStrategy}.\n\t * @return the {@link SessionAuthenticationStrategy} to use\n\t */\n\tprivate SessionAuthenticationStrategy getSessionAuthenticationStrategy(H http) {\n\t\tif (this.sessionAuthenticationStrategy != null) {\n\t\t\treturn this.sessionAuthenticationStrategy;\n\t\t}\n\t\tList<SessionAuthenticationStrategy> delegateStrategies = this.sessionAuthenticationStrategies;\n\t\tSessionAuthenticationStrategy defaultSessionAuthenticationStrategy;\n\t\tif (this.providedSessionAuthenticationStrategy == null) {\n\t\t\t// If the user did not provide a SessionAuthenticationStrategy\n\t\t\t// then default to sessionFixationAuthenticationStrategy\n\t\t\tdefaultSessionAuthenticationStrategy = postProcess(this.sessionFixationAuthenticationStrategy);\n\t\t}\n\t\telse {\n\t\t\tdefaultSessionAuthenticationStrategy = this.providedSessionAuthenticationStrategy;\n\t\t}\n\t\tif (isConcurrentSessionControlEnabled()) {\n\t\t\tSessionRegistry sessionRegistry = getSessionRegistry(http);\n\t\t\tConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(\n\t\t\t\t\tsessionRegistry);\n\t\t\tconcurrentSessionControlStrategy.setMaximumSessions(this.sessionLimit);\n\t\t\tconcurrentSessionControlStrategy.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);\n\t\t\tconcurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy);\n\t\t\tRegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(\n\t\t\t\t\tsessionRegistry);\n\t\t\tregisterSessionStrategy = postProcess(registerSessionStrategy);\n\n\t\t\tdelegateStrategies.addAll(Arrays.asList(concurrentSessionControlStrategy,\n\t\t\t\t\tdefaultSessionAuthenticationStrategy, registerSessionStrategy));\n\t\t}\n\t\telse {\n\t\t\tdelegateStrategies.add(defaultSessionAuthenticationStrategy);\n\t\t}\n\t\tthis.sessionAuthenticationStrategy = postProcess(\n\t\t\t\tnew CompositeSessionAuthenticationStrategy(delegateStrategies));\n\t\treturn this.sessionAuthenticationStrategy;\n\t}\n\n\tprivate SessionRegistry getSessionRegistry(H http) {\n\t\tif (this.sessionRegistry == null) {\n\t\t\tthis.sessionRegistry = getBeanOrNull(SessionRegistry.class);\n\t\t}\n\t\tif (this.sessionRegistry == null) {\n\t\t\tSessionRegistryImpl sessionRegistry = new SessionRegistryImpl();\n\t\t\tregisterDelegateApplicationListener(http, sessionRegistry);\n\t\t\tthis.sessionRegistry = sessionRegistry;\n\t\t}\n\t\treturn this.sessionRegistry;\n\t}\n\n\tprivate void registerDelegateApplicationListener(H http, ApplicationListener<?> delegate) {\n\t\tDelegatingApplicationListener delegating = getBeanOrNull(DelegatingApplicationListener.class);\n\t\tif (delegating == null) {\n\t\t\treturn;\n\t\t}\n\t\tSmartApplicationListener smartListener = new GenericApplicationListenerAdapter(delegate);\n\t\tdelegating.addListener(smartListener);\n\t}\n\n\t/**\n\t * Returns true if the number of concurrent sessions per user should be restricted.\n\t * @return\n\t */\n\tprivate boolean isConcurrentSessionControlEnabled() {\n\t\treturn this.sessionLimit != null;\n\t}\n\n\t/**\n\t * Creates the default {@link SessionAuthenticationStrategy} for session fixation\n\t * @return the default {@link SessionAuthenticationStrategy} for session fixation\n\t */\n\tprivate static SessionAuthenticationStrategy createDefaultSessionFixationProtectionStrategy() {\n\t\treturn new ChangeSessionIdAuthenticationStrategy();\n\t}\n\n\tprivate <T> T getBeanOrNull(Class<T> type) {\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn context.getBeanProvider(type).getIfUnique();\n\t}\n\n\t/**\n\t * Allows configuring SessionFixation protection\n\t *\n\t * @author Rob Winch\n\t */\n\tpublic final class SessionFixationConfigurer {\n\n\t\t/**\n\t\t * Specifies that a new session should be created, but the session attributes from\n\t\t * the original {@link HttpSession} should not be retained.\n\t\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t\t */\n\t\tpublic SessionManagementConfigurer<H> newSession() {\n\t\t\tSessionFixationProtectionStrategy sessionFixationProtectionStrategy = new SessionFixationProtectionStrategy();\n\t\t\tsessionFixationProtectionStrategy.setMigrateSessionAttributes(false);\n\t\t\tsetSessionFixationAuthenticationStrategy(sessionFixationProtectionStrategy);\n\t\t\treturn SessionManagementConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies that a new session should be created and the session attributes from\n\t\t * the original {@link HttpSession} should be retained.\n\t\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t\t */\n\t\tpublic SessionManagementConfigurer<H> migrateSession() {\n\t\t\tsetSessionFixationAuthenticationStrategy(new SessionFixationProtectionStrategy());\n\t\t\treturn SessionManagementConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies that the Servlet container-provided session fixation protection\n\t\t * should be used. When a session authenticates, the Servlet method\n\t\t * {@code HttpServletRequest#changeSessionId()} is called to change the session ID\n\t\t * and retain all session attributes.\n\t\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t\t */\n\t\tpublic SessionManagementConfigurer<H> changeSessionId() {\n\t\t\tsetSessionFixationAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());\n\t\t\treturn SessionManagementConfigurer.this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies that no session fixation protection should be enabled. This may be\n\t\t * useful when utilizing other mechanisms for protecting against session fixation.\n\t\t * For example, if application container session fixation protection is already in\n\t\t * use. Otherwise, this option is not recommended.\n\t\t * @return the {@link SessionManagementConfigurer} for further customizations\n\t\t */\n\t\tpublic SessionManagementConfigurer<H> none() {\n\t\t\tsetSessionFixationAuthenticationStrategy(new NullAuthenticatedSessionStrategy());\n\t\t\treturn SessionManagementConfigurer.this;\n\t\t}\n\n\t}\n\n\t/**\n\t * Allows configuring controlling of multiple sessions.\n\t *\n\t * @author Rob Winch\n\t */\n\tpublic final class ConcurrencyControlConfigurer {\n\n\t\tprivate ConcurrencyControlConfigurer() {\n\t\t}\n\n\t\t/**\n\t\t * Controls the maximum number of sessions for a user. The default is to allow any\n\t\t * number of users.\n\t\t * @param maximumSessions the maximum number of sessions for a user\n\t\t * @return the {@link ConcurrencyControlConfigurer} for further customizations\n\t\t */\n\t\tpublic ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {\n\t\t\tSessionManagementConfigurer.this.sessionLimit = SessionLimit.of(maximumSessions);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Determines the behaviour when a session limit is detected.\n\t\t * @param sessionLimit the {@link SessionLimit} to check the maximum number of\n\t\t * sessions for a user\n\t\t * @return the {@link ConcurrencyControlConfigurer} for further customizations\n\t\t * @since 6.5\n\t\t */\n\t\tpublic ConcurrencyControlConfigurer maximumSessions(SessionLimit sessionLimit) {\n\t\t\tSessionManagementConfigurer.this.sessionLimit = sessionLimit;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The URL to redirect to if a user tries to access a resource and their session\n\t\t * has been expired due to too many sessions for the current user. The default is\n\t\t * to write a simple error message to the response.\n\t\t * @param expiredUrl the URL to redirect to\n\t\t * @return the {@link ConcurrencyControlConfigurer} for further customizations\n\t\t */\n\t\tpublic ConcurrencyControlConfigurer expiredUrl(String expiredUrl) {\n\t\t\tSessionManagementConfigurer.this.expiredUrl = expiredUrl;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Determines the behaviour when an expired session is detected.\n\t\t * @param expiredSessionStrategy the {@link SessionInformationExpiredStrategy} to\n\t\t * use when an expired session is detected.\n\t\t * @return the {@link ConcurrencyControlConfigurer} for further customizations\n\t\t */\n\t\tpublic ConcurrencyControlConfigurer expiredSessionStrategy(\n\t\t\t\tSessionInformationExpiredStrategy expiredSessionStrategy) {\n\t\t\tSessionManagementConfigurer.this.expiredSessionStrategy = expiredSessionStrategy;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * If true, prevents a user from authenticating when the\n\t\t * {@link #maximumSessions(int)} has been reached. Otherwise (default), the user\n\t\t * who authenticates is allowed access and an existing user's session is expired.\n\t\t * The user's who's session is forcibly expired is sent to\n\t\t * {@link #expiredUrl(String)}. The advantage of this approach is if a user\n\t\t * accidentally does not log out, there is no need for an administrator to\n\t\t * intervene or wait till their session expires.\n\t\t * @param maxSessionsPreventsLogin true to have an error at time of\n\t\t * authentication, else false (default)\n\t\t * @return the {@link ConcurrencyControlConfigurer} for further customizations\n\t\t */\n\t\tpublic ConcurrencyControlConfigurer maxSessionsPreventsLogin(boolean maxSessionsPreventsLogin) {\n\t\t\tSessionManagementConfigurer.this.maxSessionsPreventsLogin = maxSessionsPreventsLogin;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Controls the {@link SessionRegistry} implementation used. The default is\n\t\t * {@link SessionRegistryImpl} which is an in memory implementation.\n\t\t * @param sessionRegistry the {@link SessionRegistry} to use\n\t\t * @return the {@link ConcurrencyControlConfigurer} for further customizations\n\t\t */\n\t\tpublic ConcurrencyControlConfigurer sessionRegistry(SessionRegistry sessionRegistry) {\n\t\t\tSessionManagementConfigurer.this.sessionRegistry = sessionRegistry;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;\nimport org.springframework.security.web.webauthn.authentication.PublicKeyCredentialRequestOptionsFilter;\nimport org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;\nimport org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationProvider;\nimport org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository;\nimport org.springframework.security.web.webauthn.management.MapUserCredentialRepository;\nimport org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.security.web.webauthn.management.Webauthn4JRelyingPartyOperations;\nimport org.springframework.security.web.webauthn.registration.DefaultWebAuthnRegistrationPageGeneratingFilter;\nimport org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;\nimport org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository;\nimport org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;\nimport org.springframework.util.Assert;\n\n/**\n * Configures WebAuthn for Spring Security applications\n *\n * @param <H> the type of builder\n * @author Rob Winch\n * @since 6.4\n */\npublic class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<WebAuthnConfigurer<H>, H> {\n\n\tprivate String rpId;\n\n\tprivate String rpName;\n\n\tprivate Set<String> allowedOrigins = new HashSet<>();\n\n\tprivate boolean disableDefaultRegistrationPage = false;\n\n\tprivate PublicKeyCredentialCreationOptionsRepository creationOptionsRepository;\n\n\tprivate HttpMessageConverter<Object> converter;\n\n\t/**\n\t * The Relying Party id.\n\t * @param rpId the relying party id\n\t * @return the {@link WebAuthnConfigurer} for further customization\n\t */\n\tpublic WebAuthnConfigurer<H> rpId(String rpId) {\n\t\tAssert.hasText(rpId, \"rpId be null or empty\");\n\t\tthis.rpId = rpId;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the relying party name\n\t * @param rpName the relying party name\n\t * @return the {@link WebAuthnConfigurer} for further customization\n\t */\n\tpublic WebAuthnConfigurer<H> rpName(String rpName) {\n\t\tAssert.hasText(rpName, \"rpName can't be null or empty\");\n\t\tthis.rpName = rpName;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Convenience method for {@link #allowedOrigins(Set)}\n\t * @param allowedOrigins the allowed origins\n\t * @return the {@link WebAuthnConfigurer} for further customization\n\t * @see #allowedOrigins(Set)\n\t */\n\tpublic WebAuthnConfigurer<H> allowedOrigins(String... allowedOrigins) {\n\t\treturn allowedOrigins(Set.of(allowedOrigins));\n\t}\n\n\t/**\n\t * Sets the allowed origins.\n\t * @param allowedOrigins the allowed origins\n\t * @return the {@link WebAuthnConfigurer} for further customization\n\t * @see #allowedOrigins(String...)\n\t */\n\tpublic WebAuthnConfigurer<H> allowedOrigins(Set<String> allowedOrigins) {\n\t\tAssert.notNull(allowedOrigins, \"allowedOrigins can't be null\");\n\t\tthis.allowedOrigins = allowedOrigins;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures whether the default webauthn registration should be disabled. Setting it\n\t * to {@code true} will prevent the configurer from registering the\n\t * {@link DefaultWebAuthnRegistrationPageGeneratingFilter}.\n\t * @param disable disable the default registration page if true, enable it otherwise\n\t * @return the {@link WebAuthnConfigurer} for further customization\n\t */\n\tpublic WebAuthnConfigurer<H> disableDefaultRegistrationPage(boolean disable) {\n\t\tthis.disableDefaultRegistrationPage = disable;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets {@link HttpMessageConverter} used for WebAuthn to read/write to the HTTP\n\t * request/response.\n\t * @param converter the {@link HttpMessageConverter}\n\t * @return the {@link WebAuthnConfigurer} for further customization\n\t */\n\tpublic WebAuthnConfigurer<H> messageConverter(HttpMessageConverter<Object> converter) {\n\t\tAssert.notNull(converter, \"converter can't be null\");\n\t\tthis.converter = converter;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets PublicKeyCredentialCreationOptionsRepository\n\t * @param creationOptionsRepository the creationOptionsRepository\n\t * @return the {@link WebAuthnConfigurer} for further customization\n\t */\n\tpublic WebAuthnConfigurer<H> creationOptionsRepository(\n\t\t\tPublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {\n\t\tAssert.notNull(creationOptionsRepository, \"creationOptionsRepository can't be null\");\n\t\tthis.creationOptionsRepository = creationOptionsRepository;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\tExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptions != null) {\n\t\t\tAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint(\"/login\");\n\t\t\texceptions.defaultDeniedHandlerForMissingAuthority((ep) -> ep.defaultEntryPoint(entryPoint),\n\t\t\t\t\tFactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tUserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class)\n\t\t\t.orElseThrow(() -> new IllegalStateException(\"Missing UserDetailsService Bean\"));\n\t\tPublicKeyCredentialUserEntityRepository userEntities = getSharedOrBean(http,\n\t\t\t\tPublicKeyCredentialUserEntityRepository.class)\n\t\t\t.orElse(userEntityRepository());\n\t\tUserCredentialRepository userCredentials = getSharedOrBean(http, UserCredentialRepository.class)\n\t\t\t.orElse(userCredentialRepository());\n\t\tWebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);\n\t\tPublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository();\n\t\tWebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();\n\t\twebAuthnAuthnFilter.setAuthenticationManager(\n\t\t\t\tnew ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));\n\t\twebAuthnAuthnFilter = postProcess(webAuthnAuthnFilter);\n\t\tWebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,\n\t\t\t\trpOperations);\n\t\tPublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(\n\t\t\t\trpOperations);\n\t\tif (creationOptionsRepository != null) {\n\t\t\twebAuthnRegistrationFilter.setCreationOptionsRepository(creationOptionsRepository);\n\t\t\tcreationOptionsFilter.setCreationOptionsRepository(creationOptionsRepository);\n\t\t}\n\t\tif (this.converter != null) {\n\t\t\twebAuthnRegistrationFilter.setConverter(this.converter);\n\t\t\tcreationOptionsFilter.setConverter(this.converter);\n\t\t}\n\t\thttp.addFilterBefore(webAuthnAuthnFilter, BasicAuthenticationFilter.class);\n\t\thttp.addFilterAfter(webAuthnRegistrationFilter, AuthorizationFilter.class);\n\t\thttp.addFilterBefore(creationOptionsFilter, AuthorizationFilter.class);\n\t\thttp.addFilterBefore(new PublicKeyCredentialRequestOptionsFilter(rpOperations), AuthorizationFilter.class);\n\n\t\tDefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http\n\t\t\t.getSharedObject(DefaultLoginPageGeneratingFilter.class);\n\t\tboolean isLoginPageEnabled = loginPageGeneratingFilter != null && loginPageGeneratingFilter.isEnabled();\n\t\tif (isLoginPageEnabled) {\n\t\t\tloginPageGeneratingFilter.setPasskeysEnabled(true);\n\t\t\tloginPageGeneratingFilter.setResolveHeaders((request) -> {\n\t\t\t\tCsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\t\t\treturn Map.of(csrfToken.getHeaderName(), csrfToken.getToken());\n\t\t\t});\n\t\t}\n\n\t\tif (!this.disableDefaultRegistrationPage) {\n\t\t\thttp.addFilterAfter(new DefaultWebAuthnRegistrationPageGeneratingFilter(userEntities, userCredentials),\n\t\t\t\t\tAuthorizationFilter.class);\n\t\t\tif (!isLoginPageEnabled) {\n\t\t\t\thttp.addFilter(DefaultResourcesFilter.css());\n\t\t\t}\n\t\t}\n\n\t\tif (isLoginPageEnabled || !this.disableDefaultRegistrationPage) {\n\t\t\thttp.addFilter(DefaultResourcesFilter.webauthn());\n\t\t}\n\t}\n\n\tprivate PublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {\n\t\tif (this.creationOptionsRepository != null) {\n\t\t\treturn this.creationOptionsRepository;\n\t\t}\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\treturn context.getBeanProvider(PublicKeyCredentialCreationOptionsRepository.class).getIfUnique();\n\t}\n\n\tprivate <C> Optional<C> getSharedOrBean(H http, Class<C> type) {\n\t\tC shared = http.getSharedObject(type);\n\t\treturn Optional.ofNullable(shared).or(() -> getBeanOrNull(type));\n\t}\n\n\tprivate <T> Optional<T> getBeanOrNull(Class<T> type) {\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\tif (context == null) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\ttry {\n\t\t\treturn Optional.of(context.getBean(type));\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\tprivate MapUserCredentialRepository userCredentialRepository() {\n\t\treturn new MapUserCredentialRepository();\n\t}\n\n\tprivate PublicKeyCredentialUserEntityRepository userEntityRepository() {\n\t\treturn new MapPublicKeyCredentialUserEntityRepository();\n\t}\n\n\tprivate WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations(\n\t\t\tPublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository userCredentials) {\n\t\tOptional<WebAuthnRelyingPartyOperations> webauthnOperationsBean = getBeanOrNull(\n\t\t\t\tWebAuthnRelyingPartyOperations.class);\n\t\tString rpName = (this.rpName != null) ? this.rpName : this.rpId;\n\t\treturn webauthnOperationsBean\n\t\t\t.orElseGet(() -> new Webauthn4JRelyingPartyOperations(userEntities, userCredentials,\n\t\t\t\t\tPublicKeyCredentialRpEntity.builder().id(this.rpId).name(rpName).build(), this.allowedOrigins));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/X509Configurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;\nimport org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;\nimport org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;\nimport org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\n\n/**\n * Adds X509 based pre authentication to an application. Since validating the certificate\n * happens when the client connects, the requesting and validation of the client\n * certificate should be performed by the container. Spring Security will then use the\n * certificate to look up the {@link Authentication} for the user.\n *\n * <h2>Security Filters</h2>\n * <p>\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link X509AuthenticationFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n * <p>\n * The following shared objects are created\n *\n * <ul>\n * <li>{@link AuthenticationEntryPoint} is populated with an\n * {@link Http403ForbiddenEntryPoint}</li>\n * <li>A {@link PreAuthenticatedAuthenticationProvider} is populated into\n * {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}\n * </li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n * <p>\n * The following shared objects are used:\n *\n * <ul>\n * <li>A {@link UserDetailsService} shared object is used if no\n * {@link AuthenticationUserDetailsService} is specified</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Ngoc Nhan\n * @since 3.2\n */\npublic final class X509Configurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<X509Configurer<H>, H> {\n\n\tprivate X509AuthenticationFilter x509AuthenticationFilter;\n\n\tprivate X509PrincipalExtractor x509PrincipalExtractor;\n\n\tprivate AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService;\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> authenticationDetailsSource;\n\n\t/**\n\t * Creates a new instance\n\t *\n\t * @see HttpSecurity#x509(Customizer)\n\t */\n\tpublic X509Configurer() {\n\t}\n\n\t/**\n\t * Allows specifying the entire {@link X509AuthenticationFilter}. If this is\n\t * specified, the properties on {@link X509Configurer} will not be populated on the\n\t * {@link X509AuthenticationFilter}.\n\t * @param x509AuthenticationFilter the {@link X509AuthenticationFilter} to use\n\t * @return the {@link X509Configurer} for further customizations\n\t */\n\tpublic X509Configurer<H> x509AuthenticationFilter(X509AuthenticationFilter x509AuthenticationFilter) {\n\t\tthis.x509AuthenticationFilter = x509AuthenticationFilter;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the {@link X509PrincipalExtractor}\n\t * @param x509PrincipalExtractor the {@link X509PrincipalExtractor} to use\n\t * @return the {@link X509Configurer} to use\n\t */\n\tpublic X509Configurer<H> x509PrincipalExtractor(X509PrincipalExtractor x509PrincipalExtractor) {\n\t\tthis.x509PrincipalExtractor = x509PrincipalExtractor;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the {@link AuthenticationDetailsSource}\n\t * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} to use\n\t * @return the {@link X509Configurer} to use\n\t */\n\tpublic X509Configurer<H> authenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> authenticationDetailsSource) {\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Shortcut for invoking\n\t * {@link #authenticationUserDetailsService(AuthenticationUserDetailsService)} with a\n\t * {@link UserDetailsByNameServiceWrapper}.\n\t * @param userDetailsService the {@link UserDetailsService} to use\n\t * @return the {@link X509Configurer} for further customizations\n\t */\n\tpublic X509Configurer<H> userDetailsService(UserDetailsService userDetailsService) {\n\t\tUserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService = new UserDetailsByNameServiceWrapper<>();\n\t\tauthenticationUserDetailsService.setUserDetailsService(userDetailsService);\n\t\treturn authenticationUserDetailsService(authenticationUserDetailsService);\n\t}\n\n\t/**\n\t * Specifies the {@link AuthenticationUserDetailsService} to use. If not specified,\n\t * then the {@link UserDetailsService} bean will be used by default.\n\t * @param authenticationUserDetailsService the\n\t * {@link AuthenticationUserDetailsService} to use\n\t * @return the {@link X509Configurer} for further customizations\n\t */\n\tpublic X509Configurer<H> authenticationUserDetailsService(\n\t\t\tAuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService) {\n\t\tthis.authenticationUserDetailsService = authenticationUserDetailsService;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the regex to extract the principal from the certificate. If not\n\t * specified, the default expression from {@link SubjectDnX509PrincipalExtractor} is\n\t * used.\n\t * @param subjectPrincipalRegex the regex to extract the user principal from the\n\t * certificate (i.e. \"CN=(.*?)(?:,|$)\").\n\t * @return the {@link X509Configurer} for further customizations\n\t * @deprecated Please use {{@link #x509PrincipalExtractor(X509PrincipalExtractor)}\n\t * instead\n\t */\n\t@Deprecated\n\tpublic X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {\n\t\tSubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();\n\t\tprincipalExtractor.setSubjectDnRegex(subjectPrincipalRegex);\n\t\tthis.x509PrincipalExtractor = principalExtractor;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\tPreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();\n\t\tauthenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http));\n\t\tauthenticationProvider.setGrantedAuthoritySupplier(\n\t\t\t\t() -> AuthorityUtils.createAuthorityList(FactorGrantedAuthority.X509_AUTHORITY));\n\t\thttp.authenticationProvider(authenticationProvider)\n\t\t\t.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());\n\t\tExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptions != null) {\n\t\t\tAuthenticationEntryPoint forbidden = new Http403ForbiddenEntryPoint();\n\t\t\texceptions.defaultDeniedHandlerForMissingAuthority((ep) -> ep.defaultEntryPoint(forbidden),\n\t\t\t\t\tFactorGrantedAuthority.X509_AUTHORITY);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tX509AuthenticationFilter filter = getFilter(http.getSharedObject(AuthenticationManager.class), http);\n\t\thttp.addFilter(filter);\n\t}\n\n\tprivate X509AuthenticationFilter getFilter(AuthenticationManager authenticationManager, H http) {\n\t\tif (this.x509AuthenticationFilter == null) {\n\t\t\tthis.x509AuthenticationFilter = new X509AuthenticationFilter();\n\t\t\tthis.x509AuthenticationFilter.setAuthenticationManager(authenticationManager);\n\t\t\tif (this.x509PrincipalExtractor != null) {\n\t\t\t\tthis.x509AuthenticationFilter.setPrincipalExtractor(this.x509PrincipalExtractor);\n\t\t\t}\n\t\t\tif (this.authenticationDetailsSource != null) {\n\t\t\t\tthis.x509AuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);\n\t\t\t}\n\t\t\tthis.x509AuthenticationFilter.setSecurityContextRepository(new RequestAttributeSecurityContextRepository());\n\t\t\tthis.x509AuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\t\tthis.x509AuthenticationFilter = postProcess(this.x509AuthenticationFilter);\n\t\t}\n\n\t\treturn this.x509AuthenticationFilter;\n\t}\n\n\tprivate AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getAuthenticationUserDetailsService(\n\t\t\tH http) {\n\t\tif (this.authenticationUserDetailsService == null) {\n\t\t\tuserDetailsService(getSharedOrBean(http, UserDetailsService.class));\n\t\t}\n\t\treturn this.authenticationUserDetailsService;\n\t}\n\n\tprivate <C> C getSharedOrBean(H http, Class<C> type) {\n\t\tC shared = http.getSharedObject(type);\n\t\tif (shared != null) {\n\t\t\treturn shared;\n\t\t}\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn context.getBeanProvider(type).getIfUnique();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AbstractHttpConfigurer} for OAuth 2.0 Client support.\n *\n * <p>\n * The following configuration options are available:\n *\n * <ul>\n * <li>{@link #authorizationCodeGrant(Customizer)} - support for the OAuth 2.0\n * Authorization Code Grant</li>\n * </ul>\n *\n * <p>\n * Defaults are provided for all configuration options with the only required\n * configuration being\n * {@link #clientRegistrationRepository(ClientRegistrationRepository)}. Alternatively, a\n * {@link ClientRegistrationRepository} {@code @Bean} may be registered instead.\n *\n * <h2>Security Filters</h2>\n *\n * The following {@code Filter}'s are populated for\n * {@link #authorizationCodeGrant(Customizer)}:\n *\n * <ul>\n * <li>{@link OAuth2AuthorizationRequestRedirectFilter}</li>\n * <li>{@link OAuth2AuthorizationCodeGrantFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * The following shared objects are populated:\n *\n * <ul>\n * <li>{@link ClientRegistrationRepository} (required)</li>\n * <li>{@link OAuth2AuthorizedClientRepository} (optional)</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link ClientRegistrationRepository}</li>\n * <li>{@link OAuth2AuthorizedClientRepository}</li>\n * </ul>\n *\n * @author Joe Grandja\n * @author Parikshit Dutta\n * @author Ngoc Nhan\n * @since 5.1\n * @see OAuth2AuthorizationRequestRedirectFilter\n * @see OAuth2AuthorizationCodeGrantFilter\n * @see ClientRegistrationRepository\n * @see OAuth2AuthorizedClientRepository\n * @see AbstractHttpConfigurer\n */\npublic final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>\n\t\textends AbstractHttpConfigurer<OAuth2ClientConfigurer<B>, B> {\n\n\tprivate AuthorizationCodeGrantConfigurer authorizationCodeGrantConfigurer = new AuthorizationCodeGrantConfigurer();\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t/**\n\t * Sets the repository of client registrations.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @return the {@link OAuth2ClientConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientConfigurer<B> clientRegistrationRepository(\n\t\t\tClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the repository for authorized client(s).\n\t * @param authorizedClientRepository the authorized client repository\n\t * @return the {@link OAuth2ClientConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientConfigurer<B> authorizedClientRepository(\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.getBuilder().setSharedObject(OAuth2AuthorizedClientRepository.class, authorizedClientRepository);\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the service for authorized client(s).\n\t * @param authorizedClientService the authorized client service\n\t * @return the {@link OAuth2ClientConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientConfigurer<B> authorizedClientService(OAuth2AuthorizedClientService authorizedClientService) {\n\t\tAssert.notNull(authorizedClientService, \"authorizedClientService cannot be null\");\n\t\tthis.authorizedClientRepository(\n\t\t\t\tnew AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Authorization Code Grant.\n\t * @param authorizationCodeGrantCustomizer the {@link Customizer} to provide more\n\t * options for the {@link AuthorizationCodeGrantConfigurer}\n\t * @return the {@link OAuth2ClientConfigurer} for further customizations\n\t */\n\tpublic OAuth2ClientConfigurer<B> authorizationCodeGrant(\n\t\t\tCustomizer<AuthorizationCodeGrantConfigurer> authorizationCodeGrantCustomizer) {\n\t\tauthorizationCodeGrantCustomizer.customize(this.authorizationCodeGrantConfigurer);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(B builder) {\n\t\tthis.authorizationCodeGrantConfigurer.init(builder);\n\t}\n\n\t@Override\n\tpublic void configure(B builder) {\n\t\tthis.authorizationCodeGrantConfigurer.configure(builder);\n\t}\n\n\t/**\n\t * Configuration options for the OAuth 2.0 Authorization Code Grant.\n\t */\n\tpublic final class AuthorizationCodeGrantConfigurer {\n\n\t\tprivate OAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\t\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\t\tprivate RedirectStrategy authorizationRedirectStrategy;\n\n\t\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\t\tprivate AuthorizationCodeGrantConfigurer() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the resolver used for resolving {@link OAuth2AuthorizationRequest}'s.\n\t\t * @param authorizationRequestResolver the resolver used for resolving\n\t\t * {@link OAuth2AuthorizationRequest}'s\n\t\t * @return the {@link AuthorizationCodeGrantConfigurer} for further configuration\n\t\t */\n\t\tpublic AuthorizationCodeGrantConfigurer authorizationRequestResolver(\n\t\t\t\tOAuth2AuthorizationRequestResolver authorizationRequestResolver) {\n\t\t\tAssert.notNull(authorizationRequestResolver, \"authorizationRequestResolver cannot be null\");\n\t\t\tthis.authorizationRequestResolver = authorizationRequestResolver;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.\n\t\t * @param authorizationRequestRepository the repository used for storing\n\t\t * {@link OAuth2AuthorizationRequest}'s\n\t\t * @return the {@link AuthorizationCodeGrantConfigurer} for further configuration\n\t\t */\n\t\tpublic AuthorizationCodeGrantConfigurer authorizationRequestRepository(\n\t\t\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\t\tAssert.notNull(authorizationRequestRepository, \"authorizationRequestRepository cannot be null\");\n\t\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the redirect strategy for Authorization Endpoint redirect URI.\n\t\t * @param authorizationRedirectStrategy the redirect strategy\n\t\t * @return the {@link AuthorizationCodeGrantConfigurer} for further configuration\n\t\t */\n\t\tpublic AuthorizationCodeGrantConfigurer authorizationRedirectStrategy(\n\t\t\t\tRedirectStrategy authorizationRedirectStrategy) {\n\t\t\tthis.authorizationRedirectStrategy = authorizationRedirectStrategy;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the client used for requesting the access token credential from the Token\n\t\t * Endpoint.\n\t\t * @param accessTokenResponseClient the client used for requesting the access\n\t\t * token credential from the Token Endpoint\n\t\t * @return the {@link AuthorizationCodeGrantConfigurer} for further configuration\n\t\t */\n\t\tpublic AuthorizationCodeGrantConfigurer accessTokenResponseClient(\n\t\t\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient) {\n\t\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate void init(B builder) {\n\t\t\tOAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(\n\t\t\t\t\tgetAccessTokenResponseClient());\n\t\t\tbuilder.authenticationProvider(postProcess(authorizationCodeAuthenticationProvider));\n\t\t}\n\n\t\tprivate void configure(B builder) {\n\t\t\tOAuth2AuthorizationRequestRedirectFilter authorizationRequestRedirectFilter = createAuthorizationRequestRedirectFilter(\n\t\t\t\t\tbuilder);\n\t\t\tbuilder.addFilter(postProcess(authorizationRequestRedirectFilter));\n\t\t\tOAuth2AuthorizationCodeGrantFilter authorizationCodeGrantFilter = createAuthorizationCodeGrantFilter(\n\t\t\t\t\tbuilder);\n\t\t\tbuilder.addFilter(postProcess(authorizationCodeGrantFilter));\n\t\t}\n\n\t\tprivate OAuth2AuthorizationRequestRedirectFilter createAuthorizationRequestRedirectFilter(B builder) {\n\t\t\tOAuth2AuthorizationRequestResolver resolver = getAuthorizationRequestResolver();\n\t\t\tOAuth2AuthorizationRequestRedirectFilter authorizationRequestRedirectFilter = new OAuth2AuthorizationRequestRedirectFilter(\n\t\t\t\t\tresolver);\n\t\t\tif (this.authorizationRequestRepository != null) {\n\t\t\t\tauthorizationRequestRedirectFilter\n\t\t\t\t\t.setAuthorizationRequestRepository(this.authorizationRequestRepository);\n\t\t\t}\n\t\t\tif (this.authorizationRedirectStrategy != null) {\n\t\t\t\tauthorizationRequestRedirectFilter.setAuthorizationRedirectStrategy(this.authorizationRedirectStrategy);\n\t\t\t}\n\t\t\tRequestCache requestCache = builder.getSharedObject(RequestCache.class);\n\t\t\tif (requestCache != null) {\n\t\t\t\tauthorizationRequestRedirectFilter.setRequestCache(requestCache);\n\t\t\t}\n\t\t\treturn authorizationRequestRedirectFilter;\n\t\t}\n\n\t\tprivate OAuth2AuthorizationRequestResolver getAuthorizationRequestResolver() {\n\t\t\tif (this.authorizationRequestResolver != null) {\n\t\t\t\treturn this.authorizationRequestResolver;\n\t\t\t}\n\t\t\tClientRegistrationRepository clientRegistrationRepository = getClientRegistrationRepository(getBuilder());\n\t\t\tResolvableType resolvableType = ResolvableType.forClass(OAuth2AuthorizationRequestResolver.class);\n\t\t\tOAuth2AuthorizationRequestResolver bean = getBeanOrNull(resolvableType);\n\t\t\treturn (bean != null) ? bean : new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,\n\t\t\t\t\tOAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);\n\t\t}\n\n\t\tprivate OAuth2AuthorizationCodeGrantFilter createAuthorizationCodeGrantFilter(B builder) {\n\t\t\tAuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);\n\t\t\tOAuth2AuthorizationCodeGrantFilter authorizationCodeGrantFilter = new OAuth2AuthorizationCodeGrantFilter(\n\t\t\t\t\tgetClientRegistrationRepository(builder), getAuthorizedClientRepository(builder),\n\t\t\t\t\tauthenticationManager);\n\t\t\tif (this.authorizationRequestRepository != null) {\n\t\t\t\tauthorizationCodeGrantFilter.setAuthorizationRequestRepository(this.authorizationRequestRepository);\n\t\t\t}\n\t\t\tauthorizationCodeGrantFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\t\tRequestCache requestCache = builder.getSharedObject(RequestCache.class);\n\t\t\tif (requestCache != null) {\n\t\t\t\tauthorizationCodeGrantFilter.setRequestCache(requestCache);\n\t\t\t}\n\t\t\treturn authorizationCodeGrantFilter;\n\t\t}\n\n\t\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> getAccessTokenResponseClient() {\n\t\t\tif (this.accessTokenResponseClient != null) {\n\t\t\t\treturn this.accessTokenResponseClient;\n\t\t\t}\n\t\t\tResolvableType resolvableType = ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\t\tOAuth2AuthorizationCodeGrantRequest.class);\n\t\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> bean = getBeanOrNull(resolvableType);\n\t\t\treturn (bean != null) ? bean : new RestClientAuthorizationCodeTokenResponseClient();\n\t\t}\n\n\t\tprivate ClientRegistrationRepository getClientRegistrationRepository(B builder) {\n\t\t\treturn (OAuth2ClientConfigurer.this.clientRegistrationRepository != null)\n\t\t\t\t\t? OAuth2ClientConfigurer.this.clientRegistrationRepository\n\t\t\t\t\t: OAuth2ClientConfigurerUtils.getClientRegistrationRepository(builder);\n\t\t}\n\n\t\tprivate OAuth2AuthorizedClientRepository getAuthorizedClientRepository(B builder) {\n\t\t\treturn (OAuth2ClientConfigurer.this.authorizedClientRepository != null)\n\t\t\t\t\t? OAuth2ClientConfigurer.this.authorizedClientRepository\n\t\t\t\t\t: OAuth2ClientConfigurerUtils.getAuthorizedClientRepository(builder);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate <T> T getBeanOrNull(ResolvableType type) {\n\t\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\t\tif (context == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn (T) context.getBeanProvider(type).getIfUnique();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurerUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.util.Map;\n\nimport org.springframework.beans.factory.BeanFactoryUtils;\nimport org.springframework.beans.factory.NoUniqueBeanDefinitionException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.util.StringUtils;\n\n/**\n * Utility methods for the OAuth 2.0 Client {@link AbstractHttpConfigurer}'s.\n *\n * @author Joe Grandja\n * @since 5.1\n */\nfinal class OAuth2ClientConfigurerUtils {\n\n\tprivate OAuth2ClientConfigurerUtils() {\n\t}\n\n\tstatic <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepository(B builder) {\n\t\tClientRegistrationRepository clientRegistrationRepository = builder\n\t\t\t.getSharedObject(ClientRegistrationRepository.class);\n\t\tif (clientRegistrationRepository == null) {\n\t\t\tclientRegistrationRepository = getClientRegistrationRepositoryBean(builder);\n\t\t\tbuilder.setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);\n\t\t}\n\t\treturn clientRegistrationRepository;\n\t}\n\n\tprivate static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepositoryBean(\n\t\t\tB builder) {\n\t\treturn builder.getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);\n\t}\n\n\tstatic <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientRepository getAuthorizedClientRepository(\n\t\t\tB builder) {\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = builder\n\t\t\t.getSharedObject(OAuth2AuthorizedClientRepository.class);\n\t\tif (authorizedClientRepository == null) {\n\t\t\tauthorizedClientRepository = getAuthorizedClientRepositoryBean(builder);\n\t\t\tif (authorizedClientRepository == null) {\n\t\t\t\tauthorizedClientRepository = new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(\n\t\t\t\t\t\tgetAuthorizedClientService((builder)));\n\t\t\t}\n\t\t\tbuilder.setSharedObject(OAuth2AuthorizedClientRepository.class, authorizedClientRepository);\n\t\t}\n\t\treturn authorizedClientRepository;\n\t}\n\n\tprivate static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientRepository getAuthorizedClientRepositoryBean(\n\t\t\tB builder) {\n\t\tMap<String, OAuth2AuthorizedClientRepository> authorizedClientRepositoryMap = BeanFactoryUtils\n\t\t\t.beansOfTypeIncludingAncestors(builder.getSharedObject(ApplicationContext.class),\n\t\t\t\t\tOAuth2AuthorizedClientRepository.class);\n\t\tif (authorizedClientRepositoryMap.size() > 1) {\n\t\t\tthrow new NoUniqueBeanDefinitionException(OAuth2AuthorizedClientRepository.class,\n\t\t\t\t\tauthorizedClientRepositoryMap.size(),\n\t\t\t\t\t\"Expected single matching bean of type '\" + OAuth2AuthorizedClientRepository.class.getName()\n\t\t\t\t\t\t\t+ \"' but found \" + authorizedClientRepositoryMap.size() + \": \"\n\t\t\t\t\t\t\t+ StringUtils.collectionToCommaDelimitedString(authorizedClientRepositoryMap.keySet()));\n\t\t}\n\t\treturn (!authorizedClientRepositoryMap.isEmpty() ? authorizedClientRepositoryMap.values().iterator().next()\n\t\t\t\t: null);\n\t}\n\n\tprivate static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService getAuthorizedClientService(\n\t\t\tB builder) {\n\t\tOAuth2AuthorizedClientService authorizedClientService = getAuthorizedClientServiceBean(builder);\n\t\tif (authorizedClientService == null) {\n\t\t\tauthorizedClientService = new InMemoryOAuth2AuthorizedClientService(\n\t\t\t\t\tgetClientRegistrationRepository(builder));\n\t\t}\n\t\treturn authorizedClientService;\n\t}\n\n\tprivate static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService getAuthorizedClientServiceBean(\n\t\t\tB builder) {\n\t\tMap<String, OAuth2AuthorizedClientService> authorizedClientServiceMap = BeanFactoryUtils\n\t\t\t.beansOfTypeIncludingAncestors(builder.getSharedObject(ApplicationContext.class),\n\t\t\t\t\tOAuth2AuthorizedClientService.class);\n\t\tif (authorizedClientServiceMap.size() > 1) {\n\t\t\tthrow new NoUniqueBeanDefinitionException(OAuth2AuthorizedClientService.class,\n\t\t\t\t\tauthorizedClientServiceMap.size(),\n\t\t\t\t\t\"Expected single matching bean of type '\" + OAuth2AuthorizedClientService.class.getName()\n\t\t\t\t\t\t\t+ \"' but found \" + authorizedClientServiceMap.size() + \": \"\n\t\t\t\t\t\t\t+ StringUtils.collectionToCommaDelimitedString(authorizedClientServiceMap.keySet()));\n\t\t}\n\t\treturn (!authorizedClientServiceMap.isEmpty() ? authorizedClientServiceMap.values().iterator().next() : null);\n\t}\n\n\tstatic <B extends HttpSecurityBuilder<B>> OidcSessionRegistry getOidcSessionRegistry(B builder) {\n\t\tOidcSessionRegistry sessionRegistry = builder.getSharedObject(OidcSessionRegistry.class);\n\t\tif (sessionRegistry != null) {\n\t\t\treturn sessionRegistry;\n\t\t}\n\t\tApplicationContext context = builder.getSharedObject(ApplicationContext.class);\n\t\tif (context.getBeanNamesForType(OidcSessionRegistry.class).length == 1) {\n\t\t\tsessionRegistry = context.getBean(OidcSessionRegistry.class);\n\t\t}\n\t\telse {\n\t\t\tsessionRegistry = new InMemoryOidcSessionRegistry();\n\t\t}\n\t\tbuilder.setSharedObject(OidcSessionRegistry.class, sessionRegistry);\n\t\treturn sessionRegistry;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.lang.reflect.Field;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.beans.factory.BeanFactoryUtils;\nimport org.springframework.beans.factory.NoUniqueBeanDefinitionException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.GenericApplicationListenerAdapter;\nimport org.springframework.context.event.SmartApplicationListener;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;\nimport org.springframework.security.context.DelegatingApplicationListener;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.session.AbstractSessionEvent;\nimport org.springframework.security.core.session.SessionDestroyedEvent;\nimport org.springframework.security.core.session.SessionIdChangedEvent;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient;\nimport org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizedClientRefreshedEventListener;\nimport org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.ReflectionUtils;\n\n/**\n * An {@link AbstractHttpConfigurer} for OAuth 2.0 Login, which leverages the OAuth 2.0\n * Authorization Code Grant Flow.\n *\n * <p>\n * OAuth 2.0 Login provides an application with the capability to have users log in by\n * using their existing account at an OAuth 2.0 or OpenID Connect 1.0 Provider.\n *\n * <p>\n * Defaults are provided for all configuration options with the only required\n * configuration being\n * {@link #clientRegistrationRepository(ClientRegistrationRepository)}. Alternatively, a\n * {@link ClientRegistrationRepository} {@code @Bean} may be registered instead.\n *\n * <h2>Security Filters</h2>\n *\n * The following {@code Filter}'s are populated:\n *\n * <ul>\n * <li>{@link OAuth2AuthorizationRequestRedirectFilter}</li>\n * <li>{@link OAuth2LoginAuthenticationFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * The following shared objects are populated:\n *\n * <ul>\n * <li>{@link ClientRegistrationRepository} (required)</li>\n * <li>{@link OAuth2AuthorizedClientRepository} (optional)</li>\n * <li>{@link GrantedAuthoritiesMapper} (optional)</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link ClientRegistrationRepository}</li>\n * <li>{@link OAuth2AuthorizedClientRepository}</li>\n * <li>{@link GrantedAuthoritiesMapper}</li>\n * <li>{@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not\n * configured and {@code DefaultLoginPageGeneratingFilter} is available, then a default\n * login page will be made available</li>\n * <li>{@link OidcSessionRegistry}</li>\n * </ul>\n *\n * @author Joe Grandja\n * @author Kazuki Shimizu\n * @author Ngoc Nhan\n * @since 5.0\n * @see HttpSecurity#oauth2Login(Customizer)\n * @see OAuth2AuthorizationRequestRedirectFilter\n * @see OAuth2LoginAuthenticationFilter\n * @see ClientRegistrationRepository\n * @see OAuth2AuthorizedClientRepository\n * @see AbstractAuthenticationFilterConfigurer\n */\npublic final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>\n\t\textends AbstractAuthenticationFilterConfigurer<B, OAuth2LoginConfigurer<B>, OAuth2LoginAuthenticationFilter> {\n\n\tprivate final AuthorizationEndpointConfig authorizationEndpointConfig = new AuthorizationEndpointConfig();\n\n\tprivate final TokenEndpointConfig tokenEndpointConfig = new TokenEndpointConfig();\n\n\tprivate final RedirectionEndpointConfig redirectionEndpointConfig = new RedirectionEndpointConfig();\n\n\tprivate final UserInfoEndpointConfig userInfoEndpointConfig = new UserInfoEndpointConfig();\n\n\tprivate String loginPage;\n\n\tprivate String loginProcessingUrl = OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate SecurityContextRepository securityContextRepository;\n\n\t/**\n\t * Sets the repository of client registrations.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @return the {@link OAuth2LoginConfigurer} for further configuration\n\t */\n\tpublic OAuth2LoginConfigurer<B> clientRegistrationRepository(\n\t\t\tClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the repository for authorized client(s).\n\t * @param authorizedClientRepository the authorized client repository\n\t * @return the {@link OAuth2LoginConfigurer} for further configuration\n\t * @since 5.1\n\t */\n\tpublic OAuth2LoginConfigurer<B> authorizedClientRepository(\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.getBuilder().setSharedObject(OAuth2AuthorizedClientRepository.class, authorizedClientRepository);\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the service for authorized client(s).\n\t * @param authorizedClientService the authorized client service\n\t * @return the {@link OAuth2LoginConfigurer} for further configuration\n\t */\n\tpublic OAuth2LoginConfigurer<B> authorizedClientService(OAuth2AuthorizedClientService authorizedClientService) {\n\t\tAssert.notNull(authorizedClientService, \"authorizedClientService cannot be null\");\n\t\tthis.authorizedClientRepository(\n\t\t\t\tnew AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService));\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic OAuth2LoginConfigurer<B> loginPage(String loginPage) {\n\t\tAssert.hasText(loginPage, \"loginPage cannot be empty\");\n\t\tthis.loginPage = loginPage;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic OAuth2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {\n\t\tAssert.hasText(loginProcessingUrl, \"loginProcessingUrl cannot be empty\");\n\t\tthis.loginProcessingUrl = loginProcessingUrl;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to use.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use\n\t * @return the {@link OAuth2LoginConfigurer} for further configuration\n\t */\n\t@Override\n\tpublic OAuth2LoginConfigurer<B> securityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tthis.securityContextRepository = securityContextRepository;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the registry for managing the OIDC client-provider session link\n\t * @param oidcSessionRegistry the {@link OidcSessionRegistry} to use\n\t * @return the {@link OAuth2LoginConfigurer} for further configuration\n\t * @since 6.2\n\t */\n\tpublic OAuth2LoginConfigurer<B> oidcSessionRegistry(OidcSessionRegistry oidcSessionRegistry) {\n\t\tAssert.notNull(oidcSessionRegistry, \"oidcSessionRegistry cannot be null\");\n\t\tgetBuilder().setSharedObject(OidcSessionRegistry.class, oidcSessionRegistry);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the Authorization Server's Authorization Endpoint.\n\t * @param authorizationEndpointCustomizer the {@link Customizer} to provide more\n\t * options for the {@link AuthorizationEndpointConfig}\n\t * @return the {@link OAuth2LoginConfigurer} for further customizations\n\t */\n\tpublic OAuth2LoginConfigurer<B> authorizationEndpoint(\n\t\t\tCustomizer<AuthorizationEndpointConfig> authorizationEndpointCustomizer) {\n\t\tauthorizationEndpointCustomizer.customize(this.authorizationEndpointConfig);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the Authorization Server's Token Endpoint.\n\t * @param tokenEndpointCustomizer the {@link Customizer} to provide more options for\n\t * the {@link TokenEndpointConfig}\n\t * @return the {@link OAuth2LoginConfigurer} for further customizations\n\t * @throws Exception\n\t */\n\tpublic OAuth2LoginConfigurer<B> tokenEndpoint(Customizer<TokenEndpointConfig> tokenEndpointCustomizer) {\n\t\ttokenEndpointCustomizer.customize(this.tokenEndpointConfig);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the Client's Redirection Endpoint.\n\t * @param redirectionEndpointCustomizer the {@link Customizer} to provide more options\n\t * for the {@link RedirectionEndpointConfig}\n\t * @return the {@link OAuth2LoginConfigurer} for further customizations\n\t */\n\tpublic OAuth2LoginConfigurer<B> redirectionEndpoint(\n\t\t\tCustomizer<RedirectionEndpointConfig> redirectionEndpointCustomizer) {\n\t\tredirectionEndpointCustomizer.customize(this.redirectionEndpointConfig);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the Authorization Server's UserInfo Endpoint.\n\t * @param userInfoEndpointCustomizer the {@link Customizer} to provide more options\n\t * for the {@link UserInfoEndpointConfig}\n\t * @return the {@link OAuth2LoginConfigurer} for further customizations\n\t */\n\tpublic OAuth2LoginConfigurer<B> userInfoEndpoint(Customizer<UserInfoEndpointConfig> userInfoEndpointCustomizer) {\n\t\tuserInfoEndpointCustomizer.customize(this.userInfoEndpointConfig);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(B http) {\n\t\tOAuth2LoginAuthenticationFilter authenticationFilter = new OAuth2LoginAuthenticationFilter(\n\t\t\t\tthis.getClientRegistrationRepository(), this.getAuthorizedClientRepository(), this.loginProcessingUrl);\n\t\tRequestMatcher processUri = getRequestMatcherBuilder().matcher(this.loginProcessingUrl);\n\t\tauthenticationFilter.setRequiresAuthenticationRequestMatcher(processUri);\n\t\tauthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tif (this.securityContextRepository != null) {\n\t\t\tauthenticationFilter.setSecurityContextRepository(this.securityContextRepository);\n\t\t}\n\t\tthis.setAuthenticationFilter(authenticationFilter);\n\t\tsuper.loginProcessingUrl(this.loginProcessingUrl);\n\t\tif (this.loginPage != null) {\n\t\t\t// Set custom login page\n\t\t\tsuper.loginPage(this.loginPage);\n\t\t\tsuper.init(http);\n\t\t}\n\t\telse {\n\t\t\tMap<String, String> loginUrlToClientName = this.getLoginLinks();\n\t\t\tif (loginUrlToClientName.size() == 1) {\n\t\t\t\t// Setup auto-redirect to provider login page\n\t\t\t\t// when only 1 client is configured\n\t\t\t\tthis.updateAuthenticationDefaults();\n\t\t\t\tthis.updateAccessDefaults(http);\n\t\t\t\tString providerLoginPage = loginUrlToClientName.keySet().iterator().next();\n\t\t\t\tthis.registerAuthenticationEntryPoint(http, this.getLoginEntryPoint(http, providerLoginPage));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsuper.init(http);\n\t\t\t}\n\t\t}\n\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient = getAccessTokenResponseClient();\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService = getOAuth2UserService();\n\t\tOAuth2LoginAuthenticationProvider oauth2LoginAuthenticationProvider = new OAuth2LoginAuthenticationProvider(\n\t\t\t\taccessTokenResponseClient, oauth2UserService);\n\t\tGrantedAuthoritiesMapper userAuthoritiesMapper = this.getGrantedAuthoritiesMapper();\n\t\tif (userAuthoritiesMapper != null) {\n\t\t\toauth2LoginAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);\n\t\t}\n\t\thttp.authenticationProvider(this.postProcess(oauth2LoginAuthenticationProvider));\n\t\tboolean oidcAuthenticationProviderEnabled = ClassUtils\n\t\t\t.isPresent(\"org.springframework.security.oauth2.jwt.JwtDecoder\", this.getClass().getClassLoader());\n\t\tif (oidcAuthenticationProviderEnabled) {\n\t\t\tOAuth2UserService<OidcUserRequest, OidcUser> oidcUserService = getOidcUserService();\n\t\t\tOidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider = new OidcAuthorizationCodeAuthenticationProvider(\n\t\t\t\t\taccessTokenResponseClient, oidcUserService);\n\t\t\tOidcAuthorizedClientRefreshedEventListener oidcAuthorizedClientRefreshedEventListener = new OidcAuthorizedClientRefreshedEventListener();\n\t\t\toidcAuthorizedClientRefreshedEventListener.setUserService(oidcUserService);\n\t\t\toidcAuthorizedClientRefreshedEventListener\n\t\t\t\t.setApplicationEventPublisher(http.getSharedObject(ApplicationContext.class));\n\n\t\t\tJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = this.getJwtDecoderFactoryBean();\n\t\t\tif (jwtDecoderFactory != null) {\n\t\t\t\toidcAuthorizationCodeAuthenticationProvider.setJwtDecoderFactory(jwtDecoderFactory);\n\t\t\t\toidcAuthorizedClientRefreshedEventListener.setJwtDecoderFactory(jwtDecoderFactory);\n\t\t\t}\n\t\t\tif (userAuthoritiesMapper != null) {\n\t\t\t\toidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);\n\t\t\t\toidcAuthorizedClientRefreshedEventListener.setAuthoritiesMapper(userAuthoritiesMapper);\n\t\t\t}\n\t\t\thttp.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider));\n\n\t\t\tregisterDelegateApplicationListener(this.postProcess(oidcAuthorizedClientRefreshedEventListener));\n\t\t\tconfigureOidcUserRefreshedEventListener(http);\n\t\t}\n\t\telse {\n\t\t\thttp.authenticationProvider(new OidcAuthenticationRequestChecker());\n\t\t}\n\t\tthis.initDefaultLoginFilter(http);\n\t}\n\n\t@Override\n\tpublic void configure(B http) {\n\t\tOAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(\n\t\t\t\tgetAuthorizationRequestResolver());\n\t\tif (this.authorizationEndpointConfig.authorizationRequestRepository != null) {\n\t\t\tauthorizationRequestFilter\n\t\t\t\t.setAuthorizationRequestRepository(this.authorizationEndpointConfig.authorizationRequestRepository);\n\t\t}\n\t\tif (this.authorizationEndpointConfig.authorizationRedirectStrategy != null) {\n\t\t\tauthorizationRequestFilter\n\t\t\t\t.setAuthorizationRedirectStrategy(this.authorizationEndpointConfig.authorizationRedirectStrategy);\n\t\t}\n\t\tRequestCache requestCache = http.getSharedObject(RequestCache.class);\n\t\tif (requestCache != null) {\n\t\t\tauthorizationRequestFilter.setRequestCache(requestCache);\n\t\t}\n\t\thttp.addFilter(this.postProcess(authorizationRequestFilter));\n\t\tOAuth2LoginAuthenticationFilter authenticationFilter = this.getAuthenticationFilter();\n\t\tif (this.redirectionEndpointConfig.authorizationResponseBaseUri != null) {\n\t\t\tauthenticationFilter.setRequiresAuthenticationRequestMatcher(\n\t\t\t\t\tgetRequestMatcherBuilder().matcher(this.redirectionEndpointConfig.authorizationResponseBaseUri));\n\t\t}\n\t\tif (this.authorizationEndpointConfig.authorizationRequestRepository != null) {\n\t\t\tauthenticationFilter\n\t\t\t\t.setAuthorizationRequestRepository(this.authorizationEndpointConfig.authorizationRequestRepository);\n\t\t}\n\t\tconfigureOidcSessionRegistry(http);\n\t\tsuper.configure(http);\n\t}\n\n\t@Override\n\tprotected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {\n\t\treturn getRequestMatcherBuilder().matcher(loginProcessingUrl);\n\t}\n\n\tprivate OAuth2AuthorizationRequestResolver getAuthorizationRequestResolver() {\n\t\tif (this.authorizationEndpointConfig.authorizationRequestResolver != null) {\n\t\t\treturn this.authorizationEndpointConfig.authorizationRequestResolver;\n\t\t}\n\t\tClientRegistrationRepository clientRegistrationRepository = this.getClientRegistrationRepository();\n\t\tResolvableType resolvableType = ResolvableType.forClass(OAuth2AuthorizationRequestResolver.class);\n\t\tOAuth2AuthorizationRequestResolver bean = getBeanOrNull(resolvableType);\n\t\tif (bean != null) {\n\t\t\treturn bean;\n\t\t}\n\t\tString authorizationRequestBaseUri = this.authorizationEndpointConfig.authorizationRequestBaseUri;\n\t\tif (authorizationRequestBaseUri == null) {\n\t\t\tauthorizationRequestBaseUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;\n\t\t}\n\t\treturn new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, authorizationRequestBaseUri);\n\t}\n\n\tprivate ClientRegistrationRepository getClientRegistrationRepository() {\n\t\treturn (this.clientRegistrationRepository != null) ? this.clientRegistrationRepository\n\t\t\t\t: OAuth2ClientConfigurerUtils.getClientRegistrationRepository(this.getBuilder());\n\t}\n\n\tprivate OAuth2AuthorizedClientRepository getAuthorizedClientRepository() {\n\t\treturn (this.authorizedClientRepository != null) ? this.authorizedClientRepository\n\t\t\t\t: OAuth2ClientConfigurerUtils.getAuthorizedClientRepository(this.getBuilder());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate JwtDecoderFactory<ClientRegistration> getJwtDecoderFactoryBean() {\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(JwtDecoderFactory.class, ClientRegistration.class);\n\t\tString[] names = this.getBuilder().getSharedObject(ApplicationContext.class).getBeanNamesForType(type);\n\t\tif (names.length > 1) {\n\t\t\tthrow new NoUniqueBeanDefinitionException(type, names);\n\t\t}\n\t\treturn (JwtDecoderFactory<ClientRegistration>) this.getBuilder()\n\t\t\t.getSharedObject(ApplicationContext.class)\n\t\t\t.getBeanProvider(type)\n\t\t\t.getIfUnique();\n\t}\n\n\tprivate GrantedAuthoritiesMapper getGrantedAuthoritiesMapper() {\n\t\tGrantedAuthoritiesMapper grantedAuthoritiesMapper = this.getBuilder()\n\t\t\t.getSharedObject(GrantedAuthoritiesMapper.class);\n\t\tif (grantedAuthoritiesMapper == null) {\n\t\t\tgrantedAuthoritiesMapper = this.getGrantedAuthoritiesMapperBean();\n\t\t\tif (grantedAuthoritiesMapper != null) {\n\t\t\t\tthis.getBuilder().setSharedObject(GrantedAuthoritiesMapper.class, grantedAuthoritiesMapper);\n\t\t\t}\n\t\t}\n\t\treturn grantedAuthoritiesMapper;\n\t}\n\n\tprivate GrantedAuthoritiesMapper getGrantedAuthoritiesMapperBean() {\n\t\tMap<String, GrantedAuthoritiesMapper> grantedAuthoritiesMapperMap = BeanFactoryUtils\n\t\t\t.beansOfTypeIncludingAncestors(this.getBuilder().getSharedObject(ApplicationContext.class),\n\t\t\t\t\tGrantedAuthoritiesMapper.class);\n\t\treturn (!grantedAuthoritiesMapperMap.isEmpty() ? grantedAuthoritiesMapperMap.values().iterator().next() : null);\n\t}\n\n\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> getAccessTokenResponseClient() {\n\t\tif (this.tokenEndpointConfig.accessTokenResponseClient != null) {\n\t\t\treturn this.tokenEndpointConfig.accessTokenResponseClient;\n\t\t}\n\t\tResolvableType resolvableType = ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\tOAuth2AuthorizationCodeGrantRequest.class);\n\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> bean = getBeanOrNull(resolvableType);\n\t\treturn (bean != null) ? bean : new RestClientAuthorizationCodeTokenResponseClient();\n\t}\n\n\tprivate OAuth2UserService<OidcUserRequest, OidcUser> getOidcUserService() {\n\t\tif (this.userInfoEndpointConfig.oidcUserService != null) {\n\t\t\treturn this.userInfoEndpointConfig.oidcUserService;\n\t\t}\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(OAuth2UserService.class, OidcUserRequest.class,\n\t\t\t\tOidcUser.class);\n\t\tOAuth2UserService<OidcUserRequest, OidcUser> bean = getBeanOrNull(type);\n\t\treturn (bean != null) ? bean : new OidcUserService();\n\t}\n\n\tprivate OAuth2UserService<OAuth2UserRequest, OAuth2User> getOAuth2UserService() {\n\t\tif (this.userInfoEndpointConfig.userService != null) {\n\t\t\treturn this.userInfoEndpointConfig.userService;\n\t\t}\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(OAuth2UserService.class, OAuth2UserRequest.class,\n\t\t\t\tOAuth2User.class);\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> bean = getBeanOrNull(type);\n\t\treturn (bean != null) ? bean : new DefaultOAuth2UserService();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T> T getBeanOrNull(ResolvableType type) {\n\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (T) context.getBeanProvider(type).getIfUnique();\n\t}\n\n\tprivate void initDefaultLoginFilter(B http) {\n\t\tDefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http\n\t\t\t.getSharedObject(DefaultLoginPageGeneratingFilter.class);\n\t\tif (loginPageGeneratingFilter == null || this.isCustomLoginPage()) {\n\t\t\treturn;\n\t\t}\n\t\tloginPageGeneratingFilter.setOauth2LoginEnabled(true);\n\t\tloginPageGeneratingFilter.setOauth2AuthenticationUrlToClientName(this.getLoginLinks());\n\t\tloginPageGeneratingFilter.setLoginPageUrl(this.getLoginPage());\n\t\tloginPageGeneratingFilter.setFailureUrl(this.getFailureUrl());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Map<String, String> getLoginLinks() {\n\t\tIterable<ClientRegistration> clientRegistrations = null;\n\t\tClientRegistrationRepository clientRegistrationRepository = this.getClientRegistrationRepository();\n\t\tResolvableType type = ResolvableType.forInstance(clientRegistrationRepository).as(Iterable.class);\n\t\tif (type != ResolvableType.NONE && ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {\n\t\t\tclientRegistrations = (Iterable<ClientRegistration>) clientRegistrationRepository;\n\t\t}\n\t\tif (clientRegistrations == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tString authorizationRequestBaseUri = (this.authorizationEndpointConfig.authorizationRequestBaseUri != null)\n\t\t\t\t? this.authorizationEndpointConfig.authorizationRequestBaseUri\n\t\t\t\t: OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;\n\t\tMap<String, String> loginUrlToClientName = new HashMap<>();\n\t\tclientRegistrations.forEach((registration) -> {\n\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(registration.getAuthorizationGrantType())) {\n\t\t\t\tString authorizationRequestUri = authorizationRequestBaseUri + \"/\" + registration.getRegistrationId();\n\t\t\t\tloginUrlToClientName.put(authorizationRequestUri, registration.getClientName());\n\t\t\t}\n\t\t});\n\t\treturn loginUrlToClientName;\n\t}\n\n\tprivate AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLoginPage) {\n\t\tRequestMatcher loginPageMatcher = getRequestMatcherBuilder().matcher(this.getLoginPage());\n\t\tRequestMatcher faviconMatcher = getRequestMatcherBuilder().matcher(\"/favicon.ico\");\n\t\tRequestMatcher defaultEntryPointMatcher = this.getAuthenticationEntryPointMatcher(http);\n\t\tRequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(\n\t\t\t\tnew OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);\n\t\tRequestMatcher notXRequestedWith = new NegatedRequestMatcher(\n\t\t\t\tnew RequestHeaderRequestMatcher(\"X-Requested-With\", \"XMLHttpRequest\"));\n\t\tRequestMatcher formLoginNotEnabled = getFormLoginNotEnabledRequestMatcher(http);\n\t\tLoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage);\n\t\tRequestMatcher loginUrlMatcher = new AndRequestMatcher(notXRequestedWith,\n\t\t\t\tnew NegatedRequestMatcher(defaultLoginPageMatcher), formLoginNotEnabled);\n\t\t// @formatter:off\n\t\tAuthenticationEntryPoint loginEntryPoint = DelegatingAuthenticationEntryPoint.builder()\n\t\t\t.addEntryPointFor(loginUrlEntryPoint, loginUrlMatcher)\n\t\t\t.defaultEntryPoint(getAuthenticationEntryPoint())\n\t\t\t.build();\n\t\t// @formatter:on\n\t\tExceptionHandlingConfigurer<B> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptions != null) {\n\t\t\tRequestMatcher requestMatcher = getAuthenticationEntryPointMatcher(http);\n\t\t\texceptions.defaultDeniedHandlerForMissingAuthority(\n\t\t\t\t\t(ep) -> ep.addEntryPointFor(loginEntryPoint, requestMatcher),\n\t\t\t\t\tFactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY);\n\t\t}\n\t\treturn loginEntryPoint;\n\t}\n\n\tprivate RequestMatcher getFormLoginNotEnabledRequestMatcher(B http) {\n\t\tDefaultLoginPageGeneratingFilter defaultLoginPageGeneratingFilter = http\n\t\t\t.getSharedObject(DefaultLoginPageGeneratingFilter.class);\n\t\tField formLoginEnabledField = (defaultLoginPageGeneratingFilter != null)\n\t\t\t\t? ReflectionUtils.findField(DefaultLoginPageGeneratingFilter.class, \"formLoginEnabled\") : null;\n\t\tif (formLoginEnabledField != null) {\n\t\t\tReflectionUtils.makeAccessible(formLoginEnabledField);\n\t\t\treturn (request) -> Boolean.FALSE\n\t\t\t\t.equals(ReflectionUtils.getField(formLoginEnabledField, defaultLoginPageGeneratingFilter));\n\t\t}\n\t\treturn AnyRequestMatcher.INSTANCE;\n\t}\n\n\tprivate void configureOidcSessionRegistry(B http) {\n\t\tif (http.getConfigurer(OidcLogoutConfigurer.class) == null\n\t\t\t\t&& http.getSharedObject(OidcSessionRegistry.class) == null) {\n\t\t\treturn;\n\t\t}\n\t\tOidcSessionRegistry sessionRegistry = OAuth2ClientConfigurerUtils.getOidcSessionRegistry(http);\n\t\tSessionManagementConfigurer<B> sessionConfigurer = http.getConfigurer(SessionManagementConfigurer.class);\n\t\tif (sessionConfigurer != null) {\n\t\t\tOidcSessionRegistryAuthenticationStrategy sessionAuthenticationStrategy = new OidcSessionRegistryAuthenticationStrategy();\n\t\t\tsessionAuthenticationStrategy.setSessionRegistry(sessionRegistry);\n\t\t\tsessionConfigurer.addSessionAuthenticationStrategy(sessionAuthenticationStrategy);\n\t\t}\n\t\tOidcClientSessionEventListener listener = new OidcClientSessionEventListener();\n\t\tlistener.setSessionRegistry(sessionRegistry);\n\t\tregisterDelegateApplicationListener(listener);\n\t}\n\n\tprivate void configureOidcUserRefreshedEventListener(B http) {\n\t\tOidcUserRefreshedEventListener oidcUserRefreshedEventListener = new OidcUserRefreshedEventListener();\n\t\toidcUserRefreshedEventListener.setSecurityContextHolderStrategy(this.getSecurityContextHolderStrategy());\n\t\tSecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);\n\t\tif (securityContextRepository != null) {\n\t\t\toidcUserRefreshedEventListener.setSecurityContextRepository(securityContextRepository);\n\t\t}\n\t\tregisterDelegateApplicationListener(oidcUserRefreshedEventListener);\n\t}\n\n\tprivate void registerDelegateApplicationListener(ApplicationListener<?> delegate) {\n\t\tDelegatingApplicationListener delegating = getBeanOrNull(\n\t\t\t\tResolvableType.forType(DelegatingApplicationListener.class));\n\t\tif (delegating == null) {\n\t\t\treturn;\n\t\t}\n\t\tSmartApplicationListener smartListener = new GenericApplicationListenerAdapter(delegate);\n\t\tdelegating.addListener(smartListener);\n\t}\n\n\t/**\n\t * Configuration options for the Authorization Server's Authorization Endpoint.\n\t */\n\tpublic final class AuthorizationEndpointConfig {\n\n\t\tprivate String authorizationRequestBaseUri;\n\n\t\tprivate OAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\t\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\t\tprivate RedirectStrategy authorizationRedirectStrategy;\n\n\t\tprivate AuthorizationEndpointConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the base {@code URI} used for authorization requests.\n\t\t * @param authorizationRequestBaseUri the base {@code URI} used for authorization\n\t\t * requests\n\t\t * @return the {@link AuthorizationEndpointConfig} for further configuration\n\t\t */\n\t\tpublic AuthorizationEndpointConfig baseUri(String authorizationRequestBaseUri) {\n\t\t\tAssert.hasText(authorizationRequestBaseUri, \"authorizationRequestBaseUri cannot be empty\");\n\t\t\tthis.authorizationRequestBaseUri = authorizationRequestBaseUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the resolver used for resolving {@link OAuth2AuthorizationRequest}'s.\n\t\t * @param authorizationRequestResolver the resolver used for resolving\n\t\t * {@link OAuth2AuthorizationRequest}'s\n\t\t * @return the {@link AuthorizationEndpointConfig} for further configuration\n\t\t * @since 5.1\n\t\t */\n\t\tpublic AuthorizationEndpointConfig authorizationRequestResolver(\n\t\t\t\tOAuth2AuthorizationRequestResolver authorizationRequestResolver) {\n\t\t\tAssert.notNull(authorizationRequestResolver, \"authorizationRequestResolver cannot be null\");\n\t\t\tthis.authorizationRequestResolver = authorizationRequestResolver;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.\n\t\t * @param authorizationRequestRepository the repository used for storing\n\t\t * {@link OAuth2AuthorizationRequest}'s\n\t\t * @return the {@link AuthorizationEndpointConfig} for further configuration\n\t\t */\n\t\tpublic AuthorizationEndpointConfig authorizationRequestRepository(\n\t\t\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\t\tAssert.notNull(authorizationRequestRepository, \"authorizationRequestRepository cannot be null\");\n\t\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the redirect strategy for Authorization Endpoint redirect URI.\n\t\t * @param authorizationRedirectStrategy the redirect strategy\n\t\t * @return the {@link AuthorizationEndpointConfig} for further configuration\n\t\t */\n\t\tpublic AuthorizationEndpointConfig authorizationRedirectStrategy(\n\t\t\t\tRedirectStrategy authorizationRedirectStrategy) {\n\t\t\tthis.authorizationRedirectStrategy = authorizationRedirectStrategy;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t/**\n\t * Configuration options for the Authorization Server's Token Endpoint.\n\t */\n\tpublic final class TokenEndpointConfig {\n\n\t\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\t\tprivate TokenEndpointConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the client used for requesting the access token credential from the Token\n\t\t * Endpoint.\n\t\t * @param accessTokenResponseClient the client used for requesting the access\n\t\t * token credential from the Token Endpoint\n\t\t * @return the {@link TokenEndpointConfig} for further configuration\n\t\t */\n\t\tpublic TokenEndpointConfig accessTokenResponseClient(\n\t\t\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient) {\n\t\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t/**\n\t * Configuration options for the Client's Redirection Endpoint.\n\t */\n\tpublic final class RedirectionEndpointConfig {\n\n\t\tprivate String authorizationResponseBaseUri;\n\n\t\tprivate RedirectionEndpointConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code URI} where the authorization response will be processed.\n\t\t * @param authorizationResponseBaseUri the {@code URI} where the authorization\n\t\t * response will be processed\n\t\t * @return the {@link RedirectionEndpointConfig} for further configuration\n\t\t */\n\t\tpublic RedirectionEndpointConfig baseUri(String authorizationResponseBaseUri) {\n\t\t\tAssert.hasText(authorizationResponseBaseUri, \"authorizationResponseBaseUri cannot be empty\");\n\t\t\tthis.authorizationResponseBaseUri = authorizationResponseBaseUri;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t/**\n\t * Configuration options for the Authorization Server's UserInfo Endpoint.\n\t */\n\tpublic final class UserInfoEndpointConfig {\n\n\t\tprivate OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;\n\n\t\tprivate OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;\n\n\t\tprivate UserInfoEndpointConfig() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 service used for obtaining the user attributes of the\n\t\t * End-User from the UserInfo Endpoint.\n\t\t * @param userService the OAuth 2.0 service used for obtaining the user attributes\n\t\t * of the End-User from the UserInfo Endpoint\n\t\t * @return the {@link UserInfoEndpointConfig} for further configuration\n\t\t */\n\t\tpublic UserInfoEndpointConfig userService(OAuth2UserService<OAuth2UserRequest, OAuth2User> userService) {\n\t\t\tAssert.notNull(userService, \"userService cannot be null\");\n\t\t\tthis.userService = userService;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the OpenID Connect 1.0 service used for obtaining the user attributes of\n\t\t * the End-User from the UserInfo Endpoint.\n\t\t * @param oidcUserService the OpenID Connect 1.0 service used for obtaining the\n\t\t * user attributes of the End-User from the UserInfo Endpoint\n\t\t * @return the {@link UserInfoEndpointConfig} for further configuration\n\t\t */\n\t\tpublic UserInfoEndpointConfig oidcUserService(OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService) {\n\t\t\tAssert.notNull(oidcUserService, \"oidcUserService cannot be null\");\n\t\t\tthis.oidcUserService = oidcUserService;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link GrantedAuthoritiesMapper} used for mapping\n\t\t * {@link OAuth2User#getAuthorities()}.\n\t\t * @param userAuthoritiesMapper the {@link GrantedAuthoritiesMapper} used for\n\t\t * mapping the user's authorities\n\t\t * @return the {@link UserInfoEndpointConfig} for further configuration\n\t\t */\n\t\tpublic UserInfoEndpointConfig userAuthoritiesMapper(GrantedAuthoritiesMapper userAuthoritiesMapper) {\n\t\t\tAssert.notNull(userAuthoritiesMapper, \"userAuthoritiesMapper cannot be null\");\n\t\t\tOAuth2LoginConfigurer.this.getBuilder()\n\t\t\t\t.setSharedObject(GrantedAuthoritiesMapper.class, userAuthoritiesMapper);\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tprivate static class OidcAuthenticationRequestChecker implements AuthenticationProvider {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\tOAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken) authentication;\n\t\t\tOAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t\t.getAuthorizationRequest();\n\t\t\tif (authorizationRequest.getScopes().contains(OidcScopes.OPENID)) {\n\t\t\t\t// Section 3.1.2.1 Authentication Request -\n\t\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest scope\n\t\t\t\t// REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope\n\t\t\t\t// value.\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(\"oidc_provider_not_configured\",\n\t\t\t\t\t\t\"An OpenID Connect Authentication Provider has not been configured. \"\n\t\t\t\t\t\t\t\t+ \"Check to ensure you include the dependency 'spring-security-oauth2-jose'.\",\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\treturn OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);\n\t\t}\n\n\t}\n\n\tprivate static final class OidcClientSessionEventListener implements ApplicationListener<AbstractSessionEvent> {\n\n\t\tprivate final Log logger = LogFactory.getLog(OidcClientSessionEventListener.class);\n\n\t\tprivate OidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry();\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic void onApplicationEvent(AbstractSessionEvent event) {\n\t\t\tif (event instanceof SessionDestroyedEvent destroyed) {\n\t\t\t\tthis.logger.debug(\"Received SessionDestroyedEvent\");\n\t\t\t\tthis.sessionRegistry.removeSessionInformation(destroyed.getId());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event instanceof SessionIdChangedEvent changed) {\n\t\t\t\tthis.logger.debug(\"Received SessionIdChangedEvent\");\n\t\t\t\tOidcSessionInformation information = this.sessionRegistry\n\t\t\t\t\t.removeSessionInformation(changed.getOldSessionId());\n\t\t\t\tif (information == null) {\n\t\t\t\t\tthis.logger\n\t\t\t\t\t\t.debug(\"Failed to register new session id since old session id was not found in registry\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.sessionRegistry.saveSessionInformation(information.withSessionId(changed.getNewSessionId()));\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * The registry where OIDC Provider sessions are linked to the Client session.\n\t\t * Defaults to in-memory storage.\n\t\t * @param sessionRegistry the {@link OidcSessionRegistry} to use\n\t\t */\n\t\tvoid setSessionRegistry(OidcSessionRegistry sessionRegistry) {\n\t\t\tAssert.notNull(sessionRegistry, \"sessionRegistry cannot be null\");\n\t\t\tthis.sessionRegistry = sessionRegistry;\n\t\t}\n\n\t}\n\n\tprivate static final class OidcSessionRegistryAuthenticationStrategy implements SessionAuthenticationStrategy {\n\n\t\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t\tprivate OidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry();\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic void onAuthentication(Authentication authentication, HttpServletRequest request,\n\t\t\t\tHttpServletResponse response) throws SessionAuthenticationException {\n\t\t\tHttpSession session = request.getSession(false);\n\t\t\tif (session == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!(authentication.getPrincipal() instanceof OidcUser user)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString sessionId = session.getId();\n\t\t\tCsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\t\tMap<String, String> headers = (csrfToken != null) ? Map.of(csrfToken.getHeaderName(), csrfToken.getToken())\n\t\t\t\t\t: Collections.emptyMap();\n\t\t\tOidcSessionInformation registration = new OidcSessionInformation(sessionId, headers, user);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger\n\t\t\t\t\t.trace(String.format(\"Linking a provider [%s] session to this client's session\", user.getIssuer()));\n\t\t\t}\n\t\t\tthis.sessionRegistry.saveSessionInformation(registration);\n\t\t}\n\n\t\t/**\n\t\t * The registration for linking OIDC Provider Session information to the Client's\n\t\t * session. Defaults to in-memory storage.\n\t\t * @param sessionRegistry the {@link OidcSessionRegistry} to use\n\t\t */\n\t\tvoid setSessionRegistry(OidcSessionRegistry sessionRegistry) {\n\t\t\tAssert.notNull(sessionRegistry, \"sessionRegistry cannot be null\");\n\t\t\tthis.sessionRegistry = sessionRegistry;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.io.Serial;\nimport java.util.Collections;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\n\n/**\n * An {@link org.springframework.security.core.Authentication} implementation that\n * represents the result of authenticating an OIDC Logout token for the purposes of\n * performing Back-Channel Logout.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see OidcLogoutAuthenticationToken\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html\">OIDC Back-Channel\n * Logout</a>\n */\nclass OidcBackChannelLogoutAuthentication extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 9095810699956350287L;\n\n\tprivate final OidcLogoutToken logoutToken;\n\n\tprivate final ClientRegistration clientRegistration;\n\n\t/**\n\t * Construct an {@link OidcBackChannelLogoutAuthentication}\n\t * @param logoutToken a deserialized, verified OIDC Logout Token\n\t */\n\tOidcBackChannelLogoutAuthentication(OidcLogoutToken logoutToken, ClientRegistration clientRegistration) {\n\t\tsuper(Collections.emptyList());\n\t\tthis.logoutToken = logoutToken;\n\t\tthis.clientRegistration = clientRegistration;\n\t\tsetAuthenticated(true);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic OidcLogoutToken getPrincipal() {\n\t\treturn this.logoutToken;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic OidcLogoutToken getCredentials() {\n\t\treturn this.logoutToken;\n\t}\n\n\tClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.util.function.Function;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.JwtTypeValidator;\nimport org.springframework.security.oauth2.jwt.JwtValidators;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} that authenticates an OIDC Logout Token; namely\n * deserializing it, verifying its signature, and validating its claims.\n *\n * <p>\n * Intended to be included in a\n * {@link org.springframework.security.authentication.ProviderManager}\n *\n * @author Josh Cummings\n * @since 6.2\n * @see OidcLogoutAuthenticationToken\n * @see org.springframework.security.authentication.ProviderManager\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html\">OIDC Back-Channel\n * Logout</a>\n */\nfinal class OidcBackChannelLogoutAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate JwtDecoderFactory<ClientRegistration> logoutTokenDecoderFactory;\n\n\t/**\n\t * Construct an {@link OidcBackChannelLogoutAuthenticationProvider}\n\t */\n\tOidcBackChannelLogoutAuthenticationProvider() {\n\t\tJwtTypeValidator type = new JwtTypeValidator(\"JWT\", \"logout+jwt\");\n\t\ttype.setAllowEmpty(true);\n\t\tFunction<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators\n\t\t\t.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));\n\t\tthis.logoutTokenDecoderFactory = (clientRegistration) -> {\n\t\t\tString jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();\n\t\t\tif (!StringUtils.hasText(jwkSetUri)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(\"missing_signature_verifier\",\n\t\t\t\t\t\t\"Failed to find a Signature Verifier for Client Registration: '\"\n\t\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId()\n\t\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured the JwkSet URI.\",\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();\n\t\t\tdecoder.setJwtValidator(jwtValidator.apply(clientRegistration));\n\t\t\tdecoder.setClaimSetConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());\n\t\t\treturn decoder;\n\t\t};\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (!(authentication instanceof OidcLogoutAuthenticationToken token)) {\n\t\t\treturn null;\n\t\t}\n\t\tString logoutToken = token.getLogoutToken();\n\t\tClientRegistration registration = token.getClientRegistration();\n\t\tJwt jwt = decode(registration, logoutToken);\n\t\tOidcLogoutToken oidcLogoutToken = OidcLogoutToken.withTokenValue(logoutToken)\n\t\t\t.claims((claims) -> claims.putAll(jwt.getClaims()))\n\t\t\t.build();\n\t\treturn new OidcBackChannelLogoutAuthentication(oidcLogoutToken, registration);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OidcLogoutAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tprivate Jwt decode(ClientRegistration registration, String token) {\n\t\tJwtDecoder logoutTokenDecoder = this.logoutTokenDecoderFactory.createDecoder(registration);\n\t\ttry {\n\t\t\treturn logoutTokenDecoder.decode(token);\n\t\t}\n\t\tcatch (BadJwtException failed) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, failed.getMessage(),\n\t\t\t\t\t\"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\");\n\t\t\tthrow new OAuth2AuthenticationException(error, failed);\n\t\t}\n\t\tcatch (Exception failed) {\n\t\t\tthrow new AuthenticationServiceException(failed.getMessage(), failed);\n\t\t}\n\t}\n\n\t/**\n\t * Use this {@link JwtDecoderFactory} to generate {@link JwtDecoder}s that correspond\n\t * to the {@link ClientRegistration} associated with the OIDC logout token.\n\t * @param logoutTokenDecoderFactory the {@link JwtDecoderFactory} to use\n\t */\n\tvoid setLogoutTokenDecoderFactory(JwtDecoderFactory<ClientRegistration> logoutTokenDecoderFactory) {\n\t\tAssert.notNull(logoutTokenDecoderFactory, \"logoutTokenDecoderFactory cannot be null\");\n\t\tthis.logoutTokenDecoderFactory = logoutTokenDecoderFactory;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A filter for the Client-side OIDC Back-Channel Logout endpoint\n *\n * @author Josh Cummings\n * @since 6.2\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html\">OIDC Back-Channel Logout\n * Spec</a>\n */\nclass OidcBackChannelLogoutFilter extends OncePerRequestFilter {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final AuthenticationConverter authenticationConverter;\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final OAuth2ErrorHttpMessageConverter errorHttpMessageConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tprivate final LogoutHandler logoutHandler;\n\n\t/**\n\t * Construct an {@link OidcBackChannelLogoutFilter}\n\t * @param authenticationConverter the {@link AuthenticationConverter} for deriving\n\t * Logout Token authentication\n\t * @param authenticationManager the {@link AuthenticationManager} for authenticating\n\t * Logout Tokens\n\t */\n\tOidcBackChannelLogoutFilter(AuthenticationConverter authenticationConverter,\n\t\t\tAuthenticationManager authenticationManager, LogoutHandler logoutHandler) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.notNull(logoutHandler, \"logoutHandler cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.logoutHandler = logoutHandler;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\t\tAuthentication token;\n\t\ttry {\n\t\t\ttoken = this.authenticationConverter.convert(request);\n\t\t}\n\t\tcatch (AuthenticationServiceException ex) {\n\t\t\tthis.logger.debug(\"Failed to process OIDC Back-Channel Logout\", ex);\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (AuthenticationException ex) {\n\t\t\thandleAuthenticationFailure(response, ex);\n\t\t\treturn;\n\t\t}\n\t\tif (token == null) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tAuthentication authentication;\n\t\ttry {\n\t\t\tauthentication = this.authenticationManager.authenticate(token);\n\t\t}\n\t\tcatch (AuthenticationServiceException ex) {\n\t\t\tthis.logger.debug(\"Failed to process OIDC Back-Channel Logout\", ex);\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (AuthenticationException ex) {\n\t\t\thandleAuthenticationFailure(response, ex);\n\t\t\treturn;\n\t\t}\n\t\tthis.logoutHandler.logout(request, response, authentication);\n\t}\n\n\tprivate void handleAuthenticationFailure(HttpServletResponse response, Exception ex) throws IOException {\n\t\tthis.logger.debug(\"Failed to process OIDC Back-Channel Logout\", ex);\n\t\tresponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n\t\tthis.errorHttpMessageConverter.write(oauth2Error(ex), null, new ServletServerHttpResponse(response));\n\t}\n\n\tprivate OAuth2Error oauth2Error(Exception ex) {\n\t\tif (ex instanceof OAuth2AuthenticationException oauth2) {\n\t\t\treturn oauth2.getError();\n\t\t}\n\t\treturn new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, ex.getMessage(),\n\t\t\t\t\"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.RestClientException;\nimport org.springframework.web.client.RestOperations;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@link LogoutHandler} that locates the sessions associated with a given OIDC\n * Back-Channel Logout Token and invalidates each one.\n *\n * @author Josh Cummings\n * @since 6.4\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html\">OIDC Back-Channel Logout\n * Spec</a>\n */\npublic final class OidcBackChannelLogoutHandler implements LogoutHandler {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OidcSessionRegistry sessionRegistry;\n\n\tprivate RestOperations restOperations = new RestTemplate();\n\n\tprivate String logoutUri = \"{baseUrl}/logout/connect/back-channel/{registrationId}\";\n\n\tprivate String sessionCookieName = \"JSESSIONID\";\n\n\tprivate final OAuth2ErrorHttpMessageConverter errorHttpMessageConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tpublic OidcBackChannelLogoutHandler(OidcSessionRegistry sessionRegistry) {\n\t\tthis.sessionRegistry = sessionRegistry;\n\t}\n\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {\n\t\tif (!(authentication instanceof OidcBackChannelLogoutAuthentication token)) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tString message = \"Did not perform OIDC Back-Channel Logout since authentication [%s] was of the wrong type\";\n\t\t\t\tthis.logger.debug(String.format(message, authentication.getClass().getSimpleName()));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tIterable<OidcSessionInformation> sessions = this.sessionRegistry.removeSessionInformation(token.getPrincipal());\n\t\tCollection<String> errors = new ArrayList<>();\n\t\tint totalCount = 0;\n\t\tint invalidatedCount = 0;\n\t\tfor (OidcSessionInformation session : sessions) {\n\t\t\ttotalCount++;\n\t\t\ttry {\n\t\t\t\teachLogout(request, token, session);\n\t\t\t\tinvalidatedCount++;\n\t\t\t}\n\t\t\tcatch (RestClientException ex) {\n\t\t\t\tthis.logger.debug(\"Failed to invalidate session\", ex);\n\t\t\t\terrors.add(ex.getMessage());\n\t\t\t\tthis.sessionRegistry.saveSessionInformation(session);\n\t\t\t}\n\t\t}\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(String.format(\"Invalidated %d out of %d sessions\", invalidatedCount, totalCount));\n\t\t}\n\t\tif (!errors.isEmpty()) {\n\t\t\thandleLogoutFailure(response, oauth2Error(errors));\n\t\t}\n\t}\n\n\tprivate void eachLogout(HttpServletRequest request, OidcBackChannelLogoutAuthentication token,\n\t\t\tOidcSessionInformation session) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.add(HttpHeaders.COOKIE, this.sessionCookieName + \"=\" + session.getSessionId());\n\t\tfor (Map.Entry<String, String> credential : session.getAuthorities().entrySet()) {\n\t\t\theaders.add(credential.getKey(), credential.getValue());\n\t\t}\n\t\theaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);\n\t\tString logout = computeLogoutEndpoint(request, token);\n\t\tMultiValueMap<String, String> body = new LinkedMultiValueMap<>();\n\t\tbody.add(\"logout_token\", token.getPrincipal().getTokenValue());\n\t\tbody.add(\"_spring_security_internal_logout\", \"true\");\n\t\tHttpEntity<?> entity = new HttpEntity<>(body, headers);\n\t\tthis.restOperations.postForEntity(logout, entity, Object.class);\n\t}\n\n\tString computeLogoutEndpoint(HttpServletRequest request, OidcBackChannelLogoutAuthentication token) {\n\t\t// @formatter:off\n\t\tUriComponents uriComponents = UriComponentsBuilder\n\t\t\t\t.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t\t.replacePath(request.getContextPath())\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.fragment(null)\n\t\t\t\t.build();\n\n\t\tMap<String, String> uriVariables = new HashMap<>();\n\t\tString scheme = uriComponents.getScheme();\n\t\turiVariables.put(\"baseScheme\", (scheme != null) ? scheme : \"\");\n\t\turiVariables.put(\"baseUrl\", uriComponents.toUriString());\n\n\t\tString host = uriComponents.getHost();\n\t\turiVariables.put(\"baseHost\", (host != null) ? host : \"\");\n\n\t\tString path = uriComponents.getPath();\n\t\turiVariables.put(\"basePath\", (path != null) ? path : \"\");\n\n\t\tint port = uriComponents.getPort();\n\t\turiVariables.put(\"basePort\", (port == -1) ? \"\" : \":\" + port);\n\n\t\tString registrationId = token.getClientRegistration().getRegistrationId();\n\t\turiVariables.put(\"registrationId\", registrationId);\n\n\t\treturn UriComponentsBuilder.fromUriString(this.logoutUri)\n\t\t\t\t.buildAndExpand(uriVariables)\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate OAuth2Error oauth2Error(Collection<String> errors) {\n\t\treturn new OAuth2Error(\"partial_logout\", \"not all sessions were terminated: \" + errors,\n\t\t\t\t\"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\");\n\t}\n\n\tprivate void handleLogoutFailure(HttpServletResponse response, OAuth2Error error) {\n\t\tresponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n\t\ttry {\n\t\t\tthis.errorHttpMessageConverter.write(error, null, new ServletServerHttpResponse(response));\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Use this logout URI for performing per-session logout. Defaults to {@code /logout}\n\t * since that is the default URI for\n\t * {@link org.springframework.security.web.authentication.logout.LogoutFilter}.\n\t * @param logoutUri the URI to use\n\t */\n\tpublic void setLogoutUri(String logoutUri) {\n\t\tAssert.hasText(logoutUri, \"logoutUri cannot be empty\");\n\t\tthis.logoutUri = logoutUri;\n\t}\n\n\t/**\n\t * Use this cookie name for the session identifier. Defaults to {@code JSESSIONID}.\n\t *\n\t * <p>\n\t * Note that if you are using Spring Session, this likely needs to change to SESSION.\n\t * @param sessionCookieName the cookie name to use\n\t */\n\tpublic void setSessionCookieName(String sessionCookieName) {\n\t\tAssert.hasText(sessionCookieName, \"clientSessionCookieName cannot be empty\");\n\t\tthis.sessionCookieName = sessionCookieName;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutTokenValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimAccessor;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link OAuth2TokenValidator} that validates OIDC Logout Token claims in conformance\n * with the OIDC Back-Channel Logout Spec.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see OidcLogoutToken\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken\">Logout\n * Token</a>\n * @see <a target=\"blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\">the OIDC\n * Back-Channel Logout spec</a>\n */\nfinal class OidcBackChannelLogoutTokenValidator implements OAuth2TokenValidator<Jwt> {\n\n\tprivate static final String LOGOUT_VALIDATION_URL = \"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\";\n\n\tprivate static final String BACK_CHANNEL_LOGOUT_EVENT = \"http://schemas.openid.net/event/backchannel-logout\";\n\n\tprivate final String audience;\n\n\tprivate final String issuer;\n\n\tOidcBackChannelLogoutTokenValidator(ClientRegistration clientRegistration) {\n\t\tthis.audience = clientRegistration.getClientId();\n\t\tString issuer = clientRegistration.getProviderDetails().getIssuerUri();\n\t\tAssert.hasText(issuer, \"Provider issuer cannot be null\");\n\t\tthis.issuer = issuer;\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\tCollection<OAuth2Error> errors = new ArrayList<>();\n\n\t\tLogoutTokenClaimAccessor logoutClaims = jwt::getClaims;\n\t\tMap<String, Object> events = logoutClaims.getEvents();\n\t\tif (events == null) {\n\t\t\terrors.add(invalidLogoutToken(\"events claim must not be null\"));\n\t\t}\n\t\telse if (events.get(BACK_CHANNEL_LOGOUT_EVENT) == null) {\n\t\t\terrors.add(invalidLogoutToken(\"events claim map must contain \\\"\" + BACK_CHANNEL_LOGOUT_EVENT + \"\\\" key\"));\n\t\t}\n\n\t\tString issuer = logoutClaims.getIssuer().toExternalForm();\n\t\tif (issuer == null) {\n\t\t\terrors.add(invalidLogoutToken(\"iss claim must not be null\"));\n\t\t}\n\t\telse if (!this.issuer.equals(issuer)) {\n\t\t\terrors.add(invalidLogoutToken(\n\t\t\t\t\t\"iss claim value must match `ClientRegistration#getProviderDetails#getIssuerUri`\"));\n\t\t}\n\n\t\tList<String> audience = logoutClaims.getAudience();\n\t\tif (audience == null) {\n\t\t\terrors.add(invalidLogoutToken(\"aud claim must not be null\"));\n\t\t}\n\t\telse if (!audience.contains(this.audience)) {\n\t\t\terrors.add(invalidLogoutToken(\"aud claim value must include `ClientRegistration#getClientId`\"));\n\t\t}\n\n\t\tInstant issuedAt = logoutClaims.getIssuedAt();\n\t\tif (issuedAt == null) {\n\t\t\terrors.add(invalidLogoutToken(\"iat claim must not be null\"));\n\t\t}\n\n\t\tString jwtId = logoutClaims.getId();\n\t\tif (jwtId == null) {\n\t\t\terrors.add(invalidLogoutToken(\"jti claim must not be null\"));\n\t\t}\n\n\t\tif (logoutClaims.getSubject() == null && logoutClaims.getSessionId() == null) {\n\t\t\terrors.add(invalidLogoutToken(\"sub and sid claims must not both be null\"));\n\t\t}\n\n\t\tif (logoutClaims.getClaim(\"nonce\") != null) {\n\t\t\terrors.add(invalidLogoutToken(\"nonce claim must not be present\"));\n\t\t}\n\n\t\treturn OAuth2TokenValidatorResult.failure(errors);\n\t}\n\n\tprivate static OAuth2Error invalidLogoutToken(String description) {\n\t\treturn new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, description, LOGOUT_VALIDATION_URL);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationConverter} that extracts the OIDC Logout Token authentication\n * request\n *\n * @author Josh Cummings\n * @since 6.2\n */\nfinal class OidcLogoutAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final String DEFAULT_LOGOUT_URI = \"/logout/connect/back-channel/{registrationId}\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate RequestMatcher requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.POST, DEFAULT_LOGOUT_URI);\n\n\tOidcLogoutAuthenticationConverter(ClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t}\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tRequestMatcher.MatchResult result = this.requestMatcher.matcher(request);\n\t\tif (!result.isMatch()) {\n\t\t\treturn null;\n\t\t}\n\t\tString registrationId = result.getVariables().get(\"registrationId\");\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);\n\t\tif (clientRegistration == null) {\n\t\t\tthis.logger.debug(\"Did not process OIDC Back-Channel Logout since no ClientRegistration was found\");\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\t\tString logoutToken = request.getParameter(\"logout_token\");\n\t\tif (logoutToken == null) {\n\t\t\tthis.logger.debug(\"Failed to process OIDC Back-Channel Logout since no logout token was found\");\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\t\treturn new OidcLogoutAuthenticationToken(logoutToken, clientRegistration);\n\t}\n\n\t/**\n\t * The logout endpoint. Defaults to\n\t * {@code /logout/connect/back-channel/{registrationId}}.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tvoid setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.io.Serial;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\n\n/**\n * An {@link org.springframework.security.core.Authentication} instance that represents a\n * request to authenticate an OIDC Logout Token.\n *\n * @author Josh Cummings\n * @since 6.2\n */\nclass OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1568528983223505540L;\n\n\tprivate final String logoutToken;\n\n\tprivate final ClientRegistration clientRegistration;\n\n\t/**\n\t * Construct an {@link OidcLogoutAuthenticationToken}\n\t * @param logoutToken a signed, serialized OIDC Logout token\n\t * @param clientRegistration the {@link ClientRegistration client} associated with\n\t * this token; this is usually derived from material in the logout HTTP request\n\t */\n\tOidcLogoutAuthenticationToken(String logoutToken, ClientRegistration clientRegistration) {\n\t\tsuper(AuthorityUtils.NO_AUTHORITIES);\n\t\tthis.logoutToken = logoutToken;\n\t\tthis.clientRegistration = clientRegistration;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic String getCredentials() {\n\t\treturn this.logoutToken;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic String getPrincipal() {\n\t\treturn this.logoutToken;\n\t}\n\n\t/**\n\t * Get the signed, serialized OIDC Logout token\n\t * @return the logout token\n\t */\n\tString getLogoutToken() {\n\t\treturn this.logoutToken;\n\t}\n\n\t/**\n\t * Get the {@link ClientRegistration} associated with this logout token\n\t * @return the {@link ClientRegistration}\n\t */\n\tClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.logout.CompositeLogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AbstractHttpConfigurer} for OIDC Logout flows\n *\n * <p>\n * OIDC Logout provides an application with the capability to have users log out by using\n * their existing account at an OAuth 2.0 or OpenID Connect 1.0 Provider.\n *\n *\n * <h2>Security Filters</h2>\n *\n * The following {@code Filter} is populated:\n *\n * <ul>\n * <li>{@link OidcBackChannelLogoutFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link ClientRegistrationRepository}</li>\n * </ul>\n *\n * @author Josh Cummings\n * @author Ngoc Nhan\n * @since 6.2\n * @see HttpSecurity#oidcLogout()\n * @see OidcBackChannelLogoutFilter\n * @see ClientRegistrationRepository\n */\npublic final class OidcLogoutConfigurer<B extends HttpSecurityBuilder<B>>\n\t\textends AbstractHttpConfigurer<OidcLogoutConfigurer<B>, B> {\n\n\tprivate BackChannelLogoutConfigurer backChannel;\n\n\t/**\n\t * Sets the repository of client registrations.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @return the {@link OAuth2LoginConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutConfigurer<B> clientRegistrationRepository(\n\t\t\tClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the registry for managing the OIDC client-provider session link\n\t * @param oidcSessionRegistry the {@link OidcSessionRegistry} to use\n\t * @return the {@link OAuth2LoginConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutConfigurer<B> oidcSessionRegistry(OidcSessionRegistry oidcSessionRegistry) {\n\t\tAssert.notNull(oidcSessionRegistry, \"oidcSessionRegistry cannot be null\");\n\t\tgetBuilder().setSharedObject(OidcSessionRegistry.class, oidcSessionRegistry);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configure OIDC Back-Channel Logout using the provided {@link Consumer}\n\t * @return the {@link OidcLogoutConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutConfigurer<B> backChannel(Customizer<BackChannelLogoutConfigurer> backChannelLogoutConfigurer) {\n\t\tif (this.backChannel == null) {\n\t\t\tthis.backChannel = new BackChannelLogoutConfigurer();\n\t\t}\n\t\tbackChannelLogoutConfigurer.customize(this.backChannel);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void configure(B builder) {\n\t\tif (this.backChannel != null) {\n\t\t\tthis.backChannel.configure(builder);\n\t\t}\n\t}\n\n\t/**\n\t * A configurer for configuring OIDC Back-Channel Logout\n\t */\n\tpublic final class BackChannelLogoutConfigurer {\n\n\t\tprivate AuthenticationConverter authenticationConverter;\n\n\t\tprivate final AuthenticationManager authenticationManager = new ProviderManager(\n\t\t\t\tnew OidcBackChannelLogoutAuthenticationProvider());\n\n\t\tprivate Function<B, LogoutHandler> logoutHandler = this::logoutHandler;\n\n\t\tprivate AuthenticationConverter authenticationConverter(B http) {\n\t\t\tif (this.authenticationConverter == null) {\n\t\t\t\tClientRegistrationRepository clientRegistrationRepository = OAuth2ClientConfigurerUtils\n\t\t\t\t\t.getClientRegistrationRepository(http);\n\t\t\t\tthis.authenticationConverter = new OidcLogoutAuthenticationConverter(clientRegistrationRepository);\n\t\t\t}\n\t\t\treturn this.authenticationConverter;\n\t\t}\n\n\t\tprivate AuthenticationManager authenticationManager() {\n\t\t\treturn this.authenticationManager;\n\t\t}\n\n\t\tprivate LogoutHandler logoutHandler(B http) {\n\t\t\tOidcBackChannelLogoutHandler logoutHandler = getBeanOrNull(OidcBackChannelLogoutHandler.class);\n\t\t\tif (logoutHandler != null) {\n\t\t\t\treturn logoutHandler;\n\t\t\t}\n\t\t\tlogoutHandler = new OidcBackChannelLogoutHandler(OAuth2ClientConfigurerUtils.getOidcSessionRegistry(http));\n\t\t\treturn logoutHandler;\n\t\t}\n\n\t\t/**\n\t\t * Use this endpoint when invoking a back-channel logout.\n\t\t *\n\t\t * <p>\n\t\t * The resulting {@link LogoutHandler} will {@code POST} the session cookie and\n\t\t * CSRF token to this endpoint to invalidate the corresponding end-user session.\n\t\t *\n\t\t * <p>\n\t\t * Supports URI templates like {@code {baseUrl}}, {@code {baseScheme}}, and\n\t\t * {@code {basePort}}.\n\t\t *\n\t\t * <p>\n\t\t * By default, the URI is set to\n\t\t * {@code {baseScheme}://localhost{basePort}/logout}, meaning that the scheme and\n\t\t * port of the original back-channel request is preserved, while the host and\n\t\t * endpoint are changed.\n\t\t *\n\t\t * <p>\n\t\t * If you are using Spring Security for the logout endpoint, the path part of this\n\t\t * URI should match the value configured there.\n\t\t *\n\t\t * <p>\n\t\t * Otherwise, this is handy in the event that your server configuration means that\n\t\t * the scheme, server name, or port in the {@code Host} header are different from\n\t\t * how you would address the same server internally.\n\t\t * @param logoutUri the URI to request logout on the back-channel\n\t\t * @return the {@link BackChannelLogoutConfigurer} for further customizations\n\t\t * @since 6.2.4\n\t\t */\n\t\tpublic BackChannelLogoutConfigurer logoutUri(String logoutUri) {\n\t\t\tthis.logoutHandler = (http) -> {\n\t\t\t\tOidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(\n\t\t\t\t\t\tOAuth2ClientConfigurerUtils.getOidcSessionRegistry(http));\n\t\t\t\tlogoutHandler.setLogoutUri(logoutUri);\n\t\t\t\treturn logoutHandler;\n\t\t\t};\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure what and how per-session logout will be performed.\n\t\t *\n\t\t * <p>\n\t\t * This overrides any value given to {@link #logoutUri(String)}\n\t\t *\n\t\t * <p>\n\t\t * By default, the resulting {@link LogoutHandler} will {@code POST} the session\n\t\t * cookie and OIDC logout token back to the original back-channel logout endpoint.\n\t\t *\n\t\t * <p>\n\t\t * Using this method changes the underlying default that {@code POST}s the session\n\t\t * cookie and CSRF token to your application's {@code /logout} endpoint. As such,\n\t\t * it is recommended to call this instead of accepting the {@code /logout} default\n\t\t * as this does not require any special CSRF configuration, even if you don't\n\t\t * require other changes.\n\t\t *\n\t\t * <p>\n\t\t * For example, configuring Back-Channel Logout in the following way:\n\t\t *\n\t\t * <pre>\n\t\t * \thttp\n\t\t *     \t.oidcLogout((oidc) -&gt; oidc\n\t\t *     \t\t.backChannel((backChannel) -&gt; backChannel\n\t\t *     \t\t\t.logoutHandler(new OidcBackChannelLogoutHandler())\n\t\t *     \t\t)\n\t\t *     \t);\n\t\t * </pre>\n\t\t *\n\t\t * will make so that the per-session logout invocation no longer requires special\n\t\t * CSRF configurations.\n\t\t *\n\t\t * <p>\n\t\t * The default URI is\n\t\t * {@code {baseUrl}/logout/connect/back-channel/{registrationId}}, which is simply\n\t\t * an internal version of the same endpoint exposed to your Back-Channel services.\n\t\t * You can use {@link OidcBackChannelLogoutHandler#setLogoutUri(String)} to alter\n\t\t * the scheme, server name, or port in the {@code Host} header to accommodate how\n\t\t * your application would address itself internally.\n\t\t *\n\t\t * <p>\n\t\t * For example, if the way your application would internally call itself is on a\n\t\t * different scheme and port than incoming traffic, you can configure the endpoint\n\t\t * in the following way:\n\t\t *\n\t\t * <pre>\n\t\t * \thttp\n\t\t * \t\t.oidcLogout((oidc) -&gt; oidc\n\t\t * \t\t\t.backChannel((backChannel) -&gt; backChannel\n\t\t * \t\t\t\t.logoutHandler(\"http://localhost:9000/logout/connect/back-channel/{registrationId}\")\n\t\t * \t\t\t)\n\t\t * \t\t);\n\t\t * </pre>\n\t\t *\n\t\t * <p>\n\t\t * You can also publish it as a {@code @Bean} as follows:\n\t\t *\n\t\t * <pre>\n\t\t *\t&commat;Bean\n\t\t *\tOidcBackChannelLogoutHandler oidcLogoutHandler(OidcSessionRegistry sessionRegistry) {\n\t\t *  \tOidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(sessionRegistry);\n\t\t *  \tlogoutHandler.setSessionCookieName(\"SESSION\");\n\t\t *  \treturn logoutHandler;\n\t\t *\t}\n\t\t * </pre>\n\t\t *\n\t\t * to have the same effect.\n\t\t * @param logoutHandler the {@link LogoutHandler} to use each individual session\n\t\t * @return {@link BackChannelLogoutConfigurer} for further customizations\n\t\t * @since 6.4\n\t\t */\n\t\tpublic BackChannelLogoutConfigurer logoutHandler(LogoutHandler logoutHandler) {\n\t\t\tthis.logoutHandler = (http) -> logoutHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\tvoid configure(B http) {\n\t\t\tLogoutHandler oidcLogout = this.logoutHandler.apply(http);\n\t\t\tLogoutHandler sessionLogout = new SecurityContextLogoutHandler();\n\t\t\tLogoutConfigurer<B> logout = http.getConfigurer(LogoutConfigurer.class);\n\t\t\tif (logout != null) {\n\t\t\t\tsessionLogout = new CompositeLogoutHandler(logout.getLogoutHandlers());\n\t\t\t}\n\t\t\tOidcBackChannelLogoutFilter filter = new OidcBackChannelLogoutFilter(authenticationConverter(http),\n\t\t\t\t\tauthenticationManager(), new EitherLogoutHandler(oidcLogout, sessionLogout));\n\t\t\thttp.addFilterBefore(filter, CsrfFilter.class);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate <T> T getBeanOrNull(Class<?> clazz) {\n\t\t\tApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);\n\t\t\tif (context == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn (T) context.getBeanProvider(clazz).getIfUnique();\n\t\t}\n\n\t\tprivate static final class EitherLogoutHandler implements LogoutHandler {\n\n\t\t\tprivate final LogoutHandler left;\n\n\t\t\tprivate final LogoutHandler right;\n\n\t\t\tEitherLogoutHandler(LogoutHandler left, LogoutHandler right) {\n\t\t\t\tthis.left = left;\n\t\t\t\tthis.right = right;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\t\tAuthentication authentication) {\n\t\t\t\tif (request.getParameter(\"_spring_security_internal_logout\") == null) {\n\t\t\t\t\tthis.left.logout(request, response, authentication);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.right.logout(request, response, authentication);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcUserRefreshedEventListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.client.oidc.authentication.event.OidcUserRefreshedEvent;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\n/**\n * An {@link ApplicationListener} that listens for events of type\n * {@link OidcUserRefreshedEvent} and refreshes the {@link SecurityContext}.\n *\n * @author Steve Riesenberg\n * @since 6.5\n * @see org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizedClientRefreshedEventListener\n */\nfinal class OidcUserRefreshedEventListener implements ApplicationListener<OidcUserRefreshedEvent> {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();\n\n\t@Override\n\tpublic void onApplicationEvent(OidcUserRefreshedEvent event) {\n\t\tSecurityContext securityContext = this.securityContextHolderStrategy.createEmptyContext();\n\t\tsecurityContext.setAuthentication(event.getAuthentication());\n\t\tthis.securityContextHolderStrategy.setContext(securityContext);\n\n\t\tRequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n\t\tif (!(requestAttributes instanceof ServletRequestAttributes servletRequestAttributes)) {\n\t\t\treturn;\n\t\t}\n\n\t\tHttpServletRequest request = servletRequestAttributes.getRequest();\n\t\tHttpServletResponse response = servletRequestAttributes.getResponse();\n\t\tthis.securityContextRepository.saveContext(securityContext, request, response);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t * @param securityContextHolderStrategy the {@link SecurityContextHolderStrategy} to\n\t * use\n\t */\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} upon\n\t * receiving an {@link OidcUserRefreshedEvent}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use\n\t */\n\tvoid setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/AbstractOAuth2Configurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Base configurer for an OAuth 2.0 component (e.g. protocol endpoint).\n *\n * @author Joe Grandja\n * @since 7.0\n */\nabstract class AbstractOAuth2Configurer {\n\n\tprivate final ObjectPostProcessor<Object> objectPostProcessor;\n\n\tAbstractOAuth2Configurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tthis.objectPostProcessor = objectPostProcessor;\n\t}\n\n\tabstract void init(HttpSecurity httpSecurity);\n\n\tabstract void configure(HttpSecurity httpSecurity);\n\n\tabstract RequestMatcher getRequestMatcher();\n\n\tprotected final <T> T postProcess(T object) {\n\t\treturn (T) this.objectPostProcessor.postProcess(object);\n\t}\n\n\tprotected final ObjectPostProcessor<Object> getObjectPostProcessor() {\n\t\treturn this.objectPostProcessor;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/AuthorizationServerContextFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Filter} that associates the {@link AuthorizationServerContext} to the\n * {@link AuthorizationServerContextHolder}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthorizationServerContext\n * @see AuthorizationServerContextHolder\n * @see AuthorizationServerSettings\n */\nfinal class AuthorizationServerContextFilter extends OncePerRequestFilter {\n\n\tprivate final AuthorizationServerSettings authorizationServerSettings;\n\n\tprivate final IssuerResolver issuerResolver;\n\n\tAuthorizationServerContextFilter(AuthorizationServerSettings authorizationServerSettings) {\n\t\tAssert.notNull(authorizationServerSettings, \"authorizationServerSettings cannot be null\");\n\t\tthis.authorizationServerSettings = authorizationServerSettings;\n\t\tthis.issuerResolver = new IssuerResolver(authorizationServerSettings);\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\ttry {\n\t\t\tString issuer = this.issuerResolver.resolve(request);\n\t\t\tAuthorizationServerContext authorizationServerContext = new DefaultAuthorizationServerContext(issuer,\n\t\t\t\t\tthis.authorizationServerSettings);\n\t\t\tAuthorizationServerContextHolder.setContext(authorizationServerContext);\n\t\t\tfilterChain.doFilter(request, response);\n\t\t}\n\t\tfinally {\n\t\t\tAuthorizationServerContextHolder.resetContext();\n\t\t}\n\t}\n\n\tprivate static final class IssuerResolver {\n\n\t\tprivate final String issuer;\n\n\t\tprivate final Set<String> endpointUris;\n\n\t\tprivate IssuerResolver(AuthorizationServerSettings authorizationServerSettings) {\n\t\t\tif (authorizationServerSettings.getIssuer() != null) {\n\t\t\t\tthis.issuer = authorizationServerSettings.getIssuer();\n\t\t\t\tthis.endpointUris = Collections.emptySet();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.issuer = null;\n\t\t\t\tthis.endpointUris = new HashSet<>();\n\t\t\t\tthis.endpointUris.add(\"/.well-known/oauth-authorization-server\");\n\t\t\t\tthis.endpointUris.add(\"/.well-known/openid-configuration\");\n\t\t\t\tfor (Map.Entry<String, Object> setting : authorizationServerSettings.getSettings().entrySet()) {\n\t\t\t\t\tif (setting.getKey().endsWith(\"-endpoint\")) {\n\t\t\t\t\t\tthis.endpointUris.add((String) setting.getValue());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate String resolve(HttpServletRequest request) {\n\t\t\tif (this.issuer != null) {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\t// Resolve Issuer Identifier dynamically from request\n\t\t\tString path = request.getRequestURI();\n\t\t\tif (!StringUtils.hasText(path)) {\n\t\t\t\tpath = \"\";\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfor (String endpointUri : this.endpointUris) {\n\t\t\t\t\tif (path.contains(endpointUri)) {\n\t\t\t\t\t\tpath = path.replace(endpointUri, \"\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// @formatter:off\n\t\t\treturn UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t\t\t.replacePath(path)\n\t\t\t\t\t.replaceQuery(null)\n\t\t\t\t\t.fragment(null)\n\t\t\t\t\t.build()\n\t\t\t\t\t.toUriString();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tprivate static final class DefaultAuthorizationServerContext implements AuthorizationServerContext {\n\n\t\tprivate final String issuer;\n\n\t\tprivate final AuthorizationServerSettings authorizationServerSettings;\n\n\t\tprivate DefaultAuthorizationServerContext(String issuer,\n\t\t\t\tAuthorizationServerSettings authorizationServerSettings) {\n\t\t\tthis.issuer = issuer;\n\t\t\tthis.authorizationServerSettings = authorizationServerSettings;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getIssuer() {\n\t\t\treturn this.issuer;\n\t\t}\n\n\t\t@Override\n\t\tpublic AuthorizationServerSettings getAuthorizationServerSettings() {\n\t\t\treturn this.authorizationServerSettings;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/DefaultOAuth2TokenCustomizers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.security.MessageDigest;\nimport java.security.cert.X509Certificate;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport com.nimbusds.jose.jwk.JWK;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * @author Joe Grandja\n * @author Steve Riesenberg\n * @since 7.0\n */\nfinal class DefaultOAuth2TokenCustomizers {\n\n\tprivate DefaultOAuth2TokenCustomizers() {\n\t}\n\n\tstatic OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {\n\t\treturn (context) -> context.getClaims().claims((claims) -> customize(context, claims));\n\t}\n\n\tstatic OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer() {\n\t\treturn (context) -> context.getClaims().claims((claims) -> customize(context, claims));\n\t}\n\n\tprivate static void customize(OAuth2TokenContext tokenContext, Map<String, Object> claims) {\n\t\tMap<String, Object> cnfClaims = null;\n\n\t\t// Add 'cnf' claim for Mutual-TLS Client Certificate-Bound Access Tokens\n\t\tif (OAuth2TokenType.ACCESS_TOKEN.equals(tokenContext.getTokenType())\n\t\t\t\t&& tokenContext.getAuthorizationGrant() != null && tokenContext.getAuthorizationGrant()\n\t\t\t\t\t.getPrincipal() instanceof OAuth2ClientAuthenticationToken clientAuthentication) {\n\n\t\t\tif ((ClientAuthenticationMethod.TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod())\n\t\t\t\t\t|| ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH\n\t\t\t\t\t\t.equals(clientAuthentication.getClientAuthenticationMethod()))\n\t\t\t\t\t&& tokenContext.getRegisteredClient().getTokenSettings().isX509CertificateBoundAccessTokens()) {\n\n\t\t\t\tX509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();\n\t\t\t\ttry {\n\t\t\t\t\tString sha256Thumbprint = computeSHA256Thumbprint(clientCertificateChain[0]);\n\t\t\t\t\tcnfClaims = new HashMap<>();\n\t\t\t\t\tcnfClaims.put(\"x5t#S256\", sha256Thumbprint);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\t\"Failed to compute SHA-256 Thumbprint for client X509Certificate.\", null);\n\t\t\t\t\tthrow new OAuth2AuthenticationException(error, ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add 'cnf' claim for OAuth 2.0 Demonstrating Proof of Possession (DPoP)\n\t\tJwt dPoPProofJwt = tokenContext.get(OAuth2TokenContext.DPOP_PROOF_KEY);\n\t\tif (OAuth2TokenType.ACCESS_TOKEN.equals(tokenContext.getTokenType()) && dPoPProofJwt != null) {\n\t\t\tJWK jwk = null;\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tMap<String, Object> jwkJson = (Map<String, Object>) dPoPProofJwt.getHeaders().get(\"jwk\");\n\t\t\ttry {\n\t\t\t\tjwk = JWK.parse(jwkJson);\n\t\t\t}\n\t\t\tcatch (Exception ignored) {\n\t\t\t}\n\t\t\tif (jwk == null) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF,\n\t\t\t\t\t\t\"jwk header is missing or invalid.\", null);\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tString sha256Thumbprint = jwk.computeThumbprint().toString();\n\t\t\t\tif (cnfClaims == null) {\n\t\t\t\t\tcnfClaims = new HashMap<>();\n\t\t\t\t}\n\t\t\t\tcnfClaims.put(\"jkt\", sha256Thumbprint);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\"Failed to compute SHA-256 Thumbprint for DPoP Proof PublicKey.\", null);\n\t\t\t\tthrow new OAuth2AuthenticationException(error, ex);\n\t\t\t}\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(cnfClaims)) {\n\t\t\tclaims.put(\"cnf\", cnfClaims);\n\t\t}\n\n\t\t// Add 'act' claim for delegation use case of Token Exchange Grant.\n\t\t// If more than one actor is present, we create a chain of delegation by nesting\n\t\t// \"act\" claims.\n\t\tif (tokenContext\n\t\t\t.getPrincipal() instanceof OAuth2TokenExchangeCompositeAuthenticationToken compositeAuthenticationToken) {\n\t\t\tMap<String, Object> currentClaims = claims;\n\t\t\tfor (OAuth2TokenExchangeActor actor : compositeAuthenticationToken.getActors()) {\n\t\t\t\tMap<String, Object> actorClaims = actor.getClaims();\n\t\t\t\tMap<String, Object> actClaim = new LinkedHashMap<>();\n\t\t\t\tactClaim.put(OAuth2TokenClaimNames.ISS, actorClaims.get(OAuth2TokenClaimNames.ISS));\n\t\t\t\tactClaim.put(OAuth2TokenClaimNames.SUB, actorClaims.get(OAuth2TokenClaimNames.SUB));\n\t\t\t\tcurrentClaims.put(\"act\", Collections.unmodifiableMap(actClaim));\n\t\t\t\tcurrentClaims = actClaim;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static String computeSHA256Thumbprint(X509Certificate x509Certificate) throws Exception {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(x509Certificate.getEncoded());\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationValidator;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationConsentAuthenticationConverter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Configurer for the OAuth 2.0 Authorization Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#authorizationEndpoint\n * @see OAuth2AuthorizationEndpointFilter\n */\npublic final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> authorizationRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> authorizationRequestConvertersConsumer = (\n\t\t\tauthorizationRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler authorizationResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\tprivate String consentPage;\n\n\tprivate Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationCodeRequestAuthenticationValidator;\n\n\tprivate Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationCodeRequestAuthenticationValidatorComposite;\n\n\tprivate SessionAuthenticationStrategy sessionAuthenticationStrategy;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2AuthorizationEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract an\n\t * Authorization Request (or Consent) from {@link HttpServletRequest} to an instance\n\t * of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or\n\t * {@link OAuth2AuthorizationConsentAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param authorizationRequestConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract an Authorization Request (or Consent) from\n\t * {@link HttpServletRequest}\n\t * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationEndpointConfigurer authorizationRequestConverter(\n\t\t\tAuthenticationConverter authorizationRequestConverter) {\n\t\tAssert.notNull(authorizationRequestConverter, \"authorizationRequestConverter cannot be null\");\n\t\tthis.authorizationRequestConverters.add(authorizationRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authorizationRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param authorizationRequestConvertersConsumer the {@code Consumer} providing access\n\t * to the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationEndpointConfigurer authorizationRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> authorizationRequestConvertersConsumer) {\n\t\tAssert.notNull(authorizationRequestConvertersConsumer, \"authorizationRequestConvertersConsumer cannot be null\");\n\t\tthis.authorizationRequestConvertersConsumer = authorizationRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}\n\t * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and returning the\n\t * {@link OAuth2AuthorizationResponse Authorization Response}.\n\t * @param authorizationResponseHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}\n\t * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationEndpointConfigurer authorizationResponseHandler(\n\t\t\tAuthenticationSuccessHandler authorizationResponseHandler) {\n\t\tthis.authorizationResponseHandler = authorizationResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationException} and returning the\n\t * {@link OAuth2Error Error Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException}\n\t * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationEndpointConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the URI to redirect Resource Owners to if consent is required during the\n\t * {@code authorization_code} flow. A default consent page will be generated when this\n\t * attribute is not specified.\n\t *\n\t * If a URI is specified, applications are required to process the specified URI to\n\t * generate a consent page. The query string will contain the following parameters:\n\t *\n\t * <ul>\n\t * <li>{@code client_id} - the client identifier</li>\n\t * <li>{@code scope} - a space-delimited list of scopes present in the authorization\n\t * request</li>\n\t * <li>{@code state} - a CSRF protection token</li>\n\t * </ul>\n\t *\n\t * In general, the consent page should create a form that submits a request with the\n\t * following requirements:\n\t *\n\t * <ul>\n\t * <li>It must be an HTTP POST</li>\n\t * <li>It must be submitted to\n\t * {@link AuthorizationServerSettings#getAuthorizationEndpoint()}</li>\n\t * <li>It must include the received {@code client_id} as an HTTP parameter</li>\n\t * <li>It must include the received {@code state} as an HTTP parameter</li>\n\t * <li>It must include the list of {@code scope}s the {@code Resource Owner} consented\n\t * to as an HTTP parameter</li>\n\t * </ul>\n\t * @param consentPage the URI of the custom consent page to redirect to if consent is\n\t * required (e.g. \"/oauth2/consent\")\n\t * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationEndpointConfigurer consentPage(String consentPage) {\n\t\tthis.consentPage = consentPage;\n\t\treturn this;\n\t}\n\n\tvoid addAuthorizationCodeRequestAuthenticationValidator(\n\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {\n\t\tthis.authorizationCodeRequestAuthenticationValidator = (this.authorizationCodeRequestAuthenticationValidator == null)\n\t\t\t\t? authenticationValidator\n\t\t\t\t: this.authorizationCodeRequestAuthenticationValidator.andThen(authenticationValidator);\n\t}\n\n\tvoid setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthenticationStrategy) {\n\t\tthis.sessionAuthenticationStrategy = sessionAuthenticationStrategy;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString authorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getAuthorizationEndpoint())\n\t\t\t\t: authorizationServerSettings.getAuthorizationEndpoint();\n\t\tthis.requestMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, authorizationEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, authorizationEndpointUri));\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\thttpSecurity.authenticationProvider(postProcess(authenticationProvider));\n\t\t\tif (authenticationProvider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider) {\n\t\t\t\tMethod method = ReflectionUtils.findMethod(OAuth2AuthorizationCodeRequestAuthenticationProvider.class,\n\t\t\t\t\t\t\"getAuthenticationValidatorComposite\");\n\t\t\t\tReflectionUtils.makeAccessible(method);\n\t\t\t\tthis.authorizationCodeRequestAuthenticationValidatorComposite = (Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext>) ReflectionUtils\n\t\t\t\t\t.invokeMethod(method, authenticationProvider);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString authorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getAuthorizationEndpoint())\n\t\t\t\t: authorizationServerSettings.getAuthorizationEndpoint();\n\t\tOAuth2AuthorizationEndpointFilter authorizationEndpointFilter = new OAuth2AuthorizationEndpointFilter(\n\t\t\t\tauthenticationManager, authorizationEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.authorizationRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.authorizationRequestConverters);\n\t\t}\n\t\tthis.authorizationRequestConvertersConsumer.accept(authenticationConverters);\n\t\tauthorizationEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.authorizationResponseHandler != null) {\n\t\t\tauthorizationEndpointFilter.setAuthenticationSuccessHandler(this.authorizationResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\tauthorizationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\tif (StringUtils.hasText(this.consentPage)) {\n\t\t\tauthorizationEndpointFilter.setConsentPage(this.consentPage);\n\t\t}\n\t\tif (this.sessionAuthenticationStrategy != null) {\n\t\t\tauthorizationEndpointFilter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(authorizationEndpointFilter), AuthorizationFilter.class);\n\t\t// Create and add\n\t\t// OAuth2AuthorizationEndpointFilter.OAuth2AuthorizationCodeRequestValidatingFilter\n\t\tMethod method = ReflectionUtils.findMethod(OAuth2AuthorizationEndpointFilter.class,\n\t\t\t\t\"createAuthorizationCodeRequestValidatingFilter\", RegisteredClientRepository.class, Consumer.class);\n\t\tReflectionUtils.makeAccessible(method);\n\t\tRegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils\n\t\t\t.getRegisteredClientRepository(httpSecurity);\n\t\tFilter authorizationCodeRequestValidatingFilter = (Filter) ReflectionUtils.invokeMethod(method,\n\t\t\t\tauthorizationEndpointFilter, registeredClientRepository,\n\t\t\t\tthis.authorizationCodeRequestAuthenticationValidatorComposite);\n\t\thttpSecurity.addFilterBefore(postProcess(authorizationCodeRequestValidatingFilter),\n\t\t\t\tAbstractPreAuthenticatedProcessingFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OAuth2AuthorizationCodeRequestAuthenticationConverter());\n\t\tauthenticationConverters.add(new OAuth2AuthorizationConsentAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider = new OAuth2AuthorizationCodeRequestAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity));\n\t\tif (this.authorizationCodeRequestAuthenticationValidator != null) {\n\t\t\tauthorizationCodeRequestAuthenticationProvider\n\t\t\t\t.setAuthenticationValidator(new OAuth2AuthorizationCodeRequestAuthenticationValidator()\n\t\t\t\t\t.andThen(this.authorizationCodeRequestAuthenticationValidator));\n\t\t}\n\t\tauthenticationProviders.add(authorizationCodeRequestAuthenticationProvider);\n\n\t\tOAuth2AuthorizationConsentAuthenticationProvider authorizationConsentAuthenticationProvider = new OAuth2AuthorizationConsentAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity));\n\t\tauthenticationProviders.add(authorizationConsentAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.source.JWKSource;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.GenericApplicationListenerAdapter;\nimport org.springframework.context.event.SmartApplicationListener;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;\nimport org.springframework.security.context.DelegatingApplicationListener;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.core.session.SessionRegistryImpl;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter;\nimport org.springframework.security.web.authentication.HttpStatusEntryPoint;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * An {@link AbstractHttpConfigurer} for OAuth 2.1 Authorization Server support.\n *\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n * @author Gerardo Roza\n * @author Ovidiu Popa\n * @author Gaurav Tiwari\n * @since 7.0\n * @see AbstractHttpConfigurer\n * @see OAuth2ClientAuthenticationConfigurer\n * @see OAuth2AuthorizationServerMetadataEndpointConfigurer\n * @see OAuth2AuthorizationEndpointConfigurer\n * @see OAuth2PushedAuthorizationRequestEndpointConfigurer\n * @see OAuth2TokenEndpointConfigurer\n * @see OAuth2TokenIntrospectionEndpointConfigurer\n * @see OAuth2TokenRevocationEndpointConfigurer\n * @see OAuth2DeviceAuthorizationEndpointConfigurer\n * @see OAuth2DeviceVerificationEndpointConfigurer\n * @see OAuth2ClientRegistrationEndpointConfigurer\n * @see OidcConfigurer\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see OAuth2AuthorizationConsentService\n * @see NimbusJwkSetEndpointFilter\n */\npublic final class OAuth2AuthorizationServerConfigurer\n\t\textends AbstractHttpConfigurer<OAuth2AuthorizationServerConfigurer, HttpSecurity> {\n\n\tprivate final Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = createConfigurers();\n\n\tprivate RequestMatcher endpointsMatcher;\n\n\t/**\n\t * Sets the repository of registered clients.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer registeredClientRepository(\n\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tgetBuilder().setSharedObject(RegisteredClientRepository.class, registeredClientRepository);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the authorization service.\n\t * @param authorizationService the authorization service\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer authorizationService(OAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tgetBuilder().setSharedObject(OAuth2AuthorizationService.class, authorizationService);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the authorization consent service.\n\t * @param authorizationConsentService the authorization consent service\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer authorizationConsentService(\n\t\t\tOAuth2AuthorizationConsentService authorizationConsentService) {\n\t\tAssert.notNull(authorizationConsentService, \"authorizationConsentService cannot be null\");\n\t\tgetBuilder().setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the authorization server settings.\n\t * @param authorizationServerSettings the authorization server settings\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer authorizationServerSettings(\n\t\t\tAuthorizationServerSettings authorizationServerSettings) {\n\t\tAssert.notNull(authorizationServerSettings, \"authorizationServerSettings cannot be null\");\n\t\tgetBuilder().setSharedObject(AuthorizationServerSettings.class, authorizationServerSettings);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the token generator.\n\t * @param tokenGenerator the token generator\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer tokenGenerator(\n\t\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {\n\t\tAssert.notNull(tokenGenerator, \"tokenGenerator cannot be null\");\n\t\tgetBuilder().setSharedObject(OAuth2TokenGenerator.class, tokenGenerator);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures OAuth 2.0 Client Authentication.\n\t * @param clientAuthenticationCustomizer the {@link Customizer} providing access to\n\t * the {@link OAuth2ClientAuthenticationConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer clientAuthentication(\n\t\t\tCustomizer<OAuth2ClientAuthenticationConfigurer> clientAuthenticationCustomizer) {\n\t\tclientAuthenticationCustomizer.customize(getConfigurer(OAuth2ClientAuthenticationConfigurer.class));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Authorization Server Metadata Endpoint.\n\t * @param authorizationServerMetadataEndpointCustomizer the {@link Customizer}\n\t * providing access to the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer authorizationServerMetadataEndpoint(\n\t\t\tCustomizer<OAuth2AuthorizationServerMetadataEndpointConfigurer> authorizationServerMetadataEndpointCustomizer) {\n\t\tauthorizationServerMetadataEndpointCustomizer\n\t\t\t.customize(getConfigurer(OAuth2AuthorizationServerMetadataEndpointConfigurer.class));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Authorization Endpoint.\n\t * @param authorizationEndpointCustomizer the {@link Customizer} providing access to\n\t * the {@link OAuth2AuthorizationEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer authorizationEndpoint(\n\t\t\tCustomizer<OAuth2AuthorizationEndpointConfigurer> authorizationEndpointCustomizer) {\n\t\tauthorizationEndpointCustomizer.customize(getConfigurer(OAuth2AuthorizationEndpointConfigurer.class));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Pushed Authorization Request Endpoint.\n\t * @param pushedAuthorizationRequestEndpointCustomizer the {@link Customizer}\n\t * providing access to the {@link OAuth2PushedAuthorizationRequestEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer pushedAuthorizationRequestEndpoint(\n\t\t\tCustomizer<OAuth2PushedAuthorizationRequestEndpointConfigurer> pushedAuthorizationRequestEndpointCustomizer) {\n\t\tOAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationRequestEndpointConfigurer = getConfigurer(\n\t\t\t\tOAuth2PushedAuthorizationRequestEndpointConfigurer.class);\n\t\tif (pushedAuthorizationRequestEndpointConfigurer == null) {\n\t\t\taddConfigurer(OAuth2PushedAuthorizationRequestEndpointConfigurer.class,\n\t\t\t\t\tnew OAuth2PushedAuthorizationRequestEndpointConfigurer(this::postProcess));\n\t\t\tpushedAuthorizationRequestEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOAuth2PushedAuthorizationRequestEndpointConfigurer.class);\n\t\t}\n\t\tpushedAuthorizationRequestEndpointCustomizer.customize(pushedAuthorizationRequestEndpointConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Token Endpoint.\n\t * @param tokenEndpointCustomizer the {@link Customizer} providing access to the\n\t * {@link OAuth2TokenEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer tokenEndpoint(\n\t\t\tCustomizer<OAuth2TokenEndpointConfigurer> tokenEndpointCustomizer) {\n\t\ttokenEndpointCustomizer.customize(getConfigurer(OAuth2TokenEndpointConfigurer.class));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Token Introspection Endpoint.\n\t * @param tokenIntrospectionEndpointCustomizer the {@link Customizer} providing access\n\t * to the {@link OAuth2TokenIntrospectionEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer tokenIntrospectionEndpoint(\n\t\t\tCustomizer<OAuth2TokenIntrospectionEndpointConfigurer> tokenIntrospectionEndpointCustomizer) {\n\t\ttokenIntrospectionEndpointCustomizer.customize(getConfigurer(OAuth2TokenIntrospectionEndpointConfigurer.class));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Token Revocation Endpoint.\n\t * @param tokenRevocationEndpointCustomizer the {@link Customizer} providing access to\n\t * the {@link OAuth2TokenRevocationEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(\n\t\t\tCustomizer<OAuth2TokenRevocationEndpointConfigurer> tokenRevocationEndpointCustomizer) {\n\t\ttokenRevocationEndpointCustomizer.customize(getConfigurer(OAuth2TokenRevocationEndpointConfigurer.class));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Device Authorization Endpoint.\n\t * @param deviceAuthorizationEndpointCustomizer the {@link Customizer} providing\n\t * access to the {@link OAuth2DeviceAuthorizationEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer deviceAuthorizationEndpoint(\n\t\t\tCustomizer<OAuth2DeviceAuthorizationEndpointConfigurer> deviceAuthorizationEndpointCustomizer) {\n\t\tOAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer = getConfigurer(\n\t\t\t\tOAuth2DeviceAuthorizationEndpointConfigurer.class);\n\t\tif (deviceAuthorizationEndpointConfigurer == null) {\n\t\t\taddConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class,\n\t\t\t\t\tnew OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess));\n\t\t\tdeviceAuthorizationEndpointConfigurer = getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class);\n\t\t\tdeviceVerificationEndpoint((configurer) -> {\n\t\t\t}); // Ensure the Device Verification Endpoint is enabled\n\t\t}\n\t\tdeviceAuthorizationEndpointCustomizer.customize(deviceAuthorizationEndpointConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Device Verification Endpoint.\n\t * @param deviceVerificationEndpointCustomizer the {@link Customizer} providing access\n\t * to the {@link OAuth2DeviceVerificationEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint(\n\t\t\tCustomizer<OAuth2DeviceVerificationEndpointConfigurer> deviceVerificationEndpointCustomizer) {\n\t\tOAuth2DeviceVerificationEndpointConfigurer deviceVerificationEndpointConfigurer = getConfigurer(\n\t\t\t\tOAuth2DeviceVerificationEndpointConfigurer.class);\n\t\tif (deviceVerificationEndpointConfigurer == null) {\n\t\t\taddConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class,\n\t\t\t\t\tnew OAuth2DeviceVerificationEndpointConfigurer(this::postProcess));\n\t\t\tdeviceVerificationEndpointConfigurer = getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class);\n\t\t\tdeviceAuthorizationEndpoint((configurer) -> {\n\t\t\t}); // Ensure the Device Authorization Endpoint is enabled\n\t\t}\n\t\tdeviceVerificationEndpointCustomizer.customize(deviceVerificationEndpointConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth 2.0 Dynamic Client Registration Endpoint.\n\t * @param clientRegistrationEndpointCustomizer the {@link Customizer} providing access\n\t * to the {@link OAuth2ClientRegistrationEndpointConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer clientRegistrationEndpoint(\n\t\t\tCustomizer<OAuth2ClientRegistrationEndpointConfigurer> clientRegistrationEndpointCustomizer) {\n\t\tOAuth2ClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer(\n\t\t\t\tOAuth2ClientRegistrationEndpointConfigurer.class);\n\t\tif (clientRegistrationEndpointConfigurer == null) {\n\t\t\taddConfigurer(OAuth2ClientRegistrationEndpointConfigurer.class,\n\t\t\t\t\tnew OAuth2ClientRegistrationEndpointConfigurer(this::postProcess));\n\t\t\tclientRegistrationEndpointConfigurer = getConfigurer(OAuth2ClientRegistrationEndpointConfigurer.class);\n\t\t}\n\t\tclientRegistrationEndpointCustomizer.customize(clientRegistrationEndpointConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures OpenID Connect 1.0 support (disabled by default).\n\t * @param oidcCustomizer the {@link Customizer} providing access to the\n\t * {@link OidcConfigurer}\n\t * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration\n\t */\n\tpublic OAuth2AuthorizationServerConfigurer oidc(Customizer<OidcConfigurer> oidcCustomizer) {\n\t\tOidcConfigurer oidcConfigurer = getConfigurer(OidcConfigurer.class);\n\t\tif (oidcConfigurer == null) {\n\t\t\taddConfigurer(OidcConfigurer.class, new OidcConfigurer(this::postProcess));\n\t\t\toidcConfigurer = getConfigurer(OidcConfigurer.class);\n\t\t}\n\t\toidcCustomizer.customize(oidcConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Returns a {@link RequestMatcher} for the authorization server endpoints.\n\t * @return a {@link RequestMatcher} for the authorization server endpoints\n\t */\n\tpublic RequestMatcher getEndpointsMatcher() {\n\t\t// Return a deferred RequestMatcher\n\t\t// since endpointsMatcher is constructed in init(HttpSecurity).\n\t\treturn (request) -> this.endpointsMatcher.matches(request);\n\t}\n\n\t@Override\n\tpublic void init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tvalidateAuthorizationServerSettings(authorizationServerSettings);\n\n\t\tif (isOidcEnabled()) {\n\t\t\t// Add OpenID Connect session tracking capabilities.\n\t\t\tinitSessionRegistry(httpSecurity);\n\t\t\tSessionRegistry sessionRegistry = httpSecurity.getSharedObject(SessionRegistry.class);\n\t\t\tOAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOAuth2AuthorizationEndpointConfigurer.class);\n\t\t\tauthorizationEndpointConfigurer.setSessionAuthenticationStrategy((authentication, request, response) -> {\n\t\t\t\tif (authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) {\n\t\t\t\t\tif (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {\n\t\t\t\t\t\tif (sessionRegistry.getSessionInformation(request.getSession().getId()) == null) {\n\t\t\t\t\t\t\tsessionRegistry.registerNewSession(request.getSession().getId(),\n\t\t\t\t\t\t\t\t\t((Authentication) authorizationCodeRequestAuthentication.getPrincipal())\n\t\t\t\t\t\t\t\t\t\t.getPrincipal());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t// OpenID Connect is disabled.\n\t\t\t// Add an authentication validator that rejects authentication requests.\n\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> oidcAuthenticationRequestValidator = (\n\t\t\t\t\tauthenticationContext) -> {\n\t\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext\n\t\t\t\t\t.getAuthentication();\n\t\t\t\tif (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {\n\t\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE,\n\t\t\t\t\t\t\t\"OpenID Connect 1.0 authentication requests are restricted.\",\n\t\t\t\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1\");\n\t\t\t\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error,\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthentication);\n\t\t\t\t}\n\t\t\t};\n\t\t\tOAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOAuth2AuthorizationEndpointConfigurer.class);\n\t\t\tauthorizationEndpointConfigurer\n\t\t\t\t.addAuthorizationCodeRequestAuthenticationValidator(oidcAuthenticationRequestValidator);\n\t\t\tOAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationRequestEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOAuth2PushedAuthorizationRequestEndpointConfigurer.class);\n\t\t\tif (pushedAuthorizationRequestEndpointConfigurer != null) {\n\t\t\t\tpushedAuthorizationRequestEndpointConfigurer\n\t\t\t\t\t.addAuthorizationCodeRequestAuthenticationValidator(oidcAuthenticationRequestValidator);\n\t\t\t}\n\t\t}\n\n\t\tList<RequestMatcher> requestMatchers = new ArrayList<>();\n\t\tthis.configurers.values().forEach((configurer) -> {\n\t\t\tconfigurer.init(httpSecurity);\n\t\t\trequestMatchers.add(configurer.getRequestMatcher());\n\t\t});\n\t\tString jwkSetEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils.withMultipleIssuersPattern(authorizationServerSettings.getJwkSetEndpoint())\n\t\t\t\t: authorizationServerSettings.getJwkSetEndpoint();\n\t\trequestMatchers.add(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, jwkSetEndpointUri));\n\t\tthis.endpointsMatcher = new OrRequestMatcher(requestMatchers);\n\n\t\tExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = httpSecurity\n\t\t\t.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptionHandling != null) {\n\t\t\tList<RequestMatcher> preferredMatchers = new ArrayList<>();\n\t\t\tpreferredMatchers.add(getRequestMatcher(OAuth2TokenEndpointConfigurer.class));\n\t\t\tpreferredMatchers.add(getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class));\n\t\t\tpreferredMatchers.add(getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class));\n\t\t\tRequestMatcher preferredMatcher = getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class);\n\t\t\tif (preferredMatcher != null) {\n\t\t\t\tpreferredMatchers.add(preferredMatcher);\n\t\t\t}\n\t\t\tpreferredMatcher = getRequestMatcher(OAuth2PushedAuthorizationRequestEndpointConfigurer.class);\n\t\t\tif (preferredMatcher != null) {\n\t\t\t\tpreferredMatchers.add(preferredMatcher);\n\t\t\t}\n\t\t\texceptionHandling.defaultAuthenticationEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),\n\t\t\t\t\tnew OrRequestMatcher(preferredMatchers));\n\t\t}\n\n\t\thttpSecurity.csrf((csrf) -> csrf.ignoringRequestMatchers(this.endpointsMatcher));\n\n\t\tif (getConfigurer(OAuth2ClientRegistrationEndpointConfigurer.class) != null) {\n\t\t\thttpSecurity\n\t\t\t\t// Accept access tokens for Client Registration\n\t\t\t\t.oauth2ResourceServer((oauth2ResourceServer) -> oauth2ResourceServer.jwt(Customizer.withDefaults()));\n\t\t}\n\n\t\tOidcConfigurer oidcConfigurer = getConfigurer(OidcConfigurer.class);\n\t\tif (oidcConfigurer != null) {\n\t\t\tif (oidcConfigurer.getConfigurer(OidcUserInfoEndpointConfigurer.class) != null\n\t\t\t\t\t|| oidcConfigurer.getConfigurer(OidcClientRegistrationEndpointConfigurer.class) != null) {\n\t\t\t\thttpSecurity\n\t\t\t\t\t// Accept access tokens for User Info and/or Client Registration\n\t\t\t\t\t.oauth2ResourceServer(\n\t\t\t\t\t\t\t(oauth2ResourceServer) -> oauth2ResourceServer.jwt(Customizer.withDefaults()));\n\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void configure(HttpSecurity httpSecurity) {\n\t\tOAuth2ClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer(\n\t\t\t\tOAuth2ClientRegistrationEndpointConfigurer.class);\n\t\tif (clientRegistrationEndpointConfigurer != null) {\n\t\t\tOAuth2AuthorizationServerMetadataEndpointConfigurer authorizationServerMetadataEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOAuth2AuthorizationServerMetadataEndpointConfigurer.class);\n\n\t\t\tauthorizationServerMetadataEndpointConfigurer.addDefaultAuthorizationServerMetadataCustomizer((builder) -> {\n\t\t\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\t\t\tString issuer = authorizationServerContext.getIssuer();\n\t\t\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t\t\t.getAuthorizationServerSettings();\n\n\t\t\t\tString clientRegistrationEndpoint = UriComponentsBuilder.fromUriString(issuer)\n\t\t\t\t\t.path(authorizationServerSettings.getClientRegistrationEndpoint())\n\t\t\t\t\t.build()\n\t\t\t\t\t.toUriString();\n\n\t\t\t\tbuilder.clientRegistrationEndpoint(clientRegistrationEndpoint);\n\t\t\t});\n\t\t}\n\n\t\tOAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer = getConfigurer(\n\t\t\t\tOAuth2DeviceAuthorizationEndpointConfigurer.class);\n\t\tif (deviceAuthorizationEndpointConfigurer != null) {\n\t\t\tOAuth2AuthorizationServerMetadataEndpointConfigurer authorizationServerMetadataEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOAuth2AuthorizationServerMetadataEndpointConfigurer.class);\n\n\t\t\tauthorizationServerMetadataEndpointConfigurer.addDefaultAuthorizationServerMetadataCustomizer((builder) -> {\n\t\t\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\t\t\tString issuer = authorizationServerContext.getIssuer();\n\t\t\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t\t\t.getAuthorizationServerSettings();\n\n\t\t\t\tString deviceAuthorizationEndpoint = UriComponentsBuilder.fromUriString(issuer)\n\t\t\t\t\t.path(authorizationServerSettings.getDeviceAuthorizationEndpoint())\n\t\t\t\t\t.build()\n\t\t\t\t\t.toUriString();\n\n\t\t\t\tbuilder.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint);\n\t\t\t\tbuilder.grantType(AuthorizationGrantType.DEVICE_CODE.getValue());\n\t\t\t});\n\t\t}\n\n\t\tOAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationRequestEndpointConfigurer = getConfigurer(\n\t\t\t\tOAuth2PushedAuthorizationRequestEndpointConfigurer.class);\n\t\tif (pushedAuthorizationRequestEndpointConfigurer != null) {\n\t\t\tOAuth2AuthorizationServerMetadataEndpointConfigurer authorizationServerMetadataEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOAuth2AuthorizationServerMetadataEndpointConfigurer.class);\n\n\t\t\tauthorizationServerMetadataEndpointConfigurer.addDefaultAuthorizationServerMetadataCustomizer((builder) -> {\n\t\t\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\t\t\tString issuer = authorizationServerContext.getIssuer();\n\t\t\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t\t\t.getAuthorizationServerSettings();\n\n\t\t\t\tString pushedAuthorizationRequestEndpoint = UriComponentsBuilder.fromUriString(issuer)\n\t\t\t\t\t.path(authorizationServerSettings.getPushedAuthorizationRequestEndpoint())\n\t\t\t\t\t.build()\n\t\t\t\t\t.toUriString();\n\n\t\t\t\tbuilder.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint);\n\t\t\t});\n\t\t}\n\n\t\tthis.configurers.values().forEach((configurer) -> configurer.configure(httpSecurity));\n\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\n\t\tAuthorizationServerContextFilter authorizationServerContextFilter = new AuthorizationServerContextFilter(\n\t\t\t\tauthorizationServerSettings);\n\t\thttpSecurity.addFilterAfter(postProcess(authorizationServerContextFilter), SecurityContextHolderFilter.class);\n\n\t\tJWKSource<com.nimbusds.jose.proc.SecurityContext> jwkSource = OAuth2ConfigurerUtils.getJwkSource(httpSecurity);\n\t\tif (jwkSource != null) {\n\t\t\tString jwkSetEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t\t? OAuth2ConfigurerUtils.withMultipleIssuersPattern(authorizationServerSettings.getJwkSetEndpoint())\n\t\t\t\t\t: authorizationServerSettings.getJwkSetEndpoint();\n\t\t\tNimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(jwkSource,\n\t\t\t\t\tjwkSetEndpointUri);\n\t\t\thttpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter),\n\t\t\t\t\tAbstractPreAuthenticatedProcessingFilter.class);\n\t\t}\n\t}\n\n\tprivate boolean isOidcEnabled() {\n\t\treturn getConfigurer(OidcConfigurer.class) != null;\n\t}\n\n\tprivate Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> createConfigurers() {\n\t\tMap<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();\n\t\tconfigurers.put(OAuth2ClientAuthenticationConfigurer.class,\n\t\t\t\tnew OAuth2ClientAuthenticationConfigurer(this::postProcess));\n\t\tconfigurers.put(OAuth2AuthorizationServerMetadataEndpointConfigurer.class,\n\t\t\t\tnew OAuth2AuthorizationServerMetadataEndpointConfigurer(this::postProcess));\n\t\tconfigurers.put(OAuth2AuthorizationEndpointConfigurer.class,\n\t\t\t\tnew OAuth2AuthorizationEndpointConfigurer(this::postProcess));\n\t\tconfigurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess));\n\t\tconfigurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class,\n\t\t\t\tnew OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess));\n\t\tconfigurers.put(OAuth2TokenRevocationEndpointConfigurer.class,\n\t\t\t\tnew OAuth2TokenRevocationEndpointConfigurer(this::postProcess));\n\t\treturn configurers;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t<T> T getConfigurer(Class<T> type) {\n\t\treturn (T) this.configurers.get(type);\n\t}\n\n\tprivate <T extends AbstractOAuth2Configurer> void addConfigurer(Class<T> configurerType, T configurer) {\n\t\tthis.configurers.put(configurerType, configurer);\n\t}\n\n\tprivate <T extends AbstractOAuth2Configurer> RequestMatcher getRequestMatcher(Class<T> configurerType) {\n\t\tT configurer = getConfigurer(configurerType);\n\t\treturn (configurer != null) ? configurer.getRequestMatcher() : null;\n\t}\n\n\tprivate static void validateAuthorizationServerSettings(AuthorizationServerSettings authorizationServerSettings) {\n\t\tif (authorizationServerSettings.getIssuer() != null) {\n\t\t\tURI issuerUri;\n\t\t\ttry {\n\t\t\t\tissuerUri = new URI(authorizationServerSettings.getIssuer());\n\t\t\t\tissuerUri.toURL();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(\"issuer must be a valid URL\", ex);\n\t\t\t}\n\t\t\t// rfc8414 https://datatracker.ietf.org/doc/html/rfc8414#section-2\n\t\t\tif (issuerUri.getQuery() != null || issuerUri.getFragment() != null) {\n\t\t\t\tthrow new IllegalArgumentException(\"issuer cannot contain query or fragment component\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void initSessionRegistry(HttpSecurity httpSecurity) {\n\t\tSessionRegistry sessionRegistry = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, SessionRegistry.class);\n\t\tif (sessionRegistry == null) {\n\t\t\tsessionRegistry = new SessionRegistryImpl();\n\t\t\tregisterDelegateApplicationListener(httpSecurity, (SessionRegistryImpl) sessionRegistry);\n\t\t}\n\t\thttpSecurity.setSharedObject(SessionRegistry.class, sessionRegistry);\n\t}\n\n\tprivate static void registerDelegateApplicationListener(HttpSecurity httpSecurity,\n\t\t\tApplicationListener<?> delegate) {\n\t\tDelegatingApplicationListener delegatingApplicationListener = OAuth2ConfigurerUtils\n\t\t\t.getOptionalBean(httpSecurity, DelegatingApplicationListener.class);\n\t\tif (delegatingApplicationListener == null) {\n\t\t\treturn;\n\t\t}\n\t\tSmartApplicationListener smartListener = new GenericApplicationListenerAdapter(delegate);\n\t\tdelegatingApplicationListener.addListener(smartListener);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerMetadataEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.function.Consumer;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Configurer for the OAuth 2.0 Authorization Server Metadata Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#authorizationServerMetadataEndpoint\n * @see OAuth2AuthorizationServerMetadataEndpointFilter\n */\npublic final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer;\n\n\tprivate Consumer<OAuth2AuthorizationServerMetadata.Builder> defaultAuthorizationServerMetadataCustomizer;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2AuthorizationServerMetadataEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationServerMetadata.Builder} allowing the ability to customize\n\t * the claims of the Authorization Server's configuration.\n\t * @param authorizationServerMetadataCustomizer the {@code Consumer} providing access\n\t * to the {@link OAuth2AuthorizationServerMetadata.Builder}\n\t * @return the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2AuthorizationServerMetadataEndpointConfigurer authorizationServerMetadataCustomizer(\n\t\t\tConsumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer) {\n\t\tthis.authorizationServerMetadataCustomizer = authorizationServerMetadataCustomizer;\n\t\treturn this;\n\t}\n\n\tvoid addDefaultAuthorizationServerMetadataCustomizer(\n\t\t\tConsumer<OAuth2AuthorizationServerMetadata.Builder> defaultAuthorizationServerMetadataCustomizer) {\n\t\tthis.defaultAuthorizationServerMetadataCustomizer = (this.defaultAuthorizationServerMetadataCustomizer == null)\n\t\t\t\t? defaultAuthorizationServerMetadataCustomizer : this.defaultAuthorizationServerMetadataCustomizer\n\t\t\t\t\t.andThen(defaultAuthorizationServerMetadataCustomizer);\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString authorizationServerMetadataEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? \"/.well-known/oauth-authorization-server/**\" : \"/.well-known/oauth-authorization-server\";\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, authorizationServerMetadataEndpointUri);\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tOAuth2AuthorizationServerMetadataEndpointFilter authorizationServerMetadataEndpointFilter = new OAuth2AuthorizationServerMetadataEndpointFilter();\n\t\tConsumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer = getAuthorizationServerMetadataCustomizer();\n\t\tif (authorizationServerMetadataCustomizer != null) {\n\t\t\tauthorizationServerMetadataEndpointFilter\n\t\t\t\t.setAuthorizationServerMetadataCustomizer(authorizationServerMetadataCustomizer);\n\t\t}\n\t\thttpSecurity.addFilterBefore(postProcess(authorizationServerMetadataEndpointFilter),\n\t\t\t\tAbstractPreAuthenticatedProcessingFilter.class);\n\t}\n\n\tprivate Consumer<OAuth2AuthorizationServerMetadata.Builder> getAuthorizationServerMetadataCustomizer() {\n\t\tConsumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer = null;\n\t\tif (this.defaultAuthorizationServerMetadataCustomizer != null\n\t\t\t\t|| this.authorizationServerMetadataCustomizer != null) {\n\t\t\tif (this.defaultAuthorizationServerMetadataCustomizer != null) {\n\t\t\t\tauthorizationServerMetadataCustomizer = this.defaultAuthorizationServerMetadataCustomizer;\n\t\t\t}\n\t\t\tif (this.authorizationServerMetadataCustomizer != null) {\n\t\t\t\tauthorizationServerMetadataCustomizer = (authorizationServerMetadataCustomizer != null)\n\t\t\t\t\t\t? authorizationServerMetadataCustomizer.andThen(this.authorizationServerMetadataCustomizer)\n\t\t\t\t\t\t: this.authorizationServerMetadataCustomizer;\n\t\t\t}\n\t\t}\n\t\treturn authorizationServerMetadataCustomizer;\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ClientAuthenticationConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.authentication.ClientSecretAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.JwtClientAssertionAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.PublicClientAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.X509ClientCertificateAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretBasicAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretPostAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.JwtClientAssertionAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.PublicClientAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.X509ClientCertificateAuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for OAuth 2.0 Client Authentication.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#clientAuthentication\n * @see OAuth2ClientAuthenticationFilter\n */\npublic final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer = (authenticationConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2ClientAuthenticationConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract client\n\t * credentials from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2ClientAuthenticationToken} used for authenticating the client.\n\t * @param authenticationConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract client credentials from {@link HttpServletRequest}\n\t * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientAuthenticationConfigurer authenticationConverter(\n\t\t\tAuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverters.add(authenticationConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param authenticationConvertersConsumer the {@code Consumer} providing access to\n\t * the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientAuthenticationConfigurer authenticationConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> authenticationConvertersConsumer) {\n\t\tAssert.notNull(authenticationConvertersConsumer, \"authenticationConvertersConsumer cannot be null\");\n\t\tthis.authenticationConvertersConsumer = authenticationConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OAuth2ClientAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OAuth2ClientAuthenticationToken}\n\t * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientAuthenticationConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientAuthenticationConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling a successful client\n\t * authentication and associating the {@link OAuth2ClientAuthenticationToken} to the\n\t * {@link SecurityContext}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling a successful client authentication\n\t * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientAuthenticationConfigurer authenticationSuccessHandler(\n\t\t\tAuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling a failed client\n\t * authentication and returning the {@link OAuth2Error Error Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling a failed client authentication\n\t * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration\n\t */\n\tpublic OAuth2ClientAuthenticationConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils.withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenEndpoint();\n\t\tString tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenIntrospectionEndpoint();\n\t\tString tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenRevocationEndpoint();\n\t\tString deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint())\n\t\t\t\t: authorizationServerSettings.getDeviceAuthorizationEndpoint();\n\t\tString pushedAuthorizationRequestEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getPushedAuthorizationRequestEndpoint())\n\t\t\t\t: authorizationServerSettings.getPushedAuthorizationRequestEndpoint();\n\t\tthis.requestMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, tokenEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, tokenIntrospectionEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, tokenRevocationEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, deviceAuthorizationEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults()\n\t\t\t\t\t.matcher(HttpMethod.POST, pushedAuthorizationRequestEndpointUri));\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tOAuth2ClientAuthenticationFilter clientAuthenticationFilter = new OAuth2ClientAuthenticationFilter(\n\t\t\t\tauthenticationManager, this.requestMatcher);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.authenticationConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.authenticationConverters);\n\t\t}\n\t\tthis.authenticationConvertersConsumer.accept(authenticationConverters);\n\t\tclientAuthenticationFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.authenticationSuccessHandler != null) {\n\t\t\tclientAuthenticationFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\tclientAuthenticationFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(clientAuthenticationFilter),\n\t\t\t\tAbstractPreAuthenticatedProcessingFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new JwtClientAssertionAuthenticationConverter());\n\t\tauthenticationConverters.add(new ClientSecretBasicAuthenticationConverter());\n\t\tauthenticationConverters.add(new ClientSecretPostAuthenticationConverter());\n\t\tauthenticationConverters.add(new PublicClientAuthenticationConverter());\n\t\tauthenticationConverters.add(new X509ClientCertificateAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tRegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils\n\t\t\t.getRegisteredClientRepository(httpSecurity);\n\t\tOAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity);\n\n\t\tJwtClientAssertionAuthenticationProvider jwtClientAssertionAuthenticationProvider = new JwtClientAssertionAuthenticationProvider(\n\t\t\t\tregisteredClientRepository, authorizationService);\n\t\tauthenticationProviders.add(jwtClientAssertionAuthenticationProvider);\n\n\t\tX509ClientCertificateAuthenticationProvider x509ClientCertificateAuthenticationProvider = new X509ClientCertificateAuthenticationProvider(\n\t\t\t\tregisteredClientRepository, authorizationService);\n\t\tauthenticationProviders.add(x509ClientCertificateAuthenticationProvider);\n\n\t\tClientSecretAuthenticationProvider clientSecretAuthenticationProvider = new ClientSecretAuthenticationProvider(\n\t\t\t\tregisteredClientRepository, authorizationService);\n\t\tPasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class);\n\t\tif (passwordEncoder != null) {\n\t\t\tclientSecretAuthenticationProvider.setPasswordEncoder(passwordEncoder);\n\t\t}\n\t\tauthenticationProviders.add(clientSecretAuthenticationProvider);\n\n\t\tPublicClientAuthenticationProvider publicClientAuthenticationProvider = new PublicClientAuthenticationProvider(\n\t\t\t\tregisteredClientRepository, authorizationService);\n\t\tauthenticationProviders.add(publicClientAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ClientRegistrationEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2ClientRegistrationEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientRegistrationAuthenticationConverter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for OAuth 2.0 Dynamic Client Registration Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#clientRegistrationEndpoint\n * @see OAuth2ClientRegistrationEndpointFilter\n */\npublic final class OAuth2ClientRegistrationEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> clientRegistrationRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer = (\n\t\t\tclientRegistrationRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler clientRegistrationResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\tprivate boolean openRegistrationAllowed;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2ClientRegistrationEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract a Client\n\t * Registration Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2ClientRegistrationAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used\n\t * when attempting to extract a Client Registration Request from\n\t * {@link HttpServletRequest}\n\t * @return the {@link OAuth2ClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2ClientRegistrationEndpointConfigurer clientRegistrationRequestConverter(\n\t\t\tAuthenticationConverter clientRegistrationRequestConverter) {\n\t\tAssert.notNull(clientRegistrationRequestConverter, \"clientRegistrationRequestConverter cannot be null\");\n\t\tthis.clientRegistrationRequestConverters.add(clientRegistrationRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added\n\t * {@link #clientRegistrationRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param clientRegistrationRequestConvertersConsumer the {@code Consumer} providing\n\t * access to the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2ClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2ClientRegistrationEndpointConfigurer clientRegistrationRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer) {\n\t\tAssert.notNull(clientRegistrationRequestConvertersConsumer,\n\t\t\t\t\"clientRegistrationRequestConvertersConsumer cannot be null\");\n\t\tthis.clientRegistrationRequestConvertersConsumer = clientRegistrationRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OAuth2ClientRegistrationAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OAuth2ClientRegistrationAuthenticationToken}\n\t * @return the {@link OAuth2ClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2ClientRegistrationEndpointConfigurer authenticationProvider(\n\t\t\tAuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2ClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2ClientRegistrationEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2ClientRegistrationAuthenticationToken} and returning the\n\t * {@link OAuth2ClientRegistration Client Registration Response}.\n\t * @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler}\n\t * used for handling an {@link OAuth2ClientRegistrationAuthenticationToken}\n\t * @return the {@link OAuth2ClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2ClientRegistrationEndpointConfigurer clientRegistrationResponseHandler(\n\t\t\tAuthenticationSuccessHandler clientRegistrationResponseHandler) {\n\t\tthis.clientRegistrationResponseHandler = clientRegistrationResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OAuth2ClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2ClientRegistrationEndpointConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set to {@code true} if open client registration (with no initial access token) is\n\t * allowed. The default is {@code false}.\n\t * @param openRegistrationAllowed {@code true} if open client registration is allowed,\n\t * {@code false} otherwise\n\t * @return the {@link OAuth2ClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2ClientRegistrationEndpointConfigurer openRegistrationAllowed(boolean openRegistrationAllowed) {\n\t\tthis.openRegistrationAllowed = openRegistrationAllowed;\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString clientRegistrationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getClientRegistrationEndpoint())\n\t\t\t\t: authorizationServerSettings.getClientRegistrationEndpoint();\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, clientRegistrationEndpointUri);\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity,\n\t\t\t\tthis.openRegistrationAllowed);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\n\t\tString clientRegistrationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getClientRegistrationEndpoint())\n\t\t\t\t: authorizationServerSettings.getClientRegistrationEndpoint();\n\t\tOAuth2ClientRegistrationEndpointFilter clientRegistrationEndpointFilter = new OAuth2ClientRegistrationEndpointFilter(\n\t\t\t\tauthenticationManager, clientRegistrationEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.clientRegistrationRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.clientRegistrationRequestConverters);\n\t\t}\n\t\tthis.clientRegistrationRequestConvertersConsumer.accept(authenticationConverters);\n\t\tclientRegistrationEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.clientRegistrationResponseHandler != null) {\n\t\t\tclientRegistrationEndpointFilter.setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\tclientRegistrationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(clientRegistrationEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OAuth2ClientRegistrationAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity,\n\t\t\tboolean openRegistrationAllowed) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOAuth2ClientRegistrationAuthenticationProvider clientRegistrationAuthenticationProvider = new OAuth2ClientRegistrationAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));\n\t\tPasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class);\n\t\tif (passwordEncoder != null) {\n\t\t\tclientRegistrationAuthenticationProvider.setPasswordEncoder(passwordEncoder);\n\t\t}\n\t\tclientRegistrationAuthenticationProvider.setOpenRegistrationAllowed(openRegistrationAllowed);\n\t\tauthenticationProviders.add(clientRegistrationAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ConfigurerUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.JwtGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\n\n/**\n * Utility methods for the OAuth 2.0 Configurers.\n *\n * @author Joe Grandja\n * @since 7.0\n */\nfinal class OAuth2ConfigurerUtils {\n\n\tprivate OAuth2ConfigurerUtils() {\n\t}\n\n\tstatic String withMultipleIssuersPattern(String endpointUri) {\n\t\tAssert.hasText(endpointUri, \"endpointUri cannot be empty\");\n\t\treturn endpointUri.startsWith(\"/\") ? \"/**\" + endpointUri : \"/**/\" + endpointUri;\n\t}\n\n\tstatic RegisteredClientRepository getRegisteredClientRepository(HttpSecurity httpSecurity) {\n\t\tRegisteredClientRepository registeredClientRepository = httpSecurity\n\t\t\t.getSharedObject(RegisteredClientRepository.class);\n\t\tif (registeredClientRepository == null) {\n\t\t\tregisteredClientRepository = getBean(httpSecurity, RegisteredClientRepository.class);\n\t\t\thttpSecurity.setSharedObject(RegisteredClientRepository.class, registeredClientRepository);\n\t\t}\n\t\treturn registeredClientRepository;\n\t}\n\n\tstatic OAuth2AuthorizationService getAuthorizationService(HttpSecurity httpSecurity) {\n\t\tOAuth2AuthorizationService authorizationService = httpSecurity\n\t\t\t.getSharedObject(OAuth2AuthorizationService.class);\n\t\tif (authorizationService == null) {\n\t\t\tauthorizationService = getOptionalBean(httpSecurity, OAuth2AuthorizationService.class);\n\t\t\tif (authorizationService == null) {\n\t\t\t\tauthorizationService = new InMemoryOAuth2AuthorizationService();\n\t\t\t}\n\t\t\thttpSecurity.setSharedObject(OAuth2AuthorizationService.class, authorizationService);\n\t\t}\n\t\treturn authorizationService;\n\t}\n\n\tstatic OAuth2AuthorizationConsentService getAuthorizationConsentService(HttpSecurity httpSecurity) {\n\t\tOAuth2AuthorizationConsentService authorizationConsentService = httpSecurity\n\t\t\t.getSharedObject(OAuth2AuthorizationConsentService.class);\n\t\tif (authorizationConsentService == null) {\n\t\t\tauthorizationConsentService = getOptionalBean(httpSecurity, OAuth2AuthorizationConsentService.class);\n\t\t\tif (authorizationConsentService == null) {\n\t\t\t\tauthorizationConsentService = new InMemoryOAuth2AuthorizationConsentService();\n\t\t\t}\n\t\t\thttpSecurity.setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService);\n\t\t}\n\t\treturn authorizationConsentService;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tstatic OAuth2TokenGenerator<? extends OAuth2Token> getTokenGenerator(HttpSecurity httpSecurity) {\n\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = httpSecurity\n\t\t\t.getSharedObject(OAuth2TokenGenerator.class);\n\t\tif (tokenGenerator == null) {\n\t\t\ttokenGenerator = getOptionalBean(httpSecurity, OAuth2TokenGenerator.class);\n\t\t\tif (tokenGenerator == null) {\n\t\t\t\tJwtGenerator jwtGenerator = getJwtGenerator(httpSecurity);\n\t\t\t\tOAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();\n\t\t\t\taccessTokenGenerator.setAccessTokenCustomizer(getAccessTokenCustomizer(httpSecurity));\n\t\t\t\tOAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();\n\t\t\t\tif (jwtGenerator != null) {\n\t\t\t\t\ttokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator,\n\t\t\t\t\t\t\trefreshTokenGenerator);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\ttokenGenerator = new DelegatingOAuth2TokenGenerator(accessTokenGenerator, refreshTokenGenerator);\n\t\t\t\t}\n\t\t\t}\n\t\t\thttpSecurity.setSharedObject(OAuth2TokenGenerator.class, tokenGenerator);\n\t\t}\n\t\treturn tokenGenerator;\n\t}\n\n\tprivate static JwtGenerator getJwtGenerator(HttpSecurity httpSecurity) {\n\t\tJwtGenerator jwtGenerator = httpSecurity.getSharedObject(JwtGenerator.class);\n\t\tif (jwtGenerator == null) {\n\t\t\tJwtEncoder jwtEncoder = getJwtEncoder(httpSecurity);\n\t\t\tif (jwtEncoder != null) {\n\t\t\t\tjwtGenerator = new JwtGenerator(jwtEncoder);\n\t\t\t\tjwtGenerator.setJwtCustomizer(getJwtCustomizer(httpSecurity));\n\t\t\t\thttpSecurity.setSharedObject(JwtGenerator.class, jwtGenerator);\n\t\t\t}\n\t\t}\n\t\treturn jwtGenerator;\n\t}\n\n\tprivate static JwtEncoder getJwtEncoder(HttpSecurity httpSecurity) {\n\t\tJwtEncoder jwtEncoder = httpSecurity.getSharedObject(JwtEncoder.class);\n\t\tif (jwtEncoder == null) {\n\t\t\tjwtEncoder = getOptionalBean(httpSecurity, JwtEncoder.class);\n\t\t\tif (jwtEncoder == null) {\n\t\t\t\tJWKSource<SecurityContext> jwkSource = getJwkSource(httpSecurity);\n\t\t\t\tif (jwkSource != null) {\n\t\t\t\t\tjwtEncoder = new NimbusJwtEncoder(jwkSource);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (jwtEncoder != null) {\n\t\t\t\thttpSecurity.setSharedObject(JwtEncoder.class, jwtEncoder);\n\t\t\t}\n\t\t}\n\t\treturn jwtEncoder;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tstatic JWKSource<SecurityContext> getJwkSource(HttpSecurity httpSecurity) {\n\t\tJWKSource<SecurityContext> jwkSource = httpSecurity.getSharedObject(JWKSource.class);\n\t\tif (jwkSource == null) {\n\t\t\tResolvableType type = ResolvableType.forClassWithGenerics(JWKSource.class, SecurityContext.class);\n\t\t\tjwkSource = getOptionalBean(httpSecurity, type);\n\t\t\tif (jwkSource != null) {\n\t\t\t\thttpSecurity.setSharedObject(JWKSource.class, jwkSource);\n\t\t\t}\n\t\t}\n\t\treturn jwkSource;\n\t}\n\n\tprivate static OAuth2TokenCustomizer<JwtEncodingContext> getJwtCustomizer(HttpSecurity httpSecurity) {\n\t\tfinal OAuth2TokenCustomizer<JwtEncodingContext> defaultJwtCustomizer = DefaultOAuth2TokenCustomizers\n\t\t\t.jwtCustomizer();\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class,\n\t\t\t\tJwtEncodingContext.class);\n\t\tfinal OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer = getOptionalBean(httpSecurity, type);\n\t\tif (jwtCustomizer == null) {\n\t\t\treturn defaultJwtCustomizer;\n\t\t}\n\t\treturn (context) -> {\n\t\t\tdefaultJwtCustomizer.customize(context);\n\t\t\tjwtCustomizer.customize(context);\n\t\t};\n\t}\n\n\tprivate static OAuth2TokenCustomizer<OAuth2TokenClaimsContext> getAccessTokenCustomizer(HttpSecurity httpSecurity) {\n\t\tfinal OAuth2TokenCustomizer<OAuth2TokenClaimsContext> defaultAccessTokenCustomizer = DefaultOAuth2TokenCustomizers\n\t\t\t.accessTokenCustomizer();\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class,\n\t\t\t\tOAuth2TokenClaimsContext.class);\n\t\tOAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer = getOptionalBean(httpSecurity, type);\n\t\tif (accessTokenCustomizer == null) {\n\t\t\treturn defaultAccessTokenCustomizer;\n\t\t}\n\t\treturn (context) -> {\n\t\t\tdefaultAccessTokenCustomizer.customize(context);\n\t\t\taccessTokenCustomizer.customize(context);\n\t\t};\n\t}\n\n\tstatic AuthorizationServerSettings getAuthorizationServerSettings(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = httpSecurity\n\t\t\t.getSharedObject(AuthorizationServerSettings.class);\n\t\tif (authorizationServerSettings == null) {\n\t\t\tauthorizationServerSettings = getBean(httpSecurity, AuthorizationServerSettings.class);\n\t\t\thttpSecurity.setSharedObject(AuthorizationServerSettings.class, authorizationServerSettings);\n\t\t}\n\t\treturn authorizationServerSettings;\n\t}\n\n\tstatic <T> T getBean(HttpSecurity httpSecurity, Class<T> type) {\n\t\treturn httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getObject();\n\t}\n\n\tstatic <T> T getOptionalBean(HttpSecurity httpSecurity, Class<T> type) {\n\t\treturn httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getIfUnique();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tstatic <T> T getOptionalBean(HttpSecurity httpSecurity, ResolvableType type) {\n\t\treturn (T) httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getIfUnique();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2DeviceAuthorizationEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2DeviceAuthorizationResponse;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2DeviceAuthorizationEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceAuthorizationRequestAuthenticationConverter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Configurer for the OAuth 2.0 Device Authorization Endpoint.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#deviceAuthorizationEndpoint\n * @see OAuth2DeviceAuthorizationEndpointFilter\n */\npublic final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> deviceAuthorizationRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> deviceAuthorizationRequestConvertersConsumer = (\n\t\t\tdeviceAuthorizationRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler deviceAuthorizationResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\tprivate String verificationUri;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2DeviceAuthorizationEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Device\n\t * Authorization Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating\n\t * the request.\n\t * @param deviceAuthorizationRequestConverter the {@link AuthenticationConverter} used\n\t * when attempting to extract a Device Authorization Request from\n\t * {@link HttpServletRequest}\n\t * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverter(\n\t\t\tAuthenticationConverter deviceAuthorizationRequestConverter) {\n\t\tAssert.notNull(deviceAuthorizationRequestConverter, \"deviceAuthorizationRequestConverter cannot be null\");\n\t\tthis.deviceAuthorizationRequestConverters.add(deviceAuthorizationRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added\n\t * {@link #deviceAuthorizationRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param deviceAuthorizationRequestConvertersConsumer the {@code Consumer} providing\n\t * access to the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> deviceAuthorizationRequestConvertersConsumer) {\n\t\tAssert.notNull(deviceAuthorizationRequestConvertersConsumer,\n\t\t\t\t\"deviceAuthorizationRequestConvertersConsumer cannot be null\");\n\t\tthis.deviceAuthorizationRequestConvertersConsumer = deviceAuthorizationRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}\n\t * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointConfigurer authenticationProvider(\n\t\t\tAuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} and returning the\n\t * {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}.\n\t * @param deviceAuthorizationResponseHandler the {@link AuthenticationSuccessHandler}\n\t * used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}\n\t * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationResponseHandler(\n\t\t\tAuthenticationSuccessHandler deviceAuthorizationResponseHandler) {\n\t\tthis.deviceAuthorizationResponseHandler = deviceAuthorizationResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the end-user verification {@code URI} on the authorization server.\n\t * @param verificationUri the end-user verification {@code URI} on the authorization\n\t * server\n\t * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointConfigurer verificationUri(String verificationUri) {\n\t\tthis.verificationUri = verificationUri;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(HttpSecurity builder) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(builder);\n\t\tString deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint())\n\t\t\t\t: authorizationServerSettings.getDeviceAuthorizationEndpoint();\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, deviceAuthorizationEndpointUri);\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(builder);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders\n\t\t\t.forEach((authenticationProvider) -> builder.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tpublic void configure(HttpSecurity builder) {\n\t\tAuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(builder);\n\n\t\tString deviceAuthorizationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getDeviceAuthorizationEndpoint())\n\t\t\t\t: authorizationServerSettings.getDeviceAuthorizationEndpoint();\n\t\tOAuth2DeviceAuthorizationEndpointFilter deviceAuthorizationEndpointFilter = new OAuth2DeviceAuthorizationEndpointFilter(\n\t\t\t\tauthenticationManager, deviceAuthorizationEndpointUri);\n\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.deviceAuthorizationRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.deviceAuthorizationRequestConverters);\n\t\t}\n\t\tthis.deviceAuthorizationRequestConvertersConsumer.accept(authenticationConverters);\n\t\tdeviceAuthorizationEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.deviceAuthorizationResponseHandler != null) {\n\t\t\tdeviceAuthorizationEndpointFilter.setAuthenticationSuccessHandler(this.deviceAuthorizationResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\tdeviceAuthorizationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\tif (StringUtils.hasText(this.verificationUri)) {\n\t\t\tdeviceAuthorizationEndpointFilter.setVerificationUri(this.verificationUri);\n\t\t}\n\t\tbuilder.addFilterAfter(postProcess(deviceAuthorizationEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\t\tauthenticationConverters.add(new OAuth2DeviceAuthorizationRequestAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity builder) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(builder);\n\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationProvider deviceAuthorizationRequestAuthenticationProvider = new OAuth2DeviceAuthorizationRequestAuthenticationProvider(\n\t\t\t\tauthorizationService);\n\t\tauthenticationProviders.add(deviceAuthorizationRequestAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2DeviceVerificationEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2DeviceVerificationEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceAuthorizationConsentAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceVerificationAuthenticationConverter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Configurer for the OAuth 2.0 Device Verification Endpoint.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#deviceVerificationEndpoint\n * @see OAuth2DeviceVerificationEndpointFilter\n */\npublic final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> deviceVerificationRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> deviceVerificationRequestConvertersConsumer = (\n\t\t\tdeviceVerificationRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler deviceVerificationResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\tprivate String consentPage;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2DeviceVerificationEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Device\n\t * Verification Request (or Device Authorization Consent) from\n\t * {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2DeviceVerificationAuthenticationToken} or\n\t * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating\n\t * the request.\n\t * @param deviceVerificationRequestConverter the {@link AuthenticationConverter} used\n\t * when attempting to extract a Device Verification Request (or Device Authorization\n\t * Consent) from {@link HttpServletRequest}\n\t * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverter(\n\t\t\tAuthenticationConverter deviceVerificationRequestConverter) {\n\t\tAssert.notNull(deviceVerificationRequestConverter, \"deviceVerificationRequestConverter cannot be null\");\n\t\tthis.deviceVerificationRequestConverters.add(deviceVerificationRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added\n\t * {@link #deviceVerificationRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param deviceVerificationRequestConvertersConsumer the {@code Consumer} providing\n\t * access to the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> deviceVerificationRequestConvertersConsumer) {\n\t\tAssert.notNull(deviceVerificationRequestConvertersConsumer,\n\t\t\t\t\"deviceVerificationRequestConvertersConsumer cannot be null\");\n\t\tthis.deviceVerificationRequestConvertersConsumer = deviceVerificationRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OAuth2DeviceVerificationAuthenticationToken} or\n\t * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or\n\t * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}\n\t * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceVerificationEndpointConfigurer authenticationProvider(\n\t\t\tAuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceVerificationEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2DeviceVerificationAuthenticationToken} and returning the response.\n\t * @param deviceVerificationResponseHandler the {@link AuthenticationSuccessHandler}\n\t * used for handling an {@link OAuth2DeviceVerificationAuthenticationToken}\n\t * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceVerificationEndpointConfigurer deviceVerificationResponseHandler(\n\t\t\tAuthenticationSuccessHandler deviceVerificationResponseHandler) {\n\t\tthis.deviceVerificationResponseHandler = deviceVerificationResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceVerificationEndpointConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the URI to redirect Resource Owners to if consent is required during the\n\t * {@code device_code} flow. A default consent page will be generated when this\n\t * attribute is not specified.\n\t *\n\t * If a URI is specified, applications are required to process the specified URI to\n\t * generate a consent page. The query string will contain the following parameters:\n\t *\n\t * <ul>\n\t * <li>{@code client_id} - the client identifier</li>\n\t * <li>{@code scope} - a space-delimited list of scopes present in the device\n\t * authorization request</li>\n\t * <li>{@code state} - a CSRF protection token</li>\n\t * <li>{@code user_code} - the user code</li>\n\t * </ul>\n\t *\n\t * In general, the consent page should create a form that submits a request with the\n\t * following requirements:\n\t *\n\t * <ul>\n\t * <li>It must be an HTTP POST</li>\n\t * <li>It must be submitted to\n\t * {@link AuthorizationServerSettings#getDeviceVerificationEndpoint()}</li>\n\t * <li>It must include the received {@code client_id} as an HTTP parameter</li>\n\t * <li>It must include the received {@code state} as an HTTP parameter</li>\n\t * <li>It must include the list of {@code scope}s the {@code Resource Owner} consented\n\t * to as an HTTP parameter</li>\n\t * <li>It must include the received {@code user_code} as an HTTP parameter</li>\n\t * </ul>\n\t * @param consentPage the URI of the custom consent page to redirect to if consent is\n\t * required (e.g. \"/oauth2/consent\")\n\t * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2DeviceVerificationEndpointConfigurer consentPage(String consentPage) {\n\t\tthis.consentPage = consentPage;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(HttpSecurity builder) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(builder);\n\t\tString deviceVerificationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getDeviceVerificationEndpoint())\n\t\t\t\t: authorizationServerSettings.getDeviceVerificationEndpoint();\n\t\tthis.requestMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, deviceVerificationEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, deviceVerificationEndpointUri));\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(builder);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders\n\t\t\t.forEach((authenticationProvider) -> builder.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tpublic void configure(HttpSecurity builder) {\n\t\tAuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(builder);\n\n\t\tString deviceVerificationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getDeviceVerificationEndpoint())\n\t\t\t\t: authorizationServerSettings.getDeviceVerificationEndpoint();\n\t\tOAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter = new OAuth2DeviceVerificationEndpointFilter(\n\t\t\t\tauthenticationManager, deviceVerificationEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.deviceVerificationRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.deviceVerificationRequestConverters);\n\t\t}\n\t\tthis.deviceVerificationRequestConvertersConsumer.accept(authenticationConverters);\n\t\tdeviceVerificationEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.deviceVerificationResponseHandler != null) {\n\t\t\tdeviceVerificationEndpointFilter.setAuthenticationSuccessHandler(this.deviceVerificationResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\tdeviceVerificationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\tif (StringUtils.hasText(this.consentPage)) {\n\t\t\tdeviceVerificationEndpointFilter.setConsentPage(this.consentPage);\n\t\t}\n\t\tbuilder.addFilterAfter(postProcess(deviceVerificationEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OAuth2DeviceVerificationAuthenticationConverter());\n\t\tauthenticationConverters.add(new OAuth2DeviceAuthorizationConsentAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity builder) {\n\t\tRegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils\n\t\t\t.getRegisteredClientRepository(builder);\n\t\tOAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(builder);\n\t\tOAuth2AuthorizationConsentService authorizationConsentService = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationConsentService(builder);\n\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\t// @formatter:off\n\t\tOAuth2DeviceVerificationAuthenticationProvider deviceVerificationAuthenticationProvider =\n\t\t\t\tnew OAuth2DeviceVerificationAuthenticationProvider(\n\t\t\t\t\t\tregisteredClientRepository, authorizationService, authorizationConsentService);\n\t\t// @formatter:on\n\t\tauthenticationProviders.add(deviceVerificationAuthenticationProvider);\n\n\t\t// @formatter:off\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider deviceAuthorizationConsentAuthenticationProvider =\n\t\t\t\tnew OAuth2DeviceAuthorizationConsentAuthenticationProvider(\n\t\t\t\t\t\tregisteredClientRepository, authorizationService, authorizationConsentService);\n\t\t// @formatter:on\n\t\tauthenticationProviders.add(deviceAuthorizationConsentAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2PushedAuthorizationRequestEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationValidator;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2PushedAuthorizationRequestAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2PushedAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2PushedAuthorizationRequestEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for the OAuth 2.0 Pushed Authorization Request Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#pushedAuthorizationRequestEndpoint\n * @see OAuth2PushedAuthorizationRequestEndpointFilter\n */\npublic final class OAuth2PushedAuthorizationRequestEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> pushedAuthorizationRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> pushedAuthorizationRequestConvertersConsumer = (\n\t\t\tauthorizationRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler pushedAuthorizationResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\tprivate Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationCodeRequestAuthenticationValidator;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2PushedAuthorizationRequestEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract a Pushed\n\t * Authorization Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2PushedAuthorizationRequestAuthenticationToken} used for authenticating\n\t * the request.\n\t * @param pushedAuthorizationRequestConverter an {@link AuthenticationConverter} used\n\t * when attempting to extract a Pushed Authorization Request from\n\t * {@link HttpServletRequest}\n\t * @return the {@link OAuth2PushedAuthorizationRequestEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationRequestConverter(\n\t\t\tAuthenticationConverter pushedAuthorizationRequestConverter) {\n\t\tAssert.notNull(pushedAuthorizationRequestConverter, \"pushedAuthorizationRequestConverter cannot be null\");\n\t\tthis.pushedAuthorizationRequestConverters.add(pushedAuthorizationRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added\n\t * {@link #pushedAuthorizationRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param pushedAuthorizationRequestConvertersConsumer the {@code Consumer} providing\n\t * access to the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2PushedAuthorizationRequestEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> pushedAuthorizationRequestConvertersConsumer) {\n\t\tAssert.notNull(pushedAuthorizationRequestConvertersConsumer,\n\t\t\t\t\"pushedAuthorizationRequestConvertersConsumer cannot be null\");\n\t\tthis.pushedAuthorizationRequestConvertersConsumer = pushedAuthorizationRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OAuth2PushedAuthorizationRequestAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OAuth2PushedAuthorizationRequestAuthenticationToken}\n\t * @return the {@link OAuth2PushedAuthorizationRequestEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2PushedAuthorizationRequestEndpointConfigurer authenticationProvider(\n\t\t\tAuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2PushedAuthorizationRequestEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2PushedAuthorizationRequestEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2PushedAuthorizationRequestAuthenticationToken} and returning the\n\t * Pushed Authorization Response.\n\t * @param pushedAuthorizationResponseHandler the {@link AuthenticationSuccessHandler}\n\t * used for handling an {@link OAuth2PushedAuthorizationRequestAuthenticationToken}\n\t * @return the {@link OAuth2PushedAuthorizationRequestEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationResponseHandler(\n\t\t\tAuthenticationSuccessHandler pushedAuthorizationResponseHandler) {\n\t\tthis.pushedAuthorizationResponseHandler = pushedAuthorizationResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationException} and returning the\n\t * {@link OAuth2Error Error Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException}\n\t * @return the {@link OAuth2PushedAuthorizationRequestEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2PushedAuthorizationRequestEndpointConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\tvoid addAuthorizationCodeRequestAuthenticationValidator(\n\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {\n\t\tthis.authorizationCodeRequestAuthenticationValidator = (this.authorizationCodeRequestAuthenticationValidator == null)\n\t\t\t\t? authenticationValidator\n\t\t\t\t: this.authorizationCodeRequestAuthenticationValidator.andThen(authenticationValidator);\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString pushedAuthorizationRequestEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getPushedAuthorizationRequestEndpoint())\n\t\t\t\t: authorizationServerSettings.getPushedAuthorizationRequestEndpoint();\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, pushedAuthorizationRequestEndpointUri);\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString pushedAuthorizationRequestEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getPushedAuthorizationRequestEndpoint())\n\t\t\t\t: authorizationServerSettings.getPushedAuthorizationRequestEndpoint();\n\t\tOAuth2PushedAuthorizationRequestEndpointFilter pushedAuthorizationRequestEndpointFilter = new OAuth2PushedAuthorizationRequestEndpointFilter(\n\t\t\t\tauthenticationManager, pushedAuthorizationRequestEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.pushedAuthorizationRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.pushedAuthorizationRequestConverters);\n\t\t}\n\t\tthis.pushedAuthorizationRequestConvertersConsumer.accept(authenticationConverters);\n\t\tpushedAuthorizationRequestEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.pushedAuthorizationResponseHandler != null) {\n\t\t\tpushedAuthorizationRequestEndpointFilter\n\t\t\t\t.setAuthenticationSuccessHandler(this.pushedAuthorizationResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\tpushedAuthorizationRequestEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(pushedAuthorizationRequestEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OAuth2AuthorizationCodeRequestAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOAuth2PushedAuthorizationRequestAuthenticationProvider pushedAuthorizationRequestAuthenticationProvider = new OAuth2PushedAuthorizationRequestAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));\n\t\tif (this.authorizationCodeRequestAuthenticationValidator != null) {\n\t\t\tpushedAuthorizationRequestAuthenticationProvider\n\t\t\t\t.setAuthenticationValidator(new OAuth2AuthorizationCodeRequestAuthenticationValidator()\n\t\t\t\t\t.andThen(this.authorizationCodeRequestAuthenticationValidator));\n\t\t}\n\t\tauthenticationProviders.add(pushedAuthorizationRequestAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2TokenEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceCodeAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenExchangeAuthenticationConverter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for the OAuth 2.0 Token Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#tokenEndpoint\n * @see OAuth2TokenEndpointFilter\n */\npublic final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> accessTokenRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> accessTokenRequestConvertersConsumer = (\n\t\t\taccessTokenRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler accessTokenResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2TokenEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract an Access\n\t * Token Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the\n\t * authorization grant.\n\t * @param accessTokenRequestConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract an Access Token Request from {@link HttpServletRequest}\n\t * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2TokenEndpointConfigurer accessTokenRequestConverter(\n\t\t\tAuthenticationConverter accessTokenRequestConverter) {\n\t\tAssert.notNull(accessTokenRequestConverter, \"accessTokenRequestConverter cannot be null\");\n\t\tthis.accessTokenRequestConverters.add(accessTokenRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #accessTokenRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param accessTokenRequestConvertersConsumer the {@code Consumer} providing access\n\t * to the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2TokenEndpointConfigurer accessTokenRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> accessTokenRequestConvertersConsumer) {\n\t\tAssert.notNull(accessTokenRequestConvertersConsumer, \"accessTokenRequestConvertersConsumer cannot be null\");\n\t\tthis.accessTokenRequestConvertersConsumer = accessTokenRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating a type of\n\t * {@link OAuth2AuthorizationGrantAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken}\n\t * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2TokenEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2TokenEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2AccessTokenAuthenticationToken} and returning the\n\t * {@link OAuth2AccessTokenResponse Access Token Response}.\n\t * @param accessTokenResponseHandler the {@link AuthenticationSuccessHandler} used for\n\t * handling an {@link OAuth2AccessTokenAuthenticationToken}\n\t * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2TokenEndpointConfigurer accessTokenResponseHandler(\n\t\t\tAuthenticationSuccessHandler accessTokenResponseHandler) {\n\t\tthis.accessTokenResponseHandler = accessTokenResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration\n\t */\n\tpublic OAuth2TokenEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils.withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenEndpoint();\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, tokenEndpointUri);\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\n\t\tString tokenEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils.withMultipleIssuersPattern(authorizationServerSettings.getTokenEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenEndpoint();\n\t\tOAuth2TokenEndpointFilter tokenEndpointFilter = new OAuth2TokenEndpointFilter(authenticationManager,\n\t\t\t\ttokenEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.accessTokenRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.accessTokenRequestConverters);\n\t\t}\n\t\tthis.accessTokenRequestConvertersConsumer.accept(authenticationConverters);\n\t\ttokenEndpointFilter.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.accessTokenResponseHandler != null) {\n\t\t\ttokenEndpointFilter.setAuthenticationSuccessHandler(this.accessTokenResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\ttokenEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(tokenEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OAuth2AuthorizationCodeAuthenticationConverter());\n\t\tauthenticationConverters.add(new OAuth2RefreshTokenAuthenticationConverter());\n\t\tauthenticationConverters.add(new OAuth2ClientCredentialsAuthenticationConverter());\n\t\tauthenticationConverters.add(new OAuth2DeviceCodeAuthenticationConverter());\n\t\tauthenticationConverters.add(new OAuth2TokenExchangeAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity);\n\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = OAuth2ConfigurerUtils\n\t\t\t.getTokenGenerator(httpSecurity);\n\n\t\tOAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(\n\t\t\t\tauthorizationService, tokenGenerator);\n\t\tSessionRegistry sessionRegistry = httpSecurity.getSharedObject(SessionRegistry.class);\n\t\tif (sessionRegistry != null) {\n\t\t\tauthorizationCodeAuthenticationProvider.setSessionRegistry(sessionRegistry);\n\t\t}\n\t\tauthenticationProviders.add(authorizationCodeAuthenticationProvider);\n\n\t\tOAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider = new OAuth2RefreshTokenAuthenticationProvider(\n\t\t\t\tauthorizationService, tokenGenerator);\n\t\tauthenticationProviders.add(refreshTokenAuthenticationProvider);\n\n\t\tOAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider(\n\t\t\t\tauthorizationService, tokenGenerator);\n\t\tauthenticationProviders.add(clientCredentialsAuthenticationProvider);\n\n\t\tOAuth2DeviceCodeAuthenticationProvider deviceCodeAuthenticationProvider = new OAuth2DeviceCodeAuthenticationProvider(\n\t\t\t\tauthorizationService, tokenGenerator);\n\t\tauthenticationProviders.add(deviceCodeAuthenticationProvider);\n\n\t\tOAuth2TokenExchangeAuthenticationProvider tokenExchangeAuthenticationProvider = new OAuth2TokenExchangeAuthenticationProvider(\n\t\t\t\tauthorizationService, tokenGenerator);\n\t\tauthenticationProviders.add(tokenExchangeAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2TokenIntrospectionEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenIntrospectionAuthenticationConverter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for the OAuth 2.0 Token Introspection Endpoint.\n *\n * @author Gaurav Tiwari\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#tokenIntrospectionEndpoint(Customizer)\n * @see OAuth2TokenIntrospectionEndpointFilter\n */\npublic final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> introspectionRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> introspectionRequestConvertersConsumer = (\n\t\t\tintrospectionRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler introspectionResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2TokenIntrospectionEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract an\n\t * Introspection Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param introspectionRequestConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract an Introspection Request from {@link HttpServletRequest}\n\t * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverter(\n\t\t\tAuthenticationConverter introspectionRequestConverter) {\n\t\tAssert.notNull(introspectionRequestConverter, \"introspectionRequestConverter cannot be null\");\n\t\tthis.introspectionRequestConverters.add(introspectionRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #introspectionRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param introspectionRequestConvertersConsumer the {@code Consumer} providing access\n\t * to the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> introspectionRequestConvertersConsumer) {\n\t\tAssert.notNull(introspectionRequestConvertersConsumer, \"introspectionRequestConvertersConsumer cannot be null\");\n\t\tthis.introspectionRequestConvertersConsumer = introspectionRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating a type of\n\t * {@link OAuth2TokenIntrospectionAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken}\n\t * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenIntrospectionEndpointConfigurer authenticationProvider(\n\t\t\tAuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenIntrospectionEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2TokenIntrospectionAuthenticationToken}.\n\t * @param introspectionResponseHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}\n\t * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenIntrospectionEndpointConfigurer introspectionResponseHandler(\n\t\t\tAuthenticationSuccessHandler introspectionResponseHandler) {\n\t\tthis.introspectionResponseHandler = introspectionResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenIntrospectionEndpointConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenIntrospectionEndpoint();\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, tokenIntrospectionEndpointUri);\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString tokenIntrospectionEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getTokenIntrospectionEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenIntrospectionEndpoint();\n\t\tOAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter = new OAuth2TokenIntrospectionEndpointFilter(\n\t\t\t\tauthenticationManager, tokenIntrospectionEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.introspectionRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.introspectionRequestConverters);\n\t\t}\n\t\tthis.introspectionRequestConvertersConsumer.accept(authenticationConverters);\n\t\tintrospectionEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.introspectionResponseHandler != null) {\n\t\t\tintrospectionEndpointFilter.setAuthenticationSuccessHandler(this.introspectionResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\tintrospectionEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(introspectionEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OAuth2TokenIntrospectionAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOAuth2TokenIntrospectionAuthenticationProvider tokenIntrospectionAuthenticationProvider = new OAuth2TokenIntrospectionAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));\n\t\tauthenticationProviders.add(tokenIntrospectionAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2TokenRevocationEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenRevocationAuthenticationConverter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for the OAuth 2.0 Token Revocation Endpoint.\n *\n * @author Arfat Chaus\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#tokenRevocationEndpoint\n * @see OAuth2TokenRevocationEndpointFilter\n */\npublic final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> revocationRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> revocationRequestConvertersConsumer = (\n\t\t\trevocationRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler revocationResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOAuth2TokenRevocationEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract a Revoke\n\t * Token Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param revocationRequestConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract a Revoke Token Request from {@link HttpServletRequest}\n\t * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenRevocationEndpointConfigurer revocationRequestConverter(\n\t\t\tAuthenticationConverter revocationRequestConverter) {\n\t\tAssert.notNull(revocationRequestConverter, \"revocationRequestConverter cannot be null\");\n\t\tthis.revocationRequestConverters.add(revocationRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #revocationRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param revocationRequestConvertersConsumer the {@code Consumer} providing access to\n\t * the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenRevocationEndpointConfigurer revocationRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> revocationRequestConvertersConsumer) {\n\t\tAssert.notNull(revocationRequestConvertersConsumer, \"revocationRequestConvertersConsumer cannot be null\");\n\t\tthis.revocationRequestConvertersConsumer = revocationRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating a type of\n\t * {@link OAuth2TokenRevocationAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken}\n\t * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenRevocationEndpointConfigurer authenticationProvider(\n\t\t\tAuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenRevocationEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2TokenRevocationAuthenticationToken}.\n\t * @param revocationResponseHandler the {@link AuthenticationSuccessHandler} used for\n\t * handling an {@link OAuth2TokenRevocationAuthenticationToken}\n\t * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenRevocationEndpointConfigurer revocationResponseHandler(\n\t\t\tAuthenticationSuccessHandler revocationResponseHandler) {\n\t\tthis.revocationResponseHandler = revocationResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OAuth2TokenRevocationEndpointConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenRevocationEndpoint();\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, tokenRevocationEndpointUri);\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\n\t\tString tokenRevocationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getTokenRevocationEndpoint())\n\t\t\t\t: authorizationServerSettings.getTokenRevocationEndpoint();\n\t\tOAuth2TokenRevocationEndpointFilter revocationEndpointFilter = new OAuth2TokenRevocationEndpointFilter(\n\t\t\t\tauthenticationManager, tokenRevocationEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.revocationRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.revocationRequestConverters);\n\t\t}\n\t\tthis.revocationRequestConvertersConsumer.accept(authenticationConverters);\n\t\trevocationEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.revocationResponseHandler != null) {\n\t\t\trevocationEndpointFilter.setAuthenticationSuccessHandler(this.revocationResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\trevocationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(revocationEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OAuth2TokenRevocationAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider = new OAuth2TokenRevocationAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));\n\t\tauthenticationProviders.add(tokenRevocationAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcClientRegistrationEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientConfigurationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.OidcClientRegistrationEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.authentication.OidcClientRegistrationAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for OpenID Connect 1.0 Dynamic Client Registration Endpoint.\n *\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OidcConfigurer#clientRegistrationEndpoint\n * @see OidcClientRegistrationEndpointFilter\n */\npublic final class OidcClientRegistrationEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> clientRegistrationRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer = (\n\t\t\tclientRegistrationRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler clientRegistrationResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOidcClientRegistrationEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract a Client\n\t * Registration Request from {@link HttpServletRequest} to an instance of\n\t * {@link OidcClientRegistrationAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used\n\t * when attempting to extract a Client Registration Request from\n\t * {@link HttpServletRequest}\n\t * @return the {@link OidcClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverter(\n\t\t\tAuthenticationConverter clientRegistrationRequestConverter) {\n\t\tAssert.notNull(clientRegistrationRequestConverter, \"clientRegistrationRequestConverter cannot be null\");\n\t\tthis.clientRegistrationRequestConverters.add(clientRegistrationRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added\n\t * {@link #clientRegistrationRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param clientRegistrationRequestConvertersConsumer the {@code Consumer} providing\n\t * access to the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer) {\n\t\tAssert.notNull(clientRegistrationRequestConvertersConsumer,\n\t\t\t\t\"clientRegistrationRequestConvertersConsumer cannot be null\");\n\t\tthis.clientRegistrationRequestConvertersConsumer = clientRegistrationRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OidcClientRegistrationAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OidcClientRegistrationAuthenticationToken}\n\t * @return the {@link OidcClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OidcClientRegistrationEndpointConfigurer authenticationProvider(\n\t\t\tAuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OidcClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OidcClientRegistrationEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OidcClientRegistrationAuthenticationToken} and returning the\n\t * {@link OidcClientRegistration Client Registration Response}.\n\t * @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler}\n\t * used for handling an {@link OidcClientRegistrationAuthenticationToken}\n\t * @return the {@link OidcClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OidcClientRegistrationEndpointConfigurer clientRegistrationResponseHandler(\n\t\t\tAuthenticationSuccessHandler clientRegistrationResponseHandler) {\n\t\tthis.clientRegistrationResponseHandler = clientRegistrationResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OidcClientRegistrationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OidcClientRegistrationEndpointConfigurer errorResponseHandler(\n\t\t\tAuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString clientRegistrationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getOidcClientRegistrationEndpoint())\n\t\t\t\t: authorizationServerSettings.getOidcClientRegistrationEndpoint();\n\t\tthis.requestMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, clientRegistrationEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, clientRegistrationEndpointUri));\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\n\t\tString clientRegistrationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getOidcClientRegistrationEndpoint())\n\t\t\t\t: authorizationServerSettings.getOidcClientRegistrationEndpoint();\n\t\tOidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter = new OidcClientRegistrationEndpointFilter(\n\t\t\t\tauthenticationManager, clientRegistrationEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.clientRegistrationRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.clientRegistrationRequestConverters);\n\t\t}\n\t\tthis.clientRegistrationRequestConvertersConsumer.accept(authenticationConverters);\n\t\toidcClientRegistrationEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.clientRegistrationResponseHandler != null) {\n\t\t\toidcClientRegistrationEndpointFilter\n\t\t\t\t.setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\toidcClientRegistrationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(oidcClientRegistrationEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OidcClientRegistrationAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOidcClientRegistrationAuthenticationProvider oidcClientRegistrationAuthenticationProvider = new OidcClientRegistrationAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getTokenGenerator(httpSecurity));\n\t\tPasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class);\n\t\tif (passwordEncoder != null) {\n\t\t\toidcClientRegistrationAuthenticationProvider.setPasswordEncoder(passwordEncoder);\n\t\t}\n\t\tauthenticationProviders.add(oidcClientRegistrationAuthenticationProvider);\n\n\t\tOidcClientConfigurationAuthenticationProvider oidcClientConfigurationAuthenticationProvider = new OidcClientConfigurationAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));\n\t\tauthenticationProviders.add(oidcClientConfigurationAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Configurer for OpenID Connect 1.0 support.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerConfigurer#oidc\n * @see OidcProviderConfigurationEndpointConfigurer\n * @see OidcLogoutEndpointConfigurer\n * @see OidcClientRegistrationEndpointConfigurer\n * @see OidcUserInfoEndpointConfigurer\n */\npublic final class OidcConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate final Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();\n\n\tprivate RequestMatcher requestMatcher;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOidcConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t\taddConfigurer(OidcProviderConfigurationEndpointConfigurer.class,\n\t\t\t\tnew OidcProviderConfigurationEndpointConfigurer(objectPostProcessor));\n\t\taddConfigurer(OidcLogoutEndpointConfigurer.class, new OidcLogoutEndpointConfigurer(objectPostProcessor));\n\t\taddConfigurer(OidcUserInfoEndpointConfigurer.class, new OidcUserInfoEndpointConfigurer(objectPostProcessor));\n\t}\n\n\t/**\n\t * Configures the OpenID Connect 1.0 Provider Configuration Endpoint.\n\t * @param providerConfigurationEndpointCustomizer the {@link Customizer} providing\n\t * access to the {@link OidcProviderConfigurationEndpointConfigurer}\n\t * @return the {@link OidcConfigurer} for further configuration\n\t */\n\tpublic OidcConfigurer providerConfigurationEndpoint(\n\t\t\tCustomizer<OidcProviderConfigurationEndpointConfigurer> providerConfigurationEndpointCustomizer) {\n\t\tproviderConfigurationEndpointCustomizer\n\t\t\t.customize(getConfigurer(OidcProviderConfigurationEndpointConfigurer.class));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OpenID Connect 1.0 RP-Initiated Logout Endpoint.\n\t * @param logoutEndpointCustomizer the {@link Customizer} providing access to the\n\t * {@link OidcLogoutEndpointConfigurer}\n\t * @return the {@link OidcConfigurer} for further configuration\n\t */\n\tpublic OidcConfigurer logoutEndpoint(Customizer<OidcLogoutEndpointConfigurer> logoutEndpointCustomizer) {\n\t\tlogoutEndpointCustomizer.customize(getConfigurer(OidcLogoutEndpointConfigurer.class));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OpenID Connect Dynamic Client Registration 1.0 Endpoint.\n\t * @param clientRegistrationEndpointCustomizer the {@link Customizer} providing access\n\t * to the {@link OidcClientRegistrationEndpointConfigurer}\n\t * @return the {@link OidcConfigurer} for further configuration\n\t */\n\tpublic OidcConfigurer clientRegistrationEndpoint(\n\t\t\tCustomizer<OidcClientRegistrationEndpointConfigurer> clientRegistrationEndpointCustomizer) {\n\t\tOidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer(\n\t\t\t\tOidcClientRegistrationEndpointConfigurer.class);\n\t\tif (clientRegistrationEndpointConfigurer == null) {\n\t\t\taddConfigurer(OidcClientRegistrationEndpointConfigurer.class,\n\t\t\t\t\tnew OidcClientRegistrationEndpointConfigurer(getObjectPostProcessor()));\n\t\t\tclientRegistrationEndpointConfigurer = getConfigurer(OidcClientRegistrationEndpointConfigurer.class);\n\t\t}\n\t\tclientRegistrationEndpointCustomizer.customize(clientRegistrationEndpointConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OpenID Connect 1.0 UserInfo Endpoint.\n\t * @param userInfoEndpointCustomizer the {@link Customizer} providing access to the\n\t * {@link OidcUserInfoEndpointConfigurer}\n\t * @return the {@link OidcConfigurer} for further configuration\n\t */\n\tpublic OidcConfigurer userInfoEndpoint(Customizer<OidcUserInfoEndpointConfigurer> userInfoEndpointCustomizer) {\n\t\tuserInfoEndpointCustomizer.customize(getConfigurer(OidcUserInfoEndpointConfigurer.class));\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tList<RequestMatcher> requestMatchers = new ArrayList<>();\n\t\tthis.configurers.values().forEach((configurer) -> {\n\t\t\tconfigurer.init(httpSecurity);\n\t\t\trequestMatchers.add(configurer.getRequestMatcher());\n\t\t});\n\t\tthis.requestMatcher = new OrRequestMatcher(requestMatchers);\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tOidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer(\n\t\t\t\tOidcClientRegistrationEndpointConfigurer.class);\n\t\tif (clientRegistrationEndpointConfigurer != null) {\n\t\t\tOidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOidcProviderConfigurationEndpointConfigurer.class);\n\n\t\t\tproviderConfigurationEndpointConfigurer.addDefaultProviderConfigurationCustomizer((builder) -> {\n\t\t\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\t\t\tString issuer = authorizationServerContext.getIssuer();\n\t\t\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t\t\t.getAuthorizationServerSettings();\n\n\t\t\t\tString clientRegistrationEndpoint = UriComponentsBuilder.fromUriString(issuer)\n\t\t\t\t\t.path(authorizationServerSettings.getOidcClientRegistrationEndpoint())\n\t\t\t\t\t.build()\n\t\t\t\t\t.toUriString();\n\n\t\t\t\tbuilder.clientRegistrationEndpoint(clientRegistrationEndpoint);\n\t\t\t});\n\t\t}\n\n\t\tOAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer = httpSecurity\n\t\t\t.getConfigurer(OAuth2AuthorizationServerConfigurer.class)\n\t\t\t.getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class);\n\t\tif (deviceAuthorizationEndpointConfigurer != null) {\n\t\t\tOidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOidcProviderConfigurationEndpointConfigurer.class);\n\n\t\t\tproviderConfigurationEndpointConfigurer.addDefaultProviderConfigurationCustomizer((builder) -> {\n\t\t\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\t\t\tString issuer = authorizationServerContext.getIssuer();\n\t\t\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t\t\t.getAuthorizationServerSettings();\n\n\t\t\t\tString deviceAuthorizationEndpoint = UriComponentsBuilder.fromUriString(issuer)\n\t\t\t\t\t.path(authorizationServerSettings.getDeviceAuthorizationEndpoint())\n\t\t\t\t\t.build()\n\t\t\t\t\t.toUriString();\n\n\t\t\t\tbuilder.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint);\n\t\t\t\tbuilder.grantType(AuthorizationGrantType.DEVICE_CODE.getValue());\n\t\t\t});\n\t\t}\n\n\t\tOAuth2PushedAuthorizationRequestEndpointConfigurer pushedAuthorizationRequestEndpointConfigurer = httpSecurity\n\t\t\t.getConfigurer(OAuth2AuthorizationServerConfigurer.class)\n\t\t\t.getConfigurer(OAuth2PushedAuthorizationRequestEndpointConfigurer.class);\n\t\tif (pushedAuthorizationRequestEndpointConfigurer != null) {\n\t\t\tOidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer = getConfigurer(\n\t\t\t\t\tOidcProviderConfigurationEndpointConfigurer.class);\n\n\t\t\tproviderConfigurationEndpointConfigurer.addDefaultProviderConfigurationCustomizer((builder) -> {\n\t\t\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\t\t\tString issuer = authorizationServerContext.getIssuer();\n\t\t\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t\t\t.getAuthorizationServerSettings();\n\n\t\t\t\tString pushedAuthorizationRequestEndpoint = UriComponentsBuilder.fromUriString(issuer)\n\t\t\t\t\t.path(authorizationServerSettings.getPushedAuthorizationRequestEndpoint())\n\t\t\t\t\t.build()\n\t\t\t\t\t.toUriString();\n\n\t\t\t\tbuilder.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint);\n\t\t\t});\n\t\t}\n\n\t\tthis.configurers.values().forEach((configurer) -> configurer.configure(httpSecurity));\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t<T> T getConfigurer(Class<T> type) {\n\t\treturn (T) this.configurers.get(type);\n\t}\n\n\tprivate <T extends AbstractOAuth2Configurer> void addConfigurer(Class<T> configurerType, T configurer) {\n\t\tthis.configurers.put(configurerType, configurer);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcLogoutEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.OidcLogoutEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.authentication.OidcLogoutAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for OpenID Connect 1.0 RP-Initiated Logout Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OidcConfigurer#logoutEndpoint\n * @see OidcLogoutEndpointFilter\n */\npublic final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> logoutRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> logoutRequestConvertersConsumer = (logoutRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler logoutResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOidcLogoutEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract a Logout\n\t * Request from {@link HttpServletRequest} to an instance of\n\t * {@link OidcLogoutAuthenticationToken} used for authenticating the request.\n\t * @param logoutRequestConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract a Logout Request from {@link HttpServletRequest}\n\t * @return the {@link OidcLogoutEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutEndpointConfigurer logoutRequestConverter(AuthenticationConverter logoutRequestConverter) {\n\t\tAssert.notNull(logoutRequestConverter, \"logoutRequestConverter cannot be null\");\n\t\tthis.logoutRequestConverters.add(logoutRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #logoutRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param logoutRequestConvertersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationConverter}'s\n\t * @return the {@link OidcLogoutEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutEndpointConfigurer logoutRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> logoutRequestConvertersConsumer) {\n\t\tAssert.notNull(logoutRequestConvertersConsumer, \"logoutRequestConvertersConsumer cannot be null\");\n\t\tthis.logoutRequestConvertersConsumer = logoutRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OidcLogoutAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OidcLogoutAuthenticationToken}\n\t * @return the {@link OidcLogoutEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OidcLogoutEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OidcLogoutAuthenticationToken} and performing the logout.\n\t * @param logoutResponseHandler the {@link AuthenticationSuccessHandler} used for\n\t * handling an {@link OidcLogoutAuthenticationToken}\n\t * @return the {@link OidcLogoutEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutEndpointConfigurer logoutResponseHandler(AuthenticationSuccessHandler logoutResponseHandler) {\n\t\tthis.logoutResponseHandler = logoutResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OidcLogoutEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcLogoutEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString logoutEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils.withMultipleIssuersPattern(authorizationServerSettings.getOidcLogoutEndpoint())\n\t\t\t\t: authorizationServerSettings.getOidcLogoutEndpoint();\n\t\tthis.requestMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, logoutEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, logoutEndpointUri));\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\n\t\tString logoutEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils.withMultipleIssuersPattern(authorizationServerSettings.getOidcLogoutEndpoint())\n\t\t\t\t: authorizationServerSettings.getOidcLogoutEndpoint();\n\t\tOidcLogoutEndpointFilter oidcLogoutEndpointFilter = new OidcLogoutEndpointFilter(authenticationManager,\n\t\t\t\tlogoutEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.logoutRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.logoutRequestConverters);\n\t\t}\n\t\tthis.logoutRequestConvertersConsumer.accept(authenticationConverters);\n\t\toidcLogoutEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.logoutResponseHandler != null) {\n\t\t\toidcLogoutEndpointFilter.setAuthenticationSuccessHandler(this.logoutResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\toidcLogoutEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterBefore(postProcess(oidcLogoutEndpointFilter), LogoutFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add(new OidcLogoutAuthenticationConverter());\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOidcLogoutAuthenticationProvider oidcLogoutAuthenticationProvider = new OidcLogoutAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),\n\t\t\t\thttpSecurity.getSharedObject(SessionRegistry.class));\n\t\tauthenticationProviders.add(oidcLogoutAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcProviderConfigurationEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.function.Consumer;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Configurer for the OpenID Connect 1.0 Provider Configuration Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OidcConfigurer#providerConfigurationEndpoint\n * @see OidcProviderConfigurationEndpointFilter\n */\npublic final class OidcProviderConfigurationEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer;\n\n\tprivate Consumer<OidcProviderConfiguration.Builder> defaultProviderConfigurationCustomizer;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOidcProviderConfigurationEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OidcProviderConfiguration.Builder} allowing the ability to customize the\n\t * claims of the OpenID Provider's configuration.\n\t * @param providerConfigurationCustomizer the {@code Consumer} providing access to the\n\t * {@link OidcProviderConfiguration.Builder}\n\t * @return the {@link OidcProviderConfigurationEndpointConfigurer} for further\n\t * configuration\n\t */\n\tpublic OidcProviderConfigurationEndpointConfigurer providerConfigurationCustomizer(\n\t\t\tConsumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer) {\n\t\tthis.providerConfigurationCustomizer = providerConfigurationCustomizer;\n\t\treturn this;\n\t}\n\n\tvoid addDefaultProviderConfigurationCustomizer(\n\t\t\tConsumer<OidcProviderConfiguration.Builder> defaultProviderConfigurationCustomizer) {\n\t\tthis.defaultProviderConfigurationCustomizer = (this.defaultProviderConfigurationCustomizer == null)\n\t\t\t\t? defaultProviderConfigurationCustomizer\n\t\t\t\t: this.defaultProviderConfigurationCustomizer.andThen(defaultProviderConfigurationCustomizer);\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString oidcProviderConfigurationEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? \"/**/.well-known/openid-configuration\" : \"/.well-known/openid-configuration\";\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, oidcProviderConfigurationEndpointUri);\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tOidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter = new OidcProviderConfigurationEndpointFilter();\n\t\tConsumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = getProviderConfigurationCustomizer();\n\t\tif (providerConfigurationCustomizer != null) {\n\t\t\toidcProviderConfigurationEndpointFilter.setProviderConfigurationCustomizer(providerConfigurationCustomizer);\n\t\t}\n\t\thttpSecurity.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter),\n\t\t\t\tAbstractPreAuthenticatedProcessingFilter.class);\n\t}\n\n\tprivate Consumer<OidcProviderConfiguration.Builder> getProviderConfigurationCustomizer() {\n\t\tConsumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = null;\n\t\tif (this.defaultProviderConfigurationCustomizer != null || this.providerConfigurationCustomizer != null) {\n\t\t\tif (this.defaultProviderConfigurationCustomizer != null) {\n\t\t\t\tproviderConfigurationCustomizer = this.defaultProviderConfigurationCustomizer;\n\t\t\t}\n\t\t\tif (this.providerConfigurationCustomizer != null) {\n\t\t\t\tproviderConfigurationCustomizer = (providerConfigurationCustomizer != null)\n\t\t\t\t\t\t? providerConfigurationCustomizer.andThen(this.providerConfigurationCustomizer)\n\t\t\t\t\t\t: this.providerConfigurationCustomizer;\n\t\t\t}\n\t\t}\n\t\treturn providerConfigurationCustomizer;\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcUserInfoEndpointConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Configurer for OpenID Connect 1.0 UserInfo Endpoint.\n *\n * @author Steve Riesenberg\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OidcConfigurer#userInfoEndpoint\n * @see OidcUserInfoEndpointFilter\n */\npublic final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configurer {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate final List<AuthenticationConverter> userInfoRequestConverters = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationConverter>> userInfoRequestConvertersConsumer = (userInfoRequestConverters) -> {\n\t};\n\n\tprivate final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\tprivate Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {\n\t};\n\n\tprivate AuthenticationSuccessHandler userInfoResponseHandler;\n\n\tprivate AuthenticationFailureHandler errorResponseHandler;\n\n\tprivate Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper;\n\n\t/**\n\t * Restrict for internal use only.\n\t * @param objectPostProcessor an {@code ObjectPostProcessor}\n\t */\n\tOidcUserInfoEndpointConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\tsuper(objectPostProcessor);\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationConverter} used when attempting to extract an UserInfo\n\t * Request from {@link HttpServletRequest} to an instance of\n\t * {@link OidcUserInfoAuthenticationToken} used for authenticating the request.\n\t * @param userInfoRequestConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract an UserInfo Request from {@link HttpServletRequest}\n\t * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcUserInfoEndpointConfigurer userInfoRequestConverter(AuthenticationConverter userInfoRequestConverter) {\n\t\tAssert.notNull(userInfoRequestConverter, \"userInfoRequestConverter cannot be null\");\n\t\tthis.userInfoRequestConverters.add(userInfoRequestConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #userInfoRequestConverter(AuthenticationConverter)\n\t * AuthenticationConverter}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationConverter}.\n\t * @param userInfoRequestConvertersConsumer the {@code Consumer} providing access to\n\t * the {@code List} of default and (optionally) added\n\t * {@link AuthenticationConverter}'s\n\t * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcUserInfoEndpointConfigurer userInfoRequestConverters(\n\t\t\tConsumer<List<AuthenticationConverter>> userInfoRequestConvertersConsumer) {\n\t\tAssert.notNull(userInfoRequestConvertersConsumer, \"userInfoRequestConvertersConsumer cannot be null\");\n\t\tthis.userInfoRequestConvertersConsumer = userInfoRequestConvertersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an {@link AuthenticationProvider} used for authenticating an\n\t * {@link OidcUserInfoAuthenticationToken}.\n\t * @param authenticationProvider an {@link AuthenticationProvider} used for\n\t * authenticating an {@link OidcUserInfoAuthenticationToken}\n\t * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcUserInfoEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProviders.add(authenticationProvider);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@code List} of default and\n\t * (optionally) added {@link #authenticationProvider(AuthenticationProvider)\n\t * AuthenticationProvider}'s allowing the ability to add, remove, or customize a\n\t * specific {@link AuthenticationProvider}.\n\t * @param authenticationProvidersConsumer the {@code Consumer} providing access to the\n\t * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s\n\t * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcUserInfoEndpointConfigurer authenticationProviders(\n\t\t\tConsumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {\n\t\tAssert.notNull(authenticationProvidersConsumer, \"authenticationProvidersConsumer cannot be null\");\n\t\tthis.authenticationProvidersConsumer = authenticationProvidersConsumer;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OidcUserInfoAuthenticationToken} and returning the {@link OidcUserInfo\n\t * UserInfo Response}.\n\t * @param userInfoResponseHandler the {@link AuthenticationSuccessHandler} used for\n\t * handling an {@link OidcUserInfoAuthenticationToken}\n\t * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcUserInfoEndpointConfigurer userInfoResponseHandler(\n\t\t\tAuthenticationSuccessHandler userInfoResponseHandler) {\n\t\tthis.userInfoResponseHandler = userInfoResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for\n\t * handling an {@link OAuth2AuthenticationException}\n\t * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcUserInfoEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {\n\t\tthis.errorResponseHandler = errorResponseHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link Function} used to extract claims from\n\t * {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}\n\t * for the UserInfo response.\n\t *\n\t * <p>\n\t * The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the\n\t * {@link OidcUserInfoAuthenticationToken}, as well as, the following context\n\t * attributes:\n\t * <ul>\n\t * <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the\n\t * bearer token used to make the request.</li>\n\t * <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the\n\t * {@link OidcIdToken} and {@link OAuth2AccessToken} associated with the bearer token\n\t * used to make the request.</li>\n\t * </ul>\n\t * @param userInfoMapper the {@link Function} used to extract claims from\n\t * {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}\n\t * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration\n\t */\n\tpublic OidcUserInfoEndpointConfigurer userInfoMapper(\n\t\t\tFunction<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {\n\t\tthis.userInfoMapper = userInfoMapper;\n\t\treturn this;\n\t}\n\n\t@Override\n\tvoid init(HttpSecurity httpSecurity) {\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\t\tString userInfoEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getOidcUserInfoEndpoint())\n\t\t\t\t: authorizationServerSettings.getOidcUserInfoEndpoint();\n\t\tthis.requestMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, userInfoEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, userInfoEndpointUri));\n\n\t\tList<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);\n\t\tif (!this.authenticationProviders.isEmpty()) {\n\t\t\tauthenticationProviders.addAll(0, this.authenticationProviders);\n\t\t}\n\t\tthis.authenticationProvidersConsumer.accept(authenticationProviders);\n\t\tauthenticationProviders.forEach(\n\t\t\t\t(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));\n\t}\n\n\t@Override\n\tvoid configure(HttpSecurity httpSecurity) {\n\t\tAuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);\n\t\tAuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils\n\t\t\t.getAuthorizationServerSettings(httpSecurity);\n\n\t\tString userInfoEndpointUri = authorizationServerSettings.isMultipleIssuersAllowed()\n\t\t\t\t? OAuth2ConfigurerUtils\n\t\t\t\t\t.withMultipleIssuersPattern(authorizationServerSettings.getOidcUserInfoEndpoint())\n\t\t\t\t: authorizationServerSettings.getOidcUserInfoEndpoint();\n\t\tOidcUserInfoEndpointFilter oidcUserInfoEndpointFilter = new OidcUserInfoEndpointFilter(authenticationManager,\n\t\t\t\tuserInfoEndpointUri);\n\t\tList<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();\n\t\tif (!this.userInfoRequestConverters.isEmpty()) {\n\t\t\tauthenticationConverters.addAll(0, this.userInfoRequestConverters);\n\t\t}\n\t\tthis.userInfoRequestConvertersConsumer.accept(authenticationConverters);\n\t\toidcUserInfoEndpointFilter\n\t\t\t.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));\n\t\tif (this.userInfoResponseHandler != null) {\n\t\t\toidcUserInfoEndpointFilter.setAuthenticationSuccessHandler(this.userInfoResponseHandler);\n\t\t}\n\t\tif (this.errorResponseHandler != null) {\n\t\t\toidcUserInfoEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);\n\t\t}\n\t\thttpSecurity.addFilterAfter(postProcess(oidcUserInfoEndpointFilter), AuthorizationFilter.class);\n\t}\n\n\t@Override\n\tRequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate static List<AuthenticationConverter> createDefaultAuthenticationConverters() {\n\t\tList<AuthenticationConverter> authenticationConverters = new ArrayList<>();\n\n\t\tauthenticationConverters.add((request) -> {\n\t\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t\treturn new OidcUserInfoAuthenticationToken(authentication);\n\t\t});\n\n\t\treturn authenticationConverters;\n\t}\n\n\tprivate List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {\n\t\tList<AuthenticationProvider> authenticationProviders = new ArrayList<>();\n\n\t\tOidcUserInfoAuthenticationProvider oidcUserInfoAuthenticationProvider = new OidcUserInfoAuthenticationProvider(\n\t\t\t\tOAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));\n\t\tif (this.userInfoMapper != null) {\n\t\t\toidcUserInfoAuthenticationProvider.setUserInfoMapper(this.userInfoMapper);\n\t\t}\n\t\tauthenticationProviders.add(oidcUserInfoAuthenticationProvider);\n\n\t\treturn authenticationProviders;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/DPoPAuthenticationConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;\n\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\nimport org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationProvider;\nimport org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationToken;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationFilter;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\n/**\n * An {@link AbstractHttpConfigurer} for OAuth 2.0 Demonstrating Proof of Possession\n * (DPoP) support.\n *\n * @author Joe Grandja\n * @since 6.5\n * @see DPoPAuthenticationProvider\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc9449\">RFC 9449\n * OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>\n */\nfinal class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>\n\t\textends AbstractHttpConfigurer<DPoPAuthenticationConfigurer<B>, B> {\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler;\n\n\t@Override\n\tpublic void configure(B http) {\n\t\tAuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);\n\t\thttp.authenticationProvider(new DPoPAuthenticationProvider(getTokenAuthenticationManager(http)));\n\t\tAuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager,\n\t\t\t\tgetAuthenticationConverter());\n\t\tauthenticationFilter.setRequestMatcher(getRequestMatcher());\n\t\tauthenticationFilter.setSuccessHandler(getAuthenticationSuccessHandler());\n\t\tauthenticationFilter.setFailureHandler(getAuthenticationFailureHandler());\n\t\tauthenticationFilter.setSecurityContextRepository(new RequestAttributeSecurityContextRepository());\n\t\tauthenticationFilter = postProcess(authenticationFilter);\n\t\thttp.addFilter(authenticationFilter);\n\t}\n\n\tprivate AuthenticationManager getTokenAuthenticationManager(B http) {\n\t\tOAuth2ResourceServerConfigurer<B> resourceServerConfigurer = http\n\t\t\t.getConfigurer(OAuth2ResourceServerConfigurer.class);\n\t\tfinal AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver = resourceServerConfigurer\n\t\t\t.getAuthenticationManagerResolver();\n\t\tif (authenticationManagerResolver == null) {\n\t\t\treturn resourceServerConfigurer.getAuthenticationManager(http);\n\t\t}\n\t\treturn (authentication) -> {\n\t\t\tRequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n\t\t\tServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;\n\t\t\tAuthenticationManager authenticationManager = authenticationManagerResolver\n\t\t\t\t.resolve(servletRequestAttributes.getRequest());\n\t\t\treturn authenticationManager.authenticate(authentication);\n\t\t};\n\t}\n\n\tprivate RequestMatcher getRequestMatcher() {\n\t\tif (this.requestMatcher == null) {\n\t\t\tthis.requestMatcher = new DPoPRequestMatcher();\n\t\t}\n\t\treturn this.requestMatcher;\n\t}\n\n\tprivate AuthenticationConverter getAuthenticationConverter() {\n\t\tif (this.authenticationConverter == null) {\n\t\t\tthis.authenticationConverter = new DPoPAuthenticationConverter();\n\t\t}\n\t\treturn this.authenticationConverter;\n\t}\n\n\tprivate AuthenticationSuccessHandler getAuthenticationSuccessHandler() {\n\t\tif (this.authenticationSuccessHandler == null) {\n\t\t\tthis.authenticationSuccessHandler = (request, response, authentication) -> {\n\t\t\t\t// No-op - will continue on filter chain\n\t\t\t};\n\t\t}\n\t\treturn this.authenticationSuccessHandler;\n\t}\n\n\tprivate AuthenticationFailureHandler getAuthenticationFailureHandler() {\n\t\tif (this.authenticationFailureHandler == null) {\n\t\t\tthis.authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(\n\t\t\t\t\tnew DPoPAuthenticationEntryPoint());\n\t\t}\n\t\treturn this.authenticationFailureHandler;\n\t}\n\n\tprivate static final class DPoPRequestMatcher implements RequestMatcher {\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\tString authorization = request.getHeader(HttpHeaders.AUTHORIZATION);\n\t\t\tif (!StringUtils.hasText(authorization)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn StringUtils.startsWithIgnoreCase(authorization, OAuth2AccessToken.TokenType.DPOP.getValue());\n\t\t}\n\n\t}\n\n\tprivate static final class DPoPAuthenticationConverter implements AuthenticationConverter {\n\n\t\tprivate static final Pattern AUTHORIZATION_PATTERN = Pattern.compile(\"^DPoP (?<token>[a-zA-Z0-9-._~+/]+=*)$\",\n\t\t\t\tPattern.CASE_INSENSITIVE);\n\n\t\t@Override\n\t\tpublic Authentication convert(HttpServletRequest request) {\n\t\t\tList<String> authorizationList = Collections.list(request.getHeaders(HttpHeaders.AUTHORIZATION));\n\t\t\tif (CollectionUtils.isEmpty(authorizationList)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (authorizationList.size() != 1) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\t\"Found multiple Authorization headers.\", null);\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\t\t\tString authorization = authorizationList.get(0);\n\t\t\tif (!StringUtils.startsWithIgnoreCase(authorization, OAuth2AccessToken.TokenType.DPOP.getValue())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tMatcher matcher = AUTHORIZATION_PATTERN.matcher(authorization);\n\t\t\tif (!matcher.matches()) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, \"DPoP access token is malformed.\",\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\t\t\tString accessToken = matcher.group(\"token\");\n\t\t\tList<String> dPoPProofList = Collections\n\t\t\t\t.list(request.getHeaders(OAuth2AccessToken.TokenType.DPOP.getValue()));\n\t\t\tif (CollectionUtils.isEmpty(dPoPProofList) || dPoPProofList.size() != 1) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\t\"DPoP proof is missing or invalid.\", null);\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\t\t\tString dPoPProof = dPoPProofList.get(0);\n\t\t\treturn new DPoPAuthenticationToken(accessToken, dPoPProof, request.getMethod(),\n\t\t\t\t\trequest.getRequestURL().toString());\n\t\t}\n\n\t}\n\n\tprivate static final class DPoPAuthenticationEntryPoint implements AuthenticationEntryPoint {\n\n\t\t@Override\n\t\tpublic void commence(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthenticationException authenticationException) {\n\t\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\t\tif (authenticationException instanceof OAuth2AuthenticationException oauth2AuthenticationException) {\n\t\t\t\tOAuth2Error error = oauth2AuthenticationException.getError();\n\t\t\t\tparameters.put(OAuth2ParameterNames.ERROR, error.getErrorCode());\n\t\t\t\tif (StringUtils.hasText(error.getDescription())) {\n\t\t\t\t\tparameters.put(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription());\n\t\t\t\t}\n\t\t\t\tif (StringUtils.hasText(error.getUri())) {\n\t\t\t\t\tparameters.put(OAuth2ParameterNames.ERROR_URI, error.getUri());\n\t\t\t\t}\n\t\t\t}\n\t\t\tparameters.put(\"algs\",\n\t\t\t\t\tJwsAlgorithms.RS256 + \" \" + JwsAlgorithms.RS384 + \" \" + JwsAlgorithms.RS512 + \" \"\n\t\t\t\t\t\t\t+ JwsAlgorithms.PS256 + \" \" + JwsAlgorithms.PS384 + \" \" + JwsAlgorithms.PS512 + \" \"\n\t\t\t\t\t\t\t+ JwsAlgorithms.ES256 + \" \" + JwsAlgorithms.ES384 + \" \" + JwsAlgorithms.ES512);\n\t\t\tString wwwAuthenticate = toWWWAuthenticateHeader(parameters);\n\t\t\tresponse.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);\n\t\t\tresponse.setStatus(HttpStatus.UNAUTHORIZED.value());\n\t\t}\n\n\t\tprivate static String toWWWAuthenticateHeader(Map<String, String> parameters) {\n\t\t\tStringBuilder wwwAuthenticate = new StringBuilder();\n\t\t\twwwAuthenticate.append(OAuth2AccessToken.TokenType.DPOP.getValue());\n\t\t\tif (!parameters.isEmpty()) {\n\t\t\t\twwwAuthenticate.append(\" \");\n\t\t\t\tint i = 0;\n\t\t\t\tfor (Map.Entry<String, String> entry : parameters.entrySet()) {\n\t\t\t\t\twwwAuthenticate.append(entry.getKey()).append(\"=\\\"\").append(entry.getValue()).append(\"\\\"\");\n\t\t\t\t\tif (i++ != parameters.size() - 1) {\n\t\t\t\t\t\twwwAuthenticate.append(\", \");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn wwwAuthenticate.toString();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.server.resource.OAuth2ProtectedResourceMetadata;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;\nimport org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;\nimport org.springframework.security.oauth2.server.resource.web.OAuth2ProtectedResourceMetadataFilter;\nimport org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl;\nimport org.springframework.security.web.access.DelegatingAccessDeniedHandler;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.csrf.CsrfException;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.accept.HeaderContentNegotiationStrategy;\n\n/**\n *\n * An {@link AbstractHttpConfigurer} for OAuth 2.0 Resource Server Support.\n *\n * By default, this wires a {@link BearerTokenAuthenticationFilter}, which can be used to\n * parse the request for bearer tokens and make an authentication attempt.\n *\n * <p>\n * The following configuration options are available:\n *\n * <ul>\n * <li>{@link #accessDeniedHandler(AccessDeniedHandler)}</li> - customizes how access\n * denied errors are handled\n * <li>{@link #authenticationEntryPoint(AuthenticationEntryPoint)}</li> - customizes how\n * authentication failures are handled\n * <li>{@link #bearerTokenResolver(BearerTokenResolver)} - customizes how to resolve a\n * bearer token from the request</li>\n * <li>{@link #jwt(Customizer)} - enables Jwt-encoded bearer token support</li>\n * <li>{@link #opaqueToken(Customizer)} - enables opaque bearer token support</li>\n * </ul>\n *\n * <p>\n * When using {@link #jwt(Customizer)}, either\n *\n * <ul>\n * <li>supply a Jwk Set Uri via {@link JwtConfigurer#jwkSetUri}, or</li>\n * <li>supply a {@link JwtDecoder} instance via {@link JwtConfigurer#decoder}, or</li>\n * <li>expose a {@link JwtDecoder} bean</li>\n * </ul>\n *\n * Also with {@link #jwt(Customizer)} consider\n *\n * <ul>\n * <li>customizing the conversion from a {@link Jwt} to an\n * {@link org.springframework.security.core.Authentication} with\n * {@link JwtConfigurer#jwtAuthenticationConverter(Converter)}</li>\n * </ul>\n *\n * <p>\n * When using {@link #opaqueToken(Customizer)}, supply an introspection endpoint with its\n * client credentials and an OpaqueTokenAuthenticationConverter\n * </p>\n *\n * <h2>Security Filters</h2>\n *\n * The following {@code Filter}s are populated when {@link #jwt(Customizer)} is\n * configured:\n *\n * <ul>\n * <li>{@link BearerTokenAuthenticationFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * The following shared objects are populated:\n *\n * <ul>\n * <li>{@link SessionCreationPolicy} (optional)</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link AuthenticationManager}</li>\n * </ul>\n *\n * @author Josh Cummings\n * @author Evgeniy Cheban\n * @author Jerome Wacongne &lt;ch4mp@c4-soft.com&gt;\n * @since 5.1\n * @see BearerTokenAuthenticationFilter\n * @see JwtAuthenticationProvider\n * @see NimbusJwtDecoder\n * @see AbstractHttpConfigurer\n */\npublic final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {\n\n\tprivate static final boolean dPoPAuthenticationAvailable;\n\n\tstatic {\n\t\tClassLoader classLoader = OAuth2ResourceServerConfigurer.class.getClassLoader();\n\t\tdPoPAuthenticationAvailable = ClassUtils\n\t\t\t.isPresent(\"org.springframework.security.oauth2.jwt.DPoPProofJwtDecoderFactory\", classLoader);\n\t}\n\n\tprivate static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher(\n\t\t\t\"X-Requested-With\", \"XMLHttpRequest\");\n\n\tprivate final ApplicationContext context;\n\n\tprivate AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate JwtConfigurer jwtConfigurer;\n\n\tprivate OpaqueTokenConfigurer opaqueTokenConfigurer;\n\n\tprivate final ProtectedResourceMetadataConfigurer protectedResourceMetadataConfigurer = new ProtectedResourceMetadataConfigurer();\n\n\tprivate AccessDeniedHandler accessDeniedHandler = new DelegatingAccessDeniedHandler(\n\t\t\tnew LinkedHashMap<>(Map.of(CsrfException.class, new AccessDeniedHandlerImpl())),\n\t\t\tnew BearerTokenAccessDeniedHandler());\n\n\tprivate AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();\n\n\tprivate BearerTokenRequestMatcher requestMatcher = new BearerTokenRequestMatcher();\n\n\tpublic OAuth2ResourceServerConfigurer(ApplicationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tthis.context = context;\n\t}\n\n\tpublic OAuth2ResourceServerConfigurer<H> accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {\n\t\tAssert.notNull(accessDeniedHandler, \"accessDeniedHandler cannot be null\");\n\t\tthis.accessDeniedHandler = accessDeniedHandler;\n\t\treturn this;\n\t}\n\n\tpublic OAuth2ResourceServerConfigurer<H> authenticationEntryPoint(AuthenticationEntryPoint entryPoint) {\n\t\tAssert.notNull(entryPoint, \"entryPoint cannot be null\");\n\t\tthis.authenticationEntryPoint = entryPoint;\n\t\treturn this;\n\t}\n\n\tpublic OAuth2ResourceServerConfigurer<H> authenticationManagerResolver(\n\t\t\tAuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {\n\t\tAssert.notNull(authenticationManagerResolver, \"authenticationManagerResolver cannot be null\");\n\t\tthis.authenticationManagerResolver = authenticationManagerResolver;\n\t\treturn this;\n\t}\n\n\tpublic OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {\n\t\tAssert.notNull(bearerTokenResolver, \"bearerTokenResolver cannot be null\");\n\t\tthis.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} to use.\n\t * @param authenticationConverter the authentication converter\n\t * @return the {@link OAuth2ResourceServerConfigurer} for further configuration\n\t * @since 7.0\n\t */\n\tpublic OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Enables Jwt-encoded bearer token support.\n\t * @param jwtCustomizer the {@link Customizer} to provide more options for the\n\t * {@link JwtConfigurer}\n\t * @return the {@link OAuth2ResourceServerConfigurer} for further customizations\n\t */\n\tpublic OAuth2ResourceServerConfigurer<H> jwt(Customizer<JwtConfigurer> jwtCustomizer) {\n\t\tif (this.jwtConfigurer == null) {\n\t\t\tthis.jwtConfigurer = new JwtConfigurer(this.context);\n\t\t}\n\t\tjwtCustomizer.customize(this.jwtConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Enables opaque bearer token support.\n\t * @param opaqueTokenCustomizer the {@link Customizer} to provide more options for the\n\t * {@link OpaqueTokenConfigurer}\n\t * @return the {@link OAuth2ResourceServerConfigurer} for further customizations\n\t */\n\tpublic OAuth2ResourceServerConfigurer<H> opaqueToken(Customizer<OpaqueTokenConfigurer> opaqueTokenCustomizer) {\n\t\tif (this.opaqueTokenConfigurer == null) {\n\t\t\tthis.opaqueTokenConfigurer = new OpaqueTokenConfigurer(this.context);\n\t\t}\n\t\topaqueTokenCustomizer.customize(this.opaqueTokenConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configure OAuth 2.0 Protected Resource Metadata.\n\t * @param protectedResourceMetadataCustomizer the {@link Customizer} to provide more\n\t * options for the {@link ProtectedResourceMetadataConfigurer}\n\t * @return the {@link OAuth2ResourceServerConfigurer} for further customizations\n\t */\n\tpublic OAuth2ResourceServerConfigurer<H> protectedResourceMetadata(\n\t\t\tCustomizer<ProtectedResourceMetadataConfigurer> protectedResourceMetadataCustomizer) {\n\t\tprotectedResourceMetadataCustomizer.customize(this.protectedResourceMetadataConfigurer);\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\tvalidateConfiguration();\n\t\tregisterDefaultAccessDeniedHandler(http);\n\t\tregisterDefaultEntryPoint(http);\n\t\tregisterDefaultCsrfOverride(http);\n\t\tAuthenticationProvider authenticationProvider = getAuthenticationProvider();\n\t\tif (authenticationProvider != null) {\n\t\t\thttp.authenticationProvider(authenticationProvider);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tAuthenticationManagerResolver resolver = this.authenticationManagerResolver;\n\t\tif (resolver == null) {\n\t\t\tAuthenticationManager authenticationManager = getAuthenticationManager(http);\n\t\t\tresolver = (request) -> authenticationManager;\n\t\t}\n\n\t\tAuthenticationConverter converter = getAuthenticationConverter();\n\t\tthis.requestMatcher.setAuthenticationConverter(converter);\n\t\tBearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver, converter);\n\t\tfilter.setAuthenticationEntryPoint(this.authenticationEntryPoint);\n\t\tfilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tfilter = postProcess(filter);\n\t\thttp.addFilter(filter);\n\n\t\tif (dPoPAuthenticationAvailable) {\n\t\t\tDPoPAuthenticationConfigurer<H> dPoPAuthenticationConfigurer = new DPoPAuthenticationConfigurer<>();\n\t\t\tdPoPAuthenticationConfigurer.configure(http);\n\t\t}\n\n\t\tOAuth2ProtectedResourceMetadataFilter protectedResourceMetadataFilter = new OAuth2ProtectedResourceMetadataFilter();\n\t\tif (this.protectedResourceMetadataConfigurer.protectedResourceMetadataCustomizer != null) {\n\t\t\tprotectedResourceMetadataFilter.setProtectedResourceMetadataCustomizer(\n\t\t\t\t\tthis.protectedResourceMetadataConfigurer.protectedResourceMetadataCustomizer);\n\t\t}\n\t\tprotectedResourceMetadataFilter = postProcess(protectedResourceMetadataFilter);\n\t\thttp.addFilterBefore(protectedResourceMetadataFilter, AbstractPreAuthenticatedProcessingFilter.class);\n\t}\n\n\tprivate void validateConfiguration() {\n\t\tif (this.authenticationManagerResolver == null) {\n\t\t\tAssert.state(this.jwtConfigurer != null || this.opaqueTokenConfigurer != null,\n\t\t\t\t\t\"Jwt and Opaque Token are the only supported formats for bearer tokens \"\n\t\t\t\t\t\t\t+ \"in Spring Security and neither was found. Make sure to configure JWT \"\n\t\t\t\t\t\t\t+ \"via http.oauth2ResourceServer().jwt() or Opaque Tokens via \"\n\t\t\t\t\t\t\t+ \"http.oauth2ResourceServer().opaqueToken().\");\n\t\t\tAssert.state(this.jwtConfigurer == null || this.opaqueTokenConfigurer == null,\n\t\t\t\t\t\"Spring Security only supports JWTs or Opaque Tokens, not both at the \" + \"same time.\");\n\t\t}\n\t\telse {\n\t\t\tAssert.state(this.jwtConfigurer == null && this.opaqueTokenConfigurer == null,\n\t\t\t\t\t\"If an authenticationManagerResolver() is configured, then it takes \"\n\t\t\t\t\t\t\t+ \"precedence over any jwt() or opaqueToken() configuration.\");\n\t\t}\n\t}\n\n\tprivate void registerDefaultAccessDeniedHandler(H http) {\n\t\tExceptionHandlingConfigurer<H> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptionHandling != null) {\n\t\t\texceptionHandling.defaultAccessDeniedHandlerFor(this.accessDeniedHandler, this.requestMatcher);\n\t\t}\n\t}\n\n\tprivate void registerDefaultEntryPoint(H http) {\n\t\tExceptionHandlingConfigurer<H> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptionHandling != null) {\n\t\t\tContentNegotiationStrategy contentNegotiationStrategy = http\n\t\t\t\t.getSharedObject(ContentNegotiationStrategy.class);\n\t\t\tif (contentNegotiationStrategy == null) {\n\t\t\t\tcontentNegotiationStrategy = new HeaderContentNegotiationStrategy();\n\t\t\t}\n\t\t\tMediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,\n\t\t\t\t\tMediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,\n\t\t\t\t\tMediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,\n\t\t\t\t\tMediaType.TEXT_XML);\n\t\t\trestMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\t\tMediaTypeRequestMatcher allMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.ALL);\n\t\t\tallMatcher.setUseEquals(true);\n\t\t\tRequestMatcher notHtmlMatcher = new NegatedRequestMatcher(\n\t\t\t\t\tnew MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.TEXT_HTML));\n\t\t\tRequestMatcher restNotHtmlMatcher = new AndRequestMatcher(Arrays.asList(notHtmlMatcher, restMatcher));\n\t\t\tRequestMatcher preferredMatcher = new OrRequestMatcher(\n\t\t\t\t\tArrays.asList(this.requestMatcher, X_REQUESTED_WITH, restNotHtmlMatcher, allMatcher));\n\t\t\texceptionHandling.defaultAuthenticationEntryPointFor(this.authenticationEntryPoint, preferredMatcher);\n\t\t\texceptionHandling.defaultDeniedHandlerForMissingAuthority(\n\t\t\t\t\t(ep) -> ep.addEntryPointFor(this.authenticationEntryPoint, preferredMatcher),\n\t\t\t\t\tFactorGrantedAuthority.BEARER_AUTHORITY);\n\t\t}\n\t}\n\n\tprivate void registerDefaultCsrfOverride(H http) {\n\t\tCsrfConfigurer<H> csrf = http.getConfigurer(CsrfConfigurer.class);\n\t\tif (csrf != null) {\n\t\t\tcsrf.ignoringRequestMatchers(this.requestMatcher);\n\t\t}\n\t}\n\n\tAuthenticationProvider getAuthenticationProvider() {\n\t\tif (this.jwtConfigurer != null) {\n\t\t\treturn this.jwtConfigurer.getAuthenticationProvider();\n\t\t}\n\t\tif (this.opaqueTokenConfigurer != null) {\n\t\t\treturn this.opaqueTokenConfigurer.getAuthenticationProvider();\n\t\t}\n\t\treturn null;\n\t}\n\n\tAuthenticationManager getAuthenticationManager(H http) {\n\t\tif (this.jwtConfigurer != null) {\n\t\t\treturn this.jwtConfigurer.getAuthenticationManager(http);\n\t\t}\n\t\tif (this.opaqueTokenConfigurer != null) {\n\t\t\treturn this.opaqueTokenConfigurer.getAuthenticationManager(http);\n\t\t}\n\t\treturn http.getSharedObject(AuthenticationManager.class);\n\t}\n\n\tAuthenticationManagerResolver<HttpServletRequest> getAuthenticationManagerResolver() {\n\t\treturn this.authenticationManagerResolver;\n\t}\n\n\tAuthenticationConverter getAuthenticationConverter() {\n\t\tif (this.authenticationConverter != null) {\n\t\t\treturn this.authenticationConverter;\n\t\t}\n\t\tif (this.context.getBeanNamesForType(AuthenticationConverter.class).length > 0) {\n\t\t\tthis.authenticationConverter = this.context.getBean(AuthenticationConverter.class);\n\t\t}\n\t\telse if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {\n\t\t\tBearerTokenResolver bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);\n\t\t\tthis.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);\n\t\t}\n\t\telse {\n\t\t\tthis.authenticationConverter = new BearerTokenAuthenticationConverter();\n\t\t}\n\t\treturn this.authenticationConverter;\n\t}\n\n\tBearerTokenResolver getBearerTokenResolver() {\n\t\tAuthenticationConverter authenticationConverter = getAuthenticationConverter();\n\t\tif (authenticationConverter instanceof OAuth2ResourceServerConfigurer.BearerTokenResolverHoldingAuthenticationConverter bearer) {\n\t\t\treturn bearer.bearerTokenResolver;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic class JwtConfigurer {\n\n\t\tprivate final ApplicationContext context;\n\n\t\tprivate AuthenticationManager authenticationManager;\n\n\t\tprivate JwtDecoder decoder;\n\n\t\tprivate Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter;\n\n\t\tJwtConfigurer(ApplicationContext context) {\n\t\t\tthis.context = context;\n\t\t}\n\n\t\tpublic JwtConfigurer authenticationManager(AuthenticationManager authenticationManager) {\n\t\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic JwtConfigurer decoder(JwtDecoder decoder) {\n\t\t\tthis.decoder = decoder;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic JwtConfigurer jwkSetUri(String uri) {\n\t\t\tthis.decoder = NimbusJwtDecoder.withJwkSetUri(uri).build();\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic JwtConfigurer jwtAuthenticationConverter(\n\t\t\t\tConverter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter) {\n\t\t\tthis.jwtAuthenticationConverter = jwtAuthenticationConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\tConverter<Jwt, ? extends AbstractAuthenticationToken> getJwtAuthenticationConverter() {\n\t\t\tif (this.jwtAuthenticationConverter == null) {\n\t\t\t\tif (this.context.getBeanNamesForType(JwtAuthenticationConverter.class).length > 0) {\n\t\t\t\t\tthis.jwtAuthenticationConverter = this.context.getBean(JwtAuthenticationConverter.class);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.jwtAuthenticationConverter = new JwtAuthenticationConverter();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this.jwtAuthenticationConverter;\n\t\t}\n\n\t\tJwtDecoder getJwtDecoder() {\n\t\t\tif (this.decoder == null) {\n\t\t\t\treturn this.context.getBean(JwtDecoder.class);\n\t\t\t}\n\t\t\treturn this.decoder;\n\t\t}\n\n\t\tAuthenticationProvider getAuthenticationProvider() {\n\t\t\tif (this.authenticationManager != null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tJwtDecoder decoder = getJwtDecoder();\n\t\t\tConverter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter = getJwtAuthenticationConverter();\n\t\t\tJwtAuthenticationProvider provider = new JwtAuthenticationProvider(decoder);\n\t\t\tprovider.setJwtAuthenticationConverter(jwtAuthenticationConverter);\n\t\t\treturn postProcess(provider);\n\t\t}\n\n\t\tAuthenticationManager getAuthenticationManager(H http) {\n\t\t\tif (this.authenticationManager != null) {\n\t\t\t\treturn this.authenticationManager;\n\t\t\t}\n\t\t\treturn http.getSharedObject(AuthenticationManager.class);\n\t\t}\n\n\t}\n\n\tpublic class OpaqueTokenConfigurer {\n\n\t\tprivate final ApplicationContext context;\n\n\t\tprivate AuthenticationManager authenticationManager;\n\n\t\tprivate String introspectionUri;\n\n\t\tprivate String clientId;\n\n\t\tprivate String clientSecret;\n\n\t\tprivate Supplier<OpaqueTokenIntrospector> introspector;\n\n\t\tprivate OpaqueTokenAuthenticationConverter authenticationConverter;\n\n\t\tOpaqueTokenConfigurer(ApplicationContext context) {\n\t\t\tthis.context = context;\n\t\t}\n\n\t\tpublic OpaqueTokenConfigurer authenticationManager(AuthenticationManager authenticationManager) {\n\t\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic OpaqueTokenConfigurer introspectionUri(String introspectionUri) {\n\t\t\tAssert.notNull(introspectionUri, \"introspectionUri cannot be null\");\n\t\t\tthis.introspectionUri = introspectionUri;\n\t\t\tthis.introspector = () -> SpringOpaqueTokenIntrospector.withIntrospectionUri(this.introspectionUri)\n\t\t\t\t.clientId(this.clientId)\n\t\t\t\t.clientSecret(this.clientSecret)\n\t\t\t\t.build();\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic OpaqueTokenConfigurer introspectionClientCredentials(String clientId, String clientSecret) {\n\t\t\tAssert.notNull(clientId, \"clientId cannot be null\");\n\t\t\tAssert.notNull(clientSecret, \"clientSecret cannot be null\");\n\t\t\tthis.clientId = clientId;\n\t\t\tthis.clientSecret = clientSecret;\n\t\t\tthis.introspector = () -> SpringOpaqueTokenIntrospector.withIntrospectionUri(this.introspectionUri)\n\t\t\t\t.clientId(this.clientId)\n\t\t\t\t.clientSecret(this.clientSecret)\n\t\t\t\t.build();\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic OpaqueTokenConfigurer introspector(OpaqueTokenIntrospector introspector) {\n\t\t\tAssert.notNull(introspector, \"introspector cannot be null\");\n\t\t\tthis.introspector = () -> introspector;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic OpaqueTokenConfigurer authenticationConverter(\n\t\t\t\tOpaqueTokenAuthenticationConverter authenticationConverter) {\n\t\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\tOpaqueTokenIntrospector getIntrospector() {\n\t\t\tif (this.introspector != null) {\n\t\t\t\treturn this.introspector.get();\n\t\t\t}\n\t\t\treturn this.context.getBean(OpaqueTokenIntrospector.class);\n\t\t}\n\n\t\tOpaqueTokenAuthenticationConverter getAuthenticationConverter() {\n\t\t\tif (this.authenticationConverter != null) {\n\t\t\t\treturn this.authenticationConverter;\n\t\t\t}\n\t\t\tif (this.context.getBeanNamesForType(OpaqueTokenAuthenticationConverter.class).length > 0) {\n\t\t\t\treturn this.context.getBean(OpaqueTokenAuthenticationConverter.class);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tAuthenticationProvider getAuthenticationProvider() {\n\t\t\tif (this.authenticationManager != null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tOpaqueTokenIntrospector introspector = getIntrospector();\n\t\t\tOpaqueTokenAuthenticationProvider opaqueTokenAuthenticationProvider = new OpaqueTokenAuthenticationProvider(\n\t\t\t\t\tintrospector);\n\t\t\tOpaqueTokenAuthenticationConverter authenticationConverter = getAuthenticationConverter();\n\t\t\tif (authenticationConverter != null) {\n\t\t\t\topaqueTokenAuthenticationProvider.setAuthenticationConverter(authenticationConverter);\n\t\t\t}\n\t\t\treturn opaqueTokenAuthenticationProvider;\n\t\t}\n\n\t\tAuthenticationManager getAuthenticationManager(H http) {\n\t\t\tif (this.authenticationManager != null) {\n\t\t\t\treturn this.authenticationManager;\n\t\t\t}\n\t\t\treturn http.getSharedObject(AuthenticationManager.class);\n\t\t}\n\n\t}\n\n\tpublic static final class ProtectedResourceMetadataConfigurer {\n\n\t\tprivate Consumer<OAuth2ProtectedResourceMetadata.Builder> protectedResourceMetadataCustomizer;\n\n\t\tprivate ProtectedResourceMetadataConfigurer() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code Consumer} providing access to the\n\t\t * {@link OAuth2ProtectedResourceMetadata.Builder} allowing the ability to\n\t\t * customize the claims of the Resource Server's configuration.\n\t\t * @param protectedResourceMetadataCustomizer the {@code Consumer} providing\n\t\t * access to the {@link OAuth2ProtectedResourceMetadata.Builder}\n\t\t * @return the {@link ProtectedResourceMetadataConfigurer} for further\n\t\t * configuration\n\t\t */\n\t\tpublic ProtectedResourceMetadataConfigurer protectedResourceMetadataCustomizer(\n\t\t\t\tConsumer<OAuth2ProtectedResourceMetadata.Builder> protectedResourceMetadataCustomizer) {\n\t\t\tthis.protectedResourceMetadataCustomizer = protectedResourceMetadataCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tprivate static final class BearerTokenRequestMatcher implements RequestMatcher {\n\n\t\tprivate AuthenticationConverter authenticationConverter;\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\ttry {\n\t\t\t\treturn this.authenticationConverter.convert(request) != null;\n\t\t\t}\n\t\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tvoid setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t}\n\n\t}\n\n\tprivate static final class BearerTokenResolverHoldingAuthenticationConverter implements AuthenticationConverter {\n\n\t\tprivate final BearerTokenResolver bearerTokenResolver;\n\n\t\tprivate final AuthenticationConverter authenticationConverter;\n\n\t\tBearerTokenResolverHoldingAuthenticationConverter(BearerTokenResolver bearerTokenResolver) {\n\t\t\tthis.bearerTokenResolver = bearerTokenResolver;\n\t\t\tBearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();\n\t\t\tauthenticationConverter.setBearerTokenResolver(bearerTokenResolver);\n\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication convert(HttpServletRequest request) {\n\t\t\treturn this.authenticationConverter.convert(request);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.ott;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.InMemoryOneTimeTokenService;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationProvider;\nimport org.springframework.security.authentication.ott.OneTimeTokenService;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver;\nimport org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;\nimport org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationConverter;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationFilter;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AbstractHttpConfigurer} for One-Time Token Login.\n *\n * <p>\n * One-Time Token Login provides an application with the capability to have users log in\n * by obtaining a single-use token out of band, for example through email.\n *\n * <p>\n * Defaults are provided for all configuration options, with the only required\n * configuration being\n * {@link #tokenGenerationSuccessHandler(OneTimeTokenGenerationSuccessHandler)}.\n * Alternatively, a {@link OneTimeTokenGenerationSuccessHandler} {@code @Bean} may be\n * registered instead.\n *\n * <h2>Security Filters</h2>\n *\n * The following {@code Filter}s are populated:\n *\n * <ul>\n * <li>{@link DefaultOneTimeTokenSubmitPageGeneratingFilter}</li>\n * <li>{@link GenerateOneTimeTokenFilter}</li>\n * <li>{@link OneTimeTokenAuthenticationFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not\n * configured and {@code DefaultLoginPageGeneratingFilter} is available, then a default\n * login page will be made available</li>\n * </ul>\n *\n * @author Marcus Da Coregio\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n * @see HttpSecurity#oneTimeTokenLogin(Customizer)\n * @see DefaultOneTimeTokenSubmitPageGeneratingFilter\n * @see GenerateOneTimeTokenFilter\n * @see OneTimeTokenAuthenticationFilter\n * @see AbstractAuthenticationFilterConfigurer\n */\npublic final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>> extends\n\t\tAbstractAuthenticationFilterConfigurer<H, OneTimeTokenLoginConfigurer<H>, OneTimeTokenAuthenticationFilter> {\n\n\tprivate final ApplicationContext context;\n\n\tprivate OneTimeTokenService oneTimeTokenService;\n\n\tprivate String defaultSubmitPageUrl = DefaultOneTimeTokenSubmitPageGeneratingFilter.DEFAULT_SUBMIT_PAGE_URL;\n\n\tprivate boolean submitPageEnabled = true;\n\n\tprivate String loginProcessingUrl = OneTimeTokenAuthenticationFilter.DEFAULT_LOGIN_PROCESSING_URL;\n\n\tprivate String tokenGeneratingUrl = GenerateOneTimeTokenFilter.DEFAULT_GENERATE_URL;\n\n\tprivate OneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler;\n\n\tprivate AuthenticationProvider authenticationProvider;\n\n\tprivate GenerateOneTimeTokenRequestResolver requestResolver;\n\n\tpublic OneTimeTokenLoginConfigurer(ApplicationContext context) {\n\t\tsuper(new OneTimeTokenAuthenticationFilter(), null);\n\t\tthis.context = context;\n\t}\n\n\t@Override\n\tpublic void init(H http) {\n\t\tif (getLoginProcessingUrl() == null) {\n\t\t\tloginProcessingUrl(OneTimeTokenAuthenticationFilter.DEFAULT_LOGIN_PROCESSING_URL);\n\t\t}\n\t\tsuper.init(http);\n\t\tAuthenticationProvider authenticationProvider = getAuthenticationProvider();\n\t\thttp.authenticationProvider(postProcess(authenticationProvider));\n\t\tintiDefaultLoginFilter(http);\n\t\tExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptions != null) {\n\t\t\tAuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint();\n\t\t\tRequestMatcher requestMatcher = getAuthenticationEntryPointMatcher(http);\n\t\t\texceptions.defaultDeniedHandlerForMissingAuthority((ep) -> ep.addEntryPointFor(entryPoint, requestMatcher),\n\t\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY);\n\t\t}\n\t}\n\n\tprivate void intiDefaultLoginFilter(H http) {\n\t\tDefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http\n\t\t\t.getSharedObject(DefaultLoginPageGeneratingFilter.class);\n\t\tif (loginPageGeneratingFilter == null || isCustomLoginPage()) {\n\t\t\treturn;\n\t\t}\n\t\tloginPageGeneratingFilter.setOneTimeTokenEnabled(true);\n\t\tloginPageGeneratingFilter.setOneTimeTokenGenerationUrl(this.tokenGeneratingUrl);\n\n\t\tif (!StringUtils.hasText(loginPageGeneratingFilter.getLoginPageUrl())) {\n\t\t\tloginPageGeneratingFilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\t\tloginPageGeneratingFilter.setFailureUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + \"?\"\n\t\t\t\t\t+ DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME);\n\t\t\tloginPageGeneratingFilter\n\t\t\t\t.setLogoutSuccessUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + \"?logout\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tsuper.configure(http);\n\t\tconfigureSubmitPage(http);\n\t\tconfigureOttGenerateFilter(http);\n\t}\n\n\tprivate void configureOttGenerateFilter(H http) {\n\t\tGenerateOneTimeTokenFilter generateFilter = new GenerateOneTimeTokenFilter(getOneTimeTokenService(),\n\t\t\t\tgetOneTimeTokenGenerationSuccessHandler());\n\t\tgenerateFilter.setRequestMatcher(getRequestMatcherBuilder().matcher(HttpMethod.POST, this.tokenGeneratingUrl));\n\t\tgenerateFilter.setRequestResolver(getGenerateRequestResolver());\n\t\thttp.addFilter(postProcess(generateFilter));\n\t\thttp.addFilter(DefaultResourcesFilter.css());\n\t}\n\n\tprivate OneTimeTokenGenerationSuccessHandler getOneTimeTokenGenerationSuccessHandler() {\n\t\tif (this.oneTimeTokenGenerationSuccessHandler == null) {\n\t\t\tthis.oneTimeTokenGenerationSuccessHandler = this.context\n\t\t\t\t.getBeanProvider(OneTimeTokenGenerationSuccessHandler.class)\n\t\t\t\t.getIfUnique();\n\t\t}\n\t\tif (this.oneTimeTokenGenerationSuccessHandler == null) {\n\t\t\tthrow new IllegalStateException(\"\"\"\n\t\t\t\t\tA OneTimeTokenGenerationSuccessHandler is required to enable oneTimeTokenLogin().\n\t\t\t\t\tPlease provide it as a bean or pass it to the oneTimeTokenLogin() DSL.\n\t\t\t\t\t\"\"\");\n\t\t}\n\t\treturn this.oneTimeTokenGenerationSuccessHandler;\n\t}\n\n\tprivate void configureSubmitPage(H http) {\n\t\tif (!this.submitPageEnabled) {\n\t\t\treturn;\n\t\t}\n\t\tDefaultOneTimeTokenSubmitPageGeneratingFilter submitPage = new DefaultOneTimeTokenSubmitPageGeneratingFilter();\n\t\tsubmitPage.setResolveHiddenInputs(this::hiddenInputs);\n\t\tsubmitPage.setRequestMatcher(getRequestMatcherBuilder().matcher(HttpMethod.GET, this.defaultSubmitPageUrl));\n\t\tsubmitPage.setLoginProcessingUrl(this.getLoginProcessingUrl());\n\t\thttp.addFilter(postProcess(submitPage));\n\t}\n\n\tprivate AuthenticationProvider getAuthenticationProvider() {\n\t\tif (this.authenticationProvider != null) {\n\t\t\treturn this.authenticationProvider;\n\t\t}\n\t\tUserDetailsService userDetailsService = this.context.getBean(UserDetailsService.class);\n\t\tthis.authenticationProvider = new OneTimeTokenAuthenticationProvider(getOneTimeTokenService(),\n\t\t\t\tuserDetailsService);\n\t\treturn this.authenticationProvider;\n\t}\n\n\t@Override\n\tprotected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {\n\t\treturn getRequestMatcherBuilder().matcher(HttpMethod.POST, loginProcessingUrl);\n\t}\n\n\t/**\n\t * Specifies the {@link AuthenticationProvider} to use when authenticating the user.\n\t * @param authenticationProvider\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> authenticationProvider(AuthenticationProvider authenticationProvider) {\n\t\tAssert.notNull(authenticationProvider, \"authenticationProvider cannot be null\");\n\t\tthis.authenticationProvider = authenticationProvider;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the URL that a One-Time Token generate request will be processed.\n\t * Defaults to {@code /ott/generate}.\n\t * @param tokenGeneratingUrl\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> tokenGeneratingUrl(String tokenGeneratingUrl) {\n\t\tAssert.hasText(tokenGeneratingUrl, \"tokenGeneratingUrl cannot be null or empty\");\n\t\tthis.tokenGeneratingUrl = tokenGeneratingUrl;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies strategy to be used to handle generated one-time tokens.\n\t * @param oneTimeTokenGenerationSuccessHandler\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> tokenGenerationSuccessHandler(\n\t\t\tOneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler) {\n\t\tAssert.notNull(oneTimeTokenGenerationSuccessHandler, \"oneTimeTokenGenerationSuccessHandler cannot be null\");\n\t\tthis.oneTimeTokenGenerationSuccessHandler = oneTimeTokenGenerationSuccessHandler;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the URL to process the login request, defaults to {@code /login/ott}.\n\t * Only POST requests are processed, for that reason make sure that you pass a valid\n\t * CSRF token if CSRF protection is enabled.\n\t * @param loginProcessingUrl\n\t * @see HttpSecurity#csrf(Customizer)\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> loginProcessingUrl(String loginProcessingUrl) {\n\t\tAssert.hasText(loginProcessingUrl, \"loginProcessingUrl cannot be null or empty\");\n\t\tsuper.loginProcessingUrl(loginProcessingUrl);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the URL to send users to if login is required. If used with\n\t * {@link EnableWebSecurity} a default login page will be generated when this\n\t * attribute is not specified.\n\t * @param loginPage\n\t */\n\t@Override\n\tpublic OneTimeTokenLoginConfigurer<H> loginPage(String loginPage) {\n\t\treturn super.loginPage(loginPage);\n\t}\n\n\t/**\n\t * Configures whether the default one-time token submit page should be shown. This\n\t * will prevent the {@link DefaultOneTimeTokenSubmitPageGeneratingFilter} to be\n\t * configured.\n\t * @param show\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> showDefaultSubmitPage(boolean show) {\n\t\tthis.submitPageEnabled = show;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the URL that the default submit page will be generated. Defaults to\n\t * {@code /login/ott}. If you don't want to generate the default submit page you\n\t * should use {@link #showDefaultSubmitPage(boolean)}. Note that this method always\n\t * invoke {@link #showDefaultSubmitPage(boolean)} passing {@code true}.\n\t * @param submitPageUrl\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> defaultSubmitPageUrl(String submitPageUrl) {\n\t\tAssert.hasText(submitPageUrl, \"submitPageUrl cannot be null or empty\");\n\t\tthis.defaultSubmitPageUrl = submitPageUrl;\n\t\tshowDefaultSubmitPage(true);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the {@link OneTimeTokenService} used to generate and consume\n\t * {@link OneTimeToken}\n\t * @param oneTimeTokenService\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> tokenService(OneTimeTokenService oneTimeTokenService) {\n\t\tAssert.notNull(oneTimeTokenService, \"oneTimeTokenService cannot be null\");\n\t\tthis.oneTimeTokenService = oneTimeTokenService;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Use this {@link AuthenticationConverter} when converting incoming requests to an\n\t * {@link Authentication}. By default, the {@link OneTimeTokenAuthenticationConverter}\n\t * is used.\n\t * @param authenticationConverter the {@link AuthenticationConverter} to use\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.getAuthenticationFilter().setAuthenticationConverter(authenticationConverter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the {@link AuthenticationFailureHandler} to use when authentication\n\t * fails. The default is redirecting to \"/login?error\" using\n\t * {@link SimpleUrlAuthenticationFailureHandler}\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use\n\t * when authentication fails.\n\t * @deprecated Use {@link #failureHandler(AuthenticationFailureHandler)} instead\n\t */\n\t@Deprecated(since = \"6.5\")\n\tpublic OneTimeTokenLoginConfigurer<H> authenticationFailureHandler(\n\t\t\tAuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tsuper.failureHandler(authenticationFailureHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the {@link AuthenticationSuccessHandler} to be used. The default is\n\t * {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties\n\t * set.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler}.\n\t * @deprecated Use {@link #successHandler(AuthenticationSuccessHandler)} instead\n\t */\n\t@Deprecated(since = \"6.5\")\n\tpublic OneTimeTokenLoginConfigurer<H> authenticationSuccessHandler(\n\t\t\tAuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tsuper.successHandler(authenticationSuccessHandler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Use this {@link GenerateOneTimeTokenRequestResolver} when resolving\n\t * {@link GenerateOneTimeTokenRequest} from {@link HttpServletRequest}. By default,\n\t * the {@link DefaultGenerateOneTimeTokenRequestResolver} is used.\n\t * @param requestResolver the {@link GenerateOneTimeTokenRequestResolver}\n\t * @since 6.5\n\t */\n\tpublic OneTimeTokenLoginConfigurer<H> generateRequestResolver(GenerateOneTimeTokenRequestResolver requestResolver) {\n\t\tAssert.notNull(requestResolver, \"requestResolver cannot be null\");\n\t\tthis.requestResolver = requestResolver;\n\t\treturn this;\n\t}\n\n\tprivate GenerateOneTimeTokenRequestResolver getGenerateRequestResolver() {\n\t\tif (this.requestResolver != null) {\n\t\t\treturn this.requestResolver;\n\t\t}\n\t\tthis.requestResolver = this.context.getBeanProvider(GenerateOneTimeTokenRequestResolver.class)\n\t\t\t.getIfUnique(DefaultGenerateOneTimeTokenRequestResolver::new);\n\t\treturn this.requestResolver;\n\t}\n\n\tprivate OneTimeTokenService getOneTimeTokenService() {\n\t\tif (this.oneTimeTokenService != null) {\n\t\t\treturn this.oneTimeTokenService;\n\t\t}\n\t\tthis.oneTimeTokenService = this.context.getBeanProvider(OneTimeTokenService.class)\n\t\t\t.getIfUnique(InMemoryOneTimeTokenService::new);\n\t\treturn this.oneTimeTokenService;\n\t}\n\n\tprivate Map<String, String> hiddenInputs(HttpServletRequest request) {\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\treturn (token != null) ? Collections.singletonMap(token.getParameterName(), token.getToken())\n\t\t\t\t: Collections.emptyMap();\n\t}\n\n\t/**\n\t * @deprecated Use this.context instead\n\t */\n\t@Deprecated\n\tpublic ApplicationContext getContext() {\n\t\treturn this.context;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.saml2;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.opensaml.core.Version;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.OpenSaml5AuthenticationTokenConverter;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;\nimport org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter;\nimport org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.ParameterRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AbstractHttpConfigurer} for SAML 2.0 Login, which leverages the SAML 2.0 Web\n * Browser Single Sign On (WebSSO) Flow.\n *\n * <p>\n * SAML 2.0 Login provides an application with the capability to have users log in by\n * using their existing account at an SAML 2.0 Identity Provider.\n *\n * <p>\n * Defaults are provided for all configuration options with the only required\n * configuration being\n * {@link #relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository)} .\n * Alternatively, a {@link RelyingPartyRegistrationRepository} {@code @Bean} may be\n * registered instead.\n *\n * <h2>Security Filters</h2>\n *\n * The following {@code Filter}'s are populated:\n *\n * <ul>\n * <li>{@link Saml2WebSsoAuthenticationFilter}</li>\n * <li>{@link Saml2WebSsoAuthenticationRequestFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * The following shared objects are populated:\n *\n * <ul>\n * <li>{@link RelyingPartyRegistrationRepository} (required)</li>\n * </ul>\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link RelyingPartyRegistrationRepository} (required)</li>\n * <li>{@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not\n * configured and {@code DefaultLoginPageGeneratingFilter} is available, than a default\n * login page will be made available</li>\n * </ul>\n *\n * @since 5.2\n * @see HttpSecurity#saml2Login(Customizer)\n * @see Saml2WebSsoAuthenticationFilter\n * @see Saml2WebSsoAuthenticationRequestFilter\n * @see RelyingPartyRegistrationRepository\n * @see AbstractAuthenticationFilterConfigurer\n */\npublic final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>\n\t\textends AbstractAuthenticationFilterConfigurer<B, Saml2LoginConfigurer<B>, Saml2WebSsoAuthenticationFilter> {\n\n\tprivate static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith(\"5\");\n\n\tprivate String loginPage;\n\n\tprivate String authenticationRequestUri = \"/saml2/authenticate\";\n\n\tprivate String[] authenticationRequestParams = { \"registrationId={registrationId}\" };\n\n\tprivate RequestMatcher authenticationRequestMatcher;\n\n\tprivate Saml2AuthenticationRequestResolver authenticationRequestResolver;\n\n\tprivate RequestMatcher loginProcessingUrl;\n\n\tprivate RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate Saml2WebSsoAuthenticationFilter saml2WebSsoAuthenticationFilter;\n\n\t/**\n\t * Use this {@link AuthenticationConverter} when converting incoming requests to an\n\t * {@link Authentication}. By default the {@link Saml2AuthenticationTokenConverter} is\n\t * used.\n\t * @param authenticationConverter the {@link AuthenticationConverter} to use\n\t * @return the {@link Saml2LoginConfigurer} for further configuration\n\t * @since 5.4\n\t */\n\tpublic Saml2LoginConfigurer<B> authenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows a configuration of a {@link AuthenticationManager} to be used during SAML 2\n\t * authentication. If none is specified, the system will create one inject it into the\n\t * {@link Saml2WebSsoAuthenticationFilter}\n\t * @param authenticationManager the authentication manager to be used\n\t * @return the {@link Saml2LoginConfigurer} for further configuration\n\t * @throws IllegalArgumentException if authenticationManager is null configure the\n\t * default manager\n\t * @since 5.3\n\t */\n\tpublic Saml2LoginConfigurer<B> authenticationManager(AuthenticationManager authenticationManager) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@code RelyingPartyRegistrationRepository} of relying parties, each party\n\t * representing a service provider, SP and this host, and identity provider, IDP pair\n\t * that communicate with each other.\n\t * @param repo the repository of relying parties\n\t * @return the {@link Saml2LoginConfigurer} for further configuration\n\t */\n\tpublic Saml2LoginConfigurer<B> relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {\n\t\tthis.relyingPartyRegistrationRepository = repo;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic Saml2LoginConfigurer<B> loginPage(String loginPage) {\n\t\tAssert.hasText(loginPage, \"loginPage cannot be empty\");\n\t\tthis.loginPage = loginPage;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Use this {@link Saml2AuthenticationRequestResolver} for generating SAML 2.0\n\t * Authentication Requests.\n\t * @param authenticationRequestResolver\n\t * @return the {@link Saml2LoginConfigurer} for further configuration\n\t * @since 5.7\n\t */\n\tpublic Saml2LoginConfigurer<B> authenticationRequestResolver(\n\t\t\tSaml2AuthenticationRequestResolver authenticationRequestResolver) {\n\t\tAssert.notNull(authenticationRequestResolver, \"authenticationRequestResolver cannot be null\");\n\t\tthis.authenticationRequestResolver = authenticationRequestResolver;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Customize the URL that the SAML Authentication Request will be sent to.\n\t * @param authenticationRequestUri the URI to use for the SAML 2.0 Authentication\n\t * Request\n\t * @return the {@link Saml2LoginConfigurer} for further configuration\n\t * @since 6.0\n\t * @deprecated Use {@link #authenticationRequestUriQuery} instead\n\t */\n\t@Deprecated\n\tpublic Saml2LoginConfigurer<B> authenticationRequestUri(String authenticationRequestUri) {\n\t\treturn authenticationRequestUriQuery(authenticationRequestUri);\n\t}\n\n\t/**\n\t * Customize the URL that the SAML Authentication Request will be sent to. This method\n\t * also supports query parameters like so: <pre>\n\t * \tauthenticationRequestUriQuery(\"/saml/authenticate?registrationId={registrationId}\")\n\t * </pre> {@link RelyingPartyRegistrations}\n\t * @param authenticationRequestUriQuery the URI and query to use for the SAML 2.0\n\t * Authentication Request\n\t * @return the {@link Saml2LoginConfigurer} for further configuration\n\t * @since 6.0\n\t */\n\tpublic Saml2LoginConfigurer<B> authenticationRequestUriQuery(String authenticationRequestUriQuery) {\n\t\tAssert.state(authenticationRequestUriQuery.contains(\"{registrationId}\"),\n\t\t\t\t\"authenticationRequestUri must contain {registrationId} path variable or query value\");\n\t\tString[] parts = authenticationRequestUriQuery.split(\"[?&]\");\n\t\tthis.authenticationRequestUri = parts[0];\n\t\tthis.authenticationRequestParams = new String[parts.length - 1];\n\t\tSystem.arraycopy(parts, 1, this.authenticationRequestParams, 0, parts.length - 1);\n\t\tthis.authenticationRequestMatcher = new PathQueryRequestMatcher(\n\t\t\t\tgetRequestMatcherBuilder().matcher(this.authenticationRequestUri), this.authenticationRequestParams);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specifies the URL to validate the credentials. If specified a custom URL, consider\n\t * specifying a custom {@link AuthenticationConverter} via\n\t * {@link #authenticationConverter(AuthenticationConverter)}, since the default\n\t * {@link AuthenticationConverter} implementation relies on the\n\t * <code>{registrationId}</code> path variable to be present in the URL\n\t * @param loginProcessingUrl the URL to validate the credentials\n\t * @return the {@link Saml2LoginConfigurer} for additional customization\n\t * @see Saml2WebSsoAuthenticationFilter#DEFAULT_FILTER_PROCESSES_URI\n\t */\n\t@Override\n\tpublic Saml2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {\n\t\tAssert.hasText(loginProcessingUrl, \"loginProcessingUrl cannot be empty\");\n\t\tthis.loginProcessingUrl = getRequestMatcherBuilder().matcher(loginProcessingUrl);\n\t\treturn this;\n\t}\n\n\t@Override\n\tprotected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {\n\t\treturn getRequestMatcherBuilder().matcher(loginProcessingUrl);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <p>\n\t * Initializes this filter chain for SAML 2 Login. The following actions are taken:\n\t * <ul>\n\t * <li>The WebSSO endpoint has CSRF disabled, typically {@code /login/saml2/sso}</li>\n\t * <li>A {@link Saml2WebSsoAuthenticationFilter is configured}</li>\n\t * <li>The {@code loginProcessingUrl} is set</li>\n\t * <li>A custom login page is configured, <b>or</b></li>\n\t * <li>A default login page with all SAML 2.0 Identity Providers is configured</li>\n\t * <li>An {@link AuthenticationProvider} is configured</li>\n\t * </ul>\n\t */\n\t@Override\n\tpublic void init(B http) {\n\t\tregisterDefaultCsrfOverride(http);\n\t\trelyingPartyRegistrationRepository(http);\n\t\tthis.saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(getAuthenticationConverter(http));\n\t\tthis.saml2WebSsoAuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\tthis.saml2WebSsoAuthenticationFilter.setRequiresAuthenticationRequestMatcher(getLoginProcessingEndpoint());\n\t\tsetAuthenticationRequestRepository(http, this.saml2WebSsoAuthenticationFilter);\n\t\tsetAuthenticationFilter(this.saml2WebSsoAuthenticationFilter);\n\t\tif (StringUtils.hasText(this.loginPage)) {\n\t\t\t// Set custom login page\n\t\t\tsuper.loginPage(this.loginPage);\n\t\t\tsuper.init(http);\n\t\t}\n\t\telse {\n\t\t\tMap<String, String> providerUrlMap = getIdentityProviderUrlMap(this.authenticationRequestUri,\n\t\t\t\t\tthis.authenticationRequestParams, this.relyingPartyRegistrationRepository);\n\t\t\tboolean singleProvider = providerUrlMap.size() == 1;\n\t\t\tif (singleProvider) {\n\t\t\t\t// Setup auto-redirect to provider login page\n\t\t\t\t// when only 1 IDP is configured\n\t\t\t\tthis.updateAuthenticationDefaults();\n\t\t\t\tthis.updateAccessDefaults(http);\n\t\t\t\tString loginUrl = providerUrlMap.entrySet().iterator().next().getKey();\n\t\t\t\tregisterAuthenticationEntryPoint(http, getLoginEntryPoint(http, loginUrl));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsuper.init(http);\n\t\t\t}\n\t\t}\n\t\tthis.initDefaultLoginFilter(http);\n\t\tif (this.authenticationManager == null) {\n\t\t\tregisterDefaultAuthenticationProvider(http);\n\t\t}\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <p>\n\t * During the {@code configure} phase, a\n\t * {@link Saml2WebSsoAuthenticationRequestFilter} is added to handle SAML 2.0\n\t * AuthNRequest redirects\n\t */\n\t@Override\n\tpublic void configure(B http) {\n\t\tSaml2WebSsoAuthenticationRequestFilter filter = getAuthenticationRequestFilter(http);\n\t\tfilter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));\n\t\thttp.addFilter(postProcess(filter));\n\t\tsuper.configure(http);\n\t\tif (this.authenticationManager != null) {\n\t\t\tthis.saml2WebSsoAuthenticationFilter.setAuthenticationManager(this.authenticationManager);\n\t\t}\n\t}\n\n\tRelyingPartyRegistrationRepository relyingPartyRegistrationRepository(B http) {\n\t\tif (this.relyingPartyRegistrationRepository == null) {\n\t\t\tthis.relyingPartyRegistrationRepository = getSharedOrBean(http, RelyingPartyRegistrationRepository.class);\n\t\t}\n\t\treturn this.relyingPartyRegistrationRepository;\n\t}\n\n\tprivate AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLoginPage) {\n\t\tRequestMatcher loginPageMatcher = getRequestMatcherBuilder().matcher(this.getLoginPage());\n\t\tRequestMatcher faviconMatcher = getRequestMatcherBuilder().matcher(\"/favicon.ico\");\n\t\tRequestMatcher defaultEntryPointMatcher = this.getAuthenticationEntryPointMatcher(http);\n\t\tRequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(\n\t\t\t\tnew OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);\n\t\tRequestMatcher notXRequestedWith = new NegatedRequestMatcher(\n\t\t\t\tnew RequestHeaderRequestMatcher(\"X-Requested-With\", \"XMLHttpRequest\"));\n\t\tLoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage);\n\t\tRequestMatcher loginUrlMatcher = new AndRequestMatcher(notXRequestedWith,\n\t\t\t\tnew NegatedRequestMatcher(defaultLoginPageMatcher));\n\t\t// @formatter:off\n\t\tAuthenticationEntryPoint loginEntryPoint = DelegatingAuthenticationEntryPoint.builder()\n\t\t\t\t.addEntryPointFor(loginUrlEntryPoint, loginUrlMatcher)\n\t\t\t\t.defaultEntryPoint(getAuthenticationEntryPoint())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tExceptionHandlingConfigurer<B> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);\n\t\tif (exceptions != null) {\n\t\t\tRequestMatcher requestMatcher = getAuthenticationEntryPointMatcher(http);\n\t\t\texceptions.defaultDeniedHandlerForMissingAuthority(\n\t\t\t\t\t(ep) -> ep.addEntryPointFor(loginEntryPoint, requestMatcher),\n\t\t\t\t\tFactorGrantedAuthority.SAML_RESPONSE_AUTHORITY);\n\t\t}\n\t\treturn loginEntryPoint;\n\t}\n\n\tprivate void setAuthenticationRequestRepository(B http,\n\t\t\tSaml2WebSsoAuthenticationFilter saml2WebSsoAuthenticationFilter) {\n\t\tsaml2WebSsoAuthenticationFilter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));\n\t}\n\n\tprivate Saml2WebSsoAuthenticationRequestFilter getAuthenticationRequestFilter(B http) {\n\t\tSaml2AuthenticationRequestResolver authenticationRequestResolver = getAuthenticationRequestResolver(http);\n\t\treturn new Saml2WebSsoAuthenticationRequestFilter(authenticationRequestResolver);\n\t}\n\n\tprivate Saml2AuthenticationRequestResolver getAuthenticationRequestResolver(B http) {\n\t\tif (this.authenticationRequestResolver != null) {\n\t\t\treturn this.authenticationRequestResolver;\n\t\t}\n\t\tSaml2AuthenticationRequestResolver bean = getBeanOrNull(http, Saml2AuthenticationRequestResolver.class);\n\t\tif (bean != null) {\n\t\t\treturn bean;\n\t\t}\n\t\tif (USE_OPENSAML_5) {\n\t\t\tOpenSaml5AuthenticationRequestResolver openSamlAuthenticationRequestResolver = new OpenSaml5AuthenticationRequestResolver(\n\t\t\t\t\trelyingPartyRegistrationRepository(http));\n\t\t\topenSamlAuthenticationRequestResolver.setRequestMatcher(getAuthenticationRequestMatcher());\n\t\t\treturn openSamlAuthenticationRequestResolver;\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t\t}\n\t}\n\n\tprivate RequestMatcher getAuthenticationRequestMatcher() {\n\t\tif (this.authenticationRequestMatcher == null) {\n\t\t\tthis.authenticationRequestMatcher = RequestMatchers.anyOf(\n\t\t\t\t\tgetRequestMatcherBuilder()\n\t\t\t\t\t\t.matcher(Saml2AuthenticationRequestResolver.DEFAULT_AUTHENTICATION_REQUEST_URI),\n\t\t\t\t\tnew PathQueryRequestMatcher(getRequestMatcherBuilder().matcher(this.authenticationRequestUri),\n\t\t\t\t\t\t\tthis.authenticationRequestParams));\n\t\t}\n\t\treturn this.authenticationRequestMatcher;\n\t}\n\n\tprivate RequestMatcher getLoginProcessingEndpoint() {\n\t\tif (this.loginProcessingUrl == null) {\n\t\t\tthis.loginProcessingUrl = RequestMatchers.anyOf(\n\t\t\t\t\tgetRequestMatcherBuilder().matcher(Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI),\n\t\t\t\t\tgetRequestMatcherBuilder().matcher(\"/login/saml2/sso\"));\n\t\t}\n\n\t\treturn this.loginProcessingUrl;\n\t}\n\n\tprivate AuthenticationConverter getAuthenticationConverter(B http) {\n\t\tif (this.authenticationConverter != null) {\n\t\t\treturn this.authenticationConverter;\n\t\t}\n\t\tAuthenticationConverter authenticationConverterBean = getBeanOrNull(http,\n\t\t\t\tSaml2AuthenticationTokenConverter.class);\n\t\tif (authenticationConverterBean != null) {\n\t\t\treturn authenticationConverterBean;\n\t\t}\n\t\tif (USE_OPENSAML_5) {\n\t\t\tauthenticationConverterBean = getBeanOrNull(http, OpenSaml5AuthenticationTokenConverter.class);\n\t\t\tif (authenticationConverterBean != null) {\n\t\t\t\treturn authenticationConverterBean;\n\t\t\t}\n\t\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(\n\t\t\t\t\tthis.relyingPartyRegistrationRepository);\n\t\t\tconverter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));\n\t\t\tconverter.setRequestMatcher(getLoginProcessingEndpoint());\n\t\t\treturn converter;\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n\tprivate void registerDefaultAuthenticationProvider(B http) {\n\t\tif (USE_OPENSAML_5) {\n\t\t\tOpenSaml5AuthenticationProvider provider = getBeanOrNull(http, OpenSaml5AuthenticationProvider.class);\n\t\t\tif (provider == null) {\n\t\t\t\thttp.authenticationProvider(postProcess(new OpenSaml5AuthenticationProvider()));\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t\t}\n\t}\n\n\tprivate void registerDefaultCsrfOverride(B http) {\n\t\tCsrfConfigurer<B> csrf = http.getConfigurer(CsrfConfigurer.class);\n\t\tif (csrf == null) {\n\t\t\treturn;\n\t\t}\n\t\tcsrf.ignoringRequestMatchers(getLoginProcessingEndpoint());\n\t}\n\n\tprivate void initDefaultLoginFilter(B http) {\n\t\tDefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http\n\t\t\t.getSharedObject(DefaultLoginPageGeneratingFilter.class);\n\t\tif (loginPageGeneratingFilter == null || this.isCustomLoginPage()) {\n\t\t\treturn;\n\t\t}\n\t\tloginPageGeneratingFilter.setSaml2LoginEnabled(true);\n\t\tloginPageGeneratingFilter\n\t\t\t.setSaml2AuthenticationUrlToProviderName(this.getIdentityProviderUrlMap(this.authenticationRequestUri,\n\t\t\t\t\tthis.authenticationRequestParams, this.relyingPartyRegistrationRepository));\n\t\tloginPageGeneratingFilter.setLoginPageUrl(this.getLoginPage());\n\t\tloginPageGeneratingFilter.setFailureUrl(this.getFailureUrl());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Map<String, String> getIdentityProviderUrlMap(String authRequestPrefixUrl, String[] authRequestQueryParams,\n\t\t\tRelyingPartyRegistrationRepository idpRepo) {\n\t\tMap<String, String> idps = new LinkedHashMap<>();\n\t\tif (idpRepo instanceof Iterable) {\n\t\t\tIterable<RelyingPartyRegistration> repo = (Iterable<RelyingPartyRegistration>) idpRepo;\n\t\t\tStringBuilder authRequestQuery = new StringBuilder(\"?\");\n\t\t\tfor (String authRequestQueryParam : authRequestQueryParams) {\n\t\t\t\tauthRequestQuery.append(authRequestQueryParam + \"&\");\n\t\t\t}\n\t\t\tauthRequestQuery.deleteCharAt(authRequestQuery.length() - 1);\n\t\t\tString authenticationRequestUriQuery = authRequestPrefixUrl + authRequestQuery;\n\t\t\trepo.forEach(\n\t\t\t\t\t(p) -> idps.put(authenticationRequestUriQuery.replace(\"{registrationId}\", p.getRegistrationId()),\n\t\t\t\t\t\t\tp.getRegistrationId()));\n\t\t}\n\t\treturn idps;\n\t}\n\n\tprivate Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> getAuthenticationRequestRepository(\n\t\t\tB http) {\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = getBeanOrNull(http,\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\t\tif (repository == null) {\n\t\t\treturn new HttpSessionSaml2AuthenticationRequestRepository();\n\t\t}\n\t\treturn repository;\n\t}\n\n\tprivate <C> C getSharedOrBean(B http, Class<C> clazz) {\n\t\tC shared = http.getSharedObject(clazz);\n\t\tif (shared != null) {\n\t\t\treturn shared;\n\t\t}\n\t\treturn getBeanOrNull(http, clazz);\n\t}\n\n\tprivate <C> C getBeanOrNull(B http, Class<C> clazz) {\n\t\tApplicationContext context = http.getSharedObject(ApplicationContext.class);\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn context.getBeanProvider(clazz).getIfUnique();\n\t}\n\n\tprivate <C> void setSharedObject(B http, Class<C> clazz, C object) {\n\t\tif (http.getSharedObject(clazz) == null) {\n\t\t\thttp.setSharedObject(clazz, object);\n\t\t}\n\t}\n\n\tstatic class PathQueryRequestMatcher implements RequestMatcher {\n\n\t\tprivate final RequestMatcher matcher;\n\n\t\tPathQueryRequestMatcher(RequestMatcher pathMatcher, String... params) {\n\t\t\tList<RequestMatcher> matchers = new ArrayList<>();\n\t\t\tmatchers.add(pathMatcher);\n\t\t\tfor (String param : params) {\n\t\t\t\tString[] parts = param.split(\"=\");\n\t\t\t\tif (parts.length == 1) {\n\t\t\t\t\tmatchers.add(new ParameterRequestMatcher(parts[0]));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tmatchers.add(new ParameterRequestMatcher(parts[0], parts[1]));\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.matcher = new AndRequestMatcher(matchers);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn matcher(request).isMatch();\n\t\t}\n\n\t\t@Override\n\t\tpublic MatchResult matcher(HttpServletRequest request) {\n\t\t\treturn this.matcher.matcher(request);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.saml2;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.opensaml.core.Version;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutResponseValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutRequestResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutRequestValidatorParametersResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutResponseResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestValidatorParametersResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2RelyingPartyInitiatedLogoutSuccessHandler;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.csrf.CsrfLogoutHandler;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.ParameterRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * Adds SAML 2.0 logout support.\n *\n * <h2>Security Filters</h2>\n *\n * The following Filters are populated\n *\n * <ul>\n * <li>{@link LogoutFilter}</li>\n * <li>{@link Saml2LogoutRequestFilter}</li>\n * <li>{@link Saml2LogoutResponseFilter}</li>\n * </ul>\n *\n * <p>\n * The following configuration options are available:\n *\n * <ul>\n * <li>{@link #logoutUrl} - The URL to to process SAML 2.0 Logout</li>\n * <li>{@link LogoutRequestConfigurer#logoutRequestValidator} - The\n * {@link AuthenticationManager} for authenticating SAML 2.0 Logout Requests</li>\n * <li>{@link LogoutRequestConfigurer#logoutRequestResolver} - The\n * {@link Saml2LogoutRequestResolver} for creating SAML 2.0 Logout Requests</li>\n * <li>{@link LogoutRequestConfigurer#logoutRequestRepository} - The\n * {@link Saml2LogoutRequestRepository} for storing SAML 2.0 Logout Requests</li>\n * <li>{@link LogoutResponseConfigurer#logoutResponseValidator} - The\n * {@link AuthenticationManager} for authenticating SAML 2.0 Logout Responses</li>\n * <li>{@link LogoutResponseConfigurer#logoutResponseResolver} - The\n * {@link Saml2LogoutResponseResolver} for creating SAML 2.0 Logout Responses</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * No shared Objects are created\n *\n * <h2>Shared Objects Used</h2>\n *\n * Uses {@link CsrfTokenRepository} to add the {@link CsrfLogoutHandler}.\n *\n * @author Josh Cummings\n * @author Ngoc Nhan\n * @since 5.6\n * @see Saml2LogoutConfigurer\n */\npublic final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<Saml2LogoutConfigurer<H>, H> {\n\n\tprivate static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith(\"5\");\n\n\tprivate ApplicationContext context;\n\n\tprivate RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;\n\n\tprivate String logoutUrl = \"/logout\";\n\n\tprivate List<LogoutHandler> logoutHandlers = new ArrayList<>();\n\n\tprivate LogoutSuccessHandler logoutSuccessHandler;\n\n\tprivate LogoutRequestConfigurer logoutRequestConfigurer;\n\n\tprivate LogoutResponseConfigurer logoutResponseConfigurer;\n\n\t/**\n\t * Creates a new instance\n\t * @see HttpSecurity#logout(Customizer)\n\t */\n\tpublic Saml2LogoutConfigurer(ApplicationContext context) {\n\t\tthis.context = context;\n\t\tthis.logoutHandlers.add(new SecurityContextLogoutHandler());\n\t\tthis.logoutHandlers.add(new LogoutSuccessEventPublishingLogoutHandler());\n\t\tSimpleUrlLogoutSuccessHandler logoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();\n\t\tlogoutSuccessHandler.setDefaultTargetUrl(\"/login?logout\");\n\t\tthis.logoutSuccessHandler = logoutSuccessHandler;\n\t\tthis.logoutRequestConfigurer = new LogoutRequestConfigurer();\n\t\tthis.logoutResponseConfigurer = new LogoutResponseConfigurer();\n\t}\n\n\t/**\n\t * The URL by which the relying or asserting party can trigger logout.\n\t *\n\t * <p>\n\t * The Relying Party triggers logout by POSTing to the endpoint. The Asserting Party\n\t * triggers logout based on what is specified by\n\t * {@link RelyingPartyRegistration#getSingleLogoutServiceBindings()}.\n\t * @param logoutUrl the URL that will invoke logout\n\t * @return the {@link LogoutConfigurer} for further customizations\n\t * @see LogoutConfigurer#logoutUrl(String)\n\t * @see HttpSecurity#csrf(Customizer)\n\t */\n\tpublic Saml2LogoutConfigurer<H> logoutUrl(String logoutUrl) {\n\t\tthis.logoutUrl = logoutUrl;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the {@link RelyingPartyRegistrationRepository} of relying parties, each party\n\t * representing a service provider, SP and this host, and identity provider, IDP pair\n\t * that communicate with each other.\n\t * @param repo the repository of relying parties\n\t * @return the {@link Saml2LogoutConfigurer} for further customizations\n\t */\n\tpublic Saml2LogoutConfigurer<H> relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {\n\t\tthis.relyingPartyRegistrationRepository = repo;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures SAML 2.0 Logout Request components\n\t * @param logoutRequestConfigurerCustomizer the {@link Customizer} to provide more\n\t * options for the {@link LogoutRequestConfigurer}\n\t * @return the {@link Saml2LogoutConfigurer} for further customizations\n\t */\n\tpublic Saml2LogoutConfigurer<H> logoutRequest(\n\t\t\tCustomizer<LogoutRequestConfigurer> logoutRequestConfigurerCustomizer) {\n\t\tlogoutRequestConfigurerCustomizer.customize(this.logoutRequestConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures SAML 2.0 Logout Response components\n\t * @param logoutResponseConfigurerCustomizer the {@link Customizer} to provide more\n\t * options for the {@link LogoutResponseConfigurer}\n\t * @return the {@link Saml2LogoutConfigurer} for further customizations\n\t */\n\tpublic Saml2LogoutConfigurer<H> logoutResponse(\n\t\t\tCustomizer<LogoutResponseConfigurer> logoutResponseConfigurerCustomizer) {\n\t\tlogoutResponseConfigurerCustomizer.customize(this.logoutResponseConfigurer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void configure(H http) {\n\t\tLogoutConfigurer<H> logout = http.getConfigurer(LogoutConfigurer.class);\n\t\tif (logout != null) {\n\t\t\tthis.logoutHandlers = logout.getLogoutHandlers();\n\t\t\tthis.logoutSuccessHandler = logout.getLogoutSuccessHandler();\n\t\t}\n\t\tRelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);\n\t\thttp.addFilterBefore(createLogoutRequestProcessingFilter(registrations), CsrfFilter.class);\n\t\thttp.addFilterBefore(createLogoutResponseProcessingFilter(registrations), CsrfFilter.class);\n\t\thttp.addFilterBefore(createRelyingPartyLogoutFilter(registrations), LogoutFilter.class);\n\t}\n\n\tprivate RelyingPartyRegistrationRepository getRelyingPartyRegistrationRepository(H http) {\n\t\tif (this.relyingPartyRegistrationRepository != null) {\n\t\t\treturn this.relyingPartyRegistrationRepository;\n\t\t}\n\t\tSaml2LoginConfigurer<H> login = http.getConfigurer(Saml2LoginConfigurer.class);\n\t\tif (login != null) {\n\t\t\tthis.relyingPartyRegistrationRepository = login.relyingPartyRegistrationRepository(http);\n\t\t}\n\t\telse {\n\t\t\tthis.relyingPartyRegistrationRepository = getBeanOrNull(RelyingPartyRegistrationRepository.class);\n\t\t}\n\t\treturn this.relyingPartyRegistrationRepository;\n\t}\n\n\tprivate Saml2LogoutRequestFilter createLogoutRequestProcessingFilter(\n\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\tLogoutHandler[] logoutHandlers = this.logoutHandlers.toArray(new LogoutHandler[0]);\n\t\tSaml2LogoutResponseResolver logoutResponseResolver = createSaml2LogoutResponseResolver(registrations);\n\t\tSaml2LogoutRequestFilter filter = new Saml2LogoutRequestFilter(\n\t\t\t\tcreateSaml2LogoutResponseParametersResolver(registrations),\n\t\t\t\tthis.logoutRequestConfigurer.logoutRequestValidator(), logoutResponseResolver, logoutHandlers);\n\t\tfilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());\n\t\treturn postProcess(filter);\n\t}\n\n\tprivate Saml2LogoutRequestValidatorParametersResolver createSaml2LogoutResponseParametersResolver(\n\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\tRequestMatcher requestMatcher = createLogoutRequestMatcher();\n\t\tif (USE_OPENSAML_5) {\n\t\t\tOpenSaml5LogoutRequestValidatorParametersResolver parameters = new OpenSaml5LogoutRequestValidatorParametersResolver(\n\t\t\t\t\tregistrations);\n\t\t\tparameters.setRequestMatcher(requestMatcher);\n\t\t\treturn parameters;\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n\tprivate Saml2LogoutResponseFilter createLogoutResponseProcessingFilter(\n\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\tSaml2LogoutResponseFilter logoutResponseFilter = new Saml2LogoutResponseFilter(registrations,\n\t\t\t\tthis.logoutResponseConfigurer.logoutResponseValidator(), this.logoutSuccessHandler);\n\t\tlogoutResponseFilter.setLogoutRequestMatcher(createLogoutResponseMatcher());\n\t\tlogoutResponseFilter.setLogoutRequestRepository(this.logoutRequestConfigurer.logoutRequestRepository);\n\t\treturn postProcess(logoutResponseFilter);\n\t}\n\n\tprivate Saml2RelyingPartyInitiatedLogoutFilter createRelyingPartyLogoutFilter(\n\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\tLogoutHandler[] logoutHandlers = this.logoutHandlers.toArray(new LogoutHandler[0]);\n\t\tSaml2RelyingPartyInitiatedLogoutSuccessHandler logoutRequestSuccessHandler = createSaml2LogoutRequestSuccessHandler(\n\t\t\t\tregistrations);\n\t\tlogoutRequestSuccessHandler.setLogoutRequestRepository(this.logoutRequestConfigurer.logoutRequestRepository);\n\t\tSaml2RelyingPartyInitiatedLogoutFilter logoutFilter = new Saml2RelyingPartyInitiatedLogoutFilter(\n\t\t\t\tlogoutRequestSuccessHandler, logoutHandlers);\n\t\tlogoutFilter.setLogoutRequestMatcher(createLogoutMatcher());\n\t\treturn postProcess(logoutFilter);\n\t}\n\n\tprivate RequestMatcher createLogoutMatcher() {\n\t\tRequestMatcher logout = getRequestMatcherBuilder().matcher(HttpMethod.POST, this.logoutUrl);\n\t\tRequestMatcher saml2 = new Saml2RequestMatcher(getSecurityContextHolderStrategy());\n\t\treturn new AndRequestMatcher(logout, saml2);\n\t}\n\n\tprivate RequestMatcher createLogoutRequestMatcher() {\n\t\tRequestMatcher logout = getRequestMatcherBuilder().matcher(this.logoutRequestConfigurer.logoutUrl);\n\t\tRequestMatcher samlRequest = new ParameterRequestMatcher(\"SAMLRequest\");\n\t\treturn new AndRequestMatcher(logout, samlRequest);\n\t}\n\n\tprivate RequestMatcher createLogoutResponseMatcher() {\n\t\tRequestMatcher logout = getRequestMatcherBuilder().matcher(this.logoutResponseConfigurer.logoutUrl);\n\t\tRequestMatcher samlResponse = new ParameterRequestMatcher(\"SAMLResponse\");\n\t\treturn new AndRequestMatcher(logout, samlResponse);\n\t}\n\n\tprivate Saml2RelyingPartyInitiatedLogoutSuccessHandler createSaml2LogoutRequestSuccessHandler(\n\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\tSaml2LogoutRequestResolver logoutRequestResolver = this.logoutRequestConfigurer\n\t\t\t.logoutRequestResolver(registrations);\n\t\treturn new Saml2RelyingPartyInitiatedLogoutSuccessHandler(logoutRequestResolver);\n\t}\n\n\tprivate Saml2LogoutResponseResolver createSaml2LogoutResponseResolver(\n\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\treturn this.logoutResponseConfigurer.logoutResponseResolver(registrations);\n\t}\n\n\tprivate <C> C getBeanOrNull(Class<C> clazz) {\n\t\tif (this.context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.context.getBeanProvider(clazz).getIfAvailable();\n\t}\n\n\t/**\n\t * A configurer for SAML 2.0 LogoutRequest components\n\t */\n\tpublic final class LogoutRequestConfigurer {\n\n\t\tprivate String logoutUrl = \"/logout/saml2/slo\";\n\n\t\tprivate Saml2LogoutRequestValidator logoutRequestValidator;\n\n\t\tprivate Saml2LogoutRequestResolver logoutRequestResolver;\n\n\t\tprivate Saml2LogoutRequestRepository logoutRequestRepository = new HttpSessionLogoutRequestRepository();\n\n\t\tLogoutRequestConfigurer() {\n\t\t}\n\n\t\t/**\n\t\t * The URL by which the asserting party can send a SAML 2.0 Logout Request\n\t\t *\n\t\t * <p>\n\t\t * The Asserting Party should use whatever HTTP method specified in\n\t\t * {@link RelyingPartyRegistration#getSingleLogoutServiceBindings()}.\n\t\t * @param logoutUrl the URL that will receive the SAML 2.0 Logout Request\n\t\t * @return the {@link LogoutRequestConfigurer} for further customizations\n\t\t * @see Saml2LogoutConfigurer#logoutUrl(String)\n\t\t */\n\t\tpublic LogoutRequestConfigurer logoutUrl(String logoutUrl) {\n\t\t\tthis.logoutUrl = logoutUrl;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link LogoutHandler} for processing a logout request from the\n\t\t * asserting party\n\t\t * @param authenticator the {@link Saml2LogoutRequestValidator} to use\n\t\t * @return the {@link LogoutRequestConfigurer} for further customizations\n\t\t */\n\t\tpublic LogoutRequestConfigurer logoutRequestValidator(Saml2LogoutRequestValidator authenticator) {\n\t\t\tthis.logoutRequestValidator = authenticator;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link Saml2LogoutRequestResolver} for producing a logout request to\n\t\t * send to the asserting party\n\t\t * @param logoutRequestResolver the {@link Saml2LogoutRequestResolver} to use\n\t\t * @return the {@link LogoutRequestConfigurer} for further customizations\n\t\t */\n\t\tpublic LogoutRequestConfigurer logoutRequestResolver(Saml2LogoutRequestResolver logoutRequestResolver) {\n\t\t\tthis.logoutRequestResolver = logoutRequestResolver;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link Saml2LogoutRequestRepository} for storing logout requests\n\t\t * @param logoutRequestRepository the {@link Saml2LogoutRequestRepository} to use\n\t\t * @return the {@link LogoutRequestConfigurer} for further customizations\n\t\t */\n\t\tpublic LogoutRequestConfigurer logoutRequestRepository(Saml2LogoutRequestRepository logoutRequestRepository) {\n\t\t\tthis.logoutRequestRepository = logoutRequestRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate Saml2LogoutRequestValidator logoutRequestValidator() {\n\t\t\tif (this.logoutRequestValidator != null) {\n\t\t\t\treturn this.logoutRequestValidator;\n\t\t\t}\n\t\t\tif (USE_OPENSAML_5) {\n\t\t\t\treturn new OpenSaml5LogoutRequestValidator();\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t\t}\n\n\t\tprivate Saml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationRepository registrations) {\n\t\t\tif (this.logoutRequestResolver != null) {\n\t\t\t\treturn this.logoutRequestResolver;\n\t\t\t}\n\t\t\tif (USE_OPENSAML_5) {\n\t\t\t\treturn new OpenSaml5LogoutRequestResolver(registrations);\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t\t}\n\n\t}\n\n\tpublic final class LogoutResponseConfigurer {\n\n\t\tprivate String logoutUrl = \"/logout/saml2/slo\";\n\n\t\tprivate Saml2LogoutResponseValidator logoutResponseValidator;\n\n\t\tprivate Saml2LogoutResponseResolver logoutResponseResolver;\n\n\t\tLogoutResponseConfigurer() {\n\t\t}\n\n\t\t/**\n\t\t * The URL by which the asserting party can send a SAML 2.0 Logout Response\n\t\t *\n\t\t * <p>\n\t\t * The Asserting Party should use whatever HTTP method specified in\n\t\t * {@link RelyingPartyRegistration#getSingleLogoutServiceBindings()}.\n\t\t * @param logoutUrl the URL that will receive the SAML 2.0 Logout Response\n\t\t * @return the {@link LogoutResponseConfigurer} for further customizations\n\t\t * @see Saml2LogoutConfigurer#logoutUrl(String)\n\t\t */\n\t\tpublic LogoutResponseConfigurer logoutUrl(String logoutUrl) {\n\t\t\tthis.logoutUrl = logoutUrl;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link LogoutHandler} for processing a logout response from the\n\t\t * asserting party\n\t\t * @param authenticator the {@link AuthenticationManager} to use\n\t\t * @return the {@link LogoutRequestConfigurer} for further customizations\n\t\t */\n\t\tpublic LogoutResponseConfigurer logoutResponseValidator(Saml2LogoutResponseValidator authenticator) {\n\t\t\tthis.logoutResponseValidator = authenticator;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link Saml2LogoutRequestResolver} for producing a logout response to\n\t\t * send to the asserting party\n\t\t * @param logoutResponseResolver the {@link Saml2LogoutResponseResolver} to use\n\t\t * @return the {@link LogoutRequestConfigurer} for further customizations\n\t\t */\n\t\tpublic LogoutResponseConfigurer logoutResponseResolver(Saml2LogoutResponseResolver logoutResponseResolver) {\n\t\t\tthis.logoutResponseResolver = logoutResponseResolver;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate Saml2LogoutResponseValidator logoutResponseValidator() {\n\t\t\tif (this.logoutResponseValidator != null) {\n\t\t\t\treturn this.logoutResponseValidator;\n\t\t\t}\n\t\t\tif (USE_OPENSAML_5) {\n\t\t\t\treturn new OpenSaml5LogoutResponseValidator();\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t\t}\n\n\t\tprivate Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) {\n\t\t\tif (this.logoutResponseResolver != null) {\n\t\t\t\treturn this.logoutResponseResolver;\n\t\t\t}\n\t\t\tif (USE_OPENSAML_5) {\n\t\t\t\treturn new OpenSaml5LogoutResponseResolver(registrations);\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t\t}\n\n\t}\n\n\tprivate static class Saml2RequestMatcher implements RequestMatcher {\n\n\t\tprivate final SecurityContextHolderStrategy securityContextHolderStrategy;\n\n\t\tSaml2RequestMatcher(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (authentication == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn authentication instanceof Saml2Authentication;\n\t\t}\n\n\t}\n\n\tprivate static class Saml2RelyingPartyInitiatedLogoutFilter extends LogoutFilter {\n\n\t\tSaml2RelyingPartyInitiatedLogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) {\n\t\t\tsuper(logoutSuccessHandler, handlers);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2MetadataConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.saml2;\n\nimport java.util.function.Function;\n\nimport org.opensaml.core.Version;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.saml2.provider.service.metadata.OpenSaml5MetadataResolver;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter;\nimport org.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AbstractHttpConfigurer} for SAML 2.0 Metadata.\n *\n * <p>\n * SAML 2.0 Metadata provides an application with the capability to publish configuration\n * information as a {@code <md:EntityDescriptor>} or {@code <md:EntitiesDescriptor>}.\n *\n * <p>\n * Defaults are provided for all configuration options with the only required\n * configuration being a\n * {@link Saml2LoginConfigurer#relyingPartyRegistrationRepository(HttpSecurityBuilder)}.\n * Alternatively, a {@link RelyingPartyRegistrationRepository} {@code @Bean} may be\n * registered instead.\n *\n * <h2>Security Filters</h2>\n *\n * The following {@code Filter} is populated:\n *\n * <ul>\n * <li>{@link Saml2MetadataFilter}</li>\n * </ul>\n *\n * <h2>Shared Objects Created</h2>\n *\n * none\n *\n * <h2>Shared Objects Used</h2>\n *\n * The following shared objects are used:\n *\n * <ul>\n * <li>{@link RelyingPartyRegistrationRepository} (required)</li>\n * </ul>\n *\n * @since 6.1\n * @see HttpSecurity#saml2Metadata(Customizer)\n * @see Saml2MetadataFilter\n * @see RelyingPartyRegistrationRepository\n */\npublic class Saml2MetadataConfigurer<H extends HttpSecurityBuilder<H>>\n\t\textends AbstractHttpConfigurer<Saml2LogoutConfigurer<H>, H> {\n\n\tprivate static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith(\"5\");\n\n\tprivate final ApplicationContext context;\n\n\tprivate Function<RelyingPartyRegistrationRepository, Saml2MetadataResponseResolver> metadataResponseResolver;\n\n\tpublic Saml2MetadataConfigurer(ApplicationContext context) {\n\t\tthis.context = context;\n\t}\n\n\t/**\n\t * Use this endpoint to request relying party metadata.\n\t *\n\t * <p>\n\t * If you specify a {@code registrationId} placeholder in the URL, then the filter\n\t * will lookup a {@link RelyingPartyRegistration} using that.\n\t *\n\t * <p>\n\t * If there is no {@code registrationId} and your\n\t * {@link RelyingPartyRegistrationRepository} is {code Iterable}, the metadata\n\t * endpoint will try and show all relying parties' metadata in a single\n\t * {@code <md:EntitiesDescriptor} element.\n\t *\n\t * <p>\n\t * If you need a more sophisticated lookup strategy than these, use\n\t * {@link #metadataResponseResolver} instead.\n\t * @param metadataUrl the url to use\n\t * @return the {@link Saml2MetadataConfigurer} for more customizations\n\t */\n\tpublic Saml2MetadataConfigurer<H> metadataUrl(String metadataUrl) {\n\t\tAssert.hasText(metadataUrl, \"metadataUrl cannot be empty\");\n\t\tthis.metadataResponseResolver = (registrations) -> {\n\t\t\tif (USE_OPENSAML_5) {\n\t\t\t\tRequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(\n\t\t\t\t\t\tregistrations, new OpenSaml5MetadataResolver());\n\t\t\t\tmetadata.setRequestMatcher(getRequestMatcherBuilder().matcher(metadataUrl));\n\t\t\t\treturn metadata;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t\t};\n\t\treturn this;\n\t}\n\n\t/**\n\t * Use this {@link Saml2MetadataResponseResolver} to parse the request and respond\n\t * with SAML 2.0 metadata.\n\t * @param metadataResponseResolver to use\n\t * @return the {@link Saml2MetadataConfigurer} for more customizations\n\t */\n\tpublic Saml2MetadataConfigurer<H> metadataResponseResolver(Saml2MetadataResponseResolver metadataResponseResolver) {\n\t\tAssert.notNull(metadataResponseResolver, \"metadataResponseResolver cannot be null\");\n\t\tthis.metadataResponseResolver = (registrations) -> metadataResponseResolver;\n\t\treturn this;\n\t}\n\n\tpublic H and() {\n\t\treturn getBuilder();\n\t}\n\n\t@Override\n\tpublic void configure(H http) {\n\t\tSaml2MetadataResponseResolver metadataResponseResolver = createMetadataResponseResolver(http);\n\t\thttp.addFilterBefore(new Saml2MetadataFilter(metadataResponseResolver), BasicAuthenticationFilter.class);\n\t}\n\n\tprivate Saml2MetadataResponseResolver createMetadataResponseResolver(H http) {\n\t\tif (this.metadataResponseResolver != null) {\n\t\t\tRelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);\n\t\t\treturn this.metadataResponseResolver.apply(registrations);\n\t\t}\n\t\tSaml2MetadataResponseResolver metadataResponseResolver = getBeanOrNull(Saml2MetadataResponseResolver.class);\n\t\tif (metadataResponseResolver != null) {\n\t\t\treturn metadataResponseResolver;\n\t\t}\n\t\tRelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);\n\t\tif (USE_OPENSAML_5) {\n\t\t\treturn new RequestMatcherMetadataResponseResolver(registrations, new OpenSaml5MetadataResolver());\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n\tprivate RelyingPartyRegistrationRepository getRelyingPartyRegistrationRepository(H http) {\n\t\tSaml2LoginConfigurer<H> login = http.getConfigurer(Saml2LoginConfigurer.class);\n\t\tif (login != null) {\n\t\t\treturn login.relyingPartyRegistrationRepository(http);\n\t\t}\n\t\telse {\n\t\t\treturn getBeanOrNull(RelyingPartyRegistrationRepository.class);\n\t\t}\n\t}\n\n\tprivate <C> C getBeanOrNull(Class<C> clazz) {\n\t\tif (this.context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.context.getBeanProvider(clazz).getIfAvailable();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\n\n/**\n * Add this annotation to a {@code Configuration} class to have Spring Security WebFlux\n * support added. User's can then create one or more {@link ServerHttpSecurity}\n * {@code Bean} instances.\n *\n * A minimal configuration can be found below:\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableWebFluxSecurity\n * public class MyMinimalSecurityConfiguration {\n *\n *     &#064;Bean\n *     public MapReactiveUserDetailsService userDetailsService() {\n *          UserDetails user = User.withDefaultPasswordEncoder()\n *               .username(\"user\")\n *               .password(\"password\")\n *               .roles(\"USER\")\n *               .build();\n *          return new MapReactiveUserDetailsService(user);\n *     }\n * }\n * </pre>\n *\n * Below is the same as our minimal configuration, but explicitly declaring the\n * {@code ServerHttpSecurity}.\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableWebFluxSecurity\n * public class MyExplicitSecurityConfiguration {\n *     &#064;Bean\n *     public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n *          http\n *               .authorizeExchange()\n *                    .anyExchange().authenticated()\n *                         .and()\n *                    .httpBasic().and()\n *                    .formLogin();\n *          return http.build();\n *     }\n *\n *     &#064;Bean\n *     public MapReactiveUserDetailsService userDetailsService() {\n *          UserDetails user = User.withDefaultPasswordEncoder()\n *               .username(\"user\")\n *               .password(\"password\")\n *               .roles(\"USER\")\n *               .build();\n *          return new MapReactiveUserDetailsService(user);\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @since 5.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import({ ServerHttpSecurityConfiguration.class, WebFluxSecurityConfiguration.class,\n\t\tReactiveOAuth2ClientImportSelector.class, ReactiveObservationImportSelector.class })\npublic @interface EnableWebFluxSecurity {\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/reactive/ReactiveOAuth2ClientConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.BeanFactoryUtils;\nimport org.springframework.beans.factory.BeanInitializationException;\nimport org.springframework.beans.factory.ListableBeanFactory;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.context.annotation.AnnotationBeanNameGenerator;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.oauth2.client.AuthorizationCodeReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ClientCredentialsReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.DelegatingReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.JwtBearerReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.RefreshTokenReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.TokenExchangeReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.reactive.result.method.annotation.OAuth2AuthorizedClientArgumentResolver;\nimport org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.web.reactive.config.WebFluxConfigurer;\nimport org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;\n\n/**\n * {@link Configuration} for OAuth 2.0 Client support.\n *\n * <p>\n * This {@code Configuration} is conditionally imported by\n * {@link ReactiveOAuth2ClientImportSelector} when the\n * {@code spring-security-oauth2-client} module is present on the classpath.\n *\n * @author Steve Riesenberg\n * @since 6.3\n * @see ReactiveOAuth2ClientImportSelector\n */\n@Import({ ReactiveOAuth2ClientConfiguration.ReactiveOAuth2AuthorizedClientManagerConfiguration.class,\n\t\tReactiveOAuth2ClientConfiguration.OAuth2ClientWebFluxSecurityConfiguration.class })\nfinal class ReactiveOAuth2ClientConfiguration {\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class ReactiveOAuth2AuthorizedClientManagerConfiguration {\n\n\t\t@Bean(name = ReactiveOAuth2AuthorizedClientManagerRegistrar.BEAN_NAME)\n\t\tReactiveOAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar() {\n\t\t\treturn new ReactiveOAuth2AuthorizedClientManagerRegistrar();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class OAuth2ClientWebFluxSecurityConfiguration implements WebFluxConfigurer {\n\n\t\tprivate final ReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\t\tprivate final ReactiveOAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar;\n\n\t\tOAuth2ClientWebFluxSecurityConfiguration(\n\t\t\t\tObjectProvider<ReactiveOAuth2AuthorizedClientManager> authorizedClientManager,\n\t\t\t\tReactiveOAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar) {\n\t\t\tthis.authorizedClientManager = authorizedClientManager.getIfUnique();\n\t\t\tthis.authorizedClientManagerRegistrar = authorizedClientManagerRegistrar;\n\t\t}\n\n\t\t@Override\n\t\tpublic void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {\n\t\t\tReactiveOAuth2AuthorizedClientManager authorizedClientManager = getAuthorizedClientManager();\n\t\t\tif (authorizedClientManager != null) {\n\t\t\t\tconfigurer.addCustomResolver(new OAuth2AuthorizedClientArgumentResolver(authorizedClientManager));\n\t\t\t}\n\t\t}\n\n\t\tprivate ReactiveOAuth2AuthorizedClientManager getAuthorizedClientManager() {\n\t\t\tif (this.authorizedClientManager != null) {\n\t\t\t\treturn this.authorizedClientManager;\n\t\t\t}\n\t\t\treturn this.authorizedClientManagerRegistrar.getAuthorizedClientManagerIfAvailable();\n\t\t}\n\n\t}\n\n\t/**\n\t * A registrar for registering the default\n\t * {@link ReactiveOAuth2AuthorizedClientManager} bean definition, if not already\n\t * present.\n\t */\n\tstatic final class ReactiveOAuth2AuthorizedClientManagerRegistrar\n\t\t\timplements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {\n\n\t\tstatic final String BEAN_NAME = \"authorizedClientManagerRegistrar\";\n\n\t\tstatic final String FACTORY_METHOD_NAME = \"getAuthorizedClientManager\";\n\n\t\t// @formatter:off\n\t\tprivate static final Set<Class<?>> KNOWN_AUTHORIZED_CLIENT_PROVIDERS = Set.of(\n\t\t\t\tAuthorizationCodeReactiveOAuth2AuthorizedClientProvider.class,\n\t\t\t\tRefreshTokenReactiveOAuth2AuthorizedClientProvider.class,\n\t\t\t\tClientCredentialsReactiveOAuth2AuthorizedClientProvider.class,\n\t\t\t\tJwtBearerReactiveOAuth2AuthorizedClientProvider.class,\n\t\t\t\tTokenExchangeReactiveOAuth2AuthorizedClientProvider.class\n\t\t);\n\t\t// @formatter:on\n\n\t\tprivate final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();\n\n\t\tprivate ListableBeanFactory beanFactory;\n\n\t\t@Override\n\t\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\t\tif (getBeanNamesForType(ReactiveOAuth2AuthorizedClientManager.class).length != 0\n\t\t\t\t\t|| getBeanNamesForType(ReactiveClientRegistrationRepository.class).length != 1\n\t\t\t\t\t|| getBeanNamesForType(ServerOAuth2AuthorizedClientRepository.class).length != 1\n\t\t\t\t\t\t\t&& getBeanNamesForType(ReactiveOAuth2AuthorizedClientService.class).length != 1) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tBeanDefinition beanDefinition = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(ReactiveOAuth2AuthorizedClientManager.class)\n\t\t\t\t.setFactoryMethodOnBean(FACTORY_METHOD_NAME, BEAN_NAME)\n\t\t\t\t.getBeanDefinition();\n\n\t\t\tregistry.registerBeanDefinition(this.beanNameGenerator.generateBeanName(beanDefinition, registry),\n\t\t\t\t\tbeanDefinition);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\t\tthis.beanFactory = (ListableBeanFactory) beanFactory;\n\t\t}\n\n\t\tReactiveOAuth2AuthorizedClientManager getAuthorizedClientManagerIfAvailable() {\n\t\t\tif (getBeanNamesForType(ReactiveClientRegistrationRepository.class).length != 1\n\t\t\t\t\t|| getBeanNamesForType(ServerOAuth2AuthorizedClientRepository.class).length != 1\n\t\t\t\t\t\t\t&& getBeanNamesForType(ReactiveOAuth2AuthorizedClientService.class).length != 1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getAuthorizedClientManager();\n\t\t}\n\n\t\tReactiveOAuth2AuthorizedClientManager getAuthorizedClientManager() {\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository = BeanFactoryUtils\n\t\t\t\t.beanOfTypeIncludingAncestors(this.beanFactory, ReactiveClientRegistrationRepository.class, true, true);\n\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\t\t\ttry {\n\t\t\t\tauthorizedClientRepository = BeanFactoryUtils.beanOfTypeIncludingAncestors(this.beanFactory,\n\t\t\t\t\t\tServerOAuth2AuthorizedClientRepository.class, true, true);\n\t\t\t}\n\t\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t\t\tReactiveOAuth2AuthorizedClientService authorizedClientService = BeanFactoryUtils\n\t\t\t\t\t.beanOfTypeIncludingAncestors(this.beanFactory, ReactiveOAuth2AuthorizedClientService.class, true,\n\t\t\t\t\t\t\ttrue);\n\t\t\t\tauthorizedClientRepository = new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(\n\t\t\t\t\t\tauthorizedClientService);\n\t\t\t}\n\n\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviderBeans = BeanFactoryUtils\n\t\t\t\t.beansOfTypeIncludingAncestors(this.beanFactory, ReactiveOAuth2AuthorizedClientProvider.class, true,\n\t\t\t\t\t\ttrue)\n\t\t\t\t.values();\n\n\t\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;\n\t\t\tif (hasDelegatingAuthorizedClientProvider(authorizedClientProviderBeans)) {\n\t\t\t\tauthorizedClientProvider = authorizedClientProviderBeans.iterator().next();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tList<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders = new ArrayList<>();\n\t\t\t\tauthorizedClientProviders\n\t\t\t\t\t.add(getAuthorizationCodeAuthorizedClientProvider(authorizedClientProviderBeans));\n\t\t\t\tauthorizedClientProviders.add(getRefreshTokenAuthorizedClientProvider(authorizedClientProviderBeans));\n\t\t\t\tauthorizedClientProviders\n\t\t\t\t\t.add(getClientCredentialsAuthorizedClientProvider(authorizedClientProviderBeans));\n\n\t\t\t\tReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = getJwtBearerAuthorizedClientProvider(\n\t\t\t\t\t\tauthorizedClientProviderBeans);\n\t\t\t\tif (jwtBearerAuthorizedClientProvider != null) {\n\t\t\t\t\tauthorizedClientProviders.add(jwtBearerAuthorizedClientProvider);\n\t\t\t\t}\n\n\t\t\t\tReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = getTokenExchangeAuthorizedClientProvider(\n\t\t\t\t\t\tauthorizedClientProviderBeans);\n\t\t\t\tif (tokenExchangeAuthorizedClientProvider != null) {\n\t\t\t\t\tauthorizedClientProviders.add(tokenExchangeAuthorizedClientProvider);\n\t\t\t\t}\n\n\t\t\t\tauthorizedClientProviders.addAll(getAdditionalAuthorizedClientProviders(authorizedClientProviderBeans));\n\t\t\t\tauthorizedClientProvider = new DelegatingReactiveOAuth2AuthorizedClientProvider(\n\t\t\t\t\t\tauthorizedClientProviders);\n\t\t\t}\n\n\t\t\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t\t\tConsumer<DefaultReactiveOAuth2AuthorizedClientManager> authorizedClientManagerConsumer = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(Consumer.class,\n\t\t\t\t\t\t\tDefaultReactiveOAuth2AuthorizedClientManager.class));\n\t\t\tif (authorizedClientManagerConsumer != null) {\n\t\t\t\tauthorizedClientManagerConsumer.accept(authorizedClientManager);\n\t\t\t}\n\n\t\t\treturn authorizedClientManager;\n\t\t}\n\n\t\tprivate boolean hasDelegatingAuthorizedClientProvider(\n\t\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tif (authorizedClientProviders.size() != 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn authorizedClientProviders.iterator()\n\t\t\t\t.next() instanceof DelegatingReactiveOAuth2AuthorizedClientProvider;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AuthorizedClientProvider getAuthorizationCodeAuthorizedClientProvider(\n\t\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tAuthorizationCodeReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, AuthorizationCodeReactiveOAuth2AuthorizedClientProvider.class);\n\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\tauthorizedClientProvider = new AuthorizationCodeReactiveOAuth2AuthorizedClientProvider();\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AuthorizedClientProvider getRefreshTokenAuthorizedClientProvider(\n\t\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tRefreshTokenReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, RefreshTokenReactiveOAuth2AuthorizedClientProvider.class);\n\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\tauthorizedClientProvider = new RefreshTokenReactiveOAuth2AuthorizedClientProvider();\n\t\t\t}\n\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(ReactiveOAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\t\tOAuth2RefreshTokenGrantRequest.class));\n\t\t\tif (accessTokenResponseClient != null) {\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AuthorizedClientProvider getClientCredentialsAuthorizedClientProvider(\n\t\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tClientCredentialsReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, ClientCredentialsReactiveOAuth2AuthorizedClientProvider.class);\n\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\tauthorizedClientProvider = new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();\n\t\t\t}\n\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(ReactiveOAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\t\tOAuth2ClientCredentialsGrantRequest.class));\n\t\t\tif (accessTokenResponseClient != null) {\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AuthorizedClientProvider getJwtBearerAuthorizedClientProvider(\n\t\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tJwtBearerReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, JwtBearerReactiveOAuth2AuthorizedClientProvider.class);\n\n\t\t\tReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(ReactiveOAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\t\tJwtBearerGrantRequest.class));\n\t\t\tif (accessTokenResponseClient != null) {\n\t\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\t\tauthorizedClientProvider = new JwtBearerReactiveOAuth2AuthorizedClientProvider();\n\t\t\t\t}\n\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AuthorizedClientProvider getTokenExchangeAuthorizedClientProvider(\n\t\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tTokenExchangeReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\t\tauthorizedClientProviders, TokenExchangeReactiveOAuth2AuthorizedClientProvider.class);\n\n\t\t\tReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\t\tResolvableType.forClassWithGenerics(ReactiveOAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\t\tTokenExchangeGrantRequest.class));\n\t\t\tif (accessTokenResponseClient != null) {\n\t\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\t\tauthorizedClientProvider = new TokenExchangeReactiveOAuth2AuthorizedClientProvider();\n\t\t\t\t}\n\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t\t}\n\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate List<ReactiveOAuth2AuthorizedClientProvider> getAdditionalAuthorizedClientProviders(\n\t\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\t\tList<ReactiveOAuth2AuthorizedClientProvider> additionalAuthorizedClientProviders = new ArrayList<>(\n\t\t\t\t\tauthorizedClientProviders);\n\t\t\tadditionalAuthorizedClientProviders\n\t\t\t\t.removeIf((provider) -> KNOWN_AUTHORIZED_CLIENT_PROVIDERS.contains(provider.getClass()));\n\t\t\treturn additionalAuthorizedClientProviders;\n\t\t}\n\n\t\tprivate <T extends ReactiveOAuth2AuthorizedClientProvider> T getAuthorizedClientProviderByType(\n\t\t\t\tCollection<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders, Class<T> providerClass) {\n\t\t\tT authorizedClientProvider = null;\n\t\t\tfor (ReactiveOAuth2AuthorizedClientProvider current : authorizedClientProviders) {\n\t\t\t\tif (providerClass.isInstance(current)) {\n\t\t\t\t\tassertAuthorizedClientProviderIsNull(authorizedClientProvider);\n\t\t\t\t\tauthorizedClientProvider = providerClass.cast(current);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\tprivate static void assertAuthorizedClientProviderIsNull(\n\t\t\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider) {\n\t\t\tif (authorizedClientProvider != null) {\n\t\t\t\t// @formatter:off\n\t\t\t\tthrow new BeanInitializationException(String.format(\n\t\t\t\t\t\t\"Unable to create a %s bean. Expected one bean of type %s, but found multiple. \" +\n\t\t\t\t\t\t\"Please consider defining only a single bean of this type, or define a %s bean yourself.\",\n\t\t\t\t\t\tReactiveOAuth2AuthorizedClientManager.class.getName(),\n\t\t\t\t\t\tauthorizedClientProvider.getClass().getName(),\n\t\t\t\t\t\tReactiveOAuth2AuthorizedClientManager.class.getName()));\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t}\n\n\t\tprivate <T> String[] getBeanNamesForType(Class<T> beanClass) {\n\t\t\treturn BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, beanClass, true, true);\n\t\t}\n\n\t\tprivate <T> T getBeanOfType(ResolvableType resolvableType) {\n\t\t\tObjectProvider<T> objectProvider = this.beanFactory.getBeanProvider(resolvableType, true);\n\t\t\treturn objectProvider.getIfAvailable();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/reactive/ReactiveOAuth2ClientImportSelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Used by {@link EnableWebFluxSecurity} to conditionally import\n * {@link ReactiveOAuth2ClientConfiguration}.\n *\n * <p>\n * This {@code Configuration} is imported by {@link EnableWebFluxSecurity}\n *\n * @author Rob Winch\n * @author Alavudin Kuttikkattil\n * @since 5.1\n */\nfinal class ReactiveOAuth2ClientImportSelector implements ImportSelector {\n\n\tprivate static final boolean oauth2ClientPresent;\n\n\tstatic {\n\t\toauth2ClientPresent = ClassUtils.isPresent(\n\t\t\t\t\"org.springframework.security.oauth2.client.registration.ClientRegistration\",\n\t\t\t\tReactiveOAuth2ClientImportSelector.class.getClassLoader());\n\t}\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\tif (!oauth2ClientPresent) {\n\t\t\treturn new String[0];\n\t\t}\n\t\treturn new String[] {\n\t\t\t\t\"org.springframework.security.config.annotation.web.reactive.ReactiveOAuth2ClientConfiguration\" };\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/reactive/ReactiveObservationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.authentication.ObservationReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authorization.ObservationReactiveAuthorizationManager;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\nimport org.springframework.security.web.server.ObservationWebFilterChainDecorator;\nimport org.springframework.security.web.server.WebFilterChainProxy.WebFilterChainDecorator;\nimport org.springframework.web.server.ServerWebExchange;\n\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nclass ReactiveObservationConfiguration {\n\n\tprivate static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()\n\t\t.shouldObserveRequests(true)\n\t\t.shouldObserveAuthentications(true)\n\t\t.shouldObserveAuthorizations(true)\n\t\t.build();\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>> webAuthorizationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();\n\t\t\t\treturn active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<ReactiveAuthenticationManager> reactiveAuthenticationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic ReactiveAuthenticationManager postProcess(ReactiveAuthenticationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthentications();\n\t\t\t\treturn active ? new ObservationReactiveAuthenticationManager(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<WebFilterChainDecorator> filterChainDecoratorPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic WebFilterChainDecorator postProcess(WebFilterChainDecorator object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveRequests();\n\t\t\t\treturn active ? new ObservationWebFilterChainDecorator(r) : object;\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/reactive/ReactiveObservationImportSelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Used by {@link EnableWebFluxSecurity} to conditionally import observation configuration\n * when {@link ObservationRegistry} is present.\n *\n * @author Josh Cummings\n * @since 6.4\n */\nclass ReactiveObservationImportSelector implements ImportSelector {\n\n\tprivate static final boolean observabilityPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = ReactiveObservationImportSelector.class.getClassLoader();\n\t\tobservabilityPresent = ClassUtils.isPresent(\"io.micrometer.observation.ObservationRegistry\", classLoader);\n\t}\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\tif (!observabilityPresent) {\n\t\t\treturn new String[0];\n\t\t}\n\t\treturn new String[] { ReactiveObservationConfiguration.class.getName() };\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/reactive/ServerHttpSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport java.lang.reflect.Modifier;\nimport java.util.Map;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.context.expression.BeanFactoryResolver;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;\nimport org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver;\nimport org.springframework.security.web.reactive.result.method.annotation.CurrentSecurityContextArgumentResolver;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.web.reactive.config.WebFluxConfigurer;\nimport org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n * @author Dan Zheng\n * @since 5.0\n */\n@Configuration(proxyBeanMethods = false)\nclass ServerHttpSecurityConfiguration {\n\n\tprivate static final String BEAN_NAME_PREFIX = \"org.springframework.security.config.annotation.web.reactive.HttpSecurityConfiguration.\";\n\n\tprivate static final String HTTPSECURITY_BEAN_NAME = BEAN_NAME_PREFIX + \"httpSecurity\";\n\n\tprivate ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry();\n\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\tprivate ReactiveUserDetailsService reactiveUserDetailsService;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\tprivate ReactiveUserDetailsPasswordService userDetailsPasswordService;\n\n\tprivate ReactiveCompromisedPasswordChecker compromisedPasswordChecker;\n\n\tprivate ObjectPostProcessor<ReactiveAuthenticationManager> postProcessor = ObjectPostProcessor.identity();\n\n\t@Autowired(required = false)\n\tprivate BeanFactory beanFactory;\n\n\t@Autowired(required = false)\n\tvoid setAdapterRegistry(ReactiveAdapterRegistry adapterRegistry) {\n\t\tthis.adapterRegistry = adapterRegistry;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setAuthenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setReactiveUserDetailsService(ReactiveUserDetailsService reactiveUserDetailsService) {\n\t\tthis.reactiveUserDetailsService = reactiveUserDetailsService;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tthis.passwordEncoder = passwordEncoder;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setUserDetailsPasswordService(ReactiveUserDetailsPasswordService userDetailsPasswordService) {\n\t\tthis.userDetailsPasswordService = userDetailsPasswordService;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setAuthenticationManagerPostProcessor(\n\t\t\tMap<String, ObjectPostProcessor<ReactiveAuthenticationManager>> postProcessors) {\n\t\tif (postProcessors.size() == 1) {\n\t\t\tthis.postProcessor = postProcessors.values().iterator().next();\n\t\t}\n\t\tthis.postProcessor = postProcessors.get(\"reactiveAuthenticationManagerPostProcessor\");\n\t}\n\n\t@Autowired(required = false)\n\tvoid setCompromisedPasswordChecker(ReactiveCompromisedPasswordChecker compromisedPasswordChecker) {\n\t\tthis.compromisedPasswordChecker = compromisedPasswordChecker;\n\t}\n\n\t@Bean\n\tstatic WebFluxConfigurer authenticationPrincipalArgumentResolverConfigurer(\n\t\t\tObjectProvider<AuthenticationPrincipalArgumentResolver> authenticationPrincipalArgumentResolver,\n\t\t\tObjectProvider<CurrentSecurityContextArgumentResolver> currentSecurityContextArgumentResolvers) {\n\t\treturn new WebFluxConfigurer() {\n\n\t\t\t@Override\n\t\t\tpublic void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {\n\t\t\t\tconfigurer.addCustomResolver(authenticationPrincipalArgumentResolver.getObject(),\n\t\t\t\t\t\tcurrentSecurityContextArgumentResolvers.getObject());\n\t\t\t}\n\n\t\t};\n\t}\n\n\t@Bean\n\tAuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver(\n\t\t\tObjectProvider<AnnotationTemplateExpressionDefaults> templateDefaults) {\n\t\tAuthenticationPrincipalArgumentResolver resolver = new AuthenticationPrincipalArgumentResolver(\n\t\t\t\tthis.adapterRegistry);\n\t\tif (this.beanFactory != null) {\n\t\t\tresolver.setBeanResolver(new BeanFactoryResolver(this.beanFactory));\n\t\t}\n\t\ttemplateDefaults.ifAvailable(resolver::setTemplateDefaults);\n\t\treturn resolver;\n\t}\n\n\t@Bean\n\tCurrentSecurityContextArgumentResolver reactiveCurrentSecurityContextArgumentResolver(\n\t\t\tObjectProvider<AnnotationTemplateExpressionDefaults> templateDefaults) {\n\t\tCurrentSecurityContextArgumentResolver resolver = new CurrentSecurityContextArgumentResolver(\n\t\t\t\tthis.adapterRegistry);\n\t\tif (this.beanFactory != null) {\n\t\t\tresolver.setBeanResolver(new BeanFactoryResolver(this.beanFactory));\n\t\t}\n\t\ttemplateDefaults.ifAvailable(resolver::setTemplateDefaults);\n\t\treturn resolver;\n\t}\n\n\t@Bean(HTTPSECURITY_BEAN_NAME)\n\t@Scope(\"prototype\")\n\tServerHttpSecurity httpSecurity(ApplicationContext context) {\n\t\tServerHttpSecurity http = httpSecurity();\n\t\tapplyServerHttpSecurityCustomizers(context, http);\n\t\tapplyTopLevelBeanCustomizers(context, http);\n\t\treturn http;\n\t}\n\n\t/**\n\t * Applies all {@code Customizer<ServerHttpSecurity>} Beans to\n\t * {@link ServerHttpSecurity}.\n\t * @param context the {@link ApplicationContext}\n\t * @param http the {@link ServerHttpSecurity}\n\t * @throws Exception\n\t */\n\tprivate void applyServerHttpSecurityCustomizers(ApplicationContext context, ServerHttpSecurity http) {\n\t\tResolvableType httpSecurityCustomizerType = ResolvableType.forClassWithGenerics(Customizer.class,\n\t\t\t\tServerHttpSecurity.class);\n\t\tObjectProvider<Customizer<ServerHttpSecurity>> customizerProvider = context\n\t\t\t.getBeanProvider(httpSecurityCustomizerType);\n\n\t\t// @formatter:off\n\t\tcustomizerProvider.orderedStream().forEach((customizer) ->\n\t\t\tcustomizer.customize(http)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Applies all {@link Customizer} Beans to top level {@link ServerHttpSecurity}\n\t * method.\n\t *\n\t * For each public, non-static method in ServerHttpSecurity that accepts a Customizer\n\t * <ul>\n\t * <li>Use the {@link MethodParameter} (this preserves generics) to resolve all Beans\n\t * for that type</li>\n\t * <li>For each {@link Customizer} Bean invoke the {@link java.lang.reflect.Method}\n\t * with the {@link Customizer} Bean as the argument</li>\n\t * </ul>\n\t * @param context the {@link ApplicationContext}\n\t * @param http the {@link ServerHttpSecurity}\n\t * @throws Exception\n\t */\n\tprivate void applyTopLevelBeanCustomizers(ApplicationContext context, ServerHttpSecurity http) {\n\t\tReflectionUtils.MethodFilter isCustomizerMethod = (method) -> {\n\t\t\tif (Modifier.isStatic(method.getModifiers())) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!Modifier.isPublic(method.getModifiers())) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!method.canAccess(http)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (method.getParameterCount() != 1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (method.getParameterTypes()[0] == Customizer.class) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\t\tReflectionUtils.MethodCallback invokeWithEachCustomizerBean = (customizerMethod) -> {\n\n\t\t\tMethodParameter customizerParameter = new MethodParameter(customizerMethod, 0);\n\t\t\tResolvableType customizerType = ResolvableType.forMethodParameter(customizerParameter);\n\t\t\tObjectProvider<?> customizerProvider = context.getBeanProvider(customizerType);\n\n\t\t\t// @formatter:off\n\t\t\tcustomizerProvider.orderedStream().forEach((customizer) ->\n\t\t\t\t\tReflectionUtils.invokeMethod(customizerMethod, http, customizer)\n\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t};\n\t\tReflectionUtils.doWithMethods(ServerHttpSecurity.class, invokeWithEachCustomizerBean, isCustomizerMethod);\n\t}\n\n\tServerHttpSecurity httpSecurity() {\n\t\tContextAwareServerHttpSecurity http = new ContextAwareServerHttpSecurity();\n\t\t// @formatter:off\n\t\treturn http.authenticationManager(authenticationManager())\n\t\t\t.headers(withDefaults())\n\t\t\t.logout(withDefaults());\n\t\t// @formatter:on\n\t}\n\n\tprivate ReactiveAuthenticationManager authenticationManager() {\n\t\tif (this.authenticationManager != null) {\n\t\t\treturn this.authenticationManager;\n\t\t}\n\t\tif (this.reactiveUserDetailsService != null) {\n\t\t\tUserDetailsRepositoryReactiveAuthenticationManager manager = new UserDetailsRepositoryReactiveAuthenticationManager(\n\t\t\t\t\tthis.reactiveUserDetailsService);\n\t\t\tif (this.passwordEncoder != null) {\n\t\t\t\tmanager.setPasswordEncoder(this.passwordEncoder);\n\t\t\t}\n\t\t\tif (this.userDetailsPasswordService != null) {\n\t\t\t\tmanager.setUserDetailsPasswordService(this.userDetailsPasswordService);\n\t\t\t}\n\t\t\tmanager.setCompromisedPasswordChecker(this.compromisedPasswordChecker);\n\t\t\treturn this.postProcessor.postProcess(manager);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static class ContextAwareServerHttpSecurity extends ServerHttpSecurity implements ApplicationContextAware {\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\t\tsuper.setApplicationContext(applicationContext);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.WebFilterChainProxy;\nimport org.springframework.security.web.server.WebFilterChainProxy.DefaultWebFilterChainDecorator;\nimport org.springframework.security.web.server.WebFilterChainProxy.WebFilterChainDecorator;\nimport org.springframework.security.web.server.firewall.ServerExchangeRejectedHandler;\nimport org.springframework.security.web.server.firewall.ServerWebExchangeFirewall;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.web.reactive.result.view.AbstractView;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n * @author Yanming Zhou\n * @since 5.0\n */\n@Configuration(proxyBeanMethods = false)\nclass WebFluxSecurityConfiguration {\n\n\tpublic static final int WEB_FILTER_CHAIN_FILTER_ORDER = -100;\n\n\tprivate static final String BEAN_NAME_PREFIX = \"org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.\";\n\n\tprivate static final String SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME = BEAN_NAME_PREFIX\n\t\t\t+ \"WebFilterChainFilter\";\n\n\tpublic static final String REACTIVE_CLIENT_REGISTRATION_REPOSITORY_CLASSNAME = \"org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository\";\n\n\tprivate static final boolean isOAuth2Present;\n\n\tprivate List<SecurityWebFilterChain> securityWebFilterChains;\n\n\tprivate ObjectPostProcessor<WebFilterChainDecorator> postProcessor = ObjectPostProcessor.identity();\n\n\tstatic {\n\t\tisOAuth2Present = ClassUtils.isPresent(REACTIVE_CLIENT_REGISTRATION_REPOSITORY_CLASSNAME,\n\t\t\t\tWebFluxSecurityConfiguration.class.getClassLoader());\n\t}\n\n\t@Autowired\n\tApplicationContext context;\n\n\t@Autowired(required = false)\n\tvoid setSecurityWebFilterChains(List<SecurityWebFilterChain> securityWebFilterChains) {\n\t\tthis.securityWebFilterChains = securityWebFilterChains;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setFilterChainPostProcessor(ObjectPostProcessor<WebFilterChainDecorator> postProcessor) {\n\t\tthis.postProcessor = postProcessor;\n\t}\n\n\t@Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME)\n\t@Order(WEB_FILTER_CHAIN_FILTER_ORDER)\n\tWebFilterChainProxy springSecurityWebFilterChainFilter(ObjectProvider<ServerWebExchangeFirewall> firewall,\n\t\t\tObjectProvider<ServerExchangeRejectedHandler> rejectedHandler) {\n\t\tWebFilterChainProxy proxy = new WebFilterChainProxy(getSecurityWebFilterChains());\n\t\tWebFilterChainDecorator decorator = this.postProcessor.postProcess(new DefaultWebFilterChainDecorator());\n\t\tproxy.setFilterChainDecorator(decorator);\n\t\tfirewall.ifUnique(proxy::setFirewall);\n\t\trejectedHandler.ifUnique(proxy::setExchangeRejectedHandler);\n\t\treturn proxy;\n\t}\n\n\t@Bean(name = AbstractView.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME)\n\tCsrfRequestDataValueProcessor requestDataValueProcessor() {\n\t\treturn new CsrfRequestDataValueProcessor();\n\t}\n\n\t@Bean\n\tstatic RsaKeyConversionServicePostProcessor conversionServicePostProcessor() {\n\t\treturn new RsaKeyConversionServicePostProcessor();\n\t}\n\n\tprivate List<SecurityWebFilterChain> getSecurityWebFilterChains() {\n\t\tList<SecurityWebFilterChain> result = this.securityWebFilterChains;\n\t\tif (ObjectUtils.isEmpty(result)) {\n\t\t\treturn Arrays.asList(springSecurityFilterChain());\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate SecurityWebFilterChain springSecurityFilterChain() {\n\t\tServerHttpSecurity http = this.context.getBean(ServerHttpSecurity.class);\n\t\treturn springSecurityFilterChain(http);\n\t}\n\n\t/**\n\t * The default {@link ServerHttpSecurity} configuration.\n\t * @param http\n\t * @return\n\t */\n\tprivate SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t\thttp.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\tif (isOAuth2Present && OAuth2ClasspathGuard.shouldConfigure(this.context)) {\n\t\t\tOAuth2ClasspathGuard.configure(this.context, http);\n\t\t}\n\t\telse {\n\t\t\thttp.httpBasic(withDefaults());\n\t\t\thttp.formLogin(withDefaults());\n\t\t}\n\t\tSecurityWebFilterChain result = http.build();\n\t\treturn result;\n\t}\n\n\tprivate static class OAuth2ClasspathGuard {\n\n\t\tstatic void configure(ApplicationContext context, ServerHttpSecurity http) {\n\t\t\thttp.oauth2Login(withDefaults());\n\t\t\thttp.oauth2Client(withDefaults());\n\t\t}\n\n\t\tstatic boolean shouldConfigure(ApplicationContext context) {\n\t\t\tClassLoader loader = context.getClassLoader();\n\t\t\tClass<?> reactiveClientRegistrationRepositoryClass = ClassUtils\n\t\t\t\t.resolveClassName(REACTIVE_CLIENT_REGISTRATION_REPOSITORY_CLASSNAME, loader);\n\t\t\treturn context.getBeanNamesForType(reactiveClientRegistrationRepositoryClass).length == 1;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/servlet/configuration/WebMvcSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.servlet.configuration;\n\nimport java.util.List;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver;\nimport org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\nimport org.springframework.web.servlet.support.RequestDataValueProcessor;\n\n/**\n * Used to add a {@link RequestDataValueProcessor} for Spring MVC and Spring Security CSRF\n * integration. This configuration is added whenever {@link EnableWebMvc} is added by\n * <a href=\"\n * {@docRoot}/org/springframework/security/config/annotation/web/configuration/SpringWebMvcImportSelector.html\">SpringWebMvcImportSelector</a> and\n * the DispatcherServlet is present on the classpath. It also adds the\n * {@link AuthenticationPrincipalArgumentResolver} as a\n * {@link HandlerMethodArgumentResolver}.\n *\n * @author Rob Winch\n * @since 3.2\n * @deprecated This is applied internally using SpringWebMvcImportSelector\n */\n@Deprecated\n@Configuration(proxyBeanMethods = false)\n@EnableWebSecurity\npublic class WebMvcSecurityConfiguration implements WebMvcConfigurer {\n\n\t@Override\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {\n\t\targumentResolvers.add(new AuthenticationPrincipalArgumentResolver());\n\t\targumentResolvers\n\t\t\t.add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());\n\t}\n\n\t@Bean\n\tpublic RequestDataValueProcessor requestDataValueProcessor() {\n\t\treturn new CsrfRequestDataValueProcessor();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/socket/EnableWebSocketSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.Import;\n\n/**\n * Allows configuring WebSocket Authorization.\n *\n * <p>\n * For example:\n * </p>\n *\n * <pre>\n * &#064;Configuration\n * &#064;EnableWebSocketSecurity\n * public class WebSocketSecurityConfig {\n *\n * \t&#064;Bean\n * \tAuthorizationManager&lt;Message&lt;?&gt;&gt; authorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n * \t\tmessages.simpDestMatchers(&quot;/user/queue/errors&quot;).permitAll()\n * \t\t\t\t.simpDestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)\n * \t\t\t\t.anyMessage().authenticated();\n *\t\treturn messages.build();\n * \t}\n * }\n * </pre>\n *\n * @author Josh Cummings\n * @since 5.8\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import({ WebSocketMessageBrokerSecurityConfiguration.class, WebSocketObservationImportSelector.class })\npublic @interface EnableWebSocketSecurity {\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/socket/MessageMatcherAuthorizationManagerConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Fallback;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.security.config.web.messaging.PathPatternMessageMatcherBuilderFactoryBean;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\n\nfinal class MessageMatcherAuthorizationManagerConfiguration {\n\n\t@Bean\n\t@Fallback\n\tPathPatternMessageMatcherBuilderFactoryBean messageMatcherBuilderFactoryBean() {\n\t\treturn new PathPatternMessageMatcherBuilderFactoryBean();\n\t}\n\n\t@Bean\n\t@Scope(\"prototype\")\n\tMessageMatcherDelegatingAuthorizationManager.Builder messageAuthorizationManagerBuilder() {\n\t\treturn MessageMatcherDelegatingAuthorizationManager.builder();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.SmartInitializingSingleton;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;\nimport org.springframework.messaging.simp.config.ChannelRegistration;\nimport org.springframework.messaging.support.ChannelInterceptor;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.SpringAuthorizationEventPublisher;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;\nimport org.springframework.security.messaging.context.SecurityContextChannelInterceptor;\nimport org.springframework.security.messaging.web.csrf.XorCsrfChannelInterceptor;\nimport org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;\nimport org.springframework.util.Assert;\nimport org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;\nimport org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;\nimport org.springframework.web.socket.server.HandshakeInterceptor;\nimport org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;\nimport org.springframework.web.socket.sockjs.SockJsService;\nimport org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;\nimport org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService;\n\n@Order(Ordered.HIGHEST_PRECEDENCE + 100)\n@Import(MessageMatcherAuthorizationManagerConfiguration.class)\nfinal class WebSocketMessageBrokerSecurityConfiguration\n\t\timplements WebSocketMessageBrokerConfigurer, SmartInitializingSingleton {\n\n\tprivate static final String SIMPLE_URL_HANDLER_MAPPING_BEAN_NAME = \"stompWebSocketHandlerMapping\";\n\n\tprivate static final String CSRF_CHANNEL_INTERCEPTOR_BEAN_NAME = \"csrfChannelInterceptor\";\n\n\tprivate MessageMatcherDelegatingAuthorizationManager b;\n\n\tprivate static final AuthorizationManager<Message<?>> ANY_MESSAGE_AUTHENTICATED = MessageMatcherDelegatingAuthorizationManager\n\t\t.builder()\n\t\t.anyMessage()\n\t\t.authenticated()\n\t\t.build();\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final SecurityContextChannelInterceptor securityContextChannelInterceptor = new SecurityContextChannelInterceptor();\n\n\tprivate ChannelInterceptor csrfChannelInterceptor = new XorCsrfChannelInterceptor();\n\n\tprivate AuthorizationManager<Message<?>> authorizationManager = ANY_MESSAGE_AUTHENTICATED;\n\n\tprivate ObjectPostProcessor<AuthorizationManager<Message<?>>> postProcessor = ObjectPostProcessor.identity();\n\n\tprivate ApplicationContext context;\n\n\tprivate AnnotationTemplateExpressionDefaults templateDefaults;\n\n\tWebSocketMessageBrokerSecurityConfiguration(ApplicationContext context) {\n\t\tthis.context = context;\n\t}\n\n\t@Override\n\tpublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {\n\t\tAuthenticationPrincipalArgumentResolver resolver = new AuthenticationPrincipalArgumentResolver();\n\t\tresolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tresolver.setTemplateDefaults(this.templateDefaults);\n\t\targumentResolvers.add(resolver);\n\t}\n\n\t@Override\n\tpublic void configureClientInboundChannel(ChannelRegistration registration) {\n\t\tChannelInterceptor csrfChannelInterceptor = getBeanOrNull(CSRF_CHANNEL_INTERCEPTOR_BEAN_NAME,\n\t\t\t\tChannelInterceptor.class);\n\t\tif (csrfChannelInterceptor != null) {\n\t\t\tthis.csrfChannelInterceptor = csrfChannelInterceptor;\n\t\t}\n\n\t\tAuthorizationManager<Message<?>> manager = this.authorizationManager;\n\t\tmanager = this.postProcessor.postProcess(manager);\n\t\tAuthorizationChannelInterceptor interceptor = new AuthorizationChannelInterceptor(manager);\n\t\tinterceptor.setAuthorizationEventPublisher(new SpringAuthorizationEventPublisher(this.context));\n\t\tinterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tthis.securityContextChannelInterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tregistration.interceptors(this.securityContextChannelInterceptor, this.csrfChannelInterceptor, interceptor);\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setAuthorizationManager(AuthorizationManager<Message<?>> authorizationManager) {\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setMessageAuthorizationManagerPostProcessor(\n\t\t\tObjectPostProcessor<AuthorizationManager<Message<?>>> postProcessor) {\n\t\tthis.postProcessor = postProcessor;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.templateDefaults = templateDefaults;\n\t}\n\n\t@Override\n\tpublic void afterSingletonsInstantiated() {\n\t\tSimpleUrlHandlerMapping mapping = getBeanOrNull(SIMPLE_URL_HANDLER_MAPPING_BEAN_NAME,\n\t\t\t\tSimpleUrlHandlerMapping.class);\n\t\tif (mapping == null) {\n\t\t\treturn;\n\t\t}\n\t\tconfigureCsrf(mapping);\n\t}\n\n\tprivate <T> T getBeanOrNull(String name, Class<T> type) {\n\t\tMap<String, T> beans = this.context.getBeansOfType(type);\n\t\treturn beans.get(name);\n\t}\n\n\tprivate void configureCsrf(SimpleUrlHandlerMapping mapping) {\n\t\tMap<String, Object> mappings = mapping.getHandlerMap();\n\t\tfor (Object object : mappings.values()) {\n\t\t\tif (object instanceof SockJsHttpRequestHandler) {\n\t\t\t\tsetHandshakeInterceptors((SockJsHttpRequestHandler) object);\n\t\t\t}\n\t\t\telse if (object instanceof WebSocketHttpRequestHandler) {\n\t\t\t\tsetHandshakeInterceptors((WebSocketHttpRequestHandler) object);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\"Bean \" + SIMPLE_URL_HANDLER_MAPPING_BEAN_NAME + \" is expected to contain mappings to either a \"\n\t\t\t\t\t\t\t\t+ \"SockJsHttpRequestHandler or a WebSocketHttpRequestHandler but got \" + object);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void setHandshakeInterceptors(SockJsHttpRequestHandler handler) {\n\t\tSockJsService sockJsService = handler.getSockJsService();\n\t\tAssert.state(sockJsService instanceof TransportHandlingSockJsService,\n\t\t\t\t() -> \"sockJsService must be instance of TransportHandlingSockJsService got \" + sockJsService);\n\t\tTransportHandlingSockJsService transportHandlingSockJsService = (TransportHandlingSockJsService) sockJsService;\n\t\tList<HandshakeInterceptor> handshakeInterceptors = transportHandlingSockJsService.getHandshakeInterceptors();\n\t\tList<HandshakeInterceptor> interceptorsToSet = new ArrayList<>(handshakeInterceptors.size() + 1);\n\t\tinterceptorsToSet.add(new CsrfTokenHandshakeInterceptor());\n\t\tinterceptorsToSet.addAll(handshakeInterceptors);\n\t\ttransportHandlingSockJsService.setHandshakeInterceptors(interceptorsToSet);\n\t}\n\n\tprivate void setHandshakeInterceptors(WebSocketHttpRequestHandler handler) {\n\t\tList<HandshakeInterceptor> handshakeInterceptors = handler.getHandshakeInterceptors();\n\t\tList<HandshakeInterceptor> interceptorsToSet = new ArrayList<>(handshakeInterceptors.size() + 1);\n\t\tinterceptorsToSet.add(new CsrfTokenHandshakeInterceptor());\n\t\tinterceptorsToSet.addAll(handshakeInterceptors);\n\t\thandler.setHandshakeInterceptors(interceptorsToSet);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/socket/WebSocketObservationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.ObservationAuthorizationManager;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\n\n@Configuration(proxyBeanMethods = false)\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nclass WebSocketObservationConfiguration {\n\n\tprivate static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()\n\t\t.shouldObserveRequests(true)\n\t\t.shouldObserveAuthentications(true)\n\t\t.shouldObserveAuthorizations(true)\n\t\t.build();\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tstatic ObjectPostProcessor<AuthorizationManager<Message<?>>> webSocketAuthorizationManagerPostProcessor(\n\t\t\tObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {\n\t\treturn new ObjectPostProcessor<>() {\n\t\t\t@Override\n\t\t\tpublic AuthorizationManager postProcess(AuthorizationManager object) {\n\t\t\t\tObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);\n\t\t\t\tboolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();\n\t\t\t\treturn active ? new ObservationAuthorizationManager<>(r, object) : object;\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/annotation/web/socket/WebSocketObservationImportSelector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Used by {@link EnableWebSocketSecurity} to conditionally import observation\n * configuration when {@link ObservationRegistry} is present.\n *\n * @author Josh Cummings\n * @since 6.4\n */\nclass WebSocketObservationImportSelector implements ImportSelector {\n\n\tprivate static final boolean observabilityPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = WebSocketObservationImportSelector.class.getClassLoader();\n\t\tobservabilityPresent = ClassUtils.isPresent(\"io.micrometer.observation.ObservationRegistry\", classLoader);\n\t}\n\n\t@Override\n\tpublic String[] selectImports(AnnotationMetadata importingClassMetadata) {\n\t\tif (!observabilityPresent) {\n\t\t\treturn new String[0];\n\t\t}\n\t\treturn new String[] { WebSocketObservationConfiguration.class.getName() };\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/aot/hint/OAuth2LoginRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.aot.hint;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\n\n/**\n * {@link RuntimeHintsRegistrar} for\n * {@link org.springframework.security.config.web.server.ServerHttpSecurity}\n *\n * @author Marcus Da Coregio\n * @since 6.0.2\n */\nclass OAuth2LoginRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {\n\t\thints.reflection()\n\t\t\t.registerTypeIfPresent(classLoader, \"org.springframework.security.oauth2.jwt.JwtDecoder\",\n\t\t\t\t\tMemberCategory.INVOKE_PUBLIC_METHODS);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.aot.hint;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\n\n/**\n * Runtime hints for\n * {@link org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration}\n *\n * @author Marcus da Coregio\n */\nclass WebMvcSecurityConfigurationRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {\n\t\thints.reflection()\n\t\t\t.registerType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy\"),\n\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/aot/hint/WebSecurityConfigurationRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.aot.hint;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\n\n/**\n * Runtime hints for\n * {@link org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration}\n *\n * @author Marcus da Coregio\n */\nclass WebSecurityConfigurationRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {\n\t\thints.reflection()\n\t\t\t.registerType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy\"),\n\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS);\n\t\thints.reflection()\n\t\t\t.registerType(TypeReference.of(\"org.springframework.web.filter.ServletRequestPathFilter\"),\n\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/authentication/AbstractUserDetailsServiceBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.BeanDefinitionStoreException;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.AbstractBeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.authentication.CachingUserDetailsService;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Luke Taylor\n */\npublic abstract class AbstractUserDetailsServiceBeanDefinitionParser implements BeanDefinitionParser {\n\n\tstatic final String CACHE_REF = \"cache-ref\";\n\n\tpublic static final String CACHING_SUFFIX = \".caching\";\n\n\tprotected abstract String getBeanClassName(Element element);\n\n\tprotected abstract void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClassName(element));\n\t\tdoParse(element, parserContext, builder);\n\t\tRootBeanDefinition userService = (RootBeanDefinition) builder.getBeanDefinition();\n\t\tString beanId = resolveId(element, userService, parserContext);\n\t\tparserContext.registerBeanComponent(new BeanComponentDefinition(userService, beanId));\n\t\tString cacheRef = element.getAttribute(CACHE_REF);\n\t\t// Register a caching version of the user service if there's a cache-ref\n\t\tif (StringUtils.hasText(cacheRef)) {\n\t\t\tBeanDefinitionBuilder cachingUSBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(CachingUserDetailsService.class);\n\t\t\tcachingUSBuilder.addConstructorArgReference(beanId);\n\t\t\tcachingUSBuilder.addPropertyValue(\"userCache\", new RuntimeBeanReference(cacheRef));\n\t\t\tBeanDefinition cachingUserService = cachingUSBuilder.getBeanDefinition();\n\t\t\tparserContext\n\t\t\t\t.registerBeanComponent(new BeanComponentDefinition(cachingUserService, beanId + CACHING_SUFFIX));\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String resolveId(Element element, AbstractBeanDefinition definition, ParserContext pc)\n\t\t\tthrows BeanDefinitionStoreException {\n\t\tString id = element.getAttribute(\"id\");\n\t\tif (pc.isNested()) {\n\t\t\t// We're inside an <authentication-provider> element\n\t\t\tif (!StringUtils.hasText(id)) {\n\t\t\t\tid = pc.getReaderContext().generateBeanName(definition);\n\t\t\t}\n\t\t\tValueHolder userDetailsServiceValueHolder = new ValueHolder(new RuntimeBeanReference(id));\n\t\t\tuserDetailsServiceValueHolder.setName(\"userDetailsService\");\n\t\t\tBeanDefinition container = pc.getContainingBeanDefinition();\n\t\t\tcontainer.getConstructorArgumentValues().addGenericArgumentValue(userDetailsServiceValueHolder);\n\t\t}\n\t\tif (StringUtils.hasText(id)) {\n\t\t\treturn id;\n\t\t}\n\t\t// If top level, use the default name or throw an exception if already used\n\t\tif (pc.getRegistry().containsBeanDefinition(BeanIds.USER_DETAILS_SERVICE)) {\n\t\t\tthrow new BeanDefinitionStoreException(\n\t\t\t\t\t\"No id supplied and another bean is already registered as \" + BeanIds.USER_DETAILS_SERVICE);\n\t\t}\n\t\treturn BeanIds.USER_DETAILS_SERVICE;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport java.util.List;\n\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.parsing.CompositeComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.NamespaceHandlerResolver;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Registers the central ProviderManager used by the namespace configuration, and allows\n * the configuration of an alias, allowing users to reference it in their beans and\n * clearly see where the name is coming from.\n *\n * @author Luke Taylor\n */\npublic class AuthenticationManagerBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ATT_ALIAS = \"alias\";\n\n\tprivate static final String ATT_REF = \"ref\";\n\n\tprivate static final String ATT_ERASE_CREDENTIALS = \"erase-credentials\";\n\n\tprivate static final String AUTHENTICATION_EVENT_PUBLISHER_BEAN_NAME = \"defaultAuthenticationEventPublisher\";\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tString id = element.getAttribute(\"id\");\n\t\tif (!StringUtils.hasText(id)) {\n\t\t\tif (pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER)) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.warning(\"Overriding globally registered AuthenticationManager\", pc.extractSource(element));\n\t\t\t}\n\t\t\tid = BeanIds.AUTHENTICATION_MANAGER;\n\t\t}\n\t\tpc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)));\n\t\tBeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);\n\t\tString alias = element.getAttribute(ATT_ALIAS);\n\t\tList<BeanMetadataElement> providers = new ManagedList<>();\n\t\tNamespaceHandlerResolver resolver = pc.getReaderContext().getNamespaceHandlerResolver();\n\t\tNodeList children = element.getChildNodes();\n\t\tfor (int i = 0; i < children.getLength(); i++) {\n\t\t\tNode node = children.item(i);\n\t\t\tif (node instanceof Element) {\n\t\t\t\tproviders.add(extracted(element, pc, resolver, (Element) node));\n\t\t\t}\n\t\t}\n\t\tif (providers.isEmpty()) {\n\t\t\tproviders.add(new RootBeanDefinition(NullAuthenticationProvider.class));\n\t\t}\n\t\tproviderManagerBldr.addConstructorArgValue(providers);\n\t\tif (\"false\".equals(element.getAttribute(ATT_ERASE_CREDENTIALS))) {\n\t\t\tproviderManagerBldr.addPropertyValue(\"eraseCredentialsAfterAuthentication\", false);\n\t\t}\n\n\t\tif (!pc.getRegistry().containsBeanDefinition(AUTHENTICATION_EVENT_PUBLISHER_BEAN_NAME)) {\n\t\t\t// Add the default event publisher to the context\n\t\t\tBeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class);\n\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(publisher, AUTHENTICATION_EVENT_PUBLISHER_BEAN_NAME));\n\t\t}\n\n\t\tproviderManagerBldr.addPropertyReference(\"authenticationEventPublisher\",\n\t\t\t\tAUTHENTICATION_EVENT_PUBLISHER_BEAN_NAME);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), id));\n\t\tif (StringUtils.hasText(alias)) {\n\t\t\tpc.getRegistry().registerAlias(id, alias);\n\t\t\tpc.getReaderContext().fireAliasRegistered(id, alias, pc.extractSource(element));\n\t\t}\n\t\tif (!BeanIds.AUTHENTICATION_MANAGER.equals(id)\n\t\t\t\t&& !pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER)\n\t\t\t\t&& !pc.getRegistry().isAlias(BeanIds.AUTHENTICATION_MANAGER)) {\n\t\t\tpc.getRegistry().registerAlias(id, BeanIds.AUTHENTICATION_MANAGER);\n\t\t\tpc.getReaderContext().fireAliasRegistered(id, BeanIds.AUTHENTICATION_MANAGER, pc.extractSource(element));\n\t\t}\n\t\tpc.popAndRegisterContainingComponent();\n\t\treturn null;\n\t}\n\n\tprivate BeanMetadataElement extracted(Element element, ParserContext pc, NamespaceHandlerResolver resolver,\n\t\t\tElement providerElement) {\n\t\tString ref = providerElement.getAttribute(ATT_REF);\n\t\tif (!StringUtils.hasText(ref)) {\n\t\t\tBeanDefinition provider = resolver.resolve(providerElement.getNamespaceURI()).parse(providerElement, pc);\n\t\t\tAssert.notNull(provider,\n\t\t\t\t\t() -> \"Parser for \" + providerElement.getNodeName() + \" returned a null bean definition\");\n\t\t\tString providerId = pc.getReaderContext().generateBeanName(provider);\n\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(provider, providerId));\n\t\t\treturn new RuntimeBeanReference(providerId);\n\t\t}\n\t\tif (providerElement.getAttributes().getLength() > 1) {\n\t\t\tpc.getReaderContext()\n\t\t\t\t.error(\"authentication-provider element cannot be used with other attributes \"\n\t\t\t\t\t\t+ \"when using 'ref' attribute\", pc.extractSource(element));\n\t\t}\n\t\tNodeList providerChildren = providerElement.getChildNodes();\n\t\tfor (int i = 0; i < providerChildren.getLength(); i++) {\n\t\t\tif (providerChildren.item(i) instanceof Element) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(\"authentication-provider element cannot have child elements when used \"\n\t\t\t\t\t\t\t+ \"with 'ref' attribute\", pc.extractSource(element));\n\t\t\t}\n\t\t}\n\t\treturn new RuntimeBeanReference(ref);\n\t}\n\n\t/**\n\t * Provider which doesn't provide any service. Only used to prevent a configuration\n\t * exception if the provider list is empty (usually because a child ProviderManager\n\t * from the &lt;http&gt; namespace, such as OpenID, is expected to handle the\n\t * request).\n\t */\n\tpublic static final class NullAuthenticationProvider implements AuthenticationProvider {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport java.util.Arrays;\n\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.ObservationAuthenticationManager;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n/**\n * Factory bean for the namespace AuthenticationManager, which allows a more meaningful\n * error message to be reported in the <tt>NoSuchBeanDefinitionException</tt>, if the user\n * has forgotten to declare the &lt;authentication-manager&gt; element.\n *\n * @author Luke Taylor\n * @author Ngoc Nhan\n * @since 3.0\n */\npublic class AuthenticationManagerFactoryBean implements FactoryBean<AuthenticationManager>, BeanFactoryAware {\n\n\tprivate BeanFactory bf;\n\n\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\tpublic static final String MISSING_BEAN_ERROR_MESSAGE = \"Did you forget to add a global <authentication-manager> element \"\n\t\t\t+ \"to your configuration (with child <authentication-provider> elements)? Alternatively you can use the \"\n\t\t\t+ \"authentication-manager-ref attribute on your <http> and <global-method-security> elements.\";\n\n\t@Override\n\tpublic AuthenticationManager getObject() throws Exception {\n\t\ttry {\n\t\t\treturn (AuthenticationManager) this.bf.getBean(BeanIds.AUTHENTICATION_MANAGER);\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t\tif (!BeanIds.AUTHENTICATION_MANAGER.equals(ex.getBeanName())) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tUserDetailsService uds = this.bf.getBeanProvider(UserDetailsService.class).getIfUnique();\n\t\t\tif (uds == null) {\n\t\t\t\tthrow new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, MISSING_BEAN_ERROR_MESSAGE);\n\t\t\t}\n\t\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(uds);\n\t\t\tPasswordEncoder passwordEncoder = this.bf.getBeanProvider(PasswordEncoder.class).getIfUnique();\n\t\t\tif (passwordEncoder != null) {\n\t\t\t\tprovider.setPasswordEncoder(passwordEncoder);\n\t\t\t}\n\t\t\tprovider.afterPropertiesSet();\n\t\t\tProviderManager manager = new ProviderManager(Arrays.asList(provider));\n\t\t\tif (this.observationRegistry.isNoop()) {\n\t\t\t\treturn manager;\n\t\t\t}\n\t\t\treturn new ObservationAuthenticationManager(this.observationRegistry, manager);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Class<? extends AuthenticationManager> getObjectType() {\n\t\treturn ProviderManager.class;\n\t}\n\n\t@Override\n\tpublic boolean isSingleton() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\tthis.bf = beanFactory;\n\t}\n\n\tpublic void setObservationRegistry(ObservationRegistry observationRegistry) {\n\t\tthis.observationRegistry = observationRegistry;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/authentication/AuthenticationProviderBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.Elements;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Wraps a UserDetailsService bean with a DaoAuthenticationProvider and registers the\n * latter with the ProviderManager.\n *\n * @author Luke Taylor\n */\npublic class AuthenticationProviderBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ATT_USER_DETAILS_REF = \"user-service-ref\";\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tRootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class);\n\t\tauthProvider.setSource(pc.extractSource(element));\n\t\tElement passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER);\n\t\tPasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, pc);\n\t\tBeanMetadataElement passwordEncoder = pep.getPasswordEncoder();\n\t\tif (passwordEncoder != null) {\n\t\t\tauthProvider.getPropertyValues().addPropertyValue(\"passwordEncoder\", passwordEncoder);\n\t\t}\n\t\tElement userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);\n\t\tif (userServiceElt == null) {\n\t\t\tuserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);\n\t\t}\n\t\tif (userServiceElt == null) {\n\t\t\tuserServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE);\n\t\t}\n\t\tString ref = element.getAttribute(ATT_USER_DETAILS_REF);\n\t\tif (StringUtils.hasText(ref)) {\n\t\t\tif (userServiceElt != null) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(\"The \" + ATT_USER_DETAILS_REF + \" attribute cannot be used in combination with child\"\n\t\t\t\t\t\t\t+ \"elements '\" + Elements.USER_SERVICE + \"', '\" + Elements.JDBC_USER_SERVICE + \"' or '\"\n\t\t\t\t\t\t\t+ Elements.LDAP_USER_SERVICE + \"'\", element);\n\t\t\t}\n\t\t\tValueHolder userDetailsServiceValueHolder = new ValueHolder(new RuntimeBeanReference(ref));\n\t\t\tuserDetailsServiceValueHolder.setName(\"userDetailsService\");\n\t\t\tauthProvider.getConstructorArgumentValues().addGenericArgumentValue(userDetailsServiceValueHolder);\n\t\t}\n\t\telse {\n\t\t\t// Use the child elements to create the UserDetailsService\n\t\t\tif (userServiceElt != null) {\n\t\t\t\tpc.getDelegate().parseCustomElement(userServiceElt, authProvider);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpc.getReaderContext().error(\"A user-service is required\", element);\n\t\t\t}\n\t\t\t// Pinch the cache-ref from the UserDetailsService element, if set.\n\t\t\tString cacheRef = userServiceElt.getAttribute(AbstractUserDetailsServiceBeanDefinitionParser.CACHE_REF);\n\n\t\t\tif (StringUtils.hasText(cacheRef)) {\n\t\t\t\tauthProvider.getPropertyValues().addPropertyValue(\"userCache\", new RuntimeBeanReference(cacheRef));\n\t\t\t}\n\t\t}\n\t\treturn authProvider;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/authentication/JdbcUserServiceBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.Elements;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Luke Taylor\n */\npublic class JdbcUserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser {\n\n\tstatic final String ATT_DATA_SOURCE = \"data-source-ref\";\n\tstatic final String ATT_USERS_BY_USERNAME_QUERY = \"users-by-username-query\";\n\tstatic final String ATT_AUTHORITIES_BY_USERNAME_QUERY = \"authorities-by-username-query\";\n\tstatic final String ATT_GROUP_AUTHORITIES_QUERY = \"group-authorities-by-username-query\";\n\tstatic final String ATT_ROLE_PREFIX = \"role-prefix\";\n\n\t@Override\n\tprotected String getBeanClassName(Element element) {\n\t\treturn \"org.springframework.security.provisioning.JdbcUserDetailsManager\";\n\t}\n\n\t@Override\n\tprotected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {\n\t\tString dataSource = element.getAttribute(ATT_DATA_SOURCE);\n\t\tif (StringUtils.hasText(dataSource)) {\n\t\t\tbuilder.addPropertyReference(\"dataSource\", dataSource);\n\t\t}\n\t\telse {\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(ATT_DATA_SOURCE + \" is required for \" + Elements.JDBC_USER_SERVICE,\n\t\t\t\t\t\tparserContext.extractSource(element));\n\t\t}\n\t\tString usersQuery = element.getAttribute(ATT_USERS_BY_USERNAME_QUERY);\n\t\tString authoritiesQuery = element.getAttribute(ATT_AUTHORITIES_BY_USERNAME_QUERY);\n\t\tString groupAuthoritiesQuery = element.getAttribute(ATT_GROUP_AUTHORITIES_QUERY);\n\t\tString rolePrefix = element.getAttribute(ATT_ROLE_PREFIX);\n\t\tif (StringUtils.hasText(rolePrefix)) {\n\t\t\tbuilder.addPropertyValue(\"rolePrefix\", rolePrefix);\n\t\t}\n\t\tif (StringUtils.hasText(usersQuery)) {\n\t\t\tbuilder.addPropertyValue(\"usersByUsernameQuery\", usersQuery);\n\t\t}\n\t\tif (StringUtils.hasText(authoritiesQuery)) {\n\t\t\tbuilder.addPropertyValue(\"authoritiesByUsernameQuery\", authoritiesQuery);\n\t\t}\n\t\tif (StringUtils.hasText(groupAuthoritiesQuery)) {\n\t\t\tbuilder.addPropertyValue(\"enableGroups\", Boolean.TRUE);\n\t\t\tbuilder.addPropertyValue(\"groupAuthoritiesByUsernameQuery\", groupAuthoritiesQuery);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/authentication/PasswordEncoderParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.util.StringUtils;\n\n/**\n * Stateful parser for the &lt;password-encoder&gt; element.\n *\n * @author Luke Taylor\n */\npublic class PasswordEncoderParser {\n\n\tstatic final String ATT_REF = \"ref\";\n\n\tpublic static final String ATT_HASH = \"hash\";\n\n\tstatic final String OPT_HASH_BCRYPT = \"bcrypt\";\n\n\tprivate static final Map<String, Class<?>> ENCODER_CLASSES = Collections.singletonMap(OPT_HASH_BCRYPT,\n\t\t\tBCryptPasswordEncoder.class);\n\n\tprivate BeanMetadataElement passwordEncoder;\n\n\tpublic PasswordEncoderParser(Element element, ParserContext parserContext) {\n\t\tparse(element, parserContext);\n\t}\n\n\tprivate void parse(Element element, ParserContext parserContext) {\n\t\tif (element == null) {\n\t\t\tif (parserContext.getRegistry().containsBeanDefinition(\"passwordEncoder\")) {\n\t\t\t\tthis.passwordEncoder = parserContext.getRegistry().getBeanDefinition(\"passwordEncoder\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tString hash = element.getAttribute(ATT_HASH);\n\t\tString ref = element.getAttribute(ATT_REF);\n\t\tif (StringUtils.hasText(ref)) {\n\t\t\tthis.passwordEncoder = new RuntimeBeanReference(ref);\n\t\t}\n\t\telse {\n\t\t\tthis.passwordEncoder = createPasswordEncoderBeanDefinition(hash);\n\t\t\t((RootBeanDefinition) this.passwordEncoder).setSource(parserContext.extractSource(element));\n\t\t}\n\t}\n\n\tpublic static BeanDefinition createPasswordEncoderBeanDefinition(String hash) {\n\t\tClass<?> beanClass = ENCODER_CLASSES.get(hash);\n\t\tBeanDefinitionBuilder beanBldr = BeanDefinitionBuilder.rootBeanDefinition(beanClass);\n\t\treturn beanBldr.getBeanDefinition();\n\t}\n\n\tpublic BeanMetadataElement getPasswordEncoder() {\n\t\treturn this.passwordEncoder;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.List;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.BeanDefinitionStoreException;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.PropertiesFactoryBean;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * @author Luke Taylor\n * @author Ben Alex\n */\npublic class UserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser {\n\n\tstatic final String ATT_PASSWORD = \"password\";\n\tstatic final String ATT_NAME = \"name\";\n\tstatic final String ELT_USER = \"user\";\n\tstatic final String ATT_AUTHORITIES = \"authorities\";\n\tstatic final String ATT_PROPERTIES = \"properties\";\n\tstatic final String ATT_DISABLED = \"disabled\";\n\tstatic final String ATT_LOCKED = \"locked\";\n\n\tprivate SecureRandom random;\n\n\t@Override\n\tprotected String getBeanClassName(Element element) {\n\t\treturn InMemoryUserDetailsManager.class.getName();\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {\n\t\tString userProperties = element.getAttribute(ATT_PROPERTIES);\n\t\tList<Element> userElts = DomUtils.getChildElementsByTagName(element, ELT_USER);\n\t\tif (StringUtils.hasText(userProperties)) {\n\t\t\tif (!CollectionUtils.isEmpty(userElts)) {\n\t\t\t\tthrow new BeanDefinitionStoreException(\n\t\t\t\t\t\t\"Use of a properties file and user elements are mutually exclusive\");\n\t\t\t}\n\t\t\tBeanDefinition bd = new RootBeanDefinition(PropertiesFactoryBean.class);\n\t\t\tbd.getPropertyValues().addPropertyValue(\"location\", userProperties);\n\t\t\tbuilder.addConstructorArgValue(bd);\n\t\t\treturn;\n\t\t}\n\t\tif (CollectionUtils.isEmpty(userElts)) {\n\t\t\tthrow new BeanDefinitionStoreException(\"You must supply user definitions, either with <\" + ELT_USER\n\t\t\t\t\t+ \"> child elements or a \" + \"properties file (using the '\" + ATT_PROPERTIES + \"' attribute)\");\n\t\t}\n\t\tManagedList<BeanDefinition> users = new ManagedList<>();\n\t\tfor (Object elt : userElts) {\n\t\t\tElement userElt = (Element) elt;\n\t\t\tString userName = userElt.getAttribute(ATT_NAME);\n\t\t\tString password = userElt.getAttribute(ATT_PASSWORD);\n\t\t\tif (!StringUtils.hasLength(password)) {\n\t\t\t\tpassword = generateRandomPassword();\n\t\t\t}\n\t\t\tboolean locked = \"true\".equals(userElt.getAttribute(ATT_LOCKED));\n\t\t\tboolean disabled = \"true\".equals(userElt.getAttribute(ATT_DISABLED));\n\t\t\tBeanDefinitionBuilder authorities = BeanDefinitionBuilder.rootBeanDefinition(AuthorityUtils.class);\n\t\t\tauthorities.addConstructorArgValue(userElt.getAttribute(ATT_AUTHORITIES));\n\t\t\tauthorities.setFactoryMethod(\"commaSeparatedStringToAuthorityList\");\n\t\t\tBeanDefinitionBuilder user = BeanDefinitionBuilder.rootBeanDefinition(User.class);\n\t\t\tuser.addConstructorArgValue(userName);\n\t\t\tuser.addConstructorArgValue(password);\n\t\t\tuser.addConstructorArgValue(!disabled);\n\t\t\tuser.addConstructorArgValue(true);\n\t\t\tuser.addConstructorArgValue(true);\n\t\t\tuser.addConstructorArgValue(!locked);\n\t\t\tuser.addConstructorArgValue(authorities.getBeanDefinition());\n\t\t\tusers.add(user.getBeanDefinition());\n\t\t}\n\t\tbuilder.addConstructorArgValue(users);\n\t}\n\n\tprivate String generateRandomPassword() {\n\t\tif (this.random == null) {\n\t\t\ttry {\n\t\t\t\tthis.random = SecureRandom.getInstance(\"SHA1PRNG\");\n\t\t\t}\n\t\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\t\t// Shouldn't happen...\n\t\t\t\tthrow new RuntimeException(\"Failed find SHA1PRNG algorithm!\");\n\t\t\t}\n\t\t}\n\t\treturn Long.toString(this.random.nextLong());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Parsing of &lt;authentication-manager&gt; and related elements.\n */\npackage org.springframework.security.config.authentication;\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/core/GrantedAuthorityDefaults.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Allows providing defaults for {@link GrantedAuthority}\n *\n * @author Eddú Meléndez\n * @since 4.2.0\n */\npublic final class GrantedAuthorityDefaults {\n\n\tprivate final String rolePrefix;\n\n\tpublic GrantedAuthorityDefaults(String rolePrefix) {\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n\t/**\n\t * The default prefix used with role based authorization. Default is \"ROLE_\".\n\t * @return the default role prefix\n\t */\n\tpublic String getRolePrefix() {\n\t\treturn this.rolePrefix;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/core/userdetails/ReactiveUserDetailsServiceResourceFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core.userdetails;\n\nimport java.util.Collection;\n\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.context.ResourceLoaderAware;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.util.InMemoryResource;\n\n/**\n * Constructs an {@link MapReactiveUserDetailsService} from a resource using\n * {@link UserDetailsResourceFactoryBean}.\n *\n * @author Rob Winch\n * @since 5.0\n * @see UserDetailsResourceFactoryBean\n */\npublic class ReactiveUserDetailsServiceResourceFactoryBean\n\t\timplements ResourceLoaderAware, FactoryBean<MapReactiveUserDetailsService> {\n\n\tprivate UserDetailsResourceFactoryBean userDetails = new UserDetailsResourceFactoryBean();\n\n\t@Override\n\tpublic MapReactiveUserDetailsService getObject() throws Exception {\n\t\tCollection<UserDetails> users = this.userDetails.getObject();\n\t\treturn new MapReactiveUserDetailsService(users);\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn MapReactiveUserDetailsService.class;\n\t}\n\n\t@Override\n\tpublic void setResourceLoader(ResourceLoader resourceLoader) {\n\t\tthis.userDetails.setResourceLoader(resourceLoader);\n\t}\n\n\t/**\n\t * Sets the location of a Resource that is a Properties file in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}.\n\t * @param resourceLocation the location of the properties file that contains the users\n\t * (i.e. \"classpath:users.properties\")\n\t */\n\tpublic void setResourceLocation(String resourceLocation) {\n\t\tthis.userDetails.setResourceLocation(resourceLocation);\n\t}\n\n\t/**\n\t * Sets a Resource that is a Properties file in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}.\n\t * @param resource the Resource to use\n\t */\n\tpublic void setResource(Resource resource) {\n\t\tthis.userDetails.setResource(resource);\n\t}\n\n\t/**\n\t * Create a ReactiveUserDetailsServiceResourceFactoryBean with the location of a\n\t * Resource that is a Properties file in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}.\n\t * @param resourceLocation the location of the properties file that contains the users\n\t * (i.e. \"classpath:users.properties\")\n\t * @return the ReactiveUserDetailsServiceResourceFactoryBean\n\t */\n\tpublic static ReactiveUserDetailsServiceResourceFactoryBean fromResourceLocation(String resourceLocation) {\n\t\tReactiveUserDetailsServiceResourceFactoryBean result = new ReactiveUserDetailsServiceResourceFactoryBean();\n\t\tresult.setResourceLocation(resourceLocation);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Create a ReactiveUserDetailsServiceResourceFactoryBean with a Resource that is a\n\t * Properties file in the format defined in {@link UserDetailsResourceFactoryBean}.\n\t * @param propertiesResource the Resource that is a properties file that contains the\n\t * users\n\t * @return the ReactiveUserDetailsServiceResourceFactoryBean\n\t */\n\tpublic static ReactiveUserDetailsServiceResourceFactoryBean fromResource(Resource propertiesResource) {\n\t\tReactiveUserDetailsServiceResourceFactoryBean result = new ReactiveUserDetailsServiceResourceFactoryBean();\n\t\tresult.setResource(propertiesResource);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Create a ReactiveUserDetailsServiceResourceFactoryBean with a String that is in the\n\t * format defined in {@link UserDetailsResourceFactoryBean}.\n\t * @param users the users in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}\n\t * @return the ReactiveUserDetailsServiceResourceFactoryBean\n\t */\n\tpublic static ReactiveUserDetailsServiceResourceFactoryBean fromString(String users) {\n\t\tReactiveUserDetailsServiceResourceFactoryBean result = new ReactiveUserDetailsServiceResourceFactoryBean();\n\t\tresult.setResource(new InMemoryResource(users));\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/core/userdetails/UserDetailsMapFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core.userdetails;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.memory.UserAttribute;\nimport org.springframework.security.core.userdetails.memory.UserAttributeEditor;\nimport org.springframework.util.Assert;\n\n/**\n * Creates a {@code Collection<UserDetails>} from a @{code Map} in the format of\n * <p>\n * <code>\n * username=password[,enabled|disabled],roles...\n * </code>\n * <p>\n * The enabled and disabled properties are optional with enabled being the default. For\n * example:\n * <p>\n * <code>\n * user=password,ROLE_USER\n * admin=secret,ROLE_USER,ROLE_ADMIN\n * disabled_user=does_not_matter,disabled,ROLE_USER\n * </code>\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class UserDetailsMapFactoryBean implements FactoryBean<Collection<UserDetails>> {\n\n\tprivate final Map<String, String> userProperties;\n\n\tpublic UserDetailsMapFactoryBean(Map<String, String> userProperties) {\n\t\tAssert.notNull(userProperties, \"userProperties cannot be null\");\n\t\tthis.userProperties = userProperties;\n\t}\n\n\t@Override\n\tpublic Collection<UserDetails> getObject() {\n\t\tCollection<UserDetails> users = new ArrayList<>(this.userProperties.size());\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\tthis.userProperties.forEach((name, property) -> {\n\t\t\teditor.setAsText(property);\n\t\t\tUserAttribute attr = (UserAttribute) editor.getValue();\n\t\t\tAssert.state(attr != null, () -> \"The entry with username '\" + name + \"' and value '\" + property\n\t\t\t\t\t+ \"' could not be converted to a UserDetails.\");\n\t\t\tString password = attr.getPassword();\n\t\t\tboolean disabled = !attr.isEnabled();\n\t\t\tList<GrantedAuthority> authorities = attr.getAuthorities();\n\t\t\tusers.add(User.withUsername(name).password(password).disabled(disabled).authorities(authorities).build());\n\t\t});\n\t\treturn users;\n\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn Collection.class;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/core/userdetails/UserDetailsResourceFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core.userdetails;\n\nimport java.io.InputStream;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.context.ResourceLoaderAware;\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.util.InMemoryResource;\nimport org.springframework.util.Assert;\n\n/**\n * Parses a Resource that is a Properties file in the format of:\n *\n * <code>\n * username=password[,enabled|disabled],roles...\n * </code>\n *\n * The enabled and disabled properties are optional with enabled being the default. For\n * example:\n *\n * <code>\n * user=password,ROLE_USER\n * admin=secret,ROLE_USER,ROLE_ADMIN\n * disabled_user=does_not_matter,disabled,ROLE_USER\n * </code>\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class UserDetailsResourceFactoryBean implements ResourceLoaderAware, FactoryBean<Collection<UserDetails>> {\n\n\tprivate ResourceLoader resourceLoader = new DefaultResourceLoader();\n\n\tprivate String resourceLocation;\n\n\tprivate Resource resource;\n\n\t@Override\n\tpublic void setResourceLoader(ResourceLoader resourceLoader) {\n\t\tAssert.notNull(resourceLoader, \"resourceLoader cannot be null\");\n\t\tthis.resourceLoader = resourceLoader;\n\t}\n\n\t@Override\n\tpublic Collection<UserDetails> getObject() throws Exception {\n\t\tProperties userProperties = new Properties();\n\t\tResource resource = getPropertiesResource();\n\t\ttry (InputStream in = resource.getInputStream()) {\n\t\t\tuserProperties.load(in);\n\t\t}\n\t\treturn new UserDetailsMapFactoryBean((Map) userProperties).getObject();\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn Collection.class;\n\t}\n\n\t/**\n\t * Sets the location of a Resource that is a Properties file in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}.\n\t * @param resourceLocation the location of the properties file that contains the users\n\t * (i.e. \"classpath:users.properties\")\n\t */\n\tpublic void setResourceLocation(String resourceLocation) {\n\t\tthis.resourceLocation = resourceLocation;\n\t}\n\n\t/**\n\t * Sets a Resource that is a Properties file in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}.\n\t * @param resource the Resource to use\n\t */\n\tpublic void setResource(Resource resource) {\n\t\tthis.resource = resource;\n\t}\n\n\tprivate Resource getPropertiesResource() {\n\t\tResource result = this.resource;\n\t\tif (result == null && this.resourceLocation != null) {\n\t\t\tresult = this.resourceLoader.getResource(this.resourceLocation);\n\t\t}\n\t\tAssert.notNull(result, \"resource cannot be null if resourceLocation is null\");\n\t\treturn result;\n\t}\n\n\t/**\n\t * Create a UserDetailsResourceFactoryBean with the location of a Resource that is a\n\t * Properties file in the format defined in {@link UserDetailsResourceFactoryBean}.\n\t * @param resourceLocation the location of the properties file that contains the users\n\t * (i.e. \"classpath:users.properties\")\n\t * @return the UserDetailsResourceFactoryBean\n\t */\n\tpublic static UserDetailsResourceFactoryBean fromResourceLocation(String resourceLocation) {\n\t\tUserDetailsResourceFactoryBean result = new UserDetailsResourceFactoryBean();\n\t\tresult.setResourceLocation(resourceLocation);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Create a UserDetailsResourceFactoryBean with a Resource that is a Properties file\n\t * in the format defined in {@link UserDetailsResourceFactoryBean}.\n\t * @param propertiesResource the Resource that is a properties file that contains the\n\t * users\n\t * @return the UserDetailsResourceFactoryBean\n\t */\n\tpublic static UserDetailsResourceFactoryBean fromResource(Resource propertiesResource) {\n\t\tUserDetailsResourceFactoryBean result = new UserDetailsResourceFactoryBean();\n\t\tresult.setResource(propertiesResource);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Creates a UserDetailsResourceFactoryBean with a resource from the provided String\n\t * @param users the string representing the users\n\t * @return the UserDetailsResourceFactoryBean\n\t */\n\tpublic static UserDetailsResourceFactoryBean fromString(String users) {\n\t\tInMemoryResource resource = new InMemoryResource(users);\n\t\treturn fromResource(resource);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/crypto/RsaKeyConversionServicePostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.crypto;\n\nimport java.beans.PropertyEditor;\nimport java.beans.PropertyEditorSupport;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UncheckedIOException;\nimport java.nio.charset.StandardCharsets;\nimport java.security.Key;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanFactoryPostProcessor;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.core.convert.ConversionService;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.convert.converter.ConverterRegistry;\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.converter.RsaKeyConverters;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Adds {@link RsaKeyConverters} to the configured {@link ConversionService} or\n * {@link PropertyEditor}s\n *\n * @author Josh Cummings\n * @since 5.2\n */\npublic class RsaKeyConversionServicePostProcessor implements BeanFactoryPostProcessor {\n\n\tprivate static final String CONVERSION_SERVICE_BEAN_NAME = \"conversionService\";\n\n\tprivate ResourceKeyConverterAdapter<RSAPublicKey> x509 = new ResourceKeyConverterAdapter<>(RsaKeyConverters.x509());\n\n\tprivate ResourceKeyConverterAdapter<RSAPrivateKey> pkcs8 = new ResourceKeyConverterAdapter<>(\n\t\t\tRsaKeyConverters.pkcs8());\n\n\tpublic void setResourceLoader(ResourceLoader resourceLoader) {\n\t\tAssert.notNull(resourceLoader, \"resourceLoader cannot be null\");\n\t\tthis.x509.setResourceLoader(resourceLoader);\n\t\tthis.pkcs8.setResourceLoader(resourceLoader);\n\t}\n\n\t@Override\n\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t\tif (hasUserDefinedConversionService(beanFactory)) {\n\t\t\treturn;\n\t\t}\n\t\tConversionService service = beanFactory.getConversionService();\n\t\tif (service instanceof ConverterRegistry registry) {\n\t\t\tregistry.addConverter(String.class, RSAPrivateKey.class, this.pkcs8);\n\t\t\tregistry.addConverter(String.class, RSAPublicKey.class, this.x509);\n\t\t}\n\t\telse {\n\t\t\tbeanFactory.addPropertyEditorRegistrar((registry) -> {\n\t\t\t\tregistry.registerCustomEditor(RSAPublicKey.class, new ConverterPropertyEditorAdapter<>(this.x509));\n\t\t\t\tregistry.registerCustomEditor(RSAPrivateKey.class, new ConverterPropertyEditorAdapter<>(this.pkcs8));\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate boolean hasUserDefinedConversionService(ConfigurableListableBeanFactory beanFactory) {\n\t\treturn beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)\n\t\t\t\t&& beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);\n\t}\n\n\tprivate static class ConverterPropertyEditorAdapter<T> extends PropertyEditorSupport {\n\n\t\tprivate final Converter<String, T> converter;\n\n\t\tConverterPropertyEditorAdapter(Converter<String, T> converter) {\n\t\t\tthis.converter = converter;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getAsText() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAsText(String text) throws IllegalArgumentException {\n\t\t\tif (StringUtils.hasText(text)) {\n\t\t\t\tsetValue(this.converter.convert(text));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsetValue(null);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class ResourceKeyConverterAdapter<T extends Key> implements Converter<String, T> {\n\n\t\tprivate ResourceLoader resourceLoader = new DefaultResourceLoader();\n\n\t\tprivate final Converter<String, T> delegate;\n\n\t\t/**\n\t\t * Construct a {@link ResourceKeyConverterAdapter} with the provided parameters\n\t\t * @param delegate converts a stream of key material into a {@link Key}\n\t\t */\n\t\tResourceKeyConverterAdapter(Converter<InputStream, T> delegate) {\n\t\t\tthis.delegate = pemInputStreamConverter().andThen(autoclose(delegate));\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic T convert(String source) {\n\t\t\treturn this.delegate.convert(source);\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link ResourceLoader} to read the key material\n\t\t * @param resourceLoader the {@link ResourceLoader} to use\n\t\t */\n\t\tvoid setResourceLoader(ResourceLoader resourceLoader) {\n\t\t\tAssert.notNull(resourceLoader, \"resourceLoader cannot be null\");\n\t\t\tthis.resourceLoader = resourceLoader;\n\t\t}\n\n\t\tprivate Converter<String, InputStream> pemInputStreamConverter() {\n\t\t\treturn (source) -> source.startsWith(\"-----\") ? toInputStream(source)\n\t\t\t\t\t: toInputStream(this.resourceLoader.getResource(source));\n\t\t}\n\n\t\tprivate InputStream toInputStream(String raw) {\n\t\t\treturn new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8));\n\t\t}\n\n\t\tprivate InputStream toInputStream(Resource resource) {\n\t\t\ttry {\n\t\t\t\treturn resource.getInputStream();\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tthrow new UncheckedIOException(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate <T> Converter<InputStream, T> autoclose(Converter<InputStream, T> inputStreamKeyConverter) {\n\t\t\treturn (inputStream) -> {\n\t\t\t\ttry (InputStream is = inputStream) {\n\t\t\t\t\treturn inputStreamKeyConverter.convert(is);\n\t\t\t\t}\n\t\t\t\tcatch (IOException ex) {\n\t\t\t\t\tthrow new UncheckedIOException(ex);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/debug/SecurityDebugBeanFactoryPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.debug;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.web.debug.DebugFilter;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n */\npublic class SecurityDebugBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t@Override\n\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\tthis.logger.warn(\"\\n\\n\" + \"********************************************************************\\n\"\n\t\t\t\t+ \"**********        Security debugging is enabled.       *************\\n\"\n\t\t\t\t+ \"**********    This may include sensitive information.  *************\\n\"\n\t\t\t\t+ \"**********      Do not use in a production system!     *************\\n\"\n\t\t\t\t+ \"********************************************************************\\n\\n\");\n\t\t// SPRING_SECURITY_FILTER_CHAIN does not exist yet since it is an alias that has\n\t\t// not been processed, so use FILTER_CHAIN_PROXY\n\t\tif (registry.containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {\n\t\t\tBeanDefinition fcpBeanDef = registry.getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY);\n\t\t\tBeanDefinitionBuilder debugFilterBldr = BeanDefinitionBuilder.genericBeanDefinition(DebugFilter.class);\n\t\t\tdebugFilterBldr.addConstructorArgValue(fcpBeanDef);\n\t\t\t// Remove the alias to SPRING_SECURITY_FILTER_CHAIN, so that it does not\n\t\t\t// override the new\n\t\t\t// SPRING_SECURITY_FILTER_CHAIN definition\n\t\t\tregistry.removeAlias(BeanIds.SPRING_SECURITY_FILTER_CHAIN);\n\t\t\tregistry.registerBeanDefinition(BeanIds.SPRING_SECURITY_FILTER_CHAIN, debugFilterBldr.getBeanDefinition());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.security.SecureRandom;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.authentication.AnonymousAuthenticationProvider;\nimport org.springframework.security.authentication.RememberMeAuthenticationProvider;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever;\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.RequestMatcherDelegatingAccessDeniedHandler;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService;\nimport org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;\nimport org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Handles creation of authentication mechanism filters and related beans for &lt;http&gt;\n * parsing.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.0\n */\nfinal class AuthenticationConfigBuilder {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final boolean webMvcPresent;\n\n\tprivate static final String ATT_REALM = \"realm\";\n\n\tprivate static final String DEF_REALM = \"Realm\";\n\n\tstatic final String AUTHENTICATION_PROCESSING_FILTER_CLASS = \"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter\";\n\n\tstatic final String ATT_AUTH_DETAILS_SOURCE_REF = \"authentication-details-source-ref\";\n\n\tprivate static final String ATT_AUTO_CONFIG = \"auto-config\";\n\n\tprivate static final String ATT_ACCESS_DENIED_ERROR_PAGE = \"error-page\";\n\n\tprivate static final String ATT_ENTRY_POINT_REF = \"entry-point-ref\";\n\n\tprivate static final String ATT_USER_SERVICE_REF = \"user-service-ref\";\n\n\tprivate static final String ATT_KEY = \"key\";\n\n\tprivate static final String ATT_MAPPABLE_ROLES = \"mappable-roles\";\n\n\tprivate final Element httpElt;\n\n\tprivate final ParserContext pc;\n\n\tprivate final boolean autoConfig;\n\n\tprivate final boolean allowSessionCreation;\n\n\tprivate RootBeanDefinition anonymousFilter;\n\n\tprivate BeanReference anonymousProviderRef;\n\n\tprivate BeanDefinition rememberMeFilter;\n\n\tprivate String rememberMeServicesId;\n\n\tprivate BeanReference rememberMeProviderRef;\n\n\tprivate BeanDefinition basicFilter;\n\n\tprivate RuntimeBeanReference basicEntryPoint;\n\n\tprivate BeanDefinition formEntryPoint;\n\n\tprivate String formFilterId = null;\n\n\tprivate BeanDefinition x509Filter;\n\n\tprivate BeanReference x509ProviderRef;\n\n\tprivate BeanDefinition jeeFilter;\n\n\tprivate BeanReference jeeProviderRef;\n\n\tprivate RootBeanDefinition preAuthEntryPoint;\n\n\tprivate BeanMetadataElement mainEntryPoint;\n\n\tprivate BeanMetadataElement accessDeniedHandler;\n\n\tprivate BeanDefinition bearerTokenAuthenticationFilter;\n\n\tprivate BeanDefinition logoutFilter;\n\n\t@SuppressWarnings(\"rawtypes\")\n\tprivate ManagedList logoutHandlers;\n\n\tprivate BeanMetadataElement logoutSuccessHandler;\n\n\tprivate BeanDefinition loginPageGenerationFilter;\n\n\tprivate BeanDefinition logoutPageGenerationFilter;\n\n\tprivate BeanDefinition defaultResourcesFilter;\n\n\tprivate BeanDefinition etf;\n\n\tprivate final BeanReference requestCache;\n\n\tprivate final BeanReference portMapper;\n\n\tprivate final BeanMetadataElement csrfLogoutHandler;\n\n\tprivate String loginProcessingUrl;\n\n\tprivate String formLoginPage;\n\n\tprivate boolean oauth2LoginEnabled;\n\n\tprivate boolean defaultAuthorizedClientRepositoryRegistered;\n\n\tprivate String oauth2LoginFilterId;\n\n\tprivate BeanDefinition oauth2AuthorizationRequestRedirectFilter;\n\n\tprivate BeanDefinition oauth2LoginEntryPoint;\n\n\tprivate BeanReference oauth2LoginAuthenticationProviderRef;\n\n\tprivate BeanReference oauth2LoginOidcAuthenticationProviderRef;\n\n\tprivate BeanDefinition oauth2LoginLinks;\n\n\tprivate BeanDefinition saml2AuthenticationUrlToProviderName;\n\n\tprivate BeanDefinition saml2AuthorizationRequestFilter;\n\n\tprivate String saml2AuthenticationFilterId;\n\n\tprivate String saml2AuthenticationRequestFilterId;\n\n\tprivate String saml2LogoutFilterId;\n\n\tprivate String saml2LogoutRequestFilterId;\n\n\tprivate String saml2LogoutResponseFilterId;\n\n\tprivate boolean oauth2ClientEnabled;\n\n\tprivate BeanDefinition authorizationRequestRedirectFilter;\n\n\tprivate BeanDefinition authorizationCodeGrantFilter;\n\n\tprivate BeanReference authorizationCodeAuthenticationProviderRef;\n\n\tprivate final List<BeanReference> authenticationProviders = new ManagedList<>();\n\n\tprivate final Map<BeanDefinition, BeanMetadataElement> defaultDeniedHandlerMappings = new ManagedMap<>();\n\n\tprivate final Map<BeanDefinition, BeanMetadataElement> defaultEntryPointMappings = new ManagedMap<>();\n\n\tprivate final List<BeanDefinition> csrfIgnoreRequestMatchers = new ManagedList<>();\n\n\tstatic {\n\t\tClassLoader classLoader = AuthenticationConfigBuilder.class.getClassLoader();\n\t\twebMvcPresent = ClassUtils.isPresent(\"org.springframework.web.servlet.DispatcherServlet\", classLoader);\n\t}\n\n\tAuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc,\n\t\t\tSessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef,\n\t\t\tBeanReference authenticationFilterSecurityContextRepositoryRef, BeanReference sessionStrategy,\n\t\t\tBeanReference portMapper, BeanMetadataElement csrfLogoutHandler) {\n\t\tthis.httpElt = element;\n\t\tthis.pc = pc;\n\t\tthis.requestCache = requestCache;\n\t\tthis.autoConfig = forceAutoConfig | \"true\".equals(element.getAttribute(ATT_AUTO_CONFIG));\n\t\tthis.allowSessionCreation = sessionPolicy != SessionCreationPolicy.NEVER\n\t\t\t\t&& sessionPolicy != SessionCreationPolicy.STATELESS;\n\t\tthis.portMapper = portMapper;\n\t\tthis.csrfLogoutHandler = csrfLogoutHandler;\n\t\tcreateAnonymousFilter(authenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateRememberMeFilter(authenticationManager, authenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateBasicFilter(authenticationManager, authenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateBearerTokenAuthenticationFilter(authenticationManager,\n\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateFormLoginFilter(sessionStrategy, authenticationManager,\n\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef, authenticationFilterSecurityContextRepositoryRef);\n\t\tcreateOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager,\n\t\t\t\tauthenticationFilterSecurityContextRepositoryRef, authenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateSaml2LoginFilter(authenticationManager, authenticationFilterSecurityContextRepositoryRef);\n\t\tcreateX509Filter(authenticationManager, authenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateJeeFilter(authenticationManager, authenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateLogoutFilter(authenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateSaml2LogoutFilter(authenticationFilterSecurityContextHolderStrategyRef);\n\t\tcreateLoginPageFilterIfNeeded();\n\t\tcreateUserDetailsServiceFactory();\n\t\tcreateExceptionTranslationFilter(authenticationFilterSecurityContextHolderStrategyRef);\n\t}\n\n\tvoid createRememberMeFilter(BeanReference authenticationManager,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\t// Parse remember me before logout as RememberMeServices is also a LogoutHandler\n\t\t// implementation.\n\t\tElement rememberMeElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.REMEMBER_ME);\n\t\tif (rememberMeElt != null) {\n\t\t\tString key = rememberMeElt.getAttribute(ATT_KEY);\n\t\t\tif (!StringUtils.hasText(key)) {\n\t\t\t\tkey = createKey();\n\t\t\t}\n\t\t\tRememberMeBeanDefinitionParser rememberMeParser = new RememberMeBeanDefinitionParser(key,\n\t\t\t\t\tauthenticationManager, authenticationFilterSecurityContextHolderStrategyRef);\n\t\t\tthis.rememberMeFilter = rememberMeParser.parse(rememberMeElt, this.pc);\n\t\t\tthis.rememberMeServicesId = rememberMeParser.getRememberMeServicesId();\n\t\t\tcreateRememberMeProvider(key);\n\t\t}\n\t}\n\n\tprivate void createRememberMeProvider(String key) {\n\t\tRootBeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class);\n\t\tprovider.setSource(this.rememberMeFilter.getSource());\n\t\tprovider.getConstructorArgumentValues().addGenericArgumentValue(key);\n\t\tString id = this.pc.getReaderContext().generateBeanName(provider);\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(provider, id));\n\t\tthis.rememberMeProviderRef = new RuntimeBeanReference(id);\n\t}\n\n\tvoid createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef,\n\t\t\tBeanReference authenticationFilterSecurityContextRepositoryRef) {\n\t\tElement formLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.FORM_LOGIN);\n\t\tRootBeanDefinition formFilter = null;\n\t\tif (formLoginElt != null || this.autoConfig) {\n\t\t\tFormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser(\"/login\", \"POST\",\n\t\t\t\t\tAUTHENTICATION_PROCESSING_FILTER_CLASS, this.requestCache, sessionStrategy,\n\t\t\t\t\tthis.allowSessionCreation, this.portMapper);\n\t\t\tparser.parse(formLoginElt, this.pc);\n\t\t\tformFilter = parser.getFilterBean();\n\t\t\tthis.formEntryPoint = parser.getEntryPointBean();\n\t\t\tthis.loginProcessingUrl = parser.getLoginProcessingUrl();\n\t\t\tthis.formLoginPage = parser.getLoginPage();\n\t\t}\n\t\tif (formFilter != null) {\n\t\t\tformFilter.getPropertyValues().addPropertyValue(\"allowSessionCreation\", this.allowSessionCreation);\n\t\t\tformFilter.getPropertyValues().addPropertyValue(\"authenticationManager\", authManager);\n\t\t\tif (authenticationFilterSecurityContextRepositoryRef != null) {\n\t\t\t\tformFilter.getPropertyValues()\n\t\t\t\t\t.addPropertyValue(\"securityContextRepository\", authenticationFilterSecurityContextRepositoryRef);\n\t\t\t}\n\t\t\tformFilter.getPropertyValues()\n\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef);\n\t\t\t// Id is required by login page filter\n\t\t\tthis.formFilterId = this.pc.getReaderContext().generateBeanName(formFilter);\n\t\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(formFilter, this.formFilterId));\n\t\t\tinjectRememberMeServicesRef(formFilter, this.rememberMeServicesId);\n\t\t}\n\t}\n\n\tvoid createOAuth2ClientFilters(BeanReference sessionStrategy, BeanReference requestCache,\n\t\t\tBeanReference authenticationManager, BeanReference authenticationFilterSecurityContextRepositoryRef,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategy) {\n\t\tcreateOAuth2LoginFilter(sessionStrategy, authenticationManager,\n\t\t\t\tauthenticationFilterSecurityContextRepositoryRef, authenticationFilterSecurityContextHolderStrategy);\n\t\tcreateOAuth2ClientFilter(requestCache, authenticationManager, authenticationFilterSecurityContextRepositoryRef,\n\t\t\t\tauthenticationFilterSecurityContextHolderStrategy);\n\t\tregisterOAuth2ClientPostProcessors();\n\t}\n\n\tvoid createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager,\n\t\t\tBeanReference authenticationFilterSecurityContextRepositoryRef,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategy) {\n\t\tElement oauth2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_LOGIN);\n\t\tif (oauth2LoginElt == null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.oauth2LoginEnabled = true;\n\t\tOAuth2LoginBeanDefinitionParser parser = new OAuth2LoginBeanDefinitionParser(this.requestCache, this.portMapper,\n\t\t\t\tsessionStrategy, this.allowSessionCreation, authenticationFilterSecurityContextHolderStrategy);\n\t\tBeanDefinition oauth2LoginFilterBean = parser.parse(oauth2LoginElt, this.pc);\n\t\tBeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();\n\t\tregisterDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);\n\t\toauth2LoginFilterBean.getPropertyValues().addPropertyValue(\"authenticationManager\", authManager);\n\t\tif (authenticationFilterSecurityContextRepositoryRef != null) {\n\t\t\toauth2LoginFilterBean.getPropertyValues()\n\t\t\t\t.addPropertyValue(\"securityContextRepository\", authenticationFilterSecurityContextRepositoryRef);\n\t\t}\n\n\t\t// retrieve the other bean result\n\t\tBeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider();\n\t\tthis.oauth2AuthorizationRequestRedirectFilter = parser.getOAuth2AuthorizationRequestRedirectFilter();\n\t\tthis.oauth2LoginEntryPoint = parser.getOAuth2LoginAuthenticationEntryPoint();\n\n\t\t// generate bean name to be registered\n\t\tString oauth2LoginAuthProviderId = this.pc.getReaderContext().generateBeanName(oauth2LoginAuthProvider);\n\t\tthis.oauth2LoginFilterId = this.pc.getReaderContext().generateBeanName(oauth2LoginFilterBean);\n\t\tString oauth2AuthorizationRequestRedirectFilterId = this.pc.getReaderContext()\n\t\t\t.generateBeanName(this.oauth2AuthorizationRequestRedirectFilter);\n\t\tthis.oauth2LoginLinks = parser.getOAuth2LoginLinks();\n\n\t\t// register the component\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(oauth2LoginFilterBean, this.oauth2LoginFilterId));\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(this.oauth2AuthorizationRequestRedirectFilter,\n\t\t\t\toauth2AuthorizationRequestRedirectFilterId));\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(oauth2LoginAuthProvider, oauth2LoginAuthProviderId));\n\n\t\tthis.oauth2LoginAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginAuthProviderId);\n\n\t\t// oidc provider\n\t\tBeanDefinition oauth2LoginOidcAuthProvider = parser.getOAuth2LoginOidcAuthenticationProvider();\n\t\tString oauth2LoginOidcAuthProviderId = this.pc.getReaderContext().generateBeanName(oauth2LoginOidcAuthProvider);\n\t\tthis.pc.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(oauth2LoginOidcAuthProvider, oauth2LoginOidcAuthProviderId));\n\t\tthis.oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginOidcAuthProviderId);\n\t}\n\n\tvoid createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager,\n\t\t\tBeanReference authenticationFilterSecurityContextRepositoryRef,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategy) {\n\t\tElement oauth2ClientElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_CLIENT);\n\t\tif (oauth2ClientElt == null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.oauth2ClientEnabled = true;\n\t\tOAuth2ClientBeanDefinitionParser parser = new OAuth2ClientBeanDefinitionParser(requestCache,\n\t\t\t\tauthenticationManager, authenticationFilterSecurityContextRepositoryRef,\n\t\t\t\tauthenticationFilterSecurityContextHolderStrategy);\n\t\tparser.parse(oauth2ClientElt, this.pc);\n\t\tBeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();\n\t\tregisterDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);\n\t\tthis.authorizationRequestRedirectFilter = parser.getAuthorizationRequestRedirectFilter();\n\t\tString authorizationRequestRedirectFilterId = this.pc.getReaderContext()\n\t\t\t.generateBeanName(this.authorizationRequestRedirectFilter);\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(this.authorizationRequestRedirectFilter,\n\t\t\t\tauthorizationRequestRedirectFilterId));\n\t\tthis.authorizationCodeGrantFilter = parser.getAuthorizationCodeGrantFilter();\n\t\tString authorizationCodeGrantFilterId = this.pc.getReaderContext()\n\t\t\t.generateBeanName(this.authorizationCodeGrantFilter);\n\t\tthis.pc.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(this.authorizationCodeGrantFilter, authorizationCodeGrantFilterId));\n\t\tBeanDefinition authorizationCodeAuthenticationProvider = parser.getAuthorizationCodeAuthenticationProvider();\n\t\tString authorizationCodeAuthenticationProviderId = this.pc.getReaderContext()\n\t\t\t.generateBeanName(authorizationCodeAuthenticationProvider);\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(authorizationCodeAuthenticationProvider,\n\t\t\t\tauthorizationCodeAuthenticationProviderId));\n\t\tthis.authorizationCodeAuthenticationProviderRef = new RuntimeBeanReference(\n\t\t\t\tauthorizationCodeAuthenticationProviderId);\n\t}\n\n\tvoid registerDefaultAuthorizedClientRepositoryIfNecessary(BeanDefinition defaultAuthorizedClientRepository) {\n\t\tif (!this.defaultAuthorizedClientRepositoryRegistered && defaultAuthorizedClientRepository != null) {\n\t\t\tString authorizedClientRepositoryId = this.pc.getReaderContext()\n\t\t\t\t.generateBeanName(defaultAuthorizedClientRepository);\n\t\t\tthis.pc.registerBeanComponent(\n\t\t\t\t\tnew BeanComponentDefinition(defaultAuthorizedClientRepository, authorizedClientRepositoryId));\n\t\t\tthis.defaultAuthorizedClientRepositoryRegistered = true;\n\t\t}\n\t}\n\n\tprivate void registerOAuth2ClientPostProcessors() {\n\t\tif (!this.oauth2LoginEnabled && !this.oauth2ClientEnabled) {\n\t\t\treturn;\n\t\t}\n\t\tif (webMvcPresent) {\n\t\t\tthis.pc.getReaderContext()\n\t\t\t\t.registerWithGeneratedName(new RootBeanDefinition(OAuth2ClientWebMvcSecurityPostProcessor.class));\n\t\t}\n\t\tthis.pc.getReaderContext()\n\t\t\t.getRegistry()\n\t\t\t.registerBeanDefinition(OAuth2AuthorizedClientManagerRegistrar.BEAN_NAME,\n\t\t\t\t\tnew RootBeanDefinition(OAuth2AuthorizedClientManagerRegistrar.class));\n\t}\n\n\tprivate void createSaml2LoginFilter(BeanReference authenticationManager,\n\t\t\tBeanReference authenticationFilterSecurityContextRepositoryRef) {\n\t\tElement saml2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGIN);\n\t\tif (saml2LoginElt == null) {\n\t\t\treturn;\n\t\t}\n\t\tSaml2LoginBeanDefinitionParser parser = new Saml2LoginBeanDefinitionParser(this.csrfIgnoreRequestMatchers,\n\t\t\t\tthis.portMapper, this.requestCache, this.allowSessionCreation, authenticationManager,\n\t\t\t\tauthenticationFilterSecurityContextRepositoryRef, this.authenticationProviders,\n\t\t\t\tthis.defaultEntryPointMappings);\n\t\tBeanDefinition saml2WebSsoAuthenticationFilter = parser.parse(saml2LoginElt, this.pc);\n\t\tthis.saml2AuthorizationRequestFilter = parser.getSaml2WebSsoAuthenticationRequestFilter();\n\n\t\tthis.saml2AuthenticationFilterId = this.pc.getReaderContext().generateBeanName(saml2WebSsoAuthenticationFilter);\n\t\tthis.saml2AuthenticationRequestFilterId = this.pc.getReaderContext()\n\t\t\t.generateBeanName(this.saml2AuthorizationRequestFilter);\n\t\tthis.saml2AuthenticationUrlToProviderName = parser.getSaml2AuthenticationUrlToProviderName();\n\n\t\t// register the component\n\t\tthis.pc.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(saml2WebSsoAuthenticationFilter, this.saml2AuthenticationFilterId));\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(this.saml2AuthorizationRequestFilter,\n\t\t\t\tthis.saml2AuthenticationRequestFilterId));\n\t}\n\n\tprivate void injectRememberMeServicesRef(RootBeanDefinition bean, String rememberMeServicesId) {\n\t\tif (rememberMeServicesId != null) {\n\t\t\tbean.getPropertyValues()\n\t\t\t\t.addPropertyValue(\"rememberMeServices\", new RuntimeBeanReference(rememberMeServicesId));\n\t\t}\n\t}\n\n\tvoid createBasicFilter(BeanReference authManager,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tElement basicAuthElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.BASIC_AUTH);\n\t\tif (basicAuthElt == null && !this.autoConfig) {\n\t\t\t// No basic auth, do nothing\n\t\t\treturn;\n\t\t}\n\t\tString realm = this.httpElt.getAttribute(ATT_REALM);\n\t\tif (!StringUtils.hasText(realm)) {\n\t\t\trealm = DEF_REALM;\n\t\t}\n\t\tBeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicAuthenticationFilter.class);\n\t\tString entryPointId;\n\t\tif (basicAuthElt != null) {\n\t\t\tif (StringUtils.hasText(basicAuthElt.getAttribute(ATT_ENTRY_POINT_REF))) {\n\t\t\t\tthis.basicEntryPoint = new RuntimeBeanReference(basicAuthElt.getAttribute(ATT_ENTRY_POINT_REF));\n\t\t\t}\n\t\t\tinjectAuthenticationDetailsSource(basicAuthElt, filterBuilder);\n\t\t}\n\t\tif (this.basicEntryPoint == null) {\n\t\t\tRootBeanDefinition entryPoint = new RootBeanDefinition(BasicAuthenticationEntryPoint.class);\n\t\t\tentryPoint.setSource(this.pc.extractSource(this.httpElt));\n\t\t\tentryPoint.getPropertyValues().addPropertyValue(\"realmName\", realm);\n\t\t\tentryPointId = this.pc.getReaderContext().generateBeanName(entryPoint);\n\t\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(entryPoint, entryPointId));\n\t\t\tthis.basicEntryPoint = new RuntimeBeanReference(entryPointId);\n\t\t}\n\t\tfilterBuilder.addConstructorArgValue(authManager);\n\t\tfilterBuilder.addConstructorArgValue(this.basicEntryPoint);\n\t\tfilterBuilder.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef);\n\t\tthis.basicFilter = filterBuilder.getBeanDefinition();\n\t}\n\n\tvoid createBearerTokenAuthenticationFilter(BeanReference authManager,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tElement resourceServerElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_RESOURCE_SERVER);\n\t\tif (resourceServerElt == null) {\n\t\t\t// No resource server, do nothing\n\t\t\treturn;\n\t\t}\n\t\tOAuth2ResourceServerBeanDefinitionParser resourceServerBuilder = new OAuth2ResourceServerBeanDefinitionParser(\n\t\t\t\tauthManager, this.authenticationProviders, this.defaultEntryPointMappings,\n\t\t\t\tthis.defaultDeniedHandlerMappings, this.csrfIgnoreRequestMatchers,\n\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef);\n\t\tthis.bearerTokenAuthenticationFilter = resourceServerBuilder.parse(resourceServerElt, this.pc);\n\t}\n\n\tvoid createX509Filter(BeanReference authManager,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tElement x509Elt = DomUtils.getChildElementByTagName(this.httpElt, Elements.X509);\n\t\tRootBeanDefinition filter = null;\n\t\tif (x509Elt != null) {\n\t\t\tBeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(X509AuthenticationFilter.class);\n\t\t\tfilterBuilder.getRawBeanDefinition().setSource(this.pc.extractSource(x509Elt));\n\t\t\tfilterBuilder.addPropertyValue(\"authenticationManager\", authManager);\n\t\t\tfilterBuilder.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef);\n\t\t\tString principalExtractorRef = x509Elt.getAttribute(\"principal-extractor-ref\");\n\t\t\tString subjectPrincipalRegex = x509Elt.getAttribute(\"subject-principal-regex\");\n\t\t\tboolean hasPrincipalExtractorRef = StringUtils.hasText(principalExtractorRef);\n\t\t\tboolean hasSubjectPrincipalRegex = StringUtils.hasText(subjectPrincipalRegex);\n\t\t\tif (hasPrincipalExtractorRef && hasSubjectPrincipalRegex) {\n\t\t\t\tthis.pc.getReaderContext()\n\t\t\t\t\t.error(\"The attribute 'principal-extractor-ref' cannot be used together with the 'subject-principal-regex' attribute within <\"\n\t\t\t\t\t\t\t+ Elements.X509 + \">\", this.pc.extractSource(x509Elt));\n\t\t\t}\n\t\t\tif (hasPrincipalExtractorRef) {\n\t\t\t\tRuntimeBeanReference principalExtractor = new RuntimeBeanReference(principalExtractorRef);\n\t\t\t\tfilterBuilder.addPropertyValue(\"principalExtractor\", principalExtractor);\n\t\t\t}\n\t\t\tif (hasSubjectPrincipalRegex) {\n\t\t\t\tBeanDefinitionBuilder extractor = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(SubjectDnX509PrincipalExtractor.class);\n\t\t\t\textractor.addPropertyValue(\"subjectDnRegex\", subjectPrincipalRegex);\n\t\t\t\tfilterBuilder.addPropertyValue(\"principalExtractor\", extractor.getBeanDefinition());\n\t\t\t}\n\t\t\tinjectAuthenticationDetailsSource(x509Elt, filterBuilder);\n\t\t\tfilter = (RootBeanDefinition) filterBuilder.getBeanDefinition();\n\t\t\tcreatePreauthEntryPoint(x509Elt);\n\t\t\tcreateX509Provider();\n\t\t}\n\t\tthis.x509Filter = filter;\n\t}\n\n\tprivate void injectAuthenticationDetailsSource(Element elt, BeanDefinitionBuilder filterBuilder) {\n\t\tString authDetailsSourceRef = elt.getAttribute(AuthenticationConfigBuilder.ATT_AUTH_DETAILS_SOURCE_REF);\n\t\tif (StringUtils.hasText(authDetailsSourceRef)) {\n\t\t\tfilterBuilder.addPropertyReference(\"authenticationDetailsSource\", authDetailsSourceRef);\n\t\t}\n\t}\n\n\tprivate void createX509Provider() {\n\t\tElement x509Elt = DomUtils.getChildElementByTagName(this.httpElt, Elements.X509);\n\t\tBeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);\n\t\tRootBeanDefinition uds = new RootBeanDefinition();\n\t\tuds.setFactoryBeanName(BeanIds.USER_DETAILS_SERVICE_FACTORY);\n\t\tuds.setFactoryMethodName(\"authenticationUserDetailsService\");\n\t\tuds.getConstructorArgumentValues().addGenericArgumentValue(x509Elt.getAttribute(ATT_USER_SERVICE_REF));\n\t\tprovider.getPropertyValues().addPropertyValue(\"preAuthenticatedUserDetailsService\", uds);\n\t\tthis.x509ProviderRef = new RuntimeBeanReference(this.pc.getReaderContext().registerWithGeneratedName(provider));\n\t}\n\n\tprivate void createPreauthEntryPoint(Element source) {\n\t\tif (this.preAuthEntryPoint == null) {\n\t\t\tthis.preAuthEntryPoint = new RootBeanDefinition(Http403ForbiddenEntryPoint.class);\n\t\t\tthis.preAuthEntryPoint.setSource(this.pc.extractSource(source));\n\t\t}\n\t}\n\n\tvoid createJeeFilter(BeanReference authManager,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tElement jeeElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.JEE);\n\t\tRootBeanDefinition filter = null;\n\t\tif (jeeElt != null) {\n\t\t\tBeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(J2eePreAuthenticatedProcessingFilter.class);\n\t\t\tfilterBuilder.getRawBeanDefinition().setSource(this.pc.extractSource(jeeElt));\n\t\t\tfilterBuilder.addPropertyValue(\"authenticationManager\", authManager);\n\t\t\tfilterBuilder.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef);\n\t\t\tBeanDefinitionBuilder adsBldr = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class);\n\t\t\tadsBldr.addPropertyValue(\"userRoles2GrantedAuthoritiesMapper\",\n\t\t\t\t\tnew RootBeanDefinition(SimpleAttributes2GrantedAuthoritiesMapper.class));\n\t\t\tString roles = jeeElt.getAttribute(ATT_MAPPABLE_ROLES);\n\t\t\tAssert.hasLength(roles, \"roles is expected to have length\");\n\t\t\tBeanDefinitionBuilder rolesBuilder = BeanDefinitionBuilder.rootBeanDefinition(StringUtils.class);\n\t\t\trolesBuilder.addConstructorArgValue(roles);\n\t\t\trolesBuilder.setFactoryMethod(\"commaDelimitedListToSet\");\n\t\t\tRootBeanDefinition mappableRolesRetriever = new RootBeanDefinition(SimpleMappableAttributesRetriever.class);\n\t\t\tmappableRolesRetriever.getPropertyValues()\n\t\t\t\t.addPropertyValue(\"mappableAttributes\", rolesBuilder.getBeanDefinition());\n\t\t\tadsBldr.addPropertyValue(\"mappableRolesRetriever\", mappableRolesRetriever);\n\t\t\tfilterBuilder.addPropertyValue(\"authenticationDetailsSource\", adsBldr.getBeanDefinition());\n\t\t\tfilter = (RootBeanDefinition) filterBuilder.getBeanDefinition();\n\t\t\tcreatePreauthEntryPoint(jeeElt);\n\t\t\tcreateJeeProvider();\n\t\t}\n\t\tthis.jeeFilter = filter;\n\t}\n\n\tprivate void createJeeProvider() {\n\t\tElement jeeElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.JEE);\n\t\tBeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);\n\t\tRootBeanDefinition uds;\n\t\tif (StringUtils.hasText(jeeElt.getAttribute(ATT_USER_SERVICE_REF))) {\n\t\t\tuds = new RootBeanDefinition();\n\t\t\tuds.setFactoryBeanName(BeanIds.USER_DETAILS_SERVICE_FACTORY);\n\t\t\tuds.setFactoryMethodName(\"authenticationUserDetailsService\");\n\t\t\tuds.getConstructorArgumentValues().addGenericArgumentValue(jeeElt.getAttribute(ATT_USER_SERVICE_REF));\n\t\t}\n\t\telse {\n\t\t\tuds = new RootBeanDefinition(PreAuthenticatedGrantedAuthoritiesUserDetailsService.class);\n\t\t}\n\t\tprovider.getPropertyValues().addPropertyValue(\"preAuthenticatedUserDetailsService\", uds);\n\t\tthis.jeeProviderRef = new RuntimeBeanReference(this.pc.getReaderContext().registerWithGeneratedName(provider));\n\t}\n\n\tvoid createLoginPageFilterIfNeeded() {\n\t\tboolean needLoginPage = this.formFilterId != null || this.oauth2LoginFilterId != null;\n\t\t// If no login page has been defined, add in the default page generator.\n\t\tif (needLoginPage && this.formLoginPage == null) {\n\t\t\tthis.logger.info(\"No login page configured. The default internal one will be used. Use the '\"\n\t\t\t\t\t+ FormLoginBeanDefinitionParser.ATT_LOGIN_PAGE + \"' attribute to set the URL of the login page.\");\n\t\t\tBeanDefinitionBuilder loginPageFilter = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(DefaultLoginPageGeneratingFilter.class);\n\t\t\tloginPageFilter.addPropertyValue(\"resolveHiddenInputs\", new CsrfTokenHiddenInputFunction());\n\n\t\t\tBeanDefinitionBuilder logoutPageFilter = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(DefaultLogoutPageGeneratingFilter.class);\n\t\t\tlogoutPageFilter.addPropertyValue(\"resolveHiddenInputs\", new CsrfTokenHiddenInputFunction());\n\t\t\tif (this.formFilterId != null) {\n\t\t\t\tloginPageFilter.addConstructorArgReference(this.formFilterId);\n\t\t\t\tloginPageFilter.addPropertyValue(\"authenticationUrl\", this.loginProcessingUrl);\n\t\t\t}\n\t\t\tif (this.oauth2LoginFilterId != null) {\n\t\t\t\tloginPageFilter.addPropertyValue(\"Oauth2LoginEnabled\", true);\n\t\t\t\tloginPageFilter.addPropertyValue(\"Oauth2AuthenticationUrlToClientName\", this.oauth2LoginLinks);\n\t\t\t}\n\t\t\tif (this.saml2AuthenticationFilterId != null) {\n\t\t\t\tloginPageFilter.addPropertyValue(\"saml2LoginEnabled\", true);\n\t\t\t\tloginPageFilter.addPropertyValue(\"saml2AuthenticationUrlToProviderName\",\n\t\t\t\t\t\tthis.saml2AuthenticationUrlToProviderName);\n\t\t\t}\n\t\t\tthis.loginPageGenerationFilter = loginPageFilter.getBeanDefinition();\n\t\t\tthis.logoutPageGenerationFilter = logoutPageFilter.getBeanDefinition();\n\t\t\tthis.defaultResourcesFilter = BeanDefinitionBuilder.rootBeanDefinition(DefaultResourcesFilter.class)\n\t\t\t\t.setFactoryMethod(\"css\")\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\t}\n\n\tvoid createLogoutFilter(BeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tElement logoutElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.LOGOUT);\n\t\tif (logoutElt != null || this.autoConfig) {\n\t\t\tString formLoginPage = this.formLoginPage;\n\t\t\tif (formLoginPage == null) {\n\t\t\t\tformLoginPage = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;\n\t\t\t}\n\t\t\tLogoutBeanDefinitionParser logoutParser = new LogoutBeanDefinitionParser(formLoginPage,\n\t\t\t\t\tthis.rememberMeServicesId, this.csrfLogoutHandler,\n\t\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef);\n\t\t\tthis.logoutFilter = logoutParser.parse(logoutElt, this.pc);\n\t\t\tthis.logoutHandlers = logoutParser.getLogoutHandlers();\n\t\t\tthis.logoutSuccessHandler = logoutParser.getLogoutSuccessHandler();\n\t\t}\n\t}\n\n\tprivate void createSaml2LogoutFilter(BeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tElement saml2LogoutElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGOUT);\n\t\tif (saml2LogoutElt == null) {\n\t\t\treturn;\n\t\t}\n\t\tSaml2LogoutBeanDefinitionParser parser = new Saml2LogoutBeanDefinitionParser(this.logoutHandlers,\n\t\t\t\tthis.logoutSuccessHandler, authenticationFilterSecurityContextHolderStrategyRef);\n\t\tparser.parse(saml2LogoutElt, this.pc);\n\t\tBeanDefinition saml2LogoutFilter = parser.getLogoutFilter();\n\t\tBeanDefinition saml2LogoutRequestFilter = parser.getLogoutRequestFilter();\n\t\tBeanDefinition saml2LogoutResponseFilter = parser.getLogoutResponseFilter();\n\t\tthis.saml2LogoutFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutFilter);\n\t\tthis.saml2LogoutRequestFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutRequestFilter);\n\t\tthis.saml2LogoutResponseFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutResponseFilter);\n\n\t\t// register the component\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(saml2LogoutFilter, this.saml2LogoutFilterId));\n\t\tthis.pc.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(saml2LogoutRequestFilter, this.saml2LogoutRequestFilterId));\n\t\tthis.pc.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(saml2LogoutResponseFilter, this.saml2LogoutResponseFilterId));\n\t}\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tManagedList getLogoutHandlers() {\n\t\tif (this.logoutHandlers == null && this.rememberMeProviderRef != null) {\n\t\t\tthis.logoutHandlers = new ManagedList();\n\t\t\tif (this.csrfLogoutHandler != null) {\n\t\t\t\tthis.logoutHandlers.add(this.csrfLogoutHandler);\n\t\t\t}\n\t\t\tthis.logoutHandlers.add(new RuntimeBeanReference(this.rememberMeServicesId));\n\t\t\tthis.logoutHandlers.add(new RootBeanDefinition(SecurityContextLogoutHandler.class));\n\t\t}\n\n\t\treturn this.logoutHandlers;\n\t}\n\n\tBeanMetadataElement getEntryPointBean() {\n\t\treturn this.mainEntryPoint;\n\t}\n\n\tBeanMetadataElement getAccessDeniedHandlerBean() {\n\t\treturn this.accessDeniedHandler;\n\t}\n\n\tList<BeanDefinition> getCsrfIgnoreRequestMatchers() {\n\t\treturn this.csrfIgnoreRequestMatchers;\n\t}\n\n\tvoid createAnonymousFilter(BeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tElement anonymousElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.ANONYMOUS);\n\t\tif (anonymousElt != null && \"false\".equals(anonymousElt.getAttribute(\"enabled\"))) {\n\t\t\treturn;\n\t\t}\n\t\tString grantedAuthority = null;\n\t\tString username = null;\n\t\tString key = null;\n\t\tObject source = this.pc.extractSource(this.httpElt);\n\t\tif (anonymousElt != null) {\n\t\t\tgrantedAuthority = anonymousElt.getAttribute(\"granted-authority\");\n\t\t\tusername = anonymousElt.getAttribute(\"username\");\n\t\t\tkey = anonymousElt.getAttribute(ATT_KEY);\n\t\t\tsource = this.pc.extractSource(anonymousElt);\n\t\t}\n\t\tif (!StringUtils.hasText(grantedAuthority)) {\n\t\t\tgrantedAuthority = \"ROLE_ANONYMOUS\";\n\t\t}\n\t\tif (!StringUtils.hasText(username)) {\n\t\t\tusername = \"anonymousUser\";\n\t\t}\n\t\tif (!StringUtils.hasText(key)) {\n\t\t\t// Generate a random key for the Anonymous provider\n\t\t\tkey = createKey();\n\t\t}\n\t\tthis.anonymousFilter = new RootBeanDefinition(AnonymousAuthenticationFilter.class);\n\t\tthis.anonymousFilter.getConstructorArgumentValues().addIndexedArgumentValue(0, key);\n\t\tthis.anonymousFilter.getConstructorArgumentValues().addIndexedArgumentValue(1, username);\n\t\tthis.anonymousFilter.getConstructorArgumentValues()\n\t\t\t.addIndexedArgumentValue(2, AuthorityUtils.commaSeparatedStringToAuthorityList(grantedAuthority));\n\t\tthis.anonymousFilter.getPropertyValues()\n\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", authenticationFilterSecurityContextHolderStrategyRef);\n\t\tthis.anonymousFilter.setSource(source);\n\t\tRootBeanDefinition anonymousProviderBean = new RootBeanDefinition(AnonymousAuthenticationProvider.class);\n\t\tanonymousProviderBean.getConstructorArgumentValues().addIndexedArgumentValue(0, key);\n\t\tanonymousProviderBean.setSource(this.anonymousFilter.getSource());\n\t\tString id = this.pc.getReaderContext().generateBeanName(anonymousProviderBean);\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(anonymousProviderBean, id));\n\t\tthis.anonymousProviderRef = new RuntimeBeanReference(id);\n\t}\n\n\tprivate String createKey() {\n\t\tSecureRandom random = new SecureRandom();\n\t\treturn Long.toString(random.nextLong());\n\t}\n\n\tvoid createExceptionTranslationFilter(BeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tBeanDefinitionBuilder etfBuilder = BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);\n\t\tthis.accessDeniedHandler = createAccessDeniedHandler(this.httpElt, this.pc);\n\t\tetfBuilder.addPropertyValue(\"accessDeniedHandler\", this.accessDeniedHandler);\n\t\tAssert.state(this.requestCache != null, \"No request cache found\");\n\t\tthis.mainEntryPoint = selectEntryPoint();\n\t\tetfBuilder.addConstructorArgValue(this.mainEntryPoint);\n\t\tetfBuilder.addConstructorArgValue(this.requestCache);\n\t\tetfBuilder.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\tauthenticationFilterSecurityContextHolderStrategyRef);\n\t\tthis.etf = etfBuilder.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement createAccessDeniedHandler(Element element, ParserContext pc) {\n\t\tElement accessDeniedElt = DomUtils.getChildElementByTagName(element, Elements.ACCESS_DENIED_HANDLER);\n\t\tBeanDefinitionBuilder accessDeniedHandler = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(AccessDeniedHandlerImpl.class);\n\t\tif (accessDeniedElt != null) {\n\t\t\tString errorPage = accessDeniedElt.getAttribute(\"error-page\");\n\t\t\tString ref = accessDeniedElt.getAttribute(\"ref\");\n\t\t\tif (StringUtils.hasText(errorPage)) {\n\t\t\t\tif (StringUtils.hasText(ref)) {\n\t\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t\t.error(\"The attribute \" + ATT_ACCESS_DENIED_ERROR_PAGE\n\t\t\t\t\t\t\t\t+ \" cannot be used together with the 'ref' attribute within <\"\n\t\t\t\t\t\t\t\t+ Elements.ACCESS_DENIED_HANDLER + \">\", pc.extractSource(accessDeniedElt));\n\n\t\t\t\t}\n\t\t\t\taccessDeniedHandler.addPropertyValue(\"errorPage\", errorPage);\n\t\t\t\treturn accessDeniedHandler.getBeanDefinition();\n\t\t\t}\n\t\t\tif (StringUtils.hasText(ref)) {\n\t\t\t\treturn new RuntimeBeanReference(ref);\n\t\t\t}\n\t\t}\n\t\tif (this.defaultDeniedHandlerMappings.isEmpty()) {\n\t\t\treturn accessDeniedHandler.getBeanDefinition();\n\t\t}\n\t\tif (this.defaultDeniedHandlerMappings.size() == 1) {\n\t\t\treturn this.defaultDeniedHandlerMappings.values().iterator().next();\n\t\t}\n\t\taccessDeniedHandler = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestMatcherDelegatingAccessDeniedHandler.class);\n\t\taccessDeniedHandler.addConstructorArgValue(this.defaultDeniedHandlerMappings);\n\t\taccessDeniedHandler\n\t\t\t.addConstructorArgValue(BeanDefinitionBuilder.rootBeanDefinition(AccessDeniedHandlerImpl.class));\n\t\treturn accessDeniedHandler.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement selectEntryPoint() {\n\t\t// We need to establish the main entry point.\n\t\t// First check if a custom entry point bean is set\n\t\tString customEntryPoint = this.httpElt.getAttribute(ATT_ENTRY_POINT_REF);\n\t\tif (StringUtils.hasText(customEntryPoint)) {\n\t\t\treturn new RuntimeBeanReference(customEntryPoint);\n\t\t}\n\t\tif (!this.defaultEntryPointMappings.isEmpty()) {\n\t\t\tif (this.defaultEntryPointMappings.size() == 1) {\n\t\t\t\treturn this.defaultEntryPointMappings.values().iterator().next();\n\t\t\t}\n\t\t\tBeanDefinitionBuilder delegatingEntryPoint = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(DelegatingAuthenticationEntryPoint.class);\n\t\t\tdelegatingEntryPoint.addConstructorArgValue(this.defaultEntryPointMappings);\n\t\t\treturn delegatingEntryPoint.getBeanDefinition();\n\t\t}\n\t\tElement basicAuthElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.BASIC_AUTH);\n\t\tElement formLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.FORM_LOGIN);\n\t\t// Basic takes precedence if explicit element is used and no others are configured\n\t\tif (basicAuthElt != null && formLoginElt == null && this.oauth2LoginEntryPoint == null) {\n\t\t\treturn this.basicEntryPoint;\n\t\t}\n\t\tif (this.formFilterId != null) {\n\t\t\t// If form login was enabled through element and Oauth2 login was enabled from\n\t\t\t// element then use form login (gh-6802)\n\t\t\tif (formLoginElt != null && this.oauth2LoginEntryPoint != null) {\n\t\t\t\treturn this.formEntryPoint;\n\t\t\t}\n\t\t\t// If form login was enabled through auto-config, and Oauth2 login & Saml2\n\t\t\t// login was not\n\t\t\t// enabled then use form login\n\t\t\tif (this.oauth2LoginEntryPoint == null) {\n\t\t\t\treturn this.formEntryPoint;\n\t\t\t}\n\t\t}\n\t\t// If X.509 or JEE have been enabled, use the preauth entry point.\n\t\tif (this.preAuthEntryPoint != null) {\n\t\t\treturn this.preAuthEntryPoint;\n\t\t}\n\t\t// OAuth2 entry point will not be null if only 1 client registration\n\t\tif (this.oauth2LoginEntryPoint != null) {\n\t\t\treturn this.oauth2LoginEntryPoint;\n\t\t}\n\t\tthis.pc.getReaderContext()\n\t\t\t.error(\"No AuthenticationEntryPoint could be established. Please \"\n\t\t\t\t\t+ \"make sure you have a login mechanism configured through the namespace (such as form-login) or \"\n\t\t\t\t\t+ \"specify a custom AuthenticationEntryPoint with the '\" + ATT_ENTRY_POINT_REF + \"' attribute \",\n\t\t\t\t\tthis.pc.extractSource(this.httpElt));\n\t\treturn null;\n\t}\n\n\tprivate void createUserDetailsServiceFactory() {\n\t\tif (this.pc.getRegistry().containsBeanDefinition(BeanIds.USER_DETAILS_SERVICE_FACTORY)) {\n\t\t\t// Multiple <http> case\n\t\t\treturn;\n\t\t}\n\t\tRootBeanDefinition bean = new RootBeanDefinition(UserDetailsServiceFactoryBean.class);\n\t\tbean.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(bean, BeanIds.USER_DETAILS_SERVICE_FACTORY));\n\t}\n\n\tList<OrderDecorator> getFilters() {\n\t\tList<OrderDecorator> filters = new ArrayList<>();\n\t\tif (this.anonymousFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.anonymousFilter, SecurityFilters.ANONYMOUS_FILTER));\n\t\t}\n\t\tif (this.rememberMeFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.rememberMeFilter, SecurityFilters.REMEMBER_ME_FILTER));\n\t\t}\n\t\tif (this.logoutFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.logoutFilter, SecurityFilters.LOGOUT_FILTER));\n\t\t}\n\t\tif (this.x509Filter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.x509Filter, SecurityFilters.X509_FILTER));\n\t\t}\n\t\tif (this.jeeFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.jeeFilter, SecurityFilters.PRE_AUTH_FILTER));\n\t\t}\n\t\tif (this.formFilterId != null) {\n\t\t\tfilters.add(\n\t\t\t\t\tnew OrderDecorator(new RuntimeBeanReference(this.formFilterId), SecurityFilters.FORM_LOGIN_FILTER));\n\t\t}\n\t\tif (this.oauth2LoginFilterId != null) {\n\t\t\tfilters.add(new OrderDecorator(new RuntimeBeanReference(this.oauth2LoginFilterId),\n\t\t\t\t\tSecurityFilters.OAUTH2_LOGIN_FILTER));\n\t\t\tfilters.add(new OrderDecorator(this.oauth2AuthorizationRequestRedirectFilter,\n\t\t\t\t\tSecurityFilters.OAUTH2_AUTHORIZATION_REQUEST_FILTER));\n\t\t}\n\t\tif (this.loginPageGenerationFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.loginPageGenerationFilter, SecurityFilters.LOGIN_PAGE_FILTER));\n\t\t\tfilters.add(new OrderDecorator(this.logoutPageGenerationFilter, SecurityFilters.LOGOUT_PAGE_FILTER));\n\t\t}\n\t\tif (this.defaultResourcesFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.defaultResourcesFilter, SecurityFilters.DEFAULT_RESOURCES_FILTER));\n\t\t}\n\t\tif (this.basicFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.basicFilter, SecurityFilters.BASIC_AUTH_FILTER));\n\t\t}\n\t\tif (this.bearerTokenAuthenticationFilter != null) {\n\t\t\tfilters.add(\n\t\t\t\t\tnew OrderDecorator(this.bearerTokenAuthenticationFilter, SecurityFilters.BEARER_TOKEN_AUTH_FILTER));\n\t\t}\n\t\tif (this.authorizationCodeGrantFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.authorizationRequestRedirectFilter,\n\t\t\t\t\tSecurityFilters.OAUTH2_AUTHORIZATION_REQUEST_FILTER.getOrder() + 1));\n\t\t\tfilters.add(new OrderDecorator(this.authorizationCodeGrantFilter,\n\t\t\t\t\tSecurityFilters.OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER));\n\t\t}\n\t\tif (this.saml2AuthenticationFilterId != null) {\n\t\t\tfilters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2AuthenticationFilterId),\n\t\t\t\t\tSecurityFilters.SAML2_AUTHENTICATION_FILTER));\n\t\t\tfilters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2AuthenticationRequestFilterId),\n\t\t\t\t\tSecurityFilters.SAML2_AUTHENTICATION_REQUEST_FILTER));\n\t\t}\n\t\tif (this.saml2LogoutFilterId != null) {\n\t\t\tfilters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutFilterId),\n\t\t\t\t\tSecurityFilters.SAML2_LOGOUT_FILTER));\n\t\t\tfilters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutRequestFilterId),\n\t\t\t\t\tSecurityFilters.SAML2_LOGOUT_REQUEST_FILTER));\n\t\t\tfilters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutResponseFilterId),\n\t\t\t\t\tSecurityFilters.SAML2_LOGOUT_RESPONSE_FILTER));\n\t\t}\n\t\tfilters.add(new OrderDecorator(this.etf, SecurityFilters.EXCEPTION_TRANSLATION_FILTER));\n\t\treturn filters;\n\t}\n\n\tList<BeanReference> getProviders() {\n\t\tList<BeanReference> providers = new ArrayList<>();\n\t\tif (this.anonymousProviderRef != null) {\n\t\t\tproviders.add(this.anonymousProviderRef);\n\t\t}\n\t\tif (this.rememberMeProviderRef != null) {\n\t\t\tproviders.add(this.rememberMeProviderRef);\n\t\t}\n\t\tif (this.x509ProviderRef != null) {\n\t\t\tproviders.add(this.x509ProviderRef);\n\t\t}\n\t\tif (this.jeeProviderRef != null) {\n\t\t\tproviders.add(this.jeeProviderRef);\n\t\t}\n\t\tif (this.oauth2LoginAuthenticationProviderRef != null) {\n\t\t\tproviders.add(this.oauth2LoginAuthenticationProviderRef);\n\t\t}\n\t\tif (this.oauth2LoginOidcAuthenticationProviderRef != null) {\n\t\t\tproviders.add(this.oauth2LoginOidcAuthenticationProviderRef);\n\t\t}\n\t\tif (this.authorizationCodeAuthenticationProviderRef != null) {\n\t\t\tproviders.add(this.authorizationCodeAuthenticationProviderRef);\n\t\t}\n\t\tproviders.addAll(this.authenticationProviders);\n\t\treturn providers;\n\t}\n\n\tprivate static class CsrfTokenHiddenInputFunction implements Function<HttpServletRequest, Map<String, String>> {\n\n\t\t@Override\n\t\tpublic Map<String, String> apply(HttpServletRequest request) {\n\t\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\t\tif (token == null) {\n\t\t\t\treturn Collections.emptyMap();\n\t\t\t}\n\t\t\treturn Collections.singletonMap(token.getParameterName(), token.getToken());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/AuthorizationFilterParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.beans.factory.xml.XmlReaderContext;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.ObservationAuthorizationManager;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;\nimport org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\nclass AuthorizationFilterParser implements BeanDefinitionParser {\n\n\tprivate static final String ATT_USE_EXPRESSIONS = \"use-expressions\";\n\n\tprivate static final String ATT_ACCESS_DECISION_MANAGER_REF = \"access-decision-manager-ref\";\n\n\tprivate static final String ATT_OBSERVATION_REGISTRY_REF = \"observation-registry-ref\";\n\n\tprivate static final String ATT_HTTP_METHOD = \"method\";\n\n\tprivate static final String ATT_PATTERN = \"pattern\";\n\n\tprivate static final String ATT_ACCESS = \"access\";\n\n\tprivate static final String ATT_SERVLET_PATH = \"servlet-path\";\n\n\tprivate static final String ATT_FILTER_ALL_DISPATCHER_TYPES = \"filter-all-dispatcher-types\";\n\n\tprivate String authorizationManagerRef;\n\n\tprivate final BeanMetadataElement securityContextHolderStrategy;\n\n\tAuthorizationFilterParser(BeanMetadataElement securityContextHolderStrategy) {\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tif (!isUseExpressions(element)) {\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(\"AuthorizationManager must be used with `use-expressions=\\\"true\\\"; either add `use-authorization-manager=\\\"false\\\"` or `use-expressions=`\\\"false\\\"` in your `<http>` block\",\n\t\t\t\t\t\telement);\n\t\t\treturn null;\n\t\t}\n\t\tif (StringUtils.hasText(element.getAttribute(ATT_ACCESS_DECISION_MANAGER_REF))) {\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(\"AuthorizationManager cannot be used in conjunction with `access-decision-manager-ref`; either remove the reference to AccessDecisionManager or add `use-authorization-manager=\\\"false\\\"` to your `<http>` block\",\n\t\t\t\t\t\telement);\n\t\t\treturn null;\n\t\t}\n\t\tthis.authorizationManagerRef = createAuthorizationManager(element, parserContext);\n\t\tBeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(AuthorizationFilter.class);\n\t\tfilterBuilder.getRawBeanDefinition().setSource(parserContext.extractSource(element));\n\t\tfilterBuilder.addConstructorArgReference(this.authorizationManagerRef);\n\t\tif (\"false\".equals(element.getAttribute(ATT_FILTER_ALL_DISPATCHER_TYPES))) {\n\t\t\tfilterBuilder.addPropertyValue(\"shouldFilterAllDispatcherTypes\", Boolean.FALSE);\n\t\t}\n\t\tBeanDefinition filter = filterBuilder\n\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", this.securityContextHolderStrategy)\n\t\t\t.getBeanDefinition();\n\t\tString id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);\n\t\tif (StringUtils.hasText(id)) {\n\t\t\tparserContext.registerComponent(new BeanComponentDefinition(filter, id));\n\t\t\tparserContext.getRegistry().registerBeanDefinition(id, filter);\n\t\t}\n\t\treturn filter;\n\t}\n\n\tString getAuthorizationManagerRef() {\n\t\treturn this.authorizationManagerRef;\n\t}\n\n\tprivate String createAuthorizationManager(Element element, ParserContext parserContext) {\n\t\tXmlReaderContext context = parserContext.getReaderContext();\n\t\tString authorizationManagerRef = element.getAttribute(\"authorization-manager-ref\");\n\t\tif (StringUtils.hasText(authorizationManagerRef)) {\n\t\t\treturn authorizationManagerRef;\n\t\t}\n\t\tElement expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);\n\t\tString expressionHandlerRef = (expressionHandlerElt != null) ? expressionHandlerElt.getAttribute(\"ref\") : null;\n\t\tif (expressionHandlerRef == null) {\n\t\t\texpressionHandlerRef = registerDefaultExpressionHandler(parserContext);\n\t\t}\n\t\tMatcherType matcherType = MatcherType.fromElementOrMvc(element);\n\t\tManagedMap<BeanMetadataElement, BeanDefinition> matcherToExpression = new ManagedMap<>();\n\t\tList<Element> interceptMessages = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);\n\t\tfor (Element interceptMessage : interceptMessages) {\n\t\t\tString accessExpression = interceptMessage.getAttribute(ATT_ACCESS);\n\t\t\tif (!StringUtils.hasText(accessExpression)) {\n\t\t\t\tparserContext.getReaderContext().error(\"access attribute cannot be empty or null\", interceptMessage);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tBeanDefinitionBuilder authorizationManager = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(WebExpressionAuthorizationManager.class);\n\t\t\tauthorizationManager.addPropertyReference(\"expressionHandler\", expressionHandlerRef);\n\t\t\tauthorizationManager.addConstructorArgValue(accessExpression);\n\t\t\tBeanMetadataElement matcher = createMatcher(matcherType, interceptMessage, parserContext);\n\t\t\tmatcherToExpression.put(matcher, authorizationManager.getBeanDefinition());\n\t\t}\n\t\tBeanDefinitionBuilder mds = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestMatcherDelegatingAuthorizationManagerFactory.class)\n\t\t\t.addPropertyValue(\"requestMatcherMap\", matcherToExpression)\n\t\t\t.addPropertyValue(\"observationRegistry\", getObservationRegistry(element));\n\t\treturn context.registerWithGeneratedName(mds.getBeanDefinition());\n\t}\n\n\tprivate BeanMetadataElement createMatcher(MatcherType matcherType, Element urlElt, ParserContext parserContext) {\n\t\tString path = urlElt.getAttribute(ATT_PATTERN);\n\t\tString matcherRef = urlElt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REQUEST_MATCHER_REF);\n\t\tboolean hasMatcherRef = StringUtils.hasText(matcherRef);\n\t\tif (!hasMatcherRef && !StringUtils.hasText(path)) {\n\t\t\tparserContext.getReaderContext().error(\"path attribute cannot be empty or null\", urlElt);\n\t\t}\n\t\tString method = urlElt.getAttribute(ATT_HTTP_METHOD);\n\t\tif (!StringUtils.hasText(method)) {\n\t\t\tmethod = null;\n\t\t}\n\t\tString servletPath = urlElt.getAttribute(ATT_SERVLET_PATH);\n\t\tif (!StringUtils.hasText(servletPath)) {\n\t\t\tservletPath = null;\n\t\t}\n\t\telse if (!MatcherType.path.equals(matcherType)) {\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(ATT_SERVLET_PATH + \" is not applicable for request-matcher: '\" + matcherType.name() + \"'\",\n\t\t\t\t\t\turlElt);\n\t\t}\n\t\treturn hasMatcherRef ? new RuntimeBeanReference(matcherRef)\n\t\t\t\t: matcherType.createMatcher(parserContext, path, method, servletPath);\n\t}\n\n\tString registerDefaultExpressionHandler(ParserContext pc) {\n\t\tBeanDefinition expressionHandler = GrantedAuthorityDefaultsParserUtils.registerWithDefaultRolePrefix(pc,\n\t\t\t\tDefaultWebSecurityExpressionHandlerBeanFactory.class);\n\t\tString expressionHandlerRef = pc.getReaderContext().generateBeanName(expressionHandler);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));\n\t\treturn expressionHandlerRef;\n\t}\n\n\tboolean isUseExpressions(Element elt) {\n\t\tString useExpressions = elt.getAttribute(ATT_USE_EXPRESSIONS);\n\t\treturn !StringUtils.hasText(useExpressions) || \"true\".equals(useExpressions);\n\t}\n\n\tprivate BeanMetadataElement getObservationRegistry(Element methodSecurityElmt) {\n\t\tString holderStrategyRef = methodSecurityElmt.getAttribute(ATT_OBSERVATION_REGISTRY_REF);\n\t\tif (StringUtils.hasText(holderStrategyRef)) {\n\t\t\treturn new RuntimeBeanReference(holderStrategyRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(ObservationRegistryFactory.class).getBeanDefinition();\n\t}\n\n\tpublic static final class RequestMatcherDelegatingAuthorizationManagerFactory\n\t\t\timplements FactoryBean<AuthorizationManager<HttpServletRequest>> {\n\n\t\tprivate Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> beans;\n\n\t\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\t\t@Override\n\t\tpublic AuthorizationManager<HttpServletRequest> getObject() throws Exception {\n\t\t\tRequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager\n\t\t\t\t.builder();\n\t\t\tfor (Map.Entry<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> entry : this.beans\n\t\t\t\t.entrySet()) {\n\t\t\t\tbuilder.add(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t\tAuthorizationManager<HttpServletRequest> manager = builder.build();\n\t\t\tif (!this.observationRegistry.isNoop()) {\n\t\t\t\treturn new ObservationAuthorizationManager<>(this.observationRegistry, manager);\n\t\t\t}\n\t\t\treturn manager;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn AuthorizationManager.class;\n\t\t}\n\n\t\tpublic void setRequestMatcherMap(Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> beans) {\n\t\t\tthis.beans = beans;\n\t\t}\n\n\t\tpublic void setObservationRegistry(ObservationRegistry observationRegistry) {\n\t\t\tthis.observationRegistry = observationRegistry;\n\t\t}\n\n\t}\n\n\tstatic class DefaultWebSecurityExpressionHandlerBeanFactory\n\t\t\textends GrantedAuthorityDefaultsParserUtils.AbstractGrantedAuthorityDefaultsBeanFactory {\n\n\t\tprivate DefaultHttpSecurityExpressionHandler handler = new DefaultHttpSecurityExpressionHandler();\n\n\t\t@Override\n\t\tpublic DefaultHttpSecurityExpressionHandler getBean() {\n\t\t\tif (this.rolePrefix != null) {\n\t\t\t\tthis.handler.setDefaultRolePrefix(this.rolePrefix);\n\t\t\t}\n\t\t\treturn this.handler;\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryFactory implements FactoryBean<ObservationRegistry> {\n\n\t\t@Override\n\t\tpublic ObservationRegistry getObject() throws Exception {\n\t\t\treturn ObservationRegistry.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn ObservationRegistry.class;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/ChannelAttributeFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.List;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;\n\n/**\n * Used as a factory bean to create config attribute values for the\n * <tt>requires-channel</tt> attribute.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated In modern Spring Security APIs, each API manages its own configuration\n * context. As such there is no direct replacement for this interface. In the case of\n * method security, please see {@link SecurityAnnotationScanner} and\n * {@link AuthorizationManager}. In the case of channel security, please see\n * {@code HttpsRedirectFilter}. In the case of web security, please see\n * {@link AuthorizationManager}.\n */\n@Deprecated\npublic final class ChannelAttributeFactory {\n\n\tprivate static final String OPT_REQUIRES_HTTP = \"http\";\n\n\tprivate static final String OPT_REQUIRES_HTTPS = \"https\";\n\n\tprivate static final String OPT_ANY_CHANNEL = \"any\";\n\n\tprivate ChannelAttributeFactory() {\n\t}\n\n\tpublic static List<ConfigAttribute> createChannelAttributes(String requiredChannel) {\n\t\tString channelConfigAttribute = switch (requiredChannel) {\n\t\t\tcase OPT_REQUIRES_HTTPS -> \"REQUIRES_SECURE_CHANNEL\";\n\t\t\tcase OPT_REQUIRES_HTTP -> \"REQUIRES_INSECURE_CHANNEL\";\n\t\t\tcase OPT_ANY_CHANNEL -> ChannelDecisionManagerImpl.ANY_CHANNEL;\n\t\t\tdefault -> throw new BeanCreationException(\"Unknown channel attribute \" + requiredChannel);\n\t\t};\n\t\treturn SecurityConfig.createList(channelConfigAttribute);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/CorsBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.CorsFilter;\n\n/**\n * Parser for the {@code CorsFilter}.\n *\n * @author Rob Winch\n * @since 4.1.1\n */\npublic class CorsBeanDefinitionParser {\n\n\tprivate static final String ATT_SOURCE = \"configuration-source-ref\";\n\n\tprivate static final String ATT_REF = \"ref\";\n\n\tpublic BeanMetadataElement parse(Element element, ParserContext parserContext) {\n\t\tif (element == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString filterRef = element.getAttribute(ATT_REF);\n\t\tif (StringUtils.hasText(filterRef)) {\n\t\t\treturn new RuntimeBeanReference(filterRef);\n\t\t}\n\t\tBeanMetadataElement configurationSource = getSource(element, parserContext);\n\t\tif (configurationSource == null) {\n\t\t\tthrow new BeanCreationException(\"Could not create CorsFilter\");\n\t\t}\n\t\tBeanDefinitionBuilder filterBldr = BeanDefinitionBuilder.rootBeanDefinition(CorsFilter.class);\n\t\tfilterBldr.addConstructorArgValue(configurationSource);\n\t\treturn filterBldr.getBeanDefinition();\n\t}\n\n\tpublic BeanMetadataElement getSource(Element element, ParserContext parserContext) {\n\t\tString configurationSourceRef = element.getAttribute(ATT_SOURCE);\n\t\tif (StringUtils.hasText(configurationSourceRef)) {\n\t\t\treturn new RuntimeBeanReference(configurationSourceRef);\n\t\t}\n\t\treturn new RootBeanDefinition(CorsConfigurationSourceFactoryBean.class);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/CorsConfigurationSourceFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.lang.Nullable;\nimport org.springframework.web.cors.CorsConfigurationSource;\n\n/**\n * Used for creating an instance of {@link CorsConfigurationSource} and autowiring the\n * {@link ApplicationContext}.\n *\n * @author Rob Winch\n * @since 4.1.1\n */\nclass CorsConfigurationSourceFactoryBean implements FactoryBean<CorsConfigurationSource>, ApplicationContextAware {\n\n\tprivate static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = \"mvcHandlerMappingIntrospector\";\n\n\tprivate ApplicationContext context;\n\n\t@Override\n\tpublic CorsConfigurationSource getObject() {\n\t\tif (!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {\n\t\t\tthrow new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,\n\t\t\t\t\t\"A Bean named \" + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + \" of type \"\n\t\t\t\t\t\t\t+ CorsConfigurationSource.class.getName()\n\t\t\t\t\t\t\t+ \" is required to use <cors>. Please ensure Spring Security & Spring \"\n\t\t\t\t\t\t\t+ \"MVC are configured in a shared ApplicationContext.\");\n\t\t}\n\t\treturn this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn CorsConfigurationSource.class;\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.context = applicationContext;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/CsrfBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.config.annotation.web.HttpSecurityBuilder;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.access.CompositeAccessDeniedHandler;\nimport org.springframework.security.web.access.DelegatingAccessDeniedHandler;\nimport org.springframework.security.web.access.ObservationMarkingAccessDeniedHandler;\nimport org.springframework.security.web.csrf.CsrfAuthenticationStrategy;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.csrf.CsrfLogoutHandler;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\nimport org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;\nimport org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;\nimport org.springframework.security.web.session.InvalidSessionStrategy;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Parser for the {@code CsrfFilter}.\n *\n * @author Rob Winch\n * @author Ankur Pathak\n * @since 3.2\n */\npublic class CsrfBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String REQUEST_DATA_VALUE_PROCESSOR = \"requestDataValueProcessor\";\n\n\tprivate static final String ATT_MATCHER = \"request-matcher-ref\";\n\n\tprivate static final String ATT_REPOSITORY = \"token-repository-ref\";\n\n\tprivate static final String ATT_REQUEST_HANDLER = \"request-handler-ref\";\n\n\tprivate static final boolean webMvcPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = CsrfBeanDefinitionParser.class.getClassLoader();\n\t\twebMvcPresent = ClassUtils.isPresent(\"org.springframework.web.servlet.DispatcherServlet\", classLoader);\n\t}\n\n\tprivate String csrfRepositoryRef;\n\n\tprivate BeanDefinition csrfFilter;\n\n\tprivate String requestMatcherRef;\n\n\tprivate String requestHandlerRef;\n\n\tprivate BeanMetadataElement observationRegistry;\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tboolean disabled = element != null && \"true\".equals(element.getAttribute(\"disabled\"));\n\t\tif (disabled) {\n\t\t\treturn null;\n\t\t}\n\t\tif (webMvcPresent) {\n\t\t\tif (!pc.getRegistry().containsBeanDefinition(REQUEST_DATA_VALUE_PROCESSOR)) {\n\t\t\t\tRootBeanDefinition beanDefinition = new RootBeanDefinition(CsrfRequestDataValueProcessor.class);\n\t\t\t\tBeanComponentDefinition componentDefinition = new BeanComponentDefinition(beanDefinition,\n\t\t\t\t\t\tREQUEST_DATA_VALUE_PROCESSOR);\n\t\t\t\tpc.registerBeanComponent(componentDefinition);\n\t\t\t}\n\t\t}\n\t\tif (element != null) {\n\t\t\tthis.csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);\n\t\t\tthis.requestMatcherRef = element.getAttribute(ATT_MATCHER);\n\t\t\tthis.requestHandlerRef = element.getAttribute(ATT_REQUEST_HANDLER);\n\t\t}\n\t\tif (!StringUtils.hasText(this.csrfRepositoryRef)) {\n\t\t\tBeanDefinitionBuilder httpSessionCsrfTokenRepository = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(HttpSessionCsrfTokenRepository.class);\n\t\t\tthis.csrfRepositoryRef = pc.getReaderContext()\n\t\t\t\t.generateBeanName(httpSessionCsrfTokenRepository.getBeanDefinition());\n\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(httpSessionCsrfTokenRepository.getBeanDefinition(),\n\t\t\t\t\tthis.csrfRepositoryRef));\n\t\t}\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(CsrfFilter.class);\n\t\tbuilder.addConstructorArgReference(this.csrfRepositoryRef);\n\t\tif (StringUtils.hasText(this.requestMatcherRef)) {\n\t\t\tbuilder.addPropertyReference(\"requireCsrfProtectionMatcher\", this.requestMatcherRef);\n\t\t}\n\t\tif (StringUtils.hasText(this.requestHandlerRef)) {\n\t\t\tbuilder.addPropertyReference(\"requestHandler\", this.requestHandlerRef);\n\t\t}\n\t\tthis.csrfFilter = builder.getBeanDefinition();\n\t\treturn this.csrfFilter;\n\t}\n\n\t/**\n\t * Populate the AccessDeniedHandler on the {@link CsrfFilter}\n\t * @param invalidSessionStrategy the {@link InvalidSessionStrategy} to use\n\t * @param defaultDeniedHandler the {@link AccessDeniedHandler} to use\n\t */\n\tvoid initAccessDeniedHandler(BeanDefinition invalidSessionStrategy, BeanMetadataElement defaultDeniedHandler) {\n\t\tBeanMetadataElement accessDeniedHandler = createAccessDeniedHandler(invalidSessionStrategy,\n\t\t\t\tdefaultDeniedHandler);\n\t\tthis.csrfFilter.getPropertyValues().addPropertyValue(\"accessDeniedHandler\", accessDeniedHandler);\n\t}\n\n\t/**\n\t * Creates the {@link AccessDeniedHandler} from the result of\n\t * {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} and\n\t * {@link #getInvalidSessionStrategy(HttpSecurityBuilder)}. If\n\t * {@link #getInvalidSessionStrategy(HttpSecurityBuilder)} is non-null, then a\n\t * {@link DelegatingAccessDeniedHandler} is used in combination with\n\t * {@link InvalidSessionAccessDeniedHandler} and the\n\t * {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)}. Otherwise, only\n\t * {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} is used.\n\t * @param invalidSessionStrategy the {@link InvalidSessionStrategy} to use\n\t * @param defaultDeniedHandler the {@link AccessDeniedHandler} to use\n\t * @return the {@link BeanMetadataElement} that is the {@link AccessDeniedHandler} to\n\t * populate on the {@link CsrfFilter}\n\t */\n\tprivate BeanMetadataElement createAccessDeniedHandler(BeanDefinition invalidSessionStrategy,\n\t\t\tBeanMetadataElement defaultDeniedHandler) {\n\t\tif (invalidSessionStrategy == null) {\n\t\t\treturn defaultDeniedHandler;\n\t\t}\n\t\tManagedMap<Class<? extends AccessDeniedException>, BeanDefinition> handlers = new ManagedMap<>();\n\t\tBeanDefinitionBuilder invalidSessionHandlerBldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(InvalidSessionAccessDeniedHandler.class);\n\t\tinvalidSessionHandlerBldr.addConstructorArgValue(invalidSessionStrategy);\n\t\thandlers.put(MissingCsrfTokenException.class, invalidSessionHandlerBldr.getBeanDefinition());\n\t\tBeanDefinitionBuilder deniedBldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(DelegatingAccessDeniedHandler.class);\n\t\tdeniedBldr.addConstructorArgValue(handlers);\n\t\tdeniedBldr.addConstructorArgValue(defaultDeniedHandler);\n\t\tBeanDefinition denied = deniedBldr.getBeanDefinition();\n\t\tManagedList compositeList = new ManagedList();\n\t\tBeanDefinitionBuilder compositeBldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(CompositeAccessDeniedHandler.class);\n\t\tBeanDefinition observing = BeanDefinitionBuilder.rootBeanDefinition(ObservationMarkingAccessDeniedHandler.class)\n\t\t\t.addConstructorArgValue(this.observationRegistry)\n\t\t\t.getBeanDefinition();\n\t\tcompositeList.add(denied);\n\t\tcompositeList.add(observing);\n\t\tcompositeBldr.addConstructorArgValue(compositeList);\n\t\treturn compositeBldr.getBeanDefinition();\n\t}\n\n\tBeanDefinition getCsrfAuthenticationStrategy() {\n\t\tBeanDefinitionBuilder csrfAuthenticationStrategy = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(CsrfAuthenticationStrategy.class);\n\t\tcsrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);\n\t\tif (StringUtils.hasText(this.requestHandlerRef)) {\n\t\t\tcsrfAuthenticationStrategy.addPropertyReference(\"requestHandler\", this.requestHandlerRef);\n\t\t}\n\t\treturn csrfAuthenticationStrategy.getBeanDefinition();\n\t}\n\n\tBeanDefinition getCsrfLogoutHandler() {\n\t\tBeanDefinitionBuilder csrfAuthenticationStrategy = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(CsrfLogoutHandler.class);\n\t\tcsrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);\n\t\treturn csrfAuthenticationStrategy.getBeanDefinition();\n\t}\n\n\tvoid setIgnoreCsrfRequestMatchers(List<BeanDefinition> requestMatchers) {\n\t\tif (!requestMatchers.isEmpty()) {\n\t\t\tBeanMetadataElement requestMatcher = (!StringUtils.hasText(this.requestMatcherRef))\n\t\t\t\t\t? new RootBeanDefinition(DefaultRequiresCsrfMatcher.class)\n\t\t\t\t\t: new RuntimeBeanReference(this.requestMatcherRef);\n\t\t\tBeanDefinitionBuilder and = BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class);\n\t\t\tBeanDefinitionBuilder negated = BeanDefinitionBuilder.rootBeanDefinition(NegatedRequestMatcher.class);\n\t\t\tBeanDefinitionBuilder or = BeanDefinitionBuilder.rootBeanDefinition(OrRequestMatcher.class);\n\t\t\tor.addConstructorArgValue(requestMatchers);\n\t\t\tnegated.addConstructorArgValue(or.getBeanDefinition());\n\t\t\tList<BeanMetadataElement> ands = new ManagedList<>();\n\t\t\tands.add(requestMatcher);\n\t\t\tands.add(negated.getBeanDefinition());\n\t\t\tand.addConstructorArgValue(ands);\n\t\t\tthis.csrfFilter.getPropertyValues().add(\"requireCsrfProtectionMatcher\", and.getBeanDefinition());\n\t\t}\n\t}\n\n\tvoid setObservationRegistry(BeanMetadataElement observationRegistry) {\n\t\tthis.observationRegistry = observationRegistry;\n\t}\n\n\tprivate static final class DefaultRequiresCsrfMatcher implements RequestMatcher {\n\n\t\tprivate final HashSet<String> allowedMethods = new HashSet<>(Arrays.asList(\"GET\", \"HEAD\", \"TRACE\", \"OPTIONS\"));\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn !this.allowedMethods.contains(request.getMethod());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.UnreachableFilterChainException;\nimport org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.PathPatternRequestTransformer;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\nimport org.springframework.security.web.session.SessionManagementFilter;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.util.ClassUtils;\n\npublic class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator {\n\n\tprivate static final boolean USING_ACCESS = ClassUtils\n\t\t.isPresent(\"org.springframework.security.access.SecurityConfig\", null);\n\n\tprivate static final Authentication TEST = new TestingAuthenticationToken(\"\", \"\", Collections.emptyList());\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = new PathPatternRequestTransformer();\n\n\t@Override\n\tpublic void validate(FilterChainProxy fcp) {\n\t\tfor (SecurityFilterChain filterChain : fcp.getFilterChains()) {\n\t\t\tcheckLoginPageIsntProtected(fcp, filterChain.getFilters());\n\t\t\tcheckFilterStack(filterChain.getFilters());\n\t\t}\n\t\tcheckPathOrder(new ArrayList<>(fcp.getFilterChains()));\n\t\tcheckForDuplicateMatchers(new ArrayList<>(fcp.getFilterChains()));\n\t\tcheckAuthorizationFilters(new ArrayList<>(fcp.getFilterChains()));\n\t}\n\n\tprivate void checkPathOrder(List<SecurityFilterChain> filterChains) {\n\t\t// Check that the universal pattern is listed at the end, if at all\n\t\tIterator<SecurityFilterChain> chains = filterChains.iterator();\n\t\twhile (chains.hasNext()) {\n\t\t\tif (chains.next() instanceof DefaultSecurityFilterChain securityFilterChain) {\n\t\t\t\tif (AnyRequestMatcher.INSTANCE.equals(securityFilterChain.getRequestMatcher()) && chains.hasNext()) {\n\t\t\t\t\tthrow new UnreachableFilterChainException(\"A universal match pattern ('/**') is defined \"\n\t\t\t\t\t\t\t+ \" before other patterns in the filter chain, causing them to be ignored. Please check the \"\n\t\t\t\t\t\t\t+ \"ordering in your <security:http> namespace or FilterChainProxy bean configuration\",\n\t\t\t\t\t\t\tsecurityFilterChain, chains.next());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {\n\t\tDefaultSecurityFilterChain filterChain = null;\n\t\tfor (SecurityFilterChain chain : chains) {\n\t\t\tif (filterChain != null) {\n\t\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\t\tif (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {\n\t\t\t\t\t\tthrow new UnreachableFilterChainException(\n\t\t\t\t\t\t\t\t\"The FilterChainProxy contains two filter chains using the\" + \" matcher \"\n\t\t\t\t\t\t\t\t\t\t+ defaultChain.getRequestMatcher()\n\t\t\t\t\t\t\t\t\t\t+ \". If you are using multiple <http> namespace \"\n\t\t\t\t\t\t\t\t\t\t+ \"elements, you must use a 'pattern' attribute to define the request patterns to which they apply.\",\n\t\t\t\t\t\t\t\tdefaultChain, chain);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\tfilterChain = defaultChain;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkAuthorizationFilters(List<SecurityFilterChain> chains) {\n\t\tFilter authorizationFilter = null;\n\t\tFilter filterSecurityInterceptor = null;\n\t\tfor (SecurityFilterChain chain : chains) {\n\t\t\tfor (Filter filter : chain.getFilters()) {\n\t\t\t\tif (filter instanceof AuthorizationFilter) {\n\t\t\t\t\tauthorizationFilter = filter;\n\t\t\t\t}\n\t\t\t\tif (USING_ACCESS && AccessComponents.isFilterSecurityInterceptor(filter)) {\n\t\t\t\t\tfilterSecurityInterceptor = filter;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (authorizationFilter != null && filterSecurityInterceptor != null) {\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\t\"It is not recommended to use authorizeRequests or FilterSecurityInterceptor in the configuration. Please only use authorizeHttpRequests\");\n\t\t\t}\n\t\t\tif (filterSecurityInterceptor != null) {\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\t\"Usage of authorizeRequests and FilterSecurityInterceptor are deprecated. Please use authorizeHttpRequests in the configuration\");\n\t\t\t}\n\t\t\tauthorizationFilter = null;\n\t\t\tfilterSecurityInterceptor = null;\n\t\t}\n\t}\n\n\t@SuppressWarnings({ \"unchecked\" })\n\tprivate static <F extends Filter> F getFilter(Class<F> type, List<Filter> filters) {\n\t\tfor (Filter f : filters) {\n\t\t\tif (type.isAssignableFrom(f.getClass())) {\n\t\t\t\treturn (F) f;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Checks the filter list for possible errors and logs them\n\t */\n\tprivate void checkFilterStack(List<Filter> filters) {\n\t\tcheckForDuplicates(SecurityContextPersistenceFilter.class, filters);\n\t\tcheckForDuplicates(UsernamePasswordAuthenticationFilter.class, filters);\n\t\tcheckForDuplicates(SessionManagementFilter.class, filters);\n\t\tcheckForDuplicates(BasicAuthenticationFilter.class, filters);\n\t\tcheckForDuplicates(SecurityContextHolderAwareRequestFilter.class, filters);\n\t\tcheckForDuplicates(JaasApiIntegrationFilter.class, filters);\n\t\tcheckForDuplicates(ExceptionTranslationFilter.class, filters);\n\t\tif (USING_ACCESS) {\n\t\t\tcheckForDuplicates(AccessComponents.getFilterSecurityInterceptorClass(), filters);\n\t\t}\n\t\tcheckForDuplicates(AuthorizationFilter.class, filters);\n\t}\n\n\tprivate void checkForDuplicates(Class<? extends Filter> clazz, List<Filter> filters) {\n\t\tfor (int i = 0; i < filters.size(); i++) {\n\t\t\tFilter f1 = filters.get(i);\n\t\t\tif (clazz.isAssignableFrom(f1.getClass())) {\n\t\t\t\t// Found the first one, check remaining for another\n\t\t\t\tfor (int j = i + 1; j < filters.size(); j++) {\n\t\t\t\t\tFilter f2 = filters.get(j);\n\t\t\t\t\tif (clazz.isAssignableFrom(f2.getClass())) {\n\t\t\t\t\t\tthis.logger.warn(\"Possible error: Filters at position \" + i + \" and \" + j + \" are both \"\n\t\t\t\t\t\t\t\t+ \"instances of \" + clazz.getName());\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Checks for the common error of having a login page URL protected by the security\n\t * interceptor\n\t */\n\tprivate void checkLoginPageIsntProtected(FilterChainProxy fcp, List<Filter> filterStack) {\n\t\tExceptionTranslationFilter exceptions = getFilter(ExceptionTranslationFilter.class, filterStack);\n\t\tif (exceptions == null\n\t\t\t\t|| !(exceptions.getAuthenticationEntryPoint() instanceof LoginUrlAuthenticationEntryPoint)) {\n\t\t\treturn;\n\t\t}\n\t\tString loginPage = ((LoginUrlAuthenticationEntryPoint) exceptions.getAuthenticationEntryPoint())\n\t\t\t.getLoginFormUrl();\n\t\tthis.logger.info(\"Checking whether login URL '\" + loginPage + \"' is accessible with your configuration\");\n\t\tFilterInvocation invocation = new FilterInvocation(loginPage, \"POST\");\n\t\tHttpServletRequest loginRequest = this.requestTransformer.transform(invocation.getRequest());\n\t\tList<Filter> filters = null;\n\t\ttry {\n\t\t\tfilters = fcp.getFilters(loginPage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// May happen legitimately if a filter-chain request matcher requires more\n\t\t\t// request data than that provided\n\t\t\t// by the dummy request used when creating the filter invocation.\n\t\t\tthis.logger.info(\"Failed to obtain filter chain information for the login page. Unable to complete check.\");\n\t\t}\n\t\tif (filters == null || filters.isEmpty()) {\n\t\t\tthis.logger.debug(\"Filter chain is empty for the login page\");\n\t\t\treturn;\n\t\t}\n\t\tif (getFilter(DefaultLoginPageGeneratingFilter.class, filters) != null) {\n\t\t\tthis.logger.debug(\"Default generated login page is in use\");\n\t\t\treturn;\n\t\t}\n\t\tif (checkLoginPageIsPublic(filters, loginRequest)) {\n\t\t\treturn;\n\t\t}\n\t\tAnonymousAuthenticationFilter anonymous = getFilter(AnonymousAuthenticationFilter.class, filters);\n\t\tif (anonymous == null) {\n\t\t\tthis.logger.warn(\"The login page is being protected by the filter chain, but you don't appear to have\"\n\t\t\t\t\t+ \" anonymous authentication enabled. This is almost certainly an error.\");\n\t\t\treturn;\n\t\t}\n\t\t// Simulate an anonymous access with the supplied attributes.\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(\"key\", anonymous.getPrincipal(),\n\t\t\t\tanonymous.getAuthorities());\n\t\tSupplier<Boolean> check = deriveAnonymousCheck(filters, loginRequest, token);\n\t\ttry {\n\t\t\tboolean allowed = check.get();\n\t\t\tif (!allowed) {\n\t\t\t\tthis.logger.warn(\"Anonymous access to the login page doesn't appear to be enabled. \"\n\t\t\t\t\t\t+ \"This is almost certainly an error. Please check your configuration allows unauthenticated \"\n\t\t\t\t\t\t+ \"access to the configured login page. (Simulated access was rejected)\");\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// May happen legitimately if a filter-chain request matcher requires more\n\t\t\t// request data than that provided\n\t\t\t// by the dummy request used when creating the filter invocation. See SEC-1878\n\t\t\tthis.logger.info(\"Unable to check access to the login page to determine if anonymous access is allowed. \"\n\t\t\t\t\t+ \"This might be an error, but can happen under normal circumstances.\", ex);\n\t\t}\n\t}\n\n\tprivate boolean checkLoginPageIsPublic(List<Filter> filters, HttpServletRequest loginRequest) {\n\t\tif (USING_ACCESS) {\n\t\t\tBoolean isPublic = AccessComponents.checkLoginPageIsPublic(filters, loginRequest);\n\t\t\tif (isPublic != null) {\n\t\t\t\treturn isPublic;\n\t\t\t}\n\t\t}\n\t\tAuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);\n\t\tif (authorizationFilter != null) {\n\t\t\tAuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter\n\t\t\t\t.getAuthorizationManager();\n\t\t\ttry {\n\t\t\t\tAuthorizationResult result = authorizationManager.authorize(() -> TEST, loginRequest);\n\t\t\t\treturn result != null && result.isGranted();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate Supplier<Boolean> deriveAnonymousCheck(List<Filter> filters, HttpServletRequest loginRequest,\n\t\t\tAnonymousAuthenticationToken token) {\n\t\tif (USING_ACCESS) {\n\t\t\tSupplier<Boolean> check = AccessComponents.getAnonymousCheck(filters, loginRequest, token);\n\t\t\tif (check != null) {\n\t\t\t\treturn check;\n\t\t\t}\n\t\t}\n\t\tAuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);\n\t\tif (authorizationFilter != null) {\n\t\t\treturn () -> {\n\t\t\t\tAuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter\n\t\t\t\t\t.getAuthorizationManager();\n\t\t\t\tAuthorizationResult result = authorizationManager.authorize(() -> token, loginRequest);\n\t\t\t\treturn result != null && result.isGranted();\n\t\t\t};\n\t\t}\n\t\treturn () -> true;\n\t}\n\n\tprivate static final class AccessComponents {\n\n\t\tprivate static final Log logger = LogFactory.getLog(DefaultFilterChainValidator.class);\n\n\t\tprivate static boolean isFilterSecurityInterceptor(Filter filter) {\n\t\t\treturn filter instanceof FilterSecurityInterceptor;\n\t\t}\n\n\t\tprivate static Class<FilterSecurityInterceptor> getFilterSecurityInterceptorClass() {\n\t\t\treturn FilterSecurityInterceptor.class;\n\t\t}\n\n\t\tprivate static Boolean checkLoginPageIsPublic(List<Filter> filters, HttpServletRequest loginRequest) {\n\t\t\tFilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);\n\t\t\tif (authorizationInterceptor == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tFilterInvocationSecurityMetadataSource fids = authorizationInterceptor.getSecurityMetadataSource();\n\t\t\tCollection<ConfigAttribute> attributes = fids.getAttributes(loginRequest);\n\t\t\tif (attributes == null) {\n\t\t\t\tlogger.debug(\"No access attributes defined for login page URL\");\n\t\t\t\tif (authorizationInterceptor.isRejectPublicInvocations()) {\n\t\t\t\t\tlogger.warn(\"FilterSecurityInterceptor is configured to reject public invocations.\"\n\t\t\t\t\t\t\t+ \" Your login page may not be accessible.\");\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate static Supplier<Boolean> getAnonymousCheck(List<Filter> filters, HttpServletRequest loginRequest,\n\t\t\t\tAnonymousAuthenticationToken token) {\n\t\t\tFilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);\n\t\t\tif (authorizationInterceptor == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn () -> {\n\t\t\t\tFilterInvocationSecurityMetadataSource source = authorizationInterceptor.getSecurityMetadataSource();\n\t\t\t\tCollection<ConfigAttribute> attributes = source.getAttributes(loginRequest);\n\t\t\t\ttry {\n\t\t\t\t\tauthorizationInterceptor.getAccessDecisionManager().decide(token, loginRequest, attributes);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tcatch (AccessDeniedException ex) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/FilterChainBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Collections;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Luke Taylor\n */\npublic class FilterChainBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ATT_REQUEST_MATCHER_REF = \"request-matcher-ref\";\n\n\t@Override\n\tpublic BeanDefinition parse(Element elt, ParserContext pc) {\n\t\tMatcherType matcherType = MatcherType.fromElementOrMvc(elt);\n\t\tString path = elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_PATH_PATTERN);\n\t\tString requestMatcher = elt.getAttribute(ATT_REQUEST_MATCHER_REF);\n\t\tString filters = elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_FILTERS);\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DefaultSecurityFilterChain.class);\n\t\tif (StringUtils.hasText(path)) {\n\t\t\tAssert.isTrue(!StringUtils.hasText(requestMatcher), \"\");\n\t\t\tbuilder.addConstructorArgValue(matcherType.createMatcher(pc, path, null));\n\t\t}\n\t\telse {\n\t\t\tAssert.isTrue(StringUtils.hasText(requestMatcher), \"\");\n\t\t\tbuilder.addConstructorArgReference(requestMatcher);\n\t\t}\n\t\tif (filters.equals(HttpSecurityBeanDefinitionParser.OPT_FILTERS_NONE)) {\n\t\t\tbuilder.addConstructorArgValue(Collections.EMPTY_LIST);\n\t\t}\n\t\telse {\n\t\t\tString[] filterBeanNames = StringUtils.tokenizeToStringArray(filters, \",\");\n\t\t\tManagedList<RuntimeBeanReference> filterChain = new ManagedList<>(filterBeanNames.length);\n\t\t\tfor (String name : filterBeanNames) {\n\t\t\t\tfilterChain.add(new RuntimeBeanReference(name));\n\t\t\t}\n\t\t\tbuilder.addConstructorArgValue(filterChain);\n\t\t}\n\t\treturn builder.getBeanDefinition();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/FilterChainMapBeanDefinitionDecorator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.List;\n\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanDefinitionHolder;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.xml.BeanDefinitionDecorator;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Sets the filter chain Map for a FilterChainProxy bean declaration.\n *\n * @author Luke Taylor\n */\npublic class FilterChainMapBeanDefinitionDecorator implements BeanDefinitionDecorator {\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {\n\t\tBeanDefinition filterChainProxy = holder.getBeanDefinition();\n\t\tManagedList<BeanMetadataElement> securityFilterChains = new ManagedList<>();\n\t\tElement elt = (Element) node;\n\t\tMatcherType matcherType = MatcherType.fromElementOrMvc(elt);\n\t\tList<Element> filterChainElts = DomUtils.getChildElementsByTagName(elt, Elements.FILTER_CHAIN);\n\t\tfor (Element chain : filterChainElts) {\n\t\t\tString path = chain.getAttribute(HttpSecurityBeanDefinitionParser.ATT_PATH_PATTERN);\n\t\t\tString filters = chain.getAttribute(HttpSecurityBeanDefinitionParser.ATT_FILTERS);\n\t\t\tif (!StringUtils.hasText(path)) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"The attribute '\" + HttpSecurityBeanDefinitionParser.ATT_PATH_PATTERN\n\t\t\t\t\t\t\t+ \"' must not be empty\", elt);\n\t\t\t}\n\t\t\tif (!StringUtils.hasText(filters)) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"The attribute '\" + HttpSecurityBeanDefinitionParser.ATT_FILTERS + \"'must not be empty\",\n\t\t\t\t\t\t\telt);\n\t\t\t}\n\t\t\tBeanDefinition matcher = matcherType.createMatcher(parserContext, path, null);\n\t\t\tif (filters.equals(HttpSecurityBeanDefinitionParser.OPT_FILTERS_NONE)) {\n\t\t\t\tsecurityFilterChains.add(createSecurityFilterChain(matcher, new ManagedList<>(0)));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tString[] filterBeanNames = StringUtils.tokenizeToStringArray(filters, \",\");\n\t\t\t\tManagedList filterChain = new ManagedList(filterBeanNames.length);\n\t\t\t\tfor (String name : filterBeanNames) {\n\t\t\t\t\tfilterChain.add(new RuntimeBeanReference(name));\n\t\t\t\t}\n\t\t\t\tsecurityFilterChains.add(createSecurityFilterChain(matcher, filterChain));\n\t\t\t}\n\t\t}\n\t\tfilterChainProxy.getConstructorArgumentValues().addGenericArgumentValue(securityFilterChains);\n\t\treturn holder;\n\t}\n\n\tprivate BeanDefinition createSecurityFilterChain(BeanDefinition matcher, ManagedList<?> filters) {\n\t\tBeanDefinitionBuilder sfc = BeanDefinitionBuilder.rootBeanDefinition(DefaultSecurityFilterChain.class);\n\t\tsfc.addConstructorArgValue(matcher);\n\t\tsfc.addConstructorArgValue(filters);\n\t\treturn sfc.getBeanDefinition();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;\nimport org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Allows for convenient creation of a {@link FilterInvocationSecurityMetadataSource} bean\n * for use with a FilterSecurityInterceptor.\n *\n * @author Luke Taylor\n * @deprecated Use `use-authorization-manager` property instead\n */\n@Deprecated\npublic class FilterInvocationSecurityMetadataSourceParser implements BeanDefinitionParser {\n\n\tprivate static final String ATT_USE_EXPRESSIONS = \"use-expressions\";\n\n\tprivate static final String ATT_HTTP_METHOD = \"method\";\n\n\tprivate static final String ATT_PATTERN = \"pattern\";\n\n\tprivate static final String ATT_ACCESS = \"access\";\n\n\tprivate static final String ATT_SERVLET_PATH = \"servlet-path\";\n\n\tprivate static final Log logger = LogFactory.getLog(FilterInvocationSecurityMetadataSourceParser.class);\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tList<Element> interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);\n\t\t// Check for attributes that aren't allowed in this context\n\t\tfor (Element elt : interceptUrls) {\n\t\t\tif (StringUtils.hasLength(elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REQUIRES_CHANNEL))) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"The attribute '\" + HttpSecurityBeanDefinitionParser.ATT_REQUIRES_CHANNEL\n\t\t\t\t\t\t\t+ \"' isn't allowed here.\", elt);\n\t\t\t}\n\t\t\tif (StringUtils.hasLength(elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_FILTERS))) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"The attribute '\" + HttpSecurityBeanDefinitionParser.ATT_FILTERS + \"' isn't allowed here.\",\n\t\t\t\t\t\t\telt);\n\t\t\t}\n\t\t\tif (StringUtils.hasLength(elt.getAttribute(ATT_SERVLET_PATH))) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"The attribute '\" + ATT_SERVLET_PATH + \"' isn't allowed here.\", elt);\n\t\t\t}\n\t\t}\n\t\tBeanDefinition mds = createSecurityMetadataSource(interceptUrls, false, element, parserContext);\n\t\tString id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);\n\t\tif (StringUtils.hasText(id)) {\n\t\t\tparserContext.registerComponent(new BeanComponentDefinition(mds, id));\n\t\t\tparserContext.getRegistry().registerBeanDefinition(id, mds);\n\t\t}\n\t\treturn mds;\n\t}\n\n\tstatic RootBeanDefinition createSecurityMetadataSource(List<Element> interceptUrls, boolean addAllAuth,\n\t\t\tElement httpElt, ParserContext pc) {\n\t\tMatcherType matcherType = MatcherType.fromElementOrMvc(httpElt);\n\t\tboolean useExpressions = isUseExpressions(httpElt);\n\t\tManagedMap<BeanMetadataElement, BeanDefinition> requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap(\n\t\t\t\tmatcherType, interceptUrls, useExpressions, addAllAuth, pc);\n\t\tBeanDefinitionBuilder fidsBuilder;\n\t\tif (useExpressions) {\n\t\t\tElement expressionHandlerElt = DomUtils.getChildElementByTagName(httpElt, Elements.EXPRESSION_HANDLER);\n\t\t\tString expressionHandlerRef = (expressionHandlerElt != null) ? expressionHandlerElt.getAttribute(\"ref\")\n\t\t\t\t\t: null;\n\t\t\tif (StringUtils.hasText(expressionHandlerRef)) {\n\t\t\t\tlogger.info(\"Using bean '\" + expressionHandlerRef + \"' as web \"\n\t\t\t\t\t\t+ \"SecurityExpressionHandler implementation\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\texpressionHandlerRef = registerDefaultExpressionHandler(pc);\n\t\t\t}\n\t\t\tfidsBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(ExpressionBasedFilterInvocationSecurityMetadataSource.class);\n\t\t\tfidsBuilder.addConstructorArgValue(requestToAttributesMap);\n\t\t\tfidsBuilder.addConstructorArgReference(expressionHandlerRef);\n\t\t}\n\t\telse {\n\t\t\tfidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);\n\t\t\tfidsBuilder.addConstructorArgValue(requestToAttributesMap);\n\t\t}\n\t\tfidsBuilder.getRawBeanDefinition().setSource(pc.extractSource(httpElt));\n\t\treturn (RootBeanDefinition) fidsBuilder.getBeanDefinition();\n\t}\n\n\tstatic String registerDefaultExpressionHandler(ParserContext pc) {\n\t\tBeanDefinition expressionHandler = GrantedAuthorityDefaultsParserUtils.registerWithDefaultRolePrefix(pc,\n\t\t\t\tDefaultWebSecurityExpressionHandlerBeanFactory.class);\n\t\tString expressionHandlerRef = pc.getReaderContext().generateBeanName(expressionHandler);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));\n\t\treturn expressionHandlerRef;\n\t}\n\n\tstatic boolean isUseExpressions(Element elt) {\n\t\tString useExpressions = elt.getAttribute(ATT_USE_EXPRESSIONS);\n\t\treturn !StringUtils.hasText(useExpressions) || \"true\".equals(useExpressions);\n\t}\n\n\tprivate static ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForFilterInvocationRequestMap(\n\t\t\tMatcherType matcherType, List<Element> urlElts, boolean useExpressions, boolean addAuthenticatedAll,\n\t\t\tParserContext parserContext) {\n\t\tManagedMap<BeanMetadataElement, BeanDefinition> filterInvocationDefinitionMap = new ManagedMap<>();\n\t\tfor (Element urlElt : urlElts) {\n\t\t\tString access = urlElt.getAttribute(ATT_ACCESS);\n\t\t\tString path = urlElt.getAttribute(ATT_PATTERN);\n\t\t\tif (!StringUtils.hasText(access)) {\n\t\t\t\tparserContext.getReaderContext().error(\"access attribute cannot be empty or null\", urlElt);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString matcherRef = urlElt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REQUEST_MATCHER_REF);\n\t\t\tboolean hasMatcherRef = StringUtils.hasText(matcherRef);\n\t\t\tif (!hasMatcherRef && !StringUtils.hasText(path)) {\n\t\t\t\tparserContext.getReaderContext().error(\"path attribute cannot be empty or null\", urlElt);\n\t\t\t}\n\t\t\tString method = urlElt.getAttribute(ATT_HTTP_METHOD);\n\t\t\tif (!StringUtils.hasText(method)) {\n\t\t\t\tmethod = null;\n\t\t\t}\n\t\t\tString servletPath = urlElt.getAttribute(ATT_SERVLET_PATH);\n\t\t\tif (!StringUtils.hasText(servletPath)) {\n\t\t\t\tservletPath = null;\n\t\t\t}\n\t\t\telse if (!MatcherType.path.equals(matcherType)) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(ATT_SERVLET_PATH + \" is not applicable for request-matcher: '\" + matcherType.name() + \"'\",\n\t\t\t\t\t\t\turlElt);\n\t\t\t}\n\t\t\tBeanMetadataElement matcher = hasMatcherRef ? new RuntimeBeanReference(matcherRef)\n\t\t\t\t\t: matcherType.createMatcher(parserContext, path, method, servletPath);\n\t\t\tBeanDefinitionBuilder attributeBuilder = BeanDefinitionBuilder.rootBeanDefinition(SecurityConfig.class);\n\t\t\tif (useExpressions) {\n\t\t\t\tlogger.info(\"Creating access control expression attribute '\" + access + \"' for \" + path);\n\t\t\t\t// The single expression will be parsed later by the\n\t\t\t\t// ExpressionBasedFilterInvocationSecurityMetadataSource\n\t\t\t\tattributeBuilder.addConstructorArgValue(new String[] { access });\n\t\t\t\tattributeBuilder.setFactoryMethod(\"createList\");\n\n\t\t\t}\n\t\t\telse {\n\t\t\t\tattributeBuilder.addConstructorArgValue(access);\n\t\t\t\tattributeBuilder.setFactoryMethod(\"createListFromCommaDelimitedString\");\n\t\t\t}\n\t\t\tif (filterInvocationDefinitionMap.containsKey(matcher)) {\n\t\t\t\tlogger.warn(\"Duplicate URL defined: \" + path + \". The original attribute values will be overwritten\");\n\t\t\t}\n\t\t\tfilterInvocationDefinitionMap.put(matcher, attributeBuilder.getBeanDefinition());\n\t\t}\n\t\tif (addAuthenticatedAll && filterInvocationDefinitionMap.isEmpty()) {\n\t\t\tBeanDefinition matcher = matcherType.createMatcher(parserContext, \"/**\", null);\n\t\t\tBeanDefinitionBuilder attributeBuilder = BeanDefinitionBuilder.rootBeanDefinition(SecurityConfig.class);\n\t\t\tattributeBuilder.addConstructorArgValue(new String[] { \"authenticated\" });\n\t\t\tattributeBuilder.setFactoryMethod(\"createList\");\n\t\t\tfilterInvocationDefinitionMap.put(matcher, attributeBuilder.getBeanDefinition());\n\t\t}\n\t\treturn filterInvocationDefinitionMap;\n\t}\n\n\tstatic class DefaultWebSecurityExpressionHandlerBeanFactory\n\t\t\textends GrantedAuthorityDefaultsParserUtils.AbstractGrantedAuthorityDefaultsBeanFactory {\n\n\t\tprivate DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();\n\n\t\t@Override\n\t\tpublic DefaultWebSecurityExpressionHandler getBean() {\n\t\t\tthis.handler.setDefaultRolePrefix(this.rolePrefix);\n\t\t\treturn this.handler;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.web.authentication.ForwardAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Luke Taylor\n * @author Ben Alex\n * @author Rob Winch\n * @author Kazuki Shimizu\n * @author Shazin Sadakath\n */\npublic class FormLoginBeanDefinitionParser {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final String ATT_LOGIN_URL = \"login-processing-url\";\n\n\tstatic final String ATT_LOGIN_PAGE = \"login-page\";\n\n\tprivate static final String DEF_LOGIN_PAGE = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;\n\n\tprivate static final String ATT_FORM_LOGIN_TARGET_URL = \"default-target-url\";\n\n\tprivate static final String ATT_ALWAYS_USE_DEFAULT_TARGET_URL = \"always-use-default-target\";\n\n\tprivate static final String DEF_FORM_LOGIN_TARGET_URL = \"/\";\n\n\tprivate static final String ATT_USERNAME_PARAMETER = \"username-parameter\";\n\n\tprivate static final String ATT_PASSWORD_PARAMETER = \"password-parameter\";\n\n\tprivate static final String ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL = \"authentication-failure-url\";\n\n\tprivate static final String DEF_FORM_LOGIN_AUTHENTICATION_FAILURE_URL = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL\n\t\t\t+ \"?\" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME;\n\n\tprivate static final String ATT_SUCCESS_HANDLER_REF = \"authentication-success-handler-ref\";\n\n\tprivate static final String ATT_FAILURE_HANDLER_REF = \"authentication-failure-handler-ref\";\n\n\tprivate static final String ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_FORWARD_URL = \"authentication-failure-forward-url\";\n\n\tprivate static final String ATT_FORM_LOGIN_AUTHENTICATION_SUCCESS_FORWARD_URL = \"authentication-success-forward-url\";\n\n\tprivate final String defaultLoginProcessingUrl;\n\n\tprivate final String filterClassName;\n\n\tprivate final BeanReference requestCache;\n\n\tprivate final BeanReference sessionStrategy;\n\n\tprivate final boolean allowSessionCreation;\n\n\tprivate final BeanReference portMapper;\n\n\tprivate RootBeanDefinition filterBean;\n\n\tprivate RootBeanDefinition entryPointBean;\n\n\tprivate String loginPage;\n\n\tprivate String loginMethod;\n\n\tprivate String loginProcessingUrl;\n\n\tFormLoginBeanDefinitionParser(String defaultLoginProcessingUrl, String loginMethod, String filterClassName,\n\t\t\tBeanReference requestCache, BeanReference sessionStrategy, boolean allowSessionCreation,\n\t\t\tBeanReference portMapper) {\n\t\tthis.defaultLoginProcessingUrl = defaultLoginProcessingUrl;\n\t\tthis.loginMethod = loginMethod;\n\t\tthis.filterClassName = filterClassName;\n\t\tthis.requestCache = requestCache;\n\t\tthis.sessionStrategy = sessionStrategy;\n\t\tthis.allowSessionCreation = allowSessionCreation;\n\t\tthis.portMapper = portMapper;\n\t}\n\n\tpublic BeanDefinition parse(Element elt, ParserContext pc) {\n\t\tString loginUrl = null;\n\t\tString defaultTargetUrl = null;\n\t\tString authenticationFailureUrl = null;\n\t\tString alwaysUseDefault = null;\n\t\tString successHandlerRef = null;\n\t\tString failureHandlerRef = null;\n\t\t// Only available with form-login\n\t\tString usernameParameter = null;\n\t\tString passwordParameter = null;\n\t\tString authDetailsSourceRef = null;\n\t\tString authenticationFailureForwardUrl = null;\n\t\tString authenticationSuccessForwardUrl = null;\n\t\tObject source = null;\n\t\tif (elt != null) {\n\t\t\tsource = pc.extractSource(elt);\n\t\t\tloginUrl = elt.getAttribute(ATT_LOGIN_URL);\n\t\t\tWebConfigUtils.validateHttpRedirect(loginUrl, pc, source);\n\t\t\tdefaultTargetUrl = elt.getAttribute(ATT_FORM_LOGIN_TARGET_URL);\n\t\t\tWebConfigUtils.validateHttpRedirect(defaultTargetUrl, pc, source);\n\t\t\tauthenticationFailureUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL);\n\t\t\tWebConfigUtils.validateHttpRedirect(authenticationFailureUrl, pc, source);\n\t\t\talwaysUseDefault = elt.getAttribute(ATT_ALWAYS_USE_DEFAULT_TARGET_URL);\n\t\t\tthis.loginPage = elt.getAttribute(ATT_LOGIN_PAGE);\n\t\t\tsuccessHandlerRef = elt.getAttribute(ATT_SUCCESS_HANDLER_REF);\n\t\t\tfailureHandlerRef = elt.getAttribute(ATT_FAILURE_HANDLER_REF);\n\t\t\tauthDetailsSourceRef = elt.getAttribute(AuthenticationConfigBuilder.ATT_AUTH_DETAILS_SOURCE_REF);\n\t\t\tauthenticationFailureForwardUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_FORWARD_URL);\n\t\t\tWebConfigUtils.validateHttpRedirect(authenticationFailureForwardUrl, pc, source);\n\t\t\tauthenticationSuccessForwardUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_SUCCESS_FORWARD_URL);\n\t\t\tWebConfigUtils.validateHttpRedirect(authenticationSuccessForwardUrl, pc, source);\n\t\t\tif (!StringUtils.hasText(this.loginPage)) {\n\t\t\t\tthis.loginPage = null;\n\t\t\t}\n\t\t\tWebConfigUtils.validateHttpRedirect(this.loginPage, pc, source);\n\t\t\tusernameParameter = elt.getAttribute(ATT_USERNAME_PARAMETER);\n\t\t\tpasswordParameter = elt.getAttribute(ATT_PASSWORD_PARAMETER);\n\t\t}\n\t\tthis.filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault, this.loginPage,\n\t\t\t\tauthenticationFailureUrl, successHandlerRef, failureHandlerRef, authDetailsSourceRef,\n\t\t\t\tauthenticationFailureForwardUrl, authenticationSuccessForwardUrl);\n\t\tif (StringUtils.hasText(usernameParameter)) {\n\t\t\tthis.filterBean.getPropertyValues().addPropertyValue(\"usernameParameter\", usernameParameter);\n\t\t}\n\t\tif (StringUtils.hasText(passwordParameter)) {\n\t\t\tthis.filterBean.getPropertyValues().addPropertyValue(\"passwordParameter\", passwordParameter);\n\t\t}\n\t\tthis.filterBean.setSource(source);\n\t\tBeanDefinitionBuilder entryPointBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class);\n\t\tentryPointBuilder.getRawBeanDefinition().setSource(source);\n\t\tentryPointBuilder.addConstructorArgValue((this.loginPage != null) ? this.loginPage : DEF_LOGIN_PAGE);\n\t\tentryPointBuilder.addPropertyValue(\"portMapper\", this.portMapper);\n\t\tthis.entryPointBean = (RootBeanDefinition) entryPointBuilder.getBeanDefinition();\n\t\treturn null;\n\t}\n\n\tprivate RootBeanDefinition createFilterBean(String loginUrl, String defaultTargetUrl, String alwaysUseDefault,\n\t\t\tString loginPage, String authenticationFailureUrl, String successHandlerRef, String failureHandlerRef,\n\t\t\tString authDetailsSourceRef, String authenticationFailureForwardUrl,\n\t\t\tString authenticationSuccessForwardUrl) {\n\t\tBeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(this.filterClassName);\n\t\tif (!StringUtils.hasText(loginUrl)) {\n\t\t\tloginUrl = this.defaultLoginProcessingUrl;\n\t\t}\n\t\tthis.loginProcessingUrl = loginUrl;\n\t\tBeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class);\n\t\tmatcherBuilder.addConstructorArgValue(loginUrl);\n\t\tif (this.loginMethod != null) {\n\t\t\tmatcherBuilder.addConstructorArgValue(\"POST\");\n\t\t}\n\t\tfilterBuilder.addPropertyValue(\"requiresAuthenticationRequestMatcher\", matcherBuilder.getBeanDefinition());\n\t\tif (StringUtils.hasText(successHandlerRef)) {\n\t\t\tfilterBuilder.addPropertyReference(\"authenticationSuccessHandler\", successHandlerRef);\n\t\t}\n\t\telse if (StringUtils.hasText(authenticationSuccessForwardUrl)) {\n\t\t\tBeanDefinitionBuilder forwardSuccessHandler = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(ForwardAuthenticationSuccessHandler.class);\n\t\t\tforwardSuccessHandler.addConstructorArgValue(authenticationSuccessForwardUrl);\n\t\t\tfilterBuilder.addPropertyValue(\"authenticationSuccessHandler\", forwardSuccessHandler.getBeanDefinition());\n\t\t}\n\t\telse {\n\t\t\tBeanDefinitionBuilder successHandler = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(SavedRequestAwareAuthenticationSuccessHandler.class);\n\t\t\tif (\"true\".equals(alwaysUseDefault)) {\n\t\t\t\tsuccessHandler.addPropertyValue(\"alwaysUseDefaultTargetUrl\", Boolean.TRUE);\n\t\t\t}\n\t\t\tsuccessHandler.addPropertyValue(\"requestCache\", this.requestCache);\n\t\t\tsuccessHandler.addPropertyValue(\"defaultTargetUrl\",\n\t\t\t\t\tStringUtils.hasText(defaultTargetUrl) ? defaultTargetUrl : DEF_FORM_LOGIN_TARGET_URL);\n\t\t\tfilterBuilder.addPropertyValue(\"authenticationSuccessHandler\", successHandler.getBeanDefinition());\n\t\t}\n\t\tif (StringUtils.hasText(authDetailsSourceRef)) {\n\t\t\tfilterBuilder.addPropertyReference(\"authenticationDetailsSource\", authDetailsSourceRef);\n\t\t}\n\t\tif (this.sessionStrategy != null) {\n\t\t\tfilterBuilder.addPropertyValue(\"sessionAuthenticationStrategy\", this.sessionStrategy);\n\t\t}\n\t\tif (StringUtils.hasText(failureHandlerRef)) {\n\t\t\tfilterBuilder.addPropertyReference(\"authenticationFailureHandler\", failureHandlerRef);\n\t\t}\n\t\telse if (StringUtils.hasText(authenticationFailureForwardUrl)) {\n\t\t\tBeanDefinitionBuilder forwardFailureHandler = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(ForwardAuthenticationFailureHandler.class);\n\t\t\tforwardFailureHandler.addConstructorArgValue(authenticationFailureForwardUrl);\n\t\t\tfilterBuilder.addPropertyValue(\"authenticationFailureHandler\", forwardFailureHandler.getBeanDefinition());\n\t\t}\n\t\telse {\n\t\t\tBeanDefinitionBuilder failureHandler = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);\n\t\t\tif (!StringUtils.hasText(authenticationFailureUrl)) {\n\t\t\t\t// Fall back to re-displaying the custom login page, if one was specified.\n\t\t\t\tif (StringUtils.hasText(loginPage)) {\n\t\t\t\t\tauthenticationFailureUrl = loginPage + \"?\" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tauthenticationFailureUrl = DEF_FORM_LOGIN_AUTHENTICATION_FAILURE_URL;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfailureHandler.addPropertyValue(\"defaultFailureUrl\", authenticationFailureUrl);\n\t\t\tfailureHandler.addPropertyValue(\"allowSessionCreation\", this.allowSessionCreation);\n\t\t\tfilterBuilder.addPropertyValue(\"authenticationFailureHandler\", failureHandler.getBeanDefinition());\n\t\t}\n\t\treturn (RootBeanDefinition) filterBuilder.getBeanDefinition();\n\t}\n\n\tRootBeanDefinition getFilterBean() {\n\t\treturn this.filterBean;\n\t}\n\n\tRootBeanDefinition getEntryPointBean() {\n\t\treturn this.entryPointBean;\n\t}\n\n\tString getLoginPage() {\n\t\treturn this.loginPage;\n\t}\n\n\tString getLoginProcessingUrl() {\n\t\treturn this.loginProcessingUrl;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/GrantedAuthorityDefaultsParserUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\n\n/**\n * @author Rob Winch\n * @author Ngoc Nhan\n * @since 4.2\n */\nfinal class GrantedAuthorityDefaultsParserUtils {\n\n\tprivate GrantedAuthorityDefaultsParserUtils() {\n\t}\n\n\tstatic RootBeanDefinition registerWithDefaultRolePrefix(ParserContext pc,\n\t\t\tClass<? extends AbstractGrantedAuthorityDefaultsBeanFactory> beanFactoryClass) {\n\t\tRootBeanDefinition beanFactoryDefinition = new RootBeanDefinition(beanFactoryClass);\n\t\tString beanFactoryRef = pc.getReaderContext().generateBeanName(beanFactoryDefinition);\n\t\tpc.getRegistry().registerBeanDefinition(beanFactoryRef, beanFactoryDefinition);\n\t\tRootBeanDefinition bean = new RootBeanDefinition();\n\t\tbean.setFactoryBeanName(beanFactoryRef);\n\t\tbean.setFactoryMethodName(\"getBean\");\n\t\treturn bean;\n\t}\n\n\tabstract static class AbstractGrantedAuthorityDefaultsBeanFactory implements ApplicationContextAware {\n\n\t\tprotected String rolePrefix = \"ROLE_\";\n\n\t\t@Override\n\t\tpublic final void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\t\tapplicationContext.getBeanProvider(GrantedAuthorityDefaults.class)\n\t\t\t\t.ifUnique((grantedAuthorityDefaults) -> this.rolePrefix = grantedAuthorityDefaults.getRolePrefix());\n\t\t}\n\n\t\tabstract Object getBean();\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.web.header.HeaderWriterFilter;\nimport org.springframework.security.web.header.writers.CacheControlHeadersWriter;\nimport org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.HpkpHeaderWriter;\nimport org.springframework.security.web.header.writers.HstsHeaderWriter;\nimport org.springframework.security.web.header.writers.PermissionsPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;\nimport org.springframework.security.web.header.writers.StaticHeadersWriter;\nimport org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter;\nimport org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;\nimport org.springframework.security.web.header.writers.frameoptions.RegExpAllowFromStrategy;\nimport org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;\nimport org.springframework.security.web.header.writers.frameoptions.WhiteListedAllowFromStrategy;\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Parser for the {@code HeadersFilter}.\n *\n * @author Marten Deinum\n * @author Tim Ysewyn\n * @author Eddú Meléndez\n * @author Vedran Pavic\n * @author Rafiullah Hamedy\n * @since 3.2\n */\npublic class HeadersBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ATT_DISABLED = \"disabled\";\n\n\tprivate static final String ATT_POLICY = \"policy\";\n\n\tprivate static final String ATT_STRATEGY = \"strategy\";\n\n\tprivate static final String ATT_FROM_PARAMETER = \"from-parameter\";\n\n\tprivate static final String ATT_NAME = \"name\";\n\n\tprivate static final String ATT_VALUE = \"value\";\n\n\tprivate static final String ATT_REF = \"ref\";\n\n\tprivate static final String ATT_INCLUDE_SUBDOMAINS = \"include-subdomains\";\n\n\tprivate static final String ATT_MAX_AGE_SECONDS = \"max-age-seconds\";\n\n\tprivate static final String ATT_REQUEST_MATCHER_REF = \"request-matcher-ref\";\n\n\tprivate static final String ATT_PRELOAD = \"preload\";\n\n\tprivate static final String ATT_REPORT_ONLY = \"report-only\";\n\n\tprivate static final String ATT_REPORT_URI = \"report-uri\";\n\n\tprivate static final String ATT_ALGORITHM = \"algorithm\";\n\n\tprivate static final String ATT_POLICY_DIRECTIVES = \"policy-directives\";\n\n\tprivate static final String ATT_HEADER_VALUE = \"header-value\";\n\n\tprivate static final String CACHE_CONTROL_ELEMENT = \"cache-control\";\n\n\tprivate static final String HPKP_ELEMENT = \"hpkp\";\n\n\tprivate static final String PINS_ELEMENT = \"pins\";\n\n\tprivate static final String HSTS_ELEMENT = \"hsts\";\n\n\tprivate static final String XSS_ELEMENT = \"xss-protection\";\n\n\tprivate static final String CONTENT_TYPE_ELEMENT = \"content-type-options\";\n\n\tprivate static final String FRAME_OPTIONS_ELEMENT = \"frame-options\";\n\n\tprivate static final String GENERIC_HEADER_ELEMENT = \"header\";\n\n\tprivate static final String CONTENT_SECURITY_POLICY_ELEMENT = \"content-security-policy\";\n\n\tprivate static final String REFERRER_POLICY_ELEMENT = \"referrer-policy\";\n\n\tprivate static final String FEATURE_POLICY_ELEMENT = \"feature-policy\";\n\n\tprivate static final String PERMISSIONS_POLICY_ELEMENT = \"permissions-policy\";\n\n\tprivate static final String CROSS_ORIGIN_OPENER_POLICY_ELEMENT = \"cross-origin-opener-policy\";\n\n\tprivate static final String CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT = \"cross-origin-embedder-policy\";\n\n\tprivate static final String CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT = \"cross-origin-resource-policy\";\n\n\tprivate static final String ALLOW_FROM = \"ALLOW-FROM\";\n\n\tprivate ManagedList<BeanMetadataElement> headerWriters;\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tthis.headerWriters = new ManagedList<>();\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(HeaderWriterFilter.class);\n\t\tboolean disabled = element != null && \"true\".equals(resolveAttribute(parserContext, element, \"disabled\"));\n\t\tboolean defaultsDisabled = element != null\n\t\t\t\t&& \"true\".equals(resolveAttribute(parserContext, element, \"defaults-disabled\"));\n\t\tboolean addIfNotPresent = element == null || !disabled && !defaultsDisabled;\n\t\tparseCacheControlElement(addIfNotPresent, element);\n\t\tparseHstsElement(addIfNotPresent, element, parserContext);\n\t\tparseXssElement(addIfNotPresent, element, parserContext);\n\t\tparseFrameOptionsElement(addIfNotPresent, element, parserContext);\n\t\tparseContentTypeOptionsElement(addIfNotPresent, element);\n\t\tparseHpkpElement(element == null || !disabled, element, parserContext);\n\t\tparseContentSecurityPolicyElement(disabled, element, parserContext);\n\t\tparseReferrerPolicyElement(element, parserContext);\n\t\tparseFeaturePolicyElement(element, parserContext);\n\t\tparsePermissionsPolicyElement(element, parserContext);\n\t\tparseCrossOriginOpenerPolicy(disabled, element);\n\t\tparseCrossOriginEmbedderPolicy(disabled, element);\n\t\tparseCrossOriginResourcePolicy(disabled, element);\n\t\tparseHeaderElements(element);\n\t\tboolean noWriters = this.headerWriters.isEmpty();\n\t\tif (disabled && !noWriters) {\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(\"Cannot specify <headers disabled=\\\"true\\\"> with child elements.\", element);\n\t\t}\n\t\telse if (noWriters) {\n\t\t\treturn null;\n\t\t}\n\t\tbuilder.addConstructorArgValue(this.headerWriters);\n\t\treturn builder.getBeanDefinition();\n\t}\n\n\t/**\n\t * Resolve the placeholder for a given attribute on a element.\n\t * @param pc\n\t * @param element\n\t * @param attributeName\n\t * @return Resolved value of the placeholder\n\t */\n\tprivate String resolveAttribute(ParserContext pc, Element element, String attributeName) {\n\t\treturn pc.getReaderContext().getEnvironment().resolvePlaceholders(element.getAttribute(attributeName));\n\t}\n\n\tprivate void parseCacheControlElement(boolean addIfNotPresent, Element element) {\n\t\tElement cacheControlElement = (element != null)\n\t\t\t\t? DomUtils.getChildElementByTagName(element, CACHE_CONTROL_ELEMENT) : null;\n\t\tboolean disabled = \"true\".equals(getAttribute(cacheControlElement, ATT_DISABLED, \"false\"));\n\t\tif (disabled) {\n\t\t\treturn;\n\t\t}\n\t\tif (addIfNotPresent || cacheControlElement != null) {\n\t\t\taddCacheControl();\n\t\t}\n\t}\n\n\tprivate void addCacheControl() {\n\t\tBeanDefinitionBuilder headersWriter = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(CacheControlHeadersWriter.class);\n\t\tthis.headerWriters.add(headersWriter.getBeanDefinition());\n\t}\n\n\tprivate void parseHstsElement(boolean addIfNotPresent, Element element, ParserContext context) {\n\t\tElement hstsElement = (element != null) ? DomUtils.getChildElementByTagName(element, HSTS_ELEMENT) : null;\n\t\tif (addIfNotPresent || hstsElement != null) {\n\t\t\taddHsts(addIfNotPresent, hstsElement, context);\n\t\t}\n\t}\n\n\tprivate void addHsts(boolean addIfNotPresent, Element hstsElement, ParserContext context) {\n\t\tBeanDefinitionBuilder headersWriter = BeanDefinitionBuilder.genericBeanDefinition(HstsHeaderWriter.class);\n\t\tif (hstsElement != null) {\n\t\t\tboolean disabled = \"true\".equals(getAttribute(hstsElement, ATT_DISABLED, \"false\"));\n\t\t\tString includeSubDomains = hstsElement.getAttribute(ATT_INCLUDE_SUBDOMAINS);\n\t\t\tif (StringUtils.hasText(includeSubDomains)) {\n\t\t\t\tif (disabled) {\n\t\t\t\t\tattrNotAllowed(context, ATT_INCLUDE_SUBDOMAINS, ATT_DISABLED, hstsElement);\n\t\t\t\t}\n\t\t\t\theadersWriter.addPropertyValue(\"includeSubDomains\", includeSubDomains);\n\t\t\t}\n\t\t\tString maxAgeSeconds = hstsElement.getAttribute(ATT_MAX_AGE_SECONDS);\n\t\t\tif (StringUtils.hasText(maxAgeSeconds)) {\n\t\t\t\tif (disabled) {\n\t\t\t\t\tattrNotAllowed(context, ATT_MAX_AGE_SECONDS, ATT_DISABLED, hstsElement);\n\t\t\t\t}\n\t\t\t\theadersWriter.addPropertyValue(\"maxAgeInSeconds\", maxAgeSeconds);\n\t\t\t}\n\t\t\tString requestMatcherRef = hstsElement.getAttribute(ATT_REQUEST_MATCHER_REF);\n\t\t\tif (StringUtils.hasText(requestMatcherRef)) {\n\t\t\t\tif (disabled) {\n\t\t\t\t\tattrNotAllowed(context, ATT_REQUEST_MATCHER_REF, ATT_DISABLED, hstsElement);\n\t\t\t\t}\n\t\t\t\theadersWriter.addPropertyReference(\"requestMatcher\", requestMatcherRef);\n\t\t\t}\n\t\t\tString preload = hstsElement.getAttribute(ATT_PRELOAD);\n\t\t\tif (StringUtils.hasText(preload)) {\n\t\t\t\tif (disabled) {\n\t\t\t\t\tattrNotAllowed(context, ATT_PRELOAD, ATT_DISABLED, hstsElement);\n\t\t\t\t}\n\t\t\t\theadersWriter.addPropertyValue(\"preload\", preload);\n\t\t\t}\n\t\t\tif (disabled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (addIfNotPresent || hstsElement != null) {\n\t\t\tthis.headerWriters.add(headersWriter.getBeanDefinition());\n\t\t}\n\t}\n\n\tprivate void parseHpkpElement(boolean addIfNotPresent, Element element, ParserContext context) {\n\t\tElement hpkpElement = (element != null) ? DomUtils.getChildElementByTagName(element, HPKP_ELEMENT) : null;\n\t\tif (addIfNotPresent || hpkpElement != null) {\n\t\t\taddHpkp(addIfNotPresent, hpkpElement, context);\n\t\t}\n\t}\n\n\tprivate void addHpkp(boolean addIfNotPresent, Element hpkpElement, ParserContext context) {\n\t\tif (hpkpElement != null) {\n\t\t\tboolean disabled = \"true\".equals(getAttribute(hpkpElement, ATT_DISABLED, \"false\"));\n\t\t\tif (disabled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tBeanDefinitionBuilder headersWriter = BeanDefinitionBuilder.genericBeanDefinition(HpkpHeaderWriter.class);\n\t\t\tElement pinsElement = DomUtils.getChildElementByTagName(hpkpElement, PINS_ELEMENT);\n\t\t\tif (pinsElement != null) {\n\t\t\t\tList<Element> pinElements = DomUtils.getChildElements(pinsElement);\n\t\t\t\tMap<String, String> pins = new LinkedHashMap<>();\n\t\t\t\tfor (Element pinElement : pinElements) {\n\t\t\t\t\tString hash = pinElement.getAttribute(ATT_ALGORITHM);\n\t\t\t\t\tif (!StringUtils.hasText(hash)) {\n\t\t\t\t\t\thash = \"sha256\";\n\t\t\t\t\t}\n\t\t\t\t\tNode pinValueNode = pinElement.getFirstChild();\n\t\t\t\t\tif (pinValueNode == null) {\n\t\t\t\t\t\tcontext.getReaderContext().warning(\"Missing value for pin entry.\", hpkpElement);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tString fingerprint = pinElement.getFirstChild().getTextContent();\n\t\t\t\t\tpins.put(fingerprint, hash);\n\t\t\t\t}\n\t\t\t\theadersWriter.addPropertyValue(\"pins\", pins);\n\t\t\t}\n\t\t\tString includeSubDomains = hpkpElement.getAttribute(ATT_INCLUDE_SUBDOMAINS);\n\t\t\tif (StringUtils.hasText(includeSubDomains)) {\n\t\t\t\theadersWriter.addPropertyValue(\"includeSubDomains\", includeSubDomains);\n\t\t\t}\n\t\t\tString maxAgeSeconds = hpkpElement.getAttribute(ATT_MAX_AGE_SECONDS);\n\t\t\tif (StringUtils.hasText(maxAgeSeconds)) {\n\t\t\t\theadersWriter.addPropertyValue(\"maxAgeInSeconds\", maxAgeSeconds);\n\t\t\t}\n\t\t\tString reportOnly = hpkpElement.getAttribute(ATT_REPORT_ONLY);\n\t\t\tif (StringUtils.hasText(reportOnly)) {\n\t\t\t\theadersWriter.addPropertyValue(\"reportOnly\", reportOnly);\n\t\t\t}\n\t\t\tString reportUri = hpkpElement.getAttribute(ATT_REPORT_URI);\n\t\t\tif (StringUtils.hasText(reportUri)) {\n\t\t\t\theadersWriter.addPropertyValue(\"reportUri\", reportUri);\n\t\t\t}\n\t\t\tif (addIfNotPresent) {\n\t\t\t\tthis.headerWriters.add(headersWriter.getBeanDefinition());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void parseContentSecurityPolicyElement(boolean elementDisabled, Element element, ParserContext context) {\n\t\tElement contentSecurityPolicyElement = (elementDisabled || element == null) ? null\n\t\t\t\t: DomUtils.getChildElementByTagName(element, CONTENT_SECURITY_POLICY_ELEMENT);\n\t\tif (contentSecurityPolicyElement != null) {\n\t\t\taddContentSecurityPolicy(contentSecurityPolicyElement, context);\n\t\t}\n\t}\n\n\tprivate void addContentSecurityPolicy(Element contentSecurityPolicyElement, ParserContext context) {\n\t\tBeanDefinitionBuilder headersWriter = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(ContentSecurityPolicyHeaderWriter.class);\n\t\tString policyDirectives = contentSecurityPolicyElement.getAttribute(ATT_POLICY_DIRECTIVES);\n\t\tif (!StringUtils.hasText(policyDirectives)) {\n\t\t\tcontext.getReaderContext()\n\t\t\t\t.error(ATT_POLICY_DIRECTIVES + \" requires a 'value' to be set.\", contentSecurityPolicyElement);\n\t\t}\n\t\telse {\n\t\t\theadersWriter.addConstructorArgValue(policyDirectives);\n\t\t}\n\t\tString reportOnly = contentSecurityPolicyElement.getAttribute(ATT_REPORT_ONLY);\n\t\tif (StringUtils.hasText(reportOnly)) {\n\t\t\theadersWriter.addPropertyValue(\"reportOnly\", reportOnly);\n\t\t}\n\t\tthis.headerWriters.add(headersWriter.getBeanDefinition());\n\t}\n\n\tprivate void parseReferrerPolicyElement(Element element, ParserContext context) {\n\t\tElement referrerPolicyElement = (element != null)\n\t\t\t\t? DomUtils.getChildElementByTagName(element, REFERRER_POLICY_ELEMENT) : null;\n\t\tif (referrerPolicyElement != null) {\n\t\t\taddReferrerPolicy(referrerPolicyElement, context);\n\t\t}\n\t}\n\n\tprivate void addReferrerPolicy(Element referrerPolicyElement, ParserContext context) {\n\t\tBeanDefinitionBuilder headersWriter = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(ReferrerPolicyHeaderWriter.class);\n\t\tString policy = referrerPolicyElement.getAttribute(ATT_POLICY);\n\t\tif (StringUtils.hasLength(policy)) {\n\t\t\theadersWriter.addConstructorArgValue(ReferrerPolicy.get(policy));\n\t\t}\n\t\tthis.headerWriters.add(headersWriter.getBeanDefinition());\n\t}\n\n\tprivate void parseFeaturePolicyElement(Element element, ParserContext context) {\n\t\tElement featurePolicyElement = (element != null)\n\t\t\t\t? DomUtils.getChildElementByTagName(element, FEATURE_POLICY_ELEMENT) : null;\n\t\tif (featurePolicyElement != null) {\n\t\t\taddFeaturePolicy(featurePolicyElement, context);\n\t\t}\n\t}\n\n\tprivate void addFeaturePolicy(Element featurePolicyElement, ParserContext context) {\n\t\tBeanDefinitionBuilder headersWriter = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(FeaturePolicyHeaderWriter.class);\n\t\tString policyDirectives = featurePolicyElement.getAttribute(ATT_POLICY_DIRECTIVES);\n\t\tif (!StringUtils.hasText(policyDirectives)) {\n\t\t\tcontext.getReaderContext()\n\t\t\t\t.error(ATT_POLICY_DIRECTIVES + \" requires a 'value' to be set.\", featurePolicyElement);\n\t\t}\n\t\telse {\n\t\t\theadersWriter.addConstructorArgValue(policyDirectives);\n\t\t}\n\t\tthis.headerWriters.add(headersWriter.getBeanDefinition());\n\t}\n\n\tprivate void parsePermissionsPolicyElement(Element element, ParserContext context) {\n\t\tElement permissionsPolicyElement = (element != null)\n\t\t\t\t? DomUtils.getChildElementByTagName(element, PERMISSIONS_POLICY_ELEMENT) : null;\n\t\tif (permissionsPolicyElement != null) {\n\t\t\taddPermissionsPolicy(permissionsPolicyElement, context);\n\t\t}\n\t}\n\n\tprivate void addPermissionsPolicy(Element permissionsPolicyElement, ParserContext context) {\n\t\tBeanDefinitionBuilder headersWriter = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(PermissionsPolicyHeaderWriter.class);\n\t\tString policyDirectives = permissionsPolicyElement.getAttribute(ATT_POLICY);\n\t\tif (!StringUtils.hasText(policyDirectives)) {\n\t\t\tcontext.getReaderContext().error(ATT_POLICY + \" requires a 'value' to be set.\", permissionsPolicyElement);\n\t\t}\n\t\telse {\n\t\t\theadersWriter.addConstructorArgValue(policyDirectives);\n\t\t}\n\t\tthis.headerWriters.add(headersWriter.getBeanDefinition());\n\t}\n\n\tprivate void parseCrossOriginOpenerPolicy(boolean elementDisabled, Element element) {\n\t\tif (elementDisabled || element == null) {\n\t\t\treturn;\n\t\t}\n\t\tCrossOriginOpenerPolicyHeaderWriter writer = new CrossOriginOpenerPolicyHeaderWriter();\n\t\tElement crossOriginOpenerPolicyElement = DomUtils.getChildElementByTagName(element,\n\t\t\t\tCROSS_ORIGIN_OPENER_POLICY_ELEMENT);\n\t\tif (crossOriginOpenerPolicyElement != null) {\n\t\t\taddCrossOriginOpenerPolicy(crossOriginOpenerPolicyElement, writer);\n\t\t}\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(CrossOriginOpenerPolicyHeaderWriter.class, () -> writer);\n\t\tthis.headerWriters.add(builder.getBeanDefinition());\n\t}\n\n\tprivate void parseCrossOriginEmbedderPolicy(boolean elementDisabled, Element element) {\n\t\tif (elementDisabled || element == null) {\n\t\t\treturn;\n\t\t}\n\t\tCrossOriginEmbedderPolicyHeaderWriter writer = new CrossOriginEmbedderPolicyHeaderWriter();\n\t\tElement crossOriginEmbedderPolicyElement = DomUtils.getChildElementByTagName(element,\n\t\t\t\tCROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT);\n\t\tif (crossOriginEmbedderPolicyElement != null) {\n\t\t\taddCrossOriginEmbedderPolicy(crossOriginEmbedderPolicyElement, writer);\n\t\t}\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(CrossOriginEmbedderPolicyHeaderWriter.class, () -> writer);\n\t\tthis.headerWriters.add(builder.getBeanDefinition());\n\t}\n\n\tprivate void parseCrossOriginResourcePolicy(boolean elementDisabled, Element element) {\n\t\tif (elementDisabled || element == null) {\n\t\t\treturn;\n\t\t}\n\t\tCrossOriginResourcePolicyHeaderWriter writer = new CrossOriginResourcePolicyHeaderWriter();\n\t\tElement crossOriginResourcePolicyElement = DomUtils.getChildElementByTagName(element,\n\t\t\t\tCROSS_ORIGIN_RESOURCE_POLICY_ELEMENT);\n\t\tif (crossOriginResourcePolicyElement != null) {\n\t\t\taddCrossOriginResourcePolicy(crossOriginResourcePolicyElement, writer);\n\t\t}\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(CrossOriginResourcePolicyHeaderWriter.class, () -> writer);\n\t\tthis.headerWriters.add(builder.getBeanDefinition());\n\t}\n\n\tprivate void addCrossOriginResourcePolicy(Element crossOriginResourcePolicyElement,\n\t\t\tCrossOriginResourcePolicyHeaderWriter writer) {\n\t\tString policy = crossOriginResourcePolicyElement.getAttribute(ATT_POLICY);\n\t\tif (StringUtils.hasText(policy)) {\n\t\t\twriter.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.from(policy));\n\t\t}\n\t}\n\n\tprivate void addCrossOriginEmbedderPolicy(Element crossOriginEmbedderPolicyElement,\n\t\t\tCrossOriginEmbedderPolicyHeaderWriter writer) {\n\t\tString policy = crossOriginEmbedderPolicyElement.getAttribute(ATT_POLICY);\n\t\tif (StringUtils.hasText(policy)) {\n\t\t\twriter.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.from(policy));\n\t\t}\n\t}\n\n\tprivate void addCrossOriginOpenerPolicy(Element crossOriginOpenerPolicyElement,\n\t\t\tCrossOriginOpenerPolicyHeaderWriter writer) {\n\t\tString policy = crossOriginOpenerPolicyElement.getAttribute(ATT_POLICY);\n\t\tif (StringUtils.hasText(policy)) {\n\t\t\twriter.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.from(policy));\n\t\t}\n\t}\n\n\tprivate void attrNotAllowed(ParserContext context, String attrName, String otherAttrName, Element element) {\n\t\tcontext.getReaderContext()\n\t\t\t.error(\"Only one of '\" + attrName + \"' or '\" + otherAttrName + \"' can be set.\", element);\n\t}\n\n\tprivate void parseHeaderElements(Element element) {\n\t\tList<Element> headerElts = (element != null)\n\t\t\t\t? DomUtils.getChildElementsByTagName(element, GENERIC_HEADER_ELEMENT) : Collections.emptyList();\n\t\tfor (Element headerElt : headerElts) {\n\t\t\tString headerFactoryRef = headerElt.getAttribute(ATT_REF);\n\t\t\tif (StringUtils.hasText(headerFactoryRef)) {\n\t\t\t\tthis.headerWriters.add(new RuntimeBeanReference(headerFactoryRef));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(StaticHeadersWriter.class);\n\t\t\t\tbuilder.addConstructorArgValue(headerElt.getAttribute(ATT_NAME));\n\t\t\t\tbuilder.addConstructorArgValue(headerElt.getAttribute(ATT_VALUE));\n\t\t\t\tthis.headerWriters.add(builder.getBeanDefinition());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void parseContentTypeOptionsElement(boolean addIfNotPresent, Element element) {\n\t\tElement contentTypeElt = (element != null) ? DomUtils.getChildElementByTagName(element, CONTENT_TYPE_ELEMENT)\n\t\t\t\t: null;\n\t\tboolean disabled = \"true\".equals(getAttribute(contentTypeElt, ATT_DISABLED, \"false\"));\n\t\tif (disabled) {\n\t\t\treturn;\n\t\t}\n\t\tif (addIfNotPresent || contentTypeElt != null) {\n\t\t\taddContentTypeOptions();\n\t\t}\n\t}\n\n\tprivate void addContentTypeOptions() {\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder\n\t\t\t.genericBeanDefinition(XContentTypeOptionsHeaderWriter.class);\n\t\tthis.headerWriters.add(builder.getBeanDefinition());\n\t}\n\n\tprivate void parseFrameOptionsElement(boolean addIfNotPresent, Element element, ParserContext parserContext) {\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XFrameOptionsHeaderWriter.class);\n\t\tElement frameElement = (element != null) ? DomUtils.getChildElementByTagName(element, FRAME_OPTIONS_ELEMENT)\n\t\t\t\t: null;\n\t\tif (frameElement == null) {\n\t\t\tif (addIfNotPresent) {\n\t\t\t\tthis.headerWriters.add(builder.getBeanDefinition());\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tString header = getAttribute(frameElement, ATT_POLICY, null);\n\t\tboolean disabled = \"true\".equals(getAttribute(frameElement, ATT_DISABLED, \"false\"));\n\t\tif (disabled && header != null) {\n\t\t\tthis.attrNotAllowed(parserContext, ATT_DISABLED, ATT_POLICY, frameElement);\n\t\t}\n\t\theader = StringUtils.hasText(header) ? header : \"DENY\";\n\t\tif (ALLOW_FROM.equals(header)) {\n\t\t\tparseAllowFromFrameOptionsElement(parserContext, builder, frameElement);\n\t\t}\n\t\telse {\n\t\t\tbuilder.addConstructorArgValue(header);\n\t\t}\n\t\tif (!disabled) {\n\t\t\tthis.headerWriters.add(builder.getBeanDefinition());\n\t\t}\n\t}\n\n\tprivate void parseAllowFromFrameOptionsElement(ParserContext parserContext, BeanDefinitionBuilder builder,\n\t\t\tElement frameElement) {\n\t\tString strategyRef = getAttribute(frameElement, ATT_REF, null);\n\t\tString strategy = getAttribute(frameElement, ATT_STRATEGY, null);\n\t\tif (StringUtils.hasText(strategy) && StringUtils.hasText(strategyRef)) {\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(\"Only one of 'strategy' or 'strategy-ref' can be set.\", frameElement);\n\t\t\treturn;\n\t\t}\n\t\tif (strategyRef != null) {\n\t\t\tbuilder.addConstructorArgReference(strategyRef);\n\t\t\treturn;\n\t\t}\n\t\tif (strategy == null) {\n\t\t\tparserContext.getReaderContext().error(\"One of 'strategy' and 'strategy-ref' must be set.\", frameElement);\n\t\t\treturn;\n\t\t}\n\t\tString value = getAttribute(frameElement, ATT_VALUE, null);\n\t\tif (!StringUtils.hasText(value)) {\n\t\t\tparserContext.getReaderContext().error(\"Strategy requires a 'value' to be set.\", frameElement);\n\t\t\treturn;\n\t\t}\n\t\t// static, whitelist, regexp\n\t\tif (\"static\".equals(strategy)) {\n\t\t\ttry {\n\t\t\t\tbuilder.addConstructorArgValue(new StaticAllowFromStrategy(new URI(value)));\n\t\t\t}\n\t\t\tcatch (URISyntaxException ex) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"'value' attribute doesn't represent a valid URI.\", frameElement, ex);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tBeanDefinitionBuilder allowFromStrategy = getAllowFromStrategy(strategy, value);\n\t\tString fromParameter = getAttribute(frameElement, ATT_FROM_PARAMETER, \"from\");\n\t\tallowFromStrategy.addPropertyValue(\"allowFromParameterName\", fromParameter);\n\t\tbuilder.addConstructorArgValue(allowFromStrategy.getBeanDefinition());\n\t}\n\n\tprivate BeanDefinitionBuilder getAllowFromStrategy(String strategy, String value) {\n\t\tif (\"whitelist\".equals(strategy)) {\n\t\t\tBeanDefinitionBuilder allowFromStrategy = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(WhiteListedAllowFromStrategy.class);\n\t\t\tallowFromStrategy.addConstructorArgValue(StringUtils.commaDelimitedListToSet(value));\n\t\t\treturn allowFromStrategy;\n\t\t}\n\t\tBeanDefinitionBuilder allowFromStrategy;\n\t\tallowFromStrategy = BeanDefinitionBuilder.rootBeanDefinition(RegExpAllowFromStrategy.class);\n\t\tallowFromStrategy.addConstructorArgValue(value);\n\t\treturn allowFromStrategy;\n\t}\n\n\tprivate void parseXssElement(boolean addIfNotPresent, Element element, ParserContext parserContext) {\n\t\tElement xssElt = (element != null) ? DomUtils.getChildElementByTagName(element, XSS_ELEMENT) : null;\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XXssProtectionHeaderWriter.class);\n\t\tif (xssElt != null) {\n\t\t\tboolean disabled = \"true\".equals(getAttribute(xssElt, ATT_DISABLED, \"false\"));\n\t\t\tXXssProtectionHeaderWriter.HeaderValue headerValue = XXssProtectionHeaderWriter.HeaderValue\n\t\t\t\t.from(xssElt.getAttribute(ATT_HEADER_VALUE));\n\t\t\tif (headerValue != null) {\n\t\t\t\tif (disabled) {\n\t\t\t\t\tattrNotAllowed(parserContext, ATT_HEADER_VALUE, ATT_DISABLED, xssElt);\n\t\t\t\t}\n\t\t\t\tbuilder.addPropertyValue(\"headerValue\", headerValue);\n\t\t\t}\n\t\t\tif (disabled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (addIfNotPresent || xssElt != null) {\n\t\t\tthis.headerWriters.add(builder.getBeanDefinition());\n\t\t}\n\t}\n\n\tprivate String getAttribute(Element element, String name, String defaultValue) {\n\t\tif (element == null) {\n\t\t\treturn defaultValue;\n\t\t}\n\t\tString value = element.getAttribute(name);\n\t\tif (StringUtils.hasText(value)) {\n\t\t\treturn value;\n\t\t}\n\t\treturn defaultValue;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.ServletRequest;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.parsing.CompositeComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.security.access.vote.AffirmativeBased;\nimport org.springframework.security.access.vote.AuthenticatedVoter;\nimport org.springframework.security.access.vote.RoleVoter;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.config.http.GrantedAuthorityDefaultsParserUtils.AbstractGrantedAuthorityDefaultsBeanFactory;\nimport org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.session.SessionRegistryImpl;\nimport org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.PathPatternRequestTransformer;\nimport org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;\nimport org.springframework.security.web.access.channel.ChannelProcessingFilter;\nimport org.springframework.security.web.access.channel.InsecureChannelProcessor;\nimport org.springframework.security.web.access.channel.RetryWithHttpEntryPoint;\nimport org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint;\nimport org.springframework.security.web.access.channel.SecureChannelProcessor;\nimport org.springframework.security.web.access.expression.WebExpressionVoter;\nimport org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.NullRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCacheAwareFilter;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\nimport org.springframework.security.web.session.ConcurrentSessionFilter;\nimport org.springframework.security.web.session.DisableEncodeUrlFilter;\nimport org.springframework.security.web.session.ForceEagerSessionCreationFilter;\nimport org.springframework.security.web.session.SessionManagementFilter;\nimport org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;\nimport org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;\nimport org.springframework.security.web.transport.HttpsRedirectFilter;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Stateful class which helps HttpSecurityBDP to create the configuration for the\n * &lt;http&gt; element.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.0\n */\nclass HttpConfigurationBuilder {\n\n\tprivate static final String ATT_CREATE_SESSION = \"create-session\";\n\n\tprivate static final String ATT_SESSION_FIXATION_PROTECTION = \"session-fixation-protection\";\n\n\tprivate static final String OPT_SESSION_FIXATION_NO_PROTECTION = \"none\";\n\n\tprivate static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = \"migrateSession\";\n\n\tprivate static final String OPT_CHANGE_SESSION_ID = \"changeSessionId\";\n\n\tprivate static final String ATT_AUTHENTICATION_STRATEGY_EXPLICIT_INVOCATION = \"authentication-strategy-explicit-invocation\";\n\n\tprivate static final String ATT_INVALID_SESSION_URL = \"invalid-session-url\";\n\n\tprivate static final String ATT_OBSERVATION_REGISTRY_REF = \"observation-registry-ref\";\n\n\tprivate static final String ATT_SESSION_AUTH_STRATEGY_REF = \"session-authentication-strategy-ref\";\n\n\tprivate static final String ATT_MAX_SESSIONS_REF = \"max-sessions-ref\";\n\n\tprivate static final String ATT_MAX_SESSIONS = \"max-sessions\";\n\n\tprivate static final String ATT_SESSION_AUTH_ERROR_URL = \"session-authentication-error-url\";\n\n\tprivate static final String ATT_SECURITY_CONTEXT_HOLDER_STRATEGY = \"security-context-holder-strategy-ref\";\n\n\tprivate static final String ATT_SECURITY_CONTEXT_REPOSITORY = \"security-context-repository-ref\";\n\n\tprivate static final String ATT_SECURITY_CONTEXT_EXPLICIT_SAVE = \"security-context-explicit-save\";\n\n\tprivate static final String ATT_INVALID_SESSION_STRATEGY_REF = \"invalid-session-strategy-ref\";\n\n\tprivate static final String ATT_DISABLE_URL_REWRITING = \"disable-url-rewriting\";\n\n\tprivate static final String ATT_USE_AUTHORIZATION_MGR = \"use-authorization-manager\";\n\n\tprivate static final String ATT_AUTHORIZATION_MGR = \"authorization-manager-ref\";\n\n\tprivate static final String ATT_ACCESS_MGR = \"access-decision-manager-ref\";\n\n\tprivate static final String ATT_ONCE_PER_REQUEST = \"once-per-request\";\n\n\tprivate static final String ATT_REF = \"ref\";\n\n\tprivate static final String ATT_EXPIRY_URL = \"expired-url\";\n\n\tprivate static final String ATT_EXPIRED_SESSION_STRATEGY_REF = \"expired-session-strategy-ref\";\n\n\tprivate static final String ATT_SESSION_REGISTRY_ALIAS = \"session-registry-alias\";\n\n\tprivate static final String ATT_SESSION_REGISTRY_REF = \"session-registry-ref\";\n\n\tprivate static final String ATT_SERVLET_API_PROVISION = \"servlet-api-provision\";\n\n\tprivate static final String DEF_SERVLET_API_PROVISION = \"true\";\n\n\tprivate static final String ATT_JAAS_API_PROVISION = \"jaas-api-provision\";\n\n\tprivate static final String DEF_JAAS_API_PROVISION = \"false\";\n\n\tprivate static final String REQUEST_MATCHER_BUILDER_BEAN_NAME = \"HttpConfigurationBuilder-pathPatternRequestMatcherBuilder\";\n\n\tprivate final Element httpElt;\n\n\tprivate final ParserContext pc;\n\n\tprivate final SessionCreationPolicy sessionPolicy;\n\n\tprivate final List<Element> interceptUrls;\n\n\tprivate final MatcherType matcherType;\n\n\tprivate BeanDefinition cpf;\n\n\tprivate BeanDefinition httpsRedirectFilter;\n\n\tprivate BeanDefinition securityContextPersistenceFilter;\n\n\tprivate BeanDefinition forceEagerSessionCreationFilter;\n\n\tprivate BeanMetadataElement holderStrategyRef;\n\n\tprivate BeanReference contextRepoRef;\n\n\tprivate BeanReference sessionRegistryRef;\n\n\tprivate BeanDefinition concurrentSessionFilter;\n\n\tprivate BeanDefinition webAsyncManagerFilter;\n\n\tprivate BeanDefinition requestCacheAwareFilter;\n\n\tprivate BeanReference sessionStrategyRef;\n\n\tprivate RootBeanDefinition sfpf;\n\n\tprivate BeanDefinition servApiFilter;\n\n\tprivate BeanDefinition jaasApiFilter;\n\n\tprivate final BeanReference portMapper;\n\n\tprivate BeanReference fsi;\n\n\tprivate BeanReference requestCache;\n\n\tprivate BeanDefinition addHeadersFilter;\n\n\tprivate BeanMetadataElement corsFilter;\n\n\tprivate BeanDefinition csrfFilter;\n\n\tprivate BeanDefinition disableUrlRewriteFilter;\n\n\tprivate BeanDefinition wellKnownChangePasswordRedirectFilter;\n\n\tprivate BeanMetadataElement csrfLogoutHandler;\n\n\tprivate BeanMetadataElement csrfAuthStrategy;\n\n\tprivate CsrfBeanDefinitionParser csrfParser;\n\n\tprivate BeanDefinition invalidSession;\n\n\tprivate boolean addAllAuth;\n\n\tHttpConfigurationBuilder(Element element, boolean addAllAuth, ParserContext pc, BeanReference portMapper,\n\t\t\tBeanReference authenticationManager, BeanMetadataElement observationRegistry) {\n\t\tthis.httpElt = element;\n\t\tthis.addAllAuth = addAllAuth;\n\t\tthis.pc = pc;\n\t\tthis.portMapper = portMapper;\n\t\tthis.matcherType = MatcherType.fromElementOrMvc(element);\n\t\tthis.interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);\n\t\tvalidateInterceptUrls(pc);\n\t\tString createSession = element.getAttribute(ATT_CREATE_SESSION);\n\t\tthis.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED\n\t\t\t\t: createPolicy(createSession);\n\t\tif (!this.pc.getRegistry().containsBeanDefinition(REQUEST_MATCHER_BUILDER_BEAN_NAME)) {\n\t\t\tBeanDefinitionBuilder pathPatternRequestMatcherBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PathPatternRequestMatcherBuilderFactoryBean.class);\n\t\t\tpathPatternRequestMatcherBuilder.setFallback(true);\n\t\t\tBeanDefinition bean = pathPatternRequestMatcherBuilder.getBeanDefinition();\n\t\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(bean, REQUEST_MATCHER_BUILDER_BEAN_NAME));\n\t\t}\n\t\tcreateSecurityContextHolderStrategy();\n\t\tcreateForceEagerSessionCreationFilter();\n\t\tcreateDisableEncodeUrlFilter();\n\t\tcreateCsrfFilter(observationRegistry);\n\t\tcreateSecurityPersistence();\n\t\tcreateSessionManagementFilters();\n\t\tcreateWebAsyncManagerFilter();\n\t\tcreateRequestCacheFilter();\n\t\tcreateServletApiFilter(authenticationManager);\n\t\tcreateJaasApiFilter();\n\t\tcreateChannelProcessingFilter();\n\t\tcreateHttpsRedirectFilter();\n\t\tcreateFilterSecurity(authenticationManager);\n\t\tcreateAddHeadersFilter();\n\t\tcreateCorsFilter();\n\t\tcreateWellKnownChangePasswordRedirectFilter();\n\t}\n\n\tprivate void validateInterceptUrls(ParserContext pc) {\n\t\tfor (Element element : this.interceptUrls) {\n\t\t\tif (StringUtils.hasText(element.getAttribute(HttpSecurityBeanDefinitionParser.ATT_FILTERS))) {\n\t\t\t\tString message = \"The use of \\\"filters='none'\\\" is no longer supported. Please define a\"\n\t\t\t\t\t\t+ \" separate <http> element for the pattern you want to exclude and use the attribute\"\n\t\t\t\t\t\t+ \" \\\"security='none'\\\".\";\n\t\t\t\tpc.getReaderContext().error(message, pc.extractSource(element));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate SessionCreationPolicy createPolicy(String createSession) {\n\t\tif (\"ifRequired\".equals(createSession)) {\n\t\t\treturn SessionCreationPolicy.IF_REQUIRED;\n\t\t}\n\t\tif (\"always\".equals(createSession)) {\n\t\t\treturn SessionCreationPolicy.ALWAYS;\n\t\t}\n\t\tif (\"never\".equals(createSession)) {\n\t\t\treturn SessionCreationPolicy.NEVER;\n\t\t}\n\t\tif (\"stateless\".equals(createSession)) {\n\t\t\treturn SessionCreationPolicy.STATELESS;\n\t\t}\n\t\tthrow new IllegalStateException(\n\t\t\t\t\"Cannot convert \" + createSession + \" to \" + SessionCreationPolicy.class.getName());\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\tvoid setLogoutHandlers(ManagedList logoutHandlers) {\n\t\tif (logoutHandlers != null) {\n\t\t\tif (this.concurrentSessionFilter != null) {\n\t\t\t\tthis.concurrentSessionFilter.getPropertyValues().add(\"logoutHandlers\", logoutHandlers);\n\t\t\t}\n\t\t\tif (this.servApiFilter != null) {\n\t\t\t\tthis.servApiFilter.getPropertyValues().add(\"logoutHandlers\", logoutHandlers);\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid setEntryPoint(BeanMetadataElement entryPoint) {\n\t\tif (this.servApiFilter != null) {\n\t\t\tthis.servApiFilter.getPropertyValues().add(\"authenticationEntryPoint\", entryPoint);\n\t\t}\n\t}\n\n\tvoid setAccessDeniedHandler(BeanMetadataElement accessDeniedHandler) {\n\t\tif (this.csrfParser != null) {\n\t\t\tthis.csrfParser.initAccessDeniedHandler(this.invalidSession, accessDeniedHandler);\n\t\t}\n\t}\n\n\tvoid setCsrfIgnoreRequestMatchers(List<BeanDefinition> requestMatchers) {\n\t\tif (this.csrfParser != null) {\n\t\t\tthis.csrfParser.setIgnoreCsrfRequestMatchers(requestMatchers);\n\t\t}\n\t}\n\n\t// Needed to account for placeholders\n\tstatic String createPath(String path, boolean lowerCase) {\n\t\treturn lowerCase ? path.toLowerCase(Locale.ENGLISH) : path;\n\t}\n\n\tBeanMetadataElement getSecurityContextHolderStrategyForAuthenticationFilters() {\n\t\treturn this.holderStrategyRef;\n\t}\n\n\tBeanReference getSecurityContextRepositoryForAuthenticationFilters() {\n\t\treturn (isExplicitSave()) ? this.contextRepoRef : null;\n\t}\n\n\tprivate void createSecurityPersistence() {\n\t\tcreateSecurityContextRepository();\n\t\tif (isExplicitSave()) {\n\t\t\tcreateSecurityContextHolderFilter();\n\t\t}\n\t\telse {\n\t\t\tcreateSecurityContextPersistenceFilter();\n\t\t}\n\t}\n\n\tprivate boolean isExplicitSave() {\n\t\tString explicitSaveAttr = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_EXPLICIT_SAVE);\n\t\treturn !StringUtils.hasText(explicitSaveAttr) || Boolean.parseBoolean(explicitSaveAttr);\n\t}\n\n\tprivate void createForceEagerSessionCreationFilter() {\n\t\tif (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {\n\t\t\tthis.forceEagerSessionCreationFilter = new RootBeanDefinition(ForceEagerSessionCreationFilter.class);\n\t\t}\n\t}\n\n\tprivate void createSecurityContextPersistenceFilter() {\n\t\tBeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);\n\t\tswitch (this.sessionPolicy) {\n\t\t\tcase ALWAYS:\n\t\t\t\tscpf.addPropertyValue(\"forceEagerSessionCreation\", Boolean.TRUE);\n\t\t\t\tbreak;\n\t\t\tcase NEVER:\n\t\t\t\tscpf.addPropertyValue(\"forceEagerSessionCreation\", Boolean.FALSE);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tscpf.addPropertyValue(\"forceEagerSessionCreation\", Boolean.FALSE);\n\t\t}\n\t\tscpf.addPropertyValue(\"securityContextHolderStrategy\", this.holderStrategyRef);\n\t\tscpf.addConstructorArgValue(this.contextRepoRef);\n\n\t\tthis.securityContextPersistenceFilter = scpf.getBeanDefinition();\n\t}\n\n\tprivate void createSecurityContextHolderStrategy() {\n\t\tString holderStrategyRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_HOLDER_STRATEGY);\n\t\tif (StringUtils.hasText(holderStrategyRef)) {\n\t\t\tthis.holderStrategyRef = new RuntimeBeanReference(holderStrategyRef);\n\t\t\treturn;\n\t\t}\n\t\tthis.holderStrategyRef = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderStrategyFactory.class)\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate void createSecurityContextRepository() {\n\t\tString repoRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY);\n\t\tif (!StringUtils.hasText(repoRef)) {\n\t\t\tBeanDefinitionBuilder contextRepo;\n\t\t\tif (this.sessionPolicy == SessionCreationPolicy.STATELESS) {\n\t\t\t\tcontextRepo = BeanDefinitionBuilder.rootBeanDefinition(RequestAttributeSecurityContextRepository.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcontextRepo = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSecurityContextRepository.class);\n\t\t\t\tswitch (this.sessionPolicy) {\n\t\t\t\t\tcase ALWAYS:\n\t\t\t\t\t\tcontextRepo.addPropertyValue(\"allowSessionCreation\", Boolean.TRUE);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase NEVER:\n\t\t\t\t\t\tcontextRepo.addPropertyValue(\"allowSessionCreation\", Boolean.FALSE);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcontextRepo.addPropertyValue(\"allowSessionCreation\", Boolean.TRUE);\n\t\t\t\t}\n\t\t\t\tif (isDisableUrlRewriting()) {\n\t\t\t\t\tcontextRepo.addPropertyValue(\"disableUrlRewriting\", Boolean.TRUE);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontextRepo.addPropertyValue(\"securityContextHolderStrategy\", this.holderStrategyRef);\n\t\t\tBeanDefinition repoBean = contextRepo.getBeanDefinition();\n\t\t\trepoRef = this.pc.getReaderContext().generateBeanName(repoBean);\n\t\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(repoBean, repoRef));\n\t\t}\n\n\t\tthis.contextRepoRef = new RuntimeBeanReference(repoRef);\n\t}\n\n\tprivate boolean isDisableUrlRewriting() {\n\t\tString disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);\n\t\treturn !\"false\".equals(disableUrlRewriting);\n\t}\n\n\tprivate void createSecurityContextHolderFilter() {\n\t\tBeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class);\n\t\tfilter.addPropertyValue(\"securityContextHolderStrategy\", this.holderStrategyRef);\n\t\tfilter.addConstructorArgValue(this.contextRepoRef);\n\t\tthis.securityContextPersistenceFilter = filter.getBeanDefinition();\n\t}\n\n\tprivate void createSessionManagementFilters() {\n\t\tElement sessionMgmtElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SESSION_MANAGEMENT);\n\t\tElement sessionCtrlElt = null;\n\t\tString sessionFixationAttribute = null;\n\t\tString invalidSessionUrl = null;\n\t\tString invalidSessionStrategyRef = null;\n\t\tString sessionAuthStratRef = null;\n\t\tString errorUrl = null;\n\t\tboolean sessionControlEnabled = false;\n\t\tif (sessionMgmtElt != null) {\n\t\t\tif (this.sessionPolicy == SessionCreationPolicy.STATELESS) {\n\t\t\t\tthis.pc.getReaderContext()\n\t\t\t\t\t.error(Elements.SESSION_MANAGEMENT + \"  cannot be used\" + \" in combination with \"\n\t\t\t\t\t\t\t+ ATT_CREATE_SESSION + \"='\" + SessionCreationPolicy.STATELESS + \"'\",\n\t\t\t\t\t\t\tthis.pc.extractSource(sessionMgmtElt));\n\t\t\t}\n\t\t\tsessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);\n\t\t\tinvalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);\n\t\t\tinvalidSessionStrategyRef = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_STRATEGY_REF);\n\t\t\tsessionAuthStratRef = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_STRATEGY_REF);\n\t\t\terrorUrl = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_ERROR_URL);\n\t\t\tsessionCtrlElt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);\n\t\t\tsessionControlEnabled = sessionCtrlElt != null;\n\t\t\tif (StringUtils.hasText(invalidSessionUrl) && StringUtils.hasText(invalidSessionStrategyRef)) {\n\t\t\t\tthis.pc.getReaderContext()\n\t\t\t\t\t.error(ATT_INVALID_SESSION_URL + \" attribute cannot be used in combination with\" + \" the \"\n\t\t\t\t\t\t\t+ ATT_INVALID_SESSION_STRATEGY_REF + \" attribute.\", sessionMgmtElt);\n\t\t\t}\n\t\t\tif (sessionControlEnabled) {\n\t\t\t\tif (StringUtils.hasText(sessionAuthStratRef)) {\n\t\t\t\t\tthis.pc.getReaderContext()\n\t\t\t\t\t\t.error(ATT_SESSION_AUTH_STRATEGY_REF + \" attribute cannot be used\" + \" in combination with <\"\n\t\t\t\t\t\t\t\t+ Elements.CONCURRENT_SESSIONS + \">\", this.pc.extractSource(sessionCtrlElt));\n\t\t\t\t}\n\t\t\t\tcreateConcurrencyControlFilterAndSessionRegistry(sessionCtrlElt);\n\t\t\t}\n\t\t}\n\n\t\tif (!StringUtils.hasText(sessionFixationAttribute)) {\n\t\t\tsessionFixationAttribute = OPT_CHANGE_SESSION_ID;\n\t\t}\n\t\telse if (StringUtils.hasText(sessionAuthStratRef)) {\n\t\t\tthis.pc.getReaderContext()\n\t\t\t\t.error(ATT_SESSION_FIXATION_PROTECTION + \" attribute cannot be used\" + \" in combination with \"\n\t\t\t\t\t\t+ ATT_SESSION_AUTH_STRATEGY_REF, this.pc.extractSource(sessionMgmtElt));\n\t\t}\n\n\t\tif (this.sessionPolicy == SessionCreationPolicy.STATELESS) {\n\t\t\t// SEC-1424: do nothing\n\t\t\treturn;\n\t\t}\n\t\tboolean sessionFixationProtectionRequired = !sessionFixationAttribute\n\t\t\t.equals(OPT_SESSION_FIXATION_NO_PROTECTION);\n\t\tManagedList<BeanMetadataElement> delegateSessionStrategies = new ManagedList<>();\n\t\tBeanDefinitionBuilder concurrentSessionStrategy;\n\t\tBeanDefinitionBuilder sessionFixationStrategy = null;\n\t\tBeanDefinitionBuilder registerSessionStrategy;\n\t\tif (this.csrfAuthStrategy != null) {\n\t\t\tdelegateSessionStrategies.add(this.csrfAuthStrategy);\n\t\t}\n\t\tif (sessionControlEnabled) {\n\t\t\tAssert.state(this.sessionRegistryRef != null, \"No sessionRegistryRef found\");\n\t\t\tconcurrentSessionStrategy = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(ConcurrentSessionControlAuthenticationStrategy.class);\n\t\t\tconcurrentSessionStrategy.addConstructorArgValue(this.sessionRegistryRef);\n\t\t\tString maxSessions = this.pc.getReaderContext()\n\t\t\t\t.getEnvironment()\n\t\t\t\t.resolvePlaceholders(sessionCtrlElt.getAttribute(ATT_MAX_SESSIONS));\n\t\t\tif (StringUtils.hasText(maxSessions)) {\n\t\t\t\tconcurrentSessionStrategy.addPropertyValue(\"maximumSessions\", maxSessions);\n\t\t\t}\n\t\t\tString maxSessionsRef = this.pc.getReaderContext()\n\t\t\t\t.getEnvironment()\n\t\t\t\t.resolvePlaceholders(sessionCtrlElt.getAttribute(ATT_MAX_SESSIONS_REF));\n\t\t\tif (StringUtils.hasText(maxSessionsRef)) {\n\t\t\t\tconcurrentSessionStrategy.addPropertyReference(\"maximumSessions\", maxSessionsRef);\n\t\t\t}\n\t\t\tString exceptionIfMaximumExceeded = sessionCtrlElt.getAttribute(\"error-if-maximum-exceeded\");\n\t\t\tif (StringUtils.hasText(exceptionIfMaximumExceeded)) {\n\t\t\t\tconcurrentSessionStrategy.addPropertyValue(\"exceptionIfMaximumExceeded\", exceptionIfMaximumExceeded);\n\t\t\t}\n\t\t\tdelegateSessionStrategies.add(concurrentSessionStrategy.getBeanDefinition());\n\t\t}\n\t\tboolean useChangeSessionId = OPT_CHANGE_SESSION_ID.equals(sessionFixationAttribute);\n\t\tif (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {\n\t\t\tif (useChangeSessionId) {\n\t\t\t\tsessionFixationStrategy = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(ChangeSessionIdAuthenticationStrategy.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsessionFixationStrategy = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(SessionFixationProtectionStrategy.class);\n\t\t\t}\n\t\t\tdelegateSessionStrategies.add(sessionFixationStrategy.getBeanDefinition());\n\t\t}\n\t\tif (StringUtils.hasText(sessionAuthStratRef)) {\n\t\t\tdelegateSessionStrategies.add(new RuntimeBeanReference(sessionAuthStratRef));\n\t\t}\n\t\tif (sessionControlEnabled) {\n\t\t\tregisterSessionStrategy = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(RegisterSessionAuthenticationStrategy.class);\n\t\t\tregisterSessionStrategy.addConstructorArgValue(this.sessionRegistryRef);\n\t\t\tdelegateSessionStrategies.add(registerSessionStrategy.getBeanDefinition());\n\t\t}\n\t\tif (delegateSessionStrategies.isEmpty()) {\n\t\t\tthis.sfpf = null;\n\t\t\treturn;\n\t\t}\n\t\tBeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(SessionManagementFilter.class);\n\t\tRootBeanDefinition failureHandler = new RootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);\n\t\tif (StringUtils.hasText(errorUrl)) {\n\t\t\tfailureHandler.getPropertyValues().addPropertyValue(\"defaultFailureUrl\", errorUrl);\n\t\t}\n\t\tsessionMgmtFilter.addPropertyValue(\"securityContextHolderStrategy\", this.holderStrategyRef);\n\t\tsessionMgmtFilter.addPropertyValue(\"authenticationFailureHandler\", failureHandler);\n\t\tsessionMgmtFilter.addConstructorArgValue(this.contextRepoRef);\n\t\tif (!StringUtils.hasText(sessionAuthStratRef) && sessionFixationStrategy != null && !useChangeSessionId) {\n\t\t\tif (sessionFixationProtectionRequired) {\n\t\t\t\tsessionFixationStrategy.addPropertyValue(\"migrateSessionAttributes\",\n\t\t\t\t\t\tsessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION));\n\t\t\t}\n\t\t}\n\t\tif (!delegateSessionStrategies.isEmpty()) {\n\t\t\tBeanDefinitionBuilder sessionStrategy = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(CompositeSessionAuthenticationStrategy.class);\n\t\t\tBeanDefinition strategyBean = sessionStrategy.getBeanDefinition();\n\t\t\tsessionStrategy.addConstructorArgValue(delegateSessionStrategies);\n\t\t\tsessionAuthStratRef = this.pc.getReaderContext().generateBeanName(strategyBean);\n\t\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(strategyBean, sessionAuthStratRef));\n\t\t}\n\t\tif (StringUtils.hasText(invalidSessionUrl)) {\n\t\t\tBeanDefinitionBuilder invalidSessionBldr = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(SimpleRedirectInvalidSessionStrategy.class);\n\t\t\tinvalidSessionBldr.addConstructorArgValue(invalidSessionUrl);\n\t\t\tthis.invalidSession = invalidSessionBldr.getBeanDefinition();\n\t\t\tsessionMgmtFilter.addPropertyValue(\"invalidSessionStrategy\", this.invalidSession);\n\t\t}\n\t\telse if (StringUtils.hasText(invalidSessionStrategyRef)) {\n\t\t\tsessionMgmtFilter.addPropertyReference(\"invalidSessionStrategy\", invalidSessionStrategyRef);\n\t\t}\n\t\tsessionMgmtFilter.addConstructorArgReference(sessionAuthStratRef);\n\t\tboolean registerSessionMgmtFilter = (sessionMgmtElt != null\n\t\t\t\t&& \"false\".equals(sessionMgmtElt.getAttribute(ATT_AUTHENTICATION_STRATEGY_EXPLICIT_INVOCATION)));\n\t\tif (registerSessionMgmtFilter || StringUtils.hasText(errorUrl) || StringUtils.hasText(invalidSessionUrl)\n\t\t\t\t|| StringUtils.hasText(invalidSessionStrategyRef)) {\n\t\t\tthis.sfpf = (RootBeanDefinition) sessionMgmtFilter.getBeanDefinition();\n\t\t}\n\t\tthis.sessionStrategyRef = new RuntimeBeanReference(sessionAuthStratRef);\n\t}\n\n\tprivate void createConcurrencyControlFilterAndSessionRegistry(Element element) {\n\t\tCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),\n\t\t\t\tthis.pc.extractSource(element));\n\t\tthis.pc.pushContainingComponent(compositeDef);\n\t\tBeanDefinitionRegistry beanRegistry = this.pc.getRegistry();\n\t\tString sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);\n\t\tif (!StringUtils.hasText(sessionRegistryId)) {\n\t\t\t// Register an internal SessionRegistryImpl if no external reference supplied.\n\t\t\tRootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);\n\t\t\tsessionRegistryId = this.pc.getReaderContext().registerWithGeneratedName(sessionRegistry);\n\t\t\tthis.pc.registerComponent(new BeanComponentDefinition(sessionRegistry, sessionRegistryId));\n\t\t}\n\t\tString registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);\n\t\tif (StringUtils.hasText(registryAlias)) {\n\t\t\tbeanRegistry.registerAlias(sessionRegistryId, registryAlias);\n\t\t}\n\t\tBeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);\n\t\tfilterBuilder.addConstructorArgReference(sessionRegistryId);\n\t\tObject source = this.pc.extractSource(element);\n\t\tfilterBuilder.getRawBeanDefinition().setSource(source);\n\t\tfilterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\tString expiryUrl = element.getAttribute(ATT_EXPIRY_URL);\n\t\tString expiredSessionStrategyRef = element.getAttribute(ATT_EXPIRED_SESSION_STRATEGY_REF);\n\t\tif (StringUtils.hasText(expiryUrl) && StringUtils.hasText(expiredSessionStrategyRef)) {\n\t\t\tthis.pc.getReaderContext()\n\t\t\t\t.error(\"Cannot use 'expired-url' attribute and 'expired-session-strategy-ref'\" + \" attribute together.\",\n\t\t\t\t\t\tsource);\n\t\t}\n\t\tString maxSessions = element.getAttribute(ATT_MAX_SESSIONS);\n\t\tString maxSessionsRef = element.getAttribute(ATT_MAX_SESSIONS_REF);\n\t\tif (StringUtils.hasText(maxSessions) && StringUtils.hasText(maxSessionsRef)) {\n\t\t\tthis.pc.getReaderContext()\n\t\t\t\t.error(\"Cannot use 'max-sessions' attribute and 'max-sessions-ref' attribute together.\", source);\n\t\t}\n\t\tif (StringUtils.hasText(expiryUrl)) {\n\t\t\tBeanDefinitionBuilder expiredSessionBldr = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(SimpleRedirectSessionInformationExpiredStrategy.class);\n\t\t\texpiredSessionBldr.addConstructorArgValue(expiryUrl);\n\t\t\tfilterBuilder.addConstructorArgValue(expiredSessionBldr.getBeanDefinition());\n\t\t}\n\t\telse if (StringUtils.hasText(expiredSessionStrategyRef)) {\n\t\t\tfilterBuilder.addConstructorArgReference(expiredSessionStrategyRef);\n\t\t}\n\t\tthis.pc.popAndRegisterContainingComponent();\n\t\tthis.concurrentSessionFilter = filterBuilder.getBeanDefinition();\n\t\tthis.sessionRegistryRef = new RuntimeBeanReference(sessionRegistryId);\n\t}\n\n\tprivate void createWebAsyncManagerFilter() {\n\t\tboolean asyncSupported = ClassUtils.hasMethod(ServletRequest.class, \"startAsync\");\n\t\tif (asyncSupported) {\n\t\t\tthis.webAsyncManagerFilter = new RootBeanDefinition(WebAsyncManagerIntegrationFilter.class);\n\t\t\tthis.webAsyncManagerFilter.getPropertyValues().add(\"securityContextHolderStrategy\", this.holderStrategyRef);\n\t\t}\n\t}\n\n\t// Adds the servlet-api integration filter if required\n\tprivate void createServletApiFilter(BeanReference authenticationManager) {\n\t\tString provideServletApi = this.httpElt.getAttribute(ATT_SERVLET_API_PROVISION);\n\t\tif (!StringUtils.hasText(provideServletApi)) {\n\t\t\tprovideServletApi = DEF_SERVLET_API_PROVISION;\n\t\t}\n\t\tif (\"true\".equals(provideServletApi)) {\n\t\t\tthis.servApiFilter = GrantedAuthorityDefaultsParserUtils.registerWithDefaultRolePrefix(this.pc,\n\t\t\t\t\tSecurityContextHolderAwareRequestFilterBeanFactory.class);\n\t\t\tthis.servApiFilter.getPropertyValues().add(\"authenticationManager\", authenticationManager);\n\t\t\tthis.servApiFilter.getPropertyValues().add(\"securityContextHolderStrategy\", this.holderStrategyRef);\n\t\t}\n\t}\n\n\t// Adds the jaas-api integration filter if required\n\tprivate void createJaasApiFilter() {\n\t\tString provideJaasApi = this.httpElt.getAttribute(ATT_JAAS_API_PROVISION);\n\t\tif (!StringUtils.hasText(provideJaasApi)) {\n\t\t\tprovideJaasApi = DEF_JAAS_API_PROVISION;\n\t\t}\n\t\tif (\"true\".equals(provideJaasApi)) {\n\t\t\tthis.jaasApiFilter = BeanDefinitionBuilder.rootBeanDefinition(JaasApiIntegrationFilter.class)\n\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", this.holderStrategyRef)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\t}\n\n\tprivate void createHttpsRedirectFilter() {\n\t\tString ref = this.httpElt\n\t\t\t.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF);\n\t\tif (!StringUtils.hasText(ref)) {\n\t\t\treturn;\n\t\t}\n\t\tRootBeanDefinition channelFilter = new RootBeanDefinition(HttpsRedirectFilter.class);\n\t\tchannelFilter.getPropertyValues().addPropertyValue(\"requestMatcher\", new RuntimeBeanReference(ref));\n\t\tchannelFilter.getPropertyValues().addPropertyValue(\"portMapper\", this.portMapper);\n\t\tthis.httpsRedirectFilter = channelFilter;\n\t}\n\n\t@Deprecated\n\tprivate void createChannelProcessingFilter() {\n\t\tManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();\n\t\tif (channelRequestMap.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tRootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);\n\t\tBeanDefinitionBuilder metadataSourceBldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);\n\t\tmetadataSourceBldr.addConstructorArgValue(channelRequestMap);\n\t\tchannelFilter.getPropertyValues()\n\t\t\t.addPropertyValue(\"securityMetadataSource\", metadataSourceBldr.getBeanDefinition());\n\t\tRootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);\n\t\tManagedList<RootBeanDefinition> channelProcessors = new ManagedList<>(3);\n\t\tRootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);\n\t\tRootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);\n\t\tRootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);\n\t\tretryWithHttp.getPropertyValues().addPropertyValue(\"portMapper\", this.portMapper);\n\t\tretryWithHttps.getPropertyValues().addPropertyValue(\"portMapper\", this.portMapper);\n\t\tsecureChannelProcessor.getPropertyValues().addPropertyValue(\"entryPoint\", retryWithHttps);\n\t\tRootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);\n\t\tinSecureChannelProcessor.getPropertyValues().addPropertyValue(\"entryPoint\", retryWithHttp);\n\t\tchannelProcessors.add(secureChannelProcessor);\n\t\tchannelProcessors.add(inSecureChannelProcessor);\n\t\tchannelDecisionManager.getPropertyValues().addPropertyValue(\"channelProcessors\", channelProcessors);\n\t\tString id = this.pc.getReaderContext().registerWithGeneratedName(channelDecisionManager);\n\t\tchannelFilter.getPropertyValues().addPropertyValue(\"channelDecisionManager\", new RuntimeBeanReference(id));\n\t\tthis.cpf = channelFilter;\n\t}\n\n\t/**\n\t * Parses the intercept-url elements to obtain the map used by channel security. This\n\t * will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL\n\t * path.\n\t * @deprecated please use {@link #createHttpsRedirectFilter} instead\n\t */\n\t@Deprecated\n\tprivate ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForChannelSecurity() {\n\t\tManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = new ManagedMap<>();\n\t\tfor (Element urlElt : this.interceptUrls) {\n\t\t\tString path = urlElt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_PATH_PATTERN);\n\t\t\tString method = urlElt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_HTTP_METHOD);\n\t\t\tString matcherRef = urlElt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REQUEST_MATCHER_REF);\n\t\t\tboolean hasMatcherRef = StringUtils.hasText(matcherRef);\n\t\t\tif (!hasMatcherRef && !StringUtils.hasText(path)) {\n\t\t\t\tthis.pc.getReaderContext().error(\"pattern attribute cannot be empty or null\", urlElt);\n\t\t\t}\n\t\t\tString requiredChannel = urlElt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REQUIRES_CHANNEL);\n\t\t\tif (StringUtils.hasText(requiredChannel)) {\n\t\t\t\tBeanMetadataElement matcher = hasMatcherRef ? new RuntimeBeanReference(matcherRef)\n\t\t\t\t\t\t: this.matcherType.createMatcher(this.pc, path, method);\n\t\t\t\tRootBeanDefinition channelAttributes = new RootBeanDefinition(ChannelAttributeFactory.class);\n\t\t\t\tchannelAttributes.getConstructorArgumentValues().addGenericArgumentValue(requiredChannel);\n\t\t\t\tchannelAttributes.setFactoryMethodName(\"createChannelAttributes\");\n\t\t\t\tchannelRequestMap.put(matcher, channelAttributes);\n\t\t\t}\n\t\t}\n\t\treturn channelRequestMap;\n\t}\n\n\tprivate void createRequestCacheFilter() {\n\t\tElement requestCacheElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.REQUEST_CACHE);\n\t\tif (requestCacheElt != null) {\n\t\t\tthis.requestCache = new RuntimeBeanReference(requestCacheElt.getAttribute(ATT_REF));\n\t\t}\n\t\telse {\n\t\t\tBeanDefinitionBuilder requestCacheBldr;\n\t\t\tif (this.sessionPolicy == SessionCreationPolicy.STATELESS) {\n\t\t\t\trequestCacheBldr = BeanDefinitionBuilder.rootBeanDefinition(NullRequestCache.class);\n\t\t\t}\n\t\t\telse {\n\t\t\t\trequestCacheBldr = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionRequestCache.class);\n\t\t\t\trequestCacheBldr.addPropertyValue(\"createSessionAllowed\",\n\t\t\t\t\t\tthis.sessionPolicy == SessionCreationPolicy.IF_REQUIRED);\n\t\t\t\tif (this.csrfFilter != null) {\n\t\t\t\t\tBeanDefinitionBuilder requestCacheMatcherBldr = BeanDefinitionBuilder\n\t\t\t\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class);\n\t\t\t\t\trequestCacheMatcherBldr.addConstructorArgValue(\"/**\");\n\t\t\t\t\trequestCacheMatcherBldr.addConstructorArgValue(\"GET\");\n\t\t\t\t\trequestCacheBldr.addPropertyValue(\"requestMatcher\", requestCacheMatcherBldr.getBeanDefinition());\n\t\t\t\t}\n\t\t\t}\n\t\t\tBeanDefinition bean = requestCacheBldr.getBeanDefinition();\n\t\t\tString id = this.pc.getReaderContext().generateBeanName(bean);\n\t\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(bean, id));\n\t\t\tthis.requestCache = new RuntimeBeanReference(id);\n\t\t}\n\t\tthis.requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);\n\t\tthis.requestCacheAwareFilter.getConstructorArgumentValues().addGenericArgumentValue(this.requestCache);\n\t}\n\n\tprivate void createFilterSecurity(BeanReference authManager) {\n\t\tif (StringUtils.hasText(this.httpElt.getAttribute(ATT_AUTHORIZATION_MGR))) {\n\t\t\tcreateAuthorizationFilter();\n\t\t\treturn;\n\t\t}\n\t\tboolean useAuthorizationManager = true;\n\t\tif (StringUtils.hasText(this.httpElt.getAttribute(ATT_USE_AUTHORIZATION_MGR))) {\n\t\t\tuseAuthorizationManager = Boolean.parseBoolean(this.httpElt.getAttribute(ATT_USE_AUTHORIZATION_MGR));\n\t\t}\n\t\tif (useAuthorizationManager) {\n\t\t\tcreateAuthorizationFilter();\n\t\t\treturn;\n\t\t}\n\t\tcreateFilterSecurityInterceptor(authManager);\n\t}\n\n\tprivate void createAuthorizationFilter() {\n\t\tAuthorizationFilterParser authorizationFilterParser = new AuthorizationFilterParser(this.holderStrategyRef);\n\t\tBeanDefinition fsiBean = authorizationFilterParser.parse(this.httpElt, this.pc);\n\t\tString fsiId = this.pc.getReaderContext().generateBeanName(fsiBean);\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(fsiBean, fsiId));\n\t\t// Create and register a AuthorizationManagerWebInvocationPrivilegeEvaluator for\n\t\t// use with\n\t\t// taglibs etc.\n\t\tBeanDefinitionBuilder wipeBldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(AuthorizationManagerWebInvocationPrivilegeEvaluator.class)\n\t\t\t.addConstructorArgReference(authorizationFilterParser.getAuthorizationManagerRef());\n\t\twipeBldr.addPropertyValue(\"requestTransformer\",\n\t\t\t\tnew RootBeanDefinition(PathPatternRequestTransformerFactoryBean.class));\n\t\tBeanDefinition wipe = wipeBldr.getBeanDefinition();\n\t\tthis.pc.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(wipe, this.pc.getReaderContext().generateBeanName(wipe)));\n\t\tthis.fsi = new RuntimeBeanReference(fsiId);\n\t}\n\n\tprivate void createFilterSecurityInterceptor(BeanReference authManager) {\n\t\tboolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(this.httpElt);\n\t\tRootBeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser\n\t\t\t.createSecurityMetadataSource(this.interceptUrls, this.addAllAuth, this.httpElt, this.pc);\n\t\tRootBeanDefinition accessDecisionMgr;\n\t\tManagedList<BeanDefinition> voters = new ManagedList<>(2);\n\t\tif (useExpressions) {\n\t\t\tBeanDefinitionBuilder expressionVoter = BeanDefinitionBuilder.rootBeanDefinition(WebExpressionVoter.class);\n\t\t\t// Read the expression handler from the FISMS\n\t\t\tRuntimeBeanReference expressionHandler = (RuntimeBeanReference) securityMds.getConstructorArgumentValues()\n\t\t\t\t.getArgumentValue(1, RuntimeBeanReference.class)\n\t\t\t\t.getValue();\n\t\t\texpressionVoter.addPropertyValue(\"expressionHandler\", expressionHandler);\n\t\t\tvoters.add(expressionVoter.getBeanDefinition());\n\t\t}\n\t\telse {\n\t\t\tvoters.add(GrantedAuthorityDefaultsParserUtils.registerWithDefaultRolePrefix(this.pc,\n\t\t\t\t\tRoleVoterBeanFactory.class));\n\t\t\tvoters.add(new RootBeanDefinition(AuthenticatedVoter.class));\n\t\t}\n\t\taccessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);\n\t\taccessDecisionMgr.getConstructorArgumentValues().addGenericArgumentValue(voters);\n\t\taccessDecisionMgr.setSource(this.pc.extractSource(this.httpElt));\n\t\t// Set up the access manager reference for http\n\t\tString accessManagerId = this.httpElt.getAttribute(ATT_ACCESS_MGR);\n\t\tif (!StringUtils.hasText(accessManagerId)) {\n\t\t\taccessManagerId = this.pc.getReaderContext().generateBeanName(accessDecisionMgr);\n\t\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(accessDecisionMgr, accessManagerId));\n\t\t}\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);\n\t\tbuilder.addPropertyReference(\"accessDecisionManager\", accessManagerId);\n\t\tbuilder.addPropertyValue(\"authenticationManager\", authManager);\n\t\tif (\"true\".equals(this.httpElt.getAttribute(ATT_ONCE_PER_REQUEST))) {\n\t\t\tbuilder.addPropertyValue(\"observeOncePerRequest\", Boolean.TRUE);\n\t\t}\n\t\tbuilder.addPropertyValue(\"securityMetadataSource\", securityMds);\n\t\tbuilder.addPropertyValue(\"securityContextHolderStrategy\", this.holderStrategyRef);\n\t\tBeanDefinition fsiBean = builder.getBeanDefinition();\n\t\tString fsiId = this.pc.getReaderContext().generateBeanName(fsiBean);\n\t\tthis.pc.registerBeanComponent(new BeanComponentDefinition(fsiBean, fsiId));\n\t\t// Create and register a DefaultWebInvocationPrivilegeEvaluator for use with\n\t\t// taglibs etc.\n\t\tBeanDefinition wipe = new RootBeanDefinition(DefaultWebInvocationPrivilegeEvaluator.class);\n\t\twipe.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(fsiId));\n\t\tthis.pc.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(wipe, this.pc.getReaderContext().generateBeanName(wipe)));\n\t\tthis.fsi = new RuntimeBeanReference(fsiId);\n\t}\n\n\tprivate void createAddHeadersFilter() {\n\t\tElement elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.HEADERS);\n\t\tthis.addHeadersFilter = new HeadersBeanDefinitionParser().parse(elmt, this.pc);\n\t}\n\n\tprivate void createCorsFilter() {\n\t\tElement elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.CORS);\n\t\tthis.corsFilter = new CorsBeanDefinitionParser().parse(elmt, this.pc);\n\n\t}\n\n\tprivate void createDisableEncodeUrlFilter() {\n\t\tif (isDisableUrlRewriting()) {\n\t\t\tthis.disableUrlRewriteFilter = new RootBeanDefinition(DisableEncodeUrlFilter.class);\n\t\t}\n\t}\n\n\tprivate void createCsrfFilter(BeanMetadataElement observationRegistry) {\n\t\tElement elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.CSRF);\n\t\tthis.csrfParser = new CsrfBeanDefinitionParser();\n\t\tthis.csrfParser.setObservationRegistry(observationRegistry);\n\t\tthis.csrfFilter = this.csrfParser.parse(elmt, this.pc);\n\t\tif (this.csrfFilter == null) {\n\t\t\tthis.csrfParser = null;\n\t\t\treturn;\n\t\t}\n\t\tthis.csrfAuthStrategy = this.csrfParser.getCsrfAuthenticationStrategy();\n\t\tthis.csrfLogoutHandler = this.csrfParser.getCsrfLogoutHandler();\n\t}\n\n\tprivate void createWellKnownChangePasswordRedirectFilter() {\n\t\tElement element = DomUtils.getChildElementByTagName(this.httpElt, Elements.PASSWORD_MANAGEMENT);\n\t\tif (element == null) {\n\t\t\treturn;\n\t\t}\n\t\tWellKnownChangePasswordBeanDefinitionParser parser = new WellKnownChangePasswordBeanDefinitionParser();\n\t\tthis.wellKnownChangePasswordRedirectFilter = parser.parse(element, this.pc);\n\t}\n\n\tBeanMetadataElement getCsrfLogoutHandler() {\n\t\treturn this.csrfLogoutHandler;\n\t}\n\n\tBeanReference getSessionStrategy() {\n\t\treturn this.sessionStrategyRef;\n\t}\n\n\tSessionCreationPolicy getSessionCreationPolicy() {\n\t\treturn this.sessionPolicy;\n\t}\n\n\tBeanReference getRequestCache() {\n\t\treturn this.requestCache;\n\t}\n\n\tList<OrderDecorator> getFilters() {\n\t\tList<OrderDecorator> filters = new ArrayList<>();\n\t\tif (this.forceEagerSessionCreationFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.forceEagerSessionCreationFilter,\n\t\t\t\t\tSecurityFilters.FORCE_EAGER_SESSION_FILTER));\n\t\t}\n\t\tif (this.disableUrlRewriteFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));\n\t\t}\n\t\tif (this.httpsRedirectFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.httpsRedirectFilter, SecurityFilters.HTTPS_REDIRECT_FILTER));\n\t\t}\n\t\tif (this.cpf != null) {\n\t\t\tfilters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER));\n\t\t}\n\t\tif (this.concurrentSessionFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.concurrentSessionFilter, SecurityFilters.CONCURRENT_SESSION_FILTER));\n\t\t}\n\t\tif (this.webAsyncManagerFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.webAsyncManagerFilter, SecurityFilters.WEB_ASYNC_MANAGER_FILTER));\n\t\t}\n\t\tfilters.add(new OrderDecorator(this.securityContextPersistenceFilter, SecurityFilters.SECURITY_CONTEXT_FILTER));\n\t\tif (this.servApiFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.servApiFilter, SecurityFilters.SERVLET_API_SUPPORT_FILTER));\n\t\t}\n\t\tif (this.jaasApiFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.jaasApiFilter, SecurityFilters.JAAS_API_SUPPORT_FILTER));\n\t\t}\n\t\tif (this.sfpf != null) {\n\t\t\tfilters.add(new OrderDecorator(this.sfpf, SecurityFilters.SESSION_MANAGEMENT_FILTER));\n\t\t}\n\t\tfilters.add(new OrderDecorator(this.fsi, SecurityFilters.FILTER_SECURITY_INTERCEPTOR));\n\t\tif (this.sessionPolicy != SessionCreationPolicy.STATELESS) {\n\t\t\tfilters.add(new OrderDecorator(this.requestCacheAwareFilter, SecurityFilters.REQUEST_CACHE_FILTER));\n\t\t}\n\t\tif (this.corsFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.corsFilter, SecurityFilters.CORS_FILTER));\n\t\t}\n\t\tif (this.addHeadersFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.addHeadersFilter, SecurityFilters.HEADERS_FILTER));\n\t\t}\n\t\tif (this.csrfFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.csrfFilter, SecurityFilters.CSRF_FILTER));\n\t\t}\n\t\tif (this.wellKnownChangePasswordRedirectFilter != null) {\n\t\t\tfilters.add(new OrderDecorator(this.wellKnownChangePasswordRedirectFilter,\n\t\t\t\t\tSecurityFilters.WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER));\n\t\t}\n\t\treturn filters;\n\t}\n\n\tprivate static BeanMetadataElement getObservationRegistry(Element httpElmt) {\n\t\tString holderStrategyRef = httpElmt.getAttribute(ATT_OBSERVATION_REGISTRY_REF);\n\t\tif (StringUtils.hasText(holderStrategyRef)) {\n\t\t\treturn new RuntimeBeanReference(holderStrategyRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(ObservationRegistryFactory.class).getBeanDefinition();\n\t}\n\n\tstatic class PathPatternRequestTransformerFactoryBean\n\t\t\timplements FactoryBean<AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer>,\n\t\t\tApplicationContextAware {\n\n\t\tprivate ApplicationContext applicationContext;\n\n\t\t@Override\n\t\tpublic AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer getObject()\n\t\t\t\tthrows Exception {\n\t\t\tAuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = this.applicationContext\n\t\t\t\t.getBeanProvider(\n\t\t\t\t\t\tAuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.class)\n\t\t\t\t.getIfUnique();\n\t\t\tif (requestTransformer != null) {\n\t\t\t\treturn requestTransformer;\n\t\t\t}\n\t\t\treturn new PathPatternRequestTransformer();\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\t\tthis.applicationContext = applicationContext;\n\t\t}\n\n\t}\n\n\tstatic class RoleVoterBeanFactory extends AbstractGrantedAuthorityDefaultsBeanFactory {\n\n\t\tprivate RoleVoter voter = new RoleVoter();\n\n\t\t@Override\n\t\tpublic RoleVoter getBean() {\n\t\t\tthis.voter.setRolePrefix(this.rolePrefix);\n\t\t\treturn this.voter;\n\t\t}\n\n\t}\n\n\tstatic class SecurityContextHolderAwareRequestFilterBeanFactory\n\t\t\textends GrantedAuthorityDefaultsParserUtils.AbstractGrantedAuthorityDefaultsBeanFactory {\n\n\t\tprivate SecurityContextHolderAwareRequestFilter filter = new SecurityContextHolderAwareRequestFilter();\n\n\t\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t\t.getContextHolderStrategy();\n\n\t\t@Override\n\t\tpublic SecurityContextHolderAwareRequestFilter getBean() {\n\t\t\tthis.filter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\t\tthis.filter.setRolePrefix(this.rolePrefix);\n\t\t\treturn this.filter;\n\t\t}\n\n\t\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t}\n\n\t}\n\n\tstatic class SecurityContextHolderStrategyFactory implements FactoryBean<SecurityContextHolderStrategy> {\n\n\t\t@Override\n\t\tpublic SecurityContextHolderStrategy getObject() throws Exception {\n\t\t\treturn SecurityContextHolder.getContextHolderStrategy();\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn SecurityContextHolderStrategy.class;\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryFactory implements FactoryBean<ObservationRegistry> {\n\n\t\t@Override\n\t\tpublic ObservationRegistry getObject() throws Exception {\n\t\t\treturn ObservationRegistry.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn ObservationRegistry.class;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/HttpFirewallBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.util.StringUtils;\n\n/**\n * Injects the supplied {@code HttpFirewall} bean reference into the\n * {@code FilterChainProxy}.\n *\n * @author Luke Taylor\n */\npublic class HttpFirewallBeanDefinitionParser implements BeanDefinitionParser {\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tString ref = element.getAttribute(\"ref\");\n\t\tif (!StringUtils.hasText(ref)) {\n\t\t\tpc.getReaderContext().error(\"ref attribute is required\", pc.extractSource(element));\n\t\t}\n\t\t// Ensure the FCP is registered.\n\t\tHttpSecurityBeanDefinitionParser.registerFilterChainProxyIfNecessary(pc, element);\n\t\tBeanDefinition filterChainProxy = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY);\n\t\tfilterChainProxy.getPropertyValues().addPropertyValue(\"firewall\", new RuntimeBeanReference(ref));\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.config.ListFactoryBean;\nimport org.springframework.beans.factory.config.MethodInvokingFactoryBean;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.parsing.CompositeComponentDefinition;\nimport org.springframework.beans.factory.support.AbstractBeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.core.OrderComparator;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.authentication.ObservationAuthenticationManager;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.config.authentication.AuthenticationManagerFactoryBean;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.ObservationFilterChainDecorator;\nimport org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Sets up HTTP security: filter stack and protected URLs.\n *\n * @author Luke Taylor\n * @author Ben Alex\n * @author Rob Winch\n * @since 2.0\n */\npublic class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final Log logger = LogFactory.getLog(HttpSecurityBeanDefinitionParser.class);\n\n\tprivate static final String ATT_AUTHENTICATION_MANAGER_REF = \"authentication-manager-ref\";\n\n\tprivate static final String ATT_OBSERVATION_REGISTRY_REF = \"observation-registry-ref\";\n\n\tstatic final String ATT_REQUEST_MATCHER_REF = \"request-matcher-ref\";\n\n\tstatic final String ATT_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF = \"redirect-to-https-request-matcher-ref\";\n\n\tstatic final String ATT_PATH_PATTERN = \"pattern\";\n\n\tstatic final String ATT_HTTP_METHOD = \"method\";\n\n\tstatic final String ATT_FILTERS = \"filters\";\n\n\tstatic final String OPT_FILTERS_NONE = \"none\";\n\n\tstatic final String ATT_REQUIRES_CHANNEL = \"requires-channel\";\n\n\tprivate static final String ATT_REF = \"ref\";\n\n\tprivate static final String ATT_SECURED = \"security\";\n\n\tprivate static final String OPT_SECURITY_NONE = \"none\";\n\n\tprivate static final String ATT_AFTER = \"after\";\n\n\tprivate static final String ATT_BEFORE = \"before\";\n\n\tprivate static final String ATT_POSITION = \"position\";\n\n\tpublic HttpSecurityBeanDefinitionParser() {\n\t}\n\n\t/**\n\t * The aim of this method is to build the list of filters which have been defined by\n\t * the namespace elements and attributes within the &lt;http&gt; configuration, along\n\t * with any custom-filter's linked to user-defined filter beans.\n\t * <p>\n\t * By the end of this method, the default <tt>FilterChainProxy</tt> bean should have\n\t * been registered and will have the map of filter chains defined, with the\n\t * \"universal\" match pattern mapped to the list of beans which have been parsed here.\n\t */\n\t@SuppressWarnings({ \"unchecked\" })\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),\n\t\t\t\tpc.extractSource(element));\n\t\tpc.pushContainingComponent(compositeDef);\n\t\tregisterFilterChainProxyIfNecessary(pc, element);\n\t\t// Obtain the filter chains and add the new chain to it\n\t\tBeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAINS);\n\t\tList<BeanReference> filterChains = (List<BeanReference>) listFactoryBean.getPropertyValues()\n\t\t\t.getPropertyValue(\"sourceList\")\n\t\t\t.getValue();\n\t\tfilterChains.add(createFilterChain(element, pc));\n\t\tpc.popAndRegisterContainingComponent();\n\t\treturn null;\n\t}\n\n\t/**\n\t * Creates the {@code SecurityFilterChain} bean from an &lt;http&gt; element.\n\t */\n\tprivate BeanReference createFilterChain(Element element, ParserContext pc) {\n\t\tboolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED));\n\t\tif (!secured) {\n\t\t\tvalidateSecuredFilterChainElement(element, pc);\n\t\t\tfor (int i = 0; i < element.getChildNodes().getLength(); i++) {\n\t\t\t\tif (element.getChildNodes().item(i) instanceof Element) {\n\t\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t\t.error(\"If you are using <http> to define an unsecured pattern, \"\n\t\t\t\t\t\t\t\t+ \"it cannot contain child elements.\", pc.extractSource(element));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn createSecurityFilterChainBean(element, pc, Collections.emptyList());\n\t\t}\n\t\tBeanReference portMapper = createPortMapper(element, pc);\n\t\tManagedList<BeanReference> authenticationProviders = new ManagedList<>();\n\t\tBeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders);\n\t\tboolean forceAutoConfig = isDefaultHttpConfig(element);\n\t\tBeanMetadataElement observationRegistry = getObservationRegistry(element);\n\t\tHttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper,\n\t\t\t\tauthenticationManager, observationRegistry);\n\t\thttpBldr.getSecurityContextRepositoryForAuthenticationFilters();\n\t\tAuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc,\n\t\t\t\thttpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,\n\t\t\t\thttpBldr.getSecurityContextHolderStrategyForAuthenticationFilters(),\n\t\t\t\thttpBldr.getSecurityContextRepositoryForAuthenticationFilters(), httpBldr.getSessionStrategy(),\n\t\t\t\tportMapper, httpBldr.getCsrfLogoutHandler());\n\t\thttpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());\n\t\thttpBldr.setEntryPoint(authBldr.getEntryPointBean());\n\t\thttpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());\n\t\thttpBldr.setCsrfIgnoreRequestMatchers(authBldr.getCsrfIgnoreRequestMatchers());\n\t\tauthenticationProviders.addAll(authBldr.getProviders());\n\t\tList<OrderDecorator> unorderedFilterChain = new ArrayList<>();\n\t\tunorderedFilterChain.addAll(httpBldr.getFilters());\n\t\tunorderedFilterChain.addAll(authBldr.getFilters());\n\t\tunorderedFilterChain.addAll(buildCustomFilterList(element, pc));\n\t\tunorderedFilterChain.sort(new OrderComparator());\n\t\tcheckFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));\n\t\t// The list of filter beans\n\t\tList<BeanMetadataElement> filterChain = new ManagedList<>();\n\t\tfor (OrderDecorator od : unorderedFilterChain) {\n\t\t\tfilterChain.add(od.bean);\n\t\t}\n\t\treturn createSecurityFilterChainBean(element, pc, filterChain);\n\t}\n\n\tprivate void validateSecuredFilterChainElement(Element element, ParserContext pc) {\n\t\tif (!StringUtils.hasText(element.getAttribute(ATT_PATH_PATTERN))\n\t\t\t\t&& !StringUtils.hasText(ATT_REQUEST_MATCHER_REF)) {\n\t\t\tString message = \"The '\" + ATT_SECURED + \"' attribute must be used in combination with\" + \" the '\"\n\t\t\t\t\t+ ATT_PATH_PATTERN + \"' or '\" + ATT_REQUEST_MATCHER_REF + \"' attributes.\";\n\t\t\tpc.getReaderContext().error(message, pc.extractSource(element));\n\t\t}\n\t}\n\n\tprivate static boolean isDefaultHttpConfig(Element httpElt) {\n\t\treturn httpElt.getChildNodes().getLength() == 0 && httpElt.getAttributes().getLength() == 0;\n\t}\n\n\tprivate BeanReference createSecurityFilterChainBean(Element element, ParserContext pc, List<?> filterChain) {\n\t\tBeanMetadataElement filterChainMatcher;\n\t\tString requestMatcherRef = element.getAttribute(ATT_REQUEST_MATCHER_REF);\n\t\tString filterChainPattern = element.getAttribute(ATT_PATH_PATTERN);\n\t\tif (StringUtils.hasText(requestMatcherRef)) {\n\t\t\tif (StringUtils.hasText(filterChainPattern)) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(\"You can't define a pattern and a request-matcher-ref for the \" + \"same filter chain\",\n\t\t\t\t\t\t\tpc.extractSource(element));\n\t\t\t}\n\t\t\tfilterChainMatcher = new RuntimeBeanReference(requestMatcherRef);\n\n\t\t}\n\t\telse if (StringUtils.hasText(filterChainPattern)) {\n\t\t\tfilterChainMatcher = MatcherType.fromElementOrMvc(element).createMatcher(pc, filterChainPattern, null);\n\t\t}\n\t\telse {\n\t\t\tfilterChainMatcher = new RootBeanDefinition(AnyRequestMatcher.class);\n\t\t}\n\t\tBeanDefinitionBuilder filterChainBldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(DefaultSecurityFilterChain.class);\n\t\tfilterChainBldr.addConstructorArgValue(filterChainMatcher);\n\t\tfilterChainBldr.addConstructorArgValue(filterChain);\n\t\tBeanDefinition filterChainBean = filterChainBldr.getBeanDefinition();\n\t\tString id = element.getAttribute(\"name\");\n\t\tif (!StringUtils.hasText(id)) {\n\t\t\tid = element.getAttribute(\"id\");\n\t\t\tif (!StringUtils.hasText(id)) {\n\t\t\t\tid = pc.getReaderContext().generateBeanName(filterChainBean);\n\t\t\t}\n\t\t}\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(filterChainBean, id));\n\t\treturn new RuntimeBeanReference(id);\n\t}\n\n\tprivate BeanReference createPortMapper(Element elt, ParserContext pc) {\n\t\t// Register the portMapper. A default will always be created, even if no element\n\t\t// exists.\n\t\tBeanDefinition portMapper = new PortMappingsBeanDefinitionParser()\n\t\t\t.parse(DomUtils.getChildElementByTagName(elt, Elements.PORT_MAPPINGS), pc);\n\t\tString portMapperName = pc.getReaderContext().generateBeanName(portMapper);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(portMapper, portMapperName));\n\t\treturn new RuntimeBeanReference(portMapperName);\n\t}\n\n\t/**\n\t * Creates the internal AuthenticationManager bean which uses either the externally\n\t * registered (global) one as a parent or the bean specified by\n\t * \"authentication-manager-ref\".\n\t *\n\t * All the providers registered by this &lt;http&gt; block will be registered with the\n\t * internal authentication manager.\n\t */\n\tprivate BeanReference createAuthenticationManager(Element element, ParserContext pc,\n\t\t\tManagedList<BeanReference> authenticationProviders) {\n\t\tString parentMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);\n\t\tBeanDefinitionBuilder authManager = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(ChildAuthenticationManagerFactoryBean.class);\n\t\tauthManager.addConstructorArgValue(authenticationProviders);\n\t\tif (StringUtils.hasText(parentMgrRef)) {\n\t\t\tRuntimeBeanReference parentAuthManager = new RuntimeBeanReference(parentMgrRef);\n\t\t\tauthManager.addConstructorArgValue(parentAuthManager);\n\t\t\tRootBeanDefinition clearCredentials = new RootBeanDefinition(\n\t\t\t\t\tClearCredentialsMethodInvokingFactoryBean.class);\n\t\t\tclearCredentials.getPropertyValues().addPropertyValue(\"targetObject\", parentAuthManager);\n\t\t\tclearCredentials.getPropertyValues()\n\t\t\t\t.addPropertyValue(\"targetMethod\", \"isEraseCredentialsAfterAuthentication\");\n\t\t\tauthManager.addPropertyValue(\"eraseCredentialsAfterAuthentication\", clearCredentials);\n\t\t}\n\t\telse {\n\t\t\tRootBeanDefinition amfb = new RootBeanDefinition(AuthenticationManagerFactoryBean.class);\n\t\t\tamfb.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\t\tString amfbId = pc.getReaderContext().generateBeanName(amfb);\n\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(amfb, amfbId));\n\t\t\tRootBeanDefinition clearCredentials = new RootBeanDefinition(MethodInvokingFactoryBean.class);\n\t\t\tclearCredentials.getPropertyValues().addPropertyValue(\"targetObject\", new RuntimeBeanReference(amfbId));\n\t\t\tclearCredentials.getPropertyValues()\n\t\t\t\t.addPropertyValue(\"targetMethod\", \"isEraseCredentialsAfterAuthentication\");\n\t\t\tauthManager.addConstructorArgValue(new RuntimeBeanReference(amfbId));\n\t\t\tauthManager.addPropertyValue(\"eraseCredentialsAfterAuthentication\", clearCredentials);\n\t\t}\n\t\t// gh-6009\n\t\tauthManager.addPropertyValue(\"authenticationEventPublisher\",\n\t\t\t\tnew RootBeanDefinition(DefaultAuthenticationEventPublisher.class));\n\t\tauthManager.addPropertyValue(\"observationRegistry\", getObservationRegistry(element));\n\t\tauthManager.getRawBeanDefinition().setSource(pc.extractSource(element));\n\t\tBeanDefinition authMgrBean = authManager.getBeanDefinition();\n\t\tString id = pc.getReaderContext().generateBeanName(authMgrBean);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(authMgrBean, id));\n\t\treturn new RuntimeBeanReference(id);\n\t}\n\n\tprivate void checkFilterChainOrder(List<OrderDecorator> filters, ParserContext pc, Object source) {\n\t\tlogger.info(\"Checking sorted filter chain: \" + filters);\n\t\tfor (int i = 0; i < filters.size(); i++) {\n\t\t\tOrderDecorator filter = filters.get(i);\n\t\t\tif (i > 0) {\n\t\t\t\tOrderDecorator previous = filters.get(i - 1);\n\t\t\t\tif (filter.getOrder() == previous.getOrder()) {\n\t\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t\t.error(\"Filter beans '\" + filter.bean + \"' and '\" + previous.bean\n\t\t\t\t\t\t\t\t+ \"' have the same 'order' value. When using custom filters, \"\n\t\t\t\t\t\t\t\t+ \"please make sure the positions do not conflict with default filters. \"\n\t\t\t\t\t\t\t\t+ \"Alternatively you can disable the default filters by removing the corresponding \"\n\t\t\t\t\t\t\t\t+ \"child elements from <http> and avoiding the use of <http auto-config='true'>.\",\n\t\t\t\t\t\t\t\tsource);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tList<OrderDecorator> buildCustomFilterList(Element element, ParserContext pc) {\n\t\tList<Element> customFilterElts = DomUtils.getChildElementsByTagName(element, Elements.CUSTOM_FILTER);\n\t\tList<OrderDecorator> customFilters = new ArrayList<>();\n\t\tfor (Element elt : customFilterElts) {\n\t\t\tString after = elt.getAttribute(ATT_AFTER);\n\t\t\tString before = elt.getAttribute(ATT_BEFORE);\n\t\t\tString position = elt.getAttribute(ATT_POSITION);\n\t\t\tString ref = elt.getAttribute(ATT_REF);\n\t\t\tif (!StringUtils.hasText(ref)) {\n\t\t\t\tpc.getReaderContext().error(\"The '\" + ATT_REF + \"' attribute must be supplied\", pc.extractSource(elt));\n\t\t\t}\n\t\t\tRuntimeBeanReference bean = new RuntimeBeanReference(ref);\n\t\t\tif (WebConfigUtils.countNonEmpty(new String[] { after, before, position }) != 1) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(\"A single '\" + ATT_AFTER + \"', '\" + ATT_BEFORE + \"', or '\" + ATT_POSITION\n\t\t\t\t\t\t\t+ \"' attribute must be supplied\", pc.extractSource(elt));\n\t\t\t}\n\t\t\tif (StringUtils.hasText(position)) {\n\t\t\t\tcustomFilters.add(new OrderDecorator(bean, SecurityFilters.valueOf(position)));\n\t\t\t}\n\t\t\telse if (StringUtils.hasText(after)) {\n\t\t\t\tSecurityFilters order = SecurityFilters.valueOf(after);\n\t\t\t\tif (order == SecurityFilters.LAST) {\n\t\t\t\t\tcustomFilters.add(new OrderDecorator(bean, SecurityFilters.LAST));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcustomFilters.add(new OrderDecorator(bean, order.getOrder() + 1));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (StringUtils.hasText(before)) {\n\t\t\t\tSecurityFilters order = SecurityFilters.valueOf(before);\n\t\t\t\tif (order == SecurityFilters.FIRST) {\n\t\t\t\t\tcustomFilters.add(new OrderDecorator(bean, SecurityFilters.FIRST));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcustomFilters.add(new OrderDecorator(bean, order.getOrder() - 1));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn customFilters;\n\t}\n\n\tstatic void registerFilterChainProxyIfNecessary(ParserContext pc, Element element) {\n\t\tObject source = pc.extractSource(element);\n\t\tBeanDefinitionRegistry registry = pc.getRegistry();\n\t\tif (registry.containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {\n\t\t\treturn;\n\t\t}\n\t\t// Not already registered, so register the list of filter chains and the\n\t\t// FilterChainProxy\n\t\tBeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);\n\t\tlistFactoryBean.getPropertyValues().add(\"sourceList\", new ManagedList<>());\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, BeanIds.FILTER_CHAINS));\n\t\tBeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);\n\t\tfcpBldr.getRawBeanDefinition().setSource(source);\n\t\tfcpBldr.addConstructorArgReference(BeanIds.FILTER_CHAINS);\n\t\tfcpBldr.addPropertyValue(\"filterChainValidator\", new RootBeanDefinition(DefaultFilterChainValidator.class));\n\t\tBeanDefinition filterChainDecorator = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(FilterChainDecoratorFactory.class)\n\t\t\t.addPropertyValue(\"observationRegistry\", getObservationRegistry(element))\n\t\t\t.getBeanDefinition();\n\t\tfcpBldr.addPropertyValue(\"filterChainDecorator\", filterChainDecorator);\n\t\tBeanDefinition fcpBean = fcpBldr.getBeanDefinition();\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));\n\t\tregistry.registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);\n\t\tBeanDefinitionBuilder requestRejected = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestRejectedHandlerPostProcessor.class);\n\t\trequestRejected.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\trequestRejected.addConstructorArgValue(\"requestRejectedHandler\");\n\t\trequestRejected.addConstructorArgValue(BeanIds.FILTER_CHAIN_PROXY);\n\t\trequestRejected.addConstructorArgValue(\"requestRejectedHandler\");\n\t\trequestRejected.addPropertyValue(\"observationRegistry\", getObservationRegistry(element));\n\t\tAbstractBeanDefinition requestRejectedBean = requestRejected.getBeanDefinition();\n\t\tString requestRejectedPostProcessorName = pc.getReaderContext().generateBeanName(requestRejectedBean);\n\t\tregistry.registerBeanDefinition(requestRejectedPostProcessorName, requestRejectedBean);\n\t}\n\n\tprivate static BeanMetadataElement getObservationRegistry(Element methodSecurityElmt) {\n\t\tString holderStrategyRef = methodSecurityElmt.getAttribute(ATT_OBSERVATION_REGISTRY_REF);\n\t\tif (StringUtils.hasText(holderStrategyRef)) {\n\t\t\treturn new RuntimeBeanReference(holderStrategyRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(ObservationRegistryFactory.class).getBeanDefinition();\n\t}\n\n\tpublic static class RequestRejectedHandlerPostProcessor implements BeanDefinitionRegistryPostProcessor {\n\n\t\tprivate final String beanName;\n\n\t\tprivate final String targetBeanName;\n\n\t\tprivate final String targetPropertyName;\n\n\t\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\t\tRequestRejectedHandlerPostProcessor(String beanName, String targetBeanName, String targetPropertyName) {\n\t\t\tthis.beanName = beanName;\n\t\t\tthis.targetBeanName = targetBeanName;\n\t\t\tthis.targetPropertyName = targetPropertyName;\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\t\tif (registry.containsBeanDefinition(this.beanName)) {\n\t\t\t\tBeanDefinition beanDefinition = registry.getBeanDefinition(this.targetBeanName);\n\t\t\t\tbeanDefinition.getPropertyValues()\n\t\t\t\t\t.add(this.targetPropertyName, new RuntimeBeanReference(this.beanName));\n\t\t\t}\n\t\t\telse if (!this.observationRegistry.isNoop()) {\n\t\t\t\tBeanDefinition observable = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(ObservationMarkingRequestRejectedHandler.class)\n\t\t\t\t\t.addConstructorArgValue(this.observationRegistry)\n\t\t\t\t\t.getBeanDefinition();\n\t\t\t\tBeanDefinition beanDefinition = registry.getBeanDefinition(this.targetBeanName);\n\t\t\t\tbeanDefinition.getPropertyValues().add(this.targetPropertyName, observable);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\n\t\t}\n\n\t\tpublic void setObservationRegistry(ObservationRegistry registry) {\n\t\t\tthis.observationRegistry = registry;\n\t\t}\n\n\t}\n\n\t/**\n\t * Custom {@link MethodInvokingFactoryBean} that is specifically used for looking up\n\t * the child {@link ProviderManager} value for\n\t * {@link ProviderManager#setEraseCredentialsAfterAuthentication(boolean)} given the\n\t * parent {@link AuthenticationManager}. This is necessary because the parent\n\t * {@link AuthenticationManager} might not be a {@link ProviderManager}.\n\t *\n\t * @author Rob Winch\n\t */\n\tstatic final class ClearCredentialsMethodInvokingFactoryBean extends MethodInvokingFactoryBean {\n\n\t\t@Override\n\t\tpublic void afterPropertiesSet() throws Exception {\n\t\t\tboolean isTargetProviderManager = getTargetObject() instanceof ProviderManager;\n\t\t\tif (!isTargetProviderManager) {\n\t\t\t\tsetTargetObject(this);\n\t\t\t}\n\t\t\tsuper.afterPropertiesSet();\n\t\t}\n\n\t\t/**\n\t\t * The default value if the target object is not a ProviderManager is false. We\n\t\t * use false because this feature is associated with {@link ProviderManager} not\n\t\t * {@link AuthenticationManager}. If the user wants to leverage\n\t\t * {@link ProviderManager#setEraseCredentialsAfterAuthentication(boolean)} their\n\t\t * original {@link AuthenticationManager} must be a {@link ProviderManager} (we\n\t\t * should not magically add this functionality to their implementation since we\n\t\t * cannot determine if it should be on or off).\n\t\t * @return\n\t\t */\n\t\tboolean isEraseCredentialsAfterAuthentication() {\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n\tpublic static final class ChildAuthenticationManagerFactoryBean implements FactoryBean<AuthenticationManager> {\n\n\t\tprivate final ProviderManager delegate;\n\n\t\tprivate AuthenticationEventPublisher authenticationEventPublisher = new DefaultAuthenticationEventPublisher();\n\n\t\tprivate boolean eraseCredentialsAfterAuthentication = true;\n\n\t\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\t\tpublic ChildAuthenticationManagerFactoryBean(List<AuthenticationProvider> providers,\n\t\t\t\tAuthenticationManager parent) {\n\t\t\tthis.delegate = new ProviderManager(providers, parent);\n\t\t}\n\n\t\t@Override\n\t\tpublic AuthenticationManager getObject() throws Exception {\n\t\t\tthis.delegate.setAuthenticationEventPublisher(this.authenticationEventPublisher);\n\t\t\tthis.delegate.setEraseCredentialsAfterAuthentication(this.eraseCredentialsAfterAuthentication);\n\t\t\tif (!this.observationRegistry.isNoop()) {\n\t\t\t\treturn new ObservationAuthenticationManager(this.observationRegistry, this.delegate);\n\t\t\t}\n\t\t\treturn this.delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn AuthenticationManager.class;\n\t\t}\n\n\t\tpublic void setEraseCredentialsAfterAuthentication(boolean eraseCredentialsAfterAuthentication) {\n\t\t\tthis.eraseCredentialsAfterAuthentication = eraseCredentialsAfterAuthentication;\n\t\t}\n\n\t\tpublic void setAuthenticationEventPublisher(AuthenticationEventPublisher authenticationEventPublisher) {\n\t\t\tthis.authenticationEventPublisher = authenticationEventPublisher;\n\t\t}\n\n\t\tpublic void setObservationRegistry(ObservationRegistry observationRegistry) {\n\t\t\tthis.observationRegistry = observationRegistry;\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryFactory implements FactoryBean<ObservationRegistry> {\n\n\t\t@Override\n\t\tpublic ObservationRegistry getObject() throws Exception {\n\t\t\treturn ObservationRegistry.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn ObservationRegistry.class;\n\t\t}\n\n\t}\n\n\tpublic static final class FilterChainDecoratorFactory\n\t\t\timplements FactoryBean<FilterChainProxy.FilterChainDecorator> {\n\n\t\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\t\t@Override\n\t\tpublic FilterChainProxy.FilterChainDecorator getObject() throws Exception {\n\t\t\tif (this.observationRegistry.isNoop()) {\n\t\t\t\treturn new FilterChainProxy.VirtualFilterChainDecorator();\n\t\t\t}\n\t\t\treturn new ObservationFilterChainDecorator(this.observationRegistry);\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn FilterChainProxy.FilterChainDecorator.class;\n\t\t}\n\n\t\tpublic void setObservationRegistry(ObservationRegistry registry) {\n\t\t\tthis.observationRegistry = registry;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Luke Taylor\n * @author Ben Alex\n * @author Onur Kagan Ozcan\n */\nclass LogoutBeanDefinitionParser implements BeanDefinitionParser {\n\n\tstatic final String ATT_LOGOUT_SUCCESS_URL = \"logout-success-url\";\n\n\tstatic final String ATT_INVALIDATE_SESSION = \"invalidate-session\";\n\n\tstatic final String ATT_LOGOUT_URL = \"logout-url\";\n\n\tstatic final String DEF_LOGOUT_URL = \"/logout\";\n\n\tstatic final String ATT_LOGOUT_HANDLER = \"success-handler-ref\";\n\n\tstatic final String ATT_DELETE_COOKIES = \"delete-cookies\";\n\n\tfinal String rememberMeServices;\n\n\tprivate final String defaultLogoutUrl;\n\n\tprivate ManagedList<BeanMetadataElement> logoutHandlers = new ManagedList<>();\n\n\tprivate boolean csrfEnabled;\n\n\tprivate BeanMetadataElement logoutSuccessHandler;\n\n\tprivate BeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef;\n\n\tLogoutBeanDefinitionParser(String loginPageUrl, String rememberMeServices, BeanMetadataElement csrfLogoutHandler,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tthis.defaultLogoutUrl = loginPageUrl + \"?logout\";\n\t\tthis.rememberMeServices = rememberMeServices;\n\t\tthis.csrfEnabled = csrfLogoutHandler != null;\n\t\tif (this.csrfEnabled) {\n\t\t\tthis.logoutHandlers.add(csrfLogoutHandler);\n\t\t}\n\t\tthis.authenticationFilterSecurityContextHolderStrategyRef = authenticationFilterSecurityContextHolderStrategyRef;\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tString logoutUrl = null;\n\t\tString successHandlerRef = null;\n\t\tString logoutSuccessUrl = null;\n\t\tString invalidateSession = null;\n\t\tString deleteCookies = null;\n\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class);\n\t\tif (element != null) {\n\t\t\tObject source = pc.extractSource(element);\n\t\t\tbuilder.getRawBeanDefinition().setSource(source);\n\t\t\tlogoutUrl = element.getAttribute(ATT_LOGOUT_URL);\n\t\t\tsuccessHandlerRef = element.getAttribute(ATT_LOGOUT_HANDLER);\n\t\t\tWebConfigUtils.validateHttpRedirect(logoutUrl, pc, source);\n\t\t\tlogoutSuccessUrl = element.getAttribute(ATT_LOGOUT_SUCCESS_URL);\n\t\t\tWebConfigUtils.validateHttpRedirect(logoutSuccessUrl, pc, source);\n\t\t\tinvalidateSession = element.getAttribute(ATT_INVALIDATE_SESSION);\n\t\t\tdeleteCookies = element.getAttribute(ATT_DELETE_COOKIES);\n\t\t}\n\t\tif (!StringUtils.hasText(logoutUrl)) {\n\t\t\tlogoutUrl = DEF_LOGOUT_URL;\n\t\t}\n\t\tbuilder.addPropertyValue(\"logoutRequestMatcher\", getLogoutRequestMatcher(logoutUrl));\n\t\tif (StringUtils.hasText(successHandlerRef)) {\n\t\t\tif (StringUtils.hasText(logoutSuccessUrl)) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(\"Use \" + ATT_LOGOUT_SUCCESS_URL + \" or \" + ATT_LOGOUT_HANDLER + \", but not both\",\n\t\t\t\t\t\t\tpc.extractSource(element));\n\t\t\t}\n\t\t\tbuilder.addConstructorArgReference(successHandlerRef);\n\t\t\tthis.logoutSuccessHandler = new RuntimeBeanReference(successHandlerRef);\n\t\t}\n\t\telse {\n\t\t\t// Use the logout URL if no handler set\n\t\t\tif (!StringUtils.hasText(logoutSuccessUrl)) {\n\t\t\t\tlogoutSuccessUrl = this.defaultLogoutUrl;\n\t\t\t}\n\t\t\tbuilder.addConstructorArgValue(logoutSuccessUrl);\n\t\t}\n\t\tBeanDefinition sclh = new RootBeanDefinition(SecurityContextLogoutHandler.class);\n\t\tsclh.getPropertyValues().addPropertyValue(\"invalidateHttpSession\", !\"false\".equals(invalidateSession));\n\t\tthis.logoutHandlers.add(sclh);\n\t\tif (this.rememberMeServices != null) {\n\t\t\tthis.logoutHandlers.add(new RuntimeBeanReference(this.rememberMeServices));\n\t\t}\n\t\tif (StringUtils.hasText(deleteCookies)) {\n\t\t\tBeanDefinition cookieDeleter = new RootBeanDefinition(CookieClearingLogoutHandler.class);\n\t\t\tString[] names = StringUtils.tokenizeToStringArray(deleteCookies, \",\");\n\t\t\tcookieDeleter.getConstructorArgumentValues().addGenericArgumentValue(names);\n\t\t\tthis.logoutHandlers.add(cookieDeleter);\n\t\t}\n\t\tthis.logoutHandlers.add(new RootBeanDefinition(LogoutSuccessEventPublishingLogoutHandler.class));\n\t\tbuilder.addConstructorArgValue(this.logoutHandlers);\n\t\tbuilder.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\tthis.authenticationFilterSecurityContextHolderStrategyRef);\n\t\treturn builder.getBeanDefinition();\n\t}\n\n\tprivate BeanDefinition getLogoutRequestMatcher(String logoutUrl) {\n\t\tBeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class);\n\t\tmatcherBuilder.addConstructorArgValue(logoutUrl);\n\t\tif (this.csrfEnabled) {\n\t\t\tmatcherBuilder.addConstructorArgValue(\"POST\");\n\t\t}\n\t\treturn matcherBuilder.getBeanDefinition();\n\t}\n\n\tManagedList<BeanMetadataElement> getLogoutHandlers() {\n\t\treturn this.logoutHandlers;\n\t}\n\n\tBeanMetadataElement getLogoutSuccessHandler() {\n\t\treturn this.logoutSuccessHandler;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/MatcherType.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RegexRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.StringUtils;\n\n/**\n * Defines the {@link RequestMatcher} types supported by the namespace.\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic enum MatcherType {\n\n\tpath(PathPatternRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(RegexRequestMatcher.class);\n\n\tprivate static final String ATT_MATCHER_TYPE = \"request-matcher\";\n\n\tfinal Class<? extends RequestMatcher> type;\n\n\tMatcherType(Class<? extends RequestMatcher> type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic BeanDefinition createMatcher(ParserContext pc, String path, String method) {\n\t\treturn createMatcher(pc, path, method, null);\n\t}\n\n\tpublic BeanDefinition createMatcher(ParserContext pc, String path, String method, String servletPath) {\n\t\tif ((\"/**\".equals(path) || \"**\".equals(path)) && method == null) {\n\t\t\treturn new RootBeanDefinition(AnyRequestMatcher.class);\n\t\t}\n\t\tBeanDefinitionBuilder matcherBldr;\n\t\tif (this == MatcherType.path) {\n\t\t\tmatcherBldr = BeanDefinitionBuilder.rootBeanDefinition(PathPatternRequestMatcherFactoryBean.class);\n\t\t\tmatcherBldr.addConstructorArgValue(path);\n\t\t\tmatcherBldr.addPropertyValue(\"basePath\", servletPath);\n\t\t}\n\t\telse {\n\t\t\tmatcherBldr = BeanDefinitionBuilder.rootBeanDefinition(this.type);\n\t\t\tmatcherBldr.addConstructorArgValue(path);\n\t\t}\n\t\tmatcherBldr.addConstructorArgValue(method);\n\t\tif (this == ciRegex) {\n\t\t\tmatcherBldr.addConstructorArgValue(true);\n\t\t}\n\t\treturn matcherBldr.getBeanDefinition();\n\t}\n\n\tstatic MatcherType fromElement(Element elt) {\n\t\tif (StringUtils.hasText(elt.getAttribute(ATT_MATCHER_TYPE))) {\n\t\t\treturn valueOf(elt.getAttribute(ATT_MATCHER_TYPE));\n\t\t}\n\n\t\treturn path;\n\t}\n\n\tstatic MatcherType fromElementOrMvc(Element elt) {\n\t\treturn MatcherType.fromElement(elt);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/MessageMatcherFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\nimport org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;\n\n@Deprecated\npublic final class MessageMatcherFactoryBean implements FactoryBean<MessageMatcher<?>>, ApplicationContextAware {\n\n\tprivate PathPatternMessageMatcher.Builder builder;\n\n\tprivate final SimpMessageType method;\n\n\tprivate final String path;\n\n\tpublic MessageMatcherFactoryBean(String path) {\n\t\tthis(path, null);\n\t}\n\n\tpublic MessageMatcherFactoryBean(String path, SimpMessageType method) {\n\t\tthis.method = method;\n\t\tthis.path = path;\n\t}\n\n\t@Override\n\tpublic MessageMatcher<?> getObject() throws Exception {\n\t\treturn this.builder.matcher(this.method, this.path);\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\tthis.builder = context.getBean(PathPatternMessageMatcher.Builder.class);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.BeanFactoryUtils;\nimport org.springframework.beans.factory.BeanInitializationException;\nimport org.springframework.beans.factory.ListableBeanFactory;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.annotation.AnnotationBeanNameGenerator;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.DelegatingOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.TokenExchangeOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\n\n/**\n * A registrar for registering the default {@link OAuth2AuthorizedClientManager} bean\n * definition, if not already present.\n * <p>\n * Note: This class is a direct copy of\n * {@link org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration.OAuth2AuthorizedClientManagerRegistrar}.\n *\n * @author Joe Grandja\n * @author Steve Riesenberg\n * @since 6.2.0\n */\nfinal class OAuth2AuthorizedClientManagerRegistrar implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {\n\n\tstatic final String BEAN_NAME = \"authorizedClientManagerRegistrar\";\n\n\tstatic final String FACTORY_METHOD_NAME = \"getAuthorizedClientManager\";\n\n\t// @formatter:off\n\tprivate static final Set<Class<?>> KNOWN_AUTHORIZED_CLIENT_PROVIDERS = Set.of(\n\t\t\tAuthorizationCodeOAuth2AuthorizedClientProvider.class,\n\t\t\tRefreshTokenOAuth2AuthorizedClientProvider.class,\n\t\t\tClientCredentialsOAuth2AuthorizedClientProvider.class,\n\t\t\tJwtBearerOAuth2AuthorizedClientProvider.class,\n\t\t\tTokenExchangeOAuth2AuthorizedClientProvider.class\n\t);\n\t// @formatter:on\n\n\tprivate final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();\n\n\tprivate ListableBeanFactory beanFactory;\n\n\t@Override\n\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\tif (getBeanNamesForType(OAuth2AuthorizedClientManager.class).length != 0\n\t\t\t\t|| getBeanNamesForType(ClientRegistrationRepository.class).length != 1\n\t\t\t\t|| getBeanNamesForType(OAuth2AuthorizedClientRepository.class).length != 1) {\n\t\t\treturn;\n\t\t}\n\n\t\tBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(OAuth2AuthorizedClientManager.class)\n\t\t\t.setFactoryMethodOnBean(FACTORY_METHOD_NAME, BEAN_NAME)\n\t\t\t.getBeanDefinition();\n\n\t\tregistry.registerBeanDefinition(this.beanNameGenerator.generateBeanName(beanDefinition, registry),\n\t\t\t\tbeanDefinition);\n\t}\n\n\t@Override\n\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t}\n\n\t@Override\n\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\tthis.beanFactory = (ListableBeanFactory) beanFactory;\n\t}\n\n\tprivate OAuth2AuthorizedClientManager getAuthorizedClientManager() {\n\t\tClientRegistrationRepository clientRegistrationRepository = BeanFactoryUtils\n\t\t\t.beanOfTypeIncludingAncestors(this.beanFactory, ClientRegistrationRepository.class, true, true);\n\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = BeanFactoryUtils\n\t\t\t.beanOfTypeIncludingAncestors(this.beanFactory, OAuth2AuthorizedClientRepository.class, true, true);\n\n\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviderBeans = BeanFactoryUtils\n\t\t\t.beansOfTypeIncludingAncestors(this.beanFactory, OAuth2AuthorizedClientProvider.class, true, true)\n\t\t\t.values();\n\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider;\n\t\tif (hasDelegatingAuthorizedClientProvider(authorizedClientProviderBeans)) {\n\t\t\tauthorizedClientProvider = authorizedClientProviderBeans.iterator().next();\n\t\t}\n\t\telse {\n\t\t\tList<OAuth2AuthorizedClientProvider> authorizedClientProviders = new ArrayList<>();\n\t\t\tauthorizedClientProviders.add(getAuthorizationCodeAuthorizedClientProvider(authorizedClientProviderBeans));\n\t\t\tauthorizedClientProviders.add(getRefreshTokenAuthorizedClientProvider(authorizedClientProviderBeans));\n\t\t\tauthorizedClientProviders.add(getClientCredentialsAuthorizedClientProvider(authorizedClientProviderBeans));\n\n\t\t\tOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = getJwtBearerAuthorizedClientProvider(\n\t\t\t\t\tauthorizedClientProviderBeans);\n\t\t\tif (jwtBearerAuthorizedClientProvider != null) {\n\t\t\t\tauthorizedClientProviders.add(jwtBearerAuthorizedClientProvider);\n\t\t\t}\n\n\t\t\tOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = getTokenExchangeAuthorizedClientProvider(\n\t\t\t\t\tauthorizedClientProviderBeans);\n\t\t\tif (tokenExchangeAuthorizedClientProvider != null) {\n\t\t\t\tauthorizedClientProviders.add(tokenExchangeAuthorizedClientProvider);\n\t\t\t}\n\n\t\t\tauthorizedClientProviders.addAll(getAdditionalAuthorizedClientProviders(authorizedClientProviderBeans));\n\t\t\tauthorizedClientProvider = new DelegatingOAuth2AuthorizedClientProvider(authorizedClientProviders);\n\t\t}\n\n\t\tDefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(\n\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t\tConsumer<DefaultOAuth2AuthorizedClientManager> authorizedClientManagerConsumer = getBeanOfType(\n\t\t\t\tResolvableType.forClassWithGenerics(Consumer.class, DefaultOAuth2AuthorizedClientManager.class));\n\t\tif (authorizedClientManagerConsumer != null) {\n\t\t\tauthorizedClientManagerConsumer.accept(authorizedClientManager);\n\t\t}\n\n\t\treturn authorizedClientManager;\n\t}\n\n\tprivate boolean hasDelegatingAuthorizedClientProvider(\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tif (authorizedClientProviders.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\t\treturn authorizedClientProviders.iterator().next() instanceof DelegatingOAuth2AuthorizedClientProvider;\n\t}\n\n\tprivate OAuth2AuthorizedClientProvider getAuthorizationCodeAuthorizedClientProvider(\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tAuthorizationCodeOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\tauthorizedClientProviders, AuthorizationCodeOAuth2AuthorizedClientProvider.class);\n\t\tif (authorizedClientProvider == null) {\n\t\t\tauthorizedClientProvider = new AuthorizationCodeOAuth2AuthorizedClientProvider();\n\t\t}\n\n\t\treturn authorizedClientProvider;\n\t}\n\n\tprivate OAuth2AuthorizedClientProvider getRefreshTokenAuthorizedClientProvider(\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tRefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\tauthorizedClientProviders, RefreshTokenOAuth2AuthorizedClientProvider.class);\n\t\tif (authorizedClientProvider == null) {\n\t\t\tauthorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider();\n\t\t}\n\n\t\tOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\tResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\tOAuth2RefreshTokenGrantRequest.class));\n\t\tif (accessTokenResponseClient != null) {\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t}\n\n\t\tApplicationEventPublisher applicationEventPublisher = getBeanOfType(\n\t\t\t\tResolvableType.forClass(ApplicationEventPublisher.class));\n\t\tif (applicationEventPublisher != null) {\n\t\t\tauthorizedClientProvider.setApplicationEventPublisher(applicationEventPublisher);\n\t\t}\n\n\t\treturn authorizedClientProvider;\n\t}\n\n\tprivate OAuth2AuthorizedClientProvider getClientCredentialsAuthorizedClientProvider(\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\tauthorizedClientProviders, ClientCredentialsOAuth2AuthorizedClientProvider.class);\n\t\tif (authorizedClientProvider == null) {\n\t\t\tauthorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();\n\t\t}\n\n\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\tResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\tOAuth2ClientCredentialsGrantRequest.class));\n\t\tif (accessTokenResponseClient != null) {\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t}\n\n\t\treturn authorizedClientProvider;\n\t}\n\n\tprivate OAuth2AuthorizedClientProvider getJwtBearerAuthorizedClientProvider(\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tJwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\tauthorizedClientProviders, JwtBearerOAuth2AuthorizedClientProvider.class);\n\n\t\tOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient = getBeanOfType(ResolvableType\n\t\t\t.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, JwtBearerGrantRequest.class));\n\t\tif (accessTokenResponseClient != null) {\n\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\tauthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();\n\t\t\t}\n\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t}\n\n\t\treturn authorizedClientProvider;\n\t}\n\n\tprivate OAuth2AuthorizedClientProvider getTokenExchangeAuthorizedClientProvider(\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tTokenExchangeOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(\n\t\t\t\tauthorizedClientProviders, TokenExchangeOAuth2AuthorizedClientProvider.class);\n\n\t\tOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient = getBeanOfType(\n\t\t\t\tResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,\n\t\t\t\t\t\tTokenExchangeGrantRequest.class));\n\t\tif (accessTokenResponseClient != null) {\n\t\t\tif (authorizedClientProvider == null) {\n\t\t\t\tauthorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();\n\t\t\t}\n\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);\n\t\t}\n\n\t\treturn authorizedClientProvider;\n\t}\n\n\tprivate List<OAuth2AuthorizedClientProvider> getAdditionalAuthorizedClientProviders(\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tList<OAuth2AuthorizedClientProvider> additionalAuthorizedClientProviders = new ArrayList<>(\n\t\t\t\tauthorizedClientProviders);\n\t\tadditionalAuthorizedClientProviders\n\t\t\t.removeIf((provider) -> KNOWN_AUTHORIZED_CLIENT_PROVIDERS.contains(provider.getClass()));\n\t\treturn additionalAuthorizedClientProviders;\n\t}\n\n\tprivate <T extends OAuth2AuthorizedClientProvider> T getAuthorizedClientProviderByType(\n\t\t\tCollection<OAuth2AuthorizedClientProvider> authorizedClientProviders, Class<T> providerClass) {\n\t\tT authorizedClientProvider = null;\n\t\tfor (OAuth2AuthorizedClientProvider current : authorizedClientProviders) {\n\t\t\tif (providerClass.isInstance(current)) {\n\t\t\t\tassertAuthorizedClientProviderIsNull(authorizedClientProvider);\n\t\t\t\tauthorizedClientProvider = providerClass.cast(current);\n\t\t\t}\n\t\t}\n\t\treturn authorizedClientProvider;\n\t}\n\n\tprivate static void assertAuthorizedClientProviderIsNull(OAuth2AuthorizedClientProvider authorizedClientProvider) {\n\t\tif (authorizedClientProvider != null) {\n\t\t\t// @formatter:off\n\t\t\tthrow new BeanInitializationException(String.format(\n\t\t\t\t\t\"Unable to create an %s bean. Expected one bean of type %s, but found multiple. \" +\n\t\t\t\t\t\"Please consider defining only a single bean of this type, or define an %s bean yourself.\",\n\t\t\t\t\tOAuth2AuthorizedClientManager.class.getName(),\n\t\t\t\t\tauthorizedClientProvider.getClass().getName(),\n\t\t\t\t\tOAuth2AuthorizedClientManager.class.getName()));\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\tprivate <T> String[] getBeanNamesForType(Class<T> beanClass) {\n\t\treturn BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, beanClass, false, false);\n\t}\n\n\tprivate <T> T getBeanOfType(ResolvableType resolvableType) {\n\t\tObjectProvider<T> objectProvider = this.beanFactory.getBeanProvider(resolvableType, true);\n\t\treturn objectProvider.getIfAvailable();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * @author Joe Grandja\n * @since 5.3\n */\nfinal class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ELT_AUTHORIZATION_CODE_GRANT = \"authorization-code-grant\";\n\n\tprivate static final String ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF = \"authorization-request-repository-ref\";\n\n\tprivate static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = \"authorization-request-resolver-ref\";\n\n\tprivate static final String ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF = \"authorization-redirect-strategy-ref\";\n\n\tprivate static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = \"access-token-response-client-ref\";\n\n\tprivate final BeanReference requestCache;\n\n\tprivate final BeanReference authenticationManager;\n\n\tprivate final BeanReference authenticationFilterSecurityContextRepositoryRef;\n\n\tprivate final BeanMetadataElement authenticationFilterSecurityContextHolderStrategy;\n\n\tprivate BeanDefinition defaultAuthorizedClientRepository;\n\n\tprivate BeanDefinition authorizationRequestRedirectFilter;\n\n\tprivate BeanDefinition authorizationCodeGrantFilter;\n\n\tprivate BeanDefinition authorizationCodeAuthenticationProvider;\n\n\tOAuth2ClientBeanDefinitionParser(BeanReference requestCache, BeanReference authenticationManager,\n\t\t\tBeanReference authenticationFilterSecurityContextRepositoryRef,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategy) {\n\t\tthis.requestCache = requestCache;\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.authenticationFilterSecurityContextRepositoryRef = authenticationFilterSecurityContextRepositoryRef;\n\t\tthis.authenticationFilterSecurityContextHolderStrategy = authenticationFilterSecurityContextHolderStrategy;\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tElement authorizationCodeGrantElt = DomUtils.getChildElementByTagName(element, ELT_AUTHORIZATION_CODE_GRANT);\n\t\tBeanMetadataElement clientRegistrationRepository = OAuth2ClientBeanDefinitionParserUtils\n\t\t\t.getClientRegistrationRepository(element);\n\t\tBeanMetadataElement authorizedClientRepository = OAuth2ClientBeanDefinitionParserUtils\n\t\t\t.getAuthorizedClientRepository(element);\n\t\tif (authorizedClientRepository == null) {\n\t\t\tBeanMetadataElement authorizedClientService = OAuth2ClientBeanDefinitionParserUtils\n\t\t\t\t.getAuthorizedClientService(element);\n\t\t\tthis.defaultAuthorizedClientRepository = OAuth2ClientBeanDefinitionParserUtils\n\t\t\t\t.createDefaultAuthorizedClientRepository(clientRegistrationRepository, authorizedClientService);\n\t\t\tauthorizedClientRepository = new RuntimeBeanReference(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\t\tBeanMetadataElement authorizationRequestRepository = getAuthorizationRequestRepository(\n\t\t\t\tauthorizationCodeGrantElt);\n\t\tBeanMetadataElement authorizationRedirectStrategy = getAuthorizationRedirectStrategy(authorizationCodeGrantElt);\n\t\tBeanDefinitionBuilder authorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class);\n\t\tString authorizationRequestResolverRef = (authorizationCodeGrantElt != null)\n\t\t\t\t? authorizationCodeGrantElt.getAttribute(ATT_AUTHORIZATION_REQUEST_RESOLVER_REF) : null;\n\t\tif (StringUtils.hasLength(authorizationRequestResolverRef)) {\n\t\t\tauthorizationRequestRedirectFilterBuilder.addConstructorArgReference(authorizationRequestResolverRef);\n\t\t}\n\t\telse {\n\t\t\tauthorizationRequestRedirectFilterBuilder.addConstructorArgValue(clientRegistrationRepository);\n\t\t}\n\t\tthis.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder\n\t\t\t.addPropertyValue(\"authorizationRequestRepository\", authorizationRequestRepository)\n\t\t\t.addPropertyValue(\"authorizationRedirectStrategy\", authorizationRedirectStrategy)\n\t\t\t.addPropertyValue(\"requestCache\", this.requestCache)\n\t\t\t.getBeanDefinition();\n\t\tBeanDefinitionBuilder authorizationCodeGrantFilterBldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)\n\t\t\t.addConstructorArgValue(clientRegistrationRepository)\n\t\t\t.addConstructorArgValue(authorizedClientRepository)\n\t\t\t.addConstructorArgValue(this.authenticationManager)\n\t\t\t.addPropertyValue(\"authorizationRequestRepository\", authorizationRequestRepository);\n\t\tthis.authorizationCodeGrantFilter = authorizationCodeGrantFilterBldr.getBeanDefinition();\n\n\t\tBeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(authorizationCodeGrantElt);\n\t\tthis.authorizationCodeAuthenticationProvider = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(OAuth2AuthorizationCodeAuthenticationProvider.class)\n\t\t\t.addConstructorArgValue(accessTokenResponseClient)\n\t\t\t.getBeanDefinition();\n\n\t\treturn null;\n\t}\n\n\tprivate BeanMetadataElement getAuthorizationRequestRepository(Element element) {\n\t\tString authorizationRequestRepositoryRef = (element != null)\n\t\t\t\t? element.getAttribute(ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF) : null;\n\t\tif (StringUtils.hasLength(authorizationRequestRepositoryRef)) {\n\t\t\treturn new RuntimeBeanReference(authorizationRequestRepositoryRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(\n\t\t\t\t\t\"org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement getAuthorizationRedirectStrategy(Element element) {\n\t\tString authorizationRedirectStrategyRef = (element != null)\n\t\t\t\t? element.getAttribute(ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF) : null;\n\t\tif (StringUtils.hasText(authorizationRedirectStrategyRef)) {\n\t\t\treturn new RuntimeBeanReference(authorizationRedirectStrategyRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(\"org.springframework.security.web.DefaultRedirectStrategy\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement getAccessTokenResponseClient(Element element) {\n\t\tString accessTokenResponseClientRef = (element != null)\n\t\t\t\t? element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF) : null;\n\t\tif (StringUtils.hasLength(accessTokenResponseClientRef)) {\n\t\t\treturn new RuntimeBeanReference(accessTokenResponseClientRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(\n\t\t\t\t\"org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tBeanDefinition getDefaultAuthorizedClientRepository() {\n\t\treturn this.defaultAuthorizedClientRepository;\n\t}\n\n\tBeanDefinition getAuthorizationRequestRedirectFilter() {\n\t\treturn this.authorizationRequestRedirectFilter;\n\t}\n\n\tBeanDefinition getAuthorizationCodeGrantFilter() {\n\t\treturn this.authorizationCodeGrantFilter;\n\t}\n\n\tBeanDefinition getAuthorizationCodeAuthenticationProvider() {\n\t\treturn this.authorizationCodeAuthenticationProvider;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Joe Grandja\n * @since 5.4\n */\nfinal class OAuth2ClientBeanDefinitionParserUtils {\n\n\tprivate static final String ATT_CLIENT_REGISTRATION_REPOSITORY_REF = \"client-registration-repository-ref\";\n\n\tprivate static final String ATT_AUTHORIZED_CLIENT_REPOSITORY_REF = \"authorized-client-repository-ref\";\n\n\tprivate static final String ATT_AUTHORIZED_CLIENT_SERVICE_REF = \"authorized-client-service-ref\";\n\n\tprivate OAuth2ClientBeanDefinitionParserUtils() {\n\t}\n\n\tstatic BeanMetadataElement getClientRegistrationRepository(Element element) {\n\t\tString clientRegistrationRepositoryRef = element.getAttribute(ATT_CLIENT_REGISTRATION_REPOSITORY_REF);\n\t\tif (StringUtils.hasLength(clientRegistrationRepositoryRef)) {\n\t\t\treturn new RuntimeBeanReference(clientRegistrationRepositoryRef);\n\t\t}\n\t\treturn new RuntimeBeanReference(ClientRegistrationRepository.class);\n\t}\n\n\tstatic BeanMetadataElement getAuthorizedClientRepository(Element element) {\n\t\tString authorizedClientRepositoryRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_REPOSITORY_REF);\n\t\tif (StringUtils.hasLength(authorizedClientRepositoryRef)) {\n\t\t\treturn new RuntimeBeanReference(authorizedClientRepositoryRef);\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic BeanMetadataElement getAuthorizedClientService(Element element) {\n\t\tString authorizedClientServiceRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_SERVICE_REF);\n\t\tif (StringUtils.hasLength(authorizedClientServiceRef)) {\n\t\t\treturn new RuntimeBeanReference(authorizedClientServiceRef);\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic BeanDefinition createDefaultAuthorizedClientRepository(BeanMetadataElement clientRegistrationRepository,\n\t\t\tBeanMetadataElement authorizedClientService) {\n\t\tif (authorizedClientService == null) {\n\t\t\tauthorizedClientService = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(\"org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService\")\n\t\t\t\t.addConstructorArgValue(clientRegistrationRepository)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(\n\t\t\t\t\"org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository\")\n\t\t\t.addConstructorArgValue(authorizedClientService)\n\t\t\t.getBeanDefinition();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/OAuth2ClientWebMvcSecurityPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.PropertyValue;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.BeanFactoryUtils;\nimport org.springframework.beans.factory.ListableBeanFactory;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.method.annotation.OAuth2AuthorizedClientArgumentResolver;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;\n\n/**\n * @author Joe Grandja\n * @since 5.4\n */\nfinal class OAuth2ClientWebMvcSecurityPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {\n\n\tprivate static final String CUSTOM_ARGUMENT_RESOLVERS_PROPERTY = \"customArgumentResolvers\";\n\n\tprivate BeanFactory beanFactory;\n\n\t@Override\n\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\tString[] clientRegistrationRepositoryBeanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(\n\t\t\t\t(ListableBeanFactory) this.beanFactory, ClientRegistrationRepository.class, false, false);\n\t\tString[] authorizedClientRepositoryBeanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(\n\t\t\t\t(ListableBeanFactory) this.beanFactory, OAuth2AuthorizedClientRepository.class, false, false);\n\t\tif (clientRegistrationRepositoryBeanNames.length != 1 || authorizedClientRepositoryBeanNames.length != 1) {\n\t\t\treturn;\n\t\t}\n\t\tfor (String beanName : registry.getBeanDefinitionNames()) {\n\t\t\tBeanDefinition beanDefinition = registry.getBeanDefinition(beanName);\n\t\t\tif (RequestMappingHandlerAdapter.class.getName().equals(beanDefinition.getBeanClassName())) {\n\t\t\t\tPropertyValue currentArgumentResolvers = beanDefinition.getPropertyValues()\n\t\t\t\t\t.getPropertyValue(CUSTOM_ARGUMENT_RESOLVERS_PROPERTY);\n\t\t\t\tManagedList<Object> argumentResolvers = new ManagedList<>();\n\t\t\t\tif (currentArgumentResolvers != null) {\n\t\t\t\t\targumentResolvers.addAll((ManagedList<?>) currentArgumentResolvers.getValue());\n\t\t\t\t}\n\t\t\t\tString[] authorizedClientManagerBeanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(\n\t\t\t\t\t\t(ListableBeanFactory) this.beanFactory, OAuth2AuthorizedClientManager.class, false, false);\n\t\t\t\tBeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder\n\t\t\t\t\t.genericBeanDefinition(OAuth2AuthorizedClientArgumentResolver.class);\n\t\t\t\tif (authorizedClientManagerBeanNames.length == 1) {\n\t\t\t\t\tbeanDefinitionBuilder.addConstructorArgReference(authorizedClientManagerBeanNames[0]);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tbeanDefinitionBuilder.addConstructorArgReference(clientRegistrationRepositoryBeanNames[0]);\n\t\t\t\t\tbeanDefinitionBuilder.addConstructorArgReference(authorizedClientRepositoryBeanNames[0]);\n\t\t\t\t}\n\t\t\t\targumentResolvers.add(beanDefinitionBuilder.getBeanDefinition());\n\t\t\t\tbeanDefinition.getPropertyValues().add(CUSTOM_ARGUMENT_RESOLVERS_PROPERTY, argumentResolvers);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t}\n\n\t@Override\n\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\tthis.beanFactory = beanFactory;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.accept.HeaderContentNegotiationStrategy;\n\n/**\n * @author Ruby Hartono\n * @since 5.3\n */\nfinal class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = \"/oauth2/authorization\";\n\n\tprivate static final String DEFAULT_LOGIN_URI = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;\n\n\tprivate static final String ELT_CLIENT_REGISTRATION = \"client-registration\";\n\n\tprivate static final String ATT_REGISTRATION_ID = \"registration-id\";\n\n\tprivate static final String ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF = \"authorization-request-repository-ref\";\n\n\tprivate static final String ATT_AUTHORIZATION_REQUEST_RESOLVER_REF = \"authorization-request-resolver-ref\";\n\n\tprivate static final String ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF = \"authorization-redirect-strategy-ref\";\n\n\tprivate static final String ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF = \"access-token-response-client-ref\";\n\n\tprivate static final String ATT_USER_AUTHORITIES_MAPPER_REF = \"user-authorities-mapper-ref\";\n\n\tprivate static final String ATT_USER_SERVICE_REF = \"user-service-ref\";\n\n\tprivate static final String ATT_OIDC_USER_SERVICE_REF = \"oidc-user-service-ref\";\n\n\tprivate static final String ATT_LOGIN_PROCESSING_URL = \"login-processing-url\";\n\n\tprivate static final String ATT_LOGIN_PAGE = \"login-page\";\n\n\tprivate static final String ATT_AUTHENTICATION_SUCCESS_HANDLER_REF = \"authentication-success-handler-ref\";\n\n\tprivate static final String ATT_AUTHENTICATION_FAILURE_HANDLER_REF = \"authentication-failure-handler-ref\";\n\n\tprivate static final String ATT_JWT_DECODER_FACTORY_REF = \"jwt-decoder-factory-ref\";\n\n\tprivate final BeanReference requestCache;\n\n\tprivate final BeanReference portMapper;\n\n\tprivate final BeanReference sessionStrategy;\n\n\tprivate final boolean allowSessionCreation;\n\n\tprivate final BeanMetadataElement authenticationFilterSecurityContextHolderStrategy;\n\n\tprivate BeanDefinition defaultAuthorizedClientRepository;\n\n\tprivate BeanDefinition oauth2AuthorizationRequestRedirectFilter;\n\n\tprivate BeanDefinition oauth2LoginAuthenticationEntryPoint;\n\n\tprivate BeanDefinition oauth2LoginAuthenticationProvider;\n\n\tprivate BeanDefinition oauth2LoginOidcAuthenticationProvider;\n\n\tprivate BeanDefinition oauth2LoginLinks;\n\n\tOAuth2LoginBeanDefinitionParser(BeanReference requestCache, BeanReference portMapper, BeanReference sessionStrategy,\n\t\t\tboolean allowSessionCreation, BeanMetadataElement authenticationFilterSecurityContextHolderStrategy) {\n\t\tthis.requestCache = requestCache;\n\t\tthis.portMapper = portMapper;\n\t\tthis.sessionStrategy = sessionStrategy;\n\t\tthis.allowSessionCreation = allowSessionCreation;\n\t\tthis.authenticationFilterSecurityContextHolderStrategy = authenticationFilterSecurityContextHolderStrategy;\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\t// register magic bean\n\t\tBeanDefinition oauth2LoginBeanConfig = BeanDefinitionBuilder.rootBeanDefinition(OAuth2LoginBeanConfig.class)\n\t\t\t.getBeanDefinition();\n\t\tString oauth2LoginBeanConfigId = parserContext.getReaderContext().generateBeanName(oauth2LoginBeanConfig);\n\t\tparserContext\n\t\t\t.registerBeanComponent(new BeanComponentDefinition(oauth2LoginBeanConfig, oauth2LoginBeanConfigId));\n\t\t// configure filter\n\t\tBeanMetadataElement clientRegistrationRepository = OAuth2ClientBeanDefinitionParserUtils\n\t\t\t.getClientRegistrationRepository(element);\n\t\tBeanMetadataElement authorizedClientRepository = OAuth2ClientBeanDefinitionParserUtils\n\t\t\t.getAuthorizedClientRepository(element);\n\t\tif (authorizedClientRepository == null) {\n\t\t\tBeanMetadataElement authorizedClientService = OAuth2ClientBeanDefinitionParserUtils\n\t\t\t\t.getAuthorizedClientService(element);\n\t\t\tthis.defaultAuthorizedClientRepository = OAuth2ClientBeanDefinitionParserUtils\n\t\t\t\t.createDefaultAuthorizedClientRepository(clientRegistrationRepository, authorizedClientService);\n\t\t\tauthorizedClientRepository = new RuntimeBeanReference(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\t\tBeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(element);\n\t\tBeanMetadataElement oauth2UserService = getOAuth2UserService(element);\n\t\tBeanMetadataElement authorizationRequestRepository = getAuthorizationRequestRepository(element);\n\t\tBeanDefinitionBuilder oauth2LoginAuthenticationFilterBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(OAuth2LoginAuthenticationFilter.class)\n\t\t\t.addConstructorArgValue(clientRegistrationRepository)\n\t\t\t.addConstructorArgValue(authorizedClientRepository)\n\t\t\t.addPropertyValue(\"authorizationRequestRepository\", authorizationRequestRepository);\n\t\tif (this.sessionStrategy != null) {\n\t\t\toauth2LoginAuthenticationFilterBuilder.addPropertyValue(\"sessionAuthenticationStrategy\",\n\t\t\t\t\tthis.sessionStrategy);\n\t\t}\n\t\tObject source = parserContext.extractSource(element);\n\t\tString loginProcessingUrl = element.getAttribute(ATT_LOGIN_PROCESSING_URL);\n\t\tif (StringUtils.hasLength(loginProcessingUrl)) {\n\t\t\tWebConfigUtils.validateHttpRedirect(loginProcessingUrl, parserContext, source);\n\t\t\toauth2LoginAuthenticationFilterBuilder.addConstructorArgValue(loginProcessingUrl);\n\t\t}\n\t\telse {\n\t\t\toauth2LoginAuthenticationFilterBuilder\n\t\t\t\t.addConstructorArgValue(OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI);\n\t\t}\n\t\tBeanDefinitionBuilder oauth2LoginAuthenticationProviderBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(OAuth2LoginAuthenticationProvider.class)\n\t\t\t.addConstructorArgValue(accessTokenResponseClient)\n\t\t\t.addConstructorArgValue(oauth2UserService);\n\t\tString userAuthoritiesMapperRef = element.getAttribute(ATT_USER_AUTHORITIES_MAPPER_REF);\n\t\tif (StringUtils.hasLength(userAuthoritiesMapperRef)) {\n\t\t\toauth2LoginAuthenticationProviderBuilder.addPropertyReference(\"authoritiesMapper\",\n\t\t\t\t\tuserAuthoritiesMapperRef);\n\t\t}\n\t\tthis.oauth2LoginAuthenticationProvider = oauth2LoginAuthenticationProviderBuilder.getBeanDefinition();\n\t\tthis.oauth2LoginOidcAuthenticationProvider = getOidcAuthProvider(element, accessTokenResponseClient,\n\t\t\t\tuserAuthoritiesMapperRef);\n\t\tBeanDefinitionBuilder oauth2AuthorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class);\n\t\tString authorizationRequestResolverRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_RESOLVER_REF);\n\t\tif (StringUtils.hasLength(authorizationRequestResolverRef)) {\n\t\t\toauth2AuthorizationRequestRedirectFilterBuilder.addConstructorArgReference(authorizationRequestResolverRef);\n\t\t}\n\t\telse {\n\t\t\toauth2AuthorizationRequestRedirectFilterBuilder.addConstructorArgValue(clientRegistrationRepository);\n\t\t}\n\t\toauth2AuthorizationRequestRedirectFilterBuilder\n\t\t\t.addPropertyValue(\"authorizationRequestRepository\", authorizationRequestRepository)\n\t\t\t.addPropertyValue(\"authorizationRedirectStrategy\", getAuthorizationRedirectStrategy(element))\n\t\t\t.addPropertyValue(\"requestCache\", this.requestCache);\n\t\tthis.oauth2AuthorizationRequestRedirectFilter = oauth2AuthorizationRequestRedirectFilterBuilder\n\t\t\t.getBeanDefinition();\n\t\tString authenticationSuccessHandlerRef = element.getAttribute(ATT_AUTHENTICATION_SUCCESS_HANDLER_REF);\n\t\tif (StringUtils.hasLength(authenticationSuccessHandlerRef)) {\n\t\t\toauth2LoginAuthenticationFilterBuilder.addPropertyReference(\"authenticationSuccessHandler\",\n\t\t\t\t\tauthenticationSuccessHandlerRef);\n\t\t}\n\t\telse {\n\t\t\tBeanDefinitionBuilder successHandlerBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(\n\t\t\t\t\t\t\"org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler\")\n\t\t\t\t.addPropertyValue(\"requestCache\", this.requestCache);\n\t\t\toauth2LoginAuthenticationFilterBuilder.addPropertyValue(\"authenticationSuccessHandler\",\n\t\t\t\t\tsuccessHandlerBuilder.getBeanDefinition());\n\t\t}\n\t\tString loginPage = element.getAttribute(ATT_LOGIN_PAGE);\n\t\tif (StringUtils.hasLength(loginPage)) {\n\t\t\tWebConfigUtils.validateHttpRedirect(loginPage, parserContext, source);\n\t\t\tthis.oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class)\n\t\t\t\t.addConstructorArgValue(loginPage)\n\t\t\t\t.addPropertyValue(\"portMapper\", this.portMapper)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\t\telse {\n\t\t\tMap<BeanDefinition, AuthenticationEntryPoint> entryPoint = getLoginEntryPoint(element);\n\t\t\tif (entryPoint != null) {\n\t\t\t\tthis.oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(DelegatingAuthenticationEntryPoint.class)\n\t\t\t\t\t.addConstructorArgValue(entryPoint)\n\t\t\t\t\t.addPropertyValue(\"defaultEntryPoint\", new LoginUrlAuthenticationEntryPoint(DEFAULT_LOGIN_URI))\n\t\t\t\t\t.getBeanDefinition();\n\t\t\t}\n\t\t}\n\t\tString authenticationFailureHandlerRef = element.getAttribute(ATT_AUTHENTICATION_FAILURE_HANDLER_REF);\n\t\tif (StringUtils.hasLength(authenticationFailureHandlerRef)) {\n\t\t\toauth2LoginAuthenticationFilterBuilder.addPropertyReference(\"authenticationFailureHandler\",\n\t\t\t\t\tauthenticationFailureHandlerRef);\n\t\t}\n\t\telse {\n\t\t\tBeanDefinitionBuilder failureHandlerBuilder = BeanDefinitionBuilder.rootBeanDefinition(\n\t\t\t\t\t\"org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler\");\n\t\t\tfailureHandlerBuilder.addConstructorArgValue(\n\t\t\t\t\tDEFAULT_LOGIN_URI + \"?\" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME);\n\t\t\tfailureHandlerBuilder.addPropertyValue(\"allowSessionCreation\", this.allowSessionCreation);\n\t\t\toauth2LoginAuthenticationFilterBuilder.addPropertyValue(\"authenticationFailureHandler\",\n\t\t\t\t\tfailureHandlerBuilder.getBeanDefinition());\n\t\t}\n\t\toauth2LoginAuthenticationFilterBuilder.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\tthis.authenticationFilterSecurityContextHolderStrategy);\n\t\t// prepare loginlinks\n\t\tthis.oauth2LoginLinks = BeanDefinitionBuilder.rootBeanDefinition(Map.class)\n\t\t\t.setFactoryMethodOnBean(\"getLoginLinks\", oauth2LoginBeanConfigId)\n\t\t\t.getBeanDefinition();\n\t\treturn oauth2LoginAuthenticationFilterBuilder.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement getAuthorizationRequestRepository(Element element) {\n\t\tString authorizationRequestRepositoryRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF);\n\t\tif (StringUtils.hasLength(authorizationRequestRepositoryRef)) {\n\t\t\treturn new RuntimeBeanReference(authorizationRequestRepositoryRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(\n\t\t\t\t\t\"org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement getAuthorizationRedirectStrategy(Element element) {\n\t\tString authorizationRedirectStrategyRef = element.getAttribute(ATT_AUTHORIZATION_REDIRECT_STRATEGY_REF);\n\t\tif (StringUtils.hasText(authorizationRedirectStrategyRef)) {\n\t\t\treturn new RuntimeBeanReference(authorizationRedirectStrategyRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(\"org.springframework.security.web.DefaultRedirectStrategy\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanDefinition getOidcAuthProvider(Element element, BeanMetadataElement accessTokenResponseClient,\n\t\t\tString userAuthoritiesMapperRef) {\n\t\tboolean oidcAuthenticationProviderEnabled = ClassUtils\n\t\t\t.isPresent(\"org.springframework.security.oauth2.jwt.JwtDecoder\", this.getClass().getClassLoader());\n\t\tif (!oidcAuthenticationProviderEnabled) {\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(OidcAuthenticationRequestChecker.class).getBeanDefinition();\n\t\t}\n\t\tBeanMetadataElement oidcUserService = getOidcUserService(element);\n\t\tBeanDefinitionBuilder oidcAuthProviderBuilder = BeanDefinitionBuilder.rootBeanDefinition(\n\t\t\t\t\"org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider\")\n\t\t\t.addConstructorArgValue(accessTokenResponseClient)\n\t\t\t.addConstructorArgValue(oidcUserService);\n\t\tif (StringUtils.hasLength(userAuthoritiesMapperRef)) {\n\t\t\toidcAuthProviderBuilder.addPropertyReference(\"authoritiesMapper\", userAuthoritiesMapperRef);\n\t\t}\n\t\tString jwtDecoderFactoryRef = element.getAttribute(ATT_JWT_DECODER_FACTORY_REF);\n\t\tif (StringUtils.hasLength(jwtDecoderFactoryRef)) {\n\t\t\toidcAuthProviderBuilder.addPropertyReference(\"jwtDecoderFactory\", jwtDecoderFactoryRef);\n\t\t}\n\t\treturn oidcAuthProviderBuilder.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement getOidcUserService(Element element) {\n\t\tString oidcUserServiceRef = element.getAttribute(ATT_OIDC_USER_SERVICE_REF);\n\t\tif (StringUtils.hasLength(oidcUserServiceRef)) {\n\t\t\treturn new RuntimeBeanReference(oidcUserServiceRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(\"org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement getOAuth2UserService(Element element) {\n\t\tString oauth2UserServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);\n\t\tif (StringUtils.hasLength(oauth2UserServiceRef)) {\n\t\t\treturn new RuntimeBeanReference(oauth2UserServiceRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(\"org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement getAccessTokenResponseClient(Element element) {\n\t\tString accessTokenResponseClientRef = element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF);\n\t\tif (StringUtils.hasLength(accessTokenResponseClientRef)) {\n\t\t\treturn new RuntimeBeanReference(accessTokenResponseClientRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(\n\t\t\t\t\"org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tBeanDefinition getDefaultAuthorizedClientRepository() {\n\t\treturn this.defaultAuthorizedClientRepository;\n\t}\n\n\tBeanDefinition getOAuth2AuthorizationRequestRedirectFilter() {\n\t\treturn this.oauth2AuthorizationRequestRedirectFilter;\n\t}\n\n\tBeanDefinition getOAuth2LoginAuthenticationEntryPoint() {\n\t\treturn this.oauth2LoginAuthenticationEntryPoint;\n\t}\n\n\tBeanDefinition getOAuth2LoginAuthenticationProvider() {\n\t\treturn this.oauth2LoginAuthenticationProvider;\n\t}\n\n\tBeanDefinition getOAuth2LoginOidcAuthenticationProvider() {\n\t\treturn this.oauth2LoginOidcAuthenticationProvider;\n\t}\n\n\tBeanDefinition getOAuth2LoginLinks() {\n\t\treturn this.oauth2LoginLinks;\n\t}\n\n\tprivate Map<BeanDefinition, AuthenticationEntryPoint> getLoginEntryPoint(Element element) {\n\t\tMap<BeanDefinition, AuthenticationEntryPoint> entryPoints = null;\n\t\tElement clientRegsElt = DomUtils.getChildElementByTagName(element.getOwnerDocument().getDocumentElement(),\n\t\t\t\tElements.CLIENT_REGISTRATIONS);\n\t\tif (clientRegsElt != null) {\n\t\t\tList<Element> clientRegList = DomUtils.getChildElementsByTagName(clientRegsElt, ELT_CLIENT_REGISTRATION);\n\t\t\tif (clientRegList.size() == 1) {\n\t\t\t\tBeanDefinition loginPageMatcher = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class)\n\t\t\t\t\t.addConstructorArgValue(DEFAULT_LOGIN_URI)\n\t\t\t\t\t.getBeanDefinition();\n\t\t\t\tBeanDefinition faviconMatcher = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class)\n\t\t\t\t\t.addConstructorArgValue(\"/favicon.ico\")\n\t\t\t\t\t.getBeanDefinition();\n\t\t\t\tBeanDefinition entryPointMatcher = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(EntryPointMatcherFactoryBean.class)\n\t\t\t\t\t.addConstructorArgValue(loginPageMatcher)\n\t\t\t\t\t.addConstructorArgValue(faviconMatcher)\n\t\t\t\t\t.getBeanDefinition();\n\t\t\t\tElement clientRegElt = clientRegList.get(0);\n\t\t\t\tentryPoints = new ManagedMap<>();\n\t\t\t\tentryPoints.put(entryPointMatcher, new LoginUrlAuthenticationEntryPoint(\n\t\t\t\t\t\tDEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/\" + clientRegElt.getAttribute(ATT_REGISTRATION_ID)));\n\t\t\t}\n\t\t}\n\t\treturn entryPoints;\n\t}\n\n\tprivate static class OidcAuthenticationRequestChecker implements AuthenticationProvider {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\tOAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken) authentication;\n\t\t\tif (!authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t\t.getAuthorizationRequest()\n\t\t\t\t.getScopes()\n\t\t\t\t.contains(OidcScopes.OPENID)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// Section 3.1.2.1 Authentication Request -\n\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest scope\n\t\t\t// REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope\n\t\t\t// value.\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(\"oidc_provider_not_configured\",\n\t\t\t\t\t\"An OpenID Connect Authentication Provider has not been configured. \"\n\t\t\t\t\t\t\t+ \"Check to ensure you include the dependency 'spring-security-oauth2-jose'.\",\n\t\t\t\t\tnull);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\treturn OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);\n\t\t}\n\n\t}\n\n\t/**\n\t * Wrapper bean class to provide configuration from applicationContext\n\t */\n\tprivate static class OAuth2LoginBeanConfig implements ApplicationContextAware {\n\n\t\tprivate ApplicationContext context;\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\t\tthis.context = context;\n\t\t}\n\n\t\t@SuppressWarnings({ \"unchecked\", \"unused\" })\n\t\tMap<String, String> getLoginLinks() {\n\t\t\tIterable<ClientRegistration> clientRegistrations = null;\n\t\t\tClientRegistrationRepository clientRegistrationRepository = this.context\n\t\t\t\t.getBean(ClientRegistrationRepository.class);\n\t\t\tResolvableType type = ResolvableType.forInstance(clientRegistrationRepository).as(Iterable.class);\n\t\t\tif (type != ResolvableType.NONE && ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {\n\t\t\t\tclientRegistrations = (Iterable<ClientRegistration>) clientRegistrationRepository;\n\t\t\t}\n\t\t\tif (clientRegistrations == null) {\n\t\t\t\treturn Collections.emptyMap();\n\t\t\t}\n\t\t\tString authorizationRequestBaseUri = DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;\n\t\t\tMap<String, String> loginUrlToClientName = new HashMap<>();\n\t\t\tclientRegistrations.forEach((registration) -> loginUrlToClientName.put(\n\t\t\t\t\tauthorizationRequestBaseUri + \"/\" + registration.getRegistrationId(),\n\t\t\t\t\tregistration.getClientName()));\n\t\t\treturn loginUrlToClientName;\n\t\t}\n\n\t}\n\n\t@Deprecated\n\tstatic class EntryPointMatcherFactoryBean implements FactoryBean<RequestMatcher> {\n\n\t\tprivate final RequestMatcher entryPointMatcher;\n\n\t\tEntryPointMatcherFactoryBean(RequestMatcher loginPageMatcher, RequestMatcher faviconMatcher) {\n\t\t\tRequestMatcher defaultEntryPointMatcher = getAuthenticationEntryPointMatcher();\n\t\t\tRequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(\n\t\t\t\t\tnew OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);\n\t\t\tRequestMatcher notXRequestedWith = new NegatedRequestMatcher(\n\t\t\t\t\tnew RequestHeaderRequestMatcher(\"X-Requested-With\", \"XMLHttpRequest\"));\n\t\t\tthis.entryPointMatcher = new AndRequestMatcher(notXRequestedWith,\n\t\t\t\t\tnew NegatedRequestMatcher(defaultLoginPageMatcher));\n\t\t}\n\n\t\tprivate RequestMatcher getAuthenticationEntryPointMatcher() {\n\t\t\tContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();\n\t\t\tMediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,\n\t\t\t\t\tMediaType.APPLICATION_XHTML_XML, new MediaType(\"image\", \"*\"), MediaType.TEXT_HTML,\n\t\t\t\t\tMediaType.TEXT_PLAIN);\n\t\t\tmediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\t\tRequestMatcher notXRequestedWith = new NegatedRequestMatcher(\n\t\t\t\t\tnew RequestHeaderRequestMatcher(\"X-Requested-With\", \"XMLHttpRequest\"));\n\t\t\treturn new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));\n\t\t}\n\n\t\t@Override\n\t\tpublic RequestMatcher getObject() {\n\t\t\treturn this.entryPointMatcher;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn RequestMatcher.class;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.BeanDefinitionStoreException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;\nimport org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;\nimport org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;\nimport org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * A {@link BeanDefinitionParser} for &lt;http&gt;'s &lt;oauth2-resource-server&gt;\n * element.\n *\n * @author Josh Cummings\n * @since 5.3\n */\nfinal class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionParser {\n\n\tstatic final String AUTHENTICATION_MANAGER_RESOLVER_REF = \"authentication-manager-resolver-ref\";\n\n\tstatic final String BEARER_TOKEN_RESOLVER_REF = \"bearer-token-resolver-ref\";\n\n\tstatic final String AUTHENTICATION_CONVERTER_REF = \"authentication-converter-ref\";\n\n\tstatic final String ENTRY_POINT_REF = \"entry-point-ref\";\n\n\tstatic final String BEARER_TOKEN_RESOLVER = \"bearerTokenResolver\";\n\n\tstatic final String AUTHENTICATION_ENTRY_POINT = \"authenticationEntryPoint\";\n\n\tprivate final BeanReference authenticationManager;\n\n\tprivate final List<BeanReference> authenticationProviders;\n\n\tprivate final Map<BeanDefinition, BeanMetadataElement> entryPoints;\n\n\tprivate final Map<BeanDefinition, BeanMetadataElement> deniedHandlers;\n\n\tprivate final List<BeanDefinition> ignoreCsrfRequestMatchers;\n\n\tprivate final BeanDefinition authenticationEntryPoint = new RootBeanDefinition(\n\t\t\tBearerTokenAuthenticationEntryPoint.class);\n\n\tprivate final BeanDefinition accessDeniedHandler = new RootBeanDefinition(BearerTokenAccessDeniedHandler.class);\n\n\tprivate final BeanMetadataElement authenticationFilterSecurityContextHolderStrategy;\n\n\tOAuth2ResourceServerBeanDefinitionParser(BeanReference authenticationManager,\n\t\t\tList<BeanReference> authenticationProviders, Map<BeanDefinition, BeanMetadataElement> entryPoints,\n\t\t\tMap<BeanDefinition, BeanMetadataElement> deniedHandlers, List<BeanDefinition> ignoreCsrfRequestMatchers,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategy) {\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.authenticationProviders = authenticationProviders;\n\t\tthis.entryPoints = entryPoints;\n\t\tthis.deniedHandlers = deniedHandlers;\n\t\tthis.ignoreCsrfRequestMatchers = ignoreCsrfRequestMatchers;\n\t\tthis.authenticationFilterSecurityContextHolderStrategy = authenticationFilterSecurityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Parse a &lt;oauth2-resource-server&gt; element and return the corresponding\n\t * {@link BearerTokenAuthenticationFilter}\n\t * @param oauth2ResourceServer the &lt;oauth2-resource-server&gt; element.\n\t * @param pc the {@link ParserContext}\n\t * @return a {@link BeanDefinition} representing a\n\t * {@link BearerTokenAuthenticationFilter} definition\n\t */\n\t@Override\n\tpublic BeanDefinition parse(Element oauth2ResourceServer, ParserContext pc) {\n\t\tElement jwt = DomUtils.getChildElementByTagName(oauth2ResourceServer, Elements.JWT);\n\t\tElement opaqueToken = DomUtils.getChildElementByTagName(oauth2ResourceServer, Elements.OPAQUE_TOKEN);\n\t\tvalidateConfiguration(oauth2ResourceServer, jwt, opaqueToken, pc);\n\t\tif (jwt != null) {\n\t\t\tBeanDefinition jwtAuthenticationProvider = new JwtBeanDefinitionParser().parse(jwt, pc);\n\t\t\tthis.authenticationProviders.add(new RuntimeBeanReference(\n\t\t\t\t\tpc.getReaderContext().registerWithGeneratedName(jwtAuthenticationProvider)));\n\t\t}\n\t\tif (opaqueToken != null) {\n\t\t\tBeanDefinition opaqueTokenAuthenticationProvider = new OpaqueTokenBeanDefinitionParser().parse(opaqueToken,\n\t\t\t\t\tpc);\n\t\t\tthis.authenticationProviders.add(new RuntimeBeanReference(\n\t\t\t\t\tpc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));\n\t\t}\n\t\tBeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);\n\t\tBeanMetadataElement authenticationConverter = getAuthenticationConverter(oauth2ResourceServer);\n\t\tif (bearerTokenResolver != null && authenticationConverter != null) {\n\t\t\tthrow new BeanDefinitionStoreException(\n\t\t\t\t\t\"You cannot use bearer-token-ref and authentication-converter-ref in the same oauth2-resource-server element\");\n\t\t}\n\t\tif (bearerTokenResolver == null && authenticationConverter == null) {\n\t\t\tauthenticationConverter = new RootBeanDefinition(BearerTokenAuthenticationConverter.class);\n\t\t}\n\t\tBeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);\n\t\tBeanDefinition requestMatcher = buildRequestMatcher(bearerTokenResolver, authenticationConverter);\n\t\tthis.entryPoints.put(requestMatcher, authenticationEntryPoint);\n\t\tthis.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);\n\t\tthis.ignoreCsrfRequestMatchers.add(requestMatcher);\n\t\tBeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(BearerTokenAuthenticationFilter.class);\n\t\tBeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);\n\t\tfilterBuilder.addConstructorArgValue(authenticationManagerResolver);\n\t\tfilterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);\n\t\tfilterBuilder.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\tthis.authenticationFilterSecurityContextHolderStrategy);\n\n\t\tif (authenticationConverter != null) {\n\t\t\tfilterBuilder.addConstructorArgValue(authenticationConverter);\n\t\t}\n\t\tif (bearerTokenResolver != null) {\n\t\t\tfilterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);\n\t\t}\n\t\treturn filterBuilder.getBeanDefinition();\n\t}\n\n\tprivate BeanDefinition buildRequestMatcher(BeanMetadataElement bearerTokenResolver,\n\t\t\tBeanMetadataElement authenticationConverter) {\n\t\tif (bearerTokenResolver != null) {\n\t\t\tBeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(BearerTokenRequestMatcher.class);\n\t\t\trequestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);\n\t\t\treturn requestMatcherBuilder.getBeanDefinition();\n\t\t}\n\t\tBeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(BearerTokenAuthenticationRequestMatcher.class);\n\t\tif (authenticationConverter != null) {\n\t\t\trequestMatcherBuilder.addConstructorArgValue(authenticationConverter);\n\t\t}\n\t\treturn requestMatcherBuilder.getBeanDefinition();\n\t}\n\n\tvoid validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {\n\t\tif (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {\n\t\t\tif (jwt == null && opaqueToken == null) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(\"Didn't find authentication-manager-resolver-ref, \" + \"<jwt>, or <opaque-token>. \"\n\t\t\t\t\t\t\t+ \"Please select one.\", oauth2ResourceServer);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (jwt != null) {\n\t\t\tpc.getReaderContext()\n\t\t\t\t.error(\"Found <jwt> as well as authentication-manager-resolver-ref. Please select just one.\",\n\t\t\t\t\t\toauth2ResourceServer);\n\t\t}\n\t\tif (opaqueToken != null) {\n\t\t\tpc.getReaderContext()\n\t\t\t\t.error(\"Found <opaque-token> as well as authentication-manager-resolver-ref. Please select just one.\",\n\t\t\t\t\t\toauth2ResourceServer);\n\t\t}\n\t}\n\n\tBeanMetadataElement getAuthenticationManagerResolver(Element element) {\n\t\tString authenticationManagerResolverRef = element.getAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF);\n\t\tif (StringUtils.hasLength(authenticationManagerResolverRef)) {\n\t\t\treturn new RuntimeBeanReference(authenticationManagerResolverRef);\n\t\t}\n\t\tBeanDefinitionBuilder authenticationManagerResolver = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(StaticAuthenticationManagerResolver.class);\n\t\tauthenticationManagerResolver.addConstructorArgValue(this.authenticationManager);\n\t\treturn authenticationManagerResolver.getBeanDefinition();\n\t}\n\n\tBeanMetadataElement getBearerTokenResolver(Element element) {\n\t\tString bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);\n\t\tif (!StringUtils.hasLength(bearerTokenResolverRef)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new RuntimeBeanReference(bearerTokenResolverRef);\n\t}\n\n\tBeanMetadataElement getAuthenticationConverter(Element element) {\n\t\tString authenticationConverterRef = element.getAttribute(AUTHENTICATION_CONVERTER_REF);\n\t\tif (!StringUtils.hasLength(authenticationConverterRef)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new RuntimeBeanReference(authenticationConverterRef);\n\t}\n\n\tBeanMetadataElement getEntryPoint(Element element) {\n\t\tString entryPointRef = element.getAttribute(ENTRY_POINT_REF);\n\t\tif (!StringUtils.hasLength(entryPointRef)) {\n\t\t\treturn this.authenticationEntryPoint;\n\t\t}\n\t\treturn new RuntimeBeanReference(entryPointRef);\n\t}\n\n\tstatic final class JwtBeanDefinitionParser implements BeanDefinitionParser {\n\n\t\tstatic final String DECODER_REF = \"decoder-ref\";\n\n\t\tstatic final String JWK_SET_URI = \"jwk-set-uri\";\n\n\t\tstatic final String JWT_AUTHENTICATION_CONVERTER_REF = \"jwt-authentication-converter-ref\";\n\n\t\tstatic final String JWT_AUTHENTICATION_CONVERTER = \"jwtAuthenticationConverter\";\n\n\t\tJwtBeanDefinitionParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\t\tvalidateConfiguration(element, pc);\n\t\t\tBeanDefinitionBuilder jwtProviderBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(JwtAuthenticationProvider.class);\n\t\t\tjwtProviderBuilder.addConstructorArgValue(getDecoder(element));\n\t\t\tjwtProviderBuilder.addPropertyValue(JWT_AUTHENTICATION_CONVERTER, getJwtAuthenticationConverter(element));\n\t\t\treturn jwtProviderBuilder.getBeanDefinition();\n\t\t}\n\n\t\tvoid validateConfiguration(Element element, ParserContext pc) {\n\t\t\tboolean usesDecoder = element.hasAttribute(DECODER_REF);\n\t\t\tboolean usesJwkSetUri = element.hasAttribute(JWK_SET_URI);\n\t\t\tif (usesDecoder == usesJwkSetUri) {\n\t\t\t\tpc.getReaderContext().error(\"Please specify either decoder-ref or jwk-set-uri.\", element);\n\t\t\t}\n\t\t}\n\n\t\tObject getDecoder(Element element) {\n\t\t\tString decoderRef = element.getAttribute(DECODER_REF);\n\t\t\tif (StringUtils.hasLength(decoderRef)) {\n\t\t\t\treturn new RuntimeBeanReference(decoderRef);\n\t\t\t}\n\t\t\tBeanDefinitionBuilder builder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(NimbusJwtDecoderJwkSetUriFactoryBean.class);\n\t\t\tbuilder.addConstructorArgValue(element.getAttribute(JWK_SET_URI));\n\t\t\treturn builder.getBeanDefinition();\n\t\t}\n\n\t\tObject getJwtAuthenticationConverter(Element element) {\n\t\t\tString jwtDecoderRef = element.getAttribute(JWT_AUTHENTICATION_CONVERTER_REF);\n\t\t\treturn (StringUtils.hasLength(jwtDecoderRef)) ? new RuntimeBeanReference(jwtDecoderRef)\n\t\t\t\t\t: new JwtAuthenticationConverter();\n\t\t}\n\n\t}\n\n\tstatic final class OpaqueTokenBeanDefinitionParser implements BeanDefinitionParser {\n\n\t\tstatic final String INTROSPECTOR_REF = \"introspector-ref\";\n\n\t\tstatic final String INTROSPECTION_URI = \"introspection-uri\";\n\n\t\tstatic final String CLIENT_ID = \"client-id\";\n\n\t\tstatic final String CLIENT_SECRET = \"client-secret\";\n\n\t\tstatic final String AUTHENTICATION_CONVERTER_REF = \"authentication-converter-ref\";\n\n\t\tstatic final String AUTHENTICATION_CONVERTER = \"authenticationConverter\";\n\n\t\tOpaqueTokenBeanDefinitionParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\t\tvalidateConfiguration(element, pc);\n\t\t\tBeanMetadataElement introspector = getIntrospector(element);\n\t\t\tString authenticationConverterRef = element.getAttribute(AUTHENTICATION_CONVERTER_REF);\n\t\t\tBeanDefinitionBuilder opaqueTokenProviderBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(OpaqueTokenAuthenticationProvider.class);\n\t\t\topaqueTokenProviderBuilder.addConstructorArgValue(introspector);\n\t\t\tif (StringUtils.hasText(authenticationConverterRef)) {\n\t\t\t\topaqueTokenProviderBuilder.addPropertyReference(AUTHENTICATION_CONVERTER, authenticationConverterRef);\n\t\t\t}\n\t\t\treturn opaqueTokenProviderBuilder.getBeanDefinition();\n\t\t}\n\n\t\tvoid validateConfiguration(Element element, ParserContext pc) {\n\t\t\tboolean usesIntrospector = element.hasAttribute(INTROSPECTOR_REF);\n\t\t\tboolean usesEndpoint = element.hasAttribute(INTROSPECTION_URI) || element.hasAttribute(CLIENT_ID)\n\t\t\t\t\t|| element.hasAttribute(CLIENT_SECRET);\n\t\t\tif (usesIntrospector == usesEndpoint) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(\"Please specify either introspector-ref or all of \"\n\t\t\t\t\t\t\t+ \"introspection-uri, client-id, and client-secret.\", element);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (usesEndpoint) {\n\t\t\t\tif (!(element.hasAttribute(INTROSPECTION_URI) && element.hasAttribute(CLIENT_ID)\n\t\t\t\t\t\t&& element.hasAttribute(CLIENT_SECRET))) {\n\t\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t\t.error(\"Please specify introspection-uri, client-id, and client-secret together\", element);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tBeanMetadataElement getIntrospector(Element element) {\n\t\t\tString introspectorRef = element.getAttribute(INTROSPECTOR_REF);\n\t\t\tif (StringUtils.hasLength(introspectorRef)) {\n\t\t\t\treturn new RuntimeBeanReference(introspectorRef);\n\t\t\t}\n\t\t\tString introspectionUri = element.getAttribute(INTROSPECTION_URI);\n\t\t\tString clientId = element.getAttribute(CLIENT_ID);\n\t\t\tString clientSecret = element.getAttribute(CLIENT_SECRET);\n\t\t\tBeanDefinitionBuilder introspectorBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(SpringOpaqueTokenIntrospector.class);\n\t\t\tintrospectorBuilder.addConstructorArgValue(introspectionUri);\n\t\t\tintrospectorBuilder.addConstructorArgValue(clientId);\n\t\t\tintrospectorBuilder.addConstructorArgValue(clientSecret);\n\t\t\treturn introspectorBuilder.getBeanDefinition();\n\t\t}\n\n\t}\n\n\tstatic final class StaticAuthenticationManagerResolver\n\t\t\timplements AuthenticationManagerResolver<HttpServletRequest> {\n\n\t\tprivate final AuthenticationManager authenticationManager;\n\n\t\tStaticAuthenticationManagerResolver(AuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t}\n\n\t\t@Override\n\t\tpublic AuthenticationManager resolve(HttpServletRequest context) {\n\t\t\treturn this.authenticationManager;\n\t\t}\n\n\t}\n\n\tstatic final class NimbusJwtDecoderJwkSetUriFactoryBean implements FactoryBean<JwtDecoder> {\n\n\t\tprivate final String jwkSetUri;\n\n\t\tNimbusJwtDecoderJwkSetUriFactoryBean(String jwkSetUri) {\n\t\t\tthis.jwkSetUri = jwkSetUri;\n\t\t}\n\n\t\t@Override\n\t\tpublic JwtDecoder getObject() {\n\t\t\treturn NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn JwtDecoder.class;\n\t\t}\n\n\t}\n\n\tstatic final class BearerTokenRequestMatcher implements RequestMatcher {\n\n\t\tprivate final BearerTokenResolver bearerTokenResolver;\n\n\t\tBearerTokenRequestMatcher(BearerTokenResolver bearerTokenResolver) {\n\t\t\tAssert.notNull(bearerTokenResolver, \"bearerTokenResolver cannot be null\");\n\t\t\tthis.bearerTokenResolver = bearerTokenResolver;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\ttry {\n\t\t\t\treturn this.bearerTokenResolver.resolve(request) != null;\n\t\t\t}\n\t\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic final class BearerTokenAuthenticationRequestMatcher implements RequestMatcher {\n\n\t\tprivate final AuthenticationConverter authenticationConverter;\n\n\t\tBearerTokenAuthenticationRequestMatcher() {\n\t\t\tthis.authenticationConverter = new BearerTokenAuthenticationConverter();\n\t\t}\n\n\t\tBearerTokenAuthenticationRequestMatcher(AuthenticationConverter authenticationConverter) {\n\t\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\ttry {\n\t\t\t\treturn this.authenticationConverter.convert(request) != null;\n\t\t\t}\n\t\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/OrderDecorator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.core.Ordered;\n\n/**\n * Wrapper to provide ordering to a {@link BeanMetadataElement}.\n *\n * @author Rob Winch\n */\nclass OrderDecorator implements Ordered {\n\n\tfinal BeanMetadataElement bean;\n\n\tfinal int order;\n\n\tOrderDecorator(BeanMetadataElement bean, SecurityFilters filterOrder) {\n\t\tthis.bean = bean;\n\t\tthis.order = filterOrder.getOrder();\n\t}\n\n\tOrderDecorator(BeanMetadataElement bean, int order) {\n\t\tthis.bean = bean;\n\t\tthis.order = order;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.bean + \", order = \" + this.order;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/PathPatternRequestMatcherFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.util.StringUtils;\n\npublic final class PathPatternRequestMatcherFactoryBean\n\t\timplements FactoryBean<PathPatternRequestMatcher>, ApplicationContextAware, InitializingBean {\n\n\tprivate final String pattern;\n\n\tprivate String basePath;\n\n\tprivate HttpMethod method;\n\n\tprivate PathPatternRequestMatcher.Builder builder;\n\n\tPathPatternRequestMatcherFactoryBean(String pattern) {\n\t\tthis.pattern = pattern;\n\t}\n\n\tPathPatternRequestMatcherFactoryBean(String pattern, String method) {\n\t\tthis.pattern = pattern;\n\t\tthis.method = StringUtils.hasText(method) ? HttpMethod.valueOf(method) : null;\n\t}\n\n\t@Override\n\tpublic @Nullable PathPatternRequestMatcher getObject() throws Exception {\n\t\treturn this.builder.matcher(this.method, this.pattern);\n\t}\n\n\t@Override\n\tpublic @Nullable Class<?> getObjectType() {\n\t\treturn PathPatternRequestMatcher.class;\n\t}\n\n\tpublic void setBasePath(String basePath) {\n\t\tthis.basePath = basePath;\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\tthis.builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class)\n\t\t\t.getIfUnique(PathPatternRequestMatcher::withDefaults);\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tif (this.basePath != null) {\n\t\t\tthis.builder.basePath(this.basePath);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/PortMappingsBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Parses a port-mappings element, producing a single\n * {@link org.springframework.security.web.PortMapperImpl} bean.\n *\n * @author Luke Taylor\n */\nclass PortMappingsBeanDefinitionParser implements BeanDefinitionParser {\n\n\tpublic static final String ATT_HTTP_PORT = \"http\";\n\n\tpublic static final String ATT_HTTPS_PORT = \"https\";\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tRootBeanDefinition portMapper = new RootBeanDefinition(PortMapperImpl.class);\n\t\tportMapper.setSource(parserContext.extractSource(element));\n\t\tif (element != null) {\n\t\t\tList<Element> mappingElts = DomUtils.getChildElementsByTagName(element, Elements.PORT_MAPPING);\n\t\t\tif (mappingElts.isEmpty()) {\n\t\t\t\tparserContext.getReaderContext().error(\"No port-mapping child elements specified\", element);\n\t\t\t}\n\t\t\tMap mappings = new ManagedMap();\n\t\t\tfor (Element elt : mappingElts) {\n\t\t\t\tString httpPort = elt.getAttribute(ATT_HTTP_PORT);\n\t\t\t\tString httpsPort = elt.getAttribute(ATT_HTTPS_PORT);\n\t\t\t\tif (!StringUtils.hasText(httpPort)) {\n\t\t\t\t\tparserContext.getReaderContext().error(\"No http port supplied in port mapping\", elt);\n\t\t\t\t}\n\t\t\t\tif (!StringUtils.hasText(httpsPort)) {\n\t\t\t\t\tparserContext.getReaderContext().error(\"No https port supplied in port mapping\", elt);\n\t\t\t\t}\n\t\t\t\tmappings.put(httpPort, httpsPort);\n\t\t\t}\n\t\t\tportMapper.getPropertyValues().addPropertyValue(\"portMappings\", mappings);\n\t\t}\n\t\treturn portMapper;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/RememberMeBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.parsing.CompositeComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;\nimport org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Luke Taylor\n * @author Ben Alex\n * @author Rob Winch\n * @author Oliver Becker\n */\nclass RememberMeBeanDefinitionParser implements BeanDefinitionParser {\n\n\tstatic final String ATT_DATA_SOURCE = \"data-source-ref\";\n\n\tstatic final String ATT_SERVICES_REF = \"services-ref\";\n\n\tstatic final String ATT_SERVICES_ALIAS = \"services-alias\";\n\n\tstatic final String ATT_TOKEN_REPOSITORY = \"token-repository-ref\";\n\n\tstatic final String ATT_USER_SERVICE_REF = \"user-service-ref\";\n\n\tstatic final String ATT_SUCCESS_HANDLER_REF = \"authentication-success-handler-ref\";\n\n\tstatic final String ATT_TOKEN_VALIDITY = \"token-validity-seconds\";\n\n\tstatic final String ATT_SECURE_COOKIE = \"use-secure-cookie\";\n\n\tstatic final String ATT_FORM_REMEMBERME_PARAMETER = \"remember-me-parameter\";\n\n\tstatic final String ATT_REMEMBERME_COOKIE = \"remember-me-cookie\";\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final String key;\n\n\tprivate final BeanReference authenticationManager;\n\n\tprivate final BeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef;\n\n\tprivate String rememberMeServicesId;\n\n\tRememberMeBeanDefinitionParser(String key, BeanReference authenticationManager,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {\n\t\tthis.key = key;\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.authenticationFilterSecurityContextHolderStrategyRef = authenticationFilterSecurityContextHolderStrategyRef;\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),\n\t\t\t\tpc.extractSource(element));\n\t\tpc.pushContainingComponent(compositeDef);\n\t\tString tokenRepository = element.getAttribute(ATT_TOKEN_REPOSITORY);\n\t\tString dataSource = element.getAttribute(ATT_DATA_SOURCE);\n\t\tString userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);\n\t\tString successHandlerRef = element.getAttribute(ATT_SUCCESS_HANDLER_REF);\n\t\tString rememberMeServicesRef = element.getAttribute(ATT_SERVICES_REF);\n\t\tString tokenValiditySeconds = element.getAttribute(ATT_TOKEN_VALIDITY);\n\t\tString useSecureCookie = element.getAttribute(ATT_SECURE_COOKIE);\n\t\tString remembermeParameter = element.getAttribute(ATT_FORM_REMEMBERME_PARAMETER);\n\t\tString remembermeCookie = element.getAttribute(ATT_REMEMBERME_COOKIE);\n\t\tObject source = pc.extractSource(element);\n\t\tRootBeanDefinition services = null;\n\t\tboolean dataSourceSet = StringUtils.hasText(dataSource);\n\t\tboolean tokenRepoSet = StringUtils.hasText(tokenRepository);\n\t\tboolean servicesRefSet = StringUtils.hasText(rememberMeServicesRef);\n\t\tboolean userServiceSet = StringUtils.hasText(userServiceRef);\n\t\tboolean useSecureCookieSet = StringUtils.hasText(useSecureCookie);\n\t\tboolean tokenValiditySet = StringUtils.hasText(tokenValiditySeconds);\n\t\tboolean remembermeParameterSet = StringUtils.hasText(remembermeParameter);\n\t\tboolean remembermeCookieSet = StringUtils.hasText(remembermeCookie);\n\t\tif (servicesRefSet && (dataSourceSet || tokenRepoSet || userServiceSet || tokenValiditySet || useSecureCookieSet\n\t\t\t\t|| remembermeParameterSet || remembermeCookieSet)) {\n\t\t\tpc.getReaderContext()\n\t\t\t\t.error(ATT_SERVICES_REF + \" can't be used in combination with attributes \" + ATT_TOKEN_REPOSITORY + \",\"\n\t\t\t\t\t\t+ ATT_DATA_SOURCE + \", \" + ATT_USER_SERVICE_REF + \", \" + ATT_TOKEN_VALIDITY + \", \"\n\t\t\t\t\t\t+ ATT_SECURE_COOKIE + \", \" + ATT_FORM_REMEMBERME_PARAMETER + \" or \" + ATT_REMEMBERME_COOKIE,\n\t\t\t\t\t\tsource);\n\t\t}\n\t\tif (dataSourceSet && tokenRepoSet) {\n\t\t\tpc.getReaderContext()\n\t\t\t\t.error(\"Specify \" + ATT_TOKEN_REPOSITORY + \" or \" + ATT_DATA_SOURCE + \" but not both\", source);\n\t\t}\n\t\tboolean isPersistent = dataSourceSet | tokenRepoSet;\n\t\tif (isPersistent) {\n\t\t\tObject tokenRepo;\n\t\t\tservices = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class);\n\t\t\tif (tokenRepoSet) {\n\t\t\t\ttokenRepo = new RuntimeBeanReference(tokenRepository);\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttokenRepo = new RootBeanDefinition(JdbcTokenRepositoryImpl.class);\n\t\t\t\t((BeanDefinition) tokenRepo).getPropertyValues()\n\t\t\t\t\t.addPropertyValue(\"dataSource\", new RuntimeBeanReference(dataSource));\n\t\t\t}\n\t\t\tservices.getConstructorArgumentValues().addIndexedArgumentValue(2, tokenRepo);\n\t\t}\n\t\telse if (!servicesRefSet) {\n\t\t\tservices = new RootBeanDefinition(TokenBasedRememberMeServices.class);\n\t\t}\n\t\tString servicesName;\n\t\tif (services != null) {\n\t\t\tRootBeanDefinition uds = new RootBeanDefinition();\n\t\t\tuds.setFactoryBeanName(BeanIds.USER_DETAILS_SERVICE_FACTORY);\n\t\t\tuds.setFactoryMethodName(\"cachingUserDetailsService\");\n\t\t\tuds.getConstructorArgumentValues().addGenericArgumentValue(userServiceRef);\n\t\t\tservices.getConstructorArgumentValues().addGenericArgumentValue(this.key);\n\t\t\tservices.getConstructorArgumentValues().addGenericArgumentValue(uds);\n\t\t\t// tokenRepo is already added if it is a\n\t\t\t// PersistentTokenBasedRememberMeServices\n\t\t\tif (useSecureCookieSet) {\n\t\t\t\tservices.getPropertyValues().addPropertyValue(\"useSecureCookie\", Boolean.valueOf(useSecureCookie));\n\t\t\t}\n\t\t\tif (tokenValiditySet) {\n\t\t\t\tboolean isTokenValidityNegative = tokenValiditySeconds.startsWith(\"-\");\n\t\t\t\tif (isTokenValidityNegative && isPersistent) {\n\t\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t\t.error(ATT_TOKEN_VALIDITY + \" cannot be negative if using\"\n\t\t\t\t\t\t\t\t+ \" a persistent remember-me token repository\", source);\n\t\t\t\t}\n\t\t\t\tservices.getPropertyValues().addPropertyValue(\"tokenValiditySeconds\", tokenValiditySeconds);\n\t\t\t}\n\t\t\tif (remembermeParameterSet) {\n\t\t\t\tservices.getPropertyValues().addPropertyValue(\"parameter\", remembermeParameter);\n\t\t\t}\n\t\t\tif (remembermeCookieSet) {\n\t\t\t\tservices.getPropertyValues().addPropertyValue(\"cookieName\", remembermeCookie);\n\t\t\t}\n\t\t\tservices.setSource(source);\n\t\t\tservicesName = pc.getReaderContext().generateBeanName(services);\n\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(services, servicesName));\n\t\t}\n\t\telse {\n\t\t\tservicesName = rememberMeServicesRef;\n\t\t}\n\t\tif (StringUtils.hasText(element.getAttribute(ATT_SERVICES_ALIAS))) {\n\t\t\tpc.getRegistry().registerAlias(servicesName, element.getAttribute(ATT_SERVICES_ALIAS));\n\t\t}\n\t\tthis.rememberMeServicesId = servicesName;\n\t\tBeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(RememberMeAuthenticationFilter.class);\n\t\tfilter.getRawBeanDefinition().setSource(source);\n\t\tif (StringUtils.hasText(successHandlerRef)) {\n\t\t\tfilter.addPropertyReference(\"authenticationSuccessHandler\", successHandlerRef);\n\t\t}\n\t\tfilter.addConstructorArgValue(this.authenticationManager);\n\t\tfilter.addConstructorArgReference(servicesName);\n\t\tfilter.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\tthis.authenticationFilterSecurityContextHolderStrategyRef);\n\t\tpc.popAndRegisterContainingComponent();\n\t\treturn filter.getBeanDefinition();\n\t}\n\n\tString getRememberMeServicesId() {\n\t\treturn this.rememberMeServicesId;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/RequestMatcherFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n@Deprecated\npublic final class RequestMatcherFactoryBean implements FactoryBean<RequestMatcher>, ApplicationContextAware {\n\n\tprivate PathPatternRequestMatcher.Builder builder;\n\n\tprivate final HttpMethod method;\n\n\tprivate final String path;\n\n\tpublic RequestMatcherFactoryBean(String path) {\n\t\tthis(path, null);\n\t}\n\n\tpublic RequestMatcherFactoryBean(String path, HttpMethod method) {\n\t\tthis.method = method;\n\t\tthis.path = path;\n\t}\n\n\t@Override\n\tpublic RequestMatcher getObject() throws Exception {\n\t\treturn this.builder.matcher(this.method, this.path);\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\tthis.builder = context.getBean(PathPatternRequestMatcher.Builder.class);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * SAML 2.0 Login {@link BeanDefinitionParser}\n *\n * @author Marcus da Coregio\n * @since 5.7\n */\nfinal class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String DEFAULT_LOGIN_URI = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;\n\n\tprivate static final String DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL = \"/saml2/authenticate/{registrationId}\";\n\n\tprivate static final String ATT_LOGIN_PROCESSING_URL = \"login-processing-url\";\n\n\tprivate static final String ATT_LOGIN_PAGE = \"login-page\";\n\n\tprivate static final String ELT_RELYING_PARTY_REGISTRATION = \"relying-party-registration\";\n\n\tprivate static final String ELT_REGISTRATION_ID = \"registration-id\";\n\n\tprivate static final String ATT_AUTHENTICATION_FAILURE_HANDLER_REF = \"authentication-failure-handler-ref\";\n\n\tprivate static final String ATT_AUTHENTICATION_SUCCESS_HANDLER_REF = \"authentication-success-handler-ref\";\n\n\tprivate static final String ATT_AUTHENTICATION_MANAGER_REF = \"authentication-manager-ref\";\n\n\tprivate final List<BeanDefinition> csrfIgnoreRequestMatchers;\n\n\tprivate final BeanReference portMapper;\n\n\tprivate final BeanReference requestCache;\n\n\tprivate final boolean allowSessionCreation;\n\n\tprivate final BeanReference authenticationManager;\n\n\tprivate final BeanReference authenticationFilterSecurityContextRepositoryRef;\n\n\tprivate final List<BeanReference> authenticationProviders;\n\n\tprivate final Map<BeanDefinition, BeanMetadataElement> entryPoints;\n\n\tprivate String loginProcessingUrl = Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;\n\n\tprivate BeanDefinition saml2WebSsoAuthenticationRequestFilter;\n\n\tprivate BeanDefinition saml2AuthenticationUrlToProviderName;\n\n\tSaml2LoginBeanDefinitionParser(List<BeanDefinition> csrfIgnoreRequestMatchers, BeanReference portMapper,\n\t\t\tBeanReference requestCache, boolean allowSessionCreation, BeanReference authenticationManager,\n\t\t\tBeanReference authenticationFilterSecurityContextRepositoryRef, List<BeanReference> authenticationProviders,\n\t\t\tMap<BeanDefinition, BeanMetadataElement> entryPoints) {\n\t\tthis.csrfIgnoreRequestMatchers = csrfIgnoreRequestMatchers;\n\t\tthis.portMapper = portMapper;\n\t\tthis.requestCache = requestCache;\n\t\tthis.allowSessionCreation = allowSessionCreation;\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.authenticationFilterSecurityContextRepositoryRef = authenticationFilterSecurityContextRepositoryRef;\n\t\tthis.authenticationProviders = authenticationProviders;\n\t\tthis.entryPoints = entryPoints;\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tString loginProcessingUrl = element.getAttribute(ATT_LOGIN_PROCESSING_URL);\n\t\tif (StringUtils.hasText(loginProcessingUrl)) {\n\t\t\tthis.loginProcessingUrl = loginProcessingUrl;\n\t\t}\n\t\tBeanDefinition saml2LoginBeanConfig = BeanDefinitionBuilder.rootBeanDefinition(Saml2LoginBeanConfig.class)\n\t\t\t.getBeanDefinition();\n\t\tString saml2LoginBeanConfigId = pc.getReaderContext().generateBeanName(saml2LoginBeanConfig);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(saml2LoginBeanConfig, saml2LoginBeanConfigId));\n\t\tregisterDefaultCsrfOverride();\n\t\tBeanMetadataElement relyingPartyRegistrationRepository = Saml2LoginBeanDefinitionParserUtils\n\t\t\t.getRelyingPartyRegistrationRepository(element);\n\t\tBeanMetadataElement authenticationRequestRepository = Saml2LoginBeanDefinitionParserUtils\n\t\t\t.getAuthenticationRequestRepository(element);\n\t\tBeanMetadataElement authenticationRequestResolver = Saml2LoginBeanDefinitionParserUtils\n\t\t\t.getAuthenticationRequestResolver(element);\n\t\tif (authenticationRequestResolver == null) {\n\t\t\tauthenticationRequestResolver = Saml2LoginBeanDefinitionParserUtils\n\t\t\t\t.createDefaultAuthenticationRequestResolver(relyingPartyRegistrationRepository);\n\t\t}\n\t\tBeanMetadataElement authenticationConverter = Saml2LoginBeanDefinitionParserUtils\n\t\t\t.getAuthenticationConverter(element);\n\t\tif (authenticationConverter == null) {\n\t\t\tif (!this.loginProcessingUrl.contains(\"{registrationId}\")) {\n\t\t\t\tpc.getReaderContext().error(\"loginProcessingUrl must contain {registrationId} path variable\", element);\n\t\t\t}\n\t\t\tauthenticationConverter = Saml2LoginBeanDefinitionParserUtils\n\t\t\t\t.createDefaultAuthenticationConverter(relyingPartyRegistrationRepository);\n\t\t}\n\t\t// Configure the Saml2WebSsoAuthenticationFilter\n\t\tBeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(Saml2WebSsoAuthenticationFilter.class)\n\t\t\t.addConstructorArgValue(authenticationConverter)\n\t\t\t.addConstructorArgValue(this.loginProcessingUrl)\n\t\t\t.addPropertyValue(\"authenticationRequestRepository\", authenticationRequestRepository);\n\t\tresolveLoginPage(element, pc);\n\t\tresolveAuthenticationSuccessHandler(element, saml2WebSsoAuthenticationFilterBuilder);\n\t\tresolveAuthenticationFailureHandler(element, saml2WebSsoAuthenticationFilterBuilder);\n\t\tresolveAuthenticationManager(element, saml2WebSsoAuthenticationFilterBuilder);\n\t\tresolveSecurityContextRepository(element, saml2WebSsoAuthenticationFilterBuilder);\n\t\t// Configure the Saml2WebSsoAuthenticationRequestFilter\n\t\tthis.saml2WebSsoAuthenticationRequestFilter = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(Saml2WebSsoAuthenticationRequestFilter.class)\n\t\t\t.addConstructorArgValue(authenticationRequestResolver)\n\t\t\t.addPropertyValue(\"authenticationRequestRepository\", authenticationRequestRepository)\n\t\t\t.getBeanDefinition();\n\t\tBeanDefinition saml2AuthenticationProvider = Saml2LoginBeanDefinitionParserUtils.createAuthenticationProvider();\n\t\tthis.authenticationProviders.add(\n\t\t\t\tnew RuntimeBeanReference(pc.getReaderContext().registerWithGeneratedName(saml2AuthenticationProvider)));\n\t\tthis.saml2AuthenticationUrlToProviderName = BeanDefinitionBuilder.rootBeanDefinition(Map.class)\n\t\t\t.setFactoryMethodOnBean(\"getAuthenticationUrlToProviderName\", saml2LoginBeanConfigId)\n\t\t\t.getBeanDefinition();\n\t\treturn saml2WebSsoAuthenticationFilterBuilder.getBeanDefinition();\n\t}\n\n\tprivate void resolveAuthenticationManager(Element element,\n\t\t\tBeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {\n\t\tString authenticationManagerRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);\n\t\tif (StringUtils.hasText(authenticationManagerRef)) {\n\t\t\tsaml2WebSsoAuthenticationFilterBuilder.addPropertyReference(\"authenticationManager\",\n\t\t\t\t\tauthenticationManagerRef);\n\t\t}\n\t\telse {\n\t\t\tsaml2WebSsoAuthenticationFilterBuilder.addPropertyValue(\"authenticationManager\",\n\t\t\t\t\tthis.authenticationManager);\n\t\t}\n\t}\n\n\tprivate void resolveSecurityContextRepository(Element element,\n\t\t\tBeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {\n\t\tif (this.authenticationFilterSecurityContextRepositoryRef != null) {\n\t\t\tsaml2WebSsoAuthenticationFilterBuilder.addPropertyValue(\"securityContextRepository\",\n\t\t\t\t\tthis.authenticationFilterSecurityContextRepositoryRef);\n\t\t}\n\t}\n\n\tprivate void resolveLoginPage(Element element, ParserContext parserContext) {\n\t\tString loginPage = element.getAttribute(ATT_LOGIN_PAGE);\n\t\tObject source = parserContext.extractSource(element);\n\t\tBeanDefinition saml2LoginAuthenticationEntryPoint = null;\n\t\tif (StringUtils.hasText(loginPage)) {\n\t\t\tWebConfigUtils.validateHttpRedirect(loginPage, parserContext, source);\n\t\t\tsaml2LoginAuthenticationEntryPoint = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class)\n\t\t\t\t.addConstructorArgValue(loginPage)\n\t\t\t\t.addPropertyValue(\"portMapper\", this.portMapper)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\t\telse {\n\t\t\tMap<String, String> identityProviderUrlMap = getIdentityProviderUrlMap(element);\n\t\t\tif (identityProviderUrlMap.size() == 1) {\n\t\t\t\tString loginUrl = identityProviderUrlMap.entrySet().iterator().next().getKey();\n\t\t\t\tsaml2LoginAuthenticationEntryPoint = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class)\n\t\t\t\t\t.addConstructorArgValue(loginUrl)\n\t\t\t\t\t.addPropertyValue(\"portMapper\", this.portMapper)\n\t\t\t\t\t.getBeanDefinition();\n\t\t\t}\n\t\t}\n\t\tif (saml2LoginAuthenticationEntryPoint != null) {\n\t\t\tBeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class);\n\t\t\trequestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);\n\t\t\tBeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();\n\t\t\tthis.entryPoints.put(requestMatcher, saml2LoginAuthenticationEntryPoint);\n\t\t}\n\t}\n\n\tprivate void resolveAuthenticationFailureHandler(Element element,\n\t\t\tBeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {\n\t\tString authenticationFailureHandlerRef = element.getAttribute(ATT_AUTHENTICATION_FAILURE_HANDLER_REF);\n\t\tif (StringUtils.hasText(authenticationFailureHandlerRef)) {\n\t\t\tsaml2WebSsoAuthenticationFilterBuilder.addPropertyReference(\"authenticationFailureHandler\",\n\t\t\t\t\tauthenticationFailureHandlerRef);\n\t\t}\n\t\telse {\n\t\t\tBeanDefinitionBuilder failureHandlerBuilder = BeanDefinitionBuilder.rootBeanDefinition(\n\t\t\t\t\t\"org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler\");\n\t\t\tfailureHandlerBuilder.addConstructorArgValue(\n\t\t\t\t\tDEFAULT_LOGIN_URI + \"?\" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME);\n\t\t\tfailureHandlerBuilder.addPropertyValue(\"allowSessionCreation\", this.allowSessionCreation);\n\t\t\tsaml2WebSsoAuthenticationFilterBuilder.addPropertyValue(\"authenticationFailureHandler\",\n\t\t\t\t\tfailureHandlerBuilder.getBeanDefinition());\n\t\t}\n\t}\n\n\tprivate void resolveAuthenticationSuccessHandler(Element element,\n\t\t\tBeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {\n\t\tString authenticationSuccessHandlerRef = element.getAttribute(ATT_AUTHENTICATION_SUCCESS_HANDLER_REF);\n\t\tif (StringUtils.hasText(authenticationSuccessHandlerRef)) {\n\t\t\tsaml2WebSsoAuthenticationFilterBuilder.addPropertyReference(\"authenticationSuccessHandler\",\n\t\t\t\t\tauthenticationSuccessHandlerRef);\n\t\t}\n\t\telse {\n\t\t\tBeanDefinitionBuilder successHandlerBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(\n\t\t\t\t\t\t\"org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler\")\n\t\t\t\t.addPropertyValue(\"requestCache\", this.requestCache);\n\t\t\tsaml2WebSsoAuthenticationFilterBuilder.addPropertyValue(\"authenticationSuccessHandler\",\n\t\t\t\t\tsuccessHandlerBuilder.getBeanDefinition());\n\t\t}\n\t}\n\n\tprivate void registerDefaultCsrfOverride() {\n\t\tBeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class);\n\t\trequestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);\n\t\tBeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();\n\t\tthis.csrfIgnoreRequestMatchers.add(requestMatcher);\n\t}\n\n\tprivate Map<String, String> getIdentityProviderUrlMap(Element element) {\n\t\tMap<String, String> idps = new LinkedHashMap<>();\n\t\tElement relyingPartyRegistrationsElt = DomUtils.getChildElementByTagName(\n\t\t\t\telement.getOwnerDocument().getDocumentElement(), Elements.RELYING_PARTY_REGISTRATIONS);\n\t\tString authenticationRequestProcessingUrl = DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL;\n\t\tif (relyingPartyRegistrationsElt != null) {\n\t\t\tList<Element> relyingPartyRegList = DomUtils.getChildElementsByTagName(relyingPartyRegistrationsElt,\n\t\t\t\t\tELT_RELYING_PARTY_REGISTRATION);\n\t\t\tfor (Element relyingPartyReg : relyingPartyRegList) {\n\t\t\t\tString registrationId = relyingPartyReg.getAttribute(ELT_REGISTRATION_ID);\n\t\t\t\tidps.put(authenticationRequestProcessingUrl.replace(\"{registrationId}\", registrationId),\n\t\t\t\t\t\tregistrationId);\n\t\t\t}\n\t\t}\n\t\treturn idps;\n\t}\n\n\tBeanDefinition getSaml2WebSsoAuthenticationRequestFilter() {\n\t\treturn this.saml2WebSsoAuthenticationRequestFilter;\n\t}\n\n\tBeanDefinition getSaml2AuthenticationUrlToProviderName() {\n\t\treturn this.saml2AuthenticationUrlToProviderName;\n\t}\n\n\t/**\n\t * Wrapper bean class to provide configuration from applicationContext\n\t */\n\tpublic static class Saml2LoginBeanConfig implements ApplicationContextAware {\n\n\t\tprivate ApplicationContext context;\n\n\t\t@SuppressWarnings({ \"unchecked\", \"unused\" })\n\t\tMap<String, String> getAuthenticationUrlToProviderName() {\n\t\t\tIterable<RelyingPartyRegistration> relyingPartyRegistrations = null;\n\t\t\tRelyingPartyRegistrationRepository relyingPartyRegistrationRepository = this.context\n\t\t\t\t.getBean(RelyingPartyRegistrationRepository.class);\n\t\t\tResolvableType type = ResolvableType.forInstance(relyingPartyRegistrationRepository).as(Iterable.class);\n\t\t\tif (type != ResolvableType.NONE\n\t\t\t\t\t&& RelyingPartyRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {\n\t\t\t\trelyingPartyRegistrations = (Iterable<RelyingPartyRegistration>) relyingPartyRegistrationRepository;\n\t\t\t}\n\t\t\tif (relyingPartyRegistrations == null) {\n\t\t\t\treturn Collections.emptyMap();\n\t\t\t}\n\t\t\tString authenticationRequestProcessingUrl = DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL;\n\t\t\tMap<String, String> saml2AuthenticationUrlToProviderName = new HashMap<>();\n\t\t\trelyingPartyRegistrations.forEach((registration) -> saml2AuthenticationUrlToProviderName.put(\n\t\t\t\t\tauthenticationRequestProcessingUrl.replace(\"{registrationId}\", registration.getRegistrationId()),\n\t\t\t\t\tregistration.getRegistrationId()));\n\t\t\treturn saml2AuthenticationUrlToProviderName;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\t\tthis.context = context;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.opensaml.core.Version;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.AbstractBeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;\nimport org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;\nimport org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Marcus da Coregio\n * @since 5.7\n */\nfinal class Saml2LoginBeanDefinitionParserUtils {\n\n\tprivate static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith(\"5\");\n\n\tprivate static final String ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF = \"relying-party-registration-repository-ref\";\n\n\tprivate static final String ATT_AUTHENTICATION_REQUEST_REPOSITORY_REF = \"authentication-request-repository-ref\";\n\n\tprivate static final String ATT_AUTHENTICATION_REQUEST_RESOLVER_REF = \"authentication-request-resolver-ref\";\n\n\tprivate static final String ATT_AUTHENTICATION_CONVERTER = \"authentication-converter-ref\";\n\n\tprivate Saml2LoginBeanDefinitionParserUtils() {\n\t}\n\n\tstatic BeanMetadataElement getRelyingPartyRegistrationRepository(Element element) {\n\t\tString relyingPartyRegistrationRepositoryRef = element\n\t\t\t.getAttribute(ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF);\n\t\tif (StringUtils.hasText(relyingPartyRegistrationRepositoryRef)) {\n\t\t\treturn new RuntimeBeanReference(relyingPartyRegistrationRepositoryRef);\n\t\t}\n\t\treturn new RuntimeBeanReference(RelyingPartyRegistrationRepository.class);\n\t}\n\n\tstatic BeanMetadataElement getAuthenticationRequestRepository(Element element) {\n\t\tString authenticationRequestRepositoryRef = element.getAttribute(ATT_AUTHENTICATION_REQUEST_REPOSITORY_REF);\n\t\tif (StringUtils.hasText(authenticationRequestRepositoryRef)) {\n\t\t\treturn new RuntimeBeanReference(authenticationRequestRepositoryRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSaml2AuthenticationRequestRepository.class)\n\t\t\t.getBeanDefinition();\n\t}\n\n\tstatic BeanMetadataElement getAuthenticationRequestResolver(Element element) {\n\t\tString authenticationRequestContextResolver = element.getAttribute(ATT_AUTHENTICATION_REQUEST_RESOLVER_REF);\n\t\tif (StringUtils.hasText(authenticationRequestContextResolver)) {\n\t\t\treturn new RuntimeBeanReference(authenticationRequestContextResolver);\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic BeanMetadataElement createDefaultAuthenticationRequestResolver(\n\t\t\tBeanMetadataElement relyingPartyRegistrationRepository) {\n\t\tBeanMetadataElement defaultRelyingPartyRegistrationResolver = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)\n\t\t\t.addConstructorArgValue(relyingPartyRegistrationRepository)\n\t\t\t.getBeanDefinition();\n\t\tif (USE_OPENSAML_5) {\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(OpenSaml5AuthenticationRequestResolver.class)\n\t\t\t\t.addConstructorArgValue(defaultRelyingPartyRegistrationResolver)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n\tstatic BeanDefinition createAuthenticationProvider() {\n\t\tif (USE_OPENSAML_5) {\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(OpenSaml5AuthenticationProvider.class).getBeanDefinition();\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n\tstatic BeanMetadataElement getAuthenticationConverter(Element element) {\n\t\tString authenticationConverter = element.getAttribute(ATT_AUTHENTICATION_CONVERTER);\n\t\tif (StringUtils.hasText(authenticationConverter)) {\n\t\t\treturn new RuntimeBeanReference(authenticationConverter);\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic BeanDefinition createDefaultAuthenticationConverter(BeanMetadataElement relyingPartyRegistrationRepository) {\n\t\tAbstractBeanDefinition resolver = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)\n\t\t\t.addConstructorArgValue(relyingPartyRegistrationRepository)\n\t\t\t.getBeanDefinition();\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(Saml2AuthenticationTokenConverter.class)\n\t\t\t.addConstructorArgValue(resolver)\n\t\t\t.getBeanDefinition();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;\nimport org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2RelyingPartyInitiatedLogoutSuccessHandler;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.ParameterRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * SAML 2.0 Single Logout {@link BeanDefinitionParser}\n *\n * @author Marcus da Coregio\n * @since 5.7\n */\nfinal class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ATT_LOGOUT_REQUEST_URL = \"logout-request-url\";\n\n\tprivate static final String ATT_LOGOUT_RESPONSE_URL = \"logout-response-url\";\n\n\tprivate static final String ATT_LOGOUT_URL = \"logout-url\";\n\n\tprivate List<BeanMetadataElement> logoutHandlers;\n\n\tprivate String logoutUrl = \"/logout\";\n\n\tprivate String logoutRequestUrl = \"/logout/saml2/slo\";\n\n\tprivate String logoutResponseUrl = \"/logout/saml2/slo\";\n\n\tprivate BeanMetadataElement logoutSuccessHandler;\n\n\tprivate BeanDefinition logoutRequestFilter;\n\n\tprivate BeanDefinition logoutResponseFilter;\n\n\tprivate BeanDefinition logoutFilter;\n\n\tprivate BeanMetadataElement authenticationFilterSecurityContextHolderStrategy;\n\n\tSaml2LogoutBeanDefinitionParser(ManagedList<BeanMetadataElement> logoutHandlers,\n\t\t\tBeanMetadataElement logoutSuccessHandler,\n\t\t\tBeanMetadataElement authenticationFilterSecurityContextHolderStrategy) {\n\t\tthis.logoutHandlers = logoutHandlers;\n\t\tthis.logoutSuccessHandler = logoutSuccessHandler;\n\t\tthis.authenticationFilterSecurityContextHolderStrategy = authenticationFilterSecurityContextHolderStrategy;\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tString logoutUrl = element.getAttribute(ATT_LOGOUT_URL);\n\t\tif (StringUtils.hasText(logoutUrl)) {\n\t\t\tthis.logoutUrl = logoutUrl;\n\t\t}\n\t\tString logoutRequestUrl = element.getAttribute(ATT_LOGOUT_REQUEST_URL);\n\t\tif (StringUtils.hasText(logoutRequestUrl)) {\n\t\t\tthis.logoutRequestUrl = logoutRequestUrl;\n\t\t}\n\t\tString logoutResponseUrl = element.getAttribute(ATT_LOGOUT_RESPONSE_URL);\n\t\tif (StringUtils.hasText(logoutResponseUrl)) {\n\t\t\tthis.logoutResponseUrl = logoutResponseUrl;\n\t\t}\n\t\tWebConfigUtils.validateHttpRedirect(this.logoutUrl, pc, element);\n\t\tWebConfigUtils.validateHttpRedirect(this.logoutRequestUrl, pc, element);\n\t\tWebConfigUtils.validateHttpRedirect(this.logoutResponseUrl, pc, element);\n\t\tif (CollectionUtils.isEmpty(this.logoutHandlers)) {\n\t\t\tthis.logoutHandlers = createDefaultLogoutHandlers();\n\t\t}\n\t\tif (this.logoutSuccessHandler == null) {\n\t\t\tthis.logoutSuccessHandler = createDefaultLogoutSuccessHandler();\n\t\t}\n\t\tBeanMetadataElement relyingPartyRegistrationRepository = Saml2LogoutBeanDefinitionParserUtils\n\t\t\t.getRelyingPartyRegistrationRepository(element);\n\t\tBeanMetadataElement registrations = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)\n\t\t\t.addConstructorArgValue(relyingPartyRegistrationRepository)\n\t\t\t.getBeanDefinition();\n\t\tBeanMetadataElement logoutResponseResolver = Saml2LogoutBeanDefinitionParserUtils\n\t\t\t.getLogoutResponseResolver(element, registrations);\n\t\tBeanMetadataElement logoutRequestValidator = Saml2LogoutBeanDefinitionParserUtils\n\t\t\t.getLogoutRequestValidator(element);\n\t\tBeanMetadataElement logoutRequestMatcher = createSaml2LogoutRequestMatcher();\n\t\tthis.logoutRequestFilter = BeanDefinitionBuilder.rootBeanDefinition(Saml2LogoutRequestFilter.class)\n\t\t\t.addConstructorArgValue(registrations)\n\t\t\t.addConstructorArgValue(logoutRequestValidator)\n\t\t\t.addConstructorArgValue(logoutResponseResolver)\n\t\t\t.addConstructorArgValue(this.logoutHandlers)\n\t\t\t.addPropertyValue(\"logoutRequestMatcher\", logoutRequestMatcher)\n\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", this.authenticationFilterSecurityContextHolderStrategy)\n\t\t\t.getBeanDefinition();\n\t\tBeanMetadataElement logoutResponseValidator = Saml2LogoutBeanDefinitionParserUtils\n\t\t\t.getLogoutResponseValidator(element);\n\t\tBeanMetadataElement logoutRequestRepository = Saml2LogoutBeanDefinitionParserUtils\n\t\t\t.getLogoutRequestRepository(element);\n\t\tBeanMetadataElement logoutResponseMatcher = createSaml2LogoutResponseMatcher();\n\t\tthis.logoutResponseFilter = BeanDefinitionBuilder.rootBeanDefinition(Saml2LogoutResponseFilter.class)\n\t\t\t.addConstructorArgValue(registrations)\n\t\t\t.addConstructorArgValue(logoutResponseValidator)\n\t\t\t.addConstructorArgValue(this.logoutSuccessHandler)\n\t\t\t.addPropertyValue(\"logoutRequestMatcher\", logoutResponseMatcher)\n\t\t\t.addPropertyValue(\"logoutRequestRepository\", logoutRequestRepository)\n\t\t\t.getBeanDefinition();\n\t\tBeanMetadataElement logoutRequestResolver = Saml2LogoutBeanDefinitionParserUtils\n\t\t\t.getLogoutRequestResolver(element, registrations);\n\t\tBeanMetadataElement saml2LogoutRequestSuccessHandler = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(Saml2RelyingPartyInitiatedLogoutSuccessHandler.class)\n\t\t\t.addConstructorArgValue(logoutRequestResolver)\n\t\t\t.addPropertyValue(\"logoutRequestRepository\", logoutRequestRepository)\n\t\t\t.getBeanDefinition();\n\t\tthis.logoutFilter = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class)\n\t\t\t.addConstructorArgValue(saml2LogoutRequestSuccessHandler)\n\t\t\t.addConstructorArgValue(this.logoutHandlers)\n\t\t\t.addPropertyValue(\"logoutRequestMatcher\", createLogoutRequestMatcher())\n\t\t\t.getBeanDefinition();\n\t\treturn null;\n\t}\n\n\tprivate static List<BeanMetadataElement> createDefaultLogoutHandlers() {\n\t\tList<BeanMetadataElement> handlers = new ManagedList<>();\n\t\thandlers.add(BeanDefinitionBuilder.rootBeanDefinition(SecurityContextLogoutHandler.class).getBeanDefinition());\n\t\thandlers.add(BeanDefinitionBuilder.rootBeanDefinition(LogoutSuccessEventPublishingLogoutHandler.class)\n\t\t\t.getBeanDefinition());\n\t\treturn handlers;\n\t}\n\n\tprivate static BeanMetadataElement createDefaultLogoutSuccessHandler() {\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(SimpleUrlLogoutSuccessHandler.class)\n\t\t\t.addPropertyValue(\"defaultTargetUrl\", \"/login?logout\")\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement createLogoutRequestMatcher() {\n\t\tBeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(RequestMatcherFactoryBean.class)\n\t\t\t.addConstructorArgValue(this.logoutUrl)\n\t\t\t.addConstructorArgValue(\"POST\")\n\t\t\t.getBeanDefinition();\n\t\tBeanMetadataElement saml2Matcher = BeanDefinitionBuilder.rootBeanDefinition(Saml2RequestMatcher.class)\n\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", this.authenticationFilterSecurityContextHolderStrategy)\n\t\t\t.getBeanDefinition();\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)\n\t\t\t.addConstructorArgValue(toManagedList(logoutMatcher, saml2Matcher))\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement createSaml2LogoutRequestMatcher() {\n\t\tBeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class)\n\t\t\t.addConstructorArgValue(this.logoutRequestUrl)\n\t\t\t.getBeanDefinition();\n\t\tBeanMetadataElement saml2RequestMatcher = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(ParameterRequestMatcher.class)\n\t\t\t.addConstructorArgValue(\"SAMLRequest\")\n\t\t\t.getBeanDefinition();\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)\n\t\t\t.addConstructorArgValue(toManagedList(logoutRequestMatcher, saml2RequestMatcher))\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement createSaml2LogoutResponseMatcher() {\n\t\tBeanMetadataElement logoutResponseMatcher = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestMatcherFactoryBean.class)\n\t\t\t.addConstructorArgValue(this.logoutResponseUrl)\n\t\t\t.getBeanDefinition();\n\t\tBeanMetadataElement saml2ResponseMatcher = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(ParameterRequestMatcher.class)\n\t\t\t.addConstructorArgValue(\"SAMLResponse\")\n\t\t\t.getBeanDefinition();\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)\n\t\t\t.addConstructorArgValue(toManagedList(logoutResponseMatcher, saml2ResponseMatcher))\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate static List<BeanMetadataElement> toManagedList(BeanMetadataElement... elements) {\n\t\tList<BeanMetadataElement> managedList = new ManagedList<>();\n\t\tmanagedList.addAll(Arrays.asList(elements));\n\t\treturn managedList;\n\t}\n\n\tBeanDefinition getLogoutRequestFilter() {\n\t\treturn this.logoutRequestFilter;\n\t}\n\n\tBeanDefinition getLogoutResponseFilter() {\n\t\treturn this.logoutResponseFilter;\n\t}\n\n\tBeanDefinition getLogoutFilter() {\n\t\treturn this.logoutFilter;\n\t}\n\n\tpublic static class Saml2RequestMatcher implements RequestMatcher {\n\n\t\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t\t.getContextHolderStrategy();\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (authentication == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn authentication instanceof Saml2Authentication;\n\t\t}\n\n\t\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.opensaml.core.Version;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutResponseValidator;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutRequestResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutResponseResolver;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Marcus da Coregio\n * @since 5.7\n */\nfinal class Saml2LogoutBeanDefinitionParserUtils {\n\n\tprivate static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith(\"5\");\n\n\tprivate static final String ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF = \"relying-party-registration-repository-ref\";\n\n\tprivate static final String ATT_LOGOUT_REQUEST_VALIDATOR_REF = \"logout-request-validator-ref\";\n\n\tprivate static final String ATT_LOGOUT_REQUEST_REPOSITORY_REF = \"logout-request-repository-ref\";\n\n\tprivate static final String ATT_LOGOUT_REQUEST_RESOLVER_REF = \"logout-request-resolver-ref\";\n\n\tprivate static final String ATT_LOGOUT_RESPONSE_RESOLVER_REF = \"logout-response-resolver-ref\";\n\n\tprivate static final String ATT_LOGOUT_RESPONSE_VALIDATOR_REF = \"logout-response-validator-ref\";\n\n\tprivate Saml2LogoutBeanDefinitionParserUtils() {\n\t}\n\n\tstatic BeanMetadataElement getRelyingPartyRegistrationRepository(Element element) {\n\t\tString relyingPartyRegistrationRepositoryRef = element\n\t\t\t.getAttribute(ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF);\n\t\tif (StringUtils.hasText(relyingPartyRegistrationRepositoryRef)) {\n\t\t\treturn new RuntimeBeanReference(relyingPartyRegistrationRepositoryRef);\n\t\t}\n\t\treturn new RuntimeBeanReference(RelyingPartyRegistrationRepository.class);\n\t}\n\n\tstatic BeanMetadataElement getLogoutResponseResolver(Element element, BeanMetadataElement registrations) {\n\t\tString logoutResponseResolver = element.getAttribute(ATT_LOGOUT_RESPONSE_RESOLVER_REF);\n\t\tif (StringUtils.hasText(logoutResponseResolver)) {\n\t\t\treturn new RuntimeBeanReference(logoutResponseResolver);\n\t\t}\n\t\tif (USE_OPENSAML_5) {\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(OpenSaml5LogoutResponseResolver.class)\n\t\t\t\t.addConstructorArgValue(registrations)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n\tstatic BeanMetadataElement getLogoutRequestValidator(Element element) {\n\t\tString logoutRequestValidator = element.getAttribute(ATT_LOGOUT_REQUEST_VALIDATOR_REF);\n\t\tif (StringUtils.hasText(logoutRequestValidator)) {\n\t\t\treturn new RuntimeBeanReference(logoutRequestValidator);\n\t\t}\n\t\tif (USE_OPENSAML_5) {\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(OpenSaml5LogoutRequestValidator.class).getBeanDefinition();\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n\tstatic BeanMetadataElement getLogoutResponseValidator(Element element) {\n\t\tString logoutResponseValidator = element.getAttribute(ATT_LOGOUT_RESPONSE_VALIDATOR_REF);\n\t\tif (StringUtils.hasText(logoutResponseValidator)) {\n\t\t\treturn new RuntimeBeanReference(logoutResponseValidator);\n\t\t}\n\t\tif (USE_OPENSAML_5) {\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(OpenSaml5LogoutResponseValidator.class).getBeanDefinition();\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n\tstatic BeanMetadataElement getLogoutRequestRepository(Element element) {\n\t\tString logoutRequestRepository = element.getAttribute(ATT_LOGOUT_REQUEST_REPOSITORY_REF);\n\t\tif (StringUtils.hasText(logoutRequestRepository)) {\n\t\t\treturn new RuntimeBeanReference(logoutRequestRepository);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(HttpSessionLogoutRequestRepository.class).getBeanDefinition();\n\t}\n\n\tstatic BeanMetadataElement getLogoutRequestResolver(Element element, BeanMetadataElement registrations) {\n\t\tString logoutRequestResolver = element.getAttribute(ATT_LOGOUT_REQUEST_RESOLVER_REF);\n\t\tif (StringUtils.hasText(logoutRequestResolver)) {\n\t\t\treturn new RuntimeBeanReference(logoutRequestResolver);\n\t\t}\n\t\tif (USE_OPENSAML_5) {\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(OpenSaml5LogoutRequestResolver.class)\n\t\t\t\t.addConstructorArgValue(registrations)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Spring Security does not support OpenSAML \" + Version.getVersion() + \". Please use OpenSAML 5\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/SecurityFilters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\n/**\n * Stores the default order numbers of all Spring Security filters for use in\n * configuration.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @author Evgeniy Cheban\n */\n\nenum SecurityFilters {\n\n\tFIRST(Integer.MIN_VALUE),\n\n\tDISABLE_ENCODE_URL_FILTER,\n\n\tFORCE_EAGER_SESSION_FILTER,\n\n\t@Deprecated\n\tCHANNEL_FILTER,\n\n\tHTTPS_REDIRECT_FILTER,\n\n\tSECURITY_CONTEXT_FILTER,\n\n\tCONCURRENT_SESSION_FILTER,\n\n\tWEB_ASYNC_MANAGER_FILTER,\n\n\tHEADERS_FILTER,\n\n\tCORS_FILTER,\n\n\tSAML2_LOGOUT_REQUEST_FILTER,\n\n\tSAML2_LOGOUT_RESPONSE_FILTER,\n\n\tCSRF_FILTER,\n\n\tSAML2_LOGOUT_FILTER,\n\n\tLOGOUT_FILTER,\n\n\tOAUTH2_AUTHORIZATION_REQUEST_FILTER,\n\n\tSAML2_AUTHENTICATION_REQUEST_FILTER,\n\n\tX509_FILTER,\n\n\tPRE_AUTH_FILTER,\n\n\tCAS_FILTER,\n\n\tOAUTH2_LOGIN_FILTER,\n\n\tSAML2_AUTHENTICATION_FILTER,\n\n\tFORM_LOGIN_FILTER,\n\n\tDEFAULT_RESOURCES_FILTER,\n\n\tLOGIN_PAGE_FILTER,\n\n\tLOGOUT_PAGE_FILTER,\n\n\tDIGEST_AUTH_FILTER,\n\n\tBEARER_TOKEN_AUTH_FILTER,\n\n\tBASIC_AUTH_FILTER,\n\n\tREQUEST_CACHE_FILTER,\n\n\tSERVLET_API_SUPPORT_FILTER,\n\n\tJAAS_API_SUPPORT_FILTER,\n\n\tREMEMBER_ME_FILTER,\n\n\tANONYMOUS_FILTER,\n\n\tOAUTH2_AUTHORIZATION_CODE_GRANT_FILTER,\n\n\tWELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER,\n\n\tSESSION_MANAGEMENT_FILTER,\n\n\tEXCEPTION_TRANSLATION_FILTER,\n\n\tFILTER_SECURITY_INTERCEPTOR,\n\n\tSWITCH_USER_FILTER,\n\n\tLAST(Integer.MAX_VALUE);\n\n\tprivate static final int INTERVAL = 100;\n\n\tprivate final int order;\n\n\tSecurityFilters() {\n\t\tthis.order = ordinal() * INTERVAL;\n\t}\n\n\tSecurityFilters(int order) {\n\t\tthis.order = order;\n\t}\n\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/SessionCreationPolicy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Specifies the various session creation policies for Spring Security.\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic enum SessionCreationPolicy {\n\n\t/**\n\t * Always create an {@link HttpSession}\n\t */\n\tALWAYS,\n\n\t/**\n\t * Spring Security will never create an {@link HttpSession}, but will use the\n\t * {@link HttpSession} if it already exists\n\t */\n\tNEVER,\n\n\t/**\n\t * Spring Security will only create an {@link HttpSession} if required\n\t */\n\tIF_REQUIRED,\n\n\t/**\n\t * Spring Security will never create an {@link HttpSession} and it will never use it\n\t * to obtain the {@link SecurityContext}\n\t */\n\tSTATELESS\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/UserDetailsServiceFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Map;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.HierarchicalBeanFactory;\nimport org.springframework.beans.factory.ListableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.ApplicationContextException;\nimport org.springframework.security.authentication.CachingUserDetailsService;\nimport org.springframework.security.config.authentication.AbstractUserDetailsServiceBeanDefinitionParser;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.util.StringUtils;\n\n/**\n * Bean used to lookup a named UserDetailsService or AuthenticationUserDetailsService.\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic class UserDetailsServiceFactoryBean implements ApplicationContextAware {\n\n\tprivate ApplicationContext beanFactory;\n\n\tUserDetailsService userDetailsService(String id) {\n\t\tif (!StringUtils.hasText(id)) {\n\t\t\treturn getUserDetailsService();\n\t\t}\n\t\treturn (UserDetailsService) this.beanFactory.getBean(id);\n\t}\n\n\tUserDetailsService cachingUserDetailsService(String id) {\n\t\tif (!StringUtils.hasText(id)) {\n\t\t\treturn getUserDetailsService();\n\t\t}\n\t\t// Overwrite with the caching version if available\n\t\tString cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;\n\t\tif (this.beanFactory.containsBeanDefinition(cachingId)) {\n\t\t\treturn (UserDetailsService) this.beanFactory.getBean(cachingId);\n\t\t}\n\t\treturn (UserDetailsService) this.beanFactory.getBean(id);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tAuthenticationUserDetailsService authenticationUserDetailsService(String name) {\n\t\tUserDetailsService uds;\n\t\tif (!StringUtils.hasText(name)) {\n\t\t\tMap<String, ?> beans = getBeansOfType(AuthenticationUserDetailsService.class);\n\t\t\tif (!beans.isEmpty()) {\n\t\t\t\tif (beans.size() > 1) {\n\t\t\t\t\tthrow new ApplicationContextException(\"More than one AuthenticationUserDetailsService registered.\"\n\t\t\t\t\t\t\t+ \" Please use a specific Id reference.\");\n\t\t\t\t}\n\t\t\t\treturn (AuthenticationUserDetailsService) beans.values().toArray()[0];\n\t\t\t}\n\t\t\tuds = getUserDetailsService();\n\t\t}\n\t\telse {\n\t\t\tObject bean = this.beanFactory.getBean(name);\n\t\t\tif (bean instanceof AuthenticationUserDetailsService) {\n\t\t\t\treturn (AuthenticationUserDetailsService) bean;\n\t\t\t}\n\t\t\telse if (bean instanceof UserDetailsService) {\n\t\t\t\tuds = cachingUserDetailsService(name);\n\t\t\t\tif (uds == null) {\n\t\t\t\t\tuds = (UserDetailsService) bean;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new ApplicationContextException(\n\t\t\t\t\t\t\"Bean '\" + name + \"' must be a UserDetailsService or an\" + \" AuthenticationUserDetailsService\");\n\t\t\t}\n\t\t}\n\t\treturn new UserDetailsByNameServiceWrapper(uds);\n\t}\n\n\t/**\n\t * Obtains a user details service for use in RememberMeServices etc. Will return a\n\t * caching version if available so should not be used for beans which need to separate\n\t * the two.\n\t */\n\tprivate UserDetailsService getUserDetailsService() {\n\t\tMap<String, ?> beans = getBeansOfType(CachingUserDetailsService.class);\n\t\tif (beans.isEmpty()) {\n\t\t\tbeans = getBeansOfType(UserDetailsService.class);\n\t\t}\n\t\tif (beans.isEmpty()) {\n\t\t\tthrow new ApplicationContextException(\"No UserDetailsService registered.\");\n\t\t}\n\t\tif (beans.size() > 1) {\n\t\t\tthrow new ApplicationContextException(\"More than one UserDetailsService registered. Please \"\n\t\t\t\t\t+ \"use a specific Id reference in <remember-me/> or <x509 /> elements.\");\n\t\t}\n\t\treturn (UserDetailsService) beans.values().toArray()[0];\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext beanFactory) throws BeansException {\n\t\tthis.beanFactory = beanFactory;\n\t}\n\n\tprivate Map<String, ?> getBeansOfType(Class<?> type) {\n\t\tMap<String, ?> beans = this.beanFactory.getBeansOfType(type);\n\t\t// Check ancestor bean factories if they exist and the current one has none of the\n\t\t// required type\n\t\tBeanFactory parent = this.beanFactory.getParentBeanFactory();\n\t\twhile (parent != null && beans.isEmpty()) {\n\t\t\tif (parent instanceof ListableBeanFactory) {\n\t\t\t\tbeans = ((ListableBeanFactory) parent).getBeansOfType(type);\n\t\t\t}\n\t\t\tif (parent instanceof HierarchicalBeanFactory) {\n\t\t\t\tparent = ((HierarchicalBeanFactory) parent).getParentBeanFactory();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn beans;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/WebConfigUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Utility methods used internally by the Spring Security http namespace configuration\n * code.\n *\n * @author Luke Taylor\n * @author Ben Alex\n */\nfinal class WebConfigUtils {\n\n\tprivate WebConfigUtils() {\n\t}\n\n\tstatic int countNonEmpty(String[] objects) {\n\t\tint nonNulls = 0;\n\t\tfor (String object : objects) {\n\t\t\tif (StringUtils.hasText(object)) {\n\t\t\t\tnonNulls++;\n\t\t\t}\n\t\t}\n\t\treturn nonNulls;\n\t}\n\n\t/**\n\t * Checks the value of an XML attribute which represents a redirect URL. If not empty\n\t * or starting with \"$\" (potential placeholder), or starting with \"#\" (potential\n\t * SpEL), \"/\" or \"http\" it will raise an error.\n\t */\n\tstatic void validateHttpRedirect(String url, ParserContext pc, Object source) {\n\t\tif (!StringUtils.hasText(url) || UrlUtils.isValidRedirectUrl(url) || url.startsWith(\"$\")\n\t\t\t\t|| url.startsWith(\"#\")) {\n\t\t\treturn;\n\t\t}\n\t\tpc.getReaderContext().warning(url + \" is not a valid redirect URL (must start with '/' or http(s))\", source);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.web.RequestMatcherRedirectFilter;\nimport org.springframework.util.StringUtils;\n\n/**\n * The bean definition parser for a Well-Known URL for Changing Passwords.\n *\n * @author Evgeniy Cheban\n * @since 5.6\n */\npublic final class WellKnownChangePasswordBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String WELL_KNOWN_CHANGE_PASSWORD_PATTERN = \"/.well-known/change-password\";\n\n\tprivate static final String DEFAULT_CHANGE_PASSWORD_PAGE = \"/change-password\";\n\n\tprivate static final String ATT_CHANGE_PASSWORD_PAGE = \"change-password-page\";\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tBeanDefinition requestMatcher = BeanDefinitionBuilder.rootBeanDefinition(RequestMatcherFactoryBean.class)\n\t\t\t.addConstructorArgValue(WELL_KNOWN_CHANGE_PASSWORD_PATTERN)\n\t\t\t.getBeanDefinition();\n\t\tBeanDefinition changePasswordFilter = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(RequestMatcherRedirectFilter.class)\n\t\t\t.addConstructorArgValue(requestMatcher)\n\t\t\t.addConstructorArgValue(getChangePasswordPage(element))\n\t\t\t.getBeanDefinition();\n\t\tparserContext.getReaderContext().registerWithGeneratedName(changePasswordFilter);\n\t\treturn changePasswordFilter;\n\t}\n\n\tprivate String getChangePasswordPage(Element element) {\n\t\tString changePasswordPage = element.getAttribute(ATT_CHANGE_PASSWORD_PAGE);\n\t\treturn (StringUtils.hasText(changePasswordPage) ? changePasswordPage : DEFAULT_CHANGE_PASSWORD_PAGE);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/http/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Parsing of the &lt;http&gt; namespace element.\n */\npackage org.springframework.security.config.http;\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/AbstractLdapAuthenticationManagerFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;\nimport org.springframework.security.ldap.authentication.LdapAuthenticationProvider;\nimport org.springframework.security.ldap.search.FilterBasedLdapUserSearch;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.UserDetailsContextMapper;\n\n/**\n * Creates an {@link AuthenticationManager} that can perform LDAP authentication.\n *\n * @author Eleftheria Stein\n * @since 5.7\n */\npublic abstract class AbstractLdapAuthenticationManagerFactory<T extends AbstractLdapAuthenticator> {\n\n\tAbstractLdapAuthenticationManagerFactory(BaseLdapPathContextSource contextSource) {\n\t\tthis.contextSource = contextSource;\n\t}\n\n\tprivate BaseLdapPathContextSource contextSource;\n\n\tprivate String[] userDnPatterns;\n\n\tprivate LdapAuthoritiesPopulator ldapAuthoritiesPopulator;\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper;\n\n\tprivate UserDetailsContextMapper userDetailsContextMapper;\n\n\tprivate String userSearchFilter;\n\n\tprivate String userSearchBase = \"\";\n\n\t/**\n\t * Sets the {@link BaseLdapPathContextSource} used to perform LDAP authentication.\n\t * @param contextSource the {@link BaseLdapPathContextSource} used to perform LDAP\n\t * authentication\n\t */\n\tpublic void setContextSource(BaseLdapPathContextSource contextSource) {\n\t\tthis.contextSource = contextSource;\n\t}\n\n\t/**\n\t * Gets the {@link BaseLdapPathContextSource} used to perform LDAP authentication.\n\t * @return the {@link BaseLdapPathContextSource} used to perform LDAP authentication\n\t */\n\tprotected final BaseLdapPathContextSource getContextSource() {\n\t\treturn this.contextSource;\n\t}\n\n\t/**\n\t * Sets the {@link LdapAuthoritiesPopulator} used to obtain a list of granted\n\t * authorities for an LDAP user.\n\t * @param ldapAuthoritiesPopulator the {@link LdapAuthoritiesPopulator} to use\n\t */\n\tpublic void setLdapAuthoritiesPopulator(LdapAuthoritiesPopulator ldapAuthoritiesPopulator) {\n\t\tthis.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator;\n\t}\n\n\t/**\n\t * Sets the {@link GrantedAuthoritiesMapper} used for converting the authorities\n\t * loaded from storage to a new set of authorities which will be associated to the\n\t * {@link UsernamePasswordAuthenticationToken}.\n\t * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the\n\t * user's authorities\n\t */\n\tpublic void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\t/**\n\t * Sets a custom strategy to be used for creating the {@link UserDetails} which will\n\t * be stored as the principal in the {@link Authentication}.\n\t * @param userDetailsContextMapper the strategy instance\n\t */\n\tpublic void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {\n\t\tthis.userDetailsContextMapper = userDetailsContextMapper;\n\t}\n\n\t/**\n\t * If your users are at a fixed location in the directory (i.e. you can work out the\n\t * DN directly from the username without doing a directory search), you can use this\n\t * attribute to map directly to the DN. It maps directly to the userDnPatterns\n\t * property of AbstractLdapAuthenticator. The value is a specific pattern used to\n\t * build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present\n\t * and will be substituted with the username.\n\t * @param userDnPatterns the LDAP patterns for finding the usernames\n\t */\n\tpublic void setUserDnPatterns(String... userDnPatterns) {\n\t\tthis.userDnPatterns = userDnPatterns;\n\t}\n\n\t/**\n\t * The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n\t * substituted parameter is the user's login name.\n\t * @param userSearchFilter the LDAP filter used to search for users\n\t */\n\tpublic void setUserSearchFilter(String userSearchFilter) {\n\t\tthis.userSearchFilter = userSearchFilter;\n\t}\n\n\t/**\n\t * Search base for user searches. Defaults to \"\". Only used with\n\t * {@link #setUserSearchFilter(String)}.\n\t * @param userSearchBase search base for user searches\n\t */\n\tpublic void setUserSearchBase(String userSearchBase) {\n\t\tthis.userSearchBase = userSearchBase;\n\t}\n\n\t/**\n\t * Returns the configured {@link AuthenticationManager} that can be used to perform\n\t * LDAP authentication.\n\t * @return the configured {@link AuthenticationManager}\n\t */\n\tpublic final AuthenticationManager createAuthenticationManager() {\n\t\tLdapAuthenticationProvider ldapAuthenticationProvider = getProvider();\n\t\treturn new ProviderManager(ldapAuthenticationProvider);\n\t}\n\n\tprivate LdapAuthenticationProvider getProvider() {\n\t\tAbstractLdapAuthenticator authenticator = getAuthenticator();\n\t\tLdapAuthenticationProvider provider;\n\t\tif (this.ldapAuthoritiesPopulator != null) {\n\t\t\tprovider = new LdapAuthenticationProvider(authenticator, this.ldapAuthoritiesPopulator);\n\t\t}\n\t\telse {\n\t\t\tprovider = new LdapAuthenticationProvider(authenticator);\n\t\t}\n\t\tif (this.authoritiesMapper != null) {\n\t\t\tprovider.setAuthoritiesMapper(this.authoritiesMapper);\n\t\t}\n\t\tif (this.userDetailsContextMapper != null) {\n\t\t\tprovider.setUserDetailsContextMapper(this.userDetailsContextMapper);\n\t\t}\n\t\treturn provider;\n\t}\n\n\tprivate AbstractLdapAuthenticator getAuthenticator() {\n\t\tAbstractLdapAuthenticator authenticator = createDefaultLdapAuthenticator();\n\t\tif (this.userSearchFilter != null) {\n\t\t\tauthenticator.setUserSearch(\n\t\t\t\t\tnew FilterBasedLdapUserSearch(this.userSearchBase, this.userSearchFilter, this.contextSource));\n\t\t}\n\t\tif (this.userDnPatterns != null && this.userDnPatterns.length > 0) {\n\t\t\tauthenticator.setUserDnPatterns(this.userDnPatterns);\n\t\t}\n\t\tauthenticator.afterPropertiesSet();\n\t\treturn authenticator;\n\t}\n\n\t/**\n\t * Allows subclasses to supply the default {@link AbstractLdapAuthenticator}.\n\t * @return the {@link AbstractLdapAuthenticator} that will be configured for LDAP\n\t * authentication\n\t */\n\tprotected abstract T createDefaultLdapAuthenticator();\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/ContextSourceSettingPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanFactoryPostProcessor;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.context.ApplicationContextException;\nimport org.springframework.core.Ordered;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.Elements;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Checks for the presence of a ContextSource instance. Also supplies the standard\n * reference to any unconfigured &lt;ldap-authentication-provider&gt; or\n * &lt;ldap-user-service&gt; beans. This is necessary in cases where the user has given\n * the server a specific Id, but hasn't used the server-ref attribute to link this to the\n * other ldap definitions. See SEC-799.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class ContextSourceSettingPostProcessor implements BeanFactoryPostProcessor, Ordered {\n\n\tprivate static final String REQUIRED_CONTEXT_SOURCE_CLASS_NAME = \"org.springframework.ldap.core.support.BaseLdapPathContextSource\";\n\n\t/**\n\t * If set to true, a bean parser has indicated that the default context source name\n\t * needs to be set\n\t */\n\tprivate boolean defaultNameRequired;\n\n\tContextSourceSettingPostProcessor() {\n\t}\n\n\t@Override\n\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException {\n\t\tClass<?> contextSourceClass = getContextSourceClass();\n\t\tString[] sources = bf.getBeanNamesForType(contextSourceClass, false, false);\n\t\tif (sources.length == 0) {\n\t\t\tthrow new ApplicationContextException(\"No BaseLdapPathContextSource instances found. Have you \"\n\t\t\t\t\t+ \"added an <\" + Elements.LDAP_SERVER + \" /> element to your application context? If you have \"\n\t\t\t\t\t+ \"declared an explicit bean, do not use lazy-init\");\n\t\t}\n\t\tif (!bf.containsBean(BeanIds.CONTEXT_SOURCE) && this.defaultNameRequired) {\n\t\t\tif (sources.length > 1) {\n\t\t\t\tthrow new ApplicationContextException(\"More than one BaseLdapPathContextSource instance found. \"\n\t\t\t\t\t\t+ \"Please specify a specific server id using the 'server-ref' attribute when configuring your <\"\n\t\t\t\t\t\t+ Elements.LDAP_PROVIDER + \"> \" + \"or <\" + Elements.LDAP_USER_SERVICE + \">.\");\n\t\t\t}\n\t\t\tbf.registerAlias(sources[0], BeanIds.CONTEXT_SOURCE);\n\t\t}\n\t}\n\n\tprivate Class<?> getContextSourceClass() throws LinkageError {\n\t\ttry {\n\t\t\treturn ClassUtils.forName(REQUIRED_CONTEXT_SOURCE_CLASS_NAME, ClassUtils.getDefaultClassLoader());\n\t\t}\n\t\tcatch (ClassNotFoundException ex) {\n\t\t\tthrow new ApplicationContextException(\"Couldn't locate: \" + REQUIRED_CONTEXT_SOURCE_CLASS_NAME + \". \"\n\t\t\t\t\t+ \" If you are using LDAP with Spring Security, please ensure that you include the spring-ldap \"\n\t\t\t\t\t+ \"jar file in your application\", ex);\n\t\t}\n\t}\n\n\tpublic void setDefaultNameRequired(boolean defaultNameRequired) {\n\t\tthis.defaultNameRequired = defaultNameRequired;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn LOWEST_PRECEDENCE;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/EmbeddedLdapServerContextSourceFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.Lifecycle;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.server.EmbeddedLdapServerContainer;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Creates a {@link DefaultSpringSecurityContextSource} used to perform LDAP\n * authentication and starts and in-memory LDAP server.\n *\n * @author Eleftheria Stein\n * @since 5.7\n */\npublic class EmbeddedLdapServerContextSourceFactoryBean\n\t\timplements FactoryBean<DefaultSpringSecurityContextSource>, DisposableBean, ApplicationContextAware {\n\n\tprivate static final String UNBOUNDID_CLASSNAME = \"com.unboundid.ldap.listener.InMemoryDirectoryServer\";\n\n\tprivate static final boolean unboundIdPresent;\n\n\tprivate static final int DEFAULT_PORT = 33389;\n\n\tprivate static final int RANDOM_PORT = 0;\n\n\tprivate Integer port;\n\n\tprivate String ldif = \"classpath*:*.ldif\";\n\n\tprivate String root = \"dc=springframework,dc=org\";\n\n\tprivate ApplicationContext context;\n\n\tprivate String managerDn;\n\n\tprivate String managerPassword;\n\n\tprivate EmbeddedLdapServerContainer container;\n\n\tstatic {\n\t\tClassLoader classLoader = EmbeddedLdapServerContextSourceFactoryBean.class.getClassLoader();\n\t\tunboundIdPresent = ClassUtils.isPresent(UNBOUNDID_CLASSNAME, classLoader);\n\t}\n\n\t/**\n\t * Create an EmbeddedLdapServerContextSourceFactoryBean that will use an embedded LDAP\n\t * server to perform LDAP authentication. This requires a dependency on\n\t * `com.unboundid:unboundid-ldapsdk`.\n\t * @return the EmbeddedLdapServerContextSourceFactoryBean\n\t */\n\tpublic static EmbeddedLdapServerContextSourceFactoryBean fromEmbeddedLdapServer() {\n\t\treturn new EmbeddedLdapServerContextSourceFactoryBean();\n\t}\n\n\t/**\n\t * Specifies an LDIF to load at startup for an embedded LDAP server. The default is\n\t * \"classpath*:*.ldif\".\n\t * @param ldif the ldif to load at startup for an embedded LDAP server.\n\t */\n\tpublic void setLdif(String ldif) {\n\t\tthis.ldif = ldif;\n\t}\n\n\t/**\n\t * The port to connect to LDAP to (the default is 33389 or random available port if\n\t * unavailable). Supplying 0 as the port indicates that a random available port should\n\t * be selected.\n\t * @param port the port to connect to\n\t */\n\tpublic void setPort(int port) {\n\t\tthis.port = port;\n\t}\n\n\t/**\n\t * Optional root suffix for the embedded LDAP server. Default is\n\t * \"dc=springframework,dc=org\".\n\t * @param root root suffix for the embedded LDAP server\n\t */\n\tpublic void setRoot(String root) {\n\t\tthis.root = root;\n\t}\n\n\t/**\n\t * Username (DN) of the \"manager\" user identity (i.e. \"uid=admin,ou=system\") which\n\t * will be used to authenticate to an LDAP server. If omitted, anonymous access will\n\t * be used.\n\t * @param managerDn the username (DN) of the \"manager\" user identity used to\n\t * authenticate to a LDAP server.\n\t */\n\tpublic void setManagerDn(String managerDn) {\n\t\tthis.managerDn = managerDn;\n\t}\n\n\t/**\n\t * The password for the manager DN. This is required if the\n\t * {@link #setManagerDn(String)} is specified.\n\t * @param managerPassword password for the manager DN\n\t */\n\tpublic void setManagerPassword(String managerPassword) {\n\t\tthis.managerPassword = managerPassword;\n\t}\n\n\t@Override\n\tpublic DefaultSpringSecurityContextSource getObject() throws Exception {\n\t\tif (!unboundIdPresent) {\n\t\t\tthrow new IllegalStateException(\"Embedded LDAP server is not provided\");\n\t\t}\n\t\tthis.container = getContainer();\n\t\tthis.port = this.container.getPort();\n\t\tDefaultSpringSecurityContextSource contextSourceFromProviderUrl = new DefaultSpringSecurityContextSource(\n\t\t\t\t\"ldap://127.0.0.1:\" + this.port + \"/\" + this.root);\n\t\tif (this.managerDn != null) {\n\t\t\tcontextSourceFromProviderUrl.setUserDn(this.managerDn);\n\t\t\tif (this.managerPassword == null) {\n\t\t\t\tthrow new IllegalStateException(\"managerPassword is required if managerDn is supplied\");\n\t\t\t}\n\t\t\tcontextSourceFromProviderUrl.setPassword(this.managerPassword);\n\t\t}\n\t\tcontextSourceFromProviderUrl.afterPropertiesSet();\n\t\treturn contextSourceFromProviderUrl;\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn DefaultSpringSecurityContextSource.class;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tif (this.container instanceof Lifecycle) {\n\t\t\t((Lifecycle) this.container).stop();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.context = applicationContext;\n\t}\n\n\tprivate EmbeddedLdapServerContainer getContainer() {\n\t\tif (!unboundIdPresent) {\n\t\t\tthrow new IllegalStateException(\"Embedded LDAP server is not provided\");\n\t\t}\n\t\tUnboundIdContainer unboundIdContainer = new UnboundIdContainer(this.root, this.ldif);\n\t\tunboundIdContainer.setApplicationContext(this.context);\n\t\tunboundIdContainer.setPort(getEmbeddedServerPort());\n\t\tunboundIdContainer.afterPropertiesSet();\n\t\treturn unboundIdContainer;\n\t}\n\n\tprivate int getEmbeddedServerPort() {\n\t\tif (this.port == null) {\n\t\t\tthis.port = getDefaultEmbeddedServerPort();\n\t\t}\n\t\treturn this.port;\n\t}\n\n\tprivate int getDefaultEmbeddedServerPort() {\n\t\ttry (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {\n\t\t\treturn serverSocket.getLocalPort();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\treturn RANDOM_PORT;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/LdapBindAuthenticationManagerFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.ldap.authentication.BindAuthenticator;\n\n/**\n * Creates an {@link AuthenticationManager} that can perform LDAP authentication using\n * bind authentication.\n *\n * @author Eleftheria Stein\n * @since 5.7\n */\npublic class LdapBindAuthenticationManagerFactory extends AbstractLdapAuthenticationManagerFactory<BindAuthenticator> {\n\n\tpublic LdapBindAuthenticationManagerFactory(BaseLdapPathContextSource contextSource) {\n\t\tsuper(contextSource);\n\t}\n\n\t@Override\n\tprotected BindAuthenticator createDefaultLdapAuthenticator() {\n\t\treturn new BindAuthenticator(getContextSource());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/LdapPasswordComparisonAuthenticationManagerFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;\nimport org.springframework.util.Assert;\n\n/**\n * Creates an {@link AuthenticationManager} that can perform LDAP authentication using\n * password comparison.\n *\n * @author Eleftheria Stein\n * @since 5.7\n */\npublic class LdapPasswordComparisonAuthenticationManagerFactory\n\t\textends AbstractLdapAuthenticationManagerFactory<PasswordComparisonAuthenticator> {\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\tprivate String passwordAttribute;\n\n\tpublic LdapPasswordComparisonAuthenticationManagerFactory(BaseLdapPathContextSource contextSource,\n\t\t\tPasswordEncoder passwordEncoder) {\n\t\tsuper(contextSource);\n\t\tsetPasswordEncoder(passwordEncoder);\n\t}\n\n\t/**\n\t * Specifies the {@link PasswordEncoder} to be used when authenticating with password\n\t * comparison.\n\t * @param passwordEncoder the {@link PasswordEncoder} to use\n\t */\n\tpublic void setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder must not be null.\");\n\t\tthis.passwordEncoder = passwordEncoder;\n\t}\n\n\t/**\n\t * The attribute in the directory which contains the user password. Only used when\n\t * authenticating with password comparison. Defaults to \"userPassword\".\n\t * @param passwordAttribute the attribute in the directory which contains the user\n\t * password\n\t */\n\tpublic void setPasswordAttribute(String passwordAttribute) {\n\t\tthis.passwordAttribute = passwordAttribute;\n\t}\n\n\t@Override\n\tprotected PasswordComparisonAuthenticator createDefaultLdapAuthenticator() {\n\t\tPasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(getContextSource());\n\t\tif (this.passwordAttribute != null) {\n\t\t\tldapAuthenticator.setPasswordAttributeName(this.passwordAttribute);\n\t\t}\n\t\tldapAuthenticator.setPasswordEncoder(this.passwordEncoder);\n\t\treturn ldapAuthenticator;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/LdapProviderBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.config.authentication.PasswordEncoderParser;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Ldap authentication provider namespace configuration.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final String ATT_USER_DN_PATTERN = \"user-dn-pattern\";\n\n\tprivate static final String ATT_USER_PASSWORD = \"password-attribute\";\n\n\tprivate static final String ATT_HASH = PasswordEncoderParser.ATT_HASH;\n\n\tprivate static final String DEF_USER_SEARCH_FILTER = \"uid={0}\";\n\n\tstatic final String PROVIDER_CLASS = \"org.springframework.security.ldap.authentication.LdapAuthenticationProvider\";\n\n\tstatic final String BIND_AUTH_CLASS = \"org.springframework.security.ldap.authentication.BindAuthenticator\";\n\n\tstatic final String PASSWD_AUTH_CLASS = \"org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator\";\n\n\t@Override\n\tpublic BeanDefinition parse(Element elt, ParserContext parserContext) {\n\t\tRuntimeBeanReference contextSource = LdapUserServiceBeanDefinitionParser.parseServerReference(elt,\n\t\t\t\tparserContext);\n\t\tBeanDefinition searchBean = LdapUserServiceBeanDefinitionParser.parseSearchBean(elt, parserContext);\n\t\tString userDnPattern = elt.getAttribute(ATT_USER_DN_PATTERN);\n\t\tString[] userDnPatternArray = new String[0];\n\t\tif (StringUtils.hasText(userDnPattern)) {\n\t\t\tuserDnPatternArray = new String[] { userDnPattern };\n\t\t\t// TODO: Validate the pattern and make sure it is a valid DN.\n\t\t}\n\t\telse if (searchBean == null) {\n\t\t\tthis.logger.info(\"No search information or DN pattern specified. Using default search filter '\"\n\t\t\t\t\t+ DEF_USER_SEARCH_FILTER + \"'\");\n\t\t\tBeanDefinitionBuilder searchBeanBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(LdapUserServiceBeanDefinitionParser.LDAP_SEARCH_CLASS);\n\t\t\tsearchBeanBuilder.getRawBeanDefinition().setSource(elt);\n\t\t\tsearchBeanBuilder.addConstructorArgValue(\"\");\n\t\t\tsearchBeanBuilder.addConstructorArgValue(DEF_USER_SEARCH_FILTER);\n\t\t\tsearchBeanBuilder.addConstructorArgValue(contextSource);\n\t\t\tsearchBean = searchBeanBuilder.getBeanDefinition();\n\t\t}\n\t\tBeanDefinitionBuilder authenticatorBuilder = BeanDefinitionBuilder.rootBeanDefinition(BIND_AUTH_CLASS);\n\t\tElement passwordCompareElt = DomUtils.getChildElementByTagName(elt, Elements.LDAP_PASSWORD_COMPARE);\n\t\tif (passwordCompareElt != null) {\n\t\t\tauthenticatorBuilder = BeanDefinitionBuilder.rootBeanDefinition(PASSWD_AUTH_CLASS);\n\t\t\tString passwordAttribute = passwordCompareElt.getAttribute(ATT_USER_PASSWORD);\n\t\t\tif (StringUtils.hasText(passwordAttribute)) {\n\t\t\t\tauthenticatorBuilder.addPropertyValue(\"passwordAttributeName\", passwordAttribute);\n\t\t\t}\n\t\t\tElement passwordEncoderElement = DomUtils.getChildElementByTagName(passwordCompareElt,\n\t\t\t\t\tElements.PASSWORD_ENCODER);\n\t\t\tString hash = passwordCompareElt.getAttribute(ATT_HASH);\n\t\t\tif (passwordEncoderElement != null) {\n\t\t\t\tif (StringUtils.hasText(hash)) {\n\t\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t\t.warning(\"Attribute 'hash' cannot be used with 'password-encoder' and \" + \"will be ignored.\",\n\t\t\t\t\t\t\t\tparserContext.extractSource(elt));\n\t\t\t\t}\n\t\t\t\tPasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElement, parserContext);\n\t\t\t\tauthenticatorBuilder.addPropertyValue(\"passwordEncoder\", pep.getPasswordEncoder());\n\t\t\t}\n\t\t\telse if (StringUtils.hasText(hash)) {\n\t\t\t\tauthenticatorBuilder.addPropertyValue(\"passwordEncoder\",\n\t\t\t\t\t\tPasswordEncoderParser.createPasswordEncoderBeanDefinition(hash));\n\t\t\t}\n\t\t}\n\t\tauthenticatorBuilder.addConstructorArgValue(contextSource);\n\t\tauthenticatorBuilder.addPropertyValue(\"userDnPatterns\", userDnPatternArray);\n\t\tif (searchBean != null) {\n\t\t\tauthenticatorBuilder.addPropertyValue(\"userSearch\", searchBean);\n\t\t}\n\t\tBeanDefinitionBuilder ldapProvider = BeanDefinitionBuilder.rootBeanDefinition(PROVIDER_CLASS);\n\t\tldapProvider.addConstructorArgValue(authenticatorBuilder.getBeanDefinition());\n\t\tldapProvider\n\t\t\t.addConstructorArgValue(LdapUserServiceBeanDefinitionParser.parseAuthoritiesPopulator(elt, parserContext));\n\t\tldapProvider.addPropertyValue(\"userDetailsContextMapper\",\n\t\t\t\tLdapUserServiceBeanDefinitionParser.parseUserDetailsClassOrUserMapperRef(elt, parserContext));\n\t\treturn ldapProvider.getBeanDefinition();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/LdapServerBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @author Evgeniy Cheban\n */\npublic class LdapServerBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String CONTEXT_SOURCE_CLASS = \"org.springframework.security.ldap.DefaultSpringSecurityContextSource\";\n\n\t/**\n\t * Defines the Url of the ldap server to use. If not specified, an embedded UnboundID\n\t * instance will be created\n\t */\n\tprivate static final String ATT_URL = \"url\";\n\n\tprivate static final String ATT_PRINCIPAL = \"manager-dn\";\n\n\tprivate static final String ATT_PASSWORD = \"manager-password\";\n\n\t// Properties which apply to embedded server only - when no Url is set\n\n\t/** sets the configuration suffix (default is \"dc=springframework,dc=org\"). */\n\tpublic static final String ATT_ROOT_SUFFIX = \"root\";\n\n\tprivate static final String OPT_DEFAULT_ROOT_SUFFIX = \"dc=springframework,dc=org\";\n\n\t/**\n\t * Optionally defines an ldif resource to be loaded. Otherwise an attempt will be made\n\t * to load all ldif files found on the classpath.\n\t */\n\tpublic static final String ATT_LDIF_FILE = \"ldif\";\n\n\tprivate static final String OPT_DEFAULT_LDIF_FILE = \"classpath*:*.ldif\";\n\n\t/** Defines the port the LDAP_PROVIDER server should run on */\n\tpublic static final String ATT_PORT = \"port\";\n\n\tprivate static final String RANDOM_PORT = \"0\";\n\n\tprivate static final int DEFAULT_PORT = 33389;\n\n\tprivate static final String UNBOUNID_CLASSNAME = \"com.unboundid.ldap.listener.InMemoryDirectoryServer\";\n\n\tprivate static final String UNBOUNDID_CONTAINER_CLASSNAME = \"org.springframework.security.ldap.server.UnboundIdContainer\";\n\n\tprivate static final boolean unboundIdPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = LdapServerBeanDefinitionParser.class.getClassLoader();\n\t\tunboundIdPresent = ClassUtils.isPresent(UNBOUNID_CLASSNAME, classLoader);\n\t}\n\n\t@Override\n\tpublic BeanDefinition parse(Element elt, ParserContext parserContext) {\n\t\tString url = elt.getAttribute(ATT_URL);\n\t\tRootBeanDefinition contextSource;\n\t\tif (!StringUtils.hasText(url)) {\n\t\t\tcontextSource = createEmbeddedServer(elt, parserContext);\n\t\t}\n\t\telse {\n\t\t\tcontextSource = new RootBeanDefinition();\n\t\t\tcontextSource.setBeanClassName(CONTEXT_SOURCE_CLASS);\n\t\t\tcontextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);\n\t\t}\n\t\tcontextSource.setSource(parserContext.extractSource(elt));\n\t\tString managerDn = elt.getAttribute(ATT_PRINCIPAL);\n\t\tString managerPassword = elt.getAttribute(ATT_PASSWORD);\n\t\tif (StringUtils.hasText(managerDn)) {\n\t\t\tif (!StringUtils.hasText(managerPassword)) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"You must specify the \" + ATT_PASSWORD + \" if you supply a \" + managerDn, elt);\n\t\t\t}\n\t\t\tcontextSource.getPropertyValues().addPropertyValue(\"userDn\", managerDn);\n\t\t\tcontextSource.getPropertyValues().addPropertyValue(\"password\", managerPassword);\n\t\t}\n\t\tString id = elt.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);\n\t\tString contextSourceId = StringUtils.hasText(id) ? id : BeanIds.CONTEXT_SOURCE;\n\t\tparserContext.getRegistry().registerBeanDefinition(contextSourceId, contextSource);\n\t\treturn null;\n\t}\n\n\t/**\n\t * Will be called if no url attribute is supplied.\n\t *\n\t * Registers beans to create an embedded UnboundID Server.\n\t * @return the BeanDefinition for the ContextSource for the embedded server.\n\t *\n\t * @see UnboundIdContainer\n\t */\n\tprivate RootBeanDefinition createEmbeddedServer(Element element, ParserContext parserContext) {\n\t\tObject source = parserContext.extractSource(element);\n\t\tString suffix = element.getAttribute(ATT_ROOT_SUFFIX);\n\t\tif (!StringUtils.hasText(suffix)) {\n\t\t\tsuffix = OPT_DEFAULT_ROOT_SUFFIX;\n\t\t}\n\t\tBeanDefinitionBuilder contextSource = BeanDefinitionBuilder.rootBeanDefinition(CONTEXT_SOURCE_CLASS);\n\t\tcontextSource.addConstructorArgValue(suffix);\n\t\tcontextSource.addPropertyValue(\"userDn\", \"uid=admin,ou=system\");\n\t\tcontextSource.addPropertyValue(\"password\", \"secret\");\n\t\tBeanDefinition embeddedLdapServerConfigBean = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(EmbeddedLdapServerConfigBean.class)\n\t\t\t.getBeanDefinition();\n\t\tString embeddedLdapServerConfigBeanName = parserContext.getReaderContext()\n\t\t\t.generateBeanName(embeddedLdapServerConfigBean);\n\t\tparserContext.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(embeddedLdapServerConfigBean, embeddedLdapServerConfigBeanName));\n\t\tcontextSource.setFactoryMethodOnBean(\"createEmbeddedContextSource\", embeddedLdapServerConfigBeanName);\n\t\tString mode = element.getAttribute(\"mode\");\n\t\tRootBeanDefinition ldapContainer = getRootBeanDefinition(mode);\n\t\tldapContainer.setSource(source);\n\t\tldapContainer.getConstructorArgumentValues().addGenericArgumentValue(suffix);\n\t\tString ldifs = element.getAttribute(ATT_LDIF_FILE);\n\t\tif (!StringUtils.hasText(ldifs)) {\n\t\t\tldifs = OPT_DEFAULT_LDIF_FILE;\n\t\t}\n\t\tldapContainer.getConstructorArgumentValues().addGenericArgumentValue(ldifs);\n\t\tldapContainer.getPropertyValues().addPropertyValue(\"port\", getPort(element));\n\t\tif (parserContext.getRegistry().containsBeanDefinition(BeanIds.EMBEDDED_UNBOUNDID)) {\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(\"Only one embedded server bean is allowed per application context\", element);\n\t\t}\n\t\tString beanId = resolveBeanId(mode);\n\t\tif (beanId != null) {\n\t\t\tparserContext.getRegistry().registerBeanDefinition(beanId, ldapContainer);\n\t\t}\n\t\treturn (RootBeanDefinition) contextSource.getBeanDefinition();\n\t}\n\n\tprivate RootBeanDefinition getRootBeanDefinition(String mode) {\n\t\tif (isUnboundIdEnabled(mode)) {\n\t\t\treturn new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);\n\t\t}\n\t\tthrow new IllegalStateException(\"Embedded LDAP server is not provided\");\n\t}\n\n\tprivate String resolveBeanId(String mode) {\n\t\tif (isUnboundIdEnabled(mode)) {\n\t\t\treturn BeanIds.EMBEDDED_UNBOUNDID;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate boolean isUnboundIdEnabled(String mode) {\n\t\treturn \"unboundid\".equals(mode) || unboundIdPresent;\n\t}\n\n\tprivate String getPort(Element element) {\n\t\tString port = element.getAttribute(ATT_PORT);\n\t\treturn (StringUtils.hasText(port) ? port : getDefaultPort());\n\t}\n\n\tprivate String getDefaultPort() {\n\t\ttry (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {\n\t\t\treturn String.valueOf(serverSocket.getLocalPort());\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\treturn RANDOM_PORT;\n\t\t}\n\t}\n\n\tprivate static class EmbeddedLdapServerConfigBean implements ApplicationContextAware {\n\n\t\tprivate ApplicationContext applicationContext;\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext applicationContext) {\n\t\t\tthis.applicationContext = applicationContext;\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tprivate DefaultSpringSecurityContextSource createEmbeddedContextSource(String suffix) {\n\t\t\tint port = getPort();\n\t\t\tString providerUrl = \"ldap://127.0.0.1:\" + port + \"/\" + suffix;\n\t\t\treturn new DefaultSpringSecurityContextSource(providerUrl);\n\t\t}\n\n\t\tprivate int getPort() {\n\t\t\tif (unboundIdPresent) {\n\t\t\t\tUnboundIdContainer unboundIdContainer = this.applicationContext.getBean(UnboundIdContainer.class);\n\t\t\t\treturn unboundIdContainer.getPort();\n\t\t\t}\n\t\t\tthrow new IllegalStateException(\"Embedded LDAP server is not provided\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.ldap;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.authentication.AbstractUserDetailsServiceBeanDefinitionParser;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Luke Taylor\n * @since 2.0\n */\npublic class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServiceBeanDefinitionParser {\n\n\tpublic static final String ATT_SERVER = \"server-ref\";\n\n\tpublic static final String ATT_USER_SEARCH_FILTER = \"user-search-filter\";\n\n\tpublic static final String ATT_USER_SEARCH_BASE = \"user-search-base\";\n\n\tpublic static final String DEF_USER_SEARCH_BASE = \"\";\n\n\tpublic static final String ATT_GROUP_SEARCH_FILTER = \"group-search-filter\";\n\n\tpublic static final String ATT_GROUP_SEARCH_BASE = \"group-search-base\";\n\n\tpublic static final String ATT_GROUP_ROLE_ATTRIBUTE = \"group-role-attribute\";\n\n\tpublic static final String DEF_GROUP_SEARCH_FILTER = \"(uniqueMember={0})\";\n\n\tpublic static final String DEF_GROUP_SEARCH_BASE = \"\";\n\n\tstatic final String ATT_ROLE_PREFIX = \"role-prefix\";\n\n\tstatic final String ATT_USER_CLASS = \"user-details-class\";\n\n\tstatic final String ATT_USER_CONTEXT_MAPPER_REF = \"user-context-mapper-ref\";\n\n\tstatic final String OPT_PERSON = \"person\";\n\n\tstatic final String OPT_INETORGPERSON = \"inetOrgPerson\";\n\n\tpublic static final String LDAP_SEARCH_CLASS = \"org.springframework.security.ldap.search.FilterBasedLdapUserSearch\";\n\n\tpublic static final String PERSON_MAPPER_CLASS = \"org.springframework.security.ldap.userdetails.PersonContextMapper\";\n\n\tpublic static final String INET_ORG_PERSON_MAPPER_CLASS = \"org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper\";\n\n\tpublic static final String LDAP_USER_MAPPER_CLASS = \"org.springframework.security.ldap.userdetails.LdapUserDetailsMapper\";\n\n\tpublic static final String LDAP_AUTHORITIES_POPULATOR_CLASS = \"org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator\";\n\n\t@Override\n\tprotected String getBeanClassName(Element element) {\n\t\treturn \"org.springframework.security.ldap.userdetails.LdapUserDetailsService\";\n\t}\n\n\t@Override\n\tprotected void doParse(Element elt, ParserContext parserContext, BeanDefinitionBuilder builder) {\n\t\tif (!StringUtils.hasText(elt.getAttribute(ATT_USER_SEARCH_FILTER))) {\n\t\t\tparserContext.getReaderContext().error(\"User search filter must be supplied\", elt);\n\t\t}\n\t\tbuilder.addConstructorArgValue(parseSearchBean(elt, parserContext));\n\t\tbuilder.getRawBeanDefinition().setSource(parserContext.extractSource(elt));\n\t\tbuilder.addConstructorArgValue(parseAuthoritiesPopulator(elt, parserContext));\n\t\tbuilder.addPropertyValue(\"userDetailsMapper\", parseUserDetailsClassOrUserMapperRef(elt, parserContext));\n\t}\n\n\tstatic RootBeanDefinition parseSearchBean(Element elt, ParserContext parserContext) {\n\t\tString userSearchFilter = elt.getAttribute(ATT_USER_SEARCH_FILTER);\n\t\tString userSearchBase = elt.getAttribute(ATT_USER_SEARCH_BASE);\n\t\tObject source = parserContext.extractSource(elt);\n\t\tif (StringUtils.hasText(userSearchBase)) {\n\t\t\tif (!StringUtils.hasText(userSearchFilter)) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(ATT_USER_SEARCH_BASE + \" cannot be used without a \" + ATT_USER_SEARCH_FILTER, source);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tuserSearchBase = DEF_USER_SEARCH_BASE;\n\t\t}\n\t\tif (!StringUtils.hasText(userSearchFilter)) {\n\t\t\treturn null;\n\t\t}\n\t\tBeanDefinitionBuilder searchBuilder = BeanDefinitionBuilder.rootBeanDefinition(LDAP_SEARCH_CLASS);\n\t\tsearchBuilder.getRawBeanDefinition().setSource(source);\n\t\tsearchBuilder.addConstructorArgValue(userSearchBase);\n\t\tsearchBuilder.addConstructorArgValue(userSearchFilter);\n\t\tsearchBuilder.addConstructorArgValue(parseServerReference(elt, parserContext));\n\t\treturn (RootBeanDefinition) searchBuilder.getBeanDefinition();\n\t}\n\n\tstatic RuntimeBeanReference parseServerReference(Element elt, ParserContext parserContext) {\n\t\tString server = elt.getAttribute(ATT_SERVER);\n\t\tboolean requiresDefaultName = false;\n\t\tif (!StringUtils.hasText(server)) {\n\t\t\tserver = BeanIds.CONTEXT_SOURCE;\n\t\t\trequiresDefaultName = true;\n\t\t}\n\t\tRuntimeBeanReference contextSource = new RuntimeBeanReference(server);\n\t\tcontextSource.setSource(parserContext.extractSource(elt));\n\t\tregisterPostProcessorIfNecessary(parserContext.getRegistry(), requiresDefaultName);\n\t\treturn contextSource;\n\t}\n\n\tprivate static void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry, boolean defaultNameRequired) {\n\t\tif (registry.containsBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR)) {\n\t\t\tif (defaultNameRequired) {\n\t\t\t\tBeanDefinition bd = registry.getBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR);\n\t\t\t\tbd.getPropertyValues().addPropertyValue(\"defaultNameRequired\", defaultNameRequired);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tBeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(ContextSourceSettingPostProcessor.class);\n\t\tbdb.addPropertyValue(\"defaultNameRequired\", defaultNameRequired);\n\t\tregistry.registerBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR, bdb.getBeanDefinition());\n\t}\n\n\tstatic BeanMetadataElement parseUserDetailsClassOrUserMapperRef(Element elt, ParserContext parserContext) {\n\t\tString userDetailsClass = elt.getAttribute(ATT_USER_CLASS);\n\t\tString userMapperRef = elt.getAttribute(ATT_USER_CONTEXT_MAPPER_REF);\n\t\tif (StringUtils.hasText(userDetailsClass) && StringUtils.hasText(userMapperRef)) {\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(\"Attributes \" + ATT_USER_CLASS + \" and \" + ATT_USER_CONTEXT_MAPPER_REF\n\t\t\t\t\t\t+ \" cannot be used together.\", parserContext.extractSource(elt));\n\t\t}\n\t\tif (StringUtils.hasText(userMapperRef)) {\n\t\t\treturn new RuntimeBeanReference(userMapperRef);\n\t\t}\n\t\tRootBeanDefinition mapper = getMapper(userDetailsClass);\n\t\tmapper.setSource(parserContext.extractSource(elt));\n\t\treturn mapper;\n\t}\n\n\tprivate static RootBeanDefinition getMapper(String userDetailsClass) {\n\t\tif (OPT_PERSON.equals(userDetailsClass)) {\n\t\t\treturn new RootBeanDefinition(PERSON_MAPPER_CLASS, null, null);\n\t\t}\n\t\tif (OPT_INETORGPERSON.equals(userDetailsClass)) {\n\t\t\treturn new RootBeanDefinition(INET_ORG_PERSON_MAPPER_CLASS, null, null);\n\t\t}\n\t\treturn new RootBeanDefinition(LDAP_USER_MAPPER_CLASS, null, null);\n\t}\n\n\tstatic RootBeanDefinition parseAuthoritiesPopulator(Element elt, ParserContext parserContext) {\n\t\tString groupSearchFilter = elt.getAttribute(ATT_GROUP_SEARCH_FILTER);\n\t\tString groupSearchBase = elt.getAttribute(ATT_GROUP_SEARCH_BASE);\n\t\tString groupRoleAttribute = elt.getAttribute(ATT_GROUP_ROLE_ATTRIBUTE);\n\t\tString rolePrefix = elt.getAttribute(ATT_ROLE_PREFIX);\n\t\tif (!StringUtils.hasText(groupSearchFilter)) {\n\t\t\tgroupSearchFilter = DEF_GROUP_SEARCH_FILTER;\n\t\t}\n\t\tif (!StringUtils.hasText(groupSearchBase)) {\n\t\t\tgroupSearchBase = DEF_GROUP_SEARCH_BASE;\n\t\t}\n\t\tBeanDefinitionBuilder populator = BeanDefinitionBuilder.rootBeanDefinition(LDAP_AUTHORITIES_POPULATOR_CLASS);\n\t\tpopulator.getRawBeanDefinition().setSource(parserContext.extractSource(elt));\n\t\tpopulator.addConstructorArgValue(parseServerReference(elt, parserContext));\n\t\tpopulator.addConstructorArgValue(groupSearchBase);\n\t\tpopulator.addPropertyValue(\"groupSearchFilter\", groupSearchFilter);\n\t\tpopulator.addPropertyValue(\"searchSubtree\", Boolean.TRUE);\n\t\tif (StringUtils.hasText(rolePrefix)) {\n\t\t\tif (\"none\".equals(rolePrefix)) {\n\t\t\t\trolePrefix = \"\";\n\t\t\t}\n\t\t\tpopulator.addPropertyValue(\"rolePrefix\", rolePrefix);\n\t\t}\n\t\tif (StringUtils.hasLength(groupRoleAttribute)) {\n\t\t\tpopulator.addPropertyValue(\"groupRoleAttribute\", groupRoleAttribute);\n\t\t}\n\t\treturn (RootBeanDefinition) populator.getBeanDefinition();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/ldap/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Security namespace support for LDAP authentication.\n */\npackage org.springframework.security.config.ldap;\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/AspectJMethodMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.aspectj.weaver.tools.PointcutExpression;\nimport org.aspectj.weaver.tools.PointcutParser;\nimport org.aspectj.weaver.tools.PointcutPrimitive;\n\nimport org.springframework.aop.ClassFilter;\nimport org.springframework.aop.MethodMatcher;\nimport org.springframework.aop.Pointcut;\n\nclass AspectJMethodMatcher implements MethodMatcher, ClassFilter, Pointcut {\n\n\tprivate static final PointcutParser parser;\n\n\tstatic {\n\t\tSet<PointcutPrimitive> supportedPrimitives = new HashSet<>(3);\n\t\tsupportedPrimitives.add(PointcutPrimitive.EXECUTION);\n\t\tsupportedPrimitives.add(PointcutPrimitive.ARGS);\n\t\tsupportedPrimitives.add(PointcutPrimitive.REFERENCE);\n\t\tparser = PointcutParser\n\t\t\t.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives);\n\t}\n\n\tprivate final PointcutExpression expression;\n\n\tAspectJMethodMatcher(String expression) {\n\t\tthis.expression = parser.parsePointcutExpression(expression);\n\t}\n\n\t@Override\n\tpublic boolean matches(Class<?> clazz) {\n\t\treturn this.expression.couldMatchJoinPointsInType(clazz);\n\t}\n\n\t@Override\n\tpublic boolean matches(Method method, Class<?> targetClass) {\n\t\treturn this.expression.matchesMethodExecution(method).alwaysMatches();\n\t}\n\n\t@Override\n\tpublic boolean isRuntime() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean matches(Method method, Class<?> targetClass, Object... args) {\n\t\treturn matches(method, targetClass);\n\t}\n\n\t@Override\n\tpublic ClassFilter getClassFilter() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic MethodMatcher getMethodMatcher() {\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\n\nimport org.springframework.aop.config.AopNamespaceUtils;\nimport org.springframework.aop.framework.ProxyFactoryBean;\nimport org.springframework.aop.target.LazyInitTargetSource;\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanReference;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.parsing.CompositeComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;\nimport org.springframework.security.access.annotation.Jsr250Voter;\nimport org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;\nimport org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;\nimport org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.intercept.AfterInvocationProviderManager;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;\nimport org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;\nimport org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;\nimport org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;\nimport org.springframework.security.access.prepost.PostInvocationAdviceProvider;\nimport org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;\nimport org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;\nimport org.springframework.security.access.vote.AffirmativeBased;\nimport org.springframework.security.access.vote.AuthenticatedVoter;\nimport org.springframework.security.access.vote.RoleVoter;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.config.authentication.AuthenticationManagerFactoryBean;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Processes the top-level \"global-method-security\" element.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @author Rob Winch\n * @author Ngoc Nhan\n * @since 2.0\n * @deprecated Use {@link MethodSecurityBeanDefinitionParser} instead\n */\n@Deprecated\npublic class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final String ATT_AUTHENTICATION_MANAGER_REF = \"authentication-manager-ref\";\n\n\tprivate static final String ATT_ACCESS = \"access\";\n\n\tprivate static final String ATT_EXPRESSION = \"expression\";\n\n\tprivate static final String ATT_ACCESS_MGR = \"access-decision-manager-ref\";\n\n\tprivate static final String ATT_RUN_AS_MGR = \"run-as-manager-ref\";\n\n\tprivate static final String ATT_USE_JSR250 = \"jsr250-annotations\";\n\n\tprivate static final String ATT_USE_SECURED = \"secured-annotations\";\n\n\tprivate static final String ATT_USE_PREPOST = \"pre-post-annotations\";\n\n\tprivate static final String ATT_REF = \"ref\";\n\n\tprivate static final String ATT_MODE = \"mode\";\n\n\tprivate static final String ATT_ADVICE_ORDER = \"order\";\n\n\tprivate static final String ATT_META_DATA_SOURCE_REF = \"metadata-source-ref\";\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),\n\t\t\t\tpc.extractSource(element));\n\t\tpc.pushContainingComponent(compositeDef);\n\t\tObject source = pc.extractSource(element);\n\t\t// The list of method metadata delegates\n\t\tManagedList<BeanMetadataElement> delegates = new ManagedList<>();\n\t\tboolean jsr250Enabled = \"enabled\".equals(element.getAttribute(ATT_USE_JSR250));\n\t\tboolean useSecured = \"enabled\".equals(element.getAttribute(ATT_USE_SECURED));\n\t\tboolean prePostAnnotationsEnabled = \"enabled\".equals(element.getAttribute(ATT_USE_PREPOST));\n\t\tboolean useAspectJ = \"aspectj\".equals(element.getAttribute(ATT_MODE));\n\t\tBeanDefinition preInvocationVoter = null;\n\t\tManagedList<BeanMetadataElement> afterInvocationProviders = new ManagedList<>();\n\t\t// Check for an external SecurityMetadataSource, which takes priority over other\n\t\t// sources\n\t\tString metaDataSourceId = element.getAttribute(ATT_META_DATA_SOURCE_REF);\n\t\tif (StringUtils.hasText(metaDataSourceId)) {\n\t\t\tdelegates.add(new RuntimeBeanReference(metaDataSourceId));\n\t\t}\n\t\tif (prePostAnnotationsEnabled) {\n\t\t\tElement prePostElt = DomUtils.getChildElementByTagName(element, Elements.INVOCATION_HANDLING);\n\t\t\tElement expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);\n\t\t\tif (prePostElt != null && expressionHandlerElt != null) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(Elements.INVOCATION_HANDLING + \" and \" + Elements.EXPRESSION_HANDLER\n\t\t\t\t\t\t\t+ \" cannot be used together \", source);\n\t\t\t}\n\t\t\tBeanDefinitionBuilder preInvocationVoterBldr = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PreInvocationAuthorizationAdviceVoter.class);\n\t\t\t// After-invocation provider to handle post-invocation filtering and\n\t\t\t// authorization expression annotations.\n\t\t\tBeanDefinitionBuilder afterInvocationBldr = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PostInvocationAdviceProvider.class);\n\t\t\t// The metadata source for the security interceptor\n\t\t\tBeanDefinitionBuilder mds = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PrePostAnnotationSecurityMetadataSource.class);\n\t\t\tif (prePostElt != null) {\n\t\t\t\t// Customized override of expression handling system\n\t\t\t\tString attributeFactoryRef = DomUtils\n\t\t\t\t\t.getChildElementByTagName(prePostElt, Elements.INVOCATION_ATTRIBUTE_FACTORY)\n\t\t\t\t\t.getAttribute(\"ref\");\n\t\t\t\tString preAdviceRef = DomUtils.getChildElementByTagName(prePostElt, Elements.PRE_INVOCATION_ADVICE)\n\t\t\t\t\t.getAttribute(\"ref\");\n\t\t\t\tString postAdviceRef = DomUtils.getChildElementByTagName(prePostElt, Elements.POST_INVOCATION_ADVICE)\n\t\t\t\t\t.getAttribute(\"ref\");\n\t\t\t\tmds.addConstructorArgReference(attributeFactoryRef);\n\t\t\t\tpreInvocationVoterBldr.addConstructorArgReference(preAdviceRef);\n\t\t\t\tafterInvocationBldr.addConstructorArgReference(postAdviceRef);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The default expression-based system\n\t\t\t\tString expressionHandlerRef = (expressionHandlerElt != null) ? expressionHandlerElt.getAttribute(\"ref\")\n\t\t\t\t\t\t: null;\n\t\t\t\tif (StringUtils.hasText(expressionHandlerRef)) {\n\t\t\t\t\tthis.logger.info(LogMessage.format(\"Using bean '%s' as method ExpressionHandler implementation\",\n\t\t\t\t\t\t\texpressionHandlerRef));\n\t\t\t\t\tRootBeanDefinition lazyInitPP = new RootBeanDefinition(\n\t\t\t\t\t\t\tLazyInitBeanDefinitionRegistryPostProcessor.class);\n\t\t\t\t\tlazyInitPP.getConstructorArgumentValues().addGenericArgumentValue(expressionHandlerRef);\n\t\t\t\t\tpc.getReaderContext().registerWithGeneratedName(lazyInitPP);\n\t\t\t\t\tBeanDefinitionBuilder lazyMethodSecurityExpressionHandlerBldr = BeanDefinitionBuilder\n\t\t\t\t\t\t.rootBeanDefinition(LazyInitTargetSource.class);\n\t\t\t\t\tlazyMethodSecurityExpressionHandlerBldr.addPropertyValue(\"targetBeanName\", expressionHandlerRef);\n\t\t\t\t\tBeanDefinitionBuilder expressionHandlerProxyBldr = BeanDefinitionBuilder\n\t\t\t\t\t\t.rootBeanDefinition(ProxyFactoryBean.class);\n\t\t\t\t\texpressionHandlerProxyBldr.addPropertyValue(\"targetSource\",\n\t\t\t\t\t\t\tlazyMethodSecurityExpressionHandlerBldr.getBeanDefinition());\n\t\t\t\t\texpressionHandlerProxyBldr.addPropertyValue(\"proxyInterfaces\",\n\t\t\t\t\t\t\tMethodSecurityExpressionHandler.class);\n\t\t\t\t\texpressionHandlerRef = pc.getReaderContext()\n\t\t\t\t\t\t.generateBeanName(expressionHandlerProxyBldr.getBeanDefinition());\n\t\t\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(expressionHandlerProxyBldr.getBeanDefinition(),\n\t\t\t\t\t\t\texpressionHandlerRef));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tRootBeanDefinition expressionHandler = registerWithDefaultRolePrefix(pc,\n\t\t\t\t\t\t\tDefaultMethodSecurityExpressionHandlerBeanFactory.class);\n\t\t\t\t\texpressionHandlerRef = pc.getReaderContext().generateBeanName(expressionHandler);\n\t\t\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));\n\t\t\t\t\tthis.logger.info(\"Expressions were enabled for method security but no SecurityExpressionHandler \"\n\t\t\t\t\t\t\t+ \"was configured. All hasPermission() expressions will evaluate to false.\");\n\t\t\t\t}\n\t\t\t\tBeanDefinitionBuilder expressionPreAdviceBldr = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(ExpressionBasedPreInvocationAdvice.class);\n\t\t\t\texpressionPreAdviceBldr.addPropertyReference(\"expressionHandler\", expressionHandlerRef);\n\t\t\t\tpreInvocationVoterBldr.addConstructorArgValue(expressionPreAdviceBldr.getBeanDefinition());\n\t\t\t\tBeanDefinitionBuilder expressionPostAdviceBldr = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(ExpressionBasedPostInvocationAdvice.class);\n\t\t\t\texpressionPostAdviceBldr.addConstructorArgReference(expressionHandlerRef);\n\t\t\t\tafterInvocationBldr.addConstructorArgValue(expressionPostAdviceBldr.getBeanDefinition());\n\t\t\t\tBeanDefinitionBuilder annotationInvocationFactory = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(ExpressionBasedAnnotationAttributeFactory.class);\n\t\t\t\tannotationInvocationFactory.addConstructorArgReference(expressionHandlerRef);\n\t\t\t\tmds.addConstructorArgValue(annotationInvocationFactory.getBeanDefinition());\n\t\t\t}\n\t\t\tpreInvocationVoter = preInvocationVoterBldr.getBeanDefinition();\n\t\t\tafterInvocationProviders.add(afterInvocationBldr.getBeanDefinition());\n\t\t\tdelegates.add(mds.getBeanDefinition());\n\t\t}\n\t\tif (useSecured) {\n\t\t\tdelegates.add(BeanDefinitionBuilder.rootBeanDefinition(SecuredAnnotationSecurityMetadataSource.class)\n\t\t\t\t.getBeanDefinition());\n\t\t}\n\t\tif (jsr250Enabled) {\n\t\t\tRootBeanDefinition jsrMetadataSource = registerWithDefaultRolePrefix(pc,\n\t\t\t\t\tJsr250MethodSecurityMetadataSourceBeanFactory.class);\n\t\t\tdelegates.add(jsrMetadataSource);\n\t\t}\n\t\t// Now create a Map<String, ConfigAttribute> for each <protect-pointcut>\n\t\t// sub-element\n\t\tMap<String, List<ConfigAttribute>> pointcutMap = parseProtectPointcuts(pc,\n\t\t\t\tDomUtils.getChildElementsByTagName(element, Elements.PROTECT_POINTCUT));\n\t\tif (pointcutMap.size() > 0) {\n\t\t\tif (useAspectJ) {\n\t\t\t\tpc.getReaderContext().error(\"You can't use AspectJ mode with protect-pointcut definitions\", source);\n\t\t\t}\n\t\t\t// Only add it if there are actually any pointcuts defined.\n\t\t\tBeanDefinition mapBasedMetadataSource = new RootBeanDefinition(MapBasedMethodSecurityMetadataSource.class);\n\t\t\tBeanReference ref = new RuntimeBeanReference(\n\t\t\t\t\tpc.getReaderContext().generateBeanName(mapBasedMetadataSource));\n\t\t\tdelegates.add(ref);\n\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(mapBasedMetadataSource, ref.getBeanName()));\n\t\t\tregisterProtectPointcutPostProcessor(pc, pointcutMap, ref, source);\n\t\t}\n\t\tBeanReference metadataSource = registerDelegatingMethodSecurityMetadataSource(pc, delegates, source);\n\t\t// Check for additional after-invocation-providers..\n\t\tList<Element> afterInvocationElts = DomUtils.getChildElementsByTagName(element,\n\t\t\t\tElements.AFTER_INVOCATION_PROVIDER);\n\t\tfor (Element elt : afterInvocationElts) {\n\t\t\tafterInvocationProviders.add(new RuntimeBeanReference(elt.getAttribute(ATT_REF)));\n\t\t}\n\t\tString accessManagerId = element.getAttribute(ATT_ACCESS_MGR);\n\t\tif (!StringUtils.hasText(accessManagerId)) {\n\t\t\taccessManagerId = registerAccessManager(pc, jsr250Enabled, preInvocationVoter);\n\t\t}\n\t\tString authMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);\n\t\tString runAsManagerId = element.getAttribute(ATT_RUN_AS_MGR);\n\t\tBeanReference interceptor = registerMethodSecurityInterceptor(pc, authMgrRef, accessManagerId, runAsManagerId,\n\t\t\t\tmetadataSource, afterInvocationProviders, source, useAspectJ);\n\t\tif (useAspectJ) {\n\t\t\tBeanDefinitionBuilder aspect = BeanDefinitionBuilder.rootBeanDefinition(\n\t\t\t\t\t\"org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect\");\n\t\t\taspect.setFactoryMethod(\"aspectOf\");\n\t\t\taspect.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\t\taspect.addPropertyValue(\"securityInterceptor\", interceptor);\n\t\t\tString id = pc.getReaderContext().registerWithGeneratedName(aspect.getBeanDefinition());\n\t\t\tpc.registerBeanComponent(new BeanComponentDefinition(aspect.getBeanDefinition(), id));\n\t\t}\n\t\telse {\n\t\t\tregisterAdvisor(pc, interceptor, metadataSource, source, element.getAttribute(ATT_ADVICE_ORDER));\n\t\t\tAopNamespaceUtils.registerAutoProxyCreatorIfNecessary(pc, element);\n\t\t}\n\t\tpc.popAndRegisterContainingComponent();\n\t\treturn null;\n\t}\n\n\t/**\n\t * Register the default AccessDecisionManager. Adds the special JSR 250 voter jsr-250\n\t * is enabled and an expression voter if expression-based access control is enabled.\n\t * @return\n\t */\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprivate String registerAccessManager(ParserContext pc, boolean jsr250Enabled, BeanDefinition expressionVoter) {\n\t\tBeanDefinitionBuilder accessMgrBuilder = BeanDefinitionBuilder.rootBeanDefinition(AffirmativeBased.class);\n\t\tManagedList voters = new ManagedList(4);\n\t\tif (expressionVoter != null) {\n\t\t\tvoters.add(expressionVoter);\n\t\t}\n\t\tvoters.add(new RootBeanDefinition(RoleVoter.class));\n\t\tvoters.add(new RootBeanDefinition(AuthenticatedVoter.class));\n\t\tif (jsr250Enabled) {\n\t\t\tvoters.add(new RootBeanDefinition(Jsr250Voter.class));\n\t\t}\n\t\taccessMgrBuilder.addConstructorArgValue(voters);\n\t\tBeanDefinition accessManager = accessMgrBuilder.getBeanDefinition();\n\t\tString id = pc.getReaderContext().generateBeanName(accessManager);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(accessManager, id));\n\t\treturn id;\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\tprivate BeanReference registerDelegatingMethodSecurityMetadataSource(ParserContext pc, ManagedList delegates,\n\t\t\tObject source) {\n\t\tRootBeanDefinition delegatingMethodSecurityMetadataSource = new RootBeanDefinition(\n\t\t\t\tDelegatingMethodSecurityMetadataSource.class);\n\t\tdelegatingMethodSecurityMetadataSource.setSource(source);\n\t\tdelegatingMethodSecurityMetadataSource.getConstructorArgumentValues().addGenericArgumentValue(delegates);\n\t\tString id = pc.getReaderContext().generateBeanName(delegatingMethodSecurityMetadataSource);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(delegatingMethodSecurityMetadataSource, id));\n\n\t\treturn new RuntimeBeanReference(id);\n\t}\n\n\tprivate void registerProtectPointcutPostProcessor(ParserContext parserContext,\n\t\t\tMap<String, List<ConfigAttribute>> pointcutMap, BeanReference mapBasedMethodSecurityMetadataSource,\n\t\t\tObject source) {\n\t\tRootBeanDefinition ppbp = new RootBeanDefinition(ProtectPointcutPostProcessor.class);\n\t\tppbp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\tppbp.setSource(source);\n\t\tppbp.getConstructorArgumentValues().addGenericArgumentValue(mapBasedMethodSecurityMetadataSource);\n\t\tppbp.getPropertyValues().addPropertyValue(\"pointcutMap\", pointcutMap);\n\t\tparserContext.getReaderContext().registerWithGeneratedName(ppbp);\n\t}\n\n\tprivate Map<String, List<ConfigAttribute>> parseProtectPointcuts(ParserContext parserContext,\n\t\t\tList<Element> protectPointcutElts) {\n\t\tMap<String, List<ConfigAttribute>> pointcutMap = new LinkedHashMap<>();\n\t\tfor (Element childElt : protectPointcutElts) {\n\t\t\tString accessConfig = childElt.getAttribute(ATT_ACCESS);\n\t\t\tString expression = childElt.getAttribute(ATT_EXPRESSION);\n\t\t\tif (!StringUtils.hasText(accessConfig)) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"Access configuration required\", parserContext.extractSource(childElt));\n\t\t\t}\n\t\t\tif (!StringUtils.hasText(expression)) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"Pointcut expression required\", parserContext.extractSource(childElt));\n\t\t\t}\n\t\t\tString[] attributeTokens = StringUtils.commaDelimitedListToStringArray(accessConfig);\n\t\t\tList<ConfigAttribute> attributes = new ArrayList<>(attributeTokens.length);\n\t\t\tfor (String token : attributeTokens) {\n\t\t\t\tattributes.add(new SecurityConfig(token));\n\t\t\t}\n\t\t\tpointcutMap.put(expression, attributes);\n\t\t}\n\t\treturn pointcutMap;\n\t}\n\n\tprivate BeanReference registerMethodSecurityInterceptor(ParserContext pc, String authMgrRef, String accessManagerId,\n\t\t\tString runAsManagerId, BeanReference metadataSource, List<BeanMetadataElement> afterInvocationProviders,\n\t\t\tObject source, boolean useAspectJ) {\n\t\tBeanDefinitionBuilder bldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(useAspectJ ? AspectJMethodSecurityInterceptor.class : MethodSecurityInterceptor.class);\n\t\tbldr.getRawBeanDefinition().setSource(source);\n\t\tbldr.addPropertyReference(\"accessDecisionManager\", accessManagerId);\n\t\tRootBeanDefinition authMgr = new RootBeanDefinition(AuthenticationManagerDelegator.class);\n\t\tauthMgr.getConstructorArgumentValues().addGenericArgumentValue(authMgrRef);\n\t\tbldr.addPropertyValue(\"authenticationManager\", authMgr);\n\t\tbldr.addPropertyValue(\"securityMetadataSource\", metadataSource);\n\t\tif (StringUtils.hasText(runAsManagerId)) {\n\t\t\tbldr.addPropertyReference(\"runAsManager\", runAsManagerId);\n\t\t}\n\t\tif (!afterInvocationProviders.isEmpty()) {\n\t\t\tBeanDefinition afterInvocationManager;\n\t\t\tafterInvocationManager = new RootBeanDefinition(AfterInvocationProviderManager.class);\n\t\t\tafterInvocationManager.getPropertyValues().addPropertyValue(\"providers\", afterInvocationProviders);\n\t\t\tbldr.addPropertyValue(\"afterInvocationManager\", afterInvocationManager);\n\t\t}\n\t\tBeanDefinition bean = bldr.getBeanDefinition();\n\t\tString id = pc.getReaderContext().generateBeanName(bean);\n\t\tpc.registerBeanComponent(new BeanComponentDefinition(bean, id));\n\t\treturn new RuntimeBeanReference(id);\n\t}\n\n\tprivate void registerAdvisor(ParserContext parserContext, BeanReference interceptor, BeanReference metadataSource,\n\t\t\tObject source, String adviceOrder) {\n\t\tif (parserContext.getRegistry().containsBeanDefinition(BeanIds.METHOD_SECURITY_METADATA_SOURCE_ADVISOR)) {\n\t\t\tparserContext.getReaderContext().error(\"Duplicate <global-method-security> detected.\", source);\n\t\t}\n\t\tRootBeanDefinition advisor = new RootBeanDefinition(MethodSecurityMetadataSourceAdvisor.class);\n\t\tif (StringUtils.hasText(adviceOrder)) {\n\t\t\tadvisor.getPropertyValues().addPropertyValue(\"order\", adviceOrder);\n\t\t}\n\t\t// advisor must be an infrastructure bean as Spring's\n\t\t// InfrastructureAdvisorAutoProxyCreator will ignore it\n\t\t// otherwise\n\t\tadvisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\tadvisor.setSource(source);\n\t\tadvisor.getConstructorArgumentValues().addGenericArgumentValue(interceptor.getBeanName());\n\t\tadvisor.getConstructorArgumentValues().addGenericArgumentValue(metadataSource);\n\t\tadvisor.getConstructorArgumentValues().addGenericArgumentValue(metadataSource.getBeanName());\n\t\tparserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_METADATA_SOURCE_ADVISOR, advisor);\n\t}\n\n\tprivate RootBeanDefinition registerWithDefaultRolePrefix(ParserContext pc,\n\t\t\tClass<? extends AbstractGrantedAuthorityDefaultsBeanFactory> beanFactoryClass) {\n\t\tRootBeanDefinition beanFactoryDefinition = new RootBeanDefinition(beanFactoryClass);\n\t\tString beanFactoryRef = pc.getReaderContext().generateBeanName(beanFactoryDefinition);\n\t\tpc.getRegistry().registerBeanDefinition(beanFactoryRef, beanFactoryDefinition);\n\t\tRootBeanDefinition bean = new RootBeanDefinition();\n\t\tbean.setFactoryBeanName(beanFactoryRef);\n\t\tbean.setFactoryMethodName(\"getBean\");\n\t\treturn bean;\n\t}\n\n\t/**\n\t * Delays the lookup of the AuthenticationManager within MethodSecurityInterceptor, to\n\t * prevent issues like SEC-933.\n\t *\n\t * @author Luke Taylor\n\t * @since 3.0\n\t */\n\tstatic final class AuthenticationManagerDelegator implements AuthenticationManager, BeanFactoryAware {\n\n\t\tprivate AuthenticationManager delegate;\n\n\t\tprivate final Object delegateMonitor = new Object();\n\n\t\tprivate BeanFactory beanFactory;\n\n\t\tprivate final String authMgrBean;\n\n\t\tAuthenticationManagerDelegator(String authMgrBean) {\n\t\t\tthis.authMgrBean = StringUtils.hasText(authMgrBean) ? authMgrBean : BeanIds.AUTHENTICATION_MANAGER;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\tsynchronized (this.delegateMonitor) {\n\t\t\t\tif (this.delegate == null) {\n\t\t\t\t\tAssert.state(this.beanFactory != null,\n\t\t\t\t\t\t\t() -> \"BeanFactory must be set to resolve \" + this.authMgrBean);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.delegate = this.beanFactory.getBean(this.authMgrBean, AuthenticationManager.class);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t\t\t\t\tif (BeanIds.AUTHENTICATION_MANAGER.equals(ex.getBeanName())) {\n\t\t\t\t\t\t\tthrow new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER,\n\t\t\t\t\t\t\t\t\tAuthenticationManagerFactoryBean.MISSING_BEAN_ERROR_MESSAGE);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this.delegate.authenticate(authentication);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\t\tthis.beanFactory = beanFactory;\n\t\t}\n\n\t}\n\n\tstatic class Jsr250MethodSecurityMetadataSourceBeanFactory extends AbstractGrantedAuthorityDefaultsBeanFactory {\n\n\t\tprivate Jsr250MethodSecurityMetadataSource source = new Jsr250MethodSecurityMetadataSource();\n\n\t\tJsr250MethodSecurityMetadataSource getBean() {\n\t\t\tthis.source.setDefaultRolePrefix(this.rolePrefix);\n\t\t\treturn this.source;\n\t\t}\n\n\t}\n\n\tstatic class DefaultMethodSecurityExpressionHandlerBeanFactory extends AbstractGrantedAuthorityDefaultsBeanFactory {\n\n\t\tprivate DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();\n\n\t\tDefaultMethodSecurityExpressionHandler getBean() {\n\t\t\tthis.handler.setDefaultRolePrefix(this.rolePrefix);\n\t\t\treturn this.handler;\n\t\t}\n\n\t}\n\n\tabstract static class AbstractGrantedAuthorityDefaultsBeanFactory implements ApplicationContextAware {\n\n\t\tprotected String rolePrefix = \"ROLE_\";\n\n\t\t@Override\n\t\tpublic final void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\t\tapplicationContext.getBeanProvider(GrantedAuthorityDefaults.class)\n\t\t\t\t.ifUnique((grantedAuthorityDefaults) -> this.rolePrefix = grantedAuthorityDefaults.getRolePrefix());\n\t\t}\n\n\t}\n\n\t/**\n\t * Delays setting a bean of a given name to be lazyily initialized until after all the\n\t * beans are registered.\n\t *\n\t * @author Rob Winch\n\t * @since 3.2\n\t */\n\tprivate static final class LazyInitBeanDefinitionRegistryPostProcessor\n\t\t\timplements BeanDefinitionRegistryPostProcessor {\n\n\t\tprivate final String beanName;\n\n\t\tprivate LazyInitBeanDefinitionRegistryPostProcessor(String beanName) {\n\t\t\tthis.beanName = beanName;\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\t\tif (!registry.containsBeanDefinition(this.beanName)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tBeanDefinition beanDefinition = registry.getBeanDefinition(this.beanName);\n\t\t\tbeanDefinition.setLazyInit(true);\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecorator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.config.AbstractInterceptorDrivenBeanDefinitionDecorator;\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanDefinitionHolder;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.support.AbstractBeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionDecorator;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;\nimport org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\nimport org.springframework.security.authorization.method.MethodExpressionAuthorizationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.Elements;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * @author Luke Taylor\n * @author Ben Alex\n *\n */\npublic class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDecorator {\n\n\tprivate final InternalAuthorizationManagerInterceptMethodsBeanDefinitionDecorator authorizationManagerDelegate = new InternalAuthorizationManagerInterceptMethodsBeanDefinitionDecorator();\n\n\tprivate final BeanDefinitionDecorator delegate = new InternalInterceptMethodsBeanDefinitionDecorator();\n\n\t@Override\n\tpublic BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {\n\t\tif (this.authorizationManagerDelegate.supports(node)) {\n\t\t\treturn this.authorizationManagerDelegate.decorate(node, definition, parserContext);\n\t\t}\n\t\tMethodConfigUtils.registerDefaultMethodAccessManagerIfNecessary(parserContext);\n\t\treturn this.delegate.decorate(node, definition, parserContext);\n\t}\n\n\tstatic class InternalAuthorizationManagerInterceptMethodsBeanDefinitionDecorator\n\t\t\textends AbstractInterceptorDrivenBeanDefinitionDecorator {\n\n\t\tstatic final String ATT_METHOD = \"method\";\n\n\t\tstatic final String ATT_ACCESS = \"access\";\n\n\t\tprivate static final String ATT_USE_AUTHORIZATION_MGR = \"use-authorization-manager\";\n\n\t\tprivate static final String ATT_AUTHORIZATION_MGR = \"authorization-manager-ref\";\n\n\t\t@Override\n\t\tprotected BeanDefinition createInterceptorDefinition(Node node) {\n\t\t\tElement interceptMethodsElt = (Element) node;\n\t\t\tBeanDefinitionBuilder interceptor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(AuthorizationManagerBeforeMethodInterceptor.class);\n\t\t\tinterceptor.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);\n\t\t\tMap<Pointcut, BeanMetadataElement> managers = new ManagedMap<>();\n\t\t\tList<Element> methods = DomUtils.getChildElementsByTagName(interceptMethodsElt, Elements.PROTECT);\n\t\t\tfor (Element protectElt : methods) {\n\t\t\t\tmanagers.put(pointcut(interceptMethodsElt, protectElt),\n\t\t\t\t\t\tauthorizationManager(interceptMethodsElt, protectElt));\n\t\t\t}\n\t\t\treturn interceptor.addConstructorArgValue(Pointcut.TRUE)\n\t\t\t\t.addConstructorArgValue(authorizationManager(managers))\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\n\t\tboolean supports(Node node) {\n\t\t\tElement interceptMethodsElt = (Element) node;\n\t\t\tif (StringUtils.hasText(interceptMethodsElt.getAttribute(ATT_AUTHORIZATION_MGR))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (StringUtils.hasText(interceptMethodsElt.getAttribute(ATT_USE_AUTHORIZATION_MGR))) {\n\t\t\t\treturn Boolean.parseBoolean(interceptMethodsElt.getAttribute(ATT_USE_AUTHORIZATION_MGR));\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate Pointcut pointcut(Element interceptorElt, Element protectElt) {\n\t\t\tString method = protectElt.getAttribute(ATT_METHOD);\n\t\t\tString parentBeanClass = ((Element) interceptorElt.getParentNode()).getAttribute(\"class\");\n\t\t\treturn PrefixBasedMethodMatcher.fromClass(parentBeanClass, method);\n\t\t}\n\n\t\tprivate BeanMetadataElement authorizationManager(Element interceptMethodsElt, Element protectElt) {\n\t\t\tString authorizationManager = interceptMethodsElt.getAttribute(ATT_AUTHORIZATION_MGR);\n\t\t\tif (StringUtils.hasText(authorizationManager)) {\n\t\t\t\treturn new RuntimeBeanReference(authorizationManager);\n\t\t\t}\n\t\t\tString access = protectElt.getAttribute(ATT_ACCESS);\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(MethodExpressionAuthorizationManager.class)\n\t\t\t\t.addConstructorArgValue(access)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\n\t\tprivate BeanMetadataElement authorizationManager(Map<Pointcut, BeanMetadataElement> managers) {\n\t\t\treturn BeanDefinitionBuilder.rootBeanDefinition(PointcutDelegatingAuthorizationManager.class)\n\t\t\t\t.addConstructorArgValue(managers)\n\t\t\t\t.getBeanDefinition();\n\t\t}\n\n\t}\n\n\t/**\n\t * This is the real class which does the work. We need access to the ParserContext in\n\t * order to do bean registration.\n\t *\n\t * @deprecated Use\n\t * {@link InternalAuthorizationManagerInterceptMethodsBeanDefinitionDecorator}\n\t */\n\t@Deprecated\n\tstatic class InternalInterceptMethodsBeanDefinitionDecorator\n\t\t\textends AbstractInterceptorDrivenBeanDefinitionDecorator {\n\n\t\tstatic final String ATT_METHOD = \"method\";\n\n\t\tstatic final String ATT_ACCESS = \"access\";\n\n\t\tprivate static final String ATT_ACCESS_MGR = \"access-decision-manager-ref\";\n\n\t\t@Override\n\t\tprotected BeanDefinition createInterceptorDefinition(Node node) {\n\t\t\tElement interceptMethodsElt = (Element) node;\n\t\t\tBeanDefinitionBuilder interceptor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(MethodSecurityInterceptor.class);\n\t\t\t// Default to autowiring to pick up after invocation mgr\n\t\t\tinterceptor.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);\n\t\t\tString accessManagerId = interceptMethodsElt.getAttribute(ATT_ACCESS_MGR);\n\t\t\tif (!StringUtils.hasText(accessManagerId)) {\n\t\t\t\taccessManagerId = BeanIds.METHOD_ACCESS_MANAGER;\n\t\t\t}\n\t\t\tinterceptor.addPropertyValue(\"accessDecisionManager\", new RuntimeBeanReference(accessManagerId));\n\t\t\tinterceptor.addPropertyValue(\"authenticationManager\",\n\t\t\t\t\tnew RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));\n\t\t\t// Lookup parent bean information\n\t\t\tString parentBeanClass = ((Element) interceptMethodsElt.getParentNode()).getAttribute(\"class\");\n\t\t\t// Parse the included methods\n\t\t\tList<Element> methods = DomUtils.getChildElementsByTagName(interceptMethodsElt, Elements.PROTECT);\n\t\t\tMap<String, BeanDefinition> mappings = new ManagedMap<>();\n\t\t\tfor (Element protectmethodElt : methods) {\n\t\t\t\tBeanDefinitionBuilder attributeBuilder = BeanDefinitionBuilder.rootBeanDefinition(SecurityConfig.class);\n\t\t\t\tattributeBuilder.setFactoryMethod(\"createListFromCommaDelimitedString\");\n\t\t\t\tattributeBuilder.addConstructorArgValue(protectmethodElt.getAttribute(ATT_ACCESS));\n\t\t\t\t// Support inference of class names\n\t\t\t\tString methodName = protectmethodElt.getAttribute(ATT_METHOD);\n\t\t\t\tif (methodName.lastIndexOf(\".\") == -1) {\n\t\t\t\t\tif (parentBeanClass != null && !\"\".equals(parentBeanClass)) {\n\t\t\t\t\t\tmethodName = parentBeanClass + \".\" + methodName;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmappings.put(methodName, attributeBuilder.getBeanDefinition());\n\t\t\t}\n\t\t\tBeanDefinition metadataSource = new RootBeanDefinition(MapBasedMethodSecurityMetadataSource.class);\n\t\t\tmetadataSource.getConstructorArgumentValues().addGenericArgumentValue(mappings);\n\t\t\tinterceptor.addPropertyValue(\"securityMetadataSource\", metadataSource);\n\t\t\treturn interceptor.getBeanDefinition();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/MethodConfigUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.access.AccessDecisionVoter;\nimport org.springframework.security.access.vote.AffirmativeBased;\nimport org.springframework.security.access.vote.AuthenticatedVoter;\nimport org.springframework.security.access.vote.RoleVoter;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.BeanIds;\n\n/**\n * Utility methods used internally by the Spring Security namespace configuration code.\n *\n * @author Luke Taylor\n * @author Ben Alex\n * @author Rob Winch\n * @deprecated Please use {@link AuthorizationManager} instead\n */\n@Deprecated\nabstract class MethodConfigUtils {\n\n\t@SuppressWarnings(\"unchecked\")\n\tstatic void registerDefaultMethodAccessManagerIfNecessary(ParserContext parserContext) {\n\t\tif (!parserContext.getRegistry().containsBeanDefinition(BeanIds.METHOD_ACCESS_MANAGER)) {\n\t\t\tparserContext.getRegistry()\n\t\t\t\t.registerBeanDefinition(BeanIds.METHOD_ACCESS_MANAGER,\n\t\t\t\t\t\tcreateAccessManagerBean(RoleVoter.class, AuthenticatedVoter.class));\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static RootBeanDefinition createAccessManagerBean(Class<? extends AccessDecisionVoter>... voters) {\n\t\tManagedList defaultVoters = new ManagedList(voters.length);\n\t\tfor (Class<? extends AccessDecisionVoter> voter : voters) {\n\t\t\tdefaultVoters.add(new RootBeanDefinition(voter));\n\t\t}\n\t\tBeanDefinitionBuilder accessMgrBuilder = BeanDefinitionBuilder.rootBeanDefinition(AffirmativeBased.class);\n\t\taccessMgrBuilder.addConstructorArgValue(defaultVoters);\n\t\treturn (RootBeanDefinition) accessMgrBuilder.getBeanDefinition();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.w3c.dom.Element;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.config.AopNamespaceUtils;\nimport org.springframework.aop.support.Pointcuts;\nimport org.springframework.beans.BeanMetadataElement;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.CompositeComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.ObservationAuthorizationManager;\nimport org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\nimport org.springframework.security.authorization.method.Jsr250AuthorizationManager;\nimport org.springframework.security.authorization.method.MethodExpressionAuthorizationManager;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;\nimport org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;\nimport org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;\nimport org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;\nimport org.springframework.security.authorization.method.SecuredAuthorizationManager;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Processes the top-level \"method-security\" element.\n *\n * @author Josh Cummings\n * @author Ngoc Nhan\n * @since 5.6\n */\npublic class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final String ATT_USE_JSR250 = \"jsr250-enabled\";\n\n\tprivate static final String ATT_USE_SECURED = \"secured-enabled\";\n\n\tprivate static final String ATT_USE_PREPOST = \"pre-post-enabled\";\n\n\tprivate static final String ATT_AUTHORIZATION_MGR = \"authorization-manager-ref\";\n\n\tprivate static final String ATT_OBSERVATION_REGISTRY_REF = \"observation-registry-ref\";\n\n\tprivate static final String ATT_ACCESS = \"access\";\n\n\tprivate static final String ATT_EXPRESSION = \"expression\";\n\n\tprivate static final String ATT_MODE = \"mode\";\n\n\tprivate static final String ATT_SECURITY_CONTEXT_HOLDER_STRATEGY_REF = \"security-context-holder-strategy-ref\";\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext pc) {\n\t\tCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),\n\t\t\t\tpc.extractSource(element));\n\t\tpc.pushContainingComponent(compositeDef);\n\t\tBeanMetadataElement securityContextHolderStrategy = getSecurityContextHolderStrategy(element);\n\t\tBeanMetadataElement observationRegistry = getObservationRegistry(element);\n\t\tboolean prePostAnnotationsEnabled = !element.hasAttribute(ATT_USE_PREPOST)\n\t\t\t\t|| \"true\".equals(element.getAttribute(ATT_USE_PREPOST));\n\t\tboolean useAspectJ = \"aspectj\".equals(element.getAttribute(ATT_MODE));\n\t\tif (prePostAnnotationsEnabled) {\n\t\t\tBeanDefinitionBuilder preFilterInterceptor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PreFilterAuthorizationMethodInterceptor.class)\n\t\t\t\t.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", securityContextHolderStrategy);\n\t\t\tBeanDefinitionBuilder preAuthorizeInterceptor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PreAuthorizeAuthorizationMethodInterceptor.class)\n\t\t\t\t.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", securityContextHolderStrategy)\n\t\t\t\t.addPropertyValue(\"observationRegistry\", observationRegistry);\n\t\t\tBeanDefinitionBuilder postAuthorizeInterceptor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PostAuthorizeAuthorizationMethodInterceptor.class)\n\t\t\t\t.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", securityContextHolderStrategy)\n\t\t\t\t.addPropertyValue(\"observationRegistry\", observationRegistry);\n\t\t\tBeanDefinitionBuilder postFilterInterceptor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PostFilterAuthorizationMethodInterceptor.class)\n\t\t\t\t.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", securityContextHolderStrategy);\n\t\t\tElement expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);\n\t\t\tif (expressionHandlerElt != null) {\n\t\t\t\tString expressionHandlerRef = expressionHandlerElt.getAttribute(\"ref\");\n\t\t\t\tpreFilterInterceptor.addPropertyReference(\"expressionHandler\", expressionHandlerRef);\n\t\t\t\tpreAuthorizeInterceptor.addPropertyReference(\"expressionHandler\", expressionHandlerRef);\n\t\t\t\tpostAuthorizeInterceptor.addPropertyReference(\"expressionHandler\", expressionHandlerRef);\n\t\t\t\tpostFilterInterceptor.addPropertyReference(\"expressionHandler\", expressionHandlerRef);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tBeanDefinition expressionHandler = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(MethodSecurityExpressionHandlerBean.class)\n\t\t\t\t\t.getBeanDefinition();\n\t\t\t\tpreFilterInterceptor.addPropertyValue(\"expressionHandler\", expressionHandler);\n\t\t\t\tpreAuthorizeInterceptor.addPropertyValue(\"expressionHandler\", expressionHandler);\n\t\t\t\tpostAuthorizeInterceptor.addPropertyValue(\"expressionHandler\", expressionHandler);\n\t\t\t\tpostFilterInterceptor.addPropertyValue(\"expressionHandler\", expressionHandler);\n\t\t\t}\n\t\t\tpc.getRegistry()\n\t\t\t\t.registerBeanDefinition(\"preFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\t\tpreFilterInterceptor.getBeanDefinition());\n\t\t\tpc.getRegistry()\n\t\t\t\t.registerBeanDefinition(\"preAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\t\tpreAuthorizeInterceptor.getBeanDefinition());\n\t\t\tpc.getRegistry()\n\t\t\t\t.registerBeanDefinition(\"postAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\t\tpostAuthorizeInterceptor.getBeanDefinition());\n\t\t\tpc.getRegistry()\n\t\t\t\t.registerBeanDefinition(\"postFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\t\tpostFilterInterceptor.getBeanDefinition());\n\t\t}\n\t\tboolean securedEnabled = \"true\".equals(element.getAttribute(ATT_USE_SECURED));\n\t\tif (securedEnabled) {\n\t\t\tBeanDefinitionBuilder securedInterceptor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(SecuredAuthorizationMethodInterceptor.class)\n\t\t\t\t.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", securityContextHolderStrategy)\n\t\t\t\t.addPropertyValue(\"observationRegistry\", observationRegistry);\n\t\t\tpc.getRegistry()\n\t\t\t\t.registerBeanDefinition(\"securedAuthorizationMethodInterceptor\",\n\t\t\t\t\t\tsecuredInterceptor.getBeanDefinition());\n\t\t}\n\t\tboolean jsr250Enabled = \"true\".equals(element.getAttribute(ATT_USE_JSR250));\n\t\tif (jsr250Enabled) {\n\t\t\tBeanDefinitionBuilder jsr250Interceptor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(Jsr250AuthorizationMethodInterceptor.class)\n\t\t\t\t.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", securityContextHolderStrategy)\n\t\t\t\t.addPropertyValue(\"observationRegistry\", observationRegistry);\n\t\t\tpc.getRegistry()\n\t\t\t\t.registerBeanDefinition(\"jsr250AuthorizationMethodInterceptor\", jsr250Interceptor.getBeanDefinition());\n\t\t}\n\t\tMap<Pointcut, BeanMetadataElement> managers = new ManagedMap<>();\n\t\tList<Element> methods = DomUtils.getChildElementsByTagName(element, Elements.PROTECT_POINTCUT);\n\t\tif (useAspectJ) {\n\t\t\tif (!methods.isEmpty()) {\n\t\t\t\tpc.getReaderContext()\n\t\t\t\t\t.error(\"Cannot use <protect-pointcut> and mode='aspectj' together\", pc.extractSource(element));\n\t\t\t}\n\t\t\tregisterInterceptors(pc.getRegistry());\n\t\t}\n\t\telse {\n\t\t\tif (!methods.isEmpty()) {\n\t\t\t\tfor (Element protectElt : methods) {\n\t\t\t\t\tmanagers.put(pointcut(protectElt), authorizationManager(element, protectElt));\n\t\t\t\t}\n\t\t\t\tBeanDefinitionBuilder protectPointcutInterceptor = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(AuthorizationManagerBeforeMethodInterceptor.class)\n\t\t\t\t\t.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\t\t\t\t.addPropertyValue(\"securityContextHolderStrategy\", securityContextHolderStrategy)\n\t\t\t\t\t.addConstructorArgValue(pointcut(managers.keySet()))\n\t\t\t\t\t.addConstructorArgValue(authorizationManager(managers));\n\t\t\t\tpc.getRegistry()\n\t\t\t\t\t.registerBeanDefinition(\"protectPointcutInterceptor\",\n\t\t\t\t\t\t\tprotectPointcutInterceptor.getBeanDefinition());\n\t\t\t}\n\t\t\tAopNamespaceUtils.registerAutoProxyCreatorIfNecessary(pc, element);\n\t\t}\n\t\tpc.popAndRegisterContainingComponent();\n\t\treturn null;\n\t}\n\n\tprivate BeanMetadataElement getObservationRegistry(Element methodSecurityElmt) {\n\t\tString holderStrategyRef = methodSecurityElmt.getAttribute(ATT_OBSERVATION_REGISTRY_REF);\n\t\tif (StringUtils.hasText(holderStrategyRef)) {\n\t\t\treturn new RuntimeBeanReference(holderStrategyRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(ObservationRegistryFactory.class).getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement getSecurityContextHolderStrategy(Element methodSecurityElmt) {\n\t\tString holderStrategyRef = methodSecurityElmt.getAttribute(ATT_SECURITY_CONTEXT_HOLDER_STRATEGY_REF);\n\t\tif (StringUtils.hasText(holderStrategyRef)) {\n\t\t\treturn new RuntimeBeanReference(holderStrategyRef);\n\t\t}\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderStrategyFactory.class).getBeanDefinition();\n\t}\n\n\tprivate Pointcut pointcut(Element protectElt) {\n\t\tString expression = protectElt.getAttribute(ATT_EXPRESSION);\n\t\texpression = replaceBooleanOperators(expression);\n\t\treturn new AspectJMethodMatcher(expression);\n\t}\n\n\tprivate Pointcut pointcut(Collection<Pointcut> pointcuts) {\n\t\tPointcut result = null;\n\t\tfor (Pointcut pointcut : pointcuts) {\n\t\t\tif (result == null) {\n\t\t\t\tresult = pointcut;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tresult = Pointcuts.union(result, pointcut);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate String replaceBooleanOperators(String expression) {\n\t\texpression = StringUtils.replace(expression, \" and \", \" && \");\n\t\texpression = StringUtils.replace(expression, \" or \", \" || \");\n\t\texpression = StringUtils.replace(expression, \" not \", \" ! \");\n\t\treturn expression;\n\t}\n\n\tprivate BeanMetadataElement authorizationManager(Element element, Element protectElt) {\n\t\tString authorizationManager = element.getAttribute(ATT_AUTHORIZATION_MGR);\n\t\tif (StringUtils.hasText(authorizationManager)) {\n\t\t\treturn new RuntimeBeanReference(authorizationManager);\n\t\t}\n\t\tString access = protectElt.getAttribute(ATT_ACCESS);\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(MethodExpressionAuthorizationManager.class)\n\t\t\t.addConstructorArgValue(access)\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate BeanMetadataElement authorizationManager(Map<Pointcut, BeanMetadataElement> managers) {\n\t\treturn BeanDefinitionBuilder.rootBeanDefinition(PointcutDelegatingAuthorizationManager.class)\n\t\t\t.addConstructorArgValue(managers)\n\t\t\t.getBeanDefinition();\n\t}\n\n\tprivate void registerInterceptors(BeanDefinitionRegistry registry) {\n\t\tregisterBeanDefinition(\"preFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.PreFilterAspect\", \"preFilterAspect$0\",\n\t\t\t\tregistry);\n\t\tregisterBeanDefinition(\"postFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.PostFilterAspect\", \"postFilterAspect$0\",\n\t\t\t\tregistry);\n\t\tregisterBeanDefinition(\"preAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.PreAuthorizeAspect\", \"preAuthorizeAspect$0\",\n\t\t\t\tregistry);\n\t\tregisterBeanDefinition(\"postAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.PostAuthorizeAspect\",\n\t\t\t\t\"postAuthorizeAspect$0\", registry);\n\t\tregisterBeanDefinition(\"securedAuthorizationMethodInterceptor\",\n\t\t\t\t\"org.springframework.security.authorization.method.aspectj.SecuredAspect\", \"securedAspect$0\", registry);\n\t}\n\n\tprivate void registerBeanDefinition(String beanName, String aspectClassName, String aspectBeanName,\n\t\t\tBeanDefinitionRegistry registry) {\n\t\tif (!registry.containsBeanDefinition(beanName)) {\n\t\t\treturn;\n\t\t}\n\t\tBeanDefinition interceptor = registry.getBeanDefinition(beanName);\n\t\tBeanDefinitionBuilder aspect = BeanDefinitionBuilder.rootBeanDefinition(aspectClassName);\n\t\taspect.setFactoryMethod(\"aspectOf\");\n\t\taspect.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\taspect.addPropertyValue(\"securityInterceptor\", interceptor);\n\t\tregistry.registerBeanDefinition(aspectBeanName, aspect.getBeanDefinition());\n\t}\n\n\tpublic static final class MethodSecurityExpressionHandlerBean\n\t\t\timplements FactoryBean<MethodSecurityExpressionHandler>, ApplicationContextAware {\n\n\t\tprivate final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\n\t\t@Override\n\t\tpublic MethodSecurityExpressionHandler getObject() {\n\t\t\treturn this.expressionHandler;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn MethodSecurityExpressionHandler.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\t\tapplicationContext.getBeanProvider(GrantedAuthorityDefaults.class)\n\t\t\t\t.ifUnique((grantedAuthorityDefaults) -> this.expressionHandler\n\t\t\t\t\t.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix()));\n\t\t}\n\n\t}\n\n\tpublic static final class Jsr250AuthorizationMethodInterceptor\n\t\t\timplements FactoryBean<AuthorizationManagerBeforeMethodInterceptor>, ApplicationContextAware {\n\n\t\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t\t.getContextHolderStrategy();\n\n\t\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\t\tprivate final Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\n\t\t@Override\n\t\tpublic AuthorizationManagerBeforeMethodInterceptor getObject() {\n\t\t\tAuthorizationManager<MethodInvocation> manager = this.manager;\n\t\t\tif (!this.observationRegistry.isNoop()) {\n\t\t\t\tmanager = new ObservationAuthorizationManager<>(this.observationRegistry, this.manager);\n\t\t\t}\n\t\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor\n\t\t\t\t.jsr250(manager);\n\t\t\tinterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\t\treturn interceptor;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn AuthorizationManagerBeforeMethodInterceptor.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\t\tapplicationContext.getBeanProvider(GrantedAuthorityDefaults.class)\n\t\t\t\t.ifUnique((grantedAuthorityDefaults) -> this.manager\n\t\t\t\t\t.setRolePrefix(grantedAuthorityDefaults.getRolePrefix()));\n\t\t}\n\n\t\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t}\n\n\t\tpublic void setObservationRegistry(ObservationRegistry observationRegistry) {\n\t\t\tthis.observationRegistry = observationRegistry;\n\t\t}\n\n\t}\n\n\tpublic static final class SecuredAuthorizationMethodInterceptor\n\t\t\timplements FactoryBean<AuthorizationManagerBeforeMethodInterceptor> {\n\n\t\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t\t.getContextHolderStrategy();\n\n\t\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\t\tprivate final SecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\n\t\t@Override\n\t\tpublic AuthorizationManagerBeforeMethodInterceptor getObject() {\n\t\t\tAuthorizationManager<MethodInvocation> manager = this.manager;\n\t\t\tif (!this.observationRegistry.isNoop()) {\n\t\t\t\tmanager = new ObservationAuthorizationManager<>(this.observationRegistry, this.manager);\n\t\t\t}\n\t\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor\n\t\t\t\t.secured(manager);\n\t\t\tinterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\t\treturn interceptor;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn AuthorizationManagerBeforeMethodInterceptor.class;\n\t\t}\n\n\t\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t}\n\n\t\tpublic void setObservationRegistry(ObservationRegistry observationRegistry) {\n\t\t\tthis.observationRegistry = observationRegistry;\n\t\t}\n\n\t}\n\n\tpublic static final class PreAuthorizeAuthorizationMethodInterceptor\n\t\t\timplements FactoryBean<AuthorizationManagerBeforeMethodInterceptor> {\n\n\t\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t\t.getContextHolderStrategy();\n\n\t\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\t\tprivate final PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\n\t\t@Override\n\t\tpublic AuthorizationManagerBeforeMethodInterceptor getObject() {\n\t\t\tAuthorizationManager<MethodInvocation> manager = this.manager;\n\t\t\tif (!this.observationRegistry.isNoop()) {\n\t\t\t\tmanager = new ObservationAuthorizationManager<>(this.observationRegistry, this.manager);\n\t\t\t}\n\t\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor\n\t\t\t\t.preAuthorize(manager);\n\t\t\tinterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\t\treturn interceptor;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn AuthorizationManagerBeforeMethodInterceptor.class;\n\t\t}\n\n\t\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t}\n\n\t\tpublic void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\t\tthis.manager.setExpressionHandler(expressionHandler);\n\t\t}\n\n\t\tpublic void setObservationRegistry(ObservationRegistry registry) {\n\t\t\tthis.observationRegistry = registry;\n\t\t}\n\n\t}\n\n\tpublic static final class PostAuthorizeAuthorizationMethodInterceptor\n\t\t\timplements FactoryBean<AuthorizationManagerAfterMethodInterceptor> {\n\n\t\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t\t.getContextHolderStrategy();\n\n\t\tprivate ObservationRegistry observationRegistry = ObservationRegistry.NOOP;\n\n\t\tprivate final PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\n\t\t@Override\n\t\tpublic AuthorizationManagerAfterMethodInterceptor getObject() {\n\t\t\tAuthorizationManager<MethodInvocationResult> manager = this.manager;\n\t\t\tif (!this.observationRegistry.isNoop()) {\n\t\t\t\tmanager = new ObservationAuthorizationManager<>(this.observationRegistry, this.manager);\n\t\t\t}\n\t\t\tAuthorizationManagerAfterMethodInterceptor interceptor = AuthorizationManagerAfterMethodInterceptor\n\t\t\t\t.postAuthorize(manager);\n\t\t\tinterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\t\treturn interceptor;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn AuthorizationManagerAfterMethodInterceptor.class;\n\t\t}\n\n\t\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\t}\n\n\t\tpublic void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\t\tthis.manager.setExpressionHandler(expressionHandler);\n\t\t}\n\n\t\tpublic void setObservationRegistry(ObservationRegistry registry) {\n\t\t\tthis.observationRegistry = registry;\n\t\t}\n\n\t}\n\n\tstatic class SecurityContextHolderStrategyFactory implements FactoryBean<SecurityContextHolderStrategy> {\n\n\t\t@Override\n\t\tpublic SecurityContextHolderStrategy getObject() throws Exception {\n\t\t\treturn SecurityContextHolder.getContextHolderStrategy();\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn SecurityContextHolderStrategy.class;\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryFactory implements FactoryBean<ObservationRegistry> {\n\n\t\t@Override\n\t\tpublic ObservationRegistry getObject() throws Exception {\n\t\t\treturn ObservationRegistry.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn ObservationRegistry.class;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/MethodSecurityMetadataSourceBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.support.AbstractBeanDefinition;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;\nimport org.springframework.security.config.Elements;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * @author Luke Taylor\n * @since 3.1\n * @deprecated Use {@code <intercept-methods>}, {@code <method-security>}, or\n * {@code @EnableMethodSecurity}\n */\n@Deprecated\npublic class MethodSecurityMetadataSourceBeanDefinitionParser extends AbstractBeanDefinitionParser {\n\n\tstatic final String ATT_METHOD = \"method\";\n\n\tstatic final String ATT_ACCESS = \"access\";\n\n\t@Override\n\tpublic AbstractBeanDefinition parseInternal(Element elt, ParserContext pc) {\n\t\t// Parse the included methods\n\t\tList<Element> methods = DomUtils.getChildElementsByTagName(elt, Elements.PROTECT);\n\t\tMap<String, List<ConfigAttribute>> mappings = new LinkedHashMap<>();\n\t\tfor (Element protectmethodElt : methods) {\n\t\t\tString[] tokens = StringUtils.commaDelimitedListToStringArray(protectmethodElt.getAttribute(ATT_ACCESS));\n\t\t\tString methodName = protectmethodElt.getAttribute(ATT_METHOD);\n\t\t\tmappings.put(methodName, SecurityConfig.createList(tokens));\n\t\t}\n\t\tRootBeanDefinition metadataSource = new RootBeanDefinition(MapBasedMethodSecurityMetadataSource.class);\n\t\tmetadataSource.getConstructorArgumentValues().addGenericArgumentValue(mappings);\n\t\treturn metadataSource;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/PointcutDelegatingAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\nclass PointcutDelegatingAuthorizationManager implements AuthorizationManager<MethodInvocation> {\n\n\tprivate final Map<Pointcut, AuthorizationManager<MethodInvocation>> managers;\n\n\tPointcutDelegatingAuthorizationManager(Map<Pointcut, AuthorizationManager<MethodInvocation>> managers) {\n\t\tthis.managers = managers;\n\t}\n\n\t@Override\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMethodInvocation object) {\n\t\tfor (Map.Entry<Pointcut, AuthorizationManager<MethodInvocation>> entry : this.managers.entrySet()) {\n\t\t\tClass<?> targetClass = (object.getThis() != null) ? AopUtils.getTargetClass(object.getThis()) : null;\n\t\t\tif (entry.getKey().getClassFilter().matches(targetClass)\n\t\t\t\t\t&& entry.getKey().getMethodMatcher().matches(object.getMethod(), targetClass)) {\n\t\t\t\treturn entry.getValue().authorize(authentication, object);\n\t\t\t}\n\t\t}\n\t\treturn new AuthorizationDecision(false);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/PrefixBasedMethodMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.lang.reflect.Method;\n\nimport org.springframework.aop.ClassFilter;\nimport org.springframework.aop.MethodMatcher;\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.support.RootClassFilter;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\nclass PrefixBasedMethodMatcher implements MethodMatcher, Pointcut {\n\n\tprivate static final ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();\n\n\tprivate final ClassFilter classFilter;\n\n\tprivate final String methodPrefix;\n\n\tPrefixBasedMethodMatcher(Class<?> javaType, String methodPrefix) {\n\t\tthis.classFilter = new RootClassFilter(javaType);\n\t\tthis.methodPrefix = methodPrefix;\n\t}\n\n\tstatic PrefixBasedMethodMatcher fromClass(String className, String method) {\n\t\tint lastDotIndex = method.lastIndexOf(\".\");\n\t\tAssert.isTrue(lastDotIndex != -1 || StringUtils.hasText(className),\n\t\t\t\t() -> \"'\" + method + \"' is not a valid method name: format is FQN.methodName\");\n\t\tif (lastDotIndex == -1) {\n\t\t\tClass<?> javaType = ClassUtils.resolveClassName(className, beanClassLoader);\n\t\t\treturn new PrefixBasedMethodMatcher(javaType, method);\n\t\t}\n\t\tString methodName = method.substring(lastDotIndex + 1);\n\t\tAssert.hasText(methodName, () -> \"Method not found for '\" + method + \"'\");\n\t\tString typeName = method.substring(0, lastDotIndex);\n\t\tClass<?> javaType = ClassUtils.resolveClassName(typeName, beanClassLoader);\n\t\treturn new PrefixBasedMethodMatcher(javaType, method);\n\t}\n\n\t@Override\n\tpublic ClassFilter getClassFilter() {\n\t\treturn this.classFilter;\n\t}\n\n\t@Override\n\tpublic MethodMatcher getMethodMatcher() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean matches(Method method, Class<?> targetClass) {\n\t\treturn matches(this.methodPrefix, method.getName());\n\t}\n\n\t@Override\n\tpublic boolean isRuntime() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean matches(Method method, Class<?> targetClass, Object... args) {\n\t\treturn matches(this.methodPrefix, method.getName());\n\t}\n\n\tprivate boolean matches(String mappedName, String methodName) {\n\t\tboolean equals = methodName.equals(mappedName);\n\t\treturn equals || prefixMatches(mappedName, methodName) || suffixMatches(mappedName, methodName);\n\t}\n\n\tprivate boolean prefixMatches(String mappedName, String methodName) {\n\t\treturn mappedName.endsWith(\"*\") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1));\n\t}\n\n\tprivate boolean suffixMatches(String mappedName, String methodName) {\n\t\treturn mappedName.startsWith(\"*\") && methodName.endsWith(mappedName.substring(1));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/ProtectPointcutPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.aspectj.weaver.tools.PointcutExpression;\nimport org.aspectj.weaver.tools.PointcutParser;\nimport org.aspectj.weaver.tools.PointcutPrimitive;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;\nimport org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Parses AspectJ pointcut expressions, registering methods that match the pointcut with a\n * traditional {@link MapBasedMethodSecurityMetadataSource}.\n *\n * <p>\n * This class provides a convenient way of declaring a list of pointcuts, and then having\n * every method of every bean defined in the Spring application context compared with\n * those pointcuts. Where a match is found, the matching method will be registered with\n * the {@link MapBasedMethodSecurityMetadataSource}.\n * <p>\n * It is very important to understand that only the <b>first</b> pointcut that matches a\n * given method will be taken as authoritative for that method. This is why pointcuts\n * should be provided as a <tt>LinkedHashMap</tt>, because their order is very important.\n * <p>\n * Note also that only beans defined in the Spring application context will be examined by\n * this class.\n * <p>\n * Because this class registers method security metadata with\n * {@link MapBasedMethodSecurityMetadataSource}, normal Spring Security capabilities such\n * as {@link MethodSecurityMetadataSourceAdvisor} can be used. It does not matter the fact\n * the method metadata was originally obtained from an AspectJ pointcut expression\n * evaluation.\n *\n * @author Ben Alex\n * @since 2.0\n * @deprecated Use {@code use-authorization-manager} flag instead\n */\n@Deprecated\nfinal class ProtectPointcutPostProcessor implements BeanPostProcessor {\n\n\tprivate static final Log logger = LogFactory.getLog(ProtectPointcutPostProcessor.class);\n\n\tprivate final Map<String, List<ConfigAttribute>> pointcutMap = new LinkedHashMap<>();\n\n\tprivate final MapBasedMethodSecurityMetadataSource mapBasedMethodSecurityMetadataSource;\n\n\tprivate final Set<PointcutExpression> pointCutExpressions = new LinkedHashSet<>();\n\n\tprivate final PointcutParser parser;\n\n\tprivate final Set<String> processedBeans = new HashSet<>();\n\n\tProtectPointcutPostProcessor(MapBasedMethodSecurityMetadataSource mapBasedMethodSecurityMetadataSource) {\n\t\tAssert.notNull(mapBasedMethodSecurityMetadataSource,\n\t\t\t\t\"MapBasedMethodSecurityMetadataSource to populate is required\");\n\t\tthis.mapBasedMethodSecurityMetadataSource = mapBasedMethodSecurityMetadataSource;\n\t\t// Set up AspectJ pointcut expression parser\n\t\tSet<PointcutPrimitive> supportedPrimitives = new HashSet<>(3);\n\t\tsupportedPrimitives.add(PointcutPrimitive.EXECUTION);\n\t\tsupportedPrimitives.add(PointcutPrimitive.ARGS);\n\t\tsupportedPrimitives.add(PointcutPrimitive.REFERENCE);\n\t\t// supportedPrimitives.add(PointcutPrimitive.THIS);\n\t\t// supportedPrimitives.add(PointcutPrimitive.TARGET);\n\t\t// supportedPrimitives.add(PointcutPrimitive.WITHIN);\n\t\t// supportedPrimitives.add(PointcutPrimitive.AT_ANNOTATION);\n\t\t// supportedPrimitives.add(PointcutPrimitive.AT_WITHIN);\n\t\t// supportedPrimitives.add(PointcutPrimitive.AT_ARGS);\n\t\t// supportedPrimitives.add(PointcutPrimitive.AT_TARGET);\n\t\tthis.parser = PointcutParser\n\t\t\t.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives);\n\t}\n\n\t@Override\n\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\treturn bean;\n\t}\n\n\t@Override\n\tpublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n\t\tif (this.processedBeans.contains(beanName)) {\n\t\t\t// We already have the metadata for this bean\n\t\t\treturn bean;\n\t\t}\n\t\tsynchronized (this.processedBeans) {\n\t\t\t// check again synchronized this time\n\t\t\tif (this.processedBeans.contains(beanName)) {\n\t\t\t\treturn bean;\n\t\t\t}\n\t\t\t// Obtain methods for the present bean\n\t\t\tMethod[] methods = getBeanMethods(bean);\n\t\t\t// Check to see if any of those methods are compatible with our pointcut\n\t\t\t// expressions\n\t\t\tfor (Method method : methods) {\n\t\t\t\tfor (PointcutExpression expression : this.pointCutExpressions) {\n\t\t\t\t\t// Try for the bean class directly\n\t\t\t\t\tif (attemptMatch(bean.getClass(), method, expression, beanName)) {\n\t\t\t\t\t\t// We've found the first expression that matches this method, so\n\t\t\t\t\t\t// move onto the next method now\n\t\t\t\t\t\tbreak; // the \"while\" loop, not the \"for\" loop\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.processedBeans.add(beanName);\n\t\t}\n\t\treturn bean;\n\t}\n\n\tprivate Method[] getBeanMethods(Object bean) {\n\t\ttry {\n\t\t\treturn bean.getClass().getMethods();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex.getMessage());\n\t\t}\n\t}\n\n\tprivate boolean attemptMatch(Class<?> targetClass, Method method, PointcutExpression expression, String beanName) {\n\t\t// Determine if the presented AspectJ pointcut expression matches this method\n\t\tboolean matches = expression.matchesMethodExecution(method).alwaysMatches();\n\t\t// Handle accordingly\n\t\tif (matches) {\n\t\t\tList<ConfigAttribute> attr = this.pointcutMap.get(expression.getPointcutExpression());\n\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\tlogger.debug(\"AspectJ pointcut expression '\" + expression.getPointcutExpression()\n\t\t\t\t\t\t+ \"' matches target class '\" + targetClass.getName() + \"' (bean ID '\" + beanName\n\t\t\t\t\t\t+ \"') for method '\" + method + \"'; registering security configuration attribute '\" + attr\n\t\t\t\t\t\t+ \"'\");\n\t\t\t}\n\t\t\tthis.mapBasedMethodSecurityMetadataSource.addSecureMethod(targetClass, method, attr);\n\t\t}\n\t\treturn matches;\n\t}\n\n\tpublic void setPointcutMap(Map<String, List<ConfigAttribute>> map) {\n\t\tAssert.notEmpty(map, \"configAttributes cannot be empty\");\n\t\tfor (String expression : map.keySet()) {\n\t\t\tList<ConfigAttribute> value = map.get(expression);\n\t\t\taddPointcut(expression, value);\n\t\t}\n\t}\n\n\tprivate void addPointcut(String pointcutExpression, List<ConfigAttribute> definition) {\n\t\tAssert.hasText(pointcutExpression, \"An AspectJ pointcut expression is required\");\n\t\tAssert.notNull(definition, \"A List of ConfigAttributes is required\");\n\t\tpointcutExpression = replaceBooleanOperators(pointcutExpression);\n\t\tthis.pointcutMap.put(pointcutExpression, definition);\n\t\t// Parse the presented AspectJ pointcut expression and add it to the cache\n\t\tthis.pointCutExpressions.add(this.parser.parsePointcutExpression(pointcutExpression));\n\t\tif (logger.isDebugEnabled()) {\n\t\t\tlogger.debug(\"AspectJ pointcut expression '\" + pointcutExpression\n\t\t\t\t\t+ \"' registered for security configuration attribute '\" + definition + \"'\");\n\t\t}\n\t}\n\n\t/**\n\t * @see org.springframework.aop.aspectj.AspectJExpressionPointcut#replaceBooleanOperators\n\t */\n\tprivate String replaceBooleanOperators(String pcExpr) {\n\t\tpcExpr = StringUtils.replace(pcExpr, \" and \", \" && \");\n\t\tpcExpr = StringUtils.replace(pcExpr, \" or \", \" || \");\n\t\tpcExpr = StringUtils.replace(pcExpr, \" not \", \" ! \");\n\t\treturn pcExpr;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/method/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for parsing of the &lt;global-method-security&gt; and &lt;intercept-methods&gt;\n * elements.\n */\npackage org.springframework.security.config.method;\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.oauth2.client;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.parsing.CompositeComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrations;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * @author Ruby Hartono\n * @author Evgeniy Cheban\n * @since 5.3\n */\npublic final class ClientRegistrationsBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ELT_CLIENT_REGISTRATION = \"client-registration\";\n\n\tprivate static final String ELT_PROVIDER = \"provider\";\n\n\tprivate static final String ATT_REGISTRATION_ID = \"registration-id\";\n\n\tprivate static final String ATT_CLIENT_ID = \"client-id\";\n\n\tprivate static final String ATT_CLIENT_SECRET = \"client-secret\";\n\n\tprivate static final String ATT_CLIENT_AUTHENTICATION_METHOD = \"client-authentication-method\";\n\n\tprivate static final String ATT_AUTHORIZATION_GRANT_TYPE = \"authorization-grant-type\";\n\n\tprivate static final String ATT_REDIRECT_URI = \"redirect-uri\";\n\n\tprivate static final String ATT_SCOPE = \"scope\";\n\n\tprivate static final String ATT_CLIENT_NAME = \"client-name\";\n\n\tprivate static final String ATT_PROVIDER_ID = \"provider-id\";\n\n\tprivate static final String ATT_AUTHORIZATION_URI = \"authorization-uri\";\n\n\tprivate static final String ATT_TOKEN_URI = \"token-uri\";\n\n\tprivate static final String ATT_USER_INFO_URI = \"user-info-uri\";\n\n\tprivate static final String ATT_USER_INFO_AUTHENTICATION_METHOD = \"user-info-authentication-method\";\n\n\tprivate static final String ATT_USER_INFO_USER_NAME_ATTRIBUTE = \"user-info-user-name-attribute\";\n\n\tprivate static final String ATT_JWK_SET_URI = \"jwk-set-uri\";\n\n\tprivate static final String ATT_ISSUER_URI = \"issuer-uri\";\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),\n\t\t\t\tparserContext.extractSource(element));\n\t\tparserContext.pushContainingComponent(compositeDef);\n\t\tMap<String, Map<String, String>> providers = getProviders(element, parserContext);\n\t\tList<ClientRegistration> clientRegistrations = getClientRegistrations(element, parserContext, providers);\n\t\tBeanDefinition clientRegistrationRepositoryBean = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(InMemoryClientRegistrationRepository.class)\n\t\t\t.addConstructorArgValue(clientRegistrations)\n\t\t\t.getBeanDefinition();\n\t\tString clientRegistrationRepositoryId = parserContext.getReaderContext()\n\t\t\t.generateBeanName(clientRegistrationRepositoryBean);\n\t\tparserContext.registerBeanComponent(\n\t\t\t\tnew BeanComponentDefinition(clientRegistrationRepositoryBean, clientRegistrationRepositoryId));\n\t\tparserContext.popAndRegisterContainingComponent();\n\t\treturn null;\n\t}\n\n\tprivate List<ClientRegistration> getClientRegistrations(Element element, ParserContext parserContext,\n\t\t\tMap<String, Map<String, String>> providers) {\n\t\tList<Element> clientRegistrationElts = DomUtils.getChildElementsByTagName(element, ELT_CLIENT_REGISTRATION);\n\t\tList<ClientRegistration> clientRegistrations = new ArrayList<>();\n\t\tfor (Element clientRegistrationElt : clientRegistrationElts) {\n\t\t\tString registrationId = clientRegistrationElt.getAttribute(ATT_REGISTRATION_ID);\n\t\t\tString providerId = clientRegistrationElt.getAttribute(ATT_PROVIDER_ID);\n\t\t\tClientRegistration.Builder builder = getBuilderFromIssuerIfPossible(parserContext, registrationId,\n\t\t\t\t\tproviderId, providers);\n\t\t\tif (builder == null) {\n\t\t\t\tbuilder = getBuilder(parserContext, registrationId, providerId, providers);\n\t\t\t\tif (builder == null) {\n\t\t\t\t\tObject source = parserContext.extractSource(element);\n\t\t\t\t\tparserContext.getReaderContext().error(getErrorMessage(providerId, registrationId), source);\n\t\t\t\t\t// error on the config skip to next element\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tgetOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_ID))\n\t\t\t\t.ifPresent(builder::clientId);\n\t\t\tgetOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_SECRET))\n\t\t\t\t.ifPresent(builder::clientSecret);\n\t\t\tgetOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_AUTHENTICATION_METHOD))\n\t\t\t\t.map(ClientAuthenticationMethod::valueOf)\n\t\t\t\t.ifPresent(builder::clientAuthenticationMethod);\n\t\t\tgetOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_AUTHORIZATION_GRANT_TYPE))\n\t\t\t\t.map(AuthorizationGrantType::new)\n\t\t\t\t.ifPresent(builder::authorizationGrantType);\n\t\t\tgetOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_REDIRECT_URI))\n\t\t\t\t.ifPresent(builder::redirectUri);\n\t\t\tgetOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_SCOPE))\n\t\t\t\t.map(StringUtils::commaDelimitedListToSet)\n\t\t\t\t.ifPresent(builder::scope);\n\t\t\tgetOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_NAME))\n\t\t\t\t.ifPresent(builder::clientName);\n\t\t\tclientRegistrations.add(builder.build());\n\t\t}\n\t\treturn clientRegistrations;\n\t}\n\n\tprivate Map<String, Map<String, String>> getProviders(Element element, ParserContext parserContext) {\n\t\tList<Element> providerElts = DomUtils.getChildElementsByTagName(element, ELT_PROVIDER);\n\t\tMap<String, Map<String, String>> providers = new HashMap<>();\n\t\tfor (Element providerElt : providerElts) {\n\t\t\tMap<String, String> provider = new HashMap<>();\n\t\t\tString providerId = providerElt.getAttribute(ATT_PROVIDER_ID);\n\t\t\tprovider.put(ATT_PROVIDER_ID, providerId);\n\t\t\tgetOptionalIfNotEmpty(parserContext, providerElt.getAttribute(ATT_AUTHORIZATION_URI))\n\t\t\t\t.ifPresent((value) -> provider.put(ATT_AUTHORIZATION_URI, value));\n\t\t\tgetOptionalIfNotEmpty(parserContext, providerElt.getAttribute(ATT_TOKEN_URI))\n\t\t\t\t.ifPresent((value) -> provider.put(ATT_TOKEN_URI, value));\n\t\t\tgetOptionalIfNotEmpty(parserContext, providerElt.getAttribute(ATT_USER_INFO_URI))\n\t\t\t\t.ifPresent((value) -> provider.put(ATT_USER_INFO_URI, value));\n\t\t\tgetOptionalIfNotEmpty(parserContext, providerElt.getAttribute(ATT_USER_INFO_AUTHENTICATION_METHOD))\n\t\t\t\t.ifPresent((value) -> provider.put(ATT_USER_INFO_AUTHENTICATION_METHOD, value));\n\t\t\tgetOptionalIfNotEmpty(parserContext, providerElt.getAttribute(ATT_USER_INFO_USER_NAME_ATTRIBUTE))\n\t\t\t\t.ifPresent((value) -> provider.put(ATT_USER_INFO_USER_NAME_ATTRIBUTE, value));\n\t\t\tgetOptionalIfNotEmpty(parserContext, providerElt.getAttribute(ATT_JWK_SET_URI))\n\t\t\t\t.ifPresent((value) -> provider.put(ATT_JWK_SET_URI, value));\n\t\t\tgetOptionalIfNotEmpty(parserContext, providerElt.getAttribute(ATT_ISSUER_URI))\n\t\t\t\t.ifPresent((value) -> provider.put(ATT_ISSUER_URI, value));\n\t\t\tproviders.put(providerId, provider);\n\t\t}\n\t\treturn providers;\n\t}\n\n\tprivate static ClientRegistration.Builder getBuilderFromIssuerIfPossible(ParserContext parserContext,\n\t\t\tString registrationId, String configuredProviderId, Map<String, Map<String, String>> providers) {\n\t\tString providerId = (configuredProviderId != null) ? configuredProviderId : registrationId;\n\t\tif (providers.containsKey(providerId)) {\n\t\t\tMap<String, String> provider = providers.get(providerId);\n\t\t\tString issuer = provider.get(ATT_ISSUER_URI);\n\t\t\tif (StringUtils.hasLength(issuer)) {\n\t\t\t\tClientRegistration.Builder builder = ClientRegistrations.fromIssuerLocation(issuer)\n\t\t\t\t\t.registrationId(registrationId);\n\t\t\t\treturn getBuilder(parserContext, builder, provider);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static ClientRegistration.Builder getBuilder(ParserContext parserContext, String registrationId,\n\t\t\tString configuredProviderId, Map<String, Map<String, String>> providers) {\n\t\tString providerId = (configuredProviderId != null) ? configuredProviderId : registrationId;\n\t\tCommonOAuth2Provider provider = getCommonProvider(providerId);\n\t\tif (provider == null && !providers.containsKey(providerId)) {\n\t\t\treturn null;\n\t\t}\n\t\tClientRegistration.Builder builder = (provider != null) ? provider.getBuilder(registrationId)\n\t\t\t\t: ClientRegistration.withRegistrationId(registrationId);\n\t\tif (providers.containsKey(providerId)) {\n\t\t\treturn getBuilder(parserContext, builder, providers.get(providerId));\n\t\t}\n\t\treturn builder;\n\t}\n\n\tprivate static ClientRegistration.Builder getBuilder(ParserContext parserContext,\n\t\t\tClientRegistration.Builder builder, Map<String, String> provider) {\n\t\tgetOptionalIfNotEmpty(parserContext, provider.get(ATT_AUTHORIZATION_URI)).ifPresent(builder::authorizationUri);\n\t\tgetOptionalIfNotEmpty(parserContext, provider.get(ATT_TOKEN_URI)).ifPresent(builder::tokenUri);\n\t\tgetOptionalIfNotEmpty(parserContext, provider.get(ATT_USER_INFO_URI)).ifPresent(builder::userInfoUri);\n\t\tgetOptionalIfNotEmpty(parserContext, provider.get(ATT_USER_INFO_AUTHENTICATION_METHOD))\n\t\t\t.map(AuthenticationMethod::new)\n\t\t\t.ifPresent(builder::userInfoAuthenticationMethod);\n\t\tgetOptionalIfNotEmpty(parserContext, provider.get(ATT_JWK_SET_URI)).ifPresent(builder::jwkSetUri);\n\t\tgetOptionalIfNotEmpty(parserContext, provider.get(ATT_USER_INFO_USER_NAME_ATTRIBUTE))\n\t\t\t.ifPresent(builder::userNameAttributeName);\n\t\treturn builder;\n\t}\n\n\tprivate static Optional<String> getOptionalIfNotEmpty(ParserContext parserContext, String str) {\n\t\treturn Optional.ofNullable(str)\n\t\t\t.filter((s) -> !s.isEmpty())\n\t\t\t.map(parserContext.getReaderContext().getEnvironment()::resolvePlaceholders);\n\t}\n\n\tprivate static CommonOAuth2Provider getCommonProvider(String providerId) {\n\t\ttry {\n\t\t\tString value = providerId.trim();\n\t\t\tif (value.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn CommonOAuth2Provider.valueOf(value);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\treturn findEnum(value);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static CommonOAuth2Provider findEnum(String value) {\n\t\tString name = getCanonicalName(value);\n\t\tfor (CommonOAuth2Provider candidate : EnumSet.allOf(CommonOAuth2Provider.class)) {\n\t\t\tString candidateName = getCanonicalName(candidate.name());\n\t\t\tif (name.equals(candidateName)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"No enum constant \" + CommonOAuth2Provider.class.getCanonicalName() + \".\" + value);\n\t}\n\n\tprivate static String getCanonicalName(String name) {\n\t\tStringBuilder canonicalName = new StringBuilder(name.length());\n\t\tname.chars()\n\t\t\t.filter(Character::isLetterOrDigit)\n\t\t\t.map(Character::toLowerCase)\n\t\t\t.forEach((c) -> canonicalName.append((char) c));\n\t\treturn canonicalName.toString();\n\t}\n\n\tprivate static String getErrorMessage(String configuredProviderId, String registrationId) {\n\t\treturn (configuredProviderId != null) ? \"Unknown provider ID '\" + configuredProviderId + \"'\"\n\t\t\t\t: \"Provider ID must be specified for client registration '\" + registrationId + \"'\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.oauth2.client;\n\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\n\n/**\n * Common OAuth2 Providers that can be used to create\n * {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder\n * builders} pre-configured with sensible defaults for the\n * {@link HttpSecurity#oauth2Login(Customizer)} flow.\n *\n * @author Phillip Webb\n * @since 5.0\n */\npublic enum CommonOAuth2Provider {\n\n\tGOOGLE {\n\n\t\t@Override\n\t\tpublic Builder getBuilder(String registrationId) {\n\t\t\tClientRegistration.Builder builder = getBuilder(registrationId,\n\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, DEFAULT_REDIRECT_URL);\n\t\t\tbuilder.scope(\"openid\", \"profile\", \"email\");\n\t\t\tbuilder.authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\");\n\t\t\tbuilder.tokenUri(\"https://www.googleapis.com/oauth2/v4/token\");\n\t\t\tbuilder.jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\");\n\t\t\tbuilder.issuerUri(\"https://accounts.google.com\");\n\t\t\tbuilder.userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\");\n\t\t\tbuilder.userNameAttributeName(IdTokenClaimNames.SUB);\n\t\t\tbuilder.clientName(\"Google\");\n\t\t\treturn builder;\n\t\t}\n\n\t},\n\n\tGITHUB {\n\n\t\t@Override\n\t\tpublic Builder getBuilder(String registrationId) {\n\t\t\tClientRegistration.Builder builder = getBuilder(registrationId,\n\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, DEFAULT_REDIRECT_URL);\n\t\t\tbuilder.scope(\"read:user\");\n\t\t\tbuilder.authorizationUri(\"https://github.com/login/oauth/authorize\");\n\t\t\tbuilder.tokenUri(\"https://github.com/login/oauth/access_token\");\n\t\t\tbuilder.userInfoUri(\"https://api.github.com/user\");\n\t\t\tbuilder.userNameAttributeName(\"id\");\n\t\t\tbuilder.clientName(\"GitHub\");\n\t\t\treturn builder;\n\t\t}\n\n\t},\n\n\tFACEBOOK {\n\n\t\t@Override\n\t\tpublic Builder getBuilder(String registrationId) {\n\t\t\tClientRegistration.Builder builder = getBuilder(registrationId,\n\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_POST, DEFAULT_REDIRECT_URL);\n\t\t\tbuilder.scope(\"public_profile\", \"email\");\n\t\t\tbuilder.authorizationUri(\"https://www.facebook.com/v2.8/dialog/oauth\");\n\t\t\tbuilder.tokenUri(\"https://graph.facebook.com/v2.8/oauth/access_token\");\n\t\t\tbuilder.userInfoUri(\"https://graph.facebook.com/me?fields=id,name,email\");\n\t\t\tbuilder.userNameAttributeName(\"id\");\n\t\t\tbuilder.clientName(\"Facebook\");\n\t\t\treturn builder;\n\t\t}\n\n\t},\n\n\tX {\n\n\t\t@Override\n\t\tpublic Builder getBuilder(String registrationId) {\n\t\t\tClientRegistration.Builder builder = getBuilder(registrationId,\n\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_POST, DEFAULT_REDIRECT_URL);\n\t\t\tbuilder.scope(\"users.read\", \"tweet.read\");\n\t\t\tbuilder.authorizationUri(\"https://x.com/i/oauth2/authorize\");\n\t\t\tbuilder.tokenUri(\"https://api.x.com/2/oauth2/token\");\n\t\t\tbuilder.userInfoUri(\"https://api.x.com/2/users/me\");\n\t\t\tbuilder.userNameAttributeName(\"username\");\n\t\t\tbuilder.clientName(\"X\");\n\t\t\treturn builder;\n\t\t}\n\n\t},\n\n\tOKTA {\n\n\t\t@Override\n\t\tpublic Builder getBuilder(String registrationId) {\n\t\t\tClientRegistration.Builder builder = getBuilder(registrationId,\n\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, DEFAULT_REDIRECT_URL);\n\t\t\tbuilder.scope(\"openid\", \"profile\", \"email\");\n\t\t\tbuilder.userNameAttributeName(IdTokenClaimNames.SUB);\n\t\t\tbuilder.clientName(\"Okta\");\n\t\t\treturn builder;\n\t\t}\n\n\t};\n\n\tprivate static final String DEFAULT_REDIRECT_URL = \"{baseUrl}/{action}/oauth2/code/{registrationId}\";\n\n\tprotected final ClientRegistration.Builder getBuilder(String registrationId, ClientAuthenticationMethod method,\n\t\t\tString redirectUri) {\n\t\tClientRegistration.Builder builder = ClientRegistration.withRegistrationId(registrationId);\n\t\tbuilder.clientAuthenticationMethod(method);\n\t\tbuilder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tbuilder.redirectUri(redirectUri);\n\t\treturn builder;\n\t}\n\n\t/**\n\t * Create a new\n\t * {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder\n\t * ClientRegistration.Builder} pre-configured with provider defaults.\n\t * @param registrationId the registration-id used with the new builder\n\t * @return a builder instance\n\t */\n\tpublic abstract ClientRegistration.Builder getBuilder(String registrationId);\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/observation/SecurityObservationSettings.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.observation;\n\nimport io.micrometer.observation.ObservationPredicate;\n\n/**\n * An {@link ObservationPredicate} that can be used to change which Spring Security\n * observations are made with Micrometer.\n *\n * <p>\n * By default, web requests are not observed and authentications and authorizations are\n * observed.\n *\n * @author Josh Cummings\n * @since 6.4\n */\npublic final class SecurityObservationSettings {\n\n\tprivate final boolean observeRequests;\n\n\tprivate final boolean observeAuthentications;\n\n\tprivate final boolean observeAuthorizations;\n\n\tprivate SecurityObservationSettings(boolean observeRequests, boolean observeAuthentications,\n\t\t\tboolean observeAuthorizations) {\n\t\tthis.observeRequests = observeRequests;\n\t\tthis.observeAuthentications = observeAuthentications;\n\t\tthis.observeAuthorizations = observeAuthorizations;\n\t}\n\n\t/**\n\t * Make no Spring Security observations\n\t * @return a {@link SecurityObservationSettings} with all exclusions turned on\n\t */\n\tpublic static SecurityObservationSettings noObservations() {\n\t\treturn new SecurityObservationSettings(false, false, false);\n\t}\n\n\t/**\n\t * Begin the configuration of a {@link SecurityObservationSettings}\n\t * @return a {@link Builder} where filter chain observations are off and authn/authz\n\t * observations are on\n\t */\n\tpublic static Builder withDefaults() {\n\t\treturn new Builder(false, true, true);\n\t}\n\n\tpublic boolean shouldObserveRequests() {\n\t\treturn this.observeRequests;\n\t}\n\n\tpublic boolean shouldObserveAuthentications() {\n\t\treturn this.observeAuthentications;\n\t}\n\n\tpublic boolean shouldObserveAuthorizations() {\n\t\treturn this.observeAuthorizations;\n\t}\n\n\t/**\n\t * A builder for configuring a {@link SecurityObservationSettings}\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate boolean observeRequests;\n\n\t\tprivate boolean observeAuthentications;\n\n\t\tprivate boolean observeAuthorizations;\n\n\t\tBuilder(boolean observeRequests, boolean observeAuthentications, boolean observeAuthorizations) {\n\t\t\tthis.observeRequests = observeRequests;\n\t\t\tthis.observeAuthentications = observeAuthentications;\n\t\t\tthis.observeAuthorizations = observeAuthorizations;\n\t\t}\n\n\t\tpublic Builder shouldObserveRequests(boolean excludeFilters) {\n\t\t\tthis.observeRequests = excludeFilters;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder shouldObserveAuthentications(boolean excludeAuthentications) {\n\t\t\tthis.observeAuthentications = excludeAuthentications;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder shouldObserveAuthorizations(boolean excludeAuthorizations) {\n\t\t\tthis.observeAuthorizations = excludeAuthorizations;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic SecurityObservationSettings build() {\n\t\t\treturn new SecurityObservationSettings(this.observeRequests, this.observeAuthentications,\n\t\t\t\t\tthis.observeAuthorizations);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes for the Spring Security namespace. None of the code in these packages\n * should be used directly in applications.\n */\npackage org.springframework.security.config;\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/provisioning/UserDetailsManagerResourceFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.provisioning;\n\nimport java.util.Collection;\n\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.context.ResourceLoaderAware;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.config.core.userdetails.UserDetailsResourceFactoryBean;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.util.InMemoryResource;\n\n/**\n * Constructs an {@link InMemoryUserDetailsManager} from a resource using\n * {@link UserDetailsResourceFactoryBean}.\n *\n * @author Rob Winch\n * @since 5.0\n * @see UserDetailsResourceFactoryBean\n */\npublic class UserDetailsManagerResourceFactoryBean\n\t\timplements ResourceLoaderAware, FactoryBean<InMemoryUserDetailsManager> {\n\n\tprivate UserDetailsResourceFactoryBean userDetails = new UserDetailsResourceFactoryBean();\n\n\t@Override\n\tpublic InMemoryUserDetailsManager getObject() throws Exception {\n\t\tCollection<UserDetails> users = this.userDetails.getObject();\n\t\treturn new InMemoryUserDetailsManager(users);\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn InMemoryUserDetailsManager.class;\n\t}\n\n\t@Override\n\tpublic void setResourceLoader(ResourceLoader resourceLoader) {\n\t\tthis.userDetails.setResourceLoader(resourceLoader);\n\t}\n\n\t/**\n\t * Sets the location of a Resource that is a Properties file in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}.\n\t * @param resourceLocation the location of the properties file that contains the users\n\t * (i.e. \"classpath:users.properties\")\n\t */\n\tpublic void setResourceLocation(String resourceLocation) {\n\t\tthis.userDetails.setResourceLocation(resourceLocation);\n\t}\n\n\t/**\n\t * Sets a Resource that is a Properties file in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}.\n\t * @param resource the Resource to use\n\t */\n\tpublic void setResource(Resource resource) {\n\t\tthis.userDetails.setResource(resource);\n\t}\n\n\t/**\n\t * Create a UserDetailsManagerResourceFactoryBean with the location of a Resource that\n\t * is a Properties file in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}.\n\t * @param resourceLocation the location of the properties file that contains the users\n\t * (i.e. \"classpath:users.properties\")\n\t * @return the UserDetailsManagerResourceFactoryBean\n\t */\n\tpublic static UserDetailsManagerResourceFactoryBean fromResourceLocation(String resourceLocation) {\n\t\tUserDetailsManagerResourceFactoryBean result = new UserDetailsManagerResourceFactoryBean();\n\t\tresult.setResourceLocation(resourceLocation);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Create a UserDetailsManagerResourceFactoryBean with a Resource that is a Properties\n\t * file in the format defined in {@link UserDetailsResourceFactoryBean}.\n\t * @param resource the Resource that is a properties file that contains the users\n\t * @return the UserDetailsManagerResourceFactoryBean\n\t */\n\tpublic static UserDetailsManagerResourceFactoryBean fromResource(Resource resource) {\n\t\tUserDetailsManagerResourceFactoryBean result = new UserDetailsManagerResourceFactoryBean();\n\t\tresult.setResource(resource);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Create a UserDetailsManagerResourceFactoryBean with a String that is in the format\n\t * defined in {@link UserDetailsResourceFactoryBean}.\n\t * @param users the users in the format defined in\n\t * {@link UserDetailsResourceFactoryBean}\n\t * @return the UserDetailsManagerResourceFactoryBean\n\t */\n\tpublic static UserDetailsManagerResourceFactoryBean fromString(String users) {\n\t\tUserDetailsManagerResourceFactoryBean result = new UserDetailsManagerResourceFactoryBean();\n\t\tresult.setResource(new InMemoryResource(users));\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.saml2;\n\nimport java.io.InputStream;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.parsing.CompositeComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.converter.RsaKeyConverters;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata;\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * @author Marcus da Coregio\n * @since 5.7\n */\npublic final class RelyingPartyRegistrationsBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ELT_RELYING_PARTY_REGISTRATION = \"relying-party-registration\";\n\n\tprivate static final String ELT_SIGNING_CREDENTIAL = \"signing-credential\";\n\n\tprivate static final String ELT_DECRYPTION_CREDENTIAL = \"decryption-credential\";\n\n\tprivate static final String ELT_ASSERTING_PARTY = \"asserting-party\";\n\n\tprivate static final String ELT_VERIFICATION_CREDENTIAL = \"verification-credential\";\n\n\tprivate static final String ELT_ENCRYPTION_CREDENTIAL = \"encryption-credential\";\n\n\tprivate static final String ATT_ID = \"id\";\n\n\tprivate static final String ATT_REGISTRATION_ID = \"registration-id\";\n\n\tprivate static final String ATT_ASSERTING_PARTY_ID = \"asserting-party-id\";\n\n\tprivate static final String ATT_ENTITY_ID = \"entity-id\";\n\n\tprivate static final String ATT_METADATA_LOCATION = \"metadata-location\";\n\n\tprivate static final String ATT_ASSERTION_CONSUMER_SERVICE_LOCATION = \"assertion-consumer-service-location\";\n\n\tprivate static final String ATT_ASSERTION_CONSUMER_SERVICE_BINDING = \"assertion-consumer-service-binding\";\n\n\tprivate static final String ATT_PRIVATE_KEY_LOCATION = \"private-key-location\";\n\n\tprivate static final String ATT_CERTIFICATE_LOCATION = \"certificate-location\";\n\n\tprivate static final String ATT_WANT_AUTHN_REQUESTS_SIGNED = \"want-authn-requests-signed\";\n\n\tprivate static final String ATT_SINGLE_SIGN_ON_SERVICE_LOCATION = \"single-sign-on-service-location\";\n\n\tprivate static final String ATT_SINGLE_SIGN_ON_SERVICE_BINDING = \"single-sign-on-service-binding\";\n\n\tprivate static final String ATT_SIGNING_ALGORITHMS = \"signing-algorithms\";\n\n\tprivate static final String ATT_SINGLE_LOGOUT_SERVICE_LOCATION = \"single-logout-service-location\";\n\n\tprivate static final String ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION = \"single-logout-service-response-location\";\n\n\tprivate static final String ATT_SINGLE_LOGOUT_SERVICE_BINDING = \"single-logout-service-binding\";\n\n\tprivate static final ResourceLoader resourceLoader = new DefaultResourceLoader();\n\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),\n\t\t\t\tparserContext.extractSource(element));\n\t\tparserContext.pushContainingComponent(compositeDef);\n\t\tMap<String, Map<String, Object>> assertingParties = getAssertingParties(element);\n\t\tList<RelyingPartyRegistration> relyingPartyRegistrations = getRelyingPartyRegistrations(element,\n\t\t\t\tassertingParties, parserContext);\n\t\tBeanDefinition relyingPartyRegistrationRepositoryBean = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(InMemoryRelyingPartyRegistrationRepository.class)\n\t\t\t.addConstructorArgValue(relyingPartyRegistrations)\n\t\t\t.getBeanDefinition();\n\t\tString relyingPartyRegistrationRepositoryId = element.getAttribute(ATT_ID);\n\t\tif (!StringUtils.hasText(relyingPartyRegistrationRepositoryId)) {\n\t\t\trelyingPartyRegistrationRepositoryId = parserContext.getReaderContext()\n\t\t\t\t.generateBeanName(relyingPartyRegistrationRepositoryBean);\n\t\t}\n\t\tparserContext.registerBeanComponent(new BeanComponentDefinition(relyingPartyRegistrationRepositoryBean,\n\t\t\t\trelyingPartyRegistrationRepositoryId));\n\t\tparserContext.popAndRegisterContainingComponent();\n\t\treturn null;\n\t}\n\n\tprivate static Map<String, Map<String, Object>> getAssertingParties(Element element) {\n\t\tList<Element> assertingPartyElts = DomUtils.getChildElementsByTagName(element, ELT_ASSERTING_PARTY);\n\t\tMap<String, Map<String, Object>> providers = new HashMap<>();\n\t\tfor (Element assertingPartyElt : assertingPartyElts) {\n\t\t\tMap<String, Object> assertingParty = new HashMap<>();\n\t\t\tString assertingPartyId = assertingPartyElt.getAttribute(ATT_ASSERTING_PARTY_ID);\n\t\t\tString entityId = assertingPartyElt.getAttribute(ATT_ENTITY_ID);\n\t\t\tString wantAuthnRequestsSigned = assertingPartyElt.getAttribute(ATT_WANT_AUTHN_REQUESTS_SIGNED);\n\t\t\tString singleSignOnServiceLocation = assertingPartyElt.getAttribute(ATT_SINGLE_SIGN_ON_SERVICE_LOCATION);\n\t\t\tString singleSignOnServiceBinding = assertingPartyElt.getAttribute(ATT_SINGLE_SIGN_ON_SERVICE_BINDING);\n\t\t\tString signingAlgorithms = assertingPartyElt.getAttribute(ATT_SIGNING_ALGORITHMS);\n\t\t\tString singleLogoutServiceLocation = assertingPartyElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_LOCATION);\n\t\t\tString singleLogoutServiceResponseLocation = assertingPartyElt\n\t\t\t\t.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION);\n\t\t\tString singleLogoutServiceBinding = assertingPartyElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_BINDING);\n\t\t\tassertingParty.put(ATT_ASSERTING_PARTY_ID, assertingPartyId);\n\t\t\tassertingParty.put(ATT_ENTITY_ID, entityId);\n\t\t\tassertingParty.put(ATT_WANT_AUTHN_REQUESTS_SIGNED, wantAuthnRequestsSigned);\n\t\t\tassertingParty.put(ATT_SINGLE_SIGN_ON_SERVICE_LOCATION, singleSignOnServiceLocation);\n\t\t\tassertingParty.put(ATT_SINGLE_SIGN_ON_SERVICE_BINDING, singleSignOnServiceBinding);\n\t\t\tassertingParty.put(ATT_SIGNING_ALGORITHMS, signingAlgorithms);\n\t\t\tassertingParty.put(ATT_SINGLE_LOGOUT_SERVICE_LOCATION, singleLogoutServiceLocation);\n\t\t\tassertingParty.put(ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION, singleLogoutServiceResponseLocation);\n\t\t\tassertingParty.put(ATT_SINGLE_LOGOUT_SERVICE_BINDING, singleLogoutServiceBinding);\n\t\t\taddVerificationCredentials(assertingPartyElt, assertingParty);\n\t\t\taddEncryptionCredentials(assertingPartyElt, assertingParty);\n\t\t\tproviders.put(assertingPartyId, assertingParty);\n\t\t}\n\t\treturn providers;\n\t}\n\n\tprivate static void addVerificationCredentials(Map<String, Object> assertingParty,\n\t\t\tAssertingPartyMetadata.Builder<?> builder) {\n\t\tList<String> verificationCertificateLocations = (List<String>) assertingParty.get(ELT_VERIFICATION_CREDENTIAL);\n\t\tList<Saml2X509Credential> verificationCredentials = new ArrayList<>();\n\t\tfor (String certificateLocation : verificationCertificateLocations) {\n\t\t\tverificationCredentials.add(getSaml2VerificationCredential(certificateLocation));\n\t\t}\n\t\tbuilder.verificationX509Credentials((credentials) -> credentials.addAll(verificationCredentials));\n\t}\n\n\tprivate static void addEncryptionCredentials(Map<String, Object> assertingParty,\n\t\t\tAssertingPartyMetadata.Builder<?> builder) {\n\t\tList<String> encryptionCertificateLocations = (List<String>) assertingParty.get(ELT_ENCRYPTION_CREDENTIAL);\n\t\tList<Saml2X509Credential> encryptionCredentials = new ArrayList<>();\n\t\tfor (String certificateLocation : encryptionCertificateLocations) {\n\t\t\tencryptionCredentials.add(getSaml2EncryptionCredential(certificateLocation));\n\t\t}\n\t\tbuilder.encryptionX509Credentials((credentials) -> credentials.addAll(encryptionCredentials));\n\t}\n\n\tprivate static void addVerificationCredentials(Element assertingPartyElt, Map<String, Object> assertingParty) {\n\t\tList<String> verificationCertificateLocations = new ArrayList<>();\n\t\tList<Element> verificationCredentialElts = DomUtils.getChildElementsByTagName(assertingPartyElt,\n\t\t\t\tELT_VERIFICATION_CREDENTIAL);\n\t\tfor (Element verificationCredentialElt : verificationCredentialElts) {\n\t\t\tString certificateLocation = verificationCredentialElt.getAttribute(ATT_CERTIFICATE_LOCATION);\n\t\t\tverificationCertificateLocations.add(certificateLocation);\n\t\t}\n\t\tassertingParty.put(ELT_VERIFICATION_CREDENTIAL, verificationCertificateLocations);\n\t}\n\n\tprivate static void addEncryptionCredentials(Element assertingPartyElt, Map<String, Object> assertingParty) {\n\t\tList<String> encryptionCertificateLocations = new ArrayList<>();\n\t\tList<Element> encryptionCredentialElts = DomUtils.getChildElementsByTagName(assertingPartyElt,\n\t\t\t\tELT_VERIFICATION_CREDENTIAL);\n\t\tfor (Element encryptionCredentialElt : encryptionCredentialElts) {\n\t\t\tString certificateLocation = encryptionCredentialElt.getAttribute(ATT_CERTIFICATE_LOCATION);\n\t\t\tencryptionCertificateLocations.add(certificateLocation);\n\t\t}\n\t\tassertingParty.put(ELT_ENCRYPTION_CREDENTIAL, encryptionCertificateLocations);\n\t}\n\n\tprivate List<RelyingPartyRegistration> getRelyingPartyRegistrations(Element element,\n\t\t\tMap<String, Map<String, Object>> assertingParties, ParserContext parserContext) {\n\t\tList<Element> relyingPartyRegistrationElts = DomUtils.getChildElementsByTagName(element,\n\t\t\t\tELT_RELYING_PARTY_REGISTRATION);\n\t\tList<RelyingPartyRegistration> relyingPartyRegistrations = new ArrayList<>();\n\t\tfor (Element relyingPartyRegistrationElt : relyingPartyRegistrationElts) {\n\t\t\tRelyingPartyRegistration.Builder builder = getBuilderFromMetadataLocationIfPossible(\n\t\t\t\t\trelyingPartyRegistrationElt, assertingParties, parserContext);\n\t\t\taddSigningCredentials(relyingPartyRegistrationElt, builder);\n\t\t\taddDecryptionCredentials(relyingPartyRegistrationElt, builder);\n\t\t\trelyingPartyRegistrations.add(builder.build());\n\t\t}\n\t\treturn relyingPartyRegistrations;\n\t}\n\n\tprivate static RelyingPartyRegistration.Builder getBuilderFromMetadataLocationIfPossible(\n\t\t\tElement relyingPartyRegistrationElt, Map<String, Map<String, Object>> assertingParties,\n\t\t\tParserContext parserContext) {\n\t\tString registrationId = resolveAttribute(parserContext,\n\t\t\t\trelyingPartyRegistrationElt.getAttribute(ATT_REGISTRATION_ID));\n\t\tString metadataLocation = resolveAttribute(parserContext,\n\t\t\t\trelyingPartyRegistrationElt.getAttribute(ATT_METADATA_LOCATION));\n\t\tRelyingPartyRegistration.Builder builder;\n\t\tif (StringUtils.hasText(metadataLocation)) {\n\t\t\tbuilder = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation).registrationId(registrationId);\n\t\t}\n\t\telse {\n\t\t\tbuilder = RelyingPartyRegistration.withRegistrationId(registrationId)\n\t\t\t\t.assertingPartyMetadata((apBuilder) -> buildAssertingParty(relyingPartyRegistrationElt,\n\t\t\t\t\t\tassertingParties, apBuilder, parserContext));\n\t\t}\n\t\taddRemainingProperties(parserContext, relyingPartyRegistrationElt, builder);\n\t\treturn builder;\n\t}\n\n\tprivate static void addRemainingProperties(ParserContext pc, Element relyingPartyRegistrationElt,\n\t\t\tRelyingPartyRegistration.Builder builder) {\n\t\tString entityId = resolveAttribute(pc, relyingPartyRegistrationElt.getAttribute(ATT_ENTITY_ID));\n\t\tString singleLogoutServiceLocation = resolveAttribute(pc,\n\t\t\t\trelyingPartyRegistrationElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_LOCATION));\n\t\tString singleLogoutServiceResponseLocation = resolveAttribute(pc,\n\t\t\t\trelyingPartyRegistrationElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION));\n\t\tSaml2MessageBinding singleLogoutServiceBinding = getSingleLogoutServiceBinding(relyingPartyRegistrationElt);\n\t\tString assertionConsumerServiceLocation = resolveAttribute(pc,\n\t\t\t\trelyingPartyRegistrationElt.getAttribute(ATT_ASSERTION_CONSUMER_SERVICE_LOCATION));\n\t\tSaml2MessageBinding assertionConsumerServiceBinding = getAssertionConsumerServiceBinding(\n\t\t\t\trelyingPartyRegistrationElt);\n\t\tif (StringUtils.hasText(entityId)) {\n\t\t\tbuilder.entityId(entityId);\n\t\t}\n\t\tif (StringUtils.hasText(singleLogoutServiceLocation)) {\n\t\t\tbuilder.singleLogoutServiceLocation(singleLogoutServiceLocation);\n\t\t}\n\t\tif (StringUtils.hasText(singleLogoutServiceResponseLocation)) {\n\t\t\tbuilder.singleLogoutServiceResponseLocation(singleLogoutServiceResponseLocation);\n\t\t}\n\t\tif (singleLogoutServiceBinding != null) {\n\t\t\tbuilder.singleLogoutServiceBinding(singleLogoutServiceBinding);\n\t\t}\n\t\tif (StringUtils.hasText(assertionConsumerServiceLocation)) {\n\t\t\tbuilder.assertionConsumerServiceLocation(assertionConsumerServiceLocation);\n\t\t}\n\t\tif (assertionConsumerServiceBinding != null) {\n\t\t\tbuilder.assertionConsumerServiceBinding(assertionConsumerServiceBinding);\n\t\t}\n\t}\n\n\tprivate static void buildAssertingParty(Element relyingPartyElt, Map<String, Map<String, Object>> assertingParties,\n\t\t\tAssertingPartyMetadata.Builder<?> builder, ParserContext parserContext) {\n\t\tString assertingPartyId = relyingPartyElt.getAttribute(ATT_ASSERTING_PARTY_ID);\n\t\tif (!assertingParties.containsKey(assertingPartyId)) {\n\t\t\tObject source = parserContext.extractSource(relyingPartyElt);\n\t\t\tparserContext.getReaderContext()\n\t\t\t\t.error(String.format(\"Could not find asserting party with id %s\", assertingPartyId), source);\n\t\t}\n\t\tMap<String, Object> assertingParty = assertingParties.get(assertingPartyId);\n\t\tString entityId = getAsString(assertingParty, ATT_ENTITY_ID);\n\t\tString wantAuthnRequestsSigned = getAsString(assertingParty, ATT_WANT_AUTHN_REQUESTS_SIGNED);\n\t\tString singleSignOnServiceLocation = getAsString(assertingParty, ATT_SINGLE_SIGN_ON_SERVICE_LOCATION);\n\t\tString singleSignOnServiceBinding = getAsString(assertingParty, ATT_SINGLE_SIGN_ON_SERVICE_BINDING);\n\t\tSaml2MessageBinding saml2MessageBinding = StringUtils.hasText(singleSignOnServiceBinding)\n\t\t\t\t? Saml2MessageBinding.valueOf(singleSignOnServiceBinding) : Saml2MessageBinding.REDIRECT;\n\t\tString singleLogoutServiceLocation = getAsString(assertingParty, ATT_SINGLE_LOGOUT_SERVICE_LOCATION);\n\t\tString singleLogoutServiceResponseLocation = getAsString(assertingParty,\n\t\t\t\tATT_SINGLE_LOGOUT_SERVICE_RESPONSE_LOCATION);\n\t\tString singleLogoutServiceBinding = getAsString(assertingParty, ATT_SINGLE_LOGOUT_SERVICE_BINDING);\n\t\tSaml2MessageBinding saml2LogoutMessageBinding = StringUtils.hasText(singleLogoutServiceBinding)\n\t\t\t\t? Saml2MessageBinding.valueOf(singleLogoutServiceBinding) : Saml2MessageBinding.REDIRECT;\n\t\tbuilder.entityId(entityId)\n\t\t\t.wantAuthnRequestsSigned(Boolean.parseBoolean(wantAuthnRequestsSigned))\n\t\t\t.singleSignOnServiceLocation(singleSignOnServiceLocation)\n\t\t\t.singleSignOnServiceBinding(saml2MessageBinding)\n\t\t\t.singleLogoutServiceLocation(singleLogoutServiceLocation)\n\t\t\t.singleLogoutServiceResponseLocation(singleLogoutServiceResponseLocation)\n\t\t\t.singleLogoutServiceBinding(saml2LogoutMessageBinding);\n\t\taddSigningAlgorithms(assertingParty, builder);\n\t\taddVerificationCredentials(assertingParty, builder);\n\t\taddEncryptionCredentials(assertingParty, builder);\n\t}\n\n\tprivate static void addSigningAlgorithms(Map<String, Object> assertingParty,\n\t\t\tAssertingPartyMetadata.Builder<?> builder) {\n\t\tString signingAlgorithmsAttr = getAsString(assertingParty, ATT_SIGNING_ALGORITHMS);\n\t\tif (StringUtils.hasText(signingAlgorithmsAttr)) {\n\t\t\tList<String> signingAlgorithms = Arrays.asList(signingAlgorithmsAttr.split(\",\"));\n\t\t\tbuilder.signingAlgorithms((s) -> s.addAll(signingAlgorithms));\n\t\t}\n\t}\n\n\tprivate static void addSigningCredentials(Element relyingPartyRegistrationElt,\n\t\t\tRelyingPartyRegistration.Builder builder) {\n\t\tList<Element> credentialElts = DomUtils.getChildElementsByTagName(relyingPartyRegistrationElt,\n\t\t\t\tELT_SIGNING_CREDENTIAL);\n\t\tfor (Element credentialElt : credentialElts) {\n\t\t\tString privateKeyLocation = credentialElt.getAttribute(ATT_PRIVATE_KEY_LOCATION);\n\t\t\tString certificateLocation = credentialElt.getAttribute(ATT_CERTIFICATE_LOCATION);\n\t\t\tbuilder.signingX509Credentials(\n\t\t\t\t\t(c) -> c.add(getSaml2SigningCredential(privateKeyLocation, certificateLocation)));\n\t\t}\n\t}\n\n\tprivate static void addDecryptionCredentials(Element relyingPartyRegistrationElt,\n\t\t\tRelyingPartyRegistration.Builder builder) {\n\t\tList<Element> credentialElts = DomUtils.getChildElementsByTagName(relyingPartyRegistrationElt,\n\t\t\t\tELT_DECRYPTION_CREDENTIAL);\n\t\tfor (Element credentialElt : credentialElts) {\n\t\t\tString privateKeyLocation = credentialElt.getAttribute(ATT_PRIVATE_KEY_LOCATION);\n\t\t\tString certificateLocation = credentialElt.getAttribute(ATT_CERTIFICATE_LOCATION);\n\t\t\tSaml2X509Credential credential = getSaml2DecryptionCredential(privateKeyLocation, certificateLocation);\n\t\t\tbuilder.decryptionX509Credentials((c) -> c.add(credential));\n\t\t}\n\t}\n\n\tprivate static String getAsString(Map<String, Object> assertingParty, String key) {\n\t\treturn (String) assertingParty.get(key);\n\t}\n\n\tprivate static Saml2MessageBinding getAssertionConsumerServiceBinding(Element relyingPartyRegistrationElt) {\n\t\tString assertionConsumerServiceBinding = relyingPartyRegistrationElt\n\t\t\t.getAttribute(ATT_ASSERTION_CONSUMER_SERVICE_BINDING);\n\t\tif (StringUtils.hasText(assertionConsumerServiceBinding)) {\n\t\t\treturn Saml2MessageBinding.valueOf(assertionConsumerServiceBinding);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static Saml2MessageBinding getSingleLogoutServiceBinding(Element relyingPartyRegistrationElt) {\n\t\tString singleLogoutServiceBinding = relyingPartyRegistrationElt.getAttribute(ATT_SINGLE_LOGOUT_SERVICE_BINDING);\n\t\tif (StringUtils.hasText(singleLogoutServiceBinding)) {\n\t\t\treturn Saml2MessageBinding.valueOf(singleLogoutServiceBinding);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static Saml2X509Credential getSaml2VerificationCredential(String certificateLocation) {\n\t\treturn getSaml2Credential(certificateLocation, Saml2X509Credential.Saml2X509CredentialType.VERIFICATION);\n\t}\n\n\tprivate static Saml2X509Credential getSaml2EncryptionCredential(String certificateLocation) {\n\t\treturn getSaml2Credential(certificateLocation, Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION);\n\t}\n\n\tprivate static Saml2X509Credential getSaml2SigningCredential(String privateKeyLocation,\n\t\t\tString certificateLocation) {\n\t\treturn getSaml2Credential(privateKeyLocation, certificateLocation,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.SIGNING);\n\t}\n\n\tprivate static Saml2X509Credential getSaml2DecryptionCredential(String privateKeyLocation,\n\t\t\tString certificateLocation) {\n\t\treturn getSaml2Credential(privateKeyLocation, certificateLocation,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.DECRYPTION);\n\t}\n\n\tprivate static Saml2X509Credential getSaml2Credential(String privateKeyLocation, String certificateLocation,\n\t\t\tSaml2X509Credential.Saml2X509CredentialType credentialType) {\n\t\tRSAPrivateKey privateKey = readPrivateKey(privateKeyLocation);\n\t\tX509Certificate certificate = readCertificate(certificateLocation);\n\t\treturn new Saml2X509Credential(privateKey, certificate, credentialType);\n\t}\n\n\tprivate static Saml2X509Credential getSaml2Credential(String certificateLocation,\n\t\t\tSaml2X509Credential.Saml2X509CredentialType credentialType) {\n\t\tX509Certificate certificate = readCertificate(certificateLocation);\n\t\treturn new Saml2X509Credential(certificate, credentialType);\n\t}\n\n\tprivate static RSAPrivateKey readPrivateKey(String privateKeyLocation) {\n\t\tResource privateKey = resourceLoader.getResource(privateKeyLocation);\n\t\ttry (InputStream inputStream = privateKey.getInputStream()) {\n\t\t\treturn RsaKeyConverters.pkcs8().convert(inputStream);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static X509Certificate readCertificate(String certificateLocation) {\n\t\tResource certificate = resourceLoader.getResource(certificateLocation);\n\t\ttry (InputStream inputStream = certificate.getInputStream()) {\n\t\t\treturn (X509Certificate) CertificateFactory.getInstance(\"X.509\").generateCertificate(inputStream);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static String resolveAttribute(ParserContext pc, String value) {\n\t\treturn pc.getReaderContext().getEnvironment().resolvePlaceholders(value);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/PathPatternRequestMatcherBuilderFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.BeanNameAware;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.lang.NonNull;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\n/**\n * Use this factory bean to configure the {@link PathPatternRequestMatcher.Builder} bean\n * used to create request matchers in {@link AuthorizeHttpRequestsConfigurer} and other\n * parts of the DSL.\n *\n * @author Josh Cummings\n * @since 6.5\n */\npublic final class PathPatternRequestMatcherBuilderFactoryBean implements\n\t\tFactoryBean<PathPatternRequestMatcher.Builder>, ApplicationContextAware, BeanNameAware, BeanFactoryAware {\n\n\tstatic final String MVC_PATTERN_PARSER_BEAN_NAME = \"mvcPatternParser\";\n\n\tprivate final PathPatternParser parser;\n\n\tprivate String basePath;\n\n\tprivate ApplicationContext context;\n\n\tprivate String beanName;\n\n\tprivate ConfigurableListableBeanFactory beanFactory;\n\n\t/**\n\t * Construct this factory bean using the default {@link PathPatternParser}\n\t *\n\t * <p>\n\t * If you are using Spring MVC, it will use the Spring MVC instance.\n\t */\n\tpublic PathPatternRequestMatcherBuilderFactoryBean() {\n\t\tthis(null);\n\t}\n\n\t/**\n\t * Construct this factory bean using this {@link PathPatternParser}.\n\t *\n\t * <p>\n\t * If you are using Spring MVC, it is likely incorrect to call this constructor.\n\t * Please call the default constructor instead.\n\t * @param parser the {@link PathPatternParser} to use\n\t */\n\tpublic PathPatternRequestMatcherBuilderFactoryBean(PathPatternParser parser) {\n\t\tthis.parser = parser;\n\t}\n\n\t@Override\n\tpublic PathPatternRequestMatcher.Builder getObject() throws Exception {\n\t\tif (!this.context.containsBean(MVC_PATTERN_PARSER_BEAN_NAME)) {\n\t\t\tPathPatternParser parser = (this.parser != null) ? this.parser : PathPatternParser.defaultInstance;\n\t\t\treturn withPathPatternParser(parser);\n\t\t}\n\t\tPathPatternParser mvc = this.context.getBean(MVC_PATTERN_PARSER_BEAN_NAME, PathPatternParser.class);\n\t\tPathPatternParser parser = (this.parser != null) ? this.parser : mvc;\n\t\tif (mvc.equals(parser)) {\n\t\t\treturn withPathPatternParser(parser);\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Spring Security and Spring MVC must use the same path pattern parser. \"\n\t\t\t\t+ \"To have Spring Security use Spring MVC's [\" + describe(mvc, MVC_PATTERN_PARSER_BEAN_NAME)\n\t\t\t\t+ \"] simply publish this bean [\" + describe(this, this.beanName) + \"] using its default constructor\");\n\t}\n\n\tprivate PathPatternRequestMatcher.Builder withPathPatternParser(PathPatternParser parser) {\n\t\tif (this.basePath == null) {\n\t\t\treturn PathPatternRequestMatcher.withPathPatternParser(parser);\n\t\t}\n\t\telse {\n\t\t\treturn PathPatternRequestMatcher.withPathPatternParser(parser).basePath(this.basePath);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn PathPatternRequestMatcher.Builder.class;\n\t}\n\n\t/**\n\t * Use this as the base path for patterns built by the resulting\n\t * {@link PathPatternRequestMatcher.Builder} instance\n\t * @param basePath the base path to use\n\t * @since 7.0\n\t * @see PathPatternRequestMatcher.Builder#basePath(String)\n\t */\n\tpublic void setBasePath(String basePath) {\n\t\tthis.basePath = basePath;\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\tthis.context = context;\n\t}\n\n\t@Override\n\tpublic void setBeanName(@NonNull String name) {\n\t\tthis.beanName = name;\n\t}\n\n\t@Override\n\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\tif (beanFactory instanceof ConfigurableListableBeanFactory listable) {\n\t\t\tthis.beanFactory = listable;\n\t\t}\n\t}\n\n\tprivate String describe(Object bean, String name) {\n\t\tString text = bean.getClass().getSimpleName();\n\t\tif (name == null) {\n\t\t\treturn text;\n\t\t}\n\t\ttext += \"defined as '\" + name + \"'\";\n\t\tif (this.beanFactory == null) {\n\t\t\treturn text;\n\t\t}\n\t\tBeanDefinition bd = this.beanFactory.getBeanDefinition(name);\n\t\tString description = bd.getResourceDescription();\n\t\tif (description == null) {\n\t\t\treturn text;\n\t\t}\n\t\ttext += \" in [\" + description + \"]\";\n\t\treturn text;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/messaging/PathPatternMessageMatcherBuilderFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.messaging;\n\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\n/**\n * Use this factory bean to configure the {@link PathPatternMessageMatcher.Builder} bean\n * used to create request matchers in {@link MessageMatcherDelegatingAuthorizationManager}\n * and other parts of the DSL.\n *\n * @author Pat McCusker\n * @since 6.5\n */\npublic final class PathPatternMessageMatcherBuilderFactoryBean\n\t\timplements FactoryBean<PathPatternMessageMatcher.Builder> {\n\n\tprivate PathPatternParser parser;\n\n\t/**\n\t * Create {@link PathPatternMessageMatcher}s using\n\t * {@link PathPatternParser#defaultInstance}\n\t */\n\tpublic PathPatternMessageMatcherBuilderFactoryBean() {\n\n\t}\n\n\t/**\n\t * Create {@link PathPatternMessageMatcher}s using the given {@link PathPatternParser}\n\t * @param parser the {@link PathPatternParser} to use\n\t */\n\tpublic PathPatternMessageMatcherBuilderFactoryBean(PathPatternParser parser) {\n\t\tthis.parser = parser;\n\t}\n\n\t@Override\n\tpublic PathPatternMessageMatcher.Builder getObject() throws Exception {\n\t\tif (this.parser == null) {\n\t\t\treturn PathPatternMessageMatcher.withDefaults();\n\t\t}\n\t\treturn PathPatternMessageMatcher.withPathPatternParser(this.parser);\n\t}\n\n\t@Override\n\tpublic Class<?> getObjectType() {\n\t\treturn PathPatternMessageMatcher.Builder.class;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/AbstractServerWebExchangeMatcherRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.web.util.pattern.PathPattern;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic abstract class AbstractServerWebExchangeMatcherRegistry<T> {\n\n\tAbstractServerWebExchangeMatcherRegistry() {\n\t}\n\n\t/**\n\t * Maps any request.\n\t * @return the object that is chained after creating the\n\t * {@link ServerWebExchangeMatcher}\n\t */\n\tpublic T anyExchange() {\n\t\treturn matcher(ServerWebExchangeMatchers.anyExchange());\n\t}\n\n\t/**\n\t * Maps a {@link List} of {@link PathPatternParserServerWebExchangeMatcher} instances.\n\t * @param method the {@link HttpMethod} to use for any {@link HttpMethod}.\n\t * @return the object that is chained after creating the\n\t * {@link ServerWebExchangeMatcher}\n\t */\n\tpublic T pathMatchers(HttpMethod method) {\n\t\treturn pathMatchers(method, new String[] { \"/**\" });\n\t}\n\n\t/**\n\t * Maps a {@link List} of {@link PathPatternParserServerWebExchangeMatcher} instances.\n\t * @param method the {@link HttpMethod} to use or {@code null} for any\n\t * {@link HttpMethod}.\n\t * @param antPatterns the ant patterns to create. If {@code null} or empty, then\n\t * matches on nothing. {@link PathPatternParserServerWebExchangeMatcher} from\n\t * @return the object that is chained after creating the\n\t * {@link ServerWebExchangeMatcher}\n\t */\n\tpublic T pathMatchers(HttpMethod method, String... antPatterns) {\n\t\tList<PathPattern> pathPatterns = parsePatterns(antPatterns);\n\t\treturn matcher(ServerWebExchangeMatchers.pathMatchers(method, pathPatterns.toArray(new PathPattern[0])));\n\t}\n\n\t/**\n\t * Maps a {@link List} of {@link PathPatternParserServerWebExchangeMatcher} instances\n\t * that do not care which {@link HttpMethod} is used.\n\t * @param antPatterns the ant patterns to create\n\t * {@link PathPatternParserServerWebExchangeMatcher} from\n\t * @return the object that is chained after creating the\n\t * {@link ServerWebExchangeMatcher}\n\t */\n\tpublic T pathMatchers(String... antPatterns) {\n\t\tList<PathPattern> pathPatterns = parsePatterns(antPatterns);\n\t\treturn matcher(ServerWebExchangeMatchers.pathMatchers(pathPatterns.toArray(new PathPattern[0])));\n\t}\n\n\tprivate List<PathPattern> parsePatterns(String[] antPatterns) {\n\t\tPathPatternParser parser = getPathPatternParser();\n\t\tList<PathPattern> pathPatterns = new ArrayList<>(antPatterns.length);\n\t\tfor (String pattern : antPatterns) {\n\t\t\tpattern = parser.initFullPathPattern(pattern);\n\t\t\tPathPattern pathPattern = parser.parse(pattern);\n\t\t\tpathPatterns.add(pathPattern);\n\t\t}\n\t\treturn pathPatterns;\n\t}\n\n\t/**\n\t * Associates a list of {@link ServerWebExchangeMatcher} instances\n\t * @param matchers the {@link ServerWebExchangeMatcher} instances\n\t * @return the object that is chained after creating the\n\t * {@link ServerWebExchangeMatcher}\n\t */\n\tpublic T matchers(ServerWebExchangeMatcher... matchers) {\n\t\treturn registerMatcher(new OrServerWebExchangeMatcher(matchers));\n\t}\n\n\t/**\n\t * Subclasses should implement this method for returning the object that is chained to\n\t * the creation of the {@link ServerWebExchangeMatcher} instances.\n\t * @param matcher the {@link ServerWebExchangeMatcher} instances that were created\n\t * @return the chained Object for the subclass which allows association of something\n\t * else to the {@link ServerWebExchangeMatcher}\n\t */\n\tprotected abstract T registerMatcher(ServerWebExchangeMatcher matcher);\n\n\tprotected PathPatternParser getPathPatternParser() {\n\t\treturn PathPatternParser.defaultInstance;\n\t}\n\n\t/**\n\t * Associates a {@link ServerWebExchangeMatcher} instances\n\t * @param matcher the {@link ServerWebExchangeMatcher} instance\n\t * @return the object that is chained after creating the\n\t * {@link ServerWebExchangeMatcher}\n\t */\n\tprivate T matcher(ServerWebExchangeMatcher matcher) {\n\t\treturn registerMatcher(matcher);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/GenericHttpMessageConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\n\n/**\n * {@link GenericHttpMessageConverter} implementation that delegates to a\n * {@link SmartHttpMessageConverter}.\n *\n * @param <T> the converted object type\n * @author Sebastien Deleuze\n * @since 7.0\n */\nfinal class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {\n\n\tprivate final SmartHttpMessageConverter<T> smartConverter;\n\n\tGenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {\n\t\tthis.smartConverter = smartConverter;\n\t}\n\n\t@Override\n\tpublic boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forType(type), mediaType);\n\t}\n\n\t@Override\n\tpublic T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\treturn this.smartConverter.getSupportedMediaTypes();\n\t}\n\n\t@Override\n\tpublic T read(Class<? extends T> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(clazz, inputMessage);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, contentType, outputMessage);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/HttpMessageConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.GsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JsonbHttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Utility methods for {@link HttpMessageConverter}'s.\n *\n * @author Joe Grandja\n * @author luamas\n * @since 5.1\n */\nfinal class HttpMessageConverters {\n\n\tprivate static final boolean jacksonPresent;\n\n\tprivate static final boolean jackson2Present;\n\n\tprivate static final boolean gsonPresent;\n\n\tprivate static final boolean jsonbPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = HttpMessageConverters.class.getClassLoader();\n\t\tjacksonPresent = ClassUtils.isPresent(\"tools.jackson.databind.json.JsonMapper\", classLoader);\n\t\tjackson2Present = ClassUtils.isPresent(\"com.fasterxml.jackson.databind.ObjectMapper\", classLoader)\n\t\t\t\t&& ClassUtils.isPresent(\"com.fasterxml.jackson.core.JsonGenerator\", classLoader);\n\t\tgsonPresent = ClassUtils.isPresent(\"com.google.gson.Gson\", classLoader);\n\t\tjsonbPresent = ClassUtils.isPresent(\"jakarta.json.bind.Jsonb\", classLoader);\n\t}\n\n\tprivate HttpMessageConverters() {\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tstatic GenericHttpMessageConverter<Object> getJsonMessageConverter() {\n\t\tif (jacksonPresent) {\n\t\t\treturn new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());\n\t\t}\n\t\tif (jackson2Present) {\n\t\t\treturn new MappingJackson2HttpMessageConverter();\n\t\t}\n\t\tif (gsonPresent) {\n\t\t\treturn new GsonHttpMessageConverter();\n\t\t}\n\t\tif (jsonbPresent) {\n\t\t\treturn new JsonbHttpMessageConverter();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/OAuth2ErrorEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.codec.HttpMessageEncoder;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.lang.NonNull;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.util.MimeType;\n\nclass OAuth2ErrorEncoder implements HttpMessageEncoder<OAuth2Error> {\n\n\tprivate final HttpMessageConverter<Object> messageConverter = HttpMessageConverters.getJsonMessageConverter();\n\n\t@NonNull\n\t@Override\n\tpublic List<MediaType> getStreamingMediaTypes() {\n\t\treturn List.of();\n\t}\n\n\t@Override\n\tpublic boolean canEncode(ResolvableType elementType, MimeType mimeType) {\n\t\treturn getEncodableMimeTypes().contains(mimeType);\n\t}\n\n\t@NonNull\n\t@Override\n\tpublic Flux<DataBuffer> encode(Publisher<? extends OAuth2Error> error, DataBufferFactory bufferFactory,\n\t\t\tResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {\n\t\treturn Mono.from(error).flatMap((data) -> {\n\t\t\tByteArrayHttpOutputMessage bytes = new ByteArrayHttpOutputMessage();\n\t\t\ttry {\n\t\t\t\tthis.messageConverter.write(data, MediaType.APPLICATION_JSON, bytes);\n\t\t\t\treturn Mono.just(bytes.getBody().toByteArray());\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\treturn Mono.error(ex);\n\t\t\t}\n\t\t}).map(bufferFactory::wrap).flux();\n\t}\n\n\t@NonNull\n\t@Override\n\tpublic List<MimeType> getEncodableMimeTypes() {\n\t\treturn List.of(MediaType.APPLICATION_JSON);\n\t}\n\n\tprivate static class ByteArrayHttpOutputMessage implements HttpOutputMessage {\n\n\t\tprivate final ByteArrayOutputStream body = new ByteArrayOutputStream();\n\n\t\t@NonNull\n\t\t@Override\n\t\tpublic ByteArrayOutputStream getBody() {\n\t\t\treturn this.body;\n\t\t}\n\n\t\t@NonNull\n\t\t@Override\n\t\tpublic HttpHeaders getHeaders() {\n\t\t\treturn new HttpHeaders();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.io.Serial;\nimport java.util.Collections;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\n\n/**\n * An {@link org.springframework.security.core.Authentication} implementation that\n * represents the result of authenticating an OIDC Logout token for the purposes of\n * performing Back-Channel Logout.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see OidcLogoutAuthenticationToken\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html\">OIDC Back-Channel\n * Logout</a>\n */\nclass OidcBackChannelLogoutAuthentication extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 9095810699956350287L;\n\n\tprivate final OidcLogoutToken logoutToken;\n\n\tprivate final ClientRegistration clientRegistration;\n\n\t/**\n\t * Construct an {@link OidcBackChannelLogoutAuthentication}\n\t * @param logoutToken a deserialized, verified OIDC Logout Token\n\t */\n\tOidcBackChannelLogoutAuthentication(OidcLogoutToken logoutToken, ClientRegistration clientRegistration) {\n\t\tsuper(Collections.emptyList());\n\t\tthis.logoutToken = logoutToken;\n\t\tthis.clientRegistration = clientRegistration;\n\t\tsetAuthenticated(true);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic OidcLogoutToken getPrincipal() {\n\t\treturn this.logoutToken;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic OidcLogoutToken getCredentials() {\n\t\treturn this.logoutToken;\n\t}\n\n\tClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.JwtTypeValidator;\nimport org.springframework.security.oauth2.jwt.JwtValidators;\nimport org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} that authenticates an OIDC Logout Token; namely\n * deserializing it, verifying its signature, and validating its claims.\n *\n * <p>\n * Intended to be included in a\n * {@link org.springframework.security.authentication.ProviderManager}\n *\n * @author Josh Cummings\n * @since 6.2\n * @see OidcLogoutAuthenticationToken\n * @see org.springframework.security.authentication.ProviderManager\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html\">OIDC Back-Channel\n * Logout</a>\n */\nfinal class OidcBackChannelLogoutReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate ReactiveJwtDecoderFactory<ClientRegistration> logoutTokenDecoderFactory;\n\n\t/**\n\t * Construct an {@link OidcBackChannelLogoutReactiveAuthenticationManager}\n\t */\n\tOidcBackChannelLogoutReactiveAuthenticationManager() {\n\t\tJwtTypeValidator type = new JwtTypeValidator(\"JWT\", \"logout+jwt\");\n\t\ttype.setAllowEmpty(true);\n\t\tFunction<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidator = (clientRegistration) -> JwtValidators\n\t\t\t.createDefaultWithValidators(type, new OidcBackChannelLogoutTokenValidator(clientRegistration));\n\t\tthis.logoutTokenDecoderFactory = (clientRegistration) -> {\n\t\t\tString jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();\n\t\t\tif (!StringUtils.hasText(jwkSetUri)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(\"missing_signature_verifier\",\n\t\t\t\t\t\t\"Failed to find a Signature Verifier for Client Registration: '\"\n\t\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId()\n\t\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured the JwkSet URI.\",\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build();\n\t\t\tdecoder.setJwtValidator(jwtValidator.apply(clientRegistration));\n\t\t\tdecoder.setClaimSetConverter(\n\t\t\t\t\tnew ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));\n\t\t\treturn decoder;\n\t\t};\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (!(authentication instanceof OidcLogoutAuthenticationToken token)) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tString logoutToken = token.getLogoutToken();\n\t\tClientRegistration registration = token.getClientRegistration();\n\t\treturn decode(registration, logoutToken)\n\t\t\t.map((jwt) -> OidcLogoutToken.withTokenValue(logoutToken)\n\t\t\t\t.claims((claims) -> claims.putAll(jwt.getClaims()))\n\t\t\t\t.build())\n\t\t\t.map((oidcLogoutToken) -> new OidcBackChannelLogoutAuthentication(oidcLogoutToken, registration));\n\t}\n\n\tprivate Mono<Jwt> decode(ClientRegistration registration, String token) {\n\t\tReactiveJwtDecoder logoutTokenDecoder = this.logoutTokenDecoderFactory.createDecoder(registration);\n\t\treturn logoutTokenDecoder.decode(token).onErrorResume(Exception.class, (ex) -> {\n\t\t\tif (ex instanceof BadJwtException) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, ex.getMessage(),\n\t\t\t\t\t\t\"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\");\n\t\t\t\treturn Mono.error(new OAuth2AuthenticationException(error, ex));\n\t\t\t}\n\t\t\treturn Mono.error(new AuthenticationServiceException(ex.getMessage(), ex));\n\t\t});\n\t}\n\n\t/**\n\t * Use this {@link ReactiveJwtDecoderFactory} to generate {@link JwtDecoder}s that\n\t * correspond to the {@link ClientRegistration} associated with the OIDC logout token.\n\t * @param logoutTokenDecoderFactory the {@link JwtDecoderFactory} to use\n\t */\n\tvoid setLogoutTokenDecoderFactory(ReactiveJwtDecoderFactory<ClientRegistration> logoutTokenDecoderFactory) {\n\t\tAssert.notNull(logoutTokenDecoderFactory, \"logoutTokenDecoderFactory cannot be null\");\n\t\tthis.logoutTokenDecoderFactory = logoutTokenDecoderFactory;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutTokenValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimAccessor;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link OAuth2TokenValidator} that validates OIDC Logout Token claims in conformance\n * with the OIDC Back-Channel Logout Spec.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see OidcLogoutToken\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken\">Logout\n * Token</a>\n * @see <a target=\"blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\">the OIDC\n * Back-Channel Logout spec</a>\n */\nfinal class OidcBackChannelLogoutTokenValidator implements OAuth2TokenValidator<Jwt> {\n\n\tprivate static final String LOGOUT_VALIDATION_URL = \"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\";\n\n\tprivate static final String BACK_CHANNEL_LOGOUT_EVENT = \"http://schemas.openid.net/event/backchannel-logout\";\n\n\tprivate final String audience;\n\n\tprivate final String issuer;\n\n\tOidcBackChannelLogoutTokenValidator(ClientRegistration clientRegistration) {\n\t\tthis.audience = clientRegistration.getClientId();\n\t\tString issuer = clientRegistration.getProviderDetails().getIssuerUri();\n\t\tAssert.hasText(issuer, \"Provider issuer cannot be null\");\n\t\tthis.issuer = issuer;\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\tCollection<OAuth2Error> errors = new ArrayList<>();\n\n\t\tLogoutTokenClaimAccessor logoutClaims = jwt::getClaims;\n\t\tMap<String, Object> events = logoutClaims.getEvents();\n\t\tif (events == null) {\n\t\t\terrors.add(invalidLogoutToken(\"events claim must not be null\"));\n\t\t}\n\t\telse if (events.get(BACK_CHANNEL_LOGOUT_EVENT) == null) {\n\t\t\terrors.add(invalidLogoutToken(\"events claim map must contain \\\"\" + BACK_CHANNEL_LOGOUT_EVENT + \"\\\" key\"));\n\t\t}\n\n\t\tString issuer = logoutClaims.getIssuer().toExternalForm();\n\t\tif (issuer == null) {\n\t\t\terrors.add(invalidLogoutToken(\"iss claim must not be null\"));\n\t\t}\n\t\telse if (!this.issuer.equals(issuer)) {\n\t\t\terrors.add(invalidLogoutToken(\n\t\t\t\t\t\"iss claim value must match `ClientRegistration#getProviderDetails#getIssuerUri`\"));\n\t\t}\n\n\t\tList<String> audience = logoutClaims.getAudience();\n\t\tif (audience == null) {\n\t\t\terrors.add(invalidLogoutToken(\"aud claim must not be null\"));\n\t\t}\n\t\telse if (!audience.contains(this.audience)) {\n\t\t\terrors.add(invalidLogoutToken(\"aud claim value must include `ClientRegistration#getClientId`\"));\n\t\t}\n\n\t\tInstant issuedAt = logoutClaims.getIssuedAt();\n\t\tif (issuedAt == null) {\n\t\t\terrors.add(invalidLogoutToken(\"iat claim must not be null\"));\n\t\t}\n\n\t\tString jwtId = logoutClaims.getId();\n\t\tif (jwtId == null) {\n\t\t\terrors.add(invalidLogoutToken(\"jti claim must not be null\"));\n\t\t}\n\n\t\tif (logoutClaims.getSubject() == null && logoutClaims.getSessionId() == null) {\n\t\t\terrors.add(invalidLogoutToken(\"sub and sid claims must not both be null\"));\n\t\t}\n\n\t\tif (logoutClaims.getClaim(\"nonce\") != null) {\n\t\t\terrors.add(invalidLogoutToken(\"nonce claim must not be present\"));\n\t\t}\n\n\t\treturn OAuth2TokenValidatorResult.failure(errors);\n\t}\n\n\tprivate static OAuth2Error invalidLogoutToken(String description) {\n\t\treturn new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, description, LOGOUT_VALIDATION_URL);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.util.Collections;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.codec.EncoderHttpMessageWriter;\nimport org.springframework.http.codec.HttpMessageWriter;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * A filter for the Client-side OIDC Back-Channel Logout endpoint\n *\n * @author Josh Cummings\n * @author Andrey Litvitski\n * @since 6.2\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html\">OIDC Back-Channel Logout\n * Spec</a>\n */\nclass OidcBackChannelLogoutWebFilter implements WebFilter {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final ServerAuthenticationConverter authenticationConverter;\n\n\tprivate final ReactiveAuthenticationManager authenticationManager;\n\n\tprivate final ServerLogoutHandler logoutHandler;\n\n\tprivate final HttpMessageWriter<OAuth2Error> errorHttpMessageConverter = new EncoderHttpMessageWriter<>(\n\t\t\tnew OAuth2ErrorEncoder());\n\n\t/**\n\t * Construct an {@link OidcBackChannelLogoutWebFilter}\n\t * @param authenticationConverter the {@link AuthenticationConverter} for deriving\n\t * Logout Token authentication\n\t * @param authenticationManager the {@link AuthenticationManager} for authenticating\n\t * Logout Tokens\n\t */\n\tOidcBackChannelLogoutWebFilter(ServerAuthenticationConverter authenticationConverter,\n\t\t\tReactiveAuthenticationManager authenticationManager, ServerLogoutHandler logoutHandler) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.notNull(logoutHandler, \"logoutHandler cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.logoutHandler = logoutHandler;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.authenticationConverter.convert(exchange).onErrorResume(AuthenticationException.class, (ex) -> {\n\t\t\tthis.logger.debug(\"Failed to process OIDC Back-Channel Logout\", ex);\n\t\t\tif (ex instanceof AuthenticationServiceException) {\n\t\t\t\treturn Mono.error(ex);\n\t\t\t}\n\t\t\treturn handleAuthenticationFailure(exchange, ex).then(Mono.empty());\n\t\t})\n\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t.flatMap(this.authenticationManager::authenticate)\n\t\t\t.onErrorResume(AuthenticationException.class, (ex) -> {\n\t\t\t\tthis.logger.debug(\"Failed to process OIDC Back-Channel Logout\", ex);\n\t\t\t\tif (ex instanceof AuthenticationServiceException) {\n\t\t\t\t\treturn Mono.error(ex);\n\t\t\t\t}\n\t\t\t\treturn handleAuthenticationFailure(exchange, ex).then(Mono.empty());\n\t\t\t})\n\t\t\t.flatMap((authentication) -> {\n\t\t\t\tWebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain);\n\t\t\t\treturn this.logoutHandler.logout(webFilterExchange, authentication);\n\t\t\t});\n\t}\n\n\tprivate Mono<Void> handleAuthenticationFailure(ServerWebExchange exchange, Exception ex) {\n\t\tthis.logger.debug(\"Failed to process OIDC Back-Channel Logout\", ex);\n\t\texchange.getResponse().setRawStatusCode(HttpStatus.BAD_REQUEST.value());\n\t\treturn this.errorHttpMessageConverter.write(Mono.just(oauth2Error(ex)), ResolvableType.forClass(Object.class),\n\t\t\t\tResolvableType.forClass(Object.class), MediaType.APPLICATION_JSON, exchange.getRequest(),\n\t\t\t\texchange.getResponse(), Collections.emptyMap());\n\t}\n\n\tprivate OAuth2Error oauth2Error(Exception ex) {\n\t\tif (ex instanceof OAuth2AuthenticationException oauth2) {\n\t\t\treturn oauth2.getError();\n\t\t}\n\t\treturn new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, ex.getMessage(),\n\t\t\t\t\"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelServerLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.codec.EncoderHttpMessageWriter;\nimport org.springframework.http.codec.HttpMessageWriter;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@link ServerLogoutHandler} that locates the sessions associated with a given OIDC\n * Back-Channel Logout Token and invalidates each one.\n *\n * @author Josh Cummings\n * @author Andrey Litvitski\n * @since 6.2\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html\">OIDC Back-Channel Logout\n * Spec</a>\n */\npublic final class OidcBackChannelServerLogoutHandler implements ServerLogoutHandler {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final ReactiveOidcSessionRegistry sessionRegistry;\n\n\tprivate final HttpMessageWriter<OAuth2Error> errorHttpMessageConverter = new EncoderHttpMessageWriter<>(\n\t\t\tnew OAuth2ErrorEncoder());\n\n\tprivate WebClient web = WebClient.create();\n\n\tprivate String logoutUri = \"{baseUrl}/logout/connect/back-channel/{registrationId}\";\n\n\tprivate String sessionCookieName = \"SESSION\";\n\n\tpublic OidcBackChannelServerLogoutHandler(ReactiveOidcSessionRegistry sessionRegistry) {\n\t\tthis.sessionRegistry = sessionRegistry;\n\t}\n\n\t@Override\n\tpublic Mono<Void> logout(WebFilterExchange exchange, Authentication authentication) {\n\t\tif (!(authentication instanceof OidcBackChannelLogoutAuthentication token)) {\n\t\t\treturn Mono.defer(() -> {\n\t\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\t\tString message = \"Did not perform OIDC Back-Channel Logout since authentication [%s] was of the wrong type\";\n\t\t\t\t\tthis.logger.debug(String.format(message, authentication.getClass().getSimpleName()));\n\t\t\t\t}\n\t\t\t\treturn Mono.empty();\n\t\t\t});\n\t\t}\n\t\tAtomicInteger totalCount = new AtomicInteger(0);\n\t\tAtomicInteger invalidatedCount = new AtomicInteger(0);\n\t\treturn this.sessionRegistry.removeSessionInformation(token.getPrincipal()).concatMap((session) -> {\n\t\t\ttotalCount.incrementAndGet();\n\t\t\treturn eachLogout(exchange, session, token).flatMap((response) -> {\n\t\t\t\tinvalidatedCount.incrementAndGet();\n\t\t\t\treturn Mono.empty();\n\t\t\t}).onErrorResume((ex) -> {\n\t\t\t\tthis.logger.debug(\"Failed to invalidate session\", ex);\n\t\t\t\treturn this.sessionRegistry.saveSessionInformation(session).then(Mono.just(ex.getMessage()));\n\t\t\t});\n\t\t}).collectList().flatMap((list) -> {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(String.format(\"Invalidated %d out of %d sessions\", invalidatedCount.intValue(),\n\t\t\t\t\t\ttotalCount.intValue()));\n\t\t\t}\n\t\t\tif (!list.isEmpty()) {\n\t\t\t\treturn handleLogoutFailure(exchange.getExchange(), oauth2Error(list));\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn Mono.empty();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate Mono<ResponseEntity<Void>> eachLogout(WebFilterExchange exchange, OidcSessionInformation session,\n\t\t\tOidcBackChannelLogoutAuthentication token) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.add(HttpHeaders.COOKIE, this.sessionCookieName + \"=\" + session.getSessionId());\n\t\tfor (Map.Entry<String, String> credential : session.getAuthorities().entrySet()) {\n\t\t\theaders.add(credential.getKey(), credential.getValue());\n\t\t}\n\t\tString logout = computeLogoutEndpoint(exchange.getExchange().getRequest(), token);\n\t\tMultiValueMap<String, String> body = new LinkedMultiValueMap<>();\n\t\tbody.add(\"logout_token\", token.getPrincipal().getTokenValue());\n\t\tbody.add(\"_spring_security_internal_logout\", \"true\");\n\t\treturn this.web.post()\n\t\t\t.uri(logout)\n\t\t\t.headers((h) -> h.putAll(headers))\n\t\t\t.body(BodyInserters.fromFormData(body))\n\t\t\t.retrieve()\n\t\t\t.toBodilessEntity();\n\t}\n\n\tString computeLogoutEndpoint(ServerHttpRequest request, OidcBackChannelLogoutAuthentication token) {\n\t\t// @formatter:off\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI())\n\t\t\t\t.replacePath(request.getPath().contextPath().value())\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.fragment(null)\n\t\t\t\t.build();\n\n\t\tMap<String, String> uriVariables = new HashMap<>();\n\t\tString scheme = uriComponents.getScheme();\n\t\turiVariables.put(\"baseScheme\", (scheme != null) ? scheme : \"\");\n\t\turiVariables.put(\"baseUrl\", uriComponents.toUriString());\n\n\t\tString host = uriComponents.getHost();\n\t\turiVariables.put(\"baseHost\", (host != null) ? host : \"\");\n\n\t\tString path = uriComponents.getPath();\n\t\turiVariables.put(\"basePath\", (path != null) ? path : \"\");\n\n\t\tint port = uriComponents.getPort();\n\t\turiVariables.put(\"basePort\", (port == -1) ? \"\" : \":\" + port);\n\n\t\tString registrationId = token.getClientRegistration().getRegistrationId();\n\t\turiVariables.put(\"registrationId\", registrationId);\n\n\t\treturn UriComponentsBuilder.fromUriString(this.logoutUri)\n\t\t\t\t.buildAndExpand(uriVariables)\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate OAuth2Error oauth2Error(Collection<?> errors) {\n\t\treturn new OAuth2Error(\"partial_logout\", \"not all sessions were terminated: \" + errors,\n\t\t\t\t\"https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation\");\n\t}\n\n\tprivate Mono<Void> handleLogoutFailure(ServerWebExchange exchange, OAuth2Error error) {\n\t\texchange.getResponse().setRawStatusCode(HttpStatus.BAD_REQUEST.value());\n\t\treturn this.errorHttpMessageConverter.write(Mono.just(error), ResolvableType.forClass(Object.class),\n\t\t\t\tResolvableType.forClass(Object.class), MediaType.APPLICATION_JSON, exchange.getRequest(),\n\t\t\t\texchange.getResponse(), Collections.emptyMap());\n\t}\n\n\t/**\n\t * Use this logout URI for performing per-session logout. Defaults to {@code /logout}\n\t * since that is the default URI for\n\t * {@link org.springframework.security.web.authentication.logout.LogoutFilter}.\n\t * @param logoutUri the URI to use\n\t */\n\tpublic void setLogoutUri(String logoutUri) {\n\t\tAssert.hasText(logoutUri, \"logoutUri cannot be empty\");\n\t\tthis.logoutUri = logoutUri;\n\t}\n\n\t/**\n\t * Use this cookie name for the session identifier. Defaults to {@code JSESSIONID}.\n\t *\n\t * <p>\n\t * Note that if you are using Spring Session, this likely needs to change to SESSION.\n\t * @param sessionCookieName the cookie name to use\n\t */\n\tpublic void setSessionCookieName(String sessionCookieName) {\n\t\tAssert.hasText(sessionCookieName, \"clientSessionCookieName cannot be empty\");\n\t\tthis.sessionCookieName = sessionCookieName;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/OidcLogoutAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.io.Serial;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\n\n/**\n * An {@link org.springframework.security.core.Authentication} instance that represents a\n * request to authenticate an OIDC Logout Token.\n *\n * @author Josh Cummings\n * @since 6.2\n */\nclass OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1568528983223505540L;\n\n\tprivate final String logoutToken;\n\n\tprivate final ClientRegistration clientRegistration;\n\n\t/**\n\t * Construct an {@link OidcLogoutAuthenticationToken}\n\t * @param logoutToken a signed, serialized OIDC Logout token\n\t * @param clientRegistration the {@link ClientRegistration client} associated with\n\t * this token; this is usually derived from material in the logout HTTP request\n\t */\n\tOidcLogoutAuthenticationToken(String logoutToken, ClientRegistration clientRegistration) {\n\t\tsuper(AuthorityUtils.NO_AUTHORITIES);\n\t\tthis.logoutToken = logoutToken;\n\t\tthis.clientRegistration = clientRegistration;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic String getCredentials() {\n\t\treturn this.logoutToken;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic String getPrincipal() {\n\t\treturn this.logoutToken;\n\t}\n\n\t/**\n\t * Get the signed, serialized OIDC Logout token\n\t * @return the logout token\n\t */\n\tString getLogoutToken() {\n\t\treturn this.logoutToken;\n\t}\n\n\t/**\n\t * Get the {@link ClientRegistration} associated with this logout token\n\t * @return the {@link ClientRegistration}\n\t */\n\tClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/OidcLogoutServerAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An {@link AuthenticationConverter} that extracts the OIDC Logout Token authentication\n * request\n *\n * @author Josh Cummings\n * @since 6.2\n */\nfinal class OidcLogoutServerAuthenticationConverter implements ServerAuthenticationConverter {\n\n\tprivate static final String DEFAULT_LOGOUT_URI = \"/logout/connect/back-channel/{registrationId}\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate ServerWebExchangeMatcher exchangeMatcher = new PathPatternParserServerWebExchangeMatcher(DEFAULT_LOGOUT_URI,\n\t\t\tHttpMethod.POST);\n\n\tOidcLogoutServerAuthenticationConverter(ReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> convert(ServerWebExchange exchange) {\n\t\treturn this.exchangeMatcher.matches(exchange)\n\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t.flatMap((match) -> {\n\t\t\t\tString registrationId = (String) match.getVariables().get(\"registrationId\");\n\t\t\t\treturn this.clientRegistrationRepository.findByRegistrationId(registrationId)\n\t\t\t\t\t.switchIfEmpty(Mono.error(() -> {\n\t\t\t\t\t\tthis.logger\n\t\t\t\t\t\t\t.debug(\"Did not process OIDC Back-Channel Logout since no ClientRegistration was found\");\n\t\t\t\t\t\treturn new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\t\t\t}));\n\t\t\t})\n\t\t\t.flatMap((clientRegistration) -> exchange.getFormData().map((data) -> {\n\t\t\t\tString logoutToken = data.getFirst(\"logout_token\");\n\t\t\t\treturn new OidcLogoutAuthenticationToken(logoutToken, clientRegistration);\n\t\t\t}).switchIfEmpty(Mono.error(() -> {\n\t\t\t\tthis.logger.debug(\"Failed to process OIDC Back-Channel Logout since no logout token was found\");\n\t\t\t\treturn new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\t})));\n\t}\n\n\t/**\n\t * The logout endpoint. Defaults to\n\t * {@code /logout/connect/back-channel/{registrationId}}.\n\t * @param exchangeMatcher the {@link ServerWebExchangeMatcher} to use\n\t */\n\tvoid setExchangeMatcher(ServerWebExchangeMatcher exchangeMatcher) {\n\t\tAssert.notNull(exchangeMatcher, \"exchangeMatcher cannot be null\");\n\t\tthis.exchangeMatcher = exchangeMatcher;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/SecurityWebFiltersOrder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic enum SecurityWebFiltersOrder {\n\n\tFIRST(Integer.MIN_VALUE),\n\n\tHTTP_HEADERS_WRITER,\n\n\t/**\n\t * {@link org.springframework.security.web.server.transport.HttpsRedirectWebFilter}\n\t */\n\tHTTPS_REDIRECT,\n\n\t/**\n\t * {@link org.springframework.web.cors.reactive.CorsWebFilter}\n\t */\n\tCORS,\n\n\t/**\n\t * {@link org.springframework.security.web.server.csrf.CsrfWebFilter}\n\t */\n\tCSRF,\n\n\t/**\n\t * {@link org.springframework.security.web.server.context.ReactorContextWebFilter}\n\t */\n\tREACTOR_CONTEXT,\n\n\t/**\n\t * Instance of AuthenticationWebFilter\n\t */\n\tHTTP_BASIC,\n\n\t/**\n\t * Instance of AuthenticationWebFilter\n\t */\n\tFORM_LOGIN, AUTHENTICATION,\n\n\t/**\n\t * Instance of AnonymousAuthenticationWebFilter\n\t */\n\tANONYMOUS_AUTHENTICATION,\n\n\tOAUTH2_AUTHORIZATION_CODE,\n\n\tLOGIN_PAGE_GENERATING,\n\n\tLOGOUT_PAGE_GENERATING,\n\n\t/**\n\t * {@link org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter}\n\t */\n\tONE_TIME_TOKEN,\n\n\t/**\n\t * {@link org.springframework.security.web.server.ui.OneTimeTokenSubmitPageGeneratingWebFilter}\n\t */\n\tONE_TIME_TOKEN_SUBMIT_PAGE_GENERATING,\n\n\t/**\n\t * {@link org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter}\n\t */\n\tSECURITY_CONTEXT_SERVER_WEB_EXCHANGE,\n\n\t/**\n\t * {@link org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter}\n\t */\n\tSERVER_REQUEST_CACHE,\n\n\tLOGOUT,\n\n\tEXCEPTION_TRANSLATION,\n\n\tAUTHORIZATION,\n\n\tLAST(Integer.MAX_VALUE);\n\n\tprivate static final int INTERVAL = 100;\n\n\tprivate final int order;\n\n\tSecurityWebFiltersOrder() {\n\t\tthis.order = ordinal() * INTERVAL;\n\t}\n\n\tSecurityWebFiltersOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.security.interfaces.RSAPublicKey;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.UUID;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotationAwareOrderComparator;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.authentication.ott.reactive.InMemoryReactiveOneTimeTokenService;\nimport org.springframework.security.authentication.ott.reactive.OneTimeTokenReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService;\nimport org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityReactiveAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.session.ReactiveSessionRegistry;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeReactiveAuthenticationManager;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginReactiveAuthenticationManager;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.WebClientReactiveAuthorizationCodeTokenResponseClient;\nimport org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeReactiveAuthenticationManager;\nimport org.springframework.security.oauth2.client.oidc.server.session.InMemoryReactiveOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationCodeGrantWebFilter;\nimport org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter;\nimport org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationCodeAuthenticationTokenConverter;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;\nimport org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenReactiveAuthenticationManager;\nimport org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.introspection.SpringReactiveOpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.web.access.server.BearerTokenServerAccessDeniedHandler;\nimport org.springframework.security.oauth2.server.resource.web.server.BearerTokenServerAuthenticationEntryPoint;\nimport org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor;\nimport org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;\nimport org.springframework.security.web.server.ExchangeMatcherRedirectWebFilter;\nimport org.springframework.security.web.server.MatcherSecurityWebFilterChain;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter;\nimport org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher;\nimport org.springframework.security.web.server.authentication.AuthenticationWebFilter;\nimport org.springframework.security.web.server.authentication.ConcurrentSessionControlServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.DelegatingServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;\nimport org.springframework.security.web.server.authentication.InvalidateLeastUsedServerMaximumSessionsExceededHandler;\nimport org.springframework.security.web.server.authentication.ReactivePreAuthenticatedAuthenticationManager;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.RegisterSessionServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationEntryPointFailureHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.ServerFormLoginAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ServerHttpBasicAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ServerMaximumSessionsExceededHandler;\nimport org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter;\nimport org.springframework.security.web.server.authentication.SessionLimit;\nimport org.springframework.security.web.server.authentication.WebFilterChainServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;\nimport org.springframework.security.web.server.authentication.logout.LogoutWebFilter;\nimport org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;\nimport org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver;\nimport org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter;\nimport org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver;\nimport org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.server.authorization.AuthorizationContext;\nimport org.springframework.security.web.server.authorization.AuthorizationWebFilter;\nimport org.springframework.security.web.server.authorization.DelegatingReactiveAuthorizationManager;\nimport org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter;\nimport org.springframework.security.web.server.authorization.IpAddressReactiveAuthorizationManager;\nimport org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;\nimport org.springframework.security.web.server.authorization.ServerWebExchangeDelegatingServerAccessDeniedHandler;\nimport org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.ReactorContextWebFilter;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.security.web.server.csrf.CsrfServerLogoutHandler;\nimport org.springframework.security.web.server.csrf.CsrfToken;\nimport org.springframework.security.web.server.csrf.CsrfWebFilter;\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRepository;\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRequestHandler;\nimport org.springframework.security.web.server.csrf.WebSessionServerCsrfTokenRepository;\nimport org.springframework.security.web.server.header.CacheControlServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.CompositeServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy;\nimport org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy;\nimport org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy;\nimport org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.HttpHeaderWriterWebFilter;\nimport org.springframework.security.web.server.header.PermissionsPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy;\nimport org.springframework.security.web.server.header.ServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter;\nimport org.springframework.security.web.server.savedrequest.NoOpServerRequestCache;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter;\nimport org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;\nimport org.springframework.security.web.server.transport.HttpsRedirectWebFilter;\nimport org.springframework.security.web.server.ui.DefaultResourcesWebFilter;\nimport org.springframework.security.web.server.ui.LoginPageGeneratingWebFilter;\nimport org.springframework.security.web.server.ui.LogoutPageGeneratingWebFilter;\nimport org.springframework.security.web.server.ui.OneTimeTokenSubmitPageGeneratingWebFilter;\nimport org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.MediaTypeServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcherEntry;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.cors.reactive.CorsConfigurationSource;\nimport org.springframework.web.cors.reactive.CorsProcessor;\nimport org.springframework.web.cors.reactive.CorsWebFilter;\nimport org.springframework.web.cors.reactive.DefaultCorsProcessor;\nimport org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.ServerWebExchangeDecorator;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.WebSession;\nimport org.springframework.web.server.adapter.WebHttpHandlerBuilder;\nimport org.springframework.web.server.session.DefaultWebSessionManager;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\n/**\n * A {@link ServerHttpSecurity} is similar to Spring Security's {@code HttpSecurity} but\n * for WebFlux. It allows configuring web based security for specific http requests. By\n * default it will be applied to all requests, but can be restricted using\n * {@link #securityMatcher(ServerWebExchangeMatcher)} or other similar methods.\n *\n * A minimal configuration can be found below:\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableWebFluxSecurity\n * public class MyMinimalSecurityConfiguration {\n *\n *     &#064;Bean\n *     public MapReactiveUserDetailsService userDetailsService() {\n *         UserDetails user = User.withDefaultPasswordEncoder()\n *             .username(\"user\")\n *             .password(\"password\")\n *             .roles(\"USER\")\n *             .build();\n *         return new MapReactiveUserDetailsService(user);\n *     }\n * }\n * </pre>\n *\n * Below is the same as our minimal configuration, but explicitly declaring the\n * {@code ServerHttpSecurity}.\n *\n * <pre class=\"code\">\n * &#064;Configuration\n * &#064;EnableWebFluxSecurity\n * public class MyExplicitSecurityConfiguration {\n *\n *     &#064;Bean\n *     public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n *         http\n *             .authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n *             .httpBasic(Customizer.withDefaults())\n *               .formLogin();\n *             return http.build();\n *     }\n *\n *     &#064;Bean\n *     public MapReactiveUserDetailsService userDetailsService() {\n *         UserDetails user = User.withDefaultPasswordEncoder()\n *             .username(\"user\")\n *             .password(\"password\")\n *             .roles(\"USER\")\n *             .build();\n *         return new MapReactiveUserDetailsService(user);\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @author Vedran Pavic\n * @author Rafiullah Hamedy\n * @author Eddú Meléndez\n * @author Joe Grandja\n * @author Parikshit Dutta\n * @author Ankur Pathak\n * @author Alexey Nesterov\n * @author Yanming Zhou\n * @since 5.0\n */\npublic class ServerHttpSecurity {\n\n\tprivate ServerWebExchangeMatcher securityMatcher = ServerWebExchangeMatchers.anyExchange();\n\n\tprivate AuthorizeExchangeSpec authorizeExchange;\n\n\tprivate HttpsRedirectSpec httpsRedirectSpec;\n\n\tprivate HeaderSpec headers = new HeaderSpec();\n\n\tprivate CsrfSpec csrf = new CsrfSpec();\n\n\tprivate CorsSpec cors = new CorsSpec();\n\n\tprivate ExceptionHandlingSpec exceptionHandling = new ExceptionHandlingSpec();\n\n\tprivate HttpBasicSpec httpBasic;\n\n\tprivate PasswordManagementSpec passwordManagement;\n\n\tprivate X509Spec x509;\n\n\tprivate final RequestCacheSpec requestCache = new RequestCacheSpec();\n\n\tprivate FormLoginSpec formLogin;\n\n\tprivate OAuth2LoginSpec oauth2Login;\n\n\tprivate OAuth2ResourceServerSpec resourceServer;\n\n\tprivate OAuth2ClientSpec client;\n\n\tprivate OidcLogoutSpec oidcLogout;\n\n\tprivate LogoutSpec logout = new LogoutSpec();\n\n\tprivate LoginPageSpec loginPage = new LoginPageSpec();\n\n\tprivate SessionManagementSpec sessionManagement;\n\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\tprivate ServerSecurityContextRepository securityContextRepository;\n\n\tprivate ServerAuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate List<DelegateEntry> defaultEntryPoints = new ArrayList<>();\n\n\tprivate ServerAccessDeniedHandler accessDeniedHandler;\n\n\tprivate List<ServerWebExchangeDelegatingServerAccessDeniedHandler.DelegateEntry> defaultAccessDeniedHandlers = new ArrayList<>();\n\n\tprivate List<WebFilter> webFilters = new ArrayList<>();\n\n\tprivate ApplicationContext context;\n\n\tprivate Throwable built;\n\n\tprivate AnonymousSpec anonymous;\n\n\tprivate OneTimeTokenLoginSpec oneTimeTokenLogin;\n\n\tprotected ServerHttpSecurity() {\n\t}\n\n\t/**\n\t * The ServerExchangeMatcher that determines which requests apply to this HttpSecurity\n\t * instance.\n\t * @param matcher the ServerExchangeMatcher that determines which requests apply to\n\t * this HttpSecurity instance. Default is all requests.\n\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t */\n\tpublic ServerHttpSecurity securityMatcher(ServerWebExchangeMatcher matcher) {\n\t\tAssert.notNull(matcher, \"matcher cannot be null\");\n\t\tthis.securityMatcher = matcher;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a {@link WebFilter} at a specific position.\n\t * @param webFilter the {@link WebFilter} to add\n\t * @param order the place to insert the {@link WebFilter}\n\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t */\n\tpublic ServerHttpSecurity addFilterAt(WebFilter webFilter, SecurityWebFiltersOrder order) {\n\t\tthis.webFilters.add(new OrderedWebFilter(webFilter, order.getOrder()));\n\t\treturn this;\n\t}\n\n\t/**\n\t *\n\t * Adds a {@link WebFilter} before specific position.\n\t * @param webFilter the {@link WebFilter} to add\n\t * @param order the place before which to insert the {@link WebFilter}\n\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t * @since 5.2.0\n\t */\n\tpublic ServerHttpSecurity addFilterBefore(WebFilter webFilter, SecurityWebFiltersOrder order) {\n\t\tthis.webFilters.add(new OrderedWebFilter(webFilter, order.getOrder() - 1));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a {@link WebFilter} after specific position.\n\t * @param webFilter the {@link WebFilter} to add\n\t * @param order the place after which to insert the {@link WebFilter}\n\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t * @since 5.2.0\n\t */\n\tpublic ServerHttpSecurity addFilterAfter(WebFilter webFilter, SecurityWebFiltersOrder order) {\n\t\tthis.webFilters.add(new OrderedWebFilter(webFilter, order.getOrder() + 1));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the ServerExchangeMatcher that determines which requests apply to this\n\t * HttpSecurity instance.\n\t * @return the ServerExchangeMatcher that determines which requests apply to this\n\t * HttpSecurity instance.\n\t */\n\tprivate ServerWebExchangeMatcher getSecurityMatcher() {\n\t\treturn this.securityMatcher;\n\t}\n\n\t/**\n\t * The strategy used with {@code ReactorContextWebFilter}. It does impact how the\n\t * {@code SecurityContext} is saved which is configured on a per\n\t * {@link AuthenticationWebFilter} basis.\n\t * @param securityContextRepository the repository to use\n\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t */\n\tpublic ServerHttpSecurity securityContextRepository(ServerSecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures HTTPS redirection rules. If the default is used:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t * \tpublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t * \t    http\n\t * \t        // ...\n\t * \t        .redirectToHttps(withDefaults());\n\t * \t    return http.build();\n\t * \t}\n\t * </pre>\n\t *\n\t * Then all non-HTTPS requests will be redirected to HTTPS.\n\t *\n\t * Typically, all requests should be HTTPS; however, the focus for redirection can\n\t * also be narrowed:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t * \tpublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t * \t    http\n\t * \t        // ...\n\t * \t        .redirectToHttps((redirectToHttps) -&gt;\n\t * \t        \tredirectToHttps\n\t * \t            \t.httpsRedirectWhen((serverWebExchange) -&gt;\n\t * \t            \t\tserverWebExchange.getRequest().getHeaders().containsKey(\"X-Requires-Https\"))\n\t * \t            );\n\t * \t    return http.build();\n\t * \t}\n\t * </pre>\n\t * @param httpsRedirectCustomizer the {@link Customizer} to provide more options for\n\t * the {@link HttpsRedirectSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity redirectToHttps(Customizer<HttpsRedirectSpec> httpsRedirectCustomizer) {\n\t\tthis.httpsRedirectSpec = new HttpsRedirectSpec();\n\t\thttpsRedirectCustomizer.customize(this.httpsRedirectSpec);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures <a href=\n\t * \"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet\">CSRF\n\t * Protection</a> which is enabled by default. You can disable it using:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .csrf((csrf) -&gt;\n\t *              csrf.disabled()\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t *\n\t * Additional configuration options can be seen below:\n\t *\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .csrf((csrf) -&gt;\n\t *              csrf\n\t *                  // Handle CSRF failures\n\t *                  .accessDeniedHandler(accessDeniedHandler)\n\t *                  // Custom persistence of CSRF Token\n\t *                  .csrfTokenRepository(csrfTokenRepository)\n\t *                  // custom matching when CSRF protection is enabled\n\t *                  .requireCsrfProtectionMatcher(matcher)\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param csrfCustomizer the {@link Customizer} to provide more options for the\n\t * {@link CsrfSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity csrf(Customizer<CsrfSpec> csrfCustomizer) {\n\t\tif (this.csrf == null) {\n\t\t\tthis.csrf = new CsrfSpec();\n\t\t}\n\t\tcsrfCustomizer.customize(this.csrf);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures CORS headers. By default if a {@link CorsConfigurationSource} Bean is\n\t * found, it will be used to create a {@link CorsWebFilter}. If\n\t * {@link CorsSpec#configurationSource(CorsConfigurationSource)} is invoked it will be\n\t * used instead. If neither has been configured, the Cors configuration will do\n\t * nothing.\n\t * @param corsCustomizer the {@link Customizer} to provide more options for the\n\t * {@link CorsSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity cors(Customizer<CorsSpec> corsCustomizer) {\n\t\tif (this.cors == null) {\n\t\t\tthis.cors = new CorsSpec();\n\t\t}\n\t\tcorsCustomizer.customize(this.cors);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Enables and Configures anonymous authentication. Anonymous Authentication is\n\t * disabled by default.\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .anonymous((anonymous) -&gt;\n\t *              anonymous\n\t *                  .key(\"key\")\n\t *                  .authorities(\"ROLE_ANONYMOUS\")\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param anonymousCustomizer the {@link Customizer} to provide more options for the\n\t * {@link AnonymousSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity anonymous(Customizer<AnonymousSpec> anonymousCustomizer) {\n\t\tif (this.anonymous == null) {\n\t\t\tthis.anonymous = new AnonymousSpec();\n\t\t}\n\t\tanonymousCustomizer.customize(this.anonymous);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures HTTP Basic authentication. An example configuration is provided below:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .httpBasic((httpBasic) -&gt;\n\t *              httpBasic\n\t *                  // used for authenticating the credentials\n\t *                  .authenticationManager(authenticationManager)\n\t *                  // Custom persistence of the authentication\n\t *                  .securityContextRepository(securityContextRepository)\n\t *              );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param httpBasicCustomizer the {@link Customizer} to provide more options for the\n\t * {@link HttpBasicSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity httpBasic(Customizer<HttpBasicSpec> httpBasicCustomizer) {\n\t\tif (this.httpBasic == null) {\n\t\t\tthis.httpBasic = new HttpBasicSpec();\n\t\t}\n\t\thttpBasicCustomizer.customize(this.httpBasic);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures Session Management. An example configuration is provided below:\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  SecurityWebFilterChain filterChain(ServerHttpSecurity http, ReactiveSessionRegistry sessionRegistry) {\n\t *      http\n\t *          // ...\n\t *          .sessionManagement((sessionManagement) -> sessionManagement\n\t *              .concurrentSessions((concurrentSessions) -> concurrentSessions\n\t *                  .maxSessions(1)\n\t *                  .maxSessionsPreventsLogin(true)\n\t *                  .sessionRegistry(sessionRegistry)\n\t *              )\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param customizer the {@link Customizer} to provide more options for the\n\t * {@link SessionManagementSpec}\n\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t * @since 6.3\n\t */\n\tpublic ServerHttpSecurity sessionManagement(Customizer<SessionManagementSpec> customizer) {\n\t\tif (this.sessionManagement == null) {\n\t\t\tthis.sessionManagement = new SessionManagementSpec();\n\t\t}\n\t\tcustomizer.customize(this.sessionManagement);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures password management. An example configuration is provided below:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .passwordManagement(passwordManagement -&gt;\n\t *          \t// Custom change password page.\n\t *          \tpasswordManagement.changePasswordPage(\"/custom-change-password-page\")\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param passwordManagementCustomizer the {@link Customizer} to provide more options\n\t * for the {@link PasswordManagementSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t * @since 5.6\n\t */\n\tpublic ServerHttpSecurity passwordManagement(Customizer<PasswordManagementSpec> passwordManagementCustomizer) {\n\t\tif (this.passwordManagement == null) {\n\t\t\tthis.passwordManagement = new PasswordManagementSpec();\n\t\t}\n\t\tpasswordManagementCustomizer.customize(this.passwordManagement);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures form based authentication. An example configuration is provided below:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .formLogin((formLogin) -&gt;\n\t *              formLogin\n\t *              \t// used for authenticating the credentials\n\t *              \t.authenticationManager(authenticationManager)\n\t *              \t// Custom persistence of the authentication\n\t *              \t.securityContextRepository(securityContextRepository)\n\t *              \t// expect a log in page at \"/authenticate\"\n\t *              \t// a POST \"/authenticate\" is where authentication occurs\n\t *              \t// error page at \"/authenticate?error\"\n\t *              \t.loginPage(\"/authenticate\")\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param formLoginCustomizer the {@link Customizer} to provide more options for the\n\t * {@link FormLoginSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity formLogin(Customizer<FormLoginSpec> formLoginCustomizer) {\n\t\tif (this.formLogin == null) {\n\t\t\tthis.formLogin = new FormLoginSpec();\n\t\t}\n\t\tformLoginCustomizer.customize(this.formLogin);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures x509 authentication using a certificate provided by a client.\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          .x509((x509) -&gt;\n\t *              x509\n\t *          \t    .authenticationManager(authenticationManager)\n\t *                  .principalExtractor(principalExtractor)\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t *\n\t * Note that if extractor is not specified, {@link SubjectX500PrincipalExtractor} will\n\t * be used. If authenticationManager is not specified,\n\t * {@link ReactivePreAuthenticatedAuthenticationManager} will be used.\n\t * @param x509Customizer the {@link Customizer} to provide more options for the\n\t * {@link X509Spec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t * @since 5.2\n\t */\n\tpublic ServerHttpSecurity x509(Customizer<X509Spec> x509Customizer) {\n\t\tif (this.x509 == null) {\n\t\t\tthis.x509 = new X509Spec();\n\t\t}\n\t\tx509Customizer.customize(this.x509);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0\n\t * Provider.\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .oauth2Login((oauth2Login) -&gt;\n\t *              oauth2Login\n\t *                  .authenticationConverter(authenticationConverter)\n\t *                  .authenticationManager(manager)\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param oauth2LoginCustomizer the {@link Customizer} to provide more options for the\n\t * {@link OAuth2LoginSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity oauth2Login(Customizer<OAuth2LoginSpec> oauth2LoginCustomizer) {\n\t\tif (this.oauth2Login == null) {\n\t\t\tthis.oauth2Login = new OAuth2LoginSpec();\n\t\t}\n\t\toauth2LoginCustomizer.customize(this.oauth2Login);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the OAuth2 client.\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .oauth2Client((oauth2Client) -&gt;\n\t *              oauth2Client\n\t *                  .clientRegistrationRepository(clientRegistrationRepository)\n\t *                  .authorizedClientRepository(authorizedClientRepository)\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param oauth2ClientCustomizer the {@link Customizer} to provide more options for\n\t * the {@link OAuth2ClientSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity oauth2Client(Customizer<OAuth2ClientSpec> oauth2ClientCustomizer) {\n\t\tif (this.client == null) {\n\t\t\tthis.client = new OAuth2ClientSpec();\n\t\t}\n\t\toauth2ClientCustomizer.customize(this.client);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures OAuth 2.0 Resource Server support.\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .oauth2ResourceServer((oauth2ResourceServer) -&gt;\n\t *              oauth2ResourceServer\n\t *                  .jwt((jwt) -&gt;\n\t *                      jwt\n\t *                          .publicKey(publicKey())\n\t *                  )\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param oauth2ResourceServerCustomizer the {@link Customizer} to provide more\n\t * options for the {@link OAuth2ResourceServerSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity oauth2ResourceServer(\n\t\t\tCustomizer<OAuth2ResourceServerSpec> oauth2ResourceServerCustomizer) {\n\t\tif (this.resourceServer == null) {\n\t\t\tthis.resourceServer = new OAuth2ResourceServerSpec();\n\t\t}\n\t\toauth2ResourceServerCustomizer.customize(this.resourceServer);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures OIDC Connect 1.0 Logout support.\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .oidcLogout((logout) -&gt; logout\n\t *              .backChannel(Customizer.withDefaults())\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param oidcLogoutCustomizer the {@link Customizer} to provide more options for the\n\t * {@link OidcLogoutSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t * @since 6.2\n\t */\n\tpublic ServerHttpSecurity oidcLogout(Customizer<OidcLogoutSpec> oidcLogoutCustomizer) {\n\t\tif (this.oidcLogout == null) {\n\t\t\tthis.oidcLogout = new OidcLogoutSpec();\n\t\t}\n\t\toidcLogoutCustomizer.customize(this.oidcLogout);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures HTTP Response Headers. The default headers are:\n\t *\n\t * <pre>\n\t * Cache-Control: no-cache, no-store, max-age=0, must-revalidate\n\t * Pragma: no-cache\n\t * Expires: 0\n\t * X-Content-Type-Options: nosniff\n\t * Strict-Transport-Security: max-age=31536000 ; includeSubDomains\n\t * X-Frame-Options: DENY\n\t * X-XSS-Protection: 0\n\t * </pre>\n\t *\n\t * such that \"Strict-Transport-Security\" is only added on secure requests.\n\t *\n\t * An example configuration is provided below:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .headers((headers) -&gt;\n\t *              headers\n\t *                  // customize frame options to be same origin\n\t *                  .frameOptions((frameOptions) -&gt;\n\t *                      frameOptions\n\t *                          .mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)\n\t *                   )\n\t *                  // disable cache control\n\t *                  .cache((cache) -&gt;\n\t *                      cache\n\t *                          .disable()\n\t *                  )\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param headerCustomizer the {@link Customizer} to provide more options for the\n\t * {@link HeaderSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity headers(Customizer<HeaderSpec> headerCustomizer) {\n\t\tif (this.headers == null) {\n\t\t\tthis.headers = new HeaderSpec();\n\t\t}\n\t\theaderCustomizer.customize(this.headers);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures exception handling (i.e. handles when authentication is requested). An\n\t * example configuration can be found below:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .exceptionHandling((exceptionHandling) -&gt;\n\t *              exceptionHandling\n\t *                  // customize how to request for authentication\n\t *                  .authenticationEntryPoint(entryPoint)\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param exceptionHandlingCustomizer the {@link Customizer} to provide more options\n\t * for the {@link ExceptionHandlingSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity exceptionHandling(Customizer<ExceptionHandlingSpec> exceptionHandlingCustomizer) {\n\t\tif (this.exceptionHandling == null) {\n\t\t\tthis.exceptionHandling = new ExceptionHandlingSpec();\n\t\t}\n\t\texceptionHandlingCustomizer.customize(this.exceptionHandling);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures authorization. An example configuration can be found below:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .authorizeExchange((exchanges) -&gt;\n\t *              exchanges\n\t *                  // any URL that starts with /admin/ requires the role \"ROLE_ADMIN\"\n\t *                  .pathMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t *                  // a POST to /users requires the role \"USER_POST\"\n\t *                  .pathMatchers(HttpMethod.POST, \"/users\").hasAuthority(\"USER_POST\")\n\t *                  // a request to /users/{username} requires the current authentication's username\n\t *                  // to be equal to the {username}\n\t *                  .pathMatchers(\"/users/{username}\").access((authentication, context) -&gt;\n\t *                      authentication\n\t *                          .map(Authentication::getName)\n\t *                          .map((username) -&gt; username.equals(context.getVariables().get(\"username\")))\n\t *                          .map(AuthorizationDecision::new)\n\t *                  )\n\t *                  // allows providing a custom matching strategy that requires the role \"ROLE_CUSTOM\"\n\t *                  .matchers(customMatcher).hasRole(\"CUSTOM\")\n\t *                  // any other request requires the user to be authenticated\n\t *                  .anyExchange().authenticated()\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param authorizeExchangeCustomizer the {@link Customizer} to provide more options\n\t * for the {@link AuthorizeExchangeSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity authorizeExchange(Customizer<AuthorizeExchangeSpec> authorizeExchangeCustomizer) {\n\t\tif (this.authorizeExchange == null) {\n\t\t\tthis.authorizeExchange = new AuthorizeExchangeSpec();\n\t\t}\n\t\tauthorizeExchangeCustomizer.customize(this.authorizeExchange);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures log out. An example configuration can be found below:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .logout((logout) -&gt;\n\t *              logout\n\t *                  // configures how log out is done\n\t *                  .logoutHandler(logoutHandler)\n\t *                  // log out will be performed on POST /signout\n\t *                  .logoutUrl(\"/signout\")\n\t *                  // configure what is done on logout success\n\t *                  .logoutSuccessHandler(successHandler)\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param logoutCustomizer the {@link Customizer} to provide more options for the\n\t * {@link LogoutSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity logout(Customizer<LogoutSpec> logoutCustomizer) {\n\t\tif (this.logout == null) {\n\t\t\tthis.logout = new LogoutSpec();\n\t\t}\n\t\tlogoutCustomizer.customize(this.logout);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures the request cache which is used when a flow is interrupted (i.e. due to\n\t * requesting credentials) so that the request can be replayed after authentication.\n\t * An example configuration can be found below:\n\t *\n\t * <pre class=\"code\">\n\t *  &#064;Bean\n\t *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t *      http\n\t *          // ...\n\t *          .requestCache((requestCache) -&gt;\n\t *              requestCache\n\t *                  // configures how the request is cached\n\t *                  .requestCache(customRequestCache)\n\t *          );\n\t *      return http.build();\n\t *  }\n\t * </pre>\n\t * @param requestCacheCustomizer the {@link Customizer} to provide more options for\n\t * the {@link RequestCacheSpec}\n\t * @return the {@link ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity requestCache(Customizer<RequestCacheSpec> requestCacheCustomizer) {\n\t\trequestCacheCustomizer.customize(this.requestCache);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configure the default authentication manager.\n\t * @param manager the authentication manager to use\n\t * @return the {@code ServerHttpSecurity} to customize\n\t */\n\tpublic ServerHttpSecurity authenticationManager(ReactiveAuthenticationManager manager) {\n\t\tthis.authenticationManager = manager;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configures One-Time Token Login Support.\n\t *\n\t * <h2>Example Configuration</h2>\n\t *\n\t * <pre>\n\t * &#064;Configuration\n\t * &#064;EnableWebFluxSecurity\n\t * public class SecurityConfig {\n\t *\n\t * \t&#064;Bean\n\t * \tpublic SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) throws Exception {\n\t * \t\thttp\n\t * \t\t\t// ...\n\t * \t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t * \t\treturn http.build();\n\t * \t}\n\t *\n\t * \t&#064;Bean\n\t * \tpublic ServerOneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler() {\n\t * \t\treturn new MyMagicLinkServerOneTimeTokenGenerationSuccessHandler();\n\t * \t}\n\t *\n\t * }\n\t * </pre>\n\t * @param oneTimeTokenLoginCustomizer the {@link Customizer} to provide more options\n\t * for the {@link OneTimeTokenLoginSpec}\n\t * @return the {@link ServerHttpSecurity} for further customizations\n\t */\n\tpublic ServerHttpSecurity oneTimeTokenLogin(Customizer<OneTimeTokenLoginSpec> oneTimeTokenLoginCustomizer) {\n\t\tif (this.oneTimeTokenLogin == null) {\n\t\t\tthis.oneTimeTokenLogin = new OneTimeTokenLoginSpec();\n\t\t}\n\t\toneTimeTokenLoginCustomizer.customize(this.oneTimeTokenLogin);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Builds the {@link SecurityWebFilterChain}\n\t * @return the {@link SecurityWebFilterChain}\n\t */\n\tpublic SecurityWebFilterChain build() {\n\t\tif (this.built != null) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"This has already been built with the following stacktrace. \" + buildToString());\n\t\t}\n\t\tthis.built = new RuntimeException(\"First Build Invocation\").fillInStackTrace();\n\t\tif (this.headers != null) {\n\t\t\tthis.headers.configure(this);\n\t\t}\n\t\tWebFilter securityContextRepositoryWebFilter = securityContextRepositoryWebFilter();\n\t\tthis.webFilters.add(securityContextRepositoryWebFilter);\n\t\tif (this.sessionManagement != null) {\n\t\t\tthis.sessionManagement.configure(this);\n\t\t}\n\t\tif (this.httpsRedirectSpec != null) {\n\t\t\tthis.httpsRedirectSpec.configure(this);\n\t\t}\n\t\tif (this.x509 != null) {\n\t\t\tthis.x509.configure(this);\n\t\t}\n\t\tif (this.csrf != null) {\n\t\t\tthis.csrf.configure(this);\n\t\t}\n\t\tif (this.cors != null) {\n\t\t\tthis.cors.configure(this);\n\t\t}\n\t\tif (this.httpBasic != null) {\n\t\t\tif (this.httpBasic.authenticationManager == null) {\n\t\t\t\tthis.httpBasic.authenticationManager(this.authenticationManager);\n\t\t\t}\n\t\t\tif (this.httpBasic.securityContextRepository != null) {\n\t\t\t\tthis.httpBasic.securityContextRepository(this.httpBasic.securityContextRepository);\n\t\t\t}\n\t\t\telse if (this.securityContextRepository != null) {\n\t\t\t\tthis.httpBasic.securityContextRepository(this.securityContextRepository);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.httpBasic.securityContextRepository(NoOpServerSecurityContextRepository.getInstance());\n\t\t\t}\n\t\t\tthis.httpBasic.configure(this);\n\t\t}\n\t\tif (this.passwordManagement != null) {\n\t\t\tthis.passwordManagement.configure(this);\n\t\t}\n\t\tif (this.formLogin != null) {\n\t\t\tif (this.formLogin.authenticationManager == null) {\n\t\t\t\tthis.formLogin.authenticationManager(this.authenticationManager);\n\t\t\t}\n\t\t\tif (this.formLogin.securityContextRepository != null) {\n\t\t\t\tthis.formLogin.securityContextRepository(this.formLogin.securityContextRepository);\n\t\t\t}\n\t\t\telse if (this.securityContextRepository != null) {\n\t\t\t\tthis.formLogin.securityContextRepository(this.securityContextRepository);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.formLogin.securityContextRepository(new WebSessionServerSecurityContextRepository());\n\t\t\t}\n\t\t\tthis.formLogin.configure(this);\n\t\t}\n\t\tif (this.oauth2Login != null) {\n\t\t\tif (this.oauth2Login.securityContextRepository != null) {\n\t\t\t\tthis.oauth2Login.securityContextRepository(this.oauth2Login.securityContextRepository);\n\t\t\t}\n\t\t\telse if (this.securityContextRepository != null) {\n\t\t\t\tthis.oauth2Login.securityContextRepository(this.securityContextRepository);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.oauth2Login.securityContextRepository(new WebSessionServerSecurityContextRepository());\n\t\t\t}\n\t\t\tthis.oauth2Login.configure(this);\n\t\t}\n\t\tif (this.resourceServer != null) {\n\t\t\tthis.resourceServer.configure(this);\n\t\t}\n\t\tif (this.oidcLogout != null) {\n\t\t\tthis.oidcLogout.configure(this);\n\t\t}\n\t\tif (this.client != null) {\n\t\t\tthis.client.configure(this);\n\t\t}\n\t\tif (this.anonymous != null) {\n\t\t\tthis.anonymous.configure(this);\n\t\t}\n\t\tthis.loginPage.configure(this);\n\t\tif (this.logout != null) {\n\t\t\tthis.logout.configure(this);\n\t\t}\n\t\tthis.requestCache.configure(this);\n\t\tif (this.oneTimeTokenLogin != null) {\n\t\t\tif (this.oneTimeTokenLogin.securityContextRepository != null) {\n\t\t\t\tthis.oneTimeTokenLogin.securityContextRepository(this.oneTimeTokenLogin.securityContextRepository);\n\t\t\t}\n\t\t\telse if (this.securityContextRepository != null) {\n\t\t\t\tthis.oneTimeTokenLogin.securityContextRepository(this.securityContextRepository);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.oneTimeTokenLogin.securityContextRepository(new WebSessionServerSecurityContextRepository());\n\t\t\t}\n\t\t\tthis.oneTimeTokenLogin.configure(this);\n\t\t}\n\t\tthis.addFilterAt(new SecurityContextServerWebExchangeWebFilter(),\n\t\t\t\tSecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE);\n\t\tif (this.authorizeExchange != null) {\n\t\t\tServerAuthenticationEntryPoint authenticationEntryPoint = getAuthenticationEntryPoint();\n\t\t\tExceptionTranslationWebFilter exceptionTranslationWebFilter = new ExceptionTranslationWebFilter();\n\t\t\tif (authenticationEntryPoint != null) {\n\t\t\t\texceptionTranslationWebFilter.setAuthenticationEntryPoint(authenticationEntryPoint);\n\t\t\t}\n\t\t\tServerAccessDeniedHandler accessDeniedHandler = getAccessDeniedHandler();\n\t\t\tif (accessDeniedHandler != null) {\n\t\t\t\texceptionTranslationWebFilter.setAccessDeniedHandler(accessDeniedHandler);\n\t\t\t}\n\t\t\tthis.addFilterAt(exceptionTranslationWebFilter, SecurityWebFiltersOrder.EXCEPTION_TRANSLATION);\n\t\t\tthis.authorizeExchange.configure(this);\n\t\t}\n\t\tAnnotationAwareOrderComparator.sort(this.webFilters);\n\t\tList<WebFilter> sortedWebFilters = new ArrayList<>();\n\t\tthis.webFilters.forEach((f) -> {\n\t\t\tif (f instanceof OrderedWebFilter) {\n\t\t\t\tf = ((OrderedWebFilter) f).webFilter;\n\t\t\t}\n\t\t\tsortedWebFilters.add(f);\n\t\t});\n\t\tsortedWebFilters.add(0, new ServerWebExchangeReactorContextWebFilter());\n\t\treturn new MatcherSecurityWebFilterChain(getSecurityMatcher(), sortedWebFilters);\n\t}\n\n\tprivate String buildToString() {\n\t\ttry (StringWriter writer = new StringWriter()) {\n\t\t\ttry (PrintWriter printer = new PrintWriter(writer)) {\n\t\t\t\tprinter.println();\n\t\t\t\tprinter.println();\n\t\t\t\tthis.built.printStackTrace(printer);\n\t\t\t\tprinter.println();\n\t\t\t\tprinter.println();\n\t\t\t\treturn writer.toString();\n\t\t\t}\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprivate ServerAuthenticationEntryPoint getAuthenticationEntryPoint() {\n\t\tif (this.authenticationEntryPoint != null || this.defaultEntryPoints.isEmpty()) {\n\t\t\treturn this.authenticationEntryPoint;\n\t\t}\n\t\tif (this.defaultEntryPoints.size() == 1) {\n\t\t\treturn this.defaultEntryPoints.get(0).getEntryPoint();\n\t\t}\n\t\tDelegatingServerAuthenticationEntryPoint result = new DelegatingServerAuthenticationEntryPoint(\n\t\t\t\tthis.defaultEntryPoints);\n\t\tresult.setDefaultEntryPoint(this.defaultEntryPoints.get(this.defaultEntryPoints.size() - 1).getEntryPoint());\n\t\treturn result;\n\t}\n\n\tprivate ServerAccessDeniedHandler getAccessDeniedHandler() {\n\t\tif (this.accessDeniedHandler != null || this.defaultAccessDeniedHandlers.isEmpty()) {\n\t\t\treturn this.accessDeniedHandler;\n\t\t}\n\t\tif (this.defaultAccessDeniedHandlers.size() == 1) {\n\t\t\treturn this.defaultAccessDeniedHandlers.get(0).getAccessDeniedHandler();\n\t\t}\n\t\tServerWebExchangeDelegatingServerAccessDeniedHandler result = new ServerWebExchangeDelegatingServerAccessDeniedHandler(\n\t\t\t\tthis.defaultAccessDeniedHandlers);\n\t\tresult.setDefaultAccessDeniedHandler(\n\t\t\t\tthis.defaultAccessDeniedHandlers.get(this.defaultAccessDeniedHandlers.size() - 1)\n\t\t\t\t\t.getAccessDeniedHandler());\n\t\treturn result;\n\t}\n\n\t/**\n\t * Creates a new instance.\n\t * @return the new {@link ServerHttpSecurity} instance\n\t */\n\tpublic static ServerHttpSecurity http() {\n\t\treturn new ServerHttpSecurity();\n\t}\n\n\tprivate WebFilter securityContextRepositoryWebFilter() {\n\t\tServerSecurityContextRepository repository = (this.securityContextRepository != null)\n\t\t\t\t? this.securityContextRepository : new WebSessionServerSecurityContextRepository();\n\t\tWebFilter result = new ReactorContextWebFilter(repository);\n\t\treturn new OrderedWebFilter(result, SecurityWebFiltersOrder.REACTOR_CONTEXT.getOrder());\n\t}\n\n\tprivate <T> T getBean(Class<T> beanClass) {\n\t\tif (this.context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this.context.getBean(beanClass);\n\t}\n\n\tprivate <T> T getBeanOrDefault(Class<T> beanClass, T defaultInstance) {\n\t\tif (this.context == null) {\n\t\t\treturn defaultInstance;\n\t\t}\n\t\treturn this.context.getBeanProvider(beanClass).getIfUnique(() -> defaultInstance);\n\t}\n\n\tprivate <T> ObjectProvider<T> getBeanProvider(ResolvableType type) {\n\t\tif (this.context == null) {\n\t\t\treturn new ObjectProvider<>() {\n\t\t\t\t@Override\n\t\t\t\tpublic Iterator<T> iterator() {\n\t\t\t\t\treturn Collections.emptyIterator();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\treturn this.context.getBeanProvider(type);\n\t}\n\n\tprivate <T> T getBeanOrNull(Class<T> beanClass) {\n\t\treturn getBeanOrNull(ResolvableType.forClass(beanClass));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T> T getBeanOrNull(ResolvableType type) {\n\t\tif (this.context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (T) this.context.getBeanProvider(type).getIfUnique();\n\t}\n\n\tprivate <T> T getBeanOrNull(String beanName, Class<T> requiredClass) {\n\t\tif (this.context == null) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\treturn this.context.getBean(beanName, requiredClass);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate <T> String[] getBeanNamesForTypeOrEmpty(Class<T> beanClass) {\n\t\tif (this.context == null) {\n\t\t\treturn new String[0];\n\t\t}\n\t\treturn this.context.getBeanNamesForType(beanClass);\n\t}\n\n\tApplicationContext getApplicationContext() {\n\t\treturn this.context;\n\t}\n\n\tprotected void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.context = applicationContext;\n\t}\n\n\t/**\n\t * Configures authorization\n\t *\n\t * @author Rob Winch\n\t * @since 5.0\n\t * @see #authorizeExchange(Customizer)\n\t */\n\tpublic class AuthorizeExchangeSpec extends AbstractServerWebExchangeMatcherRegistry<AuthorizeExchangeSpec.Access> {\n\n\t\tprivate static final String REQUEST_MAPPING_HANDLER_MAPPING_BEAN_NAME = \"requestMappingHandlerMapping\";\n\n\t\tprivate DelegatingReactiveAuthorizationManager.Builder managerBldr = DelegatingReactiveAuthorizationManager\n\t\t\t.builder();\n\n\t\tprivate ServerWebExchangeMatcher matcher;\n\n\t\tprivate boolean anyExchangeRegistered;\n\n\t\tprivate PathPatternParser pathPatternParser;\n\n\t\tprivate ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>> postProcessor = ObjectPostProcessor\n\t\t\t.identity();\n\n\t\tpublic AuthorizeExchangeSpec() {\n\t\t\tResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,\n\t\t\t\t\tResolvableType.forClassWithGenerics(ReactiveAuthorizationManager.class, ServerWebExchange.class));\n\t\t\tObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<ServerWebExchange>>> postProcessor = getBeanProvider(\n\t\t\t\t\ttype);\n\t\t\tpostProcessor.ifUnique((p) -> this.postProcessor = p);\n\t\t}\n\n\t\t/**\n\t\t * Disables authorization.\n\t\t * @return the {@link Access} to continue configuring\n\t\t */\n\t\t@Override\n\t\tpublic Access anyExchange() {\n\t\t\tAccess result = super.anyExchange();\n\t\t\tthis.anyExchangeRegistered = true;\n\t\t\treturn result;\n\t\t}\n\n\t\t@Override\n\t\tprotected PathPatternParser getPathPatternParser() {\n\t\t\tif (this.pathPatternParser != null) {\n\t\t\t\treturn this.pathPatternParser;\n\t\t\t}\n\t\t\tRequestMappingHandlerMapping requestMappingHandlerMapping = getBeanOrNull(\n\t\t\t\t\tREQUEST_MAPPING_HANDLER_MAPPING_BEAN_NAME, RequestMappingHandlerMapping.class);\n\t\t\tif (requestMappingHandlerMapping != null) {\n\t\t\t\tthis.pathPatternParser = requestMappingHandlerMapping.getPathPatternParser();\n\t\t\t}\n\t\t\tif (this.pathPatternParser == null) {\n\t\t\t\tthis.pathPatternParser = PathPatternParser.defaultInstance;\n\t\t\t}\n\t\t\treturn this.pathPatternParser;\n\t\t}\n\n\t\t@Override\n\t\tprotected Access registerMatcher(ServerWebExchangeMatcher matcher) {\n\t\t\tAssert.state(!this.anyExchangeRegistered, () -> \"Cannot register \" + matcher\n\t\t\t\t\t+ \" which would be unreachable because anyExchange() has already been registered.\");\n\t\t\tAssert.state(this.matcher == null,\n\t\t\t\t\t() -> \"The matcher \" + matcher + \" does not have an access rule defined\");\n\t\t\tthis.matcher = matcher;\n\t\t\treturn new Access();\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tAssert.state(this.matcher == null,\n\t\t\t\t\t() -> \"The matcher \" + this.matcher + \" does not have an access rule defined\");\n\t\t\tReactiveAuthorizationManager<ServerWebExchange> manager = this.managerBldr.build();\n\t\t\tmanager = this.postProcessor.postProcess(manager);\n\t\t\tAuthorizationWebFilter result = new AuthorizationWebFilter(manager);\n\t\t\thttp.addFilterAt(result, SecurityWebFiltersOrder.AUTHORIZATION);\n\t\t}\n\n\t\t/**\n\t\t * Configures the access for a particular set of exchanges.\n\t\t */\n\t\tpublic final class Access {\n\n\t\t\t/**\n\t\t\t * Allow access for anyone\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec permitAll() {\n\t\t\t\treturn access((a, e) -> Mono.just(new AuthorizationDecision(true)));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Deny access for everyone\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec denyAll() {\n\t\t\t\treturn access((a, e) -> Mono.just(new AuthorizationDecision(false)));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Require a specific role. This is a shorcut for\n\t\t\t * {@link #hasAuthority(String)}\n\t\t\t * @param role the role (i.e. \"USER\" would require \"ROLE_USER\")\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec hasRole(String role) {\n\t\t\t\treturn access(AuthorityReactiveAuthorizationManager.hasRole(role));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Require any specific role. This is a shortcut for\n\t\t\t * {@link #hasAnyAuthority(String...)}\n\t\t\t * @param roles the roles (i.e. \"USER\" would require \"ROLE_USER\")\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec hasAnyRole(String... roles) {\n\t\t\t\treturn access(AuthorityReactiveAuthorizationManager.hasAnyRole(roles));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Require a specific authority.\n\t\t\t * @param authority the authority to require (i.e. \"USER\" would require\n\t\t\t * authority of \"USER\").\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec hasAuthority(String authority) {\n\t\t\t\treturn access(AuthorityReactiveAuthorizationManager.hasAuthority(authority));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Require any authority\n\t\t\t * @param authorities the authorities to require (i.e. \"USER\" would require\n\t\t\t * authority of \"USER\").\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec hasAnyAuthority(String... authorities) {\n\t\t\t\treturn access(AuthorityReactiveAuthorizationManager.hasAnyAuthority(authorities));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Require an authenticated user\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec authenticated() {\n\t\t\t\treturn access(AuthenticatedReactiveAuthorizationManager.authenticated());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Require a specific IP address or range using an IP/Netmask (e.g.\n\t\t\t * 192.168.1.0/24).\n\t\t\t * @param ipAddress the address or range of addresses from which the request\n\t\t\t * must come.\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t * @since 5.7\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec hasIpAddress(String ipAddress) {\n\t\t\t\treturn access(IpAddressReactiveAuthorizationManager.hasIpAddress(ipAddress));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Allows plugging in a custom authorization strategy\n\t\t\t * @param manager the authorization manager to use\n\t\t\t * @return the {@link AuthorizeExchangeSpec} to configure\n\t\t\t */\n\t\t\tpublic AuthorizeExchangeSpec access(ReactiveAuthorizationManager<AuthorizationContext> manager) {\n\t\t\t\tAuthorizeExchangeSpec.this.managerBldr\n\t\t\t\t\t.add(new ServerWebExchangeMatcherEntry<>(AuthorizeExchangeSpec.this.matcher, manager));\n\t\t\t\tAuthorizeExchangeSpec.this.matcher = null;\n\t\t\t\treturn AuthorizeExchangeSpec.this;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures how sessions are managed.\n\t */\n\tpublic class SessionManagementSpec {\n\n\t\tprivate ConcurrentSessionsSpec concurrentSessions;\n\n\t\tprivate ServerAuthenticationSuccessHandler authenticationSuccessHandler;\n\n\t\tprivate ReactiveSessionRegistry sessionRegistry;\n\n\t\tprivate SessionLimit sessionLimit = SessionLimit.UNLIMITED;\n\n\t\tprivate ServerMaximumSessionsExceededHandler maximumSessionsExceededHandler;\n\n\t\t/**\n\t\t * Configures how many sessions are allowed for a given user.\n\t\t * @param customizer the customizer to provide more options\n\t\t * @return the {@link SessionManagementSpec} to customize\n\t\t */\n\t\tpublic SessionManagementSpec concurrentSessions(Customizer<ConcurrentSessionsSpec> customizer) {\n\t\t\tif (this.concurrentSessions == null) {\n\t\t\t\tthis.concurrentSessions = new ConcurrentSessionsSpec();\n\t\t\t}\n\t\t\tcustomizer.customize(this.concurrentSessions);\n\t\t\treturn this;\n\t\t}\n\n\t\tvoid configure(ServerHttpSecurity http) {\n\t\t\tif (this.concurrentSessions != null) {\n\t\t\t\tReactiveSessionRegistry reactiveSessionRegistry = getSessionRegistry();\n\t\t\t\tConcurrentSessionControlServerAuthenticationSuccessHandler concurrentSessionControlStrategy = new ConcurrentSessionControlServerAuthenticationSuccessHandler(\n\t\t\t\t\t\treactiveSessionRegistry, getMaximumSessionsExceededHandler());\n\t\t\t\tconcurrentSessionControlStrategy.setSessionLimit(this.sessionLimit);\n\t\t\t\tRegisterSessionServerAuthenticationSuccessHandler registerSessionAuthenticationStrategy = new RegisterSessionServerAuthenticationSuccessHandler(\n\t\t\t\t\t\treactiveSessionRegistry);\n\t\t\t\tthis.authenticationSuccessHandler = new DelegatingServerAuthenticationSuccessHandler(\n\t\t\t\t\t\tconcurrentSessionControlStrategy, registerSessionAuthenticationStrategy);\n\t\t\t\tSessionRegistryWebFilter sessionRegistryWebFilter = new SessionRegistryWebFilter(\n\t\t\t\t\t\treactiveSessionRegistry);\n\t\t\t\tconfigureSuccessHandlerOnAuthenticationFilters();\n\t\t\t\thttp.addFilterAfter(sessionRegistryWebFilter, SecurityWebFiltersOrder.HTTP_HEADERS_WRITER);\n\t\t\t}\n\t\t}\n\n\t\tprivate ServerMaximumSessionsExceededHandler getMaximumSessionsExceededHandler() {\n\t\t\tif (this.maximumSessionsExceededHandler != null) {\n\t\t\t\treturn this.maximumSessionsExceededHandler;\n\t\t\t}\n\t\t\tDefaultWebSessionManager webSessionManager = getBeanOrNull(\n\t\t\t\t\tWebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME, DefaultWebSessionManager.class);\n\t\t\tif (webSessionManager != null) {\n\t\t\t\tthis.maximumSessionsExceededHandler = new InvalidateLeastUsedServerMaximumSessionsExceededHandler(\n\t\t\t\t\t\twebSessionManager.getSessionStore());\n\t\t\t}\n\t\t\tif (this.maximumSessionsExceededHandler == null) {\n\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\"Could not create a default ServerMaximumSessionsExceededHandler. Please provide \"\n\t\t\t\t\t\t\t\t+ \"a ServerMaximumSessionsExceededHandler via DSL\");\n\t\t\t}\n\t\t\treturn this.maximumSessionsExceededHandler;\n\t\t}\n\n\t\tprivate void configureSuccessHandlerOnAuthenticationFilters() {\n\t\t\tif (ServerHttpSecurity.this.formLogin != null) {\n\t\t\t\tServerHttpSecurity.this.formLogin.defaultSuccessHandlers.add(0, this.authenticationSuccessHandler);\n\t\t\t}\n\t\t\tif (ServerHttpSecurity.this.oauth2Login != null) {\n\t\t\t\tServerHttpSecurity.this.oauth2Login.defaultSuccessHandlers.add(0, this.authenticationSuccessHandler);\n\t\t\t}\n\t\t\tif (ServerHttpSecurity.this.httpBasic != null) {\n\t\t\t\tServerHttpSecurity.this.httpBasic.defaultSuccessHandlers.add(0, this.authenticationSuccessHandler);\n\t\t\t}\n\t\t}\n\n\t\tprivate ReactiveSessionRegistry getSessionRegistry() {\n\t\t\tif (this.sessionRegistry == null) {\n\t\t\t\tthis.sessionRegistry = getBeanOrNull(ReactiveSessionRegistry.class);\n\t\t\t}\n\t\t\tif (this.sessionRegistry == null) {\n\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\"A ReactiveSessionRegistry is needed for concurrent session management\");\n\t\t\t}\n\t\t\treturn this.sessionRegistry;\n\t\t}\n\n\t\t/**\n\t\t * Configures how many sessions are allowed for a given user.\n\t\t */\n\t\tpublic class ConcurrentSessionsSpec {\n\n\t\t\t/**\n\t\t\t * Sets the {@link ReactiveSessionRegistry} to use.\n\t\t\t * @param reactiveSessionRegistry the {@link ReactiveSessionRegistry} to use\n\t\t\t * @return the {@link ConcurrentSessionsSpec} to continue customizing\n\t\t\t */\n\t\t\tpublic ConcurrentSessionsSpec sessionRegistry(ReactiveSessionRegistry reactiveSessionRegistry) {\n\t\t\t\tSessionManagementSpec.this.sessionRegistry = reactiveSessionRegistry;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the maximum number of sessions allowed for any user. You can use\n\t\t\t * {@link SessionLimit#of(int)} to specify a positive integer or\n\t\t\t * {@link SessionLimit#UNLIMITED} to allow unlimited sessions. To customize\n\t\t\t * the maximum number of sessions on a per-user basis, you can provide a\n\t\t\t * custom {@link SessionLimit} implementation, like so: <pre>\n\t\t\t *     http\n\t\t\t *         .sessionManagement((sessions) -> sessions\n\t\t\t *             .concurrentSessions((concurrency) -> concurrency\n\t\t\t *                 .maximumSessions((authentication) -> {\n\t\t\t *                     if (authentication.getName().equals(\"admin\")) {\n\t\t\t *                         return Mono.empty() // unlimited sessions for admin\n\t\t\t *                     }\n\t\t\t *                     return Mono.just(1); // one session for every other user\n\t\t\t *                 })\n\t\t\t *             )\n\t\t\t *         )\n\t\t\t * </pre>\n\t\t\t * @param sessionLimit the maximum number of sessions allowed for any user\n\t\t\t * @return the {@link ConcurrentSessionsSpec} to continue customizing\n\t\t\t */\n\t\t\tpublic ConcurrentSessionsSpec maximumSessions(SessionLimit sessionLimit) {\n\t\t\t\tAssert.notNull(sessionLimit, \"sessionLimit cannot be null\");\n\t\t\t\tSessionManagementSpec.this.sessionLimit = sessionLimit;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the {@link ServerMaximumSessionsExceededHandler} to use when the\n\t\t\t * maximum number of sessions is exceeded.\n\t\t\t * @param maximumSessionsExceededHandler the\n\t\t\t * {@link ServerMaximumSessionsExceededHandler} to use\n\t\t\t * @return the {@link ConcurrentSessionsSpec} to continue customizing\n\t\t\t */\n\t\t\tpublic ConcurrentSessionsSpec maximumSessionsExceededHandler(\n\t\t\t\t\tServerMaximumSessionsExceededHandler maximumSessionsExceededHandler) {\n\t\t\t\tAssert.notNull(maximumSessionsExceededHandler, \"maximumSessionsExceededHandler cannot be null\");\n\t\t\t\tSessionManagementSpec.this.maximumSessionsExceededHandler = maximumSessionsExceededHandler;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t}\n\n\t\tprivate static final class SessionRegistryWebFilter implements WebFilter {\n\n\t\t\tprivate final ReactiveSessionRegistry sessionRegistry;\n\n\t\t\tprivate SessionRegistryWebFilter(ReactiveSessionRegistry sessionRegistry) {\n\t\t\t\tAssert.notNull(sessionRegistry, \"sessionRegistry cannot be null\");\n\t\t\t\tthis.sessionRegistry = sessionRegistry;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\t\treturn chain.filter(new SessionRegistryWebExchange(exchange));\n\t\t\t}\n\n\t\t\tprivate final class SessionRegistryWebExchange extends ServerWebExchangeDecorator {\n\n\t\t\t\tprivate final Mono<WebSession> sessionMono;\n\n\t\t\t\tprivate SessionRegistryWebExchange(ServerWebExchange delegate) {\n\t\t\t\t\tsuper(delegate);\n\t\t\t\t\tthis.sessionMono = delegate.getSession()\n\t\t\t\t\t\t.flatMap((session) -> SessionRegistryWebFilter.this.sessionRegistry\n\t\t\t\t\t\t\t.updateLastAccessTime(session.getId())\n\t\t\t\t\t\t\t.thenReturn(session))\n\t\t\t\t\t\t.map(SessionRegistryWebSession::new);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Mono<WebSession> getSession() {\n\t\t\t\t\treturn this.sessionMono;\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tprivate final class SessionRegistryWebSession implements WebSession {\n\n\t\t\t\tprivate final WebSession session;\n\n\t\t\t\tprivate SessionRegistryWebSession(WebSession session) {\n\t\t\t\t\tthis.session = session;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String getId() {\n\t\t\t\t\treturn this.session.getId();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Map<String, Object> getAttributes() {\n\t\t\t\t\treturn this.session.getAttributes();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void start() {\n\t\t\t\t\tthis.session.start();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean isStarted() {\n\t\t\t\t\treturn this.session.isStarted();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Mono<Void> changeSessionId() {\n\t\t\t\t\tString currentId = this.session.getId();\n\t\t\t\t\treturn this.session.changeSessionId()\n\t\t\t\t\t\t.then(Mono.defer(\n\t\t\t\t\t\t\t\t() -> SessionRegistryWebFilter.this.sessionRegistry.removeSessionInformation(currentId)\n\t\t\t\t\t\t\t\t\t.flatMap((information) -> {\n\t\t\t\t\t\t\t\t\t\tinformation = information.withSessionId(this.session.getId());\n\t\t\t\t\t\t\t\t\t\treturn SessionRegistryWebFilter.this.sessionRegistry\n\t\t\t\t\t\t\t\t\t\t\t.saveSessionInformation(information);\n\t\t\t\t\t\t\t\t\t})));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Mono<Void> invalidate() {\n\t\t\t\t\tString currentId = this.session.getId();\n\t\t\t\t\treturn this.session.invalidate()\n\t\t\t\t\t\t.then(Mono.defer(() -> SessionRegistryWebFilter.this.sessionRegistry\n\t\t\t\t\t\t\t.removeSessionInformation(currentId)))\n\t\t\t\t\t\t.then();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Mono<Void> save() {\n\t\t\t\t\treturn this.session.save();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean isExpired() {\n\t\t\t\t\treturn this.session.isExpired();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Instant getCreationTime() {\n\t\t\t\t\treturn this.session.getCreationTime();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Instant getLastAccessTime() {\n\t\t\t\t\treturn this.session.getLastAccessTime();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void setMaxIdleTime(Duration maxIdleTime) {\n\t\t\t\t\tthis.session.setMaxIdleTime(maxIdleTime);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Duration getMaxIdleTime() {\n\t\t\t\t\treturn this.session.getMaxIdleTime();\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures HTTPS redirection rules\n\t *\n\t * @author Josh Cummings\n\t * @since 5.1\n\t * @see #redirectToHttps(Customizer)\n\t */\n\tpublic class HttpsRedirectSpec {\n\n\t\tprivate ServerWebExchangeMatcher serverWebExchangeMatcher;\n\n\t\tprivate PortMapper portMapper;\n\n\t\t/**\n\t\t * Configures when this filter should redirect to https\n\t\t *\n\t\t * By default, the filter will redirect whenever an exchange's scheme is not https\n\t\t * @param matchers the list of conditions that, when any are met, the filter\n\t\t * should redirect to https\n\t\t * @return the {@link HttpsRedirectSpec} for additional configuration\n\t\t */\n\t\tpublic HttpsRedirectSpec httpsRedirectWhen(ServerWebExchangeMatcher... matchers) {\n\t\t\tthis.serverWebExchangeMatcher = new OrServerWebExchangeMatcher(matchers);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures when this filter should redirect to https\n\t\t *\n\t\t * By default, the filter will redirect whenever an exchange's scheme is not https\n\t\t * @param when determines when to redirect to https\n\t\t * @return the {@link HttpsRedirectSpec} for additional configuration\n\t\t */\n\t\tpublic HttpsRedirectSpec httpsRedirectWhen(Function<ServerWebExchange, Boolean> when) {\n\t\t\tServerWebExchangeMatcher matcher = (e) -> when.apply(e) ? ServerWebExchangeMatcher.MatchResult.match()\n\t\t\t\t\t: ServerWebExchangeMatcher.MatchResult.notMatch();\n\t\t\treturn httpsRedirectWhen(matcher);\n\t\t}\n\n\t\t/**\n\t\t * Configures a custom HTTPS port to redirect to\n\t\t * @param portMapper the {@link PortMapper} to use\n\t\t * @return the {@link HttpsRedirectSpec} for additional configuration\n\t\t */\n\t\tpublic HttpsRedirectSpec portMapper(PortMapper portMapper) {\n\t\t\tthis.portMapper = portMapper;\n\t\t\treturn this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tHttpsRedirectWebFilter httpsRedirectWebFilter = new HttpsRedirectWebFilter();\n\t\t\tif (this.serverWebExchangeMatcher != null) {\n\t\t\t\thttpsRedirectWebFilter.setRequiresHttpsRedirectMatcher(this.serverWebExchangeMatcher);\n\t\t\t}\n\t\t\tif (this.portMapper != null) {\n\t\t\t\thttpsRedirectWebFilter.setPortMapper(this.portMapper);\n\t\t\t}\n\t\t\thttp.addFilterAt(httpsRedirectWebFilter, SecurityWebFiltersOrder.HTTPS_REDIRECT);\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures <a href=\n\t * \"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet\">CSRF\n\t * Protection</a>\n\t *\n\t * @author Rob Winch\n\t * @since 5.0\n\t * @see #csrf(Customizer)\n\t */\n\tpublic final class CsrfSpec {\n\n\t\tprivate CsrfSpec() {\n\t\t}\n\n\t\tprivate CsrfWebFilter filter = new CsrfWebFilter();\n\n\t\tprivate ServerCsrfTokenRepository csrfTokenRepository = new WebSessionServerCsrfTokenRepository();\n\n\t\tprivate boolean specifiedRequireCsrfProtectionMatcher;\n\n\t\t/**\n\t\t * Configures the {@link ServerAccessDeniedHandler} used when a CSRF token is\n\t\t * invalid. Default is to send an\n\t\t * {@link org.springframework.http.HttpStatus#FORBIDDEN}.\n\t\t * @param accessDeniedHandler the access denied handler.\n\t\t * @return the {@link CsrfSpec} for additional configuration\n\t\t */\n\t\tpublic CsrfSpec accessDeniedHandler(ServerAccessDeniedHandler accessDeniedHandler) {\n\t\t\tthis.filter.setAccessDeniedHandler(accessDeniedHandler);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ServerCsrfTokenRepository} used to persist the CSRF\n\t\t * Token. Default is\n\t\t * {@link org.springframework.security.web.server.csrf.WebSessionServerCsrfTokenRepository}.\n\t\t * @param csrfTokenRepository the repository to use\n\t\t * @return the {@link CsrfSpec} for additional configuration\n\t\t */\n\t\tpublic CsrfSpec csrfTokenRepository(ServerCsrfTokenRepository csrfTokenRepository) {\n\t\t\tthis.csrfTokenRepository = csrfTokenRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ServerWebExchangeMatcher} used to determine when CSRF\n\t\t * protection is enabled. Default is PUT, POST, DELETE requests.\n\t\t * @param requireCsrfProtectionMatcher the matcher to use\n\t\t * @return the {@link CsrfSpec} for additional configuration\n\t\t */\n\t\tpublic CsrfSpec requireCsrfProtectionMatcher(ServerWebExchangeMatcher requireCsrfProtectionMatcher) {\n\t\t\tthis.filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);\n\t\t\tthis.specifiedRequireCsrfProtectionMatcher = true;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies a {@link ServerCsrfTokenRequestHandler} that is used to make the\n\t\t * {@code CsrfToken} available as an exchange attribute.\n\t\t * @param requestHandler the {@link ServerCsrfTokenRequestHandler} to use\n\t\t * @return the {@link CsrfSpec} for additional configuration\n\t\t * @since 5.8\n\t\t */\n\t\tpublic CsrfSpec csrfTokenRequestHandler(ServerCsrfTokenRequestHandler requestHandler) {\n\t\t\tthis.filter.setRequestHandler(requestHandler);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Disables CSRF Protection. Disabling CSRF Protection is only recommended when\n\t\t * the application is never used within a browser.\n\t\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t\t */\n\t\tpublic ServerHttpSecurity disable() {\n\t\t\tServerHttpSecurity.this.csrf = null;\n\t\t\treturn ServerHttpSecurity.this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tif (this.csrfTokenRepository != null) {\n\t\t\t\tthis.filter.setCsrfTokenRepository(this.csrfTokenRepository);\n\t\t\t\tif (ServerHttpSecurity.this.logout != null) {\n\t\t\t\t\tServerHttpSecurity.this.logout\n\t\t\t\t\t\t.addLogoutHandler(new CsrfServerLogoutHandler(this.csrfTokenRepository));\n\t\t\t\t}\n\t\t\t}\n\t\t\thttp.addFilterAt(this.filter, SecurityWebFiltersOrder.CSRF);\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures exception handling\n\t *\n\t * @author Rob Winch\n\t * @since 5.0\n\t * @see #exceptionHandling(Customizer)\n\t */\n\tpublic final class ExceptionHandlingSpec {\n\n\t\tprivate ExceptionHandlingSpec() {\n\t\t}\n\n\t\t/**\n\t\t * Configures what to do when the application request authentication\n\t\t * @param authenticationEntryPoint the entry point to use\n\t\t * @return the {@link ExceptionHandlingSpec} to configure\n\t\t */\n\t\tpublic ExceptionHandlingSpec authenticationEntryPoint(ServerAuthenticationEntryPoint authenticationEntryPoint) {\n\t\t\tServerHttpSecurity.this.authenticationEntryPoint = authenticationEntryPoint;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures what to do when an authenticated user does not hold a required\n\t\t * authority\n\t\t * @param accessDeniedHandler the access denied handler to use\n\t\t * @return the {@link ExceptionHandlingSpec} to configure\n\t\t *\n\t\t * @since 5.0.5\n\t\t */\n\t\tpublic ExceptionHandlingSpec accessDeniedHandler(ServerAccessDeniedHandler accessDeniedHandler) {\n\t\t\tServerHttpSecurity.this.accessDeniedHandler = accessDeniedHandler;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures the request cache which is used when a flow is interrupted (i.e. due to\n\t * requesting credentials) so that the request can be replayed after authentication.\n\t *\n\t * @author Rob Winch\n\t * @since 5.0\n\t * @see #requestCache(Customizer)\n\t */\n\tpublic final class RequestCacheSpec {\n\n\t\tprivate ServerRequestCache requestCache = new WebSessionServerRequestCache();\n\n\t\tprivate RequestCacheSpec() {\n\t\t}\n\n\t\t/**\n\t\t * Configures the cache used\n\t\t * @param requestCache the request cache\n\t\t * @return the {@link RequestCacheSpec} to configure\n\t\t */\n\t\tpublic RequestCacheSpec requestCache(ServerRequestCache requestCache) {\n\t\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\t\tthis.requestCache = requestCache;\n\t\t\treturn this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tServerRequestCacheWebFilter filter = new ServerRequestCacheWebFilter();\n\t\t\tfilter.setRequestCache(this.requestCache);\n\t\t\thttp.addFilterAt(filter, SecurityWebFiltersOrder.SERVER_REQUEST_CACHE);\n\t\t}\n\n\t\t/**\n\t\t * Disables the {@link RequestCacheSpec}\n\t\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t\t */\n\t\tpublic ServerHttpSecurity disable() {\n\t\t\tthis.requestCache = NoOpServerRequestCache.getInstance();\n\t\t\treturn ServerHttpSecurity.this;\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures HTTP Basic Authentication\n\t *\n\t * @author Rob Winch\n\t * @since 5.0\n\t * @see #httpBasic(Customizer)\n\t */\n\tpublic final class HttpBasicSpec {\n\n\t\tprivate final ServerWebExchangeMatcher xhrMatcher = (exchange) -> Mono.just(exchange.getRequest().getHeaders())\n\t\t\t.filter((h) -> h.getOrEmpty(\"X-Requested-With\").contains(\"XMLHttpRequest\"))\n\t\t\t.flatMap((h) -> ServerWebExchangeMatcher.MatchResult.match())\n\t\t\t.switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch());\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate ServerSecurityContextRepository securityContextRepository;\n\n\t\tprivate ServerAuthenticationEntryPoint entryPoint;\n\n\t\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler;\n\n\t\tprivate final List<ServerAuthenticationSuccessHandler> defaultSuccessHandlers = new ArrayList<>(\n\t\t\t\tList.of(new WebFilterChainServerAuthenticationSuccessHandler()));\n\n\t\tprivate List<ServerAuthenticationSuccessHandler> authenticationSuccessHandlers = new ArrayList<>();\n\n\t\tprivate HttpBasicSpec() {\n\t\t\tList<DelegateEntry> entryPoints = new ArrayList<>();\n\t\t\tentryPoints\n\t\t\t\t.add(new DelegateEntry(this.xhrMatcher, new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)));\n\t\t\tDelegatingServerAuthenticationEntryPoint defaultEntryPoint = new DelegatingServerAuthenticationEntryPoint(\n\t\t\t\t\tentryPoints);\n\t\t\tdefaultEntryPoint.setDefaultEntryPoint(new HttpBasicServerAuthenticationEntryPoint());\n\t\t\tthis.entryPoint = defaultEntryPoint;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ServerAuthenticationSuccessHandler} used after authentication\n\t\t * success. Defaults to {@link WebFilterChainServerAuthenticationSuccessHandler}.\n\t\t * Note that this method clears previously added success handlers via\n\t\t * {@link #authenticationSuccessHandler(Consumer)}\n\t\t * @param authenticationSuccessHandler the success handler to use\n\t\t * @return the {@link HttpBasicSpec} to continue configuring\n\t\t * @since 6.3\n\t\t */\n\t\tpublic HttpBasicSpec authenticationSuccessHandler(\n\t\t\t\tServerAuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\t\tauthenticationSuccessHandler((handlers) -> {\n\t\t\t\thandlers.clear();\n\t\t\t\thandlers.add(authenticationSuccessHandler);\n\t\t\t});\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows customizing the list of {@link ServerAuthenticationSuccessHandler}. The\n\t\t * default list contains a\n\t\t * {@link WebFilterChainServerAuthenticationSuccessHandler}.\n\t\t * @param handlersConsumer the handlers consumer\n\t\t * @return the {@link HttpBasicSpec} to continue configuring\n\t\t * @since 6.3\n\t\t */\n\t\tpublic HttpBasicSpec authenticationSuccessHandler(\n\t\t\t\tConsumer<List<ServerAuthenticationSuccessHandler>> handlersConsumer) {\n\t\t\tAssert.notNull(handlersConsumer, \"handlersConsumer cannot be null\");\n\t\t\thandlersConsumer.accept(this.authenticationSuccessHandlers);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ReactiveAuthenticationManager} used to authenticate. Defaults to\n\t\t * {@link ServerHttpSecurity#authenticationManager(ReactiveAuthenticationManager)}.\n\t\t * @param authenticationManager the authentication manager to use\n\t\t * @return the {@link HttpBasicSpec} to continue configuring\n\t\t */\n\t\tpublic HttpBasicSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ServerSecurityContextRepository} used to save the\n\t\t * {@code Authentication}. Defaults to\n\t\t * {@link NoOpServerSecurityContextRepository}. For the {@code SecurityContext} to\n\t\t * be loaded on subsequent requests the {@link ReactorContextWebFilter} must be\n\t\t * configured to be able to load the value (they are not implicitly linked).\n\t\t * @param securityContextRepository the repository to use\n\t\t * @return the {@link HttpBasicSpec} to continue configuring\n\t\t */\n\t\tpublic HttpBasicSpec securityContextRepository(ServerSecurityContextRepository securityContextRepository) {\n\t\t\tthis.securityContextRepository = securityContextRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows easily setting the entry point.\n\t\t * @param authenticationEntryPoint the {@link ServerAuthenticationEntryPoint} to\n\t\t * use\n\t\t * @return {@link HttpBasicSpec} for additional customization\n\t\t * @since 5.2.0\n\t\t */\n\t\tpublic HttpBasicSpec authenticationEntryPoint(ServerAuthenticationEntryPoint authenticationEntryPoint) {\n\t\t\tAssert.notNull(authenticationEntryPoint, \"authenticationEntryPoint cannot be null\");\n\t\t\tthis.entryPoint = authenticationEntryPoint;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic HttpBasicSpec authenticationFailureHandler(\n\t\t\t\tServerAuthenticationFailureHandler authenticationFailureHandler) {\n\t\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Disables HTTP Basic authentication.\n\t\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t\t */\n\t\tpublic ServerHttpSecurity disable() {\n\t\t\tServerHttpSecurity.this.httpBasic = null;\n\t\t\treturn ServerHttpSecurity.this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tMediaTypeServerWebExchangeMatcher restMatcher = new MediaTypeServerWebExchangeMatcher(\n\t\t\t\t\tMediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,\n\t\t\t\t\tMediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,\n\t\t\t\t\tMediaType.TEXT_XML);\n\t\t\trestMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\t\tServerWebExchangeMatcher notHtmlMatcher = new NegatedServerWebExchangeMatcher(\n\t\t\t\t\tnew MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML));\n\t\t\tServerWebExchangeMatcher restNotHtmlMatcher = new AndServerWebExchangeMatcher(\n\t\t\t\t\tArrays.asList(notHtmlMatcher, restMatcher));\n\t\t\tServerWebExchangeMatcher preferredMatcher = new OrServerWebExchangeMatcher(\n\t\t\t\t\tArrays.asList(this.xhrMatcher, restNotHtmlMatcher));\n\t\t\tServerHttpSecurity.this.defaultEntryPoints.add(new DelegateEntry(preferredMatcher, this.entryPoint));\n\t\t\tAuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(this.authenticationManager);\n\t\t\tauthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler());\n\t\t\tauthenticationFilter.setServerAuthenticationConverter(new ServerHttpBasicAuthenticationConverter());\n\t\t\tauthenticationFilter.setSecurityContextRepository(this.securityContextRepository);\n\t\t\tauthenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));\n\t\t\thttp.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.HTTP_BASIC);\n\t\t}\n\n\t\tprivate ServerAuthenticationSuccessHandler getAuthenticationSuccessHandler(ServerHttpSecurity http) {\n\t\t\tif (this.authenticationSuccessHandlers.isEmpty()) {\n\t\t\t\treturn new DelegatingServerAuthenticationSuccessHandler(this.defaultSuccessHandlers);\n\t\t\t}\n\t\t\treturn new DelegatingServerAuthenticationSuccessHandler(this.authenticationSuccessHandlers);\n\t\t}\n\n\t\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler() {\n\t\t\tif (this.authenticationFailureHandler != null) {\n\t\t\t\treturn this.authenticationFailureHandler;\n\t\t\t}\n\t\t\treturn new ServerAuthenticationEntryPointFailureHandler(this.entryPoint);\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures password management.\n\t *\n\t * @author Evgeniy Cheban\n\t * @since 5.6\n\t * @see #passwordManagement(Customizer)\n\t */\n\tpublic final class PasswordManagementSpec {\n\n\t\tprivate static final String WELL_KNOWN_CHANGE_PASSWORD_PATTERN = \"/.well-known/change-password\";\n\n\t\tprivate static final String DEFAULT_CHANGE_PASSWORD_PAGE = \"/change-password\";\n\n\t\tprivate String changePasswordPage = DEFAULT_CHANGE_PASSWORD_PAGE;\n\n\t\t/**\n\t\t * Sets the change password page. Defaults to\n\t\t * {@link PasswordManagementSpec#DEFAULT_CHANGE_PASSWORD_PAGE}.\n\t\t * @param changePasswordPage the change password page\n\t\t * @return the {@link PasswordManagementSpec} to continue configuring\n\t\t */\n\t\tpublic PasswordManagementSpec changePasswordPage(String changePasswordPage) {\n\t\t\tAssert.hasText(changePasswordPage, \"changePasswordPage cannot be empty\");\n\t\t\tthis.changePasswordPage = changePasswordPage;\n\t\t\treturn this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tExchangeMatcherRedirectWebFilter changePasswordWebFilter = new ExchangeMatcherRedirectWebFilter(\n\t\t\t\t\tnew PathPatternParserServerWebExchangeMatcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN),\n\t\t\t\t\tthis.changePasswordPage);\n\t\t\thttp.addFilterBefore(changePasswordWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);\n\t\t}\n\n\t\tprivate PasswordManagementSpec() {\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures Form Based authentication\n\t *\n\t * @author Rob Winch\n\t * @since 5.0\n\t * @see #formLogin(Customizer)\n\t */\n\tpublic final class FormLoginSpec {\n\n\t\tprivate final RedirectServerAuthenticationSuccessHandler defaultSuccessHandler = new RedirectServerAuthenticationSuccessHandler(\n\t\t\t\t\"/\");\n\n\t\tprivate final List<ServerAuthenticationSuccessHandler> defaultSuccessHandlers = new ArrayList<>(\n\t\t\t\tList.of(this.defaultSuccessHandler));\n\n\t\tprivate RedirectServerAuthenticationEntryPoint defaultEntryPoint;\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate ServerSecurityContextRepository securityContextRepository;\n\n\t\tprivate ServerAuthenticationEntryPoint authenticationEntryPoint;\n\n\t\tprivate boolean isEntryPointExplicit;\n\n\t\tprivate ServerWebExchangeMatcher requiresAuthenticationMatcher;\n\n\t\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler;\n\n\t\tprivate List<ServerAuthenticationSuccessHandler> authenticationSuccessHandlers = new ArrayList<>();\n\n\t\tprivate FormLoginSpec() {\n\t\t}\n\n\t\t/**\n\t\t * The {@link ReactiveAuthenticationManager} used to authenticate. Defaults to\n\t\t * {@link ServerHttpSecurity#authenticationManager(ReactiveAuthenticationManager)}.\n\t\t * @param authenticationManager the authentication manager to use\n\t\t * @return the {@link FormLoginSpec} to continue configuring\n\t\t */\n\t\tpublic FormLoginSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ServerAuthenticationSuccessHandler} used after authentication\n\t\t * success. Defaults to {@link RedirectServerAuthenticationSuccessHandler}. Note\n\t\t * that this method clears previously added success handlers via\n\t\t * {@link #authenticationSuccessHandler(Consumer)}\n\t\t * @param authenticationSuccessHandler the success handler to use\n\t\t * @return the {@link FormLoginSpec} to continue configuring\n\t\t */\n\t\tpublic FormLoginSpec authenticationSuccessHandler(\n\t\t\t\tServerAuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\t\tauthenticationSuccessHandler((handlers) -> {\n\t\t\t\thandlers.clear();\n\t\t\t\thandlers.add(authenticationSuccessHandler);\n\t\t\t});\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows customizing the list of {@link ServerAuthenticationSuccessHandler}. The\n\t\t * default list contains a {@link RedirectServerAuthenticationSuccessHandler} that\n\t\t * redirects to \"/\".\n\t\t * @param handlersConsumer the handlers consumer\n\t\t * @return the {@link FormLoginSpec} to continue configuring\n\t\t * @since 6.3\n\t\t */\n\t\tpublic FormLoginSpec authenticationSuccessHandler(\n\t\t\t\tConsumer<List<ServerAuthenticationSuccessHandler>> handlersConsumer) {\n\t\t\tAssert.notNull(handlersConsumer, \"handlersConsumer cannot be null\");\n\t\t\thandlersConsumer.accept(this.authenticationSuccessHandlers);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the log in page to redirect to, the authentication failure page, and\n\t\t * when authentication is performed. The default is that Spring Security will\n\t\t * generate a log in page at \"/login\" and a log out page at \"/logout\". If this is\n\t\t * customized:\n\t\t * <ul>\n\t\t * <li>The default log in &amp; log out page are no longer provided</li>\n\t\t * <li>The application must render a log in page at the provided URL</li>\n\t\t * <li>The application must render an authentication error page at the provided\n\t\t * URL + \"?error\"</li>\n\t\t * <li>Authentication will occur for POST to the provided URL</li>\n\t\t * </ul>\n\t\t * @param loginPage the url to redirect to which provides a form to log in (i.e.\n\t\t * \"/login\")\n\t\t * @return the {@link FormLoginSpec} to continue configuring\n\t\t * @see #authenticationEntryPoint(ServerAuthenticationEntryPoint)\n\t\t * @see #requiresAuthenticationMatcher(ServerWebExchangeMatcher)\n\t\t * @see #authenticationFailureHandler(ServerAuthenticationFailureHandler)\n\t\t */\n\t\tpublic FormLoginSpec loginPage(String loginPage) {\n\t\t\tthis.defaultEntryPoint = new RedirectServerAuthenticationEntryPoint(loginPage);\n\t\t\tthis.authenticationEntryPoint = this.defaultEntryPoint;\n\t\t\tif (this.requiresAuthenticationMatcher == null) {\n\t\t\t\tthis.requiresAuthenticationMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, loginPage);\n\t\t\t}\n\t\t\tif (this.authenticationFailureHandler == null) {\n\t\t\t\tthis.authenticationFailureHandler = new RedirectServerAuthenticationFailureHandler(\n\t\t\t\t\t\tloginPage + \"?error\");\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * How to request for authentication. The default is that Spring Security will\n\t\t * generate a log in page at \"/login\".\n\t\t * @param authenticationEntryPoint the entry point to use\n\t\t * @return the {@link FormLoginSpec} to continue configuring\n\t\t * @see #loginPage(String)\n\t\t */\n\t\tpublic FormLoginSpec authenticationEntryPoint(ServerAuthenticationEntryPoint authenticationEntryPoint) {\n\t\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures when authentication is performed. The default is a POST to \"/login\".\n\t\t * @param requiresAuthenticationMatcher the matcher to use\n\t\t * @return the {@link FormLoginSpec} to continue configuring\n\t\t * @see #loginPage(String)\n\t\t */\n\t\tpublic FormLoginSpec requiresAuthenticationMatcher(ServerWebExchangeMatcher requiresAuthenticationMatcher) {\n\t\t\tthis.requiresAuthenticationMatcher = requiresAuthenticationMatcher;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures how a failed authentication is handled. The default is to redirect\n\t\t * to \"/login?error\".\n\t\t * @param authenticationFailureHandler the handler to use\n\t\t * @return the {@link FormLoginSpec} to continue configuring\n\t\t * @see #loginPage(String)\n\t\t */\n\t\tpublic FormLoginSpec authenticationFailureHandler(\n\t\t\t\tServerAuthenticationFailureHandler authenticationFailureHandler) {\n\t\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ServerSecurityContextRepository} used to save the\n\t\t * {@code Authentication}. Defaults to\n\t\t * {@link WebSessionServerSecurityContextRepository}. For the\n\t\t * {@code SecurityContext} to be loaded on subsequent requests the\n\t\t * {@link ReactorContextWebFilter} must be configured to be able to load the value\n\t\t * (they are not implicitly linked).\n\t\t * @param securityContextRepository the repository to use\n\t\t * @return the {@link FormLoginSpec} to continue configuring\n\t\t */\n\t\tpublic FormLoginSpec securityContextRepository(ServerSecurityContextRepository securityContextRepository) {\n\t\t\tthis.securityContextRepository = securityContextRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Disables HTTP Basic authentication.\n\t\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t\t */\n\t\tpublic ServerHttpSecurity disable() {\n\t\t\tServerHttpSecurity.this.formLogin = null;\n\t\t\treturn ServerHttpSecurity.this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tif (this.authenticationEntryPoint == null) {\n\t\t\t\tthis.isEntryPointExplicit = false;\n\t\t\t\tloginPage(\"/login\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.isEntryPointExplicit = true;\n\t\t\t}\n\t\t\tif (http.requestCache != null) {\n\t\t\t\tServerRequestCache requestCache = http.requestCache.requestCache;\n\t\t\t\tthis.defaultSuccessHandler.setRequestCache(requestCache);\n\t\t\t\tif (this.defaultEntryPoint != null) {\n\t\t\t\t\tthis.defaultEntryPoint.setRequestCache(requestCache);\n\t\t\t\t}\n\t\t\t}\n\t\t\tMediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML);\n\t\t\thtmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\t\tServerHttpSecurity.this.defaultEntryPoints.add(0,\n\t\t\t\t\tnew DelegateEntry(htmlMatcher, this.authenticationEntryPoint));\n\t\t\tAuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(this.authenticationManager);\n\t\t\tauthenticationFilter.setRequiresAuthenticationMatcher(this.requiresAuthenticationMatcher);\n\t\t\tauthenticationFilter.setAuthenticationFailureHandler(this.authenticationFailureHandler);\n\t\t\tauthenticationFilter.setServerAuthenticationConverter(new ServerFormLoginAuthenticationConverter());\n\t\t\tauthenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));\n\t\t\tauthenticationFilter.setSecurityContextRepository(this.securityContextRepository);\n\t\t\thttp.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.FORM_LOGIN);\n\t\t}\n\n\t\tprivate ServerAuthenticationSuccessHandler getAuthenticationSuccessHandler(ServerHttpSecurity http) {\n\t\t\tif (this.authenticationSuccessHandlers.isEmpty()) {\n\t\t\t\treturn new DelegatingServerAuthenticationSuccessHandler(this.defaultSuccessHandlers);\n\t\t\t}\n\t\t\treturn new DelegatingServerAuthenticationSuccessHandler(this.authenticationSuccessHandlers);\n\t\t}\n\n\t}\n\n\tprivate final class LoginPageSpec {\n\n\t\tprivate LoginPageSpec() {\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tif (http.authenticationEntryPoint != null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (http.formLogin != null && http.formLogin.isEntryPointExplicit\n\t\t\t\t\t|| http.oauth2Login != null && StringUtils.hasText(http.oauth2Login.loginPage)\n\t\t\t\t\t|| http.oneTimeTokenLogin != null && StringUtils.hasText(http.oneTimeTokenLogin.loginPage)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tLoginPageGeneratingWebFilter loginPage = null;\n\t\t\tif (http.formLogin != null && !http.formLogin.isEntryPointExplicit) {\n\t\t\t\tloginPage = new LoginPageGeneratingWebFilter();\n\t\t\t\tloginPage.setFormLoginEnabled(true);\n\t\t\t}\n\t\t\tif (http.oauth2Login != null) {\n\t\t\t\tMap<String, String> urlToText = http.oauth2Login.getLinks();\n\t\t\t\tif (loginPage == null) {\n\t\t\t\t\tloginPage = new LoginPageGeneratingWebFilter();\n\t\t\t\t}\n\t\t\t\tloginPage.setOauth2AuthenticationUrlToClientName(urlToText);\n\t\t\t}\n\t\t\tif (http.oneTimeTokenLogin != null) {\n\t\t\t\tif (loginPage == null) {\n\t\t\t\t\tloginPage = new LoginPageGeneratingWebFilter();\n\t\t\t\t}\n\t\t\t\tloginPage.setOneTimeTokenEnabled(true);\n\t\t\t\tloginPage.setGenerateOneTimeTokenUrl(http.oneTimeTokenLogin.tokenGeneratingUrl);\n\t\t\t}\n\t\t\tif (loginPage != null) {\n\t\t\t\thttp.addFilterAt(loginPage, SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);\n\t\t\t\thttp.addFilterBefore(DefaultResourcesWebFilter.css(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);\n\t\t\t\tif (http.logout != null) {\n\t\t\t\t\thttp.addFilterAt(new LogoutPageGeneratingWebFilter(),\n\t\t\t\t\t\t\tSecurityWebFiltersOrder.LOGOUT_PAGE_GENERATING);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures HTTP Response Headers.\n\t *\n\t * @author Rob Winch\n\t * @since 5.0\n\t * @see #headers(Customizer)\n\t */\n\tpublic final class HeaderSpec {\n\n\t\tprivate final List<ServerHttpHeadersWriter> writers;\n\n\t\tprivate CacheControlServerHttpHeadersWriter cacheControl = new CacheControlServerHttpHeadersWriter();\n\n\t\tprivate ContentTypeOptionsServerHttpHeadersWriter contentTypeOptions = new ContentTypeOptionsServerHttpHeadersWriter();\n\n\t\tprivate StrictTransportSecurityServerHttpHeadersWriter hsts = new StrictTransportSecurityServerHttpHeadersWriter();\n\n\t\tprivate XFrameOptionsServerHttpHeadersWriter frameOptions = new XFrameOptionsServerHttpHeadersWriter();\n\n\t\tprivate XXssProtectionServerHttpHeadersWriter xss = new XXssProtectionServerHttpHeadersWriter();\n\n\t\tprivate FeaturePolicyServerHttpHeadersWriter featurePolicy = new FeaturePolicyServerHttpHeadersWriter();\n\n\t\tprivate PermissionsPolicyServerHttpHeadersWriter permissionsPolicy = new PermissionsPolicyServerHttpHeadersWriter();\n\n\t\tprivate ContentSecurityPolicyServerHttpHeadersWriter contentSecurityPolicy = new ContentSecurityPolicyServerHttpHeadersWriter();\n\n\t\tprivate ReferrerPolicyServerHttpHeadersWriter referrerPolicy = new ReferrerPolicyServerHttpHeadersWriter();\n\n\t\tprivate CrossOriginOpenerPolicyServerHttpHeadersWriter crossOriginOpenerPolicy = new CrossOriginOpenerPolicyServerHttpHeadersWriter();\n\n\t\tprivate CrossOriginEmbedderPolicyServerHttpHeadersWriter crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyServerHttpHeadersWriter();\n\n\t\tprivate CrossOriginResourcePolicyServerHttpHeadersWriter crossOriginResourcePolicy = new CrossOriginResourcePolicyServerHttpHeadersWriter();\n\n\t\tprivate HeaderSpec() {\n\t\t\tthis.writers = new ArrayList<>(Arrays.asList(this.cacheControl, this.contentTypeOptions, this.hsts,\n\t\t\t\t\tthis.frameOptions, this.xss, this.featurePolicy, this.permissionsPolicy, this.contentSecurityPolicy,\n\t\t\t\t\tthis.referrerPolicy, this.crossOriginOpenerPolicy, this.crossOriginEmbedderPolicy,\n\t\t\t\t\tthis.crossOriginResourcePolicy));\n\t\t}\n\n\t\t/**\n\t\t * Disables http response headers\n\t\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t\t */\n\t\tpublic ServerHttpSecurity disable() {\n\t\t\tServerHttpSecurity.this.headers = null;\n\t\t\treturn ServerHttpSecurity.this;\n\t\t}\n\n\t\t/**\n\t\t * Configures cache control headers\n\t\t * @param cacheCustomizer the {@link Customizer} to provide more options for the\n\t\t * {@link CacheSpec}\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t */\n\t\tpublic HeaderSpec cache(Customizer<CacheSpec> cacheCustomizer) {\n\t\t\tcacheCustomizer.customize(new CacheSpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures content type response headers\n\t\t * @param contentTypeOptionsCustomizer the {@link Customizer} to provide more\n\t\t * options for the {@link ContentTypeOptionsSpec}\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t */\n\t\tpublic HeaderSpec contentTypeOptions(Customizer<ContentTypeOptionsSpec> contentTypeOptionsCustomizer) {\n\t\t\tcontentTypeOptionsCustomizer.customize(new ContentTypeOptionsSpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures frame options response headers\n\t\t * @param frameOptionsCustomizer the {@link Customizer} to provide more options\n\t\t * for the {@link FrameOptionsSpec}\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t */\n\t\tpublic HeaderSpec frameOptions(Customizer<FrameOptionsSpec> frameOptionsCustomizer) {\n\t\t\tframeOptionsCustomizer.customize(new FrameOptionsSpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures custom headers writer\n\t\t * @param serverHttpHeadersWriter the {@link ServerHttpHeadersWriter} to provide\n\t\t * custom headers writer\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t * @since 5.3.0\n\t\t */\n\t\tpublic HeaderSpec writer(ServerHttpHeadersWriter serverHttpHeadersWriter) {\n\t\t\tAssert.notNull(serverHttpHeadersWriter, \"serverHttpHeadersWriter cannot be null\");\n\t\t\tthis.writers.add(serverHttpHeadersWriter);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the Strict Transport Security response headers\n\t\t * @param hstsCustomizer the {@link Customizer} to provide more options for the\n\t\t * {@link HstsSpec}\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t */\n\t\tpublic HeaderSpec hsts(Customizer<HstsSpec> hstsCustomizer) {\n\t\t\thstsCustomizer.customize(new HstsSpec());\n\t\t\treturn this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tServerHttpHeadersWriter writer = new CompositeServerHttpHeadersWriter(this.writers);\n\t\t\tHttpHeaderWriterWebFilter result = new HttpHeaderWriterWebFilter(writer);\n\t\t\thttp.addFilterAt(result, SecurityWebFiltersOrder.HTTP_HEADERS_WRITER);\n\t\t}\n\n\t\t/**\n\t\t * Configures x-xss-protection response header.\n\t\t * @param xssProtectionCustomizer the {@link Customizer} to provide more options\n\t\t * for the {@link XssProtectionSpec}\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t */\n\t\tpublic HeaderSpec xssProtection(Customizer<XssProtectionSpec> xssProtectionCustomizer) {\n\t\t\txssProtectionCustomizer.customize(new XssProtectionSpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures {@code Content-Security-Policy} response header.\n\t\t * @param contentSecurityPolicyCustomizer the {@link Customizer} to provide more\n\t\t * options for the {@link ContentSecurityPolicySpec}\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t */\n\t\tpublic HeaderSpec contentSecurityPolicy(Customizer<ContentSecurityPolicySpec> contentSecurityPolicyCustomizer) {\n\t\t\tcontentSecurityPolicyCustomizer.customize(new ContentSecurityPolicySpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures {@code Feature-Policy} response header.\n\t\t * @param policyDirectives the policy\n\t\t * @return the {@link FeaturePolicySpec} to configure\n\t\t * @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)}\n\t\t * instead.\n\t\t */\n\t\t@Deprecated\n\t\tpublic FeaturePolicySpec featurePolicy(String policyDirectives) {\n\t\t\treturn new FeaturePolicySpec(policyDirectives);\n\t\t}\n\n\t\t/**\n\t\t * Configures {@code Permissions-Policy} response header.\n\t\t * @param permissionsPolicyCustomizer the {@link Customizer} to provide more\n\t\t * options for the {@link PermissionsPolicySpec}\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t */\n\t\tpublic HeaderSpec permissionsPolicy(Customizer<PermissionsPolicySpec> permissionsPolicyCustomizer) {\n\t\t\tpermissionsPolicyCustomizer.customize(new PermissionsPolicySpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures {@code Referrer-Policy} response header.\n\t\t * @param referrerPolicyCustomizer the {@link Customizer} to provide more options\n\t\t * for the {@link ReferrerPolicySpec}\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t */\n\t\tpublic HeaderSpec referrerPolicy(Customizer<ReferrerPolicySpec> referrerPolicyCustomizer) {\n\t\t\treferrerPolicyCustomizer.customize(new ReferrerPolicySpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the <a href=\n\t\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy\">\n\t\t * Cross-Origin-Opener-Policy</a> header.\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t * @since 5.7\n\t\t * @see CrossOriginOpenerPolicyServerHttpHeadersWriter\n\t\t */\n\t\tpublic HeaderSpec crossOriginOpenerPolicy(\n\t\t\t\tCustomizer<CrossOriginOpenerPolicySpec> crossOriginOpenerPolicyCustomizer) {\n\t\t\tcrossOriginOpenerPolicyCustomizer.customize(new CrossOriginOpenerPolicySpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the <a href=\n\t\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy\">\n\t\t * Cross-Origin-Embedder-Policy</a> header.\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t * @since 5.7\n\t\t * @see CrossOriginEmbedderPolicyServerHttpHeadersWriter\n\t\t */\n\t\tpublic HeaderSpec crossOriginEmbedderPolicy(\n\t\t\t\tCustomizer<CrossOriginEmbedderPolicySpec> crossOriginEmbedderPolicyCustomizer) {\n\t\t\tcrossOriginEmbedderPolicyCustomizer.customize(new CrossOriginEmbedderPolicySpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the <a href=\n\t\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy\">\n\t\t * Cross-Origin-Resource-Policy</a> header.\n\t\t * @return the {@link HeaderSpec} to customize\n\t\t * @since 5.7\n\t\t * @see CrossOriginResourcePolicyServerHttpHeadersWriter\n\t\t */\n\t\tpublic HeaderSpec crossOriginResourcePolicy(\n\t\t\t\tCustomizer<CrossOriginResourcePolicySpec> crossOriginResourcePolicyCustomizer) {\n\t\t\tcrossOriginResourcePolicyCustomizer.customize(new CrossOriginResourcePolicySpec());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures cache control headers\n\t\t *\n\t\t * @see HeaderSpec#cache(Customizer)\n\t\t */\n\t\tpublic final class CacheSpec {\n\n\t\t\tprivate CacheSpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Disables cache control response headers\n\t\t\t * @return the {@link HeaderSpec} to configure\n\t\t\t */\n\t\t\tpublic HeaderSpec disable() {\n\t\t\t\tHeaderSpec.this.writers.remove(HeaderSpec.this.cacheControl);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * The content type headers\n\t\t *\n\t\t * @see HeaderSpec#contentTypeOptions(Customizer)\n\t\t */\n\t\tpublic final class ContentTypeOptionsSpec {\n\n\t\t\tprivate ContentTypeOptionsSpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Disables the content type options response header\n\t\t\t * @return the {@link HeaderSpec} to configure\n\t\t\t */\n\t\t\tpublic HeaderSpec disable() {\n\t\t\t\tHeaderSpec.this.writers.remove(HeaderSpec.this.contentTypeOptions);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures frame options response header\n\t\t *\n\t\t * @see HeaderSpec#frameOptions(Customizer)\n\t\t */\n\t\tpublic final class FrameOptionsSpec {\n\n\t\t\tprivate FrameOptionsSpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * The mode to configure. Default is\n\t\t\t * {@link org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter.Mode#DENY}\n\t\t\t * @param mode the mode to use\n\t\t\t * @return the {@link HeaderSpec} to configure\n\t\t\t */\n\t\t\tpublic HeaderSpec mode(XFrameOptionsServerHttpHeadersWriter.Mode mode) {\n\t\t\t\tHeaderSpec.this.frameOptions.setMode(mode);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Disables frame options response header\n\t\t\t * @return the {@link HeaderSpec} to continue configuring\n\t\t\t */\n\t\t\tpublic HeaderSpec disable() {\n\t\t\t\tHeaderSpec.this.writers.remove(HeaderSpec.this.frameOptions);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures Strict Transport Security response header\n\t\t *\n\t\t * @see HeaderSpec#hsts(Customizer)\n\t\t */\n\t\tpublic final class HstsSpec {\n\n\t\t\tprivate HstsSpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configures the max age. Default is one year.\n\t\t\t * @param maxAge the max age\n\t\t\t * @return the {@link HstsSpec} to continue configuring\n\t\t\t */\n\t\t\tpublic HstsSpec maxAge(Duration maxAge) {\n\t\t\t\tHeaderSpec.this.hsts.setMaxAge(maxAge);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configures if subdomains should be included. Default is true\n\t\t\t * @param includeSubDomains if subdomains should be included\n\t\t\t * @return the {@link HstsSpec} to continue configuring\n\t\t\t */\n\t\t\tpublic HstsSpec includeSubdomains(boolean includeSubDomains) {\n\t\t\t\tHeaderSpec.this.hsts.setIncludeSubDomains(includeSubDomains);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * <p>\n\t\t\t * Configures if preload should be included. Default is false\n\t\t\t * </p>\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * See <a href=\"https://hstspreload.org/\">Website hstspreload.org</a> for\n\t\t\t * additional details.\n\t\t\t * </p>\n\t\t\t * @param preload if subdomains should be included\n\t\t\t * @return the {@link HstsSpec} to continue configuring\n\t\t\t * @since 5.2.0\n\t\t\t */\n\t\t\tpublic HstsSpec preload(boolean preload) {\n\t\t\t\tHeaderSpec.this.hsts.setPreload(preload);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Disables strict transport security response header\n\t\t\t * @return the {@link HeaderSpec} to continue configuring\n\t\t\t */\n\t\t\tpublic HeaderSpec disable() {\n\t\t\t\tHeaderSpec.this.writers.remove(HeaderSpec.this.hsts);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures x-xss-protection response header\n\t\t *\n\t\t * @see HeaderSpec#xssProtection(Customizer)\n\t\t */\n\t\tpublic final class XssProtectionSpec {\n\n\t\t\tprivate XssProtectionSpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Disables the x-xss-protection response header\n\t\t\t * @return the {@link HeaderSpec} to continue configuring\n\t\t\t */\n\t\t\tpublic HeaderSpec disable() {\n\t\t\t\tHeaderSpec.this.writers.remove(HeaderSpec.this.xss);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the value of x-xss-protection header. OWASP recommends using\n\t\t\t * {@link XXssProtectionServerHttpHeadersWriter.HeaderValue#DISABLED}.\n\t\t\t * @param headerValue the headerValue\n\t\t\t * @return the {@link HeaderSpec} to continue configuring\n\t\t\t * @since 5.8\n\t\t\t */\n\t\t\tpublic HeaderSpec headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue headerValue) {\n\t\t\t\tHeaderSpec.this.xss.setHeaderValue(headerValue);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures {@code Content-Security-Policy} response header.\n\t\t *\n\t\t * @since 5.1\n\t\t * @see HeaderSpec#contentSecurityPolicy(Customizer)\n\t\t */\n\t\tpublic final class ContentSecurityPolicySpec {\n\n\t\t\tprivate static final String DEFAULT_SRC_SELF_POLICY = \"default-src 'self'\";\n\n\t\t\tprivate ContentSecurityPolicySpec() {\n\t\t\t\tHeaderSpec.this.contentSecurityPolicy.setPolicyDirectives(DEFAULT_SRC_SELF_POLICY);\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Whether to include the {@code Content-Security-Policy-Report-Only} header\n\t\t\t * in the response. Otherwise, defaults to the {@code Content-Security-Policy}\n\t\t\t * header.\n\t\t\t * @param reportOnly whether to only report policy violations\n\t\t\t * @return the {@link HeaderSpec} to continue configuring\n\t\t\t */\n\t\t\tpublic HeaderSpec reportOnly(boolean reportOnly) {\n\t\t\t\tHeaderSpec.this.contentSecurityPolicy.setReportOnly(reportOnly);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the security policy directive(s) to be used in the response header.\n\t\t\t * @param policyDirectives the security policy directive(s)\n\t\t\t * @return the {@link HeaderSpec} to continue configuring\n\t\t\t */\n\t\t\tpublic HeaderSpec policyDirectives(String policyDirectives) {\n\t\t\t\tHeaderSpec.this.contentSecurityPolicy.setPolicyDirectives(policyDirectives);\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t\tprivate ContentSecurityPolicySpec(String policyDirectives) {\n\t\t\t\tHeaderSpec.this.contentSecurityPolicy.setPolicyDirectives(policyDirectives);\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures {@code Feature-Policy} response header.\n\t\t *\n\t\t * @since 5.1\n\t\t * @see #featurePolicy(String)\n\t\t */\n\t\tpublic final class FeaturePolicySpec {\n\n\t\t\tprivate FeaturePolicySpec(String policyDirectives) {\n\t\t\t\tHeaderSpec.this.featurePolicy.setPolicyDirectives(policyDirectives);\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Allows method chaining to continue configuring the\n\t\t\t * {@link ServerHttpSecurity}.\n\t\t\t * @return the {@link HeaderSpec} to continue configuring\n\t\t\t * @deprecated For removal in 7.0. Use {@link #featurePolicy(String)} instead\n\t\t\t */\n\t\t\t@Deprecated(since = \"6.1\", forRemoval = true)\n\t\t\tpublic HeaderSpec and() {\n\t\t\t\treturn HeaderSpec.this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures {@code Permissions-Policy} response header.\n\t\t *\n\t\t * @since 5.5\n\t\t * @see HeaderSpec#permissionsPolicy(Customizer)\n\t\t */\n\t\tpublic final class PermissionsPolicySpec {\n\n\t\t\tprivate PermissionsPolicySpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the policy to be used in the response header.\n\t\t\t * @param policy a permissions policy\n\t\t\t * @return the {@link PermissionsPolicySpec} to continue configuring\n\t\t\t */\n\t\t\tpublic PermissionsPolicySpec policy(String policy) {\n\t\t\t\tHeaderSpec.this.permissionsPolicy.setPolicy(policy);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures {@code Referrer-Policy} response header.\n\t\t *\n\t\t * @since 5.1\n\t\t * @see HeaderSpec#referrerPolicy(Customizer)\n\t\t */\n\t\tpublic final class ReferrerPolicySpec {\n\n\t\t\tprivate ReferrerPolicySpec() {\n\t\t\t}\n\n\t\t\tprivate ReferrerPolicySpec(ReferrerPolicy referrerPolicy) {\n\t\t\t\tHeaderSpec.this.referrerPolicy.setPolicy(referrerPolicy);\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the policy to be used in the response header.\n\t\t\t * @param referrerPolicy a referrer policy\n\t\t\t * @return the {@link ReferrerPolicySpec} to continue configuring\n\t\t\t */\n\t\t\tpublic ReferrerPolicySpec policy(ReferrerPolicy referrerPolicy) {\n\t\t\t\tHeaderSpec.this.referrerPolicy.setPolicy(referrerPolicy);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures the Cross-Origin-Opener-Policy header\n\t\t *\n\t\t * @since 5.7\n\t\t */\n\t\tpublic final class CrossOriginOpenerPolicySpec {\n\n\t\t\tprivate CrossOriginOpenerPolicySpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the value to be used in the `Cross-Origin-Opener-Policy` header\n\t\t\t * @param openerPolicy a opener policy\n\t\t\t * @return the {@link CrossOriginOpenerPolicySpec} to continue configuring\n\t\t\t */\n\t\t\tpublic CrossOriginOpenerPolicySpec policy(CrossOriginOpenerPolicy openerPolicy) {\n\t\t\t\tHeaderSpec.this.crossOriginOpenerPolicy.setPolicy(openerPolicy);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures the Cross-Origin-Embedder-Policy header\n\t\t *\n\t\t * @since 5.7\n\t\t */\n\t\tpublic final class CrossOriginEmbedderPolicySpec {\n\n\t\t\tprivate CrossOriginEmbedderPolicySpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the value to be used in the `Cross-Origin-Embedder-Policy` header\n\t\t\t * @param embedderPolicy a opener policy\n\t\t\t * @return the {@link CrossOriginEmbedderPolicySpec} to continue configuring\n\t\t\t */\n\t\t\tpublic CrossOriginEmbedderPolicySpec policy(CrossOriginEmbedderPolicy embedderPolicy) {\n\t\t\t\tHeaderSpec.this.crossOriginEmbedderPolicy.setPolicy(embedderPolicy);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures the Cross-Origin-Resource-Policy header\n\t\t *\n\t\t * @since 5.7\n\t\t */\n\t\tpublic final class CrossOriginResourcePolicySpec {\n\n\t\t\tprivate CrossOriginResourcePolicySpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the value to be used in the `Cross-Origin-Resource-Policy` header\n\t\t\t * @param resourcePolicy a opener policy\n\t\t\t * @return the {@link CrossOriginResourcePolicySpec} to continue configuring\n\t\t\t */\n\t\t\tpublic CrossOriginResourcePolicySpec policy(CrossOriginResourcePolicy resourcePolicy) {\n\t\t\t\tHeaderSpec.this.crossOriginResourcePolicy.setPolicy(resourcePolicy);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures log out\n\t *\n\t * @author Shazin Sadakath\n\t * @since 5.0\n\t * @see #logout(Customizer)\n\t */\n\tpublic final class LogoutSpec {\n\n\t\tprivate LogoutWebFilter logoutWebFilter = new LogoutWebFilter();\n\n\t\tprivate final SecurityContextServerLogoutHandler DEFAULT_LOGOUT_HANDLER = new SecurityContextServerLogoutHandler();\n\n\t\tprivate List<ServerLogoutHandler> logoutHandlers = new ArrayList<>(Arrays.asList(this.DEFAULT_LOGOUT_HANDLER));\n\n\t\tprivate LogoutSpec() {\n\t\t}\n\n\t\t/**\n\t\t * Configures the logout handler. Default is\n\t\t * {@code SecurityContextServerLogoutHandler}. This clears any previous handlers\n\t\t * configured.\n\t\t * @param logoutHandler\n\t\t * @return the {@link LogoutSpec} to configure\n\t\t */\n\t\tpublic LogoutSpec logoutHandler(ServerLogoutHandler logoutHandler) {\n\t\t\tAssert.notNull(logoutHandler, \"logoutHandler cannot be null\");\n\t\t\tthis.logoutHandlers.clear();\n\t\t\treturn addLogoutHandler(logoutHandler);\n\t\t}\n\n\t\tprivate LogoutSpec addLogoutHandler(ServerLogoutHandler logoutHandler) {\n\t\t\tAssert.notNull(logoutHandler, \"logoutHandler cannot be null\");\n\t\t\tthis.logoutHandlers.add(logoutHandler);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows managing the list of {@link ServerLogoutHandler} instances.\n\t\t * @param handlersConsumer {@link Consumer} for managing the list of handlers.\n\t\t * @return the {@link LogoutSpec} to configure\n\t\t * @since 7.0\n\t\t */\n\t\tpublic LogoutSpec logoutHandler(Consumer<List<ServerLogoutHandler>> handlersConsumer) {\n\t\t\tAssert.notNull(handlersConsumer, \"consumer cannot be null\");\n\t\t\thandlersConsumer.accept(this.logoutHandlers);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures what URL a POST to will trigger a log out.\n\t\t * @param logoutUrl the url to trigger a log out (i.e. \"/signout\" would mean a\n\t\t * POST to \"/signout\" would trigger log out)\n\t\t * @return the {@link LogoutSpec} to configure\n\t\t */\n\t\tpublic LogoutSpec logoutUrl(String logoutUrl) {\n\t\t\tAssert.notNull(logoutUrl, \"logoutUrl must not be null\");\n\t\t\tServerWebExchangeMatcher requiresLogout = ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,\n\t\t\t\t\tlogoutUrl);\n\t\t\treturn requiresLogout(requiresLogout);\n\t\t}\n\n\t\t/**\n\t\t * Configures when the log out will be triggered.\n\t\t * @param requiresLogout the matcher to determine when log out is triggered\n\t\t * @return the {@link LogoutSpec} to configure\n\t\t */\n\t\tpublic LogoutSpec requiresLogout(ServerWebExchangeMatcher requiresLogout) {\n\t\t\tthis.logoutWebFilter.setRequiresLogoutMatcher(requiresLogout);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic LogoutSpec logoutSuccessHandler(ServerLogoutSuccessHandler handler) {\n\t\t\tthis.logoutWebFilter.setLogoutSuccessHandler(handler);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Disables log out\n\t\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t\t */\n\t\tpublic ServerHttpSecurity disable() {\n\t\t\tServerHttpSecurity.this.logout = null;\n\t\t\treturn ServerHttpSecurity.this;\n\t\t}\n\n\t\tprivate ServerLogoutHandler createLogoutHandler() {\n\t\t\tServerSecurityContextRepository securityContextRepository = ServerHttpSecurity.this.securityContextRepository;\n\t\t\tif (securityContextRepository != null) {\n\t\t\t\tthis.DEFAULT_LOGOUT_HANDLER.setSecurityContextRepository(securityContextRepository);\n\t\t\t}\n\t\t\tif (this.logoutHandlers.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (this.logoutHandlers.size() == 1) {\n\t\t\t\treturn this.logoutHandlers.get(0);\n\t\t\t}\n\t\t\treturn new DelegatingServerLogoutHandler(this.logoutHandlers);\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tServerLogoutHandler logoutHandler = createLogoutHandler();\n\t\t\tif (logoutHandler != null) {\n\t\t\t\tthis.logoutWebFilter.setLogoutHandler(logoutHandler);\n\t\t\t}\n\t\t\thttp.addFilterAt(this.logoutWebFilter, SecurityWebFiltersOrder.LOGOUT);\n\t\t}\n\n\t}\n\n\tprivate static class OrderedWebFilter implements WebFilter, Ordered {\n\n\t\tprivate final WebFilter webFilter;\n\n\t\tprivate final int order;\n\n\t\tOrderedWebFilter(WebFilter webFilter, int order) {\n\t\t\tthis.webFilter = webFilter;\n\t\t\tthis.order = order;\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn this.webFilter.filter(exchange, chain);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getOrder() {\n\t\t\treturn this.order;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"OrderedWebFilter{\" + \"webFilter=\" + this.webFilter + \", order=\" + this.order + '}';\n\t\t}\n\n\t}\n\n\t/**\n\t * Workaround https://jira.spring.io/projects/SPR/issues/SPR-17213\n\t */\n\tstatic class ServerWebExchangeReactorContextWebFilter implements WebFilter {\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn chain.filter(exchange).contextWrite(Context.of(ServerWebExchange.class, exchange));\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures CORS support within Spring Security. This ensures that the\n\t * {@link CorsWebFilter} is place in the correct order.\n\t */\n\tpublic final class CorsSpec {\n\n\t\tprivate CorsWebFilter corsFilter;\n\n\t\tprivate CorsSpec() {\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link CorsConfigurationSource} to be used\n\t\t * @param source the source to use\n\t\t * @return the {@link CorsSpec} for additional configuration\n\t\t */\n\t\tpublic CorsSpec configurationSource(CorsConfigurationSource source) {\n\t\t\tthis.corsFilter = new CorsWebFilter(source);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Disables CORS support within Spring Security.\n\t\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t\t */\n\t\tpublic ServerHttpSecurity disable() {\n\t\t\tServerHttpSecurity.this.cors = null;\n\t\t\treturn ServerHttpSecurity.this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tCorsWebFilter corsFilter = getCorsFilter();\n\t\t\tif (corsFilter != null) {\n\t\t\t\thttp.addFilterAt(this.corsFilter, SecurityWebFiltersOrder.CORS);\n\t\t\t}\n\t\t}\n\n\t\tprivate CorsWebFilter getCorsFilter() {\n\t\t\tif (this.corsFilter != null) {\n\t\t\t\treturn this.corsFilter;\n\t\t\t}\n\t\t\tCorsConfigurationSource source = getBeanOrNull(CorsConfigurationSource.class);\n\t\t\tif (source == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tCorsProcessor processor = getBeanOrNull(CorsProcessor.class);\n\t\t\tif (processor == null) {\n\t\t\t\tprocessor = new DefaultCorsProcessor();\n\t\t\t}\n\t\t\tthis.corsFilter = new CorsWebFilter(source, processor);\n\t\t\treturn this.corsFilter;\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures X509 authentication\n\t *\n\t * @author Alexey Nesterov\n\t * @since 5.2\n\t * @see #x509(Customizer)\n\t */\n\tpublic final class X509Spec {\n\n\t\tprivate X509PrincipalExtractor principalExtractor;\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate ServerAuthenticationConverter serverAuthenticationConverter;\n\n\t\tprivate X509Spec() {\n\t\t}\n\n\t\tpublic X509Spec principalExtractor(X509PrincipalExtractor principalExtractor) {\n\t\t\tthis.principalExtractor = principalExtractor;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic X509Spec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic X509Spec serverAuthenticationConverter(ServerAuthenticationConverter serverAuthenticationConverter) {\n\t\t\tthis.serverAuthenticationConverter = serverAuthenticationConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tReactiveAuthenticationManager authenticationManager = getAuthenticationManager();\n\t\t\tX509PrincipalExtractor principalExtractor = getPrincipalExtractor();\n\t\t\tServerAuthenticationConverter converter = getServerAuthenticationConverter(principalExtractor);\n\t\t\tAuthenticationWebFilter filter = new AuthenticationWebFilter(authenticationManager);\n\t\t\tfilter.setServerAuthenticationConverter(converter);\n\t\t\thttp.addFilterAt(filter, SecurityWebFiltersOrder.AUTHENTICATION);\n\t\t}\n\n\t\tprivate X509PrincipalExtractor getPrincipalExtractor() {\n\t\t\tif (this.principalExtractor != null) {\n\t\t\t\treturn this.principalExtractor;\n\t\t\t}\n\t\t\treturn new SubjectX500PrincipalExtractor();\n\t\t}\n\n\t\tprivate ReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\tif (this.authenticationManager != null) {\n\t\t\t\treturn this.authenticationManager;\n\t\t\t}\n\t\t\tReactiveUserDetailsService userDetailsService = getBean(ReactiveUserDetailsService.class);\n\t\t\treturn new ReactivePreAuthenticatedAuthenticationManager(userDetailsService);\n\t\t}\n\n\t\tprivate ServerAuthenticationConverter getServerAuthenticationConverter(X509PrincipalExtractor extractor) {\n\t\t\tif (this.serverAuthenticationConverter != null) {\n\t\t\t\treturn this.serverAuthenticationConverter;\n\t\t\t}\n\t\t\treturn new ServerX509AuthenticationConverter(extractor);\n\t\t}\n\n\t}\n\n\tpublic final class OAuth2LoginSpec {\n\n\t\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate ServerSecurityContextRepository securityContextRepository;\n\n\t\tprivate ServerAuthenticationConverter authenticationConverter;\n\n\t\tprivate ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\t\tprivate ServerRedirectStrategy authorizationRedirectStrategy;\n\n\t\tprivate ServerWebExchangeMatcher authenticationMatcher;\n\n\t\tprivate ReactiveOidcSessionRegistry oidcSessionRegistry;\n\n\t\tprivate final RedirectServerAuthenticationSuccessHandler defaultAuthenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler();\n\n\t\tprivate final List<ServerAuthenticationSuccessHandler> defaultSuccessHandlers = new ArrayList<>(\n\t\t\t\tList.of(this.defaultAuthenticationSuccessHandler));\n\n\t\tprivate List<ServerAuthenticationSuccessHandler> authenticationSuccessHandlers = new ArrayList<>();\n\n\t\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler;\n\n\t\tprivate String loginPage;\n\n\t\tprivate OAuth2LoginSpec() {\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ReactiveAuthenticationManager} to use. The default is\n\t\t * {@link OAuth2AuthorizationCodeReactiveAuthenticationManager}\n\t\t * @param authenticationManager the manager to use\n\t\t * @return the {@link OAuth2LoginSpec} to customize\n\t\t */\n\t\tpublic OAuth2LoginSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ServerSecurityContextRepository} used to save the\n\t\t * {@code Authentication}. Defaults to\n\t\t * {@link WebSessionServerSecurityContextRepository}.\n\t\t * @param securityContextRepository the repository to use\n\t\t * @return the {@link OAuth2LoginSpec} to continue configuring\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2LoginSpec securityContextRepository(ServerSecurityContextRepository securityContextRepository) {\n\t\t\tthis.securityContextRepository = securityContextRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ReactiveOidcSessionRegistry} to use when logins use OIDC.\n\t\t * Default is to look the value up as a Bean, or else use an\n\t\t * {@link InMemoryReactiveOidcSessionRegistry}.\n\t\t * @param oidcSessionRegistry the registry to use\n\t\t * @return the {@link OidcLogoutSpec} to customize\n\t\t * @since 6.2\n\t\t */\n\t\tpublic OAuth2LoginSpec oidcSessionRegistry(ReactiveOidcSessionRegistry oidcSessionRegistry) {\n\t\t\tAssert.notNull(oidcSessionRegistry, \"oidcSessionRegistry cannot be null\");\n\t\t\tthis.oidcSessionRegistry = oidcSessionRegistry;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ServerAuthenticationSuccessHandler} used after authentication\n\t\t * success. Defaults to {@link RedirectServerAuthenticationSuccessHandler}\n\t\t * redirecting to \"/\". Note that this method clears previously added success\n\t\t * handlers via {@link #authenticationSuccessHandler(Consumer)}\n\t\t * @param authenticationSuccessHandler the success handler to use\n\t\t * @return the {@link OAuth2LoginSpec} to customize\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2LoginSpec authenticationSuccessHandler(\n\t\t\t\tServerAuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\t\tauthenticationSuccessHandler((handlers) -> {\n\t\t\t\thandlers.clear();\n\t\t\t\thandlers.add(authenticationSuccessHandler);\n\t\t\t});\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows customizing the list of {@link ServerAuthenticationSuccessHandler}. The\n\t\t * default list contains a {@link RedirectServerAuthenticationSuccessHandler} that\n\t\t * redirects to \"/\".\n\t\t * @param handlersConsumer the handlers consumer\n\t\t * @return the {@link OAuth2LoginSpec} to continue configuring\n\t\t * @since 6.3\n\t\t */\n\t\tpublic OAuth2LoginSpec authenticationSuccessHandler(\n\t\t\t\tConsumer<List<ServerAuthenticationSuccessHandler>> handlersConsumer) {\n\t\t\tAssert.notNull(handlersConsumer, \"handlersConsumer cannot be null\");\n\t\t\thandlersConsumer.accept(this.authenticationSuccessHandlers);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ServerAuthenticationFailureHandler} used after authentication\n\t\t * failure. Defaults to {@link RedirectServerAuthenticationFailureHandler}\n\t\t * redirecting to \"/login?error\".\n\t\t * @param authenticationFailureHandler the failure handler to use\n\t\t * @return the {@link OAuth2LoginSpec} to customize\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2LoginSpec authenticationFailureHandler(\n\t\t\t\tServerAuthenticationFailureHandler authenticationFailureHandler) {\n\t\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Gets the {@link ReactiveAuthenticationManager} to use. First tries an\n\t\t * explicitly configured manager, and defaults to\n\t\t * {@link OAuth2AuthorizationCodeReactiveAuthenticationManager}\n\t\t * @return the {@link ReactiveAuthenticationManager} to use\n\t\t */\n\t\tprivate ReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\tif (this.authenticationManager == null) {\n\t\t\t\tthis.authenticationManager = createDefault();\n\t\t\t}\n\t\t\treturn this.authenticationManager;\n\t\t}\n\n\t\tprivate ReactiveAuthenticationManager createDefault() {\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> client = getAccessTokenResponseClient();\n\t\t\tOAuth2LoginReactiveAuthenticationManager oauth2Manager = new OAuth2LoginReactiveAuthenticationManager(\n\t\t\t\t\tclient, getOauth2UserService());\n\t\t\tGrantedAuthoritiesMapper authoritiesMapper = getBeanOrNull(GrantedAuthoritiesMapper.class);\n\t\t\tif (authoritiesMapper != null) {\n\t\t\t\toauth2Manager.setAuthoritiesMapper(authoritiesMapper);\n\t\t\t}\n\t\t\tboolean oidcAuthenticationProviderEnabled = ClassUtils\n\t\t\t\t.isPresent(\"org.springframework.security.oauth2.jwt.JwtDecoder\", this.getClass().getClassLoader());\n\t\t\tif (!oidcAuthenticationProviderEnabled) {\n\t\t\t\treturn oauth2Manager;\n\t\t\t}\n\t\t\tOidcAuthorizationCodeReactiveAuthenticationManager oidc = new OidcAuthorizationCodeReactiveAuthenticationManager(\n\t\t\t\t\tclient, getOidcUserService());\n\t\t\tResolvableType type = ResolvableType.forClassWithGenerics(ReactiveJwtDecoderFactory.class,\n\t\t\t\t\tClientRegistration.class);\n\t\t\tReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = getBeanOrNull(type);\n\t\t\tif (jwtDecoderFactory != null) {\n\t\t\t\toidc.setJwtDecoderFactory(jwtDecoderFactory);\n\t\t\t}\n\t\t\tif (authoritiesMapper != null) {\n\t\t\t\toidc.setAuthoritiesMapper(authoritiesMapper);\n\t\t\t}\n\t\t\treturn new DelegatingReactiveAuthenticationManager(oidc, oauth2Manager);\n\t\t}\n\n\t\t/**\n\t\t * Sets the converter to use\n\t\t * @param authenticationConverter the converter to use\n\t\t * @return the {@link OAuth2LoginSpec} to customize\n\t\t */\n\t\tpublic OAuth2LoginSpec authenticationConverter(ServerAuthenticationConverter authenticationConverter) {\n\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ServerAuthenticationConverter getAuthenticationConverter(\n\t\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\t\tif (this.authenticationConverter != null) {\n\t\t\t\treturn this.authenticationConverter;\n\t\t\t}\n\t\t\tServerOAuth2AuthorizationCodeAuthenticationTokenConverter delegate = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(\n\t\t\t\t\tclientRegistrationRepository);\n\t\t\tdelegate.setAuthorizationRequestRepository(getAuthorizationRequestRepository());\n\t\t\tServerAuthenticationConverter authenticationConverter = (exchange) -> delegate.convert(exchange)\n\t\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t\t(e) -> new OAuth2AuthenticationException(e.getError(), e.getError().toString()));\n\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t\treturn authenticationConverter;\n\t\t}\n\n\t\tpublic OAuth2LoginSpec clientRegistrationRepository(\n\t\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic OAuth2LoginSpec authorizedClientService(ReactiveOAuth2AuthorizedClientService authorizedClientService) {\n\t\t\tthis.authorizedClientRepository = new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(\n\t\t\t\t\tauthorizedClientService);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic OAuth2LoginSpec authorizedClientRepository(\n\t\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the repository to use for storing {@link OAuth2AuthorizationRequest}'s.\n\t\t * @param authorizationRequestRepository the repository to use for storing\n\t\t * {@link OAuth2AuthorizationRequest}'s\n\t\t * @return the {@link OAuth2LoginSpec} for further configuration\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2LoginSpec authorizationRequestRepository(\n\t\t\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the resolver used for resolving {@link OAuth2AuthorizationRequest}'s.\n\t\t * @param authorizationRequestResolver the resolver used for resolving\n\t\t * {@link OAuth2AuthorizationRequest}'s\n\t\t * @return the {@link OAuth2LoginSpec} for further configuration\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2LoginSpec authorizationRequestResolver(\n\t\t\t\tServerOAuth2AuthorizationRequestResolver authorizationRequestResolver) {\n\t\t\tthis.authorizationRequestResolver = authorizationRequestResolver;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the redirect strategy for Authorization Endpoint redirect URI.\n\t\t * @param authorizationRedirectStrategy the redirect strategy\n\t\t * @return the {@link OAuth2LoginSpec} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginSpec authorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {\n\t\t\tthis.authorizationRedirectStrategy = authorizationRedirectStrategy;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link ServerWebExchangeMatcher matcher} used for determining if the\n\t\t * request is an authentication request.\n\t\t * @param authenticationMatcher the {@link ServerWebExchangeMatcher matcher} used\n\t\t * for determining if the request is an authentication request\n\t\t * @return the {@link OAuth2LoginSpec} for further configuration\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2LoginSpec authenticationMatcher(ServerWebExchangeMatcher authenticationMatcher) {\n\t\t\tthis.authenticationMatcher = authenticationMatcher;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ServerWebExchangeMatcher getAuthenticationMatcher() {\n\t\t\tif (this.authenticationMatcher == null) {\n\t\t\t\tthis.authenticationMatcher = createAttemptAuthenticationRequestMatcher();\n\t\t\t}\n\t\t\treturn this.authenticationMatcher;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the URL to send users to if login is required. A default login page\n\t\t * will be generated when this attribute is not specified.\n\t\t * @param loginPage the URL to send users to if login is required\n\t\t * @return the {@link OAuth2LoginSpec} for further configuration\n\t\t * @since 6.4\n\t\t */\n\t\tpublic OAuth2LoginSpec loginPage(String loginPage) {\n\t\t\tAssert.hasText(loginPage, \"loginPage cannot be empty\");\n\t\t\tthis.loginPage = loginPage;\n\t\t\treturn this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository = getClientRegistrationRepository();\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = getAuthorizedClientRepository();\n\t\t\tOAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = getRedirectWebFilter();\n\t\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = getAuthorizationRequestRepository();\n\t\t\toauthRedirectFilter.setAuthorizationRequestRepository(authorizationRequestRepository);\n\t\t\toauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());\n\t\t\toauthRedirectFilter.setRequestCache(http.requestCache.requestCache);\n\n\t\t\tReactiveAuthenticationManager manager = getAuthenticationManager();\n\t\t\tReactiveOidcSessionRegistry sessionRegistry = getOidcSessionRegistry();\n\t\t\tAuthenticationWebFilter authenticationFilter = (sessionRegistry != null)\n\t\t\t\t\t? new OidcSessionRegistryAuthenticationWebFilter(manager, authorizedClientRepository,\n\t\t\t\t\t\t\tsessionRegistry)\n\t\t\t\t\t: new OAuth2LoginAuthenticationWebFilter(manager, authorizedClientRepository);\n\t\t\tauthenticationFilter.setRequiresAuthenticationMatcher(getAuthenticationMatcher());\n\t\t\tauthenticationFilter\n\t\t\t\t.setServerAuthenticationConverter(getAuthenticationConverter(clientRegistrationRepository));\n\t\t\tauthenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));\n\t\t\tauthenticationFilter.setAuthenticationFailureHandler(getAuthenticationFailureHandler());\n\t\t\tauthenticationFilter.setSecurityContextRepository(this.securityContextRepository);\n\n\t\t\tsetDefaultEntryPoints(http);\n\t\t\tif (sessionRegistry != null) {\n\t\t\t\thttp.addFilterAfter(new OidcSessionRegistryWebFilter(sessionRegistry),\n\t\t\t\t\t\tSecurityWebFiltersOrder.HTTP_HEADERS_WRITER);\n\t\t\t}\n\t\t\thttp.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);\n\t\t\thttp.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);\n\t\t}\n\n\t\tprivate void setDefaultEntryPoints(ServerHttpSecurity http) {\n\t\t\tMediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(\n\t\t\t\t\tMediaType.APPLICATION_XHTML_XML, new MediaType(\"image\", \"*\"), MediaType.TEXT_HTML,\n\t\t\t\t\tMediaType.TEXT_PLAIN);\n\t\t\thtmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\t\tServerWebExchangeMatcher xhrMatcher = (exchange) -> {\n\t\t\t\tif (exchange.getRequest().getHeaders().getOrEmpty(\"X-Requested-With\").contains(\"XMLHttpRequest\")) {\n\t\t\t\t\treturn ServerWebExchangeMatcher.MatchResult.match();\n\t\t\t\t}\n\t\t\t\treturn ServerWebExchangeMatcher.MatchResult.notMatch();\n\t\t\t};\n\t\t\tServerWebExchangeMatcher notXhrMatcher = new NegatedServerWebExchangeMatcher(xhrMatcher);\n\t\t\tServerWebExchangeMatcher defaultEntryPointMatcher = new AndServerWebExchangeMatcher(notXhrMatcher,\n\t\t\t\t\thtmlMatcher);\n\t\t\tString loginPage = \"/login\";\n\t\t\tif (StringUtils.hasText(this.loginPage)) {\n\t\t\t\tloginPage = this.loginPage;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tMap<String, String> urlToText = http.oauth2Login.getLinks();\n\t\t\t\tString providerLoginPage = null;\n\t\t\t\tif (urlToText.size() == 1) {\n\t\t\t\t\tproviderLoginPage = urlToText.keySet().iterator().next();\n\t\t\t\t}\n\t\t\t\tif (providerLoginPage != null) {\n\t\t\t\t\tServerWebExchangeMatcher loginPageMatcher = new PathPatternParserServerWebExchangeMatcher(\n\t\t\t\t\t\t\tloginPage);\n\t\t\t\t\tServerWebExchangeMatcher faviconMatcher = new PathPatternParserServerWebExchangeMatcher(\n\t\t\t\t\t\t\t\"/favicon.ico\");\n\t\t\t\t\tServerWebExchangeMatcher defaultLoginPageMatcher = new AndServerWebExchangeMatcher(\n\t\t\t\t\t\t\tnew OrServerWebExchangeMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);\n\n\t\t\t\t\tServerWebExchangeMatcher matcher = new AndServerWebExchangeMatcher(notXhrMatcher,\n\t\t\t\t\t\t\tnew NegatedServerWebExchangeMatcher(defaultLoginPageMatcher));\n\t\t\t\t\tRedirectServerAuthenticationEntryPoint entryPoint = new RedirectServerAuthenticationEntryPoint(\n\t\t\t\t\t\t\tproviderLoginPage);\n\t\t\t\t\tentryPoint.setRequestCache(http.requestCache.requestCache);\n\t\t\t\t\thttp.defaultEntryPoints.add(new DelegateEntry(matcher, entryPoint));\n\t\t\t\t}\n\t\t\t}\n\t\t\tRedirectServerAuthenticationEntryPoint defaultEntryPoint = new RedirectServerAuthenticationEntryPoint(\n\t\t\t\t\tloginPage);\n\t\t\tdefaultEntryPoint.setRequestCache(http.requestCache.requestCache);\n\t\t\thttp.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint));\n\t\t}\n\n\t\tprivate ReactiveOidcSessionRegistry getOidcSessionRegistry() {\n\t\t\tif (ServerHttpSecurity.this.oidcLogout == null && this.oidcSessionRegistry == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (this.oidcSessionRegistry == null) {\n\t\t\t\tthis.oidcSessionRegistry = getBeanOrNull(ReactiveOidcSessionRegistry.class);\n\t\t\t}\n\t\t\tif (this.oidcSessionRegistry == null) {\n\t\t\t\tthis.oidcSessionRegistry = new InMemoryReactiveOidcSessionRegistry();\n\t\t\t}\n\t\t\treturn this.oidcSessionRegistry;\n\t\t}\n\n\t\tprivate ServerAuthenticationSuccessHandler getAuthenticationSuccessHandler(ServerHttpSecurity http) {\n\t\t\tthis.defaultAuthenticationSuccessHandler.setRequestCache(http.requestCache.requestCache);\n\t\t\tif (this.authenticationSuccessHandlers.isEmpty()) {\n\t\t\t\treturn new DelegatingServerAuthenticationSuccessHandler(this.defaultSuccessHandlers);\n\t\t\t}\n\t\t\treturn new DelegatingServerAuthenticationSuccessHandler(this.authenticationSuccessHandlers);\n\t\t}\n\n\t\tprivate ServerAuthenticationFailureHandler getAuthenticationFailureHandler() {\n\t\t\tif (this.authenticationFailureHandler == null) {\n\t\t\t\tthis.authenticationFailureHandler = new RedirectServerAuthenticationFailureHandler(\"/login?error\");\n\t\t\t}\n\t\t\treturn this.authenticationFailureHandler;\n\t\t}\n\n\t\tprivate ServerWebExchangeMatcher createAttemptAuthenticationRequestMatcher() {\n\t\t\treturn new PathPatternParserServerWebExchangeMatcher(\"/login/oauth2/code/{registrationId}\");\n\t\t}\n\n\t\tprivate ReactiveOAuth2UserService<OidcUserRequest, OidcUser> getOidcUserService() {\n\t\t\tResolvableType type = ResolvableType.forClassWithGenerics(ReactiveOAuth2UserService.class,\n\t\t\t\t\tOidcUserRequest.class, OidcUser.class);\n\t\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> bean = getBeanOrNull(type);\n\t\t\tif (bean != null) {\n\t\t\t\treturn bean;\n\t\t\t}\n\t\t\tOidcReactiveOAuth2UserService reactiveOAuth2UserService = new OidcReactiveOAuth2UserService();\n\t\t\treactiveOAuth2UserService.setOauth2UserService(getOauth2UserService());\n\t\t\treturn reactiveOAuth2UserService;\n\t\t}\n\n\t\tprivate ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> getOauth2UserService() {\n\t\t\tResolvableType type = ResolvableType.forClassWithGenerics(ReactiveOAuth2UserService.class,\n\t\t\t\t\tOAuth2UserRequest.class, OAuth2User.class);\n\t\t\tReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> bean = getBeanOrNull(type);\n\t\t\tif (bean != null) {\n\t\t\t\treturn bean;\n\t\t\t}\n\t\t\treturn new DefaultReactiveOAuth2UserService();\n\t\t}\n\n\t\tprivate Map<String, String> getLinks() {\n\t\t\tIterable<ClientRegistration> registrations = getBeanOrNull(\n\t\t\t\t\tResolvableType.forClassWithGenerics(Iterable.class, ClientRegistration.class));\n\t\t\tif (registrations == null) {\n\t\t\t\treturn Collections.emptyMap();\n\t\t\t}\n\t\t\tMap<String, String> result = new HashMap<>();\n\t\t\tregistrations.iterator().forEachRemaining((r) -> {\n\t\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(r.getAuthorizationGrantType())) {\n\t\t\t\t\tresult.put(\"/oauth2/authorization/\" + r.getRegistrationId(), r.getClientName());\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> getAccessTokenResponseClient() {\n\t\t\tResolvableType type = ResolvableType.forClassWithGenerics(ReactiveOAuth2AccessTokenResponseClient.class,\n\t\t\t\t\tOAuth2AuthorizationCodeGrantRequest.class);\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> bean = getBeanOrNull(type);\n\t\t\tif (bean != null) {\n\t\t\t\treturn bean;\n\t\t\t}\n\t\t\treturn new WebClientReactiveAuthorizationCodeTokenResponseClient();\n\t\t}\n\n\t\tprivate ReactiveClientRegistrationRepository getClientRegistrationRepository() {\n\t\t\tif (this.clientRegistrationRepository == null) {\n\t\t\t\tthis.clientRegistrationRepository = getBeanOrNull(ReactiveClientRegistrationRepository.class);\n\t\t\t}\n\t\t\treturn this.clientRegistrationRepository;\n\t\t}\n\n\t\tprivate OAuth2AuthorizationRequestRedirectWebFilter getRedirectWebFilter() {\n\t\t\tServerOAuth2AuthorizationRequestResolver authorizationRequestResolver = this.authorizationRequestResolver;\n\t\t\tif (authorizationRequestResolver == null) {\n\t\t\t\tauthorizationRequestResolver = getBeanOrNull(ServerOAuth2AuthorizationRequestResolver.class);\n\t\t\t}\n\t\t\tif (authorizationRequestResolver != null) {\n\t\t\t\treturn new OAuth2AuthorizationRequestRedirectWebFilter(authorizationRequestResolver);\n\t\t\t}\n\t\t\treturn new OAuth2AuthorizationRequestRedirectWebFilter(getClientRegistrationRepository());\n\t\t}\n\n\t\tprivate ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository() {\n\t\t\tServerOAuth2AuthorizedClientRepository result = this.authorizedClientRepository;\n\t\t\tif (result == null) {\n\t\t\t\tresult = getBeanOrNull(ServerOAuth2AuthorizedClientRepository.class);\n\t\t\t}\n\t\t\tif (result == null) {\n\t\t\t\tReactiveOAuth2AuthorizedClientService authorizedClientService = getAuthorizedClientService();\n\t\t\t\tif (authorizedClientService != null) {\n\t\t\t\t\tresult = new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> getAuthorizationRequestRepository() {\n\t\t\tif (this.authorizationRequestRepository == null) {\n\t\t\t\tthis.authorizationRequestRepository = new WebSessionOAuth2ServerAuthorizationRequestRepository();\n\t\t\t}\n\t\t\treturn this.authorizationRequestRepository;\n\t\t}\n\n\t\tprivate ServerRedirectStrategy getAuthorizationRedirectStrategy() {\n\t\t\tif (this.authorizationRedirectStrategy == null) {\n\t\t\t\tthis.authorizationRedirectStrategy = new DefaultServerRedirectStrategy();\n\t\t\t}\n\t\t\treturn this.authorizationRedirectStrategy;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AuthorizedClientService getAuthorizedClientService() {\n\t\t\tReactiveOAuth2AuthorizedClientService bean = getBeanOrNull(ReactiveOAuth2AuthorizedClientService.class);\n\t\t\tif (bean != null) {\n\t\t\t\treturn bean;\n\t\t\t}\n\t\t\treturn new InMemoryReactiveOAuth2AuthorizedClientService(getClientRegistrationRepository());\n\t\t}\n\n\t\tprivate static final class OidcSessionRegistryWebFilter implements WebFilter {\n\n\t\t\tprivate final ReactiveOidcSessionRegistry oidcSessionRegistry;\n\n\t\t\tOidcSessionRegistryWebFilter(ReactiveOidcSessionRegistry oidcSessionRegistry) {\n\t\t\t\tthis.oidcSessionRegistry = oidcSessionRegistry;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\t\treturn chain.filter(new OidcSessionRegistryServerWebExchange(exchange));\n\t\t\t}\n\n\t\t\tprivate final class OidcSessionRegistryServerWebExchange extends ServerWebExchangeDecorator {\n\n\t\t\t\tprivate final Mono<WebSession> sessionMono;\n\n\t\t\t\tprotected OidcSessionRegistryServerWebExchange(ServerWebExchange delegate) {\n\t\t\t\t\tsuper(delegate);\n\t\t\t\t\tthis.sessionMono = delegate.getSession().map(OidcSessionRegistryWebSession::new);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Mono<WebSession> getSession() {\n\t\t\t\t\treturn this.sessionMono;\n\t\t\t\t}\n\n\t\t\t\tprivate final class OidcSessionRegistryWebSession implements WebSession {\n\n\t\t\t\t\tprivate final WebSession session;\n\n\t\t\t\t\tOidcSessionRegistryWebSession(WebSession session) {\n\t\t\t\t\t\tthis.session = session;\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic String getId() {\n\t\t\t\t\t\treturn this.session.getId();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Map<String, Object> getAttributes() {\n\t\t\t\t\t\treturn this.session.getAttributes();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void start() {\n\t\t\t\t\t\tthis.session.start();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic boolean isStarted() {\n\t\t\t\t\t\treturn this.session.isStarted();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Mono<Void> changeSessionId() {\n\t\t\t\t\t\tString currentId = this.session.getId();\n\t\t\t\t\t\treturn this.session.changeSessionId()\n\t\t\t\t\t\t\t.then(Mono.defer(() -> OidcSessionRegistryWebFilter.this.oidcSessionRegistry\n\t\t\t\t\t\t\t\t.removeSessionInformation(currentId)\n\t\t\t\t\t\t\t\t.flatMap((information) -> {\n\t\t\t\t\t\t\t\t\tinformation = information.withSessionId(this.session.getId());\n\t\t\t\t\t\t\t\t\treturn OidcSessionRegistryWebFilter.this.oidcSessionRegistry\n\t\t\t\t\t\t\t\t\t\t.saveSessionInformation(information);\n\t\t\t\t\t\t\t\t})));\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Mono<Void> invalidate() {\n\t\t\t\t\t\tString currentId = this.session.getId();\n\t\t\t\t\t\treturn this.session.invalidate()\n\t\t\t\t\t\t\t.then(Mono.defer(() -> OidcSessionRegistryWebFilter.this.oidcSessionRegistry\n\t\t\t\t\t\t\t\t.removeSessionInformation(currentId)\n\t\t\t\t\t\t\t\t.then(Mono.empty())));\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Mono<Void> save() {\n\t\t\t\t\t\treturn this.session.save();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic boolean isExpired() {\n\t\t\t\t\t\treturn this.session.isExpired();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Instant getCreationTime() {\n\t\t\t\t\t\treturn this.session.getCreationTime();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Instant getLastAccessTime() {\n\t\t\t\t\t\treturn this.session.getLastAccessTime();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void setMaxIdleTime(Duration maxIdleTime) {\n\t\t\t\t\t\tthis.session.setMaxIdleTime(maxIdleTime);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Duration getMaxIdleTime() {\n\t\t\t\t\t\treturn this.session.getMaxIdleTime();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tstatic final class OidcSessionRegistryAuthenticationWebFilter extends OAuth2LoginAuthenticationWebFilter {\n\n\t\t\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t\t\tprivate final ReactiveOidcSessionRegistry oidcSessionRegistry;\n\n\t\t\tOidcSessionRegistryAuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager,\n\t\t\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository,\n\t\t\t\t\tReactiveOidcSessionRegistry oidcSessionRegistry) {\n\t\t\t\tsuper(authenticationManager, authorizedClientRepository);\n\t\t\t\tthis.oidcSessionRegistry = oidcSessionRegistry;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected Mono<Void> onAuthenticationSuccess(Authentication authentication,\n\t\t\t\t\tWebFilterExchange webFilterExchange) {\n\t\t\t\tif (!(authentication.getPrincipal() instanceof OidcUser user)) {\n\t\t\t\t\treturn super.onAuthenticationSuccess(authentication, webFilterExchange);\n\t\t\t\t}\n\t\t\t\treturn webFilterExchange.getExchange().getSession().doOnNext((session) -> {\n\t\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\t\tthis.logger.trace(String.format(\"Linking a provider [%s] session to this client's session\",\n\t\t\t\t\t\t\t\tuser.getIssuer()));\n\t\t\t\t\t}\n\t\t\t\t}).flatMap((session) -> {\n\t\t\t\t\tMono<CsrfToken> csrfToken = webFilterExchange.getExchange().getAttribute(CsrfToken.class.getName());\n\t\t\t\t\treturn (csrfToken != null)\n\t\t\t\t\t\t\t? csrfToken.map((token) -> new OidcSessionInformation(session.getId(),\n\t\t\t\t\t\t\t\t\tMap.of(token.getHeaderName(), token.getToken()), user))\n\t\t\t\t\t\t\t: Mono.just(new OidcSessionInformation(session.getId(), Map.of(), user));\n\t\t\t\t})\n\t\t\t\t\t.flatMap(this.oidcSessionRegistry::saveSessionInformation)\n\t\t\t\t\t.then(super.onAuthenticationSuccess(authentication, webFilterExchange));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tpublic final class OAuth2ClientSpec {\n\n\t\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t\tprivate ServerAuthenticationConverter authenticationConverter;\n\n\t\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\t\tprivate ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\t\tprivate ServerRedirectStrategy authorizationRedirectStrategy;\n\n\t\tprivate OAuth2ClientSpec() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the converter to use\n\t\t * @param authenticationConverter the converter to use\n\t\t * @return the {@link OAuth2ClientSpec} to customize\n\t\t */\n\t\tpublic OAuth2ClientSpec authenticationConverter(ServerAuthenticationConverter authenticationConverter) {\n\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ServerAuthenticationConverter getAuthenticationConverter() {\n\t\t\tif (this.authenticationConverter == null) {\n\t\t\t\tServerOAuth2AuthorizationCodeAuthenticationTokenConverter authenticationConverter = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(\n\t\t\t\t\t\tgetClientRegistrationRepository());\n\t\t\t\tauthenticationConverter.setAuthorizationRequestRepository(getAuthorizationRequestRepository());\n\t\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t\t}\n\t\t\treturn this.authenticationConverter;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ReactiveAuthenticationManager} to use. The default is\n\t\t * {@link OAuth2AuthorizationCodeReactiveAuthenticationManager}\n\t\t * @param authenticationManager the manager to use\n\t\t * @return the {@link OAuth2ClientSpec} to customize\n\t\t */\n\t\tpublic OAuth2ClientSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Gets the {@link ReactiveAuthenticationManager} to use. First tries an\n\t\t * explicitly configured manager, and defaults to\n\t\t * {@link OAuth2AuthorizationCodeReactiveAuthenticationManager}\n\t\t * @return the {@link ReactiveAuthenticationManager} to use\n\t\t */\n\t\tprivate ReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\tif (this.authenticationManager == null) {\n\t\t\t\tthis.authenticationManager = new OAuth2AuthorizationCodeReactiveAuthenticationManager(\n\t\t\t\t\t\tgetAuthorizationCodeTokenResponseClient());\n\t\t\t}\n\t\t\treturn this.authenticationManager;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> getAuthorizationCodeTokenResponseClient() {\n\t\t\tResolvableType resolvableType = ResolvableType.forClassWithGenerics(\n\t\t\t\t\tReactiveOAuth2AccessTokenResponseClient.class, OAuth2AuthorizationCodeGrantRequest.class);\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient = getBeanOrNull(\n\t\t\t\t\tresolvableType);\n\t\t\tif (accessTokenResponseClient == null) {\n\t\t\t\taccessTokenResponseClient = new WebClientReactiveAuthorizationCodeTokenResponseClient();\n\t\t\t}\n\t\t\treturn accessTokenResponseClient;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ReactiveClientRegistrationRepository}. Default is to look\n\t\t * the value up as a Bean.\n\t\t * @param clientRegistrationRepository the repository to use\n\t\t * @return the {@link OAuth2ClientSpec} to customize\n\t\t */\n\t\tpublic OAuth2ClientSpec clientRegistrationRepository(\n\t\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ReactiveClientRegistrationRepository}. Default is to look\n\t\t * the value up as a Bean.\n\t\t * @param authorizedClientRepository the repository to use\n\t\t * @return the {@link OAuth2ClientSpec} to customize\n\t\t */\n\t\tpublic OAuth2ClientSpec authorizedClientRepository(\n\t\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the repository to use for storing {@link OAuth2AuthorizationRequest}'s.\n\t\t * @param authorizationRequestRepository the repository to use for storing\n\t\t * {@link OAuth2AuthorizationRequest}'s\n\t\t * @return the {@link OAuth2ClientSpec} to customize\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2ClientSpec authorizationRequestRepository(\n\t\t\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> getAuthorizationRequestRepository() {\n\t\t\tif (this.authorizationRequestRepository == null) {\n\t\t\t\tthis.authorizationRequestRepository = new WebSessionOAuth2ServerAuthorizationRequestRepository();\n\t\t\t}\n\t\t\treturn this.authorizationRequestRepository;\n\t\t}\n\n\t\t/**\n\t\t * Sets the resolver used for resolving {@link OAuth2AuthorizationRequest}'s.\n\t\t * @param authorizationRequestResolver the resolver used for resolving\n\t\t * {@link OAuth2AuthorizationRequest}'s\n\t\t * @return the {@link OAuth2ClientSpec} to customize\n\t\t * @since 6.1\n\t\t */\n\t\tpublic OAuth2ClientSpec authorizationRequestResolver(\n\t\t\t\tServerOAuth2AuthorizationRequestResolver authorizationRequestResolver) {\n\t\t\tthis.authorizationRequestResolver = authorizationRequestResolver;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate OAuth2AuthorizationRequestRedirectWebFilter getRedirectWebFilter() {\n\t\t\tif (this.authorizationRequestResolver != null) {\n\t\t\t\treturn new OAuth2AuthorizationRequestRedirectWebFilter(this.authorizationRequestResolver);\n\t\t\t}\n\t\t\treturn new OAuth2AuthorizationRequestRedirectWebFilter(getClientRegistrationRepository());\n\t\t}\n\n\t\t/**\n\t\t * Sets the redirect strategy for Authorization Endpoint redirect URI.\n\t\t * @param authorizationRedirectStrategy the redirect strategy\n\t\t * @return the {@link OAuth2ClientSpec} for further configuration\n\t\t */\n\t\tpublic OAuth2ClientSpec authorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {\n\t\t\tthis.authorizationRedirectStrategy = authorizationRedirectStrategy;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ServerRedirectStrategy getAuthorizationRedirectStrategy() {\n\t\t\tif (this.authorizationRedirectStrategy == null) {\n\t\t\t\tthis.authorizationRedirectStrategy = new DefaultServerRedirectStrategy();\n\t\t\t}\n\t\t\treturn this.authorizationRedirectStrategy;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = getAuthorizedClientRepository();\n\t\t\tServerAuthenticationConverter authenticationConverter = getAuthenticationConverter();\n\t\t\tReactiveAuthenticationManager authenticationManager = getAuthenticationManager();\n\t\t\tOAuth2AuthorizationCodeGrantWebFilter codeGrantWebFilter = new OAuth2AuthorizationCodeGrantWebFilter(\n\t\t\t\t\tauthenticationManager, authenticationConverter, authorizedClientRepository);\n\t\t\tcodeGrantWebFilter.setAuthorizationRequestRepository(getAuthorizationRequestRepository());\n\t\t\tif (http.requestCache != null) {\n\t\t\t\tcodeGrantWebFilter.setRequestCache(http.requestCache.requestCache);\n\t\t\t}\n\n\t\t\tOAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = getRedirectWebFilter();\n\t\t\toauthRedirectFilter.setAuthorizationRequestRepository(getAuthorizationRequestRepository());\n\t\t\toauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());\n\t\t\tif (http.requestCache != null) {\n\t\t\t\toauthRedirectFilter.setRequestCache(http.requestCache.requestCache);\n\t\t\t}\n\n\t\t\thttp.addFilterAt(codeGrantWebFilter, SecurityWebFiltersOrder.OAUTH2_AUTHORIZATION_CODE);\n\t\t\thttp.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);\n\t\t}\n\n\t\tprivate ReactiveClientRegistrationRepository getClientRegistrationRepository() {\n\t\t\tif (this.clientRegistrationRepository != null) {\n\t\t\t\treturn this.clientRegistrationRepository;\n\t\t\t}\n\t\t\treturn getBeanOrNull(ReactiveClientRegistrationRepository.class);\n\t\t}\n\n\t\tprivate ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository() {\n\t\t\tif (this.authorizedClientRepository != null) {\n\t\t\t\treturn this.authorizedClientRepository;\n\t\t\t}\n\t\t\tServerOAuth2AuthorizedClientRepository result = getBeanOrNull(ServerOAuth2AuthorizedClientRepository.class);\n\t\t\tif (result != null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tReactiveOAuth2AuthorizedClientService authorizedClientService = getAuthorizedClientService();\n\t\t\tif (authorizedClientService != null) {\n\t\t\t\treturn new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate ReactiveOAuth2AuthorizedClientService getAuthorizedClientService() {\n\t\t\tReactiveOAuth2AuthorizedClientService bean = getBeanOrNull(ReactiveOAuth2AuthorizedClientService.class);\n\t\t\tif (bean != null) {\n\t\t\t\treturn bean;\n\t\t\t}\n\t\t\treturn new InMemoryReactiveOAuth2AuthorizedClientService(getClientRegistrationRepository());\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures OAuth2 Resource Server Support\n\t */\n\tpublic class OAuth2ResourceServerSpec {\n\n\t\tprivate ServerAuthenticationEntryPoint entryPoint = new BearerTokenServerAuthenticationEntryPoint();\n\n\t\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler;\n\n\t\tprivate ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();\n\n\t\tprivate ServerAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();\n\n\t\tprivate AuthenticationConverterServerWebExchangeMatcher authenticationConverterServerWebExchangeMatcher;\n\n\t\tprivate JwtSpec jwt;\n\n\t\tprivate OpaqueTokenSpec opaqueToken;\n\n\t\tprivate ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver;\n\n\t\t/**\n\t\t * Configures the {@link ServerAccessDeniedHandler} to use for requests\n\t\t * authenticating with\n\t\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\n\t\t * \"_blank\">Bearer Token</a>s. requests.\n\t\t * @param accessDeniedHandler the {@link ServerAccessDeniedHandler} to use\n\t\t * @return the {@link OAuth2ResourceServerSpec} for additional configuration\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2ResourceServerSpec accessDeniedHandler(ServerAccessDeniedHandler accessDeniedHandler) {\n\t\t\tAssert.notNull(accessDeniedHandler, \"accessDeniedHandler cannot be null\");\n\t\t\tthis.accessDeniedHandler = accessDeniedHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ServerAuthenticationEntryPoint} to use for requests\n\t\t * authenticating with\n\t\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\n\t\t * \"_blank\">Bearer Token</a>s.\n\t\t * @param entryPoint the {@link ServerAuthenticationEntryPoint} to use\n\t\t * @return the {@link OAuth2ResourceServerSpec} for additional configuration\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2ResourceServerSpec authenticationEntryPoint(ServerAuthenticationEntryPoint entryPoint) {\n\t\t\tAssert.notNull(entryPoint, \"entryPoint cannot be null\");\n\t\t\tthis.entryPoint = entryPoint;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic OAuth2ResourceServerSpec authenticationFailureHandler(\n\t\t\t\tServerAuthenticationFailureHandler authenticationFailureHandler) {\n\t\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ServerAuthenticationConverter} to use for requests\n\t\t * authenticating with\n\t\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\n\t\t * \"_blank\">Bearer Token</a>s.\n\t\t * @param bearerTokenConverter The {@link ServerAuthenticationConverter} to use\n\t\t * @return The {@link OAuth2ResourceServerSpec} for additional configuration\n\t\t * @since 5.2\n\t\t */\n\t\tpublic OAuth2ResourceServerSpec bearerTokenConverter(ServerAuthenticationConverter bearerTokenConverter) {\n\t\t\tAssert.notNull(bearerTokenConverter, \"bearerTokenConverter cannot be null\");\n\t\t\tthis.bearerTokenConverter = bearerTokenConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ReactiveAuthenticationManagerResolver}\n\t\t * @param authenticationManagerResolver the\n\t\t * {@link ReactiveAuthenticationManagerResolver}\n\t\t * @return the {@link OAuth2ResourceServerSpec} for additional configuration\n\t\t * @since 5.3\n\t\t */\n\t\tpublic OAuth2ResourceServerSpec authenticationManagerResolver(\n\t\t\t\tReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver) {\n\t\t\tAssert.notNull(authenticationManagerResolver, \"authenticationManagerResolver cannot be null\");\n\t\t\tthis.authenticationManagerResolver = authenticationManagerResolver;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Enables JWT Resource Server support.\n\t\t * @param jwtCustomizer the {@link Customizer} to provide more options for the\n\t\t * {@link JwtSpec}\n\t\t * @return the {@link OAuth2ResourceServerSpec} to customize\n\t\t */\n\t\tpublic OAuth2ResourceServerSpec jwt(Customizer<JwtSpec> jwtCustomizer) {\n\t\t\tif (this.jwt == null) {\n\t\t\t\tthis.jwt = new JwtSpec();\n\t\t\t}\n\t\t\tjwtCustomizer.customize(this.jwt);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Enables Opaque Token Resource Server support.\n\t\t * @param opaqueTokenCustomizer the {@link Customizer} to provide more options for\n\t\t * the {@link OpaqueTokenSpec}\n\t\t * @return the {@link OAuth2ResourceServerSpec} to customize\n\t\t */\n\t\tpublic OAuth2ResourceServerSpec opaqueToken(Customizer<OpaqueTokenSpec> opaqueTokenCustomizer) {\n\t\t\tif (this.opaqueToken == null) {\n\t\t\t\tthis.opaqueToken = new OpaqueTokenSpec();\n\t\t\t}\n\t\t\topaqueTokenCustomizer.customize(this.opaqueToken);\n\t\t\treturn this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tthis.authenticationConverterServerWebExchangeMatcher = new AuthenticationConverterServerWebExchangeMatcher(\n\t\t\t\t\tthis.bearerTokenConverter);\n\t\t\tregisterDefaultAccessDeniedHandler(http);\n\t\t\tregisterDefaultAuthenticationEntryPoint(http);\n\t\t\tregisterDefaultCsrfOverride(http);\n\t\t\tvalidateConfiguration();\n\t\t\tif (this.authenticationManagerResolver != null) {\n\t\t\t\tAuthenticationWebFilter oauth2 = new AuthenticationWebFilter(this.authenticationManagerResolver);\n\t\t\t\toauth2.setServerAuthenticationConverter(this.bearerTokenConverter);\n\t\t\t\toauth2.setAuthenticationFailureHandler(authenticationFailureHandler());\n\t\t\t\thttp.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);\n\t\t\t}\n\t\t\telse if (this.jwt != null) {\n\t\t\t\tthis.jwt.configure(http);\n\t\t\t}\n\t\t\telse if (this.opaqueToken != null) {\n\t\t\t\tthis.opaqueToken.configure(http);\n\t\t\t}\n\t\t}\n\n\t\tprivate void validateConfiguration() {\n\t\t\tif (this.authenticationManagerResolver == null) {\n\t\t\t\tAssert.state(this.jwt != null || this.opaqueToken != null,\n\t\t\t\t\t\t\"Jwt and Opaque Token are the only supported formats for bearer tokens \"\n\t\t\t\t\t\t\t\t+ \"in Spring Security and neither was found. Make sure to configure JWT \"\n\t\t\t\t\t\t\t\t+ \"via http.oauth2ResourceServer().jwt() or Opaque Tokens via \"\n\t\t\t\t\t\t\t\t+ \"http.oauth2ResourceServer().opaqueToken().\");\n\t\t\t\tAssert.state(this.jwt == null || this.opaqueToken == null,\n\t\t\t\t\t\t\"Spring Security only supports JWTs or Opaque Tokens, not both at the \" + \"same time.\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tAssert.state(this.jwt == null && this.opaqueToken == null,\n\t\t\t\t\t\t\"If an authenticationManagerResolver() is configured, then it takes \"\n\t\t\t\t\t\t\t\t+ \"precedence over any jwt() or opaqueToken() configuration.\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void registerDefaultAccessDeniedHandler(ServerHttpSecurity http) {\n\t\t\tif (http.exceptionHandling != null) {\n\t\t\t\thttp.defaultAccessDeniedHandlers\n\t\t\t\t\t.add(new ServerWebExchangeDelegatingServerAccessDeniedHandler.DelegateEntry(\n\t\t\t\t\t\t\tthis.authenticationConverterServerWebExchangeMatcher,\n\t\t\t\t\t\t\tOAuth2ResourceServerSpec.this.accessDeniedHandler));\n\t\t\t}\n\t\t}\n\n\t\tprivate void registerDefaultAuthenticationEntryPoint(ServerHttpSecurity http) {\n\t\t\tif (http.exceptionHandling != null) {\n\t\t\t\thttp.defaultEntryPoints.add(new DelegateEntry(this.authenticationConverterServerWebExchangeMatcher,\n\t\t\t\t\t\tOAuth2ResourceServerSpec.this.entryPoint));\n\t\t\t}\n\t\t}\n\n\t\tprivate void registerDefaultCsrfOverride(ServerHttpSecurity http) {\n\t\t\tif (http.csrf != null && !http.csrf.specifiedRequireCsrfProtectionMatcher) {\n\t\t\t\tAndServerWebExchangeMatcher matcher = new AndServerWebExchangeMatcher(\n\t\t\t\t\t\tCsrfWebFilter.DEFAULT_CSRF_MATCHER,\n\t\t\t\t\t\tnew NegatedServerWebExchangeMatcher(this.authenticationConverterServerWebExchangeMatcher));\n\t\t\t\thttp.csrf((csrf) -> csrf.requireCsrfProtectionMatcher(matcher));\n\t\t\t}\n\t\t}\n\n\t\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler() {\n\t\t\tif (this.authenticationFailureHandler != null) {\n\t\t\t\treturn this.authenticationFailureHandler;\n\t\t\t}\n\t\t\treturn new ServerAuthenticationEntryPointFailureHandler(this.entryPoint);\n\t\t}\n\n\t\t/**\n\t\t * Configures JWT Resource Server Support\n\t\t */\n\t\tpublic class JwtSpec {\n\n\t\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\t\tprivate ReactiveJwtDecoder jwtDecoder;\n\n\t\t\tprivate Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter;\n\n\t\t\t/**\n\t\t\t * Configures the {@link ReactiveAuthenticationManager} to use\n\t\t\t * @param authenticationManager the authentication manager to use\n\t\t\t * @return the {@code JwtSpec} for additional configuration\n\t\t\t */\n\t\t\tpublic JwtSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configures the {@link Converter} to use for converting a {@link Jwt} into\n\t\t\t * an {@link AbstractAuthenticationToken}.\n\t\t\t * @param jwtAuthenticationConverter the converter to use\n\t\t\t * @return the {@code JwtSpec} for additional configuration\n\t\t\t * @since 5.1.1\n\t\t\t */\n\t\t\tpublic JwtSpec jwtAuthenticationConverter(\n\t\t\t\t\tConverter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter) {\n\t\t\t\tAssert.notNull(jwtAuthenticationConverter, \"jwtAuthenticationConverter cannot be null\");\n\t\t\t\tthis.jwtAuthenticationConverter = jwtAuthenticationConverter;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configures the {@link ReactiveJwtDecoder} to use\n\t\t\t * @param jwtDecoder the decoder to use\n\t\t\t * @return the {@code JwtSpec} for additional configuration\n\t\t\t */\n\t\t\tpublic JwtSpec jwtDecoder(ReactiveJwtDecoder jwtDecoder) {\n\t\t\t\tthis.jwtDecoder = jwtDecoder;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configures a {@link ReactiveJwtDecoder} that leverages the provided\n\t\t\t * {@link RSAPublicKey}\n\t\t\t * @param publicKey the public key to use.\n\t\t\t * @return the {@code JwtSpec} for additional configuration\n\t\t\t */\n\t\t\tpublic JwtSpec publicKey(RSAPublicKey publicKey) {\n\t\t\t\tthis.jwtDecoder = new NimbusReactiveJwtDecoder(publicKey);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configures a {@link ReactiveJwtDecoder} using\n\t\t\t * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7517\">JSON Web Key\n\t\t\t * (JWK)</a> URL\n\t\t\t * @param jwkSetUri the URL to use.\n\t\t\t * @return the {@code JwtSpec} for additional configuration\n\t\t\t */\n\t\t\tpublic JwtSpec jwkSetUri(String jwkSetUri) {\n\t\t\t\tthis.jwtDecoder = new NimbusReactiveJwtDecoder(jwkSetUri);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\t\tReactiveAuthenticationManager authenticationManager = getAuthenticationManager();\n\t\t\t\tAuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);\n\t\t\t\toauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);\n\t\t\t\toauth2.setAuthenticationFailureHandler(authenticationFailureHandler());\n\t\t\t\thttp.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);\n\t\t\t}\n\n\t\t\tprotected ReactiveJwtDecoder getJwtDecoder() {\n\t\t\t\treturn (this.jwtDecoder != null) ? this.jwtDecoder : getBean(ReactiveJwtDecoder.class);\n\t\t\t}\n\n\t\t\tprotected Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> getJwtAuthenticationConverter() {\n\t\t\t\tif (this.jwtAuthenticationConverter != null) {\n\t\t\t\t\treturn this.jwtAuthenticationConverter;\n\t\t\t\t}\n\n\t\t\t\tif (getBeanNamesForTypeOrEmpty(ReactiveJwtAuthenticationConverter.class).length > 0) {\n\t\t\t\t\treturn getBean(ReactiveJwtAuthenticationConverter.class);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn new ReactiveJwtAuthenticationConverter();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate ReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\t\tif (this.authenticationManager != null) {\n\t\t\t\t\treturn this.authenticationManager;\n\t\t\t\t}\n\t\t\t\tReactiveJwtDecoder jwtDecoder = getJwtDecoder();\n\t\t\t\tConverter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter = getJwtAuthenticationConverter();\n\t\t\t\tJwtReactiveAuthenticationManager authenticationManager = new JwtReactiveAuthenticationManager(\n\t\t\t\t\t\tjwtDecoder);\n\t\t\t\tauthenticationManager.setJwtAuthenticationConverter(jwtAuthenticationConverter);\n\t\t\t\treturn authenticationManager;\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Configures Opaque Token Resource Server support\n\t\t *\n\t\t * @author Josh Cummings\n\t\t * @since 5.2\n\t\t */\n\t\tpublic final class OpaqueTokenSpec {\n\n\t\t\tprivate String introspectionUri;\n\n\t\t\tprivate String clientId;\n\n\t\t\tprivate String clientSecret;\n\n\t\t\tprivate Supplier<ReactiveOpaqueTokenIntrospector> introspector;\n\n\t\t\tprivate ReactiveOpaqueTokenAuthenticationConverter authenticationConverter;\n\n\t\t\tprivate OpaqueTokenSpec() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configures the URI of the Introspection endpoint\n\t\t\t * @param introspectionUri The URI of the Introspection endpoint\n\t\t\t * @return the {@code OpaqueTokenSpec} for additional configuration\n\t\t\t */\n\t\t\tpublic OpaqueTokenSpec introspectionUri(String introspectionUri) {\n\t\t\t\tAssert.hasText(introspectionUri, \"introspectionUri cannot be empty\");\n\t\t\t\tthis.introspectionUri = introspectionUri;\n\t\t\t\tthis.introspector = () -> SpringReactiveOpaqueTokenIntrospector\n\t\t\t\t\t.withIntrospectionUri(this.introspectionUri)\n\t\t\t\t\t.clientId(this.clientId)\n\t\t\t\t\t.clientSecret(this.clientSecret)\n\t\t\t\t\t.build();\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configures the credentials for Introspection endpoint\n\t\t\t * @param clientId The clientId part of the credentials\n\t\t\t * @param clientSecret The clientSecret part of the credentials\n\t\t\t * @return the {@code OpaqueTokenSpec} for additional configuration\n\t\t\t */\n\t\t\tpublic OpaqueTokenSpec introspectionClientCredentials(String clientId, String clientSecret) {\n\t\t\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\t\t\tAssert.notNull(clientSecret, \"clientSecret cannot be null\");\n\t\t\t\tthis.clientId = clientId;\n\t\t\t\tthis.clientSecret = clientSecret;\n\t\t\t\tthis.introspector = () -> SpringReactiveOpaqueTokenIntrospector\n\t\t\t\t\t.withIntrospectionUri(this.introspectionUri)\n\t\t\t\t\t.clientId(this.clientId)\n\t\t\t\t\t.clientSecret(this.clientSecret)\n\t\t\t\t\t.build();\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic OpaqueTokenSpec introspector(ReactiveOpaqueTokenIntrospector introspector) {\n\t\t\t\tAssert.notNull(introspector, \"introspector cannot be null\");\n\t\t\t\tthis.introspector = () -> introspector;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic OpaqueTokenSpec authenticationConverter(\n\t\t\t\t\tReactiveOpaqueTokenAuthenticationConverter authenticationConverter) {\n\t\t\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tprotected ReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\t\tOpaqueTokenReactiveAuthenticationManager authenticationManager = new OpaqueTokenReactiveAuthenticationManager(\n\t\t\t\t\t\tgetIntrospector());\n\t\t\t\tReactiveOpaqueTokenAuthenticationConverter authenticationConverter = getAuthenticationConverter();\n\t\t\t\tif (authenticationConverter != null) {\n\t\t\t\t\tauthenticationManager.setAuthenticationConverter(authenticationConverter);\n\t\t\t\t}\n\t\t\t\treturn authenticationManager;\n\t\t\t}\n\n\t\t\tprotected ReactiveOpaqueTokenIntrospector getIntrospector() {\n\t\t\t\tif (this.introspector != null) {\n\t\t\t\t\treturn this.introspector.get();\n\t\t\t\t}\n\t\t\t\treturn getBean(ReactiveOpaqueTokenIntrospector.class);\n\t\t\t}\n\n\t\t\tprotected ReactiveOpaqueTokenAuthenticationConverter getAuthenticationConverter() {\n\t\t\t\tif (this.authenticationConverter != null) {\n\t\t\t\t\treturn this.authenticationConverter;\n\t\t\t\t}\n\t\t\t\treturn getBeanOrNull(ReactiveOpaqueTokenAuthenticationConverter.class);\n\t\t\t}\n\n\t\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\t\tReactiveAuthenticationManager authenticationManager = getAuthenticationManager();\n\t\t\t\tAuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);\n\t\t\t\toauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);\n\t\t\t\toauth2.setAuthenticationFailureHandler(authenticationFailureHandler());\n\t\t\t\thttp.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures OIDC 1.0 Logout support\n\t *\n\t * @author Josh Cummings\n\t * @since 6.2\n\t */\n\tpublic final class OidcLogoutSpec {\n\n\t\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t\tprivate ReactiveOidcSessionRegistry sessionRegistry;\n\n\t\tprivate BackChannelLogoutConfigurer backChannel;\n\n\t\t/**\n\t\t * Configures the {@link ReactiveClientRegistrationRepository}. Default is to look\n\t\t * the value up as a Bean.\n\t\t * @param clientRegistrationRepository the repository to use\n\t\t * @return the {@link OidcLogoutSpec} to customize\n\t\t */\n\t\tpublic OidcLogoutSpec clientRegistrationRepository(\n\t\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ReactiveOidcSessionRegistry}. Default is to use the value\n\t\t * from {@link OAuth2LoginSpec#oidcSessionRegistry}, then look the value up as a\n\t\t * Bean, or else use an {@link InMemoryReactiveOidcSessionRegistry}.\n\t\t * @param sessionRegistry the registry to use\n\t\t * @return the {@link OidcLogoutSpec} to customize\n\t\t */\n\t\tpublic OidcLogoutSpec oidcSessionRegistry(ReactiveOidcSessionRegistry sessionRegistry) {\n\t\t\tAssert.notNull(sessionRegistry, \"sessionRegistry cannot be null\");\n\t\t\tthis.sessionRegistry = sessionRegistry;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure OIDC Back-Channel Logout using the provided {@link Consumer}\n\t\t * @return the {@link OidcLogoutSpec} for further configuration\n\t\t */\n\t\tpublic OidcLogoutSpec backChannel(Customizer<BackChannelLogoutConfigurer> backChannelLogoutConfigurer) {\n\t\t\tif (this.backChannel == null) {\n\t\t\t\tthis.backChannel = new OidcLogoutSpec.BackChannelLogoutConfigurer();\n\t\t\t}\n\t\t\tbackChannelLogoutConfigurer.customize(this.backChannel);\n\t\t\treturn this;\n\t\t}\n\n\t\tvoid configure(ServerHttpSecurity http) {\n\t\t\tif (this.backChannel != null) {\n\t\t\t\tthis.backChannel.configure(http);\n\t\t\t}\n\t\t}\n\n\t\tprivate ReactiveClientRegistrationRepository getClientRegistrationRepository() {\n\t\t\tif (this.clientRegistrationRepository == null) {\n\t\t\t\tthis.clientRegistrationRepository = getBeanOrNull(ReactiveClientRegistrationRepository.class);\n\t\t\t}\n\t\t\treturn this.clientRegistrationRepository;\n\t\t}\n\n\t\tprivate ReactiveOidcSessionRegistry getSessionRegistry() {\n\t\t\tif (this.sessionRegistry == null && ServerHttpSecurity.this.oauth2Login == null) {\n\t\t\t\treturn getBeanOrDefault(ReactiveOidcSessionRegistry.class, new InMemoryReactiveOidcSessionRegistry());\n\t\t\t}\n\t\t\tif (this.sessionRegistry == null) {\n\t\t\t\treturn ServerHttpSecurity.this.oauth2Login.oidcSessionRegistry;\n\t\t\t}\n\t\t\treturn this.sessionRegistry;\n\t\t}\n\n\t\t/**\n\t\t * A configurer for configuring OIDC Back-Channel Logout\n\t\t */\n\t\tpublic final class BackChannelLogoutConfigurer {\n\n\t\t\tprivate ServerAuthenticationConverter authenticationConverter;\n\n\t\t\tprivate final ReactiveAuthenticationManager authenticationManager = new OidcBackChannelLogoutReactiveAuthenticationManager();\n\n\t\t\tprivate Supplier<ServerLogoutHandler> logoutHandler = this::logoutHandler;\n\n\t\t\tprivate ServerAuthenticationConverter authenticationConverter() {\n\t\t\t\tif (this.authenticationConverter == null) {\n\t\t\t\t\tthis.authenticationConverter = new OidcLogoutServerAuthenticationConverter(\n\t\t\t\t\t\t\tOidcLogoutSpec.this.getClientRegistrationRepository());\n\t\t\t\t}\n\t\t\t\treturn this.authenticationConverter;\n\t\t\t}\n\n\t\t\tprivate ReactiveAuthenticationManager authenticationManager() {\n\t\t\t\treturn this.authenticationManager;\n\t\t\t}\n\n\t\t\tprivate ServerLogoutHandler logoutHandler() {\n\t\t\t\tOidcBackChannelServerLogoutHandler logoutHandler = getBeanOrNull(\n\t\t\t\t\t\tOidcBackChannelServerLogoutHandler.class);\n\t\t\t\tif (logoutHandler != null) {\n\t\t\t\t\treturn logoutHandler;\n\t\t\t\t}\n\t\t\t\tlogoutHandler = new OidcBackChannelServerLogoutHandler(OidcLogoutSpec.this.getSessionRegistry());\n\t\t\t\treturn logoutHandler;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Use this endpoint when invoking a back-channel logout.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * The resulting {@link LogoutHandler} will {@code POST} the session cookie\n\t\t\t * and CSRF token to this endpoint to invalidate the corresponding end-user\n\t\t\t * session.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * Supports URI templates like {@code {baseUrl}}, {@code {baseScheme}}, and\n\t\t\t * {@code {basePort}}.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * By default, the URI is set to\n\t\t\t * {@code {baseUrl}/logout/connect/back-channel/{registrationId}}, meaning\n\t\t\t * that the scheme and port of the original back-channel request is preserved,\n\t\t\t * while the host and endpoint are changed.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * If you are using Spring Security for the logout endpoint, the path part of\n\t\t\t * this URI should match the value configured there.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * Otherwise, this is handy in the event that your server configuration means\n\t\t\t * that the scheme, server name, or port in the {@code Host} header are\n\t\t\t * different from how you would address the same server internally.\n\t\t\t * @param logoutUri the URI to request logout on the back-channel\n\t\t\t * @return the {@link BackChannelLogoutConfigurer} for further customizations\n\t\t\t * @since 6.2.4\n\t\t\t */\n\t\t\tpublic BackChannelLogoutConfigurer logoutUri(String logoutUri) {\n\t\t\t\tthis.logoutHandler = () -> {\n\t\t\t\t\tOidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler(\n\t\t\t\t\t\t\tOidcLogoutSpec.this.getSessionRegistry());\n\t\t\t\t\tlogoutHandler.setLogoutUri(logoutUri);\n\t\t\t\t\treturn logoutHandler;\n\t\t\t\t};\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Configure what and how per-session logout will be performed.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * This overrides any value given to {@link #logoutUri(String)}\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * By default, the resulting {@link LogoutHandler} will {@code POST} the\n\t\t\t * session cookie and OIDC logout token back to the original back-channel\n\t\t\t * logout endpoint.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * Using this method changes the underlying default that {@code POST}s the\n\t\t\t * session cookie and CSRF token to your application's {@code /logout}\n\t\t\t * endpoint. As such, it is recommended to call this instead of accepting the\n\t\t\t * {@code /logout} default as this does not require any special CSRF\n\t\t\t * configuration, even if you don't require other changes.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * For example, configuring Back-Channel Logout in the following way:\n\t\t\t *\n\t\t\t * <pre>\n\t\t\t * \thttp\n\t\t\t *     \t.oidcLogout((oidc) -&gt; oidc\n\t\t\t *     \t\t.backChannel((backChannel) -&gt; backChannel\n\t\t\t *     \t\t\t.logoutHandler(new OidcBackChannelServerLogoutHandler())\n\t\t\t *     \t\t)\n\t\t\t *     \t);\n\t\t\t * </pre>\n\t\t\t *\n\t\t\t * will make so that the per-session logout invocation no longer requires\n\t\t\t * special CSRF configurations.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * The default URI is\n\t\t\t * {@code {baseUrl}/logout/connect/back-channel/{registrationId}}, which is\n\t\t\t * simply an internal version of the same endpoint exposed to your\n\t\t\t * Back-Channel services. You can use\n\t\t\t * {@link OidcBackChannelServerLogoutHandler#setLogoutUri(String)} to alter\n\t\t\t * the scheme, server name, or port in the {@code Host} header to accommodate\n\t\t\t * how your application would address itself internally.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * For example, if the way your application would internally call itself is on\n\t\t\t * a different scheme and port than incoming traffic, you can configure the\n\t\t\t * endpoint in the following way:\n\t\t\t *\n\t\t\t * <pre>\n\t\t\t * \thttp\n\t\t\t * \t\t.oidcLogout((oidc) -&gt; oidc\n\t\t\t * \t\t\t.backChannel((backChannel) -&gt; backChannel\n\t\t\t * \t\t\t\t.logoutUri(\"http://localhost:9000/logout/connect/back-channel/{registrationId}\")\n\t\t\t * \t\t\t)\n\t\t\t * \t\t);\n\t\t\t * </pre>\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * You can also publish it as a {@code @Bean} as follows:\n\t\t\t *\n\t\t\t * <pre>\n\t\t\t *\t&commat;Bean\n\t\t\t *\tOidcBackChannelServerLogoutHandler oidcLogoutHandler() {\n\t\t\t *  \tOidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler();\n\t\t\t *  \tlogoutHandler.setLogoutUri(\"http://localhost:9000/logout/connect/back-channel/{registrationId}\");\n\t\t\t *  \treturn logoutHandler;\n\t\t\t *\t}\n\t\t\t * </pre>\n\t\t\t *\n\t\t\t * to have the same effect.\n\t\t\t * @param logoutHandler the {@link ServerLogoutHandler} to use each individual\n\t\t\t * session\n\t\t\t * @return {@link BackChannelLogoutConfigurer} for further customizations\n\t\t\t * @since 6.4\n\t\t\t */\n\t\t\tpublic BackChannelLogoutConfigurer logoutHandler(ServerLogoutHandler logoutHandler) {\n\t\t\t\tthis.logoutHandler = () -> logoutHandler;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tvoid configure(ServerHttpSecurity http) {\n\t\t\t\tServerLogoutHandler oidcLogout = this.logoutHandler.get();\n\t\t\t\tServerLogoutHandler sessionLogout = new SecurityContextServerLogoutHandler();\n\t\t\t\tLogoutSpec logout = ServerHttpSecurity.this.logout;\n\t\t\t\tif (logout != null) {\n\t\t\t\t\tsessionLogout = new DelegatingServerLogoutHandler(logout.logoutHandlers);\n\t\t\t\t}\n\t\t\t\tOidcBackChannelLogoutWebFilter filter = new OidcBackChannelLogoutWebFilter(authenticationConverter(),\n\t\t\t\t\t\tauthenticationManager(), new EitherLogoutHandler(oidcLogout, sessionLogout));\n\t\t\t\thttp.addFilterBefore(filter, SecurityWebFiltersOrder.CSRF);\n\t\t\t}\n\n\t\t\tprivate static final class EitherLogoutHandler implements ServerLogoutHandler {\n\n\t\t\t\tprivate final ServerLogoutHandler left;\n\n\t\t\t\tprivate final ServerLogoutHandler right;\n\n\t\t\t\tEitherLogoutHandler(ServerLogoutHandler left, ServerLogoutHandler right) {\n\t\t\t\t\tthis.left = left;\n\t\t\t\t\tthis.right = right;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Mono<Void> logout(WebFilterExchange exchange, Authentication authentication) {\n\t\t\t\t\treturn exchange.getExchange().getFormData().flatMap((data) -> {\n\t\t\t\t\t\tif (data.getFirst(\"_spring_security_internal_logout\") == null) {\n\t\t\t\t\t\t\treturn this.left.logout(exchange, authentication);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\treturn this.right.logout(exchange, authentication);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures anonymous authentication\n\t *\n\t * @since 5.2.0\n\t */\n\tpublic final class AnonymousSpec {\n\n\t\tprivate String key;\n\n\t\tprivate AnonymousAuthenticationWebFilter authenticationFilter;\n\n\t\tprivate Object principal = \"anonymousUser\";\n\n\t\tprivate List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\");\n\n\t\t/**\n\t\t * Sets the key to identify tokens created for anonymous authentication. Default\n\t\t * is a secure randomly generated key.\n\t\t * @param key the key to identify tokens created for anonymous authentication.\n\t\t * Default is a secure randomly generated key.\n\t\t * @return the {@link AnonymousSpec} for further customization of anonymous\n\t\t * authentication\n\t\t */\n\t\tpublic AnonymousSpec key(String key) {\n\t\t\tthis.key = key;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the principal for {@link Authentication} objects of anonymous users\n\t\t * @param principal used for the {@link Authentication} object of anonymous users\n\t\t * @return the {@link AnonymousSpec} for further customization of anonymous\n\t\t * authentication\n\t\t */\n\t\tpublic AnonymousSpec principal(Object principal) {\n\t\t\tthis.principal = principal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the\n\t\t * {@link org.springframework.security.core.Authentication#getAuthorities()} for\n\t\t * anonymous users\n\t\t * @param authorities Sets the\n\t\t * {@link org.springframework.security.core.Authentication#getAuthorities()} for\n\t\t * anonymous users\n\t\t * @return the {@link AnonymousSpec} for further customization of anonymous\n\t\t * authentication\n\t\t */\n\t\tpublic AnonymousSpec authorities(List<GrantedAuthority> authorities) {\n\t\t\tthis.authorities = authorities;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the\n\t\t * {@link org.springframework.security.core.Authentication#getAuthorities()} for\n\t\t * anonymous users\n\t\t * @param authorities Sets the\n\t\t * {@link org.springframework.security.core.Authentication#getAuthorities()} for\n\t\t * anonymous users (i.e. \"ROLE_ANONYMOUS\")\n\t\t * @return the {@link AnonymousSpec} for further customization of anonymous\n\t\t * authentication\n\t\t */\n\t\tpublic AnonymousSpec authorities(String... authorities) {\n\t\t\treturn authorities(AuthorityUtils.createAuthorityList(authorities));\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link AnonymousAuthenticationWebFilter} used to populate an anonymous\n\t\t * user. If this is set, no attributes on the {@link AnonymousSpec} will be set on\n\t\t * the {@link AnonymousAuthenticationWebFilter}.\n\t\t * @param authenticationFilter the {@link AnonymousAuthenticationWebFilter} used\n\t\t * to populate an anonymous user.\n\t\t * @return the {@link AnonymousSpec} for further customization of anonymous\n\t\t * authentication\n\t\t */\n\t\tpublic AnonymousSpec authenticationFilter(AnonymousAuthenticationWebFilter authenticationFilter) {\n\t\t\tthis.authenticationFilter = authenticationFilter;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Disables anonymous authentication.\n\t\t * @return the {@link ServerHttpSecurity} to continue configuring\n\t\t */\n\t\tpublic ServerHttpSecurity disable() {\n\t\t\tServerHttpSecurity.this.anonymous = null;\n\t\t\treturn ServerHttpSecurity.this;\n\t\t}\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tif (this.authenticationFilter == null) {\n\t\t\t\tthis.authenticationFilter = new AnonymousAuthenticationWebFilter(getKey(), this.principal,\n\t\t\t\t\t\tthis.authorities);\n\t\t\t}\n\t\t\thttp.addFilterAt(this.authenticationFilter, SecurityWebFiltersOrder.ANONYMOUS_AUTHENTICATION);\n\t\t}\n\n\t\tprivate String getKey() {\n\t\t\tif (this.key == null) {\n\t\t\t\tthis.key = UUID.randomUUID().toString();\n\t\t\t}\n\t\t\treturn this.key;\n\t\t}\n\n\t\tprivate AnonymousSpec() {\n\t\t}\n\n\t}\n\n\t/**\n\t * Configures One-Time Token Login Support\n\t *\n\t * @author Max Batischev\n\t * @since 6.4\n\t * @see #oneTimeTokenLogin(Customizer)\n\t */\n\tpublic final class OneTimeTokenLoginSpec {\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t\tprivate ReactiveOneTimeTokenService tokenService;\n\n\t\tprivate ServerAuthenticationConverter authenticationConverter = new ServerOneTimeTokenAuthenticationConverter();\n\n\t\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler;\n\n\t\tprivate final RedirectServerAuthenticationSuccessHandler defaultSuccessHandler = new RedirectServerAuthenticationSuccessHandler(\n\t\t\t\t\"/\");\n\n\t\tprivate final List<ServerAuthenticationSuccessHandler> defaultSuccessHandlers = new ArrayList<>(\n\t\t\t\tList.of(this.defaultSuccessHandler));\n\n\t\tprivate final List<ServerAuthenticationSuccessHandler> authenticationSuccessHandlers = new ArrayList<>();\n\n\t\tprivate ServerOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler;\n\n\t\tprivate ServerSecurityContextRepository securityContextRepository;\n\n\t\tprivate ServerGenerateOneTimeTokenRequestResolver requestResolver;\n\n\t\tprivate String loginProcessingUrl = \"/login/ott\";\n\n\t\tprivate String defaultSubmitPageUrl = \"/login/ott\";\n\n\t\tprivate String tokenGeneratingUrl = \"/ott/generate\";\n\n\t\tprivate boolean submitPageEnabled = true;\n\n\t\tprivate String loginPage;\n\n\t\tprotected void configure(ServerHttpSecurity http) {\n\t\t\tconfigureSubmitPage(http);\n\t\t\tconfigureOttGenerateFilter(http);\n\t\t\tconfigureOttAuthenticationFilter(http);\n\t\t\tconfigureDefaultEntryPoint(http);\n\t\t}\n\n\t\tprivate void configureOttAuthenticationFilter(ServerHttpSecurity http) {\n\t\t\tAuthenticationWebFilter ottWebFilter = new AuthenticationWebFilter(getAuthenticationManager());\n\t\t\tottWebFilter.setServerAuthenticationConverter(this.authenticationConverter);\n\t\t\tottWebFilter.setAuthenticationFailureHandler(getAuthenticationFailureHandler());\n\t\t\tottWebFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler());\n\t\t\tottWebFilter.setRequiresAuthenticationMatcher(\n\t\t\t\t\tServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, this.loginProcessingUrl));\n\t\t\tottWebFilter.setSecurityContextRepository(this.securityContextRepository);\n\t\t\thttp.addFilterAt(ottWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);\n\t\t}\n\n\t\tprivate void configureSubmitPage(ServerHttpSecurity http) {\n\t\t\tif (!this.submitPageEnabled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tOneTimeTokenSubmitPageGeneratingWebFilter submitPage = new OneTimeTokenSubmitPageGeneratingWebFilter();\n\t\t\tsubmitPage.setLoginProcessingUrl(this.loginProcessingUrl);\n\n\t\t\tif (StringUtils.hasText(this.defaultSubmitPageUrl)) {\n\t\t\t\tsubmitPage.setRequestMatcher(\n\t\t\t\t\t\tServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, this.defaultSubmitPageUrl));\n\t\t\t}\n\t\t\thttp.addFilterAt(submitPage, SecurityWebFiltersOrder.ONE_TIME_TOKEN_SUBMIT_PAGE_GENERATING);\n\t\t}\n\n\t\tprivate void configureOttGenerateFilter(ServerHttpSecurity http) {\n\t\t\tGenerateOneTimeTokenWebFilter generateFilter = new GenerateOneTimeTokenWebFilter(getTokenService(),\n\t\t\t\t\tgetTokenGenerationSuccessHandler());\n\t\t\tgenerateFilter\n\t\t\t\t.setRequestMatcher(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, this.tokenGeneratingUrl));\n\t\t\tgenerateFilter.setGenerateRequestResolver(getRequestResolver());\n\t\t\thttp.addFilterAt(generateFilter, SecurityWebFiltersOrder.ONE_TIME_TOKEN);\n\t\t}\n\n\t\tprivate void configureDefaultEntryPoint(ServerHttpSecurity http) {\n\t\t\tMediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(\n\t\t\t\t\tMediaType.APPLICATION_XHTML_XML, new MediaType(\"image\", \"*\"), MediaType.TEXT_HTML,\n\t\t\t\t\tMediaType.TEXT_PLAIN);\n\t\t\thtmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\t\tServerWebExchangeMatcher xhrMatcher = (exchange) -> {\n\t\t\t\tif (exchange.getRequest().getHeaders().getOrEmpty(\"X-Requested-With\").contains(\"XMLHttpRequest\")) {\n\t\t\t\t\treturn ServerWebExchangeMatcher.MatchResult.match();\n\t\t\t\t}\n\t\t\t\treturn ServerWebExchangeMatcher.MatchResult.notMatch();\n\t\t\t};\n\t\t\tServerWebExchangeMatcher notXhrMatcher = new NegatedServerWebExchangeMatcher(xhrMatcher);\n\t\t\tServerWebExchangeMatcher defaultEntryPointMatcher = new AndServerWebExchangeMatcher(notXhrMatcher,\n\t\t\t\t\thtmlMatcher);\n\t\t\tString loginPage = \"/login\";\n\t\t\tif (this.loginPage != null) {\n\t\t\t\tloginPage = this.loginPage;\n\t\t\t}\n\t\t\tRedirectServerAuthenticationEntryPoint defaultEntryPoint = new RedirectServerAuthenticationEntryPoint(\n\t\t\t\t\tloginPage);\n\t\t\tdefaultEntryPoint.setRequestCache(http.requestCache.requestCache);\n\t\t\thttp.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint));\n\n\t\t}\n\n\t\t/**\n\t\t * Allows customizing the list of {@link ServerAuthenticationSuccessHandler}. The\n\t\t * default list contains a {@link RedirectServerAuthenticationSuccessHandler} that\n\t\t * redirects to \"/\".\n\t\t * @param handlersConsumer the handlers consumer\n\t\t * @return the {@link OneTimeTokenLoginSpec} to continue configuring\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec authenticationSuccessHandler(\n\t\t\t\tConsumer<List<ServerAuthenticationSuccessHandler>> handlersConsumer) {\n\t\t\tAssert.notNull(handlersConsumer, \"handlersConsumer cannot be null\");\n\t\t\thandlersConsumer.accept(this.authenticationSuccessHandlers);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the {@link ServerAuthenticationSuccessHandler}\n\t\t * @param authenticationSuccessHandler the\n\t\t * {@link ServerAuthenticationSuccessHandler}.\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec authenticationSuccessHandler(\n\t\t\t\tServerAuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\t\tauthenticationSuccessHandler((handlers) -> {\n\t\t\t\thandlers.clear();\n\t\t\t\thandlers.add(authenticationSuccessHandler);\n\t\t\t});\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ServerAuthenticationSuccessHandler getAuthenticationSuccessHandler() {\n\t\t\tif (this.authenticationSuccessHandlers.isEmpty()) {\n\t\t\t\treturn new DelegatingServerAuthenticationSuccessHandler(this.defaultSuccessHandlers);\n\t\t\t}\n\t\t\treturn new DelegatingServerAuthenticationSuccessHandler(this.authenticationSuccessHandlers);\n\t\t}\n\n\t\t/**\n\t\t * Specifies the {@link ServerAuthenticationFailureHandler} to use when\n\t\t * authentication fails. The default is redirecting to \"/login?error\" using\n\t\t * {@link RedirectServerAuthenticationFailureHandler}\n\t\t * @param authenticationFailureHandler the\n\t\t * {@link ServerAuthenticationFailureHandler} to use when authentication fails.\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec authenticationFailureHandler(\n\t\t\t\tServerAuthenticationFailureHandler authenticationFailureHandler) {\n\t\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\tServerAuthenticationFailureHandler getAuthenticationFailureHandler() {\n\t\t\tif (this.authenticationFailureHandler == null) {\n\t\t\t\tthis.authenticationFailureHandler = new RedirectServerAuthenticationFailureHandler(\"/login?error\");\n\t\t\t}\n\t\t\treturn this.authenticationFailureHandler;\n\t\t}\n\n\t\t/**\n\t\t * Specifies {@link ReactiveAuthenticationManager} for one time tokens. Default\n\t\t * implementation is {@link OneTimeTokenReactiveAuthenticationManager}\n\t\t * @param authenticationManager\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {\n\t\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\t\tthis.authenticationManager = authenticationManager;\n\t\t\treturn this;\n\t\t}\n\n\t\tReactiveAuthenticationManager getAuthenticationManager() {\n\t\t\tif (this.authenticationManager == null) {\n\t\t\t\tReactiveUserDetailsService userDetailsService = getBean(ReactiveUserDetailsService.class);\n\t\t\t\treturn new OneTimeTokenReactiveAuthenticationManager(getTokenService(), userDetailsService);\n\t\t\t}\n\t\t\treturn this.authenticationManager;\n\t\t}\n\n\t\t/**\n\t\t * Configures the {@link ReactiveOneTimeTokenService} used to generate and consume\n\t\t * {@link OneTimeToken}\n\t\t * @param oneTimeTokenService\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec tokenService(ReactiveOneTimeTokenService oneTimeTokenService) {\n\t\t\tAssert.notNull(oneTimeTokenService, \"oneTimeTokenService cannot be null\");\n\t\t\tthis.tokenService = oneTimeTokenService;\n\t\t\treturn this;\n\t\t}\n\n\t\tReactiveOneTimeTokenService getTokenService() {\n\t\t\tif (this.tokenService != null) {\n\t\t\t\treturn this.tokenService;\n\t\t\t}\n\t\t\tReactiveOneTimeTokenService oneTimeTokenService = getBeanOrNull(ReactiveOneTimeTokenService.class);\n\t\t\tif (oneTimeTokenService != null) {\n\t\t\t\treturn oneTimeTokenService;\n\t\t\t}\n\t\t\tthis.tokenService = new InMemoryReactiveOneTimeTokenService();\n\t\t\treturn this.tokenService;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link ServerAuthenticationConverter} when converting incoming\n\t\t * requests to an {@link Authentication}. By default, the\n\t\t * {@link ServerOneTimeTokenAuthenticationConverter} is used.\n\t\t * @param authenticationConverter the {@link ServerAuthenticationConverter} to use\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec authenticationConverter(ServerAuthenticationConverter authenticationConverter) {\n\t\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\t\tthis.authenticationConverter = authenticationConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link ServerGenerateOneTimeTokenRequestResolver} when resolving\n\t\t * {@link GenerateOneTimeTokenRequest} from {@link ServerWebExchange}. By default,\n\t\t * the {@link DefaultServerGenerateOneTimeTokenRequestResolver} is used.\n\t\t * @param requestResolver the\n\t\t * {@link DefaultServerGenerateOneTimeTokenRequestResolver} to use\n\t\t * @since 6.5\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec generateRequestResolver(\n\t\t\t\tServerGenerateOneTimeTokenRequestResolver requestResolver) {\n\t\t\tAssert.notNull(requestResolver, \"generateRequestResolver cannot be null\");\n\t\t\tthis.requestResolver = requestResolver;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ServerGenerateOneTimeTokenRequestResolver getRequestResolver() {\n\t\t\tif (this.requestResolver != null) {\n\t\t\t\treturn this.requestResolver;\n\t\t\t}\n\t\t\tServerGenerateOneTimeTokenRequestResolver bean = getBeanOrNull(\n\t\t\t\t\tServerGenerateOneTimeTokenRequestResolver.class);\n\t\t\tthis.requestResolver = Objects.requireNonNullElseGet(bean,\n\t\t\t\t\tDefaultServerGenerateOneTimeTokenRequestResolver::new);\n\t\t\treturn this.requestResolver;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the URL to process the login request, defaults to {@code /login/ott}.\n\t\t * Only POST requests are processed, for that reason make sure that you pass a\n\t\t * valid CSRF token if CSRF protection is enabled.\n\t\t * @param loginProcessingUrl\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec loginProcessingUrl(String loginProcessingUrl) {\n\t\t\tAssert.hasText(loginProcessingUrl, \"loginProcessingUrl cannot be null or empty\");\n\t\t\tthis.loginProcessingUrl = loginProcessingUrl;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures whether the default one-time token submit page should be shown. This\n\t\t * will prevent the {@link OneTimeTokenSubmitPageGeneratingWebFilter} to be\n\t\t * configured.\n\t\t * @param show\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec showDefaultSubmitPage(boolean show) {\n\t\t\tthis.submitPageEnabled = show;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the URL that the default submit page will be generated. Defaults to\n\t\t * {@code /login/ott}. If you don't want to generate the default submit page you\n\t\t * should use {@link #showDefaultSubmitPage(boolean)}. Note that this method\n\t\t * always invoke {@link #showDefaultSubmitPage(boolean)} passing {@code true}.\n\t\t * @param submitPageUrl\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec defaultSubmitPageUrl(String submitPageUrl) {\n\t\t\tAssert.hasText(submitPageUrl, \"submitPageUrl cannot be null or empty\");\n\t\t\tthis.defaultSubmitPageUrl = submitPageUrl;\n\t\t\tshowDefaultSubmitPage(true);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies strategy to be used to handle generated one-time tokens.\n\t\t * @param oneTimeTokenGenerationSuccessHandler\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec tokenGenerationSuccessHandler(\n\t\t\t\tServerOneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler) {\n\t\t\tAssert.notNull(oneTimeTokenGenerationSuccessHandler, \"oneTimeTokenGenerationSuccessHandler cannot be null\");\n\t\t\tthis.tokenGenerationSuccessHandler = oneTimeTokenGenerationSuccessHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the URL that a One-Time Token generate request will be processed.\n\t\t * Defaults to {@code /ott/generate}.\n\t\t * @param tokenGeneratingUrl\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec tokenGeneratingUrl(String tokenGeneratingUrl) {\n\t\t\tAssert.hasText(tokenGeneratingUrl, \"tokenGeneratingUrl cannot be null or empty\");\n\t\t\tthis.tokenGeneratingUrl = tokenGeneratingUrl;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The {@link ServerSecurityContextRepository} used to save the\n\t\t * {@code Authentication}. Defaults to\n\t\t * {@link WebSessionServerSecurityContextRepository}. For the\n\t\t * {@code SecurityContext} to be loaded on subsequent requests the\n\t\t * {@link ReactorContextWebFilter} must be configured to be able to load the value\n\t\t * (they are not implicitly linked).\n\t\t * @param securityContextRepository the repository to use\n\t\t * @return the {@link OneTimeTokenLoginSpec} to continue configuring\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec securityContextRepository(\n\t\t\t\tServerSecurityContextRepository securityContextRepository) {\n\t\t\tthis.securityContextRepository = securityContextRepository;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate ServerOneTimeTokenGenerationSuccessHandler getTokenGenerationSuccessHandler() {\n\t\t\tif (this.tokenGenerationSuccessHandler == null) {\n\t\t\t\tthis.tokenGenerationSuccessHandler = getBeanOrNull(ServerOneTimeTokenGenerationSuccessHandler.class);\n\t\t\t}\n\t\t\tif (this.tokenGenerationSuccessHandler == null) {\n\t\t\t\tthrow new IllegalStateException(\"\"\"\n\t\t\t\t\t\tA ServerOneTimeTokenGenerationSuccessHandler is required to enable oneTimeTokenLogin().\n\t\t\t\t\t\tPlease provide it as a bean or pass it to the oneTimeTokenLogin() DSL.\n\t\t\t\t\t\t\"\"\");\n\t\t\t}\n\t\t\treturn this.tokenGenerationSuccessHandler;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the URL to send users to if login is required. A default login page\n\t\t * will be generated when this attribute is not specified.\n\t\t * @param loginPage the URL to send users to if login is required\n\t\t * @return the {@link OAuth2LoginSpec} for further configuration\n\t\t * @since 6.5\n\t\t */\n\t\tpublic OneTimeTokenLoginSpec loginPage(String loginPage) {\n\t\t\tAssert.hasText(loginPage, \"loginPage cannot be empty\");\n\t\t\tthis.loginPage = loginPage;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/java/org/springframework/security/config/websocket/WebSocketMessageBrokerSecurityBeanDefinitionParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.websocket;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.PropertyValue;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.config.RuntimeBeanReference;\nimport org.springframework.beans.factory.parsing.BeanComponentDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.beans.factory.support.ManagedList;\nimport org.springframework.beans.factory.support.ManagedMap;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.beans.factory.xml.BeanDefinitionParser;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.beans.factory.xml.XmlReaderContext;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;\nimport org.springframework.security.access.vote.ConsensusBased;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.Elements;\nimport org.springframework.security.config.http.MessageMatcherFactoryBean;\nimport org.springframework.security.config.web.messaging.PathPatternMessageMatcherBuilderFactoryBean;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.messaging.access.expression.ExpressionBasedMessageSecurityMetadataSourceFactory;\nimport org.springframework.security.messaging.access.expression.MessageExpressionAuthorizationManager;\nimport org.springframework.security.messaging.access.expression.MessageExpressionVoter;\nimport org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;\nimport org.springframework.security.messaging.access.intercept.ChannelSecurityInterceptor;\nimport org.springframework.security.messaging.access.intercept.MessageAuthorizationContext;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;\nimport org.springframework.security.messaging.context.SecurityContextChannelInterceptor;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\nimport org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;\nimport org.springframework.security.messaging.web.csrf.XorCsrfChannelInterceptor;\nimport org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;\nimport org.springframework.util.AntPathMatcher;\nimport org.springframework.util.PathMatcher;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.xml.DomUtils;\n\n/**\n * Parses Spring Security's websocket namespace support. A simple example is:\n *\n * <code>\n * &lt;websocket-message-broker&gt;\n *     &lt;intercept-message pattern='/permitAll' access='permitAll' /&gt;\n *     &lt;intercept-message pattern='/denyAll' access='denyAll' /&gt;\n * &lt;/websocket-message-broker&gt;\n * </code>\n *\n * <p>\n * The above configuration will ensure that any SimpAnnotationMethodMessageHandler has the\n * AuthenticationPrincipalArgumentResolver registered as a custom argument resolver. It\n * also ensures that the SecurityContextChannelInterceptor is automatically registered for\n * the clientInboundChannel. Last, it ensures that a ChannelSecurityInterceptor is\n * registered with the clientInboundChannel.\n * </p>\n *\n * <p>\n * If finer control is necessary, the id attribute can be used as shown below:\n * </p>\n *\n * <code>\n * &lt;websocket-message-broker id=\"channelSecurityInterceptor\"&gt;\n *     &lt;intercept-message pattern='/permitAll' access='permitAll' /&gt;\n *     &lt;intercept-message pattern='/denyAll' access='denyAll' /&gt;\n * &lt;/websocket-message-broker&gt;\n * </code>\n *\n * <p>\n * Now the configuration will only create a bean named ChannelSecurityInterceptor and\n * assign it to the id of channelSecurityInterceptor. Users can explicitly wire Spring\n * Security using the standard Spring Messaging XML namespace support.\n * </p>\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements BeanDefinitionParser {\n\n\tprivate static final String ID_ATTR = \"id\";\n\n\tprivate static final String DISABLED_ATTR = \"same-origin-disabled\";\n\n\tprivate static final String USE_AUTHORIZATION_MANAGER_ATTR = \"use-authorization-manager\";\n\n\tprivate static final String AUTHORIZATION_MANAGER_REF_ATTR = \"authorization-manager-ref\";\n\n\tprivate static final String SECURITY_CONTEXT_HOLDER_STRATEGY_REF_ATTR = \"security-context-holder-strategy-ref\";\n\n\tprivate static final String PATTERN_ATTR = \"pattern\";\n\n\tprivate static final String ACCESS_ATTR = \"access\";\n\n\tprivate static final String TYPE_ATTR = \"type\";\n\n\tprivate static final String MESSAGE_MATCHER_BUILDER_BEAN_NAME = \"HttpConfigurationBuilder-pathPatternMessageMatcherBuilder\";\n\n\t/**\n\t * @param element\n\t * @param parserContext\n\t * @return the {@link BeanDefinition}\n\t */\n\t@Override\n\tpublic BeanDefinition parse(Element element, ParserContext parserContext) {\n\t\tString id = element.getAttribute(ID_ATTR);\n\t\tif (!parserContext.getRegistry().containsBeanDefinition(MESSAGE_MATCHER_BUILDER_BEAN_NAME)) {\n\t\t\tBeanDefinitionBuilder pathPatternMessageMatcherBuilder = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(PathPatternMessageMatcherBuilderFactoryBean.class);\n\t\t\tpathPatternMessageMatcherBuilder.setFallback(true);\n\t\t\tBeanDefinition bean = pathPatternMessageMatcherBuilder.getBeanDefinition();\n\t\t\tparserContext.registerBeanComponent(new BeanComponentDefinition(bean, MESSAGE_MATCHER_BUILDER_BEAN_NAME));\n\t\t}\n\t\tString inSecurityInterceptorName = parseAuthorization(element, parserContext);\n\t\tBeanDefinitionRegistry registry = parserContext.getRegistry();\n\t\tif (StringUtils.hasText(id)) {\n\t\t\tregistry.registerAlias(inSecurityInterceptorName, id);\n\t\t}\n\t\telse {\n\t\t\tboolean sameOriginDisabled = Boolean.parseBoolean(element.getAttribute(DISABLED_ATTR));\n\t\t\tXmlReaderContext context = parserContext.getReaderContext();\n\t\t\tBeanDefinitionBuilder mspp = BeanDefinitionBuilder.rootBeanDefinition(MessageSecurityPostProcessor.class);\n\t\t\tmspp.addConstructorArgValue(inSecurityInterceptorName);\n\t\t\tmspp.addConstructorArgValue(sameOriginDisabled);\n\t\t\tcontext.registerWithGeneratedName(mspp.getBeanDefinition());\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String parseAuthorization(Element element, ParserContext parserContext) {\n\t\tboolean useAuthorizationManager = true;\n\t\tif (StringUtils.hasText(element.getAttribute(USE_AUTHORIZATION_MANAGER_ATTR))) {\n\t\t\tuseAuthorizationManager = Boolean.parseBoolean(element.getAttribute(USE_AUTHORIZATION_MANAGER_ATTR));\n\t\t}\n\t\tif (useAuthorizationManager) {\n\t\t\treturn parseAuthorizationManager(element, parserContext);\n\t\t}\n\t\tif (StringUtils.hasText(element.getAttribute(AUTHORIZATION_MANAGER_REF_ATTR))) {\n\t\t\treturn parseAuthorizationManager(element, parserContext);\n\t\t}\n\t\treturn parseSecurityMetadataSource(element, parserContext);\n\t}\n\n\tprivate String parseAuthorizationManager(Element element, ParserContext parserContext) {\n\t\tXmlReaderContext context = parserContext.getReaderContext();\n\t\tString mdsId = createAuthorizationManager(element, parserContext);\n\t\tBeanDefinitionBuilder inboundChannelSecurityInterceptor = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(AuthorizationChannelInterceptor.class);\n\t\tinboundChannelSecurityInterceptor.addConstructorArgReference(mdsId);\n\t\tString holderStrategyRef = element.getAttribute(SECURITY_CONTEXT_HOLDER_STRATEGY_REF_ATTR);\n\t\tif (StringUtils.hasText(holderStrategyRef)) {\n\t\t\tinboundChannelSecurityInterceptor.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\t\tnew RuntimeBeanReference(holderStrategyRef));\n\t\t}\n\t\telse {\n\t\t\tinboundChannelSecurityInterceptor.addPropertyValue(\"securityContextHolderStrategy\",\n\t\t\t\t\tBeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderStrategyFactory.class)\n\t\t\t\t\t\t.getBeanDefinition());\n\t\t}\n\n\t\treturn context.registerWithGeneratedName(inboundChannelSecurityInterceptor.getBeanDefinition());\n\t}\n\n\tprivate String createAuthorizationManager(Element element, ParserContext parserContext) {\n\t\tXmlReaderContext context = parserContext.getReaderContext();\n\t\tString authorizationManagerRef = element.getAttribute(AUTHORIZATION_MANAGER_REF_ATTR);\n\t\tif (StringUtils.hasText(authorizationManagerRef)) {\n\t\t\treturn authorizationManagerRef;\n\t\t}\n\t\tElement expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);\n\t\tString expressionHandlerRef = (expressionHandlerElt != null) ? expressionHandlerElt.getAttribute(\"ref\") : null;\n\t\tManagedMap<BeanDefinition, BeanDefinition> matcherToExpression = new ManagedMap<>();\n\t\tList<Element> interceptMessages = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_MESSAGE);\n\t\tfor (Element interceptMessage : interceptMessages) {\n\t\t\tString matcherPattern = interceptMessage.getAttribute(PATTERN_ATTR);\n\t\t\tString accessExpression = interceptMessage.getAttribute(ACCESS_ATTR);\n\t\t\tString messageType = interceptMessage.getAttribute(TYPE_ATTR);\n\t\t\tBeanDefinition matcher = createMatcher(matcherPattern, messageType, parserContext, interceptMessage);\n\t\t\tBeanDefinitionBuilder authorizationManager = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(MessageExpressionAuthorizationManager.class);\n\t\t\tif (StringUtils.hasText(expressionHandlerRef)) {\n\t\t\t\tBeanDefinitionBuilder authorizationManagerBuilder = BeanDefinitionBuilder\n\t\t\t\t\t.rootBeanDefinition(MessageExpressionAuthorizationManager.class);\n\t\t\t\tauthorizationManagerBuilder.setFactoryMethod(\"withSecurityExpressionHandler\");\n\t\t\t\tauthorizationManagerBuilder.addConstructorArgReference(expressionHandlerRef);\n\t\t\t\tString authorizationManagerBuilderRef = context\n\t\t\t\t\t.registerWithGeneratedName(authorizationManagerBuilder.getBeanDefinition());\n\t\t\t\tauthorizationManager.setFactoryMethodOnBean(\"expression\", authorizationManagerBuilderRef);\n\t\t\t}\n\t\t\tauthorizationManager.addConstructorArgValue(accessExpression);\n\t\t\tmatcherToExpression.put(matcher, authorizationManager.getBeanDefinition());\n\t\t}\n\t\tBeanDefinitionBuilder mds = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(MessageMatcherDelegatingAuthorizationManagerFactory.class);\n\t\tmds.setFactoryMethod(\"createMessageMatcherDelegatingAuthorizationManager\");\n\t\tmds.addConstructorArgValue(matcherToExpression);\n\t\treturn context.registerWithGeneratedName(mds.getBeanDefinition());\n\t}\n\n\tprivate String parseSecurityMetadataSource(Element element, ParserContext parserContext) {\n\t\tBeanDefinitionRegistry registry = parserContext.getRegistry();\n\t\tXmlReaderContext context = parserContext.getReaderContext();\n\t\tManagedMap<BeanDefinition, String> matcherToExpression = new ManagedMap<>();\n\t\tElement expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);\n\t\tString expressionHandlerRef = (expressionHandlerElt != null) ? expressionHandlerElt.getAttribute(\"ref\") : null;\n\t\tboolean expressionHandlerDefined = StringUtils.hasText(expressionHandlerRef);\n\t\tList<Element> interceptMessages = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_MESSAGE);\n\t\tfor (Element interceptMessage : interceptMessages) {\n\t\t\tString matcherPattern = interceptMessage.getAttribute(PATTERN_ATTR);\n\t\t\tString accessExpression = interceptMessage.getAttribute(ACCESS_ATTR);\n\t\t\tString messageType = interceptMessage.getAttribute(TYPE_ATTR);\n\t\t\tBeanDefinition matcher = createMatcher(matcherPattern, messageType, parserContext, interceptMessage);\n\t\t\tmatcherToExpression.put(matcher, accessExpression);\n\t\t}\n\t\tBeanDefinitionBuilder mds = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(ExpressionBasedMessageSecurityMetadataSourceFactory.class);\n\t\tmds.setFactoryMethod(\"createExpressionMessageMetadataSource\");\n\t\tmds.addConstructorArgValue(matcherToExpression);\n\t\tif (expressionHandlerDefined) {\n\t\t\tmds.addConstructorArgReference(expressionHandlerRef);\n\t\t}\n\t\tString mdsId = context.registerWithGeneratedName(mds.getBeanDefinition());\n\t\tManagedList<BeanDefinition> voters = new ManagedList<>();\n\t\tBeanDefinitionBuilder messageExpressionVoterBldr = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(MessageExpressionVoter.class);\n\t\tif (expressionHandlerDefined) {\n\t\t\tmessageExpressionVoterBldr.addPropertyReference(\"expressionHandler\", expressionHandlerRef);\n\t\t}\n\t\tvoters.add(messageExpressionVoterBldr.getBeanDefinition());\n\t\tBeanDefinitionBuilder adm = BeanDefinitionBuilder.rootBeanDefinition(ConsensusBased.class);\n\t\tadm.addConstructorArgValue(voters);\n\t\tBeanDefinitionBuilder inboundChannelSecurityInterceptor = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(ChannelSecurityInterceptor.class);\n\t\tinboundChannelSecurityInterceptor.addConstructorArgValue(registry.getBeanDefinition(mdsId));\n\t\tinboundChannelSecurityInterceptor.addPropertyValue(\"accessDecisionManager\", adm.getBeanDefinition());\n\t\treturn context.registerWithGeneratedName(inboundChannelSecurityInterceptor.getBeanDefinition());\n\t}\n\n\tprivate BeanDefinition createMatcher(String matcherPattern, String messageType, ParserContext parserContext,\n\t\t\tElement interceptMessage) {\n\t\tboolean hasPattern = StringUtils.hasText(matcherPattern);\n\t\tboolean hasMessageType = StringUtils.hasText(messageType);\n\t\tif (!hasPattern) {\n\t\t\tBeanDefinitionBuilder matcher = BeanDefinitionBuilder.rootBeanDefinition(SimpMessageTypeMatcher.class);\n\t\t\tmatcher.addConstructorArgValue(messageType);\n\t\t\treturn matcher.getBeanDefinition();\n\t\t}\n\t\tBeanDefinitionBuilder matcher = BeanDefinitionBuilder.rootBeanDefinition(MessageMatcherFactoryBean.class);\n\t\tmatcher.addConstructorArgValue(matcherPattern);\n\t\tif (hasMessageType) {\n\t\t\tSimpMessageType type = SimpMessageType.valueOf(messageType);\n\t\t\tmatcher.addConstructorArgValue(type);\n\t\t\tif (SimpMessageType.SUBSCRIBE != type && SimpMessageType.MESSAGE != type) {\n\t\t\t\tparserContext.getReaderContext()\n\t\t\t\t\t.error(\"Cannot use intercept-websocket@message-type=\" + messageType\n\t\t\t\t\t\t\t+ \" with a pattern because the type does not have a destination.\", interceptMessage);\n\t\t\t}\n\t\t}\n\t\treturn matcher.getBeanDefinition();\n\t}\n\n\tstatic class MessageSecurityPostProcessor implements BeanDefinitionRegistryPostProcessor {\n\n\t\t/**\n\t\t * This is not available prior to Spring 4.2\n\t\t */\n\t\tprivate static final String WEB_SOCKET_AMMH_CLASS_NAME = \"org.springframework.web.socket.messaging.WebSocketAnnotationMethodMessageHandler\";\n\n\t\tprivate static final String CLIENT_INBOUND_CHANNEL_BEAN_ID = \"clientInboundChannel\";\n\n\t\tprivate static final String CSRF_CHANNEL_INTERCEPTOR_BEAN_ID = \"csrfChannelInterceptor\";\n\n\t\tprivate static final String INTERCEPTORS_PROP = \"interceptors\";\n\n\t\tprivate static final String CUSTOM_ARG_RESOLVERS_PROP = \"customArgumentResolvers\";\n\n\t\tprivate static final String TEMPLATE_EXPRESSION_BEAN_ID = \"annotationExpressionTemplateDefaults\";\n\n\t\tprivate static final Set<String> CSRF_HANDSHAKE_HANDLER_CLASSES = Collections.unmodifiableSet(\n\t\t\t\tnew HashSet<>(Arrays.asList(\"org.springframework.web.socket.server.support.WebSocketHttpRequestHandler\",\n\t\t\t\t\t\t\"org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService\",\n\t\t\t\t\t\t\"org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService\")));\n\n\t\tprivate final String inboundSecurityInterceptorId;\n\n\t\tprivate final boolean sameOriginDisabled;\n\n\t\tMessageSecurityPostProcessor(String inboundSecurityInterceptorId, boolean sameOriginDisabled) {\n\t\t\tthis.inboundSecurityInterceptorId = inboundSecurityInterceptorId;\n\t\t\tthis.sameOriginDisabled = sameOriginDisabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\t\tString[] beanNames = registry.getBeanDefinitionNames();\n\t\t\tfor (String beanName : beanNames) {\n\t\t\t\tBeanDefinition bd = registry.getBeanDefinition(beanName);\n\t\t\t\tString beanClassName = bd.getBeanClassName();\n\t\t\t\tif (SimpAnnotationMethodMessageHandler.class.getName().equals(beanClassName)\n\t\t\t\t\t\t|| WEB_SOCKET_AMMH_CLASS_NAME.equals(beanClassName)) {\n\t\t\t\t\tPropertyValue current = bd.getPropertyValues().getPropertyValue(CUSTOM_ARG_RESOLVERS_PROP);\n\t\t\t\t\tManagedList<Object> argResolvers = new ManagedList<>();\n\t\t\t\t\tif (current != null) {\n\t\t\t\t\t\targResolvers.addAll((ManagedList<?>) current.getValue());\n\t\t\t\t\t}\n\t\t\t\t\tRootBeanDefinition beanDefinition = new RootBeanDefinition(\n\t\t\t\t\t\t\tAuthenticationPrincipalArgumentResolver.class);\n\t\t\t\t\tif (registry.containsBeanDefinition(TEMPLATE_EXPRESSION_BEAN_ID)) {\n\t\t\t\t\t\tbeanDefinition.getPropertyValues()\n\t\t\t\t\t\t\t.add(\"templateDefaults\", new RuntimeBeanReference(TEMPLATE_EXPRESSION_BEAN_ID));\n\t\t\t\t\t}\n\t\t\t\t\targResolvers.add(beanDefinition);\n\t\t\t\t\tbd.getPropertyValues().add(CUSTOM_ARG_RESOLVERS_PROP, argResolvers);\n\t\t\t\t}\n\t\t\t\telse if (CSRF_HANDSHAKE_HANDLER_CLASSES.contains(beanClassName)) {\n\t\t\t\t\taddCsrfTokenHandshakeInterceptor(bd);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!registry.containsBeanDefinition(CLIENT_INBOUND_CHANNEL_BEAN_ID)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tManagedList<Object> interceptors = new ManagedList<>();\n\t\t\tinterceptors.add(new RootBeanDefinition(SecurityContextChannelInterceptor.class));\n\t\t\tif (!this.sameOriginDisabled) {\n\t\t\t\tif (!registry.containsBeanDefinition(CSRF_CHANNEL_INTERCEPTOR_BEAN_ID)) {\n\t\t\t\t\tinterceptors.add(new RootBeanDefinition(XorCsrfChannelInterceptor.class));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tinterceptors.add(new RuntimeBeanReference(CSRF_CHANNEL_INTERCEPTOR_BEAN_ID));\n\t\t\t\t}\n\t\t\t}\n\t\t\tinterceptors.add(registry.getBeanDefinition(this.inboundSecurityInterceptorId));\n\t\t\tBeanDefinition inboundChannel = registry.getBeanDefinition(CLIENT_INBOUND_CHANNEL_BEAN_ID);\n\t\t\tPropertyValue currentInterceptorsPv = inboundChannel.getPropertyValues()\n\t\t\t\t.getPropertyValue(INTERCEPTORS_PROP);\n\t\t\tif (currentInterceptorsPv != null) {\n\t\t\t\tManagedList<?> currentInterceptors = (ManagedList<?>) currentInterceptorsPv.getValue();\n\t\t\t\tinterceptors.addAll(currentInterceptors);\n\t\t\t}\n\t\t\tinboundChannel.getPropertyValues().add(INTERCEPTORS_PROP, interceptors);\n\t\t}\n\n\t\tprivate void addCsrfTokenHandshakeInterceptor(BeanDefinition bd) {\n\t\t\tif (this.sameOriginDisabled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString interceptorPropertyName = \"handshakeInterceptors\";\n\t\t\tManagedList<? super Object> interceptors = new ManagedList<>();\n\t\t\tinterceptors.add(new RootBeanDefinition(CsrfTokenHandshakeInterceptor.class));\n\t\t\tinterceptors.addAll((ManagedList<Object>) bd.getPropertyValues().get(interceptorPropertyName));\n\t\t\tbd.getPropertyValues().add(interceptorPropertyName, interceptors);\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\n\t\t}\n\n\t}\n\n\tstatic class DelegatingPathMatcher implements PathMatcher {\n\n\t\tprivate PathMatcher delegate = new AntPathMatcher();\n\n\t\t@Override\n\t\tpublic boolean isPattern(String path) {\n\t\t\treturn this.delegate.isPattern(path);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean match(String pattern, String path) {\n\t\t\treturn this.delegate.match(pattern, path);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matchStart(String pattern, String path) {\n\t\t\treturn this.delegate.matchStart(pattern, path);\n\t\t}\n\n\t\t@Override\n\t\tpublic String extractPathWithinPattern(String pattern, String path) {\n\t\t\treturn this.delegate.extractPathWithinPattern(pattern, path);\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> extractUriTemplateVariables(String pattern, String path) {\n\t\t\treturn this.delegate.extractUriTemplateVariables(pattern, path);\n\t\t}\n\n\t\t@Override\n\t\tpublic Comparator<String> getPatternComparator(String path) {\n\t\t\treturn this.delegate.getPatternComparator(path);\n\t\t}\n\n\t\t@Override\n\t\tpublic String combine(String pattern1, String pattern2) {\n\t\t\treturn this.delegate.combine(pattern1, pattern2);\n\t\t}\n\n\t\tvoid setPathMatcher(PathMatcher pathMatcher) {\n\t\t\tthis.delegate = pathMatcher;\n\t\t}\n\n\t}\n\n\tprivate static class MessageMatcherDelegatingAuthorizationManagerFactory {\n\n\t\tprivate static AuthorizationManager<Message<?>> createMessageMatcherDelegatingAuthorizationManager(\n\t\t\t\tMap<MessageMatcher<?>, AuthorizationManager<MessageAuthorizationContext<?>>> beans) {\n\t\t\tMessageMatcherDelegatingAuthorizationManager.Builder builder = MessageMatcherDelegatingAuthorizationManager\n\t\t\t\t.builder();\n\t\t\tfor (Map.Entry<MessageMatcher<?>, AuthorizationManager<MessageAuthorizationContext<?>>> entry : beans\n\t\t\t\t.entrySet()) {\n\t\t\t\tbuilder.matchers(entry.getKey()).access(entry.getValue());\n\t\t\t}\n\t\t\treturn builder.anyMessage().permitAll().build();\n\t\t}\n\n\t}\n\n\tstatic class SecurityContextHolderStrategyFactory implements FactoryBean<SecurityContextHolderStrategy> {\n\n\t\t@Override\n\t\tpublic SecurityContextHolderStrategy getObject() throws Exception {\n\t\t\treturn SecurityContextHolder.getContextHolderStrategy();\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn SecurityContextHolderStrategy.class;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/AbstractRequestMatcherDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.http.HttpMethod\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher\nimport org.springframework.security.web.util.matcher.RequestMatcher\n\n/**\n * A base class that provides authorization rules for [RequestMatcher]s and patterns.\n *\n * @author Eleftheria Stein\n * @since 5.3\n */\n@SecurityMarker\nabstract class AbstractRequestMatcherDsl {\n    /**\n     * Matches any request.\n     */\n    val anyRequest: RequestMatcher = AnyRequestMatcher.INSTANCE\n\n    protected data class MatcherAuthorizationRule(val matcher: RequestMatcher,\n                                                  override val rule: String) : AuthorizationRule(rule)\n\n    protected data class MatcherAuthorizationManagerRule(val matcher: RequestMatcher,\n                                                         override val rule: AuthorizationManager<in RequestAuthorizationContext>) : AuthorizationManagerRule(rule)\n\n    protected data class PatternAuthorizationRule(val pattern: String,\n                                                  val patternType: PatternType,\n                                                  val servletPath: String? = null,\n                                                  val httpMethod: HttpMethod? = null,\n                                                  override val rule: String) : AuthorizationRule(rule)\n\n    protected data class PatternAuthorizationManagerRule(val pattern: String,\n                                                         val patternType: PatternType,\n                                                         val servletPath: String? = null,\n                                                         val httpMethod: HttpMethod? = null,\n                                                         override val rule: AuthorizationManager<in RequestAuthorizationContext>) : AuthorizationManagerRule(rule)\n\n    protected abstract class AuthorizationRule(open val rule: String)\n\n    protected abstract class AuthorizationManagerRule(open val rule: AuthorizationManager<in RequestAuthorizationContext>)\n\n    protected enum class PatternType {\n        ANT, MVC, PATH;\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/AnonymousDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.authentication.AuthenticationProvider\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.GrantedAuthority\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] anonymous authentication using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property key the key to identify tokens created for anonymous authentication\n * @property principal the principal for [Authentication] objects of anonymous users\n * @property authorities the [Authentication.getAuthorities] for anonymous users\n * @property authenticationProvider the [AuthenticationProvider] used to validate an\n * anonymous user\n * @property authenticationFilter the [AnonymousAuthenticationFilter] used to populate\n * an anonymous user.\n */\n@SecurityMarker\nclass AnonymousDsl {\n    var key: String? = null\n    var principal: Any? = null\n    var authorities: List<GrantedAuthority>? = null\n    var authenticationProvider: AuthenticationProvider? = null\n    var authenticationFilter: AnonymousAuthenticationFilter? = null\n\n    private var disabled = false\n\n    /**\n     * Disable anonymous authentication\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (AnonymousConfigurer<HttpSecurity>) -> Unit {\n        return { anonymous ->\n            key?.also { anonymous.key(key) }\n            principal?.also { anonymous.principal(principal) }\n            authorities?.also { anonymous.authorities(authorities) }\n            authenticationProvider?.also { anonymous.authenticationProvider(authenticationProvider) }\n            authenticationFilter?.also { anonymous.authenticationFilter(authenticationFilter) }\n            if (disabled) {\n                anonymous.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/AuthorizeHttpRequestsDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.beans.factory.getBeanProvider\nimport org.springframework.context.ApplicationContext\nimport org.springframework.core.ResolvableType\nimport org.springframework.http.HttpMethod\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer\nimport org.springframework.security.config.core.GrantedAuthorityDefaults\nimport org.springframework.security.web.access.IpAddressAuthorizationManager\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher\nimport org.springframework.security.web.util.matcher.RequestMatcher\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] request authorization using idiomatic Kotlin code.\n *\n * @author Yuriy Savchenko\n * @since 5.7\n */\nclass AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {\n\n    private val authorizationRules = mutableListOf<AuthorizationManagerRule>()\n    private val authorizationManagerFactory: AuthorizationManagerFactory<in RequestAuthorizationContext>\n\n    private val PATTERN_TYPE = PatternType.PATH\n\n    /**\n     * Adds a request authorization rule.\n     *\n     * @param matches the [RequestMatcher] to match incoming requests against\n     * @param access the [AuthorizationManager] to secure the matching request\n     * (i.e. created via hasAuthority(\"ROLE_USER\"))\n     */\n    fun authorize(matches: RequestMatcher = AnyRequestMatcher.INSTANCE,\n                  access: AuthorizationManager<in RequestAuthorizationContext>) {\n        authorizationRules.add(MatcherAuthorizationManagerRule(matches, access))\n    }\n\n    /**\n     * Adds a request authorization rule for an endpoint matching the provided\n     * pattern.\n     * If Spring MVC is on the classpath, it will use an MVC matcher.\n     * If Spring MVC is not on the classpath, it will use an ant matcher.\n     * The MVC will use the same rules that Spring MVC uses for matching.\n     * For example, often times a mapping of the path \"/path\" will match on\n     * \"/path\", \"/path/\", \"/path.html\", etc.\n     * If the current request will not be processed by Spring MVC, a reasonable default\n     * using the pattern as an ant pattern will be used.\n     *\n     * @param pattern the pattern to match incoming requests against.\n     * @param access the [AuthorizationManager] to secure the matching request\n     * (i.e. created via hasAuthority(\"ROLE_USER\"))\n     */\n    fun authorize(pattern: String,\n                  access: AuthorizationManager<in RequestAuthorizationContext>) {\n        authorizationRules.add(\n            PatternAuthorizationManagerRule(\n                pattern = pattern,\n                patternType = PATTERN_TYPE,\n                rule = access\n            )\n        )\n    }\n\n    /**\n     * Adds a request authorization rule for an endpoint matching the provided\n     * pattern.\n     * If Spring MVC is on the classpath, it will use an MVC matcher.\n     * If Spring MVC is not on the classpath, it will use an ant matcher.\n     * The MVC will use the same rules that Spring MVC uses for matching.\n     * For example, often times a mapping of the path \"/path\" will match on\n     * \"/path\", \"/path/\", \"/path.html\", etc.\n     * If the current request will not be processed by Spring MVC, a reasonable default\n     * using the pattern as an ant pattern will be used.\n     *\n     * @param method the HTTP method to match the income requests against.\n     * @param pattern the pattern to match incoming requests against.\n     * @param access the [AuthorizationManager] to secure the matching request\n     * (i.e. created via hasAuthority(\"ROLE_USER\"))\n     */\n    fun authorize(method: HttpMethod,\n                  pattern: String,\n                  access: AuthorizationManager<in RequestAuthorizationContext>) {\n        authorizationRules.add(\n            PatternAuthorizationManagerRule(\n                pattern = pattern,\n                patternType = PATTERN_TYPE,\n                httpMethod = method,\n                rule = access\n            )\n        )\n    }\n\n    /**\n     * Adds a request authorization rule for an endpoint matching the provided\n     * pattern.\n     * If Spring MVC is on the classpath, it will use an MVC matcher.\n     * If Spring MVC is not on the classpath, it will use an ant matcher.\n     * The MVC will use the same rules that Spring MVC uses for matching.\n     * For example, often times a mapping of the path \"/path\" will match on\n     * \"/path\", \"/path/\", \"/path.html\", etc.\n     * If the current request will not be processed by Spring MVC, a reasonable default\n     * using the pattern as an ant pattern will be used.\n     *\n     * @param pattern the pattern to match incoming requests against.\n     * @param servletPath the servlet path to match incoming requests against. This\n     * only applies when using an MVC pattern matcher.\n     * @param access the [AuthorizationManager] to secure the matching request\n     * (i.e. created via hasAuthority(\"ROLE_USER\"))\n     */\n    fun authorize(pattern: String,\n                  servletPath: String,\n                  access: AuthorizationManager<in RequestAuthorizationContext>) {\n        authorizationRules.add(\n            PatternAuthorizationManagerRule(\n                pattern = pattern,\n                patternType = PATTERN_TYPE,\n                servletPath = servletPath,\n                rule = access\n            )\n        )\n    }\n\n    /**\n     * Adds a request authorization rule for an endpoint matching the provided\n     * pattern.\n     * If Spring MVC is on the classpath, it will use an MVC matcher.\n     * If Spring MVC is not on the classpath, it will use an ant matcher.\n     * The MVC will use the same rules that Spring MVC uses for matching.\n     * For example, often times a mapping of the path \"/path\" will match on\n     * \"/path\", \"/path/\", \"/path.html\", etc.\n     * If the current request will not be processed by Spring MVC, a reasonable default\n     * using the pattern as an ant pattern will be used.\n     *\n     * @param method the HTTP method to match the income requests against.\n     * @param pattern the pattern to match incoming requests against.\n     * @param servletPath the servlet path to match incoming requests against. This\n     * only applies when using an MVC pattern matcher.\n     * @param access the [AuthorizationManager] to secure the matching request\n     * (i.e. created via hasAuthority(\"ROLE_USER\"))\n     */\n    fun authorize(method: HttpMethod,\n                  pattern: String,\n                  servletPath: String,\n                  access: AuthorizationManager<in RequestAuthorizationContext>) {\n        authorizationRules.add(\n            PatternAuthorizationManagerRule(\n                pattern = pattern,\n                patternType = PATTERN_TYPE,\n                servletPath = servletPath,\n                httpMethod = method,\n                rule = access\n            )\n        )\n    }\n\n    /**\n     * Specify that URLs require a particular authority.\n     *\n     * @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).\n     * @return the [AuthorizationManager] with the provided authority\n     */\n    fun hasAuthority(authority: String): AuthorizationManager<in RequestAuthorizationContext> = this.authorizationManagerFactory.hasAuthority(authority)\n\n    /**\n     * Specify that URLs require any of the provided authorities.\n     *\n     * @param authorities the authorities to require (i.e. ROLE_USER, ROLE_ADMIN, etc).\n     * @return the [AuthorizationManager] with the provided authorities\n     */\n    fun hasAnyAuthority(vararg authorities: String): AuthorizationManager<in RequestAuthorizationContext> = this.authorizationManagerFactory.hasAnyAuthority(*authorities)\n\n\n    /**\n     * Specify that URLs require any of the provided authorities.\n     *\n     * @param authorities the authorities to require (i.e. ROLE_USER, ROLE_ADMIN, etc).\n     * @return the [AuthorizationManager] with the provided authorities\n     */\n    fun hasAllAuthorities(vararg authorities: String): AuthorizationManager<in RequestAuthorizationContext> = this.authorizationManagerFactory.hasAllAuthorities(*authorities)\n\n    /**\n     * Specify that URLs require a particular role.\n     *\n     * @param role the role to require (i.e. USER, ADMIN, etc).\n     * @return the [AuthorizationManager] with the provided role\n     */\n    fun hasRole(role: String): AuthorizationManager<in RequestAuthorizationContext> = this.authorizationManagerFactory.hasRole(role)\n\n    /**\n     * Specify that URLs require any of the provided roles.\n     *\n     * @param roles the roles to require (i.e. USER, ADMIN, etc).\n     * @return the [AuthorizationManager] with the provided roles\n     */\n    fun hasAnyRole(vararg roles: String): AuthorizationManager<in RequestAuthorizationContext> = this.authorizationManagerFactory.hasAnyRole(*roles)\n\n    /**\n     * Specify that URLs require any of the provided roles.\n     *\n     * @param roles the roles to require (i.e. USER, ADMIN, etc).\n     * @return the [AuthorizationManager] with the provided roles\n     */\n    fun hasAllRoles(vararg roles: String): AuthorizationManager<in RequestAuthorizationContext> = this.authorizationManagerFactory.hasAllRoles(*roles)\n\n    /**\n     * Require a specific IP or range of IP addresses.\n     * @since 6.3\n     */\n    fun hasIpAddress(ipAddress: String): AuthorizationManager<RequestAuthorizationContext> =\n        IpAddressAuthorizationManager.hasIpAddress(ipAddress)\n\n    /**\n     * Specify that URLs are allowed by anyone.\n     */\n    val permitAll: AuthorizationManager<in RequestAuthorizationContext>\n\n    /**\n     * Specify that URLs are not allowed by anyone.\n     */\n    val denyAll: AuthorizationManager<in RequestAuthorizationContext>\n\n    /**\n     * Specify that URLs are allowed by any authenticated user.\n     */\n    val authenticated: AuthorizationManager<in RequestAuthorizationContext>\n\n    /**\n     * Specify that URLs are allowed by users who have authenticated and were not \"remembered\".\n     * @since 6.5\n     */\n    val fullyAuthenticated: AuthorizationManager<in RequestAuthorizationContext>\n\n    internal fun get(): (AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry) -> Unit {\n        return { requests ->\n            authorizationRules.forEach { rule ->\n                when (rule) {\n                    is MatcherAuthorizationManagerRule -> requests.requestMatchers(rule.matcher).access(rule.rule)\n                    is PatternAuthorizationManagerRule -> {\n                        var builder = requests.applicationContext.getBeanProvider(\n                            PathPatternRequestMatcher.Builder::class.java)\n                            .getIfUnique(PathPatternRequestMatcher::withDefaults)\n                        if (rule.servletPath != null) {\n                            builder = builder.basePath(rule.servletPath)\n                        }\n                        requests.requestMatchers(builder.matcher(rule.httpMethod, rule.pattern)).access(rule.rule)\n                    }\n                }\n            }\n        }\n    }\n\n    constructor(context: ApplicationContext) {\n        this.authorizationManagerFactory =  resolveAuthorizationManagerFactory(context)\n        this.authenticated = this.authorizationManagerFactory.authenticated()\n        this.denyAll = this.authorizationManagerFactory.denyAll()\n        this.fullyAuthenticated = this.authorizationManagerFactory.fullyAuthenticated()\n        this.permitAll = this.authorizationManagerFactory.permitAll()\n    }\n\n    private fun resolveAuthorizationManagerFactory(context: ApplicationContext): AuthorizationManagerFactory<in RequestAuthorizationContext> {\n        val factoryOfRequestAuthorizationContext = context.getBeanProvider<AuthorizationManagerFactory<RequestAuthorizationContext>>().getIfUnique()\n        if (factoryOfRequestAuthorizationContext != null) {\n            return factoryOfRequestAuthorizationContext\n        }\n        val factoryOfObjectType = ResolvableType.forClassWithGenerics(AuthorizationManagerFactory::class.java, Any::class.java)\n        val factoryOfAny = context.getBeanProvider<AuthorizationManagerFactory<Any>>(factoryOfObjectType).getIfUnique()\n        if (factoryOfAny != null) {\n            return factoryOfAny\n        }\n        val defaultFactory: DefaultAuthorizationManagerFactory<RequestAuthorizationContext> = DefaultAuthorizationManagerFactory()\n        val rolePrefix = resolveRolePrefix(context)\n        if (rolePrefix != null) {\n            defaultFactory.setRolePrefix(rolePrefix)\n        }\n        val roleHierarchy = resolveRoleHierarchy(context)\n        if (roleHierarchy != null) {\n            defaultFactory.setRoleHierarchy(roleHierarchy)\n        }\n        return defaultFactory\n    }\n\n    private fun resolveRolePrefix(context: ApplicationContext): String? {\n        val beanNames = context.getBeanNamesForType(GrantedAuthorityDefaults::class.java)\n        if (beanNames.isNotEmpty()) {\n            return context.getBean(GrantedAuthorityDefaults::class.java).rolePrefix\n        }\n        return null\n    }\n\n    private fun resolveRoleHierarchy(context: ApplicationContext): RoleHierarchy? {\n        val beanNames = context.getBeanNamesForType(RoleHierarchy::class.java)\n        if (beanNames.isNotEmpty()) {\n            return context.getBean(RoleHierarchy::class.java)\n        }\n        return null\n    }\n\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/CorsDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.CorsConfigurer\nimport org.springframework.web.cors.CorsConfigurationSource\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] CORS using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property configurationSource the [CorsConfigurationSource] to use.\n */\n@SecurityMarker\nclass CorsDsl {\n    var configurationSource: CorsConfigurationSource? = null\n\n    private var disabled = false\n\n    /**\n     * Disable CORS.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (CorsConfigurer<HttpSecurity>) -> Unit {\n        return { cors ->\n            configurationSource?.also { cors.configurationSource(configurationSource) }\n            if (disabled) {\n                cors.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/CsrfDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport jakarta.servlet.http.HttpServletRequest\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.CsrfConfigurer\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy\nimport org.springframework.security.web.csrf.CsrfTokenRepository\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler\nimport org.springframework.security.web.util.matcher.RequestMatcher\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] CSRF protection\n * using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property csrfTokenRepository the [CsrfTokenRepository] to use.\n * @property requireCsrfProtectionMatcher specify the [RequestMatcher] to use for\n * determining when CSRF should be applied.\n * @property sessionAuthenticationStrategy the [SessionAuthenticationStrategy] to use.\n * @property csrfTokenRequestHandler the [CsrfTokenRequestHandler] to use for making\n * the CSRF token available as a request attribute\n */\n@SecurityMarker\nclass CsrfDsl {\n    var csrfTokenRepository: CsrfTokenRepository? = null\n    var requireCsrfProtectionMatcher: RequestMatcher? = null\n    var sessionAuthenticationStrategy: SessionAuthenticationStrategy? = null\n    var csrfTokenRequestHandler: CsrfTokenRequestHandler? = null\n\n    private var ignoringRequestMatchers: Array<out RequestMatcher>? = null\n    private var ignoringRequestMatchersPatterns: Array<out String>? = null\n    private var disabled = false\n\n    /**\n     * Allows specifying [HttpServletRequest]s that should not use CSRF Protection\n     * even if they match the [requireCsrfProtectionMatcher].\n     *\n     * @param requestMatchers the request matchers that should not use CSRF\n     * protection\n     */\n    fun ignoringRequestMatchers(vararg requestMatchers: RequestMatcher) {\n        ignoringRequestMatchers = requestMatchers\n    }\n\n    /**\n     * Allows specifying [HttpServletRequest]s that should not use CSRF Protection\n     * even if they match the [requireCsrfProtectionMatcher].\n     *\n     * @param patterns the patterns that should not use CSRF protection\n     */\n    fun ignoringRequestMatchers(vararg patterns: String) {\n        ignoringRequestMatchersPatterns = patterns\n    }\n\n    /**\n     * Disable CSRF protection\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (CsrfConfigurer<HttpSecurity>) -> Unit {\n        return { csrf ->\n            csrfTokenRepository?.also { csrf.csrfTokenRepository(csrfTokenRepository) }\n            requireCsrfProtectionMatcher?.also { csrf.requireCsrfProtectionMatcher(requireCsrfProtectionMatcher) }\n            sessionAuthenticationStrategy?.also { csrf.sessionAuthenticationStrategy(sessionAuthenticationStrategy) }\n            csrfTokenRequestHandler?.also { csrf.csrfTokenRequestHandler(csrfTokenRequestHandler) }\n            ignoringRequestMatchers?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchers!!) }\n            ignoringRequestMatchersPatterns?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchersPatterns!!) }\n            if (disabled) {\n                csrf.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer\nimport org.springframework.security.web.AuthenticationEntryPoint\nimport org.springframework.security.web.access.AccessDeniedHandler\nimport org.springframework.security.web.util.matcher.RequestMatcher\nimport java.util.*\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] exception handling using idiomatic Kotlin\n * code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property accessDeniedPage the URL to the access denied page\n * @property accessDeniedHandler the [AccessDeniedHandler] to use\n * @property authenticationEntryPoint the [AuthenticationEntryPoint] to use\n */\n@SecurityMarker\nclass ExceptionHandlingDsl {\n    var accessDeniedPage: String? = null\n    var accessDeniedHandler: AccessDeniedHandler? = null\n    var authenticationEntryPoint: AuthenticationEntryPoint? = null\n\n    private var defaultDeniedHandlerMappings: LinkedHashMap<RequestMatcher, AccessDeniedHandler> = linkedMapOf()\n    private var defaultEntryPointMappings: LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> = linkedMapOf()\n    private var disabled = false\n\n    /**\n     * Sets a default [AccessDeniedHandler] to be used which prefers being\n     * invoked for the provided [RequestMatcher].\n     *\n     * @param deniedHandler the [AccessDeniedHandler] to use\n     * @param preferredMatcher the [RequestMatcher] for this default\n     * [AccessDeniedHandler]\n     */\n    fun defaultAccessDeniedHandlerFor(deniedHandler: AccessDeniedHandler, preferredMatcher: RequestMatcher) {\n        defaultDeniedHandlerMappings[preferredMatcher] = deniedHandler\n    }\n\n    /**\n     * Sets a default [AuthenticationEntryPoint] to be used which prefers being\n     * invoked for the provided [RequestMatcher].\n     *\n     * @param entryPoint the [AuthenticationEntryPoint] to use\n     * @param preferredMatcher the [RequestMatcher] for this default\n     * [AccessDeniedHandler]\n     */\n    fun defaultAuthenticationEntryPointFor(entryPoint: AuthenticationEntryPoint, preferredMatcher: RequestMatcher) {\n        defaultEntryPointMappings[preferredMatcher] = entryPoint\n    }\n\n    /**\n     * Disable exception handling.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ExceptionHandlingConfigurer<HttpSecurity>) -> Unit {\n        return { exceptionHandling ->\n            accessDeniedPage?.also { exceptionHandling.accessDeniedPage(accessDeniedPage) }\n            accessDeniedHandler?.also { exceptionHandling.accessDeniedHandler(accessDeniedHandler) }\n            authenticationEntryPoint?.also { exceptionHandling.authenticationEntryPoint(authenticationEntryPoint) }\n            defaultDeniedHandlerMappings.forEach { (matcher, handler) ->\n                exceptionHandling.defaultAccessDeniedHandlerFor(handler, matcher)\n            }\n            defaultEntryPointMappings.forEach { (matcher, entryPoint) ->\n                exceptionHandling.defaultAuthenticationEntryPointFor(entryPoint, matcher)\n            }\n            if (disabled) {\n                exceptionHandling.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/FormLoginDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.authentication.AuthenticationDetailsSource\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler\nimport jakarta.servlet.http.HttpServletRequest\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] form login using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property loginPage the login page to redirect to if authentication is required (i.e.\n * \"/login\")\n * @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after\n * authentication success\n * @property authenticationFailureHandler the [AuthenticationFailureHandler] used after\n * authentication failure\n * @property failureUrl the URL to send users if authentication fails\n * @property loginProcessingUrl the URL to validate the credentials\n * @property permitAll whether to grant access to the urls for [failureUrl] as well as\n * for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user\n * @property usernameParameter the HTTP parameter to look for the username when performing authentication\n * @property passwordParameter the HTTP parameter to look for the password when performing authentication\n */\n@SecurityMarker\nclass FormLoginDsl {\n    var loginPage: String? = null\n    var authenticationSuccessHandler: AuthenticationSuccessHandler? = null\n    var authenticationFailureHandler: AuthenticationFailureHandler? = null\n    var failureUrl: String? = null\n    var loginProcessingUrl: String? = null\n    var permitAll: Boolean? = null\n    var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>? = null\n    var usernameParameter: String? = null\n    var passwordParameter: String? = null\n\n    private var defaultSuccessUrlOption: Pair<String, Boolean>? = null\n\n    private var disabled = false\n\n    /**\n     * Disable FormLogin.\n     *\n     * @since 6.1\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    /**\n     * Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the\n     * [loginPage] and [loginProcessingUrl] for every user.\n     */\n    fun permitAll() {\n        permitAll = true\n    }\n\n    /**\n     * Specifies where users will be redirected after authenticating successfully if\n     * they have not visited a secured page prior to authenticating or [alwaysUse]\n     * is true.\n     *\n     * @param defaultSuccessUrl the default success url\n     * @param alwaysUse true if the [defaultSuccessUrl] should be used after\n     * authentication despite if a protected page had been previously visited\n     */\n    fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) {\n        defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse)\n    }\n\n    internal fun get(): (FormLoginConfigurer<HttpSecurity>) -> Unit {\n        return { login ->\n            loginPage?.also { login.loginPage(loginPage) }\n            failureUrl?.also { login.failureUrl(failureUrl) }\n            loginProcessingUrl?.also { login.loginProcessingUrl(loginProcessingUrl) }\n            permitAll?.also { login.permitAll(permitAll!!) }\n            defaultSuccessUrlOption?.also {\n                login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second)\n            }\n            authenticationSuccessHandler?.also { login.successHandler(authenticationSuccessHandler) }\n            authenticationFailureHandler?.also { login.failureHandler(authenticationFailureHandler) }\n            authenticationDetailsSource?.also { login.authenticationDetailsSource(authenticationDetailsSource) }\n            usernameParameter?.also { login.usernameParameter(usernameParameter) }\n            passwordParameter?.also { login.passwordParameter(passwordParameter) }\n            if (disabled) {\n                login.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/HeadersDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.config.annotation.web.headers.*\nimport org.springframework.security.web.header.HeaderWriter\nimport org.springframework.security.web.header.writers.*\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] headers using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property defaultsDisabled whether all of the default headers should be included in the response\n */\n@SecurityMarker\nclass HeadersDsl {\n    private var contentTypeOptions: ((HeadersConfigurer<HttpSecurity>.ContentTypeOptionsConfig) -> Unit)? = null\n    private var xssProtection: ((HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit)? = null\n    private var cacheControl: ((HeadersConfigurer<HttpSecurity>.CacheControlConfig) -> Unit)? = null\n    private var hsts: ((HeadersConfigurer<HttpSecurity>.HstsConfig) -> Unit)? = null\n    private var frameOptions: ((HeadersConfigurer<HttpSecurity>.FrameOptionsConfig) -> Unit)? = null\n    @Suppress(\"DEPRECATION\")\n    private var hpkp: ((HeadersConfigurer<HttpSecurity>.HpkpConfig) -> Unit)? = null\n    private var contentSecurityPolicy: ((HeadersConfigurer<HttpSecurity>.ContentSecurityPolicyConfig) -> Unit)? = null\n    private var referrerPolicy: ((HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit)? = null\n    @Suppress(\"DEPRECATION\")\n    private var featurePolicyDirectives: String? = null\n    private var permissionsPolicy: ((HeadersConfigurer<HttpSecurity>.PermissionsPolicyConfig) -> Unit)? = null\n    private var crossOriginOpenerPolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginOpenerPolicyConfig) -> Unit)? = null\n    private var crossOriginEmbedderPolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginEmbedderPolicyConfig) -> Unit)? = null\n    private var crossOriginResourcePolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginResourcePolicyConfig) -> Unit)? = null\n    private var disabled = false\n    private var headerWriters = mutableListOf<HeaderWriter>()\n\n    var defaultsDisabled: Boolean? = null\n\n    /**\n     * Configures the [XContentTypeOptionsHeaderWriter] which inserts the <a href=\n     * \"https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx\"\n     * >X-Content-Type-Options header</a>\n     *\n     * @param contentTypeOptionsConfig the customization to apply to the header\n     */\n    fun contentTypeOptions(contentTypeOptionsConfig: ContentTypeOptionsDsl.() -> Unit) {\n        this.contentTypeOptions = ContentTypeOptionsDsl().apply(contentTypeOptionsConfig).get()\n    }\n\n    /**\n     * <strong>Note this is not comprehensive XSS protection!</strong>\n     *\n     * <p>\n     * Allows customizing the [XXssProtectionHeaderWriter] which adds the <a href=\n     * \"https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx\"\n     * >X-XSS-Protection header</a>\n     * </p>\n     *\n     * @param xssProtectionConfig the customization to apply to the header\n     */\n    fun xssProtection(xssProtectionConfig: XssProtectionConfigDsl.() -> Unit) {\n        this.xssProtection = XssProtectionConfigDsl().apply(xssProtectionConfig).get()\n    }\n\n    /**\n     * Allows customizing the [CacheControlHeadersWriter]. Specifically it adds the\n     * following headers:\n     * <ul>\n     * <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>\n     * <li>Pragma: no-cache</li>\n     * <li>Expires: 0</li>\n     * </ul>\n     *\n     * @param cacheControlConfig the customization to apply to the header\n     */\n    fun cacheControl(cacheControlConfig: CacheControlDsl.() -> Unit) {\n        this.cacheControl = CacheControlDsl().apply(cacheControlConfig).get()\n    }\n\n    /**\n     * Allows customizing the [HstsHeaderWriter] which provides support for <a\n     * href=\"https://tools.ietf.org/html/rfc6797\">HTTP Strict Transport Security\n     * (HSTS)</a>.\n     *\n     * @param hstsConfig the customization to apply to the header\n     */\n    fun httpStrictTransportSecurity(hstsConfig: HttpStrictTransportSecurityDsl.() -> Unit) {\n        this.hsts = HttpStrictTransportSecurityDsl().apply(hstsConfig).get()\n    }\n\n    /**\n     * Allows customizing the [XFrameOptionsHeaderWriter] which add the X-Frame-Options\n     * header.\n     *\n     * @param frameOptionsConfig the customization to apply to the header\n     */\n    fun frameOptions(frameOptionsConfig: FrameOptionsDsl.() -> Unit) {\n        this.frameOptions = FrameOptionsDsl().apply(frameOptionsConfig).get()\n    }\n\n    /**\n     * Allows customizing the [HpkpHeaderWriter] which provides support for <a\n     * href=\"https://tools.ietf.org/html/rfc7469\">HTTP Public Key Pinning (HPKP)</a>.\n     *\n     * @param hpkpConfig the customization to apply to the header\n     * @deprecated see <a href=\"https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning\">Certificate and Public Key Pinning</a> for more context\n     */\n    @Deprecated(message = \"as of 5.8 with no replacement\")\n    @Suppress(\"DEPRECATION\")\n    fun httpPublicKeyPinning(hpkpConfig: HttpPublicKeyPinningDsl.() -> Unit) {\n        this.hpkp = HttpPublicKeyPinningDsl().apply(hpkpConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://www.w3.org/TR/CSP2/\">Content Security Policy (CSP) Level 2</a>.\n     *\n     * <p>\n     * Calling this method automatically enables (includes) the Content-Security-Policy header in the response\n     * using the supplied security policy directive(s).\n     * </p>\n     *\n     * @param contentSecurityPolicyConfig the customization to apply to the header\n     */\n    fun contentSecurityPolicy(contentSecurityPolicyConfig: ContentSecurityPolicyDsl.() -> Unit) {\n        this.contentSecurityPolicy = ContentSecurityPolicyDsl().apply(contentSecurityPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://www.w3.org/TR/referrer-policy/\">Referrer Policy</a>.\n     *\n     * <p>\n     * Configuration is provided to the [ReferrerPolicyHeaderWriter] which support the writing\n     * of the header as detailed in the W3C Technical Report:\n     * </p>\n     * <ul>\n     *  <li>Referrer-Policy</li>\n     * </ul>\n     *\n     * @param referrerPolicyConfig the customization to apply to the header\n     */\n    fun referrerPolicy(referrerPolicyConfig: ReferrerPolicyDsl.() -> Unit) {\n        this.referrerPolicy = ReferrerPolicyDsl().apply(referrerPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://wicg.github.io/feature-policy/\">Feature\n     * Policy</a>.\n     *\n     * <p>\n     * Calling this method automatically enables (includes) the Feature-Policy\n     * header in the response using the supplied policy directive(s).\n     * <p>\n     *\n     * @param policyDirectives policyDirectives the security policy directive(s)\n     */\n    @Deprecated(\"Use 'permissionsPolicy { }' instead.\")\n    @Suppress(\"DEPRECATION\")\n    fun featurePolicy(policyDirectives: String) {\n        this.featurePolicyDirectives = policyDirectives\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://w3c.github.io/webappsec-permissions-policy/\">Permissions\n     * Policy</a>.\n     *\n     * <p>\n     * Calling this method automatically enables (includes) the Permissions-Policy\n     * header in the response using the supplied policy directive(s).\n     * <p>\n     *\n     * @param permissionsPolicyConfig the customization to apply to the header\n     */\n    fun permissionsPolicy(permissionsPolicyConfig: PermissionsPolicyDsl.() -> Unit) {\n        this.permissionsPolicy = PermissionsPolicyDsl().apply(permissionsPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy\">\n     * Cross-Origin-Opener-Policy</a> header.\n     *\n     * <p>\n     * Calling this method automatically enables (includes) the Cross-Origin-Opener-Policy\n     * header in the response using the supplied policy.\n     * <p>\n     *\n     * @since 5.7\n     * @param crossOriginOpenerPolicyConfig the customization to apply to the header\n     */\n    fun crossOriginOpenerPolicy(crossOriginOpenerPolicyConfig: CrossOriginOpenerPolicyDsl.() -> Unit) {\n        this.crossOriginOpenerPolicy = CrossOriginOpenerPolicyDsl().apply(crossOriginOpenerPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy\">\n     * Cross-Origin-Embedder-Policy</a> header.\n     *\n     * <p>\n     * Calling this method automatically enables (includes) the Cross-Origin-Embedder-Policy\n     * header in the response using the supplied policy.\n     * <p>\n     *\n     * @since 5.7\n     * @param crossOriginEmbedderPolicyConfig the customization to apply to the header\n     */\n    fun crossOriginEmbedderPolicy(crossOriginEmbedderPolicyConfig: CrossOriginEmbedderPolicyDsl.() -> Unit) {\n        this.crossOriginEmbedderPolicy = CrossOriginEmbedderPolicyDsl().apply(crossOriginEmbedderPolicyConfig).get()\n    }\n\n    /**\n     * Configures the <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy\">\n     * Cross-Origin-Resource-Policy</a> header.\n     *\n     * <p>\n     * Calling this method automatically enables (includes) the Cross-Origin-Resource-Policy\n     * header in the response using the supplied policy.\n     * <p>\n     *\n     * @since 5.7\n     * @param crossOriginResourcePolicyConfig the customization to apply to the header\n     */\n    fun crossOriginResourcePolicy(crossOriginResourcePolicyConfig: CrossOriginResourcePolicyDsl.() -> Unit) {\n        this.crossOriginResourcePolicy = CrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()\n    }\n\n    /**\n     * Adds a [HeaderWriter] instance.\n     *\n     * @param headerWriter the [HeaderWriter] instance to add\n     * @since 5.4\n     */\n    fun addHeaderWriter(headerWriter: HeaderWriter) {\n        this.headerWriters.add(headerWriter)\n    }\n\n    /**\n     * Disable all HTTP security headers.\n     *\n     * @since 5.4\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    @Suppress(\"DEPRECATION\")\n    internal fun get(): (HeadersConfigurer<HttpSecurity>) -> Unit {\n        return { headers ->\n            defaultsDisabled?.also {\n                if (defaultsDisabled!!) {\n                    headers.defaultsDisabled()\n                }\n            }\n            contentTypeOptions?.also {\n                headers.contentTypeOptions(contentTypeOptions)\n            }\n            xssProtection?.also {\n                headers.xssProtection(xssProtection)\n            }\n            cacheControl?.also {\n                headers.cacheControl(cacheControl)\n            }\n            hsts?.also {\n                headers.httpStrictTransportSecurity(hsts)\n            }\n            frameOptions?.also {\n                headers.frameOptions(frameOptions)\n            }\n            hpkp?.also {\n                headers.httpPublicKeyPinning(hpkp)\n            }\n            contentSecurityPolicy?.also {\n                headers.contentSecurityPolicy(contentSecurityPolicy)\n            }\n            referrerPolicy?.also {\n                headers.referrerPolicy(referrerPolicy)\n            }\n            featurePolicyDirectives?.also {\n                headers.featurePolicy(featurePolicyDirectives)\n            }\n            permissionsPolicy?.also {\n                headers.permissionsPolicy(permissionsPolicy)\n            }\n            crossOriginOpenerPolicy?.also {\n                headers.crossOriginOpenerPolicy(crossOriginOpenerPolicy)\n            }\n            crossOriginEmbedderPolicy?.also {\n                headers.crossOriginEmbedderPolicy(crossOriginEmbedderPolicy)\n            }\n            crossOriginResourcePolicy?.also {\n                headers.crossOriginResourcePolicy(crossOriginResourcePolicy)\n            }\n            headerWriters.forEach { headerWriter ->\n                headers.addHeaderWriter(headerWriter)\n            }\n            if (disabled) {\n                headers.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/HttpBasicDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.authentication.AuthenticationDetailsSource\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer\nimport org.springframework.security.web.AuthenticationEntryPoint\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter\nimport jakarta.servlet.http.HttpServletRequest\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] basic authentication using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property realmName the HTTP Basic realm to use. If [authenticationEntryPoint]\n * has been invoked, invoking this method will result in an error.\n * @property authenticationEntryPoint the [AuthenticationEntryPoint] to be populated on\n * [BasicAuthenticationFilter] in the event that authentication fails.\n * @property authenticationDetailsSource the custom [AuthenticationDetailsSource] to use for\n * basic authentication.\n */\n@SecurityMarker\nclass HttpBasicDsl {\n    var realmName: String? = null\n    var authenticationEntryPoint: AuthenticationEntryPoint? = null\n    var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>? = null\n\n    private var disabled = false\n\n    /**\n     * Disables HTTP basic authentication\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (HttpBasicConfigurer<HttpSecurity>) -> Unit {\n        return { httpBasic ->\n            realmName?.also { httpBasic.realmName(realmName) }\n            authenticationEntryPoint?.also { httpBasic.authenticationEntryPoint(authenticationEntryPoint) }\n            authenticationDetailsSource?.also { httpBasic.authenticationDetailsSource(authenticationDetailsSource) }\n            if (disabled) {\n                httpBasic.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport jakarta.servlet.Filter\nimport jakarta.servlet.http.HttpServletRequest\nimport org.springframework.beans.factory.ObjectProvider\nimport org.springframework.context.ApplicationContext\nimport org.springframework.core.MethodParameter\nimport org.springframework.core.ResolvableType\nimport org.springframework.core.annotation.AnnotationUtils\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\nimport org.springframework.security.web.DefaultSecurityFilterChain\nimport org.springframework.security.web.util.matcher.RequestMatcher\nimport org.springframework.util.ReflectionUtils\nimport java.lang.reflect.Method\nimport java.lang.reflect.Modifier\n\n/**\n * Configures [HttpSecurity] using a [HttpSecurity Kotlin DSL][HttpSecurityDsl].\n *\n * Example:\n *\n * ```\n * @Configuration\n * @EnableWebSecurity\n * class SecurityConfig {\n *\n *     @Bean\n *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n *         http {\n *             authorizeHttpRequests {\n *                 authorize(\"/public\", permitAll)\n *                 authorize(anyRequest, authenticated)\n *             }\n *             formLogin {\n *                 loginPage = \"/log-in\"\n *             }\n *         }\n *         return http.build()\n *     }\n * }\n * ```\n *\n * @author Eleftheria Stein\n * @author Norbert Nowak\n * @since 5.3\n * @param httpConfiguration the configurations to apply to [HttpSecurity]\n */\noperator fun HttpSecurity.invoke(httpConfiguration: HttpSecurityDsl.() -> Unit) =\n    HttpSecurityDsl(this, httpConfiguration).build()\n\n/**\n * An [HttpSecurity] Kotlin DSL created by [`http { }`][invoke]\n * in order to configure [HttpSecurity] using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @param http the [HttpSecurity] which all configurations will be applied to\n * @param init the configurations to apply to the provided [HttpSecurity]\n * @property authenticationManager the default [AuthenticationManager] to use\n */\n@SecurityMarker\nclass HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecurityDsl.() -> Unit) {\n\n    var authenticationManager: AuthenticationManager? = null\n    val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)\n\n    init {\n        applyFunction1HttpSecurityDslBeans(this.context, this)\n        applyTopLevelFunction1SecurityDslBeans(this.context, this)\n    }\n\n\n    /**\n     * Applies all `Function1<HttpSecurity,Unit>` Beans which\n     * allows exposing the DSL as Beans to be applied.\n     *\n     * ```\n     * @Bean\n     * fun httpSecurityDslBean(): HttpSecurityDsl.() -> Unit {\n     *     return {\n     *         headers {\n     *             contentSecurityPolicy {\n     *                 policyDirectives = \"object-src 'none'\"\n     *             }\n     *         }\n     *         redirectToHttps { }\n     *    }\n     * }\n     * ```\n     */\n    private fun applyFunction1HttpSecurityDslBeans(context: ApplicationContext, http: HttpSecurityDsl) : Unit {\n        val httpSecurityDslFnType = ResolvableType.forClassWithGenerics(Function1::class.java,\n            HttpSecurityDsl::class.java, Unit::class.java)\n        val httpSecurityDslFnProvider = context\n            .getBeanProvider<Function1<HttpSecurityDsl,Unit>>(httpSecurityDslFnType)\n\n        // @formatter:off\n        httpSecurityDslFnProvider.orderedStream().forEach { fn -> fn.invoke(http) }\n        // @formatter:on\n    }\n\n    /**\n     * Applies all `Function1<T,Unit>` Beans such that `T` is a top level\n     * DSL on `HttpSecurityDsl`. This allows exposing the top level\n     * DSLs as Beans to be applied.\n     *\n     *\n     * ```\n     * @Bean\n     * fun httpSecurityCustomizer(): ThrowingCustomizer<HttpSecurity> {\n     *     return ThrowingCustomizer { http -> http\n     *         .headers { headers -> headers\n     *             .contentSecurityPolicy { csp -> csp\n     *                 .policyDirectives(\"object-src 'none'\")\n     *             }\n     *         }\n     *         .redirectToHttps(Customizer.withDefaults())\n     *     }\n     * }\n     * ```\n     *\n     * @param context the [ApplicationContext]\n     * @param http the [HttpSecurity]\n     * @throws Exception\n     */\n    private fun applyTopLevelFunction1SecurityDslBeans(context: ApplicationContext, http: HttpSecurityDsl) {\n        val isCustomizerMethod = ReflectionUtils.MethodFilter { method: Method ->\n            if (Modifier.isStatic(method.modifiers)) {\n                return@MethodFilter false\n            }\n            if (!Modifier.isPublic(method.modifiers)) {\n                return@MethodFilter false\n            }\n            if (!method.canAccess(http)) {\n                return@MethodFilter false\n            }\n            if (method.parameterCount != 1) {\n                return@MethodFilter false\n            }\n            return@MethodFilter extractDslType(method) != null\n        }\n        val invokeWithEachDslBean = ReflectionUtils.MethodCallback { dslMethod: Method ->\n            val dslFunctionType = firstMethodResolvableType(dslMethod)!!\n            val dslFunctionProvider: ObjectProvider<*> = context.getBeanProvider<Any>(dslFunctionType)\n\n            // @formatter:off\n            dslFunctionProvider.orderedStream().forEach {customizer: Any -> ReflectionUtils.invokeMethod(dslMethod, http, customizer)}\n        }\n        ReflectionUtils.doWithMethods(HttpSecurityDsl::class.java, invokeWithEachDslBean, isCustomizerMethod)\n    }\n\n    /**\n     * From a `Method` with the first argument `Function<T,Unit>` return `T` or `null`\n     * if the first argument is not a `Function`.\n     * @return From a `Method` with the first argument `Function<T,Unit>` return `T`.\n     */\n    private fun extractDslType(method: Method): ResolvableType? {\n        val functionType = firstMethodResolvableType(method)\n        if (!Function::class.java.isAssignableFrom(functionType.toClass())) {\n            return null\n        }\n        val functionInputType = functionType.getGeneric(0)\n        val securityMarker = AnnotationUtils.findAnnotation(functionInputType.toClass(), SecurityMarker::class.java)\n        val isSecurityDsl = securityMarker != null\n        if (!isSecurityDsl) {\n            return null\n        }\n        return functionInputType\n    }\n\n    private fun firstMethodResolvableType(method: Method): ResolvableType {\n        val parameter = MethodParameter(\n            method, 0\n        )\n        return ResolvableType.forMethodParameter(parameter)\n    }\n\n    /**\n     * Applies a [SecurityConfigurerAdapter] to this [HttpSecurity]\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             apply(CustomSecurityConfigurer<HttpSecurity>()) {\n     *                 customProperty = \"...\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param configurer\n     * the [SecurityConfigurerAdapter] for further customizations\n     */\n    fun <C : SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> apply(\n        configurer: C,\n        configuration: C.() -> Unit = { }\n    ): C {\n        this.http.with(configurer, configuration)\n        return configurer\n    }\n\n    /**\n     * Applies a [SecurityConfigurerAdapter] to this [HttpSecurity]\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             with(CustomSecurityConfigurer<HttpSecurity>()) {\n     *                 customProperty = \"...\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param configurer\n     * the [HttpSecurity] for further customizations\n     * @since 6.2\n     */\n    fun <C : SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> with(\n        configurer: C,\n        configuration: C.() -> Unit = { }\n    ): HttpSecurity? {\n        return this.http.with(configurer, configuration)\n    }\n\n    /**\n     * Allows configuring the [HttpSecurity] to only be invoked when matching the\n     * provided pattern.\n     * If Spring MVC is on the classpath, it will use an MVC matcher.\n     * If Spring MVC is not an the classpath, it will use an ant matcher.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             securityMatcher(\"/private/&ast;&ast;\")\n     *             formLogin {\n     *                 loginPage = \"/log-in\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param pattern one or more patterns used to determine whether this\n     * configuration should be invoked.\n     */\n    fun securityMatcher(vararg pattern: String) {\n        this.http.securityMatchers {\n            it.requestMatchers(*pattern)\n        }\n    }\n\n    /**\n     * Allows configuring the [HttpSecurity] to only be invoked when matching the\n     * provided [RequestMatcher].\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             securityMatcher(pathPattern(\"/private/&ast;&ast;\"))\n     *             formLogin {\n     *                 loginPage = \"/log-in\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param requestMatcher one or more [RequestMatcher] used to determine whether\n     * this configuration should be invoked.\n     */\n    fun securityMatcher(vararg requestMatcher: RequestMatcher) {\n        this.http.securityMatchers {\n            it.requestMatchers(*requestMatcher)\n        }\n    }\n\n    /**\n     * Enables form based authentication.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             formLogin {\n     *                 loginPage = \"/log-in\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param formLoginConfiguration custom configurations to be applied\n     * to the form based authentication\n     * @see [FormLoginDsl]\n     */\n    fun formLogin(formLoginConfiguration: FormLoginDsl.() -> Unit) {\n        val loginCustomizer = FormLoginDsl().apply(formLoginConfiguration).get()\n        this.http.formLogin(loginCustomizer)\n    }\n\n    /**\n     * Allows restricting access based upon the [HttpServletRequest]\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             authorizeHttpRequests {\n     *                 authorize(\"/public\", permitAll)\n     *                 authorize(anyRequest, authenticated)\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param authorizeHttpRequestsConfiguration custom configuration that specifies\n     * access for requests\n     * @see [AuthorizeHttpRequestsDsl]\n     * @since 5.7\n     */\n    fun authorizeHttpRequests(authorizeHttpRequestsConfiguration: AuthorizeHttpRequestsDsl.() -> Unit) {\n        val authorizeHttpRequestsCustomizer =\n            AuthorizeHttpRequestsDsl(this.context).apply(authorizeHttpRequestsConfiguration).get()\n        this.http.authorizeHttpRequests(authorizeHttpRequestsCustomizer)\n    }\n\n    /**\n     * Enables HTTP basic authentication.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             httpBasic {\n     *                 realmName = \"Custom Realm\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param httpBasicConfiguration custom configurations to be applied to the\n     * HTTP basic authentication\n     * @see [HttpBasicDsl]\n     */\n    fun httpBasic(httpBasicConfiguration: HttpBasicDsl.() -> Unit) {\n        val httpBasicCustomizer = HttpBasicDsl().apply(httpBasicConfiguration).get()\n        this.http.httpBasic(httpBasicCustomizer)\n    }\n\n    /**\n     * Enables password management.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             passwordManagement {\n     *                 changePasswordPage = \"/custom-change-password-page\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param passwordManagementConfiguration custom configurations to be applied to the\n     * password management\n     * @see [PasswordManagementDsl]\n     * @since 5.6\n     */\n    fun passwordManagement(passwordManagementConfiguration: PasswordManagementDsl.() -> Unit) {\n        val passwordManagementCustomizer = PasswordManagementDsl().apply(passwordManagementConfiguration).get()\n        this.http.passwordManagement(passwordManagementCustomizer)\n    }\n\n    /**\n     * Allows configuring response headers.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             headers {\n     *                 referrerPolicy {\n     *                     policy = ReferrerPolicy.SAME_ORIGIN\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param headersConfiguration custom configurations to configure the\n     * response headers\n     * @see [HeadersDsl]\n     */\n    fun headers(headersConfiguration: HeadersDsl.() -> Unit) {\n        val headersCustomizer = HeadersDsl().apply(headersConfiguration).get()\n        this.http.headers(headersCustomizer)\n    }\n\n    /**\n     * Enables CORS.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             cors {\n     *                 disable()\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param corsConfiguration custom configurations to configure the\n     * response headers\n     * @see [CorsDsl]\n     */\n    fun cors(corsConfiguration: CorsDsl.() -> Unit) {\n        val corsCustomizer = CorsDsl().apply(corsConfiguration).get()\n        this.http.cors(corsCustomizer)\n    }\n\n    /**\n     * Allows configuring session management.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             sessionManagement {\n     *                 invalidSessionUrl = \"/invalid-session\"\n     *                 sessionConcurrency {\n     *                     maximumSessions = 1\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param sessionManagementConfiguration custom configurations to configure\n     * session management\n     * @see [SessionManagementDsl]\n     */\n    fun sessionManagement(sessionManagementConfiguration: SessionManagementDsl.() -> Unit) {\n        val sessionManagementCustomizer = SessionManagementDsl().apply(sessionManagementConfiguration).get()\n        this.http.sessionManagement(sessionManagementCustomizer)\n    }\n\n    /**\n     * Allows configuring a port mapper.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             portMapper {\n     *                 map(80, 443)\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param portMapperConfiguration custom configurations to configure\n     * the port mapper\n     * @see [PortMapperDsl]\n     */\n    fun portMapper(portMapperConfiguration: PortMapperDsl.() -> Unit) {\n        val portMapperCustomizer = PortMapperDsl().apply(portMapperConfiguration).get()\n        this.http.portMapper(portMapperCustomizer)\n    }\n\n    /**\n     * Allows configuring channel security based upon the [HttpServletRequest]\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             requiresChannel {\n     *                 secure(\"/public\", requiresInsecure)\n     *                 secure(anyRequest, requiresSecure)\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param requiresChannelConfiguration custom configuration that specifies\n     * channel security\n     * @see [RequiresChannelDsl]\n     * @deprecated please use [redirectToHttps] instead\n     */\n    @Suppress(\"DEPRECATION\")\n    @Deprecated(message=\"since 6.5 use redirectToHttps instead\")\n    fun requiresChannel(requiresChannelConfiguration: RequiresChannelDsl.() -> Unit) {\n        val requiresChannelCustomizer = RequiresChannelDsl().apply(requiresChannelConfiguration).get()\n        this.http.requiresChannel(requiresChannelCustomizer)\n    }\n\n    /**\n     * Configures channel security. In order for this configuration to be useful at least\n     * one mapping to a required channel must be provided.\n     *\n     * Example:\n     *\n     * The example below demonstrates how to require HTTPS for every request. Only\n     * requiring HTTPS for some requests is supported, for example if you need to differentiate\n     * between local and production deployments.\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class RequireHttpsConfig {\n     *\n     * \t@Bean\n     * \tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     * \t\thttp {\n     * \t\t\tredirectToHttps { }\n     * \t\t}\n     * \t\treturn http.build();\n     * \t}\n     * }\n     * ```\n     * @param httpsRedirectConfiguration custom configuration to apply to HTTPS redirect rules\n     * @see [HttpsRedirectDsl]\n     * @since 6.5\n     */\n    fun redirectToHttps(httpsRedirectConfiguration: HttpsRedirectDsl.() -> Unit) {\n        val httpsRedirectCustomizer = HttpsRedirectDsl().apply(httpsRedirectConfiguration).get()\n        this.http.redirectToHttps(httpsRedirectCustomizer)\n    }\n\n    /**\n     * Adds X509 based pre authentication to an application\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             x509 { }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param x509Configuration custom configuration to apply to the\n     * X509 based pre authentication\n     * @see [X509Dsl]\n     */\n    fun x509(x509Configuration: X509Dsl.() -> Unit) {\n        val x509Customizer = X509Dsl().apply(x509Configuration).get()\n        this.http.x509(x509Customizer)\n    }\n\n    /**\n     * Enables request caching. Specifically this ensures that requests that\n     * are saved (i.e. after authentication is required) are later replayed.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             requestCache { }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param requestCacheConfiguration custom configuration to apply to the\n     * request cache\n     * @see [RequestCacheDsl]\n     */\n    fun requestCache(requestCacheConfiguration: RequestCacheDsl.() -> Unit) {\n        val requestCacheCustomizer = RequestCacheDsl().apply(requestCacheConfiguration).get()\n        this.http.requestCache(requestCacheCustomizer)\n    }\n\n    /**\n     * Allows configuring exception handling.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             exceptionHandling {\n     *                 accessDeniedPage = \"/access-denied\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param exceptionHandlingConfiguration custom configuration to apply to the\n     * exception handling\n     * @see [ExceptionHandlingDsl]\n     */\n    fun exceptionHandling(exceptionHandlingConfiguration: ExceptionHandlingDsl.() -> Unit) {\n        val exceptionHandlingCustomizer = ExceptionHandlingDsl().apply(exceptionHandlingConfiguration).get()\n        this.http.exceptionHandling(exceptionHandlingCustomizer)\n    }\n\n    /**\n     * Enables CSRF protection.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             csrf { }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param csrfConfiguration custom configuration to apply to CSRF\n     * @see [CsrfDsl]\n     */\n    fun csrf(csrfConfiguration: CsrfDsl.() -> Unit) {\n        val csrfCustomizer = CsrfDsl().apply(csrfConfiguration).get()\n        this.http.csrf(csrfCustomizer)\n    }\n\n    /**\n     * Provides logout support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             logout {\n     *                 logoutUrl = \"/log-out\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param logoutConfiguration custom configuration to apply to logout\n     * @see [LogoutDsl]\n     */\n    fun logout(logoutConfiguration: LogoutDsl.() -> Unit) {\n        val logoutCustomizer = LogoutDsl().apply(logoutConfiguration).get()\n        this.http.logout(logoutCustomizer)\n    }\n\n    /**\n     * Configures authentication support using a SAML 2.0 Service Provider.\n     * A [RelyingPartyRegistrationRepository] is required and must be registered with\n     * the [ApplicationContext] or configured via\n     * [Saml2Dsl.relyingPartyRegistrationRepository]\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             saml2Login {\n     *                 relyingPartyRegistration = getSaml2RelyingPartyRegistration()\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param saml2LoginConfiguration custom configuration to configure the\n     * SAML2 service provider\n     * @see [Saml2Dsl]\n     */\n    fun saml2Login(saml2LoginConfiguration: Saml2Dsl.() -> Unit) {\n        val saml2LoginCustomizer = Saml2Dsl().apply(saml2LoginConfiguration).get()\n        this.http.saml2Login(saml2LoginCustomizer)\n    }\n\n    /**\n     * Configures logout support for a SAML 2.0 Service Provider. <br>\n     * <br>\n     *\n     * Implements the <b>Single Logout Profile, using POST and REDIRECT bindings</b>, as\n     * documented in the\n     * <a target=\"_blank\" href=\"https://docs.oasis-open.org/security/saml/\">SAML V2.0\n     * Core, Profiles and Bindings</a> specifications. <br>\n     * <br>\n     *\n     * As a prerequisite to using this feature, is that you have a SAML v2.0 Asserting\n     * Party to send a logout request to. The representation of the relying party and the\n     * asserting party is contained within [RelyingPartyRegistration]. <br>\n     * <br>\n     *\n     * [RelyingPartyRegistration] (s) are composed within a\n     * [RelyingPartyRegistrationRepository], which is <b>required</b> and must be\n     * registered with the [ApplicationContext] or configured via\n     * [HttpSecurityDsl.saml2Login].<br>\n     * <br>\n     *\n     * The default configuration provides an auto-generated logout endpoint at\n     * `/logout` and redirects to `/login?logout` when\n     * logout completes. <br>\n     * <br>\n     *\n     * <p>\n     * <h2>Example Configuration</h2>\n     *\n     * The following example shows the minimal configuration required, using a\n     * hypothetical asserting party.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             saml2Login {\n     *                 relyingPartyRegistration = getSaml2RelyingPartyRegistration()\n     *             }\n     *             saml2Logout { }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * <p>\n     * @param saml2LogoutConfiguration custom configuration to configure the\n     * SAML 2.0 service provider\n     * @since 6.3\n     * @see [Saml2LogoutDsl]\n     */\n    fun saml2Logout(saml2LogoutConfiguration: Saml2LogoutDsl.() -> Unit) {\n        val saml2LogoutCustomizer = Saml2LogoutDsl().apply(saml2LogoutConfiguration).get()\n        this.http.saml2Logout(saml2LogoutCustomizer)\n    }\n\n    /**\n     * Configures a SAML 2.0 relying party metadata endpoint.\n     *\n     * A [RelyingPartyRegistrationRepository] is required and must be registered with\n     * the [ApplicationContext] or configured via\n     * [Saml2Dsl.relyingPartyRegistrationRepository]\n     *\n     * Example:\n     *\n     * The following example shows the minimal configuration required, using a\n     * hypothetical asserting party.\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             saml2Login { }\n     *             saml2Metadata { }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     * @param saml2MetadataConfiguration custom configuration to configure the\n     * SAML2 relying party metadata endpoint\n     * @see [Saml2MetadataDsl]\n     * @since 6.1\n     */\n    fun saml2Metadata(saml2MetadataConfiguration: Saml2MetadataDsl.() -> Unit) {\n        val saml2MetadataCustomizer = Saml2MetadataDsl().apply(saml2MetadataConfiguration).get()\n        this.http.saml2Metadata(saml2MetadataCustomizer)\n    }\n\n    /**\n     * Allows configuring how an anonymous user is represented.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             anonymous {\n     *                 authorities = listOf(SimpleGrantedAuthority(\"ROLE_ANON\"))\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param anonymousConfiguration custom configuration to configure the\n     * anonymous user\n     * @see [AnonymousDsl]\n     */\n    fun anonymous(anonymousConfiguration: AnonymousDsl.() -> Unit) {\n        val anonymousCustomizer = AnonymousDsl().apply(anonymousConfiguration).get()\n        this.http.anonymous(anonymousCustomizer)\n    }\n\n    /**\n     * Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n     * A [ClientRegistrationRepository] is required and must be registered as a Bean or\n     * configured via [OAuth2LoginDsl.clientRegistrationRepository]\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Login {\n     *                 clientRegistrationRepository = getClientRegistrationRepository()\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param oauth2LoginConfiguration custom configuration to configure the\n     * OAuth 2.0 Login\n     * @see [OAuth2LoginDsl]\n     */\n    fun oauth2Login(oauth2LoginConfiguration: OAuth2LoginDsl.() -> Unit) {\n        val oauth2LoginCustomizer = OAuth2LoginDsl().apply(oauth2LoginConfiguration).get()\n        this.http.oauth2Login(oauth2LoginCustomizer)\n    }\n\n    /**\n     * Configures OAuth 2.0 client support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Client { }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param oauth2ClientConfiguration custom configuration to configure the\n     * OAuth 2.0 client support\n     * @see [OAuth2ClientDsl]\n     */\n    fun oauth2Client(oauth2ClientConfiguration: OAuth2ClientDsl.() -> Unit) {\n        val oauth2ClientCustomizer = OAuth2ClientDsl().apply(oauth2ClientConfiguration).get()\n        this.http.oauth2Client(oauth2ClientCustomizer)\n    }\n\n    /**\n     * Configures OAuth 2.0 resource server support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2ResourceServer {\n     *                 jwt { }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param oauth2ResourceServerConfiguration custom configuration to configure the\n     * OAuth 2.0 resource server support\n     * @see [OAuth2ResourceServerDsl]\n     */\n    fun oauth2ResourceServer(oauth2ResourceServerConfiguration: OAuth2ResourceServerDsl.() -> Unit) {\n        val oauth2ResourceServerCustomizer = OAuth2ResourceServerDsl().apply(oauth2ResourceServerConfiguration).get()\n        this.http.oauth2ResourceServer(oauth2ResourceServerCustomizer)\n    }\n\n    /**\n     * Configures OIDC 1.0 logout support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Login { }\n     *             oidcLogout {\n     *                 backChannel { }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param oidcLogoutConfiguration custom configuration to configure the\n     * OIDC 1.0 logout support\n     * @see [OidcLogoutDsl]\n     */\n    fun oidcLogout(oidcLogoutConfiguration: OidcLogoutDsl.() -> Unit) {\n        val oidcLogoutCustomizer = OidcLogoutDsl().apply(oidcLogoutConfiguration).get()\n        this.http.oidcLogout(oidcLogoutCustomizer)\n    }\n\n    /**\n     * Configures One-Time Token Login Support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *    @Bean\n     *    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *        http {\n     *               oneTimeTokenLogin {\n     *                     oneTimeTokenGenerationSuccessHandler = MyMagicLinkOneTimeTokenGenerationSuccessHandler()\n     *                }\n     *             }\n     *        return http.build()\n     *       }\n     * }\n     *\n     * ```\n     * @since 6.4\n     * @param oneTimeTokenLoginConfiguration custom configuration to configure one-time token login\n     */\n    fun oneTimeTokenLogin(oneTimeTokenLoginConfiguration: OneTimeTokenLoginDsl.() -> Unit) {\n        val oneTimeTokenLoginCustomizer = OneTimeTokenLoginDsl().apply(oneTimeTokenLoginConfiguration).get()\n        this.http.oneTimeTokenLogin(oneTimeTokenLoginCustomizer)\n    }\n\n    /**\n     * Configures Remember Me authentication.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             rememberMe {\n     *                 tokenValiditySeconds = 604800\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param rememberMeConfiguration custom configuration to configure remember me\n     * @see [RememberMeDsl]\n     */\n    fun rememberMe(rememberMeConfiguration: RememberMeDsl.() -> Unit) {\n        val rememberMeCustomizer = RememberMeDsl().apply(rememberMeConfiguration).get()\n        this.http.rememberMe(rememberMeCustomizer)\n    }\n\n    /**\n     * Enable WebAuthn configuration.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             webAuthn {\n     *                 loginPage = \"/log-in\"\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param webAuthnConfiguration custom configurations to be applied\n     * to the WebAuthn authentication\n     * @see [WebAuthnDsl]\n     */\n    fun webAuthn(webAuthnConfiguration: WebAuthnDsl.() -> Unit) {\n        val webAuthnCustomizer = WebAuthnDsl().apply(webAuthnConfiguration).get()\n        this.http.webAuthn(webAuthnCustomizer)\n    }\n\n    /**\n     * Adds the [Filter] at the location of the specified [Filter] class.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             addFilterAt(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java)\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param filter the [Filter] to register\n     * @param atFilter the location of another [Filter] that is already registered\n     * (i.e. known) with Spring Security.\n     */\n    @Deprecated(\"Use 'addFilterAt<T>(filter)' instead.\")\n    fun addFilterAt(filter: Filter, atFilter: Class<out Filter>) {\n        this.http.addFilterAt(filter, atFilter)\n    }\n\n    /**\n     * Adds the [Filter] at the location of the specified [Filter] class.\n     * Variant that is leveraging Kotlin reified type parameters.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             addFilterAt<UsernamePasswordAuthenticationFilter>(CustomFilter())\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param filter the [Filter] to register\n     * @param T the location of another [Filter] that is already registered\n     * (i.e. known) with Spring Security.\n     */\n    @Suppress(\"DEPRECATION\")\n    inline fun <reified T : Filter> addFilterAt(filter: Filter) {\n        this.addFilterAt(filter, T::class.java)\n    }\n\n    /**\n     * Adds the [Filter] after the location of the specified [Filter] class.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             addFilterAfter(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java)\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param filter the [Filter] to register\n     * @param afterFilter the location of another [Filter] that is already registered\n     * (i.e. known) with Spring Security.\n     */\n    @Deprecated(\"Use 'addFilterAfter<T>(filter)' instead.\")\n    fun addFilterAfter(filter: Filter, afterFilter: Class<out Filter>) {\n        this.http.addFilterAfter(filter, afterFilter)\n    }\n\n    /**\n     * Adds the [Filter] after the location of the specified [Filter] class.\n     * Variant that is leveraging Kotlin reified type parameters.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             addFilterAfter<UsernamePasswordAuthenticationFilter>(CustomFilter())\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param filter the [Filter] to register\n     * @param T the location of another [Filter] that is already registered\n     * (i.e. known) with Spring Security.\n     */\n    @Suppress(\"DEPRECATION\")\n    inline fun <reified T : Filter> addFilterAfter(filter: Filter) {\n        this.addFilterAfter(filter, T::class.java)\n    }\n\n    /**\n     * Adds the [Filter] before the location of the specified [Filter] class.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             addFilterBefore(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java)\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param filter the [Filter] to register\n     * @param beforeFilter the location of another [Filter] that is already registered\n     * (i.e. known) with Spring Security.\n     */\n    @Deprecated(\"Use 'addFilterBefore<T>(filter)' instead.\")\n    fun addFilterBefore(filter: Filter, beforeFilter: Class<out Filter>) {\n        this.http.addFilterBefore(filter, beforeFilter)\n    }\n\n    /**\n     * Adds the [Filter] before the location of the specified [Filter] class.\n     * Variant that is leveraging Kotlin reified type parameters.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             addFilterBefore<UsernamePasswordAuthenticationFilter>(CustomFilter())\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param filter the [Filter] to register\n     * @param T the location of another [Filter] that is already registered\n     * (i.e. known) with Spring Security.\n     */\n    @Suppress(\"DEPRECATION\")\n    inline fun <reified T : Filter> addFilterBefore(filter: Filter) {\n        this.addFilterBefore(filter, T::class.java)\n    }\n\n    /**\n     * Apply all configurations to the provided [HttpSecurity]\n     */\n    internal fun build() {\n        init()\n        authenticationManager?.also { this.http.authenticationManager(authenticationManager) }\n    }\n\n    /**\n     * Enables security context configuration.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *        http {\n     *           securityContext {\n     *               securityContextRepository = SECURITY_CONTEXT_REPOSITORY\n     *           }\n     *        }\n     *        return http.build()\n     *     }\n     * }\n     * ```\n     * @author Norbert Nowak\n     * @since 5.7\n     * @param securityContextConfiguration configuration to be applied to Security Context\n     * @see [SecurityContextDsl]\n     */\n    fun securityContext(securityContextConfiguration: SecurityContextDsl.() -> Unit) {\n        val securityContextCustomizer = SecurityContextDsl().apply(securityContextConfiguration).get()\n        this.http.securityContext(securityContextCustomizer)\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/HttpsRedirectDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer\nimport org.springframework.security.web.PortMapper\nimport org.springframework.security.web.util.matcher.RequestMatcher\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] HTTPS redirection rules using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property portMapper the [PortMapper] that specifies a custom HTTPS port to redirect to.\n */\n@SecurityMarker\nclass HttpsRedirectDsl {\n    var requestMatchers: Array<out RequestMatcher>? = null\n\n    internal fun get(): (HttpsRedirectConfigurer<HttpSecurity>) -> Unit {\n        return { https ->\n            requestMatchers?.also { https.requestMatchers(*requestMatchers!!) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/LogoutDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.LogoutConfigurer\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.web.access.AccessDeniedHandler\nimport org.springframework.security.web.authentication.logout.LogoutHandler\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler\nimport org.springframework.security.web.util.matcher.RequestMatcher\nimport java.util.*\nimport jakarta.servlet.http.HttpSession\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] logout support\n * using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property clearAuthentication whether the [SecurityContextLogoutHandler] should clear\n * the [Authentication] at the time of logout.\n * @property invalidateHttpSession whether to invalidate the [HttpSession] at the time of logout.\n * @property logoutUrl the URL that triggers log out to occur.\n * @property logoutRequestMatcher the [RequestMatcher] that triggers log out to occur.\n * @property logoutSuccessUrl the URL to redirect to after logout has occurred.\n * @property logoutSuccessHandler the [LogoutSuccessHandler] to use after logout has occurred.\n * If this is specified, [logoutSuccessUrl] is ignored.\n */\n@SecurityMarker\nclass LogoutDsl {\n    var clearAuthentication: Boolean? = null\n    var invalidateHttpSession: Boolean? = null\n    var logoutUrl: String? = null\n    var logoutRequestMatcher: RequestMatcher? = null\n    var logoutSuccessUrl: String? = null\n    var logoutSuccessHandler: LogoutSuccessHandler? = null\n    var permitAll: Boolean? = null\n\n    private var logoutHandlers = mutableListOf<LogoutHandler>()\n    private var deleteCookies: Array<out String>? = null\n    private var defaultLogoutSuccessHandlerMappings: LinkedHashMap<RequestMatcher, LogoutSuccessHandler> = linkedMapOf()\n    private var disabled = false\n\n\n    /**\n     * Adds a [LogoutHandler]. The [SecurityContextLogoutHandler] is added as\n     * the last [LogoutHandler] by default.\n     *\n     * @param logoutHandler the [LogoutHandler] to add\n     */\n    fun addLogoutHandler(logoutHandler: LogoutHandler) {\n        this.logoutHandlers.add(logoutHandler)\n    }\n\n    /**\n     * Allows specifying the names of cookies to be removed on logout success.\n     *\n     * @param cookieNamesToClear the names of cookies to be removed on logout success.\n     */\n    fun deleteCookies(vararg cookieNamesToClear: String) {\n        this.deleteCookies = cookieNamesToClear\n    }\n\n    /**\n     * Sets a default [LogoutSuccessHandler] to be used which prefers being\n     * invoked for the provided [RequestMatcher].\n     *\n     * @param logoutHandler the [LogoutSuccessHandler] to use\n     * @param preferredMatcher the [RequestMatcher] for this default\n     * [AccessDeniedHandler]\n     */\n    fun defaultLogoutSuccessHandlerFor(logoutHandler: LogoutSuccessHandler, preferredMatcher: RequestMatcher) {\n        defaultLogoutSuccessHandlerMappings[preferredMatcher] = logoutHandler\n    }\n\n    /**\n     * Disables logout\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    /**\n     * Grants access to the [logoutSuccessUrl] and the [logoutUrl] for every user.\n     */\n    fun permitAll() {\n        permitAll = true\n    }\n\n    internal fun get(): (LogoutConfigurer<HttpSecurity>) -> Unit {\n        return { logout ->\n            clearAuthentication?.also { logout.clearAuthentication(clearAuthentication!!) }\n            invalidateHttpSession?.also { logout.invalidateHttpSession(invalidateHttpSession!!) }\n            logoutUrl?.also { logout.logoutUrl(logoutUrl) }\n            logoutRequestMatcher?.also { logout.logoutRequestMatcher(logoutRequestMatcher) }\n            logoutSuccessUrl?.also { logout.logoutSuccessUrl(logoutSuccessUrl) }\n            logoutSuccessHandler?.also { logout.logoutSuccessHandler(logoutSuccessHandler) }\n            deleteCookies?.also { logout.deleteCookies(*deleteCookies!!) }\n            permitAll?.also { logout.permitAll(permitAll!!) }\n            defaultLogoutSuccessHandlerMappings.forEach { (matcher, handler) ->\n                logout.defaultLogoutSuccessHandlerFor(handler, matcher)\n            }\n            logoutHandlers.forEach { logoutHandler ->\n                logout.addLogoutHandler(logoutHandler)\n            }\n            if (disabled) {\n                logout.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/OAuth2ClientDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.oauth2.client.AuthorizationCodeGrantDsl\nimport org.springframework.security.config.annotation.web.oauth2.login.AuthorizationEndpointDsl\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 client support using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property clientRegistrationRepository the repository of client registrations.\n * @property authorizedClientRepository the repository for authorized client(s).\n * @property authorizedClientService the service for authorized client(s).\n */\n@SecurityMarker\nclass OAuth2ClientDsl {\n    var clientRegistrationRepository: ClientRegistrationRepository? = null\n    var authorizedClientRepository: OAuth2AuthorizedClientRepository? = null\n    var authorizedClientService: OAuth2AuthorizedClientService? = null\n\n    private var authorizationCodeGrant: ((OAuth2ClientConfigurer<HttpSecurity>.AuthorizationCodeGrantConfigurer) -> Unit)? = null\n\n    /**\n     * Configures the OAuth 2.0 Authorization Code Grant.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Client {\n     *                 authorizationCodeGrant {\n     *                     authorizationRequestResolver = getAuthorizationRequestResolver()\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param authorizationCodeGrantConfig custom configurations to configure the authorization\n     * code grant\n     * @see [AuthorizationEndpointDsl]\n     */\n    fun authorizationCodeGrant(authorizationCodeGrantConfig: AuthorizationCodeGrantDsl.() -> Unit) {\n        this.authorizationCodeGrant = AuthorizationCodeGrantDsl().apply(authorizationCodeGrantConfig).get()\n    }\n\n    internal fun get(): (OAuth2ClientConfigurer<HttpSecurity>) -> Unit {\n        return { oauth2Client ->\n            clientRegistrationRepository?.also { oauth2Client.clientRegistrationRepository(clientRegistrationRepository) }\n            authorizedClientRepository?.also { oauth2Client.authorizedClientRepository(authorizedClientRepository) }\n            authorizedClientService?.also { oauth2Client.authorizedClientService(authorizedClientService) }\n            authorizationCodeGrant?.also { oauth2Client.authorizationCodeGrant(authorizationCodeGrant) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/OAuth2LoginDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport jakarta.servlet.http.HttpServletRequest\nimport org.springframework.security.authentication.AuthenticationDetailsSource\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer\nimport org.springframework.security.config.annotation.web.oauth2.login.AuthorizationEndpointDsl\nimport org.springframework.security.config.annotation.web.oauth2.login.RedirectionEndpointDsl\nimport org.springframework.security.config.annotation.web.oauth2.login.TokenEndpointDsl\nimport org.springframework.security.config.annotation.web.oauth2.login.UserInfoEndpointDsl\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 login using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property clientRegistrationRepository the repository of client registrations.\n * @property authorizedClientRepository the repository for authorized client(s).\n * @property authorizedClientService the service for authorized client(s).\n * @property loginPage the login page to redirect to if authentication is required (i.e.\n * \"/login\")\n * @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after\n * authentication success\n * @property authenticationFailureHandler the [AuthenticationFailureHandler] used after\n * authentication success\n * @property failureUrl the URL to send users if authentication fails\n * @property loginProcessingUrl the URL to validate the credentials\n * @property permitAll whether to grant access to the urls for [failureUrl] as well as\n * for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user\n */\n@SecurityMarker\nclass OAuth2LoginDsl {\n    var clientRegistrationRepository: ClientRegistrationRepository? = null\n    var authorizedClientRepository: OAuth2AuthorizedClientRepository? = null\n    var authorizedClientService: OAuth2AuthorizedClientService? = null\n    var loginPage: String? = null\n    var authenticationSuccessHandler: AuthenticationSuccessHandler? = null\n    var authenticationFailureHandler: AuthenticationFailureHandler? = null\n    var failureUrl: String? = null\n    var loginProcessingUrl: String? = null\n    var permitAll: Boolean? = null\n    var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>? = null\n    var oidcSessionRegistry: OidcSessionRegistry? = null\n\n    private var defaultSuccessUrlOption: Pair<String, Boolean>? = null\n    private var authorizationEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.AuthorizationEndpointConfig) -> Unit)? = null\n    private var tokenEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.TokenEndpointConfig) -> Unit)? = null\n    private var redirectionEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.RedirectionEndpointConfig) -> Unit)? = null\n    private var userInfoEndpoint: ((OAuth2LoginConfigurer<HttpSecurity>.UserInfoEndpointConfig) -> Unit)? = null\n\n    /**\n     * Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the\n     * [loginPage] and [loginProcessingUrl] for every user.\n     */\n    fun permitAll() {\n        permitAll = true\n    }\n\n    /**\n     * Specifies where users will be redirected after authenticating successfully if\n     * they have not visited a secured page prior to authenticating or [alwaysUse]\n     * is true.\n     *\n     * @param defaultSuccessUrl the default success url\n     * @param alwaysUse true if the [defaultSuccessUrl] should be used after\n     * authentication despite if a protected page had been previously visited\n     */\n    fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) {\n        defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse)\n    }\n\n    /**\n     * Configures the Authorization Server's Authorization Endpoint.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Login {\n     *                 authorizationEndpoint {\n     *                     baseUri = \"/auth\"\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param authorizationEndpointConfig custom configurations to configure the authorization\n     * endpoint\n     * @see [AuthorizationEndpointDsl]\n     */\n    fun authorizationEndpoint(authorizationEndpointConfig: AuthorizationEndpointDsl.() -> Unit) {\n        this.authorizationEndpoint = AuthorizationEndpointDsl().apply(authorizationEndpointConfig).get()\n    }\n\n    /**\n     * Configures the Authorization Server's Token Endpoint.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Login {\n     *                 tokenEndpoint {\n     *                     accessTokenResponseClient = getAccessTokenResponseClient()\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param tokenEndpointConfig custom configurations to configure the token\n     * endpoint\n     * @see [TokenEndpointDsl]\n     */\n    fun tokenEndpoint(tokenEndpointConfig: TokenEndpointDsl.() -> Unit) {\n        this.tokenEndpoint = TokenEndpointDsl().apply(tokenEndpointConfig).get()\n    }\n\n    /**\n     * Configures the Authorization Server's Redirection Endpoint.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Login {\n     *                 redirectionEndpoint {\n     *                     baseUri = \"/home\"\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param redirectionEndpointConfig custom configurations to configure the redirection\n     * endpoint\n     * @see [RedirectionEndpointDsl]\n     */\n    fun redirectionEndpoint(redirectionEndpointConfig: RedirectionEndpointDsl.() -> Unit) {\n        this.redirectionEndpoint = RedirectionEndpointDsl().apply(redirectionEndpointConfig).get()\n    }\n\n    /**\n     * Configures the Authorization Server's UserInfo Endpoint.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Login {\n     *                 userInfoEndpoint {\n     *                     userService = getUserService()\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param userInfoEndpointConfig custom configurations to configure the user info\n     * endpoint\n     * @see [UserInfoEndpointDsl]\n     */\n    fun userInfoEndpoint(userInfoEndpointConfig: UserInfoEndpointDsl.() -> Unit) {\n        this.userInfoEndpoint = UserInfoEndpointDsl().apply(userInfoEndpointConfig).get()\n    }\n\n    internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>) -> Unit {\n        return { oauth2Login ->\n            clientRegistrationRepository?.also { oauth2Login.clientRegistrationRepository(clientRegistrationRepository) }\n            authorizedClientRepository?.also { oauth2Login.authorizedClientRepository(authorizedClientRepository) }\n            authorizedClientService?.also { oauth2Login.authorizedClientService(authorizedClientService) }\n            loginPage?.also { oauth2Login.loginPage(loginPage) }\n            failureUrl?.also { oauth2Login.failureUrl(failureUrl) }\n            loginProcessingUrl?.also { oauth2Login.loginProcessingUrl(loginProcessingUrl) }\n            permitAll?.also { oauth2Login.permitAll(permitAll!!) }\n            defaultSuccessUrlOption?.also {\n                oauth2Login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second)\n            }\n            authenticationSuccessHandler?.also { oauth2Login.successHandler(authenticationSuccessHandler) }\n            authenticationFailureHandler?.also { oauth2Login.failureHandler(authenticationFailureHandler) }\n            authorizationEndpoint?.also { oauth2Login.authorizationEndpoint(authorizationEndpoint) }\n            tokenEndpoint?.also { oauth2Login.tokenEndpoint(tokenEndpoint) }\n            redirectionEndpoint?.also { oauth2Login.redirectionEndpoint(redirectionEndpoint) }\n            userInfoEndpoint?.also { oauth2Login.userInfoEndpoint(userInfoEndpoint) }\n            authenticationDetailsSource?.also { oauth2Login.authenticationDetailsSource(authenticationDetailsSource) }\n            oidcSessionRegistry?.also { oauth2Login.oidcSessionRegistry(oidcSessionRegistry) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/OAuth2ResourceServerDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.authentication.AuthenticationManagerResolver\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.oauth2.resourceserver.JwtDsl\nimport org.springframework.security.config.annotation.web.oauth2.resourceserver.OpaqueTokenDsl\nimport org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver\nimport org.springframework.security.web.AuthenticationEntryPoint\nimport org.springframework.security.web.access.AccessDeniedHandler\nimport jakarta.servlet.http.HttpServletRequest\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 resource server support using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property accessDeniedHandler the [AccessDeniedHandler] to use for requests authenticating\n * with <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer Token</a>s.\n * @property authenticationEntryPoint the [AuthenticationEntryPoint] to use for requests authenticating\n * with <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer Token</a>s.\n * @property bearerTokenResolver the [BearerTokenResolver] to use for requests authenticating\n * with <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer Token</a>s.\n */\n@SecurityMarker\nclass OAuth2ResourceServerDsl {\n    var accessDeniedHandler: AccessDeniedHandler? = null\n    var authenticationEntryPoint: AuthenticationEntryPoint? = null\n    var bearerTokenResolver: BearerTokenResolver? = null\n    var authenticationManagerResolver: AuthenticationManagerResolver<HttpServletRequest>? = null\n\n    private var jwt: ((OAuth2ResourceServerConfigurer<HttpSecurity>.JwtConfigurer) -> Unit)? = null\n    private var opaqueToken: ((OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer) -> Unit)? = null\n\n    /**\n     * Enables JWT-encoded bearer token support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2ResourceServer {\n     *                 jwt {\n     *                     jwkSetUri = \"https://example.com/oauth2/jwk\"\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param jwtConfig custom configurations to configure JWT resource server support\n     * @see [JwtDsl]\n     */\n    fun jwt(jwtConfig: JwtDsl.() -> Unit) {\n        this.jwt = JwtDsl().apply(jwtConfig).get()\n    }\n\n    /**\n     * Enables opaque token support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2ResourceServer {\n     *                 opaqueToken { }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param opaqueTokenConfig custom configurations to configure opaque token resource server support\n     * @see [OpaqueTokenDsl]\n     */\n    fun opaqueToken(opaqueTokenConfig: OpaqueTokenDsl.() -> Unit) {\n        this.opaqueToken = OpaqueTokenDsl().apply(opaqueTokenConfig).get()\n    }\n\n    internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>) -> Unit {\n        return { oauth2ResourceServer ->\n            accessDeniedHandler?.also { oauth2ResourceServer.accessDeniedHandler(accessDeniedHandler) }\n            authenticationEntryPoint?.also { oauth2ResourceServer.authenticationEntryPoint(authenticationEntryPoint) }\n            bearerTokenResolver?.also { oauth2ResourceServer.bearerTokenResolver(bearerTokenResolver) }\n            authenticationManagerResolver?.also { oauth2ResourceServer.authenticationManagerResolver(authenticationManagerResolver) }\n            jwt?.also { oauth2ResourceServer.jwt(jwt) }\n            opaqueToken?.also { oauth2ResourceServer.opaqueToken(opaqueToken) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDsl.kt",
    "content": "\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer\nimport org.springframework.security.config.annotation.web.oauth2.login.OidcBackChannelLogoutDsl\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] OAuth 1.0 Logout using idiomatic Kotlin code.\n *\n * @author Josh Cummings\n * @since 6.2\n */\n@SecurityMarker\nclass OidcLogoutDsl {\n    var clientRegistrationRepository: ClientRegistrationRepository? = null\n    var oidcSessionRegistry: OidcSessionRegistry? = null\n\n    private var backChannel: ((OidcLogoutConfigurer<HttpSecurity>.BackChannelLogoutConfigurer) -> Unit)? = null\n\n    /**\n     * Configures the OIDC 1.0 Back-Channel endpoint.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             oauth2Login { }\n     *             oidcLogout {\n     *                 backChannel { }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param backChannelConfig custom configurations to configure the back-channel endpoint\n     * @see [OidcBackChannelLogoutDsl]\n     */\n    fun backChannel(backChannelConfig: OidcBackChannelLogoutDsl.() -> Unit) {\n        this.backChannel = OidcBackChannelLogoutDsl().apply(backChannelConfig).get()\n    }\n\n    internal fun get(): (OidcLogoutConfigurer<HttpSecurity>) -> Unit {\n        return { oidcLogout ->\n            clientRegistrationRepository?.also { oidcLogout.clientRegistrationRepository(clientRegistrationRepository) }\n            oidcSessionRegistry?.also { oidcLogout.oidcSessionRegistry(oidcSessionRegistry) }\n            backChannel?.also { oidcLogout.backChannel(backChannel) }\n        }\n    }\n\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/OneTimeTokenLoginDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.authentication.AuthenticationProvider\nimport org.springframework.security.authentication.ott.OneTimeTokenService\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.ott.OneTimeTokenLoginConfigurer\nimport org.springframework.security.web.authentication.AuthenticationConverter\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler\nimport org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] OAuth 2.0 login using idiomatic Kotlin code.\n *\n * @author Max Batischev\n * @since 6.4\n * @property tokenService configures the [OneTimeTokenService] used to generate and consume\n * @property authenticationConverter Use this [AuthenticationConverter] when converting incoming requests to an authentication\n * @property authenticationFailureHandler the [AuthenticationFailureHandler] to use when authentication\n * @property authenticationSuccessHandler the [AuthenticationSuccessHandler] to be used\n * @property generateRequestResolver the [GenerateOneTimeTokenRequestResolver] to be used\n * @property defaultSubmitPageUrl sets the URL that the default submit page will be generated\n * @property showDefaultSubmitPage configures whether the default one-time token submit page should be shown\n * @property loginProcessingUrl the URL to process the login request\n * @property tokenGeneratingUrl the URL that a One-Time Token generate request will be processed\n * @property oneTimeTokenGenerationSuccessHandler the strategy to be used to handle generated one-time tokens\n * @property authenticationProvider the [AuthenticationProvider] to use when authenticating the user\n */\n@SecurityMarker\nclass OneTimeTokenLoginDsl {\n    var tokenService: OneTimeTokenService? = null\n    var authenticationConverter: AuthenticationConverter? = null\n    var authenticationFailureHandler: AuthenticationFailureHandler? = null\n    var authenticationSuccessHandler: AuthenticationSuccessHandler? = null\n    var generateRequestResolver: GenerateOneTimeTokenRequestResolver? = null\n    var defaultSubmitPageUrl: String? = null\n    var loginProcessingUrl: String? = null\n    var tokenGeneratingUrl: String? = null\n    var showDefaultSubmitPage: Boolean? = true\n    var oneTimeTokenGenerationSuccessHandler: OneTimeTokenGenerationSuccessHandler? = null\n    var authenticationProvider: AuthenticationProvider? = null\n\n    internal fun get(): (OneTimeTokenLoginConfigurer<HttpSecurity>) -> Unit {\n        return { oneTimeTokenLoginConfigurer ->\n            tokenService?.also { oneTimeTokenLoginConfigurer.tokenService(tokenService) }\n            authenticationConverter?.also { oneTimeTokenLoginConfigurer.authenticationConverter(authenticationConverter) }\n            authenticationFailureHandler?.also {\n                oneTimeTokenLoginConfigurer.failureHandler(\n                    authenticationFailureHandler\n                )\n            }\n            authenticationSuccessHandler?.also {\n                oneTimeTokenLoginConfigurer.successHandler(\n                    authenticationSuccessHandler\n                )\n            }\n            generateRequestResolver?.also {\n                oneTimeTokenLoginConfigurer.generateRequestResolver(\n                        generateRequestResolver\n                )\n            }\n            defaultSubmitPageUrl?.also { oneTimeTokenLoginConfigurer.defaultSubmitPageUrl(defaultSubmitPageUrl) }\n            showDefaultSubmitPage?.also { oneTimeTokenLoginConfigurer.showDefaultSubmitPage(showDefaultSubmitPage!!) }\n            loginProcessingUrl?.also { oneTimeTokenLoginConfigurer.loginProcessingUrl(loginProcessingUrl) }\n            tokenGeneratingUrl?.also { oneTimeTokenLoginConfigurer.tokenGeneratingUrl(tokenGeneratingUrl) }\n            oneTimeTokenGenerationSuccessHandler?.also {\n                oneTimeTokenLoginConfigurer.tokenGenerationSuccessHandler(\n                    oneTimeTokenGenerationSuccessHandler\n                )\n            }\n            authenticationProvider?.also { oneTimeTokenLoginConfigurer.authenticationProvider(authenticationProvider) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/PasswordManagementDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.PasswordManagementConfigurer\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] password management\n * using idiomatic Kotlin code.\n *\n * @author Evgeniy Cheban\n * @property changePasswordPage the change password page.\n * @since 5.6\n */\n@SecurityMarker\nclass PasswordManagementDsl {\n    var changePasswordPage: String? = null\n\n    internal fun get(): (PasswordManagementConfigurer<HttpSecurity>) -> Unit {\n        return { passwordManagement ->\n            changePasswordPage?.also { passwordManagement.changePasswordPage(changePasswordPage) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/PortMapperDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.PortMapperConfigurer\nimport org.springframework.security.web.PortMapper\n\n/**\n * A Kotlin DSL to configure a [PortMapper] for [HttpSecurity] using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property portMapper allows specifying the [PortMapper] instance.\n */\n@SecurityMarker\nclass PortMapperDsl {\n    private val mappings = mutableListOf<Pair<Int, Int>>()\n\n    var portMapper: PortMapper? = null\n\n    /**\n     * Adds a mapping to the port mapper.\n     *\n     * @param fromPort the HTTP port number to map from\n     * @param toPort the HTTPS port number to map to\n     */\n    fun map(fromPort: Int, toPort: Int) {\n        mappings.add(Pair(fromPort, toPort))\n    }\n\n    internal fun get(): (PortMapperConfigurer<HttpSecurity>) -> Unit {\n        return { portMapperConfig ->\n            portMapper?.also {\n                portMapperConfig.portMapper(portMapper)\n            }\n            this.mappings.forEach {\n                portMapperConfig.http(it.first).mapsTo(it.second)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/RememberMeDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler\nimport org.springframework.security.web.authentication.RememberMeServices\nimport org.springframework.security.web.authentication.rememberme.PersistentTokenRepository\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] Remember me using idiomatic Kotlin code.\n *\n * @author Ivan Pavlov\n * @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after\n * authentication success\n * @property key the key to identify tokens\n * @property rememberMeServices the [RememberMeServices] to use\n * @property rememberMeParameter the HTTP parameter used to indicate to remember\n * the user at time of login. Defaults to 'remember-me'\n * @property rememberMeCookieName the name of cookie which store the token for\n * remember me authentication. Defaults to 'remember-me'\n * @property rememberMeCookieDomain the domain name within which the remember me cookie\n * is visible\n * @property tokenRepository the [PersistentTokenRepository] to use. Defaults to\n * [org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices] instead\n * @property userDetailsService the [UserDetailsService] used to look up the UserDetails\n * when a remember me token is valid\n * @property tokenValiditySeconds how long (in seconds) a token is valid for.\n * Defaults to 2 weeks\n * @property useSecureCookie whether the cookie should be flagged as secure or not\n * @property alwaysRemember whether the cookie should always be created even if\n * the remember-me parameter is not set. Defaults to `false`\n */\n@SecurityMarker\nclass RememberMeDsl {\n    var authenticationSuccessHandler: AuthenticationSuccessHandler? = null\n    var key: String? = null\n    var rememberMeServices: RememberMeServices? = null\n    var rememberMeParameter: String? = null\n    var rememberMeCookieName: String? = null\n    var rememberMeCookieDomain: String? = null\n    var tokenRepository: PersistentTokenRepository? = null\n    var userDetailsService: UserDetailsService? = null\n    var tokenValiditySeconds: Int? = null\n    var useSecureCookie: Boolean? = null\n    var alwaysRemember: Boolean? = null\n\n    internal fun get(): (RememberMeConfigurer<HttpSecurity>) -> Unit {\n        return { rememberMe ->\n            authenticationSuccessHandler?.also { rememberMe.authenticationSuccessHandler(authenticationSuccessHandler) }\n            key?.also { rememberMe.key(key) }\n            rememberMeServices?.also { rememberMe.rememberMeServices(rememberMeServices) }\n            rememberMeParameter?.also { rememberMe.rememberMeParameter(rememberMeParameter) }\n            rememberMeCookieName?.also { rememberMe.rememberMeCookieName(rememberMeCookieName) }\n            rememberMeCookieDomain?.also { rememberMe.rememberMeCookieDomain(rememberMeCookieDomain) }\n            tokenRepository?.also { rememberMe.tokenRepository(tokenRepository) }\n            userDetailsService?.also { rememberMe.userDetailsService(userDetailsService) }\n            tokenValiditySeconds?.also { rememberMe.tokenValiditySeconds(tokenValiditySeconds!!) }\n            useSecureCookie?.also { rememberMe.useSecureCookie(useSecureCookie!!) }\n            alwaysRemember?.also { rememberMe.alwaysRemember(alwaysRemember!!) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/RequestCacheDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer\nimport org.springframework.security.web.savedrequest.RequestCache\n\n/**\n * A Kotlin DSL to enable request caching for [HttpSecurity] using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property requestCache allows explicit configuration of the [RequestCache] to be used\n */\n@SecurityMarker\nclass RequestCacheDsl {\n    var requestCache: RequestCache? = null\n\n    internal fun get(): (RequestCacheConfigurer<HttpSecurity>) -> Unit {\n        return { requestCacheConfig ->\n            requestCache?.also {\n                requestCacheConfig.requestCache(requestCache)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/RequiresChannelDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:Suppress(\"DEPRECATION\")\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer\nimport org.springframework.security.web.access.channel.ChannelDecisionManagerImpl\nimport org.springframework.security.web.access.channel.ChannelProcessor\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher\nimport org.springframework.security.web.util.matcher.RequestMatcher\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] channel security using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property channelProcessors the [ChannelProcessor] instances to use in\n * [ChannelDecisionManagerImpl]\n */\n@Deprecated(message=\"since 6.5 use redirectToHttps instead\")\nclass RequiresChannelDsl : AbstractRequestMatcherDsl() {\n    private val channelSecurityRules = mutableListOf<AuthorizationRule>()\n\n    private val PATTERN_TYPE = PatternType.PATH\n\n    var channelProcessors: List<ChannelProcessor>? = null\n\n    /**\n     * Adds a channel security rule.\n     *\n     * @param matches the [RequestMatcher] to match incoming requests against\n     * @param attribute the configuration attribute to secure the matching request\n     * (i.e. \"REQUIRES_SECURE_CHANNEL\")\n     */\n    fun secure(matches: RequestMatcher = AnyRequestMatcher.INSTANCE,\n               attribute: String = \"REQUIRES_SECURE_CHANNEL\") {\n        channelSecurityRules.add(MatcherAuthorizationRule(matches, attribute))\n    }\n\n    /**\n     * Adds a request authorization rule for an endpoint matching the provided\n     * pattern.\n     * If Spring MVC is not an the classpath, it will use an ant matcher.\n     * If Spring MVC is on the classpath, it will use an MVC matcher.\n     * The MVC will use the same rules that Spring MVC uses for matching.\n     * For example, often times a mapping of the path \"/path\" will match on\n     * \"/path\", \"/path/\", \"/path.html\", etc.\n     * If the current request will not be processed by Spring MVC, a reasonable default\n     * using the pattern as an ant pattern will be used.\n     *\n     * @param pattern the pattern to match incoming requests against.\n     * @param attribute the configuration attribute to secure the matching request\n     * (i.e. \"REQUIRES_SECURE_CHANNEL\")\n     */\n    fun secure(pattern: String, attribute: String = \"REQUIRES_SECURE_CHANNEL\") {\n        channelSecurityRules.add(PatternAuthorizationRule(pattern = pattern,\n                                                          patternType = PATTERN_TYPE,\n                                                          rule = attribute))\n    }\n\n    /**\n     * Adds a request authorization rule for an endpoint matching the provided\n     * pattern.\n     * If Spring MVC is not an the classpath, it will use an ant matcher.\n     * If Spring MVC is on the classpath, it will use an MVC matcher.\n     * The MVC will use the same rules that Spring MVC uses for matching.\n     * For example, often times a mapping of the path \"/path\" will match on\n     * \"/path\", \"/path/\", \"/path.html\", etc.\n     * If the current request will not be processed by Spring MVC, a reasonable default\n     * using the pattern as an ant pattern will be used.\n     *\n     * @param pattern the pattern to match incoming requests against.\n     * @param servletPath the servlet path to match incoming requests against. This\n     * only applies when using an MVC pattern matcher.\n     * @param attribute the configuration attribute to secure the matching request\n     * (i.e. \"REQUIRES_SECURE_CHANNEL\")\n     */\n    fun secure(pattern: String, servletPath: String, attribute: String = \"REQUIRES_SECURE_CHANNEL\") {\n        channelSecurityRules.add(PatternAuthorizationRule(pattern = pattern,\n                                                          patternType = PATTERN_TYPE,\n                                                          servletPath = servletPath,\n                                                          rule = attribute))\n    }\n\n    /**\n     * Specify channel security is active.\n     */\n    val requiresSecure = \"REQUIRES_SECURE_CHANNEL\"\n\n    /**\n     * Specify channel security is inactive.\n     */\n    val requiresInsecure = \"REQUIRES_INSECURE_CHANNEL\"\n\n    internal fun get(): (ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry) -> Unit {\n        return { channelSecurity ->\n            channelProcessors?.also { channelSecurity.channelProcessors(channelProcessors) }\n            channelSecurityRules.forEach { rule ->\n                when (rule) {\n                    is MatcherAuthorizationRule -> channelSecurity.requestMatchers(rule.matcher).requires(rule.rule)\n                    is PatternAuthorizationRule -> {\n                        var builder = channelSecurity.applicationContext.getBeanProvider(\n                            PathPatternRequestMatcher.Builder::class.java)\n                            .getIfUnique(PathPatternRequestMatcher::withDefaults);\n                        if (rule.servletPath != null) {\n                            builder = builder.basePath(rule.servletPath)\n                        }\n                        channelSecurity.requestMatchers(builder.matcher(rule.httpMethod, rule.pattern)).requires(rule.rule)\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/Saml2Dsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] SAML2 login using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property relyingPartyRegistrationRepository the [RelyingPartyRegistrationRepository] of relying parties,\n * each party representing a service provider, SP and this host, and identity provider, IDP pair that\n * communicate with each other.\n * @property loginPage the login page to redirect to if authentication is required (i.e.\n * \"/login\")\n * @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after\n * authentication success\n * @property authenticationFailureHandler the [AuthenticationFailureHandler] used after\n * authentication success\n * @property failureUrl the URL to send users if authentication fails\n * @property loginProcessingUrl the URL to validate the credentials\n * @property permitAll whether to grant access to the urls for [failureUrl] as well as\n * for the [HttpSecurityBuilder], the [loginPage] and [loginProcessingUrl] for every user\n * @property authenticationManager the [AuthenticationManager] to be used during SAML 2\n * authentication.\n */\n@SecurityMarker\nclass Saml2Dsl {\n    var relyingPartyRegistrationRepository: RelyingPartyRegistrationRepository? = null\n    var loginPage: String? = null\n    var authenticationRequestUriQuery: String? = null\n    var authenticationSuccessHandler: AuthenticationSuccessHandler? = null\n    var authenticationFailureHandler: AuthenticationFailureHandler? = null\n    var failureUrl: String? = null\n    var loginProcessingUrl: String? = null\n    var permitAll: Boolean? = null\n    var authenticationManager: AuthenticationManager? = null\n\n    private var defaultSuccessUrlOption: Pair<String, Boolean>? = null\n\n    /**\n     * Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the\n     * [loginPage] and [loginProcessingUrl] for every user.\n     */\n    fun permitAll() {\n        permitAll = true\n    }\n\n    /**\n     * Specifies where users will be redirected after authenticating successfully if\n     * they have not visited a secured page prior to authenticating or [alwaysUse]\n     * is true.\n     *\n     * @param defaultSuccessUrl the default success url\n     * @param alwaysUse true if the [defaultSuccessUrl] should be used after\n     * authentication despite if a protected page had been previously visited\n     */\n    fun defaultSuccessUrl(defaultSuccessUrl: String, alwaysUse: Boolean) {\n        defaultSuccessUrlOption = Pair(defaultSuccessUrl, alwaysUse)\n    }\n\n    internal fun get(): (Saml2LoginConfigurer<HttpSecurity>) -> Unit {\n        return { saml2Login ->\n            relyingPartyRegistrationRepository?.also { saml2Login.relyingPartyRegistrationRepository(relyingPartyRegistrationRepository) }\n            loginPage?.also { saml2Login.loginPage(loginPage) }\n            failureUrl?.also { saml2Login.failureUrl(failureUrl) }\n            loginProcessingUrl?.also { saml2Login.loginProcessingUrl(loginProcessingUrl) }\n            permitAll?.also { saml2Login.permitAll(permitAll!!) }\n            defaultSuccessUrlOption?.also {\n                saml2Login.defaultSuccessUrl(defaultSuccessUrlOption!!.first, defaultSuccessUrlOption!!.second)\n            }\n            authenticationRequestUriQuery?.also {\n                saml2Login.authenticationRequestUriQuery(authenticationRequestUriQuery)\n            }\n            authenticationSuccessHandler?.also { saml2Login.successHandler(authenticationSuccessHandler) }\n            authenticationFailureHandler?.also { saml2Login.failureHandler(authenticationFailureHandler) }\n            authenticationManager?.also { saml2Login.authenticationManager(authenticationManager) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/Saml2LogoutDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer\nimport org.springframework.security.config.annotation.web.saml2.LogoutRequestDsl\nimport org.springframework.security.config.annotation.web.saml2.LogoutResponseDsl\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] SAML2 logout using idiomatic Kotlin code.\n *\n * @author Josh Cummings\n * @since 6.3\n * @property relyingPartyRegistrationRepository the [RelyingPartyRegistrationRepository] of relying parties,\n * each party representing a service provider, SP and this host, and identity provider, IDP pair that\n * communicate with each other.\n * @property logoutUrl the logout page to begin the SLO redirect flow\n */\n@SecurityMarker\nclass Saml2LogoutDsl {\n    var relyingPartyRegistrationRepository: RelyingPartyRegistrationRepository? = null\n    var logoutUrl: String? = null\n\n    private var logoutRequest: ((Saml2LogoutConfigurer<HttpSecurity>.LogoutRequestConfigurer) -> Unit)? = null\n    private var logoutResponse: ((Saml2LogoutConfigurer<HttpSecurity>.LogoutResponseConfigurer) -> Unit)? = null\n\n    /**\n     * Configures SAML 2.0 Logout Request components\n     * @param logoutRequestConfig the {@link Customizer} to provide more\n     * options for the {@link LogoutRequestConfigurer}\n     */\n    fun logoutRequest(logoutRequestConfig: LogoutRequestDsl.() -> Unit) {\n        this.logoutRequest = LogoutRequestDsl().apply(logoutRequestConfig).get()\n    }\n\n    /**\n     * Configures SAML 2.0 Logout Response components\n     * @param logoutResponseConfig the {@link Customizer} to provide more\n     * options for the {@link LogoutResponseConfigurer}\n     */\n    fun logoutResponse(logoutResponseConfig: LogoutResponseDsl.() -> Unit) {\n        this.logoutResponse = LogoutResponseDsl().apply(logoutResponseConfig).get()\n    }\n\n    internal fun get(): (Saml2LogoutConfigurer<HttpSecurity>) -> Unit {\n        return { saml2Logout ->\n            relyingPartyRegistrationRepository?.also { saml2Logout.relyingPartyRegistrationRepository(relyingPartyRegistrationRepository) }\n            logoutUrl?.also { saml2Logout.logoutUrl(logoutUrl) }\n            logoutRequest?.also { saml2Logout.logoutRequest(logoutRequest) }\n            logoutResponse?.also { saml2Logout.logoutResponse(logoutResponse) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/Saml2MetadataDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.authentication.AuthenticationManagerResolver\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.oauth2.resourceserver.JwtDsl\nimport org.springframework.security.config.annotation.web.oauth2.resourceserver.OpaqueTokenDsl\nimport org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver\nimport org.springframework.security.web.AuthenticationEntryPoint\nimport org.springframework.security.web.access.AccessDeniedHandler\nimport jakarta.servlet.http.HttpServletRequest\nimport org.springframework.security.config.annotation.web.configurers.saml2.Saml2MetadataConfigurer\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] SAML 2.0 relying party metadata support using\n * idiomatic Kotlin code.\n *\n * @author Josh Cummings\n * @since 6.1\n * @property metadataUrl the name of the relying party metadata endpoint; defaults to `/saml2/metadata` and `/saml2/metadata/{registrationId}`\n * @property metadataResponseResolver the [Saml2MetadataResponseResolver] to use for resolving the\n * metadata request into metadata\n */\n@SecurityMarker\nclass Saml2MetadataDsl {\n    var metadataUrl: String? = null\n    var metadataResponseResolver: Saml2MetadataResponseResolver? = null\n\n    internal fun get(): (Saml2MetadataConfigurer<HttpSecurity>) -> Unit {\n        return { saml2Metadata ->\n            metadataResponseResolver?.also { saml2Metadata.metadataResponseResolver(metadataResponseResolver) }\n            metadataUrl?.also { saml2Metadata.metadataUrl(metadataUrl) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/SecurityContextDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer\nimport org.springframework.security.web.context.SecurityContextRepository\n\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] security context using idiomatic Kotlin code.\n *\n * @property securityContextRepository the [SecurityContextRepository] used for persisting [org.springframework.security.core.context.SecurityContext] between requests\n * @author Norbert Nowak\n * @since 5.7\n */\n@SecurityMarker\nclass SecurityContextDsl {\n\n    var securityContextRepository: SecurityContextRepository? = null\n    var requireExplicitSave: Boolean? = null\n\n    internal fun get(): (SecurityContextConfigurer<HttpSecurity>) -> Unit {\n        return { securityContext ->\n            securityContextRepository?.also { securityContext.securityContextRepository(it) }\n            requireExplicitSave?.also { securityContext.requireExplicitSave(it) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/SecurityMarker.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\n/**\n * Marker annotation indicating that the annotated class is part of the security DSL.\n *\n * @author Eleftheria Stein\n * @since 5.3\n */\n@DslMarker\nannotation class SecurityMarker\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/SessionManagementDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.session.SessionConcurrencyDsl\nimport org.springframework.security.config.annotation.web.session.SessionFixationDsl\nimport org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer\nimport org.springframework.security.config.http.SessionCreationPolicy\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy\nimport org.springframework.security.web.session.InvalidSessionStrategy\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] session management using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n */\n@SecurityMarker\nclass SessionManagementDsl {\n    var invalidSessionUrl: String? = null\n    var invalidSessionStrategy: InvalidSessionStrategy? = null\n    var sessionAuthenticationErrorUrl: String? = null\n    var sessionAuthenticationFailureHandler: AuthenticationFailureHandler? = null\n    var enableSessionUrlRewriting: Boolean? = null\n    var requireExplicitAuthenticationStrategy: Boolean? = null\n    var sessionCreationPolicy: SessionCreationPolicy? = null\n    var sessionAuthenticationStrategy: SessionAuthenticationStrategy? = null\n    private var sessionFixation: ((SessionManagementConfigurer<HttpSecurity>.SessionFixationConfigurer) -> Unit)? = null\n    private var sessionConcurrency: ((SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer) -> Unit)? = null\n\n    /**\n     * Enables session fixation protection.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             sessionManagement {\n     *                 sessionFixation { }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param sessionFixationConfig custom configurations to configure session fixation\n     * protection\n     * @see [SessionFixationDsl]\n     */\n    fun sessionFixation(sessionFixationConfig: SessionFixationDsl.() -> Unit) {\n        this.sessionFixation = SessionFixationDsl().apply(sessionFixationConfig).get()\n    }\n\n    /**\n     * Controls the behaviour of multiple sessions for a user.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebSecurity\n     * class SecurityConfig {\n     *\n     *     @Bean\n     *     fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n     *         http {\n     *             sessionManagement {\n     *                 sessionConcurrency {\n     *                     maximumSessions = 1\n     *                     maxSessionsPreventsLogin = true\n     *                 }\n     *             }\n     *         }\n     *         return http.build()\n     *     }\n     * }\n     * ```\n     *\n     * @param sessionConcurrencyConfig custom configurations to configure concurrency\n     * control\n     * @see [SessionConcurrencyDsl]\n     */\n    fun sessionConcurrency(sessionConcurrencyConfig: SessionConcurrencyDsl.() -> Unit) {\n        this.sessionConcurrency = SessionConcurrencyDsl().apply(sessionConcurrencyConfig).get()\n    }\n\n    internal fun get(): (SessionManagementConfigurer<HttpSecurity>) -> Unit {\n        return { sessionManagement ->\n            invalidSessionUrl?.also { sessionManagement.invalidSessionUrl(invalidSessionUrl) }\n            requireExplicitAuthenticationStrategy?.also { sessionManagement.requireExplicitAuthenticationStrategy(requireExplicitAuthenticationStrategy!!) }\n            invalidSessionStrategy?.also { sessionManagement.invalidSessionStrategy(invalidSessionStrategy) }\n            sessionAuthenticationErrorUrl?.also { sessionManagement.sessionAuthenticationErrorUrl(sessionAuthenticationErrorUrl) }\n            sessionAuthenticationFailureHandler?.also { sessionManagement.sessionAuthenticationFailureHandler(sessionAuthenticationFailureHandler) }\n            enableSessionUrlRewriting?.also { sessionManagement.enableSessionUrlRewriting(enableSessionUrlRewriting!!) }\n            sessionCreationPolicy?.also { sessionManagement.sessionCreationPolicy(sessionCreationPolicy) }\n            sessionAuthenticationStrategy?.also { sessionManagement.sessionAuthenticationStrategy(sessionAuthenticationStrategy) }\n            sessionFixation?.also { sessionManagement.sessionFixation(sessionFixation) }\n            sessionConcurrency?.also { sessionManagement.sessionConcurrency(sessionConcurrency) }\n        }\n    }\n}\n\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/WebAuthnDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.http.converter.HttpMessageConverter\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.WebAuthnConfigurer\nimport org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] webauthn using idiomatic Kotlin code.\n * @property rpName the relying party name\n * @property rpId the relying party id\n * @property allowedOrigins allowed origins\n * @property disableDefaultRegistrationPage disable default webauthn registration page\n * @since 6.4\n * @author Rob Winch\n * @author Max Batischev\n */\n@SecurityMarker\nclass WebAuthnDsl {\n    var rpName: String? = null\n    var rpId: String? = null\n    var allowedOrigins: Set<String>? = null\n    var disableDefaultRegistrationPage: Boolean? = false\n    var creationOptionsRepository: PublicKeyCredentialCreationOptionsRepository? = null\n    var messageConverter: HttpMessageConverter<Any>? = null\n\n    internal fun get(): (WebAuthnConfigurer<HttpSecurity>) -> Unit {\n        return { webAuthn ->\n            rpName?.also { webAuthn.rpName(rpName) }\n            rpId?.also { webAuthn.rpId(rpId) }\n            allowedOrigins?.also { webAuthn.allowedOrigins(allowedOrigins) }\n            disableDefaultRegistrationPage?.also { webAuthn.disableDefaultRegistrationPage(disableDefaultRegistrationPage!!) }\n            creationOptionsRepository?.also { webAuthn.creationOptionsRepository(creationOptionsRepository) }\n            messageConverter?.also { webAuthn.messageConverter(messageConverter) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/X509Dsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.springframework.security.authentication.AuthenticationDetailsSource\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.X509Configurer\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService\nimport org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails\nimport org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter\nimport org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor\nimport jakarta.servlet.http.HttpServletRequest\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] X509 based pre authentication\n * using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property x509AuthenticationFilter the entire [X509AuthenticationFilter]. If\n * this is specified, the properties on [X509Configurer] will not be populated\n * on the {@link X509AuthenticationFilter}.\n * @property x509PrincipalExtractor the [X509PrincipalExtractor]\n * @property authenticationDetailsSource the [X509PrincipalExtractor]\n * @property userDetailsService shortcut for invoking\n * [authenticationUserDetailsService] with a [UserDetailsByNameServiceWrapper]\n * @property authenticationUserDetailsService the [AuthenticationUserDetailsService] to use\n * @property subjectPrincipalRegex the regex to extract the principal from the certificate\n */\n@SecurityMarker\nclass X509Dsl {\n    var x509AuthenticationFilter: X509AuthenticationFilter? = null\n    var x509PrincipalExtractor: X509PrincipalExtractor? = null\n    var authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails>? = null\n    var userDetailsService: UserDetailsService? = null\n    var authenticationUserDetailsService: AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>? = null\n    @Deprecated(\"Use x509PrincipalExtractor instead\")\n    var subjectPrincipalRegex: String? = null\n\n\n    internal fun get(): (X509Configurer<HttpSecurity>) -> Unit {\n        return { x509 ->\n            x509AuthenticationFilter?.also { x509.x509AuthenticationFilter(x509AuthenticationFilter) }\n            x509PrincipalExtractor?.also { x509.x509PrincipalExtractor(x509PrincipalExtractor) }\n            authenticationDetailsSource?.also { x509.authenticationDetailsSource(authenticationDetailsSource) }\n            userDetailsService?.also { x509.userDetailsService(userDetailsService) }\n            authenticationUserDetailsService?.also { x509.authenticationUserDetailsService(authenticationUserDetailsService) }\n            @Suppress(\"DEPRECATION\")\n            subjectPrincipalRegex?.also { x509.subjectPrincipalRegex(subjectPrincipalRegex) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/CacheControlDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] cache control headers using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n */\n@HeadersSecurityMarker\nclass CacheControlDsl {\n    private var disabled = false\n\n    /**\n     * Disable cache control headers.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.CacheControlConfig) -> Unit {\n        return { cacheControl ->\n            if (disabled) {\n                cacheControl.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/ContentSecurityPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] Content-Security-Policy header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property policyDirectives the security policy directive(s) to be used in the response header.\n * @property reportOnly includes the Content-Security-Policy-Report-Only header in the response.\n */\n@HeadersSecurityMarker\nclass ContentSecurityPolicyDsl {\n    var policyDirectives: String? = null\n    var reportOnly: Boolean? = null\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.ContentSecurityPolicyConfig) -> Unit {\n        return { contentSecurityPolicy ->\n            policyDirectives?.also {\n                contentSecurityPolicy.policyDirectives(policyDirectives)\n            }\n            reportOnly?.also {\n                if (reportOnly!!) {\n                    contentSecurityPolicy.reportOnly()\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/ContentTypeOptionsDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\n\n/**\n * A Kotlin DSL to configure [HttpSecurity] X-Content-Type-Options header using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n */\n@HeadersSecurityMarker\nclass ContentTypeOptionsDsl {\n    private var disabled = false\n\n    /**\n     * Disable the X-Content-Type-Options header.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.ContentTypeOptionsConfig) -> Unit {\n        return { contentTypeOptions ->\n            if (disabled) {\n                contentTypeOptions.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/CrossOriginEmbedderPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Embedder-Policy header using\n * idiomatic Kotlin code.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @property policy the policy to be used in the response header.\n */\n@HeadersSecurityMarker\nclass CrossOriginEmbedderPolicyDsl {\n\n    var policy: CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy? = null\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginEmbedderPolicyConfig) -> Unit {\n        return { crossOriginEmbedderPolicy ->\n            policy?.also {\n                crossOriginEmbedderPolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/CrossOriginOpenerPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Opener-Policy header using\n * idiomatic Kotlin code.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @property policy the policy to be used in the response header.\n */\n@HeadersSecurityMarker\nclass CrossOriginOpenerPolicyDsl {\n\n    var policy: CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy? = null\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginOpenerPolicyConfig) -> Unit {\n        return { crossOriginOpenerPolicy ->\n            policy?.also {\n                crossOriginOpenerPolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/CrossOriginResourcePolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Resource-Policy header using\n * idiomatic Kotlin code.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @property policy the policy to be used in the response header.\n */\n@HeadersSecurityMarker\nclass CrossOriginResourcePolicyDsl {\n\n    var policy: CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy? = null\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginResourcePolicyConfig) -> Unit {\n        return { crossOriginResourcePolicy ->\n            policy?.also {\n                crossOriginResourcePolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/FrameOptionsDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] X-Frame-Options header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property sameOrigin allow any request that comes from the same origin to frame this\n * application.\n * @property deny deny framing any content from this application.\n */\n@HeadersSecurityMarker\nclass FrameOptionsDsl {\n    var sameOrigin: Boolean? = null\n    var deny: Boolean? = null\n\n    private var disabled = false\n\n    /**\n     * Disable the X-Frame-Options header.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.FrameOptionsConfig) -> Unit {\n        return { frameOptions ->\n            sameOrigin?.also {\n                if (sameOrigin!!) {\n                    frameOptions.sameOrigin()\n                }\n            }\n            deny?.also {\n                if (deny!!) {\n                    frameOptions.deny()\n                }\n            }\n            if (disabled) {\n                frameOptions.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/HeadersSecurityMarker.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\n/**\n * Marker annotation indicating that the annotated class is part of the headers security DSL.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@DslMarker\nannotation class HeadersSecurityMarker\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/HttpPublicKeyPinningDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:Suppress(\"DEPRECATION\")\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] HTTP Public Key Pinning header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property pins the value for the pin- directive of the Public-Key-Pins header.\n * @property maxAgeInSeconds the value (in seconds) for the max-age directive of the\n * Public-Key-Pins header.\n * @property includeSubDomains if true, the pinning policy applies to this pinned host\n * as well as any subdomains of the host's domain name.\n * @property reportOnly if true, the browser should not terminate the connection with\n * the server.\n * @property reportUri the URI to which the browser should report pin validation failures.\n * @deprecated see <a href=\"https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning\">Certificate and Public Key Pinning</a> for more context\n */\n@HeadersSecurityMarker\n@Deprecated(message = \"as of 5.8 with no replacement\")\nclass HttpPublicKeyPinningDsl {\n    var pins: Map<String, String>? = null\n    var maxAgeInSeconds: Long? = null\n    var includeSubDomains: Boolean? = null\n    var reportOnly: Boolean? = null\n    var reportUri: String? = null\n\n    private var disabled = false\n\n    /**\n     * Disable the HTTP Public Key Pinning header.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.HpkpConfig) -> Unit {\n        return { hpkp ->\n            pins?.also {\n                hpkp.withPins(pins)\n            }\n            maxAgeInSeconds?.also {\n                hpkp.maxAgeInSeconds(maxAgeInSeconds!!)\n            }\n            includeSubDomains?.also {\n                hpkp.includeSubDomains(includeSubDomains!!)\n            }\n            reportOnly?.also {\n                hpkp.reportOnly(reportOnly!!)\n            }\n            reportUri?.also {\n                hpkp.reportUri(reportUri)\n            }\n            if (disabled) {\n                hpkp.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/HttpStrictTransportSecurityDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.web.util.matcher.RequestMatcher\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] HTTP Strict Transport Security header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property maxAgeInSeconds the value (in seconds) for the max-age directive of the\n * Strict-Transport-Security header.\n * @property requestMatcher the [RequestMatcher] used to determine if the\n * \"Strict-Transport-Security\" header should be added. If true the header is added,\n * else the header is not added.\n * @property includeSubDomains if true, subdomains should be considered HSTS Hosts too.\n * @property preload if true, preload will be included in HSTS Header.\n */\n@HeadersSecurityMarker\nclass HttpStrictTransportSecurityDsl {\n    var maxAgeInSeconds: Long? = null\n    var requestMatcher: RequestMatcher? = null\n    var includeSubDomains: Boolean? = null\n    var preload: Boolean? = null\n\n    private var disabled = false\n\n    /**\n     * Disable the HTTP Strict Transport Security header.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.HstsConfig) -> Unit {\n        return { hsts ->\n            maxAgeInSeconds?.also { hsts.maxAgeInSeconds(maxAgeInSeconds!!) }\n            requestMatcher?.also { hsts.requestMatcher(requestMatcher) }\n            includeSubDomains?.also { hsts.includeSubDomains(includeSubDomains!!) }\n            preload?.also { hsts.preload(preload!!) }\n            if (disabled) {\n                hsts.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/PermissionsPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] permissions policy header using\n * idiomatic Kotlin code.\n *\n * @author Christophe Gilles\n * @since 5.5\n * @property policy the policy to be used in the response header.\n */\n@HeadersSecurityMarker\nclass PermissionsPolicyDsl {\n    var policy: String? = null\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.PermissionsPolicyConfig) -> Unit {\n        return { permissionsPolicy ->\n            policy?.also {\n                permissionsPolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/ReferrerPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] referrer policy header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property policy the policy to be used in the response header.\n */\n@HeadersSecurityMarker\nclass ReferrerPolicyDsl {\n    var policy: ReferrerPolicyHeaderWriter.ReferrerPolicy? = null\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit {\n        return { referrerPolicy ->\n            policy?.also {\n                referrerPolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/headers/XssProtectionConfigDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.web.header.writers.XXssProtectionHeaderWriter.HeaderValue\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] XSS protection header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @author Daniel Garnier-Moiroux\n * @since 5.3\n * @property headerValue the value of the X-XSS-Protection header. OWASP recommends [HeaderValue.DISABLED].\n */\n@HeadersSecurityMarker\nclass XssProtectionConfigDsl {\n    var headerValue: HeaderValue? = null\n\n    private var disabled = false\n\n    /**\n     * Do not include the X-XSS-Protection header in the response.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit {\n        return { xssProtection ->\n            headerValue?.also { xssProtection.headerValue(headerValue) }\n\n            if (disabled) {\n                xssProtection.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/client/AuthorizationCodeGrantDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.client\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.web.RedirectStrategy\n\n/**\n * A Kotlin DSL to configure OAuth 2.0 Authorization Code Grant.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s.\n * @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s.\n * @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.\n * @property accessTokenResponseClient the client used for requesting the access token credential\n * from the Token Endpoint.\n */\n@OAuth2ClientSecurityMarker\nclass AuthorizationCodeGrantDsl {\n    var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null\n    var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null\n    var authorizationRedirectStrategy: RedirectStrategy? = null\n    var accessTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>? = null\n\n    internal fun get(): (OAuth2ClientConfigurer<HttpSecurity>.AuthorizationCodeGrantConfigurer) -> Unit {\n        return { authorizationCodeGrant ->\n            authorizationRequestResolver?.also { authorizationCodeGrant.authorizationRequestResolver(authorizationRequestResolver) }\n            authorizationRequestRepository?.also { authorizationCodeGrant.authorizationRequestRepository(authorizationRequestRepository) }\n            authorizationRedirectStrategy?.also { authorizationCodeGrant.authorizationRedirectStrategy(authorizationRedirectStrategy) }\n            accessTokenResponseClient?.also { authorizationCodeGrant.accessTokenResponseClient(accessTokenResponseClient) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/client/OAuth2ClientSecurityMarker.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.client\n\n/**\n * Marker annotation indicating that the annotated class is part of the OAuth 2.0 client security DSL.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@DslMarker\nannotation class OAuth2ClientSecurityMarker\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/AuthorizationEndpointDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.web.RedirectStrategy\n\n/**\n * A Kotlin DSL to configure the Authorization Server's Authorization Endpoint using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property baseUri the base URI used for authorization requests.\n * @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]'s.\n * @property authorizationRequestRepository the repository used for storing [OAuth2AuthorizationRequest]'s.\n * @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.\n */\n@OAuth2LoginSecurityMarker\nclass AuthorizationEndpointDsl {\n    var baseUri: String? = null\n    var authorizationRequestResolver: OAuth2AuthorizationRequestResolver? = null\n    var authorizationRequestRepository: AuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null\n    var authorizationRedirectStrategy: RedirectStrategy? = null\n\n    internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.AuthorizationEndpointConfig) -> Unit {\n        return { authorizationEndpoint ->\n            baseUri?.also { authorizationEndpoint.baseUri(baseUri) }\n            authorizationRequestResolver?.also { authorizationEndpoint.authorizationRequestResolver(authorizationRequestResolver) }\n            authorizationRequestRepository?.also { authorizationEndpoint.authorizationRequestRepository(authorizationRequestRepository) }\n            authorizationRedirectStrategy?.also { authorizationEndpoint.authorizationRedirectStrategy(authorizationRedirectStrategy) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/OAuth2LoginSecurityMarker.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\n/**\n * Marker annotation indicating that the annotated class is part of the OAuth 2.0 login security DSL.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@DslMarker\nannotation class OAuth2LoginSecurityMarker\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/OidcBackChannelLogoutDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer\nimport org.springframework.security.web.authentication.logout.LogoutHandler\n\n/**\n * A Kotlin DSL to configure the OIDC 1.0 Back-Channel configuration using\n * idiomatic Kotlin code.\n *\n * @author Josh Cummings\n * @since 6.2\n */\n@OAuth2LoginSecurityMarker\nclass OidcBackChannelLogoutDsl {\n    private var _logoutUri: String? = null\n    private var _logoutHandler: LogoutHandler? = null\n\n    var logoutHandler: LogoutHandler?\n        get() = _logoutHandler\n        set(value) {\n            _logoutHandler = value\n            _logoutUri = null\n        }\n    var logoutUri: String?\n        get() = _logoutUri\n        set(value) {\n            _logoutUri = value\n            _logoutHandler = null\n        }\n\n    internal fun get(): (OidcLogoutConfigurer<HttpSecurity>.BackChannelLogoutConfigurer) -> Unit {\n        return { backChannel ->\n            logoutHandler?.also { backChannel.logoutHandler(logoutHandler) }\n            logoutUri?.also { backChannel.logoutUri(logoutUri) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/RedirectionEndpointDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer\n\n/**\n * A Kotlin DSL to configure the Authorization Server's Redirection Endpoint using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property baseUri the URI where the authorization response will be processed.\n */\n@OAuth2LoginSecurityMarker\nclass RedirectionEndpointDsl {\n    var baseUri: String? = null\n\n    internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.RedirectionEndpointConfig) -> Unit {\n        return { redirectionEndpoint ->\n            baseUri?.also { redirectionEndpoint.baseUri(baseUri) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/TokenEndpointDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest\n\n/**\n * A Kotlin DSL to configure the Authorization Server's Token Endpoint using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property accessTokenResponseClient the client used for requesting the access token credential\n * from the Token Endpoint.\n */\n@OAuth2LoginSecurityMarker\nclass TokenEndpointDsl {\n    var accessTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>? = null\n\n    internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.TokenEndpointConfig) -> Unit {\n        return { tokenEndpoint ->\n            accessTokenResponseClient?.also { tokenEndpoint.accessTokenResponseClient(accessTokenResponseClient) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/UserInfoEndpointDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser\nimport org.springframework.security.oauth2.core.user.OAuth2User\n\n/**\n * A Kotlin DSL to configure the Authorization Server's UserInfo Endpoint using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property userService the OAuth 2.0 service used for obtaining the user attributes of the End-User\n * from the UserInfo Endpoint.\n * @property oidcUserService the OpenID Connect 1.0 service used for obtaining the user attributes of the\n * End-User from the UserInfo Endpoint.\n * @property userAuthoritiesMapper the [GrantedAuthoritiesMapper] used for mapping [OAuth2User.getAuthorities]\n */\n@OAuth2LoginSecurityMarker\nclass UserInfoEndpointDsl {\n    var userService: OAuth2UserService<OAuth2UserRequest, OAuth2User>? = null\n    var oidcUserService: OAuth2UserService<OidcUserRequest, OidcUser>? = null\n    var userAuthoritiesMapper: GrantedAuthoritiesMapper? = null\n\n    internal fun get(): (OAuth2LoginConfigurer<HttpSecurity>.UserInfoEndpointConfig) -> Unit {\n        return { userInfoEndpoint ->\n            userService?.also { userInfoEndpoint.userService(userService) }\n            oidcUserService?.also { userInfoEndpoint.oidcUserService(oidcUserService) }\n            userAuthoritiesMapper?.also { userInfoEndpoint.userAuthoritiesMapper(userAuthoritiesMapper) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/resourceserver/JwtDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.resourceserver\n\nimport org.springframework.core.convert.converter.Converter\nimport org.springframework.security.authentication.AbstractAuthenticationToken\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.jwt.Jwt\nimport org.springframework.security.oauth2.jwt.JwtDecoder\n\n/**\n * A Kotlin DSL to configure JWT Resource Server Support using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property jwtAuthenticationConverter the [Converter] to use for converting a [Jwt] into\n * an [AbstractAuthenticationToken].\n * @property jwtDecoder the [JwtDecoder] to use.\n * @property jwkSetUri configures a [JwtDecoder] using a\n * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7517\">JSON Web Key (JWK)</a> URL\n * @property authenticationManager the [AuthenticationManager] used to determine if the provided\n * [Authentication] can be authenticated.\n */\n@OAuth2ResourceServerSecurityMarker\nclass JwtDsl {\n    private var _jwtDecoder: JwtDecoder? = null\n    private var _jwkSetUri: String? = null\n\n    var authenticationManager: AuthenticationManager? = null\n\n    var jwtAuthenticationConverter: Converter<Jwt, out AbstractAuthenticationToken>? = null\n    var jwtDecoder: JwtDecoder?\n        get() = _jwtDecoder\n        set(value) {\n            _jwtDecoder = value\n            _jwkSetUri = null\n        }\n    var jwkSetUri: String?\n        get() = _jwkSetUri\n        set(value) {\n            _jwkSetUri = value\n            _jwtDecoder = null\n        }\n\n    internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>.JwtConfigurer) -> Unit {\n        return { jwt ->\n            jwtAuthenticationConverter?.also { jwt.jwtAuthenticationConverter(jwtAuthenticationConverter) }\n            jwtDecoder?.also { jwt.decoder(jwtDecoder) }\n            jwkSetUri?.also { jwt.jwkSetUri(jwkSetUri) }\n            authenticationManager?.also { jwt.authenticationManager(authenticationManager) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/resourceserver/OAuth2ResourceServerSecurityMarker.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.resourceserver\n\n/**\n * Marker annotation indicating that the annotated class is part of the OAuth 2.0 resource server security DSL.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@DslMarker\nannotation class OAuth2ResourceServerSecurityMarker\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/resourceserver/OpaqueTokenDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.resourceserver\n\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector\n\n/**\n * A Kotlin DSL to configure opaque token Resource Server Support using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property introspectionUri the URI of the Introspection endpoint.\n * @property introspector the [OpaqueTokenIntrospector] to use.\n * @property authenticationManager the [AuthenticationManager] used to determine if the provided\n * [Authentication] can be authenticated.\n */\n@OAuth2ResourceServerSecurityMarker\nclass OpaqueTokenDsl {\n    private var _introspectionUri: String? = null\n    private var _introspector: OpaqueTokenIntrospector? = null\n    private var clientCredentials: Pair<String, String>? = null\n\n    var authenticationManager: AuthenticationManager? = null\n\n    var introspectionUri: String?\n        get() = _introspectionUri\n        set(value) {\n            _introspectionUri = value\n            _introspector = null\n        }\n    var introspector: OpaqueTokenIntrospector?\n        get() = _introspector\n        set(value) {\n            _introspector = value\n            _introspectionUri = null\n            clientCredentials = null\n        }\n\n    var authenticationConverter: OpaqueTokenAuthenticationConverter? = null\n\n    /**\n     * Configures the credentials for Introspection endpoint.\n     *\n     * @param clientId the clientId part of the credentials.\n     * @param clientSecret the clientSecret part of the credentials.\n     */\n    fun introspectionClientCredentials(clientId: String, clientSecret: String) {\n        clientCredentials = Pair(clientId, clientSecret)\n        _introspector = null\n    }\n\n    internal fun get(): (OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer) -> Unit {\n        return { opaqueToken ->\n            introspectionUri?.also { opaqueToken.introspectionUri(introspectionUri) }\n            introspector?.also { opaqueToken.introspector(introspector) }\n            authenticationConverter?.also { opaqueToken.authenticationConverter(authenticationConverter) }\n            clientCredentials?.also { opaqueToken.introspectionClientCredentials(clientCredentials!!.first, clientCredentials!!.second) }\n            authenticationManager?.also { opaqueToken.authenticationManager(authenticationManager) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/saml2/LogoutRequestDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.saml2\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver\n\n/**\n * A Kotlin DSL to configure SAML 2.0 Logout Request components using idiomatic Kotlin code.\n *\n * @author Josh Cummings\n * @since 6.3\n * @property logoutUrl The URL by which the asserting party can send a SAML 2.0 Logout Request.\n * The Asserting Party should use whatever HTTP method specified in {@link RelyingPartyRegistration#getSingleLogoutServiceBindings()}.\n * @property logoutRequestValidator the [Saml2LogoutRequestValidator] to use for validating incoming {@code LogoutRequest}s.\n * @property logoutRequestResolver the [Saml2LogoutRequestResolver] to use for generating outgoing {@code LogoutRequest}s.\n * @property logoutRequestRepository the [Saml2LogoutRequestRepository] to use for storing outgoing {@code LogoutRequest}s for\n * linking to the corresponding {@code LogoutResponse} from the asserting party\n */\n@Saml2SecurityMarker\nclass LogoutRequestDsl {\n    var logoutUrl = \"/logout/saml2/slo\"\n    var logoutRequestValidator: Saml2LogoutRequestValidator? = null\n    var logoutRequestResolver: Saml2LogoutRequestResolver? = null\n    var logoutRequestRepository: Saml2LogoutRequestRepository = HttpSessionLogoutRequestRepository()\n\n    internal fun get(): (Saml2LogoutConfigurer<HttpSecurity>.LogoutRequestConfigurer) -> Unit {\n        return { logoutRequest ->\n            logoutUrl.also { logoutRequest.logoutUrl(logoutUrl) }\n            logoutRequestValidator?.also { logoutRequest.logoutRequestValidator(logoutRequestValidator) }\n            logoutRequestResolver?.also { logoutRequest.logoutRequestResolver(logoutRequestResolver) }\n            logoutRequestRepository.also { logoutRequest.logoutRequestRepository(logoutRequestRepository) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/saml2/LogoutResponseDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.saml2\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver\n\n/**\n * A Kotlin DSL to configure SAML 2.0 Logout Response components using idiomatic Kotlin code.\n *\n * @author Josh Cummings\n * @since 6.3\n * @property logoutUrl The URL by which the asserting party can send a SAML 2.0 Logout Response.\n * The Asserting Party should use whatever HTTP method specified in {@link RelyingPartyRegistration#getSingleLogoutServiceBindings()}.\n * @property logoutResponseValidator the [Saml2LogoutResponseValidator] to use for validating incoming {@code LogoutResponse}s.\n * @property logoutResponseResolver the [Saml2LogoutResponseResolver] to use for generating outgoing {@code LogoutResponse}s.\n */\n@Saml2SecurityMarker\nclass LogoutResponseDsl {\n    var logoutUrl = \"/logout/saml2/slo\"\n    var logoutResponseValidator: Saml2LogoutResponseValidator? = null\n    var logoutResponseResolver: Saml2LogoutResponseResolver? = null\n\n    internal fun get(): (Saml2LogoutConfigurer<HttpSecurity>.LogoutResponseConfigurer) -> Unit {\n        return { logoutResponse ->\n            logoutUrl.also { logoutResponse.logoutUrl(logoutUrl) }\n            logoutResponseValidator?.also { logoutResponse.logoutResponseValidator(logoutResponseValidator) }\n            logoutResponseResolver?.also { logoutResponse.logoutResponseResolver(logoutResponseResolver) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/saml2/Saml2SecurityMarker.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.saml2\n\n/**\n * Marker annotation indicating that the annotated class is part of the SAML 2.0 logout security DSL.\n *\n * @author Josh Cummings\n * @since 6.3\n */\n@DslMarker\nannotation class Saml2SecurityMarker\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/session/SessionConcurrencyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.session\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer\nimport org.springframework.security.core.session.SessionRegistry\nimport org.springframework.security.web.authentication.session.SessionLimit\nimport org.springframework.security.web.session.SessionInformationExpiredStrategy\nimport org.springframework.util.Assert\n\n/**\n * A Kotlin DSL to configure the behaviour of multiple sessions using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n * @property maximumSessions controls the maximum number of sessions for a user.\n * @property expiredUrl the URL to redirect to if a user tries to access a resource and\n * their session has been expired due to too many sessions for the current user.\n * @property expiredSessionStrategy determines the behaviour when an expired session\n * is detected.\n * @property maxSessionsPreventsLogin if true, prevents a user from authenticating when the\n * [maximumSessions] has been reached. Otherwise (default), the user who authenticates\n * is allowed access and an existing user's session is expired.\n * @property sessionRegistry the [SessionRegistry] implementation used.\n */\n@SessionSecurityMarker\nclass SessionConcurrencyDsl {\n    var maximumSessions: Int? = null\n    var expiredUrl: String? = null\n    var expiredSessionStrategy: SessionInformationExpiredStrategy? = null\n    var maxSessionsPreventsLogin: Boolean? = null\n    var sessionRegistry: SessionRegistry? = null\n    private var sessionLimit: SessionLimit? = null\n\n    fun maximumSessions(max: SessionLimit) {\n        this.sessionLimit = max\n    }\n\n    internal fun get(): (SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer) -> Unit {\n        Assert.isTrue(maximumSessions == null || sessionLimit == null, \"You cannot specify maximumSessions as both an Int and a SessionLimit. Please use only one.\")\n        return { sessionConcurrencyControl ->\n            maximumSessions?.also {\n                sessionConcurrencyControl.maximumSessions(maximumSessions!!)\n            }\n            sessionLimit?.also {\n                sessionConcurrencyControl.maximumSessions(sessionLimit!!)\n            }\n            expiredUrl?.also {\n                sessionConcurrencyControl.expiredUrl(expiredUrl)\n            }\n            expiredSessionStrategy?.also {\n                sessionConcurrencyControl.expiredSessionStrategy(expiredSessionStrategy)\n            }\n            maxSessionsPreventsLogin?.also {\n                sessionConcurrencyControl.maxSessionsPreventsLogin(maxSessionsPreventsLogin!!)\n            }\n            sessionRegistry?.also {\n                sessionConcurrencyControl.sessionRegistry(sessionRegistry)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/session/SessionFixationDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.session\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer\nimport jakarta.servlet.http.HttpServletRequest\nimport jakarta.servlet.http.HttpSession\n\n/**\n * A Kotlin DSL to configure session fixation protection using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.3\n */\n@SessionSecurityMarker\nclass SessionFixationDsl {\n    private var strategy: SessionFixationStrategy? = null\n\n    /**\n     * Specifies that a new session should be created, but the session attributes from\n     * the original [HttpSession] should not be retained.\n     */\n    fun newSession() {\n        this.strategy = SessionFixationStrategy.NEW\n    }\n\n    /**\n     * Specifies that a new session should be created and the session attributes from\n     * the original [HttpSession] should be retained.\n     */\n    fun migrateSession() {\n        this.strategy = SessionFixationStrategy.MIGRATE\n    }\n\n    /**\n     * Specifies that the Servlet container-provided session fixation protection\n     * should be used. When a session authenticates, the Servlet method\n     * [HttpServletRequest.changeSessionId] is called to change the session ID\n     * and retain all session attributes.\n     */\n    fun changeSessionId() {\n        this.strategy = SessionFixationStrategy.CHANGE_ID\n    }\n\n    /**\n     * Specifies that no session fixation protection should be enabled.\n     */\n    fun none() {\n        this.strategy = SessionFixationStrategy.NONE\n    }\n\n    internal fun get(): (SessionManagementConfigurer<HttpSecurity>.SessionFixationConfigurer) -> Unit {\n        return { sessionFixation ->\n            strategy?.also {\n                when (it) {\n                    SessionFixationStrategy.NEW -> sessionFixation.newSession()\n                    SessionFixationStrategy.MIGRATE -> sessionFixation.migrateSession()\n                    SessionFixationStrategy.CHANGE_ID -> sessionFixation.changeSessionId()\n                    SessionFixationStrategy.NONE -> sessionFixation.none()\n                }\n            }\n        }\n    }\n}\n\nprivate enum class SessionFixationStrategy {\n    NEW, MIGRATE, CHANGE_ID, NONE\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/annotation/web/session/SessionSecurityMarker.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.session\n\n/**\n * Marker annotation indicating that the annotated class is part of the session security DSL.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@DslMarker\nannotation class SessionSecurityMarker\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/AuthorizeExchangeDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager\nimport org.springframework.security.authorization.AuthorityReactiveAuthorizationManager\nimport org.springframework.security.authorization.AuthorizationDecision\nimport org.springframework.security.authorization.ReactiveAuthorizationManager\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.web.server.authorization.AuthorizationContext\nimport org.springframework.security.web.server.authorization.IpAddressReactiveAuthorizationManager\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers\nimport org.springframework.security.web.util.matcher.RequestMatcher\nimport reactor.core.publisher.Mono\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] exchange authorization using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@ServerSecurityMarker\nclass AuthorizeExchangeDsl {\n    private val authorizationRules = mutableListOf<ExchangeAuthorizationRule>()\n\n    /**\n     * Adds an exchange authorization rule for an endpoint matching the provided\n     * matcher.\n     *\n     * @param matcher the [RequestMatcher] to match incoming requests against\n     * @param access the [ReactiveAuthorizationManager] which determines the access\n     * to the specific matcher.\n     * Some predefined shortcuts have already been created, such as\n     * [hasAnyAuthority], [hasAnyRole], [permitAll], [authenticated] and more\n     */\n    fun authorize(matcher: ServerWebExchangeMatcher = ServerWebExchangeMatchers.anyExchange(),\n                  access: ReactiveAuthorizationManager<AuthorizationContext> = authenticated) {\n        authorizationRules.add(MatcherExchangeAuthorizationRule(matcher, access))\n    }\n\n    /**\n     * Adds an exchange authorization rule for an endpoint matching the provided\n     * ant pattern.\n     *\n     * @param antPattern the ant ant pattern to match incoming requests against.\n     * @param access the [ReactiveAuthorizationManager] which determines the access\n     * to the specific matcher.\n     * Some predefined shortcuts have already been created, such as\n     * [hasAnyAuthority], [hasAnyRole], [permitAll], [authenticated] and more\n     */\n    fun authorize(antPattern: String, access: ReactiveAuthorizationManager<AuthorizationContext> = authenticated) {\n        authorizationRules.add(PatternExchangeAuthorizationRule(antPattern, access))\n    }\n\n    /**\n     * Matches any exchange.\n     */\n    val anyExchange: ServerWebExchangeMatcher = ServerWebExchangeMatchers.anyExchange()\n\n    /**\n     * Allow access for anyone.\n     */\n    val permitAll: ReactiveAuthorizationManager<AuthorizationContext> =\n            ReactiveAuthorizationManager { _: Mono<Authentication>, _: AuthorizationContext -> Mono.just(AuthorizationDecision(true)) }\n\n    /**\n     * Deny access for everyone.\n     */\n    val denyAll: ReactiveAuthorizationManager<AuthorizationContext> =\n            ReactiveAuthorizationManager { _: Mono<Authentication>, _: AuthorizationContext -> Mono.just(AuthorizationDecision(false)) }\n\n    /**\n     * Require a specific role. This is a shortcut for [hasAuthority].\n     */\n    fun hasRole(role: String): ReactiveAuthorizationManager<AuthorizationContext> =\n            AuthorityReactiveAuthorizationManager.hasRole<AuthorizationContext>(role)\n\n    /**\n     * Require any specific role. This is a shortcut for [hasAnyAuthority].\n     */\n    fun hasAnyRole(vararg roles: String): ReactiveAuthorizationManager<AuthorizationContext> =\n            AuthorityReactiveAuthorizationManager.hasAnyRole<AuthorizationContext>(*roles)\n\n    /**\n     * Require a specific authority.\n     */\n    fun hasAuthority(authority: String): ReactiveAuthorizationManager<AuthorizationContext> =\n            AuthorityReactiveAuthorizationManager.hasAuthority<AuthorizationContext>(authority)\n\n    /**\n     * Require any authority.\n     */\n    fun hasAnyAuthority(vararg authorities: String): ReactiveAuthorizationManager<AuthorizationContext> =\n            AuthorityReactiveAuthorizationManager.hasAnyAuthority<AuthorizationContext>(*authorities)\n\n    /**\n     * Require a specific IP or range of IP addresses.\n     * @since 5.7\n     */\n    fun hasIpAddress(ipAddress: String): ReactiveAuthorizationManager<AuthorizationContext> =\n            IpAddressReactiveAuthorizationManager.hasIpAddress(ipAddress)\n\n    /**\n     * Require an authenticated user.\n     */\n    val authenticated: ReactiveAuthorizationManager<AuthorizationContext> =\n            AuthenticatedReactiveAuthorizationManager.authenticated<AuthorizationContext>()\n\n    internal fun get(): (ServerHttpSecurity.AuthorizeExchangeSpec) -> Unit {\n        return { requests ->\n            authorizationRules.forEach { rule ->\n                when (rule) {\n                    is MatcherExchangeAuthorizationRule -> requests.matchers(rule.matcher).access(rule.rule)\n                    is PatternExchangeAuthorizationRule -> requests.pathMatchers(rule.pattern).access(rule.rule)\n                }\n            }\n        }\n    }\n\n    private data class MatcherExchangeAuthorizationRule(val matcher: ServerWebExchangeMatcher,\n                                                        override val rule: ReactiveAuthorizationManager<AuthorizationContext>) : ExchangeAuthorizationRule(rule)\n\n    private data class PatternExchangeAuthorizationRule(val pattern: String,\n                                                        override val rule: ReactiveAuthorizationManager<AuthorizationContext>) : ExchangeAuthorizationRule(rule)\n\n    private abstract class ExchangeAuthorizationRule(open val rule: ReactiveAuthorizationManager<AuthorizationContext>)\n}\n\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerAnonymousDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.GrantedAuthority\nimport org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] anonymous authentication using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property key the key to identify tokens created for anonymous authentication\n * @property principal the principal for [Authentication] objects of anonymous users\n * @property authorities the [Authentication.getAuthorities] for anonymous users\n * @property authenticationFilter the [AnonymousAuthenticationWebFilter] used to populate\n * an anonymous user.\n */\n@ServerSecurityMarker\nclass ServerAnonymousDsl {\n    var key: String? = null\n    var principal: Any? = null\n    var authorities: List<GrantedAuthority>? = null\n    var authenticationFilter: AnonymousAuthenticationWebFilter? = null\n\n    private var disabled = false\n\n    /**\n     * Disables anonymous authentication\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.AnonymousSpec) -> Unit {\n        return { anonymous ->\n            key?.also { anonymous.key(key) }\n            principal?.also { anonymous.principal(principal) }\n            authorities?.also { anonymous.authorities(authorities) }\n            authenticationFilter?.also { anonymous.authenticationFilter(authenticationFilter) }\n            if (disabled) {\n                anonymous.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerCacheControlDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] cache control headers using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@ServerSecurityMarker\nclass ServerCacheControlDsl {\n    private var disabled = false\n\n    /**\n     * Disables cache control response headers\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.CacheSpec) -> Unit {\n        return { cacheControl ->\n            if (disabled) {\n                cacheControl.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerContentSecurityPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] Content-Security-Policy header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@ServerSecurityMarker\nclass ServerContentSecurityPolicyDsl {\n    var policyDirectives: String? = null\n    var reportOnly: Boolean? = null\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.ContentSecurityPolicySpec) -> Unit {\n        return { contentSecurityPolicy ->\n            policyDirectives?.also {\n                contentSecurityPolicy.policyDirectives(policyDirectives)\n            }\n            reportOnly?.also {\n                contentSecurityPolicy.reportOnly(reportOnly!!)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerContentTypeOptionsDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] the content type options header\n * using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@ServerSecurityMarker\nclass ServerContentTypeOptionsDsl {\n    private var disabled = false\n\n    /**\n     * Disables content type options response header\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.ContentTypeOptionsSpec) -> Unit {\n        return { contentTypeOptions ->\n            if (disabled) {\n                contentTypeOptions.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerCorsDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.web.cors.reactive.CorsConfigurationSource\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] CORS headers using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property configurationSource the [CorsConfigurationSource] to use.\n */\n@ServerSecurityMarker\nclass ServerCorsDsl {\n    var configurationSource: CorsConfigurationSource? = null\n\n    private var disabled = false\n\n    /**\n     * Disables CORS support within Spring Security.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.CorsSpec) -> Unit {\n        return { cors ->\n            configurationSource?.also { cors.configurationSource(configurationSource) }\n            if (disabled) {\n                cors.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginEmbedderPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Embedder-Policy header using\n * idiomatic Kotlin code.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @property policy the policy to be used in the response header.\n */\n@ServerSecurityMarker\nclass ServerCrossOriginEmbedderPolicyDsl {\n\n    var policy: CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy? = null\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit {\n        return { crossOriginEmbedderPolicy ->\n            policy?.also {\n                crossOriginEmbedderPolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginOpenerPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Opener-Policy header using\n * idiomatic Kotlin code.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @property policy the policy to be used in the response header.\n */\n@ServerSecurityMarker\nclass ServerCrossOriginOpenerPolicyDsl {\n\n    var policy: CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy? = null\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit {\n        return { crossOriginOpenerPolicy ->\n            policy?.also {\n                crossOriginOpenerPolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerCrossOriginResourcePolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter\n\n/**\n * A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Resource-Policy header using\n * idiomatic Kotlin code.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @property policy the policy to be used in the response header.\n */\n@ServerSecurityMarker\nclass ServerCrossOriginResourcePolicyDsl {\n\n    var policy: CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy? = null\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit {\n        return { crossOriginResourcePolicy ->\n            policy?.also {\n                crossOriginResourcePolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerCsrfDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.authorization.ServerAccessDeniedHandler\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRepository\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRequestHandler\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] CSRF protection using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property accessDeniedHandler the [ServerAccessDeniedHandler] used when a CSRF token is invalid.\n * @property csrfTokenRepository the [ServerCsrfTokenRepository] used to persist the CSRF token.\n * @property requireCsrfProtectionMatcher the [ServerWebExchangeMatcher] used to determine when CSRF protection\n * is enabled.\n * @property csrfTokenRequestHandler the  [ServerCsrfTokenRequestHandler] that is used to make the CSRF token\n * available as an exchange attribute\n */\n@ServerSecurityMarker\nclass ServerCsrfDsl {\n    var accessDeniedHandler: ServerAccessDeniedHandler? = null\n    var csrfTokenRepository: ServerCsrfTokenRepository? = null\n    var requireCsrfProtectionMatcher: ServerWebExchangeMatcher? = null\n    var csrfTokenRequestHandler: ServerCsrfTokenRequestHandler? = null\n\n    private var disabled = false\n\n    /**\n     * Disables CSRF protection\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.CsrfSpec) -> Unit {\n        return { csrf ->\n            accessDeniedHandler?.also { csrf.accessDeniedHandler(accessDeniedHandler) }\n            csrfTokenRepository?.also { csrf.csrfTokenRepository(csrfTokenRepository) }\n            requireCsrfProtectionMatcher?.also { csrf.requireCsrfProtectionMatcher(requireCsrfProtectionMatcher) }\n            csrfTokenRequestHandler?.also { csrf.csrfTokenRequestHandler(csrfTokenRequestHandler) }\n            if (disabled) {\n                csrf.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerExceptionHandlingDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint\nimport org.springframework.security.web.server.authorization.ServerAccessDeniedHandler\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] exception handling using idiomatic Kotlin\n * code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property authenticationEntryPoint the [ServerAuthenticationEntryPoint] to use when\n * the application request authentication\n * @property accessDeniedHandler the [ServerAccessDeniedHandler] to use when an\n * authenticated user does not hold a required authority\n */\n@ServerSecurityMarker\nclass ServerExceptionHandlingDsl {\n    var authenticationEntryPoint: ServerAuthenticationEntryPoint? = null\n    var accessDeniedHandler: ServerAccessDeniedHandler? = null\n\n    internal fun get(): (ServerHttpSecurity.ExceptionHandlingSpec) -> Unit {\n        return { exceptionHandling ->\n            authenticationEntryPoint?.also { exceptionHandling.authenticationEntryPoint(authenticationEntryPoint) }\n            accessDeniedHandler?.also { exceptionHandling.accessDeniedHandler(accessDeniedHandler) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerFormLoginDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.context.SecurityContext\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler\nimport org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler\nimport org.springframework.security.web.server.context.ReactorContextWebFilter\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] form login using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property authenticationManager the [ReactiveAuthenticationManager] used to authenticate.\n * @property loginPage the url to redirect to which provides a form to log in (i.e. \"/login\").\n * If this is customized:\n * - The default log in & log out page are no longer provided\n * - The application must render a log in page at the provided URL\n * - The application must render an authentication error page at the provided URL + \"?error\"\n * - Authentication will occur for POST to the provided URL\n * @property authenticationEntryPoint configures how to request for authentication.\n * @property requiresAuthenticationMatcher configures when authentication is performed.\n * @property authenticationSuccessHandler the [ServerAuthenticationSuccessHandler] used after\n * authentication success.\n * @property authenticationFailureHandler the [ServerAuthenticationFailureHandler] used to handle\n * a failed authentication.\n * @property securityContextRepository the [ServerSecurityContextRepository] used to save\n * the [Authentication]. For the [SecurityContext] to be loaded on subsequent requests the\n * [ReactorContextWebFilter] must be configured to be able to load the value (they are not\n * implicitly linked).\n */\n@ServerSecurityMarker\nclass ServerFormLoginDsl {\n    var authenticationManager: ReactiveAuthenticationManager? = null\n    var loginPage: String? = null\n    var authenticationEntryPoint: ServerAuthenticationEntryPoint? = null\n    var requiresAuthenticationMatcher: ServerWebExchangeMatcher? = null\n    var authenticationSuccessHandler: ServerAuthenticationSuccessHandler? = null\n    var authenticationFailureHandler: ServerAuthenticationFailureHandler? = null\n    var securityContextRepository: ServerSecurityContextRepository? = null\n\n    private var disabled = false\n\n    /**\n     * Disables HTTP basic authentication\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.FormLoginSpec) -> Unit {\n        return { formLogin ->\n            authenticationManager?.also { formLogin.authenticationManager(authenticationManager) }\n            loginPage?.also { formLogin.loginPage(loginPage) }\n            authenticationEntryPoint?.also { formLogin.authenticationEntryPoint(authenticationEntryPoint) }\n            requiresAuthenticationMatcher?.also { formLogin.requiresAuthenticationMatcher(requiresAuthenticationMatcher) }\n            authenticationSuccessHandler?.also { formLogin.authenticationSuccessHandler(authenticationSuccessHandler) }\n            authenticationFailureHandler?.also { formLogin.authenticationFailureHandler(authenticationFailureHandler) }\n            securityContextRepository?.also { formLogin.securityContextRepository(securityContextRepository) }\n            if (disabled) {\n                formLogin.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerFrameOptionsDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] X-Frame-Options header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property mode the X-Frame-Options mode to set in the response header.\n */\n@ServerSecurityMarker\nclass ServerFrameOptionsDsl {\n    var mode: XFrameOptionsServerHttpHeadersWriter.Mode? = null\n\n    private var disabled = false\n\n    /**\n     * Disables the X-Frame-Options response header\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.FrameOptionsSpec) -> Unit {\n        return { frameOptions ->\n            mode?.also {\n                frameOptions.mode(mode)\n            }\n            if (disabled) {\n                frameOptions.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerHeadersDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.header.CacheControlServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.ServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] headers using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@ServerSecurityMarker\nclass ServerHeadersDsl {\n    private var contentTypeOptions: ((ServerHttpSecurity.HeaderSpec.ContentTypeOptionsSpec) -> Unit)? = null\n    private var xssProtection: ((ServerHttpSecurity.HeaderSpec.XssProtectionSpec) -> Unit)? = null\n    private var cacheControl: ((ServerHttpSecurity.HeaderSpec.CacheSpec) -> Unit)? = null\n    private var hsts: ((ServerHttpSecurity.HeaderSpec.HstsSpec) -> Unit)? = null\n    private var frameOptions: ((ServerHttpSecurity.HeaderSpec.FrameOptionsSpec) -> Unit)? = null\n    private var contentSecurityPolicy: ((ServerHttpSecurity.HeaderSpec.ContentSecurityPolicySpec) -> Unit)? = null\n    private var referrerPolicy: ((ServerHttpSecurity.HeaderSpec.ReferrerPolicySpec) -> Unit)? = null\n    private var featurePolicyDirectives: String? = null\n    private var permissionsPolicy: ((ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit)? = null\n    private var crossOriginOpenerPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit)? = null\n    private var crossOriginEmbedderPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit)? = null\n    private var crossOriginResourcePolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit)? = null\n    private var writers = mutableListOf<ServerHttpHeadersWriter>()\n\n    private var disabled = false\n\n    /**\n     * Configures the [ContentTypeOptionsServerHttpHeadersWriter] which inserts the <a href=\n     * \"https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx\"\n     * >X-Content-Type-Options header</a>\n     *\n     * @param contentTypeOptionsConfig the customization to apply to the header\n     */\n    fun contentTypeOptions(contentTypeOptionsConfig: ServerContentTypeOptionsDsl.() -> Unit) {\n        this.contentTypeOptions = ServerContentTypeOptionsDsl().apply(contentTypeOptionsConfig).get()\n    }\n\n    /**\n     * <strong>Note this is not comprehensive XSS protection!</strong>\n     *\n     * <p>\n     * Allows customizing the [XXssProtectionServerHttpHeadersWriter] which adds the <a href=\n     * \"https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx\"\n     * >X-XSS-Protection header</a>\n     * </p>\n     *\n     * @param xssProtectionConfig the customization to apply to the header\n     */\n    fun xssProtection(xssProtectionConfig: ServerXssProtectionDsl.() -> Unit) {\n        this.xssProtection = ServerXssProtectionDsl().apply(xssProtectionConfig).get()\n    }\n\n    /**\n     * Allows customizing the [CacheControlServerHttpHeadersWriter]. Specifically it adds\n     * the following headers:\n     * <ul>\n     * <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>\n     * <li>Pragma: no-cache</li>\n     * <li>Expires: 0</li>\n     * </ul>\n     *\n     * @param cacheControlConfig the customization to apply to the headers\n     */\n    fun cache(cacheControlConfig: ServerCacheControlDsl.() -> Unit) {\n        this.cacheControl = ServerCacheControlDsl().apply(cacheControlConfig).get()\n    }\n\n    /**\n     * Allows customizing the [StrictTransportSecurityServerHttpHeadersWriter] which provides support\n     * for <a href=\"https://tools.ietf.org/html/rfc6797\">HTTP Strict Transport Security\n     * (HSTS)</a>.\n     *\n     * @param hstsConfig the customization to apply to the header\n     */\n    fun hsts(hstsConfig: ServerHttpStrictTransportSecurityDsl.() -> Unit) {\n        this.hsts = ServerHttpStrictTransportSecurityDsl().apply(hstsConfig).get()\n    }\n\n    /**\n     * Allows customizing the [XFrameOptionsServerHttpHeadersWriter] which add the X-Frame-Options\n     * header.\n     *\n     * @param frameOptionsConfig the customization to apply to the header\n     */\n    fun frameOptions(frameOptionsConfig: ServerFrameOptionsDsl.() -> Unit) {\n        this.frameOptions = ServerFrameOptionsDsl().apply(frameOptionsConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://www.w3.org/TR/CSP2/\">Content Security Policy (CSP) Level 2</a>.\n     *\n     * @param contentSecurityPolicyConfig the customization to apply to the header\n     */\n    fun contentSecurityPolicy(contentSecurityPolicyConfig: ServerContentSecurityPolicyDsl.() -> Unit) {\n        this.contentSecurityPolicy = ServerContentSecurityPolicyDsl().apply(contentSecurityPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://www.w3.org/TR/referrer-policy/\">Referrer Policy</a>.\n     *\n     * <p>\n     * Configuration is provided to the [ReferrerPolicyServerHttpHeadersWriter] which support the writing\n     * of the header as detailed in the W3C Technical Report:\n     * </p>\n     * <ul>\n     *  <li>Referrer-Policy</li>\n     * </ul>\n     *\n     * @param referrerPolicyConfig the customization to apply to the header\n     */\n    fun referrerPolicy(referrerPolicyConfig: ServerReferrerPolicyDsl.() -> Unit) {\n        this.referrerPolicy = ServerReferrerPolicyDsl().apply(referrerPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://wicg.github.io/feature-policy/\">Feature\n     * Policy</a>.\n     *\n     * <p>\n     * Calling this method automatically enables (includes) the Feature-Policy\n     * header in the response using the supplied policy directive(s).\n     * <p>\n     *\n     * @param policyDirectives policyDirectives the security policy directive(s)\n     */\n    @Deprecated(\"Use 'permissionsPolicy { }' instead.\")\n    fun featurePolicy(policyDirectives: String) {\n        this.featurePolicyDirectives = policyDirectives\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://w3c.github.io/webappsec-permissions-policy/\">Permissions\n     * Policy</a>.\n     *\n     * <p>\n     * Calling this method automatically enables (includes) the Permissions-Policy\n     * header in the response using the supplied policy directive(s).\n     * <p>\n     *\n     * @param permissionsPolicyConfig the customization to apply to the header\n     */\n    fun permissionsPolicy(permissionsPolicyConfig: ServerPermissionsPolicyDsl.() -> Unit) {\n        this.permissionsPolicy = ServerPermissionsPolicyDsl().apply(permissionsPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy\">\n     * Cross-Origin-Opener-Policy</a> header.\n     *\n     * @since 5.7\n     * @param crossOriginOpenerPolicyConfig the customization to apply to the header\n     */\n    fun crossOriginOpenerPolicy(crossOriginOpenerPolicyConfig: ServerCrossOriginOpenerPolicyDsl.() -> Unit) {\n        this.crossOriginOpenerPolicy = ServerCrossOriginOpenerPolicyDsl().apply(crossOriginOpenerPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy\">\n     * Cross-Origin-Embedder-Policy</a> header.\n     *\n     * @since 5.7\n     * @param crossOriginEmbedderPolicyConfig the customization to apply to the header\n     */\n    fun crossOriginEmbedderPolicy(crossOriginEmbedderPolicyConfig: ServerCrossOriginEmbedderPolicyDsl.() -> Unit) {\n        this.crossOriginEmbedderPolicy = ServerCrossOriginEmbedderPolicyDsl().apply(crossOriginEmbedderPolicyConfig).get()\n    }\n\n    /**\n     * Allows configuration for <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy\">\n     * Cross-Origin-Resource-Policy</a> header.\n     *\n     * @since 5.7\n     * @param crossOriginResourcePolicyConfig the customization to apply to the header\n     */\n    fun crossOriginResourcePolicy(crossOriginResourcePolicyConfig: ServerCrossOriginResourcePolicyDsl.() -> Unit) {\n        this.crossOriginResourcePolicy = ServerCrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()\n    }\n\n    /**\n     * Configures custom headers writer\n     *\n     * @since 6.5\n     * @param writer the [ServerHttpHeadersWriter] to provide custom headers writer\n     */\n    fun writer(writer: ServerHttpHeadersWriter) {\n        this.writers.add(writer)\n    }\n\n    /**\n     * Disables HTTP response headers.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    @Suppress(\"DEPRECATION\")\n    internal fun get(): (ServerHttpSecurity.HeaderSpec) -> Unit {\n        return { headers ->\n            contentTypeOptions?.also {\n                headers.contentTypeOptions(contentTypeOptions)\n            }\n            xssProtection?.also {\n                headers.xssProtection(xssProtection)\n            }\n            cacheControl?.also {\n                headers.cache(cacheControl)\n            }\n            hsts?.also {\n                headers.hsts(hsts)\n            }\n            frameOptions?.also {\n                headers.frameOptions(frameOptions)\n            }\n            contentSecurityPolicy?.also {\n                headers.contentSecurityPolicy(contentSecurityPolicy)\n            }\n            featurePolicyDirectives?.also {\n                headers.featurePolicy(featurePolicyDirectives)\n            }\n            permissionsPolicy?.also {\n                headers.permissionsPolicy(permissionsPolicy)\n            }\n            referrerPolicy?.also {\n                headers.referrerPolicy(referrerPolicy)\n            }\n            crossOriginOpenerPolicy?.also {\n                headers.crossOriginOpenerPolicy(crossOriginOpenerPolicy)\n            }\n            crossOriginEmbedderPolicy?.also {\n                headers.crossOriginEmbedderPolicy(crossOriginEmbedderPolicy)\n            }\n            crossOriginResourcePolicy?.also {\n                headers.crossOriginResourcePolicy(crossOriginResourcePolicy)\n            }\n            writers.also {\n                writers.forEach { writer -> headers.writer(writer) }\n            }\n            if (disabled) {\n                headers.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpBasicDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.context.SecurityContext\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler\nimport org.springframework.security.web.server.context.ReactorContextWebFilter\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] basic authorization using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property authenticationManager the [ReactiveAuthenticationManager] used to authenticate.\n * @property securityContextRepository the [ServerSecurityContextRepository] used to save\n * the [Authentication]. For the [SecurityContext] to be loaded on subsequent requests the\n * [ReactorContextWebFilter] must be configured to be able to load the value (they are not\n * implicitly linked).\n * @property authenticationEntryPoint the [ServerAuthenticationEntryPoint] to be\n * populated on [BasicAuthenticationFilter] in the event that authentication fails.\n */\n@ServerSecurityMarker\nclass ServerHttpBasicDsl {\n    var authenticationManager: ReactiveAuthenticationManager? = null\n    var securityContextRepository: ServerSecurityContextRepository? = null\n    var authenticationFailureHandler: ServerAuthenticationFailureHandler? = null\n    var authenticationEntryPoint: ServerAuthenticationEntryPoint? = null\n\n    private var disabled = false\n\n    /**\n     * Disables HTTP basic authentication\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.HttpBasicSpec) -> Unit {\n        return { httpBasic ->\n            authenticationManager?.also { httpBasic.authenticationManager(authenticationManager) }\n            securityContextRepository?.also { httpBasic.securityContextRepository(securityContextRepository) }\n            authenticationFailureHandler?.also { httpBasic.authenticationFailureHandler(authenticationFailureHandler) }\n            authenticationEntryPoint?.also { httpBasic.authenticationEntryPoint(authenticationEntryPoint) }\n            if (disabled) {\n                httpBasic.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.beans.factory.ObjectProvider\nimport org.springframework.context.ApplicationContext\nimport org.springframework.core.MethodParameter\nimport org.springframework.core.ResolvableType\nimport org.springframework.core.annotation.AnnotationUtils\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher\nimport org.springframework.util.ReflectionUtils\nimport org.springframework.web.server.ServerWebExchange\nimport org.springframework.web.server.WebFilter\nimport java.lang.reflect.Method\nimport java.lang.reflect.Modifier\n\n/**\n * Configures [ServerHttpSecurity] using a [ServerHttpSecurity Kotlin DSL][ServerHttpSecurityDsl].\n *\n * Example:\n *\n * ```\n * @Configuration\n * @EnableWebFluxSecurity\n * class SecurityConfig {\n *\n *  @Bean\n *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n *      return http {\n *          authorizeExchange {\n *              authorize(\"/public\", permitAll)\n *              authorize(anyExchange, authenticated)\n *          }\n *       }\n *   }\n * }\n * ```\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @param httpConfiguration the configurations to apply to [ServerHttpSecurity]\n */\noperator fun ServerHttpSecurity.invoke(httpConfiguration: ServerHttpSecurityDsl.() -> Unit): SecurityWebFilterChain =\n        ServerHttpSecurityDsl(this, httpConfiguration).build()\n\n/**\n * A [ServerHttpSecurity] Kotlin DSL created by [`http { }`][invoke]\n * in order to configure [ServerHttpSecurity] using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @param init the configurations to apply to the provided [ServerHttpSecurity]\n * @property authenticationManager the default [ReactiveAuthenticationManager] to use\n */\n@ServerSecurityMarker\nclass ServerHttpSecurityDsl(private val http: ServerHttpSecurity, private val init: ServerHttpSecurityDsl.() -> Unit) {\n\n    var authenticationManager: ReactiveAuthenticationManager? = null\n    var securityContextRepository: ServerSecurityContextRepository? = null\n\n    init {\n        applyFunction1HttpSecurityDslBeans(this.http.applicationContext, this)\n        applyTopLevelFunction1SecurityDslBeans(this.http.applicationContext, this)\n    }\n\n    /**\n     * Applies all `Function1<ServerHttpSecurityDsl,Unit>` Beans which\n     * allows exposing the DSL as Beans to be applied.\n     *\n     * ```\n     * @Bean\n     * @Order(Ordered.LOWEST_PRECEDENCE)\n     * fun userAuthorization(): ServerHttpSecurityDsl.() -> Unit {\n     *     // @formatter:off\n     *     return {\n     *         authorizeExchange {\n     *             authorize(\"/user/profile\", hasRole(\"USER\"))\n     *         }\n     *     }\n     *     // @formatter:on\n     * }\n     * ```\n     */\n    private fun applyFunction1HttpSecurityDslBeans(context: ApplicationContext, http: ServerHttpSecurityDsl) : Unit {\n        val httpSecurityDslFnType = ResolvableType.forClassWithGenerics(Function1::class.java,\n            ServerHttpSecurityDsl::class.java, Unit::class.java)\n        val httpSecurityDslFnProvider = context\n            .getBeanProvider<Function1<ServerHttpSecurityDsl,Unit>>(httpSecurityDslFnType)\n\n        // @formatter:off\n        httpSecurityDslFnProvider.orderedStream().forEach { fn -> fn.invoke(http) }\n        // @formatter:on\n    }\n\n    /**\n     * Applies all `Function1<T,Unit>` Beans such that `T` is a top level\n     * DSL on `ServerHttpSecurityDsl`. This allows exposing the top level\n     * DSLs as Beans to be applied.\n     *\n     * ```\n     * @Bean\n     * fun headersSecurity(): Customizer<ServerHttpSecurity.HeaderSpec> {\n     *     // @formatter:off\n     *     return Customizer { headers -> headers\n     *         .contentSecurityPolicy { csp -> csp\n     *             .policyDirectives(\"object-src 'none'\")\n     *         }\n     *     }\n     *     // @formatter:on\n     * }\n     * ```\n     *\n     * @param context the [ApplicationContext]\n     * @param http the [HttpSecurity]\n     * @throws Exception\n     */\n    private fun applyTopLevelFunction1SecurityDslBeans(context: ApplicationContext, http: ServerHttpSecurityDsl) {\n        val isCustomizerMethod = ReflectionUtils.MethodFilter { method: Method ->\n            if (Modifier.isStatic(method.modifiers)) {\n                return@MethodFilter false\n            }\n            if (!Modifier.isPublic(method.modifiers)) {\n                return@MethodFilter false\n            }\n            if (!method.canAccess(http)) {\n                return@MethodFilter false\n            }\n            if (method.parameterCount != 1) {\n                return@MethodFilter false\n            }\n            return@MethodFilter extractDslType(method) != null\n        }\n\n        val invokeWithEachDslBean = ReflectionUtils.MethodCallback { dslMethod: Method ->\n            val dslFunctionType = firstMethodResolvableType(dslMethod)!!\n            val dslFunctionProvider: ObjectProvider<*> = context.getBeanProvider<Any>(dslFunctionType)\n\n            // @formatter:off\n            dslFunctionProvider.orderedStream().forEach {customizer: Any -> ReflectionUtils.invokeMethod(dslMethod, http, customizer)}\n        }\n        ReflectionUtils.doWithMethods(ServerHttpSecurityDsl::class.java, invokeWithEachDslBean, isCustomizerMethod)\n    }\n\n    /**\n     * From a `Method` with the first argument `Function<T,Unit>` return `T` or `null`\n     * if the first argument is not a `Function`.\n     * @return From a `Method` with the first argument `Function<T,Unit>` return `T`.\n     */\n    private fun extractDslType(method: Method): ResolvableType? {\n        val functionType = firstMethodResolvableType(method)\n        if (!Function::class.java.isAssignableFrom(functionType.toClass())) {\n            return null\n        }\n        val functionInputType = functionType.getGeneric(0)\n        val securityMarker = AnnotationUtils.findAnnotation(functionInputType.toClass(), ServerSecurityMarker::class.java)\n        val isSecurityDsl = securityMarker != null\n        if (!isSecurityDsl) {\n            return null\n        }\n        return functionInputType\n    }\n\n    private fun firstMethodResolvableType(method: Method): ResolvableType {\n        val parameter = MethodParameter(\n            method, 0\n        )\n        return ResolvableType.forMethodParameter(parameter)\n    }\n\n    /**\n     * Allows configuring the [ServerHttpSecurity] to only be invoked when matching the\n     * provided [ServerWebExchangeMatcher].\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          securityMatcher(PathPatternParserServerWebExchangeMatcher(\"/api/&ast;&ast;\"))\n     *          formLogin {\n     *              loginPage = \"/log-in\"\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param securityMatcher a [ServerWebExchangeMatcher] used to determine whether this\n     * configuration should be invoked.\n     */\n    fun securityMatcher(securityMatcher: ServerWebExchangeMatcher) {\n        this.http.securityMatcher(securityMatcher)\n    }\n\n    /**\n     * Adds a [WebFilter] at a specific position.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          addFilterAt(CustomWebFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param webFilter the [WebFilter] to add\n     * @param order the place to insert the [WebFilter]\n     */\n    fun addFilterAt(webFilter: WebFilter, order: SecurityWebFiltersOrder) {\n        this.http.addFilterAt(webFilter, order)\n    }\n\n    /**\n     * Adds a [WebFilter] before specific position.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          addFilterBefore(CustomWebFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param webFilter the [WebFilter] to add\n     * @param order the place before which to insert the [WebFilter]\n     */\n    fun addFilterBefore(webFilter: WebFilter, order: SecurityWebFiltersOrder) {\n        this.http.addFilterBefore(webFilter, order)\n    }\n\n    /**\n     * Adds a [WebFilter] after specific position.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          addFilterAfter(CustomWebFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param webFilter the [WebFilter] to add\n     * @param order the place after which to insert the [WebFilter]\n     */\n    fun addFilterAfter(webFilter: WebFilter, order: SecurityWebFiltersOrder) {\n        this.http.addFilterAfter(webFilter, order)\n    }\n\n    /**\n     * Enables form based authentication.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          formLogin {\n     *              loginPage = \"/log-in\"\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param formLoginConfiguration custom configuration to apply to the form based\n     * authentication\n     * @see [ServerFormLoginDsl]\n     */\n    fun formLogin(formLoginConfiguration: ServerFormLoginDsl.() -> Unit) {\n        val formLoginCustomizer = ServerFormLoginDsl().apply(formLoginConfiguration).get()\n        this.http.formLogin(formLoginCustomizer)\n    }\n\n    /**\n     * Allows restricting access based upon the [ServerWebExchange]\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          authorizeExchange {\n     *              authorize(\"/public\", permitAll)\n     *              authorize(anyExchange, authenticated)\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param authorizeExchangeConfiguration custom configuration that specifies\n     * access for an exchange\n     * @see [AuthorizeExchangeDsl]\n     */\n    fun authorizeExchange(authorizeExchangeConfiguration: AuthorizeExchangeDsl.() -> Unit) {\n        val authorizeExchangeCustomizer = AuthorizeExchangeDsl().apply(authorizeExchangeConfiguration).get()\n        this.http.authorizeExchange(authorizeExchangeCustomizer)\n    }\n\n    /**\n     * Enables HTTP basic authentication.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          httpBasic { }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param httpBasicConfiguration custom configuration to be applied to the\n     * HTTP basic authentication\n     * @see [ServerHttpBasicDsl]\n     */\n    fun httpBasic(httpBasicConfiguration: ServerHttpBasicDsl.() -> Unit) {\n        val httpBasicCustomizer = ServerHttpBasicDsl().apply(httpBasicConfiguration).get()\n        this.http.httpBasic(httpBasicCustomizer)\n    }\n\n    /**\n     * Enables password management.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          passwordManagement {\n     *              changePasswordPage = \"/custom-change-password-page\"\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param passwordManagementConfiguration custom configuration to be applied to the\n     * password management\n     * @see [ServerPasswordManagementDsl]\n     * @since 5.6\n     */\n    fun passwordManagement(passwordManagementConfiguration: ServerPasswordManagementDsl.() -> Unit) {\n        val passwordManagementCustomizer = ServerPasswordManagementDsl().apply(passwordManagementConfiguration).get()\n        this.http.passwordManagement(passwordManagementCustomizer)\n    }\n\n    /**\n     * Allows configuring response headers.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          headers {\n     *              referrerPolicy {\n     *                  policy = ReferrerPolicy.SAME_ORIGIN\n     *              }\n     *              frameOptions {\n     *                  mode = Mode.DENY\n     *              }\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param headersConfiguration custom configuration to be applied to the\n     * response headers\n     * @see [ServerHeadersDsl]\n     */\n    fun headers(headersConfiguration: ServerHeadersDsl.() -> Unit) {\n        val headersCustomizer = ServerHeadersDsl().apply(headersConfiguration).get()\n        this.http.headers(headersCustomizer)\n    }\n\n    /**\n     * Allows configuring CORS.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          cors {\n     *              configurationSource = customConfigurationSource\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param corsConfiguration custom configuration to be applied to the\n     * CORS headers\n     * @see [ServerCorsDsl]\n     */\n    fun cors(corsConfiguration: ServerCorsDsl.() -> Unit) {\n        val corsCustomizer = ServerCorsDsl().apply(corsConfiguration).get()\n        this.http.cors(corsCustomizer)\n    }\n\n    /**\n     * Allows configuring HTTPS redirection rules.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          redirectToHttps {\n     *              httpsRedirectWhen {\n     *                  it.request.headers.containsKey(\"X-Requires-Https\")\n     *              }\n     *          }\n     *      }\n     *   }\n     * }\n     * ```\n     *\n     * @param httpsRedirectConfiguration custom configuration for the HTTPS redirect\n     * rules.\n     * @see [ServerHttpsRedirectDsl]\n     */\n    fun redirectToHttps(httpsRedirectConfiguration: ServerHttpsRedirectDsl.() -> Unit) {\n        val httpsRedirectCustomizer = ServerHttpsRedirectDsl().apply(httpsRedirectConfiguration).get()\n        this.http.redirectToHttps(httpsRedirectCustomizer)\n    }\n\n    /**\n     * Allows configuring exception handling.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          exceptionHandling {\n     *              authenticationEntryPoint = RedirectServerAuthenticationEntryPoint(\"/auth\")\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param exceptionHandlingConfiguration custom configuration to apply to\n     * exception handling\n     * @see [ServerExceptionHandlingDsl]\n     */\n    fun exceptionHandling(exceptionHandlingConfiguration: ServerExceptionHandlingDsl.() -> Unit) {\n        val exceptionHandlingCustomizer = ServerExceptionHandlingDsl().apply(exceptionHandlingConfiguration).get()\n        this.http.exceptionHandling(exceptionHandlingCustomizer)\n    }\n\n    /**\n     * Adds X509 based pre authentication to an application using a certificate provided by a client.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          x509 { }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param x509Configuration custom configuration to apply to the X509 based pre authentication\n     * @see [ServerX509Dsl]\n     */\n    fun x509(x509Configuration: ServerX509Dsl.() -> Unit) {\n        val x509Customizer = ServerX509Dsl().apply(x509Configuration).get()\n        this.http.x509(x509Customizer)\n    }\n\n    /**\n     * Allows configuring request cache which is used when a flow is interrupted (i.e. due to requesting credentials)\n     * so that the request can be replayed after authentication.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          requestCache { }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param requestCacheConfiguration custom configuration to apply to the request cache\n     * @see [ServerRequestCacheDsl]\n     */\n    fun requestCache(requestCacheConfiguration: ServerRequestCacheDsl.() -> Unit) {\n        val requestCacheCustomizer = ServerRequestCacheDsl().apply(requestCacheConfiguration).get()\n        this.http.requestCache(requestCacheCustomizer)\n    }\n\n    /**\n     * Enables CSRF protection.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          csrf { }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param csrfConfiguration custom configuration to apply to the CSRF protection\n     * @see [ServerCsrfDsl]\n     */\n    fun csrf(csrfConfiguration: ServerCsrfDsl.() -> Unit) {\n        val csrfCustomizer = ServerCsrfDsl().apply(csrfConfiguration).get()\n        this.http.csrf(csrfCustomizer)\n    }\n\n    /**\n     * Provides logout support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          logout {\n     *              logoutUrl = \"/sign-out\"\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param logoutConfiguration custom configuration to apply to logout\n     * @see [ServerLogoutDsl]\n     */\n    fun logout(logoutConfiguration: ServerLogoutDsl.() -> Unit) {\n        val logoutCustomizer = ServerLogoutDsl().apply(logoutConfiguration).get()\n        this.http.logout(logoutCustomizer)\n    }\n\n    /**\n     * Enables and configures anonymous authentication.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          anonymous {\n     *              authorities = listOf(SimpleGrantedAuthority(\"ROLE_ANON\"))\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param anonymousConfiguration custom configuration to apply to anonymous authentication\n     * @see [ServerAnonymousDsl]\n     */\n    fun anonymous(anonymousConfiguration: ServerAnonymousDsl.() -> Unit) {\n        val anonymousCustomizer = ServerAnonymousDsl().apply(anonymousConfiguration).get()\n        this.http.anonymous(anonymousCustomizer)\n    }\n\n    /**\n     * Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n     * A [ReactiveClientRegistrationRepository] is required and must be registered as a Bean or\n     * configured via [ServerOAuth2LoginDsl.clientRegistrationRepository].\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          oauth2Login {\n     *              clientRegistrationRepository = getClientRegistrationRepository()\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param oauth2LoginConfiguration custom configuration to configure the OAuth 2.0 Login\n     * @see [ServerOAuth2LoginDsl]\n     */\n    fun oauth2Login(oauth2LoginConfiguration: ServerOAuth2LoginDsl.() -> Unit) {\n        val oauth2LoginCustomizer = ServerOAuth2LoginDsl().apply(oauth2LoginConfiguration).get()\n        this.http.oauth2Login(oauth2LoginCustomizer)\n    }\n\n    /**\n     * Configures OAuth2 client support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          oauth2Client {\n     *              clientRegistrationRepository = getClientRegistrationRepository()\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param oauth2ClientConfiguration custom configuration to configure the OAuth 2.0 client\n     * @see [ServerOAuth2ClientDsl]\n     */\n    fun oauth2Client(oauth2ClientConfiguration: ServerOAuth2ClientDsl.() -> Unit) {\n        val oauth2ClientCustomizer = ServerOAuth2ClientDsl().apply(oauth2ClientConfiguration).get()\n        this.http.oauth2Client(oauth2ClientCustomizer)\n    }\n\n    /**\n     * Configures OAuth2 resource server support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          oauth2ResourceServer {\n     *              jwt { }\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param oauth2ResourceServerConfiguration custom configuration to configure the OAuth 2.0 resource server\n     * @see [ServerOAuth2ResourceServerDsl]\n     */\n    fun oauth2ResourceServer(oauth2ResourceServerConfiguration: ServerOAuth2ResourceServerDsl.() -> Unit) {\n        val oauth2ResourceServerCustomizer = ServerOAuth2ResourceServerDsl().apply(oauth2ResourceServerConfiguration).get()\n        this.http.oauth2ResourceServer(oauth2ResourceServerCustomizer)\n    }\n\n    /**\n     * Configures logout support using an OpenID Connect 1.0 Provider.\n     * A [ReactiveClientRegistrationRepository] is required and must be registered as a Bean or\n     * configured via [ServerOidcLogoutDsl.clientRegistrationRepository].\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          oauth2Login { }\n     *          oidcLogout {\n     *              backChannel { }\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param oidcLogoutConfiguration custom configuration to configure the OIDC 1.0 Logout\n     * @see [ServerOidcLogoutDsl]\n     */\n    fun oidcLogout(oidcLogoutConfiguration: ServerOidcLogoutDsl.() -> Unit) {\n        val oidcLogoutCustomizer = ServerOidcLogoutDsl().apply(oidcLogoutConfiguration).get()\n        this.http.oidcLogout(oidcLogoutCustomizer)\n    }\n\n    /**\n     * Configures Session Management support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * open class SecurityConfig {\n     *\n     *  @Bean\n     *  open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          sessionManagement {\n     *              sessionConcurrency { }\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param sessionManagementConfig custom configuration to configure the Session Management\n     * @since 6.3\n     * @see [ServerSessionManagementDsl]\n     */\n    fun sessionManagement(sessionManagementConfig: ServerSessionManagementDsl.() -> Unit) {\n        val sessionManagementCustomizer = ServerSessionManagementDsl().apply(sessionManagementConfig).get()\n        this.http.sessionManagement(sessionManagementCustomizer)\n    }\n\n    /**\n     * Configures One-Time Token Login support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * open class SecurityConfig {\n     *\n     *  @Bean\n     *  open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          oneTimeTokenLogin {\n     *              tokenGenerationSuccessHandler = MyMagicLinkServerOneTimeTokenGenerationSuccessHandler()\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param oneTimeTokenLoginConfiguration custom configuration to configure the One-Time Token Login\n     * @since 6.4\n     * @see [ServerOneTimeTokenLoginDsl]\n     */\n    fun oneTimeTokenLogin(oneTimeTokenLoginConfiguration: ServerOneTimeTokenLoginDsl.()-> Unit){\n        val oneTimeTokenLoginCustomizer = ServerOneTimeTokenLoginDsl().apply(oneTimeTokenLoginConfiguration).get()\n        this.http.oneTimeTokenLogin(oneTimeTokenLoginCustomizer)\n    }\n\n    /**\n     * Apply all configurations to the provided [ServerHttpSecurity]\n     */\n    internal fun build(): SecurityWebFilterChain {\n        init()\n        authenticationManager?.also { this.http.authenticationManager(authenticationManager) }\n        securityContextRepository?.also { this.http.securityContextRepository(securityContextRepository) }\n        return this.http.build()\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpStrictTransportSecurityDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport java.time.Duration\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] HTTP Strict Transport Security\n * header using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property maxAge he value for the max-age directive of the Strict-Transport-Security\n * header.\n * @property includeSubdomains if true, subdomains should be considered HSTS Hosts too.\n * @property preload if true, preload will be included in HSTS Header.\n */\n@ServerSecurityMarker\nclass ServerHttpStrictTransportSecurityDsl {\n    var maxAge: Duration? = null\n    var includeSubdomains: Boolean? = null\n    var preload: Boolean? = null\n\n    private var disabled = false\n\n    /**\n     * Disables the X-Frame-Options response header\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.HstsSpec) -> Unit {\n        return { hsts ->\n            maxAge?.also { hsts.maxAge(maxAge) }\n            includeSubdomains?.also { hsts.includeSubdomains(includeSubdomains!!) }\n            preload?.also { hsts.preload(preload!!) }\n            if (disabled) {\n                hsts.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpsRedirectDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.PortMapper\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher\nimport org.springframework.web.server.ServerWebExchange\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] HTTPS redirection rules using idiomatic\n * Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property portMapper the [PortMapper] that specifies a custom HTTPS port to redirect to.\n */\n@ServerSecurityMarker\nclass ServerHttpsRedirectDsl {\n    var portMapper: PortMapper? = null\n\n    private var redirectMatchers: Array<out ServerWebExchangeMatcher>? = null\n    private var redirectMatcherFunction: ((ServerWebExchange) -> Boolean)? = null\n\n    /**\n     * Configures when this filter should redirect to https.\n     * If invoked multiple times, whether a matcher or a function is provided, only the\n     * last redirect rule will apply and all previous rules will be overridden.\n     *\n     * @param redirectMatchers the list of conditions that, when any are met, the\n     * filter should redirect to https.\n     */\n    fun httpsRedirectWhen(vararg redirectMatchers: ServerWebExchangeMatcher) {\n        this.redirectMatcherFunction = null\n        this.redirectMatchers = redirectMatchers\n    }\n\n    /**\n     * Configures when this filter should redirect to https.\n     * If invoked multiple times, whether a matcher or a function is provided, only the\n     * last redirect rule will apply and all previous rules will be overridden.\n     *\n     * @param redirectMatcherFunction the condition in which the filter should redirect to\n     * https.\n     */\n    fun httpsRedirectWhen(redirectMatcherFunction: (ServerWebExchange) -> Boolean) {\n        this.redirectMatchers = null\n        this.redirectMatcherFunction = redirectMatcherFunction\n    }\n\n    internal fun get(): (ServerHttpSecurity.HttpsRedirectSpec) -> Unit {\n        return { httpsRedirect ->\n            portMapper?.also { httpsRedirect.portMapper(portMapper) }\n            redirectMatchers?.also { httpsRedirect.httpsRedirectWhen(*redirectMatchers!!) }\n            redirectMatcherFunction?.also { httpsRedirect.httpsRedirectWhen(redirectMatcherFunction) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerJwtDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.core.convert.converter.Converter\nimport org.springframework.security.authentication.AbstractAuthenticationToken\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.jwt.Jwt\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder\nimport reactor.core.publisher.Mono\nimport java.security.interfaces.RSAPublicKey\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] JWT Resource Server support using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property authenticationManager the [ReactiveAuthenticationManager] used to determine if the provided\n * [Authentication] can be authenticated.\n * @property jwtAuthenticationConverter the [Converter] to use for converting a [Jwt] into an\n * [AbstractAuthenticationToken].\n * @property jwtDecoder the [ReactiveJwtDecoder] to use.\n * @property publicKey configures a [ReactiveJwtDecoder] that leverages the provided [RSAPublicKey]\n * @property jwkSetUri configures a [ReactiveJwtDecoder] using a\n * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7517\">JSON Web Key (JWK)</a> URL\n */\n@ServerSecurityMarker\nclass ServerJwtDsl {\n    private var _jwtDecoder: ReactiveJwtDecoder? = null\n    private var _publicKey: RSAPublicKey? = null\n    private var _jwkSetUri: String? = null\n\n    var authenticationManager: ReactiveAuthenticationManager? = null\n    var jwtAuthenticationConverter: Converter<Jwt, out Mono<out AbstractAuthenticationToken>>? = null\n\n    var jwtDecoder: ReactiveJwtDecoder?\n        get() = _jwtDecoder\n        set(value) {\n            _jwtDecoder = value\n            _publicKey = null\n            _jwkSetUri = null\n        }\n    var publicKey: RSAPublicKey?\n        get() = _publicKey\n        set(value) {\n            _publicKey = value\n            _jwtDecoder = null\n            _jwkSetUri = null\n        }\n    var jwkSetUri: String?\n        get() = _jwkSetUri\n        set(value) {\n            _jwkSetUri = value\n            _jwtDecoder = null\n            _publicKey = null\n        }\n\n    internal fun get(): (ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec) -> Unit {\n        return { jwt ->\n            authenticationManager?.also { jwt.authenticationManager(authenticationManager) }\n            jwtAuthenticationConverter?.also { jwt.jwtAuthenticationConverter(jwtAuthenticationConverter) }\n            jwtDecoder?.also { jwt.jwtDecoder(jwtDecoder) }\n            publicKey?.also { jwt.publicKey(publicKey) }\n            jwkSetUri?.also { jwt.jwkSetUri(jwkSetUri) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerLogoutDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] logout support using idiomatic Kotlin\n * code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property logoutHandler a [ServerLogoutHandler] that is invoked when logout occurs.\n * @property logoutUrl the URL that triggers logout to occur.\n * @property requiresLogout the [ServerWebExchangeMatcher] that triggers logout to occur.\n * @property logoutSuccessHandler the [ServerLogoutSuccessHandler] to use after logout has\n * occurred.\n */\n@ServerSecurityMarker\nclass ServerLogoutDsl {\n    var logoutHandler: ServerLogoutHandler? = null\n    var logoutUrl: String? = null\n    var requiresLogout: ServerWebExchangeMatcher? = null\n    var logoutSuccessHandler: ServerLogoutSuccessHandler? = null\n\n    private var disabled = false\n\n    /**\n     * Disables logout\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.LogoutSpec) -> Unit {\n        return { logout ->\n            logoutHandler?.also { logout.logoutHandler(logoutHandler) }\n            logoutUrl?.also { logout.logoutUrl(logoutUrl) }\n            requiresLogout?.also { logout.requiresLogout(requiresLogout) }\n            logoutSuccessHandler?.also { logout.logoutSuccessHandler(logoutSuccessHandler) }\n            if (disabled) {\n                logout.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerOAuth2ClientDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.web.server.ServerRedirectStrategy\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter\nimport org.springframework.web.server.ServerWebExchange\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] OAuth 2.0 client using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property authenticationManager the [ReactiveAuthenticationManager] used to determine if the provided\n * [Authentication] can be authenticated.\n * @property authenticationConverter the [ServerAuthenticationConverter] used for converting from a [ServerWebExchange]\n * to an [Authentication].\n * @property clientRegistrationRepository the repository of client registrations.\n * @property authorizedClientRepository the repository for authorized client(s).\n * @property authorizationRequestRepository the repository to use for storing [OAuth2AuthorizationRequest]s.\n * @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.\n */\n@ServerSecurityMarker\nclass ServerOAuth2ClientDsl {\n    var authenticationManager: ReactiveAuthenticationManager? = null\n    var authenticationConverter: ServerAuthenticationConverter? = null\n    var clientRegistrationRepository: ReactiveClientRegistrationRepository? = null\n    var authorizedClientRepository: ServerOAuth2AuthorizedClientRepository? = null\n    var authorizationRequestRepository: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null\n    var authorizationRedirectStrategy: ServerRedirectStrategy? = null\n\n    internal fun get(): (ServerHttpSecurity.OAuth2ClientSpec) -> Unit {\n        return { oauth2Client ->\n            authenticationManager?.also { oauth2Client.authenticationManager(authenticationManager) }\n            authenticationConverter?.also { oauth2Client.authenticationConverter(authenticationConverter) }\n            clientRegistrationRepository?.also { oauth2Client.clientRegistrationRepository(clientRegistrationRepository) }\n            authorizedClientRepository?.also { oauth2Client.authorizedClientRepository(authorizedClientRepository) }\n            authorizationRequestRepository?.also { oauth2Client.authorizationRequestRepository(authorizationRequestRepository) }\n            authorizationRedirectStrategy?.also { oauth2Client.authorizationRedirectStrategy(authorizationRedirectStrategy) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerOAuth2LoginDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService\nimport org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.web.server.ServerRedirectStrategy\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler\nimport org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher\nimport org.springframework.web.server.ServerWebExchange\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] OAuth 2.0 login using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property authenticationManager the [ReactiveAuthenticationManager] used to determine if the provided\n * [Authentication] can be authenticated.\n * @property securityContextRepository the [ServerSecurityContextRepository] used to save the [Authentication].\n * @property authenticationSuccessHandler the [ServerAuthenticationSuccessHandler] used after authentication success.\n * @property authenticationFailureHandler the [ServerAuthenticationFailureHandler] used after authentication failure.\n * @property authenticationConverter the [ServerAuthenticationConverter] used for converting from a [ServerWebExchange]\n * to an [Authentication].\n * @property clientRegistrationRepository the repository of client registrations.\n * @property authorizedClientService the service responsible for associating an access token to a client and resource\n * owner.\n * @property authorizedClientRepository the repository for authorized client(s).\n * @property authorizationRequestRepository the repository to use for storing [OAuth2AuthorizationRequest]s.\n * @property authorizationRequestResolver the resolver used for resolving [OAuth2AuthorizationRequest]s.\n * @property authorizationRedirectStrategy the redirect strategy for Authorization Endpoint redirect URI.\n * @property authenticationMatcher the [ServerWebExchangeMatcher] used for determining if the request is an\n * authentication request.\n * @property loginPage the URL to send users to if login is required.\n */\n@ServerSecurityMarker\nclass ServerOAuth2LoginDsl {\n    var authenticationManager: ReactiveAuthenticationManager? = null\n    var securityContextRepository: ServerSecurityContextRepository? = null\n    var authenticationSuccessHandler: ServerAuthenticationSuccessHandler? = null\n    var authenticationFailureHandler: ServerAuthenticationFailureHandler? = null\n    var authenticationConverter: ServerAuthenticationConverter? = null\n    var clientRegistrationRepository: ReactiveClientRegistrationRepository? = null\n    var authorizedClientService: ReactiveOAuth2AuthorizedClientService? = null\n    var authorizedClientRepository: ServerOAuth2AuthorizedClientRepository? = null\n    var authorizationRequestRepository: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest>? = null\n    var authorizationRequestResolver: ServerOAuth2AuthorizationRequestResolver? = null\n    var authorizationRedirectStrategy: ServerRedirectStrategy? = null\n    var authenticationMatcher: ServerWebExchangeMatcher? = null\n    var loginPage: String? = null\n    var oidcSessionRegistry: ReactiveOidcSessionRegistry? = null\n\n    internal fun get(): (ServerHttpSecurity.OAuth2LoginSpec) -> Unit {\n        return { oauth2Login ->\n            authenticationManager?.also { oauth2Login.authenticationManager(authenticationManager) }\n            securityContextRepository?.also { oauth2Login.securityContextRepository(securityContextRepository) }\n            authenticationSuccessHandler?.also { oauth2Login.authenticationSuccessHandler(authenticationSuccessHandler) }\n            authenticationFailureHandler?.also { oauth2Login.authenticationFailureHandler(authenticationFailureHandler) }\n            authenticationConverter?.also { oauth2Login.authenticationConverter(authenticationConverter) }\n            clientRegistrationRepository?.also { oauth2Login.clientRegistrationRepository(clientRegistrationRepository) }\n            authorizedClientService?.also { oauth2Login.authorizedClientService(authorizedClientService) }\n            authorizedClientRepository?.also { oauth2Login.authorizedClientRepository(authorizedClientRepository) }\n            authorizationRequestRepository?.also { oauth2Login.authorizationRequestRepository(authorizationRequestRepository) }\n            authorizationRequestResolver?.also { oauth2Login.authorizationRequestResolver(authorizationRequestResolver) }\n            authorizationRedirectStrategy?.also { oauth2Login.authorizationRedirectStrategy(authorizationRedirectStrategy) }\n            authenticationMatcher?.also { oauth2Login.authenticationMatcher(authenticationMatcher) }\n            loginPage?.also { oauth2Login.loginPage(loginPage) }\n            oidcSessionRegistry?.also { oauth2Login.oidcSessionRegistry(oidcSessionRegistry) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerOAuth2ResourceServerDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler\nimport org.springframework.security.web.server.authorization.ServerAccessDeniedHandler\nimport org.springframework.web.server.ServerWebExchange\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] OAuth 2.0 resource server using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property accessDeniedHandler the [ServerAccessDeniedHandler] to use for requests authenticating with\n * Bearer Tokens.\n * @property authenticationEntryPoint the [ServerAuthenticationEntryPoint] to use for requests authenticating with\n * Bearer Tokens.\n * @property bearerTokenConverter the [ServerAuthenticationConverter] to use for requests authenticating with\n * Bearer Tokens.\n * @property authenticationManagerResolver the [ReactiveAuthenticationManagerResolver] to use.\n */\n@ServerSecurityMarker\nclass ServerOAuth2ResourceServerDsl {\n    var accessDeniedHandler: ServerAccessDeniedHandler? = null\n    var authenticationFailureHandler: ServerAuthenticationFailureHandler? = null\n    var authenticationEntryPoint: ServerAuthenticationEntryPoint? = null\n    var bearerTokenConverter: ServerAuthenticationConverter? = null\n    var authenticationManagerResolver: ReactiveAuthenticationManagerResolver<ServerWebExchange>? = null\n\n    private var jwt: ((ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec) -> Unit)? = null\n    private var opaqueToken: ((ServerHttpSecurity.OAuth2ResourceServerSpec.OpaqueTokenSpec) -> Unit)? = null\n\n    /**\n     * Enables JWT-encoded bearer token support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          oauth2ResourceServer {\n     *              jwt {\n     *                  jwkSetUri = \"https://example.com/oauth2/jwk\"\n     *              }\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param jwtConfig custom configurations to configure JWT resource server support\n     * @see [ServerJwtDsl]\n     */\n    fun jwt(jwtConfig: ServerJwtDsl.() -> Unit) {\n        this.jwt = ServerJwtDsl().apply(jwtConfig).get()\n    }\n\n    /**\n     * Enables opaque token support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          oauth2ResourceServer {\n     *              opaqueToken {\n     *                  introspectionUri = \"https://example.com/introspect\"\n     *                  introspectionClientCredentials(\"client\", \"secret\")\n     *              }\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param opaqueTokenConfig custom configurations to configure JWT resource server support\n     * @see [ServerOpaqueTokenDsl]\n     */\n    fun opaqueToken(opaqueTokenConfig: ServerOpaqueTokenDsl.() -> Unit) {\n        this.opaqueToken = ServerOpaqueTokenDsl().apply(opaqueTokenConfig).get()\n    }\n\n    internal fun get(): (ServerHttpSecurity.OAuth2ResourceServerSpec) -> Unit {\n        return { oauth2ResourceServer ->\n            accessDeniedHandler?.also { oauth2ResourceServer.accessDeniedHandler(accessDeniedHandler) }\n            authenticationFailureHandler?.also { oauth2ResourceServer.authenticationFailureHandler(authenticationFailureHandler) }\n            authenticationEntryPoint?.also { oauth2ResourceServer.authenticationEntryPoint(authenticationEntryPoint) }\n            bearerTokenConverter?.also { oauth2ResourceServer.bearerTokenConverter(bearerTokenConverter) }\n            authenticationManagerResolver?.also { oauth2ResourceServer.authenticationManagerResolver(authenticationManagerResolver!!) }\n            jwt?.also { oauth2ResourceServer.jwt(jwt) }\n            opaqueToken?.also { oauth2ResourceServer.opaqueToken(opaqueToken) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcBackChannelLogoutDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] OIDC 1.0 Back-Channel Logout support using idiomatic Kotlin code.\n *\n * @author Josh Cummings\n * @since 6.2\n */\n@ServerSecurityMarker\nclass ServerOidcBackChannelLogoutDsl {\n    private var _logoutUri: String? = null\n    private var _logoutHandler: ServerLogoutHandler? = null\n\n    var logoutHandler: ServerLogoutHandler?\n        get() = _logoutHandler\n        set(value) {\n            _logoutHandler = value\n            _logoutUri = null\n        }\n    var logoutUri: String?\n        get() = _logoutUri\n        set(value) {\n            _logoutUri = value\n            _logoutHandler = null\n        }\n\n    internal fun get(): (ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer) -> Unit {\n        return { backChannel ->\n            logoutHandler?.also { backChannel.logoutHandler(logoutHandler) }\n            logoutUri?.also { backChannel.logoutUri(logoutUri) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] OIDC 1.0 login using idiomatic Kotlin code.\n *\n * @author Josh Cummings\n * @since 6.2\n */\n@ServerSecurityMarker\nclass ServerOidcLogoutDsl {\n    var clientRegistrationRepository: ReactiveClientRegistrationRepository? = null\n    var oidcSessionRegistry: ReactiveOidcSessionRegistry? = null\n\n    private var backChannel: ((ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer) -> Unit)? = null\n\n    /**\n     * Enables OIDC 1.0 Back-Channel Logout support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * class SecurityConfig {\n     *\n     *  @Bean\n     *  fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          oauth2Login { }\n     *          oidcLogout {\n     *              backChannel {\n     *                  sessionLogout { }\n     *              }\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param backChannelConfig custom configurations to configure OIDC 1.0 Back-Channel Logout support\n     * @see [ServerOidcBackChannelLogoutDsl]\n     */\n    fun backChannel(backChannelConfig: ServerOidcBackChannelLogoutDsl.() -> Unit) {\n        this.backChannel = ServerOidcBackChannelLogoutDsl().apply(backChannelConfig).get()\n    }\n\n    internal fun get(): (ServerHttpSecurity.OidcLogoutSpec) -> Unit {\n        return { oidcLogout ->\n            clientRegistrationRepository?.also { oidcLogout.clientRegistrationRepository(clientRegistrationRepository) }\n            oidcSessionRegistry?.also { oidcLogout.oidcSessionRegistry(oidcSessionRegistry) }\n            backChannel?.also { oidcLogout.backChannel(backChannel) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerOneTimeTokenLoginDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService\nimport org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler\nimport org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler\nimport org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] form login using idiomatic Kotlin code.\n *\n * @author Max Batischev\n * @since 6.4\n * @property tokenService configures the [ReactiveOneTimeTokenService] used to generate and consume\n * @property authenticationManager configures the [ReactiveAuthenticationManager] used to generate and consume\n * @property authenticationConverter Use this [ServerAuthenticationConverter] when converting incoming requests to an authentication\n * @property authenticationFailureHandler the [ServerAuthenticationFailureHandler] to use when authentication\n * @property authenticationSuccessHandler the [ServerAuthenticationSuccessHandler] to be used\n * @property generateRequestResolver the [ServerGenerateOneTimeTokenRequestResolver] to be used\n * @property defaultSubmitPageUrl sets the URL that the default submit page will be generated\n * @property showDefaultSubmitPage configures whether the default one-time token submit page should be shown\n * @property loginProcessingUrl the URL to process the login request\n * @property tokenGeneratingUrl the URL that a One-Time Token generate request will be processed\n * @property tokenGenerationSuccessHandler the strategy to be used to handle generated one-time tokens\n * @property securityContextRepository the [ServerSecurityContextRepository] used to save the [Authentication]. For the [SecurityContext] to be loaded on subsequent requests the [ReactorContextWebFilter] must be configured to be able to load the value (they are not implicitly linked).\n */\n@ServerSecurityMarker\nclass ServerOneTimeTokenLoginDsl {\n    var authenticationManager: ReactiveAuthenticationManager? = null\n    var tokenService: ReactiveOneTimeTokenService? = null\n    var authenticationConverter: ServerAuthenticationConverter? = null\n    var authenticationFailureHandler: ServerAuthenticationFailureHandler? = null\n    var authenticationSuccessHandler: ServerAuthenticationSuccessHandler? = null\n    var tokenGenerationSuccessHandler: ServerOneTimeTokenGenerationSuccessHandler? = null\n    var securityContextRepository: ServerSecurityContextRepository? = null\n    var generateRequestResolver: ServerGenerateOneTimeTokenRequestResolver? = null\n    var defaultSubmitPageUrl: String? = null\n    var loginProcessingUrl: String? = null\n    var tokenGeneratingUrl: String? = null\n    var showDefaultSubmitPage: Boolean? = true\n\n    internal fun get(): (ServerHttpSecurity.OneTimeTokenLoginSpec) -> Unit {\n        return { oneTimeTokenLogin ->\n            authenticationManager?.also { oneTimeTokenLogin.authenticationManager(authenticationManager) }\n            tokenService?.also { oneTimeTokenLogin.tokenService(tokenService) }\n            authenticationConverter?.also { oneTimeTokenLogin.authenticationConverter(authenticationConverter) }\n            authenticationFailureHandler?.also {\n                oneTimeTokenLogin.authenticationFailureHandler(\n                    authenticationFailureHandler\n                )\n            }\n            authenticationSuccessHandler?.also {\n                oneTimeTokenLogin.authenticationSuccessHandler(\n                    authenticationSuccessHandler\n                )\n            }\n            securityContextRepository?.also { oneTimeTokenLogin.securityContextRepository(securityContextRepository) }\n            generateRequestResolver?.also { oneTimeTokenLogin.generateRequestResolver(generateRequestResolver) }\n            defaultSubmitPageUrl?.also { oneTimeTokenLogin.defaultSubmitPageUrl(defaultSubmitPageUrl) }\n            showDefaultSubmitPage?.also { oneTimeTokenLogin.showDefaultSubmitPage(showDefaultSubmitPage!!) }\n            loginProcessingUrl?.also { oneTimeTokenLogin.loginProcessingUrl(loginProcessingUrl) }\n            tokenGeneratingUrl?.also { oneTimeTokenLogin.tokenGeneratingUrl(tokenGeneratingUrl) }\n            tokenGenerationSuccessHandler?.also {\n                oneTimeTokenLogin.tokenGenerationSuccessHandler(\n                    tokenGenerationSuccessHandler\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerOpaqueTokenDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] Opaque Token Resource Server support using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property introspectionUri the URI of the Introspection endpoint.\n * @property introspector the [ReactiveOpaqueTokenIntrospector] to use.\n */\n@ServerSecurityMarker\nclass ServerOpaqueTokenDsl {\n    private var _introspectionUri: String? = null\n    private var _introspector: ReactiveOpaqueTokenIntrospector? = null\n    private var clientCredentials: Pair<String, String>? = null\n\n    var introspectionUri: String?\n        get() = _introspectionUri\n        set(value) {\n            _introspectionUri = value\n            _introspector = null\n        }\n    var introspector: ReactiveOpaqueTokenIntrospector?\n        get() = _introspector\n        set(value) {\n            _introspector = value\n            _introspectionUri = null\n            clientCredentials = null\n        }\n    var authenticationConverter: ReactiveOpaqueTokenAuthenticationConverter? = null\n\n    /**\n     * Configures the credentials for Introspection endpoint.\n     *\n     * @param clientId the clientId part of the credentials.\n     * @param clientSecret the clientSecret part of the credentials.\n     */\n    fun introspectionClientCredentials(clientId: String, clientSecret: String) {\n        clientCredentials = Pair(clientId, clientSecret)\n        _introspector = null\n    }\n\n    internal fun get(): (ServerHttpSecurity.OAuth2ResourceServerSpec.OpaqueTokenSpec) -> Unit {\n        return { opaqueToken ->\n            introspectionUri?.also { opaqueToken.introspectionUri(introspectionUri) }\n            clientCredentials?.also { opaqueToken.introspectionClientCredentials(clientCredentials!!.first, clientCredentials!!.second) }\n            introspector?.also { opaqueToken.introspector(introspector) }\n            authenticationConverter?.also { opaqueToken.authenticationConverter(authenticationConverter) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerPasswordManagementDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] password management\n * using idiomatic Kotlin code.\n *\n * @author Evgeniy Cheban\n * @property changePasswordPage the change password page.\n * @since 5.6\n */\n@ServerSecurityMarker\nclass ServerPasswordManagementDsl {\n    var changePasswordPage: String? = null\n\n    internal fun get(): (ServerHttpSecurity.PasswordManagementSpec) -> Unit {\n        return { passwordManagement ->\n            changePasswordPage?.also { passwordManagement.changePasswordPage(changePasswordPage) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerPermissionsPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] permissions policy header using\n * idiomatic Kotlin code.\n *\n * @author Christophe Gilles\n * @since 5.5\n * @property policy the policy to be used in the response header.\n */\n@ServerSecurityMarker\nclass ServerPermissionsPolicyDsl {\n    var policy: String? = null\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit {\n        return { permissionsPolicy ->\n            policy?.also {\n                permissionsPolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerReferrerPolicyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] referrer policy header using\n * idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property policy the policy to be used in the response header.\n */\n@ServerSecurityMarker\nclass ServerReferrerPolicyDsl {\n    var policy: ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy? = null\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.ReferrerPolicySpec) -> Unit {\n        return { referrerPolicy ->\n            policy?.also {\n                referrerPolicy.policy(policy)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerRequestCacheDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache\n\n/**\n * A Kotlin DSL to configure the request cache using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property requestCache allows explicit configuration of the [ServerRequestCache] to be used.\n */\n@ServerSecurityMarker\nclass ServerRequestCacheDsl {\n    var requestCache: ServerRequestCache? = null\n\n    private var disabled = false\n\n    /**\n     * Disables the request cache.\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.RequestCacheSpec) -> Unit {\n        return { requestCacheConfig ->\n            requestCache?.also {\n                requestCacheConfig.requestCache(requestCache)\n                if (disabled) {\n                    requestCacheConfig.disable()\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerSecurityMarker.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\n/**\n * Marker annotation indicating that the annotated class is part of the security DSL for server configuration.\n *\n * @author Loïc Labagnara\n * @since 5.4\n */\n@DslMarker\nannotation class ServerSecurityMarker\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerSessionConcurrencyDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.core.session.ReactiveSessionRegistry\nimport org.springframework.security.web.server.authentication.ServerMaximumSessionsExceededHandler\nimport org.springframework.security.web.server.authentication.SessionLimit\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] Session Concurrency support using idiomatic Kotlin code.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\n@ServerSecurityMarker\nclass ServerSessionConcurrencyDsl {\n    var maximumSessions: SessionLimit? = null\n    var maximumSessionsExceededHandler: ServerMaximumSessionsExceededHandler? = null\n    var sessionRegistry: ReactiveSessionRegistry? = null\n\n    internal fun get(): (ServerHttpSecurity.SessionManagementSpec.ConcurrentSessionsSpec) -> Unit {\n        return { sessionConcurrency ->\n            maximumSessions?.also {\n                sessionConcurrency.maximumSessions(maximumSessions!!)\n            }\n            maximumSessionsExceededHandler?.also {\n                sessionConcurrency.maximumSessionsExceededHandler(maximumSessionsExceededHandler!!)\n            }\n            sessionRegistry?.also {\n                sessionConcurrency.sessionRegistry(sessionRegistry!!)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerSessionManagementDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] Session Management using idiomatic Kotlin code.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\n@ServerSecurityMarker\nclass ServerSessionManagementDsl {\n    private var sessionConcurrency: ((ServerHttpSecurity.SessionManagementSpec.ConcurrentSessionsSpec) -> Unit)? = null\n\n    /**\n     * Enables Session Management support.\n     *\n     * Example:\n     *\n     * ```\n     * @Configuration\n     * @EnableWebFluxSecurity\n     * open class SecurityConfig {\n     *\n     *  @Bean\n     *  open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n     *      return http {\n     *          sessionManagement {\n     *              sessionConcurrency {\n     *                  maximumSessions = { authentication -> Mono.just(1) }\n     *              }\n     *          }\n     *       }\n     *   }\n     * }\n     * ```\n     *\n     * @param backChannelConfig custom configurations to configure OIDC 1.0 Back-Channel Logout support\n     * @see [ServerOidcBackChannelLogoutDsl]\n     */\n    fun sessionConcurrency(sessionConcurrencyConfig: ServerSessionConcurrencyDsl.() -> Unit) {\n        this.sessionConcurrency = ServerSessionConcurrencyDsl().apply(sessionConcurrencyConfig).get()\n    }\n\n    internal fun get(): (ServerHttpSecurity.SessionManagementSpec) -> Unit {\n        return { sessionManagement ->\n            sessionConcurrency?.also { sessionManagement.concurrentSessions(sessionConcurrency) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerX509Dsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor\n\n/**\n * A Kotlin DSL to configure [ServerHttpSecurity] X509 based pre authentication using idiomatic Kotlin code.\n *\n * @author Eleftheria Stein\n * @since 5.4\n * @property principalExtractor the [X509PrincipalExtractor] used to obtain the principal for use within the framework.\n * @property authenticationManager the [ReactiveAuthenticationManager] used to determine if the provided\n * [Authentication] can be authenticated.\n */\n@ServerSecurityMarker\nclass ServerX509Dsl {\n    var principalExtractor: X509PrincipalExtractor? = null\n    var authenticationManager: ReactiveAuthenticationManager? = null\n\n    internal fun get(): (ServerHttpSecurity.X509Spec) -> Unit {\n        return { x509 ->\n            authenticationManager?.also { x509.authenticationManager(authenticationManager) }\n            principalExtractor?.also { x509.principalExtractor(principalExtractor) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/kotlin/org/springframework/security/config/web/server/ServerXssProtectionDsl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter.HeaderValue\n\n/**\n * A Kotlin DSL to configure the [ServerHttpSecurity] XSS protection header using\n * idiomatic Kotlin code.\n *\n * @property headerValue the value of the X-XSS-Protection header. OWASP recommends [HeaderValue.DISABLED].\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\n@ServerSecurityMarker\nclass ServerXssProtectionDsl {\n    private var disabled = false\n    var headerValue: HeaderValue? = null\n\n    /**\n     * Disables cache control response headers\n     */\n    fun disable() {\n        disabled = true\n    }\n\n    internal fun get(): (ServerHttpSecurity.HeaderSpec.XssProtectionSpec) -> Unit {\n        return { xss ->\n            headerValue?.also { xss.headerValue(headerValue) }\n            if (disabled) {\n                xss.disable()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\\\norg.springframework.security.config.annotation.authentication.configuration.AuthenticationManagerBeanRegistrationAotProcessor\n\norg.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.config.aot.hint.OAuth2LoginRuntimeHints,\\\norg.springframework.security.config.aot.hint.WebMvcSecurityConfigurationRuntimeHints, \\\norg.springframework.security.config.aot.hint.WebSecurityConfigurationRuntimeHints\n"
  },
  {
    "path": "config/src/main/resources/META-INF/spring.handlers",
    "content": "http\\://www.springframework.org/schema/security=org.springframework.security.config.SecurityNamespaceHandler\n"
  },
  {
    "path": "config/src/main/resources/META-INF/spring.schemas",
    "content": "#\n# Copyright 2004-present the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nhttp\\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-7.1.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-7.0.xsd=org/springframework/security/config/spring-security-7.1.xsd\nhttp\\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-7.0.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-7.0.xsd=org/springframework/security/config/spring-security-7.0.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-6.5.xsd=org/springframework/security/config/spring-security-6.5.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-6.2.xsd=org/springframework/security/config/spring-security-6.2.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-6.1.xsd=org/springframework/security/config/spring-security-6.1.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-6.0.xsd=org/springframework/security/config/spring-security-6.0.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.8.xsd=org/springframework/security/config/spring-security-5.8.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.7.xsd=org/springframework/security/config/spring-security-5.7.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.6.xsd=org/springframework/security/config/spring-security-5.6.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.5.xsd=org/springframework/security/config/spring-security-5.5.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.4.xsd=org/springframework/security/config/spring-security-5.4.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.3.xsd=org/springframework/security/config/spring-security-5.3.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.2.xsd=org/springframework/security/config/spring-security-5.2.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.1.xsd=org/springframework/security/config/spring-security-5.1.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-5.0.xsd=org/springframework/security/config/spring-security-5.0.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-4.2.xsd=org/springframework/security/config/spring-security-4.2.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-4.1.xsd=org/springframework/security/config/spring-security-4.1.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-4.0.xsd=org/springframework/security/config/spring-security-4.0.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-3.2.xsd=org/springframework/security/config/spring-security-3.2.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-3.1.xsd=org/springframework/security/config/spring-security-3.1.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-3.0.3.xsd=org/springframework/security/config/spring-security-3.0.3.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-3.0.xsd=org/springframework/security/config/spring-security-3.0.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/springframework/security/config/spring-security-2.0.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd\nhttp\\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd\nhttps\\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-7.1.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-7.1.xsd=org/springframework/security/config/spring-security-7.1.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-7.0.xsd=org/springframework/security/config/spring-security-7.0.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-6.5.xsd=org/springframework/security/config/spring-security-6.5.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-6.4.xsd=org/springframework/security/config/spring-security-6.4.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-6.3.xsd=org/springframework/security/config/spring-security-6.3.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-6.2.xsd=org/springframework/security/config/spring-security-6.2.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-6.1.xsd=org/springframework/security/config/spring-security-6.1.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-6.0.xsd=org/springframework/security/config/spring-security-6.0.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.8.xsd=org/springframework/security/config/spring-security-5.8.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.7.xsd=org/springframework/security/config/spring-security-5.7.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.6.xsd=org/springframework/security/config/spring-security-5.6.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.5.xsd=org/springframework/security/config/spring-security-5.5.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.4.xsd=org/springframework/security/config/spring-security-5.4.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.3.xsd=org/springframework/security/config/spring-security-5.3.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.2.xsd=org/springframework/security/config/spring-security-5.2.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.1.xsd=org/springframework/security/config/spring-security-5.1.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-5.0.xsd=org/springframework/security/config/spring-security-5.0.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-4.2.xsd=org/springframework/security/config/spring-security-4.2.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-4.1.xsd=org/springframework/security/config/spring-security-4.1.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-4.0.xsd=org/springframework/security/config/spring-security-4.0.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-3.2.xsd=org/springframework/security/config/spring-security-3.2.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-3.1.xsd=org/springframework/security/config/spring-security-3.1.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-3.0.3.xsd=org/springframework/security/config/spring-security-3.0.3.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-3.0.xsd=org/springframework/security/config/spring-security-3.0.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/springframework/security/config/spring-security-2.0.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd\nhttps\\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/catalog.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE catalog PUBLIC \"-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN\" \"https://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd\">\n<catalog xmlns=\"urn:oasis:names:tc:entity:xmlns:xml:catalog\">\n\t<system systemId=\"https://www.springframework.org/schema/security/spring-security-2.0.xsd\" uri=\"spring-security-2.0.xsd\"/>\n</catalog>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-2.0.1.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n  xmlns:security=\"http://www.springframework.org/schema/security\" elementFormDefault=\"qualified\"\n  targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n    <xs:attribute name=\"hash\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n    <xs:attribute name=\"base64\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"path-type\">\n    <xs:attribute name=\"path-type\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n    <xs:attribute name=\"port\" use=\"required\" type=\"xs:integer\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server,\n          for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n    <xs:attribute name=\"url\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n    <xs:attribute name=\"id\" use=\"required\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n    <xs:attribute name=\"ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n    <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n    <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"password-encoder.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"base64\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-property\">\n    <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A property of the UserDetails object which will be used as salt by a\n          password encoder. Typically something like \"username\" might be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n    <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A single value that will be used as the salt for a password encoder.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"boolean\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"true\"/>\n      <xs:enumeration value=\"false\"/>\n    </xs:restriction>\n  </xs:simpleType>\n  <xs:attributeGroup name=\"role-prefix\">\n    <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\").</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n    <xs:annotation>\n      <xs:documentation>Defines an LDAP server location or starts an embedded server. The url\n        indicates the location of a remote server. If no url is given, an embedded server will be\n        started, listening on the supplied port number. The port is optional and defaults to 33389.\n        A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n      </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"port\" type=\"xs:integer\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server,\n          for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to\n          authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-password\" type=\"xs:string\"/>\n    <xs:attribute name=\"ldif\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP\n          server</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"root\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional root suffix for the embedded LDAP server. Default is\n          \"dc=springframework,dc=org\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n    <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n    <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n    <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n    <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n    <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n    <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n    <xs:attribute name=\"user-details-class\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"server-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:string\"/>\n    <xs:attribute name=\"user-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\").</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Sets up an ldap authentication provider</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"password-compare\">\n          <xs:annotation>\n            <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation\n              of the user's password to authenticate the user</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                <xs:annotation>\n                  <xs:documentation>element which defines a password encoding strategy. Used by an\n                    authentication provider to convert submitted passwords to hashed versions, for\n                    example.</xs:documentation>\n                </xs:annotation>\n                <xs:complexType>\n                  <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"salt-source\">\n                      <xs:complexType>\n                        <xs:attribute name=\"user-property\" type=\"xs:string\">\n                          <xs:annotation>\n                            <xs:documentation>A property of the UserDetails object which will be\n                              used as salt by a password encoder. Typically something like\n                              \"username\" might be used. </xs:documentation>\n                          </xs:annotation>\n                        </xs:attribute>\n                        <xs:attribute name=\"system-wide\" type=\"xs:string\">\n                          <xs:annotation>\n                            <xs:documentation>A single value that will be used as the salt for a\n                              password encoder. </xs:documentation>\n                          </xs:annotation>\n                        </xs:attribute>\n                      </xs:complexType>\n                    </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                </xs:complexType>\n              </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n    <xs:attribute name=\"server-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:string\"/>\n    <xs:attribute name=\"group-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-dn-pattern\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A specific pattern used to build the user's DN, for example\n          \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the\n          username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\").</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"password-compare.attlist\">\n    <xs:attribute name=\"password-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The attribute in the directory which contains the user password. Defaults\n          to \"userPassword\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n    <xs:annotation>\n      <xs:documentation>Can be used inside a bean definition to add a security interceptor to the\n        bean and set up access configuration attributes for the bean's methods</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" ref=\"security:protect\"/>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method\n          security interceptor.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"protect\">\n    <xs:annotation>\n      <xs:documentation>Defines a protected method and the access control configuration attributes\n        that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services\n        provided \"global-method-security\".</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:protect.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"protect.attlist\">\n    <xs:attribute name=\"method\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A method name</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to the method, e.g.\n          \"ROLE_A,ROLE_B\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n    <xs:annotation>\n      <xs:documentation>Provides method security for all beans registered in the Spring application\n        context. Specifically, beans will be scanned for Spring Security annotations and/or matches\n        with the ordered list of \"protect-pointcut\" sub-elements. Where there is a match, the beans\n        will automatically be proxied and security authorization applied to the methods accordingly.\n        If you use and enable all three sources of method security metadata (ie \"protect-pointcut\"\n        declarations, @Secured and also JSR 250 security annotations), the metadata sources will be\n        queried in that order. In practical terms, this enables you to use XML to override method\n        security metadata expressed by way of @Secured annotations, with @Secured annotations\n        overriding method security metadata expressed by JSR 250 annotations. It is perfectly\n        acceptable to mix and match, with a given Java type using a combination of XML, @Secured and\n        JSR 250 to express method security metadata (albeit on different\n      methods).</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n          <xs:annotation>\n            <xs:documentation>Defines a protected pointcut and the access control configuration\n              attributes that apply to it. Every bean registered in the Spring application context\n              that provides a method that matches the pointcut will receive security\n            authorization.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n    <xs:attribute name=\"secured-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should\n          be enabled for this application context. Please ensure you have the\n          spring-security-tiger-xxx.jar on the classpath. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"jsr250-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example\n          \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath.\n          Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for\n          method security.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"custom-after-invocation-provider\">\n    <xs:complexType/>\n  </xs:element>\n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n    <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example,\n          'execution(int com.foo.TargetObject.countLength(String))' (without the\n        quotes).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to all methods matching\n          the pointcut, e.g. \"ROLE_A,ROLE_B\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http\">\n    <xs:annotation>\n      <xs:documentation>Container element for HTTP security configuration</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"intercept-url\">\n          <xs:annotation>\n            <xs:documentation>Specifies the access attributes and/or filter list for a particular\n              set of URLs.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"form-login\">\n          <xs:annotation>\n            <xs:documentation>Sets up a form login configuration for authentication with a username\n              and password</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element ref=\"security:openid-login\"/>\n        <xs:element name=\"x509\">\n          <xs:annotation>\n            <xs:documentation>Adds support for X.509 client authentication.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:x509.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"http-basic\">\n          <xs:annotation>\n            <xs:documentation>Adds support for basic authentication (this is an element to permit\n              future expansion, such as supporting an \"ignoreFailure\" attribute)</xs:documentation>\n          </xs:annotation>\n          <xs:complexType/>\n        </xs:element>\n        <xs:element name=\"logout\">\n          <xs:annotation>\n            <xs:documentation>Incorporates a logout processing filter. Most web applications require\n              a logout filter, although you may not require one if you write a controller to\n              provider similar logic.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:logout.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"concurrent-session-control\">\n          <xs:annotation>\n            <xs:documentation>Adds support for concurrent session control, allowing limits to be\n              placed on the number of sessions a user can have.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:concurrent-sessions.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"remember-me\">\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"anonymous\">\n          <xs:annotation>\n            <xs:documentation>Adds support for automatically granting all anonymous web requests a\n              particular principal identity and a corresponding granted\n            authority.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"port-mappings\">\n          <xs:annotation>\n            <xs:documentation>Defines the list of mappings between http and https ports for use in\n              redirects</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element maxOccurs=\"unbounded\" ref=\"security:port-mapping\"/>\n            </xs:sequence>\n          </xs:complexType>\n        </xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:http.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n    <xs:attribute name=\"auto-config\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Automatically registers a login form, BASIC authentication, anonymous\n          authentication, logout services, remember-me and servlet-api-integration. If set to\n          \"true\", all of these capabilities are added (although you can still customize the\n          configuration of each by providing the respective element). If unspecified, defaults to\n          \"false\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"create-session\">\n      <xs:annotation>\n        <xs:documentation>Controls the eagerness with which an HTTP session is created. If not set,\n          defaults to \"ifRequired\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ifRequired\"/>\n          <xs:enumeration value=\"always\"/>\n          <xs:enumeration value=\"never\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether test URLs should be converted to lower case prior to comparing\n          with defined path patterns. If unspecified, defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"servlet-api-provision\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Provides versions of HttpServletRequest security methods such as\n          isUserInRole() and getPrincipal() which are implemented by accessing the Spring\n          SecurityContext. Defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager\n          implementation which should be used for authorizing HTTP requests.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"realm\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the realm name that will be used for all\n          authentication features that require a realm name (eg BASIC and Digest authentication). If\n          unspecified, defaults to \"Spring Security Application\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-fixation-protection\">\n      <xs:annotation>\n        <xs:documentation>Indicates whether an existing session should be invalidated when a user\n          authenticates and a new session started. If set to \"none\" no change will be made.\n          \"newSession\" will create a new empty session. \"migrateSession\" will create a new session\n          and copy the session attributes to the new session. Defaults to\n        \"migrateSession\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n          <xs:enumeration value=\"newSession\"/>\n          <xs:enumeration value=\"migrateSession\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"entry-point-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows a customized AuthenticationEntryPoint to be\n        used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"once-per-request\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Corresponds to the observeOncePerRequest property of\n          FilterSecurityInterceptor. Defaults to \"true\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-denied-page\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows the access denied page to be set (the user will be redirected here\n          if an AccessDeniedException is raised).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"intercept-url.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The pattern which defines the URL path. The content will depend on the\n          type set in the containing http element, so will default to ant path\n        syntax.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The access configuration attributes that apply for the configured\n        path.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"method\">\n      <xs:annotation>\n        <xs:documentation>The HTTP Method for which the access configuration attributes should\n          apply. If not specified, the attributes will apply to any method.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"GET\"/>\n          <xs:enumeration value=\"DELETE\"/>\n          <xs:enumeration value=\"HEAD\"/>\n          <xs:enumeration value=\"OPTIONS\"/>\n          <xs:enumeration value=\"POST\"/>\n          <xs:enumeration value=\"PUT\"/>\n          <xs:enumeration value=\"TRACE\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"filters\">\n      <xs:annotation>\n        <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a\n          path from having any filters applied. The full filter stack (consisting of all defined\n          filters, will be applied to any other paths).</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"requires-channel\">\n      <xs:annotation>\n        <xs:documentation>Used to specify that a URL must be accessed over http or\n        https</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"http\"/>\n          <xs:enumeration value=\"https\"/>\n          <xs:enumeration value=\"any\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"logout.attlist\">\n    <xs:attribute name=\"logout-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL that will cause a logout. Spring Security will\n          initialize a filter that responds to this particular URL. Defaults to\n          /logout if unspecified.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"logout-success-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL to display once the user has logged out. If not\n          specified, defaults to /.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"invalidate-session\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is\n          generally desirable. If unspecified, defaults to true.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"form-login.attlist\">\n    <xs:attribute name=\"login-processing-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to\n          /login.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"default-target-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL that will be redirected to after successful authentication, if the\n          user's previous action could not be resumed. This generally happens if the user visits a\n          login page without having first requested a secured operation that triggers\n          authentication. If unspecified, defaults to the root of the\n        application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"always-use-default-target\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether the user should always be redirected to the default-target-url\n          after login. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"login-page\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security\n          will automatically create a login URL at /spring_security_login and a corresponding filter\n          to render that login URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-failure-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login failure page. If no login failure URL is specified,\n          Spring Security will automatically create a failure login URL at\n          /spring_security_login?login_error and a corresponding filter to render that login failure\n          URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-login\">\n    <xs:annotation>\n      <xs:documentation>Sets up form login for authentication with an Open ID\n      identity</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n        <xs:annotation>\n          <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n          Id</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n  <xs:element name=\"filter-chain-map\">\n    <xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a\n        FilterChainMap</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"filter-chain\">\n          <xs:annotation>\n            <xs:documentation>Used within filter-chain-map to define a specific URL pattern and the\n              list of filters which apply to the URLs matching that pattern. When multiple\n              filter-chain elements are used within a filter-chain-map element, the most specific\n              patterns must be placed at the top of the list, with most general ones at the\n            bottom.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n    <xs:attributeGroup ref=\"security:path-type\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:string\"/>\n    <xs:attribute name=\"filters\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-invocation-definition-source\">\n    <xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterInvocationDefinitionSource bean for use\n        with a FilterSecurityInterceptor. Usually only needed if you are configuring a\n        FilterChainProxy explicitly, rather than using the &lt;http&gt; element. The\n        intercept-url elements used should only contain pattern, method and access attributes. Any\n        others will result in a configuration error. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n          <xs:annotation>\n            <xs:documentation>Specifies the access attributes and/or filter list for a particular\n              set of URLs.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:fids.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"fids.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>as for http element</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"concurrent-sessions.attlist\">\n    <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\"/>\n    <xs:attribute name=\"expired-url\" type=\"xs:string\"/>\n    <xs:attribute name=\"exception-if-maximum-exceeded\" type=\"security:boolean\"/>\n    <xs:attribute name=\"session-registry-alias\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to\n          access it in your own configuration</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:string\"/>\n    <xs:attribute name=\"token-repository-ref\" type=\"xs:string\"/>\n    <xs:attribute name=\"data-source-ref\" type=\"xs:string\"/>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"anonymous.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The key shared between the provider and filter. This generally does not\n          need to be set. If unset, it will default to \"doesNotMatter\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"username\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The username that should be assigned to the anonymous request. This allows\n          the principal to be identified, which may be important for logging and auditing. if unset,\n          defaults to \"anonymousUser\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"granted-authority\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The granted authority that should be assigned to the anonymous request.\n          Commonly this is used to assign the anonymous request particular roles, which can\n          subsequently be used in authorization decisions. If unset, defaults to\n        \"ROLE_ANONYMOUS\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"port-mapping\">\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:http-port\"/>\n      <xs:attributeGroup ref=\"security:https-port\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"http-port\">\n    <xs:attribute name=\"http\" use=\"required\" type=\"xs:integer\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n    <xs:attribute name=\"https\" use=\"required\" type=\"xs:integer\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"x509.attlist\">\n    <xs:attribute name=\"subject-principal-regex\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The regular expression used to obtain the username from the certificate's\n          subject. Defaults to matching on the common name using the pattern\n        \"CN=(.*?),\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n    <xs:annotation>\n      <xs:documentation>If you are using namespace configuration with Spring Security, an\n        AuthenticationManager will automatically be registered. This element simple allows you to\n        define an alias to allow you to reference the authentication-manager in your own beans.\n      </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:authman.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n    <xs:annotation>\n      <xs:documentation>The alias you wish to use for the AuthenticationManager\n      bean</xs:documentation>\n    </xs:annotation>\n    <xs:attribute name=\"alias\" use=\"required\" type=\"xs:ID\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Indicates that the contained user-service should be used as an\n        authentication source. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element ref=\"security:any-user-service\"/>\n        <xs:element name=\"password-encoder\">\n          <xs:annotation>\n            <xs:documentation>element which defines a password encoding strategy. Used by an\n              authentication provider to convert submitted passwords to hashed versions, for\n              example.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                <xs:complexType>\n                  <xs:attribute name=\"user-property\" type=\"xs:string\">\n                    <xs:annotation>\n                      <xs:documentation>A property of the UserDetails object which will be used as\n                        salt by a password encoder. Typically something like \"username\" might be\n                        used. </xs:documentation>\n                    </xs:annotation>\n                  </xs:attribute>\n                  <xs:attribute name=\"system-wide\" type=\"xs:string\">\n                    <xs:annotation>\n                      <xs:documentation>A single value that will be used as the salt for a password\n                        encoder. </xs:documentation>\n                    </xs:annotation>\n                  </xs:attribute>\n                </xs:complexType>\n              </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:ap.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ap.attlist\">\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"custom-authentication-provider\">\n    <xs:complexType/>\n  </xs:element>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:annotation>\n      <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of\n        \"user\" child elements.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:user\"/>\n      </xs:sequence>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n            context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:properties-file\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n    <xs:attribute name=\"properties\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"user\">\n    <xs:annotation>\n      <xs:documentation>Represents a user in the application.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:user.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"user.attlist\">\n    <xs:attribute name=\"name\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The username assigned to the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"password\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password assigned to the user. This may be hashed if the corresponding\n          authentication provider supports hashing (remember to set the \"hash\" attribute of the\n          \"user-service\" element).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>One of more authorities granted to the user. Separate authorities with a\n          comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"locked\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as locked and\n        unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"disabled\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as disabled and\n        unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:annotation>\n      <xs:documentation>Causes creation of a JDBC-based UserDetailsService.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n            context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The bean ID of the DataSource which provides the required\n        tables.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"users-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query a username, password, and enabled status given a\n          username</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query for a user's granted authorities given a\n          username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query user's group authorities given a\n        username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\").</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:group name=\"custom-filter\">\n    <xs:sequence>\n      <xs:element minOccurs=\"0\" ref=\"security:custom-filter\"/>\n    </xs:sequence>\n  </xs:group>\n  <xs:element name=\"custom-filter\">\n    <xs:annotation>\n      <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into\n        the security filter chain. If neither the 'after' or 'before' options are supplied, then the\n        filter must implement the Ordered interface directly. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The filter immediately after which the custom-filter should be placed in\n            the chain. This feature will only be needed by advanced users who wish to mix their own\n            filters into the security filter chain and have some knowledge of the standard Spring\n            Security filters. The filter names map to specific Spring Security implementation\n            filters. </xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The filter immediately before which the custom-filter should be placed\n            in the chain</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The explicit position at which the custom-filter should be placed in the\n            chain. Use if you are replacing a standard filter.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"after\">\n    <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately after which the custom-filter should be placed in\n          the chain. This feature will only be needed by advanced users who wish to mix their own\n          filters into the security filter chain and have some knowledge of the standard Spring\n          Security filters. The filter names map to specific Spring Security implementation filters.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n    <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately before which the custom-filter should be placed in\n          the chain</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n    <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The explicit position at which the custom-filter should be placed in the\n          chain. Use if you are replacing a standard filter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"FIRST\"/>\n      <xs:enumeration value=\"CHANNEL_FILTER\"/>\n      <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n      <xs:enumeration value=\"SESSION_CONTEXT_INTEGRATION_FILTER\"/>\n      <xs:enumeration value=\"LOGOUT_FILTER\"/>\n      <xs:enumeration value=\"X509_FILTER\"/>\n      <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n      <xs:enumeration value=\"CAS_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"AUTHENTICATION_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"BASIC_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n      <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n      <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n      <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n      <xs:enumeration value=\"NTLM_FILTER\"/>\n      <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n      <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n      <xs:enumeration value=\"LAST\"/>\n    </xs:restriction>\n  </xs:simpleType>\n</xs:schema>\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-2.0.2.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n  xmlns:security=\"http://www.springframework.org/schema/security\" elementFormDefault=\"qualified\"\n  targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n    <xs:attribute name=\"hash\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n    <xs:attribute name=\"base64\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"path-type\">\n    <xs:attribute name=\"path-type\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n    <xs:attribute name=\"port\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server,\n          for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n    <xs:attribute name=\"url\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n    <xs:attribute name=\"id\" use=\"required\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n    <xs:attribute name=\"ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n    <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n    <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a DataSource bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"password-encoder.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"base64\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-property\">\n    <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A property of the UserDetails object which will be used as salt by a\n          password encoder. Typically something like \"username\" might be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n    <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A single value that will be used as the salt for a password encoder.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"boolean\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"true\"/>\n      <xs:enumeration value=\"false\"/>\n    </xs:restriction>\n  </xs:simpleType>\n  <xs:attributeGroup name=\"role-prefix\">\n    <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the\n          default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n    <xs:annotation>\n      <xs:documentation>Defines an LDAP server location or starts an embedded server. The url\n        indicates the location of a remote server. If no url is given, an embedded server will be\n        started, listening on the supplied port number. The port is optional and defaults to 33389.\n        A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n      </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"port\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server,\n          for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to\n          authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-password\" type=\"xs:string\"/>\n    <xs:attribute name=\"ldif\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP\n          server</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"root\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional root suffix for the embedded LDAP server. Default is\n          \"dc=springframework,dc=org\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n    <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n    <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n    <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n    <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n    <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n    <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n    <xs:attribute name=\"user-details-class\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"server-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:string\"/>\n    <xs:attribute name=\"user-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the\n          default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Sets up an ldap authentication provider</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"password-compare\">\n          <xs:annotation>\n            <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation\n              of the user's password to authenticate the user</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                <xs:annotation>\n                  <xs:documentation>element which defines a password encoding strategy. Used by an\n                    authentication provider to convert submitted passwords to hashed versions, for\n                    example.</xs:documentation>\n                </xs:annotation>\n                <xs:complexType>\n                  <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"salt-source\">\n                      <xs:complexType>\n                        <xs:attribute name=\"user-property\" type=\"xs:string\">\n                          <xs:annotation>\n                            <xs:documentation>A property of the UserDetails object which will be\n                              used as salt by a password encoder. Typically something like\n                              \"username\" might be used. </xs:documentation>\n                          </xs:annotation>\n                        </xs:attribute>\n                        <xs:attribute name=\"system-wide\" type=\"xs:string\">\n                          <xs:annotation>\n                            <xs:documentation>A single value that will be used as the salt for a\n                              password encoder. </xs:documentation>\n                          </xs:annotation>\n                        </xs:attribute>\n                      </xs:complexType>\n                    </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                </xs:complexType>\n              </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n    <xs:attribute name=\"server-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:string\"/>\n    <xs:attribute name=\"group-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-dn-pattern\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A specific pattern used to build the user's DN, for example\n          \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the\n          username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the\n          default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"password-compare.attlist\">\n    <xs:attribute name=\"password-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The attribute in the directory which contains the user password. Defaults\n          to \"userPassword\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n    <xs:annotation>\n      <xs:documentation>Can be used inside a bean definition to add a security interceptor to the\n        bean and set up access configuration attributes for the bean's methods</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n          <xs:annotation>\n            <xs:documentation>Defines a protected method and the access control configuration\n              attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations\n              with any services provided \"global-method-security\".</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:protect.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method\n          security interceptor.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"protect.attlist\">\n    <xs:attribute name=\"method\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A method name</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to the method, e.g.\n          \"ROLE_A,ROLE_B\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n    <xs:annotation>\n      <xs:documentation>Provides method security for all beans registered in the Spring application\n        context. Specifically, beans will be scanned for Spring Security annotations and/or matches\n        with the ordered list of \"protect-pointcut\" sub-elements. Where there is a match, the beans\n        will automatically be proxied and security authorization applied to the methods accordingly.\n        If you use and enable all three sources of method security metadata (ie \"protect-pointcut\"\n        declarations, @Secured and also JSR 250 security annotations), the metadata sources will be\n        queried in that order. In practical terms, this enables you to use XML to override method\n        security metadata expressed by way of @Secured annotations, with @Secured annotations\n        overriding method security metadata expressed by JSR 250 annotations. It is perfectly\n        acceptable to mix and match, with a given Java type using a combination of XML, @Secured and\n        JSR 250 to express method security metadata (albeit on different\n      methods).</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n          <xs:annotation>\n            <xs:documentation>Defines a protected pointcut and the access control configuration\n              attributes that apply to it. Every bean registered in the Spring application context\n              that provides a method that matches the pointcut will receive security\n            authorization.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n    <xs:attribute name=\"secured-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should\n          be enabled for this application context. Please ensure you have the\n          spring-security-tiger-xxx.jar on the classpath. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"jsr250-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example\n          \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath.\n          Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for\n          method security.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"custom-after-invocation-provider\">\n    <xs:complexType/>\n  </xs:element>\n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n    <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example,\n          'execution(int com.foo.TargetObject.countLength(String))' (without the\n        quotes).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to all methods matching\n          the pointcut, e.g. \"ROLE_A,ROLE_B\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http\">\n    <xs:annotation>\n      <xs:documentation>Container element for HTTP security configuration</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"intercept-url\">\n          <xs:annotation>\n            <xs:documentation>Specifies the access attributes and/or filter list for a particular\n              set of URLs.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"form-login\">\n          <xs:annotation>\n            <xs:documentation>Sets up a form login configuration for authentication with a username\n              and password</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element ref=\"security:openid-login\"/>\n        <xs:element name=\"x509\">\n          <xs:annotation>\n            <xs:documentation>Adds support for X.509 client authentication.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:x509.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"http-basic\">\n          <xs:annotation>\n            <xs:documentation>Adds support for basic authentication (this is an element to permit\n              future expansion, such as supporting an \"ignoreFailure\" attribute)</xs:documentation>\n          </xs:annotation>\n          <xs:complexType/>\n        </xs:element>\n        <xs:element name=\"logout\">\n          <xs:annotation>\n            <xs:documentation>Incorporates a logout processing filter. Most web applications require\n              a logout filter, although you may not require one if you write a controller to\n              provider similar logic.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:logout.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"concurrent-session-control\">\n          <xs:annotation>\n            <xs:documentation>Adds support for concurrent session control, allowing limits to be\n              placed on the number of sessions a user can have.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:concurrent-sessions.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"remember-me\">\n          <xs:annotation>\n            <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute\n              (or no attributes) the cookie-only implementation will be used. Specifying\n              \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure,\n              persisten token approach. </xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"anonymous\">\n          <xs:annotation>\n            <xs:documentation>Adds support for automatically granting all anonymous web requests a\n              particular principal identity and a corresponding granted\n            authority.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"port-mappings\">\n          <xs:annotation>\n            <xs:documentation>Defines the list of mappings between http and https ports for use in\n              redirects</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element maxOccurs=\"unbounded\" ref=\"security:port-mapping\"/>\n            </xs:sequence>\n          </xs:complexType>\n        </xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:http.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n    <xs:attribute name=\"auto-config\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Automatically registers a login form, BASIC authentication, anonymous\n          authentication, logout services, remember-me and servlet-api-integration. If set to\n          \"true\", all of these capabilities are added (although you can still customize the\n          configuration of each by providing the respective element). If unspecified, defaults to\n          \"false\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"create-session\">\n      <xs:annotation>\n        <xs:documentation>Controls the eagerness with which an HTTP session is created. If not set,\n          defaults to \"ifRequired\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ifRequired\"/>\n          <xs:enumeration value=\"always\"/>\n          <xs:enumeration value=\"never\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether test URLs should be converted to lower case prior to comparing\n          with defined path patterns. If unspecified, defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"servlet-api-provision\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Provides versions of HttpServletRequest security methods such as\n          isUserInRole() and getPrincipal() which are implemented by accessing the Spring\n          SecurityContext. Defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager\n          implementation which should be used for authorizing HTTP requests.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"realm\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the realm name that will be used for all\n          authentication features that require a realm name (eg BASIC and Digest authentication). If\n          unspecified, defaults to \"Spring Security Application\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-fixation-protection\">\n      <xs:annotation>\n        <xs:documentation>Indicates whether an existing session should be invalidated when a user\n          authenticates and a new session started. If set to \"none\" no change will be made.\n          \"newSession\" will create a new empty session. \"migrateSession\" will create a new session\n          and copy the session attributes to the new session. Defaults to\n        \"migrateSession\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n          <xs:enumeration value=\"newSession\"/>\n          <xs:enumeration value=\"migrateSession\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"entry-point-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows a customized AuthenticationEntryPoint to be\n        used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"once-per-request\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Corresponds to the observeOncePerRequest property of\n          FilterSecurityInterceptor. Defaults to \"true\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-denied-page\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows the access denied page to be set (the user will be redirected here\n          if an AccessDeniedException is raised).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"intercept-url.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The pattern which defines the URL path. The content will depend on the\n          type set in the containing http element, so will default to ant path\n        syntax.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The access configuration attributes that apply for the configured\n        path.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"method\">\n      <xs:annotation>\n        <xs:documentation>The HTTP Method for which the access configuration attributes should\n          apply. If not specified, the attributes will apply to any method.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"GET\"/>\n          <xs:enumeration value=\"DELETE\"/>\n          <xs:enumeration value=\"HEAD\"/>\n          <xs:enumeration value=\"OPTIONS\"/>\n          <xs:enumeration value=\"POST\"/>\n          <xs:enumeration value=\"PUT\"/>\n          <xs:enumeration value=\"TRACE\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"filters\">\n      <xs:annotation>\n        <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a\n          path from having any filters applied. The full filter stack (consisting of all filters\n          created by the namespace configuration, and any added using 'custom-filter'), will be\n          applied to any other paths.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"requires-channel\">\n      <xs:annotation>\n        <xs:documentation>Used to specify that a URL must be accessed over http or\n        https</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"http\"/>\n          <xs:enumeration value=\"https\"/>\n          <xs:enumeration value=\"any\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"logout.attlist\">\n    <xs:attribute name=\"logout-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL that will cause a logout. Spring Security will\n          initialize a filter that responds to this particular URL. Defaults to\n          /logout if unspecified.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"logout-success-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL to display once the user has logged out. If not\n          specified, defaults to /.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"invalidate-session\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is\n          generally desirable. If unspecified, defaults to true.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"form-login.attlist\">\n    <xs:attribute name=\"login-processing-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to\n          /login.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"default-target-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL that will be redirected to after successful authentication, if the\n          user's previous action could not be resumed. This generally happens if the user visits a\n          login page without having first requested a secured operation that triggers\n          authentication. If unspecified, defaults to the root of the\n        application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"always-use-default-target\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether the user should always be redirected to the default-target-url\n          after login. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"login-page\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security\n          will automatically create a login URL at /spring_security_login and a corresponding filter\n          to render that login URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-failure-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login failure page. If no login failure URL is specified,\n          Spring Security will automatically create a failure login URL at\n          /spring_security_login?login_error and a corresponding filter to render that login failure\n          URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-login\">\n    <xs:annotation>\n      <xs:documentation>Sets up form login for authentication with an Open ID\n      identity</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n        <xs:annotation>\n          <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n          Id</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n  <xs:element name=\"filter-chain-map\">\n    <xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a\n        FilterChainMap</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"filter-chain\">\n          <xs:annotation>\n            <xs:documentation>Used within filter-chain-map to define a specific URL pattern and the\n              list of filters which apply to the URLs matching that pattern. When multiple\n              filter-chain elements are used within a filter-chain-map element, the most specific\n              patterns must be placed at the top of the list, with most general ones at the\n            bottom.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n    <xs:attributeGroup ref=\"security:path-type\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:string\"/>\n    <xs:attribute name=\"filters\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-invocation-definition-source\">\n    <xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterInvocationDefinitionSource bean for use\n        with a FilterSecurityInterceptor. Usually only needed if you are configuring a\n        FilterChainProxy explicitly, rather than using the &lt;http&gt; element. The\n        intercept-url elements used should only contain pattern, method and access attributes. Any\n        others will result in a configuration error. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n          <xs:annotation>\n            <xs:documentation>Specifies the access attributes and/or filter list for a particular\n              set of URLs.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:fids.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"fids.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>as for http element</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"concurrent-sessions.attlist\">\n    <xs:attribute name=\"max-sessions\" type=\"xs:string\"/>\n    <xs:attribute name=\"expired-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL a user will be redirected to if they attempt to use a session\n          which has been \"expired\" by the concurrent session controller.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"exception-if-maximum-exceeded\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies that an exception should be raised when a user attempts to login\n          twice. The default behaviour is to expire the original session.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-registry-alias\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to\n          access it in your own configuration</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-registry-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to an external SessionRegistry implementation which will be\n          used in place of the standard one. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me\n          application. You should set this to a unique value for your\n        application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"token-repository-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent\n          token remember-me implementation. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"data-source-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a DataSource bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n    <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent\n          token remember-me implementation. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n    <xs:attribute name=\"services-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that\n          this implementation should return RememberMeAuthenticationToken instances with the same\n          \"key\" value as specified in the remember-me element. Alternatively it should register its\n          own AuthenticationProvider. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n    <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"anonymous.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The key shared between the provider and filter. This generally does not\n          need to be set. If unset, it will default to \"doesNotMatter\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"username\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The username that should be assigned to the anonymous request. This allows\n          the principal to be identified, which may be important for logging and auditing. if unset,\n          defaults to \"anonymousUser\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"granted-authority\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The granted authority that should be assigned to the anonymous request.\n          Commonly this is used to assign the anonymous request particular roles, which can\n          subsequently be used in authorization decisions. If unset, defaults to\n        \"ROLE_ANONYMOUS\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"port-mapping\">\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:http-port\"/>\n      <xs:attributeGroup ref=\"security:https-port\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"http-port\">\n    <xs:attribute name=\"http\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n    <xs:attribute name=\"https\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"x509.attlist\">\n    <xs:attribute name=\"subject-principal-regex\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The regular expression used to obtain the username from the certificate's\n          subject. Defaults to matching on the common name using the pattern\n        \"CN=(.*?),\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n    <xs:annotation>\n      <xs:documentation>If you are using namespace configuration with Spring Security, an\n        AuthenticationManager will automatically be registered. This element allows you to define an\n        alias to allow you to reference the authentication-manager in your own beans.\n      </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:authman.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n    <xs:attribute name=\"alias\" use=\"required\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>The alias you wish to use for the AuthenticationManager\n        bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-controller-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows the session controller to be set on the internal\n          AuthenticationManager. This should not be used with the &lt;concurrent-session-control\n          /&gt; element</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Indicates that the contained user-service should be used as an\n        authentication source. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element ref=\"security:any-user-service\"/>\n        <xs:element name=\"password-encoder\">\n          <xs:annotation>\n            <xs:documentation>element which defines a password encoding strategy. Used by an\n              authentication provider to convert submitted passwords to hashed versions, for\n              example.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                <xs:complexType>\n                  <xs:attribute name=\"user-property\" type=\"xs:string\">\n                    <xs:annotation>\n                      <xs:documentation>A property of the UserDetails object which will be used as\n                        salt by a password encoder. Typically something like \"username\" might be\n                        used. </xs:documentation>\n                    </xs:annotation>\n                  </xs:attribute>\n                  <xs:attribute name=\"system-wide\" type=\"xs:string\">\n                    <xs:annotation>\n                      <xs:documentation>A single value that will be used as the salt for a password\n                        encoder. </xs:documentation>\n                    </xs:annotation>\n                  </xs:attribute>\n                </xs:complexType>\n              </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:ap.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ap.attlist\">\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"custom-authentication-provider\">\n    <xs:complexType/>\n  </xs:element>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:annotation>\n      <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of\n        \"user\" child elements.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:user\"/>\n      </xs:sequence>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n            context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:properties-file\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n    <xs:attribute name=\"properties\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"user\">\n    <xs:annotation>\n      <xs:documentation>Represents a user in the application.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:user.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"user.attlist\">\n    <xs:attribute name=\"name\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The username assigned to the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"password\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password assigned to the user. This may be hashed if the corresponding\n          authentication provider supports hashing (remember to set the \"hash\" attribute of the\n          \"user-service\" element).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>One of more authorities granted to the user. Separate authorities with a\n          comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"locked\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as locked and\n        unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"disabled\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as disabled and\n        unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:annotation>\n      <xs:documentation>Causes creation of a JDBC-based UserDetailsService.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n            context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The bean ID of the DataSource which provides the required\n        tables.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"users-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query a username, password, and enabled status given a\n          username</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query for a user's granted authorities given a\n          username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query user's group authorities given a\n        username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the\n          default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:group name=\"custom-filter\">\n    <xs:sequence>\n      <xs:element minOccurs=\"0\" ref=\"security:custom-filter\"/>\n    </xs:sequence>\n  </xs:group>\n  <xs:element name=\"custom-filter\">\n    <xs:annotation>\n      <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into\n        the security filter chain. If neither the 'after' or 'before' options are supplied, then the\n        filter must implement the Ordered interface directly. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The filter immediately after which the custom-filter should be placed in\n            the chain. This feature will only be needed by advanced users who wish to mix their own\n            filters into the security filter chain and have some knowledge of the standard Spring\n            Security filters. The filter names map to specific Spring Security implementation\n            filters. </xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The filter immediately before which the custom-filter should be placed\n            in the chain</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The explicit position at which the custom-filter should be placed in the\n            chain. Use if you are replacing a standard filter.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"after\">\n    <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately after which the custom-filter should be placed in\n          the chain. This feature will only be needed by advanced users who wish to mix their own\n          filters into the security filter chain and have some knowledge of the standard Spring\n          Security filters. The filter names map to specific Spring Security implementation filters.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n    <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately before which the custom-filter should be placed in\n          the chain</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n    <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The explicit position at which the custom-filter should be placed in the\n          chain. Use if you are replacing a standard filter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"FIRST\"/>\n      <xs:enumeration value=\"CHANNEL_FILTER\"/>\n      <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n      <xs:enumeration value=\"SESSION_CONTEXT_INTEGRATION_FILTER\"/>\n      <xs:enumeration value=\"LOGOUT_FILTER\"/>\n      <xs:enumeration value=\"X509_FILTER\"/>\n      <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n      <xs:enumeration value=\"CAS_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"AUTHENTICATION_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"BASIC_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n      <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n      <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n      <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n      <xs:enumeration value=\"NTLM_FILTER\"/>\n      <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n      <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n      <xs:enumeration value=\"LAST\"/>\n    </xs:restriction>\n  </xs:simpleType>\n</xs:schema>\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-2.0.4.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n  xmlns:security=\"http://www.springframework.org/schema/security\" elementFormDefault=\"qualified\"\n  targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n    <xs:attribute name=\"hash\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n    <xs:attribute name=\"base64\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"path-type\">\n    <xs:attribute name=\"path-type\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n    <xs:attribute name=\"port\" use=\"required\" type=\"xs:integer\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server,\n          for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n    <xs:attribute name=\"url\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n    <xs:attribute name=\"id\" use=\"required\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n    <xs:attribute name=\"ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n    <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n    <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a DataSource bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"password-encoder.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"base64\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-property\">\n    <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A property of the UserDetails object which will be used as salt by a\n          password encoder. Typically something like \"username\" might be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n    <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A single value that will be used as the salt for a password encoder.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"boolean\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"true\"/>\n      <xs:enumeration value=\"false\"/>\n    </xs:restriction>\n  </xs:simpleType>\n  <xs:attributeGroup name=\"role-prefix\">\n    <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the\n          default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n    <xs:annotation>\n      <xs:documentation>Defines an LDAP server location or starts an embedded server. The url\n        indicates the location of a remote server. If no url is given, an embedded server will be\n        started, listening on the supplied port number. The port is optional and defaults to 33389.\n        A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n      </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"port\" type=\"xs:integer\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server,\n          for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to\n          authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-password\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password for the manager DN.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"ldif\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP\n          server</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"root\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional root suffix for the embedded LDAP server. Default is\n          \"dc=springframework,dc=org\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n    <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n    <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n    <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n    <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example\n          \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n    <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a\n          'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n    <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n    <xs:attribute name=\"user-details-class\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"server-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example\n          \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a\n          'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the\n          default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Sets up an ldap authentication provider</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"password-compare\">\n          <xs:annotation>\n            <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation\n              of the user's password to authenticate the user</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                <xs:annotation>\n                  <xs:documentation>element which defines a password encoding strategy. Used by an\n                    authentication provider to convert submitted passwords to hashed versions, for\n                    example.</xs:documentation>\n                </xs:annotation>\n                <xs:complexType>\n                  <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"salt-source\">\n                      <xs:annotation>\n                        <xs:documentation>Password salting strategy. A system-wide constant or a\n                          property from the UserDetails object can be used.</xs:documentation>\n                      </xs:annotation>\n                      <xs:complexType>\n                        <xs:attribute name=\"user-property\" type=\"xs:string\">\n                          <xs:annotation>\n                            <xs:documentation>A property of the UserDetails object which will be\n                              used as salt by a password encoder. Typically something like\n                              \"username\" might be used. </xs:documentation>\n                          </xs:annotation>\n                        </xs:attribute>\n                        <xs:attribute name=\"system-wide\" type=\"xs:string\">\n                          <xs:annotation>\n                            <xs:documentation>A single value that will be used as the salt for a\n                              password encoder. </xs:documentation>\n                          </xs:annotation>\n                        </xs:attribute>\n                      </xs:complexType>\n                    </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                </xs:complexType>\n              </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n    <xs:attribute name=\"server-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a\n          'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example\n          \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-dn-pattern\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A specific pattern used to build the user's DN, for example\n          \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the\n          username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the\n          default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the\n          framework will attempt to load standard attributes for the defined class into the returned\n          UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"password-compare.attlist\">\n    <xs:attribute name=\"password-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The attribute in the directory which contains the user password. Defaults\n          to \"userPassword\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n    <xs:annotation>\n      <xs:documentation>Can be used inside a bean definition to add a security interceptor to the\n        bean and set up access configuration attributes for the bean's methods</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n          <xs:annotation>\n            <xs:documentation>Defines a protected method and the access control configuration\n              attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations\n              with any services provided \"global-method-security\".</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:protect.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method\n          security interceptor.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"protect.attlist\">\n    <xs:attribute name=\"method\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A method name</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to the method, e.g.\n          \"ROLE_A,ROLE_B\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n    <xs:annotation>\n      <xs:documentation>Provides method security for all beans registered in the Spring application\n        context. Specifically, beans will be scanned for Spring Security annotations and/or matches\n        with the ordered list of \"protect-pointcut\" sub-elements. Where there is a match, the beans\n        will automatically be proxied and security authorization applied to the methods accordingly.\n        If you use and enable all three sources of method security metadata (ie \"protect-pointcut\"\n        declarations, @Secured and also JSR 250 security annotations), the metadata sources will be\n        queried in that order. In practical terms, this enables you to use XML to override method\n        security metadata expressed by way of @Secured annotations, with @Secured annotations\n        overriding method security metadata expressed by JSR 250 annotations. It is perfectly\n        acceptable to mix and match, with a given Java type using a combination of XML, @Secured and\n        JSR 250 to express method security metadata (albeit on different\n      methods).</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n          <xs:annotation>\n            <xs:documentation>Defines a protected pointcut and the access control configuration\n              attributes that apply to it. Every bean registered in the Spring application context\n              that provides a method that matches the pointcut will receive security\n            authorization.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n    <xs:attribute name=\"secured-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should\n          be enabled for this application context. Please ensure you have the\n          spring-security-tiger-xxx.jar on the classpath. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"jsr250-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example\n          \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath.\n          Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for\n          method security.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"custom-after-invocation-provider\">\n    <xs:annotation>\n      <xs:documentation>Used to decorate an AfterInvocationProvider to specify that it should be\n        used with method security.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType/>\n  </xs:element>\n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n    <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example,\n          'execution(int com.foo.TargetObject.countLength(String))' (without the\n        quotes).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to all methods matching\n          the pointcut, e.g. \"ROLE_A,ROLE_B\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http\">\n    <xs:annotation>\n      <xs:documentation>Container element for HTTP security configuration</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"intercept-url\">\n          <xs:annotation>\n            <xs:documentation>Specifies the access attributes and/or filter list for a particular\n              set of URLs.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"form-login\">\n          <xs:annotation>\n            <xs:documentation>Sets up a form login configuration for authentication with a username\n              and password</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element ref=\"security:openid-login\"/>\n        <xs:element name=\"x509\">\n          <xs:annotation>\n            <xs:documentation>Adds support for X.509 client authentication.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:x509.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"http-basic\">\n          <xs:annotation>\n            <xs:documentation>Adds support for basic authentication (this is an element to permit\n              future expansion, such as supporting an \"ignoreFailure\" attribute)</xs:documentation>\n          </xs:annotation>\n          <xs:complexType/>\n        </xs:element>\n        <xs:element name=\"logout\">\n          <xs:annotation>\n            <xs:documentation>Incorporates a logout processing filter. Most web applications require\n              a logout filter, although you may not require one if you write a controller to\n              provider similar logic.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:logout.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"concurrent-session-control\">\n          <xs:annotation>\n            <xs:documentation>Adds support for concurrent session control, allowing limits to be\n              placed on the number of sessions a user can have.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:concurrent-sessions.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"remember-me\">\n          <xs:annotation>\n            <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute\n              (or no attributes) the cookie-only implementation will be used. Specifying\n              \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure,\n              persisten token approach. </xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"anonymous\">\n          <xs:annotation>\n            <xs:documentation>Adds support for automatically granting all anonymous web requests a\n              particular principal identity and a corresponding granted\n            authority.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"port-mappings\">\n          <xs:annotation>\n            <xs:documentation>Defines the list of mappings between http and https ports for use in\n              redirects</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element maxOccurs=\"unbounded\" ref=\"security:port-mapping\"/>\n            </xs:sequence>\n          </xs:complexType>\n        </xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:http.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n    <xs:attribute name=\"auto-config\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Automatically registers a login form, BASIC authentication, anonymous\n          authentication, logout services, remember-me and servlet-api-integration. If set to\n          \"true\", all of these capabilities are added (although you can still customize the\n          configuration of each by providing the respective element). If unspecified, defaults to\n          \"false\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"create-session\">\n      <xs:annotation>\n        <xs:documentation>Controls the eagerness with which an HTTP session is created. If not set,\n          defaults to \"ifRequired\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ifRequired\"/>\n          <xs:enumeration value=\"always\"/>\n          <xs:enumeration value=\"never\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether test URLs should be converted to lower case prior to comparing\n          with defined path patterns. If unspecified, defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"servlet-api-provision\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Provides versions of HttpServletRequest security methods such as\n          isUserInRole() and getPrincipal() which are implemented by accessing the Spring\n          SecurityContext. Defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager\n          implementation which should be used for authorizing HTTP requests.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"realm\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the realm name that will be used for all\n          authentication features that require a realm name (eg BASIC and Digest authentication). If\n          unspecified, defaults to \"Spring Security Application\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-fixation-protection\">\n      <xs:annotation>\n        <xs:documentation>Indicates whether an existing session should be invalidated when a user\n          authenticates and a new session started. If set to \"none\" no change will be made.\n          \"newSession\" will create a new empty session. \"migrateSession\" will create a new session\n          and copy the session attributes to the new session. Defaults to\n        \"migrateSession\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n          <xs:enumeration value=\"newSession\"/>\n          <xs:enumeration value=\"migrateSession\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"entry-point-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows a customized AuthenticationEntryPoint to be\n        used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"once-per-request\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Corresponds to the observeOncePerRequest property of\n          FilterSecurityInterceptor. Defaults to \"true\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-denied-page\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows the access denied page to be set (the user will be redirected here\n          if an AccessDeniedException is raised).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"intercept-url.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The pattern which defines the URL path. The content will depend on the\n          type set in the containing http element, so will default to ant path\n        syntax.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The access configuration attributes that apply for the configured\n        path.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"method\">\n      <xs:annotation>\n        <xs:documentation>The HTTP Method for which the access configuration attributes should\n          apply. If not specified, the attributes will apply to any method.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"GET\"/>\n          <xs:enumeration value=\"DELETE\"/>\n          <xs:enumeration value=\"HEAD\"/>\n          <xs:enumeration value=\"OPTIONS\"/>\n          <xs:enumeration value=\"POST\"/>\n          <xs:enumeration value=\"PUT\"/>\n          <xs:enumeration value=\"TRACE\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"filters\">\n      <xs:annotation>\n        <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a\n          path from having any filters applied. The full filter stack (consisting of all filters\n          created by the namespace configuration, and any added using 'custom-filter'), will be\n          applied to any other paths.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"requires-channel\">\n      <xs:annotation>\n        <xs:documentation>Used to specify that a URL must be accessed over http or https, or that\n          there is no preference.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"http\"/>\n          <xs:enumeration value=\"https\"/>\n          <xs:enumeration value=\"any\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"logout.attlist\">\n    <xs:attribute name=\"logout-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL that will cause a logout. Spring Security will\n          initialize a filter that responds to this particular URL. Defaults to\n          /logout if unspecified.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"logout-success-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL to display once the user has logged out. If not\n          specified, defaults to /.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"invalidate-session\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is\n          generally desirable. If unspecified, defaults to true.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"form-login.attlist\">\n    <xs:attribute name=\"login-processing-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to\n          /login.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"default-target-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL that will be redirected to after successful authentication, if the\n          user's previous action could not be resumed. This generally happens if the user visits a\n          login page without having first requested a secured operation that triggers\n          authentication. If unspecified, defaults to the root of the\n        application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"always-use-default-target\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether the user should always be redirected to the default-target-url\n          after login. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"login-page\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security\n          will automatically create a login URL at /spring_security_login and a corresponding filter\n          to render that login URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-failure-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login failure page. If no login failure URL is specified,\n          Spring Security will automatically create a failure login URL at\n          /spring_security_login?login_error and a corresponding filter to render that login failure\n          URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-login\">\n    <xs:annotation>\n      <xs:documentation>Sets up form login for authentication with an Open ID\n      identity</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n        <xs:annotation>\n          <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n          Id</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n  <xs:element name=\"filter-chain-map\">\n    <xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a\n        FilterChainMap</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"filter-chain\">\n          <xs:annotation>\n            <xs:documentation>Used within filter-chain-map to define a specific URL pattern and the\n              list of filters which apply to the URLs matching that pattern. When multiple\n              filter-chain elements are used within a filter-chain-map element, the most specific\n              patterns must be placed at the top of the list, with most general ones at the\n            bottom.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n    <xs:attributeGroup ref=\"security:path-type\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:string\"/>\n    <xs:attribute name=\"filters\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-invocation-definition-source\">\n    <xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterInvocationDefinitionSource bean for use\n        with a FilterSecurityInterceptor. Usually only needed if you are configuring a\n        FilterChainProxy explicitly, rather than using the &lt;http&gt; element. The\n        intercept-url elements used should only contain pattern, method and access attributes. Any\n        others will result in a configuration error. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n          <xs:annotation>\n            <xs:documentation>Specifies the access attributes and/or filter list for a particular\n              set of URLs.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:fids.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"fids.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>as for http element</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"concurrent-sessions.attlist\">\n    <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\">\n      <xs:annotation>\n        <xs:documentation>The maximum number of sessions a single user can have open at the same\n          time. Defaults to \"1\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"expired-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL a user will be redirected to if they attempt to use a session\n          which has been \"expired\" by the concurrent session controller because they have logged in\n          again.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"exception-if-maximum-exceeded\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies that an exception should be raised when a user attempts to login\n          when they already have the maximum configured sessions open. The default behaviour is to\n          expire the original session.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-registry-alias\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to\n          access it in your own configuration</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-registry-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to an external SessionRegistry implementation which will be\n          used in place of the standard one. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me\n          application. You should set this to a unique value for your\n        application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"token-repository-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent\n          token remember-me implementation. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"data-source-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a DataSource bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"token-validity-seconds\" type=\"xs:positiveInteger\">\n      <xs:annotation>\n        <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n    <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent\n          token remember-me implementation. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n    <xs:attribute name=\"services-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that\n          this implementation should return RememberMeAuthenticationToken instances with the same\n          \"key\" value as specified in the remember-me element. Alternatively it should register its\n          own AuthenticationProvider. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n    <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"anonymous.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The key shared between the provider and filter. This generally does not\n          need to be set. If unset, it will default to \"doesNotMatter\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"username\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The username that should be assigned to the anonymous request. This allows\n          the principal to be identified, which may be important for logging and auditing. if unset,\n          defaults to \"anonymousUser\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"granted-authority\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The granted authority that should be assigned to the anonymous request.\n          Commonly this is used to assign the anonymous request particular roles, which can\n          subsequently be used in authorization decisions. If unset, defaults to\n        \"ROLE_ANONYMOUS\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"port-mapping\">\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:http-port\"/>\n      <xs:attributeGroup ref=\"security:https-port\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"http-port\">\n    <xs:attribute name=\"http\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n    <xs:attribute name=\"https\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"x509.attlist\">\n    <xs:attribute name=\"subject-principal-regex\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The regular expression used to obtain the username from the certificate's\n          subject. Defaults to matching on the common name using the pattern\n        \"CN=(.*?),\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n    <xs:annotation>\n      <xs:documentation>If you are using namespace configuration with Spring Security, an\n        AuthenticationManager will automatically be registered. This element allows you to define an\n        alias to allow you to reference the authentication-manager in your own beans.\n      </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:authman.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n    <xs:attribute name=\"alias\" use=\"required\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>The alias you wish to use for the AuthenticationManager\n        bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-controller-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows the session controller to be set on the internal\n          AuthenticationManager. This should not be used with the &lt;concurrent-session-control\n          /&gt; element</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Indicates that the contained user-service should be used as an\n        authentication source. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element ref=\"security:any-user-service\"/>\n        <xs:element name=\"password-encoder\">\n          <xs:annotation>\n            <xs:documentation>element which defines a password encoding strategy. Used by an\n              authentication provider to convert submitted passwords to hashed versions, for\n              example.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                <xs:annotation>\n                  <xs:documentation>Password salting strategy. A system-wide constant or a property\n                    from the UserDetails object can be used.</xs:documentation>\n                </xs:annotation>\n                <xs:complexType>\n                  <xs:attribute name=\"user-property\" type=\"xs:string\">\n                    <xs:annotation>\n                      <xs:documentation>A property of the UserDetails object which will be used as\n                        salt by a password encoder. Typically something like \"username\" might be\n                        used. </xs:documentation>\n                    </xs:annotation>\n                  </xs:attribute>\n                  <xs:attribute name=\"system-wide\" type=\"xs:string\">\n                    <xs:annotation>\n                      <xs:documentation>A single value that will be used as the salt for a password\n                        encoder. </xs:documentation>\n                    </xs:annotation>\n                  </xs:attribute>\n                </xs:complexType>\n              </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:ap.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ap.attlist\">\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"custom-authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Element used to decorate an AuthenticationProvider bean to add it to the\n        internal AuthenticationManager maintained by the namespace.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType/>\n  </xs:element>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:annotation>\n      <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of\n        \"user\" child elements.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:user\"/>\n      </xs:sequence>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n            context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:properties-file\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n    <xs:attribute name=\"properties\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"user\">\n    <xs:annotation>\n      <xs:documentation>Represents a user in the application.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:user.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"user.attlist\">\n    <xs:attribute name=\"name\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The username assigned to the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"password\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password assigned to the user. This may be hashed if the corresponding\n          authentication provider supports hashing (remember to set the \"hash\" attribute of the\n          \"user-service\" element).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>One of more authorities granted to the user. Separate authorities with a\n          comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"locked\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as locked and\n        unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"disabled\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as disabled and\n        unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:annotation>\n      <xs:documentation>Causes creation of a JDBC-based UserDetailsService.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n            context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The bean ID of the DataSource which provides the required\n        tables.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"users-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query a username, password, and enabled status given a\n          username</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query for a user's granted authorities given a\n          username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query user's group authorities given a\n        username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from\n          persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the\n          default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:group name=\"custom-filter\">\n    <xs:sequence>\n      <xs:element minOccurs=\"0\" ref=\"security:custom-filter\"/>\n    </xs:sequence>\n  </xs:group>\n  <xs:element name=\"custom-filter\">\n    <xs:annotation>\n      <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into\n        the security filter chain. If neither the 'after' or 'before' options are supplied, then the\n        filter must implement the Ordered interface directly. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The filter immediately after which the custom-filter should be placed in\n            the chain. This feature will only be needed by advanced users who wish to mix their own\n            filters into the security filter chain and have some knowledge of the standard Spring\n            Security filters. The filter names map to specific Spring Security implementation\n            filters. </xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The filter immediately before which the custom-filter should be placed\n            in the chain</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The explicit position at which the custom-filter should be placed in the\n            chain. Use if you are replacing a standard filter.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"after\">\n    <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately after which the custom-filter should be placed in\n          the chain. This feature will only be needed by advanced users who wish to mix their own\n          filters into the security filter chain and have some knowledge of the standard Spring\n          Security filters. The filter names map to specific Spring Security implementation filters.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n    <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately before which the custom-filter should be placed in\n          the chain</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n    <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The explicit position at which the custom-filter should be placed in the\n          chain. Use if you are replacing a standard filter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"FIRST\"/>\n      <xs:enumeration value=\"CHANNEL_FILTER\"/>\n      <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n      <xs:enumeration value=\"SESSION_CONTEXT_INTEGRATION_FILTER\"/>\n      <xs:enumeration value=\"LOGOUT_FILTER\"/>\n      <xs:enumeration value=\"X509_FILTER\"/>\n      <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n      <xs:enumeration value=\"CAS_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"AUTHENTICATION_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"OPENID_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"BASIC_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n      <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n      <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n      <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n      <xs:enumeration value=\"NTLM_FILTER\"/>\n      <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n      <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n      <xs:enumeration value=\"LAST\"/>\n    </xs:restriction>\n  </xs:simpleType>\n</xs:schema>\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n  xmlns:security=\"http://www.springframework.org/schema/security\" elementFormDefault=\"qualified\"\n  targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n    <xs:attribute name=\"hash\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n    <xs:attribute name=\"base64\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"path-type\">\n    <xs:attribute name=\"path-type\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n    <xs:attribute name=\"port\" use=\"required\" type=\"xs:integer\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server,\n          for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n    <xs:attribute name=\"url\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n    <xs:attribute name=\"id\" use=\"required\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n    <xs:attribute name=\"ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n    <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n    <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"password-encoder.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"base64\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-property\">\n    <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A property of the UserDetails object which will be used as salt by a\n          password encoder. Typically something like \"username\" might be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n    <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A single value that will be used as the salt for a password encoder.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"boolean\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"true\"/>\n      <xs:enumeration value=\"false\"/>\n    </xs:restriction>\n  </xs:simpleType>\n  <xs:element name=\"ldap-server\">\n    <xs:annotation>\n      <xs:documentation>Defines an LDAP server location or starts an embedded server. The url\n        indicates the location of a remote server. If no url is given, an embedded server will be\n        started, listening on the supplied port number. The port is optional and defaults to 33389.\n        A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n      </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"port\" type=\"xs:integer\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server,\n          for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to\n          authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-password\" type=\"xs:string\"/>\n    <xs:attribute name=\"ldif\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP\n          server</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"root\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional root suffix for the embedded LDAP server. Default is\n          \"dc=springframework,dc=org\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n    <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n    <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n    <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n          \"\" (searching from the root).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n    <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n    <xs:attribute name=\"user-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n    <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"server-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:string\"/>\n    <xs:attributeGroup ref=\"security:user-search-base-attribute\"/>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Sets up an ldap authentication provider</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"password-compare\">\n          <xs:annotation>\n            <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation\n              of the user's password to authenticate the user</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                <xs:annotation>\n                  <xs:documentation>element which defines a password encoding strategy. Used by an\n                    authentication provider to convert submitted passwords to hashed versions, for\n                    example.</xs:documentation>\n                </xs:annotation>\n                <xs:complexType>\n                  <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"salt-source\">\n                      <xs:complexType>\n                        <xs:attribute name=\"user-property\" type=\"xs:string\">\n                          <xs:annotation>\n                            <xs:documentation>A property of the UserDetails object which will be\n                              used as salt by a password encoder. Typically something like\n                              \"username\" might be used. </xs:documentation>\n                          </xs:annotation>\n                        </xs:attribute>\n                        <xs:attribute name=\"system-wide\" type=\"xs:string\">\n                          <xs:annotation>\n                            <xs:documentation>A single value that will be used as the salt for a\n                              password encoder. </xs:documentation>\n                          </xs:annotation>\n                        </xs:attribute>\n                      </xs:complexType>\n                    </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                </xs:complexType>\n              </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n    <xs:attribute name=\"server-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is\n          registered (using &lt;ldap-server&gt; with no Id), that server will be used.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attributeGroup ref=\"security:user-search-base-attribute\"/>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:string\"/>\n    <xs:attribute name=\"group-search-base\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to\n        \"ou=groups\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted\n          parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used\n          within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-dn-pattern\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A specific pattern used to build the user's DN, for example\n          \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the\n          username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"password-compare.attlist\">\n    <xs:attribute name=\"password-attribute\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The attribute in the directory which contains the user password. Defaults\n          to \"userPassword\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend\n          strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n    <xs:annotation>\n      <xs:documentation>Can be used inside a bean definition to add a security interceptor to the\n        bean and set up access configuration attributes for the bean's methods</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" ref=\"security:protect\"/>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method\n          security interceptor.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"protect\">\n    <xs:annotation>\n      <xs:documentation>Defines a protected method and the access control configuration attributes\n        that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services\n        provided \"global-method-security\".</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:protect.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"protect.attlist\">\n    <xs:attribute name=\"method\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A method name</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to the method, e.g.\n          \"ROLE_A,ROLE_B\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n    <xs:annotation>\n      <xs:documentation>Provides method security for all beans registered in the Spring application\n        context. Specifically, beans will be scanned for Spring Security annotations and/or matches\n        with the ordered list of \"protect-pointcut\" sub-elements. Where there is a match, the beans\n        will automatically be proxied and security authorization applied to the methods accordingly.\n        If you use and enable all three sources of method security metadata (ie \"protect-pointcut\"\n        declarations, @Secured and also JSR 250 security annotations), the metadata sources will be\n        queried in that order. In practical terms, this enables you to use XML to override method\n        security metadata expressed by way of @Secured annotations, with @Secured annotations\n        overriding method security metadata expressed by JSR 250 annotations. It is perfectly\n        acceptable to mix and match, with a given Java type using a combination of XML, @Secured and\n        JSR 250 to express method security metadata (albeit on different\n      methods).</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n          <xs:annotation>\n            <xs:documentation>Defines a protected pointcut and the access control configuration\n              attributes that apply to it. Every bean registered in the Spring application context\n              that provides a method that matches the pointcut will receive security\n            authorization.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n    <xs:attribute name=\"secured-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should\n          be enabled for this application context. Please ensure you have the\n          spring-security-tiger-xxx.jar on the classpath. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"jsr250-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example\n          \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath.\n          Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for\n          method security.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n    <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example,\n          'execution(int com.foo.TargetObject.countLength(String))' (without the\n        quotes).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to all methods matching\n          the pointcut, e.g. \"ROLE_A,ROLE_B\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http\">\n    <xs:annotation>\n      <xs:documentation>Container element for HTTP security configuration</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"intercept-url\">\n          <xs:annotation>\n            <xs:documentation>Specifies the access attributes and/or filter list for a particular\n              set of URLs.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"form-login\">\n          <xs:annotation>\n            <xs:documentation>Sets up a form login configuration for authentication with a username\n              and password</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element ref=\"security:openid-login\"/>\n        <xs:element name=\"x509\">\n          <xs:annotation>\n            <xs:documentation>Adds support for X.509 client authentication.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:x509.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"http-basic\">\n          <xs:annotation>\n            <xs:documentation>Adds support for basic authentication (this is an element to permit\n              future expansion, such as supporting an \"ignoreFailure\" attribute)</xs:documentation>\n          </xs:annotation>\n          <xs:complexType/>\n        </xs:element>\n        <xs:element name=\"logout\">\n          <xs:annotation>\n            <xs:documentation>Incorporates a logout processing filter. Most web applications require\n              a logout filter, although you may not require one if you write a controller to\n              provider similar logic.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:logout.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"concurrent-session-control\">\n          <xs:annotation>\n            <xs:documentation>Adds support for concurrent session control, allowing limits to be\n              placed on the number of sessions a user can have.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:concurrent-sessions.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"remember-me\">\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"anonymous\">\n          <xs:annotation>\n            <xs:documentation>Adds support for automatically granting all anonymous web requests a\n              particular principal identity and a corresponding granted\n            authority.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n        <xs:element name=\"port-mappings\">\n          <xs:annotation>\n            <xs:documentation>Defines the list of mappings between http and https ports for use in\n              redirects</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element maxOccurs=\"unbounded\" ref=\"security:port-mapping\"/>\n            </xs:sequence>\n          </xs:complexType>\n        </xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:http.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n    <xs:attribute name=\"auto-config\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Automatically registers a login form, BASIC authentication, anonymous\n          authentication, logout services, remember-me and servlet-api-integration. If set to\n          \"true\", all of these capabilities are added (although you can still customize the\n          configuration of each by providing the respective element). If unspecified, defaults to\n          \"false\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"create-session\">\n      <xs:annotation>\n        <xs:documentation>Controls the eagerness with which an HTTP session is created. If not set,\n          defaults to \"ifRequired\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ifRequired\"/>\n          <xs:enumeration value=\"always\"/>\n          <xs:enumeration value=\"never\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether test URLs should be converted to lower case prior to comparing\n          with defined path patterns. If unspecified, defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"servlet-api-provision\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Provides versions of HttpServletRequest security methods such as\n          isUserInRole() and getPrincipal() which are implemented by accessing the Spring\n          SecurityContext. Defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager\n          implementation which should be used for authorizing HTTP requests.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"realm\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the realm name that will be used for all\n          authentication features that require a realm name (eg BASIC and Digest authentication). If\n          unspecified, defaults to \"Spring Security Application\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-fixation-protection\">\n      <xs:annotation>\n        <xs:documentation>Indicates whether an existing session should be invalidated when a user\n          authenticates and a new session started. If set to \"none\" no change will be made.\n          \"newSession\" will create a new empty session. \"migrateSession\" will create a new session\n          and copy the session attributes to the new session. Defaults to\n        \"migrateSession\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n          <xs:enumeration value=\"newSession\"/>\n          <xs:enumeration value=\"migrateSession\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"entry-point-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows a customized AuthenticationEntryPoint to be\n        used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"once-per-request\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Corresponds to the observeOncePerRequest property of\n          FilterSecurityInterceptor. Defaults to \"false\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-denied-page\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows the access denied page to be set (the user will be redirected here\n          if an AccessDeniedException is raised).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"intercept-url.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The pattern which defines the URL path. The content will depend on the\n          type set in the containing http element, so will default to ant path\n        syntax.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The access configuration attributes that apply for the configured\n        path.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"method\">\n      <xs:annotation>\n        <xs:documentation>The HTTP Method for which the access configuration attributes should\n          apply. If not specified, the attributes will apply to any method.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"GET\"/>\n          <xs:enumeration value=\"DELETE\"/>\n          <xs:enumeration value=\"HEAD\"/>\n          <xs:enumeration value=\"OPTIONS\"/>\n          <xs:enumeration value=\"POST\"/>\n          <xs:enumeration value=\"PUT\"/>\n          <xs:enumeration value=\"TRACE\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"filters\">\n      <xs:annotation>\n        <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a\n          path from having any filters applied. The full filter stack (consisting of all defined\n          filters, will be applied to any other paths).</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"requires-channel\">\n      <xs:annotation>\n        <xs:documentation>Used to specify that a URL must be accessed over http or\n        https</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"http\"/>\n          <xs:enumeration value=\"https\"/>\n          <xs:enumeration value=\"any\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"logout.attlist\">\n    <xs:attribute name=\"logout-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL that will cause a logout. Spring Security will\n          initialize a filter that responds to this particular URL. Defaults to\n          /logout if unspecified.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"logout-success-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL to display once the user has logged out. If not\n          specified, defaults to /.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"invalidate-session\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is\n          generally desirable. If unspecified, defaults to true.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"form-login.attlist\">\n    <xs:attribute name=\"login-processing-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to\n          /login.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"default-target-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL that will be redirected to after successful authentication, if the\n          user's previous action could not be resumed. This generally happens if the user visits a\n          login page without having first requested a secured operation that triggers\n          authentication. If unspecified, defaults to the root of the\n        application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"always-use-default-target\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether the user should always be redirected to the default-target-url\n          after login. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"login-page\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security\n          will automatically create a login URL at /spring_security_login and a corresponding filter\n          to render that login URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-failure-url\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login failure page. If no login failure URL is specified,\n          Spring Security will automatically create a failure login URL at\n          /spring_security_login?login_error and a corresponding filter to render that login failure\n          URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-login\">\n    <xs:annotation>\n      <xs:documentation>Sets up form login for authentication with an Open ID\n      identity</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n        <xs:annotation>\n          <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n          Id</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n  <xs:element name=\"filter-chain-map\">\n    <xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a\n        FilterChainMap</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"filter-chain\">\n          <xs:annotation>\n            <xs:documentation>Used within filter-chain-map to define a specific URL pattern and the\n              list of filters which apply to the URLs matching that pattern. When multiple\n              filter-chain elements are used within a filter-chain-map element, the most specific\n              patterns must be placed at the top of the list, with most general ones at the\n            bottom.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n    <xs:attributeGroup ref=\"security:path-type\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:string\"/>\n    <xs:attribute name=\"filters\" use=\"required\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-invocation-definition-source\">\n    <xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterInvocationDefinitionSource bean for use\n        with a FilterSecurityInterceptor. Usually only needed if you are configuring a\n        FilterChainProxy explicitly, rather than using the &lt;http&gt; element. The\n        intercept-url elements used should only contain pattern, method and access attributes. Any\n        others will result in a configuration error. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n          <xs:annotation>\n            <xs:documentation>Specifies the access attributes and/or filter list for a particular\n              set of URLs.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:fids.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"fids.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n          context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>as for http element</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK\n          1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if\n          unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"concurrent-sessions.attlist\">\n    <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\"/>\n    <xs:attribute name=\"expired-url\" type=\"xs:string\"/>\n    <xs:attribute name=\"exception-if-maximum-exceeded\" type=\"security:boolean\"/>\n    <xs:attribute name=\"session-registry-alias\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to\n          access it in your own configuration</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:string\"/>\n    <xs:attribute name=\"token-repository-ref\" type=\"xs:string\"/>\n    <xs:attribute name=\"data-source-ref\" type=\"xs:string\"/>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"anonymous.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The key shared between the provider and filter. This generally does not\n          need to be set. If unset, it will default to \"doesNotMatter\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"username\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The username that should be assigned to the anonymous request. This allows\n          the principal to be identified, which may be important for logging and auditing. if unset,\n          defaults to \"anonymousUser\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"granted-authority\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The granted authority that should be assigned to the anonymous request.\n          Commonly this is used to assign the anonymous request particular roles, which can\n          subsequently be used in authorization decisions. If unset, defaults to\n        \"ROLE_ANONYMOUS\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"port-mapping\">\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:http-port\"/>\n      <xs:attributeGroup ref=\"security:https-port\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"http-port\">\n    <xs:attribute name=\"http\" use=\"required\" type=\"xs:integer\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n    <xs:attribute name=\"https\" use=\"required\" type=\"xs:integer\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"x509.attlist\">\n    <xs:attribute name=\"subject-principal-regex\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The regular expression used to obtain the username from the certificate's\n          subject. Defaults to matching on the common name using the pattern\n        \"CN=(.*?),\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n    <xs:annotation>\n      <xs:documentation>If you are using namespace configuration with Spring Security, an\n        AuthenticationManager will automatically be registered. This element simple allows you to\n        define an alias to allow you to reference the authentication-manager in your own beans.\n      </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:authman.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n    <xs:annotation>\n      <xs:documentation>The alias you wish to use for the AuthenticationManager\n      bean</xs:documentation>\n    </xs:annotation>\n    <xs:attribute name=\"alias\" use=\"required\" type=\"xs:ID\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-provider\">\n    <xs:annotation>\n      <xs:documentation>Indicates that the contained user-service should be used as an\n        authentication source. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element ref=\"security:any-user-service\"/>\n        <xs:element name=\"password-encoder\">\n          <xs:annotation>\n            <xs:documentation>element which defines a password encoding strategy. Used by an\n              authentication provider to convert submitted passwords to hashed versions, for\n              example.</xs:documentation>\n          </xs:annotation>\n          <xs:complexType>\n            <xs:sequence>\n              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                <xs:complexType>\n                  <xs:attribute name=\"user-property\" type=\"xs:string\">\n                    <xs:annotation>\n                      <xs:documentation>A property of the UserDetails object which will be used as\n                        salt by a password encoder. Typically something like \"username\" might be\n                        used. </xs:documentation>\n                    </xs:annotation>\n                  </xs:attribute>\n                  <xs:attribute name=\"system-wide\" type=\"xs:string\">\n                    <xs:annotation>\n                      <xs:documentation>A single value that will be used as the salt for a password\n                        encoder. </xs:documentation>\n                    </xs:annotation>\n                  </xs:attribute>\n                </xs:complexType>\n              </xs:element>\n            </xs:sequence>\n            <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n          </xs:complexType>\n        </xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:ap.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"ap.attlist\">\n    <xs:attribute name=\"user-service-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean)\n        Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"custom-authentication-provider\">\n    <xs:complexType/>\n  </xs:element>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:annotation>\n      <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of\n        \"user\" child elements.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:user\"/>\n      </xs:sequence>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n            context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:properties-file\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n    <xs:attribute name=\"properties\" type=\"xs:string\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"user\">\n    <xs:annotation>\n      <xs:documentation>Represents a user in the application.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attributeGroup ref=\"security:user.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"user.attlist\">\n    <xs:attribute name=\"name\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The username assigned to the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"password\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password assigned to the user. This may be hashed if the corresponding\n          authentication provider supports hashing (remember to set the \"hash\" attribute of the\n          \"user-service\" element).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>One of more authorities granted to the user. Separate authorities with a\n          comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"locked\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as locked and\n        unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n    <xs:annotation>\n      <xs:documentation>Causes creation of a JDBC-based UserDetailsService.</xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the\n            context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The bean ID of the DataSource which provides the required\n        tables.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a\n        UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"users-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query a username, password, and enabled status given a\n          username</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query for a user's granted authorities given a\n          username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query user's group authorities given a\n        username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:group name=\"custom-filter\">\n    <xs:sequence>\n      <xs:element minOccurs=\"0\" ref=\"security:custom-filter\"/>\n    </xs:sequence>\n  </xs:group>\n  <xs:element name=\"custom-filter\">\n    <xs:annotation>\n      <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into\n        the security filter chain. If neither the 'after' or 'before' options are supplied, then the\n        filter must implement the Ordered interface directly. </xs:documentation>\n    </xs:annotation>\n    <xs:complexType>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The filter immediately after which the custom-filter should be placed in\n            the chain. This feature will only be needed by advanced users who wish to mix their own\n            filters into the security filter chain and have some knowledge of the standard Spring\n            Security filters. The filter names map to specific Spring Security implementation\n            filters. </xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The filter immediately before which the custom-filter should be placed\n            in the chain</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n        <xs:annotation>\n          <xs:documentation>The explicit position at which the custom-filter should be placed in the\n            chain. Use if you are replacing a standard filter.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType>\n  </xs:element>\n  <xs:attributeGroup name=\"after\">\n    <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately after which the custom-filter should be placed in\n          the chain. This feature will only be needed by advanced users who wish to mix their own\n          filters into the security filter chain and have some knowledge of the standard Spring\n          Security filters. The filter names map to specific Spring Security implementation filters.\n        </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n    <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately before which the custom-filter should be placed in\n          the chain</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n    <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The explicit position at which the custom-filter should be placed in the\n          chain. Use if you are replacing a standard filter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"FIRST\"/>\n      <xs:enumeration value=\"CHANNEL_FILTER\"/>\n      <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n      <xs:enumeration value=\"SESSION_CONTEXT_INTEGRATION_FILTER\"/>\n      <xs:enumeration value=\"LOGOUT_FILTER\"/>\n      <xs:enumeration value=\"X509_FILTER\"/>\n      <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n      <xs:enumeration value=\"CAS_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"AUTHENTICATION_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"BASIC_PROCESSING_FILTER\"/>\n      <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n      <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n      <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n      <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n      <xs:enumeration value=\"NTLM_FILTER\"/>\n      <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n      <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n      <xs:enumeration value=\"LAST\"/>\n    </xs:restriction>\n  </xs:simpleType>\n</xs:schema>\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-3.0.3.xsd",
    "content": "<?xml version=\"1.0\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:security=\"http://www.springframework.org/schema/security\" elementFormDefault=\"qualified\" targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n    <xs:attribute name=\"hash\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n    <xs:attribute name=\"base64\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"path-type\">\n    <xs:attribute name=\"path-type\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n    <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n    <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n    <xs:attribute name=\"id\" use=\"required\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n    <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n    <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n    <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a DataSource bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"password-encoder.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"base64\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"user-property\">\n    <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n    <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A single value that will be used as the salt for a password encoder.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"boolean\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"true\"/>\n      <xs:enumeration value=\"false\"/>\n    </xs:restriction>\n  </xs:simpleType>\n  <xs:attributeGroup name=\"role-prefix\">\n    <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n    <xs:attribute name=\"use-expressions\" use=\"required\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\"><xs:annotation>\n      <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-password\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password for the manager DN.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"ldif\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"root\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n    <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n    <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n    <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n    <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n    <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n    <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n    <xs:attribute name=\"user-details-class\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n    <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"server-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n    <xs:attribute name=\"server-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"password-compare.attlist\">\n    <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The attribute in the directory which contains the user password. Defaults to \"userPassword\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\"><xs:annotation>\n      <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"protect\"><xs:annotation>\n      <xs:documentation>Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:protect.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security interceptor.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"protect.attlist\">\n    <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A method name</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\"><xs:annotation>\n      <xs:documentation>Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:choice minOccurs=\"0\">\n          <xs:element name=\"pre-post-annotation-handling\"><xs:annotation>\n      <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element name=\"invocation-attribute-factory\"><xs:annotation>\n      <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"pre-invocation-advice\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"post-invocation-advice\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n    </xs:complexType></xs:element>\n          <xs:element name=\"expression-handler\"><xs:annotation>\n      <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n        </xs:choice>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\"><xs:annotation>\n      <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\"><xs:annotation>\n      <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n    <xs:attribute name=\"pre-post-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"secured-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"jsr250-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"order\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"proxy-target-class\" type=\"security:boolean\"/>\n    <xs:attribute name=\"mode\">\n      <xs:annotation>\n        <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"aspectj\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n\n\n\n\n\n\n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n    <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http\"><xs:annotation>\n      <xs:documentation>Container element for HTTP security configuration</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"intercept-url\"><xs:annotation>\n      <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"access-denied-handler\"><xs:annotation>\n      <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"form-login\"><xs:annotation>\n      <xs:documentation>Sets up a form login configuration for authentication with a username and password</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"openid-login\"><xs:annotation>\n      <xs:documentation>Sets up form login for authentication with an Open ID identity</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" ref=\"security:attribute-exchange\"/>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType></xs:element>\n        <xs:element name=\"x509\"><xs:annotation>\n      <xs:documentation>Adds support for X.509 client authentication.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:x509.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"http-basic\"><xs:annotation>\n      <xs:documentation>Adds support for basic authentication (this is an element to permit future expansion, such as supporting an \"ignoreFailure\" attribute)</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"logout\"><xs:annotation>\n      <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:logout.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"session-management\"><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"concurrency-control\"><xs:annotation>\n      <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"remember-me\"><xs:annotation>\n      <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"anonymous\"><xs:annotation>\n      <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"port-mappings\"><xs:annotation>\n      <xs:documentation>Defines the list of mappings between http and https ports for use in redirects</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:http-port\"/>\n      <xs:attributeGroup ref=\"security:https-port\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n    </xs:complexType></xs:element>\n        <xs:element ref=\"security:custom-filter\"/>\n        <xs:element ref=\"security:request-cache\"/>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:http.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n    <xs:attribute name=\"auto-config\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Automatically registers a login form, BASIC authentication, anonymous authentication, logout services, remember-me and servlet-api-integration. If set to \"true\", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to \"false\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"use-expressions\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"create-session\">\n      <xs:annotation>\n        <xs:documentation>Controls the eagerness with which an HTTP session is created. If not set, defaults to \"ifRequired\". Note that if a custom SecurityContextRepository is set using security-context-repository-ref, then the only value which can be set is \"always\". Otherwise the session creation behaviour will be determined by the repository bean implementation.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ifRequired\"/>\n          <xs:enumeration value=\"always\"/>\n          <xs:enumeration value=\"never\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether test URLs should be converted to lower case prior to comparing with defined path patterns. If unspecified, defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"servlet-api-provision\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"realm\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"once-per-request\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-denied-page\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Deprecated in favour of the access-denied-handler element.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"disable-url-rewriting\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation/>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"error-page\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n    <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"intercept-url.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The pattern which defines the URL path. The content will depend on the type set in the containing http element, so will default to ant path syntax.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The access configuration attributes that apply for the configured path.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"method\">\n      <xs:annotation>\n        <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"GET\"/>\n          <xs:enumeration value=\"DELETE\"/>\n          <xs:enumeration value=\"HEAD\"/>\n          <xs:enumeration value=\"OPTIONS\"/>\n          <xs:enumeration value=\"POST\"/>\n          <xs:enumeration value=\"PUT\"/>\n          <xs:enumeration value=\"TRACE\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"filters\">\n      <xs:annotation>\n        <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"logout.attlist\">\n    <xs:attribute name=\"logout-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to /.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"invalidate-session\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\"><xs:annotation>\n      <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n\n  <xs:attributeGroup name=\"form-login.attlist\">\n    <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"always-use-default-target\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether the user should always be redirected to the default-target-url after login.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"login-page\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at /spring_security_login and a corresponding filter to render that login URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /spring_security_login?login_error and a corresponding filter to render that login failure URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:element name=\"attribute-exchange\"><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n      </xs:sequence>\n    </xs:complexType></xs:element>\n  <xs:element name=\"openid-attribute\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n    <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\"/>\n    <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\"/>\n    <xs:attribute name=\"required\" type=\"security:boolean\"/>\n    <xs:attribute name=\"count\" type=\"xs:int\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\"><xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"filter-chain\"><xs:annotation>\n      <xs:documentation>Used within filter-chain-map to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are used within a filter-chain-map element, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n    <xs:attributeGroup ref=\"security:path-type\"/>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\"/>\n    <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\"><xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\"><xs:annotation>\n      <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n    <xs:attribute name=\"use-expressions\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>as for http element</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-invocation-definition-source\"><xs:annotation>\n      <xs:documentation>Deprecated synonym for filter-security-metadata-source</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\"><xs:annotation>\n      <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n    </xs:complexType></xs:element>\n\n  <xs:attributeGroup name=\"http-basic.attlist\">\n    <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"session-management.attlist\">\n    <xs:attribute name=\"session-fixation-protection\">\n      <xs:annotation>\n        <xs:documentation>Indicates whether an existing session should be invalidated when a user authenticates and a new session started. If set to \"none\" no change will be made. \"newSession\" will create a new empty session. \"migrateSession\" will create a new session and copy the session attributes to the new session. Defaults to \"migrateSession\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n          <xs:enumeration value=\"newSession\"/>\n          <xs:enumeration value=\"migrateSession\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (402) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n    <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\">\n      <xs:annotation>\n        <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"expired-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"error-if-maximum-exceeded\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"remember-me.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a DataSource bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"services-alias\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"use-secure-cookie\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS. Defaults to false.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"token-validity-seconds\" type=\"xs:integer\">\n      <xs:annotation>\n        <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n    <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n    <xs:attribute name=\"services-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n    <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"anonymous.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to \"doesNotMatter\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"username\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"enabled\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n\n  <xs:attributeGroup name=\"http-port\">\n    <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n    <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\"/>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"x509.attlist\">\n    <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\"><xs:annotation>\n      <xs:documentation>Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"authentication-provider\"><xs:annotation>\n      <xs:documentation>Indicates that the contained user-service should be used as an authentication source.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element ref=\"security:any-user-service\"/>\n        <xs:element name=\"password-encoder\"><xs:annotation>\n      <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"salt-source\"><xs:annotation>\n      <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attribute name=\"user-property\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"system-wide\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A single value that will be used as the salt for a password encoder.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:ap.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"ldap-authentication-provider\"><xs:annotation>\n      <xs:documentation>Sets up an ldap authentication provider</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"password-compare\"><xs:annotation>\n      <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"password-encoder\"><xs:annotation>\n      <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"salt-source\"><xs:annotation>\n      <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attribute name=\"user-property\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"system-wide\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A single value that will be used as the salt for a password encoder.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:authman.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n    <xs:attribute name=\"alias\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>The alias you wish to use for the AuthenticationManager bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"erase-credentials\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>If set to true, the AuthenticationManager will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated. Defaults to false.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"ap.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\"><xs:annotation>\n      <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\"><xs:annotation>\n      <xs:documentation>Represents a user in the application.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:user.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:properties-file\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n    <xs:attribute name=\"properties\" type=\"xs:token\"/>\n  </xs:attributeGroup>\n\n  <xs:attributeGroup name=\"user.attlist\">\n    <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The username assigned to the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"password\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"locked\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"disabled\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\"><xs:annotation>\n      <xs:documentation>Causes creation of a JDBC-based UserDetailsService.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The bean ID of the DataSource which provides the required tables.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query a username, password, and enabled status given a username</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query for a user's granted authorities given a username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query user's group authorities given a username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\"><xs:annotation>\n      <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security filter chain.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n    <xs:attributeGroup ref=\"security:ref\"/>\n    <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n    <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n    <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n    <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"FIRST\"/>\n      <xs:enumeration value=\"CHANNEL_FILTER\"/>\n      <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n      <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n      <xs:enumeration value=\"LOGOUT_FILTER\"/>\n      <xs:enumeration value=\"X509_FILTER\"/>\n      <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n      <xs:enumeration value=\"CAS_FILTER\"/>\n      <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n      <xs:enumeration value=\"OPENID_FILTER\"/>\n      <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n      <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n      <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n      <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n      <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n      <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n      <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n      <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n      <xs:enumeration value=\"LAST\"/>\n    </xs:restriction>\n  </xs:simpleType>\n</xs:schema>\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-3.0.xsd",
    "content": "<?xml version=\"1.0\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:security=\"http://www.springframework.org/schema/security\" elementFormDefault=\"qualified\" targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n    <xs:attribute name=\"hash\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n    <xs:attribute name=\"base64\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"path-type\">\n    <xs:attribute name=\"path-type\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n    <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n    <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n    <xs:attribute name=\"id\" use=\"required\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n    <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n    <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n    <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a DataSource bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"base64\">\n      <xs:annotation>\n        <xs:documentation>Whether a string should be base64 encoded</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"true\"/>\n          <xs:enumeration value=\"false\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user-property\">\n    <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n    <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A single value that will be used as the salt for a password encoder. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"boolean\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"true\"/>\n      <xs:enumeration value=\"false\"/>\n    </xs:restriction>\n  </xs:simpleType>\n  <xs:attributeGroup name=\"role-prefix\">\n    <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n    <xs:attribute name=\"use-expressions\" use=\"required\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\"><xs:annotation>\n      <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied. </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Specifies a URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n      <xs:annotation>\n        <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"manager-password\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password for the manager DN.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"ldif\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"root\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n    <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n    <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n    <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n    <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n    <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n    <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n    <xs:attribute name=\"user-details-class\" use=\"required\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n    <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"server-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n    <xs:attribute name=\"server-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using &lt;ldap-server&gt; with no Id), that server will be used. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-details-class\">\n      <xs:annotation>\n        <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"person\"/>\n          <xs:enumeration value=\"inetOrgPerson\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n    <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The attribute in the directory which contains the user password. Defaults to \"userPassword\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"hash\">\n      <xs:annotation>\n        <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"plaintext\"/>\n          <xs:enumeration value=\"sha\"/>\n          <xs:enumeration value=\"sha-256\"/>\n          <xs:enumeration value=\"md5\"/>\n          <xs:enumeration value=\"md4\"/>\n          <xs:enumeration value=\"{sha}\"/>\n          <xs:enumeration value=\"{ssha}\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\"><xs:annotation>\n      <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"protect\"><xs:annotation>\n      <xs:documentation>Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:protect.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security interceptor.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n    <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A method name</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\"><xs:annotation>\n      <xs:documentation>Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:choice minOccurs=\"0\">\n          <xs:element name=\"pre-post-annotation-handling\"><xs:annotation>\n      <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled. </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element name=\"invocation-attribute-factory\"><xs:annotation>\n      <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.  </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"pre-invocation-advice\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"post-invocation-advice\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n    </xs:complexType></xs:element>\n          <xs:element name=\"expression-handler\"><xs:annotation>\n      <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n        </xs:choice>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\"><xs:annotation>\n      <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\"><xs:annotation>\n      <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n    <xs:attribute name=\"pre-post-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"secured-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"jsr250-annotations\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"disabled\"/>\n          <xs:enumeration value=\"enabled\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"order\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"proxy-target-class\" type=\"security:boolean\"/>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n    <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http\"><xs:annotation>\n      <xs:documentation>Container element for HTTP security configuration</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"intercept-url\"><xs:annotation>\n      <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"access-denied-handler\"><xs:annotation>\n      <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance. </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"form-login\"><xs:annotation>\n      <xs:documentation>Sets up a form login configuration for authentication with a username and password</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"openid-login\"><xs:annotation>\n      <xs:documentation>Sets up form login for authentication with an Open ID identity</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" ref=\"security:attribute-exchange\"/>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType></xs:element>\n        <xs:element name=\"x509\"><xs:annotation>\n      <xs:documentation>Adds support for X.509 client authentication.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:x509.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"http-basic\"><xs:annotation>\n      <xs:documentation>Adds support for basic authentication (this is an element to permit future expansion, such as supporting an \"ignoreFailure\" attribute)</xs:documentation>\n    </xs:annotation><xs:complexType/></xs:element>\n        <xs:element name=\"logout\"><xs:annotation>\n      <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:logout.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"session-management\"><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"concurrency-control\"><xs:annotation>\n      <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"remember-me\"><xs:annotation>\n      <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.     </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"anonymous\"><xs:annotation>\n      <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"port-mappings\"><xs:annotation>\n      <xs:documentation>Defines the list of mappings between http and https ports for use in redirects</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:http-port\"/>\n      <xs:attributeGroup ref=\"security:https-port\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n    </xs:complexType></xs:element>\n        <xs:element ref=\"security:custom-filter\"/>\n        <xs:element ref=\"security:request-cache\"/>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:http.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n    <xs:attribute name=\"auto-config\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Automatically registers a login form, BASIC authentication, anonymous authentication, logout services, remember-me and servlet-api-integration. If set to \"true\", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to \"false\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"use-expressions\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"create-session\">\n      <xs:annotation>\n        <xs:documentation>Controls the eagerness with which an HTTP session is created. If not set, defaults to \"ifRequired\". Note that if a custom SecurityContextRepository is set using security-context-repository-ref, then the only value which can be set is \"always\". Otherwise the session creation behaviour will be determined by the repository bean implementation.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ifRequired\"/>\n          <xs:enumeration value=\"always\"/>\n          <xs:enumeration value=\"never\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether test URLs should be converted to lower case prior to comparing with defined path patterns. If unspecified, defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"servlet-api-provision\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"realm\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows a customized AuthenticationEntryPoint to be used.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"once-per-request\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access-denied-page\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Deprecated in favour of the access-denied-handler element.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"disable-url-rewriting\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation> </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"error-page\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n    <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The pattern which defines the URL path. The content will depend on the type set in the containing http element, so will default to ant path syntax.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"access\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The access configuration attributes that apply for the configured path.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"method\">\n      <xs:annotation>\n        <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"GET\"/>\n          <xs:enumeration value=\"DELETE\"/>\n          <xs:enumeration value=\"HEAD\"/>\n          <xs:enumeration value=\"OPTIONS\"/>\n          <xs:enumeration value=\"POST\"/>\n          <xs:enumeration value=\"PUT\"/>\n          <xs:enumeration value=\"TRACE\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"filters\">\n      <xs:annotation>\n        <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n    <xs:attribute name=\"logout-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to /.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"invalidate-session\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\"><xs:annotation>\n      <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:ref\"/>\n    </xs:complexType></xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n    <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"always-use-default-target\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Whether the user should always be redirected to the default-target-url after login. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"login-page\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at /spring_security_login and a corresponding filter to render that login URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /spring_security_login?login_error and a corresponding filter to render that login failure URL when requested.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\"><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n      </xs:sequence>\n    </xs:complexType></xs:element>\n  <xs:element name=\"openid-attribute\"><xs:complexType>\n      <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n    <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\"/>\n    <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\"/>\n    <xs:attribute name=\"required\" type=\"security:boolean\"/>\n    <xs:attribute name=\"count\" type=\"xs:int\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\"><xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"filter-chain\"><xs:annotation>\n      <xs:documentation>Used within filter-chain-map to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are used within a filter-chain-map element, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n    <xs:attributeGroup ref=\"security:path-type\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"filter-chain.attlist\">\n    <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\"/>\n    <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\"/>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\"><xs:annotation>\n      <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error. </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\"><xs:annotation>\n      <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n    <xs:attribute name=\"use-expressions\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"id\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"lowercase-comparisons\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>as for http element</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"path-type\">\n      <xs:annotation>\n        <xs:documentation>Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to \"ant\" if unspecified.</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"ant\"/>\n          <xs:enumeration value=\"regex\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-invocation-definition-source\"><xs:annotation>\n      <xs:documentation>Deprecated synonym for filter-security-metadata-source</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\"><xs:annotation>\n      <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n    </xs:complexType></xs:element>\n  \n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n    <xs:attribute name=\"session-fixation-protection\">\n      <xs:annotation>\n        <xs:documentation>Indicates whether an existing session should be invalidated when a user authenticates and a new session started. If set to \"none\" no change will be made. \"newSession\" will create a new empty session. \"migrateSession\" will create a new session and copy the session attributes to the new session. Defaults to \"migrateSession\".</xs:documentation>\n      </xs:annotation>\n      <xs:simpleType>\n        <xs:restriction base=\"xs:token\">\n          <xs:enumeration value=\"none\"/>\n          <xs:enumeration value=\"newSession\"/>\n          <xs:enumeration value=\"migrateSession\"/>\n        </xs:restriction>\n      </xs:simpleType>\n    </xs:attribute>\n    <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (402) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n    <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\">\n      <xs:annotation>\n        <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"expired-url\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"error-if-maximum-exceeded\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a DataSource bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"services-alias\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"use-secure-cookie\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS. Defaults to false.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"token-validity-seconds\" type=\"xs:integer\">\n      <xs:annotation>\n        <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n    <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n    <xs:attribute name=\"services-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n    <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n    <xs:attribute name=\"key\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to \"doesNotMatter\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"username\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"enabled\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n    <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\"/>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n    <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n    <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\"><xs:annotation>\n      <xs:documentation>Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans. </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element name=\"authentication-provider\"><xs:annotation>\n      <xs:documentation>Indicates that the contained user-service should be used as an authentication source. </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n        <xs:element ref=\"security:any-user-service\"/>\n        <xs:element name=\"password-encoder\"><xs:annotation>\n      <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"salt-source\"><xs:annotation>\n      <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attribute name=\"user-property\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used. </xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"system-wide\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A single value that will be used as the salt for a password encoder. </xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:ap.attlist\"/>\n    </xs:complexType></xs:element>\n        <xs:element name=\"ldap-authentication-provider\"><xs:annotation>\n      <xs:documentation>Sets up an ldap authentication provider</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"password-compare\"><xs:annotation>\n      <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"password-encoder\"><xs:annotation>\n      <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" name=\"salt-source\"><xs:annotation>\n      <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attribute name=\"user-property\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used. </xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"system-wide\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>A single value that will be used as the salt for a password encoder. </xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n        <xs:annotation>\n          <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:choice>\n      <xs:attributeGroup ref=\"security:authman.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n    <xs:attribute name=\"alias\" type=\"xs:ID\">\n      <xs:annotation>\n        <xs:documentation>The alias you wish to use for the AuthenticationManager bean</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n    <xs:attribute name=\"ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a Spring bean Id.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\"><xs:annotation>\n      <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:sequence>\n        <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\"><xs:annotation>\n      <xs:documentation>Represents a user in the application.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:user.attlist\"/>\n    </xs:complexType></xs:element>\n      </xs:sequence>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:properties-file\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n    <xs:attribute name=\"properties\" type=\"xs:token\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n    <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The username assigned to the user.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"password\" type=\"xs:string\">\n      <xs:annotation>\n        <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"locked\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"disabled\" type=\"security:boolean\">\n      <xs:annotation>\n        <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\"><xs:annotation>\n      <xs:documentation>Causes creation of a JDBC-based UserDetailsService.</xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attribute name=\"id\" type=\"xs:ID\">\n        <xs:annotation>\n          <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>\n        </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n    <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>The bean ID of the DataSource which provides the required tables.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query a username, password, and enabled status given a username</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query for a user's granted authorities given a username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>An SQL statement to query user's group authorities given a username.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n      <xs:annotation>\n        <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\"><xs:annotation>\n      <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security filter chain. </xs:documentation>\n    </xs:annotation><xs:complexType>\n      <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n    </xs:complexType></xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n    <xs:attributeGroup ref=\"security:ref\"/>\n    <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n    <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n    <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters. </xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n    <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n    <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n      <xs:annotation>\n        <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.</xs:documentation>\n      </xs:annotation>\n    </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n    <xs:restriction base=\"xs:token\">\n      <xs:enumeration value=\"FIRST\"/>\n      <xs:enumeration value=\"CHANNEL_FILTER\"/>\n      <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n      <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n      <xs:enumeration value=\"LOGOUT_FILTER\"/>\n      <xs:enumeration value=\"X509_FILTER\"/>\n      <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n      <xs:enumeration value=\"CAS_FILTER\"/>\n      <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n      <xs:enumeration value=\"OPENID_FILTER\"/>\n      <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n      <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n      <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n      <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n      <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n      <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n      <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n      <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n      <xs:enumeration value=\"LAST\"/>\n    </xs:restriction>\n  </xs:simpleType>\n</xs:schema>\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n    ## Defines the hashing algorithm used on user passwords. We recommend strongly against using MD4, as it is a very weak hashing algorithm.\n    attribute hash {\"plaintext\" | \"sha\" | \"sha-256\" | \"md5\" | \"md4\" | \"{sha}\" | \"{ssha}\"}\nbase64 =\n    ## Whether a string should be base64 encoded\n    attribute base64 {xsd:boolean}\nrequest-matcher =\n    ## Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n    attribute request-matcher {\"ant\" | \"regex\" | \"ciRegex\"}\npath-type =\n    ## Deprecated. Use request-matcher instead.\n    attribute path-type {\"ant\" | \"regex\"}\nport =\n    ## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n    attribute port { xsd:positiveInteger }\nurl =\n    ## Specifies a URL.\n    attribute url { xsd:token }\nid =\n    ## A bean identifier, used for referring to the bean elsewhere in the context.\n    attribute id {xsd:token}\nname =\n    ## A bean identifier, used for referring to the bean elsewhere in the context.\n    attribute name {xsd:token}\nref =\n    ## Defines a reference to a Spring bean Id.\n    attribute ref {xsd:token}\n\ncache-ref =\n    ## Defines a reference to a cache for use with a UserDetailsService.\n    attribute cache-ref {xsd:token}\n\nuser-service-ref =\n    ## A reference to a user-service (or UserDetailsService bean) Id\n    attribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n    ## A reference to an AuthenticationManager bean\n    attribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n    ## A reference to a DataSource bean\n    attribute data-source-ref {xsd:token}\n\n\n\ndebug =\n    ## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n    element debug {empty}\n\npassword-encoder =\n    ## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n    element password-encoder {password-encoder.attlist, salt-source?}\npassword-encoder.attlist &=\n    ref | (hash? & base64?)\n\nsalt-source =\n    ## Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.\n    element salt-source {user-property | system-wide | ref}\nuser-property =\n    ## A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used.\n    attribute user-property {xsd:token}\nsystem-wide =\n    ## A single value that will be used as the salt for a password encoder.\n    attribute system-wide {xsd:token}\n\nrole-prefix =\n    ## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n    attribute role-prefix {xsd:token}\n\nuse-expressions =\n    ## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n    attribute use-expressions {xsd:boolean}\n\nldap-server =\n    ## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n    element ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n    ## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n    attribute manager-dn {xsd:string}?\nldap-server.attlist &=\n    ## The password for the manager DN. This is required if the manager-dn is specified.\n    attribute manager-password {xsd:string}?\nldap-server.attlist &=\n    ## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n    attribute ldif { xsd:string }?\nldap-server.attlist &=\n    ## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n    attribute root { xsd:string }?\n\nldap-server-ref-attribute =\n    ## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n    attribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n    ## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n    attribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n    ## Search base for group membership searches. Defaults to \"\" (searching from the root).\n    attribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n    ## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n    attribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n    ## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n    attribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n    ## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n    attribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n    ## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n    attribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n    ## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n    attribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n    ## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n    element ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n    ldap-server-ref-attribute?\nldap-us.attlist &=\n    user-search-filter-attribute?\nldap-us.attlist &=\n    user-search-base-attribute?\nldap-us.attlist &=\n    group-search-filter-attribute?\nldap-us.attlist &=\n    group-search-base-attribute?\nldap-us.attlist &=\n    group-role-attribute-attribute?\nldap-us.attlist &=\n    cache-ref?\nldap-us.attlist &=\n    role-prefix?\nldap-us.attlist &=\n    (user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n    ## Sets up an ldap authentication provider\n    element ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n    ldap-server-ref-attribute?\nldap-ap.attlist &=\n    user-search-base-attribute?\nldap-ap.attlist &=\n    user-search-filter-attribute?\nldap-ap.attlist &=\n    group-search-base-attribute?\nldap-ap.attlist &=\n    group-search-filter-attribute?\nldap-ap.attlist &=\n    group-role-attribute-attribute?\nldap-ap.attlist &=\n    ## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n    attribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n    role-prefix?\nldap-ap.attlist &=\n    (user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n    ## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n    element password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n    ## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n    attribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n    hash?\n\nintercept-methods =\n    ## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n    element intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n    ## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n    attribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n    ## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n    element protect {protect.attlist, empty}\nprotect.attlist &=\n    ## A method name\n    attribute method {xsd:token}\nprotect.attlist &=\n    ## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n    attribute access {xsd:token}\n\nmethod-security-metadata-source =\n    ## Creates a MethodSecurityMetadataSource instance\n    element method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n    ## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n    element global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n    ## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n    attribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n    ## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n    attribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n    ## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n    attribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n    ## Optional AccessDecisionManager bean ID to override the default used for method security.\n    attribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n    ## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n    attribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n    ## Allows the advice \"order\" to be set for the method security interceptor.\n    attribute order {xsd:token}?\nglobal-method-security.attlist &=\n    ## If true, class based proxying will be used instead of interface based proxying.\n    attribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n    ## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n    attribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n    ## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n    attribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n    authentication-manager-ref?\n\n\nafter-invocation-provider =\n    ## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n    element after-invocation-provider {ref}\n\npre-post-annotation-handling =\n    ## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n    element pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n    ## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n    element invocation-attribute-factory {ref}\n\npre-invocation-advice =\n    ## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n    element pre-invocation-advice {ref}\n\npost-invocation-advice =\n    ## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n    element post-invocation-advice {ref}\n\n\nexpression-handler =\n    ## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n    element expression-handler {ref}\n\nprotect-pointcut =\n    ## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n    element protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n    ## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n    attribute expression {xsd:string}\nprotect-pointcut.attlist &=\n    ## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n    attribute access {xsd:token}\n\nhttp-firewall =\n    ## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n    element http-firewall {ref}\n\nhttp =\n    ## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n    element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler?) }\nhttp.attlist &=\n    ## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n    attribute pattern {xsd:token}?\nhttp.attlist &=\n    ## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n    attribute security {\"none\"}?\nhttp.attlist &=\n    ## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n    attribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n    ## Automatically registers a login form, BASIC authentication, anonymous authentication, logout services, remember-me and servlet-api-integration. If set to \"true\", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to \"false\".\n    attribute auto-config {xsd:boolean}?\nhttp.attlist &=\n    use-expressions?\nhttp.attlist &=\n    ## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n    attribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n    ## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n    attribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n    request-matcher?\nhttp.attlist &=\n    ## Deprecated. Use request-matcher instead.\n    path-type?\nhttp.attlist &=\n    ## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n    attribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n    ## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n    attribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n    ## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n    attribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n    ## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n    attribute realm {xsd:token}?\nhttp.attlist &=\n    ## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n    attribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n    ## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n    attribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n    ## Deprecated in favour of the access-denied-handler element.\n    attribute access-denied-page {xsd:token}?\nhttp.attlist &=\n    ## Prevents the jsessionid parameter from being added to rendered URLs.\n    attribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n    ## Exposes the list of filters defined by this configuration under this bean name in the application context.\n    name?\nhttp.attlist &=\n    authentication-manager-ref?\n\naccess-denied-handler =\n    ## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n    element access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n    ## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n    attribute error-page {xsd:token}\n\nintercept-url =\n    ## Specifies the access attributes and/or filter list for a particular set of URLs.\n    element intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n    ## The pattern which defines the URL path. The content will depend on the type set in the containing http element, so will default to ant path syntax.\n    attribute pattern {xsd:token}\nintercept-url.attlist &=\n    ## The access configuration attributes that apply for the configured path.\n    attribute access {xsd:token}?\nintercept-url.attlist &=\n    ## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n    attribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n    ## The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.\n    attribute filters {\"none\"}?\nintercept-url.attlist &=\n    ## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n    attribute requires-channel {xsd:token}?\n\nlogout =\n    ## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n    element logout {logout.attlist, empty}\nlogout.attlist &=\n    ## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n    attribute logout-url {xsd:token}?\nlogout.attlist &=\n    ## Specifies the URL to display once the user has logged out. If not specified, defaults to /.\n    attribute logout-success-url {xsd:token}?\nlogout.attlist &=\n    ## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n    attribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n    ## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n    attribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n    ## A comma-separated list of the names of cookies which should be deleted when the user logs out\n    attribute delete-cookies {xsd:token}?\n\nrequest-cache =\n    ## Allow the RequestCache used for saving requests during the login process to be set\n    element request-cache {ref}\n\nform-login =\n    ## Sets up a form login configuration for authentication with a username and password\n    element form-login {form-login.attlist, empty}\nform-login.attlist &=\n    ## The URL that the login form is posted to. If unspecified, it defaults to /login.\n    attribute login-processing-url {xsd:token}?\nform-login.attlist &=\n    ## The name of the request parameter which contains the username. Defaults to 'username'.\n    attribute username-parameter {xsd:token}?\nform-login.attlist &=\n    ## The name of the request parameter which contains the password. Defaults to 'password'.\n    attribute password-parameter {xsd:token}?\nform-login.attlist &=\n    ## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n    attribute default-target-url {xsd:token}?\nform-login.attlist &=\n    ## Whether the user should always be redirected to the default-target-url after login.\n    attribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n    ## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at /spring_security_login and a corresponding filter to render that login URL when requested.\n    attribute login-page {xsd:token}?\nform-login.attlist &=\n    ## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /spring_security_login?login_error and a corresponding filter to render that login failure URL when requested.\n    attribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n    ## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n    attribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n    ## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n    attribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n    ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n    attribute authentication-details-source-ref {xsd:token}?\n\n\nopenid-login =\n    ## Sets up form login for authentication with an Open ID identity\n    element openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n    ## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n    element attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n    ## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n    attribute identifier-match {xsd:token}?\n\nopenid-attribute =\n    ## Attributes used when making an OpenID AX Fetch Request\n    element openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n    ## Specifies the name of the attribute that you wish to get back. For example, email.\n    attribute name {xsd:token}\nopenid-attribute.attlist &=\n    ## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n    attribute type {xsd:token}\nopenid-attribute.attlist &=\n    ## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n    attribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n    ## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n    attribute count {xsd:int}?\n\n\nfilter-chain-map =\n    ## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n    element filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n    ## Deprecated. Use request-matcher instead.\n    path-type?\nfilter-chain-map.attlist &=\n    request-matcher?\n\nfilter-chain =\n    ## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n    element filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n    (pattern | request-matcher-ref)\nfilter-chain.attlist &=\n    ## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n    attribute filters {xsd:token}\n\npattern =\n    ## The request URL pattern which will be mapped to the FilterChain.\n    attribute pattern {xsd:token}\nrequest-matcher-ref =\n    ## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n    attribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n    ## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n    element filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n    use-expressions?\nfsmds.attlist &=\n    id?\nfsmds.attlist &=\n    ## Compare after forcing to lowercase\n    attribute lowercase-comparisons {xsd:boolean}?\nfsmds.attlist &=\n    ## Deprecate. Use request-matcher instead.\n    path-type?\nfsmds.attlist &=\n    request-matcher?\n\nfilter-invocation-definition-source =\n    ## Deprecated synonym for filter-security-metadata-source\n    element filter-invocation-definition-source {fsmds.attlist, intercept-url+}\n\nhttp-basic =\n    ## Adds support for basic authentication\n    element http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n    ## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n    attribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n    ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n    attribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n    ## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n    element session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n    ## Indicates whether an existing session should be invalidated when a user authenticates and a new session started. If set to \"none\" no change will be made. \"newSession\" will create a new empty session. \"migrateSession\" will create a new session and copy the session attributes to the new session. Defaults to \"migrateSession\".\n    attribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" }?\nsession-management.attlist &=\n    ## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n    attribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n    ## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n    attribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n    ## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n    attribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n    ## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n    element concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n    ## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\".\n    attribute max-sessions {xsd:positiveInteger}?\nconcurrency-control.attlist &=\n    ## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n    attribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n    ## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n    attribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n    ## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n    attribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n    ## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n    attribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n    ## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n    element remember-me {remember-me.attlist}\nremember-me.attlist &=\n    ## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application.\n    attribute key {xsd:token}?\n\nremember-me.attlist &=\n    (token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n    user-service-ref?\n\nremember-me.attlist &=\n    ## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n    attribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n    ## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n    attribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n    ## The period (in seconds) for which the remember-me cookie should be valid.\n    attribute token-validity-seconds {xsd:integer}?\n\nremember-me.attlist &=\n    ## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n    attribute authentication-success-handler-ref {xsd:token}?\n\n\ntoken-repository-ref =\n    ## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n    attribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n    ## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n    attribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n    ## DataSource bean for the database that contains the token repository schema.\n    data-source-ref\n\nanonymous =\n    ## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n    element anonymous {anonymous.attlist}\nanonymous.attlist &=\n    ## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to \"doesNotMatter\".\n    attribute key {xsd:token}?\nanonymous.attlist &=\n    ## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n    attribute username {xsd:token}?\nanonymous.attlist &=\n    ## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n    attribute granted-authority {xsd:token}?\nanonymous.attlist &=\n    ## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n    attribute enabled {xsd:boolean}?\n\n\nport-mappings =\n    ## Defines the list of mappings between http and https ports for use in redirects\n    element port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n    ## Provides a method to map http ports to https ports when forcing a redirect.\n    element port-mapping {http-port, https-port}\n\nhttp-port =\n    ## The http port to use.\n    attribute http {xsd:token}\n\nhttps-port =\n    ## The https port to use.\n    attribute https {xsd:token}\n\n\nx509 =\n    ## Adds support for X.509 client authentication.\n    element x509 {x509.attlist}\nx509.attlist &=\n    ## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n    attribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n    ## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n    user-service-ref?\nx509.attlist &=\n    ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n    attribute authentication-details-source-ref {xsd:token}?\n\njee =\n    ## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n    element jee {jee.attlist}\njee.attlist &=\n    ## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n    attribute mappable-roles {xsd:token}\njee.attlist &=\n    ## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n    user-service-ref?\n\nauthentication-manager =\n    ## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n    element authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n    id?\nauthman.attlist &=\n    ## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n    attribute alias {xsd:token}?\nauthman.attlist &=\n    ## If set to true, the AuthenticationManager will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n    attribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n    ## Indicates that the contained user-service should be used as an authentication source.\n    element authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n    ## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n    ref?\nap.attlist &=\n    ## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n    user-service-ref?\n\nuser-service =\n    ## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n    element user-service {id? & (properties-file | (user*))}\nproperties-file =\n    ## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n    attribute properties {xsd:token}?\n\nuser =\n    ## Represents a user in the application.\n    element user {user.attlist, empty}\nuser.attlist &=\n    ## The username assigned to the user.\n    attribute name {xsd:token}\nuser.attlist &=\n    ## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n    attribute password {xsd:string}?\nuser.attlist &=\n    ## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n    attribute authorities {xsd:token}\nuser.attlist &=\n    ## Can be set to \"true\" to mark an account as locked and unusable.\n    attribute locked {xsd:boolean}?\nuser.attlist &=\n     ## Can be set to \"true\" to mark an account as disabled and unusable.\n    attribute disabled {xsd:boolean}?\n\njdbc-user-service =\n    ## Causes creation of a JDBC-based UserDetailsService.\n    element jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n    ## The bean ID of the DataSource which provides the required tables.\n    attribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n    cache-ref?\njdbc-user-service.attlist &=\n    ## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n    attribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n    ## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n    attribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n    ## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n    attribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n    role-prefix?\n\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n    ## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n    element custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n    ref\n\ncustom-filter.attlist &=\n    (after | before | position)\n\nafter =\n    ## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n    attribute after {named-security-filter}\nbefore =\n    ## The filter immediately before which the custom-filter should be placed in the chain\n    attribute before {named-security-filter}\nposition =\n    ## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n    attribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"LOGOUT_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" |\"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\""
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using\n                MD4, as it is a very weak hashing algorithm.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming\n                requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular\n                expressions and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"path-type\">\n      <xs:attribute name=\"path-type\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Deprecated. Use request-matcher instead.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using\n                MD4, as it is a very weak hashing algorithm.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"base64\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user-property\">\n      <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n      <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'false'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. We recommend strongly against using\n                MD4, as it is a very weak hashing algorithm.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'false'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Automatically registers a login form, BASIC authentication, anonymous authentication,\n                logout services, remember-me and servlet-api-integration. If set to \"true\", all of these\n                capabilities are added (although you can still customize the configuration of each by\n                providing the respective element). If unspecified, defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'false'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming\n                requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular\n                expressions and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"path-type\">\n         <xs:annotation>\n            <xs:documentation>Deprecated. Use request-matcher instead.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-denied-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Deprecated in favour of the access-denied-handler element.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The pattern which defines the URL path. The content will depend on the type set in the\n                containing http element, so will default to ant path syntax.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"filters\">\n         <xs:annotation>\n            <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having\n                any filters applied. The full filter stack (consisting of all filters created by the\n                namespace configuration, and any added using 'custom-filter'), will be applied to any\n                other paths.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                /.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at /spring_security_login and a corresponding filter to\n                render that login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /spring_security_login?login_error and a\n                corresponding filter to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"path-type\">\n         <xs:annotation>\n            <xs:documentation>Deprecated. Use request-matcher instead.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming\n                requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular\n                expressions and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'false'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"lowercase-comparisons\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Compare after forcing to lowercase\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"path-type\">\n         <xs:annotation>\n            <xs:documentation>Deprecated. Use request-matcher instead.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming\n                requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular\n                expressions and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-invocation-definition-source\">\n      <xs:annotation>\n         <xs:documentation>Deprecated synonym for filter-security-metadata-source\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates whether an existing session should be invalidated when a user authenticates and\n                a new session started. If set to \"none\" no change will be made. \"newSession\" will create a\n                new empty session. \"migrateSession\" will create a new session and copy the session\n                attributes to the new session. Defaults to \"migrateSession\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to \"doesNotMatter\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                 <xs:annotation>\n                                    <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"ref\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:sequence>\n                                       <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                          <xs:annotation>\n                                             <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                          </xs:annotation>\n                                          <xs:complexType>\n                                             <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"ref\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                          </xs:complexType>\n                                       </xs:element>\n                                    </xs:sequence>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManager will attempt to clear any credentials data in\n                the returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-3.2.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n    ## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n    attribute hash {\"bcrypt\" | \"plaintext\" | \"sha\" | \"sha-256\" | \"md5\" | \"md4\" | \"{sha}\" | \"{ssha}\"}\nbase64 =\n    ## Whether a string should be base64 encoded\n    attribute base64 {xsd:boolean}\nrequest-matcher =\n    ## Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n    attribute request-matcher {\"ant\" | \"regex\" | \"ciRegex\"}\npath-type =\n    ## Deprecated. Use request-matcher instead.\n    attribute path-type {\"ant\" | \"regex\"}\nport =\n    ## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n    attribute port { xsd:positiveInteger }\nurl =\n    ## Specifies a URL.\n    attribute url { xsd:token }\nid =\n    ## A bean identifier, used for referring to the bean elsewhere in the context.\n    attribute id {xsd:token}\nname =\n    ## A bean identifier, used for referring to the bean elsewhere in the context.\n    attribute name {xsd:token}\nref =\n    ## Defines a reference to a Spring bean Id.\n    attribute ref {xsd:token}\n\ncache-ref =\n    ## Defines a reference to a cache for use with a UserDetailsService.\n    attribute cache-ref {xsd:token}\n\nuser-service-ref =\n    ## A reference to a user-service (or UserDetailsService bean) Id\n    attribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n    ## A reference to an AuthenticationManager bean\n    attribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n    ## A reference to a DataSource bean\n    attribute data-source-ref {xsd:token}\n\n\n\ndebug =\n    ## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n    element debug {empty}\n\npassword-encoder =\n    ## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n    element password-encoder {password-encoder.attlist, salt-source?}\npassword-encoder.attlist &=\n    ref | (hash? & base64?)\n\nsalt-source =\n    ## Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.\n    element salt-source {user-property | system-wide | ref}\nuser-property =\n    ## A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used.\n    attribute user-property {xsd:token}\nsystem-wide =\n    ## A single value that will be used as the salt for a password encoder.\n    attribute system-wide {xsd:token}\n\nrole-prefix =\n    ## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n    attribute role-prefix {xsd:token}\n\nuse-expressions =\n    ## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n    attribute use-expressions {xsd:boolean}\n\nldap-server =\n    ## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n    element ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n    ## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n    attribute manager-dn {xsd:string}?\nldap-server.attlist &=\n    ## The password for the manager DN. This is required if the manager-dn is specified.\n    attribute manager-password {xsd:string}?\nldap-server.attlist &=\n    ## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n    attribute ldif { xsd:string }?\nldap-server.attlist &=\n    ## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n    attribute root { xsd:string }?\n\nldap-server-ref-attribute =\n    ## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n    attribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n    ## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n    attribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n    ## Search base for group membership searches. Defaults to \"\" (searching from the root).\n    attribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n    ## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n    attribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n    ## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n    attribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n    ## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n    attribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n    ## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n    attribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n    ## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n    attribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n    ## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n    element ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n    ldap-server-ref-attribute?\nldap-us.attlist &=\n    user-search-filter-attribute?\nldap-us.attlist &=\n    user-search-base-attribute?\nldap-us.attlist &=\n    group-search-filter-attribute?\nldap-us.attlist &=\n    group-search-base-attribute?\nldap-us.attlist &=\n    group-role-attribute-attribute?\nldap-us.attlist &=\n    cache-ref?\nldap-us.attlist &=\n    role-prefix?\nldap-us.attlist &=\n    (user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n    ## Sets up an ldap authentication provider\n    element ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n    ldap-server-ref-attribute?\nldap-ap.attlist &=\n    user-search-base-attribute?\nldap-ap.attlist &=\n    user-search-filter-attribute?\nldap-ap.attlist &=\n    group-search-base-attribute?\nldap-ap.attlist &=\n    group-search-filter-attribute?\nldap-ap.attlist &=\n    group-role-attribute-attribute?\nldap-ap.attlist &=\n    ## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n    attribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n    role-prefix?\nldap-ap.attlist &=\n    (user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n    ## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n    element password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n    ## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n    attribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n    hash?\n\nintercept-methods =\n    ## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n    element intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n    ## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n    attribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n    ## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n    element protect {protect.attlist, empty}\nprotect.attlist &=\n    ## A method name\n    attribute method {xsd:token}\nprotect.attlist &=\n    ## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n    attribute access {xsd:token}\n\nmethod-security-metadata-source =\n    ## Creates a MethodSecurityMetadataSource instance\n    element method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n    ## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n    element global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n    ## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n    attribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n    ## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n    attribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n    ## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n    attribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n    ## Optional AccessDecisionManager bean ID to override the default used for method security.\n    attribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n    ## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n    attribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n    ## Allows the advice \"order\" to be set for the method security interceptor.\n    attribute order {xsd:token}?\nglobal-method-security.attlist &=\n    ## If true, class based proxying will be used instead of interface based proxying.\n    attribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n    ## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n    attribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n    ## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n    attribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n    authentication-manager-ref?\n\n\nafter-invocation-provider =\n    ## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n    element after-invocation-provider {ref}\n\npre-post-annotation-handling =\n    ## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n    element pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n    ## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n    element invocation-attribute-factory {ref}\n\npre-invocation-advice =\n    ## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n    element pre-invocation-advice {ref}\n\npost-invocation-advice =\n    ## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n    element post-invocation-advice {ref}\n\n\nexpression-handler =\n    ## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n    element expression-handler {ref}\n\nprotect-pointcut =\n    ## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n    element protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n    ## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n    attribute expression {xsd:string}\nprotect-pointcut.attlist &=\n    ## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n    attribute access {xsd:token}\n\nhttp-firewall =\n    ## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n    element http-firewall {ref}\n\nhttp =\n    ## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n    element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf?) }\nhttp.attlist &=\n    ## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n    attribute pattern {xsd:token}?\nhttp.attlist &=\n    ## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n    attribute security {\"none\"}?\nhttp.attlist &=\n    ## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n    attribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n    ## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n    attribute auto-config {xsd:boolean}?\nhttp.attlist &=\n    use-expressions?\nhttp.attlist &=\n    ## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n    attribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n    ## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n    attribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n    request-matcher?\nhttp.attlist &=\n    ## Deprecated. Use request-matcher instead.\n    path-type?\nhttp.attlist &=\n    ## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n    attribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n    ## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n    attribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n    ## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n    attribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n    ## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n    attribute realm {xsd:token}?\nhttp.attlist &=\n    ## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n    attribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n    ## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n    attribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n    ## Deprecated in favour of the access-denied-handler element.\n    attribute access-denied-page {xsd:token}?\nhttp.attlist &=\n    ## Prevents the jsessionid parameter from being added to rendered URLs.\n    attribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n    ## Exposes the list of filters defined by this configuration under this bean name in the application context.\n    name?\nhttp.attlist &=\n    authentication-manager-ref?\n\naccess-denied-handler =\n    ## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n    element access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n    ## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n    attribute error-page {xsd:token}\n\nintercept-url =\n    ## Specifies the access attributes and/or filter list for a particular set of URLs.\n    element intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n    ## The pattern which defines the URL path. The content will depend on the type set in the containing http element, so will default to ant path syntax.\n    attribute pattern {xsd:token}\nintercept-url.attlist &=\n    ## The access configuration attributes that apply for the configured path.\n    attribute access {xsd:token}?\nintercept-url.attlist &=\n    ## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n    attribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n    ## The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.\n    attribute filters {\"none\"}?\nintercept-url.attlist &=\n    ## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n    attribute requires-channel {xsd:token}?\n\nlogout =\n    ## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n    element logout {logout.attlist, empty}\nlogout.attlist &=\n    ## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n    attribute logout-url {xsd:token}?\nlogout.attlist &=\n    ## Specifies the URL to display once the user has logged out. If not specified, defaults to /.\n    attribute logout-success-url {xsd:token}?\nlogout.attlist &=\n    ## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n    attribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n    ## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n    attribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n    ## A comma-separated list of the names of cookies which should be deleted when the user logs out\n    attribute delete-cookies {xsd:token}?\n\nrequest-cache =\n    ## Allow the RequestCache used for saving requests during the login process to be set\n    element request-cache {ref}\n\nform-login =\n    ## Sets up a form login configuration for authentication with a username and password\n    element form-login {form-login.attlist, empty}\nform-login.attlist &=\n    ## The URL that the login form is posted to. If unspecified, it defaults to /login.\n    attribute login-processing-url {xsd:token}?\nform-login.attlist &=\n    ## The name of the request parameter which contains the username. Defaults to 'username'.\n    attribute username-parameter {xsd:token}?\nform-login.attlist &=\n    ## The name of the request parameter which contains the password. Defaults to 'password'.\n    attribute password-parameter {xsd:token}?\nform-login.attlist &=\n    ## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n    attribute default-target-url {xsd:token}?\nform-login.attlist &=\n    ## Whether the user should always be redirected to the default-target-url after login.\n    attribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n    ## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at /spring_security_login and a corresponding filter to render that login URL when requested.\n    attribute login-page {xsd:token}?\nform-login.attlist &=\n    ## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /spring_security_login?login_error and a corresponding filter to render that login failure URL when requested.\n    attribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n    ## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n    attribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n    ## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n    attribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n    ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n    attribute authentication-details-source-ref {xsd:token}?\n\n\nopenid-login =\n    ## Sets up form login for authentication with an Open ID identity\n    element openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n    ## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n    element attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n    ## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n    attribute identifier-match {xsd:token}?\n\nopenid-attribute =\n    ## Attributes used when making an OpenID AX Fetch Request\n    element openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n    ## Specifies the name of the attribute that you wish to get back. For example, email.\n    attribute name {xsd:token}\nopenid-attribute.attlist &=\n    ## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n    attribute type {xsd:token}\nopenid-attribute.attlist &=\n    ## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n    attribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n    ## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n    attribute count {xsd:int}?\n\n\nfilter-chain-map =\n    ## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n    element filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n    ## Deprecated. Use request-matcher instead.\n    path-type?\nfilter-chain-map.attlist &=\n    request-matcher?\n\nfilter-chain =\n    ## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n    element filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n    (pattern | request-matcher-ref)\nfilter-chain.attlist &=\n    ## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n    attribute filters {xsd:token}\n\npattern =\n    ## The request URL pattern which will be mapped to the FilterChain.\n    attribute pattern {xsd:token}\nrequest-matcher-ref =\n    ## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n    attribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n    ## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n    element filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n    use-expressions?\nfsmds.attlist &=\n    id?\nfsmds.attlist &=\n    ## Compare after forcing to lowercase\n    attribute lowercase-comparisons {xsd:boolean}?\nfsmds.attlist &=\n    ## Deprecate. Use request-matcher instead.\n    path-type?\nfsmds.attlist &=\n    request-matcher?\n\nfilter-invocation-definition-source =\n    ## Deprecated synonym for filter-security-metadata-source\n    element filter-invocation-definition-source {fsmds.attlist, intercept-url+}\n\nhttp-basic =\n    ## Adds support for basic authentication\n    element http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n    ## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n    attribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n    ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n    attribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n    ## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n    element session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n    ## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n    attribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n    ## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n    attribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n    ## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n    attribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n    ## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n    attribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n    ## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n    element concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n    ## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\".\n    attribute max-sessions {xsd:positiveInteger}?\nconcurrency-control.attlist &=\n    ## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n    attribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n    ## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n    attribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n    ## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n    attribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n    ## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n    attribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n    ## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n    element remember-me {remember-me.attlist}\nremember-me.attlist &=\n    ## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n    attribute key {xsd:token}?\n\nremember-me.attlist &=\n    (token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n    user-service-ref?\n\nremember-me.attlist &=\n    ## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n    attribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n    ## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n    attribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n    ## The period (in seconds) for which the remember-me cookie should be valid.\n    attribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n    ## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n    attribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n    ## The name of the request parameter which toggles remember-me authentication. Defaults to '_spring_security_remember_me'.\n    attribute remember-me-parameter {xsd:token}?\n\ntoken-repository-ref =\n    ## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n    attribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n    ## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n    attribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n    ## DataSource bean for the database that contains the token repository schema.\n    data-source-ref\n\nanonymous =\n    ## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n    element anonymous {anonymous.attlist}\nanonymous.attlist &=\n    ## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n    attribute key {xsd:token}?\nanonymous.attlist &=\n    ## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n    attribute username {xsd:token}?\nanonymous.attlist &=\n    ## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n    attribute granted-authority {xsd:token}?\nanonymous.attlist &=\n    ## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n    attribute enabled {xsd:boolean}?\n\n\nport-mappings =\n    ## Defines the list of mappings between http and https ports for use in redirects\n    element port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n    ## Provides a method to map http ports to https ports when forcing a redirect.\n    element port-mapping {http-port, https-port}\n\nhttp-port =\n    ## The http port to use.\n    attribute http {xsd:token}\n\nhttps-port =\n    ## The https port to use.\n    attribute https {xsd:token}\n\n\nx509 =\n    ## Adds support for X.509 client authentication.\n    element x509 {x509.attlist}\nx509.attlist &=\n    ## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n    attribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n    ## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n    user-service-ref?\nx509.attlist &=\n    ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n    attribute authentication-details-source-ref {xsd:token}?\n\njee =\n    ## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n    element jee {jee.attlist}\njee.attlist &=\n    ## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n    attribute mappable-roles {xsd:token}\njee.attlist &=\n    ## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n    user-service-ref?\n\nauthentication-manager =\n    ## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n    element authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n    id?\nauthman.attlist &=\n    ## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n    attribute alias {xsd:token}?\nauthman.attlist &=\n    ## If set to true, the AuthenticationManager will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n    attribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n    ## Indicates that the contained user-service should be used as an authentication source.\n    element authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n    ## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n    ref?\nap.attlist &=\n    ## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n    user-service-ref?\n\nuser-service =\n    ## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n    element user-service {id? & (properties-file | (user*))}\nproperties-file =\n    ## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n    attribute properties {xsd:token}?\n\nuser =\n    ## Represents a user in the application.\n    element user {user.attlist, empty}\nuser.attlist &=\n    ## The username assigned to the user.\n    attribute name {xsd:token}\nuser.attlist &=\n    ## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n    attribute password {xsd:string}?\nuser.attlist &=\n    ## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n    attribute authorities {xsd:token}\nuser.attlist &=\n    ## Can be set to \"true\" to mark an account as locked and unusable.\n    attribute locked {xsd:boolean}?\nuser.attlist &=\n     ## Can be set to \"true\" to mark an account as disabled and unusable.\n    attribute disabled {xsd:boolean}?\n\njdbc-user-service =\n    ## Causes creation of a JDBC-based UserDetailsService.\n    element jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n    ## The bean ID of the DataSource which provides the required tables.\n    attribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n    cache-ref?\njdbc-user-service.attlist &=\n    ## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n    attribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n    ## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n    attribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n    ## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n    attribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n    role-prefix?\n\ncsrf =\n ## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n    element csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n    ## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n    attribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n    ## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository\n    attribute token-repository-ref { xsd:token }?\n\nheaders =\n ## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n element headers {cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & header*}\n\nhsts =\n    ## Adds support for HTTP Strict Transport Security (HSTS)\n    element hsts {hsts-options.attlist}\nhsts-options.attlist &=\n    ## Specifies if subdomains should be included. Default true.\n    attribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n    ## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n    attribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n    ## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n    attribute request-matcher-ref { xsd:token }?\n\ncache-control =\n    ## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n    element cache-control {empty}\n\nframe-options =\n    ## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n    element frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n    ## Specify the policy to use for the X-Frame-Options-Header.\n    attribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n    ## Specify the strategy to use when ALLOW-FROM is chosen.\n    attribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n    ## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n    ref?\nframe-options.attlist &=\n    ## Specify a value to use for the chosen strategy.\n    attribute value {xsd:string}?\nframe-options.attlist &=\n    ## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n    attribute from-parameter {xsd:string}?\n\n\nxss-protection =\n    ## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n    element xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n    ## enable or disable the X-XSS-Protection header. Default is 'true' meaning it is enabled.\n    attribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n    ## Add mode=block to the header or not, default is on.\n    attribute block {xsd:boolean}?\n\ncontent-type-options =\n    ## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n    element content-type-options {empty}\n\nheader=\n    ## Add additional headers to the response.\n    element header {header.attlist}\nheader.attlist &=\n    ## The name of the header to add.\n    attribute name {xsd:token}?\nheader.attlist &=\n    ## The value for the header.\n    attribute value {xsd:token}?\nheader.attlist &=\n    ## Reference to a custom HeaderWriter implementation.\n    ref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n    ## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n    element custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n    ref\n\ncustom-filter.attlist &=\n    (after | before | position)\n\nafter =\n    ## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n    attribute after {named-security-filter}\nbefore =\n    ## The filter immediately before which the custom-filter should be placed in the chain\n    attribute before {named-security-filter}\nposition =\n    ## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n    attribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-3.2.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming\n                requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular\n                expressions and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"path-type\">\n      <xs:attribute name=\"path-type\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Deprecated. Use request-matcher instead.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"base64\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user-property\">\n      <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n      <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'false'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'false'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'false'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming\n                requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular\n                expressions and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"path-type\">\n         <xs:annotation>\n            <xs:documentation>Deprecated. Use request-matcher instead.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-denied-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Deprecated in favour of the access-denied-handler element.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The pattern which defines the URL path. The content will depend on the type set in the\n                containing http element, so will default to ant path syntax.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"filters\">\n         <xs:annotation>\n            <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having\n                any filters applied. The full filter stack (consisting of all filters created by the\n                namespace configuration, and any added using 'custom-filter'), will be applied to any\n                other paths.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                /.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at /spring_security_login and a corresponding filter to\n                render that login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /spring_security_login?login_error and a\n                corresponding filter to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"path-type\">\n         <xs:annotation>\n            <xs:documentation>Deprecated. Use request-matcher instead.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming\n                requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular\n                expressions and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'false'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"lowercase-comparisons\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Compare after forcing to lowercase\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"path-type\">\n         <xs:annotation>\n            <xs:documentation>Deprecated. Use request-matcher instead.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Supersedes the 'path-type' attribute. Defines the strategy use for matching incoming\n                requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular\n                expressions and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-invocation-definition-source\">\n      <xs:annotation>\n         <xs:documentation>Deprecated synonym for filter-security-metadata-source\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                '_spring_security_remember_me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                 <xs:annotation>\n                                    <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"ref\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:sequence>\n                                       <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                          <xs:annotation>\n                                             <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                          </xs:annotation>\n                                          <xs:complexType>\n                                             <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"ref\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                          </xs:complexType>\n                                       </xs:element>\n                                    </xs:sequence>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManager will attempt to clear any credentials data in\n                the returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>enable or disable the X-XSS-Protection header. Default is 'true' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-4.0.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\" | \"plaintext\" | \"sha\" | \"sha-256\" | \"md5\" | \"md4\" | \"{sha}\" | \"{ssha}\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:positiveInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist, salt-source?}\npassword-encoder.attlist &=\n\tref | (hash? & base64?)\n\nsalt-source =\n\t## Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.\n\telement salt-source {user-property | system-wide | ref}\nuser-property =\n\t## A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used.\n\tattribute user-property {xsd:token}\nsystem-wide =\n\t## A single value that will be used as the salt for a password encoder.\n\tattribute system-wide {xsd:token}\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t## The pattern which defines the URL path. The content will depend on the type set in the containing http element, so will default to ant path syntax.\n\tattribute pattern {xsd:token}\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.\n\tattribute filters {\"none\"}?\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\t## Compare after forcing to lowercase\n\tattribute lowercase-comparisons {xsd:boolean}?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\".\n\tattribute max-sessions {xsd:positiveInteger}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManager will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:boolean}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-4.0.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'ant'\n                (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for\n                case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"base64\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user-property\">\n      <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n      <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'ant'\n                (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for\n                case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The pattern which defines the URL path. The content will depend on the type set in the\n                containing http element, so will default to ant path syntax.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"filters\">\n         <xs:annotation>\n            <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having\n                any filters applied. The full filter stack (consisting of all filters created by the\n                namespace configuration, and any added using 'custom-filter'), will be applied to any\n                other paths.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'ant'\n                (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for\n                case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"lowercase-comparisons\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Compare after forcing to lowercase\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'ant'\n                (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for\n                case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                 <xs:annotation>\n                                    <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"ref\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:sequence>\n                                       <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                          <xs:annotation>\n                                             <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                          </xs:annotation>\n                                          <xs:complexType>\n                                             <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"ref\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                          </xs:complexType>\n                                       </xs:element>\n                                    </xs:sequence>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManager will attempt to clear any credentials data in\n                the returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-4.1.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\" | \"plaintext\" | \"sha\" | \"sha-256\" | \"md5\" | \"md4\" | \"{sha}\" | \"{ssha}\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:positiveInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist, salt-source?}\npassword-encoder.attlist &=\n\tref | (hash? & base64?)\n\nsalt-source =\n\t## Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.\n\telement salt-source {user-property | system-wide | ref}\nuser-property =\n\t## A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used.\n\tattribute user-property {xsd:token}\nsystem-wide =\n\t## A single value that will be used as the salt for a password encoder.\n\tattribute system-wide {xsd:token}\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t## The pattern which defines the URL path. The content will depend on the type set in the containing http element, so will default to ant path syntax.\n\tattribute pattern {xsd:token}\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.\n\tattribute filters {\"none\"}?\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\t## Compare after forcing to lowercase\n\tattribute lowercase-comparisons {xsd:boolean}?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\".\n\tattribute max-sessions {xsd:positiveInteger}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:boolean}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-4.1.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"base64\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user-property\">\n      <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n      <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The pattern which defines the URL path. The content will depend on the type set in the\n                containing http element, so will default to ant path syntax.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"filters\">\n         <xs:annotation>\n            <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having\n                any filters applied. The full filter stack (consisting of all filters created by the\n                namespace configuration, and any added using 'custom-filter'), will be applied to any\n                other paths.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"lowercase-comparisons\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Compare after forcing to lowercase\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                 <xs:annotation>\n                                    <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"ref\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:sequence>\n                                       <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                          <xs:annotation>\n                                             <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                          </xs:annotation>\n                                          <xs:complexType>\n                                             <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"ref\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                          </xs:complexType>\n                                       </xs:element>\n                                    </xs:sequence>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-4.2.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\" | \"plaintext\" | \"sha\" | \"sha-256\" | \"md5\" | \"md4\" | \"{sha}\" | \"{ssha}\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:positiveInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist, salt-source?}\npassword-encoder.attlist &=\n\tref | (hash? & base64?)\n\nsalt-source =\n\t## Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.\n\telement salt-source {user-property | system-wide | ref}\nuser-property =\n\t## A property of the UserDetails object which will be used as salt by a password encoder. Typically something like \"username\" might be used.\n\tattribute user-property {xsd:token}\nsystem-wide =\n\t## A single value that will be used as the salt for a password encoder.\n\tattribute system-wide {xsd:token}\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Realm\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.\n\tattribute filters {\"none\"}?\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:integer}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:boolean}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-4.2.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"base64\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user-property\">\n      <xs:attribute name=\"user-property\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"system-wide\">\n      <xs:attribute name=\"system-wide\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n               <xs:enumeration value=\"plaintext\"/>\n               <xs:enumeration value=\"sha\"/>\n               <xs:enumeration value=\"sha-256\"/>\n               <xs:enumeration value=\"md5\"/>\n               <xs:enumeration value=\"md4\"/>\n               <xs:enumeration value=\"{sha}\"/>\n               <xs:enumeration value=\"{ssha}\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Realm\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"filters\">\n         <xs:annotation>\n            <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having\n                any filters applied. The full filter stack (consisting of all filters created by the\n                namespace configuration, and any added using 'custom-filter'), will be applied to any\n                other paths.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                 <xs:annotation>\n                                    <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                    <xs:attribute name=\"ref\" type=\"xs:token\">\n                                       <xs:annotation>\n                                          <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                       </xs:annotation>\n                                    </xs:attribute>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:sequence>\n                                       <xs:element minOccurs=\"0\" name=\"salt-source\">\n                                          <xs:annotation>\n                                             <xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails\n                object can be used.\n                </xs:documentation>\n                                          </xs:annotation>\n                                          <xs:complexType>\n                                             <xs:attribute name=\"user-property\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.\n                Typically something like \"username\" might be used.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"system-wide\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>A single value that will be used as the salt for a password encoder.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                             <xs:attribute name=\"ref\" type=\"xs:token\">\n                                                <xs:annotation>\n                                                   <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n                                                </xs:annotation>\n                                             </xs:attribute>\n                                          </xs:complexType>\n                                       </xs:element>\n                                    </xs:sequence>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.0.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:positiveInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.\n\tattribute filters {\"none\"}?\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:integer}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:boolean}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.0.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"filters\">\n         <xs:annotation>\n            <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having\n                any filters applied. The full filter stack (consisting of all filters created by the\n                namespace configuration, and any added using 'custom-filter'), will be applied to any\n                other paths.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.1.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:positiveInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## The filter list for the path. Currently can be set to \"none\" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.\n\tattribute filters {\"none\"}?\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:integer}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:boolean}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.1.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"filters\">\n         <xs:annotation>\n            <xs:documentation>The filter list for the path. Currently can be set to \"none\" to remove a path from having\n                any filters applied. The full filter stack (consisting of all filters created by the\n                namespace configuration, and any added using 'custom-filter'), will be applied to any\n                other paths.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.2.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:positiveInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:integer}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.2.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.3.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:positiveInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are basic, post and none (public clients).\n\tattribute client-authentication-method {\"basic\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\" | \"implicit\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:integer}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.3.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:positiveInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                basic, post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials, password and implicit.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n               <xs:enumeration value=\"implicit\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.4.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are basic, post and none (public clients).\n\tattribute client-authentication-method {\"basic\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\" | \"implicit\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:integer}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.4.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and\n                2.0 protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                basic, post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials, password and implicit.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n               <xs:enumeration value=\"implicit\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0\n                protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.5.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"private_key_jwt\" | \"client_secret_jwt\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password, implicit, as well as, extension grant type urn:ietf:params:oauth:grant-type:jwt-bearer.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\" | \"implicit\" | \"urn:ietf:params:oauth:grant-type:jwt-bearer\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.5.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and\n                2.0 protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt and none\n                (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"private_key_jwt\"/>\n               <xs:enumeration value=\"client_secret_jwt\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials, password, implicit, as well\n                as, extension grant type urn:ietf:params:oauth:grant-type:jwt-bearer.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n               <xs:enumeration value=\"implicit\"/>\n               <xs:enumeration value=\"urn:ietf:params:oauth:grant-type:jwt-bearer\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0\n                protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.6.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n\telement method-security {method-security.attlist, expression-handler?}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & openid-login? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\" | \"implicit\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"CSRF_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.6.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and\n                2.0 protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials, password and implicit.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n               <xs:enumeration value=\"implicit\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0\n                protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.7.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\n\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n\telement method-security {method-security.attlist, expression-handler?}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & openid-login? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"false\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\" | \"implicit\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registration+, asserting-party*}\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.7.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and\n                2.0 protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials, password and implicit.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n               <xs:enumeration value=\"implicit\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0\n                protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.8.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to false)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & openid-login? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"false\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"true\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Only works when use-authorization-manager=true. Defauls to \"false\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\" | \"implicit\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nopenid-login =\n\t## Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-login {form-login.attlist, user-service-ref?, attribute-exchange*}\n\nattribute-exchange =\n\t## Sets up an attribute exchange configuration to request specified attributes from the OpenID identity provider. When multiple elements are used, each must have an identifier-attribute attribute. Each configuration will be matched in turn against the supplied login identifier until a match is found.\n\telement attribute-exchange {attribute-exchange.attlist, openid-attribute+}\n\nattribute-exchange.attlist &=\n\t## A regular expression which will be compared against the claimed identity, when deciding which attribute-exchange configuration to use during authentication.\n\tattribute identifier-match {xsd:token}?\n\nopenid-attribute =\n\t## Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are <a href=\"https://openid.net/specs/openid-connect-migration-1_0.html\">encouraged to migrate</a> to <a href=\"https://openid.net/connect/\">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>.\n\telement openid-attribute {openid-attribute.attlist}\n\nopenid-attribute.attlist &=\n\t## Specifies the name of the attribute that you wish to get back. For example, email.\n\tattribute name {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies the attribute type. For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types.\n\tattribute type {xsd:token}\nopenid-attribute.attlist &=\n\t## Specifies if this attribute is required to the OP, but does not error out if the OP does not return the attribute. Default is false.\n\tattribute required {xsd:boolean}?\nopenid-attribute.attlist &=\n\t## Specifies the number of attributes that you wish to get back. For example, return 3 emails. The default value is 1.\n\tattribute count {xsd:int}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registration+, asserting-party*}\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Deprecated. The HPKP header no longer works in modern browsers, see <a href=\"https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning\">Certificate and Public Key Pinning</a> for more context\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled.\n\tattribute enabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Add mode=block to the header or not, default is on.\n\tattribute block {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. When set, overrides both enabled and block attributes.\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"OPENID_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-5.8.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to false)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"openid-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up form login for authentication with an Open ID identity. NOTE: The OpenID 1.0 and\n                2.0 protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:attribute-exchange\"/>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n                  <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n                     <xs:annotation>\n                        <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n                     </xs:annotation>\n                  </xs:attribute>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"true\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Only\n                works when use-authorization-manager=true. Defauls to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials, password and implicit.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n               <xs:enumeration value=\"implicit\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:element name=\"attribute-exchange\">\n      <xs:annotation>\n         <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the\n                OpenID identity provider. When multiple elements are used, each must have an\n                identifier-attribute attribute. Each configuration will be matched in turn against the\n                supplied login identifier until a match is found.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:openid-attribute\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:attribute-exchange.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"attribute-exchange.attlist\">\n      <xs:attribute name=\"identifier-match\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A regular expression which will be compared against the claimed identity, when deciding\n                which attribute-exchange configuration to use during authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"openid-attribute\">\n      <xs:annotation>\n         <xs:documentation>Attributes used when making an OpenID AX Fetch Request. NOTE: The OpenID 1.0 and 2.0\n                protocols have been deprecated and users are &lt;a\n                href=\"https://openid.net/specs/openid-connect-migration-1_0.html\"&gt;encouraged to\n                migrate&lt;/a&gt; to &lt;a href=\"https://openid.net/connect/\"&gt;OpenID Connect&lt;/a&gt;, which is\n                supported by &lt;code&gt;spring-security-oauth2&lt;/code&gt;.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:openid-attribute.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"openid-attribute.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the name of the attribute that you wish to get back. For example, email.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the attribute type. For example, https://axschema.org/contact/email. See your\n                OP's documentation for valid attribute types.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if this attribute is required to the OP, but does not error out if the OP does\n                not return the attribute. Default is false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"count\" type=\"xs:int\">\n         <xs:annotation>\n            <xs:documentation>Specifies the number of attributes that you wish to get back. For example, return 3\n                emails. The default value is 1.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Deprecated. The HPKP header no longer works in modern browsers, see &lt;a\n                href=\"https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning\"&gt;Certificate\n                and Public Key Pinning&lt;/a&gt; for more context Adds support for HTTP Public Key Pinning\n                (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true'\n                meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"block\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Add mode=block to the header or not, default is on.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. When set, overrides both enabled and\n                block attributes.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"OPENID_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.0.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\nmethod-security.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"false\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to \"true\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\nhttp.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registration+, asserting-party*}\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\nauthman.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.0.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"false\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not\n                work when use-authorization-manager=false. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials and password.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.1.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\nmethod-security.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"false\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to \"true\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\nhttp.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registration+, asserting-party*}\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\nauthman.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.1.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"false\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not\n                work when use-authorization-manager=false. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials and password.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.2.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\nmethod-security.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"false\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to \"true\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\nhttp.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registration+, asserting-party*}\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\nauthman.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.2.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"false\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not\n                work when use-authorization-manager=false. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials and password.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.3.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\nmethod-security.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"false\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to \"true\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\nhttp.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registrations.attlist, relying-party-registration+, asserting-party*}\nrelying-party-registrations.attlist &=\n    ## The identifier by which to refer to the repository in other beans\n    attribute id {xsd:token}?\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\nauthman.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"LOGIN_PAGE_FILTER\" |\"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.3.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"false\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not\n                work when use-authorization-manager=false. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials and password.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registrations.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registrations.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The identifier by which to refer to the repository in other beans\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.4.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\nmethod-security.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"false\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to \"true\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\nhttp.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registrations.attlist, relying-party-registration+, asserting-party*}\nrelying-party-registrations.attlist &=\n    ## The identifier by which to refer to the repository in other beans\n    attribute id {xsd:token}?\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\nauthman.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"DEFAULT_RESOURCES_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.4.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"false\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not\n                work when use-authorization-manager=false. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials and password.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registrations.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registrations.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The identifier by which to refer to the repository in other beans\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"DEFAULT_RESOURCES_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.5.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"apacheds\" | \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\nmethod-security.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the RequestMatcher implementation used to decide whether to redirect a request to HTTPS\n    attribute redirect-to-https-request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"false\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to \"true\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\nhttp.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registrations.attlist, relying-party-registration+, asserting-party*}\nrelying-party-registrations.attlist &=\n    ## The identifier by which to refer to the repository in other beans\n    attribute id {xsd:token}?\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionLimit instance used by the ConcurrentSessionControlAuthenticationStrategy\n\tattribute max-sessions-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\nauthman.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"HTTPS_REDIRECT_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"DEFAULT_RESOURCES_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-6.5.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"apacheds\"/>\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-to-https-request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the RequestMatcher implementation used to decide\n                whether to redirect a request to HTTPS\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"false\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not\n                work when use-authorization-manager=false. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials and password.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registrations.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registrations.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The identifier by which to refer to the repository in other beans\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-sessions-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionLimit instance used by the\n                ConcurrentSessionControlAuthenticationStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is\n                specified a HandlerMappingIntrospector is used as the CorsConfigurationSource\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"HTTPS_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"DEFAULT_RESOURCES_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-7.0.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'path' (for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"path\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. The only supported value is 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\nmethod-security.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the RequestMatcher implementation used to decide whether to redirect a request to HTTPS\n    attribute redirect-to-https-request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"false\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to \"true\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\nhttp.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registrations.attlist, relying-party-registration+, asserting-party*}\nrelying-party-registrations.attlist &=\n    ## The identifier by which to refer to the repository in other beans\n    attribute id {xsd:token}?\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionLimit instance used by the ConcurrentSessionControlAuthenticationStrategy\n\tattribute max-sessions-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nx509.attlist &=\n\t## Reference to an X509PrincipalExtractor which will be used by the authentication filter\n\tattribute principal-extractor-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\nauthman.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. A CorsConfigurationSource must be specified. If Spring MVC is present, then it will attempt to look up its `CorsConfigurationSource`.\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\", \"credentialless\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"HTTPS_REDIRECT_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"DEFAULT_RESOURCES_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-7.0.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'path'\n                (for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for\n                case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"path\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. The only supported value is\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-to-https-request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the RequestMatcher implementation used to decide\n                whether to redirect a request to HTTPS\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'path'\n                (for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for\n                case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"path\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"false\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not\n                work when use-authorization-manager=false. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials and password.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registrations.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registrations.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The identifier by which to refer to the repository in other beans\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'path'\n                (for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for\n                case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"path\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'path'\n                (for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for\n                case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"path\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-sessions-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionLimit instance used by the\n                ConcurrentSessionControlAuthenticationStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"principal-extractor-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an X509PrincipalExtractor which will be used by the authentication filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. A CorsConfigurationSource must be specified. If\n                Spring MVC is present, then it will attempt to look up its `CorsConfigurationSource`.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n               <xs:enumeration value=\"credentialless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"HTTPS_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"DEFAULT_RESOURCES_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-7.1.rnc",
    "content": "namespace a = \"https://relaxng.org/ns/compatibility/annotations/1.0\"\ndatatypes xsd = \"http://www.w3.org/2001/XMLSchema-datatypes\"\n\ndefault namespace = \"http://www.springframework.org/schema/security\"\n\nstart = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider\n\nhash =\n\t## Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n\tattribute hash {\"bcrypt\"}\nbase64 =\n\t## Whether a string should be base64 encoded\n\tattribute base64 {xsd:boolean}\nrequest-matcher =\n\t## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\tattribute request-matcher {\"mvc\" | \"ant\" | \"regex\" | \"ciRegex\"}\nport =\n\t## Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n\tattribute port { xsd:nonNegativeInteger }\nurl =\n\t## Specifies a URL.\n\tattribute url { xsd:token }\nid =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute id {xsd:token}\nname =\n\t## A bean identifier, used for referring to the bean elsewhere in the context.\n\tattribute name {xsd:token}\nref =\n\t## Defines a reference to a Spring bean Id.\n\tattribute ref {xsd:token}\n\ncache-ref =\n\t## Defines a reference to a cache for use with a UserDetailsService.\n\tattribute cache-ref {xsd:token}\n\nuser-service-ref =\n\t## A reference to a user-service (or UserDetailsService bean) Id\n\tattribute user-service-ref {xsd:token}\n\nauthentication-manager-ref =\n\t## A reference to an AuthenticationManager bean\n\tattribute authentication-manager-ref {xsd:token}\n\ndata-source-ref =\n\t## A reference to a DataSource bean\n\tattribute data-source-ref {xsd:token}\n\n\n\ndebug =\n\t## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\telement debug {empty}\n\npassword-encoder =\n\t## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.\n\telement password-encoder {password-encoder.attlist}\npassword-encoder.attlist &=\n\tref | (hash)\n\nrole-prefix =\n\t## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is non-empty.\n\tattribute role-prefix {xsd:token}\n\nuse-expressions =\n\t## Enables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.\n\tattribute use-expressions {xsd:boolean}\n\nldap-server =\n\t## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n\telement ldap-server {ldap-server.attlist}\nldap-server.attlist &= id?\nldap-server.attlist &= (url | port)?\nldap-server.attlist &=\n\t## Username (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used.\n\tattribute manager-dn {xsd:string}?\nldap-server.attlist &=\n\t## The password for the manager DN. This is required if the manager-dn is specified.\n\tattribute manager-password {xsd:string}?\nldap-server.attlist &=\n\t## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff\n\tattribute ldif { xsd:string }?\nldap-server.attlist &=\n\t## Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n\tattribute root { xsd:string }?\nldap-server.attlist &=\n\t## Explicitly specifies which embedded ldap server should use. The only supported value is 'unboundid'. By default, it will depends if the library is available in the classpath.\n\tattribute mode { \"unboundid\" }?\n\nldap-server-ref-attribute =\n\t## The optional server to use. If omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\tattribute server-ref {xsd:token}\n\n\ngroup-search-filter-attribute =\n\t## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user.\n\tattribute group-search-filter {xsd:token}\ngroup-search-base-attribute =\n\t## Search base for group membership searches. Defaults to \"\" (searching from the root).\n\tattribute group-search-base {xsd:token}\nuser-search-filter-attribute =\n\t## The LDAP filter used to search for users (optional). For example \"(uid={0})\". The substituted parameter is the user's login name.\n\tattribute user-search-filter {xsd:token}\nuser-search-base-attribute =\n\t## Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n\tattribute user-search-base {xsd:token}\ngroup-role-attribute-attribute =\n\t## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to \"cn\".\n\tattribute group-role-attribute {xsd:token}\nuser-details-class-attribute =\n\t## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\tattribute user-details-class {\"person\" | \"inetOrgPerson\"}\nuser-context-mapper-attribute =\n\t## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\tattribute user-context-mapper-ref {xsd:token}\n\n\nldap-user-service =\n\t## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n\telement ldap-user-service {ldap-us.attlist}\nldap-us.attlist &= id?\nldap-us.attlist &=\n\tldap-server-ref-attribute?\nldap-us.attlist &=\n\tuser-search-filter-attribute?\nldap-us.attlist &=\n\tuser-search-base-attribute?\nldap-us.attlist &=\n\tgroup-search-filter-attribute?\nldap-us.attlist &=\n\tgroup-search-base-attribute?\nldap-us.attlist &=\n\tgroup-role-attribute-attribute?\nldap-us.attlist &=\n\tcache-ref?\nldap-us.attlist &=\n\trole-prefix?\nldap-us.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\nldap-authentication-provider =\n\t## Sets up an ldap authentication provider\n\telement ldap-authentication-provider {ldap-ap.attlist, password-compare-element?}\nldap-ap.attlist &=\n\tldap-server-ref-attribute?\nldap-ap.attlist &=\n\tuser-search-base-attribute?\nldap-ap.attlist &=\n\tuser-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-search-base-attribute?\nldap-ap.attlist &=\n\tgroup-search-filter-attribute?\nldap-ap.attlist &=\n\tgroup-role-attribute-attribute?\nldap-ap.attlist &=\n\t## A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key \"{0}\" must be present and will be substituted with the username.\n\tattribute user-dn-pattern {xsd:token}?\nldap-ap.attlist &=\n\trole-prefix?\nldap-ap.attlist &=\n\t(user-details-class-attribute | user-context-mapper-attribute)?\n\npassword-compare-element =\n\t## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user\n\telement password-compare {password-compare.attlist, password-encoder?}\n\npassword-compare.attlist &=\n\t## The attribute in the directory which contains the user password. Defaults to \"userPassword\".\n\tattribute password-attribute {xsd:token}?\npassword-compare.attlist &=\n\thash?\n\nintercept-methods =\n\t## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\telement intercept-methods {intercept-methods.attlist, protect+}\nintercept-methods.attlist &=\n\t## Optional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\tattribute access-decision-manager-ref {xsd:token}?\nintercept-methods.attlist &=\n\t## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nintercept-methods.attlist &=\n\t## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager)\n\tattribute authorization-manager-ref {xsd:token}?\n\nprotect =\n\t## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\telement protect {protect.attlist, empty}\nprotect.attlist &=\n\t## A method name\n\tattribute method {xsd:token}\nprotect.attlist &=\n\t## Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n\tattribute access {xsd:token}\n\nmethod-security-metadata-source =\n\t## Creates a MethodSecurityMetadataSource instance\n\telement method-security-metadata-source {msmds.attlist, protect+}\nmsmds.attlist &= id?\n\nmsmds.attlist &= use-expressions?\n\nmethod-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in <protect-pointcut> elements.\n\telement method-security {method-security.attlist, expression-handler?, protect-pointcut*}\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"true\".\n\tattribute pre-post-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"false\".\n\tattribute secured-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"false\".\n\tattribute jsr250-enabled {xsd:boolean}?\nmethod-security.attlist &=\n\t## If true, class-based proxying will be used instead of interface-based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nmethod-security.attlist &=\n\t## If set to aspectj, then use AspectJ to intercept method invocation\n\tattribute mode {\"aspectj\"}?\nmethod-security.attlist &=\n\t## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy\n\tattribute security-context-holder-strategy-ref {xsd:string}?\nmethod-security.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nglobal-method-security =\n\t## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250.\n\telement global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to \"disabled\".\n\tattribute pre-post-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to \"disabled\".\n\tattribute secured-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\"). This will require the javax.annotation.security classes on the classpath. Defaults to \"disabled\".\n\tattribute jsr250-annotations {\"disabled\" | \"enabled\" }?\nglobal-method-security.attlist &=\n\t## Optional AccessDecisionManager bean ID to override the default used for method security.\n\tattribute access-decision-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor\n\tattribute run-as-manager-ref {xsd:token}?\nglobal-method-security.attlist &=\n\t## Allows the advice \"order\" to be set for the method security interceptor.\n\tattribute order {xsd:token}?\nglobal-method-security.attlist &=\n\t## If true, class based proxying will be used instead of interface based proxying.\n\tattribute proxy-target-class {xsd:boolean}?\nglobal-method-security.attlist &=\n\t## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module.\n\tattribute mode {\"aspectj\"}?\nglobal-method-security.attlist &=\n\t## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations).\n\tattribute metadata-source-ref {xsd:token}?\nglobal-method-security.attlist &=\n\tauthentication-manager-ref?\n\n\nafter-invocation-provider =\n\t## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.\n\telement after-invocation-provider {ref}\n\npre-post-annotation-handling =\n\t## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled.\n\telement pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice}\n\ninvocation-attribute-factory =\n\t## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\telement invocation-attribute-factory {ref}\n\npre-invocation-advice =\n\t## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element.\n\telement pre-invocation-advice {ref}\n\npost-invocation-advice =\n\t## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element.\n\telement post-invocation-advice {ref}\n\n\nexpression-handler =\n\t## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied.\n\telement expression-handler {ref}\n\nprotect-pointcut =\n\t## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization.\n\telement protect-pointcut {protect-pointcut.attlist, empty}\nprotect-pointcut.attlist &=\n\t## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes).\n\tattribute expression {xsd:string}\nprotect-pointcut.attlist &=\n\t## Access configuration attributes list that applies to all methods matching the pointcut, e.g. \"ROLE_A,ROLE_B\"\n\tattribute access {xsd:token}\n\nwebsocket-message-broker =\n\t## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel.\n\telement websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) }\n\nwebsocket-message-broker.attrlist &=\n\t## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\n\tattribute id {xsd:token}?\nwebsocket-message-broker.attrlist &=\n\t## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections.\n\tattribute same-origin-disabled {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-message> elements\n\tattribute authorization-manager-ref {xsd:string}?\nwebsocket-message-broker.attrlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nwebsocket-message-broker.attrlist &=\n\t## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API)\n\tattribute security-context-holder-strategy-ref {xsd:string}?\n\nintercept-message =\n\t## Creates an authorization rule for a websocket message.\n\telement intercept-message {intercept-message.attrlist}\n\nintercept-message.attrlist &=\n\t## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin.\n\tattribute pattern {xsd:token}?\nintercept-message.attrlist &=\n\t## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'.\n\tattribute access {xsd:token}?\nintercept-message.attrlist &=\n\t## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\tattribute type {\"CONNECT\" | \"CONNECT_ACK\" | \"HEARTBEAT\" | \"MESSAGE\" | \"SUBSCRIBE\"| \"UNSUBSCRIBE\" | \"DISCONNECT\" | \"DISCONNECT_ACK\" | \"OTHER\"}?\n\nhttp-firewall =\n\t## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace.\n\telement http-firewall {ref}\n\nhttp =\n\t## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the \"security\" attribute to \"none\".\n\telement http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }\nhttp.attlist &=\n\t## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.\n\tattribute pattern {xsd:token}?\nhttp.attlist &=\n\t## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the <http> element must be empty, with no children.\n\tattribute security {\"none\"}?\nhttp.attlist &=\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the RequestMatcher implementation used to decide whether to redirect a request to HTTPS\n    attribute redirect-to-https-request-matcher-ref { xsd:token }?\nhttp.attlist &=\n\t## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you avoid using this and instead explicitly configure the services you require.\n\tattribute auto-config {xsd:boolean}?\nhttp.attlist &=\n\tuse-expressions?\nhttp.attlist &=\n\t## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request\n\tattribute security-context-holder-strategy-ref {xsd:token}?\nhttp.attlist &=\n\t## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the application guarantees that it will not create a session. This differs from the use of \"never\" which means that Spring Security will not create a session, but will make use of one if the application does.\n\tattribute create-session {\"ifRequired\" | \"always\" | \"never\" | \"stateless\"}?\nhttp.attlist &=\n\t## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.\n\tattribute security-context-repository-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n\tattribute security-context-explicit-save {xsd:boolean}?\nhttp.attlist &=\n\trequest-matcher?\nhttp.attlist &=\n\t## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to \"true\".\n\tattribute servlet-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to \"false\".\n\tattribute jaas-api-provision {xsd:boolean}?\nhttp.attlist &=\n\t## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\tattribute use-authorization-manager {xsd:boolean}?\nhttp.attlist &=\n\t## Use this AuthorizationManager instead of deriving one from <intercept-url> elements\n\tattribute authorization-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.\n\tattribute access-decision-manager-ref {xsd:token}?\nhttp.attlist &=\n\t## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to \"Spring Security Application\".\n\tattribute realm {xsd:token}?\nhttp.attlist &=\n\t## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp.attlist &=\n\t## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to \"false\"\n\tattribute once-per-request {xsd:boolean}?\nhttp.attlist &=\n\t## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to \"true\".\n\tattribute filter-all-dispatcher-types {xsd:boolean}?\nhttp.attlist &=\n\t## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\" (rewriting is disabled).\n\tattribute disable-url-rewriting {xsd:boolean}?\nhttp.attlist &=\n\t## Exposes the list of filters defined by this configuration under this bean name in the application context.\n\tname?\nhttp.attlist &=\n\tauthentication-manager-ref?\nhttp.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\naccess-denied-handler =\n\t## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.\n\telement access-denied-handler {access-denied-handler.attlist, empty}\naccess-denied-handler.attlist &= (ref | access-denied-handler-page)\n\naccess-denied-handler-page =\n\t## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\tattribute error-page {xsd:token}\n\nintercept-url =\n\t## Specifies the access attributes and/or filter list for a particular set of URLs.\n\telement intercept-url {intercept-url.attlist, empty}\nintercept-url.attlist &=\n\t(pattern | request-matcher-ref)\nintercept-url.attlist &=\n\t## The access configuration attributes that apply for the configured path.\n\tattribute access {xsd:token}?\nintercept-url.attlist &=\n\t## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.\n\tattribute method {\"GET\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\" | \"POST\" | \"PUT\" | \"PATCH\" | \"TRACE\"}?\n\nintercept-url.attlist &=\n\t## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be \"http\", \"https\" or \"any\", respectively.\n\tattribute requires-channel {xsd:token}?\nintercept-url.attlist &=\n\t## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.\n\tattribute servlet-path {xsd:token}?\n\nlogout =\n\t## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.\n\telement logout {logout.attlist, empty}\nlogout.attlist &=\n\t## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified.\n\tattribute logout-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies the URL to display once the user has logged out. If not specified, defaults to <form-login-login-page>/?logout (i.e. /login?logout).\n\tattribute logout-success-url {xsd:token}?\nlogout.attlist &=\n\t## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true.\n\tattribute invalidate-session {xsd:boolean}?\nlogout.attlist &=\n\t## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out.\n\tattribute success-handler-ref {xsd:token}?\nlogout.attlist &=\n\t## A comma-separated list of the names of cookies which should be deleted when the user logs out\n\tattribute delete-cookies {xsd:token}?\n\nrequest-cache =\n\t## Allow the RequestCache used for saving requests during the login process to be set\n\telement request-cache {ref}\n\nform-login =\n\t## Sets up a form login configuration for authentication with a username and password\n\telement form-login {form-login.attlist, empty}\nform-login.attlist &=\n\t## The URL that the login form is posted to. If unspecified, it defaults to /login.\n\tattribute login-processing-url {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the username. Defaults to 'username'.\n\tattribute username-parameter {xsd:token}?\nform-login.attlist &=\n\t## The name of the request parameter which contains the password. Defaults to 'password'.\n\tattribute password-parameter {xsd:token}?\nform-login.attlist &=\n\t## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application.\n\tattribute default-target-url {xsd:token}?\nform-login.attlist &=\n\t## Whether the user should always be redirected to the default-target-url after login.\n\tattribute always-use-default-target {xsd:boolean}?\nform-login.attlist &=\n\t## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested.\n\tattribute login-page {xsd:token}?\nform-login.attlist &=\n\t## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested.\n\tattribute authentication-failure-url {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-success-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination\n\tattribute authentication-failure-handler-ref {xsd:token}?\nform-login.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationFailureHandler\n\tattribute authentication-failure-forward-url {xsd:token}?\nform-login.attlist &=\n\t## The URL for the ForwardAuthenticationSuccessHandler\n\tattribute authentication-success-forward-url {xsd:token}?\n\noauth2-login =\n\t## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\telement oauth2-login {oauth2-login.attlist}\noauth2-login.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the GrantedAuthoritiesMapper\n\tattribute user-authorities-mapper-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OAuth2UserService\n\tattribute user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the OpenID Connect OAuth2UserService\n\tattribute oidc-user-service-ref {xsd:token}?\noauth2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\noauth2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\noauth2-login.attlist &=\n\t## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n\tattribute jwt-decoder-factory-ref {xsd:token}?\n\noauth2-client =\n\t## Configures OAuth 2.0 Client support.\n\telement oauth2-client {oauth2-client.attlist, (authorization-code-grant?) }\noauth2-client.attlist &=\n\t## Reference to the ClientRegistrationRepository\n\tattribute client-registration-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientRepository\n\tattribute authorized-client-repository-ref {xsd:token}?\noauth2-client.attlist &=\n\t## Reference to the OAuth2AuthorizedClientService\n\tattribute authorized-client-service-ref {xsd:token}?\n\nauthorization-code-grant =\n\t## Configures OAuth 2.0 Authorization Code Grant.\n\telement authorization-code-grant {authorization-code-grant.attlist, empty}\nauthorization-code-grant.attlist &=\n\t## Reference to the AuthorizationRequestRepository\n\tattribute authorization-request-repository-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n    ## Reference to the authorization RedirectStrategy\n\tattribute authorization-redirect-strategy-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AuthorizationRequestResolver\n\tattribute authorization-request-resolver-ref {xsd:token}?\nauthorization-code-grant.attlist &=\n\t## Reference to the OAuth2AccessTokenResponseClient\n\tattribute access-token-response-client-ref {xsd:token}?\n\nclient-registrations =\n\t## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registrations {client-registration+, provider*}\n\nclient-registration =\n\t## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement client-registration {client-registration.attlist}\nclient-registration.attlist &=\n\t## The ID that uniquely identifies the client registration.\n\tattribute registration-id {xsd:token}\nclient-registration.attlist &=\n\t## The client identifier.\n\tattribute client-id {xsd:token}\nclient-registration.attlist &=\n\t## The client secret.\n\tattribute client-secret {xsd:token}?\nclient-registration.attlist &=\n\t## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).\n\tattribute client-authentication-method {\"client_secret_basic\" | \"basic\" | \"client_secret_post\" | \"post\" | \"none\"}?\nclient-registration.attlist &=\n\t## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.\n\tattribute authorization-grant-type {\"authorization_code\" | \"client_credentials\" | \"password\"}?\nclient-registration.attlist &=\n\t## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.\n\tattribute redirect-uri {xsd:token}?\nclient-registration.attlist &=\n\t## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\tattribute scope {xsd:token}?\nclient-registration.attlist &=\n\t## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\tattribute client-name {xsd:token}?\nclient-registration.attlist &=\n\t## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta).\n\tattribute provider-id {xsd:token}\n\nprovider =\n\t## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\telement provider {provider.attlist}\nprovider.attlist &=\n\t## The ID that uniquely identifies the provider.\n\tattribute provider-id {xsd:token}\nprovider.attlist &=\n\t## The Authorization Endpoint URI for the Authorization Server.\n\tattribute authorization-uri {xsd:token}?\nprovider.attlist &=\n\t## The Token Endpoint URI for the Authorization Server.\n\tattribute token-uri {xsd:token}?\nprovider.attlist &=\n\t## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\tattribute user-info-uri {xsd:token}?\nprovider.attlist &=\n\t## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.\n\tattribute user-info-authentication-method {\"header\" | \"form\" | \"query\"}?\nprovider.attlist &=\n\t## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\tattribute user-info-user-name-attribute {xsd:token}?\nprovider.attlist &=\n\t## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.\n\tattribute jwk-set-uri {xsd:token}?\nprovider.attlist &=\n\t## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\tattribute issuer-uri {xsd:token}?\n\noauth2-resource-server =\n\t## Configures authentication support as an OAuth 2.0 Resource Server.\n\telement oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)}\noauth2-resource-server.attlist &=\n\t## Reference to an AuthenticationManagerResolver\n\tattribute authentication-manager-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a BearerTokenResolver\n\tattribute bearer-token-resolver-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationEntryPoint\n\tattribute entry-point-ref {xsd:token}?\noauth2-resource-server.attlist &=\n\t## Reference to a AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\n\njwt =\n    ## Configures JWT authentication\n    element jwt {jwt.attlist}\njwt.attlist &=\n    ## The URI to use to collect the JWK Set for verifying JWTs\n    attribute jwk-set-uri {xsd:token}?\njwt.attlist &=\n    ## Reference to a JwtDecoder\n    attribute decoder-ref {xsd:token}?\njwt.attlist &=\n    ## Reference to a Converter<Jwt, AbstractAuthenticationToken>\n    attribute jwt-authentication-converter-ref {xsd:token}?\n\nopaque-token =\n    ## Configuration Opaque Token authentication\n    element opaque-token {opaque-token.attlist}\nopaque-token.attlist &=\n    ## The URI to use to introspect opaque token attributes\n    attribute introspection-uri {xsd:token}?\nopaque-token.attlist &=\n    ## The Client ID to use to authenticate the introspection request\n    attribute client-id {xsd:token}?\nopaque-token.attlist &=\n    ## The Client secret to use to authenticate the introspection request\n    attribute client-secret {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenIntrospector\n    attribute introspector-ref {xsd:token}?\nopaque-token.attlist &=\n    ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication.\n    attribute authentication-converter-ref {xsd:token}?\n\nsaml2-login =\n\t## Configures authentication support for SAML 2.0 Login\n\telement saml2-login {saml2-login.attlist}\nsaml2-login.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestRepository\n\tattribute authentication-request-repository-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the Saml2AuthenticationRequestResolver\n\tattribute authentication-request-resolver-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationConverter\n\tattribute authentication-converter-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI where the filter processes authentication requests\n\tattribute login-processing-url {xsd:token}?\nsaml2-login.attlist &=\n\t## The URI to send users to login\n\tattribute login-page {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationSuccessHandler\n\tattribute authentication-success-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationFailureHandler\n\tattribute authentication-failure-handler-ref {xsd:token}?\nsaml2-login.attlist &=\n\t## Reference to the AuthenticationManager\n\tattribute authentication-manager-ref {xsd:token}?\n\nsaml2-logout =\n\t## Configures SAML 2.0 Single Logout support\n\telement saml2-logout {saml2-logout.attlist}\nsaml2-logout.attlist &=\n\t## The URL by which the relying or asserting party can trigger logout\n\tattribute logout-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Request\n\tattribute logout-request-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## The URL by which the asserting party can send a SAML 2.0 Logout Response\n\tattribute logout-response-url {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the RelyingPartyRegistrationRepository\n\tattribute relying-party-registration-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestValidator\n\tattribute logout-request-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestResolver\n\tattribute logout-request-resolver-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutRequestRepository\n\tattribute logout-request-repository-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseValidator\n\tattribute logout-response-validator-ref {xsd:token}?\nsaml2-logout.attlist &=\n\t## Reference to the Saml2LogoutResponseResolver\n\tattribute logout-response-resolver-ref {xsd:token}?\n\nrelying-party-registrations =\n\t## Container element for relying party(ies) registered with a SAML 2.0 identity provider\n\telement relying-party-registrations {relying-party-registrations.attlist, relying-party-registration+, asserting-party*}\nrelying-party-registrations.attlist &=\n    ## The identifier by which to refer to the repository in other beans\n    attribute id {xsd:token}?\n\nrelying-party-registration =\n\t## Represents a relying party registered with a SAML 2.0 identity provider\n\telement relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*}\nrelying-party-registration.attlist &=\n\t## The ID that uniquely identifies the relying party registration.\n\tattribute registration-id {xsd:token}\nrelying-party-registration.attlist &=\n\t## The location of the Identity Provider's metadata.\n\tattribute metadata-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party's EntityID\n\tattribute entity-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Location\n\tattribute assertion-consumer-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The Assertion Consumer Service Binding\n\tattribute assertion-consumer-service-binding {xsd:token}?\nrelying-party-registration.attlist &=\n\t## A reference to the associated asserting party.\n\tattribute asserting-party-id {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nrelying-party-registration.attlist &=\n\t## The relying party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nsigning-credential =\n\t## The relying party's signing credential\n\telement signing-credential {signing-credential.attlist}\nsigning-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nsigning-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\ndecryption-credential =\n\t## The relying party's decryption credential\n\telement decryption-credential {decryption-credential.attlist}\ndecryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\ndecryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nasserting-party =\n\t## The configuration metadata of the Asserting party\n\telement asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*}\nasserting-party.attlist &=\n\t## A unique identifier of the asserting party.\n\tattribute asserting-party-id {xsd:token}\nasserting-party.attlist &=\n\t## The asserting party's EntityID.\n\tattribute entity-id {xsd:token}\nasserting-party.attlist &=\n\t## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending\n\tattribute want-authn-requests-signed {xsd:token}?\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Location.\n\tattribute single-sign-on-service-location {xsd:token}\nasserting-party.attlist &=\n\t## The <a href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a> Binding.\n\tattribute single-sign-on-service-binding {xsd:token}?\nasserting-party.attlist &=\n\t## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order.\n\tattribute signing-algorithms {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Location</a>\n\tattribute single-logout-service-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Response Location</a>\n\tattribute single-logout-service-response-location {xsd:token}?\nasserting-party.attlist &=\n\t## The asserting party <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService Binding</a>\n\tattribute single-logout-service-binding {xsd:token}?\n\nverification-credential =\n\t## The relying party's verification credential\n\telement verification-credential {verification-credential.attlist}\nverification-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nverification-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\nencryption-credential =\n\t## The asserting party's encryption credential\n\telement encryption-credential {encryption-credential.attlist}\nencryption-credential.attlist &=\n\t## The private key location\n\tattribute private-key-location {xsd:token}\nencryption-credential.attlist &=\n\t## The certificate location\n\tattribute certificate-location {xsd:token}\n\n\nfilter-chain-map =\n\t## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\telement filter-chain-map {filter-chain-map.attlist, filter-chain+}\nfilter-chain-map.attlist &=\n\trequest-matcher?\n\nfilter-chain =\n\t## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with  most general ones at the bottom.\n\telement filter-chain {filter-chain.attlist, empty}\nfilter-chain.attlist &=\n\t(pattern | request-matcher-ref)\nfilter-chain.attlist &=\n\t## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n\tattribute filters {xsd:token}\n\npattern =\n\t## The request URL pattern which will be mapped to the FilterChain.\n\tattribute pattern {xsd:token}\nrequest-matcher-ref =\n\t## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n\tattribute request-matcher-ref {xsd:token}\n\nfilter-security-metadata-source =\n\t## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.\n\telement filter-security-metadata-source {fsmds.attlist, intercept-url+}\nfsmds.attlist &=\n\tuse-expressions?\nfsmds.attlist &=\n\tid?\nfsmds.attlist &=\n\trequest-matcher?\n\nhttp-basic =\n\t## Adds support for basic authentication\n\telement http-basic {http-basic.attlist, empty}\n\nhttp-basic.attlist &=\n\t## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n\tattribute entry-point-ref {xsd:token}?\nhttp-basic.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\n\npassword-management =\n    ## Adds support for the password management.\n    element password-management {password-management.attlist, empty}\n\npassword-management.attlist &=\n    ## The change password page. Defaults to \"/change-password\".\n    attribute change-password-page {xsd:string}?\n\nsession-management =\n\t## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack.\n\telement session-management {session-management.attlist, concurrency-control?}\n\nsession-management.attlist &=\n\t## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n\tattribute authentication-strategy-explicit-invocation {xsd:boolean}?\nsession-management.attlist &=\n\t## Indicates how session fixation protection will be applied when a user authenticates. If set to \"none\", no protection will be applied. \"newSession\" will create a new empty session, with only Spring Security-related attributes migrated. \"migrateSession\" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers. Throws an exception if \"changeSessionId\" is used in older containers.\n\tattribute session-fixation-protection {\"none\" | \"newSession\" | \"migrateSession\" | \"changeSessionId\" }?\nsession-management.attlist &=\n\t## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.\n\tattribute invalid-session-url {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter\n\tattribute invalid-session-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\tattribute session-authentication-strategy-ref {xsd:token}?\nsession-management.attlist &=\n\t## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\tattribute session-authentication-error-url {xsd:token}?\n\n\nconcurrency-control =\n\t## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time.\n\telement concurrency-control {concurrency-control.attlist, empty}\n\nconcurrency-control.attlist &=\n\t## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to \"1\". A negative value denotes unlimited sessions.\n\tattribute max-sessions {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionLimit instance used by the ConcurrentSessionControlAuthenticationStrategy\n\tattribute max-sessions-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## The URL a user will be redirected to if they attempt to use a session which has been \"expired\" because they have logged in again.\n\tattribute expired-url {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter\n\tattribute expired-session-strategy-ref {xsd:token}?\nconcurrency-control.attlist &=\n\t## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.\n\tattribute error-if-maximum-exceeded {xsd:boolean}?\nconcurrency-control.attlist &=\n\t## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.\n\tattribute session-registry-alias {xsd:token}?\nconcurrency-control.attlist &=\n\t## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup.\n\tattribute session-registry-ref {xsd:token}?\n\n\nremember-me =\n\t## Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes) the cookie-only implementation will be used. Specifying \"token-repository-ref\" or \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n\telement remember-me {remember-me.attlist}\nremember-me.attlist &=\n\t## The \"key\" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\n\nremember-me.attlist &=\n\t(token-repository-ref | remember-me-data-source-ref | remember-me-services-ref)\n\nremember-me.attlist &=\n\tuser-service-ref?\n\nremember-me.attlist &=\n\t## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context.\n\tattribute services-alias {xsd:token}?\n\nremember-me.attlist &=\n\t## Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection.\n\tattribute use-secure-cookie {xsd:boolean}?\n\nremember-me.attlist &=\n\t## The period (in seconds) for which the remember-me cookie should be valid.\n\tattribute token-validity-seconds {xsd:string}?\n\nremember-me.attlist &=\n\t## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication.\n\tattribute authentication-success-handler-ref {xsd:token}?\nremember-me.attlist &=\n\t## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-parameter {xsd:token}?\nremember-me.attlist &=\n\t## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'.\n\tattribute remember-me-cookie {xsd:token}?\n\ntoken-repository-ref =\n\t## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation.\n\tattribute token-repository-ref {xsd:token}\nremember-me-services-ref =\n\t## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same \"key\" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.\n\tattribute services-ref {xsd:token}?\nremember-me-data-source-ref =\n\t## DataSource bean for the database that contains the token repository schema.\n\tdata-source-ref\n\nanonymous =\n\t## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority.\n\telement anonymous {anonymous.attlist}\nanonymous.attlist &=\n\t## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom.\n\tattribute key {xsd:token}?\nanonymous.attlist &=\n\t## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to \"anonymousUser\".\n\tattribute username {xsd:token}?\nanonymous.attlist &=\n\t## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n\tattribute granted-authority {xsd:token}?\nanonymous.attlist &=\n\t## With the default namespace setup, the anonymous \"authentication\" facility is automatically enabled. You can disable it using this property.\n\tattribute enabled {xsd:boolean}?\n\n\nport-mappings =\n\t## Defines the list of mappings between http and https ports for use in redirects\n\telement port-mappings {port-mappings.attlist, port-mapping+}\n\nport-mappings.attlist &= empty\n\nport-mapping =\n\t## Provides a method to map http ports to https ports when forcing a redirect.\n\telement port-mapping {http-port, https-port}\n\nhttp-port =\n\t## The http port to use.\n\tattribute http {xsd:token}\n\nhttps-port =\n\t## The https port to use.\n\tattribute https {xsd:token}\n\n\nx509 =\n\t## Adds support for X.509 client authentication.\n\telement x509 {x509.attlist}\nx509.attlist &=\n\t## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n\tattribute subject-principal-regex {xsd:token}?\nx509.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.\n\tuser-service-ref?\nx509.attlist &=\n\t## Reference to an AuthenticationDetailsSource which will be used by the authentication filter\n\tattribute authentication-details-source-ref {xsd:token}?\nx509.attlist &=\n\t## Reference to an X509PrincipalExtractor which will be used by the authentication filter\n\tattribute principal-extractor-ref {xsd:token}?\n\njee =\n\t## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\telement jee {jee.attlist}\njee.attlist &=\n\t## A comma-separate list of roles to look for in the incoming HttpServletRequest.\n\tattribute mappable-roles {xsd:token}\njee.attlist &=\n\t## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.\n\tuser-service-ref?\n\nauthentication-manager =\n\t## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.\n\telement authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}\nauthman.attlist &=\n\tid?\nauthman.attlist &=\n\t## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)\n\tattribute alias {xsd:token}?\nauthman.attlist &=\n\t## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\n\tattribute erase-credentials {xsd:boolean}?\nauthman.attlist &=\n\t## Use this ObservationRegistry to collect metrics on various parts of the filter chain\n\tattribute observation-registry-ref {xsd:token}?\n\nauthentication-provider =\n\t## Indicates that the contained user-service should be used as an authentication source.\n\telement authentication-provider {ap.attlist & any-user-service & password-encoder?}\nap.attlist &=\n\t## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager.\n\tref?\nap.attlist &=\n\t## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data.\n\tuser-service-ref?\n\nuser-service =\n\t## Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\telement user-service {id? & (properties-file | (user*))}\nproperties-file =\n\t## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n\tattribute properties {xsd:token}?\n\nuser =\n\t## Represents a user in the application.\n\telement user {user.attlist, empty}\nuser.attlist &=\n\t## The username assigned to the user.\n\tattribute name {xsd:token}\nuser.attlist &=\n\t## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty.\n\tattribute password {xsd:string}?\nuser.attlist &=\n\t## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\tattribute authorities {xsd:token}\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as locked and unusable.\n\tattribute locked {xsd:boolean}?\nuser.attlist &=\n\t## Can be set to \"true\" to mark an account as disabled and unusable.\n\tattribute disabled {xsd:boolean}?\n\njdbc-user-service =\n\t## Causes creation of a JDBC-based UserDetailsService.\n\telement jdbc-user-service {id? & jdbc-user-service.attlist}\njdbc-user-service.attlist &=\n\t## The bean ID of the DataSource which provides the required tables.\n\tattribute data-source-ref {xsd:token}\njdbc-user-service.attlist &=\n\tcache-ref?\njdbc-user-service.attlist &=\n\t## An SQL statement to query a username, password, and enabled status given a username. Default is \"select username,password,enabled from users where username = ?\"\n\tattribute users-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query for a user's granted authorities given a username. The default is \"select username, authority from authorities where username = ?\"\n\tattribute authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\t## An SQL statement to query user's group authorities given a username. The default is \"select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n\tattribute group-authorities-by-username-query {xsd:token}?\njdbc-user-service.attlist &=\n\trole-prefix?\n\ncsrf =\n## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay \"GET\" requests.\n\telement csrf {csrf-options.attlist}\ncsrf-options.attlist &=\n\t## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).\n\tattribute disabled {xsd:boolean}?\ncsrf-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n\tattribute request-matcher-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.\n\tattribute token-repository-ref { xsd:token }?\ncsrf-options.attlist &=\n\t## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n\tattribute request-handler-ref { xsd:token }?\n\nheaders =\n## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\nelement headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}\nheaders-options.attlist &=\n\t## Specifies if the default headers should be disabled. Default false.\n\tattribute defaults-disabled {xsd:token}?\nheaders-options.attlist &=\n\t## Specifies if headers should be disabled. Default false.\n\tattribute disabled {xsd:token}?\nhsts =\n\t## Adds support for HTTP Strict Transport Security (HSTS)\n\telement hsts {hsts-options.attlist}\nhsts-options.attlist &=\n\t## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies if subdomains should be included. Default true.\n\tattribute include-subdomains {xsd:boolean}?\nhsts-options.attlist &=\n\t## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year.\n\tattribute max-age-seconds {xsd:integer}?\nhsts-options.attlist &=\n\t## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.\n\tattribute request-matcher-ref { xsd:token }?\nhsts-options.attlist &=\n\t## Specifies if preload should be included. Default false.\n\tattribute preload {xsd:boolean}?\n\ncors =\n## Element for configuration of CorsFilter. A CorsConfigurationSource must be specified. If Spring MVC is present, then it will attempt to look up its `CorsConfigurationSource`.\nelement cors { cors-options.attlist }\ncors-options.attlist &=\n\tref?\ncors-options.attlist &=\n\t## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use\n\tattribute configuration-source-ref {xsd:token}?\n\nhpkp =\n\t## Adds support for HTTP Public Key Pinning (HPKP).\n\telement hpkp {hpkp.pins,hpkp.attlist}\nhpkp.pins =\n\t## The list with pins\n\telement pins {hpkp.pin+}\nhpkp.pin =\n\t## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\telement pin {\n\t\t## The cryptographic hash algorithm\n\t\tattribute algorithm { xsd:string }?,\n\t\ttext\n\t}\nhpkp.attlist &=\n\t## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies if subdomains should be included. Default false.\n\tattribute include-subdomains {xsd:boolean}?\nhpkp.attlist &=\n\t## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n\tattribute max-age-seconds {xsd:integer}?\nhpkp.attlist &=\n\t## Specifies if the browser should only report pin validation failures. Default true.\n\tattribute report-only {xsd:boolean}?\nhpkp.attlist &=\n\t## Specifies the URI to which the browser should report pin validation failures.\n\tattribute report-uri {xsd:string}?\n\ncontent-security-policy =\n\t## Adds support for Content Security Policy (CSP)\n\telement content-security-policy {csp-options.attlist}\ncsp-options.attlist &=\n\t## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\tattribute policy-directives {xsd:token}?\ncsp-options.attlist &=\n\t## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false.\n\tattribute report-only {xsd:boolean}?\n\nreferrer-policy =\n\t## Adds support for Referrer Policy\n\telement referrer-policy {referrer-options.attlist}\nreferrer-options.attlist &=\n\t## The policies for the Referrer-Policy header.\n\tattribute policy {\"no-referrer\",\"no-referrer-when-downgrade\",\"same-origin\",\"origin\",\"strict-origin\",\"origin-when-cross-origin\",\"strict-origin-when-cross-origin\",\"unsafe-url\"}?\n\nfeature-policy =\n\t## Adds support for Feature Policy\n\telement feature-policy {feature-options.attlist}\nfeature-options.attlist &=\n\t## The security policy directive(s) for the Feature-Policy header.\n\tattribute policy-directives {xsd:token}?\n\npermissions-policy =\n\t## Adds support for Permissions Policy\n\telement permissions-policy {permissions-options.attlist}\npermissions-options.attlist &=\n\t## The policies for the Permissions-Policy header.\n\tattribute policy {xsd:token}?\n\ncache-control =\n\t## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request\n\telement cache-control {cache-control.attlist}\ncache-control.attlist &=\n\t## Specifies if Cache Control should be disabled. Default false.\n\tattribute disabled {xsd:boolean}?\n\nframe-options =\n\t## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.\n\telement frame-options {frame-options.attlist,empty}\nframe-options.attlist &=\n\t## If disabled, the X-Frame-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\nframe-options.attlist &=\n\t## Specify the policy to use for the X-Frame-Options-Header.\n\tattribute policy {\"DENY\",\"SAMEORIGIN\",\"ALLOW-FROM\"}?\nframe-options.attlist &=\n\t## Specify the strategy to use when ALLOW-FROM is chosen.\n\tattribute strategy {\"static\",\"whitelist\",\"regexp\"}?\nframe-options.attlist &=\n\t## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.\n\tref?\nframe-options.attlist &=\n\t## Specify a value to use for the chosen strategy.\n\tattribute value {xsd:string}?\nframe-options.attlist &=\n\t## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.\n\t## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use\n\t## Content-Security-Policy with the\n\t## <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t## directive.\n\tattribute from-parameter {xsd:string}?\n\n\nxss-protection =\n\t## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.\n\telement xss-protection {xss-protection.attlist,empty}\nxss-protection.attlist &=\n\t## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n\tattribute disabled {xsd:boolean}?\nxss-protection.attlist &=\n\t## Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n\tattribute header-value {\"0\"|\"1\"|\"1; mode=block\"}?\n\ncontent-type-options =\n\t## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n\telement content-type-options {content-type-options.attlist, empty}\ncontent-type-options.attlist &=\n\t## If disabled, the X-Content-Type-Options header will not be included. Default false.\n\tattribute disabled {xsd:boolean}?\n\ncross-origin-opener-policy =\n\t## Adds support for Cross-Origin-Opener-Policy header\n\telement cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}\ncross-origin-opener-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Opener-Policy header.\n\tattribute policy {\"unsafe-none\",\"same-origin\",\"same-origin-allow-popups\"}?\n\ncross-origin-embedder-policy =\n\t## Adds support for Cross-Origin-Embedder-Policy header\n\telement cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}\ncross-origin-embedder-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Embedder-Policy header.\n\tattribute policy {\"unsafe-none\",\"require-corp\", \"credentialless\"}?\n\ncross-origin-resource-policy =\n\t## Adds support for Cross-Origin-Resource-Policy header\n\telement cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}\ncross-origin-resource-policy-options.attlist &=\n\t## The policies for the Cross-Origin-Resource-Policy header.\n\tattribute policy {\"cross-origin\",\"same-origin\",\"same-site\"}?\n\nheader=\n\t## Add additional headers to the response.\n\telement header {header.attlist}\nheader.attlist &=\n\t## The name of the header to add.\n\tattribute name {xsd:token}?\nheader.attlist &=\n\t## The value for the header.\n\tattribute value {xsd:token}?\nheader.attlist &=\n\t## Reference to a custom HeaderWriter implementation.\n\tref?\n\nany-user-service = user-service | jdbc-user-service | ldap-user-service\n\ncustom-filter =\n\t## Used to indicate that a filter bean declaration should be incorporated into the security filter chain.\n\telement custom-filter {custom-filter.attlist}\n\ncustom-filter.attlist &=\n\tref\n\ncustom-filter.attlist &=\n\t(after | before | position)\n\nafter =\n\t## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters.\n\tattribute after {named-security-filter}\nbefore =\n\t## The filter immediately before which the custom-filter should be placed in the chain\n\tattribute before {named-security-filter}\nposition =\n\t## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.\n\tattribute position {named-security-filter}\n\nnamed-security-filter = \"FIRST\" | \"DISABLE_ENCODE_URL_FILTER\" | \"FORCE_EAGER_SESSION_FILTER\" | \"CHANNEL_FILTER\" | \"HTTPS_REDIRECT_FILTER\" | \"SECURITY_CONTEXT_FILTER\" | \"CONCURRENT_SESSION_FILTER\" | \"WEB_ASYNC_MANAGER_FILTER\" | \"HEADERS_FILTER\" | \"CORS_FILTER\" | \"SAML2_LOGOUT_REQUEST_FILTER\" | \"SAML2_LOGOUT_RESPONSE_FILTER\" | \"CSRF_FILTER\" | \"SAML2_LOGOUT_FILTER\" | \"LOGOUT_FILTER\" | \"OAUTH2_AUTHORIZATION_REQUEST_FILTER\" | \"SAML2_AUTHENTICATION_REQUEST_FILTER\" | \"X509_FILTER\" | \"PRE_AUTH_FILTER\" | \"CAS_FILTER\" | \"OAUTH2_LOGIN_FILTER\" | \"SAML2_AUTHENTICATION_FILTER\" | \"FORM_LOGIN_FILTER\" | \"DEFAULT_RESOURCES_FILTER\" | \"LOGIN_PAGE_FILTER\" | \"LOGOUT_PAGE_FILTER\" | \"DIGEST_AUTH_FILTER\" | \"BEARER_TOKEN_AUTH_FILTER\" | \"BASIC_AUTH_FILTER\" | \"REQUEST_CACHE_FILTER\" | \"SERVLET_API_SUPPORT_FILTER\" | \"JAAS_API_SUPPORT_FILTER\" | \"REMEMBER_ME_FILTER\" | \"ANONYMOUS_FILTER\" | \"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\" | \"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\" | \"SESSION_MANAGEMENT_FILTER\" | \"EXCEPTION_TRANSLATION_FILTER\" | \"FILTER_SECURITY_INTERCEPTOR\" | \"SWITCH_USER_FILTER\" | \"LAST\"\n"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security-7.1.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           xmlns:security=\"http://www.springframework.org/schema/security\"\n           elementFormDefault=\"qualified\"\n           targetNamespace=\"http://www.springframework.org/schema/security\">\n  <xs:attributeGroup name=\"hash\">\n      <xs:attribute name=\"hash\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"base64\">\n      <xs:attribute name=\"base64\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether a string should be base64 encoded\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher\">\n      <xs:attribute name=\"request-matcher\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"port\">\n      <xs:attribute name=\"port\" use=\"required\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"url\">\n      <xs:attribute name=\"url\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"id\">\n      <xs:attribute name=\"id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"name\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ref\">\n      <xs:attribute name=\"ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"cache-ref\">\n      <xs:attribute name=\"cache-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-service-ref\">\n      <xs:attribute name=\"user-service-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"authentication-manager-ref\">\n      <xs:attribute name=\"authentication-manager-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"data-source-ref\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"debug\">\n      <xs:annotation>\n         <xs:documentation>Enables Spring Security debugging infrastructure. This will provide human-readable\n                (multi-line) debugging information to monitor requests coming into the security filters.\n                This may include sensitive information, such as request parameters or headers, and should\n                only be used in a development environment.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType/>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"password-encoder.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"role-prefix\">\n      <xs:attribute name=\"role-prefix\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"use-expressions\">\n      <xs:attribute name=\"use-expressions\" use=\"required\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-server\">\n      <xs:annotation>\n         <xs:documentation>Defines an LDAP server location or starts an embedded server. The url indicates the\n                location of a remote server. If no url is given, an embedded server will be started,\n                listening on the supplied port number. The port is optional and defaults to 33389. A\n                Spring LDAP ContextSource bean will be registered for the server with the id supplied.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-server.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"port\" type=\"xs:nonNegativeInteger\">\n         <xs:annotation>\n            <xs:documentation>Specifies an IP port number. Used to configure an embedded LDAP server, for example.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-dn\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Username (DN) of the \"manager\" user identity which will be used to authenticate to a\n                (non-embedded) LDAP server. If omitted, anonymous access will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"manager-password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password for the manager DN. This is required if the manager-dn is specified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ldif\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies an ldif file resource to load into an embedded LDAP server. The\n                default is classpath*:*.ldiff\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"root\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Optional root suffix for the embedded LDAP server. Default is \"dc=springframework,dc=org\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Explicitly specifies which embedded ldap server should use. The only supported value is\n                'unboundid'. By default, it will depends if the library is available in the classpath.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unboundid\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"ldap-server-ref-attribute\">\n      <xs:attribute name=\"server-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-filter-attribute\">\n      <xs:attribute name=\"group-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-search-base-attribute\">\n      <xs:attribute name=\"group-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-filter-attribute\">\n      <xs:attribute name=\"user-search-filter\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-search-base-attribute\">\n      <xs:attribute name=\"user-search-base\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"group-role-attribute-attribute\">\n      <xs:attribute name=\"group-role-attribute\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-details-class-attribute\">\n      <xs:attribute name=\"user-details-class\" use=\"required\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"user-context-mapper-attribute\">\n      <xs:attribute name=\"user-context-mapper-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"ldap-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>This element configures a LdapUserDetailsService which is a combination of a\n                FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ldap-us.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"ldap-us.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ldap-ap.attlist\">\n      <xs:attribute name=\"server-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The optional server to use. If omitted, and a default LDAP server is registered (using\n                &lt;ldap-server&gt; with no Id), that server will be used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for user searches. Defaults to \"\". Only used with a 'user-search-filter'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP filter used to search for users (optional). For example \"(uid={0})\". The\n                substituted parameter is the user's login name.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-base\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Search base for group membership searches. Defaults to \"\" (searching from the root).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-search-filter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN\n                of the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-role-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The LDAP attribute name which contains the role name which will be used within Spring\n                Security. Defaults to \"cn\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-dn-pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A specific pattern used to build the user's DN, for example \"uid={0},ou=people\". The key\n                \"{0}\" must be present and will be substituted with the username.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-details-class\">\n         <xs:annotation>\n            <xs:documentation>Allows the objectClass of the user entry to be specified. If set, the framework will\n                attempt to load standard attributes for the defined class into the returned UserDetails\n                object\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"person\"/>\n               <xs:enumeration value=\"inetOrgPerson\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-context-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows explicit customization of the loaded user object by specifying a\n                UserDetailsContextMapper bean which will be called with the context information from the\n                user's directory entry\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"password-compare.attlist\">\n      <xs:attribute name=\"password-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The attribute in the directory which contains the user password. Defaults to\n                \"userPassword\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"hash\">\n         <xs:annotation>\n            <xs:documentation>Defines the hashing algorithm used on user passwords. Bcrypt is recommended.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"bcrypt\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-methods\">\n      <xs:annotation>\n         <xs:documentation>Can be used inside a bean definition to add a security interceptor to the bean and set up\n                access configuration attributes for the bean's methods\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:intercept-methods.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-methods.attlist\">\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to be used by the created method security\n                interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of the default (supercedes\n                use-authorization-manager)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"protect.attlist\">\n      <xs:attribute name=\"method\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A method name\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to the method, e.g. \"ROLE_A,ROLE_B\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Creates a MethodSecurityMetadataSource instance\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"protect\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected method and the access control configuration attributes that apply to\n                it. We strongly advise you NOT to mix \"protect\" declarations with any services provided\n                \"global-method-security\".\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:msmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"msmds.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with Spring Security annotations. Where\n                there is a match, the beans will automatically be proxied and security authorization\n                applied to the methods accordingly. Interceptors are invoked in the order specified in\n                AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.\n                Also, annotation-based interception can be overridden by expressions listed in\n                &lt;protect-pointcut&gt; elements.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"method-security.attlist\">\n      <xs:attribute name=\"pre-post-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"secured-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>If set to aspectj, then use AspectJ to intercept method invocation\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based\n                strategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"global-method-security\">\n      <xs:annotation>\n         <xs:documentation>Provides method security for all beans registered in the Spring application context.\n                Specifically, beans will be scanned for matches with the ordered list of\n                \"protect-pointcut\" sub-elements, Spring Security annotations and/or. Where there is a\n                match, the beans will automatically be proxied and security authorization applied to the\n                methods accordingly. If you use and enable all four sources of method security metadata\n                (ie \"protect-pointcut\" declarations, expression annotations, @Secured and also JSR250\n                security annotations), the metadata sources will be queried in that order. In practical\n                terms, this enables you to use XML to override method security metadata expressed in\n                annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize\n                etc.), @Secured and finally JSR-250.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:choice minOccurs=\"0\">\n               <xs:element name=\"pre-post-annotation-handling\">\n                  <xs:annotation>\n                     <xs:documentation>Allows the default expression-based mechanism for handling Spring Security's pre and post\n                invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be\n                replace entirely. Only applies if these annotations are enabled.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:sequence>\n                        <xs:element name=\"invocation-attribute-factory\">\n                           <xs:annotation>\n                              <xs:documentation>Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and\n                post invocation metadata from the annotated methods.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"pre-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the\n                PreInvocationAuthorizationAdviceVoter for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                        <xs:element name=\"post-invocation-advice\">\n                           <xs:annotation>\n                              <xs:documentation>Customizes the PostInvocationAdviceProvider with the ref as the\n                PostInvocationAuthorizationAdvice for the &lt;pre-post-annotation-handling&gt; element.\n                </xs:documentation>\n                           </xs:annotation>\n                           <xs:complexType>\n                              <xs:attributeGroup ref=\"security:ref\"/>\n                           </xs:complexType>\n                        </xs:element>\n                     </xs:sequence>\n                  </xs:complexType>\n               </xs:element>\n               <xs:element name=\"expression-handler\">\n                  <xs:annotation>\n                     <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n                  </xs:annotation>\n                  <xs:complexType>\n                     <xs:attributeGroup ref=\"security:ref\"/>\n                  </xs:complexType>\n               </xs:element>\n            </xs:choice>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"protect-pointcut\">\n               <xs:annotation>\n                  <xs:documentation>Defines a protected pointcut and the access control configuration attributes that apply to\n                it. Every bean registered in the Spring application context that provides a method that\n                matches the pointcut will receive security authorization.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:protect-pointcut.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"after-invocation-provider\">\n               <xs:annotation>\n                  <xs:documentation>Allows addition of extra AfterInvocationProvider beans which should be called by the\n                MethodSecurityInterceptor created by global-method-security.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:global-method-security.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"global-method-security.attlist\">\n      <xs:attribute name=\"pre-post-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations\n                (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this\n                application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"secured-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for\n                this application context. Defaults to \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"jsr250-annotations\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\n                This will require the javax.annotation.security classes on the classpath. Defaults to\n                \"disabled\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"disabled\"/>\n               <xs:enumeration value=\"enabled\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional AccessDecisionManager bean ID to override the default used for method security.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"run-as-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional RunAsmanager implementation which will be used by the configured\n                MethodSecurityInterceptor\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"order\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows the advice \"order\" to be set for the method security interceptor.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"proxy-target-class\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If true, class based proxying will be used instead of interface based proxying.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"mode\">\n         <xs:annotation>\n            <xs:documentation>Can be used to specify that AspectJ should be used instead of the default Spring AOP. If\n                set, secured classes must be woven with the AnnotationSecurityAspect from the\n                spring-security-aspects module.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"aspectj\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An external MethodSecurityMetadataSource instance can be supplied which will take priority\n                over other sources (such as the default annotations).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  \n  \n  \n  \n  \n  <xs:attributeGroup name=\"protect-pointcut.attlist\">\n      <xs:attribute name=\"expression\" use=\"required\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>An AspectJ expression, including the 'execution' keyword. For example, 'execution(int\n                com.foo.TargetObject.countLength(String))' (without the quotes).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Access configuration attributes list that applies to all methods matching the pointcut,\n                e.g. \"ROLE_A,ROLE_B\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"websocket-message-broker\">\n      <xs:annotation>\n         <xs:documentation>Allows securing a Message Broker. There are two modes. If no id is specified: ensures that\n                any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver\n                registered as a custom argument resolver; ensures that the\n                SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that\n                can be manually registered with the clientInboundChannel.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:intercept-message\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:websocket-message-broker.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"websocket-message-broker.attrlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context. If specified,\n                explicit configuration within clientInboundChannel is required. If not specified, ensures\n                that any SimpAnnotationMethodMessageHandler has the\n                AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures\n                that the SecurityContextChannelInterceptor is automatically registered for the\n                clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the\n                clientInboundChannel.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"same-origin-disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Disables the requirement for CSRF token to be present in the Stomp headers (default\n                false). Changing the default is useful if it is necessary to allow other origins to make\n                SockJS connections.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-message&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Use this SecurityContextHolderStrategy (note only supported in conjunction with the\n                AuthorizationManager API)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"intercept-message\">\n      <xs:annotation>\n         <xs:documentation>Creates an authorization rule for a websocket message.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:intercept-message.attrlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"intercept-message.attrlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The destination ant pattern which will be mapped to the access attribute. For example, /**\n                matches any message with a destination, /admin/** matches any message that has a\n                destination that starts with admin.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured message. For example,\n                permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role\n                'ROLE_ADMIN'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"type\">\n         <xs:annotation>\n            <xs:documentation>The type of message to match on. Valid values are defined in SimpMessageType (i.e.\n                CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT,\n                DISCONNECT_ACK, OTHER).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"CONNECT\"/>\n               <xs:enumeration value=\"CONNECT_ACK\"/>\n               <xs:enumeration value=\"HEARTBEAT\"/>\n               <xs:enumeration value=\"MESSAGE\"/>\n               <xs:enumeration value=\"SUBSCRIBE\"/>\n               <xs:enumeration value=\"UNSUBSCRIBE\"/>\n               <xs:enumeration value=\"DISCONNECT\"/>\n               <xs:enumeration value=\"DISCONNECT_ACK\"/>\n               <xs:enumeration value=\"OTHER\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"http-firewall\">\n      <xs:annotation>\n         <xs:documentation>Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created\n                by the namespace.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"http\">\n      <xs:annotation>\n         <xs:documentation>Container element for HTTP security configuration. Multiple elements can now be defined,\n                each with a specific pattern to which the enclosed security configuration applies. A\n                pattern can also be configured to bypass Spring Security's filters completely by setting\n                the \"security\" attribute to \"none\".\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"access-denied-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the access-denied strategy that should be used. An access denied page can be\n                defined or a reference to an AccessDeniedHandler instance.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:access-denied-handler.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"form-login\">\n               <xs:annotation>\n                  <xs:documentation>Sets up a form login configuration for authentication with a username and password\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:form-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:oauth2-login\"/>\n            <xs:element ref=\"security:oauth2-client\"/>\n            <xs:element ref=\"security:oauth2-resource-server\"/>\n            <xs:element name=\"saml2-login\">\n               <xs:annotation>\n                  <xs:documentation>Configures authentication support for SAML 2.0 Login\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-login.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"saml2-logout\">\n               <xs:annotation>\n                  <xs:documentation>Configures SAML 2.0 Single Logout support\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:saml2-logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"x509\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for X.509 client authentication.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:x509.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:jee\"/>\n            <xs:element name=\"http-basic\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for basic authentication\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:http-basic.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"logout\">\n               <xs:annotation>\n                  <xs:documentation>Incorporates a logout processing filter. Most web applications require a logout filter,\n                although you may not require one if you write a controller to provider similar logic.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:logout.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:password-management\"/>\n            <xs:element name=\"session-management\">\n               <xs:annotation>\n                  <xs:documentation>Session-management related functionality is implemented by the addition of a\n                SessionManagementFilter to the filter stack.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"concurrency-control\">\n                        <xs:annotation>\n                           <xs:documentation>Enables concurrent session control, limiting the number of authenticated sessions a user\n                may have at the same time.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:concurrency-control.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:session-management.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"remember-me\">\n               <xs:annotation>\n                  <xs:documentation>Sets up remember-me authentication. If used with the \"key\" attribute (or no attributes)\n                the cookie-only implementation will be used. Specifying \"token-repository-ref\" or\n                \"remember-me-data-source-ref\" will use the more secure, persisten token approach.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:remember-me.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"anonymous\">\n               <xs:annotation>\n                  <xs:documentation>Adds support for automatically granting all anonymous web requests a particular principal\n                identity and a corresponding granted authority.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:anonymous.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"port-mappings\">\n               <xs:annotation>\n                  <xs:documentation>Defines the list of mappings between http and https ports for use in redirects\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element maxOccurs=\"unbounded\" name=\"port-mapping\">\n                        <xs:annotation>\n                           <xs:documentation>Provides a method to map http ports to https ports when forcing a redirect.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:http-port\"/>\n                           <xs:attributeGroup ref=\"security:https-port\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:custom-filter\"/>\n            <xs:element ref=\"security:request-cache\"/>\n            <xs:element name=\"expression-handler\">\n               <xs:annotation>\n                  <xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based\n                access-control is enabled. A default implementation (with no ACL support) will be used if\n                not supplied.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:ref\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element ref=\"security:headers\"/>\n            <xs:element ref=\"security:csrf\"/>\n            <xs:element ref=\"security:cors\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:http.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"http.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the filter chain created by this &lt;http&gt;\n                element. If omitted, the filter chain will match all requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security\">\n         <xs:annotation>\n            <xs:documentation>When set to 'none', requests matching the pattern attribute will be ignored by Spring\n                Security. No security filters will be applied and no SecurityContext will be available. If\n                set, the &lt;http&gt; element must be empty, with no children.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-to-https-request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the RequestMatcher implementation used to decide\n                whether to redirect a request to HTTPS\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"auto-config\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>A legacy attribute which automatically registers a login form, BASIC authentication and a\n                logout URL and logout services. If unspecified, defaults to \"false\". We'd recommend you\n                avoid using this and instead explicitly configure the services you require.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-holder-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the\n                SecurityContextHolder is stored during a request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"create-session\">\n         <xs:annotation>\n            <xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes.\n                If not set, defaults to \"ifRequired\". If \"stateless\" is used, this implies that the\n                application guarantees that it will not create a session. This differs from the use of\n                \"never\" which means that Spring Security will not create a session, but will make use of\n                one if the application does.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"ifRequired\"/>\n               <xs:enumeration value=\"always\"/>\n               <xs:enumeration value=\"never\"/>\n               <xs:enumeration value=\"stateless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a SecurityContextRepository bean. This can be used to customize how the\n                SecurityContext is stored between requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"security-context-explicit-save\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving\n                rather than being synchronized from the SecurityContextHolder. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and\n                getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to\n                \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jaas-api-provision\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken.\n                Defaults to \"false\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-authorization-manager\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this AuthorizationManager instead of deriving one from &lt;intercept-url&gt; elements\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-decision-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which\n                should be used for authorizing HTTP requests.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"realm\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Optional attribute specifying the realm name that will be used for all authentication\n                features that require a realm name (eg BASIC and Digest authentication). If unspecified,\n                defaults to \"Spring Security Application\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"once-per-request\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults\n                to \"false\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filter-all-dispatcher-types\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not\n                work when use-authorization-manager=false. Defaults to \"true\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disable-url-rewriting\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Prevents the jsessionid parameter from being added to rendered URLs. Defaults to \"true\"\n                (rewriting is disabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to an AuthenticationManager bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"access-denied-handler.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"access-denied-handler-page\">\n      <xs:attribute name=\"error-page\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access denied page that an authenticated user will be redirected to if they request a\n                page which they don't have the authority to access.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"intercept-url.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The access configuration attributes that apply for the configured path.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"method\">\n         <xs:annotation>\n            <xs:documentation>The HTTP Method for which the access configuration attributes should apply. If not\n                specified, the attributes will apply to any method.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"GET\"/>\n               <xs:enumeration value=\"DELETE\"/>\n               <xs:enumeration value=\"HEAD\"/>\n               <xs:enumeration value=\"OPTIONS\"/>\n               <xs:enumeration value=\"POST\"/>\n               <xs:enumeration value=\"PUT\"/>\n               <xs:enumeration value=\"PATCH\"/>\n               <xs:enumeration value=\"TRACE\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"requires-channel\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Used to specify that a URL must be accessed over http or https, or that there is no\n                preference. The value should be \"http\", \"https\" or \"any\", respectively.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"servlet-path\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is\n                'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are\n                2 or more HttpServlet's registered in the ServletContext that have mappings starting with\n                '/' and are different; 2) The pattern starts with the same value of a registered\n                HttpServlet path, excluding the default (root) HttpServlet '/'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL that will cause a logout. Spring Security will initialize a filter that\n                responds to this particular URL. Defaults to /logout if unspecified.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-success-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URL to display once the user has logged out. If not specified, defaults to\n                &lt;form-login-login-page&gt;/?logout (i.e. /login?logout).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalidate-session\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies whether a logout also causes HttpSession invalidation, which is generally\n                desirable. If unspecified, defaults to true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a LogoutSuccessHandler implementation which will be used to determine the\n                destination to which the user is taken after logging out.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"delete-cookies\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of the names of cookies which should be deleted when the user logs\n                out\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"request-cache\">\n      <xs:annotation>\n         <xs:documentation>Allow the RequestCache used for saving requests during the login process to be set\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:ref\"/>\n      </xs:complexType>\n   </xs:element>\n  \n  <xs:attributeGroup name=\"form-login.attlist\">\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that the login form is posted to. If unspecified, it defaults to /login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the username. Defaults to 'username'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which contains the password. Defaults to 'password'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"default-target-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL that will be redirected to after successful authentication, if the user's previous\n                action could not be resumed. This generally happens if the user visits a login page\n                without having first requested a secured operation that triggers authentication. If\n                unspecified, defaults to the root of the application.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"always-use-default-target\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Whether the user should always be redirected to the default-target-url after login.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login page. If no login URL is specified, Spring Security will\n                automatically create a login URL at GET /login and a corresponding filter to render that\n                login URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the login failure page. If no login failure URL is specified, Spring Security\n                will automatically create a failure login URL at /login?error and a corresponding filter\n                to render that login failure URL when requested.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful authentication request. Should not be used in combination with\n                default-target-url (or always-use-default-target-url) as the implementation should always\n                deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed\n                authentication request. Should not be used in combination with authentication-failure-url\n                as the implementation should always deal with navigation to the subsequent destination\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-forward-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL for the ForwardAuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-login\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:oauth2-login.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-login.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-authorities-mapper-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the GrantedAuthoritiesMapper\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"oidc-user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OpenID Connect OAuth2UserService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-decoder-factory-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-client\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Client support.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" ref=\"security:authorization-code-grant\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:oauth2-client.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-client.attlist\">\n      <xs:attribute name=\"client-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the ClientRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorized-client-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizedClientService\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authorization-code-grant\">\n      <xs:annotation>\n         <xs:documentation>Configures OAuth 2.0 Authorization Code Grant.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:authorization-code-grant.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authorization-code-grant.attlist\">\n      <xs:attribute name=\"authorization-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthorizationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-redirect-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the authorization RedirectStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AuthorizationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"access-token-response-client-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the OAuth2AccessTokenResponseClient\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"client-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0\n                Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:client-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:provider\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"client-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:client-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"client-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the client registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client identifier.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client secret.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The method used to authenticate the client with the provider. The supported values are\n                client_secret_basic, client_secret_post and none (public clients).\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"client_secret_basic\"/>\n               <xs:enumeration value=\"basic\"/>\n               <xs:enumeration value=\"client_secret_post\"/>\n               <xs:enumeration value=\"post\"/>\n               <xs:enumeration value=\"none\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-grant-type\">\n         <xs:annotation>\n            <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The\n                supported values are authorization_code, client_credentials and password.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"authorization_code\"/>\n               <xs:enumeration value=\"client_credentials\"/>\n               <xs:enumeration value=\"password\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"redirect-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The client’s registered redirect URI that the Authorization Server redirects the\n                end-user’s user-agent to after the end-user has authenticated and authorized access to the\n                client.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"scope\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separated list of scope(s) requested by the client during the Authorization\n                Request flow, such as openid, email, or profile.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A descriptive name used for the client. The name may be used in certain scenarios, such as\n                when displaying the name of the client in the auto-generated login page.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated provider. May reference a 'provider' element or use one of\n                the common providers (google, github, facebook, okta).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"provider\">\n      <xs:annotation>\n         <xs:documentation>The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:provider.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"provider.attlist\">\n      <xs:attribute name=\"provider-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorization-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Authorization Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Token Endpoint URI for the Authorization Server.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The UserInfo Endpoint URI used to access the claims/attributes of the authenticated\n                end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-authentication-method\">\n         <xs:annotation>\n            <xs:documentation>The authentication method used when sending the access token to the UserInfo Endpoint. The\n                supported values are header, form and query.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"header\"/>\n               <xs:enumeration value=\"form\"/>\n               <xs:enumeration value=\"query\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"user-info-user-name-attribute\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the attribute returned in the UserInfo Response that references the Name or\n                Identifier of the end-user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which\n                contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID\n                Token and optionally the UserInfo Response.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"issuer-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect\n                1.0 Provider.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"oauth2-resource-server\">\n      <xs:annotation>\n         <xs:documentation>Configures authentication support as an OAuth 2.0 Resource Server.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:jwt\"/>\n            <xs:element ref=\"security:opaque-token\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:oauth2-resource-server.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"oauth2-resource-server.attlist\">\n      <xs:attribute name=\"authentication-manager-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationManagerResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"bearer-token-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a BearerTokenResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationEntryPoint\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jwt\">\n      <xs:annotation>\n         <xs:documentation>Configures JWT authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jwt.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jwt.attlist\">\n      <xs:attribute name=\"jwk-set-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to collect the JWK Set for verifying JWTs\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"decoder-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a JwtDecoder\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"jwt-authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a Converter&lt;Jwt, AbstractAuthenticationToken&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"opaque-token\">\n      <xs:annotation>\n         <xs:documentation>Configuration Opaque Token authentication\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:opaque-token.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"opaque-token.attlist\">\n      <xs:attribute name=\"introspection-uri\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to use to introspect opaque token attributes\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client ID to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"client-secret\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Client secret to use to authenticate the introspection request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"introspector-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenIntrospector\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful\n                introspection result into an Authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-login.attlist\">\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2AuthenticationRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-converter-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationConverter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-processing-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI where the filter processes authentication requests\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"login-page\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URI to send users to login\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationSuccessHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-failure-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationFailureHandler\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-manager-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the AuthenticationManager\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"saml2-logout.attlist\">\n      <xs:attribute name=\"logout-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the relying or asserting party can trigger logout\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Request\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL by which the asserting party can send a SAML 2.0 Logout Response\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"relying-party-registration-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the RelyingPartyRegistrationRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-request-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutRequestRepository\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-validator-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseValidator\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"logout-response-resolver-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to the Saml2LogoutResponseResolver\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registrations\">\n      <xs:annotation>\n         <xs:documentation>Container element for relying party(ies) registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:relying-party-registration\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:asserting-party\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registrations.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registrations.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The identifier by which to refer to the repository in other beans\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"relying-party-registration\">\n      <xs:annotation>\n         <xs:documentation>Represents a relying party registered with a SAML 2.0 identity provider\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:signing-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:decryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:relying-party-registration.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"relying-party-registration.attlist\">\n      <xs:attribute name=\"registration-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The ID that uniquely identifies the relying party registration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"metadata-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of the Identity Provider's metadata.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party's EntityID\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"assertion-consumer-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The Assertion Consumer Service Binding\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"asserting-party-id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to the associated asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The relying party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"signing-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's signing credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:signing-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"signing-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"decryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's decryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:decryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"decryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"asserting-party\">\n      <xs:annotation>\n         <xs:documentation>The configuration metadata of the Asserting party\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:verification-credential\"/>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" ref=\"security:encryption-credential\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:asserting-party.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"asserting-party.attlist\">\n      <xs:attribute name=\"asserting-party-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A unique identifier of the asserting party.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"entity-id\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party's EntityID.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"want-authn-requests-signed\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Indicates the asserting party's preference that relying parties should sign the\n                AuthnRequest before sending\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Location.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-sign-on-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The &lt;a\n                href=\"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\"&gt;SingleSignOnService&lt;/a&gt;\n                Binding.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"signing-algorithms\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n                asserting party, in preference order.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-response-location\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Response Location&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"single-logout-service-binding\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The asserting party &lt;a\n                href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\"&gt;SingleLogoutService\n                Binding&lt;/a&gt;\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"verification-credential\">\n      <xs:annotation>\n         <xs:documentation>The relying party's verification credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:verification-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"verification-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"encryption-credential\">\n      <xs:annotation>\n         <xs:documentation>The asserting party's encryption credential\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:encryption-credential.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"encryption-credential.attlist\">\n      <xs:attribute name=\"private-key-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The private key location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"certificate-location\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The certificate location\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain-map\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:filter-chain\"/>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:filter-chain-map.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain-map.attlist\">\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-chain\">\n      <xs:annotation>\n         <xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the\n                URLs matching that pattern. When multiple filter-chain elements are assembled in a list in\n                order to configure a FilterChainProxy, the most specific patterns must be placed at the\n                top of the list, with most general ones at the bottom.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:filter-chain.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"filter-chain.attlist\">\n      <xs:attribute name=\"pattern\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"filters\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma separated list of bean names that implement Filter that should be processed for\n                this FilterChain. If the value is none, then no Filters will be used for this FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"pattern\">\n      <xs:attribute name=\"pattern\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The request URL pattern which will be mapped to the FilterChain.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"request-matcher-ref\">\n      <xs:attribute name=\"request-matcher-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"filter-security-metadata-source\">\n      <xs:annotation>\n         <xs:documentation>Used to explicitly configure a FilterSecurityMetadataSource bean for use with a\n                FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy\n                explicitly, rather than using the &lt;http&gt; element. The intercept-url elements used should\n                only contain pattern, method and access attributes. Any others will result in a\n                configuration error.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" name=\"intercept-url\">\n               <xs:annotation>\n                  <xs:documentation>Specifies the access attributes and/or filter list for a particular set of URLs.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:intercept-url.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attributeGroup ref=\"security:fsmds.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"fsmds.attlist\">\n      <xs:attribute name=\"use-expressions\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements\n                rather than the traditional list of configuration attributes. Defaults to 'true'. If\n                enabled, each attribute should contain a single boolean expression. If the expression\n                evaluates to 'true', access will be granted.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher\">\n         <xs:annotation>\n            <xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'\n                (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions\n                and 'ciRegex' for case-insensitive regular expressions.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"mvc\"/>\n               <xs:enumeration value=\"ant\"/>\n               <xs:enumeration value=\"regex\"/>\n               <xs:enumeration value=\"ciRegex\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"http-basic.attlist\">\n      <xs:attribute name=\"entry-point-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"password-management\">\n      <xs:annotation>\n         <xs:documentation>Adds support for the password management.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:password-management.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"password-management.attlist\">\n      <xs:attribute name=\"change-password-page\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The change password page. Defaults to \"/change-password\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"session-management.attlist\">\n      <xs:attribute name=\"authentication-strategy-explicit-invocation\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false\n                (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-fixation-protection\">\n         <xs:annotation>\n            <xs:documentation>Indicates how session fixation protection will be applied when a user authenticates. If\n                set to \"none\", no protection will be applied. \"newSession\" will create a new empty\n                session, with only Spring Security-related attributes migrated. \"migrateSession\" will\n                create a new session and copy all session attributes to the new session. In Servlet 3.1\n                (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing\n                session and use the container-supplied session fixation protection\n                (HttpServletRequest#changeSessionId()). Defaults to \"changeSessionId\" in Servlet 3.1 and\n                newer containers, \"migrateSession\" in older containers. Throws an exception if\n                \"changeSessionId\" is used in older containers.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"none\"/>\n               <xs:enumeration value=\"newSession\"/>\n               <xs:enumeration value=\"migrateSession\"/>\n               <xs:enumeration value=\"changeSessionId\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier.\n                Typically used to detect session timeouts.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"invalid-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the InvalidSessionStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the\n                SessionManagementFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-authentication-error-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines the URL of the error page which should be shown when the\n                SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error\n                code will be returned to the client. Note that this attribute doesn't apply if the error\n                occurs during a form-based login, where the URL for authentication failure will take\n                precedence.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"concurrency-control.attlist\">\n      <xs:attribute name=\"max-sessions\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The maximum number of sessions a single authenticated user can have open at the same time.\n                Defaults to \"1\". A negative value denotes unlimited sessions.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-sessions-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionLimit instance used by the\n                ConcurrentSessionControlAuthenticationStrategy\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-url\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The URL a user will be redirected to if they attempt to use a session which has been\n                \"expired\" because they have logged in again.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"expired-session-strategy-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows injection of the SessionInformationExpiredStrategy instance used by the\n                ConcurrentSessionFilter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"error-if-maximum-exceeded\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when\n                they already have the maximum configured sessions open. The default behaviour is to expire\n                the original session. If the session-authentication-error-url attribute is set on the\n                session-management URL, the user will be redirected to this URL.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an alias for the SessionRegistry bean in order to access it in your\n                own configuration.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"session-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows you to define an external SessionRegistry bean to be used by the concurrency\n                control setup.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"remember-me.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The \"key\" used to identify cookies from a specific token-based remember-me application.\n                You should set this to a unique value for your application. If unset, it will default to a\n                random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"data-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a DataSource bean\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attributeGroup ref=\"security:remember-me-services-ref\"/>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"services-alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Exports the internally defined RememberMeServices as a bean alias, allowing it to be used\n                by other beans in the application context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"use-secure-cookie\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Determines whether the \"secure\" flag will be set on the remember-me cookie. If set to\n                true, the cookie will only be submitted over HTTPS (recommended). By default, secure\n                cookies will be used if the request is made on a secure connection.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-validity-seconds\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The period (in seconds) for which the remember-me cookie should be valid.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-success-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationSuccessHandler bean which should be used to handle a\n                successful remember-me authentication.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-parameter\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the request parameter which toggles remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"remember-me-cookie\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of cookie which store the token for remember-me authentication. Defaults to\n                'remember-me'.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"token-repository-ref\">\n      <xs:attribute name=\"token-repository-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to a PersistentTokenRepository bean for use with the persistent token\n                remember-me implementation.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-services-ref\">\n      <xs:attribute name=\"services-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this\n                implementation should return RememberMeAuthenticationToken instances with the same \"key\"\n                value as specified in the remember-me element. Alternatively it should register its own\n                AuthenticationProvider. It should also implement the LogoutHandler interface, which will\n                be invoked when a user logs out. Typically the remember-me cookie would be removed on\n                logout.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"remember-me-data-source-ref\">\n      <xs:attributeGroup ref=\"security:data-source-ref\"/>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"anonymous.attlist\">\n      <xs:attribute name=\"key\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The key shared between the provider and filter. This generally does not need to be set. If\n                unset, it will default to a random value generated by SecureRandom.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"username\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username that should be assigned to the anonymous request. This allows the principal\n                to be identified, which may be important for logging and auditing. if unset, defaults to\n                \"anonymousUser\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"granted-authority\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The granted authority that should be assigned to the anonymous request. Commonly this is\n                used to assign the anonymous request particular roles, which can subsequently be used in\n                authorization decisions. If unset, defaults to \"ROLE_ANONYMOUS\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"enabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>With the default namespace setup, the anonymous \"authentication\" facility is automatically\n                enabled. You can disable it using this property.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  \n  <xs:attributeGroup name=\"http-port\">\n      <xs:attribute name=\"http\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The http port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"https-port\">\n      <xs:attribute name=\"https\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The https port to use.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"x509.attlist\">\n      <xs:attribute name=\"subject-principal-regex\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The regular expression used to obtain the username from the certificate's subject.\n                Defaults to matching on the common name using the pattern \"CN=(.*?),\".\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authentication-details-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication\n                filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"principal-extractor-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Reference to an X509PrincipalExtractor which will be used by the authentication filter\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jee\">\n      <xs:annotation>\n         <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration\n                with container authentication.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:jee.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jee.attlist\">\n      <xs:attribute name=\"mappable-roles\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A comma-separate list of roles to look for in the incoming HttpServletRequest.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"authentication-manager\">\n      <xs:annotation>\n         <xs:documentation>Registers the AuthenticationManager instance and allows its list of\n                AuthenticationProviders to be defined. Also allows you to define an alias to allow you to\n                reference the AuthenticationManager in your own beans.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element name=\"authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Indicates that the contained user-service should be used as an authentication source.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n                     <xs:element ref=\"security:any-user-service\"/>\n                     <xs:element name=\"password-encoder\">\n                        <xs:annotation>\n                           <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:choice>\n                  <xs:attributeGroup ref=\"security:ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n            <xs:element name=\"ldap-authentication-provider\">\n               <xs:annotation>\n                  <xs:documentation>Sets up an ldap authentication provider\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:sequence>\n                     <xs:element minOccurs=\"0\" name=\"password-compare\">\n                        <xs:annotation>\n                           <xs:documentation>Specifies that an LDAP provider should use an LDAP compare operation of the user's\n                password to authenticate the user\n                </xs:documentation>\n                        </xs:annotation>\n                        <xs:complexType>\n                           <xs:sequence>\n                              <xs:element minOccurs=\"0\" name=\"password-encoder\">\n                                 <xs:annotation>\n                                    <xs:documentation>element which defines a password encoding strategy. Used by an authentication provider to\n                convert submitted passwords to hashed versions, for example.\n                </xs:documentation>\n                                 </xs:annotation>\n                                 <xs:complexType>\n                                    <xs:attributeGroup ref=\"security:password-encoder.attlist\"/>\n                                 </xs:complexType>\n                              </xs:element>\n                           </xs:sequence>\n                           <xs:attributeGroup ref=\"security:password-compare.attlist\"/>\n                        </xs:complexType>\n                     </xs:element>\n                  </xs:sequence>\n                  <xs:attributeGroup ref=\"security:ldap-ap.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:authman.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"authman.attlist\">\n      <xs:attribute name=\"id\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"alias\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using\n                a specific id)\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"erase-credentials\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If set to true, the AuthenticationManger will attempt to clear any credentials data in the\n                returned Authentication object, once the user has been authenticated.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"observation-registry-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Use this ObservationRegistry to collect metrics on various parts of the filter chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"ap.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"user-service-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Creates an in-memory UserDetailsService from a properties file or a list of \"user\" child\n                elements. Usernames are converted to lower-case internally to allow for case-insensitive\n                lookups, so this should not be used if case-sensitivity is required.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element minOccurs=\"0\" maxOccurs=\"unbounded\" name=\"user\">\n               <xs:annotation>\n                  <xs:documentation>Represents a user in the application.\n                </xs:documentation>\n               </xs:annotation>\n               <xs:complexType>\n                  <xs:attributeGroup ref=\"security:user.attlist\"/>\n               </xs:complexType>\n            </xs:element>\n         </xs:sequence>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:properties-file\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"properties-file\">\n      <xs:attribute name=\"properties\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The location of a Properties file where each line is in the format of\n                username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  \n  <xs:attributeGroup name=\"user.attlist\">\n      <xs:attribute name=\"name\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The username assigned to the user.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"password\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>The password assigned to the user. This may be hashed if the corresponding authentication\n                provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\"\n                element). This attribute be omitted in the case where the data will not be used for\n                authentication, but only for accessing authorities. If omitted, the namespace will\n                generate a random value, preventing its accidental use for authentication. Cannot be\n                empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>One of more authorities granted to the user. Separate authorities with a comma (but no\n                space). For example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"locked\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as locked and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Can be set to \"true\" to mark an account as disabled and unusable.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"jdbc-user-service\" substitutionGroup=\"security:any-user-service\">\n      <xs:annotation>\n         <xs:documentation>Causes creation of a JDBC-based UserDetailsService.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attribute name=\"id\" type=\"xs:token\">\n            <xs:annotation>\n               <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n         <xs:attributeGroup ref=\"security:jdbc-user-service.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"jdbc-user-service.attlist\">\n      <xs:attribute name=\"data-source-ref\" use=\"required\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The bean ID of the DataSource which provides the required tables.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"cache-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a cache for use with a UserDetailsService.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"users-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query a username, password, and enabled status given a username.\n                Default is \"select username,password,enabled from users where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query for a user's granted authorities given a username. The default\n                is \"select username, authority from authorities where username = ?\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"group-authorities-by-username-query\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>An SQL statement to query user's group authorities given a username. The default is\n                \"select g.id, g.group_name, ga.authority from groups g, group_members gm,\n                group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"role-prefix\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>A non-empty string prefix that will be added to role strings loaded from persistent\n                storage (e.g. \"ROLE_\"). Use the value \"none\" for no prefix in cases where the default is\n                non-empty.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"csrf\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the CsrfFilter for protection against CSRF. It also updates\n                the default RequestCache to only replay \"GET\" requests.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csrf-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csrf-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is\n                enabled).\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is\n                any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\"\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"token-repository-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by\n                LazyCsrfTokenRepository.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-handler-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"headers\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of the HeaderWritersFilter. Enables easy setting for the\n                X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">\n            <xs:element ref=\"security:cache-control\"/>\n            <xs:element ref=\"security:xss-protection\"/>\n            <xs:element ref=\"security:hsts\"/>\n            <xs:element ref=\"security:frame-options\"/>\n            <xs:element ref=\"security:content-type-options\"/>\n            <xs:element ref=\"security:hpkp\"/>\n            <xs:element ref=\"security:content-security-policy\"/>\n            <xs:element ref=\"security:referrer-policy\"/>\n            <xs:element ref=\"security:feature-policy\"/>\n            <xs:element ref=\"security:permissions-policy\"/>\n            <xs:element ref=\"security:cross-origin-opener-policy\"/>\n            <xs:element ref=\"security:cross-origin-embedder-policy\"/>\n            <xs:element ref=\"security:cross-origin-resource-policy\"/>\n            <xs:element ref=\"security:header\"/>\n         </xs:choice>\n         <xs:attributeGroup ref=\"security:headers-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"headers-options.attlist\">\n      <xs:attribute name=\"defaults-disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the default headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"disabled\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies if headers should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hsts\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Strict Transport Security (HSTS)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:hsts-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hsts-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Specifies the maximum amount of time the host should be considered a Known HSTS Host.\n                Default one year.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"request-matcher-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The RequestMatcher instance to be used to determine if the header should be set. Default\n                is if HttpServletRequest.isSecure() is true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"preload\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if preload should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cors\">\n      <xs:annotation>\n         <xs:documentation>Element for configuration of CorsFilter. A CorsConfigurationSource must be specified. If\n                Spring MVC is present, then it will attempt to look up its `CorsConfigurationSource`.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cors-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cors-options.attlist\">\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"configuration-source-ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to\n                use\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"hpkp\">\n      <xs:annotation>\n         <xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:complexContent>\n            <xs:extension base=\"security:hpkp.pins\">\n               <xs:attributeGroup ref=\"security:hpkp.attlist\"/>\n            </xs:extension>\n         </xs:complexContent>\n      </xs:complexType>\n   </xs:element>\n  <xs:complexType name=\"hpkp.pins\">\n      <xs:sequence>\n         <xs:element ref=\"security:pins\"/>\n      </xs:sequence>\n  </xs:complexType>\n  <xs:element name=\"pins\">\n      <xs:annotation>\n         <xs:documentation>The list with pins\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:sequence>\n            <xs:element maxOccurs=\"unbounded\" ref=\"security:pin\"/>\n         </xs:sequence>\n      </xs:complexType>\n   </xs:element>\n  <xs:element name=\"pin\">\n      <xs:annotation>\n         <xs:documentation>A pin is specified using the base64-encoded SPKI fingerprint as value and the\n                cryptographic hash algorithm as attribute\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType mixed=\"true\">\n         <xs:attribute name=\"algorithm\" type=\"xs:string\">\n            <xs:annotation>\n               <xs:documentation>The cryptographic hash algorithm\n                </xs:documentation>\n            </xs:annotation>\n         </xs:attribute>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"hpkp.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"include-subdomains\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if subdomains should be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"max-age-seconds\" type=\"xs:integer\">\n         <xs:annotation>\n            <xs:documentation>Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if the browser should only report pin validation failures. Default true.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-uri\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specifies the URI to which the browser should report pin validation failures.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-security-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Content Security Policy (CSP)\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:csp-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"csp-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Content-Security-Policy header or if report-only\n                is set to true, then the Content-Security-Policy-Report-Only header is used.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"report-only\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy\n                violations only. Defaults to false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"referrer-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Referrer Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:referrer-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"referrer-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Referrer-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"no-referrer\"/>\n               <xs:enumeration value=\"no-referrer-when-downgrade\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"origin\"/>\n               <xs:enumeration value=\"strict-origin\"/>\n               <xs:enumeration value=\"origin-when-cross-origin\"/>\n               <xs:enumeration value=\"strict-origin-when-cross-origin\"/>\n               <xs:enumeration value=\"unsafe-url\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"feature-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Feature Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:feature-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"feature-options.attlist\">\n      <xs:attribute name=\"policy-directives\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The security policy directive(s) for the Feature-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"permissions-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Permissions Policy\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:permissions-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"permissions-options.attlist\">\n      <xs:attribute name=\"policy\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Permissions-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cache-control\">\n      <xs:annotation>\n         <xs:documentation>Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for\n                every request\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cache-control.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cache-control.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>Specifies if Cache Control should be disabled. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"frame-options\">\n      <xs:annotation>\n         <xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options\n                header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:frame-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"frame-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Frame-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>Specify the policy to use for the X-Frame-Options-Header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"DENY\"/>\n               <xs:enumeration value=\"SAMEORIGIN\"/>\n               <xs:enumeration value=\"ALLOW-FROM\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"strategy\">\n         <xs:annotation>\n            <xs:documentation>Specify the strategy to use when ALLOW-FROM is chosen.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"static\"/>\n               <xs:enumeration value=\"whitelist\"/>\n               <xs:enumeration value=\"regexp\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify a value to use for the chosen strategy.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"from-parameter\" type=\"xs:string\">\n         <xs:annotation>\n            <xs:documentation>Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp'\n                based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no\n                longer works in modern browsers. Instead use Content-Security-Policy with the &lt;a\n                href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\"&gt;frame-ancestors&lt;/a&gt;\n                directive.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"xss-protection\">\n      <xs:annotation>\n         <xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the\n                X-XSS-Protection header.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:xss-protection.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"xss-protection.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>disable the X-XSS-Protection header. Default is 'false' meaning it is enabled.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"header-value\">\n         <xs:annotation>\n            <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to \"0\".\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"0\"/>\n               <xs:enumeration value=\"1\"/>\n               <xs:enumeration value=\"1; mode=block\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"content-type-options\">\n      <xs:annotation>\n         <xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:content-type-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"content-type-options.attlist\">\n      <xs:attribute name=\"disabled\" type=\"xs:boolean\">\n         <xs:annotation>\n            <xs:documentation>If disabled, the X-Content-Type-Options header will not be included. Default false.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-opener-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Opener-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-opener-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-opener-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Opener-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-origin-allow-popups\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-embedder-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Embedder-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-embedder-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-embedder-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"unsafe-none\"/>\n               <xs:enumeration value=\"require-corp\"/>\n               <xs:enumeration value=\"credentialless\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"cross-origin-resource-policy\">\n      <xs:annotation>\n         <xs:documentation>Adds support for Cross-Origin-Resource-Policy header\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:cross-origin-resource-policy-options.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"cross-origin-resource-policy-options.attlist\">\n      <xs:attribute name=\"policy\">\n         <xs:annotation>\n            <xs:documentation>The policies for the Cross-Origin-Resource-Policy header.\n                </xs:documentation>\n         </xs:annotation>\n         <xs:simpleType>\n            <xs:restriction base=\"xs:token\">\n               <xs:enumeration value=\"cross-origin\"/>\n               <xs:enumeration value=\"same-origin\"/>\n               <xs:enumeration value=\"same-site\"/>\n            </xs:restriction>\n         </xs:simpleType>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"header\">\n      <xs:annotation>\n         <xs:documentation>Add additional headers to the response.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:header.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"header.attlist\">\n      <xs:attribute name=\"name\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The name of the header to add.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"value\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>The value for the header.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"ref\" type=\"xs:token\">\n         <xs:annotation>\n            <xs:documentation>Defines a reference to a Spring bean Id.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:element name=\"any-user-service\" abstract=\"true\"/>\n  <xs:element name=\"custom-filter\">\n      <xs:annotation>\n         <xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security\n                filter chain.\n                </xs:documentation>\n      </xs:annotation>\n      <xs:complexType>\n         <xs:attributeGroup ref=\"security:custom-filter.attlist\"/>\n      </xs:complexType>\n   </xs:element>\n  <xs:attributeGroup name=\"custom-filter.attlist\">\n      <xs:attributeGroup ref=\"security:ref\"/>\n      <xs:attribute name=\"after\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"before\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n      <xs:attribute name=\"position\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"after\">\n      <xs:attribute name=\"after\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately after which the custom-filter should be placed in the chain. This\n                feature will only be needed by advanced users who wish to mix their own filters into the\n                security filter chain and have some knowledge of the standard Spring Security filters. The\n                filter names map to specific Spring Security implementation filters.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"before\">\n      <xs:attribute name=\"before\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The filter immediately before which the custom-filter should be placed in the chain\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:attributeGroup name=\"position\">\n      <xs:attribute name=\"position\" use=\"required\" type=\"security:named-security-filter\">\n         <xs:annotation>\n            <xs:documentation>The explicit position at which the custom-filter should be placed in the chain. Use if you\n                are replacing a standard filter.\n                </xs:documentation>\n         </xs:annotation>\n      </xs:attribute>\n  </xs:attributeGroup>\n  <xs:simpleType name=\"named-security-filter\">\n      <xs:restriction base=\"xs:token\">\n         <xs:enumeration value=\"FIRST\"/>\n         <xs:enumeration value=\"DISABLE_ENCODE_URL_FILTER\"/>\n         <xs:enumeration value=\"FORCE_EAGER_SESSION_FILTER\"/>\n         <xs:enumeration value=\"CHANNEL_FILTER\"/>\n         <xs:enumeration value=\"HTTPS_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SECURITY_CONTEXT_FILTER\"/>\n         <xs:enumeration value=\"CONCURRENT_SESSION_FILTER\"/>\n         <xs:enumeration value=\"WEB_ASYNC_MANAGER_FILTER\"/>\n         <xs:enumeration value=\"HEADERS_FILTER\"/>\n         <xs:enumeration value=\"CORS_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_RESPONSE_FILTER\"/>\n         <xs:enumeration value=\"CSRF_FILTER\"/>\n         <xs:enumeration value=\"SAML2_LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_REQUEST_FILTER\"/>\n         <xs:enumeration value=\"X509_FILTER\"/>\n         <xs:enumeration value=\"PRE_AUTH_FILTER\"/>\n         <xs:enumeration value=\"CAS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"SAML2_AUTHENTICATION_FILTER\"/>\n         <xs:enumeration value=\"FORM_LOGIN_FILTER\"/>\n         <xs:enumeration value=\"DEFAULT_RESOURCES_FILTER\"/>\n         <xs:enumeration value=\"LOGIN_PAGE_FILTER\"/>\n         <xs:enumeration value=\"LOGOUT_PAGE_FILTER\"/>\n         <xs:enumeration value=\"DIGEST_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BEARER_TOKEN_AUTH_FILTER\"/>\n         <xs:enumeration value=\"BASIC_AUTH_FILTER\"/>\n         <xs:enumeration value=\"REQUEST_CACHE_FILTER\"/>\n         <xs:enumeration value=\"SERVLET_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"JAAS_API_SUPPORT_FILTER\"/>\n         <xs:enumeration value=\"REMEMBER_ME_FILTER\"/>\n         <xs:enumeration value=\"ANONYMOUS_FILTER\"/>\n         <xs:enumeration value=\"OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER\"/>\n         <xs:enumeration value=\"WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER\"/>\n         <xs:enumeration value=\"SESSION_MANAGEMENT_FILTER\"/>\n         <xs:enumeration value=\"EXCEPTION_TRANSLATION_FILTER\"/>\n         <xs:enumeration value=\"FILTER_SECURITY_INTERCEPTOR\"/>\n         <xs:enumeration value=\"SWITCH_USER_FILTER\"/>\n         <xs:enumeration value=\"LAST\"/>\n      </xs:restriction>\n  </xs:simpleType>\n</xs:schema>"
  },
  {
    "path": "config/src/main/resources/org/springframework/security/config/spring-security.xsl",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n    XSL to manipulate trang's output XSD file. Contributed by Brian Ewins.\n\n-->\n\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" version=\"2.0\">\n    <xsl:output method=\"xml\"  indent=\"yes\"/>\n\n    <xsl:variable name=\"elts-to-inline\">\n        <xsl:text>,access-denied-handler,anonymous,session-management,concurrency-control,after-invocation-provider,authentication-provider,ldap-authentication-provider,user,port-mapping,openid-login,saml2-login,saml2-logout,expression-handler,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,add-headers,</xsl:text>\n    </xsl:variable>\n\n    <xsl:template match=\"xs:element\">\n        <xsl:choose>\n            <xsl:when test=\"contains($elts-to-inline, concat(',',substring-after(current()/@ref, ':'),','))\">\n                <xsl:variable name=\"node\" select=\".\"/>\n                <xsl:for-each select=\"/xs:schema/xs:element[@name=substring-after(current()/@ref, ':')]\">\n                    <xsl:copy>\n                        <xsl:apply-templates select=\"$node/@*[local-name() != 'ref']\"/>\n                        <xsl:apply-templates select=\"@*|*\"/>\n                    </xsl:copy>\n                </xsl:for-each>\n            </xsl:when>\n            <!-- Ignore global elements which have been inlined -->\n            <xsl:when test=\"contains($elts-to-inline, concat(',',@name,','))\">\n            </xsl:when>\n\n            <xsl:otherwise>\n                <xsl:copy>\n                    <xsl:apply-templates select=\"@*|*\"/>\n                </xsl:copy>\n            </xsl:otherwise>\n        </xsl:choose>\n    </xsl:template>\n\n    <!-- Copy any non-element content -->\n    <xsl:template match=\"text()|@*|*\">\n        <xsl:copy>\n            <xsl:apply-templates select=\"text()|@*|*\"/>\n        </xsl:copy>\n    </xsl:template>\n\n    <xsl:template match=\"xs:documentation\">\n       <xsl:element name=\"xs:documentation\">\n         <xsl:copy-of select=\"@*\" />\n        <xsl:value-of select=\"replace(concat(normalize-space(text()),' '), '(.{0,90}) ','$1&#xA;                ')\"/>\n       </xsl:element>\n    </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/BeanNameCollectingPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\n\n/**\n * @author Luke Taylor\n */\npublic class BeanNameCollectingPostProcessor implements BeanPostProcessor {\n\n\tSet<String> beforeInitPostProcessedBeans = new HashSet<>();\n\n\tSet<String> afterInitPostProcessedBeans = new HashSet<>();\n\n\t@Override\n\tpublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n\t\tif (beanName != null) {\n\t\t\tthis.beforeInitPostProcessedBeans.add(beanName);\n\t\t}\n\t\treturn bean;\n\t}\n\n\t@Override\n\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\tif (beanName != null) {\n\t\t\tthis.afterInitPostProcessedBeans.add(beanName);\n\t\t}\n\t\treturn bean;\n\t}\n\n\tpublic Set<String> getBeforeInitPostProcessedBeans() {\n\t\treturn this.beforeInitPostProcessedBeans;\n\t}\n\n\tpublic Set<String> getAfterInitPostProcessedBeans() {\n\t\treturn this.afterInitPostProcessedBeans;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/CollectingAppListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.security.access.event.AbstractAuthorizationEvent;\nimport org.springframework.security.authentication.event.AbstractAuthenticationEvent;\nimport org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;\n\n/**\n * ApplicationListener which collects events for use in test assertions\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic class CollectingAppListener implements ApplicationListener {\n\n\tSet<ApplicationEvent> events = new HashSet<>();\n\n\tSet<AbstractAuthenticationEvent> authenticationEvents = new HashSet<>();\n\n\tSet<AbstractAuthenticationFailureEvent> authenticationFailureEvents = new HashSet<>();\n\n\tSet<AbstractAuthorizationEvent> authorizationEvents = new HashSet<>();\n\n\t@Override\n\tpublic void onApplicationEvent(ApplicationEvent event) {\n\t\tif (event instanceof AbstractAuthenticationEvent) {\n\t\t\tthis.events.add(event);\n\t\t\tthis.authenticationEvents.add((AbstractAuthenticationEvent) event);\n\t\t}\n\t\tif (event instanceof AbstractAuthenticationFailureEvent) {\n\t\t\tthis.events.add(event);\n\t\t\tthis.authenticationFailureEvents.add((AbstractAuthenticationFailureEvent) event);\n\t\t}\n\t\tif (event instanceof AbstractAuthorizationEvent) {\n\t\t\tthis.events.add(event);\n\t\t\tthis.authorizationEvents.add((AbstractAuthorizationEvent) event);\n\t\t}\n\t}\n\n\tpublic Set<ApplicationEvent> getEvents() {\n\t\treturn this.events;\n\t}\n\n\tpublic Set<AbstractAuthenticationEvent> getAuthenticationEvents() {\n\t\treturn this.authenticationEvents;\n\t}\n\n\tpublic Set<AbstractAuthenticationFailureEvent> getAuthenticationFailureEvents() {\n\t\treturn this.authenticationFailureEvents;\n\t}\n\n\tpublic Set<AbstractAuthorizationEvent> getAuthorizationEvents() {\n\t\treturn this.authorizationEvents;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/SerializationSamples.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.lang.reflect.Field;\nimport java.security.Principal;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.Cookie;\nimport org.apereo.cas.client.validation.AssertionImpl;\nimport org.instancio.Instancio;\nimport org.instancio.InstancioApi;\nimport org.instancio.InstancioOfClassApi;\nimport org.instancio.Select;\nimport org.instancio.generator.Generator;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.AuthorizationServiceException;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.hierarchicalroles.CycleInRoleHierarchyException;\nimport org.springframework.security.access.intercept.RunAsUserToken;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.InsufficientAuthenticationException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.ProviderNotFoundException;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureCredentialsExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureDisabledEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureLockedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProviderNotFoundEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProxyUntrustedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureServiceExceptionEvent;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;\nimport org.springframework.security.authentication.event.LogoutSuccessEvent;\nimport org.springframework.security.authentication.jaas.JaasAuthenticationToken;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationFailedEvent;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationSuccessEvent;\nimport org.springframework.security.authentication.ott.DefaultOneTimeToken;\nimport org.springframework.security.authentication.ott.InvalidOneTimeTokenException;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthentication;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.authentication.password.CompromisedPasswordException;\nimport org.springframework.security.authorization.AuthorityAuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.event.AuthorizationEvent;\nimport org.springframework.security.authorization.event.AuthorizationGrantedEvent;\nimport org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;\nimport org.springframework.security.cas.authentication.CasAuthenticationToken;\nimport org.springframework.security.cas.authentication.CasServiceTicketAuthenticationToken;\nimport org.springframework.security.config.annotation.AlreadyBuiltException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.context.TransientSecurityContext;\nimport org.springframework.security.core.session.AbstractSessionEvent;\nimport org.springframework.security.core.session.ReactiveSessionInformation;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyControl;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyErrorStatus;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyException;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyResponseControl;\nimport org.springframework.security.ldap.userdetails.LdapAuthority;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.TestOAuth2AuthenticationTokens;\nimport org.springframework.security.oauth2.client.authentication.TestOAuth2AuthorizationCodeAuthenticationTokens;\nimport org.springframework.security.oauth2.client.event.OAuth2AuthorizedClientRefreshedEvent;\nimport org.springframework.security.oauth2.client.oidc.authentication.event.OidcUserRefreshedEvent;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;\nimport org.springframework.security.oauth2.client.oidc.session.TestOidcSessionInformations;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoderInitializationException;\nimport org.springframework.security.oauth2.jwt.JwtEncodingException;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.JwtValidationException;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2PushedAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrors;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.security.oauth2.server.resource.OAuth2ProtectedResourceMetadata;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.introspection.BadOpaqueTokenException;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.credentials.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationTokens;\nimport org.springframework.security.saml2.provider.service.authentication.TestSaml2Authentications;\nimport org.springframework.security.saml2.provider.service.authentication.TestSaml2LogoutRequests;\nimport org.springframework.security.saml2.provider.service.authentication.TestSaml2PostAuthenticationRequests;\nimport org.springframework.security.saml2.provider.service.authentication.TestSaml2RedirectAuthenticationRequests;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.OpenSamlAssertingPartyDetails;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.web.authentication.AuthenticationFilter;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;\nimport org.springframework.security.web.authentication.rememberme.CookieTheftException;\nimport org.springframework.security.web.authentication.rememberme.InvalidCookieException;\nimport org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\nimport org.springframework.security.web.authentication.session.SessionFixationProtectionEvent;\nimport org.springframework.security.web.authentication.switchuser.AuthenticationSwitchUserEvent;\nimport org.springframework.security.web.authentication.www.NonceExpiredException;\nimport org.springframework.security.web.csrf.CsrfException;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.InvalidCsrfTokenException;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\nimport org.springframework.security.web.firewall.RequestRejectedException;\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\nimport org.springframework.security.web.savedrequest.SimpleSavedRequest;\nimport org.springframework.security.web.server.firewall.ServerExchangeRejectedException;\nimport org.springframework.security.web.session.HttpSessionCreatedEvent;\nimport org.springframework.security.web.session.HttpSessionIdChangedEvent;\nimport org.springframework.security.web.webauthn.api.AttestationConveyancePreference;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\nimport org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\nimport org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialParameters;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.ResidentKeyRequirement;\nimport org.springframework.security.web.webauthn.api.TestAuthenticationAssertionResponses;\nimport org.springframework.security.web.webauthn.api.TestBytes;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentials;\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\nimport org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication;\nimport org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationRequestToken;\nimport org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;\nimport org.springframework.security.web.webauthn.management.TestPublicKeyCredentialRpEntities;\nimport org.springframework.util.ReflectionUtils;\n\nfinal class SerializationSamples {\n\n\tstatic final Map<Class<?>, Generator<?>> generatorByClassName = new HashMap<>();\n\n\tstatic final Map<Class<?>, Supplier<InstancioApi<?>>> instancioByClassName = new HashMap<>();\n\n\tstatic {\n\t\tUserDetails user = TestAuthentication.user();\n\t\tAuthentication authentication = TestAuthentication.authenticated(user);\n\t\tSecurityContext securityContext = new SecurityContextImpl(authentication);\n\n\t\tinstancioByClassName.put(OneTimeTokenAuthenticationToken.class, () -> {\n\t\t\t@SuppressWarnings(\"removal\")\n\t\t\tInstancioOfClassApi<?> instancio = Instancio.of(OneTimeTokenAuthenticationToken.class);\n\t\t\tinstancio.supply(Select.all(OneTimeTokenAuthenticationToken.class),\n\t\t\t\t\t(r) -> applyDetails(new OneTimeTokenAuthenticationToken(\"token\")));\n\t\t\treturn instancio;\n\t\t});\n\n\t\t// oauth2-core\n\t\tgeneratorByClassName.put(DefaultOAuth2User.class, (r) -> TestOAuth2Users.create());\n\t\tgeneratorByClassName.put(OAuth2AuthorizationRequest.class,\n\t\t\t\t(r) -> TestOAuth2AuthorizationRequests.request().build());\n\t\tgeneratorByClassName.put(OAuth2AuthorizationResponse.class,\n\t\t\t\t(r) -> TestOAuth2AuthorizationResponses.success().build());\n\t\tgeneratorByClassName.put(OAuth2UserAuthority.class, (r) -> new OAuth2UserAuthority(Map.of(\"username\", \"user\")));\n\t\tgeneratorByClassName.put(OAuth2AuthorizationExchange.class, (r) -> TestOAuth2AuthorizationExchanges.success());\n\t\tgeneratorByClassName.put(OidcUserInfo.class, (r) -> OidcUserInfo.builder().email(\"email@example.com\").build());\n\t\tgeneratorByClassName.put(SessionInformation.class,\n\t\t\t\t(r) -> new SessionInformation(user, r.alphanumeric(4), new Date(1704378933936L)));\n\t\tgeneratorByClassName.put(ReactiveSessionInformation.class,\n\t\t\t\t(r) -> new ReactiveSessionInformation(user, r.alphanumeric(4), Instant.ofEpochMilli(1704378933936L)));\n\t\tgeneratorByClassName.put(OAuth2AccessToken.class, (r) -> TestOAuth2AccessTokens.scopes(\"scope\"));\n\t\tgeneratorByClassName.put(OAuth2DeviceCode.class,\n\t\t\t\t(r) -> new OAuth2DeviceCode(\"token\", Instant.now(), Instant.now().plusSeconds(1)));\n\t\tgeneratorByClassName.put(OAuth2RefreshToken.class,\n\t\t\t\t(r) -> new OAuth2RefreshToken(\"refreshToken\", Instant.now(), Instant.now().plusSeconds(1)));\n\t\tgeneratorByClassName.put(OAuth2UserCode.class,\n\t\t\t\t(r) -> new OAuth2UserCode(\"token\", Instant.now(), Instant.now().plusSeconds(1)));\n\t\tgeneratorByClassName.put(ClientRegistration.ClientSettings.class,\n\t\t\t\t(r) -> ClientRegistration.ClientSettings.builder().build());\n\t\tgeneratorByClassName.put(DefaultOidcUser.class, (r) -> TestOidcUsers.create());\n\t\tgeneratorByClassName.put(OidcUserAuthority.class,\n\t\t\t\t(r) -> new OidcUserAuthority(TestOidcIdTokens.idToken().build(),\n\t\t\t\t\t\tnew OidcUserInfo(Map.of(\"claim\", \"value\")), \"claim\"));\n\t\tgeneratorByClassName.put(OAuth2AuthenticationException.class,\n\t\t\t\t(r) -> new OAuth2AuthenticationException(new OAuth2Error(\"error\", \"description\", \"uri\"), \"message\",\n\t\t\t\t\t\tnew RuntimeException()));\n\t\tgeneratorByClassName.put(OAuth2AuthorizationException.class,\n\t\t\t\t(r) -> new OAuth2AuthorizationException(new OAuth2Error(\"error\", \"description\", \"uri\"), \"message\",\n\t\t\t\t\t\tnew RuntimeException()));\n\n\t\t// oauth2-client\n\t\tClientRegistration.Builder clientRegistrationBuilder = TestClientRegistrations.clientRegistration();\n\t\tClientRegistration clientRegistration = clientRegistrationBuilder.build();\n\t\tWebAuthenticationDetails details = new WebAuthenticationDetails(\"remote\", \"sessionId\");\n\t\tgeneratorByClassName.put(ClientRegistration.class, (r) -> clientRegistration);\n\t\tgeneratorByClassName.put(ClientRegistration.ProviderDetails.class,\n\t\t\t\t(r) -> clientRegistration.getProviderDetails());\n\t\tgeneratorByClassName.put(ClientRegistration.ProviderDetails.UserInfoEndpoint.class,\n\t\t\t\t(r) -> clientRegistration.getProviderDetails().getUserInfoEndpoint());\n\t\tgeneratorByClassName.put(ClientRegistration.Builder.class, (r) -> clientRegistrationBuilder);\n\t\tgeneratorByClassName.put(OAuth2AuthorizedClient.class,\n\t\t\t\t(r) -> new OAuth2AuthorizedClient(clientRegistration, \"principal\", TestOAuth2AccessTokens.noScopes()));\n\t\tgeneratorByClassName.put(OAuth2LoginAuthenticationToken.class, (r) -> {\n\t\t\tvar token = new OAuth2LoginAuthenticationToken(clientRegistration,\n\t\t\t\t\tTestOAuth2AuthorizationExchanges.success());\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2AuthorizationCodeAuthenticationToken.class, (r) -> {\n\t\t\tvar token = TestOAuth2AuthorizationCodeAuthenticationTokens.authenticated();\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2AuthenticationToken.class, (r) -> {\n\t\t\tvar token = TestOAuth2AuthenticationTokens.authenticated();\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(OidcIdToken.class, (r) -> TestOidcIdTokens.idToken().build());\n\t\tgeneratorByClassName.put(OidcLogoutToken.class,\n\t\t\t\t(r) -> TestOidcLogoutTokens.withSessionId(\"issuer\", \"sessionId\").issuedAt(Instant.now()).build());\n\t\tgeneratorByClassName.put(OidcSessionInformation.class, (r) -> TestOidcSessionInformations.create());\n\t\tgeneratorByClassName.put(DefaultOAuth2AuthenticatedPrincipal.class, (r) -> {\n\t\t\tOAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals.active();\n\t\t\treturn new DefaultOAuth2AuthenticatedPrincipal(principal.getName(), principal.getAttributes(),\n\t\t\t\t\t(Collection<GrantedAuthority>) principal.getAuthorities());\n\t\t});\n\t\tgeneratorByClassName.put(ClientAuthorizationException.class,\n\t\t\t\t(r) -> new ClientAuthorizationException(new OAuth2Error(\"error\", \"description\", \"uri\"), \"id\", \"message\",\n\t\t\t\t\t\tnew RuntimeException()));\n\t\tgeneratorByClassName.put(ClientAuthorizationRequiredException.class,\n\t\t\t\t(r) -> new ClientAuthorizationRequiredException(\"id\"));\n\t\tgeneratorByClassName\n\t\t\t.put(OAuth2AuthorizedClientRefreshedEvent.class, (r) -> new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\t\tTestOAuth2AccessTokenResponses.accessTokenResponse().build(),\n\t\t\t\t\tnew OAuth2AuthorizedClient(clientRegistration, \"principal\", TestOAuth2AccessTokens.noScopes())));\n\t\tgeneratorByClassName.put(OidcUserRefreshedEvent.class,\n\t\t\t\t(r) -> new OidcUserRefreshedEvent(TestOAuth2AccessTokenResponses.accessTokenResponse().build(),\n\t\t\t\t\t\tTestOidcUsers.create(), TestOidcUsers.create(), authentication));\n\n\t\t// oauth2-jose\n\t\tgeneratorByClassName.put(BadJwtException.class, (r) -> new BadJwtException(\"token\", new RuntimeException()));\n\t\tgeneratorByClassName.put(JwtDecoderInitializationException.class,\n\t\t\t\t(r) -> new JwtDecoderInitializationException(\"message\", new RuntimeException()));\n\t\tgeneratorByClassName.put(JwtEncodingException.class,\n\t\t\t\t(r) -> new JwtEncodingException(\"message\", new RuntimeException()));\n\t\tgeneratorByClassName.put(JwtException.class, (r) -> new JwtException(\"message\", new RuntimeException()));\n\t\tgeneratorByClassName.put(JwtValidationException.class,\n\t\t\t\t(r) -> new JwtValidationException(\"message\", List.of(new OAuth2Error(\"error\", \"description\", \"uri\"))));\n\n\t\t// oauth2-jwt\n\t\tgeneratorByClassName.put(Jwt.class, (r) -> TestJwts.user());\n\n\t\t// oauth2-resource-server\n\t\tgeneratorByClassName.put(BearerTokenAuthenticationToken.class, (r) -> {\n\t\t\tvar token = new BearerTokenAuthenticationToken(\"token\");\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(BearerTokenAuthentication.class, (r) -> {\n\t\t\tvar token = new BearerTokenAuthentication(TestOAuth2AuthenticatedPrincipals.active(),\n\t\t\t\t\tTestOAuth2AccessTokens.noScopes(), user.getAuthorities());\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(JwtAuthenticationToken.class, (r) -> {\n\t\t\tvar token = new JwtAuthenticationToken(TestJwts.user());\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(BearerTokenError.class, (r) -> BearerTokenErrors.invalidToken(\"invalid token\"));\n\t\tgeneratorByClassName.put(OAuth2IntrospectionAuthenticatedPrincipal.class,\n\t\t\t\t(r) -> TestOAuth2AuthenticatedPrincipals.active());\n\t\tgeneratorByClassName.put(InvalidBearerTokenException.class,\n\t\t\t\t(r) -> new InvalidBearerTokenException(\"description\", new RuntimeException()));\n\t\tgeneratorByClassName.put(BadOpaqueTokenException.class,\n\t\t\t\t(r) -> new BadOpaqueTokenException(\"message\", new RuntimeException()));\n\t\tgeneratorByClassName.put(OAuth2IntrospectionException.class,\n\t\t\t\t(r) -> new OAuth2IntrospectionException(\"message\", new RuntimeException()));\n\t\tgeneratorByClassName.put(DPoPAuthenticationToken.class,\n\t\t\t\t(r) -> applyDetails(new DPoPAuthenticationToken(\"token\", \"proof\", \"method\", \"uri\")));\n\t\tgeneratorByClassName.put(OAuth2ProtectedResourceMetadata.class,\n\t\t\t\t(r) -> OAuth2ProtectedResourceMetadata.builder()\n\t\t\t\t\t.resource(\"https://localhost/resource\")\n\t\t\t\t\t.authorizationServer(\"https://localhost/authorizationServer\")\n\t\t\t\t\t.scope(\"scope\")\n\t\t\t\t\t.bearerMethod(\"bearerMethod\")\n\t\t\t\t\t.resourceName(\"resourceName\")\n\t\t\t\t\t.tlsClientCertificateBoundAccessTokens(true)\n\t\t\t\t\t.build());\n\n\t\t// oauth2-authorization-server\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\t\tgeneratorByClassName.put(RegisteredClient.class, (r) -> registeredClient);\n\t\tgeneratorByClassName.put(OAuth2Authorization.class, (r) -> authorization);\n\t\tgeneratorByClassName.put(OAuth2Authorization.Token.class, (r) -> authorization.getAccessToken());\n\t\tgeneratorByClassName.put(OAuth2AuthorizationConsent.class,\n\t\t\t\t(r) -> OAuth2AuthorizationConsent.withId(\"registeredClientId\", \"principalName\")\n\t\t\t\t\t.scope(\"scope1\")\n\t\t\t\t\t.scope(\"scope2\")\n\t\t\t\t\t.build());\n\t\tgeneratorByClassName.put(OAuth2AuthorizationCodeRequestAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationToken = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\t\t\"authorizationUri\", \"clientId\", principal, \"redirectUri\", \"state\", authorizationRequest.getScopes(),\n\t\t\t\t\tauthorizationRequest.getAdditionalParameters());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2PushedAuthorizationRequestAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authenticationToken = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\t\t\"authorizationUri\", \"clientId\", principal, \"redirectUri\", \"state\", authorizationRequest.getScopes(),\n\t\t\t\t\tauthorizationRequest.getAdditionalParameters());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2AuthorizationGrantAuthenticationToken.class, (r) -> {\n\t\t\torg.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken authenticationToken = new org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\t\t\"code\", principal, \"redirectUri\", new HashMap<>());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2AuthorizationConsentAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2AuthorizationConsentAuthenticationToken authenticationToken = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\t\t\"authorizationUri\", \"clientId\", principal, \"state\", authorizationRequest.getScopes(),\n\t\t\t\t\tauthorizationRequest.getAdditionalParameters());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2DeviceAuthorizationRequestAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authenticationToken = new OAuth2DeviceAuthorizationRequestAuthenticationToken(\n\t\t\t\t\tprincipal, \"authorizationUri\", authorizationRequest.getScopes(),\n\t\t\t\t\tauthorizationRequest.getAdditionalParameters());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2DeviceAuthorizationConsentAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken authenticationToken = new OAuth2DeviceAuthorizationConsentAuthenticationToken(\n\t\t\t\t\t\"authorizationUri\", \"clientId\", principal, \"userCode\", \"state\", authorizationRequest.getScopes(),\n\t\t\t\t\tauthorizationRequest.getAdditionalParameters());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2DeviceVerificationAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2DeviceVerificationAuthenticationToken authenticationToken = new OAuth2DeviceVerificationAuthenticationToken(\n\t\t\t\t\tprincipal, \"userCode\", new HashMap<>());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2TokenIntrospectionAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2TokenIntrospectionAuthenticationToken authenticationToken = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\t\t\"token\", principal, \"tokenTypeHint\", new HashMap<>());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2TokenRevocationAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2TokenRevocationAuthenticationToken authenticationToken = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\t\t\"token\", principal, \"tokenTypeHint\");\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tOAuth2ClientRegistration oauth2ClientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t.scope(\"scope1\")\n\t\t\t.redirectUri(\"https://localhost/oauth2/callback\")\n\t\t\t.build();\n\t\tgeneratorByClassName.put(OAuth2ClientRegistration.class, (r) -> oauth2ClientRegistration);\n\t\tgeneratorByClassName.put(OAuth2ClientRegistrationAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2ClientRegistrationAuthenticationToken authenticationToken = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\t\tprincipal, oauth2ClientRegistration);\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tOidcClientRegistration oidcClientRegistration = OidcClientRegistration.builder()\n\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t.scope(\"scope1\")\n\t\t\t.redirectUri(\"https://localhost/oauth2/callback\")\n\t\t\t.build();\n\t\tgeneratorByClassName.put(OidcClientRegistration.class, (r) -> oidcClientRegistration);\n\t\tgeneratorByClassName.put(OidcClientRegistrationAuthenticationToken.class, (r) -> {\n\t\t\tOidcClientRegistrationAuthenticationToken authenticationToken = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\t\tprincipal, oidcClientRegistration);\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OidcUserInfoAuthenticationToken.class, (r) -> {\n\t\t\tOidcUserInfo userInfo = OidcUserInfo.builder().subject(\"subject\").name(\"name\").build();\n\t\t\tOidcUserInfoAuthenticationToken authenticationToken = new OidcUserInfoAuthenticationToken(principal,\n\t\t\t\t\tuserInfo);\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OidcLogoutAuthenticationToken.class, (r) -> {\n\t\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"tokenValue\")\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.expiresAt(Instant.now().plusSeconds(60))\n\t\t\t\t.build();\n\t\t\tOidcLogoutAuthenticationToken authenticationToken = new OidcLogoutAuthenticationToken(idToken, principal,\n\t\t\t\t\t\"sessionId\", \"clientId\", \"postLogoutRedirectUri\", \"state\");\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2ClientAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2ClientAuthenticationToken authenticationToken = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, \"credentials\");\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2TokenIntrospection.class,\n\t\t\t\t(r) -> OAuth2TokenIntrospection.builder().active(true).clientId(\"clientId\").build());\n\t\tgeneratorByClassName.put(OAuth2AccessTokenAuthenticationToken.class, (r) -> {\n\t\t\tOAuth2AccessTokenAuthenticationToken authenticationToken = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\t\tregisteredClient, principal, authorization.getAccessToken().getToken());\n\t\t\tauthenticationToken.setDetails(details);\n\t\t\treturn authenticationToken;\n\t\t});\n\t\tgeneratorByClassName.put(OAuth2AuthorizationServerMetadata.class,\n\t\t\t\t(r) -> OAuth2AuthorizationServerMetadata.builder()\n\t\t\t\t\t.issuer(\"https://localhost\")\n\t\t\t\t\t.authorizationEndpoint(\"https://localhost/oauth2/authorize\")\n\t\t\t\t\t.tokenEndpoint(\"https://localhost/oauth2/token\")\n\t\t\t\t\t.responseType(\"code\")\n\t\t\t\t\t.build());\n\t\tgeneratorByClassName.put(OidcProviderConfiguration.class,\n\t\t\t\t(r) -> OidcProviderConfiguration.builder()\n\t\t\t\t\t.issuer(\"https://localhost\")\n\t\t\t\t\t.authorizationEndpoint(\"https://localhost/oauth2/authorize\")\n\t\t\t\t\t.tokenEndpoint(\"https://localhost/oauth2/token\")\n\t\t\t\t\t.jwkSetUrl(\"https://localhost/oauth2/jwks\")\n\t\t\t\t\t.responseType(\"code\")\n\t\t\t\t\t.subjectType(\"subjectType\")\n\t\t\t\t\t.idTokenSigningAlgorithm(\"RS256\")\n\t\t\t\t\t.build());\n\t\tgeneratorByClassName.put(OAuth2TokenType.class, (r) -> OAuth2TokenType.ACCESS_TOKEN);\n\t\tgeneratorByClassName.put(OAuth2TokenFormat.class, (r) -> OAuth2TokenFormat.SELF_CONTAINED);\n\t\tgeneratorByClassName.put(AuthorizationServerSettings.class,\n\t\t\t\t(r) -> AuthorizationServerSettings.builder().build());\n\t\tgeneratorByClassName.put(ClientSettings.class, (r) -> ClientSettings.builder().build());\n\t\tgeneratorByClassName.put(TokenSettings.class, (r) -> TokenSettings.builder().build());\n\n\t\t// config\n\t\tgeneratorByClassName.put(AlreadyBuiltException.class, (r) -> new AlreadyBuiltException(\"message\"));\n\n\t\t// core\n\t\tgeneratorByClassName.put(RunAsUserToken.class, (r) -> {\n\t\t\tRunAsUserToken token = new RunAsUserToken(\"key\", user, \"creds\", user.getAuthorities(),\n\t\t\t\t\tAnonymousAuthenticationToken.class);\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(RememberMeAuthenticationToken.class, (r) -> {\n\t\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(\"key\", user, user.getAuthorities());\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(FactorGrantedAuthority.class,\n\t\t\t\t(r) -> FactorGrantedAuthority.withAuthority(\"profile:read\").issuedAt(Instant.now()).build());\n\t\tgeneratorByClassName.put(UsernamePasswordAuthenticationToken.class, (r) -> {\n\t\t\tvar token = UsernamePasswordAuthenticationToken.unauthenticated(user, \"creds\");\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(JaasAuthenticationToken.class, (r) -> {\n\t\t\tvar token = new JaasAuthenticationToken(user, \"creds\", null);\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\n\t\tgeneratorByClassName.put(OneTimeTokenAuthentication.class,\n\t\t\t\t(r) -> applyDetails(new OneTimeTokenAuthentication(\"username\", authentication.getAuthorities())));\n\t\tgeneratorByClassName.put(AccessDeniedException.class,\n\t\t\t\t(r) -> new AccessDeniedException(\"access denied\", new RuntimeException()));\n\t\tgeneratorByClassName.put(AuthorizationServiceException.class,\n\t\t\t\t(r) -> new AuthorizationServiceException(\"access denied\", new RuntimeException()));\n\t\tgeneratorByClassName.put(AccountExpiredException.class,\n\t\t\t\t(r) -> new AccountExpiredException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(AuthenticationCredentialsNotFoundException.class,\n\t\t\t\t(r) -> new AuthenticationCredentialsNotFoundException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(AuthenticationServiceException.class,\n\t\t\t\t(r) -> new AuthenticationServiceException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(BadCredentialsException.class,\n\t\t\t\t(r) -> new BadCredentialsException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(CredentialsExpiredException.class,\n\t\t\t\t(r) -> new CredentialsExpiredException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(DisabledException.class,\n\t\t\t\t(r) -> new DisabledException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(InsufficientAuthenticationException.class,\n\t\t\t\t(r) -> new InsufficientAuthenticationException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(InternalAuthenticationServiceException.class,\n\t\t\t\t(r) -> new InternalAuthenticationServiceException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(LockedException.class, (r) -> new LockedException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(ProviderNotFoundException.class, (r) -> new ProviderNotFoundException(\"error\"));\n\t\tgeneratorByClassName.put(InvalidOneTimeTokenException.class, (r) -> new InvalidOneTimeTokenException(\"error\"));\n\t\tgeneratorByClassName.put(CompromisedPasswordException.class,\n\t\t\t\t(r) -> new CompromisedPasswordException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(UsernameNotFoundException.class,\n\t\t\t\t(r) -> new UsernameNotFoundException(\"error\", new RuntimeException()));\n\t\tgeneratorByClassName.put(TestingAuthenticationToken.class,\n\t\t\t\t(r) -> applyDetails(new TestingAuthenticationToken(\"username\", \"password\")));\n\t\tgeneratorByClassName.put(AuthenticationFailureBadCredentialsEvent.class,\n\t\t\t\t(r) -> new AuthenticationFailureBadCredentialsEvent(authentication,\n\t\t\t\t\t\tnew BadCredentialsException(\"message\")));\n\t\tgeneratorByClassName.put(AuthenticationFailureCredentialsExpiredEvent.class,\n\t\t\t\t(r) -> new AuthenticationFailureCredentialsExpiredEvent(authentication,\n\t\t\t\t\t\tnew CredentialsExpiredException(\"message\")));\n\t\tgeneratorByClassName.put(AuthenticationFailureDisabledEvent.class,\n\t\t\t\t(r) -> new AuthenticationFailureDisabledEvent(authentication, new DisabledException(\"message\")));\n\t\tgeneratorByClassName.put(AuthenticationFailureExpiredEvent.class,\n\t\t\t\t(r) -> new AuthenticationFailureExpiredEvent(authentication, new AccountExpiredException(\"message\")));\n\t\tgeneratorByClassName.put(AuthenticationFailureLockedEvent.class,\n\t\t\t\t(r) -> new AuthenticationFailureLockedEvent(authentication, new LockedException(\"message\")));\n\t\tgeneratorByClassName.put(AuthenticationFailureProviderNotFoundEvent.class,\n\t\t\t\t(r) -> new AuthenticationFailureProviderNotFoundEvent(authentication,\n\t\t\t\t\t\tnew ProviderNotFoundException(\"message\")));\n\t\tgeneratorByClassName.put(AuthenticationFailureProxyUntrustedEvent.class,\n\t\t\t\t(r) -> new AuthenticationFailureProxyUntrustedEvent(authentication,\n\t\t\t\t\t\tnew AuthenticationServiceException(\"message\")));\n\t\tgeneratorByClassName.put(AuthenticationFailureServiceExceptionEvent.class,\n\t\t\t\t(r) -> new AuthenticationFailureServiceExceptionEvent(authentication,\n\t\t\t\t\t\tnew AuthenticationServiceException(\"message\")));\n\t\tgeneratorByClassName.put(AuthenticationSuccessEvent.class,\n\t\t\t\t(r) -> new AuthenticationSuccessEvent(authentication));\n\t\tgeneratorByClassName.put(InteractiveAuthenticationSuccessEvent.class,\n\t\t\t\t(r) -> new InteractiveAuthenticationSuccessEvent(authentication, AuthenticationFilter.class));\n\t\tgeneratorByClassName.put(LogoutSuccessEvent.class, (r) -> new LogoutSuccessEvent(authentication));\n\t\tgeneratorByClassName.put(JaasAuthenticationFailedEvent.class,\n\t\t\t\t(r) -> new JaasAuthenticationFailedEvent(authentication, new RuntimeException(\"message\")));\n\t\tgeneratorByClassName.put(JaasAuthenticationSuccessEvent.class,\n\t\t\t\t(r) -> new JaasAuthenticationSuccessEvent(authentication));\n\t\tgeneratorByClassName.put(AbstractSessionEvent.class, (r) -> new AbstractSessionEvent(securityContext));\n\t\tgeneratorByClassName.put(SecurityConfig.class, (r) -> new SecurityConfig(\"value\"));\n\t\tgeneratorByClassName.put(TransientSecurityContext.class, (r) -> new TransientSecurityContext(authentication));\n\t\tgeneratorByClassName.put(AuthorizationDeniedException.class,\n\t\t\t\t(r) -> new AuthorizationDeniedException(\"message\", new AuthorizationDecision(false)));\n\t\tgeneratorByClassName.put(AuthorizationDecision.class, (r) -> new AuthorizationDecision(true));\n\t\tgeneratorByClassName.put(AuthorityAuthorizationDecision.class,\n\t\t\t\t(r) -> new AuthorityAuthorizationDecision(true, AuthorityUtils.createAuthorityList(\"ROLE_USER\")));\n\t\tgeneratorByClassName.put(CycleInRoleHierarchyException.class, (r) -> new CycleInRoleHierarchyException());\n\t\tgeneratorByClassName.put(AuthorizationEvent.class,\n\t\t\t\t(r) -> new AuthorizationEvent(new SerializableSupplier<>(authentication), \"source\",\n\t\t\t\t\t\tnew AuthorizationDecision(true)));\n\t\tgeneratorByClassName.put(AuthorizationGrantedEvent.class,\n\t\t\t\t(r) -> new AuthorizationGrantedEvent<>(new SerializableSupplier<>(authentication), \"source\",\n\t\t\t\t\t\tnew AuthorizationDecision(true)));\n\t\tinstancioByClassName.put(AuthorizationGrantedEvent.class, () -> {\n\t\t\tInstancioOfClassApi<?> instancio = Instancio.of(AuthorizationGrantedEvent.class);\n\t\t\tinstancio.withTypeParameters(String.class);\n\t\t\tinstancio.supply(Select.all(AuthorizationGrantedEvent.class),\n\t\t\t\t\tgeneratorByClassName.get(AuthorizationGrantedEvent.class));\n\t\t\treturn instancio;\n\t\t});\n\n\t\t// cas\n\t\tgeneratorByClassName.put(CasServiceTicketAuthenticationToken.class, (r) -> {\n\t\t\tCasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateless(\"creds\");\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(CasAuthenticationToken.class, (r) -> {\n\t\t\tvar token = new CasAuthenticationToken(\"key\", user, \"Password\", user.getAuthorities(), user,\n\t\t\t\t\tnew AssertionImpl(\"test\"));\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(CasAssertionAuthenticationToken.class, (r) -> {\n\t\t\tvar token = new CasAssertionAuthenticationToken(new AssertionImpl(\"test\"), \"ticket\");\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\n\t\t// ldap\n\t\tgeneratorByClassName.put(LdapAuthority.class,\n\t\t\t\t(r) -> new LdapAuthority(\"USER\", \"username\", Map.of(\"attribute\", List.of(\"value1\", \"value2\"))));\n\t\tgeneratorByClassName.put(PasswordPolicyException.class,\n\t\t\t\t(r) -> new PasswordPolicyException(PasswordPolicyErrorStatus.INSUFFICIENT_PASSWORD_QUALITY));\n\t\tgeneratorByClassName.put(PasswordPolicyControl.class, (r) -> new PasswordPolicyControl(true));\n\t\tgeneratorByClassName.put(PasswordPolicyResponseControl.class, (r) -> {\n\t\t\tbyte[] encodedResponse = { 0x30, 0x05, (byte) 0xA0, 0x03, (byte) 0xA0, 0x1, 0x21 };\n\t\t\treturn new PasswordPolicyResponseControl(encodedResponse);\n\t\t});\n\n\t\t// saml2-service-provider\n\t\tgeneratorByClassName.put(Saml2AuthenticationException.class,\n\t\t\t\t(r) -> new Saml2AuthenticationException(new Saml2Error(\"code\", \"descirption\"), \"message\",\n\t\t\t\t\t\tnew IOException(\"fail\")));\n\t\tgeneratorByClassName.put(Saml2Exception.class, (r) -> new Saml2Exception(\"message\", new IOException(\"fail\")));\n\t\tgeneratorByClassName.put(DefaultSaml2AuthenticatedPrincipal.class,\n\t\t\t\t(r) -> TestSaml2Authentications.authentication().getPrincipal());\n\t\tSaml2Authentication saml2 = TestSaml2Authentications.authentication();\n\t\tgeneratorByClassName.put(Saml2Authentication.class, (r) -> applyDetails(saml2));\n\t\tSaml2ResponseAssertionAccessor assertion = Saml2ResponseAssertion.withResponseValue(\"response\")\n\t\t\t.nameId(\"name\")\n\t\t\t.sessionIndexes(List.of(\"id\"))\n\t\t\t.attributes(Map.of(\"key\", List.of(\"value\")))\n\t\t\t.build();\n\t\tgeneratorByClassName.put(Saml2ResponseAssertion.class, (r) -> assertion);\n\t\tgeneratorByClassName.put(Saml2AssertionAuthentication.class, (r) -> applyDetails(\n\t\t\t\tnew Saml2AssertionAuthentication(assertion, authentication.getAuthorities(), \"id\")));\n\t\tgeneratorByClassName.put(Saml2PostAuthenticationRequest.class,\n\t\t\t\t(r) -> TestSaml2PostAuthenticationRequests.create());\n\t\tgeneratorByClassName.put(Saml2RedirectAuthenticationRequest.class,\n\t\t\t\t(r) -> TestSaml2RedirectAuthenticationRequests.create());\n\t\tgeneratorByClassName.put(Saml2X509Credential.class,\n\t\t\t\t(r) -> TestSaml2X509Credentials.relyingPartyVerifyingCredential());\n\t\tgeneratorByClassName.put(RelyingPartyRegistration.AssertingPartyDetails.class,\n\t\t\t\t(r) -> TestRelyingPartyRegistrations.full().build().getAssertingPartyMetadata());\n\t\tgeneratorByClassName.put(RelyingPartyRegistration.class, (r) -> TestRelyingPartyRegistrations.full().build());\n\t\tgeneratorByClassName.put(Saml2AuthenticationToken.class, (r) -> {\n\t\t\tSaml2AuthenticationToken token = TestSaml2AuthenticationTokens.tokenRequested();\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(Saml2LogoutRequest.class, (r) -> TestSaml2LogoutRequests.create());\n\t\tgeneratorByClassName.put(OpenSamlAssertingPartyDetails.class,\n\t\t\t\t(r) -> OpenSamlAssertingPartyDetails\n\t\t\t\t\t.withEntityDescriptor(\n\t\t\t\t\t\t\tTestOpenSamlObjects.entityDescriptor(TestRelyingPartyRegistrations.full().build()))\n\t\t\t\t\t.build());\n\n\t\t// web\n\t\tgeneratorByClassName.put(AnonymousAuthenticationToken.class, (r) -> {\n\t\t\tCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\t\treturn applyDetails(new AnonymousAuthenticationToken(\"key\", \"username\", authorities));\n\t\t});\n\t\tgeneratorByClassName.put(PreAuthenticatedAuthenticationToken.class, (r) -> {\n\t\t\tPreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(user, \"creds\",\n\t\t\t\t\tuser.getAuthorities());\n\t\t\ttoken.setDetails(details);\n\t\t\treturn token;\n\t\t});\n\t\tgeneratorByClassName.put(PreAuthenticatedCredentialsNotFoundException.class,\n\t\t\t\t(r) -> new PreAuthenticatedCredentialsNotFoundException(\"message\", new IOException(\"fail\")));\n\t\tgeneratorByClassName.put(CookieTheftException.class, (r) -> new CookieTheftException(\"message\"));\n\t\tgeneratorByClassName.put(InvalidCookieException.class, (r) -> new InvalidCookieException(\"message\"));\n\t\tgeneratorByClassName.put(RememberMeAuthenticationException.class,\n\t\t\t\t(r) -> new RememberMeAuthenticationException(\"message\", new IOException(\"fail\")));\n\t\tgeneratorByClassName.put(SessionAuthenticationException.class,\n\t\t\t\t(r) -> new SessionAuthenticationException(\"message\"));\n\t\tgeneratorByClassName.put(NonceExpiredException.class,\n\t\t\t\t(r) -> new NonceExpiredException(\"message\", new IOException(\"fail\")));\n\t\tgeneratorByClassName.put(CsrfException.class, (r) -> new CsrfException(\"message\"));\n\t\tgeneratorByClassName.put(org.springframework.security.web.server.csrf.CsrfException.class,\n\t\t\t\t(r) -> new org.springframework.security.web.server.csrf.CsrfException(\"message\"));\n\t\tgeneratorByClassName.put(InvalidCsrfTokenException.class,\n\t\t\t\t(r) -> new InvalidCsrfTokenException(new DefaultCsrfToken(\"header\", \"parameter\", \"token\"), \"token\"));\n\t\tgeneratorByClassName.put(MissingCsrfTokenException.class, (r) -> new MissingCsrfTokenException(\"token\"));\n\t\tgeneratorByClassName.put(DefaultCsrfToken.class, (r) -> new DefaultCsrfToken(\"header\", \"parameter\", \"token\"));\n\t\tgeneratorByClassName.put(org.springframework.security.web.server.csrf.DefaultCsrfToken.class,\n\t\t\t\t(r) -> new org.springframework.security.web.server.csrf.DefaultCsrfToken(\"header\", \"parameter\",\n\t\t\t\t\t\t\"token\"));\n\t\tgeneratorByClassName.put(RequestRejectedException.class, (r) -> new RequestRejectedException(\"message\"));\n\t\tgeneratorByClassName.put(ServerExchangeRejectedException.class,\n\t\t\t\t(r) -> new ServerExchangeRejectedException(\"message\"));\n\t\tgeneratorByClassName.put(SessionFixationProtectionEvent.class,\n\t\t\t\t(r) -> new SessionFixationProtectionEvent(authentication, \"old\", \"new\"));\n\t\tgeneratorByClassName.put(AuthenticationSwitchUserEvent.class,\n\t\t\t\t(r) -> new AuthenticationSwitchUserEvent(authentication, user));\n\t\tgeneratorByClassName.put(HttpSessionCreatedEvent.class,\n\t\t\t\t(r) -> new HttpSessionCreatedEvent(new MockHttpSession()));\n\t\tgeneratorByClassName.put(SimpleSavedRequest.class, (r) -> {\n\t\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/uri\");\n\t\t\trequest.setQueryString(\"query=string\");\n\t\t\trequest.setScheme(\"https\");\n\t\t\trequest.setServerName(\"localhost\");\n\t\t\trequest.setServerPort(80);\n\t\t\trequest.setRequestURI(\"/uri\");\n\t\t\trequest.setCookies(new Cookie(\"name\", \"value\"));\n\t\t\trequest.addHeader(\"header\", \"value\");\n\t\t\trequest.addParameter(\"parameter\", \"value\");\n\t\t\trequest.setPathInfo(\"/path\");\n\t\t\trequest.addPreferredLocale(Locale.ENGLISH);\n\t\t\treturn new SimpleSavedRequest(new DefaultSavedRequest(request, \"continue\"));\n\t\t});\n\n\t\tgeneratorByClassName.put(HttpSessionIdChangedEvent.class,\n\t\t\t\t(r) -> new HttpSessionIdChangedEvent(new MockHttpSession(), \"1\"));\n\n\t\t// webauthn\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,\n\t\t\t\ttrue);\n\t\tBytes id = TestBytes.get();\n\t\tAuthenticationExtensionsClientInputs inputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tImmutableAuthenticationExtensionsClientInput.credProps);\n\t\t// @formatter:off\n\t\tPublicKeyCredentialDescriptor descriptor = PublicKeyCredentialDescriptor.builder()\n\t\t\t\t.id(id)\n\t\t\t\t.type(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t\t.transports(Set.of(AuthenticatorTransport.USB))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgeneratorByClassName.put(AuthenticatorTransport.class, (a) -> AuthenticatorTransport.USB);\n\t\tgeneratorByClassName.put(PublicKeyCredentialType.class, (k) -> PublicKeyCredentialType.PUBLIC_KEY);\n\t\tgeneratorByClassName.put(UserVerificationRequirement.class, (r) -> UserVerificationRequirement.REQUIRED);\n\t\tgeneratorByClassName.put(CredProtectAuthenticationExtensionsClientInput.CredProtect.class, (c) -> credProtect);\n\t\tgeneratorByClassName.put(CredProtectAuthenticationExtensionsClientInput.class,\n\t\t\t\t(c) -> new CredProtectAuthenticationExtensionsClientInput(credProtect));\n\t\tgeneratorByClassName.put(ImmutableAuthenticationExtensionsClientInputs.class, (i) -> inputs);\n\t\tField credPropsField = ReflectionUtils.findField(ImmutableAuthenticationExtensionsClientInput.class,\n\t\t\t\t\"credProps\");\n\t\tgeneratorByClassName.put(credPropsField.getType(),\n\t\t\t\t(i) -> ImmutableAuthenticationExtensionsClientInput.credProps);\n\t\tgeneratorByClassName.put(Bytes.class, (b) -> id);\n\t\tgeneratorByClassName.put(PublicKeyCredentialDescriptor.class, (d) -> descriptor);\n\t\t// @formatter:off\n\t\tgeneratorByClassName.put(PublicKeyCredentialRequestOptions.class, (o) -> TestPublicKeyCredentialRequestOptions.create()\n\t\t\t\t.extensions(inputs)\n\t\t\t\t.allowCredentials(List.of(descriptor))\n\t\t\t\t.build()\n\t\t);\n\n\t\tCredentialPropertiesOutput credentialOutput = new CredentialPropertiesOutput(false);\n\t\tAuthenticationExtensionsClientOutputs outputs = new ImmutableAuthenticationExtensionsClientOutputs(credentialOutput);\n\t\tAuthenticatorAssertionResponse response = TestAuthenticationAssertionResponses.createAuthenticatorAssertionResponse()\n\t\t\t\t.build();\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> credential = TestPublicKeyCredentials.createPublicKeyCredential(\n\t\t\t\t\t\tresponse, outputs)\n\t\t\t\t.build();\n\t\tRelyingPartyAuthenticationRequest authRequest = new RelyingPartyAuthenticationRequest(\n\t\t\t\tTestPublicKeyCredentialRequestOptions.create().build(),\n\t\t\t\tcredential\n\t\t);\n\t\tWebAuthnAuthenticationRequestToken requestToken = new WebAuthnAuthenticationRequestToken(authRequest);\n\t\trequestToken.setDetails(details);\n\t\tgeneratorByClassName.put(CredentialPropertiesOutput.class, (o) -> credentialOutput);\n\t\tgeneratorByClassName.put(ImmutableAuthenticationExtensionsClientOutputs.class, (o) -> outputs);\n\t\tgeneratorByClassName.put(AuthenticatorAssertionResponse.class, (r) -> response);\n\t\tgeneratorByClassName.put(RelyingPartyAuthenticationRequest.class, (r) -> authRequest);\n\t\tgeneratorByClassName.put(PublicKeyCredential.class, (r) -> credential);\n\t\tgeneratorByClassName.put(WebAuthnAuthenticationRequestToken.class, (r) -> requestToken);\n\t\tgeneratorByClassName.put(AuthenticatorAttachment.class, (r) -> AuthenticatorAttachment.PLATFORM);\n\t\t// @formatter:on\n\t\tgeneratorByClassName.put(ImmutablePublicKeyCredentialUserEntity.class,\n\t\t\t\t(r) -> TestPublicKeyCredentialUserEntities.userEntity().id(TestBytes.get()).build());\n\t\tgeneratorByClassName.put(WebAuthnAuthentication.class, (r) -> {\n\t\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity()\n\t\t\t\t.id(TestBytes.get())\n\t\t\t\t.build();\n\t\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\t\tWebAuthnAuthentication webAuthnAuthentication = new WebAuthnAuthentication(userEntity, authorities);\n\t\t\twebAuthnAuthentication.setDetails(details);\n\t\t\treturn webAuthnAuthentication;\n\t\t});\n\t\t// @formatter:on\n\n\t\tgeneratorByClassName.put(CredentialPropertiesOutput.ExtensionOutput.class,\n\t\t\t\t(r) -> new CredentialPropertiesOutput(true).getOutput());\n\n\t\tAttestationConveyancePreference attestationConveyancePreference = AttestationConveyancePreference.DIRECT;\n\t\tResidentKeyRequirement residentKeyRequirement = ResidentKeyRequirement.REQUIRED;\n\t\tAuthenticatorSelectionCriteria authenticatorSelectionCriteria = AuthenticatorSelectionCriteria.builder()\n\t\t\t.authenticatorAttachment(AuthenticatorAttachment.PLATFORM)\n\t\t\t.residentKey(residentKeyRequirement)\n\t\t\t.userVerification(UserVerificationRequirement.REQUIRED)\n\t\t\t.build();\n\t\tPublicKeyCredentialParameters publicKeyCredentialParameters = PublicKeyCredentialParameters.RS256;\n\t\tPublicKeyCredentialRpEntity publicKeyCredentialRpEntity = TestPublicKeyCredentialRpEntities.createRpEntity()\n\t\t\t.build();\n\n\t\tgeneratorByClassName.put(AttestationConveyancePreference.class, (r) -> attestationConveyancePreference);\n\t\tgeneratorByClassName.put(ResidentKeyRequirement.class, (r) -> residentKeyRequirement);\n\t\tgeneratorByClassName.put(AuthenticatorSelectionCriteria.class, (r) -> authenticatorSelectionCriteria);\n\t\tgeneratorByClassName.put(COSEAlgorithmIdentifier.class, ((r) -> COSEAlgorithmIdentifier.RS256));\n\t\tgeneratorByClassName.put(PublicKeyCredentialParameters.class, (r) -> publicKeyCredentialParameters);\n\t\tgeneratorByClassName.put(PublicKeyCredentialRpEntity.class, (r) -> publicKeyCredentialRpEntity);\n\t\tgeneratorByClassName.put(PublicKeyCredentialCreationOptions.class,\n\t\t\t\t(o) -> TestPublicKeyCredentialCreationOptions.createPublicKeyCredentialCreationOptions()\n\t\t\t\t\t.extensions(inputs)\n\t\t\t\t\t.attestation(attestationConveyancePreference)\n\t\t\t\t\t.authenticatorSelection(authenticatorSelectionCriteria)\n\t\t\t\t\t.challenge(TestBytes.get())\n\t\t\t\t\t.excludeCredentials(List.of(descriptor))\n\t\t\t\t\t.rp(publicKeyCredentialRpEntity)\n\t\t\t\t\t.pubKeyCredParams(publicKeyCredentialParameters)\n\t\t\t\t\t.timeout(Duration.ofMinutes(5))\n\t\t\t\t\t.user(TestPublicKeyCredentialUserEntities.userEntity().id(TestBytes.get()).build())\n\t\t\t\t\t.build());\n\n\t\t// One-Time Token\n\t\tDefaultOneTimeToken oneTimeToken = new DefaultOneTimeToken(UUID.randomUUID().toString(), \"user\",\n\t\t\t\tInstant.now().plusSeconds(300));\n\t\tgeneratorByClassName.put(DefaultOneTimeToken.class, (t) -> oneTimeToken);\n\t}\n\n\tprivate SerializationSamples() {\n\n\t}\n\n\tstatic InstancioApi<?> instancioWithDefaults(Class<?> clazz) {\n\t\tif (instancioByClassName.containsKey(clazz)) {\n\t\t\treturn instancioByClassName.get(clazz).get();\n\t\t}\n\t\tInstancioOfClassApi<?> instancio = Instancio.of(clazz);\n\t\tResolvableType[] generics = ResolvableType.forClass(clazz).getGenerics();\n\t\tfor (ResolvableType type : generics) {\n\t\t\tinstancio.withTypeParameters(type.resolve());\n\t\t}\n\t\tif (generatorByClassName.containsKey(clazz)) {\n\t\t\tinstancio.supply(Select.all(clazz), generatorByClassName.get(clazz));\n\t\t}\n\t\treturn instancio;\n\t}\n\n\tprivate static <T extends AbstractAuthenticationToken> T applyDetails(T authentication) {\n\t\tWebAuthenticationDetails details = new WebAuthenticationDetails(\"remote\", \"sessionId\");\n\t\tauthentication.setDetails(details);\n\t\treturn authentication;\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tprivate static final class SerializableSupplier<T> implements Supplier<T>, Serializable {\n\n\t\tprivate final T value;\n\n\t\tSerializableSupplier(T value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic T get() {\n\t\t\treturn this.value;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/SpringSecurityCoreVersionSerializableTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.NotSerializableException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.ObjectStreamClass;\nimport java.io.Serializable;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;\nimport org.springframework.core.type.filter.AssignableTypeFilter;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\n\n/**\n * Tests that Spring Security classes that implements {@link Serializable} and have the\n * same serial version as {@link SpringSecurityCoreVersion#SERIAL_VERSION_UID} can be\n * deserialized from a previous minor version.\n * <p>\n * For example, all classes from version 6.2.x that matches the previous requirement\n * should be serialized and saved to a folder, and then later on, in 6.3.x, it is verified\n * if they can be deserialized\n *\n * @author Marcus da Coregio\n * @since 6.2.2\n * @see <a href=\"https://github.com/spring-projects/spring-security/issues/3737\">GitHub\n * Issue #3737</a>\n */\nclass SpringSecurityCoreVersionSerializableTests {\n\n\tstatic final long securitySerialVersionUid = 620L;\n\n\tstatic Path currentVersionFolder = Paths.get(\"src/test/resources/serialized/\" + getCurrentVersion());\n\n\tstatic Path previousVersionFolder = Paths.get(\"src/test/resources/serialized/\" + getPreviousVersion());\n\n\t@ParameterizedTest\n\t@MethodSource(\"getClassesToSerialize\")\n\tvoid serializeAndDeserializeAreEqual(Class<?> clazz) throws Exception {\n\t\tObject expected = SerializationSamples.instancioWithDefaults(clazz).create();\n\t\tassertThat(expected).isInstanceOf(clazz);\n\t\ttry (ByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\t\tObjectOutputStream objectOutputStream = new ObjectOutputStream(out)) {\n\t\t\tobjectOutputStream.writeObject(expected);\n\t\t\tobjectOutputStream.flush();\n\n\t\t\ttry (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());\n\t\t\t\t\tObjectInputStream objectInputStream = new ObjectInputStream(in)) {\n\t\t\t\tObject deserialized = objectInputStream.readObject();\n\t\t\t\t// Ignore transient fields Event classes extend from EventObject which has\n\t\t\t\t// transient source property\n\t\t\t\tSet<String> transientFieldNames = new HashSet<>();\n\t\t\t\tSet<Class<?>> visitedClasses = new HashSet<>();\n\t\t\t\tcollectTransientFieldNames(transientFieldNames, visitedClasses, clazz);\n\t\t\t\tassertThat(deserialized).usingRecursiveComparison()\n\t\t\t\t\t.ignoringFields(transientFieldNames.toArray(new String[0]))\n\t\t\t\t\t// RuntimeExceptions do not fully work but ensure the message does\n\t\t\t\t\t.withComparatorForType((lhs, rhs) -> ObjectUtils.compare(lhs.getMessage(), rhs.getMessage()),\n\t\t\t\t\t\t\tRuntimeException.class)\n\t\t\t\t\t.isEqualTo(expected);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void collectTransientFieldNames(Set<String> transientFieldNames, Set<Class<?>> visitedClasses,\n\t\t\tClass<?> clazz) {\n\t\tif (!visitedClasses.add(clazz) || clazz.isPrimitive()) {\n\t\t\treturn;\n\t\t}\n\t\tReflectionUtils.doWithFields(clazz, (field) -> {\n\t\t\tif (Modifier.isTransient(field.getModifiers())) {\n\t\t\t\ttransientFieldNames.add(field.getName());\n\t\t\t}\n\t\t\tcollectTransientFieldNames(transientFieldNames, visitedClasses, field.getType());\n\t\t});\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getClassesToSerialize\")\n\t@Disabled(\"This method should only be used to serialize the classes once\")\n\tvoid serializeCurrentVersionClasses(Class<?> clazz) throws Exception {\n\t\tFiles.createDirectories(currentVersionFolder);\n\t\tPath filePath = Paths.get(currentVersionFolder.toAbsolutePath() + \"/\" + clazz.getName() + \".serialized\");\n\t\tFile file = filePath.toFile();\n\t\tif (file.exists()) {\n\t\t\treturn;\n\t\t}\n\t\tFiles.createFile(filePath);\n\t\ttry (FileOutputStream fileOutputStream = new FileOutputStream(file);\n\t\t\t\tObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {\n\t\t\tObject instance = SerializationSamples.instancioWithDefaults(clazz).create();\n\t\t\tassertThat(instance).isInstanceOf(clazz);\n\t\t\tobjectOutputStream.writeObject(instance);\n\t\t\tobjectOutputStream.flush();\n\t\t}\n\t\tcatch (NotSerializableException ex) {\n\t\t\tFiles.delete(filePath);\n\t\t\tfail(\"Could not serialize \" + clazz.getName(), ex);\n\t\t}\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getCurrentSerializedFiles\")\n\tvoid shouldBeAbleToDeserializeClassFromCurrentVersion(Path filePath) {\n\t\ttry (FileInputStream fileInputStream = new FileInputStream(filePath.toFile());\n\t\t\t\tObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {\n\t\t\tObject obj = objectInputStream.readObject();\n\t\t\tClass<?> clazz = Class.forName(filePath.getFileName().toString().replace(\".serialized\", \"\"));\n\t\t\tassertThat(obj).isInstanceOf(clazz);\n\t\t}\n\t\tcatch (IOException | ClassNotFoundException ex) {\n\t\t\tfail(\"Could not deserialize \" + filePath, ex);\n\t\t}\n\t}\n\n\tstatic Stream<Path> getCurrentSerializedFiles() throws Exception {\n\t\tassertThat(currentVersionFolder.toFile().exists())\n\t\t\t.as(\"Make sure that the \" + currentVersionFolder + \" exists and is not empty\")\n\t\t\t.isTrue();\n\t\treturn getClassesToSerialize().map((clazz) -> currentVersionFolder.resolve(clazz.getName() + \".serialized\"));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getPreviousSerializedFiles\")\n\tvoid shouldBeAbleToDeserializeClassFromPreviousVersion(Path filePath) {\n\t\ttry (FileInputStream fileInputStream = new FileInputStream(filePath.toFile());\n\t\t\t\tObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {\n\t\t\tObject obj = objectInputStream.readObject();\n\t\t\tClass<?> clazz = Class.forName(filePath.getFileName().toString().replace(\".serialized\", \"\"));\n\t\t\tassertThat(obj).isInstanceOf(clazz);\n\t\t}\n\t\tcatch (IOException | ClassNotFoundException ex) {\n\t\t\tfail(\"Could not deserialize \" + filePath, ex);\n\t\t}\n\t}\n\n\tstatic Stream<Path> getPreviousSerializedFiles() throws IOException {\n\t\tassertThat(previousVersionFolder.toFile().exists())\n\t\t\t.as(\"Make sure that the \" + previousVersionFolder + \" exists and is not empty\")\n\t\t\t.isTrue();\n\t\ttry (Stream<Path> files = Files.list(previousVersionFolder)) {\n\t\t\tif (files.findFirst().isEmpty()) {\n\t\t\t\tfail(\"Please make sure to run SpringSecurityCoreVersionSerializableTests#serializeCurrentVersionClasses for the \"\n\t\t\t\t\t\t+ getPreviousVersion() + \" version\");\n\t\t\t}\n\t\t}\n\t\treturn Files.list(previousVersionFolder);\n\t}\n\n\t@Test\n\tvoid allSerializableClassesShouldHaveSerialVersionOrSuppressWarnings() throws Exception {\n\t\tClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);\n\t\tprovider.addIncludeFilter(new AssignableTypeFilter(Serializable.class));\n\t\tList<Class<?>> classes = new ArrayList<>();\n\n\t\tSet<BeanDefinition> components = provider.findCandidateComponents(\"org/springframework/security\");\n\t\tfor (BeanDefinition component : components) {\n\t\t\tClass<?> clazz = Class.forName(component.getBeanClassName());\n\t\t\tif (clazz.isEnum()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (clazz.getName().contains(\"Tests\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tboolean hasSerialVersion = Stream.of(clazz.getDeclaredFields())\n\t\t\t\t.map(Field::getName)\n\t\t\t\t.anyMatch((n) -> n.equals(\"serialVersionUID\"));\n\t\t\tSuppressWarnings suppressWarnings = clazz.getAnnotation(SuppressWarnings.class);\n\t\t\tboolean hasSerialIgnore = suppressWarnings == null\n\t\t\t\t\t|| Arrays.asList(suppressWarnings.value()).contains(\"Serial\");\n\t\t\tif (!hasSerialVersion && !hasSerialIgnore) {\n\t\t\t\tclasses.add(clazz);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tboolean isReachable = Modifier.isPublic(clazz.getModifiers());\n\t\t\tboolean hasSampleSerialization = currentVersionFolder.resolve(clazz.getName() + \".serialized\")\n\t\t\t\t.toFile()\n\t\t\t\t.exists();\n\t\t\tif (hasSerialVersion && isReachable && !hasSampleSerialization) {\n\t\t\t\tclasses.add(clazz);\n\t\t\t}\n\t\t}\n\t\tassertThat(classes).describedAs(\n\t\t\t\t\"Found Serializable classes that are either missing a serialVersionUID or a @SuppressWarnings or a sample serialized file\")\n\t\t\t.isEmpty();\n\t}\n\n\tstatic Stream<Class<?>> getClassesToSerialize() throws Exception {\n\t\tClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);\n\t\tprovider.addIncludeFilter(new AssignableTypeFilter(Serializable.class));\n\t\tList<Class<?>> classes = new ArrayList<>();\n\n\t\tSet<BeanDefinition> components = provider.findCandidateComponents(\"org/springframework/security\");\n\t\tfor (BeanDefinition component : components) {\n\t\t\tClass<?> clazz = Class.forName(component.getBeanClassName());\n\t\t\tboolean isAbstract = Modifier.isAbstract(clazz.getModifiers());\n\t\t\tif (isAbstract) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tboolean matchesExpectedSerialVersion = ObjectStreamClass.lookup(clazz)\n\t\t\t\t.getSerialVersionUID() == securitySerialVersionUid;\n\t\t\tboolean isUnderTest = SerializationSamples.generatorByClassName.containsKey(clazz);\n\t\t\tif (matchesExpectedSerialVersion || isUnderTest) {\n\t\t\t\tclasses.add(clazz);\n\t\t\t}\n\t\t}\n\t\treturn classes.stream();\n\t}\n\n\tprivate static String getCurrentVersion() {\n\t\tString version = System.getProperty(\"springSecurityVersion\");\n\t\tString[] parts = version.split(\"\\\\.\");\n\t\tparts[2] = \"x\";\n\t\treturn String.join(\".\", parts);\n\t}\n\n\tprivate static String getPreviousVersion() {\n\t\tString version = System.getProperty(\"springSecurityVersion\");\n\t\tString[] parts = version.split(\"\\\\.\");\n\t\tparts[1] = String.valueOf(Integer.parseInt(parts[1]) - 1);\n\t\t// FIXME: the 7 should not be hardcoded\n\t\tif (\"7\".equals(parts[0]) && \"-1\".equals(parts[1])) {\n\t\t\t// if it is version 7.0.x, the previous version is 6.5.x\n\t\t\tparts[0] = String.valueOf(Integer.parseInt(parts[0]) - 1);\n\t\t\tparts[1] = \"5\"; // FIXME: this should not be hard coded\n\t\t}\n\t\tparts[2] = \"x\";\n\t\treturn String.join(\".\", parts);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/ConfigTestUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\npublic abstract class ConfigTestUtils {\n\n\t// @formatter:off\n\tpublic static final String AUTH_PROVIDER_XML = \"<authentication-manager alias='authManager'>\"\n\t\t\t+ \"    <authentication-provider>\"\n\t\t\t+ \"        <user-service id='us'>\"\n\t\t\t+ \"            <user name='bob' password='{noop}bobspassword' authorities='ROLE_A,ROLE_B' />\"\n\t\t\t+ \"            <user name='bill' password='{noop}billspassword' authorities='ROLE_A,ROLE_B,AUTH_OTHER' />\"\n\t\t\t+ \"            <user name='admin' password='{noop}password' authorities='ROLE_ADMIN,ROLE_USER' />\"\n\t\t\t+ \"            <user name='user' password='{noop}password' authorities='ROLE_USER' />\"\n\t\t\t+ \"        </user-service>\"\n\t\t\t+ \"    </authentication-provider>\"\n\t\t\t+ \"</authentication-manager>\";\n\t// @formatter:on\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/DataSourcePopulator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport javax.sql.DataSource;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.util.Assert;\n\n/**\n * Populates a database with test data for JDBC testing.\n *\n * @author Ben Alex\n */\npublic class DataSourcePopulator implements InitializingBean {\n\n\tJdbcTemplate template;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.template, \"dataSource required\");\n\t\tthis.template.execute(\n\t\t\t\t\"CREATE TABLE USERS(USERNAME VARCHAR_IGNORECASE(50) NOT NULL PRIMARY KEY,PASSWORD VARCHAR_IGNORECASE(500) NOT NULL,ENABLED BOOLEAN NOT NULL);\");\n\t\tthis.template.execute(\n\t\t\t\t\"CREATE TABLE AUTHORITIES(USERNAME VARCHAR_IGNORECASE(50) NOT NULL,AUTHORITY VARCHAR_IGNORECASE(50) NOT NULL,CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME));\");\n\t\tthis.template.execute(\"CREATE UNIQUE INDEX IX_AUTH_USERNAME ON AUTHORITIES(USERNAME,AUTHORITY);\");\n\t\t/*\n\t\t * Passwords encoded using MD5, NOT in Base64 format, with null as salt Encoded\n\t\t * password for rod is \"koala\" Encoded password for dianne is \"emu\" Encoded\n\t\t * password for scott is \"wombat\" Encoded password for peter is \"opal\" (but user\n\t\t * is disabled) Encoded password for bill is \"wombat\" Encoded password for bob is\n\t\t * \"wombat\" Encoded password for jane is \"wombat\"\n\t\t */\n\t\tthis.template.execute(\"INSERT INTO USERS VALUES('rod','{noop}koala',TRUE);\");\n\t\tthis.template.execute(\"INSERT INTO USERS VALUES('dianne','{MD5}65d15fe9156f9c4bbffd98085992a44e',TRUE);\");\n\t\tthis.template.execute(\"INSERT INTO USERS VALUES('scott','{MD5}2b58af6dddbd072ed27ffc86725d7d3a',TRUE);\");\n\t\tthis.template.execute(\"INSERT INTO USERS VALUES('peter','{MD5}22b5c9accc6e1ba628cedc63a72d57f8',FALSE);\");\n\t\tthis.template.execute(\"INSERT INTO USERS VALUES('bill','{MD5}2b58af6dddbd072ed27ffc86725d7d3a',TRUE);\");\n\t\tthis.template.execute(\"INSERT INTO USERS VALUES('bob','{MD5}2b58af6dddbd072ed27ffc86725d7d3a',TRUE);\");\n\t\tthis.template.execute(\"INSERT INTO USERS VALUES('jane','{MD5}2b58af6dddbd072ed27ffc86725d7d3a',TRUE);\");\n\t\tthis.template.execute(\"INSERT INTO AUTHORITIES VALUES('rod','ROLE_USER');\");\n\t\tthis.template.execute(\"INSERT INTO AUTHORITIES VALUES('rod','ROLE_SUPERVISOR');\");\n\t\tthis.template.execute(\"INSERT INTO AUTHORITIES VALUES('dianne','ROLE_USER');\");\n\t\tthis.template.execute(\"INSERT INTO AUTHORITIES VALUES('scott','ROLE_USER');\");\n\t\tthis.template.execute(\"INSERT INTO AUTHORITIES VALUES('peter','ROLE_USER');\");\n\t\tthis.template.execute(\"INSERT INTO AUTHORITIES VALUES('bill','ROLE_USER');\");\n\t\tthis.template.execute(\"INSERT INTO AUTHORITIES VALUES('bob','ROLE_USER');\");\n\t\tthis.template.execute(\"INSERT INTO AUTHORITIES VALUES('jane','ROLE_USER');\");\n\t}\n\n\tpublic void setDataSource(DataSource dataSource) {\n\t\tthis.template = new JdbcTemplate(dataSource);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/FilterChainProxyConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.firewall.DefaultHttpFirewall;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.web.util.pattern.PathPattern;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests {@link FilterChainProxy}.\n *\n * @author Carlos Sanchez\n * @author Ben Alex\n */\npublic class FilterChainProxyConfigTests {\n\n\tprivate ClassPathXmlApplicationContext appCtx;\n\n\t@BeforeEach\n\tpublic void loadContext() {\n\t\tSystem.setProperty(\"sec1235.pattern1\", \"/login\");\n\t\tSystem.setProperty(\"sec1235.pattern2\", \"/logout\");\n\t\tthis.appCtx = new ClassPathXmlApplicationContext(\"org/springframework/security/util/filtertest-valid.xml\");\n\t}\n\n\t@AfterEach\n\tpublic void closeContext() {\n\t\tif (this.appCtx != null) {\n\t\t\tthis.appCtx.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void normalOperation() throws Exception {\n\t\tFilterChainProxy filterChainProxy = this.appCtx.getBean(\"filterChain\", FilterChainProxy.class);\n\t\tdoNormalOperation(filterChainProxy);\n\t}\n\n\t@Test\n\tpublic void normalOperationWithNewConfig() throws Exception {\n\t\tFilterChainProxy filterChainProxy = this.appCtx.getBean(\"newFilterChainProxy\", FilterChainProxy.class);\n\t\tfilterChainProxy.setFirewall(new DefaultHttpFirewall());\n\t\tcheckPathAndFilterOrder(filterChainProxy);\n\t\tdoNormalOperation(filterChainProxy);\n\t}\n\n\t@Test\n\tpublic void normalOperationWithNewConfigRegex() throws Exception {\n\t\tFilterChainProxy filterChainProxy = this.appCtx.getBean(\"newFilterChainProxyRegex\", FilterChainProxy.class);\n\t\tfilterChainProxy.setFirewall(new DefaultHttpFirewall());\n\t\tcheckPathAndFilterOrder(filterChainProxy);\n\t\tdoNormalOperation(filterChainProxy);\n\t}\n\n\t@Test\n\tpublic void normalOperationWithNewConfigNonNamespace() throws Exception {\n\t\tFilterChainProxy filterChainProxy = this.appCtx.getBean(\"newFilterChainProxyNonNamespace\",\n\t\t\t\tFilterChainProxy.class);\n\t\tfilterChainProxy.setFirewall(new DefaultHttpFirewall());\n\t\tcheckPathAndFilterOrder(filterChainProxy);\n\t\tdoNormalOperation(filterChainProxy);\n\t}\n\n\t@Test\n\tpublic void pathWithNoMatchHasNoFilters() {\n\t\tFilterChainProxy filterChainProxy = this.appCtx.getBean(\"newFilterChainProxyNoDefaultPath\",\n\t\t\t\tFilterChainProxy.class);\n\t\tassertThat(filterChainProxy.getFilters(\"/nomatch\")).isNull();\n\t}\n\n\t// SEC-1235\n\t@Test\n\tpublic void mixingPatternsAndPlaceholdersDoesntCauseOrderingIssues() {\n\t\tFilterChainProxy fcp = this.appCtx.getBean(\"sec1235FilterChainProxy\", FilterChainProxy.class);\n\t\tList<SecurityFilterChain> chains = fcp.getFilterChains();\n\t\tassertThat(getPattern(chains.get(0))).isEqualTo(\"/login*\");\n\t\tassertThat(getPattern(chains.get(1))).isEqualTo(\"/logout\");\n\t\tassertThat(((DefaultSecurityFilterChain) chains.get(2)).getRequestMatcher())\n\t\t\t.isInstanceOf(AnyRequestMatcher.class);\n\t}\n\n\tprivate String getPattern(SecurityFilterChain chain) {\n\t\tRequestMatcher requestMatcher = ((DefaultSecurityFilterChain) chain).getRequestMatcher();\n\t\treturn ((PathPattern) ReflectionTestUtils.getField(requestMatcher, \"pattern\")).getPatternString();\n\t}\n\n\tprivate void checkPathAndFilterOrder(FilterChainProxy filterChainProxy) {\n\t\tList<Filter> filters = filterChainProxy.getFilters(\"/foo/blah;x=1\");\n\t\tassertThat(filters).hasSize(1);\n\t\tassertThat(filters.get(0) instanceof SecurityContextHolderAwareRequestFilter).isTrue();\n\t\tfilters = filterChainProxy.getFilters(\"/some;x=2,y=3/other/path;z=4/blah\");\n\t\tassertThat(filters).isNotNull();\n\t\tassertThat(filters).hasSize(3);\n\t\tassertThat(filters.get(0) instanceof SecurityContextPersistenceFilter).isTrue();\n\t\tassertThat(filters.get(1) instanceof SecurityContextHolderAwareRequestFilter).isTrue();\n\t\tassertThat(filters.get(2) instanceof SecurityContextHolderAwareRequestFilter).isTrue();\n\t\tfilters = filterChainProxy.getFilters(\"/do/not/filter;x=7\");\n\t\tassertThat(filters).isEmpty();\n\t\tfilters = filterChainProxy.getFilters(\"/another/nonspecificmatch\");\n\t\tassertThat(filters).hasSize(3);\n\t\tassertThat(filters.get(0) instanceof SecurityContextPersistenceFilter).isTrue();\n\t\tassertThat(filters.get(1) instanceof UsernamePasswordAuthenticationFilter).isTrue();\n\t\tassertThat(filters.get(2) instanceof SecurityContextHolderAwareRequestFilter).isTrue();\n\t}\n\n\tprivate void doNormalOperation(FilterChainProxy filterChainProxy) throws Exception {\n\t\tMockHttpServletRequest request = get(\"/foo/secure/super/somefile.html\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilterChainProxy.doFilter(request, response, chain);\n\t\tverify(chain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\trequest = get(\"/a/path/which/doesnt/match/any/filter.html\").build();\n\t\tchain = mock(FilterChain.class);\n\t\tfilterChainProxy.doFilter(request, response, chain);\n\t\tverify(chain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/InvalidConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;\nimport org.springframework.security.config.authentication.AuthenticationManagerFactoryBean;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests which make sure invalid configurations are rejected by the namespace. In\n * particular invalid top-level elements. These are likely to fail after the namespace has\n * been updated using trang, but the spring-security.xsl transform has not been applied.\n *\n * @author Luke Taylor\n */\npublic class InvalidConfigurationTests {\n\n\tprivate InMemoryXmlApplicationContext appContext;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t}\n\t}\n\n\t// Parser should throw a SAXParseException\n\t@Test\n\tpublic void passwordEncoderCannotAppearAtTopLevel() {\n\t\tassertThatExceptionOfType(XmlBeanDefinitionStoreException.class)\n\t\t\t.isThrownBy(() -> setContext(\"<password-encoder hash='md5'/>\"));\n\t}\n\n\t@Test\n\tpublic void authenticationProviderCannotAppearAtTopLevel() {\n\t\tassertThatExceptionOfType(XmlBeanDefinitionStoreException.class)\n\t\t\t.isThrownBy(() -> setContext(\"<authentication-provider ref='blah'/>\"));\n\t}\n\n\t@Test\n\tpublic void missingAuthenticationManagerGivesSensibleErrorMessage() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> setContext(\"<http auto-config='true' />\"))\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tThrowable cause = ultimateCause(ex);\n\t\t\t\tassertThat(cause).isInstanceOf(NoSuchBeanDefinitionException.class);\n\t\t\t\tNoSuchBeanDefinitionException nsbe = (NoSuchBeanDefinitionException) cause;\n\t\t\t\tassertThat(nsbe.getBeanName()).isEqualTo(BeanIds.AUTHENTICATION_MANAGER);\n\t\t\t\tassertThat(nsbe.getMessage()).endsWith(AuthenticationManagerFactoryBean.MISSING_BEAN_ERROR_MESSAGE);\n\t\t\t});\n\t}\n\n\tprivate Throwable ultimateCause(Throwable ex) {\n\t\tif (ex.getCause() == null) {\n\t\t\treturn ex;\n\t\t}\n\t\treturn ultimateCause(ex.getCause());\n\t}\n\n\tprivate void setContext(String context) {\n\t\tthis.appContext = new InMemoryXmlApplicationContext(context);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/MockAfterInvocationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport java.util.Collection;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.AfterInvocationProvider;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.core.Authentication;\n\npublic class MockAfterInvocationProvider implements AfterInvocationProvider {\n\n\t@Override\n\tpublic Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,\n\t\t\tObject returnedObject) throws AccessDeniedException {\n\t\treturn returnedObject;\n\t}\n\n\t@Override\n\tpublic boolean supports(ConfigAttribute attribute) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> clazz) {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/MockEventListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationListener;\n\n/**\n * @author Rob Winch\n * @since 5.0.2\n */\npublic class MockEventListener<T extends ApplicationEvent> implements ApplicationListener<T> {\n\n\tprivate List<T> events = new ArrayList<>();\n\n\t@Override\n\tpublic void onApplicationEvent(T event) {\n\t\tthis.events.add(event);\n\t}\n\n\tpublic List<T> getEvents() {\n\t\treturn this.events;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/MockSecurityContextHolderStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\npublic class MockSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n\tprivate SecurityContext context;\n\n\t@Override\n\tpublic void clearContext() {\n\t\tthis.context = null;\n\t}\n\n\t@Override\n\tpublic SecurityContext getContext() {\n\t\tif (this.context == null) {\n\t\t\tthis.context = createEmptyContext();\n\t\t}\n\t\treturn this.context;\n\t}\n\n\t@Override\n\tpublic void setContext(SecurityContext context) {\n\t\tthis.context = context;\n\t}\n\n\t@Override\n\tpublic SecurityContext createEmptyContext() {\n\t\treturn new SecurityContextImpl();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/MockTransactionManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.springframework.transaction.PlatformTransactionManager;\nimport org.springframework.transaction.TransactionDefinition;\nimport org.springframework.transaction.TransactionException;\nimport org.springframework.transaction.TransactionStatus;\n\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class MockTransactionManager implements PlatformTransactionManager {\n\n\t@Override\n\tpublic TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {\n\t\treturn mock(TransactionStatus.class);\n\t}\n\n\t@Override\n\tpublic void commit(TransactionStatus status) throws TransactionException {\n\t}\n\n\t@Override\n\tpublic void rollback(TransactionStatus status) throws TransactionException {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/MockUserServiceBeanPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\n\n/**\n * Test bean post processor which injects a message into a\n * PostProcessedMockUserDetailsService.\n *\n * @author Luke Taylor\n */\npublic class MockUserServiceBeanPostProcessor implements BeanPostProcessor {\n\n\t@Override\n\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\treturn bean;\n\t}\n\n\t@Override\n\tpublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n\t\tif (bean instanceof PostProcessedMockUserDetailsService) {\n\t\t\t((PostProcessedMockUserDetailsService) bean).setPostProcessorWasHere(\"Hello from the post processor!\");\n\t\t}\n\t\treturn bean;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/PostProcessedMockUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\npublic class PostProcessedMockUserDetailsService implements UserDetailsService {\n\n\tprivate String postProcessorWasHere;\n\n\tpublic PostProcessedMockUserDetailsService() {\n\t\tthis.postProcessorWasHere = \"Post processor hasn't been yet\";\n\t}\n\n\tpublic String getPostProcessorWasHere() {\n\t\treturn this.postProcessorWasHere;\n\t}\n\n\tpublic void setPostProcessorWasHere(String postProcessorWasHere) {\n\t\tthis.postProcessorWasHere = postProcessorWasHere;\n\t}\n\n\t@Override\n\tpublic UserDetails loadUserByUsername(String username) {\n\t\tthrow new UnsupportedOperationException(\"Not for actual use\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/SecurityNamespaceHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport ch.qos.logback.classic.Level;\nimport ch.qos.logback.classic.Logger;\nimport ch.qos.logback.classic.spi.ILoggingEvent;\nimport ch.qos.logback.core.Appender;\nimport org.apache.commons.logging.Log;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Answers;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.slf4j.LoggerFactory;\n\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.config.util.SpringSecurityVersions;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class SecurityNamespaceHandlerTests {\n\n\t// @formatter:off\n\tprivate static final String XML_AUTHENTICATION_MANAGER = \"<authentication-manager>\"\n\t\t\t+ \"  <authentication-provider>\"\n\t\t\t+ \"    <user-service id='us'>\"\n\t\t\t+ \"      <user name='bob' password='bobspassword' authorities='ROLE_A' />\"\n\t\t\t+ \"    </user-service>\"\n\t\t\t+ \"  </authentication-provider>\"\n\t\t\t+ \"</authentication-manager>\";\n\t// @formatter:on\n\n\tprivate static final String XML_HTTP_BLOCK = \"<http auto-config='true'/>\";\n\n\tprivate static final String FILTER_CHAIN_PROXY_CLASSNAME = \"org.springframework.security.web.FilterChainProxy\";\n\n\t@Mock(answer = Answers.CALLS_REAL_METHODS)\n\tprivate MockedStatic<ClassUtils> classUtils;\n\n\t@Test\n\tpublic void constructionWhenVersionsMatchThenLogsNothing() {\n\t\tAppender<ILoggingEvent> appender = mock(Appender.class);\n\t\tLogger logger = (Logger) LoggerFactory.getLogger(SecurityNamespaceHandler.class);\n\t\tlogger.addAppender(appender);\n\t\tassertThat(new SecurityNamespaceHandler()).isNotNull();\n\t\tverify(appender, never()).doAppend(any(ILoggingEvent.class));\n\t}\n\n\t@Test\n\tpublic void constructorWhenDetectsMismatchingVersionsThenLogsError() {\n\t\tAppender<ILoggingEvent> appender = mock(Appender.class);\n\t\tLogger logger = (Logger) LoggerFactory.getLogger(SecurityNamespaceHandler.class);\n\t\tlogger.addAppender(appender);\n\t\ttry (MockedStatic<SpringSecurityCoreVersion> core = Mockito.mockStatic(SpringSecurityCoreVersion.class)) {\n\t\t\tcore.when(SpringSecurityCoreVersion::getVersion).thenReturn(\"mismatching\");\n\t\t\tassertThat(new SecurityNamespaceHandler()).isNotNull();\n\t\t\tArgumentCaptor<ILoggingEvent> captor = ArgumentCaptor.forClass(ILoggingEvent.class);\n\t\t\tverify(appender).doAppend(captor.capture());\n\t\t\tassertThat(captor.getValue().getLevel()).isEqualTo(Level.ERROR);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void beanIdsConstantsAreNotEmpty() {\n\t\tassertThat(BeanIds.AUTHENTICATION_MANAGER).isNotEmpty();\n\t\tassertThat(BeanIds.SPRING_SECURITY_FILTER_CHAIN).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void elementsConstantsAreNotEmpty() {\n\t\tassertThat(Elements.HTTP).isNotEmpty();\n\t\tassertThat(Elements.AUTHENTICATION_MANAGER).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void pre32SchemaAreNotSupported() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> new InMemoryXmlApplicationContext(\n\t\t\t\t\t\"<user-service id='us'><user name='bob' password='bobspassword' authorities='ROLE_A' /></user-service>\",\n\t\t\t\t\t\"3.0.3\", null))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"You cannot use any XSD older than spring-security-7.1.xsd. Either change to spring-security.xsd or spring-security-7.1.xsd\");\n\t}\n\n\t// SEC-1868\n\t@Test\n\tpublic void initDoesNotLogErrorWhenFilterChainProxyFailsToLoad() throws Exception {\n\t\tString className = \"jakarta.servlet.Filter\";\n\t\tLog logger = mock(Log.class);\n\t\tSecurityNamespaceHandler handler = new SecurityNamespaceHandler();\n\t\tReflectionTestUtils.setField(handler, \"logger\", logger);\n\t\texpectClassUtilsForNameThrowsNoClassDefFoundError(className);\n\t\thandler.init();\n\t\tverifyNoMoreInteractions(logger);\n\t}\n\n\t@Test\n\tpublic void filterNoClassDefFoundError() throws Exception {\n\t\tString className = \"jakarta.servlet.Filter\";\n\t\texpectClassUtilsForNameThrowsNoClassDefFoundError(className);\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> new InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER + XML_HTTP_BLOCK))\n\t\t\t.havingRootCause()\n\t\t\t.isInstanceOf(NoClassDefFoundError.class)\n\t\t\t.withMessage(className);\n\t}\n\n\t@Test\n\tpublic void filterNoClassDefFoundErrorNoHttpBlock() throws Exception {\n\t\tString className = \"jakarta.servlet.Filter\";\n\t\texpectClassUtilsForNameThrowsNoClassDefFoundError(className);\n\t\tnew InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER);\n\t\t// should load just fine since no http block\n\t}\n\n\t@Test\n\tpublic void filterChainProxyClassNotFoundException() throws Exception {\n\t\tString className = FILTER_CHAIN_PROXY_CLASSNAME;\n\t\texpectClassUtilsForNameThrowsClassNotFoundException(className);\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> new InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER + XML_HTTP_BLOCK))\n\t\t\t.havingRootCause()\n\t\t\t.isInstanceOf(ClassNotFoundException.class)\n\t\t\t.withMessage(className);\n\t}\n\n\t@Test\n\tpublic void filterChainProxyClassNotFoundExceptionNoHttpBlock() throws Exception {\n\t\tString className = FILTER_CHAIN_PROXY_CLASSNAME;\n\t\texpectClassUtilsForNameThrowsClassNotFoundException(className);\n\t\tnew InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER);\n\t\t// should load just fine since no http block\n\t}\n\n\t@Test\n\tpublic void websocketNotFoundExceptionNoMessageBlock() throws Exception {\n\t\tString className = FILTER_CHAIN_PROXY_CLASSNAME;\n\t\texpectClassUtilsForNameThrowsClassNotFoundException(className);\n\t\tnew InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER);\n\t\t// should load just fine since no websocket block\n\t}\n\n\t@Test\n\tpublic void configureWhenOldVersionThenErrorMessageContainsCorrectVersion() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> new InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER, \"3.0\", null))\n\t\t\t.withMessageContaining(SpringSecurityVersions.getCurrentXsdVersionFromSpringSchemas());\n\t}\n\n\tprivate void expectClassUtilsForNameThrowsNoClassDefFoundError(String className) {\n\t\tthis.classUtils.when(() -> ClassUtils.forName(eq(FILTER_CHAIN_PROXY_CLASSNAME), any()))\n\t\t\t.thenThrow(new NoClassDefFoundError(className));\n\t}\n\n\tprivate void expectClassUtilsForNameThrowsClassNotFoundException(String className) {\n\t\tthis.classUtils.when(() -> ClassUtils.forName(eq(FILTER_CHAIN_PROXY_CLASSNAME), any()))\n\t\t\t.thenThrow(new ClassNotFoundException(className));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/TestBusinessBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\n/**\n * @author luke\n */\npublic interface TestBusinessBean {\n\n\tvoid setInteger(int i);\n\n\tint getInteger();\n\n\tvoid setString(String s);\n\n\tvoid doSomething();\n\n\tvoid unprotected();\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/TestBusinessBeanImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.security.core.session.SessionCreationEvent;\n\n/**\n * @author Luke Taylor\n */\npublic class TestBusinessBeanImpl implements TestBusinessBean, ApplicationListener<SessionCreationEvent> {\n\n\t@Override\n\tpublic void setInteger(int i) {\n\t}\n\n\t@Override\n\tpublic int getInteger() {\n\t\treturn 1314;\n\t}\n\n\t@Override\n\tpublic void setString(String s) {\n\t}\n\n\tpublic String getString() {\n\t\treturn \"A string.\";\n\t}\n\n\t@Override\n\tpublic void doSomething() {\n\t}\n\n\t@Override\n\tpublic void unprotected() {\n\t}\n\n\t@Override\n\tpublic void onApplicationEvent(SessionCreationEvent event) {\n\t\tSystem.out.println(event);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/TestDeferredSecurityContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.springframework.security.core.context.DeferredSecurityContext;\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * @author Steve Riesenberg\n */\npublic class TestDeferredSecurityContext implements DeferredSecurityContext {\n\n\tprivate SecurityContext securityContext;\n\n\tprivate boolean isGenerated;\n\n\tpublic TestDeferredSecurityContext(SecurityContext securityContext, boolean isGenerated) {\n\t\tthis.securityContext = securityContext;\n\t\tthis.isGenerated = isGenerated;\n\t}\n\n\t@Override\n\tpublic SecurityContext get() {\n\t\treturn this.securityContext;\n\t}\n\n\t@Override\n\tpublic boolean isGenerated() {\n\t\treturn this.isGenerated;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/TransactionalTestBusinessBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config;\n\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * @author Luke Taylor\n */\npublic class TransactionalTestBusinessBean implements TestBusinessBean {\n\n\t@Override\n\tpublic void setInteger(int i) {\n\t}\n\n\t@Override\n\tpublic int getInteger() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void setString(String s) {\n\t}\n\n\t@Override\n\t@Transactional\n\tpublic void doSomething() {\n\t}\n\n\t@Override\n\tpublic void unprotected() {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/ConcereteSecurityConfigurerAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Rob Winch\n *\n */\nclass ConcereteSecurityConfigurerAdapter extends SecurityConfigurerAdapter<Object, SecurityBuilder<Object>> {\n\n\tprivate List<Object> list = new ArrayList<>();\n\n\t@Override\n\tpublic void configure(SecurityBuilder<Object> builder) {\n\t\tthis.list = postProcess(this.list);\n\t}\n\n\tConcereteSecurityConfigurerAdapter list(List<Object> l) {\n\t\tthis.list = l;\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/ObjectPostProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.config.ObjectPostProcessor;\n\nimport static org.assertj.core.api.Assertions.assertThatObject;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\npublic class ObjectPostProcessorTests {\n\n\t@Test\n\tpublic void convertTypes() {\n\t\tassertThatObject(PerformConversion.perform(new ArrayList<>())).isInstanceOf(LinkedList.class);\n\t}\n\n\tstatic class ListToLinkedListObjectPostProcessor implements ObjectPostProcessor<List<?>> {\n\n\t\t@Override\n\t\tpublic <O extends List<?>> O postProcess(O l) {\n\t\t\treturn (O) new LinkedList(l);\n\t\t}\n\n\t}\n\n\tstatic class PerformConversion {\n\n\t\tstatic List<?> perform(ArrayList<?> l) {\n\t\t\treturn new ListToLinkedListObjectPostProcessor().postProcess(l);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/SecurityConfigurerAdapterClosureTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.config.ObjectPostProcessor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\npublic class SecurityConfigurerAdapterClosureTests {\n\n\tConcereteSecurityConfigurerAdapter conf = new ConcereteSecurityConfigurerAdapter();\n\n\t@Test\n\tpublic void addPostProcessorClosureWhenPostProcessThenGetsApplied() throws Exception {\n\t\tSecurityBuilder<Object> builder = mock(SecurityBuilder.class);\n\t\tthis.conf.addObjectPostProcessor(new ObjectPostProcessor<List<String>>() {\n\t\t\t@Override\n\t\t\tpublic <O extends List<String>> O postProcess(O l) {\n\t\t\t\tl.add(\"a\");\n\t\t\t\treturn l;\n\t\t\t}\n\t\t});\n\t\tthis.conf.init(builder);\n\t\tthis.conf.configure(builder);\n\t\tassertThat(this.conf.list).contains(\"a\");\n\t}\n\n\tstatic class ConcereteSecurityConfigurerAdapter extends SecurityConfigurerAdapter<Object, SecurityBuilder<Object>> {\n\n\t\tprivate List<Object> list = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic void configure(SecurityBuilder<Object> builder) {\n\t\t\tthis.list = postProcess(this.list);\n\t\t}\n\n\t\tConcereteSecurityConfigurerAdapter list(List<Object> l) {\n\t\t\tthis.list = l;\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/SecurityConfigurerAdapterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.Ordered;\nimport org.springframework.security.config.ObjectPostProcessor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class SecurityConfigurerAdapterTests {\n\n\tConcereteSecurityConfigurerAdapter adapter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.adapter = new ConcereteSecurityConfigurerAdapter();\n\t}\n\n\t@Test\n\tpublic void postProcessObjectPostProcessorsAreSorted() {\n\t\tthis.adapter.addObjectPostProcessor(new OrderedObjectPostProcessor(Ordered.LOWEST_PRECEDENCE));\n\t\tthis.adapter.addObjectPostProcessor(new OrderedObjectPostProcessor(Ordered.HIGHEST_PRECEDENCE));\n\t\tassertThat(this.adapter.postProcess(\"hi\"))\n\t\t\t.isEqualTo(\"hi \" + Ordered.HIGHEST_PRECEDENCE + \" \" + Ordered.LOWEST_PRECEDENCE);\n\t}\n\n\tstatic class OrderedObjectPostProcessor implements ObjectPostProcessor<String>, Ordered {\n\n\t\tprivate final int order;\n\n\t\tOrderedObjectPostProcessor(int order) {\n\t\t\tthis.order = order;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getOrder() {\n\t\t\treturn this.order;\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic String postProcess(String object) {\n\t\t\treturn object + \" \" + this.order;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerArgumentMatchers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport org.mockito.ArgumentMatcher;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextChangedEvent;\n\nimport static org.mockito.ArgumentMatchers.argThat;\n\npublic final class SecurityContextChangedListenerArgumentMatchers {\n\n\tpublic static SecurityContextChangedEvent setAuthentication(Class<? extends Authentication> authenticationClass) {\n\t\treturn argThat(new ArgumentMatcher<SecurityContextChangedEvent>() {\n\t\t\tpublic boolean matches(SecurityContextChangedEvent event) {\n\t\t\t\tAuthentication previous = authentication(event.getOldContext());\n\t\t\t\tAuthentication next = authentication(event.getNewContext());\n\t\t\t\treturn previous == null && next != null && authenticationClass.isAssignableFrom(next.getClass());\n\t\t\t}\n\n\t\t\tpublic String toString() {\n\t\t\t\treturn \"authentication set to \" + authenticationClass;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static Authentication authentication(SecurityContext context) {\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn context.getAuthentication();\n\t}\n\n\tprivate SecurityContextChangedListenerArgumentMatchers() {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.MockSecurityContextHolderStrategy;\nimport org.springframework.security.core.context.ListeningSecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\n\n@Configuration\npublic class SecurityContextChangedListenerConfig {\n\n\tprivate SecurityContextHolderStrategy strategy = new MockSecurityContextHolderStrategy();\n\n\tprivate SecurityContextChangedListener listener = mock(SecurityContextChangedListener.class);\n\n\t@Bean\n\tSecurityContextHolderStrategy securityContextHolderStrategy() {\n\t\treturn spy(new ListeningSecurityContextHolderStrategy(this.strategy, this.listener));\n\t}\n\n\t@Bean\n\tSecurityContextChangedListener securityContextChangedListener() {\n\t\treturn this.listener;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/AuthenticationManagerBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication;\n\nimport java.util.Arrays;\nimport java.util.Properties;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.io.Resource;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;\nimport org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;\nimport org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class AuthenticationManagerBuilderTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tMockMvc mockMvc;\n\n\t@Test\n\tpublic void buildWhenAddAuthenticationProviderThenDoesNotPerformRegistration() throws Exception {\n\t\tObjectPostProcessor<Object> opp = mock(ObjectPostProcessor.class);\n\t\tAuthenticationProvider provider = mock(AuthenticationProvider.class);\n\t\tAuthenticationManagerBuilder builder = new AuthenticationManagerBuilder(opp);\n\t\tbuilder.authenticationProvider(provider);\n\t\tbuilder.build();\n\t\tverify(opp, never()).postProcess(provider);\n\t}\n\n\t// https://github.com/spring-projects/spring-security-javaconfig/issues/132\n\t@Test\n\tpublic void customAuthenticationEventPublisherWithWeb() throws Exception {\n\t\tObjectPostProcessor<Object> opp = mock(ObjectPostProcessor.class);\n\t\tAuthenticationEventPublisher aep = mock(AuthenticationEventPublisher.class);\n\t\tgiven(opp.postProcess(any())).willAnswer((a) -> a.getArgument(0));\n\t\tAuthenticationManager am = new AuthenticationManagerBuilder(opp).authenticationEventPublisher(aep)\n\t\t\t.inMemoryAuthentication()\n\t\t\t.and()\n\t\t\t.build();\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t.isThrownBy(() -> am.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\")));\n\t\tverify(aep).publishAuthenticationFailure(any(), any());\n\t}\n\n\t@Test\n\tpublic void getAuthenticationManagerWhenGlobalPasswordEncoderBeanThenUsed() throws Exception {\n\t\tthis.spring.register(PasswordEncoderGlobalConfig.class).autowire();\n\t\tAuthenticationManager manager = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tAuthentication auth = manager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tSecurityAssertions.assertThat(auth).name(\"user\").hasAuthority(\"ROLE_USER\");\n\t}\n\n\t@Test\n\tpublic void getAuthenticationManagerWhenProtectedPasswordEncoderBeanThenUsed() throws Exception {\n\t\tthis.spring.register(PasswordEncoderGlobalConfig.class).autowire();\n\t\tAuthenticationManager manager = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tAuthentication auth = manager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tSecurityAssertions.assertThat(auth).name(\"user\").hasAuthority(\"ROLE_USER\");\n\t}\n\n\t@Test\n\tpublic void authenticationManagerWhenMultipleProvidersThenWorks() throws Exception {\n\t\tthis.spring.register(MultiAuthenticationProvidersConfig.class).autowire();\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated().withUsername(\"user\")\n\t\t\t.withRoles(\"USER\");\n\t\tthis.mockMvc.perform(formLogin()).andExpect(user);\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher admin = authenticated().withUsername(\"admin\")\n\t\t\t.withRoles(\"USER\", \"ADMIN\");\n\t\tthis.mockMvc.perform(formLogin().user(\"admin\")).andExpect(admin);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthenticationProviderThenIsConfigured() throws Exception {\n\t\tObjectPostProcessor<Object> opp = mock(ObjectPostProcessor.class);\n\t\tAuthenticationProvider provider = mock(AuthenticationProvider.class);\n\t\tAuthenticationManagerBuilder builder = new AuthenticationManagerBuilder(opp);\n\t\tbuilder.authenticationProvider(provider);\n\t\tbuilder.build();\n\t\tassertThat(builder.isConfigured()).isTrue();\n\t}\n\n\t@Test\n\tpublic void buildWhenParentThenIsConfigured() throws Exception {\n\t\tObjectPostProcessor<Object> opp = mock(ObjectPostProcessor.class);\n\t\tAuthenticationManager parent = mock(AuthenticationManager.class);\n\t\tAuthenticationManagerBuilder builder = new AuthenticationManagerBuilder(opp);\n\t\tbuilder.parentAuthenticationManager(parent);\n\t\tbuilder.build();\n\t\tassertThat(builder.isConfigured()).isTrue();\n\t}\n\n\t@Test\n\tpublic void buildWhenNotConfiguredThenIsConfiguredFalse() throws Exception {\n\t\tObjectPostProcessor<Object> opp = mock(ObjectPostProcessor.class);\n\t\tAuthenticationManagerBuilder builder = new AuthenticationManagerBuilder(opp);\n\t\tbuilder.build();\n\t\tassertThat(builder.isConfigured()).isFalse();\n\t}\n\n\tpublic void buildWhenUserFromProperties() throws Exception {\n\t\tthis.spring.register(UserFromPropertiesConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin().user(\"joe\", \"joespassword\"))\n\t\t\t.andExpect(authenticated().withUsername(\"joe\").withRoles(\"USER\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MultiAuthenticationProvidersConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(PasswordEncodedUser.user())\n\t\t\t\t\t.and()\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(PasswordEncodedUser.admin());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordEncoderGlobalConfig {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordEncoderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalAuthentication\n\t@Import(ObjectPostProcessorConfiguration.class)\n\tstatic class UserFromPropertiesConfig {\n\n\t\t@Value(\"classpath:org/springframework/security/config/users.properties\")\n\t\tResource users;\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager() throws Exception {\n\t\t\treturn new ProviderManager(Arrays.asList(authenticationProvider()));\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationProvider authenticationProvider() throws Exception {\n\t\t\treturn new DaoAuthenticationProvider(userDetailsService());\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() throws Exception {\n\t\t\tProperties properties = new Properties();\n\t\t\tproperties.load(this.users.getInputStream());\n\t\t\treturn new InMemoryUserDetailsManager(properties);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/BaseAuthenticationConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\n\n/**\n * @author Rob Winch\n */\n@Configuration\npublic class BaseAuthenticationConfig {\n\n\t@Autowired\n\tprotected void configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t// @formatter:off\n\t\tauth\n\t\t\t.inMemoryAuthentication()\n\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\").and()\n\t\t\t\t.withUser(\"admin\").password(\"password\").roles(\"USER\", \"ADMIN\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/NamespaceAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceAuthenticationManagerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void authenticationMangerWhenDefaultThenEraseCredentialsIsTrue() throws Exception {\n\t\tthis.spring.register(EraseCredentialsTrueDefaultConfig.class).autowire();\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher nullCredentials = authenticated()\n\t\t\t.withAuthentication((a) -> assertThat(a.getCredentials()).isNull());\n\t\tthis.mockMvc.perform(formLogin()).andExpect(nullCredentials);\n\t\tthis.mockMvc.perform(formLogin()).andExpect(nullCredentials);\n\t\t// no exception due to username being cleared out\n\t}\n\n\t@Test\n\tpublic void authenticationMangerWhenEraseCredentialsIsFalseThenCredentialsNotNull() throws Exception {\n\t\tthis.spring.register(EraseCredentialsFalseConfig.class).autowire();\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher notNullCredentials = authenticated()\n\t\t\t.withAuthentication((a) -> assertThat(a.getCredentials()).isNotNull());\n\t\tthis.mockMvc.perform(formLogin()).andExpect(notNullCredentials);\n\t\tthis.mockMvc.perform(formLogin()).andExpect(notNullCredentials);\n\t\t// no exception due to username being cleared out\n\t}\n\n\t@Test\n\t// SEC-2533\n\tpublic void authenticationManagerWhenGlobalAndEraseCredentialsIsFalseThenCredentialsNotNull() throws Exception {\n\t\tthis.spring.register(GlobalEraseCredentialsFalseConfig.class).autowire();\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher notNullCredentials = authenticated()\n\t\t\t.withAuthentication((a) -> assertThat(a.getCredentials()).isNotNull());\n\t\tthis.mockMvc.perform(formLogin()).andExpect(notNullCredentials);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EraseCredentialsTrueDefaultConfig {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(PasswordEncodedUser.user());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EraseCredentialsFalseConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.eraseCredentials(false)\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t.withUser(PasswordEncodedUser.user());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class GlobalEraseCredentialsFalseConfig {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.eraseCredentials(false)\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t.withUser(PasswordEncodedUser.user());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/NamespaceAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceAuthenticationProviderTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\t// authentication-provider@ref\n\tpublic void authenticationProviderRef() throws Exception {\n\t\tthis.spring.register(AuthenticationProviderRefConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\t// authentication-provider@user-service-ref\n\tpublic void authenticationProviderUserServiceRef() throws Exception {\n\t\tthis.spring.register(AuthenticationProviderRefConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthenticationProviderRefConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.authenticationProvider(authenticationProvider());\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tDaoAuthenticationProvider authenticationProvider() {\n\t\t\treturn new DaoAuthenticationProvider(new InMemoryUserDetailsManager(PasswordEncodedUser.user()));\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class UserServiceRefConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.userDetailsService(userDetailsService());\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/NamespaceJdbcUserServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication;\n\nimport javax.sql.DataSource;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceJdbcUserServiceTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void jdbcUserService() throws Exception {\n\t\tthis.spring.register(DataSourceConfig.class, JdbcUserServiceConfig.class).autowire();\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated().withUsername(\"user\");\n\t\tthis.mockMvc.perform(formLogin()).andExpect(user);\n\t}\n\n\t@Test\n\tpublic void jdbcUserServiceCustom() throws Exception {\n\t\tthis.spring.register(CustomDataSourceConfig.class, CustomJdbcUserServiceSampleConfig.class).autowire();\n\t\t// @formatter:off\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher dba = authenticated()\n\t\t\t\t.withUsername(\"user\")\n\t\t\t\t.withRoles(\"DBA\", \"USER\");\n\t\t// @formatter:on\n\t\tthis.mockMvc.perform(formLogin()).andExpect(dba);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class JdbcUserServiceConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.jdbcAuthentication()\n\t\t\t\t\t.withDefaultSchema()\n\t\t\t\t\t.withUser(PasswordEncodedUser.user())\n\t\t\t\t\t.dataSource(dataSource); // jdbc-user-service@data-source-ref\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class DataSourceConfig {\n\n\t\t@Bean\n\t\tDataSource dataSource() {\n\t\t\tEmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();\n\t\t\treturn builder.setType(EmbeddedDatabaseType.HSQL).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomJdbcUserServiceSampleConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.jdbcAuthentication()\n\t\t\t\t// jdbc-user-service@dataSource\n\t\t\t\t.dataSource(dataSource)\n\t\t\t\t// jdbc-user-service@cache-ref\n\t\t\t\t.userCache(new CustomUserCache())\n\t\t\t\t// jdbc-user-service@users-byusername-query\n\t\t\t\t.usersByUsernameQuery(\"select principal,credentials,true from users where principal = ?\")\n\t\t\t\t// jdbc-user-service@authorities-by-username-query\n\t\t\t\t.authoritiesByUsernameQuery(\"select principal,role from roles where principal = ?\")\n\t\t\t\t// jdbc-user-service@group-authorities-by-username-query\n\t\t\t\t.groupAuthoritiesByUsername(JdbcDaoImpl.DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY)\n\t\t\t\t// jdbc-user-service@role-prefix\n\t\t\t\t.rolePrefix(\"ROLE_\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tstatic class CustomUserCache implements UserCache {\n\n\t\t\t@Override\n\t\t\tpublic UserDetails getUserFromCache(String username) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void putUserInCache(UserDetails user) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void removeUserFromCache(String username) {\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class CustomDataSourceConfig {\n\n\t\t@Bean\n\t\tDataSource dataSource() {\n\t\t\tEmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()\n\t\t\t\t// simulate that the DB already has the schema loaded and users in it\n\t\t\t\t.addScript(\"CustomJdbcUserServiceSampleConfig.sql\");\n\t\t\treturn builder.setType(EmbeddedDatabaseType.HSQL).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/NamespacePasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication;\n\nimport javax.sql.DataSource;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespacePasswordEncoderTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void passwordEncoderRefWithInMemory() throws Exception {\n\t\tthis.spring.register(PasswordEncoderWithInMemoryConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(authenticated());\n\t}\n\n\t@Test\n\tpublic void passwordEncoderRefWithJdbc() throws Exception {\n\t\tthis.spring.register(PasswordEncoderWithJdbcConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(authenticated());\n\t}\n\n\t@Test\n\tpublic void passwordEncoderRefWithUserDetailsService() throws Exception {\n\t\tthis.spring.register(PasswordEncoderWithUserDetailsServiceConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(authenticated());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordEncoderWithInMemoryConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tBCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t.withUser(\"user\").password(encoder.encode(\"password\")).roles(\"USER\").and()\n\t\t\t\t.passwordEncoder(encoder);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordEncoderWithJdbcConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tBCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.jdbcAuthentication()\n\t\t\t\t.withDefaultSchema()\n\t\t\t\t.dataSource(dataSource())\n\t\t\t\t.withUser(\"user\").password(encoder.encode(\"password\")).roles(\"USER\").and()\n\t\t\t\t.passwordEncoder(encoder);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tDataSource dataSource() {\n\t\t\tEmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();\n\t\t\treturn builder.setType(EmbeddedDatabaseType.HSQL).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordEncoderWithUserDetailsServiceConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tBCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n\t\t\t// @formatter:off\n\t\t\tUserDetails user = User.withUsername(\"user\")\n\t\t\t\t.passwordEncoder(encoder::encode)\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tInMemoryUserDetailsManager uds = new InMemoryUserDetailsManager(user);\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.userDetailsService(uds)\n\t\t\t\t.passwordEncoder(encoder);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tDataSource dataSource() {\n\t\t\tEmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();\n\t\t\treturn builder.setType(EmbeddedDatabaseType.HSQL).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/PasswordEncoderConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class PasswordEncoderConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void passwordEncoderRefWhenNoAuthenticationManagerBeanThenNoExceptionThrown() {\n\t\tthis.spring.register(PasswordEncoderConfig.class).autowire();\n\t}\n\n\t@Test\n\tpublic void passwordEncoderRefWhenAuthenticationManagerBuilderThenAuthenticationSuccess() throws Exception {\n\t\tthis.spring.register(PasswordEncoderNoAuthManagerLoadsConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(authenticated());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordEncoderConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tBCryptPasswordEncoder encoder = passwordEncoder();\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(encoder.encode(\"password\")).roles(\"USER\").and()\n\t\t\t\t\t.passwordEncoder(encoder);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tBCryptPasswordEncoder passwordEncoder() {\n\t\t\treturn new BCryptPasswordEncoder();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordEncoderNoAuthManagerLoadsConfig {\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tBCryptPasswordEncoder encoder = passwordEncoder();\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(encoder.encode(\"password\")).roles(\"USER\").and()\n\t\t\t\t\t.passwordEncoder(encoder);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tBCryptPasswordEncoder passwordEncoder() {\n\t\t\treturn new BCryptPasswordEncoder();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfigurationPublishTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.config.MockEventListener;\nimport org.springframework.security.config.users.AuthenticationTestConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\npublic class AuthenticationConfigurationPublishTests {\n\n\t@Autowired\n\tMockEventListener<AuthenticationSuccessEvent> listener;\n\n\tAuthenticationManager authenticationManager;\n\n\t// gh-4940\n\t@Test\n\tpublic void authenticationEventPublisherBeanUsedByDefault() {\n\t\tthis.authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tassertThat(this.listener.getEvents()).hasSize(1);\n\t}\n\n\t@Autowired\n\tpublic void setAuthenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {\n\t\tthis.authenticationManager = authenticationConfiguration.getAuthenticationManager();\n\t}\n\n\t@Configuration\n\t@EnableGlobalAuthentication\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class Config {\n\n\t\t@Bean\n\t\tAuthenticationEventPublisher publisher() {\n\t\t\treturn new DefaultAuthenticationEventPublisher();\n\t\t}\n\n\t\t@Bean\n\t\tMockEventListener<AuthenticationSuccessEvent> eventListener() {\n\t\t\treturn new MockEventListener<AuthenticationSuccessEvent>() {\n\t\t\t};\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.aop.framework.ProxyFactoryBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.AlreadyBuiltException;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.users.AuthenticationTestConfiguration;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.startsWith;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class AuthenticationConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tprivate Service service;\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void orderingAutowiredOnEnableGlobalMethodSecurity() {\n\t\tthis.spring\n\t\t\t.register(AuthenticationTestConfiguration.class, GlobalMethodSecurityAutowiredConfig.class,\n\t\t\t\t\tServicesConfig.class)\n\t\t\t.autowire();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tthis.service.run();\n\t}\n\n\t@Test\n\tpublic void orderingAutowiredOnEnableWebSecurity() {\n\t\tthis.spring\n\t\t\t.register(AuthenticationTestConfiguration.class, WebSecurityConfig.class,\n\t\t\t\t\tGlobalMethodSecurityAutowiredConfig.class, ServicesConfig.class)\n\t\t\t.autowire();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tthis.service.run();\n\t}\n\n\t@Test\n\tpublic void getAuthenticationManagerWhenNoAuthenticationThenNull() throws Exception {\n\t\tthis.spring.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(AuthenticationConfiguration.class).getAuthenticationManager())\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void getAuthenticationManagerWhenNoOpGlobalAuthenticationConfigurerAdapterThenNull() throws Exception {\n\t\tthis.spring\n\t\t\t.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class,\n\t\t\t\t\tNoOpGlobalAuthenticationConfigurerAdapter.class)\n\t\t\t.autowire();\n\t\tassertThat(this.spring.getContext().getBean(AuthenticationConfiguration.class).getAuthenticationManager())\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void getAuthenticationWhenGlobalAuthenticationConfigurerAdapterThenAuthenticates() throws Exception {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"user\",\n\t\t\t\t\"password\");\n\t\tthis.spring\n\t\t\t.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class,\n\t\t\t\t\tUserGlobalAuthenticationConfigurerAdapter.class)\n\t\t\t.autowire();\n\t\tAuthenticationManager authentication = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tassertThat(authentication.authenticate(token).getName()).isEqualTo(token.getName());\n\t}\n\n\t@Test\n\tpublic void getAuthenticationWhenAuthenticationManagerBeanThenAuthenticates() throws Exception {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"user\",\n\t\t\t\t\"password\");\n\t\tthis.spring\n\t\t\t.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class,\n\t\t\t\t\tAuthenticationManagerBeanConfig.class)\n\t\t\t.autowire();\n\t\tAuthenticationManager authentication = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tgiven(authentication.authenticate(token)).willReturn(TestAuthentication.authenticatedUser());\n\t\tassertThat(authentication.authenticate(token).getName()).isEqualTo(token.getName());\n\t}\n\n\t@Test\n\tpublic void getAuthenticationWhenMultipleThenOrdered() throws Exception {\n\t\tthis.spring\n\t\t\t.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class,\n\t\t\t\t\tAuthenticationManagerBeanConfig.class)\n\t\t\t.autowire();\n\t\tAuthenticationConfiguration config = this.spring.getContext().getBean(AuthenticationConfiguration.class);\n\t\tconfig.setGlobalAuthenticationConfigurers(Arrays.asList(new LowestOrderGlobalAuthenticationConfigurerAdapter(),\n\t\t\t\tnew HighestOrderGlobalAuthenticationConfigurerAdapter(),\n\t\t\t\tnew DefaultOrderGlobalAuthenticationConfigurerAdapter()));\n\t}\n\n\t@Test\n\tpublic void getAuthenticationWhenConfiguredThenBootNotTrigger() throws Exception {\n\t\tthis.spring.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class).autowire();\n\t\tAuthenticationConfiguration config = this.spring.getContext().getBean(AuthenticationConfiguration.class);\n\t\tconfig.setGlobalAuthenticationConfigurers(Arrays.asList(new ConfiguresInMemoryConfigurerAdapter(),\n\t\t\t\tnew BootGlobalAuthenticationConfigurerAdapter()));\n\t\tAuthenticationManager authenticationManager = config.getAuthenticationManager();\n\t\tauthenticationManager.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(() -> authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"boot\", \"password\")));\n\t}\n\n\t@Test\n\tpublic void getAuthenticationWhenNotConfiguredThenBootTrigger() throws Exception {\n\t\tthis.spring.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class).autowire();\n\t\tAuthenticationConfiguration config = this.spring.getContext().getBean(AuthenticationConfiguration.class);\n\t\tconfig.setGlobalAuthenticationConfigurers(Arrays.asList(new BootGlobalAuthenticationConfigurerAdapter()));\n\t\tAuthenticationManager authenticationManager = config.getAuthenticationManager();\n\t\tauthenticationManager.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"boot\", \"password\"));\n\t}\n\n\t// gh-2531\n\t@Test\n\tpublic void getAuthenticationManagerWhenPostProcessThenUsesBeanClassLoaderOnProxyFactoryBean() throws Exception {\n\t\tthis.spring.register(Sec2531Config.class).autowire();\n\t\tObjectPostProcessor<Object> opp = this.spring.getContext().getBean(ObjectPostProcessor.class);\n\t\tgiven(opp.postProcess(any())).willAnswer((a) -> a.getArgument(0));\n\t\tAuthenticationConfiguration config = this.spring.getContext().getBean(AuthenticationConfiguration.class);\n\t\tconfig.getAuthenticationManager();\n\t\tverify(opp).postProcess(any(ProxyFactoryBean.class));\n\t}\n\n\t@Test\n\tpublic void getAuthenticationManagerWhenSec2822ThenCannotForceAuthenticationAlreadyBuilt() throws Exception {\n\t\tthis.spring.register(Sec2822WebSecurity.class, Sec2822UseAuth.class, Sec2822Config.class).autowire();\n\t\tthis.spring.getContext().getBean(AuthenticationConfiguration.class).getAuthenticationManager();\n\t\t// no exception\n\t}\n\n\t// sec-2868\n\t@Test\n\tpublic void getAuthenticationWhenUserDetailsServiceBeanThenAuthenticationManagerUsesUserDetailsServiceBean()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(UserDetailsServiceBeanConfig.class).autowire();\n\t\tUserDetailsService uds = this.spring.getContext().getBean(UserDetailsService.class);\n\t\tAuthenticationManager am = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tgiven(uds.loadUserByUsername(\"user\")).willReturn(PasswordEncodedUser.user(), PasswordEncodedUser.user());\n\t\tam.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t.isThrownBy(() -> am.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"invalid\")));\n\t}\n\n\t@Test\n\tpublic void getAuthenticationWhenUserDetailsServiceAndPasswordEncoderBeanThenEncoderUsed() throws Exception {\n\t\tUserDetails user = new User(\"user\", \"$2a$10$FBAKClV1zBIOOC9XMXf3AO8RoGXYVYsfvUdoLxGkd/BnXEn4tqT3u\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.spring.register(UserDetailsServiceBeanWithPasswordEncoderConfig.class).autowire();\n\t\tUserDetailsService uds = this.spring.getContext().getBean(UserDetailsService.class);\n\t\tAuthenticationManager am = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tgiven(uds.loadUserByUsername(\"user\")).willReturn(User.withUserDetails(user).build(),\n\t\t\t\tUser.withUserDetails(user).build());\n\t\tam.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t.isThrownBy(() -> am.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"invalid\")));\n\t}\n\n\t@Test\n\tpublic void getAuthenticationWhenUserDetailsServiceAndPasswordManagerThenManagerUsed() throws Exception {\n\t\tUserDetails user = new User(\"user\", \"{noop}password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.spring.register(UserDetailsPasswordManagerBeanConfig.class).autowire();\n\t\tUserDetailsPasswordManagerBeanConfig.Manager manager = this.spring.getContext()\n\t\t\t.getBean(UserDetailsPasswordManagerBeanConfig.Manager.class);\n\t\tAuthenticationManager am = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tgiven(manager.loadUserByUsername(\"user\")).willReturn(User.withUserDetails(user).build(),\n\t\t\t\tUser.withUserDetails(user).build());\n\t\tgiven(manager.updatePassword(any(), any())).willReturn(user);\n\t\tam.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tverify(manager).updatePassword(eq(user), startsWith(\"{bcrypt}\"));\n\t}\n\n\t@Test\n\tpublic void getAuthenticationWhenAuthenticationProviderAndUserDetailsBeanThenAuthenticationProviderUsed()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthenticationProviderBeanAndUserDetailsServiceConfig.class).autowire();\n\t\tAuthenticationProvider ap = this.spring.getContext().getBean(AuthenticationProvider.class);\n\t\tAuthenticationManager am = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tgiven(ap.supports(any())).willReturn(true);\n\t\tgiven(ap.authenticate(any())).willReturn(TestAuthentication.authenticatedUser());\n\t\tam.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t}\n\n\t// gh-3091\n\t@Test\n\tpublic void getAuthenticationWhenAuthenticationProviderBeanThenUsed() throws Exception {\n\t\tthis.spring.register(AuthenticationProviderBeanConfig.class).autowire();\n\t\tAuthenticationProvider ap = this.spring.getContext().getBean(AuthenticationProvider.class);\n\t\tAuthenticationManager am = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConfiguration.class)\n\t\t\t.getAuthenticationManager();\n\t\tgiven(ap.supports(any())).willReturn(true);\n\t\tgiven(ap.authenticate(any())).willReturn(TestAuthentication.authenticatedUser());\n\t\tam.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t}\n\n\t@Test\n\tpublic void enableGlobalMethodSecurityWhenPreAuthorizeThenNoException() {\n\t\tthis.spring.register(UsesPreAuthorizeMethodSecurityConfig.class, AuthenticationManagerBeanConfig.class)\n\t\t\t.autowire();\n\t\t// no exception\n\t}\n\n\t@Test\n\tpublic void enableGlobalMethodSecurityWhenPreAuthorizeThenUsesMethodSecurityService() {\n\t\tthis.spring\n\t\t\t.register(ServicesConfig.class, UsesPreAuthorizeMethodSecurityConfig.class,\n\t\t\t\t\tAuthenticationManagerBeanConfig.class)\n\t\t\t.autowire();\n\t\t// no exception\n\t}\n\n\t@Test\n\tpublic void getAuthenticationManagerBeanWhenMultipleDefinedAndOnePrimaryThenNoException() throws Exception {\n\t\tthis.spring.register(MultipleAuthenticationManagerBeanConfig.class).autowire();\n\t\tthis.spring.getContext().getBeanFactory().getBean(AuthenticationConfiguration.class).getAuthenticationManager();\n\t}\n\n\t@Test\n\tpublic void getAuthenticationManagerWhenAuthenticationConfigurationSubclassedThenBuildsUsingBean()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthenticationConfigurationSubclass.class).autowire();\n\t\tAuthenticationManagerBuilder ap = this.spring.getContext().getBean(AuthenticationManagerBuilder.class);\n\t\tthis.spring.getContext().getBean(AuthenticationConfiguration.class).getAuthenticationManager();\n\t\tassertThatExceptionOfType(AlreadyBuiltException.class).isThrownBy(ap::build);\n\t}\n\n\t@Test\n\tpublic void configureWhenDefaultsThenDefaultAuthenticationEventPublisher() {\n\t\tthis.spring.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class).autowire();\n\t\tAuthenticationManagerBuilder authenticationManagerBuilder = this.spring.getContext()\n\t\t\t.getBean(AuthenticationManagerBuilder.class);\n\t\tAuthenticationEventPublisher eventPublisher = (AuthenticationEventPublisher) ReflectionTestUtils\n\t\t\t.getField(authenticationManagerBuilder, \"eventPublisher\");\n\t\tassertThat(eventPublisher).isInstanceOf(DefaultAuthenticationEventPublisher.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenCustomAuthenticationEventPublisherThenCustomAuthenticationEventPublisher() {\n\t\tthis.spring\n\t\t\t.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class,\n\t\t\t\t\tCustomAuthenticationEventPublisherConfig.class)\n\t\t\t.autowire();\n\t\tAuthenticationManagerBuilder authenticationManagerBuilder = this.spring.getContext()\n\t\t\t.getBean(AuthenticationManagerBuilder.class);\n\t\tAuthenticationEventPublisher eventPublisher = (AuthenticationEventPublisher) ReflectionTestUtils\n\t\t\t.getField(authenticationManagerBuilder, \"eventPublisher\");\n\t\tassertThat(eventPublisher)\n\t\t\t.isInstanceOf(CustomAuthenticationEventPublisherConfig.MyAuthenticationEventPublisher.class);\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(securedEnabled = true)\n\tstatic class GlobalMethodSecurityAutowiredConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebSecurityConfig {\n\n\t}\n\n\t@Configuration\n\tstatic class NoOpGlobalAuthenticationConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {\n\n\t}\n\n\t@Configuration\n\tstatic class UserGlobalAuthenticationConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {\n\n\t\t@Override\n\t\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\t\tauth.inMemoryAuthentication().withUser(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class AuthenticationManagerBeanConfig {\n\n\t\tAuthenticationManager authenticationManager = mock(AuthenticationManager.class);\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager() {\n\t\t\treturn this.authenticationManager;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ServicesConfig {\n\n\t\t@Bean\n\t\tService service() {\n\t\t\treturn new ServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class CustomAuthenticationEventPublisherConfig {\n\n\t\t@Bean\n\t\tAuthenticationEventPublisher eventPublisher() {\n\t\t\treturn new MyAuthenticationEventPublisher();\n\t\t}\n\n\t\tstatic class MyAuthenticationEventPublisher implements AuthenticationEventPublisher {\n\n\t\t\t@Override\n\t\t\tpublic void publishAuthenticationSuccess(Authentication authentication) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface Service {\n\n\t\tvoid run();\n\n\t}\n\n\tstatic class ServiceImpl implements Service {\n\n\t\t@Override\n\t\t@Secured(\"ROLE_USER\")\n\t\tpublic void run() {\n\t\t}\n\n\t}\n\n\tstatic class DefaultOrderGlobalAuthenticationConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {\n\n\t\tstatic List<Class<?>> inits = new ArrayList<>();\n\t\tstatic List<Class<?>> configs = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\t\tinits.add(getClass());\n\t\t}\n\n\t\t@Override\n\t\tpublic void configure(AuthenticationManagerBuilder auth) {\n\t\t\tconfigs.add(getClass());\n\t\t}\n\n\t}\n\n\t@Order(Ordered.LOWEST_PRECEDENCE)\n\tstatic class LowestOrderGlobalAuthenticationConfigurerAdapter\n\t\t\textends DefaultOrderGlobalAuthenticationConfigurerAdapter {\n\n\t}\n\n\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\tstatic class HighestOrderGlobalAuthenticationConfigurerAdapter\n\t\t\textends DefaultOrderGlobalAuthenticationConfigurerAdapter {\n\n\t}\n\n\tstatic class ConfiguresInMemoryConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {\n\n\t\t@Override\n\t\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(PasswordEncodedUser.user());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Order(Ordered.LOWEST_PRECEDENCE)\n\tstatic class BootGlobalAuthenticationConfigurerAdapter extends DefaultOrderGlobalAuthenticationConfigurerAdapter {\n\n\t\t@Override\n\t\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\t\tauth.apply(new DefaultBootGlobalAuthenticationConfigurerAdapter());\n\t\t}\n\n\t}\n\n\tstatic class DefaultBootGlobalAuthenticationConfigurerAdapter\n\t\t\textends DefaultOrderGlobalAuthenticationConfigurerAdapter {\n\n\t\t@Override\n\t\tpublic void configure(AuthenticationManagerBuilder auth) {\n\t\t\tif (auth.isConfigured()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tUserDetails user = User.withUserDetails(PasswordEncodedUser.user()).username(\"boot\").build();\n\t\t\tList<UserDetails> users = Arrays.asList(user);\n\t\t\tInMemoryUserDetailsManager inMemory = new InMemoryUserDetailsManager(users);\n\t\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(inMemory);\n\t\t\tauth.authenticationProvider(provider);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(AuthenticationConfiguration.class)\n\tstatic class Sec2531Config {\n\n\t\t@Bean\n\t\tObjectPostProcessor objectPostProcessor() {\n\t\t\treturn mock(ObjectPostProcessor.class);\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager manager() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(AuthenticationConfiguration.class)\n\tstatic class Sec2822Config {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class Sec2822WebSecurity {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tauth.inMemoryAuthentication();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class Sec2822UseAuth {\n\n\t\t@Autowired\n\t\tvoid useAuthenticationManager(AuthenticationConfiguration auth) throws Exception {\n\t\t\tauth.getAuthenticationManager();\n\t\t}\n\n\t\t// Ensures that Sec2822UseAuth is initialized before Sec2822WebSecurity\n\t\t// must have additional GlobalAuthenticationConfigurerAdapter to trigger SEC-2822\n\t\t@Bean\n\t\tstatic GlobalAuthenticationConfigurerAdapter bootGlobalAuthenticationConfigurerAdapter() {\n\t\t\treturn new BootGlobalAuthenticationConfigurerAdapter();\n\t\t}\n\n\t\tstatic class BootGlobalAuthenticationConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import({ AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class })\n\tstatic class UserDetailsServiceBeanConfig {\n\n\t\tUserDetailsService uds = mock(UserDetailsService.class);\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn this.uds;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import({ AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class })\n\tstatic class UserDetailsServiceBeanWithPasswordEncoderConfig {\n\n\t\tUserDetailsService uds = mock(UserDetailsService.class);\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn this.uds;\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn new BCryptPasswordEncoder();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import({ AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class })\n\tstatic class UserDetailsPasswordManagerBeanConfig {\n\n\t\tManager manager = mock(Manager.class);\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn this.manager;\n\t\t}\n\n\t\tinterface Manager extends UserDetailsService, UserDetailsPasswordService {\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import({ AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class })\n\tstatic class AuthenticationProviderBeanConfig {\n\n\t\tAuthenticationProvider provider = mock(AuthenticationProvider.class);\n\n\t\t@Bean\n\t\tAuthenticationProvider authenticationProvider() {\n\t\t\treturn this.provider;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import({ AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class })\n\tstatic class AuthenticationProviderBeanAndUserDetailsServiceConfig {\n\n\t\tAuthenticationProvider provider = mock(AuthenticationProvider.class);\n\n\t\tUserDetailsService uds = mock(UserDetailsService.class);\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn this.uds;\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationProvider authenticationProvider() {\n\t\t\treturn this.provider;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class UsesPreAuthorizeMethodSecurityConfig {\n\n\t\t@PreAuthorize(\"denyAll\")\n\t\tvoid run() {\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(securedEnabled = true)\n\tstatic class UsesServiceMethodSecurityConfig {\n\n\t\t@Autowired\n\t\tService service;\n\n\t}\n\n\t@Configuration\n\t@Import(AuthenticationConfiguration.class)\n\tstatic class MultipleAuthenticationManagerBeanConfig {\n\n\t\t@Bean\n\t\t@Primary\n\t\tAuthenticationManager manager1() {\n\t\t\treturn mock(AuthenticationManager.class);\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager manager2() {\n\t\t\treturn mock(AuthenticationManager.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class AuthenticationConfigurationSubclass extends AuthenticationConfiguration {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationManagerBeanRegistrationAotProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.SpringProxy;\nimport org.springframework.aop.framework.Advised;\nimport org.springframework.aot.generate.GenerationContext;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.aot.test.generate.TestGenerationContext;\nimport org.springframework.beans.factory.aot.BeanRegistrationAotContribution;\nimport org.springframework.beans.factory.aot.BeanRegistrationCode;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.beans.factory.support.RegisteredBean;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.core.DecoratingProxy;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link AuthenticationManagerBeanRegistrationAotProcessor}\n *\n * @author Marcus da Coregio\n */\nclass AuthenticationManagerBeanRegistrationAotProcessorTests {\n\n\tprivate final AuthenticationManagerBeanRegistrationAotProcessor processor = new AuthenticationManagerBeanRegistrationAotProcessor();\n\n\tprivate final GenerationContext generationContext = new TestGenerationContext();\n\n\t@Test\n\tvoid shouldSkipWhenInterfaceNotImplemented() {\n\t\tprocess(NoAuthenticationManager.class);\n\t\tassertThat(this.generationContext.getRuntimeHints().proxies().jdkProxyHints()).isEmpty();\n\t}\n\n\t@Test\n\tvoid shouldProcessWhenImplementsInterface() {\n\t\tprocess(MyAuthenticationManager.class);\n\t\tassertThat(RuntimeHintsPredicates.proxies()\n\t\t\t.forInterfaces(AuthenticationManager.class, SpringProxy.class, Advised.class, DecoratingProxy.class))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid shouldProcessWhenSuperclassImplementsInterface() {\n\t\tprocess(ChildAuthenticationManager.class);\n\t\tassertThat(RuntimeHintsPredicates.proxies()\n\t\t\t.forInterfaces(AuthenticationManager.class, SpringProxy.class, Advised.class, DecoratingProxy.class))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\tprivate void process(Class<?> beanClass) {\n\t\tBeanRegistrationAotContribution contribution = createContribution(beanClass);\n\t\tif (contribution != null) {\n\t\t\tcontribution.applyTo(this.generationContext, mock(BeanRegistrationCode.class));\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate BeanRegistrationAotContribution createContribution(Class<?> beanClass) {\n\t\tDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();\n\t\tbeanFactory.registerBeanDefinition(beanClass.getName(), new RootBeanDefinition(beanClass));\n\t\treturn this.processor.processAheadOfTime(RegisteredBean.of(beanFactory, beanClass.getName()));\n\t}\n\n\tstatic class NoAuthenticationManager {\n\n\t}\n\n\tstatic class MyAuthenticationManager implements AuthenticationManager {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tstatic class ChildAuthenticationManager extends MyAuthenticationManager {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/configuration/EnableGlobalAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configuration;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class EnableGlobalAuthenticationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t// gh-4086\n\t@Test\n\tpublic void authenticationConfigurationWhenGetAuthenticationManagerThenNotNull() throws Exception {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tAuthenticationConfiguration auth = this.spring.getContext().getBean(AuthenticationConfiguration.class);\n\t\tassertThat(auth.getAuthenticationManager()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void enableGlobalAuthenticationWhenNoConfigurationAnnotationThenBeanProxyingEnabled() {\n\t\tthis.spring.register(BeanProxyEnabledByDefaultConfig.class).autowire();\n\t\tChild childBean = this.spring.getContext().getBean(Child.class);\n\t\tParent parentBean = this.spring.getContext().getBean(Parent.class);\n\t\tassertThat(parentBean.getChild()).isSameAs(childBean);\n\t}\n\n\t@Test\n\tpublic void enableGlobalAuthenticationWhenProxyBeanMethodsFalseThenBeanProxyingDisabled() {\n\t\tthis.spring.register(BeanProxyDisabledConfig.class).autowire();\n\t\tChild childBean = this.spring.getContext().getBean(Child.class);\n\t\tParent parentBean = this.spring.getContext().getBean(Parent.class);\n\t\tassertThat(parentBean.getChild()).isNotSameAs(childBean);\n\t}\n\n\t@Configuration\n\t@EnableGlobalAuthentication\n\tstatic class Config {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tauth.inMemoryAuthentication().withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalAuthentication\n\tstatic class BeanProxyEnabledByDefaultConfig {\n\n\t\t@Bean\n\t\tChild child() {\n\t\t\treturn new Child();\n\t\t}\n\n\t\t@Bean\n\t\tParent parent() {\n\t\t\treturn new Parent(child());\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableGlobalAuthentication\n\tstatic class BeanProxyDisabledConfig {\n\n\t\t@Bean\n\t\tChild child() {\n\t\t\treturn new Child();\n\t\t}\n\n\t\t@Bean\n\t\tParent parent() {\n\t\t\treturn new Parent(child());\n\t\t}\n\n\t}\n\n\tstatic class Parent {\n\n\t\tprivate Child child;\n\n\t\tParent(Child child) {\n\t\t\tthis.child = child;\n\t\t}\n\n\t\tChild getChild() {\n\t\t\treturn this.child;\n\t\t}\n\n\t}\n\n\tstatic class Child {\n\n\t\tChild() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/configurers/ldap/LdapAuthenticationProviderConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.ldap;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\npublic class LdapAuthenticationProviderConfigurerTests {\n\n\tprivate LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> configurer;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.configurer = new LdapAuthenticationProviderConfigurer<>();\n\t}\n\n\t// SEC-2557\n\t@Test\n\tpublic void getAuthoritiesMapper() throws Exception {\n\t\tassertThat(this.configurer.getAuthoritiesMapper()).isInstanceOf(SimpleAuthorityMapper.class);\n\t\tthis.configurer.authoritiesMapper(new NullAuthoritiesMapper());\n\t\tassertThat(this.configurer.getAuthoritiesMapper()).isInstanceOf(NullAuthoritiesMapper.class);\n\t}\n\n\t@Test\n\tpublic void customAuthoritiesPopulator() throws Exception {\n\t\tassertThat(ReflectionTestUtils.getField(this.configurer, \"ldapAuthoritiesPopulator\")).isNull();\n\t\tthis.configurer.ldapAuthoritiesPopulator(new NullLdapAuthoritiesPopulator());\n\t\tassertThat(ReflectionTestUtils.getField(this.configurer, \"ldapAuthoritiesPopulator\"))\n\t\t\t.isInstanceOf(NullLdapAuthoritiesPopulator.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenObjectPostProcessorThenAuthoritiesPopulatorIsPostProcessed() {\n\t\tLdapAuthoritiesPopulator populator = mock(LdapAuthoritiesPopulator.class);\n\t\tassertThat(ReflectionTestUtils.getField(this.configurer, \"ldapAuthoritiesPopulator\")).isNull();\n\t\tthis.configurer.contextSource(new DefaultSpringSecurityContextSource(\"ldap://localhost:389\"));\n\t\tthis.configurer.addObjectPostProcessor(new ObjectPostProcessor<LdapAuthoritiesPopulator>() {\n\t\t\t@Override\n\t\t\tpublic <O extends LdapAuthoritiesPopulator> O postProcess(O object) {\n\t\t\t\treturn (O) populator;\n\t\t\t}\n\t\t});\n\t\tReflectionTestUtils.invokeMethod(this.configurer, \"getLdapAuthoritiesPopulator\");\n\t\tassertThat(ReflectionTestUtils.getField(this.configurer, \"ldapAuthoritiesPopulator\")).isSameAs(populator);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authentication/configurers/provisioning/UserDetailsManagerConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authentication.configurers.provisioning;\n\nimport java.util.Arrays;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @author Adolfo Eloy\n */\npublic class UserDetailsManagerConfigurerTests {\n\n\tprivate InMemoryUserDetailsManager userDetailsManager;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.userDetailsManager = new InMemoryUserDetailsManager();\n\t}\n\n\t@Test\n\tpublic void allAttributesSupported() {\n\t\t// @formatter:off\n\t\tUserDetails userDetails = configurer()\n\t\t\t\t.withUser(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.disabled(true)\n\t\t\t\t.accountExpired(true)\n\t\t\t\t.accountLocked(true)\n\t\t\t\t.credentialsExpired(true)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(userDetails.getUsername()).isEqualTo(\"user\");\n\t\tassertThat(userDetails.getPassword()).isEqualTo(\"password\");\n\t\tassertThat(userDetails.getAuthorities().stream().findFirst().get().getAuthority()).isEqualTo(\"ROLE_USER\");\n\t\tassertThat(userDetails.isAccountNonExpired()).isFalse();\n\t\tassertThat(userDetails.isAccountNonLocked()).isFalse();\n\t\tassertThat(userDetails.isCredentialsNonExpired()).isFalse();\n\t\tassertThat(userDetails.isEnabled()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authoritiesWithGrantedAuthorityWorks() {\n\t\tSimpleGrantedAuthority authority = new SimpleGrantedAuthority(\"ROLE_USER\");\n\t\t// @formatter:off\n\t\tUserDetails userDetails = configurer()\n\t\t\t\t.withUser(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.authorities(authority)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat((Optional<GrantedAuthority>) userDetails.getAuthorities().stream().findFirst()).contains(authority);\n\t}\n\n\t@Test\n\tpublic void authoritiesWithStringAuthorityWorks() {\n\t\tString authority = \"ROLE_USER\";\n\t\t// @formatter:off\n\t\tUserDetails userDetails = configurer()\n\t\t\t\t.withUser(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.authorities(authority)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(userDetails.getAuthorities().stream().findFirst().get().getAuthority()).isEqualTo(authority);\n\t}\n\n\t@Test\n\tpublic void authoritiesWithAListOfGrantedAuthorityWorks() {\n\t\tSimpleGrantedAuthority authority = new SimpleGrantedAuthority(\"ROLE_USER\");\n\t\t// @formatter:off\n\t\tUserDetails userDetails = configurer()\n\t\t\t\t.withUser(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.authorities(Arrays.asList(authority))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat((Optional<GrantedAuthority>) userDetails.getAuthorities().stream().findFirst()).contains(authority);\n\t}\n\n\tprivate UserDetailsManagerConfigurer<AuthenticationManagerBuilder, InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>> configurer() {\n\t\treturn new UserDetailsManagerConfigurer<AuthenticationManagerBuilder, InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>>(\n\t\t\t\tthis.userDetailsManager);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authorization/EnableMultiFactorAuthenticationCustomizerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\n\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link EnableMultiFactorAuthentication} with\n * {@link Customizer}&lt;{@link AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder}&gt;.\n */\n@ExtendWith(SpringExtension.class)\n@WebAppConfiguration\n@ContextConfiguration(classes = EnableMultiFactorAuthenticationCustomizerTests.ConfigWithCustomizer.class)\nclass EnableMultiFactorAuthenticationCustomizerTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\t@WithMockUser(username = \"user\", authorities = \"ROLE_USER\")\n\tvoid whenCustomizerAppliedThenConditionalMfaUsed() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"admin\", authorities = \"ROLE_USER\")\n\tvoid whenCustomizerAppliedAndConditionTrueThenMfaRequired() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"admin\", authorities = { \"ROLE_USER\", FactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\tFactorGrantedAuthority.OTT_AUTHORITY })\n\tvoid whenCustomizerAppliedAndConditionTrueWithMfaThenAuthorized() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableMultiFactorAuthentication(\n\t\t\tauthorities = { FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY })\n\tstatic class ConfigWithCustomizer {\n\n\t\t@Bean\n\t\tCustomizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer() {\n\t\t\treturn (builder) -> builder.when((auth) -> \"admin\".equals(auth.getName()));\n\t\t}\n\n\t\t@Bean\n\t\tMockMvc mvc(WebApplicationContext context) {\n\t\t\treturn MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"deprecation\")\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t\tUserDetails admin = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"admin\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user, admin);\n\t\t}\n\n\t\t@RestController\n\t\tstatic class OkController {\n\n\t\t\t@GetMapping(\"/\")\n\t\t\tString ok() {\n\t\t\t\treturn \"ok\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authorization/EnableMultiFactorAuthenticationFiltersSetTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.authentication.AuthenticationFilter;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationConverter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link EnableMultiFactorAuthentication}.\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@WebAppConfiguration\n@WithMockUser(authorities = FactorGrantedAuthority.PASSWORD_AUTHORITY)\npublic class EnableMultiFactorAuthenticationFiltersSetTests {\n\n\t@Autowired\n\tprivate AuthenticationManager manager;\n\n\tprivate TestingAuthenticationToken newAuthn = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\",\n\t\t\tFactorGrantedAuthority.OTT_AUTHORITY);\n\n\t@Test\n\tvoid preAuthenticationFilter(@Autowired AbstractAuthenticationProcessingFilter filter) throws Exception {\n\t\tassertMfaEnabled(filter);\n\t}\n\n\t@Test\n\tvoid authenticationFilter(@Autowired AuthenticationFilter filter) throws Exception {\n\t\tassertMfaEnabled(filter);\n\t}\n\n\t@Test\n\tvoid preAuthnFilter(@Autowired AbstractPreAuthenticatedProcessingFilter filter) throws Exception {\n\t\tassertMfaEnabled(filter);\n\t}\n\n\t@Test\n\tvoid basicAuthnFilter(@Autowired BasicAuthenticationFilter filter) throws Exception {\n\t\tassertMfaEnabled(filter);\n\t}\n\n\tprivate void assertMfaEnabled(Filter filter) throws Exception {\n\t\tgiven(this.manager.authenticate(any())).willReturn(this.newAuthn);\n\t\tMockHttpServletRequest request = MockMvcRequestBuilders.get(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"u\", \"p\"))\n\t\t\t.buildRequest(new MockServletContext());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tfilter.doFilter(request, response, chain);\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getAuthorities()).extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactlyInAnyOrder(FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\t\t\"ROLE_USER\");\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableMultiFactorAuthentication(\n\t\t\tauthorities = { FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY })\n\tstatic class Config {\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager() {\n\t\t\treturn mock(AuthenticationManager.class);\n\t\t}\n\n\t\t@Bean\n\t\tstatic AbstractAuthenticationProcessingFilter authnProcessingFilter(\n\t\t\t\tAuthenticationManager authenticationManager) {\n\t\t\tAbstractAuthenticationProcessingFilter result = new AbstractAuthenticationProcessingFilter(\n\t\t\t\t\tAnyRequestMatcher.INSTANCE, authenticationManager) {\n\t\t\t};\n\t\t\tresult.setAuthenticationConverter(new BasicAuthenticationConverter());\n\t\t\treturn result;\n\t\t}\n\n\t\t@Bean\n\t\tstatic AuthenticationFilter authenticationFilter(AuthenticationManager authenticationManager) {\n\t\t\treturn new AuthenticationFilter(authenticationManager, new BasicAuthenticationConverter());\n\t\t}\n\n\t\t@Bean\n\t\tstatic AbstractPreAuthenticatedProcessingFilter preAuthenticatedProcessingFilter(\n\t\t\t\tAuthenticationManager authenticationManager) {\n\t\t\tAbstractPreAuthenticatedProcessingFilter result = new AbstractPreAuthenticatedProcessingFilter() {\n\t\t\t\t@Override\n\t\t\t\tprotected @Nullable Object getPreAuthenticatedCredentials(HttpServletRequest request) {\n\t\t\t\t\treturn \"password\";\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest request) {\n\t\t\t\t\treturn \"user\";\n\t\t\t\t}\n\t\t\t};\n\t\t\tresult.setRequiresAuthenticationRequestMatcher(AnyRequestMatcher.INSTANCE);\n\t\t\tresult.setAuthenticationManager(authenticationManager);\n\t\t\treturn result;\n\t\t}\n\n\t\t@Bean\n\t\tstatic BasicAuthenticationFilter basicAuthenticationFilter(AuthenticationManager authenticationManager) {\n\t\t\treturn new BasicAuthenticationFilter(authenticationManager);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authorization/EnableMultiFactorAuthenticationMultipleCustomizersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\n\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link EnableMultiFactorAuthentication} with multiple\n * {@link Customizer}&lt;{@link AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder}&gt;\n * beans.\n */\n@ExtendWith(SpringExtension.class)\n@WebAppConfiguration\n@ContextConfiguration(\n\t\tclasses = EnableMultiFactorAuthenticationMultipleCustomizersTests.ConfigWithMultipleCustomizers.class)\nclass EnableMultiFactorAuthenticationMultipleCustomizersTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\t@WithMockUser(username = \"user\", authorities = \"ROLE_USER\")\n\tvoid whenCustomizerAppliedThenConditionalMfaUsed() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"admin\", authorities = \"ROLE_USER\")\n\tvoid whenCustomizersAppliedAndConditionTrueThenMfaRequired() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"admin\", authorities = { \"ROLE_USER\", FactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\tFactorGrantedAuthority.OTT_AUTHORITY })\n\tvoid whenCustomizersAppliedAndConditionTrueWithMfaThenAuthorized() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"manager\", authorities = \"ROLE_USER\")\n\tvoid whenSecondCustomizerAppliedAndConditionTrueThenMfaRequired() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableMultiFactorAuthentication(\n\t\t\tauthorities = { FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY })\n\tstatic class ConfigWithMultipleCustomizers {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tCustomizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> adminCustomizer() {\n\t\t\treturn (builder) -> builder.withWhen(\n\t\t\t\t\t(current) -> (auth) -> \"admin\".equals(auth.getName()) || (current != null && current.test(auth)));\n\t\t}\n\n\t\t@Bean\n\t\t@Order(2)\n\t\tCustomizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> managerCustomizer() {\n\t\t\treturn (builder) -> builder.withWhen(\n\t\t\t\t\t(current) -> (auth) -> \"manager\".equals(auth.getName()) || (current != null && current.test(auth)));\n\t\t}\n\n\t\t@Bean\n\t\tMockMvc mvc(WebApplicationContext context) {\n\t\t\treturn MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"deprecation\")\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t\tUserDetails admin = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"admin\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t\tUserDetails manager = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"manager\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user, admin, manager);\n\t\t}\n\n\t\t@RestController\n\t\tstatic class OkController {\n\n\t\t\t@GetMapping(\"/\")\n\t\t\tString ok() {\n\t\t\t\treturn \"ok\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authorization/EnableMultiFactorAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link EnableMultiFactorAuthentication}.\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@WebAppConfiguration\npublic class EnableMultiFactorAuthenticationTests {\n\n\tprivate static final String ATTR_NAME = \"org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors$SecurityContextRequestPostProcessorSupport$TestSecurityContextRepository.REPO\";\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Autowired\n\tService service;\n\n\t@Test\n\t@WithMockUser(authorities = { \"ROLE_USER\", FactorGrantedAuthority.OTT_AUTHORITY })\n\tpublic void formLoginWhenAuthenticatedThenMergedAuthorities() throws Exception {\n\t\tthis.mvc.perform(formLogin())\n\t\t\t.andExpect(authenticated().withAuthorities(\"ROLE_USER\", FactorGrantedAuthority.OTT_AUTHORITY,\n\t\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY));\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY })\n\tvoid webWhenAuthorized() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid webWhenNotAuthorized() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY })\n\tvoid methodWhenAuthorized() throws Exception {\n\t\tAssertions.assertThatNoException().isThrownBy(() -> this.service.authenticated());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid methodWhenNotAuthorized() throws Exception {\n\t\tAssertions.assertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.service.authenticated());\n\t}\n\n\t@EnableWebSecurity\n\t@EnableMethodSecurity\n\t@Configuration\n\t@EnableMultiFactorAuthentication(\n\t\t\tauthorities = { FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY })\n\tstatic class Config {\n\n\t\t@Bean\n\t\tService service() {\n\t\t\treturn new Service();\n\t\t}\n\n\t\t@Bean\n\t\tMockMvc mvc(WebApplicationContext context) {\n\t\t\treturn MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();\n\t\t}\n\n\t\t@Bean\n\t\tstatic Customizer<HttpSecurity> captureAuthn() {\n\t\t\treturn (http) -> http.addFilterAfter(new Filter() {\n\t\t\t\t@Override\n\t\t\t\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,\n\t\t\t\t\t\tFilterChain filterChain) throws IOException, ServletException {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfilterChain.doFilter(servletRequest, servletResponse);\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\tservletRequest.setAttribute(ATTR_NAME, SecurityContextHolder.getContext());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, SecurityContextHolderFilter.class);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"deprecation\")\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@RestController\n\t\tstatic class OkController {\n\n\t\t\t@GetMapping(\"/\")\n\t\t\tString ok() {\n\t\t\t\treturn \"ok\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic class Service {\n\n\t\t@PreAuthorize(\"isAuthenticated()\")\n\t\tvoid authenticated() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/authorization/MultiFactorAuthenticationSelectorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.authorization;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link MultiFactorAuthenticationSelector}.\n *\n * @author Rob Winch\n */\nclass MultiFactorAuthenticationSelectorTests {\n\n\tprivate final MultiFactorAuthenticationSelector selector = new MultiFactorAuthenticationSelector();\n\n\t@Test\n\tvoid selectImportsWhenWhenIsEmptyAndAuthoritiesSpecifiedThenReturnsImportsWithoutWebAuthnConfig() {\n\t\tAnnotationMetadata metadata = metadata(new MultiFactorCondition[0], FactorGrantedAuthority.OTT_AUTHORITY,\n\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t\tString[] imports = this.selector.selectImports(metadata);\n\t\tassertThat(imports).isNotEmpty();\n\t\tassertThat(imports).doesNotContain(WhenWebAuthnRegisteredMfaConfiguration.class.getName());\n\t}\n\n\t@Test\n\tvoid selectImportsWhenWhenOmittedThenDefaultsToEmptyAndReturnsImports() {\n\t\tAnnotationMetadata metadata = metadataWithoutWhen(FactorGrantedAuthority.OTT_AUTHORITY,\n\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t\tString[] imports = this.selector.selectImports(metadata);\n\t\tassertThat(imports).isNotEmpty();\n\t\tassertThat(imports).doesNotContain(WhenWebAuthnRegisteredMfaConfiguration.class.getName());\n\t}\n\n\t@Test\n\tvoid selectImportsWhenHasWebAuthnConditionAndAuthoritiesIncludesWebAuthnThenReturnsImports() {\n\t\tAnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },\n\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\tFactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t\tString[] imports = this.selector.selectImports(metadata);\n\t\tassertThat(imports).isNotEmpty();\n\t}\n\n\t@Test\n\tvoid selectImportsWhenHasWebAuthnConditionAndAuthoritiesOnlyWebAuthnThenReturnsImports() {\n\t\tAnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },\n\t\t\t\tFactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t\tString[] imports = this.selector.selectImports(metadata);\n\t\tassertThat(imports).isNotEmpty();\n\t}\n\n\t@Test\n\tvoid selectImportsWhenHasWebAuthnConditionAndAuthoritiesEmptyThenThrowsException() {\n\t\tAnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED });\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.selector.selectImports(metadata))\n\t\t\t.withMessageContaining(\"authorities() must include \" + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid selectImportsWhenHasWebAuthnConditionAndAuthoritiesExcludesWebAuthnThenThrowsException() {\n\t\tAnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },\n\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.selector.selectImports(metadata))\n\t\t\t.withMessageContaining(\"authorities() must include \" + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t}\n\n\tprivate static AnnotationMetadata metadata(MultiFactorCondition[] when, String... authorities) {\n\t\tAnnotationMetadata metadata = mock(AnnotationMetadata.class);\n\t\tMap<String, Object> attrs = new HashMap<>();\n\t\tattrs.put(\"authorities\", authorities);\n\t\tattrs.put(\"when\", when);\n\t\tgiven(metadata.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName())).willReturn(attrs);\n\t\treturn metadata;\n\t}\n\n\tprivate static AnnotationMetadata metadataWithoutWhen(String... authorities) {\n\t\tAnnotationMetadata metadata = mock(AnnotationMetadata.class);\n\t\tMap<String, Object> attrs = new HashMap<>();\n\t\tattrs.put(\"authorities\", authorities);\n\t\tgiven(metadata.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName())).willReturn(attrs);\n\t\treturn metadata;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/configuration/AroundMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.configuration;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\n\npublic class AroundMethodInterceptor implements MethodInterceptor {\n\n\t@Override\n\tpublic Object invoke(MethodInvocation methodInvocation) throws Throwable {\n\t\treturn String.valueOf(methodInvocation.proceed());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/configuration/AutowireBeanFactoryObjectPostProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.configuration;\n\nimport java.lang.reflect.Modifier;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport org.springframework.aop.framework.ProxyFactory;\nimport org.springframework.beans.factory.BeanClassLoaderAware;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.SmartInitializingSingleton;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.context.EnvironmentAware;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.NativeDetector;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.web.context.ServletContextAware;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatException;\nimport static org.mockito.ArgumentMatchers.isNotNull;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class AutowireBeanFactoryObjectPostProcessorTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate ObjectPostProcessor<Object> objectObjectPostProcessor;\n\n\t@Test\n\tpublic void postProcessWhenApplicationContextAwareThenAwareInvoked() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tApplicationContextAware toPostProcess = mock(ApplicationContextAware.class);\n\t\tthis.objectObjectPostProcessor.postProcess(toPostProcess);\n\t\tverify(toPostProcess).setApplicationContext(isNotNull());\n\t}\n\n\t@Test\n\tpublic void postProcessWhenApplicationEventPublisherAwareThenAwareInvoked() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tApplicationEventPublisherAware toPostProcess = mock(ApplicationEventPublisherAware.class);\n\t\tthis.objectObjectPostProcessor.postProcess(toPostProcess);\n\t\tverify(toPostProcess).setApplicationEventPublisher(isNotNull());\n\t}\n\n\t@Test\n\tpublic void postProcessWhenBeanClassLoaderAwareThenAwareInvoked() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tBeanClassLoaderAware toPostProcess = mock(BeanClassLoaderAware.class);\n\t\tthis.objectObjectPostProcessor.postProcess(toPostProcess);\n\t\tverify(toPostProcess).setBeanClassLoader(isNotNull());\n\t}\n\n\t@Test\n\tpublic void postProcessWhenBeanFactoryAwareThenAwareInvoked() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tBeanFactoryAware toPostProcess = mock(BeanFactoryAware.class);\n\t\tthis.objectObjectPostProcessor.postProcess(toPostProcess);\n\t\tverify(toPostProcess).setBeanFactory(isNotNull());\n\t}\n\n\t@Test\n\tpublic void postProcessWhenEnvironmentAwareThenAwareInvoked() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tEnvironmentAware toPostProcess = mock(EnvironmentAware.class);\n\t\tthis.objectObjectPostProcessor.postProcess(toPostProcess);\n\t\tverify(toPostProcess).setEnvironment(isNotNull());\n\t}\n\n\t@Test\n\tpublic void postProcessWhenMessageSourceAwareThenAwareInvoked() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tMessageSourceAware toPostProcess = mock(MessageSourceAware.class);\n\t\tthis.objectObjectPostProcessor.postProcess(toPostProcess);\n\t\tverify(toPostProcess).setMessageSource(isNotNull());\n\t}\n\n\t@Test\n\tpublic void postProcessWhenServletContextAwareThenAwareInvoked() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tServletContextAware toPostProcess = mock(ServletContextAware.class);\n\t\tthis.objectObjectPostProcessor.postProcess(toPostProcess);\n\t\tverify(toPostProcess).setServletContext(isNotNull());\n\t}\n\n\t@Test\n\tpublic void postProcessWhenDisposableBeanThenAwareInvoked() throws Exception {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tDisposableBean toPostProcess = mock(DisposableBean.class);\n\t\tthis.objectObjectPostProcessor.postProcess(toPostProcess);\n\t\tthis.spring.getContext().close();\n\t\tverify(toPostProcess).destroy();\n\t}\n\n\t@Test\n\tpublic void postProcessWhenSmartInitializingSingletonThenAwareInvoked() {\n\t\tthis.spring.register(Config.class, SmartConfig.class).autowire();\n\t\tSmartConfig config = this.spring.getContext().getBean(SmartConfig.class);\n\t\tverify(config.toTest).afterSingletonsInstantiated();\n\t}\n\n\t@Test\n\t// SEC-2382\n\tpublic void autowireBeanFactoryWhenBeanNameAutoProxyCreatorThenWorks() {\n\t\tthis.spring.testConfigLocations(\"AutowireBeanFactoryObjectPostProcessorTests-aopconfig.xml\").autowire();\n\t\tMyAdvisedBean bean = this.spring.getContext().getBean(MyAdvisedBean.class);\n\t\tassertThat(bean.doStuff()).isEqualTo(\"null\");\n\t}\n\n\t@Test\n\tvoid postProcessWhenObjectIsCgLibProxyAndInNativeImageThenUseExistingBean() {\n\t\ttry (MockedStatic<NativeDetector> detector = Mockito.mockStatic(NativeDetector.class)) {\n\t\t\tgiven(NativeDetector.inNativeImage()).willReturn(true);\n\n\t\t\tProxyFactory proxyFactory = new ProxyFactory(new MyClass());\n\t\t\tproxyFactory.setProxyTargetClass(!Modifier.isFinal(MyClass.class.getModifiers()));\n\t\t\tMyClass myClass = (MyClass) proxyFactory.getProxy();\n\n\t\t\tthis.spring.register(Config.class, myClass.getClass()).autowire();\n\t\t\tthis.spring.getContext().getBean(myClass.getClass()).setIdentifier(\"0000\");\n\n\t\t\tMyClass postProcessed = this.objectObjectPostProcessor.postProcess(myClass);\n\t\t\tassertThat(postProcessed.getIdentifier()).isEqualTo(\"0000\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid postProcessWhenObjectIsCgLibProxyAndInNativeImageAndBeanDoesNotExistsThenIllegalStateException() {\n\t\ttry (MockedStatic<NativeDetector> detector = Mockito.mockStatic(NativeDetector.class)) {\n\t\t\tgiven(NativeDetector.inNativeImage()).willReturn(true);\n\n\t\t\tProxyFactory proxyFactory = new ProxyFactory(new MyClass());\n\t\t\tproxyFactory.setProxyTargetClass(!Modifier.isFinal(MyClass.class.getModifiers()));\n\t\t\tMyClass myClass = (MyClass) proxyFactory.getProxy();\n\n\t\t\tthis.spring.register(Config.class).autowire();\n\n\t\t\tassertThatException().isThrownBy(() -> this.objectObjectPostProcessor.postProcess(myClass))\n\t\t\t\t.havingRootCause()\n\t\t\t\t.isInstanceOf(IllegalStateException.class)\n\t\t\t\t.withMessage(\n\t\t\t\t\t\t\"\"\"\n\t\t\t\t\t\t\t\tFailed to resolve an unique bean (single or primary) of type [class org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessorTests$MyClass$$SpringCGLIB$$0] from the BeanFactory.\n\t\t\t\t\t\t\t\tBecause the object is a CGLIB Proxy, a raw bean cannot be initialized during runtime in a native image.\n\t\t\t\t\t\t\t\t\"\"\");\n\t\t}\n\t}\n\n\tstatic class MyClass {\n\n\t\tprivate String identifier = \"1234\";\n\n\t\tString getIdentifier() {\n\t\t\treturn this.identifier;\n\t\t}\n\n\t\tvoid setIdentifier(String identifier) {\n\t\t\tthis.identifier = identifier;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tObjectPostProcessor objectPostProcessor(AutowireCapableBeanFactory beanFactory) {\n\t\t\treturn new AutowireBeanFactoryObjectPostProcessor(beanFactory);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SmartConfig {\n\n\t\tSmartInitializingSingleton toTest = mock(SmartInitializingSingleton.class);\n\n\t\t@Autowired\n\t\tvoid configure(ObjectPostProcessor<Object> p) {\n\t\t\tp.postProcess(this.toTest);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class WithBeanNameAutoProxyCreatorConfig {\n\n\t\t@Bean\n\t\tObjectPostProcessor objectPostProcessor(AutowireCapableBeanFactory beanFactory) {\n\t\t\treturn new AutowireBeanFactoryObjectPostProcessor(beanFactory);\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configure(ObjectPostProcessor<Object> p) {\n\t\t\tp.postProcess(new Object());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/configuration/MyAdvisedBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.configuration;\n\npublic class MyAdvisedBean {\n\n\tpublic Object doStuff() {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/issue50/ApplicationConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.issue50;\n\nimport javax.sql.DataSource;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.jpa.repository.config.EnableJpaRepositories;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.orm.jpa.JpaTransactionManager;\nimport org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;\nimport org.springframework.orm.jpa.vendor.Database;\nimport org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;\nimport org.springframework.security.config.annotation.issue50.domain.User;\nimport org.springframework.transaction.PlatformTransactionManager;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n/**\n * @author Rob Winch\n *\n */\n@Configuration\n@EnableJpaRepositories(\"org.springframework.security.config.annotation.issue50.repo\")\n@EnableTransactionManagement\npublic class ApplicationConfig {\n\n\t@Bean\n\tpublic DataSource dataSource() {\n\t\tEmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();\n\t\treturn builder.setType(EmbeddedDatabaseType.HSQL).build();\n\t}\n\n\t@Bean\n\tpublic LocalContainerEntityManagerFactoryBean entityManagerFactory() {\n\t\tHibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();\n\t\tvendorAdapter.setDatabase(Database.HSQL);\n\t\tvendorAdapter.setGenerateDdl(true);\n\t\tvendorAdapter.setShowSql(true);\n\t\tLocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();\n\t\tfactory.setJpaVendorAdapter(vendorAdapter);\n\t\tfactory.setPackagesToScan(User.class.getPackage().getName());\n\t\tfactory.setDataSource(dataSource());\n\t\treturn factory;\n\t}\n\n\t@Bean\n\tpublic PlatformTransactionManager transactionManager() {\n\t\tJpaTransactionManager txManager = new JpaTransactionManager();\n\t\ttxManager.setEntityManagerFactory(entityManagerFactory().getObject());\n\t\treturn txManager;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/issue50/Issue50Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.issue50;\n\nimport jakarta.transaction.Transactional;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.annotation.issue50.domain.User;\nimport org.springframework.security.config.annotation.issue50.repo.UserRepository;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n *\n */\n@Transactional\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = { ApplicationConfig.class, SecurityConfig.class })\npublic class Issue50Tests {\n\n\t@Autowired\n\tprivate AuthenticationManager authenticationManager;\n\n\t@Autowired\n\tprivate UserRepository userRepo;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"test\", null, \"ROLE_ADMIN\"));\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\t// https://github.com/spring-projects/spring-security-javaconfig/issues/50\n\tpublic void loadWhenGlobalMethodSecurityConfigurationThenAuthenticationManagerLazy() {\n\t\t// no exception\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingUserThenUsernameNotFoundException() {\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> this.authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"test\", \"password\")));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidPasswordThenBadCredentialsException() {\n\t\tthis.userRepo.save(User.withUsernameAndPassword(\"test\", \"password\"));\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"test\", \"invalid\")));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidUserThenAuthenticates() {\n\t\tthis.userRepo.save(User.withUsernameAndPassword(\"test\", \"password\"));\n\t\tAuthentication result = this.authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"test\", \"password\"));\n\t\tassertThat(result.getName()).isEqualTo(\"test\");\n\t}\n\n\t@Test\n\tpublic void globalMethodSecurityIsEnabledWhenNotAllowedThenAccessDenied() {\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(\"test\", null, \"ROLE_USER\"));\n\t\tthis.userRepo.save(User.withUsernameAndPassword(\"denied\", \"password\"));\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"test\", \"password\")));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/issue50/SecurityConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.issue50;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.issue50.domain.User;\nimport org.springframework.security.config.annotation.issue50.repo.UserRepository;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.util.Assert;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * @author Rob Winch\n *\n */\n@Configuration\n@EnableWebSecurity\n@EnableGlobalMethodSecurity(prePostEnabled = true)\npublic class SecurityConfig {\n\n\t@Autowired\n\tprivate UserRepository myUserRepository;\n\n\t@Bean\n\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t.requestMatchers(pathPattern(\"/*\")).permitAll())\n\t\t\t.authenticationProvider(authenticationProvider());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tAuthenticationManager authenticationManager() {\n\t\treturn authenticationProvider()::authenticate;\n\t}\n\n\t@Bean\n\tpublic AuthenticationProvider authenticationProvider() {\n\t\tAssert.notNull(this.myUserRepository, \"myUserRepository cannot be null\");\n\t\treturn new AuthenticationProvider() {\n\t\t\t@Override\n\t\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\t\tObject principal = authentication.getPrincipal();\n\t\t\t\tString username = String.valueOf(principal);\n\t\t\t\tUser user = SecurityConfig.this.myUserRepository.findByUsername(username);\n\t\t\t\tif (user == null) {\n\t\t\t\t\tthrow new UsernameNotFoundException(\"No user for principal \" + principal);\n\t\t\t\t}\n\t\t\t\tif (!authentication.getCredentials().equals(user.getPassword())) {\n\t\t\t\t\tthrow new BadCredentialsException(\"Invalid password\");\n\t\t\t\t}\n\t\t\t\treturn new TestingAuthenticationToken(principal, null, \"ROLE_USER\");\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/issue50/domain/User.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.issue50.domain;\n\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.GeneratedValue;\nimport jakarta.persistence.GenerationType;\nimport jakarta.persistence.Id;\n\n/**\n * @author Rob Winch\n *\n */\n@Entity\npublic class User {\n\n\t@Id\n\t@GeneratedValue(strategy = GenerationType.AUTO)\n\tprivate Long id;\n\n\tprivate String username;\n\n\tprivate String password;\n\n\tpublic Long getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic void setId(Long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getUsername() {\n\t\treturn this.username;\n\t}\n\n\tpublic void setUsername(String username) {\n\t\tthis.username = username;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn this.password;\n\t}\n\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\n\tpublic static User withUsernameAndPassword(String username, String password) {\n\t\tUser user = new User();\n\t\tuser.setUsername(username);\n\t\tuser.setPassword(password);\n\t\treturn user;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/issue50/repo/UserRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.issue50.repo;\n\nimport org.springframework.data.repository.CrudRepository;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.config.annotation.issue50.domain.User;\n\n/**\n * @author Rob Winch\n *\n */\npublic interface UserRepository extends CrudRepository<User, String> {\n\n\t@PreAuthorize(\"hasRole('ROLE_ADMIN')\")\n\tUser findByUsername(String username);\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/AuthorizationProxyConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link PrePostMethodSecurityConfiguration}.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class AuthorizationProxyConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tAuthorizationProxyFactory proxyFactory;\n\n\t@WithMockUser\n\t@Test\n\tpublic void proxyWhenNotPreAuthorizedThenDenies() {\n\t\tthis.spring.register(DefaultsConfig.class).autowire();\n\t\tToaster toaster = this.proxyFactory.proxy(new Toaster());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(toaster::makeToast)\n\t\t\t.withMessage(\"Access Denied\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(toaster::extractBread)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void proxyWhenPreAuthorizedThenAllows() {\n\t\tthis.spring.register(DefaultsConfig.class).autowire();\n\t\tToaster toaster = this.proxyFactory.proxy(new Toaster());\n\t\ttoaster.makeToast();\n\t\tassertThat(toaster.extractBread()).isEqualTo(\"yummy\");\n\t}\n\n\t@Test\n\tpublic void proxyReactiveWhenNotPreAuthorizedThenDenies() {\n\t\tthis.spring.register(ReactiveDefaultsConfig.class).autowire();\n\t\tToaster toaster = this.proxyFactory.proxy(new Toaster());\n\t\tAuthentication user = TestAuthentication.authenticatedUser();\n\t\tStepVerifier\n\t\t\t.create(toaster.reactiveMakeToast().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t\tStepVerifier\n\t\t\t.create(toaster.reactiveExtractBread().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void proxyReactiveWhenPreAuthorizedThenAllows() {\n\t\tthis.spring.register(ReactiveDefaultsConfig.class).autowire();\n\t\tToaster toaster = this.proxyFactory.proxy(new Toaster());\n\t\tAuthentication admin = TestAuthentication.authenticatedAdmin();\n\t\tStepVerifier\n\t\t\t.create(toaster.reactiveMakeToast().contextWrite(ReactiveSecurityContextHolder.withAuthentication(admin)))\n\t\t\t.expectNext()\n\t\t\t.verifyComplete();\n\t}\n\n\t@EnableMethodSecurity\n\t@Configuration\n\tstatic class DefaultsConfig {\n\n\t}\n\n\t@EnableReactiveMethodSecurity\n\t@Configuration\n\tstatic class ReactiveDefaultsConfig {\n\n\t}\n\n\tstatic class Toaster {\n\n\t\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t\tvoid makeToast() {\n\n\t\t}\n\n\t\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t\tString extractBread() {\n\t\t\treturn \"yummy\";\n\t\t}\n\n\t\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t\tMono<Void> reactiveMakeToast() {\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t\tMono<String> reactiveExtractBread() {\n\t\t\treturn Mono.just(\"yummy\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/Authz.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 5.0\n */\n@Component\npublic class Authz {\n\n\tpublic boolean check(boolean result) {\n\t\treturn result;\n\t}\n\n\tpublic boolean check(long id) {\n\t\treturn id % 2 == 0;\n\t}\n\n\tpublic Mono<Boolean> checkReactive(long id) {\n\t\treturn Mono.defer(() -> Mono.just(id % 2 == 0));\n\t}\n\n\tpublic boolean check(Authentication authentication, String message) {\n\t\treturn message != null && message.contains(authentication.getName());\n\t}\n\n\tpublic AuthorizationResult checkResult(boolean result) {\n\t\treturn new AuthzResult(result);\n\t}\n\n\tpublic Mono<AuthorizationResult> checkReactiveResult(boolean result) {\n\t\treturn Mono.just(checkResult(result));\n\t}\n\n\tpublic AuthorizationManager<MethodInvocation> checkManager(long id) {\n\t\treturn (authentication, context) -> new AuthorizationDecision(check(id));\n\t}\n\n\tpublic ReactiveAuthorizationManager<MethodInvocation> checkReactiveManager(long id) {\n\t\treturn (authentication, context) -> checkReactive(id).map(AuthorizationDecision::new);\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tpublic static class AuthzResult extends AuthorizationDecision {\n\n\t\tpublic AuthzResult(boolean granted) {\n\t\t\tsuper(granted);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/DelegatingReactiveMessageService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\n\npublic class DelegatingReactiveMessageService implements ReactiveMessageService {\n\n\tprivate final ReactiveMessageService delegate;\n\n\tpublic DelegatingReactiveMessageService(ReactiveMessageService delegate) {\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\t@PreAuthorize(\"denyAll\")\n\tpublic String notPublisherPreAuthorizeFindById(long id) {\n\t\treturn this.delegate.notPublisherPreAuthorizeFindById(id);\n\t}\n\n\t@Override\n\tpublic Mono<String> monoFindById(long id) {\n\t\treturn this.delegate.monoFindById(id);\n\t}\n\n\t@Override\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tpublic Mono<String> monoPreAuthorizeHasRoleFindById(long id) {\n\t\treturn this.delegate.monoPreAuthorizeHasRoleFindById(id);\n\t}\n\n\t@Override\n\t@PostAuthorize(\"returnObject?.contains(authentication?.name)\")\n\tpublic Mono<String> monoPostAuthorizeFindById(long id) {\n\t\treturn this.delegate.monoPostAuthorizeFindById(id);\n\t}\n\n\t@Override\n\t@PreAuthorize(\"@authz.check(#id)\")\n\tpublic Mono<String> monoPreAuthorizeBeanFindById(long id) {\n\t\treturn this.delegate.monoPreAuthorizeBeanFindById(id);\n\t}\n\n\t@Override\n\t@PreAuthorize(\"@authz.checkReactive(#id)\")\n\tpublic Mono<String> monoPreAuthorizeBeanFindByIdReactiveExpression(long id) {\n\t\treturn this.delegate.monoPreAuthorizeBeanFindByIdReactiveExpression(id);\n\t}\n\n\t@Override\n\t@PostAuthorize(\"@authz.check(authentication, returnObject)\")\n\tpublic Mono<String> monoPostAuthorizeBeanFindById(long id) {\n\t\treturn this.delegate.monoPostAuthorizeBeanFindById(id);\n\t}\n\n\t@Override\n\tpublic Flux<String> fluxFindById(long id) {\n\t\treturn this.delegate.fluxFindById(id);\n\t}\n\n\t@Override\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tpublic Flux<String> fluxPreAuthorizeHasRoleFindById(long id) {\n\t\treturn this.delegate.fluxPreAuthorizeHasRoleFindById(id);\n\t}\n\n\t@Override\n\t@PostAuthorize(\"returnObject?.contains(authentication?.name)\")\n\tpublic Flux<String> fluxPostAuthorizeFindById(long id) {\n\t\treturn this.delegate.fluxPostAuthorizeFindById(id);\n\t}\n\n\t@Override\n\t@PreAuthorize(\"@authz.check(#id)\")\n\tpublic Flux<String> fluxPreAuthorizeBeanFindById(long id) {\n\t\treturn this.delegate.fluxPreAuthorizeBeanFindById(id);\n\t}\n\n\t@Override\n\t@PostAuthorize(\"@authz.check(authentication, returnObject)\")\n\tpublic Flux<String> fluxPostAuthorizeBeanFindById(long id) {\n\t\treturn this.delegate.fluxPostAuthorizeBeanFindById(id);\n\t}\n\n\t@PreFilter(\"filterObject.length > 3\")\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@PostFilter(\"filterObject.length > 5\")\n\t@PostAuthorize(\"returnObject == 'harold' or returnObject == 'jonathan'\")\n\t@Override\n\tpublic Flux<String> fluxManyAnnotations(Flux<String> flux) {\n\t\treturn flux;\n\t}\n\n\t@PostFilter(\"filterObject.length > 5\")\n\tpublic Flux<String> fluxPostFilter(Flux<String> flux) {\n\t\treturn flux;\n\t}\n\n\t@Override\n\tpublic Publisher<String> publisherFindById(long id) {\n\t\treturn this.delegate.publisherFindById(id);\n\t}\n\n\t@Override\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tpublic Publisher<String> publisherPreAuthorizeHasRoleFindById(long id) {\n\t\treturn this.delegate.publisherPreAuthorizeHasRoleFindById(id);\n\t}\n\n\t@Override\n\t@PostAuthorize(\"returnObject?.contains(authentication?.name)\")\n\tpublic Publisher<String> publisherPostAuthorizeFindById(long id) {\n\t\treturn this.delegate.publisherPostAuthorizeFindById(id);\n\t}\n\n\t@Override\n\t@PreAuthorize(\"@authz.check(#id)\")\n\tpublic Publisher<String> publisherPreAuthorizeBeanFindById(long id) {\n\t\treturn this.delegate.publisherPreAuthorizeBeanFindById(id);\n\t}\n\n\t@Override\n\t@PostAuthorize(\"@authz.check(authentication, returnObject)\")\n\tpublic Publisher<String> publisherPostAuthorizeBeanFindById(long id) {\n\t\treturn this.delegate.publisherPostAuthorizeBeanFindById(id);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/EnableAuthorizationManagerReactiveMethodSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.TestPublisher;\nimport reactor.util.context.Context;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\n\n/**\n * Tests for {@link EnableReactiveMethodSecurity} with the\n * {@link EnableReactiveMethodSecurity#useAuthorizationManager()} flag set to true.\n *\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class EnableAuthorizationManagerReactiveMethodSecurityTests {\n\n\t@Autowired\n\tReactiveMessageService messageService;\n\n\tReactiveMessageService delegate;\n\n\tTestPublisher<String> result = TestPublisher.create();\n\n\tContext withAdmin = ReactiveSecurityContextHolder\n\t\t.withAuthentication(new TestingAuthenticationToken(\"admin\", \"password\", \"ROLE_USER\", \"ROLE_ADMIN\"));\n\n\tContext withUser = ReactiveSecurityContextHolder\n\t\t.withAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\treset(this.delegate);\n\t}\n\n\t@Autowired\n\tpublic void setConfig(Config config) {\n\t\tthis.delegate = config.delegate;\n\t}\n\n\t@Test\n\tpublic void notPublisherPreAuthorizeFindByIdThenThrowsIllegalStateException() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> this.messageService.notPublisherPreAuthorizeFindById(1L))\n\t\t\t.withMessage(\"The returnType class java.lang.String on public abstract java.lang.String \"\n\t\t\t\t\t+ \"org.springframework.security.config.annotation.method.configuration.ReactiveMessageService\"\n\t\t\t\t\t+ \".notPublisherPreAuthorizeFindById(long) must return an instance of org.reactivestreams\"\n\t\t\t\t\t+ \".Publisher (for example, a Mono or Flux) or the function must be a Kotlin coroutine in order to support Reactor Context\");\n\t}\n\n\t@Test\n\tpublic void monoWhenPermitAllThenAopDoesNotSubscribe() {\n\t\tgiven(this.delegate.monoFindById(1L)).willReturn(Mono.from(this.result));\n\t\tthis.delegate.monoFindById(1L);\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoWhenPermitAllThenSuccess() {\n\t\tgiven(this.delegate.monoFindById(1L)).willReturn(Mono.just(\"success\"));\n\t\tStepVerifier.create(this.delegate.monoFindById(1L)).expectNext(\"success\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeHasRoleWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).willReturn(Mono.just(\"result\"));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L).contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindById(2L)).willReturn(Mono.just(\"result\"));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(2L).contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindById(2L)).willReturn(Mono.just(\"result\"));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindById(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindById(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanReactiveExpressionWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindByIdReactiveExpression(2L)).willReturn(Mono.just(\"result\"));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindByIdReactiveExpression(2L)\n\t\t\t.contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanReactiveExpressionWhenNotAuthenticatedAndGrantedThenSuccess() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindByIdReactiveExpression(2L)).willReturn(Mono.just(\"result\"));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindByIdReactiveExpression(2L);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanReactiveExpressionWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindByIdReactiveExpression(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindByIdReactiveExpression(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanReactiveExpressionWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindByIdReactiveExpression(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindByIdReactiveExpression(1L)\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.monoPostAuthorizeFindById(1L)).willReturn(Mono.just(\"user\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPostAuthorizeBeanFindById(1L)).willReturn(Mono.just(\"not-authorized\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenBeanAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.monoPostAuthorizeBeanFindById(2L)).willReturn(Mono.just(\"user\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(2L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.monoPostAuthorizeBeanFindById(2L)).willReturn(Mono.just(\"anonymous\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"anonymous\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPostAuthorizeBeanFindById(1L)).willReturn(Mono.just(\"not-authorized\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t// Flux tests\n\t@Test\n\tpublic void fluxWhenPermitAllThenAopDoesNotSubscribe() {\n\t\tgiven(this.delegate.fluxFindById(1L)).willReturn(Flux.from(this.result));\n\t\tthis.delegate.fluxFindById(1L);\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxWhenPermitAllThenSuccess() {\n\t\tgiven(this.delegate.fluxFindById(1L)).willReturn(Flux.just(\"success\"));\n\t\tStepVerifier.create(this.delegate.fluxFindById(1L)).expectNext(\"success\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeHasRoleWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).willReturn(Flux.just(\"result\"));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L).contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).consumeNextWith((s) -> assertThat(s).isEqualTo(\"result\")).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).willReturn(Flux.from(this.result));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).willReturn(Flux.from(this.result));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeBeanWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.fluxPreAuthorizeBeanFindById(2L)).willReturn(Flux.just(\"result\"));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(2L).contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() {\n\t\tgiven(this.delegate.fluxPreAuthorizeBeanFindById(2L)).willReturn(Flux.just(\"result\"));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeBeanWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.fluxPreAuthorizeBeanFindById(1L)).willReturn(Flux.from(this.result));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeBeanWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.fluxPreAuthorizeBeanFindById(1L)).willReturn(Flux.from(this.result));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.fluxPostAuthorizeFindById(1L)).willReturn(Flux.just(\"user\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.fluxPostAuthorizeBeanFindById(1L)).willReturn(Flux.just(\"not-authorized\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenBeanAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.fluxPostAuthorizeBeanFindById(2L)).willReturn(Flux.just(\"user\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(2L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.fluxPostAuthorizeBeanFindById(2L)).willReturn(Flux.just(\"anonymous\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"anonymous\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.fluxPostAuthorizeBeanFindById(1L)).willReturn(Flux.just(\"not-authorized\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void fluxManyAnnotationsWhenMeetsConditionsThenReturnsFilteredFlux() {\n\t\tFlux<String> flux = this.messageService.fluxManyAnnotations(Flux.just(\"harold\", \"jonathan\", \"pete\", \"bo\"))\n\t\t\t.contextWrite(this.withAdmin);\n\t\tStepVerifier.create(flux).expectNext(\"harold\", \"jonathan\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxManyAnnotationsWhenUserThenFails() {\n\t\tFlux<String> flux = this.messageService.fluxManyAnnotations(Flux.just(\"harold\", \"jonathan\", \"pete\", \"bo\"))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(flux).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void fluxManyAnnotationsWhenNameNotAllowedThenFails() {\n\t\tFlux<String> flux = this.messageService\n\t\t\t.fluxManyAnnotations(Flux.just(\"harold\", \"jonathan\", \"michael\", \"pete\", \"bo\"))\n\t\t\t.contextWrite(this.withAdmin);\n\t\tStepVerifier.create(flux).expectNext(\"harold\", \"jonathan\").expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void fluxPostFilterWhenFilteringThenWorks() {\n\t\tFlux<String> flux = this.messageService.fluxPostFilter(Flux.just(\"harold\", \"jonathan\", \"michael\", \"pete\", \"bo\"))\n\t\t\t.contextWrite(this.withAdmin);\n\t\tStepVerifier.create(flux).expectNext(\"harold\", \"jonathan\", \"michael\").verifyComplete();\n\t}\n\n\t// Publisher tests\n\t@Test\n\tpublic void publisherWhenPermitAllThenAopDoesNotSubscribe() {\n\t\tgiven(this.delegate.publisherFindById(1L)).willReturn(this.result);\n\t\tthis.delegate.publisherFindById(1L);\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherWhenPermitAllThenSuccess() {\n\t\tgiven(this.delegate.publisherFindById(1L)).willReturn(publisherJust(\"success\"));\n\t\tStepVerifier.create(this.delegate.publisherFindById(1L)).expectNext(\"success\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeHasRoleWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).willReturn(publisherJust(\"result\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeHasRoleFindById(1L))\n\t\t\t.contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).consumeNextWith((s) -> assertThat(s).isEqualTo(\"result\")).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).willReturn(this.result);\n\t\tPublisher<String> findById = this.messageService.publisherPreAuthorizeHasRoleFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).willReturn(this.result);\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeHasRoleFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeBeanWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.publisherPreAuthorizeBeanFindById(2L)).willReturn(publisherJust(\"result\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeBeanFindById(2L))\n\t\t\t.contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() {\n\t\tgiven(this.delegate.publisherPreAuthorizeBeanFindById(2L)).willReturn(publisherJust(\"result\"));\n\t\tPublisher<String> findById = this.messageService.publisherPreAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeBeanWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.publisherPreAuthorizeBeanFindById(1L)).willReturn(this.result);\n\t\tPublisher<String> findById = this.messageService.publisherPreAuthorizeBeanFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeBeanWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.publisherPreAuthorizeBeanFindById(1L)).willReturn(this.result);\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeBeanFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.publisherPostAuthorizeFindById(1L)).willReturn(publisherJust(\"user\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.publisherPostAuthorizeBeanFindById(1L)).willReturn(publisherJust(\"not-authorized\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenBeanAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.publisherPostAuthorizeBeanFindById(2L)).willReturn(publisherJust(\"user\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(2L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.publisherPostAuthorizeBeanFindById(2L)).willReturn(publisherJust(\"anonymous\"));\n\t\tPublisher<String> findById = this.messageService.publisherPostAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"anonymous\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.publisherPostAuthorizeBeanFindById(1L)).willReturn(publisherJust(\"not-authorized\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\tstatic <T> Publisher<T> publisher(Flux<T> flux) {\n\t\treturn (subscriber) -> flux.subscribe(subscriber);\n\t}\n\n\tstatic <T> Publisher<T> publisherJust(T... data) {\n\t\treturn publisher(Flux.just(data));\n\t}\n\n\t@EnableReactiveMethodSecurity(useAuthorizationManager = true)\n\t@Configuration\n\tstatic class Config {\n\n\t\tReactiveMessageService delegate = mock(ReactiveMessageService.class);\n\n\t\t@Bean\n\t\tDelegatingReactiveMessageService defaultMessageService() {\n\t\t\treturn new DelegatingReactiveMessageService(this.delegate);\n\t\t}\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/EnableCustomMethodSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.core.annotation.AliasFor;\n\n/**\n * @author Evgeniy Cheban\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@EnableMethodSecurity\npublic @interface EnableCustomMethodSecurity {\n\n\t@AliasFor(annotation = EnableMethodSecurity.class, attribute = \"proxyTargetClass\")\n\tboolean proxyTargetClass() default false;\n\n\t@AliasFor(annotation = EnableMethodSecurity.class, attribute = \"mode\")\n\tAdviceMode mode() default AdviceMode.PROXY;\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/EnableReactiveMethodSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.TestPublisher;\nimport reactor.util.context.Context;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class EnableReactiveMethodSecurityTests {\n\n\t@Autowired\n\tReactiveMessageService messageService;\n\n\tReactiveMessageService delegate;\n\n\tTestPublisher<String> result = TestPublisher.create();\n\n\tContext withAdmin = ReactiveSecurityContextHolder\n\t\t.withAuthentication(new TestingAuthenticationToken(\"admin\", \"password\", \"ROLE_USER\", \"ROLE_ADMIN\"));\n\n\tContext withUser = ReactiveSecurityContextHolder\n\t\t.withAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\treset(this.delegate);\n\t}\n\n\t@Autowired\n\tpublic void setConfig(Config config) {\n\t\tthis.delegate = config.delegate;\n\t}\n\n\t@Test\n\tpublic void notPublisherPreAuthorizeFindByIdThenThrowsIllegalStateException() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> this.messageService.notPublisherPreAuthorizeFindById(1L))\n\t\t\t.withMessage(\"The returnType class java.lang.String on public abstract java.lang.String \"\n\t\t\t\t\t+ \"org.springframework.security.config.annotation.method.configuration.ReactiveMessageService\"\n\t\t\t\t\t+ \".notPublisherPreAuthorizeFindById(long) must return an instance of org.reactivestreams\"\n\t\t\t\t\t+ \".Publisher (for example, a Mono or Flux) or the function must be a Kotlin coroutine in order to support Reactor Context\");\n\t}\n\n\t@Test\n\tpublic void monoWhenPermitAllThenAopDoesNotSubscribe() {\n\t\tgiven(this.delegate.monoFindById(1L)).willReturn(Mono.from(this.result));\n\t\tthis.delegate.monoFindById(1L);\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoWhenPermitAllThenSuccess() {\n\t\tgiven(this.delegate.monoFindById(1L)).willReturn(Mono.just(\"success\"));\n\t\tStepVerifier.create(this.delegate.monoFindById(1L)).expectNext(\"success\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeHasRoleWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).willReturn(Mono.just(\"result\"));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L).contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeHasRoleFindById(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeHasRoleFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindById(2L)).willReturn(Mono.just(\"result\"));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(2L).contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindById(2L)).willReturn(Mono.just(\"result\"));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindById(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPreAuthorizeBeanWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPreAuthorizeBeanFindById(1L)).willReturn(Mono.from(this.result));\n\t\tMono<String> findById = this.messageService.monoPreAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.monoPostAuthorizeFindById(1L)).willReturn(Mono.just(\"user\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPostAuthorizeBeanFindById(1L)).willReturn(Mono.just(\"not-authorized\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenBeanAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.monoPostAuthorizeBeanFindById(2L)).willReturn(Mono.just(\"user\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(2L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.monoPostAuthorizeBeanFindById(2L)).willReturn(Mono.just(\"anonymous\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"anonymous\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void monoPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.monoPostAuthorizeBeanFindById(1L)).willReturn(Mono.just(\"not-authorized\"));\n\t\tMono<String> findById = this.messageService.monoPostAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t// Flux tests\n\t@Test\n\tpublic void fluxWhenPermitAllThenAopDoesNotSubscribe() {\n\t\tgiven(this.delegate.fluxFindById(1L)).willReturn(Flux.from(this.result));\n\t\tthis.delegate.fluxFindById(1L);\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxWhenPermitAllThenSuccess() {\n\t\tgiven(this.delegate.fluxFindById(1L)).willReturn(Flux.just(\"success\"));\n\t\tStepVerifier.create(this.delegate.fluxFindById(1L)).expectNext(\"success\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeHasRoleWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).willReturn(Flux.just(\"result\"));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L).contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).consumeNextWith((s) -> assertThat(s).isEqualTo(\"result\")).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).willReturn(Flux.from(this.result));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.fluxPreAuthorizeHasRoleFindById(1L)).willReturn(Flux.from(this.result));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeHasRoleFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeBeanWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.fluxPreAuthorizeBeanFindById(2L)).willReturn(Flux.just(\"result\"));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(2L).contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() {\n\t\tgiven(this.delegate.fluxPreAuthorizeBeanFindById(2L)).willReturn(Flux.just(\"result\"));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeBeanWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.fluxPreAuthorizeBeanFindById(1L)).willReturn(Flux.from(this.result));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxPreAuthorizeBeanWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.fluxPreAuthorizeBeanFindById(1L)).willReturn(Flux.from(this.result));\n\t\tFlux<String> findById = this.messageService.fluxPreAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.fluxPostAuthorizeFindById(1L)).willReturn(Flux.just(\"user\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.fluxPostAuthorizeBeanFindById(1L)).willReturn(Flux.just(\"not-authorized\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenBeanAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.fluxPostAuthorizeBeanFindById(2L)).willReturn(Flux.just(\"user\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(2L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.fluxPostAuthorizeBeanFindById(2L)).willReturn(Flux.just(\"anonymous\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"anonymous\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void fluxPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.fluxPostAuthorizeBeanFindById(1L)).willReturn(Flux.just(\"not-authorized\"));\n\t\tFlux<String> findById = this.messageService.fluxPostAuthorizeBeanFindById(1L).contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t// Publisher tests\n\t@Test\n\tpublic void publisherWhenPermitAllThenAopDoesNotSubscribe() {\n\t\tgiven(this.delegate.publisherFindById(1L)).willReturn(this.result);\n\t\tthis.delegate.publisherFindById(1L);\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherWhenPermitAllThenSuccess() {\n\t\tgiven(this.delegate.publisherFindById(1L)).willReturn(publisherJust(\"success\"));\n\t\tStepVerifier.create(this.delegate.publisherFindById(1L)).expectNext(\"success\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeHasRoleWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).willReturn(publisherJust(\"result\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeHasRoleFindById(1L))\n\t\t\t.contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).consumeNextWith((s) -> assertThat(s).isEqualTo(\"result\")).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeHasRoleWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).willReturn(this.result);\n\t\tPublisher<String> findById = this.messageService.publisherPreAuthorizeHasRoleFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeHasRoleWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.publisherPreAuthorizeHasRoleFindById(1L)).willReturn(this.result);\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeHasRoleFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeBeanWhenGrantedThenSuccess() {\n\t\tgiven(this.delegate.publisherPreAuthorizeBeanFindById(2L)).willReturn(publisherJust(\"result\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeBeanFindById(2L))\n\t\t\t.contextWrite(this.withAdmin);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeBeanWhenNotAuthenticatedAndGrantedThenSuccess() {\n\t\tgiven(this.delegate.publisherPreAuthorizeBeanFindById(2L)).willReturn(publisherJust(\"result\"));\n\t\tPublisher<String> findById = this.messageService.publisherPreAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"result\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeBeanWhenNoAuthenticationThenDenied() {\n\t\tgiven(this.delegate.publisherPreAuthorizeBeanFindById(1L)).willReturn(this.result);\n\t\tPublisher<String> findById = this.messageService.publisherPreAuthorizeBeanFindById(1L);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherPreAuthorizeBeanWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.publisherPreAuthorizeBeanFindById(1L)).willReturn(this.result);\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPreAuthorizeBeanFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t\tthis.result.assertNoSubscribers();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.publisherPostAuthorizeFindById(1L)).willReturn(publisherJust(\"user\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.publisherPostAuthorizeBeanFindById(1L)).willReturn(publisherJust(\"not-authorized\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenBeanAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.publisherPostAuthorizeBeanFindById(2L)).willReturn(publisherJust(\"user\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(2L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectNext(\"user\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenBeanAndNotAuthenticatedAndAuthorizedThenSuccess() {\n\t\tgiven(this.delegate.publisherPostAuthorizeBeanFindById(2L)).willReturn(publisherJust(\"anonymous\"));\n\t\tPublisher<String> findById = this.messageService.publisherPostAuthorizeBeanFindById(2L);\n\t\tStepVerifier.create(findById).expectNext(\"anonymous\").verifyComplete();\n\t}\n\n\t@Test\n\tpublic void publisherPostAuthorizeWhenBeanAndNotAuthorizedThenDenied() {\n\t\tgiven(this.delegate.publisherPostAuthorizeBeanFindById(1L)).willReturn(publisherJust(\"not-authorized\"));\n\t\tPublisher<String> findById = Flux.from(this.messageService.publisherPostAuthorizeBeanFindById(1L))\n\t\t\t.contextWrite(this.withUser);\n\t\tStepVerifier.create(findById).expectError(AccessDeniedException.class).verify();\n\t}\n\n\tstatic <T> Publisher<T> publisher(Flux<T> flux) {\n\t\treturn (subscriber) -> flux.subscribe(subscriber);\n\t}\n\n\tstatic <T> Publisher<T> publisherJust(T... data) {\n\t\treturn publisher(Flux.just(data));\n\t}\n\n\t@Configuration\n\t@EnableReactiveMethodSecurity\n\tstatic class Config {\n\n\t\tReactiveMessageService delegate = mock(ReactiveMessageService.class);\n\n\t\t@Bean\n\t\tDelegatingReactiveMessageService defaultMessageService() {\n\t\t\treturn new DelegatingReactiveMessageService(this.delegate);\n\t\t}\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.reflect.Proxy;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.UnsatisfiedDependencyException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.event.AbstractAuthenticationEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;\nimport org.springframework.security.config.MockEventListener;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @author Artsiom Yudovin\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class GlobalMethodSecurityConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tprivate MethodSecurityService service;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\t@Autowired\n\tpublic void setMethodInterceptor(MethodSecurityInterceptor interceptor) {\n\t\tthis.authenticationManager = interceptor.getAuthenticationManager();\n\t}\n\n\t@Autowired(required = false)\n\tMockEventListener<AbstractAuthenticationEvent> events;\n\n\t@Test\n\tpublic void configureWhenGlobalMethodSecurityIsMissingMetadataSourceThenException() {\n\t\tassertThatExceptionOfType(UnsatisfiedDependencyException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(IllegalStateGlobalMethodSecurityConfig.class).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenGlobalMethodSecurityHasCustomMetadataSourceThenNoEnablingAttributeIsNeeded() {\n\t\tthis.spring.register(CustomMetadataSourceConfig.class).autowire();\n\t}\n\n\t@Test\n\tpublic void methodSecurityAuthenticationManagerPublishesEvent() {\n\t\tthis.spring.register(InMemoryAuthWithGlobalMethodSecurityConfig.class).autowire();\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(() -> this.authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"foo\", \"bar\")));\n\t\tassertThat(this.events.getEvents()).extracting(Object::getClass)\n\t\t\t.containsOnly((Class) AuthenticationFailureBadCredentialsEvent.class);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenAuthenticationTrustResolverIsBeanThenAutowires() {\n\t\tthis.spring.register(CustomTrustResolverConfig.class).autowire();\n\t\tAuthenticationTrustResolver trustResolver = this.spring.getContext().getBean(AuthenticationTrustResolver.class);\n\t\tgiven(trustResolver.isAnonymous(any())).willReturn(true, false);\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.service.preAuthorizeNotAnonymous());\n\t\tthis.service.preAuthorizeNotAnonymous();\n\t\tverify(trustResolver, atLeastOnce()).isAnonymous(any());\n\t}\n\n\t// SEC-2301\n\t@Test\n\t@WithMockUser\n\tpublic void defaultWebSecurityExpressionHandlerHasBeanResolverSet() {\n\t\tthis.spring.register(ExpressionHandlerHasBeanResolverSetConfig.class).autowire();\n\t\tAuthz authz = this.spring.getContext().getBean(Authz.class);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorizeBean(false));\n\t\tthis.service.preAuthorizeBean(true);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecuritySupportsAnnotaitonsOnInterfaceParamerNames() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.postAnnotation(\"deny\"));\n\t\tthis.service.postAnnotation(\"grant\");\n\t\t// no exception\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void globalMethodSecurityConfigurationAutowiresPermissionEvaluator() {\n\t\tthis.spring.register(AutowirePermissionEvaluatorConfig.class).autowire();\n\t\tPermissionEvaluator permission = this.spring.getContext().getBean(PermissionEvaluator.class);\n\t\tgiven(permission.hasPermission(any(), eq(\"something\"), eq(\"read\"))).willReturn(true, false);\n\t\tthis.service.hasPermission(\"something\");\n\t\t// no exception\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.service.hasPermission(\"something\"));\n\t}\n\n\t@Test\n\tpublic void multiPermissionEvaluatorConfig() {\n\t\tthis.spring.register(MultiPermissionEvaluatorConfig.class).autowire();\n\t\t// no exception\n\t}\n\n\t// SEC-2425\n\t@Test\n\t@WithMockUser\n\tpublic void enableGlobalMethodSecurityWorksOnSuperclass() {\n\t\tthis.spring.register(ChildConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t}\n\n\t// SEC-2479\n\t@Test\n\t@WithMockUser\n\tpublic void supportAuthenticationManagerInParent() {\n\t\ttry (AnnotationConfigWebApplicationContext parent = new AnnotationConfigWebApplicationContext()) {\n\t\t\tparent.register(Sec2479ParentConfig.class);\n\t\t\tparent.refresh();\n\t\t\ttry (AnnotationConfigWebApplicationContext child = new AnnotationConfigWebApplicationContext()) {\n\t\t\t\tchild.setParent(parent);\n\t\t\t\tchild.register(Sec2479ChildConfig.class);\n\t\t\t\tchild.refresh();\n\t\t\t\tthis.spring.context(child).autowire();\n\t\t\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void enableGlobalMethodSecurityDoesNotTriggerEagerInitializationOfBeansInGlobalAuthenticationConfigurer() {\n\t\tthis.spring.register(Sec2815Config.class).autowire();\n\t\tMockBeanPostProcessor pp = this.spring.getContext().getBean(MockBeanPostProcessor.class);\n\t\tassertThat(pp.beforeInit).containsKeys(\"dataSource\");\n\t\tassertThat(pp.afterInit).containsKeys(\"dataSource\");\n\t}\n\n\t// SEC-9845\n\t@Test\n\tpublic void enableGlobalMethodSecurityWhenBeanPostProcessorThenInvokedForDefaultMethodSecurityExpressionHandler() {\n\t\tthis.spring.register(Sec9845Config.class).autowire();\n\t\tMockBeanPostProcessor pp = this.spring.getContext().getBean(MockBeanPostProcessor.class);\n\t\tassertThat(pp.beforeInitClass).containsKeys(DefaultMethodSecurityExpressionHandler.class);\n\t\tassertThat(pp.afterInitClass).containsKeys(DefaultMethodSecurityExpressionHandler.class);\n\t}\n\n\t// SEC-3045\n\t@Test\n\tpublic void globalSecurityProxiesSecurity() {\n\t\tthis.spring.register(Sec3005Config.class).autowire();\n\t\tassertThat(this.service.getClass()).matches((c) -> !Proxy.isProxyClass(c), \"is not proxy class\");\n\t}\n\n\t//\n\t// // gh-3797\n\t// def preAuthorizeBeanSpel() {\n\t// setup:\n\t// SecurityContextHolder.getContext().setAuthentication(\n\t// new TestingAuthenticationToken(\"user\", \"password\",\"ROLE_USER\"))\n\t// context = new AnnotationConfigApplicationContext(PreAuthorizeBeanSpelConfig)\n\t// BeanSpelService service = context.getBean(BeanSpelService)\n\t// when:\n\t// service.run(true)\n\t// then:\n\t// noExceptionThrown()\n\t// when:\n\t// service.run(false)\n\t// then:\n\t// thrown(AccessDeniedException)\n\t// }\n\t//\n\t@Test\n\t@WithMockUser\n\tpublic void preAuthorizeBeanSpel() {\n\t\tthis.spring.register(PreAuthorizeBeanSpelConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorizeBean(false));\n\t\tthis.service.preAuthorizeBean(true);\n\t}\n\n\t// gh-3394\n\t@Test\n\t@WithMockUser\n\tpublic void roleHierarchy() {\n\t\tthis.spring.register(RoleHierarchyConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t\tthis.service.preAuthorizeAdmin();\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"ROLE:USER\")\n\tpublic void grantedAuthorityDefaultsAutowires() {\n\t\tthis.spring.register(CustomGrantedAuthorityConfig.class).autowire();\n\t\tCustomGrantedAuthorityConfig.CustomAuthorityService customService = this.spring.getContext()\n\t\t\t.getBean(CustomGrantedAuthorityConfig.CustomAuthorityService.class);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t\tcustomService.customPrefixRoleUser();\n\t\t// no exception\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"USER\")\n\tpublic void grantedAuthorityDefaultsWithEmptyRolePrefix() {\n\t\tthis.spring.register(EmptyRolePrefixGrantedAuthorityConfig.class).autowire();\n\t\tEmptyRolePrefixGrantedAuthorityConfig.CustomAuthorityService customService = this.spring.getContext()\n\t\t\t.getBean(EmptyRolePrefixGrantedAuthorityConfig.CustomAuthorityService.class);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.securedUser());\n\t\tcustomService.emptyPrefixRoleUser();\n\t\t// no exception\n\t}\n\n\t@Test\n\tpublic void methodSecurityInterceptorUsesMetadataSourceBeanWhenProxyingDisabled() {\n\t\tthis.spring.register(CustomMetadataSourceBeanProxyEnabledConfig.class).autowire();\n\t\tMethodSecurityInterceptor methodInterceptor = (MethodSecurityInterceptor) this.spring.getContext()\n\t\t\t.getBean(MethodInterceptor.class);\n\t\tMethodSecurityMetadataSource methodSecurityMetadataSource = this.spring.getContext()\n\t\t\t.getBean(MethodSecurityMetadataSource.class);\n\t\tassertThat(methodInterceptor.getSecurityMetadataSource()).isSameAs(methodSecurityMetadataSource);\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity\n\tpublic static class IllegalStateGlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity\n\tpublic static class CustomMetadataSourceConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Bean\n\t\t@Override\n\t\tprotected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {\n\t\t\treturn mock(MethodSecurityMetadataSource.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class InMemoryAuthWithGlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Override\n\t\tprotected void configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t.inMemoryAuthentication();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tpublic MockEventListener<AbstractAuthenticationEvent> listener() {\n\t\t\treturn new MockEventListener<AbstractAuthenticationEvent>() {\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class CustomTrustResolverConfig {\n\n\t\t@Bean\n\t\tAuthenticationTrustResolver trustResolver() {\n\t\t\treturn mock(AuthenticationTrustResolver.class);\n\t\t}\n\n\t\t@Bean\n\t\tMethodSecurityServiceImpl service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)\n\tstatic class ExpressionHandlerHasBeanResolverSetConfig {\n\n\t\t@Bean\n\t\tMethodSecurityServiceImpl service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class MethodSecurityServiceConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class AutowirePermissionEvaluatorConfig {\n\n\t\t@Bean\n\t\tPermissionEvaluator permissionEvaluator() {\n\t\t\treturn mock(PermissionEvaluator.class);\n\t\t}\n\n\t\t@Bean\n\t\tMethodSecurityService service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class MultiPermissionEvaluatorConfig {\n\n\t\t@Bean\n\t\tPermissionEvaluator permissionEvaluator() {\n\t\t\treturn mock(PermissionEvaluator.class);\n\t\t}\n\n\t\t@Bean\n\t\tPermissionEvaluator permissionEvaluator2() {\n\t\t\treturn mock(PermissionEvaluator.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ChildConfig extends ParentConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class ParentConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class Sec2479ParentConfig {\n\n\t\t@Bean\n\t\tAuthenticationManager am() {\n\t\t\treturn mock(AuthenticationManager.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class Sec2479ChildConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class Sec2815Config {\n\n\t\t@Bean\n\t\tMethodSecurityService service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Bean\n\t\tMockBeanPostProcessor mockBeanPostProcessor() {\n\t\t\treturn new MockBeanPostProcessor();\n\t\t}\n\n\t\t@Bean\n\t\tDataSource dataSource() {\n\t\t\treturn mock(DataSource.class);\n\t\t}\n\n\t\t@Configuration\n\t\tstatic class AuthConfig extends GlobalAuthenticationConfigurerAdapter {\n\n\t\t\t@Autowired\n\t\t\tDataSource dataSource;\n\n\t\t\t@Override\n\t\t\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\t\t\tauth.inMemoryAuthentication();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class Sec9845Config {\n\n\t\t@Bean\n\t\tBeanPostProcessor mockBeanPostProcessor() {\n\t\t\treturn new MockBeanPostProcessor();\n\t\t}\n\n\t}\n\n\tstatic class MockBeanPostProcessor implements BeanPostProcessor {\n\n\t\tMap<String, Object> beforeInit = new HashMap<>();\n\n\t\tMap<String, Object> afterInit = new HashMap<>();\n\n\t\tMap<Class<?>, Object> beforeInitClass = new HashMap<>();\n\n\t\tMap<Class<?>, Object> afterInitClass = new HashMap<>();\n\n\t\t@Override\n\t\tpublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tthis.beforeInit.put(beanName, bean);\n\t\t\tthis.beforeInitClass.put(bean.getClass(), bean);\n\t\t\treturn bean;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tthis.afterInit.put(beanName, bean);\n\t\t\tthis.afterInitClass.put(bean.getClass(), bean);\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ)\n\t@EnableTransactionManagement\n\tstatic class Sec3005Config {\n\n\t\t@Bean\n\t\tMethodSecurityService service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tauth.inMemoryAuthentication();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class PreAuthorizeBeanSpelConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class RoleHierarchyConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService service() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Bean\n\t\tRoleHierarchy roleHierarchy() {\n\t\t\treturn RoleHierarchyImpl.fromHierarchy(\"ROLE_USER > ROLE_ADMIN\");\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class CustomGrantedAuthorityConfig {\n\n\t\t@Bean\n\t\tGrantedAuthorityDefaults ga() {\n\t\t\treturn new GrantedAuthorityDefaults(\"ROLE:\");\n\t\t}\n\n\t\t@Bean\n\t\tCustomAuthorityService service() {\n\t\t\treturn new CustomAuthorityService();\n\t\t}\n\n\t\t@Bean\n\t\tMethodSecurityServiceImpl methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\tstatic class CustomAuthorityService {\n\n\t\t\t@PreAuthorize(\"hasRole('ROLE:USER')\")\n\t\t\tvoid customPrefixRoleUser() {\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(securedEnabled = true)\n\tstatic class EmptyRolePrefixGrantedAuthorityConfig {\n\n\t\t@Bean\n\t\tGrantedAuthorityDefaults ga() {\n\t\t\treturn new GrantedAuthorityDefaults(\"\");\n\t\t}\n\n\t\t@Bean\n\t\tCustomAuthorityService service() {\n\t\t\treturn new CustomAuthorityService();\n\t\t}\n\n\t\t@Bean\n\t\tMethodSecurityServiceImpl methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\tstatic class CustomAuthorityService {\n\n\t\t\t@Secured(\"USER\")\n\t\t\tvoid emptyPrefixRoleUser() {\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class CustomMetadataSourceBeanProxyEnabledConfig extends GlobalMethodSecurityConfiguration {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.List;\n\nimport jakarta.annotation.security.DenyAll;\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.security.authorization.method.HandleAuthorizationDenied;\nimport org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.parameters.P;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Rob Winch\n */\n@MethodSecurityService.Mask(\"classmask\")\npublic interface MethodSecurityService {\n\n\t@PreAuthorize(\"denyAll\")\n\tString preAuthorize();\n\n\t@Secured(\"ROLE_ADMIN\")\n\tString secured();\n\n\t@Secured(\"ROLE_USER\")\n\tString securedUser();\n\n\t@DenyAll\n\tString jsr250();\n\n\t@PermitAll\n\tString jsr250PermitAll();\n\n\t@RolesAllowed(\"ADMIN\")\n\tString jsr250RolesAllowed();\n\n\t@RolesAllowed(\"USER\")\n\tString jsr250RolesAllowedUser();\n\n\t@Secured({ \"ROLE_USER\", \"RUN_AS_SUPER\" })\n\tAuthentication runAs();\n\n\t@PreAuthorize(\"permitAll\")\n\tString preAuthorizePermitAll();\n\n\t@PreAuthorize(\"!anonymous\")\n\tvoid preAuthorizeNotAnonymous();\n\n\t@PreAuthorize(\"@authz.check(#result)\")\n\tvoid preAuthorizeBean(@P(\"result\") boolean result);\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tvoid preAuthorizeAdmin();\n\n\t@PreAuthorize(\"hasRole('USER')\")\n\tvoid preAuthorizeUser();\n\n\t@PreAuthorize(\"hasAllRoles('USER', 'ADMIN')\")\n\tvoid hasAllRolesUserAdmin();\n\n\t@PreAuthorize(\"hasAllAuthorities('ROLE_USER', 'ROLE_ADMIN')\")\n\tvoid hasAllAuthoritiesRoleUserRoleAdmin();\n\n\t@PreAuthorize(\"hasPermission(#object,'read')\")\n\tString hasPermission(String object);\n\n\t@PostAuthorize(\"hasPermission(#object,'read')\")\n\tString postHasPermission(String object);\n\n\t@PostAuthorize(\"#o?.contains('grant')\")\n\tString postAnnotation(@P(\"o\") String object);\n\n\t@PreFilter(\"filterObject == authentication.name\")\n\tList<String> preFilterByUsername(List<String> array);\n\n\t@PostFilter(\"filterObject == authentication.name\")\n\tList<String> postFilterByUsername(List<String> array);\n\n\t@PreFilter(\"filterObject.length > 3\")\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@Secured(\"ROLE_USER\")\n\t@PostFilter(\"filterObject.length > 5\")\n\t@PostAuthorize(\"returnObject.size == 2\")\n\tList<String> manyAnnotations(List<String> array);\n\n\t@PreFilter(\"filterObject != 'DropOnPreFilter'\")\n\t@PreAuthorize(\"#list.remove('DropOnPreAuthorize')\")\n\t@Secured(\"ROLE_SECURED\")\n\t@RolesAllowed(\"JSR250\")\n\t@PostAuthorize(\"#list.remove('DropOnPostAuthorize')\")\n\t@PostFilter(\"filterObject != 'DropOnPostFilter'\")\n\tList<String> allAnnotations(List<String> list);\n\n\t@RequireUserRole\n\t@RequireAdminRole\n\tvoid repeatedAnnotations();\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)\n\tString preAuthorizeGetCardNumberIfAdmin(String cardNumber);\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = StartMaskingHandlerChild.class)\n\tString preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber);\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)\n\tString preAuthorizeThrowAccessDeniedManually();\n\n\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = CardNumberMaskingPostProcessor.class)\n\tString postAuthorizeGetCardNumberIfAdmin(String cardNumber);\n\n\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = PostMaskingPostProcessor.class)\n\tString postAuthorizeThrowAccessDeniedManually();\n\n\t@PreAuthorize(\"denyAll()\")\n\t@Mask(\"methodmask\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)\n\tString preAuthorizeDeniedMethodWithMaskAnnotation();\n\n\t@PreAuthorize(\"denyAll()\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)\n\tString preAuthorizeDeniedMethodWithNoMaskAnnotation();\n\n\t@NullDenied(role = \"ADMIN\")\n\tString postAuthorizeDeniedWithNullDenied();\n\n\t@PostAuthorize(\"denyAll()\")\n\t@Mask(\"methodmask\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)\n\tString postAuthorizeDeniedMethodWithMaskAnnotation();\n\n\t@PostAuthorize(\"denyAll()\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)\n\tString postAuthorizeDeniedMethodWithNoMaskAnnotation();\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@Mask(expression = \"@myMasker.getMask()\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)\n\tString preAuthorizeWithMaskAnnotationUsingBean();\n\n\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t@Mask(expression = \"@myMasker.getMask(returnObject)\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)\n\tString postAuthorizeWithMaskAnnotationUsingBean();\n\n\t@AuthorizeReturnObject\n\tUserRecordWithEmailProtected getUserRecordWithEmailProtected();\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = UserFallbackDeniedHandler.class)\n\tUserRecordWithEmailProtected getUserWithFallbackWhenUnauthorized();\n\n\t@PreAuthorize(\"@authz.checkResult(#result)\")\n\t@PostAuthorize(\"@authz.checkResult(!#result)\")\n\t@HandleAuthorizationDenied(handlerClass = MethodAuthorizationDeniedHandler.class)\n\tString checkCustomResult(boolean result);\n\n\t@PreAuthorize(\"@authz.checkManager(#id)\")\n\tString checkCustomManager(long id);\n\n\tclass StarMaskingHandler implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {\n\t\t\treturn \"***\";\n\t\t}\n\n\t}\n\n\tclass StartMaskingHandlerChild extends StarMaskingHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {\n\t\t\treturn super.handleDeniedInvocation(methodInvocation, result) + \"-child\";\n\t\t}\n\n\t}\n\n\tclass MaskAnnotationHandler implements MethodAuthorizationDeniedHandler {\n\n\t\tMaskValueResolver maskValueResolver;\n\n\t\tMaskAnnotationHandler(ApplicationContext context) {\n\t\t\tthis.maskValueResolver = new MaskValueResolver(context);\n\t\t}\n\n\t\tpublic Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {\n\t\t\tMask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);\n\t\t\tif (mask == null) {\n\t\t\t\tmask = AnnotationUtils.getAnnotation(methodInvocation.getMethod().getDeclaringClass(), Mask.class);\n\t\t\t}\n\t\t\treturn this.maskValueResolver.resolveValue(mask, methodInvocation, null);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn handle(methodInvocation, authorizationResult);\n\t\t}\n\n\t}\n\n\tclass MaskAnnotationPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\tMaskValueResolver maskValueResolver;\n\n\t\tMaskAnnotationPostProcessor(ApplicationContext context) {\n\t\t\tthis.maskValueResolver = new MaskValueResolver(context);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation mi, AuthorizationResult authorizationResult) {\n\t\t\tMask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);\n\t\t\tif (mask == null) {\n\t\t\t\tmask = AnnotationUtils.getAnnotation(mi.getMethod().getDeclaringClass(), Mask.class);\n\t\t\t}\n\t\t\treturn this.maskValueResolver.resolveValue(mask, mi, null);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\tMethodInvocation mi = methodInvocationResult.getMethodInvocation();\n\t\t\tMask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);\n\t\t\tif (mask == null) {\n\t\t\t\tmask = AnnotationUtils.getAnnotation(mi.getMethod().getDeclaringClass(), Mask.class);\n\t\t\t}\n\t\t\treturn this.maskValueResolver.resolveValue(mask, mi, methodInvocationResult.getResult());\n\t\t}\n\n\t}\n\n\tclass MaskValueResolver {\n\n\t\tDefaultMethodSecurityExpressionHandler expressionHandler;\n\n\t\tMaskValueResolver(ApplicationContext context) {\n\t\t\tthis.expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\t\tthis.expressionHandler.setApplicationContext(context);\n\t\t}\n\n\t\tString resolveValue(Mask mask, MethodInvocation mi, Object returnObject) {\n\t\t\tif (StringUtils.hasText(mask.value())) {\n\t\t\t\treturn mask.value();\n\t\t\t}\n\t\t\tExpression expression = this.expressionHandler.getExpressionParser().parseExpression(mask.expression());\n\t\t\tEvaluationContext evaluationContext = this.expressionHandler\n\t\t\t\t.createEvaluationContext(() -> SecurityContextHolder.getContext().getAuthentication(), mi);\n\t\t\tif (returnObject != null) {\n\t\t\t\tthis.expressionHandler.setReturnObject(returnObject, evaluationContext);\n\t\t\t}\n\t\t\treturn expression.getValue(evaluationContext, String.class);\n\t\t}\n\n\t}\n\n\tclass PostMaskingPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn \"***\";\n\t\t}\n\n\t}\n\n\tclass CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\tstatic String MASK = \"****-****-****-\";\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn \"***\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocationResult(MethodInvocationResult contextObject, AuthorizationResult result) {\n\t\t\tString cardNumber = (String) contextObject.getResult();\n\t\t\treturn MASK + cardNumber.substring(cardNumber.length() - 4);\n\t\t}\n\n\t}\n\n\tclass NullPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@interface Mask {\n\n\t\tString value() default \"\";\n\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@PostAuthorize(\"hasRole('{role}')\")\n\t@HandleAuthorizationDenied(handlerClass = NullPostProcessor.class)\n\t@interface NullDenied {\n\n\t\tString role();\n\n\t}\n\n\tclass UserFallbackDeniedHandler implements MethodAuthorizationDeniedHandler {\n\n\t\tprivate static final UserRecordWithEmailProtected FALLBACK = new UserRecordWithEmailProtected(\"Protected\",\n\t\t\t\t\"Protected\");\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn FALLBACK;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.springframework.context.annotation.Bean;\n\n/**\n * @author Josh Cummings\n */\npublic class MethodSecurityServiceConfig {\n\n\t@Bean\n\tMethodSecurityService service() {\n\t\treturn new MethodSecurityServiceImpl();\n\t}\n\n\t@Bean\n\tReactiveMethodSecurityService reactiveService() {\n\t\treturn new ReactiveMethodSecurityServiceImpl();\n\t}\n\n\t@Bean\n\tAuthz authz() {\n\t\treturn new Authz();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.List;\n\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * @author Rob Winch\n */\npublic class MethodSecurityServiceImpl implements MethodSecurityService {\n\n\t@Override\n\tpublic String preAuthorize() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String secured() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String securedUser() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String jsr250() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String jsr250PermitAll() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String jsr250RolesAllowed() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String jsr250RolesAllowedUser() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Authentication runAs() {\n\t\treturn SecurityContextHolder.getContext().getAuthentication();\n\t}\n\n\t@Override\n\tpublic void preAuthorizeNotAnonymous() {\n\t}\n\n\t@Override\n\tpublic void preAuthorizeBean(boolean b) {\n\t}\n\n\t@Override\n\tpublic void preAuthorizeAdmin() {\n\t}\n\n\t@Override\n\tpublic void preAuthorizeUser() {\n\t}\n\n\t@Override\n\tpublic String preAuthorizePermitAll() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String hasPermission(String object) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String postHasPermission(String object) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String postAnnotation(String object) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<String> preFilterByUsername(List<String> array) {\n\t\treturn array;\n\t}\n\n\t@Override\n\tpublic List<String> postFilterByUsername(List<String> array) {\n\t\treturn array;\n\t}\n\n\t@Override\n\tpublic List<String> manyAnnotations(List<String> object) {\n\t\treturn object;\n\t}\n\n\t@Override\n\tpublic List<String> allAnnotations(List<String> list) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void repeatedAnnotations() {\n\t}\n\n\t@Override\n\tpublic String postAuthorizeGetCardNumberIfAdmin(String cardNumber) {\n\t\treturn cardNumber;\n\t}\n\n\t@Override\n\tpublic String preAuthorizeGetCardNumberIfAdmin(String cardNumber) {\n\t\treturn cardNumber;\n\t}\n\n\t@Override\n\tpublic String preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber) {\n\t\treturn cardNumber;\n\t}\n\n\t@Override\n\tpublic String preAuthorizeThrowAccessDeniedManually() {\n\t\tthrow new AuthorizationDeniedException(\"Access Denied\", new AuthorizationDecision(false));\n\t}\n\n\t@Override\n\tpublic String postAuthorizeThrowAccessDeniedManually() {\n\t\tthrow new AuthorizationDeniedException(\"Access Denied\", new AuthorizationDecision(false));\n\t}\n\n\t@Override\n\tpublic String preAuthorizeDeniedMethodWithMaskAnnotation() {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic String preAuthorizeDeniedMethodWithNoMaskAnnotation() {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic String postAuthorizeDeniedWithNullDenied() {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic String postAuthorizeDeniedMethodWithMaskAnnotation() {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic String postAuthorizeDeniedMethodWithNoMaskAnnotation() {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic String preAuthorizeWithMaskAnnotationUsingBean() {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic String postAuthorizeWithMaskAnnotationUsingBean() {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic UserRecordWithEmailProtected getUserRecordWithEmailProtected() {\n\t\treturn new UserRecordWithEmailProtected(\"username\", \"useremail@example.com\");\n\t}\n\n\t@Override\n\tpublic UserRecordWithEmailProtected getUserWithFallbackWhenUnauthorized() {\n\t\treturn new UserRecordWithEmailProtected(\"username\", \"useremail@example.com\");\n\t}\n\n\t@Override\n\tpublic String checkCustomResult(boolean result) {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic String checkCustomManager(long id) {\n\t\treturn \"ok\";\n\t}\n\n\t@Override\n\tpublic void hasAllRolesUserAdmin() {\n\t}\n\n\t@Override\n\tpublic void hasAllAuthoritiesRoleUserRoleAdmin() {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/MyMasker.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\npublic class MyMasker {\n\n\tpublic String getMask(String value) {\n\t\treturn value + \"-masked\";\n\t}\n\n\tpublic String getMask() {\n\t\treturn \"mask\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/NamespaceGlobalMethodSecurityExpressionHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.io.Serializable;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class NamespaceGlobalMethodSecurityExpressionHandlerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tprivate MethodSecurityService service;\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenUsingCustomPermissionEvaluatorThenPreAuthorizesAccordingly() {\n\t\tthis.spring.register(CustomAccessDecisionManagerConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThat(this.service.hasPermission(\"granted\")).isNull();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.hasPermission(\"denied\"));\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenUsingCustomPermissionEvaluatorThenPostAuthorizesAccordingly() {\n\t\tthis.spring.register(CustomAccessDecisionManagerConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThat(this.service.postHasPermission(\"granted\")).isNull();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.service.postHasPermission(\"denied\"));\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Override\n\t\tprotected MethodSecurityExpressionHandler createExpressionHandler() {\n\t\t\tDefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\t\texpressionHandler.setPermissionEvaluator(new PermissionEvaluator() {\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasPermission(Authentication authentication, Object targetDomainObject,\n\t\t\t\t\t\tObject permission) {\n\t\t\t\t\treturn \"granted\".equals(targetDomainObject);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\t\t\t\tObject permission) {\n\t\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn expressionHandler;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/NamespaceGlobalMethodSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.intercept.AfterInvocationManager;\nimport org.springframework.security.access.intercept.RunAsManager;\nimport org.springframework.security.access.intercept.RunAsManagerImpl;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;\nimport org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor;\nimport org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;\nimport org.springframework.security.access.method.MethodSecurityMetadataSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class NamespaceGlobalMethodSecurityTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tprivate MethodSecurityService service;\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenCustomAccessDecisionManagerThenAuthorizes() {\n\t\tthis.spring.register(CustomAccessDecisionManagerConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.secured());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenCustomAfterInvocationManagerThenAuthorizes() {\n\t\tthis.spring.register(CustomAfterInvocationManagerConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorizePermitAll());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenCustomAuthenticationManagerThenAuthorizes() {\n\t\tthis.spring.register(CustomAuthenticationConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> this.service.preAuthorize());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenJsr250EnabledThenAuthorizes() {\n\t\tthis.spring.register(Jsr250Config.class, MethodSecurityServiceConfig.class).autowire();\n\t\tthis.service.preAuthorize();\n\t\tthis.service.secured();\n\t\tthis.service.jsr250PermitAll();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.jsr250());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenCustomMethodSecurityMetadataSourceThenAuthorizes() {\n\t\tthis.spring.register(CustomMethodSecurityMetadataSourceConfig.class, MethodSecurityServiceConfig.class)\n\t\t\t.autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.secured());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.jsr250());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void contextRefreshWhenUsingAspectJThenAutowire() throws Exception {\n\t\tthis.spring.register(AspectJModeConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThat(this.spring.getContext()\n\t\t\t.getBean(Class\n\t\t\t\t.forName(\"org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect\")))\n\t\t\t.isNotNull();\n\t\tassertThat(this.spring.getContext().getBean(AspectJMethodSecurityInterceptor.class)).isNotNull();\n\t\t// TODO diagnose why aspectj isn't weaving method security advice around\n\t\t// MethodSecurityServiceImpl\n\t}\n\n\t@Test\n\tpublic void contextRefreshWhenUsingAspectJAndCustomGlobalMethodSecurityConfigurationThenAutowire()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AspectJModeExtendsGMSCConfig.class).autowire();\n\t\tassertThat(this.spring.getContext()\n\t\t\t.getBean(Class\n\t\t\t\t.forName(\"org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect\")))\n\t\t\t.isNotNull();\n\t\tassertThat(this.spring.getContext().getBean(AspectJMethodSecurityInterceptor.class)).isNotNull();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenOrderSpecifiedThenConfigured() {\n\t\tthis.spring.register(CustomOrderConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThat(this.spring.getContext()\n\t\t\t.getBean(\"metaDataSourceAdvisor\", MethodSecurityMetadataSourceAdvisor.class)\n\t\t\t.getOrder()).isEqualTo(-135);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.jsr250());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenOrderUnspecifiedThenConfiguredToLowestPrecedence() {\n\t\tthis.spring.register(DefaultOrderConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThat(this.spring.getContext()\n\t\t\t.getBean(\"metaDataSourceAdvisor\", MethodSecurityMetadataSourceAdvisor.class)\n\t\t\t.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE);\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> this.service.jsr250());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenOrderUnspecifiedAndCustomGlobalMethodSecurityConfigurationThenConfiguredToLowestPrecedence() {\n\t\tthis.spring.register(DefaultOrderExtendsMethodSecurityConfig.class, MethodSecurityServiceConfig.class)\n\t\t\t.autowire();\n\t\tassertThat(this.spring.getContext()\n\t\t\t.getBean(\"metaDataSourceAdvisor\", MethodSecurityMetadataSourceAdvisor.class)\n\t\t\t.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE);\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> this.service.jsr250());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenPrePostEnabledThenPreAuthorizes() {\n\t\tthis.spring.register(PreAuthorizeConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tthis.service.secured();\n\t\tthis.service.jsr250();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenPrePostEnabledAndCustomGlobalMethodSecurityConfigurationThenPreAuthorizes() {\n\t\tthis.spring.register(PreAuthorizeExtendsGMSCConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tthis.service.secured();\n\t\tthis.service.jsr250();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenProxyTargetClassThenDoesNotWireToInterface() {\n\t\tthis.spring.register(ProxyTargetClassConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\t// make sure service was actually proxied\n\t\tassertThat(this.service.getClass().getInterfaces()).doesNotContain(MethodSecurityService.class);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenDefaultProxyThenWiresToInterface() {\n\t\tthis.spring.register(DefaultProxyConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThat(this.service.getClass().getInterfaces()).contains(MethodSecurityService.class);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenCustomRunAsManagerThenRunAsWrapsAuthentication() {\n\t\tthis.spring.register(CustomRunAsManagerConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThat(this.service.runAs().getAuthorities())\n\t\t\t.anyMatch((authority) -> \"ROLE_RUN_AS_SUPER\".equals(authority.getAuthority()));\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenSecuredEnabledThenSecures() {\n\t\tthis.spring.register(SecuredConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.secured());\n\t\tthis.service.securedUser();\n\t\tthis.service.preAuthorize();\n\t\tthis.service.jsr250();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenMissingEnableAnnotationThenShowsHelpfulError() {\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t.isThrownBy(() -> this.spring.register(ExtendsNoEnableAnntotationConfig.class).autowire())\n\t\t\t.withStackTraceContaining(EnableGlobalMethodSecurity.class.getName() + \" is required\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void methodSecurityWhenImportingGlobalMethodSecurityConfigurationSubclassThenAuthorizes() {\n\t\tthis.spring.register(ImportSubclassGMSCConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tthis.service.secured();\n\t\tthis.service.jsr250();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.service.preAuthorize());\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)\n\tpublic static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Override\n\t\tprotected AccessDecisionManager accessDecisionManager() {\n\t\t\treturn new DenyAllAccessDecisionManager();\n\t\t}\n\n\t\tpublic static class DenyAllAccessDecisionManager implements AccessDecisionManager {\n\n\t\t\t@Override\n\t\t\tpublic void decide(Authentication authentication, Object object,\n\t\t\t\t\tCollection<ConfigAttribute> configAttributes) {\n\t\t\t\tthrow new AccessDeniedException(\"Always Denied\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean supports(ConfigAttribute attribute) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean supports(Class<?> clazz) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class CustomAfterInvocationManagerConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Override\n\t\tprotected AfterInvocationManager afterInvocationManager() {\n\t\t\treturn new AfterInvocationManagerStub();\n\t\t}\n\n\t\tpublic static class AfterInvocationManagerStub implements AfterInvocationManager {\n\n\t\t\t@Override\n\t\t\tpublic Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,\n\t\t\t\t\tObject returnedObject) throws AccessDeniedException {\n\t\t\t\tthrow new AccessDeniedException(\"custom AfterInvocationManager\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean supports(ConfigAttribute attribute) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean supports(Class<?> clazz) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class CustomAuthenticationConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Override\n\t\tpublic MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {\n\t\t\tMethodInterceptor interceptor = super.methodSecurityInterceptor(methodSecurityMetadataSource);\n\t\t\t((MethodSecurityInterceptor) interceptor).setAlwaysReauthenticate(true);\n\t\t\treturn interceptor;\n\t\t}\n\n\t\t@Override\n\t\tprotected AuthenticationManager authenticationManager() {\n\t\t\treturn (authentication) -> {\n\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(jsr250Enabled = true)\n\tpublic static class Jsr250Config {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity\n\tpublic static class CustomMethodSecurityMetadataSourceConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Override\n\t\tprotected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {\n\t\t\treturn new AbstractMethodSecurityMetadataSource() {\n\t\t\t\t@Override\n\t\t\t\tpublic Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {\n\t\t\t\t\t// require ROLE_NOBODY for any method on MethodSecurityService\n\t\t\t\t\t// interface\n\t\t\t\t\treturn MethodSecurityService.class.isAssignableFrom(targetClass)\n\t\t\t\t\t\t\t? Arrays.asList(new SecurityConfig(\"ROLE_NOBODY\")) : Collections.emptyList();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Collection<ConfigAttribute> getAllConfigAttributes() {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ, securedEnabled = true)\n\tpublic static class AspectJModeConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ, securedEnabled = true)\n\tpublic static class AspectJModeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {\n\n\t}\n\n\tprivate static class AdvisorOrderConfig implements ImportBeanDefinitionRegistrar {\n\n\t\t@Override\n\t\tpublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,\n\t\t\t\tBeanDefinitionRegistry registry) {\n\t\t\tBeanDefinitionBuilder advice = BeanDefinitionBuilder.rootBeanDefinition(ExceptingInterceptor.class);\n\t\t\tregistry.registerBeanDefinition(\"exceptingInterceptor\", advice.getBeanDefinition());\n\t\t\tBeanDefinitionBuilder advisor = BeanDefinitionBuilder\n\t\t\t\t.rootBeanDefinition(MethodSecurityMetadataSourceAdvisor.class);\n\t\t\tadvisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\t\t\tadvisor.addConstructorArgValue(\"exceptingInterceptor\");\n\t\t\tadvisor.addConstructorArgReference(\"methodSecurityMetadataSource\");\n\t\t\tadvisor.addConstructorArgValue(\"methodSecurityMetadataSource\");\n\t\t\tadvisor.addPropertyValue(\"order\", 0);\n\t\t\tregistry.registerBeanDefinition(\"exceptingAdvisor\", advisor.getBeanDefinition());\n\t\t}\n\n\t\tprivate static class ExceptingInterceptor implements MethodInterceptor {\n\n\t\t\t@Override\n\t\t\tpublic Object invoke(MethodInvocation invocation) {\n\t\t\t\tthrow new UnsupportedOperationException(\"Deny All\");\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(order = -135, jsr250Enabled = true)\n\t@Import(AdvisorOrderConfig.class)\n\tpublic static class CustomOrderConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(jsr250Enabled = true)\n\t@Import(AdvisorOrderConfig.class)\n\tpublic static class DefaultOrderConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(jsr250Enabled = true)\n\t@Import(AdvisorOrderConfig.class)\n\tpublic static class DefaultOrderExtendsMethodSecurityConfig extends GlobalMethodSecurityConfiguration {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class PreAuthorizeConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class PreAuthorizeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(proxyTargetClass = true, prePostEnabled = true)\n\tpublic static class ProxyTargetClassConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class DefaultProxyConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(securedEnabled = true)\n\tpublic static class CustomRunAsManagerConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Override\n\t\tprotected RunAsManager runAsManager() {\n\t\t\tRunAsManagerImpl runAsManager = new RunAsManagerImpl();\n\t\t\trunAsManager.setKey(\"some key\");\n\t\t\treturn runAsManager;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(securedEnabled = true)\n\tpublic static class SecuredConfig {\n\n\t}\n\n\t@Configuration\n\tpublic static class ExtendsNoEnableAnntotationConfig extends GlobalMethodSecurityConfiguration {\n\n\t}\n\n\t@Configuration\n\t@Import(PreAuthorizeExtendsGMSCConfig.class)\n\tpublic static class ImportSubclassGMSCConfig {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.io.Serializable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.ObservationTextPublisher;\nimport jakarta.annotation.security.DenyAll;\nimport jakarta.servlet.RequestDispatcher;\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nimport org.springframework.aop.Advisor;\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.config.AopConfigUtils;\nimport org.springframework.aop.support.DefaultPointcutAdvisor;\nimport org.springframework.aop.support.JdkRegexpMethodPointcut;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.annotation.AdviceMode;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.core.annotation.AnnotationAwareOrderComparator;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageImpl;\nimport org.springframework.data.domain.Slice;\nimport org.springframework.data.domain.SliceImpl;\nimport org.springframework.data.geo.Distance;\nimport org.springframework.data.geo.GeoPage;\nimport org.springframework.data.geo.GeoResult;\nimport org.springframework.data.geo.GeoResults;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.annotation.BusinessService;\nimport org.springframework.security.access.annotation.BusinessServiceImpl;\nimport org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl;\nimport org.springframework.security.access.annotation.Jsr250BusinessServiceImpl;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.SpringAuthorizationEventPublisher;\nimport org.springframework.security.authorization.event.AuthorizationDeniedEvent;\nimport org.springframework.security.authorization.method.AuthorizationAdvisor;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;\nimport org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.test.SpringTestParentApplicationContextExecutionListener;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.test.context.support.WithAnonymousUser;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.security.web.util.ThrowableAnalyzer;\nimport org.springframework.stereotype.Component;\nimport org.springframework.stereotype.Service;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.ConfigurableWebApplicationContext;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.hamcrest.Matchers.nullValue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.clearInvocations;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link PrePostMethodSecurityConfiguration}.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@ContextConfiguration(classes = SecurityContextChangedListenerConfig.class)\n@TestExecutionListeners(listeners = { WithSecurityContextTestExecutionListener.class,\n\t\tSpringTestParentApplicationContextExecutionListener.class })\npublic class PrePostMethodSecurityConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tMethodSecurityService methodSecurityService;\n\n\t@Autowired(required = false)\n\tBusinessService businessService;\n\n\t@Autowired(required = false)\n\tMockMvc mvc;\n\n\t@WithMockUser\n\t@Test\n\tpublic void customMethodSecurityPreAuthorizeAdminWhenRoleUserThenAccessDeniedException() {\n\t\tthis.spring.register(CustomMethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void customMethodSecurityPreAuthorizeAdminWhenRoleAdminThenPasses() {\n\t\tthis.spring.register(CustomMethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizeAdmin();\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void preAuthorizeWhenRoleAdminThenAccessDeniedException() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithAnonymousUser\n\t@Test\n\tpublic void preAuthorizePermitAllWhenRoleAnonymousThenPasses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tString result = this.methodSecurityService.preAuthorizePermitAll();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithAnonymousUser\n\t@Test\n\tpublic void preAuthorizeNotAnonymousWhenRoleAnonymousThenAccessDeniedException() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(this.methodSecurityService::preAuthorizeNotAnonymous)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void preAuthorizeNotAnonymousWhenRoleUserThenPasses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizeNotAnonymous();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void securedWhenRoleUserThenAccessDeniedException() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void securedWhenRoleAdminThenPasses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tString result = this.methodSecurityService.secured();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void securedUserWhenRoleAdminThenAccessDeniedException() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)\n\t\t\t.withMessage(\"Access Denied\");\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void securedUserWhenRoleUserThenPasses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tString result = this.methodSecurityService.securedUser();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void preAuthorizeAdminWhenRoleUserThenAccessDeniedException() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void preAuthorizeAdminWhenRoleAdminThenPasses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizeAdmin();\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void preAuthorizeAdminWhenSecurityContextHolderStrategyThenUses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizeAdmin();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t}\n\n\t@WithMockUser(roles = { \"ADMIN\", \"USER\" })\n\t@Test\n\tpublic void hasAllAuthoritiesRoleUserRoleAdminWhenGranted() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.hasAllAuthoritiesRoleUserRoleAdmin();\n\t}\n\n\t@WithMockUser(roles = { \"USER\" })\n\t@Test\n\tpublic void hasAllAuthoritiesRoleUserRoleAdminWhenMissingOneThenDenied() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(this.methodSecurityService::hasAllAuthoritiesRoleUserRoleAdmin);\n\t}\n\n\t@WithMockUser(roles = { \"OTHER\" })\n\t@Test\n\tpublic void hasAllAuthoritiesRoleUserRoleAdminWhenAllThenDenied() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(this.methodSecurityService::hasAllAuthoritiesRoleUserRoleAdmin);\n\t}\n\n\t@WithMockUser(roles = { \"ADMIN\", \"USER\" })\n\t@Test\n\tpublic void hasAllRolesRoleUserRoleAdminWhenGranted() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.hasAllRolesUserAdmin();\n\t}\n\n\t@WithMockUser(roles = { \"USER\" })\n\t@Test\n\tpublic void hasAllRolesRoleUserRoleAdminWhenMissingOneThenDenied() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(this.methodSecurityService::hasAllRolesUserAdmin);\n\t}\n\n\t@WithMockUser(roles = { \"OTHER\" })\n\t@Test\n\tpublic void hasAllRolesRoleUserRoleAdminWhenAllThenDenied() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(this.methodSecurityService::hasAllRolesUserAdmin);\n\t}\n\n\t@WithMockUser(authorities = \"PREFIX_ADMIN\")\n\t@Test\n\tpublic void preAuthorizeAdminWhenRoleAdminAndCustomPrefixThenPasses() {\n\t\tthis.spring.register(CustomGrantedAuthorityDefaultsConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizeAdmin();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() {\n\t\tthis.spring.register(CustomPermissionEvaluatorConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.postHasPermission(\"deny\"))\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postHasPermissionWhenParameterIsGrantThenPasses() {\n\t\tthis.spring.register(CustomPermissionEvaluatorConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tString result = this.methodSecurityService.postHasPermission(\"grant\");\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postAnnotationWhenParameterIsNotGrantThenAccessDeniedException() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.postAnnotation(\"deny\"))\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postAnnotationWhenParameterIsGrantThenPasses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tString result = this.methodSecurityService.postAnnotation(\"grant\");\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser(\"bob\")\n\t@Test\n\tpublic void methodReturningAListWhenPrePostFiltersConfiguredThenFiltersList() {\n\t\tthis.spring.register(BusinessServiceConfig.class).autowire();\n\t\tList<String> names = new ArrayList<>();\n\t\tnames.add(\"bob\");\n\t\tnames.add(\"joe\");\n\t\tnames.add(\"sam\");\n\t\tList<?> result = this.businessService.methodReturningAList(names);\n\t\tassertThat(result).hasSize(1);\n\t\tassertThat(result.get(0)).isEqualTo(\"bob\");\n\t}\n\n\t@WithMockUser(\"bob\")\n\t@Test\n\tpublic void methodReturningAnArrayWhenPostFilterConfiguredThenFiltersArray() {\n\t\tthis.spring.register(BusinessServiceConfig.class).autowire();\n\t\tList<String> names = new ArrayList<>();\n\t\tnames.add(\"bob\");\n\t\tnames.add(\"joe\");\n\t\tnames.add(\"sam\");\n\t\tObject[] result = this.businessService.methodReturningAnArray(names.toArray());\n\t\tassertThat(result).hasSize(1);\n\t\tassertThat(result[0]).isEqualTo(\"bob\");\n\t}\n\n\t@WithMockUser(\"bob\")\n\t@Test\n\tpublic void securedUserWhenCustomBeforeAdviceConfiguredAndNameBobThenPasses() {\n\t\tthis.spring.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceConfig.class)\n\t\t\t.autowire();\n\t\tString result = this.methodSecurityService.securedUser();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser(\"joe\")\n\t@Test\n\tpublic void securedUserWhenCustomBeforeAdviceConfiguredAndNameNotBobThenAccessDeniedException() {\n\t\tthis.spring.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceConfig.class)\n\t\t\t.autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser(\"bob\")\n\t@Test\n\tpublic void securedUserWhenCustomAfterAdviceConfiguredAndNameBobThenGranted() {\n\t\tthis.spring.register(CustomAuthorizationManagerAfterAdviceConfig.class, MethodSecurityServiceConfig.class)\n\t\t\t.autowire();\n\t\tString result = this.methodSecurityService.securedUser();\n\t\tassertThat(result).isEqualTo(\"granted\");\n\t}\n\n\t@WithMockUser(\"joe\")\n\t@Test\n\tpublic void securedUserWhenCustomAfterAdviceConfiguredAndNameNotBobThenAccessDeniedException() {\n\t\tthis.spring.register(CustomAuthorizationManagerAfterAdviceConfig.class, MethodSecurityServiceConfig.class)\n\t\t\t.autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)\n\t\t\t.withMessage(\"Access Denied for User 'joe'\");\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void jsr250WhenRoleAdminThenAccessDeniedException() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithAnonymousUser\n\t@Test\n\tpublic void jsr250PermitAllWhenRoleAnonymousThenPasses() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tString result = this.methodSecurityService.jsr250PermitAll();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void rolesAllowedUserWhenRoleAdminThenAccessDeniedException() {\n\t\tthis.spring.register(BusinessServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.businessService::rolesAllowedUser)\n\t\t\t.withMessage(\"Access Denied\");\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void rolesAllowedUserWhenRoleUserThenPasses() {\n\t\tthis.spring.register(BusinessServiceConfig.class).autowire();\n\t\tthis.businessService.rolesAllowedUser();\n\t}\n\n\t@WithMockUser(roles = { \"ADMIN\", \"USER\" })\n\t@Test\n\tpublic void manyAnnotationsWhenMeetsConditionsThenReturnsFilteredList() throws Exception {\n\t\tList<String> names = Arrays.asList(\"harold\", \"jonathan\", \"pete\", \"bo\");\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tList<String> filtered = this.methodSecurityService.manyAnnotations(new ArrayList<>(names));\n\t\tassertThat(filtered).hasSize(2);\n\t\tassertThat(filtered).containsExactly(\"harold\", \"jonathan\");\n\t}\n\n\t// gh-4003\n\t// gh-4103\n\t@WithMockUser\n\t@Test\n\tpublic void manyAnnotationsWhenUserThenFails() {\n\t\tList<String> names = Arrays.asList(\"harold\", \"jonathan\", \"pete\", \"bo\");\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void manyAnnotationsWhenShortListThenFails() {\n\t\tList<String> names = Arrays.asList(\"harold\", \"jonathan\", \"pete\");\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void manyAnnotationsWhenAdminThenFails() {\n\t\tList<String> names = Arrays.asList(\"harold\", \"jonathan\", \"pete\", \"bo\");\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));\n\t}\n\n\t// gh-3183\n\t@Test\n\tpublic void repeatedAnnotationsWhenPresentThenFails() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.repeatedAnnotations());\n\t}\n\n\t// gh-3183\n\t@Test\n\tpublic void repeatedJsr250AnnotationsWhenPresentThenFails() {\n\t\tthis.spring.register(Jsr250Config.class).autowire();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.businessService.repeatedAnnotations());\n\t}\n\n\t// gh-3183\n\t@Test\n\tpublic void repeatedSecuredAnnotationsWhenPresentThenFails() {\n\t\tthis.spring.register(SecuredConfig.class).autowire();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.businessService.repeatedAnnotations());\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void preAuthorizeWhenAuthorizationEventPublisherThenUses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class, AuthorizationEventPublisherConfig.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.preAuthorize());\n\t\tAuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);\n\t\tverify(publisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocation.class),\n\t\t\t\tany(AuthorizationDecision.class));\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postAuthorizeWhenAuthorizationEventPublisherThenUses() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class, AuthorizationEventPublisherConfig.class).autowire();\n\t\tthis.methodSecurityService.postAnnotation(\"grant\");\n\t\tAuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);\n\t\tverify(publisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocationResult.class),\n\t\t\t\tany(AuthorizationDecision.class));\n\t}\n\n\t// gh-10305\n\t@WithMockUser\n\t@Test\n\tpublic void beanInSpelWhenEvaluatedThenLooksUpBean() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizeBean(true);\n\t}\n\n\t@Test\n\tpublic void configureWhenAspectJThenRegistersAspects() {\n\t\tthis.spring.register(AspectJMethodSecurityServiceConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().containsBean(\"preFilterAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"postFilterAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"preAuthorizeAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"postAuthorizeAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"securedAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"annotationSecurityAspect$0\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void configureWhenBeanOverridingDisallowedThenWorks() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class, BusinessServiceConfig.class)\n\t\t\t.postProcessor(disallowBeanOverriding())\n\t\t\t.autowire();\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void methodSecurityAdminWhenRoleHierarchyBeanAvailableThenUses() {\n\t\tthis.spring.register(RoleHierarchyConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizeUser();\n\t\tthis.methodSecurityService.securedUser();\n\t\tthis.methodSecurityService.jsr250RolesAllowedUser();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void methodSecurityUserWhenRoleHierarchyBeanAvailableThenUses() {\n\t\tthis.spring.register(RoleHierarchyConfig.class, MethodSecurityServiceConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizeUser();\n\t\tthis.methodSecurityService.securedUser();\n\t\tthis.methodSecurityService.jsr250RolesAllowedUser();\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void methodSecurityAdminWhenAuthorizationEventPublisherBeanAvailableThenUses() {\n\t\tthis.spring\n\t\t\t.register(RoleHierarchyConfig.class, MethodSecurityServiceConfig.class,\n\t\t\t\t\tAuthorizationEventPublisherConfig.class)\n\t\t\t.autowire();\n\t\tthis.methodSecurityService.preAuthorizeUser();\n\t\tthis.methodSecurityService.securedUser();\n\t\tthis.methodSecurityService.jsr250RolesAllowedUser();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void methodSecurityUserWhenAuthorizationEventPublisherBeanAvailableThenUses() {\n\t\tthis.spring\n\t\t\t.register(RoleHierarchyConfig.class, MethodSecurityServiceConfig.class,\n\t\t\t\t\tAuthorizationEventPublisherConfig.class)\n\t\t\t.autowire();\n\t\tthis.methodSecurityService.preAuthorizeUser();\n\t\tthis.methodSecurityService.securedUser();\n\t\tthis.methodSecurityService.jsr250RolesAllowedUser();\n\t}\n\n\t@Test\n\tpublic void allAnnotationsWhenAdviceBeforeOffsetPreFilterThenReturnsFilteredList() {\n\t\tthis.spring.register(ReturnBeforeOffsetPreFilterConfig.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tList<String> filtered = this.methodSecurityService.allAnnotations(new ArrayList<>(list));\n\t\tassertThat(filtered).hasSize(5);\n\t\tassertThat(filtered).containsExactly(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t}\n\n\t@Test\n\tpublic void allAnnotationsWhenAdviceBeforeOffsetPreAuthorizeThenReturnsFilteredList() {\n\t\tthis.spring.register(ReturnBeforeOffsetPreAuthorizeConfig.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tList<String> filtered = this.methodSecurityService.allAnnotations(new ArrayList<>(list));\n\t\tassertThat(filtered).hasSize(4);\n\t\tassertThat(filtered).containsExactly(\"DropOnPreAuthorize\", \"DropOnPostAuthorize\", \"DropOnPostFilter\",\n\t\t\t\t\"DoNotDrop\");\n\t}\n\n\t@Test\n\tpublic void allAnnotationsWhenAdviceBeforeOffsetSecuredThenReturnsFilteredList() {\n\t\tthis.spring.register(ReturnBeforeOffsetSecuredConfig.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tList<String> filtered = this.methodSecurityService.allAnnotations(new ArrayList<>(list));\n\t\tassertThat(filtered).hasSize(3);\n\t\tassertThat(filtered).containsExactly(\"DropOnPostAuthorize\", \"DropOnPostFilter\", \"DoNotDrop\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void allAnnotationsWhenAdviceBeforeOffsetJsr250WithInsufficientRolesThenFails() {\n\t\tthis.spring.register(ReturnBeforeOffsetJsr250Config.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.allAnnotations(new ArrayList<>(list)));\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"SECURED\")\n\tpublic void allAnnotationsWhenAdviceBeforeOffsetJsr250ThenReturnsFilteredList() {\n\t\tthis.spring.register(ReturnBeforeOffsetJsr250Config.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tList<String> filtered = this.methodSecurityService.allAnnotations(new ArrayList<>(list));\n\t\tassertThat(filtered).hasSize(3);\n\t\tassertThat(filtered).containsExactly(\"DropOnPostAuthorize\", \"DropOnPostFilter\", \"DoNotDrop\");\n\t}\n\n\t@Test\n\t@WithMockUser(roles = { \"SECURED\" })\n\tpublic void allAnnotationsWhenAdviceBeforeOffsetPostAuthorizeWithInsufficientRolesThenFails() {\n\t\tthis.spring.register(ReturnBeforeOffsetPostAuthorizeConfig.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.allAnnotations(new ArrayList<>(list)));\n\t}\n\n\t@Test\n\t@WithMockUser(roles = { \"SECURED\", \"JSR250\" })\n\tpublic void allAnnotationsWhenAdviceBeforeOffsetPostAuthorizeThenReturnsFilteredList() {\n\t\tthis.spring.register(ReturnBeforeOffsetPostAuthorizeConfig.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tList<String> filtered = this.methodSecurityService.allAnnotations(new ArrayList<>(list));\n\t\tassertThat(filtered).hasSize(3);\n\t\tassertThat(filtered).containsExactly(\"DropOnPostAuthorize\", \"DropOnPostFilter\", \"DoNotDrop\");\n\t}\n\n\t@Test\n\t@WithMockUser(roles = { \"SECURED\", \"JSR250\" })\n\tpublic void allAnnotationsWhenAdviceBeforeOffsetPostFilterThenReturnsFilteredList() {\n\t\tthis.spring.register(ReturnBeforeOffsetPostFilterConfig.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tList<String> filtered = this.methodSecurityService.allAnnotations(new ArrayList<>(list));\n\t\tassertThat(filtered).hasSize(2);\n\t\tassertThat(filtered).containsExactly(\"DropOnPostFilter\", \"DoNotDrop\");\n\t}\n\n\t@Test\n\t@WithMockUser(roles = { \"SECURED\", \"JSR250\" })\n\tpublic void allAnnotationsWhenAdviceAfterAllOffsetThenReturnsFilteredList() {\n\t\tthis.spring.register(ReturnAfterAllOffsetConfig.class).autowire();\n\t\tList<String> list = Arrays.asList(\"DropOnPreFilter\", \"DropOnPreAuthorize\", \"DropOnPostAuthorize\",\n\t\t\t\t\"DropOnPostFilter\", \"DoNotDrop\");\n\t\tList<String> filtered = this.methodSecurityService.allAnnotations(new ArrayList<>(list));\n\t\tassertThat(filtered).hasSize(1);\n\t\tassertThat(filtered).containsExactly(\"DoNotDrop\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodeWhenParameterizedPreAuthorizeMetaAnnotationThenPasses(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.hasRole(\"USER\")).isTrue();\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodRoleWhenPreAuthorizeMetaAnnotationHardcodedParameterThenPasses(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.hasUserRole()).isTrue();\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\tpublic void methodWhenParameterizedAnnotationThenFails(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(service::placeholdersOnlyResolvedByMetaAnnotations);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser(authorities = \"SCOPE_message:read\")\n\tpublic void methodWhenMultiplePlaceholdersHasAuthorityThenPasses(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.readMessage()).isEqualTo(\"message\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic void methodWhenMultiplePlaceholdersHasRoleThenPasses(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.readMessage()).isEqualTo(\"message\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodWhenPostAuthorizeMetaAnnotationThenAuthorizes(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tservice.startsWithDave(\"daveMatthews\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> service.startsWithDave(\"jenniferHarper\"));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodWhenPreFilterMetaAnnotationThenFilters(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.parametersContainDave(new ArrayList<>(List.of(\"dave\", \"carla\", \"vanessa\", \"paul\"))))\n\t\t\t.containsExactly(\"dave\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodWhenPostFilterMetaAnnotationThenFilters(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.resultsContainDave(new ArrayList<>(List.of(\"dave\", \"carla\", \"vanessa\", \"paul\"))))\n\t\t\t.containsExactly(\"dave\");\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findByIdWhenAuthorizedResultThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tFlight flight = flights.findById(\"1\");\n\t\tassertThatNoException().isThrownBy(flight::getAltitude);\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findByIdWhenUnauthorizedResultThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tFlight flight = flights.findById(\"1\");\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude);\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findGeoResultByIdWhenAuthorizedResultThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tGeoResult<Flight> geoResultFlight = flights.findGeoResultFlightById(\"1\");\n\t\tFlight flight = geoResultFlight.getContent();\n\t\tassertThatNoException().isThrownBy(flight::getAltitude);\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findGeoResultByIdWhenUnauthorizedResultThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tGeoResult<Flight> geoResultFlight = flights.findGeoResultFlightById(\"1\");\n\t\tFlight flight = geoResultFlight.getContent();\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude);\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findByIdWhenAuthorizedResponseEntityThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tFlight flight = flights.webFindById(\"1\").getBody();\n\t\tassertThatNoException().isThrownBy(flight::getAltitude);\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\tassertThat(flights.webFindById(\"5\").getBody()).isNull();\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findByIdWhenUnauthorizedResponseEntityThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tFlight flight = flights.webFindById(\"1\").getBody();\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude);\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findByIdWhenAuthorizedModelAndViewThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tFlight flight = (Flight) flights.webViewFindById(\"1\").getModel().get(\"flight\");\n\t\tassertThatNoException().isThrownBy(flight::getAltitude);\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\tassertThat(flights.webViewFindById(\"5\").getModel().get(\"flight\")).isNull();\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findByIdWhenUnauthorizedModelAndViewThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tFlight flight = (Flight) flights.webViewFindById(\"1\").getModel().get(\"flight\");\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude);\n\t\tassertThat(flights.webViewFindById(\"5\").getModel().get(\"flight\")).isNull();\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findAllWhenUnauthorizedResultThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findAll().forEachRemaining((flight) -> {\n\t\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizedResultThenRemoves() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.remove(\"1\");\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findAllWhenPostFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findAll()\n\t\t\t.forEachRemaining((flight) -> assertThat(flight.getPassengers()).extracting(Passenger::getName)\n\t\t\t\t.doesNotContain(\"Kevin Mitnick\"));\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findPageWhenPostFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findPage()\n\t\t\t.forEach((flight) -> assertThat(flight.getPassengers()).extracting(Passenger::getName)\n\t\t\t\t.doesNotContain(\"Kevin Mitnick\"));\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findSliceWhenPostFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findSlice()\n\t\t\t.forEach((flight) -> assertThat(flight.getPassengers()).extracting(Passenger::getName)\n\t\t\t\t.doesNotContain(\"Kevin Mitnick\"));\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findGeoPageWhenPostFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findGeoPage()\n\t\t\t.forEach((flight) -> assertThat(flight.getContent().getPassengers()).extracting(Passenger::getName)\n\t\t\t\t.doesNotContain(\"Kevin Mitnick\"));\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findGeoResultsWhenPostFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findGeoResults()\n\t\t\t.forEach((flight) -> assertThat(flight.getContent().getPassengers()).extracting(Passenger::getName)\n\t\t\t\t.doesNotContain(\"Kevin Mitnick\"));\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findAllWhenPreFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findAll().forEachRemaining((flight) -> {\n\t\t\tflight.board(new ArrayList<>(List.of(\"John\")));\n\t\t\tassertThat(flight.getPassengers()).extracting(Passenger::getName).doesNotContain(\"John\");\n\t\t\tflight.board(new ArrayList<>(List.of(\"John Doe\")));\n\t\t\tassertThat(flight.getPassengers()).extracting(Passenger::getName).contains(\"John Doe\");\n\t\t});\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findAllWhenNestedPreAuthorizeThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findAll().forEachRemaining((flight) -> {\n\t\t\tList<Passenger> passengers = flight.getPassengers();\n\t\t\tpassengers.forEach((passenger) -> assertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t\t.isThrownBy(passenger::getName));\n\t\t});\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getCardNumberWhenPostAuthorizeAndNotAdminThenReturnMasked() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tMethodSecurityService.CardNumberMaskingPostProcessor.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString cardNumber = service.postAuthorizeGetCardNumberIfAdmin(\"4444-3333-2222-1111\");\n\t\tassertThat(cardNumber).isEqualTo(\"****-****-****-1111\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getCardNumberWhenPreAuthorizeAndNotAdminThenReturnMasked() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.StarMaskingHandler.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString cardNumber = service.preAuthorizeGetCardNumberIfAdmin(\"4444-3333-2222-1111\");\n\t\tassertThat(cardNumber).isEqualTo(\"***\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getCardNumberWhenPreAuthorizeAndNotAdminAndChildHandlerThenResolveCorrectHandlerAndReturnMasked() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.StarMaskingHandler.class,\n\t\t\t\t\tMethodSecurityService.StartMaskingHandlerChild.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString cardNumber = service.preAuthorizeWithHandlerChildGetCardNumberIfAdmin(\"4444-3333-2222-1111\");\n\t\tassertThat(cardNumber).isEqualTo(\"***-child\");\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tvoid preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenHandled() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.StarMaskingHandler.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tassertThat(service.preAuthorizeThrowAccessDeniedManually()).isEqualTo(\"***\");\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tvoid postAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPostAuthorizeThenHandled() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.PostMaskingPostProcessor.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tassertThat(service.postAuthorizeThrowAccessDeniedManually()).isEqualTo(\"***\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.MaskAnnotationHandler.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString result = service.preAuthorizeDeniedMethodWithMaskAnnotation();\n\t\tassertThat(result).isEqualTo(\"methodmask\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationInClassThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.MaskAnnotationHandler.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString result = service.preAuthorizeDeniedMethodWithNoMaskAnnotation();\n\t\tassertThat(result).isEqualTo(\"classmask\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid postAuthorizeWhenDeniedAndHandlerWithCustomAnnotationThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.MaskAnnotationPostProcessor.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString result = service.postAuthorizeDeniedMethodWithMaskAnnotation();\n\t\tassertThat(result).isEqualTo(\"methodmask\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid postAuthorizeWhenDeniedAndHandlerWithCustomAnnotationInClassThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.MaskAnnotationPostProcessor.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString result = service.postAuthorizeDeniedMethodWithNoMaskAnnotation();\n\t\tassertThat(result).isEqualTo(\"classmask\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid postAuthorizeWhenDeniedAndHandlerWithCustomAnnotationUsingBeanThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.MaskAnnotationPostProcessor.class,\n\t\t\t\t\tMyMasker.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString result = service.postAuthorizeWithMaskAnnotationUsingBean();\n\t\tassertThat(result).isEqualTo(\"ok-masked\");\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tvoid postAuthorizeWhenAllowedAndHandlerWithCustomAnnotationUsingBeanThenInvokeMethodNormally() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.MaskAnnotationPostProcessor.class,\n\t\t\t\t\tMyMasker.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString result = service.postAuthorizeWithMaskAnnotationUsingBean();\n\t\tassertThat(result).isEqualTo(\"ok\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationUsingBeanThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.MaskAnnotationHandler.class,\n\t\t\t\t\tMyMasker.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString result = service.preAuthorizeWithMaskAnnotationUsingBean();\n\t\tassertThat(result).isEqualTo(\"mask\");\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tvoid preAuthorizeWhenAllowedAndHandlerWithCustomAnnotationUsingBeanThenInvokeMethodNormally() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.MaskAnnotationHandler.class,\n\t\t\t\t\tMyMasker.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tString result = service.preAuthorizeWithMaskAnnotationUsingBean();\n\t\tassertThat(result).isEqualTo(\"ok\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getUserWhenAuthorizedAndUserEmailIsProtectedAndNotAuthorizedThenReturnEmailMasked() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tUserRecordWithEmailProtected.EmailMaskingPostProcessor.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tUserRecordWithEmailProtected user = service.getUserRecordWithEmailProtected();\n\t\tassertThat(user.email()).isEqualTo(\"use******@example.com\");\n\t\tassertThat(user.name()).isEqualTo(\"username\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getUserWhenNotAuthorizedAndHandlerFallbackValueThenReturnFallbackValue() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.UserFallbackDeniedHandler.class)\n\t\t\t.autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tUserRecordWithEmailProtected user = service.getUserWithFallbackWhenUnauthorized();\n\t\tassertThat(user.email()).isEqualTo(\"Protected\");\n\t\tassertThat(user.name()).isEqualTo(\"Protected\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getUserWhenNotAuthorizedThenHandlerUsesCustomAuthorizationDecision() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class, CustomResultConfig.class).autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tMethodAuthorizationDeniedHandler handler = this.spring.getContext()\n\t\t\t.getBean(MethodAuthorizationDeniedHandler.class);\n\t\tassertThat(service.checkCustomResult(false)).isNull();\n\t\tverify(handler).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));\n\t\tverify(handler, never()).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));\n\t\tclearInvocations(handler);\n\t\tassertThat(service.checkCustomResult(true)).isNull();\n\t\tverify(handler).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));\n\t\tverify(handler, never()).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));\n\t}\n\n\t// gh-15352\n\t@Test\n\tvoid annotationsInChildClassesDoNotAffectSuperclasses() {\n\t\tthis.spring.register(AbstractClassConfig.class).autowire();\n\t\tthis.spring.getContext().getBean(ClassInheritingAbstractClassWithNoAnnotations.class).method();\n\t}\n\n\t// gh-15592\n\t@Test\n\tvoid autowireWhenDefaultsThenCreatesExactlyOneAdvisorPerAnnotation() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = this.spring.getContext()\n\t\t\t.getBean(AuthorizationAdvisorProxyFactory.class);\n\t\tassertThat(proxyFactory).hasSize(5);\n\t\tassertThat(this.spring.getContext().getBeanNamesForType(AuthorizationAdvisor.class)).hasSize(5)\n\t\t\t.containsExactlyInAnyOrder(\"preFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\t\"preAuthorizeAuthorizationMethodInterceptor\", \"postAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\t\"postFilterAuthorizationMethodInterceptor\", \"authorizeReturnObjectMethodInterceptor\");\n\t}\n\n\t// gh-15592\n\t@Test\n\tvoid autowireWhenAspectJAutoProxyAndFactoryBeanThenExactlyOneAdvisorPerAnnotation() {\n\t\tthis.spring.register(AspectJAwareAutoProxyAndFactoryBeansConfig.class).autowire();\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = this.spring.getContext()\n\t\t\t.getBean(AuthorizationAdvisorProxyFactory.class);\n\t\tassertThat(proxyFactory).hasSize(5);\n\t\tassertThat(this.spring.getContext().getBeanNamesForType(AuthorizationAdvisor.class)).hasSize(5)\n\t\t\t.containsExactlyInAnyOrder(\"preFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\t\"preAuthorizeAuthorizationMethodInterceptor\", \"postAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\t\"postFilterAuthorizationMethodInterceptor\", \"authorizeReturnObjectMethodInterceptor\");\n\t}\n\n\t// gh-15651\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic void adviseWhenPrePostEnabledThenEachInterceptorRunsExactlyOnce() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class, CustomMethodSecurityExpressionHandlerConfig.class)\n\t\t\t.autowire();\n\t\tMethodSecurityExpressionHandler expressionHandler = this.spring.getContext()\n\t\t\t.getBean(MethodSecurityExpressionHandler.class);\n\t\tthis.methodSecurityService.manyAnnotations(new ArrayList<>(Arrays.asList(\"harold\", \"jonathan\", \"tim\", \"bo\")));\n\t\tverify(expressionHandler, times(4)).createEvaluationContext(any(Supplier.class), any());\n\t}\n\n\t// gh-15721\n\t@Test\n\t@WithMockUser(roles = \"uid\")\n\tpublic void methodWhenMetaAnnotationPropertiesHasClassProperties() {\n\t\tthis.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.getIdPath(\"uid\")).isEqualTo(\"uid\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void prePostMethodWhenObservationRegistryThenObserved() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class).autowire();\n\t\tthis.methodSecurityService.preAuthorizePermitAll();\n\t\tObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tverify(handler).onStart(any());\n\t\tverify(handler).onStop(any());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize);\n\t\tverify(handler).onError(any());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void securedMethodWhenObservationRegistryThenObserved() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class).autowire();\n\t\tthis.methodSecurityService.securedUser();\n\t\tObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tverify(handler).onStart(any());\n\t\tverify(handler).onStop(any());\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured);\n\t\tverify(handler).onError(any());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void jsr250MethodWhenObservationRegistryThenObserved() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class).autowire();\n\t\tthis.methodSecurityService.jsr250RolesAllowedUser();\n\t\tObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tverify(handler).onStart(any());\n\t\tverify(handler).onStop(any());\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(this.methodSecurityService::jsr250RolesAllowed);\n\t\tverify(handler).onError(any());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void prePostMethodWhenExcludeAuthorizationObservationsThenUnobserved() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class,\n\t\t\t\t\tSelectableObservationsConfig.class)\n\t\t\t.autowire();\n\t\tthis.methodSecurityService.preAuthorizePermitAll();\n\t\tObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize);\n\t\tverifyNoInteractions(handler);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void securedMethodWhenExcludeAuthorizationObservationsThenUnobserved() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class,\n\t\t\t\t\tSelectableObservationsConfig.class)\n\t\t\t.autowire();\n\t\tthis.methodSecurityService.securedUser();\n\t\tObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tverifyNoInteractions(handler);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void jsr250MethodWhenExcludeAuthorizationObservationsThenUnobserved() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class,\n\t\t\t\t\tSelectableObservationsConfig.class)\n\t\t\t.autowire();\n\t\tthis.methodSecurityService.jsr250RolesAllowedUser();\n\t\tObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tverifyNoInteractions(handler);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void preAuthorizeWhenDenyAllThenPublishesParameterizedAuthorizationDeniedEvent() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceConfig.class, EventPublisherConfig.class, AuthorizationDeniedListener.class)\n\t\t\t.autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.preAuthorize());\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationDeniedListener.class).invocations).isEqualTo(1);\n\t}\n\n\t// gh-16819\n\t@Test\n\tvoid autowireWhenDefaultsThenAdvisorAnnotationsAreSorted() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = this.spring.getContext()\n\t\t\t.getBean(AuthorizationAdvisorProxyFactory.class);\n\t\tAnnotationAwareOrderComparator comparator = AnnotationAwareOrderComparator.INSTANCE;\n\t\tAuthorizationAdvisor previous = null;\n\t\tfor (AuthorizationAdvisor advisor : proxyFactory) {\n\t\t\tboolean ordered = previous == null || comparator.compare(previous, advisor) < 0;\n\t\t\tassertThat(ordered).isTrue();\n\t\t\tprevious = advisor;\n\t\t}\n\t}\n\n\t@Test\n\tvoid getWhenPostAuthorizeAuthenticationNameMatchesThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/authorized-person\")\n\t\t\t\t.param(\"name\", \"rob\")\n\t\t\t\t.with(user(\"rob\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tvoid getWhenPostAuthorizeAuthenticationNameNotMatchThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/authorized-person\")\n\t\t\t\t.param(\"name\", \"john\")\n\t\t\t\t.with(user(\"rob\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t// gh-17761\n\t@Test\n\tvoid getWhenPostAuthorizeAuthenticationNameNotMatchThenNoExceptionExposedInRequest() throws Exception {\n\t\tthis.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/authorized-person\")\n\t\t\t\t.param(\"name\", \"john\")\n\t\t\t\t.with(user(\"rob\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser)\n\t\t\t.andExpect(request().attribute(RequestDispatcher.ERROR_EXCEPTION, nullValue()));\n\t}\n\n\t@Test\n\tvoid getWhenPostAuthorizeWithinServiceAuthenticationNameMatchesThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/greetings/authorized-person\")\n\t\t\t\t.param(\"name\", \"rob\")\n\t\t\t\t.with(user(\"rob\"));\n\t\t// @formatter:on\n\t\tMvcResult mvcResult = this.mvc.perform(requestWithUser).andExpect(status().isOk()).andReturn();\n\t\tassertThat(mvcResult.getResponse().getContentAsString()).isEqualTo(\"Hello: rob\");\n\t}\n\n\t@Test\n\tvoid getWhenPostAuthorizeWithinServiceAuthenticationNameNotMatchThenCustomHandlerRespondsWithForbidden()\n\t\t\tthrows Exception {\n\t\tthis.spring\n\t\t\t.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class,\n\t\t\t\t\tBasicControllerAdvice.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/greetings/authorized-person\")\n\t\t\t\t.param(\"name\", \"john\")\n\t\t\t\t.with(user(\"rob\"));\n\t\t// @formatter:on\n\t\tMvcResult mvcResult = this.mvc.perform(requestWithUser).andExpect(status().isForbidden()).andReturn();\n\t\tassertThat(mvcResult.getResponse().getContentAsString()).isEqualTo(\"\"\"\n\t\t\t\t{\"message\":\"Access Denied\"}\\\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid getWhenPostAuthorizeAuthenticationNameNotMatchThenCustomHandlerRespondsWithForbidden() throws Exception {\n\t\tthis.spring\n\t\t\t.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class,\n\t\t\t\t\tBasicControllerAdvice.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/authorized-person\")\n\t\t\t\t.param(\"name\", \"john\")\n\t\t\t\t.with(user(\"rob\"));\n\t\t// @formatter:on\n\t\tMvcResult mvcResult = this.mvc.perform(requestWithUser).andExpect(status().isForbidden()).andReturn();\n\t\tassertThat(mvcResult.getResponse().getContentAsString()).isEqualTo(\"\"\"\n\t\t\t\t{\"message\":\"Could not write JSON: Access Denied\"}\\\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid getWhenCustomAdvisorAuthenticationNameMatchesThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(WebMvcMethodSecurityCustomAdvisorConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/authorized-person\")\n\t\t\t\t.param(\"name\", \"rob\")\n\t\t\t\t.with(user(\"rob\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tvoid getWhenCustomAdvisorAuthenticationNameNotMatchThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(WebMvcMethodSecurityCustomAdvisorConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/authorized-person\")\n\t\t\t\t.param(\"name\", \"john\")\n\t\t\t\t.with(user(\"rob\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tvoid checkCustomManagerWhenInvokedThenUsesBeanToAuthorize() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class).autowire();\n\t\tMethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);\n\t\tservice.checkCustomManager(2);\n\t\tassertThatExceptionOfType(AuthorizationDeniedException.class).isThrownBy(() -> service.checkCustomManager(1));\n\t}\n\n\tprivate static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {\n\t\treturn (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);\n\t}\n\n\tprivate static Advisor returnAdvisor(int order) {\n\t\tJdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();\n\t\tpointcut.setPattern(\".*MethodSecurityServiceImpl.*\");\n\t\tMethodInterceptor interceptor = (mi) -> mi.getArguments()[0];\n\t\tDefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);\n\t\tadvisor.setOrder(order);\n\t\treturn advisor;\n\t}\n\n\t@Configuration\n\tstatic class AuthzConfig {\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableCustomMethodSecurity\n\tstatic class CustomMethodSecurityServiceConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\tstatic class MethodSecurityServiceConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity(jsr250Enabled = true)\n\tstatic class BusinessServiceConfig {\n\n\t\t@Bean\n\t\tBusinessService businessService() {\n\t\t\treturn new ExpressionProtectedBusinessServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity(prePostEnabled = false, securedEnabled = true)\n\tstatic class SecuredConfig {\n\n\t\t@Bean\n\t\tBusinessService businessService() {\n\t\t\treturn new BusinessServiceImpl<>();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity(prePostEnabled = false, jsr250Enabled = true)\n\tstatic class Jsr250Config {\n\n\t\t@Bean\n\t\tBusinessService businessService() {\n\t\t\treturn new Jsr250BusinessServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)\n\tstatic class MethodSecurityServiceEnabledConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\tstatic class CustomMethodSecurityExpressionHandlerConfig {\n\n\t\tprivate final MethodSecurityExpressionHandler expressionHandler = spy(\n\t\t\t\tnew DefaultMethodSecurityExpressionHandler());\n\n\t\t@Bean\n\t\tMethodSecurityExpressionHandler methodSecurityExpressionHandler() {\n\t\t\treturn this.expressionHandler;\n\t\t}\n\n\t}\n\n\t@EnableMethodSecurity\n\tstatic class CustomPermissionEvaluatorConfig {\n\n\t\t@Bean\n\t\tMethodSecurityExpressionHandler methodSecurityExpressionHandler() {\n\t\t\tDefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\t\texpressionHandler.setPermissionEvaluator(new PermissionEvaluator() {\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasPermission(Authentication authentication, Object targetDomainObject,\n\t\t\t\t\t\tObject permission) {\n\t\t\t\t\treturn \"grant\".equals(targetDomainObject);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\t\t\t\tObject permission) {\n\t\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn expressionHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\tstatic class CustomGrantedAuthorityDefaultsConfig {\n\n\t\t@Bean\n\t\tGrantedAuthorityDefaults grantedAuthorityDefaults() {\n\t\t\treturn new GrantedAuthorityDefaults(\"PREFIX_\");\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\tstatic class CustomAuthorizationManagerBeforeAdviceConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor customBeforeAdvice(SecurityContextHolderStrategy strategy) {\n\t\t\tJdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();\n\t\t\tpointcut.setPattern(\".*MethodSecurityServiceImpl.*securedUser\");\n\t\t\tAuthorizationManager<MethodInvocation> authorizationManager = (a,\n\t\t\t\t\to) -> new AuthorizationDecision(\"bob\".equals(a.get().getName()));\n\t\t\tAuthorizationManagerBeforeMethodInterceptor before = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\t\tpointcut, authorizationManager);\n\t\t\tbefore.setSecurityContextHolderStrategy(strategy);\n\t\t\treturn before;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\tstatic class CustomAuthorizationManagerAfterAdviceConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor customAfterAdvice(SecurityContextHolderStrategy strategy) {\n\t\t\tJdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();\n\t\t\tpointcut.setPattern(\".*MethodSecurityServiceImpl.*securedUser\");\n\t\t\tMethodInterceptor interceptor = (mi) -> {\n\t\t\t\tAuthentication auth = strategy.getContext().getAuthentication();\n\t\t\t\tif (\"bob\".equals(auth.getName())) {\n\t\t\t\t\treturn \"granted\";\n\t\t\t\t}\n\t\t\t\tthrow new AccessDeniedException(\"Access Denied for User '\" + auth.getName() + \"'\");\n\t\t\t};\n\t\t\tDefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);\n\t\t\tadvisor.setOrder(AuthorizationInterceptorsOrder.POST_FILTER.getOrder() + 1);\n\t\t\treturn advisor;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class AuthorizationEventPublisherConfig {\n\n\t\tprivate final AuthorizationEventPublisher publisher = mock(AuthorizationEventPublisher.class);\n\n\t\t@Bean\n\t\tAuthorizationEventPublisher authorizationEventPublisher() {\n\t\t\treturn this.publisher;\n\t\t}\n\n\t}\n\n\t@EnableMethodSecurity(mode = AdviceMode.ASPECTJ, securedEnabled = true)\n\tstatic class AspectJMethodSecurityServiceConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true)\n\tstatic class RoleHierarchyConfig {\n\n\t\t@Bean\n\t\tstatic RoleHierarchy roleHierarchy() {\n\t\t\treturn RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > ROLE_USER\");\n\t\t}\n\n\t}\n\n\t@Import(OffsetConfig.class)\n\tstatic class ReturnBeforeOffsetPreFilterConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor returnBeforePreFilter() {\n\t\t\treturn returnAdvisor(AuthorizationInterceptorsOrder.PRE_FILTER.getOrder() + OffsetConfig.OFFSET - 1);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(OffsetConfig.class)\n\tstatic class ReturnBeforeOffsetPreAuthorizeConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor returnBeforePreAuthorize() {\n\t\t\treturn returnAdvisor(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder() + OffsetConfig.OFFSET - 1);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(OffsetConfig.class)\n\tstatic class ReturnBeforeOffsetSecuredConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor returnBeforeSecured() {\n\t\t\treturn returnAdvisor(AuthorizationInterceptorsOrder.SECURED.getOrder() + OffsetConfig.OFFSET - 1);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(OffsetConfig.class)\n\tstatic class ReturnBeforeOffsetJsr250Config {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor returnBeforeJsr250() {\n\t\t\treturn returnAdvisor(AuthorizationInterceptorsOrder.JSR250.getOrder() + OffsetConfig.OFFSET - 1);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(OffsetConfig.class)\n\tstatic class ReturnBeforeOffsetPostAuthorizeConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor returnBeforePreAuthorize() {\n\t\t\treturn returnAdvisor(AuthorizationInterceptorsOrder.POST_AUTHORIZE.getOrder() + OffsetConfig.OFFSET - 1);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(OffsetConfig.class)\n\tstatic class ReturnBeforeOffsetPostFilterConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor returnBeforePostFilter() {\n\t\t\treturn returnAdvisor(AuthorizationInterceptorsOrder.POST_FILTER.getOrder() + OffsetConfig.OFFSET - 1);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(OffsetConfig.class)\n\tstatic class ReturnAfterAllOffsetConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tAdvisor returnAfterAll() {\n\t\t\treturn returnAdvisor(AuthorizationInterceptorsOrder.POST_FILTER.getOrder() + OffsetConfig.OFFSET + 1);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity(offset = OffsetConfig.OFFSET, jsr250Enabled = true, securedEnabled = true)\n\tstatic class OffsetConfig {\n\n\t\tstatic final int OFFSET = 2;\n\n\t\t@Bean\n\t\tMethodSecurityService methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\tstatic class MetaAnnotationPlaceholderConfig {\n\n\t\t@Bean\n\t\tAnnotationTemplateExpressionDefaults methodSecurityDefaults() {\n\t\t\treturn new AnnotationTemplateExpressionDefaults();\n\t\t}\n\n\t\t@Bean\n\t\tMetaAnnotationService metaAnnotationService() {\n\t\t\treturn new MetaAnnotationService();\n\t\t}\n\n\t}\n\n\tstatic class MetaAnnotationService {\n\n\t\t@RequireRole(role = \"#role\")\n\t\tboolean hasRole(String role) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@RequireRole(role = \"'USER'\")\n\t\tboolean hasUserRole() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@PreAuthorize(\"hasRole({role})\")\n\t\tvoid placeholdersOnlyResolvedByMetaAnnotations() {\n\t\t}\n\n\t\t@HasClaim(claim = \"message:read\", roles = { \"'ADMIN'\" })\n\t\tString readMessage() {\n\t\t\treturn \"message\";\n\t\t}\n\n\t\t@ResultStartsWith(\"dave\")\n\t\tString startsWithDave(String value) {\n\t\t\treturn value;\n\t\t}\n\n\t\t@ParameterContains(\"dave\")\n\t\tList<String> parametersContainDave(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@ResultContains(\"dave\")\n\t\tList<String> resultsContainDave(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@RestrictedAccess(entityClass = EntityClass.class)\n\t\tString getIdPath(String id) {\n\t\t\treturn id;\n\t\t}\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasRole({idPath})\")\n\t@interface RestrictedAccess {\n\n\t\tString idPath() default \"#id\";\n\n\t\tClass<?> entityClass();\n\n\t\tString[] recipes() default {};\n\n\t}\n\n\tstatic class EntityClass {\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasRole({role})\")\n\t@interface RequireRole {\n\n\t\tString role();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasAuthority('SCOPE_{claim}') || hasAnyRole({roles})\")\n\t@interface HasClaim {\n\n\t\tString claim();\n\n\t\tString[] roles() default {};\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PostAuthorize(\"returnObject.startsWith('{value}')\")\n\t@interface ResultStartsWith {\n\n\t\tString value();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreFilter(\"filterObject.contains('{value}')\")\n\t@interface ParameterContains {\n\n\t\tString value();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PostFilter(\"filterObject.contains('{value}')\")\n\t@interface ResultContains {\n\n\t\tString value();\n\n\t}\n\n\t@EnableMethodSecurity\n\t@Configuration\n\tstatic class AuthorizeResultConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tstatic TargetVisitor skipValueTypes() {\n\t\t\treturn TargetVisitor.defaultsSkipValueTypes();\n\t\t}\n\n\t\t@Bean\n\t\tFlightRepository flights() {\n\t\t\tFlightRepository flights = new FlightRepository();\n\t\t\tFlight one = new Flight(\"1\", 35000d, 35);\n\t\t\tone.board(new ArrayList<>(List.of(\"Marie Curie\", \"Kevin Mitnick\", \"Ada Lovelace\")));\n\t\t\tflights.save(one);\n\t\t\tFlight two = new Flight(\"2\", 32000d, 72);\n\t\t\ttwo.board(new ArrayList<>(List.of(\"Albert Einstein\")));\n\t\t\tflights.save(two);\n\t\t\treturn flights;\n\t\t}\n\n\t\t@Bean\n\t\tRoleHierarchy roleHierarchy() {\n\t\t\treturn RoleHierarchyImpl.withRolePrefix(\"\").role(\"airplane:read\").implies(\"seating:read\").build();\n\t\t}\n\n\t}\n\n\t@AuthorizeReturnObject\n\tstatic class FlightRepository {\n\n\t\tprivate final Map<String, Flight> flights = new ConcurrentHashMap<>();\n\n\t\tIterator<Flight> findAll() {\n\t\t\treturn this.flights.values().iterator();\n\t\t}\n\n\t\tPage<Flight> findPage() {\n\t\t\treturn new PageImpl<>(new ArrayList<>(this.flights.values()));\n\t\t}\n\n\t\tSlice<Flight> findSlice() {\n\t\t\treturn new SliceImpl<>(new ArrayList<>(this.flights.values()));\n\t\t}\n\n\t\tGeoPage<Flight> findGeoPage() {\n\t\t\tList<GeoResult<Flight>> results = new ArrayList<>();\n\t\t\tfor (Flight flight : this.flights.values()) {\n\t\t\t\tresults.add(new GeoResult<>(flight, new Distance(flight.altitude)));\n\t\t\t}\n\t\t\treturn new GeoPage<>(new GeoResults<>(results));\n\t\t}\n\n\t\tGeoResults<Flight> findGeoResults() {\n\t\t\tList<GeoResult<Flight>> results = new ArrayList<>();\n\t\t\tfor (Flight flight : this.flights.values()) {\n\t\t\t\tresults.add(new GeoResult<>(flight, new Distance(flight.altitude)));\n\t\t\t}\n\t\t\treturn new GeoResults<>(results);\n\t\t}\n\n\t\tFlight findById(String id) {\n\t\t\treturn this.flights.get(id);\n\t\t}\n\n\t\tGeoResult<Flight> findGeoResultFlightById(String id) {\n\t\t\tFlight flight = this.flights.get(id);\n\t\t\treturn new GeoResult<>(flight, new Distance(flight.altitude));\n\t\t}\n\n\t\tFlight save(Flight flight) {\n\t\t\tthis.flights.put(flight.getId(), flight);\n\t\t\treturn flight;\n\t\t}\n\n\t\tvoid remove(String id) {\n\t\t\tthis.flights.remove(id);\n\t\t}\n\n\t\tResponseEntity<Flight> webFindById(String id) {\n\t\t\tFlight flight = this.flights.get(id);\n\t\t\tif (flight == null) {\n\t\t\t\treturn ResponseEntity.notFound().build();\n\t\t\t}\n\t\t\treturn ResponseEntity.ok(flight);\n\t\t}\n\n\t\tModelAndView webViewFindById(String id) {\n\t\t\tFlight flight = this.flights.get(id);\n\t\t\tif (flight == null) {\n\t\t\t\treturn new ModelAndView(\"error\", HttpStatusCode.valueOf(404));\n\t\t\t}\n\t\t\treturn new ModelAndView(\"flights\", Map.of(\"flight\", flight));\n\t\t}\n\n\t}\n\n\t@AuthorizeReturnObject\n\tstatic class Flight {\n\n\t\tprivate final String id;\n\n\t\tprivate final Double altitude;\n\n\t\tprivate final Integer seats;\n\n\t\tprivate final List<Passenger> passengers = new ArrayList<>();\n\n\t\tFlight(String id, Double altitude, Integer seats) {\n\t\t\tthis.id = id;\n\t\t\tthis.altitude = altitude;\n\t\t\tthis.seats = seats;\n\t\t}\n\n\t\tString getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('airplane:read')\")\n\t\tDouble getAltitude() {\n\t\t\treturn this.altitude;\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('seating:read')\")\n\t\tInteger getSeats() {\n\t\t\treturn this.seats;\n\t\t}\n\n\t\t@PostAuthorize(\"hasAuthority('seating:read')\")\n\t\t@PostFilter(\"filterObject.name != 'Kevin Mitnick'\")\n\t\tList<Passenger> getPassengers() {\n\t\t\treturn this.passengers;\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('seating:read')\")\n\t\t@PreFilter(\"filterObject.contains(' ')\")\n\t\tvoid board(List<String> passengers) {\n\t\t\tfor (String passenger : passengers) {\n\t\t\t\tthis.passengers.add(new Passenger(passenger));\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic static class Passenger {\n\n\t\tString name;\n\n\t\tpublic Passenger(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('airplane:read')\")\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t}\n\n\t@EnableMethodSecurity\n\tstatic class CustomResultConfig {\n\n\t\tMethodAuthorizationDeniedHandler handler = mock(MethodAuthorizationDeniedHandler.class);\n\n\t\t@Bean\n\t\tMethodAuthorizationDeniedHandler methodAuthorizationDeniedHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t}\n\n\tabstract static class AbstractClassWithNoAnnotations {\n\n\t\tString method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"denyAll()\")\n\t@Secured(\"DENIED\")\n\t@DenyAll\n\tstatic class ClassInheritingAbstractClassWithNoAnnotations extends AbstractClassWithNoAnnotations {\n\n\t}\n\n\t@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)\n\tstatic class AbstractClassConfig {\n\n\t\t@Bean\n\t\tClassInheritingAbstractClassWithNoAnnotations inheriting() {\n\t\t\treturn new ClassInheritingAbstractClassWithNoAnnotations();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\tstatic class AspectJAwareAutoProxyAndFactoryBeansConfig {\n\n\t\t@Bean\n\t\tstatic BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() {\n\t\t\treturn AopConfigUtils::registerAspectJAnnotationAutoProxyCreatorIfNecessary;\n\t\t}\n\n\t\t@Component\n\t\tstatic class MyFactoryBean implements FactoryBean<Object> {\n\n\t\t\t@Override\n\t\t\tpublic Object getObject() throws Exception {\n\t\t\t\treturn new Object();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<?> getObjectType() {\n\t\t\t\treturn Object.class;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ObservationRegistryConfig {\n\n\t\tprivate final ObservationRegistry registry = ObservationRegistry.create();\n\n\t\tprivate final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\treturn this.registry;\n\t\t}\n\n\t\t@Bean\n\t\tObservationHandler<Observation.Context> observationHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t\t@Bean\n\t\tObservationRegistryPostProcessor observationRegistryPostProcessor(\n\t\t\t\tObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\treturn new ObservationRegistryPostProcessor(handler);\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryPostProcessor implements BeanPostProcessor {\n\n\t\tprivate final ObjectProvider<ObservationHandler<Observation.Context>> handler;\n\n\t\tObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\tthis.handler = handler;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tif (bean instanceof ObservationRegistry registry) {\n\t\t\t\tregistry.observationConfig().observationHandler(this.handler.getObject());\n\t\t\t}\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SelectableObservationsConfig {\n\n\t\t@Bean\n\t\tSecurityObservationSettings observabilityDefaults() {\n\t\t\treturn SecurityObservationSettings.withDefaults().shouldObserveAuthorizations(false).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class EventPublisherConfig {\n\n\t\t@Bean\n\t\tstatic AuthorizationEventPublisher eventPublisher(ApplicationEventPublisher publisher) {\n\t\t\treturn new SpringAuthorizationEventPublisher(publisher);\n\t\t}\n\n\t}\n\n\t@Component\n\tstatic class AuthorizationDeniedListener {\n\n\t\tint invocations;\n\n\t\t@EventListener\n\t\tvoid onRequestDenied(AuthorizationDeniedEvent<? extends MethodInvocation> denied) {\n\t\t\tthis.invocations++;\n\t\t}\n\n\t}\n\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\t@EnableMethodSecurity\n\tstatic class WebMvcMethodSecurityConfig {\n\n\t}\n\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\t@EnableMethodSecurity\n\tstatic class WebMvcMethodSecurityCustomAdvisorConfig {\n\n\t\t@Bean\n\t\tAuthorizationAdvisor customAdvisor(SecurityContextHolderStrategy strategy) {\n\t\t\tJdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();\n\t\t\tpointcut.setPattern(\".*AuthorizedPerson.*getName\");\n\t\t\treturn new AuthorizationAdvisor() {\n\t\t\t\t@Override\n\t\t\t\tpublic Object invoke(MethodInvocation mi) throws Throwable {\n\t\t\t\t\tAuthentication auth = strategy.getContext().getAuthentication();\n\t\t\t\t\tObject result = mi.proceed();\n\t\t\t\t\tif (auth.getName().equals(result)) {\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t\tthrow new AccessDeniedException(\"Access Denied for User '\" + auth.getName() + \"'\");\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Pointcut getPointcut() {\n\t\t\t\t\treturn pointcut;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Advice getAdvice() {\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic int getOrder() {\n\t\t\t\t\treturn AuthorizationInterceptorsOrder.POST_FILTER.getOrder() + 1;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@Autowired(required = false)\n\t\tBasicService service;\n\n\t\t@GetMapping(\"/greetings/authorized-person\")\n\t\tString getAuthorizedPersonGreeting(@RequestParam String name) {\n\t\t\tAuthorizedPerson authorizedPerson = this.service.getAuthorizedPerson(name);\n\t\t\treturn \"Hello: \" + authorizedPerson.getName();\n\t\t}\n\n\t\t@AuthorizeReturnObject\n\t\t@GetMapping(value = \"/authorized-person\", produces = MediaType.APPLICATION_JSON_VALUE)\n\t\tAuthorizedPerson getAuthorizedPerson(@RequestParam String name) {\n\t\t\treturn new AuthorizedPerson(name);\n\t\t}\n\n\t}\n\n\t@ControllerAdvice\n\tstatic class BasicControllerAdvice {\n\n\t\t@ExceptionHandler(AccessDeniedException.class)\n\t\tResponseEntity<Map<String, String>> handleAccessDenied(AccessDeniedException ex) {\n\t\t\tMap<String, String> responseBody = Map.of(\"message\", ex.getMessage());\n\t\t\treturn ResponseEntity.status(HttpStatus.FORBIDDEN).body(responseBody);\n\t\t}\n\n\t\t@ExceptionHandler(HttpMessageNotWritableException.class)\n\t\tResponseEntity<Map<String, String>> handleHttpMessageNotWritable(HttpMessageNotWritableException ex) {\n\t\t\tThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();\n\t\t\tThrowable[] causeChain = throwableAnalyzer.determineCauseChain(ex);\n\t\t\tThrowable t = throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);\n\t\t\tif (t != null) {\n\t\t\t\tMap<String, String> responseBody = Map.of(\"message\", ex.getMessage());\n\t\t\t\treturn ResponseEntity.status(HttpStatus.FORBIDDEN).body(responseBody);\n\t\t\t}\n\t\t\tthrow ex;\n\t\t}\n\n\t}\n\n\t@Service\n\tstatic class BasicService {\n\n\t\t@AuthorizeReturnObject\n\t\tAuthorizedPerson getAuthorizedPerson(String name) {\n\t\t\treturn new AuthorizedPerson(name);\n\t\t}\n\n\t}\n\n\tpublic static class AuthorizedPerson {\n\n\t\tfinal String name;\n\n\t\tAuthorizedPerson(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@PostAuthorize(\"returnObject == authentication.name\")\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostReactiveMethodSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport jakarta.annotation.security.DenyAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.aop.config.AopConfigUtils;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.method.AuthorizationAdvisor;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.stereotype.Component;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class PrePostReactiveMethodSecurityConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\t@WithMockUser\n\tvoid getCardNumberWhenPostAuthorizeAndNotAdminThenReturnMasked() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.CardNumberMaskingPostProcessor.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.postAuthorizeGetCardNumberIfAdmin(\"4444-3333-2222-1111\"))\n\t\t\t.expectNext(\"****-****-****-1111\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getCardNumberWhenPreAuthorizeAndNotAdminThenReturnMasked() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, ReactiveMethodSecurityService.StarMaskingHandler.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.preAuthorizeGetCardNumberIfAdmin(\"4444-3333-2222-1111\"))\n\t\t\t.expectNext(\"***\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getCardNumberWhenPreAuthorizeAndNotAdminAndChildHandlerThenResolveCorrectHandlerAndReturnMasked() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, ReactiveMethodSecurityService.StarMaskingHandler.class,\n\t\t\t\t\tReactiveMethodSecurityService.StartMaskingHandlerChild.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.preAuthorizeWithHandlerChildGetCardNumberIfAdmin(\"4444-3333-2222-1111\"))\n\t\t\t.expectNext(\"***-child\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.MaskAnnotationHandler.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.preAuthorizeDeniedMethodWithMaskAnnotation())\n\t\t\t.expectNext(\"methodmask\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationInClassThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.MaskAnnotationHandler.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.preAuthorizeDeniedMethodWithNoMaskAnnotation())\n\t\t\t.expectNext(\"classmask\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tvoid postAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPostAuthorizeThenNotHandled() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.PostMaskingPostProcessor.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.postAuthorizeThrowAccessDeniedManually()).expectNext(\"***\").verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tvoid preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenHandled() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, ReactiveMethodSecurityService.StarMaskingHandler.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.preAuthorizeThrowAccessDeniedManually()).expectNext(\"***\").verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid postAuthorizeWhenNullDeniedMetaAnnotationThanWorks() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, ReactiveMethodSecurityService.NullPostProcessor.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.postAuthorizeDeniedWithNullDenied()).verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid postAuthorizeWhenDeniedAndHandlerWithCustomAnnotationThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.MaskAnnotationPostProcessor.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.postAuthorizeDeniedMethodWithMaskAnnotation())\n\t\t\t.expectNext(\"methodmask\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid postAuthorizeWhenDeniedAndHandlerWithCustomAnnotationInClassThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.MaskAnnotationPostProcessor.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.postAuthorizeDeniedMethodWithNoMaskAnnotation())\n\t\t\t.expectNext(\"classmask\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid postAuthorizeWhenDeniedAndHandlerWithCustomAnnotationUsingBeanThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.MaskAnnotationPostProcessor.class, MyMasker.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.postAuthorizeWithMaskAnnotationUsingBean())\n\t\t\t.expectNext(\"ok-masked\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tvoid postAuthorizeWhenAllowedAndHandlerWithCustomAnnotationUsingBeanThenInvokeMethodNormally() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.MaskAnnotationPostProcessor.class, MyMasker.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.postAuthorizeWithMaskAnnotationUsingBean()).expectNext(\"ok\").verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationUsingBeanThenHandlerCanUseMaskFromOtherAnnotation() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.MaskAnnotationHandler.class, MyMasker.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.preAuthorizeWithMaskAnnotationUsingBean()).expectNext(\"mask\").verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tvoid preAuthorizeWhenAllowedAndHandlerWithCustomAnnotationUsingBeanThenInvokeMethodNormally() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class,\n\t\t\t\t\tReactiveMethodSecurityService.MaskAnnotationHandler.class, MyMasker.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tStepVerifier.create(service.preAuthorizeWithMaskAnnotationUsingBean()).expectNext(\"ok\").verifyComplete();\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic void preAuthorizeWhenCustomMethodSecurityExpressionHandlerThenUses() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class, PermissionEvaluatorConfig.class).autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tPermissionEvaluator permissionEvaluator = this.spring.getContext().getBean(PermissionEvaluator.class);\n\t\tgiven(permissionEvaluator.hasPermission(any(), eq(\"grant\"), any())).willReturn(true);\n\t\tgiven(permissionEvaluator.hasPermission(any(), eq(\"deny\"), any())).willReturn(false);\n\t\tStepVerifier.create(service.preAuthorizeHasPermission(\"grant\")).expectNext(\"ok\").verifyComplete();\n\t\tStepVerifier.create(service.preAuthorizeHasPermission(\"deny\"))\n\t\t\t.expectError(AuthorizationDeniedException.class)\n\t\t\t.verify();\n\t\tverify(permissionEvaluator, times(2)).hasPermission(any(), any(), any());\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodeWhenParameterizedPreAuthorizeMetaAnnotationThenPasses(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.hasRole(\"USER\").block()).isTrue();\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodRoleWhenPreAuthorizeMetaAnnotationHardcodedParameterThenPasses(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.hasUserRole().block()).isTrue();\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\tpublic void methodWhenParameterizedAnnotationThenFails(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> service.placeholdersOnlyResolvedByMetaAnnotations().block());\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser(authorities = \"SCOPE_message:read\")\n\tpublic void methodWhenMultiplePlaceholdersHasAuthorityThenPasses(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.readMessage().block()).isEqualTo(\"message\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic void methodWhenMultiplePlaceholdersHasRoleThenPasses(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.readMessage().block()).isEqualTo(\"message\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodWhenPostAuthorizeMetaAnnotationThenAuthorizes(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tservice.startsWithDave(\"daveMatthews\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> service.startsWithDave(\"jenniferHarper\").block());\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodWhenPreFilterMetaAnnotationThenFilters(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.parametersContainDave(Flux.just(\"dave\", \"carla\", \"vanessa\", \"paul\")).collectList().block())\n\t\t\t.containsExactly(\"dave\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MetaAnnotationPlaceholderConfig.class })\n\t@WithMockUser\n\tpublic void methodWhenPostFilterMetaAnnotationThenFilters(Class<?> config) {\n\t\tthis.spring.register(config).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.resultsContainDave(Flux.just(\"dave\", \"carla\", \"vanessa\", \"paul\")).collectList().block())\n\t\t\t.containsExactly(\"dave\");\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findByIdWhenAuthorizedResultThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tFlight flight = flights.findById(\"1\").block();\n\t\tassertThatNoException().isThrownBy(flight::getAltitude);\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findByIdWhenUnauthorizedResultThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tFlight flight = flights.findById(\"1\").block();\n\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> flight.getAltitude().block());\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findAllWhenUnauthorizedResultThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findAll().collectList().block().forEach((flight) -> {\n\t\t\tassertThatNoException().isThrownBy(flight::getSeats);\n\t\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> flight.getAltitude().block());\n\t\t});\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizedResultThenRemoves() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.remove(\"1\");\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findAllWhenPostFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findAll()\n\t\t\t.collectList()\n\t\t\t.block()\n\t\t\t.forEach((flight) -> assertThat(flight.getPassengers().collectList().block())\n\t\t\t\t.extracting((p) -> p.getName().block())\n\t\t\t\t.doesNotContain(\"Kevin Mitnick\"));\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"airplane:read\")\n\tpublic void findAllWhenPreFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findAll().collectList().block().forEach((flight) -> {\n\t\t\tflight.board(Flux.just(\"John\")).block();\n\t\t\tassertThat(flight.getPassengers().collectList().block()).extracting((p) -> p.getName().block())\n\t\t\t\t.doesNotContain(\"John\");\n\t\t\tflight.board(Flux.just(\"John Doe\")).block();\n\t\t\tassertThat(flight.getPassengers().collectList().block()).extracting((p) -> p.getName().block())\n\t\t\t\t.contains(\"John Doe\");\n\t\t});\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"seating:read\")\n\tpublic void findAllWhenNestedPreAuthorizeThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tflights.findAll().collectList().block().forEach((flight) -> {\n\t\t\tList<Passenger> passengers = flight.getPassengers().collectList().block();\n\t\t\tpassengers.forEach((passenger) -> assertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t\t.isThrownBy(() -> passenger.getName().block()));\n\t\t});\n\t}\n\n\t// gh-15352\n\t@Test\n\tvoid annotationsInChildClassesDoNotAffectSuperclasses() {\n\t\tthis.spring.register(AbstractClassConfig.class).autowire();\n\t\tthis.spring.getContext().getBean(ClassInheritingAbstractClassWithNoAnnotations.class).method();\n\t}\n\n\t// gh-15592\n\t@Test\n\tvoid autowireWhenDefaultsThenCreatesExactlyOneAdvisorPerAnnotation() {\n\t\tthis.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = this.spring.getContext()\n\t\t\t.getBean(AuthorizationAdvisorProxyFactory.class);\n\t\tassertThat(proxyFactory).hasSize(5);\n\t\tassertThat(this.spring.getContext().getBeanNamesForType(AuthorizationAdvisor.class)).hasSize(5)\n\t\t\t.containsExactlyInAnyOrder(\"preFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\t\"preAuthorizeAuthorizationMethodInterceptor\", \"postAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\t\"postFilterAuthorizationMethodInterceptor\", \"authorizeReturnObjectMethodInterceptor\");\n\t}\n\n\t// gh-15592\n\t@Test\n\tvoid autowireWhenAspectJAutoProxyAndFactoryBeanThenExactlyOneAdvisorPerAnnotation() {\n\t\tthis.spring.register(AspectJAwareAutoProxyAndFactoryBeansConfig.class).autowire();\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = this.spring.getContext()\n\t\t\t.getBean(AuthorizationAdvisorProxyFactory.class);\n\t\tassertThat(proxyFactory).hasSize(5);\n\t\tassertThat(this.spring.getContext().getBeanNamesForType(AuthorizationAdvisor.class)).hasSize(5)\n\t\t\t.containsExactlyInAnyOrder(\"preFilterAuthorizationMethodInterceptor\",\n\t\t\t\t\t\"preAuthorizeAuthorizationMethodInterceptor\", \"postAuthorizeAuthorizationMethodInterceptor\",\n\t\t\t\t\t\"postFilterAuthorizationMethodInterceptor\", \"authorizeReturnObjectMethodInterceptor\");\n\t}\n\n\t// gh-15651\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic void adviseWhenPrePostEnabledThenEachInterceptorRunsExactlyOnce() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceEnabledConfig.class, CustomMethodSecurityExpressionHandlerConfig.class)\n\t\t\t.autowire();\n\t\tMethodSecurityExpressionHandler expressionHandler = this.spring.getContext()\n\t\t\t.getBean(MethodSecurityExpressionHandler.class);\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tservice.manyAnnotations(Mono.just(new ArrayList<>(Arrays.asList(\"harold\", \"jonathan\", \"tim\", \"bo\")))).block();\n\t\tverify(expressionHandler, times(4)).createEvaluationContext(any(Authentication.class), any());\n\t}\n\n\t// gh-15721\n\t@Test\n\t@WithMockUser(roles = \"uid\")\n\tpublic void methodWhenMetaAnnotationPropertiesHasClassProperties() {\n\t\tthis.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();\n\t\tMetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class);\n\t\tassertThat(service.getIdPath(\"uid\").block()).isEqualTo(\"uid\");\n\t}\n\n\t@Configuration\n\t@EnableReactiveMethodSecurity\n\tstatic class MethodSecurityServiceEnabledConfig {\n\n\t\t@Bean\n\t\tReactiveMethodSecurityService methodSecurityService() {\n\t\t\treturn new ReactiveMethodSecurityServiceImpl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableReactiveMethodSecurity\n\tstatic class CustomMethodSecurityExpressionHandlerConfig {\n\n\t\tprivate final MethodSecurityExpressionHandler expressionHandler = spy(\n\t\t\t\tnew DefaultMethodSecurityExpressionHandler());\n\n\t\t@Bean\n\t\tMethodSecurityExpressionHandler methodSecurityExpressionHandler() {\n\t\t\treturn this.expressionHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class PermissionEvaluatorConfig {\n\n\t\t@Bean\n\t\tstatic PermissionEvaluator permissionEvaluator() {\n\t\t\treturn mock(PermissionEvaluator.class);\n\t\t}\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tstatic DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(\n\t\t\t\tPermissionEvaluator permissionEvaluator) {\n\t\t\tDefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();\n\t\t\thandler.setPermissionEvaluator(permissionEvaluator);\n\t\t\treturn handler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableReactiveMethodSecurity\n\tstatic class MetaAnnotationPlaceholderConfig {\n\n\t\t@Bean\n\t\tAnnotationTemplateExpressionDefaults methodSecurityDefaults() {\n\t\t\treturn new AnnotationTemplateExpressionDefaults();\n\t\t}\n\n\t\t@Bean\n\t\tMetaAnnotationService metaAnnotationService() {\n\t\t\treturn new MetaAnnotationService();\n\t\t}\n\n\t}\n\n\tstatic class MetaAnnotationService {\n\n\t\t@RequireRole(role = \"#role\")\n\t\tMono<Boolean> hasRole(String role) {\n\t\t\treturn Mono.just(true);\n\t\t}\n\n\t\t@RequireRole(role = \"'USER'\")\n\t\tMono<Boolean> hasUserRole() {\n\t\t\treturn Mono.just(true);\n\t\t}\n\n\t\t@PreAuthorize(\"hasRole({role})\")\n\t\tMono<Void> placeholdersOnlyResolvedByMetaAnnotations() {\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\t@HasClaim(claim = \"message:read\", roles = { \"'ADMIN'\" })\n\t\tMono<String> readMessage() {\n\t\t\treturn Mono.just(\"message\");\n\t\t}\n\n\t\t@ResultStartsWith(\"dave\")\n\t\tMono<String> startsWithDave(String value) {\n\t\t\treturn Mono.just(value);\n\t\t}\n\n\t\t@ParameterContains(\"dave\")\n\t\tFlux<String> parametersContainDave(Flux<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@ResultContains(\"dave\")\n\t\tFlux<String> resultsContainDave(Flux<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@RestrictedAccess(entityClass = EntityClass.class)\n\t\tMono<String> getIdPath(String id) {\n\t\t\treturn Mono.just(id);\n\t\t}\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasRole({idPath})\")\n\t@interface RestrictedAccess {\n\n\t\tString idPath() default \"#id\";\n\n\t\tClass<?> entityClass();\n\n\t\tString[] recipes() default {};\n\n\t}\n\n\tstatic class EntityClass {\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasRole({role})\")\n\t@interface RequireRole {\n\n\t\tString role();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasAuthority('SCOPE_{claim}') || hasAnyRole({roles})\")\n\t@interface HasClaim {\n\n\t\tString claim();\n\n\t\tString[] roles() default {};\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PostAuthorize(\"returnObject.startsWith('{value}')\")\n\t@interface ResultStartsWith {\n\n\t\tString value();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreFilter(\"filterObject.contains('{value}')\")\n\t@interface ParameterContains {\n\n\t\tString value();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PostFilter(\"filterObject.contains('{value}')\")\n\t@interface ResultContains {\n\n\t\tString value();\n\n\t}\n\n\t@EnableReactiveMethodSecurity\n\t@Configuration\n\tpublic static class AuthorizeResultConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tstatic TargetVisitor skipValueTypes() {\n\t\t\treturn TargetVisitor.defaultsSkipValueTypes();\n\t\t}\n\n\t\t@Bean\n\t\tFlightRepository flights() {\n\t\t\tFlightRepository flights = new FlightRepository();\n\t\t\tFlight one = new Flight(\"1\", 35000d, 35);\n\t\t\tone.board(Flux.just(\"Marie Curie\", \"Kevin Mitnick\", \"Ada Lovelace\")).block();\n\t\t\tflights.save(one).block();\n\t\t\tFlight two = new Flight(\"2\", 32000d, 72);\n\t\t\ttwo.board(Flux.just(\"Albert Einstein\")).block();\n\t\t\tflights.save(two).block();\n\t\t\treturn flights;\n\t\t}\n\n\t\t@Bean\n\t\tstatic MethodSecurityExpressionHandler expressionHandler() {\n\t\t\tRoleHierarchy hierarchy = RoleHierarchyImpl.withRolePrefix(\"\")\n\t\t\t\t.role(\"airplane:read\")\n\t\t\t\t.implies(\"seating:read\")\n\t\t\t\t.build();\n\t\t\tDefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\t\texpressionHandler.setRoleHierarchy(hierarchy);\n\t\t\treturn expressionHandler;\n\t\t}\n\n\t\t@Bean\n\t\tAuthz authz() {\n\t\t\treturn new Authz();\n\t\t}\n\n\t\tpublic static class Authz {\n\n\t\t\tpublic Mono<Boolean> isNotKevinMitnick(Passenger passenger) {\n\t\t\t\treturn passenger.getName().map((n) -> !\"Kevin Mitnick\".equals(n));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@AuthorizeReturnObject\n\tstatic class FlightRepository {\n\n\t\tprivate final Map<String, Flight> flights = new ConcurrentHashMap<>();\n\n\t\tFlux<Flight> findAll() {\n\t\t\treturn Flux.fromIterable(this.flights.values());\n\t\t}\n\n\t\tMono<Flight> findById(String id) {\n\t\t\treturn Mono.just(this.flights.get(id));\n\t\t}\n\n\t\tMono<Flight> save(Flight flight) {\n\t\t\tthis.flights.put(flight.getId(), flight);\n\t\t\treturn Mono.just(flight);\n\t\t}\n\n\t\tMono<Void> remove(String id) {\n\t\t\tthis.flights.remove(id);\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t}\n\n\t@AuthorizeReturnObject\n\tstatic class Flight {\n\n\t\tprivate final String id;\n\n\t\tprivate final Double altitude;\n\n\t\tprivate final Integer seats;\n\n\t\tprivate final List<Passenger> passengers = new ArrayList<>();\n\n\t\tFlight(String id, Double altitude, Integer seats) {\n\t\t\tthis.id = id;\n\t\t\tthis.altitude = altitude;\n\t\t\tthis.seats = seats;\n\t\t}\n\n\t\tString getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('airplane:read')\")\n\t\tMono<Double> getAltitude() {\n\t\t\treturn Mono.just(this.altitude);\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('seating:read')\")\n\t\tMono<Integer> getSeats() {\n\t\t\treturn Mono.just(this.seats);\n\t\t}\n\n\t\t@PostAuthorize(\"hasAuthority('seating:read')\")\n\t\t@PostFilter(\"@authz.isNotKevinMitnick(filterObject)\")\n\t\tFlux<Passenger> getPassengers() {\n\t\t\treturn Flux.fromIterable(this.passengers);\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('seating:read')\")\n\t\t@PreFilter(\"filterObject.contains(' ')\")\n\t\tMono<Void> board(Flux<String> passengers) {\n\t\t\treturn passengers.doOnNext((passenger) -> this.passengers.add(new Passenger(passenger))).then(Mono.empty());\n\t\t}\n\n\t}\n\n\tpublic static class Passenger {\n\n\t\tString name;\n\n\t\tpublic Passenger(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('airplane:read')\")\n\t\tpublic Mono<String> getName() {\n\t\t\treturn Mono.just(this.name);\n\t\t}\n\n\t}\n\n\tabstract static class AbstractClassWithNoAnnotations {\n\n\t\tMono<String> method() {\n\t\t\treturn Mono.just(\"ok\");\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"denyAll()\")\n\t@Secured(\"DENIED\")\n\t@DenyAll\n\tstatic class ClassInheritingAbstractClassWithNoAnnotations extends AbstractClassWithNoAnnotations {\n\n\t}\n\n\t@EnableReactiveMethodSecurity\n\tstatic class AbstractClassConfig {\n\n\t\t@Bean\n\t\tClassInheritingAbstractClassWithNoAnnotations inheriting() {\n\t\t\treturn new ClassInheritingAbstractClassWithNoAnnotations();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableReactiveMethodSecurity\n\tstatic class AspectJAwareAutoProxyAndFactoryBeansConfig {\n\n\t\t@Bean\n\t\tstatic BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() {\n\t\t\treturn AopConfigUtils::registerAspectJAnnotationAutoProxyCreatorIfNecessary;\n\t\t}\n\n\t\t@Component\n\t\tstatic class MyFactoryBean implements FactoryBean<Object> {\n\n\t\t\t@Override\n\t\t\tpublic Object getObject() throws Exception {\n\t\t\t\treturn new Object();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<?> getObjectType() {\n\t\t\t\treturn Object.class;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMessageService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\npublic interface ReactiveMessageService {\n\n\tString notPublisherPreAuthorizeFindById(long id);\n\n\tMono<String> monoFindById(long id);\n\n\tMono<String> monoPreAuthorizeHasRoleFindById(long id);\n\n\tMono<String> monoPostAuthorizeFindById(long id);\n\n\tMono<String> monoPreAuthorizeBeanFindById(long id);\n\n\tMono<String> monoPreAuthorizeBeanFindByIdReactiveExpression(long id);\n\n\tMono<String> monoPostAuthorizeBeanFindById(long id);\n\n\tFlux<String> fluxFindById(long id);\n\n\tFlux<String> fluxPreAuthorizeHasRoleFindById(long id);\n\n\tFlux<String> fluxPostAuthorizeFindById(long id);\n\n\tFlux<String> fluxPreAuthorizeBeanFindById(long id);\n\n\tFlux<String> fluxPostAuthorizeBeanFindById(long id);\n\n\tFlux<String> fluxManyAnnotations(Flux<String> flux);\n\n\tFlux<String> fluxPostFilter(Flux<String> flux);\n\n\tPublisher<String> publisherFindById(long id);\n\n\tPublisher<String> publisherPreAuthorizeHasRoleFindById(long id);\n\n\tPublisher<String> publisherPostAuthorizeFindById(long id);\n\n\tPublisher<String> publisherPreAuthorizeBeanFindById(long id);\n\n\tPublisher<String> publisherPostAuthorizeBeanFindById(long id);\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.ObservationTextPublisher;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.intercept.method.MockMethodInvocation;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.test.context.support.WithMockUser;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.clearInvocations;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * @author Tadaya Tsuyukubo\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class ReactiveMethodSecurityConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tDefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler;\n\n\t@Test\n\tpublic void rolePrefixWithGrantedAuthorityDefaults() throws NoSuchMethodException {\n\t\tthis.spring.register(WithRolePrefixConfiguration.class).autowire();\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser(authorities(\"CUSTOM_ABC\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new Foo(), Foo.class, \"bar\", String.class);\n\t\tEvaluationContext context = this.methodSecurityExpressionHandler.createEvaluationContext(authentication,\n\t\t\t\tmethodInvocation);\n\t\tSecurityExpressionRoot root = (SecurityExpressionRoot) context.getRootObject().getValue();\n\t\tassertThat(root.hasRole(\"ROLE_ABC\")).isFalse();\n\t\tassertThat(root.hasRole(\"ROLE_CUSTOM_ABC\")).isFalse();\n\t\tassertThat(root.hasRole(\"CUSTOM_ABC\")).isTrue();\n\t\tassertThat(root.hasRole(\"ABC\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void rolePrefixWithDefaultConfig() throws NoSuchMethodException {\n\t\tthis.spring.register(ReactiveMethodSecurityConfiguration.class).autowire();\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser(authorities(\"ROLE_ABC\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new Foo(), Foo.class, \"bar\", String.class);\n\t\tEvaluationContext context = this.methodSecurityExpressionHandler.createEvaluationContext(authentication,\n\t\t\t\tmethodInvocation);\n\t\tSecurityExpressionRoot root = (SecurityExpressionRoot) context.getRootObject().getValue();\n\t\tassertThat(root.hasRole(\"ROLE_ABC\")).isTrue();\n\t\tassertThat(root.hasRole(\"ABC\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void rolePrefixWithGrantedAuthorityDefaultsAndSubclassWithProxyingEnabled() throws NoSuchMethodException {\n\t\tthis.spring.register(SubclassConfig.class).autowire();\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser(authorities(\"ROLE_ABC\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new Foo(), Foo.class, \"bar\", String.class);\n\t\tEvaluationContext context = this.methodSecurityExpressionHandler.createEvaluationContext(authentication,\n\t\t\t\tmethodInvocation);\n\t\tSecurityExpressionRoot root = (SecurityExpressionRoot) context.getRootObject().getValue();\n\t\tassertThat(root.hasRole(\"ROLE_ABC\")).isTrue();\n\t\tassertThat(root.hasRole(\"ABC\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void findByIdWhenAuthorizedResultThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tAuthentication pilot = TestAuthentication.authenticatedUser(authorities(\"airplane:read\"));\n\t\tStepVerifier\n\t\t\t.create(flights.findById(\"1\")\n\t\t\t\t.flatMap(Flight::getAltitude)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.expectNextCount(1)\n\t\t\t.verifyComplete();\n\t\tStepVerifier\n\t\t\t.create(flights.findById(\"1\")\n\t\t\t\t.flatMap(Flight::getSeats)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.expectNextCount(1)\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void findByIdWhenUnauthorizedResultThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tAuthentication pilot = TestAuthentication.authenticatedUser(authorities(\"seating:read\"));\n\t\tStepVerifier\n\t\t\t.create(flights.findById(\"1\")\n\t\t\t\t.flatMap(Flight::getSeats)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.expectNextCount(1)\n\t\t\t.verifyComplete();\n\t\tStepVerifier\n\t\t\t.create(flights.findById(\"1\")\n\t\t\t\t.flatMap(Flight::getAltitude)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void findAllWhenUnauthorizedResultThenDenies() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tAuthentication pilot = TestAuthentication.authenticatedUser(authorities(\"seating:read\"));\n\t\tStepVerifier\n\t\t\t.create(flights.findAll()\n\t\t\t\t.flatMap(Flight::getSeats)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.expectNextCount(2)\n\t\t\t.verifyComplete();\n\t\tStepVerifier\n\t\t\t.create(flights.findAll()\n\t\t\t\t.flatMap(Flight::getAltitude)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizedResultThenRemoves() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tAuthentication pilot = TestAuthentication.authenticatedUser(authorities(\"seating:read\"));\n\t\tStepVerifier.create(flights.remove(\"1\").contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void findAllWhenPostFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tAuthentication pilot = TestAuthentication.authenticatedUser(authorities(\"airplane:read\"));\n\t\tStepVerifier\n\t\t\t.create(flights.findAll()\n\t\t\t\t.flatMap(Flight::getPassengers)\n\t\t\t\t.flatMap(Passenger::getName)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.expectNext(\"Marie Curie\", \"Ada Lovelace\", \"Albert Einstein\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void findAllWhenPreFilterThenFilters() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tAuthentication pilot = TestAuthentication.authenticatedUser(authorities(\"airplane:read\"));\n\t\tStepVerifier\n\t\t\t.create(flights.findAll()\n\t\t\t\t.flatMap((flight) -> flight.board(Flux.just(\"John Doe\", \"John\")).then(Mono.just(flight)))\n\t\t\t\t.flatMap(Flight::getPassengers)\n\t\t\t\t.flatMap(Passenger::getName)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.expectNext(\"Marie Curie\", \"Ada Lovelace\", \"John Doe\", \"Albert Einstein\", \"John Doe\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void findAllWhenNestedPreAuthorizeThenAuthorizes() {\n\t\tthis.spring.register(AuthorizeResultConfig.class).autowire();\n\t\tFlightRepository flights = this.spring.getContext().getBean(FlightRepository.class);\n\t\tAuthentication pilot = TestAuthentication.authenticatedUser(authorities(\"seating:read\"));\n\t\tStepVerifier\n\t\t\t.create(flights.findAll()\n\t\t\t\t.flatMap(Flight::getPassengers)\n\t\t\t\t.flatMap(Passenger::getName)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(pilot)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getUserWhenNotAuthorizedThenHandlerUsesCustomAuthorizationDecision() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class, CustomResultConfig.class).autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tMethodAuthorizationDeniedHandler handler = this.spring.getContext()\n\t\t\t.getBean(MethodAuthorizationDeniedHandler.class);\n\t\tassertThat(service.checkCustomResult(false).block()).isNull();\n\t\tverify(handler).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));\n\t\tverify(handler, never()).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));\n\t\tclearInvocations(handler);\n\t\tassertThat(service.checkCustomResult(true).block()).isNull();\n\t\tverify(handler).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));\n\t\tverify(handler, never()).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));\n\t}\n\n\t@Test\n\tpublic void prePostMethodWhenObservationRegistryThenObserved() {\n\t\tthis.spring.register(MethodSecurityServiceConfig.class, ObservationRegistryConfig.class).autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tAuthentication user = TestAuthentication.authenticatedUser();\n\t\tStepVerifier\n\t\t\t.create(service.preAuthorizeUser().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))\n\t\t\t.expectNextCount(1)\n\t\t\t.verifyComplete();\n\t\tObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tverify(handler).onStart(any());\n\t\tverify(handler).onStop(any());\n\t\tStepVerifier\n\t\t\t.create(service.preAuthorizeAdmin().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))\n\t\t\t.expectError()\n\t\t\t.verify();\n\t\tverify(handler).onError(any());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void prePostMethodWhenExcludeAuthorizationObservationsThenUnobserved() {\n\t\tthis.spring\n\t\t\t.register(MethodSecurityServiceConfig.class, ObservationRegistryConfig.class,\n\t\t\t\t\tSelectableObservationsConfig.class)\n\t\t\t.autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tAuthentication user = TestAuthentication.authenticatedUser();\n\t\tStepVerifier\n\t\t\t.create(service.preAuthorizeUser().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))\n\t\t\t.expectNextCount(1)\n\t\t\t.verifyComplete();\n\t\tObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tStepVerifier\n\t\t\t.create(service.preAuthorizeAdmin().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))\n\t\t\t.expectError()\n\t\t\t.verify();\n\t\tverifyNoInteractions(handler);\n\t}\n\n\t@Test\n\tvoid checkCustomManagerWhenInvokedThenUsesBeanToAuthorize() {\n\t\tthis.spring.register(WithRolePrefixConfiguration.class, MethodSecurityServiceConfig.class).autowire();\n\t\tReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);\n\t\tservice.checkCustomManager(2).block();\n\t\tassertThatExceptionOfType(AuthorizationDeniedException.class)\n\t\t\t.isThrownBy(() -> service.checkCustomManager(1).block());\n\t}\n\n\tprivate static Consumer<User.UserBuilder> authorities(String... authorities) {\n\t\treturn (builder) -> builder.authorities(authorities);\n\t}\n\n\t@Configuration\n\t@EnableReactiveMethodSecurity // this imports ReactiveMethodSecurityConfiguration\n\tstatic class WithRolePrefixConfiguration {\n\n\t\t@Bean\n\t\tGrantedAuthorityDefaults grantedAuthorityDefaults() {\n\t\t\treturn new GrantedAuthorityDefaults(\"CUSTOM_\");\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SubclassConfig extends ReactiveMethodSecurityConfiguration {\n\n\t}\n\n\tstatic class Foo {\n\n\t\tpublic void bar(String param) {\n\t\t}\n\n\t}\n\n\t@EnableReactiveMethodSecurity\n\t@Configuration\n\tstatic class AuthorizeResultConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tstatic Customizer<AuthorizationAdvisorProxyFactory> skipValueTypes() {\n\t\t\treturn (factory) -> factory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());\n\t\t}\n\n\t\t@Bean\n\t\tFlightRepository flights() {\n\t\t\tFlightRepository flights = new FlightRepository();\n\t\t\tFlight one = new Flight(\"1\", 35000d, 35);\n\t\t\tone.board(Flux.just(\"Marie Curie\", \"Kevin Mitnick\", \"Ada Lovelace\")).block();\n\t\t\tflights.save(one).block();\n\t\t\tFlight two = new Flight(\"2\", 32000d, 72);\n\t\t\ttwo.board(Flux.just(\"Albert Einstein\")).block();\n\t\t\tflights.save(two).block();\n\t\t\treturn flights;\n\t\t}\n\n\t\t@Bean\n\t\tFunction<Passenger, Mono<Boolean>> isNotKevin() {\n\t\t\treturn (passenger) -> passenger.getName().map((name) -> !name.equals(\"Kevin Mitnick\"));\n\t\t}\n\n\t}\n\n\t@AuthorizeReturnObject\n\tstatic class FlightRepository {\n\n\t\tprivate final Map<String, Flight> flights = new ConcurrentHashMap<>();\n\n\t\tFlux<Flight> findAll() {\n\t\t\treturn Flux.fromIterable(this.flights.values());\n\t\t}\n\n\t\tMono<Flight> findById(String id) {\n\t\t\treturn Mono.just(this.flights.get(id));\n\t\t}\n\n\t\tMono<Flight> save(Flight flight) {\n\t\t\tthis.flights.put(flight.getId(), flight);\n\t\t\treturn Mono.just(flight);\n\t\t}\n\n\t\tMono<Void> remove(String id) {\n\t\t\tthis.flights.remove(id);\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t}\n\n\t@AuthorizeReturnObject\n\tstatic class Flight {\n\n\t\tprivate final String id;\n\n\t\tprivate final Double altitude;\n\n\t\tprivate final Integer seats;\n\n\t\tprivate final List<Passenger> passengers = new ArrayList<>();\n\n\t\tFlight(String id, Double altitude, Integer seats) {\n\t\t\tthis.id = id;\n\t\t\tthis.altitude = altitude;\n\t\t\tthis.seats = seats;\n\t\t}\n\n\t\tString getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('airplane:read')\")\n\t\tMono<Double> getAltitude() {\n\t\t\treturn Mono.just(this.altitude);\n\t\t}\n\n\t\t@PreAuthorize(\"hasAnyAuthority('seating:read', 'airplane:read')\")\n\t\tMono<Integer> getSeats() {\n\t\t\treturn Mono.just(this.seats);\n\t\t}\n\n\t\t@PostAuthorize(\"hasAnyAuthority('seating:read', 'airplane:read')\")\n\t\t@PostFilter(\"@isNotKevin.apply(filterObject)\")\n\t\tFlux<Passenger> getPassengers() {\n\t\t\treturn Flux.fromIterable(this.passengers);\n\t\t}\n\n\t\t@PreAuthorize(\"hasAnyAuthority('seating:read', 'airplane:read')\")\n\t\t@PreFilter(\"filterObject.contains(' ')\")\n\t\tMono<Void> board(Flux<String> passengers) {\n\t\t\treturn passengers.doOnNext((passenger) -> this.passengers.add(new Passenger(passenger))).then();\n\t\t}\n\n\t}\n\n\tpublic static class Passenger {\n\n\t\tString name;\n\n\t\tpublic Passenger(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@PreAuthorize(\"hasAuthority('airplane:read')\")\n\t\tpublic Mono<String> getName() {\n\t\t\treturn Mono.just(this.name);\n\t\t}\n\n\t}\n\n\t@EnableReactiveMethodSecurity\n\tstatic class CustomResultConfig {\n\n\t\tMethodAuthorizationDeniedHandler handler = mock(MethodAuthorizationDeniedHandler.class);\n\n\t\t@Bean\n\t\tMethodAuthorizationDeniedHandler methodAuthorizationDeniedHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableReactiveMethodSecurity\n\tstatic class ObservationRegistryConfig {\n\n\t\tprivate final ObservationRegistry registry = ObservationRegistry.create();\n\n\t\tprivate final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\treturn this.registry;\n\t\t}\n\n\t\t@Bean\n\t\tObservationHandler<Observation.Context> observationHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t\t@Bean\n\t\tObservationRegistryPostProcessor observationRegistryPostProcessor(\n\t\t\t\tObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\treturn new ObservationRegistryPostProcessor(handler);\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryPostProcessor implements BeanPostProcessor {\n\n\t\tprivate final ObjectProvider<ObservationHandler<Observation.Context>> handler;\n\n\t\tObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\tthis.handler = handler;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tif (bean instanceof ObservationRegistry registry) {\n\t\t\t\tregistry.observationConfig().observationHandler(this.handler.getObject());\n\t\t\t}\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SelectableObservationsConfig {\n\n\t\t@Bean\n\t\tSecurityObservationSettings observabilityDefaults() {\n\t\t\treturn SecurityObservationSettings.withDefaults().shouldObserveAuthorizations(false).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.List;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.method.HandleAuthorizationDenied;\nimport org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Rob Winch\n */\n@ReactiveMethodSecurityService.Mask(\"classmask\")\npublic interface ReactiveMethodSecurityService {\n\n\t@PreAuthorize(\"hasRole('USER')\")\n\tMono<String> preAuthorizeUser();\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tMono<String> preAuthorizeAdmin();\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)\n\tMono<String> preAuthorizeGetCardNumberIfAdmin(String cardNumber);\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = StartMaskingHandlerChild.class)\n\tMono<String> preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber);\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)\n\tMono<String> preAuthorizeThrowAccessDeniedManually();\n\n\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = CardNumberMaskingPostProcessor.class)\n\tMono<String> postAuthorizeGetCardNumberIfAdmin(String cardNumber);\n\n\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = PostMaskingPostProcessor.class)\n\tMono<String> postAuthorizeThrowAccessDeniedManually();\n\n\t@PreAuthorize(\"denyAll()\")\n\t@Mask(\"methodmask\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)\n\tMono<String> preAuthorizeDeniedMethodWithMaskAnnotation();\n\n\t@PreAuthorize(\"denyAll()\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)\n\tMono<String> preAuthorizeDeniedMethodWithNoMaskAnnotation();\n\n\t@NullDenied(role = \"ADMIN\")\n\tMono<String> postAuthorizeDeniedWithNullDenied();\n\n\t@PostAuthorize(\"denyAll()\")\n\t@Mask(\"methodmask\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)\n\tMono<String> postAuthorizeDeniedMethodWithMaskAnnotation();\n\n\t@PostAuthorize(\"denyAll()\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)\n\tMono<String> postAuthorizeDeniedMethodWithNoMaskAnnotation();\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@Mask(expression = \"@myMasker.getMask()\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)\n\tMono<String> preAuthorizeWithMaskAnnotationUsingBean();\n\n\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t@Mask(expression = \"@myMasker.getMask(returnObject)\")\n\t@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)\n\tMono<String> postAuthorizeWithMaskAnnotationUsingBean();\n\n\t@PreAuthorize(\"@authz.checkReactiveResult(#result)\")\n\t@PostAuthorize(\"@authz.checkReactiveResult(!#result)\")\n\t@HandleAuthorizationDenied(handlerClass = MethodAuthorizationDeniedHandler.class)\n\tMono<String> checkCustomResult(boolean result);\n\n\t@PreAuthorize(\"@authz.checkReactiveManager(#id)\")\n\tMono<String> checkCustomManager(long id);\n\n\t@PreAuthorize(\"hasPermission(#kgName, 'read')\")\n\tMono<String> preAuthorizeHasPermission(String kgName);\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t@PreFilter(\"true\")\n\t@PostFilter(\"true\")\n\tMono<List<String>> manyAnnotations(Mono<List<String>> array);\n\n\tclass StarMaskingHandler implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {\n\t\t\treturn \"***\";\n\t\t}\n\n\t}\n\n\tclass StartMaskingHandlerChild extends StarMaskingHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {\n\t\t\treturn super.handleDeniedInvocation(methodInvocation, result) + \"-child\";\n\t\t}\n\n\t}\n\n\tclass MaskAnnotationHandler implements MethodAuthorizationDeniedHandler {\n\n\t\tMaskValueResolver maskValueResolver;\n\n\t\tMaskAnnotationHandler(ApplicationContext context) {\n\t\t\tthis.maskValueResolver = new MaskValueResolver(context);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {\n\t\t\tMask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);\n\t\t\tif (mask == null) {\n\t\t\t\tmask = AnnotationUtils.getAnnotation(methodInvocation.getMethod().getDeclaringClass(), Mask.class);\n\t\t\t}\n\t\t\treturn this.maskValueResolver.resolveValue(mask, methodInvocation, null);\n\t\t}\n\n\t}\n\n\tclass MaskAnnotationPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\tMaskValueResolver maskValueResolver;\n\n\t\tMaskAnnotationPostProcessor(ApplicationContext context) {\n\t\t\tthis.maskValueResolver = new MaskValueResolver(context);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation mi, AuthorizationResult authorizationResult) {\n\t\t\tMask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);\n\t\t\tif (mask == null) {\n\t\t\t\tmask = AnnotationUtils.getAnnotation(mi.getMethod().getDeclaringClass(), Mask.class);\n\t\t\t}\n\t\t\treturn this.maskValueResolver.resolveValue(mask, mi, null);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\tMethodInvocation mi = methodInvocationResult.getMethodInvocation();\n\t\t\tMask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);\n\t\t\tif (mask == null) {\n\t\t\t\tmask = AnnotationUtils.getAnnotation(mi.getMethod().getDeclaringClass(), Mask.class);\n\t\t\t}\n\t\t\treturn this.maskValueResolver.resolveValue(mask, mi, methodInvocationResult.getResult());\n\t\t}\n\n\t}\n\n\tclass MaskValueResolver {\n\n\t\tDefaultMethodSecurityExpressionHandler expressionHandler;\n\n\t\tMaskValueResolver(ApplicationContext context) {\n\t\t\tthis.expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\t\tthis.expressionHandler.setApplicationContext(context);\n\t\t}\n\n\t\tMono<String> resolveValue(Mask mask, MethodInvocation mi, Object returnObject) {\n\t\t\tif (StringUtils.hasText(mask.value())) {\n\t\t\t\treturn Mono.just(mask.value());\n\t\t\t}\n\t\t\tExpression expression = this.expressionHandler.getExpressionParser().parseExpression(mask.expression());\n\t\t\tEvaluationContext evaluationContext = this.expressionHandler\n\t\t\t\t.createEvaluationContext(() -> SecurityContextHolder.getContext().getAuthentication(), mi);\n\t\t\tif (returnObject != null) {\n\t\t\t\tthis.expressionHandler.setReturnObject(returnObject, evaluationContext);\n\t\t\t}\n\t\t\treturn Mono.just(expression.getValue(evaluationContext, String.class));\n\t\t}\n\n\t}\n\n\tclass PostMaskingPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn \"***\";\n\t\t}\n\n\t}\n\n\tclass CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\tstatic String MASK = \"****-****-****-\";\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn \"***\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocationResult(MethodInvocationResult contextObject, AuthorizationResult result) {\n\t\t\tString cardNumber = (String) contextObject.getResult();\n\t\t\treturn MASK + cardNumber.substring(cardNumber.length() - 4);\n\t\t}\n\n\t}\n\n\tclass NullPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@interface Mask {\n\n\t\tString value() default \"\";\n\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@PostAuthorize(\"hasRole('{value}')\")\n\t@HandleAuthorizationDenied(handlerClass = NullPostProcessor.class)\n\t@interface NullDenied {\n\n\t\tString role();\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMethodSecurityServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.util.List;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\n\npublic class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurityService {\n\n\t@Override\n\tpublic Mono<String> preAuthorizeUser() {\n\t\treturn Mono.just(\"user\");\n\t}\n\n\t@Override\n\tpublic Mono<String> preAuthorizeAdmin() {\n\t\treturn Mono.just(\"admin\");\n\t}\n\n\t@Override\n\tpublic Mono<String> preAuthorizeGetCardNumberIfAdmin(String cardNumber) {\n\t\treturn Mono.just(cardNumber);\n\t}\n\n\t@Override\n\tpublic Mono<String> preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber) {\n\t\treturn Mono.just(cardNumber);\n\t}\n\n\t@Override\n\tpublic Mono<String> preAuthorizeThrowAccessDeniedManually() {\n\t\treturn Mono.error(new AuthorizationDeniedException(\"Access Denied\", new AuthorizationDecision(false)));\n\t}\n\n\t@Override\n\tpublic Mono<String> postAuthorizeGetCardNumberIfAdmin(String cardNumber) {\n\t\treturn Mono.just(cardNumber);\n\t}\n\n\t@Override\n\tpublic Mono<String> postAuthorizeThrowAccessDeniedManually() {\n\t\treturn Mono.error(new AuthorizationDeniedException(\"Access Denied\", new AuthorizationDecision(false)));\n\t}\n\n\t@Override\n\tpublic Mono<String> preAuthorizeDeniedMethodWithMaskAnnotation() {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> preAuthorizeDeniedMethodWithNoMaskAnnotation() {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> postAuthorizeDeniedWithNullDenied() {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> postAuthorizeDeniedMethodWithMaskAnnotation() {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> postAuthorizeDeniedMethodWithNoMaskAnnotation() {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> preAuthorizeWithMaskAnnotationUsingBean() {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> postAuthorizeWithMaskAnnotationUsingBean() {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> checkCustomResult(boolean result) {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> checkCustomManager(long id) {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<String> preAuthorizeHasPermission(String kgName) {\n\t\treturn Mono.just(\"ok\");\n\t}\n\n\t@Override\n\tpublic Mono<List<String>> manyAnnotations(Mono<List<String>> array) {\n\t\treturn array;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/RequireAdminRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('ADMIN')\")\npublic @interface RequireAdminRole {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/RequireUserRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('USER')\")\npublic @interface RequireUserRole {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/SampleEnableGlobalMethodSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport java.io.Serializable;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Demonstrate the samples\n *\n * @author Rob Winch\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SampleEnableGlobalMethodSecurityTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MethodSecurityService methodSecurityService;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void preAuthorize() {\n\t\tthis.spring.register(SampleWebSecurityConfig.class).autowire();\n\t\tassertThat(this.methodSecurityService.secured()).isNull();\n\t\tassertThat(this.methodSecurityService.jsr250()).isNull();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.preAuthorize());\n\t}\n\n\t@Test\n\tpublic void customPermissionHandler() {\n\t\tthis.spring.register(CustomPermissionEvaluatorWebSecurityConfig.class).autowire();\n\t\tassertThat(this.methodSecurityService.hasPermission(\"allowed\")).isNull();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.hasPermission(\"denied\"));\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class SampleWebSecurityConfig {\n\n\t\t@Bean\n\t\tMethodSecurityService methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Autowired\n\t\tprotected void configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\").and()\n\t\t\t\t\t.withUser(\"admin\").password(\"password\").roles(\"USER\", \"ADMIN\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tpublic static class CustomPermissionEvaluatorWebSecurityConfig extends GlobalMethodSecurityConfiguration {\n\n\t\t@Bean\n\t\tpublic MethodSecurityService methodSecurityService() {\n\t\t\treturn new MethodSecurityServiceImpl();\n\t\t}\n\n\t\t@Override\n\t\tprotected MethodSecurityExpressionHandler createExpressionHandler() {\n\t\t\tDefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\t\texpressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());\n\t\t\treturn expressionHandler;\n\t\t}\n\n\t\t@Override\n\t\tprotected void configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\").and()\n\t\t\t\t.withUser(\"admin\").password(\"password\").roles(\"USER\", \"ADMIN\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tstatic class CustomPermissionEvaluator implements PermissionEvaluator {\n\n\t\t@Override\n\t\tpublic boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n\t\t\treturn !\"denied\".equals(targetDomainObject);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\t\tObject permission) {\n\t\t\treturn !\"denied\".equals(targetId);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/UserRecordWithEmailProtected.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration;\n\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.method.HandleAuthorizationDenied;\nimport org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\n\npublic class UserRecordWithEmailProtected {\n\n\tprivate final String name;\n\n\tprivate final String email;\n\n\tpublic UserRecordWithEmailProtected(String name, String email) {\n\t\tthis.name = name;\n\t\tthis.email = email;\n\t}\n\n\tpublic String name() {\n\t\treturn this.name;\n\t}\n\n\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t@HandleAuthorizationDenied(handlerClass = EmailMaskingPostProcessor.class)\n\tpublic String email() {\n\t\treturn this.email;\n\t}\n\n\tpublic static class EmailMaskingPostProcessor implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn \"***\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\tString email = (String) methodInvocationResult.getResult();\n\t\t\treturn email.replaceAll(\"(^[^@]{3}|(?!^)\\\\G)[^@]\", \"$1*\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/aot/EnableMethodSecurityAotTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.aot;\n\nimport javax.sql.DataSource;\n\nimport jakarta.persistence.EntityManager;\nimport org.jspecify.annotations.NonNull;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.aot.generate.GenerationContext;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.aot.test.generate.TestGenerationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.aot.ApplicationContextAotGenerator;\nimport org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;\nimport org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * AOT Tests for {@code PrePostMethodSecurityConfiguration}.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\npublic class EnableMethodSecurityAotTests {\n\n\tprivate final ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();\n\n\tprivate final GenerationContext context = new TestGenerationContext();\n\n\t@Test\n\tvoid whenProcessAheadOfTimeThenCreatesAuthorizationProxies() {\n\t\tAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();\n\t\tcontext.register(AppConfig.class);\n\t\tthis.generator.processAheadOfTime(context, this.context);\n\t\tRuntimeHints hints = this.context.getRuntimeHints();\n\t\tassertThat(hints.reflection().getTypeHint(TypeReference.of(cglibClassName(Message.class)))).isNotNull();\n\t\tassertThat(hints.reflection().getTypeHint(TypeReference.of(cglibClassName(User.class)))).isNotNull();\n\t\tassertThat(hints.proxies()\n\t\t\t.jdkProxyHints()\n\t\t\t.anyMatch((hint) -> hint.getProxiedInterfaces().contains(TypeReference.of(UserProjection.class)))).isTrue();\n\t}\n\n\tprivate static String cglibClassName(Class<?> clazz) {\n\t\treturn clazz.getCanonicalName() + \"$$SpringCGLIB$$0\";\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\tstatic class AppConfig {\n\n\t\t@Bean\n\t\tDataSource dataSource() {\n\t\t\tEmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();\n\t\t\treturn builder.setType(EmbeddedDatabaseType.HSQL).build();\n\t\t}\n\n\t\t@Bean\n\t\tLocalContainerEntityManagerFactoryBean entityManagerFactory() {\n\t\t\tHibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();\n\t\t\tvendorAdapter.setGenerateDdl(true);\n\t\t\tLocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();\n\t\t\tfactory.setJpaVendorAdapter(vendorAdapter);\n\t\t\tfactory.setPackagesToScan(\"org.springframework.security.config.annotation.method.configuration.aot\");\n\t\t\tfactory.setDataSource(dataSource());\n\t\t\treturn factory;\n\t\t}\n\n\t\t@Bean\n\t\tJpaRepositoryFactoryBean<@NonNull MessageRepository, Message, Long> repo(EntityManager entityManager) {\n\t\t\tJpaRepositoryFactoryBean<@NonNull MessageRepository, Message, Long> bean = new JpaRepositoryFactoryBean<>(\n\t\t\t\t\tMessageRepository.class);\n\t\t\tbean.setEntityManager(entityManager);\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/aot/Message.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.aot;\n\nimport java.time.Instant;\n\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.GeneratedValue;\nimport jakarta.persistence.GenerationType;\nimport jakarta.persistence.Id;\nimport jakarta.persistence.ManyToOne;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\n\n@Entity\npublic class Message {\n\n\t@Id\n\t@GeneratedValue(strategy = GenerationType.AUTO)\n\tprivate Long id;\n\n\tprivate String text;\n\n\tprivate String summary;\n\n\tprivate Instant created = Instant.now();\n\n\t@ManyToOne\n\tprivate User to;\n\n\t@AuthorizeReturnObject\n\tpublic User getTo() {\n\t\treturn this.to;\n\t}\n\n\tpublic void setTo(User to) {\n\t\tthis.to = to;\n\t}\n\n\tpublic Long getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic void setId(Long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic Instant getCreated() {\n\t\treturn this.created;\n\t}\n\n\tpublic void setCreated(Instant created) {\n\t\tthis.created = created;\n\t}\n\n\t@PreAuthorize(\"hasAuthority('message:read')\")\n\tpublic String getText() {\n\t\treturn this.text;\n\t}\n\n\tpublic void setText(String text) {\n\t\tthis.text = text;\n\t}\n\n\t@PreAuthorize(\"hasAuthority('message:read')\")\n\tpublic String getSummary() {\n\t\treturn this.summary;\n\t}\n\n\tpublic void setSummary(String summary) {\n\t\tthis.summary = summary;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/aot/MessageRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.aot;\n\nimport org.springframework.data.jpa.repository.Query;\nimport org.springframework.data.repository.CrudRepository;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.stereotype.Repository;\n\n/**\n * A repository for accessing {@link Message}s.\n *\n * @author Rob Winch\n */\n@Repository\n@AuthorizeReturnObject\npublic interface MessageRepository extends CrudRepository<Message, Long> {\n\n\t@Query(\"select m from Message m where m.to.id = ?#{ authentication.name }\")\n\tIterable<Message> findAll();\n\n\t@Query(\"from org.springframework.security.config.annotation.method.configuration.aot.User u where u.id = ?#{ authentication.name }\")\n\tUserProjection findCurrentUser();\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/aot/User.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.aot;\n\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.Id;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * A user.\n *\n * @author Rob Winch\n */\n@Entity(name = \"users\")\npublic class User {\n\n\t@Id\n\tprivate String id;\n\n\tprivate String firstName;\n\n\tprivate String lastName;\n\n\tprivate String email;\n\n\tprivate String password;\n\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic void setId(String id) {\n\t\tthis.id = id;\n\t}\n\n\t@PreAuthorize(\"hasAuthority('user:read')\")\n\tpublic String getFirstName() {\n\t\treturn this.firstName;\n\t}\n\n\tpublic void setFirstName(String firstName) {\n\t\tthis.firstName = firstName;\n\t}\n\n\t@PreAuthorize(\"hasAuthority('user:read')\")\n\tpublic String getLastName() {\n\t\treturn this.lastName;\n\t}\n\n\tpublic void setLastName(String lastName) {\n\t\tthis.lastName = lastName;\n\t}\n\n\tpublic String getEmail() {\n\t\treturn this.email;\n\t}\n\n\tpublic void setEmail(String email) {\n\t\tthis.email = email;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn this.password;\n\t}\n\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/aot/UserProjection.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.aot;\n\npublic interface UserProjection {\n\n\tString getFirstName();\n\n\tString getLastName();\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/issue14637/ApplicationConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.issue14637;\n\nimport javax.sql.DataSource;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.jpa.repository.config.EnableJpaRepositories;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.orm.jpa.JpaTransactionManager;\nimport org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;\nimport org.springframework.orm.jpa.vendor.Database;\nimport org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;\nimport org.springframework.security.config.annotation.method.configuration.issue14637.domain.Entry;\nimport org.springframework.transaction.PlatformTransactionManager;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n/**\n * @author Josh Cummings\n */\n@Configuration\n@EnableJpaRepositories(\"org.springframework.security.config.annotation.method.configuration.issue14637.repo\")\n@EnableTransactionManagement\npublic class ApplicationConfig {\n\n\t@Bean\n\tpublic DataSource dataSource() {\n\t\tEmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();\n\t\treturn builder.setType(EmbeddedDatabaseType.HSQL).build();\n\t}\n\n\t@Bean\n\tpublic LocalContainerEntityManagerFactoryBean entityManagerFactory() {\n\t\tHibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();\n\t\tvendorAdapter.setDatabase(Database.HSQL);\n\t\tvendorAdapter.setGenerateDdl(true);\n\t\tvendorAdapter.setShowSql(true);\n\t\tLocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();\n\t\tfactory.setJpaVendorAdapter(vendorAdapter);\n\t\tfactory.setPackagesToScan(Entry.class.getPackage().getName());\n\t\tfactory.setDataSource(dataSource());\n\t\treturn factory;\n\t}\n\n\t@Bean\n\tpublic PlatformTransactionManager transactionManager() {\n\t\tJpaTransactionManager txManager = new JpaTransactionManager();\n\t\ttxManager.setEntityManagerFactory(entityManagerFactory().getObject());\n\t\treturn txManager;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/issue14637/Issue14637Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.issue14637;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.config.annotation.method.configuration.issue14637.domain.Entry;\nimport org.springframework.security.config.annotation.method.configuration.issue14637.repo.EntryRepository;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = { ApplicationConfig.class, SecurityConfig.class })\npublic class Issue14637Tests {\n\n\t@Autowired\n\tprivate EntryRepository entries;\n\n\t@Test\n\t@WithMockUser\n\tpublic void authenticateWhenInvalidPasswordThenBadCredentialsException() {\n\t\tEntry entry = new Entry();\n\t\tentry.setId(123L);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.entries.save(entry));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/issue14637/SecurityConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.issue14637;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\n\n/**\n * @author Josh Cummings\n */\n@Configuration\n@EnableMethodSecurity\npublic class SecurityConfig {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/issue14637/domain/Entry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.issue14637.domain;\n\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.GeneratedValue;\nimport jakarta.persistence.GenerationType;\nimport jakarta.persistence.Id;\n\n/**\n * @author Josh Cummings\n */\n@Entity\npublic class Entry {\n\n\t@Id\n\t@GeneratedValue(strategy = GenerationType.AUTO)\n\tprivate Long id;\n\n\tpublic Long getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic void setId(Long id) {\n\t\tthis.id = id;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/method/configuration/issue14637/repo/EntryRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration.issue14637.repo;\n\nimport org.springframework.data.repository.CrudRepository;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.config.annotation.method.configuration.issue14637.domain.Entry;\n\n/**\n * @author Josh Cummings\n */\npublic interface EntryRepository extends CrudRepository<Entry, String> {\n\n\t@PreAuthorize(\"#entry.id == null\")\n\tEntry save(Entry entry);\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/sec2758/Sec2758Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.sec2758;\n\nimport jakarta.annotation.security.RolesAllowed;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.PriorityOrdered;\nimport org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;\nimport org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;\nimport org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Josh Cummings\n *\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class Sec2758Tests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Autowired(required = false)\n\tService service;\n\n\t@WithMockUser(authorities = \"CUSTOM\")\n\t@Test\n\tpublic void requestWhenNullifyingRolePrefixThenPassivityRestored() throws Exception {\n\t\tthis.spring.register(SecurityConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@WithMockUser(authorities = \"CUSTOM\")\n\t@Test\n\tpublic void methodSecurityWhenNullifyingRolePrefixThenPassivityRestored() {\n\t\tthis.spring.register(SecurityConfig.class).autowire();\n\t\tthis.service.doJsr250();\n\t\tthis.service.doPreAuthorize();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)\n\tstatic class SecurityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, WebExpressionAuthorizationManager.Builder authz)\n\t\t\t\tthrows Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().access(authz.expression(\"hasAnyRole('CUSTOM')\"))\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tService service() {\n\t\t\treturn new Service();\n\t\t}\n\n\t\t@Bean\n\t\tstatic DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {\n\t\t\treturn new DefaultRolesPrefixPostProcessor();\n\t\t}\n\n\t\t@Bean\n\t\tstatic WebExpressionAuthorizationManager.Builder authz(DefaultHttpSecurityExpressionHandler expressionHandler) {\n\t\t\treturn WebExpressionAuthorizationManager.withExpressionHandler(expressionHandler);\n\t\t}\n\n\t\t@Bean\n\t\tstatic DefaultHttpSecurityExpressionHandler expressionHandler() {\n\t\t\treturn new DefaultHttpSecurityExpressionHandler();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class RootController {\n\n\t\t\t@GetMapping(\"/\")\n\t\t\tString ok() {\n\t\t\t\treturn \"ok\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic class Service {\n\n\t\t@PreAuthorize(\"hasRole('CUSTOM')\")\n\t\tvoid doPreAuthorize() {\n\t\t}\n\n\t\t@RolesAllowed(\"CUSTOM\")\n\t\tvoid doJsr250() {\n\t\t}\n\n\t}\n\n\tstatic class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {\n\n\t\t@Override\n\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tif (bean instanceof Jsr250MethodSecurityMetadataSource) {\n\t\t\t\t((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);\n\t\t\t}\n\t\t\tif (bean instanceof DefaultMethodSecurityExpressionHandler) {\n\t\t\t\t((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);\n\t\t\t}\n\t\t\tif (bean instanceof DefaultWebSecurityExpressionHandler) {\n\t\t\t\t((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);\n\t\t\t}\n\t\t\tif (bean instanceof DefaultHttpSecurityExpressionHandler http) {\n\t\t\t\thttp.setDefaultRolePrefix(\"\");\n\t\t\t}\n\t\t\treturn bean;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n\t\t\treturn bean;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getOrder() {\n\t\t\treturn Ordered.HIGHEST_PRECEDENCE;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;\nimport org.springframework.security.config.annotation.SecurityConfigurer;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link AbstractConfiguredSecurityBuilder}.\n *\n * @author Joe Grandja\n */\npublic class AbstractConfiguredSecurityBuilderTests {\n\n\tprivate TestConfiguredSecurityBuilder builder;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.builder = new TestConfiguredSecurityBuilder(mock(ObjectPostProcessor.class));\n\t}\n\n\t@Test\n\tpublic void constructorWhenObjectPostProcessorIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new TestConfiguredSecurityBuilder(null));\n\t}\n\n\t@Test\n\tpublic void objectPostProcessorWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.builder.objectPostProcessor(null));\n\t}\n\n\t@Test\n\tpublic void applyWhenDuplicateConfigurerAddedThenDuplicateConfigurerRemoved() throws Exception {\n\t\tthis.builder.with(new TestSecurityConfigurer());\n\t\tthis.builder.with(new TestSecurityConfigurer());\n\t\tassertThat(this.builder.getConfigurers(TestSecurityConfigurer.class)).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void buildWhenBuildTwiceThenThrowIllegalStateException() throws Exception {\n\t\tthis.builder.build();\n\t\tassertThatIllegalStateException().isThrownBy(() -> this.builder.build());\n\t}\n\n\t@Test\n\tpublic void getObjectWhenNotBuiltThenThrowIllegalStateException() {\n\t\tassertThatIllegalStateException().isThrownBy(this.builder::getObject);\n\t}\n\n\t@Test\n\tpublic void buildWhenConfigurerAppliesAnotherConfigurerThenObjectStillBuilds() throws Exception {\n\t\tDelegateSecurityConfigurer.CONFIGURER = mock(SecurityConfigurer.class);\n\t\tthis.builder.with(new DelegateSecurityConfigurer());\n\t\tthis.builder.build();\n\t\tverify(DelegateSecurityConfigurer.CONFIGURER).init(this.builder);\n\t\tverify(DelegateSecurityConfigurer.CONFIGURER).configure(this.builder);\n\t}\n\n\t@Test\n\tpublic void buildWhenConfigurerAppliesAndRemoveAnotherConfigurerThenNotConfigured() throws Exception {\n\t\tApplyAndRemoveSecurityConfigurer.CONFIGURER = mock(SecurityConfigurer.class);\n\t\tthis.builder.with(new ApplyAndRemoveSecurityConfigurer());\n\t\tthis.builder.build();\n\t\tverify(ApplyAndRemoveSecurityConfigurer.CONFIGURER, never()).init(this.builder);\n\t\tverify(ApplyAndRemoveSecurityConfigurer.CONFIGURER, never()).configure(this.builder);\n\t}\n\n\t@Test\n\tpublic void buildWhenConfigurerAppliesAndRemoveAnotherConfigurersThenNotConfigured() throws Exception {\n\t\tApplyAndRemoveAllSecurityConfigurer.CONFIGURER = mock(SecurityConfigurer.class);\n\t\tthis.builder.with(new ApplyAndRemoveAllSecurityConfigurer());\n\t\tthis.builder.build();\n\t\tverify(ApplyAndRemoveAllSecurityConfigurer.CONFIGURER, never()).init(this.builder);\n\t\tverify(ApplyAndRemoveAllSecurityConfigurer.CONFIGURER, never()).configure(this.builder);\n\t}\n\n\t@Test\n\tpublic void getConfigurerWhenMultipleConfigurersThenThrowIllegalStateException() throws Exception {\n\t\tTestConfiguredSecurityBuilder builder = new TestConfiguredSecurityBuilder(mock(ObjectPostProcessor.class),\n\t\t\t\ttrue);\n\t\tbuilder.with(new DelegateSecurityConfigurer());\n\t\tbuilder.with(new DelegateSecurityConfigurer());\n\t\tassertThatIllegalStateException().isThrownBy(() -> builder.getConfigurer(DelegateSecurityConfigurer.class));\n\t}\n\n\t@Test\n\tpublic void removeConfigurerWhenMultipleConfigurersThenThrowIllegalStateException() throws Exception {\n\t\tTestConfiguredSecurityBuilder builder = new TestConfiguredSecurityBuilder(mock(ObjectPostProcessor.class),\n\t\t\t\ttrue);\n\t\tbuilder.with(new DelegateSecurityConfigurer());\n\t\tbuilder.with(new DelegateSecurityConfigurer());\n\t\tassertThatIllegalStateException().isThrownBy(() -> builder.removeConfigurer(DelegateSecurityConfigurer.class));\n\t}\n\n\t@Test\n\tpublic void removeConfigurersWhenMultipleConfigurersThenConfigurersRemoved() throws Exception {\n\t\tDelegateSecurityConfigurer configurer1 = new DelegateSecurityConfigurer();\n\t\tDelegateSecurityConfigurer configurer2 = new DelegateSecurityConfigurer();\n\t\tTestConfiguredSecurityBuilder builder = new TestConfiguredSecurityBuilder(mock(ObjectPostProcessor.class),\n\t\t\t\ttrue);\n\t\tbuilder.with(configurer1);\n\t\tbuilder.with(configurer2);\n\t\tList<DelegateSecurityConfigurer> removedConfigurers = builder\n\t\t\t.removeConfigurers(DelegateSecurityConfigurer.class);\n\t\tassertThat(removedConfigurers).hasSize(2);\n\t\tassertThat(removedConfigurers).containsExactly(configurer1, configurer2);\n\t\tassertThat(builder.getConfigurers(DelegateSecurityConfigurer.class)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getConfigurersWhenMultipleConfigurersThenConfigurersReturned() throws Exception {\n\t\tDelegateSecurityConfigurer configurer1 = new DelegateSecurityConfigurer();\n\t\tDelegateSecurityConfigurer configurer2 = new DelegateSecurityConfigurer();\n\t\tTestConfiguredSecurityBuilder builder = new TestConfiguredSecurityBuilder(mock(ObjectPostProcessor.class),\n\t\t\t\ttrue);\n\t\tbuilder.with(configurer1);\n\t\tbuilder.with(configurer2);\n\t\tList<DelegateSecurityConfigurer> configurers = builder.getConfigurers(DelegateSecurityConfigurer.class);\n\t\tassertThat(configurers).hasSize(2);\n\t\tassertThat(configurers).containsExactly(configurer1, configurer2);\n\t\tassertThat(builder.getConfigurers(DelegateSecurityConfigurer.class)).hasSize(2);\n\t}\n\n\t@Test\n\tpublic void withWhenConfigurerThenConfigurerAdded() throws Exception {\n\t\tthis.builder.with(new TestSecurityConfigurer(), Customizer.withDefaults());\n\t\tassertThat(this.builder.getConfigurers(TestSecurityConfigurer.class)).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void withWhenDuplicateConfigurerAddedThenDuplicateConfigurerRemoved() throws Exception {\n\t\tthis.builder.with(new TestSecurityConfigurer(), Customizer.withDefaults());\n\t\tthis.builder.with(new TestSecurityConfigurer(), Customizer.withDefaults());\n\t\tassertThat(this.builder.getConfigurers(TestSecurityConfigurer.class)).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void withWhenConfigurerAddInitializing() throws Exception {\n\t\tthis.builder.with(new AppliesNestedConfigurer(), Customizer.withDefaults());\n\t\tassertThat(this.builder.build()).isEqualTo(\"success\");\n\t}\n\n\tprivate static class AppliesNestedConfigurer\n\t\t\textends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {\n\n\t\t@Override\n\t\tpublic void init(TestConfiguredSecurityBuilder builder) {\n\t\t\tbuilder.with(new NestedConfigurer(), Customizer.withDefaults());\n\t\t}\n\n\t}\n\n\tprivate static class NestedConfigurer extends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {\n\n\t\t@Override\n\t\tpublic void init(TestConfiguredSecurityBuilder http) {\n\t\t\thttp.with(new DoubleNestedConfigurer(), Customizer.withDefaults());\n\t\t}\n\n\t}\n\n\tprivate static class DoubleNestedConfigurer\n\t\t\textends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {\n\n\t}\n\n\tprivate static class ApplyAndRemoveSecurityConfigurer\n\t\t\textends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {\n\n\t\tprivate static SecurityConfigurer<Object, TestConfiguredSecurityBuilder> CONFIGURER;\n\n\t\t@Override\n\t\tpublic void init(TestConfiguredSecurityBuilder builder) {\n\t\t\tbuilder.apply(CONFIGURER);\n\t\t\tbuilder.removeConfigurer(CONFIGURER.getClass());\n\t\t}\n\n\t}\n\n\tprivate static class ApplyAndRemoveAllSecurityConfigurer\n\t\t\textends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {\n\n\t\tprivate static SecurityConfigurer<Object, TestConfiguredSecurityBuilder> CONFIGURER;\n\n\t\t@Override\n\t\tpublic void init(TestConfiguredSecurityBuilder builder) {\n\t\t\tbuilder.apply(CONFIGURER);\n\t\t\tbuilder.removeConfigurers(CONFIGURER.getClass());\n\t\t}\n\n\t}\n\n\tprivate static class DelegateSecurityConfigurer\n\t\t\textends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {\n\n\t\tprivate static SecurityConfigurer<Object, TestConfiguredSecurityBuilder> CONFIGURER;\n\n\t\t@Override\n\t\tpublic void init(TestConfiguredSecurityBuilder builder) {\n\t\t\tbuilder.apply(CONFIGURER);\n\t\t}\n\n\t}\n\n\tprivate static class TestSecurityConfigurer\n\t\t\textends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {\n\n\t}\n\n\tprivate static final class TestConfiguredSecurityBuilder\n\t\t\textends AbstractConfiguredSecurityBuilder<Object, TestConfiguredSecurityBuilder> {\n\n\t\tprivate TestConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor) {\n\t\t\tsuper(objectPostProcessor);\n\t\t}\n\n\t\tprivate TestConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor,\n\t\t\t\tboolean allowConfigurersOfSameType) {\n\t\t\tsuper(objectPostProcessor, allowConfigurersOfSameType);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object performBuild() {\n\t\t\treturn \"success\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryAnyMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RegexRequestMatcher;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests for {@link AbstractRequestMatcherRegistry}.\n *\n * @author Ankur Pathak\n */\npublic class AbstractRequestMatcherRegistryAnyMatcherTests {\n\n\t@Test\n\tpublic void antMatchersCanNotWorkAfterAnyRequest() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> loadConfig(AntMatchersAfterAnyRequestConfig.class));\n\t}\n\n\t@Test\n\tpublic void mvcMatchersCanNotWorkAfterAnyRequest() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> loadConfig(MvcMatchersAfterAnyRequestConfig.class));\n\t}\n\n\t@Test\n\tpublic void regexMatchersCanNotWorkAfterAnyRequest() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> loadConfig(RegexMatchersAfterAnyRequestConfig.class));\n\t}\n\n\t@Test\n\tpublic void anyRequestCanNotWorkAfterItself() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> loadConfig(AnyRequestAfterItselfConfig.class));\n\t}\n\n\t@Test\n\tpublic void requestMatchersCanNotWorkAfterAnyRequest() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> loadConfig(RequestMatchersAfterAnyRequestConfig.class));\n\t}\n\n\tprivate void loadConfig(Class<?>... configs) {\n\t\tAnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();\n\t\tcontext.setAllowCircularReferences(false);\n\t\tcontext.register(configs);\n\t\tcontext.setServletContext(new MockServletContext());\n\t\tcontext.refresh();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AntMatchersAfterAnyRequestConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t.requestMatchers(pathPattern(\"/demo/**\")).permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MvcMatchersAfterAnyRequestConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/demo/**\")).permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RegexMatchersAfterAnyRequestConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t.requestMatchers(new RegexRequestMatcher(\".*\", null)).permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AnyRequestAfterItselfConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t.anyRequest().permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestMatchersAfterAnyRequestConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t.requestMatchers(pathPattern(\"/**\")).permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryNoMvcTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.test.support.ClassPathExclusions;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link AbstractRequestMatcherRegistry} with no Spring MVC in the classpath\n *\n * @author Marcus Da Coregio\n */\n@ClassPathExclusions(\"spring-webmvc-*.jar\")\npublic class AbstractRequestMatcherRegistryNoMvcTests {\n\n\tprivate TestRequestMatcherRegistry matcherRegistry;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.matcherRegistry = new TestRequestMatcherRegistry();\n\t\tGenericApplicationContext context = new GenericApplicationContext();\n\t\tcontext.registerBean(PathPatternRequestMatcher.Builder.class, PathPatternRequestMatcher::withDefaults);\n\t\tcontext.refresh();\n\t\tthis.matcherRegistry.setApplicationContext(context);\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenPatternAndMvcNotPresentThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(\"/path\");\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenHttpMethodAndPatternAndMvcNotPresentThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET, \"/path\");\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenHttpMethodAndMvcNotPresentThenReturnAntPathMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET);\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\tprivate static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {\n\n\t\t@Override\n\t\tprotected List<RequestMatcher> chainRequestMatchers(List<RequestMatcher> requestMatchers) {\n\t\t\treturn requestMatchers;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport jakarta.servlet.DispatcherType;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;\nimport org.springframework.security.web.util.matcher.RegexRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests for {@link AbstractRequestMatcherRegistry}.\n *\n * @author Joe Grandja\n * @author Marcus Da Coregio\n */\npublic class AbstractRequestMatcherRegistryTests {\n\n\tprivate static final ObjectPostProcessor<Object> NO_OP_OBJECT_POST_PROCESSOR = new ObjectPostProcessor<Object>() {\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\t};\n\n\tprivate TestRequestMatcherRegistry matcherRegistry;\n\n\tprivate WebApplicationContext context;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.matcherRegistry = new TestRequestMatcherRegistry();\n\t\tthis.context = mock(WebApplicationContext.class);\n\t\tObjectProvider<ObjectPostProcessor<Object>> postProcessors = mock(ObjectProvider.class);\n\t\tResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class, Object.class);\n\t\tObjectProvider<ObjectPostProcessor<Object>> given = this.context.getBeanProvider(type);\n\t\tgiven(given).willReturn(postProcessors);\n\t\tgiven(postProcessors.getObject()).willReturn(NO_OP_OBJECT_POST_PROCESSOR);\n\t\tgiven(this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class))\n\t\t\t.willReturn(new SingleObjectProvider<>(PathPatternRequestMatcher.withDefaults()));\n\t\tthis.matcherRegistry.setApplicationContext(this.context);\n\t}\n\n\t@Test\n\tpublic void regexMatchersWhenHttpMethodAndPatternParamsThenReturnRegexRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry\n\t\t\t.requestMatchers(new RegexRequestMatcher(\"/a.*\", HttpMethod.GET.name()));\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(RegexRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void regexMatchersWhenPatternParamThenReturnRegexRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry\n\t\t\t.requestMatchers(new RegexRequestMatcher(\"/a.*\", null));\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(RegexRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void pathPatternWhenHttpMethodAndPatternParamsThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry\n\t\t\t.requestMatchers(pathPattern(HttpMethod.GET, \"/a.*\"));\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void pathPatternWhenPatternParamThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(pathPattern(\"/a.*\"));\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void dispatcherTypeMatchersWhenHttpMethodAndPatternParamsThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.dispatcherTypeMatchers(HttpMethod.GET,\n\t\t\t\tDispatcherType.ASYNC);\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void dispatcherMatchersWhenPatternParamThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.dispatcherTypeMatchers(DispatcherType.INCLUDE);\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenPatternAndMvcPresentThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(\"/path\");\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenHttpMethodAndPatternAndMvcPresentThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET, \"/path\");\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenHttpMethodAndMvcPresentThenReturnPathPatternRequestMatcherType() {\n\t\tList<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET);\n\t\tassertThat(requestMatchers).isNotEmpty();\n\t\tassertThat(requestMatchers).hasSize(1);\n\t\tassertThat(requestMatchers.get(0)).isExactlyInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\tprivate static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {\n\n\t\t@Override\n\t\tprotected List<RequestMatcher> chainRequestMatchers(List<RequestMatcher> requestMatchers) {\n\t\t\treturn requestMatchers;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class MockMvcConfiguration {\n\n\t}\n\n\tprivate static final class SingleObjectProvider<T> implements ObjectProvider<T> {\n\n\t\tprivate final T object;\n\n\t\tprivate SingleObjectProvider(T object) {\n\t\t\tthis.object = object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<T> stream() {\n\t\t\treturn Stream.of(this.object);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/HttpSecurityHeadersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web;\n\nimport jakarta.servlet.Filter;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class HttpSecurityHeadersTests {\n\n\t@Autowired\n\tWebApplicationContext wac;\n\n\t@Autowired\n\tFilter springSecurityFilterChain;\n\n\tMockMvc mockMvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilters(this.springSecurityFilterChain).build();\n\t}\n\n\t// gh-2953\n\t// gh-3975\n\t@Test\n\tpublic void headerWhenSpringMvcResourceThenCacheRelatedHeadersReset() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/resources/file.js\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, \"max-age=12345\"))\n\t\t\t\t.andExpect(header().doesNotExist(HttpHeaders.PRAGMA))\n\t\t\t\t.andExpect(header().doesNotExist(HttpHeaders.EXPIRES));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void headerWhenNotSpringResourceThenCacheRelatedHeadersSet() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/notresource\"))\n\t\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\"))\n\t\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, \"no-cache\"))\n\t\t\t\t.andExpect(header().string(HttpHeaders.EXPIRES, \"0\"));\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebSecurityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebMvc\n\t@Configuration\n\tstatic class WebMvcConfig implements WebMvcConfigurer {\n\n\t\t@Override\n\t\tpublic void addResourceHandlers(ResourceHandlerRegistry registry) {\n\t\t\t// @formatter:off\n\t\t\tregistry.addResourceHandler(\"/resources/**\")\n\t\t\t\t\t.addResourceLocations(\"classpath:/resources/\")\n\t\t\t\t\t.setCachePeriod(12345);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.web.access.channel.ChannelProcessingFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class FilterOrderRegistrationTests {\n\n\tprivate final FilterOrderRegistration filterOrderRegistration = new FilterOrderRegistration();\n\n\t@Test\n\tpublic void putWhenNewFilterThenInsertCorrect() {\n\t\tint position = 153;\n\t\tthis.filterOrderRegistration.put(MyFilter.class, position);\n\t\tInteger order = this.filterOrderRegistration.getOrder(MyFilter.class);\n\t\tassertThat(order).isEqualTo(position);\n\t}\n\n\t@Test\n\tpublic void putWhenCustomFilterAlreadyExistsThenDoesNotOverride() {\n\t\tint position = 160;\n\t\tthis.filterOrderRegistration.put(MyFilter.class, position);\n\t\tthis.filterOrderRegistration.put(MyFilter.class, 173);\n\t\tInteger order = this.filterOrderRegistration.getOrder(MyFilter.class);\n\t\tassertThat(order).isEqualTo(position);\n\t}\n\n\t@Test\n\tpublic void putWhenPredefinedFilterThenDoesNotOverride() {\n\t\tint position = 300;\n\t\tInteger predefinedFilterOrderBefore = this.filterOrderRegistration.getOrder(ChannelProcessingFilter.class);\n\t\tthis.filterOrderRegistration.put(MyFilter.class, position);\n\t\tInteger myFilterOrder = this.filterOrderRegistration.getOrder(MyFilter.class);\n\t\tInteger predefinedFilterOrderAfter = this.filterOrderRegistration.getOrder(ChannelProcessingFilter.class);\n\t\tassertThat(myFilterOrder).isEqualTo(position);\n\t\tassertThat(predefinedFilterOrderAfter).isEqualTo(predefinedFilterOrderBefore).isEqualTo(position);\n\t}\n\n\tstatic class MyFilter implements Filter {\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tfilterChain.doFilter(servletRequest, servletResponse);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/builders/HttpConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.cas.web.CasAuthenticationFilter;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link HttpSecurity}.\n *\n * @author Rob Winch\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void configureWhenAddFilterUnregisteredThenThrowsBeanCreationException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(UnregisteredFilterConfig.class).autowire())\n\t\t\t.withMessageContaining(\"The Filter class \" + UnregisteredFilter.class.getName()\n\t\t\t\t\t+ \" does not have a registered order and cannot be added without a specified order.\"\n\t\t\t\t\t+ \" Consider using addFilterBefore or addFilterAfter instead.\");\n\t}\n\n\t// https://github.com/spring-projects/spring-security-javaconfig/issues/104\n\t@Test\n\tpublic void configureWhenAddFilterCasAuthenticationFilterThenFilterAdded() throws Exception {\n\t\tCasAuthenticationFilterConfig.CAS_AUTHENTICATION_FILTER = spy(new CasAuthenticationFilter());\n\t\tthis.spring.register(CasAuthenticationFilterConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\"));\n\t\tverify(CasAuthenticationFilterConfig.CAS_AUTHENTICATION_FILTER).doFilter(any(ServletRequest.class),\n\t\t\t\tany(ServletResponse.class), any(FilterChain.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenConfigIsRequestMatchersJavadocThenAuthorizationApplied() throws Exception {\n\t\tthis.spring.register(RequestMatcherRegistryConfigs.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/oauth/a\")).andExpect(status().isUnauthorized());\n\t\tthis.mockMvc.perform(get(\"/oauth/b\")).andExpect(status().isUnauthorized());\n\t\tthis.mockMvc.perform(get(\"/api/a\")).andExpect(status().isUnauthorized());\n\t\tthis.mockMvc.perform(get(\"/api/b\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class UnregisteredFilterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.addFilter(new UnregisteredFilter());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\tstatic class UnregisteredFilter extends OncePerRequestFilter {\n\n\t\t@Override\n\t\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tFilterChain filterChain) throws ServletException, IOException {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\tstatic class CasAuthenticationFilterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilter(CAS_AUTHENTICATION_FILTER);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\tstatic CasAuthenticationFilter CAS_AUTHENTICATION_FILTER;\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class RequestMatcherRegistryConfigs {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(pathPattern(\"/api/**\"))\n\t\t\t\t\t.requestMatchers(pathPattern(\"/oauth/**\")))\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/builders/HttpSecurityAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpSecurityAuthenticationManagerTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void authenticationManagerWhenConfiguredThenUsed() throws Exception {\n\t\tthis.spring.register(AuthenticationManagerConfig.class).autowire();\n\n\t\tgiven(AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()))\n\t\t\t.willReturn(new TestingAuthenticationToken(\"user\", \"test\", \"ROLE_USER\"));\n\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"test\")));\n\n\t\tverify(AuthenticationManagerConfig.AUTHENTICATION_MANAGER).authenticate(any());\n\t}\n\n\t@Test\n\tpublic void authenticationManagerWhenBuilderAndAuthenticationManagerConfiguredThenBuilderIgnored()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthenticationManagerBuilderConfig.class).autowire();\n\n\t\tgiven(AuthenticationManagerBuilderConfig.AUTHENTICATION_MANAGER.authenticate(any()))\n\t\t\t.willReturn(new TestingAuthenticationToken(\"user\", \"test\", \"ROLE_USER\"));\n\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"test\")));\n\n\t\tverify(AuthenticationManagerBuilderConfig.AUTHENTICATION_MANAGER).authenticate(any());\n\t\tverifyNoInteractions(AuthenticationManagerBuilderConfig.USER_DETAILS_SERVICE);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthenticationManagerConfig {\n\n\t\tstatic final AuthenticationManager AUTHENTICATION_MANAGER = mock(AuthenticationManager.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t\t.authenticationManager(AUTHENTICATION_MANAGER);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthenticationManagerBuilderConfig {\n\n\t\tstatic final AuthenticationManager AUTHENTICATION_MANAGER = mock(AuthenticationManager.class);\n\t\tstatic final UserDetailsService USER_DETAILS_SERVICE = mock(UserDetailsService.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t\t.authenticationManager(AUTHENTICATION_MANAGER);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn USER_DETAILS_SERVICE;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/builders/HttpSecurityDeferAddFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.assertj.core.api.ListAssert;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.UnsatisfiedDependencyException;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.channel.ChannelProcessingFilter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.security.web.header.HeaderWriterFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpSecurityDeferAddFilterTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void addFilterAfterFilterNotRegisteredYetThenThrowIllegalArgument() {\n\t\tassertThatExceptionOfType(UnsatisfiedDependencyException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(MyOtherFilterAfterMyFilterNotRegisteredYetConfig.class).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.isInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void addFilterBeforeFilterNotRegisteredYetThenThrowIllegalArgument() {\n\t\tassertThatExceptionOfType(UnsatisfiedDependencyException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(MyOtherFilterBeforeMyFilterNotRegisteredYetConfig.class).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.isInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void addFilterAfterWhenSameFilterDifferentPlacesThenOrderCorrect() {\n\t\tthis.spring.register(MyFilterMultipleAfterConfig.class).autowire();\n\n\t\tassertThatFilters().containsSubsequence(WebAsyncManagerIntegrationFilter.class, MyFilter.class,\n\t\t\t\tExceptionTranslationFilter.class, MyFilter.class);\n\t}\n\n\t@Test\n\tpublic void addFilterBeforeWhenSameFilterDifferentPlacesThenOrderCorrect() {\n\t\tthis.spring.register(MyFilterMultipleBeforeConfig.class).autowire();\n\n\t\tassertThatFilters().containsSubsequence(MyFilter.class, WebAsyncManagerIntegrationFilter.class, MyFilter.class,\n\t\t\t\tExceptionTranslationFilter.class);\n\t}\n\n\t@Test\n\tpublic void addFilterAtWhenSameFilterDifferentPlacesThenOrderCorrect() {\n\t\tthis.spring.register(MyFilterMultipleAtConfig.class).autowire();\n\n\t\tassertThatFilters().containsSubsequence(MyFilter.class, WebAsyncManagerIntegrationFilter.class, MyFilter.class,\n\t\t\t\tExceptionTranslationFilter.class);\n\t}\n\n\t@Test\n\tpublic void addFilterAfterWhenAfterCustomFilterThenOrderCorrect() {\n\t\tthis.spring.register(MyOtherFilterRelativeToMyFilterAfterConfig.class).autowire();\n\n\t\tassertThatFilters().containsSubsequence(WebAsyncManagerIntegrationFilter.class, MyFilter.class,\n\t\t\t\tMyOtherFilter.class);\n\t}\n\n\t@Test\n\tpublic void addFilterBeforeWhenBeforeCustomFilterThenOrderCorrect() {\n\t\tthis.spring.register(MyOtherFilterRelativeToMyFilterBeforeConfig.class).autowire();\n\n\t\tassertThatFilters().containsSubsequence(MyOtherFilter.class, MyFilter.class,\n\t\t\t\tWebAsyncManagerIntegrationFilter.class);\n\t}\n\n\t@Test\n\tpublic void addFilterAtWhenAtCustomFilterThenOrderCorrect() {\n\t\tthis.spring.register(MyOtherFilterRelativeToMyFilterAtConfig.class).autowire();\n\n\t\tassertThatFilters().containsSubsequence(WebAsyncManagerIntegrationFilter.class, MyFilter.class,\n\t\t\t\tMyOtherFilter.class, SecurityContextHolderFilter.class);\n\t}\n\n\t@Test\n\tpublic void addFilterBeforeWhenCustomFilterDifferentPlacesThenOrderCorrect() {\n\t\tthis.spring.register(MyOtherFilterBeforeToMyFilterMultipleAfterConfig.class).autowire();\n\n\t\tassertThatFilters().containsSubsequence(WebAsyncManagerIntegrationFilter.class, MyOtherFilter.class,\n\t\t\t\tMyFilter.class, ExceptionTranslationFilter.class);\n\t}\n\n\t@Test\n\tpublic void addFilterBeforeAndAfterWhenCustomFiltersDifferentPlacesThenOrderCorrect() {\n\t\tthis.spring.register(MyAnotherFilterRelativeToMyCustomFiltersMultipleConfig.class).autowire();\n\n\t\tassertThatFilters().containsSubsequence(HeaderWriterFilter.class, MyFilter.class, MyOtherFilter.class,\n\t\t\t\tMyOtherFilter.class, MyAnotherFilter.class, MyFilter.class, ExceptionTranslationFilter.class);\n\t}\n\n\tprivate ListAssert<Class<?>> assertThatFilters() {\n\t\tFilterChainProxy filterChain = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tList<Class<?>> filters = filterChain.getFilters(\"/\")\n\t\t\t.stream()\n\t\t\t.map(Object::getClass)\n\t\t\t.collect(Collectors.toList());\n\t\treturn assertThat(filters);\n\t}\n\n\tpublic static class MyFilter implements Filter {\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tfilterChain.doFilter(servletRequest, servletResponse);\n\t\t}\n\n\t}\n\n\tstatic class MyOtherFilter implements Filter {\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tfilterChain.doFilter(servletRequest, servletResponse);\n\t\t}\n\n\t}\n\n\tstatic class MyAnotherFilter implements Filter {\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tfilterChain.doFilter(servletRequest, servletResponse);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MyFilterMultipleAfterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterAfter(new MyFilter(), WebAsyncManagerIntegrationFilter.class)\n\t\t\t\t\t.addFilterAfter(new MyFilter(), ExceptionTranslationFilter.class);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MyFilterMultipleBeforeConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterBefore(new MyFilter(), WebAsyncManagerIntegrationFilter.class)\n\t\t\t\t\t.addFilterBefore(new MyFilter(), ExceptionTranslationFilter.class);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MyFilterMultipleAtConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterAt(new MyFilter(), ChannelProcessingFilter.class)\n\t\t\t\t\t.addFilterAt(new MyFilter(), UsernamePasswordAuthenticationFilter.class);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MyOtherFilterRelativeToMyFilterAfterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterAfter(new MyFilter(), WebAsyncManagerIntegrationFilter.class)\n\t\t\t\t\t.addFilterAfter(new MyOtherFilter(), MyFilter.class);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MyOtherFilterAfterMyFilterNotRegisteredYetConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterAfter(new MyOtherFilter(), MyFilter.class);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\tstatic class MyOtherFilterBeforeMyFilterNotRegisteredYetConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterBefore(new MyOtherFilter(), MyFilter.class);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\tstatic class MyOtherFilterRelativeToMyFilterBeforeConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterBefore(new MyFilter(), WebAsyncManagerIntegrationFilter.class)\n\t\t\t\t\t.addFilterBefore(new MyOtherFilter(), MyFilter.class);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MyOtherFilterRelativeToMyFilterAtConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterAt(new MyFilter(), WebAsyncManagerIntegrationFilter.class)\n\t\t\t\t\t.addFilterAt(new MyOtherFilter(), MyFilter.class);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MyOtherFilterBeforeToMyFilterMultipleAfterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterAfter(new MyFilter(), WebAsyncManagerIntegrationFilter.class)\n\t\t\t\t\t.addFilterAfter(new MyFilter(), ExceptionTranslationFilter.class)\n\t\t\t\t\t.addFilterBefore(new MyOtherFilter(), MyFilter.class);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MyAnotherFilterRelativeToMyCustomFiltersMultipleConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.addFilterAfter(new MyFilter(), HeaderWriterFilter.class)\n\t\t\t\t\t.addFilterBefore(new MyOtherFilter(), ExceptionTranslationFilter.class)\n\t\t\t\t\t.addFilterAfter(new MyOtherFilter(), MyFilter.class)\n\t\t\t\t\t.addFilterAt(new MyAnotherFilter(), MyOtherFilter.class)\n\t\t\t\t\t.addFilterAfter(new MyFilter(), MyAnotherFilter.class);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.function.Supplier;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.login.LoginContext;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.SecurityMetadataSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.jaas.JaasAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;\nimport org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.context.NullSecurityContextRepository;\nimport org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RegexRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyCollection;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;http&gt; attributes are present in\n * Java Config.\n *\n * @author Rob Winch\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test // http@access-decision-manager-ref\n\tpublic void configureWhenAccessDecisionManagerSetThenVerifyUse() throws Exception {\n\t\tthis.spring.register(AccessDecisionManagerRefConfig.class).autowire();\n\t\tAccessDecisionManager accessDecisionManager = this.spring.getContext().getBean(AccessDecisionManager.class);\n\t\tgiven(accessDecisionManager.supports(FilterInvocation.class)).willReturn(true);\n\t\tgiven(accessDecisionManager.supports(any(ConfigAttribute.class))).willReturn(true);\n\t\tthis.mockMvc.perform(get(\"/\"));\n\t\tverify(accessDecisionManager, times(1)).decide(any(Authentication.class), any(), anyCollection());\n\t}\n\n\t@Test // http@access-denied-page\n\tpublic void configureWhenAccessDeniedPageSetAndRequestForbiddenThenForwardedToAccessDeniedPage() throws Exception {\n\t\tthis.spring.register(AccessDeniedPageConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/admin\").with(user(PasswordEncodedUser.user())))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(forwardedUrl(\"/AccessDeniedPage\"));\n\t}\n\n\t@Test // http@authentication-manager-ref\n\tpublic void configureWhenAuthenticationManagerProvidedThenVerifyUse() throws Exception {\n\t\tAuthenticationManagerRefConfig.AUTHENTICATION_MANAGER = mock(AuthenticationManager.class);\n\t\tthis.spring.register(AuthenticationManagerRefConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin());\n\t\tverify(AuthenticationManagerRefConfig.AUTHENTICATION_MANAGER, times(1)).authenticate(any(Authentication.class));\n\t}\n\n\t@Test // http@create-session=always\n\tpublic void configureWhenSessionCreationPolicyAlwaysThenSessionCreatedOnRequest() throws Exception {\n\t\tthis.spring.register(CreateSessionAlwaysConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/\")).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\tassertThat(session.isNew()).isTrue();\n\t}\n\n\t@Test // http@create-session=stateless\n\tpublic void configureWhenSessionCreationPolicyStatelessThenSessionNotCreatedOnRequest() throws Exception {\n\t\tthis.spring.register(CreateSessionStatelessConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/\")).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test // http@create-session=ifRequired\n\tpublic void configureWhenSessionCreationPolicyIfRequiredThenSessionCreatedWhenRequiredOnRequest() throws Exception {\n\t\tthis.spring.register(IfRequiredConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/unsecure\")).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t\tmvcResult = this.mockMvc.perform(formLogin()).andReturn();\n\t\tsession = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\tassertThat(session.isNew()).isTrue();\n\t}\n\n\t@Test // http@create-session=never\n\tpublic void configureWhenSessionCreationPolicyNeverThenSessionNotCreatedOnRequest() throws Exception {\n\t\tthis.spring.register(CreateSessionNeverConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/\")).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test // http@entry-point-ref\n\tpublic void configureWhenAuthenticationEntryPointSetAndRequestUnauthorizedThenRedirectedToAuthenticationEntryPoint()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(EntryPointRefConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/entry-point\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test // http@jaas-api-provision\n\tpublic void configureWhenJaasApiIntegrationFilterAddedThenJaasSubjectObtained() throws Exception {\n\t\tLoginContext loginContext = mock(LoginContext.class);\n\t\tgiven(loginContext.getSubject()).willReturn(new Subject());\n\t\tJaasAuthenticationToken authenticationToken = mock(JaasAuthenticationToken.class);\n\t\tgiven(authenticationToken.isAuthenticated()).willReturn(true);\n\t\tgiven(authenticationToken.getLoginContext()).willReturn(loginContext);\n\t\tthis.spring.register(JaasApiProvisionConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\").with(authentication(authenticationToken)));\n\t\tverify(loginContext, times(1)).getSubject();\n\t}\n\n\t@Test // http@realm\n\tpublic void configureWhenHttpBasicAndRequestUnauthorizedThenReturnWWWAuthenticateWithRealm() throws Exception {\n\t\tthis.spring.register(RealmConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(\"WWW-Authenticate\", \"Basic realm=\\\"RealmConfig\\\", charset=\\\"UTF-8\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test // http@request-matcher-ref ant\n\tpublic void configureWhenAntPatternMatchingThenPathPatternRequestMatcherUsed() {\n\t\tthis.spring.register(RequestMatcherAntConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tassertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class);\n\t\tDefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains()\n\t\t\t.get(0);\n\t\tassertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(PathPatternRequestMatcher.class);\n\t}\n\n\t@Test // http@request-matcher-ref regex\n\tpublic void configureWhenRegexPatternMatchingThenRegexRequestMatcherUsed() {\n\t\tthis.spring.register(RequestMatcherRegexConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tassertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class);\n\t\tDefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains()\n\t\t\t.get(0);\n\t\tassertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(RegexRequestMatcher.class);\n\t}\n\n\t@Test // http@request-matcher-ref\n\tpublic void configureWhenRequestMatcherProvidedThenRequestMatcherUsed() {\n\t\tthis.spring.register(RequestMatcherRefConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tassertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class);\n\t\tDefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains()\n\t\t\t.get(0);\n\t\tassertThat(securityFilterChain.getRequestMatcher())\n\t\t\t.isInstanceOf(RequestMatcherRefConfig.MyRequestMatcher.class);\n\t}\n\n\t@Test // http@security=none\n\tpublic void configureWhenIgnoredAntPatternsThenPathPatternRequestMatcherUsedWithNoFilters() {\n\t\tthis.spring.register(SecurityNoneConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tassertThat(filterChainProxy.getFilterChains().get(0)).isInstanceOf(DefaultSecurityFilterChain.class);\n\t\tDefaultSecurityFilterChain securityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains()\n\t\t\t.get(0);\n\t\tassertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(PathPatternRequestMatcher.class);\n\t\tassertThat(securityFilterChain.getRequestMatcher()).isEqualTo(pathPattern(\"/resources/**\"));\n\t\tassertThat(securityFilterChain.getFilters()).isEmpty();\n\t\tassertThat(filterChainProxy.getFilterChains().get(1)).isInstanceOf(DefaultSecurityFilterChain.class);\n\t\tsecurityFilterChain = (DefaultSecurityFilterChain) filterChainProxy.getFilterChains().get(1);\n\t\tassertThat(securityFilterChain.getRequestMatcher()).isInstanceOf(PathPatternRequestMatcher.class);\n\t\tassertThat(securityFilterChain.getRequestMatcher()).isEqualTo(pathPattern(\"/public/**\"));\n\t\tassertThat(securityFilterChain.getFilters()).isEmpty();\n\t}\n\n\t@Test // http@security-context-repository-ref\n\tpublic void configureWhenNullSecurityContextRepositoryThenSecurityContextNotSavedInSession() throws Exception {\n\t\tthis.spring.register(SecurityContextRepoConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mockMvc.perform(formLogin()).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test // http@servlet-api-provision=false\n\tpublic void configureWhenServletApiDisabledThenRequestNotServletApiWrapper() throws Exception {\n\t\tthis.spring.register(ServletApiProvisionConfig.class, MainController.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\"));\n\t\tassertThat(MainController.HTTP_SERVLET_REQUEST_TYPE)\n\t\t\t.isNotInstanceOf(SecurityContextHolderAwareRequestWrapper.class);\n\t}\n\n\t@Test // http@servlet-api-provision defaults to true\n\tpublic void configureWhenServletApiDefaultThenRequestIsServletApiWrapper() throws Exception {\n\t\tthis.spring.register(ServletApiProvisionDefaultsConfig.class, MainController.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\"));\n\t\tassertThat(SecurityContextHolderAwareRequestWrapper.class)\n\t\t\t.isAssignableFrom(MainController.HTTP_SERVLET_REQUEST_TYPE);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AccessDecisionManagerRefConfig {\n\n\t\tAccessDecisionManager accessDecisionManager = mock(AccessDecisionManager.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().access(new AccessAuthorizationManagerAdapter(this.accessDecisionManager, \"permitAll\"))\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAccessDecisionManager accessDecisionManager() {\n\t\t\treturn this.accessDecisionManager;\n\t\t}\n\n\t\tprivate static final class AccessAuthorizationManagerAdapter\n\t\t\t\timplements AuthorizationManager<RequestAuthorizationContext> {\n\n\t\t\tprivate final AccessDecisionManager delegate;\n\n\t\t\tprivate final SecurityMetadataSource metadataSource;\n\n\t\t\tprivate AccessAuthorizationManagerAdapter(AccessDecisionManager delegate, String expression) {\n\t\t\t\tthis.delegate = delegate;\n\t\t\t\tLinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> mappings = new LinkedHashMap<>();\n\t\t\t\tmappings.put(AnyRequestMatcher.INSTANCE, SecurityConfig.createList(expression));\n\t\t\t\tDefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();\n\t\t\t\tthis.metadataSource = new ExpressionBasedFilterInvocationSecurityMetadataSource(mappings, handler);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\t\t\tRequestAuthorizationContext object) {\n\t\t\t\tHttpServletRequest request = object.getRequest();\n\t\t\t\tFilterInvocation invocation = new FilterInvocation(request.getContextPath(), request.getServletPath(),\n\t\t\t\t\t\trequest.getPathInfo(), request.getQueryString(), request.getMethod());\n\t\t\t\tCollection<ConfigAttribute> attributes = this.metadataSource.getAttributes(invocation);\n\t\t\t\ttry {\n\t\t\t\t\tthis.delegate.decide(authentication.get(), invocation, attributes);\n\t\t\t\t\treturn new AuthorizationDecision(true);\n\t\t\t\t}\n\t\t\t\tcatch (AccessDeniedException ex) {\n\t\t\t\t\treturn new AuthorizationDecision(false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AccessDeniedPageConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin\").hasRole(\"ADMIN\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.accessDeniedPage(\"/AccessDeniedPage\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthenticationManagerRefConfig {\n\n\t\tstatic AuthenticationManager AUTHENTICATION_MANAGER;\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager() {\n\t\t\treturn AUTHENTICATION_MANAGER;\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CreateSessionAlwaysConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().permitAll())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.ALWAYS));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CreateSessionStatelessConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().permitAll())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.STATELESS));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class IfRequiredConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/unsecure\").permitAll()\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CreateSessionNeverConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().anonymous())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.NEVER));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EntryPointRefConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint(\"/entry-point\")))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class JaasApiProvisionConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.addFilter(new JaasApiIntegrationFilter());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RealmConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic((basic) -> basic\n\t\t\t\t\t.realmName(\"RealmConfig\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestMatcherAntConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(pathPattern(\"/api/**\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestMatcherRegexConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(new RegexRequestMatcher(\"/regex/.*\", null));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestMatcherRefConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(new MyRequestMatcher());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tstatic class MyRequestMatcher implements RequestMatcher {\n\n\t\t\t@Override\n\t\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityNoneConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\tPathPatternRequestMatcher.Builder builder = PathPatternRequestMatcher.withDefaults();\n\t\t\treturn (web) -> web.ignoring()\n\t\t\t\t.requestMatchers(builder.matcher(\"/resources/**\"), builder.matcher(\"/public/**\"));\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityContextRepoConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.securityContext((context) -> context\n\t\t\t\t\t.securityContextRepository(new NullSecurityContextRepository()))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ServletApiProvisionConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().permitAll())\n\t\t\t\t.servletApi((api) -> api\n\t\t\t\t\t.disable());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ServletApiProvisionDefaultsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class MainController {\n\n\t\tstatic Class<? extends HttpServletRequest> HTTP_SERVLET_REQUEST_TYPE;\n\n\t\t@GetMapping(\"/\")\n\t\tString index(HttpServletRequest request) {\n\t\t\tHTTP_SERVLET_REQUEST_TYPE = request.getClass();\n\t\t\treturn \"index\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/builders/TestHttpSecurities.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.util.List;\n\nimport org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;\nimport org.springframework.test.util.ReflectionTestUtils;\n\npublic final class TestHttpSecurities {\n\n\tprivate TestHttpSecurities() {\n\n\t}\n\n\tpublic static void disableDefaults(HttpSecurity http) throws Exception {\n\t\tList<Object> orderedFilters = (List<Object>) ReflectionTestUtils.getField(http, \"filters\");\n\t\torderedFilters.clear();\n\t\thttp.csrf((c) -> c.disable())\n\t\t\t.exceptionHandling((c) -> c.disable())\n\t\t\t.headers((c) -> c.disable())\n\t\t\t.sessionManagement((c) -> c.disable())\n\t\t\t.securityContext((c) -> c.disable())\n\t\t\t.requestCache((c) -> c.disable())\n\t\t\t.anonymous((c) -> c.disable())\n\t\t\t.servletApi((c) -> c.disable())\n\t\t\t.removeConfigurer(DefaultLoginPageConfigurer.class);\n\t\thttp.logout((c) -> c.disable());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/builders/WebSecurityFilterChainValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.UnreachableFilterChainException;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatchers;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Tests for {@link WebSecurityFilterChainValidator}\n *\n * @author Max Batischev\n */\n@ExtendWith(MockitoExtension.class)\npublic class WebSecurityFilterChainValidatorTests {\n\n\tprivate final WebSecurityFilterChainValidator validator = new WebSecurityFilterChainValidator();\n\n\tprivate final PathPatternRequestMatcher.Builder builder = PathPatternRequestMatcher.withDefaults();\n\n\t@Mock\n\tprivate AnonymousAuthenticationFilter authenticationFilter;\n\n\t@Mock\n\tprivate ExceptionTranslationFilter exceptionTranslationFilter;\n\n\t@Mock\n\tprivate FilterSecurityInterceptor authorizationInterceptor;\n\n\t@Test\n\tvoid validateWhenFilterSecurityInterceptorConfiguredThenValidates() {\n\t\tSecurityFilterChain chain = new DefaultSecurityFilterChain(this.builder.matcher(\"/api\"),\n\t\t\t\tthis.authenticationFilter, this.exceptionTranslationFilter, this.authorizationInterceptor);\n\t\tFilterChainProxy proxy = new FilterChainProxy(List.of(chain));\n\n\t\tassertThatNoException().isThrownBy(() -> this.validator.validate(proxy));\n\t}\n\n\t@Test\n\tvoid validateWhenAnyRequestMatcherIsPresentThenUnreachableFilterChainException() {\n\t\tSecurityFilterChain chain1 = new DefaultSecurityFilterChain(this.builder.matcher(\"/api\"),\n\t\t\t\tthis.authenticationFilter, this.exceptionTranslationFilter, this.authorizationInterceptor);\n\t\tSecurityFilterChain chain2 = new DefaultSecurityFilterChain(AnyRequestMatcher.INSTANCE,\n\t\t\t\tthis.authenticationFilter, this.exceptionTranslationFilter, this.authorizationInterceptor);\n\t\tList<SecurityFilterChain> chains = new ArrayList<>();\n\t\tchains.add(chain2);\n\t\tchains.add(chain1);\n\t\tFilterChainProxy proxy = new FilterChainProxy(chains);\n\n\t\tassertThatExceptionOfType(UnreachableFilterChainException.class)\n\t\t\t.isThrownBy(() -> this.validator.validate(proxy));\n\t}\n\n\t@Test\n\tvoid validateWhenSameRequestMatchersArePresentThenUnreachableFilterChainException() {\n\t\tSecurityFilterChain chain1 = new DefaultSecurityFilterChain(this.builder.matcher(\"/api\"),\n\t\t\t\tthis.authenticationFilter, this.exceptionTranslationFilter, this.authorizationInterceptor);\n\t\tSecurityFilterChain chain2 = new DefaultSecurityFilterChain(this.builder.matcher(\"/api\"),\n\t\t\t\tthis.authenticationFilter, this.exceptionTranslationFilter, this.authorizationInterceptor);\n\t\tList<SecurityFilterChain> chains = new ArrayList<>();\n\t\tchains.add(chain2);\n\t\tchains.add(chain1);\n\t\tFilterChainProxy proxy = new FilterChainProxy(chains);\n\n\t\tassertThatExceptionOfType(UnreachableFilterChainException.class)\n\t\t\t.isThrownBy(() -> this.validator.validate(proxy));\n\t}\n\n\t@Test\n\tvoid validateWhenSameComposedRequestMatchersArePresentThenUnreachableFilterChainException() {\n\t\tRequestMatcher matcher1 = RequestMatchers.anyOf(\n\t\t\t\tRequestMatchers.allOf(this.builder.matcher(\"/api\"), this.builder.matcher(\"/*.do\")),\n\t\t\t\tthis.builder.matcher(\"/admin\"));\n\t\tRequestMatcher matcher2 = RequestMatchers.anyOf(\n\t\t\t\tRequestMatchers.allOf(this.builder.matcher(\"/api\"), this.builder.matcher(\"/*.do\")),\n\t\t\t\tthis.builder.matcher(\"/admin\"));\n\t\tSecurityFilterChain chain1 = new DefaultSecurityFilterChain(matcher1, this.authenticationFilter,\n\t\t\t\tthis.exceptionTranslationFilter, this.authorizationInterceptor);\n\t\tSecurityFilterChain chain2 = new DefaultSecurityFilterChain(matcher2, this.authenticationFilter,\n\t\t\t\tthis.exceptionTranslationFilter, this.authorizationInterceptor);\n\t\tList<SecurityFilterChain> chains = new ArrayList<>();\n\t\tchains.add(chain2);\n\t\tchains.add(chain1);\n\t\tFilterChainProxy proxy = new FilterChainProxy(chains);\n\n\t\tassertThatExceptionOfType(UnreachableFilterChainException.class)\n\t\t\t.isThrownBy(() -> this.validator.validate(proxy));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/builders/WebSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.builders;\n\nimport java.io.IOException;\n\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.ObservationTextPublisher;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n */\npublic class WebSecurityTests {\n\n\tAnnotationConfigWebApplicationContext context;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\t@Autowired\n\tFilter springSecurityFilterChain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest(\"GET\", \"\");\n\t\tthis.request.setMethod(\"GET\");\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void requestRejectedHandlerInvoked() throws ServletException, IOException {\n\t\tloadConfig(DefaultConfig.class);\n\t\tthis.request.setServletPath(\"/spring\");\n\t\tthis.request.setRequestURI(\"/spring/\\u0019path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n\t}\n\n\t@Test\n\tpublic void customRequestRejectedHandlerInvoked() throws ServletException, IOException {\n\t\tloadConfig(RequestRejectedHandlerConfig.class);\n\t\tthis.request.setServletPath(\"/spring\");\n\t\tthis.request.setRequestURI(\"/spring/\\u0019path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n\t}\n\n\t// gh-12548\n\t@Test\n\tpublic void requestRejectedHandlerInvokedWhenOperationalObservationRegistry() throws ServletException, IOException {\n\t\tloadConfig(ObservationRegistryConfig.class);\n\t\tthis.request.setServletPath(\"/spring\");\n\t\tthis.request.setRequestURI(\"/spring/\\u0019path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);\n\t}\n\n\tpublic void loadConfig(Class<?>... configs) {\n\t\tthis.context = new AnnotationConfigWebApplicationContext();\n\t\tthis.context.register(configs);\n\t\tthis.context.setServletContext(new MockServletContext());\n\t\tthis.context.refresh();\n\t\tthis.context.getAutowireCapableBeanFactory().autowireBean(this);\n\t}\n\n\t@EnableWebSecurity\n\tstatic class DefaultConfig {\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class MvcMatcherConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer(PathPatternRequestMatcher.Builder builder) {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(builder.matcher(\"/path\"));\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class MvcMatcherServletPathConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer(PathPatternRequestMatcher.Builder builder) {\n\t\t\treturn (web) -> web.ignoring()\n\t\t\t\t.requestMatchers(builder.basePath(\"/spring\").matcher(\"/path\"))\n\t\t\t\t.requestMatchers(\"/notused\");\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestRejectedHandlerConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web\n\t\t\t\t.requestRejectedHandler(new HttpStatusRequestRejectedHandler(HttpStatus.BAD_REQUEST.value()));\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObservationRegistryConfig {\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\tObservationRegistry observationRegistry = ObservationRegistry.create();\n\t\t\tobservationRegistry.observationConfig().observationHandler(new ObservationTextPublisher());\n\t\t\treturn observationRegistry;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/AuthenticationPrincipalArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class AuthenticationPrincipalArgumentResolverTests {\n\n\t@Autowired\n\tWebApplicationContext wac;\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void authenticationPrincipalExpressionWhenBeanExpressionSuppliedThenBeanUsed() throws Exception {\n\t\tUser user = new User(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tcontext.setAuthentication(\n\t\t\t\tUsernamePasswordAuthenticationToken.authenticated(user, user.getPassword(), user.getAuthorities()));\n\t\tSecurityContextHolder.setContext(context);\n\t\tMockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();\n\t\t// @formatter:off\n\t\tmockMvc.perform(get(\"/users/self\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"extracted-user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t\t@Bean\n\t\tpublic UsernameExtractor usernameExtractor() {\n\t\t\treturn new UsernameExtractor();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class UserController {\n\n\t\t\t@GetMapping(\"/users/self\")\n\t\t\tpublic String usersSelf(\n\t\t\t\t\t@AuthenticationPrincipal(expression = \"@usernameExtractor.extract(#this)\") String userName) {\n\t\t\t\treturn userName;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic class UsernameExtractor {\n\n\t\tpublic String extract(User u) {\n\t\t\treturn \"extracted-\" + u.getUsername();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/AuthorizationManagerWebInvocationPrivilegeEvaluatorConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer;\nimport org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Checks that\n * {@link org.springframework.security.web.access.PathPatternRequestTransformer} is\n * autowired into\n * {@link org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator}.\n *\n * @author Rob Winch\n */\n@ContextConfiguration\n@WebAppConfiguration\n@ExtendWith({ SpringExtension.class })\n@SecurityTestExecutionListeners\npublic class AuthorizationManagerWebInvocationPrivilegeEvaluatorConfigTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tHttpServletRequestTransformer requestTransformer;\n\n\t@Autowired\n\tWebInvocationPrivilegeEvaluator wipe;\n\n\t@Test\n\tvoid webAndTransformerThenWIPEDelegatesToTransformer() {\n\t\tthis.spring.register(WebConfig.class, TransformerConfig.class).autowire();\n\n\t\tthis.wipe.isAllowed(\"/uri\", TestAuthentication.authenticatedUser());\n\n\t\tverify(this.requestTransformer).transform(any());\n\t}\n\n\t@Configuration\n\tstatic class TransformerConfig {\n\n\t\t@Bean\n\t\tHttpServletRequestTransformer httpServletRequestTransformer() {\n\t\t\treturn mock(HttpServletRequestTransformer.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebConfig {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/DeferHttpSessionJavaConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\n\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class DeferHttpSessionJavaConfigTests {\n\n\t@Autowired\n\tprivate FilterChainProxy springSecurityFilterChain;\n\n\t@Autowired\n\tprivate Service service;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void explicitDeferHttpSession() throws Exception {\n\t\tthis.spring.register(DeferHttpSessionConfig.class).autowire();\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletRequest mockRequest = spy(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = (httpRequest, httpResponse) -> httpResponse.getWriter().write(this.service.getMessage());\n\n\t\tthis.springSecurityFilterChain.doFilter(mockRequest, response, chain);\n\n\t\tverify(mockRequest, never()).getSession(anyBoolean());\n\t\tverify(mockRequest, never()).getSession();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableMethodSecurity(prePostEnabled = true)\n\tstatic class DeferHttpSessionConfig {\n\n\t\t@Bean\n\t\tService service() {\n\t\t\treturn new Service();\n\t\t}\n\n\t\t@Bean\n\t\tDefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().permitAll()\n\t\t\t\t)\n\t\t\t\t.sessionManagement((sessions) -> sessions\n\t\t\t\t\t.requireExplicitAuthenticationStrategy(true)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\tpublic static class Service {\n\n\t\t@PreAuthorize(\"permitAll\")\n\t\tpublic String getMessage() {\n\t\t\treturn \"message\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/EnableWebSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport ch.qos.logback.classic.Level;\nimport ch.qos.logback.classic.Logger;\nimport ch.qos.logback.classic.spi.ILoggingEvent;\nimport ch.qos.logback.core.Appender;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.slf4j.LoggerFactory;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\n\n/**\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class EnableWebSecurityTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void loadConfigWhenChildConfigExtendsSecurityConfigThenSecurityConfigInherited() throws Exception {\n\t\tAppender<ILoggingEvent> appender = mockAppenderFor(\"Spring Security Debugger\");\n\t\tthis.spring.register(ChildSecurityConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\"));\n\t\tverify(appender, atLeastOnce()).doAppend(any(ILoggingEvent.class));\n\t}\n\n\tprivate Appender<ILoggingEvent> mockAppenderFor(String name) {\n\t\tAppender<ILoggingEvent> appender = mock(Appender.class);\n\t\tLogger logger = (Logger) LoggerFactory.getLogger(name);\n\t\tlogger.setLevel(Level.DEBUG);\n\t\tlogger.addAppender(appender);\n\t\treturn appender;\n\t}\n\n\t// gh-14370\n\t@Test\n\tpublic void loadConfigWhenEnableWebMvcDebugConfigThenContextIsBuilt() {\n\t\tassertThatNoException().isThrownBy(() -> this.spring.register(EnableWebMvcDebugConfig.class).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenEnableWebMvcThenAuthenticationPrincipalResolvable() throws Exception {\n\t\tthis.spring.register(AuthenticationPrincipalConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\").with(authentication(new TestingAuthenticationToken(\"user1\", \"password\"))))\n\t\t\t.andExpect(content().string(\"user1\"));\n\t}\n\n\t@Test\n\tpublic void securityFilterChainWhenEnableWebMvcThenAuthenticationPrincipalResolvable() throws Exception {\n\t\tthis.spring.register(SecurityFilterChainAuthenticationPrincipalConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\").with(authentication(new TestingAuthenticationToken(\"user1\", \"password\"))))\n\t\t\t.andExpect(content().string(\"user1\"));\n\t}\n\n\t@Test\n\tpublic void enableWebSecurityWhenNoConfigurationAnnotationThenBeanProxyingEnabled() {\n\t\tthis.spring.register(BeanProxyEnabledByDefaultConfig.class).autowire();\n\t\tChild childBean = this.spring.getContext().getBean(Child.class);\n\t\tParent parentBean = this.spring.getContext().getBean(Parent.class);\n\t\tassertThat(parentBean.getChild()).isSameAs(childBean);\n\t}\n\n\t@Test\n\tpublic void enableWebSecurityWhenProxyBeanMethodsFalseThenBeanProxyingDisabled() {\n\t\tthis.spring.register(BeanProxyDisabledConfig.class).autowire();\n\t\tChild childBean = this.spring.getContext().getBean(Child.class);\n\t\tParent parentBean = this.spring.getContext().getBean(Parent.class);\n\t\tassertThat(parentBean.getChild()).isNotSameAs(childBean);\n\t}\n\n\t// gh-17484\n\t@Test\n\tvoid configureWhenEnableWebSecuritySeparateFromSecurityFilterChainThenWires() {\n\t\ttry (AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext()) {\n\t\t\tcontext.register(TestConfiguration.class, EnableWebSecurityConfiguration.class);\n\t\t\tcontext.refresh();\n\t\t}\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity(debug = true)\n\tstatic class EnableWebMvcDebugConfig {\n\n\t}\n\n\t@Configuration\n\tstatic class ChildSecurityConfig extends DebugSecurityConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity(debug = true)\n\tstatic class DebugSecurityConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AuthenticationPrincipalConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class AuthController {\n\n\t\t\t@GetMapping(\"/\")\n\t\t\tString principal(@AuthenticationPrincipal String principal) {\n\t\t\t\treturn principal;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class SecurityFilterChainAuthenticationPrincipalConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class AuthController {\n\n\t\t\t@GetMapping(\"/\")\n\t\t\tString principal(@AuthenticationPrincipal String principal) {\n\t\t\t\treturn principal;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class BeanProxyEnabledByDefaultConfig {\n\n\t\t@Bean\n\t\tChild child() {\n\t\t\treturn new Child();\n\t\t}\n\n\t\t@Bean\n\t\tParent parent() {\n\t\t\treturn new Parent(child());\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\tstatic class BeanProxyDisabledConfig {\n\n\t\t@Bean\n\t\tChild child() {\n\t\t\treturn new Child();\n\t\t}\n\n\t\t@Bean\n\t\tParent parent() {\n\t\t\treturn new Parent(child());\n\t\t}\n\n\t}\n\n\tstatic class Parent {\n\n\t\tprivate Child child;\n\n\t\tParent(Child child) {\n\t\t\tthis.child = child;\n\t\t}\n\n\t\tChild getChild() {\n\t\t\treturn this.child;\n\t\t}\n\n\t}\n\n\tstatic class Child {\n\n\t\tChild() {\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class TestConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class EnableWebSecurityConfiguration {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.Callable;\n\nimport com.google.common.net.HttpHeaders;\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.TestingAuthenticationProvider;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.event.AbstractAuthenticationEvent;\nimport org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.authentication.password.CompromisedPasswordChecker;\nimport org.springframework.security.authentication.password.CompromisedPasswordDecision;\nimport org.springframework.security.authentication.password.CompromisedPasswordException;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.provisioning.UserDetailsManager;\nimport org.springframework.security.test.web.servlet.RequestCacheResultMatcher;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.CorsConfigurationSource;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.withSettings;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link HttpSecurityConfiguration}.\n *\n * @author Eleftheria Stein\n */\n@ExtendWith({ MockitoExtension.class, SpringTestContextExtension.class })\npublic class HttpSecurityConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Mock\n\tprivate MockedStatic<SpringFactoriesLoader> springFactoriesLoader;\n\n\t@Test\n\tpublic void postWhenDefaultFilterChainBeanThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(DefaultWithFilterChainConfig.class).autowire();\n\n\t\tthis.mockMvc.perform(post(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultFilterChainBeanThenDefaultHeadersInResponse() throws Exception {\n\t\tthis.spring.register(DefaultWithFilterChainConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(header().string(HttpHeaders.X_CONTENT_TYPE_OPTIONS, \"nosniff\"))\n\t\t\t\t.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS,\n\t\t\t\t\t\tXFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name()))\n\t\t\t\t.andExpect(\n\t\t\t\t\t\theader().string(HttpHeaders.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\"))\n\t\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\"))\n\t\t\t\t.andExpect(header().string(HttpHeaders.EXPIRES, \"0\"))\n\t\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, \"no-cache\"))\n\t\t\t\t.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, \"0\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(\n\t\t\t\tHttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,\n\t\t\t\tHttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);\n\t}\n\n\t@Test\n\tpublic void logoutWhenDefaultFilterChainBeanThenCreatesDefaultLogoutEndpoint() throws Exception {\n\t\tthis.spring.register(DefaultWithFilterChainConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenDefaultConfigThenWebAsyncManagerIntegrationFilterAdded() throws Exception {\n\t\tthis.spring.register(DefaultWithFilterChainConfig.class, NameController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithBob = get(\"/name\").with(user(\"Bob\"));\n\t\tMvcResult mvcResult = this.mockMvc.perform(requestWithBob)\n\t\t\t\t.andExpect(request().asyncStarted())\n\t\t\t\t.andReturn();\n\t\tthis.mockMvc.perform(asyncDispatch(mvcResult))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"Bob\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void asyncDispatchWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring\n\t\t\t.register(DefaultWithFilterChainConfig.class, SecurityContextChangedListenerConfig.class,\n\t\t\t\t\tNameController.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithBob = get(\"/name\").with(user(\"Bob\"));\n\t\tMvcResult mvcResult = this.mockMvc.perform(requestWithBob)\n\t\t\t\t.andExpect(request().asyncStarted())\n\t\t\t\t.andReturn();\n\t\tthis.mockMvc.perform(asyncDispatch(mvcResult))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"Bob\"));\n\t\t// @formatter:on\n\t\tverify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultFilterChainBeanThenAnonymousPermitted() throws Exception {\n\t\tthis.spring.register(AuthorizeRequestsConfig.class, UserDetailsConfig.class, BaseController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDefaultFilterChainBeanThenSessionIdChanges() throws Exception {\n\t\tthis.spring.register(SecurityEnabledConfig.class, UserDetailsConfig.class).autowire();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tString sessionId = session.getId();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session(session)\n\t\t\t\t.with(csrf());\n\t\t// @formatter:on\n\t\tMvcResult result = this.mockMvc.perform(loginRequest).andReturn();\n\t\tassertThat(result.getRequest().getSession(false).getId()).isNotEqualTo(sessionId);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDefaultFilterChainBeanThenRedirectsToSavedRequest() throws Exception {\n\t\tthis.spring.register(SecurityEnabledConfig.class, UserDetailsConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/messages\"))\n\t\t\t\t.andReturn();\n\t\tHttpServletRequest request = mvcResult.getRequest();\n\t\tHttpServletResponse response = mvcResult.getResponse();\n\t\tMockHttpSession session = (MockHttpSession) mvcResult\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session(session)\n\t\t\t\t.with(csrf());\n\t\tthis.mockMvc.perform(loginRequest)\n\t\t\t\t.andExpect(RequestCacheResultMatcher.redirectToCachedRequest());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDefaultFilterChainBeanThenRolePrefixIsSet() throws Exception {\n\t\tthis.spring.register(SecurityEnabledConfig.class, UserDetailsConfig.class, UserController.class).autowire();\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\t// @formatter:off\n\t\tthis.mockMvc\n\t\t\t\t.perform(get(\"/user\").with(authentication(user)))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingDefaultsThenDefaultLoginPageGenerated() throws Exception {\n\t\tthis.spring.register(SecurityEnabledConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/login\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingDefaultsThenDefaultLoginFailurePageGenerated() throws Exception {\n\t\tthis.spring.register(SecurityEnabledConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/login?error\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingDefaultsThenDefaultLogoutSuccessPageGenerated() throws Exception {\n\t\tthis.spring.register(SecurityEnabledConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/login?logout\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingDefaultThenAuthenticationEventPublished() throws Exception {\n\t\tthis.spring\n\t\t\t.register(SecurityEnabledConfig.class, UserDetailsConfig.class, AuthenticationEventListenerConfig.class)\n\t\t\t.autowire();\n\t\tAuthenticationEventListenerConfig.clearEvents();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection());\n\t\tassertThat(AuthenticationEventListenerConfig.EVENTS).isNotEmpty();\n\t\tassertThat(AuthenticationEventListenerConfig.EVENTS).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingDefaultAndNoUserDetailsServiceThenAuthenticationEventPublished() throws Exception {\n\t\tthis.spring\n\t\t\t.register(SecurityEnabledConfig.class, UserDetailsConfig.class, AuthenticationEventListenerConfig.class)\n\t\t\t.autowire();\n\t\tAuthenticationEventListenerConfig.clearEvents();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection());\n\t\tassertThat(AuthenticationEventListenerConfig.EVENTS).isNotEmpty();\n\t\tassertThat(AuthenticationEventListenerConfig.EVENTS).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingCustomAuthenticationEventPublisherThenAuthenticationEventPublished() throws Exception {\n\t\tthis.spring\n\t\t\t.register(SecurityEnabledConfig.class, UserDetailsConfig.class,\n\t\t\t\t\tCustomAuthenticationEventPublisherConfig.class)\n\t\t\t.autowire();\n\t\tCustomAuthenticationEventPublisherConfig.clearEvents();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection());\n\t\tassertThat(CustomAuthenticationEventPublisherConfig.EVENTS).isNotEmpty();\n\t\tassertThat(CustomAuthenticationEventPublisherConfig.EVENTS).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingCustomAuthenticationEventPublisherAndNoUserDetailsServiceThenAuthenticationEventPublished()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(SecurityEnabledConfig.class, CustomAuthenticationEventPublisherConfig.class).autowire();\n\t\tCustomAuthenticationEventPublisherConfig.clearEvents();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection());\n\t\tassertThat(CustomAuthenticationEventPublisherConfig.EVENTS).isNotEmpty();\n\t\tassertThat(CustomAuthenticationEventPublisherConfig.EVENTS).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void configureWhenDefaultConfigurerAsSpringFactoryThenDefaultConfigurerApplied() {\n\t\tDefaultConfigurer configurer = new DefaultConfigurer();\n\t\tthis.springFactoriesLoader\n\t\t\t.when(() -> SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, getClass().getClassLoader()))\n\t\t\t.thenReturn(Arrays.asList(configurer));\n\t\tthis.spring.register(DefaultWithFilterChainConfig.class).autowire();\n\t\tassertThat(configurer.init).isTrue();\n\t\tassertThat(configurer.configure).isTrue();\n\t}\n\n\t@Test\n\tpublic void getWhenCustomContentNegotiationStrategyThenUses() throws Exception {\n\t\tthis.spring.register(CustomContentNegotiationStrategyConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\"));\n\t\tverify(CustomContentNegotiationStrategyConfig.CNS, atLeastOnce())\n\t\t\t.resolveMediaTypes(any(NativeWebRequest.class));\n\t}\n\n\t// gh-13084\n\t@Test\n\tpublic void configureWhenNoAuthenticationManagerAndObservationRegistryNotNoOpThenConfigure() throws Exception {\n\t\tthis.spring.register(ObservationConfig.class, NoAuthenticationManagerConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\"));\n\t}\n\n\t// gh-13203\n\t@Test\n\tpublic void disableConfigurerWhenAppliedByAnotherConfigurerThenNotApplied() {\n\t\tthis.spring.register(ApplyCustomDslConfig.class).autowire();\n\t\tSecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class);\n\t\tList<Filter> filters = filterChain.getFilters();\n\t\tassertThat(filters).doesNotHaveAnyElementsOfTypes(DefaultLoginPageGeneratingFilter.class,\n\t\t\t\tDefaultLogoutPageGeneratingFilter.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenCorsConfigurationSourceThenApplyCors() throws Exception {\n\t\tthis.spring.register(CorsConfigurationSourceConfig.class, DefaultWithFilterChainConfig.class).autowire();\n\t\tSecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class);\n\t\tCorsFilter corsFilter = (CorsFilter) filterChain.getFilters()\n\t\t\t.stream()\n\t\t\t.filter((f) -> f instanceof CorsFilter)\n\t\t\t.findFirst()\n\t\t\t.get();\n\t\tObject configSource = ReflectionTestUtils.getField(corsFilter, \"configSource\");\n\t\tassertThat(configSource).isInstanceOf(UrlBasedCorsConfigurationSource.class);\n\t}\n\n\t// gh-15378\n\t@Test\n\tpublic void configureWhenNoUrlBasedCorsConfigThenNoCorsAppliedAndVaryHeaderNotPresent() throws Exception {\n\t\tthis.spring.register(NonUrlBasedCorsConfig.class, DefaultWithFilterChainConfig.class).autowire();\n\t\tSecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class);\n\t\tassertThat(filterChain.getFilters()).noneMatch((filter) -> filter instanceof CorsFilter);\n\n\t\tthis.mockMvc.perform(get(\"/\")).andExpect(header().doesNotExist(\"Vary\"));\n\t}\n\n\t@Test\n\tpublic void configureWhenAddingCustomDslUsingWithThenApplied() throws Exception {\n\t\tthis.spring.register(WithCustomDslConfig.class, UserDetailsConfig.class).autowire();\n\t\tSecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class);\n\t\tList<Filter> filters = filterChain.getFilters();\n\t\tassertThat(filters).hasAtLeastOneElementOfType(UsernamePasswordAuthenticationFilter.class);\n\t\tthis.mockMvc.perform(formLogin()).andExpectAll(redirectedUrl(\"/\"), authenticated());\n\t}\n\n\t@Test\n\tpublic void configureWhenCustomDslAddedFromFactoriesAndDisablingUsingWithThenNotApplied() throws Exception {\n\t\tthis.springFactoriesLoader\n\t\t\t.when(() -> SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, getClass().getClassLoader()))\n\t\t\t.thenReturn(List.of(new WithCustomDsl()));\n\t\tthis.spring.register(WithCustomDslDisabledConfig.class, UserDetailsConfig.class).autowire();\n\t\tSecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class);\n\t\tList<Filter> filters = filterChain.getFilters();\n\t\tassertThat(filters).doesNotHaveAnyElementsOfTypes(UsernamePasswordAuthenticationFilter.class);\n\t\tthis.mockMvc.perform(formLogin()).andExpectAll(status().isNotFound(), unauthenticated());\n\t}\n\n\t@Test\n\tvoid loginWhenCompromisePasswordCheckerConfiguredAndPasswordCompromisedThenUnauthorized() throws Exception {\n\t\tthis.spring\n\t\t\t.register(SecurityEnabledConfig.class, UserDetailsConfig.class, CompromisedPasswordCheckerConfig.class)\n\t\t\t.autowire();\n\t\tthis.mockMvc.perform(formLogin().password(\"password\"))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/login?error\"), unauthenticated());\n\t}\n\n\t@Test\n\tvoid loginWhenCompromisedPasswordAndRedirectIfPasswordExceptionThenRedirectedToResetPassword() throws Exception {\n\t\tthis.spring\n\t\t\t.register(SecurityEnabledRedirectIfPasswordExceptionConfig.class, UserDetailsConfig.class,\n\t\t\t\t\tCompromisedPasswordCheckerConfig.class)\n\t\t\t.autowire();\n\t\tthis.mockMvc.perform(formLogin().password(\"password\"))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/reset-password\"), unauthenticated());\n\t}\n\n\t@Test\n\tvoid loginWhenCompromisePasswordCheckerConfiguredAndPasswordNotCompromisedThenSuccess() throws Exception {\n\t\tthis.spring\n\t\t\t.register(SecurityEnabledConfig.class, UserDetailsConfig.class, CompromisedPasswordCheckerConfig.class)\n\t\t\t.autowire();\n\t\tUserDetailsManager userDetailsManager = this.spring.getContext().getBean(UserDetailsManager.class);\n\t\tUserDetails notCompromisedPwUser = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user2\")\n\t\t\t.password(\"password2\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\tuserDetailsManager.createUser(notCompromisedPwUser);\n\t\tthis.mockMvc.perform(formLogin().user(\"user2\").password(\"password2\"))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/\"), authenticated());\n\t}\n\n\t@Test\n\tvoid authorizeHttpRequestsCustomizerBean() throws Exception {\n\t\tthis.spring.register(AuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();\n\n\t\tCustomizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(\"authorizeRequests\", Customizer.class);\n\n\t\tverify(authorizeRequests).customize(any());\n\n\t}\n\n\t@Test\n\tvoid multiAuthorizeHttpRequestsCustomizerBean() throws Exception {\n\t\tthis.spring.register(MultiAuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();\n\n\t\tCustomizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests0 = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(\"authorizeRequests0\", Customizer.class);\n\t\tCustomizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(\"authorizeRequests\", Customizer.class);\n\t\tInOrder inOrder = Mockito.inOrder(authorizeRequests0, authorizeRequests);\n\n\t\tArgumentCaptor<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> arg0 = ArgumentCaptor\n\t\t\t.forClass(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry.class);\n\t\tArgumentCaptor<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> arg1 = ArgumentCaptor\n\t\t\t.forClass(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry.class);\n\t\tinOrder.verify(authorizeRequests0).customize(arg0.capture());\n\t\tinOrder.verify(authorizeRequests).customize(arg1.capture());\n\t}\n\n\t@Test\n\tvoid disableAuthorizeHttpRequestsCustomizerBean() throws Exception {\n\t\tthis.spring.register(AuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();\n\n\t\tCustomizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(\"authorizeRequests\", Customizer.class);\n\n\t\tverify(authorizeRequests).customize(any());\n\n\t}\n\n\t@Test\n\tvoid httpSecurityCustomizerBean() throws Exception {\n\t\tthis.spring.register(HttpSecurityCustomizerBeanConfiguration.class, UserDetailsConfig.class).autowire();\n\n\t\tCustomizer<HttpSecurity> httpSecurityCustomizer = this.spring.getContext()\n\t\t\t.getBean(\"httpSecurityCustomizer\", Customizer.class);\n\n\t\tArgumentCaptor<HttpSecurity> arg0 = ArgumentCaptor.forClass(HttpSecurity.class);\n\t\tverify(httpSecurityCustomizer).customize(arg0.capture());\n\t}\n\n\t@Test\n\tvoid multiHttpSecurityCustomizerBean() throws Exception {\n\t\tthis.spring.register(MultiHttpSecurityCustomizerBeanConfiguration.class, UserDetailsConfig.class).autowire();\n\n\t\tCustomizer<HttpSecurity> httpSecurityCustomizer = this.spring.getContext()\n\t\t\t.getBean(\"httpSecurityCustomizer\", Customizer.class);\n\t\tCustomizer<HttpSecurity> httpSecurityCustomizer0 = this.spring.getContext()\n\t\t\t.getBean(\"httpSecurityCustomizer0\", Customizer.class);\n\t\tInOrder inOrder = Mockito.inOrder(httpSecurityCustomizer0, httpSecurityCustomizer);\n\n\t\tArgumentCaptor<HttpSecurity> arg0 = ArgumentCaptor.forClass(HttpSecurity.class);\n\t\tArgumentCaptor<HttpSecurity> arg1 = ArgumentCaptor.forClass(HttpSecurity.class);\n\t\tinOrder.verify(httpSecurityCustomizer0).customize(arg0.capture());\n\t\tinOrder.verify(httpSecurityCustomizer).customize(arg1.capture());\n\t}\n\n\t@RestController\n\tstatic class NameController {\n\n\t\t@GetMapping(\"/name\")\n\t\tCallable<String> name(Authentication authentication) {\n\t\t\treturn () -> authentication.getName();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultWithFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthorizeRequestsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().permitAll()\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityEnabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UserDetailsConfig {\n\n\t\t@Bean\n\t\tInMemoryUserDetailsManager userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class CustomAuthenticationEventPublisherConfig {\n\n\t\tstatic List<Authentication> EVENTS = new ArrayList<>();\n\n\t\tstatic void clearEvents() {\n\t\t\tEVENTS.clear();\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationEventPublisher publisher() {\n\t\t\treturn new AuthenticationEventPublisher() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void publishAuthenticationSuccess(Authentication authentication) {\n\t\t\t\t\tEVENTS.add(authentication);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void publishAuthenticationFailure(AuthenticationException exception,\n\t\t\t\t\t\tAuthentication authentication) {\n\t\t\t\t\tEVENTS.add(authentication);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class AuthenticationEventListenerConfig {\n\n\t\tstatic List<AbstractAuthenticationEvent> EVENTS = new ArrayList<>();\n\n\t\tstatic void clearEvents() {\n\t\t\tEVENTS.clear();\n\t\t}\n\n\t\t@EventListener\n\t\tvoid onAuthenticationSuccessEvent(AuthenticationSuccessEvent event) {\n\t\t\tEVENTS.add(event);\n\t\t}\n\n\t\t@EventListener\n\t\tvoid onAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) {\n\t\t\tEVENTS.add(event);\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BaseController {\n\n\t\t@GetMapping(\"/\")\n\t\tvoid index() {\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class UserController {\n\n\t\t@GetMapping(\"/user\")\n\t\tvoid user(HttpServletRequest request) {\n\t\t\tif (!request.isUserInRole(\"USER\")) {\n\t\t\t\tthrow new AccessDeniedException(\"This resource is only available to users\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomContentNegotiationStrategyConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\tstatic ContentNegotiationStrategy CNS = mock(ContentNegotiationStrategy.class);\n\n\t\t@Bean\n\t\tstatic ContentNegotiationStrategy cns() {\n\t\t\treturn CNS;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NoAuthenticationManagerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {\n\t\t\thttp.anonymous(AnonymousConfigurer::disable);\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationProvider authenticationProvider1() {\n\t\t\treturn new TestingAuthenticationProvider();\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationProvider authenticationProvider2() {\n\t\t\treturn new TestingAuthenticationProvider();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ObservationConfig {\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\treturn ObservationRegistry.create();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ApplyCustomDslConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.with(CustomDsl.customDsl());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\tstatic class CustomDsl extends AbstractHttpConfigurer<CustomDsl, HttpSecurity> {\n\n\t\t@Override\n\t\tpublic void init(HttpSecurity http) {\n\t\t\thttp.formLogin(FormLoginConfigurer::disable);\n\t\t}\n\n\t\tstatic CustomDsl customDsl() {\n\t\t\treturn new CustomDsl();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class CorsConfigurationSourceConfig {\n\n\t\t@Bean\n\t\tCorsConfigurationSource corsConfigurationSource() {\n\t\t\tUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n\t\t\tCorsConfiguration corsConfiguration = new CorsConfiguration();\n\t\t\tcorsConfiguration.setAllowedOrigins(List.of(\"http://localhost:8080\"));\n\t\t\tsource.registerCorsConfiguration(\"/**\", corsConfiguration);\n\t\t\treturn source;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NonUrlBasedCorsConfig {\n\n\t\t@Bean\n\t\tCorsConfigurationSource corsConfigurationSource() {\n\t\t\treturn new CustomCorsConfigurationSource();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\tstatic class CustomCorsConfigurationSource implements CorsConfigurationSource {\n\n\t\t@Override\n\t\tpublic CorsConfiguration getCorsConfiguration(HttpServletRequest request) {\n\t\t\tCorsConfiguration configuration = new CorsConfiguration();\n\t\t\tconfiguration.setAllowedOrigins(List.of(\"http://localhost:8080\"));\n\t\t\treturn configuration;\n\t\t}\n\n\t}\n\n\tstatic class DefaultConfigurer extends AbstractHttpConfigurer<DefaultConfigurer, HttpSecurity> {\n\n\t\tboolean init;\n\n\t\tboolean configure;\n\n\t\t@Override\n\t\tpublic void init(HttpSecurity builder) {\n\t\t\tthis.init = true;\n\t\t}\n\n\t\t@Override\n\t\tpublic void configure(HttpSecurity builder) {\n\t\t\tthis.configure = true;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WithCustomDslConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.with(new WithCustomDsl(), Customizer.withDefaults())\n\t\t\t\t\t.httpBasic(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WithCustomDslDisabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.with(new WithCustomDsl(), (dsl) -> dsl.disable())\n\t\t\t\t\t.httpBasic(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\tstatic class WithCustomDsl extends AbstractHttpConfigurer<WithCustomDsl, HttpSecurity> {\n\n\t\t@Override\n\t\tpublic void init(HttpSecurity builder) {\n\t\t\tbuilder.formLogin(Customizer.withDefaults());\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class CompromisedPasswordCheckerConfig {\n\n\t\t@Bean\n\t\tTestCompromisedPasswordChecker compromisedPasswordChecker() {\n\t\t\treturn new TestCompromisedPasswordChecker();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\tstatic class SecurityEnabledRedirectIfPasswordExceptionConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.formLogin((form) -> form\n\t\t\t\t\t\t\t.failureHandler((request, response, exception) -> {\n\t\t\t\t\t\t\t\tif (exception instanceof CompromisedPasswordException) {\n\t\t\t\t\t\t\t\t\tresponse.sendRedirect(\"/reset-password\");\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tresponse.sendRedirect(\"/login?error\");\n\t\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AuthorizeRequestsBeanConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain noAuthorizeSecurity(HttpSecurity http) throws Exception {\n\t\t\thttp.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tstatic Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests()\n\t\t\t\tthrows Exception {\n\t\t\tCustomizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authz = mock(\n\t\t\t\t\tCustomizer.class, withSettings().name(\"authz\"));\n\t\t\t// prevent validation errors of no authorization rules being defined\n\t\t\twillAnswer(((invocation) -> {\n\t\t\t\tAuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry requests = invocation\n\t\t\t\t\t.getArgument(0);\n\t\t\t\trequests.anyRequest().authenticated();\n\t\t\t\treturn null;\n\t\t\t})).given(authz).customize(any());\n\t\t\treturn authz;\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PublicController {\n\n\t\t\t@GetMapping(\"/public\")\n\t\t\tString permitAll() {\n\t\t\t\treturn \"public\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class DisableAuthorizeRequestsBeanConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t\thttp.httpBasic(withDefaults());\n\t\t\t// @formatter:off\n\t\t\thttp.authorizeHttpRequests((requests) -> requests\n\t\t\t\t.anyRequest().permitAll()\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tstatic Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests()\n\t\t\t\tthrows Exception {\n\t\t\t// @formatter:off\n\t\t\treturn (requests) -> requests\n\t\t\t\t.anyRequest().denyAll();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PublicController {\n\n\t\t\t@GetMapping(\"/public\")\n\t\t\tString permitAll() {\n\t\t\t\treturn \"public\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@Import(AuthorizeRequestsBeanConfiguration.class)\n\tstatic class MultiAuthorizeRequestsBeanConfiguration {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tstatic Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests0()\n\t\t\t\tthrows Exception {\n\t\t\treturn mock(Customizer.class, withSettings().name(\"authz0\"));\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class HttpSecurityCustomizerBeanConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t\thttp.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tstatic Customizer<HttpSecurity> httpSecurityCustomizer() {\n\t\t\treturn mock(Customizer.class, withSettings().name(\"httpSecurityCustomizer\"));\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PublicController {\n\n\t\t\t@GetMapping(\"/public\")\n\t\t\tString permitAll() {\n\t\t\t\treturn \"public\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@Import(HttpSecurityCustomizerBeanConfiguration.class)\n\tstatic class MultiHttpSecurityCustomizerBeanConfiguration {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tstatic Customizer<HttpSecurity> httpSecurityCustomizer0() throws Exception {\n\t\t\treturn mock(Customizer.class, withSettings().name(\"httpSecurityCustomizer0\"));\n\t\t}\n\n\t}\n\n\tprivate static class TestCompromisedPasswordChecker implements CompromisedPasswordChecker {\n\n\t\t@Override\n\t\tpublic CompromisedPasswordDecision check(String password) {\n\t\t\tif (\"password\".equals(password)) {\n\t\t\t\treturn new CompromisedPasswordDecision(true);\n\t\t\t}\n\t\t\treturn new CompromisedPasswordDecision(false);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.OrderUtils;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2AuthorizationServerConfiguration}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationServerConfigurationTests {\n\n\t@Test\n\tpublic void assertOrderHighestPrecedence() {\n\t\tMethod authorizationServerSecurityFilterChainMethod = ClassUtils.getMethod(\n\t\t\t\tOAuth2AuthorizationServerConfiguration.class, \"authorizationServerSecurityFilterChain\",\n\t\t\t\tHttpSecurity.class);\n\t\tInteger order = OrderUtils.getOrder(authorizationServerSecurityFilterChainMethod);\n\t\tassertThat(order).isEqualTo(Ordered.HIGHEST_PRECEDENCE);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizedClientManagerConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationContext;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.TokenExchangeOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2ClientConfiguration.OAuth2AuthorizedClientManagerConfiguration}.\n *\n * @author Joe Grandja\n * @author Steve Riesenberg\n */\npublic class OAuth2AuthorizedClientManagerConfigurationTests {\n\n\tprivate static OAuth2AccessTokenResponseClient<? super AbstractOAuth2AuthorizationGrantRequest> MOCK_RESPONSE_CLIENT;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Autowired(required = false)\n\tprivate AuthorizationCodeOAuth2AuthorizedClientProvider authorizationCodeAuthorizedClientProvider;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setUp() {\n\t\tMOCK_RESPONSE_CLIENT = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void loadContextWhenOAuth2ClientEnabledThenConfigured() {\n\t\tthis.spring.register(MinimalOAuth2ClientConfig.class).autowire();\n\t\tassertThat(this.authorizedClientManager).isNotNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizationCodeAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(\"google\")\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest))\n\t\t\t\t.extracting(OAuth2AuthorizationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(\"client_authorization_required\");\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationCodeAuthorizedClientProvider).authorize(any(OAuth2AuthorizationContext.class));\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRefreshTokenAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire();\n\t\ttestRefreshTokenGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRefreshTokenAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\t\ttestRefreshTokenGrant();\n\t}\n\n\tprivate void testRefreshTokenGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null);\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google\");\n\t\tOAuth2AuthorizedClient existingAuthorizedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\tauthentication.getName(), getExpiredAccessToken(), TestOAuth2RefreshTokens.refreshToken());\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(existingAuthorizedClient, authentication, this.request,\n\t\t\t\tthis.response);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withAuthorizedClient(existingAuthorizedClient)\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<OAuth2RefreshTokenGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2RefreshTokenGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);\n\t\tassertThat(grantRequest.getAccessToken()).isEqualTo(existingAuthorizedClient.getAccessToken());\n\t\tassertThat(grantRequest.getRefreshToken()).isEqualTo(existingAuthorizedClient.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire();\n\t\ttestClientCredentialsGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\t\ttestClientCredentialsGrant();\n\t}\n\n\tprivate void testClientCredentialsGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null);\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"github\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<OAuth2ClientCredentialsGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2ClientCredentialsGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire();\n\t\ttestJwtBearerGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\t\ttestJwtBearerGrant();\n\t}\n\n\tprivate void testJwtBearerGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(JwtBearerGrantRequest.class))).willReturn(accessTokenResponse);\n\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt());\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"okta\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<JwtBearerGrantRequest> grantRequestCaptor = ArgumentCaptor.forClass(JwtBearerGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tJwtBearerGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER);\n\t\tassertThat(grantRequest.getJwt().getSubject()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire();\n\t\ttestTokenExchangeGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\t\ttestTokenExchangeGrant();\n\t}\n\n\tprivate void testTokenExchangeGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt());\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"auth0\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(authentication.getToken());\n\t}\n\n\tprivate static OAuth2AccessToken getExpiredAccessToken() {\n\t\tInstant expiresAt = Instant.now().minusSeconds(60);\n\t\tInstant issuedAt = expiresAt.minus(Duration.ofDays(1));\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"scopes\", issuedAt, expiresAt,\n\t\t\t\tnew HashSet<>(Arrays.asList(\"read\", \"write\")));\n\t}\n\n\tprivate static Jwt getJwt() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new Jwt(\"token\", issuedAt, issuedAt.plusSeconds(300),\n\t\t\t\tCollections.singletonMap(JoseHeaderNames.ALG, \"RS256\"),\n\t\t\t\tCollections.singletonMap(JwtClaimNames.SUB, \"user\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MinimalOAuth2ClientConfig extends OAuth2ClientBaseConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAccessTokenResponseClientsConfig extends OAuth2ClientBaseConfig {\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthorizedClientProvidersConfig extends OAuth2ClientBaseConfig {\n\n\t\t@Bean\n\t\tAuthorizationCodeOAuth2AuthorizedClientProvider authorizationCodeProvider() {\n\t\t\treturn spy(new AuthorizationCodeOAuth2AuthorizedClientProvider());\n\t\t}\n\n\t\t@Bean\n\t\tRefreshTokenOAuth2AuthorizedClientProvider refreshTokenProvider() {\n\t\t\tRefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider();\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(new MockAccessTokenResponseClient<>());\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\t@Bean\n\t\tClientCredentialsOAuth2AuthorizedClientProvider clientCredentialsProvider() {\n\t\t\tClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(new MockAccessTokenResponseClient<>());\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\t@Bean\n\t\tJwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider() {\n\t\t\tJwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(new MockAccessTokenResponseClient<>());\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\t@Bean\n\t\tTokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider() {\n\t\t\tTokenExchangeOAuth2AuthorizedClientProvider authorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(new MockAccessTokenResponseClient<>());\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t}\n\n\tabstract static class OAuth2ClientBaseConfig {\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\t// @formatter:off\n\t\t\treturn new InMemoryClientRegistrationRepository(\n\t\t\t\t\tCommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t\t\t\t\t\t\t.clientId(\"google-client-id\")\n\t\t\t\t\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t\t\t.build(),\n\t\t\t\t\tCommonOAuth2Provider.GITHUB.getBuilder(\"github\")\n\t\t\t\t\t\t\t.clientId(\"github-client-id\")\n\t\t\t\t\t\t\t.clientSecret(\"github-client-secret\")\n\t\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t\t\t\t.build(),\n\t\t\t\t\tCommonOAuth2Provider.OKTA.getBuilder(\"okta\")\n\t\t\t\t\t\t\t.clientId(\"okta-client-id\")\n\t\t\t\t\t\t\t.clientSecret(\"okta-client-secret\")\n\t\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t\t\t\t\t.build(),\n\t\t\t\t\tClientRegistration.withRegistrationId(\"auth0\")\n\t\t\t\t\t\t\t.clientName(\"Auth0\")\n\t\t\t\t\t\t\t.clientId(\"auth0-client-id\")\n\t\t\t\t\t\t\t.clientSecret(\"auth0-client-secret\")\n\t\t\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t\t\t\t\t.scope(\"user.read\", \"user.write\")\n\t\t\t\t\t\t\t.build());\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t}\n\n\tprivate static class MockAccessTokenResponseClient<T extends AbstractOAuth2AuthorizationGrantRequest>\n\t\t\timplements OAuth2AccessTokenResponseClient<T> {\n\n\t\t@Override\n\t\tpublic OAuth2AccessTokenResponse getTokenResponse(T authorizationGrantRequest) {\n\t\t\treturn MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.NoUniqueBeanDefinitionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link OAuth2ClientConfiguration}.\n *\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2ClientConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void requestWhenAuthorizedClientFoundThenMethodArgumentResolved() throws Exception {\n\t\tString clientRegistrationId = \"client1\";\n\t\tString principalName = \"user1\";\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, \"password\");\n\t\tClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.registrationId(clientRegistrationId)\n\t\t\t.build();\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(eq(clientRegistrationId)))\n\t\t\t.willReturn(clientRegistration);\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class);\n\t\tOAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);\n\t\tgiven(authorizedClient.getClientRegistration()).willReturn(clientRegistration);\n\t\tgiven(authorizedClientRepository.loadAuthorizedClient(eq(clientRegistrationId), eq(authentication),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(authorizedClient);\n\t\tOAuth2AccessToken accessToken = mock(OAuth2AccessToken.class);\n\t\tgiven(authorizedClient.getAccessToken()).willReturn(accessToken);\n\t\tOAuth2AccessTokenResponseClient accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tOAuth2AuthorizedClientArgumentResolverConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository;\n\t\tOAuth2AuthorizedClientArgumentResolverConfig.AUTHORIZED_CLIENT_REPOSITORY = authorizedClientRepository;\n\t\tOAuth2AuthorizedClientArgumentResolverConfig.ACCESS_TOKEN_RESPONSE_CLIENT = accessTokenResponseClient;\n\t\tthis.spring.register(OAuth2AuthorizedClientArgumentResolverConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/authorized-client\").with(authentication(authentication)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"resolved\"));\n\t\t// @formatter:on\n\t\tverifyNoMoreInteractions(accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizedClientNotFoundAndClientCredentialsThenTokenResponseClientIsUsed()\n\t\t\tthrows Exception {\n\t\tString clientRegistrationId = \"client1\";\n\t\tString principalName = \"user1\";\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, \"password\");\n\t\tClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class);\n\t\tOAuth2AccessTokenResponseClient accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.registrationId(clientRegistrationId)\n\t\t\t.build();\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(clientRegistrationId)).willReturn(clientRegistration);\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withToken(\"access-token-1234\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(300)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(accessTokenResponseClient.getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\t\tOAuth2AuthorizedClientArgumentResolverConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository;\n\t\tOAuth2AuthorizedClientArgumentResolverConfig.AUTHORIZED_CLIENT_REPOSITORY = authorizedClientRepository;\n\t\tOAuth2AuthorizedClientArgumentResolverConfig.ACCESS_TOKEN_RESPONSE_CLIENT = accessTokenResponseClient;\n\t\tthis.spring.register(OAuth2AuthorizedClientArgumentResolverConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder authenticatedRequest = get(\"/authorized-client\")\n\t\t\t.with(authentication(authentication));\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(authenticatedRequest)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"resolved\"));\n\t\t// @formatter:on\n\t\tverify(accessTokenResponseClient, times(1)).getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class));\n\t}\n\n\t// gh-5321\n\t@Test\n\tpublic void loadContextWhenOAuth2AuthorizedClientRepositoryRegisteredTwiceThenThrowNoUniqueBeanDefinitionException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.spring.register(OAuth2AuthorizedClientRepositoryRegisteredTwiceConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class)\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"Expected single matching bean of type '\" + OAuth2AuthorizedClientRepository.class.getName()\n\t\t\t\t\t\t\t+ \"' but found 2: authorizedClientRepository1,authorizedClientRepository2\");\n\t}\n\n\t@Test\n\tpublic void loadContextWhenClientRegistrationRepositoryNotRegisteredThenThrowNoSuchBeanDefinitionException() {\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t.isThrownBy(() -> this.spring.register(ClientRegistrationRepositoryNotRegisteredConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(NoSuchBeanDefinitionException.class)\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"No qualifying bean of type '\" + ClientRegistrationRepository.class.getName() + \"' available\");\n\t}\n\n\t@Test\n\tpublic void loadContextWhenClientRegistrationRepositoryRegisteredTwiceThenThrowNoUniqueBeanDefinitionException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t\t.isThrownBy(\n\t\t\t\t\t\t() -> this.spring.register(ClientRegistrationRepositoryRegisteredTwiceConfig.class).autowire())\n\t\t\t\t.withMessageContaining(\n\t\t\t\t\t\t\"expected single matching bean but found 2: clientRegistrationRepository1,clientRegistrationRepository2\")\n\t\t\t\t.withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadContextWhenAccessTokenResponseClientRegisteredTwiceThenThrowNoUniqueBeanDefinitionException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t\t.isThrownBy(() -> this.spring.register(AccessTokenResponseClientRegisteredTwiceConfig.class).autowire())\n\t\t\t\t.havingRootCause()\n\t\t\t\t.isInstanceOf(NoUniqueBeanDefinitionException.class)\n\t\t\t\t.withMessageContaining(\n\t\t\t\t\t\t\"expected single matching bean but found 2: accessTokenResponseClient1,accessTokenResponseClient2\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-8700\n\t@Test\n\tpublic void requestWhenAuthorizedClientManagerConfiguredThenUsed() throws Exception {\n\t\tString clientRegistrationId = \"client1\";\n\t\tString principalName = \"user1\";\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, \"password\");\n\t\tClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class);\n\t\tOAuth2AuthorizedClientManager authorizedClientManager = mock(OAuth2AuthorizedClientManager.class);\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.registrationId(clientRegistrationId)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principalName,\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tgiven(authorizedClientManager.authorize(any())).willReturn(authorizedClient);\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository;\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.AUTHORIZED_CLIENT_REPOSITORY = authorizedClientRepository;\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.AUTHORIZED_CLIENT_MANAGER = authorizedClientManager;\n\t\tthis.spring.register(OAuth2AuthorizedClientManagerRegisteredConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder authenticatedRequest = get(\"/authorized-client\")\n\t\t\t.with(authentication(authentication));\n\t\t// @formatter:off\n\t\tthis.mockMvc\n\t\t\t\t.perform(authenticatedRequest)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"resolved\"));\n\t\t// @formatter:on\n\t\tverify(authorizedClientManager).authorize(any());\n\t\tverifyNoInteractions(clientRegistrationRepository);\n\t\tverifyNoInteractions(authorizedClientRepository);\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class OAuth2AuthorizedClientArgumentResolverConfig {\n\n\t\tstatic ClientRegistrationRepository CLIENT_REGISTRATION_REPOSITORY;\n\t\tstatic OAuth2AuthorizedClientRepository AUTHORIZED_CLIENT_REPOSITORY;\n\t\tstatic OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> ACCESS_TOKEN_RESPONSE_CLIENT;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn CLIENT_REGISTRATION_REPOSITORY;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn AUTHORIZED_CLIENT_REPOSITORY;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {\n\t\t\treturn ACCESS_TOKEN_RESPONSE_CLIENT;\n\t\t}\n\n\t\t@RestController\n\t\tclass Controller {\n\n\t\t\t@GetMapping(\"/authorized-client\")\n\t\t\tString authorizedClient(\n\t\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"client1\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn (authorizedClient != null) ? \"resolved\" : \"not-resolved\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class OAuth2AuthorizedClientRepositoryRegisteredTwiceConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2Login(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn mock(ClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository1() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository2() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {\n\t\t\treturn mock(OAuth2AccessTokenResponseClient.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class ClientRegistrationRepositoryNotRegisteredConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2Login(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class ClientRegistrationRepositoryRegisteredTwiceConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2Login(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository1() {\n\t\t\treturn mock(ClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository2() {\n\t\t\treturn mock(ClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {\n\t\t\treturn mock(OAuth2AccessTokenResponseClient.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class AccessTokenResponseClientRegisteredTwiceConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2Login(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn mock(ClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient1() {\n\t\t\treturn mock(OAuth2AccessTokenResponseClient.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient2() {\n\t\t\treturn mock(OAuth2AccessTokenResponseClient.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class OAuth2AuthorizedClientManagerRegisteredConfig {\n\n\t\tstatic ClientRegistrationRepository CLIENT_REGISTRATION_REPOSITORY;\n\t\tstatic OAuth2AuthorizedClientRepository AUTHORIZED_CLIENT_REPOSITORY;\n\t\tstatic OAuth2AuthorizedClientManager AUTHORIZED_CLIENT_MANAGER;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn CLIENT_REGISTRATION_REPOSITORY;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn AUTHORIZED_CLIENT_REPOSITORY;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientManager authorizedClientManager() {\n\t\t\treturn AUTHORIZED_CLIENT_MANAGER;\n\t\t}\n\n\t\t@RestController\n\t\tclass Controller {\n\n\t\t\t@GetMapping(\"/authorized-client\")\n\t\t\tString authorizedClient(\n\t\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"client1\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn (authorizedClient != null) ? \"resolved\" : \"not-resolved\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.endsWith;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link RegisterMissingBeanPostProcessor}.\n *\n * @author Steve Riesenberg\n */\npublic class RegisterMissingBeanPostProcessorTests {\n\n\tprivate final RegisterMissingBeanPostProcessor postProcessor = new RegisterMissingBeanPostProcessor();\n\n\t@Test\n\tpublic void postProcessBeanDefinitionRegistryWhenClassAddedThenRegisteredWithClass() {\n\t\tthis.postProcessor.addBeanDefinition(SimpleBean.class, null);\n\t\tthis.postProcessor.setBeanFactory(new DefaultListableBeanFactory());\n\n\t\tBeanDefinitionRegistry beanDefinitionRegistry = mock(BeanDefinitionRegistry.class);\n\t\tthis.postProcessor.postProcessBeanDefinitionRegistry(beanDefinitionRegistry);\n\n\t\tArgumentCaptor<BeanDefinition> beanDefinitionCaptor = ArgumentCaptor.forClass(BeanDefinition.class);\n\t\tverify(beanDefinitionRegistry).registerBeanDefinition(endsWith(\"SimpleBean\"), beanDefinitionCaptor.capture());\n\n\t\tRootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionCaptor.getValue();\n\t\tassertThat(beanDefinition.getBeanClass()).isEqualTo(SimpleBean.class);\n\t\tassertThat(beanDefinition.getInstanceSupplier()).isNull();\n\t}\n\n\t@Test\n\tpublic void postProcessBeanDefinitionRegistryWhenSupplierAddedThenRegisteredWithSupplier() {\n\t\tSupplier<SimpleBean> beanSupplier = () -> new SimpleBean(\"string\");\n\t\tthis.postProcessor.addBeanDefinition(SimpleBean.class, beanSupplier);\n\t\tthis.postProcessor.setBeanFactory(new DefaultListableBeanFactory());\n\n\t\tBeanDefinitionRegistry beanDefinitionRegistry = mock(BeanDefinitionRegistry.class);\n\t\tthis.postProcessor.postProcessBeanDefinitionRegistry(beanDefinitionRegistry);\n\n\t\tArgumentCaptor<BeanDefinition> beanDefinitionCaptor = ArgumentCaptor.forClass(BeanDefinition.class);\n\t\tverify(beanDefinitionRegistry).registerBeanDefinition(endsWith(\"SimpleBean\"), beanDefinitionCaptor.capture());\n\n\t\tRootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionCaptor.getValue();\n\t\tassertThat(beanDefinition.getBeanClass()).isEqualTo(SimpleBean.class);\n\t\tassertThat(beanDefinition.getInstanceSupplier()).isEqualTo(beanSupplier);\n\t}\n\n\t@Test\n\tpublic void postProcessBeanDefinitionRegistryWhenNoBeanDefinitionsAddedThenNoneRegistered() {\n\t\tthis.postProcessor.setBeanFactory(new DefaultListableBeanFactory());\n\n\t\tBeanDefinitionRegistry beanDefinitionRegistry = mock(BeanDefinitionRegistry.class);\n\t\tthis.postProcessor.postProcessBeanDefinitionRegistry(beanDefinitionRegistry);\n\t\tverifyNoInteractions(beanDefinitionRegistry);\n\t}\n\n\t@Test\n\tpublic void postProcessBeanDefinitionRegistryWhenBeanDefinitionAlreadyExistsThenNoneRegistered() {\n\t\tthis.postProcessor.addBeanDefinition(SimpleBean.class, null);\n\t\tDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();\n\t\tbeanFactory.registerBeanDefinition(\"simpleBean\", new RootBeanDefinition(SimpleBean.class));\n\t\tthis.postProcessor.setBeanFactory(beanFactory);\n\n\t\tBeanDefinitionRegistry beanDefinitionRegistry = mock(BeanDefinitionRegistry.class);\n\t\tthis.postProcessor.postProcessBeanDefinitionRegistry(beanDefinitionRegistry);\n\t\tverifyNoInteractions(beanDefinitionRegistry);\n\t}\n\n\tprivate static final class SimpleBean {\n\n\t\tprivate final String field;\n\n\t\tprivate SimpleBean(String field) {\n\t\t\tthis.field = field;\n\t\t}\n\n\t\tprivate String getField() {\n\t\t\treturn this.field;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/SecurityReactorContextConfigurationResourceServerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport jakarta.annotation.PreDestroy;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;\nimport org.springframework.security.oauth2.server.resource.authentication.TestBearerTokenAuthentications;\nimport org.springframework.security.oauth2.server.resource.web.reactive.function.client.ServletBearerExchangeFilterFunction;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for applications of {@link SecurityReactorContextConfiguration} in resource\n * servers.\n *\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SecurityReactorContextConfigurationResourceServerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t// gh-7418\n\t@Test\n\tpublic void requestWhenUsingFilterThenBearerTokenPropagated() throws Exception {\n\t\tBearerTokenAuthentication authentication = TestBearerTokenAuthentications.bearer();\n\t\tthis.spring.register(BearerFilterConfig.class, WebServerConfig.class, Controller.class).autowire();\n\t\tMockHttpServletRequestBuilder authenticatedRequest = get(\"/token\").with(authentication(authentication));\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(authenticatedRequest)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"Bearer token\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-7418\n\t@Test\n\tpublic void requestWhenNotUsingFilterThenBearerTokenNotPropagated() throws Exception {\n\t\tBearerTokenAuthentication authentication = TestBearerTokenAuthentications.bearer();\n\t\tthis.spring.register(BearerFilterlessConfig.class, WebServerConfig.class, Controller.class).autowire();\n\t\tMockHttpServletRequestBuilder authenticatedRequest = get(\"/token\").with(authentication(authentication));\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(authenticatedRequest)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tBearerTokenAuthentication authentication = TestBearerTokenAuthentications.bearer();\n\t\tthis.spring\n\t\t\t.register(BearerFilterConfig.class, WebServerConfig.class, Controller.class,\n\t\t\t\t\tSecurityContextChangedListenerConfig.class)\n\t\t\t.autowire();\n\t\tMockHttpServletRequestBuilder authenticatedRequest = get(\"/token\").with(authentication(authentication));\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(authenticatedRequest)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"Bearer token\"));\n\t\t// @formatter:on\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class BearerFilterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.securityContext((context) -> context.requireExplicitSave(false));\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tWebClient rest() {\n\t\t\tServletBearerExchangeFilterFunction bearer = new ServletBearerExchangeFilterFunction();\n\t\t\treturn WebClient.builder().filter(bearer).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class BearerFilterlessConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tWebClient rest() {\n\t\t\treturn WebClient.create();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class Controller {\n\n\t\tprivate final WebClient rest;\n\n\t\tprivate final String uri;\n\n\t\t@Autowired\n\t\tController(MockWebServer server, WebClient rest) {\n\t\t\tthis.uri = server.url(\"/\").toString();\n\t\t\tthis.rest = rest;\n\t\t}\n\n\t\t@GetMapping(\"/token\")\n\t\tString token() {\n\t\t\t// @formatter:off\n\t\t\treturn this.rest.get()\n\t\t\t\t\t.uri(this.uri)\n\t\t\t\t\t.retrieve()\n\t\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t\t.flatMap((result) -> this.rest.get()\n\t\t\t\t\t\t\t.uri(this.uri)\n\t\t\t\t\t\t\t.retrieve()\n\t\t\t\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t\t)\n\t\t\t\t\t.block();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class WebServerConfig {\n\n\t\tprivate final MockWebServer server = new MockWebServer();\n\n\t\t@Bean\n\t\tMockWebServer server() throws Exception {\n\t\t\tthis.server.setDispatcher(new AuthorizationHeaderDispatcher());\n\t\t\tthis.server.start();\n\t\t\treturn this.server;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws Exception {\n\t\t\tthis.server.shutdown();\n\t\t}\n\n\t}\n\n\tstatic class AuthorizationHeaderDispatcher extends Dispatcher {\n\n\t\t@Override\n\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\tMockResponse response = new MockResponse().setResponseCode(200);\n\t\t\tString header = request.getHeader(\"Authorization\");\n\t\t\tif (!StringUtils.hasText(header)) {\n\t\t\t\treturn response;\n\t\t\t}\n\t\t\treturn response.setBody(header);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/SecurityReactorContextConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.net.URI;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadFactory;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.CoreSubscriber;\nimport reactor.core.publisher.BaseSubscriber;\nimport reactor.core.publisher.Mono;\nimport reactor.core.publisher.Operators;\nimport reactor.test.StepVerifier;\nimport reactor.util.context.Context;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.task.SimpleAsyncTaskExecutor;\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration.SecurityReactorContextSubscriber;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.client.web.reactive.function.client.MockExchangeFunction;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link SecurityReactorContextConfiguration}.\n *\n * @author Joe Grandja\n * @since 5.2\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SecurityReactorContextConfigurationTests {\n\n\tprivate MockHttpServletRequest servletRequest;\n\n\tprivate MockHttpServletResponse servletResponse;\n\n\tprivate Authentication authentication;\n\n\tprivate SecurityReactorContextConfiguration.SecurityReactorContextSubscriberRegistrar subscriberRegistrar = new SecurityReactorContextConfiguration.SecurityReactorContextSubscriberRegistrar();\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.servletRequest = new MockHttpServletRequest();\n\t\tthis.servletResponse = new MockHttpServletResponse();\n\t\tthis.authentication = new TestingAuthenticationToken(\"principal\", \"password\");\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t\tRequestContextHolder.resetRequestAttributes();\n\t}\n\n\t@Test\n\tpublic void createSubscriberIfNecessaryWhenSubscriberContextContainsSecurityContextAttributesThenReturnOriginalSubscriber() {\n\t\tContext context = Context.of(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES, new HashMap<>());\n\t\tBaseSubscriber<Object> originalSubscriber = new BaseSubscriber<Object>() {\n\t\t\t@Override\n\t\t\tpublic Context currentContext() {\n\t\t\t\treturn context;\n\t\t\t}\n\t\t};\n\t\tCoreSubscriber<Object> resultSubscriber = this.subscriberRegistrar\n\t\t\t.createSubscriberIfNecessary(originalSubscriber);\n\t\tassertThat(resultSubscriber).isSameAs(originalSubscriber);\n\t}\n\n\t@Test\n\tpublic void createSubscriberIfNecessaryWhenWebSecurityContextAvailableThenCreateWithParentContext() {\n\t\tRequestContextHolder\n\t\t\t.setRequestAttributes(new ServletRequestAttributes(this.servletRequest, this.servletResponse));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tString testKey = \"test_key\";\n\t\tString testValue = \"test_value\";\n\t\tBaseSubscriber<Object> parent = new BaseSubscriber<Object>() {\n\t\t\t@Override\n\t\t\tpublic Context currentContext() {\n\t\t\t\treturn Context.of(testKey, testValue);\n\t\t\t}\n\t\t};\n\t\tCoreSubscriber<Object> subscriber = this.subscriberRegistrar.createSubscriberIfNecessary(parent);\n\t\tContext resultContext = subscriber.currentContext();\n\t\tassertThat(resultContext.getOrEmpty(testKey)).hasValue(testValue);\n\t\tMap<Object, Object> securityContextAttributes = resultContext\n\t\t\t.getOrDefault(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES, null);\n\t\tassertThat(securityContextAttributes).hasSize(3);\n\t\tassertThat(securityContextAttributes).contains(entry(HttpServletRequest.class, this.servletRequest),\n\t\t\t\tentry(HttpServletResponse.class, this.servletResponse),\n\t\t\t\tentry(Authentication.class, this.authentication));\n\t}\n\n\t@Test\n\tpublic void createSubscriberIfNecessaryWhenParentContextContainsSecurityContextAttributesThenUseParentContext() {\n\t\tRequestContextHolder\n\t\t\t.setRequestAttributes(new ServletRequestAttributes(this.servletRequest, this.servletResponse));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tContext parentContext = Context.of(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES,\n\t\t\t\tnew HashMap<>());\n\t\tBaseSubscriber<Object> parent = new BaseSubscriber<Object>() {\n\t\t\t@Override\n\t\t\tpublic Context currentContext() {\n\t\t\t\treturn parentContext;\n\t\t\t}\n\t\t};\n\t\tCoreSubscriber<Object> subscriber = this.subscriberRegistrar.createSubscriberIfNecessary(parent);\n\t\tContext resultContext = subscriber.currentContext();\n\t\tassertThat(resultContext).isSameAs(parentContext);\n\t}\n\n\t@Test\n\tpublic void createSubscriberIfNecessaryWhenNotServletRequestAttributesThenStillCreate() {\n\t\tRequestContextHolder.setRequestAttributes(new RequestAttributes() {\n\t\t\t@Override\n\t\t\tpublic Object getAttribute(String name, int scope) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void setAttribute(String name, Object value, int scope) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void removeAttribute(String name, int scope) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String[] getAttributeNames(int scope) {\n\t\t\t\treturn new String[0];\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void registerDestructionCallback(String name, Runnable callback, int scope) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object resolveReference(String key) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String getSessionId() {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object getSessionMutex() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\t\tCoreSubscriber<Object> subscriber = this.subscriberRegistrar\n\t\t\t.createSubscriberIfNecessary(Operators.emptySubscriber());\n\t\tassertThat(subscriber).isInstanceOf(SecurityReactorContextConfiguration.SecurityReactorContextSubscriber.class);\n\t}\n\n\t@Test\n\tpublic void createPublisherWhenLastOperatorAddedThenSecurityContextAttributesAvailable() {\n\t\t// Trigger the importing of SecurityReactorContextConfiguration via\n\t\t// OAuth2ImportSelector\n\t\tthis.spring.register(SecurityConfig.class).autowire();\n\t\t// Setup for SecurityReactorContextSubscriberRegistrar\n\t\tRequestContextHolder\n\t\t\t.setRequestAttributes(new ServletRequestAttributes(this.servletRequest, this.servletResponse));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tClientResponse clientResponseOk = ClientResponse.create(HttpStatus.OK).build();\n\t\t// @formatter:off\n\t\tExchangeFilterFunction filter = (req, next) -> Mono.deferContextual(Mono::just)\n\t\t\t\t.filter((ctx) -> ctx.hasKey(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES))\n\t\t\t\t.map((ctx) -> ctx.get(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES))\n\t\t\t\t.cast(Map.class)\n\t\t\t\t.map((attributes) -> {\n\t\t\t\t\tif (attributes.containsKey(HttpServletRequest.class)\n\t\t\t\t\t\t\t&& attributes.containsKey(HttpServletResponse.class)\n\t\t\t\t\t\t\t&& attributes.containsKey(Authentication.class)) {\n\t\t\t\t\t\treturn clientResponseOk;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\treturn ClientResponse.create(HttpStatus.NOT_FOUND).build();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t// @formatter:on\n\t\tClientRequest clientRequest = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tMockExchangeFunction exchange = new MockExchangeFunction();\n\t\tMap<Object, Object> expectedContextAttributes = new HashMap<>();\n\t\texpectedContextAttributes.put(HttpServletRequest.class, this.servletRequest);\n\t\texpectedContextAttributes.put(HttpServletResponse.class, this.servletResponse);\n\t\texpectedContextAttributes.put(Authentication.class, this.authentication);\n\t\tMono<ClientResponse> clientResponseMono = filter.filter(clientRequest, exchange)\n\t\t\t.flatMap((response) -> filter.filter(clientRequest, exchange));\n\t\t// @formatter:off\n\t\tStepVerifier.create(clientResponseMono)\n\t\t\t\t.expectAccessibleContext()\n\t\t\t\t.contains(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES, expectedContextAttributes)\n\t\t\t\t.then()\n\t\t\t\t.expectNext(clientResponseOk)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void createPublisherWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tthis.spring.register(SecurityConfig.class, SecurityContextChangedListenerConfig.class).autowire();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tstrategy.getContext().setAuthentication(this.authentication);\n\t\tClientResponse clientResponseOk = ClientResponse.create(HttpStatus.OK).build();\n\t\t// @formatter:off\n\t\tExchangeFilterFunction filter = (req, next) -> Mono.deferContextual(Mono::just)\n\t\t\t\t.filter((ctx) -> ctx.hasKey(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES))\n\t\t\t\t.map((ctx) -> ctx.get(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES))\n\t\t\t\t.cast(Map.class)\n\t\t\t\t.map((attributes) -> clientResponseOk);\n\t\t// @formatter:on\n\t\tClientRequest clientRequest = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tMockExchangeFunction exchange = new MockExchangeFunction();\n\t\tMap<Object, Object> expectedContextAttributes = new HashMap<>();\n\t\texpectedContextAttributes.put(HttpServletRequest.class, null);\n\t\texpectedContextAttributes.put(HttpServletResponse.class, null);\n\t\texpectedContextAttributes.put(Authentication.class, this.authentication);\n\t\tMono<ClientResponse> clientResponseMono = filter.filter(clientRequest, exchange)\n\t\t\t.flatMap((response) -> filter.filter(clientRequest, exchange));\n\t\t// @formatter:off\n\t\tStepVerifier.create(clientResponseMono)\n\t\t\t\t.expectAccessibleContext()\n\t\t\t\t.contains(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES, expectedContextAttributes)\n\t\t\t\t.then()\n\t\t\t\t.expectNext(clientResponseOk)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t\tverify(strategy, times(2)).getContext();\n\t}\n\n\t@Test\n\tpublic void createPublisherWhenThreadFactoryIsPlatformThenSecurityContextAttributesAvailable() throws Exception {\n\t\tthis.spring.register(SecurityConfig.class).autowire();\n\n\t\tThreadFactory threadFactory = Executors.defaultThreadFactory();\n\t\tassertContextAttributesAvailable(threadFactory);\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void createPublisherWhenThreadFactoryIsVirtualThenSecurityContextAttributesAvailable() throws Exception {\n\t\tthis.spring.register(SecurityConfig.class).autowire();\n\n\t\tThreadFactory threadFactory = new VirtualThreadTaskExecutor().getVirtualThreadFactory();\n\t\tassertContextAttributesAvailable(threadFactory);\n\t}\n\n\tprivate void assertContextAttributesAvailable(ThreadFactory threadFactory) throws Exception {\n\t\tMap<Object, Object> expectedContextAttributes = new HashMap<>();\n\t\texpectedContextAttributes.put(HttpServletRequest.class, this.servletRequest);\n\t\texpectedContextAttributes.put(HttpServletResponse.class, this.servletResponse);\n\t\texpectedContextAttributes.put(Authentication.class, this.authentication);\n\n\t\ttry (SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(threadFactory)) {\n\t\t\tFuture<Map<Object, Object>> future = taskExecutor.submit(this::propagateRequestAttributes);\n\t\t\tassertThat(future.get()).isEqualTo(expectedContextAttributes);\n\t\t}\n\t}\n\n\tprivate Map<Object, Object> propagateRequestAttributes() {\n\t\tRequestAttributes requestAttributes = new ServletRequestAttributes(this.servletRequest, this.servletResponse);\n\t\tRequestContextHolder.setRequestAttributes(requestAttributes);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(this.authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\t// @formatter:off\n\t\treturn Mono.deferContextual(Mono::just)\n\t\t\t\t.filter((ctx) -> ctx.hasKey(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES))\n\t\t\t\t.map((ctx) -> ctx.<Map<Object, Object>>get(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES))\n\t\t\t\t.map((attributes) -> {\n\t\t\t\t\tMap<Object, Object> map = new HashMap<>();\n\t\t\t\t\t// Copy over items from lazily loaded map\n\t\t\t\t\tArrays.asList(HttpServletRequest.class, HttpServletResponse.class, Authentication.class)\n\t\t\t\t\t\t\t.forEach((key) -> map.put(key, attributes.get(key)));\n\t\t\t\t\treturn map;\n\t\t\t\t})\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class WebMvcSecurityConfigurationTests {\n\n\t@Autowired\n\tWebApplicationContext context;\n\n\tMockMvc mockMvc;\n\n\tAuthentication authentication;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();\n\t\tthis.authentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void authenticationPrincipalResolved() throws Exception {\n\t\tthis.mockMvc.perform(get(\"/authentication-principal\"))\n\t\t\t.andExpect(assertResult(this.authentication.getPrincipal()))\n\t\t\t.andExpect(view().name(\"authentication-principal-view\"));\n\t}\n\n\t@Test\n\tpublic void deprecatedAuthenticationPrincipalResolved() throws Exception {\n\t\tthis.mockMvc.perform(get(\"/deprecated-authentication-principal\"))\n\t\t\t.andExpect(assertResult(this.authentication.getPrincipal()))\n\t\t\t.andExpect(view().name(\"deprecated-authentication-principal-view\"));\n\t}\n\n\t@Test\n\tpublic void csrfToken() throws Exception {\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"headerName\", \"paramName\", \"token\");\n\t\tMockHttpServletRequestBuilder request = get(\"/csrf\").requestAttr(CsrfToken.class.getName(), csrfToken);\n\t\tthis.mockMvc.perform(request).andExpect(assertResult(csrfToken));\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenTemplateDefaultsBeanThenResolvesExpression() throws Exception {\n\t\tthis.mockMvc.perform(get(\"/hi\")).andExpect(content().string(\"Hi, Stranger!\"));\n\t\tAuthentication harold = new TestingAuthenticationToken(\"harold\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(harold);\n\t\tthis.mockMvc.perform(get(\"/hi\")).andExpect(content().string(\"Hi, Harold!\"));\n\t}\n\n\t@Test\n\tpublic void resolveMetaAnnotationWhenTemplateDefaultsBeanThenResolvesExpression() throws Exception {\n\t\tthis.mockMvc.perform(get(\"/hello\")).andExpect(content().string(\"user\"));\n\t\tAuthentication harold = new TestingAuthenticationToken(\"harold\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(harold);\n\t\tthis.mockMvc.perform(get(\"/hello\")).andExpect(content().string(\"harold\"));\n\t}\n\n\tprivate ResultMatcher assertResult(Object expected) {\n\t\treturn model().attribute(\"result\", expected);\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@AuthenticationPrincipal(expression = \"#this.equals('{value}')\")\n\t@interface IsUser {\n\n\t\tString value() default \"user\";\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(expression = \"authentication.{property}\")\n\t@interface CurrentAuthenticationProperty {\n\n\t\tString property();\n\n\t}\n\n\t@Controller\n\tstatic class TestController {\n\n\t\t@RequestMapping(\"/authentication-principal\")\n\t\tModelAndView authenticationPrincipal(@AuthenticationPrincipal String principal) {\n\t\t\treturn new ModelAndView(\"authentication-principal-view\", \"result\", principal);\n\t\t}\n\n\t\t@RequestMapping(\"/deprecated-authentication-principal\")\n\t\tModelAndView deprecatedAuthenticationPrincipal(\n\t\t\t\t@org.springframework.security.web.bind.annotation.AuthenticationPrincipal String principal) {\n\t\t\treturn new ModelAndView(\"deprecated-authentication-principal-view\", \"result\", principal);\n\t\t}\n\n\t\t@RequestMapping(\"/csrf\")\n\t\tModelAndView csrf(CsrfToken token) {\n\t\t\treturn new ModelAndView(\"view\", \"result\", token);\n\t\t}\n\n\t\t@GetMapping(\"/hi\")\n\t\t@ResponseBody\n\t\tString ifUser(@IsUser(\"harold\") boolean isHarold) {\n\t\t\tif (isHarold) {\n\t\t\t\treturn \"Hi, Harold!\";\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn \"Hi, Stranger!\";\n\t\t\t}\n\t\t}\n\n\t\t@GetMapping(\"/hello\")\n\t\t@ResponseBody\n\t\tString getCurrentAuthenticationProperty(\n\t\t\t\t@CurrentAuthenticationProperty(property = \"principal\") String principal) {\n\t\t\treturn principal;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tTestController testController() {\n\t\t\treturn new TestController();\n\t\t}\n\n\t\t@Bean\n\t\tAnnotationTemplateExpressionDefaults templateExpressionDefaults() {\n\t\t\treturn new AnnotationTemplateExpressionDefaults();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.expression.AbstractSecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.users.AuthenticationTestConfiguration;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.PathPatternRequestTransformer;\nimport org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link WebSecurityConfiguration}.\n *\n * @author Rob Winch\n * @author Joe Grandja\n * @author Evgeniy Cheban\n * @author Marcus Da Coregio\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class WebSecurityConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tpublic SpringTestContext child = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void loadConfigWhenSecurityFilterChainsHaveOrderThenFilterChainsOrdered() {\n\t\tthis.spring.register(SortedSecurityFilterChainConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tList<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();\n\t\tassertThat(filterChains).hasSize(4);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/role1/**\");\n\t\tassertThat(filterChains.get(0).matches(request)).isTrue();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/role2/**\");\n\t\tassertThat(filterChains.get(1).matches(request)).isTrue();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/role3/**\");\n\t\tassertThat(filterChains.get(2).matches(request)).isTrue();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/**\");\n\t\tassertThat(filterChains.get(3).matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenSecurityFilterChainsHaveOrderOnBeanDefinitionsThenFilterChainsOrdered() {\n\t\tthis.spring.register(OrderOnBeanDefinitionsSecurityFilterChainConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tList<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();\n\t\tassertThat(filterChains).hasSize(2);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/role1/**\");\n\t\tassertThat(filterChains.get(0).matches(request)).isTrue();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/role2/**\");\n\t\tassertThat(filterChains.get(1).matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenWebInvocationPrivilegeEvaluatorSetThenIsRegistered() {\n\t\tPrivilegeEvaluatorConfigurerAdapterConfig.PRIVILEGE_EVALUATOR = mock(WebInvocationPrivilegeEvaluator.class);\n\t\tthis.spring.register(PrivilegeEvaluatorConfigurerAdapterConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))\n\t\t\t.isSameAs(PrivilegeEvaluatorConfigurerAdapterConfig.PRIVILEGE_EVALUATOR);\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenSecurityExpressionHandlerSetThenIsRegistered() {\n\t\tthis.spring.register(WebSecurityExpressionHandlerConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(\"webSecurityExpressionHandler\", SecurityExpressionHandler.class))\n\t\t\t.isSameAs(this.spring.getContext().getBean(\"mock\"));\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenSecurityExpressionHandlerIsNullThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullWebSecurityExpressionHandlerConfig.class).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.isExactlyInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenDefaultSecurityExpressionHandlerThenDefaultIsRegistered() {\n\t\tthis.spring.register(WebSecurityExpressionHandlerDefaultsConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(SecurityExpressionHandler.class))\n\t\t\t.isInstanceOf(AbstractSecurityExpressionHandler.class);\n\t}\n\n\t@Test\n\tpublic void securityExpressionHandlerWhenRoleHierarchyBeanThenRoleHierarchyUsed() {\n\t\tthis.spring.register(WebSecurityExpressionHandlerRoleHierarchyBeanConfig.class).autowire();\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"notused\", \"ROLE_ADMIN\");\n\t\tFilterInvocation invocation = new FilterInvocation(new MockHttpServletRequest(\"GET\", \"\"),\n\t\t\t\tnew MockHttpServletResponse(), new MockFilterChain());\n\t\tAbstractSecurityExpressionHandler handler = this.spring.getContext()\n\t\t\t.getBean(AbstractSecurityExpressionHandler.class);\n\t\tEvaluationContext evaluationContext = handler.createEvaluationContext(authentication, invocation);\n\t\tExpression expression = handler.getExpressionParser().parseExpression(\"hasRole('ROLE_USER')\");\n\t\tboolean granted = expression.getValue(evaluationContext, Boolean.class);\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void securityExpressionHandlerWhenPermissionEvaluatorBeanThenPermissionEvaluatorUsed() {\n\t\tthis.spring.register(WebSecurityExpressionHandlerPermissionEvaluatorBeanConfig.class).autowire();\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"notused\");\n\t\tFilterInvocation invocation = new FilterInvocation(new MockHttpServletRequest(\"GET\", \"\"),\n\t\t\t\tnew MockHttpServletResponse(), new MockFilterChain());\n\t\tAbstractSecurityExpressionHandler handler = this.spring.getContext()\n\t\t\t.getBean(AbstractSecurityExpressionHandler.class);\n\t\tEvaluationContext evaluationContext = handler.createEvaluationContext(authentication, invocation);\n\t\tExpression expression = handler.getExpressionParser().parseExpression(\"hasPermission(#study,'DELETE')\");\n\t\tboolean granted = expression.getValue(evaluationContext, Boolean.class);\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenRequestMatcherIsRegistered() {\n\t\tthis.spring.register(WebInvocationPrivilegeEvaluatorDefaultsConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))\n\t\t\t.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenSecurityFilterChainBeanThenDefaultWebInvocationPrivilegeEvaluatorIsRegistered() {\n\t\tthis.spring.register(AuthorizeRequestsFilterChainConfig.class).autowire();\n\n\t\tassertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))\n\t\t\t.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);\n\t}\n\n\t// SEC-2303\n\t@Test\n\tpublic void loadConfigWhenDefaultSecurityExpressionHandlerThenBeanResolverSet() throws Exception {\n\t\tthis.spring.register(DefaultExpressionHandlerSetsBeanResolverConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\")).andExpect(status().isOk());\n\t\tthis.mockMvc.perform(post(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t// SEC-2461\n\t@Test\n\tpublic void loadConfigWhenMultipleWebSecurityConfigurationThenContextLoads() {\n\t\tthis.spring.register(ParentConfig.class).autowire();\n\t\tthis.child.register(ChildConfig.class);\n\t\tthis.child.getContext().setParent(this.spring.getContext());\n\t\tthis.child.autowire();\n\t\tassertThat(this.spring.getContext().getBean(\"springSecurityFilterChain\")).isNotNull();\n\t\tassertThat(this.child.getContext().getBean(\"springSecurityFilterChain\")).isNotNull();\n\t\tassertThat(this.spring.getContext().containsBean(\"springSecurityFilterChain\")).isTrue();\n\t\tassertThat(this.child.getContext().containsBean(\"springSecurityFilterChain\")).isTrue();\n\t}\n\n\t// SEC-2773\n\t@Test\n\tpublic void getMethodDelegatingApplicationListenerWhenWebSecurityConfigurationThenIsStatic() {\n\t\tMethod method = ClassUtils.getMethod(WebSecurityConfiguration.class, \"delegatingApplicationListener\",\n\t\t\t\t(Class<?>[]) null);\n\t\tassertThat(Modifier.isStatic(method.getModifiers())).isTrue();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenOnlyWebSecurityCustomizerThenDefaultFilterChainCreated() {\n\t\tthis.spring.register(WebSecurityCustomizerConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tList<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();\n\t\tassertThat(filterChains).hasSize(3);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/ignore1\");\n\t\tassertThat(filterChains.get(0).matches(request)).isTrue();\n\t\tassertThat(filterChains.get(0).getFilters()).isEmpty();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/ignore2\");\n\t\tassertThat(filterChains.get(1).matches(request)).isTrue();\n\t\tassertThat(filterChains.get(1).getFilters()).isEmpty();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/test/**\");\n\t\tassertThat(filterChains.get(2).matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenWebSecurityCustomizerAndFilterChainThenFilterChainsOrdered() {\n\t\tthis.spring.register(CustomizerAndFilterChainConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tList<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();\n\t\tassertThat(filterChains).hasSize(3);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/ignore1\");\n\t\tassertThat(filterChains.get(0).matches(request)).isTrue();\n\t\tassertThat(filterChains.get(0).getFilters()).isEmpty();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/ignore2\");\n\t\tassertThat(filterChains.get(1).matches(request)).isTrue();\n\t\tassertThat(filterChains.get(1).getFilters()).isEmpty();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/role1/**\");\n\t\tassertThat(filterChains.get(2).matches(request)).isTrue();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/test/**\");\n\t\tassertThat(filterChains.get(2).matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenCustomizersHaveOrderThenCustomizersOrdered() {\n\t\tthis.spring.register(OrderedCustomizerConfig.class).autowire();\n\t\tFilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tList<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();\n\t\tassertThat(filterChains).hasSize(3);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/ignore1\");\n\t\tassertThat(filterChains.get(0).matches(request)).isTrue();\n\t\tassertThat(filterChains.get(0).getFilters()).isEmpty();\n\t\trequest = new MockHttpServletRequest(\"GET\", \"/ignore2\");\n\t\tassertThat(filterChains.get(1).matches(request)).isTrue();\n\t\tassertThat(filterChains.get(1).getFilters()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenTwoSecurityFilterChainsThenRequestMatcherDelegatingWebInvocationPrivilegeEvaluator() {\n\t\tthis.spring.register(TwoSecurityFilterChainConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))\n\t\t\t.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenTwoSecurityFilterChainDebugThenRequestMatcherDelegatingWebInvocationPrivilegeEvaluator() {\n\t\tthis.spring.register(TwoSecurityFilterChainConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))\n\t\t\t.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);\n\t}\n\n\t// gh-10554\n\t@Test\n\tpublic void loadConfigWhenMultipleSecurityFilterChainsThenWebInvocationPrivilegeEvaluatorApplySecurity() {\n\t\tthis.spring.register(MultipleSecurityFilterChainConfig.class).autowire();\n\t\tWebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext()\n\t\t\t.getBean(WebInvocationPrivilegeEvaluator.class);\n\t\tassertUserPermissions(privilegeEvaluator);\n\t\tassertAdminPermissions(privilegeEvaluator);\n\t\tassertAnotherUserPermission(privilegeEvaluator);\n\t}\n\n\t// gh-10554\n\t@Test\n\tpublic void loadConfigWhenMultipleSecurityFilterChainAndIgnoringThenWebInvocationPrivilegeEvaluatorAcceptsNullAuthenticationOnIgnored() {\n\t\tthis.spring.register(MultipleSecurityFilterChainIgnoringConfig.class).autowire();\n\t\tWebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext()\n\t\t\t.getBean(WebInvocationPrivilegeEvaluator.class);\n\t\tassertUserPermissions(privilegeEvaluator);\n\t\tassertAdminPermissions(privilegeEvaluator);\n\t\tassertAnotherUserPermission(privilegeEvaluator);\n\t\t// null authentication\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/user\", null)).isFalse();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/admin\", null)).isFalse();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/another\", null)).isTrue();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/ignoring1\", null)).isTrue();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/ignoring1/child\", null)).isTrue();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenUsePathPatternThenEvaluates() {\n\t\tthis.spring.register(UsePathPatternConfig.class).autowire();\n\t\tWebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext()\n\t\t\t.getBean(WebInvocationPrivilegeEvaluator.class);\n\t\tassertUserPermissions(privilegeEvaluator);\n\t\tassertAdminPermissions(privilegeEvaluator);\n\t\tassertAnotherUserPermission(privilegeEvaluator);\n\t\t// null authentication\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/user\", null)).isFalse();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/admin\", null)).isFalse();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/another\", null)).isTrue();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/ignoring1\", null)).isTrue();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/ignoring1/child\", null)).isTrue();\n\t\tAuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.class);\n\t\tverify(requestTransformer, atLeastOnce()).transform(any());\n\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenTwoSecurityFilterChainsPresentAndSecondWithAnyRequestThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(MultipleAnyRequestSecurityFilterChainConfig.class).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.isInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void avoidUnnecessaryHttpSecurityInstantiationWhenProvideOneSecurityFilterChain() {\n\t\tthis.spring.register(SecurityFilterChainConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(CountHttpSecurityBeanPostProcessor.class).instantiationCount)\n\t\t\t.isEqualTo(1);\n\t}\n\n\tprivate void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) {\n\t\tAuthentication anotherUser = new TestingAuthenticationToken(\"anotherUser\", \"password\", \"ROLE_ANOTHER\");\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/user\", anotherUser)).isFalse();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/admin\", anotherUser)).isFalse();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/another\", anotherUser)).isTrue();\n\t}\n\n\tprivate void assertAdminPermissions(WebInvocationPrivilegeEvaluator privilegeEvaluator) {\n\t\tAuthentication admin = new TestingAuthenticationToken(\"admin\", \"password\", \"ROLE_ADMIN\");\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/user\", admin)).isFalse();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/admin\", admin)).isTrue();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/another\", admin)).isTrue();\n\t}\n\n\tprivate void assertUserPermissions(WebInvocationPrivilegeEvaluator privilegeEvaluator) {\n\t\tAuthentication user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/user\", user)).isTrue();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/admin\", user)).isFalse();\n\t\tassertThat(privilegeEvaluator.isAllowed(\"/another\", user)).isTrue();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(CountHttpSecurityBeanPostProcessor.class)\n\tstatic class SecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()).build();\n\t\t}\n\n\t}\n\n\tstatic class CountHttpSecurityBeanPostProcessor implements BeanPostProcessor {\n\n\t\tint instantiationCount = 0;\n\n\t\t@Override\n\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tif (bean instanceof HttpSecurity) {\n\t\t\t\tthis.instantiationCount++;\n\t\t\t}\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class SortedSecurityFilterChainConfig {\n\n\t\t@Order(1)\n\t\t@Bean\n\t\tSecurityFilterChain filterChain1(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.securityMatcher(pathPattern(\"/role1/**\"))\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().hasRole(\"1\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Order(2)\n\t\t@Bean\n\t\tSecurityFilterChain filterChain2(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.securityMatcher(pathPattern(\"/role2/**\"))\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().hasRole(\"2\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Order(3)\n\t\t@Bean\n\t\tSecurityFilterChain filterChain3(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.securityMatcher(pathPattern(\"/role3/**\"))\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().hasRole(\"3\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain4(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"4\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class OrderOnBeanDefinitionsSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain securityFilterChain1(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.securityMatcher(pathPattern(\"/role1/**\"))\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().hasRole(\"1\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tTestSecurityFilterChain securityFilterChain2(HttpSecurity http) throws Exception {\n\t\t\treturn new TestSecurityFilterChain();\n\t\t}\n\n\t\t@Order(2)\n\t\tstatic class TestSecurityFilterChain implements SecurityFilterChain {\n\n\t\t\t@Override\n\t\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic List<Filter> getFilters() {\n\t\t\t\treturn new ArrayList<>();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PrivilegeEvaluatorConfigurerAdapterConfig {\n\n\t\tstatic WebInvocationPrivilegeEvaluator PRIVILEGE_EVALUATOR;\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.privilegeEvaluator(PRIVILEGE_EVALUATOR);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebSecurityExpressionHandlerConfig {\n\n\t\tSecurityExpressionHandler<FilterInvocation> expressionHandler = mock(SecurityExpressionHandler.class);\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.expressionHandler(this.expressionHandler);\n\t\t}\n\n\t\t@Bean(\"mock\")\n\t\tSecurityExpressionHandler<FilterInvocation> expressionHandler() {\n\t\t\treturn this.expressionHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullWebSecurityExpressionHandlerConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.expressionHandler(null);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebSecurityExpressionHandlerDefaultsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebSecurityExpressionHandlerRoleHierarchyBeanConfig {\n\n\t\t@Bean\n\t\tRoleHierarchy roleHierarchy() {\n\t\t\treturn RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > ROLE_USER\");\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebSecurityExpressionHandlerPermissionEvaluatorBeanConfig {\n\n\t\tstatic final PermissionEvaluator PERMIT_ALL_PERMISSION_EVALUATOR = new PermissionEvaluator() {\n\t\t\t@Override\n\t\t\tpublic boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\t\t\tObject permission) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\n\t\t@Bean\n\t\tPermissionEvaluator permissionEvaluator() {\n\t\t\treturn PERMIT_ALL_PERMISSION_EVALUATOR;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebInvocationPrivilegeEvaluatorDefaultsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthorizeRequestsFilterChainConfig {\n\n\t\t@Bean\n\t\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultExpressionHandlerSetsBeanResolverConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, WebExpressionAuthorizationManager.Builder authz)\n\t\t\t\tthrows Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().access(authz.expression(\"request.method == 'GET' ? @b.grant() : @b.deny()\"))\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tWebExpressionAuthorizationManager.Builder authz() {\n\t\t\treturn WebExpressionAuthorizationManager.withDefaults();\n\t\t}\n\n\t\t@Bean\n\t\tpublic MyBean b() {\n\t\t\treturn new MyBean();\n\t\t}\n\n\t\t@RestController\n\t\tclass HomeController {\n\n\t\t\t@GetMapping(\"/\")\n\t\t\tString home() {\n\t\t\t\treturn \"home\";\n\t\t\t}\n\n\t\t}\n\n\t\tstatic class MyBean {\n\n\t\t\tpublic boolean deny() {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tpublic boolean grant() {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ParentConfig {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tauth.inMemoryAuthentication();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ChildConfig {\n\n\t}\n\n\t@Configuration\n\tstatic class SubclassConfig extends WebSecurityConfiguration {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class WebSecurityCustomizerConfig {\n\n\t\t@Bean\n\t\tpublic WebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/ignore1\", \"/ignore2\");\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class CustomizerAndFilterChainConfig {\n\n\t\t@Bean\n\t\tpublic WebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/ignore1\", \"/ignore2\");\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.securityMatcher(pathPattern(\"/role1/**\"))\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().hasRole(\"1\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class OrderedCustomizerConfig {\n\n\t\t@Order(1)\n\t\t@Bean\n\t\tpublic WebSecurityCustomizer webSecurityCustomizer1() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/ignore1\");\n\t\t}\n\n\t\t@Order(2)\n\t\t@Bean\n\t\tpublic WebSecurityCustomizer webSecurityCustomizer2() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/ignore2\");\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class TwoSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain path1(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests.requestMatchers(pathPattern(\"/path1/**\")))\n\t\t\t\t.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.LOWEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain permitAll(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity(debug = true)\n\tstatic class TwoSecurityFilterChainDebugConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain path1(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.securityMatchers((requests) -> requests.requestMatchers(pathPattern(\"/path1/**\")))\n\t\t\t\t\t.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.LOWEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain permitAll(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class MultipleSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests.requestMatchers(pathPattern(\"/user\")))\n\t\t\t\t.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole(\"USER\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE + 1)\n\t\tpublic SecurityFilterChain path1(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests.requestMatchers(pathPattern(\"/admin\")))\n\t\t\t\t.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole(\"ADMIN\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.LOWEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain permitAll(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class MultipleSecurityFilterChainIgnoringConfig {\n\n\t\t@Bean\n\t\tpublic WebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/ignoring1/**\");\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.securityMatchers((requests) -> requests.requestMatchers(pathPattern(\"/user\")))\n\t\t\t\t\t.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole(\"USER\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE + 1)\n\t\tpublic SecurityFilterChain admin(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.securityMatchers((requests) -> requests.requestMatchers(pathPattern(\"/admin\")))\n\t\t\t\t\t.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole(\"ADMIN\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.LOWEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain permitAll(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class UsePathPatternConfig {\n\n\t\t@Bean\n\t\tAuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer pathPatternRequestTransformer() {\n\t\t\treturn spy(new PathPatternRequestTransformer());\n\t\t}\n\n\t\t@Bean\n\t\tpublic WebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/ignoring1/**\");\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests.requestMatchers(pathPattern(\"/user\")))\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().hasRole(\"USER\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE + 1)\n\t\tpublic SecurityFilterChain admin(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests.requestMatchers(pathPattern(\"/admin\")))\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().hasRole(\"ADMIN\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.LOWEST_PRECEDENCE)\n\t\tpublic SecurityFilterChain permitAll(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class MultipleAnyRequestSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\t@Order(0)\n\t\tSecurityFilterChain api1(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((auth) -> auth.anyRequest().authenticated());\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain api2(HttpSecurity http) throws Exception {\n\t\t\thttp.securityMatcher(\"/app/**\").authorizeHttpRequests((auth) -> auth.anyRequest().authenticated());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/sec2377/Sec2377Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration.sec2377;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.security.config.annotation.web.configuration.sec2377.a.Sec2377AConfig;\nimport org.springframework.security.config.annotation.web.configuration.sec2377.b.Sec2377BConfig;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class Sec2377Tests {\n\n\tpublic final SpringTestContext parent = new SpringTestContext(this);\n\n\tpublic final SpringTestContext child = new SpringTestContext(this);\n\n\t@Test\n\tpublic void refreshContextWhenParentAndChildRegisteredThenNoException() {\n\t\tthis.parent.register(Sec2377AConfig.class).autowire();\n\t\tConfigurableApplicationContext context = this.child.register(Sec2377BConfig.class).getContext();\n\t\tcontext.setParent(this.parent.getContext());\n\t\tthis.child.autowire();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/sec2377/a/Sec2377AConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration.sec2377.a;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\n\n@Configuration\n@EnableWebSecurity\npublic class Sec2377AConfig {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configuration/sec2377/b/Sec2377BConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configuration.sec2377.b;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\n\n@Configuration\n@EnableWebSecurity\npublic class Sec2377BConfig {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RegexRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\npublic class AbstractConfigAttributeRequestMatcherRegistryTests {\n\n\tprivate ConcreteAbstractRequestMatcherMappingConfigurer registry;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.registry = new ConcreteAbstractRequestMatcherMappingConfigurer();\n\t}\n\n\t@Test\n\tpublic void testGetRequestMatcherIsTypeRegexMatcher() {\n\t\tList<RequestMatcher> requestMatchers = this.registry\n\t\t\t.requestMatchers(new RegexRequestMatcher(\"/a.*\", HttpMethod.GET.name()));\n\t\tfor (RequestMatcher requestMatcher : requestMatchers) {\n\t\t\tassertThat(requestMatcher).isInstanceOf(RegexRequestMatcher.class);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testRequestMatcherIsTypeRegexMatcher() {\n\t\tList<RequestMatcher> requestMatchers = this.registry.requestMatchers(new RegexRequestMatcher(\"/a.*\", null));\n\t\tfor (RequestMatcher requestMatcher : requestMatchers) {\n\t\t\tassertThat(requestMatcher).isInstanceOf(RegexRequestMatcher.class);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testGetRequestMatcherIsTypePathPatternRequestMatcher() {\n\t\tList<RequestMatcher> requestMatchers = this.registry.requestMatchers(pathPattern(HttpMethod.GET, \"/a.*\"));\n\t\tfor (RequestMatcher requestMatcher : requestMatchers) {\n\t\t\tassertThat(requestMatcher).isInstanceOf(PathPatternRequestMatcher.class);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testRequestMatcherIsTypePathPatternRequestMatcher() {\n\t\tList<RequestMatcher> requestMatchers = this.registry.requestMatchers(pathPattern(\"/a.*\"));\n\t\tfor (RequestMatcher requestMatcher : requestMatchers) {\n\t\t\tassertThat(requestMatcher).isInstanceOf(PathPatternRequestMatcher.class);\n\t\t}\n\t}\n\n\tstatic class ConcreteAbstractRequestMatcherMappingConfigurer\n\t\t\textends AbstractConfigAttributeRequestMatcherRegistry<List<RequestMatcher>> {\n\n\t\t@Override\n\t\tprotected List<RequestMatcher> chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {\n\t\t\treturn requestMatchers;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class AnonymousConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void requestWhenAnonymousTwiceInvokedThenDoesNotOverride() throws Exception {\n\t\tthis.spring.register(InvokeTwiceDoesNotOverride.class, PrincipalController.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\")).andExpect(content().string(\"principal\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenAnonymousPrincipalInLambdaThenPrincipalUsed() throws Exception {\n\t\tthis.spring.register(AnonymousPrincipalInLambdaConfig.class, PrincipalController.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\")).andExpect(content().string(\"principal\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring\n\t\t\t.register(AnonymousPrincipalInLambdaConfig.class, SecurityContextChangedListenerConfig.class,\n\t\t\t\t\tPrincipalController.class)\n\t\t\t.autowire();\n\t\tthis.mockMvc.perform(get(\"/\")).andExpect(content().string(\"principal\"));\n\t\tSecurityContextChangedListener listener = this.spring.getContext()\n\t\t\t.getBean(SecurityContextChangedListener.class);\n\t\tverify(listener).securityContextChanged(setAuthentication(AnonymousAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void requestWhenAnonymousDisabledInLambdaThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(AnonymousDisabledInLambdaConfig.class, PrincipalController.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void requestWhenAnonymousWithDefaultsInLambdaThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(AnonymousWithDefaultsInLambdaConfig.class, PrincipalController.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t// gh-14941\n\t@Test\n\tpublic void shouldReturnMyCustomAnonymousConfig() throws Exception {\n\t\tthis.spring.register(AnonymousInCustomConfigurer.class, PrincipalController.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/\")).andExpect(status().isOk()).andExpect(content().string(\"myAnonymousUser\"));\n\t}\n\n\t@Test\n\tpublic void anonymousAuthenticationWhenUsingAuthenticationDetailsSourceRefThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(AuthenticationDetailsSourceAnonymousConfig.class).autowire();\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> source = this.spring.getContext()\n\t\t\t.getBean(AuthenticationDetailsSource.class);\n\t\tthis.mockMvc.perform(get(\"/\"));\n\t\tverify(source).buildDetails(any(HttpServletRequest.class));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AuthenticationDetailsSourceAnonymousConfig {\n\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.anonymous((anonymous) -> anonymous\n\t\t\t\t.withObjectPostProcessor(new ObjectPostProcessor<AnonymousAuthenticationFilter>() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic <O extends AnonymousAuthenticationFilter> O postProcess(O object) {\n\t\t\t\t\t\tobject.setAuthenticationDetailsSource(\n\t\t\t\t\t\t\t\tAuthenticationDetailsSourceAnonymousConfig.this.authenticationDetailsSource);\n\t\t\t\t\t\treturn object;\n\t\t\t\t\t}\n\n\t\t\t\t})).build();\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource() {\n\t\t\treturn this.authenticationDetailsSource;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class InvokeTwiceDoesNotOverride {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.anonymous((anonymous) -> anonymous\n\t\t\t\t\t.key(\"key\")\n\t\t\t\t\t.principal(\"principal\"))\n\t\t\t\t.anonymous(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AnonymousPrincipalInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.anonymous((anonymous) -> anonymous\n\t\t\t\t\t\t.principal(\"principal\")\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AnonymousDisabledInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().anonymous())\n\t\t\t\t.anonymous(AbstractHttpConfigurer::disable);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AnonymousWithDefaultsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().permitAll()\n\t\t\t\t)\n\t\t\t\t.anonymous(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class AnonymousInCustomConfigurer {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll())\n\t\t\t\t.with(new CustomDsl(), withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\tstatic class CustomDsl extends AbstractHttpConfigurer<CustomDsl, HttpSecurity> {\n\n\t\t\t@Override\n\t\t\tpublic void init(HttpSecurity http) {\n\t\t\t\thttp.anonymous((anonymous) -> anonymous.principal(\"myAnonymousUser\"));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class PrincipalController {\n\n\t\t@GetMapping(\"/\")\n\t\tString principal(@AuthenticationPrincipal String principal) {\n\t\t\treturn principal;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.ObservationTextPublisher;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.AuthorizationObservationContext;\nimport org.springframework.security.authorization.SingleResultAuthorizationManager;\nimport org.springframework.security.authorization.SpringAuthorizationEventPublisher;\nimport org.springframework.security.authorization.event.AuthorizationDeniedEvent;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.core.GrantedAuthorityDefaults;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.stereotype.Component;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.DispatcherServlet;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link AuthorizeHttpRequestsConfigurer}.\n *\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class AuthorizeHttpRequestsConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenAuthorizedHttpRequestsAndNoRequestsThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NoRequestsConfig.class).autowire())\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())\");\n\t}\n\n\t@Test\n\tpublic void configureNoParameterWhenAuthorizedHttpRequestsAndNoRequestsThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NoRequestsNoParameterConfig.class).autowire())\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())\");\n\t}\n\n\t@Test\n\tpublic void configureWhenAnyRequestIncompleteMappingThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(IncompleteMappingConfig.class).autowire())\n\t\t\t.withMessageContaining(\"An incomplete mapping was found for \");\n\t}\n\n\t@Test\n\tpublic void configureNoParameterWhenAnyRequestIncompleteMappingThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(IncompleteMappingNoParameterConfig.class).autowire())\n\t\t\t.withMessageContaining(\"An incomplete mapping was found for \");\n\t}\n\n\t@Test\n\tpublic void configureWhenMvcMatcherAfterAnyRequestThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(AfterAnyRequestConfig.class).autowire())\n\t\t\t.withMessageContaining(\"Can't configure requestMatchers after anyRequest\");\n\t}\n\n\t@Test\n\tpublic void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {\n\t\tCustomAuthorizationManagerConfig.authorizationManager = mock(AuthorizationManager.class);\n\t\tthis.spring.register(CustomAuthorizationManagerConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t\tverify(CustomAuthorizationManagerConfig.authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void configureNoParameterMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {\n\t\tCustomAuthorizationManagerNoParameterConfig.authorizationManager = mock(AuthorizationManager.class);\n\t\tthis.spring.register(CustomAuthorizationManagerNoParameterConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t\tverify(CustomAuthorizationManagerNoParameterConfig.authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void configureMvcMatcherAccessAuthorizationManagerWhenNullThenException() {\n\t\tCustomAuthorizationManagerConfig.authorizationManager = null;\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(CustomAuthorizationManagerConfig.class).autowire())\n\t\t\t.withMessageContaining(\"manager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void configureWhenCustomAuthorizationManagerFactoryRegisteredThenUsed() {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class).autowire();\n\t\tverify(authorizationManagerFactory).permitAll();\n\t\tverify(authorizationManagerFactory).denyAll();\n\t\tverify(authorizationManagerFactory).hasRole(\"ADMIN\");\n\t\tverify(authorizationManagerFactory).hasAllRoles(\"hasAllRoles1\", \"hasAllRoles2\");\n\t\tverify(authorizationManagerFactory).hasAnyRole(\"USER\", \"ADMIN\");\n\t\tverify(authorizationManagerFactory).hasAuthority(\"write\");\n\t\tverify(authorizationManagerFactory).hasAnyAuthority(\"resource.read\", \"read\");\n\t\tverify(authorizationManagerFactory).hasAllAuthorities(\"hasAllAuthorities1\", \"hasAllAuthorities2\");\n\t\tverify(authorizationManagerFactory).authenticated();\n\t\tverify(authorizationManagerFactory).fullyAuthenticated();\n\t\tverify(authorizationManagerFactory).rememberMe();\n\t\tverify(authorizationManagerFactory).anonymous();\n\t\tverifyNoMoreInteractions(authorizationManagerFactory);\n\t}\n\n\t@Test\n\tpublic void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext()\n\t\t\t.getBean(ObjectPostProcessorConfig.class).objectPostProcessor;\n\t\tverify(objectPostProcessor).postProcess(any(RequestMatcherDelegatingAuthorizationManager.class));\n\t\tverify(objectPostProcessor).postProcess(any(AuthorizationManager.class));\n\t\tverify(objectPostProcessor).postProcess(any(AuthorizationFilter.class));\n\t}\n\n\t@Test\n\tpublic void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"ROLE_ADMIN\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenHasAnyAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() throws Exception {\n\t\tthis.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"ROLE_ADMIN\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenHasAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() throws Exception {\n\t\tthis.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleAdminThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"ROLE_ADMIN\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleOtherThenRespondsWithForbidden()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithOther = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"ROLE_OTHER\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithOther).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenAuthorityRoleUserOrAdminAuthRequiredAndNoUserThenRespondsWithUnauthorized() throws Exception {\n\t\tthis.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenHasRoleUserConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RoleUserConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenHasRoleUserConfiguredAndRoleIsAdminThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(RoleUserConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"ADMIN\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenHasRoleUserAndRoleHierarchyConfiguredThenGreaterRoleTakesPrecedence() throws Exception {\n\t\tthis.spring.register(RoleHierarchyUserConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"ADMIN\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenRoleUserOrAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"ADMIN\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenRoleUserOrAdminConfiguredAndRoleIsOtherThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithRoleOther = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"OTHER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenDenyAllConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {\n\t\tthis.spring.register(DenyAllConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenDenyAllConfiguredAndUserLoggedInThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(DenyAllConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenPermitAllConfiguredAndNoUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(PermitAllConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenPermitAllConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(PermitAllConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void authorizeHttpRequestsWhenInvokedTwiceThenUsesOriginalConfiguration() throws Exception {\n\t\tthis.spring.register(InvokeTwiceDoesNotResetConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(post(\"/\").with(csrf())).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenServletPathRoleAdminConfiguredAndRoleIsUserThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(ServletPathConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/spring/\")\n\t\t\t\t.servletPath(\"/spring\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenServletPathRoleAdminConfiguredAndRoleIsUserAndWithoutServletPathThenRespondsWithForbidden()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ServletPathConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenServletPathRoleAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(ServletPathConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/spring/\")\n\t\t\t\t.servletPath(\"/spring\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"ADMIN\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenAnyRequestAuthenticatedConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationEventPublisherThenUses() throws Exception {\n\t\tthis.spring.register(AuthenticatedConfig.class, AuthorizationEventPublisherConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t\tAuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);\n\t\tverify(publisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class),\n\t\t\t\tany(AuthorizationDecision.class));\n\t}\n\n\t@Test\n\tpublic void getWhenAnyRequestAuthenticatedConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenExpressionHasRoleUserConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(ExpressionRoleUserConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenExpressionHasRoleUserConfiguredAndRoleIsAdminThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(ExpressionRoleUserConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"ADMIN\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenExpressionRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(ExpressionRoleUserOrAdminConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"USER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenExpressionRoleUserOrAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(ExpressionRoleUserOrAdminConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"ADMIN\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenExpressionRoleUserOrAdminConfiguredAndRoleIsOtherThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(ExpressionRoleUserOrAdminConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithRoleOther = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t.roles(\"OTHER\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenCustomRolePrefixAndRoleHasDifferentPrefixThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(GrantedAuthorityDefaultHasRoleConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t\t\t.authorities(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenCustomRolePrefixAndHasRoleThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(GrantedAuthorityDefaultHasRoleConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t\t\t.authorities(new SimpleGrantedAuthority(\"CUSTOM_PREFIX_USER\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenCustomRolePrefixAndHasAnyRoleThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(GrantedAuthorityDefaultHasAnyRoleConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t\t\t.authorities(new SimpleGrantedAuthority(\"CUSTOM_PREFIX_USER\")));\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/\")\n\t\t\t\t.with(user(\"user\")\n\t\t\t\t\t\t.authorities(new SimpleGrantedAuthority(\"CUSTOM_PREFIX_ADMIN\")));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndPermitAllThenRespondsWithOk() throws Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> permitAll = spy(SingleResultAuthorizationManager.permitAll());\n\t\tgiven(authorizationManagerFactory.permitAll()).willReturn(permitAll);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/public\").with(anonymous());\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(permitAll).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(permitAll);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndDenyAllThenRespondsWithForbidden()\n\t\t\tthrows Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> denyAll = spy(SingleResultAuthorizationManager.denyAll());\n\t\tgiven(authorizationManagerFactory.denyAll()).willReturn(denyAll);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/private\").with(user(\"user\"));\n\t\tthis.mvc.perform(request).andExpect(status().isForbidden());\n\t\tverify(denyAll).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(denyAll);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndHasRoleThenRespondsWithOk() throws Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> hasRole = spy(AuthorityAuthorizationManager.hasRole(\"ADMIN\"));\n\t\tgiven(authorizationManagerFactory.hasRole(anyString())).willReturn(hasRole);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/admin\").with(user(\"admin\").roles(\"ADMIN\"));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(hasRole).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(hasRole);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndHasAnyRoleThenRespondsWithOk() throws Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> hasAnyRole = spy(\n\t\t\t\tAuthorityAuthorizationManager.hasAnyRole(\"USER\", \"ADMIN\"));\n\t\tgiven(authorizationManagerFactory.hasAnyRole(any(String[].class))).willReturn(hasAnyRole);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/user\").with(user(\"user\").roles(\"USER\"));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(hasAnyRole).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(hasAnyRole);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void postWhenCustomAuthorizationManagerFactoryRegisteredAndHasAuthorityThenRespondsWithOk()\n\t\t\tthrows Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> hasAuthority = spy(\n\t\t\t\tAuthorityAuthorizationManager.hasAuthority(\"write\"));\n\t\tgiven(authorizationManagerFactory.hasAuthority(anyString())).willReturn(hasAuthority);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = post(\"/resource\")\n\t\t\t.with(user(\"user\").authorities(new SimpleGrantedAuthority(\"write\")))\n\t\t\t.with(csrf());\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(hasAuthority).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(hasAuthority);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndHasAnyAuthorityThenRespondsWithOk()\n\t\t\tthrows Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> hasAnyAuthority = spy(\n\t\t\t\tAuthorityAuthorizationManager.hasAnyAuthority(\"resource.read\", \"read\"));\n\t\tgiven(authorizationManagerFactory.hasAnyAuthority(any(String[].class))).willReturn(hasAnyAuthority);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/resource\")\n\t\t\t.with(user(\"user\").authorities(new SimpleGrantedAuthority(\"read\")));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(hasAnyAuthority).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(hasAnyAuthority);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndAuthenticatedThenRespondsWithOk()\n\t\t\tthrows Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> authenticated = spy(\n\t\t\t\tAuthenticatedAuthorizationManager.authenticated());\n\t\tgiven(authorizationManagerFactory.authenticated()).willReturn(authenticated);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/authenticated\").with(user(\"user\"));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(authenticated).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(authenticated);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndFullyAuthenticatedThenRespondsWithOk()\n\t\t\tthrows Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> fullyAuthenticated = spy(\n\t\t\t\tAuthenticatedAuthorizationManager.fullyAuthenticated());\n\t\tgiven(authorizationManagerFactory.fullyAuthenticated()).willReturn(fullyAuthenticated);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/fully-authenticated\").with(user(\"user\"));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(fullyAuthenticated).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(fullyAuthenticated);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndRememberMeThenRespondsWithOk() throws Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> rememberMe = spy(\n\t\t\t\tAuthenticatedAuthorizationManager.rememberMe());\n\t\tgiven(authorizationManagerFactory.rememberMe()).willReturn(rememberMe);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/remember-me\")\n\t\t\t.with(authentication(new RememberMeAuthenticationToken(\"test\", \"user\", Set.of())));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(rememberMe).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(rememberMe);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndAnonymousThenRespondsWithOk() throws Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\t\tAuthorizationManager<RequestAuthorizationContext> anonymous = spy(\n\t\t\t\tAuthenticatedAuthorizationManager.anonymous());\n\t\tgiven(authorizationManagerFactory.anonymous()).willReturn(anonymous);\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = authorizationManagerFactory;\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class, AccessTestController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/anonymous\").with(anonymous());\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(anonymous).authorize(any(), any(RequestAuthorizationContext.class));\n\t\tverifyNoMoreInteractions(anonymous);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthorizationManagerFactoryRegisteredAndAccessThenRespondsWithForbidden()\n\t\t\tthrows Exception {\n\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager = mock();\n\t\tAuthorizationManagerFactoryConfig.authorizationManagerFactory = mockAuthorizationManagerFactory(\n\t\t\t\tauthorizationManager);\n\n\t\tthis.spring.register(AuthorizationManagerFactoryConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/\").with(user(\"user\"));\n\t\tthis.mvc.perform(request).andExpect(status().isForbidden());\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenExpressionHasIpAddressLocalhostConfiguredIpAddressIsLocalhostThenRespondsWithOk()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ExpressionIpAddressLocalhostConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestFromLocalhost = get(\"/\")\n\t\t\t\t.with(remoteAddress(\"127.0.0.1\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestFromLocalhost).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenExpressionHasIpAddressLocalhostConfiguredIpAddressIsOtherThenRespondsWithForbidden()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ExpressionIpAddressLocalhostConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestFromOtherHost = get(\"/\")\n\t\t\t\t.with(remoteAddress(\"192.168.0.1\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestFromOtherHost).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void requestWhenMvcMatcherPathVariablesThenMatchesOnPathVariables() throws Exception {\n\t\tthis.spring.register(MvcMatcherPathVariablesInLambdaConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/user/user\");\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\trequest = get(\"/user/deny\");\n\t\tthis.mvc.perform(request).andExpect(status().isUnauthorized());\n\n\t\tUserDetails user = TestAuthentication.withUsername(\"taehong\").build();\n\t\tAuthentication authentication = TestAuthentication.authenticated(user);\n\t\trequest = get(\"/v2/user/{username}\", user.getUsername()).with(authentication(authentication));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\n\t\trequest = get(\"/v2/user/{username}\", \"withNoAuthentication\");\n\t\tthis.mvc.perform(request).andExpect(status().isUnauthorized());\n\n\t\trequest = get(\"/v2/user/{username}\", \"another\").with(authentication(authentication));\n\t\tthis.mvc.perform(request).andExpect(status().isForbidden());\n\t}\n\n\tprivate static RequestPostProcessor remoteAddress(String remoteAddress) {\n\t\treturn (request) -> {\n\t\t\trequest.setRemoteAddr(remoteAddress);\n\t\t\treturn request;\n\t\t};\n\t}\n\n\tprivate AuthorizationManagerFactory<RequestAuthorizationContext> mockAuthorizationManagerFactory(\n\t\t\tAuthorizationManager<RequestAuthorizationContext> authorizationManager) {\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory = mock();\n\t\tgiven(authorizationManagerFactory.permitAll()).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.denyAll()).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.hasRole(anyString())).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.hasAnyRole(any(String[].class))).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.hasAllRoles(any(String[].class))).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.hasAuthority(anyString())).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.hasAnyAuthority(any(String[].class))).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.hasAllAuthorities(any(String[].class))).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.authenticated()).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.fullyAuthenticated()).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.rememberMe()).willReturn(authorizationManager);\n\t\tgiven(authorizationManagerFactory.anonymous()).willReturn(authorizationManager);\n\n\t\treturn authorizationManagerFactory;\n\t}\n\n\t@Test\n\tpublic void getWhenFullyAuthenticatedConfiguredAndRememberMeTokenThenRespondsWithUnauthorized() throws Exception {\n\t\tthis.spring.register(FullyAuthenticatedConfig.class, BasicController.class).autowire();\n\t\tRememberMeAuthenticationToken rememberMe = new RememberMeAuthenticationToken(\"key\", \"user\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tMockHttpServletRequestBuilder requestWithRememberMe = get(\"/\").with(authentication(rememberMe));\n\t\tthis.mvc.perform(requestWithRememberMe).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenFullyAuthenticatedConfiguredAndUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(FullyAuthenticatedConfig.class, BasicController.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\").with(user(\"user\").roles(\"USER\"));\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenRememberMeConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenRememberMeConfiguredAndRememberMeTokenThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class, BasicController.class).autowire();\n\t\tRememberMeAuthenticationToken rememberMe = new RememberMeAuthenticationToken(\"key\", \"user\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tMockHttpServletRequestBuilder requestWithRememberMe = get(\"/\").with(authentication(rememberMe));\n\t\tthis.mvc.perform(requestWithRememberMe).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenAnonymousConfiguredAndAnonymousUserThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(AnonymousConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenAnonymousConfiguredAndLoggedInUserThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(AnonymousConfig.class, BasicController.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\").with(user(\"user\"));\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenNotConfigAndAuthenticatedThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(NotConfig.class, BasicController.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\").with(user(\"user\"));\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenNotConfigAndNotAuthenticatedThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(NotConfig.class, BasicController.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/\");\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void getWhenObservationRegistryThenObserves() throws Exception {\n\t\tthis.spring.register(RoleUserConfig.class, BasicController.class, ObservationRegistryConfig.class).autowire();\n\t\tObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tthis.mvc.perform(get(\"/\").with(user(\"user\").roles(\"USER\"))).andExpect(status().isOk());\n\t\tArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, atLeastOnce()).onStart(context.capture());\n\t\tassertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthorizationObservationContext);\n\t\tverify(handler, atLeastOnce()).onStop(context.capture());\n\t\tassertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthorizationObservationContext);\n\t\tthis.mvc.perform(get(\"/\").with(user(\"user\").roles(\"WRONG\"))).andExpect(status().isForbidden());\n\t\tverify(handler).onError(any());\n\t}\n\n\t@Test\n\tpublic void getWhenExcludeAuthorizationObservationsThenUnobserved() throws Exception {\n\t\tthis.spring\n\t\t\t.register(RoleUserConfig.class, BasicController.class, ObservationRegistryConfig.class,\n\t\t\t\t\tSelectableObservationsConfig.class)\n\t\t\t.autowire();\n\t\tObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tthis.mvc.perform(get(\"/\").with(user(\"user\").roles(\"USER\"))).andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/\").with(user(\"user\").roles(\"WRONG\"))).andExpect(status().isForbidden());\n\t\tverifyNoInteractions(handler);\n\t}\n\n\t@Test\n\tpublic void getWhenDeniedThenParameterizedAuthorizationDeniedEventIsPublished() throws Exception {\n\t\tthis.spring.register(DenyAllConfig.class, EventPublisherConfig.class, AuthorizationDeniedListener.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(get(\"/\").with(user(\"user\")));\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationDeniedListener.class).invocations).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenMultipleDispatcherServletsAndPathBeanThenAllows() throws Exception {\n\t\tthis.spring.register(PathPatternRequestMatcherBuilderConfig.class, BasicController.class)\n\t\t\t.postProcessor((context) -> context.getServletContext()\n\t\t\t\t.addServlet(\"otherDispatcherServlet\", DispatcherServlet.class)\n\t\t\t\t.addMapping(\"/mvc\"))\n\t\t\t.autowire();\n\t\tthis.mvc.perform(get(\"/mvc/path\").servletPath(\"/mvc\").with(user(\"user\"))).andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/mvc/path\").servletPath(\"/mvc\").with(user(\"user\").roles(\"DENIED\")))\n\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/path\").with(user(\"user\"))).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenFactoryBeanThenAuthorizes() throws Exception {\n\t\tthis.spring.register(PathPatternFactoryBeanConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/path/resource\")).andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/path/resource\").with(user(\"user\").roles(\"USER\"))).andExpect(status().isNotFound());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class GrantedAuthorityDefaultHasRoleConfig {\n\n\t\t@Bean\n\t\tGrantedAuthorityDefaults grantedAuthorityDefaults() {\n\t\t\treturn new GrantedAuthorityDefaults(\"CUSTOM_PREFIX_\");\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain myFilterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.authorizeHttpRequests((c) -> c.anyRequest().hasRole(\"USER\")).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class GrantedAuthorityDefaultHasAnyRoleConfig {\n\n\t\t@Bean\n\t\tGrantedAuthorityDefaults grantedAuthorityDefaults() {\n\t\t\treturn new GrantedAuthorityDefaults(\"CUSTOM_PREFIX_\");\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain myFilterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.authorizeHttpRequests((c) -> c.anyRequest().hasAnyRole(\"USER\", \"ADMIN\")).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NoRequestsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests(withDefaults())\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NoRequestsNoParameterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests(withDefaults());\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class IncompleteMappingConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests(AbstractRequestMatcherRegistry::anyRequest)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class IncompleteMappingNoParameterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest());\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AfterAnyRequestConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t\t.requestMatchers(\"/path\").hasRole(\"USER\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthorizationManagerConfig {\n\n\t\tstatic AuthorizationManager<RequestAuthorizationContext> authorizationManager;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().access(authorizationManager)\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthorizationManagerNoParameterConfig {\n\n\t\tstatic AuthorizationManager<RequestAuthorizationContext> authorizationManager;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().access(authorizationManager));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthorizationManagerFactoryConfig {\n\n\t\tstatic AuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory;\n\n\t\t@Bean\n\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authorizationManagerFactory() {\n\t\t\treturn authorizationManagerFactory;\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.requestMatchers(\"/public\").permitAll()\n\t\t\t\t\t.requestMatchers(\"/private\").denyAll()\n\t\t\t\t\t.requestMatchers(\"/admin\").hasRole(\"ADMIN\")\n\t\t\t\t\t.requestMatchers(\"/user\").hasAnyRole(\"USER\", \"ADMIN\")\n\t\t\t\t\t.requestMatchers(\"/hasAllRoles\").hasAllRoles(\"hasAllRoles1\", \"hasAllRoles2\")\n\t\t\t\t\t.requestMatchers(HttpMethod.POST, \"/resource\").hasAuthority(\"write\")\n\t\t\t\t\t.requestMatchers(\"/resource\").hasAnyAuthority(\"resource.read\", \"read\")\n\t\t\t\t\t.requestMatchers(\"/hasAllAuthorities\").hasAllAuthorities(\"hasAllAuthorities1\", \"hasAllAuthorities2\")\n\t\t\t\t\t.requestMatchers(\"/authenticated\").authenticated()\n\t\t\t\t\t.requestMatchers(\"/fully-authenticated\").fullyAuthenticated()\n\t\t\t\t\t.requestMatchers(\"/remember-me\").rememberMe()\n\t\t\t\t\t.requestMatchers(\"/anonymous\").anonymous()\n\t\t\t\t\t.anyRequest().access((authentication, context) -> new AuthorizationDecision(false))\n\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn this.objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RoleUserAnyAuthorityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasAnyAuthority(\"ROLE_USER\")\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RoleUserAuthorityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasAuthority(\"ROLE_USER\")\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RoleUserOrRoleAdminAuthorityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasAnyAuthority(\"ROLE_USER\", \"ROLE_ADMIN\")\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RoleUserConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RoleHierarchyUserConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tRoleHierarchy roleHierarchy() {\n\t\t\treturn RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > ROLE_USER\");\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RoleUserOrAdminConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasAnyRole(\"USER\", \"ADMIN\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DenyAllConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PermitAllConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().permitAll()\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvokeTwiceDoesNotResetConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.authorizeHttpRequests(withDefaults())\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class ServletPathConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\tPathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t\tbean.setBasePath(\"/spring\");\n\t\t\treturn bean;\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.requestMatchers(builder.matcher(\"/\")).hasRole(\"ADMIN\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthenticatedConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService users() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\t\tUser.withUsername(\"user\").password(\"{noop}password\").roles(\"USER\").build());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ExpressionRoleUserConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().access(new WebExpressionAuthorizationManager(\"hasRole('USER')\"))\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ExpressionRoleUserOrAdminConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().access(new WebExpressionAuthorizationManager(\"hasRole('USER') or hasRole('ADMIN')\"))\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ExpressionIpAddressLocalhostConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().access(new WebExpressionAuthorizationManager(\"hasIpAddress('127.0.0.1')\"))\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class MvcMatcherPathVariablesInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain chain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.requestMatchers(\"/user/{username}\").access(new WebExpressionAuthorizationManager(\"#username == 'user'\"))\n\t\t\t\t\t.requestMatchers(\"/v2/user/{username}\").hasVariable(\"username\").equalTo(Authentication::getName)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/user/{username}\")\n\t\t\tString path(@PathVariable(\"username\") String username) {\n\t\t\t\treturn username;\n\t\t\t}\n\n\t\t\t@RequestMapping(\"/v2/user/{username}\")\n\t\t\tString pathV2(@PathVariable(\"username\") String username) {\n\t\t\t\treturn username;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FullyAuthenticatedConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain chain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.rememberMe(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().fullyAuthenticated()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(TestAuthentication.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain chain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.rememberMe(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().rememberMe()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(TestAuthentication.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AnonymousConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain chain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().anonymous()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NotConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain chain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().not().authenticated()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class AuthorizationEventPublisherConfig {\n\n\t\tprivate final AuthorizationEventPublisher publisher = mock(AuthorizationEventPublisher.class);\n\n\t\t@Bean\n\t\tAuthorizationEventPublisher authorizationEventPublisher() {\n\t\t\treturn this.publisher;\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@GetMapping(\"/\")\n\t\tvoid rootGet() {\n\t\t}\n\n\t\t@PostMapping(\"/\")\n\t\tvoid rootPost() {\n\t\t}\n\n\t\t@GetMapping(\"/path\")\n\t\tvoid path() {\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class AccessTestController {\n\n\t\t@RequestMapping(\"/public\")\n\t\tvoid publicEndpoint() {\n\t\t}\n\n\t\t@RequestMapping(\"/private\")\n\t\tvoid privateEndpoint() {\n\t\t}\n\n\t\t@RequestMapping(\"/admin\")\n\t\tvoid adminEndpoint() {\n\t\t}\n\n\t\t@RequestMapping(\"/user\")\n\t\tvoid userEndpoint() {\n\t\t}\n\n\t\t@RequestMapping(\"/resource\")\n\t\tvoid resourceEndpoint() {\n\t\t}\n\n\t\t@RequestMapping(\"/authenticated\")\n\t\tvoid authenticatedEndpoint() {\n\t\t}\n\n\t\t@RequestMapping(\"/fully-authenticated\")\n\t\tvoid fullyAuthenticatedEndpoint() {\n\t\t}\n\n\t\t@RequestMapping(\"/remember-me\")\n\t\tvoid rememberMeEndpoint() {\n\t\t}\n\n\t\t@RequestMapping(\"/anonymous\")\n\t\tvoid anonymousEndpoint() {\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ObservationRegistryConfig {\n\n\t\tprivate final ObservationRegistry registry = ObservationRegistry.create();\n\n\t\tprivate final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\treturn this.registry;\n\t\t}\n\n\t\t@Bean\n\t\tObservationHandler<Observation.Context> observationHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t\t@Bean\n\t\tObservationRegistryPostProcessor observationRegistryPostProcessor(\n\t\t\t\tObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\treturn new ObservationRegistryPostProcessor(handler);\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryPostProcessor implements BeanPostProcessor {\n\n\t\tprivate final ObjectProvider<ObservationHandler<Observation.Context>> handler;\n\n\t\tObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\tthis.handler = handler;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tif (bean instanceof ObservationRegistry registry) {\n\t\t\t\tregistry.observationConfig().observationHandler(this.handler.getObject());\n\t\t\t}\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SelectableObservationsConfig {\n\n\t\t@Bean\n\t\tSecurityObservationSettings observabilityDefaults() {\n\t\t\treturn SecurityObservationSettings.withDefaults().shouldObserveAuthorizations(false).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class PathPatternRequestMatcherBuilderConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain security(HttpSecurity http) throws Exception {\n\t\t\tPathPatternRequestMatcher.Builder mvc = PathPatternRequestMatcher.withDefaults().basePath(\"/mvc\");\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.requestMatchers(mvc.matcher(\"/path/**\")).hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class PathPatternFactoryBeanConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain security(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.requestMatchers(\"/path/**\").hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class EventPublisherConfig {\n\n\t\t@Bean\n\t\tstatic AuthorizationEventPublisher eventPublisher(ApplicationEventPublisher publisher) {\n\t\t\treturn new SpringAuthorizationEventPublisher(publisher);\n\t\t}\n\n\t}\n\n\t@Component\n\tstatic class AuthorizationDeniedListener {\n\n\t\tint invocations;\n\n\t\t@EventListener\n\t\tvoid onRequestDenied(AuthorizationDeniedEvent<? extends HttpServletRequest> denied) {\n\t\t\tthis.invocations++;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;\nimport org.springframework.security.web.access.channel.ChannelProcessingFilter;\nimport org.springframework.security.web.access.channel.InsecureChannelProcessor;\nimport org.springframework.security.web.access.channel.SecureChannelProcessor;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\n\n/**\n * Tests for {@link ChannelSecurityConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n * @author Onur Kagan Ozcan\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class ChannelSecurityConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnInsecureChannelProcessor() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(InsecureChannelProcessor.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnSecureChannelProcessor() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(SecureChannelProcessor.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnChannelDecisionManagerImpl() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(ChannelDecisionManagerImpl.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnChannelProcessingFilter() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(ChannelProcessingFilter.class));\n\t}\n\n\t@Test\n\tpublic void requiresChannelWhenInvokesTwiceThenUsesOriginalRequiresSecure() throws Exception {\n\t\tthis.spring.register(DuplicateInvocationsDoesNotOverrideConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(redirectedUrl(\"https://localhost/\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenRequiresChannelConfiguredInLambdaThenRedirectsToHttps() throws Exception {\n\t\tthis.spring.register(RequiresChannelInLambdaConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(redirectedUrl(\"https://localhost/\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenRequiresChannelConfiguredWithUrlRedirectThenRedirectsToUrlWithHttps() throws Exception {\n\t\tthis.spring.register(RequiresChannelWithTestUrlRedirectStrategy.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(redirectedUrl(\"https://localhost/test\"));\n\t}\n\n\t// gh-10956\n\t@Test\n\tpublic void requestWhenRequiresChannelWithMultiMvcMatchersThenRedirectsToHttps() throws Exception {\n\t\tthis.spring.register(RequiresChannelMultiMvcMatchersConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/test-1\")).andExpect(redirectedUrl(\"https://localhost/test-1\"));\n\t\tthis.mvc.perform(get(\"/test-2\")).andExpect(redirectedUrl(\"https://localhost/test-2\"));\n\t\tthis.mvc.perform(get(\"/test-3\")).andExpect(redirectedUrl(\"https://localhost/test-3\"));\n\t}\n\n\t// gh-10956\n\t@Test\n\tpublic void requestWhenRequiresChannelWithMultiMvcMatchersInLambdaThenRedirectsToHttps() throws Exception {\n\t\tthis.spring.register(RequiresChannelMultiMvcMatchersInLambdaConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/test-1\")).andExpect(redirectedUrl(\"https://localhost/test-1\"));\n\t\tthis.mvc.perform(get(\"/test-2\")).andExpect(redirectedUrl(\"https://localhost/test-2\"));\n\t\tthis.mvc.perform(get(\"/test-3\")).andExpect(redirectedUrl(\"https://localhost/test-3\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requiresChannel((channel) -> channel\n\t\t\t\t\t.anyRequest().requiresSecure());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DuplicateInvocationsDoesNotOverrideConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requiresChannel((channel) -> channel\n\t\t\t\t\t.anyRequest().requiresSecure())\n\t\t\t\t.requiresChannel(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequiresChannelInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requiresChannel((requiresChannel) -> requiresChannel\n\t\t\t\t\t\t.anyRequest().requiresSecure()\n\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequiresChannelWithTestUrlRedirectStrategy {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.portMapper((mapper) -> mapper\n\t\t\t\t\t.portMapper(new PortMapperImpl()))\n\t\t\t\t.requiresChannel((channel) -> channel\n\t\t\t\t\t.redirectStrategy(new TestUrlRedirectStrategy())\n\t\t\t\t\t.anyRequest()\n\t\t\t\t\t.requiresSecure());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tstatic class TestUrlRedirectStrategy implements RedirectStrategy {\n\n\t\t@Override\n\t\tpublic void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)\n\t\t\t\tthrows IOException {\n\t\t\tString redirectUrl = url + \"test\";\n\t\t\tredirectUrl = response.encodeRedirectURL(redirectUrl);\n\t\t\tresponse.sendRedirect(redirectUrl);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class RequiresChannelMultiMvcMatchersConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.portMapper((mapper) -> mapper\n\t\t\t\t\t.portMapper(new PortMapperImpl()))\n\t\t\t\t.requiresChannel((channel) -> channel\n\t\t\t\t\t.requestMatchers(\"/test-1\")\n\t\t\t\t\t.requiresSecure()\n\t\t\t\t\t.requestMatchers(\"/test-2\")\n\t\t\t\t\t.requiresSecure()\n\t\t\t\t\t.requestMatchers(\"/test-3\")\n\t\t\t\t\t.requiresSecure()\n\t\t\t\t\t.anyRequest()\n\t\t\t\t\t.requiresInsecure());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class RequiresChannelMultiMvcMatchersInLambdaConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.portMapper((port) -> port\n\t\t\t\t\t.portMapper(new PortMapperImpl())\n\t\t\t\t)\n\t\t\t\t.requiresChannel((channel) -> channel\n\t\t\t\t\t.requestMatchers(\"/test-1\")\n\t\t\t\t\t\t.requiresSecure()\n\t\t\t\t\t.requestMatchers(\"/test-2\")\n\t\t\t\t\t\t.requiresSecure()\n\t\t\t\t\t.requestMatchers(\"/test-3\")\n\t\t\t\t\t\t.requiresSecure()\n\t\t\t\t\t.anyRequest()\n\t\t\t\t\t\t.requiresInsecure()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/CorsConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport com.google.common.net.HttpHeaders;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.CrossOrigin;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.CorsConfigurationSource;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link CorsConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class CorsConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenNoMvcThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(DefaultCorsConfig.class).autowire())\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"Please ensure that you are using `@EnableWebMvc`, are publishing a `WebMvcConfigurer`, \"\n\t\t\t\t\t\t\t+ \"or are publishing a `CorsConfigurationSource` bean.\");\n\t}\n\n\t@Test\n\tpublic void getWhenCrossOriginAnnotationThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(MvcCorsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void optionsWhenCrossOriginAnnotationThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(MvcCorsConfig.class).autowire();\n\t\tthis.mvc\n\t\t\t.perform(options(\"/\")\n\t\t\t\t.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())\n\t\t\t\t.header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultsInLambdaAndCrossOriginAnnotationThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(MvcCorsInLambdaConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void optionsWhenDefaultsInLambdaAndCrossOriginAnnotationThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(MvcCorsInLambdaConfig.class).autowire();\n\t\tthis.mvc\n\t\t\t.perform(options(\"/\")\n\t\t\t\t.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())\n\t\t\t\t.header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void getWhenCorsConfigurationSourceBeanThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(ConfigSourceConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void optionsWhenCorsConfigurationSourceBeanThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(ConfigSourceConfig.class).autowire();\n\t\tthis.mvc\n\t\t\t.perform(options(\"/\")\n\t\t\t\t.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())\n\t\t\t\t.header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void getWhenMvcCorsInLambdaConfigAndCorsConfigurationSourceBeanThenRespondsWithCorsHeaders()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ConfigSourceInLambdaConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void optionsWhenMvcCorsInLambdaConfigAndCorsConfigurationSourceBeanThenRespondsWithCorsHeaders()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ConfigSourceInLambdaConfig.class).autowire();\n\t\tthis.mvc\n\t\t\t.perform(options(\"/\")\n\t\t\t\t.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())\n\t\t\t\t.header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void getWhenCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(CorsFilterConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void optionsWhenCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(CorsFilterConfig.class).autowire();\n\t\tthis.mvc\n\t\t\t.perform(options(\"/\")\n\t\t\t\t.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())\n\t\t\t\t.header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void getWhenConfigSourceInLambdaConfigAndCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(CorsFilterInLambdaConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void optionsWhenConfigSourceInLambdaConfigAndCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {\n\t\tthis.spring.register(CorsFilterInLambdaConfig.class).autowire();\n\t\tthis.mvc\n\t\t\t.perform(options(\"/\")\n\t\t\t\t.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())\n\t\t\t\t.header(HttpHeaders.ORIGIN, \"https://example.com\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().exists(\"Access-Control-Allow-Origin\"))\n\t\t\t.andExpect(header().exists(\"X-Content-Type-Options\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultCorsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.cors(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class MvcCorsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.cors(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@RestController\n\t\t@CrossOrigin(methods = { RequestMethod.GET, RequestMethod.POST })\n\t\tstatic class CorsController {\n\n\t\t\t@RequestMapping(\"/\")\n\t\t\tString hello() {\n\t\t\t\treturn \"Hello\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class MvcCorsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.cors(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@RestController\n\t\t@CrossOrigin(methods = { RequestMethod.GET, RequestMethod.POST })\n\t\tstatic class CorsController {\n\n\t\t\t@RequestMapping(\"/\")\n\t\t\tString hello() {\n\t\t\t\treturn \"Hello\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConfigSourceConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.cors(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tCorsConfigurationSource corsConfigurationSource() {\n\t\t\tUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n\t\t\tCorsConfiguration corsConfiguration = new CorsConfiguration();\n\t\t\tcorsConfiguration.setAllowedOrigins(Collections.singletonList(\"*\"));\n\t\t\tcorsConfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));\n\t\t\tsource.registerCorsConfiguration(\"/**\", corsConfiguration);\n\t\t\treturn source;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConfigSourceInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.cors(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tCorsConfigurationSource corsConfigurationSource() {\n\t\t\tUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n\t\t\tCorsConfiguration corsConfiguration = new CorsConfiguration();\n\t\t\tcorsConfiguration.setAllowedOrigins(Collections.singletonList(\"*\"));\n\t\t\tcorsConfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));\n\t\t\tsource.registerCorsConfiguration(\"/**\", corsConfiguration);\n\t\t\treturn source;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CorsFilterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.cors(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tCorsFilter corsFilter() {\n\t\t\tUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n\t\t\tCorsConfiguration corsConfiguration = new CorsConfiguration();\n\t\t\tcorsConfiguration.setAllowedOrigins(Collections.singletonList(\"*\"));\n\t\t\tcorsConfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));\n\t\t\tsource.registerCorsConfiguration(\"/**\", corsConfiguration);\n\t\t\treturn new CorsFilter(source);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CorsFilterInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.cors(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tCorsFilter corsFilter() {\n\t\t\tUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n\t\t\tCorsConfiguration corsConfiguration = new CorsConfiguration();\n\t\t\tcorsConfiguration.setAllowedOrigins(Collections.singletonList(\"*\"));\n\t\t\tcorsConfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));\n\t\t\tsource.registerCorsConfiguration(\"/**\", corsConfiguration);\n\t\t\treturn new CorsFilter(source);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerIgnoringRequestMatchersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class CsrfConfigurerIgnoringRequestMatchersTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void requestWhenIgnoringRequestMatchersThenAugmentedByConfiguredRequestMatcher() throws Exception {\n\t\tthis.spring.register(IgnoringRequestMatchers.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/path\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(post(\"/path\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requestWhenIgnoringRequestMatchersInLambdaThenAugmentedByConfiguredRequestMatcher() throws Exception {\n\t\tthis.spring.register(IgnoringRequestInLambdaMatchers.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/path\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(post(\"/path\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requestWhenIgnoringRequestMatcherThenUnionsWithConfiguredIgnoringAntMatchers() throws Exception {\n\t\tthis.spring.register(IgnoringPathsAndMatchers.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(put(\"/csrf\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(post(\"/csrf\")).andExpect(status().isOk());\n\t\tthis.mvc.perform(put(\"/no-csrf\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requestWhenIgnoringRequestMatcherInLambdaThenUnionsWithConfiguredIgnoringAntMatchers()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(IgnoringPathsAndMatchersInLambdaConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(put(\"/csrf\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(post(\"/csrf\")).andExpect(status().isOk());\n\t\tthis.mvc.perform(put(\"/no-csrf\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requestWhenIgnoringRequestMatcherPatternThenIgnores() throws Exception {\n\t\tthis.spring.register(IgnoringPathsAndMatchersPatternConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(put(\"/csrf\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(post(\"/csrf\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(put(\"/no-csrf\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requestWhenIgnoringRequestMatcherPatternInLambdaThenIgnores() throws Exception {\n\t\tthis.spring.register(IgnoringPathsAndMatchersPatternInLambdaConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(put(\"/csrf\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(post(\"/csrf\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(put(\"/no-csrf\")).andExpect(status().isOk());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class IgnoringRequestMatchers {\n\n\t\tRequestMatcher requestMatcher = (request) -> HttpMethod.POST.name().equals(request.getMethod());\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.requireCsrfProtectionMatcher(pathPattern(\"/path\"))\n\t\t\t\t\t.ignoringRequestMatchers(this.requestMatcher));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class IgnoringRequestInLambdaMatchers {\n\n\t\tRequestMatcher requestMatcher = (request) -> HttpMethod.POST.name().equals(request.getMethod());\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t\t.requireCsrfProtectionMatcher(pathPattern(\"/path\"))\n\t\t\t\t\t\t.ignoringRequestMatchers(this.requestMatcher)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class IgnoringPathsAndMatchers {\n\n\t\tRequestMatcher requestMatcher = (request) -> HttpMethod.POST.name().equals(request.getMethod());\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.ignoringRequestMatchers(pathPattern(\"/no-csrf\"))\n\t\t\t\t\t.ignoringRequestMatchers(this.requestMatcher));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class IgnoringPathsAndMatchersInLambdaConfig {\n\n\t\tRequestMatcher requestMatcher = (request) -> HttpMethod.POST.name().equals(request.getMethod());\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t\t.ignoringRequestMatchers(pathPattern(\"/no-csrf\"))\n\t\t\t\t\t\t.ignoringRequestMatchers(this.requestMatcher)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class IgnoringPathsAndMatchersPatternConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.ignoringRequestMatchers(\"/no-csrf\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class IgnoringPathsAndMatchersPatternInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.ignoringRequestMatchers(\"/no-csrf\")\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@RestController\n\tpublic static class BasicController {\n\n\t\t@RequestMapping(\"/path\")\n\t\tpublic String path() {\n\t\t\treturn \"path\";\n\t\t}\n\n\t\t@RequestMapping(\"/csrf\")\n\t\tpublic String csrf() {\n\t\t\treturn \"csrf\";\n\t\t}\n\n\t\t@RequestMapping(\"/no-csrf\")\n\t\tpublic String noCsrf() {\n\t\t\treturn \"no-csrf\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerNoWebMvcTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;\nimport org.springframework.web.servlet.support.RequestDataValueProcessor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n *\n */\npublic class CsrfConfigurerNoWebMvcTests {\n\n\tConfigurableApplicationContext context;\n\n\t@AfterEach\n\tpublic void teardown() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void missingDispatcherServletPreventsCsrfRequestDataValueProcessor() {\n\t\tloadContext(EnableWebConfig.class);\n\t\tassertThat(this.context.containsBeanDefinition(\"requestDataValueProcessor\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void findDispatcherServletPreventsCsrfRequestDataValueProcessor() {\n\t\tloadContext(EnableWebMvcConfig.class);\n\t\tassertThat(this.context.containsBeanDefinition(\"requestDataValueProcessor\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void overrideCsrfRequestDataValueProcessor() {\n\t\tloadContext(EnableWebOverrideRequestDataConfig.class);\n\t\tassertThat(this.context.getBean(RequestDataValueProcessor.class).getClass())\n\t\t\t.isNotEqualTo(CsrfRequestDataValueProcessor.class);\n\t}\n\n\tprivate void loadContext(Class<?> configs) {\n\t\tAnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();\n\t\tannotationConfigApplicationContext.register(configs);\n\t\tannotationConfigApplicationContext.refresh();\n\t\tthis.context = annotationConfigApplicationContext;\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EnableWebConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EnableWebOverrideRequestDataConfig {\n\n\t\t@Bean\n\t\t@Primary\n\t\tRequestDataValueProcessor requestDataValueProcessor() {\n\t\t\treturn mock(RequestDataValueProcessor.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EnableWebMvcConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.net.URI;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.csrf.CookieCsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\nimport org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;\nimport org.springframework.security.web.firewall.StrictHttpFirewall;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.support.RequestDataValueProcessor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.hamcrest.Matchers.not;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.isNull;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.head;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link CsrfConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n * @author Michael Vitz\n * @author Sam Simmons\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class CsrfConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void postWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(post(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void putWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(put(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void patchWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(patch(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void deleteWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(delete(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void invalidWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(request(\"INVALID\", URI.create(\"/\"))).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void getWhenWebSecurityEnabledThenRespondsWithOk() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void headWhenWebSecurityEnabledThenRespondsWithOk() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(head(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void traceWhenWebSecurityEnabledThenRespondsWithOk() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(request(HttpMethod.TRACE, \"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void optionsWhenWebSecurityEnabledThenRespondsWithOk() throws Exception {\n\t\tthis.spring\n\t\t\t.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(options(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void enableWebSecurityWhenDefaultConfigurationThenCreatesRequestDataValueProcessor() {\n\t\tthis.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(RequestDataValueProcessor.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void postWhenCsrfDisabledThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(DisableCsrfConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(post(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void postWhenCsrfDisabledInLambdaThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(DisableCsrfInLambdaConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(post(\"/\")).andExpect(status().isOk());\n\t}\n\n\t// SEC-2498\n\t@Test\n\tpublic void loginWhenCsrfDisabledThenRedirectsToPreviousPostRequest() throws Exception {\n\t\tthis.spring.register(DisableCsrfEnablesRequestCacheConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(post(\"/to-save\")).andReturn();\n\t\tRequestCache requestCache = new HttpSessionRequestCache();\n\t\tString redirectUrl = requestCache.getRequest(mvcResult.getRequest(), mvcResult.getResponse()).getRedirectUrl();\n\t\tthis.mvc\n\t\t\t.perform(post(\"/login\").param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session((MockHttpSession) mvcResult.getRequest().getSession()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(redirectUrl));\n\t}\n\n\t@Test\n\tpublic void loginWhenCsrfEnabledThenDoesNotRedirectToPreviousPostRequest() throws Exception {\n\t\tCsrfDisablesPostRequestFromRequestCacheConfig.REPO = mock(CsrfTokenRepository.class);\n\t\tDefaultCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tgiven(CsrfDisablesPostRequestFromRequestCacheConfig.REPO.loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(csrfToken));\n\t\tthis.spring.register(CsrfDisablesPostRequestFromRequestCacheConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(post(\"/some-url\")).andReturn();\n\t\tthis.mvc\n\t\t\t.perform(post(\"/login\").param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.session((MockHttpSession) mvcResult.getRequest().getSession()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\tverify(CsrfDisablesPostRequestFromRequestCacheConfig.REPO, atLeastOnce())\n\t\t\t.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void loginWhenCsrfEnabledThenRedirectsToPreviousGetRequest() throws Exception {\n\t\tCsrfDisablesPostRequestFromRequestCacheConfig.REPO = mock(CsrfTokenRepository.class);\n\t\tDefaultCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tgiven(CsrfDisablesPostRequestFromRequestCacheConfig.REPO.loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(csrfToken));\n\t\tthis.spring.register(CsrfDisablesPostRequestFromRequestCacheConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/some-url\")).andReturn();\n\t\tRequestCache requestCache = new HttpSessionRequestCache();\n\t\tString redirectUrl = requestCache.getRequest(mvcResult.getRequest(), mvcResult.getResponse()).getRedirectUrl();\n\t\tthis.mvc\n\t\t\t.perform(post(\"/login\").param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.session((MockHttpSession) mvcResult.getRequest().getSession()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(redirectUrl));\n\t\tverify(CsrfDisablesPostRequestFromRequestCacheConfig.REPO, atLeastOnce())\n\t\t\t.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t// SEC-2422\n\t@Test\n\tpublic void postWhenCsrfEnabledAndSessionIsExpiredThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(InvalidSessionUrlConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(post(\"/\").param(\"_csrf\", \"abc\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/error/sessionError\"))\n\t\t\t.andReturn();\n\t\tthis.mvc.perform(post(\"/\").session((MockHttpSession) mvcResult.getRequest().getSession()))\n\t\t\t.andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void requireCsrfProtectionMatcherWhenRequestDoesNotMatchThenRespondsWithOk() throws Exception {\n\t\tthis.spring.register(RequireCsrfProtectionMatcherConfig.class, BasicController.class).autowire();\n\t\tgiven(RequireCsrfProtectionMatcherConfig.MATCHER.matches(any())).willReturn(false);\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requireCsrfProtectionMatcherWhenRequestMatchesThenRespondsWithForbidden() throws Exception {\n\t\tRequireCsrfProtectionMatcherConfig.MATCHER = mock(RequestMatcher.class);\n\t\tgiven(RequireCsrfProtectionMatcherConfig.MATCHER.matches(any())).willReturn(true);\n\t\tthis.spring.register(RequireCsrfProtectionMatcherConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void requireCsrfProtectionMatcherInLambdaWhenRequestDoesNotMatchThenRespondsWithOk() throws Exception {\n\t\tRequireCsrfProtectionMatcherInLambdaConfig.MATCHER = mock(RequestMatcher.class);\n\t\tthis.spring.register(RequireCsrfProtectionMatcherInLambdaConfig.class, BasicController.class).autowire();\n\t\tgiven(RequireCsrfProtectionMatcherInLambdaConfig.MATCHER.matches(any())).willReturn(false);\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requireCsrfProtectionMatcherInLambdaWhenRequestMatchesThenRespondsWithForbidden() throws Exception {\n\t\tRequireCsrfProtectionMatcherInLambdaConfig.MATCHER = mock(RequestMatcher.class);\n\t\tgiven(RequireCsrfProtectionMatcherInLambdaConfig.MATCHER.matches(any())).willReturn(true);\n\t\tthis.spring.register(RequireCsrfProtectionMatcherInLambdaConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void postWhenCustomCsrfTokenRepositoryThenRepositoryIsUsed() throws Exception {\n\t\tCsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);\n\t\tgiven(CsrfTokenRepositoryConfig.REPO.loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\")));\n\t\tthis.spring.register(CsrfTokenRepositoryConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(post(\"/\"));\n\t\tverify(CsrfTokenRepositoryConfig.REPO).loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomCsrfTokenRepositoryThenCsrfTokenIsCleared() throws Exception {\n\t\tCsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);\n\t\tthis.spring.register(CsrfTokenRepositoryConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf()).with(user(\"user\")));\n\t\tverify(CsrfTokenRepositoryConfig.REPO).saveToken(isNull(), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void loginWhenCustomCsrfTokenRepositoryThenCsrfTokenIsCleared() throws Exception {\n\t\tCsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);\n\t\tDefaultCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tgiven(CsrfTokenRepositoryConfig.REPO.loadToken(any())).willReturn(csrfToken);\n\t\tgiven(CsrfTokenRepositoryConfig.REPO.loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(csrfToken));\n\t\tthis.spring.register(CsrfTokenRepositoryConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(loginRequest).andExpect(redirectedUrl(\"/\"));\n\t\tverify(CsrfTokenRepositoryConfig.REPO).loadToken(any(HttpServletRequest.class));\n\t\tverify(CsrfTokenRepositoryConfig.REPO).saveToken(isNull(), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void getWhenCustomCsrfTokenRepositoryInLambdaThenRepositoryIsUsed() throws Exception {\n\t\tCsrfTokenRepositoryInLambdaConfig.REPO = mock(CsrfTokenRepository.class);\n\t\tgiven(CsrfTokenRepositoryInLambdaConfig.REPO.loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\")));\n\t\tthis.spring.register(CsrfTokenRepositoryInLambdaConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(post(\"/\"));\n\t\tverify(CsrfTokenRepositoryInLambdaConfig.REPO).loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAccessDeniedHandlerThenHandlerIsUsed() throws Exception {\n\t\tAccessDeniedHandlerConfig.DENIED_HANDLER = mock(AccessDeniedHandler.class);\n\t\tthis.spring.register(AccessDeniedHandlerConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(post(\"/\")).andExpect(status().isOk());\n\t\tverify(AccessDeniedHandlerConfig.DENIED_HANDLER).handle(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any());\n\t}\n\n\t@Test\n\tpublic void getWhenCustomDefaultAccessDeniedHandlerForThenHandlerIsUsed() throws Exception {\n\t\tDefaultAccessDeniedHandlerForConfig.DENIED_HANDLER = mock(AccessDeniedHandler.class);\n\t\tDefaultAccessDeniedHandlerForConfig.MATCHER = mock(RequestMatcher.class);\n\t\tgiven(DefaultAccessDeniedHandlerForConfig.MATCHER.matches(any())).willReturn(true);\n\t\tthis.spring.register(DefaultAccessDeniedHandlerForConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(post(\"/\")).andExpect(status().isOk());\n\t\tverify(DefaultAccessDeniedHandlerForConfig.DENIED_HANDLER).handle(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any());\n\t}\n\n\t@Test\n\tpublic void loginWhenNoCsrfTokenThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(unauthenticated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenNoCsrfTokenThenRespondsWithForbidden() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\").with(user(\"username\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(logoutRequest)\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(authenticated());\n\t\t// @formatter:on\n\t}\n\n\t// SEC-2543\n\t@Test\n\tpublic void logoutWhenCsrfEnabledAndGetRequestThenDoesNotLogout() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder logoutRequest = get(\"/logout\").with(user(\"username\"));\n\t\tthis.mvc.perform(logoutRequest).andExpect(authenticated());\n\t}\n\n\t@Test\n\tpublic void logoutWhenGetRequestAndGetEnabledForLogoutThenLogsOut() throws Exception {\n\t\tthis.spring.register(LogoutAllowsGetConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder logoutRequest = get(\"/logout\").with(user(\"username\"));\n\t\tthis.mvc.perform(logoutRequest).andExpect(unauthenticated());\n\t}\n\n\t// SEC-2749\n\t@Test\n\tpublic void configureWhenRequireCsrfProtectionMatcherNullThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullRequireCsrfProtectionMatcherConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultCsrfTokenRepositoryThenDoesNotCreateSession() throws Exception {\n\t\tthis.spring.register(DefaultDoesNotCreateSession.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\")).andReturn();\n\t\tassertThat(mvcResult.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void getWhenNullAuthenticationStrategyThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullAuthenticationStrategy.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void csrfAuthenticationStrategyConfiguredThenStrategyUsed() throws Exception {\n\t\tCsrfAuthenticationStrategyConfig.STRATEGY = mock(SessionAuthenticationStrategy.class);\n\t\tthis.spring.register(CsrfAuthenticationStrategyConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(loginRequest).andExpect(redirectedUrl(\"/\"));\n\t\tverify(CsrfAuthenticationStrategyConfig.STRATEGY, atLeastOnce()).onAuthentication(any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void getLoginWhenCsrfTokenRequestAttributeHandlerSetThenRespondsWithNormalCsrfToken() throws Exception {\n\t\tCsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tgiven(csrfTokenRepository.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(csrfToken));\n\t\tCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;\n\t\tCsrfTokenRequestHandlerConfig.HANDLER = new CsrfTokenRequestAttributeHandler();\n\t\tthis.spring.register(CsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/login\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(containsString(csrfToken.getToken())));\n\t\tverify(csrfTokenRepository).loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tverifyNoMoreInteractions(csrfTokenRepository);\n\t}\n\n\t@Test\n\tpublic void loginWhenCsrfTokenRequestAttributeHandlerSetAndNormalCsrfTokenThenSuccess() throws Exception {\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tCsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);\n\t\tgiven(csrfTokenRepository.loadToken(any(HttpServletRequest.class))).willReturn(csrfToken);\n\t\tgiven(csrfTokenRepository.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(csrfToken));\n\t\tCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;\n\t\tCsrfTokenRequestHandlerConfig.HANDLER = new CsrfTokenRequestAttributeHandler();\n\t\tthis.spring.register(CsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();\n\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.header(csrfToken.getHeaderName(), csrfToken.getToken())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(loginRequest).andExpect(redirectedUrl(\"/\"));\n\t\tverify(csrfTokenRepository).loadToken(any(HttpServletRequest.class));\n\t\tverify(csrfTokenRepository).saveToken(isNull(), any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tverify(csrfTokenRepository, times(2)).loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tverifyNoMoreInteractions(csrfTokenRepository);\n\t}\n\n\t@Test\n\tpublic void getLoginWhenXorCsrfTokenRequestAttributeHandlerSetThenRespondsWithMaskedCsrfToken() throws Exception {\n\t\tCsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tgiven(csrfTokenRepository.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(csrfToken));\n\t\tCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;\n\t\tCsrfTokenRequestHandlerConfig.HANDLER = new XorCsrfTokenRequestAttributeHandler();\n\t\tthis.spring.register(CsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();\n\t\tthis.mvc.perform(get(\"/login\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(not(containsString(csrfToken.getToken()))));\n\t\tverify(csrfTokenRepository).loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tverifyNoMoreInteractions(csrfTokenRepository);\n\t}\n\n\t@Test\n\tpublic void loginWhenXorCsrfTokenRequestAttributeHandlerSetAndMaskedCsrfTokenThenSuccess() throws Exception {\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tCsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);\n\t\tgiven(csrfTokenRepository.loadToken(any(HttpServletRequest.class))).willReturn(csrfToken);\n\t\tgiven(csrfTokenRepository.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class)))\n\t\t\t.willReturn(new TestDeferredCsrfToken(csrfToken));\n\t\tCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;\n\t\tCsrfTokenRequestHandlerConfig.HANDLER = new XorCsrfTokenRequestAttributeHandler();\n\t\tthis.spring.register(CsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();\n\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/login\")).andReturn();\n\t\tCsrfToken csrfTokenAttribute = (CsrfToken) mvcResult.getRequest().getAttribute(CsrfToken.class.getName());\n\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.header(csrfToken.getHeaderName(), csrfTokenAttribute.getToken())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(loginRequest).andExpect(redirectedUrl(\"/\"));\n\t\tverify(csrfTokenRepository).loadToken(any(HttpServletRequest.class));\n\t\tverify(csrfTokenRepository).saveToken(isNull(), any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tverify(csrfTokenRepository, times(3)).loadDeferredToken(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tverifyNoMoreInteractions(csrfTokenRepository);\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginAndCookieCsrfTokenRepositorySetAndExistingTokenThenRemoves() throws Exception {\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-XSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tCookie existingCookie = new Cookie(\"XSRF-TOKEN\", csrfToken.getToken());\n\t\tCookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();\n\t\tcsrfTokenRepository.setCookieName(existingCookie.getName());\n\t\tCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;\n\t\tCsrfTokenRequestHandlerConfig.HANDLER = new CsrfTokenRequestAttributeHandler();\n\t\tthis.spring.register(CsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();\n\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.cookie(existingCookie)\n\t\t\t\t.header(csrfToken.getHeaderName(), csrfToken.getToken())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\t// @formatter:on\n\t\tMvcResult mvcResult = this.mvc.perform(loginRequest).andExpect(redirectedUrl(\"/\")).andReturn();\n\t\tList<Cookie> cookies = Arrays.asList(mvcResult.getResponse().getCookies());\n\t\tcookies.removeIf((cookie) -> !cookie.getName().equalsIgnoreCase(existingCookie.getName()));\n\t\tassertThat(cookies).hasSize(1);\n\t\tassertThat(cookies.get(0).getValue()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void postWhenHttpBasicAndCookieCsrfTokenRepositorySetAndExistingTokenThenDoesNotGenerateNewToken()\n\t\t\tthrows Exception {\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-XSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tCookie existingCookie = new Cookie(\"XSRF-TOKEN\", csrfToken.getToken());\n\t\tCookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();\n\t\tcsrfTokenRepository.setCookieName(existingCookie.getName());\n\t\tHttpBasicCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;\n\t\tHttpBasicCsrfTokenRequestHandlerConfig.HANDLER = new CsrfTokenRequestAttributeHandler();\n\t\tthis.spring.register(HttpBasicCsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();\n\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.set(csrfToken.getHeaderName(), csrfToken.getToken());\n\t\theaders.setBasicAuth(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(\"/\")\n\t\t\t\t.cookie(existingCookie)\n\t\t\t\t.headers(headers))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tList<Cookie> cookies = Arrays.asList(mvcResult.getResponse().getCookies());\n\t\tcookies.removeIf((cookie) -> !cookie.getName().equalsIgnoreCase(existingCookie.getName()));\n\t\tassertThat(cookies).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getWhenHttpBasicAndCookieCsrfTokenRepositorySetAndNoExistingCookieThenDoesNotGenerateNewToken()\n\t\t\tthrows Exception {\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-XSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tCookie expectedCookie = new Cookie(\"XSRF-TOKEN\", csrfToken.getToken());\n\t\tCookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();\n\t\tcsrfTokenRepository.setCookieName(expectedCookie.getName());\n\t\tHttpBasicCsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;\n\t\tHttpBasicCsrfTokenRequestHandlerConfig.HANDLER = new CsrfTokenRequestAttributeHandler();\n\t\tthis.spring.register(HttpBasicCsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();\n\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.set(csrfToken.getHeaderName(), csrfToken.getToken());\n\t\theaders.setBasicAuth(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\")\n\t\t\t\t.headers(headers))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tList<Cookie> cookies = Arrays.asList(mvcResult.getResponse().getCookies());\n\t\tcookies.removeIf((cookie) -> !cookie.getName().equalsIgnoreCase(expectedCookie.getName()));\n\t\tassertThat(cookies).isEmpty();\n\t}\n\n\t@Test\n\tpublic void spaConfigForbidden() throws Exception {\n\t\tthis.spring.register(CsrfSpaConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(post(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void spaConfigOk() throws Exception {\n\t\tthis.spring.register(CsrfSpaConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(post(\"/\").with(csrf())).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void spaConfigDoubleSubmit() throws Exception {\n\t\tthis.spring.register(CsrfSpaConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tvar token = this.mvc.perform(post(\"/\"))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(cookie().exists(\"XSRF-TOKEN\"))\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getCookie(\"XSRF-TOKEN\");\n\n\t\tthis.mvc\n\t\t\t.perform(post(\"/\").header(\"X-XSRF-TOKEN\", token.getValue())\n\t\t\t\t.cookie(new Cookie(\"XSRF-TOKEN\", token.getValue())))\n\t\t\t.andExpect(status().isOk());\n\t}\n\n\t@Configuration\n\tstatic class AllowHttpMethodsFirewallConfig {\n\n\t\t@Bean\n\t\tStrictHttpFirewall strictHttpFirewall() {\n\t\t\tStrictHttpFirewall result = new StrictHttpFirewall();\n\t\t\tresult.setUnsafeAllowAnyHttpMethod(true);\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfAppliedDefaultConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DisableCsrfConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.disable());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DisableCsrfInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf(AbstractHttpConfigurer::disable);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DisableCsrfEnablesRequestCacheConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.disable());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfDisablesPostRequestFromRequestCacheConfig {\n\n\t\tstatic CsrfTokenRepository REPO;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.csrfTokenRepository(REPO));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvalidSessionUrlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf(withDefaults())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.invalidSessionUrl(\"/error/sessionError\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequireCsrfProtectionMatcherConfig {\n\n\t\tstatic RequestMatcher MATCHER;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.requireCsrfProtectionMatcher(MATCHER));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequireCsrfProtectionMatcherInLambdaConfig {\n\n\t\tstatic RequestMatcher MATCHER;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf.requireCsrfProtectionMatcher(MATCHER));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfTokenRepositoryConfig {\n\n\t\tstatic CsrfTokenRepository REPO;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.csrfTokenRepository(REPO));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfTokenRepositoryInLambdaConfig {\n\n\t\tstatic CsrfTokenRepository REPO;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf.csrfTokenRepository(REPO));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AccessDeniedHandlerConfig {\n\n\t\tstatic AccessDeniedHandler DENIED_HANDLER;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.accessDeniedHandler(DENIED_HANDLER));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultAccessDeniedHandlerForConfig {\n\n\t\tstatic AccessDeniedHandler DENIED_HANDLER;\n\n\t\tstatic RequestMatcher MATCHER;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.defaultAccessDeniedHandlerFor(DENIED_HANDLER, MATCHER));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class LogoutAllowsGetConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.logoutRequestMatcher(pathPattern(\"/logout\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullRequireCsrfProtectionMatcherConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.requireCsrfProtectionMatcher(null));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultDoesNotCreateSession {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().permitAll())\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullAuthenticationStrategy {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.sessionAuthenticationStrategy(null));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfAuthenticationStrategyConfig {\n\n\t\tstatic SessionAuthenticationStrategy STRATEGY;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.sessionAuthenticationStrategy(STRATEGY));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfTokenRequestHandlerConfig {\n\n\t\tstatic CsrfTokenRepository REPO;\n\n\t\tstatic CsrfTokenRequestHandler HANDLER;\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.csrfTokenRepository(REPO)\n\t\t\t\t\t.csrfTokenRequestHandler(HANDLER)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(PasswordEncodedUser.user());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfSpaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.csrf(CsrfConfigurer::spa);\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpBasicCsrfTokenRequestHandlerConfig {\n\n\t\tstatic CsrfTokenRepository REPO;\n\n\t\tstatic CsrfTokenRequestHandler HANDLER;\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.csrfTokenRepository(REPO)\n\t\t\t\t\t.csrfTokenRequestHandler(HANDLER)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(PasswordEncodedUser.user());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@GetMapping(\"/\")\n\t\tvoid rootGet() {\n\t\t}\n\n\t\t@PostMapping(\"/\")\n\t\tvoid rootPost() {\n\t\t}\n\n\t}\n\n\tprivate static final class TestDeferredCsrfToken implements DeferredCsrfToken {\n\n\t\tprivate final CsrfToken csrfToken;\n\n\t\tprivate TestDeferredCsrfToken(CsrfToken csrfToken) {\n\t\t\tthis.csrfToken = csrfToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic CsrfToken get() {\n\t\t\treturn this.csrfToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGenerated() {\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultFiltersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletException;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.builders.TestHttpSecurities;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;\nimport org.springframework.security.web.header.HeaderWriterFilter;\nimport org.springframework.security.web.savedrequest.RequestCacheAwareFilter;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n * @author Konstantin Volivach\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class DefaultFiltersTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void defaultTheWebSecurityConfigurerAdapter() {\n\t\tthis.spring.register(FilterChainProxyBuilderMissingConfig.class);\n\t\tassertThat(this.spring.getContext().getBean(FilterChainProxy.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void nullWebInvocationPrivilegeEvaluator() {\n\t\tthis.spring.register(NullWebInvocationPrivilegeEvaluatorConfig.class, UserDetailsServiceConfig.class);\n\t\tList<SecurityFilterChain> filterChains = this.spring.getContext()\n\t\t\t.getBean(FilterChainProxy.class)\n\t\t\t.getFilterChains();\n\t\tassertThat(filterChains).hasSize(1);\n\t\tDefaultSecurityFilterChain filterChain = (DefaultSecurityFilterChain) filterChains.get(0);\n\t\tassertThat(filterChain.getRequestMatcher()).isInstanceOf(AnyRequestMatcher.class);\n\t\tassertThat(filterChain.getFilters()).hasSize(1);\n\t\tlong filter = filterChain.getFilters()\n\t\t\t.stream()\n\t\t\t.filter((it) -> it instanceof UsernamePasswordAuthenticationFilter)\n\t\t\t.count();\n\t\tassertThat(filter).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void filterChainProxyBuilderIgnoringResources() {\n\t\tthis.spring.register(FilterChainProxyBuilderIgnoringConfig.class, UserDetailsServiceConfig.class);\n\t\tList<SecurityFilterChain> filterChains = this.spring.getContext()\n\t\t\t.getBean(FilterChainProxy.class)\n\t\t\t.getFilterChains();\n\t\tassertThat(filterChains).hasSize(2);\n\t\tDefaultSecurityFilterChain firstFilter = (DefaultSecurityFilterChain) filterChains.get(0);\n\t\tDefaultSecurityFilterChain secondFilter = (DefaultSecurityFilterChain) filterChains.get(1);\n\t\tassertThat(firstFilter.getFilters().isEmpty()).isEqualTo(true);\n\t\tassertThat(secondFilter.getRequestMatcher()).isInstanceOf(AnyRequestMatcher.class);\n\t\tList<Class<? extends Filter>> classes = secondFilter.getFilters()\n\t\t\t.stream()\n\t\t\t.map(Filter::getClass)\n\t\t\t.collect(Collectors.toList());\n\t\tassertThat(classes).contains(WebAsyncManagerIntegrationFilter.class);\n\t\tassertThat(classes).contains(SecurityContextHolderFilter.class);\n\t\tassertThat(classes).contains(HeaderWriterFilter.class);\n\t\tassertThat(classes).contains(LogoutFilter.class);\n\t\tassertThat(classes).contains(CsrfFilter.class);\n\t\tassertThat(classes).contains(RequestCacheAwareFilter.class);\n\t\tassertThat(classes).contains(SecurityContextHolderAwareRequestFilter.class);\n\t\tassertThat(classes).contains(AnonymousAuthenticationFilter.class);\n\t\tassertThat(classes).contains(ExceptionTranslationFilter.class);\n\t\tassertThat(classes).contains(AuthorizationFilter.class);\n\t}\n\n\t@Test\n\tpublic void defaultFiltersPermitAll() throws IOException, ServletException {\n\t\tthis.spring.register(DefaultFiltersConfigPermitAll.class, UserDetailsServiceConfig.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/logout\");\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"BaseSpringSpec_CSRFTOKEN\");\n\t\tCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();\n\t\trepository.saveToken(csrfToken, request, response);\n\t\tCsrfTokenRequestHandler handler = new XorCsrfTokenRequestAttributeHandler();\n\t\thandler.handle(request, response, () -> csrfToken);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\trequest.setParameter(token.getParameterName(), token.getToken());\n\t\tthis.spring.getContext()\n\t\t\t.getBean(\"springSecurityFilterChain\", Filter.class)\n\t\t\t.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/login?logout\");\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FilterChainProxyBuilderMissingConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UserDetailsServiceConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullWebInvocationPrivilegeEvaluatorConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tTestHttpSecurities.disableDefaults(http);\n\t\t\thttp.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class FilterChainProxyBuilderIgnoringConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/resources/**\");\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultFiltersConfigPermitAll {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link DefaultLoginPageConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class DefaultLoginPageConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void getWhenFormLoginEnabledThenRedirectsToLoginPage() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(redirectedUrl(\"/login\"));\n\t}\n\n\t@Test\n\tpublic void loginPageThenDefaultLoginPageIsRendered() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageConfig.class).autowire();\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"BaseSpringSpec_CSRFTOKEN\");\n\t\tString csrfAttributeName = HttpSessionCsrfTokenRepository.class.getName().concat(\".CSRF_TOKEN\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/login\").sessionAttr(csrfAttributeName, csrfToken))\n\t\t\t\t.andExpect((result) -> {\n\t\t\t\t\tCsrfToken token = (CsrfToken) result.getRequest().getAttribute(CsrfToken.class.getName());\n\t\t\t\t\tassertThat(result.getResponse().getContentAsString()).isEqualTo(\"\"\"\n\t\t\t\t\t\t<!DOCTYPE html>\n\t\t\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t\t  <head>\n\t\t\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t\t  </head>\n\t\t\t\t\t\t  <body>\n\t\t\t\t\t\t    <div class=\"content\">\n\t\t\t\t\t\t      <form class=\"login-form\" method=\"post\" action=\"/login\">\n\t\t\t\t\t\t        <h2>Please sign in</h2>\n\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t\t\t\t          <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n\t\t\t\t\t\t        </p>\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t\t\t\t          <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n\t\t\t\t\t\t        </p>\n\n\t\t\t\t\t\t<input name=\"_csrf\" type=\"hidden\" value=\"%s\" />\n\t\t\t\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t\t\t\t      </form>\n\n\n\n\t\t\t\t\t\t    </div>\n\t\t\t\t\t\t  </body>\n\t\t\t\t\t\t</html>\"\"\".formatted(token.getToken()));\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenNoCredentialsThenRedirectedToLoginPageWithError() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/login\").with(csrf())).andExpect(redirectedUrl(\"/login?error\"));\n\t}\n\n\t@Test\n\tpublic void loginPageWhenErrorThenDefaultLoginPageWithError() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageConfig.class).autowire();\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"BaseSpringSpec_CSRFTOKEN\");\n\t\tString csrfAttributeName = HttpSessionCsrfTokenRepository.class.getName().concat(\".CSRF_TOKEN\");\n\t\tMvcResult mvcResult = this.mvc.perform(post(\"/login\").with(csrf())).andReturn();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/login?error\").session((MockHttpSession) mvcResult.getRequest().getSession())\n\t\t\t\t.sessionAttr(csrfAttributeName, csrfToken))\n\t\t\t\t.andExpect((result) -> {\n\t\t\t\t\tString defaultErrorMessage = \"Invalid credentials\";\n\t\t\t\t\tCsrfToken token = (CsrfToken) result.getRequest().getAttribute(CsrfToken.class.getName());\n\t\t\t\t\tassertThat(result.getResponse().getContentAsString()).isEqualTo(\"\"\"\n\t\t\t\t\t\t<!DOCTYPE html>\n\t\t\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t\t  <head>\n\t\t\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t\t  </head>\n\t\t\t\t\t\t  <body>\n\t\t\t\t\t\t    <div class=\"content\">\n\t\t\t\t\t\t      <form class=\"login-form\" method=\"post\" action=\"/login\">\n\t\t\t\t\t\t        <h2>Please sign in</h2>\n\t\t\t\t\t\t<div class=\"alert alert-danger\" role=\"alert\">%s</div>\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t\t\t\t          <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n\t\t\t\t\t\t        </p>\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t\t\t\t          <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n\t\t\t\t\t\t        </p>\n\n\t\t\t\t\t\t<input name=\"_csrf\" type=\"hidden\" value=\"%s\" />\n\t\t\t\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t\t\t\t      </form>\n\n\n\n\t\t\t\t\t\t    </div>\n\t\t\t\t\t\t  </body>\n\t\t\t\t\t\t</html>\"\"\".formatted(defaultErrorMessage, token.getToken()));\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenValidCredentialsThenRedirectsToDefaultSuccessPage() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(loginRequest).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void loginPageWhenLoggedOutThenDefaultLoginPageWithLogoutMessage() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageConfig.class).autowire();\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"BaseSpringSpec_CSRFTOKEN\");\n\t\tString csrfAttributeName = HttpSessionCsrfTokenRepository.class.getName().concat(\".CSRF_TOKEN\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/login?logout\").sessionAttr(csrfAttributeName, csrfToken))\n\t\t\t\t.andExpect((result) -> {\n\t\t\t\t\tCsrfToken token = (CsrfToken) result.getRequest().getAttribute(CsrfToken.class.getName());\n\t\t\t\t\tassertThat(result.getResponse().getContentAsString()).isEqualTo(\"\"\"\n\t\t\t\t\t\t<!DOCTYPE html>\n\t\t\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t\t  <head>\n\t\t\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t\t  </head>\n\t\t\t\t\t\t  <body>\n\t\t\t\t\t\t    <div class=\"content\">\n\t\t\t\t\t\t      <form class=\"login-form\" method=\"post\" action=\"/login\">\n\t\t\t\t\t\t        <h2>Please sign in</h2>\n\t\t\t\t\t\t<div class=\"alert alert-success\" role=\"alert\">You have been signed out</div>\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t\t\t\t          <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n\t\t\t\t\t\t        </p>\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t\t\t\t          <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n\t\t\t\t\t\t        </p>\n\n\t\t\t\t\t\t<input name=\"_csrf\" type=\"hidden\" value=\"%s\" />\n\t\t\t\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t\t\t\t      </form>\n\n\n\n\t\t\t\t\t\t    </div>\n\t\t\t\t\t\t  </body>\n\t\t\t\t\t\t</html>\"\"\".formatted(token.getToken()));\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void cssWhenFormLoginConfiguredThenServesCss() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/default-ui.css\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"content-type\", \"text/css;charset=UTF-8\"))\n\t\t\t\t.andExpect(content().string(containsString(\"body {\")));\n\t}\n\n\t@Test\n\tpublic void loginPageWhenLoggedOutAndCustomLogoutSuccessHandlerThenDoesNotRenderLoginPage() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageCustomLogoutSuccessHandlerConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/login?logout\")).andExpect(content().string(\"\"));\n\t}\n\n\t@Test\n\tpublic void loginPageWhenLoggedOutAndCustomLogoutSuccessUrlThenDoesNotRenderLoginPage() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageCustomLogoutSuccessUrlConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/login?logout\")).andExpect(content().string(\"\"));\n\t}\n\n\t@Test\n\tpublic void loginPageWhenRememberConfigureThenDefaultLoginPageWithRememberMeCheckbox() throws Exception {\n\t\tthis.spring.register(DefaultLoginPageWithRememberMeConfig.class).autowire();\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"BaseSpringSpec_CSRFTOKEN\");\n\t\tString csrfAttributeName = HttpSessionCsrfTokenRepository.class.getName().concat(\".CSRF_TOKEN\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/login\").sessionAttr(csrfAttributeName, csrfToken))\n\t\t\t\t.andExpect((result) -> {\n\t\t\t\t\tCsrfToken token = (CsrfToken) result.getRequest().getAttribute(CsrfToken.class.getName());\n\t\t\t\t\tassertThat(result.getResponse().getContentAsString()).isEqualTo(\"\"\"\n\t\t\t\t\t\t<!DOCTYPE html>\n\t\t\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t\t  <head>\n\t\t\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t\t  </head>\n\t\t\t\t\t\t  <body>\n\t\t\t\t\t\t    <div class=\"content\">\n\t\t\t\t\t\t      <form class=\"login-form\" method=\"post\" action=\"/login\">\n\t\t\t\t\t\t        <h2>Please sign in</h2>\n\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t\t\t\t          <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n\t\t\t\t\t\t        </p>\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t\t\t\t          <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n\t\t\t\t\t\t        </p>\n\t\t\t\t\t\t<p><input type='checkbox' name='remember-me'/> Remember me on this computer.</p>\n\t\t\t\t\t\t<input name=\"_csrf\" type=\"hidden\" value=\"%s\" />\n\t\t\t\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t\t\t\t      </form>\n\n\n\n\t\t\t\t\t\t    </div>\n\t\t\t\t\t\t  </body>\n\t\t\t\t\t\t</html>\"\"\".formatted(token.getToken()));\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnDefaultLoginPageGeneratingFilter() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(DefaultLoginPageGeneratingFilter.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnUsernamePasswordAuthenticationFilter() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(UsernamePasswordAuthenticationFilter.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnLoginUrlAuthenticationEntryPoint() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(LoginUrlAuthenticationEntryPoint.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnExceptionTranslationFilter() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(ExceptionTranslationFilter.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenAuthenticationEntryPointThenNoDefaultLoginPageGeneratingFilter() {\n\t\tthis.spring.register(DefaultLoginWithCustomAuthenticationEntryPointConfig.class).autowire();\n\t\tFilterChainProxy filterChain = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tassertThat(filterChain.getFilterChains()\n\t\t\t.get(0)\n\t\t\t.getFilters()\n\t\t\t.stream()\n\t\t\t.filter((filter) -> filter.getClass().isAssignableFrom(DefaultLoginPageGeneratingFilter.class))\n\t\t\t.count()).isZero();\n\t}\n\n\t@Test\n\tpublic void configureWhenAuthenticationEntryPointThenDoesNotServeCss() throws Exception {\n\t\tthis.spring.register(DefaultLoginWithCustomAuthenticationEntryPointConfig.class).autowire();\n\t\tFilterChainProxy filterChain = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tassertThat(filterChain.getFilterChains()\n\t\t\t.get(0)\n\t\t\t.getFilters()\n\t\t\t.stream()\n\t\t\t.filter((filter) -> filter.getClass().isAssignableFrom(DefaultResourcesFilter.class))\n\t\t\t.count()).isZero();\n\t\t//@formatter:off\n\t\tthis.mvc.perform(get(\"/default-ui.css\"))\n\t\t\t\t.andExpect(status().is3xxRedirection());\n\t\t//@formatter:on\n\t}\n\n\t@Test\n\tpublic void formLoginWhenLogoutEnabledThenCreatesDefaultLogoutPage() throws Exception {\n\t\tthis.spring.register(DefaultLogoutPageConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/logout\").with(user(\"user\"))).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void formLoginWhenLogoutDisabledThenDefaultLogoutPageDoesNotExist() throws Exception {\n\t\tthis.spring.register(LogoutDisabledConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/logout\").with(user(\"user\"))).andExpect(status().isNotFound());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultLoginPageConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultLoginPageCustomLogoutSuccessHandlerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.logoutSuccessHandler(new SimpleUrlLogoutSuccessHandler()))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultLoginPageCustomLogoutSuccessUrlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.logoutSuccessUrl(\"/login?logout\"))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultLoginPageWithRememberMeConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultLoginWithCustomAuthenticationEntryPointConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint(\"/login\")))\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.exceptionHandling(withDefaults())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultLogoutPageConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class LogoutDisabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t\t\t.disable()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class ExceptionHandlingConfigurerAccessDeniedHandlerTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\t@WithMockUser(roles = \"ANYTHING\")\n\tpublic void getWhenAccessDeniedOverriddenThenCustomizesResponseByRequest() throws Exception {\n\t\tthis.spring.register(RequestMatcherBasedAccessDeniedHandlerConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/hello\")).andExpect(status().isIAmATeapot());\n\t\tthis.mvc.perform(get(\"/goodbye\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ANYTHING\")\n\tpublic void getWhenAccessDeniedOverriddenInLambdaThenCustomizesResponseByRequest() throws Exception {\n\t\tthis.spring.register(RequestMatcherBasedAccessDeniedHandlerInLambdaConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/hello\")).andExpect(status().isIAmATeapot());\n\t\tthis.mvc.perform(get(\"/goodbye\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ANYTHING\")\n\tpublic void getWhenAccessDeniedOverriddenByOnlyOneHandlerThenAllRequestsUseThatHandler() throws Exception {\n\t\tthis.spring.register(SingleRequestMatcherAccessDeniedHandlerConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/hello\")).andExpect(status().isIAmATeapot());\n\t\tthis.mvc.perform(get(\"/goodbye\")).andExpect(status().isIAmATeapot());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestMatcherBasedAccessDeniedHandlerConfig {\n\n\t\tAccessDeniedHandler teapotDeniedHandler = (request, response, exception) -> response\n\t\t\t.setStatus(HttpStatus.I_AM_A_TEAPOT.value());\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.defaultAccessDeniedHandlerFor(\n\t\t\t\t\t\tthis.teapotDeniedHandler,\n\t\t\t\t\t\t\tpathPattern(\"/hello/**\"))\n\t\t\t\t\t.defaultAccessDeniedHandlerFor(\n\t\t\t\t\t\tnew AccessDeniedHandlerImpl(),\n\t\t\t\t\t\tAnyRequestMatcher.INSTANCE));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestMatcherBasedAccessDeniedHandlerInLambdaConfig {\n\n\t\tAccessDeniedHandler teapotDeniedHandler = (request, response, exception) -> response\n\t\t\t.setStatus(HttpStatus.I_AM_A_TEAPOT.value());\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t)\n\t\t\t\t.exceptionHandling((exceptionHandling) -> exceptionHandling\n\t\t\t\t\t\t.defaultAccessDeniedHandlerFor(\n\t\t\t\t\t\t\t\tthis.teapotDeniedHandler,\n\t\t\t\t\t\t\t\tpathPattern(\"/hello/**\")\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.defaultAccessDeniedHandlerFor(\n\t\t\t\t\t\t\t\tnew AccessDeniedHandlerImpl(),\n\t\t\t\t\t\t\t\tAnyRequestMatcher.INSTANCE\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SingleRequestMatcherAccessDeniedHandlerConfig {\n\n\t\tAccessDeniedHandler teapotDeniedHandler = (request, response, exception) -> response\n\t\t\t.setStatus(HttpStatus.I_AM_A_TEAPOT.value());\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.defaultAccessDeniedHandlerFor(\n\t\t\t\t\t\tthis.teapotDeniedHandler,\n\t\t\t\t\t\t\tpathPattern(\"/hello/**\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.context.request.NativeWebRequest;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link ExceptionHandlingConfigurer}\n *\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class ExceptionHandlingConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnExceptionTranslationFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(ExceptionTranslationFilter.class));\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsApplicationXhtmlXmlThenRespondsWith302() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_XHTML_XML))\n\t\t\t.andExpect(status().isFound());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsImageGifThenRespondsWith302() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.IMAGE_GIF)).andExpect(status().isFound());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsImageJpgThenRespondsWith302() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.IMAGE_JPEG)).andExpect(status().isFound());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsImagePngThenRespondsWith302() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.IMAGE_PNG)).andExpect(status().isFound());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsTextHtmlThenRespondsWith302() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML)).andExpect(status().isFound());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsTextPlainThenRespondsWith302() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.TEXT_PLAIN)).andExpect(status().isFound());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsApplicationAtomXmlThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_ATOM_XML))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsApplicationFormUrlEncodedThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_FORM_URLENCODED))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsApplicationJsonThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsApplicationOctetStreamThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_OCTET_STREAM))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsMultipartFormDataThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.MULTIPART_FORM_DATA))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t// SEC-2199\n\t@Test\n\tpublic void getWhenAcceptHeaderIsTextXmlThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.TEXT_XML)).andExpect(status().isUnauthorized());\n\t}\n\n\t// gh-4831\n\t@Test\n\tpublic void getWhenAcceptIsAnyThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(DefaultSecurityConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, MediaType.ALL)).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenAcceptIsChromeThenRespondsWith302() throws Exception {\n\t\tthis.spring.register(DefaultSecurityConfig.class).autowire();\n\t\tthis.mvc\n\t\t\t.perform(get(\"/\").header(HttpHeaders.ACCEPT,\n\t\t\t\t\t\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\"))\n\t\t\t.andExpect(status().isFound());\n\t}\n\n\t@Test\n\tpublic void getWhenAcceptIsTextPlainAndXRequestedWithIsXHRThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(HttpBasicAndFormLoginEntryPointsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(\"Accept\", MediaType.TEXT_PLAIN).header(\"X-Requested-With\", \"XMLHttpRequest\"))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenCustomContentNegotiationStrategyThenStrategyIsUsed() throws Exception {\n\t\tthis.spring.register(OverrideContentNegotiationStrategySharedObjectConfig.class, DefaultSecurityConfig.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(OverrideContentNegotiationStrategySharedObjectConfig.CNS, atLeastOnce())\n\t\t\t.resolveMediaTypes(any(NativeWebRequest.class));\n\t}\n\n\t@Test\n\tpublic void getWhenCustomSecurityContextHolderStrategyThenUsed() throws Exception {\n\t\tthis.spring.register(SecurityContextChangedListenerConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t\tSecurityContextChangedListener listener = this.spring.getContext()\n\t\t\t.getBean(SecurityContextChangedListener.class);\n\t\tverify(listener).securityContextChanged(setAuthentication(AnonymousAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsAndUnauthenticatedThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(DefaultHttpConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, \"bogus/type\")).andExpect(redirectedUrl(\"/login\"));\n\t}\n\n\t@Test\n\tpublic void getWhenDeclaringHttpBasicBeforeFormLoginThenRespondsWith401() throws Exception {\n\t\tthis.spring.register(BasicAuthenticationEntryPointBeforeFormLoginConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").header(HttpHeaders.ACCEPT, \"bogus/type\")).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void getWhenInvokingExceptionHandlingTwiceThenOriginalEntryPointUsed() throws Exception {\n\t\tthis.spring.register(InvokeTwiceDoesNotOverrideConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(InvokeTwiceDoesNotOverrideConfig.AEP).commence(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(AuthenticationException.class));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.exceptionHandling(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultSecurityConfig {\n\n\t\t@Bean\n\t\tInMemoryUserDetailsManager userDetailsManager() {\n\t\t\t// @formatter:off\n\t\t\treturn new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build()\n\t\t\t);\n\t\t\t// @formatter:off\n\t\t}\n\t}\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpBasicAndFormLoginEntryPointsConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OverrideContentNegotiationStrategySharedObjectConfig {\n\n\t\tstatic ContentNegotiationStrategy CNS = mock(ContentNegotiationStrategy.class);\n\n\t\t@Bean\n\t\tstatic ContentNegotiationStrategy cns() {\n\t\t\treturn CNS;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultHttpConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class BasicAuthenticationEntryPointBeforeFormLoginConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvokeTwiceDoesNotOverrideConfig {\n\n\t\tstatic AuthenticationEntryPoint AEP = mock(AuthenticationEntryPoint.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.authenticationEntryPoint(AEP))\n\t\t\t\t.exceptionHandling(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authorization.AllAuthoritiesAuthorizationManager;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagers;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.users.AuthenticationTestConfiguration;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Eleftheria Stein\n * @since 5.1\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class FormLoginConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void requestCache() throws Exception {\n\t\tthis.spring.register(RequestCacheConfig.class, AuthenticationTestConfiguration.class).autowire();\n\t\tRequestCacheConfig config = this.spring.getContext().getBean(RequestCacheConfig.class);\n\t\tthis.mockMvc.perform(formLogin()).andExpect(authenticated());\n\t\tverify(config.requestCache).getRequest(any(), any());\n\t}\n\n\t@Test\n\tpublic void requestCacheAsBean() throws Exception {\n\t\tthis.spring.register(RequestCacheBeanConfig.class, AuthenticationTestConfiguration.class).autowire();\n\t\tRequestCache requestCache = this.spring.getContext().getBean(RequestCache.class);\n\t\tthis.mockMvc.perform(formLogin()).andExpect(authenticated());\n\t\tverify(requestCache).getRequest(any(), any());\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginConfiguredThenHasDefaultUsernameAndPasswordParameterNames() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder loginRequest = formLogin()\n\t\t\t\t.user(\"username\", \"user\")\n\t\t\t\t.password(\"password\", \"password\");\n\t\tthis.mockMvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void formLoginWhenSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class, SecurityContextChangedListenerConfig.class).autowire();\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder loginRequest = formLogin()\n\t\t\t\t.user(\"username\", \"user\")\n\t\t\t\t.password(\"password\", \"password\");\n\t\tthis.mockMvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t\tSecurityContextChangedListener listener = this.spring.getContext()\n\t\t\t.getBean(SecurityContextChangedListener.class);\n\t\tverify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginConfiguredThenHasDefaultFailureUrl() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(formLogin().user(\"invalid\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginConfiguredThenHasDefaultSuccessUrl() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(formLogin())\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getLoginPageWhenFormLoginConfiguredThenNotSecured() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/login\")).andExpect(status().isFound());\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginConfiguredThenSecured() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\tthis.mockMvc.perform(post(\"/login\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void requestProtectedWhenFormLoginConfiguredThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/private\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginDefaultsInLambdaThenHasDefaultUsernameAndPasswordParameterNames() throws Exception {\n\t\tthis.spring.register(FormLoginInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder loginRequest = formLogin()\n\t\t\t\t.user(\"username\", \"user\")\n\t\t\t\t.password(\"password\", \"password\");\n\t\tthis.mockMvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginDefaultsInLambdaThenHasDefaultFailureUrl() throws Exception {\n\t\tthis.spring.register(FormLoginInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(formLogin().user(\"invalid\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginDefaultsInLambdaThenHasDefaultSuccessUrl() throws Exception {\n\t\tthis.spring.register(FormLoginInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(formLogin())\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getLoginPageWhenFormLoginDefaultsInLambdaThenNotSecured() throws Exception {\n\t\tthis.spring.register(FormLoginInLambdaConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/login\")).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginDefaultsInLambdaThenSecured() throws Exception {\n\t\tthis.spring.register(FormLoginInLambdaConfig.class).autowire();\n\t\tthis.mockMvc.perform(post(\"/login\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void requestProtectedWhenFormLoginDefaultsInLambdaThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(FormLoginInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/private\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getLoginPageWhenFormLoginPermitAllThenPermittedAndNoRedirect() throws Exception {\n\t\tthis.spring.register(FormLoginConfigPermitAll.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/login\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(redirectedUrl(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getLoginPageWithErrorQueryWhenFormLoginPermitAllThenPermittedAndNoRedirect() throws Exception {\n\t\tthis.spring.register(FormLoginConfigPermitAll.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/login?error\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(redirectedUrl(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenFormLoginPermitAllAndInvalidUserThenRedirectsToLoginPageWithError() throws Exception {\n\t\tthis.spring.register(FormLoginConfigPermitAll.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(formLogin().user(\"invalid\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getLoginPageWhenCustomLoginPageThenPermittedAndNoRedirect() throws Exception {\n\t\tthis.spring.register(FormLoginDefaultsConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/authenticate\")).andExpect(redirectedUrl(null));\n\t}\n\n\t@Test\n\tpublic void getLoginPageWithErrorQueryWhenCustomLoginPageThenPermittedAndNoRedirect() throws Exception {\n\t\tthis.spring.register(FormLoginDefaultsConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/authenticate?error\")).andExpect(redirectedUrl(null));\n\t}\n\n\t@Test\n\tpublic void loginWhenCustomLoginPageAndInvalidUserThenRedirectsToCustomLoginPageWithError() throws Exception {\n\t\tthis.spring.register(FormLoginDefaultsConfig.class).autowire();\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin(\"/authenticate\").user(\"invalid\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(request)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/authenticate?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomLoginPageThenRedirectsToCustomLoginPage() throws Exception {\n\t\tthis.spring.register(FormLoginDefaultsConfig.class).autowire();\n\t\tthis.mockMvc.perform(logout()).andExpect(redirectedUrl(\"/authenticate?logout\"));\n\t}\n\n\t@Test\n\tpublic void getLoginPageWithLogoutQueryWhenCustomLoginPageThenPermittedAndNoRedirect() throws Exception {\n\t\tthis.spring.register(FormLoginDefaultsConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/authenticate?logout\")).andExpect(redirectedUrl(null));\n\t}\n\n\t@Test\n\tpublic void getLoginPageWhenCustomLoginPageInLambdaThenPermittedAndNoRedirect() throws Exception {\n\t\tthis.spring.register(FormLoginDefaultsInLambdaConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/authenticate\")).andExpect(redirectedUrl(null));\n\t}\n\n\t@Test\n\tpublic void loginWhenCustomLoginProcessingUrlThenRedirectsToHome() throws Exception {\n\t\tthis.spring.register(FormLoginLoginProcessingUrlConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(formLogin(\"/loginCheck\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenCustomLoginProcessingUrlInLambdaThenRedirectsToHome() throws Exception {\n\t\tthis.spring.register(FormLoginLoginProcessingUrlInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(formLogin(\"/loginCheck\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomPortMapperThenPortMapperUsed() throws Exception {\n\t\tFormLoginUsesPortMapperConfig.PORT_MAPPER = mock(PortMapper.class);\n\t\tgiven(FormLoginUsesPortMapperConfig.PORT_MAPPER.lookupHttpsPort(any())).willReturn(9443);\n\t\tthis.spring.register(FormLoginUsesPortMapperConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"http://localhost:9090\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost:9443/login\"));\n\t\t// @formatter:on\n\t\tverify(FormLoginUsesPortMapperConfig.PORT_MAPPER).lookupHttpsPort(any());\n\t}\n\n\t@Test\n\tpublic void failureUrlWhenPermitAllAndFailureHandlerThenSecured() throws Exception {\n\t\tthis.spring.register(PermitAllIgnoresFailureHandlerConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/login?error\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void formLoginWhenInvokedTwiceThenUsesOriginalUsernameParameter() throws Exception {\n\t\tthis.spring.register(DuplicateInvocationsDoesNotOverrideConfig.class).autowire();\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder loginRequest = formLogin().user(\"custom-username\",\n\t\t\t\t\"user\");\n\t\tthis.mockMvc.perform(loginRequest).andExpect(authenticated());\n\t}\n\n\t@Test\n\tpublic void loginWhenInvalidLoginAndFailureForwardUrlThenForwardsToFailureForwardUrl() throws Exception {\n\t\tthis.spring.register(FormLoginUserForwardAuthenticationSuccessAndFailureConfig.class).autowire();\n\t\tSecurityMockMvcRequestBuilders.FormLoginRequestBuilder loginRequest = formLogin().user(\"invalid\");\n\t\tthis.mockMvc.perform(loginRequest).andExpect(forwardedUrl(\"/failure_forward_url\"));\n\t}\n\n\t@Test\n\tpublic void loginWhenSuccessForwardUrlThenForwardsToSuccessForwardUrl() throws Exception {\n\t\tthis.spring.register(FormLoginUserForwardAuthenticationSuccessAndFailureConfig.class).autowire();\n\t\tthis.mockMvc.perform(formLogin()).andExpect(forwardedUrl(\"/success_forward_url\"));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnUsernamePasswordAuthenticationFilter() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(UsernamePasswordAuthenticationFilter.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnLoginUrlAuthenticationEntryPoint() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(LoginUrlAuthenticationEntryPoint.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnExceptionTranslationFilter() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(ExceptionTranslationFilter.class));\n\t}\n\n\t@Test\n\tvoid requestWhenUnauthenticatedThenRequiresTwoSteps() throws Exception {\n\t\tthis.spring.register(MfaDslConfig.class, UserConfig.class).autowire();\n\t\tUserDetails user = PasswordEncodedUser.user();\n\t\tthis.mockMvc.perform(get(\"/profile\").with(user(user)))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\n\t\t\t\t\t\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"));\n\t\tthis.mockMvc\n\t\t\t.perform(post(\"/ott/generate\").param(\"username\", \"rod\")\n\t\t\t\t.with(user(user))\n\t\t\t\t.with(SecurityMockMvcRequestPostProcessors.csrf()))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/ott/sent\"));\n\t\tthis.mockMvc\n\t\t\t.perform(post(\"/login\").param(\"username\", \"rod\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(SecurityMockMvcRequestPostProcessors.csrf()))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\tuser = PasswordEncodedUser.withUserDetails(user)\n\t\t\t.authorities(\"profile:read\", FactorGrantedAuthority.OTT_AUTHORITY)\n\t\t\t.build();\n\t\tthis.mockMvc.perform(get(\"/profile\").with(user(user)))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"));\n\t\tuser = PasswordEncodedUser.withUserDetails(user)\n\t\t\t.authorities(\"profile:read\", FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.build();\n\t\tthis.mockMvc.perform(get(\"/profile\").with(user(user)))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"));\n\t\tuser = PasswordEncodedUser.withUserDetails(user)\n\t\t\t.authorities(\"profile:read\", FactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY)\n\t\t\t.build();\n\t\tthis.mockMvc.perform(get(\"/profile\").with(user(user))).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tvoid requestWhenUnauthenticatedX509ThenRequiresTwoSteps() throws Exception {\n\t\tthis.spring.register(MfaDslX509Config.class, UserConfig.class, BasicMfaController.class).autowire();\n\t\tthis.mockMvc.perform(get(\"/profile\")).andExpect(status().is3xxRedirection());\n\t\tthis.mockMvc.perform(get(\"/profile\").with(user(User.withUsername(\"rod\").authorities(\"profile:read\").build())))\n\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mockMvc.perform(get(\"/login\")).andExpect(status().isOk());\n\t\tthis.mockMvc.perform(get(\"/profile\").with(SecurityMockMvcRequestPostProcessors.x509(\"rod.cer\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"));\n\t\tthis.mockMvc\n\t\t\t.perform(post(\"/login\").param(\"username\", \"rod\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(SecurityMockMvcRequestPostProcessors.x509(\"rod.cer\"))\n\t\t\t\t.with(SecurityMockMvcRequestPostProcessors.csrf()))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\tUserDetails authorized = PasswordEncodedUser.withUsername(\"rod\")\n\t\t\t.authorities(\"profile:read\", FactorGrantedAuthority.X509_AUTHORITY,\n\t\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.build();\n\t\tthis.mockMvc.perform(get(\"/profile\").with(user(authorized))).andExpect(status().isOk());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestCacheConfig {\n\n\t\tprivate RequestCache requestCache = mock(RequestCache.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.requestCache((cache) -> cache\n\t\t\t\t\t.requestCache(this.requestCache));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestCacheBeanConfig {\n\n\t\t@Bean\n\t\tRequestCache requestCache() {\n\t\t\treturn mock(RequestCache.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class FormLoginConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/resources/**\");\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginPage(\"/login\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginConfigPermitAll {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginDefaultsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginPage(\"/authenticate\")\n\t\t\t\t\t.permitAll())\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginDefaultsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.formLogin((formLogin) -> formLogin\n\t\t\t\t\t\t.loginPage(\"/authenticate\")\n\t\t\t\t\t\t.permitAll()\n\t\t\t\t)\n\t\t\t\t.logout(LogoutConfigurer::permitAll);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginLoginProcessingUrlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginProcessingUrl(\"/loginCheck\")\n\t\t\t\t\t.loginPage(\"/login\")\n\t\t\t\t\t.defaultSuccessUrl(\"/\", true)\n\t\t\t\t\t.passwordParameter(\"password\")\n\t\t\t\t\t.usernameParameter(\"username\")\n\t\t\t\t\t.permitAll())\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.logoutSuccessUrl(\"/login\")\n\t\t\t\t\t.logoutUrl(\"/logout\")\n\t\t\t\t\t.deleteCookies(\"JSESSIONID\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginLoginProcessingUrlInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin((formLogin) -> formLogin\n\t\t\t\t\t\t.loginProcessingUrl(\"/loginCheck\")\n\t\t\t\t\t\t.loginPage(\"/login\")\n\t\t\t\t\t\t.defaultSuccessUrl(\"/\", true)\n\t\t\t\t\t\t.permitAll()\n\t\t\t\t)\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t\t.logoutSuccessUrl(\"/login\")\n\t\t\t\t\t\t.logoutUrl(\"/logout\")\n\t\t\t\t\t\t.deleteCookies(\"JSESSIONID\")\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginUsesPortMapperConfig {\n\n\t\tstatic PortMapper PORT_MAPPER;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.permitAll())\n\t\t\t\t.portMapper((mapper) -> mapper\n\t\t\t\t\t.portMapper(PORT_MAPPER));\n\t\t\t// @formatter:on\n\t\t\tLoginUrlAuthenticationEntryPoint authenticationEntryPoint = (LoginUrlAuthenticationEntryPoint) http\n\t\t\t\t.getConfigurer(FormLoginConfigurer.class)\n\t\t\t\t.getAuthenticationEntryPoint();\n\t\t\tauthenticationEntryPoint.setForceHttps(true);\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PermitAllIgnoresFailureHandlerConfig {\n\n\t\tstatic AuthenticationFailureHandler FAILURE_HANDLER = mock(AuthenticationFailureHandler.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.failureHandler(FAILURE_HANDLER)\n\t\t\t\t\t.permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DuplicateInvocationsDoesNotOverrideConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.usernameParameter(\"custom-username\"))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginUserForwardAuthenticationSuccessAndFailureConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.disable())\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.failureForwardUrl(\"/failure_forward_url\")\n\t\t\t\t\t.successForwardUrl(\"/success_forward_url\")\n\t\t\t\t\t.permitAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.exceptionHandling(withDefaults())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MfaDslConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http,\n\t\t\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authz) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.oneTimeTokenLogin(Customizer.withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.requestMatchers(\"/profile\").access(authz.hasAuthority(\"profile:read\"))\n\t\t\t\t\t.anyRequest().access(authz.authenticated())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationManagerFactory<?> authz() {\n\t\t\treturn new AuthorizationManagerFactory<>(FactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableMethodSecurity\n\tstatic class MfaDslX509Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http,\n\t\t\t\tAuthorizationManagerFactory<RequestAuthorizationContext> authz) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.x509(Customizer.withDefaults())\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().access(authz.authenticated())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationManagerFactory<?> authz() {\n\t\t\treturn new AuthorizationManagerFactory<>(FactorGrantedAuthority.X509_AUTHORITY,\n\t\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UserConfig {\n\n\t\t@Bean\n\t\tUserDetails rod() {\n\t\t\treturn PasswordEncodedUser.withUsername(\"rod\").password(\"password\").build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService users(UserDetails user) {\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BasicMfaController {\n\n\t\t@GetMapping(\"/profile\")\n\t\t@PreAuthorize(\"@authz.hasAuthority('profile:read')\")\n\t\tString profile() {\n\t\t\treturn \"profile\";\n\t\t}\n\n\t}\n\n\tpublic static class AuthorizationManagerFactory<T> {\n\n\t\tprivate final AuthorizationManager<T> authorities;\n\n\t\tAuthorizationManagerFactory(String... authorities) {\n\t\t\tthis.authorities = AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities);\n\t\t}\n\n\t\tpublic AuthorizationManager<T> authenticated() {\n\t\t\tAuthenticatedAuthorizationManager<T> authenticated = AuthenticatedAuthorizationManager.authenticated();\n\t\t\treturn AuthorizationManagers.allOf(new AuthorizationDecision(false), this.authorities, authenticated);\n\t\t}\n\n\t\tpublic AuthorizationManager<T> hasAuthority(String authority) {\n\t\t\tAuthorityAuthorizationManager<T> authorized = AuthorityAuthorizationManager.hasAuthority(authority);\n\t\t\treturn AuthorizationManagers.allOf(new AuthorizationDecision(false), this.authorities, authorized);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerEagerHeadersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.header.HeaderWriterFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\n\n/**\n * Tests for {@link HeadersConfigurer}.\n *\n * @author Ankur Pathak\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HeadersConfigurerEagerHeadersTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenHeadersEagerlyConfiguredThenHeadersAreWritten() throws Exception {\n\t\tthis.spring.register(HeadersAtTheBeginningOfRequestConfig.class, HomeController.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(\"X-Content-Type-Options\", \"nosniff\"))\n\t\t\t.andExpect(header().string(\"X-Frame-Options\", \"DENY\"))\n\t\t\t.andExpect(header().string(\"Strict-Transport-Security\", \"max-age=31536000 ; includeSubDomains\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\"))\n\t\t\t.andExpect(header().string(HttpHeaders.EXPIRES, \"0\"))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, \"no-cache\"))\n\t\t\t.andExpect(header().string(\"X-XSS-Protection\", \"0\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class HeadersAtTheBeginningOfRequestConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.addObjectPostProcessor(new ObjectPostProcessor<HeaderWriterFilter>() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic HeaderWriterFilter postProcess(HeaderWriterFilter filter) {\n\t\t\t\t\t\t\tfilter.setShouldWriteHeadersEagerly(true);\n\t\t\t\t\t\t\treturn filter;\n\t\t\t\t\t\t}\n\t\t\t\t\t}));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@RestController\n\tprivate static class HomeController {\n\n\t\t@GetMapping(\"/\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.net.URI;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport com.google.common.net.HttpHeaders;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;\nimport org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\n\n/**\n * Tests for {@link HeadersConfigurer}.\n *\n * @author Rob Winch\n * @author Tim Ysewyn\n * @author Joe Grandja\n * @author Eddú Meléndez\n * @author Vedran Pavic\n * @author Eleftheria Stein\n * @author Marcus Da Coregio\n * @author Daniel Garnier-Moiroux\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HeadersConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void getWhenHeadersConfiguredThenDefaultHeadersInResponse() throws Exception {\n\t\tthis.spring.register(HeadersConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.X_CONTENT_TYPE_OPTIONS, \"nosniff\"))\n\t\t\t.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS, XFrameOptionsMode.DENY.name()))\n\t\t\t.andExpect(header().string(HttpHeaders.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\"))\n\t\t\t.andExpect(header().string(HttpHeaders.EXPIRES, \"0\"))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, \"no-cache\"))\n\t\t\t.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, \"0\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(\n\t\t\t\tHttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,\n\t\t\t\tHttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);\n\t}\n\n\t@Test\n\tpublic void getWhenHeadersConfiguredInLambdaThenDefaultHeadersInResponse() throws Exception {\n\t\tthis.spring.register(HeadersInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.X_CONTENT_TYPE_OPTIONS, \"nosniff\"))\n\t\t\t.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS, XFrameOptionsMode.DENY.name()))\n\t\t\t.andExpect(header().string(HttpHeaders.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\"))\n\t\t\t.andExpect(header().string(HttpHeaders.EXPIRES, \"0\"))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, \"no-cache\"))\n\t\t\t.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, \"0\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(\n\t\t\t\tHttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,\n\t\t\t\tHttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);\n\t}\n\n\t@Test\n\tpublic void getWhenHeaderDefaultsDisabledAndContentTypeConfiguredThenOnlyContentTypeHeaderInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ContentTypeOptionsConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\"))\n\t\t\t.andExpect(header().string(HttpHeaders.X_CONTENT_TYPE_OPTIONS, \"nosniff\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_CONTENT_TYPE_OPTIONS);\n\t}\n\n\t@Test\n\tpublic void getWhenOnlyContentTypeConfiguredInLambdaThenOnlyContentTypeHeaderInResponse() throws Exception {\n\t\tthis.spring.register(ContentTypeOptionsInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\"))\n\t\t\t.andExpect(header().string(HttpHeaders.X_CONTENT_TYPE_OPTIONS, \"nosniff\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_CONTENT_TYPE_OPTIONS);\n\t}\n\n\t@Test\n\tpublic void getWhenHeaderDefaultsDisabledAndFrameOptionsConfiguredThenOnlyFrameOptionsHeaderInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(FrameOptionsConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\"))\n\t\t\t.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS, XFrameOptionsMode.DENY.name()))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_FRAME_OPTIONS);\n\t}\n\n\t@Test\n\tpublic void getWhenHeaderDefaultsDisabledAndHstsConfiguredThenOnlyStrictTransportSecurityHeaderInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HstsConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY);\n\t}\n\n\t@Test\n\tpublic void getWhenHeaderDefaultsDisabledAndCacheControlConfiguredThenCacheControlAndExpiresAndPragmaHeadersInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CacheControlConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\"))\n\t\t\t.andExpect(header().string(HttpHeaders.EXPIRES, \"0\"))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, \"no-cache\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(HttpHeaders.CACHE_CONTROL,\n\t\t\t\tHttpHeaders.EXPIRES, HttpHeaders.PRAGMA);\n\t}\n\n\t@Test\n\tpublic void getWhenOnlyCacheControlConfiguredInLambdaThenCacheControlAndExpiresAndPragmaHeadersInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CacheControlInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\"))\n\t\t\t.andExpect(header().string(HttpHeaders.EXPIRES, \"0\"))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, \"no-cache\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(HttpHeaders.CACHE_CONTROL,\n\t\t\t\tHttpHeaders.EXPIRES, HttpHeaders.PRAGMA);\n\t}\n\n\t@Test\n\tpublic void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredThenOnlyXssProtectionHeaderInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(XssProtectionConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, \"0\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);\n\t}\n\n\t@Test\n\tpublic void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredEnabledModeBlockThenOnlyXssProtectionHeaderInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(XssProtectionValueEnabledModeBlockConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, \"1; mode=block\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);\n\t}\n\n\t@Test\n\tpublic void getWhenOnlyXssProtectionConfiguredInLambdaThenOnlyXssProtectionHeaderInResponse() throws Exception {\n\t\tthis.spring.register(XssProtectionInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, \"0\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);\n\t}\n\n\t@Test\n\tpublic void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueEnabledModeBlockInLambdaThenOnlyXssProtectionHeaderInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(XssProtectionValueEnabledModeBlockInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, \"1; mode=block\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION);\n\t}\n\n\t@Test\n\tpublic void getWhenFrameOptionsSameOriginConfiguredThenFrameOptionsHeaderHasValueSameOrigin() throws Exception {\n\t\tthis.spring.register(HeadersCustomSameOriginConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS, XFrameOptionsMode.SAMEORIGIN.name()))\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void getWhenFrameOptionsSameOriginConfiguredInLambdaThenFrameOptionsHeaderHasValueSameOrigin()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HeadersCustomSameOriginInLambdaConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS, XFrameOptionsMode.SAMEORIGIN.name()))\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void getWhenHeaderDefaultsDisabledAndPublicHpkpWithNoPinThenNoHeadersInResponse() throws Exception {\n\t\tthis.spring.register(HpkpConfigNoPins.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\")).andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getWhenSecureRequestAndHpkpWithPinThenPublicKeyPinsReportOnlyHeaderInResponse() throws Exception {\n\t\tthis.spring.register(HpkpConfig.class).autowire();\n\t\tResultMatcher pinsReportOnly = header().string(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY,\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(pinsReportOnly)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureRequestHeaderDefaultsDisabledAndHpkpWithPinThenNoHeadersInResponse() throws Exception {\n\t\tthis.spring.register(HpkpConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\")).andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getWhenHpkpWithMultiplePinsThenPublicKeyPinsReportOnlyHeaderWithMultiplePinsInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HpkpConfigWithPins.class).autowire();\n\t\tResultMatcher pinsReportOnly = header().string(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY,\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; pin-sha256=\\\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\\\"\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(pinsReportOnly)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void getWhenHpkpWithCustomAgeThenPublicKeyPinsReportOnlyHeaderWithCustomAgeInResponse() throws Exception {\n\t\tthis.spring.register(HpkpConfigCustomAge.class).autowire();\n\t\tResultMatcher pinsReportOnly = header().string(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY,\n\t\t\t\t\"max-age=604800 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(pinsReportOnly)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void getWhenHpkpWithReportOnlyFalseThenPublicKeyPinsHeaderInResponse() throws Exception {\n\t\tthis.spring.register(HpkpConfigTerminateConnection.class).autowire();\n\t\tResultMatcher pins = header().string(HttpHeaders.PUBLIC_KEY_PINS,\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(pins)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS);\n\t}\n\n\t@Test\n\tpublic void getWhenHpkpIncludeSubdomainThenPublicKeyPinsReportOnlyHeaderWithIncludeSubDomainsInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HpkpConfigIncludeSubDomains.class).autowire();\n\t\tResultMatcher pinsReportOnly = header().string(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY,\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; includeSubDomains\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(pinsReportOnly)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void getWhenHpkpWithReportUriThenPublicKeyPinsReportOnlyHeaderWithReportUriInResponse() throws Exception {\n\t\tthis.spring.register(HpkpConfigWithReportURI.class).autowire();\n\t\tResultMatcher pinsReportOnly = header().string(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY,\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; report-uri=\\\"https://example.net/pkp-report\\\"\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(pinsReportOnly)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void getWhenHpkpWithReportUriAsStringThenPublicKeyPinsReportOnlyHeaderWithReportUriInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HpkpConfigWithReportURIAsString.class).autowire();\n\t\tResultMatcher pinsReportOnly = header().string(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY,\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; report-uri=\\\"https://example.net/pkp-report\\\"\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(pinsReportOnly)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void getWhenHpkpWithReportUriInLambdaThenPublicKeyPinsReportOnlyHeaderWithReportUriInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HpkpWithReportUriInLambdaConfig.class).autowire();\n\t\tResultMatcher pinsReportOnly = header().string(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY,\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; report-uri=\\\"https://example.net/pkp-report\\\"\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(pinsReportOnly)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.PUBLIC_KEY_PINS_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void getWhenContentSecurityPolicyConfiguredThenContentSecurityPolicyHeaderInResponse() throws Exception {\n\t\tthis.spring.register(ContentSecurityPolicyDefaultConfig.class).autowire();\n\t\tResultMatcher csp = header().string(HttpHeaders.CONTENT_SECURITY_POLICY, \"default-src 'self'\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(csp)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CONTENT_SECURITY_POLICY);\n\t}\n\n\t@Test\n\tpublic void getWhenContentSecurityPolicyWithReportOnlyThenContentSecurityPolicyReportOnlyHeaderInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ContentSecurityPolicyReportOnlyConfig.class).autowire();\n\t\tResultMatcher cspReportOnly = header().string(HttpHeaders.CONTENT_SECURITY_POLICY_REPORT_ONLY,\n\t\t\t\t\"default-src 'self'; script-src trustedscripts.example.com\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(cspReportOnly)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames())\n\t\t\t.containsExactly(HttpHeaders.CONTENT_SECURITY_POLICY_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void getWhenContentSecurityPolicyWithReportOnlyInLambdaThenContentSecurityPolicyReportOnlyHeaderInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ContentSecurityPolicyReportOnlyInLambdaConfig.class).autowire();\n\t\tResultMatcher csp = header().string(HttpHeaders.CONTENT_SECURITY_POLICY_REPORT_ONLY,\n\t\t\t\t\"default-src 'self'; script-src trustedscripts.example.com\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(csp)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames())\n\t\t\t.containsExactly(HttpHeaders.CONTENT_SECURITY_POLICY_REPORT_ONLY);\n\t}\n\n\t@Test\n\tpublic void configureWhenContentSecurityPolicyEmptyThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(ContentSecurityPolicyInvalidConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenContentSecurityPolicyEmptyInLambdaThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(ContentSecurityPolicyInvalidInLambdaConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenContentSecurityPolicyNoPolicyDirectivesInLambdaThenDefaultHeaderValue() throws Exception {\n\t\tthis.spring.register(ContentSecurityPolicyNoDirectivesInLambdaConfig.class).autowire();\n\t\tResultMatcher csp = header().string(HttpHeaders.CONTENT_SECURITY_POLICY, \"default-src 'self'\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(csp)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CONTENT_SECURITY_POLICY);\n\t}\n\n\t@Test\n\tpublic void getWhenReferrerPolicyConfiguredThenReferrerPolicyHeaderInResponse() throws Exception {\n\t\tthis.spring.register(ReferrerPolicyDefaultConfig.class).autowire();\n\t\tResultMatcher referrerPolicy = header().string(\"Referrer-Policy\", ReferrerPolicy.NO_REFERRER.getPolicy());\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(referrerPolicy)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(\"Referrer-Policy\");\n\t}\n\n\t@Test\n\tpublic void getWhenReferrerPolicyInLambdaThenReferrerPolicyHeaderInResponse() throws Exception {\n\t\tthis.spring.register(ReferrerPolicyDefaultInLambdaConfig.class).autowire();\n\t\tResultMatcher referrerPolicy = header().string(\"Referrer-Policy\", ReferrerPolicy.NO_REFERRER.getPolicy());\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(referrerPolicy)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(\"Referrer-Policy\");\n\t}\n\n\t@Test\n\tpublic void getWhenReferrerPolicyConfiguredWithCustomValueThenReferrerPolicyHeaderWithCustomValueInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ReferrerPolicyCustomConfig.class).autowire();\n\t\tResultMatcher referrerPolicy = header().string(\"Referrer-Policy\", ReferrerPolicy.SAME_ORIGIN.getPolicy());\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(referrerPolicy)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(\"Referrer-Policy\");\n\t}\n\n\t@Test\n\tpublic void getWhenReferrerPolicyConfiguredWithCustomValueInLambdaThenCustomValueInResponse() throws Exception {\n\t\tthis.spring.register(ReferrerPolicyCustomInLambdaConfig.class).autowire();\n\t\tResultMatcher referrerPolicy = header().string(\"Referrer-Policy\", ReferrerPolicy.SAME_ORIGIN.getPolicy());\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(referrerPolicy)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(\"Referrer-Policy\");\n\t}\n\n\t@Test\n\tpublic void getWhenFeaturePolicyConfiguredThenFeaturePolicyHeaderInResponse() throws Exception {\n\t\tthis.spring.register(FeaturePolicyConfig.class).autowire();\n\t\tResultMatcher featurePolicy = header().string(\"Feature-Policy\", \"geolocation 'self'\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(featurePolicy)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(\"Feature-Policy\");\n\t}\n\n\t@Test\n\tpublic void configureWhenFeaturePolicyEmptyThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(FeaturePolicyInvalidConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void getWhenPermissionsPolicyConfiguredThenPermissionsPolicyHeaderInResponse() throws Exception {\n\t\tthis.spring.register(PermissionsPolicyConfig.class).autowire();\n\t\tResultMatcher permissionsPolicy = header().string(\"Permissions-Policy\", \"geolocation=(self)\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(permissionsPolicy)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(\"Permissions-Policy\");\n\t}\n\n\t@Test\n\tpublic void getWhenPermissionsPolicyConfiguredWithStringThenPermissionsPolicyHeaderInResponse() throws Exception {\n\t\tthis.spring.register(PermissionsPolicyStringConfig.class).autowire();\n\t\tResultMatcher permissionsPolicy = header().string(\"Permissions-Policy\", \"geolocation=(self)\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(permissionsPolicy)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(\"Permissions-Policy\");\n\t}\n\n\t@Test\n\tpublic void configureWhenPermissionsPolicyEmptyThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(PermissionsPolicyInvalidConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenPermissionsPolicyStringEmptyThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(PermissionsPolicyInvalidStringConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void getWhenHstsConfiguredWithPreloadThenStrictTransportSecurityHeaderWithPreloadInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HstsWithPreloadConfig.class).autowire();\n\t\tResultMatcher hsts = header().string(HttpHeaders.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=31536000 ; includeSubDomains ; preload\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(hsts)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY);\n\t}\n\n\t@Test\n\tpublic void getWhenHstsConfiguredWithPreloadInLambdaThenStrictTransportSecurityHeaderWithPreloadInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HstsWithPreloadInLambdaConfig.class).autowire();\n\t\tResultMatcher hsts = header().string(HttpHeaders.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=31536000 ; includeSubDomains ; preload\");\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(hsts)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomCrossOriginPoliciesInLambdaThenCrossOriginPolicyHeadersWithCustomValuesInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CrossOriginCustomPoliciesInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY, \"same-origin\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, \"require-corp\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY, \"same-origin\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY,\n\t\t\t\tHttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY);\n\t}\n\n\t@Test\n\tpublic void getWhenCustomCrossOriginPoliciesThenCrossOriginPolicyHeadersWithCustomValuesInResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CrossOriginCustomPoliciesConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY, \"same-origin\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, \"require-corp\"))\n\t\t\t.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY, \"same-origin\"))\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY,\n\t\t\t\tHttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HeadersConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HeadersInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentTypeOptionsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.contentTypeOptions(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentTypeOptionsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.contentTypeOptions(withDefaults())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FrameOptionsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.frameOptions(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HstsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpStrictTransportSecurity(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CacheControlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.cacheControl(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CacheControlInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.cacheControl(withDefaults())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class XssProtectionConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.xssProtection(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class XssProtectionValueEnabledModeBlockConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.xssProtection((xss) -> xss\n\t\t\t\t\t\t.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\tstatic class XssProtectionInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.xssProtection(withDefaults())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class XssProtectionValueEnabledModeBlockInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.xssProtection((xXssConfig) -> xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\tstatic class HeadersCustomSameOriginConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.frameOptions((frameOptions) -> frameOptions.sameOrigin()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HeadersCustomSameOriginInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.frameOptions((frameOptionsConfig) -> frameOptionsConfig.sameOrigin())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpConfigNoPins {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpPublicKeyPinning(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp\n\t\t\t\t\t\t.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpConfigWithPins {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tMap<String, String> pins = new LinkedHashMap<>();\n\t\t\tpins.put(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\");\n\t\t\tpins.put(\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\", \"sha256\");\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp.withPins(pins)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpConfigCustomAge {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp\n\t\t\t\t\t\t.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\")\n\t\t\t\t\t\t.maxAgeInSeconds(604800)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpConfigTerminateConnection {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp\n\t\t\t\t\t\t.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\")\n\t\t\t\t\t\t.reportOnly(false)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpConfigIncludeSubDomains {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp\n\t\t\t\t\t\t.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\")\n\t\t\t\t\t\t.includeSubDomains(true)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpConfigWithReportURI {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp\n\t\t\t\t\t\t.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\")\n\t\t\t\t\t\t.reportUri(URI.create(\"https://example.net/pkp-report\"))));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpConfigWithReportURIAsString {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp\n\t\t\t\t\t\t.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\")\n\t\t\t\t\t\t.reportUri(\"https://example.net/pkp-report\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HpkpWithReportUriInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp\n\t\t\t\t\t\t\t\t.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\")\n\t\t\t\t\t\t\t\t.reportUri(\"https://example.net/pkp-report\")\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentSecurityPolicyDefaultConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.contentSecurityPolicy((csp) -> csp.policyDirectives(\"default-src 'self'\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentSecurityPolicyReportOnlyConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t\t\t.policyDirectives(\"default-src 'self'; script-src trustedscripts.example.com\")\n\t\t\t\t\t\t.reportOnly()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentSecurityPolicyReportOnlyInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t\t\t\t\t.policyDirectives(\"default-src 'self'; script-src trustedscripts.example.com\")\n\t\t\t\t\t\t\t\t.reportOnly()\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentSecurityPolicyInvalidConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.contentSecurityPolicy((csp) -> csp.policyDirectives(\"\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentSecurityPolicyInvalidInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.contentSecurityPolicy((csp) -> csp.policyDirectives(\"\")\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentSecurityPolicyNoDirectivesInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.contentSecurityPolicy(withDefaults())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ReferrerPolicyDefaultConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.referrerPolicy(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ReferrerPolicyDefaultInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.referrerPolicy(Customizer.withDefaults())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ReferrerPolicyCustomConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.referrerPolicy((referrer) -> referrer.policy(ReferrerPolicy.SAME_ORIGIN)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ReferrerPolicyCustomInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.referrerPolicy((referrerPolicy) -> referrerPolicy.policy(ReferrerPolicy.SAME_ORIGIN)\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FeaturePolicyConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.featurePolicy(\"geolocation 'self'\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FeaturePolicyInvalidConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.featurePolicy(\"\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@SuppressWarnings(\"removal\")\n\tstatic class PermissionsPolicyConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.permissionsPolicy((permissionsPolicy) -> permissionsPolicy.policy(\"geolocation=(self)\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PermissionsPolicyStringConfig {\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.permissionsPolicy((permissions) -> permissions.policy(\"geolocation=(self)\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@SuppressWarnings(\"removal\")\n\tstatic class PermissionsPolicyInvalidConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.permissionsPolicy((permissionsPolicy) -> permissionsPolicy.policy(null)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@SuppressWarnings(\"removal\")\n\tstatic class PermissionsPolicyInvalidStringConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.permissionsPolicy((permissions) -> permissions.policy(\"\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HstsWithPreloadConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpStrictTransportSecurity((hsts) -> hsts.preload(true)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HstsWithPreloadInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t\t.httpStrictTransportSecurity((hstsConfig) -> hstsConfig.preload(true))\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CrossOriginCustomPoliciesInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.crossOriginOpenerPolicy((policy) -> policy\n\t\t\t\t\t\t.policy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN)\n\t\t\t\t\t)\n\t\t\t\t\t.crossOriginEmbedderPolicy((policy) -> policy\n\t\t\t\t\t\t.policy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)\n\t\t\t\t\t)\n\t\t\t\t\t.crossOriginResourcePolicy((policy) -> policy\n\t\t\t\t\t\t.policy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN)\n\t\t\t\t\t)\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CrossOriginCustomPoliciesConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp.headers((headers) -> headers\n\t\t\t\t.defaultsDisabled()\n\t\t\t\t.crossOriginOpenerPolicy((opener) -> opener\n\t\t\t\t\t.policy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN))\n\t\t\t\t.crossOriginEmbedderPolicy((embedder) -> embedder\n\t\t\t\t\t.policy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP))\n\t\t\t\t.crossOriginResourcePolicy((resource) -> resource\n\t\t\t\t\t.policy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN)));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.ObservationTextPublisher;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationObservationContext;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link HttpBasicConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpBasicConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnBasicAuthenticationFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(BasicAuthenticationFilter.class));\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenUsingDefaultsInLambdaThenResponseIncludesBasicChallenge() throws Exception {\n\t\tthis.spring.register(DefaultsLambdaEntryPointConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(\"WWW-Authenticate\", \"Basic realm=\\\"Realm\\\", charset=\\\"UTF-8\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t// SEC-2198\n\t@Test\n\tpublic void httpBasicWhenUsingDefaultsThenResponseIncludesBasicChallenge() throws Exception {\n\t\tthis.spring.register(DefaultsEntryPointConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(\"WWW-Authenticate\", \"Basic realm=\\\"Realm\\\", charset=\\\"UTF-8\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenUsingCustomAuthenticationEntryPointThenResponseIncludesBasicChallenge() throws Exception {\n\t\tCustomAuthenticationEntryPointConfig.ENTRY_POINT = mock(AuthenticationEntryPoint.class);\n\t\tthis.spring.register(CustomAuthenticationEntryPointConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(CustomAuthenticationEntryPointConfig.ENTRY_POINT).commence(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(AuthenticationException.class));\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenInvokedTwiceThenUsesOriginalEntryPoint() throws Exception {\n\t\tthis.spring.register(DuplicateDoesNotOverrideConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(DuplicateDoesNotOverrideConfig.ENTRY_POINT).commence(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(AuthenticationException.class));\n\t}\n\n\t// SEC-3019\n\t@Test\n\tpublic void httpBasicWhenRememberMeConfiguredThenSetsRememberMeCookie() throws Exception {\n\t\tthis.spring.register(BasicUsesRememberMeConfig.class, Home.class).autowire();\n\t\tMockHttpServletRequestBuilder rememberMeRequest = get(\"/\").with(httpBasic(\"user\", \"password\"))\n\t\t\t.param(\"remember-me\", \"true\");\n\t\tthis.mvc.perform(rememberMeRequest).andExpect(cookie().exists(\"remember-me\"));\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenDefaultsThenAcceptsBasicCredentials() throws Exception {\n\t\tthis.spring.register(HttpBasic.class, Users.class, Home.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"user\"));\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.register(HttpBasic.class, Users.class, Home.class, SecurityContextChangedListenerConfig.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"user\"));\n\t\tSecurityContextChangedListener listener = this.spring.getContext()\n\t\t\t.getBean(SecurityContextChangedListener.class);\n\t\tverify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenUsingCustomSecurityContextRepositoryThenUses() throws Exception {\n\t\tthis.spring.register(CustomSecurityContextRepositoryConfig.class, Users.class, Home.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"user\"));\n\t\tverify(CustomSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY)\n\t\t\t.saveContext(any(SecurityContext.class), any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenObservationRegistryThenObserves() throws Exception {\n\t\tthis.spring.register(HttpBasic.class, Users.class, Home.class, ObservationRegistryConfig.class).autowire();\n\t\tObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"user\"));\n\t\tArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, atLeastOnce()).onStart(context.capture());\n\t\tassertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthenticationObservationContext);\n\t\tverify(handler, atLeastOnce()).onStop(context.capture());\n\t\tassertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthenticationObservationContext);\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"wrong\"))).andExpect(status().isUnauthorized());\n\t\tverify(handler).onError(context.capture());\n\t\tassertThat(context.getValue()).isInstanceOf(AuthenticationObservationContext.class);\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenExcludeAuthenticationObservationsThenUnobserved() throws Exception {\n\t\tthis.spring\n\t\t\t.register(HttpBasic.class, Users.class, Home.class, ObservationRegistryConfig.class,\n\t\t\t\t\tSelectableObservationsConfig.class)\n\t\t\t.autowire();\n\t\tObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"user\"));\n\t\tArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, atLeastOnce()).onStart(context.capture());\n\t\tassertThat(context.getAllValues()).noneMatch((c) -> c instanceof AuthenticationObservationContext);\n\t\tcontext = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, atLeastOnce()).onStop(context.capture());\n\t\tassertThat(context.getAllValues()).noneMatch((c) -> c instanceof AuthenticationObservationContext);\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"wrong\"))).andExpect(status().isUnauthorized());\n\t\tverify(handler, never()).onError(any());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultsLambdaEntryPointConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultsEntryPointConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthenticationEntryPointConfig {\n\n\t\tstatic AuthenticationEntryPoint ENTRY_POINT;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic((basic) -> basic\n\t\t\t\t\t.authenticationEntryPoint(ENTRY_POINT));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DuplicateDoesNotOverrideConfig {\n\n\t\tstatic AuthenticationEntryPoint ENTRY_POINT = mock(AuthenticationEntryPoint.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic((basic) -> basic\n\t\t\t\t\t.authenticationEntryPoint(ENTRY_POINT))\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class BasicUsesRememberMeConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\t\torg.springframework.security.core.userdetails.User.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t\t\t.build()\n\t\t\t\t\t// @formatter:on\n\t\t\t);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpBasic {\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.httpBasic(Customizer.withDefaults());\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomSecurityContextRepositoryConfig {\n\n\t\tstatic final SecurityContextRepository SECURITY_CONTEXT_REPOSITORY = mock(SecurityContextRepository.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic((basic) -> basic\n\t\t\t\t\t.securityContextRepository(SECURITY_CONTEXT_REPOSITORY));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class Users {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t\t\t.build()\n\t\t\t\t\t// @formatter:on\n\t\t\t);\n\t\t}\n\n\t}\n\n\t@EnableWebMvc\n\t@RestController\n\tstatic class Home {\n\n\t\t@GetMapping(\"/\")\n\t\tString home(@AuthenticationPrincipal UserDetails user) {\n\t\t\treturn user.getUsername();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ObservationRegistryConfig {\n\n\t\tprivate final ObservationRegistry registry = ObservationRegistry.create();\n\n\t\tprivate final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\treturn this.registry;\n\t\t}\n\n\t\t@Bean\n\t\tObservationHandler<Observation.Context> observationHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t\t@Bean\n\t\tObservationRegistryPostProcessor observationRegistryPostProcessor(\n\t\t\t\tObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\treturn new ObservationRegistryPostProcessor(handler);\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryPostProcessor implements BeanPostProcessor {\n\n\t\tprivate final ObjectProvider<ObservationHandler<Observation.Context>> handler;\n\n\t\tObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\tthis.handler = handler;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tif (bean instanceof ObservationRegistry registry) {\n\t\t\t\tregistry.observationConfig().observationHandler(this.handler.getObject());\n\t\t\t}\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SelectableObservationsConfig {\n\n\t\t@Bean\n\t\tSecurityObservationSettings observabilityDefaults() {\n\t\t\treturn SecurityObservationSettings.withDefaults().shouldObserveAuthentications(false).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecurityLogoutTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * @author Rob Winch\n *\n */\npublic class HttpSecurityLogoutTests {\n\n\tAnnotationConfigWebApplicationContext context;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t// SEC-2848\n\t@Test\n\tpublic void clearAuthenticationFalse() throws Exception {\n\t\tloadConfig(ClearAuthenticationFalseConfig.class);\n\t\tSecurityContext currentContext = SecurityContextHolder.createEmptyContext();\n\t\tcurrentContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMockHttpServletRequest request = post(\"/logout\").build();\n\t\trequest.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, currentContext);\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(currentContext.getAuthentication()).isNotNull();\n\t}\n\n\tpublic void loadConfig(Class<?>... configs) {\n\t\tthis.context = new AnnotationConfigWebApplicationContext();\n\t\tthis.context.register(configs);\n\t\tthis.context.refresh();\n\t\tthis.context.getAutowireCapableBeanFactory().autowireBean(this);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class ClearAuthenticationFalseConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.clearAuthentication(false));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecurityObservationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Iterator;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpSecurityObservationTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void getWhenUsingObservationRegistryThenObservesRequest() throws Exception {\n\t\tthis.spring.register(ObservationRegistryConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(5)).onStart(captor.capture());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"security filterchain before\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authentications\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authorizations\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.http.secured.requests\");\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"security filterchain after\");\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class ObservationRegistryConfig {\n\n\t\tprivate ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain app(HttpSecurity http) throws Exception {\n\t\t\thttp.httpBasic(withDefaults()).authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\t\tUser.withDefaultPasswordEncoder().username(\"user\").password(\"password\").authorities(\"app\").build());\n\t\t}\n\n\t\t@Bean\n\t\tObservationHandler<Observation.Context> observationHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\t\tregistry.observationConfig().observationHandler(this.handler);\n\t\t\treturn registry;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecurityRequestMatchersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * @author Rob Winch\n *\n */\npublic class HttpSecurityRequestMatchersTests {\n\n\tAnnotationConfigWebApplicationContext context;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void mvcMatcherGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {\n\t\tloadConfig(MvcMatcherConfig.class);\n\t\tassertThat(this.springSecurityFilterChain.getFilters(\"/path\")).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void requestMatchersMvcMatcherServletPath() throws Exception {\n\t\tloadConfig(RequestMatchersMvcMatcherServeltPathConfig.class);\n\t\tMockHttpServletRequest request = get().requestUri(null, \"/spring\", \"/path\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\trequest = get().requestUri(null, \"\", \"/path\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tsetup();\n\t\trequest = get().requestUri(null, \"/other\", \"/path\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void requestMatcherWhensMvcMatcherServletPathInLambdaThenPathIsSecured() throws Exception {\n\t\tloadConfig(RequestMatchersMvcMatcherServletPathInLambdaConfig.class);\n\t\tMockHttpServletRequest request = get().requestUri(null, \"/spring\", \"/path\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\trequest = get().requestUri(null, \"\", \"/path\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tsetup();\n\t\trequest = get().requestUri(null, \"/other\", \"/path\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void requestMatcherWhenMultiMvcMatcherInLambdaThenAllPathsAreDenied() throws Exception {\n\t\tloadConfig(MultiMvcMatcherInLambdaConfig.class);\n\t\tMockHttpServletRequest request = get(\"/test-1\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\trequest = get(\"/test-2\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\trequest = get(\"/test-3\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void requestMatcherWhenMultiMvcMatcherThenAllPathsAreDenied() throws Exception {\n\t\tloadConfig(MultiMvcMatcherConfig.class);\n\t\tMockHttpServletRequest request = get(\"/test-1\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\trequest = get(\"/test-2\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\trequest = get(\"/test-3\").build();\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t}\n\n\tpublic void loadConfig(Class<?>... configs) {\n\t\tthis.context = new AnnotationConfigWebApplicationContext();\n\t\tthis.context.register(configs);\n\t\tthis.context.setServletContext(new MockServletContext());\n\t\tthis.context.refresh();\n\t\tthis.context.getAutowireCapableBeanFactory().autowireBean(this);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class MultiMvcMatcherInLambdaConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tSecurityFilterChain first(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/test-1\"))\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/test-2\"))\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/test-3\"))\n\t\t\t\t)\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().denyAll())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain second(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/test-1\"))\n\t\t\t\t)\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().permitAll()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping({ \"/test-1\", \"/test-2\", \"/test-3\" })\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class MultiMvcMatcherConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t}\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tSecurityFilterChain first(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/test-1\"))\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/test-2\"))\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/test-3\")))\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain second(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/test-1\")))\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().permitAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping({ \"/test-1\", \"/test-2\", \"/test-3\" })\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class MvcMatcherConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(builder.matcher(\"/path\"))\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class RequestMatchersMvcMatcherConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/path\")))\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class RequestMatchersMvcMatcherInLambdaConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((secure) -> secure\n\t\t\t\t\t\t.requestMatchers(builder.matcher(\"/path\"))\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class RequestMatchersMvcMatcherServeltPathConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(builder.basePath(\"/spring\").matcher(\"/path\"))\n\t\t\t\t\t.requestMatchers(\"/never-match\"))\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class RequestMatchersMvcMatcherServletPathInLambdaConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((secure) -> secure\n\t\t\t\t\t\t.requestMatchers(builder.basePath(\"/spring\").matcher(\"/path\"))\n\t\t\t\t\t\t.requestMatchers(\"/never-match\")\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersNoMvcTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.test.support.ClassPathExclusions;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Marcus Da Coregio\n *\n */\n@ClassPathExclusions(\"spring-webmvc-*.jar\")\npublic class HttpSecuritySecurityMatchersNoMvcTests {\n\n\tAnnotationConfigWebApplicationContext context;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.request.setMethod(\"GET\");\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void securityMatcherWhenNoMvcThenAntMatcher() throws Exception {\n\t\tloadConfig(SecurityMatcherNoMvcConfig.class);\n\t\tthis.request.setRequestURI(\"/path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\tthis.request.setRequestURI(\"/path.html\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tsetup();\n\t\tthis.request.setRequestURI(\"/path/\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tList<RequestMatcher> requestMatchers = this.springSecurityFilterChain.getFilterChains()\n\t\t\t.stream()\n\t\t\t.map((chain) -> ((DefaultSecurityFilterChain) chain).getRequestMatcher())\n\t\t\t.map((matcher) -> ReflectionTestUtils.getField(matcher, \"requestMatchers\"))\n\t\t\t.map((matchers) -> (List<RequestMatcher>) matchers)\n\t\t\t.findFirst()\n\t\t\t.get();\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tassertThat(requestMatchers).hasOnlyElementsOfType(PathPatternRequestMatcher.class);\n\t}\n\n\tpublic void loadConfig(Class<?>... configs) {\n\t\tthis.context = new AnnotationConfigWebApplicationContext();\n\t\tthis.context.register(configs);\n\t\tthis.context.setServletContext(new MockServletContext());\n\t\tthis.context.refresh();\n\t\tthis.context.getAutowireCapableBeanFactory().autowireBean(this);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@Import(HttpSecuritySecurityMatchersTests.UsersConfig.class)\n\tstatic class SecurityMatcherNoMvcConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain appSecurity(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(\"/path\")\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.servlet.MockServletContext;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n *\n */\npublic class HttpSecuritySecurityMatchersTests {\n\n\tAnnotationConfigWebApplicationContext context;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.request = new MockHttpServletRequest(MockServletContext.mvc(), \"GET\", \"\");\n\t\tthis.request.setMethod(\"GET\");\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void securityMatcherWhenMvcMatcherAndGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {\n\t\tloadConfig(SecurityMatcherMvcConfig.class);\n\t\tassertThat(this.springSecurityFilterChain.getFilters(\"/path\")).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void securityMatchersMvcMatcherServletPath() throws Exception {\n\t\tloadConfig(SecurityMatchersMvcMatcherServletPathConfig.class);\n\t\tthis.request.setServletPath(\"/spring\");\n\t\tthis.request.setRequestURI(\"/spring/path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\tthis.request.setServletPath(\"\");\n\t\tthis.request.setRequestURI(\"/path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tsetup();\n\t\tthis.request.setServletPath(\"/other\");\n\t\tthis.request.setRequestURI(\"/other/path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void securityMatchersWhensMvcMatcherServletPathInLambdaThenPathIsSecured() throws Exception {\n\t\tloadConfig(SecurityMatchersMvcMatcherServletPathInLambdaConfig.class);\n\t\tthis.request.setServletPath(\"/spring\");\n\t\tthis.request.setRequestURI(\"/spring/path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\tthis.request.setServletPath(\"\");\n\t\tthis.request.setRequestURI(\"/path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tsetup();\n\t\tthis.request.setServletPath(\"/other\");\n\t\tthis.request.setRequestURI(\"/other/path\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void securityMatchersWhenMultiMvcMatcherInLambdaThenAllPathsAreDenied() throws Exception {\n\t\tloadConfig(MultiMvcMatcherInLambdaConfig.class);\n\t\tthis.request.setRequestURI(\"/test-1\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\tthis.request.setRequestURI(\"/test-2\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\tthis.request.setRequestURI(\"/test-3\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void securityMatchersWhenMultiMvcMatcherThenAllPathsAreDenied() throws Exception {\n\t\tloadConfig(MultiMvcMatcherConfig.class);\n\t\tthis.request.setRequestURI(\"/test-1\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\tthis.request.setRequestURI(\"/test-2\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tsetup();\n\t\tthis.request.setRequestURI(\"/test-3\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t}\n\n\tpublic void loadConfig(Class<?>... configs) {\n\t\tthis.context = new AnnotationConfigWebApplicationContext();\n\t\tthis.context.register(configs);\n\t\tthis.context.setServletContext(MockServletContext.mvc());\n\t\tthis.context.refresh();\n\t\tthis.context.getAutowireCapableBeanFactory().autowireBean(this);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class MultiMvcMatcherInLambdaConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tSecurityFilterChain first(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/test-1\")\n\t\t\t\t\t.requestMatchers(\"/test-2\")\n\t\t\t\t\t.requestMatchers(\"/test-3\")\n\t\t\t\t)\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().denyAll())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain second(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/test-1\")\n\t\t\t\t)\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().permitAll()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping({ \"/test-1\", \"/test-2\", \"/test-3\" })\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class MultiMvcMatcherConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tSecurityFilterChain first(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(\"/test-1\")\n\t\t\t\t\t.requestMatchers(\"/test-2\")\n\t\t\t\t\t.requestMatchers(\"/test-3\"))\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain second(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(\"/test-1\"))\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().permitAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping({ \"/test-1\", \"/test-2\", \"/test-3\" })\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Configuration\n\t@Import(UsersConfig.class)\n\tstatic class SecurityMatcherMvcConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain appSecurity(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(\"/path\")\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\t@Import(UsersConfig.class)\n\tstatic class SecurityMatchersMvcMatcherConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain appSecurity(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(\"/path\")\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class SecurityMatchersMvcMatcherInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain appSecurity(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((matchers) -> matchers\n\t\t\t\t\t.requestMatchers(\"/path\")\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\t@Import(UsersConfig.class)\n\tstatic class SecurityMatchersMvcMatcherServletPathConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\tPathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t\tbean.setBasePath(\"/spring\");\n\t\t\treturn bean;\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain appSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/path\"))\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/never-match\"))\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\t@Import(UsersConfig.class)\n\tstatic class SecurityMatchersMvcMatcherServletPathInLambdaConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\tPathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t\tbean.setBasePath(\"/spring\");\n\t\t\treturn bean;\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain appSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((matchers) -> matchers\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/path\"))\n\t\t\t\t\t.requestMatchers(builder.matcher(\"/never-match\"))\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PathController {\n\n\t\t\t@RequestMapping(\"/path\")\n\t\t\tString path() {\n\t\t\t\treturn \"path\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UsersConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpsRedirectConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link HttpsRedirectConfigurerTests}\n *\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpsRedirectConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void getWhenSecureThenDoesNotRedirect() throws Exception {\n\t\tthis.spring.register(RedirectToHttpConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"https://localhost\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureThenRespondsWithRedirectToSecure() throws Exception {\n\t\tthis.spring.register(RedirectToHttpConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"http://localhost\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureAndPathRequiresTransportSecurityThenRedirects() throws Exception {\n\t\tthis.spring.register(SometimesRedirectToHttpsConfig.class, UsePathPatternConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"http://localhost:8080\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"http://localhost:8080/secure\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost:8443/secure\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureAndUsingCustomPortMapperThenRespondsWithRedirectToSecurePort() throws Exception {\n\t\tthis.spring.register(RedirectToHttpsViaCustomPortsConfig.class).autowire();\n\t\tPortMapper portMapper = this.spring.getContext().getBean(PortMapper.class);\n\t\tgiven(portMapper.lookupHttpsPort(4080)).willReturn(4443);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"http://localhost:4080\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost:4443\"));\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class RedirectToHttpConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.redirectToHttps(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class SometimesRedirectToHttpsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain springSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder path) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.redirectToHttps((https) -> https.requestMatchers(path.matcher(\"/secure\")));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class RedirectToHttpsViaCustomPortsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.portMapper((p) -> p.portMapper(portMapper()))\n\t\t\t\t.redirectToHttps(withDefaults());\n\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tPortMapper portMapper() {\n\t\t\treturn mock(PortMapper.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UsePathPatternConfig {\n\n\t\t@Bean\n\t\tPathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {\n\t\t\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/JeeConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.security.Principal;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\nimport org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n * Tests for {@link JeeConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class JeeConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnJ2eePreAuthenticatedProcessingFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);\n\t\tverify(objectPostProcessor).postProcess(any(J2eePreAuthenticatedProcessingFilter.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);\n\t\tverify(objectPostProcessor).postProcess(any(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class));\n\t}\n\n\t@Test\n\tpublic void jeeWhenInvokedTwiceThenUsesOriginalMappableRoles() throws Exception {\n\t\tthis.spring.register(InvokeTwiceDoesNotOverride.class).autowire();\n\t\tPrincipal user = mock(Principal.class);\n\t\tgiven(user.getName()).willReturn(\"user\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder authRequest = get(\"/\")\n\t\t\t\t.principal(user)\n\t\t\t\t.with((request) -> {\n\t\t\t\t\trequest.addUserRole(\"ROLE_ADMIN\");\n\t\t\t\t\trequest.addUserRole(\"ROLE_USER\");\n\t\t\t\t\treturn request;\n\t\t\t\t});\n\t\t// @formatter:on\n\t\tthis.mvc.perform(authRequest).andExpect(authenticated().withRoles(\"USER\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenJeeMappableRolesInLambdaThenAuthenticatedWithMappableRoles() throws Exception {\n\t\tthis.spring.register(JeeMappableRolesConfig.class).autowire();\n\t\tPrincipal user = mock(Principal.class);\n\t\tgiven(user.getName()).willReturn(\"user\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder authRequest = get(\"/\")\n\t\t\t\t.principal(user)\n\t\t\t\t.with((request) -> {\n\t\t\t\t\trequest.addUserRole(\"ROLE_ADMIN\");\n\t\t\t\t\trequest.addUserRole(\"ROLE_USER\");\n\t\t\t\t\treturn request;\n\t\t\t\t});\n\t\t// @formatter:on\n\t\tthis.mvc.perform(authRequest).andExpect(authenticated().withRoles(\"USER\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenJeeMappableAuthoritiesInLambdaThenAuthenticatedWithMappableAuthorities() throws Exception {\n\t\tthis.spring.register(JeeMappableAuthoritiesConfig.class).autowire();\n\t\tPrincipal user = mock(Principal.class);\n\t\tgiven(user.getName()).willReturn(\"user\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder authRequest = get(\"/\")\n\t\t\t\t.principal(user)\n\t\t\t\t.with((request) -> {\n\t\t\t\t\trequest.addUserRole(\"ROLE_ADMIN\");\n\t\t\t\t\trequest.addUserRole(\"ROLE_USER\");\n\t\t\t\t\treturn request;\n\t\t\t\t});\n\t\t// @formatter:on\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher authenticatedAsUser = authenticated()\n\t\t\t.withAuthorities(AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.mvc.perform(authRequest).andExpect(authenticatedAsUser);\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthenticatedUserDetailsServiceInLambdaThenCustomAuthenticatedUserDetailsServiceUsed()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(JeeCustomAuthenticatedUserDetailsServiceConfig.class).autowire();\n\t\tAuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> userDetailsService = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(AuthenticationUserDetailsService.class);\n\t\tPrincipal user = mock(Principal.class);\n\t\tUser userDetails = new User(\"user\", \"N/A\", true, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tgiven(user.getName()).willReturn(\"user\");\n\t\tgiven(userDetailsService.loadUserDetails(any())).willReturn(userDetails);\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder authRequest = get(\"/\")\n\t\t\t\t.principal(user)\n\t\t\t\t.with((request) -> {\n\t\t\t\t\trequest.addUserRole(\"ROLE_ADMIN\");\n\t\t\t\t\trequest.addUserRole(\"ROLE_USER\");\n\t\t\t\t\treturn request;\n\t\t\t\t});\n\t\t// @formatter:on\n\t\tthis.mvc.perform(authRequest).andExpect(authenticated().withRoles(\"USER\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.jee(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\t@Primary\n\t\tObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn this.objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvokeTwiceDoesNotOverride {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.jee((jee) -> jee\n\t\t\t\t\t.mappableRoles(\"USER\"))\n\t\t\t\t.jee(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class JeeMappableRolesConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.jee((jee) -> jee\n\t\t\t\t\t\t.mappableRoles(\"USER\")\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class JeeMappableAuthoritiesConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.jee((jee) -> jee\n\t\t\t\t\t\t.mappableAuthorities(\"ROLE_USER\")\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class JeeCustomAuthenticatedUserDetailsServiceConfig {\n\n\t\tprivate AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService = mock(\n\t\t\t\tAuthenticationUserDetailsService.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.jee((jee) -> jee\n\t\t\t\t\t\t.authenticatedUserDetailsService(this.authenticationUserDetailsService)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService() {\n\t\t\treturn this.authenticationUserDetailsService;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerClearSiteDataTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.logout.HeaderWriterLogoutHandler;\nimport org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter;\nimport org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter.Directive;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\n\n/**\n *\n * Tests for {@link HeaderWriterLogoutHandler} that passing\n * {@link ClearSiteDataHeaderWriter} implementation.\n *\n * @author Rafiullah Hamedy\n *\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class LogoutConfigurerClearSiteDataTests {\n\n\tprivate static final String CLEAR_SITE_DATA_HEADER = \"Clear-Site-Data\";\n\n\tprivate static final Directive[] SOURCE = { Directive.CACHE, Directive.COOKIES, Directive.STORAGE,\n\t\t\tDirective.EXECUTION_CONTEXTS };\n\n\tprivate static final String HEADER_VALUE = \"\\\"cache\\\", \\\"cookies\\\", \\\"storage\\\", \\\"executionContexts\\\"\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenRequestTypeGetThenHeaderNotPresentt() throws Exception {\n\t\tthis.spring.register(HttpLogoutConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder logoutRequest = get(\"/logout\").secure(true).with(csrf());\n\t\tthis.mvc.perform(logoutRequest).andExpect(header().doesNotExist(CLEAR_SITE_DATA_HEADER));\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenRequestTypePostAndNotSecureThenHeaderNotPresent() throws Exception {\n\t\tthis.spring.register(HttpLogoutConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\").with(csrf());\n\t\tthis.mvc.perform(logoutRequest).andExpect(header().doesNotExist(CLEAR_SITE_DATA_HEADER));\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenRequestTypePostAndSecureThenHeaderIsPresent() throws Exception {\n\t\tthis.spring.register(HttpLogoutConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\").secure(true).with(csrf());\n\t\tthis.mvc.perform(logoutRequest).andExpect(header().stringValues(CLEAR_SITE_DATA_HEADER, HEADER_VALUE));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpLogoutConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(SOURCE))));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.http.HttpHeaders;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link LogoutConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class LogoutConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenDefaultLogoutSuccessHandlerForHasNullLogoutHandlerThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullLogoutSuccessHandlerConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenDefaultLogoutSuccessHandlerForHasNullLogoutHandlerInLambdaThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullLogoutSuccessHandlerInLambdaConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenDefaultLogoutSuccessHandlerForHasNullMatcherThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullMatcherConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenDefaultLogoutSuccessHandlerForHasNullMatcherInLambdaThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullMatcherInLambdaConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnLogoutFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext()\n\t\t\t.getBean(ObjectPostProcessorConfig.class).objectPostProcessor;\n\t\tverify(objectPostProcessor).postProcess(any(LogoutFilter.class));\n\t}\n\n\t@Test\n\tpublic void logoutWhenInvokedTwiceThenUsesOriginalLogoutUrl() throws Exception {\n\t\tthis.spring.register(DuplicateDoesNotOverrideConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/custom/logout\").with(csrf());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(logoutRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t// SEC-2311\n\t@Test\n\tpublic void logoutWhenGetRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenPostRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenPutRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(put(\"/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenDeleteRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(delete(\"/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenGetRequestAndCsrfDisabledAndCustomLogoutUrlThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledAndCustomLogoutConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/custom/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenPostRequestAndCsrfDisabledAndCustomLogoutUrlThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledAndCustomLogoutConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/custom/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenPutRequestAndCsrfDisabledAndCustomLogoutUrlThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledAndCustomLogoutConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(put(\"/custom/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenDeleteRequestAndCsrfDisabledAndCustomLogoutUrlThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledAndCustomLogoutConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(delete(\"/custom/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomLogoutUrlInLambdaThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(CsrfDisabledAndCustomLogoutInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/custom/logout\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t// SEC-3170\n\t@Test\n\tpublic void configureWhenLogoutHandlerNullThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullLogoutHandlerConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenLogoutHandlerNullInLambdaThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullLogoutHandlerInLambdaConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t// SEC-3170\n\t@Test\n\tpublic void rememberMeWhenRememberMeServicesNotLogoutHandlerThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(RememberMeNoLogoutHandler.class).autowire();\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t}\n\n\t@Test\n\tpublic void logoutWhenAcceptTextHtmlThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(BasicSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);\n\t\tthis.mvc.perform(logoutRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.register(BasicSecurityConfig.class, SecurityContextChangedListenerConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);\n\t\tthis.mvc.perform(logoutRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t}\n\n\t// gh-3282\n\t@Test\n\tpublic void logoutWhenAcceptApplicationJsonThenReturnsStatusNoContent() throws Exception {\n\t\tthis.spring.register(BasicSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(status().isNoContent());\n\t}\n\n\t// gh-4831\n\t@Test\n\tpublic void logoutWhenAcceptAllThenReturnsStatusNoContent() throws Exception {\n\t\tthis.spring.register(BasicSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.ALL_VALUE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(logoutRequest).andExpect(status().isNoContent());\n\t}\n\n\t// gh-3902\n\t@Test\n\tpublic void logoutWhenAcceptFromChromeThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(BasicSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.header(HttpHeaders.ACCEPT, \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\");\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-3997\n\t@Test\n\tpublic void logoutWhenXMLHttpRequestThenReturnsStatusNoContent() throws Exception {\n\t\tthis.spring.register(BasicSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.header(HttpHeaders.ACCEPT, \"text/html,application/json\")\n\t\t\t\t.header(\"X-Requested-With\", \"XMLHttpRequest\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(status().isNoContent());\n\t}\n\n\t@Test\n\tpublic void logoutWhenDisabledThenLogoutUrlNotFound() throws Exception {\n\t\tthis.spring.register(LogoutDisabledConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf())).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomSecurityContextRepositoryThenUses() throws Exception {\n\t\tCustomSecurityContextRepositoryConfig.repository = mock(SecurityContextRepository.class);\n\t\tthis.spring.register(CustomSecurityContextRepositoryConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);\n\t\tthis.mvc.perform(logoutRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\t// @formatter:on\n\t\tint invocationCount = 2; // 1 from user() post processor and 1 from\n\t\t\t\t\t\t\t\t\t// SecurityContextLogoutHandler\n\t\tverify(CustomSecurityContextRepositoryConfig.repository, times(invocationCount)).saveContext(any(),\n\t\t\t\tany(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void logoutWhenNoSecurityContextRepositoryThenHttpSessionSecurityContextRepository() throws Exception {\n\t\tthis.spring.register(InvalidateHttpSessionFalseConfig.class).autowire();\n\t\tMockHttpSession session = mock(MockHttpSession.class);\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.session(session)\n\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE);\n\t\tthis.mvc.perform(logoutRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tverify(session).removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvalidateHttpSessionFalseConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout.invalidateHttpSession(false))\n\t\t\t\t.securityContext((context) -> context.requireExplicitSave(true));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomSecurityContextRepositoryConfig {\n\n\t\tstatic SecurityContextRepository repository;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout(Customizer.withDefaults())\n\t\t\t\t.securityContext((context) -> context\n\t\t\t\t\t.requireExplicitSave(true)\n\t\t\t\t\t.securityContextRepository(repository)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullLogoutSuccessHandlerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.defaultLogoutSuccessHandlerFor(null, mock(RequestMatcher.class)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullLogoutSuccessHandlerInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout.defaultLogoutSuccessHandlerFor(null, mock(RequestMatcher.class))\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullMatcherConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.defaultLogoutSuccessHandlerFor(mock(LogoutSuccessHandler.class), null));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullMatcherInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout.defaultLogoutSuccessHandlerFor(mock(LogoutSuccessHandler.class), null)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn this.objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DuplicateDoesNotOverrideConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.logoutUrl(\"/custom/logout\"))\n\t\t\t\t.logout(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfDisabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.disable())\n\t\t\t\t.logout(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfDisabledAndCustomLogoutConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.disable())\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.logoutUrl(\"/custom/logout\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfDisabledAndCustomLogoutInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.disable())\n\t\t\t\t.logout((logout) -> logout.logoutUrl(\"/custom/logout\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullLogoutHandlerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.addLogoutHandler(null));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullLogoutHandlerInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout.addLogoutHandler(null));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeNoLogoutHandler {\n\n\t\tstatic RememberMeServices REMEMBER_ME = mock(RememberMeServices.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.rememberMeServices(REMEMBER_ME));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class BasicSecurityConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class LogoutDisabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.disable());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceDebugTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport ch.qos.logback.classic.Level;\nimport ch.qos.logback.classic.Logger;\nimport ch.qos.logback.classic.spi.ILoggingEvent;\nimport ch.qos.logback.core.Appender;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.slf4j.LoggerFactory;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.debug.DebugFilter;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n * Tests to verify {@code EnableWebSecurity(debug)} functionality\n *\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceDebugTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenDebugSetToTrueThenLogsDebugInformation() throws Exception {\n\t\tAppender<ILoggingEvent> appender = mockAppenderFor(\"Spring Security Debugger\");\n\t\tthis.spring.register(DebugWebSecurity.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(appender, atLeastOnce()).doAppend(any(ILoggingEvent.class));\n\t}\n\n\t@Test\n\tpublic void requestWhenDebugSetToFalseThenDoesNotLogDebugInformation() throws Exception {\n\t\tAppender<ILoggingEvent> appender = mockAppenderFor(\"Spring Security Debugger\");\n\t\tthis.spring.register(NoDebugWebSecurity.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tassertThat(filterChainClass()).isNotEqualTo(DebugFilter.class);\n\t\tverify(appender, never()).doAppend(any(ILoggingEvent.class));\n\t}\n\n\tprivate Appender<ILoggingEvent> mockAppenderFor(String name) {\n\t\tAppender<ILoggingEvent> appender = mock(Appender.class);\n\t\tLogger logger = (Logger) LoggerFactory.getLogger(name);\n\t\tlogger.setLevel(Level.DEBUG);\n\t\tlogger.addAppender(appender);\n\t\treturn appender;\n\t}\n\n\tprivate Class<?> filterChainClass() {\n\t\treturn this.spring.getContext().getBean(\"springSecurityFilterChain\").getClass();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity(debug = true)\n\tstatic class DebugWebSecurity {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NoDebugWebSecurity {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpAnonymousTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;anonymous&gt; attributes is present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpAnonymousTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void anonymousRequestWhenUsingDefaultAnonymousConfigurationThenUsesAnonymousAuthentication()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AnonymousConfig.class, AnonymousController.class).autowire();\n\t\tthis.mvc.perform(get(\"/type\")).andExpect(content().string(AnonymousAuthenticationToken.class.getSimpleName()));\n\t}\n\n\t@Test\n\tpublic void anonymousRequestWhenDisablingAnonymousThenDenies() throws Exception {\n\t\tthis.spring.register(AnonymousDisabledConfig.class, AnonymousController.class).autowire();\n\t\tthis.mvc.perform(get(\"/type\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void requestWhenAnonymousThenSendsAnonymousConfiguredAuthorities() throws Exception {\n\t\tthis.spring.register(AnonymousGrantedAuthorityConfig.class, AnonymousController.class).autowire();\n\t\tthis.mvc.perform(get(\"/type\")).andExpect(content().string(AnonymousAuthenticationToken.class.getSimpleName()));\n\t}\n\n\t@Test\n\tpublic void anonymousRequestWhenAnonymousKeyConfiguredThenKeyIsUsed() throws Exception {\n\t\tthis.spring.register(AnonymousKeyConfig.class, AnonymousController.class).autowire();\n\t\tthis.mvc.perform(get(\"/key\")).andExpect(content().string(String.valueOf(\"AnonymousKeyConfig\".hashCode())));\n\t}\n\n\t@Test\n\tpublic void anonymousRequestWhenAnonymousUsernameConfiguredThenUsernameIsUsed() throws Exception {\n\t\tthis.spring.register(AnonymousUsernameConfig.class, AnonymousController.class).autowire();\n\t\tthis.mvc.perform(get(\"/principal\")).andExpect(content().string(\"AnonymousUsernameConfig\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AnonymousConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/type\").anonymous()\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AnonymousDisabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests.anyRequest().anonymous())\n\t\t\t\t.anonymous((anonymous) -> anonymous.disable());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AnonymousGrantedAuthorityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/type\").hasRole(\"ANON\")\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.anonymous((anonymous) -> anonymous\n\t\t\t\t\t.authorities(\"ROLE_ANON\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AnonymousKeyConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/key\").anonymous()\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.anonymous((anonymous) -> anonymous.key(\"AnonymousKeyConfig\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AnonymousUsernameConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/principal\").anonymous()\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.anonymous((anonymous) -> anonymous.principal(\"AnonymousUsernameConfig\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class AnonymousController {\n\n\t\t@GetMapping(\"/type\")\n\t\tString type() {\n\t\t\treturn anonymousToken().map(AnonymousAuthenticationToken::getClass).map(Class::getSimpleName).orElse(null);\n\t\t}\n\n\t\t@GetMapping(\"/key\")\n\t\tString key() {\n\t\t\treturn anonymousToken().map(AnonymousAuthenticationToken::getKeyHash).map(String::valueOf).orElse(null);\n\t\t}\n\n\t\t@GetMapping(\"/principal\")\n\t\tString principal() {\n\t\t\treturn anonymousToken().map(AnonymousAuthenticationToken::getName).orElse(null);\n\t\t}\n\n\t\tOptional<AnonymousAuthenticationToken> anonymousToken() {\n\t\t\treturn Optional.of(SecurityContextHolder.getContext())\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.filter((a) -> a instanceof AnonymousAuthenticationToken)\n\t\t\t\t.map(AnonymousAuthenticationToken.class::cast);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpBasicTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.http.HttpHeaders;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;http-basic&gt; attributes is present\n *\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpBasicTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t/**\n\t * http/http-basic equivalent\n\t */\n\t@Test\n\tpublic void basicAuthenticationWhenUsingDefaultsThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpBasicConfig.class, UserConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t\tMockHttpServletRequestBuilder requestWithInvalidPassword = get(\"/\").with(httpBasic(\"user\", \"invalid\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(requestWithInvalidPassword)\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Basic realm=\\\"Realm\\\", charset=\\\"UTF-8\\\"\"));\n\t\t// @formatter:on\n\t\tMockHttpServletRequestBuilder requestWithValidPassword = get(\"/\").with(httpBasic(\"user\", \"password\"));\n\t\tthis.mvc.perform(requestWithValidPassword).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void basicAuthenticationWhenUsingDefaultsInLambdaThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpBasicLambdaConfig.class, UserConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isUnauthorized());\n\t\tMockHttpServletRequestBuilder requestWithInvalidPassword = get(\"/\").with(httpBasic(\"user\", \"invalid\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(requestWithInvalidPassword)\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Basic realm=\\\"Realm\\\", charset=\\\"UTF-8\\\"\"));\n\t\t// @formatter:on\n\t\tMockHttpServletRequestBuilder requestWithValidPassword = get(\"/\").with(httpBasic(\"user\", \"password\"));\n\t\tthis.mvc.perform(requestWithValidPassword).andExpect(status().isNotFound());\n\t}\n\n\t/**\n\t * http@realm equivalent\n\t */\n\t@Test\n\tpublic void basicAuthenticationWhenUsingCustomRealmThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomHttpBasicConfig.class, UserConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithInvalidPassword = get(\"/\").with(httpBasic(\"user\", \"invalid\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(requestWithInvalidPassword)\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Basic realm=\\\"Custom Realm\\\", charset=\\\"UTF-8\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void basicAuthenticationWhenUsingCustomRealmInLambdaThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomHttpBasicLambdaConfig.class, UserConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithInvalidPassword = get(\"/\").with(httpBasic(\"user\", \"invalid\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(requestWithInvalidPassword)\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Basic realm=\\\"Custom Realm\\\", charset=\\\"UTF-8\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * http/http-basic@authentication-details-source-ref equivalent\n\t */\n\t@Test\n\tpublic void basicAuthenticationWhenUsingAuthenticationDetailsSourceRefThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(AuthenticationDetailsSourceHttpBasicConfig.class, UserConfig.class).autowire();\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> source = this.spring.getContext()\n\t\t\t.getBean(AuthenticationDetailsSource.class);\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")));\n\t\tverify(source).buildDetails(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void basicAuthenticationWhenUsingAuthenticationDetailsSourceRefInLambdaThenMatchesNamespace()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthenticationDetailsSourceHttpBasicLambdaConfig.class, UserConfig.class).autowire();\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> source = this.spring.getContext()\n\t\t\t.getBean(AuthenticationDetailsSource.class);\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")));\n\t\tverify(source).buildDetails(any(HttpServletRequest.class));\n\t}\n\n\t/**\n\t * http/http-basic@entry-point-ref\n\t */\n\t@Test\n\tpublic void basicAuthenticationWhenUsingEntryPointRefThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(EntryPointRefHttpBasicConfig.class, UserConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().is(999));\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"invalid\"))).andExpect(status().is(999));\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\"))).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void basicAuthenticationWhenUsingEntryPointRefInLambdaThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(EntryPointRefHttpBasicLambdaConfig.class, UserConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().is(999));\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"invalid\"))).andExpect(status().is(999));\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\"))).andExpect(status().isNotFound());\n\t}\n\n\t@Configuration\n\tstatic class UserConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t.build()\n\t\t\t\t// @formatter:on\n\t\t\t);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpBasicConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpBasicLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomHttpBasicConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.httpBasic((basic) -> basic.realmName(\"Custom Realm\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomHttpBasicLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.httpBasic((httpBasicConfig) -> httpBasicConfig.realmName(\"Custom Realm\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthenticationDetailsSourceHttpBasicConfig {\n\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic((basic) -> basic\n\t\t\t\t\t.authenticationDetailsSource(this.authenticationDetailsSource));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource() {\n\t\t\treturn this.authenticationDetailsSource;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthenticationDetailsSourceHttpBasicLambdaConfig {\n\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic((httpBasicConfig) -> httpBasicConfig.authenticationDetailsSource(this.authenticationDetailsSource));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource() {\n\t\t\treturn this.authenticationDetailsSource;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EntryPointRefHttpBasicConfig {\n\n\t\tAuthenticationEntryPoint authenticationEntryPoint = (request, response, ex) -> response.setStatus(999);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.httpBasic((basic) -> basic\n\t\t\t\t\t.authenticationEntryPoint(this.authenticationEntryPoint));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EntryPointRefHttpBasicLambdaConfig {\n\n\t\tAuthenticationEntryPoint authenticationEntryPoint = (request, response, ex) -> response.setStatus(999);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.httpBasic((httpBasicConfig) -> httpBasicConfig.authenticationEntryPoint(this.authenticationEntryPoint));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpCustomFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.assertj.core.api.ListAssert;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.builders.TestHttpSecurities;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * Tests to verify that all the functionality of &lt;custom-filter&gt; attributes is\n * present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpCustomFilterTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void getFiltersWhenFilterAddedBeforeThenBehaviorMatchesNamespace() {\n\t\tthis.spring.register(CustomFilterBeforeConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\tassertThatFilters().containsSubsequence(CustomFilter.class, UsernamePasswordAuthenticationFilter.class);\n\t}\n\n\t@Test\n\tpublic void getFiltersWhenFilterAddedAfterThenBehaviorMatchesNamespace() {\n\t\tthis.spring.register(CustomFilterAfterConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\tassertThatFilters().containsSubsequence(UsernamePasswordAuthenticationFilter.class, CustomFilter.class);\n\t}\n\n\t@Test\n\tpublic void getFiltersWhenFilterAddedThenBehaviorMatchesNamespace() {\n\t\tthis.spring.register(CustomFilterPositionConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\tassertThatFilters().containsExactly(CustomFilter.class);\n\t}\n\n\t@Test\n\tpublic void getFiltersWhenFilterAddedAtPositionThenBehaviorMatchesNamespace() {\n\t\tthis.spring.register(CustomFilterPositionAtConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\tassertThatFilters().containsExactly(OtherCustomFilter.class);\n\t}\n\n\t@Test\n\tpublic void getFiltersWhenCustomAuthenticationManagerThenBehaviorMatchesNamespace() {\n\t\tthis.spring.register(NoAuthenticationManagerInHttpConfigurationConfig.class).autowire();\n\t\tassertThatFilters().startsWith(CustomFilter.class);\n\t}\n\n\tprivate ListAssert<Class<?>> assertThatFilters() {\n\t\tFilterChainProxy filterChain = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tList<Class<?>> filters = filterChain.getFilters(\"/\")\n\t\t\t.stream()\n\t\t\t.map(Object::getClass)\n\t\t\t.collect(Collectors.toList());\n\t\treturn assertThat(filters);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomFilterBeforeConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomFilterAfterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomFilterPositionConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tTestHttpSecurities.disableDefaults(http);\n\t\t\thttp\n\t\t\t\t// this works so long as the CustomFilter extends one of the standard filters\n\t\t\t\t// if not, use addFilterBefore or addFilterAfter\n\t\t\t\t.addFilter(new CustomFilter());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomFilterPositionAtConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tTestHttpSecurities.disableDefaults(http);\n\t\t\thttp\n\t\t\t\t.addFilterAt(new OtherCustomFilter(), UsernamePasswordAuthenticationFilter.class);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NoAuthenticationManagerInHttpConfigurationConfig {\n\n\t\t@Bean\n\t\tAuthenticationManager authenticationManager() {\n\t\t\treturn new CustomAuthenticationManager();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tTestHttpSecurities.disableDefaults(http);\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UserDetailsServiceConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n\tstatic class CustomFilter extends UsernamePasswordAuthenticationFilter {\n\n\t}\n\n\tstatic class OtherCustomFilter extends OncePerRequestFilter {\n\n\t\t@Override\n\t\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tFilterChain filterChain) throws ServletException, IOException {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t}\n\n\t}\n\n\tstatic class CustomAuthenticationManager implements AuthenticationManager {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpExpressionHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.security.Principal;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;\nimport org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\n\n/**\n * Tests to verify that all the functionality of &lt;expression-handler&gt; attributes is\n * present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class NamespaceHttpExpressionHandlerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\t@WithMockUser\n\tpublic void getWhenHasCustomExpressionHandlerThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(ExpressionHandlerController.class, ExpressionHandlerConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/whoami\")).andExpect(content().string(\"user\"));\n\t\tverifyBean(\"expressionParser\", ExpressionParser.class).parseExpression(\"hasRole('USER')\");\n\t}\n\n\tprivate <T> T verifyBean(String beanName, Class<T> beanClass) {\n\t\treturn verify(this.spring.getContext().getBean(beanName, beanClass));\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\tstatic class ExpressionHandlerConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, WebExpressionAuthorizationManager.Builder authz)\n\t\t\t\tthrows Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().access(authz.expression(\"hasRole('USER')\"))\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tWebExpressionAuthorizationManager.Builder expressions(DefaultHttpSecurityExpressionHandler expressionHandler) {\n\t\t\treturn WebExpressionAuthorizationManager.withExpressionHandler(expressionHandler);\n\t\t}\n\n\t\t@Bean\n\t\tDefaultHttpSecurityExpressionHandler expressionHandler(ExpressionParser expressionParser) {\n\t\t\tDefaultHttpSecurityExpressionHandler expressionHandler = new DefaultHttpSecurityExpressionHandler();\n\t\t\texpressionHandler.setExpressionParser(expressionParser);\n\t\t\treturn expressionHandler;\n\t\t}\n\n\t\t@Bean\n\t\tExpressionParser expressionParser() {\n\t\t\treturn spy(new SpelExpressionParser());\n\t\t}\n\n\t}\n\n\t@RestController\n\tprivate static class ExpressionHandlerController {\n\n\t\t@GetMapping(\"/whoami\")\n\t\tString whoami(Principal user) {\n\t\t\treturn user.getName();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFirewallTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.firewall.DefaultHttpFirewall;\nimport org.springframework.security.web.firewall.FirewalledRequest;\nimport org.springframework.security.web.firewall.HttpFirewall;\nimport org.springframework.security.web.firewall.RequestRejectedException;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;http-firewall&gt; attributes is\n * present\n *\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpFirewallTests {\n\n\tpublic final SpringTestContext rule = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\t@Disabled(\"MockMvc uses UriComponentsBuilder::fromUriString which was changed in https://github.com/spring-projects/spring-framework/issues/32513\")\n\tpublic void requestWhenPathContainsDoubleDotsThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.rule.register(HttpFirewallConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/public/../private/\")).andExpect(status().isBadRequest());\n\t}\n\n\t@Test\n\tpublic void requestWithCustomFirewallThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.rule.register(CustomHttpFirewallConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").param(\"deny\", \"true\")).andExpect(status().isBadRequest());\n\t}\n\n\t@Test\n\tpublic void requestWithCustomFirewallBeanThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.rule.register(CustomHttpFirewallBeanConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").param(\"deny\", \"true\")).andExpect(status().isBadRequest());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpFirewallConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomHttpFirewallConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.httpFirewall(new CustomHttpFirewall());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomHttpFirewallBeanConfig {\n\n\t\t@Bean\n\t\tHttpFirewall firewall() {\n\t\t\treturn new CustomHttpFirewall();\n\t\t}\n\n\t}\n\n\tstatic class CustomHttpFirewall extends DefaultHttpFirewall {\n\n\t\t@Override\n\t\tpublic FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {\n\t\t\tif (request.getParameter(\"deny\") != null) {\n\t\t\t\tthrow new RequestRejectedException(\"custom rejection\");\n\t\t\t}\n\t\t\treturn super.getFirewalledRequest(request);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\n\n/**\n * Tests to verify that all the functionality of &lt;form-login&gt; attributes is present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpFormLoginTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void formLoginWhenDefaultConfigurationThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(FormLoginConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(redirectedUrl(\"/login\"));\n\t\tthis.mvc.perform(post(\"/login\").with(csrf())).andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\t// @formatter:on\n\t\tthis.mvc.perform(loginRequest).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void formLoginWithCustomEndpointsThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(FormLoginCustomConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(redirectedUrl(\"/authentication/login\"));\n\t\tthis.mvc.perform(post(\"/authentication/login/process\").with(csrf()))\n\t\t\t.andExpect(redirectedUrl(\"/authentication/login?failed\"));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/authentication/login/process\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(redirectedUrl(\"/default\"));\n\t}\n\n\t@Test\n\tpublic void formLoginWithCustomHandlersThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(FormLoginCustomRefsConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(redirectedUrl(\"/login\"));\n\t\tthis.mvc.perform(post(\"/login\").with(csrf())).andExpect(redirectedUrl(\"/custom/failure\"));\n\t\tverifyBean(WebAuthenticationDetailsSource.class).buildDetails(any(HttpServletRequest.class));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\t// @formatter:on\n\t\tthis.mvc.perform(loginRequest).andExpect(redirectedUrl(\"/custom/targetUrl\"));\n\t}\n\n\tprivate <T> T verifyBean(Class<T> beanClass) {\n\t\treturn verify(this.spring.getContext().getBean(beanClass));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class FormLoginConfig {\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\treturn (web) -> web.ignoring().requestMatchers(\"/resources/**\");\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginCustomConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tboolean alwaysUseDefaultSuccess = true;\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.usernameParameter(\"username\") // form-login@username-parameter\n\t\t\t\t\t.passwordParameter(\"password\") // form-login@password-parameter\n\t\t\t\t\t.loginPage(\"/authentication/login\") // form-login@login-page\n\t\t\t\t\t.failureUrl(\"/authentication/login?failed\") // form-login@authentication-failure-url\n\t\t\t\t\t.loginProcessingUrl(\"/authentication/login/process\") // form-login@login-processing-url\n\t\t\t\t\t.defaultSuccessUrl(\"/default\", alwaysUseDefaultSuccess));\n\t\t\treturn http.build(); // form-login@default-target-url / form-login@always-use-default-target\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginCustomRefsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tSavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();\n\t\t\tsuccessHandler.setDefaultTargetUrl(\"/custom/targetUrl\");\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginPage(\"/login\")\n\t\t\t\t\t.failureHandler(new SimpleUrlAuthenticationFailureHandler(\"/custom/failure\")) // form-login@authentication-failure-handler-ref\n\t\t\t\t\t.successHandler(successHandler) // form-login@authentication-success-handler-ref\n\t\t\t\t\t.authenticationDetailsSource(authenticationDetailsSource()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tWebAuthenticationDetailsSource authenticationDetailsSource() {\n\t\t\treturn spy(WebAuthenticationDetailsSource.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UserDetailsServiceConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t\t\t.build());\n\t\t\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpHeadersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.net.URI;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.header.writers.StaticHeadersWriter;\nimport org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;\nimport org.springframework.security.web.header.writers.frameoptions.StaticAllowFromStrategy;\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\n\n/**\n * Tests to verify that all the functionality of &lt;headers&gt; attributes is present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpHeadersTests {\n\n\tstatic final Map<String, String> defaultHeaders = new LinkedHashMap<>();\n\tstatic {\n\t\tdefaultHeaders.put(\"X-Content-Type-Options\", \"nosniff\");\n\t\tdefaultHeaders.put(\"X-Frame-Options\", \"DENY\");\n\t\tdefaultHeaders.put(\"Strict-Transport-Security\", \"max-age=31536000 ; includeSubDomains\");\n\t\tdefaultHeaders.put(\"Cache-Control\", \"no-cache, no-store, max-age=0, must-revalidate\");\n\t\tdefaultHeaders.put(\"Expires\", \"0\");\n\t\tdefaultHeaders.put(\"Pragma\", \"no-cache\");\n\t\tdefaultHeaders.put(\"X-XSS-Protection\", \"0\");\n\t}\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void secureRequestWhenDefaultConfigThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HeadersDefaultConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").secure(true)).andExpect(includesDefaults());\n\t}\n\n\t@Test\n\tpublic void secureRequestWhenCacheControlOnlyThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HeadersCacheControlConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").secure(true)).andExpect(includes(\"Cache-Control\", \"Expires\", \"Pragma\"));\n\t}\n\n\t@Test\n\tpublic void secureRequestWhenHstsOnlyThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HstsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").secure(true)).andExpect(includes(\"Strict-Transport-Security\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenHstsCustomThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HstsCustomConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t.andExpect(includes(Collections.singletonMap(\"Strict-Transport-Security\", \"max-age=15768000\")));\n\t}\n\n\t@Test\n\tpublic void requestWhenFrameOptionsSameOriginThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(FrameOptionsSameOriginConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(includes(Collections.singletonMap(\"X-Frame-Options\", \"SAMEORIGIN\")));\n\t}\n\n\t@Test\n\tpublic void requestWhenFrameOptionsAllowFromThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(FrameOptionsAllowFromConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t.andExpect(includes(Collections.singletonMap(\"X-Frame-Options\", \"ALLOW-FROM https://example.com\")));\n\t}\n\n\t@Test\n\tpublic void requestWhenXssOnlyThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(XssProtectionConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(includes(\"X-XSS-Protection\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenXssCustomThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(XssProtectionCustomConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(includes(Collections.singletonMap(\"X-XSS-Protection\", \"1; mode=block\")));\n\t}\n\n\t@Test\n\tpublic void requestWhenXContentTypeOptionsOnlyThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(ContentTypeOptionsConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(includes(\"X-Content-Type-Options\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomHeaderOnlyThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HeaderRefConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t.andExpect(includes(Collections.singletonMap(\"customHeaderName\", \"customHeaderValue\")));\n\t}\n\n\tprivate static ResultMatcher includesDefaults() {\n\t\treturn includes(defaultHeaders);\n\t}\n\n\tprivate static ResultMatcher includes(String... headerNames) {\n\t\treturn includes(defaultHeaders, headerNames);\n\t}\n\n\tprivate static ResultMatcher includes(Map<String, String> headers) {\n\t\treturn includes(headers, headers.keySet().toArray(new String[headers.size()]));\n\t}\n\n\tprivate static ResultMatcher includes(Map<String, String> headers, String... headerNames) {\n\t\treturn (result) -> {\n\t\t\tassertThat(result.getResponse().getHeaderNames()).hasSameSizeAs(headerNames);\n\t\t\tfor (String headerName : headerNames) {\n\t\t\t\theader().string(headerName, headers.get(headerName)).match(result);\n\t\t\t}\n\t\t};\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HeadersDefaultConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HeadersCacheControlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.cacheControl(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HstsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpStrictTransportSecurity(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HstsCustomConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t// hsts@request-matcher-ref, hsts@max-age-seconds, hsts@include-subdomains\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.httpStrictTransportSecurity((hsts) -> hsts\n\t\t\t\t\t\t.requestMatcher(AnyRequestMatcher.INSTANCE)\n\t\t\t\t\t\t.maxAgeInSeconds(15768000)\n\t\t\t\t\t\t.includeSubDomains(false)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FrameOptionsSameOriginConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t// frame-options@policy=SAMEORIGIN\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.frameOptions((frameOptions) -> frameOptions.sameOrigin()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FrameOptionsAllowFromConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t// frame-options@ref\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.addHeaderWriter(new XFrameOptionsHeaderWriter(\n\t\t\t\t\t\tnew StaticAllowFromStrategy(URI.create(\"https://example.com\")))));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class XssProtectionConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t// xss-protection\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.xssProtection(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class XssProtectionCustomConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t// xss-protection@enabled and xss-protection@block\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.xssProtection((xss) -> xss\n\t\t\t\t\t\t.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ContentTypeOptionsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t// content-type-options\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.contentTypeOptions(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HeaderRefConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t.defaultsDisabled()\n\t\t\t\t\t.addHeaderWriter(new StaticHeadersWriter(\"customHeaderName\", \"customHeaderValue\")));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpInterceptUrlTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;intercept-url&gt; attributes is\n * present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpInterceptUrlTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void unauthenticatedRequestWhenUrlRequiresAuthenticationThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpInterceptUrlConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/users\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void authenticatedRequestWhenUrlRequiresElevatedPrivilegesThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpInterceptUrlConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithUser = get(\"/users\").with(authentication(user(\"ROLE_USER\")));\n\t\tthis.mvc.perform(requestWithUser).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void authenticatedRequestWhenAuthorizedThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpInterceptUrlConfig.class, BaseController.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithAdmin = get(\"/users\").with(authentication(user(\"ROLE_ADMIN\")));\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isOk()).andReturn();\n\t}\n\n\t@Test\n\t@Disabled // see https://github.com/spring-projects/spring-security/issues/17747\n\tpublic void requestWhenMappedByPostInterceptUrlThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpInterceptUrlConfig.class, BaseController.class).autowire();\n\t\tMockHttpServletRequestBuilder getWithUser = get(\"/admin/post\").with(authentication(user(\"ROLE_USER\")));\n\t\tthis.mvc.perform(getWithUser).andExpect(status().isOk());\n\t\tMockHttpServletRequestBuilder postWithUser = post(\"/admin/post\").with(authentication(user(\"ROLE_USER\")));\n\t\tthis.mvc.perform(postWithUser).andExpect(status().isForbidden());\n\t\tMockHttpServletRequestBuilder requestWithAdmin = post(\"/admin/post\").with(csrf())\n\t\t\t.with(authentication(user(\"ROLE_ADMIN\")));\n\t\tthis.mvc.perform(requestWithAdmin).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requestWhenRequiresChannelThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpInterceptUrlConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/login\")).andExpect(redirectedUrl(\"https://localhost/login\"));\n\t\tthis.mvc.perform(get(\"/secured/a\")).andExpect(redirectedUrl(\"https://localhost/secured/a\"));\n\t\tthis.mvc.perform(get(\"https://localhost/user\")).andExpect(redirectedUrl(\"http://localhost/user\"));\n\t}\n\n\tprivate static Authentication user(String role) {\n\t\treturn UsernamePasswordAuthenticationToken.authenticated(\"user\", null,\n\t\t\t\tAuthorityUtils.createAuthorityList(role));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class HttpInterceptUrlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests.requestMatchers(\n\t\t\t\t\t// the line below is similar to intercept-url@pattern:\n\t\t\t\t\t//    <intercept-url pattern=\"/users**\" access=\"hasRole('ROLE_ADMIN')\"/>\n\t\t\t\t\t//\" access=\"hasRole('ROLE_ADMIN')\"/>\n\"/users**\", \"/sessions/**\").hasRole(\"ADMIN\").requestMatchers(\n\t\t\t\t\t// the line below is similar to intercept-url@method:\n\t\t\t\t\t//    <intercept-url pattern=\"/admin/post\" access=\"hasRole('ROLE_ADMIN')\" method=\"POST\"/>\n\t\t\t\t\t//\" access=\"hasRole('ROLE_ADMIN')\" method=\"POST\"/>\nHttpMethod.POST, \"/admin/post\", \"/admin/another-post/**\").hasRole(\"ADMIN\")\n\t\t\t\t\t.requestMatchers(\"/signup\").permitAll()\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.requiresChannel((channel) -> channel.requestMatchers(\"/login\", \"/secured/**\")\n\t\t\t\t\t// NOTE: channel security is configured separately of authorization (i.e. intercept-url@access\n\t\t\t\t\t// the line below is similar to intercept-url@requires-channel=\"https\":\n\t\t\t\t\t//    <intercept-url pattern=\"/login\" requires-channel=\"https\"/>\n\t\t\t\t\t//\" requires-channel=\"https\"/>\n\t\t\t\t.requiresSecure().anyRequest().requiresInsecure());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BaseController {\n\n\t\t@GetMapping(\"/users\")\n\t\tString users() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t\t@GetMapping(\"/sessions\")\n\t\tString sessions() {\n\t\t\treturn \"sessions\";\n\t\t}\n\n\t\t@RequestMapping(\"/admin/post\")\n\t\tString adminPost() {\n\t\t\treturn \"adminPost\";\n\t\t}\n\n\t\t@GetMapping(\"/admin/another-post\")\n\t\tString adminAnotherPost() {\n\t\t\treturn \"adminAnotherPost\";\n\t\t}\n\n\t\t@GetMapping(\"/signup\")\n\t\tString signup() {\n\t\t\treturn \"signup\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpJeeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.security.Principal;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;jee&gt; attributes is present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpJeeTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenJeeUserThenBehaviorDiffersFromNamespaceForRoleNames() throws Exception {\n\t\tthis.spring.register(JeeMappableRolesConfig.class, BaseController.class).autowire();\n\t\tPrincipal user = mock(Principal.class);\n\t\tgiven(user.getName()).willReturn(\"joe\");\n\t\tthis.mvc.perform(get(\"/roles\").principal(user).with((request) -> {\n\t\t\trequest.addUserRole(\"ROLE_admin\");\n\t\t\trequest.addUserRole(\"ROLE_user\");\n\t\t\trequest.addUserRole(\"ROLE_unmapped\");\n\t\t\treturn request;\n\t\t})).andExpect(status().isOk()).andExpect(content().string(\"ROLE_admin,ROLE_user\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthenticatedUserDetailsServiceThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(JeeUserServiceRefConfig.class, BaseController.class).autowire();\n\t\tPrincipal user = mock(Principal.class);\n\t\tgiven(user.getName()).willReturn(\"joe\");\n\t\tUser result = new User(user.getName(), \"N/A\", true, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_user\"));\n\t\tgiven(bean(AuthenticationUserDetailsService.class).loadUserDetails(any())).willReturn(result);\n\t\tthis.mvc.perform(get(\"/roles\").principal(user))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"ROLE_user\"));\n\t\tverifyBean(AuthenticationUserDetailsService.class).loadUserDetails(any());\n\t}\n\n\tprivate <T> T bean(Class<T> beanClass) {\n\t\treturn this.spring.getContext().getBean(beanClass);\n\t}\n\n\tprivate <T> T verifyBean(Class<T> beanClass) {\n\t\treturn verify(this.spring.getContext().getBean(beanClass));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class JeeMappableRolesConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"user\"))\n\t\t\t\t.jee((jee) -> jee\n\t\t\t\t\t.mappableRoles(\"user\", \"admin\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class JeeUserServiceRefConfig {\n\n\t\tprivate final AuthenticationUserDetailsService authenticationUserDetailsService = mock(\n\t\t\t\tAuthenticationUserDetailsService.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"user\"))\n\t\t\t\t.jee((jee) -> jee\n\t\t\t\t\t.mappableAuthorities(\"ROLE_user\", \"ROLE_admin\")\n\t\t\t\t\t.authenticatedUserDetailsService(this.authenticationUserDetailsService));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tpublic AuthenticationUserDetailsService authenticationUserDetailsService() {\n\t\t\treturn this.authenticationUserDetailsService;\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BaseController {\n\n\t\t@GetMapping(\"/authenticated\")\n\t\tString authenticated(Authentication authentication) {\n\t\t\treturn authentication.getName();\n\t\t}\n\n\t\t@GetMapping(\"/roles\")\n\t\tString roles(Authentication authentication) {\n\t\t\treturn authentication.getAuthorities().stream().map(Object::toString).collect(Collectors.joining(\",\"));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpLogoutTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport jakarta.servlet.http.HttpSession;\nimport org.assertj.core.api.Condition;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;logout&gt; attributes is present\n *\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class NamespaceHttpLogoutTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t/**\n\t * http/logout equivalent\n\t */\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenUsingDefaultsThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpLogoutConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t\t.andExpect(authenticated(false))\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"))\n\t\t\t\t.andExpect(noCookies())\n\t\t\t\t.andExpect(session(Objects::isNull));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenDisabledInLambdaThenRespondsWithNotFound() throws Exception {\n\t\tthis.spring.register(HttpLogoutDisabledInLambdaConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\").with(csrf()).with(user(\"user\"));\n\t\tthis.mvc.perform(logoutRequest).andExpect(status().isNotFound());\n\t}\n\n\t/**\n\t * http/logout custom\n\t */\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenUsingVariousCustomizationsMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomHttpLogoutConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/custom-logout\").with(csrf()))\n\t\t\t\t.andExpect(authenticated(false))\n\t\t\t\t.andExpect(redirectedUrl(\"/logout-success\"))\n\t\t\t\t.andExpect((result) -> assertThat(result.getResponse().getCookies()).hasSize(1))\n\t\t\t\t.andExpect(cookie().maxAge(\"remove\", 0))\n\t\t\t\t.andExpect(session(Objects::nonNull));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenUsingVariousCustomizationsInLambdaThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomHttpLogoutInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/custom-logout\").with(csrf()))\n\t\t\t\t.andExpect(authenticated(false))\n\t\t\t\t.andExpect(redirectedUrl(\"/logout-success\"))\n\t\t\t\t.andExpect((result) -> assertThat(result.getResponse().getCookies()).hasSize(1))\n\t\t\t\t.andExpect(cookie().maxAge(\"remove\", 0))\n\t\t\t\t.andExpect(session(Objects::nonNull));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * http/logout@success-handler-ref\n\t */\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenUsingSuccessHandlerRefThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(SuccessHandlerRefHttpLogoutConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t\t.andExpect(authenticated(false))\n\t\t\t\t.andExpect(redirectedUrl(\"/SuccessHandlerRefHttpLogoutConfig\"))\n\t\t\t\t.andExpect(noCookies())\n\t\t\t\t.andExpect(session(Objects::isNull));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenUsingSuccessHandlerRefInLambdaThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(SuccessHandlerRefHttpLogoutInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t\t.andExpect(authenticated(false))\n\t\t\t\t.andExpect(redirectedUrl(\"/SuccessHandlerRefHttpLogoutConfig\"))\n\t\t\t\t.andExpect(noCookies())\n\t\t\t\t.andExpect(session(Objects::isNull));\n\t\t// @formatter:on\n\t}\n\n\tResultMatcher authenticated(boolean authenticated) {\n\t\treturn (result) -> assertThat(Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())\n\t\t\t.map(Authentication::isAuthenticated)\n\t\t\t.orElse(false)).isEqualTo(authenticated);\n\t}\n\n\tResultMatcher noCookies() {\n\t\treturn (result) -> assertThat(result.getResponse().getCookies()).isEmpty();\n\t}\n\n\tResultMatcher session(Predicate<HttpSession> sessionPredicate) {\n\t\treturn (result) -> assertThat(result.getRequest().getSession(false))\n\t\t\t.is(new Condition<>(sessionPredicate, \"sessionPredicate failed\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpLogoutConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpLogoutDisabledInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.logout(AbstractHttpConfigurer::disable);\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomHttpLogoutConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.deleteCookies(\"remove\") // logout@delete-cookies\n\t\t\t\t\t.invalidateHttpSession(false) // logout@invalidate-session=false (default is true)\n\t\t\t\t\t.logoutUrl(\"/custom-logout\") // logout@logout-url (default is /logout)\n\t\t\t\t\t.logoutSuccessUrl(\"/logout-success\"));\n\t\t\treturn http.build(); // logout@success-url (default is /login?logout)\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomHttpLogoutInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout.deleteCookies(\"remove\")\n\t\t\t\t\t\t\t.invalidateHttpSession(false)\n\t\t\t\t\t\t\t.logoutUrl(\"/custom-logout\")\n\t\t\t\t\t\t\t.logoutSuccessUrl(\"/logout-success\")\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SuccessHandlerRefHttpLogoutConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tSimpleUrlLogoutSuccessHandler logoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();\n\t\t\tlogoutSuccessHandler.setDefaultTargetUrl(\"/SuccessHandlerRefHttpLogoutConfig\");\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.logoutSuccessHandler(logoutSuccessHandler));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SuccessHandlerRefHttpLogoutInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tSimpleUrlLogoutSuccessHandler logoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();\n\t\t\tlogoutSuccessHandler.setDefaultTargetUrl(\"/SuccessHandlerRefHttpLogoutConfig\");\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout.logoutSuccessHandler(logoutSuccessHandler));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpPortMappingsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\n\n/**\n * Tests to verify that all the functionality of &lt;port-mappings&gt; attributes is\n * present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpPortMappingsTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void portMappingWhenRequestRequiresChannelThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(HttpInterceptUrlWithPortMapperConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"http://localhost:9080/login\")).andExpect(redirectedUrl(\"https://localhost:9443/login\"));\n\t\tthis.mvc.perform(get(\"http://localhost:9080/secured/a\"))\n\t\t\t.andExpect(redirectedUrl(\"https://localhost:9443/secured/a\"));\n\t\tthis.mvc.perform(get(\"https://localhost:9443/user\")).andExpect(redirectedUrl(\"http://localhost:9080/user\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class HttpInterceptUrlWithPortMapperConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.portMapper((mapper) -> mapper\n\t\t\t\t\t.http(9080).mapsTo(9443))\n\t\t\t\t.requiresChannel((channel) -> channel\n\t\t\t\t\t.requestMatchers(\"/login\", \"/secured/**\").requiresSecure()\n\t\t\t\t\t.anyRequest().requiresInsecure());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpRequestCacheTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;request-cache&gt; attributes is\n * present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpRequestCacheTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenCustomRequestCacheThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(RequestCacheRefConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isForbidden());\n\t\tverifyBean(RequestCache.class).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void requestWhenDefaultConfigurationThenUsesHttpSessionRequestCache() throws Exception {\n\t\tthis.spring.register(DefaultRequestCacheRefConfig.class).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/\")).andExpect(status().isForbidden()).andReturn();\n\t\tHttpSession session = result.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\tassertThat(session.getAttribute(\"SPRING_SECURITY_SAVED_REQUEST\")).isNotNull();\n\t}\n\n\tprivate <T> T verifyBean(Class<T> beanClass) {\n\t\treturn verify(this.spring.getContext().getBean(beanClass));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestCacheRefConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.requestCache((cache) -> cache\n\t\t\t\t\t.requestCache(requestCache()));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t\t@Bean\n\t\tRequestCache requestCache() {\n\t\t\treturn mock(RequestCache.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultRequestCacheRefConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;access-denied-handler&gt; attributes\n * is present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpServerAccessDeniedHandlerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenCustomAccessDeniedPageThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(AccessDeniedPageConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(authentication(user())))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(forwardedUrl(\"/AccessDeniedPageConfig\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAccessDeniedPageInLambdaThenForwardedToCustomPage() throws Exception {\n\t\tthis.spring.register(AccessDeniedPageInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(authentication(user())))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(forwardedUrl(\"/AccessDeniedPageConfig\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAccessDeniedHandlerThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(AccessDeniedHandlerRefConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").with(authentication(user())));\n\t\tverifyBean(AccessDeniedHandler.class).handle(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AccessDeniedException.class));\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAccessDeniedHandlerInLambdaThenBehaviorMatchesNamespace() throws Exception {\n\t\tthis.spring.register(AccessDeniedHandlerRefInLambdaConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\").with(authentication(user())));\n\t\tverify(AccessDeniedHandlerRefInLambdaConfig.accessDeniedHandler).handle(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(AccessDeniedException.class));\n\t}\n\n\tprivate static Authentication user() {\n\t\treturn UsernamePasswordAuthenticationToken.authenticated(\"user\", null, AuthorityUtils.NO_AUTHORITIES);\n\t}\n\n\tprivate <T> T verifyBean(Class<T> beanClass) {\n\t\treturn verify(this.spring.getContext().getBean(beanClass));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AccessDeniedPageConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.accessDeniedPage(\"/AccessDeniedPageConfig\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AccessDeniedPageInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t)\n\t\t\t\t.exceptionHandling((exceptionHandling) -> exceptionHandling.accessDeniedPage(\"/AccessDeniedPageConfig\")\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AccessDeniedHandlerRefConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.accessDeniedHandler(accessDeniedHandler()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAccessDeniedHandler accessDeniedHandler() {\n\t\t\treturn mock(AccessDeniedHandler.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AccessDeniedHandlerRefInLambdaConfig {\n\n\t\tstatic AccessDeniedHandler accessDeniedHandler = mock(AccessDeniedHandler.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t)\n\t\t\t\t.exceptionHandling((exceptionHandling) -> exceptionHandling.accessDeniedHandler(accessDeniedHandler())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAccessDeniedHandler accessDeniedHandler() {\n\t\t\treturn accessDeniedHandler;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpX509Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.io.InputStream;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport javax.security.auth.x500.X500Principal;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.bouncycastle.asn1.x500.X500Name;\nimport org.bouncycastle.asn1.x500.style.BCStyle;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\n\n/**\n * Tests to verify that all the functionality of &lt;x509&gt; attributes is present in\n * Java config\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceHttpX509Tests {\n\n\tprivate static final User USER = new User(\"customuser\", \"password\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void x509AuthenticationWhenUsingX509DefaultConfigurationThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(X509Config.class, X509Controller.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rod.cer\");\n\t\tthis.mvc.perform(get(\"/whoami\").with(x509(certificate))).andExpect(content().string(\"rod\"));\n\t}\n\n\t@Test\n\tpublic void x509AuthenticationWhenHasCustomAuthenticationDetailsSourceThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(AuthenticationDetailsSourceRefConfig.class, X509Controller.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rod.cer\");\n\t\tthis.mvc.perform(get(\"/whoami\").with(x509(certificate))).andExpect(content().string(\"rod\"));\n\t\tverifyBean(AuthenticationDetailsSource.class).buildDetails(any());\n\t}\n\n\t@Test\n\tpublic void x509AuthenticationWhenHasSubjectPrincipalRegexThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(SubjectPrincipalRegexConfig.class, X509Controller.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rodatexampledotcom.cer\");\n\t\tthis.mvc.perform(get(\"/whoami\").with(x509(certificate))).andExpect(content().string(\"rod\"));\n\t}\n\n\t@Test\n\tpublic void x509AuthenticationWhenHasCustomPrincipalExtractorThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomPrincipalExtractorConfig.class, X509Controller.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rodatexampledotcom.cer\");\n\t\tthis.mvc.perform(get(\"/whoami\").with(x509(certificate))).andExpect(content().string(\"rod@example.com\"));\n\t}\n\n\t@Test\n\tpublic void x509AuthenticationWhenHasCustomUserDetailsServiceThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(UserDetailsServiceRefConfig.class, X509Controller.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rodatexampledotcom.cer\");\n\t\tthis.mvc.perform(get(\"/whoami\").with(x509(certificate))).andExpect(content().string(\"customuser\"));\n\t}\n\n\t@Test\n\tpublic void x509AuthenticationWhenHasCustomAuthenticationUserDetailsServiceThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(AuthenticationUserDetailsServiceConfig.class, X509Controller.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rodatexampledotcom.cer\");\n\t\tthis.mvc.perform(get(\"/whoami\").with(x509(certificate))).andExpect(content().string(\"customuser\"));\n\t}\n\n\t<T extends Certificate> T loadCert(String location) {\n\t\ttry (InputStream is = new ClassPathResource(location).getInputStream()) {\n\t\t\tCertificateFactory certFactory = CertificateFactory.getInstance(\"X.509\");\n\t\t\treturn (T) certFactory.generateCertificate(is);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\t<T> T verifyBean(Class<T> beanClass) {\n\t\treturn verify(this.spring.getContext().getBean(beanClass));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tpublic static class X509Config {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.x509(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class AuthenticationDetailsSourceRefConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t.authenticationDetailsSource(authenticationDetailsSource()));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> authenticationDetailsSource() {\n\t\t\treturn mock(AuthenticationDetailsSource.class);\n\t\t}\n\n\t}\n\n\t@EnableWebMvc\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class SubjectPrincipalRegexConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t.subjectPrincipalRegex(\"CN=(.*?)@example.com(?:,|$)\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebMvc\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class CustomPrincipalExtractorConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod@example.com\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t.x509PrincipalExtractor(this::extractCommonName));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\tprivate String extractCommonName(X509Certificate certificate) {\n\t\t\tX500Principal principal = certificate.getSubjectX500Principal();\n\t\t\treturn new X500Name(principal.getName()).getRDNs(BCStyle.CN)[0].getFirst().getValue().toString();\n\t\t}\n\n\t}\n\n\t@EnableWebMvc\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class UserDetailsServiceRefConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t.userDetailsService((username) -> USER));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebMvc\n\t@Configuration\n\t@EnableWebSecurity\n\tpublic static class AuthenticationUserDetailsServiceConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t.authenticationUserDetailsService((authentication) -> USER));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@RestController\n\tpublic static class X509Controller {\n\n\t\t@GetMapping(\"/whoami\")\n\t\tpublic String whoami(@AuthenticationPrincipal(expression = \"username\") String name) {\n\t\t\treturn name;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;\nimport org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests to verify that all the functionality of &lt;anonymous&gt; attributes is present\n *\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceRememberMeTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void rememberMeLoginWhenUsingDefaultsThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class, SecurityController.class).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/login\").with(rememberMeLogin())).andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\tCookie rememberMe = result.getResponse().getCookie(\"remember-me\");\n\t\tassertThat(rememberMe).isNotNull();\n\t\tthis.mvc.perform(get(\"/authentication-class\").cookie(rememberMe))\n\t\t\t.andExpect(content().string(RememberMeAuthenticationToken.class.getName()));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.session(session)\n\t\t\t\t.cookie(rememberMe);\n\t\tresult = this.mvc.perform(logoutRequest)\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\trememberMe = result.getResponse().getCookie(\"remember-me\");\n\t\tassertThat(rememberMe).isNotNull().extracting(Cookie::getMaxAge).isEqualTo(0);\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder authenticationClassRequest = post(\"/authentication-class\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.cookie(rememberMe);\n\t\tthis.mvc.perform(authenticationClassRequest)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t}\n\n\t// SEC-3170 - RememberMeService implementations should not have to also implement\n\t// LogoutHandler\n\t@Test\n\tpublic void logoutWhenCustomRememberMeServicesDeclaredThenUses() throws Exception {\n\t\tRememberMeServicesRefConfig.REMEMBER_ME_SERVICES = mock(RememberMeServicesWithoutLogoutHandler.class);\n\t\tthis.spring.register(RememberMeServicesRefConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(RememberMeServicesRefConfig.REMEMBER_ME_SERVICES).autoLogin(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tthis.mvc.perform(post(\"/login\").with(csrf()));\n\t\tverify(RememberMeServicesRefConfig.REMEMBER_ME_SERVICES).loginFail(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenAuthenticationSuccessHandlerDeclaredThenUses() throws Exception {\n\t\tAuthSuccessConfig.SUCCESS_HANDLER = mock(AuthenticationSuccessHandler.class);\n\t\tthis.spring.register(AuthSuccessConfig.class).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/login\").with(rememberMeLogin())).andReturn();\n\t\tverifyNoMoreInteractions(AuthSuccessConfig.SUCCESS_HANDLER);\n\t\tCookie rememberMe = result.getResponse().getCookie(\"remember-me\");\n\t\tassertThat(rememberMe).isNotNull();\n\t\tthis.mvc.perform(get(\"/somewhere\").cookie(rememberMe));\n\t\tverify(AuthSuccessConfig.SUCCESS_HANDLER).onAuthenticationSuccess(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenKeyDeclaredThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(WithoutKeyConfig.class, SecurityController.class).autowire();\n\t\tMockHttpServletRequestBuilder requestWithRememberme = post(\"/without-key/login\").with(rememberMeLogin());\n\t\t// @formatter:off\n\t\tCookie withoutKey = this.mvc.perform(requestWithRememberme)\n\t\t\t\t.andReturn()\n\t\t\t\t.getResponse()\n\t\t\t\t.getCookie(\"remember-me\");\n\t\t// @formatter:on\n\t\tMockHttpServletRequestBuilder somewhereRequest = get(\"/somewhere\").cookie(withoutKey);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(somewhereRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\tMockHttpServletRequestBuilder loginWithRememberme = post(\"/login\").with(rememberMeLogin());\n\t\tCookie withKey = this.mvc.perform(loginWithRememberme)\n\t\t\t\t.andReturn()\n\t\t\t\t.getResponse()\n\t\t\t\t.getCookie(\"remember-me\");\n\t\tthis.mvc.perform(get(\"/somewhere\").cookie(withKey))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t// http/remember-me@services-alias is not supported use standard aliasing instead\n\t// (i.e. @Bean(\"alias\"))\n\t// http/remember-me@data-source-ref is not supported directly. Instead use\n\t// http/remember-me@token-repository-ref example\n\t@Test\n\tpublic void rememberMeLoginWhenDeclaredTokenRepositoryThenMatchesNamespace() throws Exception {\n\t\tTokenRepositoryRefConfig.TOKEN_REPOSITORY = mock(PersistentTokenRepository.class);\n\t\tthis.spring.register(TokenRepositoryRefConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/login\").with(rememberMeLogin()));\n\t\tverify(TokenRepositoryRefConfig.TOKEN_REPOSITORY).createNewToken(any(PersistentRememberMeToken.class));\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenTokenValidityDeclaredThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(TokenValiditySecondsConfig.class).autowire();\n\t\t// @formatter:off\n\t\tCookie expiredRememberMe = this.mvc.perform(post(\"/login\").with(rememberMeLogin()))\n\t\t\t\t.andReturn()\n\t\t\t\t.getResponse()\n\t\t\t\t.getCookie(\"remember-me\");\n\t\t// @formatter:on\n\t\tassertThat(expiredRememberMe).extracting(Cookie::getMaxAge).isEqualTo(314);\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenUsingDefaultsThenCookieMaxAgeMatchesNamespace() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class).autowire();\n\t\t// @formatter:off\n\t\tCookie expiredRememberMe = this.mvc.perform(post(\"/login\").with(rememberMeLogin()))\n\t\t\t\t.andReturn()\n\t\t\t\t.getResponse()\n\t\t\t\t.getCookie(\"remember-me\");\n\t\t// @formatter:on\n\t\tassertThat(expiredRememberMe).extracting(Cookie::getMaxAge).isEqualTo(AbstractRememberMeServices.TWO_WEEKS_S);\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenUsingSecureCookieThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(UseSecureCookieConfig.class).autowire();\n\t\t// @formatter:off\n\t\tCookie secureCookie = this.mvc.perform(post(\"/login\").with(rememberMeLogin()))\n\t\t\t\t.andReturn()\n\t\t\t\t.getResponse()\n\t\t\t\t.getCookie(\"remember-me\");\n\t\t// @formatter:on\n\t\tassertThat(secureCookie).extracting(Cookie::getSecure).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenUsingDefaultsThenCookieSecurityMatchesNamespace() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class).autowire();\n\t\t// @formatter:off\n\t\tCookie secureCookie = this.mvc.perform(post(\"/login\").with(rememberMeLogin()).secure(true))\n\t\t\t\t.andReturn()\n\t\t\t\t.getResponse()\n\t\t\t\t.getCookie(\"remember-me\");\n\t\t// @formatter:on\n\t\tassertThat(secureCookie).extracting(Cookie::getSecure).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenParameterSpecifiedThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(RememberMeParameterConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder loginWithRememberme = post(\"/login\").with(rememberMeLogin(\"rememberMe\", true));\n\t\t// @formatter:off\n\t\tCookie rememberMe = this.mvc.perform(loginWithRememberme)\n\t\t\t\t.andReturn()\n\t\t\t\t.getResponse()\n\t\t\t\t.getCookie(\"remember-me\");\n\t\t// @formatter:on\n\t\tassertThat(rememberMe).isNotNull();\n\t}\n\n\t// SEC-2880\n\t@Test\n\tpublic void rememberMeLoginWhenCookieNameDeclaredThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(RememberMeCookieNameConfig.class).autowire();\n\t\t// @formatter:off\n\t\tCookie rememberMe = this.mvc.perform(post(\"/login\").with(rememberMeLogin()))\n\t\t\t\t.andReturn()\n\t\t\t\t.getResponse()\n\t\t\t\t.getCookie(\"rememberMe\");\n\t\t// @formatter:on\n\t\tassertThat(rememberMe).isNotNull();\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenGlobalUserDetailsServiceDeclaredThenMatchesNamespace() throws Exception {\n\t\tDefaultsUserDetailsServiceWithDaoConfig.USERDETAILS_SERVICE = mock(UserDetailsService.class);\n\t\tthis.spring.register(DefaultsUserDetailsServiceWithDaoConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/login\").with(rememberMeLogin()));\n\t\tverify(DefaultsUserDetailsServiceWithDaoConfig.USERDETAILS_SERVICE).loadUserByUsername(\"user\");\n\t}\n\n\t@Test\n\tpublic void rememberMeLoginWhenUserDetailsServiceDeclaredThenMatchesNamespace() throws Exception {\n\t\tUserServiceRefConfig.USERDETAILS_SERVICE = mock(UserDetailsService.class);\n\t\tthis.spring.register(UserServiceRefConfig.class).autowire();\n\t\tUser user = new User(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tgiven(UserServiceRefConfig.USERDETAILS_SERVICE.loadUserByUsername(\"user\")).willReturn(user);\n\t\tthis.mvc.perform(post(\"/login\").with(rememberMeLogin()));\n\t\tverify(UserServiceRefConfig.USERDETAILS_SERVICE).loadUserByUsername(\"user\");\n\t}\n\n\tstatic RequestPostProcessor rememberMeLogin() {\n\t\treturn rememberMeLogin(\"remember-me\", true);\n\t}\n\n\tstatic RequestPostProcessor rememberMeLogin(String parameterName, boolean parameterValue) {\n\t\treturn (request) -> {\n\t\t\tcsrf().postProcessRequest(request);\n\t\t\trequest.setParameter(\"username\", \"user\");\n\t\t\trequest.setParameter(\"password\", \"password\");\n\t\t\trequest.setParameter(parameterName, String.valueOf(parameterValue));\n\t\t\treturn request;\n\t\t};\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeConfig extends UsersConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tinterface RememberMeServicesWithoutLogoutHandler extends RememberMeServices {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeServicesRefConfig {\n\n\t\tstatic RememberMeServices REMEMBER_ME_SERVICES;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.rememberMeServices(REMEMBER_ME_SERVICES));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthSuccessConfig extends UsersConfig {\n\n\t\tstatic AuthenticationSuccessHandler SUCCESS_HANDLER;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.authenticationSuccessHandler(SUCCESS_HANDLER));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WithoutKeyConfig extends UsersConfig {\n\n\t\t@Bean\n\t\t@Order(0)\n\t\tSecurityFilterChain withoutKeyFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(\"/without-key/**\")\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginProcessingUrl(\"/without-key/login\"))\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain keyFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.key(\"KeyConfig\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class TokenRepositoryRefConfig extends UsersConfig {\n\n\t\tstatic PersistentTokenRepository TOKEN_REPOSITORY;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl()\n\t\t\t// tokenRepository.setDataSource(dataSource);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.tokenRepository(TOKEN_REPOSITORY));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class TokenValiditySecondsConfig extends UsersConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.tokenValiditySeconds(314));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class UseSecureCookieConfig extends UsersConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.useSecureCookie(true));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeParameterConfig extends UsersConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.rememberMeParameter(\"rememberMe\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeCookieNameConfig extends UsersConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.rememberMeCookieName(\"rememberMe\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class DefaultsUserDetailsServiceWithDaoConfig {\n\n\t\tstatic UserDetailsService USERDETAILS_SERVICE;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn USERDETAILS_SERVICE;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class UserServiceRefConfig extends UsersConfig {\n\n\t\tstatic UserDetailsService USERDETAILS_SERVICE;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.userDetailsService(USERDETAILS_SERVICE));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tstatic class UsersConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t\t\t.build());\n\t\t\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class SecurityController {\n\n\t\t@GetMapping(\"/authentication-class\")\n\t\tString authenticationClass(Authentication authentication) {\n\t\t\treturn authentication.getClass().getName();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceSessionManagementTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.security.Principal;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.core.session.SessionRegistryImpl;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.SessionFixationProtectionEvent;\nimport org.springframework.security.web.session.InvalidSessionStrategy;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class NamespaceSessionManagementTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void authenticateWhenDefaultSessionManagementThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(SessionManagementConfig.class, BasicController.class, UserDetailsServiceConfig.class)\n\t\t\t.autowire();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tString sessionId = session.getId();\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\").session(session).with(httpBasic(\"user\", \"password\"));\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(request)\n\t\t\t\t.andExpect(session())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false).getId()).isNotEqualTo(sessionId);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUsingInvalidSessionUrlThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomSessionManagementConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder authRequest = get(\"/auth\").with((request) -> {\n\t\t\trequest.setRequestedSessionIdValid(false);\n\t\t\trequest.setRequestedSessionId(\"id\");\n\t\t\treturn request;\n\t\t});\n\t\tthis.mvc.perform(authRequest).andExpect(redirectedUrl(\"/invalid-session\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUsingExpiredUrlThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomSessionManagementConfig.class).autowire();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tSessionInformation sessionInformation = new SessionInformation(new Object(), session.getId(), new Date(0));\n\t\tsessionInformation.expireNow();\n\t\tSessionRegistry sessionRegistry = this.spring.getContext().getBean(SessionRegistry.class);\n\t\tgiven(sessionRegistry.getSessionInformation(session.getId())).willReturn(sessionInformation);\n\t\tthis.mvc.perform(get(\"/auth\").session(session)).andExpect(redirectedUrl(\"/expired-session\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUsingMaxSessionsThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomSessionManagementConfig.class, BasicController.class, UserDetailsServiceConfig.class)\n\t\t\t.autowire();\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\"))).andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t.andExpect(redirectedUrl(\"/session-auth-error\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUsingFailureUrlThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomSessionManagementConfig.class, BasicController.class, UserDetailsServiceConfig.class)\n\t\t\t.autowire();\n\t\tMockHttpServletRequest mock = spy(MockHttpServletRequest.class);\n\t\tmock.setSession(new MockHttpSession());\n\t\tgiven(mock.changeSessionId()).willThrow(SessionAuthenticationException.class);\n\t\tmock.setMethod(\"GET\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder authRequest = get(\"/auth\")\n\t\t\t\t.with((request) -> mock)\n\t\t\t\t.with(httpBasic(\"user\", \"password\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(authRequest).andExpect(redirectedUrl(\"/session-auth-error\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUsingSessionRegistryThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(CustomSessionManagementConfig.class, BasicController.class, UserDetailsServiceConfig.class)\n\t\t\t.autowire();\n\t\tSessionRegistry sessionRegistry = this.spring.getContext().getBean(SessionRegistry.class);\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\").with(httpBasic(\"user\", \"password\"));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverify(sessionRegistry).registerNewSession(any(String.class), any(Object.class));\n\t}\n\n\t// gh-3371\n\t@Test\n\tpublic void authenticateWhenUsingCustomInvalidSessionStrategyThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(InvalidSessionStrategyConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder authRequest = get(\"/auth\").with((request) -> {\n\t\t\trequest.setRequestedSessionIdValid(false);\n\t\t\trequest.setRequestedSessionId(\"id\");\n\t\t\treturn request;\n\t\t});\n\t\tthis.mvc.perform(authRequest).andExpect(status().isOk());\n\t\tverifyBean(InvalidSessionStrategy.class).onInvalidSessionDetected(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUsingCustomSessionAuthenticationStrategyThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(RefsSessionManagementConfig.class, BasicController.class, UserDetailsServiceConfig.class)\n\t\t\t.autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\").with(httpBasic(\"user\", \"password\"));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t\tverifyBean(SessionAuthenticationStrategy.class).onAuthentication(any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNoSessionFixationProtectionThenMatchesNamespace() throws Exception {\n\t\tthis.spring\n\t\t\t.register(SFPNoneSessionManagementConfig.class, BasicController.class, UserDetailsServiceConfig.class)\n\t\t\t.autowire();\n\t\tMockHttpSession givenSession = new MockHttpSession();\n\t\tString givenSessionId = givenSession.getId();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\")\n\t\t\t\t.session(givenSession)\n\t\t\t\t.with(httpBasic(\"user\", \"password\"));\n\t\tMockHttpSession resultingSession = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\t// @formatter:on\n\t\tassertThat(givenSessionId).isEqualTo(resultingSession.getId());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMigrateSessionFixationProtectionThenMatchesNamespace() throws Exception {\n\t\tthis.spring\n\t\t\t.register(SFPMigrateSessionManagementConfig.class, BasicController.class, UserDetailsServiceConfig.class)\n\t\t\t.autowire();\n\t\tMockHttpSession givenSession = new MockHttpSession();\n\t\tString givenSessionId = givenSession.getId();\n\t\tgivenSession.setAttribute(\"name\", \"value\");\n\t\t// @formatter:off\n\t\tMockHttpSession resultingSession = (MockHttpSession) this.mvc.perform(get(\"/auth\")\n\t\t\t\t.session(givenSession)\n\t\t\t\t.with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\t// @formatter:on\n\t\tassertThat(givenSessionId).isNotEqualTo(resultingSession.getId());\n\t\tassertThat(resultingSession.getAttribute(\"name\")).isEqualTo(\"value\");\n\t}\n\n\t// SEC-2913\n\t@Test\n\tpublic void authenticateWhenUsingSessionFixationProtectionThenUsesNonNullEventPublisher() throws Exception {\n\t\tthis.spring.register(SFPPostProcessedConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\")\n\t\t\t\t.session(new MockHttpSession())\n\t\t\t\t.with(httpBasic(\"user\", \"password\"));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(status().isNotFound());\n\t\tverifyBean(MockEventListener.class).onApplicationEvent(any(SessionFixationProtectionEvent.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNewSessionFixationProtectionThenMatchesNamespace() throws Exception {\n\t\tthis.spring.register(SFPNewSessionSessionManagementConfig.class, UserDetailsServiceConfig.class).autowire();\n\t\tMockHttpSession givenSession = new MockHttpSession();\n\t\tString givenSessionId = givenSession.getId();\n\t\tgivenSession.setAttribute(\"name\", \"value\");\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\").session(givenSession).with(httpBasic(\"user\", \"password\"));\n\t\t// @formatter:off\n\t\tMockHttpSession resultingSession = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(status().isNotFound())\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\t// @formatter:on\n\t\tassertThat(givenSessionId).isNotEqualTo(resultingSession.getId());\n\t\tassertThat(resultingSession.getAttribute(\"name\")).isNull();\n\t}\n\n\tprivate <T> T verifyBean(Class<T> clazz) {\n\t\treturn verify(this.spring.getContext().getBean(clazz));\n\t}\n\n\tprivate static SessionResultMatcher session() {\n\t\treturn new SessionResultMatcher();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SessionManagementConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.sessionManagement((sessions) -> sessions\n\t\t\t\t\t.requireExplicitAuthenticationStrategy(false)\n\t\t\t\t)\n\t\t\t\t.httpBasic(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomSessionManagementConfig {\n\n\t\tSessionRegistry sessionRegistry = spy(SessionRegistryImpl.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.invalidSessionUrl(\"/invalid-session\") // session-management@invalid-session-url\n\t\t\t\t\t.sessionAuthenticationErrorUrl(\"/session-auth-error\") // session-management@session-authentication-error-url\n\t\t\t\t\t.maximumSessions(1) // session-management/concurrency-control@max-sessions\n\t\t\t\t\t.maxSessionsPreventsLogin(true) // session-management/concurrency-control@error-if-maximum-exceeded\n\t\t\t\t\t.expiredUrl(\"/expired-session\") // session-management/concurrency-control@expired-url\n\t\t\t\t\t.sessionRegistry(sessionRegistry()));\n\t\t\treturn http.build(); // session-management/concurrency-control@session-registry-ref\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSessionRegistry sessionRegistry() {\n\t\t\treturn this.sessionRegistry;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvalidSessionStrategyConfig {\n\n\t\tInvalidSessionStrategy invalidSessionStrategy = mock(InvalidSessionStrategy.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.invalidSessionStrategy(invalidSessionStrategy()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tInvalidSessionStrategy invalidSessionStrategy() {\n\t\t\treturn this.invalidSessionStrategy;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RefsSessionManagementConfig {\n\n\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionAuthenticationStrategy(sessionAuthenticationStrategy()))\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy() {\n\t\t\treturn this.sessionAuthenticationStrategy;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SFPNoneSessionManagementConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy()))\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SFPMigrateSessionManagementConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.requireExplicitAuthenticationStrategy(false))\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SFPPostProcessedConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((sessions) -> sessions\n\t\t\t\t\t\t.requireExplicitAuthenticationStrategy(false)\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tMockEventListener eventListener() {\n\t\t\treturn spy(new MockEventListener());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SFPNewSessionSessionManagementConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((sessions) -> sessions\n\t\t\t\t\t\t.sessionFixation().newSession()\n\t\t\t\t\t\t.requireExplicitAuthenticationStrategy(false)\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tstatic class MockEventListener implements ApplicationListener<SessionFixationProtectionEvent> {\n\n\t\tList<SessionFixationProtectionEvent> events = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic void onApplicationEvent(SessionFixationProtectionEvent event) {\n\t\t\tthis.events.add(event);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UserDetailsServiceConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t\t\t.build());\n\t\t\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@GetMapping(\"/\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t\t@GetMapping(\"/auth\")\n\t\tString auth(Principal principal) {\n\t\t\treturn principal.getName();\n\t\t}\n\n\t}\n\n\tprivate static class SessionResultMatcher implements ResultMatcher {\n\n\t\tprivate String id;\n\n\t\tprivate Boolean valid;\n\n\t\tprivate Boolean exists = true;\n\n\t\tResultMatcher exists(boolean exists) {\n\t\t\tthis.exists = exists;\n\t\t\treturn this;\n\t\t}\n\n\t\tResultMatcher valid(boolean valid) {\n\t\t\tthis.valid = valid;\n\t\t\treturn this.exists(true);\n\t\t}\n\n\t\tResultMatcher id(String id) {\n\t\t\tthis.id = id;\n\t\t\treturn this.exists(true);\n\t\t}\n\n\t\t@Override\n\t\tpublic void match(MvcResult result) {\n\t\t\tif (!this.exists) {\n\t\t\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession(false);\n\t\t\tif (this.valid != null) {\n\t\t\t\tif (this.valid) {\n\t\t\t\t\tassertThat(session.isInvalid()).isFalse();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tassertThat(session.isInvalid()).isTrue();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.id != null) {\n\t\t\t\tassertThat(session.getId()).isEqualTo(this.id);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/PasswordManagementConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link PasswordManagementConfigurer}.\n *\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class PasswordManagementConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void whenChangePasswordPageNotSetThenDefaultChangePasswordPageUsed() throws Exception {\n\t\tthis.spring.register(PasswordManagementWithDefaultChangePasswordPageConfig.class).autowire();\n\n\t\tthis.mvc.perform(get(\"/.well-known/change-password\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/change-password\"));\n\t}\n\n\t@Test\n\tpublic void whenChangePasswordPageSetThenSpecifiedChangePasswordPageUsed() throws Exception {\n\t\tthis.spring.register(PasswordManagementWithCustomChangePasswordPageConfig.class).autowire();\n\n\t\tthis.mvc.perform(get(\"/.well-known/change-password\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/custom-change-password-page\"));\n\t}\n\n\t@Test\n\tpublic void whenSettingNullChangePasswordPage() {\n\t\tPasswordManagementConfigurer configurer = new PasswordManagementConfigurer();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> configurer.changePasswordPage(null))\n\t\t\t.withMessage(\"changePasswordPage cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void whenSettingEmptyChangePasswordPage() {\n\t\tPasswordManagementConfigurer configurer = new PasswordManagementConfigurer();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> configurer.changePasswordPage(\"\"))\n\t\t\t.withMessage(\"changePasswordPage cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void whenSettingBlankChangePasswordPage() {\n\t\tPasswordManagementConfigurer configurer = new PasswordManagementConfigurer();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> configurer.changePasswordPage(\" \"))\n\t\t\t.withMessage(\"changePasswordPage cannot be empty\");\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordManagementWithDefaultChangePasswordPageConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.passwordManagement(withDefaults())\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PasswordManagementWithCustomChangePasswordPageConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.passwordManagement((passwordManagement) -> passwordManagement\n\t\t\t\t\t\t.changePasswordPage(\"/custom-change-password-page\")\n\t\t\t\t\t)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/PermitAllSupportTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class PermitAllSupportTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Test\n\tpublic void performWhenUsingPermitAllExactUrlRequestMatcherThenMatchesExactUrl() throws Exception {\n\t\tthis.spring.register(PermitAllConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/app/xyz\").contextPath(\"/app\");\n\t\tthis.mvc.perform(request).andExpect(status().isNotFound());\n\t\tMockHttpServletRequestBuilder getWithQuery = get(\"/app/xyz?def\").contextPath(\"/app\");\n\t\tthis.mvc.perform(getWithQuery).andExpect(status().isFound());\n\t\tMockHttpServletRequestBuilder postWithQueryAndCsrf = post(\"/app/abc?def\").with(csrf()).contextPath(\"/app\");\n\t\tthis.mvc.perform(postWithQueryAndCsrf).andExpect(status().isNotFound());\n\t\tMockHttpServletRequestBuilder getWithCsrf = get(\"/app/abc\").with(csrf()).contextPath(\"/app\");\n\t\tthis.mvc.perform(getWithCsrf).andExpect(status().isFound());\n\t}\n\n\t@Test\n\tpublic void performWhenUsingPermitAllExactUrlRequestMatcherThenMatchesExactUrlWithAuthorizeHttp() throws Exception {\n\t\tthis.spring.register(PermitAllConfigAuthorizeHttpRequests.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/app/xyz\").contextPath(\"/app\");\n\t\tthis.mvc.perform(request).andExpect(status().isNotFound());\n\t\tMockHttpServletRequestBuilder getWithQuery = get(\"/app/xyz?def\").contextPath(\"/app\");\n\t\tthis.mvc.perform(getWithQuery).andExpect(status().isFound());\n\t\tMockHttpServletRequestBuilder postWithQueryAndCsrf = post(\"/app/abc?def\").with(csrf()).contextPath(\"/app\");\n\t\tthis.mvc.perform(postWithQueryAndCsrf).andExpect(status().isNotFound());\n\t\tMockHttpServletRequestBuilder getWithCsrf = get(\"/app/abc\").with(csrf()).contextPath(\"/app\");\n\t\tthis.mvc.perform(getWithCsrf).andExpect(status().isFound());\n\t}\n\n\t@Test\n\tpublic void configureWhenNotAuthorizeRequestsThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire())\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"permitAll only works with HttpSecurity.authorizeHttpRequests(). Please define one.\");\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PermitAllConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginPage(\"/xyz\").permitAll()\n\t\t\t\t\t.loginProcessingUrl(\"/abc?def\").permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PermitAllConfigAuthorizeHttpRequests {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginPage(\"/xyz\").permitAll()\n\t\t\t\t\t.loginProcessingUrl(\"/abc?def\").permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NoAuthorizedUrlsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.permitAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/PortMapperConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class PortMapperConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tpublic void requestWhenPortMapperTwiceInvokedThenDoesNotOverride() throws Exception {\n\t\tthis.spring.register(InvokeTwiceDoesNotOverride.class).autowire();\n\t\tthis.mockMvc.perform(get(\"http://localhost:543\")).andExpect(redirectedUrl(\"https://localhost:123\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenPortMapperHttpMapsToInLambdaThenRedirectsToHttpsPort() throws Exception {\n\t\tthis.spring.register(HttpMapsToInLambdaConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"http://localhost:543\")).andExpect(redirectedUrl(\"https://localhost:123\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomPortMapperInLambdaThenRedirectsToHttpsPort() throws Exception {\n\t\tthis.spring.register(CustomPortMapperInLambdaConfig.class).autowire();\n\t\tthis.mockMvc.perform(get(\"http://localhost:543\")).andExpect(redirectedUrl(\"https://localhost:123\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvokeTwiceDoesNotOverride {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requiresChannel((channel) -> channel\n\t\t\t\t\t.anyRequest().requiresSecure())\n\t\t\t\t.portMapper((mapper) -> mapper\n\t\t\t\t\t.http(543).mapsTo(123))\n\t\t\t\t.portMapper(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpMapsToInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requiresChannel((requiresChannel) -> requiresChannel\n\t\t\t\t\t.anyRequest().requiresSecure()\n\t\t\t\t)\n\t\t\t\t.portMapper((portMapper) -> portMapper\n\t\t\t\t\t\t.http(543).mapsTo(123)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomPortMapperInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tPortMapperImpl customPortMapper = new PortMapperImpl();\n\t\t\tcustomPortMapper.setPortMappings(Collections.singletonMap(\"543\", \"123\"));\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requiresChannel((requiresChannel) -> requiresChannel\n\t\t\t\t\t\t.anyRequest().requiresSecure()\n\t\t\t\t)\n\t\t\t\t.portMapper((portMapper) -> portMapper\n\t\t\t\t\t\t.portMapper(customPortMapper)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Collections;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.UnsatisfiedDependencyException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;\nimport org.springframework.security.web.context.HttpRequestResponseHolder;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.hamcrest.Matchers.startsWith;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\n\n/**\n * Tests for {@link RememberMeConfigurer}\n *\n * @author Rob Winch\n * @author Eddú Meléndez\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class RememberMeConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void postWhenNoUserDetailsServiceThenException() {\n\t\tassertThatExceptionOfType(UnsatisfiedDependencyException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(NullUserDetailsConfig.class).autowire())\n\t\t\t.withMessageContaining(\"userDetailsService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnRememberMeAuthenticationFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(this.spring.getContext().getBean(ObjectPostProcessorConfig.class).objectPostProcessor)\n\t\t\t.postProcess(any(RememberMeAuthenticationFilter.class));\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenInvokedTwiceThenUsesOriginalUserDetailsService() throws Exception {\n\t\tgiven(DuplicateDoesNotOverrideConfig.userDetailsService.loadUserByUsername(anyString()))\n\t\t\t.willReturn(new User(\"user\", \"password\", Collections.emptyList()));\n\t\tthis.spring.register(DuplicateDoesNotOverrideConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/\")\n\t\t\t\t.with(httpBasic(\"user\", \"password\"))\n\t\t\t\t.param(\"remember-me\", \"true\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request);\n\t\tverify(DuplicateDoesNotOverrideConfig.userDetailsService).loadUserByUsername(\"user\");\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenUserDetailsServiceNotConfiguredThenUsesBean() throws Exception {\n\t\tthis.spring.register(UserDetailsServiceBeanConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(\"/login\").with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\"))\n\t\t\t.andReturn();\n\t\tCookie rememberMeCookie = mvcResult.getResponse().getCookie(\"remember-me\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/abc\").cookie(rememberMeCookie);\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher remembermeAuthentication = authenticated()\n\t\t\t\t.withAuthentication((auth) -> assertThat(auth).isInstanceOf(RememberMeAuthenticationToken.class));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(remembermeAuthentication);\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.register(UserDetailsServiceBeanConfig.class, SecurityContextChangedListenerConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(\"/login\").with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\"))\n\t\t\t.andReturn();\n\t\tCookie rememberMeCookie = mvcResult.getResponse().getCookie(\"remember-me\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/abc\").cookie(rememberMeCookie);\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher remembermeAuthentication = authenticated()\n\t\t\t\t.withAuthentication((auth) -> assertThat(auth).isInstanceOf(RememberMeAuthenticationToken.class));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(remembermeAuthentication);\n\t\tverify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void loginWhenRememberMeTrueThenRespondsWithRememberMeCookie() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(cookie().exists(\"remember-me\"));\n\t}\n\n\t@Test\n\tpublic void getWhenRememberMeCookieThenAuthenticationIsRememberMeAuthenticationToken() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(\"/login\").with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\"))\n\t\t\t.andReturn();\n\t\tCookie rememberMeCookie = mvcResult.getResponse().getCookie(\"remember-me\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/abc\").cookie(rememberMeCookie);\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher remembermeAuthentication = authenticated()\n\t\t\t\t.withAuthentication((auth) -> assertThat(auth).isInstanceOf(RememberMeAuthenticationToken.class));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(remembermeAuthentication);\n\t}\n\n\t@Test\n\tpublic void logoutWhenRememberMeCookieThenAuthenticationIsRememberMeCookieExpired() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\");\n\t\t// @formatter:on\n\t\tMvcResult mvcResult = this.mvc.perform(loginRequest).andReturn();\n\t\tCookie rememberMeCookie = mvcResult.getResponse().getCookie(\"remember-me\");\n\t\tHttpSession session = mvcResult.getRequest().getSession();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.cookie(rememberMeCookie)\n\t\t\t\t.session((MockHttpSession) session);\n\t\tthis.mvc.perform(logoutRequest)\n\t\t\t\t.andExpect(redirectedUrl(\"/login?logout\"))\n\t\t\t\t.andExpect(cookie().maxAge(\"remember-me\", 0));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenRememberMeCookieAndLoggedOutThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.register(RememberMeConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\");\n\t\t// @formatter:on\n\t\tMvcResult loginMvcResult = this.mvc.perform(loginRequest).andReturn();\n\t\tCookie rememberMeCookie = loginMvcResult.getResponse().getCookie(\"remember-me\");\n\t\tHttpSession session = loginMvcResult.getRequest().getSession();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder logoutRequest = post(\"/logout\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.cookie(rememberMeCookie)\n\t\t\t\t.session((MockHttpSession) session);\n\t\t// @formatter:on\n\t\tMvcResult logoutMvcResult = this.mvc.perform(logoutRequest).andReturn();\n\t\tCookie expiredRememberMeCookie = logoutMvcResult.getResponse().getCookie(\"remember-me\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder expiredRequest = get(\"/abc\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.cookie(expiredRememberMeCookie);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(expiredRequest).andExpect(redirectedUrl(\"/login\"));\n\t}\n\n\t@Test\n\tpublic void loginWhenRememberMeConfiguredInLambdaThenRespondsWithRememberMeCookie() throws Exception {\n\t\tthis.spring.register(RememberMeInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(cookie().exists(\"remember-me\"));\n\t}\n\n\t@Test\n\tpublic void loginWhenRememberMeTrueAndCookieDomainThenRememberMeCookieHasDomain() throws Exception {\n\t\tthis.spring.register(RememberMeCookieDomainConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\");\n\t\tthis.mvc.perform(request).\n\t\t\t\tandExpect(cookie().exists(\"remember-me\"))\n\t\t\t\t.andExpect(cookie().domain(\"remember-me\", \"spring.io\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenRememberMeTrueAndCookieDomainInLambdaThenRememberMeCookieHasDomain() throws Exception {\n\t\tthis.spring.register(RememberMeCookieDomainInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\");\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(cookie().exists(\"remember-me\"))\n\t\t\t\t.andExpect(cookie().domain(\"remember-me\", \"spring.io\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenRememberMeCookieNameAndRememberMeServicesThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(RememberMeCookieNameAndRememberMeServicesConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class)\n\t\t\t.withMessageContaining(\"Can not set rememberMeCookieName and custom rememberMeServices.\");\n\t}\n\n\t@Test\n\tpublic void getWhenRememberMeCookieAndNoKeyConfiguredThenKeyFromRememberMeServicesIsUsed() throws Exception {\n\t\tthis.spring.register(FallbackRememberMeKeyConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\");\n\t\t// @formatter:on\n\t\tMvcResult mvcResult = this.mvc.perform(loginRequest).andReturn();\n\t\tCookie rememberMeCookie = mvcResult.getResponse().getCookie(\"remember-me\");\n\t\tMockHttpServletRequestBuilder requestWithRememberme = get(\"/abc\").cookie(rememberMeCookie);\n\t\t// @formatter:off\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher remembermeAuthentication = authenticated()\n\t\t\t\t.withAuthentication((auth) -> assertThat(auth).isInstanceOf(RememberMeAuthenticationToken.class));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(requestWithRememberme).andExpect(remembermeAuthentication);\n\t}\n\n\t// gh-13104\n\t@Test\n\tpublic void getWhenCustomSecurityContextRepositoryThenUses() throws Exception {\n\t\tthis.spring.register(SecurityContextRepositoryConfig.class).autowire();\n\t\tSecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(\"/login\").with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.param(\"remember-me\", \"true\"))\n\t\t\t.andReturn();\n\t\tCookie rememberMeCookie = mvcResult.getResponse().getCookie(\"remember-me\");\n\t\treset(repository);\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/abc\").cookie(rememberMeCookie);\n\t\tSecurityMockMvcResultMatchers.AuthenticatedMatcher remembermeAuthentication = authenticated()\n\t\t\t\t.withAuthentication((auth) -> assertThat(auth).isInstanceOf(RememberMeAuthenticationToken.class));\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(remembermeAuthentication);\n\t\tverify(repository).saveContext(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void rememberMeExpiresSessionWhenSessionManagementMaximumSessionsExceeds() throws Exception {\n\t\tthis.spring.register(RememberMeMaximumSessionsConfig.class).autowire();\n\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\").with(csrf())\n\t\t\t.param(\"username\", \"user\")\n\t\t\t.param(\"password\", \"password\")\n\t\t\t.param(\"remember-me\", \"true\");\n\t\tMvcResult mvcResult = this.mvc.perform(loginRequest).andReturn();\n\t\tCookie rememberMeCookie = mvcResult.getResponse().getCookie(\"remember-me\");\n\t\tHttpSession session = mvcResult.getRequest().getSession();\n\n\t\tMockHttpServletRequestBuilder exceedsMaximumSessionsRequest = get(\"/abc\").cookie(rememberMeCookie);\n\t\tthis.mvc.perform(exceedsMaximumSessionsRequest);\n\n\t\tMockHttpServletRequestBuilder sessionExpiredRequest = get(\"/abc\").cookie(rememberMeCookie)\n\t\t\t.session((MockHttpSession) session);\n\t\tthis.mvc.perform(sessionExpiredRequest)\n\t\t\t.andExpect(content().string(startsWith(\"This session has been expired\")));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullUserDetailsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configure(AuthenticationManagerBuilder auth) {\n\t\t\tUser user = (User) PasswordEncodedUser.user();\n\t\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(\n\t\t\t\t\tnew InMemoryUserDetailsManager(Collections.singletonList(user)));\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.authenticationProvider(provider);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.userDetailsService(new AuthenticationManagerBuilder(this.objectPostProcessor).getDefaultUserDetailsService()));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn this.objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DuplicateDoesNotOverrideConfig {\n\n\t\tstatic UserDetailsService userDetailsService = mock(UserDetailsService.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.userDetailsService(userDetailsService))\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t\t\t.build()\n\t\t\t\t\t// @formatter:on\n\t\t\t);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class UserDetailsServiceBeanConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService customUserDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeCookieDomainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.rememberMeCookieDomain(\"spring.io\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeCookieDomainInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((rememberMe) -> rememberMe\n\t\t\t\t\t\t.rememberMeCookieDomain(\"spring.io\")\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeCookieNameAndRememberMeServicesConfig {\n\n\t\tstatic RememberMeServices REMEMBER_ME = mock(RememberMeServices.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.rememberMeCookieName(\"SPRING_COOKIE_DOMAIN\")\n\t\t\t\t\t.rememberMeCookieDomain(\"spring.io\")\n\t\t\t\t\t.rememberMeServices(REMEMBER_ME));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(PasswordEncodedUser.user());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FallbackRememberMeKeyConfig extends RememberMeConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().hasRole(\"USER\"))\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe((me) -> me\n\t\t\t\t\t.rememberMeServices(new TokenBasedRememberMeServices(\"key\", userDetailsService())));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RememberMeMaximumSessionsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t\t\t.anyRequest().hasRole(\"USER\")\n\t\t\t\t\t)\n\t\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t\t\t\t\t.maximumSessions(1)\n\t\t\t\t\t)\n\t\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityContextRepositoryConfig {\n\n\t\tprivate SecurityContextRepository repository = spy(new SpySecurityContextRepository());\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.securityContext((context) -> context.securityContextRepository(this.repository))\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.rememberMe(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSecurityContextRepository securityContextRepository() {\n\t\t\treturn this.repository;\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t\tprivate static class SpySecurityContextRepository implements SecurityContextRepository {\n\n\t\t\tSecurityContextRepository delegate = new HttpSessionSecurityContextRepository();\n\n\t\t\t@Override\n\t\t\tpublic SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {\n\t\t\t\treturn this.delegate.loadContext(requestResponseHolder);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {\n\t\t\t\tthis.delegate.saveContext(context, request, response);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean containsContext(HttpServletRequest request) {\n\t\t\t\treturn this.delegate.containsContext(request);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.mock.web.MockMultipartFile;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.web.servlet.RequestCacheResultMatcher;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.savedrequest.NullRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.RequestCacheAwareFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.RequestBuilder;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\n\n/**\n * Tests for {@link RequestCacheConfigurer}\n *\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class RequestCacheConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnExceptionTranslationFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(RequestCacheAwareFilter.class));\n\t}\n\n\t@Test\n\tpublic void getWhenInvokingExceptionHandlingTwiceThenOriginalEntryPointUsed() throws Exception {\n\t\tthis.spring.register(InvokeTwiceDoesNotOverrideConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(InvokeTwiceDoesNotOverrideConfig.requestCache).getMatchingRequest(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void getWhenBookmarkedUrlIsFaviconIcoThenPostAuthenticationRedirectsToRoot() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(get(\"/favicon.ico\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\t// ignores favicon.ico\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void getWhenBookmarkedUrlIsFaviconPngThenPostAuthenticationRedirectsToRoot() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(get(\"/favicon.png\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\t// ignores favicon.png\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t// SEC-2321\n\t@Test\n\tpublic void getWhenBookmarkedRequestIsApplicationJsonThenPostAuthenticationRedirectsToRoot() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/messages\").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON);\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\t// ignores application/json\n\t\t// This is desirable since JSON requests are typically not invoked directly from\n\t\t// the browser and we don't want the browser to replay them\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t// SEC-2321\n\t@Test\n\tpublic void getWhenBookmarkedRequestIsXRequestedWithThenPostAuthenticationRedirectsToRoot() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder xRequestedWith = get(\"/messages\")\n\t\t\t\t.header(\"X-Requested-With\", \"XMLHttpRequest\");\n\t\tMockHttpSession session = (MockHttpSession) this.mvc\n\t\t\t\t.perform(xRequestedWith)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t\t// This is desirable since XHR requests are typically not invoked directly from\n\t\t// the browser and we don't want the browser to replay them\n\t}\n\n\t@Test\n\tpublic void getWhenBookmarkedRequestIsTextEventStreamThenPostAuthenticationRedirectsToRoot() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/messages\").header(HttpHeaders.ACCEPT,\n\t\t\t\tMediaType.TEXT_EVENT_STREAM);\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\t// ignores text/event-stream\n\t\t// This is desirable since event-stream requests are typically not invoked\n\t\t// directly from the browser and we don't want the browser to replay them\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void getWhenBookmarkedRequestIsWebSocketThenPostAuthenticationRedirectsToRoot() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/messages\").header(\"Upgrade\", \"websocket\");\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\t// ignores websocket\n\t\t// This is desirable since websocket requests are typically not invoked\n\t\t// directly from the browser and we don't want the browser to replay them\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void getWhenBookmarkedRequestIsAllMediaTypeThenPostAuthenticationRemembers() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/messages\").header(HttpHeaders.ACCEPT, MediaType.ALL);\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(RequestCacheResultMatcher.redirectToCachedRequest());\n\t}\n\n\t@Test\n\tpublic void getWhenBookmarkedRequestIsTextHtmlThenPostAuthenticationRemembers() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/messages\").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(RequestCacheResultMatcher.redirectToCachedRequest());\n\t}\n\n\t@Test\n\tpublic void getWhenBookmarkedRequestIsChromeThenPostAuthenticationRemembers() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/messages\")\n\t\t\t\t.header(HttpHeaders.ACCEPT, \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\");\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(RequestCacheResultMatcher.redirectToCachedRequest());\n\t}\n\n\t@Test\n\tpublic void getWhenBookmarkedRequestIsRequestedWithAndroidThenPostAuthenticationRemembers() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/messages\")\n\t\t\t\t.header(\"X-Requested-With\", \"com.android\");\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(RequestCacheResultMatcher.redirectToCachedRequest());\n\t}\n\n\t// gh-6102\n\t@Test\n\tpublic void getWhenRequestCacheIsDisabledThenExceptionTranslationFilterDoesNotStoreRequest() throws Exception {\n\t\tthis.spring.register(RequestCacheDisabledConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(get(\"/bob\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t// SEC-7060\n\t@Test\n\tpublic void postWhenRequestIsMultipartThenPostAuthenticationRedirectsToRoot() throws Exception {\n\t\tthis.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();\n\t\tMockMultipartFile aFile = new MockMultipartFile(\"aFile\", \"A_FILE\".getBytes());\n\t\tMockMultipartHttpServletRequestBuilder request = multipart(\"/upload\").file(aFile);\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void getWhenRequestCacheIsDisabledInLambdaThenExceptionTranslationFilterDoesNotStoreRequest()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(RequestCacheDisabledInLambdaConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(get(\"/bob\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void getWhenRequestCacheInLambdaThenRedirectedToCachedPage() throws Exception {\n\t\tthis.spring.register(RequestCacheInLambdaConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(get(\"/bob\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(RequestCacheResultMatcher.redirectToCachedRequest());\n\t}\n\n\t@Test\n\tpublic void getWhenCustomRequestCacheInLambdaThenCustomRequestCacheUsed() throws Exception {\n\t\tthis.spring.register(CustomRequestCacheInLambdaConfig.class, DefaultSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(get(\"/bob\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void getWhenPathPatternFactoryBeanThenFaviconIcoRedirectsToRoot() throws Exception {\n\t\tthis.spring\n\t\t\t.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class, PathPatternFactoryBeanConfig.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(get(\"/favicon.ico\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession();\n\t\t// @formatter:on\n\t\t// ignores favicon.ico\n\t\tthis.mvc.perform(formLogin(session)).andExpect(redirectedUrl(\"/\"));\n\t}\n\n\tprivate static RequestBuilder formLogin(MockHttpSession session) {\n\t\t// @formatter:off\n\t\treturn post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session(session)\n\t\t\t\t.with(csrf());\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requestCache(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvokeTwiceDoesNotOverrideConfig {\n\n\t\tstatic RequestCache requestCache = mock(RequestCache.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requestCache((cache) -> cache\n\t\t\t\t\t.requestCache(requestCache))\n\t\t\t\t.requestCache(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestCacheDefaultsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestCacheDisabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.requestCache(RequestCacheConfigurer::disable);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestCacheDisabledInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.requestCache(RequestCacheConfigurer::disable);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequestCacheInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.requestCache(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomRequestCacheInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.requestCache((requestCache) -> requestCache\n\t\t\t\t\t\t.requestCache(new NullRequestCache())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultSecurityConfig {\n\n\t\t@Bean\n\t\tInMemoryUserDetailsManager userDetailsManager() {\n\t\t\t// @formatter:off\n\t\t\treturn new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t.build()\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PathPatternFactoryBeanConfig {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link HttpSecurity.RequestMatcherConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class RequestMatcherConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t// SEC-2908\n\t@Test\n\tpublic void authorizeRequestsWhenInvokedMultipleTimesThenChainsPaths() throws Exception {\n\t\tthis.spring.register(Sec2908Config.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/oauth/abc\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/api/abc\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeRequestsWhenInvokedMultipleTimesInLambdaThenChainsPaths() throws Exception {\n\t\tthis.spring.register(AuthorizeRequestInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/oauth/abc\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/api/abc\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class Sec2908Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(pathPattern(\"/api/**\")))\n\t\t\t\t.securityMatchers((security) -> security\n\t\t\t\t\t.requestMatchers(pathPattern(\"/oauth/**\")))\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthorizeRequestInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatchers((secure) -> secure\n\t\t\t\t\t\t.requestMatchers(pathPattern(\"/api/**\"))\n\t\t\t\t)\n\t\t\t\t.securityMatchers((securityMatchers) -> securityMatchers\n\t\t\t\t\t\t.requestMatchers(pathPattern(\"/oauth/**\"))\n\t\t\t\t)\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.TestDeferredSecurityContext;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.builders.TestHttpSecurities;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.context.HttpRequestResponseHolder;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.NullSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n * Tests for {@link SecurityContextConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SecurityContextConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnSecurityContextPersistenceFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(SecurityContextHolderFilter.class));\n\t}\n\n\t@Test\n\tpublic void securityContextWhenInvokedTwiceThenUsesOriginalSecurityContextRepository() throws Exception {\n\t\tthis.spring.register(DuplicateDoesNotOverrideConfig.class).autowire();\n\t\tgiven(DuplicateDoesNotOverrideConfig.SCR.loadDeferredContext(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new TestDeferredSecurityContext(mock(SecurityContext.class), false));\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(DuplicateDoesNotOverrideConfig.SCR).loadDeferredContext(any(HttpServletRequest.class));\n\t}\n\n\t// SEC-2932\n\t@Test\n\tpublic void securityContextWhenSecurityContextRepositoryNotConfiguredThenDoesNotThrowException() throws Exception {\n\t\tthis.spring.register(SecurityContextRepositoryDefaultsSecurityContextRepositoryConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenSecurityContextWithDefaultsInLambdaThenSessionIsCreated() throws Exception {\n\t\tthis.spring.register(SecurityContextWithDefaultsInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(formLogin()).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenSecurityContextDisabledInLambdaThenContextNotSavedInSession() throws Exception {\n\t\tthis.spring.register(SecurityContextDisabledInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(formLogin()).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenNullSecurityContextRepositoryInLambdaThenContextNotSavedInSession() throws Exception {\n\t\tthis.spring.register(NullSecurityContextRepositoryInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(formLogin()).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void requireExplicitSave() throws Exception {\n\t\tHttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository();\n\t\tSpringTestContext testContext = this.spring.register(RequireExplicitSaveConfig.class);\n\t\ttestContext.autowire();\n\t\tFilterChainProxy filterChainProxy = testContext.getContext().getBean(FilterChainProxy.class);\n\t\t// @formatter:off\n\t\tList<Class<? extends Filter>> filterTypes = filterChainProxy.getFilters(\"/\")\n\t\t\t\t.stream()\n\t\t\t\t.map(Filter::getClass)\n\t\t\t\t.collect(Collectors.toList());\n\t\tassertThat(filterTypes)\n\t\t\t\t.contains(SecurityContextHolderFilter.class)\n\t\t\t\t.doesNotContain(SecurityContextPersistenceFilter.class);\n\t\t// @formatter:on\n\t\tMvcResult mvcResult = this.mvc.perform(formLogin()).andReturn();\n\t\tSecurityContext securityContext = repository\n\t\t\t.loadContext(new HttpRequestResponseHolder(mvcResult.getRequest(), mvcResult.getResponse()));\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityContext(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DuplicateDoesNotOverrideConfig {\n\n\t\tstatic SecurityContextRepository SCR = mock(SecurityContextRepository.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityContext((context) -> context\n\t\t\t\t\t.securityContextRepository(SCR))\n\t\t\t\t.securityContext(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityContextRepositoryDefaultsSecurityContextRepositoryConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tTestHttpSecurities.disableDefaults(http);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.addFilter(new WebAsyncManagerIntegrationFilter())\n\t\t\t\t.anonymous(withDefaults())\n\t\t\t\t.securityContext(withDefaults())\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().permitAll())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityContextWithDefaultsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.securityContext(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityContextDisabledInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.securityContext(AbstractHttpConfigurer::disable);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NullSecurityContextRepositoryInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t\t.securityContext((securityContext) -> securityContext\n\t\t\t\t\t\t\t\t\t.securityContextRepository(new NullSecurityContextRepository())\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RequireExplicitSaveConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.securityContext((securityContext) -> securityContext\n\t\t\t\t\t.requireExplicitSave(true)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/ServletApiConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;\nimport org.springframework.security.util.FieldUtils;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.logout.CompositeLogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.ConfigurableWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link ServletApiConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n * @author Onur Kagan Ozcan\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class ServletApiConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnSecurityContextHolderAwareRequestFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(SecurityContextHolderAwareRequestFilter.class));\n\t}\n\n\t// SEC-2215\n\t@Test\n\tpublic void configureWhenUsingDefaultsThenAuthenticationManagerIsNotNull() {\n\t\tthis.spring.register(ServletApiConfig.class).autowire();\n\t\tassertThat(this.spring.getContext().getBean(\"customAuthenticationManager\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDefaultsThenAuthenticationEntryPointIsLogin() throws Exception {\n\t\tthis.spring.register(ServletApiConfig.class).autowire();\n\t\tthis.mvc.perform(formLogin()).andExpect(status().isFound());\n\t}\n\n\t// SEC-2926\n\t@Test\n\tpublic void configureWhenUsingDefaultsThenRolePrefixIsSet() throws Exception {\n\t\tthis.spring.register(ServletApiConfig.class, AdminController.class).autowire();\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_ADMIN\");\n\t\tMockHttpServletRequestBuilder request = get(\"/admin\").with(authentication(user));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthenticationEntryPointThenEntryPointUsed() throws Exception {\n\t\tthis.spring.register(CustomEntryPointConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(CustomEntryPointConfig.ENTRYPOINT).commence(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(AuthenticationException.class));\n\t}\n\n\t@Test\n\tpublic void servletApiWhenInvokedTwiceThenUsesOriginalRole() throws Exception {\n\t\tthis.spring.register(DuplicateInvocationsDoesNotOverrideConfig.class, AdminController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/admin\")\n\t\t\t\t.with(user(\"user\").authorities(AuthorityUtils.createAuthorityList(\"PERMISSION_ADMIN\")));\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isOk());\n\t\tSecurityMockMvcRequestPostProcessors.UserRequestPostProcessor userWithRoleAdmin = user(\"user\")\n\t\t\t\t.authorities(AuthorityUtils.createAuthorityList(\"ROLE_ADMIN\"));\n\t\tMockHttpServletRequestBuilder requestWithRoleAdmin = get(\"/admin\")\n\t\t\t\t.with(userWithRoleAdmin);\n\t\tthis.mvc.perform(requestWithRoleAdmin)\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenSharedObjectTrustResolverThenTrustResolverUsed() throws Exception {\n\t\tthis.spring.register(SharedTrustResolverConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(SharedTrustResolverConfig.TR, atLeastOnce()).isAnonymous(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenServletApiWithDefaultsInLambdaThenUsesDefaultRolePrefix() throws Exception {\n\t\tthis.spring.register(ServletApiWithDefaultsInLambdaConfig.class, AdminController.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/admin\")\n\t\t\t.with(user(\"user\").authorities(AuthorityUtils.createAuthorityList(\"ROLE_ADMIN\")));\n\t\tthis.mvc.perform(request).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tpublic void requestWhenRolePrefixInLambdaThenUsesCustomRolePrefix() throws Exception {\n\t\tthis.spring.register(RolePrefixInLambdaConfig.class, AdminController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithAdminPermission = get(\"/admin\")\n\t\t\t\t.with(user(\"user\").authorities(AuthorityUtils.createAuthorityList(\"PERMISSION_ADMIN\")));\n\t\tthis.mvc.perform(requestWithAdminPermission)\n\t\t\t\t.andExpect(status().isOk());\n\t\tMockHttpServletRequestBuilder requestWithAdminRole = get(\"/admin\")\n\t\t\t\t.with(user(\"user\").authorities(AuthorityUtils.createAuthorityList(\"ROLE_ADMIN\")));\n\t\tthis.mvc.perform(requestWithAdminRole)\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void checkSecurityContextAwareAndLogoutFilterHasSameSizeAndHasLogoutSuccessEventPublishingLogoutHandler() {\n\t\tthis.spring.register(ServletApiWithLogoutConfig.class);\n\t\tSecurityContextHolderAwareRequestFilter scaFilter = getFilter(SecurityContextHolderAwareRequestFilter.class);\n\t\tLogoutFilter logoutFilter = getFilter(LogoutFilter.class);\n\t\tLogoutHandler lfLogoutHandler = getFieldValue(logoutFilter, \"handler\");\n\t\tassertThat(lfLogoutHandler).isInstanceOf(CompositeLogoutHandler.class);\n\t\tList<LogoutHandler> scaLogoutHandlers = getFieldValue(scaFilter, \"logoutHandlers\");\n\t\tList<LogoutHandler> lfLogoutHandlers = getFieldValue(lfLogoutHandler, \"logoutHandlers\");\n\t\tassertThat(scaLogoutHandlers).hasSameSizeAs(lfLogoutHandlers);\n\t\tassertThat(scaLogoutHandlers).hasAtLeastOneElementOfType(LogoutSuccessEventPublishingLogoutHandler.class);\n\t\tassertThat(lfLogoutHandlers).hasAtLeastOneElementOfType(LogoutSuccessEventPublishingLogoutHandler.class);\n\t}\n\n\t@Test\n\tpublic void logoutServletApiWhenCsrfDisabled() throws Exception {\n\t\tConfigurableWebApplicationContext context = this.spring.register(CsrfDisabledConfig.class).getContext();\n\t\tMockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();\n\t\tMvcResult mvcResult = mockMvc.perform(get(\"/\")).andReturn();\n\t\tassertThat(mvcResult.getRequest().getSession(false)).isNull();\n\t}\n\n\tprivate <T extends Filter> T getFilter(Class<T> filterClass) {\n\t\treturn (T) getFilters().stream().filter(filterClass::isInstance).findFirst().orElse(null);\n\t}\n\n\tprivate List<Filter> getFilters() {\n\t\tFilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\treturn proxy.getFilters(\"/\");\n\t}\n\n\tprivate <T> T getFieldValue(Object target, String fieldName) {\n\t\ttry {\n\t\t\treturn (T) FieldUtils.getFieldValue(target, fieldName);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.servletApi(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ServletApiConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t\t\t.formLogin(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationManager customAuthenticationManager(UserDetailsService userDetailsService) {\n\t\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);\n\t\t\treturn provider::authenticate;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomEntryPointConfig {\n\n\t\tstatic AuthenticationEntryPoint ENTRYPOINT = spy(AuthenticationEntryPoint.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.authenticationEntryPoint(ENTRYPOINT))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DuplicateInvocationsDoesNotOverrideConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.servletApi((api) -> api\n\t\t\t\t\t.rolePrefix(\"PERMISSION_\"))\n\t\t\t\t.servletApi(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SharedTrustResolverConfig {\n\n\t\tstatic AuthenticationTrustResolver TR = spy(AuthenticationTrustResolver.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.setSharedObject(AuthenticationTrustResolver.class, TR);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ServletApiWithDefaultsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.servletApi(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RolePrefixInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.servletApi((servletApi) -> servletApi\n\t\t\t\t\t\t.rolePrefix(\"PERMISSION_\")\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class AdminController {\n\n\t\t@GetMapping(\"/admin\")\n\t\tvoid admin(HttpServletRequest request) {\n\t\t\tif (!request.isUserInRole(\"ADMIN\")) {\n\t\t\t\tthrow new AccessDeniedException(\"This resource is only available to admins\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ServletApiWithLogoutConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.servletApi(withDefaults())\n\t\t\t\t.logout(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CsrfDisabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf.disable());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@RestController\n\t\tstatic class LogoutController {\n\n\t\t\t@GetMapping(\"/\")\n\t\t\tString logout(HttpServletRequest request) throws ServletException {\n\t\t\t\trequest.getSession().setAttribute(\"foo\", \"bar\");\n\t\t\t\trequest.logout();\n\t\t\t\treturn \"logout\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerServlet31Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.Filter;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * @author Rob Winch\n */\npublic class SessionManagementConfigurerServlet31Tests {\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\tConfigurableApplicationContext context;\n\n\tFilter springSecurityFilterChain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void teardown() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void changeSessionIdThenPreserveParameters() throws Exception {\n\t\tMockHttpServletRequest request = post(\"/login\").param(\"username\", \"user\").param(\"password\", \"password\").build();\n\t\tString id = request.getSession().getId();\n\t\trequest.getSession();\n\t\tHttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();\n\t\tCsrfTokenRequestHandler handler = new XorCsrfTokenRequestAttributeHandler();\n\t\tDeferredCsrfToken deferredCsrfToken = repository.loadDeferredToken(request, this.response);\n\t\thandler.handle(request, this.response, deferredCsrfToken);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\trequest.setParameter(token.getParameterName(), token.getToken());\n\t\trequest.getSession().setAttribute(\"attribute1\", \"value1\");\n\t\tloadConfig(SessionManagementDefaultSessionFixationServlet31Config.class);\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(request.getSession().getId()).isNotEqualTo(id);\n\t\tassertThat(request.getSession().getAttribute(\"attribute1\")).isEqualTo(\"value1\");\n\t}\n\n\tprivate void loadConfig(Class<?>... classes) {\n\t\tAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();\n\t\tcontext.register(classes);\n\t\tcontext.refresh();\n\t\tthis.context = context;\n\t\tthis.springSecurityFilterChain = this.context.getBean(\"springSecurityFilterChain\", Filter.class);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SessionManagementDefaultSessionFixationServlet31Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.sessionManagement(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerSessionAuthenticationStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\n\n/**\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SessionManagementConfigurerSessionAuthenticationStrategyTests {\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t// gh-5763\n\t@Test\n\tpublic void requestWhenCustomSessionAuthenticationStrategyProvidedThenCalled() throws Exception {\n\t\tthis.spring.register(CustomSessionAuthenticationStrategyConfig.class).autowire();\n\t\tthis.mvc.perform(formLogin().user(\"user\").password(\"password\"));\n\t\tverify(CustomSessionAuthenticationStrategyConfig.customSessionAuthenticationStrategy)\n\t\t\t.onAuthentication(any(Authentication.class), any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomSessionAuthenticationStrategyConfig {\n\n\t\tstatic SessionAuthenticationStrategy customSessionAuthenticationStrategy = mock(\n\t\t\t\tSessionAuthenticationStrategy.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionAuthenticationStrategy(customSessionAuthenticationStrategy));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerSessionCreationPolicyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SessionManagementConfigurerSessionCreationPolicyTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void getWhenSharedObjectSessionCreationPolicyConfigurationThenOverrides() throws Exception {\n\t\tthis.spring.register(StatelessCreateSessionSharedObjectConfig.class).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/\")).andReturn();\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void getWhenUserSessionCreationPolicyConfigurationThenOverrides() throws Exception {\n\t\tthis.spring.register(StatelessCreateSessionUserConfig.class).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/\")).andReturn();\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultsThenLoginChallengeCreatesSession() throws Exception {\n\t\tthis.spring.register(DefaultConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class StatelessCreateSessionSharedObjectConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.setSharedObject(SessionCreationPolicy.class, SessionCreationPolicy.STATELESS);\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class StatelessCreateSessionUserConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS));\n\t\t\t// @formatter:on\n\t\t\thttp.setSharedObject(SessionCreationPolicy.class, SessionCreationPolicy.ALWAYS);\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultConfig {\n\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@GetMapping(\"/\")\n\t\tString root() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Answers;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.TestDeferredSecurityContext;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.SessionLimit;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.session.ConcurrentSessionFilter;\nimport org.springframework.security.web.session.HttpSessionDestroyedEvent;\nimport org.springframework.security.web.session.SessionManagementFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.util.WebUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.withSettings;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link SessionManagementConfigurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SessionManagementConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void sessionManagementWhenConfiguredThenDoesNotOverrideRequestCache() throws Exception {\n\t\tSessionManagementRequestCacheConfig.REQUEST_CACHE = mock(RequestCache.class);\n\t\tthis.spring.register(SessionManagementRequestCacheConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(SessionManagementRequestCacheConfig.REQUEST_CACHE).getMatchingRequest(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void sessionManagementWhenConfiguredThenDoesNotOverrideSecurityContextRepository() throws Exception {\n\t\tSessionManagementSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPO = mock(SecurityContextRepository.class);\n\t\tgiven(SessionManagementSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPO\n\t\t\t.loadDeferredContext(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new TestDeferredSecurityContext(mock(SecurityContext.class), false));\n\t\tthis.spring.register(SessionManagementSecurityContextRepositoryConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t}\n\n\t@Test\n\tpublic void sessionManagementWhenSecurityContextRepositoryIsConfiguredThenUseIt() throws Exception {\n\t\tSessionManagementSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPO = mock(SecurityContextRepository.class);\n\t\tgiven(SessionManagementSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPO\n\t\t\t.loadDeferredContext(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new TestDeferredSecurityContext(mock(SecurityContext.class), false));\n\t\tthis.spring.register(SessionManagementSecurityContextRepositoryConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(SessionManagementSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPO)\n\t\t\t.containsContext(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void sessionManagementWhenInvokedTwiceThenUsesOriginalSessionCreationPolicy() throws Exception {\n\t\tthis.spring.register(InvokeTwiceDoesNotOverride.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\")).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t// SEC-2137\n\t@Test\n\tpublic void getWhenSessionFixationDisabledAndConcurrencyControlEnabledThenSessionIsNotInvalidated()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(DisableSessionFixationEnableConcurrencyControlConfig.class).autowire();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tString sessionId = session.getId();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/\")\n\t\t\t\t.with(httpBasic(\"user\", \"password\"))\n\t\t\t\t.session(session);\n\t\tMvcResult mvcResult = this.mvc.perform(request)\n\t\t\t\t.andExpect(status().isNotFound())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(mvcResult.getRequest().getSession().getId()).isEqualTo(sessionId);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNewSessionFixationProtectionInLambdaThenCreatesNewSession() throws Exception {\n\t\tthis.spring.register(SFPNewSessionInLambdaConfig.class).autowire();\n\t\tMockHttpSession givenSession = new MockHttpSession();\n\t\tString givenSessionId = givenSession.getId();\n\t\tgivenSession.setAttribute(\"name\", \"value\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\")\n\t\t\t\t.session(givenSession)\n\t\t\t\t.with(httpBasic(\"user\", \"password\"));\n\t\tMockHttpSession resultingSession = (MockHttpSession) this.mvc.perform(request)\n\t\t\t\t.andExpect(status().isNotFound())\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\t// @formatter:on\n\t\tassertThat(givenSessionId).isNotEqualTo(resultingSession.getId());\n\t\tassertThat(resultingSession.getAttribute(\"name\")).isNull();\n\t}\n\n\t@Test\n\tpublic void loginWhenUserLoggedInAndMaxSessionsIsOneThenLoginPrevented() throws Exception {\n\t\tthis.spring.register(ConcurrencyControlConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder firstRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(firstRequest);\n\t\tMockHttpServletRequestBuilder secondRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(secondRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenUserSessionExpiredAndMaxSessionsIsOneThenLoggedIn() throws Exception {\n\t\tthis.spring.register(ConcurrencyControlConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder firstRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tMvcResult mvcResult = this.mvc.perform(firstRequest)\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tHttpSession authenticatedSession = mvcResult.getRequest().getSession();\n\t\tthis.spring.getContext().publishEvent(new HttpSessionDestroyedEvent(authenticatedSession));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder secondRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(secondRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenUserLoggedInAndMaxSessionsOneInLambdaThenLoginPrevented() throws Exception {\n\t\tthis.spring.register(ConcurrencyControlInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder firstRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\t// @formatter:on\n\t\tthis.mvc.perform(firstRequest);\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder secondRequest = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(secondRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenAdminUserLoggedInAndSessionLimitIsConfiguredThenLoginSuccessfully() throws Exception {\n\t\tthis.spring.register(ConcurrencyControlWithSessionLimitConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestBuilder = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"admin\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tHttpSession firstSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(firstSession).isNotNull();\n\t\tHttpSession secondSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(secondSession).isNotNull();\n\t\t// @formatter:on\n\t\tassertThat(firstSession.getId()).isNotEqualTo(secondSession.getId());\n\t}\n\n\t@Test\n\tpublic void loginWhenAdminUserLoggedInAndSessionLimitIsConfiguredThenLoginPrevented() throws Exception {\n\t\tthis.spring.register(ConcurrencyControlWithSessionLimitConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestBuilder = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"admin\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tHttpSession firstSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(firstSession).isNotNull();\n\t\tHttpSession secondSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(secondSession).isNotNull();\n\t\tassertThat(firstSession.getId()).isNotEqualTo(secondSession.getId());\n\t\tthis.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenUserLoggedInAndSessionLimitIsConfiguredThenLoginPrevented() throws Exception {\n\t\tthis.spring.register(ConcurrencyControlWithSessionLimitConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestBuilder = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tHttpSession firstSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(firstSession).isNotNull();\n\t\tthis.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionCreationPolicyStateLessInLambdaThenNoSessionCreated() throws Exception {\n\t\tthis.spring.register(SessionCreationPolicyStateLessInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\")).andReturn();\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnSessionManagementFilter() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(SessionManagementFilter.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnConcurrentSessionFilter() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(ConcurrentSessionFilter.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnConcurrentSessionControlAuthenticationStrategy() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(ConcurrentSessionControlAuthenticationStrategy.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnCompositeSessionAuthenticationStrategy() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(CompositeSessionAuthenticationStrategy.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnRegisterSessionAuthenticationStrategy() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(RegisterSessionAuthenticationStrategy.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnChangeSessionIdAuthenticationStrategy() {\n\t\tObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tverify(ObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(ChangeSessionIdAuthenticationStrategy.class));\n\t}\n\n\t@Test\n\tpublic void getWhenAnonymousRequestAndTrustResolverSharedObjectReturnsAnonymousFalseThenSessionIsSaved()\n\t\t\tthrows Exception {\n\t\tSharedTrustResolverConfig.TR = mock(AuthenticationTrustResolver.class,\n\t\t\t\twithSettings().defaultAnswer(Answers.CALLS_REAL_METHODS));\n\t\tgiven(SharedTrustResolverConfig.TR.isAnonymous(any())).willReturn(false);\n\t\tthis.spring.register(SharedTrustResolverConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\")).andReturn();\n\t\tassertThat(mvcResult.getRequest().getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void whenOneSessionRegistryBeanThenUseIt() throws Exception {\n\t\tSessionRegistryOneBeanConfig.SESSION_REGISTRY = mock(SessionRegistry.class);\n\t\tthis.spring.register(SessionRegistryOneBeanConfig.class).autowire();\n\t\tMockHttpSession session = new MockHttpSession(this.spring.getContext().getServletContext());\n\t\tthis.mvc.perform(get(\"/\").session(session));\n\t\tverify(SessionRegistryOneBeanConfig.SESSION_REGISTRY).getSessionInformation(session.getId());\n\t}\n\n\t@Test\n\tpublic void whenTwoSessionRegistryBeansThenUseNeither() throws Exception {\n\t\tSessionRegistryTwoBeansConfig.SESSION_REGISTRY_ONE = mock(SessionRegistry.class);\n\t\tSessionRegistryTwoBeansConfig.SESSION_REGISTRY_TWO = mock(SessionRegistry.class);\n\t\tthis.spring.register(SessionRegistryTwoBeansConfig.class).autowire();\n\t\tMockHttpSession session = new MockHttpSession(this.spring.getContext().getServletContext());\n\t\tthis.mvc.perform(get(\"/\").session(session));\n\t\tverifyNoInteractions(SessionRegistryTwoBeansConfig.SESSION_REGISTRY_ONE);\n\t\tverifyNoInteractions(SessionRegistryTwoBeansConfig.SESSION_REGISTRY_TWO);\n\t}\n\n\t@Test\n\tpublic void whenEnableSessionUrlRewritingTrueThenEncodeNotInvoked() throws Exception {\n\t\tthis.spring.register(EnableUrlRewriteConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.spring.getContext())\n\t\t\t.addFilters((request, response, chain) -> {\n\t\t\t\tHttpServletResponse responseToSpy = spy((HttpServletResponse) response);\n\t\t\t\tchain.doFilter(request, responseToSpy);\n\t\t\t\tverify(responseToSpy, atLeastOnce()).encodeRedirectURL(any());\n\t\t\t\tverify(responseToSpy, atLeastOnce()).encodeURL(any());\n\t\t\t})\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tthis.mvc.perform(get(\"/\")).andExpect(content().string(\"encoded\"));\n\t}\n\n\t@Test\n\tpublic void whenDefaultThenEncodeNotInvoked() throws Exception {\n\t\tthis.spring.register(DefaultUrlRewriteConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.spring.getContext())\n\t\t\t.addFilters((request, response, chain) -> {\n\t\t\t\tHttpServletResponse responseToSpy = spy((HttpServletResponse) response);\n\t\t\t\tchain.doFilter(request, responseToSpy);\n\t\t\t\tverify(responseToSpy, never()).encodeRedirectURL(any());\n\t\t\t\tverify(responseToSpy, never()).encodeURL(any());\n\t\t\t})\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tthis.mvc.perform(get(\"/\")).andExpect(content().string(\"encoded\"));\n\t}\n\n\t@Test\n\tpublic void loginWhenSessionCreationPolicyStatelessThenSecurityContextIsAvailableInRequestAttributes()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(HttpBasicSessionCreationPolicyStatelessConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tHttpSession session = mvcResult.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t\tSecurityContext securityContext = (SecurityContext) mvcResult.getRequest()\n\t\t\t.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME);\n\t\tassertThat(securityContext).isNotNull();\n\t}\n\n\t/**\n\t * This ensures that if an ErrorDispatch occurs, then the SecurityContextRepository\n\t * defaulted by SessionManagementConfigurer is correct (looks at both Session and\n\t * Request Attributes).\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void gh12070WhenErrorDispatchSecurityContextRepositoryWorks() throws Exception {\n\t\tFilter errorDispatchFilter = new Filter() {\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\t\tthrows IOException, ServletException {\n\t\t\t\ttry {\n\t\t\t\t\tchain.doFilter(request, response);\n\t\t\t\t}\n\t\t\t\tcatch (ServletException ex) {\n\t\t\t\t\tif (request.getDispatcherType() == DispatcherType.ERROR) {\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t\tMockHttpServletRequest httpRequest = WebUtils.getNativeRequest(request,\n\t\t\t\t\t\t\tMockHttpServletRequest.class);\n\t\t\t\t\thttpRequest.setDispatcherType(DispatcherType.ERROR);\n\t\t\t\t\t// necessary to prevent HttpBasicFilter from invoking again\n\t\t\t\t\thttpRequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\");\n\t\t\t\t\thttpRequest.setRequestURI(\"/error\");\n\t\t\t\t\tMockFilterChain mockChain = (MockFilterChain) chain;\n\t\t\t\t\tmockChain.reset();\n\t\t\t\t\tmockChain.doFilter(httpRequest, response);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tthis.spring.addFilter(errorDispatchFilter).register(Gh12070IssueConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/500\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isInternalServerError());\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class Gh12070IssueConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t\t.formLogin(Customizer.withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t\t@RestController\n\t\tstatic class ErrorController {\n\n\t\t\t@GetMapping(\"/500\")\n\t\t\tString error() throws ServletException {\n\t\t\t\tthrow new ServletException(\"Error\");\n\t\t\t}\n\n\t\t\t@GetMapping(\"/error\")\n\t\t\tResponseEntity<String> errorHandler() {\n\t\t\t\treturn new ResponseEntity<>(\"error\", HttpStatus.INTERNAL_SERVER_ERROR);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SessionManagementRequestCacheConfig {\n\n\t\tstatic RequestCache REQUEST_CACHE;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requestCache((cache) -> cache\n\t\t\t\t\t.requestCache(REQUEST_CACHE))\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.STATELESS));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SessionManagementSecurityContextRepositoryConfig {\n\n\t\tstatic SecurityContextRepository SECURITY_CONTEXT_REPO;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityContext((context) -> context\n\t\t\t\t\t.securityContextRepository(SECURITY_CONTEXT_REPO))\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.STATELESS));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class InvokeTwiceDoesNotOverride {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.STATELESS))\n\t\t\t\t.sessionManagement(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DisableSessionFixationEnableConcurrencyControlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionFixation().none()\n\t\t\t\t\t.maximumSessions(1));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SFPNewSessionInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t\t.requireExplicitAuthenticationStrategy(false)\n\t\t\t\t\t\t.sessionFixation(SessionManagementConfigurer.SessionFixationConfigurer::newSession)\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConcurrencyControlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.maximumSessions(1)\n\t\t\t\t\t.maxSessionsPreventsLogin(true));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConcurrencyControlInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t\t.sessionConcurrency((sessionConcurrency) -> sessionConcurrency\n\t\t\t\t\t\t\t\t.maximumSessions(1)\n\t\t\t\t\t\t\t\t.maxSessionsPreventsLogin(true)\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConcurrencyControlWithSessionLimitConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, SessionLimit sessionLimit) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t\t\t\t\t.sessionConcurrency((sessionConcurrency) -> sessionConcurrency\n\t\t\t\t\t\t\t\t\t\t\t\t\t.maximumSessions(sessionLimit)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.maxSessionsPreventsLogin(true)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.admin(), PasswordEncodedUser.user());\n\t\t}\n\n\t\t@Bean\n\t\tSessionLimit SessionLimit() {\n\t\t\treturn (authentication) -> {\n\t\t\t\tif (\"admin\".equals(authentication.getName())) {\n\t\t\t\t\treturn 2;\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SessionCreationPolicyStateLessInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.STATELESS)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.maximumSessions(1));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SharedTrustResolverConfig {\n\n\t\tstatic AuthenticationTrustResolver TR;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((sessions) -> sessions\n\t\t\t\t\t.requireExplicitAuthenticationStrategy(false)\n\t\t\t\t)\n\t\t\t\t.setSharedObject(AuthenticationTrustResolver.class, TR);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SessionRegistryOneBeanConfig {\n\n\t\tprivate static SessionRegistry SESSION_REGISTRY;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.maximumSessions(1));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSessionRegistry sessionRegistry() {\n\t\t\treturn SESSION_REGISTRY;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SessionRegistryTwoBeansConfig {\n\n\t\tprivate static SessionRegistry SESSION_REGISTRY_ONE;\n\n\t\tprivate static SessionRegistry SESSION_REGISTRY_TWO;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.maximumSessions(1));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSessionRegistry sessionRegistryOne() {\n\t\t\treturn SESSION_REGISTRY_ONE;\n\t\t}\n\n\t\t@Bean\n\t\tSessionRegistry sessionRegistryTwo() {\n\t\t\treturn SESSION_REGISTRY_TWO;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultUrlRewriteConfig {\n\n\t\t@Bean\n\t\tDefaultSecurityFilterChain configure(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tEncodesUrls encodesUrls() {\n\t\t\treturn new EncodesUrls();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class EnableUrlRewriteConfig {\n\n\t\t@Bean\n\t\tDefaultSecurityFilterChain configure(HttpSecurity http) throws Exception {\n\t\t\thttp.sessionManagement((sessions) -> sessions.enableSessionUrlRewriting(true));\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tEncodesUrls encodesUrls() {\n\t\t\treturn new EncodesUrls();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class HttpBasicSessionCreationPolicyStatelessConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.STATELESS)\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t\t@Bean\n\t\tEncodesUrls encodesUrls() {\n\t\t\treturn new EncodesUrls();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class EncodesUrls {\n\n\t\t@RequestMapping(\"/\")\n\t\tString encoded(HttpServletResponse response) {\n\t\t\tresponse.encodeURL(\"/foo\");\n\t\t\tresponse.encodeRedirectURL(\"/foo\");\n\t\t\treturn \"encoded\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTransientAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.Transient;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SessionManagementConfigurerTransientAuthenticationTests {\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void postWhenTransientAuthenticationThenNoSessionCreated() throws Exception {\n\t\tthis.spring.register(WithTransientAuthenticationConfig.class).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/login\")).andReturn();\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void postWhenTransientAuthenticationThenAlwaysSessionOverrides() throws Exception {\n\t\tthis.spring.register(AlwaysCreateSessionConfig.class).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/login\")).andReturn();\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WithTransientAuthenticationConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t\t.authenticationProvider(new TransientAuthenticationProvider());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AlwaysCreateSessionConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management.sessionCreationPolicy(SessionCreationPolicy.ALWAYS));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tstatic class TransientAuthenticationProvider implements AuthenticationProvider {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\treturn new SomeTransientAuthentication();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\t@Transient\n\tstatic class SomeTransientAuthentication extends AbstractAuthenticationToken {\n\n\t\tSomeTransientAuthentication() {\n\t\t\tsuper((Collection<? extends GrantedAuthority>) null);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getCredentials() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getPrincipal() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/UrlAuthorizationsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n *\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class UrlAuthorizationsTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\t@WithMockUser(authorities = \"ROLE_USER\")\n\tpublic void hasAnyAuthorityWhenAuthoritySpecifiedThenMatchesAuthority() throws Exception {\n\t\tthis.spring.register(RoleConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/role-user-authority\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"/role-user\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"/role-admin-authority\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"ROLE_ADMIN\")\n\tpublic void hasAnyAuthorityWhenAuthoritiesSpecifiedThenMatchesAuthority() throws Exception {\n\t\tthis.spring.register(RoleConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/role-user-admin-authority\")).andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"/role-user-admin\")).andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"/role-user-authority\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"USER\")\n\tpublic void hasAnyRoleWhenRoleSpecifiedThenMatchesRole() throws Exception {\n\t\tthis.spring.register(RoleConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/role-user\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"/role-admin\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic void hasAnyRoleWhenRolesSpecifiedThenMatchesRole() throws Exception {\n\t\tthis.spring.register(RoleConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/role-admin-user\")).andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/role-user\")).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = \"USER\")\n\tpublic void hasAnyRoleWhenRoleSpecifiedThenDoesNotMatchAuthority() throws Exception {\n\t\tthis.spring.register(RoleConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/role-user\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/role-admin\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class RoleConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/role-user-authority\").hasAnyAuthority(\"ROLE_USER\")\n\t\t\t\t\t.requestMatchers(\"/role-admin-authority\").hasAnyAuthority(\"ROLE_ADMIN\")\n\t\t\t\t\t.requestMatchers(\"/role-user-admin-authority\").hasAnyAuthority(\"ROLE_USER\", \"ROLE_ADMIN\")\n\t\t\t\t\t.requestMatchers(\"/role-user\").hasAnyRole(\"USER\")\n\t\t\t\t\t.requestMatchers(\"/role-admin\").hasAnyRole(\"ADMIN\")\n\t\t\t\t\t.requestMatchers(\"/role-user-admin\").hasAnyRole(\"USER\", \"ADMIN\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Daniel Garnier-Moiroux\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class WebAuthnConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void webauthnWhenConfiguredConfiguredThenServesJavascript() throws Exception {\n\t\tthis.spring.register(DefaultWebauthnConfiguration.class).autowire();\n\t\tthis.mvc.perform(get(\"/login/webauthn.js\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(\"content-type\", \"text/javascript;charset=UTF-8\"))\n\t\t\t.andExpect(content().string(containsString(\"async function authenticate(\")));\n\t}\n\n\t@Test\n\tpublic void webauthnWhenConfiguredConfiguredThenServesCss() throws Exception {\n\t\tthis.spring.register(DefaultWebauthnConfiguration.class).autowire();\n\t\tthis.mvc.perform(get(\"/default-ui.css\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(\"content-type\", \"text/css;charset=UTF-8\"))\n\t\t\t.andExpect(content().string(containsString(\"body {\")));\n\t}\n\n\t// gh-18128\n\t@Test\n\tpublic void webAuthnAuthenticationFilterIsPostProcessed() throws Exception {\n\t\tthis.spring.register(DefaultWebauthnConfiguration.class, PostProcessorConfiguration.class).autowire();\n\t\tPostProcessorConfiguration postProcess = this.spring.getContext().getBean(PostProcessorConfiguration.class);\n\t\tassertThat(postProcess.webauthnFilter).isNotNull();\n\t}\n\n\t@Test\n\tpublic void webauthnWhenNoFormLoginAndDefaultRegistrationPageConfiguredThenServesJavascript() throws Exception {\n\t\tthis.spring.register(NoFormLoginAndDefaultRegistrationPageConfiguration.class).autowire();\n\t\tthis.mvc.perform(get(\"/login/webauthn.js\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(\"content-type\", \"text/javascript;charset=UTF-8\"))\n\t\t\t.andExpect(content().string(containsString(\"async function authenticate(\")));\n\t}\n\n\t@Test\n\tpublic void webauthnWhenNoFormLoginAndDefaultRegistrationPageConfiguredThenServesCss() throws Exception {\n\t\tthis.spring.register(NoFormLoginAndDefaultRegistrationPageConfiguration.class).autowire();\n\t\tthis.mvc.perform(get(\"/default-ui.css\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(\"content-type\", \"text/css;charset=UTF-8\"))\n\t\t\t.andExpect(content().string(containsString(\"body {\")));\n\t}\n\n\t@Test\n\tpublic void webauthnWhenFormLoginAndDefaultRegistrationPageConfiguredThenNoDuplicateFilters() {\n\t\tthis.spring.register(DefaultWebauthnConfiguration.class).autowire();\n\t\tFilterChainProxy filterChain = this.spring.getContext().getBean(FilterChainProxy.class);\n\n\t\tList<DefaultResourcesFilter> defaultResourcesFilters = filterChain.getFilterChains()\n\t\t\t.get(0)\n\t\t\t.getFilters()\n\t\t\t.stream()\n\t\t\t.filter(DefaultResourcesFilter.class::isInstance)\n\t\t\t.map(DefaultResourcesFilter.class::cast)\n\t\t\t.toList();\n\n\t\tassertThat(defaultResourcesFilters).map(DefaultResourcesFilter::toString)\n\t\t\t.filteredOn((filterDescription) -> filterDescription.contains(\"login/webauthn.js\"))\n\t\t\t.hasSize(1);\n\t\tassertThat(defaultResourcesFilters).map(DefaultResourcesFilter::toString)\n\t\t\t.filteredOn((filterDescription) -> filterDescription.contains(\"default-ui.css\"))\n\t\t\t.hasSize(1);\n\t}\n\n\t@Test\n\tvoid webauthnWhenConfiguredDefaultsRpNameToRpId() throws Exception {\n\t\tObjectMapper mapper = new ObjectMapper();\n\t\tthis.spring.register(DefaultWebauthnConfiguration.class).autowire();\n\t\tString response = this.mvc\n\t\t\t.perform(post(\"/webauthn/register/options\").with(csrf())\n\t\t\t\t.with(authentication(new TestingAuthenticationToken(\"test\", \"ignored\", \"ROLE_user\"))))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\n\t\tJsonNode parsedResponse = mapper.readTree(response);\n\n\t\tassertThat(parsedResponse.get(\"rp\").get(\"id\").asText()).isEqualTo(\"example.com\");\n\t\tassertThat(parsedResponse.get(\"rp\").get(\"name\").asText()).isEqualTo(\"example.com\");\n\t}\n\n\t@Test\n\tvoid webauthnWhenRpNameConfiguredUsesRpName() throws Exception {\n\t\tObjectMapper mapper = new ObjectMapper();\n\t\tthis.spring.register(CustomRpNameWebauthnConfiguration.class).autowire();\n\t\tString response = this.mvc\n\t\t\t.perform(post(\"/webauthn/register/options\").with(csrf())\n\t\t\t\t.with(authentication(new TestingAuthenticationToken(\"test\", \"ignored\", \"ROLE_user\"))))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\n\t\tJsonNode parsedResponse = mapper.readTree(response);\n\n\t\tassertThat(parsedResponse.get(\"rp\").get(\"id\").asText()).isEqualTo(\"example.com\");\n\t\tassertThat(parsedResponse.get(\"rp\").get(\"name\").asText()).isEqualTo(\"Test RP Name\");\n\t}\n\n\t@Test\n\tpublic void webauthnWhenConfiguredAndFormLoginThenDoesServesJavascript() throws Exception {\n\t\tthis.spring.register(FormLoginAndNoDefaultRegistrationPageConfiguration.class).autowire();\n\t\tthis.mvc.perform(get(\"/login/webauthn.js\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(\"content-type\", \"text/javascript;charset=UTF-8\"))\n\t\t\t.andExpect(content().string(containsString(\"async function authenticate(\")));\n\t}\n\n\t@Test\n\tpublic void webauthnWhenConfiguredAndNoDefaultRegistrationPageThenDoesNotServeJavascript() throws Exception {\n\t\tthis.spring.register(NoDefaultRegistrationPageConfiguration.class).autowire();\n\t\tthis.mvc.perform(get(\"/login/webauthn.js\")).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void webauthnWhenConfiguredPublicKeyCredentialCreationOptionsRepository() throws Exception {\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(user));\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tWebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);\n\t\tConfigCredentialCreationOptionsRepository.rpOperations = rpOperations;\n\t\tgiven(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);\n\t\tString attrName = \"attrName\";\n\t\tHttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();\n\t\tcreationOptionsRepository.setAttrName(attrName);\n\t\tConfigCredentialCreationOptionsRepository.creationOptionsRepository = creationOptionsRepository;\n\t\tthis.spring.register(ConfigCredentialCreationOptionsRepository.class).autowire();\n\t\tthis.mvc.perform(post(\"/webauthn/register/options\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(request().sessionAttribute(attrName, options));\n\t}\n\n\t@Test\n\tpublic void webauthnWhenConfiguredPublicKeyCredentialCreationOptionsRepositoryBeanPresent() throws Exception {\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(user));\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tWebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);\n\t\tConfigCredentialCreationOptionsRepositoryFromBean.rpOperations = rpOperations;\n\t\tgiven(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);\n\t\tString attrName = \"attrName\";\n\t\tHttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();\n\t\tcreationOptionsRepository.setAttrName(attrName);\n\t\tConfigCredentialCreationOptionsRepositoryFromBean.creationOptionsRepository = creationOptionsRepository;\n\t\tthis.spring.register(ConfigCredentialCreationOptionsRepositoryFromBean.class).autowire();\n\t\tthis.mvc.perform(post(\"/webauthn/register/options\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(request().sessionAttribute(attrName, options));\n\t}\n\n\t@Test\n\tpublic void webauthnWhenConfiguredMessageConverter() throws Exception {\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(user));\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tWebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);\n\t\tConfigMessageConverter.rpOperations = rpOperations;\n\t\tgiven(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);\n\t\tHttpMessageConverter<Object> converter = mock(HttpMessageConverter.class);\n\t\tgiven(converter.canWrite(any(), any())).willReturn(true);\n\t\tString expectedBody = \"123\";\n\t\twillAnswer((args) -> {\n\t\t\tHttpOutputMessage out = (HttpOutputMessage) args.getArguments()[2];\n\t\t\tout.getBody().write(expectedBody.getBytes(StandardCharsets.UTF_8));\n\t\t\treturn null;\n\t\t}).given(converter).write(any(), any(), any());\n\t\tConfigMessageConverter.converter = converter;\n\t\tthis.spring.register(ConfigMessageConverter.class).autowire();\n\t\tthis.mvc.perform(post(\"/webauthn/register/options\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(expectedBody));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConfigCredentialCreationOptionsRepository {\n\n\t\tprivate static HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository;\n\n\t\tprivate static WebAuthnRelyingPartyOperations rpOperations;\n\n\t\t@Bean\n\t\tWebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {\n\t\t\treturn ConfigCredentialCreationOptionsRepository.rpOperations;\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.csrf(AbstractHttpConfigurer::disable)\n\t\t\t\t.webAuthn((c) -> c.creationOptionsRepository(creationOptionsRepository))\n\t\t\t\t.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConfigCredentialCreationOptionsRepositoryFromBean {\n\n\t\tprivate static HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository;\n\n\t\tprivate static WebAuthnRelyingPartyOperations rpOperations;\n\n\t\t@Bean\n\t\tWebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {\n\t\t\treturn ConfigCredentialCreationOptionsRepositoryFromBean.rpOperations;\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tHttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {\n\t\t\treturn ConfigCredentialCreationOptionsRepositoryFromBean.creationOptionsRepository;\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.csrf(AbstractHttpConfigurer::disable).webAuthn(Customizer.withDefaults()).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConfigMessageConverter {\n\n\t\tprivate static HttpMessageConverter<Object> converter;\n\n\t\tprivate static WebAuthnRelyingPartyOperations rpOperations;\n\n\t\t@Bean\n\t\tWebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {\n\t\t\treturn ConfigMessageConverter.rpOperations;\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.csrf(AbstractHttpConfigurer::disable).webAuthn((c) -> c.messageConverter(converter)).build();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class PostProcessorConfiguration {\n\n\t\tWebAuthnAuthenticationFilter webauthnFilter;\n\n\t\t@Bean\n\t\tBeanPostProcessor beanPostProcessor() {\n\t\t\treturn new BeanPostProcessor() {\n\t\t\t\t@Override\n\t\t\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) {\n\t\t\t\t\tif (bean instanceof WebAuthnAuthenticationFilter filter) {\n\t\t\t\t\t\tPostProcessorConfiguration.this.webauthnFilter = filter;\n\t\t\t\t\t}\n\t\t\t\t\treturn bean;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultWebauthnConfiguration {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.webAuthn((authn) -> authn\n\t\t\t\t\t.rpId(\"example.com\")\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomRpNameWebauthnConfiguration {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.formLogin(Customizer.withDefaults())\n\t\t\t\t.webAuthn((webauthn) -> webauthn.rpId(\"example.com\").rpName(\"Test RP Name\"))\n\t\t\t\t.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NoFormLoginAndDefaultRegistrationPageConfiguration {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.webAuthn((authn) -> authn\n\t\t\t\t\t\t.rpId(\"spring.io\")\n\t\t\t\t\t\t.rpName(\"spring\")\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormLoginAndNoDefaultRegistrationPageConfiguration {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.webAuthn((authn) -> authn\n\t\t\t\t\t.rpId(\"spring.io\")\n\t\t\t\t\t.rpName(\"spring\")\n\t\t\t\t\t.disableDefaultRegistrationPage(true)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class NoDefaultRegistrationPageConfiguration {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t\t.loginPage(\"/custom-login-page\")\n\t\t\t\t\t)\n\t\t\t\t\t.webAuthn((authn) -> authn\n\t\t\t\t\t\t.rpId(\"spring.io\")\n\t\t\t\t\t\t.rpName(\"spring\")\n\t\t\t\t\t\t.disableDefaultRegistrationPage(true)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers;\n\nimport java.io.InputStream;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\nimport org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor;\nimport org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;\nimport org.springframework.security.web.authentication.preauth.x509.X509TestUtils;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n * Tests for {@link X509Configurer}\n *\n * @author Rob Winch\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class X509ConfigurerTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void configureWhenRegisteringObjectPostProcessorThenInvokedOnX509AuthenticationFilter() {\n\t\tthis.spring.register(ObjectPostProcessorConfig.class).autowire();\n\t\tObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);\n\t\tverify(objectPostProcessor).postProcess(any(X509AuthenticationFilter.class));\n\t}\n\n\t@Test\n\tpublic void x509WhenInvokedTwiceThenUsesOriginalSubjectPrincipalRegex() throws Exception {\n\t\tthis.spring.register(DuplicateDoesNotOverrideConfig.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rodatexampledotcom.cer\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void x509WhenConfiguredInLambdaThenUsesDefaults() throws Exception {\n\t\tthis.spring.register(DefaultsInLambdaConfig.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rod.cer\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void x509WhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.register(DefaultsInLambdaConfig.class, SecurityContextChangedListenerConfig.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rod.cer\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t\tSecurityContextChangedListener listener = this.spring.getContext()\n\t\t\t.getBean(SecurityContextChangedListener.class);\n\t\tverify(listener).securityContextChanged(setAuthentication(PreAuthenticatedAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void x509WhenSubjectPrincipalRegexInLambdaThenUsesRegexToExtractPrincipal() throws Exception {\n\t\tthis.spring.register(SubjectPrincipalRegexInLambdaConfig.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rodatexampledotcom.cer\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void x509WhenUserDetailsServiceNotConfiguredThenUsesBean() throws Exception {\n\t\tthis.spring.register(UserDetailsServiceBeanConfig.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rod.cer\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void x509WhenUserDetailsServiceAndBeanConfiguredThenDoesNotUseBean() throws Exception {\n\t\tthis.spring.register(UserDetailsServiceAndBeanConfig.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rod.cer\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-13008\n\t@Test\n\tpublic void x509WhenStatelessSessionManagementThenDoesNotCreateSession() throws Exception {\n\t\tthis.spring.register(StatelessSessionManagementConfig.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rodatexampledotcom.cer\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect((result) -> assertThat(result.getRequest().getSession(false)).isNull())\n\t\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void x509WhenSubjectX500PrincipalExtractor() throws Exception {\n\t\tthis.spring.register(SubjectX500PrincipalExtractorConfig.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rod.cer\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect((result) -> assertThat(result.getRequest().getSession(false)).isNull())\n\t\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void x509WhenSubjectX500PrincipalExtractorBean() throws Exception {\n\t\tthis.spring.register(SubjectX500PrincipalExtractorEmailConfig.class).autowire();\n\t\tX509Certificate certificate = X509TestUtils.buildTestCertificate();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect((result) -> assertThat(result.getRequest().getSession(false)).isNull())\n\t\t\t\t.andExpect(authenticated().withUsername(\"luke@monkeymachine\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate <T extends Certificate> T loadCert(String location) {\n\t\ttry (InputStream is = new ClassPathResource(location).getInputStream()) {\n\t\t\tCertificateFactory certFactory = CertificateFactory.getInstance(\"X.509\");\n\t\t\treturn (T) certFactory.generateCertificate(is);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ObjectPostProcessorConfig {\n\n\t\tObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.x509(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\t@Primary\n\t\tObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn this.objectPostProcessor;\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DuplicateDoesNotOverrideConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t.subjectPrincipalRegex(\"CN=(.*?)@example.com(?:,|$)\"))\n\t\t\t\t.x509(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.x509(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SubjectPrincipalRegexInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t\t.subjectPrincipalRegex(\"CN=(.*?)@example.com(?:,|$)\")\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class UserDetailsServiceBeanConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.x509(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"rod\")\n\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t\t\t\t.build()\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class UserDetailsServiceAndBeanConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tUserDetailsService customUserDetailsService = new InMemoryUserDetailsManager(\n\t\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"rod\")\n\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t\t\t\t.build());\n\t\t\thttp\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t.userDetailsService(customUserDetailsService)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\treturn mock(UserDetailsService.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class StatelessSessionManagementConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))\n\t\t\t\t.x509((x509) -> x509.subjectPrincipalRegex(\"CN=(.*?)@example.com(?:,|$)\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SubjectX500PrincipalExtractorConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t\t\t.x509PrincipalExtractor(new SubjectX500PrincipalExtractor())\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SubjectX500PrincipalExtractorEmailConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\tSubjectX500PrincipalExtractor principalExtractor = new SubjectX500PrincipalExtractor();\n\t\t\tprincipalExtractor.setExtractPrincipalNameFromEmail(true);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.x509((x509) -> x509\n\t\t\t\t\t.x509PrincipalExtractor(principalExtractor)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"luke@monkeymachine\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link OAuth2ClientConfigurer}.\n *\n * @author Joe Grandja\n * @author Parikshit Dutta\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2ClientConfigurerTests {\n\n\tprivate static ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate static OAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate static OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate static OAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\tprivate static RedirectStrategy authorizationRedirectStrategy;\n\n\tprivate static OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\tprivate static RequestCache requestCache;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\tprivate ClientRegistration registration1;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.registration1 = TestClientRegistrations.clientRegistration()\n\t\t\t\t.registrationId(\"registration-1\")\n\t\t\t\t.clientId(\"client-1\")\n\t\t\t\t.clientSecret(\"secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"{baseUrl}/client-1\")\n\t\t\t\t.scope(\"user\")\n\t\t\t\t.authorizationUri(\"https://provider.com/oauth2/authorize\")\n\t\t\t\t.tokenUri(\"https://provider.com/oauth2/token\")\n\t\t\t\t.userInfoUri(\"https://provider.com/oauth2/user\")\n\t\t\t\t.userNameAttributeName(\"id\")\n\t\t\t\t.clientName(\"client-1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tclientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1);\n\t\tauthorizedClientService = new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);\n\t\tauthorizedClientRepository = new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(\n\t\t\t\tauthorizedClientService);\n\t\tauthorizationRequestResolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,\n\t\t\t\t\"/oauth2/authorization\");\n\t\tauthorizationRedirectStrategy = new DefaultRedirectStrategy();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"access-token-1234\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(300)\n\t\t\t.build();\n\t\taccessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tgiven(accessTokenResponseClient.getTokenResponse(any(OAuth2AuthorizationCodeGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\t\trequestCache = mock(RequestCache.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenAuthorizationCodeRequestThenRedirectForAuthorization() throws Exception {\n\t\tthis.spring.register(OAuth2ClientConfig.class).autowire();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/oauth2/authorization/registration-1\"))\n\t\t\t\t.andExpect(status().is3xxRedirection()).andReturn();\n\t\tassertThat(mvcResult.getResponse().getRedirectedUrl())\n\t\t\t\t.matches(\"https://provider.com/oauth2/authorize\\\\?\" + \"response_type=code&client_id=client-1&\"\n\t\t\t\t\t\t+ \"scope=user&state=.{15,}&\" + \"redirect_uri=http://localhost/client-1&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenOauth2ClientInLambdaThenRedirectForAuthorization() throws Exception {\n\t\tthis.spring.register(OAuth2ClientInLambdaConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/oauth2/authorization/registration-1\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getRedirectedUrl()).matches(\"https://provider.com/oauth2/authorize\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=client-1&\" + \"scope=user&state=.{15,}&\"\n\t\t\t\t+ \"redirect_uri=http://localhost/client-1&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void configureWhenAuthorizationCodeResponseSuccessThenAuthorizedClientSaved() throws Exception {\n\t\tthis.spring.register(OAuth2ClientConfig.class).autowire();\n\t\t// Setup the Authorization Request in the session\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, this.registration1.getRegistrationId());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(this.registration1.getProviderDetails().getAuthorizationUri())\n\t\t\t\t.clientId(this.registration1.getClientId())\n\t\t\t\t.redirectUri(\"http://localhost/client-1\")\n\t\t\t\t.state(\"state\")\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tauthorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t\tMockHttpSession session = (MockHttpSession) request.getSession();\n\t\tString principalName = \"user1\";\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, \"password\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder clientRequest = get(\"/client-1\")\n\t\t\t\t.param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t\t.param(OAuth2ParameterNames.STATE, \"state\")\n\t\t\t\t.with(authentication(authentication))\n\t\t\t\t.session(session);\n\t\tthis.mockMvc.perform(clientRequest)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"http://localhost/client-1\"));\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), authentication, request);\n\t\tassertThat(authorizedClient).isNotNull();\n\t}\n\n\t@Test\n\tpublic void configureWhenRequestCacheProvidedAndClientAuthorizationRequiredExceptionThrownThenRequestCacheUsed()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(OAuth2ClientConfig.class).autowire();\n\t\tMvcResult mvcResult = this.mockMvc.perform(get(\"/resource1\").with(user(\"user1\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tassertThat(mvcResult.getResponse().getRedirectedUrl()).matches(\"https://provider.com/oauth2/authorize\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=client-1&\" + \"scope=user&state=.{15,}&\"\n\t\t\t\t+ \"redirect_uri=http://localhost/client-1&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t\tverify(requestCache).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenRequestCacheProvidedAndClientAuthorizationSucceedsThenRequestCacheUsed() throws Exception {\n\t\tthis.spring.register(OAuth2ClientConfig.class).autowire();\n\t\t// Setup the Authorization Request in the session\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, this.registration1.getRegistrationId());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(this.registration1.getProviderDetails().getAuthorizationUri())\n\t\t\t\t.clientId(this.registration1.getClientId()).redirectUri(\"http://localhost/client-1\")\n\t\t\t\t.state(\"state\")\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tauthorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t\tMockHttpSession session = (MockHttpSession) request.getSession();\n\t\tString principalName = \"user1\";\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, \"password\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder clientRequest = get(\"/client-1\")\n\t\t\t\t.param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t\t.param(OAuth2ParameterNames.STATE, \"state\")\n\t\t\t\t.with(authentication(authentication))\n\t\t\t\t.session(session);\n\t\tthis.mockMvc.perform(clientRequest)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"http://localhost/client-1\"));\n\t\t// @formatter:on\n\t\tverify(requestCache).getRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t// gh-5521\n\t@Test\n\tpublic void configureWhenCustomAuthorizationRequestResolverSetThenAuthorizationRequestIncludesCustomParameters()\n\t\t\tthrows Exception {\n\t\t// Override default resolver\n\t\tOAuth2AuthorizationRequestResolver defaultAuthorizationRequestResolver = authorizationRequestResolver;\n\t\tauthorizationRequestResolver = mock(OAuth2AuthorizationRequestResolver.class);\n\t\tgiven(authorizationRequestResolver.resolve(any()))\n\t\t\t.willAnswer((invocation) -> defaultAuthorizationRequestResolver.resolve(invocation.getArgument(0)));\n\t\tthis.spring.register(OAuth2ClientConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/oauth2/authorization/registration-1\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tverify(authorizationRequestResolver).resolve(any());\n\t}\n\n\t@Test\n\tpublic void configureWhenCustomAuthorizationRedirectStrategySetThenAuthorizationRedirectStrategyUsed()\n\t\t\tthrows Exception {\n\t\tauthorizationRedirectStrategy = mock(RedirectStrategy.class);\n\t\tthis.spring.register(OAuth2ClientConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/oauth2/authorization/registration-1\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tverify(authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());\n\t}\n\n\t@Test\n\tpublic void configureWhenCustomAuthorizationRequestResolverBeanPresentThenAuthorizationRequestResolverUsed()\n\t\t\tthrows Exception {\n\t\tOAuth2AuthorizationRequestResolver defaultAuthorizationRequestResolver = authorizationRequestResolver;\n\t\tauthorizationRequestResolver = mock(OAuth2AuthorizationRequestResolver.class);\n\t\tgiven(authorizationRequestResolver.resolve(any()))\n\t\t\t.willAnswer((invocation) -> defaultAuthorizationRequestResolver.resolve(invocation.getArgument(0)));\n\t\tthis.spring.register(OAuth2ClientInLambdaConfig.class, AuthorizationRequestResolverConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/oauth2/authorization/registration-1\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tverify(authorizationRequestResolver).resolve(any());\n\t}\n\n\t@Test\n\tpublic void configureWhenOAuth2LoginBeansConfiguredThenNotShared() throws Exception {\n\t\tthis.spring.register(OAuth2ClientConfigWithOAuth2Login.class).autowire();\n\t\t// Setup the Authorization Request in the session\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, this.registration1.getRegistrationId());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(this.registration1.getProviderDetails().getAuthorizationUri())\n\t\t\t.clientId(this.registration1.getClientId())\n\t\t\t.redirectUri(\"http://localhost/client-1\")\n\t\t\t.state(\"state\")\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\t// @formatter:on\n\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tauthorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t\tMockHttpSession session = (MockHttpSession) request.getSession();\n\t\tString principalName = \"user1\";\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, \"password\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder clientRequest = get(\"/client-1\")\n\t\t\t.param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, \"state\")\n\t\t\t.with(authentication(authentication))\n\t\t\t.session(session);\n\t\tthis.mockMvc.perform(clientRequest)\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"http://localhost/client-1\"));\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), authentication, request);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\t// Ensure shared objects set for OAuth2 Client are not used\n\t\tClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()\n\t\t\t.getBean(ClientRegistrationRepository.class);\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = this.spring.getContext()\n\t\t\t.getBean(OAuth2AuthorizedClientRepository.class);\n\t\tverifyNoInteractions(clientRegistrationRepository, authorizedClientRepository);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class OAuth2ClientConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.requestCache((cache) -> cache\n\t\t\t\t\t.requestCache(requestCache))\n\t\t\t\t.oauth2Client((client) -> client\n\t\t\t\t\t.authorizationCodeGrant((code) -> code\n\t\t\t\t\t\t.authorizationRequestResolver(authorizationRequestResolver)\n\t\t\t\t\t\t.authorizationRedirectStrategy(authorizationRedirectStrategy)\n\t\t\t\t\t\t.accessTokenResponseClient(accessTokenResponseClient)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn clientRegistrationRepository;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn authorizedClientRepository;\n\t\t}\n\n\t\t@RestController\n\t\tclass ResourceController {\n\n\t\t\t@GetMapping(\"/resource1\")\n\t\t\tString resource1(\n\t\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"registration-1\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn \"resource1\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class OAuth2ClientInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2Client(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn clientRegistrationRepository;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn authorizedClientRepository;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class AuthorizationRequestResolverConfig {\n\n\t\t@Bean\n\t\tOAuth2AuthorizationRequestResolver authorizationRequestResolver() {\n\t\t\treturn authorizationRequestResolver;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class OAuth2ClientConfigWithOAuth2Login {\n\n\t\tprivate final ClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tClientRegistrationRepository.class);\n\n\t\tprivate final OAuth2AuthorizedClientRepository authorizedClientRepository = mock(\n\t\t\t\tOAuth2AuthorizedClientRepository.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2Client((oauth2Client) -> oauth2Client\n\t\t\t\t\t.clientRegistrationRepository(OAuth2ClientConfigurerTests.clientRegistrationRepository)\n\t\t\t\t\t.authorizedClientService(OAuth2ClientConfigurerTests.authorizedClientService)\n\t\t\t\t\t.authorizationCodeGrant((authorizationCode) -> authorizationCode\n\t\t\t\t\t\t.authorizationRequestResolver(authorizationRequestResolver)\n\t\t\t\t\t\t.authorizationRedirectStrategy(authorizationRedirectStrategy)\n\t\t\t\t\t\t.accessTokenResponseClient(accessTokenResponseClient)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t.oauth2Login((oauth2Login) -> oauth2Login\n\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository)\n\t\t\t\t\t.authorizedClientRepository(this.authorizedClientRepository)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn this.clientRegistrationRepository;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn this.authorizedClientRepository;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.apache.http.HttpHeaders;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mockito;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.NoUniqueBeanDefinitionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.event.SmartApplicationListener;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurerTests.OAuth2LoginConfigCustomWithPostProcessor.SpyObjectPostProcessor;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.context.DelegatingApplicationListener;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.session.SessionDestroyedEvent;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.HttpStatusEntryPoint;\nimport org.springframework.security.web.context.HttpRequestResponseHolder;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.NullSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\nimport org.springframework.security.web.session.HttpSessionDestroyedEvent;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.then;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\n\n/**\n * Tests for {@link OAuth2LoginConfigurer}.\n *\n * @author Kazuki Shimizu\n * @author Joe Grandja\n * @since 5.0.1\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2LoginConfigurerTests {\n\n\t// @formatter:off\n\tprivate static final ClientRegistration GOOGLE_CLIENT_REGISTRATION = CommonOAuth2Provider.GOOGLE\n\t\t\t.getBuilder(\"google\")\n\t\t\t.clientId(\"clientId\")\n\t\t\t.clientSecret(\"clientSecret\")\n\t\t\t.build();\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final ClientRegistration GITHUB_CLIENT_REGISTRATION = CommonOAuth2Provider.GITHUB\n\t\t\t.getBuilder(\"github\")\n\t\t\t.clientId(\"clientId\")\n\t\t\t.clientSecret(\"clientSecret\")\n\t\t\t.build();\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final ClientRegistration CLIENT_CREDENTIALS_REGISTRATION = TestClientRegistrations.clientCredentials()\n\t\t\t.build();\n\t// @formatter:on\n\n\tprivate ConfigurableApplicationContext context;\n\n\t@Autowired\n\tprivate FilterChainProxy springSecurityFilterChain;\n\n\t@Autowired(required = false)\n\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\t@Autowired(required = false)\n\tSecurityContextRepository securityContextRepository;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tMockMvc mvc;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate MockFilterChain filterChain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = TestMockHttpServletRequests.get(\"/login/oauth2/code/google\").build();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.filterChain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void oauth2Login() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfig.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.hasAuthority(\"OAUTH2_USER\")\n\t\t\t.isInstanceOf(OAuth2UserAuthority.class);\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tloadConfig(OAuth2LoginConfig.class, SecurityContextChangedListenerConfig.class);\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.hasAuthority(\"OAUTH2_USER\")\n\t\t\t.isInstanceOf(OAuth2UserAuthority.class);\n\t\tSecurityContextHolderStrategy strategy = this.context.getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getDeferredContext();\n\t\tSecurityContextChangedListener listener = this.context.getBean(SecurityContextChangedListener.class);\n\t\tverify(listener).securityContextChanged(setAuthentication(OAuth2AuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void requestWhenOauth2LoginInLambdaThenAuthenticationContainsOauth2UserAuthority() throws Exception {\n\t\tloadConfig(OAuth2LoginInLambdaConfig.class);\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.hasAuthority(\"OAUTH2_USER\")\n\t\t\t.isInstanceOf(OAuth2UserAuthority.class);\n\t}\n\n\t// gh-6009\n\t@Test\n\tpublic void oauth2LoginWhenSuccessThenAuthenticationSuccessEventPublished() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfig.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tassertThat(OAuth2LoginConfig.EVENTS).isNotEmpty();\n\t\tassertThat(OAuth2LoginConfig.EVENTS).hasSize(1);\n\t\tassertThat(OAuth2LoginConfig.EVENTS.get(0)).isInstanceOf(AuthenticationSuccessEvent.class);\n\t}\n\n\t@Test\n\tpublic void oauth2LoginCustomWithConfigurer() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfigCustomWithConfigurer.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"OAUTH2_USER\", \"ROLE_OAUTH2_USER\");\n\t}\n\n\t@Test\n\tpublic void oauth2LoginCustomWithBeanRegistration() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfigCustomWithBeanRegistration.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"OAUTH2_USER\", \"ROLE_OAUTH2_USER\");\n\t}\n\n\t@Test\n\tpublic void oauth2LoginCustomWithUserServiceBeanRegistration() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfigCustomUserServiceBeanRegistration.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"OAUTH2_USER\", \"ROLE_OAUTH2_USER\");\n\t}\n\n\t// gh-5488\n\t@Test\n\tpublic void oauth2LoginConfigLoginProcessingUrl() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfigLoginProcessingUrl.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.request.setRequestURI(\"/login/oauth2/google\");\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.hasAuthority(\"OAUTH2_USER\")\n\t\t\t.isInstanceOf(OAuth2UserAuthority.class);\n\t}\n\n\t// gh-5521\n\t@Test\n\tpublic void oauth2LoginWithCustomAuthorizationRequestParameters() throws Exception {\n\t\tloadConfig(OAuth2LoginConfigCustomAuthorizationRequestResolver.class);\n\t\tOAuth2AuthorizationRequestResolver resolver = this.context\n\t\t\t.getBean(OAuth2LoginConfigCustomAuthorizationRequestResolver.class).resolver;\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest result = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(\"https://accounts.google.com/authorize\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.state(\"adsfa\")\n\t\t\t\t.authorizationRequestUri(\n\t\t\t\t\t\t\"https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(resolver.resolve(any())).willReturn(result);\n\t\tString requestUri = \"/oauth2/authorization/google\";\n\t\tthis.request = TestMockHttpServletRequests.get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\n\t\t\t\t\"https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1\");\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWithCustomAuthorizationRequestParametersAndResolverAsBean() throws Exception {\n\t\tloadConfig(OAuth2LoginConfigCustomAuthorizationRequestResolverBean.class);\n\t\t// @formatter:off\n\t\t// @formatter:on\n\t\tString requestUri = \"/oauth2/authorization/google\";\n\t\tthis.request = TestMockHttpServletRequests.get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\n\t\t\t\t\"https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1\");\n\t}\n\n\t@Test\n\tpublic void requestWhenOauth2LoginWithCustomAuthorizationRequestParametersThenParametersInRedirectedUrl()\n\t\t\tthrows Exception {\n\t\tloadConfig(OAuth2LoginConfigCustomAuthorizationRequestResolverInLambda.class);\n\t\tOAuth2AuthorizationRequestResolver resolver = this.context\n\t\t\t.getBean(OAuth2LoginConfigCustomAuthorizationRequestResolverInLambda.class).resolver;\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest result = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(\"https://accounts.google.com/authorize\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.state(\"adsfa\")\n\t\t\t\t.authorizationRequestUri(\n\t\t\t\t\t\t\"https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(resolver.resolve(any())).willReturn(result);\n\t\tString requestUri = \"/oauth2/authorization/google\";\n\t\tthis.request = TestMockHttpServletRequests.get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\n\t\t\t\t\"https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1\");\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWithAuthorizationRedirectStrategyThenCustomAuthorizationRedirectStrategyUsed()\n\t\t\tthrows Exception {\n\t\tloadConfig(OAuth2LoginConfigCustomAuthorizationRedirectStrategy.class);\n\t\tRedirectStrategy redirectStrategy = this.context\n\t\t\t.getBean(OAuth2LoginConfigCustomAuthorizationRedirectStrategy.class).redirectStrategy;\n\t\tString requestUri = \"/oauth2/authorization/google\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tthen(redirectStrategy).should().sendRedirect(any(), any(), anyString());\n\t}\n\n\t@Test\n\tpublic void requestWhenOauth2LoginWithCustomAuthorizationRedirectStrategyThenCustomAuthorizationRedirectStrategyUsed()\n\t\t\tthrows Exception {\n\t\tloadConfig(OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda.class);\n\t\tRedirectStrategy redirectStrategy = this.context\n\t\t\t.getBean(OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda.class).redirectStrategy;\n\t\tString requestUri = \"/oauth2/authorization/google\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tthen(redirectStrategy).should().sendRedirect(any(), any(), anyString());\n\t}\n\n\t// gh-5347\n\t@Test\n\tpublic void oauth2LoginWithOneClientConfiguredThenRedirectForAuthorization() throws Exception {\n\t\tloadConfig(OAuth2LoginConfig.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).matches(\"/oauth2/authorization/google\");\n\t}\n\n\t// gh-6802\n\t@Test\n\tpublic void oauth2LoginWithOneClientConfiguredAndFormLoginThenRedirectDefaultLoginPage() throws Exception {\n\t\tloadConfig(OAuth2LoginConfigFormLogin.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).matches(\"/login\");\n\t}\n\n\t// gh-5347\n\t@Test\n\tpublic void oauth2LoginWithOneClientConfiguredAndRequestFaviconNotAuthenticatedThenRedirectDefaultLoginPage()\n\t\t\tthrows Exception {\n\t\tloadConfig(OAuth2LoginConfig.class);\n\t\tString requestUri = \"/favicon.ico\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.request.addHeader(HttpHeaders.ACCEPT, new MediaType(\"image\", \"*\").toString());\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).matches(\"/login\");\n\t}\n\n\t// gh-5347\n\t@Test\n\tpublic void oauth2LoginWithMultipleClientsConfiguredThenRedirectDefaultLoginPage() throws Exception {\n\t\tloadConfig(OAuth2LoginConfigMultipleClients.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).matches(\"/login\");\n\t}\n\n\t// gh-6812\n\t@Test\n\tpublic void oauth2LoginWithOneClientConfiguredAndRequestXHRNotAuthenticatedThenDoesNotRedirectForAuthorization()\n\t\t\tthrows Exception {\n\t\tloadConfig(OAuth2LoginConfig.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.request.addHeader(\"X-Requested-With\", \"XMLHttpRequest\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).doesNotMatch(\"http://localhost/oauth2/authorization/google\");\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWithHttpBasicOneClientConfiguredAndRequestXHRNotAuthenticatedThenUnauthorized()\n\t\t\tthrows Exception {\n\t\tloadConfig(OAuth2LoginWithHttpBasicConfig.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.request.addHeader(\"X-Requested-With\", \"XMLHttpRequest\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWithXHREntryPointOneClientConfiguredAndRequestXHRNotAuthenticatedThenUnauthorized()\n\t\t\tthrows Exception {\n\t\tloadConfig(OAuth2LoginWithXHREntryPointConfig.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.request.addHeader(\"X-Requested-With\", \"XMLHttpRequest\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(401);\n\t}\n\n\t// gh-9457\n\t@Test\n\tpublic void oauth2LoginWithOneAuthorizationCodeClientAndOtherClientsConfiguredThenRedirectForAuthorization()\n\t\t\tthrows Exception {\n\t\tloadConfig(OAuth2LoginConfigAuthorizationCodeClientAndOtherClients.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).matches(\"/oauth2/authorization/google\");\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWithCustomLoginPageThenRedirectCustomLoginPage() throws Exception {\n\t\tloadConfig(OAuth2LoginConfigCustomLoginPage.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).matches(\"/custom-login\");\n\t}\n\n\t@Test\n\tpublic void requestWhenOauth2LoginWithCustomLoginPageInLambdaThenRedirectCustomLoginPage() throws Exception {\n\t\tloadConfig(OAuth2LoginConfigCustomLoginPageInLambda.class);\n\t\tString requestUri = \"/\";\n\t\tthis.request = get(requestUri).build();\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getRedirectedUrl()).matches(\"/custom-login\");\n\t}\n\n\t@Test\n\tpublic void oidcLogin() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfig.class, JwtDecoderFactoryConfig.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest(\"openid\");\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthority(\"OIDC_USER\").isInstanceOf(OidcUserAuthority.class);\n\t}\n\n\t@Test\n\tpublic void requestWhenOauth2LoginInLambdaAndOidcThenAuthenticationContainsOidcUserAuthority() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginInLambdaConfig.class, JwtDecoderFactoryConfig.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest(\"openid\");\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tassertThat(authentication.getAuthorities()).hasSize(1);\n\t\tSecurityAssertions.assertThat(authentication).hasAuthority(\"OIDC_USER\").isInstanceOf(OidcUserAuthority.class);\n\t}\n\n\t@Test\n\tpublic void oidcLoginCustomWithConfigurer() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfigCustomWithConfigurer.class, JwtDecoderFactoryConfig.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest(\"openid\");\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"OIDC_USER\", \"ROLE_OIDC_USER\");\n\t}\n\n\t@Test\n\tpublic void oidcLoginCustomWithBeanRegistration() throws Exception {\n\t\t// setup application context\n\t\tloadConfig(OAuth2LoginConfigCustomWithBeanRegistration.class, JwtDecoderFactoryConfig.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest(\"openid\");\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"OIDC_USER\", \"ROLE_OIDC_USER\");\n\t}\n\n\t@Test\n\tpublic void oidcLoginCustomWithNoUniqueJwtDecoderFactory() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> loadConfig(OAuth2LoginConfig.class, NoUniqueJwtDecoderFactoryConfig.class))\n\t\t\t.withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class)\n\t\t\t.withMessageContaining(\"No qualifying bean of type \"\n\t\t\t\t\t+ \"'org.springframework.security.oauth2.jwt.JwtDecoderFactory<org.springframework.security.oauth2.client.registration.ClientRegistration>' \"\n\t\t\t\t\t+ \"available: expected single matching bean but found 2: jwtDecoderFactory1,jwtDecoderFactory2\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingOidcLogoutHandlerThenRedirects() throws Exception {\n\t\tthis.spring.register(OAuth2LoginConfigWithOidcLogoutSuccessHandler.class).autowire();\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, \"registration-id\");\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(token)).with(csrf()))\n\t\t\t.andExpect(redirectedUrl(\"https://logout?id_token_hint=id-token\"));\n\t}\n\n\t@Test\n\tpublic void configureWhenOidcSessionRegistryThenUses() {\n\t\tthis.spring.register(OAuth2LoginWithOidcSessionRegistry.class).autowire();\n\t\tOidcSessionRegistry registry = this.spring.getContext().getBean(OidcSessionRegistry.class);\n\t\tthis.spring.getContext().publishEvent(new HttpSessionDestroyedEvent(this.request.getSession()));\n\t\tverify(registry).removeSessionInformation(this.request.getSession().getId());\n\t}\n\n\t// gh-14558\n\t@Test\n\tpublic void oauth2LoginWhenDefaultsThenNoOidcSessionRegistry() {\n\t\tthis.spring.register(OAuth2LoginConfig.class).autowire();\n\t\tDelegatingApplicationListener listener = this.spring.getContext().getBean(DelegatingApplicationListener.class);\n\t\tList<SmartApplicationListener> listeners = (List<SmartApplicationListener>) ReflectionTestUtils\n\t\t\t.getField(listener, \"listeners\");\n\t\tassertThat(listeners.stream()\n\t\t\t.filter((l) -> l.supportsEventType(SessionDestroyedEvent.class))\n\t\t\t.collect(Collectors.toList())).isEmpty();\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenOAuth2ClientBeansConfiguredThenNotShared() throws Exception {\n\t\tthis.spring.register(OAuth2LoginConfigWithOAuth2Client.class, JwtDecoderFactoryConfig.class).autowire();\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest(\"openid\");\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthority(\"OIDC_USER\").isInstanceOf(OidcUserAuthority.class);\n\t\t// Ensure shared objects set for OAuth2 Client are not used\n\t\tClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()\n\t\t\t.getBean(ClientRegistrationRepository.class);\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = this.spring.getContext()\n\t\t\t.getBean(OAuth2AuthorizedClientRepository.class);\n\t\tverifyNoInteractions(clientRegistrationRepository, authorizedClientRepository);\n\t}\n\n\t// gh-17175\n\t@Test\n\tpublic void oauth2LoginWhenAuthenticationProviderPostProcessorThenUses() throws Exception {\n\t\tloadConfig(OAuth2LoginConfigCustomWithPostProcessor.class);\n\t\t// setup authorization request\n\t\tOAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, this.request, this.response);\n\t\t// setup authentication parameters\n\t\tthis.request.setParameter(\"code\", \"code123\");\n\t\tthis.request.setParameter(\"state\", authorizationRequest.getState());\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tverify(this.context.getBean(SpyObjectPostProcessor.class).spy).authenticate(any());\n\t}\n\n\t// gh-16623\n\t@Test\n\tpublic void oauth2LoginWithCustomSecurityContextRepository() {\n\t\tassertThatNoException().isThrownBy(() -> loadConfig(OAuth2LoginConfigSecurityContextRepository.class));\n\t}\n\n\tprivate void loadConfig(Class<?>... configs) {\n\t\tAnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();\n\t\tapplicationContext.register(configs);\n\t\tapplicationContext.refresh();\n\t\tapplicationContext.getAutowireCapableBeanFactory().autowireBean(this);\n\t\tthis.context = applicationContext;\n\t}\n\n\tprivate OAuth2AuthorizationRequest createOAuth2AuthorizationRequest(String... scopes) {\n\t\treturn this.createOAuth2AuthorizationRequest(GOOGLE_CLIENT_REGISTRATION, scopes);\n\t}\n\n\tprivate OAuth2AuthorizationRequest createOAuth2AuthorizationRequest(ClientRegistration registration,\n\t\t\tString... scopes) {\n\t\t// @formatter:off\n\t\treturn OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(registration.getProviderDetails().getAuthorizationUri())\n\t\t\t\t.clientId(registration.getClientId())\n\t\t\t\t.state(\"state123\")\n\t\t\t\t.redirectUri(\"http://localhost\")\n\t\t\t\t.attributes(Collections.singletonMap(OAuth2ParameterNames.REGISTRATION_ID,\n\t\t\t\t\t\tregistration.getRegistrationId()))\n\t\t\t\t.scope(scopes)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> createOauth2AccessTokenResponseClient() {\n\t\treturn (request) -> {\n\t\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\t\tif (request.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains(\"openid\")) {\n\t\t\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, \"token123\");\n\t\t\t}\n\t\t\treturn OAuth2AccessTokenResponse.withToken(\"accessToken123\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t};\n\t}\n\n\tprivate static OAuth2UserService<OAuth2UserRequest, OAuth2User> createOauth2UserService() {\n\t\tMap<String, Object> userAttributes = Collections.singletonMap(\"name\", \"spring\");\n\t\treturn (request) -> new DefaultOAuth2User(Collections.singleton(new OAuth2UserAuthority(userAttributes)),\n\t\t\t\tuserAttributes, \"name\");\n\t}\n\n\tprivate static OAuth2UserService<OidcUserRequest, OidcUser> createOidcUserService() {\n\t\tOidcIdToken idToken = TestOidcIdTokens.idToken().build();\n\t\treturn (request) -> new DefaultOidcUser(Collections.singleton(new OidcUserAuthority(idToken)), idToken);\n\t}\n\n\tprivate static GrantedAuthoritiesMapper createGrantedAuthoritiesMapper() {\n\t\treturn (authorities) -> {\n\t\t\tboolean isOidc = OidcUserAuthority.class.isInstance(authorities.iterator().next());\n\t\t\tList<GrantedAuthority> mappedAuthorities = new ArrayList<>(authorities);\n\t\t\tmappedAuthorities.add(new SimpleGrantedAuthority(isOidc ? \"ROLE_OIDC_USER\" : \"ROLE_OAUTH2_USER\"));\n\t\t\treturn mappedAuthorities;\n\t\t};\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfig extends CommonSecurityFilterChainConfig\n\t\t\timplements ApplicationListener<AuthenticationSuccessEvent> {\n\n\t\tstatic List<AuthenticationSuccessEvent> EVENTS = new ArrayList<>();\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION)));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onApplicationEvent(AuthenticationSuccessEvent event) {\n\t\t\tEVENTS.add(event);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigFormLogin extends CommonSecurityFilterChainConfig {\n\n\t\tprivate final InMemoryClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(\n\t\t\t\tGOOGLE_CLIENT_REGISTRATION);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository))\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginInLambdaConfig extends CommonLambdaSecurityFilterChainConfig\n\t\t\timplements ApplicationListener<AuthenticationSuccessEvent> {\n\n\t\tstatic List<AuthenticationSuccessEvent> EVENTS = new ArrayList<>();\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onApplicationEvent(AuthenticationSuccessEvent event) {\n\t\t\tEVENTS.add(event);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomWithConfigurer extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))\n\t\t\t\t\t.userInfoEndpoint((info) -> info\n\t\t\t\t\t\t.userAuthoritiesMapper(createGrantedAuthoritiesMapper())));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomWithBeanRegistration extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn new InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION);\n\t\t}\n\n\t\t@Bean\n\t\tGrantedAuthoritiesMapper grantedAuthoritiesMapper() {\n\t\t\treturn createGrantedAuthoritiesMapper();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomUserServiceBeanRegistration {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.securityContext((context) -> context\n\t\t\t\t\t.securityContextRepository(securityContextRepository()))\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.tokenEndpoint((token) -> token\n\t\t\t\t\t\t.accessTokenResponseClient(createOauth2AccessTokenResponseClient())));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn new InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION);\n\t\t}\n\n\t\t@Bean\n\t\tGrantedAuthoritiesMapper grantedAuthoritiesMapper() {\n\t\t\treturn createGrantedAuthoritiesMapper();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityContextRepository securityContextRepository() {\n\t\t\treturn new HttpSessionSecurityContextRepository();\n\t\t}\n\n\t\t@Bean\n\t\tHttpSessionOAuth2AuthorizationRequestRepository oauth2AuthorizationRequestRepository() {\n\t\t\treturn new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {\n\t\t\treturn createOauth2UserService();\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {\n\t\t\treturn createOidcUserService();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigLoginProcessingUrl extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))\n\t\t\t\t\t.loginProcessingUrl(\"/login/oauth2/*\"));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigSecurityContextRepository extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))\n\t\t\t\t\t.securityContextRepository(new NullSecurityContextRepository()));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomAuthorizationRequestResolver extends CommonSecurityFilterChainConfig {\n\n\t\tprivate ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(\n\t\t\t\tGOOGLE_CLIENT_REGISTRATION);\n\n\t\tOAuth2AuthorizationRequestResolver resolver = mock(OAuth2AuthorizationRequestResolver.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository)\n\t\t\t\t\t.authorizationEndpoint((authorize) -> authorize\n\t\t\t\t\t\t.authorizationRequestResolver(this.resolver)));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomAuthorizationRequestResolverBean extends CommonSecurityFilterChainConfig {\n\n\t\tprivate ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(\n\t\t\t\tGOOGLE_CLIENT_REGISTRATION);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository)\n\t\t\t\t\t.authorizationEndpoint(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationRequestResolver resolver() {\n\t\t\tOAuth2AuthorizationRequestResolver resolver = mock(OAuth2AuthorizationRequestResolver.class);\n\t\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationRequest result = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t\t.authorizationUri(\"https://accounts.google.com/authorize\")\n\t\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t\t.state(\"adsfa\")\n\t\t\t\t\t.authorizationRequestUri(\n\t\t\t\t\t\t\t\"https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=clientId&scope=openid+profile+email&state=state&redirect_uri=http%3A%2F%2Flocalhost%2Flogin%2Foauth2%2Fcode%2Fgoogle&custom-param1=custom-value1\")\n\t\t\t\t\t.build();\n\t\t\tgiven(resolver.resolve(any())).willReturn(result);\n\t\t\t// @formatter:on\n\t\t\treturn resolver;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomAuthorizationRequestResolverInLambda\n\t\t\textends CommonLambdaSecurityFilterChainConfig {\n\n\t\tprivate ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(\n\t\t\t\tGOOGLE_CLIENT_REGISTRATION);\n\n\t\tOAuth2AuthorizationRequestResolver resolver = mock(OAuth2AuthorizationRequestResolver.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository)\n\t\t\t\t\t\t.authorizationEndpoint((authorizationEndpoint) -> authorizationEndpoint\n\t\t\t\t\t\t\t\t.authorizationRequestResolver(this.resolver)\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomAuthorizationRedirectStrategy extends CommonSecurityFilterChainConfig {\n\n\t\tprivate final ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(\n\t\t\t\tGOOGLE_CLIENT_REGISTRATION);\n\n\t\tRedirectStrategy redirectStrategy = mock(RedirectStrategy.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository)\n\t\t\t\t\t\t.authorizationEndpoint((authorizationEndpoint) -> authorizationEndpoint\n\t\t\t\t\t\t\t\t.authorizationRedirectStrategy(this.redirectStrategy)\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomAuthorizationRedirectStrategyInLambda\n\t\t\textends CommonLambdaSecurityFilterChainConfig {\n\n\t\tprivate final ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(\n\t\t\t\tGOOGLE_CLIENT_REGISTRATION);\n\n\t\tRedirectStrategy redirectStrategy = mock(RedirectStrategy.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain configureFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository)\n\t\t\t\t\t\t\t\t\t.authorizationEndpoint((authorizationEndpoint) -> authorizationEndpoint\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authorizationRedirectStrategy(this.redirectStrategy)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigMultipleClients extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(\n\t\t\t\t\t\t\tGOOGLE_CLIENT_REGISTRATION, GITHUB_CLIENT_REGISTRATION)));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigAuthorizationCodeClientAndOtherClients extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(\n\t\t\t\t\t\t\tGOOGLE_CLIENT_REGISTRATION, CLIENT_CREDENTIALS_REGISTRATION)));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomLoginPage extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))\n\t\t\t\t\t.loginPage(\"/custom-login\"));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomLoginPageInLambda extends CommonLambdaSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))\n\t\t\t\t\t\t\t.loginPage(\"/custom-login\")\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigWithOidcLogoutSuccessHandler extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t.logoutSuccessHandler(oidcLogoutSuccessHandler()));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t\t@Bean\n\t\tOidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler() {\n\t\t\treturn new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository());\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\tMap<String, Object> providerMetadata = Collections.singletonMap(\"end_session_endpoint\", \"https://logout\");\n\t\t\treturn new InMemoryClientRegistrationRepository(TestClientRegistrations.clientRegistration()\n\t\t\t\t.providerConfigurationMetadata(providerMetadata)\n\t\t\t\t.build());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginWithHttpBasicConfig extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION)))\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginWithOidcSessionRegistry {\n\n\t\tprivate final OidcSessionRegistry registry = mock(OidcSessionRegistry.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))\n\t\t\t\t\t.oidcSessionRegistry(this.registry)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tOidcSessionRegistry oidcSessionRegistry() {\n\t\t\treturn this.registry;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginWithXHREntryPointConfig extends CommonSecurityFilterChainConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION)))\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.defaultAuthenticationEntryPointFor(\n\t\t\t\t\t\tnew HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),\n\t\t\t\t\t\tnew RequestHeaderRequestMatcher(\"X-Requested-With\", \"XMLHttpRequest\")));\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigWithOAuth2Client extends CommonLambdaSecurityFilterChainConfig {\n\n\t\tprivate final ClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tClientRegistrationRepository.class);\n\n\t\tprivate final OAuth2AuthorizedClientRepository authorizedClientRepository = mock(\n\t\t\t\tOAuth2AuthorizedClientRepository.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((oauth2Login) -> oauth2Login\n\t\t\t\t\t.clientRegistrationRepository(\n\t\t\t\t\t\tnew InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION))\n\t\t\t\t\t.authorizedClientRepository(new HttpSessionOAuth2AuthorizedClientRepository())\n\t\t\t\t)\n\t\t\t\t.oauth2Client((oauth2Client) -> oauth2Client\n\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository)\n\t\t\t\t\t.authorizedClientRepository(this.authorizedClientRepository)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn super.configureFilterChain(http);\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn this.clientRegistrationRepository;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn this.authorizedClientRepository;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginConfigCustomWithPostProcessor {\n\n\t\tprivate final ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(\n\t\t\t\tGOOGLE_CLIENT_REGISTRATION);\n\n\t\tprivate final ObjectPostProcessor<AuthenticationProvider> postProcessor = new SpyObjectPostProcessor();\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Login((oauth2Login) -> oauth2Login\n\t\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository)\n\t\t\t\t\t.withObjectPostProcessor(this.postProcessor)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tObjectPostProcessor<AuthenticationProvider> mockPostProcessor() {\n\t\t\treturn this.postProcessor;\n\t\t}\n\n\t\t@Bean\n\t\tHttpSessionOAuth2AuthorizationRequestRepository oauth2AuthorizationRequestRepository() {\n\t\t\treturn new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\t}\n\n\t\tstatic class SpyObjectPostProcessor implements ObjectPostProcessor<AuthenticationProvider> {\n\n\t\t\tAuthenticationProvider spy;\n\n\t\t\t@Override\n\t\t\tpublic <O extends AuthenticationProvider> O postProcess(O object) {\n\t\t\t\tO spy = Mockito.spy(object);\n\t\t\t\tthis.spy = spy;\n\t\t\t\treturn spy;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tprivate abstract static class CommonSecurityFilterChainConfig {\n\n\t\tSecurityFilterChain configureFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.securityContext((context) -> context\n\t\t\t\t\t.securityContextRepository(securityContextRepository()))\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.tokenEndpoint((token) -> token\n\t\t\t\t\t\t.accessTokenResponseClient(createOauth2AccessTokenResponseClient()))\n\t\t\t\t\t.userInfoEndpoint((info) -> info\n\t\t\t\t\t\t.userService(createOauth2UserService())\n\t\t\t\t\t\t.oidcUserService(createOidcUserService())));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityContextRepository securityContextRepository() {\n\t\t\treturn new HttpSessionSecurityContextRepository();\n\t\t}\n\n\t\t@Bean\n\t\tHttpSessionOAuth2AuthorizationRequestRepository oauth2AuthorizationRequestRepository() {\n\t\t\treturn new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\t}\n\n\t}\n\n\tprivate abstract static class CommonLambdaSecurityFilterChainConfig {\n\n\t\tSecurityFilterChain configureFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.securityContext((securityContext) -> securityContext\n\t\t\t\t\t\t.securityContextRepository(securityContextRepository())\n\t\t\t\t)\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t\t.tokenEndpoint((tokenEndpoint) -> tokenEndpoint\n\t\t\t\t\t\t\t\t.accessTokenResponseClient(createOauth2AccessTokenResponseClient())\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.userInfoEndpoint((userInfoEndpoint) -> userInfoEndpoint\n\t\t\t\t\t\t\t\t.userService(createOauth2UserService())\n\t\t\t\t\t\t\t\t.oidcUserService(createOidcUserService())\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityContextRepository securityContextRepository() {\n\t\t\treturn new HttpSessionSecurityContextRepository();\n\t\t}\n\n\t\t@Bean\n\t\tHttpSessionOAuth2AuthorizationRequestRepository oauth2AuthorizationRequestRepository() {\n\t\t\treturn new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class JwtDecoderFactoryConfig {\n\n\t\t@Bean\n\t\tJwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {\n\t\t\treturn (clientRegistration) -> getJwtDecoder();\n\t\t}\n\n\t\tprivate static JwtDecoder getJwtDecoder() {\n\t\t\tMap<String, Object> claims = new HashMap<>();\n\t\t\tclaims.put(IdTokenClaimNames.SUB, \"sub123\");\n\t\t\tclaims.put(IdTokenClaimNames.ISS, \"http://localhost/iss\");\n\t\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"clientId\", \"a\", \"u\", \"d\"));\n\t\t\tclaims.put(IdTokenClaimNames.AZP, \"clientId\");\n\t\t\tJwt jwt = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\t\tJwtDecoder jwtDecoder = mock(JwtDecoder.class);\n\t\t\tgiven(jwtDecoder.decode(any())).willReturn(jwt);\n\t\t\treturn jwtDecoder;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class NoUniqueJwtDecoderFactoryConfig {\n\n\t\t@Bean\n\t\tJwtDecoderFactory<ClientRegistration> jwtDecoderFactory1() {\n\t\t\treturn (clientRegistration) -> JwtDecoderFactoryConfig.getJwtDecoder();\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoderFactory<ClientRegistration> jwtDecoderFactory2() {\n\t\t\treturn (clientRegistration) -> JwtDecoderFactoryConfig.getJwtDecoder();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens;\nimport org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class OidcBackChannelLogoutHandlerTests {\n\n\tprivate final OidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry();\n\n\tprivate final OidcBackChannelLogoutAuthentication token = new OidcBackChannelLogoutAuthentication(\n\t\t\tTestOidcLogoutTokens.withSubject(\"issuer\", \"subject\").build(),\n\t\t\tTestClientRegistrations.clientRegistration().build());\n\n\t// gh-14553\n\t@Test\n\tpublic void computeLogoutEndpointWhenDifferentHostnameThenLocalhost() {\n\t\tOidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(this.sessionRegistry);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/back-channel/logout\");\n\t\tlogoutHandler.setLogoutUri(\"{baseScheme}://localhost{basePort}/logout\");\n\t\trequest.setServerName(\"host.docker.internal\");\n\t\trequest.setServerPort(8090);\n\t\tString endpoint = logoutHandler.computeLogoutEndpoint(request, this.token);\n\t\tassertThat(endpoint).startsWith(\"http://localhost:8090/logout\");\n\t}\n\n\t@Test\n\tpublic void computeLogoutEndpointWhenUsingBaseUrlTemplateThenServerName() {\n\t\tOidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(this.sessionRegistry);\n\t\tlogoutHandler.setLogoutUri(\"{baseUrl}/logout\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/back-channel/logout\");\n\t\trequest.setServerName(\"host.docker.internal\");\n\t\trequest.setServerPort(8090);\n\t\tString endpoint = logoutHandler.computeLogoutEndpoint(request, this.token);\n\t\tassertThat(endpoint).startsWith(\"http://host.docker.internal:8090/logout\");\n\t}\n\n\t// gh-14609\n\t@Test\n\tpublic void computeLogoutEndpointWhenLogoutUriThenUses() {\n\t\tOidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(this.sessionRegistry);\n\t\tlogoutHandler.setLogoutUri(\"http://localhost:8090/logout\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/back-channel/logout\");\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"server-one.com\");\n\t\trequest.setServerPort(80);\n\t\tString endpoint = logoutHandler.computeLogoutEndpoint(request, this.token);\n\t\tassertThat(endpoint).startsWith(\"http://localhost:8090/logout\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.io.IOException;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.interfaces.RSAPublicKey;\nimport java.time.Instant;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.ImmutableJWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.oauth2.sdk.Scope;\nimport com.nimbusds.oauth2.sdk.token.BearerAccessToken;\nimport com.nimbusds.openid.connect.sdk.token.OIDCTokens;\nimport jakarta.annotation.PreDestroy;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpSession;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.htmlunit.util.UrlUtils;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimNames;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens;\nimport org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link OidcLogoutConfigurer}\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OidcLogoutConfigurerTests {\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired(required = false)\n\tprivate MockWebServer web;\n\n\t@Autowired\n\tprivate ClientRegistration clientRegistration;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tvoid logoutWhenDefaultsThenRemotelyInvalidatesSessions() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMockHttpSession session = login();\n\t\tString logoutToken = this.mvc.perform(get(\"/token/logout\").session(session))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", logoutToken))\n\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(session)).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tvoid logoutWhenInvalidLogoutTokenThenBadRequest() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/token/logout\")).andExpect(status().isUnauthorized());\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMvcResult result = this.mvc.perform(get(\"/oauth2/authorization/\" + registrationId))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\tString redirectUrl = UrlUtils.decode(result.getResponse().getRedirectedUrl());\n\t\tString state = this.mvc\n\t\t\t.perform(get(redirectUrl)\n\t\t\t\t.with(httpBasic(this.clientRegistration.getClientId(), this.clientRegistration.getClientSecret())))\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tresult = this.mvc\n\t\t\t.perform(get(\"/login/oauth2/code/\" + registrationId).param(\"code\", \"code\")\n\t\t\t\t.param(\"state\", state)\n\t\t\t\t.session(session))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tsession = (MockHttpSession) result.getRequest().getSession();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", \"invalid\"))\n\t\t\t.andExpect(status().isBadRequest());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(session)).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tvoid logoutWhenLogoutTokenSpecifiesOneSessionThenRemotelyInvalidatesOnlyThatSession() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMockHttpSession one = login();\n\t\tMockHttpSession two = login();\n\t\tMockHttpSession three = login();\n\t\tString logoutToken = this.mvc.perform(get(\"/token/logout\").session(one))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", logoutToken))\n\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(one)).andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(two)).andExpect(status().isOk());\n\t\tlogoutToken = this.mvc.perform(get(\"/token/logout/all\").session(three))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", logoutToken))\n\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(two)).andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(three)).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tvoid logoutWhenRemoteLogoutUriThenUses() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, LogoutUriConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMockHttpSession one = login();\n\t\tString logoutToken = this.mvc.perform(get(\"/token/logout/all\").session(one))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", logoutToken))\n\t\t\t.andExpect(status().isBadRequest())\n\t\t\t.andExpect(content().string(containsString(\"partial_logout\")))\n\t\t\t.andExpect(content().string(containsString(\"not all sessions were terminated\")));\n\t\tthis.mvc.perform(get(\"/token/logout\").session(one)).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tvoid logoutWhenSelfRemoteLogoutUriThenUses() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, SelfLogoutUriConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMockHttpSession session = login();\n\t\tString logoutToken = this.mvc.perform(get(\"/token/logout\").session(session))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", logoutToken))\n\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(session)).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tvoid logoutWhenDifferentCookieNameThenUses() throws Exception {\n\t\tthis.spring.register(OidcProviderConfig.class, CookieConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMockHttpSession session = login();\n\t\tString logoutToken = this.mvc.perform(get(\"/token/logout\").session(session))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", logoutToken))\n\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(session)).andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tvoid logoutWhenRemoteLogoutFailsThenReportsPartialLogout() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithBrokenLogoutConfig.class).autowire();\n\t\tLogoutHandler logoutHandler = this.spring.getContext().getBean(LogoutHandler.class);\n\t\twillThrow(IllegalStateException.class).given(logoutHandler).logout(any(), any(), any());\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMockHttpSession one = login();\n\t\tString logoutToken = this.mvc.perform(get(\"/token/logout/all\").session(one))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", logoutToken))\n\t\t\t.andExpect(status().isBadRequest())\n\t\t\t.andExpect(content().string(containsString(\"partial_logout\")));\n\t\tthis.mvc.perform(get(\"/token/logout\").session(one)).andExpect(status().isOk());\n\t}\n\n\t@Test\n\tvoid logoutWhenCustomComponentsThenUses() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithCustomComponentsConfig.class)\n\t\t\t.autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMockHttpSession session = login();\n\t\tString logoutToken = this.mvc.perform(get(\"/token/logout\").session(session))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tthis.mvc\n\t\t\t.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t.param(\"logout_token\", logoutToken))\n\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/token/logout\").session(session)).andExpect(status().isUnauthorized());\n\t\tOidcSessionRegistry sessionRegistry = this.spring.getContext().getBean(OidcSessionRegistry.class);\n\t\tverify(sessionRegistry).saveSessionInformation(any());\n\t\tverify(sessionRegistry).removeSessionInformation(any(OidcLogoutToken.class));\n\t}\n\n\t@Test\n\tvoid logoutWhenProviderIssuerMissingThenThrowIllegalArgumentException() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, ProviderIssuerMissingConfig.class)\n\t\t\t.autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMockHttpSession session = login();\n\t\tString logoutToken = this.mvc.perform(get(\"/token/logout\").session(session))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.mvc.perform(post(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t\t\t.param(\"logout_token\", logoutToken)));\n\t}\n\n\tprivate MockHttpSession login() throws Exception {\n\t\tMockMvcDispatcher dispatcher = (MockMvcDispatcher) this.web.getDispatcher();\n\t\tthis.mvc.perform(get(\"/token/logout\")).andExpect(status().isUnauthorized());\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tMvcResult result = this.mvc.perform(get(\"/oauth2/authorization/\" + registrationId))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\tString redirectUrl = UrlUtils.decode(result.getResponse().getRedirectedUrl());\n\t\tString state = this.mvc\n\t\t\t.perform(get(redirectUrl)\n\t\t\t\t.with(httpBasic(this.clientRegistration.getClientId(), this.clientRegistration.getClientSecret())))\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\t\tresult = this.mvc\n\t\t\t.perform(get(\"/login/oauth2/code/\" + registrationId).param(\"code\", \"code\")\n\t\t\t\t.param(\"state\", state)\n\t\t\t\t.session(session))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tsession = (MockHttpSession) result.getRequest().getSession();\n\t\tdispatcher.registerSession(session);\n\t\treturn session;\n\t}\n\n\t@Configuration\n\tstatic class RegistrationConfig {\n\n\t\t@Autowired(required = false)\n\t\tMockWebServer web;\n\n\t\t@Bean\n\t\tClientRegistration clientRegistration() {\n\t\t\tif (this.web == null) {\n\t\t\t\treturn TestClientRegistrations.clientRegistration().build();\n\t\t\t}\n\t\t\tString issuer = this.web.url(\"/\").toString();\n\t\t\treturn TestClientRegistrations.clientRegistration()\n\t\t\t\t.issuerUri(issuer)\n\t\t\t\t.jwkSetUri(issuer + \"jwks\")\n\t\t\t\t.tokenUri(issuer + \"token\")\n\t\t\t\t.userInfoUri(issuer + \"user\")\n\t\t\t\t.scope(\"openid\")\n\t\t\t\t.build();\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository(ClientRegistration clientRegistration) {\n\t\t\treturn new InMemoryClientRegistrationRepository(clientRegistration);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class DefaultConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class LogoutUriConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t\t.oidcLogout((oidc) -> oidc\n\t\t\t\t\t\t.backChannel((backchannel) -> backchannel.logoutUri(\"http://localhost/wrong\"))\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class SelfLogoutUriConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t.oidcLogout((oidc) -> oidc\n\t\t\t\t\t.backChannel(Customizer.withDefaults())\n\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class CookieConfig {\n\n\t\tprivate final MockWebServer server = new MockWebServer();\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t.oidcLogout((oidc) -> oidc\n\t\t\t\t\t.backChannel(Customizer.withDefaults())\n\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tOidcSessionRegistry sessionRegistry() {\n\t\t\treturn new InMemoryOidcSessionRegistry();\n\t\t}\n\n\t\t@Bean\n\t\tOidcBackChannelLogoutHandler oidcLogoutHandler(OidcSessionRegistry sessionRegistry) {\n\t\t\tOidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(sessionRegistry);\n\t\t\tlogoutHandler.setSessionCookieName(\"SESSION\");\n\t\t\treturn logoutHandler;\n\t\t}\n\n\t\t@Bean\n\t\tMockWebServer web(ObjectProvider<MockMvc> mvc) {\n\t\t\tMockMvcDispatcher dispatcher = new MockMvcDispatcher(mvc);\n\t\t\tdispatcher.setAssertion((rr) -> {\n\t\t\t\tString cookie = rr.getHeaders().get(\"Cookie\");\n\t\t\t\tif (cookie == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tassertThat(cookie).contains(\"SESSION\").doesNotContain(\"JSESSIONID\");\n\t\t\t});\n\t\t\tthis.server.setDispatcher(dispatcher);\n\t\t\treturn this.server;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.server.shutdown();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class WithCustomComponentsConfig {\n\n\t\tOidcSessionRegistry sessionRegistry = spy(new InMemoryOidcSessionRegistry());\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tOidcSessionRegistry sessionRegistry() {\n\t\t\treturn this.sessionRegistry;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class WithBrokenLogoutConfig {\n\n\t\tprivate final LogoutHandler logoutHandler = mock(LogoutHandler.class);\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t\t.logout((logout) -> logout.addLogoutHandler(this.logoutHandler))\n\t\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t\t.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tLogoutHandler logoutHandler() {\n\t\t\treturn this.logoutHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ProviderIssuerMissingRegistrationConfig {\n\n\t\t@Autowired(required = false)\n\t\tMockWebServer web;\n\n\t\t@Bean\n\t\tClientRegistration clientRegistration() {\n\t\t\tif (this.web == null) {\n\t\t\t\treturn TestClientRegistrations.clientRegistration().issuerUri(null).build();\n\t\t\t}\n\t\t\tString issuer = this.web.url(\"/\").toString();\n\t\t\treturn TestClientRegistrations.clientRegistration()\n\t\t\t\t.issuerUri(null)\n\t\t\t\t.jwkSetUri(issuer + \"jwks\")\n\t\t\t\t.tokenUri(issuer + \"token\")\n\t\t\t\t.userInfoUri(issuer + \"user\")\n\t\t\t\t.scope(\"openid\")\n\t\t\t\t.build();\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository(ClientRegistration clientRegistration) {\n\t\t\treturn new InMemoryClientRegistrationRepository(clientRegistration);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(ProviderIssuerMissingRegistrationConfig.class)\n\tstatic class ProviderIssuerMissingConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t\t.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@RestController\n\tstatic class OidcProviderConfig {\n\n\t\tprivate static final RSAKey key = key();\n\n\t\tprivate static final JWKSource<SecurityContext> jwks = jwks(key);\n\n\t\tprivate static RSAKey key() {\n\t\t\ttry {\n\t\t\t\tKeyPair pair = KeyPairGenerator.getInstance(\"RSA\").generateKeyPair();\n\t\t\t\treturn new RSAKey.Builder((RSAPublicKey) pair.getPublic()).privateKey(pair.getPrivate()).build();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate static JWKSource<SecurityContext> jwks(RSAKey key) {\n\t\t\ttry {\n\t\t\t\treturn new ImmutableJWKSet<>(new JWKSet(key));\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate final String username = \"user\";\n\n\t\tprivate final JwtEncoder encoder = new NimbusJwtEncoder(jwks);\n\n\t\tprivate String nonce;\n\n\t\t@Autowired\n\t\tClientRegistration registration;\n\n\t\t@Autowired(required = false)\n\t\tMockWebServer web;\n\n\t\t@Bean\n\t\t@Order(0)\n\t\tSecurityFilterChain authorizationServer(HttpSecurity http, ClientRegistration registration) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(\"/jwks\", \"/login/oauth/authorize\", \"/nonce\", \"/token\", \"/token/logout\", \"/user\")\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.requestMatchers(\"/jwks\").permitAll()\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t.jwt((jwt) -> jwt.jwkSetUri(registration.getProviderDetails().getJwkSetUri()))\n\t\t\t\t);\n\t\t\t// @formatter:off\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService users(ClientRegistration registration) {\n\t\t\treturn new InMemoryUserDetailsManager(User.withUsername(registration.getClientId())\n\t\t\t\t\t.password(\"{noop}\" + registration.getClientSecret()).authorities(\"APP\").build());\n\t\t}\n\n\t\t@GetMapping(\"/login/oauth/authorize\")\n\t\tString nonce(@RequestParam(\"nonce\") String nonce, @RequestParam(\"state\") String state) {\n\t\t\tthis.nonce = nonce;\n\t\t\treturn state;\n\t\t}\n\n\t\t@PostMapping(\"/token\")\n\t\tMap<String, Object> accessToken(HttpServletRequest request) {\n\t\t\tHttpSession session = request.getSession();\n\t\t\tJwtEncoderParameters parameters = JwtEncoderParameters\n\t\t\t\t\t.from(JwtClaimsSet.builder().id(\"id\").subject(this.username)\n\t\t\t\t\t\t\t.issuer(getIssuerUri()).issuedAt(Instant.now())\n\t\t\t\t\t\t\t.expiresAt(Instant.now().plusSeconds(86400)).claim(\"scope\", \"openid\").build());\n\t\t\tString token = this.encoder.encode(parameters).getTokenValue();\n\t\t\treturn new OIDCTokens(idToken(session.getId()), new BearerAccessToken(token, 86400, new Scope(\"openid\")), null)\n\t\t\t\t\t.toJSONObject();\n\t\t}\n\n\t\tString idToken(String sessionId) {\n\t\t\tOidcIdToken token = TestOidcIdTokens.idToken().issuer(getIssuerUri())\n\t\t\t\t\t.subject(this.username).expiresAt(Instant.now().plusSeconds(86400))\n\t\t\t\t\t.audience(List.of(this.registration.getClientId())).nonce(this.nonce)\n\t\t\t\t\t.claim(LogoutTokenClaimNames.SID, sessionId).build();\n\t\t\tJwtEncoderParameters parameters = JwtEncoderParameters\n\t\t\t\t\t.from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build());\n\t\t\treturn this.encoder.encode(parameters).getTokenValue();\n\t\t}\n\n\t\tprivate String getIssuerUri() {\n\t\t\tif (this.web == null) {\n\t\t\t\treturn TestClientRegistrations.clientRegistration().build().getProviderDetails().getIssuerUri();\n\t\t\t}\n\t\t\treturn this.web.url(\"/\").toString();\n\t\t}\n\n\t\t@GetMapping(\"/user\")\n\t\tMap<String, Object> userinfo() {\n\t\t\treturn Map.of(\"sub\", this.username, \"id\", this.username);\n\t\t}\n\n\t\t@GetMapping(\"/jwks\")\n\t\tString jwks() {\n\t\t\treturn new JWKSet(key).toString();\n\t\t}\n\n\t\t@GetMapping(\"/token/logout\")\n\t\tString logoutToken(@AuthenticationPrincipal OidcUser user) {\n\t\t\tOidcLogoutToken token = TestOidcLogoutTokens.withUser(user)\n\t\t\t\t\t.audience(List.of(this.registration.getClientId())).build();\n\t\t\tJwsHeader header = JwsHeader.with(SignatureAlgorithm.RS256).type(\"logout+jwt\").build();\n\t\t\tJwtClaimsSet claims = JwtClaimsSet.builder().claims((c) -> c.putAll(token.getClaims())).build();\n\t\t\tJwtEncoderParameters parameters = JwtEncoderParameters.from(header, claims);\n\t\t\treturn this.encoder.encode(parameters).getTokenValue();\n\t\t}\n\n\t\t@GetMapping(\"/token/logout/all\")\n\t\tString logoutTokenAll(@AuthenticationPrincipal OidcUser user) {\n\t\t\tOidcLogoutToken token = TestOidcLogoutTokens.withUser(user)\n\t\t\t\t\t.audience(List.of(this.registration.getClientId()))\n\t\t\t\t\t.claims((claims) -> claims.remove(LogoutTokenClaimNames.SID)).build();\n\t\t\tJwsHeader header = JwsHeader.with(SignatureAlgorithm.RS256).type(\"JWT\").build();\n\t\t\tJwtClaimsSet claims = JwtClaimsSet.builder().claims((c) -> c.putAll(token.getClaims())).build();\n\t\t\tJwtEncoderParameters parameters = JwtEncoderParameters.from(header, claims);\n\t\t\treturn this.encoder.encode(parameters).getTokenValue();\n\t\t}\n\t}\n\n\t@Configuration\n\tstatic class WebServerConfig {\n\n\t\tprivate final MockWebServer server = new MockWebServer();\n\n\t\t@Bean\n\t\tMockWebServer web(ObjectProvider<MockMvc> mvc) {\n\t\t\tthis.server.setDispatcher(new MockMvcDispatcher(mvc));\n\t\t\treturn this.server;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.server.shutdown();\n\t\t}\n\n\t}\n\n\tprivate static class MockMvcDispatcher extends Dispatcher {\n\n\t\tprivate final Map<String, MockHttpSession> session = new ConcurrentHashMap<>();\n\n\t\tprivate final ObjectProvider<MockMvc> mvcProvider;\n\n\t\tprivate MockMvc mvc;\n\n\t\tprivate Consumer<RecordedRequest> assertion = (rr) -> { };\n\n\t\tMockMvcDispatcher(ObjectProvider<MockMvc> mvc) {\n\t\t\tthis.mvcProvider = mvc;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockResponse dispatch(RecordedRequest request) throws InterruptedException {\n\t\t\tthis.assertion.accept(request);\n\t\t\tthis.mvc = this.mvcProvider.getObject();\n\t\t\tString method = request.getMethod();\n\t\t\tString path = request.getPath();\n\t\t\tString csrf = request.getHeader(\"X-CSRF-TOKEN\");\n\t\t\tMockHttpSession session = session(request);\n\t\t\tMockHttpServletRequestBuilder builder;\n\t\t\tif (\"GET\".equals(method)) {\n\t\t\t\tbuilder = get(path);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbuilder = post(path).content(request.getBody().readUtf8());\n\t\t\t\tif (csrf != null) {\n\t\t\t\t\tbuilder.header(\"X-CSRF-TOKEN\", csrf);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tbuilder.with(csrf());\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (Map.Entry<String, List<String>> header : request.getHeaders().toMultimap().entrySet()) {\n\t\t\t\tbuilder.header(header.getKey(), header.getValue().iterator().next());\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tMockHttpServletResponse mvcResponse = this.mvc.perform(builder.session(session)).andReturn().getResponse();\n\t\t\t\treturn toMockResponse(mvcResponse);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tMockResponse response = new MockResponse();\n\t\t\t\tresponse.setResponseCode(500);\n\t\t\t\treturn response;\n\t\t\t}\n\t\t}\n\n\t\tvoid registerSession(MockHttpSession session) {\n\t\t\tthis.session.put(session.getId(), session);\n\t\t}\n\n\t\tvoid setAssertion(Consumer<RecordedRequest> assertion) {\n\t\t\tthis.assertion = assertion;\n\t\t}\n\n\t\tprivate MockHttpSession session(RecordedRequest request) {\n\t\t\tString cookieHeaderValue = request.getHeader(\"Cookie\");\n\t\t\tif (cookieHeaderValue == null) {\n\t\t\t\treturn new MockHttpSession();\n\t\t\t}\n\t\t\tString[] cookies = cookieHeaderValue.split(\";\");\n\t\t\tfor (String cookie : cookies) {\n\t\t\t\tString[] parts = cookie.split(\"=\");\n\t\t\t\tif (\"JSESSIONID\".equals(parts[0])) {\n\t\t\t\t\treturn this.session.computeIfAbsent(parts[1],\n\t\t\t\t\t\t\t(k) -> new MockHttpSession(new MockServletContext(), parts[1]));\n\t\t\t\t}\n\t\t\t\tif (\"SESSION\".equals(parts[0])) {\n\t\t\t\t\treturn this.session.computeIfAbsent(parts[1],\n\t\t\t\t\t\t\t(k) -> new MockHttpSession(new MockServletContext(), parts[1]));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn new MockHttpSession();\n\t\t}\n\n\t\tprivate MockResponse toMockResponse(MockHttpServletResponse mvcResponse) {\n\t\t\tMockResponse response = new MockResponse();\n\t\t\tresponse.setResponseCode(mvcResponse.getStatus());\n\t\t\tfor (String name : mvcResponse.getHeaderNames()) {\n\t\t\t\tresponse.addHeader(name, mvcResponse.getHeaderValue(name));\n\t\t\t}\n\t\t\tresponse.setBody(getContentAsString(mvcResponse));\n\t\t\treturn response;\n\t\t}\n\n\t\tprivate String getContentAsString(MockHttpServletResponse response) {\n\t\t\ttry {\n\t\t\t\treturn response.getContentAsString();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcUserRefreshedEventListenerConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests for {@link OidcUserRefreshedEventListener} with {@link OAuth2LoginConfigurer}.\n *\n * @author Steve Riesenberg\n */\npublic class OidcUserRefreshedEventListenerConfigurationTests {\n\n\t// @formatter:off\n\tprivate static final ClientRegistration GOOGLE_CLIENT_REGISTRATION = CommonOAuth2Provider.GOOGLE\n\t\t\t.getBuilder(\"google\")\n\t\t\t.clientId(\"clientId\")\n\t\t\t.clientSecret(\"clientSecret\")\n\t\t\t.build();\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final ClientRegistration GITHUB_CLIENT_REGISTRATION = CommonOAuth2Provider.GITHUB\n\t\t\t.getBuilder(\"github\")\n\t\t\t.clientId(\"clientId\")\n\t\t\t.clientSecret(\"clientSecret\")\n\t\t\t.build();\n\t// @formatter:on\n\n\tprivate static final String SUBJECT = \"surfer-dude\";\n\n\tprivate static final String ACCESS_TOKEN_VALUE = \"hang-ten\";\n\n\tprivate static final String REFRESH_TOKEN_VALUE = \"surfs-up\";\n\n\tprivate static final String ID_TOKEN_VALUE = \"beach-break\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate SecurityContextRepository securityContextRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Autowired\n\tprivate OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient;\n\n\t@Autowired\n\tprivate JwtDecoder jwtDecoder;\n\n\t@Autowired\n\tprivate OidcUserService oidcUserService;\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.request = get(\"/\").build();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(this.request, this.response));\n\t}\n\n\t@AfterEach\n\tpublic void cleanUp() {\n\t\tSecurityContextHolder.clearContext();\n\t\tRequestContextHolder.resetRequestAttributes();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAccessTokenResponseMissingOpenidScopeThenOidcUserNotRefreshed() {\n\t\tthis.spring.register(OAuth2LoginWithOAuth2ClientConfig.class).autowire();\n\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = createAccessTokenResponse();\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(authorizedClient);\n\t\tgiven(this.refreshTokenAccessTokenResponseClient.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tOAuth2AuthenticationToken authentication = createAuthenticationToken(GOOGLE_CLIENT_REGISTRATION,\n\t\t\t\tcreateOidcUser());\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(GOOGLE_CLIENT_REGISTRATION.getRegistrationId())\n\t\t\t.principal(authentication)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(refreshedAuthorizedClient).isNotNull();\n\t\tverifyNoInteractions(this.securityContextRepository, this.jwtDecoder, this.oidcUserService);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAccessTokenResponseMissingIdTokenThenOidcUserNotRefreshed() {\n\t\tthis.spring.register(OAuth2LoginWithOAuth2ClientConfig.class).autowire();\n\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.oidcAccessTokenResponse()\n\t\t\t.build();\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(authorizedClient);\n\t\tgiven(this.refreshTokenAccessTokenResponseClient.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tOAuth2AuthenticationToken authentication = createAuthenticationToken(GOOGLE_CLIENT_REGISTRATION,\n\t\t\t\tcreateOidcUser());\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(GOOGLE_CLIENT_REGISTRATION.getRegistrationId())\n\t\t\t.principal(authentication)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(refreshedAuthorizedClient).isNotNull();\n\t\tverifyNoInteractions(this.securityContextRepository, this.jwtDecoder, this.oidcUserService);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthenticationIsNotOAuth2ThenOidcUserNotRefreshed() {\n\t\tthis.spring.register(OAuth2LoginWithOAuth2ClientConfig.class).autowire();\n\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = createAccessTokenResponse(OidcScopes.OPENID);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(authorizedClient);\n\t\tgiven(this.refreshTokenAccessTokenResponseClient.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(SUBJECT, null);\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(GOOGLE_CLIENT_REGISTRATION.getRegistrationId())\n\t\t\t.principal(authentication)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(refreshedAuthorizedClient).isNotNull();\n\t\tverifyNoInteractions(this.securityContextRepository, this.jwtDecoder, this.oidcUserService);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthenticationIsCustomThenOidcUserNotRefreshed() {\n\t\tthis.spring.register(OAuth2LoginWithOAuth2ClientConfig.class).autowire();\n\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = createAccessTokenResponse(OidcScopes.OPENID);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(authorizedClient);\n\t\tgiven(this.refreshTokenAccessTokenResponseClient.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tOidcUser oidcUser = createOidcUser();\n\t\tOAuth2AuthenticationToken authentication = new CustomOAuth2AuthenticationToken(oidcUser,\n\t\t\t\toidcUser.getAuthorities(), GOOGLE_CLIENT_REGISTRATION.getRegistrationId());\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(GOOGLE_CLIENT_REGISTRATION.getRegistrationId())\n\t\t\t.principal(authentication)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(refreshedAuthorizedClient).isNotNull();\n\t\tverifyNoInteractions(this.securityContextRepository, this.jwtDecoder, this.oidcUserService);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenPrincipalIsOAuth2UserThenOidcUserNotRefreshed() {\n\t\tthis.spring.register(OAuth2LoginWithOAuth2ClientConfig.class).autowire();\n\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = createAccessTokenResponse(OidcScopes.OPENID);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(authorizedClient);\n\t\tgiven(this.refreshTokenAccessTokenResponseClient.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tMap<String, Object> attributes = Map.of(StandardClaimNames.SUB, SUBJECT);\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"OAUTH2_USER\"), attributes,\n\t\t\t\tStandardClaimNames.SUB);\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(oauth2User,\n\t\t\t\toauth2User.getAuthorities(), GOOGLE_CLIENT_REGISTRATION.getRegistrationId());\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(GOOGLE_CLIENT_REGISTRATION.getRegistrationId())\n\t\t\t.principal(authentication)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(refreshedAuthorizedClient).isNotNull();\n\t\tverifyNoInteractions(this.securityContextRepository, this.jwtDecoder, this.oidcUserService);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthenticationClientRegistrationIdDoesNotMatchThenOidcUserNotRefreshed() {\n\t\tthis.spring.register(OAuth2LoginWithOAuth2ClientConfig.class).autowire();\n\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = createAccessTokenResponse(OidcScopes.OPENID);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(authorizedClient);\n\t\tgiven(this.refreshTokenAccessTokenResponseClient.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tOAuth2AuthenticationToken authentication = createAuthenticationToken(GITHUB_CLIENT_REGISTRATION,\n\t\t\t\tcreateOidcUser());\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(GOOGLE_CLIENT_REGISTRATION.getRegistrationId())\n\t\t\t.principal(authentication)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(refreshedAuthorizedClient).isNotNull();\n\t\tverifyNoInteractions(this.securityContextRepository, this.jwtDecoder, this.oidcUserService);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAccessTokenResponseIncludesIdTokenThenOidcUserRefreshed() {\n\t\tthis.spring.register(OAuth2LoginWithOAuth2ClientConfig.class).autowire();\n\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = createAccessTokenResponse(OidcScopes.OPENID);\n\t\tJwt jwt = createJwt().build();\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(authorizedClient);\n\t\tgiven(this.refreshTokenAccessTokenResponseClient.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\t\tgiven(this.oidcUserService.loadUser(any(OidcUserRequest.class))).willReturn(createOidcUser());\n\n\t\tOAuth2AuthenticationToken authentication = createAuthenticationToken(GOOGLE_CLIENT_REGISTRATION,\n\t\t\t\tcreateOidcUser());\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(GOOGLE_CLIENT_REGISTRATION.getRegistrationId())\n\t\t\t.principal(authentication)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(refreshedAuthorizedClient).isNotNull();\n\t\tassertThat(refreshedAuthorizedClient).isNotSameAs(authorizedClient);\n\t\tassertThat(refreshedAuthorizedClient.getClientRegistration()).isEqualTo(GOOGLE_CLIENT_REGISTRATION);\n\t\tassertThat(refreshedAuthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(refreshedAuthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\n\t\tArgumentCaptor<OAuth2RefreshTokenGrantRequest> refreshTokenGrantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2RefreshTokenGrantRequest.class);\n\t\tArgumentCaptor<OidcUserRequest> userRequestCaptor = ArgumentCaptor.forClass(OidcUserRequest.class);\n\t\tArgumentCaptor<SecurityContext> securityContextCaptor = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(this.authorizedClientRepository).loadAuthorizedClient(GOOGLE_CLIENT_REGISTRATION.getRegistrationId(),\n\t\t\t\tauthentication, this.request);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(refreshedAuthorizedClient, authentication,\n\t\t\t\tthis.request, this.response);\n\t\tverify(this.refreshTokenAccessTokenResponseClient).getTokenResponse(refreshTokenGrantRequestCaptor.capture());\n\t\tverify(this.jwtDecoder).decode(jwt.getTokenValue());\n\t\tverify(this.oidcUserService).loadUser(userRequestCaptor.capture());\n\t\tverify(this.securityContextRepository).saveContext(securityContextCaptor.capture(), eq(this.request),\n\t\t\t\teq(this.response));\n\t\tverifyNoMoreInteractions(this.authorizedClientRepository, this.jwtDecoder, this.oidcUserService,\n\t\t\t\tthis.securityContextRepository);\n\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = refreshTokenGrantRequestCaptor.getValue();\n\t\tassertThat(refreshTokenGrantRequest.getClientRegistration())\n\t\t\t.isEqualTo(authorizedClient.getClientRegistration());\n\t\tassertThat(refreshTokenGrantRequest.getRefreshToken()).isEqualTo(authorizedClient.getRefreshToken());\n\t\tassertThat(refreshTokenGrantRequest.getAccessToken()).isEqualTo(authorizedClient.getAccessToken());\n\n\t\tOidcUserRequest userRequest = userRequestCaptor.getValue();\n\t\tassertThat(userRequest.getClientRegistration()).isEqualTo(GOOGLE_CLIENT_REGISTRATION);\n\t\tassertThat(userRequest.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(userRequest.getIdToken().getTokenValue()).isEqualTo(jwt.getTokenValue());\n\n\t\tSecurityContext refreshedSecurityContext = securityContextCaptor.getValue();\n\t\tassertThat(refreshedSecurityContext).isNotNull();\n\t\tassertThat(refreshedSecurityContext).isNotSameAs(securityContext);\n\t\tassertThat(refreshedSecurityContext).isSameAs(SecurityContextHolder.getContext());\n\t\tassertThat(refreshedSecurityContext.getAuthentication()).isInstanceOf(OAuth2AuthenticationToken.class);\n\t\tassertThat(refreshedSecurityContext.getAuthentication()).isNotSameAs(authentication);\n\t\tassertThat(refreshedSecurityContext.getAuthentication().getPrincipal()).isInstanceOf(OidcUser.class);\n\t\tassertThat(refreshedSecurityContext.getAuthentication().getPrincipal())\n\t\t\t.isNotSameAs(authentication.getPrincipal());\n\t}\n\n\tprivate OAuth2AuthorizedClient createAuthorizedClient() {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(30, ChronoUnit.SECONDS);\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, ACCESS_TOKEN_VALUE,\n\t\t\t\tissuedAt, expiresAt, Set.of(OidcScopes.OPENID));\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(REFRESH_TOKEN_VALUE, issuedAt);\n\n\t\treturn new OAuth2AuthorizedClient(GOOGLE_CLIENT_REGISTRATION, SUBJECT, accessToken, refreshToken);\n\t}\n\n\tprivate OAuth2AccessTokenResponse createAccessTokenResponse(String... scope) {\n\t\tSet<String> scopes = Set.of(scope);\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tif (scopes.contains(OidcScopes.OPENID)) {\n\t\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, ID_TOKEN_VALUE);\n\t\t}\n\n\t\treturn OAuth2AccessTokenResponse.withToken(ACCESS_TOKEN_VALUE)\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.scopes(scopes)\n\t\t\t.refreshToken(REFRESH_TOKEN_VALUE)\n\t\t\t.expiresIn(60L)\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t}\n\n\tprivate static Jwt.Builder createJwt() {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.MINUTES);\n\t\treturn TestJwts.jwt()\n\t\t\t.issuer(\"https://surf.school\")\n\t\t\t.subject(SUBJECT)\n\t\t\t.tokenValue(ID_TOKEN_VALUE)\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt)\n\t\t\t.audience(List.of(\"audience1\", \"audience2\"));\n\t}\n\n\tprivate static OidcUser createOidcUser() {\n\t\tInstant issuedAt = Instant.now().minus(30, ChronoUnit.SECONDS);\n\t\tInstant expiresAt = issuedAt.plus(5, ChronoUnit.MINUTES);\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://surf.school\");\n\t\tclaims.put(IdTokenClaimNames.SUB, SUBJECT);\n\t\tclaims.put(IdTokenClaimNames.IAT, issuedAt);\n\t\tclaims.put(IdTokenClaimNames.EXP, expiresAt);\n\t\tclaims.put(IdTokenClaimNames.AUD, List.of(\"audience1\", \"audience2\"));\n\t\tclaims.put(IdTokenClaimNames.AUTH_TIME, issuedAt);\n\t\tclaims.put(IdTokenClaimNames.NONCE, \"nonce\");\n\t\tOidcIdToken idToken = new OidcIdToken(ID_TOKEN_VALUE, issuedAt, expiresAt, claims);\n\n\t\treturn new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"OIDC_USER\"), idToken);\n\t}\n\n\tprivate OAuth2AuthenticationToken createAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOidcUser oidcUser) {\n\t\treturn new OAuth2AuthenticationToken(oidcUser, oidcUser.getAuthorities(),\n\t\t\t\tclientRegistration.getRegistrationId());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginWithOAuth2ClientConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.securityContext((securityContext) -> securityContext\n\t\t\t\t\t.securityContextRepository(this.securityContextRepository())\n\t\t\t\t)\n\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t.oauth2Client(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityContextRepository securityContextRepository() {\n\t\t\treturn mock(SecurityContextRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn mock(ClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {\n\t\t\treturn mock(OAuth2AccessTokenResponseClient.class);\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder() {\n\t\t\treturn mock(JwtDecoder.class);\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {\n\t\t\treturn (clientRegistration) -> jwtDecoder();\n\t\t}\n\n\t\t@Bean\n\t\tOidcUserService oidcUserService() {\n\t\t\treturn mock(OidcUserService.class);\n\t\t}\n\n\t}\n\n\tprivate static final class CustomOAuth2AuthenticationToken extends OAuth2AuthenticationToken {\n\n\t\tCustomOAuth2AuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities,\n\t\t\t\tString authorizedClientRegistrationId) {\n\t\t\tsuper(principal, authorities, authorizedClientRegistrationId);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcUserRefreshedEventListenerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.client;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.oidc.authentication.event.OidcUserRefreshedEvent;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests for {@link OidcUserRefreshedEventListener}.\n *\n * @author Steve Riesenberg\n */\npublic class OidcUserRefreshedEventListenerTests {\n\n\tprivate OidcUserRefreshedEventListener eventListener;\n\n\tprivate SecurityContextRepository securityContextRepository;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.securityContextRepository = mock(SecurityContextRepository.class);\n\t\tthis.eventListener = new OidcUserRefreshedEventListener();\n\t\tthis.eventListener.setSecurityContextRepository(this.securityContextRepository);\n\n\t\tthis.request = get(\"/\").build();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@AfterEach\n\tpublic void cleanUp() {\n\t\tSecurityContextHolder.clearContext();\n\t\tRequestContextHolder.resetRequestAttributes();\n\t}\n\n\t@Test\n\tpublic void setSecurityContextHolderStrategyWhenNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.eventListener.setSecurityContextHolderStrategy(null))\n\t\t\t.withMessage(\"securityContextHolderStrategy cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setSecurityContextRepositoryWhenNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.eventListener.setSecurityContextRepository(null))\n\t\t\t.withMessage(\"securityContextRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenRequestAttributesSetThenSecurityContextSaved() {\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(this.request, this.response));\n\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.oidcAccessTokenResponse()\n\t\t\t.build();\n\t\tOidcUser oldOidcUser = TestOidcUsers.create();\n\t\tOidcUser newOidcUser = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(newOidcUser,\n\t\t\t\tnewOidcUser.getAuthorities(), \"test\");\n\t\tOidcUserRefreshedEvent event = new OidcUserRefreshedEvent(accessTokenResponse, oldOidcUser, newOidcUser,\n\t\t\t\tauthentication);\n\t\tthis.eventListener.onApplicationEvent(event);\n\n\t\tArgumentCaptor<SecurityContext> securityContextCaptor = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(this.securityContextRepository).saveContext(securityContextCaptor.capture(), eq(this.request),\n\t\t\t\teq(this.response));\n\t\tverifyNoMoreInteractions(this.securityContextRepository);\n\n\t\tSecurityContext securityContext = securityContextCaptor.getValue();\n\t\tassertThat(securityContext).isNotNull();\n\t\tassertThat(securityContext).isSameAs(SecurityContextHolder.getContext());\n\t\tassertThat(securityContext.getAuthentication()).isSameAs(authentication);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenRequestAttributesNotSetThenSecurityContextNotSaved() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.oidcAccessTokenResponse()\n\t\t\t.build();\n\t\tOidcUser oldOidcUser = TestOidcUsers.create();\n\t\tOidcUser newOidcUser = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(newOidcUser,\n\t\t\t\tnewOidcUser.getAuthorities(), \"test\");\n\t\tOidcUserRefreshedEvent event = new OidcUserRefreshedEvent(accessTokenResponse, oldOidcUser, newOidcUser,\n\t\t\t\tauthentication);\n\t\tOidcUserRefreshedEventListener eventListener = new OidcUserRefreshedEventListener();\n\t\teventListener.setSecurityContextRepository(this.securityContextRepository);\n\t\teventListener.onApplicationEvent(event);\n\t\tverifyNoInteractions(this.securityContextRepository);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.getContext();\n\t\tassertThat(securityContext).isNotNull();\n\t\tassertThat(securityContext.getAuthentication()).isSameAs(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/AuthorizationServerContextFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link AuthorizationServerContextFilter}.\n *\n * @author Joe Grandja\n */\nclass AuthorizationServerContextFilterTests {\n\n\tprivate static final String SCHEME = \"https\";\n\n\tprivate static final String HOST = \"example.com\";\n\n\tprivate static final int PORT = 8443;\n\n\tprivate static final String DEFAULT_ISSUER = SCHEME + \"://\" + HOST + \":\" + PORT;\n\n\tprivate AuthorizationServerContextFilter filter;\n\n\t@Test\n\tvoid doFilterWhenDefaultEndpointsThenIssuerResolved() throws Exception {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\t\tthis.filter = new AuthorizationServerContextFilter(authorizationServerSettings);\n\n\t\tString issuerPath = \"/issuer1\";\n\t\tString issuerWithPath = DEFAULT_ISSUER.concat(issuerPath);\n\t\tSet<String> endpointUris = getEndpointUris(authorizationServerSettings);\n\n\t\tfor (String endpointUri : endpointUris) {\n\t\t\tassertResolvedIssuer(issuerPath.concat(endpointUri), issuerWithPath);\n\t\t}\n\t}\n\n\t@Test\n\tvoid doFilterWhenCustomEndpointsThenIssuerResolved() throws Exception {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.authorizationEndpoint(\"/oauth2/v1/authorize\")\n\t\t\t.deviceAuthorizationEndpoint(\"/oauth2/v1/device_authorization\")\n\t\t\t.deviceVerificationEndpoint(\"/oauth2/v1/device_verification\")\n\t\t\t.tokenEndpoint(\"/oauth2/v1/token\")\n\t\t\t.jwkSetEndpoint(\"/oauth2/v1/jwks\")\n\t\t\t.tokenRevocationEndpoint(\"/oauth2/v1/revoke\")\n\t\t\t.tokenIntrospectionEndpoint(\"/oauth2/v1/introspect\")\n\t\t\t.oidcClientRegistrationEndpoint(\"/connect/v1/register\")\n\t\t\t.oidcUserInfoEndpoint(\"/v1/userinfo\")\n\t\t\t.oidcLogoutEndpoint(\"/connect/v1/logout\")\n\t\t\t.build();\n\t\tthis.filter = new AuthorizationServerContextFilter(authorizationServerSettings);\n\n\t\tString issuerPath = \"/issuer2\";\n\t\tString issuerWithPath = DEFAULT_ISSUER.concat(issuerPath);\n\t\tSet<String> endpointUris = getEndpointUris(authorizationServerSettings);\n\n\t\tfor (String endpointUri : endpointUris) {\n\t\t\tassertResolvedIssuer(issuerPath.concat(endpointUri), issuerWithPath);\n\t\t}\n\t}\n\n\t@Test\n\tvoid doFilterWhenIssuerHasMultiplePathsThenIssuerResolved() throws Exception {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\t\tthis.filter = new AuthorizationServerContextFilter(authorizationServerSettings);\n\n\t\tString issuerPath = \"/path1/path2/issuer3\";\n\t\tString issuerWithPath = DEFAULT_ISSUER.concat(issuerPath);\n\t\tSet<String> endpointUris = getEndpointUris(authorizationServerSettings);\n\n\t\tfor (String endpointUri : endpointUris) {\n\t\t\tassertResolvedIssuer(issuerPath.concat(endpointUri), issuerWithPath);\n\t\t}\n\t}\n\n\tprivate void assertResolvedIssuer(String requestUri, String expectedIssuer) throws Exception {\n\t\tMockHttpServletRequest request = createRequest(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\n\t\tAtomicReference<String> resolvedIssuer = new AtomicReference<>();\n\t\tFilterChain filterChain = (req, resp) -> resolvedIssuer\n\t\t\t.set(AuthorizationServerContextHolder.getContext().getIssuer());\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tassertThat(resolvedIssuer.get()).isEqualTo(expectedIssuer);\n\t}\n\n\tprivate static Set<String> getEndpointUris(AuthorizationServerSettings authorizationServerSettings) {\n\t\tSet<String> endpointUris = new HashSet<>();\n\t\tendpointUris.add(\"/.well-known/oauth-authorization-server\");\n\t\tendpointUris.add(\"/.well-known/openid-configuration\");\n\t\tfor (Map.Entry<String, Object> setting : authorizationServerSettings.getSettings().entrySet()) {\n\t\t\tif (setting.getKey().endsWith(\"-endpoint\")) {\n\t\t\t\tendpointUris.add((String) setting.getValue());\n\t\t\t}\n\t\t}\n\t\treturn endpointUris;\n\t}\n\n\tprivate static MockHttpServletRequest createRequest(String requestUri) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(requestUri);\n\t\trequest.setScheme(SCHEME);\n\t\trequest.setServerName(HOST);\n\t\trequest.setServerPort(PORT);\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/DefaultOAuth2TokenCustomizersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.util.TestX509Certificates;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link DefaultOAuth2TokenCustomizers}.\n *\n * @author Steve Riesenberg\n * @author Joe Grandja\n */\nclass DefaultOAuth2TokenCustomizersTests {\n\n\tprivate static final String ISSUER_1 = \"issuer-1\";\n\n\tprivate static final String ISSUER_2 = \"issuer-2\";\n\n\tprivate JwsHeader.Builder jwsHeaderBuilder;\n\n\tprivate JwtClaimsSet.Builder jwtClaimsBuilder;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.jwsHeaderBuilder = JwsHeader.with(SignatureAlgorithm.RS256);\n\t\tthis.jwtClaimsBuilder = JwtClaimsSet.builder().issuer(ISSUER_1);\n\t}\n\n\t@Test\n\tvoid customizeWhenTokenTypeIsRefreshTokenThenNoClaimsAdded() {\n\t\t// @formatter:off\n\t\tJwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)\n\t\t\t\t.tokenType(OAuth2TokenType.REFRESH_TOKEN)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);\n\t\tJwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();\n\t\tassertThat(jwtClaimsSet.getClaims()).containsOnly(entry(JwtClaimNames.ISS, ISSUER_1));\n\t}\n\n\t@Test\n\tvoid customizeWhenAuthorizationGrantIsNullThenNoClaimsAdded() {\n\t\t// @formatter:off\n\t\tJwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);\n\t\tJwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();\n\t\tassertThat(jwtClaimsSet.getClaims()).containsOnly(entry(JwtClaimNames.ISS, ISSUER_1));\n\t}\n\n\t@Test\n\tvoid customizeWhenTokenExchangeGrantAndResourcesThenNoClaimsAdded() {\n\t\tOAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(\n\t\t\t\tOAuth2TokenExchangeAuthenticationToken.class);\n\t\tgiven(tokenExchangeAuthentication.getResources()).willReturn(Set.of(\"resource1\", \"resource2\"));\n\t\t// @formatter:off\n\t\tJwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.authorizationGrant(tokenExchangeAuthentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);\n\t\tJwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();\n\t\t// We do not populate claims (e.g. `aud`) based on the resource parameter\n\t\tassertThat(jwtClaimsSet.getClaims()).containsOnly(entry(JwtClaimNames.ISS, ISSUER_1));\n\t}\n\n\t@Test\n\tvoid customizeWhenTokenExchangeGrantAndAudiencesThenNoClaimsAdded() {\n\t\tOAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(\n\t\t\t\tOAuth2TokenExchangeAuthenticationToken.class);\n\t\tgiven(tokenExchangeAuthentication.getAudiences()).willReturn(Set.of(\"audience1\", \"audience2\"));\n\t\t// @formatter:off\n\t\tJwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.authorizationGrant(tokenExchangeAuthentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);\n\t\tJwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();\n\t\t// NOTE: We do not populate claims (e.g. `aud`) based on the audience parameter\n\t\tassertThat(jwtClaimsSet.getClaims()).containsOnly(entry(JwtClaimNames.ISS, ISSUER_1));\n\t}\n\n\t@Test\n\tvoid customizeWhenTokenExchangeGrantAndDelegationThenActClaimAdded() {\n\t\tOAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = mock(\n\t\t\t\tOAuth2TokenExchangeAuthenticationToken.class);\n\t\tgiven(tokenExchangeAuthentication.getAudiences()).willReturn(Collections.emptySet());\n\n\t\tAuthentication subject = new TestingAuthenticationToken(\"subject\", null);\n\t\tOAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(JwtClaimNames.ISS, ISSUER_1, JwtClaimNames.SUB, \"actor1\"));\n\t\tOAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(JwtClaimNames.ISS, ISSUER_2, JwtClaimNames.SUB, \"actor2\"));\n\t\tOAuth2TokenExchangeCompositeAuthenticationToken principal = new OAuth2TokenExchangeCompositeAuthenticationToken(\n\t\t\t\tsubject, List.of(actor1, actor2));\n\n\t\t// @formatter:off\n\t\tJwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.principal(principal)\n\t\t\t\t.authorizationGrant(tokenExchangeAuthentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);\n\t\tJwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();\n\t\tassertThat(jwtClaimsSet.getClaims()).isNotEmpty();\n\t\tassertThat(jwtClaimsSet.getClaims()).hasSize(2);\n\t\tassertThat(jwtClaimsSet.getClaims().get(\"act\")).isNotNull();\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, Object> actClaim1 = (Map<String, Object>) jwtClaimsSet.getClaims().get(\"act\");\n\t\tassertThat(actClaim1.get(JwtClaimNames.ISS)).isEqualTo(ISSUER_1);\n\t\tassertThat(actClaim1.get(JwtClaimNames.SUB)).isEqualTo(\"actor1\");\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, Object> actClaim2 = (Map<String, Object>) actClaim1.get(\"act\");\n\t\tassertThat(actClaim2.get(JwtClaimNames.ISS)).isEqualTo(ISSUER_2);\n\t\tassertThat(actClaim2.get(JwtClaimNames.SUB)).isEqualTo(\"actor2\");\n\t}\n\n\t@Test\n\tvoid customizeWhenPKIX509ClientCertificateAndCertificateBoundAccessTokensThenX5tClaimAdded() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.x509CertificateSubjectDN(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0].getSubjectX500Principal().getName())\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.tokenSettings(\n\t\t\t\t\t\tTokenSettings.builder()\n\t\t\t\t\t\t\t\t.x509CertificateBoundAccessTokens(true)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.TLS_CLIENT_AUTH, TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);\n\t\tOAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\t\t// @formatter:off\n\t\tJwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.authorizationGrant(clientCredentialsAuthentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);\n\t\tJwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();\n\t\tassertThat(jwtClaimsSet.getClaims()).isNotEmpty();\n\t\tassertThat(jwtClaimsSet.getClaims()).hasSize(2);\n\t\tMap<String, Object> cnfClaim = jwtClaimsSet.getClaim(\"cnf\");\n\t\tassertThat(cnfClaim).isNotEmpty();\n\t\tassertThat(cnfClaim.get(\"x5t#S256\")).isNotNull();\n\t}\n\n\t@Test\n\tvoid customizeWhenSelfSignedX509ClientCertificateAndCertificateBoundAccessTokensThenX5tClaimAdded() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.jwkSetUrl(\"https://client.example.com/jwks\")\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.tokenSettings(\n\t\t\t\t\t\tTokenSettings.builder()\n\t\t\t\t\t\t\t\t.x509CertificateBoundAccessTokens(true)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);\n\t\tOAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\t\t// @formatter:off\n\t\tJwtEncodingContext tokenContext = JwtEncodingContext.with(this.jwsHeaderBuilder, this.jwtClaimsBuilder)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.authorizationGrant(clientCredentialsAuthentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultOAuth2TokenCustomizers.jwtCustomizer().customize(tokenContext);\n\t\tJwtClaimsSet jwtClaimsSet = this.jwtClaimsBuilder.build();\n\t\tassertThat(jwtClaimsSet.getClaims()).isNotEmpty();\n\t\tassertThat(jwtClaimsSet.getClaims()).hasSize(2);\n\t\tMap<String, Object> cnfClaim = jwtClaimsSet.getClaim(\"cnf\");\n\t\tassertThat(cnfClaim).isNotEmpty();\n\t\tassertThat(cnfClaim.get(\"x5t#S256\")).isNotNull();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/JwkSetTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the JWK Set endpoint.\n *\n * @author Florian Berthe\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class JwkSetTests {\n\n\tprivate static final String DEFAULT_JWK_SET_ENDPOINT_URI = \"/oauth2/jwks\";\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenJwkSetThenReturnKeys() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tassertJwkSetRequestThenReturnKeys(DEFAULT_JWK_SET_ENDPOINT_URI);\n\t}\n\n\t@Test\n\tpublic void requestWhenJwkSetCustomEndpointThenReturnKeys() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomEndpoints.class).autowire();\n\n\t\tassertJwkSetRequestThenReturnKeys(this.authorizationServerSettings.getJwkSetEndpoint());\n\t}\n\n\t@Test\n\tpublic void requestWhenJwkSetRequestIncludesIssuerPathThenReturnKeys() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomEndpoints.class).autowire();\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\t\tassertJwkSetRequestThenReturnKeys(issuer.concat(this.authorizationServerSettings.getJwkSetEndpoint()));\n\t}\n\n\tprivate void assertJwkSetRequestThenReturnKeys(String jwkSetEndpointUri) throws Exception {\n\t\tthis.mvc.perform(get(jwkSetEndpointUri))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.keys\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.keys\").isArray());\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\treturn new JdbcRegisteredClientRepository(jdbcOperations);\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfigurationCustomEndpoints extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder()\n\t\t\t\t.jwkSetEndpoint(\"/test/jwks\")\n\t\t\t\t.multipleIssuersAllowed(true)\n\t\t\t\t.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationCodeGrantTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.security.Principal;\nimport java.text.MessageFormat;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport com.jayway.jsonpath.JsonPath;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.assertj.core.matcher.AssertionMatcher;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.lang.Nullable;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.JwtGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationConsentAuthenticationConverter;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the OAuth 2.0 Authorization Code Grant.\n *\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n * @author Dmitriy Dubson\n * @author Steve Riesenberg\n * @author Greg Li\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2AuthorizationCodeGrantTests {\n\n\tprivate static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = \"/oauth2/authorize\";\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\t// See RFC 7636: Appendix B. Example for the S256 code_challenge_method\n\t// https://tools.ietf.org/html/rfc7636#appendix-B\n\tprivate static final String S256_CODE_VERIFIER = \"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\n\tprivate static final String S256_CODE_CHALLENGE = \"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\";\n\n\tprivate static final String AUTHORITIES_CLAIM = \"authorities\";\n\n\tprivate static final String STATE_URL_UNENCODED = \"awrD0fCnEcTUPFgmyy2SU89HZNcnAJ60ZW6l39YI0KyVjmIZ+004pwm9j55li7BoydXYysH4enZMF21Q\";\n\n\tprivate static final String STATE_URL_ENCODED = \"awrD0fCnEcTUPFgmyy2SU89HZNcnAJ60ZW6l39YI0KyVjmIZ%2B004pwm9j55li7BoydXYysH4enZMF21Q\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tprivate static NimbusJwtEncoder jwtEncoder;\n\n\tprivate static NimbusJwtEncoder dPoPProofJwtEncoder;\n\n\tprivate static AuthorizationServerSettings authorizationServerSettings;\n\n\tprivate static HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tprivate static AuthenticationConverter authorizationRequestConverter;\n\n\tprivate static Consumer<List<AuthenticationConverter>> authorizationRequestConvertersConsumer;\n\n\tprivate static AuthenticationProvider authorizationRequestAuthenticationProvider;\n\n\tprivate static Consumer<List<AuthenticationProvider>> authorizationRequestAuthenticationProvidersConsumer;\n\n\tprivate static AuthenticationSuccessHandler authorizationResponseHandler;\n\n\tprivate static AuthenticationFailureHandler authorizationErrorResponseHandler;\n\n\tprivate static SecurityContextRepository securityContextRepository;\n\n\tprivate static String consentPage = \"/oauth2/consent\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\t@Autowired\n\tprivate JwtDecoder jwtDecoder;\n\n\t@Autowired(required = false)\n\tprivate OAuth2TokenGenerator<?> tokenGenerator;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tjwtEncoder = new NimbusJwtEncoder(jwkSource);\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tdPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\tauthorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.authorizationEndpoint(\"/test/authorize\")\n\t\t\t.tokenEndpoint(\"/test/token\")\n\t\t\t.build();\n\t\tauthorizationRequestConverter = mock(AuthenticationConverter.class);\n\t\tauthorizationRequestConvertersConsumer = mock(Consumer.class);\n\t\tauthorizationRequestAuthenticationProvider = mock(AuthenticationProvider.class);\n\t\tauthorizationRequestAuthenticationProvidersConsumer = mock(Consumer.class);\n\t\tauthorizationResponseHandler = mock(AuthenticationSuccessHandler.class);\n\t\tauthorizationErrorResponseHandler = mock(AuthenticationFailureHandler.class);\n\t\tsecurityContextRepository = spy(new HttpSessionSecurityContextRepository());\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t}\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\treset(securityContextRepository);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization_consent\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationRequestNotAuthenticatedThenUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tthis.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient)))\n\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void requestWhenRegisteredClientMissingThenBadRequest() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\t\tthis.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient)))\n\t\t\t.andExpect(status().isBadRequest())\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationRequestAuthenticatedThenRedirectToClient() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tassertAuthorizationRequestRedirectsToClient(DEFAULT_AUTHORIZATION_ENDPOINT_URI);\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationRequestCustomEndpointThenRedirectToClient() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomEndpoints.class).autowire();\n\n\t\tassertAuthorizationRequestRedirectsToClient(authorizationServerSettings.getAuthorizationEndpoint());\n\t}\n\n\tprivate void assertAuthorizationRequestRedirectsToClient(String authorizationEndpointUri) throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris((redirectUris) -> {\n\t\t\tredirectUris.clear();\n\t\t\tredirectUris.add(\"https://example.com/callback-1?param=encoded%20parameter%20value\"); // gh-1011\n\t\t}).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(authorizationEndpointUri).queryParams(authorizationRequestParameters).with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString redirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tString code = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tassertThat(redirectedUrl).isEqualTo(redirectUri + \"&code=\" + code + \"&state=\" + STATE_URL_ENCODED);\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isNotNull();\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestValidThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2AccessTokenResponse accessTokenResponse = assertTokenRequestReturnsAccessTokenResponse(registeredClient,\n\t\t\t\tauthorization, DEFAULT_TOKEN_ENDPOINT_URI);\n\n\t\t// Assert user authorities was propagated as claim in JWT\n\t\tJwt jwt = this.jwtDecoder.decode(accessTokenResponse.getAccessToken().getTokenValue());\n\t\tList<String> authoritiesClaim = jwt.getClaim(AUTHORITIES_CLAIM);\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\t\tSet<String> userAuthorities = new HashSet<>();\n\t\tfor (GrantedAuthority authority : principal.getAuthorities()) {\n\t\t\tuserAuthorities.add(authority.getAuthority());\n\t\t}\n\n\t\tassertThat(authoritiesClaim).containsExactlyInAnyOrderElementsOf(userAuthorities);\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestCustomEndpointThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomEndpoints.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tthis.authorizationService.save(authorization);\n\n\t\tassertTokenRequestReturnsAccessTokenResponse(registeredClient, authorization,\n\t\t\t\tauthorizationServerSettings.getTokenEndpoint());\n\t}\n\n\tprivate OAuth2AccessTokenResponse assertTokenRequestReturnsAccessTokenResponse(RegisteredClient registeredClient,\n\t\t\tOAuth2Authorization authorization, String tokenEndpointUri) throws Exception {\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(tokenEndpointUri).params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\tOAuth2Authorization accessTokenAuthorization = this.authorizationService.findById(authorization.getId());\n\t\tassertThat(accessTokenAuthorization).isNotNull();\n\t\tassertThat(accessTokenAuthorization.getAccessToken()).isNotNull();\n\t\tassertThat(accessTokenAuthorization.getRefreshToken()).isNotNull();\n\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCodeToken = accessTokenAuthorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCodeToken).isNotNull();\n\t\tassertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME))\n\t\t\t.isEqualTo(true);\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\treturn accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);\n\t}\n\n\t@Test\n\tpublic void requestWhenPublicClientWithPkceThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tassertThat(redirectedUrl).matches(\"https://example.com\\\\?code=.{15,}&state=\" + STATE_URL_ENCODED);\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorizationCodeAuthorization).isNotNull();\n\t\tassertThat(authorizationCodeAuthorization.getAuthorizationGrantType())\n\t\t\t.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").doesNotExist())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty());\n\n\t\tOAuth2Authorization accessTokenAuthorization = this.authorizationService\n\t\t\t.findById(authorizationCodeAuthorization.getId());\n\t\tassertThat(accessTokenAuthorization).isNotNull();\n\t\tassertThat(accessTokenAuthorization.getAccessToken()).isNotNull();\n\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCodeToken = accessTokenAuthorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCodeToken).isNotNull();\n\t\tassertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME))\n\t\t\t.isEqualTo(true);\n\t}\n\n\t// gh-1430\n\t@Test\n\tpublic void requestWhenPublicClientWithPkceAndCustomRefreshTokenGeneratorThenReturnRefreshToken() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithCustomRefreshTokenGenerator.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tassertThat(redirectedUrl).matches(\"https://example.com\\\\?code=.{15,}&state=\" + STATE_URL_ENCODED);\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorizationCodeAuthorization).isNotNull();\n\t\tassertThat(authorizationCodeAuthorization.getAuthorizationGrantType())\n\t\t\t.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty());\n\n\t\tOAuth2Authorization authorization = this.authorizationService.findById(authorizationCodeAuthorization.getId());\n\t\tassertThat(authorization).isNotNull();\n\t\tassertThat(authorization.getAccessToken()).isNotNull();\n\t\tassertThat(authorization.getRefreshToken()).isNotNull();\n\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCodeToken = authorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCodeToken).isNotNull();\n\t\tassertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME))\n\t\t\t.isEqualTo(true);\n\t}\n\n\t// gh-1680\n\t@Test\n\tpublic void requestWhenPublicClientWithPkceAndEmptyCodeThenBadRequest() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> tokenRequestParameters = new LinkedMultiValueMap<>();\n\t\ttokenRequestParameters.set(OAuth2ParameterNames.GRANT_TYPE,\n\t\t\t\tAuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\ttokenRequestParameters.set(OAuth2ParameterNames.CODE, \"\");\n\t\ttokenRequestParameters.set(OAuth2ParameterNames.REDIRECT_URI,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next());\n\t\ttokenRequestParameters.set(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(tokenRequestParameters)\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))\n\t\t\t.andExpect(status().isBadRequest());\n\t}\n\n\t@Test\n\tpublic void requestWhenConfidentialClientWithPkceAndMissingCodeVerifierThenBadRequest() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tassertThat(redirectedUrl).matches(expectedRedirectUri + \"\\\\?code=.{15,}&state=\" + STATE_URL_ENCODED);\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorizationCodeAuthorization).isNotNull();\n\t\tassertThat(authorizationCodeAuthorization.getAuthorizationGrantType())\n\t\t\t.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\n\t\tMultiValueMap<String, String> tokenRequestParameters = getTokenRequestParameters(registeredClient,\n\t\t\t\tauthorizationCodeAuthorization);\n\t\ttokenRequestParameters.remove(PkceParameterNames.CODE_VERIFIER);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(tokenRequestParameters)\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(status().isBadRequest());\n\t}\n\n\t// gh-1011\n\t@Test\n\tpublic void requestWhenConfidentialClientWithPkceAndMissingCodeChallengeThenErrorResponseEncoded()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tString redirectUri = \"https://example.com/callback-1?param=encoded%20parameter%20value\";\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris((redirectUris) -> {\n\t\t\tredirectUris.clear();\n\t\t\tredirectUris.add(redirectUri);\n\t\t}).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tauthorizationRequestParameters.remove(PkceParameterNames.CODE_CHALLENGE);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString expectedRedirectUri = redirectUri + \"&\" + \"error=invalid_request&\" + \"error_description=\"\n\t\t\t\t+ UriUtils.encode(\"OAuth 2.0 Parameter: code_challenge\", StandardCharsets.UTF_8) + \"&\" + \"error_uri=\"\n\t\t\t\t+ UriUtils.encode(\"https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1\", StandardCharsets.UTF_8)\n\t\t\t\t+ \"&\" + \"state=\" + STATE_URL_ENCODED;\n\t\tassertThat(redirectedUrl).isEqualTo(expectedRedirectUri);\n\t}\n\n\t@Test\n\tpublic void requestWhenConfidentialClientWithPkceAndMissingCodeChallengeButCodeVerifierProvidedThenBadRequest()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.clientSettings(ClientSettings.builder().requireProofKey(false).build())\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tauthorizationRequestParameters.remove(PkceParameterNames.CODE_CHALLENGE);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tassertThat(redirectedUrl).matches(expectedRedirectUri + \"\\\\?code=.{15,}&state=\" + STATE_URL_ENCODED);\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorizationCodeAuthorization).isNotNull();\n\t\tassertThat(authorizationCodeAuthorization.getAuthorizationGrantType())\n\t\t\t.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(status().isBadRequest());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomTokenGeneratorThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithTokenGenerator.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tthis.authorizationService.save(authorization);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(status().isOk());\n\n\t\tverify(this.tokenGenerator, times(2)).generate(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenRequiresConsentThenDisplaysConsentPage() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(\"message.read\");\n\t\t\tscopes.add(\"message.write\");\n\t\t}).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tString consentPage = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\n\t\tassertThat(consentPage).contains(\"Consent required\");\n\t\tassertThat(consentPage).contains(scopeCheckbox(\"message.read\"));\n\t\tassertThat(consentPage).contains(scopeCheckbox(\"message.write\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenConsentRequestThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(\"message.read\");\n\t\t\tscopes.add(\"message.write\");\n\t\t}).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(\"user\")\n\t\t\t.build();\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationRequest updatedAuthorizationRequest = OAuth2AuthorizationRequest.from(authorizationRequest)\n\t\t\t.state(STATE_URL_UNENCODED)\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tauthorization = OAuth2Authorization.from(authorization)\n\t\t\t.attribute(OAuth2AuthorizationRequest.class.getName(), updatedAuthorizationRequest)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"message.read\")\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"message.write\")\n\t\t\t\t.param(OAuth2ParameterNames.STATE, authorization.<String>getAttribute(OAuth2ParameterNames.STATE))\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tassertThat(redirectedUrl)\n\t\t\t.matches(authorizationRequest.getRedirectUri() + \"\\\\?code=.{15,}&state=\" + STATE_URL_ENCODED);\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomConsentPageConfiguredThenRedirect() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomConsentPage.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(\"message.read\");\n\t\t\tscopes.add(\"message.write\");\n\t\t}).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tassertThat(redirectedUrl).matches(\"http://localhost/oauth2/consent\\\\?scope=.+&client_id=.+&state=.+\");\n\n\t\tString locationHeader = URLDecoder.decode(redirectedUrl, StandardCharsets.UTF_8.name());\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build();\n\t\tMultiValueMap<String, String> redirectQueryParams = uriComponents.getQueryParams();\n\n\t\tassertThat(uriComponents.getPath()).isEqualTo(consentPage);\n\t\tassertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.SCOPE)).isEqualTo(\"message.read message.write\");\n\t\tassertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.CLIENT_ID))\n\t\t\t.isEqualTo(registeredClient.getClientId());\n\n\t\tString state = extractParameterFromRedirectUri(redirectedUrl, \"state\");\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE);\n\t\tassertThat(authorization).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomConsentCustomizerConfiguredThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomConsentRequest.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.clientSettings(ClientSettings.builder()\n\t\t\t\t.requireAuthorizationConsent(true)\n\t\t\t\t.setting(\"custom.allowed-authorities\", \"authority-1 authority-2\")\n\t\t\t\t.build())\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationRequest updatedAuthorizationRequest = OAuth2AuthorizationRequest.from(authorizationRequest)\n\t\t\t.state(STATE_URL_UNENCODED)\n\t\t\t.build();\n\t\tauthorization = OAuth2Authorization.from(authorization)\n\t\t\t.attribute(OAuth2AuthorizationRequest.class.getName(), updatedAuthorizationRequest)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.param(\"authority\", \"authority-1 authority-2\")\n\t\t\t\t.param(OAuth2ParameterNames.STATE, authorization.<String>getAttribute(OAuth2ParameterNames.STATE))\n\t\t\t\t.with(user(\"principal\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tassertThat(redirectedUrl)\n\t\t\t.matches(authorizationRequest.getRedirectUri() + \"\\\\?code=.{15,}&state=\" + STATE_URL_ENCODED);\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").value(new AssertionMatcher<String>() {\n\t\t\t\t@Override\n\t\t\t\tpublic void assertion(String accessToken) throws AssertionError {\n\t\t\t\t\tJwt jwt = OAuth2AuthorizationCodeGrantTests.this.jwtDecoder.decode(accessToken);\n\t\t\t\t\tassertThat(jwt.getClaimAsStringList(AUTHORITIES_CLAIM)).containsExactlyInAnyOrder(\"authority-1\",\n\t\t\t\t\t\t\t\"authority-2\");\n\t\t\t\t}\n\t\t\t}))\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").doesNotExist())\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationEndpointCustomizedThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomAuthorizationEndpoint.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principalName\", \"password\");\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\t\"https://provider.com/oauth2/authorize\", registeredClient.getClientId(), principal,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE_URL_UNENCODED, registeredClient.getScopes(),\n\t\t\t\tadditionalParameters);\n\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(\"code\", Instant.now(),\n\t\t\t\tInstant.now().plus(5, ChronoUnit.MINUTES));\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\t\"https://provider.com/oauth2/authorize\", registeredClient.getClientId(), principal, authorizationCode,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE_URL_UNENCODED,\n\t\t\t\tregisteredClient.getScopes());\n\t\tgiven(authorizationRequestConverter.convert(any())).willReturn(authorizationCodeRequestAuthentication);\n\t\tgiven(authorizationRequestAuthenticationProvider\n\t\t\t.supports(eq(OAuth2AuthorizationCodeRequestAuthenticationToken.class))).willReturn(true);\n\t\tgiven(authorizationRequestAuthenticationProvider.authenticate(any()))\n\t\t\t.willReturn(authorizationCodeRequestAuthenticationResult);\n\n\t\tthis.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().isOk());\n\n\t\tverify(authorizationRequestConverter).convert(any());\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authorizationRequestConvertersConsumer).accept(authenticationConvertersCaptor.capture());\n\t\tList<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();\n\t\tassertThat(authenticationConverters).allMatch((converter) -> converter == authorizationRequestConverter\n\t\t\t\t|| converter instanceof OAuth2AuthorizationCodeRequestAuthenticationConverter\n\t\t\t\t|| converter instanceof OAuth2AuthorizationConsentAuthenticationConverter);\n\n\t\tverify(authorizationRequestAuthenticationProvider).authenticate(eq(authorizationCodeRequestAuthentication));\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authorizationRequestAuthenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());\n\t\tList<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();\n\t\tassertThat(authenticationProviders)\n\t\t\t.allMatch((provider) -> provider == authorizationRequestAuthenticationProvider\n\t\t\t\t\t|| provider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider\n\t\t\t\t\t|| provider instanceof OAuth2AuthorizationConsentAuthenticationProvider);\n\n\t\tverify(authorizationResponseHandler).onAuthenticationSuccess(any(), any(),\n\t\t\t\teq(authorizationCodeRequestAuthenticationResult));\n\t}\n\n\t// gh-482\n\t@Test\n\tpublic void requestWhenClientObtainsAccessTokenThenClientAuthenticationNotPersisted() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithSecurityContextRepository.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\n\t\tArgumentCaptor<org.springframework.security.core.context.SecurityContext> securityContextCaptor = ArgumentCaptor\n\t\t\t.forClass(org.springframework.security.core.context.SecurityContext.class);\n\t\tverify(securityContextRepository, times(1)).saveContext(securityContextCaptor.capture(), any(), any());\n\t\tassertThat(securityContextCaptor.getValue().getAuthentication())\n\t\t\t.isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\treset(securityContextRepository);\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(mvcResult.getResponse().getRedirectedUrl(), \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").doesNotExist())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\torg.springframework.security.core.context.SecurityContext securityContext = securityContextRepository\n\t\t\t.loadDeferredContext(mvcResult.getRequest())\n\t\t\t.get();\n\t\tassertThat(securityContext.getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationAndTokenRequestIncludesIssuerPathThenIssuerResolvedWithPath() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI))\n\t\t\t\t.queryParams(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(mvcResult.getResponse().getRedirectedUrl(), \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tthis.mvc\n\t\t\t.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").doesNotExist())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\tArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);\n\t\tverify(this.tokenGenerator).generate(tokenContextCaptor.capture());\n\t\tOAuth2TokenContext tokenContext = tokenContextCaptor.getValue();\n\t\tassertThat(tokenContext.getAuthorizationServerContext().getIssuer()).isEqualTo(issuer);\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestWithDPoPProofThenReturnDPoPBoundAccessToken() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tthis.authorizationService.save(authorization);\n\n\t\tString tokenEndpointUri = \"http://localhost\" + DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tString dPoPProof = generateDPoPProof(tokenEndpointUri);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient))\n\t\t\t\t.header(OAuth2AccessToken.TokenType.DPOP.getValue(), dPoPProof))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").value(OAuth2AccessToken.TokenType.DPOP.getValue()));\n\n\t\tauthorization = this.authorizationService.findById(authorization.getId());\n\t\tassertThat(authorization.getAccessToken().getClaims()).containsKey(\"cnf\");\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, Object> cnfClaims = (Map<String, Object>) authorization.getAccessToken().getClaims().get(\"cnf\");\n\t\tassertThat(cnfClaims).containsKey(\"jkt\");\n\t\tString jwkThumbprintClaim = (String) cnfClaims.get(\"jkt\");\n\t\tassertThat(jwkThumbprintClaim).isEqualTo(TestJwks.DEFAULT_EC_JWK.toPublicJWK().computeThumbprint().toString());\n\t}\n\n\t@Test\n\tpublic void requestWhenPushedAuthorizationRequestThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequests.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(\"/oauth2/par\").params(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(status().isCreated())\n\t\t\t.andExpect(jsonPath(\"$.request_uri\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\tString requestUri = JsonPath.read(mvcResult.getResponse().getContentAsString(), \"$.request_uri\");\n\n\t\tmvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.queryParam(OAuth2ParameterNames.REQUEST_URI, requestUri)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(mvcResult.getResponse().getRedirectedUrl(), \"code\");\n\t\tOAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\tOAuth2Authorization accessTokenAuthorization = this.authorizationService\n\t\t\t.findById(authorizationCodeAuthorization.getId());\n\t\tassertThat(accessTokenAuthorization).isNotNull();\n\t\tassertThat(accessTokenAuthorization.getAccessToken()).isNotNull();\n\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCodeToken = accessTokenAuthorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCodeToken).isNotNull();\n\t\tassertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME))\n\t\t\t.isEqualTo(true);\n\t}\n\n\t// gh-2182\n\t@Test\n\tpublic void requestWhenPushedAuthorizationRequestAndRequiresConsentThenDisplaysConsentPage() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequests.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(\"message.read\");\n\t\t\tscopes.add(\"message.write\");\n\t\t}).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(\"/oauth2/par\").params(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(status().isCreated())\n\t\t\t.andExpect(jsonPath(\"$.request_uri\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\tString requestUri = JsonPath.read(mvcResult.getResponse().getContentAsString(), \"$.request_uri\");\n\n\t\tString consentPage = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.queryParam(OAuth2ParameterNames.REQUEST_URI, requestUri)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andReturn()\n\t\t\t.getResponse()\n\t\t\t.getContentAsString();\n\n\t\tassertThat(consentPage).contains(\"Consent required\");\n\t\tassertThat(consentPage).contains(scopeCheckbox(\"message.read\"));\n\t\tassertThat(consentPage).contains(scopeCheckbox(\"message.write\"));\n\t}\n\n\t// gh-2182\n\t@Test\n\tpublic void requestWhenPushedAuthorizationRequestAndCustomConsentPageConfiguredThenRedirect() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequestsAndCustomConsentPage.class)\n\t\t\t.autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(\"message.read\");\n\t\t\tscopes.add(\"message.write\");\n\t\t}).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(\"/oauth2/par\").params(getAuthorizationRequestParameters(registeredClient))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(status().isCreated())\n\t\t\t.andExpect(jsonPath(\"$.request_uri\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\tString requestUri = JsonPath.read(mvcResult.getResponse().getContentAsString(), \"$.request_uri\");\n\n\t\tmvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.queryParam(OAuth2ParameterNames.REQUEST_URI, requestUri)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tassertThat(redirectedUrl).matches(\"http://localhost/oauth2/consent\\\\?scope=.+&client_id=.+&state=.+\");\n\n\t\tString locationHeader = URLDecoder.decode(redirectedUrl, StandardCharsets.UTF_8.name());\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build();\n\t\tMultiValueMap<String, String> redirectQueryParams = uriComponents.getQueryParams();\n\n\t\tassertThat(uriComponents.getPath()).isEqualTo(consentPage);\n\t\tassertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.SCOPE)).isEqualTo(\"message.read message.write\");\n\t\tassertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.CLIENT_ID))\n\t\t\t.isEqualTo(registeredClient.getClientId());\n\n\t\tString state = extractParameterFromRedirectUri(redirectedUrl, \"state\");\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE);\n\t\tassertThat(authorization).isNotNull();\n\t}\n\n\tprivate static OAuth2Authorization createAuthorization(RegisteredClient registeredClient) {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\treturn TestOAuth2Authorizations.authorization(registeredClient, additionalParameters).build();\n\t}\n\n\tprivate static String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n\tprivate static MultiValueMap<String, String> getAuthorizationRequestParameters(RegisteredClient registeredClient) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2AuthorizationResponseType.CODE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next());\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\t\tparameters.set(OAuth2ParameterNames.STATE, STATE_URL_UNENCODED);\n\t\tparameters.set(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\tparameters.set(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\treturn parameters;\n\t}\n\n\tprivate static MultiValueMap<String, String> getTokenRequestParameters(RegisteredClient registeredClient,\n\t\t\tOAuth2Authorization authorization) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CODE,\n\t\t\t\tauthorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue());\n\t\tparameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next());\n\t\tparameters.set(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER);\n\t\treturn parameters;\n\t}\n\n\tprivate static String getAuthorizationHeader(RegisteredClient registeredClient) throws Exception {\n\t\tString clientId = registeredClient.getClientId();\n\t\tString clientSecret = registeredClient.getClientSecret();\n\t\tclientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8);\n\t\tclientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8);\n\t\tString credentialsString = clientId + \":\" + clientSecret;\n\t\tbyte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));\n\t\treturn \"Basic \" + new String(encodedBytes, StandardCharsets.UTF_8);\n\t}\n\n\tprivate static String scopeCheckbox(String scope) {\n\t\treturn MessageFormat.format(\n\t\t\t\t\"<input class=\\\"form-check-input\\\" type=\\\"checkbox\\\" name=\\\"scope\\\" value=\\\"{0}\\\" id=\\\"{0}\\\">\", scope);\n\t}\n\n\tprivate String extractParameterFromRedirectUri(String redirectUri, String param)\n\t\t\tthrows UnsupportedEncodingException {\n\t\tString locationHeader = URLDecoder.decode(redirectUri, StandardCharsets.UTF_8.name());\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build();\n\t\treturn uriComponents.getQueryParams().getFirst(param);\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationConsentService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tJdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tRegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();\n\t\t\tjdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\t\t\treturn jdbcRegisteredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {\n\t\t\treturn (context) -> {\n\t\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType())\n\t\t\t\t\t\t&& OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {\n\t\t\t\t\tAuthentication principal = context.getPrincipal();\n\t\t\t\t\tSet<String> authorities = new HashSet<>();\n\t\t\t\t\tfor (GrantedAuthority authority : principal.getAuthorities()) {\n\t\t\t\t\t\tauthorities.add(authority.getAuthority());\n\t\t\t\t\t}\n\t\t\t\t\tcontext.getClaims().claim(AUTHORITIES_CLAIM, authorities);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfigurationWithCustomRefreshTokenGenerator\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tJwtEncoder jwtEncoder() {\n\t\t\treturn jwtEncoder;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2TokenGenerator<?> tokenGenerator() {\n\t\t\tJwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder());\n\t\t\tjwtGenerator.setJwtCustomizer(jwtCustomizer());\n\t\t\tOAuth2TokenGenerator<OAuth2RefreshToken> refreshTokenGenerator = new CustomRefreshTokenGenerator();\n\t\t\treturn new DelegatingOAuth2TokenGenerator(jwtGenerator, refreshTokenGenerator);\n\t\t}\n\n\t\tprivate static final class CustomRefreshTokenGenerator implements OAuth2TokenGenerator<OAuth2RefreshToken> {\n\n\t\t\tprivate final StringKeyGenerator refreshTokenGenerator = new Base64StringKeyGenerator(\n\t\t\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\t\t\t@Nullable\n\t\t\t@Override\n\t\t\tpublic OAuth2RefreshToken generate(OAuth2TokenContext context) {\n\t\t\t\tif (!OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tInstant issuedAt = Instant.now();\n\t\t\t\tInstant expiresAt = issuedAt\n\t\t\t\t\t.plus(context.getRegisteredClient().getTokenSettings().getRefreshTokenTimeToLive());\n\t\t\t\treturn new OAuth2RefreshToken(this.refreshTokenGenerator.generateKey(), issuedAt, expiresAt);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithSecurityContextRepository\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer(Customizer.withDefaults())\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.securityContext((securityContext) ->\n\t\t\t\t\t\t\tsecurityContext.securityContextRepository(securityContextRepository));\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfigurationWithTokenGenerator extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tJwtEncoder jwtEncoder() {\n\t\t\treturn jwtEncoder;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2TokenGenerator<?> tokenGenerator() {\n\t\t\tJwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder());\n\t\t\tjwtGenerator.setJwtCustomizer(jwtCustomizer());\n\t\t\tOAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();\n\t\t\tOAuth2TokenGenerator<OAuth2Token> delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(\n\t\t\t\t\tjwtGenerator, refreshTokenGenerator);\n\t\t\treturn spy(new OAuth2TokenGenerator<OAuth2Token>() {\n\t\t\t\t@Override\n\t\t\t\tpublic OAuth2Token generate(OAuth2TokenContext context) {\n\t\t\t\t\treturn delegatingTokenGenerator.generate(context);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfigurationCustomEndpoints extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn authorizationServerSettings;\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationCustomConsentPage extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.authorizationEndpoint((authorizationEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\tauthorizationEndpoint.consentPage(consentPage))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationCustomConsentRequest extends AuthorizationServerConfiguration {\n\n\t\t@Autowired\n\t\tprivate OAuth2AuthorizationConsentService authorizationConsentService;\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.authorizationEndpoint((authorizationEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\tauthorizationEndpoint.authenticationProviders(configureAuthenticationProviders()))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\t@Override\n\t\tOAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {\n\t\t\treturn (context) -> {\n\t\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType())\n\t\t\t\t\t\t&& OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {\n\t\t\t\t\tOAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService\n\t\t\t\t\t\t.findById(context.getRegisteredClient().getId(), context.getPrincipal().getName());\n\n\t\t\t\t\tSet<String> authorities = new HashSet<>();\n\t\t\t\t\tfor (GrantedAuthority authority : authorizationConsent.getAuthorities()) {\n\t\t\t\t\t\tauthorities.add(authority.getAuthority());\n\t\t\t\t\t}\n\t\t\t\t\tcontext.getClaims().claim(AUTHORITIES_CLAIM, authorities);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tprivate Consumer<List<AuthenticationProvider>> configureAuthenticationProviders() {\n\t\t\treturn (authenticationProviders) -> authenticationProviders.forEach((authenticationProvider) -> {\n\t\t\t\tif (authenticationProvider instanceof OAuth2AuthorizationConsentAuthenticationProvider) {\n\t\t\t\t\t((OAuth2AuthorizationConsentAuthenticationProvider) authenticationProvider)\n\t\t\t\t\t\t.setAuthorizationConsentCustomizer(new AuthorizationConsentCustomizer());\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tstatic class AuthorizationConsentCustomizer\n\t\t\t\timplements Consumer<OAuth2AuthorizationConsentAuthenticationContext> {\n\n\t\t\t@Override\n\t\t\tpublic void accept(\n\t\t\t\t\tOAuth2AuthorizationConsentAuthenticationContext authorizationConsentAuthenticationContext) {\n\t\t\t\tOAuth2AuthorizationConsent.Builder authorizationConsentBuilder = authorizationConsentAuthenticationContext\n\t\t\t\t\t.getAuthorizationConsent();\n\t\t\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = authorizationConsentAuthenticationContext\n\t\t\t\t\t.getAuthentication();\n\t\t\t\tMap<String, Object> additionalParameters = authorizationConsentAuthentication.getAdditionalParameters();\n\t\t\t\tRegisteredClient registeredClient = authorizationConsentAuthenticationContext.getRegisteredClient();\n\t\t\t\tClientSettings clientSettings = registeredClient.getClientSettings();\n\n\t\t\t\tSet<String> requestedAuthorities = authorities((String) additionalParameters.get(\"authority\"));\n\t\t\t\tSet<String> allowedAuthorities = authorities(clientSettings.getSetting(\"custom.allowed-authorities\"));\n\t\t\t\tfor (String requestedAuthority : requestedAuthorities) {\n\t\t\t\t\tif (allowedAuthorities.contains(requestedAuthority)) {\n\t\t\t\t\t\tauthorizationConsentBuilder.authority(new SimpleGrantedAuthority(requestedAuthority));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate static Set<String> authorities(String param) {\n\t\t\t\tSet<String> authorities = new HashSet<>();\n\t\t\t\tif (param != null) {\n\t\t\t\t\tList<String> authorityValues = Arrays.asList(param.split(\" \"));\n\t\t\t\t\tauthorities.addAll(authorityValues);\n\t\t\t\t}\n\n\t\t\t\treturn authorities;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationCustomAuthorizationEndpoint extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.authorizationEndpoint((authorizationEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\tauthorizationEndpoint\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authorizationRequestConverter(authorizationRequestConverter)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authorizationRequestConverters(authorizationRequestConvertersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(authorizationRequestAuthenticationProvider)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(authorizationRequestAuthenticationProvidersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authorizationResponseHandler(authorizationResponseHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.errorResponseHandler(authorizationErrorResponseHandler))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfigurationWithMultipleIssuersAllowed\n\t\t\textends AuthorizationServerConfigurationWithTokenGenerator {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithPushedAuthorizationRequests\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.pushedAuthorizationRequestEndpoint(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithPushedAuthorizationRequestsAndCustomConsentPage\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.pushedAuthorizationRequestEndpoint(Customizer.withDefaults())\n\t\t\t\t\t\t\t\t\t.authorizationEndpoint((authorizationEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\tauthorizationEndpoint.consentPage(consentPage))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.hamcrest.CoreMatchers.hasItems;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the OAuth 2.0 Authorization Server Metadata endpoint.\n *\n * @author Daniel Garnier-Moiroux\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2AuthorizationServerMetadataTests {\n\n\tprivate static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = \"/.well-known/oauth-authorization-server\";\n\n\tprivate static final String ISSUER = \"https://example.com\";\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@BeforeAll\n\tpublic static void setupClass() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationServerMetadataRequestAndIssuerSetThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(\"issuer\").value(ISSUER))\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationServerMetadataRequestIncludesIssuerPathThenMetadataResponseHasIssuerPath()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();\n\n\t\tString host = \"https://example.com:8443\";\n\n\t\tString issuerPath = \"/issuer1\";\n\t\tString issuer = host.concat(issuerPath);\n\t\tthis.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(\"issuer\").value(issuer))\n\t\t\t.andReturn();\n\n\t\tissuerPath = \"/path1/issuer2\";\n\t\tissuer = host.concat(issuerPath);\n\t\tthis.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(\"issuer\").value(issuer))\n\t\t\t.andReturn();\n\n\t\tissuerPath = \"/path1/path2/issuer3\";\n\t\tissuer = host.concat(issuerPath);\n\t\tthis.mvc.perform(get(host.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI).concat(issuerPath)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(\"issuer\").value(issuer))\n\t\t\t.andReturn();\n\t}\n\n\t// gh-616\n\t@Test\n\tpublic void requestWhenAuthorizationServerMetadataRequestAndMetadataCustomizerSetThenReturnCustomMetadataResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMetadataCustomizer.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,\n\t\t\t\t\thasItems(\"scope1\", \"scope2\")));\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationServerMetadataRequestAndClientRegistrationEnabledThenMetadataResponseIncludesRegistrationEndpoint()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithClientRegistrationEnabled.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(\"$.registration_endpoint\")\n\t\t\t\t.value(ISSUER.concat(this.authorizationServerSettings.getClientRegistrationEndpoint())));\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationServerMetadataRequestAndDeviceCodeGrantEnabledThenMetadataResponseIncludesDeviceAuthorizationEndpoint()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithDeviceCodeGrantEnabled.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(\"$.device_authorization_endpoint\")\n\t\t\t\t.value(ISSUER.concat(this.authorizationServerSettings.getDeviceAuthorizationEndpoint())))\n\t\t\t.andExpect(jsonPath(\"$.grant_types_supported[4]\").value(AuthorizationGrantType.DEVICE_CODE.getValue()));\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationServerMetadataRequestAndPushedAuthorizationRequestEnabledThenMetadataResponseIncludesPushedAuthorizationRequestEndpoint()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequestEnabled.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(\"$.pushed_authorization_request_endpoint\")\n\t\t\t\t.value(ISSUER.concat(this.authorizationServerSettings.getPushedAuthorizationRequestEndpoint())));\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t\tJdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tregisteredClientRepository.save(registeredClient);\n\t\t\treturn registeredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(ISSUER).build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithMetadataCustomizer extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.authorizationServerMetadataEndpoint((authorizationServerMetadataEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\tauthorizationServerMetadataEndpoint\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authorizationServerMetadataCustomizer(authorizationServerMetadataCustomizer()))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\tprivate Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer() {\n\t\t\treturn (authorizationServerMetadata) -> authorizationServerMetadata.scope(\"scope1\").scope(\"scope2\");\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfigurationWithMultipleIssuersAllowed extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithClientRegistrationEnabled\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithDeviceCodeGrantEnabled extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.deviceAuthorizationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithPushedAuthorizationRequestEnabled\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.pushedAuthorizationRequestEndpoint(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ClientCredentialsGrantTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.authentication.ClientSecretAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.JwtClientAssertionAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.PublicClientAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.X509ClientCertificateAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.oauth2.server.authorization.util.TestX509Certificates;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretBasicAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretPostAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.JwtClientAssertionAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceCodeAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenExchangeAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.PublicClientAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.X509ClientCertificateAuthenticationConverter;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the OAuth 2.0 Client Credentials Grant.\n *\n * @author Alexey Nesterov\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2ClientCredentialsGrantTests {\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tprivate static OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer;\n\n\tprivate static NimbusJwtEncoder dPoPProofJwtEncoder;\n\n\tprivate static AuthenticationConverter authenticationConverter;\n\n\tprivate static Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer;\n\n\tprivate static AuthenticationProvider authenticationProvider;\n\n\tprivate static Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer;\n\n\tprivate static AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate static AuthenticationFailureHandler authenticationFailureHandler;\n\n\tprivate static PasswordEncoder passwordEncoder;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tjwtCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tdPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\tauthenticationConverter = mock(AuthenticationConverter.class);\n\t\tauthenticationConvertersConsumer = mock(Consumer.class);\n\t\tauthenticationProvider = mock(AuthenticationProvider.class);\n\t\tauthenticationProvidersConsumer = mock(Consumer.class);\n\t\tauthenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tauthenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tpasswordEncoder = mock(PasswordEncoder.class);\n\t\tgiven(passwordEncoder.matches(any(), any())).willReturn(true);\n\t\tgiven(passwordEncoder.upgradeEncoding(any())).willReturn(false);\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@BeforeEach\n\tpublic void setup() {\n\t\treset(jwtCustomizer);\n\t\treset(authenticationConverter);\n\t\treset(authenticationConvertersConsumer);\n\t\treset(authenticationProvider);\n\t\treset(authenticationProvidersConsumer);\n\t\treset(authenticationSuccessHandler);\n\t\treset(authenticationFailureHandler);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestNotAuthenticatedThenUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tthis.mvc\n\t\t\t.perform(MockMvcRequestBuilders.post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestValidThenTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1 scope2\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1 scope2\"));\n\n\t\tverify(jwtCustomizer).customize(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestPostsClientCredentialsThenTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1 scope2\")\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_SECRET, registeredClient.getClientSecret()))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1 scope2\"));\n\n\t\tverify(jwtCustomizer).customize(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestPostsClientCredentialsAndRequiresUpgradingThenClientSecretUpgraded()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomPasswordEncoder.class).autowire();\n\n\t\tString clientSecret = \"secret-2\";\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2()\n\t\t\t.clientSecret(\"{noop}\" + clientSecret)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1 scope2\")\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_SECRET, clientSecret))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1 scope2\"));\n\n\t\tverify(jwtCustomizer).customize(any());\n\t\tRegisteredClient updatedRegisteredClient = this.registeredClientRepository\n\t\t\t.findByClientId(registeredClient.getClientId());\n\t\tassertThat(updatedRegisteredClient.getClientSecret()).startsWith(\"{bcrypt}\");\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestWithPKIX509ClientCertificateThenTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.x509CertificateSubjectDN(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0].getSubjectX500Principal().getName())\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.with(SecurityMockMvcRequestPostProcessors.x509(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1 scope2\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1 scope2\"));\n\n\t\tverify(jwtCustomizer).customize(any());\n\t}\n\n\t// gh-1635\n\t@Test\n\tpublic void requestWhenTokenRequestIncludesBasicClientCredentialsAndX509ClientCertificateThenTokenResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.with(SecurityMockMvcRequestPostProcessors.x509(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE))\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1 scope2\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1 scope2\"));\n\n\t\tverify(jwtCustomizer).customize(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenEndpointCustomizedThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomTokenEndpoint.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\t\tgiven(authenticationConverter.convert(any())).willReturn(clientCredentialsAuthentication);\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)));\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tregisteredClient, clientPrincipal, accessToken);\n\t\tgiven(authenticationProvider.supports(eq(OAuth2ClientCredentialsAuthenticationToken.class))).willReturn(true);\n\t\tgiven(authenticationProvider.authenticate(any())).willReturn(accessTokenAuthentication);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk());\n\n\t\tverify(authenticationConverter).convert(any());\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());\n\t\tList<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();\n\t\tassertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter\n\t\t\t\t|| converter instanceof OAuth2AuthorizationCodeAuthenticationConverter\n\t\t\t\t|| converter instanceof OAuth2RefreshTokenAuthenticationConverter\n\t\t\t\t|| converter instanceof OAuth2ClientCredentialsAuthenticationConverter\n\t\t\t\t|| converter instanceof OAuth2DeviceCodeAuthenticationConverter\n\t\t\t\t|| converter instanceof OAuth2TokenExchangeAuthenticationConverter);\n\n\t\tverify(authenticationProvider).authenticate(eq(clientCredentialsAuthentication));\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());\n\t\tList<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();\n\t\tassertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider\n\t\t\t\t|| provider instanceof OAuth2AuthorizationCodeAuthenticationProvider\n\t\t\t\t|| provider instanceof OAuth2RefreshTokenAuthenticationProvider\n\t\t\t\t|| provider instanceof OAuth2ClientCredentialsAuthenticationProvider\n\t\t\t\t|| provider instanceof OAuth2DeviceCodeAuthenticationProvider\n\t\t\t\t|| provider instanceof OAuth2TokenExchangeAuthenticationProvider);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(accessTokenAuthentication));\n\t}\n\n\t@Test\n\tpublic void requestWhenClientAuthenticationCustomizedThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomClientAuthentication.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tnew ClientAuthenticationMethod(\"custom\"), null);\n\t\tgiven(authenticationConverter.convert(any())).willReturn(clientPrincipal);\n\t\tgiven(authenticationProvider.supports(eq(OAuth2ClientAuthenticationToken.class))).willReturn(true);\n\t\tgiven(authenticationProvider.authenticate(any())).willReturn(clientPrincipal);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).param(OAuth2ParameterNames.GRANT_TYPE,\n\t\t\t\t\tAuthorizationGrantType.CLIENT_CREDENTIALS.getValue()))\n\t\t\t.andExpect(status().isOk());\n\n\t\tverify(authenticationConverter).convert(any());\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());\n\t\tList<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();\n\t\tassertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter\n\t\t\t\t|| converter instanceof JwtClientAssertionAuthenticationConverter\n\t\t\t\t|| converter instanceof ClientSecretBasicAuthenticationConverter\n\t\t\t\t|| converter instanceof ClientSecretPostAuthenticationConverter\n\t\t\t\t|| converter instanceof PublicClientAuthenticationConverter\n\t\t\t\t|| converter instanceof X509ClientCertificateAuthenticationConverter);\n\n\t\tverify(authenticationProvider).authenticate(eq(clientPrincipal));\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());\n\t\tList<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();\n\t\tassertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider\n\t\t\t\t|| provider instanceof JwtClientAssertionAuthenticationProvider\n\t\t\t\t|| provider instanceof X509ClientCertificateAuthenticationProvider\n\t\t\t\t|| provider instanceof ClientSecretAuthenticationProvider\n\t\t\t\t|| provider instanceof PublicClientAuthenticationProvider);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(clientPrincipal));\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestIncludesIssuerPathThenIssuerResolvedWithPath() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\n\t\tthis.mvc\n\t\t\t.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1 scope2\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1 scope2\"));\n\n\t\tArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);\n\t\tverify(jwtCustomizer).customize(jwtEncodingContextCaptor.capture());\n\t\tJwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue();\n\t\tassertThat(jwtEncodingContext.getAuthorizationServerContext().getIssuer()).isEqualTo(issuer);\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestWithDPoPProofThenReturnDPoPBoundAccessToken() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tString tokenEndpointUri = \"http://localhost\" + DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tString dPoPProof = generateDPoPProof(tokenEndpointUri);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1 scope2\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))\n\t\t\t\t.header(OAuth2AccessToken.TokenType.DPOP.getValue(), dPoPProof))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").value(OAuth2AccessToken.TokenType.DPOP.getValue()));\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRequestWithMultiplePasswordEncodersThenPrimaryPasswordEncoderUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMultiplePasswordEncoders.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1 scope2\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1 scope2\"));\n\n\t\tverify(passwordEncoder).matches(any(), any());\n\t}\n\n\tprivate static String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n\tprivate static String encodeBasicAuth(String clientId, String secret) throws Exception {\n\t\tclientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());\n\t\tsecret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());\n\t\tString credentialsString = clientId + \":\" + secret;\n\t\tbyte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));\n\t\treturn new String(encodedBytes, StandardCharsets.UTF_8);\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tJdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tRegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();\n\t\t\tjdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\t\t\treturn jdbcRegisteredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {\n\t\t\treturn jwtCustomizer;\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationCustomTokenEndpoint extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.tokenEndpoint((tokenEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\ttokenEndpoint\n\t\t\t\t\t\t\t\t\t\t\t\t\t.accessTokenRequestConverter(authenticationConverter)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.accessTokenRequestConverters(authenticationConvertersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(authenticationProvider)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(authenticationProvidersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.accessTokenResponseHandler(authenticationSuccessHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.errorResponseHandler(authenticationFailureHandler))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationCustomPasswordEncoder extends AuthorizationServerConfiguration {\n\n\t\t@Override\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationCustomClientAuthentication extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\tauthenticationSuccessHandler = spy(authenticationSuccessHandler());\n\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientAuthentication((clientAuthentication) ->\n\t\t\t\t\t\t\t\t\t\t\tclientAuthentication\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationConverter(authenticationConverter)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationConverters(authenticationConvertersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(authenticationProvider)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(authenticationProvidersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationSuccessHandler(authenticationSuccessHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.errorResponseHandler(authenticationFailureHandler))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\tprivate AuthenticationSuccessHandler authenticationSuccessHandler() {\n\t\t\treturn new AuthenticationSuccessHandler() {\n\t\t\t\t@Override\n\t\t\t\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\t\t\tAuthentication authentication) throws IOException, ServletException {\n\t\t\t\t\torg.springframework.security.core.context.SecurityContext securityContext = SecurityContextHolder\n\t\t\t\t\t\t.createEmptyContext();\n\t\t\t\t\tsecurityContext.setAuthentication(authentication);\n\t\t\t\t\tSecurityContextHolder.setContext(securityContext);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfigurationWithMultipleIssuersAllowed extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithMultiplePasswordEncoders extends AuthorizationServerConfiguration {\n\n\t\t@Primary\n\t\t@Bean\n\t\tPasswordEncoder primaryPasswordEncoder() {\n\t\t\treturn passwordEncoder;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ClientRegistrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport jakarta.servlet.http.HttpServletResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.assertj.core.data.TemporalUnitWithinOffset;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.converter.OAuth2ClientRegistrationRegisteredClientConverter;\nimport org.springframework.security.oauth2.server.authorization.converter.RegisteredClientOAuth2ClientRegistrationConverter;\nimport org.springframework.security.oauth2.server.authorization.http.converter.OAuth2ClientRegistrationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientRegistrationAuthenticationConverter;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.util.CollectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for OAuth 2.0 Dynamic Client Registration.\n *\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2ClientRegistrationTests {\n\n\tprivate static final String ISSUER = \"https://example.com:8443/issuer1\";\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static final String DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI = \"/oauth2/register\";\n\n\tprivate static final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tprivate static final HttpMessageConverter<OAuth2ClientRegistration> clientRegistrationHttpMessageConverter = new OAuth2ClientRegistrationHttpMessageConverter();\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate static AuthenticationConverter authenticationConverter;\n\n\tprivate static Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer;\n\n\tprivate static AuthenticationProvider authenticationProvider;\n\n\tprivate static Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer;\n\n\tprivate static AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate static AuthenticationFailureHandler authenticationFailureHandler;\n\n\tprivate MockWebServer server;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t\tauthenticationConverter = mock(AuthenticationConverter.class);\n\t\tauthenticationConvertersConsumer = mock(Consumer.class);\n\t\tauthenticationProvider = mock(AuthenticationProvider.class);\n\t\tauthenticationProvidersConsumer = mock(Consumer.class);\n\t\tauthenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tauthenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t}\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tgiven(authenticationProvider.supports(OAuth2ClientRegistrationAuthenticationToken.class)).willReturn(true);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() throws Exception {\n\t\tthis.server.shutdown();\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t\treset(authenticationConverter);\n\t\treset(authenticationConvertersConsumer);\n\t\treset(authenticationProvider);\n\t\treset(authenticationProvidersConsumer);\n\t\treset(authenticationSuccessHandler);\n\t\treset(authenticationFailureHandler);\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistrationRequestAuthorizedThenClientRegistrationResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tassertClientRegistrationResponse(clientRegistration, clientRegistrationResponse);\n\t}\n\n\t@Test\n\tpublic void requestWhenOpenClientRegistrationRequestThenClientRegistrationResponse() throws Exception {\n\t\tthis.spring.register(OpenClientRegistrationConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(ISSUER.concat(DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI))\n\t\t\t\t.contentType(MediaType.APPLICATION_JSON)\n\t\t\t\t.content(getClientRegistrationRequestContent(clientRegistration)))\n\t\t\t.andExpect(status().isCreated())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andReturn();\n\n\t\tOAuth2ClientRegistration clientRegistrationResponse = readClientRegistrationResponse(mvcResult.getResponse());\n\n\t\tassertClientRegistrationResponse(clientRegistration, clientRegistrationResponse);\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistrationEndpointCustomizedThenUsed() throws Exception {\n\t\tthis.spring.register(CustomClientRegistrationConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\twillAnswer((invocation) -> {\n\t\t\tHttpServletResponse response = invocation.getArgument(1, HttpServletResponse.class);\n\t\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\t\thttpResponse.setStatusCode(HttpStatus.CREATED);\n\t\t\tnew OAuth2ClientRegistrationHttpMessageConverter().write(clientRegistration, null, httpResponse);\n\t\t\treturn null;\n\t\t}).given(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\n\t\tregisterClient(clientRegistration);\n\n\t\tverify(authenticationConverter).convert(any());\n\t\tArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());\n\t\tList<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();\n\t\tassertThat(authenticationConverters).hasSize(2)\n\t\t\t.allMatch((converter) -> converter == authenticationConverter\n\t\t\t\t\t|| converter instanceof OAuth2ClientRegistrationAuthenticationConverter);\n\n\t\tverify(authenticationProvider).authenticate(any());\n\t\tArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());\n\t\tList<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();\n\t\tassertThat(authenticationProviders).hasSize(2)\n\t\t\t.allMatch((provider) -> provider == authenticationProvider\n\t\t\t\t\t|| provider instanceof OAuth2ClientRegistrationAuthenticationProvider);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\t\tverifyNoInteractions(authenticationFailureHandler);\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistrationEndpointCustomizedWithAuthenticationFailureHandlerThenUsed()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CustomClientRegistrationConfiguration.class).autowire();\n\n\t\tgiven(authenticationProvider.authenticate(any())).willThrow(new OAuth2AuthenticationException(\"error\"));\n\n\t\tthis.mvc.perform(post(ISSUER.concat(DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI)).with(jwt()));\n\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());\n\t\tverifyNoInteractions(authenticationSuccessHandler);\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistersWithSecretThenClientAuthenticationSuccess() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tthis.mvc\n\t\t\t.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1\")\n\t\t\t\t.with(httpBasic(clientRegistrationResponse.getClientId(),\n\t\t\t\t\t\tclientRegistrationResponse.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1\"))\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistersWithCustomMetadataThenSavedToRegisteredClient() throws Exception {\n\t\tthis.spring.register(CustomClientMetadataConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.claim(\"custom-metadata-name-1\", \"value-1\")\n\t\t\t\t.claim(\"custom-metadata-name-2\", \"value-2\")\n\t\t\t\t.claim(\"non-registered-custom-metadata\", \"value-3\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(clientRegistrationResponse.getClientId());\n\n\t\tassertClientRegistrationResponse(clientRegistration, clientRegistrationResponse);\n\t\tassertThat(clientRegistrationResponse.<String>getClaim(\"custom-metadata-name-1\")).isEqualTo(\"value-1\");\n\t\tassertThat(clientRegistrationResponse.<String>getClaim(\"custom-metadata-name-2\")).isEqualTo(\"value-2\");\n\t\tassertThat(clientRegistrationResponse.<String>getClaim(\"non-registered-custom-metadata\")).isNull();\n\n\t\tassertThat(registeredClient.getClientSettings().<String>getSetting(\"custom-metadata-name-1\"))\n\t\t\t.isEqualTo(\"value-1\");\n\t\tassertThat(registeredClient.getClientSettings().<String>getSetting(\"custom-metadata-name-2\"))\n\t\t\t.isEqualTo(\"value-2\");\n\t\tassertThat(registeredClient.getClientSettings().<String>getSetting(\"non-registered-custom-metadata\")).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistersWithSecretExpirationThenClientRegistrationResponse() throws Exception {\n\t\tthis.spring.register(ClientSecretExpirationConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tInstant expectedSecretExpiryDate = Instant.now().plus(Duration.ofHours(24));\n\t\tTemporalUnitWithinOffset allowedDelta = new TemporalUnitWithinOffset(1, ChronoUnit.MINUTES);\n\n\t\t// Returned response contains expiration date\n\t\tassertThat(clientRegistrationResponse.getClientSecretExpiresAt()).isNotNull()\n\t\t\t.isCloseTo(expectedSecretExpiryDate, allowedDelta);\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(clientRegistrationResponse.getClientId());\n\n\t\t// Persisted RegisteredClient contains expiration date\n\t\tassertThat(registeredClient).isNotNull();\n\t\tassertThat(registeredClient.getClientSecretExpiresAt()).isNotNull()\n\t\t\t.isCloseTo(expectedSecretExpiryDate, allowedDelta);\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistersWithCustomTokenSettingsThenSavedToRegisteredClient() throws Exception {\n\t\tthis.spring.register(CustomTokenSettingsConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(clientRegistrationResponse.getClientId());\n\n\t\tassertThat(registeredClient).isNotNull();\n\t\tassertThat(registeredClient.getTokenSettings().getAccessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));\n\t}\n\n\tprivate OAuth2ClientRegistration registerClient(OAuth2ClientRegistration clientRegistration) throws Exception {\n\t\t// ***** (1) Obtain the \"initial\" access token used for registering the client\n\n\t\tString clientRegistrationScope = \"client.create\";\n\t\t// @formatter:off\n\t\tRegisteredClient clientRegistrar = RegisteredClient.withId(\"client-registrar-1\")\n\t\t\t\t.clientId(\"client-registrar-1\")\n\t\t\t\t.clientSecret(\"{noop}secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.scope(clientRegistrationScope)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(clientRegistrar);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)\n\t\t\t\t.with(httpBasic(\"client-registrar-1\", \"secret\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(clientRegistrationScope))\n\t\t\t.andReturn();\n\n\t\tOAuth2AccessToken accessToken = readAccessTokenResponse(mvcResult.getResponse()).getAccessToken();\n\n\t\t// ***** (2) Register the client\n\n\t\tHttpHeaders httpHeaders = new HttpHeaders();\n\t\thttpHeaders.setBearerAuth(accessToken.getTokenValue());\n\n\t\t// Register the client\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(ISSUER.concat(DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI)).headers(httpHeaders)\n\t\t\t\t.contentType(MediaType.APPLICATION_JSON)\n\t\t\t\t.content(getClientRegistrationRequestContent(clientRegistration)))\n\t\t\t.andExpect(status().isCreated())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andReturn();\n\n\t\treturn readClientRegistrationResponse(mvcResult.getResponse());\n\t}\n\n\tprivate static void assertClientRegistrationResponse(OAuth2ClientRegistration clientRegistrationRequest,\n\t\t\tOAuth2ClientRegistration clientRegistrationResponse) {\n\t\tassertThat(clientRegistrationResponse.getClientId()).isNotNull();\n\t\tassertThat(clientRegistrationResponse.getClientIdIssuedAt()).isNotNull();\n\t\tassertThat(clientRegistrationResponse.getClientSecret()).isNotNull();\n\t\tassertThat(clientRegistrationResponse.getClientSecretExpiresAt()).isNull();\n\t\tassertThat(clientRegistrationResponse.getClientName()).isEqualTo(clientRegistrationRequest.getClientName());\n\t\tassertThat(clientRegistrationResponse.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistrationRequest.getRedirectUris());\n\t\tassertThat(clientRegistrationResponse.getGrantTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistrationRequest.getGrantTypes());\n\t\tassertThat(clientRegistrationResponse.getResponseTypes())\n\t\t\t.containsExactly(OAuth2AuthorizationResponseType.CODE.getValue());\n\t\tassertThat(clientRegistrationResponse.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistrationRequest.getScopes());\n\t\tassertThat(clientRegistrationResponse.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t}\n\n\tprivate static OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response)\n\t\t\tthrows Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);\n\t}\n\n\tprivate static byte[] getClientRegistrationRequestContent(OAuth2ClientRegistration clientRegistration)\n\t\t\tthrows Exception {\n\t\tMockHttpOutputMessage httpRequest = new MockHttpOutputMessage();\n\t\tclientRegistrationHttpMessageConverter.write(clientRegistration, null, httpRequest);\n\t\treturn httpRequest.getBodyAsBytes();\n\t}\n\n\tprivate static OAuth2ClientRegistration readClientRegistrationResponse(MockHttpServletResponse response)\n\t\t\tthrows Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn clientRegistrationHttpMessageConverter.read(OAuth2ClientRegistration.class, httpResponse);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class CustomClientRegistrationConfiguration extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\t@Override\n\t\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint((clientRegistration) ->\n\t\t\t\t\t\t\t\t\t\t\tclientRegistration\n\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationRequestConverter(authenticationConverter)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationRequestConverters(authenticationConvertersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(authenticationProvider)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(authenticationProvidersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationResponseHandler(authenticationSuccessHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.errorResponseHandler(authenticationFailureHandler)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class CustomClientMetadataConfiguration extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\t@Override\n\t\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint((clientRegistration) ->\n\t\t\t\t\t\t\t\t\t\t\tclientRegistration\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(configureClientRegistrationConverters())\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\tprivate Consumer<List<AuthenticationProvider>> configureClientRegistrationConverters() {\n\t\t\t// @formatter:off\n\t\t\treturn (authenticationProviders) ->\n\t\t\t\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\t\t\t\tList<String> supportedCustomClientMetadata = List.of(\"custom-metadata-name-1\", \"custom-metadata-name-2\");\n\t\t\t\t\t\tif (authenticationProvider instanceof OAuth2ClientRegistrationAuthenticationProvider provider) {\n\t\t\t\t\t\t\tprovider.setRegisteredClientConverter(new CustomRegisteredClientConverter(supportedCustomClientMetadata));\n\t\t\t\t\t\t\tprovider.setClientRegistrationConverter(new CustomClientRegistrationConverter(supportedCustomClientMetadata));\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class ClientSecretExpirationConfiguration extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\t@Override\n\t\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint((clientRegistration) ->\n\t\t\t\t\t\t\t\t\t\t\tclientRegistration\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(configureClientRegistrationConverters())\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\tprivate Consumer<List<AuthenticationProvider>> configureClientRegistrationConverters() {\n\t\t\t// @formatter:off\n\t\t\treturn (authenticationProviders) ->\n\t\t\t\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\t\t\t\tif (authenticationProvider instanceof OAuth2ClientRegistrationAuthenticationProvider provider) {\n\t\t\t\t\t\t\tprovider.setRegisteredClientConverter(new ClientSecretExpirationRegisteredClientConverter());\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class CustomTokenSettingsConfiguration extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\t@Override\n\t\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint((clientRegistration) ->\n\t\t\t\t\t\t\t\t\t\t\tclientRegistration\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(configureClientRegistrationConverters())\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\tprivate Consumer<List<AuthenticationProvider>> configureClientRegistrationConverters() {\n\t\t\t// @formatter:off\n\t\t\treturn (authenticationProviders) ->\n\t\t\t\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\t\t\t\tif (authenticationProvider instanceof OAuth2ClientRegistrationAuthenticationProvider provider) {\n\t\t\t\t\t\t\tOAuth2ClientRegistrationRegisteredClientConverter clientRegistrationRegisteredClientConverter = new OAuth2ClientRegistrationRegisteredClientConverter();\n\t\t\t\t\t\t\tclientRegistrationRegisteredClientConverter.setTokenSettingsCustomizer((tokenSettings) -> tokenSettings.accessTokenTimeToLive(Duration.ofMinutes(60)));\n\t\t\t\t\t\t\tprovider.setRegisteredClientConverter(clientRegistrationRegisteredClientConverter);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class OpenClientRegistrationConfiguration extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\t@Override\n\t\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint((clientRegistration) ->\n\t\t\t\t\t\t\t\t\t\t\tclientRegistration\n\t\t\t\t\t\t\t\t\t\t\t\t\t.openRegistrationAllowed(true)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize\n\t\t\t\t\t\t\t\t\t.requestMatchers(\"/**/oauth2/register\").permitAll()\n\t\t\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t\tRegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();\n\t\t\tJdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tregisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\t\t\tregisteredClientRepository.save(registeredClient);\n\t\t\treturn registeredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t\t}\n\n\t}\n\n\tprivate static final class CustomRegisteredClientConverter\n\t\t\timplements Converter<OAuth2ClientRegistration, RegisteredClient> {\n\n\t\tprivate final OAuth2ClientRegistrationRegisteredClientConverter delegate = new OAuth2ClientRegistrationRegisteredClientConverter();\n\n\t\tprivate final List<String> supportedCustomClientMetadata;\n\n\t\tprivate CustomRegisteredClientConverter(List<String> supportedCustomClientMetadata) {\n\t\t\tthis.supportedCustomClientMetadata = supportedCustomClientMetadata;\n\t\t}\n\n\t\t@Override\n\t\tpublic RegisteredClient convert(OAuth2ClientRegistration clientRegistration) {\n\t\t\tRegisteredClient registeredClient = this.delegate.convert(clientRegistration);\n\n\t\t\tClientSettings.Builder clientSettingsBuilder = ClientSettings\n\t\t\t\t.withSettings(registeredClient.getClientSettings().getSettings());\n\t\t\tif (!CollectionUtils.isEmpty(this.supportedCustomClientMetadata)) {\n\t\t\t\tclientRegistration.getClaims().forEach((claim, value) -> {\n\t\t\t\t\tif (this.supportedCustomClientMetadata.contains(claim)) {\n\t\t\t\t\t\tclientSettingsBuilder.setting(claim, value);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn RegisteredClient.from(registeredClient).clientSettings(clientSettingsBuilder.build()).build();\n\t\t}\n\n\t}\n\n\tprivate static final class CustomClientRegistrationConverter\n\t\t\timplements Converter<RegisteredClient, OAuth2ClientRegistration> {\n\n\t\tprivate final RegisteredClientOAuth2ClientRegistrationConverter delegate = new RegisteredClientOAuth2ClientRegistrationConverter();\n\n\t\tprivate final List<String> supportedCustomClientMetadata;\n\n\t\tprivate CustomClientRegistrationConverter(List<String> supportedCustomClientMetadata) {\n\t\t\tthis.supportedCustomClientMetadata = supportedCustomClientMetadata;\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2ClientRegistration convert(RegisteredClient registeredClient) {\n\t\t\tOAuth2ClientRegistration clientRegistration = this.delegate.convert(registeredClient);\n\n\t\t\tMap<String, Object> clientMetadata = new HashMap<>(clientRegistration.getClaims());\n\t\t\tif (!CollectionUtils.isEmpty(this.supportedCustomClientMetadata)) {\n\t\t\t\tMap<String, Object> clientSettings = registeredClient.getClientSettings().getSettings();\n\t\t\t\tthis.supportedCustomClientMetadata.forEach((customClaim) -> {\n\t\t\t\t\tif (clientSettings.containsKey(customClaim)) {\n\t\t\t\t\t\tclientMetadata.put(customClaim, clientSettings.get(customClaim));\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn OAuth2ClientRegistration.withClaims(clientMetadata).build();\n\t\t}\n\n\t}\n\n\t/**\n\t * This customization adds client secret expiration time by setting\n\t * {@code RegisteredClient.clientSecretExpiresAt} during\n\t * {@code OAuth2ClientRegistration} -> {@code RegisteredClient} conversion\n\t */\n\tprivate static final class ClientSecretExpirationRegisteredClientConverter\n\t\t\timplements Converter<OAuth2ClientRegistration, RegisteredClient> {\n\n\t\tprivate static final OAuth2ClientRegistrationRegisteredClientConverter delegate = new OAuth2ClientRegistrationRegisteredClientConverter();\n\n\t\t@Override\n\t\tpublic RegisteredClient convert(OAuth2ClientRegistration clientRegistration) {\n\t\t\tRegisteredClient registeredClient = delegate.convert(clientRegistration);\n\t\t\tRegisteredClient.Builder registeredClientBuilder = RegisteredClient.from(registeredClient);\n\n\t\t\tInstant clientSecretExpiresAt = Instant.now().plus(Duration.ofHours(24));\n\t\t\tregisteredClientBuilder.clientSecretExpiresAt(clientSecretExpiresAt);\n\n\t\t\treturn registeredClientBuilder.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2DeviceCodeGrantTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2DeviceAuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2DeviceAuthorizationResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for OAuth 2.0 Device Grant.\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2DeviceCodeGrantTests {\n\n\tprivate static final String DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI = \"/oauth2/device_authorization\";\n\n\tprivate static final String DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI = \"/oauth2/device_verification\";\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static final OAuth2TokenType DEVICE_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.DEVICE_CODE);\n\n\tprivate static final String USER_CODE = \"ABCD-EFGH\";\n\n\tprivate static final String STATE = \"123\";\n\n\tprivate static final String DEVICE_CODE = \"abc-XYZ\";\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tprivate static NimbusJwtEncoder dPoPProofJwtEncoder;\n\n\tprivate static final HttpMessageConverter<OAuth2DeviceAuthorizationResponse> deviceAuthorizationResponseHttpMessageConverter = new OAuth2DeviceAuthorizationResponseHttpMessageConverter();\n\n\tprivate static final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationConsentService authorizationConsentService;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tdPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\t// @formatter:off\n\t\tdb = new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql\")\n\t\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization_consent\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenDeviceAuthorizationRequestNotAuthenticatedThenUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.params(parameters))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenRegisteredClientMissingThenUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI)\n\t\t\t\t.params(parameters)\n\t\t\t\t.headers(withClientAuth(registeredClient)))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDeviceAuthorizationRequestValidThenReturnDeviceAuthorizationResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(issuer.concat(DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI))\n\t\t\t\t.params(parameters)\n\t\t\t\t.headers(withClientAuth(registeredClient)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(jsonPath(\"$.device_code\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.user_code\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNumber())\n\t\t\t\t.andExpect(jsonPath(\"$.verification_uri\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.verification_uri_complete\").isNotEmpty())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse = deviceAuthorizationResponseHttpMessageConverter\n\t\t\t.read(OAuth2DeviceAuthorizationResponse.class, httpResponse);\n\t\tString userCode = deviceAuthorizationResponse.getUserCode().getTokenValue();\n\t\tassertThat(userCode).matches(\"[A-Z]{4}-[A-Z]{4}\");\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUri())\n\t\t\t.isEqualTo(\"https://example.com:8443/oauth2/device_verification\");\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUriComplete())\n\t\t\t.isEqualTo(\"https://example.com:8443/oauth2/device_verification?user_code=\" + userCode);\n\n\t\tString deviceCode = deviceAuthorizationResponse.getDeviceCode().getTokenValue();\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(deviceCode, DEVICE_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization.getToken(OAuth2DeviceCode.class)).isNotNull();\n\t\tassertThat(authorization.getToken(OAuth2UserCode.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenDeviceVerificationRequestUnauthenticatedThenUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(300);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(registeredClient.getClientId())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(new OAuth2DeviceCode(DEVICE_CODE, issuedAt, expiresAt))\n\t\t\t\t.token(new OAuth2UserCode(USER_CODE, issuedAt, expiresAt))\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI)\n\t\t\t\t.queryParams(parameters))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDeviceVerificationRequestValidThenDisplaysConsentPage() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(300);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(registeredClient.getClientId())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(new OAuth2DeviceCode(DEVICE_CODE, issuedAt, expiresAt))\n\t\t\t\t.token(new OAuth2UserCode(USER_CODE, issuedAt, expiresAt))\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(issuer.concat(DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI))\n\t\t\t\t.queryParams(parameters)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tString responseHtml = mvcResult.getResponse().getContentAsString();\n\t\tassertThat(responseHtml).contains(\"Consent required\");\n\n\t\tOAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());\n\t\tassertThat(updatedAuthorization.getPrincipalName()).isEqualTo(\"user\");\n\t\tassertThat(updatedAuthorization).isNotNull();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2UserCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(false);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDeviceAuthorizationConsentRequestUnauthenticatedThenUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(300);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(\"user\")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(new OAuth2DeviceCode(DEVICE_CODE, issuedAt, expiresAt))\n\t\t\t\t.token(new OAuth2UserCode(USER_CODE, issuedAt, expiresAt))\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.attribute(OAuth2ParameterNames.STATE, STATE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, registeredClient.getScopes().iterator().next());\n\t\tparameters.set(OAuth2ParameterNames.STATE, STATE);\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI)\n\t\t\t\t.params(parameters))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDeviceAuthorizationConsentRequestValidThenRedirectsToSuccessPage() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(300);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(\"user\")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(new OAuth2DeviceCode(DEVICE_CODE, issuedAt, expiresAt))\n\t\t\t\t.token(new OAuth2UserCode(USER_CODE, issuedAt, expiresAt))\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.attribute(OAuth2ParameterNames.STATE, STATE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, registeredClient.getScopes().iterator().next());\n\t\tparameters.set(OAuth2ParameterNames.STATE, STATE);\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI)\n\t\t\t\t.params(parameters)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tassertThat(mvcResult.getResponse().getHeader(HttpHeaders.LOCATION)).isEqualTo(\"/?success\");\n\n\t\tOAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());\n\t\tassertThat(updatedAuthorization).isNotNull();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2UserCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenAccessTokenRequestUnauthenticatedThenUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(300);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(registeredClient.getClientId())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(new OAuth2DeviceCode(DEVICE_CODE, issuedAt, expiresAt))\n\t\t\t\t.token(new OAuth2UserCode(USER_CODE, issuedAt, expiresAt), withInvalidated())\n\t\t\t\t.authorizedScopes(registeredClient.getScopes())\n\t\t\t\t.attribute(Principal.class.getName(), new UsernamePasswordAuthenticationToken(\"user\", null))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.DEVICE_CODE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.DEVICE_CODE, DEVICE_CODE);\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(parameters))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenAccessTokenRequestValidThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(300);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(registeredClient.getClientId())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(new OAuth2DeviceCode(DEVICE_CODE, issuedAt, expiresAt))\n\t\t\t\t.token(new OAuth2UserCode(USER_CODE, issuedAt, expiresAt), withInvalidated())\n\t\t\t\t.authorizedScopes(registeredClient.getScopes())\n\t\t\t\t.attribute(Principal.class.getName(), new UsernamePasswordAuthenticationToken(\"user\", null))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationConsent authorizationConsent =\n\t\t\t\tOAuth2AuthorizationConsent.withId(registeredClient.getClientId(), \"user\")\n\t\t\t\t\t\t.scope(registeredClient.getScopes().iterator().next())\n\t\t\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationConsentService.save(authorizationConsent);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.DEVICE_CODE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.DEVICE_CODE, DEVICE_CODE);\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(parameters)\n\t\t\t\t.headers(withClientAuth(registeredClient)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNumber())\n\t\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tOAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());\n\t\tassertThat(updatedAuthorization).isNotNull();\n\t\tassertThat(updatedAuthorization.getAccessToken()).isNotNull();\n\t\tassertThat(updatedAuthorization.getRefreshToken()).isNotNull();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2DeviceCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\t// @formatter:on\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = accessTokenResponseHttpMessageConverter\n\t\t\t.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tString accessToken = accessTokenResponse.getAccessToken().getTokenValue();\n\t\tOAuth2Authorization accessTokenAuthorization = this.authorizationService.findByToken(accessToken,\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(accessTokenAuthorization).isEqualTo(updatedAuthorization);\n\t}\n\n\t@Test\n\tpublic void requestWhenAccessTokenRequestWithDPoPProofThenReturnDPoPBoundAccessToken() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(300);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(registeredClient.getClientId())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(new OAuth2DeviceCode(DEVICE_CODE, issuedAt, expiresAt))\n\t\t\t\t.token(new OAuth2UserCode(USER_CODE, issuedAt, expiresAt), withInvalidated())\n\t\t\t\t.authorizedScopes(registeredClient.getScopes())\n\t\t\t\t.attribute(Principal.class.getName(), new UsernamePasswordAuthenticationToken(\"user\", null))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationConsent authorizationConsent =\n\t\t\t\tOAuth2AuthorizationConsent.withId(registeredClient.getClientId(), \"user\")\n\t\t\t\t\t\t.scope(registeredClient.getScopes().iterator().next())\n\t\t\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationConsentService.save(authorizationConsent);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.DEVICE_CODE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.DEVICE_CODE, DEVICE_CODE);\n\n\t\tString tokenEndpointUri = \"http://localhost\" + DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tString dPoPProof = generateDPoPProof(tokenEndpointUri);\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t\t\t.params(parameters)\n\t\t\t\t\t\t.headers(withClientAuth(registeredClient))\n\t\t\t\t\t\t.header(OAuth2AccessToken.TokenType.DPOP.getValue(), dPoPProof))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(jsonPath(\"$.token_type\").value(OAuth2AccessToken.TokenType.DPOP.getValue()));\n\t\t// @formatter:on\n\n\t\tauthorization = this.authorizationService.findById(authorization.getId());\n\t\tassertThat(authorization.getAccessToken().getClaims()).containsKey(\"cnf\");\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, Object> cnfClaims = (Map<String, Object>) authorization.getAccessToken().getClaims().get(\"cnf\");\n\t\tassertThat(cnfClaims).containsKey(\"jkt\");\n\t\tString jwkThumbprintClaim = (String) cnfClaims.get(\"jkt\");\n\t\tassertThat(jwkThumbprintClaim).isEqualTo(TestJwks.DEFAULT_EC_JWK.toPublicJWK().computeThumbprint().toString());\n\t}\n\n\tprivate static String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n\tprivate static HttpHeaders withClientAuth(RegisteredClient registeredClient) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret());\n\t\treturn headers;\n\t}\n\n\tprivate static Consumer<Map<String, Object>> withInvalidated() {\n\t\treturn (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true);\n\t}\n\n\tprivate static Function<OAuth2Authorization.Token<? extends OAuth2Token>, Boolean> isInvalidated() {\n\t\treturn (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.deviceAuthorizationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\treturn new JdbcRegisteredClientRepository(jdbcOperations);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationConsentService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().build();\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithMultipleIssuersAllowed extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.deviceAuthorizationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t\t\t\t\t.deviceVerificationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2RefreshTokenGrantTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.Principal;\nimport java.security.PublicKey;\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.lang.Nullable;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.Transient;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the OAuth 2.0 Refresh Token Grant.\n *\n * @author Alexey Nesterov\n * @since 7.0\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2RefreshTokenGrantTests {\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = \"/oauth2/revoke\";\n\n\tprivate static final String AUTHORITIES_CLAIM = \"authorities\";\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tprivate static NimbusJwtDecoder jwtDecoder;\n\n\tprivate static NimbusJwtEncoder dPoPProofJwtEncoder;\n\n\tprivate static HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tjwtDecoder = NimbusJwtDecoder.withPublicKey(TestKeys.DEFAULT_PUBLIC_KEY).build();\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tdPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenRefreshTokenRequestValidThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter\n\t\t\t.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\t// Assert user authorities was propagated as claim in JWT\n\t\tJwt jwt = jwtDecoder.decode(accessTokenResponse.getAccessToken().getTokenValue());\n\t\tList<String> authoritiesClaim = jwt.getClaim(AUTHORITIES_CLAIM);\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\t\tSet<String> userAuthorities = new HashSet<>();\n\t\tfor (GrantedAuthority authority : principal.getAuthorities()) {\n\t\t\tuserAuthorities.add(authority.getAuthority());\n\t\t}\n\t\tassertThat(authoritiesClaim).containsExactlyInAnyOrderElementsOf(userAuthorities);\n\t}\n\n\t// gh-432\n\t@Test\n\tpublic void requestWhenRevokeAndRefreshThenAccessTokenActive() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2AccessToken token = authorization.getAccessToken().getToken();\n\t\tOAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRevocationRequestParameters(token, tokenType))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk());\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk());\n\n\t\tOAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();\n\t\tassertThat(accessToken.isActive()).isTrue();\n\t}\n\n\t// gh-1430\n\t@Test\n\tpublic void requestWhenRefreshTokenRequestWithPublicClientThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPublicClientAuthentication.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty());\n\t}\n\n\t@Test\n\tpublic void requestWhenRefreshTokenRequestWithPublicClientAndDPoPProofThenReturnDPoPBoundAccessToken()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPublicClientAuthentication.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.DPOP,\n\t\t\t\t\"dpop-bound-access-token\", Instant.now(), Instant.now().plusSeconds(300));\n\t\tMap<String, Object> accessTokenClaims = new HashMap<>();\n\t\tMap<String, Object> cnfClaim = new HashMap<>();\n\t\tcnfClaim.put(\"jkt\", TestJwks.DEFAULT_EC_JWK.toPublicJWK().computeThumbprint().toString());\n\t\taccessTokenClaims.put(\"cnf\", cnfClaim);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, accessToken, accessTokenClaims)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tString tokenEndpointUri = \"http://localhost\" + DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tString dPoPProof = generateDPoPProof(tokenEndpointUri);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.header(OAuth2AccessToken.TokenType.DPOP.getValue(), dPoPProof))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").value(OAuth2AccessToken.TokenType.DPOP.getValue()));\n\n\t\tauthorization = this.authorizationService.findById(authorization.getId());\n\t\tassertThat(authorization.getAccessToken().getClaims()).containsKey(\"cnf\");\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, Object> cnfClaims = (Map<String, Object>) authorization.getAccessToken().getClaims().get(\"cnf\");\n\t\tassertThat(cnfClaims).containsKey(\"jkt\");\n\t\tString jwkThumbprintClaim = (String) cnfClaims.get(\"jkt\");\n\t\tassertThat(jwkThumbprintClaim).isEqualTo(TestJwks.DEFAULT_EC_JWK.toPublicJWK().computeThumbprint().toString());\n\t}\n\n\t@Test\n\tpublic void requestWhenRefreshTokenRequestWithPublicClientAndDPoPProofAndAccessTokenNotBoundThenBadRequest()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPublicClientAuthentication.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tString tokenEndpointUri = \"http://localhost\" + DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tString dPoPProof = generateDPoPProof(tokenEndpointUri);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.header(OAuth2AccessToken.TokenType.DPOP.getValue(), dPoPProof))\n\t\t\t.andExpect(status().isBadRequest())\n\t\t\t.andExpect(jsonPath(\"$.error\").value(OAuth2ErrorCodes.INVALID_DPOP_PROOF))\n\t\t\t.andExpect(jsonPath(\"$.error_description\").value(\"jkt claim is missing.\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenRefreshTokenRequestWithPublicClientAndDPoPProofAndDifferentPublicKeyThenBadRequest()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPublicClientAuthentication.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.DPOP,\n\t\t\t\t\"dpop-bound-access-token\", Instant.now(), Instant.now().plusSeconds(300));\n\t\tMap<String, Object> accessTokenClaims = new HashMap<>();\n\t\t// Bind access token to different public key\n\t\tPublicKey publicKey = TestJwks.DEFAULT_RSA_JWK.toPublicKey();\n\t\tMap<String, Object> cnfClaim = new HashMap<>();\n\t\tcnfClaim.put(\"jkt\", computeSHA256(publicKey));\n\t\taccessTokenClaims.put(\"cnf\", cnfClaim);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, accessToken, accessTokenClaims)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tString tokenEndpointUri = \"http://localhost\" + DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tString dPoPProof = generateDPoPProof(tokenEndpointUri);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization))\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.header(OAuth2AccessToken.TokenType.DPOP.getValue(), dPoPProof))\n\t\t\t.andExpect(status().isBadRequest())\n\t\t\t.andExpect(jsonPath(\"$.error\").value(OAuth2ErrorCodes.INVALID_DPOP_PROOF))\n\t\t\t.andExpect(jsonPath(\"$.error_description\").value(\"jwk header is invalid.\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenRefreshTokenRequestWithDPoPProofThenReturnDPoPBoundAccessToken() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tString tokenEndpointUri = \"http://localhost\" + DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tString dPoPProof = generateDPoPProof(tokenEndpointUri);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))\n\t\t\t\t.header(OAuth2AccessToken.TokenType.DPOP.getValue(), dPoPProof))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").value(OAuth2AccessToken.TokenType.DPOP.getValue()));\n\n\t\tauthorization = this.authorizationService.findById(authorization.getId());\n\t\tassertThat(authorization.getAccessToken().getClaims()).containsKey(\"cnf\");\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, Object> cnfClaims = (Map<String, Object>) authorization.getAccessToken().getClaims().get(\"cnf\");\n\t\tassertThat(cnfClaims).containsKey(\"jkt\");\n\t}\n\n\tprivate static String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n\tprivate static String computeSHA256(PublicKey publicKey) throws Exception {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(publicKey.getEncoded());\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n\tprivate static MultiValueMap<String, String> getRefreshTokenRequestParameters(OAuth2Authorization authorization) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue());\n\t\tparameters.set(OAuth2ParameterNames.REFRESH_TOKEN, authorization.getRefreshToken().getToken().getTokenValue());\n\t\treturn parameters;\n\t}\n\n\tprivate static MultiValueMap<String, String> getTokenRevocationRequestParameters(OAuth2Token token,\n\t\t\tOAuth2TokenType tokenType) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.TOKEN, token.getTokenValue());\n\t\tparameters.set(OAuth2ParameterNames.TOKEN_TYPE_HINT, tokenType.getValue());\n\t\treturn parameters;\n\t}\n\n\tprivate static String encodeBasicAuth(String clientId, String secret) throws Exception {\n\t\tclientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());\n\t\tsecret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());\n\t\tString credentialsString = clientId + \":\" + secret;\n\t\tbyte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));\n\t\treturn new String(encodedBytes, StandardCharsets.UTF_8);\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tJdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tRegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();\n\t\t\tjdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\t\t\treturn jdbcRegisteredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {\n\t\t\treturn (context) -> {\n\t\t\t\tif (AuthorizationGrantType.REFRESH_TOKEN.equals(context.getAuthorizationGrantType())) {\n\t\t\t\t\tAuthentication principal = context.getPrincipal();\n\t\t\t\t\tSet<String> authorities = new HashSet<>();\n\t\t\t\t\tfor (GrantedAuthority authority : principal.getAuthorities()) {\n\t\t\t\t\t\tauthorities.add(authority.getAuthority());\n\t\t\t\t\t}\n\t\t\t\t\tcontext.getClaims().claim(AUTHORITIES_CLAIM, authorities);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithPublicClientAuthentication\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(\n\t\t\t\tHttpSecurity http, RegisteredClientRepository registeredClientRepository) throws Exception {\n\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.clientAuthentication((clientAuthentication) ->\n\t\t\t\t\t\t\t\t\t\t\tclientAuthentication\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationConverter(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew PublicClientRefreshTokenAuthenticationConverter())\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew PublicClientRefreshTokenAuthenticationProvider(registeredClientRepository)))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@Transient\n\tprivate static final class PublicClientRefreshTokenAuthenticationToken extends OAuth2ClientAuthenticationToken {\n\n\t\tprivate PublicClientRefreshTokenAuthenticationToken(String clientId) {\n\t\t\tsuper(clientId, ClientAuthenticationMethod.NONE, null, null);\n\t\t}\n\n\t\tprivate PublicClientRefreshTokenAuthenticationToken(RegisteredClient registeredClient) {\n\t\t\tsuper(registeredClient, ClientAuthenticationMethod.NONE, null);\n\t\t}\n\n\t}\n\n\tprivate static final class PublicClientRefreshTokenAuthenticationConverter implements AuthenticationConverter {\n\n\t\t@Nullable\n\t\t@Override\n\t\tpublic Authentication convert(HttpServletRequest request) {\n\t\t\t// grant_type (REQUIRED)\n\t\t\tString grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);\n\t\t\tif (!AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(grantType)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// client_id (REQUIRED)\n\t\t\tString clientId = request.getParameter(OAuth2ParameterNames.CLIENT_ID);\n\t\t\tif (!StringUtils.hasText(clientId)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn new PublicClientRefreshTokenAuthenticationToken(clientId);\n\t\t}\n\n\t}\n\n\tprivate static final class PublicClientRefreshTokenAuthenticationProvider implements AuthenticationProvider {\n\n\t\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\t\tprivate PublicClientRefreshTokenAuthenticationProvider(RegisteredClientRepository registeredClientRepository) {\n\t\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\tPublicClientRefreshTokenAuthenticationToken publicClientAuthentication = (PublicClientRefreshTokenAuthenticationToken) authentication;\n\n\t\t\tif (!ClientAuthenticationMethod.NONE.equals(publicClientAuthentication.getClientAuthenticationMethod())) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tString clientId = publicClientAuthentication.getPrincipal().toString();\n\t\t\tRegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);\n\t\t\tif (registeredClient == null) {\n\t\t\t\tthrowInvalidClient(OAuth2ParameterNames.CLIENT_ID);\n\t\t\t}\n\n\t\t\tif (!registeredClient.getClientAuthenticationMethods()\n\t\t\t\t.contains(publicClientAuthentication.getClientAuthenticationMethod())) {\n\t\t\t\tthrowInvalidClient(\"authentication_method\");\n\t\t\t}\n\n\t\t\treturn new PublicClientRefreshTokenAuthenticationToken(registeredClient);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\treturn PublicClientRefreshTokenAuthenticationToken.class.isAssignableFrom(authentication);\n\t\t}\n\n\t\tprivate static void throwInvalidClient(String parameterName) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\t\"Public client authentication failed: \" + parameterName, null);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2TokenExchangeGrantTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for OAuth 2.0 Token Exchange Grant.\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2TokenExchangeGrantTests {\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static final String RESOURCE = \"https://mydomain.com/resource\";\n\n\tprivate static final String AUDIENCE = \"audience\";\n\n\tprivate static final String SUBJECT_TOKEN = \"EfYu_0jEL\";\n\n\tprivate static final String ACTOR_TOKEN = \"JlNE_xR1f\";\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate static NimbusJwtEncoder dPoPProofJwtEncoder;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tprivate final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tAuthorizationServerConfiguration.JWK_SOURCE = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tdPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\t// @formatter:off\n\t\tAuthorizationServerConfiguration.DB = new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql\")\n\t\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization_consent\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tAuthorizationServerConfiguration.DB.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenAccessTokenRequestNotAuthenticatedThenUnauthorized() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(parameters))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenAccessTokenRequestValidAndNoActorTokenThenReturnAccessTokenResponseForImpersonation()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tUsernamePasswordAuthenticationToken userPrincipal = createUserPrincipal(\"user\");\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.attribute(Principal.class.getName(), userPrincipal)\n\t\t\t.build();\n\t\tthis.authorizationService.save(subjectAuthorization);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN,\n\t\t\t\tsubjectAuthorization.getAccessToken().getToken().getTokenValue());\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.RESOURCE, RESOURCE);\n\t\tparameters.set(OAuth2ParameterNames.AUDIENCE, AUDIENCE);\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(parameters)\n\t\t\t\t.headers(withClientAuth(registeredClient)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.refresh_token\").doesNotExist())\n\t\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNumber())\n\t\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.issued_token_type\").isNotEmpty())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseHttpMessageConverter\n\t\t\t.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tString accessToken = accessTokenResponse.getAccessToken().getTokenValue();\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(accessToken,\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(authorization).isNotNull();\n\t\tassertThat(authorization.getAccessToken()).isNotNull();\n\t\tassertThat(authorization.getAccessToken().getClaims()).isNotNull();\n\t\t// We do not populate claims (e.g. `aud`) based on the resource or audience\n\t\t// parameters\n\t\tassertThat(authorization.getAccessToken().getClaims().get(OAuth2TokenClaimNames.AUD))\n\t\t\t.isEqualTo(List.of(registeredClient.getClientId()));\n\t\tassertThat(authorization.getRefreshToken()).isNull();\n\t\tassertThat(authorization.<Authentication>getAttribute(Principal.class.getName())).isEqualTo(userPrincipal);\n\t}\n\n\t@Test\n\tpublic void requestWhenAccessTokenRequestValidAndActorTokenThenReturnAccessTokenResponseForDelegation()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tUsernamePasswordAuthenticationToken userPrincipal = createUserPrincipal(\"user\");\n\t\tUsernamePasswordAuthenticationToken adminPrincipal = createUserPrincipal(\"admin\");\n\t\tMap<String, Object> actorTokenClaims = new HashMap<>();\n\t\tactorTokenClaims.put(OAuth2TokenClaimNames.ISS, \"issuer2\");\n\t\tactorTokenClaims.put(OAuth2TokenClaimNames.SUB, \"admin\");\n\t\tMap<String, Object> subjectTokenClaims = new HashMap<>();\n\t\tsubjectTokenClaims.put(OAuth2TokenClaimNames.ISS, \"issuer1\");\n\t\tsubjectTokenClaims.put(OAuth2TokenClaimNames.SUB, \"user\");\n\t\tsubjectTokenClaims.put(\"may_act\", actorTokenClaims);\n\t\tOAuth2AccessToken subjectToken = createAccessToken(SUBJECT_TOKEN);\n\t\tOAuth2AccessToken actorToken = createAccessToken(ACTOR_TOKEN);\n\t\t// @formatter:off\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient, subjectToken, subjectTokenClaims)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.attribute(Principal.class.getName(), userPrincipal)\n\t\t\t\t.build();\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient, actorToken, actorTokenClaims)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.attribute(Principal.class.getName(), adminPrincipal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(subjectAuthorization);\n\t\tthis.authorizationService.save(actorAuthorization);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.ACTOR_TOKEN, ACTOR_TOKEN);\n\t\tparameters.set(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(parameters)\n\t\t\t\t.headers(withClientAuth(registeredClient)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.refresh_token\").doesNotExist())\n\t\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNumber())\n\t\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t\t.andExpect(jsonPath(\"$.issued_token_type\").isNotEmpty())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseHttpMessageConverter\n\t\t\t.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tString accessToken = accessTokenResponse.getAccessToken().getTokenValue();\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(accessToken,\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(authorization).isNotNull();\n\t\tassertThat(authorization.getAccessToken()).isNotNull();\n\t\tassertThat(authorization.getAccessToken().getClaims()).isNotNull();\n\t\tassertThat(authorization.getAccessToken().getClaims().get(\"act\")).isNotNull();\n\t\tassertThat(authorization.getRefreshToken()).isNull();\n\t\tassertThat(authorization.<Authentication>getAttribute(Principal.class.getName()))\n\t\t\t.isInstanceOf(OAuth2TokenExchangeCompositeAuthenticationToken.class);\n\t}\n\n\t@Test\n\tpublic void requestWhenAccessTokenRequestWithDPoPProofThenReturnDPoPBoundAccessToken() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tUsernamePasswordAuthenticationToken userPrincipal = createUserPrincipal(\"user\");\n\t\tUsernamePasswordAuthenticationToken adminPrincipal = createUserPrincipal(\"admin\");\n\t\tMap<String, Object> actorTokenClaims = new HashMap<>();\n\t\tactorTokenClaims.put(OAuth2TokenClaimNames.ISS, \"issuer2\");\n\t\tactorTokenClaims.put(OAuth2TokenClaimNames.SUB, \"admin\");\n\t\tMap<String, Object> subjectTokenClaims = new HashMap<>();\n\t\tsubjectTokenClaims.put(OAuth2TokenClaimNames.ISS, \"issuer1\");\n\t\tsubjectTokenClaims.put(OAuth2TokenClaimNames.SUB, \"user\");\n\t\tsubjectTokenClaims.put(\"may_act\", actorTokenClaims);\n\t\tOAuth2AccessToken subjectToken = createAccessToken(SUBJECT_TOKEN);\n\t\tOAuth2AccessToken actorToken = createAccessToken(ACTOR_TOKEN);\n\t\t// @formatter:off\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient, subjectToken, subjectTokenClaims)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.attribute(Principal.class.getName(), userPrincipal)\n\t\t\t\t.build();\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient, actorToken, actorTokenClaims)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.attribute(Principal.class.getName(), adminPrincipal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(subjectAuthorization);\n\t\tthis.authorizationService.save(actorAuthorization);\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.ACTOR_TOKEN, ACTOR_TOKEN);\n\t\tparameters.set(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\n\t\tString tokenEndpointUri = \"http://localhost\" + DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tString dPoPProof = generateDPoPProof(tokenEndpointUri);\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t\t\t.params(parameters)\n\t\t\t\t\t\t.headers(withClientAuth(registeredClient))\n\t\t\t\t\t\t.header(OAuth2AccessToken.TokenType.DPOP.getValue(), dPoPProof))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(jsonPath(\"$.token_type\").value(OAuth2AccessToken.TokenType.DPOP.getValue()));\n\t\t// @formatter:on\n\t}\n\n\tprivate static OAuth2AccessToken createAccessToken(String tokenValue) {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(300);\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, tokenValue, issuedAt, expiresAt);\n\t}\n\n\tprivate static UsernamePasswordAuthenticationToken createUserPrincipal(String username) {\n\t\tUser user = new User(username, \"\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\treturn UsernamePasswordAuthenticationToken.authenticated(user, null, user.getAuthorities());\n\t}\n\n\tprivate static HttpHeaders withClientAuth(RegisteredClient registeredClient) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret());\n\t\treturn headers;\n\t}\n\n\tprivate static Consumer<Map<String, Object>> withInvalidated() {\n\t\treturn (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true);\n\t}\n\n\tprivate static Function<OAuth2Authorization.Token<? extends OAuth2Token>, Boolean> isInvalidated() {\n\t\treturn (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME);\n\t}\n\n\tprivate static String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\tstatic JWKSource<SecurityContext> JWK_SOURCE;\n\n\t\tstatic EmbeddedDatabase DB;\n\n\t\t@Bean\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\treturn new JdbcRegisteredClientRepository(jdbcOperations);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationConsentService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(DB);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn JWK_SOURCE;\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2TokenIntrospectionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.http.converter.OAuth2TokenIntrospectionHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsSet;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenIntrospectionAuthenticationConverter;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the OAuth 2.0 Token Introspection endpoint.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2TokenIntrospectionTests {\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer;\n\n\tprivate static AuthenticationConverter authenticationConverter;\n\n\tprivate static Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer;\n\n\tprivate static AuthenticationProvider authenticationProvider;\n\n\tprivate static Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer;\n\n\tprivate static AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate static AuthenticationFailureHandler authenticationFailureHandler;\n\n\tprivate static final HttpMessageConverter<OAuth2TokenIntrospection> tokenIntrospectionHttpResponseConverter = new OAuth2TokenIntrospectionHttpMessageConverter();\n\n\tprivate static final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\t@Autowired\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tauthenticationConverter = mock(AuthenticationConverter.class);\n\t\tauthenticationConvertersConsumer = mock(Consumer.class);\n\t\tauthenticationProvider = mock(AuthenticationProvider.class);\n\t\tauthenticationProvidersConsumer = mock(Consumer.class);\n\t\tauthenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tauthenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\taccessTokenCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@BeforeEach\n\tpublic void setup() {\n\t\treset(authenticationConverter);\n\t\treset(authenticationConvertersConsumer);\n\t\treset(authenticationProvider);\n\t\treset(authenticationProvidersConsumer);\n\t\treset(authenticationSuccessHandler);\n\t\treset(authenticationFailureHandler);\n\t\treset(accessTokenCustomizer);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenIntrospectValidAccessTokenThenActive() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient introspectRegisteredClient = TestRegisteredClients.registeredClient2()\n\t\t\t.clientSecret(\"secret-2\")\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(introspectRegisteredClient);\n\n\t\tRegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient().build();\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\t\tissuedAt, expiresAt, new HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\t// @formatter:off\n\t\tOAuth2TokenClaimsSet accessTokenClaims = OAuth2TokenClaimsSet.builder()\n\t\t\t\t.issuer(\"https://provider.com\")\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.audience(Collections.singletonList(authorizedRegisteredClient.getClientId()))\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.notBefore(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.claim(OAuth2TokenIntrospectionClaimNames.SCOPE, accessToken.getScopes())\n\t\t\t\t.id(\"id\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(authorizedRegisteredClient, accessToken, accessTokenClaims.getClaims())\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(authorizedRegisteredClient);\n\t\tthis.authorizationService.save(authorization);\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(this.authorizationServerSettings.getTokenIntrospectionEndpoint())\n\t\t\t\t.params(getTokenIntrospectionRequestParameters(accessToken, OAuth2TokenType.ACCESS_TOKEN))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(introspectRegisteredClient)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tOAuth2TokenIntrospection tokenIntrospectionResponse = readTokenIntrospectionResponse(mvcResult);\n\t\tassertThat(tokenIntrospectionResponse.isActive()).isTrue();\n\t\tassertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(authorizedRegisteredClient.getClientId());\n\t\tassertThat(tokenIntrospectionResponse.getUsername()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween(accessTokenClaims.getIssuedAt().minusSeconds(1),\n\t\t\t\taccessTokenClaims.getIssuedAt().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween(\n\t\t\t\taccessTokenClaims.getExpiresAt().minusSeconds(1), accessTokenClaims.getExpiresAt().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getScopes()).containsExactlyInAnyOrderElementsOf(accessToken.getScopes());\n\t\tassertThat(tokenIntrospectionResponse.getTokenType()).isEqualTo(accessToken.getTokenType().getValue());\n\t\tassertThat(tokenIntrospectionResponse.getNotBefore()).isBetween(\n\t\t\t\taccessTokenClaims.getNotBefore().minusSeconds(1), accessTokenClaims.getNotBefore().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getSubject()).isEqualTo(accessTokenClaims.getSubject());\n\t\tassertThat(tokenIntrospectionResponse.getAudience())\n\t\t\t.containsExactlyInAnyOrderElementsOf(accessTokenClaims.getAudience());\n\t\tassertThat(tokenIntrospectionResponse.getIssuer()).isEqualTo(accessTokenClaims.getIssuer());\n\t\tassertThat(tokenIntrospectionResponse.getId()).isEqualTo(accessTokenClaims.getId());\n\t}\n\n\t@Test\n\tpublic void requestWhenIntrospectValidRefreshTokenThenActive() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient introspectRegisteredClient = TestRegisteredClients.registeredClient2()\n\t\t\t.clientSecret(\"secret-2\")\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(introspectRegisteredClient);\n\n\t\tRegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(authorizedRegisteredClient).build();\n\t\tOAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();\n\t\tthis.registeredClientRepository.save(authorizedRegisteredClient);\n\t\tthis.authorizationService.save(authorization);\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(this.authorizationServerSettings.getTokenIntrospectionEndpoint())\n\t\t\t\t.params(getTokenIntrospectionRequestParameters(refreshToken, OAuth2TokenType.REFRESH_TOKEN))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(introspectRegisteredClient)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tOAuth2TokenIntrospection tokenIntrospectionResponse = readTokenIntrospectionResponse(mvcResult);\n\t\tassertThat(tokenIntrospectionResponse.isActive()).isTrue();\n\t\tassertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(authorizedRegisteredClient.getClientId());\n\t\tassertThat(tokenIntrospectionResponse.getUsername()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween(refreshToken.getIssuedAt().minusSeconds(1),\n\t\t\t\trefreshToken.getIssuedAt().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween(refreshToken.getExpiresAt().minusSeconds(1),\n\t\t\t\trefreshToken.getExpiresAt().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getScopes()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getTokenType()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getNotBefore()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getSubject()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getAudience()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getIssuer()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getId()).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenObtainReferenceAccessTokenAndIntrospectThenActive() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tTokenSettings tokenSettings = TokenSettings.builder()\n\t\t\t\t.accessTokenFormat(OAuth2TokenFormat.REFERENCE)\n\t\t\t\t.build();\n\t\tRegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.tokenSettings(tokenSettings)\n\t\t\t\t.clientSettings(ClientSettings.builder().requireProofKey(false).build())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(authorizedRegisteredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(authorizedRegisteredClient).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(post(this.authorizationServerSettings.getTokenEndpoint())\n\t\t\t\t.params(getAuthorizationCodeTokenRequestParameters(authorizedRegisteredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(authorizedRegisteredClient)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tOAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(mvcResult);\n\t\tOAuth2AccessToken accessToken = accessTokenResponse.getAccessToken();\n\n\t\tRegisteredClient introspectRegisteredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(introspectRegisteredClient);\n\n\t\t// @formatter:off\n\t\tmvcResult = this.mvc.perform(post(this.authorizationServerSettings.getTokenIntrospectionEndpoint())\n\t\t\t\t.params(getTokenIntrospectionRequestParameters(accessToken, OAuth2TokenType.ACCESS_TOKEN))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(introspectRegisteredClient)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\tOAuth2TokenIntrospection tokenIntrospectionResponse = readTokenIntrospectionResponse(mvcResult);\n\n\t\tArgumentCaptor<OAuth2TokenClaimsContext> accessTokenClaimsContextCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2TokenClaimsContext.class);\n\t\tverify(accessTokenCustomizer).customize(accessTokenClaimsContextCaptor.capture());\n\n\t\tOAuth2TokenClaimsContext accessTokenClaimsContext = accessTokenClaimsContextCaptor.getValue();\n\t\tOAuth2TokenClaimsSet accessTokenClaims = accessTokenClaimsContext.getClaims().build();\n\n\t\tassertThat(tokenIntrospectionResponse.isActive()).isTrue();\n\t\tassertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(authorizedRegisteredClient.getClientId());\n\t\tassertThat(tokenIntrospectionResponse.getUsername()).isNull();\n\t\tassertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween(accessTokenClaims.getIssuedAt().minusSeconds(1),\n\t\t\t\taccessTokenClaims.getIssuedAt().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween(\n\t\t\t\taccessTokenClaims.getExpiresAt().minusSeconds(1), accessTokenClaims.getExpiresAt().plusSeconds(1));\n\t\tList<String> scopes = new ArrayList<>(accessTokenClaims.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tassertThat(tokenIntrospectionResponse.getScopes()).containsExactlyInAnyOrderElementsOf(scopes);\n\t\tassertThat(tokenIntrospectionResponse.getTokenType()).isEqualTo(accessToken.getTokenType().getValue());\n\t\tassertThat(tokenIntrospectionResponse.getNotBefore()).isBetween(\n\t\t\t\taccessTokenClaims.getNotBefore().minusSeconds(1), accessTokenClaims.getNotBefore().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getSubject()).isEqualTo(accessTokenClaims.getSubject());\n\t\tassertThat(tokenIntrospectionResponse.getAudience())\n\t\t\t.containsExactlyInAnyOrderElementsOf(accessTokenClaims.getAudience());\n\t\tassertThat(tokenIntrospectionResponse.getIssuer()).isEqualTo(accessTokenClaims.getIssuer());\n\t\tassertThat(tokenIntrospectionResponse.getId()).isEqualTo(accessTokenClaims.getId());\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenIntrospectionEndpointCustomizedThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomTokenIntrospectionEndpoint.class).autowire();\n\n\t\tRegisteredClient introspectRegisteredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(introspectRegisteredClient);\n\n\t\tRegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(authorizedRegisteredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(authorizedRegisteredClient).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(introspectRegisteredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, introspectRegisteredClient.getClientSecret());\n\t\tOAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, null, null);\n\n\t\tgiven(authenticationConverter.convert(any())).willReturn(tokenIntrospectionAuthentication);\n\t\tgiven(authenticationProvider.supports(eq(OAuth2TokenIntrospectionAuthenticationToken.class))).willReturn(true);\n\t\tgiven(authenticationProvider.authenticate(any())).willReturn(tokenIntrospectionAuthentication);\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(this.authorizationServerSettings.getTokenIntrospectionEndpoint())\n\t\t\t\t.params(getTokenIntrospectionRequestParameters(accessToken, OAuth2TokenType.ACCESS_TOKEN))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(introspectRegisteredClient)))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\n\t\tverify(authenticationConverter).convert(any());\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());\n\t\tList<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();\n\t\tassertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter\n\t\t\t\t|| converter instanceof OAuth2TokenIntrospectionAuthenticationConverter);\n\n\t\tverify(authenticationProvider).authenticate(eq(tokenIntrospectionAuthentication));\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());\n\t\tList<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();\n\t\tassertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider\n\t\t\t\t|| provider instanceof OAuth2TokenIntrospectionAuthenticationProvider);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(),\n\t\t\t\teq(tokenIntrospectionAuthentication));\n\t}\n\n\t@Test\n\tpublic void requestWhenIntrospectionRequestIncludesIssuerPathThenActive() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomTokenIntrospectionEndpoint.class).autowire();\n\n\t\tRegisteredClient introspectRegisteredClient = TestRegisteredClients.registeredClient2().build();\n\t\tthis.registeredClientRepository.save(introspectRegisteredClient);\n\n\t\tRegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(authorizedRegisteredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(authorizedRegisteredClient).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(introspectRegisteredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, introspectRegisteredClient.getClientSecret());\n\t\tOAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, null, null);\n\n\t\tgiven(authenticationConverter.convert(any())).willReturn(tokenIntrospectionAuthentication);\n\t\tgiven(authenticationProvider.supports(eq(OAuth2TokenIntrospectionAuthenticationToken.class))).willReturn(true);\n\t\tgiven(authenticationProvider.authenticate(any())).willReturn(tokenIntrospectionAuthentication);\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(issuer.concat(this.authorizationServerSettings.getTokenIntrospectionEndpoint()))\n\t\t\t\t\t\t.params(getTokenIntrospectionRequestParameters(accessToken, OAuth2TokenType.ACCESS_TOKEN))\n\t\t\t\t\t\t.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(introspectRegisteredClient)))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\tprivate static MultiValueMap<String, String> getTokenIntrospectionRequestParameters(OAuth2Token token,\n\t\t\tOAuth2TokenType tokenType) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.TOKEN, token.getTokenValue());\n\t\tparameters.set(OAuth2ParameterNames.TOKEN_TYPE_HINT, tokenType.getValue());\n\t\treturn parameters;\n\t}\n\n\tprivate static OAuth2TokenIntrospection readTokenIntrospectionResponse(MvcResult mvcResult) throws Exception {\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\treturn tokenIntrospectionHttpResponseConverter.read(OAuth2TokenIntrospection.class, httpResponse);\n\t}\n\n\tprivate static MultiValueMap<String, String> getAuthorizationCodeTokenRequestParameters(\n\t\t\tRegisteredClient registeredClient, OAuth2Authorization authorization) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CODE,\n\t\t\t\tauthorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue());\n\t\tparameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next());\n\t\treturn parameters;\n\t}\n\n\tprivate static OAuth2AccessTokenResponse readAccessTokenResponse(MvcResult mvcResult) throws Exception {\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\treturn accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);\n\t}\n\n\tprivate static String getAuthorizationHeader(RegisteredClient registeredClient) throws Exception {\n\t\tString clientId = registeredClient.getClientId();\n\t\tString clientSecret = registeredClient.getClientSecret();\n\t\tclientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());\n\t\tclientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8.name());\n\t\tString credentialsString = clientId + \":\" + clientSecret;\n\t\tbyte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));\n\t\treturn \"Basic \" + new String(encodedBytes, StandardCharsets.UTF_8);\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationConsentService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tJdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tRegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();\n\t\t\tjdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\t\t\treturn jdbcRegisteredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().tokenIntrospectionEndpoint(\"/test/introspect\").build();\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer() {\n\t\t\treturn accessTokenCustomizer;\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationCustomTokenIntrospectionEndpoint\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.tokenIntrospectionEndpoint((tokenIntrospectionEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\ttokenIntrospectionEndpoint\n\t\t\t\t\t\t\t\t\t\t\t\t\t.introspectionRequestConverter(authenticationConverter)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.introspectionRequestConverters(authenticationConvertersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(authenticationProvider)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(authenticationProvidersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.introspectionResponseHandler(authenticationSuccessHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.errorResponseHandler(authenticationFailureHandler))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Override\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder()\n\t\t\t\t.multipleIssuersAllowed(true)\n\t\t\t\t.tokenIntrospectionEndpoint(\"/test/introspect\")\n\t\t\t\t.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2TokenRevocationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenRevocationAuthenticationConverter;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the OAuth 2.0 Token Revocation endpoint.\n *\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2TokenRevocationTests {\n\n\tprivate static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = \"/oauth2/revoke\";\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tprivate static AuthenticationConverter authenticationConverter;\n\n\tprivate static Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer;\n\n\tprivate static AuthenticationProvider authenticationProvider;\n\n\tprivate static Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer;\n\n\tprivate static AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate static AuthenticationFailureHandler authenticationFailureHandler;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tauthenticationConverter = mock(AuthenticationConverter.class);\n\t\tauthenticationConvertersConsumer = mock(Consumer.class);\n\t\tauthenticationProvider = mock(AuthenticationProvider.class);\n\t\tauthenticationProvidersConsumer = mock(Consumer.class);\n\t\tauthenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tauthenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenRevokeRefreshTokenThenRevoked() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tOAuth2RefreshToken token = authorization.getRefreshToken().getToken();\n\t\tOAuth2TokenType tokenType = OAuth2TokenType.REFRESH_TOKEN;\n\t\tthis.authorizationService.save(authorization);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRevocationRequestParameters(token, tokenType))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk());\n\n\t\tOAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());\n\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = updatedAuthorization.getRefreshToken();\n\t\tassertThat(refreshToken.isInvalidated()).isTrue();\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();\n\t\tassertThat(accessToken.isInvalidated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void requestWhenRevokeAccessTokenThenRevoked() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tOAuth2AccessToken token = authorization.getAccessToken().getToken();\n\t\tOAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;\n\t\tthis.authorizationService.save(authorization);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRevocationRequestParameters(token, tokenType))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk());\n\n\t\tOAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();\n\t\tassertThat(accessToken.isInvalidated()).isTrue();\n\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = updatedAuthorization.getRefreshToken();\n\t\tassertThat(refreshToken.isInvalidated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void requestWhenRevokeAccessTokenAndRequestIncludesIssuerPathThenRevoked() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tOAuth2AccessToken token = authorization.getAccessToken().getToken();\n\t\tOAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;\n\t\tthis.authorizationService.save(authorization);\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(issuer.concat(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI))\n\t\t\t\t\t\t.params(getTokenRevocationRequestParameters(token, tokenType))\n\t\t\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Basic \" + encodeBasicAuth(\n\t\t\t\t\t\t\t\tregisteredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\n\t\tOAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();\n\t\tassertThat(accessToken.isInvalidated()).isTrue();\n\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = updatedAuthorization.getRefreshToken();\n\t\tassertThat(refreshToken.isInvalidated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void requestWhenTokenRevocationEndpointCustomizedThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationCustomTokenRevocationEndpoint.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tOAuth2AccessToken token = authorization.getAccessToken().getToken();\n\t\tOAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN;\n\t\tthis.authorizationService.save(authorization);\n\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\ttoken, clientPrincipal);\n\n\t\tgiven(authenticationConverter.convert(any())).willReturn(tokenRevocationAuthentication);\n\t\tgiven(authenticationProvider.supports(eq(OAuth2TokenRevocationAuthenticationToken.class))).willReturn(true);\n\t\tgiven(authenticationProvider.authenticate(any())).willReturn(tokenRevocationAuthentication);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRevocationRequestParameters(token, tokenType))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk());\n\n\t\tverify(authenticationConverter).convert(any());\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());\n\t\tList<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();\n\t\tassertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter\n\t\t\t\t|| converter instanceof OAuth2TokenRevocationAuthenticationConverter);\n\n\t\tverify(authenticationProvider).authenticate(eq(tokenRevocationAuthentication));\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());\n\t\tList<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();\n\t\tassertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider\n\t\t\t\t|| provider instanceof OAuth2TokenRevocationAuthenticationProvider);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(tokenRevocationAuthentication));\n\t}\n\n\tprivate static MultiValueMap<String, String> getTokenRevocationRequestParameters(OAuth2Token token,\n\t\t\tOAuth2TokenType tokenType) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.TOKEN, token.getTokenValue());\n\t\tparameters.set(OAuth2ParameterNames.TOKEN_TYPE_HINT, tokenType.getValue());\n\t\treturn parameters;\n\t}\n\n\tprivate static String encodeBasicAuth(String clientId, String secret) throws Exception {\n\t\tclientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());\n\t\tsecret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());\n\t\tString credentialsString = clientId + \":\" + secret;\n\t\tbyte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));\n\t\treturn new String(encodedBytes, StandardCharsets.UTF_8);\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tJdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tRegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();\n\t\t\tjdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\t\t\treturn jdbcRegisteredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationCustomTokenRevocationEndpoint\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.tokenRevocationEndpoint((tokenRevocationEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\ttokenRevocationEndpoint\n\t\t\t\t\t\t\t\t\t\t\t\t\t.revocationRequestConverter(authenticationConverter)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.revocationRequestConverters(authenticationConvertersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(authenticationProvider)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(authenticationProvidersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.revocationResponseHandler(authenticationSuccessHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.errorResponseHandler(authenticationFailureHandler))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Import(OAuth2AuthorizationServerConfiguration.class)\n\tstatic class AuthorizationServerConfigurationWithMultipleIssuersAllowed extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcClientRegistrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport javax.crypto.spec.SecretKeySpec;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport jakarta.servlet.http.HttpServletResponse;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.assertj.core.data.TemporalUnitWithinOffset;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientConfigurationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.converter.OidcClientRegistrationRegisteredClientConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.converter.RegisteredClientOidcClientRegistrationConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcClientRegistrationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.authentication.OidcClientRegistrationAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for OpenID Connect Dynamic Client Registration 1.0.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @author Dmitriy Dubson\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OidcClientRegistrationTests {\n\n\tprivate static final String ISSUER = \"https://example.com:8443/issuer1\";\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static final String DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI = \"/connect/register\";\n\n\tprivate static final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tprivate static final HttpMessageConverter<OidcClientRegistration> clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter();\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tprivate static JWKSet clientJwkSet;\n\n\tprivate static JwtEncoder jwtClientAssertionEncoder;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@Autowired\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\tprivate static AuthenticationConverter authenticationConverter;\n\n\tprivate static Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer;\n\n\tprivate static AuthenticationProvider authenticationProvider;\n\n\tprivate static Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer;\n\n\tprivate static AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate static AuthenticationFailureHandler authenticationFailureHandler;\n\n\tprivate MockWebServer server;\n\n\tprivate String clientJwkSetUrl;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tclientJwkSet = new JWKSet(TestJwks.generateRsa().build());\n\t\tjwtClientAssertionEncoder = new NimbusJwtEncoder(\n\t\t\t\t(jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet));\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t\tauthenticationConverter = mock(AuthenticationConverter.class);\n\t\tauthenticationConvertersConsumer = mock(Consumer.class);\n\t\tauthenticationProvider = mock(AuthenticationProvider.class);\n\t\tauthenticationProvidersConsumer = mock(Consumer.class);\n\t\tauthenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tauthenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t}\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.clientJwkSetUrl = this.server.url(\"/jwks\").toString();\n\t\t// @formatter:off\n\t\tMockResponse response = new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setBody(clientJwkSet.toString());\n\t\t// @formatter:on\n\t\tthis.server.enqueue(response);\n\t\tgiven(authenticationProvider.supports(OidcClientRegistrationAuthenticationToken.class)).willReturn(true);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() throws Exception {\n\t\tthis.server.shutdown();\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t\treset(authenticationConverter);\n\t\treset(authenticationConvertersConsumer);\n\t\treset(authenticationProvider);\n\t\treset(authenticationProvidersConsumer);\n\t\treset(authenticationSuccessHandler);\n\t\treset(authenticationFailureHandler);\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistrationRequestAuthorizedThenClientRegistrationResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tassertThat(clientRegistrationResponse.getClientId()).isNotNull();\n\t\tassertThat(clientRegistrationResponse.getClientIdIssuedAt()).isNotNull();\n\t\tassertThat(clientRegistrationResponse.getClientSecret()).isNotNull();\n\t\tassertThat(clientRegistrationResponse.getClientSecretExpiresAt()).isNull();\n\t\tassertThat(clientRegistrationResponse.getClientName()).isEqualTo(clientRegistration.getClientName());\n\t\tassertThat(clientRegistrationResponse.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistration.getRedirectUris());\n\t\tassertThat(clientRegistrationResponse.getGrantTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistration.getGrantTypes());\n\t\tassertThat(clientRegistrationResponse.getResponseTypes())\n\t\t\t.containsExactly(OAuth2AuthorizationResponseType.CODE.getValue());\n\t\tassertThat(clientRegistrationResponse.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistration.getScopes());\n\t\tassertThat(clientRegistrationResponse.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm())\n\t\t\t.isEqualTo(SignatureAlgorithm.RS256.getName());\n\t\tassertThat(clientRegistrationResponse.getRegistrationClientUrl()).isNotNull();\n\t\tassertThat(clientRegistrationResponse.getRegistrationAccessToken()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void requestWhenClientConfigurationRequestAuthorizedThenClientRegistrationResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tHttpHeaders httpHeaders = new HttpHeaders();\n\t\thttpHeaders.setBearerAuth(clientRegistrationResponse.getRegistrationAccessToken());\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(clientRegistrationResponse.getRegistrationClientUrl().toURI()).headers(httpHeaders))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andReturn();\n\n\t\tOidcClientRegistration clientConfigurationResponse = readClientRegistrationResponse(mvcResult.getResponse());\n\n\t\tassertThat(clientConfigurationResponse.getClientId()).isEqualTo(clientRegistrationResponse.getClientId());\n\t\tassertThat(clientConfigurationResponse.getClientIdIssuedAt())\n\t\t\t.isEqualTo(clientRegistrationResponse.getClientIdIssuedAt());\n\t\tassertThat(clientConfigurationResponse.getClientSecret()).isNotNull();\n\t\tassertThat(clientConfigurationResponse.getClientSecretExpiresAt())\n\t\t\t.isEqualTo(clientRegistrationResponse.getClientSecretExpiresAt());\n\t\tassertThat(clientConfigurationResponse.getClientName()).isEqualTo(clientRegistrationResponse.getClientName());\n\t\tassertThat(clientConfigurationResponse.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getRedirectUris());\n\t\tassertThat(clientConfigurationResponse.getGrantTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getGrantTypes());\n\t\tassertThat(clientConfigurationResponse.getResponseTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getResponseTypes());\n\t\tassertThat(clientConfigurationResponse.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getScopes());\n\t\tassertThat(clientConfigurationResponse.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(clientRegistrationResponse.getTokenEndpointAuthenticationMethod());\n\t\tassertThat(clientConfigurationResponse.getIdTokenSignedResponseAlgorithm())\n\t\t\t.isEqualTo(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm());\n\t\tassertThat(clientConfigurationResponse.getRegistrationClientUrl())\n\t\t\t.isEqualTo(clientRegistrationResponse.getRegistrationClientUrl());\n\t\tassertThat(clientConfigurationResponse.getRegistrationAccessToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistrationEndpointCustomizedThenUsed() throws Exception {\n\t\tthis.spring.register(CustomClientRegistrationConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\twillAnswer((invocation) -> {\n\t\t\tHttpServletResponse response = invocation.getArgument(1, HttpServletResponse.class);\n\t\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\t\thttpResponse.setStatusCode(HttpStatus.CREATED);\n\t\t\tnew OidcClientRegistrationHttpMessageConverter().write(clientRegistration, null, httpResponse);\n\t\t\treturn null;\n\t\t}).given(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\n\t\tregisterClient(clientRegistration);\n\n\t\tverify(authenticationConverter).convert(any());\n\t\tArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());\n\t\tList<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();\n\t\tassertThat(authenticationConverters).hasSize(2)\n\t\t\t.allMatch((converter) -> converter == authenticationConverter\n\t\t\t\t\t|| converter instanceof OidcClientRegistrationAuthenticationConverter);\n\n\t\tverify(authenticationProvider).authenticate(any());\n\t\tArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());\n\t\tList<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();\n\t\tassertThat(authenticationProviders).hasSize(3)\n\t\t\t.allMatch((provider) -> provider == authenticationProvider\n\t\t\t\t\t|| provider instanceof OidcClientRegistrationAuthenticationProvider\n\t\t\t\t\t|| provider instanceof OidcClientConfigurationAuthenticationProvider);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\t\tverifyNoInteractions(authenticationFailureHandler);\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistrationEndpointCustomizedWithAuthenticationFailureHandlerThenUsed()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CustomClientRegistrationConfiguration.class).autowire();\n\n\t\tgiven(authenticationProvider.authenticate(any())).willThrow(new OAuth2AuthenticationException(\"error\"));\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI))\n\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, \"invalid\")\n\t\t\t.with(jwt()));\n\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());\n\t\tverifyNoInteractions(authenticationSuccessHandler);\n\t}\n\n\t// gh-1056\n\t@Test\n\tpublic void requestWhenClientRegistersWithSecretThenClientAuthenticationSuccess() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tthis.mvc\n\t\t\t.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1\")\n\t\t\t\t.with(httpBasic(clientRegistrationResponse.getClientId(),\n\t\t\t\t\t\tclientRegistrationResponse.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1\"))\n\t\t\t.andReturn();\n\t}\n\n\t// gh-1344\n\t@Test\n\tpublic void requestWhenClientRegistersWithClientSecretJwtThenClientAuthenticationSuccess() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256).build();\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\t\tJwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder()\n\t\t\t.issuer(clientRegistrationResponse.getClientId())\n\t\t\t.subject(clientRegistrationResponse.getClientId())\n\t\t\t.audience(Collections.singletonList(asUrl(ISSUER, this.authorizationServerSettings.getTokenEndpoint())))\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt)\n\t\t\t.build();\n\n\t\tJWKSet jwkSet = new JWKSet(\n\t\t\t\tTestJwks.jwk(new SecretKeySpec(clientRegistrationResponse.getClientSecret().getBytes(), \"HS256\"))\n\t\t\t\t\t.build());\n\t\tJwtEncoder jwtClientAssertionEncoder = new NimbusJwtEncoder(\n\t\t\t\t(jwkSelector, securityContext) -> jwkSelector.select(jwkSet));\n\n\t\tJwt jwtAssertion = jwtClientAssertionEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tthis.mvc\n\t\t\t.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, \"scope1\")\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE,\n\t\t\t\t\t\t\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\")\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ASSERTION, jwtAssertion.getTokenValue())\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, clientRegistrationResponse.getClientId()))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(\"scope1\"));\n\t}\n\n\t@Test\n\tpublic void requestWhenClientRegistersWithCustomMetadataThenSavedToRegisteredClient() throws Exception {\n\t\tthis.spring.register(CustomClientMetadataConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.claim(\"custom-metadata-name-1\", \"value-1\")\n\t\t\t\t.claim(\"custom-metadata-name-2\", \"value-2\")\n\t\t\t\t.claim(\"non-registered-custom-metadata\", \"value-3\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(clientRegistrationResponse.getClientId());\n\n\t\tassertThat(clientRegistrationResponse.<String>getClaim(\"custom-metadata-name-1\")).isEqualTo(\"value-1\");\n\t\tassertThat(clientRegistrationResponse.<String>getClaim(\"custom-metadata-name-2\")).isEqualTo(\"value-2\");\n\t\tassertThat(clientRegistrationResponse.<String>getClaim(\"non-registered-custom-metadata\")).isNull();\n\n\t\tassertThat(registeredClient.getClientSettings().<String>getSetting(\"custom-metadata-name-1\"))\n\t\t\t.isEqualTo(\"value-1\");\n\t\tassertThat(registeredClient.getClientSettings().<String>getSetting(\"custom-metadata-name-2\"))\n\t\t\t.isEqualTo(\"value-2\");\n\t\tassertThat(registeredClient.getClientSettings().<String>getSetting(\"non-registered-custom-metadata\")).isNull();\n\t}\n\n\t// gh-2111\n\t@Test\n\tpublic void requestWhenClientRegistersWithSecretExpirationThenClientRegistrationResponse() throws Exception {\n\t\tthis.spring.register(ClientSecretExpirationConfiguration.class).autowire();\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);\n\n\t\tInstant expectedSecretExpiryDate = Instant.now().plus(Duration.ofHours(24));\n\t\tTemporalUnitWithinOffset allowedDelta = new TemporalUnitWithinOffset(1, ChronoUnit.MINUTES);\n\n\t\t// Returned response contains expiration date\n\t\tassertThat(clientRegistrationResponse.getClientSecretExpiresAt()).isNotNull()\n\t\t\t.isCloseTo(expectedSecretExpiryDate, allowedDelta);\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(clientRegistrationResponse.getClientId());\n\n\t\t// Persisted RegisteredClient contains expiration date\n\t\tassertThat(registeredClient).isNotNull();\n\t\tassertThat(registeredClient.getClientSecretExpiresAt()).isNotNull()\n\t\t\t.isCloseTo(expectedSecretExpiryDate, allowedDelta);\n\t}\n\n\tprivate OidcClientRegistration registerClient(OidcClientRegistration clientRegistration) throws Exception {\n\t\t// ***** (1) Obtain the \"initial\" access token used for registering the client\n\n\t\tString clientRegistrationScope = \"client.create\";\n\t\t// @formatter:off\n\t\tRegisteredClient clientRegistrar = RegisteredClient.withId(\"client-registrar-1\")\n\t\t\t\t.clientId(\"client-registrar-1\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.scope(clientRegistrationScope)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.jwkSetUrl(this.clientJwkSetUrl)\n\t\t\t\t\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.registeredClientRepository.save(clientRegistrar);\n\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = jwtClientAssertionClaims(clientRegistrar)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwtAssertion = jwtClientAssertionEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE,\n\t\t\t\t\t\t\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\")\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ASSERTION, jwtAssertion.getTokenValue())\n\t\t\t\t.param(OAuth2ParameterNames.CLIENT_ID, clientRegistrar.getClientId()))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").value(clientRegistrationScope))\n\t\t\t.andReturn();\n\n\t\tOAuth2AccessToken accessToken = readAccessTokenResponse(mvcResult.getResponse()).getAccessToken();\n\n\t\t// ***** (2) Register the client\n\n\t\tHttpHeaders httpHeaders = new HttpHeaders();\n\t\thttpHeaders.setBearerAuth(accessToken.getTokenValue());\n\n\t\t// Register the client\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(ISSUER.concat(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)).headers(httpHeaders)\n\t\t\t\t.contentType(MediaType.APPLICATION_JSON)\n\t\t\t\t.content(getClientRegistrationRequestContent(clientRegistration)))\n\t\t\t.andExpect(status().isCreated())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andReturn();\n\n\t\treturn readClientRegistrationResponse(mvcResult.getResponse());\n\t}\n\n\tprivate JwtClaimsSet.Builder jwtClientAssertionClaims(RegisteredClient registeredClient) {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\t\treturn JwtClaimsSet.builder()\n\t\t\t.issuer(registeredClient.getClientId())\n\t\t\t.subject(registeredClient.getClientId())\n\t\t\t.audience(Collections.singletonList(asUrl(ISSUER, this.authorizationServerSettings.getTokenEndpoint())))\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt);\n\t}\n\n\tprivate static String asUrl(String uri, String path) {\n\t\treturn UriComponentsBuilder.fromUriString(uri).path(path).build().toUriString();\n\t}\n\n\tprivate static OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response)\n\t\t\tthrows Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);\n\t}\n\n\tprivate static byte[] getClientRegistrationRequestContent(OidcClientRegistration clientRegistration)\n\t\t\tthrows Exception {\n\t\tMockHttpOutputMessage httpRequest = new MockHttpOutputMessage();\n\t\tclientRegistrationHttpMessageConverter.write(clientRegistration, null, httpRequest);\n\t\treturn httpRequest.getBodyAsBytes();\n\t}\n\n\tprivate static OidcClientRegistration readClientRegistrationResponse(MockHttpServletResponse response)\n\t\t\tthrows Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class, httpResponse);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class CustomClientRegistrationConfiguration extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\t@Override\n\t\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc((oidc) ->\n\t\t\t\t\t\t\t\t\t\t\toidc\n\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint((clientRegistration) ->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclientRegistration\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationRequestConverter(authenticationConverter)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationRequestConverters(authenticationConvertersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(authenticationProvider)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(authenticationProvidersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationResponseHandler(authenticationSuccessHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.errorResponseHandler(authenticationFailureHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class CustomClientMetadataConfiguration extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\t@Override\n\t\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc((oidc) ->\n\t\t\t\t\t\t\t\t\t\t\toidc\n\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint((clientRegistration) ->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclientRegistration\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(configureClientRegistrationConverters())\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\tprivate Consumer<List<AuthenticationProvider>> configureClientRegistrationConverters() {\n\t\t\t// @formatter:off\n\t\t\treturn (authenticationProviders) ->\n\t\t\t\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\t\t\t\tList<String> supportedCustomClientMetadata = List.of(\"custom-metadata-name-1\", \"custom-metadata-name-2\");\n\t\t\t\t\t\tif (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider provider) {\n\t\t\t\t\t\t\tprovider.setRegisteredClientConverter(new CustomRegisteredClientConverter(supportedCustomClientMetadata));\n\t\t\t\t\t\t\tprovider.setClientRegistrationConverter(new CustomClientRegistrationConverter(supportedCustomClientMetadata));\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class ClientSecretExpirationConfiguration extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\t@Override\n\t\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc((oidc) ->\n\t\t\t\t\t\t\t\t\t\t\toidc\n\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint((clientRegistration) ->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclientRegistration\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(configureClientRegistrationConverters())\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\tprivate Consumer<List<AuthenticationProvider>> configureClientRegistrationConverters() {\n\t\t\t// @formatter:off\n\t\t\treturn (authenticationProviders) ->\n\t\t\t\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\t\t\t\tif (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider provider) {\n\t\t\t\t\t\t\tprovider.setRegisteredClientConverter(new ClientSecretExpirationRegisteredClientConverter());\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc((oidc) ->\n\t\t\t\t\t\t\t\t\t\t\toidc\n\t\t\t\t\t\t\t\t\t\t\t\t\t.clientRegistrationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t\tRegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();\n\t\t\tJdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tregisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\t\t\tregisteredClientRepository.save(registeredClient);\n\t\t\treturn registeredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t\t}\n\n\t}\n\n\tprivate static final class CustomRegisteredClientConverter\n\t\t\timplements Converter<OidcClientRegistration, RegisteredClient> {\n\n\t\tprivate final OidcClientRegistrationRegisteredClientConverter delegate = new OidcClientRegistrationRegisteredClientConverter();\n\n\t\tprivate final List<String> supportedCustomClientMetadata;\n\n\t\tprivate CustomRegisteredClientConverter(List<String> supportedCustomClientMetadata) {\n\t\t\tthis.supportedCustomClientMetadata = supportedCustomClientMetadata;\n\t\t}\n\n\t\t@Override\n\t\tpublic RegisteredClient convert(OidcClientRegistration clientRegistration) {\n\t\t\tRegisteredClient registeredClient = this.delegate.convert(clientRegistration);\n\n\t\t\tClientSettings.Builder clientSettingsBuilder = ClientSettings\n\t\t\t\t.withSettings(registeredClient.getClientSettings().getSettings());\n\t\t\tif (!CollectionUtils.isEmpty(this.supportedCustomClientMetadata)) {\n\t\t\t\tclientRegistration.getClaims().forEach((claim, value) -> {\n\t\t\t\t\tif (this.supportedCustomClientMetadata.contains(claim)) {\n\t\t\t\t\t\tclientSettingsBuilder.setting(claim, value);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn RegisteredClient.from(registeredClient).clientSettings(clientSettingsBuilder.build()).build();\n\t\t}\n\n\t}\n\n\tprivate static final class CustomClientRegistrationConverter\n\t\t\timplements Converter<RegisteredClient, OidcClientRegistration> {\n\n\t\tprivate final RegisteredClientOidcClientRegistrationConverter delegate = new RegisteredClientOidcClientRegistrationConverter();\n\n\t\tprivate final List<String> supportedCustomClientMetadata;\n\n\t\tprivate CustomClientRegistrationConverter(List<String> supportedCustomClientMetadata) {\n\t\t\tthis.supportedCustomClientMetadata = supportedCustomClientMetadata;\n\t\t}\n\n\t\t@Override\n\t\tpublic OidcClientRegistration convert(RegisteredClient registeredClient) {\n\t\t\tOidcClientRegistration clientRegistration = this.delegate.convert(registeredClient);\n\n\t\t\tMap<String, Object> clientMetadata = new HashMap<>(clientRegistration.getClaims());\n\t\t\tif (!CollectionUtils.isEmpty(this.supportedCustomClientMetadata)) {\n\t\t\t\tMap<String, Object> clientSettings = registeredClient.getClientSettings().getSettings();\n\t\t\t\tthis.supportedCustomClientMetadata.forEach((customClaim) -> {\n\t\t\t\t\tif (clientSettings.containsKey(customClaim)) {\n\t\t\t\t\t\tclientMetadata.put(customClaim, clientSettings.get(customClaim));\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn OidcClientRegistration.withClaims(clientMetadata).build();\n\t\t}\n\n\t}\n\n\t/**\n\t * This customization adds client secret expiration time by setting\n\t * {@code RegisteredClient.clientSecretExpiresAt} during\n\t * {@code OidcClientRegistration} -> {@code RegisteredClient} conversion\n\t */\n\tprivate static final class ClientSecretExpirationRegisteredClientConverter\n\t\t\timplements Converter<OidcClientRegistration, RegisteredClient> {\n\n\t\tprivate static final OidcClientRegistrationRegisteredClientConverter delegate = new OidcClientRegistrationRegisteredClientConverter();\n\n\t\t@Override\n\t\tpublic RegisteredClient convert(OidcClientRegistration clientRegistration) {\n\t\t\tRegisteredClient registeredClient = delegate.convert(clientRegistration);\n\t\t\tRegisteredClient.Builder registeredClientBuilder = RegisteredClient.from(registeredClient);\n\n\t\t\tInstant clientSecretExpiresAt = Instant.now().plus(Duration.ofHours(24));\n\t\t\tregisteredClientBuilder.clientSecretExpiresAt(clientSecretExpiresAt);\n\n\t\t\treturn registeredClientBuilder.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcProviderConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.ImmutableJWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames;\nimport org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.hamcrest.CoreMatchers.hasItems;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the OpenID Connect 1.0 Provider Configuration endpoint.\n *\n * @author Sahariar Alam Khandoker\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OidcProviderConfigurationTests {\n\n\tprivate static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = \"/.well-known/openid-configuration\";\n\n\tprivate static final String ISSUER = \"https://example.com\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenConfigurationRequestAndIssuerSetThenReturnDefaultConfigurationResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpectAll(defaultConfigurationMatchers(ISSUER));\n\t}\n\n\t@Test\n\tpublic void requestWhenConfigurationRequestIncludesIssuerPathThenConfigurationResponseHasIssuerPath()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithMultipleIssuersAllowed.class).autowire();\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\t\tthis.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpectAll(defaultConfigurationMatchers(issuer));\n\n\t\tissuer = \"https://example.com:8443/path1/issuer2\";\n\t\tthis.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpectAll(defaultConfigurationMatchers(issuer));\n\n\t\tissuer = \"https://example.com:8443/path1/path2/issuer3\";\n\t\tthis.mvc.perform(get(issuer.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpectAll(defaultConfigurationMatchers(issuer));\n\t}\n\n\t// gh-632\n\t@Test\n\tpublic void requestWhenConfigurationRequestAndUserAuthenticatedThenReturnConfigurationResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)).with(user(\"user\")))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpectAll(defaultConfigurationMatchers(ISSUER));\n\t}\n\n\t// gh-616\n\t@Test\n\tpublic void requestWhenConfigurationRequestAndConfigurationCustomizerSetThenReturnCustomConfigurationResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithProviderConfigurationCustomizer.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,\n\t\t\t\t\thasItems(OidcScopes.OPENID, OidcScopes.PROFILE, OidcScopes.EMAIL)));\n\t}\n\n\t@Test\n\tpublic void requestWhenConfigurationRequestAndClientRegistrationEnabledThenConfigurationResponseIncludesRegistrationEndpoint()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithClientRegistrationEnabled.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpectAll(defaultConfigurationMatchers(ISSUER))\n\t\t\t.andExpect(jsonPath(\"$.registration_endpoint\")\n\t\t\t\t.value(ISSUER.concat(this.authorizationServerSettings.getOidcClientRegistrationEndpoint())));\n\t}\n\n\t@Test\n\tpublic void requestWhenConfigurationRequestAndDeviceCodeGrantEnabledThenConfigurationResponseIncludesDeviceAuthorizationEndpoint()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithDeviceCodeGrantEnabled.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpectAll(defaultConfigurationMatchers(ISSUER))\n\t\t\t.andExpect(jsonPath(\"$.device_authorization_endpoint\")\n\t\t\t\t.value(ISSUER.concat(this.authorizationServerSettings.getDeviceAuthorizationEndpoint())))\n\t\t\t.andExpect(jsonPath(\"$.grant_types_supported[4]\").value(AuthorizationGrantType.DEVICE_CODE.getValue()));\n\t}\n\n\t@Test\n\tpublic void requestWhenConfigurationRequestAndPushedAuthorizationRequestEnabledThenConfigurationResponseIncludesPushedAuthorizationRequestEndpoint()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithPushedAuthorizationRequestEnabled.class).autowire();\n\n\t\tthis.mvc.perform(get(ISSUER.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpectAll(defaultConfigurationMatchers(ISSUER))\n\t\t\t.andExpect(jsonPath(\"$.pushed_authorization_request_endpoint\")\n\t\t\t\t.value(ISSUER.concat(this.authorizationServerSettings.getPushedAuthorizationRequestEndpoint())));\n\t}\n\n\tprivate ResultMatcher[] defaultConfigurationMatchers(String issuer) {\n\t\t// @formatter:off\n\t\treturn new ResultMatcher[] {\n\t\t\t\tjsonPath(\"issuer\").value(issuer),\n\t\t\t\tjsonPath(\"authorization_endpoint\").value(issuer.concat(this.authorizationServerSettings.getAuthorizationEndpoint())),\n\t\t\t\tjsonPath(\"token_endpoint\").value(issuer.concat(this.authorizationServerSettings.getTokenEndpoint())),\n\t\t\t\tjsonPath(\"$.token_endpoint_auth_methods_supported[0]\").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),\n\t\t\t\tjsonPath(\"$.token_endpoint_auth_methods_supported[1]\").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),\n\t\t\t\tjsonPath(\"$.token_endpoint_auth_methods_supported[2]\").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),\n\t\t\t\tjsonPath(\"$.token_endpoint_auth_methods_supported[3]\").value(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),\n\t\t\t\tjsonPath(\"jwks_uri\").value(issuer.concat(this.authorizationServerSettings.getJwkSetEndpoint())),\n\t\t\t\tjsonPath(\"userinfo_endpoint\").value(issuer.concat(this.authorizationServerSettings.getOidcUserInfoEndpoint())),\n\t\t\t\tjsonPath(\"end_session_endpoint\").value(issuer.concat(this.authorizationServerSettings.getOidcLogoutEndpoint())),\n\t\t\t\tjsonPath(\"response_types_supported\").value(OAuth2AuthorizationResponseType.CODE.getValue()),\n\t\t\t\tjsonPath(\"$.grant_types_supported[0]\").value(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tjsonPath(\"$.grant_types_supported[1]\").value(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()),\n\t\t\t\tjsonPath(\"$.grant_types_supported[2]\").value(AuthorizationGrantType.REFRESH_TOKEN.getValue()),\n\t\t\t\tjsonPath(\"$.grant_types_supported[3]\").value(AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tjsonPath(\"revocation_endpoint\").value(issuer.concat(this.authorizationServerSettings.getTokenRevocationEndpoint())),\n\t\t\t\tjsonPath(\"$.revocation_endpoint_auth_methods_supported[0]\").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),\n\t\t\t\tjsonPath(\"$.revocation_endpoint_auth_methods_supported[1]\").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),\n\t\t\t\tjsonPath(\"$.revocation_endpoint_auth_methods_supported[2]\").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),\n\t\t\t\tjsonPath(\"$.revocation_endpoint_auth_methods_supported[3]\").value(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),\n\t\t\t\tjsonPath(\"introspection_endpoint\").value(issuer.concat(this.authorizationServerSettings.getTokenIntrospectionEndpoint())),\n\t\t\t\tjsonPath(\"$.introspection_endpoint_auth_methods_supported[0]\").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),\n\t\t\t\tjsonPath(\"$.introspection_endpoint_auth_methods_supported[1]\").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),\n\t\t\t\tjsonPath(\"$.introspection_endpoint_auth_methods_supported[2]\").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),\n\t\t\t\tjsonPath(\"$.introspection_endpoint_auth_methods_supported[3]\").value(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),\n\t\t\t\tjsonPath(\"$.code_challenge_methods_supported[0]\").value(\"S256\"),\n\t\t\t\tjsonPath(\"subject_types_supported\").value(\"public\"),\n\t\t\t\tjsonPath(\"id_token_signing_alg_values_supported\").value(SignatureAlgorithm.RS256.getName()),\n\t\t\t\tjsonPath(\"scopes_supported\").value(OidcScopes.OPENID)\n\t\t};\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadContextWhenIssuerNotValidUrlThenThrowException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class).isThrownBy(\n\t\t\t\t() -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUrl.class).autowire());\n\t}\n\n\t@Test\n\tpublic void loadContextWhenIssuerNotValidUriThenThrowException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class).isThrownBy(\n\t\t\t\t() -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUri.class).autowire());\n\t}\n\n\t@Test\n\tpublic void loadContextWhenIssuerWithQueryThenThrowException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(AuthorizationServerConfigurationWithIssuerQuery.class).autowire());\n\t}\n\n\t@Test\n\tpublic void loadContextWhenIssuerWithFragmentThenThrowException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class).isThrownBy(\n\t\t\t\t() -> this.spring.register(AuthorizationServerConfigurationWithIssuerFragment.class).autowire());\n\t}\n\n\t@Test\n\tpublic void loadContextWhenIssuerWithQueryAndFragmentThenThrowException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(AuthorizationServerConfigurationWithIssuerQueryAndFragment.class)\n\t\t\t\t.autowire());\n\t}\n\n\t@Test\n\tpublic void loadContextWhenIssuerWithEmptyQueryThenThrowException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class).isThrownBy(\n\t\t\t\t() -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyQuery.class).autowire());\n\t}\n\n\t@Test\n\tpublic void loadContextWhenIssuerWithEmptyFragmentThenThrowException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class).isThrownBy(\n\t\t\t\t() -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyFragment.class).autowire());\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t.oidc(Customizer.withDefaults())\t// Enable OpenID Connect 1.0\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tRegisteredClientRepository registeredClientRepository() {\n\t\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t\treturn new InMemoryRegisteredClientRepository(registeredClient);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn new ImmutableJWKSet<>(new JWKSet(TestJwks.DEFAULT_RSA_JWK));\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(ISSUER).build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithMultipleIssuersAllowed extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithProviderConfigurationCustomizer\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc((oidc) ->\n\t\t\t\t\t\t\t\t\t\t\toidc.providerConfigurationEndpoint((providerConfigurationEndpoint) ->\n\t\t\t\t\t\t\t\t\t\t\t\t\tproviderConfigurationEndpoint\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.providerConfigurationCustomizer(providerConfigurationCustomizer())))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\tprivate Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer() {\n\t\t\treturn (providerConfiguration) -> providerConfiguration.scope(OidcScopes.PROFILE).scope(OidcScopes.EMAIL);\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithClientRegistrationEnabled\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc((oidc) ->\n\t\t\t\t\t\t\t\t\t\t\toidc.clientRegistrationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithDeviceCodeGrantEnabled extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.deviceAuthorizationEndpoint(Customizer.withDefaults())\n\t\t\t\t\t\t\t\t\t.oidc(Customizer.withDefaults())\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithPushedAuthorizationRequestEnabled\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.pushedAuthorizationRequestEndpoint(Customizer.withDefaults())\n\t\t\t\t\t\t\t\t\t.oidc(Customizer.withDefaults())\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithInvalidIssuerUrl extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(\"urn:example\").build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithInvalidIssuerUri extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(\"https://not a valid uri\").build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithIssuerQuery extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(ISSUER + \"?param=value\").build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithIssuerFragment extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(ISSUER + \"#fragment\").build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithIssuerQueryAndFragment extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(ISSUER + \"?param=value#fragment\").build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithIssuerEmptyQuery extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(ISSUER + \"?\").build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithIssuerEmptyFragment extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().issuer(ISSUER + \"#\").build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.security.Principal;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.lang.Nullable;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.core.session.SessionRegistryImpl;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.JwtGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for OpenID Connect 1.0.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OidcTests {\n\n\tprivate static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = \"/oauth2/authorize\";\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static final String DEFAULT_OIDC_LOGOUT_ENDPOINT_URI = \"/connect/logout\";\n\n\t// See RFC 7636: Appendix B. Example for the S256 code_challenge_method\n\t// https://tools.ietf.org/html/rfc7636#appendix-B\n\tprivate static final String S256_CODE_VERIFIER = \"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\n\tprivate static final String S256_CODE_CHALLENGE = \"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\";\n\n\tprivate static final String AUTHORITIES_CLAIM = \"authorities\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate static EmbeddedDatabase db;\n\n\tprivate static JWKSource<SecurityContext> jwkSource;\n\n\tprivate static HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tprivate static SessionRegistry sessionRegistry;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JdbcOperations jdbcOperations;\n\n\t@Autowired\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\t@Autowired\n\tprivate JwtDecoder jwtDecoder;\n\n\t@Autowired(required = false)\n\tprivate OAuth2TokenGenerator<?> tokenGenerator;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tJWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t\tdb = new EmbeddedDatabaseBuilder().generateUniqueName(true)\n\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t.addScript(\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\")\n\t\t\t.addScript(\n\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\")\n\t\t\t.build();\n\t\tsessionRegistry = spy(new SessionRegistryImpl());\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tif (this.jdbcOperations != null) {\n\t\t\tthis.jdbcOperations.update(\"truncate table oauth2_authorization\");\n\t\t\tthis.jdbcOperations.update(\"truncate table oauth2_registered_client\");\n\t\t}\n\t}\n\n\t@AfterAll\n\tpublic static void destroy() {\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthenticationRequestThenTokenResponseIncludesIdToken() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user\").roles(\"A\", \"B\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tassertThat(redirectedUrl).matches(expectedRedirectUri + \"\\\\?code=.{15,}&state=state\");\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.id_token\").isNotEmpty())\n\t\t\t.andReturn();\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter\n\t\t\t.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tJwt idToken = this.jwtDecoder\n\t\t\t.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN));\n\n\t\t// Assert user authorities was propagated as claim in ID Token\n\t\tList<String> authoritiesClaim = idToken.getClaim(AUTHORITIES_CLAIM);\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\t\tSet<String> userAuthorities = new HashSet<>();\n\t\tfor (GrantedAuthority authority : principal.getAuthorities()) {\n\t\t\tuserAuthorities.add(authority.getAuthority());\n\t\t}\n\t\tassertThat(authoritiesClaim).containsExactlyInAnyOrderElementsOf(userAuthorities);\n\n\t\t// Assert sid claim was added in ID Token\n\t\tassertThat(idToken.<String>getClaim(\"sid\")).isNotNull();\n\t}\n\n\t// gh-1224\n\t@Test\n\tpublic void requestWhenRefreshTokenRequestThenIdTokenContainsSidClaim() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user\").roles(\"A\", \"B\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tassertThat(redirectedUrl).matches(expectedRedirectUri + \"\\\\?code=.{15,}&state=state\");\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn();\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter\n\t\t\t.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tJwt idToken = this.jwtDecoder\n\t\t\t.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN));\n\n\t\tString sidClaim = idToken.getClaim(\"sid\");\n\t\tassertThat(sidClaim).isNotNull();\n\n\t\t// Refresh access token\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue())\n\t\t\t\t.param(OAuth2ParameterNames.REFRESH_TOKEN, accessTokenResponse.getRefreshToken().getTokenValue())\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn();\n\n\t\tservletResponse = mvcResult.getResponse();\n\t\thttpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\taccessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tidToken = this.jwtDecoder\n\t\t\t.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN));\n\n\t\tassertThat(idToken.<String>getClaim(\"sid\")).isEqualTo(sidClaim);\n\t}\n\n\t@Test\n\tpublic void requestWhenLogoutRequestThenLogout() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\n\t\t// Login\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI)).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\n\t\tMockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession();\n\t\tassertThat(session.isNew()).isTrue();\n\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\t// Get ID Token\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(issuer.concat(DEFAULT_TOKEN_ENDPOINT_URI))\n\t\t\t\t.params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn();\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter\n\t\t\t.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tString idToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);\n\n\t\t// Logout\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(issuer.concat(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI)).param(\"id_token_hint\", idToken)\n\t\t\t\t.session(session))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tredirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\n\t\tassertThat(redirectedUrl).matches(\"/\");\n\t\tassertThat(session.isInvalid()).isTrue();\n\t}\n\n\t@Test\n\tpublic void requestWhenLogoutRequestWithOtherUsersIdTokenThenNotLogout() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\t// Login user1\n\t\tRegisteredClient registeredClient1 = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tthis.registeredClientRepository.save(registeredClient1);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient1);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user1\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\n\t\tMockHttpSession user1Session = (MockHttpSession) mvcResult.getRequest().getSession();\n\t\tassertThat(user1Session.isNew()).isTrue();\n\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization user1Authorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient1, user1Authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient1.getClientId(),\n\t\t\t\t\t\t\t\tregisteredClient1.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn();\n\n\t\tMockHttpServletResponse servletResponse = mvcResult.getResponse();\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter\n\t\t\t.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tString user1IdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);\n\n\t\t// Login user2\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient2().scope(OidcScopes.OPENID).build();\n\t\tthis.registeredClientRepository.save(registeredClient2);\n\n\t\tauthorizationRequestParameters = getAuthorizationRequestParameters(registeredClient2);\n\t\tmvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user2\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\n\t\tMockHttpSession user2Session = (MockHttpSession) mvcResult.getRequest().getSession();\n\t\tassertThat(user2Session.isNew()).isTrue();\n\n\t\tredirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tauthorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization user2Authorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)\n\t\t\t\t.params(getTokenRequestParameters(registeredClient2, user2Authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient2.getClientId(),\n\t\t\t\t\t\t\t\tregisteredClient2.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn();\n\n\t\tservletResponse = mvcResult.getResponse();\n\t\thttpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(servletResponse.getStatus()));\n\t\taccessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);\n\n\t\tString user2IdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);\n\n\t\t// Attempt to log out user1 using user2's ID Token\n\t\tmvcResult = this.mvc\n\t\t\t.perform(post(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI).param(\"id_token_hint\", user2IdToken).session(user1Session))\n\t\t\t.andExpect(status().isBadRequest())\n\t\t\t.andExpect(status().reason(\"[invalid_token] OpenID Connect 1.0 Logout Request Parameter: sub\"))\n\t\t\t.andReturn();\n\n\t\tassertThat(user1Session.isInvalid()).isFalse();\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomTokenGeneratorThenUsed() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithTokenGenerator.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tthis.authorizationService.save(authorization);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk());\n\n\t\tverify(this.tokenGenerator, times(3)).generate(any());\n\t}\n\n\t// gh-1422\n\t@Test\n\tpublic void requestWhenAuthenticationRequestWithOfflineAccessScopeThenTokenResponseIncludesRefreshToken()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithCustomRefreshTokenGenerator.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.scope(OidcScopes.OPENID)\n\t\t\t.scope(\"offline_access\")\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tassertThat(redirectedUrl).matches(expectedRedirectUri + \"\\\\?code=.{15,}&state=state\");\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.id_token\").isNotEmpty())\n\t\t\t.andReturn();\n\t}\n\n\t// gh-1422\n\t@Test\n\tpublic void requestWhenAuthenticationRequestWithoutOfflineAccessScopeThenTokenResponseDoesNotIncludeRefreshToken()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithCustomRefreshTokenGenerator.class).autowire();\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\n\t\tMultiValueMap<String, String> authorizationRequestParameters = getAuthorizationRequestParameters(\n\t\t\t\tregisteredClient);\n\t\tMvcResult mvcResult = this.mvc\n\t\t\t.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)\n\t\t\t\t.with(user(\"user\")))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andReturn();\n\t\tString redirectedUrl = mvcResult.getResponse().getRedirectedUrl();\n\t\tString expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tassertThat(redirectedUrl).matches(expectedRedirectUri + \"\\\\?code=.{15,}&state=state\");\n\n\t\tString authorizationCode = extractParameterFromRedirectUri(redirectedUrl, \"code\");\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode,\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\n\t\tthis.mvc\n\t\t\t.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString(\"no-store\")))\n\t\t\t.andExpect(header().string(HttpHeaders.PRAGMA, containsString(\"no-cache\")))\n\t\t\t.andExpect(jsonPath(\"$.access_token\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.token_type\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.expires_in\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.refresh_token\").doesNotExist())\n\t\t\t.andExpect(jsonPath(\"$.scope\").isNotEmpty())\n\t\t\t.andExpect(jsonPath(\"$.id_token\").isNotEmpty())\n\t\t\t.andReturn();\n\t}\n\n\tprivate static OAuth2Authorization createAuthorization(RegisteredClient registeredClient) {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\treturn TestOAuth2Authorizations.authorization(registeredClient, additionalParameters).build();\n\t}\n\n\tprivate static MultiValueMap<String, String> getAuthorizationRequestParameters(RegisteredClient registeredClient) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2AuthorizationResponseType.CODE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tparameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next());\n\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\t\tparameters.set(OAuth2ParameterNames.STATE, \"state\");\n\t\tparameters.set(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\tparameters.set(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\treturn parameters;\n\t}\n\n\tprivate static MultiValueMap<String, String> getTokenRequestParameters(RegisteredClient registeredClient,\n\t\t\tOAuth2Authorization authorization) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\tparameters.set(OAuth2ParameterNames.CODE,\n\t\t\t\tauthorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue());\n\t\tparameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next());\n\t\tparameters.set(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER);\n\t\treturn parameters;\n\t}\n\n\tprivate static String encodeBasicAuth(String clientId, String secret) throws Exception {\n\t\tclientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());\n\t\tsecret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());\n\t\tString credentialsString = clientId + \":\" + secret;\n\t\tbyte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));\n\t\treturn new String(encodedBytes, StandardCharsets.UTF_8);\n\t}\n\n\tprivate String extractParameterFromRedirectUri(String redirectUri, String param)\n\t\t\tthrows UnsupportedEncodingException {\n\t\tString locationHeader = URLDecoder.decode(redirectUri, StandardCharsets.UTF_8.name());\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build();\n\t\treturn uriComponents.getQueryParams().getFirst(param);\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t.oidc(Customizer.withDefaults())\t// Enable OpenID Connect 1.0\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\treturn new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"removal\")\n\t\tRegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tJdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(\n\t\t\t\t\tjdbcOperations);\n\t\t\tRegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper();\n\t\t\tjdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\t\t\treturn jdbcRegisteredClientRepository;\n\t\t}\n\n\t\t@Bean\n\t\tJdbcOperations jdbcOperations() {\n\t\t\treturn new JdbcTemplate(db);\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn jwkSource;\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {\n\t\t\treturn (context) -> {\n\t\t\t\tif (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) {\n\t\t\t\t\tAuthentication principal = context.getPrincipal();\n\t\t\t\t\tSet<String> authorities = new HashSet<>();\n\t\t\t\t\tfor (GrantedAuthority authority : principal.getAuthorities()) {\n\t\t\t\t\t\tauthorities.add(authority.getAuthority());\n\t\t\t\t\t}\n\t\t\t\t\tcontext.getClaims().claim(AUTHORITIES_CLAIM, authorities);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t\t@Bean\n\t\tPasswordEncoder passwordEncoder() {\n\t\t\treturn NoOpPasswordEncoder.getInstance();\n\t\t}\n\n\t\t@Bean\n\t\tSessionRegistry sessionRegistry() {\n\t\t\treturn sessionRegistry;\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class AuthorizationServerConfigurationWithTokenGenerator extends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.tokenGenerator(tokenGenerator())\n\t\t\t\t\t\t\t\t\t.oidc(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\tOAuth2TokenGenerator<?> tokenGenerator() {\n\t\t\tJwtGenerator jwtGenerator = new JwtGenerator(new NimbusJwtEncoder(jwkSource()));\n\t\t\tjwtGenerator.setJwtCustomizer(jwtCustomizer());\n\t\t\tOAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();\n\t\t\tOAuth2TokenGenerator<OAuth2Token> delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(\n\t\t\t\t\tjwtGenerator, refreshTokenGenerator);\n\t\t\treturn spy(new OAuth2TokenGenerator<OAuth2Token>() {\n\t\t\t\t@Override\n\t\t\t\tpublic OAuth2Token generate(OAuth2TokenContext context) {\n\t\t\t\t\treturn delegatingTokenGenerator.generate(context);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class AuthorizationServerConfigurationWithCustomRefreshTokenGenerator\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tSecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.tokenGenerator(tokenGenerator())\n\t\t\t\t\t\t\t\t\t.oidc(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\tOAuth2TokenGenerator<?> tokenGenerator() {\n\t\t\tJwtGenerator jwtGenerator = new JwtGenerator(new NimbusJwtEncoder(jwkSource()));\n\t\t\tjwtGenerator.setJwtCustomizer(jwtCustomizer());\n\t\t\tOAuth2TokenGenerator<OAuth2RefreshToken> refreshTokenGenerator = new CustomRefreshTokenGenerator();\n\t\t\treturn new DelegatingOAuth2TokenGenerator(jwtGenerator, refreshTokenGenerator);\n\t\t}\n\n\t\tprivate static final class CustomRefreshTokenGenerator implements OAuth2TokenGenerator<OAuth2RefreshToken> {\n\n\t\t\tprivate final OAuth2RefreshTokenGenerator delegate = new OAuth2RefreshTokenGenerator();\n\n\t\t\t@Nullable\n\t\t\t@Override\n\t\t\tpublic OAuth2RefreshToken generate(OAuth2TokenContext context) {\n\t\t\t\tif (context.getAuthorizedScopes().contains(OidcScopes.OPENID)\n\t\t\t\t\t\t&& !context.getAuthorizedScopes().contains(\"offline_access\")) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn this.delegate.generate(context);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcUserInfoTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.ImmutableJWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for the OpenID Connect 1.0 UserInfo endpoint.\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OidcUserInfoTests {\n\n\tprivate static final String DEFAULT_OIDC_USER_INFO_ENDPOINT_URI = \"/userinfo\";\n\n\tprivate static SecurityContextRepository securityContextRepository;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Autowired\n\tprivate JwtEncoder jwtEncoder;\n\n\t@Autowired\n\tprivate JwtDecoder jwtDecoder;\n\n\t@Autowired\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate static AuthenticationConverter authenticationConverter;\n\n\tprivate static Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer;\n\n\tprivate static AuthenticationProvider authenticationProvider;\n\n\tprivate static Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer;\n\n\tprivate static AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate static AuthenticationFailureHandler authenticationFailureHandler;\n\n\tprivate static Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tsecurityContextRepository = spy(new HttpSessionSecurityContextRepository());\n\t\tauthenticationConverter = mock(AuthenticationConverter.class);\n\t\tauthenticationConvertersConsumer = mock(Consumer.class);\n\t\tauthenticationProvider = mock(AuthenticationProvider.class);\n\t\tauthenticationProvidersConsumer = mock(Consumer.class);\n\t\tauthenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tauthenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tuserInfoMapper = mock(Function.class);\n\t}\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\treset(securityContextRepository);\n\t\treset(authenticationConverter);\n\t\treset(authenticationConvertersConsumer);\n\t\treset(authenticationProvider);\n\t\treset(authenticationProvidersConsumer);\n\t\treset(authenticationSuccessHandler);\n\t\treset(authenticationFailureHandler);\n\t\treset(userInfoMapper);\n\t}\n\n\t@Test\n\tpublic void requestWhenUserInfoRequestGetThenUserInfoResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tOAuth2Authorization authorization = createAuthorization();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(DEFAULT_OIDC_USER_INFO_ENDPOINT_URI)\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken.getTokenValue()))\n\t\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t\t.andExpectAll(userInfoResponse());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUserInfoRequestPostThenUserInfoResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tOAuth2Authorization authorization = createAuthorization();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(DEFAULT_OIDC_USER_INFO_ENDPOINT_URI)\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken.getTokenValue()))\n\t\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t\t.andExpectAll(userInfoResponse());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUserInfoRequestIncludesIssuerPathThenUserInfoResponse() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfiguration.class).autowire();\n\n\t\tOAuth2Authorization authorization = createAuthorization();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tString issuer = \"https://example.com:8443/issuer1\";\n\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(issuer.concat(DEFAULT_OIDC_USER_INFO_ENDPOINT_URI))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken.getTokenValue()))\n\t\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t\t.andExpectAll(userInfoResponse());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUserInfoEndpointCustomizedThenUsed() throws Exception {\n\t\tthis.spring.register(CustomUserInfoConfiguration.class).autowire();\n\n\t\tOAuth2Authorization authorization = createAuthorization();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tgiven(userInfoMapper.apply(any())).willReturn(createUserInfo());\n\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(DEFAULT_OIDC_USER_INFO_ENDPOINT_URI)\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken.getTokenValue()))\n\t\t\t\t.andExpect(status().is2xxSuccessful());\n\t\t// @formatter:on\n\n\t\tverify(userInfoMapper).apply(any());\n\t\tverify(authenticationConverter).convert(any());\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\t\tverifyNoInteractions(authenticationFailureHandler);\n\n\t\tArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());\n\t\tList<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();\n\t\tassertThat(authenticationProviders).hasSize(2)\n\t\t\t.allMatch((provider) -> provider == authenticationProvider\n\t\t\t\t\t|| provider instanceof OidcUserInfoAuthenticationProvider);\n\n\t\tArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor\n\t\t\t.forClass(List.class);\n\t\tverify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());\n\t\tList<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();\n\t\tassertThat(authenticationConverters).hasSize(2).allMatch(AuthenticationConverter.class::isInstance);\n\t}\n\n\t@Test\n\tpublic void requestWhenUserInfoEndpointCustomizedWithAuthenticationProviderThenUsed() throws Exception {\n\t\tthis.spring.register(CustomUserInfoConfiguration.class).autowire();\n\n\t\tOAuth2Authorization authorization = createAuthorization();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tgiven(authenticationProvider.supports(eq(OidcUserInfoAuthenticationToken.class))).willReturn(true);\n\t\tString tokenValue = authorization.getAccessToken().getToken().getTokenValue();\n\t\tJwt jwt = this.jwtDecoder.decode(tokenValue);\n\t\tOidcUserInfoAuthenticationToken oidcUserInfoAuthentication = new OidcUserInfoAuthenticationToken(\n\t\t\t\tnew JwtAuthenticationToken(jwt), createUserInfo());\n\t\tgiven(authenticationProvider.authenticate(any())).willReturn(oidcUserInfoAuthentication);\n\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(DEFAULT_OIDC_USER_INFO_ENDPOINT_URI)\n\t\t\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken.getTokenValue()))\n\t\t\t\t.andExpect(status().is2xxSuccessful());\n\t\t// @formatter:on\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\t\tverify(authenticationProvider).authenticate(any());\n\t\tverifyNoInteractions(authenticationFailureHandler);\n\t\tverifyNoInteractions(userInfoMapper);\n\t}\n\n\t@Test\n\tpublic void requestWhenUserInfoEndpointCustomizedWithAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tthis.spring.register(CustomUserInfoConfiguration.class).autowire();\n\n\t\tgiven(userInfoMapper.apply(any())).willReturn(createUserInfo());\n\t\twillAnswer((invocation) -> {\n\t\t\tHttpServletResponse response = invocation.getArgument(1);\n\t\t\tresponse.setStatus(HttpStatus.UNAUTHORIZED.value());\n\t\t\tresponse.getWriter().write(\"unauthorized\");\n\t\t\treturn null;\n\t\t}).given(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());\n\n\t\tOAuth2AccessToken accessToken = createAuthorization().getAccessToken().getToken();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(DEFAULT_OIDC_USER_INFO_ENDPOINT_URI)\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken.getTokenValue()))\n\t\t\t\t.andExpect(status().is4xxClientError());\n\t\t// @formatter:on\n\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());\n\t\tverifyNoInteractions(authenticationSuccessHandler);\n\t\tverifyNoInteractions(userInfoMapper);\n\t}\n\n\t// gh-482\n\t@Test\n\tpublic void requestWhenUserInfoRequestThenBearerTokenAuthenticationNotPersisted() throws Exception {\n\t\tthis.spring.register(AuthorizationServerConfigurationWithSecurityContextRepository.class).autowire();\n\n\t\tOAuth2Authorization authorization = createAuthorization();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(DEFAULT_OIDC_USER_INFO_ENDPOINT_URI)\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken.getTokenValue()))\n\t\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t\t.andExpectAll(userInfoResponse())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\n\t\torg.springframework.security.core.context.SecurityContext securityContext = securityContextRepository\n\t\t\t.loadDeferredContext(mvcResult.getRequest())\n\t\t\t.get();\n\t\tassertThat(securityContext.getAuthentication()).isNull();\n\t}\n\n\tprivate static ResultMatcher[] userInfoResponse() {\n\t\t// @formatter:off\n\t\treturn new ResultMatcher[] {\n\t\t\t\tjsonPath(\"sub\").value(\"user1\"),\n\t\t\t\tjsonPath(\"name\").value(\"First Last\"),\n\t\t\t\tjsonPath(\"given_name\").value(\"First\"),\n\t\t\t\tjsonPath(\"family_name\").value(\"Last\"),\n\t\t\t\tjsonPath(\"middle_name\").value(\"Middle\"),\n\t\t\t\tjsonPath(\"nickname\").value(\"User\"),\n\t\t\t\tjsonPath(\"preferred_username\").value(\"user\"),\n\t\t\t\tjsonPath(\"profile\").value(\"https://example.com/user1\"),\n\t\t\t\tjsonPath(\"picture\").value(\"https://example.com/user1.jpg\"),\n\t\t\t\tjsonPath(\"website\").value(\"https://example.com\"),\n\t\t\t\tjsonPath(\"email\").value(\"user1@example.com\"),\n\t\t\t\tjsonPath(\"email_verified\").value(\"true\"),\n\t\t\t\tjsonPath(\"gender\").value(\"female\"),\n\t\t\t\tjsonPath(\"birthdate\").value(\"1970-01-01\"),\n\t\t\t\tjsonPath(\"zoneinfo\").value(\"Europe/Paris\"),\n\t\t\t\tjsonPath(\"locale\").value(\"en-US\"),\n\t\t\t\tjsonPath(\"phone_number\").value(\"+1 (604) 555-1234;ext=5678\"),\n\t\t\t\tjsonPath(\"phone_number_verified\").value(\"false\"),\n\t\t\t\tjsonPath(\"address.formatted\").value(\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\"),\n\t\t\t\tjsonPath(\"updated_at\").value(\"1970-01-01T00:00:00Z\")\n\t\t};\n\t\t// @formatter:on\n\t}\n\n\tprivate OAuth2Authorization createAuthorization() {\n\t\tJwsHeader headers = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\t// @formatter:off\n\t\tJwtClaimsSet claimSet = JwtClaimsSet.builder()\n\t\t\t\t.claims((claims) -> claims.putAll(createUserInfo().getClaims()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = this.jwtEncoder.encode(JwtEncoderParameters.from(headers, claimSet));\n\n\t\tInstant now = Instant.now();\n\t\tSet<String> scopes = new HashSet<>(Arrays.asList(OidcScopes.OPENID, OidcScopes.ADDRESS, OidcScopes.EMAIL,\n\t\t\t\tOidcScopes.PHONE, OidcScopes.PROFILE));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(),\n\t\t\t\tnow, now.plusSeconds(300), scopes);\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.claims((claims) -> claims.putAll(createUserInfo().getClaims()))\n\t\t\t.build();\n\n\t\treturn TestOAuth2Authorizations.authorization().accessToken(accessToken).token(idToken).build();\n\t}\n\n\tprivate static OidcUserInfo createUserInfo() {\n\t\t// @formatter:off\n\t\treturn OidcUserInfo.builder()\n\t\t\t\t.subject(\"user1\")\n\t\t\t\t.name(\"First Last\")\n\t\t\t\t.givenName(\"First\")\n\t\t\t\t.familyName(\"Last\")\n\t\t\t\t.middleName(\"Middle\")\n\t\t\t\t.nickname(\"User\")\n\t\t\t\t.preferredUsername(\"user\")\n\t\t\t\t.profile(\"https://example.com/user1\")\n\t\t\t\t.picture(\"https://example.com/user1.jpg\")\n\t\t\t\t.website(\"https://example.com\")\n\t\t\t\t.email(\"user1@example.com\")\n\t\t\t\t.emailVerified(true)\n\t\t\t\t.gender(\"female\")\n\t\t\t\t.birthdate(\"1970-01-01\")\n\t\t\t\t.zoneinfo(\"Europe/Paris\")\n\t\t\t\t.locale(\"en-US\")\n\t\t\t\t.phoneNumber(\"+1 (604) 555-1234;ext=5678\")\n\t\t\t\t.phoneNumberVerified(false)\n\t\t\t\t.claim(\"address\", Collections.singletonMap(\"formatted\", \"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\"))\n\t\t\t\t.updatedAt(\"1970-01-01T00:00:00Z\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class CustomUserInfoConfiguration extends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\t@Override\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc((oidc) ->\n\t\t\t\t\t\t\t\t\t\t\toidc\n\t\t\t\t\t\t\t\t\t\t\t\t\t.userInfoEndpoint((userInfo) ->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuserInfo\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.userInfoRequestConverter(authenticationConverter)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.userInfoRequestConverters(authenticationConvertersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProvider(authenticationProvider)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.authenticationProviders(authenticationProvidersConsumer)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.userInfoResponseHandler(authenticationSuccessHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.errorResponseHandler(authenticationFailureHandler)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.userInfoMapper(userInfoMapper)))\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfigurationWithSecurityContextRepository\n\t\t\textends AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\t@Override\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.securityContext((securityContext) ->\n\t\t\t\t\t\t\tsecurityContext.securityContextRepository(securityContextRepository));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class AuthorizationServerConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\t\t\t\tauthorizationServer\n\t\t\t\t\t\t\t\t\t.oidc(Customizer.withDefaults())\n\t\t\t\t\t)\n\t\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\t\t\tauthorize.anyRequest().authenticated()\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tRegisteredClientRepository registeredClientRepository() {\n\t\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t\treturn new InMemoryRegisteredClientRepository(registeredClient);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizationService authorizationService() {\n\t\t\treturn new InMemoryOAuth2AuthorizationService();\n\t\t}\n\n\t\t@Bean\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\treturn new ImmutableJWKSet<>(new JWKSet(TestJwks.DEFAULT_RSA_JWK));\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n\t\t}\n\n\t\t@Bean\n\t\tJwtEncoder jwtEncoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\treturn new NimbusJwtEncoder(jwkSource);\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationServerSettings authorizationServerSettings() {\n\t\t\treturn AuthorizationServerSettings.builder().multipleIssuersAllowed(true).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/DPoPAuthenticationConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link DPoPAuthenticationConfigurer}.\n *\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class DPoPAuthenticationConfigurerTests {\n\n\tprivate static final RSAPublicKey PROVIDER_RSA_PUBLIC_KEY = TestKeys.DEFAULT_PUBLIC_KEY;\n\n\tprivate static final RSAPrivateKey PROVIDER_RSA_PRIVATE_KEY = TestKeys.DEFAULT_PRIVATE_KEY;\n\n\tprivate static final ECPublicKey CLIENT_EC_PUBLIC_KEY = (ECPublicKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPublic();\n\n\tprivate static final ECPrivateKey CLIENT_EC_PRIVATE_KEY = (ECPrivateKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPrivate();\n\n\tprivate static final ECKey CLIENT_EC_KEY = TestJwks.jwk(CLIENT_EC_PUBLIC_KEY, CLIENT_EC_PRIVATE_KEY).build();\n\n\tprivate static NimbusJwtEncoder providerJwtEncoder;\n\n\tprivate static NimbusJwtEncoder clientJwtEncoder;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@BeforeAll\n\tpublic static void init() {\n\t\tRSAKey providerRsaKey = TestJwks.jwk(PROVIDER_RSA_PUBLIC_KEY, PROVIDER_RSA_PRIVATE_KEY).build();\n\t\tJWKSource<SecurityContext> providerJwkSource = (jwkSelector, securityContext) -> jwkSelector\n\t\t\t.select(new JWKSet(providerRsaKey));\n\t\tproviderJwtEncoder = new NimbusJwtEncoder(providerJwkSource);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector\n\t\t\t.select(new JWKSet(CLIENT_EC_KEY));\n\t\tclientJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t}\n\n\t@Test\n\tpublic void requestWhenDPoPAndBearerAuthenticationThenUnauthorized() throws Exception {\n\t\tthis.spring.register(SecurityConfig.class, ResourceEndpoints.class).autowire();\n\t\tSet<String> scope = Collections.singleton(\"resource1.read\");\n\t\tString accessToken = generateAccessToken(scope, CLIENT_EC_KEY);\n\t\tString dPoPProof = generateDPoPProof(HttpMethod.GET.name(), \"http://localhost/resource1\", accessToken);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/resource1\")\n\t\t\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"DPoP \" + accessToken)\n\t\t\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + accessToken)\n\t\t\t\t\t\t.header(\"DPoP\", dPoPProof))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE,\n\t\t\t\t\t\t\"DPoP error=\\\"invalid_request\\\", error_description=\\\"Found multiple Authorization headers.\\\", algs=\\\"RS256 RS384 RS512 PS256 PS384 PS512 ES256 ES384 ES512\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDPoPAccessTokenMalformedThenUnauthorized() throws Exception {\n\t\tthis.spring.register(SecurityConfig.class, ResourceEndpoints.class).autowire();\n\t\tSet<String> scope = Collections.singleton(\"resource1.read\");\n\t\tString accessToken = generateAccessToken(scope, CLIENT_EC_KEY);\n\t\tString dPoPProof = generateDPoPProof(HttpMethod.GET.name(), \"http://localhost/resource1\", accessToken);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/resource1\")\n\t\t\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"DPoP \" + accessToken + \" m a l f o r m e d \")\n\t\t\t\t\t\t.header(\"DPoP\", dPoPProof))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE,\n\t\t\t\t\t\t\"DPoP error=\\\"invalid_token\\\", error_description=\\\"DPoP access token is malformed.\\\", algs=\\\"RS256 RS384 RS512 PS256 PS384 PS512 ES256 ES384 ES512\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenMultipleDPoPProofsThenUnauthorized() throws Exception {\n\t\tthis.spring.register(SecurityConfig.class, ResourceEndpoints.class).autowire();\n\t\tSet<String> scope = Collections.singleton(\"resource1.read\");\n\t\tString accessToken = generateAccessToken(scope, CLIENT_EC_KEY);\n\t\tString dPoPProof = generateDPoPProof(HttpMethod.GET.name(), \"http://localhost/resource1\", accessToken);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/resource1\")\n\t\t\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"DPoP \" + accessToken)\n\t\t\t\t\t\t.header(\"DPoP\", dPoPProof)\n\t\t\t\t\t\t.header(\"DPoP\", dPoPProof))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE,\n\t\t\t\t\t\t\"DPoP error=\\\"invalid_request\\\", error_description=\\\"DPoP proof is missing or invalid.\\\", algs=\\\"RS256 RS384 RS512 PS256 PS384 PS512 ES256 ES384 ES512\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDPoPAuthenticationValidThenAccessed() throws Exception {\n\t\tthis.spring.register(SecurityConfig.class, ResourceEndpoints.class).autowire();\n\t\tSet<String> scope = Collections.singleton(\"resource1.read\");\n\t\tString accessToken = generateAccessToken(scope, CLIENT_EC_KEY);\n\t\tString dPoPProof = generateDPoPProof(HttpMethod.GET.name(), \"http://localhost/resource1\", accessToken);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/resource1\")\n\t\t\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"DPoP \" + accessToken)\n\t\t\t\t\t\t.header(\"DPoP\", dPoPProof))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"resource1\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate static String generateAccessToken(Set<String> scope, JWK jwk) {\n\t\tMap<String, Object> jktClaim = null;\n\t\tif (jwk != null) {\n\t\t\ttry {\n\t\t\t\tString sha256Thumbprint = jwk.toPublicJWK().computeThumbprint().toString();\n\t\t\t\tjktClaim = new HashMap<>();\n\t\t\t\tjktClaim.put(\"jkt\", sha256Thumbprint);\n\t\t\t}\n\t\t\tcatch (Exception ignored) {\n\t\t\t}\n\t\t}\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);\n\t\t// @formatter:off\n\t\tJwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder()\n\t\t\t\t.issuer(\"https://provider.com\")\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.claim(OAuth2ParameterNames.SCOPE, scope);\n\t\tif (jktClaim != null) {\n\t\t\tclaimsBuilder.claim(\"cnf\", jktClaim);\t// Bind client public key\n\t\t}\n\t\t// @formatter:on\n\t\tJwt jwt = providerJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claimsBuilder.build()));\n\t\treturn jwt.getTokenValue();\n\t}\n\n\tprivate static String generateDPoPProof(String method, String resourceUri, String accessToken) throws Exception {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = CLIENT_EC_KEY.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", resourceUri)\n\t\t\t\t.claim(\"ath\", computeSHA256(accessToken))\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = clientJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n\tprivate static String computeSHA256(String value) throws Exception {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.UTF_8));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class SecurityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.requestMatchers(\"/resource1\").hasAnyAuthority(\"SCOPE_resource1.read\", \"SCOPE_resource1.write\")\n\t\t\t\t\t\t.requestMatchers(\"/resource2\").hasAnyAuthority(\"SCOPE_resource2.read\", \"SCOPE_resource2.write\")\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tNimbusJwtDecoder jwtDecoder() {\n\t\t\treturn NimbusJwtDecoder.withPublicKey(PROVIDER_RSA_PUBLIC_KEY).build();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class ResourceEndpoints {\n\n\t\t@RequestMapping(value = \"/resource1\", method = { RequestMethod.GET, RequestMethod.POST })\n\t\tString resource1() {\n\t\t\treturn \"resource1\";\n\t\t}\n\n\t\t@RequestMapping(value = \"/resource2\", method = { RequestMethod.GET, RequestMethod.POST })\n\t\tString resource2() {\n\t\t\treturn \"resource2\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ProtectedResourceMetadataTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;\n\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.server.resource.OAuth2ProtectedResourceMetadata;\nimport org.springframework.security.oauth2.server.resource.OAuth2ProtectedResourceMetadataClaimNames;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.hamcrest.Matchers.hasItem;\nimport static org.hamcrest.Matchers.hasSize;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Integration tests for OAuth 2.0 Protected Resource Metadata Requests.\n *\n * @author Joe Grandja\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2ProtectedResourceMetadataTests {\n\n\tprivate static final String DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI = \"/.well-known/oauth-protected-resource\";\n\n\tprivate static final String RESOURCE = \"https://resource.com:8443\";\n\n\tprivate static final String ISSUER_1 = \"https://provider1.com\";\n\n\tprivate static final String ISSUER_2 = \"https://provider2.com\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenProtectedResourceMetadataRequestThenReturnMetadataResponse() throws Exception {\n\t\tthis.spring.register(ResourceServerConfiguration.class).autowire();\n\n\t\tthis.mvc.perform(get(RESOURCE.concat(DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE).value(RESOURCE))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED).isArray())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED).value(hasSize(1)))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED)\n\t\t\t\t.value(hasItem(\"header\")))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS)\n\t\t\t\t.value(true))\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void requestWhenProtectedResourceMetadataRequestIncludesResourcePathThenMetadataResponseHasResourcePath()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ResourceServerConfiguration.class).autowire();\n\n\t\tString host = RESOURCE;\n\n\t\tString resourcePath = \"/resource1\";\n\t\tString resource = host.concat(resourcePath);\n\t\tthis.mvc.perform(get(host.concat(DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI).concat(resourcePath)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE).value(resource))\n\t\t\t.andReturn();\n\n\t\tresourcePath = \"/path1/resource2\";\n\t\tresource = host.concat(resourcePath);\n\t\tthis.mvc.perform(get(host.concat(DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI).concat(resourcePath)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE).value(resource))\n\t\t\t.andReturn();\n\n\t\tresourcePath = \"/path1/path2/resource3\";\n\t\tresource = host.concat(resourcePath);\n\t\tthis.mvc.perform(get(host.concat(DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI).concat(resourcePath)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE).value(resource))\n\t\t\t.andReturn();\n\t}\n\n\t@Test\n\tpublic void requestWhenProtectedResourceMetadataRequestAndMetadataCustomizerSetThenReturnCustomMetadataResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(ResourceServerConfigurationWithMetadataCustomizer.class).autowire();\n\n\t\tthis.mvc.perform(get(RESOURCE.concat(DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI)))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE).value(RESOURCE))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS).isArray())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS).value(hasSize(2)))\n\t\t\t.andExpect(\n\t\t\t\t\tjsonPath(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS).value(hasItem(ISSUER_1)))\n\t\t\t.andExpect(\n\t\t\t\t\tjsonPath(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS).value(hasItem(ISSUER_2)))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED).isArray())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED).value(hasSize(2)))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED).value(hasItem(\"scope1\")))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED).value(hasItem(\"scope2\")))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED).isArray())\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED).value(hasSize(1)))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED)\n\t\t\t\t.value(hasItem(\"header\")))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE_NAME).value(\"resourceName\"))\n\t\t\t.andExpect(jsonPath(OAuth2ProtectedResourceMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS)\n\t\t\t\t.value(true))\n\t\t\t.andReturn();\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class ResourceServerConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\tauthorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) ->\n\t\t\t\t\toauth2\n\t\t\t\t\t\t.jwt(Customizer.withDefaults())\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder() {\n\t\t\treturn NimbusJwtDecoder.withPublicKey(TestKeys.DEFAULT_PUBLIC_KEY).build();\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class ResourceServerConfigurationWithMetadataCustomizer extends ResourceServerConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\t\tauthorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) ->\n\t\t\t\t\toauth2\n\t\t\t\t\t\t.jwt(Customizer.withDefaults())\n\t\t\t\t\t\t.protectedResourceMetadata((metadata) ->\n\t\t\t\t\t\t\tmetadata.protectedResourceMetadataCustomizer(protectedResourceMetadataCustomizer())\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\tprivate Consumer<OAuth2ProtectedResourceMetadata.Builder> protectedResourceMetadataCustomizer() {\n\t\t\treturn (protectedResourceMetadata) -> protectedResourceMetadata.authorizationServer(ISSUER_1)\n\t\t\t\t.authorizationServer(ISSUER_2)\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.resourceName(\"resourceName\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;\n\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.security.KeyFactory;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.JWSHeader;\nimport com.nimbusds.jose.JWSObject;\nimport com.nimbusds.jose.Payload;\nimport com.nimbusds.jose.crypto.RSASSASigner;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.util.JSONObjectUtils;\nimport jakarta.annotation.PreDestroy;\nimport jakarta.servlet.http.HttpServletRequest;\nimport net.minidev.json.JSONObject;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.hamcrest.core.AllOf;\nimport org.hamcrest.core.StringContains;\nimport org.hamcrest.core.StringEndsWith;\nimport org.hamcrest.core.StringStartsWith;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.verification.VerificationMode;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.NoUniqueBeanDefinitionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.EnvironmentAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.core.env.Environment;\nimport org.springframework.core.env.PropertySource;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtTimestampValidator;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;\nimport org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;\nimport org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestOperations;\nimport org.springframework.web.context.support.GenericWebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.hamcrest.CoreMatchers.startsWith;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link OAuth2ResourceServerConfigurer}\n *\n * @author Josh Cummings\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2ResourceServerConfigurerTests {\n\n\tprivate static final String JWT_TOKEN = \"token\";\n\n\tprivate static final String JWT_SUBJECT = \"mock-test-subject\";\n\n\tprivate static final Map<String, Object> JWT_CLAIMS = Collections.singletonMap(JwtClaimNames.SUB, JWT_SUBJECT);\n\n\tprivate static final Jwt JWT = TestJwts.jwt().build();\n\n\tprivate static final String JWK_SET_URI = \"https://mock.org\";\n\n\tprivate static final JwtAuthenticationToken JWT_AUTHENTICATION_TOKEN = new JwtAuthenticationToken(JWT,\n\t\t\tCollections.emptyList());\n\n\tprivate static final String INTROSPECTION_URI = \"https://idp.example.com\";\n\n\tprivate static final String CLIENT_ID = \"client-id\";\n\n\tprivate static final String CLIENT_SECRET = \"client-secret\";\n\n\tprivate static final BearerTokenAuthentication INTROSPECTION_AUTHENTICATION_TOKEN = new BearerTokenAuthentication(\n\t\t\tnew DefaultOAuth2AuthenticatedPrincipal(JWT_CLAIMS, Collections.emptyList()),\n\t\t\tTestOAuth2AccessTokens.noScopes(), Collections.emptyList());\n\n\t@Autowired(required = false)\n\tMockMvc mvc;\n\n\t@Autowired(required = false)\n\tMockWebServer web;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithValidBearerTokenThenAcceptsRequest() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"ok\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring\n\t\t\t.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class,\n\t\t\t\t\tSecurityContextChangedListenerConfig.class)\n\t\t\t.autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"ok\"));\n\t\t// @formatter:on\n\t\tverifyBean(SecurityContextHolderStrategy.class, atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void getWhenSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring\n\t\t\t.register(RestOperationsConfig.class, DefaultConfig.class, SecurityContextChangedListenerConfig.class,\n\t\t\t\t\tBasicController.class)\n\t\t\t.autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"ok\"));\n\t\t// @formatter:on\n\t\tverifyBean(SecurityContextChangedListener.class, atLeastOnce()).securityContextChanged(any());\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsInLambdaWithValidBearerTokenThenAcceptsRequest() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultInLambdaConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"ok\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingJwkSetUriThenAcceptsRequest() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, JwkSetUriConfig.class, BasicController.class).autowire();\n\t\tmockWebServer(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"ok\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingJwkSetUriInLambdaThenAcceptsRequest() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, JwkSetUriInLambdaConfig.class, BasicController.class).autowire();\n\t\tmockWebServer(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"ok\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithExpiredBearerTokenThenInvalidToken() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"Expired\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithBadJwkEndpointThen500() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class).autowire();\n\t\tmockRestOperations(\"malformed\");\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t\t.isThrownBy(() -> this.mvc.perform(get(\"/\").with(bearerToken(token))));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithUnavailableJwkEndpointThen500() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, JwkSetUriConfig.class).autowire();\n\t\tthis.web.shutdown();\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t\t.isThrownBy(() -> this.mvc.perform(get(\"/\").with(bearerToken(token))));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithMalformedBearerTokenThenInvalidToken() throws Exception {\n\t\tthis.spring.register(JwkSetUriConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(\"an\\\"invalid\\\"token\")))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"Bearer token is malformed\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithMalformedPayloadThenInvalidToken() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"MalformedPayload\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt: Malformed payload\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithUnsignedBearerTokenThenInvalidToken() throws Exception {\n\t\tthis.spring.register(JwkSetUriConfig.class).autowire();\n\t\tString token = this.token(\"Unsigned\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"Unsupported algorithm of none\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithBearerTokenBeforeNotBeforeThenInvalidToken() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class).autowire();\n\t\tthis.mockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"TooEarly\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithBearerTokenInTwoPlacesThenInvalidRequest() throws Exception {\n\t\tthis.spring.register(JwkSetUriConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(\"token\")).with(bearerToken(\"token\").asParam()))\n\t\t\t\t.andExpect(status().isBadRequest())\n\t\t\t\t.andExpect(invalidRequestHeader(\"Found multiple bearer tokens in the request\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithBearerTokenInTwoParametersThenInvalidRequest() throws Exception {\n\t\tthis.spring.register(JwkSetUriConfig.class).autowire();\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"access_token\", \"token1\");\n\t\tparams.add(\"access_token\", \"token2\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").params(params))\n\t\t\t\t.andExpect(status().isBadRequest())\n\t\t\t\t.andExpect(invalidRequestHeader(\"Found multiple bearer tokens in the request\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenUsingDefaultsWithBearerTokenAsFormParameterThenIgnoresToken() throws Exception {\n\t\tthis.spring.register(JwkSetUriConfig.class).autowire();\n\t\t// engage csrf\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/\").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).with(bearerToken(\"token\").asParam()))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(header().doesNotExist(HttpHeaders.WWW_AUTHENTICATE));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenCsrfDisabledWithBearerTokenAsFormParameterThenIgnoresToken() throws Exception {\n\t\tthis.spring.register(CsrfDisabledConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/\").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).with(bearerToken(\"token\").asParam()))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Bearer resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-8031\n\t@Test\n\tpublic void getWhenAnonymousDisabledThenAllows() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, AnonymousDisabledConfig.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithNoBearerTokenThenUnauthorized() throws Exception {\n\t\tthis.spring.register(JwkSetUriConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Bearer resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithSufficientlyScopedBearerTokenThenAcceptsRequest() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidMessageReadScope\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"[SCOPE_message:read]\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithInsufficientScopeThenInsufficientScopeError() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(insufficientScopeHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsWithInsufficientScpThenInsufficientScopeError() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidMessageWriteScp\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(insufficientScopeHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsAndAuthorizationServerHasNoMatchingKeyThenInvalidToken() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Empty\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsAndAuthorizationServerHasMultipleMatchingKeysThenOk() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"TwoKeys\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingDefaultsAndKeyMatchesByKidThenOk() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"TwoKeys\"));\n\t\tString token = this.token(\"Kid\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingMethodSecurityWithValidBearerTokenThenAcceptsRequest() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, MethodSecurityConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidMessageReadScope\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/ms-requires-read-scope\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"[SCOPE_message:read]\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingMethodSecurityWithValidBearerTokenHavingScpAttributeThenAcceptsRequest() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, MethodSecurityConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidMessageReadScp\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/ms-requires-read-scope\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"[SCOPE_message:read]\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingMethodSecurityWithInsufficientScopeThenInsufficientScopeError() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, MethodSecurityConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/ms-requires-read-scope\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(insufficientScopeHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingMethodSecurityWithInsufficientScpThenInsufficientScopeError() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, MethodSecurityConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidMessageWriteScp\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/ms-requires-read-scope\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(insufficientScopeHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingMethodSecurityWithDenyAllThenInsufficientScopeError() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, MethodSecurityConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidMessageReadScope\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/ms-deny\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(insufficientScopeHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenUsingDefaultsWithValidBearerTokenAndNoCsrfTokenThenOk() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/authenticated\").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenUsingDefaultsWithNoBearerTokenThenCsrfDenies() throws Exception {\n\t\tthis.spring.register(JwkSetUriConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/authenticated\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(header().doesNotExist(HttpHeaders.WWW_AUTHENTICATE));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenUsingDefaultsWithExpiredBearerTokenAndNoCsrfThenInvalidToken() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"Expired\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/authenticated\").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDefaultConfiguredThenSessionIsNotCreated() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, DefaultConfig.class, BasicController.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenIntrospectionConfiguredThenSessionIsNotCreated() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, OpaqueTokenConfig.class, BasicController.class).autowire();\n\t\tmockJsonRestOperations(json(\"Active\"));\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingDefaultsAndNoBearerTokenThenSessionIsCreated() throws Exception {\n\t\tthis.spring.register(JwkSetUriConfig.class, BasicController.class).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionManagementConfiguredThenUserConfigurationOverrides() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, AlwaysSessionCreationConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AllowBearerTokenInRequestBodyConfig.class, JwtDecoderConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(JWT_TOKEN)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(JWT_SUBJECT));\n\t\tthis.mvc.perform(post(\"/authenticated\").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).param(\"access_token\", JWT_TOKEN))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(JWT_SUBJECT));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenBearerTokenResolverAllowsQueryParameterThenEitherHeaderOrQueryParameterIsAccepted()\n\t\t\tthrows Exception {\n\t\tthis.spring\n\t\t\t.register(AllowBearerTokenAsQueryParameterConfig.class, JwtDecoderConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(JWT_TOKEN)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(JWT_SUBJECT));\n\t\tthis.mvc.perform(get(\"/authenticated\").param(\"access_token\", JWT_TOKEN))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(JWT_SUBJECT));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenBearerTokenResolverAllowsRequestBodyAndRequestContainsTwoTokensThenInvalidRequest()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(AllowBearerTokenInRequestBodyConfig.class, JwtDecoderConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/authenticated\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.param(\"access_token\", JWT_TOKEN)\n\t\t\t\t.with(bearerToken(JWT_TOKEN))\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isBadRequest())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"invalid_request\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenBearerTokenResolverAllowsQueryParameterAndRequestContainsTwoTokensThenInvalidRequest()\n\t\t\tthrows Exception {\n\t\tthis.spring\n\t\t\t.register(AllowBearerTokenAsQueryParameterConfig.class, JwtDecoderConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/authenticated\")\n\t\t\t\t.with(bearerToken(JWT_TOKEN))\n\t\t\t\t.param(\"access_token\", JWT_TOKEN);\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isBadRequest())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"invalid_request\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getBearerTokenResolverWhenDuplicateResolverBeansAndAnotherOnTheDslThenTheDslOneIsUsed() {\n\t\tBearerTokenResolver resolverBean = mock(BearerTokenResolver.class);\n\t\tBearerTokenResolver resolver = mock(BearerTokenResolver.class);\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(\"resolverOne\", BearerTokenResolver.class, () -> resolverBean);\n\t\tcontext.registerBean(\"resolverTwo\", BearerTokenResolver.class, () -> resolverBean);\n\t\tthis.spring.context(context).autowire();\n\t\tOAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);\n\t\toauth2.bearerTokenResolver(resolver);\n\t\tassertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);\n\t}\n\n\t@Test\n\tpublic void getBearerTokenResolverWhenDuplicateResolverBeansThenWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(MultipleBearerTokenResolverBeansConfig.class, JwtDecoderConfig.class)\n\t\t\t\t.autowire())\n\t\t\t.withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class);\n\t}\n\n\t@Test\n\tpublic void getBearerTokenResolverWhenResolverBeanAndAnotherOnTheDslThenTheDslOneIsUsed() {\n\t\tBearerTokenResolver resolver = mock(BearerTokenResolver.class);\n\t\tBearerTokenResolver resolverBean = mock(BearerTokenResolver.class);\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(BearerTokenResolver.class, () -> resolverBean);\n\t\tthis.spring.context(context).autowire();\n\t\tOAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);\n\t\toauth2.bearerTokenResolver(resolver);\n\t\tassertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(JWT_TOKEN)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(JWT_SUBJECT));\n\t\tverifyBean(AuthenticationDetailsSource.class).buildDetails(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomJwtDecoderWiredOnDslThenUsed() throws Exception {\n\t\tthis.spring.register(CustomJwtDecoderOnDsl.class, BasicController.class).autowire();\n\t\tCustomJwtDecoderOnDsl config = this.spring.getContext().getBean(CustomJwtDecoderOnDsl.class);\n\t\tJwtDecoder decoder = config.decoder();\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(JWT_TOKEN)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(JWT_SUBJECT));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomJwtDecoderInLambdaOnDslThenUsed() throws Exception {\n\t\tthis.spring.register(CustomJwtDecoderInLambdaOnDsl.class, BasicController.class).autowire();\n\t\tCustomJwtDecoderInLambdaOnDsl config = this.spring.getContext().getBean(CustomJwtDecoderInLambdaOnDsl.class);\n\t\tJwtDecoder decoder = config.decoder();\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(JWT_TOKEN)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(JWT_SUBJECT));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomJwtDecoderExposedAsBeanThenUsed() throws Exception {\n\t\tthis.spring.register(CustomJwtDecoderAsBean.class, BasicController.class).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(JWT_TOKEN)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(JWT_SUBJECT));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenConfiguredWithDecoderAndJwkSetUriThenLastOneWins() {\n\t\tApplicationContext context = mock(ApplicationContext.class);\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).jwt((jwt) -> {\n\t\t\tjwt.jwkSetUri(JWK_SET_URI);\n\t\t\tjwt.decoder(decoder);\n\t\t\tassertThat(jwt.getJwtDecoder()).isEqualTo(decoder);\n\t\t});\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).jwt((jwt) -> {\n\t\t\tjwt.decoder(decoder).jwkSetUri(JWK_SET_URI);\n\t\t\tassertThat(jwt.getJwtDecoder()).isInstanceOf(NimbusJwtDecoder.class);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenConflictingJwtDecodersThenTheDslWiredOneTakesPrecedence() {\n\t\tJwtDecoder decoderBean = mock(JwtDecoder.class);\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\t\tApplicationContext context = mock(ApplicationContext.class);\n\t\tgiven(context.getBean(JwtDecoder.class)).willReturn(decoderBean);\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).jwt((jwt) -> {\n\t\t\tjwt.decoder(decoder);\n\t\t\tassertThat(jwt.getJwtDecoder()).isEqualTo(decoder);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenContextHasBeanAndUserConfiguresJwkSetUriThenJwkSetUriTakesPrecedence() {\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\t\tApplicationContext context = mock(ApplicationContext.class);\n\t\tgiven(context.getBean(JwtDecoder.class)).willReturn(decoder);\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).jwt((jwt) -> {\n\t\t\tjwt.jwkSetUri(JWK_SET_URI);\n\t\t\tassertThat(jwt.getJwtDecoder()).isNotEqualTo(decoder);\n\t\t\tassertThat(jwt.getJwtDecoder()).isInstanceOf(NimbusJwtDecoder.class);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenTwoJwtDecoderBeansAndAnotherWiredOnDslThenDslWiredOneTakesPrecedence() {\n\t\tJwtDecoder decoderBean = mock(JwtDecoder.class);\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(\"decoderOne\", JwtDecoder.class, () -> decoderBean);\n\t\tcontext.registerBean(\"decoderTwo\", JwtDecoder.class, () -> decoderBean);\n\t\tthis.spring.context(context).autowire();\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).jwt((jwt) -> {\n\t\t\tjwt.decoder(decoder);\n\t\t\tassertThat(jwt.getJwtDecoder()).isEqualTo(decoder);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenTwoJwtDecoderBeansThenThrowsException() {\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(\"decoderOne\", JwtDecoder.class, () -> decoder);\n\t\tcontext.registerBean(\"decoderTwo\", JwtDecoder.class, () -> decoder);\n\t\tthis.spring.context(context).autowire();\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context)\n\t\t\t.jwt((jwt) -> assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)\n\t\t\t\t.isThrownBy(jwt::getJwtDecoder));\n\t}\n\n\t@Test\n\tpublic void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {\n\t\tthis.spring.register(RealmNameConfiguredOnEntryPoint.class, JwtDecoderConfig.class).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willThrow(BadJwtException.class);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"invalid_token\")))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer realm=\\\"myRealm\\\"\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenRealmNameConfiguredThenUsesOnAccessDenied() throws Exception {\n\t\tthis.spring.register(RealmNameConfiguredOnAccessDeniedHandler.class, JwtDecoderConfig.class).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"insufficiently_scoped\")))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer realm=\\\"myRealm\\\"\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticationEntryPointWhenGivenNullThenThrowsException() {\n\t\tApplicationContext context = mock(ApplicationContext.class);\n\t\tOAuth2ResourceServerConfigurer configurer = new OAuth2ResourceServerConfigurer(context);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> configurer.authenticationEntryPoint(null));\n\t}\n\n\t@Test\n\tpublic void accessDeniedHandlerWhenGivenNullThenThrowsException() {\n\t\tApplicationContext context = mock(ApplicationContext.class);\n\t\tOAuth2ResourceServerConfigurer configurer = new OAuth2ResourceServerConfigurer(context);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> configurer.accessDeniedHandler(null));\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomJwtValidatorFailsThenCorrespondingErrorMessage() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, CustomJwtValidatorConfig.class).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = this.spring.getContext()\n\t\t\t.getBean(CustomJwtValidatorConfig.class)\n\t\t\t.getJwtValidator();\n\t\tOAuth2Error error = new OAuth2Error(\"custom-error\", \"custom-description\", \"custom-uri\");\n\t\tgiven(jwtValidator.validate(any(Jwt.class))).willReturn(OAuth2TokenValidatorResult.failure(error));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"custom-description\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenClockSkewSetThenTimestampWindowRelaxedAccordingly() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, UnexpiredJwtClockSkewConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ExpiresAt4687177990\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenClockSkewSetButJwtStillTooLateThenReportsExpired() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, ExpiredJwtClockSkewConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ExpiresAt4687177990\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"Jwt expired at\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenJwtAuthenticationConverterConfiguredOnDslThenIsUsed() throws Exception {\n\t\tthis.spring\n\t\t\t.register(JwtDecoderConfig.class, JwtAuthenticationConverterConfiguredOnDsl.class, BasicController.class)\n\t\t\t.autowire();\n\t\tConverter<Jwt, JwtAuthenticationToken> jwtAuthenticationConverter = this.spring.getContext()\n\t\t\t.getBean(JwtAuthenticationConverterConfiguredOnDsl.class)\n\t\t\t.getJwtAuthenticationConverter();\n\t\tgiven(jwtAuthenticationConverter.convert(JWT)).willReturn(JWT_AUTHENTICATION_TOKEN);\n\t\tJwtDecoder jwtDecoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(JWT_TOKEN)))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tverify(jwtAuthenticationConverter).convert(JWT);\n\t}\n\n\t@Test\n\tpublic void requestWhenJwtAuthenticationConverterCustomizedAuthoritiesThenThoseAuthoritiesArePropagated()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(JwtDecoderConfig.class, CustomAuthorityMappingConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(JWT_TOKEN)).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").with(bearerToken(JWT_TOKEN)))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingPublicKeyAndValidTokenThenAuthenticates() throws Exception {\n\t\tthis.spring.register(SingleKeyConfig.class, BasicController.class).autowire();\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingPublicKeyAndSignatureFailsThenReturnsInvalidToken() throws Exception {\n\t\tthis.spring.register(SingleKeyConfig.class).autowire();\n\t\tString token = this.token(\"WrongSignature\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(invalidTokenHeader(\"signature\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingPublicKeyAlgorithmDoesNotMatchThenReturnsInvalidToken() throws Exception {\n\t\tthis.spring.register(SingleKeyConfig.class).autowire();\n\t\tString token = this.token(\"WrongAlgorithm\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(token)))\n\t\t\t\t.andExpect(invalidTokenHeader(\"algorithm\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-7793\n\t@Test\n\tpublic void requestWhenUsingCustomAuthenticationEventPublisherThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationEventPublisher.class).autowire();\n\t\tgiven(bean(JwtDecoder.class).decode(anyString())).willThrow(new BadJwtException(\"problem\"));\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(\"token\")));\n\t\tverifyBean(AuthenticationEventPublisher.class)\n\t\t\t.publishAuthenticationFailure(any(OAuth2AuthenticationException.class), any(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void getWhenCustomJwtAuthenticationManagerThenUsed() throws Exception {\n\t\tthis.spring.register(JwtAuthenticationManagerConfig.class, BasicController.class).autowire();\n\t\tgiven(bean(AuthenticationProvider.class).authenticate(any(Authentication.class)))\n\t\t\t.willReturn(JWT_AUTHENTICATION_TOKEN);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"mock-test-subject\"));\n\t\t// @formatter:on\n\t\tverifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultAndCustomJwtAuthenticationManagerThenCustomUsed() throws Exception {\n\t\tthis.spring.register(DefaultAndJwtAuthenticationManagerConfig.class, BasicController.class).autowire();\n\t\tDefaultAndJwtAuthenticationManagerConfig config = this.spring.getContext()\n\t\t\t.getBean(DefaultAndJwtAuthenticationManagerConfig.class);\n\t\tAuthenticationManager defaultAuthenticationManager = config.defaultAuthenticationManager();\n\t\tAuthenticationManager jwtAuthenticationManager = config.jwtAuthenticationManager();\n\t\tgiven(defaultAuthenticationManager.authenticate(any()))\n\t\t\t.willThrow(new RuntimeException(\"should not interact with default auth manager\"));\n\t\tgiven(jwtAuthenticationManager.authenticate(any())).willReturn(JWT_AUTHENTICATION_TOKEN);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"mock-test-subject\"));\n\t\t// @formatter:on\n\t\tverify(jwtAuthenticationManager).authenticate(any(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void getWhenIntrospectingThenOk() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, OpaqueTokenConfig.class, BasicController.class).autowire();\n\t\tmockJsonRestOperations(json(\"Active\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenOpaqueTokenInLambdaAndIntrospectingThenOk() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, OpaqueTokenInLambdaConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tmockJsonRestOperations(json(\"Active\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenIntrospectionFailsThenUnauthorized() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, OpaqueTokenConfig.class).autowire();\n\t\tmockJsonRestOperations(json(\"Inactive\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"Provided token isn't active\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenIntrospectionLacksScopeThenForbidden() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, OpaqueTokenConfig.class).autowire();\n\t\tmockJsonRestOperations(json(\"ActiveNoScopes\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"scope\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenCustomIntrospectionAuthenticationManagerThenUsed() throws Exception {\n\t\tthis.spring.register(OpaqueTokenAuthenticationManagerConfig.class, BasicController.class).autowire();\n\t\tgiven(bean(AuthenticationProvider.class).authenticate(any(Authentication.class)))\n\t\t\t.willReturn(INTROSPECTION_AUTHENTICATION_TOKEN);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"mock-test-subject\"));\n\t\t// @formatter:on\n\t\tverifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultAndCustomIntrospectionAuthenticationManagerThenCustomUsed() throws Exception {\n\t\tthis.spring.register(DefaultAndOpaqueTokenAuthenticationManagerConfig.class, BasicController.class).autowire();\n\t\tDefaultAndOpaqueTokenAuthenticationManagerConfig config = this.spring.getContext()\n\t\t\t.getBean(DefaultAndOpaqueTokenAuthenticationManagerConfig.class);\n\t\tAuthenticationManager defaultAuthenticationManager = config.defaultAuthenticationManager();\n\t\tAuthenticationManager opaqueTokenAuthenticationManager = config.opaqueTokenAuthenticationManager();\n\t\tgiven(defaultAuthenticationManager.authenticate(any()))\n\t\t\t.willThrow(new RuntimeException(\"should not interact with default auth manager\"));\n\t\tgiven(opaqueTokenAuthenticationManager.authenticate(any())).willReturn(INTROSPECTION_AUTHENTICATION_TOKEN);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"mock-test-subject\"));\n\t\t// @formatter:on\n\t\tverify(opaqueTokenAuthenticationManager).authenticate(any(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void getWhenCustomIntrospectionAuthenticationManagerInLambdaThenUsed() throws Exception {\n\t\tthis.spring.register(OpaqueTokenAuthenticationManagerInLambdaConfig.class, BasicController.class).autowire();\n\t\tgiven(bean(AuthenticationProvider.class).authenticate(any(Authentication.class)))\n\t\t\t.willReturn(INTROSPECTION_AUTHENTICATION_TOKEN);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"mock-test-subject\"));\n\t\t// @formatter:on\n\t\tverifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenOnlyIntrospectionUrlThenException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(OpaqueTokenHalfConfiguredConfig.class).autowire());\n\t}\n\n\t@Test\n\tpublic void getIntrospectionClientWhenConfiguredWithClientAndIntrospectionUriThenLastOneWins() {\n\t\tApplicationContext context = mock(ApplicationContext.class);\n\t\tOpaqueTokenIntrospector client = mock(OpaqueTokenIntrospector.class);\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).opaqueToken((opaqueToken) -> {\n\t\t\topaqueToken.introspectionUri(INTROSPECTION_URI);\n\t\t\topaqueToken.introspectionClientCredentials(CLIENT_ID, CLIENT_SECRET);\n\t\t\topaqueToken.introspector(client);\n\t\t\tassertThat(opaqueToken.getIntrospector()).isEqualTo(client);\n\t\t});\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).opaqueToken((opaqueToken) -> {\n\t\t\topaqueToken.introspector(client);\n\t\t\topaqueToken.introspectionUri(INTROSPECTION_URI);\n\t\t\topaqueToken.introspectionClientCredentials(CLIENT_ID, CLIENT_SECRET);\n\t\t\tassertThat(opaqueToken.getIntrospector()).isNotSameAs(client);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void getIntrospectionClientWhenDslAndBeanWiredThenDslTakesPrecedence() {\n\t\tGenericApplicationContext context = new GenericApplicationContext();\n\t\tregisterMockBean(context, \"introspectionClientOne\", OpaqueTokenIntrospector.class);\n\t\tregisterMockBean(context, \"introspectionClientTwo\", OpaqueTokenIntrospector.class);\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).opaqueToken((opaqueToken) -> {\n\t\t\topaqueToken.introspectionUri(INTROSPECTION_URI);\n\t\t\topaqueToken.introspectionClientCredentials(CLIENT_ID, CLIENT_SECRET);\n\t\t\tassertThat(opaqueToken.getIntrospector()).isNotNull();\n\t\t});\n\t}\n\n\t@Test\n\tpublic void requestWhenBasicAndResourceServerEntryPointsThenMatchedByRequest() throws Exception {\n\t\tthis.spring.register(BasicAndResourceServerConfig.class, JwtDecoderConfig.class).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willThrow(BadJwtException.class);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(httpBasic(\"some\", \"user\")))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Basic\")));\n\t\tthis.mvc.perform(get(\"/authenticated\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Basic\")));\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"invalid_token\")))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenFormLoginAndResourceServerEntryPointsThenSessionCreatedByRequest() throws Exception {\n\t\tthis.spring.register(FormAndResourceServerConfig.class, JwtDecoderConfig.class).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willThrow(BadJwtException.class);\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/authenticated\")\n\t\t\t\t.header(\"Accept\", \"text/html\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t\t// @formatter:off\n\t\tresult = this.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void unauthenticatedRequestWhenFormOAuth2LoginAndResourceServerThenNegotiates() throws Exception {\n\t\tthis.spring.register(OAuth2LoginAndResourceServerConfig.class, JwtDecoderConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/any\").header(\"X-Requested-With\", \"XMLHttpRequest\")).andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/any\").header(\"Accept\", \"application/json\")).andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/any\").header(\"Accept\", \"text/html\")).andExpect(status().is3xxRedirection());\n\t\tthis.mvc.perform(get(\"/any\").header(\"Accept\", \"image/jpg\")).andExpect(status().is3xxRedirection());\n\t}\n\n\t@Test\n\tpublic void requestWhenDefaultAndResourceServerAccessDeniedHandlersThenMatchedByRequest() throws Exception {\n\t\tthis.spring\n\t\t\t.register(ExceptionHandlingAndResourceServerWithAccessDeniedHandlerConfig.class, JwtDecoderConfig.class)\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(JWT);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(httpBasic(\"basic-user\", \"basic-password\")))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(header().doesNotExist(HttpHeaders.WWW_AUTHENTICATE));\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"insufficiently_scoped\")))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenAlsoUsingHttpBasicThenCorrectProviderEngages() throws Exception {\n\t\tthis.spring.register(RestOperationsConfig.class, BasicAndResourceServerConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(token)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\tthis.mvc.perform(get(\"/authenticated\").with(httpBasic(\"basic-user\", \"basic-password\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"basic-user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getAuthenticationManagerWhenConfiguredAuthenticationManagerThenTakesPrecedence() {\n\t\tApplicationContext context = mock(ApplicationContext.class);\n\t\tOAuth2ResourceServerConfigurer<HttpSecurity> oauth2ResourceServer = new OAuth2ResourceServerConfigurer<>(\n\t\t\t\tcontext);\n\t\tAuthenticationManager authenticationManager = mock(AuthenticationManager.class);\n\t\toauth2ResourceServer\n\t\t\t.jwt((jwt) -> jwt.authenticationManager(authenticationManager).decoder(mock(JwtDecoder.class)));\n\t\tassertThat(oauth2ResourceServer.getAuthenticationManager(null)).isSameAs(authenticationManager);\n\t\toauth2ResourceServer = new OAuth2ResourceServerConfigurer<>(context);\n\t\toauth2ResourceServer.opaqueToken((opaqueToken) -> opaqueToken.authenticationManager(authenticationManager)\n\t\t\t.introspector(mock(OpaqueTokenIntrospector.class)));\n\t\tassertThat(oauth2ResourceServer.getAuthenticationManager(null)).isSameAs(authenticationManager);\n\t}\n\n\t@Test\n\tpublic void getWhenMultipleIssuersThenUsesIssuerClaimToDifferentiate() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, MultipleIssuersConfig.class, BasicController.class).autowire();\n\t\tMockWebServer server = this.spring.getContext().getBean(MockWebServer.class);\n\t\tString metadata = \"{\\n\" + \"    \\\"issuer\\\": \\\"%s\\\", \\n\" + \"    \\\"jwks_uri\\\": \\\"%s/.well-known/jwks.json\\\" \\n\"\n\t\t\t\t+ \"}\";\n\t\tString jwkSet = jwkSet();\n\t\tString issuerOne = server.url(\"/issuerOne\").toString();\n\t\tString issuerTwo = server.url(\"/issuerTwo\").toString();\n\t\tString issuerThree = server.url(\"/issuerThree\").toString();\n\t\tString jwtOne = jwtFromIssuer(issuerOne);\n\t\tString jwtTwo = jwtFromIssuer(issuerTwo);\n\t\tString jwtThree = jwtFromIssuer(issuerThree);\n\t\tmockWebServer(String.format(metadata, issuerOne, issuerOne));\n\t\tmockWebServer(jwkSet);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(jwtOne)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\t// @formatter:on\n\t\tmockWebServer(String.format(metadata, issuerTwo, issuerTwo));\n\t\tmockWebServer(jwkSet);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(jwtTwo)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\t// @formatter:on\n\t\tmockWebServer(String.format(metadata, issuerThree, issuerThree));\n\t\tmockWebServer(jwkSet);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(jwtThree)))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"Invalid issuer\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configuredWhenMissingJwtAuthenticationProviderThenWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(JwtlessConfig.class).autowire())\n\t\t\t.withMessageContaining(\"neither was found\");\n\t}\n\n\t@Test\n\tpublic void configureWhenMissingJwkSetUriThenWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(JwtHalfConfiguredConfig.class).autowire())\n\t\t\t.withMessageContaining(\"No qualifying bean of type\");\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingBothJwtAndOpaqueThenWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(OpaqueAndJwtConfig.class).autowire())\n\t\t\t.withMessageContaining(\"Spring Security only supports JWTs or Opaque Tokens\");\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingBothAuthenticationManagerResolverAndOpaqueThenWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(AuthenticationManagerResolverPlusOtherConfig.class).autowire())\n\t\t\t.withMessageContaining(\"authenticationManagerResolver\");\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenNoConverterSpecifiedThenTheDefaultIsUsed() {\n\t\tApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context)\n\t\t\t.jwt((jwt) -> assertThat(jwt.getJwtAuthenticationConverter())\n\t\t\t\t.isInstanceOf(JwtAuthenticationConverter.class));\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenConverterBeanSpecified() {\n\t\tJwtAuthenticationConverter converterBean = new JwtAuthenticationConverter();\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(JwtAuthenticationConverter.class, () -> converterBean);\n\t\tthis.spring.context(context).autowire();\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context)\n\t\t\t.jwt((jwt) -> assertThat(jwt.getJwtAuthenticationConverter()).isEqualTo(converterBean));\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenConverterBeanAndAnotherOnTheDslThenTheDslOneIsUsed() {\n\t\tJwtAuthenticationConverter converter = new JwtAuthenticationConverter();\n\t\tJwtAuthenticationConverter converterBean = new JwtAuthenticationConverter();\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(JwtAuthenticationConverter.class, () -> converterBean);\n\t\tthis.spring.context(context).autowire();\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).jwt((jwt) -> {\n\t\t\tjwt.jwtAuthenticationConverter(converter);\n\t\t\tassertThat(jwt.getJwtAuthenticationConverter()).isEqualTo(converter);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenDuplicateConverterBeansAndAnotherOnTheDslThenTheDslOneIsUsed() {\n\t\tJwtAuthenticationConverter converter = new JwtAuthenticationConverter();\n\t\tJwtAuthenticationConverter converterBean = new JwtAuthenticationConverter();\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(\"converterOne\", JwtAuthenticationConverter.class, () -> converterBean);\n\t\tcontext.registerBean(\"converterTwo\", JwtAuthenticationConverter.class, () -> converterBean);\n\t\tthis.spring.context(context).autowire();\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context).jwt((jwt) -> {\n\t\t\tjwt.jwtAuthenticationConverter(converter);\n\t\t\tassertThat(jwt.getJwtAuthenticationConverter()).isEqualTo(converter);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenDuplicateConverterBeansThenThrowsException() {\n\t\tJwtAuthenticationConverter converterBean = new JwtAuthenticationConverter();\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(\"converterOne\", JwtAuthenticationConverter.class, () -> converterBean);\n\t\tcontext.registerBean(\"converterTwo\", JwtAuthenticationConverter.class, () -> converterBean);\n\t\tthis.spring.context(context).autowire();\n\t\tnew OAuth2ResourceServerConfigurer<HttpSecurity>(context)\n\t\t\t.jwt((jwt) -> assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)\n\t\t\t\t.isThrownBy(jwt::getJwtAuthenticationConverter));\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthenticationConverterThenUsed() throws Exception {\n\t\tthis.spring\n\t\t\t.register(RestOperationsConfig.class, OpaqueTokenAuthenticationConverterConfig.class, BasicController.class)\n\t\t\t.autowire();\n\t\tOpaqueTokenAuthenticationConverter authenticationConverter = bean(OpaqueTokenAuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(anyString(), any(OAuth2AuthenticatedPrincipal.class)))\n\t\t\t.willReturn(new TestingAuthenticationToken(\"jdoe\", null, Collections.emptyList()));\n\t\tmockJsonRestOperations(json(\"Active\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(bearerToken(\"token\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"jdoe\"));\n\t\t// @formatter:on\n\t\tverify(authenticationConverter).convert(any(), any());\n\t}\n\n\t@Test\n\tpublic void getAuthenticationConverterWhenDuplicateConverterBeansAndAnotherOnTheDslThenTheDslOneIsUsed() {\n\t\tAuthenticationConverter converter = mock(AuthenticationConverter.class);\n\t\tAuthenticationConverter converterBean = mock(AuthenticationConverter.class);\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(\"converterOne\", AuthenticationConverter.class, () -> converterBean);\n\t\tcontext.registerBean(\"converterTwo\", AuthenticationConverter.class, () -> converterBean);\n\t\tthis.spring.context(context).autowire();\n\t\tOAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);\n\t\toauth2.authenticationConverter(converter);\n\t\tassertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);\n\t}\n\n\t@Test\n\tpublic void getAuthenticationConverterWhenConverterBeanAndAnotherOnTheDslThenTheDslOneIsUsed() {\n\t\tAuthenticationConverter converter = mock(AuthenticationConverter.class);\n\t\tAuthenticationConverter converterBean = mock(AuthenticationConverter.class);\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(AuthenticationConverter.class, () -> converterBean);\n\t\tthis.spring.context(context).autowire();\n\t\tOAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);\n\t\toauth2.authenticationConverter(converter);\n\t\tassertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);\n\t}\n\n\t@Test\n\tpublic void getAuthenticationConverterWhenDuplicateConverterBeansThenWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.spring.register(MultipleAuthenticationConverterBeansConfig.class, JwtDecoderConfig.class)\n\t\t\t\t\t\t.autowire())\n\t\t\t.withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class);\n\t}\n\n\t@Test\n\tpublic void getAuthenticationConverterWhenNoConverterSpecifiedThenTheDefaultIsUsed() {\n\t\tApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();\n\t\tOAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);\n\t\tassertThat(oauth2.getAuthenticationConverter()).isInstanceOf(BearerTokenAuthenticationConverter.class);\n\t}\n\n\tprivate static <T> void registerMockBean(GenericApplicationContext context, String name, Class<T> clazz) {\n\t\tcontext.registerBean(name, clazz, () -> mock(clazz));\n\t}\n\n\tprivate static BearerTokenRequestPostProcessor bearerToken(String token) {\n\t\treturn new BearerTokenRequestPostProcessor(token);\n\t}\n\n\tprivate static ResultMatcher invalidRequestHeader(String message) {\n\t\treturn header().string(HttpHeaders.WWW_AUTHENTICATE,\n\t\t\t\tAllOf.allOf(new StringStartsWith(\"Bearer \" + \"error=\\\"invalid_request\\\", \" + \"error_description=\\\"\"),\n\t\t\t\t\t\tnew StringContains(message),\n\t\t\t\t\t\tnew StringContains(\", \" + \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\"),\n\t\t\t\t\t\tnew StringEndsWith(\n\t\t\t\t\t\t\t\t\", \" + \"resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\")));\n\t}\n\n\tprivate static ResultMatcher invalidTokenHeader(String message) {\n\t\treturn header().string(HttpHeaders.WWW_AUTHENTICATE,\n\t\t\t\tAllOf.allOf(new StringStartsWith(\"Bearer \" + \"error=\\\"invalid_token\\\", \" + \"error_description=\\\"\"),\n\t\t\t\t\t\tnew StringContains(message),\n\t\t\t\t\t\tnew StringContains(\", \" + \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\"),\n\t\t\t\t\t\tnew StringEndsWith(\n\t\t\t\t\t\t\t\t\", \" + \"resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\")));\n\t}\n\n\tprivate static ResultMatcher insufficientScopeHeader() {\n\t\treturn header().string(HttpHeaders.WWW_AUTHENTICATE, \"Bearer \" + \"error=\\\"insufficient_scope\\\"\"\n\t\t\t\t+ \", error_description=\\\"The request requires higher privileges than provided by the access token.\\\"\"\n\t\t\t\t+ \", error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\");\n\t}\n\n\tprivate String jwkSet() {\n\t\treturn new JWKSet(new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyID(\"1\").build()).toString();\n\t}\n\n\tprivate String jwtFromIssuer(String issuer) throws Exception {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(JwtClaimNames.ISS, issuer);\n\t\tclaims.put(JwtClaimNames.SUB, \"test-subject\");\n\t\tclaims.put(\"scope\", \"message:read\");\n\t\tJWSObject jws = new JWSObject(new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(\"1\").build(),\n\t\t\t\tnew Payload(new JSONObject(claims)));\n\t\tjws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));\n\t\treturn jws.serialize();\n\t}\n\n\tprivate void mockWebServer(String response) {\n\t\tthis.web.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.setBody(response));\n\t}\n\n\tprivate void mockRestOperations(String response) {\n\t\tRestOperations rest = this.spring.getContext().getBean(RestOperations.class);\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\tResponseEntity<String> entity = new ResponseEntity<>(response, headers, HttpStatus.OK);\n\t\tgiven(rest.exchange(any(RequestEntity.class), eq(String.class))).willReturn(entity);\n\t}\n\n\tprivate void mockJwksRestOperations(String response) {\n\t\tRestOperations rest = this.spring.getContext().getBean(RestOperations.class);\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\tResponseEntity<String> entity = new ResponseEntity<>(response, headers, HttpStatus.OK);\n\t\tgiven(rest.exchange(any(RequestEntity.class), eq(String.class))).willReturn(entity);\n\t}\n\n\tprivate void mockJsonRestOperations(String response) {\n\t\ttry {\n\t\t\tRestOperations rest = this.spring.getContext().getBean(RestOperations.class);\n\t\t\tHttpHeaders headers = new HttpHeaders();\n\t\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\t\tResponseEntity<Map<String, Object>> entity = new ResponseEntity<>(JSONObjectUtils.parse(response), headers,\n\t\t\t\t\tHttpStatus.OK);\n\t\t\tgiven(rest.exchange(any(RequestEntity.class), eq(new ParameterizedTypeReference<Map<String, Object>>() {\n\t\t\t}))).willReturn(entity);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate <T> T bean(Class<T> beanClass) {\n\t\treturn this.spring.getContext().getBean(beanClass);\n\t}\n\n\tprivate <T> T verifyBean(Class<T> beanClass) {\n\t\treturn verify(this.spring.getContext().getBean(beanClass));\n\t}\n\n\tprivate <T> T verifyBean(Class<T> beanClass, VerificationMode mode) {\n\t\treturn verify(this.spring.getContext().getBean(beanClass), mode);\n\t}\n\n\tprivate String json(String name) throws IOException {\n\t\treturn resource(name + \".json\");\n\t}\n\n\tprivate String jwks(String name) throws IOException {\n\t\treturn resource(name + \".jwks\");\n\t}\n\n\tprivate String token(String name) throws IOException {\n\t\treturn resource(name + \".token\");\n\t}\n\n\tprivate String resource(String suffix) throws IOException {\n\t\tString name = this.getClass().getSimpleName() + \"-\" + suffix;\n\t\tClassPathResource resource = new ClassPathResource(name, this.getClass());\n\t\ttry (BufferedReader reader = new BufferedReader(new FileReader(resource.getFile()))) {\n\t\t\treturn reader.lines().collect(Collectors.joining());\n\t\t}\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class DefaultConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class DefaultInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.jwt(withDefaults())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class JwkSetUriConfig {\n\n\t\t@Value(\"${mockwebserver.url:https://example.org}\")\n\t\tString jwkSetUri;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tDefaultBearerTokenResolver defaultBearerTokenResolver = new DefaultBearerTokenResolver();\n\t\t\tdefaultBearerTokenResolver.setAllowUriQueryParameter(true);\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.bearerTokenResolver(defaultBearerTokenResolver)\n\t\t\t\t\t.jwt((jwt) -> jwt.jwkSetUri(this.jwkSetUri)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class JwkSetUriInLambdaConfig {\n\n\t\t@Value(\"${mockwebserver.url:https://example.org}\")\n\t\tString jwkSetUri;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t\t\t.jwkSetUri(this.jwkSetUri)\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class CsrfDisabledConfig {\n\n\t\t@Value(\"${mockwebserver.url:https://example.org}\")\n\t\tString jwkSetUri;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt.jwkSetUri(this.jwkSetUri)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AnonymousDisabledConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.anonymous((anonymous) -> anonymous.disable())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class MethodSecurityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class JwtlessConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RealmNameConfiguredOnEntryPoint {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.authenticationEntryPoint(authenticationEntryPoint())\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tAuthenticationEntryPoint authenticationEntryPoint() {\n\t\t\tBearerTokenAuthenticationEntryPoint entryPoint = new BearerTokenAuthenticationEntryPoint();\n\t\t\tentryPoint.setRealmName(\"myRealm\");\n\t\t\treturn entryPoint;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class RealmNameConfiguredOnAccessDeniedHandler {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.accessDeniedHandler(accessDeniedHandler())\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tAccessDeniedHandler accessDeniedHandler() {\n\t\t\tBearerTokenAccessDeniedHandler accessDeniedHandler = new BearerTokenAccessDeniedHandler();\n\t\t\taccessDeniedHandler.setRealmName(\"myRealm\");\n\t\t\treturn accessDeniedHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ExceptionHandlingAndResourceServerWithAccessDeniedHandlerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().denyAll())\n\t\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t\t.defaultAccessDeniedHandlerFor(new AccessDeniedHandlerImpl(), (request) -> false))\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\t\torg.springframework.security.core.userdetails.User.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"basic-user\")\n\t\t\t\t\t\t\t.password(\"basic-password\")\n\t\t\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t\t\t.build());\n\t\t\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class JwtAuthenticationConverterConfiguredOnDsl {\n\n\t\tprivate final Converter<Jwt, JwtAuthenticationToken> jwtAuthenticationConverter = mock(Converter.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t.jwtAuthenticationConverter(getJwtAuthenticationConverter())));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tConverter<Jwt, JwtAuthenticationToken> getJwtAuthenticationConverter() {\n\t\t\treturn this.jwtAuthenticationConverter;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class CustomAuthorityMappingConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"message:read\"))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t.jwtAuthenticationConverter(getJwtAuthenticationConverter())));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tConverter<Jwt, AbstractAuthenticationToken> getJwtAuthenticationConverter() {\n\t\t\tJwtAuthenticationConverter converter = new JwtAuthenticationConverter();\n\t\t\tconverter.setJwtGrantedAuthoritiesConverter(\n\t\t\t\t\t(jwt) -> Collections.singletonList(new SimpleGrantedAuthority(\"message:read\")));\n\t\t\treturn converter;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class BasicAndResourceServerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(\n\t\t\t// @formatter:off\n\t\t\t\t\torg.springframework.security.core.userdetails.User.withDefaultPasswordEncoder()\n\t\t\t\t\t\t\t.username(\"basic-user\")\n\t\t\t\t\t\t\t.password(\"basic-password\")\n\t\t\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t\t\t.build());\n\t\t\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class FormAndResourceServerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OAuth2LoginAndResourceServerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2Login(withDefaults())\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2.jwt(withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clients() {\n\t\t\tClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\t\t\treturn new InMemoryClientRegistrationRepository(registration);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class JwtHalfConfiguredConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build(); // missing key configuration, e.g. jwkSetUri\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AlwaysSessionCreationConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AllowBearerTokenInRequestBodyConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.bearerTokenResolver(allowRequestBody())\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate BearerTokenResolver allowRequestBody() {\n\t\t\tDefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();\n\t\t\tresolver.setAllowFormEncodedBodyParameter(true);\n\t\t\treturn resolver;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AllowBearerTokenAsQueryParameterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tBearerTokenResolver allowQueryParameter() {\n\t\t\tDefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();\n\t\t\tresolver.setAllowUriQueryParameter(true);\n\t\t\treturn resolver;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MultipleBearerTokenResolverBeansConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tBearerTokenResolver resolverOne() {\n\t\t\tDefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();\n\t\t\tresolver.setAllowUriQueryParameter(true);\n\t\t\treturn resolver;\n\t\t}\n\n\t\t@Bean\n\t\tBearerTokenResolver resolverTwo() {\n\t\t\tDefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();\n\t\t\tresolver.setAllowFormEncodedBodyParameter(true);\n\t\t\treturn resolver;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthenticationDetailsSource {\n\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t.jwt(withDefaults())\n\t\t\t\t\t.withObjectPostProcessor(new ObjectPostProcessor<BearerTokenAuthenticationFilter>() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic BearerTokenAuthenticationFilter postProcess(BearerTokenAuthenticationFilter object) {\n\t\t\t\t\t\t\tobject.setAuthenticationDetailsSource(CustomAuthenticationDetailsSource.this.authenticationDetailsSource);\n\t\t\t\t\t\t\treturn object;\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource() {\n\t\t\treturn this.authenticationDetailsSource;\n\t\t}\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomJwtDecoderOnDsl {\n\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt.decoder(decoder())));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tJwtDecoder decoder() {\n\t\t\treturn this.decoder;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomJwtDecoderInLambdaOnDsl {\n\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t\t\t.decoder(decoder())\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tJwtDecoder decoder() {\n\t\t\treturn this.decoder;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomJwtDecoderAsBean {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder decoder() {\n\t\t\treturn mock(JwtDecoder.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class JwtAuthenticationManagerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t.authenticationManager(authenticationProvider()::authenticate)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationProvider authenticationProvider() {\n\t\t\treturn mock(AuthenticationProvider.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultAndJwtAuthenticationManagerConfig {\n\n\t\tAuthenticationManager defaultAuthenticationManager = mock(AuthenticationManager.class);\n\n\t\tAuthenticationManager jwtAuthenticationManager = mock(AuthenticationManager.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authenticationManager(this.defaultAuthenticationManager)\n\t\t\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t\t\t\t.authenticationManager(this.jwtAuthenticationManager)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tAuthenticationManager defaultAuthenticationManager() {\n\t\t\treturn this.defaultAuthenticationManager;\n\t\t}\n\n\t\tAuthenticationManager jwtAuthenticationManager() {\n\t\t\treturn this.jwtAuthenticationManager;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomJwtValidatorConfig {\n\n\t\t@Autowired\n\t\tNimbusJwtDecoder jwtDecoder;\n\n\t\tprivate final OAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tthis.jwtDecoder.setJwtValidator(this.jwtValidator);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tOAuth2TokenValidator<Jwt> getJwtValidator() {\n\t\t\treturn this.jwtValidator;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class UnexpiredJwtClockSkewConfig {\n\n\t\t@Autowired\n\t\tNimbusJwtDecoder jwtDecoder;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tClock nearlyAnHourFromTokenExpiry = Clock.fixed(Instant.ofEpochMilli(4687181540000L),\n\t\t\t\t\tZoneId.systemDefault());\n\t\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator(Duration.ofHours(1));\n\t\t\tjwtValidator.setClock(nearlyAnHourFromTokenExpiry);\n\t\t\tthis.jwtDecoder.setJwtValidator(jwtValidator);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ExpiredJwtClockSkewConfig {\n\n\t\t@Autowired\n\t\tNimbusJwtDecoder jwtDecoder;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tClock justOverOneHourAfterExpiry = Clock.fixed(Instant.ofEpochMilli(4687181595000L),\n\t\t\t\t\tZoneId.systemDefault());\n\t\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator(Duration.ofHours(1));\n\t\t\tjwtValidator.setClock(justOverOneHourAfterExpiry);\n\t\t\tthis.jwtDecoder.setJwtValidator(jwtValidator);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t}\n\t}\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SingleKeyConfig {\n\t\tbyte[] spec = Base64.getDecoder().decode(\n\t\t\t\t\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoXJ8OyOv/eRnce4akdan\" +\n\t\t\t\t\"R4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2\" +\n\t\t\t\t\"UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48O\" +\n\t\t\t\t\"cz06PGF3lhbz4t5UEZtdF4rIe7u+977QwHuh7yRPBQ3sII+cVoOUMgaXB9SHcGF2\" +\n\t\t\t\t\"iZCtPzL/IffDUcfhLQteGebhW8A6eUHgpD5A1PQ+JCw/G7UOzZAjjDjtNM2eqm8j\" +\n\t\t\t\t\"+Ms/gqnm4MiCZ4E+9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1Hu\" +\n\t\t\t\t\"QwIDAQAB\");\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder decoder() throws Exception {\n\t\t\tRSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance(\"RSA\")\n\t\t\t\t.generatePublic(new X509EncodedKeySpec(this.spec));\n\t\t\treturn NimbusJwtDecoder.withPublicKey(publicKey).build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomAuthenticationEventPublisher {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder() {\n\t\t\treturn mock(JwtDecoder.class);\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationEventPublisher authenticationEventPublisher() {\n\t\t\treturn mock(AuthenticationEventPublisher.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class OpaqueTokenConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.opaqueToken(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class OpaqueTokenInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.opaqueToken(withDefaults())\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OpaqueTokenAuthenticationManagerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.opaqueToken((opaqueToken) -> opaqueToken\n\t\t\t\t\t\t.authenticationManager(authenticationProvider()::authenticate)));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationProvider authenticationProvider() {\n\t\t\treturn mock(AuthenticationProvider.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OpaqueTokenAuthenticationManagerInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.opaqueToken((opaqueToken) -> opaqueToken\n\t\t\t\t\t\t\t\t.authenticationManager(authenticationProvider()::authenticate)\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationProvider authenticationProvider() {\n\t\t\treturn mock(AuthenticationProvider.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultAndOpaqueTokenAuthenticationManagerConfig {\n\n\t\tAuthenticationManager defaultAuthenticationManager = mock(AuthenticationManager.class);\n\n\t\tAuthenticationManager opaqueTokenAuthenticationManager = mock(AuthenticationManager.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authenticationManager(this.defaultAuthenticationManager)\n\t\t\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t\t.opaqueToken((opaque) -> opaque\n\t\t\t\t\t\t\t\t\t.authenticationManager(this.opaqueTokenAuthenticationManager)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tAuthenticationManager defaultAuthenticationManager() {\n\t\t\treturn this.defaultAuthenticationManager;\n\t\t}\n\n\t\tAuthenticationManager opaqueTokenAuthenticationManager() {\n\t\t\treturn this.opaqueTokenAuthenticationManager;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OpaqueAndJwtConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults())\n\t\t\t\t\t.opaqueToken(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class OpaqueTokenHalfConfiguredConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.opaqueToken((opaqueToken) -> opaqueToken\n\t\t\t\t\t\t.introspectionUri(\"https://idp.example.com\")));\n\t\t\treturn http.build(); // missing credentials\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MultipleAuthenticationConverterBeansConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationConverter authenticationConverterOne() {\n\t\t\tDefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();\n\t\t\tresolver.setAllowUriQueryParameter(true);\n\t\t\tBearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();\n\t\t\tauthenticationConverter.setBearerTokenResolver(resolver);\n\t\t\treturn authenticationConverter;\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationConverter authenticationConverterTwo() {\n\t\t\tDefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();\n\t\t\tresolver.setAllowUriQueryParameter(true);\n\t\t\tBearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();\n\t\t\tauthenticationConverter.setBearerTokenResolver(resolver);\n\t\t\treturn authenticationConverter;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class MultipleIssuersConfig {\n\n\t\t@Autowired\n\t\tMockWebServer web;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tString issuerOne = this.web.url(\"/issuerOne\").toString();\n\t\t\tString issuerTwo = this.web.url(\"/issuerTwo\").toString();\n\t\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n\t\t\t\t.fromTrustedIssuers(issuerOne, issuerTwo);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.authenticationManagerResolver(authenticationManagerResolver))\n\t\t\t\t.anonymous(AbstractHttpConfigurer::disable);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class AuthenticationManagerResolverPlusOtherConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.authenticationManagerResolver(mock(AuthenticationManagerResolver.class))\n\t\t\t\t\t.opaqueToken(Customizer.withDefaults()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class OpaqueTokenAuthenticationConverterConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/requires-read-scope\").hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.opaqueToken((opaqueToken) -> opaqueToken\n\t\t\t\t\t\t.authenticationConverter(authenticationConverter())));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tOpaqueTokenAuthenticationConverter authenticationConverter() {\n\t\t\treturn mock(OpaqueTokenAuthenticationConverter.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class JwtDecoderConfig {\n\n\t\t@Bean\n\t\tJwtDecoder jwtDecoder() {\n\t\t\treturn mock(JwtDecoder.class);\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@GetMapping(\"/\")\n\t\tString get() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t\t@PostMapping(\"/post\")\n\t\tString post() {\n\t\t\treturn \"post\";\n\t\t}\n\n\t\t@RequestMapping(value = \"/authenticated\", method = { RequestMethod.GET, RequestMethod.POST })\n\t\tString authenticated(Authentication authentication) {\n\t\t\treturn authentication.getName();\n\t\t}\n\n\t\t@GetMapping(\"/requires-read-scope\")\n\t\tString requiresReadScope(JwtAuthenticationToken token) {\n\t\t\treturn token.getAuthorities()\n\t\t\t\t.stream()\n\t\t\t\t.filter((ga) -> ga.getAuthority().startsWith(\"SCOPE_\"))\n\t\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t\t.collect(Collectors.toList())\n\t\t\t\t.toString();\n\t\t}\n\n\t\t@GetMapping(\"/ms-requires-read-scope\")\n\t\t@PreAuthorize(\"hasAuthority('SCOPE_message:read')\")\n\t\tString msRequiresReadScope(JwtAuthenticationToken token) {\n\t\t\treturn requiresReadScope(token);\n\t\t}\n\n\t\t@GetMapping(\"/ms-deny\")\n\t\t@PreAuthorize(\"denyAll\")\n\t\tString deny() {\n\t\t\treturn \"hmm, that's odd\";\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class WebServerConfig implements BeanPostProcessor, EnvironmentAware {\n\n\t\tprivate final MockWebServer server = new MockWebServer();\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.server.shutdown();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setEnvironment(Environment environment) {\n\t\t\tif (environment instanceof ConfigurableEnvironment) {\n\t\t\t\t((ConfigurableEnvironment) environment).getPropertySources()\n\t\t\t\t\t.addFirst(new MockWebServerPropertySource());\n\t\t\t}\n\t\t}\n\n\t\t@Bean\n\t\tMockWebServer web() {\n\t\t\treturn this.server;\n\t\t}\n\n\t\tprivate class MockWebServerPropertySource extends PropertySource {\n\n\t\t\tMockWebServerPropertySource() {\n\t\t\t\tsuper(\"mockwebserver\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object getProperty(String name) {\n\t\t\t\tif (\"mockwebserver.url\".equals(name)) {\n\t\t\t\t\treturn WebServerConfig.this.server.url(\"/.well-known/jwks.json\").toString();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class RestOperationsConfig {\n\n\t\tRestOperations rest = mock(RestOperations.class);\n\n\t\t@Bean\n\t\tRestOperations rest() {\n\t\t\treturn this.rest;\n\t\t}\n\n\t\t@Bean\n\t\tNimbusJwtDecoder jwtDecoder() {\n\t\t\treturn NimbusJwtDecoder.withJwkSetUri(\"https://example.org/.well-known/jwks.json\")\n\t\t\t\t.restOperations(this.rest)\n\t\t\t\t.build();\n\t\t}\n\n\t\t@Bean\n\t\tOpaqueTokenIntrospector tokenIntrospectionClient() {\n\t\t\treturn new SpringOpaqueTokenIntrospector(\"https://example.org/introspect\", this.rest);\n\t\t}\n\n\t}\n\n\tprivate static class BearerTokenRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate boolean asRequestParameter;\n\n\t\tprivate String token;\n\n\t\tBearerTokenRequestPostProcessor(String token) {\n\t\t\tthis.token = token;\n\t\t}\n\n\t\tBearerTokenRequestPostProcessor asParam() {\n\t\t\tthis.asRequestParameter = true;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tif (this.asRequestParameter) {\n\t\t\t\trequest.setParameter(\"access_token\", this.token);\n\t\t\t}\n\t\t\telse {\n\t\t\t\trequest.addHeader(\"Authorization\", \"Bearer \" + this.token);\n\t\t\t}\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.ott;\n\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.hamcrest.Matchers;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.authentication.ott.DefaultOneTimeToken;\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenService;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringTestContextExtension.class)\npublic class OneTimeTokenLoginConfigurerTests {\n\n\tpublic SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tMockMvc mvc;\n\n\t@Autowired(required = false)\n\tprivate GenerateOneTimeTokenRequestResolver resolver;\n\n\t@Autowired(required = false)\n\tprivate OneTimeTokenService tokenService;\n\n\t@Autowired(required = false)\n\tprivate OneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler;\n\n\t@Test\n\tvoid oneTimeTokenWhenCorrectTokenThenCanAuthenticate() throws Exception {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/ott/generate\").param(\"username\", \"user\").with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/login/ott\"));\n\n\t\tString token = getLastToken().getTokenValue();\n\n\t\tthis.mvc.perform(post(\"/login/ott\").param(\"token\", token).with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/\"), authenticated());\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenDifferentAuthenticationUrlsThenCanAuthenticate() throws Exception {\n\t\tthis.spring.register(OneTimeTokenDifferentUrlsConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/generateurl\").param(\"username\", \"user\").with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/redirected\"));\n\n\t\tString token = getLastToken().getTokenValue();\n\n\t\tthis.mvc.perform(post(\"/loginprocessingurl\").param(\"token\", token).with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/authenticated\"), authenticated());\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenCorrectTokenUsedTwiceThenSecondTimeFails() throws Exception {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/ott/generate\").param(\"username\", \"user\").with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/login/ott\"));\n\n\t\tString token = getLastToken().getTokenValue();\n\n\t\tthis.mvc.perform(post(\"/login/ott\").param(\"token\", token).with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/\"), authenticated());\n\n\t\tthis.mvc.perform(post(\"/login/ott\").param(\"token\", token).with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/login?error\"), unauthenticated());\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenWrongTokenThenAuthenticationFail() throws Exception {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/ott/generate\").param(\"username\", \"user\").with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/login/ott\"));\n\n\t\tString token = \"wrong\";\n\n\t\tthis.mvc.perform(post(\"/login/ott\").param(\"token\", token).with(csrf()))\n\t\t\t.andExpectAll(status().isFound(), redirectedUrl(\"/login?error\"), unauthenticated());\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenConfiguredThenServesCss() throws Exception {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/default-ui.css\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(Matchers.containsString(\"body {\")));\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenConfiguredThenRendersRequestTokenForm() throws Exception {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"BaseSpringSpec_CSRFTOKEN\");\n\t\tString csrfAttributeName = HttpSessionCsrfTokenRepository.class.getName().concat(\".CSRF_TOKEN\");\n\t\t//@formatter:off\n\t\tthis.mvc.perform(get(\"/login\").sessionAttr(csrfAttributeName, csrfToken))\n\t\t\t\t.andExpect((result) -> {\n\t\t\t\t\tCsrfToken token = (CsrfToken) result.getRequest().getAttribute(CsrfToken.class.getName());\n\t\t\t\t\tassertThat(result.getResponse().getContentAsString()).isEqualTo(\n\t\t\t\t\t\t\"\"\"\n\t\t\t\t\t\t<!DOCTYPE html>\n\t\t\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t\t  <head>\n\t\t\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t\t  </head>\n\t\t\t\t\t\t  <body>\n\t\t\t\t\t\t    <div class=\"content\">\n\n\t\t\t\t\t\t      <form id=\"ott-form\" class=\"login-form\" method=\"post\" action=\"/ott/generate\">\n\t\t\t\t\t\t        <h2>Request a One-Time Token</h2>\n\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"ott-username\" class=\"screenreader\">Username</label>\n\t\t\t\t\t\t          <input type=\"text\" id=\"ott-username\" name=\"username\" placeholder=\"Username\" required>\n\t\t\t\t\t\t        </p>\n\t\t\t\t\t\t<input name=\"_csrf\" type=\"hidden\" value=\"%s\" />\n\t\t\t\t\t\t        <button class=\"primary\" type=\"submit\" form=\"ott-form\">Send Token</button>\n\t\t\t\t\t\t      </form>\n\n\n\t\t\t\t\t\t    </div>\n\t\t\t\t\t\t  </body>\n\t\t\t\t\t\t</html>\"\"\".formatted(token.getToken(), token.getToken()));\n\t\t\t\t});\n\t\t//@formatter:on\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenLoginPageConfiguredThenRedirects() throws Exception {\n\t\tthis.spring.register(OneTimeTokenLoginPageConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/login\")).andExpect(status().isFound()).andExpect(redirectedUrl(\"/custom-login\"));\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenNoTokenGenerationSuccessHandlerThenException() {\n\t\tassertThatException()\n\t\t\t.isThrownBy(() -> this.spring.register(OneTimeTokenNoGeneratedOttHandlerConfig.class).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.isInstanceOf(IllegalStateException.class)\n\t\t\t.withMessage(\"\"\"\n\t\t\t\t\tA OneTimeTokenGenerationSuccessHandler is required to enable oneTimeTokenLogin().\n\t\t\t\t\tPlease provide it as a bean or pass it to the oneTimeTokenLogin() DSL.\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenCustomTokenExpirationTimeSetThenAuthenticate() throws Exception {\n\t\tthis.spring.register(OneTimeTokenConfigWithCustomImpls.class).autowire();\n\t\tGenerateOneTimeTokenRequest expectedGenerateRequest = new GenerateOneTimeTokenRequest(\"username-123\",\n\t\t\t\tDuration.ofMinutes(10));\n\t\tOneTimeToken ott = new DefaultOneTimeToken(\"token-123\", expectedGenerateRequest.getUsername(),\n\t\t\t\tInstant.now().plus(expectedGenerateRequest.getExpiresIn()));\n\t\tgiven(this.resolver.resolve(any())).willReturn(expectedGenerateRequest);\n\t\tgiven(this.tokenService.generate(expectedGenerateRequest)).willReturn(ott);\n\t\tthis.mvc.perform(post(\"/ott/generate\").param(\"username\", \"user\").with(csrf()));\n\n\t\tverify(this.resolver).resolve(any());\n\t\tverify(this.tokenService).generate(expectedGenerateRequest);\n\t\tverify(this.tokenGenerationSuccessHandler).handle(any(), any(), eq(ott));\n\t}\n\n\tprivate OneTimeToken getLastToken() {\n\t\tOneTimeToken lastToken = this.spring.getContext()\n\t\t\t.getBean(TestOneTimeTokenGenerationSuccessHandler.class).lastToken;\n\t\treturn lastToken;\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenConfigWithCustomImpls {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http,\n\t\t\t\tGenerateOneTimeTokenRequestResolver ottRequestResolver, OneTimeTokenService ottTokenService,\n\t\t\t\tOneTimeTokenGenerationSuccessHandler ottSuccessHandler) throws Exception {\n\n\t\t\t// @formatter:off\n\t\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin((ott) -> ott\n\t\t\t\t\t\t\t.generateRequestResolver(ottRequestResolver)\n\t\t\t\t\t\t\t.tokenService(ottTokenService)\n\t\t\t\t\t\t\t.tokenGenerationSuccessHandler(ottSuccessHandler)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tGenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {\n\t\t\treturn mock(GenerateOneTimeTokenRequestResolver.class);\n\t\t}\n\n\t\t@Bean\n\t\tOneTimeTokenService ottService() {\n\t\t\treturn mock(OneTimeTokenService.class);\n\t\t}\n\n\t\t@Bean\n\t\tOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {\n\t\t\treturn mock(OneTimeTokenGenerationSuccessHandler.class);\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenDefaultConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http,\n\t\t\t\tOneTimeTokenGenerationSuccessHandler ottSuccessHandler) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin((ott) -> ott\n\t\t\t\t\t\t\t.tokenGenerationSuccessHandler(ottSuccessHandler)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tTestOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {\n\t\t\treturn new TestOneTimeTokenGenerationSuccessHandler();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenLoginPageConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http,\n\t\t\t\tOneTimeTokenGenerationSuccessHandler ottSuccessHandler) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin((ott) -> ott\n\t\t\t\t\t\t\t.tokenGenerationSuccessHandler(ottSuccessHandler)\n\t\t\t\t\t\t\t.loginPage(\"/custom-login\")\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tTestOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {\n\t\t\treturn new TestOneTimeTokenGenerationSuccessHandler();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenDifferentUrlsConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http,\n\t\t\t\tOneTimeTokenGenerationSuccessHandler ottSuccessHandler) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin((ott) -> ott\n\t\t\t\t\t\t\t.tokenGeneratingUrl(\"/generateurl\")\n\t\t\t\t\t\t\t.tokenGenerationSuccessHandler(ottSuccessHandler)\n\t\t\t\t\t\t\t.loginProcessingUrl(\"/loginprocessingurl\")\n\t\t\t\t\t\t\t.successHandler(new SimpleUrlAuthenticationSuccessHandler(\"/authenticated\"))\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tTestOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {\n\t\t\treturn new TestOneTimeTokenGenerationSuccessHandler(\"/redirected\");\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenNoGeneratedOttHandlerConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\tstatic class TestOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {\n\n\t\tprivate OneTimeToken lastToken;\n\n\t\tprivate final OneTimeTokenGenerationSuccessHandler delegate;\n\n\t\tTestOneTimeTokenGenerationSuccessHandler() {\n\t\t\tthis.delegate = new RedirectOneTimeTokenGenerationSuccessHandler(\"/login/ott\");\n\t\t}\n\n\t\tTestOneTimeTokenGenerationSuccessHandler(String redirectUrl) {\n\t\t\tthis.delegate = new RedirectOneTimeTokenGenerationSuccessHandler(redirectUrl);\n\t\t}\n\n\t\t@Override\n\t\tpublic void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tthis.lastToken = oneTimeToken;\n\t\t\tthis.delegate.handle(request, response, oneTimeToken);\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class UserDetailsServiceConfig {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.saml2;\n\nimport java.io.IOException;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.Collections;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.instancio.internal.util.ReflectionUtils;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextChangedListener;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2Utils;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;\nimport org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.context.HttpRequestResponseHolder;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.startsWith;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for different Java configuration for {@link Saml2LoginConfigurer}\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class Saml2LoginConfigurerTests {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate static final RelyingPartyRegistration registration = TestRelyingPartyRegistrations.noCredentials()\n\t\t.signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartySigningCredential()))\n\t\t.assertingPartyMetadata((party) -> party\n\t\t\t.verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))\n\t\t.build();\n\n\tprivate static String SIGNED_RESPONSE;\n\n\tprivate static final AuthenticationConverter AUTHENTICATION_CONVERTER = mock(AuthenticationConverter.class);\n\n\t@Autowired\n\tprivate ConfigurableApplicationContext context;\n\n\t@Autowired\n\tprivate FilterChainProxy springSecurityFilterChain;\n\n\t@Autowired\n\tprivate RelyingPartyRegistrationRepository repository;\n\n\t@Autowired\n\tSecurityContextRepository securityContextRepository;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tMockMvc mvc;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate MockFilterChain filterChain;\n\n\t@BeforeAll\n\tstatic void createResponse() throws Exception {\n\t\tString destination = registration.getAssertionConsumerServiceLocation();\n\t\tString assertingPartyEntityId = registration.getAssertingPartyMetadata().getEntityId();\n\t\tString relyingPartyEntityId = registration.getEntityId();\n\t\tResponse response = TestOpenSamlObjects.response(destination, assertingPartyEntityId);\n\t\tAssertion assertion = TestOpenSamlObjects.assertion(\"test@saml.user\", assertingPartyEntityId,\n\t\t\t\trelyingPartyEntityId, destination);\n\t\tresponse.getAssertions().add(assertion);\n\t\tResponse signed = TestOpenSamlObjects.signed(response,\n\t\t\t\tregistration.getSigningX509Credentials().iterator().next(), relyingPartyEntityId);\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(signed);\n\t\tElement element = marshaller.marshall(signed);\n\t\tClass<?> clazz = ReflectionUtils.loadClass(\"net.shibboleth.utilities.java.support.xml.SerializeSupport\");\n\t\tif (clazz == null) {\n\t\t\tclazz = ReflectionUtils.loadClass(\"net.shibboleth.shared.xml.SerializeSupport\");\n\t\t}\n\t\tString serialized = ReflectionTestUtils.invokeMethod(clazz, \"nodeToString\", element);\n\t\tSIGNED_RESPONSE = Saml2Utils.samlEncode(serialized.getBytes(StandardCharsets.UTF_8));\n\t}\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = TestMockHttpServletRequests.post(\"/login/saml2/sso/test-rp\").build();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.filterChain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void saml2LoginWhenDefaultsThenSaml2AuthenticatedPrincipal() throws Exception {\n\t\tthis.spring.register(Saml2LoginConfig.class, ResourceController.class).autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc\n\t\t\t\t.perform(post(\"/login/saml2/sso/registration-id\")\n\t\t\t\t.param(\"SAMLResponse\", SIGNED_RESPONSE))\n\t\t\t\t.andExpect(redirectedUrl(\"/\")).andReturn().getRequest().getSession(false);\n\t\tthis.mvc.perform(get(\"/\").session(session))\n\t\t\t\t.andExpect(content().string(\"test@saml.user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void saml2LoginWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring\n\t\t\t.register(Saml2LoginConfig.class, SecurityContextChangedListenerConfig.class, ResourceController.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc\n\t\t\t\t.perform(post(\"/login/saml2/sso/registration-id\")\n\t\t\t\t\t\t.param(\"SAMLResponse\", SIGNED_RESPONSE))\n\t\t\t\t.andExpect(redirectedUrl(\"/\")).andReturn().getRequest().getSession(false);\n\t\tthis.mvc.perform(get(\"/\").session(session))\n\t\t\t\t.andExpect(content().string(\"test@saml.user\"));\n\t\t// @formatter:on\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t\tSecurityContextChangedListener listener = this.spring.getContext()\n\t\t\t.getBean(SecurityContextChangedListener.class);\n\t\tverify(listener, times(2)).securityContextChanged(setAuthentication(Saml2Authentication.class));\n\t}\n\n\t@Test\n\tpublic void saml2LoginWhenConfiguringAuthenticationManagerThenTheManagerIsUsed() throws Exception {\n\t\t// setup application context\n\t\tthis.spring.register(Saml2LoginConfigWithCustomAuthenticationManager.class).autowire();\n\t\tperformSaml2Login(\"ROLE_AUTH_MANAGER\");\n\t}\n\n\t@Test\n\tpublic void saml2LoginWhenDefaultAndSamlAuthenticationManagerThenSamlManagerIsUsed() throws Exception {\n\t\tthis.spring.register(Saml2LoginConfigWithDefaultAndCustomAuthenticationManager.class).autowire();\n\t\tperformSaml2Login(\"ROLE_AUTH_MANAGER\");\n\t}\n\n\t@Test\n\tpublic void authenticationRequestWhenAuthenticationRequestResolverBeanThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationRequestResolverBean.class).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/saml2/authenticate/registration-id\")).andReturn();\n\t\tUriComponents components = UriComponentsBuilder.fromUriString(result.getResponse().getRedirectedUrl()).build();\n\t\tString samlRequest = components.getQueryParams().getFirst(\"SAMLRequest\");\n\t\tString decoded = URLDecoder.decode(samlRequest, \"UTF-8\");\n\t\tString inflated = Saml2Utils.samlInflate(Saml2Utils.samlDecode(decoded));\n\t\tassertThat(inflated).contains(\"ForceAuthn=\\\"true\\\"\");\n\t}\n\n\t@Test\n\tpublic void authenticationRequestWhenAuthenticationRequestResolverDslThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationRequestResolverDsl.class).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/saml2/authenticate/registration-id\")).andReturn();\n\t\tUriComponents components = UriComponentsBuilder.fromUriString(result.getResponse().getRedirectedUrl()).build();\n\t\tString samlRequest = components.getQueryParams().getFirst(\"SAMLRequest\");\n\t\tString decoded = URLDecoder.decode(samlRequest, \"UTF-8\");\n\t\tString inflated = Saml2Utils.samlInflate(Saml2Utils.samlDecode(decoded));\n\t\tassertThat(inflated).contains(\"ForceAuthn=\\\"true\\\"\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationConverterThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationConverter.class).autowire();\n\t\tRelyingPartyRegistration relyingPartyRegistration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tString response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));\n\t\tgiven(CustomAuthenticationConverter.authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/login/saml2/sso/\" + relyingPartyRegistration.getRegistrationId())\n\t\t\t\t.param(\"SAMLResponse\", SIGNED_RESPONSE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(redirectedUrl(\"/\"));\n\t\tverify(CustomAuthenticationConverter.authenticationConverter).convert(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationConverterBeanThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationConverterBean.class).autowire();\n\t\tSaml2AuthenticationTokenConverter authenticationConverter = this.spring.getContext()\n\t\t\t.getBean(Saml2AuthenticationTokenConverter.class);\n\t\tRelyingPartyRegistration relyingPartyRegistration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tString response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));\n\t\tgiven(authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/login/saml2/sso/\" + relyingPartyRegistration.getRegistrationId())\n\t\t\t\t.param(\"SAMLResponse\", SIGNED_RESPONSE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(redirectedUrl(\"/\"));\n\t\tverify(authenticationConverter).convert(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWithInvalidDeflatedSAMLResponseThenFailureHandlerUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationFailureHandler.class).autowire();\n\t\tbyte[] invalidDeflated = \"invalid\".getBytes();\n\t\tString encoded = Saml2Utils.samlEncode(invalidDeflated);\n\t\tMockHttpServletRequestBuilder request = get(\"/login/saml2/sso/registration-id\").queryParam(\"SAMLResponse\",\n\t\t\t\tencoded);\n\t\tthis.mvc.perform(request);\n\t\tArgumentCaptor<Saml2AuthenticationException> captor = ArgumentCaptor\n\t\t\t.forClass(Saml2AuthenticationException.class);\n\t\tverify(CustomAuthenticationFailureHandler.authenticationFailureHandler)\n\t\t\t.onAuthenticationFailure(any(HttpServletRequest.class), any(HttpServletResponse.class), captor.capture());\n\t\tSaml2AuthenticationException exception = captor.getValue();\n\t\tassertThat(exception.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE);\n\t\tassertThat(exception.getSaml2Error().getDescription()).isEqualTo(\"Unable to inflate string\");\n\t\tassertThat(exception).hasRootCauseInstanceOf(IOException.class);\n\t}\n\n\t@Test\n\tpublic void authenticationRequestWhenCustomAuthnRequestRepositoryThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationRequestRepository.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/saml2/authenticate/registration-id\");\n\t\tthis.mvc.perform(request).andExpect(status().isFound());\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = this.spring.getContext()\n\t\t\t.getBean(Saml2AuthenticationRequestRepository.class);\n\t\tverify(repository).saveAuthenticationRequest(any(AbstractSaml2AuthenticationRequest.class),\n\t\t\t\tany(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthnRequestRepositoryThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationRequestRepository.class).autowire();\n\t\tMockHttpServletRequestBuilder request = post(\"/login/saml2/sso/registration-id\").param(\"SAMLResponse\",\n\t\t\t\tSIGNED_RESPONSE);\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = this.spring.getContext()\n\t\t\t.getBean(Saml2AuthenticationRequestRepository.class);\n\t\tthis.mvc.perform(request);\n\t\tverify(repository).loadAuthenticationRequest(any(HttpServletRequest.class));\n\t\tverify(repository).removeAuthenticationRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticationRequestWhenCustomAuthenticationRequestUriRepositoryThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationRequestUriCustomAuthenticationConverter.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/custom/auth/registration-id\");\n\t\tthis.mvc.perform(request).andExpect(status().isFound());\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = this.spring.getContext()\n\t\t\t.getBean(Saml2AuthenticationRequestRepository.class);\n\t\tverify(repository).saveAuthenticationRequest(any(AbstractSaml2AuthenticationRequest.class),\n\t\t\t\tany(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticationRequestWhenCustomAuthenticationRequestPathRepositoryThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationRequestUriQuery.class).autowire();\n\t\tMockHttpServletRequestBuilder request = get(\"/custom/auth/sso\");\n\t\tthis.mvc.perform(request)\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/custom/auth/sso?entityId=registration-id\"));\n\t\trequest.queryParam(\"entityId\", registration.getRegistrationId());\n\t\tMvcResult result = this.mvc.perform(request).andExpect(status().isFound()).andReturn();\n\t\tString redirectedUrl = result.getResponse().getRedirectedUrl();\n\t\tassertThat(redirectedUrl).startsWith(registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation());\n\t}\n\n\t@Test\n\tpublic void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenAutowires()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CustomLoginProcessingUrlDefaultAuthenticationConverter.class).autowire();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomLoginProcessingUrlAndCustomAuthenticationConverterThenAuthenticate()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CustomLoginProcessingUrlCustomAuthenticationConverter.class).autowire();\n\t\tRelyingPartyRegistration relyingPartyRegistration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tString response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));\n\t\tgiven(AUTHENTICATION_CONVERTER.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/my/custom/url\").param(\"SAMLResponse\", SIGNED_RESPONSE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(redirectedUrl(\"/\"));\n\t\tverify(AUTHENTICATION_CONVERTER).convert(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomLoginProcessingUrlAndSaml2AuthenticationTokenConverterBeanThenAuthenticate()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(CustomLoginProcessingUrlSaml2AuthenticationTokenConverterBean.class).autowire();\n\t\tSaml2AuthenticationTokenConverter authenticationConverter = this.spring.getContext()\n\t\t\t.getBean(Saml2AuthenticationTokenConverter.class);\n\t\tRelyingPartyRegistration relyingPartyRegistration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tString response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));\n\t\tgiven(authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/my/custom/url\").param(\"SAMLResponse\", SIGNED_RESPONSE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(redirectedUrl(\"/\"));\n\t\tverify(authenticationConverter).convert(any(HttpServletRequest.class));\n\t}\n\n\t// gh-11657\n\t@Test\n\tpublic void getFaviconWhenDefaultConfigurationThenDoesNotSaveAuthnRequest() throws Exception {\n\t\tthis.spring.register(Saml2LoginConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/favicon.ico\").accept(MediaType.TEXT_HTML))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\tthis.mvc.perform(get(\"/\").accept(MediaType.TEXT_HTML))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(header().string(\"Location\", startsWith(\"/saml2/authenticate\")));\n\t}\n\n\t@Test\n\tpublic void saml2LoginWhenCustomAuthenticationProviderThenUses() throws Exception {\n\t\tthis.spring.register(CustomAuthenticationProviderConfig.class).autowire();\n\t\tAuthenticationProvider provider = this.spring.getContext().getBean(AuthenticationProvider.class);\n\t\tthis.mvc.perform(post(\"/login/saml2/sso/registration-id\").param(\"SAMLResponse\", SIGNED_RESPONSE))\n\t\t\t.andExpect(status().isFound());\n\t\tverify(provider).authenticate(any());\n\t}\n\n\tprivate void performSaml2Login(String expected) throws IOException, ServletException {\n\t\t// setup authentication parameters\n\t\tthis.request.setRequestURI(\"/login/saml2/sso/registration-id\");\n\t\tthis.request.setParameter(\"SAMLResponse\",\n\t\t\t\tBase64.getEncoder().encodeToString(\"saml2-xml-response-object\".getBytes()));\n\t\t// perform test\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);\n\t\t// assertions\n\t\tAuthentication authentication = this.securityContextRepository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response))\n\t\t\t.getAuthentication();\n\t\tassertThat(authentication).as(\"Expected a valid authentication object.\").isNotNull();\n\t\tassertThat(authentication.getAuthorities()).hasSize(1);\n\t\tassertThat(authentication.getAuthorities()).first()\n\t\t\t.isInstanceOf(SimpleGrantedAuthority.class)\n\t\t\t.hasToString(expected);\n\t}\n\n\tprivate static AuthenticationManager getAuthenticationManagerMock(String role) {\n\t\treturn new AuthenticationManager() {\n\t\t\t@Override\n\t\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\t\tif (!supports(authentication.getClass())) {\n\t\t\t\t\tthrow new AuthenticationServiceException(\"not supported\");\n\t\t\t\t}\n\t\t\t\treturn new Saml2Authentication(() -> \"auth principal\", \"saml2 response\",\n\t\t\t\t\t\tCollections.singletonList(new SimpleGrantedAuthority(role)));\n\t\t\t}\n\n\t\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\t\treturn authentication.isAssignableFrom(Saml2AuthenticationToken.class);\n\t\t\t}\n\t\t};\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class Saml2LoginConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.saml2Login(Customizer.withDefaults());\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class Saml2LoginConfigWithCustomAuthenticationManager {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.saml2Login((login) -> login.authenticationManager(getAuthenticationManagerMock(\"ROLE_AUTH_MANAGER\")));\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class Saml2LoginConfigWithDefaultAndCustomAuthenticationManager {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authenticationManager(getAuthenticationManagerMock(\"DEFAULT_AUTH_MANAGER\"))\n\t\t\t\t.saml2Login((saml) -> saml\n\t\t\t\t\t.authenticationManager(getAuthenticationManagerMock(\"ROLE_AUTH_MANAGER\"))\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationFailureHandler {\n\n\t\tstatic final AuthenticationFailureHandler authenticationFailureHandler = mock(\n\t\t\t\tAuthenticationFailureHandler.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())\n\t\t\t\t.saml2Login((saml2) -> saml2.failureHandler(authenticationFailureHandler));\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationRequestResolverBean {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.saml2Login(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSaml2AuthenticationRequestResolver authenticationRequestResolver(\n\t\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\t\tRelyingPartyRegistrationResolver registrationResolver = new DefaultRelyingPartyRegistrationResolver(\n\t\t\t\t\tregistrations);\n\t\t\tOpenSaml5AuthenticationRequestResolver delegate = new OpenSaml5AuthenticationRequestResolver(\n\t\t\t\t\tregistrationResolver);\n\t\t\tdelegate.setAuthnRequestCustomizer((parameters) -> parameters.getAuthnRequest().setForceAuthn(true));\n\t\t\treturn delegate;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationRequestResolverDsl {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http, RelyingPartyRegistrationRepository registrations)\n\t\t\t\tthrows Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.saml2Login((saml2) -> saml2\n\t\t\t\t\t\t.authenticationRequestResolver(authenticationRequestResolver(registrations))\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\tSaml2AuthenticationRequestResolver authenticationRequestResolver(\n\t\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\t\tRelyingPartyRegistrationResolver registrationResolver = new DefaultRelyingPartyRegistrationResolver(\n\t\t\t\t\tregistrations);\n\t\t\tOpenSaml5AuthenticationRequestResolver delegate = new OpenSaml5AuthenticationRequestResolver(\n\t\t\t\t\tregistrationResolver);\n\t\t\tdelegate.setAuthnRequestCustomizer((parameters) -> parameters.getAuthnRequest().setForceAuthn(true));\n\t\t\treturn delegate;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationConverter {\n\n\t\tstatic final AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())\n\t\t\t\t.saml2Login((saml2) -> saml2.authenticationConverter(authenticationConverter));\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationConverterBean {\n\n\t\tprivate final Saml2AuthenticationTokenConverter authenticationConverter = mock(\n\t\t\t\tSaml2AuthenticationTokenConverter.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain app(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.saml2Login(Customizer.withDefaults());\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSaml2AuthenticationTokenConverter authenticationConverter() {\n\t\t\treturn this.authenticationConverter;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationRequestRepository {\n\n\t\tprivate final Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = mock(\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated());\n\t\t\thttp.saml2Login(withDefaults());\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {\n\t\t\treturn this.repository;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomLoginProcessingUrlDefaultAuthenticationConverter {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())\n\t\t\t\t.saml2Login((saml2) -> saml2.loginProcessingUrl(\"/my/custom/url\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationRequestUriCustomAuthenticationConverter {\n\n\t\tprivate final Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = mock(\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\n\t\t@Bean\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {\n\t\t\treturn this.repository;\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())\n\t\t\t\t.saml2Login((saml2) -> saml2.authenticationRequestUri(\"/custom/auth/{registrationId}\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationRequestUriQuery {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t\t.saml2Login((saml2) -> saml2.authenticationRequestUriQuery(\"/custom/auth/sso?entityId={registrationId}\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomLoginProcessingUrlCustomAuthenticationConverter {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())\n\t\t\t\t\t.saml2Login((saml2) -> saml2\n\t\t\t\t\t\t.loginProcessingUrl(\"/my/custom/url\")\n\t\t\t\t\t\t.authenticationConverter(AUTHENTICATION_CONVERTER)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomLoginProcessingUrlSaml2AuthenticationTokenConverterBean {\n\n\t\tprivate final Saml2AuthenticationTokenConverter authenticationConverter = mock(\n\t\t\t\tSaml2AuthenticationTokenConverter.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())\n\t\t\t\t\t.saml2Login((saml2) -> saml2.loginProcessingUrl(\"/my/custom/url\"));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSaml2AuthenticationTokenConverter authenticationTokenConverter() {\n\t\t\treturn this.authenticationConverter;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class CustomAuthenticationProviderConfig {\n\n\t\tprivate final AuthenticationProvider provider = spy(new OpenSaml5AuthenticationProvider());\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\thttp.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.saml2Login(Customizer.withDefaults());\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationProvider provider() {\n\t\t\treturn this.provider;\n\t\t}\n\n\t}\n\n\tstatic class Saml2LoginConfigBeans {\n\n\t\t@Bean\n\t\tSecurityContextRepository securityContextRepository() {\n\t\t\treturn new HttpSessionSecurityContextRepository();\n\t\t}\n\n\t\t@Bean\n\t\tRelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {\n\t\t\treturn spy(new InMemoryRelyingPartyRegistrationRepository(registration));\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class ResourceController {\n\n\t\t@GetMapping(\"/\")\n\t\tString user(@AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) {\n\t\t\treturn principal.getName();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.saml2;\n\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.ObjectPostProcessor;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.saml2.core.Saml2Utils;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutValidatorResult;\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.mockito.BDDMockito.verify;\nimport static org.mockito.BDDMockito.verifyNoInteractions;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.spy;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for different Java configuration for {@link Saml2LogoutConfigurer}\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class Saml2LogoutConfigurerTests {\n\n\t@Autowired\n\tprivate ConfigurableApplicationContext context;\n\n\t@Autowired\n\tprivate RelyingPartyRegistrationRepository repository;\n\n\tprivate final Saml2LogoutRequestRepository logoutRequestRepository = new HttpSessionLogoutRequestRepository();\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tMockMvc mvc;\n\n\tprivate Saml2Authentication user;\n\n\tString apLogoutRequest = \"nZFBa4MwGIb/iuQeE2NTXFDLQAaC26Hrdtgt1dQFNMnyxdH9+zlboeyww275SN7nzcOX787jEH0qD9qaAiUxRZEyre206Qv0cnjAGdqVOchxYE40trdT2KuPSUGI5qQBcbkq0OSNsBI0CCNHBSK04vn+sREspsJ5G2xrBxRVc1AbGZa29xAcCEK8i9VZjm5QsfU9GZYWsoCJv5ShqK4K1Ow5p5LyU4aP6XaLN3cpw9mGctydjrxNaZt1XM5vASZVGwjShAIxyhJMU8z4gSWCM8GSmDH+hqLX1Xv+JLpaiiXsb+3+lpMAyv8IoVI6rEzQ4QvrLie3uBX+NMfr6l/waT6t0AumvI6/FlN+Aw==\";\n\n\tString apLogoutRequestSigAlg = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;\n\n\tString apLogoutRequestRelayState = \"33591874-b123-4f2c-ab0d-2d0d84aa8b56\";\n\n\tString apLogoutRequestSignature = \"oKqdzrmn2YAqXcwkow2lzRXr5PNHm0s/gWsRnaZYhC+Oq5ekK5uIKQYvtmNR94HJjDe1VRs+vVQCYivgdoTzBV2ZlffTXZmYsCsY9q4jbCWR6R5CbhU73/MkKQsPcyVvMhNYxnDYapIlxDsfoZNTboDEz3GM+HRoGRfl9emCXY0lPRYwqC4kpu7oMDBkafR0A09jPIxFuNpqlLPwUxL9m+DGkvDK3mFDN1xJcgZaK73HcuJe7Qh4huOrKNFetwc5EvqfiwgiWF6sfq9A+rZBfCIYo10NNLY7fNQAR2IqwcKtawHgTGWbeshRyFrwVYMR64EnClfxUHsHKf5kiZ2dlw==\";\n\n\tString apLogoutResponse = \"fZHRa4MwEMb/Fcl7jEadGqplrAwK3Uvb9WFvZ4ydoInk4uj++1nXbmWMvhwcd9/3Jb9bLE99530oi63RBQn9gHhKS1O3+liQ1/0zzciyXCD0HR/ExhzN6LYKB6NReZNUo/ieFWS0WhjAFoWGXqFwUuweXzaC+4EYrHFGmo54K4Wu1eDmuHfnBhSM2cFXJ+iHTvnGHlk3x7DZmNlLGvHWq4Jstk0GUSjjiIZJI2lcpQnNeRLTAOo4fwCeQg3Trr6+cm/OqmnWVHECVGWQ0jgCSatsKvXUxhFvZF7xSYU4qrVGB9oVhAc8pEFEebLnkeBc8NyPePpGvMOV1/Q3cqEjZrG9hXKfCSAqe+ZAShio0q51n7StF+zW7gf9zoEb8U/7ZGrlHaAb1f0onLfFbpRSIRJWXkJ+bdm/Fy6/AA==\";\n\n\tString apLogoutResponseSigAlg = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;\n\n\tString apLogoutResponseRelayState = \"8f63887a-ec7e-4149-b6a0-dd730017f315\";\n\n\tString apLogoutResponseSignature = \"h2fDqSIBfmnkRHKDMY4IxkCXcI0w98ydNsnPmv1b7GTZCWLbJ+oxaP2yZNPw7wOWXTv86cTPwKLjx5halKy5C+hhWnT0haKhuMcUvHlsgAMBbJKLV+1afzL4O77cvAQJmMNRK7ugXGNV5PTEnd1U4voy134OgdD5XycYiFVRZOwP5H84eJ9xxlvqQwqDvZTcgiF/ZS4ioZgzgnIFcbagZQ12LWNh26OMaUpIW04kCeO6t2dUsxOL6nZWvNrX/Zx1sORIpu4doDUa1RYC8YnjZeQEzDqUVC/dBO/mbVJ/hbF9tD0jBUx7YIgoXpqsWK4TcCsvmlmhrJXvGxDyoAWu2Q==\";\n\n\tString rpLogoutRequest = \"nZFBa4MwGIb/iuQeY6NlGtQykIHgdui6HXaLmrqAJlm+OLp/v0wrlB122CXkI3mfNw/JD5dpDD6FBalVgXZhhAKhOt1LNRTo5fSAU3Qoc+DTSA1r9KBndxQfswAX+KQCth4VaLaKaQ4SmOKTAOY69nz/2DAaRsxY7XSnRxRUPigVd0vbu3MGGCHchOLCJzOKUNuBjEsLWcDErmUoqKsCNcc+yc5tsudYpPwOJzHvcJv6pfdjEtNzl7XU3wWYRa3AceUKRCO6w1GM6f5EY0Ypo1lIk+gNBa+bt38kulqyJWxv7f6W4wDC/gih0hoslJPuC8s+J7e4Df7k43X1L/jsdxt0xZTX8dfHlN8=\";\n\n\tString rpLogoutRequestId = \"LRd49fb45a-e8a7-43ac-b8ac-d8a7432fc9b2\";\n\n\tString rpLogoutRequestRelayState = \"8f63887a-ec7e-4149-b6a0-dd730017f315\";\n\n\tString rpLogoutRequestSignature = \"h2fDqSIBfmnkRHKDMY4IxkCXcI0w98ydNsnPmv1b7GTZCWLbJ+oxaP2yZNPw7wOWXTv86cTPwKLjx5halKy5C+hhWnT0haKhuMcUvHlsgAMBbJKLV+1afzL4O77cvAQJmMNRK7ugXGNV5PTEnd1U4voy134OgdD5XycYiFVRZOwP5H84eJ9xxlvqQwqDvZTcgiF/ZS4ioZgzgnIFcbagZQ12LWNh26OMaUpIW04kCeO6t2dUsxOL6nZWvNrX/Zx1sORIpu4doDUa1RYC8YnjZeQEzDqUVC/dBO/mbVJ/hbF9tD0jBUx7YIgoXpqsWK4TcCsvmlmhrJXvGxDyoAWu2Q==\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"registration-id\");\n\t\tthis.user = new Saml2Authentication(principal, \"response\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.request = TestMockHttpServletRequests.post(\"/login/saml2/sso/test-rp\").build();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void logoutWhenDefaultsAndNotSaml2LoginThenDefaultLogout() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tMvcResult result = this.mvc.perform(post(\"/logout\").with(authentication(user)).with(csrf()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tLogoutHandler logoutHandler = this.spring.getContext().getBean(LogoutHandler.class);\n\t\tassertThat(location).isEqualTo(\"/login?logout\");\n\t\tverify(logoutHandler).logout(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenDefaultsThenLogsOutAndSendsLogoutRequest() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/logout\").with(authentication(this.user)).with(csrf()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tLogoutHandler logoutHandler = this.spring.getContext().getBean(LogoutHandler.class);\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/request\");\n\t\tverify(logoutHandler).logout(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenUnauthenticatedThenEntryPoint() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenMissingCsrfThen403() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(this.user))).andExpect(status().isForbidden());\n\t\tverifyNoInteractions(getBean(LogoutHandler.class));\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenGetThenDefaultLogoutPage() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/logout\").with(authentication(this.user)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn();\n\t\tassertThat(result.getResponse().getContentAsString()).contains(\"Are you sure you want to log out?\");\n\t\tverifyNoInteractions(getBean(LogoutHandler.class));\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenPutOrDeleteThen404() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tthis.mvc.perform(put(\"/logout\").with(authentication(this.user)).with(csrf())).andExpect(status().isNotFound());\n\t\tthis.mvc.perform(delete(\"/logout\").with(authentication(this.user)).with(csrf()))\n\t\t\t.andExpect(status().isNotFound());\n\t\tverifyNoInteractions(this.spring.getContext().getBean(LogoutHandler.class));\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenNoRegistrationThen401() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"wrong\");\n\t\tSaml2Authentication authentication = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(authentication)).with(csrf()))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenCsrfDisabledAndNoAuthenticationThenFinalRedirect() throws Exception {\n\t\tthis.spring.register(Saml2LogoutCsrfDisabledConfig.class).autowire();\n\t\tthis.mvc.perform(post(\"/logout\"));\n\t\tLogoutSuccessHandler logoutSuccessHandler = this.spring.getContext().getBean(LogoutSuccessHandler.class);\n\t\tverify(logoutSuccessHandler).onLogoutSuccess(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenCustomLogoutRequestResolverThenUses() throws Exception {\n\t\tthis.spring.register(Saml2LogoutComponentsConfig.class).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tgiven(getBean(Saml2LogoutRequestResolver.class).resolve(any(), any())).willReturn(logoutRequest);\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(this.user)).with(csrf()));\n\t\tverify(getBean(Saml2LogoutRequestResolver.class)).resolve(any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenDefaultsThenLogsOutAndSendsLogoutResponse() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"get\");\n\t\tSaml2Authentication user = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tMvcResult result = this.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", this.apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", this.apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutRequestSignature)\n\t\t\t\t.with(samlQueryString())\n\t\t\t\t.with(authentication(user)))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/response\");\n\t\tverify(getBean(LogoutHandler.class)).logout(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class, SecurityContextChangedListenerConfig.class).autowire();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"get\");\n\t\tSaml2Authentication user = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tMvcResult result = this.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", this.apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", this.apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutRequestSignature)\n\t\t\t\t.with(samlQueryString())\n\t\t\t\t.with(authentication(user)))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/response\");\n\t\tverify(getBean(LogoutHandler.class)).logout(any(), any(), any());\n\t\tverify(getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t// gh-11235\n\t@Test\n\tpublic void saml2LogoutRequestWhenLowercaseEncodingThenLogsOutAndSendsLogoutResponse() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tString apLogoutRequest = \"nZFNa4QwEIb/iuQeP6K7dYO6FKQg2B622x56G3WwgiY2E8v239fqCksPPfSWIXmfNw+THC9D73yi\\r\\n\"\n\t\t\t\t+ \"oU6rlAWuzxxUtW461abs5fzAY3bMEoKhF6Msdasne8KPCck6c1KRXK9SNhklNVBHUsGAJG0tn+8f\\r\\n\"\n\t\t\t\t+ \"SylcX45GW13rnjn5HOwU2KXt3dqRpOeZ0cULDGOPrjat1y8t3gL2zFrGnCJPWXkKcR8KCHY8xmrP\\r\\n\"\n\t\t\t\t+ \"Iz868OpOVLwO4wohggagmd8STVgosqBsyoQvBPd3XITnIJaRL8PYjcThjTmvm/f8SXa1lEvY3Nr9\\r\\n\"\n\t\t\t\t+ \"LQdEaH6EWAYjR2U7+8W7JvFucRv8aY4X+b/g03zaoCsmu46/FpN9Aw==\";\n\t\tString apLogoutRequestRelayState = \"d118dbd5-3853-4268-b3e5-c40fc033fa2f\";\n\t\tString apLogoutRequestSignature = \"VZ7rWa5u3hIX60fAQs/gBQZWDP2BAIlCMMrNrTHafoKKj0uXWnuITYLuL8NdsWmyQN0+fqWW4X05+BqiLpL80jHLmQR5RVqqL1EtVv1SpPUna938lgz2sOliuYmfQNj4Bmd+Z5G1K6QhbVrtfb7TQHURjUafzfRm8+jGz3dPjVBrn/rD/umfGoSn6RuWngugcMNL4U0A+JcEh1NSfSYNVz7y+MqlW1UhX2kF86rm97ERCrxay7Gh/bI2f3fJPJ1r+EyLjzrDUkqw5cva3rVlFgEQouMVu35lUJn7SFompW8oTxkI23oc/t+AGZqaBupNITNdjyGCBpfukZ69EZrj8g==\";\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"get\");\n\t\tSaml2Authentication user = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tMvcResult result = this.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", apLogoutRequestSignature)\n\t\t\t\t.with(new SamlQueryStringRequestPostProcessor(true))\n\t\t\t\t.with(authentication(user)))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/response\");\n\t\tverify(getBean(LogoutHandler.class)).logout(any(), any(), any());\n\t}\n\n\t// gh-12346\n\t@Test\n\tpublic void saml2LogoutRequestWhenLowercaseEncodingAndDifferentQueryParamOrderThenLogsOutAndSendsLogoutResponse()\n\t\t\tthrows Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tString apLogoutRequest = \"nZFNa4QwEIb/iuQeP6K7dYO6FKQg2B622x56G3WwgiY2E8v239fqCksPPfSWIXmfNw+THC9D73yi\\r\\n\"\n\t\t\t\t+ \"oU6rlAWuzxxUtW461abs5fzAY3bMEoKhF6Msdasne8KPCck6c1KRXK9SNhklNVBHUsGAJG0tn+8f\\r\\n\"\n\t\t\t\t+ \"SylcX45GW13rnjn5HOwU2KXt3dqRpOeZ0cULDGOPrjat1y8t3gL2zFrGnCJPWXkKcR8KCHY8xmrP\\r\\n\"\n\t\t\t\t+ \"Iz868OpOVLwO4wohggagmd8STVgosqBsyoQvBPd3XITnIJaRL8PYjcThjTmvm/f8SXa1lEvY3Nr9\\r\\n\"\n\t\t\t\t+ \"LQdEaH6EWAYjR2U7+8W7JvFucRv8aY4X+b/g03zaoCsmu46/FpN9Aw==\";\n\t\tString apLogoutRequestRelayState = \"d118dbd5-3853-4268-b3e5-c40fc033fa2f\";\n\t\tString apLogoutRequestSignature = \"VZ7rWa5u3hIX60fAQs/gBQZWDP2BAIlCMMrNrTHafoKKj0uXWnuITYLuL8NdsWmyQN0+fqWW4X05+BqiLpL80jHLmQR5RVqqL1EtVv1SpPUna938lgz2sOliuYmfQNj4Bmd+Z5G1K6QhbVrtfb7TQHURjUafzfRm8+jGz3dPjVBrn/rD/umfGoSn6RuWngugcMNL4U0A+JcEh1NSfSYNVz7y+MqlW1UhX2kF86rm97ERCrxay7Gh/bI2f3fJPJ1r+EyLjzrDUkqw5cva3rVlFgEQouMVu35lUJn7SFompW8oTxkI23oc/t+AGZqaBupNITNdjyGCBpfukZ69EZrj8g==\";\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"get\");\n\t\tSaml2Authentication user = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tMvcResult result = this.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", apLogoutRequest)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"RelayState\", apLogoutRequestRelayState)\n\t\t\t\t.param(\"Signature\", apLogoutRequestSignature)\n\t\t\t\t.with(new SamlQueryStringRequestPostProcessor(true))\n\t\t\t\t.with(authentication(user)))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/response\");\n\t\tverify(getBean(LogoutHandler.class)).logout(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenNoRegistrationThen400() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"wrong\");\n\t\tSaml2Authentication user = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", this.apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", this.apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutRequestSignature)\n\t\t\t\t.with(authentication(user)))\n\t\t\t.andExpect(status().isBadRequest());\n\t\tverifyNoInteractions(getBean(LogoutHandler.class));\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tthis.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", this.apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", this.apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.with(authentication(this.user)))\n\t\t\t.andExpect(status().isFound());\n\t\tverifyNoInteractions(getBean(LogoutHandler.class));\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenCustomLogoutRequestHandlerThenUses() throws Exception {\n\t\tthis.spring.register(Saml2LogoutComponentsConfig.class).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tlogoutRequest.setIssueInstant(Instant.now());\n\t\tgiven(getBean(Saml2LogoutRequestValidator.class).validate(any()))\n\t\t\t.willReturn(Saml2LogoutValidatorResult.success());\n\t\tSaml2LogoutResponse logoutResponse = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(\"samlResponse\")\n\t\t\t.build();\n\t\tgiven(getBean(Saml2LogoutResponseResolver.class).resolve(any(), any())).willReturn(logoutResponse);\n\t\tthis.mvc.perform(post(\"/logout/saml2/slo\").param(\"SAMLRequest\", \"samlRequest\").with(authentication(this.user)))\n\t\t\t.andReturn();\n\t\tverify(getBean(Saml2LogoutRequestValidator.class)).validate(any());\n\t\tverify(getBean(Saml2LogoutResponseResolver.class)).resolve(any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutResponseWhenDefaultsThenRedirects() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"get\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, this.request, this.response);\n\t\tthis.request.setParameter(\"RelayState\", logoutRequest.getRelayState());\n\t\tassertThat(this.logoutRequestRepository.loadLogoutRequest(this.request)).isNotNull();\n\t\tthis.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").session(((MockHttpSession) this.request.getSession()))\n\t\t\t\t.param(\"SAMLResponse\", this.apLogoutResponse)\n\t\t\t\t.param(\"RelayState\", this.apLogoutResponseRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutResponseSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutResponseSignature)\n\t\t\t\t.with(samlQueryString()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\tverifyNoInteractions(getBean(LogoutHandler.class));\n\t\tassertThat(this.logoutRequestRepository.loadLogoutRequest(this.request)).isNull();\n\t}\n\n\t@Test\n\tpublic void saml2LogoutResponseWhenInvalidSamlResponseThen401() throws Exception {\n\t\tthis.spring.register(Saml2LogoutDefaultsConfig.class).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, this.request, this.response);\n\t\tString deflatedApLogoutResponse = Saml2Utils.samlEncode(\n\t\t\t\tSaml2Utils.samlInflate(Saml2Utils.samlDecode(this.apLogoutResponse)).getBytes(StandardCharsets.UTF_8));\n\t\tthis.mvc\n\t\t\t.perform(post(\"/logout/saml2/slo\").session((MockHttpSession) this.request.getSession())\n\t\t\t\t.param(\"SAMLResponse\", deflatedApLogoutResponse)\n\t\t\t\t.param(\"RelayState\", this.rpLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutResponseSignature)\n\t\t\t\t.with(samlQueryString()))\n\t\t\t.andExpect(status().reason(containsString(\"invalid_signature\")))\n\t\t\t.andExpect(status().isUnauthorized());\n\t\tverifyNoInteractions(getBean(LogoutHandler.class));\n\t}\n\n\t@Test\n\tpublic void saml2LogoutResponseWhenCustomLogoutResponseHandlerThenUses() throws Exception {\n\t\tthis.spring.register(Saml2LogoutComponentsConfig.class).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"get\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tgiven(getBean(Saml2LogoutRequestRepository.class).removeLogoutRequest(any(), any())).willReturn(logoutRequest);\n\t\tgiven(getBean(Saml2LogoutResponseValidator.class).validate(any()))\n\t\t\t.willReturn(Saml2LogoutValidatorResult.success());\n\t\tthis.mvc.perform(get(\"/logout/saml2/slo\").param(\"SAMLResponse\", \"samlResponse\")).andReturn();\n\t\tverify(getBean(Saml2LogoutResponseValidator.class)).validate(any());\n\t}\n\n\t// gh-11363\n\t@Test\n\tpublic void saml2LogoutWhenCustomLogoutRequestRepositoryThenUses() throws Exception {\n\t\tthis.spring.register(Saml2LogoutComponentsConfig.class).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tgiven(getBean(Saml2LogoutRequestResolver.class).resolve(any(), any())).willReturn(logoutRequest);\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(this.user)).with(csrf()));\n\t\tverify(getBean(Saml2LogoutRequestRepository.class)).saveLogoutRequest(eq(logoutRequest), any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenLogoutGetThenLogsOutAndSendsLogoutRequest() throws Exception {\n\t\tthis.spring.register(Saml2LogoutWithHttpGet.class).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/logout\").with(authentication(this.user)))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tLogoutHandler logoutHandler = this.spring.getContext().getBean(LogoutHandler.class);\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/request\");\n\t\tverify(logoutHandler).logout(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenSaml2LogoutRequestFilterPostProcessedThenUses() {\n\n\t\tSaml2DefaultsWithObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(Saml2DefaultsWithObjectPostProcessorConfig.class).autowire();\n\t\tverify(Saml2DefaultsWithObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(Saml2LogoutRequestFilter.class));\n\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenSaml2LogoutResponseFilterPostProcessedThenUses() {\n\n\t\tSaml2DefaultsWithObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(Saml2DefaultsWithObjectPostProcessorConfig.class).autowire();\n\t\tverify(Saml2DefaultsWithObjectPostProcessorConfig.objectPostProcessor)\n\t\t\t.postProcess(any(Saml2LogoutResponseFilter.class));\n\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenLogoutFilterPostProcessedThenUses() {\n\n\t\tSaml2DefaultsWithObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);\n\t\tthis.spring.register(Saml2DefaultsWithObjectPostProcessorConfig.class).autowire();\n\t\tverify(Saml2DefaultsWithObjectPostProcessorConfig.objectPostProcessor, atLeastOnce())\n\t\t\t.postProcess(any(LogoutFilter.class));\n\n\t}\n\n\tprivate <T> T getBean(Class<T> clazz) {\n\t\treturn this.spring.getContext().getBean(clazz);\n\t}\n\n\tprivate SamlQueryStringRequestPostProcessor samlQueryString() {\n\t\treturn new SamlQueryStringRequestPostProcessor();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class Saml2LogoutDefaultsConfig {\n\n\t\tLogoutHandler mockLogoutHandler = mock(LogoutHandler.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.logout((logout) -> logout.addLogoutHandler(this.mockLogoutHandler))\n\t\t\t\t.saml2Login(withDefaults())\n\t\t\t\t.saml2Logout(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tLogoutHandler logoutHandler() {\n\t\t\treturn this.mockLogoutHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class Saml2LogoutCsrfDisabledConfig {\n\n\t\tLogoutSuccessHandler mockLogoutSuccessHandler = mock(LogoutSuccessHandler.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.logout((logout) -> logout.logoutSuccessHandler(this.mockLogoutSuccessHandler))\n\t\t\t\t.saml2Login(withDefaults())\n\t\t\t\t.saml2Logout(withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf.disable());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tLogoutSuccessHandler logoutSuccessHandler() {\n\t\t\treturn this.mockLogoutSuccessHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class Saml2LogoutWithHttpGet {\n\n\t\tLogoutHandler mockLogoutHandler = mock(LogoutHandler.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.logout((logout) -> logout.addLogoutHandler(this.mockLogoutHandler))\n\t\t\t\t.saml2Login(withDefaults())\n\t\t\t\t.saml2Logout((saml2) -> saml2.addObjectPostProcessor(new ObjectPostProcessor<LogoutFilter>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic <O extends LogoutFilter> O postProcess(O filter) {\n\t\t\t\t\t\tfilter.setLogoutRequestMatcher(pathPattern(HttpMethod.GET, \"/logout\"));\n\t\t\t\t\t\treturn filter;\n\t\t\t\t\t}\n\t\t\t\t}));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tLogoutHandler logoutHandler() {\n\t\t\treturn this.mockLogoutHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class Saml2DefaultsWithObjectPostProcessorConfig {\n\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor;\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.saml2Login(withDefaults())\n\t\t\t\t.saml2Logout(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic ObjectPostProcessor<Object> objectPostProcessor() {\n\t\t\treturn objectPostProcessor;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@Import(Saml2LoginConfigBeans.class)\n\tstatic class Saml2LogoutComponentsConfig {\n\n\t\tSaml2LogoutRequestRepository logoutRequestRepository = mock(Saml2LogoutRequestRepository.class);\n\n\t\tSaml2LogoutRequestValidator logoutRequestValidator = mock(Saml2LogoutRequestValidator.class);\n\n\t\tSaml2LogoutRequestResolver logoutRequestResolver = mock(Saml2LogoutRequestResolver.class);\n\n\t\tSaml2LogoutResponseValidator logoutResponseValidator = mock(Saml2LogoutResponseValidator.class);\n\n\t\tSaml2LogoutResponseResolver logoutResponseResolver = mock(Saml2LogoutResponseResolver.class);\n\n\t\t@Bean\n\t\tSecurityFilterChain web(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.saml2Login(withDefaults())\n\t\t\t\t.saml2Logout((logout) -> logout\n\t\t\t\t\t.logoutRequest((request) -> request\n\t\t\t\t\t\t.logoutRequestRepository(this.logoutRequestRepository)\n\t\t\t\t\t\t.logoutRequestValidator(this.logoutRequestValidator)\n\t\t\t\t\t\t.logoutRequestResolver(this.logoutRequestResolver)\n\t\t\t\t\t)\n\t\t\t\t\t.logoutResponse((response) -> response\n\t\t\t\t\t\t.logoutResponseValidator(this.logoutResponseValidator)\n\t\t\t\t\t\t.logoutResponseResolver(this.logoutResponseResolver)\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSaml2LogoutRequestRepository logoutRequestRepository() {\n\t\t\treturn this.logoutRequestRepository;\n\t\t}\n\n\t\t@Bean\n\t\tSaml2LogoutRequestValidator logoutRequestAuthenticator() {\n\t\t\treturn this.logoutRequestValidator;\n\t\t}\n\n\t\t@Bean\n\t\tSaml2LogoutRequestResolver logoutRequestResolver() {\n\t\t\treturn this.logoutRequestResolver;\n\t\t}\n\n\t\t@Bean\n\t\tSaml2LogoutResponseValidator logoutResponseAuthenticator() {\n\t\t\treturn this.logoutResponseValidator;\n\t\t}\n\n\t\t@Bean\n\t\tSaml2LogoutResponseResolver logoutResponseResolver() {\n\t\t\treturn this.logoutResponseResolver;\n\t\t}\n\n\t}\n\n\tstatic class Saml2LoginConfigBeans {\n\n\t\t@Bean\n\t\tRelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {\n\t\t\tSaml2X509Credential signing = TestSaml2X509Credentials.assertingPartySigningCredential();\n\t\t\tSaml2X509Credential verification = TestSaml2X509Credentials.relyingPartyVerifyingCredential();\n\t\t\tRelyingPartyRegistration.Builder withCreds = TestRelyingPartyRegistrations.noCredentials()\n\t\t\t\t.signingX509Credentials(credential(signing))\n\t\t\t\t.assertingPartyMetadata((party) -> party.verificationX509Credentials(credential(verification)));\n\t\t\tRelyingPartyRegistration post = withCreds.build();\n\t\t\tRelyingPartyRegistration get = withCreds.registrationId(\"get\")\n\t\t\t\t.singleLogoutServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t\t.build();\n\t\t\tRelyingPartyRegistration ap = withCreds.registrationId(\"ap\")\n\t\t\t\t.entityId(\"ap-entity-id\")\n\t\t\t\t.assertingPartyMetadata(\n\t\t\t\t\t\t(party) -> party.singleLogoutServiceLocation(\"https://rp.example.org/logout/saml2/request\")\n\t\t\t\t\t\t\t.singleLogoutServiceResponseLocation(\"https://rp.example.org/logout/saml2/response\"))\n\t\t\t\t.build();\n\n\t\t\treturn new InMemoryRelyingPartyRegistrationRepository(ap, get, post);\n\t\t}\n\n\t\tprivate Consumer<Collection<Saml2X509Credential>> credential(Saml2X509Credential credential) {\n\t\t\treturn (credentials) -> credentials.add(credential);\n\t\t}\n\n\t}\n\n\tstatic class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {\n\n\t\t@Override\n\t\tpublic <O> O postProcess(O object) {\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n\tstatic class SamlQueryStringRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate Function<String, String> urlEncodingPostProcessor = Function.identity();\n\n\t\tSamlQueryStringRequestPostProcessor() {\n\t\t\tthis(false);\n\t\t}\n\n\t\tSamlQueryStringRequestPostProcessor(boolean lowercased) {\n\t\t\tif (lowercased) {\n\t\t\t\tPattern encoding = Pattern.compile(\"%\\\\d[A-Fa-f]\");\n\t\t\t\tthis.urlEncodingPostProcessor = (encoded) -> {\n\t\t\t\t\tMatcher m = encoding.matcher(encoded);\n\t\t\t\t\twhile (m.find()) {\n\t\t\t\t\t\tString found = m.group(0);\n\t\t\t\t\t\tencoded = encoded.replace(found, found.toLowerCase());\n\t\t\t\t\t}\n\t\t\t\t\treturn encoded;\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String[]> entries : request.getParameterMap().entrySet()) {\n\t\t\t\tbuilder.queryParam(entries.getKey(),\n\t\t\t\t\t\tUriUtils.encode(entries.getValue()[0], StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = this.urlEncodingPostProcessor.apply(builder.build(true).toUriString().substring(1));\n\t\t\trequest.setQueryString(queryString);\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2MetadataConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.saml2;\n\nimport com.google.common.net.HttpHeaders;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.saml2.provider.service.metadata.OpenSaml5MetadataResolver;\nimport org.springframework.security.saml2.provider.service.metadata.RequestMatcherMetadataResponseResolver;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.hamcrest.Matchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link Saml2MetadataConfigurer}\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class Saml2MetadataConfigurerTests {\n\n\tstatic RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tMockMvc mvc;\n\n\t@Test\n\tvoid saml2MetadataRegistrationIdWhenDefaultsThenReturnsMetadata() throws Exception {\n\t\tthis.spring.register(DefaultConfig.class).autowire();\n\t\tString filename = \"saml-\" + registration.getRegistrationId() + \"-metadata.xml\";\n\t\tthis.mvc.perform(get(\"/saml2/metadata/\" + registration.getRegistrationId()))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(filename)))\n\t\t\t.andExpect(content().string(containsString(\"md:EntityDescriptor\")));\n\t}\n\n\t@Test\n\tvoid saml2MetadataRegistrationIdWhenWrongIdThenUnauthorized() throws Exception {\n\t\tthis.spring.register(DefaultConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/saml2/metadata/\" + registration.getRegistrationId() + \"wrong\"))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tvoid saml2MetadataWhenDefaultsThenReturnsMetadata() throws Exception {\n\t\tthis.spring.register(DefaultConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/saml2/metadata\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(\"-metadata.xml\")))\n\t\t\t.andExpect(content().string(containsString(\"md:EntityDescriptor\")));\n\t}\n\n\t@Test\n\tvoid saml2MetadataWhenMetadataResponseResolverThenUses() throws Exception {\n\t\tthis.spring.register(DefaultConfig.class, MetadataResponseResolverConfig.class).autowire();\n\t\tSaml2MetadataResponseResolver metadataResponseResolver = this.spring.getContext()\n\t\t\t.getBean(Saml2MetadataResponseResolver.class);\n\t\tgiven(metadataResponseResolver.resolve(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new Saml2MetadataResponse(\"metadata\", \"filename\"));\n\t\tthis.mvc.perform(get(\"/saml2/metadata\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(\"filename\")))\n\t\t\t.andExpect(content().string(containsString(\"metadata\")));\n\t\tverify(metadataResponseResolver).resolve(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tvoid saml2MetadataWhenMetadataResponseResolverDslThenUses() throws Exception {\n\t\tthis.spring.register(MetadataResponseResolverDslConfig.class).autowire();\n\t\tthis.mvc.perform(get(\"/saml2/metadata\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(\"filename\")))\n\t\t\t.andExpect(content().string(containsString(\"metadata\")));\n\t}\n\n\t@Test\n\tvoid saml2MetadataWhenMetadataUrlThenUses() throws Exception {\n\t\tthis.spring.register(MetadataUrlConfig.class).autowire();\n\t\tString filename = \"saml-\" + registration.getRegistrationId() + \"-metadata.xml\";\n\t\tthis.mvc.perform(get(\"/saml/metadata\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(filename)))\n\t\t\t.andExpect(content().string(containsString(\"md:EntityDescriptor\")));\n\t\tthis.mvc.perform(get(\"/saml2/metadata\")).andExpect(status().isForbidden());\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@Import(RelyingPartyRegistrationConfig.class)\n\tstatic class DefaultConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.saml2Metadata(Customizer.withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@Import(RelyingPartyRegistrationConfig.class)\n\tstatic class MetadataUrlConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.saml2Metadata((saml2) -> saml2.metadataUrl(\"/saml/metadata\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t// should ignore\n\t\t@Bean\n\t\tSaml2MetadataResponseResolver metadataResponseResolver(RelyingPartyRegistrationRepository registrations) {\n\t\t\treturn new RequestMatcherMetadataResponseResolver(registrations, new OpenSaml5MetadataResolver());\n\t\t}\n\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@Import(RelyingPartyRegistrationConfig.class)\n\tstatic class MetadataResponseResolverDslConfig {\n\n\t\tSaml2MetadataResponseResolver metadataResponseResolver = mock(Saml2MetadataResponseResolver.class);\n\n\t\t{\n\t\t\tgiven(this.metadataResponseResolver.resolve(any(HttpServletRequest.class)))\n\t\t\t\t.willReturn(new Saml2MetadataResponse(\"metadata\", \"filename\"));\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filters(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t\t.saml2Metadata((saml2) -> saml2.metadataResponseResolver(this.metadataResponseResolver));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class MetadataResponseResolverConfig {\n\n\t\tSaml2MetadataResponseResolver metadataResponseResolver = mock(Saml2MetadataResponseResolver.class);\n\n\t\t@Bean\n\t\tSaml2MetadataResponseResolver metadataResponseResolver() {\n\t\t\treturn this.metadataResponseResolver;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class RelyingPartyRegistrationConfig {\n\n\t\tRelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(registration);\n\n\t\t@Bean\n\t\tRelyingPartyRegistrationRepository registrations() {\n\t\t\treturn this.registrations;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/TestSaml2Credentials.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.configurers.saml2;\n\nimport java.io.ByteArrayInputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport org.springframework.security.converter.RsaKeyConverters;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\n\n/**\n * Preconfigured SAML credentials for SAML integration tests.\n */\npublic final class TestSaml2Credentials {\n\n\tprivate TestSaml2Credentials() {\n\t}\n\n\tstatic Saml2X509Credential verificationCertificate() {\n\t\t// @formatter:off\n\t\tString certificate = \"-----BEGIN CERTIFICATE-----\\n\"\n\t\t\t\t+ \"MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\\n\"\n\t\t\t\t+ \"VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\\n\"\n\t\t\t\t+ \"VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\\n\"\n\t\t\t\t+ \"c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\\n\"\n\t\t\t\t+ \"aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\\n\"\n\t\t\t\t+ \"BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\\n\"\n\t\t\t\t+ \"BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\\n\"\n\t\t\t\t+ \"DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\\n\"\n\t\t\t\t+ \"QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\\n\"\n\t\t\t\t+ \"E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\\n\"\n\t\t\t\t+ \"2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\\n\"\n\t\t\t\t+ \"RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\\n\"\n\t\t\t\t+ \"nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\\n\"\n\t\t\t\t+ \"cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\\n\"\n\t\t\t\t+ \"iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\\n\"\n\t\t\t\t+ \"ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\\n\"\n\t\t\t\t+ \"AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\\n\"\n\t\t\t\t+ \"nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\\n\"\n\t\t\t\t+ \"ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\\n\"\n\t\t\t\t+ \"xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\\n\"\n\t\t\t\t+ \"V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\\n\"\n\t\t\t\t+ \"lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\\n\"\n\t\t\t\t+ \"-----END CERTIFICATE-----\";\n\t\t// @formatter:on\n\t\treturn new Saml2X509Credential(x509Certificate(certificate),\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.VERIFICATION);\n\t}\n\n\tstatic X509Certificate x509Certificate(String source) {\n\t\ttry {\n\t\t\tfinal CertificateFactory factory = CertificateFactory.getInstance(\"X.509\");\n\t\t\treturn (X509Certificate) factory\n\t\t\t\t.generateCertificate(new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)));\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tstatic Saml2X509Credential signingCredential() {\n\t\t// @formatter:off\n\t\tString key = \"-----BEGIN PRIVATE KEY-----\\n\"\n\t\t\t\t+ \"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\\n\"\n\t\t\t\t+ \"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\\n\"\n\t\t\t\t+ \"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\\n\"\n\t\t\t\t+ \"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\\n\"\n\t\t\t\t+ \"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\\n\"\n\t\t\t\t+ \"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\\n\"\n\t\t\t\t+ \"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\\n\"\n\t\t\t\t+ \"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\\n\"\n\t\t\t\t+ \"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\\n\"\n\t\t\t\t+ \"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\\n\"\n\t\t\t\t+ \"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\\n\"\n\t\t\t\t+ \"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\\n\"\n\t\t\t\t+ \"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\\n\"\n\t\t\t\t+ \"INrtuLp4YHbgk1mi\\n\"\n\t\t\t\t+ \"-----END PRIVATE KEY-----\";\n\t\t// @formatter:on\n\t\t// @formatter:off\n\t\tString certificate = \"-----BEGIN CERTIFICATE-----\\n\"\n\t\t\t\t+ \"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\\n\"\n\t\t\t\t+ \"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\\n\"\n\t\t\t\t+ \"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\\n\"\n\t\t\t\t+ \"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\\n\"\n\t\t\t\t+ \"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\\n\"\n\t\t\t\t+ \"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\\n\"\n\t\t\t\t+ \"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\\n\"\n\t\t\t\t+ \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\\n\"\n\t\t\t\t+ \"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\\n\"\n\t\t\t\t+ \"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\\n\"\n\t\t\t\t+ \"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\\n\"\n\t\t\t\t+ \"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\\n\"\n\t\t\t\t+ \"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\\n\"\n\t\t\t\t+ \"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\\n\"\n\t\t\t\t+ \"-----END CERTIFICATE-----\";\n\t\t// @formatter:on\n\t\tPrivateKey pk = RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(key.getBytes()));\n\t\tX509Certificate cert = x509Certificate(certificate);\n\t\treturn new Saml2X509Credential(pk, cert, Saml2X509Credential.Saml2X509CredentialType.SIGNING,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.DECRYPTION);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.nio.charset.StandardCharsets;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DefaultDataBufferFactory;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver;\nimport org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.WebFilterChainProxy;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.reactive.config.DelegatingWebFluxConfiguration;\nimport org.springframework.web.reactive.config.EnableWebFlux;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.result.view.AbstractView;\nimport org.springframework.web.server.WebFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class EnableWebFluxSecurityTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tWebFilterChainProxy springSecurityFilterChain;\n\n\t@Test\n\tpublic void defaultRequiresAuthentication() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurityFilterChain)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectBody().isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t// gh-4831\n\t@Test\n\tpublic void defaultMediaAllThenUnAuthorized() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurityFilterChain)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.accept(MediaType.ALL)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectBody().isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenBasicThenNoSession() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurityFilterChain)\n\t\t\t\t.build();\n\t\tFluxExchangeResult<String> result = client.get()\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.returnResult(String.class);\n\t\t// @formatter:on\n\t\tresult.assertWithDiagnostics(() -> assertThat(result.getResponseCookies().isEmpty()));\n\t}\n\n\t@Test\n\tpublic void defaultPopulatesReactorContext() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tAuthentication currentPrincipal = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tWebSessionServerSecurityContextRepository contextRepository = new WebSessionServerSecurityContextRepository();\n\t\tSecurityContext context = new SecurityContextImpl(currentPrincipal);\n\t\t// @formatter:off\n\t\tWebFilter contextRepositoryWebFilter = (exchange, chain) -> contextRepository.save(exchange, context)\n\t\t\t\t.switchIfEmpty(chain.filter(exchange))\n\t\t\t\t.flatMap((e) -> chain.filter(exchange));\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(contextRepositoryWebFilter, this.springSecurityFilterChain, writePrincipalWebFilter())\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).consumeWith((result) -> assertThat(result.getResponseBody()).isEqualTo(currentPrincipal.getName()));\n\t\t// @formatter:on\n\t}\n\n\tprivate WebFilter writePrincipalWebFilter() {\n\t\t// @formatter:off\n\t\treturn (exchange, chain) -> ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.flatMap((principal) -> exchange.getResponse()\n\t\t\t\t\t\t.writeWith(Mono.just(toDataBuffer(principal.getName())))\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void defaultPopulatesReactorContextWhenAuthenticating() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurityFilterChain, writePrincipalWebFilter())\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).consumeWith((result) -> assertThat(result.getResponseBody()).isEqualTo(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestDataValueProcessor() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\tConfigurableApplicationContext context = this.spring.getContext();\n\t\tCsrfRequestDataValueProcessor rdvp = context.getBean(AbstractView.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME,\n\t\t\t\tCsrfRequestDataValueProcessor.class);\n\t\tassertThat(rdvp).isNotNull();\n\t}\n\n\t@Test\n\tpublic void passwordEncoderBeanIsUsed() {\n\t\tthis.spring.register(CustomPasswordEncoderConfig.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurityFilterChain, writePrincipalWebFilter())\n\t\t\t\t.build();\n\t\tclient.get().uri(\"/\").headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange().expectStatus().isOk()\n\t\t\t\t.expectBody(String.class)\n\t\t\t\t.consumeWith((result) -> assertThat(result.getResponseBody()).isEqualTo(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void passwordUpdateManagerUsed() {\n\t\tthis.spring.register(MapReactiveUserDetailsServiceConfig.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurityFilterChain)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.headers((h) -> h.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t\tReactiveUserDetailsService users = this.spring.getContext().getBean(ReactiveUserDetailsService.class);\n\t\tassertThat(users.findByUsername(\"user\").block().getPassword()).startsWith(\"{bcrypt}\");\n\t}\n\n\t@Test\n\tpublic void formLoginWorks() {\n\t\tthis.spring.register(Config.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurityFilterChain, writePrincipalWebFilter())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\t\t// @formatter:off\n\t\tclient.mutateWith(csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri(\"/login\")\n\t\t\t\t.body(BodyInserters.fromFormData(data))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueMatches(\"Location\", \"/\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void multiWorks() {\n\t\tthis.spring.register(MultiSecurityHttpConfig.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurityFilterChain)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/api/test\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectBody().isEmpty();\n\t\tclient.get()\n\t\t\t\t.uri(\"/test\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void authenticationPrincipalArgumentResolverWhenSpelThenWorks() {\n\t\tthis.spring.register(AuthenticationPrincipalConfig.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClient\n\t\t\t\t.bindToApplicationContext(this.spring.getContext())\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/spel\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).isEqualTo(\"user\");\n\t\t// @formatter:on\n\t}\n\n\tprivate static DataBuffer toDataBuffer(String body) {\n\t\tDataBuffer buffer = new DefaultDataBufferFactory().allocateBuffer();\n\t\tbuffer.write(body.getBytes(StandardCharsets.UTF_8));\n\t\treturn buffer;\n\t}\n\n\t@Test\n\tpublic void enableWebFluxSecurityWhenNoConfigurationAnnotationThenBeanProxyingEnabled() {\n\t\tthis.spring.register(BeanProxyEnabledByDefaultConfig.class).autowire();\n\t\tChild childBean = this.spring.getContext().getBean(Child.class);\n\t\tParent parentBean = this.spring.getContext().getBean(Parent.class);\n\t\tassertThat(parentBean.getChild()).isSameAs(childBean);\n\t}\n\n\t@Test\n\tpublic void enableWebFluxSecurityWhenProxyBeanMethodsFalseThenBeanProxyingDisabled() {\n\t\tthis.spring.register(BeanProxyDisabledConfig.class).autowire();\n\t\tChild childBean = this.spring.getContext().getBean(Child.class);\n\t\tParent parentBean = this.spring.getContext().getBean(Parent.class);\n\t\tassertThat(parentBean.getChild()).isNotSameAs(childBean);\n\t}\n\n\t@Test\n\t// gh-8596\n\tpublic void resolveAuthenticationPrincipalArgumentResolverFirstDoesNotCauseBeanCurrentlyInCreationException() {\n\t\tthis.spring\n\t\t\t.register(EnableWebFluxSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\t\tDelegatingWebFluxConfiguration.class)\n\t\t\t.autowire();\n\t}\n\n\t@Test\n\t// gh-10076\n\tpublic void webFluxConfigurationSupportAndServerHttpSecurityConfigurationDoNotCauseCircularReference() {\n\t\tAnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();\n\t\tcontext.setAllowCircularReferences(false);\n\t\tcontext.register(EnableWebFluxSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\tDelegatingWebFluxConfiguration.class);\n\t\tcontext.setServletContext(new MockServletContext());\n\t\tcontext.refresh();\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(ReactiveAuthenticationTestConfiguration.class)\n\tstatic class Config {\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class CustomPasswordEncoderConfig {\n\n\t\t@Bean\n\t\tReactiveUserDetailsService userDetailsService(PasswordEncoder encoder) {\n\t\t\treturn new MapReactiveUserDetailsService(\n\t\t\t\t\tUser.withUsername(\"user\").password(encoder.encode(\"password\")).roles(\"USER\").build());\n\t\t}\n\n\t\t@Bean\n\t\tstatic PasswordEncoder passwordEncoder() {\n\t\t\treturn new BCryptPasswordEncoder();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class MapReactiveUserDetailsServiceConfig {\n\n\t\t@Bean\n\t\tMapReactiveUserDetailsService userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\treturn new MapReactiveUserDetailsService(User.withUsername(\"user\")\n\t\t\t\t\t.password(\"{noop}password\")\n\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t.build()\n\t\t\t// @formatter:on\n\t\t\t);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(ReactiveAuthenticationTestConfiguration.class)\n\tstatic class MultiSecurityHttpConfig {\n\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\t@Bean\n\t\tSecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {\n\t\t\thttp.securityMatcher(new PathPatternParserServerWebExchangeMatcher(\"/api/**\"))\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().denyAll());\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityWebFilterChain httpSecurity(ServerHttpSecurity http) {\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@EnableWebFlux\n\t@Import(ReactiveAuthenticationTestConfiguration.class)\n\tstatic class AuthenticationPrincipalConfig {\n\n\t\t@Bean\n\t\tPrincipalBean principalBean() {\n\t\t\treturn new PrincipalBean();\n\t\t}\n\n\t\tstatic class PrincipalBean {\n\n\t\t\tpublic String username(UserDetails user) {\n\t\t\t\treturn user.getUsername();\n\t\t\t}\n\n\t\t}\n\n\t\t@Target({ ElementType.PARAMETER })\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\t@AuthenticationPrincipal\n\t\t@interface Property {\n\n\t\t\t@AliasFor(attribute = \"expression\", annotation = AuthenticationPrincipal.class)\n\t\t\tString value() default \"id\";\n\n\t\t}\n\n\t\tinterface UsernameResolver {\n\n\t\t\tString username(@Property(\"@principalBean.username(#this)\") String username);\n\n\t\t}\n\n\t\t@RestController\n\t\tstatic class AuthenticationPrincipalResolver implements UsernameResolver {\n\n\t\t\t@Override\n\t\t\t@GetMapping(\"/spel\")\n\t\t\tpublic String username(String username) {\n\t\t\t\treturn username;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(ReactiveAuthenticationTestConfiguration.class)\n\tstatic class BeanProxyEnabledByDefaultConfig {\n\n\t\t@Bean\n\t\tChild child() {\n\t\t\treturn new Child();\n\t\t}\n\n\t\t@Bean\n\t\tParent parent() {\n\t\t\treturn new Parent(child());\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFluxSecurity\n\t@Import(ReactiveAuthenticationTestConfiguration.class)\n\tstatic class BeanProxyDisabledConfig {\n\n\t\t@Bean\n\t\tChild child() {\n\t\t\treturn new Child();\n\t\t}\n\n\t\t@Bean\n\t\tParent parent() {\n\t\t\treturn new Parent(child());\n\t\t}\n\n\t}\n\n\tstatic class Parent {\n\n\t\tprivate Child child;\n\n\t\tParent(Child child) {\n\t\t\tthis.child = child;\n\t\t}\n\n\t\tChild getChild() {\n\t\t\treturn this.child;\n\t\t}\n\n\t}\n\n\tstatic class Child {\n\n\t\tChild() {\n\t\t}\n\n\t}\n\n\t@EnableWebFluxSecurity\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class EnableWebFluxSecurityConfiguration {\n\n\t\t/**\n\t\t * It is necessary to Autowire AuthenticationPrincipalArgumentResolver because it\n\t\t * triggers eager loading of AuthenticationPrincipalArgumentResolver bean which\n\t\t * causes BeanCurrentlyInCreationException\n\t\t */\n\t\t@Autowired\n\t\tAuthenticationPrincipalArgumentResolver resolver;\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/reactive/ReactiveOAuth2AuthorizedClientManagerConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.oauth2.client.AuthorizationCodeReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.ClientCredentialsReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.JwtBearerReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationContext;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.RefreshTokenReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.TokenExchangeReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.server.WebSessionServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for\n * {@link ReactiveOAuth2ClientConfiguration.ReactiveOAuth2AuthorizedClientManagerConfiguration}.\n *\n * @author Steve Riesenberg\n */\npublic class ReactiveOAuth2AuthorizedClientManagerConfigurationTests {\n\n\tprivate static ReactiveOAuth2AccessTokenResponseClient<? super AbstractOAuth2AuthorizationGrantRequest> MOCK_RESPONSE_CLIENT;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate ReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@Autowired\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Autowired(required = false)\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Autowired(required = false)\n\tprivate ReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\t@Autowired(required = false)\n\tprivate AuthorizationCodeReactiveOAuth2AuthorizedClientProvider authorizationCodeAuthorizedClientProvider;\n\n\tprivate MockServerWebExchange exchange;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setUp() {\n\t\tMOCK_RESPONSE_CLIENT = mock(ReactiveOAuth2AccessTokenResponseClient.class);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/\").build();\n\t\tthis.exchange = MockServerWebExchange.builder(request).build();\n\t}\n\n\t@Test\n\tpublic void loadContextWhenOAuth2ClientEnabledThenConfigured() {\n\t\tthis.spring.register(MinimalOAuth2ClientConfig.class).autowire();\n\t\tassertThat(this.authorizedClientManager).isNotNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizationCodeAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null, \"ROLE_USER\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(\"google\")\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(ServerWebExchange.class.getName(), this.exchange)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest).block())\n\t\t\t\t.extracting(OAuth2AuthorizationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(\"client_authorization_required\");\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationCodeAuthorizedClientProvider).authorize(any(OAuth2AuthorizationContext.class));\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedClientServiceBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientServiceConfig.class).autowire();\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null, \"ROLE_USER\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(\"google\")\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(ServerWebExchange.class.getName(), this.exchange)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest).block())\n\t\t\t\t.extracting(OAuth2AuthorizationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(\"client_authorization_required\");\n\t\t// @formatter:on\n\n\t\tverify(this.authorizedClientService).loadAuthorizedClient(authorizeRequest.getClientRegistrationId(),\n\t\t\t\tauthentication.getName());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRefreshTokenAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire();\n\t\ttestRefreshTokenGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRefreshTokenAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\t\ttestRefreshTokenGrant();\n\t}\n\n\tprivate void testRefreshTokenGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null, \"ROLE_USER\");\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google\")\n\t\t\t.block();\n\t\tassertThat(clientRegistration).isNotNull();\n\t\tOAuth2AuthorizedClient existingAuthorizedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\tauthentication.getName(), getExpiredAccessToken(), TestOAuth2RefreshTokens.refreshToken());\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(existingAuthorizedClient, authentication, this.exchange)\n\t\t\t.block();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(ServerWebExchange.class.getName(), this.exchange)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<OAuth2RefreshTokenGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2RefreshTokenGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);\n\t\tassertThat(grantRequest.getAccessToken()).isEqualTo(existingAuthorizedClient.getAccessToken());\n\t\tassertThat(grantRequest.getRefreshToken()).isEqualTo(existingAuthorizedClient.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire();\n\t\ttestClientCredentialsGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\t\ttestClientCredentialsGrant();\n\t}\n\n\tprivate void testClientCredentialsGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null, \"ROLE_USER\");\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"github\")\n\t\t\t.block();\n\t\tassertThat(clientRegistration).isNotNull();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(ServerWebExchange.class.getName(), this.exchange)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<OAuth2ClientCredentialsGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2ClientCredentialsGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire();\n\t\ttestJwtBearerGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\t\ttestJwtBearerGrant();\n\t}\n\n\tprivate void testJwtBearerGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(JwtBearerGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt());\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"okta\").block();\n\t\tassertThat(clientRegistration).isNotNull();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(ServerWebExchange.class.getName(), this.exchange)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<JwtBearerGrantRequest> grantRequestCaptor = ArgumentCaptor.forClass(JwtBearerGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tJwtBearerGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER);\n\t\tassertThat(grantRequest.getJwt().getSubject()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire();\n\t\ttestTokenExchangeGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire();\n\t\ttestTokenExchangeGrant();\n\t}\n\n\tprivate void testTokenExchangeGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt());\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"auth0\").block();\n\t\tassertThat(clientRegistration).isNotNull();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(ServerWebExchange.class.getName(), this.exchange)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(authentication.getToken());\n\t}\n\n\tprivate static OAuth2AccessToken getExpiredAccessToken() {\n\t\tInstant expiresAt = Instant.now().minusSeconds(60);\n\t\tInstant issuedAt = expiresAt.minus(Duration.ofDays(1));\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"scopes\", issuedAt, expiresAt,\n\t\t\t\tnew HashSet<>(Arrays.asList(\"read\", \"write\")));\n\t}\n\n\tprivate static Jwt getJwt() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new Jwt(\"token\", issuedAt, issuedAt.plusSeconds(300),\n\t\t\t\tCollections.singletonMap(JoseHeaderNames.ALG, \"RS256\"),\n\t\t\t\tCollections.singletonMap(JwtClaimNames.SUB, \"user\"));\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class MinimalOAuth2ClientConfig extends OAuth2ClientBaseConfig {\n\n\t\t@Bean\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn new WebSessionServerOAuth2AuthorizedClientRepository();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class CustomAuthorizedClientServiceConfig extends OAuth2ClientBaseConfig {\n\n\t\t@Bean\n\t\tReactiveOAuth2AuthorizedClientService authorizedClientService() {\n\t\t\tReactiveOAuth2AuthorizedClientService authorizedClientService = mock(\n\t\t\t\t\tReactiveOAuth2AuthorizedClientService.class);\n\t\t\tgiven(authorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(Mono.empty());\n\t\t\treturn authorizedClientService;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class CustomAccessTokenResponseClientsConfig extends MinimalOAuth2ClientConfig {\n\n\t\t@Bean\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenAccessResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {\n\t\t\treturn new MockAccessTokenResponseClient<>();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class CustomAuthorizedClientProvidersConfig extends MinimalOAuth2ClientConfig {\n\n\t\t@Bean\n\t\tAuthorizationCodeReactiveOAuth2AuthorizedClientProvider authorizationCode() {\n\t\t\treturn spy(new AuthorizationCodeReactiveOAuth2AuthorizedClientProvider());\n\t\t}\n\n\t\t@Bean\n\t\tRefreshTokenReactiveOAuth2AuthorizedClientProvider refreshToken() {\n\t\t\tRefreshTokenReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = new RefreshTokenReactiveOAuth2AuthorizedClientProvider();\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(new MockAccessTokenResponseClient<>());\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\t@Bean\n\t\tClientCredentialsReactiveOAuth2AuthorizedClientProvider clientCredentials() {\n\t\t\tClientCredentialsReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(new MockAccessTokenResponseClient<>());\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\t@Bean\n\t\tJwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearer() {\n\t\t\tJwtBearerReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = new JwtBearerReactiveOAuth2AuthorizedClientProvider();\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(new MockAccessTokenResponseClient<>());\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t\t@Bean\n\t\tTokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchange() {\n\t\t\tTokenExchangeReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = new TokenExchangeReactiveOAuth2AuthorizedClientProvider();\n\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(new MockAccessTokenResponseClient<>());\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t}\n\n\tabstract static class OAuth2ClientBaseConfig {\n\n\t\t@Bean\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\t// @formatter:off\n\t\t\treturn new InMemoryReactiveClientRegistrationRepository(\n\t\t\t\t\tCommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t\t\t\t\t\t.clientId(\"google-client-id\")\n\t\t\t\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t\t.build(),\n\t\t\t\t\tCommonOAuth2Provider.GITHUB.getBuilder(\"github\")\n\t\t\t\t\t\t.clientId(\"github-client-id\")\n\t\t\t\t\t\t.clientSecret(\"github-client-secret\")\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t\t\t.build(),\n\t\t\t\t\tCommonOAuth2Provider.OKTA.getBuilder(\"okta\")\n\t\t\t\t\t\t.clientId(\"okta-client-id\")\n\t\t\t\t\t\t.clientSecret(\"okta-client-secret\")\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t\t\t\t.build(),\n\t\t\t\t\tClientRegistration.withRegistrationId(\"auth0\")\n\t\t\t\t\t\t.clientName(\"Auth0\")\n\t\t\t\t\t\t.clientId(\"auth0-client-id\")\n\t\t\t\t\t\t.clientSecret(\"auth0-client-secret\")\n\t\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t\t\t\t.scope(\"user.read\", \"user.write\")\n\t\t\t\t\t\t.build());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tprivate static class MockAccessTokenResponseClient<T extends AbstractOAuth2AuthorizationGrantRequest>\n\t\t\timplements ReactiveOAuth2AccessTokenResponseClient<T> {\n\n\t\t@Override\n\t\tpublic Mono<OAuth2AccessTokenResponse> getTokenResponse(T grantRequest) {\n\t\t\treturn MOCK_RESPONSE_CLIENT.getTokenResponse(grantRequest);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/reactive/ReactiveOAuth2ClientImportSelectorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.config.EnableWebFlux;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link ReactiveOAuth2ClientImportSelector}.\n *\n * @author Alavudin Kuttikkattil\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class ReactiveOAuth2ClientImportSelectorTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tWebTestClient client;\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\t// @formatter:off\n\t\tthis.client = WebTestClient\n\t\t\t\t.bindToApplicationContext(context)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizedClientManagerConfiguredThenUsed() {\n\t\tString clientRegistrationId = \"client\";\n\t\tString principalName = \"user\";\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tReactiveClientRegistrationRepository.class);\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = mock(\n\t\t\t\tServerOAuth2AuthorizedClientRepository.class);\n\t\tReactiveOAuth2AuthorizedClientManager authorizedClientManager = mock(\n\t\t\t\tReactiveOAuth2AuthorizedClientManager.class);\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.registrationId(clientRegistrationId)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principalName,\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tgiven(authorizedClientManager.authorize(any())).willReturn(Mono.just(authorizedClient));\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository;\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.AUTHORIZED_CLIENT_REPOSITORY = authorizedClientRepository;\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.AUTHORIZED_CLIENT_MANAGER = authorizedClientManager;\n\t\tthis.spring.register(OAuth2AuthorizedClientManagerRegisteredConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client\n\t\t\t\t.get()\n\t\t\t\t.uri(\"http://localhost/authorized-client\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\")).exchange().expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).isEqualTo(\"resolved\");\n\t\t// @formatter:on\n\t\tverify(authorizedClientManager).authorize(any());\n\t\tverifyNoInteractions(clientRegistrationRepository);\n\t\tverifyNoInteractions(authorizedClientRepository);\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizedClientManagerNotConfigureThenUseDefaultAuthorizedClientManager() {\n\t\tString clientRegistrationId = \"client\";\n\t\tString principalName = \"user\";\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tReactiveClientRegistrationRepository.class);\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = mock(\n\t\t\t\tServerOAuth2AuthorizedClientRepository.class);\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.registrationId(clientRegistrationId)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principalName,\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.CLIENT_REGISTRATION_REPOSITORY = clientRegistrationRepository;\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.AUTHORIZED_CLIENT_REPOSITORY = authorizedClientRepository;\n\t\tOAuth2AuthorizedClientManagerRegisteredConfig.AUTHORIZED_CLIENT_MANAGER = null;\n\t\tgiven(authorizedClientRepository.loadAuthorizedClient(any(), any(), any()))\n\t\t\t.willReturn(Mono.just(authorizedClient));\n\t\tthis.spring.register(OAuth2AuthorizedClientManagerRegisteredConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client\n\t\t\t\t.get()\n\t\t\t\t.uri(\"http://localhost/authorized-client\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\")).exchange().expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).isEqualTo(\"resolved\");\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2AuthorizedClientManagerRegisteredConfig {\n\n\t\tstatic ReactiveClientRegistrationRepository CLIENT_REGISTRATION_REPOSITORY;\n\t\tstatic ServerOAuth2AuthorizedClientRepository AUTHORIZED_CLIENT_REPOSITORY;\n\t\tstatic ReactiveOAuth2AuthorizedClientManager AUTHORIZED_CLIENT_MANAGER;\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn CLIENT_REGISTRATION_REPOSITORY;\n\t\t}\n\n\t\t@Bean\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn AUTHORIZED_CLIENT_REPOSITORY;\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOAuth2AuthorizedClientManager authorizedClientManager() {\n\t\t\treturn AUTHORIZED_CLIENT_MANAGER;\n\t\t}\n\n\t\t@RestController\n\t\tclass Controller {\n\n\t\t\t@GetMapping(\"/authorized-client\")\n\t\t\tString authorizedClient(\n\t\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"client1\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn (authorizedClient != null) ? \"resolved\" : \"not-resolved\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/reactive/ServerHttpSecurityConfigurationBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;\nimport org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic final class ServerHttpSecurityConfigurationBuilder {\n\n\tprivate ServerHttpSecurityConfigurationBuilder() {\n\t}\n\n\tpublic static ServerHttpSecurity http() {\n\t\treturn new ServerHttpSecurityConfiguration().httpSecurity();\n\t}\n\n\tpublic static ServerHttpSecurity httpWithDefaultAuthentication() {\n\t\tReactiveUserDetailsService reactiveUserDetailsService = ReactiveAuthenticationTestConfiguration\n\t\t\t.userDetailsService();\n\t\tReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(\n\t\t\t\treactiveUserDetailsService);\n\t\treturn http().authenticationManager(authenticationManager);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/reactive/ServerHttpSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.net.URI;\nimport java.util.Iterator;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InOrder;\nimport org.mockito.Mockito;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.password.CompromisedPasswordDecision;\nimport org.springframework.security.authentication.password.CompromisedPasswordException;\nimport org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.rsocket.EnableRSocketSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.config.web.server.ServerHttpSecurity.AuthorizeExchangeSpec;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.config.EnableWebFlux;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.server.adapter.WebHttpHandlerBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockAuthentication;\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;\n\n/**\n * Tests for {@link ServerHttpSecurityConfiguration}.\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class ServerHttpSecurityConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tWebTestClient webClient;\n\n\t@Autowired\n\tvoid setup(ApplicationContext context) {\n\t\tif (!context.containsBean(WebHttpHandlerBuilder.WEB_HANDLER_BEAN_NAME)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.webClient = WebTestClient.bindToApplicationContext(context)\n\t\t\t.apply(springSecurity())\n\t\t\t.configureClient()\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenReactiveUserAuthenticationServiceConfiguredThenServerHttpSecurityExists() {\n\t\tthis.spring\n\t\t\t.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\t\tWebFluxSecurityConfiguration.class)\n\t\t\t.autowire();\n\t\tServerHttpSecurity serverHttpSecurity = this.spring.getContext().getBean(ServerHttpSecurity.class);\n\t\tassertThat(serverHttpSecurity).isNotNull();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenOnlyReactiveUserDetailsServiceConfiguredThenServerHttpSecurityExists() {\n\t\tthis.spring\n\t\t\t.register(ServerHttpSecurityConfiguration.class, ReactiveUserDetailsServiceOnlyTestConfiguration.class,\n\t\t\t\t\tWebFluxSecurityConfiguration.class)\n\t\t\t.autowire();\n\t\tServerHttpSecurity serverHttpSecurity = this.spring.getContext().getBean(ServerHttpSecurity.class);\n\t\tassertThat(serverHttpSecurity).isNotNull();\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenProxyingEnabledAndSubclassThenServerHttpSecurityExists() {\n\t\tthis.spring\n\t\t\t.register(SubclassConfig.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\t\tWebFluxSecurityConfiguration.class)\n\t\t\t.autowire();\n\t\tServerHttpSecurity serverHttpSecurity = this.spring.getContext().getBean(ServerHttpSecurity.class);\n\t\tassertThat(serverHttpSecurity).isNotNull();\n\t}\n\n\t@Test\n\tvoid loginWhenCompromisePasswordCheckerConfiguredAndPasswordCompromisedThenUnauthorized() {\n\t\tthis.spring.register(FormLoginConfig.class, UserDetailsConfig.class, CompromisedPasswordCheckerConfig.class)\n\t\t\t.autowire();\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\t\t// @formatter:off\n\t\tthis.webClient.mutateWith(csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri(\"/login\")\n\t\t\t\t.body(BodyInserters.fromFormData(data))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().location(\"/login?error\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid loginWhenCompromisePasswordCheckerConfiguredAndPasswordNotCompromisedThenUnauthorized() {\n\t\tthis.spring.register(FormLoginConfig.class, UserDetailsConfig.class, CompromisedPasswordCheckerConfig.class)\n\t\t\t.autowire();\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"admin\");\n\t\tdata.add(\"password\", \"password2\");\n\t\t// @formatter:off\n\t\tthis.webClient.mutateWith(csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri(\"/login\")\n\t\t\t\t.body(BodyInserters.fromFormData(data))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().location(\"/\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid loginWhenCompromisedPasswordAndRedirectIfPasswordExceptionThenRedirectedToResetPassword() {\n\t\tthis.spring\n\t\t\t.register(FormLoginRedirectToResetPasswordConfig.class, UserDetailsConfig.class,\n\t\t\t\t\tCompromisedPasswordCheckerConfig.class)\n\t\t\t.autowire();\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\t\t// @formatter:off\n\t\tthis.webClient.mutateWith(csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri(\"/login\")\n\t\t\t\t.body(BodyInserters.fromFormData(data))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().location(\"/reset-password\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenTemplateDefaultsBeanThenResolvesExpression() throws Exception {\n\t\tthis.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();\n\t\tAuthentication user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tthis.webClient.mutateWith(mockAuthentication(user))\n\t\t\t.get()\n\t\t\t.uri(\"/hi\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.isEqualTo(\"Hi, Stranger!\");\n\t\tAuthentication harold = new TestingAuthenticationToken(\"harold\", \"password\", \"ROLE_USER\");\n\t\tthis.webClient.mutateWith(mockAuthentication(harold))\n\t\t\t.get()\n\t\t\t.uri(\"/hi\")\n\t\t\t.exchange()\n\t\t\t.expectBody(String.class)\n\t\t\t.isEqualTo(\"Hi, Harold!\");\n\t}\n\n\t@Test\n\tpublic void resoleMetaAnnotationWhenTemplateDefaultsBeanThenResolvesExpression() throws Exception {\n\t\tthis.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();\n\t\tAuthentication user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tthis.webClient.mutateWith(mockAuthentication(user))\n\t\t\t.get()\n\t\t\t.uri(\"/hello\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.isEqualTo(\"user\");\n\t\tAuthentication harold = new TestingAuthenticationToken(\"harold\", \"password\", \"ROLE_USER\");\n\t\tthis.webClient.mutateWith(mockAuthentication(harold))\n\t\t\t.get()\n\t\t\t.uri(\"/hello\")\n\t\t\t.exchange()\n\t\t\t.expectBody(String.class)\n\t\t\t.isEqualTo(\"harold\");\n\t}\n\n\t@Test\n\tpublic void getWhenUsingObservationRegistryThenObservesRequest() {\n\t\tthis.spring.register(ObservationRegistryConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.webClient\n\t\t\t\t.get()\n\t\t\t\t.uri(\"/hello\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.isNotFound();\n\t\t// @formatter:on\n\t\tObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(6)).onStart(captor.capture());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"http get\");\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"security filterchain before\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authentications\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authorizations\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.http.secured.requests\");\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"security filterchain after\");\n\t}\n\n\t// gh-16161\n\t@Test\n\tpublic void getWhenUsingRSocketThenObservesRequest() {\n\t\tthis.spring.register(ObservationRegistryConfig.class, RSocketSecurityConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.webClient\n\t\t\t\t.get()\n\t\t\t\t.uri(\"/hello\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.isNotFound();\n\t\t// @formatter:on\n\t\tObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(6)).onStart(captor.capture());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"http get\");\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"security filterchain before\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authentications\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authorizations\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.http.secured.requests\");\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"security filterchain after\");\n\t}\n\n\t@Test\n\tvoid authorizeExchangeCustomizerBean() {\n\t\tthis.spring.register(AuthorizeExchangeCustomizerBeanConfig.class).autowire();\n\t\tCustomizer<AuthorizeExchangeSpec> authzCustomizer = this.spring.getContext().getBean(\"authz\", Customizer.class);\n\n\t\tArgumentCaptor<AuthorizeExchangeSpec> arg0 = ArgumentCaptor.forClass(AuthorizeExchangeSpec.class);\n\t\tverify(authzCustomizer).customize(arg0.capture());\n\t}\n\n\t@Test\n\tvoid multiAuthorizeExchangeCustomizerBean() {\n\t\tthis.spring.register(MultiAuthorizeExchangeCustomizerBeanConfig.class).autowire();\n\t\tCustomizer<AuthorizeExchangeSpec> authzCustomizer = this.spring.getContext().getBean(\"authz\", Customizer.class);\n\n\t\tArgumentCaptor<AuthorizeExchangeSpec> arg0 = ArgumentCaptor.forClass(AuthorizeExchangeSpec.class);\n\t\tverify(authzCustomizer).customize(arg0.capture());\n\t}\n\n\t@Test\n\tvoid serverHttpSecurityCustomizerBean() {\n\t\tthis.spring.register(ServerHttpSecurityCustomizerConfig.class).autowire();\n\t\tCustomizer<ServerHttpSecurity> httpSecurityCustomizer = this.spring.getContext()\n\t\t\t.getBean(\"httpSecurityCustomizer\", Customizer.class);\n\n\t\tArgumentCaptor<ServerHttpSecurity> arg0 = ArgumentCaptor.forClass(ServerHttpSecurity.class);\n\t\tverify(httpSecurityCustomizer).customize(arg0.capture());\n\t}\n\n\t@Test\n\tvoid multiServerHttpSecurityCustomizerBean() {\n\t\tthis.spring.register(MultiServerHttpSecurityCustomizerConfig.class).autowire();\n\t\tCustomizer<ServerHttpSecurity> httpSecurityCustomizer = this.spring.getContext()\n\t\t\t.getBean(\"httpSecurityCustomizer\", Customizer.class);\n\t\tCustomizer<ServerHttpSecurity> httpSecurityCustomizer0 = this.spring.getContext()\n\t\t\t.getBean(\"httpSecurityCustomizer0\", Customizer.class);\n\t\tInOrder inOrder = Mockito.inOrder(httpSecurityCustomizer0, httpSecurityCustomizer);\n\t\tArgumentCaptor<ServerHttpSecurity> arg0 = ArgumentCaptor.forClass(ServerHttpSecurity.class);\n\t\tinOrder.verify(httpSecurityCustomizer0).customize(arg0.capture());\n\t\tinOrder.verify(httpSecurityCustomizer).customize(arg0.capture());\n\t}\n\n\t@Configuration\n\tstatic class SubclassConfig extends ServerHttpSecurityConfiguration {\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class FormLoginConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.formLogin(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class FormLoginRedirectToResetPasswordConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.formLogin((form) -> form\n\t\t\t\t\t\t\t.authenticationFailureHandler((webFilterExchange, exception) -> {\n\t\t\t\t\t\t\t\tString redirectUrl = \"/login?error\";\n\t\t\t\t\t\t\t\tif (exception instanceof CompromisedPasswordException) {\n\t\t\t\t\t\t\t\t\tredirectUrl = \"/reset-password\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn new DefaultServerRedirectStrategy().sendRedirect(webFilterExchange.getExchange(), URI.create(redirectUrl));\n\t\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class UserDetailsConfig {\n\n\t\t@Bean\n\t\tMapReactiveUserDetailsService userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails user = PasswordEncodedUser.user();\n\t\t\tUserDetails admin = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"admin\")\n\t\t\t\t\t.password(\"password2\")\n\t\t\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\treturn new MapReactiveUserDetailsService(user, admin);\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class CompromisedPasswordCheckerConfig {\n\n\t\t@Bean\n\t\tTestReactivePasswordChecker compromisedPasswordChecker() {\n\t\t\treturn new TestReactivePasswordChecker();\n\t\t}\n\n\t}\n\n\tstatic class TestReactivePasswordChecker implements ReactiveCompromisedPasswordChecker {\n\n\t\t@Override\n\t\tpublic Mono<CompromisedPasswordDecision> check(String password) {\n\t\t\tif (\"password\".equals(password)) {\n\t\t\t\treturn Mono.just(new CompromisedPasswordDecision(true));\n\t\t\t}\n\t\t\treturn Mono.just(new CompromisedPasswordDecision(false));\n\t\t}\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@AuthenticationPrincipal(expression = \"#this.equals('{value}')\")\n\t@interface IsUser {\n\n\t\tString value() default \"user\";\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(expression = \"authentication.{property}\")\n\t@interface CurrentAuthenticationProperty {\n\n\t\tString property();\n\n\t}\n\n\t@RestController\n\tstatic class TestController {\n\n\t\t@GetMapping(\"/hi\")\n\t\tString ifUser(@IsUser(\"harold\") boolean isHarold) {\n\t\t\tif (isHarold) {\n\t\t\t\treturn \"Hi, Harold!\";\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn \"Hi, Stranger!\";\n\t\t\t}\n\t\t}\n\n\t\t@GetMapping(\"/hello\")\n\t\tString getCurrentAuthenticationProperty(\n\t\t\t\t@CurrentAuthenticationProperty(property = \"principal\") String principal) {\n\t\t\treturn principal;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class MetaAnnotationPlaceholderConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.httpBasic(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveUserDetailsService userDetailsService() {\n\t\t\treturn new MapReactiveUserDetailsService(\n\t\t\t\t\tUser.withUsername(\"user\").password(\"password\").authorities(\"app\").build());\n\t\t}\n\n\t\t@Bean\n\t\tTestController testController() {\n\t\t\treturn new TestController();\n\t\t}\n\n\t\t@Bean\n\t\tAnnotationTemplateExpressionDefaults templateExpressionDefaults() {\n\t\t\treturn new AnnotationTemplateExpressionDefaults();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class ObservationRegistryConfig {\n\n\t\tprivate ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain app(ServerHttpSecurity http) throws Exception {\n\t\t\thttp.httpBasic(withDefaults()).authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveUserDetailsService userDetailsService() {\n\t\t\treturn new MapReactiveUserDetailsService(\n\t\t\t\t\tUser.withDefaultPasswordEncoder().username(\"user\").password(\"password\").authorities(\"app\").build());\n\t\t}\n\n\t\t@Bean\n\t\tObservationHandler<Observation.Context> observationHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\t\tregistry.observationConfig().observationHandler(this.handler);\n\t\t\treturn registry;\n\t\t}\n\n\t}\n\n\t@EnableRSocketSecurity\n\tstatic class RSocketSecurityConfig {\n\n\t\t@Bean\n\t\tRSocketMessageHandler messageHandler() {\n\t\t\treturn new RSocketMessageHandler();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(UserDetailsConfig.class)\n\tstatic class AuthorizeExchangeCustomizerBeanConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tstatic Customizer<AuthorizeExchangeSpec> authz() {\n\t\t\treturn mock(Customizer.class);\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@Import(AuthorizeExchangeCustomizerBeanConfig.class)\n\tstatic class MultiAuthorizeExchangeCustomizerBeanConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tCustomizer<AuthorizeExchangeSpec> authz0() {\n\t\t\treturn mock(Customizer.class);\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(UserDetailsConfig.class)\n\tstatic class ServerHttpSecurityCustomizerConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tstatic Customizer<ServerHttpSecurity> httpSecurityCustomizer() {\n\t\t\treturn mock(Customizer.class);\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@Import(ServerHttpSecurityCustomizerConfig.class)\n\tstatic class MultiServerHttpSecurityCustomizerConfig {\n\n\t\t@Bean\n\t\t@Order(Ordered.HIGHEST_PRECEDENCE)\n\t\tstatic Customizer<ServerHttpSecurity> httpSecurityCustomizer0() {\n\t\t\treturn mock(Customizer.class);\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class ReactiveUserDetailsServiceOnlyTestConfiguration {\n\n\t\t@Bean\n\t\tstatic ReactiveUserDetailsService userDetailsService() {\n\t\t\treturn (username) -> Mono.just(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.reactive;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.lang.NonNull;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;\nimport org.springframework.security.web.server.WebFilterChainProxy;\nimport org.springframework.security.web.server.firewall.HttpStatusExchangeRejectedHandler;\nimport org.springframework.security.web.server.firewall.ServerExchangeRejectedHandler;\nimport org.springframework.security.web.server.firewall.ServerWebExchangeFirewall;\nimport org.springframework.web.server.handler.DefaultWebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link WebFluxSecurityConfiguration}.\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class WebFluxSecurityConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void loadConfigWhenReactiveUserDetailsServiceConfiguredThenWebFilterChainProxyExists() {\n\t\tthis.spring\n\t\t\t.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\t\tWebFluxSecurityConfiguration.class)\n\t\t\t.autowire();\n\t\tWebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class);\n\t\tassertThat(webFilterChainProxy).isNotNull();\n\t}\n\n\t@Test\n\tvoid loadConfigWhenDefaultThenFirewalled() throws Exception {\n\t\tthis.spring\n\t\t\t.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\t\tWebFluxSecurityConfiguration.class)\n\t\t\t.autowire();\n\t\tWebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/;/\").build());\n\t\tDefaultWebFilterChain chain = emptyChain();\n\t\twebFilterChainProxy.filter(exchange, chain).block();\n\t\tassertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);\n\t}\n\n\t@Test\n\tvoid loadConfigWhenCustomRejectedHandler() throws Exception {\n\t\tthis.spring\n\t\t\t.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\t\tWebFluxSecurityConfiguration.class, CustomServerExchangeRejectedHandlerConfig.class)\n\t\t\t.autowire();\n\t\tWebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/;/\").build());\n\t\tDefaultWebFilterChain chain = emptyChain();\n\t\twebFilterChainProxy.filter(exchange, chain).block();\n\t\tassertThat(exchange.getResponse().getStatusCode())\n\t\t\t.isEqualTo(CustomServerExchangeRejectedHandlerConfig.EXPECTED_STATUS);\n\t}\n\n\t@Test\n\tvoid loadConfigWhenFirewallBeanThenCustomized() throws Exception {\n\t\tthis.spring\n\t\t\t.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\t\tWebFluxSecurityConfiguration.class, NoOpFirewallConfig.class)\n\t\t\t.autowire();\n\t\tWebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/;/\").build());\n\t\tDefaultWebFilterChain chain = emptyChain();\n\t\twebFilterChainProxy.filter(exchange, chain).block();\n\t\tassertThat(exchange.getResponse().getStatusCode()).isNotEqualTo(HttpStatus.BAD_REQUEST);\n\t}\n\n\t@Test\n\tpublic void loadConfigWhenBeanProxyingEnabledAndSubclassThenWebFilterChainProxyExists() {\n\t\tthis.spring\n\t\t\t.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,\n\t\t\t\t\tWebFluxSecurityConfigurationTests.SubclassConfig.class)\n\t\t\t.autowire();\n\t\tWebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class);\n\t\tassertThat(webFilterChainProxy).isNotNull();\n\t}\n\n\tprivate static @NonNull DefaultWebFilterChain emptyChain() {\n\t\treturn new DefaultWebFilterChain((webExchange) -> Mono.empty(), Collections.emptyList());\n\t}\n\n\t@Configuration\n\tstatic class NoOpFirewallConfig {\n\n\t\t@Bean\n\t\tServerWebExchangeFirewall noOpFirewall() {\n\t\t\treturn ServerWebExchangeFirewall.INSECURE_NOOP;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class CustomServerExchangeRejectedHandlerConfig {\n\n\t\tstatic HttpStatus EXPECTED_STATUS = HttpStatus.I_AM_A_TEAPOT;\n\n\t\t@Bean\n\t\tServerExchangeRejectedHandler rejectedHandler() {\n\t\t\treturn new HttpStatusExchangeRejectedHandler(EXPECTED_STATUS);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SubclassConfig extends WebFluxSecurityConfiguration {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/socket/SyncExecutorSubscribableChannelPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.messaging.support.ExecutorSubscribableChannel;\n\n/**\n * @author Rob Winch\n */\npublic class SyncExecutorSubscribableChannelPostProcessor implements BeanPostProcessor {\n\n\t@Override\n\tpublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n\t\tif (bean instanceof ExecutorSubscribableChannel original) {\n\t\t\tExecutorSubscribableChannel channel = new ExecutorSubscribableChannel();\n\t\t\tchannel.setInterceptors(original.getInterceptors());\n\t\t\treturn channel;\n\t\t}\n\t\treturn bean;\n\t}\n\n\t@Override\n\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\treturn bean;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/socket/TestDeferredCsrfToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\n\n/**\n * @author Steve Riesenberg\n */\nfinal class TestDeferredCsrfToken implements DeferredCsrfToken {\n\n\tprivate final CsrfToken csrfToken;\n\n\tTestDeferredCsrfToken(CsrfToken csrfToken) {\n\t\tthis.csrfToken = csrfToken;\n\t}\n\n\t@Override\n\tpublic CsrfToken get() {\n\t\treturn this.csrfToken;\n\t}\n\n\t@Override\n\tpublic boolean isGenerated() {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfigurationDocTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport java.util.HashMap;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.MessageDeliveryException;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.simp.config.MessageBrokerRegistry;\nimport org.springframework.messaging.support.GenericMessage;\nimport org.springframework.mock.web.MockServletConfig;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;\nimport org.springframework.web.socket.config.annotation.StompEndpointRegistry;\nimport org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\npublic class WebSocketMessageBrokerSecurityConfigurationDocTests {\n\n\tAnnotationConfigWebApplicationContext context;\n\n\tTestingAuthenticationToken messageUser;\n\n\tCsrfToken token;\n\n\tString sessionAttr;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.token = new DefaultCsrfToken(\"header\", \"param\", \"token\");\n\t\tthis.sessionAttr = \"sessionAttr\";\n\t\tthis.messageUser = new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\");\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void securityMappings() {\n\t\tloadConfig(WebSocketSecurityConfig.class);\n\t\tclientInboundChannel().send(message(\"/user/queue/errors\", SimpMessageType.SUBSCRIBE));\n\t\tassertThatExceptionOfType(MessageDeliveryException.class)\n\t\t\t.isThrownBy(() -> clientInboundChannel().send(message(\"/denyAll\", SimpMessageType.MESSAGE)))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\tprivate void loadConfig(Class<?>... configs) {\n\t\tthis.context = new AnnotationConfigWebApplicationContext();\n\t\tthis.context.register(configs);\n\t\tthis.context.register(WebSocketConfig.class, SyncExecutorConfig.class);\n\t\tthis.context.setServletConfig(new MockServletConfig());\n\t\tthis.context.refresh();\n\t}\n\n\tprivate MessageChannel clientInboundChannel() {\n\t\treturn this.context.getBean(\"clientInboundChannel\", MessageChannel.class);\n\t}\n\n\tprivate Message<String> message(String destination, SimpMessageType type) {\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type);\n\t\treturn message(headers, destination);\n\t}\n\n\tprivate Message<String> message(SimpMessageHeaderAccessor headers, String destination) {\n\t\theaders.setSessionId(\"123\");\n\t\theaders.setSessionAttributes(new HashMap<>());\n\t\tif (destination != null) {\n\t\t\theaders.setDestination(destination);\n\t\t}\n\t\tif (this.messageUser != null) {\n\t\t\theaders.setUser(this.messageUser);\n\t\t}\n\t\treturn new GenericMessage<>(\"hi\", headers.getMessageHeaders());\n\t}\n\n\t@Controller\n\tstatic class MyController {\n\n\t\t@MessageMapping(\"/authentication\")\n\t\tvoid authentication(@AuthenticationPrincipal String un) {\n\t\t\t// ... do something ...\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSocketSecurity\n\tstatic class WebSocketSecurityConfig {\n\n\t\t@Bean\n\t\tAuthorizationManager<Message<?>> authorizationManager(\n\t\t\t\tMessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\t\tmessages.nullDestMatcher()\n\t\t\t\t.authenticated()\n\t\t\t\t// <1>\n\t\t\t\t.simpSubscribeDestMatchers(\"/user/queue/errors\")\n\t\t\t\t.permitAll()\n\t\t\t\t// <2>\n\t\t\t\t.simpDestMatchers(\"/app/**\")\n\t\t\t\t.hasRole(\"USER\")\n\t\t\t\t// <3>\n\t\t\t\t.simpSubscribeDestMatchers(\"/user/**\", \"/topic/friends/*\")\n\t\t\t\t.hasRole(\"USER\") // <4>\n\t\t\t\t.simpTypeMatchers(SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE)\n\t\t\t\t.denyAll() // <5>\n\t\t\t\t.anyMessage()\n\t\t\t\t.denyAll(); // <6>\n\t\t\treturn messages.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSocketMessageBroker\n\tstatic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\tregistry.addEndpoint(\"/chat\").withSockJS();\n\t\t}\n\n\t\t@Override\n\t\tpublic void configureMessageBroker(MessageBrokerRegistry registry) {\n\t\t\tregistry.enableSimpleBroker(\"/queue/\", \"/topic/\");\n\t\t\tregistry.setApplicationDestinationPrefixes(\"/permitAll\", \"/denyAll\");\n\t\t}\n\n\t\t@Bean\n\t\tMyController myController() {\n\t\t\treturn new MyController();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SyncExecutorConfig {\n\n\t\t@Bean\n\t\tstatic SyncExecutorSubscribableChannelPostProcessor postProcessor() {\n\t\t\treturn new SyncExecutorSubscribableChannelPostProcessor();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.socket;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.ObservationTextPublisher;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.http.server.PathContainer;\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.MessageDeliveryException;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.simp.config.MessageBrokerRegistry;\nimport org.springframework.messaging.support.AbstractMessageChannel;\nimport org.springframework.messaging.support.ChannelInterceptor;\nimport org.springframework.messaging.support.GenericMessage;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletConfig;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.observation.SecurityObservationSettings;\nimport org.springframework.security.config.web.messaging.PathPatternMessageMatcherBuilderFactoryBean;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;\nimport org.springframework.security.messaging.access.intercept.MessageAuthorizationContext;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.messaging.context.SecurityContextChannelInterceptor;\nimport org.springframework.security.messaging.web.csrf.XorCsrfChannelInterceptor;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.util.AntPathMatcher;\nimport org.springframework.web.HttpRequestHandler;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.servlet.HandlerMapping;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;\nimport org.springframework.web.socket.config.annotation.StompEndpointRegistry;\nimport org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;\nimport org.springframework.web.socket.server.HandshakeFailureException;\nimport org.springframework.web.socket.server.HandshakeHandler;\nimport org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;\nimport org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler;\nimport org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;\n\npublic class WebSocketMessageBrokerSecurityConfigurationTests {\n\n\tprivate static final String XOR_CSRF_TOKEN_VALUE = \"wpe7zB62-NCpcA==\";\n\n\tAnnotationConfigWebApplicationContext context;\n\n\tAuthentication messageUser;\n\n\tCsrfToken token;\n\n\tString sessionAttr;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.token = new DefaultCsrfToken(\"header\", \"param\", \"token\");\n\t\tthis.sessionAttr = \"sessionAttr\";\n\t\tthis.messageUser = new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\");\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void simpleRegistryMappings() {\n\t\tloadConfig(SockJsSecurityConfig.class);\n\t\tclientInboundChannel().send(message(\"/permitAll\"));\n\t\tassertThatExceptionOfType(MessageDeliveryException.class)\n\t\t\t.isThrownBy(() -> clientInboundChannel().send(message(\"/denyAll\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void annonymousSupported() {\n\t\tloadConfig(SockJsSecurityConfig.class);\n\t\tthis.messageUser = null;\n\t\tclientInboundChannel().send(message(\"/permitAll\"));\n\t}\n\n\t// gh-3797\n\t@Test\n\tpublic void beanResolver() {\n\t\tloadConfig(SockJsSecurityConfig.class);\n\t\tthis.messageUser = null;\n\t\tclientInboundChannel().send(message(\"/beanResolver\"));\n\t}\n\n\t@Test\n\tpublic void addsAuthenticationPrincipalResolver() {\n\t\tloadConfig(SockJsSecurityConfig.class);\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tMessage<String> message = message(\"/permitAll/authentication\");\n\t\tmessageChannel.send(message);\n\t\tassertThat(this.context.getBean(MyController.class).authenticationPrincipal)\n\t\t\t.isEqualTo((String) this.messageUser.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void addsAuthenticationPrincipalResolverWhenNoAuthorization() {\n\t\tloadConfig(NoInboundSecurityConfig.class);\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tMessage<String> message = message(\"/permitAll/authentication\");\n\t\tmessageChannel.send(message);\n\t\tassertThat(this.context.getBean(MyController.class).authenticationPrincipal)\n\t\t\t.isEqualTo((String) this.messageUser.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenMetaAnnotationThenParsesExpression() {\n\t\tloadConfig(NoInboundSecurityConfig.class);\n\t\tthis.messageUser = new TestingAuthenticationToken(\"harold\", \"password\", \"ROLE_USER\");\n\t\tclientInboundChannel().send(message(\"/permitAll/hi\"));\n\t\tassertThat(this.context.getBean(MyController.class).message).isEqualTo(\"Hi, Harold!\");\n\t\tthis.messageUser = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tclientInboundChannel().send(message(\"/permitAll/hi\"));\n\t\tassertThat(this.context.getBean(MyController.class).message).isEqualTo(\"Hi, Stranger!\");\n\t}\n\n\t@Test\n\tpublic void addsCsrfProtectionWhenNoAuthorization() {\n\t\tloadConfig(NoInboundSecurityConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\tMessage<?> message = message(headers, \"/authentication\");\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tassertThatExceptionOfType(MessageDeliveryException.class).isThrownBy(() -> messageChannel.send(message))\n\t\t\t.withCauseInstanceOf(MissingCsrfTokenException.class);\n\t}\n\n\t@Test\n\tpublic void csrfProtectionForConnect() {\n\t\tloadConfig(SockJsSecurityConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\tMessage<?> message = message(headers, \"/authentication\");\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tassertThatExceptionOfType(MessageDeliveryException.class).isThrownBy(() -> messageChannel.send(message))\n\t\t\t.withCauseInstanceOf(MissingCsrfTokenException.class);\n\t}\n\n\t@Test\n\t@Disabled // to be added back in with the introduction of DSL support\n\tpublic void csrfProtectionDisabledForConnect() {\n\t\tloadConfig(CsrfDisabledSockJsSecurityConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\tMessage<?> message = message(headers, \"/permitAll/connect\");\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tmessageChannel.send(message);\n\t}\n\n\t@Test\n\tpublic void csrfProtectionDefinedByBean() {\n\t\tloadConfig(SockJsProxylessSecurityConfig.class);\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tStream<Class<? extends ChannelInterceptor>> interceptors = ((AbstractMessageChannel) messageChannel)\n\t\t\t.getInterceptors()\n\t\t\t.stream()\n\t\t\t.map(ChannelInterceptor::getClass);\n\t\tassertThat(interceptors).contains(XorCsrfChannelInterceptor.class);\n\t}\n\n\t@Test\n\tpublic void messagesConnectUseCsrfTokenHandshakeInterceptor() throws Exception {\n\t\tloadConfig(SockJsSecurityConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\tMessage<?> message = message(headers, \"/authentication\");\n\t\tMockHttpServletRequest request = sockjsHttpRequest(\"/chat\");\n\t\tHttpRequestHandler handler = handler(request);\n\t\thandler.handleRequest(request, new MockHttpServletResponse());\n\t\tassertHandshake(request);\n\t}\n\n\t@Test\n\tpublic void messagesConnectUseCsrfTokenHandshakeInterceptorMultipleMappings() throws Exception {\n\t\tloadConfig(SockJsSecurityConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\tMessage<?> message = message(headers, \"/authentication\");\n\t\tMockHttpServletRequest request = sockjsHttpRequest(\"/other\");\n\t\tHttpRequestHandler handler = handler(request);\n\t\thandler.handleRequest(request, new MockHttpServletResponse());\n\t\tassertHandshake(request);\n\t}\n\n\t@Test\n\tpublic void messagesConnectWebSocketUseCsrfTokenHandshakeInterceptor() throws Exception {\n\t\tloadConfig(WebSocketSecurityConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\tMessage<?> message = message(headers, \"/authentication\");\n\t\tMockHttpServletRequest request = websocketHttpRequest(\"/websocket\");\n\t\tHttpRequestHandler handler = handler(request);\n\t\thandler.handleRequest(request, new MockHttpServletResponse());\n\t\tassertHandshake(request);\n\t}\n\n\t@Test\n\tpublic void messagesContextWebSocketUseSecurityContextHolderStrategy() {\n\t\tloadConfig(WebSocketSecurityConfig.class, SecurityContextChangedListenerConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\theaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tMessage<?> message = message(headers, \"/authenticated\");\n\t\theaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tmessageChannel.send(message);\n\t\tverify(this.context.getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void msmsRegistryCustomPatternMatcher() {\n\t\tloadConfig(MsmsRegistryCustomPatternMatcherConfig.class);\n\t\tclientInboundChannel().send(message(\"/app/a.b\"));\n\t\tassertThatExceptionOfType(MessageDeliveryException.class)\n\t\t\t.isThrownBy(() -> clientInboundChannel().send(message(\"/app/a.b.c\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void overrideMsmsRegistryCustomPatternMatcher() {\n\t\tloadConfig(OverrideMsmsRegistryCustomPatternMatcherConfig.class);\n\t\tclientInboundChannel().send(message(\"/app/a/b\"));\n\t\tassertThatExceptionOfType(MessageDeliveryException.class)\n\t\t\t.isThrownBy(() -> clientInboundChannel().send(message(\"/app/a/b/c\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void defaultPatternMatcher() {\n\t\tloadConfig(DefaultPatternMatcherConfig.class);\n\t\tclientInboundChannel().send(message(\"/app/a/b\"));\n\t\tassertThatExceptionOfType(MessageDeliveryException.class)\n\t\t\t.isThrownBy(() -> clientInboundChannel().send(message(\"/app/a/b/c\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void customExpression() {\n\t\tloadConfig(CustomExpressionConfig.class);\n\t\tclientInboundChannel().send(message(\"/denyRob\"));\n\t\tthis.messageUser = new TestingAuthenticationToken(\"rob\", \"password\", \"ROLE_USER\");\n\t\tassertThatExceptionOfType(MessageDeliveryException.class)\n\t\t\t.isThrownBy(() -> clientInboundChannel().send(message(\"/denyRob\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void channelSecurityInterceptorUsesMetadataSourceBeanWhenProxyingDisabled() {\n\t\tloadConfig(SockJsProxylessSecurityConfig.class);\n\t\tAbstractMessageChannel messageChannel = clientInboundChannel();\n\t\tAuthorizationManager<Message<?>> authorizationManager = this.context.getBean(AuthorizationManager.class);\n\t\tfor (ChannelInterceptor interceptor : messageChannel.getInterceptors()) {\n\t\t\tif (interceptor instanceof AuthorizationChannelInterceptor) {\n\t\t\t\tassertThat(ReflectionTestUtils.getField(interceptor, \"preSendAuthorizationManager\"))\n\t\t\t\t\t.isSameAs(authorizationManager);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tfail(\"did not find AuthorizationChannelInterceptor\");\n\t}\n\n\t@Test\n\tpublic void securityContextChannelInterceptorDefinedByBean() {\n\t\tloadConfig(SockJsProxylessSecurityConfig.class);\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tStream<Class<? extends ChannelInterceptor>> interceptors = ((AbstractMessageChannel) messageChannel)\n\t\t\t.getInterceptors()\n\t\t\t.stream()\n\t\t\t.map(ChannelInterceptor::getClass);\n\t\tassertThat(interceptors).contains(SecurityContextChannelInterceptor.class);\n\t}\n\n\t@Test\n\tpublic void inboundChannelSecurityDefinedByBean() {\n\t\tloadConfig(SockJsProxylessSecurityConfig.class);\n\t\tMessageChannel messageChannel = clientInboundChannel();\n\t\tStream<Class<? extends ChannelInterceptor>> interceptors = ((AbstractMessageChannel) messageChannel)\n\t\t\t.getInterceptors()\n\t\t\t.stream()\n\t\t\t.map(ChannelInterceptor::getClass);\n\t\tassertThat(interceptors).contains(AuthorizationChannelInterceptor.class);\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenFullyAuthenticatedConfiguredAndRememberMeTokenThenAccessDeniedException() {\n\t\tloadConfig(WebSocketSecurityConfig.class);\n\t\tthis.messageUser = new RememberMeAuthenticationToken(\"key\", \"user\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tassertThatExceptionOfType(MessageDeliveryException.class)\n\t\t\t.isThrownBy(() -> clientInboundChannel().send(message(\"/fullyAuthenticated\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenFullyAuthenticatedConfiguredAndUserThenPasses() {\n\t\tloadConfig(WebSocketSecurityConfig.class);\n\t\tclientInboundChannel().send(message(\"/fullyAuthenticated\"));\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenRememberMeConfiguredAndNoUserThenAccessDeniedException() {\n\t\tloadConfig(WebSocketSecurityConfig.class);\n\t\tthis.messageUser = null;\n\t\tassertThatExceptionOfType(MessageDeliveryException.class)\n\t\t\t.isThrownBy(() -> clientInboundChannel().send(message(\"/rememberMe\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenRememberMeConfiguredAndRememberMeTokenThenPasses() {\n\t\tloadConfig(WebSocketSecurityConfig.class);\n\t\tthis.messageUser = new RememberMeAuthenticationToken(\"key\", \"user\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tclientInboundChannel().send(message(\"/rememberMe\"));\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenAnonymousConfiguredAndAnonymousUserThenPasses() {\n\t\tloadConfig(WebSocketSecurityConfig.class);\n\t\tthis.messageUser = new AnonymousAuthenticationToken(\"key\", \"user\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tclientInboundChannel().send(message(\"/anonymous\"));\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenObservationRegistryThenObserves() {\n\t\tloadConfig(WebSocketSecurityConfig.class, ObservationRegistryConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\theaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tMessage<?> message = message(headers, \"/authenticated\");\n\t\theaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\tclientInboundChannel().send(message);\n\t\tObservationHandler<Observation.Context> observationHandler = this.context.getBean(ObservationHandler.class);\n\t\tverify(observationHandler).onStart(any());\n\t\tverify(observationHandler).onStop(any());\n\t\theaders = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\theaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tmessage = message(headers, \"/denyAll\");\n\t\theaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\ttry {\n\t\t\tclientInboundChannel().send(message);\n\t\t}\n\t\tcatch (MessageDeliveryException ex) {\n\t\t\t// okay\n\t\t}\n\t\tverify(observationHandler).onError(any());\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenExcludeAuthorizationObservationsThenUnobserved() {\n\t\tloadConfig(WebSocketSecurityConfig.class, ObservationRegistryConfig.class, SelectableObservationsConfig.class);\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\theaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tMessage<?> message = message(headers, \"/authenticated\");\n\t\theaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\tclientInboundChannel().send(message);\n\t\tObservationHandler<Observation.Context> observationHandler = this.context.getBean(ObservationHandler.class);\n\t\theaders = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\theaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tmessage = message(headers, \"/denyAll\");\n\t\theaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\ttry {\n\t\t\tclientInboundChannel().send(message);\n\t\t}\n\t\tcatch (MessageDeliveryException ex) {\n\t\t\t// okay\n\t\t}\n\t\tverifyNoInteractions(observationHandler);\n\t}\n\n\t// gh-16011\n\t@Test\n\tpublic void enableWebSocketSecurityWhenWebSocketSecurityUsedThenAutowires() {\n\t\tloadConfig(WithWebSecurity.class);\n\t}\n\n\tprivate void assertHandshake(HttpServletRequest request) {\n\t\tTestHandshakeHandler handshakeHandler = this.context.getBean(TestHandshakeHandler.class);\n\t\tassertThatCsrfToken(handshakeHandler.attributes.get(CsrfToken.class.getName())).isEqualTo(this.token);\n\t\tassertThat(handshakeHandler.attributes).containsEntry(this.sessionAttr,\n\t\t\t\trequest.getSession().getAttribute(this.sessionAttr));\n\t}\n\n\tprivate HttpRequestHandler handler(HttpServletRequest request) throws Exception {\n\t\tHandlerMapping handlerMapping = this.context.getBean(HandlerMapping.class);\n\t\treturn (HttpRequestHandler) handlerMapping.getHandler(request).getHandler();\n\t}\n\n\tprivate MockHttpServletRequest websocketHttpRequest(String mapping) {\n\t\tMockHttpServletRequest request = sockjsHttpRequest(mapping);\n\t\trequest.setRequestURI(mapping);\n\t\treturn request;\n\t}\n\n\tprivate MockHttpServletRequest sockjsHttpRequest(String mapping) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setMethod(\"GET\");\n\t\trequest.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, \"/289/tpyx6mde/websocket\");\n\t\trequest.setRequestURI(mapping + \"/289/tpyx6mde/websocket\");\n\t\trequest.getSession().setAttribute(this.sessionAttr, \"sessionValue\");\n\t\trequest.setAttribute(DeferredCsrfToken.class.getName(), new TestDeferredCsrfToken(this.token));\n\t\treturn request;\n\t}\n\n\tprivate Message<String> message(String destination) {\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();\n\t\treturn message(headers, destination);\n\t}\n\n\tprivate Message<String> message(SimpMessageHeaderAccessor headers, String destination) {\n\t\theaders.setSessionId(\"123\");\n\t\theaders.setSessionAttributes(new HashMap<>());\n\t\tif (destination != null) {\n\t\t\theaders.setDestination(destination);\n\t\t}\n\t\tif (this.messageUser != null) {\n\t\t\theaders.setUser(this.messageUser);\n\t\t}\n\t\treturn new GenericMessage<>(\"hi\", headers.getMessageHeaders());\n\t}\n\n\tprivate <T extends MessageChannel> T clientInboundChannel() {\n\t\treturn (T) this.context.getBean(\"clientInboundChannel\", MessageChannel.class);\n\t}\n\n\tprivate void loadConfig(Class<?>... configs) {\n\t\tthis.context = new AnnotationConfigWebApplicationContext();\n\t\tthis.context.setAllowBeanDefinitionOverriding(false);\n\t\tthis.context.register(configs);\n\t\tthis.context.setServletConfig(new MockServletConfig());\n\t\tthis.context.refresh();\n\t}\n\n\t@Configuration\n\t@EnableWebSocketMessageBroker\n\t@EnableWebSocketSecurity\n\t@Import(SyncExecutorConfig.class)\n\tstatic class MsmsRegistryCustomPatternMatcherConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\t@Bean\n\t\tPathPatternMessageMatcherBuilderFactoryBean messageMatcherBuilder() {\n\t\t\tPathPatternParser parser = new PathPatternParser();\n\t\t\tparser.setPathOptions(PathContainer.Options.MESSAGE_ROUTE);\n\t\t\treturn new PathPatternMessageMatcherBuilderFactoryBean(parser);\n\t\t}\n\n\t\t// @formatter:off\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\tregistry\n\t\t\t\t.addEndpoint(\"/other\")\n\t\t\t\t.setHandshakeHandler(testHandshakeHandler());\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Override\n\t\tpublic void configureMessageBroker(MessageBrokerRegistry registry) {\n\t\t\tregistry.enableSimpleBroker(\"/queue/\", \"/topic/\");\n\t\t\tregistry.setApplicationDestinationPrefixes(\"/app\");\n\t\t}\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tAuthorizationManager<Message<?>> authorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\t\tmessages\n\t\t\t\t\t.simpDestMatchers(\"/app/a.*\").permitAll()\n\t\t\t\t\t.anyMessage().denyAll();\n\n\t\t\treturn messages.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\tTestHandshakeHandler testHandshakeHandler() {\n\t\t\treturn new TestHandshakeHandler();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSocketMessageBroker\n\t@EnableWebSocketSecurity\n\t@Import(SyncExecutorConfig.class)\n\tstatic class OverrideMsmsRegistryCustomPatternMatcherConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\t// @formatter:off\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\tregistry\n\t\t\t\t.addEndpoint(\"/other\")\n\t\t\t\t.setHandshakeHandler(testHandshakeHandler());\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Override\n\t\tpublic void configureMessageBroker(MessageBrokerRegistry registry) {\n\t\t\tregistry.setPathMatcher(new AntPathMatcher(\".\"));\n\t\t\tregistry.enableSimpleBroker(\"/queue/\", \"/topic/\");\n\t\t\tregistry.setApplicationDestinationPrefixes(\"/app\");\n\t\t}\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tAuthorizationManager<Message<?>> authorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\t\tmessages\n\t\t\t\t\t.simpDestMatchers(\"/app/a/*\").permitAll()\n\t\t\t\t\t.anyMessage().denyAll();\n\t\t\treturn messages.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\tTestHandshakeHandler testHandshakeHandler() {\n\t\t\treturn new TestHandshakeHandler();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSocketMessageBroker\n\t@EnableWebSocketSecurity\n\t@Import(SyncExecutorConfig.class)\n\tstatic class DefaultPatternMatcherConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\t// @formatter:off\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\tregistry\n\t\t\t\t.addEndpoint(\"/other\")\n\t\t\t\t.setHandshakeHandler(testHandshakeHandler());\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Override\n\t\tpublic void configureMessageBroker(MessageBrokerRegistry registry) {\n\t\t\tregistry.enableSimpleBroker(\"/queue/\", \"/topic/\");\n\t\t\tregistry.setApplicationDestinationPrefixes(\"/app\");\n\t\t}\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tAuthorizationManager<Message<?>> authorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\t\tmessages\n\t\t\t\t\t.simpDestMatchers(\"/app/a/*\").permitAll()\n\t\t\t\t\t.anyMessage().denyAll();\n\n\t\t\treturn messages.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\tTestHandshakeHandler testHandshakeHandler() {\n\t\t\treturn new TestHandshakeHandler();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSocketMessageBroker\n\t@EnableWebSocketSecurity\n\t@Import(SyncExecutorConfig.class)\n\tstatic class CustomExpressionConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\t// @formatter:off\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\tregistry\n\t\t\t\t.addEndpoint(\"/other\")\n\t\t\t\t.setHandshakeHandler(testHandshakeHandler());\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Override\n\t\tpublic void configureMessageBroker(MessageBrokerRegistry registry) {\n\t\t\tregistry.enableSimpleBroker(\"/queue/\", \"/topic/\");\n\t\t\tregistry.setApplicationDestinationPrefixes(\"/app\");\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationManager<Message<Object>> authorizationManager() {\n\t\t\treturn (authentication, message) -> {\n\t\t\t\tAuthentication auth = authentication.get();\n\t\t\t\treturn new AuthorizationDecision(auth != null && !\"rob\".equals(auth.getName()));\n\t\t\t};\n\t\t}\n\n\t\t@Bean\n\t\tTestHandshakeHandler testHandshakeHandler() {\n\t\t\treturn new TestHandshakeHandler();\n\t\t}\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@AuthenticationPrincipal(expression = \"#this.equals('{value}')\")\n\t@interface IsUser {\n\n\t\tString value() default \"user\";\n\n\t}\n\n\t@Controller\n\tstatic class MyController {\n\n\t\tString authenticationPrincipal;\n\n\t\tMyCustomArgument myCustomArgument;\n\n\t\tString message;\n\n\t\t@MessageMapping(\"/authentication\")\n\t\tvoid authentication(@AuthenticationPrincipal String un) {\n\t\t\tthis.authenticationPrincipal = un;\n\t\t}\n\n\t\t@MessageMapping(\"/myCustom\")\n\t\tvoid myCustom(MyCustomArgument myCustomArgument) {\n\t\t\tthis.myCustomArgument = myCustomArgument;\n\t\t}\n\n\t\t@MessageMapping(\"/hi\")\n\t\tvoid sayHello(@IsUser(\"harold\") boolean isHarold) {\n\t\t\tthis.message = isHarold ? \"Hi, Harold!\" : \"Hi, Stranger!\";\n\t\t}\n\n\t}\n\n\tstatic class MyCustomArgument {\n\n\t\tMyCustomArgument(String notDefaultConstr) {\n\t\t}\n\n\t}\n\n\tstatic class MyCustomArgumentResolver implements HandlerMethodArgumentResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\t\treturn parameter.getParameterType().isAssignableFrom(MyCustomArgument.class);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveArgument(MethodParameter parameter, Message<?> message) {\n\t\t\treturn new MyCustomArgument(\"\");\n\t\t}\n\n\t}\n\n\tstatic class TestHandshakeHandler implements HandshakeHandler {\n\n\t\tMap<String, Object> attributes;\n\n\t\t@Override\n\t\tpublic boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,\n\t\t\t\tMap<String, Object> attributes) throws HandshakeFailureException {\n\t\t\tthis.attributes = attributes;\n\t\t\tif (wsHandler instanceof SockJsWebSocketHandler sockJs) {\n\t\t\t\t// work around SPR-12716\n\t\t\t\tWebSocketServerSockJsSession session = (WebSocketServerSockJsSession) ReflectionTestUtils\n\t\t\t\t\t.getField(sockJs, \"sockJsSession\");\n\t\t\t\tthis.attributes = session.getAttributes();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSocketSecurity\n\t@EnableWebSocketMessageBroker\n\t@Import(SyncExecutorConfig.class)\n\tstatic class SockJsSecurityConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\t// @formatter:off\n\t\t\tregistry.addEndpoint(\"/other\").setHandshakeHandler(testHandshakeHandler())\n\t\t\t\t\t.withSockJS().setInterceptors(new HttpSessionHandshakeInterceptor());\n\t\t\tregistry.addEndpoint(\"/chat\").setHandshakeHandler(testHandshakeHandler())\n\t\t\t\t\t.withSockJS().setInterceptors(new HttpSessionHandshakeInterceptor());\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tAuthorizationManager<Message<?>> authorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages,\n\t\t\t\tSecurityCheck security) {\n\t\t\tAuthorizationManager<MessageAuthorizationContext<?>> beanResolver =\n\t\t\t\t\t(authentication, context) -> new AuthorizationDecision(security.check());\n\t\t\tmessages\n\t\t\t\t.simpDestMatchers(\"/permitAll/**\").permitAll()\n\t\t\t\t.simpDestMatchers(\"/beanResolver/**\").access(beanResolver)\n\t\t\t\t.anyMessage().denyAll();\n\t\t\treturn messages.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Override\n\t\tpublic void configureMessageBroker(MessageBrokerRegistry registry) {\n\t\t\tregistry.enableSimpleBroker(\"/queue/\", \"/topic/\");\n\t\t\tregistry.setApplicationDestinationPrefixes(\"/permitAll\", \"/denyAll\");\n\t\t}\n\n\t\t@Bean\n\t\tMyController myController() {\n\t\t\treturn new MyController();\n\t\t}\n\n\t\t@Bean\n\t\tTestHandshakeHandler testHandshakeHandler() {\n\t\t\treturn new TestHandshakeHandler();\n\t\t}\n\n\t\t@Bean\n\t\tSecurityCheck security() {\n\t\t\treturn new SecurityCheck();\n\t\t}\n\n\t\tstatic class SecurityCheck {\n\n\t\t\tprivate boolean check;\n\n\t\t\tboolean check() {\n\t\t\t\tthis.check = !this.check;\n\t\t\t\treturn this.check;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSocketSecurity\n\t@EnableWebSocketMessageBroker\n\t@Import(SyncExecutorConfig.class)\n\tstatic class NoInboundSecurityConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\t// @formatter:off\n\t\t\tregistry.addEndpoint(\"/other\")\n\t\t\t\t\t.withSockJS().setInterceptors(new HttpSessionHandshakeInterceptor());\n\t\t\tregistry.addEndpoint(\"/chat\")\n\t\t\t\t\t.withSockJS().setInterceptors(new HttpSessionHandshakeInterceptor());\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Override\n\t\tpublic void configureMessageBroker(MessageBrokerRegistry registry) {\n\t\t\tregistry.enableSimpleBroker(\"/queue/\", \"/topic/\");\n\t\t\tregistry.setApplicationDestinationPrefixes(\"/permitAll\", \"/denyAll\");\n\t\t}\n\n\t\t@Bean\n\t\tMyController myController() {\n\t\t\treturn new MyController();\n\t\t}\n\n\t\t@Bean\n\t\tAnnotationTemplateExpressionDefaults templateExpressionDefaults() {\n\t\t\treturn new AnnotationTemplateExpressionDefaults();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import(SockJsSecurityConfig.class)\n\tstatic class CsrfDisabledSockJsSecurityConfig {\n\n\t\t@Bean\n\t\tConsumer<List<ChannelInterceptor>> channelInterceptorCustomizer() {\n\t\t\treturn (interceptors) -> interceptors.remove(1);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSocketSecurity\n\t@EnableWebSocketMessageBroker\n\t@Import(SyncExecutorConfig.class)\n\tstatic class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\t// @formatter:off\n\t\t\tregistry.addEndpoint(\"/websocket\")\n\t\t\t\t\t.setHandshakeHandler(testHandshakeHandler())\n\t\t\t\t\t.addInterceptors(new HttpSessionHandshakeInterceptor());\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tAuthorizationManager<Message<?>> authorizationManager(\n\t\t\t\tMessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\t\t// @formatter:off\n\t\t\tmessages\n\t\t\t\t.simpDestMatchers(\"/permitAll/**\").permitAll()\n\t\t\t\t.simpDestMatchers(\"/authenticated/**\").authenticated()\n\t\t\t\t.simpDestMatchers(\"/fullyAuthenticated/**\").fullyAuthenticated()\n\t\t\t\t.simpDestMatchers(\"/rememberMe/**\").rememberMe()\n\t\t\t\t.simpDestMatchers(\"/anonymous/**\").anonymous()\n\t\t\t\t.anyMessage().denyAll();\n\t\t\t// @formatter:on\n\t\t\treturn messages.build();\n\t\t}\n\n\t\t@Bean\n\t\tTestHandshakeHandler testHandshakeHandler() {\n\t\t\treturn new TestHandshakeHandler();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSocketSecurity\n\t@EnableWebSocketMessageBroker\n\t@Import(SyncExecutorConfig.class)\n\tstatic class SockJsProxylessSecurityConfig implements WebSocketMessageBrokerConfigurer {\n\n\t\tprivate ApplicationContext context;\n\n\t\t@Override\n\t\tpublic void registerStompEndpoints(StompEndpointRegistry registry) {\n\t\t\t// @formatter:off\n\t\t\tregistry.addEndpoint(\"/chat\")\n\t\t\t\t\t.setHandshakeHandler(this.context.getBean(TestHandshakeHandler.class))\n\t\t\t\t\t.withSockJS().setInterceptors(new HttpSessionHandshakeInterceptor());\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Autowired\n\t\tvoid setContext(ApplicationContext context) {\n\t\t\tthis.context = context;\n\t\t}\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tAuthorizationManager<Message<?>> authorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\t\tmessages\n\t\t\t\t\t.anyMessage().denyAll();\n\t\t\treturn messages.build();\n\t\t}\n\t\t// @formatter:on\n\n\t\t@Bean\n\t\tTestHandshakeHandler testHandshakeHandler() {\n\t\t\treturn new TestHandshakeHandler();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebSecurity\n\t@Import(WebSocketSecurityConfig.class)\n\tstatic class WithWebSecurity {\n\n\t}\n\n\t@Configuration\n\tstatic class SyncExecutorConfig {\n\n\t\t@Bean\n\t\tstatic SyncExecutorSubscribableChannelPostProcessor postProcessor() {\n\t\t\treturn new SyncExecutorSubscribableChannelPostProcessor();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ObservationRegistryConfig {\n\n\t\tprivate final ObservationRegistry registry = ObservationRegistry.create();\n\n\t\tprivate final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());\n\n\t\t@Bean\n\t\tObservationRegistry observationRegistry() {\n\t\t\treturn this.registry;\n\t\t}\n\n\t\t@Bean\n\t\tObservationHandler<Observation.Context> observationHandler() {\n\t\t\treturn this.handler;\n\t\t}\n\n\t\t@Bean\n\t\tObservationRegistryPostProcessor observationRegistryPostProcessor(\n\t\t\t\tObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\treturn new ObservationRegistryPostProcessor(handler);\n\t\t}\n\n\t}\n\n\tstatic class ObservationRegistryPostProcessor implements BeanPostProcessor {\n\n\t\tprivate final ObjectProvider<ObservationHandler<Observation.Context>> handler;\n\n\t\tObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {\n\t\t\tthis.handler = handler;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\t\tif (bean instanceof ObservationRegistry registry) {\n\t\t\t\tregistry.observationConfig().observationHandler(this.handler.getObject());\n\t\t\t}\n\t\t\treturn bean;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class SelectableObservationsConfig {\n\n\t\t@Bean\n\t\tSecurityObservationSettings observabilityDefaults() {\n\t\t\treturn SecurityObservationSettings.withDefaults().shouldObserveAuthorizations(false).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/aot/hint/OAuth2LoginRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.aot.hint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2LoginRuntimeHints}\n *\n * @author Marcus Da Coregio\n */\nclass OAuth2LoginRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@Test\n\tvoid jwtDecoderHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(JwtDecoder.class)\n\t\t\t.withMemberCategories(MemberCategory.INVOKE_PUBLIC_METHODS)).accepts(this.hints);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.aot.hint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link WebMvcSecurityConfigurationRuntimeHints}\n *\n * @author Marcus da Coregio\n */\nclass WebMvcSecurityConfigurationRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@Test\n\tvoid compositeFilterChainProxyHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy\"))\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/aot/hint/WebSecurityConfigurationRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.aot.hint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link WebSecurityConfigurationRuntimeHints}\n *\n * @author Marcus da Coregio\n */\nclass WebSecurityConfigurationRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@Test\n\tvoid compositeFilterChainProxyHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy\"))\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference.of(\"org.springframework.web.filter.ServletRequestPathFilter\"))\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/authentication/AuthenticationConfigurationGh3935Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;\nimport org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class AuthenticationConfigurationGh3935Tests {\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\t@Autowired\n\tUserDetailsService uds;\n\n\t@Autowired\n\tBootGlobalAuthenticationConfigurationAdapter adapter;\n\n\t// gh-3935\n\t@Test\n\tpublic void loads() {\n\t\tassertThat(this.springSecurityFilterChain).isNotNull();\n\t}\n\n\t@Test\n\tpublic void delegateUsesExisitingAuthentication() {\n\t\tString username = \"user\";\n\t\tString password = \"password\";\n\t\tgiven(this.uds.loadUserByUsername(username)).willReturn(PasswordEncodedUser.user());\n\t\tAuthenticationManager authenticationManager = this.adapter.authenticationManager;\n\t\tassertThat(authenticationManager).isNotNull();\n\t\tAuthentication auth = authenticationManager\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(username, password));\n\t\tverify(this.uds).loadUserByUsername(username);\n\t\tassertThat(auth.getPrincipal()).isEqualTo(PasswordEncodedUser.user());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class WebSecurity {\n\n\t}\n\n\tstatic class BootGlobalAuthenticationConfigurationAdapter extends GlobalAuthenticationConfigurerAdapter {\n\n\t\tprivate final ApplicationContext context;\n\n\t\tprivate AuthenticationManager authenticationManager;\n\n\t\t@Autowired\n\t\tBootGlobalAuthenticationConfigurationAdapter(ApplicationContext context) {\n\t\t\tthis.context = context;\n\t\t}\n\n\t\t@Override\n\t\tpublic void init(AuthenticationManagerBuilder auth) {\n\t\t\tAuthenticationConfiguration configuration = this.context.getBean(AuthenticationConfiguration.class);\n\t\t\tthis.authenticationManager = configuration.getAuthenticationManager();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class AutoConfig {\n\n\t\t@Bean\n\t\tstatic BootGlobalAuthenticationConfigurationAdapter adapter(ApplicationContext context) {\n\t\t\treturn new BootGlobalAuthenticationConfigurationAdapter(context);\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn mock(UserDetailsService.class);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.security.authentication.AuthenticationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.event.AbstractAuthenticationEvent;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.util.InMemoryXmlWebApplicationContext;\nimport org.springframework.security.util.FieldUtils;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Luke Taylor\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class AuthenticationManagerBeanDefinitionParserTests {\n\n\t// @formatter:off\n\tprivate static final String CONTEXT = \"<authentication-manager id='am'>\"\n\t\t\t+ \"    <authentication-provider>\"\n\t\t\t+ \"        <user-service>\"\n\t\t\t+ \"            <user name='bob' password='{noop}bobspassword' authorities='ROLE_A,ROLE_B' />\"\n\t\t\t+ \"        </user-service>\"\n\t\t\t+ \"    </authentication-provider>\"\n\t\t\t+ \"</authentication-manager>\";\n\t// @formatter:on\n\n\t// Issue #7282\n\t// @formatter:off\n\tprivate static final String CONTEXT_MULTI = \"<authentication-manager id='amSecondary'>\"\n\t\t\t+ \"    <authentication-provider>\"\n\t\t\t+ \"        <user-service>\"\n\t\t\t+ \"            <user name='john' password='{noop}doe' authorities='ROLE_C,ROLE_D' />\"\n\t\t\t+ \"        </user-service>\"\n\t\t\t+ \"    </authentication-provider>\"\n\t\t\t+ \"</authentication-manager>\";\n\t// @formatter:on\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\t// SEC-1225\n\tpublic void providersAreRegisteredAsTopLevelBeans() {\n\t\tConfigurableApplicationContext context = this.spring.context(CONTEXT).getContext();\n\t\tassertThat(context.getBeansOfType(AuthenticationProvider.class)).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void eventPublishersAreRegisteredAsTopLevelBeans() {\n\t\tConfigurableApplicationContext context = this.spring.context(CONTEXT).getContext();\n\t\tassertThat(context.getBeansOfType(AuthenticationEventPublisher.class)).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void onlyOneEventPublisherIsRegisteredForMultipleAuthenticationManagers() {\n\t\tConfigurableApplicationContext context = this.spring.context(CONTEXT + '\\n' + CONTEXT_MULTI).getContext();\n\t\tassertThat(context.getBeansOfType(AuthenticationEventPublisher.class)).hasSize(1);\n\t}\n\n\t@Test\n\t// gh-8767\n\tpublic void multipleAuthenticationManagersAndDisableBeanDefinitionOverridingThenNoException() {\n\t\tInMemoryXmlWebApplicationContext xmlContext = new InMemoryXmlWebApplicationContext(\n\t\t\t\tCONTEXT + '\\n' + CONTEXT_MULTI);\n\t\txmlContext.setAllowBeanDefinitionOverriding(false);\n\t\tConfigurableApplicationContext context = this.spring.context(xmlContext).getContext();\n\t\tassertThat(context.getBeansOfType(AuthenticationManager.class)).hasSize(2);\n\t}\n\n\t@Test\n\tpublic void eventsArePublishedByDefault() throws Exception {\n\t\tConfigurableApplicationContext appContext = this.spring.context(CONTEXT).getContext();\n\t\tAuthListener listener = new AuthListener();\n\t\tappContext.addApplicationListener(listener);\n\t\tProviderManager pm = (ProviderManager) appContext.getBeansOfType(ProviderManager.class).values().toArray()[0];\n\t\tObject eventPublisher = FieldUtils.getFieldValue(pm, \"eventPublisher\");\n\t\tassertThat(eventPublisher).isNotNull();\n\t\tassertThat(eventPublisher instanceof DefaultAuthenticationEventPublisher).isTrue();\n\t\tpm.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"bob\", \"bobspassword\"));\n\t\tassertThat(listener.events).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void credentialsAreClearedByDefault() {\n\t\tConfigurableApplicationContext appContext = this.spring.context(CONTEXT).getContext();\n\t\tProviderManager pm = (ProviderManager) appContext.getBeansOfType(ProviderManager.class).values().toArray()[0];\n\t\tassertThat(pm.isEraseCredentialsAfterAuthentication()).isTrue();\n\t}\n\n\t@Test\n\tpublic void clearCredentialsPropertyIsRespected() {\n\t\tConfigurableApplicationContext appContext = this.spring\n\t\t\t.context(\"<authentication-manager erase-credentials='false'/>\")\n\t\t\t.getContext();\n\t\tProviderManager pm = (ProviderManager) appContext.getBeansOfType(ProviderManager.class).values().toArray()[0];\n\t\tassertThat(pm.isEraseCredentialsAfterAuthentication()).isFalse();\n\t}\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\tpublic void passwordEncoderBeanUsed() throws Exception {\n\t\t// @formatter:off\n\t\tthis.spring.context(\"<b:bean id='passwordEncoder' class='org.springframework.security.crypto.password.NoOpPasswordEncoder' factory-method='getInstance'/>\"\n\t\t\t\t+ \"<user-service>\"\n\t\t\t\t+ \"  <user name='user' password='password' authorities='ROLE_A,ROLE_B' />\"\n\t\t\t\t+ \"</user-service>\"\n\t\t\t\t+ \"<http>\"\n\t\t\t\t+ \"  <intercept-url pattern=\\\"/**\\\" access=\\\"authenticated\\\"/>\"\n\t\t\t\t+ \"  <http-basic />\"\n\t\t\t\t+ \"</http>\")\n\t\t\t\t.mockMvcAfterSpringSecurityOk()\n\t\t\t\t.autowire();\n\t\tthis.mockMvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\tprivate static class AuthListener implements ApplicationListener<AbstractAuthenticationEvent> {\n\n\t\tList<AbstractAuthenticationEvent> events = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic void onApplicationEvent(AbstractAuthenticationEvent event) {\n\t\t\tthis.events.add(event);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/authentication/AuthenticationProviderBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.context.support.AbstractXmlApplicationContext;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.crypto.password.LdapShaPasswordEncoder;\nimport org.springframework.security.crypto.password.MessageDigestPasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link AuthenticationProviderBeanDefinitionParser}.\n *\n * @author Luke Taylor\n */\npublic class AuthenticationProviderBeanDefinitionParserTests {\n\n\tprivate AbstractXmlApplicationContext appContext;\n\n\tprivate UsernamePasswordAuthenticationToken bob = UsernamePasswordAuthenticationToken.unauthenticated(\"bob\",\n\t\t\t\"bobspassword\");\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void worksWithEmbeddedUserService() {\n\t\t// @formatter:off\n\t\tsetContext(\" <authentication-provider>\"\n\t\t\t\t+ \"        <user-service>\"\n\t\t\t\t+ \"            <user name='bob' password='{noop}bobspassword' authorities='ROLE_A' />\"\n\t\t\t\t+ \"        </user-service>\"\n\t\t\t\t+ \"    </authentication-provider>\");\n\t\t// @formatter:on\n\t\tgetProvider().authenticate(this.bob);\n\t}\n\n\t@Test\n\tpublic void externalUserServiceRefWorks() {\n\t\t// @formatter:off\n\t\tthis.appContext = new InMemoryXmlApplicationContext(\n\t\t\t\t\"    <authentication-manager>\"\n\t\t\t\t+ \"        <authentication-provider user-service-ref='myUserService' />\"\n\t\t\t\t+ \"    </authentication-manager>\" + \"    <user-service id='myUserService'>\"\n\t\t\t\t+ \"       <user name='bob' password='{noop}bobspassword' authorities='ROLE_A' />\"\n\t\t\t\t+ \"    </user-service>\");\n\t\t// @formatter:on\n\t\tgetProvider().authenticate(this.bob);\n\t}\n\n\t@Test\n\tpublic void providerWithBCryptPasswordEncoderWorks() {\n\t\t// @formatter:off\n\t\tsetContext(\" <authentication-provider>\"\n\t\t\t\t+ \"        <password-encoder hash='bcrypt'/>\"\n\t\t\t\t+ \"        <user-service>\"\n\t\t\t\t+ \"            <user name='bob' password='$2a$05$dRmjl1T05J7rvCPD2NgsHesCEJHww3pdmesUhjM3PD4m/gaEYyx/G' authorities='ROLE_A' />\"\n\t\t\t\t+ \"        </user-service>\"\n\t\t// @formatter:on\n\t\t\t\t+ \"    </authentication-provider>\");\n\t\tgetProvider().authenticate(this.bob);\n\t}\n\n\t@Test\n\tpublic void providerWithMd5PasswordEncoderWorks() {\n\t\t// @formatter:off\n\t\tthis.appContext = new InMemoryXmlApplicationContext(\" <authentication-manager>\"\n\t\t\t\t+ \" <authentication-provider>\"\n\t\t\t\t+ \"        <password-encoder ref='passwordEncoder'/>\"\n\t\t\t\t+ \"        <user-service>\"\n\t\t\t\t+ \"            <user name='bob' password='12b141f35d58b8b3a46eea65e6ac179e' authorities='ROLE_A' />\"\n\t\t\t\t+ \"        </user-service>\"\n\t\t\t\t+ \"    </authentication-provider>\"\n\t\t\t\t+ \" </authentication-manager>\"\n\t\t\t\t+ \" <b:bean id='passwordEncoder'  class='\" + MessageDigestPasswordEncoder.class.getName() + \"'>\"\n\t\t\t\t+ \"     <b:constructor-arg value='MD5'/>\"\n\t\t\t\t+ \" </b:bean>\");\n\t\t// @formatter:on\n\t\tgetProvider().authenticate(this.bob);\n\t}\n\n\t@Test\n\tpublic void providerWithShaPasswordEncoderWorks() {\n\t\t// @formatter:off\n\t\tthis.appContext = new InMemoryXmlApplicationContext(\" <authentication-manager>\"\n\t\t\t\t+ \" <authentication-provider>\"\n\t\t\t\t+ \"        <password-encoder ref='passwordEncoder'/>\"\n\t\t\t\t+ \"        <user-service>\"\n\t\t\t\t+ \"            <user name='bob' password='{SSHA}PpuEwfdj7M1rs0C2W4ssSM2XEN/Y6S5U' authorities='ROLE_A' />\"\n\t\t\t\t+ \"        </user-service>\"\n\t\t\t\t+ \"    </authentication-provider>\"\n\t\t\t\t+ \" </authentication-manager>\"\n\t\t\t\t+ \" <b:bean id='passwordEncoder'  class='\" + LdapShaPasswordEncoder.class.getName() + \"'/>\");\n\t\t// @formatter:on\n\t\tgetProvider().authenticate(this.bob);\n\t}\n\n\t@Test\n\tpublic void passwordIsBase64EncodedWhenBase64IsEnabled() {\n\t\t// @formatter:off\n\t\tthis.appContext = new InMemoryXmlApplicationContext(\" <authentication-manager>\"\n\t\t\t\t+ \" <authentication-provider>\"\n\t\t\t\t+ \"        <password-encoder ref='passwordEncoder'/>\"\n\t\t\t\t+ \"        <user-service>\"\n\t\t\t\t+ \"            <user name='bob' password='ErFB811YuLOkbupl5qwXng==' authorities='ROLE_A' />\"\n\t\t\t\t+ \"        </user-service>\"\n\t\t\t\t+ \"    </authentication-provider>\"\n\t\t\t\t+ \" </authentication-manager>\"\n\t\t\t\t+ \" <b:bean id='passwordEncoder'  class='\" + MessageDigestPasswordEncoder.class.getName() + \"'>\"\n\t\t\t\t+ \"     <b:constructor-arg value='MD5'/>\" + \"     <b:property name='encodeHashAsBase64' value='true'/>\"\n\t\t\t\t+ \" </b:bean>\");\n\t\t// @formatter:on\n\t\tgetProvider().authenticate(this.bob);\n\t}\n\n\t// SEC-1466\n\t@Test\n\tpublic void exernalProviderDoesNotSupportChildElements() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tthis.appContext = new InMemoryXmlApplicationContext(\"    <authentication-manager>\"\n\t\t\t\t\t+ \"      <authentication-provider ref='aProvider'> \"\n\t\t\t\t\t+ \"        <password-encoder ref='customPasswordEncoder'/>\"\n\t\t\t\t\t+ \"      </authentication-provider>\"\n\t\t\t\t\t+ \"    </authentication-manager>\"\n\t\t\t\t\t+ \"    <b:bean id='aProvider' class='org.springframework.security.authentication.TestingAuthenticationProvider'/>\"\n\t\t\t\t\t+ \"    <b:bean id='customPasswordEncoder' \"\n\t\t\t\t\t+ \"        class='org.springframework.security.authentication.encoding.Md5PasswordEncoder'/>\")\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\tprivate AuthenticationProvider getProvider() {\n\t\tList<AuthenticationProvider> providers = ((ProviderManager) this.appContext\n\t\t\t.getBean(BeanIds.AUTHENTICATION_MANAGER)).getProviders();\n\t\treturn providers.get(0);\n\t}\n\n\tprivate void setContext(String context) {\n\t\tthis.appContext = new InMemoryXmlApplicationContext(\n\t\t\t\t\"<authentication-manager>\" + context + \"</authentication-manager>\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/authentication/JdbcUserServiceBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.w3c.dom.Element;\nimport org.xml.sax.SAXParseException;\n\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.CachingUserDetailsService;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.provisioning.JdbcUserDetailsManager;\nimport org.springframework.security.util.FieldUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Ben Alex\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\npublic class JdbcUserServiceBeanDefinitionParserTests {\n\n\tprivate static String USER_CACHE_XML = \"<b:bean id='userCache' class='org.springframework.security.authentication.dao.MockUserCache'/>\";\n\n\t// @formatter:off\n\tprivate static String DATA_SOURCE = \"    <b:bean id='populator' class='org.springframework.security.config.DataSourcePopulator'>\"\n\t\t\t+ \"        <b:property name='dataSource' ref='dataSource'/>\"\n\t\t\t+ \"    </b:bean>\"\n\t\t\t+ \"    <b:bean id='dataSource' class='org.springframework.security.TestDataSource'>\"\n\t\t\t+ \"        <b:constructor-arg value='jdbcnamespaces'/>\"\n\t\t\t+ \"    </b:bean>\";\n\t// @formatter:on\n\n\tprivate InMemoryXmlApplicationContext appContext;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void beanNameIsCorrect() {\n\t\tassertThat(JdbcUserDetailsManager.class.getName())\n\t\t\t.isEqualTo(new JdbcUserServiceBeanDefinitionParser().getBeanClassName(mock(Element.class)));\n\t}\n\n\t@Test\n\tpublic void validUsernameIsFound() {\n\t\tsetContext(\"<jdbc-user-service data-source-ref='dataSource'/>\" + DATA_SOURCE);\n\t\tJdbcUserDetailsManager mgr = (JdbcUserDetailsManager) this.appContext.getBean(BeanIds.USER_DETAILS_SERVICE);\n\t\tassertThat(mgr.loadUserByUsername(\"rod\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void beanIdIsParsedCorrectly() {\n\t\tsetContext(\"<jdbc-user-service id='myUserService' data-source-ref='dataSource'/>\" + DATA_SOURCE);\n\t\tassertThat(this.appContext.getBean(\"myUserService\") instanceof JdbcUserDetailsManager).isTrue();\n\t}\n\n\t@Test\n\tpublic void usernameAndAuthorityQueriesAreParsedCorrectly() throws Exception {\n\t\tString userQuery = \"select username, password, true from users where username = ?\";\n\t\tString authoritiesQuery = \"select username, authority from authorities where username = ? and 1 = 1\";\n\t\t// @formatter:off\n\t\tsetContext(\"<jdbc-user-service id='myUserService' \"\n\t\t\t\t+ \"data-source-ref='dataSource' \"\n\t\t\t\t+ \"users-by-username-query='\" + userQuery + \"' \"\n\t\t\t\t+ \"authorities-by-username-query='\" + authoritiesQuery\n\t\t\t\t+ \"'/>\" + DATA_SOURCE);\n\t\t// @formatter:on\n\t\tJdbcUserDetailsManager mgr = (JdbcUserDetailsManager) this.appContext.getBean(\"myUserService\");\n\t\tassertThat(FieldUtils.getFieldValue(mgr, \"usersByUsernameQuery\")).isEqualTo(userQuery);\n\t\tassertThat(FieldUtils.getFieldValue(mgr, \"authoritiesByUsernameQuery\")).isEqualTo(authoritiesQuery);\n\t\tassertThat(mgr.loadUserByUsername(\"rod\") != null).isTrue();\n\t}\n\n\t@Test\n\tpublic void groupQueryIsParsedCorrectly() throws Exception {\n\t\tsetContext(\"<jdbc-user-service id='myUserService' \" + \"data-source-ref='dataSource' \"\n\t\t\t\t+ \"group-authorities-by-username-query='blah blah'/>\" + DATA_SOURCE);\n\t\tJdbcUserDetailsManager mgr = (JdbcUserDetailsManager) this.appContext.getBean(\"myUserService\");\n\t\tassertThat(FieldUtils.getFieldValue(mgr, \"groupAuthoritiesByUsernameQuery\")).isEqualTo(\"blah blah\");\n\t\tassertThat((Boolean) FieldUtils.getFieldValue(mgr, \"enableGroups\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void cacheRefIsparsedCorrectly() {\n\t\tsetContext(\"<jdbc-user-service id='myUserService' cache-ref='userCache' data-source-ref='dataSource'/>\"\n\t\t\t\t+ DATA_SOURCE + USER_CACHE_XML);\n\t\tCachingUserDetailsService cachingUserService = (CachingUserDetailsService) this.appContext\n\t\t\t.getBean(\"myUserService\" + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX);\n\t\tassertThat(this.appContext.getBean(\"userCache\")).isSameAs(cachingUserService.getUserCache());\n\t\tassertThat(cachingUserService.loadUserByUsername(\"rod\")).isNotNull();\n\t\tassertThat(cachingUserService.loadUserByUsername(\"rod\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void isSupportedByAuthenticationProviderElement() {\n\t\t// @formatter:off\n\t\tsetContext(\"<authentication-manager>\"\n\t\t\t\t+ \"  <authentication-provider>\"\n\t\t\t\t+ \"    <jdbc-user-service data-source-ref='dataSource'/>\"\n\t\t\t\t+ \"  </authentication-provider>\"\n\t\t\t\t+ \"</authentication-manager>\"\n\t\t\t\t+ DATA_SOURCE);\n\t\t// @formatter:on\n\t\tAuthenticationManager mgr = (AuthenticationManager) this.appContext.getBean(BeanIds.AUTHENTICATION_MANAGER);\n\t\tmgr.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\"));\n\t}\n\n\t@Test\n\tpublic void cacheIsInjectedIntoAuthenticationProvider() {\n\t\t// @formatter:off\n\t\tsetContext(\"<authentication-manager>\"\n\t\t\t\t+ \"  <authentication-provider>\"\n\t\t\t\t+ \"    <jdbc-user-service cache-ref='userCache' data-source-ref='dataSource'/>\"\n\t\t\t\t+ \"  </authentication-provider>\"\n\t\t\t\t+ \"</authentication-manager>\"\n\t\t\t\t+ DATA_SOURCE\n\t\t\t\t+ USER_CACHE_XML);\n\t\t// @formatter:on\n\t\tProviderManager mgr = (ProviderManager) this.appContext.getBean(BeanIds.AUTHENTICATION_MANAGER);\n\t\tDaoAuthenticationProvider provider = (DaoAuthenticationProvider) mgr.getProviders().get(0);\n\t\tassertThat(this.appContext.getBean(\"userCache\")).isSameAs(provider.getUserCache());\n\t\tprovider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\"));\n\t\tassertThat(provider.getUserCache().getUserFromCache(\"rod\")).isNotNull()\n\t\t\t.withFailMessage(\"Cache should contain user after authentication\");\n\t}\n\n\t@Test\n\tpublic void rolePrefixIsUsedWhenSet() {\n\t\tsetContext(\"<jdbc-user-service id='myUserService' role-prefix='PREFIX_' data-source-ref='dataSource'/>\"\n\t\t\t\t+ DATA_SOURCE);\n\t\tJdbcUserDetailsManager mgr = (JdbcUserDetailsManager) this.appContext.getBean(\"myUserService\");\n\t\tUserDetails rod = mgr.loadUserByUsername(\"rod\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(rod.getAuthorities())).contains(\"PREFIX_ROLE_SUPERVISOR\");\n\t}\n\n\t@Test\n\tpublic void testEmptyDataSourceRef() {\n\t\t// @formatter:off\n\t\tString xml = \"<authentication-manager>\"\n\t\t\t\t\t+ \"  <authentication-provider>\"\n\t\t\t\t\t+ \"    <jdbc-user-service data-source-ref=''/>\"\n\t\t\t\t\t+ \"  </authentication-provider>\"\n\t\t\t\t\t+ \"</authentication-manager>\";\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t\t.isThrownBy(() -> setContext(xml))\n\t\t\t\t.withFailMessage(\"Expected exception due to empty data-source-ref\")\n\t\t\t\t.withMessageContaining(\"data-source-ref is required for jdbc-user-service\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void testMissingDataSourceRef() {\n\t\t// @formatter:off\n\t\tString xml = \"<authentication-manager>\"\n\t\t\t\t\t+ \"  <authentication-provider>\"\n\t\t\t\t\t+ \"    <jdbc-user-service/>\"\n\t\t\t\t\t+ \"  </authentication-provider>\"\n\t\t\t\t\t+ \"</authentication-manager>\";\n\t\tassertThatExceptionOfType(XmlBeanDefinitionStoreException.class)\n\t\t\t\t.isThrownBy(() -> setContext(xml))\n\t\t\t\t.withFailMessage(\"Expected exception due to missing data-source-ref\")\n\t\t\t\t.havingRootCause()\n\t\t\t\t.isInstanceOf(SAXParseException.class)\n\t\t\t\t.withMessageContaining(\"Attribute 'data-source-ref' must appear on element 'jdbc-user-service'\");\n\t\t// @formatter:on\n\t}\n\n\tprivate void setContext(String context) {\n\t\tthis.appContext = new InMemoryXmlApplicationContext(context);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/authentication/PasswordEncoderParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class PasswordEncoderParserTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\tpublic void passwordEncoderDefaultsToDelegatingPasswordEncoder() throws Exception {\n\t\tthis.spring.configLocations(\n\t\t\t\t\"classpath:org/springframework/security/config/authentication/PasswordEncoderParserTests-default.xml\")\n\t\t\t.mockMvcAfterSpringSecurityOk()\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void passwordEncoderDefaultsToPasswordEncoderBean() throws Exception {\n\t\tthis.spring\n\t\t\t.configLocations(\n\t\t\t\t\t\"classpath:org/springframework/security/config/authentication/PasswordEncoderParserTests-bean.xml\")\n\t\t\t.mockMvcAfterSpringSecurityOk()\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid testCreatePasswordEncoderBeanDefinition() throws Exception {\n\t\tString hash = \"bcrypt\";\n\t\tClass<?> expectedBeanClass = BCryptPasswordEncoder.class;\n\n\t\tBeanDefinition beanDefinition = PasswordEncoderParser.createPasswordEncoderBeanDefinition(hash);\n\n\t\tClass<?> actualBeanClass = Class.forName(beanDefinition.getBeanClassName());\n\t\tassertThat(actualBeanClass).isEqualTo(expectedBeanClass);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/authentication/UserServiceBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.authentication;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.FatalBeanException;\nimport org.springframework.context.support.AbstractXmlApplicationContext;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n */\npublic class UserServiceBeanDefinitionParserTests {\n\n\tprivate AbstractXmlApplicationContext appContext;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void userServiceWithValidPropertiesFileWorksSuccessfully() {\n\t\tsetContext(\"<user-service id='service' \"\n\t\t\t\t+ \"properties='classpath:org/springframework/security/config/users.properties'/>\");\n\t\tUserDetailsService userService = (UserDetailsService) this.appContext.getBean(\"service\");\n\t\tuserService.loadUserByUsername(\"bob\");\n\t\tuserService.loadUserByUsername(\"joe\");\n\t}\n\n\t@Test\n\tpublic void userServiceWithEmbeddedUsersWorksSuccessfully() {\n\t\t// @formatter:off\n\t\tsetContext(\"<user-service id='service'>\"\n\t\t\t\t+ \"    <user name='joe' password='joespassword' authorities='ROLE_A'/>\"\n\t\t\t\t+ \"</user-service>\");\n\t\t// @formatter:on\n\t\tUserDetailsService userService = (UserDetailsService) this.appContext.getBean(\"service\");\n\t\tuserService.loadUserByUsername(\"joe\");\n\t}\n\n\t@Test\n\tpublic void namePasswordAndAuthoritiesSupportPlaceholders() {\n\t\tSystem.setProperty(\"principal.name\", \"joe\");\n\t\tSystem.setProperty(\"principal.pass\", \"joespassword\");\n\t\tSystem.setProperty(\"principal.authorities\", \"ROLE_A,ROLE_B\");\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>\"\n\t\t\t\t+ \"<user-service id='service'>\"\n\t\t\t\t+ \"    <user name='${principal.name}' password='${principal.pass}' authorities='${principal.authorities}'/>\"\n\t\t\t\t+ \"</user-service>\");\n\t\t// @formatter:on\n\t\tUserDetailsService userService = (UserDetailsService) this.appContext.getBean(\"service\");\n\t\tUserDetails joe = userService.loadUserByUsername(\"joe\");\n\t\tassertThat(joe.getPassword()).isEqualTo(\"joespassword\");\n\t\tassertThat(joe.getAuthorities()).hasSize(2);\n\t}\n\n\t@Test\n\tpublic void embeddedUsersWithNoPasswordIsGivenGeneratedValue() {\n\t\t// @formatter:off\n\t\tsetContext(\"<user-service id='service'>\"\n\t\t\t\t+ \"    <user name='joe' authorities='ROLE_A'/>\"\n\t\t\t\t+ \"</user-service>\");\n\t\t// @formatter:on\n\t\tUserDetailsService userService = (UserDetailsService) this.appContext.getBean(\"service\");\n\t\tUserDetails joe = userService.loadUserByUsername(\"joe\");\n\t\tassertThat(joe.getPassword().length() > 0).isTrue();\n\t\tLong.parseLong(joe.getPassword());\n\t}\n\n\t@Test\n\tpublic void disabledAndEmbeddedFlagsAreSupported() {\n\t\t// @formatter:off\n\t\tsetContext(\"<user-service id='service'>\"\n\t\t\t\t+ \"    <user name='joe' password='joespassword' authorities='ROLE_A' locked='true'/>\"\n\t\t\t\t+ \"    <user name='Bob' password='bobspassword' authorities='ROLE_A' disabled='true'/>\"\n\t\t\t\t+ \"</user-service>\");\n\t\t// @formatter:on\n\t\tUserDetailsService userService = (UserDetailsService) this.appContext.getBean(\"service\");\n\t\tUserDetails joe = userService.loadUserByUsername(\"joe\");\n\t\tassertThat(joe.isAccountNonLocked()).isFalse();\n\t\t// Check case-sensitive lookup SEC-1432\n\t\tUserDetails bob = userService.loadUserByUsername(\"Bob\");\n\t\tassertThat(bob.isEnabled()).isFalse();\n\t}\n\n\t@Test\n\tpublic void userWithBothPropertiesAndEmbeddedUsersThrowsException() {\n\t\tassertThatExceptionOfType(FatalBeanException.class).isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tsetContext(\"<user-service id='service' properties='doesntmatter.props'>\"\n\t\t\t\t\t+ \"    <user name='joe' password='joespassword' authorities='ROLE_A'/>\"\n\t\t\t\t\t+ \"</user-service>\")\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void multipleTopLevelUseWithoutIdThrowsException() {\n\t\tassertThatExceptionOfType(FatalBeanException.class).isThrownBy(() -> setContext(\n\t\t\t\t\"<user-service properties='classpath:org/springframework/security/config/users.properties'/>\"\n\t\t\t\t\t\t+ \"<user-service properties='classpath:org/springframework/security/config/users.properties'/>\"));\n\t}\n\n\t@Test\n\tpublic void userServiceWithMissingPropertiesFileThrowsException() {\n\t\tassertThatExceptionOfType(FatalBeanException.class)\n\t\t\t.isThrownBy(() -> setContext(\"<user-service id='service' properties='classpath:doesntexist.properties'/>\"));\n\t}\n\n\tprivate void setContext(String context) {\n\t\tthis.appContext = new InMemoryXmlApplicationContext(context);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/core/GrantedAuthorityDefaultsJcTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class GrantedAuthorityDefaultsJcTests {\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\t@Autowired\n\tMessageService messageService;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tsetup(\"USER\");\n\t\tthis.request = new MockHttpServletRequest(\"GET\", \"\");\n\t\tthis.request.setMethod(\"GET\");\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void doFilter() throws Exception {\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tthis.request.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void doFilterDenied() throws Exception {\n\t\tsetup(\"DENIED\");\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tthis.request.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);\n\t}\n\n\t@Test\n\tpublic void message() {\n\t\tthis.messageService.getMessage();\n\t}\n\n\t@Test\n\tpublic void jsrMessage() {\n\t\tthis.messageService.getJsrMessage();\n\t}\n\n\t@Test\n\tpublic void messageDenied() {\n\t\tsetup(\"DENIED\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.messageService::getMessage);\n\t}\n\n\t@Test\n\tpublic void jsrMessageDenied() {\n\t\tsetup(\"DENIED\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.messageService::getJsrMessage);\n\t}\n\n\t// SEC-2926\n\t@Test\n\tpublic void doFilterIsUserInRole() throws Exception {\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tthis.request.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);\n\t\tthis.chain = new MockFilterChain() {\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response)\n\t\t\t\t\tthrows IOException, ServletException {\n\t\t\t\tHttpServletRequest httpRequest = (HttpServletRequest) request;\n\t\t\t\tassertThat(httpRequest.isUserInRole(\"USER\")).isTrue();\n\t\t\t\tassertThat(httpRequest.isUserInRole(\"INVALID\")).isFalse();\n\t\t\t\tsuper.doFilter(request, response);\n\t\t\t}\n\t\t};\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.chain.getRequest()).isNotNull();\n\t}\n\n\tprivate void setup(String role) {\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\", role);\n\t\tSecurityContextHolder.getContext().setAuthentication(user);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)\n\tstatic class Config {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole(\"USER\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tMessageService messageService() {\n\t\t\treturn new HelloWorldMessageService();\n\t\t}\n\n\t\t@Bean\n\t\tstatic GrantedAuthorityDefaults grantedAuthorityDefaults() {\n\t\t\treturn new GrantedAuthorityDefaults(\"\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/core/GrantedAuthorityDefaultsXmlTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class GrantedAuthorityDefaultsXmlTests {\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\t@Autowired\n\tMessageService messageService;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tsetup(\"USER\");\n\t\tthis.request = new MockHttpServletRequest(\"GET\", \"\");\n\t\tthis.request.setMethod(\"GET\");\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void doFilter() throws Exception {\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tthis.request.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void doFilterDenied() throws Exception {\n\t\tsetup(\"DENIED\");\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tthis.request.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);\n\t}\n\n\t@Test\n\tpublic void message() {\n\t\tthis.messageService.getMessage();\n\t}\n\n\t@Test\n\tpublic void jsrMessage() {\n\t\tthis.messageService.getJsrMessage();\n\t}\n\n\t@Test\n\tpublic void messageDenied() {\n\t\tsetup(\"DENIED\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.messageService::getMessage);\n\t}\n\n\t@Test\n\tpublic void jsrMessageDenied() {\n\t\tsetup(\"DENIED\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.messageService::getJsrMessage);\n\t}\n\n\t// SEC-2926\n\t@Test\n\tpublic void doFilterIsUserInRole() throws Exception {\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tthis.request.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);\n\t\tthis.chain = new MockFilterChain() {\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response)\n\t\t\t\t\tthrows IOException, ServletException {\n\t\t\t\tHttpServletRequest httpRequest = (HttpServletRequest) request;\n\t\t\t\tassertThat(httpRequest.isUserInRole(\"USER\")).isTrue();\n\t\t\t\tassertThat(httpRequest.isUserInRole(\"INVALID\")).isFalse();\n\t\t\t\tsuper.doFilter(request, response);\n\t\t\t}\n\t\t};\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.chain.getRequest()).isNotNull();\n\t}\n\n\tprivate void setup(String role) {\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\", role);\n\t\tSecurityContextHolder.getContext().setAuthentication(user);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/core/HelloWorldMessageService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core;\n\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * @author Rob Winch\n */\npublic class HelloWorldMessageService implements MessageService {\n\n\t@Override\n\t@PreAuthorize(\"hasRole('USER')\")\n\tpublic String getMessage() {\n\t\treturn \"Hello World\";\n\t}\n\n\t@Override\n\t@RolesAllowed(\"USER\")\n\tpublic String getJsrMessage() {\n\t\treturn \"Hello JSR\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/core/MessageService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core;\n\n/**\n * @author Rob Winch\n */\npublic interface MessageService {\n\n\tString getMessage();\n\n\tString getJsrMessage();\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/core/userdetails/ReactiveUserDetailsServiceResourceFactoryBeanPropertiesResourceITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core.userdetails;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.util.InMemoryResource;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringExtension.class)\npublic class ReactiveUserDetailsServiceResourceFactoryBeanPropertiesResourceITests {\n\n\t@Autowired\n\tReactiveUserDetailsService users;\n\n\t@Test\n\tpublic void loadUserByUsernameWhenUserFoundThenNotNull() {\n\t\tassertThat(this.users.findByUsername(\"user\").block()).isNotNull();\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tReactiveUserDetailsServiceResourceFactoryBean userDetailsService() {\n\t\t\treturn ReactiveUserDetailsServiceResourceFactoryBean\n\t\t\t\t.fromResource(new InMemoryResource(\"user=password,ROLE_USER\"));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/core/userdetails/ReactiveUserDetailsServiceResourceFactoryBeanPropertiesResourceLocationITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core.userdetails;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringExtension.class)\npublic class ReactiveUserDetailsServiceResourceFactoryBeanPropertiesResourceLocationITests {\n\n\t@Autowired\n\tReactiveUserDetailsService users;\n\n\t@Test\n\tpublic void loadUserByUsernameWhenUserFoundThenNotNull() {\n\t\tassertThat(this.users.findByUsername(\"user\").block()).isNotNull();\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tReactiveUserDetailsServiceResourceFactoryBean userDetailsService() {\n\t\t\treturn ReactiveUserDetailsServiceResourceFactoryBean.fromResourceLocation(\"classpath:users.properties\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/core/userdetails/ReactiveUserDetailsServiceResourceFactoryBeanStringITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core.userdetails;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringExtension.class)\npublic class ReactiveUserDetailsServiceResourceFactoryBeanStringITests {\n\n\t@Autowired\n\tReactiveUserDetailsService users;\n\n\t@Test\n\tpublic void findByUsernameWhenUserFoundThenNotNull() {\n\t\tassertThat(this.users.findByUsername(\"user\").block()).isNotNull();\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tReactiveUserDetailsServiceResourceFactoryBean userDetailsService() {\n\t\t\treturn ReactiveUserDetailsServiceResourceFactoryBean.fromString(\"user=password,ROLE_USER\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/core/userdetails/UserDetailsResourceFactoryBeanTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.core.userdetails;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.util.InMemoryResource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class UserDetailsResourceFactoryBeanTests {\n\n\t@Mock\n\tResourceLoader resourceLoader;\n\n\tUserDetailsResourceFactoryBean factory = new UserDetailsResourceFactoryBean();\n\n\t@Test\n\tpublic void setResourceLoaderWhenNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.factory.setResourceLoader(null))\n\t\t\t\t.withStackTraceContaining(\"resourceLoader cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getObjectWhenPropertiesResourceLocationNullThenThrowsIllegalStateException() {\n\t\tthis.factory.setResourceLoader(this.resourceLoader);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.factory.getObject())\n\t\t\t\t.withStackTraceContaining(\"resource cannot be null if resourceLocation is null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getObjectWhenPropertiesResourceLocationSingleUserThenThrowsGetsSingleUser() throws Exception {\n\t\tthis.factory.setResourceLocation(\"classpath:users.properties\");\n\t\tCollection<UserDetails> users = this.factory.getObject();\n\t\tassertLoaded();\n\t}\n\n\t@Test\n\tpublic void getObjectWhenPropertiesResourceSingleUserThenThrowsGetsSingleUser() throws Exception {\n\t\tthis.factory.setResource(new InMemoryResource(\"user=password,ROLE_USER\"));\n\t\tassertLoaded();\n\t}\n\n\t@Test\n\tpublic void getObjectWhenInvalidUserThenThrowsMeaningfulException() {\n\t\tthis.factory.setResource(new InMemoryResource(\"user=invalidFormatHere\"));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> this.factory.getObject())\n\t\t\t\t.withStackTraceContaining(\"user\")\n\t\t\t\t.withStackTraceContaining(\"invalidFormatHere\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getObjectWhenStringSingleUserThenGetsSingleUser() throws Exception {\n\t\tthis.factory = UserDetailsResourceFactoryBean.fromString(\"user=password,ROLE_USER\");\n\t\tassertLoaded();\n\t}\n\n\tprivate void assertLoaded() throws Exception {\n\t\tCollection<UserDetails> users = this.factory.getObject();\n\t\t// @formatter:off\n\t\tUserDetails expectedUser = User.withUsername(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.authorities(\"ROLE_USER\")\n\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(users).containsExactly(expectedUser);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/crypto/RsaKeyConversionServicePostProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.crypto;\n\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.beans.factory.config.BeanFactoryPostProcessor;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.convert.ConversionService;\nimport org.springframework.core.convert.support.GenericConversionService;\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link RsaKeyConversionServicePostProcessor}\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class RsaKeyConversionServicePostProcessorTests {\n\n\t// @formatter:off\n\tprivate static final String PKCS8_PRIVATE_KEY = \"-----BEGIN PRIVATE KEY-----\\n\"\n\t\t\t+ \"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMk7CKSTfu3QoV\\n\"\n\t\t\t+ \"HoPVXxwZO+qweztd36cVWYqGOZinrOR2crWFu50AgR2CsdIH0+cqo7F4Vx7/3O8i\\n\"\n\t\t\t+ \"RpYYZPe2VoO5sumzJt8P6fS80/TAKjhJDAqgZKRJTgGN8KxCM6p/aJli1ZeDBqiV\\n\"\n\t\t\t+ \"v7vJJe+ZgJuPGRS+HMNa/wPxEkqqXsglcJcQV1ZEtfKXSHB7jizKpRL38185SyAC\\n\"\n\t\t\t+ \"pwyjvBu6Cmm1URfhQo88mf239ONh4dZ2HoDfzN1q6Ssu4F4hgutxr9B0DVLDP5u+\\n\"\n\t\t\t+ \"WFrm3nsJ76zf99uJ+ntMUHJ+bY+gOjSlVWIVBIZeAaEGKCNWRk/knjvjbijpvm3U\\n\"\n\t\t\t+ \"acGlgdL3AgMBAAECggEACxxxS7zVyu91qI2s5eSKmAQAXMqgup6+2hUluc47nqUv\\n\"\n\t\t\t+ \"uZz/c/6MPkn2Ryo+65d4IgqmMFjSfm68B/2ER5FTcvoLl1Xo2twrrVpUmcg3BClS\\n\"\n\t\t\t+ \"IZPuExdhVNnxjYKEWwcyZrehyAoR261fDdcFxLRW588efIUC+rPTTRHzAc7sT+Ln\\n\"\n\t\t\t+ \"t/uFeYNWJm3LaegOLoOmlMAhJ5puAWSN1F0FxtRf/RVgzbLA9QC975SKHJsfWCSr\\n\"\n\t\t\t+ \"IZyPsdeaqomKaF65l8nfqlE0Ua2L35gIOGKjUwb7uUE8nI362RWMtYdoi3zDDyoY\\n\"\n\t\t\t+ \"hSFbgjylCHDM0u6iSh6KfqOHtkYyJ8tUYgVWl787wQKBgQDYO3wL7xuDdD101Lyl\\n\"\n\t\t\t+ \"AnaDdFB9fxp83FG1cWr+t7LYm9YxGfEUsKHAJXN6TIayDkOOoVwIl+Gz0T3Z06Bm\\n\"\n\t\t\t+ \"eBGLrB9mrVA7+C7NJwu5gTMlzP6HxUR9zKJIQ/VB1NUGM77LSmvOFbHc9Q0+z8EH\\n\"\n\t\t\t+ \"X5WO516a3Z7lNtZJcCoPOtu2rwKBgQCmbj41Fh+SSEUApCEKms5ETRpe7LXQlJgx\\n\"\n\t\t\t+ \"yW7zcJNNuIb1C3vBLPxjiOTMgYKOeMg5rtHTGLT43URHLh9ArjawasjSAr4AM3J4\\n\"\n\t\t\t+ \"xpoi/sKGDdiKOsuDWIGfzdYL8qyTHSdpZLQsCTMRiRYgAHZFPgNa7SLZRfZicGlr\\n\"\n\t\t\t+ \"GHN1rJW6OQKBgEjiM/upyrJSWeypUDSmUeAZMpA6aWkwsfHgmtnkfUn5rQa74cDB\\n\"\n\t\t\t+ \"kKO9e+D7LmOR3z+SL/1NhGwh2SE07dncGr3jdGodfO/ZxZyszozmeaECKcEFwwJM\\n\"\n\t\t\t+ \"GV8WWPKplGwUwPiwywmZ0mvRxXcoe73KgBS88+xrSwWjqDL0tZiQlEJNAoGATkei\\n\"\n\t\t\t+ \"GMQMG3jEg9Wu+NbxV6zQT3+U0MNjhl9RQU1c63x0dcNt9OFc4NAdlZcAulRTENaK\\n\"\n\t\t\t+ \"OHjxffBM0hH+fySx8m53gFfr2BpaqDX5f6ZGBlly1SlsWZ4CchCVsc71nshipi7I\\n\"\n\t\t\t+ \"k8HL9F5/OpQdDNprJ5RMBNfkWE65Nrcsb1e6oPkCgYAxwgdiSOtNg8PjDVDmAhwT\\n\"\n\t\t\t+ \"Mxj0Dtwi2fAqQ76RVrrXpNp3uCOIAu4CfruIb5llcJ3uak0ZbnWri32AxSgk80y3\\n\"\n\t\t\t+ \"EWiRX/WEDu5znejF+5O3pI02atWWcnxifEKGGlxwkcMbQdA67MlrJLFaSnnGpNXo\\n\"\n\t\t\t+ \"yPfcul058SOqhafIZQMEKQ==\\n\"\n\t\t\t+ \"-----END PRIVATE KEY-----\";\n\t// @formatter:on\n\n\tprivate static final String X509_PUBLIC_KEY_LOCATION = \"classpath:org/springframework/security/config/annotation/web/configuration/simple.pub\";\n\n\tprivate final RsaKeyConversionServicePostProcessor postProcessor = new RsaKeyConversionServicePostProcessor();\n\n\tprivate ConversionService service;\n\n\t@Value(\"classpath:org/springframework/security/config/annotation/web/configuration/simple.pub\")\n\tRSAPublicKey publicKey;\n\n\t@Value(\"classpath:org/springframework/security/config/annotation/web/configuration/simple.priv\")\n\tRSAPrivateKey privateKey;\n\n\t@Value(\"custom:simple.pub\")\n\tRSAPublicKey samePublicKey;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tConfigurableListableBeanFactory beanFactory = new DefaultListableBeanFactory();\n\t\tbeanFactory.setConversionService(new GenericConversionService());\n\t\tthis.postProcessor.postProcessBeanFactory(beanFactory);\n\t\tthis.service = beanFactory.getConversionService();\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingConversionServiceForRawKeyThenOk() {\n\t\tRSAPrivateKey key = this.service.convert(PKCS8_PRIVATE_KEY, RSAPrivateKey.class);\n\t\tassertThat(key.getModulus().bitLength()).isEqualTo(2048);\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingConversionServiceForClasspathThenOk() {\n\t\tRSAPublicKey key = this.service.convert(X509_PUBLIC_KEY_LOCATION, RSAPublicKey.class);\n\t\tassertThat(key.getModulus().bitLength()).isEqualTo(1024);\n\t}\n\n\t@Test\n\tpublic void valueWhenReferringToClasspathPublicKeyThenConverts() {\n\t\tthis.spring.register(CustomResourceLoaderConfig.class, DefaultConfig.class).autowire();\n\t\tassertThat(this.publicKey.getModulus().bitLength()).isEqualTo(1024);\n\t}\n\n\t@Test\n\tpublic void valueWhenReferringToClasspathPrivateKeyThenConverts() {\n\t\tthis.spring.register(CustomResourceLoaderConfig.class, DefaultConfig.class).autowire();\n\t\tassertThat(this.privateKey.getModulus().bitLength()).isEqualTo(2048);\n\t}\n\n\t@Test\n\tpublic void valueWhenReferringToCustomResourceLoadedPublicKeyThenConverts() {\n\t\tthis.spring.register(CustomResourceLoaderConfig.class, DefaultConfig.class).autowire();\n\t\tassertThat(this.samePublicKey.getModulus().bitLength()).isEqualTo(1024);\n\t}\n\n\t@Test\n\tpublic void valueWhenOverridingConversionServiceThenUsed() {\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.spring.register(OverrideConversionServiceConfig.class, DefaultConfig.class).autowire())\n\t\t\t.withRootCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class DefaultConfig {\n\n\t}\n\n\t@Configuration\n\tstatic class CustomResourceLoaderConfig {\n\n\t\t@Bean\n\t\tBeanFactoryPostProcessor conversionServiceCustomizer() {\n\t\t\treturn (beanFactory) -> beanFactory.getBean(RsaKeyConversionServicePostProcessor.class)\n\t\t\t\t.setResourceLoader(new CustomResourceLoader());\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class OverrideConversionServiceConfig {\n\n\t\t@Bean\n\t\tConversionService conversionService() {\n\t\t\tGenericConversionService service = new GenericConversionService();\n\t\t\tservice.addConverter(String.class, RSAPublicKey.class, (source) -> {\n\t\t\t\tthrow new IllegalArgumentException(\"unsupported\");\n\t\t\t});\n\t\t\treturn service;\n\t\t}\n\n\t}\n\n\tprivate static class CustomResourceLoader implements ResourceLoader {\n\n\t\tprivate final ResourceLoader delegate = new DefaultResourceLoader();\n\n\t\t@Override\n\t\tpublic Resource getResource(String location) {\n\t\t\tif (location.startsWith(\"classpath:\")) {\n\t\t\t\treturn this.delegate.getResource(location);\n\t\t\t}\n\t\t\telse if (location.startsWith(\"custom:\")) {\n\t\t\t\tString[] parts = location.split(\":\");\n\t\t\t\treturn this.delegate.getResource(\n\t\t\t\t\t\t\"classpath:org/springframework/security/config/annotation/web/configuration/\" + parts[1]);\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"unsupported resource\");\n\t\t}\n\n\t\t@Override\n\t\tpublic ClassLoader getClassLoader() {\n\t\t\treturn this.delegate.getClassLoader();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/debug/AuthProviderDependency.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.debug;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * Fake depenency for {@link TestAuthenticationProvider}\n *\n * @author Rob Winch\n *\n */\n@Component\npublic class AuthProviderDependency {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/debug/SecurityDebugBeanFactoryPostProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.debug;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.debug.DebugFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SecurityDebugBeanFactoryPostProcessorTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void contextRefreshWhenInDebugModeAndDependencyHasAutowiredConstructorThenDebugModeStillWorks() {\n\t\t// SEC-1885\n\t\tthis.spring.configLocations(\n\t\t\t\t\"classpath:org/springframework/security/config/debug/SecurityDebugBeanFactoryPostProcessorTests-context.xml\")\n\t\t\t.autowire();\n\t\tassertThat(this.spring.getContext().getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN))\n\t\t\t.isInstanceOf(DebugFilter.class);\n\t\tassertThat(this.spring.getContext().getBean(BeanIds.FILTER_CHAIN_PROXY)).isInstanceOf(FilterChainProxy.class);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/debug/TestAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.debug;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.stereotype.Service;\n\n/**\n * An {@link AuthenticationProvider} that has an {@link Autowired} constructor which is\n * necessary to recreate SEC-1885.\n *\n * @author Rob Winch\n *\n */\n@Service(\"authProvider\")\npublic class TestAuthenticationProvider implements AuthenticationProvider {\n\n\t@Autowired\n\tpublic TestAuthenticationProvider(AuthProviderDependency authProviderDependency) {\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/doc/Attribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.doc;\n\n/**\n * Represents a Spring Security XSD Attribute. It is created when parsing the current xsd\n * to compare to the documented appendix.\n *\n * @author Rob Winch\n * @author Josh Cummings\n * @see SpringSecurityXsdParser\n * @see XsdDocumentedTests\n */\npublic class Attribute {\n\n\tprivate String name;\n\n\tprivate String desc;\n\n\tprivate Element elmt;\n\n\tpublic Attribute(String desc, String name) {\n\t\tthis.desc = desc;\n\t\tthis.name = name;\n\t}\n\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getDesc() {\n\t\treturn this.desc;\n\t}\n\n\tpublic void setDesc(String desc) {\n\t\tthis.desc = desc;\n\t}\n\n\tpublic Element getElmt() {\n\t\treturn this.elmt;\n\t}\n\n\tpublic void setElmt(Element elmt) {\n\t\tthis.elmt = elmt;\n\t}\n\n\tpublic String getId() {\n\t\treturn String.format(\"%s-%s\", this.elmt.getId(), this.name);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/doc/Element.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.doc;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Represents a Spring Security XSD Element. It is created when parsing the current xsd to\n * compare to the documented appendix.\n *\n * @author Rob Winch\n * @author Josh Cummings\n * @see SpringSecurityXsdParser\n * @see XsdDocumentedTests\n */\npublic class Element {\n\n\tprivate String name;\n\n\tprivate String desc;\n\n\tprivate Collection<Attribute> attrs = new ArrayList<>();\n\n\t/**\n\t * Contains the elements that extend this element (i.e. any-user-service contains\n\t * ldap-user-service)\n\t */\n\tprivate Collection<Element> subGrps = new ArrayList<>();\n\n\tprivate Map<String, Element> childElmts = new HashMap<>();\n\n\tprivate Map<String, Element> parentElmts = new HashMap<>();\n\n\tpublic String getId() {\n\t\treturn String.format(\"nsa-%s\", this.name);\n\t}\n\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getDesc() {\n\t\treturn this.desc;\n\t}\n\n\tpublic void setDesc(String desc) {\n\t\tthis.desc = desc;\n\t}\n\n\tpublic Collection<Attribute> getAttrs() {\n\t\treturn this.attrs;\n\t}\n\n\tpublic void setAttrs(Collection<Attribute> attrs) {\n\t\tthis.attrs = attrs;\n\t}\n\n\tpublic Collection<Element> getSubGrps() {\n\t\treturn this.subGrps;\n\t}\n\n\tpublic void setSubGrps(Collection<Element> subGrps) {\n\t\tthis.subGrps = subGrps;\n\t}\n\n\tpublic Map<String, Element> getChildElmts() {\n\t\treturn this.childElmts;\n\t}\n\n\tpublic void setChildElmts(Map<String, Element> childElmts) {\n\t\tthis.childElmts = childElmts;\n\t}\n\n\tpublic Map<String, Element> getParentElmts() {\n\t\treturn this.parentElmts;\n\t}\n\n\tpublic void setParentElmts(Map<String, Element> parentElmts) {\n\t\tthis.parentElmts = parentElmts;\n\t}\n\n\t/**\n\t * Gets all the ids related to this Element including attributes, parent elements, and\n\t * child elements.\n\t *\n\t * <p>\n\t * The expected ids to be found are documented below.\n\t * <ul>\n\t * <li>Elements - any xml element will have the nsa-&lt;element&gt;. For example the\n\t * http element will have the id nsa-http</li>\n\t * <li>Parent Section - Any element with a parent other than beans will have a section\n\t * named nsa-&lt;element&gt;-parents. For example, authentication-provider would have\n\t * a section id of nsa-authentication-provider-parents. The section would then contain\n\t * a list of links pointing to the documentation for each parent element.</li>\n\t * <li>Attributes Section - Any element with attributes will have a section with the\n\t * id nsa-&lt;element&gt;-attributes. For example the http element would require a\n\t * section with the id http-attributes.</li>\n\t * <li>Attribute - Each attribute of an element would have an id of\n\t * nsa-&lt;element&gt;-&lt;attributeName&gt;. For example the attribute create-session\n\t * for the http attribute would have the id http-create-session.</li>\n\t * <li>Child Section - Any element with a child element will have a section named\n\t * nsa-&lt;element&gt;-children. For example, authentication-provider would have a\n\t * section id of nsa-authentication-provider-children. The section would then contain\n\t * a list of links pointing to the documentation for each child element.</li>\n\t * </ul>\n\t * @return\n\t */\n\tpublic Collection<String> getIds() {\n\t\tCollection<String> ids = new ArrayList<>();\n\t\tids.add(getId());\n\t\tthis.childElmts.values().forEach((elmt) -> ids.add(elmt.getId()));\n\t\tthis.attrs.forEach((attr) -> ids.add(attr.getId()));\n\t\tif (!this.childElmts.isEmpty()) {\n\t\t\tids.add(getId() + \"-children\");\n\t\t}\n\t\tif (!this.attrs.isEmpty()) {\n\t\t\tids.add(getId() + \"-attributes\");\n\t\t}\n\t\tif (!this.parentElmts.isEmpty()) {\n\t\t\tids.add(getId() + \"-parents\");\n\t\t}\n\t\treturn ids;\n\t}\n\n\tpublic Map<String, Element> getAllChildElmts() {\n\t\tMap<String, Element> result = new HashMap<>();\n\t\tthis.childElmts.values()\n\t\t\t.forEach((elmt) -> elmt.subGrps.forEach((subElmt) -> result.put(subElmt.name, subElmt)));\n\t\tresult.putAll(this.childElmts);\n\t\treturn result;\n\t}\n\n\tpublic Map<String, Element> getAllParentElmts() {\n\t\tMap<String, Element> result = new HashMap<>();\n\t\tthis.parentElmts.values()\n\t\t\t.forEach((elmt) -> elmt.subGrps.forEach((subElmt) -> result.put(subElmt.name, subElmt)));\n\t\tresult.putAll(this.parentElmts);\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/doc/SpringSecurityXsdParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.doc;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.springframework.util.StringUtils;\n\n/**\n * Parses the Spring Security Xsd Document\n *\n * @author Rob Winch\n * @author Josh Cummings\n */\npublic class SpringSecurityXsdParser {\n\n\tprivate XmlNode rootElement;\n\n\tprivate Set<String> attrElmts = new LinkedHashSet<>();\n\n\tprivate Map<String, Element> elementNameToElement = new HashMap<>();\n\n\tpublic SpringSecurityXsdParser(XmlNode rootElement) {\n\t\tthis.rootElement = rootElement;\n\t}\n\n\t/**\n\t * Returns a map of the element name to the {@link Element}.\n\t * @return\n\t */\n\tpublic Map<String, Element> parse() {\n\t\telements(this.rootElement);\n\t\treturn this.elementNameToElement;\n\t}\n\n\t/**\n\t * Creates a Map of the name to an Element object of all the children of element.\n\t * @param node\n\t * @return\n\t */\n\tprivate Map<String, Element> elements(XmlNode node) {\n\t\tMap<String, Element> elementNameToElement = new HashMap<>();\n\t\tnode.children().forEach((child) -> {\n\t\t\tif (\"element\".equals(child.simpleName())) {\n\t\t\t\tElement e = elmt(child);\n\t\t\t\telementNameToElement.put(e.getName(), e);\n\t\t\t}\n\t\t\telse {\n\t\t\t\telementNameToElement.putAll(elements(child));\n\t\t\t}\n\t\t});\n\t\treturn elementNameToElement;\n\t}\n\n\t/**\n\t * Any children that are attribute will be returned as an Attribute object.\n\t * @param element\n\t * @return a collection of Attribute objects that are children of element.\n\t */\n\tprivate Collection<Attribute> attrs(XmlNode element) {\n\t\tCollection<Attribute> attrs = new ArrayList<>();\n\t\telement.children().forEach((c) -> {\n\t\t\tString name = c.simpleName();\n\t\t\tif (\"attribute\".equals(name)) {\n\t\t\t\tattrs.add(attr(c));\n\t\t\t}\n\t\t\telse if (!\"element\".equals(name)) {\n\t\t\t\tattrs.addAll(attrs(c));\n\t\t\t}\n\t\t});\n\t\treturn attrs;\n\t}\n\n\t/**\n\t * Any children will be searched for an attributeGroup, each of its children will be\n\t * returned as an Attribute\n\t * @param element\n\t * @return\n\t */\n\tprivate Collection<Attribute> attrgrps(XmlNode element) {\n\t\tCollection<Attribute> attrgrp = new ArrayList<>();\n\t\telement.children().forEach((c) -> {\n\t\t\tif (!\"element\".equals(c.simpleName())) {\n\t\t\t\tif (\"attributeGroup\".equals(c.simpleName())) {\n\t\t\t\t\tif (c.attribute(\"name\") != null) {\n\t\t\t\t\t\tattrgrp.addAll(attrgrp(c));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tString name = c.attribute(\"ref\").split(\":\")[1];\n\t\t\t\t\t\tXmlNode attrGrp = findNode(element, name);\n\t\t\t\t\t\tattrgrp.addAll(attrgrp(attrGrp));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tattrgrp.addAll(attrgrps(c));\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn attrgrp;\n\t}\n\n\tprivate XmlNode findNode(XmlNode c, String name) {\n\t\tXmlNode root = c;\n\t\twhile (!\"schema\".equals(root.simpleName())) {\n\t\t\troot = root.parent().get();\n\t\t}\n\t\t// @formatter:off\n\t\treturn expand(root)\n\t\t\t\t.filter((node) -> name.equals(node.attribute(\"name\")))\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseThrow(IllegalArgumentException::new);\n\t\t// @formatter:on\n\t}\n\n\tprivate Stream<XmlNode> expand(XmlNode root) {\n\t\t// @formatter:off\n\t\treturn Stream.concat(Stream.of(root), root.children()\n\t\t\t\t.flatMap(this::expand));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Processes an individual attributeGroup by obtaining all the attributes and then\n\t * looking for more attributeGroup elements and prcessing them.\n\t * @param e\n\t * @return all the attributes for a specific attributeGroup and any child\n\t * attributeGroups\n\t */\n\tprivate Collection<Attribute> attrgrp(XmlNode e) {\n\t\tCollection<Attribute> attrs = attrs(e);\n\t\tattrs.addAll(attrgrps(e));\n\t\treturn attrs;\n\t}\n\n\t/**\n\t * Obtains the description for a specific element\n\t * @param element\n\t * @return\n\t */\n\tprivate String desc(XmlNode element) {\n\t\treturn element.child(\"annotation\")\n\t\t\t.flatMap((annotation) -> annotation.child(\"documentation\"))\n\t\t\t.map((documentation) -> documentation.text())\n\t\t\t.orElse(null);\n\t}\n\n\t/**\n\t * Given an element creates an attribute from it.\n\t * @param n\n\t * @return\n\t */\n\tprivate Attribute attr(XmlNode n) {\n\t\treturn new Attribute(desc(n), n.attribute(\"name\"));\n\t}\n\n\t/**\n\t * Given an element creates an Element out of it by collecting all its attributes and\n\t * child elements.\n\t * @param n\n\t * @return\n\t */\n\tprivate Element elmt(XmlNode n) {\n\t\tString name = n.attribute(\"ref\");\n\t\tif (!StringUtils.hasLength(name)) {\n\t\t\tname = n.attribute(\"name\");\n\t\t}\n\t\telse {\n\t\t\tname = name.split(\":\")[1];\n\t\t\tn = findNode(n, name);\n\t\t}\n\t\tif (this.elementNameToElement.containsKey(name)) {\n\t\t\treturn this.elementNameToElement.get(name);\n\t\t}\n\t\tthis.attrElmts.add(name);\n\t\tElement e = new Element();\n\t\te.setName(n.attribute(\"name\"));\n\t\te.setDesc(desc(n));\n\t\te.setChildElmts(elements(n));\n\t\te.setAttrs(attrs(n));\n\t\te.getAttrs().addAll(attrgrps(n));\n\t\te.getAttrs().forEach((attr) -> attr.setElmt(e));\n\t\te.getChildElmts().values().forEach((element) -> element.getParentElmts().put(e.getName(), e));\n\t\tString subGrpName = n.attribute(\"substitutionGroup\");\n\t\tif (StringUtils.hasLength(subGrpName)) {\n\t\t\tElement subGrp = elmt(findNode(n, subGrpName.split(\":\")[1]));\n\t\t\tsubGrp.getSubGrps().add(e);\n\t\t}\n\t\tthis.elementNameToElement.put(name, e);\n\t\treturn e;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/doc/XmlNode.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.doc;\n\nimport java.util.Optional;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\n/**\n * @author Josh Cummings\n */\npublic class XmlNode {\n\n\tprivate final Node node;\n\n\tpublic XmlNode(Node node) {\n\t\tthis.node = node;\n\t}\n\n\tpublic String simpleName() {\n\t\tString[] parts = this.node.getNodeName().split(\":\");\n\t\treturn parts[parts.length - 1];\n\t}\n\n\tpublic String text() {\n\t\treturn this.node.getTextContent();\n\t}\n\n\tpublic Stream<XmlNode> children() {\n\t\tNodeList children = this.node.getChildNodes();\n\t\t// @formatter:off\n\t\treturn IntStream.range(0, children.getLength())\n\t\t\t\t.mapToObj(children::item)\n\t\t\t\t.map(XmlNode::new);\n\t\t// @formatter:on\n\t}\n\n\tpublic Optional<XmlNode> child(String name) {\n\t\treturn this.children().filter((child) -> name.equals(child.simpleName())).findFirst();\n\t}\n\n\tpublic Optional<XmlNode> parent() {\n\t\t// @formatter:off\n\t\treturn Optional.ofNullable(this.node.getParentNode()).map(XmlNode::new);\n\t\t// @formatter:on\n\t}\n\n\tpublic String attribute(String name) {\n\t\t// @formatter:off\n\t\treturn Optional.ofNullable(this.node.getAttributes())\n\t\t\t\t.map((attrs) -> attrs.getNamedItem(name))\n\t\t\t\t.map(Node::getTextContent)\n\t\t\t\t.orElse(null);\n\t\t// @formatter:on\n\t}\n\n\tpublic Node node() {\n\t\treturn this.node;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/doc/XmlParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.doc;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.xml.sax.SAXException;\n\n/**\n * @author Josh Cummings\n */\npublic class XmlParser implements AutoCloseable {\n\n\tprivate InputStream xml;\n\n\tpublic XmlParser(InputStream xml) {\n\t\tthis.xml = xml;\n\t}\n\n\tpublic XmlNode parse() {\n\t\ttry {\n\t\t\tDocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();\n\t\t\tDocumentBuilder dBuilder = dbFactory.newDocumentBuilder();\n\t\t\treturn new XmlNode(dBuilder.parse(this.xml));\n\t\t}\n\t\tcatch (IOException | ParserConfigurationException | SAXException ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tthis.xml.close();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/doc/XmlSupport.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.doc;\n\nimport java.io.IOException;\nimport java.util.Map;\n\nimport org.springframework.core.io.ClassPathResource;\n\n/**\n * Support for ensuring preparing the givens in {@link XsdDocumentedTests}\n *\n * @author Josh Cummings\n */\npublic class XmlSupport {\n\n\tprivate XmlParser parser;\n\n\tpublic XmlNode parse(String location) throws IOException {\n\t\tClassPathResource resource = new ClassPathResource(location);\n\t\tthis.parser = new XmlParser(resource.getInputStream());\n\t\treturn this.parser.parse();\n\t}\n\n\tpublic Map<String, Element> elementsByElementName(String location) throws IOException {\n\t\tXmlNode node = parse(location);\n\t\treturn new SpringSecurityXsdParser(node).parse();\n\t}\n\n\tpublic void close() throws IOException {\n\t\tif (this.parser != null) {\n\t\t\tthis.parser.close();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/doc/XsdDocumentedTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.doc;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.security.config.http.SecurityFiltersAssertions;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests to ensure that the xsd is properly documented.\n *\n * @author Rob Winch\n * @author Josh Cummings\n */\npublic class XsdDocumentedTests {\n\n\t// @formatter:off\n\tCollection<String> ignoredIds = Arrays.asList(\"nsa-any-user-service\",\n\t\t\t\"nsa-any-user-service-parents\",\n\t\t\t\"nsa-authentication\",\n\t\t\t\"nsa-websocket-security\",\n\t\t\t\"nsa-ldap\",\n\t\t\t\"nsa-web\",\n\t\t\t// deprecated and for removal\n\t\t\t\"nsa-frame-options-strategy\",\n\t\t\t\"nsa-frame-options-ref\",\n\t\t\t\"nsa-frame-options-value\",\n\t\t\t\"nsa-frame-options-from-parameter\");\n\t// @formatter:on\n\n\tString referenceLocation = \"../docs/modules/ROOT/pages/servlet/appendix/namespace\";\n\n\tString schema31xDocumentLocation = \"org/springframework/security/config/spring-security-3.1.xsd\";\n\n\tString schemaDocumentLocation = \"org/springframework/security/config/spring-security-7.1.xsd\";\n\n\tXmlSupport xml = new XmlSupport();\n\n\t@AfterEach\n\tpublic void close() throws IOException {\n\t\tthis.xml.close();\n\t}\n\n\t@Test\n\tpublic void parseWhenLatestXsdThenAllNamedSecurityFiltersAreDefinedAndOrderedProperly() throws IOException {\n\t\tXmlNode root = this.xml.parse(this.schemaDocumentLocation);\n\t\t// @formatter:off\n\t\tList<String> nodes = root.child(\"schema\")\n\t\t\t\t.map(XmlNode::children)\n\t\t\t\t.orElse(Stream.empty())\n\t\t\t\t.filter((node) -> \"simpleType\".equals(node.simpleName())\n\t\t\t\t\t\t&& \"named-security-filter\".equals(node.attribute(\"name\")))\n\t\t\t\t.flatMap(XmlNode::children)\n\t\t\t\t.flatMap(XmlNode::children)\n\t\t\t\t.map((node) -> node.attribute(\"value\"))\n\t\t\t\t.filter(StringUtils::hasText)\n\t\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t\tSecurityFiltersAssertions.assertEquals(nodes);\n\t}\n\n\t@Test\n\tpublic void parseWhen31XsdThenAllNamedSecurityFiltersAreDefinedAndOrderedProperly() throws IOException {\n\t\t// @formatter:off\n\t\tList<String> expected = Arrays.asList(\"FIRST\",\n\t\t\t\t\"CHANNEL_FILTER\",\n\t\t\t\t\"SECURITY_CONTEXT_FILTER\",\n\t\t\t\t\"CONCURRENT_SESSION_FILTER\",\n\t\t\t\t\"LOGOUT_FILTER\",\n\t\t\t\t\"X509_FILTER\",\n\t\t\t\t\"PRE_AUTH_FILTER\",\n\t\t\t\t\"CAS_FILTER\",\n\t\t\t\t\"FORM_LOGIN_FILTER\",\n\t\t\t\t\"OPENID_FILTER\",\n\t\t\t\t\"LOGIN_PAGE_FILTER\",\n\t\t\t\t\"DIGEST_AUTH_FILTER\",\n\t\t\t\t\"BASIC_AUTH_FILTER\",\n\t\t\t\t\"REQUEST_CACHE_FILTER\",\n\t\t\t\t\"SERVLET_API_SUPPORT_FILTER\",\n\t\t\t\t\"JAAS_API_SUPPORT_FILTER\",\n\t\t\t\t\"REMEMBER_ME_FILTER\",\n\t\t\t\t\"ANONYMOUS_FILTER\",\n\t\t\t\t\"SESSION_MANAGEMENT_FILTER\",\n\t\t\t\t\"EXCEPTION_TRANSLATION_FILTER\",\n\t\t\t\t\"FILTER_SECURITY_INTERCEPTOR\",\n\t\t\t\t\"SWITCH_USER_FILTER\",\n\t\t\t\t\"LAST\");\n\t\t// @formatter:on\n\t\tXmlNode root = this.xml.parse(this.schema31xDocumentLocation);\n\t\t// @formatter:off\n\t\tList<String> nodes = root.child(\"schema\")\n\t\t\t\t.map(XmlNode::children)\n\t\t\t\t.orElse(Stream.empty())\n\t\t\t\t.filter((node) -> \"simpleType\".equals(node.simpleName())\n\t\t\t\t\t\t&& \"named-security-filter\".equals(node.attribute(\"name\")))\n\t\t\t\t.flatMap(XmlNode::children)\n\t\t\t\t.flatMap(XmlNode::children)\n\t\t\t\t.map((node) -> node.attribute(\"value\"))\n\t\t\t\t.filter(StringUtils::hasText)\n\t\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t\tassertThat(nodes).isEqualTo(expected);\n\t}\n\n\t/**\n\t * This will check to ensure that the expected number of xsd documents are found to\n\t * ensure that we are validating against the current xsd document. If this test fails,\n\t * all that is needed is to update the schemaDocument and the expected size for this\n\t * test.\n\t * @return\n\t */\n\t@Test\n\tpublic void sizeWhenReadingFilesystemThenIsCorrectNumberOfSchemaFiles() throws IOException {\n\t\tClassPathResource resource = new ClassPathResource(this.schemaDocumentLocation);\n\t\t// @formatter:off\n\t\tString[] schemas = resource.getFile()\n\t\t\t\t.getParentFile()\n\t\t\t\t.list((dir, name) -> name.endsWith(\".xsd\"));\n\t\t// @formatter:on\n\t\tassertThat(schemas.length)\n\t\t\t.withFailMessage(\"the count is equal to 29, if not then schemaDocument needs updating\")\n\t\t\t.isEqualTo(29);\n\t}\n\n\t/**\n\t * This uses a naming convention for the ids of the appendix to ensure that the entire\n\t * appendix is documented. The naming convention for the ids is documented in\n\t * {@link Element#getIds()}.\n\t * @return\n\t */\n\t@Test\n\tpublic void countReferencesWhenReviewingDocumentationThenEntireSchemaIsIncluded() throws IOException {\n\t\tMap<String, Element> elementsByElementName = this.xml.elementsByElementName(this.schemaDocumentLocation);\n\t\t// @formatter:off\n\t\tList<String> documentIds = namespaceLines()\n\t\t\t\t.filter((line) -> line.matches(\"\\\\[\\\\[(nsa-.*)\\\\]\\\\]\"))\n\t\t\t\t.map((line) -> line.substring(2, line.length() - 2))\n\t\t\t\t.collect(Collectors.toList());\n\t\tSet<String> expectedIds = elementsByElementName.values()\n\t\t\t\t.stream()\n\t\t\t\t.flatMap((element) -> element.getIds().stream())\n\t\t\t\t.collect(Collectors.toSet());\n\t\t// @formatter:on\n\t\tdocumentIds.removeAll(this.ignoredIds);\n\t\texpectedIds.removeAll(this.ignoredIds);\n\t\tassertThat(documentIds).containsAll(expectedIds);\n\t\tassertThat(expectedIds).containsAll(documentIds);\n\t}\n\n\t/**\n\t * This test ensures that any element that has children or parents contains a section\n\t * that has links pointing to that documentation.\n\t * @return\n\t */\n\t@Test\n\tpublic void countLinksWhenReviewingDocumentationThenParentsAndChildrenAreCorrectlyLinked() throws IOException {\n\t\tMap<String, List<String>> docAttrNameToChildren = new TreeMap<>();\n\t\tMap<String, List<String>> docAttrNameToParents = new TreeMap<>();\n\t\tString docAttrName = null;\n\t\tMap<String, List<String>> currentDocAttrNameToElmt = null;\n\t\tList<String> lines = namespaceLines().collect(Collectors.toList());\n\t\tfor (String line : lines) {\n\t\t\tif (line.matches(\"^\\\\[\\\\[.*\\\\]\\\\]$\")) {\n\t\t\t\tString id = line.substring(2, line.length() - 2);\n\t\t\t\tif (id.endsWith(\"-children\")) {\n\t\t\t\t\tdocAttrName = id.substring(0, id.length() - 9);\n\t\t\t\t\tcurrentDocAttrNameToElmt = docAttrNameToChildren;\n\t\t\t\t}\n\t\t\t\telse if (id.endsWith(\"-parents\")) {\n\t\t\t\t\tdocAttrName = id.substring(0, id.length() - 8);\n\t\t\t\t\tcurrentDocAttrNameToElmt = docAttrNameToParents;\n\t\t\t\t}\n\t\t\t\telse if (id.endsWith(\"-attributes\") || docAttrName != null && !id.startsWith(docAttrName)) {\n\t\t\t\t\tcurrentDocAttrNameToElmt = null;\n\t\t\t\t\tdocAttrName = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (docAttrName != null && currentDocAttrNameToElmt != null) {\n\t\t\t\tString expression = \".*<<(nsa-.*),.*>>.*\";\n\t\t\t\tif (line.matches(expression)) {\n\t\t\t\t\tString elmtId = line.replaceAll(expression, \"$1\");\n\t\t\t\t\tcurrentDocAttrNameToElmt.computeIfAbsent(docAttrName, (key) -> new ArrayList<>()).add(elmtId);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\texpression = \".*xref:.*#(nsa-.*)\\\\[.*\\\\]\";\n\t\t\t\t\tif (line.matches(expression)) {\n\t\t\t\t\t\tString elmtId = line.replaceAll(expression, \"$1\");\n\t\t\t\t\t\tcurrentDocAttrNameToElmt.computeIfAbsent(docAttrName, (key) -> new ArrayList<>()).add(elmtId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tMap<String, Element> elementNameToElement = this.xml.elementsByElementName(this.schemaDocumentLocation);\n\t\tMap<String, List<String>> schemaAttrNameToChildren = new TreeMap<>();\n\t\tMap<String, List<String>> schemaAttrNameToParents = new TreeMap<>();\n\t\telementNameToElement.entrySet().stream().forEach((entry) -> {\n\t\t\tString key = \"nsa-\" + entry.getKey();\n\t\t\tif (this.ignoredIds.contains(key)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// @formatter:off\n\t\t\tList<String> parentIds = entry.getValue()\n\t\t\t\t\t.getAllParentElmts()\n\t\t\t\t\t.values()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter((element) -> !this.ignoredIds.contains(element.getId()))\n\t\t\t\t\t.map((element) -> element.getId())\n\t\t\t\t\t.sorted()\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t// @formatter:on\n\t\t\tif (!parentIds.isEmpty()) {\n\t\t\t\tschemaAttrNameToParents.put(key, parentIds);\n\t\t\t}\n\t\t\t// @formatter:off\n\t\t\tList<String> childIds = entry.getValue()\n\t\t\t\t\t.getAllChildElmts()\n\t\t\t\t\t.values()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter((element) -> !this.ignoredIds.contains(element.getId())).map((element) -> element.getId())\n\t\t\t\t\t.sorted()\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t// @formatter:on\n\t\t\tif (!childIds.isEmpty()) {\n\t\t\t\tschemaAttrNameToChildren.put(key, childIds);\n\t\t\t}\n\t\t});\n\t\tassertThat(docAttrNameToChildren)\n\t\t\t.describedAs(toString(docAttrNameToChildren) + \"\\n!=\\n\\n\" + toString(schemaAttrNameToChildren))\n\t\t\t.containsExactlyInAnyOrderEntriesOf(schemaAttrNameToChildren);\n\t\tassertThat(docAttrNameToParents)\n\t\t\t.describedAs(toString(docAttrNameToParents) + \"\\n!=\\n\\n\" + toString(schemaAttrNameToParents))\n\t\t\t.containsExactlyInAnyOrderEntriesOf(schemaAttrNameToParents);\n\t}\n\n\tprivate String toString(Map<?, ?> map) {\n\t\tStringBuffer buffer = new StringBuffer();\n\t\tmap.forEach((k, v) -> {\n\t\t\tbuffer.append(k);\n\t\t\tbuffer.append(\"=\");\n\t\t\tbuffer.append(v);\n\t\t\tbuffer.append(\"\\n\");\n\t\t});\n\t\treturn buffer.toString();\n\t}\n\n\t/**\n\t * This test checks each xsd element and ensures there is documentation for it.\n\t * @return\n\t */\n\t@Test\n\tpublic void countWhenReviewingDocumentationThenAllElementsDocumented() throws IOException {\n\t\tMap<String, Element> elementNameToElement = this.xml.elementsByElementName(this.schemaDocumentLocation);\n\t\t// @formatter:off\n\t\tString notDocElmtIds = elementNameToElement.values()\n\t\t\t\t.stream()\n\t\t\t\t.filter((element) -> StringUtils.isEmpty(element.getDesc())\n\t\t\t\t\t\t&& !this.ignoredIds.contains(element.getId()))\n\t\t\t\t.map((element) -> element.getId())\n\t\t\t\t.sorted()\n\t\t\t\t.collect(Collectors.joining(\"\\n\"));\n\t\tString notDocAttrIds = elementNameToElement.values()\n\t\t\t\t.stream()\n\t\t\t\t.flatMap((element) -> element.getAttrs().stream())\n\t\t\t\t.filter((element) -> StringUtils.isEmpty(element.getDesc())\n\t\t\t\t\t\t&& !this.ignoredIds.contains(element.getId()))\n\t\t\t\t.map((element) -> element.getId())\n\t\t\t\t.sorted()\n\t\t\t\t.collect(Collectors.joining(\"\\n\"));\n\t\t// @formatter:on\n\t\tassertThat(notDocElmtIds).isEmpty();\n\t\tassertThat(notDocAttrIds).isEmpty();\n\t}\n\n\tprivate Stream<String> namespaceLines() {\n\t\treturn Stream.of(new File(this.referenceLocation).listFiles()).map(File::toPath).flatMap(this::fileLines);\n\t}\n\n\tprivate Stream<String> fileLines(Path path) {\n\t\ttry {\n\t\t\treturn Files.lines(path);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/AccessDeniedConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Luke Taylor\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class AccessDeniedConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/AccessDeniedConfigTests\";\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void configureWhenAccessDeniedHandlerIsMissingLeadingSlashThenException() {\n\t\tSpringTestContext context = this.spring.configLocations(this.xml(\"NoLeadingSlash\"));\n\t\t/*\n\t\t * NOTE: Original error message \"errorPage must begin with '/'\" no longer shows up\n\t\t * in stack trace as of Spring Framework 6.x.\n\t\t *\n\t\t * See https://github.com/spring-projects/spring-framework/issues/25162.\n\t\t */\n\t\tassertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> context.autowire())\n\t\t\t.havingRootCause()\n\t\t\t.withMessageContaining(\"Property 'errorPage' threw exception\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void configureWhenAccessDeniedHandlerRefThenAutowire() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AccessDeniedHandler\")).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().is(HttpStatus.GONE.value()));\n\t}\n\n\t@Test\n\tpublic void configureWhenAccessDeniedHandlerUsesPathAndRefThenException() {\n\t\tSpringTestContext context = this.spring.configLocations(this.xml(\"UsesPathAndRef\"));\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(() -> context.autowire())\n\t\t\t.withMessageContaining(\"attribute error-page cannot be used together with the 'ref' attribute\");\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tpublic static class GoneAccessDeniedHandler implements AccessDeniedHandler {\n\n\t\t@Override\n\t\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAccessDeniedException accessDeniedException) {\n\t\t\tresponse.setStatus(HttpStatus.GONE.value());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/CsrfBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\n/**\n * @author Ankur Pathak\n */\npublic class CsrfBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/CsrfBeanDefinitionParserTests\";\n\n\t@Test\n\tpublic void registerDataValueProcessorOnlyIfNotRegistered() {\n\t\ttry (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext()) {\n\t\t\tcontext.setAllowBeanDefinitionOverriding(false);\n\t\t\tcontext.setConfigLocation(this.xml(\"RegisterDataValueProcessorOnyIfNotRegistered\"));\n\t\t\tcontext.refresh();\n\t\t}\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.net.URI;\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.web.servlet.RequestCacheResultMatcher;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.servlet.support.RequestDataValueProcessor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.head;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class CsrfConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/CsrfConfigTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void postWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/csrf\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void putWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(put(\"/csrf\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void patchWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(patch(\"/csrf\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void deleteWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(delete(\"/csrf\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void invalidWhenDefaultConfigurationThenForbiddenSinceCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(request(\"INVALID\", new URI(\"/csrf\")))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultConfigurationThenCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/csrf\"))\n\t\t\t\t.andExpect(csrfInBody());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void headWhenDefaultConfigurationThenCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(head(\"/csrf-in-header\"))\n\t\t\t\t.andExpect(csrfInHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void traceWhenDefaultConfigurationThenCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tMockMvc traceEnabled = MockMvcBuilders.webAppContextSetup(this.spring.getContext())\n\t\t\t\t.apply(springSecurity())\n\t\t\t\t.addDispatcherServletCustomizer((dispatcherServlet) -> dispatcherServlet.setDispatchTraceRequest(true))\n\t\t\t\t.build();\n\t\ttraceEnabled.perform(request(HttpMethod.TRACE, \"/csrf-in-header\"))\n\t\t\t\t.andExpect(csrfInHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void optionsWhenDefaultConfigurationThenCsrfIsEnabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(options(\"/csrf-in-header\"))\n\t\t\t\t.andExpect(csrfInHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenCsrfDisabledThenRequestAllowed() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"CsrfDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/ok\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tassertThat(getFilter(this.spring, CsrfFilter.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void postWhenCsrfElementEnabledThenForbidden() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/csrf\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void putWhenCsrfElementEnabledThenForbidden() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(put(\"/csrf\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void patchWhenCsrfElementEnabledThenForbidden() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(patch(\"/csrf\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void deleteWhenCsrfElementEnabledThenForbidden() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(delete(\"/csrf\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void invalidWhenCsrfElementEnabledThenForbidden() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(request(\"INVALID\", new URI(\"/csrf\")))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(csrfCreated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenCsrfElementEnabledThenOk() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/csrf\"))\n\t\t\t\t.andExpect(csrfInBody());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void headWhenCsrfElementEnabledThenOk() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(head(\"/csrf-in-header\"))\n\t\t\t\t.andExpect(csrfInHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void traceWhenCsrfElementEnabledThenOk() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tMockMvc traceEnabled = MockMvcBuilders.webAppContextSetup(this.spring.getContext())\n\t\t\t\t.apply(springSecurity())\n\t\t\t\t.addDispatcherServletCustomizer((dispatcherServlet) -> dispatcherServlet.setDispatchTraceRequest(true))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\ttraceEnabled.perform(request(HttpMethod.TRACE, \"/csrf-in-header\")).andExpect(csrfInHeader());\n\t}\n\n\t@Test\n\tpublic void optionsWhenCsrfElementEnabledThenOk() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"CsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(options(\"/csrf-in-header\"))\n\t\t\t\t.andExpect(csrfInHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void autowireWhenCsrfElementEnabledThenCreatesCsrfRequestDataValueProcessor() {\n\t\tthis.spring.configLocations(this.xml(\"CsrfEnabled\")).autowire();\n\t\tassertThat(this.spring.getContext().getBean(RequestDataValueProcessor.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void postWhenUsingCsrfAndCustomAccessDeniedHandlerThenTheHandlerIsAppropriatelyEngaged() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithAccessDeniedHandler\"), this.xml(\"shared-access-denied-handler\"))\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/ok\"))\n\t\t\t\t.andExpect(status().isIAmATeapot());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCsrfAndCustomRequestAttributeThenSetUsingCsrfAttrName() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithRequestAttrName\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/ok\")).andReturn();\n\t\tassertThat(result.getRequest().getAttribute(\"csrf-attribute-name\")).isInstanceOf(CsrfToken.class);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenUsingCsrfAndXorCsrfTokenRequestAttributeHandlerThenOk() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithXorCsrfTokenRequestAttributeHandler\"), this.xml(\"shared-controllers\"))\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/ok\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\tMockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession();\n\t\tMockHttpServletRequestBuilder ok = post(\"/ok\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.session(session);\n\t\tthis.mvc.perform(ok).andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenUsingCsrfAndXorCsrfTokenRequestAttributeHandlerWithRawTokenThenForbidden() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithXorCsrfTokenRequestAttributeHandler\"), this.xml(\"shared-controllers\"))\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/csrf\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\tMockHttpServletRequest request = mvcResult.getRequest();\n\t\tMockHttpSession session = (MockHttpSession) request.getSession();\n\t\tCsrfTokenRepository repository = WebTestUtils.getCsrfTokenRepository(request);\n\t\tCsrfToken csrfToken = repository.loadToken(request);\n\t\tMockHttpServletRequestBuilder ok = post(\"/ok\")\n\t\t\t\t.header(csrfToken.getHeaderName(), csrfToken.getToken())\n\t\t\t\t.session(session);\n\t\tthis.mvc.perform(ok).andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenUsingCsrfAndXorCsrfTokenRequestAttributeHandlerThenCsrfAuthenticationStrategyUses()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithXorCsrfTokenRequestAttributeHandler\"), this.xml(\"shared-controllers\"))\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult1 = this.mvc.perform(get(\"/csrf\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tMockHttpServletRequest request1 = mvcResult1.getRequest();\n\t\tMockHttpSession session = (MockHttpSession) request1.getSession();\n\t\tCsrfTokenRepository repository = WebTestUtils.getCsrfTokenRepository(request1);\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder login = post(\"/login\")\n\t\t\t.param(\"username\", \"user\")\n\t\t\t.param(\"password\", \"password\")\n\t\t\t.session(session)\n\t\t\t.with(csrf());\n\t\tthis.mvc.perform(login)\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t\tassertThat(repository.loadToken(request1)).isNull();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult2 = this.mvc.perform(get(\"/csrf\").session(session))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tMockHttpServletRequest request2 = mvcResult2.getRequest();\n\t\tCsrfToken csrfToken = repository.loadToken(request2);\n\t\tCsrfToken csrfTokenAttribute = (CsrfToken) request2.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(csrfTokenAttribute).isNotNull();\n\t\tassertThat(csrfTokenAttribute.getToken()).isNotBlank();\n\t\tassertThat(csrfTokenAttribute.getToken()).isNotEqualTo(csrfToken.getToken());\n\t}\n\n\t@Test\n\tpublic void postWhenHasCsrfTokenButSessionExpiresThenRequestIsCancelledAfterSuccessfulAuthentication()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"CsrfEnabled\")).autowire();\n\t\t// simulates a request that has no authentication (e.g. session time-out)\n\t\tMvcResult result = this.mvc.perform(post(\"/authenticated\").with(csrf()))\n\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t.andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\t// if the request cache is consulted, then it will redirect back to /some-url,\n\t\t// which we don't want\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder login = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session(session)\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(login)\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenHasCsrfTokenButSessionExpiresThenRequestIsRememeberedAfterSuccessfulAuthentication()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"CsrfEnabled\")).autowire();\n\t\t// simulates a request that has no authentication (e.g. session time-out)\n\t\tMvcResult result = this.mvc.perform(get(\"/authenticated\")).andExpect(redirectedUrl(\"/login\")).andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\t// if the request cache is consulted, then it will redirect back to /some-url,\n\t\t// which we do want\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder login = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session(session)\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(login)\n\t\t\t\t.andExpect(RequestCacheResultMatcher.redirectToCachedRequest());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-2422: csrf expire CSRF token and session-management invalid-session-url\n\t */\n\t@Test\n\tpublic void postWhenUsingCsrfAndCustomSessionManagementAndNoSessionThenStillRedirectsToInvalidSessionUrl()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithSessionManagement\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder postToOk = post(\"/ok\")\n\t\t\t\t.param(\"_csrf\", \"abc\");\n\t\tMvcResult result = this.mvc.perform(postToOk)\n\t\t\t\t.andExpect(redirectedUrl(\"/error/sessionError\"))\n\t\t\t\t.andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\tthis.mvc.perform(post(\"/csrf\").session(session))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingCustomRequestMatcherConfiguredThenAppliesAccordingly() throws Exception {\n\t\tSpringTestContext context = this.spring.configLocations(this.xml(\"shared-controllers\"),\n\t\t\t\tthis.xml(\"WithRequestMatcher\"), this.xml(\"mock-request-matcher\"));\n\t\tcontext.autowire();\n\t\tRequestMatcher matcher = context.getContext().getBean(RequestMatcher.class);\n\t\tgiven(matcher.matches(any(HttpServletRequest.class))).willReturn(false);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/ok\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tgiven(matcher.matches(any(HttpServletRequest.class))).willReturn(true);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/ok\"))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenDefaultConfigurationThenSessionNotImmediatelyCreated() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"AutoConfig\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/ok\"))\n\t\t\t\t.andExpect(status().isOk()).andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void postWhenCsrfMismatchesThenForbidden() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"AutoConfig\")).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/ok\")).andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder postOk = post(\"/ok\")\n\t\t\t\t.session(session)\n\t\t\t\t.with(csrf().useInvalidToken());\n\t\tthis.mvc.perform(postOk)\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenDefaultConfigurationThenCsrfCleared() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"AutoConfig\")).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/csrf\")).andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session(session)\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isFound());\n\t\tthis.mvc.perform(get(\"/csrf\").session(session))\n\t\t\t\t.andExpect(csrfChanged(result));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenDefaultConfigurationThenCsrfCleared() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"AutoConfig\")).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/csrf\")).andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/logout\").session(session).with(csrf()))\n\t\t\t\t.andExpect(status().isFound());\n\t\tthis.mvc.perform(get(\"/csrf\").session(session))\n\t\t\t\t.andExpect(csrfChanged(result));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-2495: csrf disables logout on GET\n\t */\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenDefaultConfigurationThenDisabled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"shared-controllers\"), this.xml(\"CsrfEnabled\")).autowire();\n\t\t// renders form to log out but does not perform a redirect\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/logout\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\t// still logged in\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\tprivate <T extends Filter> T getFilter(SpringTestContext context, Class<T> type) {\n\t\tFilterChainProxy chain = context.getContext().getBean(FilterChainProxy.class);\n\t\tList<Filter> filters = chain.getFilters(\"/any\");\n\t\tfor (Filter filter : filters) {\n\t\t\tif (type.isAssignableFrom(filter.getClass())) {\n\t\t\t\treturn (T) filter;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tResultMatcher csrfChanged(MvcResult first) {\n\t\treturn (second) -> {\n\t\t\tassertThat(first).isNotNull();\n\t\t\tassertThat(second).isNotNull();\n\t\t\tassertThat(first.getResponse().getContentAsString())\n\t\t\t\t.isNotEqualTo(second.getResponse().getContentAsString());\n\t\t};\n\t}\n\n\tResultMatcher csrfCreated() {\n\t\treturn new CsrfCreatedResultMatcher();\n\t}\n\n\tResultMatcher csrfInHeader() {\n\t\treturn new CsrfReturnedResultMatcher((result) -> result.getResponse().getHeader(\"X-CSRF-TOKEN\"));\n\t}\n\n\tResultMatcher csrfInBody() {\n\t\treturn new CsrfReturnedResultMatcher((result) -> result.getResponse().getContentAsString());\n\t}\n\n\t@Controller\n\tpublic static class RootController {\n\n\t\t@RequestMapping(value = \"/csrf-in-header\",\n\t\t\t\tmethod = { RequestMethod.HEAD, RequestMethod.TRACE, RequestMethod.OPTIONS })\n\t\t@ResponseBody\n\t\tString csrfInHeaderAndBody(CsrfToken token, HttpServletResponse response) {\n\t\t\tresponse.setHeader(token.getHeaderName(), token.getToken());\n\t\t\treturn csrfInBody(token);\n\t\t}\n\n\t\t@RequestMapping(value = \"/csrf\",\n\t\t\t\tmethod = { RequestMethod.POST, RequestMethod.PUT, RequestMethod.PATCH, RequestMethod.DELETE,\n\t\t\t\t\t\tRequestMethod.GET })\n\t\t@ResponseBody\n\t\tString csrfInBody(CsrfToken token) {\n\t\t\treturn token.getToken();\n\t\t}\n\n\t\t@RequestMapping(value = \"/ok\", method = { RequestMethod.POST, RequestMethod.GET })\n\t\t@ResponseBody\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t\t@GetMapping(\"/authenticated\")\n\t\t@ResponseBody\n\t\tString authenticated() {\n\t\t\treturn \"authenticated\";\n\t\t}\n\n\t}\n\n\tprivate static class TeapotAccessDeniedHandler implements AccessDeniedHandler {\n\n\t\t@Override\n\t\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAccessDeniedException accessDeniedException) {\n\t\t\tresponse.setStatus(HttpStatus.I_AM_A_TEAPOT.value());\n\t\t}\n\n\t}\n\n\t@FunctionalInterface\n\tinterface ExceptionalFunction<IN, OUT> {\n\n\t\tOUT apply(IN in) throws Exception;\n\n\t}\n\n\tstatic class CsrfCreatedResultMatcher implements ResultMatcher {\n\n\t\t@Override\n\t\tpublic void match(MvcResult result) {\n\t\t\tMockHttpServletRequest request = result.getRequest();\n\t\t\tCsrfToken token = WebTestUtils.getCsrfTokenRepository(request).loadToken(request);\n\t\t\tassertThat(token).isNotNull();\n\t\t}\n\n\t}\n\n\tstatic class CsrfReturnedResultMatcher implements ResultMatcher {\n\n\t\tExceptionalFunction<MvcResult, String> token;\n\n\t\tCsrfReturnedResultMatcher(ExceptionalFunction<MvcResult, String> token) {\n\t\t\tthis.token = token;\n\t\t}\n\n\t\t@Override\n\t\tpublic void match(MvcResult result) throws Exception {\n\t\t\tMockHttpServletRequest request = result.getRequest();\n\t\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\t\tassertThat(token).isNotNull();\n\t\t\tassertThat(token.getToken()).isEqualTo(this.token.apply(result));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/DefaultFilterChainValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.UnreachableFilterChainException;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class DefaultFilterChainValidatorTests {\n\n\tprivate DefaultFilterChainValidator validator;\n\n\tprivate FilterChainProxy chain;\n\n\tprivate FilterChainProxy chainAuthorizationFilter;\n\n\t@Mock\n\tprivate Log logger;\n\n\t@Mock\n\tprivate DefaultFilterInvocationSecurityMetadataSource metadataSource;\n\n\t@Mock\n\tprivate AccessDecisionManager accessDecisionManager;\n\n\tprivate FilterSecurityInterceptor authorizationInterceptor;\n\n\t@Mock\n\tprivate AuthorizationManager<HttpServletRequest> authorizationManager;\n\n\tprivate AuthorizationFilter authorizationFilter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tAnonymousAuthenticationFilter aaf = new AnonymousAuthenticationFilter(\"anonymous\");\n\t\tthis.authorizationInterceptor = new FilterSecurityInterceptor();\n\t\tthis.authorizationInterceptor.setAccessDecisionManager(this.accessDecisionManager);\n\t\tthis.authorizationInterceptor.setSecurityMetadataSource(this.metadataSource);\n\t\tthis.authorizationFilter = new AuthorizationFilter(this.authorizationManager);\n\t\tAuthenticationEntryPoint authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(\"/login\");\n\t\tExceptionTranslationFilter etf = new ExceptionTranslationFilter(authenticationEntryPoint);\n\t\tDefaultSecurityFilterChain securityChain = new DefaultSecurityFilterChain(AnyRequestMatcher.INSTANCE, aaf, etf,\n\t\t\t\tthis.authorizationInterceptor);\n\t\tthis.chain = new FilterChainProxy(securityChain);\n\t\tDefaultSecurityFilterChain securityChainAuthorizationFilter = new DefaultSecurityFilterChain(\n\t\t\t\tAnyRequestMatcher.INSTANCE, aaf, etf, this.authorizationFilter);\n\t\tthis.chainAuthorizationFilter = new FilterChainProxy(securityChainAuthorizationFilter);\n\t\tthis.validator = new DefaultFilterChainValidator();\n\t\tReflectionTestUtils.setField(this.validator, \"logger\", this.logger);\n\t}\n\n\t@Test\n\tvoid validateWhenFilterSecurityInterceptorConfiguredThenValidates() {\n\t\tassertThatNoException().isThrownBy(() -> this.validator.validate(this.chain));\n\t}\n\n\t// SEC-1878\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void validateCheckLoginPageIsntProtectedThrowsIllegalArgumentException() {\n\t\tIllegalArgumentException toBeThrown = new IllegalArgumentException(\"failed to eval expression\");\n\t\twillThrow(toBeThrown).given(this.accessDecisionManager)\n\t\t\t.decide(any(Authentication.class), any(), any(Collection.class));\n\t\tthis.validator.validate(this.chain);\n\t\tverify(this.logger).info(\n\t\t\t\t\"Unable to check access to the login page to determine if anonymous access is allowed. This might be an error, but can happen under normal circumstances.\",\n\t\t\t\ttoBeThrown);\n\t}\n\n\t@Test\n\tpublic void validateCheckLoginPageAllowsAnonymous() {\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(false));\n\t\tthis.validator.validate(this.chainAuthorizationFilter);\n\t\tverify(this.logger).warn(\"Anonymous access to the login page doesn't appear to be enabled. \"\n\t\t\t\t+ \"This is almost certainly an error. Please check your configuration allows unauthenticated \"\n\t\t\t\t+ \"access to the configured login page. (Simulated access was rejected)\");\n\t}\n\n\t// SEC-1957\n\t@Test\n\tpublic void validateCustomMetadataSource() {\n\t\tFilterInvocationSecurityMetadataSource customMetaDataSource = mock(\n\t\t\t\tFilterInvocationSecurityMetadataSource.class);\n\t\tthis.authorizationInterceptor.setSecurityMetadataSource(customMetaDataSource);\n\t\tthis.validator.validate(this.chain);\n\t\tverify(customMetaDataSource, atLeastOnce()).getAttributes(any());\n\t}\n\n\t@Test\n\tvoid validateWhenSameRequestMatchersArePresentThenUnreachableFilterChainException() {\n\t\tPathPatternRequestMatcher.Builder builder = PathPatternRequestMatcher.withDefaults();\n\t\tAnonymousAuthenticationFilter authenticationFilter = mock(AnonymousAuthenticationFilter.class);\n\t\tExceptionTranslationFilter exceptionTranslationFilter = mock(ExceptionTranslationFilter.class);\n\t\tSecurityFilterChain chain1 = new DefaultSecurityFilterChain(builder.matcher(\"/api\"), authenticationFilter,\n\t\t\t\texceptionTranslationFilter, this.authorizationInterceptor);\n\t\tSecurityFilterChain chain2 = new DefaultSecurityFilterChain(builder.matcher(\"/api\"), authenticationFilter,\n\t\t\t\texceptionTranslationFilter, this.authorizationInterceptor);\n\t\tList<SecurityFilterChain> chains = new ArrayList<>();\n\t\tchains.add(chain2);\n\t\tchains.add(chain1);\n\t\tFilterChainProxy proxy = new FilterChainProxy(chains);\n\n\t\tassertThatExceptionOfType(UnreachableFilterChainException.class)\n\t\t\t.isThrownBy(() -> this.validator.validate(proxy));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/DeferHttpSessionXmlConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.FilterChainProxy;\n\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class DeferHttpSessionXmlConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/DeferHttpSessionTests\";\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\t@Autowired\n\tprivate Service service;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void explicitDeferHttpSession() throws Exception {\n\t\tthis.spring.configLocations(xml(\"Explicit\")).autowire();\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletRequest mockRequest = spy(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = (httpRequest, httpResponse) -> httpResponse.getWriter().write(this.service.getMessage());\n\n\t\tthis.springSecurityFilterChain.doFilter(mockRequest, response, chain);\n\n\t\tverify(mockRequest, never()).isRequestedSessionIdValid();\n\t\tverify(mockRequest, never()).changeSessionId();\n\t\tverify(mockRequest, never()).getSession(anyBoolean());\n\t\tverify(mockRequest, never()).getSession();\n\t}\n\n\tprivate static String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tpublic static class Service {\n\n\t\t@PreAuthorize(\"permitAll\")\n\t\tpublic String getMessage() {\n\t\t\treturn \"message\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/FilterSecurityMetadataSourceBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.context.support.AbstractXmlApplicationContext;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.ConfigTestUtils;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;\nimport org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link FilterInvocationSecurityMetadataSourceParser}.\n *\n * @author Luke Taylor\n */\npublic class FilterSecurityMetadataSourceBeanDefinitionParserTests {\n\n\tprivate AbstractXmlApplicationContext appContext;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t\tthis.appContext = null;\n\t\t}\n\t}\n\n\tprivate void setContext(String context) {\n\t\tthis.appContext = new InMemoryXmlApplicationContext(context);\n\t}\n\n\t@Test\n\tpublic void parsingMinimalConfigurationIsSuccessful() {\n\t\t// @formatter:off\n\t\tsetContext(\"<filter-security-metadata-source id='fids' use-expressions='false'>\"\n\t\t\t\t+ \"   <intercept-url pattern='/**' access='ROLE_A'/>\"\n\t\t\t\t+ \"</filter-security-metadata-source>\");\n\t\t// @formatter:on\n\t\tDefaultFilterInvocationSecurityMetadataSource fids = (DefaultFilterInvocationSecurityMetadataSource) this.appContext\n\t\t\t.getBean(\"fids\");\n\t\tCollection<ConfigAttribute> cad = fids.getAttributes(createFilterInvocation(\"/anything\", \"GET\"));\n\t\tassertThat(cad).contains(new SecurityConfig(\"ROLE_A\"));\n\t}\n\n\t@Test\n\tpublic void expressionsAreSupported() {\n\t\t// @formatter:off\n\t\tsetContext(\"<filter-security-metadata-source id='fids'>\"\n\t\t\t\t+ \"   <intercept-url pattern='/**' access=\\\"hasRole('ROLE_A')\\\" />\"\n\t\t\t\t+ \"</filter-security-metadata-source>\");\n\t\t// @formatter:on\n\t\tExpressionBasedFilterInvocationSecurityMetadataSource fids = (ExpressionBasedFilterInvocationSecurityMetadataSource) this.appContext\n\t\t\t.getBean(\"fids\");\n\t\tConfigAttribute[] cad = fids.getAttributes(createFilterInvocation(\"/anything\", \"GET\"))\n\t\t\t.toArray(new ConfigAttribute[0]);\n\t\tassertThat(cad).hasSize(1);\n\t\tassertThat(cad[0].toString()).isEqualTo(\"hasRole('ROLE_A')\");\n\t}\n\n\t// SEC-1201\n\t@Test\n\tpublic void interceptUrlsSupportPropertyPlaceholders() {\n\t\tSystem.setProperty(\"secure.url\", \"/secure\");\n\t\tSystem.setProperty(\"secure.role\", \"ROLE_A\");\n\t\tsetContext(\"<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>\"\n\t\t\t\t+ \"<filter-security-metadata-source id='fids' use-expressions='false'>\"\n\t\t\t\t+ \"   <intercept-url pattern='${secure.url}' access='${secure.role}'/>\"\n\t\t\t\t+ \"</filter-security-metadata-source>\");\n\t\tDefaultFilterInvocationSecurityMetadataSource fids = (DefaultFilterInvocationSecurityMetadataSource) this.appContext\n\t\t\t.getBean(\"fids\");\n\t\tCollection<ConfigAttribute> cad = fids.getAttributes(createFilterInvocation(\"/secure\", \"GET\"));\n\t\tassertThat(cad).containsExactly(new SecurityConfig(\"ROLE_A\"));\n\t}\n\n\t@Test\n\tpublic void parsingWithinFilterSecurityInterceptorIsSuccessful() {\n\t\t// @formatter:off\n\t\tsetContext(\"<http auto-config='true' use-expressions='false' use-authorization-manager='false'/>\"\n\t\t\t\t+ \"<b:bean id='fsi' class='org.springframework.security.web.access.intercept.FilterSecurityInterceptor' autowire='byType'>\"\n\t\t\t\t+ \"   <b:property name='securityMetadataSource'>\"\n\t\t\t\t+ \"       <filter-security-metadata-source use-expressions='false'>\"\n\t\t\t\t+ \"           <intercept-url pattern='/secure/extreme/**' access='ROLE_SUPERVISOR'/>\"\n\t\t\t\t+ \"           <intercept-url pattern='/secure/**' access='ROLE_USER'/>\"\n\t\t\t\t+ \"           <intercept-url pattern='/**' access='ROLE_USER'/>\"\n\t\t\t\t+ \"       </filter-security-metadata-source>\"\n\t\t\t\t+ \"   </b:property>\"\n\t\t\t\t+ \"   <b:property name='authenticationManager' ref='\" + BeanIds.AUTHENTICATION_MANAGER + \"'/>\"\n\t\t\t\t+ \"</b:bean>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void parsingInterceptUrlServletPathFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> setContext(\"<filter-security-metadata-source id='fids' use-expressions='false'>\"\n\t\t\t\t\t+ \"   <intercept-url pattern='/secure' access='ROLE_USER' servlet-path='/spring' />\"\n\t\t\t\t\t+ \"</filter-security-metadata-source>\"));\n\t}\n\n\tprivate FilterInvocation createFilterInvocation(String path, String method) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(method, path);\n\t\treturn new FilterInvocation(request, new MockHttpServletResponse(), new MockFilterChain());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.hamcrest.CoreMatchers.not;\nimport static org.hamcrest.CoreMatchers.nullValue;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Luke Taylor\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class FormLoginBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/FormLoginBeanDefinitionParserTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void getLoginWhenAutoConfigThenShowsDefaultLoginPage() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Simple\")).autowire();\n\t\tString expectedContent = \"\"\"\n\t\t\t\t<!DOCTYPE html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t  <head>\n\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t  </head>\n\t\t\t\t  <body>\n\t\t\t\t    <div class=\"content\">\n\t\t\t\t      <form class=\"login-form\" method=\"post\" action=\"/login\">\n\t\t\t\t        <h2>Please sign in</h2>\n\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t\t          <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n\t\t\t\t        </p>\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t\t          <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n\t\t\t\t        </p>\n\n\n\t\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t\t      </form>\n\n\n\n\t\t\t\t    </div>\n\t\t\t\t  </body>\n\t\t\t\t</html>\"\"\";\n\t\tthis.mvc.perform(get(\"/login\")).andExpect(content().string(expectedContent));\n\t}\n\n\t@Test\n\tpublic void getLogoutWhenAutoConfigThenShowsDefaultLogoutPage() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AutoConfig\")).autowire();\n\t\tthis.mvc.perform(get(\"/logout\")).andExpect(content().string(containsString(\"action=\\\"/logout\\\"\")));\n\t}\n\n\t@Test\n\tpublic void getLoginWhenConfiguredWithCustomAttributesThenLoginPageReflects() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomAttributes\")).autowire();\n\n\t\tString expectedContent = \"\"\"\n\t\t\t\t<!DOCTYPE html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t  <head>\n\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t  </head>\n\t\t\t\t  <body>\n\t\t\t\t    <div class=\"content\">\n\t\t\t\t      <form class=\"login-form\" method=\"post\" action=\"/signin\">\n\t\t\t\t        <h2>Please sign in</h2>\n\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t\t          <input type=\"text\" id=\"username\" name=\"custom_user\" placeholder=\"Username\" required autofocus>\n\t\t\t\t        </p>\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t\t          <input type=\"password\" id=\"password\" name=\"custom_pass\" placeholder=\"Password\" required>\n\t\t\t\t        </p>\n\n\n\t\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t\t      </form>\n\n\n\n\t\t\t\t    </div>\n\t\t\t\t  </body>\n\t\t\t\t</html>\"\"\";\n\t\tthis.mvc.perform(get(\"/login\")).andExpect(content().string(expectedContent));\n\t\tthis.mvc.perform(get(\"/logout\")).andExpect(status().is3xxRedirection());\n\t}\n\n\t@Test\n\tpublic void failedLoginWhenConfiguredWithCustomAuthenticationFailureThenForwardsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithAuthenticationFailureForwardUrl\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"bob\")\n\t\t\t\t.param(\"password\", \"invalidpassword\");\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(forwardedUrl(\"/failure_forward_url\"))\n\t\t\t\t.andExpect(request().attribute(WebAttributes.AUTHENTICATION_EXCEPTION, not(nullValue())));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void successfulLoginWhenConfiguredWithCustomAuthenticationSuccessThenForwardsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithAuthenticationSuccessForwardUrl\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(forwardedUrl(\"/success_forward_url\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Luke Taylor\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class FormLoginConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/FormLoginConfigTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void getProtectedPageWhenFormLoginConfiguredThenRedirectsToDefaultLoginPage() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithRequestMatcher\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDefaultTargetUrlConfiguredThenRedirectsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithDefaultTargetUrl\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(redirectedUrl(\"/default\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenConfiguredWithSpelThenRedirectsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"UsingSpel\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(redirectedUrl(WebConfigUtilsTests.URL + \"/default\"));\n\t\tMockHttpServletRequestBuilder invalidPassword = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"wrong\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(invalidPassword)\n\t\t\t\t.andExpect(redirectedUrl(WebConfigUtilsTests.URL + \"/failure\"));\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(redirectedUrl(WebConfigUtilsTests.URL + \"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void autowireWhenLoginPageIsMisconfiguredThenDetects() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"NoLeadingSlashLoginPage\")).autowire());\n\t}\n\n\t@Test\n\tpublic void autowireWhenDefaultTargetUrlIsMisconfiguredThenDetects() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"NoLeadingSlashDefaultTargetUrl\")).autowire());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomHandlerBeansConfiguredThenInvokesAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithSuccessAndFailureHandlers\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isIAmATeapot());\n\t\tMockHttpServletRequestBuilder invalidPassword = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"wrong\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(invalidPassword)\n\t\t\t\t.andExpect(status().isIAmATeapot());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomUsernameAndPasswordParametersThenSucceeds() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithUsernameAndPasswordParameters\")).autowire();\n\t\tthis.mvc.perform(post(\"/login\").param(\"xname\", \"user\").param(\"xpass\", \"password\").with(csrf()))\n\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomSecurityContextHolderStrategy\")).autowire();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tthis.mvc.perform(post(\"/login\").with(csrf())).andExpect(redirectedUrl(\"/login?error\"));\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t}\n\n\t/**\n\t * SEC-2919 - DefaultLoginGeneratingFilter incorrectly used if login-url=\"/login\"\n\t */\n\t@Test\n\tpublic void autowireWhenCustomLoginPageIsSlashLoginThenNoDefaultLoginPageGeneratingFilterIsWired()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"ForSec2919\")).autowire();\n\t\tthis.mvc.perform(get(\"/login\")).andExpect(content().string(\"teapot\"));\n\t\tassertThat(getFilter(this.spring.getContext(), DefaultLoginPageGeneratingFilter.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCsrfIsEnabledThenRequiresToken() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCsrfEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCsrfIsDisabledThenDoesNotRequireToken() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCsrfDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isFound());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-3147: authentication-failure-url should be contained \"error\" parameter if\n\t * login-page=\"/login\"\n\t */\n\t@Test\n\tpublic void authenticateWhenLoginPageIsSlashLoginAndAuthenticationFailsThenRedirectContainsErrorParameter()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"ForSec3147\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"wrong\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate Filter getFilter(ApplicationContext context, Class<? extends Filter> filterClass) {\n\t\tFilterChainProxy filterChain = context.getBean(BeanIds.FILTER_CHAIN_PROXY, FilterChainProxy.class);\n\t\tList<Filter> filters = filterChain.getFilters(\"/any\");\n\t\tfor (Filter filter : filters) {\n\t\t\tif (filter.getClass() == filterClass) {\n\t\t\t\treturn filter;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tpublic static class LoginController {\n\n\t\t@GetMapping(\"/login\")\n\t\tpublic String ok() {\n\t\t\treturn \"teapot\";\n\t\t}\n\n\t}\n\n\tpublic static class TeapotAuthenticationHandler\n\t\t\timplements AuthenticationSuccessHandler, AuthenticationFailureHandler {\n\n\t\t@Override\n\t\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthenticationException exception) {\n\t\t\tresponse.setStatus(HttpStatus.I_AM_A_TEAPOT.value());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthentication authentication) {\n\t\t\tresponse.setStatus(HttpStatus.I_AM_A_TEAPOT.value());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Iterator;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpServletResponseWrapper;\nimport org.apache.http.HttpStatus;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/HttpConfigTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void getWhenUsingMinimalConfigurationThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Minimal\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingMinimalAuthorizationManagerThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MinimalAuthorizationManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingAuthorizationManagerThenRedirectsToLogin() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AuthorizationManager\")).autowire();\n\t\tAuthorizationManager<HttpServletRequest> authorizationManager = this.spring.getContext()\n\t\t\t.getBean(AuthorizationManager.class);\n\t\tgiven(authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(false));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t\tverify(authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void getWhenUsingMinimalConfigurationThenPreventsSessionAsUrlParameter() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Minimal\")).autowire();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tproxy.doFilter(request, new EncodeUrlDenyingHttpServletResponseWrapper(response), (req, resp) -> {\n\t\t});\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/login\");\n\t}\n\n\t@Test\n\tpublic void getWhenUsingObservationRegistryThenObservesRequest() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithObservationRegistry\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(5)).onStart(captor.capture());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"security filterchain before\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authentications\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.authorizations\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"spring.security.http.secured.requests\");\n\t\tassertThat(contexts.next().getContextualName()).isEqualTo(\"security filterchain after\");\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tprivate static class EncodeUrlDenyingHttpServletResponseWrapper extends HttpServletResponseWrapper {\n\n\t\tEncodeUrlDenyingHttpServletResponseWrapper(HttpServletResponse response) {\n\t\t\tsuper(response);\n\t\t}\n\n\t\t@Override\n\t\tpublic String encodeURL(String url) {\n\t\t\tthrow new RuntimeException(\"Unexpected invocation of encodeURL\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String encodeRedirectURL(String url) {\n\t\t\tthrow new RuntimeException(\"Unexpected invocation of encodeURL\");\n\t\t}\n\n\t}\n\n\tpublic static final class MockObservationRegistry implements FactoryBean<ObservationRegistry> {\n\n\t\tprivate ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\n\t\t@Override\n\t\tpublic ObservationRegistry getObject() {\n\t\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\t\tregistry.observationConfig().observationHandler(this.handler);\n\t\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\t\treturn registry;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn ObservationRegistry.class;\n\t\t}\n\n\t\tpublic void setHandler(ObservationHandler<Observation.Context> handler) {\n\t\t\tthis.handler = handler;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/HttpCorsConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.bind.annotation.CrossOrigin;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Tim Ysewyn\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpCorsConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/HttpCorsConfigTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void autowireWhenMissingMvcThenGivesInformativeError() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"RequiresMvc\")).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext\");\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCorsThenDoesSpringSecurityCorsHandshake() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCors\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(this.approved()))\n\t\t\t\t.andExpect(corsResponseHeaders())\n\t\t\t\t.andExpect((status().isIAmATeapot()));\n\t\tthis.mvc.perform(options(\"/\").with(this.preflight()))\n\t\t\t\t.andExpect(corsResponseHeaders())\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomCorsConfigurationSourceThenDoesSpringSecurityCorsHandshake() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCorsConfigurationSource\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(this.approved()))\n\t\t\t\t.andExpect(corsResponseHeaders())\n\t\t\t\t.andExpect((status().isIAmATeapot()));\n\t\tthis.mvc.perform(options(\"/\").with(this.preflight()))\n\t\t\t\t.andExpect(corsResponseHeaders())\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomCorsFilterThenDoesSPringSecurityCorsHandshake() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCorsFilter\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(this.approved()))\n\t\t\t\t.andExpect(corsResponseHeaders())\n\t\t\t\t.andExpect((status().isIAmATeapot()));\n\t\tthis.mvc.perform(options(\"/\").with(this.preflight()))\n\t\t\t\t.andExpect(corsResponseHeaders())\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tprivate RequestPostProcessor preflight() {\n\t\treturn cors(true);\n\t}\n\n\tprivate RequestPostProcessor approved() {\n\t\treturn cors(false);\n\t}\n\n\tprivate RequestPostProcessor cors(boolean preflight) {\n\t\treturn (request) -> {\n\t\t\trequest.addHeader(HttpHeaders.ORIGIN, \"https://example.com\");\n\t\t\tif (preflight) {\n\t\t\t\trequest.setMethod(HttpMethod.OPTIONS.name());\n\t\t\t\trequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name());\n\t\t\t}\n\t\t\treturn request;\n\t\t};\n\t}\n\n\tprivate ResultMatcher corsResponseHeaders() {\n\t\treturn (result) -> {\n\t\t\theader().exists(\"Access-Control-Allow-Origin\").match(result);\n\t\t\theader().exists(\"X-Content-Type-Options\").match(result);\n\t\t};\n\t}\n\n\t@RestController\n\t@CrossOrigin(methods = { RequestMethod.GET, RequestMethod.POST })\n\tstatic class CorsController {\n\n\t\t@RequestMapping(\"/\")\n\t\tString hello() {\n\t\t\treturn \"Hello\";\n\t\t}\n\n\t}\n\n\tstatic class MyCorsConfigurationSource extends UrlBasedCorsConfigurationSource {\n\n\t\tMyCorsConfigurationSource() {\n\t\t\tCorsConfiguration configuration = new CorsConfiguration();\n\t\t\tconfiguration.setAllowedOrigins(Arrays.asList(\"*\"));\n\t\t\tconfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));\n\t\t\tsuper.registerCorsConfiguration(\"/**\", configuration);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.google.common.collect.ImmutableMap;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.session.SessionLimit;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Tim Ysewyn\n * @author Josh Cummings\n * @author Rafiullah Hamedy\n * @author Marcus Da Coregio\n * @author Claudenir Freitas\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpHeadersConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/HttpHeadersConfigTests\";\n\n\t// @formatter:off\n\tstatic final Map<String, String> defaultHeaders = ImmutableMap.<String, String>builder()\n\t\t\t.put(\"X-Content-Type-Options\", \"nosniff\").put(\"X-Frame-Options\", \"DENY\")\n\t\t\t.put(\"Strict-Transport-Security\", \"max-age=31536000 ; includeSubDomains\")\n\t\t\t.put(\"Cache-Control\", \"no-cache, no-store, max-age=0, must-revalidate\")\n\t\t\t.put(\"Expires\", \"0\")\n\t\t\t.put(\"Pragma\", \"no-cache\")\n\t\t\t.put(\"X-XSS-Protection\", \"0\")\n\t\t\t.build();\n\t// @formatter:on\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenHeadersDisabledThenResponseExcludesAllSecureHeaders() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"HeadersDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHeadersDisabledViaPlaceholderThenResponseExcludesAllSecureHeaders() throws Exception {\n\t\tSystem.setProperty(\"security.headers.disabled\", \"true\");\n\t\tthis.spring.configLocations(this.xml(\"DisabledWithPlaceholder\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHeadersEnabledViaPlaceholderThenResponseIncludesAllSecureHeaders() throws Exception {\n\t\tSystem.setProperty(\"security.headers.disabled\", \"false\");\n\t\tthis.spring.configLocations(this.xml(\"DisabledWithPlaceholder\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHeadersDisabledRefMissingPlaceholderThenResponseIncludesAllSecureHeaders() throws Exception {\n\t\tSystem.clearProperty(\"security.headers.disabled\");\n\t\tthis.spring.configLocations(this.xml(\"DisabledWithPlaceholder\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenHeadersDisabledHavingChildElementThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"HeadersDisabledHavingChildElement\")).autowire())\n\t\t\t.withMessageContaining(\"Cannot specify <headers disabled=\\\"true\\\"> with child elements\");\n\t}\n\n\t@Test\n\tpublic void requestWhenHeadersEnabledThenResponseContainsAllSecureHeaders() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultConfig\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHeadersElementUsedThenResponseContainsAllSecureHeaders() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"HeadersEnabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenFrameOptionsConfiguredThenIncludesHeader() throws Exception {\n\t\tMap<String, String> headers = new HashMap<>(defaultHeaders);\n\t\theaders.put(\"X-Frame-Options\", \"SAMEORIGIN\");\n\t\tthis.spring.configLocations(this.xml(\"WithFrameOptions\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(headers));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * gh-3986\n\t */\n\t@Test\n\tpublic void requestWhenDefaultsDisabledWithNoOverrideThenExcludesAllSecureHeaders() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithNoOverride\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDefaultsDisabledWithPlaceholderTrueThenExcludesAllSecureHeaders() throws Exception {\n\t\tSystem.setProperty(\"security.headers.defaults.disabled\", \"true\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithPlaceholder\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDefaultsDisabledWithPlaceholderFalseThenIncludeAllSecureHeaders() throws Exception {\n\t\tSystem.setProperty(\"security.headers.defaults.disabled\", \"false\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithPlaceholder\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDefaultsDisabledWithPlaceholderMissingThenIncludeAllSecureHeaders() throws Exception {\n\t\tSystem.clearProperty(\"security.headers.defaults.disabled\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithPlaceholder\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingContentTypeOptionsThenDefaultsToNoSniff() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-Content-Type-Options\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithContentTypeOptions\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-Content-Type-Options\", \"nosniff\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingFrameOptionsThenDefaultsToDeny() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-Frame-Options\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithFrameOptions\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-Frame-Options\", \"DENY\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingFrameOptionsDenyThenRespondsWithDeny() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-Frame-Options\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithFrameOptionsDeny\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-Frame-Options\", \"DENY\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingFrameOptionsSameOriginThenRespondsWithSameOrigin() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-Frame-Options\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithFrameOptionsSameOrigin\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-Frame-Options\", \"SAMEORIGIN\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingFrameOptionsAllowFromNoOriginThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"DefaultsDisabledWithFrameOptionsAllowFromNoOrigin\"))\n\t\t\t\t.autowire())\n\t\t\t.withMessageContaining(\"Strategy requires a 'value' to be set.\");\n\t\t// FIXME better error message?\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingFrameOptionsAllowFromBlankOriginThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.spring.configLocations(this.xml(\"DefaultsDisabledWithFrameOptionsAllowFromBlankOrigin\"))\n\t\t\t\t\t\t.autowire())\n\t\t\t.withMessageContaining(\"Strategy requires a 'value' to be set.\");\n\t\t// FIXME better error message?\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingFrameOptionsAllowFromThenRespondsWithAllowFrom() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-Frame-Options\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithFrameOptionsAllowFrom\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-Frame-Options\", \"ALLOW-FROM https://example.org\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingFrameOptionsAllowFromWhitelistThenRespondsWithAllowFrom() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-Frame-Options\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithFrameOptionsAllowFromWhitelist\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").param(\"from\", \"https://example.org\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-Frame-Options\", \"ALLOW-FROM https://example.org\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-Frame-Options\", \"DENY\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingCustomHeaderThenRespondsWithThatHeader() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithCustomHeader\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"a\", \"b\"))\n\t\t\t\t.andExpect(header().string(\"c\", \"d\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingCustomHeaderWriterThenRespondsWithThatHeader() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithCustomHeaderWriter\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"abc\", \"def\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingCustomHeaderNameOnlyThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"DefaultsDisabledWithOnlyHeaderName\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingCustomHeaderValueOnlyThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"DefaultsDisabledWithOnlyHeaderValue\")).autowire());\n\t}\n\n\t@Test\n\tpublic void requestWhenPermissionsPolicyConfiguredWithGeolocationSelfThenGeolocationSelf() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithPermissionsPolicy\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(header().string(\"Permissions-Policy\", \"geolocation=(self)\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingXssProtectionThenDefaultsToModeBlock() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-XSS-Protection\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithXssProtection\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-XSS-Protection\", \"0\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSettingXssProtectionHeaderValueToZeroThenDefaultsToZero() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-XSS-Protection\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithXssProtectionHeaderValueZero\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-XSS-Protection\", \"0\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSettingXssProtectionHeaderValueToOneThenDefaultsToOne() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-XSS-Protection\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithXssProtectionHeaderValueOne\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-XSS-Protection\", \"1\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSettingXssProtectionHeaderValueToOneModeBlockThenDefaultsToOneModeBlock() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"X-XSS-Protection\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithXssProtectionHeaderValueOneModeBlock\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"X-XSS-Protection\", \"1; mode=block\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingCacheControlThenRespondsWithCorrespondingHeaders() throws Exception {\n\t\tMap<String, String> includedHeaders = ImmutableMap.<String, String>builder()\n\t\t\t.put(\"Cache-Control\", \"no-cache, no-store, max-age=0, must-revalidate\")\n\t\t\t.put(\"Expires\", \"0\")\n\t\t\t.put(\"Pragma\", \"no-cache\")\n\t\t\t.build();\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithCacheControl\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(includedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHstsThenRespondsWithHstsHeader() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"Strict-Transport-Security\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHsts\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"Strict-Transport-Security\", \"max-age=31536000 ; includeSubDomains\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void insecureRequestWhenUsingHstsThenExcludesHstsHeader() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHsts\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void insecureRequestWhenUsingCustomHstsRequestMatcherThenIncludesHstsHeader() throws Exception {\n\t\tSet<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet());\n\t\texcludedHeaders.remove(\"Strict-Transport-Security\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithCustomHstsRequestMatcher\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"Strict-Transport-Security\", \"max-age=1\"))\n\t\t\t\t.andExpect(excludes(excludedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingHpkpWithoutPinsThenAutowireFails() {\n\t\tassertThatExceptionOfType(XmlBeanDefinitionStoreException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"DefaultsDisabledWithEmptyHpkp\")).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.withMessageContaining(\"The content of element 'hpkp' is not complete\");\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingHpkpWithEmptyPinsThenAutowireFails() {\n\t\tassertThatExceptionOfType(XmlBeanDefinitionStoreException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"DefaultsDisabledWithEmptyPins\")).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.withMessageContaining(\"The content of element 'pins' is not complete\");\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHpkpThenIncludesHpkpHeader() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHpkp\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"Public-Key-Pins-Report-Only\",\n\t\t\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHpkpDefaultsThenIncludesHpkpHeaderUsingSha256() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHpkpDefaults\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"Public-Key-Pins-Report-Only\",\n\t\t\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void insecureRequestWhenUsingHpkpThenExcludesHpkpHeader() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHpkpDefaults\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().doesNotExist(\"Public-Key-Pins-Report-Only\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHpkpCustomMaxAgeThenIncludesHpkpHeaderAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHpkpMaxAge\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"Public-Key-Pins-Report-Only\",\n\t\t\t\t\t\t\"max-age=604800 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHpkpReportThenIncludesHpkpHeaderAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHpkpReport\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"Public-Key-Pins\",\n\t\t\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHpkpIncludeSubdomainsThenIncludesHpkpHeaderAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHpkpIncludeSubdomains\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"Public-Key-Pins-Report-Only\",\n\t\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; includeSubDomains\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHpkpReportUriThenIncludesHpkpHeaderAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithHpkpReportUri\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(header().string(\"Public-Key-Pins-Report-Only\",\n\t\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; report-uri=\\\"https://example.net/pkp-report\\\"\"))\n\t\t\t\t.andExpect(excludesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCacheControlDisabledThenExcludesHeader() throws Exception {\n\t\tCollection<String> cacheControl = Arrays.asList(\"Cache-Control\", \"Expires\", \"Pragma\");\n\t\tMap<String, String> allButCacheControl = remove(defaultHeaders, cacheControl);\n\t\tthis.spring.configLocations(this.xml(\"CacheControlDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(allButCacheControl))\n\t\t\t\t.andExpect(excludes(cacheControl));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenContentTypeOptionsDisabledThenExcludesHeader() throws Exception {\n\t\tCollection<String> contentTypeOptions = Arrays.asList(\"X-Content-Type-Options\");\n\t\tMap<String, String> allButContentTypeOptions = remove(defaultHeaders, contentTypeOptions);\n\t\tthis.spring.configLocations(this.xml(\"ContentTypeOptionsDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(allButContentTypeOptions))\n\t\t\t\t.andExpect(excludes(contentTypeOptions));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHstsDisabledThenExcludesHeader() throws Exception {\n\t\tCollection<String> hsts = Arrays.asList(\"Strict-Transport-Security\");\n\t\tMap<String, String> allButHsts = remove(defaultHeaders, hsts);\n\t\tthis.spring.configLocations(this.xml(\"HstsDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(allButHsts))\n\t\t\t\t.andExpect(excludes(hsts));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHpkpDisabledThenExcludesHeader() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"HpkpDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includesDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenFrameOptionsDisabledThenExcludesHeader() throws Exception {\n\t\tCollection<String> frameOptions = Arrays.asList(\"X-Frame-Options\");\n\t\tMap<String, String> allButFrameOptions = remove(defaultHeaders, frameOptions);\n\t\tthis.spring.configLocations(this.xml(\"FrameOptionsDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(allButFrameOptions))\n\t\t\t\t.andExpect(excludes(frameOptions));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenXssProtectionDisabledThenExcludesHeader() throws Exception {\n\t\tCollection<String> xssProtection = Arrays.asList(\"X-XSS-Protection\");\n\t\tMap<String, String> allButXssProtection = remove(defaultHeaders, xssProtection);\n\t\tthis.spring.configLocations(this.xml(\"XssProtectionDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(allButXssProtection))\n\t\t\t\t.andExpect(excludes(xssProtection));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenHstsDisabledAndIncludeSubdomainsSpecifiedThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.spring.configLocations(this.xml(\"HstsDisabledSpecifyingIncludeSubdomains\")).autowire())\n\t\t\t.withMessageContaining(\"include-subdomains\");\n\t}\n\n\t@Test\n\tpublic void configureWhenHstsDisabledAndMaxAgeSpecifiedThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"HstsDisabledSpecifyingMaxAge\")).autowire())\n\t\t\t.withMessageContaining(\"max-age\");\n\t}\n\n\t@Test\n\tpublic void configureWhenHstsDisabledAndRequestMatcherSpecifiedThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"HstsDisabledSpecifyingRequestMatcher\")).autowire())\n\t\t\t.withMessageContaining(\"request-matcher-ref\");\n\t}\n\n\t@Test\n\tpublic void configureWhenXssProtectionDisabledAndHeaderValueSpecifiedThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(\n\t\t\t\t() -> this.spring.configLocations(this.xml(\"XssProtectionDisabledSpecifyingHeaderValue\")).autowire())\n\t\t\t.withMessageContaining(\"header-value\");\n\t}\n\n\t@Test\n\tpublic void configureWhenFrameOptionsDisabledAndPolicySpecifiedThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"FrameOptionsDisabledSpecifyingPolicy\")).autowire())\n\t\t\t.withMessageContaining(\"policy\");\n\t}\n\n\t@Test\n\tpublic void requestWhenContentSecurityPolicyDirectivesConfiguredThenIncludesDirectives() throws Exception {\n\t\tMap<String, String> includedHeaders = new HashMap<>(defaultHeaders);\n\t\tincludedHeaders.put(\"Content-Security-Policy\", \"default-src 'self'\");\n\t\tthis.spring.configLocations(this.xml(\"ContentSecurityPolicyWithPolicyDirectives\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(includedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHeadersDisabledAndContentSecurityPolicyConfiguredThenExcludesHeader() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"HeadersDisabledWithContentSecurityPolicy\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(excludes(\"Content-Security-Policy\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenDefaultsDisabledAndContentSecurityPolicyConfiguredThenIncludesHeader() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithContentSecurityPolicy\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(header().string(\"Content-Security-Policy\", \"default-src 'self'\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenContentSecurityPolicyConfiguredWithEmptyDirectivesThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(\n\t\t\t\t() -> this.spring.configLocations(this.xml(\"ContentSecurityPolicyWithEmptyDirectives\")).autowire());\n\t}\n\n\t@Test\n\tpublic void requestWhenContentSecurityPolicyConfiguredWithReportOnlyThenIncludesReportOnlyHeader()\n\t\t\tthrows Exception {\n\t\tMap<String, String> includedHeaders = new HashMap<>(defaultHeaders);\n\t\tincludedHeaders.put(\"Content-Security-Policy-Report-Only\",\n\t\t\t\t\"default-src https:; report-uri https://example.org/\");\n\t\tthis.spring.configLocations(this.xml(\"ContentSecurityPolicyWithReportOnly\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").secure(true))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(includes(includedHeaders));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenReferrerPolicyConfiguredThenResponseDefaultsToNoReferrer() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithReferrerPolicy\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(header().string(\"Referrer-Policy\", \"no-referrer\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenReferrerPolicyConfiguredWithSameOriginThenRespondsWithSameOrigin() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithReferrerPolicySameOrigin\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(header().string(\"Referrer-Policy\", \"same-origin\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCrossOriginOpenerPolicyWithSameOriginAllowPopupsThenRespondsWithSameOriginAllowPopups()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithCrossOriginOpenerPolicy\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(header().string(\"Cross-Origin-Opener-Policy\", \"same-origin-allow-popups\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCrossOriginEmbedderPolicyWithRequireCorpThenRespondsWithRequireCorp() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithCrossOriginEmbedderPolicy\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(header().string(\"Cross-Origin-Embedder-Policy\", \"require-corp\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCrossOriginResourcePolicyWithSameOriginThenRespondsWithSameOrigin() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithCrossOriginResourcePolicy\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(header().string(\"Cross-Origin-Resource-Policy\", \"same-origin\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCrossOriginPoliciesRespondsCrossOriginPolicies() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsDisabledWithCrossOriginPolicies\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(excludesDefaults())\n\t\t\t\t.andExpect(header().string(\"Cross-Origin-Opener-Policy\", \"same-origin\"))\n\t\t\t\t.andExpect(header().string(\"Cross-Origin-Embedder-Policy\", \"require-corp\"))\n\t\t\t\t.andExpect(header().string(\"Cross-Origin-Resource-Policy\", \"same-origin\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionManagementConcurrencyControlMaxSessionIsOne() throws Exception {\n\t\tSystem.setProperty(\"security.session-management.concurrency-control.max-sessions\", \"1\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsSessionManagementConcurrencyControlMaxSessions\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestBuilder = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tHttpSession firstSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\t// @formatter:on\n\t\tassertThat(firstSession).isNotNull();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionManagementConcurrencyControlMaxSessionIsUnlimited() throws Exception {\n\t\tSystem.setProperty(\"security.session-management.concurrency-control.max-sessions\", \"-1\");\n\t\tthis.spring.configLocations(this.xml(\"DefaultsSessionManagementConcurrencyControlMaxSessions\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestBuilder = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tHttpSession firstSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(firstSession).isNotNull();\n\t\tHttpSession secondSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(secondSession).isNotNull();\n\t\t// @formatter:on\n\t\tassertThat(firstSession.getId()).isNotEqualTo(secondSession.getId());\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionManagementConcurrencyControlMaxSessionRefIsOneForNonAdminUsers() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsSessionManagementConcurrencyControlMaxSessionsRef\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestBuilder = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tHttpSession firstSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\t// @formatter:on\n\t\tassertThat(firstSession).isNotNull();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionManagementConcurrencyControlMaxSessionRefIsTwoForAdminUsers() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DefaultsSessionManagementConcurrencyControlMaxSessionsRef\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestBuilder = post(\"/login\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.param(\"username\", \"admin\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tHttpSession firstSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(firstSession).isNotNull();\n\t\tHttpSession secondSession = this.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tassertThat(secondSession).isNotNull();\n\t\t// @formatter:on\n\t\tassertThat(firstSession.getId()).isNotEqualTo(secondSession.getId());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(requestBuilder)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login?error\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionManagementConcurrencyControlWithInvalidMaxSessionConfig() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring\n\t\t\t\t.configLocations(this.xml(\"DefaultsSessionManagementConcurrencyControlWithInvalidMaxSessionsConfig\"))\n\t\t\t\t.autowire())\n\t\t\t.withMessageContaining(\"Cannot use 'max-sessions' attribute and 'max-sessions-ref' attribute together.\");\n\t}\n\n\tprivate static ResultMatcher includesDefaults() {\n\t\treturn includes(defaultHeaders);\n\t}\n\n\tprivate static ResultMatcher includes(Map<String, String> headers) {\n\t\treturn (result) -> {\n\t\t\tfor (Map.Entry<String, String> header : headers.entrySet()) {\n\t\t\t\theader().string(header.getKey(), header.getValue()).match(result);\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static ResultMatcher excludesDefaults() {\n\t\treturn excludes(defaultHeaders.keySet());\n\t}\n\n\tprivate static ResultMatcher excludes(Collection<String> headers) {\n\t\treturn (result) -> {\n\t\t\tfor (String name : headers) {\n\t\t\t\theader().doesNotExist(name).match(result);\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static ResultMatcher excludes(String... headers) {\n\t\treturn excludes(Arrays.asList(headers));\n\t}\n\n\tprivate static <K, V> Map<K, V> remove(Map<K, V> map, Collection<K> keys) {\n\t\tMap<K, V> copy = new HashMap<>(map);\n\t\tfor (K key : keys) {\n\t\t\tcopy.remove(key);\n\t\t}\n\t\treturn copy;\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tpublic static class SimpleController {\n\n\t\t@GetMapping(\"/\")\n\t\tpublic String ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tpublic static class CustomSessionLimit implements SessionLimit {\n\n\t\t@Override\n\t\tpublic Integer apply(Authentication authentication) {\n\t\t\tif (\"admin\".equals(authentication.getName())) {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/HttpInterceptUrlTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport jakarta.servlet.Filter;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.ConfigurableWebApplicationContext;\nimport org.springframework.web.context.support.XmlWebApplicationContext;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\npublic class HttpInterceptUrlTests {\n\n\tConfigurableWebApplicationContext context;\n\n\tMockMvc mockMvc;\n\n\t@AfterEach\n\tpublic void close() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void interceptUrlWhenRequestMatcherRefThenWorks() throws Exception {\n\t\tloadConfig(\"interceptUrlWhenRequestMatcherRefThenWorks.xml\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/foo\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\tthis.mockMvc.perform(get(\"/FOO\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\tthis.mockMvc.perform(get(\"/other\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\tprivate void loadConfig(String... configLocations) {\n\t\tfor (int i = 0; i < configLocations.length; i++) {\n\t\t\tconfigLocations[i] = getClass().getName().replaceAll(\"\\\\.\", \"/\") + \"-\" + configLocations[i];\n\t\t}\n\t\tXmlWebApplicationContext context = new XmlWebApplicationContext();\n\t\tcontext.setConfigLocations(configLocations);\n\t\tcontext.setServletContext(new MockServletContext());\n\t\tcontext.refresh();\n\t\tthis.context = context;\n\t\tcontext.getAutowireCapableBeanFactory().autowireBean(this);\n\t\tFilter springSecurityFilterChain = context.getBean(\"springSecurityFilterChain\", Filter.class);\n\t\tthis.mockMvc = MockMvcBuilders.standaloneSetup(new FooController())\n\t\t\t.addFilters(springSecurityFilterChain)\n\t\t\t.build();\n\t}\n\n\t@RestController\n\tstatic class FooController {\n\n\t\t@GetMapping(\"/*\")\n\t\tString foo() {\n\t\t\treturn \"foo\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/InterceptUrlConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.ServletRegistration;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.util.WebUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class InterceptUrlConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/InterceptUrlConfigTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t/**\n\t * sec-2256\n\t */\n\t@Test\n\tpublic void requestWhenMethodIsSpecifiedThenItIsNotGivenPriority() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Sec2256\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * sec-2256\n\t */\n\t@Test\n\tpublic void requestWhenMethodIsSpecifiedAndAuthorizationManagerThenItIsNotGivenPriority() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Sec2256AuthorizationManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();\n\t}\n\n\t/**\n\t * sec-2355\n\t */\n\t@Test\n\tpublic void requestWhenUsingPatchThenAuthorizesRequestsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"PatchMethod\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(patch(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(patch(\"/path\").with(adminCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * sec-2355\n\t */\n\t@Test\n\tpublic void requestWhenUsingPatchAndAuthorizationManagerThenAuthorizesRequestsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"PatchMethodAuthorizationManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(patch(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(patch(\"/path\").with(adminCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHasAnyRoleThenAuthorizesRequestsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"HasAnyRole\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path\").with(adminCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingHasAnyRoleAndAuthorizationManagerThenAuthorizesRequestsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"HasAnyRoleAuthorizationManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path\").with(adminCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();\n\t}\n\n\t/**\n\t * sec-2059\n\t */\n\t@Test\n\tpublic void requestWhenUsingPathVariablesThenAuthorizesRequestsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"PathVariables\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path/otheruser/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * sec-2059\n\t */\n\t@Test\n\tpublic void requestWhenUsingPathVariablesAndAuthorizationManagerThenAuthorizesRequestsAccordingly()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"PathVariablesAuthorizationManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path/otheruser/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();\n\t}\n\n\t/**\n\t * gh-3786\n\t */\n\t@Test\n\tpublic void requestWhenUsingCamelCasePathVariablesThenAuthorizesRequestsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CamelCasePathVariables\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path/otheruser/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/PATH/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * gh-3786\n\t */\n\t@Test\n\tpublic void requestWhenUsingCamelCasePathVariablesAndAuthorizationManagerThenAuthorizesRequestsAccordingly()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"CamelCasePathVariablesAuthorizationManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path/otheruser/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/PATH/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();\n\t}\n\n\t/**\n\t * sec-2059\n\t */\n\t@Test\n\tpublic void requestWhenUsingPathVariablesAndTypeConversionThenAuthorizesRequestsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"TypeConversionPathVariables\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path/1/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path/2/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * sec-2059\n\t */\n\t@Test\n\tpublic void requestWhenUsingPathVariablesAndTypeConversionAndAuthorizationManagerThenAuthorizesRequestsAccordingly()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"TypeConversionPathVariablesAuthorizationManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path/1/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path/2/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingMvcMatchersAndPathVariablesThenAuthorizesRequestsAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MvcMatchersPathVariables\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path/otheruser/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/PATH/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingMvcMatchersAndPathVariablesAndAuthorizationManagerThenAuthorizesRequestsAccordingly()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"MvcMatchersPathVariablesAuthorizationManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path/otheruser/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/PATH/user/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingRegexMatcherAndServletPathThenThrowsException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"RegexMatcherServletPath\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingRegexMatcherAndServletPathAndAuthorizationManagerThenThrowsException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(\n\t\t\t\t() -> this.spring.configLocations(this.xml(\"RegexMatcherServletPathAuthorizationManager\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingCiRegexMatcherAndServletPathThenThrowsException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"CiRegexMatcherServletPath\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingCiRegexMatcherAndServletPathAndAuthorizationManagerThenThrowsException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"CiRegexMatcherServletPathAuthorizationManager\"))\n\t\t\t\t.autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDefaultMatcherAndServletPathThenNoException() {\n\t\tassertThatNoException()\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"DefaultMatcherServletPath\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDefaultMatcherAndServletPathAndAuthorizationManagerThenNoException() {\n\t\tassertThatNoException()\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"DefaultMatcherServletPathAuthorizationManager\"))\n\t\t\t\t.autowire());\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingFilterAllDispatcherTypesAndAuthorizationManagerThenAuthorizesRequestsAccordingly()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"AuthorizationManagerFilterAllDispatcherTypes\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/path\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path\").with(adminCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/error\").with((request) -> {\n\t\t\trequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\");\n\t\t\trequest.setDispatcherType(DispatcherType.ERROR);\n\t\t\treturn request;\n\t\t})).andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/path\").with((request) -> {\n\t\t\trequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/path\");\n\t\t\trequest.setDispatcherType(DispatcherType.ERROR);\n\t\t\treturn request;\n\t\t})).andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t\tassertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();\n\t}\n\n\t/**\n\t * gh-18503\n\t */\n\t@Test\n\tpublic void configWhenInterceptUrlMissingAccessThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"MissingAccess\")).autowire())\n\t\t\t.withMessageContaining(\"access attribute cannot be empty or null\");\n\t}\n\n\t/**\n\t * gh-18503\n\t */\n\t@Test\n\tpublic void configWhenInterceptUrlEmptyAccessThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"EmptyAccess\")).autowire())\n\t\t\t.withMessageContaining(\"access attribute cannot be empty or null\");\n\t}\n\n\t/**\n\t * gh-18503\n\t */\n\t@Test\n\tpublic void configWhenInterceptUrlValidAccessThenLoads() {\n\t\tassertThatNoException().isThrownBy(() -> this.spring.configLocations(this.xml(\"ValidAccess\")).autowire());\n\t}\n\n\t/**\n\t * gh-18503\n\t */\n\t@Test\n\tpublic void configWhenUseAuthorizationManagerFalseAndMissingAccessThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"MissingAccessLegacy\")).autowire())\n\t\t\t.withMessageContaining(\"access attribute cannot be empty or null\");\n\t}\n\n\t/**\n\t * gh-18503\n\t */\n\t@Test\n\tpublic void configWhenUseAuthorizationManagerFalseAndEmptyAccessThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"EmptyAccessLegacy\")).autowire())\n\t\t\t.withMessageContaining(\"access attribute cannot be empty or null\");\n\t}\n\n\tprivate static RequestPostProcessor adminCredentials() {\n\t\treturn httpBasic(\"admin\", \"password\");\n\t}\n\n\tprivate static RequestPostProcessor userCredentials() {\n\t\treturn httpBasic(\"user\", \"password\");\n\t}\n\n\tprivate MockServletContext mockServletContext(String servletPath) {\n\t\tMockServletContext servletContext = spy(new MockServletContext());\n\t\tfinal ServletRegistration registration = mock(ServletRegistration.class);\n\t\tgiven(registration.getMappings()).willReturn(Collections.singleton(servletPath));\n\t\tAnswer<Map<String, ? extends ServletRegistration>> answer = (invocation) -> Collections.singletonMap(\"spring\",\n\t\t\t\tregistration);\n\t\tgiven(servletContext.getServletRegistrations()).willAnswer(answer);\n\t\treturn servletContext;\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tstatic class PathController {\n\n\t\t@RequestMapping(\"/path\")\n\t\tString path() {\n\t\t\treturn \"path\";\n\t\t}\n\n\t\t@RequestMapping(\"/path/{un}/path\")\n\t\tString path(@PathVariable(\"un\") String name) {\n\t\t\treturn name;\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class ErrorController {\n\n\t\t@GetMapping(\"/error\")\n\t\tString error() {\n\t\t\treturn \"error\";\n\t\t}\n\n\t}\n\n\tpublic static class Id {\n\n\t\tpublic boolean isOne(int i) {\n\t\t\treturn i == 1;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.security.Principal;\nimport java.security.cert.X509Certificate;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.stream.Collectors;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.spi.LoginModule;\n\nimport ch.qos.logback.classic.Logger;\nimport ch.qos.logback.classic.spi.ILoggingEvent;\nimport ch.qos.logback.core.Appender;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpServletResponseWrapper;\nimport org.apache.http.HttpStatus;\nimport org.assertj.core.api.iterable.Extractor;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.stubbing.Answer;\nimport org.slf4j.LoggerFactory;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.lang.NonNull;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.BeanNameCollectingPostProcessor;\nimport org.springframework.security.access.AccessDecisionManager;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.InsufficientAuthenticationException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.jaas.AuthorityGranter;\nimport org.springframework.security.config.TestDeferredSecurityContext;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.test.web.servlet.RequestCacheResultMatcher;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.access.channel.ChannelProcessingFilter;\nimport org.springframework.security.web.access.intercept.AuthorizationFilter;\nimport org.springframework.security.web.access.intercept.FilterSecurityInterceptor;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;\nimport org.springframework.security.web.authentication.preauth.x509.X509TestUtils;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.security.web.context.HttpRequestResponseHolder;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.firewall.FirewalledRequest;\nimport org.springframework.security.web.firewall.HttpFirewall;\nimport org.springframework.security.web.firewall.RequestRejectedException;\nimport org.springframework.security.web.firewall.RequestRejectedHandler;\nimport org.springframework.security.web.header.HeaderWriterFilter;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.RequestCacheAwareFilter;\nimport org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;\nimport org.springframework.security.web.session.DisableEncodeUrlFilter;\nimport org.springframework.security.web.transport.HttpsRedirectFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.support.XmlWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class MiscHttpConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/MiscHttpConfigTests\";\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void configureWhenUsingMinimalConfigurationThenParses() {\n\t\tthis.spring.configLocations(xml(\"MinimalConfiguration\")).autowire();\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingAutoConfigThenSetsUpCorrectFilterList() {\n\t\tthis.spring.configLocations(xml(\"AutoConfig\")).autowire();\n\t\tassertThatFiltersMatchExpectedAutoConfigList();\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingSecurityNoneThenNoFiltersAreSetUp() {\n\t\tthis.spring.configLocations(xml(\"NoSecurityForPattern\")).autowire();\n\t\tassertThat(getFilters(\"/unprotected\")).isEmpty();\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingDebugFilterAndPatternIsNotConfigureForSecurityThenRespondsOk() throws Exception {\n\t\tthis.spring.configLocations(xml(\"NoSecurityForPattern\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/unprotected\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"/nomatch\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHttpPatternUsesRegexMatchingThenMatchesAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"RegexSecurityPattern\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/unprotected\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHttpPatternUsesCiRegexMatchingThenMatchesAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CiRegexSecurityPattern\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/ProTectEd\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/UnProTectEd\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenHttpPatternUsesCustomRequestMatcherThenMatchesAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomRequestMatcher\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/unprotected\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-1152\n\t */\n\t@Test\n\tpublic void requestWhenUsingMinimalConfigurationThenHonorsAnonymousEndpoints() throws Exception {\n\t\tthis.spring.configLocations(xml(\"AnonymousEndpoints\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/unprotected\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tassertThat(getFilter(AnonymousAuthenticationFilter.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenAnonymousIsDisabledThenRejectsAnonymousEndpoints() throws Exception {\n\t\tthis.spring.configLocations(xml(\"AnonymousDisabled\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\tthis.mvc.perform(get(\"/unprotected\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t\tassertThat(getFilter(AnonymousAuthenticationFilter.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenAnonymousUsesCustomAttributesThenRespondsWithThoseAttributes() throws Exception {\n\t\tthis.spring.configLocations(xml(\"AnonymousCustomAttributes\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/protected\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"josh\"));\n\t\tthis.mvc.perform(get(\"/customKey\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(String.valueOf(\"myCustomKey\".hashCode())));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenAnonymousUsesMultipleGrantedAuthoritiesThenRespondsWithThoseAttributes() throws Exception {\n\t\tthis.spring.configLocations(xml(\"AnonymousMultipleAuthorities\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/protected\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"josh\"));\n\t\tthis.mvc.perform(get(\"/customKey\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(String.valueOf(\"myCustomKey\".hashCode())));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenInterceptUrlMatchesMethodThenSecuresAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"InterceptUrlMethod\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(post(\"/protected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(post(\"/protected\").with(postCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(delete(\"/protected\").with(postCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(delete(\"/protected\").with(adminCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenInterceptUrlMatchesMethodAndRequiresHttpsThenSecuresAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"InterceptUrlMethodRequiresHttps\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/protected\").with(csrf()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/protected\").secure(true).with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/protected\").secure(true).with(adminCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenInterceptUrlMatchesAnyPatternAndRequiresHttpsThenSecuresAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"InterceptUrlMethodRequiresHttpsAny\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/protected\").with(csrf()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/protected\").secure(true).with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/protected\").secure(true).with(adminCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenOncePerRequestIsFalseThenFilterSecurityInterceptorExercisedForForwards() {\n\t\tthis.spring.configLocations(xml(\"OncePerRequest\")).autowire();\n\t\tFilterSecurityInterceptor filterSecurityInterceptor = getFilter(FilterSecurityInterceptor.class);\n\t\tassertThat(filterSecurityInterceptor.isObserveOncePerRequest()).isFalse();\n\t}\n\n\t@Test\n\tpublic void configureWhenOncePerRequestIsTrueThenFilterSecurityInterceptorObserveOncePerRequestIsTrue() {\n\t\tthis.spring.configLocations(xml(\"OncePerRequestTrue\")).autowire();\n\t\tFilterSecurityInterceptor filterSecurityInterceptor = getFilter(FilterSecurityInterceptor.class);\n\t\tassertThat(filterSecurityInterceptor.isObserveOncePerRequest()).isTrue();\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomHttpBasicEntryPointRefThenInvokesOnCommence() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomHttpBasicEntryPointRef\")).autowire();\n\t\tAuthenticationEntryPoint entryPoint = this.spring.getContext().getBean(AuthenticationEntryPoint.class);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tverify(entryPoint).commence(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AuthenticationException.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenInterceptUrlWithRequiresChannelThenAddedChannelFilterToChain() {\n\t\tthis.spring.configLocations(xml(\"InterceptUrlMethodRequiresHttpsAny\")).autowire();\n\t\tassertThat(getFilter(ChannelProcessingFilter.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void configureWhenRedirectToHttpsThenFilterAdded() {\n\t\tthis.spring.configLocations(xml(\"RedirectToHttpsRequiresHttpsAny\")).autowire();\n\t\tassertThat(getFilter(HttpsRedirectFilter.class)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void getWhenRedirectToHttpsAnyThenRedirects() throws Exception {\n\t\tthis.spring.configLocations(xml(\"RedirectToHttpsRequiresHttpsAny\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"http://localhost\"))\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenPortsMappedThenRedirectedAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"PortsMappedInterceptUrlMethodRequiresAny\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"http://localhost:9080/protected\"))\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost:9443/protected\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenCustomFiltersThenAddedToChainInCorrectOrder() {\n\t\tSystem.setProperty(\"customFilterRef\", \"userFilter\");\n\t\tthis.spring.configLocations(xml(\"CustomFilters\")).autowire();\n\t\tList<Filter> filters = getFilters(\"/\");\n\t\tClass<?> userFilterClass = this.spring.getContext().getBean(\"userFilter\").getClass();\n\t\tassertThat(filters).extracting((Extractor<Filter, Class<?>>) (filter) -> filter.getClass())\n\t\t\t.containsSubsequence(userFilterClass, userFilterClass, SecurityContextHolderFilter.class,\n\t\t\t\t\tLogoutFilter.class, userFilterClass);\n\t}\n\n\t@Test\n\tpublic void configureWhenTwoFiltersWithSameOrderThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"CollidingFilters\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingX509ThenAddsX509FilterCorrectly() {\n\t\tthis.spring.configLocations(xml(\"X509\")).autowire();\n\t\tassertThat(getFilters(\"/\")).extracting((Extractor<Filter, Class<?>>) (filter) -> filter.getClass())\n\t\t\t.containsSubsequence(CsrfFilter.class, X509AuthenticationFilter.class, ExceptionTranslationFilter.class);\n\t}\n\n\t@Test\n\tpublic void getWhenUsingX509PrincipalExtractorRef() throws Exception {\n\t\tthis.spring.configLocations(xml(\"X509PrincipalExtractorRef\")).autowire();\n\t\tX509Certificate certificate = X509TestUtils.buildTestCertificate();\n\t\tRequestPostProcessor x509 = x509(certificate);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\").with(x509))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingX509PrincipalExtractorRefAndSubjectPrincipalRegex() throws Exception {\n\t\tString xmlResourceName = \"X509PrincipalExtractorRefAndSubjectPrincipalRegex\";\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(xmlResourceName)).autowire())\n\t\t\t.withMessage(\"Configuration problem: The attribute 'principal-extractor-ref' cannot be used together with the 'subject-principal-regex' attribute within <x509>\\n\" + \"Offending resource: class path resource [org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRefAndSubjectPrincipalRegex.xml]\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingX509AndPropertyPlaceholderThenSubjectPrincipalRegexIsConfigured() throws Exception {\n\t\tSystem.setProperty(\"subject_principal_regex\", \"OU=(.*?)(?:,|$)\");\n\t\tthis.spring.configLocations(xml(\"X509\")).autowire();\n\t\tRequestPostProcessor x509 = x509(\n\t\t\t\t\"classpath:org/springframework/security/config/http/MiscHttpConfigTests-certificate.pem\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\").with(x509))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingX509CustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tSystem.setProperty(\"subject_principal_regex\", \"OU=(.*?)(?:,|$)\");\n\t\tthis.spring.configLocations(xml(\"X509WithSecurityContextHolderStrategy\")).autowire();\n\t\tRequestPostProcessor x509 = x509(\n\t\t\t\t\"classpath:org/springframework/security/config/http/MiscHttpConfigTests-certificate.pem\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\").with(x509))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tverify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingInvalidLogoutSuccessUrlThenThrowsException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"InvalidLogoutSuccessUrl\")).autowire());\n\t}\n\n\t@Test\n\tpublic void logoutWhenSpecifyingCookiesToDeleteThenSetCookieAdded() throws Exception {\n\t\tthis.spring.configLocations(xml(\"DeleteCookies\")).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/logout\").with(csrf())).andReturn();\n\t\tList<String> values = result.getResponse().getHeaders(\"Set-Cookie\");\n\t\tassertThat(values).hasSize(2);\n\t\tassertThat(values).extracting((value) -> value.split(\"=\")[0]).contains(\"JSESSIONID\", \"mycookie\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenSpecifyingSuccessHandlerRefThenResponseHandledAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"LogoutSuccessHandlerRef\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t\t.andExpect(redirectedUrl(\"/logoutSuccessEndpoint\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUnauthenticatedThenUsesConfiguredRequestCache() throws Exception {\n\t\tthis.spring.configLocations(xml(\"RequestCache\")).autowire();\n\t\tRequestCache requestCache = this.spring.getContext().getBean(RequestCache.class);\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(requestCache).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void getWhenUnauthenticatedThenUsesConfiguredAuthenticationEntryPoint() throws Exception {\n\t\tthis.spring.configLocations(xml(\"EntryPoint\")).autowire();\n\t\tAuthenticationEntryPoint entryPoint = this.spring.getContext().getBean(AuthenticationEntryPoint.class);\n\t\tthis.mvc.perform(get(\"/\"));\n\t\tverify(entryPoint).commence(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AuthenticationException.class));\n\t}\n\n\t/**\n\t * See SEC-750. If the http security post processor causes beans to be instantiated\n\t * too eagerly, they way miss additional processing. In this method we have a\n\t * UserDetailsService which is referenced from the namespace and also has a post\n\t * processor registered which will modify it.\n\t */\n\t@Test\n\tpublic void configureWhenUsingCustomUserDetailsServiceThenBeanPostProcessorsAreStillApplied() {\n\t\tthis.spring.configLocations(xml(\"Sec750\")).autowire();\n\t\tBeanNameCollectingPostProcessor postProcessor = this.spring.getContext()\n\t\t\t.getBean(BeanNameCollectingPostProcessor.class);\n\t\tassertThat(postProcessor.getBeforeInitPostProcessedBeans()).contains(\"authenticationProvider\", \"userService\");\n\t\tassertThat(postProcessor.getAfterInitPostProcessedBeans()).contains(\"authenticationProvider\", \"userService\");\n\t}\n\n\t/* SEC-934 */\n\t@Test\n\tpublic void getWhenUsingTwoIdenticalInterceptUrlsThenTheSecondTakesPrecedence() throws Exception {\n\t\tthis.spring.configLocations(xml(\"Sec934\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/protected\").with(adminCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenAuthenticatingThenConsultsCustomSecurityContextRepository() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SecurityContextRepository\")).autowire();\n\t\tSecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);\n\t\tSecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\tgiven(repository.loadDeferredContext(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new TestDeferredSecurityContext(context, false));\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/protected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void getWhenExplicitSaveAndRepositoryAndAuthenticatingThenConsultsCustomSecurityContextRepository()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"ExplicitSaveAndExplicitRepository\")).autowire();\n\t\tSecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);\n\t\tSecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\tgiven(repository.loadDeferredContext(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new TestDeferredSecurityContext(context, false));\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(formLogin())\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tverify(repository, atLeastOnce()).saveContext(any(SecurityContext.class), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void getWhenExplicitSaveAndExplicitSaveAndAuthenticatingThenConsultsCustomSecurityContextRepository()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"ExplicitSave\")).autowire();\n\t\tSecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(formLogin())\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(repository.loadContext(new HttpRequestResponseHolder(result.getRequest(), result.getResponse()))\n\t\t\t.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void getWhenUsingInterceptUrlExpressionsThenAuthorizesAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"InterceptUrlExpressions\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/protected\").with(adminCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/protected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mvc.perform(get(\"/unprotected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomExpressionHandlerThenAuthorizesAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"ExpressionHandler\")).autowire();\n\t\tPermissionEvaluator permissionEvaluator = this.spring.getContext().getBean(PermissionEvaluator.class);\n\t\tgiven(permissionEvaluator.hasPermission(any(Authentication.class), any(Object.class), any(Object.class)))\n\t\t\t.willReturn(false);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t\tverify(permissionEvaluator).hasPermission(any(Authentication.class), any(Object.class), any(Object.class));\n\t}\n\n\t@Test\n\tpublic void configureWhenProtectingLoginPageThenWarningLogged() {\n\t\tByteArrayOutputStream baos = new ByteArrayOutputStream();\n\t\tredirectLogsTo(baos, DefaultFilterChainValidator.class);\n\t\tthis.spring.configLocations(xml(\"ProtectedLoginPage\")).autowire();\n\t\tassertThat(baos.toString()).contains(\"[WARN]\");\n\t}\n\n\t@Test\n\tpublic void configureWhenProtectingLoginPageAuthorizationManagerThenWarningLogged() {\n\t\tByteArrayOutputStream baos = new ByteArrayOutputStream();\n\t\tredirectLogsTo(baos, DefaultFilterChainValidator.class);\n\t\tthis.spring.configLocations(xml(\"ProtectedLoginPageAuthorizationManager\")).autowire();\n\t\tassertThat(baos.toString()).contains(\"[WARN]\");\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDisableUrlRewritingThenRedirectIsNotEncodedByResponse()\n\t\t\tthrows IOException, ServletException {\n\t\tthis.spring.configLocations(xml(\"DisableUrlRewriting\")).autowire();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tproxy.doFilter(request, new EncodeUrlDenyingHttpServletResponseWrapper(response), (req, resp) -> {\n\t\t});\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/login\");\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDisableUrlRewritingAndCustomRepositoryThenRedirectIsNotEncodedByResponse()\n\t\t\tthrows IOException, ServletException {\n\t\tthis.spring.configLocations(xml(\"DisableUrlRewriting-NullSecurityContextRepository\")).autowire();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse responseToSpy = spy(new MockHttpServletResponse());\n\t\tFilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\tproxy.doFilter(request, responseToSpy, (req, resp) -> {\n\t\t\tHttpServletResponse httpResponse = (HttpServletResponse) resp;\n\t\t\thttpResponse.encodeURL(\"/\");\n\t\t\thttpResponse.encodeRedirectURL(\"/\");\n\t\t\thttpResponse.getWriter().write(\"encodeRedirect\");\n\t\t});\n\t\tverify(responseToSpy, never()).encodeRedirectURL(any());\n\t\tverify(responseToSpy, never()).encodeURL(any());\n\t\tassertThat(responseToSpy.getContentAsString()).isEqualTo(\"encodeRedirect\");\n\t}\n\n\t@Test\n\tpublic void configureWhenUserDetailsServiceInParentContextThenLocatesSuccessfully() {\n\t\tassertThatExceptionOfType(BeansException.class).isThrownBy(\n\t\t\t\t() -> this.spring.configLocations(MiscHttpConfigTests.xml(\"MissingUserDetailsService\")).autowire());\n\t\ttry (XmlWebApplicationContext parent = new XmlWebApplicationContext()) {\n\t\t\tparent.setConfigLocations(MiscHttpConfigTests.xml(\"AutoConfig\"));\n\t\t\tparent.refresh();\n\t\t\ttry (XmlWebApplicationContext child = new XmlWebApplicationContext()) {\n\t\t\t\tchild.setParent(parent);\n\t\t\t\tchild.setConfigLocation(MiscHttpConfigTests.xml(\"MissingUserDetailsService\"));\n\t\t\t\tchild.refresh();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void loginWhenConfiguredWithNoInternalAuthenticationProvidersThenSuccessfullyAuthenticates()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"NoInternalAuthenticationProviders\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\");\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingDefaultsThenErasesCredentialsAfterAuthentication() throws Exception {\n\t\tthis.spring.configLocations(xml(\"HttpBasic\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/password\").with(userCredentials()))\n\t\t\t\t.andExpect(content().string(\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenAuthenticationManagerConfiguredToEraseCredentialsThenErasesCredentialsAfterAuthentication()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"AuthenticationManagerEraseCredentials\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/password\").with(userCredentials()))\n\t\t\t\t.andExpect(content().string(\"\"));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-2020\n\t */\n\t@Test\n\tpublic void loginWhenAuthenticationManagerRefConfiguredToKeepCredentialsThenKeepsCredentialsAfterAuthentication()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"AuthenticationManagerRefKeepCredentials\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/password\").with(userCredentials()))\n\t\t\t\t.andExpect(content().string(\"password\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenAuthenticationManagerRefIsNotAProviderManagerThenKeepsCredentialsAccordingly()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"AuthenticationManagerRefNotProviderManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/password\").with(userCredentials()))\n\t\t\t\t.andExpect(content().string(\"password\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenJeeFilterThenExtractsRoles() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JeeFilter\")).autowire();\n\t\tPrincipal user = mock(Principal.class);\n\t\tgiven(user.getName()).willReturn(\"joe\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder rolesRequest = get(\"/roles\")\n\t\t\t\t.principal(user)\n\t\t\t\t.with((request) -> {\n\t\t\t\t\trequest.addUserRole(\"admin\");\n\t\t\t\t\trequest.addUserRole(\"user\");\n\t\t\t\t\trequest.addUserRole(\"unmapped\");\n\t\t\t\t\treturn request;\n\t\t\t\t});\n\t\tthis.mvc.perform(rolesRequest)\n\t\t\t\t.andExpect(content().string(\"ROLE_admin,ROLE_user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenJeeFilterCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JeeFilterWithSecurityContextHolderStrategy\")).autowire();\n\t\tPrincipal user = mock(Principal.class);\n\t\tgiven(user.getName()).willReturn(\"joe\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder rolesRequest = get(\"/roles\")\n\t\t\t\t.principal(user)\n\t\t\t\t.with((request) -> {\n\t\t\t\t\trequest.addUserRole(\"admin\");\n\t\t\t\t\trequest.addUserRole(\"user\");\n\t\t\t\t\trequest.addUserRole(\"unmapped\");\n\t\t\t\t\treturn request;\n\t\t\t\t});\n\t\tthis.mvc.perform(rolesRequest)\n\t\t\t\t.andExpect(content().string(\"ROLE_admin,ROLE_user\"));\n\t\t// @formatter:on\n\t\tverify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingCustomAuthenticationDetailsSourceRefThenAuthenticationSourcesDetailsAccordingly()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"CustomAuthenticationDetailsSourceRef\")).autowire();\n\t\tObject details = mock(Object.class);\n\t\tAuthenticationDetailsSource source = this.spring.getContext().getBean(AuthenticationDetailsSource.class);\n\t\tgiven(source.buildDetails(any(Object.class))).willReturn(details);\n\t\tRequestPostProcessor x509 = x509(\n\t\t\t\t\"classpath:org/springframework/security/config/http/MiscHttpConfigTests-certificate.pem\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/details\").with(userCredentials()))\n\t\t\t\t.andExpect(content().string(details.getClass().getName()));\n\t\tthis.mvc.perform(get(\"/details\").with(x509))\n\t\t\t\t.andExpect(content().string(details.getClass().getName()));\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(loginRequest)\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tthis.mvc.perform(get(\"/details\").session(session))\n\t\t\t\t.andExpect(content().string(details.getClass().getName()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loginWhenUsingJaasApiProvisionThenJaasSubjectContainsUsername() throws Exception {\n\t\tthis.spring.configLocations(xml(\"Jaas\")).autowire();\n\t\tAuthorityGranter granter = this.spring.getContext().getBean(AuthorityGranter.class);\n\t\tgiven(granter.grant(any(Principal.class))).willReturn(new HashSet<>(Arrays.asList(\"USER\")));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/username\").with(userCredentials()))\n\t\t\t\t.andExpect(content().string(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomHttpFirewallThenFirewallIsInvoked() throws Exception {\n\t\tthis.spring.configLocations(xml(\"HttpFirewall\")).autowire();\n\t\tFirewalledRequest request = new FirewalledRequest(new MockHttpServletRequest()) {\n\t\t\t@Override\n\t\t\tpublic void reset() {\n\t\t\t}\n\t\t};\n\t\tHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpFirewall firewall = this.spring.getContext().getBean(HttpFirewall.class);\n\t\tgiven(firewall.getFirewalledRequest(any(HttpServletRequest.class))).willReturn(request);\n\t\tgiven(firewall.getFirewalledResponse(any(HttpServletResponse.class))).willReturn(response);\n\t\tthis.mvc.perform(get(\"/unprotected\"));\n\t\tverify(firewall).getFirewalledRequest(any(HttpServletRequest.class));\n\t\tverify(firewall).getFirewalledResponse(any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomRequestRejectedHandlerThenRequestRejectedHandlerIsInvoked() throws Exception {\n\t\tthis.spring.configLocations(xml(\"RequestRejectedHandler\")).autowire();\n\t\tHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestRejectedException rejected = new RequestRejectedException(\"failed\");\n\t\tHttpFirewall firewall = this.spring.getContext().getBean(HttpFirewall.class);\n\t\tRequestRejectedHandler requestRejectedHandler = this.spring.getContext().getBean(RequestRejectedHandler.class);\n\t\tgiven(firewall.getFirewalledRequest(any(HttpServletRequest.class))).willThrow(rejected);\n\t\tthis.mvc.perform(get(\"/unprotected\"));\n\t\tverify(requestRejectedHandler).handle(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomAccessDecisionManagerThenAuthorizesAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomAccessDecisionManager\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/unprotected\").with(userCredentials()))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void asyncDispatchWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WithSecurityContextHolderStrategy\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder requestWithBob = get(\"/name\").with(user(\"Bob\"));\n\t\tMvcResult mvcResult = this.mvc.perform(requestWithBob)\n\t\t\t\t.andExpect(request().asyncStarted())\n\t\t\t\t.andReturn();\n\t\tthis.mvc.perform(asyncDispatch(mvcResult))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"Bob\"));\n\t\t// @formatter:on\n\t\tverify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t/**\n\t * SEC-1893\n\t */\n\t@Test\n\tpublic void authenticateWhenUsingPortMapperThenRedirectsAppropriately() throws Exception {\n\t\tthis.spring.configLocations(xml(\"PortsMappedRequiresHttps\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpSession session = (MockHttpSession) this.mvc.perform(get(\"https://localhost:9080/protected\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"))\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session(session)\n\t\t\t\t.with(csrf());\n\t\tsession = (MockHttpSession) this.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(RequestCacheResultMatcher.redirectToCachedRequest())\n\t\t\t\t.andReturn()\n\t\t\t\t.getRequest()\n\t\t\t\t.getSession(false);\n\t\tthis.mvc.perform(get(\"http://localhost:9080/protected\").session(session))\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost:9443/protected\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate void redirectLogsTo(OutputStream os, Class<?> clazz) {\n\t\tLogger logger = (Logger) LoggerFactory.getLogger(clazz);\n\t\tAppender<ILoggingEvent> appender = mock(Appender.class);\n\t\tgiven(appender.isStarted()).willReturn(true);\n\t\twillAnswer(writeTo(os)).given(appender).doAppend(any(ILoggingEvent.class));\n\t\tlogger.addAppender(appender);\n\t}\n\n\tprivate Answer<ILoggingEvent> writeTo(OutputStream os) {\n\t\treturn (invocation) -> {\n\t\t\tos.write(invocation.getArgument(0).toString().getBytes());\n\t\t\treturn null;\n\t\t};\n\t}\n\n\tprivate void assertThatFiltersMatchExpectedAutoConfigList() {\n\t\tassertThatFiltersMatchExpectedAutoConfigList(\"/\");\n\t}\n\n\tprivate void assertThatFiltersMatchExpectedAutoConfigList(String url) {\n\t\tIterator<Filter> filters = getFilters(url).iterator();\n\t\tassertThat(filters.next()).isInstanceOf(DisableEncodeUrlFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(SecurityContextHolderFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(WebAsyncManagerIntegrationFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(HeaderWriterFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(CsrfFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(LogoutFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(UsernamePasswordAuthenticationFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(DefaultResourcesFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(DefaultLoginPageGeneratingFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(DefaultLogoutPageGeneratingFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(BasicAuthenticationFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(RequestCacheAwareFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(SecurityContextHolderAwareRequestFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(AnonymousAuthenticationFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(ExceptionTranslationFilter.class);\n\t\tassertThat(filters.next()).isInstanceOf(AuthorizationFilter.class);\n\t}\n\n\tprivate <T extends Filter> T getFilter(Class<T> filterClass) {\n\t\treturn (T) getFilters(\"/\").stream().filter(filterClass::isInstance).findFirst().orElse(null);\n\t}\n\n\tprivate List<Filter> getFilters(String url) {\n\t\tFilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\treturn proxy.getFilters(url);\n\t}\n\n\t@NonNull\n\tprivate static RequestPostProcessor userCredentials() {\n\t\treturn httpBasic(\"user\", \"password\");\n\t}\n\n\t@NonNull\n\tprivate static RequestPostProcessor adminCredentials() {\n\t\treturn httpBasic(\"admin\", \"password\");\n\t}\n\n\t@NonNull\n\tprivate static RequestPostProcessor postCredentials() {\n\t\treturn httpBasic(\"poster\", \"password\");\n\t}\n\n\tprivate static String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@RequestMapping(\"/unprotected\")\n\t\tString unprotected() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t\t@RequestMapping(\"/protected\")\n\t\tString protectedMethod(@AuthenticationPrincipal String name) {\n\t\t\treturn name;\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class CustomKeyController {\n\n\t\t@GetMapping(\"/customKey\")\n\t\tString customKey() {\n\t\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t\tif (authentication != null && authentication instanceof AnonymousAuthenticationToken) {\n\t\t\t\treturn String.valueOf(((AnonymousAuthenticationToken) authentication).getKeyHash());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class AuthenticationController {\n\n\t\t@GetMapping(\"/password\")\n\t\tString password(Authentication authentication) {\n\t\t\treturn (String) authentication.getCredentials();\n\t\t}\n\n\t\t@GetMapping(\"/roles\")\n\t\tString roles(Authentication authentication) {\n\t\t\treturn authentication.getAuthorities()\n\t\t\t\t.stream()\n\t\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\n\t\t@GetMapping(\"/details\")\n\t\tString details(Authentication authentication) {\n\t\t\treturn authentication.getDetails().getClass().getName();\n\t\t}\n\n\t\t@GetMapping(\"/name\")\n\t\tCallable<String> name(Authentication authentication) {\n\t\t\treturn () -> authentication.getName();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class JaasController {\n\n\t\t@GetMapping(\"/username\")\n\t\tString username() {\n\t\t\tSubject subject = Subject.current();\n\t\t\treturn subject.getPrincipals().iterator().next().getName();\n\t\t}\n\n\t}\n\n\tpublic static class JaasLoginModule implements LoginModule {\n\n\t\tprivate Subject subject;\n\n\t\t@Override\n\t\tpublic void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,\n\t\t\t\tMap<String, ?> options) {\n\t\t\tthis.subject = subject;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean login() {\n\t\t\treturn this.subject.getPrincipals().add(() -> \"user\");\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean commit() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean abort() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean logout() {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\tstatic class MockAccessDecisionManager implements AccessDecisionManager {\n\n\t\t@Override\n\t\tpublic void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)\n\t\t\t\tthrows AccessDeniedException, InsufficientAuthenticationException {\n\t\t\tthrow new AccessDeniedException(\"teapot\");\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(ConfigAttribute attribute) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> clazz) {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\tstatic class MockAuthenticationManager implements AuthenticationManager {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) {\n\t\t\treturn new TestingAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(),\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\t}\n\n\t}\n\n\tstatic class EncodeUrlDenyingHttpServletResponseWrapper extends HttpServletResponseWrapper {\n\n\t\tEncodeUrlDenyingHttpServletResponseWrapper(HttpServletResponse response) {\n\t\t\tsuper(response);\n\t\t}\n\n\t\t@Override\n\t\tpublic String encodeURL(String url) {\n\t\t\tthrow new RuntimeException(\"Unexpected invocation of encodeURL\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String encodeRedirectURL(String url) {\n\t\t\tthrow new RuntimeException(\"Unexpected invocation of encodeURL\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/MultiHttpBlockConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests scenarios with multiple &lt;http&gt; elements.\n *\n * @author Luke Taylor\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class MultiHttpBlockConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/MultiHttpBlockConfigTests\";\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void requestWhenUsingMutuallyExclusiveHttpElementsThenIsRoutedAccordingly() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"DistinctHttpElements\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/first\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\tMockHttpServletRequestBuilder formLoginRequest = post(\"/second/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(formLoginRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDuplicateHttpElementsThenThrowsWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"IdenticalHttpElements\")).autowire())\n\t\t\t.withCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingIndenticallyPatternedHttpElementsThenThrowsWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"IdenticallyPatternedHttpElements\")).autowire())\n\t\t\t.withCauseInstanceOf(IllegalArgumentException.class);\n\t}\n\n\t/**\n\t * SEC-1937\n\t */\n\t@Test\n\tpublic void requestWhenTargettingAuthenticationManagersToCorrespondingHttpElementsThenAuthenticationProceeds()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"Sec1937\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder basicLoginRequest = get(\"/first\")\n\t\t\t\t.with(httpBasic(\"first\", \"password\"))\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(basicLoginRequest)\n\t\t\t\t.andExpect(status().isOk());\n\t\tMockHttpServletRequestBuilder formLoginRequest = post(\"/second/login\")\n\t\t\t\t.param(\"username\", \"second\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(formLoginRequest)\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@Controller\n\tstatic class BasicController {\n\n\t\t@GetMapping(\"/first\")\n\t\tString first() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/NamespaceHttpBasicTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.lang.reflect.Method;\nimport java.util.Base64;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\npublic class NamespaceHttpBasicTests {\n\n\t@Mock\n\tMethod method;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\tConfigurableApplicationContext context;\n\n\tFilter springSecurityFilterChain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest(\"GET\", \"\");\n\t\tthis.request.setMethod(\"GET\");\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void teardown() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t// gh-3296\n\t@Test\n\tpublic void httpBasicWithPasswordEncoder() throws Exception {\n\t\t// @formatter:off\n\t\tloadContext(\"<http>\\n\"\n\t\t\t+ \"\t<intercept-url pattern=\\\"/**\\\" access=\\\"hasRole('USER')\\\" />\\n\"\n\t\t\t+ \"\t<http-basic />\\n\"\n\t\t\t+ \"</http>\\n\"\n\t\t\t+  \"\\n\"\n\t\t\t+  \"<authentication-manager id=\\\"authenticationManager\\\">\\n\"\n\t\t\t+  \"\t<authentication-provider>\\n\"\n\t\t\t+  \"\t\t<password-encoder ref=\\\"passwordEncoder\\\" />\\n\"\n\t\t\t+  \"\t\t<user-service>\\n\"\n\t\t\t+  \"\t\t\t<user name=\\\"user\\\" password=\\\"$2a$10$Zk1MxFEt7YYji4Ccy9xlfuewWzUMsmHZfy4UcCmNKVV6z5i/JNGJW\\\" authorities=\\\"ROLE_USER\\\"/>\\n\"\n\t\t\t+  \"\t\t</user-service>\\n\"\n\t\t\t+  \"\t</authentication-provider>\\n\"\n\t\t\t+  \"</authentication-manager>\\n\"\n\t\t\t+  \"<b:bean id=\\\"passwordEncoder\\\"\\n\"\n\t\t\t+  \"\tclass=\\\"org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder\\\" />\");\n\t\t// @formatter:on\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\t\"Basic \" + Base64.getEncoder().encodeToString(\"user:test\".getBytes(\"UTF-8\")));\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void httpBasicCustomSecurityContextHolderStrategy() throws Exception {\n\t\t// @formatter:off\n\t\tloadContext(\"<http auto-config=\\\"true\\\" use-expressions=\\\"false\\\" security-context-holder-strategy-ref=\\\"ref\\\" use-authorization-manager=\\\"false\\\"/>\\n\"\n\t\t\t\t+  \"<authentication-manager id=\\\"authenticationManager\\\">\\n\"\n\t\t\t\t+  \"\t<authentication-provider>\\n\"\n\t\t\t\t+  \"\t\t<user-service>\\n\"\n\t\t\t\t+  \"\t\t\t<user name=\\\"user\\\" password=\\\"{noop}test\\\" authorities=\\\"ROLE_USER\\\"/>\\n\"\n\t\t\t\t+  \"\t\t</user-service>\\n\"\n\t\t\t\t+  \"\t</authentication-provider>\\n\"\n\t\t\t\t+  \"</authentication-manager>\\n\"\n\t\t\t\t+  \"<b:bean id=\\\"ref\\\" class=\\\"org.mockito.Mockito\\\" factory-method=\\\"spy\\\">\\n\" +\n\t\t\t\t\"\t<b:constructor-arg>\\n\" +\n\t\t\t\t\"\t\t<b:bean class=\\\"org.springframework.security.config.MockSecurityContextHolderStrategy\\\"/>\\n\" +\n\t\t\t\t\"\t</b:constructor-arg>\\n\" +\n\t\t\t\t\"</b:bean>\");\n\t\t// @formatter:on\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\t\"Basic \" + Base64.getEncoder().encodeToString(\"user:test\".getBytes(\"UTF-8\")));\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tverify(this.context.getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t// gh-4220\n\t@Test\n\tpublic void httpBasicUnauthorizedOnDefault() throws Exception {\n\t\t// @formatter:off\n\t\tloadContext(\"<http>\\n\"\n\t\t\t+  \"\t<intercept-url pattern=\\\"/**\\\" access=\\\"hasRole('USER')\\\" />\\n\"\n\t\t\t+  \"\t<http-basic />\\n\"\n\t\t\t+  \"</http>\\n\"\n\t\t\t+  \"\\n\"\n\t\t\t+  \"<authentication-manager />\");\n\t\t// @formatter:on\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tassertThat(this.response.getHeader(\"WWW-Authenticate\")).isEqualTo(\"Basic realm=\\\"Realm\\\", charset=\\\"UTF-8\\\"\");\n\t}\n\n\tprivate void loadContext(String context) {\n\t\tthis.context = new InMemoryXmlApplicationContext(context);\n\t\tthis.springSecurityFilterChain = this.context.getBean(\"springSecurityFilterChain\", Filter.class);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationContext;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.TokenExchangeOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2AuthorizedClientManagerRegistrar}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2AuthorizedClientManagerRegistrarTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests\";\n\n\tprivate static OAuth2AccessTokenResponseClient<? super AbstractOAuth2AuthorizationGrantRequest> MOCK_RESPONSE_CLIENT;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Autowired(required = false)\n\tprivate AuthorizationCodeOAuth2AuthorizedClientProvider authorizationCodeAuthorizedClientProvider;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setUp() {\n\t\tMOCK_RESPONSE_CLIENT = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void loadContextWhenOAuth2ClientEnabledThenConfigured() {\n\t\tthis.spring.configLocations(xml(\"minimal\")).autowire();\n\t\tassertThat(this.authorizedClientManager).isNotNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizationCodeAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"providers\")).autowire();\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(\"google\")\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest))\n\t\t\t\t.extracting(OAuth2AuthorizationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(\"client_authorization_required\");\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationCodeAuthorizedClientProvider).authorize(any(OAuth2AuthorizationContext.class));\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRefreshTokenAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"clients\")).autowire();\n\t\ttestRefreshTokenGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRefreshTokenAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"providers\")).autowire();\n\t\ttestRefreshTokenGrant();\n\t}\n\n\tprivate void testRefreshTokenGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null);\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google\");\n\t\tOAuth2AuthorizedClient existingAuthorizedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\tauthentication.getName(), getExpiredAccessToken(), TestOAuth2RefreshTokens.refreshToken());\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(existingAuthorizedClient, authentication, this.request,\n\t\t\t\tthis.response);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withAuthorizedClient(existingAuthorizedClient)\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<OAuth2RefreshTokenGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2RefreshTokenGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);\n\t\tassertThat(grantRequest.getAccessToken()).isEqualTo(existingAuthorizedClient.getAccessToken());\n\t\tassertThat(grantRequest.getRefreshToken()).isEqualTo(existingAuthorizedClient.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"clients\")).autowire();\n\t\ttestClientCredentialsGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"providers\")).autowire();\n\t\ttestClientCredentialsGrant();\n\t}\n\n\tprivate void testClientCredentialsGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", null);\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"github\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<OAuth2ClientCredentialsGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2ClientCredentialsGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"clients\")).autowire();\n\t\ttestJwtBearerGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"providers\")).autowire();\n\t\ttestJwtBearerGrant();\n\t}\n\n\tprivate void testJwtBearerGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(JwtBearerGrantRequest.class))).willReturn(accessTokenResponse);\n\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt());\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"okta\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<JwtBearerGrantRequest> grantRequestCaptor = ArgumentCaptor.forClass(JwtBearerGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tJwtBearerGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER);\n\t\tassertThat(grantRequest.getJwt().getSubject()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAccessTokenResponseClientBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"clients\")).autowire();\n\t\ttestTokenExchangeGrant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAuthorizedClientProviderBeanThenUsed() {\n\t\tthis.spring.configLocations(xml(\"providers\")).autowire();\n\t\ttestTokenExchangeGrant();\n\t}\n\n\tprivate void testTokenExchangeGrant() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(MOCK_RESPONSE_CLIENT.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt());\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"auth0\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistration.getRegistrationId())\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), this.response)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tassertThat(authorizedClient).isNotNull();\n\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture());\n\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration().getRegistrationId())\n\t\t\t.isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(authentication.getToken());\n\t}\n\n\tprivate static OAuth2AccessToken getExpiredAccessToken() {\n\t\tInstant expiresAt = Instant.now().minusSeconds(60);\n\t\tInstant issuedAt = expiresAt.minus(Duration.ofDays(1));\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"scopes\", issuedAt, expiresAt,\n\t\t\t\tnew HashSet<>(Arrays.asList(\"read\", \"write\")));\n\t}\n\n\tprivate static Jwt getJwt() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new Jwt(\"token\", issuedAt, issuedAt.plusSeconds(300),\n\t\t\t\tCollections.singletonMap(JoseHeaderNames.ALG, \"RS256\"),\n\t\t\t\tCollections.singletonMap(JwtClaimNames.SUB, \"user\"));\n\t}\n\n\tprivate static String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tpublic static List<ClientRegistration> getClientRegistrations() {\n\t\t// @formatter:off\n\t\treturn Arrays.asList(\n\t\t\t\tCommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t\t\t\t\t\t.clientId(\"google-client-id\")\n\t\t\t\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t\t.build(),\n\t\t\t\tCommonOAuth2Provider.GITHUB.getBuilder(\"github\")\n\t\t\t\t\t\t.clientId(\"github-client-id\")\n\t\t\t\t\t\t.clientSecret(\"github-client-secret\")\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t\t\t.build(),\n\t\t\t\tCommonOAuth2Provider.OKTA.getBuilder(\"okta\")\n\t\t\t\t\t\t.clientId(\"okta-client-id\")\n\t\t\t\t\t\t.clientSecret(\"okta-client-secret\")\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t\t\t\t.build(),\n\t\t\t\tClientRegistration.withRegistrationId(\"auth0\")\n\t\t\t\t\t\t.clientName(\"Auth0\")\n\t\t\t\t\t\t.clientId(\"auth0-client-id\")\n\t\t\t\t\t\t.clientSecret(\"auth0-client-secret\")\n\t\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t\t\t\t.scope(\"user.read\", \"user.write\")\n\t\t\t\t\t\t.build());\n\t\t// @formatter:on\n\t}\n\n\tpublic static AuthorizationCodeOAuth2AuthorizedClientProvider authorizationCode() {\n\t\treturn spy(new AuthorizationCodeOAuth2AuthorizedClientProvider());\n\t}\n\n\tpublic static RefreshTokenOAuth2AuthorizedClientProvider refreshToken() {\n\t\tRefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider();\n\t\tauthorizedClientProvider.setAccessTokenResponseClient(refreshTokenAccessTokenResponseClient());\n\t\treturn authorizedClientProvider;\n\t}\n\n\tpublic static OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {\n\t\treturn new MockAccessTokenResponseClient<>();\n\t}\n\n\tpublic static ClientCredentialsOAuth2AuthorizedClientProvider clientCredentials() {\n\t\tClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();\n\t\tauthorizedClientProvider.setAccessTokenResponseClient(clientCredentialsAccessTokenResponseClient());\n\t\treturn authorizedClientProvider;\n\t}\n\n\tpublic static OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {\n\t\treturn new MockAccessTokenResponseClient<>();\n\t}\n\n\tpublic static JwtBearerOAuth2AuthorizedClientProvider jwtBearer() {\n\t\tJwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();\n\t\tauthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient());\n\t\treturn authorizedClientProvider;\n\t}\n\n\tpublic static OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {\n\t\treturn new MockAccessTokenResponseClient<>();\n\t}\n\n\tpublic static TokenExchangeOAuth2AuthorizedClientProvider tokenExchange() {\n\t\tTokenExchangeOAuth2AuthorizedClientProvider authorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();\n\t\tauthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient());\n\t\treturn authorizedClientProvider;\n\t}\n\n\tpublic static OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {\n\t\treturn new MockAccessTokenResponseClient<>();\n\t}\n\n\tprivate static class MockAccessTokenResponseClient<T extends AbstractOAuth2AuthorizationGrantRequest>\n\t\t\timplements OAuth2AccessTokenResponseClient<T> {\n\n\t\t@Override\n\t\tpublic OAuth2AccessTokenResponse getTokenResponse(T authorizationGrantRequest) {\n\t\t\treturn MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.ui.Model;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link OAuth2ClientBeanDefinitionParser}.\n *\n * @author Joe Grandja\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class OAuth2ClientBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@Autowired(required = false)\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Autowired(required = false)\n\tprivate OAuth2AuthorizedClientService authorizedClientService;\n\n\t@Autowired(required = false)\n\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\t@Autowired(required = false)\n\tprivate OAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\t@Autowired(required = false)\n\tprivate RedirectStrategy authorizationRedirectStrategy;\n\n\t@Autowired(required = false)\n\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenAuthorizeThenRedirect() throws Exception {\n\t\tthis.spring.configLocations(xml(\"Minimal\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/oauth2/authorization/google\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getResponse().getRedirectedUrl()).matches(\"https://accounts.google.com/o/oauth2/v2/auth\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=google-client-id&\"\n\t\t\t\t+ \"scope=scope1%20scope2&state=.{15,}&redirect_uri=http://localhost/callback/google&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomClientRegistrationRepositoryThenCalled() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomClientRegistrationRepository\")).autowire();\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t\t\t\t.clientId(\"google-client-id\")\n\t\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t\t.redirectUri(\"http://localhost/callback/google\")\n\t\t\t\t.scope(\"scope1\", \"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(clientRegistration);\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/oauth2/authorization/google\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getResponse().getRedirectedUrl()).matches(\"https://accounts.google.com/o/oauth2/v2/auth\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=google-client-id&\"\n\t\t\t\t+ \"scope=scope1%20scope2&state=.{15,}&redirect_uri=http://localhost/callback/google&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t\tverify(this.clientRegistrationRepository).findByRegistrationId(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthorizationRequestResolverThenCalled() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomConfiguration\")).autowire();\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest(clientRegistration);\n\t\tgiven(this.authorizationRequestResolver.resolve(any())).willReturn(authorizationRequest);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/oauth2/authorization/google\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"https://accounts.google.com/o/oauth2/v2/auth?\"\n\t\t\t\t\t\t+ \"response_type=code&client_id=google-client-id&\"\n\t\t\t\t\t\t+ \"scope=scope1%20scope2&state=state&redirect_uri=http://localhost/callback/google\"));\n\t\t// @formatter:on\n\t\tverify(this.authorizationRequestResolver).resolve(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthorizationRedirectStrategyThenCalled() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomAuthorizationRedirectStrategy\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/oauth2/authorization/google\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tverify(this.authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationResponseMatchThenProcess() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomConfiguration\")).autowire();\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest(clientRegistration);\n\t\tgiven(this.authorizationRequestRepository.loadAuthorizationRequest(any())).willReturn(authorizationRequest);\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(authorizationRequest.getRedirectUri()).params(params))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(authorizationRequest.getRedirectUri()));\n\t\t// @formatter:on\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(authorizedClientCaptor.capture(), any(), any(),\n\t\t\t\tany());\n\t\tOAuth2AuthorizedClient authorizedClient = authorizedClientCaptor.getValue();\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(clientRegistration);\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void requestWhenCustomAuthorizedClientServiceThenCalled() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomAuthorizedClientService\")).autowire();\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest(clientRegistration);\n\t\tgiven(this.authorizationRequestRepository.loadAuthorizationRequest(any())).willReturn(authorizationRequest);\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(authorizationRequest.getRedirectUri()).params(params))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(authorizationRequest.getRedirectUri()));\n\t\t// @formatter:on\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(any(), any());\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void requestWhenAuthorizedClientFoundThenMethodArgumentResolved() throws Exception {\n\t\tthis.spring.configLocations(xml(\"AuthorizedClientArgumentResolver\")).autowire();\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google\");\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, \"user\",\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, request, response);\n\t\tthis.mvc.perform(get(\"/authorized-client\").session((MockHttpSession) request.getSession()))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"resolved\"));\n\t}\n\n\tprivate static OAuth2AuthorizationRequest createAuthorizationRequest(ClientRegistration clientRegistration) {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());\n\t\t// @formatter:off\n\t\treturn OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t\t.clientId(clientRegistration.getClientId()).redirectUri(clientRegistration.getRedirectUri())\n\t\t\t\t.scopes(clientRegistration.getScopes())\n\t\t\t\t.state(\"state\")\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tstatic class AuthorizedClientController {\n\n\t\t@GetMapping(\"/authorized-client\")\n\t\tString authorizedClient(Model model,\n\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"google\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\treturn (authorizedClient != null) ? \"resolved\" : \"not-resolved\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.ui.Model;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link OAuth2LoginBeanDefinitionParser}.\n *\n * @author Ruby Hartono\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class OAuth2LoginBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@Autowired(required = false)\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Autowired(required = false)\n\tprivate OAuth2AuthorizedClientService authorizedClientService;\n\n\t@Autowired(required = false)\n\tprivate ApplicationListener<AuthenticationSuccessEvent> authenticationSuccessListener;\n\n\t@Autowired(required = false)\n\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\t@Autowired(required = false)\n\tprivate OAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\t@Autowired(required = false)\n\tprivate RedirectStrategy authorizationRedirectStrategy;\n\n\t@Autowired(required = false)\n\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\t@Autowired(required = false)\n\tprivate OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService;\n\n\t@Autowired(required = false)\n\tprivate OAuth2UserService<OAuth2UserRequest, OAuth2User> oidcUserService;\n\n\t@Autowired(required = false)\n\tprivate JwtDecoderFactory<ClientRegistration> jwtDecoderFactory;\n\n\t@Autowired(required = false)\n\tprivate GrantedAuthoritiesMapper userAuthoritiesMapper;\n\n\t@Autowired(required = false)\n\tprivate AuthenticationFailureHandler authenticationFailureHandler;\n\n\t@Autowired(required = false)\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\t@Autowired(required = false)\n\tprivate RequestCache requestCache;\n\n\t@Autowired(required = false)\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy;\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Test\n\tpublic void requestLoginWhenMultiClientRegistrationThenReturnLoginPageWithClients() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiClientRegistration\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/login\"))\n\t\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getResponse().getContentAsString())\n\t\t\t.contains(\"<a href=\\\"/oauth2/authorization/google-login\\\">Google</a>\");\n\t\tassertThat(result.getResponse().getContentAsString())\n\t\t\t.contains(\"<a href=\\\"/oauth2/authorization/github-login\\\">Github</a>\");\n\t}\n\n\t// gh-5347\n\t@Test\n\tpublic void requestWhenSingleClientRegistrationThenAutoRedirect() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/oauth2/authorization/google-login\"));\n\t\t// @formatter:on\n\t\tverify(this.requestCache).saveRequest(any(), any());\n\t}\n\n\t// gh-5347\n\t@Test\n\tpublic void requestWhenSingleClientRegistrationAndRequestFaviconNotAuthenticatedThenRedirectDefaultLoginPage()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/favicon.ico\").accept(new MediaType(\"image\", \"*\")))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-6812\n\t@Test\n\tpublic void requestWhenSingleClientRegistrationAndRequestXHRNotAuthenticatedThenDoesNotRedirectForAuthorization()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"X-Requested-With\", \"XMLHttpRequest\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationRequestNotFoundThenThrowAuthenticationException() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration-WithCustomAuthenticationFailureHandler\"))\n\t\t\t.autowire();\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", \"state123\");\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/google\").params(params));\n\t\tArgumentCaptor<AuthenticationException> exceptionCaptor = ArgumentCaptor\n\t\t\t.forClass(AuthenticationException.class);\n\t\tverify(this.authenticationFailureHandler).onAuthenticationFailure(any(), any(), exceptionCaptor.capture());\n\t\tAuthenticationException exception = exceptionCaptor.getValue();\n\t\tassertThat(exception).isInstanceOf(OAuth2AuthenticationException.class);\n\t\tassertThat(((OAuth2AuthenticationException) exception).getError().getErrorCode())\n\t\t\t.isEqualTo(\"authorization_request_not_found\");\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationResponseValidThenAuthenticate() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiClientRegistration-WithCustomConfiguration\")).autowire();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, \"github-login\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User oauth2User = TestOAuth2Users.create();\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(oauth2User);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/github-login\").params(params))\n\t\t\t\t.andExpect(status().is2xxSuccessful());\n\t\t// @formatter:on\n\t\tArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());\n\t\tAuthentication authentication = authenticationCaptor.getValue();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class);\n\t}\n\n\t// gh-6009\n\t@Test\n\tpublic void requestWhenAuthorizationResponseValidThenAuthenticationSuccessEventPublished() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiClientRegistration-WithCustomConfiguration\")).autowire();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, \"github-login\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User oauth2User = TestOAuth2Users.create();\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(oauth2User);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/github-login\").params(params));\n\t\tverify(this.authenticationSuccessListener).onApplicationEvent(any(AuthenticationSuccessEvent.class));\n\t}\n\n\t@Test\n\tpublic void requestWhenOidcAuthenticationResponseValidThenJwtDecoderFactoryCalled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler\"))\n\t\t\t.autowire();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, \"google-login\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.oidcRequest()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.oidcAccessTokenResponse()\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tJwt jwt = TestJwts.user();\n\t\tgiven(this.jwtDecoderFactory.createDecoder(any())).willReturn((token) -> jwt);\n\t\tDefaultOidcUser oidcUser = TestOidcUsers.create();\n\t\tgiven(this.oidcUserService.loadUser(any())).willReturn(oidcUser);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/google-login\").params(params))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t\tverify(this.jwtDecoderFactory).createDecoder(any());\n\t\tverify(this.requestCache).getRequest(any(), any());\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\t@Test\n\tpublic void requestWhenCustomGrantedAuthoritiesMapperThenCalled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiClientRegistration-WithCustomGrantedAuthorities\")).autowire();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, \"github-login\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User oauth2User = TestOAuth2Users.create();\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(oauth2User);\n\t\tgiven(this.userAuthoritiesMapper.mapAuthorities(any()))\n\t\t\t.willReturn((Collection) AuthorityUtils.createAuthorityList(\"ROLE_OAUTH2_USER\"));\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/github-login\").params(params)).andExpect(status().is2xxSuccessful());\n\t\tArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());\n\t\tAuthentication authentication = authenticationCaptor.getValue();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class);\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.roles()\n\t\t\t.hasSize(1)\n\t\t\t.first()\n\t\t\t.isInstanceOf(SimpleGrantedAuthority.class)\n\t\t\t.hasToString(\"ROLE_OAUTH2_USER\");\n\t\t// re-setup for OIDC test\n\t\tattributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, \"google-login\");\n\t\tauthorizationRequest = TestOAuth2AuthorizationRequests.oidcRequest().attributes(attributes).build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\taccessTokenResponse = TestOAuth2AccessTokenResponses.oidcAccessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tJwt jwt = TestJwts.user();\n\t\tgiven(this.jwtDecoderFactory.createDecoder(any())).willReturn((token) -> jwt);\n\t\tDefaultOidcUser oidcUser = TestOidcUsers.create();\n\t\tgiven(this.oidcUserService.loadUser(any())).willReturn(oidcUser);\n\t\tgiven(this.userAuthoritiesMapper.mapAuthorities(any()))\n\t\t\t.willReturn((Collection) AuthorityUtils.createAuthorityList(\"ROLE_OIDC_USER\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/google-login\").params(params))\n\t\t\t\t.andExpect(status().is2xxSuccessful());\n\t\t// @formatter:on\n\t\tauthenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationSuccessHandler, times(2)).onAuthenticationSuccess(any(), any(),\n\t\t\t\tauthenticationCaptor.capture());\n\t\tauthentication = authenticationCaptor.getValue();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(OidcUser.class);\n\t\tassertThat(authentication.getAuthorities()).hasSize(1);\n\t\tassertThat(authentication.getAuthorities()).first()\n\t\t\t.isInstanceOf(SimpleGrantedAuthority.class)\n\t\t\t.hasToString(\"ROLE_OIDC_USER\");\n\t}\n\n\t// gh-5488\n\t@Test\n\tpublic void requestWhenCustomLoginProcessingUrlThenProcessAuthentication() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiClientRegistration-WithCustomLoginProcessingUrl\")).autowire();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, \"github-login\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User oauth2User = TestOAuth2Users.create();\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(oauth2User);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/login/oauth2/github-login\").params(params))\n\t\t\t\t.andExpect(status().is2xxSuccessful());\n\t\t// @formatter:on\n\t\tArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());\n\t\tAuthentication authentication = authenticationCaptor.getValue();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class);\n\t}\n\n\t// gh-5521\n\t@Test\n\tpublic void requestWhenCustomAuthorizationRequestResolverThenCalled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration-WithCustomAuthorizationRequestResolver\"))\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/oauth2/authorization/google-login\"))\n\t\t\t\t.andExpect(status().is3xxRedirection());\n\t\t// @formatter:on\n\t\tverify(this.authorizationRequestResolver).resolve(any());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthorizationRedirectStrategyThenCalled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration-WithCustomAuthorizationRedirectStrategy\"))\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/oauth2/authorization/google-login\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tverify(this.authorizationRedirectStrategy).sendRedirect(any(), any(), anyString());\n\t}\n\n\t// gh-5347\n\t@Test\n\tpublic void requestWhenMultiClientRegistrationThenRedirectDefaultLoginPage() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiClientRegistration\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomLoginPageThenRedirectCustomLoginPage() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration-WithCustomLoginPage\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/custom-login\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-6802\n\t@Test\n\tpublic void requestWhenSingleClientRegistrationAndFormLoginConfiguredThenRedirectDefaultLoginPage()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleClientRegistration-WithFormLogin\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomClientRegistrationRepositoryThenCalled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomClientRegistrationRepository\")).autowire();\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(clientRegistration);\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User oauth2User = TestOAuth2Users.create();\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(oauth2User);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/\" + clientRegistration.getRegistrationId()).params(params));\n\t\tverify(this.clientRegistrationRepository).findByRegistrationId(clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthorizedClientRepositoryThenCalled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomAuthorizedClientRepository\")).autowire();\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(clientRegistration);\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User oauth2User = TestOAuth2Users.create();\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(oauth2User);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/\" + clientRegistration.getRegistrationId()).params(params));\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthorizedClientServiceThenCalled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomAuthorizedClientService\")).autowire();\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(clientRegistration);\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User oauth2User = TestOAuth2Users.create();\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(oauth2User);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/\" + clientRegistration.getRegistrationId()).params(params));\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(any(), any());\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomSecurityContextHolderStrategyThenCalled() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomSecurityContextHolderStrategy\")).autowire();\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(clientRegistration);\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any(), any()))\n\t\t\t.willReturn(authorizationRequest);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User oauth2User = TestOAuth2Users.create();\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(oauth2User);\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"code\", \"code123\");\n\t\tparams.add(\"state\", authorizationRequest.getState());\n\t\tthis.mvc.perform(get(\"/login/oauth2/code/\" + clientRegistration.getRegistrationId()).params(params));\n\t\tverify(this.securityContextHolderStrategy, atLeastOnce()).getContext();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void requestWhenAuthorizedClientFoundThenMethodArgumentResolved() throws Exception {\n\t\tthis.spring.configLocations(xml(\"AuthorizedClientArgumentResolver\")).autowire();\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google-login\");\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, \"user\",\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, request, response);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authorized-client\").session((MockHttpSession) request.getSession()))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"resolved\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tstatic class AuthorizedClientController {\n\n\t\t@GetMapping(\"/authorized-client\")\n\t\tString authorizedClient(Model model,\n\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"google-login\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\treturn (authorizedClient != null) ? \"resolved\" : \"not-resolved\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.security.interfaces.RSAPublicKey;\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.stream.Collectors;\n\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.JWSHeader;\nimport com.nimbusds.jose.JWSObject;\nimport com.nimbusds.jose.Payload;\nimport com.nimbusds.jose.crypto.RSASSASigner;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.util.JSONObjectUtils;\nimport jakarta.servlet.http.HttpServletRequest;\nimport net.minidev.json.JSONObject;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.hamcrest.core.AllOf;\nimport org.hamcrest.core.StringContains;\nimport org.hamcrest.core.StringEndsWith;\nimport org.hamcrest.core.StringStartsWith;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mockito;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.BeanDefinitionStoreException;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;\nimport org.springframework.beans.factory.xml.ParserContext;\nimport org.springframework.beans.factory.xml.XmlReaderContext;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParser.JwtBeanDefinitionParser;\nimport org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParser.OpaqueTokenBeanDefinitionParser;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.RestOperations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.hamcrest.CoreMatchers.startsWith;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class OAuth2ResourceServerBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Autowired(required = false)\n\tMockWebServer web;\n\n\t@Test\n\tpublic void getWhenValidBearerTokenThenAcceptsRequest() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"JwtCustomSecurityContextHolderStrategy\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = this.spring.getContext()\n\t\t\t.getBean(SecurityContextHolderStrategy.class);\n\t\tverify(securityContextHolderStrategy, atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void getWhenUsingJwkSetUriThenAcceptsRequest() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WebServer\"), xml(\"JwkSetUri\")).autowire();\n\t\tmockWebServer(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenExpiredBearerTokenThenInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"Expired\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenBadJwkEndpointThen500() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(\"malformed\");\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t\t.isThrownBy(() -> this.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token)));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUnavailableJwkEndpointThen500() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WebServer\"), xml(\"JwkSetUri\")).autowire();\n\t\tthis.web.shutdown();\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t\t.isThrownBy(() -> this.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token)));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenMalformedBearerTokenThenInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwkSetUri\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer an\\\"invalid\\\"token\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"Bearer token is malformed\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenMalformedPayloadThenInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"MalformedPayload\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt: Malformed payload\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUnsignedBearerTokenThenInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwkSetUri\")).autowire();\n\t\tString token = this.token(\"Unsigned\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"Unsupported algorithm of none\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenBearerTokenBeforeNotBeforeThenInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tthis.mockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"TooEarly\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenBearerTokenInTwoPlacesThenInvalidRequest() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwkSetUri\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer token\").param(\"access_token\", \"token\"))\n\t\t\t\t.andExpect(status().isBadRequest())\n\t\t\t\t.andExpect(invalidRequestHeader(\"Found multiple bearer tokens in the request\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenBearerTokenInTwoParametersThenInvalidRequest() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwkSetUri\")).autowire();\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>();\n\t\tparams.add(\"access_token\", \"token1\");\n\t\tparams.add(\"access_token\", \"token2\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").params(params))\n\t\t\t\t.andExpect(status().isBadRequest())\n\t\t\t\t.andExpect(invalidRequestHeader(\"Found multiple bearer tokens in the request\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenBearerTokenAsFormParameterThenIgnoresToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwkSetUri\")).autowire();\n\t\tthis.mvc.perform(post(\"/\") // engage csrf\n\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t.param(\"access_token\", \"token\"))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Bearer\")); // different\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// from\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// DSL\n\t}\n\n\t@Test\n\tpublic void getWhenNoBearerTokenThenUnauthorized() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwkSetUri\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Bearer resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenSufficientlyScopedBearerTokenThenAcceptsRequest() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidMessageReadScope\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsufficientScopeThenInsufficientScopeError() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(insufficientScopeHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsufficientScpThenInsufficientScopeError() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidMessageWriteScp\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(insufficientScopeHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenAuthorizationServerHasNoMatchingKeyThenInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Empty\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenAuthorizationServerHasMultipleMatchingKeysThenOk() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"TwoKeys\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenKeyMatchesByKidThenOk() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"TwoKeys\"));\n\t\tString token = this.token(\"Kid\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenValidBearerTokenAndNoCsrfTokenThenOk() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/authenticated\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenNoBearerTokenThenCsrfDenies() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwkSetUri\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/authenticated\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t// different from DSL\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, \"Bearer\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenExpiredBearerTokenAndNoCsrfThenInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"Expired\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/authenticated\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"An error occurred while attempting to decode the Jwt\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenJwtThenSessionIsNotCreated() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenIntrospectionThenSessionIsNotCreated() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WebServer\"), xml(\"IntrospectionUri\")).autowire();\n\t\tmockWebServer(json(\"Active\"));\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isNotFound())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenNoBearerTokenThenSessionIsCreated() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwkSetUri\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionManagementConfiguredThenUses() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"AlwaysSessionCreation\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void getWhenCustomBearerTokenResolverThenUses() throws Exception {\n\t\tthis.spring.configLocations(xml(\"MockBearerTokenResolver\"), xml(\"MockJwtDecoder\"), xml(\"BearerTokenResolver\"))\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(\"token\")).willReturn(TestJwts.jwt().build());\n\t\tBearerTokenResolver bearerTokenResolver = this.spring.getContext().getBean(BearerTokenResolver.class);\n\t\tgiven(bearerTokenResolver.resolve(any(HttpServletRequest.class))).willReturn(\"token\");\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isNotFound());\n\t\tverify(decoder).decode(\"token\");\n\t\tverify(bearerTokenResolver).resolve(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthenticationConverterThenUses() throws Exception {\n\t\tthis.spring\n\t\t\t.configLocations(xml(\"MockAuthenticationConverter\"), xml(\"MockJwtDecoder\"), xml(\"AuthenticationConverter\"))\n\t\t\t.autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(\"token\")).willReturn(TestJwts.jwt().build());\n\t\tAuthenticationConverter authenticationConverter = this.spring.getContext()\n\t\t\t.getBean(AuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new BearerTokenAuthenticationToken(\"token\"));\n\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isNotFound());\n\n\t\tverify(decoder).decode(\"token\");\n\t\tverify(authenticationConverter).convert(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"AllowBearerTokenInBody\")).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(TestJwts.jwt().build());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(post(\"/authenticated\").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).param(\"access_token\", \"token\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenBearerTokenResolverAllowsQueryParameterThenEitherHeaderOrQueryParameterIsAccepted()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"AllowBearerTokenInQuery\")).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(TestJwts.jwt().build());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"/authenticated\").param(\"access_token\", \"token\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tverify(decoder, times(2)).decode(\"token\");\n\t}\n\n\t@Test\n\tpublic void requestWhenBearerTokenResolverAllowsRequestBodyAndRequestContainsTwoTokensThenInvalidRequest()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"AllowBearerTokenInBody\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/authenticated\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.param(\"access_token\", \"token\")\n\t\t\t\t.header(\"Authorization\", \"Bearer token\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isBadRequest())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"invalid_request\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenBearerTokenResolverAllowsQueryParameterAndRequestContainsTwoTokensThenInvalidRequest()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"AllowBearerTokenInQuery\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/authenticated\")\n\t\t\t\t.header(\"Authorization\", \"Bearer token\")\n\t\t\t\t.param(\"access_token\", \"token\");\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isBadRequest())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"invalid_request\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomJwtDecoderThenUsed() throws Exception {\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"Jwt\")).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(TestJwts.jwt().build());\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t.andExpect(status().isNotFound());\n\t\tverify(decoder).decode(\"token\");\n\t}\n\n\t@Test\n\tpublic void configureWhenDecoderAndJwkSetUriThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"JwtDecoderAndJwkSetUri\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenAuthenticationConverterAndJwkSetUriThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(\n\t\t\t\t() -> this.spring.configLocations(xml(\"AuthenticationConverterAndBearerTokenResolver\")).autowire());\n\t}\n\n\t@Test\n\tpublic void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"AuthenticationEntryPoint\")).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tMockito.when(decoder.decode(anyString())).thenThrow(BadJwtException.class);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer invalid_token\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer realm=\\\"myRealm\\\"\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenRealmNameConfiguredThenUsesOnAccessDenied() throws Exception {\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"AccessDeniedHandler\")).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willReturn(TestJwts.jwt().build());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer insufficiently_scoped\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer realm=\\\"myRealm\\\"\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomJwtValidatorFailsThenCorrespondingErrorMessage() throws Exception {\n\t\tthis.spring.configLocations(xml(\"MockJwtValidator\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = this.spring.getContext().getBean(OAuth2TokenValidator.class);\n\t\tOAuth2Error error = new OAuth2Error(\"custom-error\", \"custom-description\", \"custom-uri\");\n\t\tgiven(jwtValidator.validate(any(Jwt.class))).willReturn(OAuth2TokenValidatorResult.failure(error));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"custom-description\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenClockSkewSetThenTimestampWindowRelaxedAccordingly() throws Exception {\n\t\tthis.spring.configLocations(xml(\"UnexpiredJwtClockSkew\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ExpiresAt4687177990\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenClockSkewSetButJwtStillTooLateThenReportsExpired() throws Exception {\n\t\tthis.spring.configLocations(xml(\"ExpiredJwtClockSkew\"), xml(\"Jwt\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ExpiresAt4687177990\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"Jwt expired at\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenJwtAuthenticationConverterThenUsed() throws Exception {\n\t\tthis.spring\n\t\t\t.configLocations(xml(\"MockJwtDecoder\"), xml(\"MockJwtAuthenticationConverter\"),\n\t\t\t\t\txml(\"JwtAuthenticationConverter\"))\n\t\t\t.autowire();\n\t\tConverter<Jwt, JwtAuthenticationToken> jwtAuthenticationConverter = (Converter<Jwt, JwtAuthenticationToken>) this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(\"jwtAuthenticationConverter\");\n\t\tgiven(jwtAuthenticationConverter.convert(any(Jwt.class)))\n\t\t\t.willReturn(new JwtAuthenticationToken(TestJwts.jwt().build(), Collections.emptyList()));\n\t\tJwtDecoder jwtDecoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(anyString())).willReturn(TestJwts.jwt().build());\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tverify(jwtAuthenticationConverter).convert(any(Jwt.class));\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingPublicKeyAndValidTokenThenAuthenticates() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SingleKey\"), xml(\"Jwt\")).autowire();\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingPublicKeyAndSignatureFailsThenReturnsInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SingleKey\"), xml(\"Jwt\")).autowire();\n\t\tString token = this.token(\"WrongSignature\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(invalidTokenHeader(\"signature\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenUsingPublicKeyAlgorithmDoesNotMatchThenReturnsInvalidToken() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SingleKey\"), xml(\"Jwt\")).autowire();\n\t\tString token = this.token(\"WrongAlgorithm\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(invalidTokenHeader(\"algorithm\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenIntrospectingThenOk() throws Exception {\n\t\tthis.spring.configLocations(xml(\"OpaqueTokenRestOperations\"), xml(\"OpaqueToken\")).autowire();\n\t\tmockJsonRestOperations(json(\"Active\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenIntrospectingWithAuthenticationConverterThenUses() throws Exception {\n\t\tthis.spring.configLocations(xml(\"OpaqueTokenRestOperations\"), xml(\"OpaqueTokenAndAuthenticationConverter\"))\n\t\t\t.autowire();\n\t\tmockJsonRestOperations(json(\"Active\"));\n\t\tOpaqueTokenAuthenticationConverter converter = bean(OpaqueTokenAuthenticationConverter.class);\n\t\tgiven(converter.convert(any(), any())).willReturn(new TestingAuthenticationToken(\"user\", \"pass\", \"app\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tverify(converter).convert(any(), any());\n\t}\n\n\t@Test\n\tpublic void getWhenIntrospectionFailsThenUnauthorized() throws Exception {\n\t\tthis.spring.configLocations(xml(\"OpaqueTokenRestOperations\"), xml(\"OpaqueToken\")).autowire();\n\t\tmockJsonRestOperations(json(\"Inactive\"));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/\")\n\t\t\t\t.header(\"Authorization\", \"Bearer token\");\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"Provided token isn't active\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenIntrospectionLacksScopeThenForbidden() throws Exception {\n\t\tthis.spring.configLocations(xml(\"OpaqueTokenRestOperations\"), xml(\"OpaqueToken\")).autowire();\n\t\tmockJsonRestOperations(json(\"ActiveNoScopes\"));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/requires-read-scope\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isForbidden())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"scope\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenOnlyIntrospectionUrlThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"OpaqueTokenHalfConfigured\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenIntrospectorAndIntrospectionUriThenError() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"OpaqueTokenAndIntrospectionUri\")).autowire());\n\t}\n\n\t@Test\n\tpublic void getWhenAuthenticationManagerResolverThenUses() throws Exception {\n\t\tthis.spring.configLocations(xml(\"AuthenticationManagerResolver\")).autowire();\n\t\tAuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver = this.spring.getContext()\n\t\t\t.getBean(AuthenticationManagerResolver.class);\n\t\tgiven(authenticationManagerResolver.resolve(any(HttpServletRequest.class))).willReturn(\n\t\t\t\t(authentication) -> new JwtAuthenticationToken(TestJwts.jwt().build(), Collections.emptyList()));\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tverify(authenticationManagerResolver).resolve(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void getWhenMultipleIssuersThenUsesIssuerClaimToDifferentiate() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WebServer\"), xml(\"MultipleIssuers\")).autowire();\n\t\tMockWebServer server = this.spring.getContext().getBean(MockWebServer.class);\n\t\tString metadata = \"{\\n\" + \"    \\\"issuer\\\": \\\"%s\\\", \\n\" + \"    \\\"jwks_uri\\\": \\\"%s/.well-known/jwks.json\\\" \\n\"\n\t\t\t\t+ \"}\";\n\t\tString jwkSet = jwkSet();\n\t\tString issuerOne = server.url(\"/issuerOne\").toString();\n\t\tString issuerTwo = server.url(\"/issuerTwo\").toString();\n\t\tString issuerThree = server.url(\"/issuerThree\").toString();\n\t\tString jwtOne = jwtFromIssuer(issuerOne);\n\t\tString jwtTwo = jwtFromIssuer(issuerTwo);\n\t\tString jwtThree = jwtFromIssuer(issuerThree);\n\t\tmockWebServer(String.format(metadata, issuerOne, issuerOne));\n\t\tmockWebServer(jwkSet);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer \" + jwtOne))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tmockWebServer(String.format(metadata, issuerTwo, issuerTwo));\n\t\tmockWebServer(jwkSet);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer \" + jwtTwo))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t\tmockWebServer(String.format(metadata, issuerThree, issuerThree));\n\t\tmockWebServer(jwkSet);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer \" + jwtThree))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(invalidTokenHeader(\"Invalid issuer\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenBasicAndResourceServerEntryPointsThenBearerTokenPresides() throws Exception {\n\t\t// different from DSL\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"BasicAndResourceServer\")).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willThrow(BadJwtException.class);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").with(httpBasic(\"some\", \"user\")))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Basic\")));\n\t\tthis.mvc.perform(get(\"/authenticated\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer\")));\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer invalid_token\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenFormLoginAndResourceServerEntryPointsThenSessionCreatedByRequest() throws Exception {\n\t\t// different from DSL\n\t\tthis.spring.configLocations(xml(\"MockJwtDecoder\"), xml(\"FormAndResourceServer\")).autowire();\n\t\tJwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);\n\t\tgiven(decoder.decode(anyString())).willThrow(BadJwtException.class);\n\t\tMvcResult result = this.mvc.perform(get(\"/authenticated\")).andExpect(status().isUnauthorized()).andReturn();\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t\t// @formatter:off\n\t\tresult = this.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer token\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void getWhenAlsoUsingHttpBasicThenCorrectProviderEngages() throws Exception {\n\t\tthis.spring.configLocations(xml(\"JwtRestOperations\"), xml(\"BasicAndResourceServer\")).autowire();\n\t\tmockJwksRestOperations(jwks(\"Default\"));\n\t\tString token = this.token(\"ValidNoScopes\");\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").header(\"Authorization\", \"Bearer \" + token))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(get(\"/authenticated\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isNotFound());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configuredWhenMissingJwtAuthenticationProviderThenWiringException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"Jwtless\")).autowire())\n\t\t\t.withMessageContaining(\"Please select one\");\n\t}\n\n\t@Test\n\tpublic void configureWhenMissingJwkSetUriThenWiringException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"JwtHalfConfigured\")).autowire())\n\t\t\t.withMessageContaining(\"Please specify either\");\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingBothAuthenticationManagerResolverAndJwtThenException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.spring.configLocations(xml(\"AuthenticationManagerResolverPlusOtherConfig\")).autowire())\n\t\t\t.withMessageContaining(\"authentication-manager-resolver-ref\");\n\t}\n\n\t@Test\n\tpublic void validateConfigurationWhenMoreThanOneResourceServerModeThenError() {\n\t\tOAuth2ResourceServerBeanDefinitionParser parser = new OAuth2ResourceServerBeanDefinitionParser(null, null, null,\n\t\t\t\tnull, null, null);\n\t\tElement element = mock(Element.class);\n\t\tgiven(element.hasAttribute(OAuth2ResourceServerBeanDefinitionParser.AUTHENTICATION_MANAGER_RESOLVER_REF))\n\t\t\t.willReturn(true);\n\t\tElement child = mock(Element.class);\n\t\tParserContext pc = new ParserContext(mock(XmlReaderContext.class), mock(BeanDefinitionParserDelegate.class));\n\t\tparser.validateConfiguration(element, child, null, pc);\n\t\tverify(pc.getReaderContext()).error(anyString(), eq(element));\n\t\treset(pc.getReaderContext());\n\t\tparser.validateConfiguration(element, null, child, pc);\n\t\tverify(pc.getReaderContext()).error(anyString(), eq(element));\n\t}\n\n\t@Test\n\tpublic void validateConfigurationWhenNoResourceServerModeThenError() {\n\t\tOAuth2ResourceServerBeanDefinitionParser parser = new OAuth2ResourceServerBeanDefinitionParser(null, null, null,\n\t\t\t\tnull, null, null);\n\t\tElement element = mock(Element.class);\n\t\tgiven(element.hasAttribute(OAuth2ResourceServerBeanDefinitionParser.AUTHENTICATION_MANAGER_RESOLVER_REF))\n\t\t\t.willReturn(false);\n\t\tParserContext pc = new ParserContext(mock(XmlReaderContext.class), mock(BeanDefinitionParserDelegate.class));\n\t\tparser.validateConfiguration(element, null, null, pc);\n\t\tverify(pc.getReaderContext()).error(anyString(), eq(element));\n\t}\n\n\t@Test\n\tpublic void validateConfigurationWhenBothJwtAttributesThenError() {\n\t\tJwtBeanDefinitionParser parser = new JwtBeanDefinitionParser();\n\t\tElement element = mock(Element.class);\n\t\tgiven(element.hasAttribute(JwtBeanDefinitionParser.JWK_SET_URI)).willReturn(true);\n\t\tgiven(element.hasAttribute(JwtBeanDefinitionParser.DECODER_REF)).willReturn(true);\n\t\tParserContext pc = new ParserContext(mock(XmlReaderContext.class), mock(BeanDefinitionParserDelegate.class));\n\t\tparser.validateConfiguration(element, pc);\n\t\tverify(pc.getReaderContext()).error(anyString(), eq(element));\n\t}\n\n\t@Test\n\tpublic void validateConfigurationWhenNoJwtAttributesThenError() {\n\t\tJwtBeanDefinitionParser parser = new JwtBeanDefinitionParser();\n\t\tElement element = mock(Element.class);\n\t\tgiven(element.hasAttribute(JwtBeanDefinitionParser.JWK_SET_URI)).willReturn(false);\n\t\tgiven(element.hasAttribute(JwtBeanDefinitionParser.DECODER_REF)).willReturn(false);\n\t\tParserContext pc = new ParserContext(mock(XmlReaderContext.class), mock(BeanDefinitionParserDelegate.class));\n\t\tparser.validateConfiguration(element, pc);\n\t\tverify(pc.getReaderContext()).error(anyString(), eq(element));\n\t}\n\n\t@Test\n\tpublic void validateConfigurationWhenBothOpaqueTokenModesThenError() {\n\t\tOpaqueTokenBeanDefinitionParser parser = new OpaqueTokenBeanDefinitionParser();\n\t\tElement element = mock(Element.class);\n\t\tgiven(element.hasAttribute(OpaqueTokenBeanDefinitionParser.INTROSPECTION_URI)).willReturn(true);\n\t\tgiven(element.hasAttribute(OpaqueTokenBeanDefinitionParser.INTROSPECTOR_REF)).willReturn(true);\n\t\tParserContext pc = new ParserContext(mock(XmlReaderContext.class), mock(BeanDefinitionParserDelegate.class));\n\t\tparser.validateConfiguration(element, pc);\n\t\tverify(pc.getReaderContext()).error(anyString(), eq(element));\n\t}\n\n\t@Test\n\tpublic void validateConfigurationWhenNoOpaqueTokenModeThenError() {\n\t\tOpaqueTokenBeanDefinitionParser parser = new OpaqueTokenBeanDefinitionParser();\n\t\tElement element = mock(Element.class);\n\t\tgiven(element.hasAttribute(OpaqueTokenBeanDefinitionParser.INTROSPECTION_URI)).willReturn(false);\n\t\tgiven(element.hasAttribute(OpaqueTokenBeanDefinitionParser.INTROSPECTOR_REF)).willReturn(false);\n\t\tParserContext pc = new ParserContext(mock(XmlReaderContext.class), mock(BeanDefinitionParserDelegate.class));\n\t\tparser.validateConfiguration(element, pc);\n\t\tverify(pc.getReaderContext()).error(anyString(), eq(element));\n\t}\n\n\tprivate static ResultMatcher invalidRequestHeader(String message) {\n\t\treturn header().string(HttpHeaders.WWW_AUTHENTICATE,\n\t\t\t\tAllOf.allOf(new StringStartsWith(\"Bearer \" + \"error=\\\"invalid_request\\\", \" + \"error_description=\\\"\"),\n\t\t\t\t\t\tnew StringContains(message),\n\t\t\t\t\t\tnew StringContains(\", \" + \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\"),\n\t\t\t\t\t\tnew StringEndsWith(\n\t\t\t\t\t\t\t\t\", \" + \"resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\")));\n\t}\n\n\tprivate static ResultMatcher invalidTokenHeader(String message) {\n\t\treturn header().string(HttpHeaders.WWW_AUTHENTICATE,\n\t\t\t\tAllOf.allOf(new StringStartsWith(\"Bearer \" + \"error=\\\"invalid_token\\\", \" + \"error_description=\\\"\"),\n\t\t\t\t\t\tnew StringContains(message),\n\t\t\t\t\t\tnew StringContains(\", \" + \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\"),\n\t\t\t\t\t\tnew StringEndsWith(\n\t\t\t\t\t\t\t\t\", \" + \"resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\")));\n\t}\n\n\tprivate static ResultMatcher insufficientScopeHeader() {\n\t\treturn header().string(HttpHeaders.WWW_AUTHENTICATE, \"Bearer \" + \"error=\\\"insufficient_scope\\\"\"\n\t\t\t\t+ \", error_description=\\\"The request requires higher privileges than provided by the access token.\\\"\"\n\t\t\t\t+ \", error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\");\n\t}\n\n\tprivate String jwkSet() {\n\t\treturn new JWKSet(new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyID(\"1\").build()).toString();\n\t}\n\n\tprivate String jwtFromIssuer(String issuer) throws Exception {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(JwtClaimNames.ISS, issuer);\n\t\tclaims.put(JwtClaimNames.SUB, \"test-subject\");\n\t\tclaims.put(\"scope\", \"message:read\");\n\t\tJWSObject jws = new JWSObject(new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(\"1\").build(),\n\t\t\t\tnew Payload(new JSONObject(claims)));\n\t\tjws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));\n\t\treturn jws.serialize();\n\t}\n\n\tprivate void mockWebServer(String response) {\n\t\tthis.web.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.setBody(response));\n\t}\n\n\tprivate void mockJwksRestOperations(String response) {\n\t\tRestOperations rest = this.spring.getContext().getBean(RestOperations.class);\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\tResponseEntity<String> entity = new ResponseEntity<>(response, headers, HttpStatus.OK);\n\t\tgiven(rest.exchange(any(RequestEntity.class), eq(String.class))).willReturn(entity);\n\t}\n\n\tprivate void mockJsonRestOperations(String response) {\n\t\ttry {\n\t\t\tRestOperations rest = this.spring.getContext().getBean(RestOperations.class);\n\t\t\tHttpHeaders headers = new HttpHeaders();\n\t\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\t\tResponseEntity<Map<String, Object>> entity = new ResponseEntity<>(JSONObjectUtils.parse(response), headers,\n\t\t\t\t\tHttpStatus.OK);\n\t\t\tgiven(rest.exchange(any(RequestEntity.class), eq(new ParameterizedTypeReference<Map<String, Object>>() {\n\t\t\t}))).willReturn(entity);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate String json(String name) throws IOException {\n\t\treturn resource(name + \".json\");\n\t}\n\n\tprivate String jwks(String name) throws IOException {\n\t\treturn resource(name + \".jwks\");\n\t}\n\n\tprivate String token(String name) throws IOException {\n\t\treturn resource(name + \".token\");\n\t}\n\n\tprivate String resource(String suffix) throws IOException {\n\t\tString name = this.getClass().getSimpleName() + \"-\" + suffix;\n\t\tClassPathResource resource = new ClassPathResource(name, this.getClass());\n\t\ttry (BufferedReader reader = new BufferedReader(new FileReader(resource.getFile()))) {\n\t\t\treturn reader.lines().collect(Collectors.joining());\n\t\t}\n\t}\n\n\tprivate <T> T bean(Class<T> beanClass) {\n\t\treturn this.spring.getContext().getBean(beanClass);\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tstatic class JwtDecoderFactoryBean implements FactoryBean<JwtDecoder> {\n\n\t\tprivate RestOperations rest;\n\n\t\tprivate RSAPublicKey key;\n\n\t\tprivate OAuth2TokenValidator<Jwt> jwtValidator;\n\n\t\t@Override\n\t\tpublic JwtDecoder getObject() {\n\t\t\tNimbusJwtDecoder decoder;\n\t\t\tif (this.key != null) {\n\t\t\t\tdecoder = NimbusJwtDecoder.withPublicKey(this.key).build();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdecoder = NimbusJwtDecoder.withJwkSetUri(\"https://idp.example.org\").restOperations(this.rest).build();\n\t\t\t}\n\t\t\tif (this.jwtValidator != null) {\n\t\t\t\tdecoder.setJwtValidator(this.jwtValidator);\n\t\t\t}\n\t\t\treturn decoder;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn JwtDecoder.class;\n\t\t}\n\n\t\tpublic void setJwtValidator(OAuth2TokenValidator<Jwt> jwtValidator) {\n\t\t\tthis.jwtValidator = jwtValidator;\n\t\t}\n\n\t\tpublic void setKey(RSAPublicKey key) {\n\t\t\tthis.key = key;\n\t\t}\n\n\t\tpublic void setRest(RestOperations rest) {\n\t\t\tthis.rest = rest;\n\t\t}\n\n\t}\n\n\tstatic class OpaqueTokenIntrospectorFactoryBean implements FactoryBean<OpaqueTokenIntrospector> {\n\n\t\tprivate RestOperations rest;\n\n\t\t@Override\n\t\tpublic OpaqueTokenIntrospector getObject() throws Exception {\n\t\t\treturn new SpringOpaqueTokenIntrospector(\"https://idp.example.org\", this.rest);\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn OpaqueTokenIntrospector.class;\n\t\t}\n\n\t\tpublic void setRest(RestOperations rest) {\n\t\t\tthis.rest = rest;\n\t\t}\n\n\t}\n\n\tstatic class MockWebServerFactoryBean implements FactoryBean<MockWebServer>, DisposableBean {\n\n\t\tprivate final MockWebServer web = new MockWebServer();\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tthis.web.shutdown();\n\t\t}\n\n\t\t@Override\n\t\tpublic MockWebServer getObject() {\n\t\t\treturn this.web;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn MockWebServer.class;\n\t\t}\n\n\t}\n\n\tstatic class MockWebServerPropertiesFactoryBean implements FactoryBean<Properties>, DisposableBean {\n\n\t\tMockWebServer web;\n\n\t\tMockWebServerPropertiesFactoryBean(MockWebServer web) {\n\t\t\tthis.web = web;\n\t\t}\n\n\t\t@Override\n\t\tpublic Properties getObject() {\n\t\t\tProperties p = new Properties();\n\t\t\tp.setProperty(\"jwk-set-uri\", this.web.url(\"\").toString());\n\t\t\tp.setProperty(\"introspection-uri\", this.web.url(\"\").toString());\n\t\t\tp.setProperty(\"issuer-one\", this.web.url(\"issuerOne\").toString());\n\t\t\tp.setProperty(\"issuer-two\", this.web.url(\"issuerTwo\").toString());\n\t\t\treturn p;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn Properties.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tthis.web.shutdown();\n\t\t}\n\n\t}\n\n\tstatic class ClockFactoryBean implements FactoryBean<Clock> {\n\n\t\tClock clock;\n\n\t\t@Override\n\t\tpublic Clock getObject() {\n\t\t\treturn this.clock;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getObjectType() {\n\t\t\treturn Clock.class;\n\t\t}\n\n\t\tpublic void setMillis(long millis) {\n\t\t\tthis.clock = Clock.fixed(Instant.ofEpochMilli(millis), ZoneId.systemDefault());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class PlaceHolderAndELConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/PlaceHolderAndELConfigTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void getWhenUsingPlaceholderThenUnsecuredPatternCorrectlyConfigured() throws Exception {\n\t\tSystem.setProperty(\"pattern.nofilters\", \"/unsecured\");\n\t\tthis.spring.configLocations(this.xml(\"UnsecuredPattern\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/unsecured\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-1201\n\t */\n\t@Test\n\tpublic void loginWhenUsingPlaceholderThenInterceptUrlsAndFormLoginWorks() throws Exception {\n\t\tSystem.setProperty(\"secure.Url\", \"/secured\");\n\t\tSystem.setProperty(\"secure.role\", \"ROLE_NUNYA\");\n\t\tSystem.setProperty(\"login.page\", \"/loginPage\");\n\t\tSystem.setProperty(\"default.target\", \"/defaultTarget\");\n\t\tSystem.setProperty(\"auth.failure\", \"/authFailure\");\n\t\tthis.spring.configLocations(this.xml(\"InterceptUrlAndFormLogin\")).autowire();\n\t\t// login-page setting\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/secured\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/loginPage\"));\n\t\t// login-processing-url setting\n\t\t// default-target-url setting\n\t\tthis.mvc.perform(post(\"/loginPage\").param(\"username\", \"user\").param(\"password\", \"password\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/defaultTarget\"));\n\t\t// authentication-failure-url setting\n\t\tthis.mvc.perform(post(\"/loginPage\").param(\"username\", \"user\").param(\"password\", \"wrong\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/authFailure\"));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-1309\n\t */\n\t@Test\n\tpublic void loginWhenUsingSpELThenInterceptUrlsAndFormLoginWorks() throws Exception {\n\t\tSystem.setProperty(\"secure.url\", \"/secured\");\n\t\tSystem.setProperty(\"secure.role\", \"ROLE_NUNYA\");\n\t\tSystem.setProperty(\"login.page\", \"/loginPage\");\n\t\tSystem.setProperty(\"default.target\", \"/defaultTarget\");\n\t\tSystem.setProperty(\"auth.failure\", \"/authFailure\");\n\t\tthis.spring.configLocations(this.xml(\"InterceptUrlAndFormLoginWithSpEL\")).autowire();\n\t\t// login-page setting\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/secured\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/loginPage\"));\n\t\t// login-processing-url setting\n\t\t// default-target-url setting\n\t\tthis.mvc.perform(post(\"/loginPage\").param(\"username\", \"user\").param(\"password\", \"password\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/defaultTarget\"));\n\t\t// authentication-failure-url setting\n\t\tthis.mvc.perform(post(\"/loginPage\").param(\"username\", \"user\").param(\"password\", \"wrong\"))\n\t\t\t\t.andExpect(redirectedUrl(\"/authFailure\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void requestWhenUsingPlaceholderOrSpELThenPortMapperWorks() throws Exception {\n\t\tSystem.setProperty(\"http\", \"9080\");\n\t\tSystem.setProperty(\"https\", \"9443\");\n\t\tthis.spring.configLocations(this.xml(\"PortMapping\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"http://localhost:9080/secured\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost:9443/secured\"));\n\t\tthis.mvc.perform(get(\"https://localhost:9443/unsecured\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"http://localhost:9080/unsecured\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void requestWhenUsingPlaceholderThenRequiresChannelWorks() throws Exception {\n\t\tSystem.setProperty(\"secure.url\", \"/secured\");\n\t\tSystem.setProperty(\"required.channel\", \"https\");\n\t\tthis.spring.configLocations(this.xml(\"RequiresChannel\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"http://localhost/secured\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"https://localhost/secured\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void requestWhenUsingPlaceholderThenAccessDeniedPageWorks() throws Exception {\n\t\tSystem.setProperty(\"accessDenied\", \"/go-away\");\n\t\tthis.spring.configLocations(this.xml(\"AccessDeniedPage\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/secured\"))\n\t\t\t\t.andExpect(forwardedUrl(\"/go-away\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void requestWhenUsingSpELThenAccessDeniedPageWorks() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"AccessDeniedPageWithSpEL\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/secured\"))\n\t\t\t\t.andExpect(forwardedUrl(\"/go-away\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tstatic class SimpleController {\n\n\t\t@GetMapping(\"/unsecured\")\n\t\tString unsecured() {\n\t\t\treturn \"unsecured\";\n\t\t}\n\n\t\t@GetMapping(\"/secured\")\n\t\tString secured() {\n\t\t\treturn \"secured\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/RememberMeConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Collections;\n\nimport jakarta.servlet.http.Cookie;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.FatalBeanException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.security.TestDataSource;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultActions;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n * @author Oliver Becker\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class RememberMeConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/RememberMeConfigTests\";\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void requestWithRememberMeWhenUsingCustomTokenRepositoryThenAutomaticallyReauthenticates() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WithTokenRepository\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = rememberAuthentication(\"user\", \"password\")\n\t\t\t\t.andExpect(cookie().secure(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, false))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tJdbcTemplate template = this.spring.getContext().getBean(JdbcTemplate.class);\n\t\tint count = template.queryForObject(\"select count(*) from persistent_logins\", int.class);\n\t\tassertThat(count).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void requestWithRememberMeWhenUsingCustomDataSourceThenAutomaticallyReauthenticates() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WithDataSource\")).autowire();\n\t\tTestDataSource dataSource = this.spring.getContext().getBean(TestDataSource.class);\n\t\tJdbcTemplate template = new JdbcTemplate(dataSource);\n\t\ttemplate.execute(JdbcTokenRepositoryImpl.CREATE_TABLE_SQL);\n\t\t// @formatter:off\n\t\tMvcResult result = rememberAuthentication(\"user\", \"password\")\n\t\t\t\t.andExpect(cookie().secure(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, false))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tint count = template.queryForObject(\"select count(*) from persistent_logins\", int.class);\n\t\tassertThat(count).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void requestWithRememberMeWhenUsingAuthenticationSuccessHandlerThenInvokesHandler() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WithAuthenticationSuccessHandler\")).autowire();\n\t\tTestDataSource dataSource = this.spring.getContext().getBean(TestDataSource.class);\n\t\tJdbcTemplate template = new JdbcTemplate(dataSource);\n\t\ttemplate.execute(JdbcTokenRepositoryImpl.CREATE_TABLE_SQL);\n\t\t// @formatter:off\n\t\tMvcResult result = rememberAuthentication(\"user\", \"password\")\n\t\t\t\t.andExpect(cookie().secure(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, false))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(redirectedUrl(\"/target\"));\n\t\t// @formatter:on\n\t\tint count = template.queryForObject(\"select count(*) from persistent_logins\", int.class);\n\t\tassertThat(count).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void requestWithRememberMeWhenUsingCustomRememberMeServicesThenAuthenticates() throws Exception {\n\t\t// SEC-1281 - using key with external services\n\t\tthis.spring.configLocations(xml(\"WithServicesRef\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = rememberAuthentication(\"user\", \"password\")\n\t\t\t\t.andExpect(cookie().secure(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, false))\n\t\t\t\t.andExpect(cookie().maxAge(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, 5000))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// SEC-909\n\t\tthis.mvc.perform(post(\"/logout\").cookie(cookie).with(csrf()))\n\t\t\t\t.andExpect(cookie().maxAge(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, 0))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingRememberMeDefaultsThenCookieIsCancelled() throws Exception {\n\t\tthis.spring.configLocations(xml(\"DefaultConfig\")).autowire();\n\t\tMvcResult result = rememberAuthentication(\"user\", \"password\").andReturn();\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/logout\").cookie(cookie).with(csrf()))\n\t\t\t\t.andExpect(cookie().maxAge(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, 0));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWithRememberMeWhenTokenValidityIsConfiguredThenCookieReflectsCorrectExpiration()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"TokenValidity\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = rememberAuthentication(\"user\", \"password\")\n\t\t\t\t.andExpect(cookie().maxAge(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, 10000))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWithRememberMeWhenTokenValidityIsNegativeThenCookieReflectsCorrectExpiration() throws Exception {\n\t\tthis.spring.configLocations(xml(\"NegativeTokenValidity\")).autowire();\n\t\t// @formatter:off\n\t\trememberAuthentication(\"user\", \"password\")\n\t\t\t\t.andExpect(cookie().maxAge(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, -1));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDataSourceAndANegativeTokenValidityThenThrowsWiringException() {\n\t\tassertThatExceptionOfType(FatalBeanException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"NegativeTokenValidityWithDataSource\")).autowire());\n\t}\n\n\t@Test\n\tpublic void requestWithRememberMeWhenTokenValidityIsResolvedByPropertyPlaceholderThenCookieReflectsCorrectExpiration()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"Sec2165\")).autowire();\n\t\trememberAuthentication(\"user\", \"password\")\n\t\t\t.andExpect(cookie().maxAge(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, 30));\n\t}\n\n\t@Test\n\tpublic void requestWithRememberMeWhenUseSecureCookieIsTrueThenCookieIsSecure() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SecureCookie\")).autowire();\n\t\trememberAuthentication(\"user\", \"password\")\n\t\t\t.andExpect(cookie().secure(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, true));\n\t}\n\n\t/**\n\t * SEC-1827\n\t */\n\t@Test\n\tpublic void requestWithRememberMeWhenUseSecureCookieIsFalseThenCookieIsNotSecure() throws Exception {\n\t\tthis.spring.configLocations(xml(\"Sec1827\")).autowire();\n\t\trememberAuthentication(\"user\", \"password\")\n\t\t\t.andExpect(cookie().secure(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, false));\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingPersistentTokenRepositoryAndANegativeTokenValidityThenThrowsWiringException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(\n\t\t\t\t() -> this.spring.configLocations(xml(\"NegativeTokenValidityWithPersistentRepository\")).autowire());\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WithSecurityContextHolderStrategy\")).autowire();\n\t\tMvcResult result = rememberAuthentication(\"user\", \"password\").andReturn();\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tverify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void requestWithRememberMeWhenUsingCustomUserDetailsServiceThenInvokesThisUserDetailsService()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"WithUserDetailsService\")).autowire();\n\t\tUserDetailsService userDetailsService = this.spring.getContext().getBean(UserDetailsService.class);\n\t\tgiven(userDetailsService.loadUserByUsername(\"user\"))\n\t\t\t.willAnswer((invocation) -> new User(\"user\", \"{noop}password\", Collections.emptyList()));\n\t\tMvcResult result = rememberAuthentication(\"user\", \"password\").andReturn();\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t\tverify(userDetailsService, atLeastOnce()).loadUserByUsername(\"user\");\n\t}\n\n\t/**\n\t * SEC-742\n\t */\n\t@Test\n\tpublic void requestWithRememberMeWhenExcludingBasicAuthenticationFilterThenStillReauthenticates() throws Exception {\n\t\tthis.spring.configLocations(xml(\"Sec742\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(login(\"user\", \"password\").param(\"remember-me\", \"true\").with(csrf()))\n\t\t\t\t.andExpect(redirectedUrl(\"/messageList.html\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-2119\n\t */\n\t@Test\n\tpublic void requestWithRememberMeWhenUsingCustomRememberMeParameterThenReauthenticates() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WithRememberMeParameter\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = login(\"user\", \"password\")\n\t\t\t\t.param(\"custom-remember-me-parameter\", \"true\")\n\t\t\t\t.with(csrf());\n\t\tMvcResult result = this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tCookie cookie = rememberMeCookie(result);\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticated\").cookie(cookie))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingRememberMeParameterAndServicesRefThenThrowsWiringException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"WithRememberMeParameterAndServicesRef\")).autowire());\n\t}\n\n\t/**\n\t * SEC-2826\n\t */\n\t@Test\n\tpublic void authenticateWhenUsingCustomRememberMeCookieNameThenIssuesCookieWithThatName() throws Exception {\n\t\tthis.spring.configLocations(xml(\"WithRememberMeCookie\")).autowire();\n\t\t// @formatter:off\n\t\trememberAuthentication(\"user\", \"password\")\n\t\t\t\t.andExpect(cookie().exists(\"custom-remember-me-cookie\"));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-2826\n\t */\n\t@Test\n\tpublic void configureWhenUsingRememberMeCookieAndServicesRefThenThrowsWiringException() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"WithRememberMeCookieAndServicesRef\")).autowire())\n\t\t\t.withMessageContaining(\"Configuration problem: services-ref can't be used in combination with attributes \"\n\t\t\t\t\t+ \"token-repository-ref,data-source-ref, user-service-ref, token-validity-seconds, \"\n\t\t\t\t\t+ \"use-secure-cookie, remember-me-parameter or remember-me-cookie\");\n\t}\n\n\tprivate ResultActions rememberAuthentication(String username, String password) throws Exception {\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = login(username, password)\n\t\t\t\t.param(AbstractRememberMeServices.DEFAULT_PARAMETER, \"true\")\n\t\t\t\t.with(csrf());\n\t\treturn this.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate static MockHttpServletRequestBuilder login(String username, String password) {\n\t\t// @formatter:off\n\t\treturn post(\"/login\")\n\t\t\t\t.param(\"username\", username)\n\t\t\t\t.param(\"password\", password);\n\t\t// @formatter:on\n\t}\n\n\tprivate static Cookie rememberMeCookie(MvcResult result) {\n\t\treturn result.getResponse().getCookie(\"remember-me\");\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@GetMapping(\"/authenticated\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.w3c.dom.Element;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2Utils;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.result.MockMvcResultHandlers;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link Saml2LoginBeanDefinitionParser}\n *\n * @author Marcus da Coregio\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class Saml2LoginBeanDefinitionParserTests {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests\";\n\n\tprivate static final RelyingPartyRegistration registration = TestRelyingPartyRegistrations.noCredentials()\n\t\t.signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartySigningCredential()))\n\t\t.assertingPartyMetadata((party) -> party\n\t\t\t.verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))\n\t\t.build();\n\n\tprivate static String SIGNED_RESPONSE;\n\n\tprivate static final String IDP_SSO_URL = \"https://sso-url.example.com/IDP/SSO\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tprivate RequestCache requestCache;\n\n\t@Autowired(required = false)\n\tprivate AuthenticationFailureHandler authenticationFailureHandler;\n\n\t@Autowired(required = false)\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler;\n\n\t@Autowired(required = false)\n\tprivate RelyingPartyRegistrationRepository repository;\n\n\t@Autowired(required = false)\n\tprivate ApplicationListener<AuthenticationSuccessEvent> authenticationSuccessListener;\n\n\t@Autowired(required = false)\n\tprivate AuthenticationConverter authenticationConverter;\n\n\t@Autowired(required = false)\n\tprivate Saml2AuthenticationRequestResolver authenticationRequestResolver;\n\n\t@Autowired(required = false)\n\tprivate Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository;\n\n\t@Autowired(required = false)\n\tprivate ApplicationContext applicationContext;\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@BeforeAll\n\tstatic void createResponse() throws Exception {\n\t\tString destination = registration.getAssertionConsumerServiceLocation();\n\t\tString assertingPartyEntityId = registration.getAssertingPartyMetadata().getEntityId();\n\t\tString relyingPartyEntityId = registration.getEntityId();\n\t\tResponse response = TestOpenSamlObjects.response(destination, assertingPartyEntityId);\n\t\tAssertion assertion = TestOpenSamlObjects.assertion(\"test@saml.user\", assertingPartyEntityId,\n\t\t\t\trelyingPartyEntityId, destination);\n\t\tresponse.getAssertions().add(assertion);\n\t\tResponse signed = TestOpenSamlObjects.signed(response,\n\t\t\t\tregistration.getSigningX509Credentials().iterator().next(), relyingPartyEntityId);\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(signed);\n\t\tElement element = marshaller.marshall(signed);\n\t\tString serialized = SerializeSupport.nodeToString(element);\n\t\tSIGNED_RESPONSE = Saml2Utils.samlEncode(serialized.getBytes(StandardCharsets.UTF_8));\n\t}\n\n\t@Test\n\tpublic void requestWhenSingleRelyingPartyRegistrationThenAutoRedirect() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleRelyingPartyRegistration\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/saml2/authenticate/one\"));\n\t\t// @formatter:on\n\t\tverify(this.requestCache).saveRequest(any(), any());\n\t}\n\n\t@Test\n\tpublic void requestWhenMultiRelyingPartyRegistrationThenRedirectToLoginWithRelyingParties() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiRelyingPartyRegistration\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestLoginWhenMultiRelyingPartyRegistrationThenReturnLoginPageWithRelyingParties() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiRelyingPartyRegistration\")).autowire();\n\t\t// @formatter:off\n\t\tMvcResult mvcResult = this.mvc.perform(get(\"/login\"))\n\t\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tString pageContent = mvcResult.getResponse().getContentAsString();\n\t\tassertThat(pageContent).contains(\"<a href=\\\"/saml2/authenticate/two\\\">two</a>\");\n\t\tassertThat(pageContent).contains(\"<a href=\\\"/saml2/authenticate/one\\\">one</a>\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationResponseNotValidThenThrowAuthenticationException() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"SingleRelyingPartyRegistration-WithCustomAuthenticationFailureHandler\"))\n\t\t\t.autowire();\n\t\tthis.mvc.perform(get(\"/login/saml2/sso/one\").param(Saml2ParameterNames.SAML_RESPONSE, \"samlResponse123\"));\n\t\tArgumentCaptor<AuthenticationException> exceptionCaptor = ArgumentCaptor\n\t\t\t.forClass(AuthenticationException.class);\n\t\tverify(this.authenticationFailureHandler).onAuthenticationFailure(any(), any(), exceptionCaptor.capture());\n\t\tAuthenticationException exception = exceptionCaptor.getValue();\n\t\tassertThat(exception).isInstanceOf(Saml2AuthenticationException.class);\n\t\tassertThat(((Saml2AuthenticationException) exception).getSaml2Error().getErrorCode())\n\t\t\t.isEqualTo(\"invalid_response\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationResponseValidThenAuthenticate() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomRelyingPartyRepository\")).autowire();\n\t\tRelyingPartyRegistration relyingPartyRegistration = relyingPartyRegistrationWithVerifyingCredential();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/login/saml2/sso/\" + relyingPartyRegistration.getRegistrationId()).param(Saml2ParameterNames.SAML_RESPONSE, SIGNED_RESPONSE))\n\t\t\t\t.andDo(MockMvcResultHandlers.print())\n\t\t\t\t.andExpect(status().is2xxSuccessful());\n\t\t// @formatter:on\n\t\tArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());\n\t\tAuthentication authentication = authenticationCaptor.getValue();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(Saml2AuthenticatedPrincipal.class);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomSecurityContextHolderStrategy\")).autowire();\n\t\tRelyingPartyRegistration relyingPartyRegistration = relyingPartyRegistrationWithVerifyingCredential();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/login/saml2/sso/\" + relyingPartyRegistration.getRegistrationId()).param(Saml2ParameterNames.SAML_RESPONSE, SIGNED_RESPONSE))\n\t\t\t\t.andDo(MockMvcResultHandlers.print())\n\t\t\t\t.andExpect(status().is2xxSuccessful());\n\t\t// @formatter:on\n\t\tArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());\n\t\tAuthentication authentication = authenticationCaptor.getValue();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(Saml2AuthenticatedPrincipal.class);\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationResponseValidThenAuthenticationSuccessEventPublished() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomRelyingPartyRepository\")).autowire();\n\t\tRelyingPartyRegistration relyingPartyRegistration = relyingPartyRegistrationWithVerifyingCredential();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(post(\"/login/saml2/sso/\" + relyingPartyRegistration.getRegistrationId()).param(Saml2ParameterNames.SAML_RESPONSE, SIGNED_RESPONSE))\n\t\t\t\t.andDo(MockMvcResultHandlers.print())\n\t\t\t\t.andExpect(status().is2xxSuccessful());\n\t\t// @formatter:on\n\t\tverify(this.authenticationSuccessListener).onApplicationEvent(any(AuthenticationSuccessEvent.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationConverterThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomRelyingPartyRepository-WithCustomAuthenticationConverter\"))\n\t\t\t.autowire();\n\t\tRelyingPartyRegistration relyingPartyRegistration = relyingPartyRegistrationWithVerifyingCredential();\n\t\tString response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));\n\t\tgiven(this.authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/login/saml2/sso/\" + relyingPartyRegistration.getRegistrationId())\n\t\t\t\t.param(\"SAMLResponse\", SIGNED_RESPONSE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(status().is3xxRedirection()).andExpect(redirectedUrl(\"/\"));\n\t\tverify(this.authenticationConverter).convert(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationManagerThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomRelyingPartyRepository-WithCustomAuthenticationManager\"))\n\t\t\t.autowire();\n\t\tRelyingPartyRegistration relyingPartyRegistration = relyingPartyRegistrationWithVerifyingCredential();\n\t\tAuthenticationManager authenticationManager = this.applicationContext.getBean(\"customAuthenticationManager\",\n\t\t\t\tAuthenticationManager.class);\n\t\tString response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));\n\t\tgiven(authenticationManager.authenticate(any()))\n\t\t\t.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/login/saml2/sso/\" + relyingPartyRegistration.getRegistrationId())\n\t\t\t\t.param(\"SAMLResponse\", SIGNED_RESPONSE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(status().is3xxRedirection()).andExpect(redirectedUrl(\"/\"));\n\t\tverify(authenticationManager).authenticate(any());\n\t}\n\n\t@Test\n\tpublic void authenticationRequestWhenCustomAuthenticationRequestContextResolverThenUses() throws Exception {\n\t\tthis.spring\n\t\t\t.configLocations(this.xml(\"WithCustomRelyingPartyRepository-WithCustomAuthenticationRequestResolver\"))\n\t\t\t.autowire();\n\t\tSaml2RedirectAuthenticationRequest request = Saml2RedirectAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.noCredentials().build())\n\t\t\t.samlRequest(\"request\")\n\t\t\t.authenticationRequestUri(IDP_SSO_URL)\n\t\t\t.build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any(HttpServletRequest.class))).willReturn(request);\n\t\tthis.mvc.perform(get(\"/saml2/authenticate/registration-id\")).andExpect(status().isFound());\n\t\tverify(this.authenticationRequestResolver).resolve(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void authenticationRequestWhenCustomAuthnRequestRepositoryThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomRelyingPartyRepository-WithCustomAuthnRequestRepository\"))\n\t\t\t.autowire();\n\t\tgiven(this.repository.findByRegistrationId(anyString()))\n\t\t\t.willReturn(TestRelyingPartyRegistrations.relyingPartyRegistration().build());\n\t\tMockHttpServletRequestBuilder request = get(\"/saml2/authenticate/registration-id\");\n\t\tthis.mvc.perform(request).andExpect(status().isFound());\n\t\tverify(this.authenticationRequestRepository).saveAuthenticationRequest(\n\t\t\t\tany(AbstractSaml2AuthenticationRequest.class), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthnRequestRepositoryThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomRelyingPartyRepository-WithCustomAuthnRequestRepository\"))\n\t\t\t.autowire();\n\t\tRelyingPartyRegistrationRepository repository = mock(RelyingPartyRegistrationRepository.class);\n\t\tgiven(this.repository.findByRegistrationId(anyString()))\n\t\t\t.willReturn(TestRelyingPartyRegistrations.relyingPartyRegistration().build());\n\t\tMockHttpServletRequestBuilder request = post(\"/login/saml2/sso/registration-id\").param(\"SAMLResponse\",\n\t\t\t\tSIGNED_RESPONSE);\n\t\tthis.mvc.perform(request);\n\t\tverify(this.authenticationRequestRepository).loadAuthenticationRequest(any(HttpServletRequest.class));\n\t\tverify(this.authenticationRequestRepository).removeAuthenticationRequest(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenValidates() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(this.xml(\"WithCustomLoginProcessingUrl\")).autowire())\n\t\t\t.withMessageContaining(\"loginProcessingUrl must contain {registrationId} path variable\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomLoginProcessingUrlAndCustomAuthenticationConverterThenAuthenticate()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithCustomLoginProcessingUrl-WithCustomAuthenticationConverter\"))\n\t\t\t.autowire();\n\t\tString response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));\n\t\tgiven(this.authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new Saml2AuthenticationToken(registration, response));\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = post(\"/my/custom/url\").param(\"SAMLResponse\", SIGNED_RESPONSE);\n\t\t// @formatter:on\n\t\tthis.mvc.perform(request).andExpect(redirectedUrl(\"/\"));\n\t\tverify(this.authenticationConverter).convert(any(HttpServletRequest.class));\n\t}\n\n\tprivate RelyingPartyRegistration relyingPartyRegistrationWithVerifyingCredential() {\n\t\tgiven(this.repository.findByRegistrationId(anyString())).willReturn(registration);\n\t\treturn registration;\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.saml2.core.Saml2Utils;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutValidatorResult;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link Saml2LogoutBeanDefinitionParser}\n *\n * @author Marcus da Coregio\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class Saml2LogoutBeanDefinitionParserTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tprivate final Saml2LogoutRequestRepository logoutRequestRepository = new HttpSessionLogoutRequestRepository();\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests\";\n\n\tString apLogoutRequest = \"nZFBa4MwGIb/iuQeE2NTXFDLQAaC26Hrdtgt1dQFNMnyxdH9+zlboeyww275SN7nzcOX787jEH0qD9qaAiUxRZEyre206Qv0cnjAGdqVOchxYE40trdT2KuPSUGI5qQBcbkq0OSNsBI0CCNHBSK04vn+sREspsJ5G2xrBxRVc1AbGZa29xAcCEK8i9VZjm5QsfU9GZYWsoCJv5ShqK4K1Ow5p5LyU4aP6XaLN3cpw9mGctydjrxNaZt1XM5vASZVGwjShAIxyhJMU8z4gSWCM8GSmDH+hqLX1Xv+JLpaiiXsb+3+lpMAyv8IoVI6rEzQ4QvrLie3uBX+NMfr6l/waT6t0AumvI6/FlN+Aw==\";\n\n\tString apLogoutRequestSigAlg = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;\n\n\tString apLogoutRequestRelayState = \"33591874-b123-4f2c-ab0d-2d0d84aa8b56\";\n\n\tString apLogoutRequestSignature = \"oKqdzrmn2YAqXcwkow2lzRXr5PNHm0s/gWsRnaZYhC+Oq5ekK5uIKQYvtmNR94HJjDe1VRs+vVQCYivgdoTzBV2ZlffTXZmYsCsY9q4jbCWR6R5CbhU73/MkKQsPcyVvMhNYxnDYapIlxDsfoZNTboDEz3GM+HRoGRfl9emCXY0lPRYwqC4kpu7oMDBkafR0A09jPIxFuNpqlLPwUxL9m+DGkvDK3mFDN1xJcgZaK73HcuJe7Qh4huOrKNFetwc5EvqfiwgiWF6sfq9A+rZBfCIYo10NNLY7fNQAR2IqwcKtawHgTGWbeshRyFrwVYMR64EnClfxUHsHKf5kiZ2dlw==\";\n\n\tString apLogoutResponse = \"fZHRa4MwEMb/Fcl7jEadGqplrAwK3Uvb9WFvZ4ydoInk4uj++1nXbmWMvhwcd9/3Jb9bLE99530oi63RBQn9gHhKS1O3+liQ1/0zzciyXCD0HR/ExhzN6LYKB6NReZNUo/ieFWS0WhjAFoWGXqFwUuweXzaC+4EYrHFGmo54K4Wu1eDmuHfnBhSM2cFXJ+iHTvnGHlk3x7DZmNlLGvHWq4Jstk0GUSjjiIZJI2lcpQnNeRLTAOo4fwCeQg3Trr6+cm/OqmnWVHECVGWQ0jgCSatsKvXUxhFvZF7xSYU4qrVGB9oVhAc8pEFEebLnkeBc8NyPePpGvMOV1/Q3cqEjZrG9hXKfCSAqe+ZAShio0q51n7StF+zW7gf9zoEb8U/7ZGrlHaAb1f0onLfFbpRSIRJWXkJ+bdm/Fy6/AA==\";\n\n\tString apLogoutResponseSigAlg = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;\n\n\tString apLogoutResponseRelayState = \"8f63887a-ec7e-4149-b6a0-dd730017f315\";\n\n\tString apLogoutResponseSignature = \"h2fDqSIBfmnkRHKDMY4IxkCXcI0w98ydNsnPmv1b7GTZCWLbJ+oxaP2yZNPw7wOWXTv86cTPwKLjx5halKy5C+hhWnT0haKhuMcUvHlsgAMBbJKLV+1afzL4O77cvAQJmMNRK7ugXGNV5PTEnd1U4voy134OgdD5XycYiFVRZOwP5H84eJ9xxlvqQwqDvZTcgiF/ZS4ioZgzgnIFcbagZQ12LWNh26OMaUpIW04kCeO6t2dUsxOL6nZWvNrX/Zx1sORIpu4doDUa1RYC8YnjZeQEzDqUVC/dBO/mbVJ/hbF9tD0jBUx7YIgoXpqsWK4TcCsvmlmhrJXvGxDyoAWu2Q==\";\n\n\tString rpLogoutRequest = \"nZFBa4MwGIb/iuQeY6NlGtQykIHgdui6HXaLmrqAJlm+OLp/v0wrlB122CXkI3mfNw/JD5dpDD6FBalVgXZhhAKhOt1LNRTo5fSAU3Qoc+DTSA1r9KBndxQfswAX+KQCth4VaLaKaQ4SmOKTAOY69nz/2DAaRsxY7XSnRxRUPigVd0vbu3MGGCHchOLCJzOKUNuBjEsLWcDErmUoqKsCNcc+yc5tsudYpPwOJzHvcJv6pfdjEtNzl7XU3wWYRa3AceUKRCO6w1GM6f5EY0Ypo1lIk+gNBa+bt38kulqyJWxv7f6W4wDC/gih0hoslJPuC8s+J7e4Df7k43X1L/jsdxt0xZTX8dfHlN8=\";\n\n\tString rpLogoutRequestId = \"LRd49fb45a-e8a7-43ac-b8ac-d8a7432fc9b2\";\n\n\tString rpLogoutRequestRelayState = \"8f63887a-ec7e-4149-b6a0-dd730017f315\";\n\n\tString rpLogoutRequestSignature = \"h2fDqSIBfmnkRHKDMY4IxkCXcI0w98ydNsnPmv1b7GTZCWLbJ+oxaP2yZNPw7wOWXTv86cTPwKLjx5halKy5C+hhWnT0haKhuMcUvHlsgAMBbJKLV+1afzL4O77cvAQJmMNRK7ugXGNV5PTEnd1U4voy134OgdD5XycYiFVRZOwP5H84eJ9xxlvqQwqDvZTcgiF/ZS4ioZgzgnIFcbagZQ12LWNh26OMaUpIW04kCeO6t2dUsxOL6nZWvNrX/Zx1sORIpu4doDUa1RYC8YnjZeQEzDqUVC/dBO/mbVJ/hbF9tD0jBUx7YIgoXpqsWK4TcCsvmlmhrJXvGxDyoAWu2Q==\";\n\n\t@Autowired(required = false)\n\tprivate RelyingPartyRegistrationRepository repository;\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\tprivate Saml2Authentication saml2User;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"registration-id\");\n\t\tthis.saml2User = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.request = new MockHttpServletRequest(\"POST\", \"/login/saml2/sso/test-rp\");\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void logoutWhenLogoutSuccessHandlerAndNotSaml2LoginThenDefaultLogoutSuccessHandler() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"LogoutSuccessHandler\")).autowire();\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tMvcResult result = this.mvc.perform(post(\"/logout\").with(authentication(user)).with(csrf()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tassertThat(location).isEqualTo(\"/logoutSuccessEndpoint\");\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenDefaultsThenLogsOutAndSendsLogoutRequest() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/logout\").with(authentication(this.saml2User)).with(csrf()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/request\");\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenUnauthenticatedThenEntryPoint() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tthis.mvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenMissingCsrfThen403() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(this.saml2User))).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenGetThenDefaultLogoutPage() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/logout\").with(authentication(this.saml2User)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andReturn();\n\t\tassertThat(result.getResponse().getContentAsString()).contains(\"Are you sure you want to log out?\");\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenPutOrDeleteThen404() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tthis.mvc.perform(put(\"/logout\").with(authentication(this.saml2User)).with(csrf()))\n\t\t\t.andExpect(status().isNotFound());\n\t\tthis.mvc.perform(delete(\"/logout\").with(authentication(this.saml2User)).with(csrf()))\n\t\t\t.andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenNoRegistrationThen401() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"wrong\");\n\t\tSaml2Authentication authentication = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(authentication)).with(csrf()))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenCsrfDisabledAndNoAuthenticationThenFinalRedirect() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CsrfDisabled-MockLogoutSuccessHandler\")).autowire();\n\t\tthis.mvc.perform(post(\"/logout\"));\n\t\tLogoutSuccessHandler logoutSuccessHandler = this.spring.getContext().getBean(LogoutSuccessHandler.class);\n\t\tverify(logoutSuccessHandler).onLogoutSuccess(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutWhenCustomLogoutRequestResolverThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CustomComponents\")).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tgiven(getBean(Saml2LogoutRequestResolver.class).resolve(any(), any())).willReturn(logoutRequest);\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(this.saml2User)).with(csrf()));\n\t\tverify(getBean(Saml2LogoutRequestResolver.class)).resolve(any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenDefaultsThenLogsOutAndSendsLogoutResponse() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"get\");\n\t\tSaml2Authentication user = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tMvcResult result = this.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", this.apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", this.apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutRequestSignature)\n\t\t\t\t.with(samlQueryString())\n\t\t\t\t.with(authentication(user)))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/response\");\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithSecurityContextHolderStrategy\")).autowire();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"get\");\n\t\tSaml2Authentication user = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tMvcResult result = this.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", this.apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", this.apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutRequestSignature)\n\t\t\t\t.with(samlQueryString())\n\t\t\t\t.with(authentication(user)))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andReturn();\n\t\tString location = result.getResponse().getHeader(\"Location\");\n\t\tassertThat(location).startsWith(\"https://ap.example.org/logout/saml2/response\");\n\t\tverify(getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenNoRegistrationThen400() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"wrong\");\n\t\tSaml2Authentication user = new Saml2Authentication(principal, \"response\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tthis.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", this.apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", this.apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutRequestSignature)\n\t\t\t\t.with(authentication(user)))\n\t\t\t.andExpect(status().isBadRequest());\n\t}\n\n\t// gh-14635\n\t@Test\n\tpublic void saml2LogoutRequestWhenInvalidSamlRequestThen302Redirect() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tthis.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").param(\"SAMLRequest\", this.apLogoutRequest)\n\t\t\t\t.param(\"RelayState\", this.apLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.with(authentication(this.saml2User)))\n\t\t\t.andExpect(status().isFound());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutRequestWhenCustomLogoutRequestHandlerThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CustomComponents\")).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tlogoutRequest.setIssueInstant(Instant.now());\n\t\tgiven(getBean(Saml2LogoutRequestValidator.class).validate(any()))\n\t\t\t.willReturn(Saml2LogoutValidatorResult.success());\n\t\tSaml2LogoutResponse logoutResponse = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(\"samlResponse\")\n\t\t\t.build();\n\t\tgiven(getBean(Saml2LogoutResponseResolver.class).resolve(any(), any())).willReturn(logoutResponse);\n\t\tthis.mvc\n\t\t\t.perform(post(\"/logout/saml2/slo\").param(\"SAMLRequest\", \"samlRequest\").with(authentication(this.saml2User)))\n\t\t\t.andReturn();\n\t\tverify(getBean(Saml2LogoutRequestValidator.class)).validate(any());\n\t\tverify(getBean(Saml2LogoutResponseResolver.class)).resolve(any(), any());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutResponseWhenDefaultsThenRedirects() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"get\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, this.request, this.response);\n\t\tthis.request.setParameter(\"RelayState\", logoutRequest.getRelayState());\n\t\tassertThat(this.logoutRequestRepository.loadLogoutRequest(this.request)).isNotNull();\n\t\tthis.mvc\n\t\t\t.perform(get(\"/logout/saml2/slo\").session(((MockHttpSession) this.request.getSession()))\n\t\t\t\t.param(\"SAMLResponse\", this.apLogoutResponse)\n\t\t\t\t.param(\"RelayState\", this.apLogoutResponseRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutResponseSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutResponseSignature)\n\t\t\t\t.with(samlQueryString()))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/login?logout\"));\n\t\tassertThat(this.logoutRequestRepository.loadLogoutRequest(this.request)).isNull();\n\t}\n\n\t@Test\n\tpublic void saml2LogoutResponseWhenInvalidSamlResponseThen401() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Default\")).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"registration-id\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, this.request, this.response);\n\t\tString deflatedApLogoutResponse = Saml2Utils.samlEncode(\n\t\t\t\tSaml2Utils.samlInflate(Saml2Utils.samlDecode(this.apLogoutResponse)).getBytes(StandardCharsets.UTF_8));\n\t\tthis.mvc\n\t\t\t.perform(post(\"/logout/saml2/slo\").session((MockHttpSession) this.request.getSession())\n\t\t\t\t.param(\"SAMLResponse\", deflatedApLogoutResponse)\n\t\t\t\t.param(\"RelayState\", this.rpLogoutRequestRelayState)\n\t\t\t\t.param(\"SigAlg\", this.apLogoutRequestSigAlg)\n\t\t\t\t.param(\"Signature\", this.apLogoutResponseSignature)\n\t\t\t\t.with(samlQueryString()))\n\t\t\t.andExpect(status().reason(containsString(\"invalid_signature\")))\n\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tpublic void saml2LogoutResponseWhenCustomLogoutResponseHandlerThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CustomComponents\")).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"get\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tgiven(getBean(Saml2LogoutRequestRepository.class).removeLogoutRequest(any(), any())).willReturn(logoutRequest);\n\t\tgiven(getBean(Saml2LogoutResponseValidator.class).validate(any()))\n\t\t\t.willReturn(Saml2LogoutValidatorResult.success());\n\t\tthis.mvc.perform(get(\"/logout/saml2/slo\").param(\"SAMLResponse\", \"samlResponse\")).andReturn();\n\t\tverify(getBean(Saml2LogoutResponseValidator.class)).validate(any());\n\t}\n\n\t// gh-11363\n\t@Test\n\tpublic void saml2LogoutWhenCustomLogoutRequestRepositoryThenUses() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CustomComponents\")).autowire();\n\t\tRelyingPartyRegistration registration = this.repository.findByRegistrationId(\"get\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(this.rpLogoutRequest)\n\t\t\t.id(this.rpLogoutRequestId)\n\t\t\t.relayState(this.rpLogoutRequestRelayState)\n\t\t\t.parameters((params) -> params.put(\"Signature\", this.rpLogoutRequestSignature))\n\t\t\t.build();\n\t\tgiven(getBean(Saml2LogoutRequestResolver.class).resolve(any(), any())).willReturn(logoutRequest);\n\t\tthis.mvc.perform(post(\"/logout\").with(authentication(this.saml2User)).with(csrf()));\n\t\tverify(getBean(Saml2LogoutRequestRepository.class)).saveLogoutRequest(eq(logoutRequest), any(), any());\n\t}\n\n\tprivate <T> T getBean(Class<T> clazz) {\n\t\treturn this.spring.getContext().getBean(clazz);\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tprivate SamlQueryStringRequestPostProcessor samlQueryString() {\n\t\treturn new SamlQueryStringRequestPostProcessor();\n\t}\n\n\tstatic class SamlQueryStringRequestPostProcessor implements RequestPostProcessor {\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String[]> entries : request.getParameterMap().entrySet()) {\n\t\t\t\tbuilder.queryParam(entries.getKey(),\n\t\t\t\t\t\tUriUtils.encode(entries.getValue()[0], StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\trequest.setQueryString(builder.build(true).toUriString().substring(1));\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.http.HttpHeaders;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class SecurityContextHolderAwareRequestConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mvc;\n\n\t@Test\n\tpublic void servletLoginWhenUsingDefaultConfigurationThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Simple\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/good-login\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void servletAuthenticateWhenUsingDefaultConfigurationThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Simple\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticate\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void servletLogoutWhenUsingDefaultConfigurationThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Simple\")).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/good-login\")).andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\t// @formatter:off\n\t\tresult = this.mvc.perform(get(\"/do-logout\").session(session))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tsession = (MockHttpSession) result.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void servletAuthenticateWhenUsingHttpBasicThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"HttpBasic\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticate\"))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, containsString(\"discworld\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void servletAuthenticateWhenUsingFormLoginThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"FormLogin\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticate\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void servletLoginWhenUsingMultipleHttpConfigsThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiHttp\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/good-login\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"user\"));\n\t\tthis.mvc.perform(get(\"/v2/good-login\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"user2\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void servletAuthenticateWhenUsingMultipleHttpConfigsThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiHttp\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/authenticate\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\tthis.mvc.perform(get(\"/v2/authenticate\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(redirectedUrl(\"/login2\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void servletLogoutWhenUsingMultipleHttpConfigsThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"MultiHttp\")).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/good-login\")).andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\t// @formatter:off\n\t\tresult = this.mvc.perform(get(\"/do-logout\").session(session))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tsession = (MockHttpSession) result.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\t// @formatter:off\n\t\tresult = this.mvc.perform(get(\"/v2/good-login\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tsession = (MockHttpSession) result.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\t// @formatter:off\n\t\tresult = this.mvc.perform(get(\"/v2/do-logout\").session(session))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tsession = (MockHttpSession) result.getRequest().getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void servletLogoutWhenUsingCustomLogoutThenUsesSpringSecurity() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Logout\")).autowire();\n\t\tthis.mvc.perform(get(\"/authenticate\")).andExpect(status().isFound()).andExpect(redirectedUrl(\"/signin\"));\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/good-login\"))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\t// @formatter:off\n\t\tresult = this.mvc.perform(get(\"/do-logout\").session(session))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().string(\"\"))\n\t\t\t\t.andExpect(cookie().maxAge(\"JSESSIONID\", 0))\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tsession = (MockHttpSession) result.getRequest().getSession(false);\n\t\tassertThat(session).isNotNull();\n\t}\n\n\t/**\n\t * SEC-2926: Role Prefix is set\n\t */\n\t@Test\n\t@WithMockUser\n\tpublic void servletIsUserInRoleWhenUsingDefaultConfigThenRoleIsSet() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"Simple\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/role\"))\n\t\t\t\t.andExpect(content().string(\"true\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\t@RestController\n\tpublic static class ServletAuthenticatedController {\n\n\t\t@GetMapping(\"/v2/good-login\")\n\t\tpublic String v2Login(HttpServletRequest request) throws ServletException {\n\t\t\trequest.login(\"user2\", \"password2\");\n\t\t\treturn this.principal();\n\t\t}\n\n\t\t@GetMapping(\"/good-login\")\n\t\tpublic String login(HttpServletRequest request) throws ServletException {\n\t\t\trequest.login(\"user\", \"password\");\n\t\t\treturn this.principal();\n\t\t}\n\n\t\t@GetMapping(\"/v2/authenticate\")\n\t\tpublic String v2Authenticate(HttpServletRequest request, HttpServletResponse response)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\treturn this.authenticate(request, response);\n\t\t}\n\n\t\t@GetMapping(\"/authenticate\")\n\t\tpublic String authenticate(HttpServletRequest request, HttpServletResponse response)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\trequest.authenticate(response);\n\t\t\treturn this.principal();\n\t\t}\n\n\t\t@GetMapping(\"/v2/do-logout\")\n\t\tpublic String v2Logout(HttpServletRequest request) throws ServletException {\n\t\t\treturn this.logout(request);\n\t\t}\n\n\t\t@GetMapping(\"/do-logout\")\n\t\tpublic String logout(HttpServletRequest request) throws ServletException {\n\t\t\trequest.logout();\n\t\t\treturn this.principal();\n\t\t}\n\n\t\t@GetMapping(\"/role\")\n\t\tpublic String role(HttpServletRequest request) {\n\t\t\treturn String.valueOf(request.isUserInRole(\"USER\"));\n\t\t}\n\n\t\tprivate String principal() {\n\t\t\tif (SecurityContextHolder.getContext().getAuthentication() != null) {\n\t\t\t\treturn SecurityContextHolder.getContext().getAuthentication().getName();\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/SecurityFiltersAssertions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Assertions for tests that rely on confirming behavior of the package-private\n * SecurityFilters enum\n *\n * @author Josh Cummings\n */\npublic final class SecurityFiltersAssertions {\n\n\tprivate static Collection<SecurityFilters> ordered = Arrays.asList(SecurityFilters.values());\n\n\tprivate SecurityFiltersAssertions() {\n\t}\n\n\tpublic static void assertEquals(List<String> filters) {\n\t\tList<String> expected = ordered.stream().map(SecurityFilters::name).collect(Collectors.toList());\n\t\tassertThat(filters).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/SessionManagementConfigServlet31Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport jakarta.servlet.Filter;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\npublic class SessionManagementConfigServlet31Tests {\n\n\t// @formatter:off\n\tprivate static final String XML_AUTHENTICATION_MANAGER = \"<authentication-manager>\"\n\t\t\t+ \"  <authentication-provider>\"\n\t\t\t+ \"    <user-service>\"\n\t\t\t+ \"      <user name='user' password='{noop}password' authorities='ROLE_USER' />\"\n\t\t\t+ \"    </user-service>\"\n\t\t\t+ \"  </authentication-provider>\"\n\t\t\t+ \"</authentication-manager>\";\n\t// @formatter:on\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\tConfigurableApplicationContext context;\n\n\tFilter springSecurityFilterChain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t}\n\n\t@AfterEach\n\tpublic void teardown() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void changeSessionIdThenPreserveParameters() throws Exception {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.post(\"/login\")\n\t\t\t.param(\"username\", \"user\")\n\t\t\t.param(\"password\", \"password\")\n\t\t\t.build();\n\t\trequest.getSession();\n\t\trequest.getSession().setAttribute(\"attribute1\", \"value1\");\n\t\tString id = request.getSession().getId();\n\t\t// @formatter:off\n\t\tloadContext(\"<http>\\n\"\n\t\t\t\t+ \"        <intercept-url pattern=\\\"/**\\\" access=\\\"authenticated\\\"/>\\n\"\n\t\t\t\t+ \"        <form-login/>\\n\"\n\t\t\t\t+ \"        <session-management/>\\n\"\n\t\t\t\t+ \"        <csrf disabled='true'/>\\n\"\n\t\t\t\t+ \"    </http>\"\n\t\t\t\t+ XML_AUTHENTICATION_MANAGER);\n\t\t// @formatter:on\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(request.getSession().getId()).isNotEqualTo(id);\n\t\tassertThat(request.getSession().getAttribute(\"attribute1\")).isEqualTo(\"value1\");\n\t}\n\n\t@Test\n\tpublic void changeSessionId() throws Exception {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.post(\"/login\")\n\t\t\t.param(\"username\", \"user\")\n\t\t\t.param(\"password\", \"password\")\n\t\t\t.build();\n\t\trequest.getSession();\n\t\tString id = request.getSession().getId();\n\t\t// @formatter:off\n\t\tloadContext(\"<http>\\n\"\n\t\t\t\t+ \"        <intercept-url pattern=\\\"/**\\\" access=\\\"authenticated\\\"/>\\n\"\n\t\t\t\t+ \"        <form-login/>\\n\"\n\t\t\t\t+ \"        <session-management session-fixation-protection='changeSessionId'/>\\n\"\n\t\t\t\t+ \"        <csrf disabled='true'/>\\n\"\n\t\t\t\t+ \"    </http>\"\n\t\t\t\t+ XML_AUTHENTICATION_MANAGER);\n\t\t// @formatter:on\n\t\tthis.springSecurityFilterChain.doFilter(request, this.response, this.chain);\n\t\tassertThat(request.getSession().getId()).isNotEqualTo(id);\n\t}\n\n\tprivate void loadContext(String context) {\n\t\tthis.context = new InMemoryXmlApplicationContext(context);\n\t\tthis.springSecurityFilterChain = this.context.getBean(\"springSecurityFilterChain\", Filter.class);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/SessionManagementConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.io.IOException;\nimport java.security.Principal;\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpServletResponseWrapper;\nimport org.apache.http.HttpStatus;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.util.FieldUtils;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.logout.CompositeLogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutFilter;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.session.ConcurrentSessionFilter;\nimport org.springframework.security.web.session.SessionManagementFilter;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests session-related functionality for the &lt;http&gt; namespace element and\n * &lt;session-management&gt;\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @author Josh Cummings\n * @author Onur Kagan Ozcan\n * @author Mazen Aissa\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SessionManagementConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/SessionManagementConfigTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void requestWhenCreateSessionAlwaysThenAlwaysCreatesSession() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionAlways\")).autowire();\n\t\tMockHttpServletRequest request = get(\"/\").buildRequest(this.servletContext());\n\t\tMockHttpServletResponse response = request(request, this.spring.getContext());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToNeverThenDoesNotCreateSessionOnLoginChallenge() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionNever\")).autowire();\n\t\tMockHttpServletRequest request = get(\"/auth\").buildRequest(this.servletContext());\n\t\tMockHttpServletResponse response = request(request, this.spring.getContext());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToNeverThenDoesNotCreateSessionOnLogin() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionNever\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequest request = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.buildRequest(this.servletContext());\n\t\t// @formatter:on\n\t\trequest = csrf().postProcessRequest(request);\n\t\tMockHttpServletResponse response = request(request, this.spring.getContext());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToNeverThenUsesExistingSession() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionNever\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequest request = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.buildRequest(this.servletContext());\n\t\t// @formatter:on\n\t\trequest = csrf().postProcessRequest(request);\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = request(request, this.spring.getContext());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t\tassertThat(request.getSession(false)\n\t\t\t.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToStatelessThenDoesNotCreateSessionOnLoginChallenge() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionStateless\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/auth\"))\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(session().exists(false));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToStatelessThenDoesNotCreateSessionOnLogin() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionStateless\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.with(csrf());\n\t\tthis.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(session().exists(false));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToStatelessThenIgnoresExistingSession() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionStateless\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder loginRequest = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.session(new MockHttpSession())\n\t\t\t\t.with(csrf());\n\t\tMvcResult result = this.mvc.perform(loginRequest)\n\t\t\t\t.andExpect(status().isFound())\n\t\t\t\t.andExpect(session()).andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest()\n\t\t\t.getSession(false)\n\t\t\t.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToIfRequiredThenDoesNotCreateSessionOnPublicInvocation() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionIfRequired\")).autowire();\n\t\tServletContext servletContext = this.mvc.getDispatcherServlet().getServletContext();\n\t\tMockHttpServletRequest request = get(\"/\").buildRequest(servletContext);\n\t\tMockHttpServletResponse response = request(request, this.spring.getContext());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToIfRequiredThenCreatesSessionOnLoginChallenge() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionIfRequired\")).autowire();\n\t\tServletContext servletContext = this.mvc.getDispatcherServlet().getServletContext();\n\t\tMockHttpServletRequest request = get(\"/auth\").buildRequest(servletContext);\n\t\tMockHttpServletResponse response = request(request, this.spring.getContext());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenCreateSessionIsSetToIfRequiredThenCreatesSessionOnLogin() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CreateSessionIfRequired\")).autowire();\n\t\tServletContext servletContext = this.mvc.getDispatcherServlet().getServletContext();\n\t\t// @formatter:off\n\t\tMockHttpServletRequest request = post(\"/login\")\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\")\n\t\t\t\t.buildRequest(servletContext);\n\t\t// @formatter:on\n\t\trequest = csrf().postProcessRequest(request);\n\t\tMockHttpServletResponse response = request(request, this.spring.getContext());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY);\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t}\n\n\t/**\n\t * SEC-1208\n\t */\n\t@Test\n\tpublic void requestWhenRejectingUserBasedOnMaxSessionsExceededThenDoesNotCreateSession() throws Exception {\n\t\tthis.spring.configLocations(xml(\"Sec1208\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(session());\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t\t.andExpect(session().exists(false));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-2137\n\t */\n\t@Test\n\tpublic void requestWhenSessionFixationProtectionDisabledAndConcurrencyControlEnabledThenSessionNotInvalidated()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"Sec2137\")).autowire();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/auth\").session(session).with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(session().id(session.getId()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void autowireWhenExportingSessionRegistryBeanThenAvailableForWiring() {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlSessionRegistryAlias\")).autowire();\n\t\tthis.sessionRegistryIsValid();\n\t}\n\n\t@Test\n\tpublic void requestWhenExpiredUrlIsSetThenInvalidatesSessionAndRedirects() throws Exception {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlExpiredUrl\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\")\n\t\t\t\t.session(expiredSession())\n\t\t\t\t.with(httpBasic(\"user\", \"password\"));\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(redirectedUrl(\"/expired\"))\n\t\t\t\t.andExpect(session().exists(false));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenConcurrencyControlAndCustomLogoutHandlersAreSetThenAllAreInvokedWhenSessionExpires()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlLogoutAndRememberMeHandlers\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\")\n\t\t\t\t.session(expiredSession())\n\t\t\t\t.with(httpBasic(\"user\", \"password\"));\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(cookie().maxAge(\"testCookie\", 0))\n\t\t\t\t.andExpect(cookie().exists(\"rememberMeCookie\"))\n\t\t\t\t.andExpect(session().valid(true));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenConcurrencyControlAndRememberMeAreSetThenInvokedWhenSessionExpires() throws Exception {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlRememberMeHandler\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\")\n\t\t\t\t.session(expiredSession())\n\t\t\t\t.with(httpBasic(\"user\", \"password\"));\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(status().isOk()).andExpect(cookie().exists(\"rememberMeCookie\"))\n\t\t\t\t.andExpect(session().exists(false));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * SEC-2057\n\t */\n\t@Test\n\tpublic void autowireWhenConcurrencyControlIsSetThenLogoutHandlersGetAuthenticationObject() throws Exception {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlCustomLogoutHandler\")).autowire();\n\t\tMvcResult result = this.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t.andExpect(session())\n\t\t\t.andReturn();\n\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession(false);\n\t\tSessionRegistry sessionRegistry = this.spring.getContext().getBean(SessionRegistry.class);\n\t\tsessionRegistry.getSessionInformation(session.getId()).expireNow();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/auth\").session(session))\n\t\t\t\t.andExpect(header().string(\"X-Username\", \"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenConcurrencyControlIsSetThenDefaultsToResponseBodyExpirationResponse() throws Exception {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlSessionRegistryAlias\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/auth\")\n\t\t\t\t.session(expiredSession())\n\t\t\t\t.with(httpBasic(\"user\", \"password\"));\n\t\tthis.mvc.perform(request)\n\t\t\t\t.andExpect(content().string(\"This session has been expired (possibly due to multiple concurrent \"\n\t\t\t\t\t\t+ \"logins being attempted as the same user).\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomSessionAuthenticationStrategyThenInvokesOnAuthentication() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SessionAuthenticationStrategyRef\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isIAmATeapot());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void autowireWhenSessionRegistryRefIsSetThenAvailableForWiring() {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlSessionRegistryRef\")).autowire();\n\t\tthis.sessionRegistryIsValid();\n\t}\n\n\t@Test\n\tpublic void requestWhenMaxSessionsIsSetThenErrorsWhenExceeded() throws Exception {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlMaxSessions\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(redirectedUrl(\"/max-exceeded\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenMaxSessionsIsSetWithPlaceHolderThenErrorsWhenExceeded() throws Exception {\n\t\tSystem.setProperty(\"sessionManagement.maxSessions\", \"1\");\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlMaxSessionsPlaceHolder\")).autowire();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mvc.perform(get(\"/auth\").with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(redirectedUrl(\"/max-exceeded\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void autowireWhenSessionFixationProtectionIsNoneAndCsrfDisabledThenSessionManagementFilterIsNotWired() {\n\t\tthis.spring.configLocations(xml(\"NoSessionManagementFilter\")).autowire();\n\t\tassertThat(this.getFilter(SessionManagementFilter.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionFixationProtectionIsNoneThenSessionNotInvalidated() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SessionFixationProtectionNone\")).autowire();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tString sessionId = session.getId();\n\t\t// @formatter:off\n\t\tthis.mvc.perform(get(\"/auth\").session(session).with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(session().id(sessionId));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionFixationProtectionIsMigrateSessionThenSessionIsReplaced() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SessionFixationProtectionMigrateSession\")).autowire();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tString sessionId = session.getId();\n\t\t// @formatter:off\n\t\tMvcResult result = this.mvc.perform(get(\"/auth\").session(session).with(httpBasic(\"user\", \"password\")))\n\t\t\t\t.andExpect(session())\n\t\t\t\t.andReturn();\n\t\t// @formatter:on\n\t\tassertThat(result.getRequest().getSession(false).getId()).isNotEqualTo(sessionId);\n\t}\n\n\t@Test\n\tpublic void requestWhenSessionFixationProtectionIsNoneAndInvalidSessionUrlIsSetThenStillRedirectsOnInvalidSession()\n\t\t\tthrows Exception {\n\t\tthis.spring.configLocations(xml(\"SessionFixationProtectionNoneWithInvalidSessionUrl\")).autowire();\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder authRequest = get(\"/auth\")\n\t\t\t\t.with((request) -> {\n\t\t\t\t\trequest.setRequestedSessionId(\"1\");\n\t\t\t\t\trequest.setRequestedSessionIdValid(false);\n\t\t\t\t\treturn request;\n\t\t\t\t});\n\t\tthis.mvc.perform(authRequest)\n\t\t\t\t.andExpect(redirectedUrl(\"/timeoutUrl\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate void sessionRegistryIsValid() {\n\t\tSessionRegistry sessionRegistry = this.spring.getContext().getBean(\"sessionRegistry\", SessionRegistry.class);\n\t\tassertThat(sessionRegistry).isNotNull();\n\t\tassertThat(this.getFilter(ConcurrentSessionFilter.class)).returns(sessionRegistry,\n\t\t\t\tthis::extractSessionRegistry);\n\t\tassertThat(this.getFilter(UsernamePasswordAuthenticationFilter.class)).returns(sessionRegistry,\n\t\t\t\tthis::extractSessionRegistry);\n\t\t// SEC-1143\n\t\tassertThat(this.getFilter(SessionManagementFilter.class)).returns(sessionRegistry,\n\t\t\t\tthis::extractSessionRegistry);\n\t}\n\n\tprivate SessionRegistry extractSessionRegistry(ConcurrentSessionFilter filter) {\n\t\treturn getFieldValue(filter, \"sessionRegistry\");\n\t}\n\n\tprivate SessionRegistry extractSessionRegistry(UsernamePasswordAuthenticationFilter filter) {\n\t\tSessionAuthenticationStrategy strategy = getFieldValue(filter, \"sessionStrategy\");\n\t\tList<SessionAuthenticationStrategy> strategies = getFieldValue(strategy, \"delegateStrategies\");\n\t\treturn getFieldValue(strategies.get(0), \"sessionRegistry\");\n\t}\n\n\tprivate SessionRegistry extractSessionRegistry(SessionManagementFilter filter) {\n\t\tSessionAuthenticationStrategy strategy = getFieldValue(filter, \"sessionAuthenticationStrategy\");\n\t\tList<SessionAuthenticationStrategy> strategies = getFieldValue(strategy, \"delegateStrategies\");\n\t\treturn getFieldValue(strategies.get(0), \"sessionRegistry\");\n\t}\n\n\tprivate <T> T getFieldValue(Object target, String fieldName) {\n\t\ttry {\n\t\t\treturn (T) FieldUtils.getFieldValue(target, fieldName);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprivate static SessionResultMatcher session() {\n\t\treturn new SessionResultMatcher();\n\t}\n\n\t/**\n\t * SEC-2680\n\t */\n\t@Test\n\tpublic void checkConcurrencyAndLogoutFilterHasSameSizeAndHasLogoutSuccessEventPublishingLogoutHandler() {\n\t\tthis.spring.configLocations(xml(\"ConcurrencyControlLogoutAndRememberMeHandlers\")).autowire();\n\t\tConcurrentSessionFilter concurrentSessionFilter = getFilter(ConcurrentSessionFilter.class);\n\t\tLogoutFilter logoutFilter = getFilter(LogoutFilter.class);\n\t\tLogoutHandler csfLogoutHandler = getFieldValue(concurrentSessionFilter, \"handlers\");\n\t\tLogoutHandler lfLogoutHandler = getFieldValue(logoutFilter, \"handler\");\n\t\tassertThat(csfLogoutHandler).isInstanceOf(CompositeLogoutHandler.class);\n\t\tassertThat(lfLogoutHandler).isInstanceOf(CompositeLogoutHandler.class);\n\t\tList<LogoutHandler> csfLogoutHandlers = getFieldValue(csfLogoutHandler, \"logoutHandlers\");\n\t\tList<LogoutHandler> lfLogoutHandlers = getFieldValue(lfLogoutHandler, \"logoutHandlers\");\n\t\tassertThat(csfLogoutHandlers).hasSameSizeAs(lfLogoutHandlers);\n\t\tassertThat(csfLogoutHandlers).hasAtLeastOneElementOfType(LogoutSuccessEventPublishingLogoutHandler.class);\n\t\tassertThat(lfLogoutHandlers).hasAtLeastOneElementOfType(LogoutSuccessEventPublishingLogoutHandler.class);\n\t}\n\n\tprivate static MockHttpServletResponse request(MockHttpServletRequest request, ApplicationContext context)\n\t\t\tthrows IOException, ServletException {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChainProxy proxy = context.getBean(FilterChainProxy.class);\n\t\tproxy.doFilter(request, new EncodeUrlDenyingHttpServletResponseWrapper(response), (req, resp) -> {\n\t\t});\n\t\treturn response;\n\t}\n\n\tprivate MockHttpSession expiredSession() {\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tSessionRegistry sessionRegistry = this.spring.getContext().getBean(SessionRegistry.class);\n\t\tsessionRegistry.registerNewSession(session.getId(), \"user\");\n\t\tsessionRegistry.getSessionInformation(session.getId()).expireNow();\n\t\treturn session;\n\t}\n\n\tprivate <T extends Filter> T getFilter(Class<T> filterClass) {\n\t\treturn (T) getFilters().stream().filter(filterClass::isInstance).findFirst().orElse(null);\n\t}\n\n\tprivate List<Filter> getFilters() {\n\t\tFilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class);\n\t\treturn proxy.getFilters(\"/\");\n\t}\n\n\tprivate ServletContext servletContext() {\n\t\tWebApplicationContext context = this.spring.getContext();\n\t\treturn context.getServletContext();\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tstatic class TeapotSessionAuthenticationStrategy implements SessionAuthenticationStrategy {\n\n\t\t@Override\n\t\tpublic void onAuthentication(Authentication authentication, HttpServletRequest request,\n\t\t\t\tHttpServletResponse response) throws SessionAuthenticationException {\n\t\t\tresponse.setStatus(org.springframework.http.HttpStatus.I_AM_A_TEAPOT.value());\n\t\t}\n\n\t}\n\n\tstatic class CustomRememberMeServices implements RememberMeServices, LogoutHandler {\n\n\t\t@Override\n\t\tpublic Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void loginFail(HttpServletRequest request, HttpServletResponse response) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void loginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthentication successfulAuthentication) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {\n\t\t\tresponse.addHeader(\"X-Username\", authentication.getName());\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class BasicController {\n\n\t\t@GetMapping(\"/\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t\t@GetMapping(\"/auth\")\n\t\tString auth(Principal principal) {\n\t\t\treturn principal.getName();\n\t\t}\n\n\t}\n\n\tprivate static class SessionResultMatcher implements ResultMatcher {\n\n\t\tprivate String id;\n\n\t\tprivate Boolean valid;\n\n\t\tprivate Boolean exists = true;\n\n\t\tResultMatcher exists(boolean exists) {\n\t\t\tthis.exists = exists;\n\t\t\treturn this;\n\t\t}\n\n\t\tResultMatcher valid(boolean valid) {\n\t\t\tthis.valid = valid;\n\t\t\treturn this.exists(true);\n\t\t}\n\n\t\tResultMatcher id(String id) {\n\t\t\tthis.id = id;\n\t\t\treturn this.exists(true);\n\t\t}\n\n\t\t@Override\n\t\tpublic void match(MvcResult result) {\n\t\t\tif (!this.exists) {\n\t\t\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t\t\tMockHttpSession session = (MockHttpSession) result.getRequest().getSession(false);\n\t\t\tif (this.valid != null) {\n\t\t\t\tif (this.valid) {\n\t\t\t\t\tassertThat(session.isInvalid()).isFalse();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tassertThat(session.isInvalid()).isTrue();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.id != null) {\n\t\t\t\tassertThat(session.getId()).isEqualTo(this.id);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate static class EncodeUrlDenyingHttpServletResponseWrapper extends HttpServletResponseWrapper {\n\n\t\tEncodeUrlDenyingHttpServletResponseWrapper(HttpServletResponse response) {\n\t\t\tsuper(response);\n\t\t}\n\n\t\t@Override\n\t\tpublic String encodeURL(String url) {\n\t\t\tthrow new RuntimeException(\"Unexpected invocation of encodeURL\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String encodeRedirectURL(String url) {\n\t\t\tthrow new RuntimeException(\"Unexpected invocation of encodeURL\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/SessionManagementConfigTransientAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.Transient;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class SessionManagementConfigTransientAuthenticationTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/SessionManagementConfigTransientAuthenticationTests\";\n\n\t@Autowired\n\tMockMvc mvc;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Test\n\tpublic void postWhenTransientAuthenticationThenNoSessionCreated() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"WithTransientAuthentication\")).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/login\")).andReturn();\n\t\tassertThat(result.getRequest().getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void postWhenTransientAuthenticationThenAlwaysSessionOverrides() throws Exception {\n\t\tthis.spring.configLocations(this.xml(\"CreateSessionAlwaysWithTransientAuthentication\")).autowire();\n\t\tMvcResult result = this.mvc.perform(post(\"/login\")).andReturn();\n\t\tassertThat(result.getRequest().getSession(false)).isNotNull();\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tstatic class TransientAuthenticationProvider implements AuthenticationProvider {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\treturn new SomeTransientAuthentication();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\t@Transient\n\tstatic class SomeTransientAuthentication extends AbstractAuthenticationToken {\n\n\t\tSomeTransientAuthentication() {\n\t\t\tsuper((Collection<? extends GrantedAuthority>) null);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getCredentials() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getPrincipal() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/WebConfigUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.beans.factory.xml.ParserContext;\n\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n@ExtendWith(MockitoExtension.class)\npublic class WebConfigUtilsTests {\n\n\tpublic static final String URL = \"/url\";\n\n\t@Mock\n\tprivate ParserContext parserContext;\n\n\t// SEC-1980\n\t@Test\n\tpublic void validateHttpRedirectSpELNoParserWarning() {\n\t\tWebConfigUtils.validateHttpRedirect(\"#{T(org.springframework.security.config.http.WebConfigUtilsTest).URL}\",\n\t\t\t\tthis.parserContext, \"fakeSource\");\n\t\tverifyNoMoreInteractions(this.parserContext);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link WellKnownChangePasswordBeanDefinitionParser}.\n *\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class WellKnownChangePasswordBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParserTests\";\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Test\n\tpublic void whenChangePasswordPageNotSetThenDefaultChangePasswordPageUsed() throws Exception {\n\t\tthis.spring.configLocations(xml(\"DefaultChangePasswordPage\")).autowire();\n\n\t\tthis.mvc.perform(get(\"/.well-known/change-password\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/change-password\"));\n\t}\n\n\t@Test\n\tpublic void whenChangePasswordPageSetThenSpecifiedChangePasswordPageUsed() throws Exception {\n\t\tthis.spring.configLocations(xml(\"CustomChangePasswordPage\")).autowire();\n\n\t\tthis.mvc.perform(get(\"/.well-known/change-password\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/custom-change-password-page\"));\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/customconfigurer/CustomConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http.customconfigurer;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * @author Rob Winch\n *\n */\npublic class CustomConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {\n\n\t@Value(\"${permitAllPattern}\")\n\tprivate String permitAllPattern;\n\n\tprivate String loginPage = \"/login\";\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic void init(HttpSecurity http) {\n\t\t// autowire this bean\n\t\tApplicationContext context = http.getSharedObject(ApplicationContext.class);\n\t\tcontext.getAutowireCapableBeanFactory().autowireBean(this);\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t.requestMatchers(pathPattern(this.permitAllPattern)).permitAll()\n\t\t\t\t.anyRequest().authenticated());\n\t\t// @formatter:on\n\t\tif (http.getConfigurer(FormLoginConfigurer.class) == null) {\n\t\t\t// only apply if formLogin() was not invoked by the user\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginPage(this.loginPage));\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\tpublic CustomConfigurer loginPage(String loginPage) {\n\t\tthis.loginPage = loginPage;\n\t\treturn this;\n\t}\n\n\tpublic static CustomConfigurer customConfigurer() {\n\t\treturn new CustomConfigurer();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/http/customconfigurer/CustomHttpSecurityConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.http.customconfigurer;\n\nimport java.util.Properties;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.support.PropertySourcesPlaceholderConfigurer;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\npublic class CustomHttpSecurityConfigurerTests {\n\n\t@Autowired\n\tConfigurableApplicationContext context;\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain chain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = new MockFilterChain();\n\t\tthis.request.setMethod(\"GET\");\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void customConfiguerPermitAll() throws Exception {\n\t\tloadContext(Config.class);\n\t\tthis.request.setRequestURI(\"/public/something\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void customConfiguerFormLogin() throws Exception {\n\t\tloadContext(Config.class);\n\t\tthis.request.setRequestURI(\"/requires-authentication\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getRedirectedUrl()).endsWith(\"/custom\");\n\t}\n\n\t@Test\n\tpublic void customConfiguerCustomizeDisablesCsrf() throws Exception {\n\t\tloadContext(ConfigCustomize.class);\n\t\tthis.request.setRequestURI(\"/public/something\");\n\t\tthis.request.setMethod(\"POST\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void customConfiguerCustomizeFormLogin() throws Exception {\n\t\tloadContext(ConfigCustomize.class);\n\t\tthis.request.setRequestURI(\"/requires-authentication\");\n\t\tthis.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getRedirectedUrl()).endsWith(\"/other\");\n\t}\n\n\tprivate void loadContext(Class<?> clazz) {\n\t\tAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(clazz);\n\t\tcontext.getAutowireCapableBeanFactory().autowireBean(this);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\treturn http\n\t\t\t\t\t.with(CustomConfigurer.customConfigurer(), (c) -> c.loginPage(\"/custom\"))\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {\n\t\t\t// Typically externalize this as a properties file\n\t\t\tProperties properties = new Properties();\n\t\t\tproperties.setProperty(\"permitAllPattern\", \"/public/**\");\n\t\t\tPropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();\n\t\t\tpropertyPlaceholderConfigurer.setProperties(properties);\n\t\t\treturn propertyPlaceholderConfigurer;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class ConfigCustomize {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.with(CustomConfigurer.customConfigurer(), Customizer.withDefaults())\n\t\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.loginPage(\"/other\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tstatic PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {\n\t\t\t// Typically externalize this as a properties file\n\t\t\tProperties properties = new Properties();\n\t\t\tproperties.setProperty(\"permitAllPattern\", \"/public/**\");\n\t\t\tPropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();\n\t\t\tpropertyPlaceholderConfigurer.setProperties(properties);\n\t\t\treturn propertyPlaceholderConfigurer;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/Contact.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\n/**\n * @author Rob Winch\n *\n */\npublic class Contact {\n\n\tprivate String name;\n\n\t/**\n\t * @param name\n\t */\n\tpublic Contact(String name) {\n\t\tthis.name = name;\n\t}\n\n\t/**\n\t * @return the name\n\t */\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/ContactPermission.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * @author Rob Winch\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"#contact.name == authentication.name\")\npublic @interface ContactPermission {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.Advisor;\nimport org.springframework.aop.framework.Advised;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.MutablePropertyValues;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.support.AbstractXmlApplicationContext;\nimport org.springframework.context.support.StaticApplicationContext;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.ConfigAttribute;\nimport org.springframework.security.access.SecurityConfig;\nimport org.springframework.security.access.annotation.BusinessService;\nimport org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl;\nimport org.springframework.security.access.intercept.AfterInvocationProviderManager;\nimport org.springframework.security.access.intercept.RunAsManagerImpl;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;\nimport org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;\nimport org.springframework.security.access.prepost.PostInvocationAdviceProvider;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;\nimport org.springframework.security.access.vote.AffirmativeBased;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.ConfigTestUtils;\nimport org.springframework.security.config.PostProcessedMockUserDetailsService;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.util.FieldUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic class GlobalMethodSecurityBeanDefinitionParserTests {\n\n\tprivate final UsernamePasswordAuthenticationToken bob = UsernamePasswordAuthenticationToken.unauthenticated(\"bob\",\n\t\t\t\"bobspassword\");\n\n\tprivate AbstractXmlApplicationContext appContext;\n\n\tprivate BusinessService target;\n\n\tpublic void loadContext() {\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean id='target' class='org.springframework.security.access.annotation.BusinessServiceImpl'/>\"\n\t\t\t\t+ \"<global-method-security order='1001' proxy-target-class='false' >\"\n\t\t\t\t+ \"    <protect-pointcut expression='execution(* *.someUser*(..))' access='ROLE_USER'/>\"\n\t\t\t\t+ \"    <protect-pointcut expression='execution(* *.someAdmin*(..))' access='ROLE_ADMIN'/>\"\n\t\t\t\t+ \"</global-method-security>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tthis.target = (BusinessService) this.appContext.getBean(\"target\");\n\t}\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t\tthis.appContext = null;\n\t\t}\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.target = null;\n\t}\n\n\t@Test\n\tpublic void targetShouldPreventProtectedMethodInvocationWithNoContext() {\n\t\tloadContext();\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(this.target::someUserMethod1);\n\t}\n\n\t@Test\n\tpublic void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {\n\t\tloadContext();\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"user\",\n\t\t\t\t\"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tthis.target.someUserMethod1();\n\t\t// SEC-1213. Check the order\n\t\tAdvisor[] advisors = ((Advised) this.target).getAdvisors();\n\t\tassertThat(advisors).hasSize(1);\n\t\tassertThat(((MethodSecurityMetadataSourceAdvisor) advisors[0]).getOrder()).isEqualTo(1001);\n\t}\n\n\t@Test\n\tpublic void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {\n\t\tloadContext();\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"Test\", \"Password\", \"ROLE_SOMEOTHERROLE\");\n\t\ttoken.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.target::someAdminMethod);\n\t}\n\n\t@Test\n\tpublic void doesntInterfereWithBeanPostProcessing() {\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean id='myUserService' class='org.springframework.security.config.PostProcessedMockUserDetailsService'/>\"\n\t\t\t\t+ \"<global-method-security />\"\n\t\t\t\t+ \"<authentication-manager>\"\n\t\t\t\t+ \"   <authentication-provider user-service-ref='myUserService'/>\"\n\t\t\t\t+ \"</authentication-manager>\"\n\t\t\t\t+ \"<b:bean id='beanPostProcessor' class='org.springframework.security.config.MockUserServiceBeanPostProcessor'/>\");\n\t\t// @formatter:on\n\t\tPostProcessedMockUserDetailsService service = (PostProcessedMockUserDetailsService) this.appContext\n\t\t\t.getBean(\"myUserService\");\n\t\tassertThat(service.getPostProcessorWasHere()).isEqualTo(\"Hello from the post processor!\");\n\t}\n\n\t@Test\n\tpublic void worksWithAspectJAutoproxy() {\n\t\t// @formatter:off\n\t\tsetContext(\"<global-method-security>\"\n\t\t\t\t+ \"  <protect-pointcut expression='execution(* org.springframework.security.config.*Service.*(..))'\"\n\t\t\t\t+ \"       access='ROLE_SOMETHING' />\"\n\t\t\t\t+ \"</global-method-security>\"\n\t\t\t\t+ \"<b:bean id='myUserService' class='org.springframework.security.config.PostProcessedMockUserDetailsService'/>\"\n\t\t\t\t+ \"<aop:aspectj-autoproxy />\"\n\t\t\t\t+ \"<authentication-manager>\"\n\t\t\t\t+ \"   <authentication-provider user-service-ref='myUserService'/>\"\n\t\t\t\t+ \"</authentication-manager>\");\n\t\t// @formatter:on\n\t\tUserDetailsService service = (UserDetailsService) this.appContext.getBean(\"myUserService\");\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_SOMEOTHERROLE\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> service.loadUserByUsername(\"notused\"));\n\t}\n\n\t@Test\n\tpublic void supportsMethodArgumentsInPointcut() {\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean id='target' class='org.springframework.security.access.annotation.BusinessServiceImpl'/>\"\n\t\t\t\t+ \"<global-method-security>\"\n\t\t\t\t+ \"   <protect-pointcut expression='execution(* org.springframework.security.access.annotation.BusinessService.someOther(String))' access='ROLE_ADMIN'/>\"\n\t\t\t\t+ \"   <protect-pointcut expression='execution(* org.springframework.security.access.annotation.BusinessService.*(..))' access='ROLE_USER'/>\"\n\t\t\t\t+ \"</global-method-security>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tthis.target = (BusinessService) this.appContext.getBean(\"target\");\n\t\t// someOther(int) should not be matched by someOther(String), but should require\n\t\t// ROLE_USER\n\t\tthis.target.someOther(0);\n\t\t// String version should required admin role\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.target.someOther(\"somestring\"));\n\t}\n\n\t@Test\n\tpublic void supportsBooleanPointcutExpressions() {\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean id='target' class='org.springframework.security.access.annotation.BusinessServiceImpl'/>\"\n\t\t\t\t+ \"<global-method-security>\"\n\t\t\t\t+ \"   <protect-pointcut expression=\"\n\t\t\t\t+ \"     'execution(* org.springframework.security.access.annotation.BusinessService.*(..)) \"\n\t\t\t\t+ \"       and not execution(* org.springframework.security.access.annotation.BusinessService.someOther(String)))' \"\n\t\t\t\t+ \"               access='ROLE_USER'/>\"\n\t\t\t\t+ \"</global-method-security>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tthis.target = (BusinessService) this.appContext.getBean(\"target\");\n\t\t// String method should not be protected\n\t\tthis.target.someOther(\"somestring\");\n\t\t// All others should require ROLE_USER\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.target.someOther(0));\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tthis.target.someOther(0);\n\t}\n\n\t@Test\n\tpublic void duplicateElementCausesError() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> setContext(\"<global-method-security />\" + \"<global-method-security />\"));\n\t}\n\n\t// Expression configuration tests\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void expressionVoterAndAfterInvocationProviderUseSameExpressionHandlerInstance() throws Exception {\n\t\tsetContext(\"<global-method-security pre-post-annotations='enabled'/>\" + ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\tAffirmativeBased adm = (AffirmativeBased) this.appContext.getBeansOfType(AffirmativeBased.class)\n\t\t\t.values()\n\t\t\t.toArray()[0];\n\t\tList voters = (List) FieldUtils.getFieldValue(adm, \"decisionVoters\");\n\t\tPreInvocationAuthorizationAdviceVoter mev = (PreInvocationAuthorizationAdviceVoter) voters.get(0);\n\t\tMethodSecurityMetadataSourceAdvisor msi = (MethodSecurityMetadataSourceAdvisor) this.appContext\n\t\t\t.getBeansOfType(MethodSecurityMetadataSourceAdvisor.class)\n\t\t\t.values()\n\t\t\t.toArray()[0];\n\t\tAfterInvocationProviderManager pm = (AfterInvocationProviderManager) ((MethodSecurityInterceptor) msi\n\t\t\t.getAdvice()).getAfterInvocationManager();\n\t\tPostInvocationAdviceProvider aip = (PostInvocationAdviceProvider) pm.getProviders().get(0);\n\t\tassertThat(FieldUtils.getFieldValue(mev, \"preAdvice.expressionHandler\"))\n\t\t\t.isSameAs(FieldUtils.getFieldValue(aip, \"postAdvice.expressionHandler\"));\n\t}\n\n\t@Test\n\tpublic void accessIsDeniedForHasRoleExpression() {\n\t\t// @formatter:off\n\t\tsetContext(\"<global-method-security pre-post-annotations='enabled'/>\"\n\t\t\t\t+ \"<b:bean id='target' class='org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl'/>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tSecurityContextHolder.getContext().setAuthentication(this.bob);\n\t\tthis.target = (BusinessService) this.appContext.getBean(\"target\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.target::someAdminMethod);\n\t}\n\n\t@Test\n\tpublic void beanNameExpressionPropertyIsSupported() {\n\t\t// @formatter:off\n\t\tsetContext(\"<global-method-security pre-post-annotations='enabled' proxy-target-class='true'/>\"\n\t\t\t\t+ \"<b:bean id='number' class='java.lang.Integer'>\"\n\t\t\t\t+ \"    <b:constructor-arg value='1294'/>\"\n\t\t\t\t+ \"</b:bean>\"\n\t\t\t\t+ \"<b:bean id='target' class='org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl'/>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tSecurityContextHolder.getContext().setAuthentication(this.bob);\n\t\tExpressionProtectedBusinessServiceImpl target = (ExpressionProtectedBusinessServiceImpl) this.appContext\n\t\t\t.getBean(\"target\");\n\t\ttarget.methodWithBeanNamePropertyAccessExpression(\"x\");\n\t}\n\n\t@Test\n\tpublic void preAndPostFilterAnnotationsWorkWithLists() {\n\t\t// @formatter:off\n\t\tsetContext(\"<global-method-security pre-post-annotations='enabled'/>\"\n\t\t\t\t+ \"<b:bean id='target' class='org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl'/>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tSecurityContextHolder.getContext().setAuthentication(this.bob);\n\t\tthis.target = (BusinessService) this.appContext.getBean(\"target\");\n\t\tList<String> arg = new ArrayList<>();\n\t\targ.add(\"joe\");\n\t\targ.add(\"bob\");\n\t\targ.add(\"sam\");\n\t\tList<?> result = this.target.methodReturningAList(arg);\n\t\t// Expression is (filterObject == name or filterObject == 'sam'), so \"joe\" should\n\t\t// be gone after pre-filter\n\t\t// PostFilter should remove sam from the return object\n\t\tassertThat(result).hasSize(1);\n\t\tassertThat(result.get(0)).isEqualTo(\"bob\");\n\t}\n\n\t@Test\n\tpublic void prePostFilterAnnotationWorksWithArrays() {\n\t\t// @formatter:off\n\t\tsetContext(\"<global-method-security pre-post-annotations='enabled'/>\"\n\t\t\t\t+ \"<b:bean id='target' class='org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl'/>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tSecurityContextHolder.getContext().setAuthentication(this.bob);\n\t\tthis.target = (BusinessService) this.appContext.getBean(\"target\");\n\t\tObject[] arg = new String[] { \"joe\", \"bob\", \"sam\" };\n\t\tObject[] result = this.target.methodReturningAnArray(arg);\n\t\tassertThat(result).hasSize(1);\n\t\tassertThat(result[0]).isEqualTo(\"bob\");\n\t}\n\n\t// SEC-1392\n\t@Test\n\tpublic void customPermissionEvaluatorIsSupported() {\n\t\t// @formatter:off\n\t\tsetContext(\"<global-method-security pre-post-annotations='enabled'>\"\n\t\t\t\t+ \"   <expression-handler ref='expressionHandler'/>\"\n\t\t\t\t+ \"</global-method-security>\"\n\t\t\t\t+ \"<b:bean id='expressionHandler' class='org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler'>\"\n\t\t\t\t+ \"   <b:property name='permissionEvaluator' ref='myPermissionEvaluator'/>\"\n\t\t\t\t+ \"</b:bean>\"\n\t\t\t\t+ \"<b:bean id='myPermissionEvaluator' class='org.springframework.security.config.method.TestPermissionEvaluator'/>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t}\n\n\t// SEC-1450\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void genericsAreMatchedByProtectPointcut() {\n\t\t// @formatter:off\n\t\tsetContext(\n\t\t\t\t\"<b:bean id='target' class='org.springframework.security.config.method.GlobalMethodSecurityBeanDefinitionParserTests$ConcreteFoo'/>\"\n\t\t\t\t\t\t+ \"<global-method-security>\"\n\t\t\t\t\t\t+ \"   <protect-pointcut expression='execution(* org..*Foo.foo(..))' access='ROLE_USER'/>\"\n\t\t\t\t\t\t+ \"</global-method-security>\"\n\t\t\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tFoo foo = (Foo) this.appContext.getBean(\"target\");\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(() -> foo.foo(new SecurityConfig(\"A\")));\n\t}\n\n\t// SEC-1448\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void genericsMethodArgumentNamesAreResolved() {\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean id='target' class='\" + ConcreteFoo.class.getName() + \"'/>\"\n\t\t\t\t+ \"<global-method-security pre-post-annotations='enabled'/>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tSecurityContextHolder.getContext().setAuthentication(this.bob);\n\t\tFoo foo = (Foo) this.appContext.getBean(\"target\");\n\t\tfoo.foo(new SecurityConfig(\"A\"));\n\t}\n\n\t@Test\n\tpublic void runAsManagerIsSetCorrectly() throws Exception {\n\t\tStaticApplicationContext parent = new StaticApplicationContext();\n\t\tMutablePropertyValues props = new MutablePropertyValues();\n\t\tprops.addPropertyValue(\"key\", \"blah\");\n\t\tparent.registerSingleton(\"runAsMgr\", RunAsManagerImpl.class, props);\n\t\tparent.refresh();\n\t\tsetContext(\"<global-method-security run-as-manager-ref='runAsMgr'/>\" + ConfigTestUtils.AUTH_PROVIDER_XML,\n\t\t\t\tparent);\n\t\tRunAsManagerImpl ram = (RunAsManagerImpl) this.appContext.getBean(\"runAsMgr\");\n\t\tMethodSecurityMetadataSourceAdvisor msi = (MethodSecurityMetadataSourceAdvisor) this.appContext\n\t\t\t.getBeansOfType(MethodSecurityMetadataSourceAdvisor.class)\n\t\t\t.values()\n\t\t\t.toArray()[0];\n\t\tassertThat(ram).isSameAs(FieldUtils.getFieldValue(msi.getAdvice(), \"runAsManager\"));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void supportsExternalMetadataSource() {\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean id='target' class='\" + ConcreteFoo.class.getName() + \"'/>\"\n\t\t\t\t+ \"<method-security-metadata-source id='mds'>\"\n\t\t\t\t+ \"      <protect method='\" + Foo.class.getName() + \".foo' access='ROLE_ADMIN'/>\"\n\t\t\t\t+ \"</method-security-metadata-source>\"\n\t\t\t\t+ \"<global-method-security pre-post-annotations='enabled' metadata-source-ref='mds'/>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\t// External MDS should take precedence over PreAuthorize\n\t\tSecurityContextHolder.getContext().setAuthentication(this.bob);\n\t\tFoo foo = (Foo) this.appContext.getBean(\"target\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> foo.foo(new SecurityConfig(\"A\")));\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.unauthenticated(\"admin\", \"password\"));\n\t\tfoo.foo(new SecurityConfig(\"A\"));\n\t}\n\n\t@Test\n\tpublic void supportsCustomAuthenticationManager() {\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean id='target' class='\" + ConcreteFoo.class.getName() + \"'/>\"\n\t\t\t\t+ \"<method-security-metadata-source id='mds'>\"\n\t\t\t\t+ \"      <protect method='\" + Foo.class.getName() + \".foo' access='ROLE_ADMIN'/>\"\n\t\t\t\t+ \"</method-security-metadata-source>\"\n\t\t\t\t+ \"<global-method-security pre-post-annotations='enabled' metadata-source-ref='mds' authentication-manager-ref='customAuthMgr'/>\"\n\t\t\t\t+ \"<b:bean id='customAuthMgr' class='org.springframework.security.config.method.GlobalMethodSecurityBeanDefinitionParserTests$CustomAuthManager'>\"\n\t\t\t\t+ \"      <b:constructor-arg value='authManager'/>\"\n\t\t\t\t+ \"</b:bean>\"\n\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tSecurityContextHolder.getContext().setAuthentication(this.bob);\n\t\tFoo foo = (Foo) this.appContext.getBean(\"target\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> foo.foo(new SecurityConfig(\"A\")));\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.unauthenticated(\"admin\", \"password\"));\n\t\tfoo.foo(new SecurityConfig(\"A\"));\n\t}\n\n\tprivate void setContext(String context) {\n\t\tthis.appContext = new InMemoryXmlApplicationContext(context);\n\t}\n\n\tprivate void setContext(String context, ApplicationContext parent) {\n\t\tthis.appContext = new InMemoryXmlApplicationContext(context, parent);\n\t}\n\n\tstatic class CustomAuthManager implements AuthenticationManager, ApplicationContextAware {\n\n\t\tprivate String beanName;\n\n\t\tprivate AuthenticationManager authenticationManager;\n\n\t\tCustomAuthManager(String beanName) {\n\t\t\tthis.beanName = beanName;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\treturn this.authenticationManager.authenticate(authentication);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\t\tthis.authenticationManager = applicationContext.getBean(this.beanName, AuthenticationManager.class);\n\t\t}\n\n\t}\n\n\tinterface Foo<T extends ConfigAttribute> {\n\n\t\tvoid foo(T action);\n\n\t}\n\n\tpublic static class ConcreteFoo implements Foo<SecurityConfig> {\n\n\t\t@Override\n\t\t@PreAuthorize(\"#action.attribute == 'A'\")\n\t\tpublic void foo(SecurityConfig action) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecoratorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.TestBusinessBean;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = \"classpath:org/springframework/security/config/method-security.xml\")\npublic class InterceptMethodsBeanDefinitionDecoratorTests implements ApplicationContextAware {\n\n\t@Autowired\n\t@Qualifier(\"target\")\n\tprivate TestBusinessBean target;\n\n\t@Autowired\n\t@Qualifier(\"transactionalTarget\")\n\tprivate TestBusinessBean transactionalTarget;\n\n\t@Autowired\n\t@Qualifier(\"targetAuthorizationManager\")\n\tprivate TestBusinessBean targetAuthorizationManager;\n\n\t@Autowired\n\t@Qualifier(\"transactionalTargetAuthorizationManager\")\n\tprivate TestBusinessBean transactionalTargetAuthorizationManager;\n\n\t@Autowired\n\t@Qualifier(\"targetCustomAuthorizationManager\")\n\tprivate TestBusinessBean targetCustomAuthorizationManager;\n\n\t@Autowired\n\tprivate AuthorizationManager<MethodInvocation> mockAuthorizationManager;\n\n\tprivate ApplicationContext appContext;\n\n\t@BeforeAll\n\tpublic static void loadContext() {\n\t\t// Set value for placeholder\n\t\tSystem.setProperty(\"admin.role\", \"ROLE_ADMIN\");\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void targetDoesntLoseApplicationListenerInterface() {\n\t\tassertThat(this.appContext.getBeansOfType(ApplicationListener.class)).isNotEmpty();\n\t\tassertThat(this.appContext.getBeanNamesForType(ApplicationListener.class)).isNotEmpty();\n\t\tthis.appContext.publishEvent(new AuthenticationSuccessEvent(new TestingAuthenticationToken(\"user\", \"\")));\n\t\tassertThat(this.target).isInstanceOf(ApplicationListener.class);\n\t\tassertThat(this.targetAuthorizationManager).isInstanceOf(ApplicationListener.class);\n\t\tassertThat(this.targetCustomAuthorizationManager).isInstanceOf(ApplicationListener.class);\n\t}\n\n\t@Test\n\tpublic void targetShouldAllowUnprotectedMethodInvocationWithNoContext() {\n\t\tthis.target.unprotected();\n\t}\n\n\t@Test\n\tpublic void targetShouldPreventProtectedMethodInvocationWithNoContext() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(this.target::doSomething);\n\t}\n\n\t@Test\n\tpublic void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tthis.target.doSomething();\n\t}\n\n\t@Test\n\tpublic void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_SOMEOTHERROLE\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.target::doSomething);\n\t}\n\n\t@Test\n\tpublic void transactionalMethodsShouldBeSecured() {\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(this.transactionalTarget::doSomething);\n\t}\n\n\t@Test\n\tpublic void targetAuthorizationManagerShouldAllowUnprotectedMethodInvocationWithNoContext() {\n\t\tthis.targetAuthorizationManager.unprotected();\n\t}\n\n\t@Test\n\tpublic void targetAuthorizationManagerShouldPreventProtectedMethodInvocationWithNoContext() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(this.targetAuthorizationManager::doSomething);\n\t}\n\n\t@Test\n\tpublic void targetAuthorizationManagerShouldAllowProtectedMethodInvocationWithCorrectRole() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tthis.targetAuthorizationManager.doSomething();\n\t}\n\n\t@Test\n\tpublic void targetAuthorizationManagerShouldPreventProtectedMethodInvocationWithIncorrectRole() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_SOMEOTHERROLE\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.targetAuthorizationManager::doSomething);\n\t}\n\n\t@Test\n\tpublic void transactionalAuthorizationManagerMethodsShouldBeSecured() {\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t.isThrownBy(this.transactionalTargetAuthorizationManager::doSomething);\n\t}\n\n\t@Test\n\tpublic void targetCustomAuthorizationManagerUsed() {\n\t\tgiven(this.mockAuthorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(true));\n\t\tthis.targetCustomAuthorizationManager.doSomething();\n\t\tverify(this.mockAuthorizationManager).authorize(any(), any());\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.appContext = applicationContext;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/Jsr250AnnotationDrivenBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.annotation.BusinessService;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.ConfigTestUtils;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n */\npublic class Jsr250AnnotationDrivenBeanDefinitionParserTests {\n\n\tprivate InMemoryXmlApplicationContext appContext;\n\n\tprivate BusinessService target;\n\n\t@BeforeEach\n\tpublic void loadContext() {\n\t\t// @formatter:off\n\t\tthis.appContext = new InMemoryXmlApplicationContext(\n\t\t\t\t\"<b:bean id='target' class='org.springframework.security.access.annotation.Jsr250BusinessServiceImpl'/>\"\n\t\t\t\t\t\t+ \"<global-method-security jsr250-annotations='enabled'/>\"\n\t\t\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\t// @formatter:on\n\t\tthis.target = (BusinessService) this.appContext.getBean(\"target\");\n\t}\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t}\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void targetShouldPreventProtectedMethodInvocationWithNoContext() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.target.someUserMethod1());\n\t}\n\n\t@Test\n\tpublic void permitAllShouldBeDefaultAttribute() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tthis.target.someOther(0);\n\t}\n\n\t@Test\n\tpublic void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tthis.target.someUserMethod1();\n\t}\n\n\t@Test\n\tpublic void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_SOMEOTHERROLE\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.target::someAdminMethod);\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleAddsDefaultPrefix() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tthis.target.rolesAllowedUser();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.lang.NonNull;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.annotation.BusinessService;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.config.annotation.method.configuration.MethodSecurityService;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithAnonymousUser;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class MethodSecurityBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests\";\n\n\tprivate final UsernamePasswordAuthenticationToken bob = UsernamePasswordAuthenticationToken.unauthenticated(\"bob\",\n\t\t\t\"bobspassword\");\n\n\t@Autowired(required = false)\n\tMethodSecurityService methodSecurityService;\n\n\t@Autowired(required = false)\n\tBusinessService businessService;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void preAuthorizeWhenRoleAdminThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@Test\n\tpublic void configureWhenAspectJThenRegistersAspects() {\n\t\tthis.spring.configLocations(xml(\"AspectJMethodSecurityServiceEnabled\")).autowire();\n\t\tassertThat(this.spring.getContext().containsBean(\"preFilterAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"postFilterAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"preAuthorizeAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"postAuthorizeAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"securedAspect$0\")).isTrue();\n\t\tassertThat(this.spring.getContext().containsBean(\"annotationSecurityAspect$0\")).isFalse();\n\t}\n\n\t@WithAnonymousUser\n\t@Test\n\tpublic void preAuthorizePermitAllWhenRoleAnonymousThenPasses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tString result = this.methodSecurityService.preAuthorizePermitAll();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithAnonymousUser\n\t@Test\n\tpublic void preAuthorizeNotAnonymousWhenRoleAnonymousThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(this.methodSecurityService::preAuthorizeNotAnonymous)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void preAuthorizeNotAnonymousWhenRoleUserThenPasses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tthis.methodSecurityService.preAuthorizeNotAnonymous();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void securedWhenRoleUserThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabled\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void securedWhenRoleAdminThenPasses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tString result = this.methodSecurityService.secured();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void securedWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy\")).autowire();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tSecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"pass\"));\n\t\tstrategy.setContext(context);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)\n\t\t\t.withMessage(\"Access Denied\");\n\t\tverify(strategy).getContext();\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void securedUserWhenRoleAdminThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabled\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void securedUserWhenRoleUserThenPasses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tString result = this.methodSecurityService.securedUser();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void preAuthorizeAdminWhenRoleUserThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void preAuthorizeAdminWhenRoleAdminThenPasses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tthis.methodSecurityService.preAuthorizeAdmin();\n\t}\n\n\t@Test\n\tpublic void preAuthorizeWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy\")).autowire();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tSecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"pass\"));\n\t\tstrategy.setContext(context);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin)\n\t\t\t.withMessage(\"Access Denied\");\n\t\tverify(strategy).getContext();\n\t}\n\n\t@WithMockUser(authorities = \"PREFIX_ADMIN\")\n\t@Test\n\tpublic void preAuthorizeAdminWhenRoleAdminAndCustomPrefixThenPasses() {\n\t\tthis.spring.configLocations(xml(\"CustomGrantedAuthorityDefaults\")).autowire();\n\t\tthis.methodSecurityService.preAuthorizeAdmin();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"CustomPermissionEvaluator\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.postHasPermission(\"deny\"))\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postHasPermissionWhenParameterIsGrantThenPasses() {\n\t\tthis.spring.configLocations(xml(\"CustomPermissionEvaluator\")).autowire();\n\t\tString result = this.methodSecurityService.postHasPermission(\"grant\");\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postAnnotationWhenParameterIsNotGrantThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.postAnnotation(\"deny\"))\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postAnnotationWhenParameterIsGrantThenPasses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tString result = this.methodSecurityService.postAnnotation(\"grant\");\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void preFilterWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy\")).autowire();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tSecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"pass\"));\n\t\tstrategy.setContext(context);\n\t\tList<String> result = this.methodSecurityService\n\t\t\t.preFilterByUsername(new ArrayList<>(Arrays.asList(\"user\", \"bob\", \"joe\")));\n\t\tassertThat(result).containsExactly(\"user\");\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void postFilterWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy\")).autowire();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tSecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"pass\"));\n\t\tstrategy.setContext(context);\n\t\tList<String> result = this.methodSecurityService\n\t\t\t.postFilterByUsername(new ArrayList<>(Arrays.asList(\"user\", \"bob\", \"joe\")));\n\t\tassertThat(result).containsExactly(\"user\");\n\t\tverify(strategy).getContext();\n\t}\n\n\t@WithMockUser(\"bob\")\n\t@Test\n\tpublic void methodReturningAListWhenPrePostFiltersConfiguredThenFiltersList() {\n\t\tthis.spring.configLocations(xml(\"BusinessService\")).autowire();\n\t\tList<String> names = new ArrayList<>();\n\t\tnames.add(\"bob\");\n\t\tnames.add(\"joe\");\n\t\tnames.add(\"sam\");\n\t\tList<?> result = this.businessService.methodReturningAList(names);\n\t\tassertThat(result).hasSize(1);\n\t\tassertThat(result.get(0)).isEqualTo(\"bob\");\n\t}\n\n\t@WithMockUser(\"bob\")\n\t@Test\n\tpublic void methodReturningAnArrayWhenPostFilterConfiguredThenFiltersArray() {\n\t\tthis.spring.configLocations(xml(\"BusinessService\")).autowire();\n\t\tList<String> names = new ArrayList<>();\n\t\tnames.add(\"bob\");\n\t\tnames.add(\"joe\");\n\t\tnames.add(\"sam\");\n\t\tObject[] result = this.businessService.methodReturningAnArray(names.toArray());\n\t\tassertThat(result).hasSize(1);\n\t\tassertThat(result[0]).isEqualTo(\"bob\");\n\t}\n\n\t@WithMockUser(\"bob\")\n\t@Test\n\tpublic void securedUserWhenCustomBeforeAdviceConfiguredAndNameBobThenPasses() {\n\t\tthis.spring.configLocations(xml(\"CustomAuthorizationManagerBeforeAdvice\")).autowire();\n\t\tString result = this.methodSecurityService.securedUser();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser(\"joe\")\n\t@Test\n\tpublic void securedUserWhenCustomBeforeAdviceConfiguredAndNameNotBobThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"CustomAuthorizationManagerBeforeAdvice\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser(\"bob\")\n\t@Test\n\tpublic void securedUserWhenCustomAfterAdviceConfiguredAndNameBobThenGranted() {\n\t\tthis.spring.configLocations(xml(\"CustomAuthorizationManagerAfterAdvice\")).autowire();\n\t\tString result = this.methodSecurityService.securedUser();\n\t\tassertThat(result).isEqualTo(\"granted\");\n\t}\n\n\t@WithMockUser(\"joe\")\n\t@Test\n\tpublic void securedUserWhenCustomAfterAdviceConfiguredAndNameNotBobThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"CustomAuthorizationManagerAfterAdvice\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)\n\t\t\t.withMessage(\"Access Denied for User 'joe'\");\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void jsr250WhenRoleAdminThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabled\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@Test\n\tpublic void jsr250WhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy\")).autowire();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tSecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"pass\"));\n\t\tstrategy.setContext(context);\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(this.methodSecurityService::jsr250RolesAllowed)\n\t\t\t.withMessage(\"Access Denied\");\n\t\tverify(strategy).getContext();\n\t}\n\n\t@WithAnonymousUser\n\t@Test\n\tpublic void jsr250PermitAllWhenRoleAnonymousThenPasses() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabled\")).autowire();\n\t\tString result = this.methodSecurityService.jsr250PermitAll();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void rolesAllowedUserWhenRoleAdminThenAccessDeniedException() {\n\t\tthis.spring.configLocations(xml(\"BusinessService\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.businessService::rolesAllowedUser)\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void rolesAllowedUserWhenRoleUserThenPasses() {\n\t\tthis.spring.configLocations(xml(\"BusinessService\")).autowire();\n\t\tthis.businessService.rolesAllowedUser();\n\t}\n\n\t@WithMockUser(roles = { \"ADMIN\", \"USER\" })\n\t@Test\n\tpublic void manyAnnotationsWhenMeetsConditionsThenReturnsFilteredList() throws Exception {\n\t\tList<String> names = Arrays.asList(\"harold\", \"jonathan\", \"pete\", \"bo\");\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabled\")).autowire();\n\t\tList<String> filtered = this.methodSecurityService.manyAnnotations(new ArrayList<>(names));\n\t\tassertThat(filtered).hasSize(2);\n\t\tassertThat(filtered).containsExactly(\"harold\", \"jonathan\");\n\t}\n\n\t// gh-4003\n\t// gh-4103\n\t@WithMockUser\n\t@Test\n\tpublic void manyAnnotationsWhenUserThenFails() {\n\t\tList<String> names = Arrays.asList(\"harold\", \"jonathan\", \"pete\", \"bo\");\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabled\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void manyAnnotationsWhenShortListThenFails() {\n\t\tList<String> names = Arrays.asList(\"harold\", \"jonathan\", \"pete\");\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabled\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));\n\t}\n\n\t@WithMockUser(roles = \"ADMIN\")\n\t@Test\n\tpublic void manyAnnotationsWhenAdminThenFails() {\n\t\tList<String> names = Arrays.asList(\"harold\", \"jonathan\", \"pete\", \"bo\");\n\t\tthis.spring.configLocations(xml(\"MethodSecurityServiceEnabled\")).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));\n\t}\n\n\t// gh-3183\n\t@Test\n\tpublic void repeatedAnnotationsWhenPresentThenFails() {\n\t\tthis.spring.configLocations(xml(\"MethodSecurityService\")).autowire();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.methodSecurityService.repeatedAnnotations());\n\t}\n\n\t// gh-3183\n\t@Test\n\tpublic void repeatedJsr250AnnotationsWhenPresentThenFails() {\n\t\tthis.spring.configLocations(xml(\"Jsr250\")).autowire();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.businessService.repeatedAnnotations());\n\t}\n\n\t// gh-3183\n\t@Test\n\tpublic void repeatedSecuredAnnotationsWhenPresentThenFails() {\n\t\tthis.spring.configLocations(xml(\"Secured\")).autowire();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.businessService.repeatedAnnotations());\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void supportsMethodArgumentsInPointcut() {\n\t\tthis.spring.configLocations(xml(\"ProtectPointcut\")).autowire();\n\t\tthis.businessService.someOther(0);\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.businessService.someOther(\"somestring\"));\n\t}\n\n\t@Test\n\tpublic void supportsBooleanPointcutExpressions() {\n\t\tthis.spring.configLocations(xml(\"ProtectPointcutBoolean\")).autowire();\n\t\tthis.businessService.someOther(\"somestring\");\n\t\t// All others should require ROLE_USER\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.businessService.someOther(0));\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\")));\n\t\tthis.businessService.someOther(0);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\tprivate static String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tstatic class MyPermissionEvaluator implements PermissionEvaluator {\n\n\t\t@Override\n\t\tpublic boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n\t\t\treturn \"grant\".equals(targetDomainObject);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\t\tObject permission) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t}\n\n\tstatic class MyAuthorizationManager implements AuthorizationManager<MethodInvocation> {\n\n\t\t@Override\n\t\tpublic AuthorizationResult authorize(\n\t\t\t\tSupplier<? extends @org.jspecify.annotations.Nullable Authentication> authentication,\n\t\t\t\tMethodInvocation object) {\n\t\t\treturn new AuthorizationDecision(\"bob\".equals(authentication.get().getName()));\n\t\t}\n\n\t}\n\n\tstatic class MyAdvice implements MethodInterceptor {\n\n\t\t@Nullable\n\t\t@Override\n\t\tpublic Object invoke(@NonNull MethodInvocation invocation) {\n\t\t\tAuthentication auth = SecurityContextHolder.getContext().getAuthentication();\n\t\t\tif (\"bob\".equals(auth.getName())) {\n\t\t\t\treturn \"granted\";\n\t\t\t}\n\t\t\tthrow new AccessDeniedException(\"Access Denied for User '\" + auth.getName() + \"'\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/PreAuthorizeAdminRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * @author Rob Winch\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('ADMIN')\")\npublic @interface PreAuthorizeAdminRole {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/PreAuthorizeServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\n/**\n * @author Rob Winch\n *\n */\npublic class PreAuthorizeServiceImpl {\n\n\t@PreAuthorizeAdminRole\n\tpublic void preAuthorizeAdminRole() {\n\t}\n\n\t@ContactPermission\n\tpublic void contactPermission(Contact contact) {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/PreAuthorizeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class PreAuthorizeTests {\n\n\t@Autowired\n\tPreAuthorizeServiceImpl service;\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void preAuthorizeAdminRoleDenied() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\"));\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.service::preAuthorizeAdminRole);\n\t}\n\n\t@Test\n\tpublic void preAuthorizeAdminRoleGranted() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_ADMIN\"));\n\t\tthis.service.preAuthorizeAdminRole();\n\t}\n\n\t@Test\n\tpublic void preAuthorizeContactPermissionGranted() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_ADMIN\"));\n\t\tthis.service.contactPermission(new Contact(\"user\"));\n\t}\n\n\t@Test\n\tpublic void preAuthorizeContactPermissionDenied() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_ADMIN\"));\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.service.contactPermission(new Contact(\"admin\")));\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/Sec2196Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n *\n */\npublic class Sec2196Tests {\n\n\tprivate ConfigurableApplicationContext context;\n\n\t@Test\n\tpublic void genericMethodsProtected() {\n\t\tloadContext(\"<global-method-security secured-annotations=\\\"enabled\\\" pre-post-annotations=\\\"enabled\\\"/>\"\n\t\t\t\t+ \"<b:bean class='\" + Service.class.getName() + \"'/>\");\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"test\", \"pass\", \"ROLE_USER\"));\n\t\tService service = this.context.getBean(Service.class);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> service.save(new User()));\n\t}\n\n\t@Test\n\tpublic void genericMethodsAllowed() {\n\t\tloadContext(\"<global-method-security secured-annotations=\\\"enabled\\\" pre-post-annotations=\\\"enabled\\\"/>\"\n\t\t\t\t+ \"<b:bean class='\" + Service.class.getName() + \"'/>\");\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"test\", \"pass\", \"saveUsers\"));\n\t\tService service = this.context.getBean(Service.class);\n\t\tservice.save(new User());\n\t}\n\n\tprivate void loadContext(String context) {\n\t\tthis.context = new InMemoryXmlApplicationContext(context);\n\t}\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t\tthis.context = null;\n\t\t}\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\tpublic static class Service {\n\n\t\t@PreAuthorize(\"hasAuthority('saveUsers')\")\n\t\tpublic <T extends User> T save(T dto) {\n\t\t\treturn dto;\n\t\t}\n\n\t}\n\n\tstatic class User {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/SecuredAdminRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.springframework.security.access.annotation.Secured;\n\n/**\n * @author Rob Winch\n *\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Secured(\"ROLE_ADMIN\")\npublic @interface SecuredAdminRole {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/SecuredAnnotationDrivenBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.annotation.BusinessService;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.ConfigTestUtils;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Ben Alex\n */\npublic class SecuredAnnotationDrivenBeanDefinitionParserTests {\n\n\tprivate InMemoryXmlApplicationContext appContext;\n\n\tprivate BusinessService target;\n\n\t@BeforeEach\n\tpublic void loadContext() {\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.appContext = new InMemoryXmlApplicationContext(\n\t\t\t\t\"<b:bean id='target' class='org.springframework.security.access.annotation.BusinessServiceImpl'/>\"\n\t\t\t\t\t\t+ \"<global-method-security secured-annotations='enabled'/>\"\n\t\t\t\t\t\t+ ConfigTestUtils.AUTH_PROVIDER_XML);\n\t\tthis.target = (BusinessService) this.appContext.getBean(\"target\");\n\t}\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t}\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void targetShouldPreventProtectedMethodInvocationWithNoContext() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(this.target::someUserMethod1);\n\t}\n\n\t@Test\n\tpublic void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tthis.target.someUserMethod1();\n\t}\n\n\t@Test\n\tpublic void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_SOMEOTHER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.target::someAdminMethod);\n\t}\n\n\t// SEC-1387\n\t@Test\n\tpublic void targetIsSerializableBeforeUse() throws Exception {\n\t\tBusinessService chompedTarget = (BusinessService) serializeAndDeserialize(this.target);\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(chompedTarget::someAdminMethod);\n\t}\n\n\t@Test\n\tpublic void targetIsSerializableAfterUse() throws Exception {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(this.target::someAdminMethod);\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(\"u\", \"p\", \"ROLE_A\"));\n\t\tBusinessService chompedTarget = (BusinessService) serializeAndDeserialize(this.target);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(chompedTarget::someAdminMethod);\n\t}\n\n\tprivate Object serializeAndDeserialize(Object o) throws IOException, ClassNotFoundException {\n\t\tByteArrayOutputStream baos = new ByteArrayOutputStream();\n\t\tObjectOutputStream oos = new ObjectOutputStream(baos);\n\t\toos.writeObject(o);\n\t\toos.flush();\n\t\tbaos.flush();\n\t\tbyte[] bytes = baos.toByteArray();\n\t\tByteArrayInputStream is = new ByteArrayInputStream(bytes);\n\t\tObjectInputStream ois = new ObjectInputStream(is);\n\t\tObject o2 = ois.readObject();\n\t\treturn o2;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/SecuredServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\n/**\n * @author Rob Winch\n *\n */\npublic class SecuredServiceImpl {\n\n\t@SecuredAdminRole\n\tpublic void securedAdminRole() {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/SecuredTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class SecuredTests {\n\n\t@Autowired\n\tSecuredServiceImpl service;\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void securedAdminRoleDenied() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\"));\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.service::securedAdminRole);\n\t}\n\n\t@Test\n\tpublic void securedAdminRoleGranted() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_ADMIN\"));\n\t\tthis.service.securedAdminRole();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/TestPermissionEvaluator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method;\n\nimport java.io.Serializable;\n\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.core.Authentication;\n\npublic class TestPermissionEvaluator implements PermissionEvaluator {\n\n\t@Override\n\tpublic boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\tObject permission) {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/configuration/Gh4020GlobalMethodSecurityConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method.configuration;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class Gh4020GlobalMethodSecurityConfigurationTests {\n\n\t@Autowired\n\tDenyAllService denyAll;\n\n\t// gh-4020\n\t@Test\n\tpublic void denyAll() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class).isThrownBy(this.denyAll::denyAll);\n\t}\n\n\t@Configuration\n\t@EnableGlobalMethodSecurity(prePostEnabled = true)\n\tstatic class SecurityConfig {\n\n\t\t@Bean\n\t\tPermissionEvaluator permissionEvaluator() {\n\t\t\treturn mock(PermissionEvaluator.class);\n\t\t}\n\n\t\t@Bean\n\t\tRoleHierarchy RoleHierarchy() {\n\t\t\treturn mock(RoleHierarchy.class);\n\t\t}\n\n\t\t@Bean\n\t\tAuthenticationTrustResolver trustResolver() {\n\t\t\treturn mock(AuthenticationTrustResolver.class);\n\t\t}\n\n\t\t@Autowired\n\t\tDenyAllService denyAll;\n\n\t}\n\n\t@Configuration\n\tstatic class ServiceConfig {\n\n\t\t@Bean\n\t\tDenyAllService denyAllService() {\n\t\t\treturn new DenyAllService();\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"denyAll\")\n\tstatic class DenyAllService {\n\n\t\tvoid denyAll() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/sec2136/JpaPermissionEvaluator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method.sec2136;\n\nimport java.io.Serializable;\n\nimport jakarta.persistence.EntityManager;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.core.Authentication;\n\n/**\n * @author Rob Winch\n *\n */\npublic class JpaPermissionEvaluator implements PermissionEvaluator {\n\n\t@Autowired\n\tprivate EntityManager entityManager;\n\n\tpublic JpaPermissionEvaluator() {\n\t\tSystem.out.println(\"initializing \" + this);\n\t}\n\n\t@Override\n\tpublic boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\tObject permission) {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/sec2136/Sec2136Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method.sec2136;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n/**\n * @author Rob Winch\n * @since 3.2\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(\"sec2136.xml\")\npublic class Sec2136Tests {\n\n\t@Test\n\tpublic void configurationLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/method/sec2499/Sec2499Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.method.sec2499;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.GenericXmlApplicationContext;\n\n/**\n * @author Rob Winch\n *\n */\npublic class Sec2499Tests {\n\n\tprivate GenericXmlApplicationContext parent;\n\n\tprivate GenericXmlApplicationContext child;\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.parent != null) {\n\t\t\tthis.parent.close();\n\t\t}\n\t\tif (this.child != null) {\n\t\t\tthis.child.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void methodExpressionHandlerInParentContextLoads() {\n\t\tthis.parent = new GenericXmlApplicationContext(\"org/springframework/security/config/method/sec2499/parent.xml\");\n\t\tthis.child = new GenericXmlApplicationContext();\n\t\tthis.child.load(\"org/springframework/security/config/method/sec2499/child.xml\");\n\t\tthis.child.setParent(this.parent);\n\t\tthis.child.refresh();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.oauth2.client;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ClientRegistrationsBeanDefinitionParser}.\n *\n * @author Ruby Hartono\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class ClientRegistrationsBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests\";\n\n\t// @formatter:off\n\tprivate static final String ISSUER_URI_XML_CONFIG = \"<b:beans xmlns:b=\\\"http://www.springframework.org/schema/beans\\\"\\n\"\n\t\t\t+ \"\t\txmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\"\\n\"\n\t\t\t+ \"\t\txmlns=\\\"http://www.springframework.org/schema/security\\\"\\n\"\n\t\t\t+ \"\t\txsi:schemaLocation=\\\"\\n\"\n\t\t\t+ \"\t\t\thttp://www.springframework.org/schema/security\\n\"\n\t\t\t+ \"\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\\n\"\n\t\t\t+ \"\t\t\thttp://www.springframework.org/schema/beans\\n\"\n\t\t\t+ \"\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\\\">\\n\"\n\t\t\t+ \"\\n\"\n\t\t\t+ \"\t<client-registrations>\\n\"\n\t\t\t+ \"\t\t<client-registration registration-id=\\\"google-login\\\" client-id=\\\"google-client-id\\\" \\n\"\n\t\t\t+ \"\t\t\t\t\t\t\t client-secret=\\\"google-client-secret\\\" provider-id=\\\"google\\\"/>\\n\"\n\t\t\t+ \"\t\t<provider provider-id=\\\"google\\\" issuer-uri=\\\"${issuer-uri}\\\"/>\\n\"\n\t\t\t+ \"\t</client-registrations>\\n\"\n\t\t\t+ \"\\n\"\n\t\t\t+ \"</b:beans>\\n\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String OIDC_DISCOVERY_RESPONSE = \"{\\n\"\n\t\t\t+ \"    \\\"authorization_endpoint\\\": \\\"https://example.com/o/oauth2/v2/auth\\\", \\n\"\n\t\t\t+ \"    \\\"claims_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"aud\\\", \\n\"\n\t\t\t+ \"        \\\"email\\\", \\n\"\n\t\t\t+ \"        \\\"email_verified\\\", \\n\"\n\t\t\t+ \"        \\\"exp\\\", \\n\"\n\t\t\t+ \"        \\\"family_name\\\", \\n\"\n\t\t\t+ \"        \\\"given_name\\\", \\n\"\n\t\t\t+ \"        \\\"iat\\\", \\n\"\n\t\t\t+ \"        \\\"iss\\\", \\n\"\n\t\t\t+ \"        \\\"locale\\\", \\n\"\n\t\t\t+ \"        \\\"name\\\", \\n\"\n\t\t\t+ \"        \\\"picture\\\", \\n\"\n\t\t\t+ \"        \\\"sub\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"code_challenge_methods_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"plain\\\", \\n\"\n\t\t\t+ \"        \\\"S256\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"id_token_signing_alg_values_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"RS256\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"issuer\\\": \\\"${issuer-uri}\\\", \\n\"\n\t\t\t+ \"    \\\"jwks_uri\\\": \\\"https://example.com/oauth2/v3/certs\\\", \\n\"\n\t\t\t+ \"    \\\"response_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"code\\\", \\n\"\n\t\t\t+ \"        \\\"token\\\", \\n\"\n\t\t\t+ \"        \\\"id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token\\\", \\n\"\n\t\t\t+ \"        \\\"code id_token\\\", \\n\"\n\t\t\t+ \"        \\\"token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"none\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"revocation_endpoint\\\": \\\"https://example.com/o/oauth2/revoke\\\", \\n\"\n\t\t\t+ \"    \\\"scopes_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"openid\\\", \\n\"\n\t\t\t+ \"        \\\"email\\\", \\n\"\n\t\t\t+ \"        \\\"profile\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"subject_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"public\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"grant_types_supported\\\" : [\\\"authorization_code\\\"], \\n\"\n\t\t\t+ \"    \\\"token_endpoint\\\": \\\"https://example.com/oauth2/v4/token\\\", \\n\"\n\t\t\t+ \"    \\\"token_endpoint_auth_methods_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"client_secret_post\\\", \\n\"\n\t\t\t+ \"        \\\"client_secret_basic\\\", \\n\"\n\t\t\t+ \"        \\\"none\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"userinfo_endpoint\\\": \\\"https://example.com/oauth2/v3/userinfo\\\"\\n\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tprivate MockWebServer server;\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tif (this.server != null) {\n\t\t\tthis.server.shutdown();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void parseWhenIssuerUriConfiguredThenRequestConfigFromIssuer() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString serverUrl = this.server.url(\"/\").toString();\n\t\tString discoveryResponse = OIDC_DISCOVERY_RESPONSE.replace(\"${issuer-uri}\", serverUrl);\n\t\tthis.server.enqueue(jsonResponse(discoveryResponse));\n\t\tString contextConfig = ISSUER_URI_XML_CONFIG.replace(\"${issuer-uri}\", serverUrl);\n\t\tthis.spring.context(contextConfig).autowire();\n\t\tassertThat(this.clientRegistrationRepository).isInstanceOf(InMemoryClientRegistrationRepository.class);\n\t\tClientRegistration googleRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google-login\");\n\t\tassertThat(googleRegistration).isNotNull();\n\t\tassertThat(googleRegistration.getRegistrationId()).isEqualTo(\"google-login\");\n\t\tassertThat(googleRegistration.getClientId()).isEqualTo(\"google-client-id\");\n\t\tassertThat(googleRegistration.getClientSecret()).isEqualTo(\"google-client-secret\");\n\t\tassertThat(googleRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(googleRegistration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(googleRegistration.getRedirectUri()).isEqualTo(\"{baseUrl}/{action}/oauth2/code/{registrationId}\");\n\t\tassertThat(googleRegistration.getScopes()).isEmpty();\n\t\tassertThat(googleRegistration.getClientName()).isEqualTo(serverUrl);\n\t\tProviderDetails googleProviderDetails = googleRegistration.getProviderDetails();\n\t\tassertThat(googleProviderDetails).isNotNull();\n\t\tassertThat(googleProviderDetails.getAuthorizationUri()).isEqualTo(\"https://example.com/o/oauth2/v2/auth\");\n\t\tassertThat(googleProviderDetails.getTokenUri()).isEqualTo(\"https://example.com/oauth2/v4/token\");\n\t\tassertThat(googleProviderDetails.getUserInfoEndpoint().getUri())\n\t\t\t.isEqualTo(\"https://example.com/oauth2/v3/userinfo\");\n\t\tassertThat(googleProviderDetails.getUserInfoEndpoint().getAuthenticationMethod())\n\t\t\t.isEqualTo(AuthenticationMethod.HEADER);\n\t\tassertThat(googleProviderDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(\"sub\");\n\t\tassertThat(googleProviderDetails.getJwkSetUri()).isEqualTo(\"https://example.com/oauth2/v3/certs\");\n\t\tassertThat(googleProviderDetails.getIssuerUri()).isEqualTo(serverUrl);\n\t}\n\n\t@Test\n\tpublic void parseWhenMultipleClientsConfiguredThenAvailableInRepository() {\n\t\tthis.spring.configLocations(ClientRegistrationsBeanDefinitionParserTests.xml(\"MultiClientRegistration\"))\n\t\t\t.autowire();\n\t\tassertThat(this.clientRegistrationRepository).isInstanceOf(InMemoryClientRegistrationRepository.class);\n\t\tClientRegistration googleRegistration = this.clientRegistrationRepository.findByRegistrationId(\"google-login\");\n\t\tassertThat(googleRegistration).isNotNull();\n\t\tassertThat(googleRegistration.getRegistrationId()).isEqualTo(\"google-login\");\n\t\tassertThat(googleRegistration.getClientId()).isEqualTo(\"google-client-id\");\n\t\tassertThat(googleRegistration.getClientSecret()).isEqualTo(\"google-client-secret\");\n\t\tassertThat(googleRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(googleRegistration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(googleRegistration.getRedirectUri()).isEqualTo(\"{baseUrl}/login/oauth2/code/{registrationId}\");\n\t\tassertThat(googleRegistration.getScopes())\n\t\t\t.isEqualTo(StringUtils.commaDelimitedListToSet(\"openid,profile,email\"));\n\t\tassertThat(googleRegistration.getClientName()).isEqualTo(\"Google\");\n\t\tProviderDetails googleProviderDetails = googleRegistration.getProviderDetails();\n\t\tassertThat(googleProviderDetails).isNotNull();\n\t\tassertThat(googleProviderDetails.getAuthorizationUri())\n\t\t\t.isEqualTo(\"https://accounts.google.com/o/oauth2/v2/auth\");\n\t\tassertThat(googleProviderDetails.getTokenUri()).isEqualTo(\"https://www.googleapis.com/oauth2/v4/token\");\n\t\tassertThat(googleProviderDetails.getUserInfoEndpoint().getUri())\n\t\t\t.isEqualTo(\"https://www.googleapis.com/oauth2/v3/userinfo\");\n\t\tassertThat(googleProviderDetails.getUserInfoEndpoint().getAuthenticationMethod())\n\t\t\t.isEqualTo(AuthenticationMethod.HEADER);\n\t\tassertThat(googleProviderDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(\"sub\");\n\t\tassertThat(googleProviderDetails.getJwkSetUri()).isEqualTo(\"https://www.googleapis.com/oauth2/v3/certs\");\n\t\tassertThat(googleProviderDetails.getIssuerUri()).isEqualTo(\"https://accounts.google.com\");\n\t\tClientRegistration githubRegistration = this.clientRegistrationRepository.findByRegistrationId(\"github-login\");\n\t\tassertThat(githubRegistration).isNotNull();\n\t\tassertThat(githubRegistration.getRegistrationId()).isEqualTo(\"github-login\");\n\t\tassertThat(githubRegistration.getClientId()).isEqualTo(\"github-client-id\");\n\t\tassertThat(githubRegistration.getClientSecret()).isEqualTo(\"github-client-secret\");\n\t\tassertThat(githubRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(githubRegistration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(githubRegistration.getRedirectUri()).isEqualTo(\"{baseUrl}/login/oauth2/code/{registrationId}\");\n\t\tassertThat(googleRegistration.getScopes())\n\t\t\t.isEqualTo(StringUtils.commaDelimitedListToSet(\"openid,profile,email\"));\n\t\tassertThat(githubRegistration.getClientName()).isEqualTo(\"Github\");\n\t\tProviderDetails githubProviderDetails = githubRegistration.getProviderDetails();\n\t\tassertThat(githubProviderDetails).isNotNull();\n\t\tassertThat(githubProviderDetails.getAuthorizationUri()).isEqualTo(\"https://github.com/login/oauth/authorize\");\n\t\tassertThat(githubProviderDetails.getTokenUri()).isEqualTo(\"https://github.com/login/oauth/access_token\");\n\t\tassertThat(githubProviderDetails.getUserInfoEndpoint().getUri()).isEqualTo(\"https://api.github.com/user\");\n\t\tassertThat(githubProviderDetails.getUserInfoEndpoint().getAuthenticationMethod())\n\t\t\t.isEqualTo(AuthenticationMethod.HEADER);\n\t\tassertThat(githubProviderDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(\"id\");\n\t}\n\n\t@Test\n\tpublic void parseWhenClientPlaceholdersThenResolvePlaceholders() {\n\t\tSystem.setProperty(\"oauth2.client.id\", \"github-client-id\");\n\t\tSystem.setProperty(\"oauth2.client.secret\", \"github-client-secret\");\n\n\t\tthis.spring.configLocations(xml(\"ClientPlaceholders\")).autowire();\n\n\t\tassertThat(this.clientRegistrationRepository).isInstanceOf(InMemoryClientRegistrationRepository.class);\n\n\t\tClientRegistration githubRegistration = this.clientRegistrationRepository.findByRegistrationId(\"github\");\n\t\tassertThat(githubRegistration.getClientId()).isEqualTo(\"github-client-id\");\n\t\tassertThat(githubRegistration.getClientSecret()).isEqualTo(\"github-client-secret\");\n\t}\n\n\tprivate static MockResponse jsonResponse(String json) {\n\t\treturn new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(json);\n\t}\n\n\tprivate static String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/oauth2/client/CommonOAuth2ProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.oauth2.client;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link CommonOAuth2Provider}.\n *\n * @author Phillip Webb\n */\npublic class CommonOAuth2ProviderTests {\n\n\tprivate static final String DEFAULT_REDIRECT_URL = \"{baseUrl}/{action}/oauth2/code/{registrationId}\";\n\n\t@Test\n\tpublic void getBuilderWhenGoogleShouldHaveGoogleSettings() {\n\t\tClientRegistration registration = build(CommonOAuth2Provider.GOOGLE);\n\t\tProviderDetails providerDetails = registration.getProviderDetails();\n\t\tassertThat(providerDetails.getAuthorizationUri()).isEqualTo(\"https://accounts.google.com/o/oauth2/v2/auth\");\n\t\tassertThat(providerDetails.getTokenUri()).isEqualTo(\"https://www.googleapis.com/oauth2/v4/token\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUri())\n\t\t\t.isEqualTo(\"https://www.googleapis.com/oauth2/v3/userinfo\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(IdTokenClaimNames.SUB);\n\t\tassertThat(providerDetails.getJwkSetUri()).isEqualTo(\"https://www.googleapis.com/oauth2/v3/certs\");\n\t\tassertThat(providerDetails.getIssuerUri()).isEqualTo(\"https://accounts.google.com\");\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);\n\t\tassertThat(registration.getScopes()).containsOnly(\"openid\", \"profile\", \"email\");\n\t\tassertThat(registration.getClientName()).isEqualTo(\"Google\");\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(\"123\");\n\t}\n\n\t@Test\n\tpublic void getBuilderWhenGitHubShouldHaveGitHubSettings() {\n\t\tClientRegistration registration = build(CommonOAuth2Provider.GITHUB);\n\t\tProviderDetails providerDetails = registration.getProviderDetails();\n\t\tassertThat(providerDetails.getAuthorizationUri()).isEqualTo(\"https://github.com/login/oauth/authorize\");\n\t\tassertThat(providerDetails.getTokenUri()).isEqualTo(\"https://github.com/login/oauth/access_token\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUri()).isEqualTo(\"https://api.github.com/user\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(\"id\");\n\t\tassertThat(providerDetails.getJwkSetUri()).isNull();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);\n\t\tassertThat(registration.getScopes()).containsOnly(\"read:user\");\n\t\tassertThat(registration.getClientName()).isEqualTo(\"GitHub\");\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(\"123\");\n\t}\n\n\t@Test\n\tpublic void getBuilderWhenFacebookShouldHaveFacebookSettings() {\n\t\tClientRegistration registration = build(CommonOAuth2Provider.FACEBOOK);\n\t\tProviderDetails providerDetails = registration.getProviderDetails();\n\t\tassertThat(providerDetails.getAuthorizationUri()).isEqualTo(\"https://www.facebook.com/v2.8/dialog/oauth\");\n\t\tassertThat(providerDetails.getTokenUri()).isEqualTo(\"https://graph.facebook.com/v2.8/oauth/access_token\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUri())\n\t\t\t.isEqualTo(\"https://graph.facebook.com/me?fields=id,name,email\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(\"id\");\n\t\tassertThat(providerDetails.getJwkSetUri()).isNull();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);\n\t\tassertThat(registration.getScopes()).containsOnly(\"public_profile\", \"email\");\n\t\tassertThat(registration.getClientName()).isEqualTo(\"Facebook\");\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(\"123\");\n\t}\n\n\t@Test\n\tpublic void getBuilderWhenOktaShouldHaveOktaSettings() {\n\t\tClientRegistration registration = builder(CommonOAuth2Provider.OKTA)\n\t\t\t.authorizationUri(\"https://example.com/auth\")\n\t\t\t.tokenUri(\"https://example.com/token\")\n\t\t\t.userInfoUri(\"https://example.com/info\")\n\t\t\t.jwkSetUri(\"https://example.com/jwkset\")\n\t\t\t.build();\n\t\tProviderDetails providerDetails = registration.getProviderDetails();\n\t\tassertThat(providerDetails.getAuthorizationUri()).isEqualTo(\"https://example.com/auth\");\n\t\tassertThat(providerDetails.getTokenUri()).isEqualTo(\"https://example.com/token\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUri()).isEqualTo(\"https://example.com/info\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(IdTokenClaimNames.SUB);\n\t\tassertThat(providerDetails.getJwkSetUri()).isEqualTo(\"https://example.com/jwkset\");\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);\n\t\tassertThat(registration.getScopes()).containsOnly(\"openid\", \"profile\", \"email\");\n\t\tassertThat(registration.getClientName()).isEqualTo(\"Okta\");\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(\"123\");\n\t}\n\n\t@Test\n\tpublic void getBuilderWhenXShouldHaveXSettings() {\n\t\tClientRegistration registration = build(CommonOAuth2Provider.X);\n\t\tProviderDetails providerDetails = registration.getProviderDetails();\n\t\tassertThat(providerDetails.getAuthorizationUri()).isEqualTo(\"https://x.com/i/oauth2/authorize\");\n\t\tassertThat(providerDetails.getTokenUri()).isEqualTo(\"https://api.x.com/2/oauth2/token\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUri()).isEqualTo(\"https://api.x.com/2/users/me\");\n\t\tassertThat(providerDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(\"username\");\n\t\tassertThat(providerDetails.getJwkSetUri()).isNull();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(registration.getRedirectUri()).isEqualTo(DEFAULT_REDIRECT_URL);\n\t\tassertThat(registration.getScopes()).containsOnly(\"users.read\", \"tweet.read\");\n\t\tassertThat(registration.getClientName()).isEqualTo(\"X\");\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(\"123\");\n\t}\n\n\tprivate ClientRegistration build(CommonOAuth2Provider provider) {\n\t\treturn builder(provider).build();\n\t}\n\n\tprivate ClientRegistration.Builder builder(CommonOAuth2Provider provider) {\n\t\treturn provider.getBuilder(\"123\").clientId(\"abcd\").clientSecret(\"secret\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/observation/SecurityObservationSettingsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.observation;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link SecurityObservationSettings}\n */\npublic class SecurityObservationSettingsTests {\n\n\t@Test\n\tvoid withDefaultsThenFilterOffAuthenticationOnAuthorizationOn() {\n\t\tSecurityObservationSettings defaults = SecurityObservationSettings.withDefaults().build();\n\t\tassertThat(defaults.shouldObserveRequests()).isFalse();\n\t\tassertThat(defaults.shouldObserveAuthentications()).isTrue();\n\t\tassertThat(defaults.shouldObserveAuthorizations()).isTrue();\n\t}\n\n\t@Test\n\tvoid noObservationsWhenConstructedThenAllOff() {\n\t\tSecurityObservationSettings defaults = SecurityObservationSettings.noObservations();\n\t\tassertThat(defaults.shouldObserveRequests()).isFalse();\n\t\tassertThat(defaults.shouldObserveAuthentications()).isFalse();\n\t\tassertThat(defaults.shouldObserveAuthorizations()).isFalse();\n\t}\n\n\t@Test\n\tvoid withDefaultsWhenExclusionsThenInstanceReflects() {\n\t\tSecurityObservationSettings defaults = SecurityObservationSettings.withDefaults()\n\t\t\t.shouldObserveAuthentications(false)\n\t\t\t.shouldObserveAuthorizations(false)\n\t\t\t.shouldObserveRequests(true)\n\t\t\t.build();\n\t\tassertThat(defaults.shouldObserveRequests()).isTrue();\n\t\tassertThat(defaults.shouldObserveAuthentications()).isFalse();\n\t\tassertThat(defaults.shouldObserveAuthorizations()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/provisioning/UserDetailsManagerResourceFactoryBeanPropertiesResourceITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.provisioning;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.provisioning.UserDetailsManager;\nimport org.springframework.security.util.InMemoryResource;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringExtension.class)\npublic class UserDetailsManagerResourceFactoryBeanPropertiesResourceITests {\n\n\t@Autowired\n\tUserDetailsManager users;\n\n\t@Test\n\tpublic void loadUserByUsernameWhenUserFoundThenNotNull() {\n\t\tassertThat(this.users.loadUserByUsername(\"user\")).isNotNull();\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tUserDetailsManagerResourceFactoryBean userDetailsService() {\n\t\t\treturn UserDetailsManagerResourceFactoryBean.fromResource(new InMemoryResource(\"user=password,ROLE_USER\"));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/provisioning/UserDetailsManagerResourceFactoryBeanPropertiesResourceLocationITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.provisioning;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.provisioning.UserDetailsManager;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringExtension.class)\npublic class UserDetailsManagerResourceFactoryBeanPropertiesResourceLocationITests {\n\n\t@Autowired\n\tUserDetailsManager users;\n\n\t@Test\n\tpublic void loadUserByUsernameWhenUserFoundThenNotNull() {\n\t\tassertThat(this.users.loadUserByUsername(\"user\")).isNotNull();\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tUserDetailsManagerResourceFactoryBean userDetailsService() {\n\t\t\treturn UserDetailsManagerResourceFactoryBean.fromResourceLocation(\"classpath:users.properties\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/provisioning/UserDetailsManagerResourceFactoryBeanStringITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.provisioning;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.provisioning.UserDetailsManager;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringExtension.class)\npublic class UserDetailsManagerResourceFactoryBeanStringITests {\n\n\t@Autowired\n\tUserDetailsManager users;\n\n\t@Test\n\tpublic void loadUserByUsernameWhenUserFoundThenNotNull() {\n\t\tassertThat(this.users.loadUserByUsername(\"user\")).isNotNull();\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tUserDetailsManagerResourceFactoryBean userDetailsService() {\n\t\t\treturn UserDetailsManagerResourceFactoryBean.fromString(\"user=password,ROLE_USER\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.saml2;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata;\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests for {@link RelyingPartyRegistrationsBeanDefinitionParser}.\n *\n * @author Marcus da Coregio\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class RelyingPartyRegistrationsBeanDefinitionParserTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParserTests\";\n\n\t// @formatter:off\n\tprivate static final String METADATA_LOCATION_XML_CONFIG = \"<b:beans xmlns:b=\\\"http://www.springframework.org/schema/beans\\\"\\n\" +\n\t\t\t\"         xmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\"\\n\" +\n\t\t\t\"         xmlns=\\\"http://www.springframework.org/schema/security\\\"\\n\" +\n\t\t\t\"         xsi:schemaLocation=\\\"\\n\" +\n\t\t\t\"\\t\\t\\thttp://www.springframework.org/schema/security\\n\" +\n\t\t\t\"\\t\\t\\thttps://www.springframework.org/schema/security/spring-security.xsd\\n\" +\n\t\t\t\"\\t\\t\\thttp://www.springframework.org/schema/beans\\n\" +\n\t\t\t\"\\t\\t\\thttps://www.springframework.org/schema/beans/spring-beans.xsd\\\">\\n\" +\n\t\t\t\"  \\n\" +\n\t\t\t\"  <relying-party-registrations>\\n\" +\n\t\t\t\"    <relying-party-registration registration-id=\\\"one\\\"\\n\" +\n\t\t\t\"                                metadata-location=\\\"${metadata-location}\\\"/>\\n\" +\n\t\t\t\"  </relying-party-registrations>\\n\" +\n\t\t\t\"\\n\" +\n\t\t\t\"</b:beans>\\n\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String METADATA_LOCATION_OVERRIDE_PROPERTIES_XML_CONFIG = \"<b:beans xmlns:b=\\\"http://www.springframework.org/schema/beans\\\"\\n\" +\n\t\t\t\"         xmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\"\\n\" +\n\t\t\t\"         xmlns=\\\"http://www.springframework.org/schema/security\\\"\\n\" +\n\t\t\t\"         xsi:schemaLocation=\\\"\\n\" +\n\t\t\t\"\\t\\t\\thttp://www.springframework.org/schema/security\\n\" +\n\t\t\t\"\\t\\t\\thttps://www.springframework.org/schema/security/spring-security.xsd\\n\" +\n\t\t\t\"\\t\\t\\thttp://www.springframework.org/schema/beans\\n\" +\n\t\t\t\"\\t\\t\\thttps://www.springframework.org/schema/beans/spring-beans.xsd\\\">\\n\" +\n\t\t\t\"  \\n\" +\n\t\t\t\"  <relying-party-registrations>\\n\" +\n\t\t\t\"    <relying-party-registration registration-id=\\\"one\\\"\\n\" +\n\t\t\t\"                                entity-id=\\\"https://rp.example.org\\\"\\n\" +\n\t\t\t\"                                metadata-location=\\\"${metadata-location}\\\"\\n\" +\n\t\t\t\"                                assertion-consumer-service-location=\\\"https://rp.example.org/location\\\"\\n\" +\n\t\t\t\"                                assertion-consumer-service-binding=\\\"REDIRECT\\\"/>\"  +\n\t\t\t\"  </relying-party-registrations>\\n\" +\n\t\t\t\"\\n\" +\n\t\t\t\"</b:beans>\\n\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String METADATA_RESPONSE = \"<?xml version=\\\"1.0\\\"?>\\n\" +\n\t\t\t\"<md:EntityDescriptor xmlns:md=\\\"urn:oasis:names:tc:SAML:2.0:metadata\\\" xmlns:ds=\\\"http://www.w3.org/2000/09/xmldsig#\\\" entityID=\\\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\\\" ID=\\\"_e793a707d3e1a9ee6cbec7454fdad2c7cd793dd3703179a527b9620a6e9682af\\\"><ds:Signature>\\n\" +\n\t\t\t\"  <ds:SignedInfo><ds:CanonicalizationMethod Algorithm=\\\"http://www.w3.org/2001/10/xml-exc-c14n#\\\"/>\\n\" +\n\t\t\t\"    <ds:SignatureMethod Algorithm=\\\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\\\"/>\\n\" +\n\t\t\t\"  <ds:Reference URI=\\\"#_e793a707d3e1a9ee6cbec7454fdad2c7cd793dd3703179a527b9620a6e9682af\\\"><ds:Transforms><ds:Transform Algorithm=\\\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\\\"/><ds:Transform Algorithm=\\\"http://www.w3.org/2001/10/xml-exc-c14n#\\\"/></ds:Transforms><ds:DigestMethod Algorithm=\\\"http://www.w3.org/2001/04/xmlenc#sha256\\\"/><ds:DigestValue>qIGOB+m2Kuq9Vp6F9qs/EFvFzuo6qEGukjICPyVAkjk=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>NgKak4k9LBAqbi8Za8ALUXW1l4npZ4+MOf8jhmpePDP3msbzjeKkkWFgxx+ILLJYwZzVWd3l028xm2l+SBOwoYRKJ670NgcdSdj6plBTGiZ5NXsXrX5M0zmgvAShREgjth/BKTUct5UVJOTqIxOPwBuCnj+Nn1+QUtY9ekPLrM0O2i+g1wckKaP6D7N+uVBwNgZGoOj5bZ082G7QXRX6Jo0925uKczAIKdIiBbMeKa/0phS2L97AkgQRGi2+j8V66TaDWuDSwd9hA2qzCwjsNui4DVLBwP0/LvgUdcu8g7JBIZ1yTddfByefOTVsU7UuZXkYEn4jU2ouk+u5klSo3Q==</ds:SignatureValue>\\n\" +\n\t\t\t\"<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>\\n\" +\n\t\t\t\"  <md:IDPSSODescriptor protocolSupportEnumeration=\\\"urn:oasis:names:tc:SAML:2.0:protocol\\\">\\n\" +\n\t\t\t\"    <md:KeyDescriptor use=\\\"signing\\\">\\n\" +\n\t\t\t\"      <ds:KeyInfo xmlns:ds=\\\"http://www.w3.org/2000/09/xmldsig#\\\">\\n\" +\n\t\t\t\"        <ds:X509Data>\\n\" +\n\t\t\t\"          <ds:X509Certificate>MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk</ds:X509Certificate>\\n\" +\n\t\t\t\"        </ds:X509Data>\\n\" +\n\t\t\t\"      </ds:KeyInfo>\\n\" +\n\t\t\t\"    </md:KeyDescriptor>\\n\" +\n\t\t\t\"    <md:KeyDescriptor use=\\\"encryption\\\">\\n\" +\n\t\t\t\"      <ds:KeyInfo xmlns:ds=\\\"http://www.w3.org/2000/09/xmldsig#\\\">\\n\" +\n\t\t\t\"        <ds:X509Data>\\n\" +\n\t\t\t\"          <ds:X509Certificate>MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk</ds:X509Certificate>\\n\" +\n\t\t\t\"        </ds:X509Data>\\n\" +\n\t\t\t\"      </ds:KeyInfo>\\n\" +\n\t\t\t\"    </md:KeyDescriptor>\\n\" +\n\t\t\t\"    <md:SingleLogoutService Binding=\\\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\\\" Location=\\\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SingleLogoutService.php\\\"/>\\n\" +\n\t\t\t\"    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>\\n\" +\n\t\t\t\"    <md:SingleSignOnService Binding=\\\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\\\" Location=\\\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\\\"/>\\n\" +\n\t\t\t\"  </md:IDPSSODescriptor>\\n\" +\n\t\t\t\"  <md:ContactPerson contactType=\\\"technical\\\">\\n\" +\n\t\t\t\"    <md:GivenName>John</md:GivenName>\\n\" +\n\t\t\t\"    <md:SurName>Doe</md:SurName>\\n\" +\n\t\t\t\"    <md:EmailAddress>john@doe.com</md:EmailAddress>\\n\" +\n\t\t\t\"  </md:ContactPerson>\\n\" +\n\t\t\t\"</md:EntityDescriptor>\\n\";\n\t// @formatter:on\n\n\t@Autowired\n\t@Qualifier(\"registrations\")\n\tprivate RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tprivate MockWebServer server;\n\n\t@AfterEach\n\tvoid cleanup() throws Exception {\n\t\tif (this.server != null) {\n\t\t\tthis.server.shutdown();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void parseWhenMetadataLocationConfiguredThenRequestMetadataFromLocation() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString serverUrl = this.server.url(\"/\").toString();\n\t\tthis.server.enqueue(xmlResponse(METADATA_RESPONSE));\n\t\tString metadataConfig = METADATA_LOCATION_XML_CONFIG.replace(\"${metadata-location}\", serverUrl);\n\t\tthis.spring.context(metadataConfig).autowire();\n\t\tassertThat(this.relyingPartyRegistrationRepository)\n\t\t\t.isInstanceOf(InMemoryRelyingPartyRegistrationRepository.class);\n\t\tRelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationRepository\n\t\t\t.findByRegistrationId(\"one\");\n\t\tAssertingPartyMetadata assertingPartyMetadata = relyingPartyRegistration.getAssertingPartyMetadata();\n\t\tassertThat(relyingPartyRegistration).isNotNull();\n\t\tassertThat(relyingPartyRegistration.getRegistrationId()).isEqualTo(\"one\");\n\t\tassertThat(relyingPartyRegistration.getEntityId())\n\t\t\t.isEqualTo(\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\");\n\t\tassertThat(relyingPartyRegistration.getAssertionConsumerServiceLocation())\n\t\t\t.isEqualTo(\"{baseUrl}/login/saml2/sso/{registrationId}\");\n\t\tassertThat(relyingPartyRegistration.getAssertionConsumerServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(assertingPartyMetadata.getEntityId())\n\t\t\t.isEqualTo(\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\");\n\t\tassertThat(assertingPartyMetadata.getWantAuthnRequestsSigned()).isFalse();\n\t\tassertThat(assertingPartyMetadata.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(assertingPartyMetadata.getEncryptionX509Credentials()).hasSize(1);\n\t\tassertThat(assertingPartyMetadata.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\");\n\t\tassertThat(assertingPartyMetadata.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(assertingPartyMetadata.getSigningAlgorithms())\n\t\t\t.containsExactly(\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\");\n\t}\n\n\t@Test\n\tpublic void parseWhenMetadataLocationConfiguredAndRegistrationHasPropertiesThenDoNotOverrideSpecifiedProperties()\n\t\t\tthrows Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString serverUrl = this.server.url(\"/\").toString();\n\t\tthis.server.enqueue(xmlResponse(METADATA_RESPONSE));\n\t\tString metadataConfig = METADATA_LOCATION_OVERRIDE_PROPERTIES_XML_CONFIG.replace(\"${metadata-location}\",\n\t\t\t\tserverUrl);\n\t\tthis.spring.context(metadataConfig).autowire();\n\t\tassertThat(this.relyingPartyRegistrationRepository)\n\t\t\t.isInstanceOf(InMemoryRelyingPartyRegistrationRepository.class);\n\t\tRelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationRepository\n\t\t\t.findByRegistrationId(\"one\");\n\t\tAssertingPartyMetadata assertingPartyMetadata = relyingPartyRegistration.getAssertingPartyMetadata();\n\t\tassertThat(relyingPartyRegistration).isNotNull();\n\t\tassertThat(relyingPartyRegistration.getRegistrationId()).isEqualTo(\"one\");\n\t\tassertThat(relyingPartyRegistration.getEntityId()).isEqualTo(\"https://rp.example.org\");\n\t\tassertThat(relyingPartyRegistration.getAssertionConsumerServiceLocation())\n\t\t\t.isEqualTo(\"https://rp.example.org/location\");\n\t\tassertThat(relyingPartyRegistration.getAssertionConsumerServiceBinding())\n\t\t\t.isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(assertingPartyMetadata.getEntityId())\n\t\t\t.isEqualTo(\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\");\n\t\tassertThat(assertingPartyMetadata.getWantAuthnRequestsSigned()).isFalse();\n\t\tassertThat(assertingPartyMetadata.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(assertingPartyMetadata.getEncryptionX509Credentials()).hasSize(1);\n\t\tassertThat(assertingPartyMetadata.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\");\n\t\tassertThat(assertingPartyMetadata.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(assertingPartyMetadata.getSigningAlgorithms())\n\t\t\t.containsExactly(\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\");\n\t}\n\n\t@Test\n\tpublic void parseWhenSingleRelyingPartyRegistrationThenAvailableInRepository() {\n\t\tthis.spring.configLocations(xml(\"SingleRegistration\")).autowire();\n\t\tassertThat(this.relyingPartyRegistrationRepository)\n\t\t\t.isInstanceOf(InMemoryRelyingPartyRegistrationRepository.class);\n\t\tRelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationRepository\n\t\t\t.findByRegistrationId(\"one\");\n\t\tAssertingPartyMetadata assertingPartyMetadata = relyingPartyRegistration.getAssertingPartyMetadata();\n\t\tassertThat(relyingPartyRegistration).isNotNull();\n\t\tassertThat(relyingPartyRegistration.getRegistrationId()).isEqualTo(\"one\");\n\t\tassertThat(relyingPartyRegistration.getEntityId())\n\t\t\t.isEqualTo(\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\");\n\t\tassertThat(relyingPartyRegistration.getAssertionConsumerServiceLocation())\n\t\t\t.isEqualTo(\"{baseUrl}/login/saml2/sso/{registrationId}\");\n\t\tassertThat(relyingPartyRegistration.getAssertionConsumerServiceBinding())\n\t\t\t.isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(assertingPartyMetadata.getEntityId()).isEqualTo(\"https://accounts.google.com/o/saml2/idp/entity-id\");\n\t\tassertThat(assertingPartyMetadata.getWantAuthnRequestsSigned()).isTrue();\n\t\tassertThat(assertingPartyMetadata.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://accounts.google.com/o/saml2/idp/sso-url\");\n\t\tassertThat(assertingPartyMetadata.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(assertingPartyMetadata.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(assertingPartyMetadata.getEncryptionX509Credentials()).hasSize(1);\n\t\tassertThat(assertingPartyMetadata.getSigningAlgorithms())\n\t\t\t.containsExactly(\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\");\n\t}\n\n\t@Test\n\tpublic void parseWhenMultiRelyingPartyRegistrationThenAvailableInRepository() {\n\t\tthis.spring.configLocations(xml(\"MultiRegistration\")).autowire();\n\t\tassertThat(this.relyingPartyRegistrationRepository)\n\t\t\t.isInstanceOf(InMemoryRelyingPartyRegistrationRepository.class);\n\t\tRelyingPartyRegistration one = this.relyingPartyRegistrationRepository.findByRegistrationId(\"one\");\n\t\tAssertingPartyMetadata google = one.getAssertingPartyMetadata();\n\t\tRelyingPartyRegistration two = this.relyingPartyRegistrationRepository.findByRegistrationId(\"two\");\n\t\tAssertingPartyMetadata simpleSaml = two.getAssertingPartyMetadata();\n\t\tassertThat(one).isNotNull();\n\t\tassertThat(one.getRegistrationId()).isEqualTo(\"one\");\n\t\tassertThat(one.getEntityId()).isEqualTo(\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\");\n\t\tassertThat(one.getAssertionConsumerServiceLocation()).isEqualTo(\"{baseUrl}/login/saml2/sso/{registrationId}\");\n\t\tassertThat(one.getAssertionConsumerServiceBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(google.getEntityId()).isEqualTo(\"https://accounts.google.com/o/saml2/idp/entity-id\");\n\t\tassertThat(google.getWantAuthnRequestsSigned()).isTrue();\n\t\tassertThat(google.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://accounts.google.com/o/saml2/idp/sso-url\");\n\t\tassertThat(google.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(google.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(google.getEncryptionX509Credentials()).hasSize(1);\n\t\tassertThat(google.getSigningAlgorithms()).containsExactly(\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\");\n\t\tassertThat(two).isNotNull();\n\t\tassertThat(two.getRegistrationId()).isEqualTo(\"two\");\n\t\tassertThat(two.getEntityId()).isEqualTo(\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\");\n\t\tassertThat(two.getAssertionConsumerServiceLocation()).isEqualTo(\"{baseUrl}/login/saml2/sso/{registrationId}\");\n\t\tassertThat(two.getAssertionConsumerServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(simpleSaml.getEntityId())\n\t\t\t.isEqualTo(\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\");\n\t\tassertThat(simpleSaml.getWantAuthnRequestsSigned()).isFalse();\n\t\tassertThat(simpleSaml.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\");\n\t\tassertThat(simpleSaml.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(simpleSaml.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(simpleSaml.getEncryptionX509Credentials()).hasSize(1);\n\t\tassertThat(simpleSaml.getSigningAlgorithms()).containsExactly(\n\t\t\t\t\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha224\",\n\t\t\t\t\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\",\n\t\t\t\t\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384\");\n\t}\n\n\t@Test\n\tpublic void parseWhenRelayStateResolverThenUses() {\n\t\tthis.spring.configLocations(xml(\"RelayStateResolver\")).autowire();\n\t\tConverter<HttpServletRequest, String> relayStateResolver = this.spring.getContext().getBean(Converter.class);\n\t\tOpenSaml5AuthenticationRequestResolver authenticationRequestResolver = this.spring.getContext()\n\t\t\t.getBean(OpenSaml5AuthenticationRequestResolver.class);\n\t\tMockHttpServletRequest request = get(\"/saml2/authenticate/one\").build();\n\t\tauthenticationRequestResolver.resolve(request);\n\t\tverify(relayStateResolver).convert(request);\n\t}\n\n\t@Test\n\tpublic void parseWhenPlaceholdersThenResolves() throws Exception {\n\t\tRelyingPartyRegistration sample = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\tSystem.setProperty(\"registration-id\", sample.getRegistrationId());\n\t\tSystem.setProperty(\"entity-id\", sample.getEntityId());\n\t\tSystem.setProperty(\"acs-location\", sample.getAssertionConsumerServiceLocation());\n\t\tSystem.setProperty(\"slo-location\", sample.getSingleLogoutServiceLocation());\n\t\tSystem.setProperty(\"slo-response-location\", sample.getSingleLogoutServiceResponseLocation());\n\t\ttry (MockWebServer web = new MockWebServer()) {\n\t\t\tweb.start();\n\t\t\tString serverUrl = web.url(\"/metadata\").toString();\n\t\t\tweb.enqueue(xmlResponse(METADATA_RESPONSE));\n\t\t\tSystem.setProperty(\"metadata-location\", serverUrl);\n\t\t\tthis.spring.configLocations(xml(\"PlaceholderRegistration\")).autowire();\n\t\t}\n\t\tRelyingPartyRegistration registration = this.relyingPartyRegistrationRepository\n\t\t\t.findByRegistrationId(sample.getRegistrationId());\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(sample.getRegistrationId());\n\t\tassertThat(registration.getEntityId()).isEqualTo(sample.getEntityId());\n\t\tassertThat(registration.getAssertionConsumerServiceLocation())\n\t\t\t.isEqualTo(sample.getAssertionConsumerServiceLocation());\n\t\tassertThat(registration.getSingleLogoutServiceLocation()).isEqualTo(sample.getSingleLogoutServiceLocation());\n\t\tassertThat(registration.getSingleLogoutServiceResponseLocation())\n\t\t\t.isEqualTo(sample.getSingleLogoutServiceResponseLocation());\n\t}\n\n\tprivate static MockResponse xmlResponse(String xml) {\n\t\treturn new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE).setBody(xml);\n\t}\n\n\tprivate static String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/test/SpringTestContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.test;\n\nimport java.io.Closeable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;\nimport org.springframework.mock.web.MockServletConfig;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.util.InMemoryXmlWebApplicationContext;\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.servlet.MockServletContext;\nimport org.springframework.test.context.web.GenericXmlWebContextLoader;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.test.web.servlet.setup.MockMvcConfigurer;\nimport org.springframework.web.context.ConfigurableWebApplicationContext;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.context.support.XmlWebApplicationContext;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.server.WebFilter;\n\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class SpringTestContext implements Closeable {\n\n\tprivate Object test;\n\n\tprivate ConfigurableWebApplicationContext context;\n\n\tprivate List<Filter> filters = new ArrayList<>();\n\n\tprivate DeferAddFilter deferAddFilter = new DeferAddFilter();\n\n\tprivate List<Consumer<ConfigurableWebApplicationContext>> postProcessors = new ArrayList<>();\n\n\tpublic SpringTestContext(Object test) {\n\t\tsetTest(test);\n\t}\n\n\tpublic void setTest(Object test) {\n\t\tthis.test = test;\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\ttry {\n\t\t\tthis.context.close();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t}\n\t}\n\n\tpublic SpringTestContext context(ConfigurableWebApplicationContext context) {\n\t\tthis.context = context;\n\t\treturn this;\n\t}\n\n\tpublic SpringTestContext register(Class<?>... classes) {\n\t\tAnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();\n\t\tapplicationContext.register(classes);\n\t\tthis.context = applicationContext;\n\t\treturn this;\n\t}\n\n\tpublic SpringTestContext testConfigLocations(String... configLocations) {\n\t\tGenericXmlWebContextLoader loader = new GenericXmlWebContextLoader();\n\t\tString[] locations = loader.processLocations(this.test.getClass(), configLocations);\n\t\treturn configLocations(locations);\n\t}\n\n\tpublic SpringTestContext configLocations(String... configLocations) {\n\t\tXmlWebApplicationContext context = new XmlWebApplicationContext();\n\t\tcontext.setConfigLocations(configLocations);\n\t\tthis.context = context;\n\t\treturn this;\n\t}\n\n\tpublic SpringTestContext context(String configuration) {\n\t\tInMemoryXmlWebApplicationContext context = new InMemoryXmlWebApplicationContext(configuration);\n\t\tthis.context = context;\n\t\treturn this;\n\t}\n\n\tpublic SpringTestContext postProcessor(Consumer<ConfigurableWebApplicationContext> contextConsumer) {\n\t\tthis.postProcessors.add(contextConsumer);\n\t\treturn this;\n\t}\n\n\tpublic SpringTestContext mockMvcAfterSpringSecurityOk() {\n\t\tthis.deferAddFilter.addFilter(new OncePerRequestFilter() {\n\t\t\t@Override\n\t\t\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\t\tFilterChain filterChain) {\n\t\t\t\tresponse.setStatus(HttpServletResponse.SC_OK);\n\t\t\t}\n\t\t});\n\t\treturn this;\n\t}\n\n\tpublic SpringTestContext addFilter(Filter filter) {\n\t\tthis.filters.add(filter);\n\t\treturn this;\n\t}\n\n\tpublic ConfigurableWebApplicationContext getContext() {\n\t\tif (!this.context.isRunning()) {\n\t\t\tthis.context.setServletContext(MockServletContext.mvc());\n\t\t\tthis.context.setServletConfig(new MockServletConfig());\n\t\t\tthis.context.refresh();\n\t\t}\n\t\treturn this.context;\n\t}\n\n\tpublic void autowire() {\n\t\tthis.context.setServletContext(MockServletContext.mvc());\n\t\tthis.context.setServletConfig(new MockServletConfig());\n\t\tfor (Consumer<ConfigurableWebApplicationContext> postProcessor : this.postProcessors) {\n\t\t\tpostProcessor.accept(this.context);\n\t\t}\n\t\tthis.context.refresh();\n\t\tif (this.context.containsBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN)) {\n\t\t\t// @formatter:off\n\t\t\tMockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t\t\t.addFilters(this.filters.toArray(new Filter[0]))\n\t\t\t\t\t.apply(springSecurity())\n\t\t\t\t\t.apply(this.deferAddFilter)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tthis.context.getBeanFactory().registerResolvableDependency(MockMvc.class, mockMvc);\n\t\t}\n\t\tString webFluxSecurityBean = \"org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.WebFilterChainFilter\";\n\t\tif (this.context.containsBean(webFluxSecurityBean)) {\n\t\t\tWebFilter springSecurityFilter = this.context.getBean(webFluxSecurityBean, WebFilter.class);\n\t\t\t// @formatter:off\n\t\t\tWebTestClient webTest = WebTestClient\n\t\t\t\t\t.bindToController(new WebTestClientBuilder.Http200RestController())\n\t\t\t\t\t.webFilter(springSecurityFilter)\n\t\t\t\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tthis.context.getBeanFactory().registerResolvableDependency(WebTestClient.class, webTest);\n\t\t}\n\t\tAutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();\n\t\tbpp.setBeanFactory(this.context.getBeanFactory());\n\t\tbpp.processInjection(this.test);\n\t}\n\n\tprivate static class DeferAddFilter implements MockMvcConfigurer {\n\n\t\tprivate List<Filter> filters = new ArrayList<>();\n\n\t\tvoid addFilter(Filter filter) {\n\t\t\tthis.filters.add(filter);\n\t\t}\n\n\t\t@Override\n\t\tpublic RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder,\n\t\t\t\tWebApplicationContext context) {\n\t\t\tbuilder.addFilters(this.filters.toArray(new Filter[0]));\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/test/SpringTestContextExtension.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.test;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\nimport org.springframework.security.test.context.TestSecurityContextHolder;\n\npublic class SpringTestContextExtension implements BeforeEachCallback, AfterEachCallback {\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) throws Exception {\n\t\tTestSecurityContextHolder.clearContext();\n\t\tgetContexts(context.getRequiredTestInstance()).forEach(SpringTestContext::close);\n\t}\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) throws Exception {\n\t\tObject testInstance = context.getRequiredTestInstance();\n\t\tgetContexts(testInstance).forEach((springTestContext) -> springTestContext.setTest(testInstance));\n\t}\n\n\tprivate static List<SpringTestContext> getContexts(Object test) throws IllegalAccessException {\n\t\tField[] declaredFields = test.getClass().getDeclaredFields();\n\t\tList<SpringTestContext> result = new ArrayList<>();\n\t\tfor (Field field : declaredFields) {\n\t\t\tif (SpringTestContext.class.isAssignableFrom(field.getType())) {\n\t\t\t\tresult.add((SpringTestContext) field.get(test));\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/test/SpringTestParentApplicationContextExecutionListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.test;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.test.context.TestContext;\nimport org.springframework.test.context.TestExecutionListener;\n\npublic class SpringTestParentApplicationContextExecutionListener implements TestExecutionListener {\n\n\t@Override\n\tpublic void beforeTestMethod(TestContext testContext) throws Exception {\n\t\tApplicationContext parent = testContext.getApplicationContext();\n\t\tObject testInstance = testContext.getTestInstance();\n\t\tgetContexts(testInstance).forEach((springTestContext) -> springTestContext\n\t\t\t.postProcessor((applicationContext) -> applicationContext.setParent(parent)));\n\t}\n\n\tprivate static List<SpringTestContext> getContexts(Object test) throws IllegalAccessException {\n\t\tField[] declaredFields = test.getClass().getDeclaredFields();\n\t\tList<SpringTestContext> result = new ArrayList<>();\n\t\tfor (Field field : declaredFields) {\n\t\t\tif (SpringTestContext.class.isAssignableFrom(field.getType())) {\n\t\t\t\tresult.add((SpringTestContext) field.get(test));\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/users/AuthenticationTestConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.users;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@Configuration\npublic class AuthenticationTestConfiguration {\n\n\t@Bean\n\tpublic static UserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/users/ReactiveAuthenticationTestConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.users;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@Configuration\npublic class ReactiveAuthenticationTestConfiguration {\n\n\t@Bean\n\tpublic static ReactiveUserDetailsService userDetailsService() {\n\t\treturn new MapReactiveUserDetailsService(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/util/InMemoryXmlApplicationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.util;\n\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.AbstractXmlApplicationContext;\nimport org.springframework.core.io.Resource;\nimport org.springframework.security.util.InMemoryResource;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @author Emil Sierżęga\n */\npublic class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext {\n\n\tstatic final String BEANS_OPENING = \"<b:beans xmlns='http://www.springframework.org/schema/security'\\n\"\n\t\t\t+ \"    xmlns:context='http://www.springframework.org/schema/context'\\n\"\n\t\t\t+ \"    xmlns:b='http://www.springframework.org/schema/beans'\\n\"\n\t\t\t+ \"    xmlns:aop='http://www.springframework.org/schema/aop'\\n\"\n\t\t\t+ \"    xmlns:mvc='http://www.springframework.org/schema/mvc'\\n\"\n\t\t\t+ \"    xmlns:websocket='http://www.springframework.org/schema/websocket'\\n\"\n\t\t\t+ \"    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\\n\"\n\t\t\t+ \"    xsi:schemaLocation='http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd\\n\"\n\t\t\t+ \"http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.5.xsd\\n\"\n\t\t\t+ \"http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd\\n\"\n\t\t\t+ \"http://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\\n\"\n\t\t\t+ \"http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd\\n\"\n\t\t\t+ \"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-\";\n\tstatic final String BEANS_CLOSE = \"</b:beans>\\n\";\n\tstatic final String SPRING_SECURITY_VERSION = SpringSecurityVersions.getCurrentXsdVersionFromSpringSchemas();\n\n\tResource inMemoryXml;\n\n\tpublic InMemoryXmlApplicationContext(String xml) {\n\t\tthis(xml, SPRING_SECURITY_VERSION, null);\n\t}\n\n\tpublic InMemoryXmlApplicationContext(String xml, ApplicationContext parent) {\n\t\tthis(xml, SPRING_SECURITY_VERSION, parent);\n\t}\n\n\tpublic InMemoryXmlApplicationContext(String xml, String secVersion, ApplicationContext parent) {\n\t\tString fullXml = BEANS_OPENING + secVersion + \".xsd'>\\n\" + xml + BEANS_CLOSE;\n\t\tthis.inMemoryXml = new InMemoryResource(fullXml);\n\t\tsetAllowBeanDefinitionOverriding(true);\n\t\tsetParent(parent);\n\t\trefresh();\n\t}\n\n\t@Override\n\tprotected DefaultListableBeanFactory createBeanFactory() {\n\t\treturn new DefaultListableBeanFactory(getInternalParentBeanFactory()) {\n\t\t\t@Override\n\t\t\tprotected boolean allowAliasOverriding() {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Override\n\tprotected Resource[] getConfigResources() {\n\t\treturn new Resource[] { this.inMemoryXml };\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/util/InMemoryXmlWebApplicationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.util;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.beans.factory.xml.XmlBeanDefinitionReader;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.io.Resource;\nimport org.springframework.security.util.InMemoryResource;\nimport org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;\n\n/**\n * @author Joe Grandja\n */\npublic class InMemoryXmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {\n\n\tprivate Resource inMemoryXml;\n\n\tpublic InMemoryXmlWebApplicationContext(String xml) {\n\t\tthis(xml, InMemoryXmlApplicationContext.SPRING_SECURITY_VERSION, null);\n\t}\n\n\tpublic InMemoryXmlWebApplicationContext(String xml, ApplicationContext parent) {\n\t\tthis(xml, InMemoryXmlApplicationContext.SPRING_SECURITY_VERSION, parent);\n\t}\n\n\tpublic InMemoryXmlWebApplicationContext(String xml, String secVersion, ApplicationContext parent) {\n\t\tString fullXml = InMemoryXmlApplicationContext.BEANS_OPENING + secVersion + \".xsd'>\\n\" + xml\n\t\t\t\t+ InMemoryXmlApplicationContext.BEANS_CLOSE;\n\t\tthis.inMemoryXml = new InMemoryResource(fullXml);\n\t\tsetAllowBeanDefinitionOverriding(true);\n\t\tsetParent(parent);\n\t}\n\n\t@Override\n\tprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException {\n\t\tXmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);\n\t\treader.loadBeanDefinitions(new Resource[] { this.inMemoryXml });\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/util/SpringSecurityVersions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Properties;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.springframework.security.core.SpringSecurityCoreVersion;\n\n/**\n * For computing different Spring Security versions\n */\npublic final class SpringSecurityVersions {\n\n\tstatic final Pattern SCHEMA_VERSION_PATTERN = Pattern.compile(\"\\\\d+\\\\.\\\\d+(\\\\.\\\\d+)?\");\n\n\tpublic static String getCurrentXsdVersionFromSpringSchemas() {\n\t\tProperties properties = new Properties();\n\t\ttry (InputStream is = SpringSecurityCoreVersion.class.getClassLoader()\n\t\t\t.getResourceAsStream(\"META-INF/spring.schemas\")) {\n\t\t\tproperties.load(is);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new RuntimeException(\"Could not read 'META-INF/spring.schemas'\", ex);\n\t\t}\n\n\t\tString inPackageLocation = properties\n\t\t\t.getProperty(\"https://www.springframework.org/schema/security/spring-security.xsd\");\n\t\tMatcher matcher = SCHEMA_VERSION_PATTERN.matcher(inPackageLocation);\n\t\tif (matcher.find()) {\n\t\t\treturn matcher.group(0);\n\t\t}\n\t\tthrow new IllegalStateException(\"Failed to find version\");\n\t}\n\n\tprivate SpringSecurityVersions() {\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/PathPatternRequestMatcherBuilderFactoryBeanTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\nclass PathPatternRequestMatcherBuilderFactoryBeanTests {\n\n\tGenericApplicationContext context;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.context = new GenericApplicationContext();\n\t}\n\n\t@Test\n\tvoid getObjectWhenDefaultsThenBuilder() throws Exception {\n\t\tfactoryBean().getObject();\n\t}\n\n\t@Test\n\tvoid getObjectWhenMvcPatternParserThenUses() throws Exception {\n\t\tPathPatternParser mvc = registerMvcPatternParser();\n\t\tPathPatternRequestMatcher.Builder builder = factoryBean().getObject();\n\t\tbuilder.matcher(\"/path/**\");\n\t\tverify(mvc).parse(\"/path/**\");\n\t}\n\n\t@Test\n\tvoid getObjectWhenPathPatternParserThenUses() throws Exception {\n\t\tPathPatternParser parser = mock(PathPatternParser.class);\n\t\tPathPatternRequestMatcher.Builder builder = factoryBean(parser).getObject();\n\t\tbuilder.matcher(\"/path/**\");\n\t\tverify(parser).parse(\"/path/**\");\n\t}\n\n\t@Test\n\tvoid getObjectWhenMvcAndPathPatternParserConflictThenIllegalArgument() {\n\t\tregisterMvcPatternParser();\n\t\tPathPatternParser parser = mock(PathPatternParser.class);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> factoryBean(parser).getObject());\n\t}\n\n\t@Test\n\tvoid getObjectWhenMvcAndPathPatternParserAgreeThenUses() throws Exception {\n\t\tPathPatternParser mvc = registerMvcPatternParser();\n\t\tPathPatternRequestMatcher.Builder builder = factoryBean(mvc).getObject();\n\t\tbuilder.matcher(\"/path/**\");\n\t\tverify(mvc).parse(\"/path/**\");\n\t}\n\n\t@Test\n\tvoid getObjectWhenBasePathThenUses() throws Exception {\n\t\tPathPatternRequestMatcherBuilderFactoryBean factoryBean = new PathPatternRequestMatcherBuilderFactoryBean();\n\t\tfactoryBean.setApplicationContext(this.context);\n\t\tfactoryBean.setBasePath(\"/mvc\");\n\t\tPathPatternRequestMatcher.Builder builder = factoryBean.getObject();\n\t\tPathPatternRequestMatcher matcher = builder.matcher(\"/path/**\");\n\t\tassertThat(matcher.matches(TestMockHttpServletRequests.get(\"/mvc/path/123\").build())).isTrue();\n\t\tassertThat(matcher.matches(TestMockHttpServletRequests.get(\"/path/123\").build())).isFalse();\n\t}\n\n\tPathPatternRequestMatcherBuilderFactoryBean factoryBean() {\n\t\tPathPatternRequestMatcherBuilderFactoryBean factoryBean = new PathPatternRequestMatcherBuilderFactoryBean();\n\t\tfactoryBean.setApplicationContext(this.context);\n\t\treturn factoryBean;\n\t}\n\n\tPathPatternRequestMatcherBuilderFactoryBean factoryBean(PathPatternParser parser) {\n\t\tPathPatternRequestMatcherBuilderFactoryBean factoryBean = new PathPatternRequestMatcherBuilderFactoryBean(\n\t\t\t\tparser);\n\t\tfactoryBean.setApplicationContext(this.context);\n\t\treturn factoryBean;\n\t}\n\n\tPathPatternParser registerMvcPatternParser() {\n\t\tPathPatternParser mvc = mock(PathPatternParser.class);\n\t\tthis.context.registerBean(PathPatternRequestMatcherBuilderFactoryBean.MVC_PATTERN_PARSER_BEAN_NAME,\n\t\t\t\tPathPatternParser.class, () -> mvc);\n\t\tthis.context.refresh();\n\t\treturn mvc;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/AuthorizeExchangeSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class AuthorizeExchangeSpecTests {\n\n\tServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication();\n\n\t@Test\n\tpublic void antMatchersWhenMethodAndPatternsThenDiscriminatesByMethod() {\n\t\tthis.http.csrf((csrf) -> csrf.disable())\n\t\t\t.authorizeExchange((authorize) -> authorize.pathMatchers(HttpMethod.POST, \"/a\", \"/b\")\n\t\t\t\t.denyAll()\n\t\t\t\t.anyExchange()\n\t\t\t\t.permitAll());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tclient.get()\n\t\t\t\t.uri(\"/a\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\tclient.get()\n\t\t\t\t.uri(\"/b\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\tclient.post()\n\t\t\t\t.uri(\"/a\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\tclient.post()\n\t\t\t\t.uri(\"/b\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void antMatchersWhenPatternsThenAnyMethod() {\n\t\tthis.http.csrf((csrf) -> csrf.disable())\n\t\t\t.authorizeExchange((authorize) -> authorize.pathMatchers(\"/a\", \"/b\").denyAll().anyExchange().permitAll());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tclient.get()\n\t\t\t\t.uri(\"/a\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\tclient.get()\n\t\t\t\t.uri(\"/b\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\tclient.post()\n\t\t\t\t.uri(\"/a\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\tclient.post()\n\t\t\t\t.uri(\"/b\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void antMatchersWhenPatternsInLambdaThenAnyMethod() {\n\t\tthis.http.csrf(ServerHttpSecurity.CsrfSpec::disable)\n\t\t\t.authorizeExchange((authorize) -> authorize.pathMatchers(\"/a\", \"/b\").denyAll().anyExchange().permitAll());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tclient.get()\n\t\t\t\t.uri(\"/a\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\tclient.get()\n\t\t\t\t.uri(\"/b\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\tclient.post()\n\t\t\t\t.uri(\"/a\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\tclient.post()\n\t\t\t\t.uri(\"/b\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void antMatchersWhenNoAccessAndAnotherMatcherThenThrowsException() {\n\t\tthis.http.authorizeExchange((authorize) -> authorize.pathMatchers(\"/incomplete\"));\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> this.http.authorizeExchange((authorize) -> authorize.pathMatchers(\"/throws-exception\")));\n\t}\n\n\t@Test\n\tpublic void anyExchangeWhenFollowedByMatcherThenThrowsException() {\n\t\tassertThatIllegalStateException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tthis.http.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().denyAll()\n\t\t\t\t.pathMatchers(\"/never-reached\"))\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildWhenMatcherDefinedWithNoAccessThenThrowsException() {\n\t\tthis.http.authorizeExchange((authorize) -> authorize.pathMatchers(\"/incomplete\"));\n\t\tassertThatIllegalStateException().isThrownBy(this.http::build);\n\t}\n\n\t@Test\n\tpublic void buildWhenMatcherDefinedWithNoAccessInLambdaThenThrowsException() {\n\t\tthis.http.authorizeExchange((authorize) -> authorize.pathMatchers(\"/incomplete\"));\n\t\tassertThatIllegalStateException().isThrownBy(this.http::build);\n\t}\n\n\tprivate WebTestClient buildClient() {\n\t\treturn WebTestClientBuilder.bindToWebFilters(this.http.build()).build();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/CorsSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.reactive.CorsConfigurationSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class CorsSpecTests {\n\n\t@Mock\n\tprivate CorsConfigurationSource source;\n\n\tprivate ApplicationContext context;\n\n\tServerHttpSecurity http;\n\n\tHttpHeaders expectedHeaders = new HttpHeaders();\n\n\tSet<String> headerNamesNotPresent = new HashSet<>();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.context = new GenericApplicationContext();\n\t\t((GenericApplicationContext) this.context).refresh();\n\t\tthis.http = new TestingServerHttpSecurity().applicationContext(this.context);\n\t}\n\n\tprivate void givenGetCorsConfigurationWillReturnWildcard() {\n\t\tCorsConfiguration value = new CorsConfiguration();\n\t\tvalue.setAllowedOrigins(Arrays.asList(\"*\"));\n\t\tgiven(this.source.getCorsConfiguration(any())).willReturn(value);\n\t}\n\n\t@Test\n\tpublic void corsWhenEnabledThenAccessControlAllowOriginAndSecurityHeaders() {\n\t\tgivenGetCorsConfigurationWillReturnWildcard();\n\t\tthis.http.cors((cors) -> cors.configurationSource(this.source));\n\t\tthis.expectedHeaders.set(\"Access-Control-Allow-Origin\", \"*\");\n\t\tthis.expectedHeaders.set(\"X-Frame-Options\", \"DENY\");\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void corsWhenEnabledInLambdaThenAccessControlAllowOriginAndSecurityHeaders() {\n\t\tgivenGetCorsConfigurationWillReturnWildcard();\n\t\tthis.http.cors((cors) -> cors.configurationSource(this.source));\n\t\tthis.expectedHeaders.set(\"Access-Control-Allow-Origin\", \"*\");\n\t\tthis.expectedHeaders.set(\"X-Frame-Options\", \"DENY\");\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void corsWhenCorsConfigurationSourceBeanThenAccessControlAllowOriginAndSecurityHeaders() {\n\t\tgivenGetCorsConfigurationWillReturnWildcard();\n\t\t((GenericApplicationContext) this.context).registerBean(CorsConfigurationSource.class, () -> this.source);\n\t\tthis.expectedHeaders.set(\"Access-Control-Allow-Origin\", \"*\");\n\t\tthis.expectedHeaders.set(\"X-Frame-Options\", \"DENY\");\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void corsWhenNoConfigurationSourceThenNoCorsHeaders() {\n\t\tthis.headerNamesNotPresent.add(\"Access-Control-Allow-Origin\");\n\t\tassertHeaders();\n\t}\n\n\tprivate void assertHeaders() {\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tFluxExchangeResult<String> response = client.get()\n\t\t\t\t.uri(\"https://example.com/\")\n\t\t\t\t.headers((h) -> h.setOrigin(\"https://origin.example.com\"))\n\t\t\t\t.exchange()\n\t\t\t\t.returnResult(String.class);\n\t\t// @formatter:on\n\t\tHttpHeaders responseHeaders = response.getResponseHeaders();\n\t\tif (!this.expectedHeaders.isEmpty()) {\n\t\t\tthis.expectedHeaders.forEach(\n\t\t\t\t\t(headerName, headerValues) -> assertThat(responseHeaders.get(headerName)).isEqualTo(headerValues));\n\t\t}\n\t\tif (!this.headerNamesNotPresent.isEmpty()) {\n\t\t\tassertThat(responseHeaders.headerNames()).doesNotContainAnyElementsOf(this.headerNamesNotPresent);\n\t\t}\n\t}\n\n\tprivate WebTestClient buildClient() {\n\t\t// @formatter:off\n\t\treturn WebTestClientBuilder.bindToWebFilters(this.http.build())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/ExceptionHandlingSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;\nimport org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Denys Ivano\n * @since 5.0.5\n */\npublic class ExceptionHandlingSpecTests {\n\n\tprivate ServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication();\n\n\t@Test\n\tpublic void defaultAuthenticationEntryPoint() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.exceptionHandling(withDefaults())\n\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/test\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().valueMatches(\"WWW-Authenticate\", \"Basic.*\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenExceptionHandlingWithDefaultsInLambdaThenDefaultAuthenticationEntryPointUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t)\n\t\t\t\t.exceptionHandling(withDefaults())\n\t\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/test\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().valueMatches(\"WWW-Authenticate\", \"Basic.*\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void customAuthenticationEntryPoint() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t.authenticationEntryPoint(redirectServerAuthenticationEntryPoint(\"/auth\")))\n\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/test\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().valueMatches(\"Location\", \".*\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAuthenticationEntryPointInLambdaThenCustomAuthenticationEntryPointUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t)\n\t\t\t\t.exceptionHandling((exceptionHandling) -> exceptionHandling\n\t\t\t\t\t\t.authenticationEntryPoint(redirectServerAuthenticationEntryPoint(\"/auth\"))\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/test\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().valueMatches(\"Location\", \".*\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void defaultAccessDeniedHandler() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().hasRole(\"ADMIN\"))\n\t\t\t.exceptionHandling(withDefaults())\n\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/admin\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isForbidden();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenExceptionHandlingWithDefaultsInLambdaThenDefaultAccessDeniedHandlerUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().hasRole(\"ADMIN\")\n\t\t\t\t)\n\t\t\t\t.exceptionHandling(withDefaults())\n\t\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/admin\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isForbidden();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void customAccessDeniedHandler() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().hasRole(\"ADMIN\"))\n\t\t\t.exceptionHandling((handling) -> handling\n\t\t\t\t.accessDeniedHandler(httpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST)))\n\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/admin\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isBadRequest();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomAccessDeniedHandlerInLambdaThenCustomAccessDeniedHandlerUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().hasRole(\"ADMIN\")\n\t\t\t\t)\n\t\t\t\t.exceptionHandling((exceptionHandling) -> exceptionHandling\n\t\t\t\t\t\t.accessDeniedHandler(httpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST))\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/admin\")\n\t\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isBadRequest();\n\t\t// @formatter:on\n\t}\n\n\tprivate ServerAuthenticationEntryPoint redirectServerAuthenticationEntryPoint(String location) {\n\t\treturn new RedirectServerAuthenticationEntryPoint(location);\n\t}\n\n\tprivate ServerAccessDeniedHandler httpStatusServerAccessDeniedHandler(HttpStatus httpStatus) {\n\t\treturn new HttpStatusServerAccessDeniedHandler(httpStatus);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.junit.jupiter.api.Test;\nimport org.openqa.selenium.By;\nimport org.openqa.selenium.NoSuchElementException;\nimport org.openqa.selenium.WebDriver;\nimport org.openqa.selenium.WebElement;\nimport org.openqa.selenium.support.FindBy;\nimport org.openqa.selenium.support.PageFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.WebFilterChainProxy;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.csrf.CsrfToken;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 5.0\n */\npublic class FormLoginTests {\n\n\tprivate ServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication();\n\n\t@Test\n\tpublic void defaultLoginPage() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin(withDefaults())\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultLoginPage loginPage = HomePage.to(driver, DefaultLoginPage.class).assertAt();\n\t\t// @formatter:off\n\t\tloginPage = loginPage.loginForm()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"invalid\")\n\t\t\t\t\t.submit(DefaultLoginPage.class)\n\t\t\t\t.assertError();\n\t\tHomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tloginPage = DefaultLogoutPage.to(driver).assertAt().logout();\n\t\tloginPage.assertAt().assertLogout();\n\t}\n\n\t@Test\n\tpublic void formLoginWhenDefaultsInLambdaThenCreatesDefaultLoginPage() {\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t.formLogin(withDefaults())\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder.bindToWebFilters(securityWebFilter).build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder.webTestClientSetup(webTestClient).build();\n\t\tDefaultLoginPage loginPage = HomePage.to(driver, DefaultLoginPage.class).assertAt();\n\t\t// @formatter:off\n\t\tloginPage = loginPage\n\t\t\t\t.loginForm()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"invalid\")\n\t\t\t\t\t.submit(DefaultLoginPage.class)\n\t\t\t\t.assertError();\n\t\tHomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tloginPage = DefaultLogoutPage.to(driver).assertAt().logout();\n\t\tloginPage.assertAt().assertLogout();\n\t}\n\n\t@Test\n\tpublic void customLoginPage() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.pathMatchers(\"/login\").permitAll()\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin((login) -> login\n\t\t\t\t.loginPage(\"/login\"))\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClient\n\t\t\t\t.bindToController(new CustomLoginPageController(), new WebTestClientBuilder.Http200RestController())\n\t\t\t\t.webFilter(new WebFilterChainProxy(securityWebFilter))\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tCustomLoginPage loginPage = HomePage.to(driver, CustomLoginPage.class).assertAt();\n\t\t// @formatter:off\n\t\tHomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t}\n\n\t@Test\n\tpublic void formLoginWhenCustomLoginPageInLambdaThenUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.pathMatchers(\"/login\").permitAll()\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin((formLogin) -> formLogin\n\t\t\t\t\t\t.loginPage(\"/login\")\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClient\n\t\t\t\t.bindToController(new CustomLoginPageController(), new WebTestClientBuilder.Http200RestController())\n\t\t\t\t.webFilter(new WebFilterChainProxy(securityWebFilter))\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tCustomLoginPage loginPage = HomePage.to(driver, CustomLoginPage.class).assertAt();\n\t\t// @formatter:off\n\t\tHomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t}\n\n\t@Test\n\tpublic void formLoginWhenCustomAuthenticationFailureHandlerThenUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.pathMatchers(\"/login\", \"/failure\").permitAll()\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin((login) -> login\n\t\t\t\t.authenticationFailureHandler(new RedirectServerAuthenticationFailureHandler(\"/failure\")))\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultLoginPage loginPage = HomePage.to(driver, DefaultLoginPage.class).assertAt();\n\t\t// @formatter:off\n\t\tloginPage.loginForm()\n\t\t\t\t.username(\"invalid\")\n\t\t\t\t.password(\"invalid\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\tassertThat(driver.getCurrentUrl()).endsWith(\"/failure\");\n\t}\n\n\t@Test\n\tpublic void formLoginWhenCustomRequiresAuthenticationMatcherThenUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.pathMatchers(\"/login\", \"/sign-in\").permitAll()\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin((login) -> login\n\t\t\t\t.requiresAuthenticationMatcher(new PathPatternParserServerWebExchangeMatcher(\"/sign-in\")))\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tdriver.get(\"http://localhost/sign-in\");\n\t\tassertThat(driver.getCurrentUrl()).endsWith(\"/login?error\");\n\t}\n\n\t@Test\n\tpublic void authenticationSuccess() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin((login) -> login\n\t\t\t\t.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler(\"/custom\")))\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultLoginPage loginPage = DefaultLoginPage.to(driver).assertAt();\n\t\t// @formatter:off\n\t\tHomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\tassertThat(driver.getCurrentUrl()).endsWith(\"/custom\");\n\t}\n\n\t@Test\n\tpublic void customAuthenticationManager() {\n\t\tReactiveAuthenticationManager defaultAuthenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tgiven(defaultAuthenticationManager.authenticate(any()))\n\t\t\t.willThrow(new RuntimeException(\"should not interact with default auth manager\"));\n\t\tgiven(customAuthenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\", \"ROLE_ADMIN\")));\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authenticationManager(defaultAuthenticationManager)\n\t\t\t.formLogin((login) -> login\n\t\t\t\t.authenticationManager(customAuthenticationManager))\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultLoginPage loginPage = DefaultLoginPage.to(driver).assertAt();\n\t\t// @formatter:off\n\t\tHomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tverifyNoMoreInteractions(defaultAuthenticationManager);\n\t}\n\n\t@Test\n\tpublic void formLoginSecurityContextRepository() {\n\t\tServerSecurityContextRepository defaultSecContextRepository = mock(ServerSecurityContextRepository.class);\n\t\tServerSecurityContextRepository formLoginSecContextRepository = mock(ServerSecurityContextRepository.class);\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"rob\", \"rob\", \"ROLE_USER\");\n\t\tgiven(defaultSecContextRepository.save(any(), any())).willReturn(Mono.empty());\n\t\tgiven(defaultSecContextRepository.load(any())).willReturn(authentication(token));\n\t\tgiven(formLoginSecContextRepository.save(any(), any())).willReturn(Mono.empty());\n\t\tgiven(formLoginSecContextRepository.load(any())).willReturn(authentication(token));\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.securityContextRepository(defaultSecContextRepository)\n\t\t\t.formLogin((login) -> login\n\t\t\t\t.securityContextRepository(formLoginSecContextRepository))\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultLoginPage loginPage = DefaultLoginPage.to(driver).assertAt();\n\t\t// @formatter:off\n\t\tHomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tverify(defaultSecContextRepository, atLeastOnce()).load(any());\n\t\tverify(formLoginSecContextRepository).save(any(), any());\n\t}\n\n\tMono<SecurityContext> authentication(Authentication authentication) {\n\t\tSecurityContext context = new SecurityContextImpl();\n\t\tcontext.setAuthentication(authentication);\n\t\treturn Mono.just(context);\n\t}\n\n\tpublic static class CustomLoginPage {\n\n\t\tprivate WebDriver driver;\n\n\t\tprivate LoginForm loginForm;\n\n\t\tpublic CustomLoginPage(WebDriver webDriver) {\n\t\t\tthis.driver = webDriver;\n\t\t\tthis.loginForm = PageFactory.initElements(webDriver, LoginForm.class);\n\t\t}\n\n\t\tpublic CustomLoginPage assertAt() {\n\t\t\tassertThat(this.driver.getTitle()).isEqualTo(\"Custom Log In Page\");\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic LoginForm loginForm() {\n\t\t\treturn this.loginForm;\n\t\t}\n\n\t\tpublic static class LoginForm {\n\n\t\t\tprivate WebDriver driver;\n\n\t\t\tprivate WebElement username;\n\n\t\t\tprivate WebElement password;\n\n\t\t\t@FindBy(css = \"button[type=submit]\")\n\t\t\tprivate WebElement submit;\n\n\t\t\tpublic LoginForm(WebDriver driver) {\n\t\t\t\tthis.driver = driver;\n\t\t\t}\n\n\t\t\tpublic LoginForm username(String username) {\n\t\t\t\tthis.username.sendKeys(username);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic LoginForm password(String password) {\n\t\t\t\tthis.password.sendKeys(password);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic <T> T submit(Class<T> page) {\n\t\t\t\tthis.submit.click();\n\t\t\t\treturn PageFactory.initElements(this.driver, page);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tpublic static class DefaultLoginPage {\n\n\t\tprivate WebDriver driver;\n\n\t\t@FindBy(css = \"div[role=alert]\")\n\t\tprivate WebElement alert;\n\n\t\tprivate LoginForm loginForm;\n\n\t\tprivate OAuth2Login oauth2Login = new OAuth2Login();\n\n\t\tpublic DefaultLoginPage(WebDriver webDriver) {\n\t\t\tthis.driver = webDriver;\n\t\t}\n\n\t\tstatic DefaultLoginPage create(WebDriver driver) {\n\t\t\treturn PageFactory.initElements(driver, DefaultLoginPage.class);\n\t\t}\n\n\t\tpublic DefaultLoginPage assertAt() {\n\t\t\tassertThat(this.driver.getTitle()).isEqualTo(\"Please sign in\");\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DefaultLoginPage assertError() {\n\t\t\tassertThat(this.alert.getText()).isEqualTo(\"Invalid credentials\");\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DefaultLoginPage assertLogout() {\n\t\t\tassertThat(this.alert.getText()).isEqualTo(\"You have been signed out\");\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DefaultLoginPage assertLoginFormNotPresent() {\n\t\t\tassertThatExceptionOfType(NoSuchElementException.class).isThrownBy(() -> loginForm().username(\"\"));\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DefaultLoginPage assertLoginFormPresent() {\n\t\t\tloginForm().username(\"\");\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic LoginForm loginForm() {\n\t\t\tif (this.loginForm == null) {\n\t\t\t\tthis.loginForm = PageFactory.initElements(this.driver, LoginForm.class);\n\t\t\t}\n\t\t\treturn this.loginForm;\n\t\t}\n\n\t\tpublic OAuth2Login oauth2Login() {\n\t\t\treturn this.oauth2Login;\n\t\t}\n\n\t\tstatic DefaultLoginPage to(WebDriver driver) {\n\t\t\tdriver.get(\"http://localhost/login\");\n\t\t\treturn PageFactory.initElements(driver, DefaultLoginPage.class);\n\t\t}\n\n\t\tpublic static class LoginForm {\n\n\t\t\tprivate WebDriver driver;\n\n\t\t\tprivate WebElement username;\n\n\t\t\tprivate WebElement password;\n\n\t\t\t@FindBy(css = \"button[type=submit]\")\n\t\t\tprivate WebElement submit;\n\n\t\t\tpublic LoginForm(WebDriver driver) {\n\t\t\t\tthis.driver = driver;\n\t\t\t}\n\n\t\t\tpublic LoginForm username(String username) {\n\t\t\t\tthis.username.sendKeys(username);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic LoginForm password(String password) {\n\t\t\t\tthis.password.sendKeys(password);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic <T> T submit(Class<T> page) {\n\t\t\t\tthis.submit.click();\n\t\t\t\treturn PageFactory.initElements(this.driver, page);\n\t\t\t}\n\n\t\t}\n\n\t\tpublic class OAuth2Login {\n\n\t\t\tpublic WebElement findClientRegistrationByName(String clientName) {\n\t\t\t\treturn DefaultLoginPage.this.driver.findElement(By.linkText(clientName));\n\t\t\t}\n\n\t\t\tpublic OAuth2Login assertClientRegistrationByName(String clientName) {\n\t\t\t\tfindClientRegistrationByName(clientName);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic DefaultLoginPage and() {\n\t\t\t\treturn DefaultLoginPage.this;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tpublic static class DefaultLogoutPage {\n\n\t\tprivate WebDriver driver;\n\n\t\t@FindBy(css = \"button[type=submit]\")\n\t\tprivate WebElement submit;\n\n\t\tpublic DefaultLogoutPage(WebDriver webDriver) {\n\t\t\tthis.driver = webDriver;\n\t\t}\n\n\t\tpublic DefaultLogoutPage assertAt() {\n\t\t\tassertThat(this.driver.getTitle()).isEqualTo(\"Confirm Log Out?\");\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DefaultLoginPage logout() {\n\t\t\tthis.submit.click();\n\t\t\treturn DefaultLoginPage.create(this.driver);\n\t\t}\n\n\t\tstatic DefaultLogoutPage to(WebDriver driver) {\n\t\t\tdriver.get(\"http://localhost/logout\");\n\t\t\treturn PageFactory.initElements(driver, DefaultLogoutPage.class);\n\t\t}\n\n\t}\n\n\tpublic static class HomePage {\n\n\t\tprivate WebDriver driver;\n\n\t\t@FindBy(tagName = \"body\")\n\t\tWebElement body;\n\n\t\tpublic HomePage(WebDriver driver) {\n\t\t\tthis.driver = driver;\n\t\t}\n\n\t\tpublic void assertAt() {\n\t\t\tassertThat(this.body.getText()).isEqualToIgnoringWhitespace(\"ok\");\n\t\t}\n\n\t\tstatic <T> T to(WebDriver driver, Class<T> page) {\n\t\t\tdriver.get(\"http://localhost/\");\n\t\t\treturn PageFactory.initElements(driver, page);\n\t\t}\n\n\t}\n\n\t@Controller\n\tpublic static class CustomLoginPageController {\n\n\t\t@ResponseBody\n\t\t@GetMapping(\"/login\")\n\t\tpublic Mono<String> login(ServerWebExchange exchange) {\n\t\t\tMono<CsrfToken> token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());\n\t\t\t// @formatter:off\n\t\t\treturn token.map((t) -> \"<!DOCTYPE html>\\n\"\n\t\t\t\t\t+ \"<html lang=\\\"en\\\">\\n\"\n\t\t\t\t\t+ \"  <head>\\n\"\n\t\t\t\t\t+ \"    <meta charset=\\\"utf-8\\\">\\n\"\n\t\t\t\t\t+ \"    <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1, shrink-to-fit=no\\\">\\n\"\n\t\t\t\t\t+ \"    <meta name=\\\"description\\\" content=\\\"\\\">\\n\"\n\t\t\t\t\t+ \"    <meta name=\\\"author\\\" content=\\\"\\\">\\n\"\n\t\t\t\t\t+ \"    <title>Custom Log In Page</title>\\n\"\n\t\t\t\t\t+ \"  </head>\\n\"\n\t\t\t\t\t+ \"  <body>\\n\"\n\t\t\t\t\t+ \"     <div>\\n\"\n\t\t\t\t\t+ \"      <form method=\\\"post\\\" action=\\\"/login\\\">\\n\"\n\t\t\t\t\t+ \"        <h2>Please sign in</h2>\\n\"\n\t\t\t\t\t+ \"        <p>\\n\"\n\t\t\t\t\t+ \"          <label for=\\\"username\\\">Username</label>\\n\"\n\t\t\t\t\t+ \"          <input type=\\\"text\\\" id=\\\"username\\\" name=\\\"username\\\" placeholder=\\\"Username\\\" required autofocus>\\n\"\n\t\t\t\t\t+ \"        </p>\\n\"\n\t\t\t\t\t+ \"        <p>\\n\"\n\t\t\t\t\t+ \"          <label for=\\\"password\\\" class=\\\"sr-only\\\">Password</label>\\n\"\n\t\t\t\t\t+ \"          <input type=\\\"password\\\" id=\\\"password\\\" name=\\\"password\\\" placeholder=\\\"Password\\\" required>\\n\"\n\t\t\t\t\t+ \"        </p>\\n\"\n\t\t\t\t\t+ \"        <input type=\\\"hidden\\\" name=\\\"\" + t.getParameterName() + \"\\\" value=\\\"\" + t.getToken() + \"\\\">\\n\"\n\t\t\t\t\t+ \"        <button type=\\\"submit\\\">Sign in</button>\\n\"\n\t\t\t\t\t+ \"      </form>\\n\"\n\t\t\t\t\t+ \"    </div>\\n\"\n\t\t\t\t\t+ \"  </body>\\n\"\n\t\t\t\t\t+ \"</html>\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.time.Duration;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy;\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter;\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * Tests for {@link ServerHttpSecurity.HeaderSpec}.\n *\n * @author Rob Winch\n * @author Vedran Pavic\n * @author Ankur Pathak\n * @author Marcus Da Coregio\n * @since 5.0\n */\npublic class HeaderSpecTests {\n\n\tprivate static final String CUSTOM_HEADER = \"CUSTOM-HEADER\";\n\n\tprivate static final String CUSTOM_VALUE = \"CUSTOM-VALUE\";\n\n\tprivate ServerHttpSecurity http = ServerHttpSecurity.http();\n\n\tprivate HttpHeaders expectedHeaders = new HttpHeaders();\n\n\tprivate Set<String> headerNamesNotPresent = new HashSet<>();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=31536000 ; includeSubDomains\");\n\t\tthis.expectedHeaders.add(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\");\n\t\tthis.expectedHeaders.add(HttpHeaders.PRAGMA, \"no-cache\");\n\t\tthis.expectedHeaders.add(HttpHeaders.EXPIRES, \"0\");\n\t\tthis.expectedHeaders.add(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, \"nosniff\");\n\t\tthis.expectedHeaders.add(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, \"DENY\");\n\t\tthis.expectedHeaders.add(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\");\n\t}\n\n\t@Test\n\tpublic void headersWhenDisableThenNoSecurityHeaders() {\n\t\tnew HashSet<>(this.expectedHeaders.headerNames()).forEach(this::expectHeaderNamesNotPresent);\n\t\tthis.http.headers((headers) -> headers.disable());\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenDisableInLambdaThenNoSecurityHeaders() {\n\t\tnew HashSet<>(this.expectedHeaders.headerNames()).forEach(this::expectHeaderNamesNotPresent);\n\t\tthis.http.headers((headers) -> headers.disable());\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenDisableAndInvokedExplicitlyThenDefautsUsed() {\n\t\tthis.http.headers((headers) -> headers.disable().headers(Customizer.withDefaults()));\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenDefaultsThenAllDefaultsWritten() {\n\t\tthis.http.headers(withDefaults());\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenDefaultsInLambdaThenAllDefaultsWritten() {\n\t\tthis.http.headers(withDefaults());\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenCacheDisableThenCacheNotWritten() {\n\t\texpectHeaderNamesNotPresent(HttpHeaders.CACHE_CONTROL, HttpHeaders.PRAGMA, HttpHeaders.EXPIRES);\n\t\tthis.http.headers((headers) -> headers.cache((cache) -> cache.disable()));\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenCacheDisableInLambdaThenCacheNotWritten() {\n\t\texpectHeaderNamesNotPresent(HttpHeaders.CACHE_CONTROL, HttpHeaders.PRAGMA, HttpHeaders.EXPIRES);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.cache((cache) -> cache.disable())\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenContentOptionsDisableThenContentTypeOptionsNotWritten() {\n\t\texpectHeaderNamesNotPresent(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS);\n\t\tthis.http.headers((headers) -> headers.contentTypeOptions((options) -> options.disable()));\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenContentOptionsDisableInLambdaThenContentTypeOptionsNotWritten() {\n\t\texpectHeaderNamesNotPresent(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS);\n\t\t// @formatter:off\n\t\tthis.http\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.contentTypeOptions((contentTypeOptions) -> contentTypeOptions.disable()\n\t\t\t\t));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenHstsDisableThenHstsNotWritten() {\n\t\texpectHeaderNamesNotPresent(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);\n\t\tthis.http.headers((headers) -> headers.hsts((hsts) -> hsts.disable()));\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenHstsDisableInLambdaThenHstsNotWritten() {\n\t\texpectHeaderNamesNotPresent(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.hsts((hsts) -> hsts.disable())\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenHstsCustomThenCustomHstsWritten() {\n\t\tthis.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);\n\t\tthis.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=60\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.hsts((hsts) -> hsts\n\t\t\t\t.maxAge(Duration.ofSeconds(60))\n\t\t\t\t.includeSubdomains(false)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenHstsCustomInLambdaThenCustomHstsWritten() {\n\t\tthis.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);\n\t\tthis.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=60\");\n\t\t// @formatter:off\n\t\tthis.http.headers(\n\t\t\t\t(headers) -> headers\n\t\t\t\t\t\t.hsts((hsts) -> hsts\n\t\t\t\t\t\t\t\t.maxAge(Duration.ofSeconds(60))\n\t\t\t\t\t\t\t\t.includeSubdomains(false)\n\t\t\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenHstsCustomWithPreloadThenCustomHstsWritten() {\n\t\tthis.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);\n\t\tthis.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=60 ; includeSubDomains ; preload\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.hsts((hsts) -> hsts\n\t\t\t\t.maxAge(Duration.ofSeconds(60))\n\t\t\t\t.preload(true)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenHstsCustomWithPreloadInLambdaThenCustomHstsWritten() {\n\t\tthis.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);\n\t\tthis.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=60 ; includeSubDomains ; preload\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.hsts((hsts) -> hsts\n\t\t\t\t\t\t.maxAge(Duration.ofSeconds(60))\n\t\t\t\t\t\t.preload(true)\n\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenFrameOptionsDisableThenFrameOptionsNotWritten() {\n\t\texpectHeaderNamesNotPresent(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.frameOptions((options) -> options.disable()));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenFrameOptionsDisableInLambdaThenFrameOptionsNotWritten() {\n\t\texpectHeaderNamesNotPresent(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.frameOptions((frameOptions) -> frameOptions\n\t\t\t\t\t\t.disable()\n\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenFrameOptionsModeThenFrameOptionsCustomMode() {\n\t\tthis.expectedHeaders.set(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, \"SAMEORIGIN\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.frameOptions((frameOptions) -> frameOptions\n\t\t\t\t.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenFrameOptionsModeInLambdaThenFrameOptionsCustomMode() {\n\t\tthis.expectedHeaders.set(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, \"SAMEORIGIN\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.frameOptions((frameOptions) -> frameOptions\n\t\t\t\t\t\t.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)\n\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenXssProtectionDisableThenXssProtectionNotWritten() {\n\t\texpectHeaderNamesNotPresent(\"X-Xss-Protection\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.xssProtection((xss) -> xss.disable()));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenXssProtectionDisableInLambdaThenXssProtectionNotWritten() {\n\t\texpectHeaderNamesNotPresent(\"X-Xss-Protection\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.xssProtection((xssProtection) -> xssProtection\n\t\t\t\t\t\t.disable()\n\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenXssProtectionValueDisabledThenXssProtectionWritten() {\n\t\tthis.expectedHeaders.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.xssProtection((xss) -> xss\n\t\t\t\t.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenXssProtectionValueEnabledThenXssProtectionWritten() {\n\t\tthis.expectedHeaders.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"1\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.xssProtection((xss) -> xss\n\t\t\t\t.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenXssProtectionValueEnabledModeBlockThenXssProtectionWritten() {\n\t\tthis.expectedHeaders.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"1; mode=block\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.xssProtection((xss) -> xss\n\t\t\t\t.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenXssProtectionValueDisabledInLambdaThenXssProtectionWritten() {\n\t\tthis.expectedHeaders.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\");\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.xssProtection((xssProtection) -> xssProtection.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED)\n\t\t\t));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenFeaturePolicyEnabledThenFeaturePolicyWritten() {\n\t\tString policyDirectives = \"Feature-Policy\";\n\t\tthis.expectedHeaders.add(FeaturePolicyServerHttpHeadersWriter.FEATURE_POLICY, policyDirectives);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.featurePolicy(policyDirectives));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenContentSecurityPolicyEnabledThenFeaturePolicyWritten() {\n\t\tString policyDirectives = \"default-src 'self'\";\n\t\tthis.expectedHeaders.add(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY,\n\t\t\t\tpolicyDirectives);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.contentSecurityPolicy((csp) -> csp.policyDirectives(policyDirectives)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenContentSecurityPolicyEnabledWithDefaultsInLambdaThenDefaultPolicyWritten() {\n\t\tString expectedPolicyDirectives = \"default-src 'self'\";\n\t\tthis.expectedHeaders.add(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY,\n\t\t\t\texpectedPolicyDirectives);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.contentSecurityPolicy(withDefaults())\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenContentSecurityPolicyEnabledInLambdaThenContentSecurityPolicyWritten() {\n\t\tString policyDirectives = \"default-src 'self' *.trusted.com\";\n\t\tthis.expectedHeaders.add(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY,\n\t\t\t\tpolicyDirectives);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t\t\t.policyDirectives(policyDirectives)\n\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenReferrerPolicyEnabledThenFeaturePolicyWritten() {\n\t\tthis.expectedHeaders.add(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY,\n\t\t\t\tReferrerPolicy.NO_REFERRER.getPolicy());\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.referrerPolicy(Customizer.withDefaults()));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenReferrerPolicyEnabledInLambdaThenReferrerPolicyWritten() {\n\t\tthis.expectedHeaders.add(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY,\n\t\t\t\tReferrerPolicy.NO_REFERRER.getPolicy());\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.referrerPolicy(withDefaults()\n\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenReferrerPolicyCustomEnabledThenFeaturePolicyCustomWritten() {\n\t\tthis.expectedHeaders.add(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY,\n\t\t\t\tReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE.getPolicy());\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.referrerPolicy((referrer) -> referrer.policy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenReferrerPolicyCustomEnabledInLambdaThenCustomReferrerPolicyWritten() {\n\t\tthis.expectedHeaders.add(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY,\n\t\t\t\tReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE.getPolicy());\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.referrerPolicy((referrerPolicy) -> referrerPolicy\n\t\t\t\t\t\t.policy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE)\n\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenCustomHeadersWriter() {\n\t\tthis.expectedHeaders.add(CUSTOM_HEADER, CUSTOM_VALUE);\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t\t.writer((exchange) -> Mono.just(exchange)\n\t\t\t\t\t.doOnNext((it) -> it.getResponse().getHeaders().add(CUSTOM_HEADER, CUSTOM_VALUE))\n\t\t\t\t\t.then()\n\t\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenCrossOriginPoliciesCustomEnabledThenCustomCrossOriginPoliciesWritten() {\n\t\tthis.expectedHeaders.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,\n\t\t\t\tCrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS\n\t\t\t\t\t.getPolicy());\n\t\tthis.expectedHeaders.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,\n\t\t\t\tCrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP.getPolicy());\n\t\tthis.expectedHeaders.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,\n\t\t\t\tCrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN.getPolicy());\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.crossOriginOpenerPolicy((opener) -> opener\n\t\t\t\t.policy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS))\n\t\t\t.crossOriginEmbedderPolicy((embedder) -> embedder\n\t\t\t\t.policy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP))\n\t\t\t.crossOriginResourcePolicy((resource) -> resource\n\t\t\t\t.policy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN)));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\t@Test\n\tpublic void headersWhenCrossOriginPoliciesCustomEnabledInLambdaThenCustomCrossOriginPoliciesWritten() {\n\t\tthis.expectedHeaders.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,\n\t\t\t\tCrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS\n\t\t\t\t\t.getPolicy());\n\t\tthis.expectedHeaders.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,\n\t\t\t\tCrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP.getPolicy());\n\t\tthis.expectedHeaders.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,\n\t\t\t\tCrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN.getPolicy());\n\t\t// @formatter:off\n\t\tthis.http.headers((headers) -> headers\n\t\t\t.crossOriginOpenerPolicy((policy) -> policy\n\t\t\t\t\t.policy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS)\n\t\t\t)\n\t\t\t.crossOriginEmbedderPolicy((policy) -> policy\n\t\t\t\t\t.policy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)\n\t\t\t)\n\t\t\t.crossOriginResourcePolicy((policy) -> policy\n\t\t\t\t\t.policy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN)\n\t\t\t));\n\t\t// @formatter:on\n\t\tassertHeaders();\n\t}\n\n\tprivate void expectHeaderNamesNotPresent(String... headerNames) {\n\t\tfor (String headerName : headerNames) {\n\t\t\tthis.expectedHeaders.remove(headerName);\n\t\t\tthis.headerNamesNotPresent.add(headerName);\n\t\t}\n\t}\n\n\tprivate void assertHeaders() {\n\t\tWebTestClient client = buildClient();\n\t\tFluxExchangeResult<String> response = client.get()\n\t\t\t.uri(\"https://example.com/\")\n\t\t\t.exchange()\n\t\t\t.returnResult(String.class);\n\t\tHttpHeaders responseHeaders = response.getResponseHeaders();\n\t\tif (!this.expectedHeaders.isEmpty()) {\n\t\t\tthis.expectedHeaders.forEach(\n\t\t\t\t\t(headerName, headerValues) -> assertThat(responseHeaders.get(headerName)).isEqualTo(headerValues));\n\t\t}\n\t\tif (!this.headerNamesNotPresent.isEmpty()) {\n\t\t\tassertThat(responseHeaders.headerNames()).doesNotContainAnyElementsOf(this.headerNamesNotPresent);\n\t\t}\n\t}\n\n\tprivate WebTestClient buildClient() {\n\t\treturn WebTestClientBuilder.bindToWebFilters(this.http.build()).build();\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/HttpsRedirectSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.apache.http.HttpHeaders;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.reactive.config.EnableWebFlux;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * Tests for {@link HttpsRedirectSpecTests}\n *\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpsRedirectSpecTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tWebTestClient client;\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\t// @formatter:off\n\t\tthis.client = WebTestClient\n\t\t\t\t.bindToApplicationContext(context)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenSecureThenDoesNotRedirect() {\n\t\tthis.spring.register(RedirectToHttpConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"https://localhost\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isNotFound();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureThenRespondsWithRedirectToSecure() {\n\t\tthis.spring.register(RedirectToHttpConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"http://localhost\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"https://localhost\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureAndRedirectConfiguredInLambdaThenRespondsWithRedirectToSecure() {\n\t\tthis.spring.register(RedirectToHttpsInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"http://localhost\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"https://localhost\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureAndPathRequiresTransportSecurityThenRedirects() {\n\t\tthis.spring.register(SometimesRedirectToHttpsConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"http://localhost:8080\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isNotFound();\n\t\tthis.client.get()\n\t\t\t\t.uri(\"http://localhost:8080/secure\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"https://localhost:8443/secure\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureAndPathRequiresTransportSecurityInLambdaThenRedirects() {\n\t\tthis.spring.register(SometimesRedirectToHttpsInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"http://localhost:8080\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isNotFound();\n\t\tthis.client.get()\n\t\t\t\t.uri(\"http://localhost:8080/secure\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"https://localhost:8443/secure\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureAndUsingCustomPortMapperThenRespondsWithRedirectToSecurePort() {\n\t\tthis.spring.register(RedirectToHttpsViaCustomPortsConfig.class).autowire();\n\t\tPortMapper portMapper = this.spring.getContext().getBean(PortMapper.class);\n\t\tgiven(portMapper.lookupHttpsPort(4080)).willReturn(4443);\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"http://localhost:4080\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"https://localhost:4443\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenInsecureAndUsingCustomPortMapperInLambdaThenRespondsWithRedirectToSecurePort() {\n\t\tthis.spring.register(RedirectToHttpsViaCustomPortsInLambdaConfig.class).autowire();\n\t\tPortMapper portMapper = this.spring.getContext().getBean(PortMapper.class);\n\t\tgiven(portMapper.lookupHttpsPort(4080)).willReturn(4443);\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"http://localhost:4080\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"https://localhost:4443\");\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class RedirectToHttpConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.redirectToHttps(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class RedirectToHttpsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.redirectToHttps(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class SometimesRedirectToHttpsConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.redirectToHttps((https) -> https\n\t\t\t\t\t.httpsRedirectWhen(new PathPatternParserServerWebExchangeMatcher(\"/secure\")));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class SometimesRedirectToHttpsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.redirectToHttps((redirectToHttps) -> redirectToHttps\n\t\t\t\t\t\t.httpsRedirectWhen(new PathPatternParserServerWebExchangeMatcher(\"/secure\"))\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class RedirectToHttpsViaCustomPortsConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.redirectToHttps((https) -> https\n\t\t\t\t\t.portMapper(portMapper()));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tPortMapper portMapper() {\n\t\t\treturn mock(PortMapper.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class RedirectToHttpsViaCustomPortsInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.redirectToHttps((redirectToHttps) -> redirectToHttps\n\t\t\t\t\t\t.portMapper(portMapper())\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tPortMapper portMapper() {\n\t\t\treturn mock(PortMapper.class);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.openqa.selenium.WebDriver;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Shazin Sadakath\n * @since 5.0\n */\npublic class LogoutSpecTests {\n\n\tprivate ServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication();\n\n\t@Test\n\tpublic void defaultLogout() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin(withDefaults())\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tFormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage\n\t\t\t.to(driver, FormLoginTests.DefaultLoginPage.class)\n\t\t\t.assertAt();\n\t\t// @formatter:off\n\t\tloginPage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"invalid\")\n\t\t\t\t.submit(FormLoginTests.DefaultLoginPage.class)\n\t\t\t\t.assertError();\n\t\tFormLoginTests.HomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(FormLoginTests.HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tloginPage = FormLoginTests.DefaultLogoutPage.to(driver).assertAt().logout();\n\t\tloginPage.assertAt().assertLogout();\n\t}\n\n\t@Test\n\tpublic void customLogout() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin(withDefaults())\n\t\t\t.logout((logout) -> logout\n\t\t\t\t.requiresLogout(ServerWebExchangeMatchers.pathMatchers(\"/custom-logout\")))\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tFormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage\n\t\t\t.to(driver, FormLoginTests.DefaultLoginPage.class)\n\t\t\t.assertAt();\n\t\t// @formatter:off\n\t\tloginPage = loginPage.loginForm()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"invalid\")\n\t\t\t\t\t.submit(FormLoginTests.DefaultLoginPage.class)\n\t\t\t\t.assertError();\n\t\tFormLoginTests.HomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(FormLoginTests.HomePage.class);\n\t\thomePage.assertAt();\n\t\t// @formatter:on\n\t\tdriver.get(\"http://localhost/custom-logout\");\n\t\tFormLoginTests.DefaultLoginPage.create(driver).assertAt().assertLogout();\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomLogoutInLambdaThenCustomLogoutUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t\t.requiresLogout(ServerWebExchangeMatchers.pathMatchers(\"/custom-logout\"))\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tFormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage\n\t\t\t.to(driver, FormLoginTests.DefaultLoginPage.class)\n\t\t\t.assertAt();\n\t\t// @formatter:off\n\t\tloginPage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"invalid\")\n\t\t\t\t.submit(FormLoginTests.DefaultLoginPage.class)\n\t\t\t\t.assertError();\n\t\tFormLoginTests.HomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\").password(\"password\")\n\t\t\t\t.submit(FormLoginTests.HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tdriver.get(\"http://localhost/custom-logout\");\n\t\tFormLoginTests.DefaultLoginPage.create(driver).assertAt().assertLogout();\n\t}\n\n\t@Test\n\tpublic void logoutWhenDisabledThenDefaultLogoutPageDoesNotExist() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin(withDefaults())\n\t\t\t.logout((logout) -> logout.disable())\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToControllerAndWebFilters(HomeController.class, securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tFormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage\n\t\t\t.to(driver, FormLoginTests.DefaultLoginPage.class)\n\t\t\t.assertAt();\n\t\t// @formatter:off\n\t\tFormLoginTests.HomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(FormLoginTests.HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tFormLoginTests.DefaultLogoutPage.to(driver);\n\t\tassertThat(driver.getPageSource()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomSecurityContextRepositoryThenLogsOut() {\n\t\tWebSessionServerSecurityContextRepository repository = new WebSessionServerSecurityContextRepository();\n\t\trepository.setSpringSecurityContextAttrName(\"CUSTOM_CONTEXT_ATTR\");\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.securityContextRepository(repository)\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin(withDefaults())\n\t\t\t.logout(withDefaults())\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tFormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage\n\t\t\t.to(driver, FormLoginTests.DefaultLoginPage.class)\n\t\t\t.assertAt();\n\t\t// @formatter:off\n\t\tFormLoginTests.HomePage homePage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(FormLoginTests.HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tFormLoginTests.DefaultLogoutPage.to(driver).assertAt().logout();\n\t\tFormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class).assertAt();\n\t}\n\n\t@Test\n\tpublic void multipleLogoutHandlers() {\n\t\tInMemorySecurityContextRepository repository = new InMemorySecurityContextRepository();\n\t\tMultiValueMap<String, String> logoutData = new LinkedMultiValueMap<>();\n\t\tServerLogoutHandler handler1 = (exchange, authentication) -> {\n\t\t\tlogoutData.add(\"handler-header\", \"value1\");\n\t\t\treturn Mono.empty();\n\t\t};\n\t\tServerLogoutHandler handler2 = (exchange, authentication) -> {\n\t\t\tlogoutData.add(\"handler-header\", \"value2\");\n\t\t\treturn Mono.empty();\n\t\t};\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t\t.securityContextRepository(repository)\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated())\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.logout((logoutSpec) -> logoutSpec.logoutHandler((handlers) -> {\n\t\t\t\t\thandlers.add(handler1);\n\t\t\t\t\thandlers.add(0, handler2);\n\t\t\t\t}))\n\t\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(securityWebFilter)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tFormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage\n\t\t\t.to(driver, FormLoginTests.DefaultLoginPage.class)\n\t\t\t.assertAt();\n\t\t// @formatter:off\n\t\tloginPage = loginPage.loginForm()\n\t\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t\t.password(\"invalid\")\n\t\t\t\t\t\t\t.submit(FormLoginTests.DefaultLoginPage.class)\n\t\t\t\t\t\t\t.assertError();\n\t\tFormLoginTests.HomePage homePage = loginPage.loginForm()\n\t\t\t\t\t\t\t\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t.submit(FormLoginTests.HomePage.class);\n\t\t// @formatter:on\n\t\thomePage.assertAt();\n\t\tSecurityContext savedContext = repository.getSavedContext();\n\t\tassertThat(savedContext).isNotNull();\n\t\tassertThat(savedContext.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\n\t\tloginPage = FormLoginTests.DefaultLogoutPage.to(driver).assertAt().logout();\n\t\tloginPage.assertAt().assertLogout();\n\t\tassertThat(logoutData).hasSize(1);\n\t\tassertThat(logoutData.get(\"handler-header\")).containsExactly(\"value2\", \"value1\");\n\t\tsavedContext = repository.getSavedContext();\n\t\tassertThat(savedContext).isNull();\n\t}\n\n\tprivate static class InMemorySecurityContextRepository implements ServerSecurityContextRepository {\n\n\t\tprivate @Nullable SecurityContext savedContext;\n\n\t\t@Override\n\t\tpublic Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {\n\t\t\tthis.savedContext = context;\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<SecurityContext> load(ServerWebExchange exchange) {\n\t\t\treturn Mono.justOrEmpty(this.savedContext);\n\t\t}\n\n\t\tprivate @Nullable SecurityContext getSavedContext() {\n\t\t\treturn this.savedContext;\n\t\t}\n\n\t}\n\n\t@RestController\n\tpublic static class HomeController {\n\n\t\t@GetMapping(\"/\")\n\t\tpublic String ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.net.URI;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.config.EnableWebFlux;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n * @author Parikshit Dutta\n * @since 5.1\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class OAuth2ClientSpecTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tprivate WebTestClient client;\n\n\tprivate ClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\t// @formatter:off\n\t\tthis.client = WebTestClient\n\t\t\t\t.bindToApplicationContext(context)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void registeredOAuth2AuthorizedClientWhenAuthenticatedThenRedirects() {\n\t\tthis.spring.register(Config.class, AuthorizedClientController.class).autowire();\n\t\tReactiveClientRegistrationRepository repository = this.spring.getContext()\n\t\t\t.getBean(ReactiveClientRegistrationRepository.class);\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = this.spring.getContext()\n\t\t\t.getBean(ServerOAuth2AuthorizedClientRepository.class);\n\t\tgiven(repository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));\n\t\tgiven(authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void registeredOAuth2AuthorizedClientWhenAnonymousThenRedirects() {\n\t\tthis.spring.register(Config.class, AuthorizedClientController.class).autowire();\n\t\tReactiveClientRegistrationRepository repository = this.spring.getContext()\n\t\t\t.getBean(ReactiveClientRegistrationRepository.class);\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = this.spring.getContext()\n\t\t\t.getBean(ServerOAuth2AuthorizedClientRepository.class);\n\t\tgiven(repository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));\n\t\tgiven(authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenCustomObjectsThenUsed() {\n\t\tthis.spring\n\t\t\t.register(ClientRegistrationConfig.class, OAuth2ClientCustomConfig.class, AuthorizedClientController.class)\n\t\t\t.autowire();\n\t\tOAuth2ClientCustomConfig config = this.spring.getContext().getBean(OAuth2ClientCustomConfig.class);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tReactiveAuthenticationManager manager = config.manager;\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = config.authorizationRequestRepository;\n\t\tServerOAuth2AuthorizationRequestResolver resolver = config.resolver;\n\t\tServerRequestCache requestCache = config.requestCache;\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.redirectUri(\"/authorize/oauth2/code/registration-id\")\n\t\t\t.build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.success()\n\t\t\t.redirectUri(\"/authorize/oauth2/code/registration-id\")\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tOAuth2AuthorizationCodeAuthenticationToken result = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tthis.registration, authorizationExchange, accessToken);\n\t\tgiven(authorizationRequestRepository.loadAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(authorizationRequest));\n\t\tgiven(resolver.resolve(any())).willReturn(Mono.empty());\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(new TestingAuthenticationToken(\"a\", \"b\", \"c\")));\n\t\tgiven(manager.authenticate(any())).willReturn(Mono.just(result));\n\t\tgiven(requestCache.getRedirectUri(any())).willReturn(Mono.just(URI.create(\"/saved-request\")));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/authorize/oauth2/code/registration-id\")\n\t\t\t\t\t\t.queryParam(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t\t\t\t.queryParam(OAuth2ParameterNames.STATE, \"state\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection();\n\t\t// @formatter:on\n\t\tverify(converter).convert(any());\n\t\tverify(manager).authenticate(any());\n\t\tverify(requestCache).getRedirectUri(any());\n\t\tverify(resolver).resolve(any());\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenCustomObjectsInLambdaThenUsed() {\n\t\tthis.spring\n\t\t\t.register(ClientRegistrationConfig.class, OAuth2ClientInLambdaCustomConfig.class,\n\t\t\t\t\tAuthorizedClientController.class)\n\t\t\t.autowire();\n\t\tOAuth2ClientInLambdaCustomConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2ClientInLambdaCustomConfig.class);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tReactiveAuthenticationManager manager = config.manager;\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = config.authorizationRequestRepository;\n\t\tServerRequestCache requestCache = config.requestCache;\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.redirectUri(\"/authorize/oauth2/code/registration-id\")\n\t\t\t.build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.success()\n\t\t\t.redirectUri(\"/authorize/oauth2/code/registration-id\")\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tOAuth2AuthorizationCodeAuthenticationToken result = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tthis.registration, authorizationExchange, accessToken);\n\t\tgiven(authorizationRequestRepository.loadAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(authorizationRequest));\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(new TestingAuthenticationToken(\"a\", \"b\", \"c\")));\n\t\tgiven(manager.authenticate(any())).willReturn(Mono.just(result));\n\t\tgiven(requestCache.getRedirectUri(any())).willReturn(Mono.just(URI.create(\"/saved-request\")));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/authorize/oauth2/code/registration-id\")\n\t\t\t\t\t\t.queryParam(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t\t\t\t.queryParam(OAuth2ParameterNames.STATE, \"state\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection();\n\t\t// @formatter:on\n\t\tverify(converter).convert(any());\n\t\tverify(manager).authenticate(any());\n\t\tverify(requestCache).getRedirectUri(any());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void oauth2ClientWhenCustomAccessTokenResponseClientThenUsed() {\n\t\tthis.spring.register(OAuth2ClientBeanConfig.class, AuthorizedClientController.class).autowire();\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()\n\t\t\t.getBean(ReactiveClientRegistrationRepository.class);\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.just(this.registration));\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = this.spring.getContext()\n\t\t\t.getBean(ServerOAuth2AuthorizedClientRepository.class);\n\t\tgiven(authorizedClientRepository.saveAuthorizedClient(any(OAuth2AuthorizedClient.class),\n\t\t\t\tany(Authentication.class), any(ServerWebExchange.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(ServerAuthorizationRequestRepository.class);\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.redirectUri(\"/authorize/oauth2/code/registration-id\")\n\t\t\t.build();\n\t\tgiven(authorizationRequestRepository.loadAuthorizationRequest(any(ServerWebExchange.class)))\n\t\t\t.willReturn(Mono.just(authorizationRequest));\n\t\tgiven(authorizationRequestRepository.removeAuthorizationRequest(any(ServerWebExchange.class)))\n\t\t\t.willReturn(Mono.just(authorizationRequest));\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(ReactiveOAuth2AccessTokenResponseClient.class);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"token\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.scopes(Set.of())\n\t\t\t.expiresIn(300)\n\t\t\t.build();\n\t\tgiven(accessTokenResponseClient.getTokenResponse(any(OAuth2AuthorizationCodeGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t.path(\"/authorize/oauth2/code/registration-id\")\n\t\t\t\t.queryParam(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t\t.queryParam(OAuth2ParameterNames.STATE, \"state\")\n\t\t\t\t.build()\n\t\t\t)\n\t\t\t.exchange()\n\t\t\t.expectStatus().is3xxRedirection();\n\t\t// @formatter:on\n\t\tArgumentCaptor<OAuth2AuthorizationCodeGrantRequest> grantRequestArgumentCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizationCodeGrantRequest.class);\n\t\tverify(accessTokenResponseClient).getTokenResponse(grantRequestArgumentCaptor.capture());\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = grantRequestArgumentCaptor.getValue();\n\t\tassertThat(grantRequest.getClientRegistration()).isEqualTo(this.registration);\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(grantRequest.getAuthorizationExchange().getAuthorizationRequest()).isEqualTo(authorizationRequest);\n\t\tassertThat(grantRequest.getAuthorizationExchange().getAuthorizationResponse().getCode()).isEqualTo(\"code\");\n\t\tassertThat(grantRequest.getAuthorizationExchange().getAuthorizationResponse().getState()).isEqualTo(\"state\");\n\t\tassertThat(grantRequest.getAuthorizationExchange().getAuthorizationResponse().getRedirectUri())\n\t\t\t.startsWith(\"/authorize/oauth2/code/registration-id\");\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Client(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn mock(ReactiveClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn mock(ServerOAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class AuthorizedClientController {\n\n\t\t@GetMapping(\"/\")\n\t\tString home(@RegisteredOAuth2AuthorizedClient(\"github\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\treturn \"home\";\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class ClientRegistrationConfig {\n\n\t\tprivate ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\n\t\t@Bean\n\t\tInMemoryReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn new InMemoryReactiveClientRegistrationRepository(this.clientRegistration);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class OAuth2ClientCustomConfig {\n\n\t\tReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);\n\n\t\tServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);\n\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = mock(\n\t\t\t\tServerAuthorizationRequestRepository.class);\n\n\t\tServerOAuth2AuthorizationRequestResolver resolver = mock(ServerOAuth2AuthorizationRequestResolver.class);\n\n\t\tServerRequestCache requestCache = mock(ServerRequestCache.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Client((client) -> client\n\t\t\t\t\t.authenticationConverter(this.authenticationConverter)\n\t\t\t\t\t.authenticationManager(this.manager)\n\t\t\t\t\t.authorizationRequestRepository(this.authorizationRequestRepository)\n\t\t\t\t\t.authorizationRequestResolver(this.resolver))\n\t\t\t\t.requestCache((c) -> c.requestCache(this.requestCache));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class OAuth2ClientInLambdaCustomConfig {\n\n\t\tReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);\n\n\t\tServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);\n\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = mock(\n\t\t\t\tServerAuthorizationRequestRepository.class);\n\n\t\tServerRequestCache requestCache = mock(ServerRequestCache.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Client((oauth2Client) -> oauth2Client\n\t\t\t\t\t\t.authenticationConverter(this.authenticationConverter)\n\t\t\t\t\t\t.authenticationManager(this.manager)\n\t\t\t\t\t\t.authorizationRequestRepository(this.authorizationRequestRepository))\n\t\t\t\t.requestCache((c) -> c.requestCache(this.requestCache));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2ClientBeanConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2Client((oauth2Client) -> oauth2Client\n\t\t\t\t\t.authorizationRequestRepository(authorizationRequestRepository())\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {\n\t\t\treturn mock(ServerAuthorizationRequestRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {\n\t\t\treturn mock(ReactiveOAuth2AccessTokenResponseClient.class);\n\t\t}\n\n\t\t@Bean\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn mock(ReactiveClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn mock(ServerOAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.stubbing.Answer;\nimport org.openqa.selenium.WebDriver;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;\nimport org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2LoginSpec.OidcSessionRegistryAuthenticationWebFilter;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeReactiveAuthenticationManager;\nimport org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.oidc.web.server.logout.OidcClientInitiatedServerLogoutSuccessHandler;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtValidationException;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.WebFilterChainProxy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.config.EnableWebFlux;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.WebHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 5.1\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OAuth2LoginTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tprivate WebTestClient client;\n\n\t@Autowired\n\tprivate WebFilterChainProxy springSecurity;\n\n\tprivate static ClientRegistration github = CommonOAuth2Provider.GITHUB.getBuilder(\"github\")\n\t\t.clientId(\"client\")\n\t\t.clientSecret(\"secret\")\n\t\t.build();\n\n\tprivate static ClientRegistration google = CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t\t.clientId(\"client\")\n\t\t.clientSecret(\"secret\")\n\t\t.build();\n\n\t// @formatter:off\n\tprivate static ClientRegistration clientCredentials = TestClientRegistrations.clientCredentials()\n\t\t\t.build();\n\t// @formatter:on\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tif (context.getBeanNamesForType(WebHandler.class).length > 0) {\n\t\t\t// @formatter:off\n\t\t\tthis.client = WebTestClient\n\t\t\t\t\t.bindToApplicationContext(context)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void defaultLoginPageWithMultipleClientRegistrationsThenLinks() {\n\t\tthis.spring.register(OAuth2LoginWithMultipleClientRegistrations.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurity)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\tFormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class)\n\t\t\t\t.assertAt()\n\t\t\t\t.assertLoginFormNotPresent()\n\t\t\t\t.oauth2Login()\n\t\t\t\t\t.assertClientRegistrationByName(OAuth2LoginTests.github.getClientName())\n\t\t\t\t\t.and();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void defaultLoginPageWithSingleClientRegistrationThenRedirect() {\n\t\tthis.spring.register(OAuth2LoginWithSingleClientRegistrations.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(new GitHubWebFilter(), this.springSecurity)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tdriver.get(\"http://localhost/\");\n\t\tassertThat(driver.getCurrentUrl()).startsWith(\"https://github.com/login/oauth/authorize\");\n\t}\n\n\t// gh-9457\n\t@Test\n\tpublic void defaultLoginPageWithAuthorizationCodeAndClientCredentialsClientRegistrationThenRedirect() {\n\t\tthis.spring.register(OAuth2LoginWithAuthorizationCodeAndClientCredentialsClientRegistration.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(new GitHubWebFilter(), this.springSecurity)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tdriver.get(\"http://localhost/\");\n\t\tassertThat(driver.getCurrentUrl()).startsWith(\"https://github.com/login/oauth/authorize\");\n\t}\n\n\t@Test\n\tpublic void defaultLoginPageWithSingleClientRegistrationAndFormLoginThenLinks() {\n\t\tthis.spring.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2LoginWithFormLogin.class).autowire();\n\t\t// @formatter:off\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(new GitHubWebFilter(), this.springSecurity)\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\tFormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class)\n\t\t\t\t.assertAt()\n\t\t\t\t.assertLoginFormPresent()\n\t\t\t\t.oauth2Login()\n\t\t\t\t\t.assertClientRegistrationByName(OAuth2LoginTests.github.getClientName());\n\t\t// @formatter:on\n\t}\n\n\t// gh-8118\n\t@Test\n\tpublic void defaultLoginPageWithSingleClientRegistrationAndXhrRequestThenDoesNotRedirectForAuthorization() {\n\t\tthis.spring.register(OAuth2LoginWithSingleClientRegistrations.class, WebFluxConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.header(\"X-Requested-With\", \"XMLHttpRequest\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"/login\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void defaultLoginPageWithOAuth2LoginHttpBasicAndXhrRequestThenUnauthorized() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2LoginWithHttpBasic.class,\n\t\t\t\t\tWebFluxConfig.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.header(\"X-Requested-With\", \"XMLHttpRequest\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void defaultLoginPageWhenCustomLoginPageThenGeneratedLoginPageDoesNotExist() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2LoginWithCustomLoginPage.class,\n\t\t\t\t\tWebFluxConfig.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t.uri(\"/login\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().isNotFound();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenCustomLoginPageAndSingleClientRegistrationThenRedirectsToLoginPage() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2LoginWithCustomLoginPage.class,\n\t\t\t\t\tWebFluxConfig.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"/login\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenCustomLoginPageAndMultipleClientRegistrationsThenRedirectsToLoginPage() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithMultipleClientRegistrations.class, OAuth2LoginWithCustomLoginPage.class,\n\t\t\t\t\tWebFluxConfig.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"/login\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenProviderLoginPageAndMultipleClientRegistrationsThenRedirectsToProvider() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithMultipleClientRegistrations.class, OAuth2LoginWithProviderLoginPage.class,\n\t\t\t\t\tWebFluxConfig.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t.expectHeader().valueEquals(HttpHeaders.LOCATION, \"/oauth2/authorization/github\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2AuthorizeWhenCustomObjectsThenUsed() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2AuthorizeWithMockObjectsConfig.class,\n\t\t\t\t\tAuthorizedClientController.class)\n\t\t\t.autowire();\n\t\tOAuth2AuthorizeWithMockObjectsConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2AuthorizeWithMockObjectsConfig.class);\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = config.authorizedClientRepository;\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = config.authorizationRequestRepository;\n\t\tServerRequestCache requestCache = config.requestCache;\n\t\tgiven(authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).willReturn(Mono.empty());\n\t\tgiven(authorizationRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());\n\t\tgiven(requestCache.removeMatchingRequest(any())).willReturn(Mono.empty());\n\t\tgiven(requestCache.saveRequest(any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection();\n\t\t// @formatter:on\n\t\tverify(authorizedClientRepository).loadAuthorizedClient(any(), any(), any());\n\t\tverify(authorizationRequestRepository).saveAuthorizationRequest(any(), any());\n\t\tverify(requestCache).saveRequest(any());\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenCustomObjectsThenUsed() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2LoginMockAuthenticationManagerConfig.class)\n\t\t\t.autowire();\n\t\tString redirectLocation = \"/custom-redirect-location\";\n\t\tWebTestClient webTestClient = WebTestClientBuilder.bindToWebFilters(this.springSecurity).build();\n\t\tOAuth2LoginMockAuthenticationManagerConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2LoginMockAuthenticationManagerConfig.class);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tReactiveAuthenticationManager manager = config.manager;\n\t\tServerWebExchangeMatcher matcher = config.matcher;\n\t\tServerOAuth2AuthorizationRequestResolver resolver = config.resolver;\n\t\tServerAuthenticationSuccessHandler successHandler = config.successHandler;\n\t\tOAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success();\n\t\tOAuth2User user = TestOAuth2Users.create();\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tOAuth2LoginAuthenticationToken result = new OAuth2LoginAuthenticationToken(github, exchange, user,\n\t\t\t\tuser.getAuthorities(), accessToken);\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(new TestingAuthenticationToken(\"a\", \"b\", \"c\")));\n\t\tgiven(manager.authenticate(any())).willReturn(Mono.just(result));\n\t\tgiven(matcher.matches(any())).willReturn(ServerWebExchangeMatcher.MatchResult.match());\n\t\tgiven(resolver.resolve(any())).willReturn(Mono.empty());\n\t\tgiven(successHandler.onAuthenticationSuccess(any(), any())).willAnswer((Answer<Mono<Void>>) (invocation) -> {\n\t\t\tWebFilterExchange webFilterExchange = invocation.getArgument(0);\n\t\t\tAuthentication authentication = invocation.getArgument(1);\n\t\t\treturn new RedirectServerAuthenticationSuccessHandler(redirectLocation)\n\t\t\t\t.onAuthenticationSuccess(webFilterExchange, authentication);\n\t\t});\n\t\t// @formatter:off\n\t\twebTestClient.get()\n\t\t\t\t.uri(\"/login/oauth2/code/github\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", redirectLocation);\n\t\t// @formatter:on\n\t\tverify(converter).convert(any());\n\t\tverify(manager).authenticate(any());\n\t\tverify(matcher).matches(any());\n\t\tverify(resolver).resolve(any());\n\t\tverify(successHandler).onAuthenticationSuccess(any(), any());\n\t}\n\n\t@Test\n\tpublic void oauth2LoginFailsWhenCustomObjectsThenUsed() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2LoginMockAuthenticationManagerConfig.class)\n\t\t\t.autowire();\n\t\tString redirectLocation = \"/custom-redirect-location\";\n\t\tString failureRedirectLocation = \"/failure-redirect-location\";\n\t\t// @formatter:off\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurity)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2LoginMockAuthenticationManagerConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2LoginMockAuthenticationManagerConfig.class);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tReactiveAuthenticationManager manager = config.manager;\n\t\tServerWebExchangeMatcher matcher = config.matcher;\n\t\tServerOAuth2AuthorizationRequestResolver resolver = config.resolver;\n\t\tServerAuthenticationSuccessHandler successHandler = config.successHandler;\n\t\tServerAuthenticationFailureHandler failureHandler = config.failureHandler;\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(new TestingAuthenticationToken(\"a\", \"b\", \"c\")));\n\t\tgiven(manager.authenticate(any()))\n\t\t\t.willReturn(Mono.error(new OAuth2AuthenticationException(new OAuth2Error(\"error\"), \"message\")));\n\t\tgiven(matcher.matches(any())).willReturn(ServerWebExchangeMatcher.MatchResult.match());\n\t\tgiven(resolver.resolve(any())).willReturn(Mono.empty());\n\t\tgiven(successHandler.onAuthenticationSuccess(any(), any())).willAnswer((Answer<Mono<Void>>) (invocation) -> {\n\t\t\tWebFilterExchange webFilterExchange = invocation.getArgument(0);\n\t\t\tAuthentication authentication = invocation.getArgument(1);\n\t\t\treturn new RedirectServerAuthenticationSuccessHandler(redirectLocation)\n\t\t\t\t.onAuthenticationSuccess(webFilterExchange, authentication);\n\t\t});\n\t\tgiven(failureHandler.onAuthenticationFailure(any(), any())).willAnswer((Answer<Mono<Void>>) (invocation) -> {\n\t\t\tWebFilterExchange webFilterExchange = invocation.getArgument(0);\n\t\t\tAuthenticationException authenticationException = invocation.getArgument(1);\n\t\t\treturn new RedirectServerAuthenticationFailureHandler(failureRedirectLocation)\n\t\t\t\t.onAuthenticationFailure(webFilterExchange, authenticationException);\n\t\t});\n\t\t// @formatter:off\n\t\twebTestClient.get()\n\t\t\t\t.uri(\"/login/oauth2/code/github\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", failureRedirectLocation);\n\t\t// @formatter:on\n\t\tverify(converter).convert(any());\n\t\tverify(manager).authenticate(any());\n\t\tverify(matcher).matches(any());\n\t\tverify(resolver).resolve(any());\n\t\tverify(failureHandler).onAuthenticationFailure(any(), any());\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenCustomObjectsInLambdaThenUsed() {\n\t\tthis.spring\n\t\t\t.register(OAuth2LoginWithSingleClientRegistrations.class,\n\t\t\t\t\tOAuth2LoginMockAuthenticationManagerInLambdaConfig.class)\n\t\t\t.autowire();\n\t\tString redirectLocation = \"/custom-redirect-location\";\n\t\tWebTestClient webTestClient = WebTestClientBuilder.bindToWebFilters(this.springSecurity).build();\n\t\tOAuth2LoginMockAuthenticationManagerInLambdaConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2LoginMockAuthenticationManagerInLambdaConfig.class);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tReactiveAuthenticationManager manager = config.manager;\n\t\tServerWebExchangeMatcher matcher = config.matcher;\n\t\tServerOAuth2AuthorizationRequestResolver resolver = config.resolver;\n\t\tServerAuthenticationSuccessHandler successHandler = config.successHandler;\n\t\tOAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success();\n\t\tOAuth2User user = TestOAuth2Users.create();\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tOAuth2LoginAuthenticationToken result = new OAuth2LoginAuthenticationToken(github, exchange, user,\n\t\t\t\tuser.getAuthorities(), accessToken);\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(new TestingAuthenticationToken(\"a\", \"b\", \"c\")));\n\t\tgiven(manager.authenticate(any())).willReturn(Mono.just(result));\n\t\tgiven(matcher.matches(any())).willReturn(ServerWebExchangeMatcher.MatchResult.match());\n\t\tgiven(resolver.resolve(any())).willReturn(Mono.empty());\n\t\tgiven(successHandler.onAuthenticationSuccess(any(), any())).willAnswer((Answer<Mono<Void>>) (invocation) -> {\n\t\t\tWebFilterExchange webFilterExchange = invocation.getArgument(0);\n\t\t\tAuthentication authentication = invocation.getArgument(1);\n\t\t\treturn new RedirectServerAuthenticationSuccessHandler(redirectLocation)\n\t\t\t\t.onAuthenticationSuccess(webFilterExchange, authentication);\n\t\t});\n\t\t// @formatter:off\n\t\twebTestClient.get()\n\t\t\t\t.uri(\"/login/oauth2/code/github\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", redirectLocation);\n\t\t// @formatter:on\n\t\tverify(converter).convert(any());\n\t\tverify(manager).authenticate(any());\n\t\tverify(matcher).matches(any());\n\t\tverify(resolver).resolve(any());\n\t\tverify(successHandler).onAuthenticationSuccess(any(), any());\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenCustomBeansThenUsed() {\n\t\tthis.spring.register(OAuth2LoginWithMultipleClientRegistrations.class, OAuth2LoginWithCustomBeansConfig.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurity)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2LoginWithCustomBeansConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2LoginWithCustomBeansConfig.class);\n\t\tOAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().scope(\"openid\").build();\n\t\tOAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build();\n\t\tOAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response);\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes(\"openid\");\n\t\tOAuth2AuthorizationCodeAuthenticationToken token = new OAuth2AuthorizationCodeAuthenticationToken(google,\n\t\t\t\texchange, accessToken);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(token));\n\t\tServerSecurityContextRepository securityContextRepository = config.securityContextRepository;\n\t\tgiven(securityContextRepository.save(any(), any())).willReturn(Mono.empty());\n\t\tgiven(securityContextRepository.load(any())).willReturn(authentication(token));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, \"id-token\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withToken(accessToken.getTokenValue())\n\t\t\t\t.tokenType(accessToken.getTokenType())\n\t\t\t\t.scopes(accessToken.getScopes())\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = config.tokenResponseClient;\n\t\tgiven(tokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tOidcUser user = TestOidcUsers.create();\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = config.userService;\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(user));\n\t\tServerOAuth2AuthorizationRequestResolver authorizationRequestResolver = config.authorizationRequestResolver;\n\t\t// @formatter:off\n\t\twebTestClient.get()\n\t\t\t\t.uri(\"/login/oauth2/code/google\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection();\n\t\t// @formatter:on\n\t\tverify(config.jwtDecoderFactory).createDecoder(any());\n\t\tverify(tokenResponseClient).getTokenResponse(any());\n\t\tverify(securityContextRepository).save(any(), any());\n\t\tverify(authorizationRequestResolver).resolve(any());\n\t}\n\n\t// gh-5562\n\t@Test\n\tpublic void oauth2LoginWhenAccessTokenRequestFailsThenDefaultRedirectToLogin() {\n\t\tthis.spring.register(OAuth2LoginWithMultipleClientRegistrations.class, OAuth2LoginWithCustomBeansConfig.class)\n\t\t\t.autowire();\n\t\t// @formatter:off\n\t\tWebTestClient webTestClient = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(this.springSecurity)\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests\n\t\t\t\t.request()\n\t\t\t\t.scope(\"openid\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build();\n\t\tOAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response);\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes(\"openid\");\n\t\tOAuth2AuthorizationCodeAuthenticationToken authenticationToken = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tgoogle, exchange, accessToken);\n\t\tOAuth2LoginWithCustomBeansConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2LoginWithCustomBeansConfig.class);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(authenticationToken));\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = config.tokenResponseClient;\n\t\tOAuth2Error oauth2Error = new OAuth2Error(\"invalid_request\", \"Invalid request\", null);\n\t\tgiven(tokenResponseClient.getTokenResponse(any())).willThrow(new OAuth2AuthenticationException(oauth2Error));\n\t\t// @formatter:off\n\t\twebTestClient.get()\n\t\t\t\t.uri(\"/login/oauth2/code/google\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login?error\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-6484\n\t@Test\n\tpublic void oauth2LoginWhenIdTokenValidationFailsThenDefaultRedirectToLogin() {\n\t\tthis.spring.register(OAuth2LoginWithMultipleClientRegistrations.class, OAuth2LoginWithCustomBeansConfig.class)\n\t\t\t.autowire();\n\t\tWebTestClient webTestClient = WebTestClientBuilder.bindToWebFilters(this.springSecurity).build();\n\t\tOAuth2LoginWithCustomBeansConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2LoginWithCustomBeansConfig.class);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests\n\t\t\t\t.request()\n\t\t\t\t.scope(\"openid\")\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses\n\t\t\t\t.success()\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response);\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes(\"openid\");\n\t\tOAuth2AuthorizationCodeAuthenticationToken authenticationToken = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tgoogle, exchange, accessToken);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(authenticationToken));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, \"id-token\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withToken(accessToken.getTokenValue())\n\t\t\t\t.tokenType(accessToken.getTokenType())\n\t\t\t\t.scopes(accessToken.getScopes())\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = config.tokenResponseClient;\n\t\tgiven(tokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = config.jwtDecoderFactory;\n\t\tOAuth2Error oauth2Error = new OAuth2Error(\"invalid_id_token\", \"Invalid ID Token\", null);\n\t\tgiven(jwtDecoderFactory.createDecoder(any())).willReturn((token) -> Mono\n\t\t\t.error(new JwtValidationException(\"ID Token validation failed\", Collections.singleton(oauth2Error))));\n\t\t// @formatter:off\n\t\twebTestClient.get()\n\t\t\t\t.uri(\"/login/oauth2/code/google\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login?error\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingOidcLogoutHandlerThenRedirects() {\n\t\tthis.spring.register(OAuth2LoginConfigWithOidcLogoutSuccessHandler.class).autowire();\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, getBean(ClientRegistration.class).getRegistrationId());\n\t\tServerSecurityContextRepository repository = getBean(ServerSecurityContextRepository.class);\n\t\tgiven(repository.load(any())).willReturn(authentication(token));\n\t\t// @formatter:off\n\t\tthis.client.post()\n\t\t\t\t.uri(\"/logout\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"https://logout?id_token_hint=id-token\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-8609\n\t@Test\n\tpublic void oauth2LoginWhenAuthenticationConverterFailsThenDefaultRedirectToLogin() {\n\t\tthis.spring.register(OAuth2LoginWithMultipleClientRegistrations.class).autowire();\n\t\tWebTestClient webTestClient = WebTestClientBuilder.bindToWebFilters(this.springSecurity).build();\n\t\t// @formatter:off\n\t\twebTestClient.get()\n\t\t\t\t.uri(\"/login/oauth2/code/google\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login?error\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenOidcSessionRegistryThenUses() {\n\t\tthis.spring.register(OAuth2LoginWithOidcSessionRegistry.class).autowire();\n\t\tSecurityWebFilterChain chain = this.spring.getContext().getBean(SecurityWebFilterChain.class);\n\t\tassertThat(chain.getWebFilters()\n\t\t\t.filter((filter) -> filter instanceof OidcSessionRegistryAuthenticationWebFilter)\n\t\t\t.collectList()\n\t\t\t.block()).isNotEmpty();\n\t}\n\n\t// gh-14558\n\t@Test\n\tpublic void oauth2LoginWhenDefaultsThenNoOidcSessionRegistry() {\n\t\tthis.spring.register(OAuth2LoginWithSingleClientRegistrations.class, OAuth2LoginConfig.class).autowire();\n\t\tSecurityWebFilterChain chain = this.spring.getContext().getBean(SecurityWebFilterChain.class);\n\t\tassertThat(chain.getWebFilters()\n\t\t\t.filter((filter) -> filter instanceof OidcSessionRegistryAuthenticationWebFilter)\n\t\t\t.collectList()\n\t\t\t.block()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenOauth2UserServiceBeanPresent() {\n\t\tthis.spring.register(OAuth2LoginWithMultipleClientRegistrations.class, OAuth2LoginWithOauth2UserService.class)\n\t\t\t.autowire();\n\t\tWebTestClient webTestClient = WebTestClientBuilder.bindToWebFilters(this.springSecurity).build();\n\t\tOAuth2LoginWithOauth2UserService config = this.spring.getContext()\n\t\t\t.getBean(OAuth2LoginWithOauth2UserService.class);\n\t\tOAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().scope(\"openid\").build();\n\t\tOAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build();\n\t\tOAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response);\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes(\"openid\");\n\t\tOAuth2AuthorizationCodeAuthenticationToken token = new OAuth2AuthorizationCodeAuthenticationToken(google,\n\t\t\t\texchange, accessToken);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(token));\n\t\tServerSecurityContextRepository securityContextRepository = config.securityContextRepository;\n\t\tgiven(securityContextRepository.save(any(), any())).willReturn(Mono.empty());\n\t\tgiven(securityContextRepository.load(any())).willReturn(authentication(token));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, \"id-token\");\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())\n\t\t\t.tokenType(accessToken.getTokenType())\n\t\t\t.scopes(accessToken.getScopes())\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = config.tokenResponseClient;\n\t\tgiven(tokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService = config.reactiveOAuth2UserService;\n\t\tgiven(userService.loadUser(any())).willReturn(Mono\n\t\t\t.just(new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"USER\"), Map.of(\"sub\", \"subject\"), \"sub\")));\n\t\twebTestClient.get().uri(\"/login/oauth2/code/google\").exchange().expectStatus().is3xxRedirection();\n\t\tverify(userService).loadUser(any());\n\n\t}\n\n\tMono<SecurityContext> authentication(Authentication authentication) {\n\t\tSecurityContext context = new SecurityContextImpl();\n\t\tcontext.setAuthentication(authentication);\n\t\treturn Mono.just(context);\n\t}\n\n\t<T> T getBean(Class<T> beanClass) {\n\t\treturn this.spring.getContext().getBean(beanClass);\n\t}\n\n\t@Configuration\n\tstatic class OAuth2LoginWithOauth2UserService {\n\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = mock(\n\t\t\t\tReactiveOAuth2AccessTokenResponseClient.class);\n\n\t\tReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> reactiveOAuth2UserService = mock(\n\t\t\t\tDefaultReactiveOAuth2UserService.class);\n\n\t\tServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);\n\n\t\tServerSecurityContextRepository securityContextRepository = mock(ServerSecurityContextRepository.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\thttp.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.oauth2Login((c) -> c.authenticationConverter(this.authenticationConverter)\n\t\t\t\t\t.securityContextRepository(this.securityContextRepository));\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> customOAuth2UserService() {\n\t\t\treturn this.reactiveOAuth2UserService;\n\t\t}\n\n\t\t@Bean\n\t\tReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {\n\t\t\treturn (clientRegistration) -> (token) -> {\n\t\t\t\tMap<String, Object> claims = new HashMap<>();\n\t\t\t\tclaims.put(IdTokenClaimNames.SUB, \"subject\");\n\t\t\t\tclaims.put(IdTokenClaimNames.ISS, \"http://localhost/issuer\");\n\t\t\t\tclaims.put(IdTokenClaimNames.AUD, Collections.singletonList(\"client\"));\n\t\t\t\tclaims.put(IdTokenClaimNames.AZP, \"client\");\n\t\t\t\treturn Mono.just(TestJwts.jwt().claims((c) -> c.putAll(claims)).build());\n\t\t\t};\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> requestReactiveOAuth2AccessTokenResponseClient() {\n\t\t\treturn this.tokenResponseClient;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2LoginWithMultipleClientRegistrations {\n\n\t\t@Bean\n\t\tInMemoryReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn new InMemoryReactiveClientRegistrationRepository(github, google);\n\t\t}\n\n\t}\n\n\t@EnableWebFlux\n\tstatic class WebFluxConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2LoginWithSingleClientRegistrations {\n\n\t\t@Bean\n\t\tInMemoryReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn new InMemoryReactiveClientRegistrationRepository(github);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2LoginWithAuthorizationCodeAndClientCredentialsClientRegistration {\n\n\t\t@Bean\n\t\tInMemoryReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn new InMemoryReactiveClientRegistrationRepository(github, clientCredentials);\n\t\t}\n\n\t}\n\n\t@EnableWebFlux\n\tstatic class OAuth2LoginConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.oauth2Login(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@EnableWebFlux\n\tstatic class OAuth2AuthorizeWithMockObjectsConfig {\n\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = mock(\n\t\t\t\tServerOAuth2AuthorizedClientRepository.class);\n\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = mock(\n\t\t\t\tServerAuthorizationRequestRepository.class);\n\n\t\tServerRequestCache requestCache = mock(ServerRequestCache.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.requestCache((cache) -> cache\n\t\t\t\t\t.requestCache(this.requestCache))\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.authorizationRequestRepository(this.authorizationRequestRepository));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn this.authorizedClientRepository;\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class AuthorizedClientController {\n\n\t\t@GetMapping(\"/\")\n\t\tString home(@RegisteredOAuth2AuthorizedClient(\"github\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\treturn \"home\";\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class OAuth2LoginWithFormLogin {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {\n\t\t\tReactiveUserDetailsService reactiveUserDetailsService = ReactiveAuthenticationTestConfiguration\n\t\t\t\t.userDetailsService();\n\t\t\tReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(\n\t\t\t\t\treactiveUserDetailsService);\n\t\t\thttp.authenticationManager(authenticationManager);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().authenticated())\n\t\t\t\t.oauth2Login(withDefaults())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class OAuth2LoginWithHttpBasic {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {\n\t\t\tReactiveUserDetailsService reactiveUserDetailsService = ReactiveAuthenticationTestConfiguration\n\t\t\t\t.userDetailsService();\n\t\t\tReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(\n\t\t\t\t\treactiveUserDetailsService);\n\t\t\thttp.authenticationManager(authenticationManager);\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().authenticated())\n\t\t\t\t.oauth2Login(withDefaults())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2LoginWithCustomLoginPage {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.pathMatchers(HttpMethod.GET, \"/login\").permitAll()\n\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t.loginPage(\"/login\")\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2LoginWithProviderLoginPage {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t.loginPage(\"/oauth2/authorization/github\")\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class OAuth2LoginMockAuthenticationManagerConfig {\n\n\t\tReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);\n\n\t\tServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);\n\n\t\tServerWebExchangeMatcher matcher = mock(ServerWebExchangeMatcher.class);\n\n\t\tServerOAuth2AuthorizationRequestResolver resolver = mock(ServerOAuth2AuthorizationRequestResolver.class);\n\n\t\tServerAuthenticationSuccessHandler successHandler = mock(ServerAuthenticationSuccessHandler.class);\n\n\t\tServerAuthenticationFailureHandler failureHandler = mock(ServerAuthenticationFailureHandler.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().authenticated())\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.authenticationConverter(this.authenticationConverter)\n\t\t\t\t\t.authenticationManager(this.manager)\n\t\t\t\t\t.authenticationMatcher(this.matcher)\n\t\t\t\t\t.authorizationRequestResolver(this.resolver)\n\t\t\t\t\t.authenticationSuccessHandler(this.successHandler)\n\t\t\t\t\t.authenticationFailureHandler(this.failureHandler));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class OAuth2LoginMockAuthenticationManagerInLambdaConfig {\n\n\t\tReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);\n\n\t\tServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);\n\n\t\tServerWebExchangeMatcher matcher = mock(ServerWebExchangeMatcher.class);\n\n\t\tServerOAuth2AuthorizationRequestResolver resolver = mock(ServerOAuth2AuthorizationRequestResolver.class);\n\n\t\tServerAuthenticationSuccessHandler successHandler = mock(ServerAuthenticationSuccessHandler.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t)\n\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t\t.authenticationConverter(this.authenticationConverter)\n\t\t\t\t\t\t.authenticationManager(this.manager)\n\t\t\t\t\t\t.authenticationMatcher(this.matcher)\n\t\t\t\t\t\t.authorizationRequestResolver(this.resolver)\n\t\t\t\t\t\t.authenticationSuccessHandler(this.successHandler)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class OAuth2LoginWithCustomBeansConfig {\n\n\t\tServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);\n\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = mock(\n\t\t\t\tReactiveOAuth2AccessTokenResponseClient.class);\n\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\n\t\tReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = spy(new JwtDecoderFactory());\n\n\t\tServerSecurityContextRepository securityContextRepository = mock(ServerSecurityContextRepository.class);\n\n\t\tServerOAuth2AuthorizationRequestResolver authorizationRequestResolver = spy(\n\t\t\t\tnew DefaultServerOAuth2AuthorizationRequestResolver(new InMemoryReactiveClientRegistrationRepository(\n\t\t\t\t\t\tTestClientRegistrations.clientRegistration().build())));\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().authenticated())\n\t\t\t\t.oauth2Login((login) -> login\n\t\t\t\t\t.authenticationConverter(this.authenticationConverter)\n\t\t\t\t\t.authenticationManager(authenticationManager())\n\t\t\t\t\t.securityContextRepository(this.securityContextRepository));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate ReactiveAuthenticationManager authenticationManager() {\n\t\t\tOidcAuthorizationCodeReactiveAuthenticationManager oidc = new OidcAuthorizationCodeReactiveAuthenticationManager(\n\t\t\t\t\tthis.tokenResponseClient, this.userService);\n\t\t\toidc.setJwtDecoderFactory(jwtDecoderFactory());\n\t\t\treturn oidc;\n\t\t}\n\n\t\t@Bean\n\t\tReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {\n\t\t\treturn this.jwtDecoderFactory;\n\t\t}\n\n\t\t@Bean\n\t\tServerOAuth2AuthorizationRequestResolver authorizationRequestResolver() {\n\t\t\treturn this.authorizationRequestResolver;\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {\n\t\t\treturn this.tokenResponseClient;\n\t\t}\n\n\t\tprivate static class JwtDecoderFactory implements ReactiveJwtDecoderFactory<ClientRegistration> {\n\n\t\t\t@Override\n\t\t\tpublic ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) {\n\t\t\t\treturn getJwtDecoder();\n\t\t\t}\n\n\t\t\tprivate ReactiveJwtDecoder getJwtDecoder() {\n\t\t\t\treturn (token) -> {\n\t\t\t\t\tMap<String, Object> claims = new HashMap<>();\n\t\t\t\t\tclaims.put(IdTokenClaimNames.SUB, \"subject\");\n\t\t\t\t\tclaims.put(IdTokenClaimNames.ISS, \"http://localhost/issuer\");\n\t\t\t\t\tclaims.put(IdTokenClaimNames.AUD, Collections.singletonList(\"client\"));\n\t\t\t\t\tclaims.put(IdTokenClaimNames.AZP, \"client\");\n\t\t\t\t\tJwt jwt = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\t\t\t\treturn Mono.just(jwt);\n\t\t\t\t};\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@EnableWebFlux\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2LoginConfigWithOidcLogoutSuccessHandler {\n\n\t\tprivate final ServerSecurityContextRepository repository = mock(ServerSecurityContextRepository.class);\n\n\t\tprivate final ClientRegistration withLogout = TestClientRegistrations.clientRegistration()\n\t\t\t.providerConfigurationMetadata(Collections.singletonMap(\"end_session_endpoint\", \"https://logout\"))\n\t\t\t.build();\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf.disable())\n\t\t\t\t.logout((logout) -> logout\n\t\t\t\t\t// avoid using mock ServerSecurityContextRepository for logout\n\t\t\t\t\t.logoutHandler(new SecurityContextServerLogoutHandler())\n\t\t\t\t\t.logoutSuccessHandler(\n\t\t\t\t\t\tnew OidcClientInitiatedServerLogoutSuccessHandler(\n\t\t\t\t\t\t\tnew InMemoryReactiveClientRegistrationRepository(this.withLogout))))\n\t\t\t\t.securityContextRepository(this.repository);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tServerSecurityContextRepository securityContextRepository() {\n\t\t\treturn this.repository;\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistration clientRegistration() {\n\t\t\treturn this.withLogout;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\tstatic class OAuth2LoginWithOidcSessionRegistry {\n\n\t\tprivate final ReactiveOidcSessionRegistry registry = mock(ReactiveOidcSessionRegistry.class);\n\n\t\tprivate final ReactiveClientRegistrationRepository clients = new InMemoryReactiveClientRegistrationRepository(\n\t\t\t\tTestClientRegistrations.clientRegistration().build());\n\n\t\t@Bean\n\t\tSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t\t\t\t.clientRegistrationRepository(this.clients)\n\t\t\t\t\t\t\t.oidcSessionRegistry(this.registry)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOidcSessionRegistry oidcSessionRegistry() {\n\t\t\treturn this.registry;\n\t\t}\n\n\t}\n\n\tstatic class GitHubWebFilter implements WebFilter {\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\tif (exchange.getRequest().getURI().getHost().equals(\"github.com\")) {\n\t\t\t\treturn exchange.getResponse().setComplete();\n\t\t\t}\n\t\t\treturn chain.filter(exchange);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.security.KeyFactory;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.RSAPublicKeySpec;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport jakarta.annotation.PreDestroy;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.apache.http.HttpHeaders;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.BeanCreationException;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.NoUniqueBeanDefinitionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;\nimport org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.support.GenericWebApplicationContext;\nimport org.springframework.web.reactive.DispatcherHandler;\nimport org.springframework.web.reactive.config.EnableWebFlux;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.hamcrest.CoreMatchers.startsWith;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for\n * {@link org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2ResourceServerSpec}\n */\n@ExtendWith({ SpringTestContextExtension.class })\n@SuppressWarnings(\"removal\")\npublic class OAuth2ResourceServerSpecTests {\n\n\tprivate String expired = \"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE1MzUwMzc4OTd9.jqZDDjfc2eysX44lHXEIr9XFd2S8vjIZHCccZU-dRWMRJNsQ1QN5VNnJGklqJBXJR4qgla6cmVqPOLkUHDb0sL0nxM5XuzQaG5ZzKP81RV88shFyAiT0fD-6nl1k-Fai-Fu-VkzSpNXgeONoTxDaYhdB-yxmgrgsApgmbOTE_9AcMk-FQDXQ-pL9kynccFGV0lZx4CA7cyknKN7KBxUilfIycvXODwgKCjj_1WddLTCNGYogJJSg__7NoxzqbyWd3udbHVjqYq7GsMMrGB4_2kBD4CkghOSNcRHbT_DIXowxfAVT7PAg7Q0E5ruZsr2zPZacEUDhJ6-wbvlA0FAOUg\";\n\n\tprivate String messageReadToken = \"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJtb2NrLXN1YmplY3QiLCJzY29wZSI6Im1lc3NhZ2U6cmVhZCIsImV4cCI6NDY4ODY0MTQxM30.cRl1bv_dDYcAN5U4NlIVKj8uu4mLMwjABF93P4dShiq-GQ-owzaqTSlB4YarNFgV3PKQvT9wxN1jBpGribvISljakoC0E8wDV-saDi8WxN-qvImYsn1zLzYFiZXCfRIxCmonJpydeiAPRxMTPtwnYDS9Ib0T_iA80TBGd-INhyxUUfrwRW5sqKRbjUciRJhpp7fW2ZYXmi9iPt3HDjRQA4IloJZ7f4-spt5Q9wl5HcQTv1t4XrX4eqhVbE5cCoIkFQnKPOc-jhVM44_eazLU6Xk-CCXP8C_UT5pX0luRS2cJrVFfHp2IR_AWxC-shItg6LNEmNFD4Zc-JLZcr0Q86Q\";\n\n\tprivate String messageReadTokenWithKid = \"eyJraWQiOiJvbmUiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJtb2NrLXN1YmplY3QiLCJzY29wZSI6Im1lc3NhZ2U6cmVhZCIsImV4cCI6NDY4ODY0MTQ2MX0.Arg3IjlNb_nkEIZpcWAQquvoiaeF_apJzO5ZxSzUQEWixH1Y7yrsW2uco452a7OtAKDNT09IplK8126z_hdI_RRk0CXVsGZYe1qppNIVLEPGv4rHxND4bPv1YA91Q8vG-vDk9rod7EvAuZU1tEP_pWkSkZVAmfuP43bP5FQcO6Q31Aba7Yb7O5qWn9U2MjruPSFvTsIx3hSXgTuJxhNCKeHnTCmv2WdjYWatR7-VujBlHd-ZolysXm7-JPz3kI75omnomG2UqnKkI76sczIpm4ieOp3fSyv-QR-i-3Z_eJ9hS3Ox46Y9NJS6Z-y1g3X0fjVyhLiIJkFV3VA5HrSf_A\";\n\n\tprivate String unsignedToken = \"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOi0yMDMzMjI0OTcsImp0aSI6IjEyMyIsInR5cCI6IkpXVCJ9.\";\n\n\t// @formatter:off\n\tprivate String jwkSet = \"{\\n\"\n\t\t\t+ \"  \\\"keys\\\":[\\n\"\n\t\t\t+ \"    {\\n\"\n\t\t\t+ \"      \\\"kty\\\":\\\"RSA\\\",\\n\"\n\t\t\t+ \"      \\\"e\\\":\\\"AQAB\\\",\\n\"\n\t\t\t+ \"      \\\"use\\\":\\\"sig\\\",\\n\"\n\t\t\t+ \"      \\\"kid\\\":\\\"one\\\",\\n\"\n\t\t\t+ \"      \\\"n\\\":\\\"0IUjrPZDz-3z0UE4ppcKU36v7hnh8FJjhu3lbJYj0qj9eZiwEJxi9HHUfSK1DhUQG7mJBbYTK1tPYCgre5EkfKh-64VhYUa-vz17zYCmuB8fFj4XHE3MLkWIG-AUn8hNbPzYYmiBTjfGnMKxLHjsbdTiF4mtn-85w366916R6midnAuiPD4HjZaZ1PAsuY60gr8bhMEDtJ8unz81hoQrozpBZJ6r8aR1PrsWb1OqPMloK9kAIutJNvWYKacp8WYAp2WWy72PxQ7Fb0eIA1br3A5dnp-Cln6JROJcZUIRJ-QvS6QONWeS2407uQmS-i-lybsqaH0ldYC7NBEBA5inPQ\\\"\\n\"\n\t\t\t+ \"    }\\n\"\n\t\t\t+ \"  ]\\n\"\n\t\t\t+ \"}\\n\";\n\t// @formatter:on\n\n\tprivate Jwt jwt = TestJwts.jwt().build();\n\n\tprivate String clientId = \"client\";\n\n\tprivate String clientSecret = \"secret\";\n\n\t// @formatter:off\n\tprivate String active = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": true,\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": \\\"read write dolphin\\\",\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tWebTestClient client;\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tthis.client = WebTestClient.bindToApplicationContext(context).build();\n\t}\n\n\t@Test\n\tpublic void getWhenValidThenReturnsOk() {\n\t\tthis.spring.register(PublicKeyConfig.class, RootController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenExpiredThenReturnsInvalidToken() {\n\t\tthis.spring.register(PublicKeyConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.expired))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer error=\\\"invalid_token\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUnsignedThenReturnsInvalidToken() {\n\t\tthis.spring.register(PublicKeyConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.unsignedToken))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer error=\\\"invalid_token\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenEmptyBearerTokenThenReturnsInvalidToken() {\n\t\tthis.spring.register(PublicKeyConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.add(\"Authorization\", \"Bearer \")\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer error=\\\"invalid_token\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenValidTokenAndPublicKeyInLambdaThenReturnsOk() {\n\t\tthis.spring.register(PublicKeyInLambdaConfig.class, RootController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenExpiredTokenAndPublicKeyInLambdaThenReturnsInvalidToken() {\n\t\tthis.spring.register(PublicKeyInLambdaConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.expired)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer error=\\\"invalid_token\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenValidUsingPlaceholderThenReturnsOk() {\n\t\tthis.spring.register(PlaceholderConfig.class, RootController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenCustomDecoderThenAuthenticatesAccordingly() {\n\t\tthis.spring.register(CustomDecoderConfig.class, RootController.class).autowire();\n\t\tReactiveJwtDecoder jwtDecoder = this.spring.getContext().getBean(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(anyString())).willReturn(Mono.just(this.jwt));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(\"token\")\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t\tverify(jwtDecoder).decode(anyString());\n\t}\n\n\t@Test\n\tpublic void getWhenUsingJwkSetUriThenConsultsAccordingly() {\n\t\tthis.spring.register(JwkSetUriConfig.class, RootController.class).autowire();\n\t\tMockWebServer mockWebServer = this.spring.getContext().getBean(MockWebServer.class);\n\t\tmockWebServer.enqueue(new MockResponse().setBody(this.jwkSet));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadTokenWithKid)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingJwkSetUriInLambdaThenConsultsAccordingly() {\n\t\tthis.spring.register(JwkSetUriInLambdaConfig.class, RootController.class).autowire();\n\t\tMockWebServer mockWebServer = this.spring.getContext().getBean(MockWebServer.class);\n\t\tmockWebServer.enqueue(new MockResponse().setBody(this.jwkSet));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadTokenWithKid)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomAuthenticationManagerThenUsesItAccordingly() {\n\t\tthis.spring.register(CustomAuthenticationManagerConfig.class).autowire();\n\t\tReactiveAuthenticationManager authenticationManager = this.spring.getContext()\n\t\t\t.getBean(ReactiveAuthenticationManager.class);\n\t\tgiven(authenticationManager.authenticate(any(Authentication.class)))\n\t\t\t.willReturn(Mono.error(new OAuth2AuthenticationException(new OAuth2Error(\"mock-failure\"))));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer error=\\\"mock-failure\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomAuthenticationManagerInLambdaThenUsesItAccordingly() {\n\t\tthis.spring.register(CustomAuthenticationManagerInLambdaConfig.class).autowire();\n\t\tReactiveAuthenticationManager authenticationManager = this.spring.getContext()\n\t\t\t.getBean(ReactiveAuthenticationManager.class);\n\t\tgiven(authenticationManager.authenticate(any(Authentication.class)))\n\t\t\t.willReturn(Mono.error(new OAuth2AuthenticationException(new OAuth2Error(\"mock-failure\"))));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer error=\\\"mock-failure\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomAuthenticationManagerResolverThenUsesItAccordingly() {\n\t\tthis.spring.register(CustomAuthenticationManagerResolverConfig.class).autowire();\n\t\tReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver = this.spring\n\t\t\t.getContext()\n\t\t\t.getBean(ReactiveAuthenticationManagerResolver.class);\n\t\tReactiveAuthenticationManager authenticationManager = this.spring.getContext()\n\t\t\t.getBean(ReactiveAuthenticationManager.class);\n\t\tgiven(authenticationManagerResolver.resolve(any(ServerWebExchange.class)))\n\t\t\t.willReturn(Mono.just(authenticationManager));\n\t\tgiven(authenticationManager.authenticate(any(Authentication.class)))\n\t\t\t.willReturn(Mono.error(new OAuth2AuthenticationException(new OAuth2Error(\"mock-failure\"))));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer error=\\\"mock-failure\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenUsingCustomAuthenticationFailureHandlerThenUsesIsAccordingly() {\n\t\tthis.spring.register(CustomAuthenticationFailureHandlerConfig.class).autowire();\n\t\tServerAuthenticationFailureHandler handler = this.spring.getContext()\n\t\t\t.getBean(ServerAuthenticationFailureHandler.class);\n\t\tReactiveAuthenticationManager authenticationManager = this.spring.getContext()\n\t\t\t.getBean(ReactiveAuthenticationManager.class);\n\t\tgiven(authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.error(() -> new BadCredentialsException(\"bad\")));\n\t\tgiven(handler.onAuthenticationFailure(any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers.setBearerAuth(this.messageReadToken))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t\tverify(handler).onAuthenticationFailure(any(), any());\n\t}\n\n\t@Test\n\tpublic void postWhenSignedThenReturnsOk() {\n\t\tthis.spring.register(PublicKeyConfig.class, RootController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.post()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenTokenHasInsufficientScopeThenReturnsInsufficientScope() {\n\t\tthis.spring.register(DenyAllConfig.class, RootController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isForbidden()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith(\"Bearer error=\\\"insufficient_scope\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void postWhenMissingTokenThenReturnsForbidden() {\n\t\tthis.spring.register(PublicKeyConfig.class, RootController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.post()\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isForbidden();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenCustomBearerTokenServerAuthenticationConverterThenResponds() {\n\t\tthis.spring.register(CustomBearerTokenServerAuthenticationConverter.class, RootController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.cookie(\"TOKEN\", this.messageReadToken)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenSignedAndCustomConverterThenConverts() {\n\t\tthis.spring.register(CustomJwtAuthenticationConverterConfig.class, RootController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenCustomBearerTokenEntryPointThenResponds() {\n\t\tthis.spring.register(CustomErrorHandlingConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"/authenticated\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isEqualTo(HttpStatus.I_AM_A_TEAPOT);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenCustomBearerTokenDeniedHandlerThenResponds() {\n\t\tthis.spring.register(CustomErrorHandlingConfig.class).autowire();\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"/unobtainable\")\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isEqualTo(HttpStatus.BANDWIDTH_LIMIT_EXCEEDED);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenBeanWiredAndDslWiredThenDslTakesPrecedence() {\n\t\tGenericWebApplicationContext context = autowireWebServerGenericWebApplicationContext();\n\t\tServerHttpSecurity http = new ServerHttpSecurity();\n\t\thttp.setApplicationContext(context);\n\t\tReactiveJwtDecoder beanWiredJwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tReactiveJwtDecoder dslWiredJwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tcontext.registerBean(ReactiveJwtDecoder.class, () -> beanWiredJwtDecoder);\n\t\thttp.oauth2ResourceServer((server) -> server.jwt((jwt) -> {\n\t\t\tjwt.jwtDecoder(dslWiredJwtDecoder);\n\t\t\tassertThat(jwt.getJwtDecoder()).isEqualTo(dslWiredJwtDecoder);\n\t\t}));\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenTwoBeansWiredAndDslWiredThenDslTakesPrecedence() {\n\t\tGenericWebApplicationContext context = autowireWebServerGenericWebApplicationContext();\n\t\tServerHttpSecurity http = new ServerHttpSecurity();\n\t\thttp.setApplicationContext(context);\n\t\tReactiveJwtDecoder beanWiredJwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tReactiveJwtDecoder dslWiredJwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tcontext.registerBean(\"firstJwtDecoder\", ReactiveJwtDecoder.class, () -> beanWiredJwtDecoder);\n\t\tcontext.registerBean(\"secondJwtDecoder\", ReactiveJwtDecoder.class, () -> beanWiredJwtDecoder);\n\t\thttp.oauth2ResourceServer((server) -> server.jwt((jwt) -> {\n\t\t\tjwt.jwtDecoder(dslWiredJwtDecoder);\n\t\t\tassertThat(jwt.getJwtDecoder()).isEqualTo(dslWiredJwtDecoder);\n\t\t}));\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenTwoBeansWiredThenThrowsWiringException() {\n\t\tGenericWebApplicationContext context = autowireWebServerGenericWebApplicationContext();\n\t\tServerHttpSecurity http = new ServerHttpSecurity();\n\t\thttp.setApplicationContext(context);\n\t\tReactiveJwtDecoder beanWiredJwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tcontext.registerBean(\"firstJwtDecoder\", ReactiveJwtDecoder.class, () -> beanWiredJwtDecoder);\n\t\tcontext.registerBean(\"secondJwtDecoder\", ReactiveJwtDecoder.class, () -> beanWiredJwtDecoder);\n\t\thttp.oauth2ResourceServer(\n\t\t\t\t(server) -> server.jwt((jwt) -> assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)\n\t\t\t\t\t.isThrownBy(jwt::getJwtDecoder)));\n\t}\n\n\t@Test\n\tpublic void getJwtDecoderWhenNoBeansAndNoDslWiredThenWiringException() {\n\t\tGenericWebApplicationContext context = autowireWebServerGenericWebApplicationContext();\n\t\tServerHttpSecurity http = new ServerHttpSecurity();\n\t\thttp.setApplicationContext(context);\n\t\thttp.oauth2ResourceServer(\n\t\t\t\t(server) -> server.jwt((jwt) -> assertThatExceptionOfType(NoSuchBeanDefinitionException.class)\n\t\t\t\t\t.isThrownBy(jwt::getJwtDecoder)));\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenBeanWiredAndDslWiredThenDslTakesPrecedence() {\n\t\tGenericWebApplicationContext context = autowireWebServerGenericWebApplicationContext();\n\t\tServerHttpSecurity http = new ServerHttpSecurity();\n\t\thttp.setApplicationContext(context);\n\t\tReactiveJwtAuthenticationConverter beanWiredJwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();\n\t\tReactiveJwtAuthenticationConverter dslWiredJwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();\n\t\tcontext.registerBean(ReactiveJwtAuthenticationConverter.class, () -> beanWiredJwtAuthenticationConverter);\n\t\thttp.oauth2ResourceServer((server) -> server.jwt((jwt) -> {\n\t\t\tjwt.jwtAuthenticationConverter(dslWiredJwtAuthenticationConverter);\n\t\t\tassertThat(jwt.getJwtAuthenticationConverter()).isEqualTo(dslWiredJwtAuthenticationConverter);\n\t\t}));\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenTwoBeansWiredAndDslWiredThenDslTakesPrecedence() {\n\t\tGenericWebApplicationContext context = autowireWebServerGenericWebApplicationContext();\n\t\tServerHttpSecurity http = new ServerHttpSecurity();\n\t\thttp.setApplicationContext(context);\n\t\tReactiveJwtAuthenticationConverter beanWiredJwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();\n\t\tReactiveJwtAuthenticationConverter dslWiredJwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();\n\t\tcontext.registerBean(\"firstJwtAuthenticationConverter\", ReactiveJwtAuthenticationConverter.class,\n\t\t\t\t() -> beanWiredJwtAuthenticationConverter);\n\t\tcontext.registerBean(\"secondJwtAuthenticationConverter\", ReactiveJwtAuthenticationConverter.class,\n\t\t\t\t() -> beanWiredJwtAuthenticationConverter);\n\t\thttp.oauth2ResourceServer((server) -> server.jwt((jwt) -> {\n\t\t\tjwt.jwtAuthenticationConverter(dslWiredJwtAuthenticationConverter);\n\t\t\tassertThat(jwt.getJwtAuthenticationConverter()).isEqualTo(dslWiredJwtAuthenticationConverter);\n\t\t}));\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenTwoBeansWiredThenThrowsWiringException() {\n\t\tGenericWebApplicationContext context = autowireWebServerGenericWebApplicationContext();\n\t\tServerHttpSecurity http = new ServerHttpSecurity();\n\t\thttp.setApplicationContext(context);\n\t\tReactiveJwtAuthenticationConverter beanWiredJwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();\n\t\tcontext.registerBean(\"firstJwtAuthenticationConverter\", ReactiveJwtAuthenticationConverter.class,\n\t\t\t\t() -> beanWiredJwtAuthenticationConverter);\n\t\tcontext.registerBean(\"secondJwtAuthenticationConverter\", ReactiveJwtAuthenticationConverter.class,\n\t\t\t\t() -> beanWiredJwtAuthenticationConverter);\n\t\thttp.oauth2ResourceServer(\n\t\t\t\t(server) -> server.jwt((jwt) -> assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)\n\t\t\t\t\t.isThrownBy(jwt::getJwtAuthenticationConverter)));\n\t}\n\n\t@Test\n\tpublic void getJwtAuthenticationConverterWhenNoBeansAndNoDslWiredThenDefaultConverter() {\n\t\tGenericWebApplicationContext context = autowireWebServerGenericWebApplicationContext();\n\t\tServerHttpSecurity http = new ServerHttpSecurity();\n\t\thttp.setApplicationContext(context);\n\t\thttp.oauth2ResourceServer((server) -> server.jwt((jwt) -> assertThat(jwt.getJwtAuthenticationConverter())\n\t\t\t.isInstanceOf(ReactiveJwtAuthenticationConverter.class)));\n\t}\n\n\t@Test\n\tpublic void introspectWhenValidThenReturnsOk() {\n\t\tthis.spring.register(IntrospectionConfig.class, RootController.class).autowire();\n\t\tthis.spring.getContext()\n\t\t\t.getBean(MockWebServer.class)\n\t\t\t.setDispatcher(requiresAuth(this.clientId, this.clientSecret, this.active));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void introspectWhenValidAndIntrospectionInLambdaThenReturnsOk() {\n\t\tthis.spring.register(IntrospectionInLambdaConfig.class, RootController.class).autowire();\n\t\tthis.spring.getContext()\n\t\t\t.getBean(MockWebServer.class)\n\t\t\t.setDispatcher(requiresAuth(this.clientId, this.clientSecret, this.active));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingBothAuthenticationManagerResolverAndOpaqueThenWiringException() {\n\t\tassertThatExceptionOfType(BeanCreationException.class)\n\t\t\t.isThrownBy(() -> this.spring.register(AuthenticationManagerResolverPlusOtherConfig.class).autowire())\n\t\t\t.withMessageContaining(\"authenticationManagerResolver\");\n\t}\n\n\t@Test\n\tpublic void getWhenCustomAuthenticationConverterThenConverts() {\n\t\tthis.spring.register(ReactiveOpaqueTokenAuthenticationConverterConfig.class, RootController.class).autowire();\n\t\tthis.spring.getContext()\n\t\t\t.getBean(MockWebServer.class)\n\t\t\t.setDispatcher(requiresAuth(this.clientId, this.clientSecret, this.active));\n\t\tReactiveOpaqueTokenAuthenticationConverter authenticationConverter = this.spring.getContext()\n\t\t\t.getBean(ReactiveOpaqueTokenAuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(anyString(), any(OAuth2AuthenticatedPrincipal.class)))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"jdoe\", null, Collections.emptyList())));\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(this.messageReadToken)\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\tprivate static Dispatcher requiresAuth(String username, String password, String response) {\n\t\treturn new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\tString authorization = request.getHeader(org.springframework.http.HttpHeaders.AUTHORIZATION);\n\t\t\t\t// @formatter:off\n\t\t\t\treturn Optional.ofNullable(authorization)\n\t\t\t\t\t\t.filter((a) -> isAuthorized(authorization, username, password))\n\t\t\t\t\t\t.map((a) -> ok(response))\n\t\t\t\t\t\t.orElse(unauthorized());\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static boolean isAuthorized(String authorization, String username, String password) {\n\t\tString[] values = new String(Base64.getDecoder().decode(authorization.substring(6))).split(\":\");\n\t\treturn username.equals(values[0]) && password.equals(values[1]);\n\t}\n\n\tprivate static MockResponse ok(String response) {\n\t\treturn new MockResponse().setBody(response)\n\t\t\t.setHeader(org.springframework.http.HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t}\n\n\tprivate static MockResponse unauthorized() {\n\t\treturn new MockResponse().setResponseCode(401);\n\t}\n\n\tprivate static RSAPublicKey publicKey() {\n\t\tString modulus = \"26323220897278656456354815752829448539647589990395639665273015355787577386000316054335559633864476469390247312823732994485311378484154955583861993455004584140858982659817218753831620205191028763754231454775026027780771426040997832758235764611119743390612035457533732596799927628476322029280486807310749948064176545712270582940917249337311592011920620009965129181413510845780806191965771671528886508636605814099711121026468495328702234901200169245493126030184941412539949521815665744267183140084667383643755535107759061065656273783542590997725982989978433493861515415520051342321336460543070448417126615154138673620797\";\n\t\tString exponent = \"65537\";\n\t\tRSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));\n\t\tRSAPublicKey rsaPublicKey = null;\n\t\ttry {\n\t\t\tKeyFactory factory = KeyFactory.getInstance(\"RSA\");\n\t\t\trsaPublicKey = (RSAPublicKey) factory.generatePublic(spec);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException | InvalidKeySpecException ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\treturn rsaPublicKey;\n\t}\n\n\tprivate GenericWebApplicationContext autowireWebServerGenericWebApplicationContext() {\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(\"webHandler\", DispatcherHandler.class);\n\t\tthis.spring.context(context).autowire();\n\t\treturn (GenericWebApplicationContext) this.spring.getContext();\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class PublicKeyConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().hasAuthority(\"SCOPE_message:read\"))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt.publicKey(publicKey())));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class PublicKeyInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().hasAuthority(\"SCOPE_message:read\")\n\t\t\t\t)\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t\t\t.publicKey(publicKey())\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class PlaceholderConfig {\n\n\t\t@Value(\"classpath:org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests-simple.pub\")\n\t\tRSAPublicKey key;\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().hasAuthority(\"SCOPE_message:read\"))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt.publicKey(this.key)));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class JwkSetUriConfig {\n\n\t\tprivate MockWebServer mockWebServer = new MockWebServer();\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\tString jwkSetUri = mockWebServer().url(\"/.well-known/jwks.json\").toString();\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt.jwkSetUri(jwkSetUri)));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tMockWebServer mockWebServer() {\n\t\t\treturn this.mockWebServer;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.mockWebServer.shutdown();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class JwkSetUriInLambdaConfig {\n\n\t\tprivate MockWebServer mockWebServer = new MockWebServer();\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\tString jwkSetUri = mockWebServer().url(\"/.well-known/jwks.json\").toString();\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t\t\t.jwkSetUri(jwkSetUri)\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tMockWebServer mockWebServer() {\n\t\t\treturn this.mockWebServer;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.mockWebServer.shutdown();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class CustomDecoderConfig {\n\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveJwtDecoder jwtDecoder() {\n\t\t\treturn this.jwtDecoder;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class DenyAllConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain authorization(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().denyAll())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt.publicKey(publicKey())));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class CustomAuthenticationManagerConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt.authenticationManager(authenticationManager())));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveAuthenticationManager authenticationManager() {\n\t\t\treturn mock(ReactiveAuthenticationManager.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class CustomAuthenticationManagerInLambdaConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t\t\t.authenticationManager(authenticationManager())\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveAuthenticationManager authenticationManager() {\n\t\t\treturn mock(ReactiveAuthenticationManager.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class CustomAuthenticationManagerResolverConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.pathMatchers(\"/*/message/**\").hasAnyAuthority(\"SCOPE_message:read\"))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.authenticationManagerResolver(authenticationManagerResolver()));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver() {\n\t\t\treturn mock(ReactiveAuthenticationManagerResolver.class);\n\t\t}\n\n\t\t@Bean\n\t\tReactiveAuthenticationManager authenticationManager() {\n\t\t\treturn mock(ReactiveAuthenticationManager.class);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class CustomAuthenticationFailureHandlerConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t.authenticationFailureHandler(authenticationFailureHandler())\n\t\t\t\t\t.jwt((jwt) -> jwt.authenticationManager(authenticationManager()))\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveAuthenticationManager authenticationManager() {\n\t\t\treturn mock(ReactiveAuthenticationManager.class);\n\t\t}\n\n\t\t@Bean\n\t\tServerAuthenticationFailureHandler authenticationFailureHandler() {\n\t\t\treturn mock(ServerAuthenticationFailureHandler.class);\n\t\t}\n\n\t}\n\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class CustomBearerTokenServerAuthenticationConverter {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().hasAuthority(\"SCOPE_message:read\"))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.bearerTokenConverter(bearerTokenAuthenticationConverter())\n\t\t\t\t\t.jwt((jwt) -> jwt.publicKey(publicKey())));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tServerAuthenticationConverter bearerTokenAuthenticationConverter() {\n\t\t\treturn (exchange) -> Mono.justOrEmpty(exchange.getRequest().getCookies().getFirst(\"TOKEN\").getValue())\n\t\t\t\t.map(BearerTokenAuthenticationToken::new);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class CustomJwtAuthenticationConverterConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().hasAuthority(\"message:read\"))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t\t\t.jwtAuthenticationConverter(jwtAuthenticationConverter())\n\t\t\t\t\t\t.publicKey(publicKey())));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tConverter<Jwt, Mono<AbstractAuthenticationToken>> jwtAuthenticationConverter() {\n\t\t\tJwtAuthenticationConverter converter = new JwtAuthenticationConverter();\n\t\t\tconverter.setJwtGrantedAuthoritiesConverter((jwt) -> {\n\t\t\t\tString[] claims = ((String) jwt.getClaims().get(\"scope\")).split(\" \");\n\t\t\t\treturn Stream.of(claims).map(SimpleGrantedAuthority::new).collect(Collectors.toList());\n\t\t\t});\n\t\t\treturn new ReactiveJwtAuthenticationConverterAdapter(converter);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class CustomErrorHandlingConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.pathMatchers(\"/authenticated\").authenticated()\n\t\t\t\t\t.pathMatchers(\"/unobtainable\").hasAuthority(\"unobtainable\"))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.accessDeniedHandler(new HttpStatusServerAccessDeniedHandler(HttpStatus.BANDWIDTH_LIMIT_EXCEEDED))\n\t\t\t\t\t.authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.I_AM_A_TEAPOT))\n\t\t\t\t\t.jwt((jwt) -> jwt.publicKey(publicKey())));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class IntrospectionConfig {\n\n\t\tprivate MockWebServer mockWebServer = new MockWebServer();\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\tString introspectionUri = mockWebServer().url(\"/introspect\").toString();\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.opaqueToken((opaqueToken) -> opaqueToken\n\t\t\t\t\t\t.introspectionUri(introspectionUri)\n\t\t\t\t\t\t.introspectionClientCredentials(\"client\", \"secret\")));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tMockWebServer mockWebServer() {\n\t\t\treturn this.mockWebServer;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.mockWebServer.shutdown();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class IntrospectionInLambdaConfig {\n\n\t\tprivate MockWebServer mockWebServer = new MockWebServer();\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\tString introspectionUri = mockWebServer().url(\"/introspect\").toString();\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t.opaqueToken((opaqueToken) -> opaqueToken\n\t\t\t\t\t\t\t\t\t.introspectionUri(introspectionUri)\n\t\t\t\t\t\t\t\t\t.introspectionClientCredentials(\"client\", \"secret\")\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tMockWebServer mockWebServer() {\n\t\t\treturn this.mockWebServer;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.mockWebServer.shutdown();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class AuthenticationManagerResolverPlusOtherConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t.anyExchange().authenticated())\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.authenticationManagerResolver(mock(ReactiveAuthenticationManagerResolver.class))\n\t\t\t\t\t.opaqueToken(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\tstatic class ReactiveOpaqueTokenAuthenticationConverterConfig {\n\n\t\tprivate MockWebServer mockWebServer = new MockWebServer();\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\tString introspectionUri = mockWebServer().url(\"/introspect\").toString();\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.opaqueToken((opaqueToken) -> opaqueToken\n\t\t\t\t\t\t.introspectionUri(introspectionUri)\n\t\t\t\t\t\t.introspectionClientCredentials(\"client\", \"secret\")\n\t\t\t\t\t\t.authenticationConverter(authenticationConverter())));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOpaqueTokenAuthenticationConverter authenticationConverter() {\n\t\t\treturn mock(ReactiveOpaqueTokenAuthenticationConverter.class);\n\t\t}\n\n\t\t@Bean\n\t\tMockWebServer mockWebServer() {\n\t\t\treturn this.mockWebServer;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.mockWebServer.shutdown();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class RootController {\n\n\t\t@GetMapping\n\t\tMono<String> get() {\n\t\t\treturn Mono.just(\"ok\");\n\t\t}\n\n\t\t@PostMapping\n\t\tMono<String> post() {\n\t\t\treturn Mono.just(\"ok\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/OidcBackChannelServerLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens;\nimport org.springframework.security.oauth2.client.oidc.server.session.InMemoryReactiveOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OidcBackChannelServerLogoutHandler}\n */\npublic class OidcBackChannelServerLogoutHandlerTests {\n\n\tprivate final ReactiveOidcSessionRegistry sessionRegistry = new InMemoryReactiveOidcSessionRegistry();\n\n\tprivate final OidcBackChannelLogoutAuthentication token = new OidcBackChannelLogoutAuthentication(\n\t\t\tTestOidcLogoutTokens.withSubject(\"issuer\", \"subject\").build(),\n\t\t\tTestClientRegistrations.clientRegistration().build());\n\n\t// gh-14553\n\t@Test\n\tpublic void computeLogoutEndpointWhenDifferentHostnameThenLocalhost() {\n\t\tOidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler(this.sessionRegistry);\n\t\tlogoutHandler.setLogoutUri(\"{baseScheme}://localhost{basePort}/logout\");\n\t\tMockServerHttpRequest request = MockServerHttpRequest\n\t\t\t.get(\"https://host.docker.internal:8090/back-channel/logout\")\n\t\t\t.build();\n\t\tString endpoint = logoutHandler.computeLogoutEndpoint(request, this.token);\n\t\tassertThat(endpoint).startsWith(\"https://localhost:8090/logout\");\n\t}\n\n\t@Test\n\tpublic void computeLogoutEndpointWhenUsingBaseUrlTemplateThenServerName() {\n\t\tOidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler(this.sessionRegistry);\n\t\tlogoutHandler.setLogoutUri(\"{baseUrl}/logout\");\n\t\tMockServerHttpRequest request = MockServerHttpRequest\n\t\t\t.get(\"http://host.docker.internal:8090/back-channel/logout\")\n\t\t\t.build();\n\t\tString endpoint = logoutHandler.computeLogoutEndpoint(request, this.token);\n\t\tassertThat(endpoint).startsWith(\"http://host.docker.internal:8090/logout\");\n\t}\n\n\t// gh-14609\n\t@Test\n\tpublic void computeLogoutEndpointWhenLogoutUriThenUses() {\n\t\tOidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler(this.sessionRegistry);\n\t\tlogoutHandler.setLogoutUri(\"http://localhost:8090/logout\");\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"https://server-one.com/back-channel/logout\").build();\n\t\tString endpoint = logoutHandler.computeLogoutEndpoint(request, this.token);\n\t\tassertThat(endpoint).startsWith(\"http://localhost:8090/logout\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/OidcLogoutSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.io.IOException;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.interfaces.RSAPublicKey;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.ImmutableJWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.oauth2.sdk.Scope;\nimport com.nimbusds.oauth2.sdk.token.BearerAccessToken;\nimport com.nimbusds.openid.connect.sdk.token.OIDCTokens;\nimport jakarta.annotation.PreDestroy;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.htmlunit.util.UrlUtils;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.http.client.reactive.ClientHttpConnector;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimNames;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens;\nimport org.springframework.security.oauth2.client.oidc.server.session.InMemoryReactiveOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;\nimport org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.test.web.reactive.server.WebTestClientConfigurer;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.ConfigurableWebApplicationContext;\nimport org.springframework.web.reactive.config.EnableWebFlux;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.server.WebSession;\nimport org.springframework.web.server.adapter.WebHttpHandlerBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.hamcrest.Matchers.hasValue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockAuthentication;\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;\n\n/**\n * Tests for {@link ServerHttpSecurity.OAuth2ResourceServerSpec}\n */\n@ExtendWith({ SpringTestContextExtension.class })\npublic class OidcLogoutSpecTests {\n\n\tprivate static final String SESSION_COOKIE_NAME = \"SESSION\";\n\n\tprivate WebTestClient test;\n\n\t@Autowired(required = false)\n\tprivate MockWebServer web;\n\n\t@Autowired\n\tprivate ClientRegistration clientRegistration;\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tthis.test = WebTestClient.bindToApplicationContext(context)\n\t\t\t.apply(springSecurity())\n\t\t\t.configureClient()\n\t\t\t.responseTimeout(Duration.ofDays(1))\n\t\t\t.build();\n\t\tif (context instanceof ConfigurableWebApplicationContext configurable) {\n\t\t\tconfigurable.getBeanFactory().registerResolvableDependency(WebTestClient.class, this.test);\n\t\t}\n\t}\n\n\t@Test\n\tvoid logoutWhenDefaultsThenRemotelyInvalidatesSessions() {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tString session = login();\n\t\tString logoutToken = this.test.mutateWith(session(session))\n\t\t\t.get()\n\t\t\t.uri(\"/token/logout\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.test.mutateWith(session(session)).get().uri(\"/token/logout\").exchange().expectStatus().isUnauthorized();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid logoutWhenInvalidLogoutTokenThenBadRequest() {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire();\n\t\tthis.test.get().uri(\"/token/logout\").exchange().expectStatus().isUnauthorized();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tFluxExchangeResult<String> result = this.test.get()\n\t\t\t.uri(\"/oauth2/authorization/\" + registrationId)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.returnResult(String.class);\n\t\tString session = sessionId(result);\n\t\tString redirectUrl = UrlUtils.decode(result.getResponseHeaders().getLocation().toString());\n\t\tString state = this.test\n\t\t\t.mutateWith(mockAuthentication(new TestingAuthenticationToken(this.clientRegistration.getClientId(),\n\t\t\t\t\tthis.clientRegistration.getClientSecret(), \"APP\")))\n\t\t\t.get()\n\t\t\t.uri(redirectUrl)\n\t\t\t.exchange()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tresult = this.test.get()\n\t\t\t.uri(\"/login/oauth2/code/\" + registrationId + \"?code=code&state=\" + state)\n\t\t\t.cookie(\"SESSION\", session)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.returnResult(String.class);\n\t\tsession = sessionId(result);\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", \"invalid\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isBadRequest()\n\t\t\t.expectBody(new ParameterizedTypeReference<Map<String, String>>() {\n\t\t\t})\n\t\t\t.value(hasValue(\"invalid_request\"));\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", session).exchange().expectStatus().isOk();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid logoutWhenLogoutTokenSpecifiesOneSessionThenRemotelyInvalidatesOnlyThatSession() throws Exception {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tString one = login();\n\t\tString two = login();\n\t\tString three = login();\n\t\tString logoutToken = this.test.get()\n\t\t\t.uri(\"/token/logout\")\n\t\t\t.cookie(\"SESSION\", one)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", one).exchange().expectStatus().isUnauthorized();\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", two).exchange().expectStatus().isOk();\n\t\tlogoutToken = this.test.get()\n\t\t\t.uri(\"/token/logout/all\")\n\t\t\t.cookie(\"SESSION\", three)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", two).exchange().expectStatus().isUnauthorized();\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", three).exchange().expectStatus().isUnauthorized();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid logoutWhenRemoteLogoutUriThenUses() {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, LogoutUriConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tString one = login();\n\t\tString logoutToken = this.test.get()\n\t\t\t.uri(\"/token/logout/all\")\n\t\t\t.cookie(\"SESSION\", one)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isBadRequest()\n\t\t\t.expectBody(new ParameterizedTypeReference<Map<String, String>>() {\n\t\t\t})\n\t\t\t.value(hasValue(\"partial_logout\"))\n\t\t\t.value(hasValue(containsString(\"not all sessions were terminated\")));\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", one).exchange().expectStatus().isOk();\n\t}\n\n\t@Test\n\tvoid logoutWhenSelfRemoteLogoutUriThenUses() {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, SelfLogoutUriConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tString sessionId = login();\n\t\tString logoutToken = this.test.get()\n\t\t\t.uri(\"/token/logout\")\n\t\t\t.cookie(\"SESSION\", sessionId)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", sessionId).exchange().expectStatus().isUnauthorized();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid logoutWhenDifferentCookieNameThenUses() {\n\t\tthis.spring.register(OidcProviderConfig.class, CookieConfig.class).autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tString sessionId = login();\n\t\tString logoutToken = this.test.get()\n\t\t\t.uri(\"/token/logout\")\n\t\t\t.cookie(\"SESSION\", sessionId)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", sessionId).exchange().expectStatus().isUnauthorized();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid logoutWhenRemoteLogoutFailsThenReportsPartialLogout() {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithBrokenLogoutConfig.class).autowire();\n\t\tServerLogoutHandler logoutHandler = this.spring.getContext().getBean(ServerLogoutHandler.class);\n\t\tgiven(logoutHandler.logout(any(), any())).willReturn(Mono.error(() -> new IllegalStateException(\"illegal\")));\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tString one = login();\n\t\tString logoutToken = this.test.get()\n\t\t\t.uri(\"/token/logout/all\")\n\t\t\t.cookie(\"SESSION\", one)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isBadRequest()\n\t\t\t.expectBody(String.class)\n\t\t\t.value(containsString(\"partial_logout\"));\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", one).exchange().expectStatus().isOk();\n\t}\n\n\t@Test\n\tvoid logoutWhenCustomComponentsThenUses() {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithCustomComponentsConfig.class)\n\t\t\t.autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tString sessionId = login();\n\t\tString logoutToken = this.test.get()\n\t\t\t.uri(\"/token/logout\")\n\t\t\t.cookie(\"SESSION\", sessionId)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.test.get().uri(\"/token/logout\").cookie(\"SESSION\", sessionId).exchange().expectStatus().isUnauthorized();\n\t\tReactiveOidcSessionRegistry sessionRegistry = this.spring.getContext()\n\t\t\t.getBean(ReactiveOidcSessionRegistry.class);\n\t\tverify(sessionRegistry, atLeastOnce()).saveSessionInformation(any());\n\t\tverify(sessionRegistry, atLeastOnce()).removeSessionInformation(any(OidcLogoutToken.class));\n\t}\n\n\t@Test\n\tvoid logoutWhenProviderIssuerMissingThen5xxServerError() {\n\t\tthis.spring.register(WebServerConfig.class, OidcProviderConfig.class, ProviderIssuerMissingConfig.class)\n\t\t\t.autowire();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tString session = login();\n\t\tString logoutToken = this.test.mutateWith(session(session))\n\t\t\t.get()\n\t\t\t.uri(\"/token/logout\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class)\n\t\t\t.getResponseBody()\n\t\t\t.blockFirst();\n\t\tthis.test.post()\n\t\t\t.uri(this.web.url(\"/logout/connect/back-channel/\" + registrationId).toString())\n\t\t\t.body(BodyInserters.fromFormData(\"logout_token\", logoutToken))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.is5xxServerError();\n\t\tthis.test.mutateWith(session(session)).get().uri(\"/token/logout\").exchange().expectStatus().isOk();\n\t}\n\n\tprivate String login() {\n\t\tthis.test.get().uri(\"/token/logout\").exchange().expectStatus().isUnauthorized();\n\t\tString registrationId = this.clientRegistration.getRegistrationId();\n\t\tFluxExchangeResult<String> result = this.test.get()\n\t\t\t.uri(\"/oauth2/authorization/\" + registrationId)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.returnResult(String.class);\n\t\tString sessionId = sessionId(result);\n\t\tString redirectUrl = UrlUtils.decode(result.getResponseHeaders().getLocation().toString());\n\t\tresult = this.test\n\t\t\t.mutateWith(mockAuthentication(new TestingAuthenticationToken(this.clientRegistration.getClientId(),\n\t\t\t\t\tthis.clientRegistration.getClientSecret(), \"APP\")))\n\t\t\t.get()\n\t\t\t.uri(redirectUrl)\n\t\t\t.exchange()\n\t\t\t.returnResult(String.class);\n\t\tString state = result.getResponseBody().blockFirst();\n\t\tresult = this.test.mutateWith(session(sessionId))\n\t\t\t.get()\n\t\t\t.uri(\"/login/oauth2/code/\" + registrationId + \"?code=code&state=\" + state)\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.returnResult(String.class);\n\t\treturn sessionId(result);\n\t}\n\n\tprivate String sessionId(FluxExchangeResult<?> result) {\n\t\tList<ResponseCookie> cookies = result.getResponseCookies().get(SESSION_COOKIE_NAME);\n\t\tif (cookies == null || cookies.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn cookies.get(0).getValue();\n\t}\n\n\tstatic SessionMutator session(String session) {\n\t\treturn new SessionMutator(session);\n\t}\n\n\tprivate record SessionMutator(String session) implements WebTestClientConfigurer {\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder, WebHttpHandlerBuilder httpHandlerBuilder,\n\t\t\t\tClientHttpConnector connector) {\n\t\t\tbuilder.defaultCookie(SESSION_COOKIE_NAME, this.session);\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class RegistrationConfig {\n\n\t\t@Autowired(required = false)\n\t\tMockWebServer web;\n\n\t\t@Bean\n\t\tClientRegistration clientRegistration() {\n\t\t\tif (this.web == null) {\n\t\t\t\treturn TestClientRegistrations.clientRegistration().build();\n\t\t\t}\n\t\t\tString issuer = this.web.url(\"/\").toString();\n\t\t\treturn TestClientRegistrations.clientRegistration()\n\t\t\t\t.issuerUri(issuer)\n\t\t\t\t.jwkSetUri(issuer + \"jwks\")\n\t\t\t\t.tokenUri(issuer + \"token\")\n\t\t\t\t.userInfoUri(issuer + \"user\")\n\t\t\t\t.scope(\"openid\")\n\t\t\t\t.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository(ClientRegistration clientRegistration) {\n\t\t\treturn new InMemoryReactiveClientRegistrationRepository(clientRegistration);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class DefaultConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t\t.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class LogoutUriConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t\t.oidcLogout((oidc) -> oidc\n\t\t\t\t\t\t.backChannel((backchannel) -> backchannel.logoutUri(\"http://localhost/wrong\"))\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class SelfLogoutUriConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t.oidcLogout((oidc) -> oidc\n\t\t\t\t\t.backChannel(Customizer.withDefaults())\n\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class CookieConfig {\n\n\t\tprivate final MockWebServer server = new MockWebServer();\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t.oidcLogout((oidc) -> oidc\n\t\t\t\t\t.backChannel(Customizer.withDefaults())\n\t\t\t\t);\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOidcSessionRegistry oidcSessionRegistry() {\n\t\t\treturn new InMemoryReactiveOidcSessionRegistry();\n\t\t}\n\n\t\t@Bean\n\t\tOidcBackChannelServerLogoutHandler oidcLogoutHandler(ReactiveOidcSessionRegistry sessionRegistry) {\n\t\t\tOidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler(sessionRegistry);\n\t\t\tlogoutHandler.setSessionCookieName(\"JSESSIONID\");\n\t\t\treturn logoutHandler;\n\t\t}\n\n\t\t@Bean\n\t\tMockWebServer web(ObjectProvider<WebTestClient> web) {\n\t\t\tWebTestClientDispatcher dispatcher = new WebTestClientDispatcher(web);\n\t\t\tdispatcher.setAssertion((rr) -> {\n\t\t\t\tString cookie = rr.getHeaders().get(\"Cookie\");\n\t\t\t\tif (cookie == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tassertThat(cookie).contains(\"JSESSIONID\");\n\t\t\t});\n\t\t\tthis.server.setDispatcher(dispatcher);\n\t\t\treturn this.server;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.server.shutdown();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class WithCustomComponentsConfig {\n\n\t\tReactiveOidcSessionRegistry sessionRegistry = spy(new InMemoryReactiveOidcSessionRegistry());\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t\t.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveOidcSessionRegistry sessionRegistry() {\n\t\t\treturn this.sessionRegistry;\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(RegistrationConfig.class)\n\tstatic class WithBrokenLogoutConfig {\n\n\t\tprivate final ServerLogoutHandler logoutHandler = mock(ServerLogoutHandler.class);\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t\t.logout((logout) -> logout.logoutHandler(this.logoutHandler))\n\t\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t\t.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tServerLogoutHandler logoutHandler() {\n\t\t\treturn this.logoutHandler;\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class ProviderIssuerMissingRegistrationConfig {\n\n\t\t@Autowired(required = false)\n\t\tMockWebServer web;\n\n\t\t@Bean\n\t\tClientRegistration clientRegistration() {\n\t\t\tif (this.web == null) {\n\t\t\t\treturn TestClientRegistrations.clientRegistration().issuerUri(null).build();\n\t\t\t}\n\t\t\tString issuer = this.web.url(\"/\").toString();\n\t\t\treturn TestClientRegistrations.clientRegistration()\n\t\t\t\t.issuerUri(null)\n\t\t\t\t.jwkSetUri(issuer + \"jwks\")\n\t\t\t\t.tokenUri(issuer + \"token\")\n\t\t\t\t.userInfoUri(issuer + \"user\")\n\t\t\t\t.scope(\"openid\")\n\t\t\t\t.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository(ClientRegistration clientRegistration) {\n\t\t\treturn new InMemoryReactiveClientRegistrationRepository(clientRegistration);\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@Import(ProviderIssuerMissingRegistrationConfig.class)\n\tstatic class ProviderIssuerMissingConfig {\n\n\t\t@Bean\n\t\t@Order(1)\n\t\tSecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t\t\t.oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults()));\n\t\t\t// @formatter:on\n\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFluxSecurity\n\t@EnableWebFlux\n\t@RestController\n\tstatic class OidcProviderConfig {\n\n\t\tprivate static final RSAKey key = key();\n\n\t\tprivate static final JWKSource<SecurityContext> jwks = jwks(key);\n\n\t\tprivate static RSAKey key() {\n\t\t\ttry {\n\t\t\t\tKeyPair pair = KeyPairGenerator.getInstance(\"RSA\").generateKeyPair();\n\t\t\t\treturn new RSAKey.Builder((RSAPublicKey) pair.getPublic()).privateKey(pair.getPrivate()).build();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate static JWKSource<SecurityContext> jwks(RSAKey key) {\n\t\t\ttry {\n\t\t\t\treturn new ImmutableJWKSet<>(new JWKSet(key));\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate final String username = \"user\";\n\n\t\tprivate final JwtEncoder encoder = new NimbusJwtEncoder(jwks);\n\n\t\tprivate String nonce;\n\n\t\t@Autowired\n\t\tClientRegistration registration;\n\n\t\t@Autowired(required = false)\n\t\tMockWebServer web;\n\n\t\tstatic ServerWebExchangeMatcher or(String... patterns) {\n\t\t\tList<ServerWebExchangeMatcher> matchers = new ArrayList<>();\n\t\t\tfor (String pattern : patterns) {\n\t\t\t\tmatchers.add(new PathPatternParserServerWebExchangeMatcher(pattern));\n\t\t\t}\n\t\t\treturn new OrServerWebExchangeMatcher(matchers);\n\t\t}\n\n\t\t@Bean\n\t\t@Order(0)\n\t\tSecurityWebFilterChain authorizationServer(ServerHttpSecurity http, ClientRegistration registration)\n\t\t\t\tthrows Exception {\n\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.securityMatcher(or(\"/jwks\", \"/login/oauth/authorize\", \"/nonce\", \"/token\", \"/token/logout\", \"/user\"))\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t\t.pathMatchers(\"/jwks\").permitAll()\n\t\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t\t\t\t.jwt((jwt) -> jwt.jwkSetUri(registration.getProviderDetails().getJwkSetUri()))\n\t\t\t\t\t);\n\t\t\t// @formatter:off\n\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveUserDetailsService users(ClientRegistration registration) {\n\t\t\treturn new MapReactiveUserDetailsService(User.withUsername(registration.getClientId())\n\t\t\t\t\t.password(\"{noop}\" + registration.getClientSecret()).authorities(\"APP\").build());\n\t\t}\n\n\t\t@GetMapping(\"/login/oauth/authorize\")\n\t\tString nonce(@RequestParam(\"nonce\") String nonce, @RequestParam(\"state\") String state) {\n\t\t\tthis.nonce = nonce;\n\t\t\treturn state;\n\t\t}\n\n\t\t@PostMapping(\"/token\")\n\t\tMap<String, Object> accessToken(WebSession session) {\n\t\t\tJwtEncoderParameters parameters = JwtEncoderParameters\n\t\t\t\t\t.from(JwtClaimsSet.builder().id(\"id\").subject(this.username)\n\t\t\t\t\t\t\t.issuer(getIssuerUri()).issuedAt(Instant.now())\n\t\t\t\t\t\t\t.expiresAt(Instant.now().plusSeconds(86400)).claim(\"scope\", \"openid\").build());\n\t\t\tString token = this.encoder.encode(parameters).getTokenValue();\n\t\t\treturn new OIDCTokens(idToken(session.getId()), new BearerAccessToken(token, 86400, new Scope(\"openid\")), null)\n\t\t\t\t\t.toJSONObject();\n\t\t}\n\n\t\tString idToken(String sessionId) {\n\t\t\tOidcIdToken token = TestOidcIdTokens.idToken().issuer(getIssuerUri())\n\t\t\t\t\t.subject(this.username).expiresAt(Instant.now().plusSeconds(86400))\n\t\t\t\t\t.audience(List.of(this.registration.getClientId())).nonce(this.nonce)\n\t\t\t\t\t.claim(LogoutTokenClaimNames.SID, sessionId).build();\n\t\t\tJwtEncoderParameters parameters = JwtEncoderParameters\n\t\t\t\t\t.from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build());\n\t\t\treturn this.encoder.encode(parameters).getTokenValue();\n\t\t}\n\n\t\tprivate String getIssuerUri() {\n\t\t\tif (this.web == null) {\n\t\t\t\treturn TestClientRegistrations.clientRegistration().build().getProviderDetails().getIssuerUri();\n\t\t\t}\n\t\t\treturn this.web.url(\"/\").toString();\n\t\t}\n\n\t\t@GetMapping(\"/user\")\n\t\tMap<String, Object> userinfo() {\n\t\t\treturn Map.of(\"sub\", this.username, \"id\", this.username);\n\t\t}\n\n\t\t@GetMapping(\"/jwks\")\n\t\tString jwks() {\n\t\t\treturn new JWKSet(key).toString();\n\t\t}\n\n\t\t@GetMapping(\"/token/logout\")\n\t\tString logoutToken(@AuthenticationPrincipal OidcUser user) {\n\t\t\tOidcLogoutToken token = TestOidcLogoutTokens.withUser(user)\n\t\t\t\t\t.audience(List.of(this.registration.getClientId())).build();\n\t\t\tJwsHeader header = JwsHeader.with(SignatureAlgorithm.RS256).type(\"logout+jwt\").build();\n\t\t\tJwtClaimsSet claims = JwtClaimsSet.builder().claims((c) -> c.putAll(token.getClaims())).build();\n\t\t\tJwtEncoderParameters parameters = JwtEncoderParameters.from(header, claims);\n\t\t\treturn this.encoder.encode(parameters).getTokenValue();\n\t\t}\n\n\t\t@GetMapping(\"/token/logout/all\")\n\t\tString logoutTokenAll(@AuthenticationPrincipal OidcUser user) {\n\t\t\tOidcLogoutToken token = TestOidcLogoutTokens.withUser(user)\n\t\t\t\t\t.audience(List.of(this.registration.getClientId()))\n\t\t\t\t\t.claims((claims) -> claims.remove(LogoutTokenClaimNames.SID)).build();\n\t\t\tJwsHeader header = JwsHeader.with(SignatureAlgorithm.RS256).type(\"JWT\").build();\n\t\t\tJwtClaimsSet claims = JwtClaimsSet.builder().claims((c) -> c.putAll(token.getClaims())).build();\n\t\t\tJwtEncoderParameters parameters = JwtEncoderParameters.from(header, claims);\n\t\t\treturn this.encoder.encode(parameters).getTokenValue();\n\t\t}\n\t}\n\n\t@Configuration\n\tstatic class WebServerConfig {\n\n\t\tprivate final MockWebServer server = new MockWebServer();\n\n\t\t@Bean\n\t\tMockWebServer web(ObjectProvider<WebTestClient> web) {\n\t\t\tthis.server.setDispatcher(new WebTestClientDispatcher(web));\n\t\t\treturn this.server;\n\t\t}\n\n\t\t@PreDestroy\n\t\tvoid shutdown() throws IOException {\n\t\t\tthis.server.shutdown();\n\t\t}\n\n\t}\n\n\tprivate static class WebTestClientDispatcher extends Dispatcher {\n\n\t\tprivate final ObjectProvider<WebTestClient> webProvider;\n\n\t\tprivate WebTestClient web;\n\n\t\tprivate Consumer<RecordedRequest> assertion = (rr) -> { };\n\n\t\tWebTestClientDispatcher(ObjectProvider<WebTestClient> web) {\n\t\t\tthis.webProvider = web;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockResponse dispatch(RecordedRequest request) throws InterruptedException {\n\t\t\tthis.assertion.accept(request);\n\t\t\tthis.web = this.webProvider.getObject();\n\t\t\tString method = request.getMethod();\n\t\t\tString path = request.getPath();\n\t\t\tString csrf = request.getHeader(\"X-CSRF-TOKEN\");\n\t\t\tString sessionId = session(request);\n\t\t\tWebTestClient.RequestHeadersSpec<?> r;\n\t\t\tif (\"GET\".equals(method)) {\n\t\t\t\tr = this.web.get().uri(path);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tWebTestClient.RequestBodySpec body;\n\t\t\t\tif (csrf == null) {\n\t\t\t\t\tbody = this.web.mutateWith(csrf()).post().uri(path);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tbody = this.web.post().uri(path).header(\"X-CSRF-TOKEN\", csrf);\n\t\t\t\t}\n\t\t\t\tbody.body(BodyInserters.fromValue(request.getBody().readUtf8()));\n\t\t\t\tr = body;\n\t\t\t}\n\t\t\tfor (Map.Entry<String, List<String>> header : request.getHeaders().toMultimap().entrySet()) {\n\t\t\t\tif (header.getKey().equalsIgnoreCase(\"Cookie\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tr.header(header.getKey(), header.getValue().iterator().next());\n\t\t\t}\n\t\t\tif (sessionId != null) {\n\t\t\t\tr.cookie(SESSION_COOKIE_NAME, sessionId);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tFluxExchangeResult<String> result = r.exchange().returnResult(String.class);\n\t\t\t\treturn toMockResponse(result);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tMockResponse response = new MockResponse();\n\t\t\t\tresponse.setResponseCode(500);\n\t\t\t\tresponse.setBody(ex.getMessage());\n\t\t\t\treturn response;\n\t\t\t}\n\t\t}\n\n\t\tvoid setAssertion(Consumer<RecordedRequest> assertion) {\n\t\t\tthis.assertion = assertion;\n\t\t}\n\n\t\tprivate String session(RecordedRequest request) {\n\t\t\tString cookieHeaderValue = request.getHeader(\"Cookie\");\n\t\t\tif (cookieHeaderValue == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString[] cookies = cookieHeaderValue.split(\";\");\n\t\t\tfor (String cookie : cookies) {\n\t\t\t\tString[] parts = cookie.split(\"=\");\n\t\t\t\tif (SESSION_COOKIE_NAME.equals(parts[0])) {\n\t\t\t\t\treturn parts[1];\n\t\t\t\t}\n\t\t\t\tif (\"JSESSIONID\".equals(parts[0])) {\n\t\t\t\t\treturn parts[1];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate MockResponse toMockResponse(FluxExchangeResult<String> result) {\n\t\t\tMockResponse response = new MockResponse();\n\t\t\tresponse.setResponseCode(result.getStatus().value());\n\t\t\tfor (String name : result.getResponseHeaders().headerNames()) {\n\t\t\t\tresponse.addHeader(name, result.getResponseHeaders().getFirst(name));\n\t\t\t}\n\t\t\tString body = result.getResponseBody().blockFirst();\n\t\t\tif (body != null) {\n\t\t\t\tresponse.setBody(body);\n\t\t\t}\n\t\t\treturn response;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/OneTimeTokenLoginSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Mockito;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver;\nimport org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver;\nimport org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.server.authentication.ott.ServerRedirectOneTimeTokenGenerationSuccessHandler;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.reactive.config.EnableWebFlux;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatException;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link ServerHttpSecurity.OneTimeTokenLoginSpec}\n *\n * @author Max Batischev\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class OneTimeTokenLoginSpecTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tprivate WebTestClient client;\n\n\tprivate static final String EXPECTED_HTML_HEAD = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <meta charset=\"utf-8\">\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t    <title>Please sign in</title>\n\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t  </head>\n\t\t\t\"\"\";\n\n\tprivate static final String LOGIN_PART = \"\"\"\n\t\t\t<form class=\"login-form\" method=\"post\" action=\"/login\">\n\t\t\t\"\"\";\n\n\tprivate static final String GENERATE_OTT_PART = \"\"\"\n\t\t\t<form id=\"ott-form\" class=\"login-form\" method=\"post\" action=\"/ott/generate\">\n\t\t\t\"\"\";\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tthis.client = WebTestClient.bindToApplicationContext(context).build();\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenCorrectTokenThenCanAuthenticate() {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/ott/generate\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t\t.body(BodyInserters.fromFormData(\"username\", \"user\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login/ott\");\n\t\t// @formatter:on\n\n\t\tString token = getLastToken().getTokenValue();\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/login/ott\")\n\t\t\t\t\t\t.queryParam(\"token\", token)\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenDifferentAuthenticationUrlsThenCanAuthenticate() {\n\t\tthis.spring.register(OneTimeTokenDifferentUrlsConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/generateurl\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t\t.body(BodyInserters.fromValue(\"username=user\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/redirected\");\n\t\t// @formatter:on\n\n\t\tString token = getLastToken().getTokenValue();\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/loginprocessingurl\")\n\t\t\t\t\t\t.queryParam(\"token\", token)\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/authenticated\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenCorrectTokenUsedTwiceThenSecondTimeFails() {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/ott/generate\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t\t.body(BodyInserters.fromValue(\"username=user\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login/ott\");\n\t\t// @formatter:on\n\n\t\tString token = getLastToken().getTokenValue();\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/login/ott\")\n\t\t\t\t\t\t.queryParam(\"token\", token)\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/\");\n\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/login/ott\")\n\t\t\t\t\t\t.queryParam(\"token\", token)\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login?error\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenWrongTokenThenAuthenticationFail() {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/ott/generate\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t\t.body(BodyInserters.fromValue(\"username=user\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login/ott\");\n\t\t// @formatter:on\n\n\t\tString token = \"wrong\";\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/login/ott\")\n\t\t\t\t\t\t.queryParam(\"token\", token)\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login?error\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenConfiguredThenRendersRequestTokenForm() {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\n\t\t//@formatter:off\n\t\tbyte[] responseByteArray = this.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.get()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/login\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectBody()\n\t\t\t\t.returnResult()\n\t\t\t\t.getResponseBody();\n\t\t// @formatter:on\n\n\t\tString response = new String(responseByteArray);\n\n\t\tassertThat(response.contains(EXPECTED_HTML_HEAD)).isTrue();\n\t\tassertThat(response.contains(GENERATE_OTT_PART)).isTrue();\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenConfiguredThenRedirectsToLoginPage() {\n\t\tthis.spring.register(OneTimeTokenDefaultConfig.class).autowire();\n\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t.get()\n\t\t\t.uri((uriBuilder) -> uriBuilder.path(\"/\").build())\n\t\t\t.exchange()\n\t\t\t.expectHeader()\n\t\t\t.location(\"/login\");\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenFormLoginConfiguredThenRendersRequestTokenForm() {\n\t\tthis.spring.register(OneTimeTokenFormLoginConfig.class).autowire();\n\n\t\t//@formatter:off\n\t\tbyte[] responseByteArray = this.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.get()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/login\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectBody()\n\t\t\t\t.returnResult()\n\t\t\t\t.getResponseBody();\n\t\t// @formatter:on\n\n\t\tString response = new String(responseByteArray);\n\n\t\tassertThat(response.contains(EXPECTED_HTML_HEAD)).isTrue();\n\t\tassertThat(response.contains(LOGIN_PART)).isTrue();\n\t\tassertThat(response.contains(GENERATE_OTT_PART)).isTrue();\n\t}\n\n\tprivate OneTimeToken getLastToken() {\n\t\tOneTimeToken lastToken = this.spring.getContext()\n\t\t\t.getBean(TestServerOneTimeTokenGenerationSuccessHandler.class).lastToken;\n\t\treturn lastToken;\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenCustomLoginPageThenRedirects() {\n\t\tthis.spring.register(OneTimeTokenDifferentUrlsConfig.class).autowire();\n\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t.get()\n\t\t\t.uri((uriBuilder) -> uriBuilder.path(\"/login\").build())\n\t\t\t.exchange()\n\t\t\t.expectHeader()\n\t\t\t.location(\"/custom-login\");\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenNoOneTimeTokenGenerationSuccessHandlerThenException() {\n\t\tassertThatException()\n\t\t\t.isThrownBy(() -> this.spring.register(OneTimeTokenNotGeneratedOttHandlerConfig.class).autowire())\n\t\t\t.havingRootCause()\n\t\t\t.isInstanceOf(IllegalStateException.class)\n\t\t\t.withMessage(\"\"\"\n\t\t\t\t\tA ServerOneTimeTokenGenerationSuccessHandler is required to enable oneTimeTokenLogin().\n\t\t\t\t\tPlease provide it as a bean or pass it to the oneTimeTokenLogin() DSL.\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid oneTimeTokenWhenCustomRequestResolverSetThenCustomResolverUse() {\n\t\tthis.spring.register(OneTimeTokenConfigWithCustomRequestResolver.class).autowire();\n\n\t\t// @formatter:off\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf())\n\t\t\t\t.post()\n\t\t\t\t.uri((uriBuilder) -> uriBuilder\n\t\t\t\t\t\t.path(\"/ott/generate\")\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t\t.body(BodyInserters.fromFormData(\"username\", \"user\"))\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/login/ott\");\n\t\t// @formatter:on\n\n\t\tServerGenerateOneTimeTokenRequestResolver resolver = this.spring.getContext()\n\t\t\t.getBean(ServerGenerateOneTimeTokenRequestResolver.class);\n\n\t\tverify(resolver, times(1)).resolve(ArgumentMatchers.any(ServerWebExchange.class));\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenDefaultConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http,\n\t\t\t\tServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t\t.anyExchange()\n\t\t\t\t\t\t\t.authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin((ott) -> ott\n\t\t\t\t\t\t\t.tokenGenerationSuccessHandler(ottSuccessHandler)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tTestServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {\n\t\t\treturn new TestServerOneTimeTokenGenerationSuccessHandler();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenDifferentUrlsConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain securityFilterChain(ServerHttpSecurity http,\n\t\t\t\tServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t\t.anyExchange()\n\t\t\t\t\t\t\t.authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin((ott) -> ott\n\t\t\t\t\t\t\t.loginPage(\"/custom-login\")\n\t\t\t\t\t\t\t.tokenGeneratingUrl(\"/generateurl\")\n\t\t\t\t\t\t\t.tokenGenerationSuccessHandler(ottSuccessHandler)\n\t\t\t\t\t\t\t.loginProcessingUrl(\"/loginprocessingurl\")\n\t\t\t\t\t\t\t.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler(\"/authenticated\"))\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tTestServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {\n\t\t\treturn new TestServerOneTimeTokenGenerationSuccessHandler(\"/redirected\");\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenFormLoginConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain securityFilterChain(ServerHttpSecurity http,\n\t\t\t\tServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t\t.anyExchange()\n\t\t\t\t\t\t\t.authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t\t.oneTimeTokenLogin((ott) -> ott\n\t\t\t\t\t\t\t.tokenGenerationSuccessHandler(ottSuccessHandler)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tTestServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {\n\t\t\treturn new TestServerOneTimeTokenGenerationSuccessHandler();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenNotGeneratedOttHandlerConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t\t.anyExchange()\n\t\t\t\t\t\t\t.authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\tstatic class UserDetailsServiceConfig {\n\n\t\t@Bean\n\t\tReactiveUserDetailsService userDetailsService() {\n\t\t\treturn new MapReactiveUserDetailsService(\n\t\t\t\t\tMap.of(\"user\", new User(\"user\", \"password\", Collections.emptyList())));\n\t\t}\n\n\t}\n\n\t@Configuration(proxyBeanMethods = false)\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(UserDetailsServiceConfig.class)\n\tstatic class OneTimeTokenConfigWithCustomRequestResolver {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http,\n\t\t\t\tServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t\t.anyExchange()\n\t\t\t\t\t\t\t.authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oneTimeTokenLogin((ott) -> ott\n\t\t\t\t\t\t\t.tokenGenerationSuccessHandler(ottSuccessHandler)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tServerGenerateOneTimeTokenRequestResolver resolver() {\n\t\t\treturn Mockito.spy(new DefaultServerGenerateOneTimeTokenRequestResolver());\n\t\t}\n\n\t\t@Bean\n\t\tTestServerOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {\n\t\t\treturn new TestServerOneTimeTokenGenerationSuccessHandler();\n\t\t}\n\n\t}\n\n\tprivate static class TestServerOneTimeTokenGenerationSuccessHandler\n\t\t\timplements ServerOneTimeTokenGenerationSuccessHandler {\n\n\t\tprivate OneTimeToken lastToken;\n\n\t\tprivate final ServerOneTimeTokenGenerationSuccessHandler delegate;\n\n\t\tTestServerOneTimeTokenGenerationSuccessHandler() {\n\t\t\tthis.delegate = new ServerRedirectOneTimeTokenGenerationSuccessHandler(\"/login/ott\");\n\t\t}\n\n\t\tTestServerOneTimeTokenGenerationSuccessHandler(String redirectUrl) {\n\t\t\tthis.delegate = new ServerRedirectOneTimeTokenGenerationSuccessHandler(redirectUrl);\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {\n\t\t\tthis.lastToken = oneTimeToken;\n\t\t\treturn this.delegate.handle(exchange, oneTimeToken);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/PasswordManagementSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.apache.http.HttpHeaders;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;\nimport org.springframework.security.config.web.server.ServerHttpSecurity.PasswordManagementSpec;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PasswordManagementSpec}.\n *\n * @author Evgeniy Cheban\n */\npublic class PasswordManagementSpecTests {\n\n\tServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication();\n\n\t@Test\n\tpublic void whenChangePasswordPageNotSetThenDefaultChangePasswordPageUsed() {\n\t\tthis.http.passwordManagement(Customizer.withDefaults());\n\n\t\tWebTestClient client = buildClient();\n\t\tclient.get()\n\t\t\t.uri(\"/.well-known/change-password\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.expectHeader()\n\t\t\t.valueEquals(HttpHeaders.LOCATION, \"/change-password\");\n\t}\n\n\t@Test\n\tpublic void whenChangePasswordPageSetThenSpecifiedChangePasswordPageUsed() {\n\t\tthis.http.passwordManagement(\n\t\t\t\t(passwordManagement) -> passwordManagement.changePasswordPage(\"/custom-change-password-page\"));\n\n\t\tWebTestClient client = buildClient();\n\t\tclient.get()\n\t\t\t.uri(\"/.well-known/change-password\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.expectHeader()\n\t\t\t.valueEquals(HttpHeaders.LOCATION, \"/custom-change-password-page\");\n\t}\n\n\tprivate WebTestClient buildClient() {\n\t\treturn WebTestClientBuilder.bindToWebFilters(this.http.build()).build();\n\t}\n\n\t@Test\n\tpublic void whenSettingNullChangePasswordPage() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.http.passwordManagement((password) -> password.changePasswordPage(null)))\n\t\t\t.withMessage(\"changePasswordPage cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void whenSettingEmptyChangePasswordPage() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.http.passwordManagement((password) -> password.changePasswordPage(\"\")))\n\t\t\t.withMessage(\"changePasswordPage cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void whenSettingBlankChangePasswordPage() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.http.passwordManagement((password) -> password.changePasswordPage(\" \")))\n\t\t\t.withMessage(\"changePasswordPage cannot be empty\");\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/RequestCacheTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.junit.jupiter.api.Test;\nimport org.openqa.selenium.WebDriver;\nimport org.openqa.selenium.support.PageFactory;\n\nimport org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;\nimport org.springframework.security.config.web.server.FormLoginTests.DefaultLoginPage;\nimport org.springframework.security.config.web.server.FormLoginTests.HomePage;\nimport org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.WebFilterChainProxy;\nimport org.springframework.security.web.server.savedrequest.NoOpServerRequestCache;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class RequestCacheTests {\n\n\tprivate ServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication();\n\n\t@Test\n\tpublic void defaultFormLoginRequestCache() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin(withDefaults())\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClient\n\t\t\t\t.bindToController(new SecuredPageController(), new WebTestClientBuilder.Http200RestController())\n\t\t\t\t.webFilter(new WebFilterChainProxy(securityWebFilter))\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultLoginPage loginPage = SecuredPage.to(driver, DefaultLoginPage.class).assertAt();\n\t\t// @formatter:off\n\t\tSecuredPage securedPage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(SecuredPage.class);\n\t\t// @formatter:on\n\t\tsecuredPage.assertAt();\n\t}\n\n\t@Test\n\tpublic void requestCacheNoOp() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated())\n\t\t\t.formLogin(withDefaults())\n\t\t\t.requestCache((cache) -> cache\n\t\t\t\t.requestCache(NoOpServerRequestCache.getInstance()))\n\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClient\n\t\t\t\t.bindToController(new SecuredPageController(), new WebTestClientBuilder.Http200RestController())\n\t\t\t\t.webFilter(new WebFilterChainProxy(securityWebFilter))\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultLoginPage loginPage = SecuredPage.to(driver, DefaultLoginPage.class).assertAt();\n\t\t// @formatter:off\n\t\tHomePage securedPage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\tsecuredPage.assertAt();\n\t}\n\n\t@Test\n\tpublic void requestWhenCustomRequestCacheInLambdaThenCustomCacheUsed() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityWebFilter = this.http\n\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t)\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.requestCache((requestCache) -> requestCache\n\t\t\t\t\t\t.requestCache(NoOpServerRequestCache.getInstance())\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tWebTestClient webTestClient = WebTestClient\n\t\t\t\t.bindToController(new SecuredPageController(), new WebTestClientBuilder.Http200RestController())\n\t\t\t\t.webFilter(new WebFilterChainProxy(securityWebFilter))\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultLoginPage loginPage = SecuredPage.to(driver, DefaultLoginPage.class).assertAt();\n\t\t// @formatter:off\n\t\tHomePage securedPage = loginPage.loginForm()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.submit(HomePage.class);\n\t\t// @formatter:on\n\t\tsecuredPage.assertAt();\n\t}\n\n\tpublic static class SecuredPage {\n\n\t\tprivate WebDriver driver;\n\n\t\tpublic SecuredPage(WebDriver driver) {\n\t\t\tthis.driver = driver;\n\t\t}\n\n\t\tpublic void assertAt() {\n\t\t\tassertThat(this.driver.getTitle()).isEqualTo(\"Secured\");\n\t\t}\n\n\t\tstatic <T> T to(WebDriver driver, Class<T> page) {\n\t\t\tdriver.get(\"http://localhost/secured\");\n\t\t\treturn PageFactory.initElements(driver, page);\n\t\t}\n\n\t}\n\n\t@Controller\n\tpublic static class SecuredPageController {\n\n\t\t@ResponseBody\n\t\t@GetMapping(\"/secured\")\n\t\tpublic String login(ServerWebExchange exchange) {\n\t\t\t// @formatter:off\n\t\t\treturn \"<!DOCTYPE html>\\n\"\n\t\t\t\t+ \"<html lang=\\\"en\\\">\\n\"\n\t\t\t\t+ \"  <head>\\n\"\n\t\t\t\t+ \"    <title>Secured</title>\\n\"\n\t\t\t\t+ \"  </head>\\n\"\n\t\t\t\t+ \"  <body>\\n\"\n\t\t\t\t+ \"    <h1>Secured</h1>\\n\"\n\t\t\t\t+ \"  </body>\\n\"\n\t\t\t\t+ \"</html>\";\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.apache.http.HttpHeaders;\nimport org.hamcrest.Matchers;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.publisher.TestPublisher;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter;\nimport org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterChainProxy;\nimport org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilterTests;\nimport org.springframework.security.web.server.authentication.DelegatingServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter;\nimport org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;\nimport org.springframework.security.web.server.authentication.logout.LogoutWebFilter;\nimport org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.security.web.server.csrf.CsrfServerLogoutHandler;\nimport org.springframework.security.web.server.csrf.CsrfToken;\nimport org.springframework.security.web.server.csrf.CsrfWebFilter;\nimport org.springframework.security.web.server.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRepository;\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRequestHandler;\nimport org.springframework.security.web.server.csrf.XorServerCsrfTokenRequestAttributeHandler;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.reactive.server.EntityExchangeResult;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n/**\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServerHttpSecurityTests {\n\n\t@Mock\n\tprivate ServerSecurityContextRepository contextRepository;\n\n\t@Mock\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t@Mock\n\tprivate ServerCsrfTokenRepository csrfTokenRepository;\n\n\tprivate ServerHttpSecurity http;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.http = ServerHttpSecurityConfigurationBuilder.http().authenticationManager(this.authenticationManager);\n\t}\n\n\t@Test\n\tpublic void defaults() {\n\t\tTestPublisher<SecurityContext> securityContext = TestPublisher.create();\n\t\tgiven(this.contextRepository.load(any())).willReturn(securityContext.mono());\n\t\tthis.http.securityContextRepository(this.contextRepository);\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tFluxExchangeResult<String> result = client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, \".+\")\n\t\t\t\t.returnResult(String.class);\n\t\t// @formatter:on\n\t\tassertThat(result.getResponseCookies()).isEmpty();\n\t\t// there is no need to try and load the SecurityContext by default\n\t\tsecurityContext.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void basic() {\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"rob\", \"rob\", \"ROLE_USER\", \"ROLE_ADMIN\")));\n\t\tthis.http.httpBasic(withDefaults());\n\t\tthis.http.authenticationManager(this.authenticationManager);\n\t\tthis.http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBasicAuth(\"rob\", \"rob\")\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, \".+\")\n\t\t\t\t.expectBody(String.class).consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t\t.returnResult();\n\t\t// @formatter:on\n\t\tassertThat(result.getResponseCookies().getFirst(\"SESSION\")).isNull();\n\t}\n\n\t@Test\n\tpublic void basicWithGlobalWebSessionServerSecurityContextRepository() {\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"rob\", \"rob\", \"ROLE_USER\", \"ROLE_ADMIN\")));\n\t\tthis.http.securityContextRepository(new WebSessionServerSecurityContextRepository());\n\t\tthis.http.httpBasic(withDefaults());\n\t\tthis.http.authenticationManager(this.authenticationManager);\n\t\tthis.http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBasicAuth(\"rob\", \"rob\")\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, \".+\")\n\t\t\t\t.expectBody(String.class).consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t\t.returnResult();\n\t\t// @formatter:on\n\t\tassertThat(result.getResponseCookies().getFirst(\"SESSION\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void basicWhenNoCredentialsThenUnauthorized() {\n\t\tthis.http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tclient.get().uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, \".+\")\n\t\t\t\t.expectBody().isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void basicWhenXHRRequestThenUnauthorized() {\n\t\tServerAuthenticationEntryPoint authenticationEntryPoint = spy(\n\t\t\t\tnew HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED));\n\t\tthis.http.httpBasic((basic) -> basic.authenticationEntryPoint(authenticationEntryPoint));\n\t\tthis.http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tclient.get().uri(\"/\")\n\t\t\t\t.header(\"X-Requested-With\", \"XMLHttpRequest\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().doesNotExist(\"WWW-Authenticate\")\n\t\t\t\t.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, \".+\")\n\t\t\t\t.expectBody().isEmpty();\n\t\t// @formatter:on\n\t\tverify(authenticationEntryPoint).commence(any(), any());\n\t}\n\n\t@Test\n\tpublic void basicWhenCustomAuthenticationFailureHandlerThenUses() {\n\t\tReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tServerAuthenticationFailureHandler authenticationFailureHandler = mock(\n\t\t\t\tServerAuthenticationFailureHandler.class);\n\t\tthis.http.httpBasic((basic) -> basic.authenticationFailureHandler(authenticationFailureHandler));\n\t\tthis.http.httpBasic((basic) -> basic.authenticationManager(authenticationManager));\n\t\tthis.http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\tgiven(authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.error(() -> new BadCredentialsException(\"bad\")));\n\t\tgiven(authenticationFailureHandler.onAuthenticationFailure(any(), any())).willReturn(Mono.empty());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tclient.get().uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any());\n\t}\n\n\t@Test\n\tpublic void buildWhenServerWebExchangeFromContextThenFound() {\n\t\tSecurityWebFilterChain filter = this.http.build();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClient\n\t\t\t\t.bindToController(new SubscriberContextController())\n\t\t\t\t.webFilter(new WebFilterChainProxy(filter))\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/foo/bar\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectBody(String.class).isEqualTo(\"/foo/bar\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void csrfServerLogoutHandlerNotAppliedIfCsrfIsntEnabled() {\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http.csrf((csrf) -> csrf.disable()).build();\n\t\tassertThat(getWebFilter(securityWebFilterChain, CsrfWebFilter.class)).isNotPresent();\n\t\tOptional<ServerLogoutHandler> logoutHandler = getWebFilter(securityWebFilterChain, LogoutWebFilter.class)\n\t\t\t.map((logoutWebFilter) -> (ServerLogoutHandler) ReflectionTestUtils.getField(logoutWebFilter,\n\t\t\t\t\tLogoutWebFilter.class, \"logoutHandler\"));\n\t\tassertThat(logoutHandler).get().isExactlyInstanceOf(SecurityContextServerLogoutHandler.class);\n\t}\n\n\t@Test\n\tpublic void csrfServerLogoutHandlerAppliedIfCsrfIsEnabled() {\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http\n\t\t\t.csrf((csrf) -> csrf.csrfTokenRepository(this.csrfTokenRepository))\n\t\t\t.build();\n\t\tassertThat(getWebFilter(securityWebFilterChain, CsrfWebFilter.class)).get()\n\t\t\t.extracting((csrfWebFilter) -> ReflectionTestUtils.getField(csrfWebFilter, \"csrfTokenRepository\"))\n\t\t\t.isEqualTo(this.csrfTokenRepository);\n\t\tOptional<ServerLogoutHandler> logoutHandler = getWebFilter(securityWebFilterChain, LogoutWebFilter.class)\n\t\t\t.map((logoutWebFilter) -> (ServerLogoutHandler) ReflectionTestUtils.getField(logoutWebFilter,\n\t\t\t\t\tLogoutWebFilter.class, \"logoutHandler\"));\n\t\tassertThat(logoutHandler).get()\n\t\t\t.isExactlyInstanceOf(DelegatingServerLogoutHandler.class)\n\t\t\t.extracting((delegatingLogoutHandler) -> ((List<ServerLogoutHandler>) ReflectionTestUtils\n\t\t\t\t.getField(delegatingLogoutHandler, DelegatingServerLogoutHandler.class, \"delegates\")).stream()\n\t\t\t\t.map(ServerLogoutHandler::getClass)\n\t\t\t\t.collect(Collectors.toList()))\n\t\t\t.isEqualTo(Arrays.asList(SecurityContextServerLogoutHandler.class, CsrfServerLogoutHandler.class));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void addFilterAfterIsApplied() {\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http\n\t\t\t.addFilterAfter(new TestWebFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)\n\t\t\t.build();\n\t\t// @formatter:off\n\t\tList filters = securityWebFilterChain.getWebFilters()\n\t\t\t\t.map(WebFilter::getClass)\n\t\t\t\t.collectList()\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(filters).isNotNull()\n\t\t\t.isNotEmpty()\n\t\t\t.containsSequence(SecurityContextServerWebExchangeWebFilter.class, TestWebFilter.class);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void addFilterBeforeIsApplied() {\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http\n\t\t\t.addFilterBefore(new TestWebFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)\n\t\t\t.build();\n\t\t// @formatter:off\n\t\tList filters = securityWebFilterChain.getWebFilters()\n\t\t\t\t.map(WebFilter::getClass)\n\t\t\t\t.collectList()\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(filters).isNotNull()\n\t\t\t.isNotEmpty()\n\t\t\t.containsSequence(TestWebFilter.class, SecurityContextServerWebExchangeWebFilter.class);\n\t}\n\n\t@Test\n\tpublic void anonymous() {\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.anonymous(withDefaults())\n\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToControllerAndWebFilters(AnonymousAuthenticationWebFilterTests.HttpMeController.class, securityFilterChain)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/me\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).isEqualTo(\"anonymousUser\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getWhenAnonymousConfiguredThenAuthenticationIsAnonymous() {\n\t\tSecurityWebFilterChain securityFilterChain = this.http.anonymous(withDefaults()).build();\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToControllerAndWebFilters(AnonymousAuthenticationWebFilterTests.HttpMeController.class, securityFilterChain)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/me\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).isEqualTo(\"anonymousUser\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void basicWithAnonymous() {\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"rob\", \"rob\", \"ROLE_USER\", \"ROLE_ADMIN\")));\n\t\tthis.http.httpBasic(withDefaults()).anonymous(withDefaults());\n\t\tthis.http.authenticationManager(this.authenticationManager);\n\t\tthis.http.authorizeExchange((authorize) -> authorize.anyExchange().hasAuthority(\"ROLE_ADMIN\"));\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBasicAuth(\"rob\", \"rob\")\n\t\t\t\t).exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, \".+\")\n\t\t\t\t.expectBody(String.class).consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t\t.returnResult();\n\t\t// @formatter:on\n\t\tassertThat(result.getResponseCookies().getFirst(\"SESSION\")).isNull();\n\t}\n\n\t@Test\n\tpublic void basicWithCustomRealmName() {\n\t\tthis.http.securityContextRepository(new WebSessionServerSecurityContextRepository());\n\t\tHttpBasicServerAuthenticationEntryPoint authenticationEntryPoint = new HttpBasicServerAuthenticationEntryPoint();\n\t\tauthenticationEntryPoint.setRealm(\"myrealm\");\n\t\tthis.http.httpBasic((basic) -> basic.authenticationEntryPoint(authenticationEntryPoint));\n\t\tthis.http.authenticationManager(this.authenticationManager);\n\t\tthis.http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, (value) -> assertThat(value).contains(\"myrealm\"))\n\t\t\t\t.expectBody(String.class)\n\t\t\t\t.returnResult();\n\t\t// @formatter:on\n\t\tassertThat(result.getResponseCookies().getFirst(\"SESSION\")).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenBasicWithRealmNameInLambdaThenRealmNameUsed() {\n\t\tthis.http.securityContextRepository(new WebSessionServerSecurityContextRepository());\n\t\tHttpBasicServerAuthenticationEntryPoint authenticationEntryPoint = new HttpBasicServerAuthenticationEntryPoint();\n\t\tauthenticationEntryPoint.setRealm(\"myrealm\");\n\t\tthis.http.httpBasic((httpBasic) -> httpBasic.authenticationEntryPoint(authenticationEntryPoint));\n\t\tthis.http.authenticationManager(this.authenticationManager);\n\t\tthis.http.authorizeExchange((authorize) -> authorize.anyExchange().authenticated());\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isUnauthorized()\n\t\t\t\t.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, (value) -> assertThat(value).contains(\"myrealm\"))\n\t\t\t\t.expectBody(String.class)\n\t\t\t\t.returnResult();\n\t\t// @formatter:on\n\t\tassertThat(result.getResponseCookies().getFirst(\"SESSION\")).isNull();\n\t}\n\n\t@Test\n\tpublic void basicWithCustomAuthenticationManager() {\n\t\tReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tgiven(customAuthenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"rob\", \"rob\", \"ROLE_USER\", \"ROLE_ADMIN\")));\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.httpBasic((basic) -> basic\n\t\t\t\t.authenticationManager(customAuthenticationManager))\n\t\t\t.build();\n\t\t// @formatter:on\n\t\tWebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain);\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(springSecurityFilterChain)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/\").headers((headers) -> headers\n\t\t\t\t\t\t.setBasicAuth(\"rob\", \"rob\")\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"));\n\t\t// @formatter:on\n\t\tverifyNoMoreInteractions(this.authenticationManager);\n\t}\n\n\t@Test\n\tpublic void requestWhenBasicWithAuthenticationManagerInLambdaThenAuthenticationManagerUsed() {\n\t\tReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tgiven(customAuthenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"rob\", \"rob\", \"ROLE_USER\", \"ROLE_ADMIN\")));\n\t\t// @formatter:off\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t\t.httpBasic((httpBasic) -> httpBasic\n\t\t\t\t\t\t.authenticationManager(customAuthenticationManager)\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tWebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain);\n\t\t// @formatter:off\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t\t.bindToWebFilters(springSecurityFilterChain)\n\t\t\t\t.build();\n\t\tclient.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBasicAuth(\"rob\", \"rob\")\n\t\t\t\t)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"));\n\t\t// @formatter:on\n\t\tverifyNoMoreInteractions(this.authenticationManager);\n\t\tverify(customAuthenticationManager).authenticate(any(Authentication.class));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void addsX509FilterWhenX509AuthenticationIsConfigured() {\n\t\tX509PrincipalExtractor mockExtractor = mock(X509PrincipalExtractor.class);\n\t\tReactiveAuthenticationManager mockAuthenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tthis.http\n\t\t\t.x509((x509) -> x509.principalExtractor(mockExtractor).authenticationManager(mockAuthenticationManager));\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http.build();\n\t\tWebFilter x509WebFilter = securityWebFilterChain.getWebFilters().filter(this::isX509Filter).blockFirst();\n\t\tassertThat(x509WebFilter).isNotNull();\n\t}\n\n\t@Test\n\tpublic void x509WhenCustomizedThenAddsX509Filter() {\n\t\tX509PrincipalExtractor mockExtractor = mock(X509PrincipalExtractor.class);\n\t\tReactiveAuthenticationManager mockAuthenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tthis.http\n\t\t\t.x509((x509) -> x509.principalExtractor(mockExtractor).authenticationManager(mockAuthenticationManager));\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http.build();\n\t\tWebFilter x509WebFilter = securityWebFilterChain.getWebFilters().filter(this::isX509Filter).blockFirst();\n\t\tassertThat(x509WebFilter).isNotNull();\n\t}\n\n\t@Test\n\tpublic void x509WithConverterAndNoExtractorThenAddsX509Filter() {\n\t\tServerAuthenticationConverter mockConverter = mock(ServerAuthenticationConverter.class);\n\t\tthis.http.x509((x509) -> x509.serverAuthenticationConverter(mockConverter));\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http.build();\n\t\tWebFilter x509WebFilter = securityWebFilterChain.getWebFilters()\n\t\t\t.filter((filter) -> matchesX509Converter(filter, mockConverter))\n\t\t\t.blockFirst();\n\t\tassertThat(x509WebFilter).isNotNull();\n\t}\n\n\t@Test\n\tpublic void addsX509FilterWhenX509AuthenticationIsConfiguredWithDefaults() {\n\t\tthis.http.x509(withDefaults());\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http.build();\n\t\tWebFilter x509WebFilter = securityWebFilterChain.getWebFilters().filter(this::isX509Filter).blockFirst();\n\t\tassertThat(x509WebFilter).isNotNull();\n\t}\n\n\t@Test\n\tpublic void x509WhenDefaultsThenAddsX509Filter() {\n\t\tthis.http.x509(withDefaults());\n\t\tSecurityWebFilterChain securityWebFilterChain = this.http.build();\n\t\tWebFilter x509WebFilter = securityWebFilterChain.getWebFilters().filter(this::isX509Filter).blockFirst();\n\t\tassertThat(x509WebFilter).isNotNull();\n\t}\n\n\t@Test\n\tpublic void postWhenCsrfDisabledThenPermitted() {\n\t\tSecurityWebFilterChain securityFilterChain = this.http.csrf((csrf) -> csrf.disable()).build();\n\t\tWebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();\n\t\tclient.post().uri(\"/\").exchange().expectStatus().isOk();\n\t}\n\n\t@Test\n\tpublic void postWhenCustomCsrfTokenRepositoryThenUsed() {\n\t\tServerCsrfTokenRepository customServerCsrfTokenRepository = mock(ServerCsrfTokenRepository.class);\n\t\tgiven(customServerCsrfTokenRepository.loadToken(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.csrf((csrf) -> csrf.csrfTokenRepository(customServerCsrfTokenRepository))\n\t\t\t.build();\n\t\tWebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();\n\t\tclient.post().uri(\"/\").exchange().expectStatus().isForbidden();\n\t\tverify(customServerCsrfTokenRepository).loadToken(any());\n\t}\n\n\t@Test\n\tpublic void postWhenCustomRequestHandlerThenUsed() {\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"headerName\", \"paramName\", \"tokenValue\");\n\t\tgiven(this.csrfTokenRepository.loadToken(any(ServerWebExchange.class))).willReturn(Mono.just(csrfToken));\n\t\tgiven(this.csrfTokenRepository.generateToken(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tServerCsrfTokenRequestHandler requestHandler = mock(ServerCsrfTokenRequestHandler.class);\n\t\tgiven(requestHandler.resolveCsrfTokenValue(any(ServerWebExchange.class), any(CsrfToken.class)))\n\t\t\t.willReturn(Mono.just(csrfToken.getToken()));\n\t\t// @formatter:off\n\t\tthis.http.csrf((csrf) -> csrf\n\t\t\t.csrfTokenRepository(this.csrfTokenRepository)\n\t\t\t.csrfTokenRequestHandler(requestHandler)\n\t\t);\n\t\t// @formatter:on\n\t\tWebTestClient client = buildClient();\n\t\tclient.post().uri(\"/\").exchange().expectStatus().isOk();\n\t\tverify(this.csrfTokenRepository, times(2)).loadToken(any(ServerWebExchange.class));\n\t\tverify(this.csrfTokenRepository).generateToken(any(ServerWebExchange.class));\n\t\tverify(requestHandler).handle(any(ServerWebExchange.class), any());\n\t\tverify(requestHandler).resolveCsrfTokenValue(any(ServerWebExchange.class), any());\n\t}\n\n\t@Test\n\tpublic void postWhenServerXorCsrfTokenRequestAttributeHandlerThenOk() {\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\");\n\t\tgiven(this.csrfTokenRepository.loadToken(any(ServerWebExchange.class))).willReturn(Mono.just(csrfToken));\n\t\tgiven(this.csrfTokenRepository.generateToken(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tServerCsrfTokenRequestHandler requestHandler = new XorServerCsrfTokenRequestAttributeHandler();\n\t\t// @formatter:off\n\t\tthis.http.csrf((csrf) -> csrf\n\t\t\t.csrfTokenRepository(this.csrfTokenRepository)\n\t\t\t.csrfTokenRequestHandler(requestHandler)\n\t\t);\n\t\t// @formatter:on\n\n\t\t// Generate masked CSRF token value\n\t\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\trequestHandler.handle(exchange, Mono.just(csrfToken));\n\t\tMono<CsrfToken> csrfTokenAttribute = exchange.getAttribute(CsrfToken.class.getName());\n\t\tString actualTokenValue = csrfTokenAttribute.map(CsrfToken::getToken).block();\n\t\tassertThat(actualTokenValue).isNotEqualTo(csrfToken.getToken());\n\n\t\tWebTestClient client = buildClient();\n\t\t// @formatter:off\n\t\tclient.post()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.header(csrfToken.getHeaderName(), actualTokenValue)\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t\tverify(this.csrfTokenRepository, times(2)).loadToken(any(ServerWebExchange.class));\n\t\tverify(this.csrfTokenRepository).generateToken(any(ServerWebExchange.class));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void shouldConfigureRequestCacheForOAuth2LoginAuthenticationEntryPointAndSuccessHandler() {\n\t\tServerRequestCache requestCache = spy(new WebSessionServerRequestCache());\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tReactiveClientRegistrationRepository.class);\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.oauth2Login((login) -> login.clientRegistrationRepository(clientRegistrationRepository))\n\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t.requestCache((c) -> c.requestCache(requestCache))\n\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();\n\t\tclient.get().uri(\"/test\").exchange();\n\t\tArgumentCaptor<ServerWebExchange> captor = ArgumentCaptor.forClass(ServerWebExchange.class);\n\t\tverify(requestCache).saveRequest(captor.capture());\n\t\tassertThat(captor.getValue().getRequest().getURI().toString()).isEqualTo(\"/test\");\n\t\tOAuth2LoginAuthenticationWebFilter authenticationWebFilter = getWebFilter(securityFilterChain,\n\t\t\t\tOAuth2LoginAuthenticationWebFilter.class)\n\t\t\t.get();\n\t\tDelegatingServerAuthenticationSuccessHandler handler = (DelegatingServerAuthenticationSuccessHandler) ReflectionTestUtils\n\t\t\t.getField(authenticationWebFilter, \"authenticationSuccessHandler\");\n\t\tList<ServerAuthenticationSuccessHandler> delegates = (List<ServerAuthenticationSuccessHandler>) ReflectionTestUtils\n\t\t\t.getField(handler, \"delegates\");\n\t\tassertThat(ReflectionTestUtils.getField(delegates.get(0), \"requestCache\")).isSameAs(requestCache);\n\t}\n\n\t@Test\n\tpublic void shouldConfigureAuthorizationRequestRepositoryForOAuth2Login() {\n\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = mock(\n\t\t\t\tServerAuthorizationRequestRepository.class);\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tReactiveClientRegistrationRepository.class);\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request().build();\n\t\tgiven(authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(authorizationRequest));\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.oauth2Login((login) -> login.clientRegistrationRepository(clientRegistrationRepository)\n\t\t\t\t.authorizationRequestRepository(authorizationRequestRepository))\n\t\t\t.build();\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();\n\t\tclient.get().uri(\"/login/oauth2/code/registration-id\").exchange();\n\t\tverify(authorizationRequestRepository).removeAuthorizationRequest(any());\n\t}\n\n\t@Test\n\tpublic void shouldUseDefaultAuthorizationRedirectStrategyForOAuth2Login() {\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tReactiveClientRegistrationRepository.class);\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(anyString()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));\n\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.oauth2Login((login) -> login.clientRegistrationRepository(clientRegistrationRepository))\n\t\t\t.build();\n\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();\n\t\tclient.get().uri(\"/oauth2/authorization/registration-id\").exchange().expectStatus().is3xxRedirection();\n\n\t\tOAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,\n\t\t\t\tOAuth2AuthorizationRequestRedirectWebFilter.class)\n\t\t\t.get();\n\t\tassertThat(ReflectionTestUtils.getField(filter, \"authorizationRedirectStrategy\"))\n\t\t\t.isInstanceOf(DefaultServerRedirectStrategy.class);\n\t}\n\n\t@Test\n\tpublic void shouldConfigureAuthorizationRedirectStrategyForOAuth2Login() {\n\t\tServerRedirectStrategy authorizationRedirectStrategy = mock(ServerRedirectStrategy.class);\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tReactiveClientRegistrationRepository.class);\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(anyString()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));\n\t\tgiven(authorizationRedirectStrategy.sendRedirect(any(), any())).willReturn(Mono.empty());\n\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.oauth2Login((login) -> login.clientRegistrationRepository(clientRegistrationRepository)\n\t\t\t\t.authorizationRedirectStrategy(authorizationRedirectStrategy))\n\t\t\t.build();\n\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();\n\t\tclient.get().uri(\"/oauth2/authorization/registration-id\").exchange();\n\t\tverify(authorizationRedirectStrategy).sendRedirect(any(), any());\n\n\t\tOAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,\n\t\t\t\tOAuth2AuthorizationRequestRedirectWebFilter.class)\n\t\t\t.get();\n\t\tassertThat(ReflectionTestUtils.getField(filter, \"authorizationRedirectStrategy\"))\n\t\t\t.isSameAs(authorizationRedirectStrategy);\n\t}\n\n\t@Test\n\tpublic void shouldUseDefaultAuthorizationRedirectStrategyForOAuth2Client() {\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tReactiveClientRegistrationRepository.class);\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(anyString()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));\n\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.oauth2Client((client) -> client.clientRegistrationRepository(clientRegistrationRepository))\n\t\t\t.build();\n\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();\n\t\tclient.get().uri(\"/oauth2/authorization/registration-id\").exchange().expectStatus().is3xxRedirection();\n\n\t\tOAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,\n\t\t\t\tOAuth2AuthorizationRequestRedirectWebFilter.class)\n\t\t\t.get();\n\t\tassertThat(ReflectionTestUtils.getField(filter, \"authorizationRedirectStrategy\"))\n\t\t\t.isInstanceOf(DefaultServerRedirectStrategy.class);\n\t}\n\n\t@Test\n\tpublic void shouldConfigureAuthorizationRedirectStrategyForOAuth2Client() {\n\t\tServerRedirectStrategy authorizationRedirectStrategy = mock(ServerRedirectStrategy.class);\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository = mock(\n\t\t\t\tReactiveClientRegistrationRepository.class);\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(anyString()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().build()));\n\t\tgiven(authorizationRedirectStrategy.sendRedirect(any(), any())).willReturn(Mono.empty());\n\n\t\tSecurityWebFilterChain securityFilterChain = this.http\n\t\t\t.oauth2Client((client) -> client.clientRegistrationRepository(clientRegistrationRepository)\n\t\t\t\t.authorizationRedirectStrategy(authorizationRedirectStrategy))\n\t\t\t.build();\n\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(securityFilterChain).build();\n\t\tclient.get().uri(\"/oauth2/authorization/registration-id\").exchange();\n\t\tverify(authorizationRedirectStrategy).sendRedirect(any(), any());\n\n\t\tOAuth2AuthorizationRequestRedirectWebFilter filter = getWebFilter(securityFilterChain,\n\t\t\t\tOAuth2AuthorizationRequestRedirectWebFilter.class)\n\t\t\t.get();\n\t\tassertThat(ReflectionTestUtils.getField(filter, \"authorizationRedirectStrategy\"))\n\t\t\t.isSameAs(authorizationRedirectStrategy);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid resourcesWhenLoginPageConfiguredThenServesCss() {\n\t\tthis.http.formLogin(withDefaults());\n\t\tthis.http.authenticationManager(this.authenticationManager);\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t.bindToControllerAndWebFilters(NotFoundController.class, this.http.build())\n\t\t\t.build();\n\n\t\tclient.get()\n\t\t\t.uri(\"/default-ui.css\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.value(Matchers.containsString(\"body {\"));\n\t}\n\n\t@Test\n\tvoid resourcesWhenLoginPageNotConfiguredThenDoesNotServeCss() {\n\t\tthis.http.httpBasic(withDefaults());\n\t\tthis.http.authenticationManager(this.authenticationManager);\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t.bindToControllerAndWebFilters(NotFoundController.class, this.http.build())\n\t\t\t.build();\n\n\t\tclient.get()\n\t\t\t.uri(\"/default-ui.css\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isNotFound()\n\t\t\t.expectBody(String.class)\n\t\t\t.isEqualTo(null);\n\t}\n\n\tprivate boolean isX509Filter(WebFilter filter) {\n\t\ttry {\n\t\t\tObject converter = ReflectionTestUtils.getField(filter, \"authenticationConverter\");\n\t\t\treturn converter.getClass().isAssignableFrom(ServerX509AuthenticationConverter.class);\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\t// field doesn't exist\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate boolean matchesX509Converter(WebFilter filter, ServerAuthenticationConverter expectedConverter) {\n\t\ttry {\n\t\t\tObject converter = ReflectionTestUtils.getField(filter, \"authenticationConverter\");\n\t\t\treturn converter.equals(expectedConverter);\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\t// field doesn't exist\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate <T extends WebFilter> Optional<T> getWebFilter(SecurityWebFilterChain filterChain, Class<T> filterClass) {\n\t\treturn (Optional<T>) filterChain.getWebFilters()\n\t\t\t.filter(Objects::nonNull)\n\t\t\t.filter((filter) -> filterClass.isAssignableFrom(filter.getClass()))\n\t\t\t.singleOrEmpty()\n\t\t\t.blockOptional();\n\t}\n\n\tprivate WebTestClient buildClient() {\n\t\tWebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(this.http.build());\n\t\treturn WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();\n\t}\n\n\t@RestController\n\tprivate static class SubscriberContextController {\n\n\t\t@GetMapping(\"/**\")\n\t\tMono<String> pathWithinApplicationFromContext() {\n\t\t\treturn Mono.deferContextual(Mono::just)\n\t\t\t\t.filter((c) -> c.hasKey(ServerWebExchange.class))\n\t\t\t\t.map((c) -> c.get(ServerWebExchange.class))\n\t\t\t\t.map((e) -> e.getRequest().getPath().pathWithinApplication().value());\n\t\t}\n\n\t}\n\n\t@RestController\n\tprivate static class NotFoundController {\n\n\t\t// Empty controller, makes WebTestClient return HTTP 404\n\n\t}\n\n\tprivate static class TestWebFilter implements WebFilter {\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn chain.filter(exchange);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/SessionManagementSpecTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;\nimport org.springframework.security.core.session.InMemoryReactiveSessionRegistry;\nimport org.springframework.security.core.session.ReactiveSessionRegistry;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.authentication.InvalidateLeastUsedServerMaximumSessionsExceededHandler;\nimport org.springframework.security.web.server.authentication.PreventLoginServerMaximumSessionsExceededHandler;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.SessionLimit;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.config.EnableWebFlux;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.server.adapter.WebHttpHandlerBuilder;\nimport org.springframework.web.server.session.DefaultWebSessionManager;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;\n\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\npublic class SessionManagementSpecTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tWebTestClient client;\n\n\t@Autowired\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tthis.client = WebTestClient.bindToApplicationContext(context).build();\n\t}\n\n\t@Test\n\tvoid loginWhenMaxSessionPreventsLoginThenSecondLoginFails() {\n\t\tthis.spring.register(ConcurrentSessionsMaxSessionPreventsLoginConfig.class).autowire();\n\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\n\t\tResponseCookie firstLoginSessionCookie = loginReturningCookie(data);\n\n\t\t// second login should fail\n\t\tResponseCookie secondLoginSessionCookie = this.client.mutateWith(csrf())\n\t\t\t.post()\n\t\t\t.uri(\"/login\")\n\t\t\t.contentType(MediaType.MULTIPART_FORM_DATA)\n\t\t\t.body(BodyInserters.fromFormData(data))\n\t\t\t.exchange()\n\t\t\t.expectHeader()\n\t\t\t.location(\"/login?error\")\n\t\t\t.returnResult(Void.class)\n\t\t\t.getResponseCookies()\n\t\t\t.getFirst(\"SESSION\");\n\n\t\tassertThat(secondLoginSessionCookie).isNull();\n\n\t\t// first login should still be valid\n\t\tthis.client.mutateWith(csrf())\n\t\t\t.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(firstLoginSessionCookie.getName(), firstLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t}\n\n\t@Test\n\tvoid httpBasicWhenUsingSavingAuthenticationInWebSessionAndPreventLoginThenSecondRequestFails() {\n\t\tthis.spring.register(ConcurrentSessionsHttpBasicWithWebSessionMaxSessionPreventsLoginConfig.class).autowire();\n\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\n\t\t// first request be successful\n\t\tResponseCookie sessionCookie = this.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectCookie()\n\t\t\t.exists(\"SESSION\")\n\t\t\t.returnResult(Void.class)\n\t\t\t.getResponseCookies()\n\t\t\t.getFirst(\"SESSION\");\n\n\t\t// request with no session should fail\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isUnauthorized();\n\n\t\t// request with session obtained from first request should be successful\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"user\", \"password\"))\n\t\t\t.cookie(sessionCookie.getName(), sessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t}\n\n\t@Test\n\tvoid loginWhenMaxSessionPerAuthenticationThenUserLoginFailsAndAdminLoginSucceeds() {\n\t\tConcurrentSessionsMaxSessionPreventsLoginConfig.sessionLimit = (authentication) -> {\n\t\t\tif (authentication.getName().equals(\"admin\")) {\n\t\t\t\treturn Mono.empty();\n\t\t\t}\n\t\t\treturn Mono.just(1);\n\t\t};\n\t\tthis.spring.register(ConcurrentSessionsMaxSessionPreventsLoginConfig.class).autowire();\n\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\t\tMultiValueMap<String, String> adminCreds = new LinkedMultiValueMap<>();\n\t\tadminCreds.add(\"username\", \"admin\");\n\t\tadminCreds.add(\"password\", \"password\");\n\n\t\tResponseCookie userFirstLoginSessionCookie = loginReturningCookie(data);\n\t\tResponseCookie adminFirstLoginSessionCookie = loginReturningCookie(adminCreds);\n\t\t// second user login should fail\n\t\tthis.client.mutateWith(csrf())\n\t\t\t.post()\n\t\t\t.uri(\"/login\")\n\t\t\t.contentType(MediaType.MULTIPART_FORM_DATA)\n\t\t\t.body(BodyInserters.fromFormData(data))\n\t\t\t.exchange()\n\t\t\t.expectHeader()\n\t\t\t.location(\"/login?error\");\n\t\t// first login should still be valid\n\t\tthis.client.mutateWith(csrf())\n\t\t\t.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(userFirstLoginSessionCookie.getName(), userFirstLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tResponseCookie adminSecondLoginSessionCookie = loginReturningCookie(adminCreds);\n\t\tthis.client.mutateWith(csrf())\n\t\t\t.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(adminFirstLoginSessionCookie.getName(), adminFirstLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.client.mutateWith(csrf())\n\t\t\t.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(adminSecondLoginSessionCookie.getName(), adminSecondLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t}\n\n\t@Test\n\tvoid loginWhenMaxSessionDoesNotPreventLoginThenSecondLoginSucceedsAndFirstSessionIsInvalidated() {\n\t\tConcurrentSessionsMaxSessionPreventsLoginFalseConfig.sessionLimit = SessionLimit.of(1);\n\t\tthis.spring.register(ConcurrentSessionsMaxSessionPreventsLoginFalseConfig.class).autowire();\n\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\n\t\tResponseCookie firstLoginSessionCookie = loginReturningCookie(data);\n\t\tResponseCookie secondLoginSessionCookie = loginReturningCookie(data);\n\n\t\t// first login should not be valid\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(firstLoginSessionCookie.getName(), firstLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.expectHeader()\n\t\t\t.location(\"/login\");\n\n\t\t// second login should be valid\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(secondLoginSessionCookie.getName(), secondLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t}\n\n\t@Test\n\tvoid loginWhenMaxSessionDoesNotPreventLoginThenLeastRecentlyUsedSessionIsInvalidated() {\n\t\tConcurrentSessionsMaxSessionPreventsLoginFalseConfig.sessionLimit = SessionLimit.of(2);\n\t\tthis.spring.register(ConcurrentSessionsMaxSessionPreventsLoginFalseConfig.class).autowire();\n\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\n\t\tResponseCookie firstLoginSessionCookie = loginReturningCookie(data);\n\t\tResponseCookie secondLoginSessionCookie = loginReturningCookie(data);\n\n\t\t// update last access time for first request\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(firstLoginSessionCookie.getName(), firstLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\n\t\tResponseCookie thirdLoginSessionCookie = loginReturningCookie(data);\n\n\t\t// second login should be invalid, it is the least recently used session\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(secondLoginSessionCookie.getName(), secondLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.expectHeader()\n\t\t\t.location(\"/login\");\n\n\t\t// first login should be valid\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(firstLoginSessionCookie.getName(), firstLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\n\t\t// third login should be valid\n\t\tthis.client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.cookie(thirdLoginSessionCookie.getName(), thirdLoginSessionCookie.getValue())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t}\n\n\t@Test\n\tvoid oauth2LoginWhenMaxSessionsThenPreventLogin() {\n\t\tOAuth2LoginConcurrentSessionsConfig.maxSessions = 1;\n\t\tOAuth2LoginConcurrentSessionsConfig.preventLogin = true;\n\t\tthis.spring.register(OAuth2LoginConcurrentSessionsConfig.class).autowire();\n\t\tprepareOAuth2Config();\n\t\t// @formatter:off\n\t\tResponseCookie sessionCookie = this.client.get()\n\t\t\t\t.uri(\"/login/oauth2/code/client-credentials\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/\")\n\t\t\t\t.expectCookie().exists(\"SESSION\")\n\t\t\t\t.returnResult(Void.class)\n\t\t\t\t.getResponseCookies()\n\t\t\t\t.getFirst(\"SESSION\");\n\n\t\tthis.client.get()\n\t\t\t\t.uri(\"/login/oauth2/code/client-credentials\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectHeader().location(\"/login?error\");\n\n\t\tthis.client.get().uri(\"/\")\n\t\t\t\t.cookie(sessionCookie.getName(), sessionCookie.getValue())\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).isEqualTo(\"ok\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid oauth2LoginWhenMaxSessionDoesNotPreventLoginThenSecondLoginSucceedsAndFirstSessionIsInvalidated() {\n\t\tOAuth2LoginConcurrentSessionsConfig.maxSessions = 1;\n\t\tOAuth2LoginConcurrentSessionsConfig.preventLogin = false;\n\t\tthis.spring.register(OAuth2LoginConcurrentSessionsConfig.class).autowire();\n\t\tprepareOAuth2Config();\n\t\t// @formatter:off\n\t\tResponseCookie firstLoginCookie = this.client.get()\n\t\t\t\t.uri(\"/login/oauth2/code/client-credentials\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/\")\n\t\t\t\t.expectCookie().exists(\"SESSION\")\n\t\t\t\t.returnResult(Void.class)\n\t\t\t\t.getResponseCookies()\n\t\t\t\t.getFirst(\"SESSION\");\n\t\tResponseCookie secondLoginCookie = this.client.get()\n\t\t\t\t.uri(\"/login/oauth2/code/client-credentials\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.expectHeader().valueEquals(\"Location\", \"/\")\n\t\t\t\t.expectCookie().exists(\"SESSION\")\n\t\t\t\t.returnResult(Void.class)\n\t\t\t\t.getResponseCookies()\n\t\t\t\t.getFirst(\"SESSION\");\n\n\t\tthis.client.get().uri(\"/\")\n\t\t\t\t.cookie(firstLoginCookie.getName(), firstLoginCookie.getValue())\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isFound()\n\t\t\t\t.expectHeader().location(\"/login\");\n\n\t\tthis.client.get().uri(\"/\")\n\t\t\t\t.cookie(secondLoginCookie.getName(), secondLoginCookie.getValue())\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk()\n\t\t\t\t.expectBody(String.class).isEqualTo(\"ok\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid loginWhenAuthenticationSuccessHandlerOverriddenThenConcurrentSessionHandlersBackOff() {\n\t\tthis.spring.register(ConcurrentSessionsFormLoginOverrideAuthenticationSuccessHandlerConfig.class).autowire();\n\t\tMultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\t\tdata.add(\"username\", \"user\");\n\t\tdata.add(\"password\", \"password\");\n\t\t// first login should be successful\n\t\tlogin(data).expectStatus().isFound().expectHeader().location(\"/\");\n\t\t// second login should be successful, there should be no concurrent session\n\t\t// control\n\t\tlogin(data).expectStatus().isFound().expectHeader().location(\"/\");\n\t}\n\n\tprivate void prepareOAuth2Config() {\n\t\tOAuth2LoginConcurrentSessionsConfig config = this.spring.getContext()\n\t\t\t.getBean(OAuth2LoginConcurrentSessionsConfig.class);\n\t\tServerAuthenticationConverter converter = config.authenticationConverter;\n\t\tReactiveAuthenticationManager manager = config.manager;\n\t\tServerOAuth2AuthorizationRequestResolver resolver = config.resolver;\n\t\tOAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success();\n\t\tOAuth2User user = TestOAuth2Users.create();\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tOAuth2LoginAuthenticationToken result = new OAuth2LoginAuthenticationToken(\n\t\t\t\tTestClientRegistrations.clientRegistration().build(), exchange, user, user.getAuthorities(),\n\t\t\t\taccessToken);\n\t\tgiven(converter.convert(any())).willReturn(Mono.just(new TestingAuthenticationToken(\"a\", \"b\", \"c\")));\n\t\tgiven(manager.authenticate(any())).willReturn(Mono.just(result));\n\t\tgiven(resolver.resolve(any())).willReturn(Mono.empty());\n\t}\n\n\tprivate ResponseCookie loginReturningCookie(MultiValueMap<String, String> data) {\n\t\treturn login(data).expectCookie()\n\t\t\t.exists(\"SESSION\")\n\t\t\t.returnResult(Void.class)\n\t\t\t.getResponseCookies()\n\t\t\t.getFirst(\"SESSION\");\n\t}\n\n\tprivate WebTestClient.ResponseSpec login(MultiValueMap<String, String> data) {\n\t\treturn this.client.mutateWith(csrf())\n\t\t\t.post()\n\t\t\t.uri(\"/login\")\n\t\t\t.contentType(MediaType.MULTIPART_FORM_DATA)\n\t\t\t.body(BodyInserters.fromFormData(data))\n\t\t\t.exchange();\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(Config.class)\n\tstatic class ConcurrentSessionsMaxSessionPreventsLoginConfig {\n\n\t\tstatic SessionLimit sessionLimit = SessionLimit.of(1);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t.concurrentSessions((concurrentSessions) -> concurrentSessions\n\t\t\t\t\t\t.maximumSessions(sessionLimit)\n\t\t\t\t\t\t.maximumSessionsExceededHandler(new PreventLoginServerMaximumSessionsExceededHandler())\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(Config.class)\n\tstatic class OAuth2LoginConcurrentSessionsConfig {\n\n\t\tstatic int maxSessions = 1;\n\n\t\tstatic boolean preventLogin = true;\n\n\t\tReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);\n\n\t\tServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);\n\n\t\tServerOAuth2AuthorizationRequestResolver resolver = mock(ServerOAuth2AuthorizationRequestResolver.class);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http,\n\t\t\t\tDefaultWebSessionManager webSessionManager) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t\t\t.anyExchange().authenticated()\n\t\t\t\t\t)\n\t\t\t\t\t.oauth2Login((oauth2Login) -> oauth2Login\n\t\t\t\t\t\t.authenticationConverter(this.authenticationConverter)\n\t\t\t\t\t\t.authenticationManager(this.manager)\n\t\t\t\t\t\t.authorizationRequestResolver(this.resolver)\n\t\t\t\t\t)\n\t\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t\t.concurrentSessions((concurrentSessions) -> concurrentSessions\n\t\t\t\t\t\t\t\t.maximumSessions(SessionLimit.of(maxSessions))\n\t\t\t\t\t\t\t\t.maximumSessionsExceededHandler(preventLogin\n\t\t\t\t\t\t\t\t\t\t? new PreventLoginServerMaximumSessionsExceededHandler()\n\t\t\t\t\t\t\t\t\t\t: new InvalidateLeastUsedServerMaximumSessionsExceededHandler(webSessionManager.getSessionStore()))\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tInMemoryReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn new InMemoryReactiveClientRegistrationRepository(\n\t\t\t\t\tTestClientRegistrations.clientCredentials().build());\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(Config.class)\n\tstatic class ConcurrentSessionsMaxSessionPreventsLoginFalseConfig {\n\n\t\tstatic SessionLimit sessionLimit = SessionLimit.of(1);\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t.concurrentSessions((concurrentSessions) -> concurrentSessions\n\t\t\t\t\t\t.maximumSessions(sessionLimit)\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(Config.class)\n\tstatic class ConcurrentSessionsFormLoginOverrideAuthenticationSuccessHandlerConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t\t.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler(\"/\"))\n\t\t\t\t)\n\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t.concurrentSessions((concurrentSessions) -> concurrentSessions\n\t\t\t\t\t\t.maximumSessions(SessionLimit.of(1))\n\t\t\t\t\t\t.maximumSessionsExceededHandler(new PreventLoginServerMaximumSessionsExceededHandler())\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebFlux\n\t@EnableWebFluxSecurity\n\t@Import(Config.class)\n\tstatic class ConcurrentSessionsHttpBasicWithWebSessionMaxSessionPreventsLoginConfig {\n\n\t\t@Bean\n\t\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t\t.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n\t\t\t\t\t.httpBasic((basic) -> basic\n\t\t\t\t\t\t\t.securityContextRepository(new WebSessionServerSecurityContextRepository())\n\t\t\t\t\t)\n\t\t\t\t\t.sessionManagement((sessionManagement) -> sessionManagement\n\t\t\t\t\t\t\t.concurrentSessions((concurrentSessions) -> concurrentSessions\n\t\t\t\t\t\t\t\t\t.maximumSessions(SessionLimit.of(1))\n\t\t\t\t\t\t\t\t\t.maximumSessionsExceededHandler(new PreventLoginServerMaximumSessionsExceededHandler())\n\t\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@Import({ ReactiveAuthenticationTestConfiguration.class, DefaultController.class })\n\tstatic class Config {\n\n\t\t@Bean(WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME)\n\t\tDefaultWebSessionManager webSessionManager() {\n\t\t\treturn new DefaultWebSessionManager();\n\t\t}\n\n\t\t@Bean\n\t\tReactiveSessionRegistry reactiveSessionRegistry() {\n\t\t\treturn new InMemoryReactiveSessionRegistry();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class DefaultController {\n\n\t\t@GetMapping(\"/\")\n\t\tString index() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/web/server/TestingServerHttpSecurity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class TestingServerHttpSecurity extends ServerHttpSecurity {\n\n\tpublic TestingServerHttpSecurity applicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tsuper.setApplicationContext(applicationContext);\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/websocket/MessageSecurityPostProcessorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.websocket;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.GenericBeanDefinition;\nimport org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;\n\npublic class MessageSecurityPostProcessorTests {\n\n\tprivate WebSocketMessageBrokerSecurityBeanDefinitionParser.MessageSecurityPostProcessor postProcessor = new WebSocketMessageBrokerSecurityBeanDefinitionParser.MessageSecurityPostProcessor(\n\t\t\t\"id\", false);\n\n\t@Test\n\tpublic void handlesBeansWithoutClass() {\n\t\tBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();\n\t\tregistry.registerBeanDefinition(\"beanWithoutClass\", new GenericBeanDefinition());\n\t\tthis.postProcessor.postProcessBeanDefinitionRegistry(registry);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.websocket;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.assertj.core.api.ThrowableAssert;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.parsing.BeanDefinitionParsingException;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.task.SyncTaskExecutor;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.support.ChannelInterceptor;\nimport org.springframework.messaging.support.ExecutorSubscribableChannel;\nimport org.springframework.messaging.support.GenericMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.expression.SecurityExpressionOperations;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler;\nimport org.springframework.security.messaging.access.expression.MessageSecurityExpressionRoot;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\nimport org.springframework.security.web.csrf.InvalidCsrfTokenException;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.server.HandshakeFailureException;\nimport org.springframework.web.socket.server.HandshakeHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@SecurityTestExecutionListeners\npublic class WebSocketMessageBrokerConfigTests {\n\n\tprivate static final String CONFIG_LOCATION_PREFIX = \"classpath:org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests\";\n\n\t/*\n\t * Token format: \"token\" length random pad bytes + \"token\" (each byte UTF8 ^= 1).\n\t */\n\tprivate static final byte[] XOR_CSRF_TOKEN_BYTES = new byte[] { 1, 1, 1, 1, 1, 117, 110, 106, 100, 111 };\n\n\tprivate static final String XOR_CSRF_TOKEN_VALUE = Base64.getEncoder().encodeToString(XOR_CSRF_TOKEN_BYTES);\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired(required = false)\n\tprivate MessageChannel clientInboundChannel;\n\n\t@Autowired(required = false)\n\tprivate MessageController messageController;\n\n\t@Autowired(required = false)\n\tprivate MessageWithArgumentController messageWithArgumentController;\n\n\t@Autowired(required = false)\n\tprivate TestHandshakeHandler testHandshakeHandler;\n\n\tprivate CsrfToken token = new DefaultCsrfToken(\"header\", \"param\", \"token\");\n\n\t@Test\n\tpublic void sendWhenNoIdSpecifiedThenIntegratesWithClientInboundChannel() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tthis.clientInboundChannel.send(message(\"/permitAll\"));\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(() -> this.clientInboundChannel.send(message(\"/denyAll\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithConnectMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\theaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tthis.clientInboundChannel.send(message(\"/permitAll\", headers));\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithConnectAckMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.CONNECT_ACK);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithDisconnectMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.DISCONNECT);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithDisconnectAckMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.DISCONNECT_ACK);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithHeartbeatMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.HEARTBEAT);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithMessageMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.MESSAGE);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithOtherMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.OTHER);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithSubscribeMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.SUBSCRIBE);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithUnsubscribeMessageTypeThenPermitted() {\n\t\tthis.spring.configLocations(xml(\"NoIdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.UNSUBSCRIBE);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenNoIdSpecifiedThenIntegratesWithAuthorizationManager() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tthis.clientInboundChannel.send(message(\"/permitAll\"));\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(() -> this.clientInboundChannel.send(message(\"/denyAll\")))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithConnectMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\theaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tthis.clientInboundChannel.send(message(\"/permitAll\", headers));\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithConnectAckMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.CONNECT_ACK);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithDisconnectMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.DISCONNECT);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithDisconnectAckMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.DISCONNECT_ACK);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithHeartbeatMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.HEARTBEAT);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithMessageMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.MESSAGE);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithOtherMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.OTHER);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithSubscribeMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.SUBSCRIBE);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithUnsubscribeMessageTypeThenAuthorizationManagerPermits() {\n\t\tthis.spring.configLocations(xml(\"NoIdAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.UNSUBSCRIBE);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenAnonymousMessageWithCustomSecurityContextHolderStrategyAndAuthorizationManagerThenUses() {\n\t\tthis.spring.configLocations(xml(\"WithSecurityContextHolderStrategy\")).autowire();\n\t\tSecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);\n\t\tMessage<?> message = message(\"/authenticated\", SimpMessageType.CONNECT);\n\t\tsend(message);\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void sendWhenConnectWithoutCsrfTokenThenDenied() {\n\t\tthis.spring.configLocations(xml(\"SyncConfig\")).autowire();\n\t\tMessage<?> message = message(\"/message\", SimpMessageType.CONNECT);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(InvalidCsrfTokenException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenConnectWithSameOriginDisabledThenCsrfTokenNotRequired() {\n\t\tthis.spring.configLocations(xml(\"SyncSameOriginDisabledConfig\")).autowire();\n\t\tMessage<?> message = message(\"/message\", SimpMessageType.CONNECT);\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenInterceptWiredForMessageTypeThenDeniesOnTypeMismatch() {\n\t\tthis.spring.configLocations(xml(\"MessageInterceptTypeConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.MESSAGE);\n\t\tsend(message);\n\t\tmessage = message(\"/permitAll\", SimpMessageType.UNSUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tmessage = message(\"/anyOther\", SimpMessageType.MESSAGE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenInterceptWiredForMessageTypeThenAuthorizationManagerDeniesOnTypeMismatch() {\n\t\tthis.spring.configLocations(xml(\"MessageInterceptTypeAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.MESSAGE);\n\t\tsend(message);\n\t\tmessage = message(\"/permitAll\", SimpMessageType.UNSUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tmessage = message(\"/anyOther\", SimpMessageType.MESSAGE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenInterceptWiredForSubscribeTypeThenDeniesOnTypeMismatch() {\n\t\tthis.spring.configLocations(xml(\"SubscribeInterceptTypeConfig\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.SUBSCRIBE);\n\t\tsend(message);\n\t\tmessage = message(\"/permitAll\", SimpMessageType.UNSUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tmessage = message(\"/anyOther\", SimpMessageType.SUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenInterceptWiredForSubscribeTypeThenAuthorizationManagerDeniesOnTypeMismatch() {\n\t\tthis.spring.configLocations(xml(\"SubscribeInterceptTypeAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.SUBSCRIBE);\n\t\tsend(message);\n\t\tmessage = message(\"/permitAll\", SimpMessageType.UNSUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tmessage = message(\"/anyOther\", SimpMessageType.SUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenPathPatternFactoryBeanThenConstructsPatternsWithPathPattern() {\n\t\tthis.spring.configLocations(xml(\"SubscribeInterceptTypePathPattern\")).autowire();\n\t\tMessage<?> message = message(\"/permitAll\", SimpMessageType.SUBSCRIBE);\n\t\tsend(message);\n\t\tmessage = message(\"/permitAll\", SimpMessageType.UNSUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tmessage = message(\"/anyOther\", SimpMessageType.SUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenCaseInsensitivePathPatternParserThenMatchesMixedCase() {\n\t\tthis.spring.configLocations(xml(\"SubscribeInterceptTypePathPatternParser\")).autowire();\n\t\tMessage<?> message = message(\"/peRmItAll\", SimpMessageType.SUBSCRIBE);\n\t\tsend(message);\n\t\tmessage = message(\"/peRmKtAll\", SimpMessageType.UNSUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tmessage = message(\"/aNyOtHer\", SimpMessageType.SUBSCRIBE);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingConnectMessageTypeThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"ConnectInterceptTypeConfig\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingConnectAckMessageTypeThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"ConnectAckInterceptTypeConfig\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDisconnectMessageTypeThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"DisconnectInterceptTypeConfig\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingDisconnectAckMessageTypeThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"DisconnectAckInterceptTypeConfig\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingHeartbeatMessageTypeThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"HeartbeatInterceptTypeConfig\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingOtherMessageTypeThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"OtherInterceptTypeConfig\")).autowire());\n\t}\n\n\t@Test\n\tpublic void configureWhenUsingUnsubscribeMessageTypeThenAutowireFails() {\n\t\tassertThatExceptionOfType(BeanDefinitionParsingException.class)\n\t\t\t.isThrownBy(() -> this.spring.configLocations(xml(\"UnsubscribeInterceptTypeConfig\")).autowire());\n\t}\n\n\t@Test\n\tpublic void sendWhenNoIdMessageThenAuthenticationPrincipalResolved() {\n\t\tthis.spring.configLocations(xml(\"SyncConfig\")).autowire();\n\t\tthis.clientInboundChannel.send(message(\"/message\"));\n\t\tassertThat(this.messageController.username).isEqualTo(\"anonymous\");\n\t}\n\n\t@Test\n\tpublic void sendMessageWhenMetaAnnotationThenAuthenticationPrincipalResolved() {\n\t\tthis.spring.configLocations(xml(\"SyncConfig\")).autowire();\n\t\tAuthentication harold = new TestingAuthenticationToken(\"harold\", \"password\", \"ROLE_USER\");\n\t\ttry {\n\t\t\tgetSecurityContextHolderStrategy().setContext(new SecurityContextImpl(harold));\n\t\t\tthis.clientInboundChannel.send(message(\"/hi\"));\n\t\t\tassertThat(this.spring.getContext().getBean(MessageController.class).message).isEqualTo(\"Hi, Harold!\");\n\t\t\tAuthentication user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\t\tgetSecurityContextHolderStrategy().setContext(new SecurityContextImpl(user));\n\t\t\tthis.clientInboundChannel.send(message(\"/hi\"));\n\t\t\tassertThat(this.spring.getContext().getBean(MessageController.class).message).isEqualTo(\"Hi, Stranger!\");\n\t\t}\n\t\tfinally {\n\t\t\tgetSecurityContextHolderStrategy().clearContext();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void requestWhenConnectMessageThenUsesCsrfTokenHandshakeInterceptor() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SyncConfig\")).autowire();\n\t\tWebApplicationContext context = this.spring.getContext();\n\t\tMockMvc mvc = MockMvcBuilders.webAppContextSetup(context).build();\n\t\tString csrfAttributeName = CsrfToken.class.getName();\n\t\tString customAttributeName = this.getClass().getName();\n\t\tMvcResult result = mvc\n\t\t\t.perform(get(\"/app\").requestAttr(DeferredCsrfToken.class.getName(), new TestDeferredCsrfToken(this.token))\n\t\t\t\t.sessionAttr(customAttributeName, \"attributeValue\"))\n\t\t\t.andReturn();\n\t\tCsrfToken handshakeToken = (CsrfToken) this.testHandshakeHandler.attributes.get(csrfAttributeName);\n\t\tString handshakeValue = (String) this.testHandshakeHandler.attributes.get(customAttributeName);\n\t\tString sessionValue = (String) result.getRequest().getSession().getAttribute(customAttributeName);\n\t\tassertThatCsrfToken(handshakeToken).isEqualTo(this.token).withFailMessage(\"CsrfToken is populated\");\n\t\tassertThat(handshakeValue).isEqualTo(sessionValue)\n\t\t\t.withFailMessage(\"Explicitly listed session variables are not overridden\");\n\t}\n\n\t@Test\n\tpublic void requestWhenConnectMessageAndUsingSockJsThenUsesCsrfTokenHandshakeInterceptor() throws Exception {\n\t\tthis.spring.configLocations(xml(\"SyncSockJsConfig\")).autowire();\n\t\tWebApplicationContext context = this.spring.getContext();\n\t\tMockMvc mvc = MockMvcBuilders.webAppContextSetup(context).build();\n\t\tString csrfAttributeName = CsrfToken.class.getName();\n\t\tString customAttributeName = this.getClass().getName();\n\t\tMvcResult result = mvc\n\t\t\t.perform(get(\"/app/289/tpyx6mde/websocket\")\n\t\t\t\t.requestAttr(DeferredCsrfToken.class.getName(), new TestDeferredCsrfToken(this.token))\n\t\t\t\t.sessionAttr(customAttributeName, \"attributeValue\"))\n\t\t\t.andReturn();\n\t\tCsrfToken handshakeToken = (CsrfToken) this.testHandshakeHandler.attributes.get(csrfAttributeName);\n\t\tString handshakeValue = (String) this.testHandshakeHandler.attributes.get(customAttributeName);\n\t\tString sessionValue = (String) result.getRequest().getSession().getAttribute(customAttributeName);\n\t\tassertThatCsrfToken(handshakeToken).isEqualTo(this.token).withFailMessage(\"CsrfToken is populated\");\n\t\tassertThat(handshakeValue).isEqualTo(sessionValue)\n\t\t\t.withFailMessage(\"Explicitly listed session variables are not overridden\");\n\t}\n\n\t@Test\n\tpublic void sendWhenNoIdSpecifiedThenCustomArgumentResolversAreNotOverridden() {\n\t\tthis.spring.configLocations(xml(\"SyncCustomArgumentResolverConfig\")).autowire();\n\t\tthis.clientInboundChannel.send(message(\"/message-with-argument\"));\n\t\tassertThat(this.messageWithArgumentController.messageArgument).isNotNull();\n\t}\n\n\t@Test\n\tpublic void sendWhenUsingCustomPathMatcherThenSecurityAppliesIt() {\n\t\tthis.spring.configLocations(xml(\"CustomPathMatcherConfig\")).autowire();\n\t\tMessage<?> message = message(\"/denyAll.a\");\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tmessage = message(\"/denyAll.a.b\");\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenUsingCustomPathMatcherThenAuthorizationManagerAppliesIt() {\n\t\tthis.spring.configLocations(xml(\"CustomPathMatcherAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/denyAll.a\");\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tmessage = message(\"/denyAll.a.b\");\n\t\tsend(message);\n\t}\n\n\t@Test\n\tpublic void sendWhenIdSpecifiedThenSecurityDoesNotIntegrateWithClientInboundChannel() {\n\t\tthis.spring.configLocations(xml(\"IdConfig\")).autowire();\n\t\tMessage<?> message = message(\"/denyAll\");\n\t\tsend(message);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void sendWhenIdSpecifiedAndExplicitlyIntegratedWhenBrokerUsesClientInboundChannel() {\n\t\tthis.spring.configLocations(xml(\"IdIntegratedConfig\")).autowire();\n\t\tMessage<?> message = message(\"/denyAll\");\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenNoIdSpecifiedThenSecurityDoesntOverrideCustomInterceptors() {\n\t\tthis.spring.configLocations(xml(\"CustomInterceptorConfig\")).autowire();\n\t\tMessage<?> message = message(\"/throwAll\");\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(UnsupportedOperationException.class);\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"nile\")\n\tpublic void sendWhenCustomExpressionHandlerThenAuthorizesAccordingly() {\n\t\tthis.spring.configLocations(xml(\"CustomExpressionHandlerConfig\")).autowire();\n\t\tMessage<?> message = message(\"/denyNile\");\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"nile\")\n\tpublic void sendWhenCustomExpressionHandlerThenAuthorizationManagerAuthorizesAccordingly() {\n\t\tthis.spring.configLocations(xml(\"CustomExpressionHandlerAuthorizationManager\")).autowire();\n\t\tMessage<?> message = message(\"/denyNile\");\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void sendWhenCustomAuthorizationManagerThenAuthorizesAccordingly() {\n\t\tthis.spring.configLocations(xml(\"CustomAuthorizationManagerConfig\")).autowire();\n\t\tAuthorizationManager<Message<?>> authorizationManager = this.spring.getContext()\n\t\t\t.getBean(AuthorizationManager.class);\n\t\tgiven(authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(false));\n\t\tMessage<?> message = message(\"/any\");\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(send(message))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tverify(authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void configureWhenCsrfChannelInterceptorBeanThenUses() {\n\t\tthis.spring.configLocations(xml(\"CustomCsrfInterceptor\")).autowire();\n\t\tExecutorSubscribableChannel channel = this.spring.getContext()\n\t\t\t.getBean(\"clientInboundChannel\", ExecutorSubscribableChannel.class);\n\t\tChannelInterceptor interceptor = this.spring.getContext()\n\t\t\t.getBean(\"csrfChannelInterceptor\", ChannelInterceptor.class);\n\t\tassertThat(channel.getInterceptors()).contains(interceptor);\n\t}\n\n\tprivate String xml(String configName) {\n\t\treturn CONFIG_LOCATION_PREFIX + \"-\" + configName + \".xml\";\n\t}\n\n\tprivate ThrowableAssert.ThrowingCallable send(Message<?> message) {\n\t\treturn () -> this.clientInboundChannel.send(message);\n\t}\n\n\tprivate Message<?> message(String destination) {\n\t\treturn message(destination, SimpMessageType.MESSAGE);\n\t}\n\n\tprivate Message<?> message(String destination, SimpMessageType type) {\n\t\tSimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type);\n\t\treturn message(destination, headers);\n\t}\n\n\tprivate Message<?> message(String destination, SimpMessageHeaderAccessor headers) {\n\t\theaders.setSessionId(\"123\");\n\t\theaders.setSessionAttributes(new HashMap<>());\n\t\theaders.setDestination(destination);\n\t\tSecurityContextHolderStrategy strategy = getSecurityContextHolderStrategy();\n\t\tif (strategy.getContext().getAuthentication() != null) {\n\t\t\theaders.setUser(strategy.getContext().getAuthentication());\n\t\t}\n\t\theaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\treturn new GenericMessage<>(\"hi\", headers.getMessageHeaders());\n\t}\n\n\tprivate SecurityContextHolderStrategy getSecurityContextHolderStrategy() {\n\t\tString[] names = this.spring.getContext().getBeanNamesForType(SecurityContextHolderStrategy.class);\n\t\tif (names.length == 1) {\n\t\t\treturn this.spring.getContext().getBean(names[0], SecurityContextHolderStrategy.class);\n\t\t}\n\t\treturn SecurityContextHolder.getContextHolderStrategy();\n\t}\n\n\tprivate static final class TestDeferredCsrfToken implements DeferredCsrfToken {\n\n\t\tprivate final CsrfToken csrfToken;\n\n\t\tTestDeferredCsrfToken(CsrfToken csrfToken) {\n\t\t\tthis.csrfToken = csrfToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic CsrfToken get() {\n\t\t\treturn this.csrfToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGenerated() {\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@AuthenticationPrincipal(expression = \"#this.equals('{value}')\")\n\t@interface IsUser {\n\n\t\tString value() default \"user\";\n\n\t}\n\n\t@Controller\n\tstatic class MessageController {\n\n\t\tString username;\n\n\t\tString message;\n\n\t\t@MessageMapping(\"/message\")\n\t\tvoid authentication(@AuthenticationPrincipal String username) {\n\t\t\tthis.username = username;\n\t\t}\n\n\t\t@MessageMapping(\"/hi\")\n\t\tvoid sayHello(@IsUser(\"harold\") boolean isHarold) {\n\t\t\tthis.message = isHarold ? \"Hi, Harold!\" : \"Hi, Stranger!\";\n\t\t}\n\n\t}\n\n\t@Controller\n\tstatic class MessageWithArgumentController {\n\n\t\tMessageArgument messageArgument;\n\n\t\t@MessageMapping(\"/message-with-argument\")\n\t\tvoid myCustom(MessageArgument messageArgument) {\n\t\t\tthis.messageArgument = messageArgument;\n\t\t}\n\n\t}\n\n\tstatic class MessageArgument {\n\n\t\tMessageArgument(String notDefaultConstructor) {\n\t\t}\n\n\t}\n\n\tstatic class MessageArgumentResolver implements HandlerMethodArgumentResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\t\treturn parameter.getParameterType().isAssignableFrom(MessageArgument.class);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveArgument(MethodParameter parameter, Message<?> message) {\n\t\t\treturn new MessageArgument(\"\");\n\t\t}\n\n\t}\n\n\tstatic class TestHandshakeHandler implements HandshakeHandler {\n\n\t\tMap<String, Object> attributes;\n\n\t\t@Override\n\t\tpublic boolean doHandshake(ServerHttpRequest request,\n\t\t\t\torg.springframework.http.server.ServerHttpResponse response, WebSocketHandler wsHandler,\n\t\t\t\tMap<String, Object> attributes) throws HandshakeFailureException {\n\t\t\tthis.attributes = attributes;\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\tstatic class InboundExecutorPostProcessor implements BeanDefinitionRegistryPostProcessor {\n\n\t\t@Override\n\t\tpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {\n\t\t\tBeanDefinition inbound = registry.getBeanDefinition(\"clientInboundChannel\");\n\t\t\tinbound.getConstructorArgumentValues()\n\t\t\t\t.addIndexedArgumentValue(0, new RootBeanDefinition(SyncTaskExecutor.class));\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n\t\t}\n\n\t}\n\n\tstatic class ExceptingInterceptor implements ChannelInterceptor {\n\n\t\t@Override\n\t\tpublic Message<?> preSend(Message<?> message, MessageChannel channel) {\n\t\t\tthrow new UnsupportedOperationException(\"no\");\n\t\t}\n\n\t}\n\n\tstatic class DenyNileMessageSecurityExpressionHandler extends DefaultMessageSecurityExpressionHandler<Object> {\n\n\t\t@Override\n\t\tprotected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,\n\t\t\t\tMessage<Object> invocation) {\n\t\t\treturn new MessageSecurityExpressionRoot(authentication, invocation) {\n\t\t\t\tpublic boolean denyNile() {\n\t\t\t\t\tAuthentication auth = getAuthentication();\n\t\t\t\t\treturn auth != null && !\"nile\".equals(auth.getName());\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\t\tMessage<Object> message) {\n\t\t\treturn new StandardEvaluationContext(new MessageSecurityExpressionRoot(authentication, message) {\n\t\t\t\tpublic boolean denyNile() {\n\t\t\t\t\tAuthentication auth = getAuthentication();\n\t\t\t\t\treturn auth != null && !\"nile\".equals(auth.getName());\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/htmlunit/server/HtmlUnitWebTestClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.htmlunit.server;\n\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.StringTokenizer;\n\nimport org.htmlunit.FormEncodingType;\nimport org.htmlunit.WebClient;\nimport org.htmlunit.WebRequest;\nimport org.htmlunit.util.Cookie;\nimport org.htmlunit.util.NameValuePair;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.lang.Nullable;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\n\nfinal class HtmlUnitWebTestClient {\n\n\tprivate final WebClient webClient;\n\n\tprivate final WebTestClient webTestClient;\n\n\tHtmlUnitWebTestClient(WebClient webClient, WebTestClient webTestClient) {\n\t\tAssert.notNull(webClient, \"WebClient must not be null\");\n\t\tAssert.notNull(webTestClient, \"WebTestClient must not be null\");\n\t\tthis.webClient = webClient;\n\t\t// @formatter:off\n\t\tthis.webTestClient = webTestClient.mutate()\n\t\t\t\t.filter(new FollowRedirects())\n\t\t\t\t.filter(new CookieManager())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tFluxExchangeResult<String> getResponse(WebRequest webRequest) {\n\t\t// @formatter:off\n\t\tWebTestClient.RequestBodySpec request = this.webTestClient\n\t\t\t\t.method(httpMethod(webRequest))\n\t\t\t\t.uri(uri(webRequest));\n\t\t// @formatter:on\n\t\tcontentType(request, webRequest);\n\t\tcookies(request, webRequest);\n\t\theaders(request, webRequest);\n\t\treturn content(request, webRequest).exchange().returnResult(String.class);\n\t}\n\n\tprivate WebTestClient.RequestHeadersSpec<?> content(WebTestClient.RequestBodySpec request, WebRequest webRequest) {\n\t\tString requestBody = webRequest.getRequestBody();\n\t\tif (requestBody == null) {\n\t\t\tList<NameValuePair> params = webRequest.getRequestParameters();\n\t\t\tif (params != null && !params.isEmpty()) {\n\t\t\t\treturn request.body(BodyInserters.fromFormData(formData(params)));\n\t\t\t}\n\t\t\treturn request;\n\t\t}\n\t\treturn request.body(BodyInserters.fromProducer(requestBody, String.class));\n\t}\n\n\tprivate MultiValueMap<String, String> formData(List<NameValuePair> params) {\n\t\tMultiValueMap<String, String> result = new LinkedMultiValueMap<>(params.size());\n\t\tparams.forEach((pair) -> result.add(pair.getName(), pair.getValue()));\n\t\treturn result;\n\t}\n\n\tprivate void contentType(WebTestClient.RequestBodySpec request, WebRequest webRequest) {\n\t\tString contentType = header(\"Content-Type\", webRequest);\n\t\tif (contentType == null) {\n\t\t\tFormEncodingType encodingType = webRequest.getEncodingType();\n\t\t\tif (encodingType != null) {\n\t\t\t\tcontentType = encodingType.getName();\n\t\t\t}\n\t\t}\n\t\tMediaType mediaType = (contentType != null) ? MediaType.parseMediaType(contentType) : MediaType.ALL;\n\t\trequest.contentType(mediaType);\n\t}\n\n\tprivate void cookies(WebTestClient.RequestBodySpec request, WebRequest webRequest) {\n\t\tString cookieHeaderValue = header(\"Cookie\", webRequest);\n\t\tif (cookieHeaderValue != null) {\n\t\t\tStringTokenizer tokens = new StringTokenizer(cookieHeaderValue, \"=;\");\n\t\t\twhile (tokens.hasMoreTokens()) {\n\t\t\t\tString cookieName = tokens.nextToken().trim();\n\t\t\t\tAssert.isTrue(tokens.hasMoreTokens(), () -> \"Expected value for cookie name '\" + cookieName\n\t\t\t\t\t\t+ \"': full cookie header was [\" + cookieHeaderValue + \"]\");\n\t\t\t\tString cookieValue = tokens.nextToken().trim();\n\t\t\t\trequest.cookie(cookieName, cookieValue);\n\t\t\t}\n\t\t}\n\t\tSet<Cookie> managedCookies = this.webClient.getCookies(webRequest.getUrl());\n\t\tfor (Cookie cookie : managedCookies) {\n\t\t\trequest.cookie(cookie.getName(), cookie.getValue());\n\t\t}\n\t}\n\n\t@Nullable\n\tprivate String header(String headerName, WebRequest webRequest) {\n\t\treturn webRequest.getAdditionalHeaders().get(headerName);\n\t}\n\n\tprivate void headers(WebTestClient.RequestBodySpec request, WebRequest webRequest) {\n\t\twebRequest.getAdditionalHeaders().forEach((name, value) -> request.header(name, value));\n\t}\n\n\tprivate HttpMethod httpMethod(WebRequest webRequest) {\n\t\tString httpMethod = webRequest.getHttpMethod().name();\n\t\treturn HttpMethod.valueOf(httpMethod);\n\t}\n\n\tprivate URI uri(WebRequest webRequest) {\n\t\tURL url = webRequest.getUrl();\n\t\treturn URI.create(url.toExternalForm());\n\t}\n\n\tstatic class FollowRedirects implements ExchangeFilterFunction {\n\n\t\t@Override\n\t\tpublic Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {\n\t\t\treturn next.exchange(request).flatMap((response) -> redirectIfNecessary(request, next, response));\n\t\t}\n\n\t\tprivate Mono<ClientResponse> redirectIfNecessary(ClientRequest request, ExchangeFunction next,\n\t\t\t\tClientResponse response) {\n\t\t\tURI location = response.headers().asHttpHeaders().getLocation();\n\t\t\tString host = request.url().getHost();\n\t\t\tString scheme = request.url().getScheme();\n\t\t\tif (location != null) {\n\t\t\t\tString redirectUrl = location.toASCIIString();\n\t\t\t\tif (location.getHost() == null) {\n\t\t\t\t\tredirectUrl = scheme + \"://\" + host + location.toASCIIString();\n\t\t\t\t}\n\t\t\t\t// @formatter:off\n\t\t\t\tClientRequest redirect = ClientRequest.create(HttpMethod.GET, URI.create(redirectUrl))\n\t\t\t\t\t\t.headers((headers) -> headers.addAll(request.headers()))\n\t\t\t\t\t\t.cookies((cookies) -> cookies.addAll(request.cookies()))\n\t\t\t\t\t\t.attributes((attributes) -> attributes.putAll(request.attributes()))\n\t\t\t\t\t\t.build();\n\t\t\t\t// @formatter:on\n\t\t\t\treturn next.exchange(redirect).flatMap((r) -> redirectIfNecessary(request, next, r));\n\t\t\t}\n\t\t\treturn Mono.just(response);\n\t\t}\n\n\t}\n\n\tstatic class CookieManager implements ExchangeFilterFunction {\n\n\t\tprivate Map<String, ResponseCookie> cookies = new HashMap<>();\n\n\t\t@Override\n\t\tpublic Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {\n\t\t\treturn next.exchange(withClientCookies(request)).doOnSuccess((response) -> {\n\t\t\t\tresponse.cookies().values().forEach((cookies) -> {\n\t\t\t\t\tcookies.forEach((cookie) -> {\n\t\t\t\t\t\tif (cookie.getMaxAge().isZero()) {\n\t\t\t\t\t\t\tthis.cookies.remove(cookie.getName());\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tthis.cookies.put(cookie.getName(), cookie);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tprivate ClientRequest withClientCookies(ClientRequest request) {\n\t\t\treturn ClientRequest.from(request).cookies((c) -> c.addAll(clientCookies())).build();\n\t\t}\n\n\t\tprivate MultiValueMap<String, String> clientCookies() {\n\t\t\tMultiValueMap<String, String> result = new LinkedMultiValueMap<>(this.cookies.size());\n\t\t\tthis.cookies.values().forEach((cookie) -> result.add(cookie.getName(), cookie.getValue()));\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/htmlunit/server/MockWebResponseBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.htmlunit.server;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.htmlunit.WebRequest;\nimport org.htmlunit.WebResponse;\nimport org.htmlunit.WebResponseData;\nimport org.htmlunit.util.NameValuePair;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.util.Assert;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\nfinal class MockWebResponseBuilder {\n\n\tprivate final long startTime;\n\n\tprivate final WebRequest webRequest;\n\n\tprivate final FluxExchangeResult<String> exchangeResult;\n\n\tMockWebResponseBuilder(long startTime, WebRequest webRequest, FluxExchangeResult<String> exchangeResult) {\n\t\tAssert.notNull(webRequest, \"WebRequest must not be null\");\n\t\tAssert.notNull(exchangeResult, \"FluxExchangeResult must not be null\");\n\t\tthis.startTime = startTime;\n\t\tthis.webRequest = webRequest;\n\t\tthis.exchangeResult = exchangeResult;\n\t}\n\n\tWebResponse build() throws IOException {\n\t\tWebResponseData webResponseData = webResponseData();\n\t\tlong endTime = System.currentTimeMillis();\n\t\treturn new WebResponse(webResponseData, this.webRequest, endTime - this.startTime);\n\t}\n\n\tprivate WebResponseData webResponseData() {\n\t\tList<NameValuePair> responseHeaders = responseHeaders();\n\t\tHttpStatus status = HttpStatus.resolve(this.exchangeResult.getStatus().value());\n\t\treturn new WebResponseData(this.exchangeResult.getResponseBodyContent(), status.value(),\n\t\t\t\tstatus.getReasonPhrase(), responseHeaders);\n\t}\n\n\tprivate List<NameValuePair> responseHeaders() {\n\t\tHttpHeaders responseHeaders = this.exchangeResult.getResponseHeaders();\n\t\tList<NameValuePair> result = new ArrayList<>(responseHeaders.size());\n\t\tresponseHeaders.forEach((headerName, headerValues) -> headerValues\n\t\t\t.forEach((headerValue) -> result.add(new NameValuePair(headerName, headerValue))));\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/htmlunit/server/WebTestClientHtmlUnitDriverBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.htmlunit.server;\n\nimport org.htmlunit.WebClient;\nimport org.htmlunit.WebConnection;\nimport org.openqa.selenium.WebDriver;\n\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.test.web.servlet.htmlunit.DelegatingWebConnection;\nimport org.springframework.test.web.servlet.htmlunit.DelegatingWebConnection.DelegateWebConnection;\nimport org.springframework.test.web.servlet.htmlunit.HostRequestMatcher;\nimport org.springframework.test.web.servlet.htmlunit.webdriver.WebConnectionHtmlUnitDriver;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic final class WebTestClientHtmlUnitDriverBuilder {\n\n\tprivate final WebTestClient webTestClient;\n\n\tprivate WebTestClientHtmlUnitDriverBuilder(WebTestClient webTestClient) {\n\t\tthis.webTestClient = webTestClient;\n\t}\n\n\tpublic WebDriver build() {\n\t\tWebConnectionHtmlUnitDriver driver = new WebConnectionHtmlUnitDriver();\n\t\tWebClient webClient = driver.getWebClient();\n\t\tWebTestClientWebConnection webClientConnection = new WebTestClientWebConnection(this.webTestClient, webClient);\n\t\tWebConnection connection = new DelegatingWebConnection(driver.getWebConnection(),\n\t\t\t\tnew DelegateWebConnection(new HostRequestMatcher(\"localhost\"), webClientConnection));\n\t\tdriver.setWebConnection(connection);\n\t\treturn driver;\n\t}\n\n\tpublic static WebTestClientHtmlUnitDriverBuilder webTestClientSetup(WebTestClient webTestClient) {\n\t\treturn new WebTestClientHtmlUnitDriverBuilder(webTestClient);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/htmlunit/server/WebTestClientHtmlUnitDriverBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.htmlunit.server;\n\nimport java.net.URI;\nimport java.time.Duration;\n\nimport org.junit.jupiter.api.Test;\nimport org.openqa.selenium.WebDriver;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.web.util.TextEscapeUtils;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.CookieValue;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebTestClientHtmlUnitDriverBuilderTests {\n\n\t@Test\n\tpublic void helloWorld() {\n\t\tWebTestClient webTestClient = WebTestClient.bindToController(new HelloWorldController()).build();\n\t\t// @formatter:off\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tdriver.get(\"http://localhost/\");\n\t\tassertThat(driver.getPageSource()).contains(\"Hello World\");\n\t}\n\n\t@Test\n\tpublic void cookies() {\n\t\t// @formatter:off\n\t\tWebTestClient webTestClient = WebTestClient\n\t\t\t\t.bindToController(new CookieController())\n\t\t\t\t.build();\n\t\tWebDriver driver = WebTestClientHtmlUnitDriverBuilder\n\t\t\t\t.webTestClientSetup(webTestClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tdriver.get(\"http://localhost/cookie\");\n\t\tassertThat(driver.getPageSource()).contains(\"theCookie\");\n\t\tdriver.get(\"http://localhost/cookie/delete\");\n\t\tassertThat(driver.getPageSource()).contains(\"null\");\n\t}\n\n\t@Controller\n\tclass HelloWorldController {\n\n\t\t@ResponseBody\n\t\t@GetMapping(path = \"/\", produces = MediaType.TEXT_HTML_VALUE)\n\t\tString index() {\n\t\t\t// @formatter:off\n\t\t\treturn \"<html>\\n\"\n\t\t\t\t+ \"<head>\\n\"\n\t\t\t\t+ \"<title>Hello World</title>\\n\"\n\t\t\t\t+ \"</head>\\n\"\n\t\t\t\t+ \"<body>\\n\"\n\t\t\t\t+ \"<h1>Hello World</h1>\\n\"\n\t\t\t\t+ \"</body>\\n\"\n\t\t\t\t+ \"</html>\";\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Controller\n\t@ResponseBody\n\tclass CookieController {\n\n\t\t@GetMapping(path = \"/\", produces = MediaType.TEXT_HTML_VALUE)\n\t\tString view(@CookieValue(required = false) String cookieName) {\n\t\t\t// @formatter:off\n\t\t\treturn \"<html>\\n\"\n\t\t\t\t+ \"<head>\\n\"\n\t\t\t\t+ \"<title>Hello World</title>\\n\"\n\t\t\t\t+ \"</head>\\n\"\n\t\t\t\t+ \"<body>\\n\"\n\t\t\t\t+ \"<h1>\"\n\t\t\t\t+ TextEscapeUtils.escapeEntities(cookieName)\n\t\t\t\t+ \"</h1>\\n\"\n\t\t\t\t+ \"</body>\\n\"\n\t\t\t\t+ \"</html>\";\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@GetMapping(\"/cookie\")\n\t\tMono<Void> setCookie(ServerHttpResponse response) {\n\t\t\tresponse.addCookie(ResponseCookie.from(\"cookieName\", \"theCookie\").build());\n\t\t\treturn redirect(response);\n\t\t}\n\n\t\tprivate Mono<Void> redirect(ServerHttpResponse response) {\n\t\t\tresponse.setStatusCode(HttpStatus.MOVED_PERMANENTLY);\n\t\t\tresponse.getHeaders().setLocation(URI.create(\"/\"));\n\t\t\treturn response.setComplete();\n\t\t}\n\n\t\t@GetMapping(\"/cookie/delete\")\n\t\tMono<Void> deleteCookie(ServerHttpResponse response) {\n\t\t\tresponse.addCookie(ResponseCookie.from(\"cookieName\", \"\").maxAge(Duration.ofSeconds(0)).build());\n\t\t\treturn redirect(response);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/htmlunit/server/WebTestClientWebConnection.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.htmlunit.server;\n\nimport java.io.IOException;\n\nimport org.htmlunit.WebClient;\nimport org.htmlunit.WebConnection;\nimport org.htmlunit.WebRequest;\nimport org.htmlunit.WebResponse;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.util.Assert;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebTestClientWebConnection implements WebConnection {\n\n\tprivate final WebTestClient webTestClient;\n\n\tprivate final String contextPath;\n\n\tprivate final HtmlUnitWebTestClient requestBuilder;\n\n\tprivate WebClient webClient;\n\n\tpublic WebTestClientWebConnection(WebTestClient webTestClient, WebClient webClient) {\n\t\tthis(webTestClient, webClient, \"\");\n\t}\n\n\tpublic WebTestClientWebConnection(WebTestClient webTestClient, WebClient webClient, String contextPath) {\n\t\tAssert.notNull(webTestClient, \"MockMvc must not be null\");\n\t\tAssert.notNull(webClient, \"WebClient must not be null\");\n\t\tvalidateContextPath(contextPath);\n\t\tthis.webClient = webClient;\n\t\tthis.webTestClient = webTestClient;\n\t\tthis.contextPath = contextPath;\n\t\tthis.requestBuilder = new HtmlUnitWebTestClient(this.webClient, this.webTestClient);\n\t}\n\n\t/**\n\t * Validate the supplied {@code contextPath}.\n\t * <p>\n\t * If the value is not {@code null}, it must conform to\n\t * {@link jakarta.servlet.http.HttpServletRequest#getContextPath()} which states that\n\t * it can be an empty string and otherwise must start with a \"/\" character and not end\n\t * with a \"/\" character.\n\t * @param contextPath the path to validate\n\t */\n\tstatic void validateContextPath(@Nullable String contextPath) {\n\t\tif (contextPath == null || \"\".equals(contextPath)) {\n\t\t\treturn;\n\t\t}\n\t\tAssert.isTrue(contextPath.startsWith(\"/\"), () -> \"contextPath '\" + contextPath + \"' must start with '/'.\");\n\t\tAssert.isTrue(!contextPath.endsWith(\"/\"), () -> \"contextPath '\" + contextPath + \"' must not end with '/'.\");\n\t}\n\n\tpublic void setWebClient(WebClient webClient) {\n\t\tAssert.notNull(webClient, \"WebClient must not be null\");\n\t\tthis.webClient = webClient;\n\t}\n\n\t@Override\n\tpublic WebResponse getResponse(WebRequest webRequest) throws IOException {\n\t\tlong startTime = System.currentTimeMillis();\n\t\tFluxExchangeResult<String> exchangeResult = this.requestBuilder.getResponse(webRequest);\n\t\twebRequest.setUrl(exchangeResult.getUrl().toURL());\n\t\treturn new MockWebResponseBuilder(startTime, webRequest, exchangeResult).build();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/intercept/method/aopalliance/MethodSecurityInterceptorWithAopConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.intercept.method.aopalliance;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.AbstractXmlApplicationContext;\nimport org.springframework.security.ITargetObject;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.config.util.InMemoryXmlApplicationContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for SEC-428 (and SEC-1204).\n *\n * @author Luke Taylor\n * @author Ben Alex\n */\npublic class MethodSecurityInterceptorWithAopConfigTests {\n\n\t// @formatter:off\n\tstatic final String AUTH_PROVIDER_XML = \"<authentication-manager>\"\n\t\t\t+ \"    <authentication-provider>\"\n\t\t\t+ \"        <user-service>\"\n\t\t\t+ \"            <user name='bob' password='bobspassword' authorities='ROLE_USER,ROLE_ADMIN' />\"\n\t\t\t+ \"            <user name='bill' password='billspassword' authorities='ROLE_USER' />\"\n\t\t\t+ \"        </user-service>\"\n\t\t\t+ \"    </authentication-provider>\"\n\t\t\t+ \"</authentication-manager>\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tstatic final String ACCESS_MANAGER_XML = \"<b:bean id='accessDecisionManager' class='org.springframework.security.access.vote.AffirmativeBased'>\"\n\t\t\t+ \"   <b:constructor-arg>\"\n\t\t\t+ \"       <b:list><b:bean class='org.springframework.security.access.vote.RoleVoter'/></b:list>\"\n\t\t\t+ \"   </b:constructor-arg>\"\n\t\t\t+ \"</b:bean>\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tstatic final String TARGET_BEAN_AND_INTERCEPTOR = \"<b:bean id='target' class='org.springframework.security.TargetObject'/>\"\n\t\t\t+ \"<b:bean id='securityInterceptor' class='org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor' autowire='byType' >\"\n\t\t\t+ \"     <b:property name='securityMetadataSource'>\"\n\t\t\t+ \"         <method-security-metadata-source>\"\n\t\t\t+ \"             <protect method='org.springframework.security.ITargetObject.makeLower*' access='ROLE_A'/>\"\n\t\t\t+ \"             <protect method='org.springframework.security.ITargetObject.makeUpper*' access='ROLE_A'/>\"\n\t\t\t+ \"             <protect method='org.springframework.security.ITargetObject.computeHashCode*' access='ROLE_B'/>\"\n\t\t\t+ \"         </method-security-metadata-source>\"\n\t\t\t+ \"     </b:property>\"\n\t\t\t+ \"</b:bean>\";\n\t// @formatter:on\n\n\tprivate AbstractXmlApplicationContext appContext;\n\n\t@BeforeEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tSecurityContextHolder.clearContext();\n\t\tif (this.appContext != null) {\n\t\t\tthis.appContext.close();\n\t\t\tthis.appContext = null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void securityInterceptorIsAppliedWhenUsedWithAopConfig() {\n\t\t// @formatter:off\n\t\tsetContext(\"<aop:config>\"\n\t\t\t\t+ \"     <aop:pointcut id='targetMethods' expression='execution(* org.springframework.security.TargetObject.*(..))'/>\"\n\t\t\t\t+ \"     <aop:advisor advice-ref='securityInterceptor' pointcut-ref='targetMethods' />\"\n\t\t\t\t+ \"</aop:config>\"\n\t\t\t\t+ TARGET_BEAN_AND_INTERCEPTOR\n\t\t\t\t+ AUTH_PROVIDER_XML\n\t\t\t\t+ ACCESS_MANAGER_XML);\n\t\t// @formatter:on\n\t\tITargetObject target = (ITargetObject) this.appContext.getBean(\"target\");\n\t\t// Check both against interface and class\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> target.makeLowerCase(\"TEST\"));\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> target.makeUpperCase(\"test\"));\n\t}\n\n\t@Test\n\tpublic void securityInterceptorIsAppliedWhenUsedWithBeanNameAutoProxyCreator() {\n\t\t// @formatter:off\n\t\tsetContext(\"<b:bean id='autoProxyCreator' class='org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator'>\"\n\t\t\t\t+ \"   <b:property name='interceptorNames'>\"\n\t\t\t\t+ \"       <b:list>\"\n\t\t\t\t+ \"          <b:value>securityInterceptor</b:value>\"\n\t\t\t\t+ \"       </b:list>\"\n\t\t\t\t+ \"   </b:property>\"\n\t\t\t\t+ \"   <b:property name='beanNames'>\"\n\t\t\t\t+ \"       <b:list>\"\n\t\t\t\t+ \"          <b:value>target</b:value>\"\n\t\t\t\t+ \"       </b:list>\"\n\t\t\t\t+ \"   </b:property>\"\n\t\t\t\t+ \"   <b:property name='proxyTargetClass' value='false'/>\"\n\t\t\t\t+ \"</b:bean>\"\n\t\t\t\t+ TARGET_BEAN_AND_INTERCEPTOR\n\t\t\t\t+ AUTH_PROVIDER_XML\n\t\t\t\t+ ACCESS_MANAGER_XML);\n\t\t// @formatter:on\n\n\t\tITargetObject target = (ITargetObject) this.appContext.getBean(\"target\");\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> target.makeLowerCase(\"TEST\"));\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> target.makeUpperCase(\"test\"));\n\t}\n\n\tprivate void setContext(String context) {\n\t\tthis.appContext = new InMemoryXmlApplicationContext(context);\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/test/support/ClassPathExclusions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.support;\n\nimport java.io.File;\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * Annotation used to exclude entries from the classpath.\n *\n * @author Andy Wilkinson\n * @since 1.5.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@ExtendWith(ModifiedClassPathExtension.class)\npublic @interface ClassPathExclusions {\n\n\t/**\n\t * One or more Ant-style patterns that identify entries to be excluded from the class\n\t * path. Matching is performed against an entry's {@link File#getName() file name}.\n\t * For example, to exclude Hibernate Validator from the classpath,\n\t * {@code \"hibernate-validator-*.jar\"} can be used.\n\t * @return the exclusion patterns\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/test/support/ClassPathOverrides.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.support;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * Annotation used to override entries on the classpath.\n *\n * @author Andy Wilkinson\n * @since 1.5.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@ExtendWith(ModifiedClassPathExtension.class)\npublic @interface ClassPathOverrides {\n\n\t/**\n\t * One or more sets of Maven coordinates ({@code groupId:artifactId:version}) to be\n\t * added to the classpath. The additions will take precedence over any existing\n\t * classes on the classpath.\n\t * @return the coordinates\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/test/support/ForkedClassPath.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.support;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * Annotation used to fork the classpath. This can be helpful where neither\n * {@link ClassPathExclusions} or {@link ClassPathOverrides} are needed, but just a copy\n * of the classpath.\n *\n * @author Christoph Dreis\n * @since 2.4.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@ExtendWith(ModifiedClassPathExtension.class)\npublic @interface ForkedClassPath {\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/test/support/ModifiedClassPathClassLoader.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.support;\n\nimport java.io.File;\nimport java.lang.management.ManagementFactory;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.jar.Attributes;\nimport java.util.jar.JarFile;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport org.apache.maven.repository.internal.MavenRepositorySystemUtils;\nimport org.eclipse.aether.DefaultRepositorySystemSession;\nimport org.eclipse.aether.RepositorySystem;\nimport org.eclipse.aether.artifact.DefaultArtifact;\nimport org.eclipse.aether.collection.CollectRequest;\nimport org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;\nimport org.eclipse.aether.graph.Dependency;\nimport org.eclipse.aether.impl.DefaultServiceLocator;\nimport org.eclipse.aether.repository.LocalRepository;\nimport org.eclipse.aether.repository.RemoteRepository;\nimport org.eclipse.aether.resolution.ArtifactResult;\nimport org.eclipse.aether.resolution.DependencyRequest;\nimport org.eclipse.aether.resolution.DependencyResult;\nimport org.eclipse.aether.spi.connector.RepositoryConnectorFactory;\nimport org.eclipse.aether.spi.connector.transport.TransporterFactory;\nimport org.eclipse.aether.transport.http.HttpTransporterFactory;\n\nimport org.springframework.core.annotation.MergedAnnotation;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.util.AntPathMatcher;\nimport org.springframework.util.ConcurrentReferenceHashMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Custom {@link URLClassLoader} that modifies the class path.\n *\n * @author Andy Wilkinson\n * @author Christoph Dreis\n */\nfinal class ModifiedClassPathClassLoader extends URLClassLoader {\n\n\tprivate static final Map<Class<?>, ModifiedClassPathClassLoader> cache = new ConcurrentReferenceHashMap<>();\n\n\tprivate static final Pattern INTELLIJ_CLASSPATH_JAR_PATTERN = Pattern.compile(\".*classpath(\\\\d+)?\\\\.jar\");\n\n\tprivate static final int MAX_RESOLUTION_ATTEMPTS = 5;\n\n\tprivate final ClassLoader junitLoader;\n\n\tModifiedClassPathClassLoader(URL[] urls, ClassLoader parent, ClassLoader junitLoader) {\n\t\tsuper(urls, parent);\n\t\tthis.junitLoader = junitLoader;\n\t}\n\n\t@Override\n\tpublic Class<?> loadClass(String name) throws ClassNotFoundException {\n\t\tif (name.startsWith(\"org.junit\") || name.startsWith(\"org.hamcrest\")\n\t\t\t\t|| name.startsWith(\"io.netty.internal.tcnative\")) {\n\t\t\treturn Class.forName(name, false, this.junitLoader);\n\t\t}\n\t\treturn super.loadClass(name);\n\t}\n\n\tstatic ModifiedClassPathClassLoader get(Class<?> testClass) {\n\t\treturn cache.computeIfAbsent(testClass, ModifiedClassPathClassLoader::compute);\n\t}\n\n\tprivate static ModifiedClassPathClassLoader compute(Class<?> testClass) {\n\t\tClassLoader classLoader = testClass.getClassLoader();\n\t\tMergedAnnotations annotations = MergedAnnotations.from(testClass,\n\t\t\t\tMergedAnnotations.SearchStrategy.TYPE_HIERARCHY);\n\t\tif (annotations.isPresent(ForkedClassPath.class) && (annotations.isPresent(ClassPathOverrides.class)\n\t\t\t\t|| annotations.isPresent(ClassPathExclusions.class))) {\n\t\t\tthrow new IllegalStateException(\"@ForkedClassPath is redundant in combination with either \"\n\t\t\t\t\t+ \"@ClassPathOverrides or @ClassPathExclusions\");\n\t\t}\n\t\treturn new ModifiedClassPathClassLoader(processUrls(extractUrls(classLoader), annotations),\n\t\t\t\tclassLoader.getParent(), classLoader);\n\t}\n\n\tprivate static URL[] extractUrls(ClassLoader classLoader) {\n\t\tList<URL> extractedUrls = new ArrayList<>();\n\t\tdoExtractUrls(classLoader).forEach((URL url) -> {\n\t\t\tif (isManifestOnlyJar(url)) {\n\t\t\t\textractedUrls.addAll(extractUrlsFromManifestClassPath(url));\n\t\t\t}\n\t\t\telse {\n\t\t\t\textractedUrls.add(url);\n\t\t\t}\n\t\t});\n\t\treturn extractedUrls.toArray(new URL[0]);\n\t}\n\n\tprivate static Stream<URL> doExtractUrls(ClassLoader classLoader) {\n\t\tif (classLoader instanceof URLClassLoader urlClassLoader) {\n\t\t\treturn Stream.of(urlClassLoader.getURLs());\n\t\t}\n\t\treturn Stream.of(ManagementFactory.getRuntimeMXBean().getClassPath().split(File.pathSeparator))\n\t\t\t.map(ModifiedClassPathClassLoader::toURL);\n\t}\n\n\tprivate static URL toURL(String entry) {\n\t\ttry {\n\t\t\treturn new File(entry).toURI().toURL();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static boolean isManifestOnlyJar(URL url) {\n\t\treturn isShortenedIntelliJJar(url);\n\t}\n\n\tprivate static boolean isShortenedIntelliJJar(URL url) {\n\t\tString urlPath = url.getPath();\n\t\tboolean isCandidate = INTELLIJ_CLASSPATH_JAR_PATTERN.matcher(urlPath).matches();\n\t\tif (isCandidate) {\n\t\t\ttry {\n\t\t\t\tAttributes attributes = getManifestMainAttributesFromUrl(url);\n\t\t\t\tString createdBy = attributes.getValue(\"Created-By\");\n\t\t\t\treturn createdBy != null && createdBy.contains(\"IntelliJ\");\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static List<URL> extractUrlsFromManifestClassPath(URL booterJar) {\n\t\tList<URL> urls = new ArrayList<>();\n\t\ttry {\n\t\t\tfor (String entry : getClassPath(booterJar)) {\n\t\t\t\turls.add(new URL(entry));\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t\treturn urls;\n\t}\n\n\tprivate static String[] getClassPath(URL booterJar) throws Exception {\n\t\tAttributes attributes = getManifestMainAttributesFromUrl(booterJar);\n\t\treturn StringUtils.delimitedListToStringArray(attributes.getValue(Attributes.Name.CLASS_PATH), \" \");\n\t}\n\n\tprivate static Attributes getManifestMainAttributesFromUrl(URL url) throws Exception {\n\t\ttry (JarFile jarFile = new JarFile(new File(url.toURI()))) {\n\t\t\treturn jarFile.getManifest().getMainAttributes();\n\t\t}\n\t}\n\n\tprivate static URL[] processUrls(URL[] urls, MergedAnnotations annotations) {\n\t\tClassPathEntryFilter filter = new ClassPathEntryFilter(annotations.get(ClassPathExclusions.class));\n\t\tList<URL> additionalUrls = getAdditionalUrls(annotations.get(ClassPathOverrides.class));\n\t\tList<URL> processedUrls = new ArrayList<>(additionalUrls);\n\t\tfor (URL url : urls) {\n\t\t\tif (!filter.isExcluded(url)) {\n\t\t\t\tprocessedUrls.add(url);\n\t\t\t}\n\t\t}\n\t\treturn processedUrls.toArray(new URL[0]);\n\t}\n\n\tprivate static List<URL> getAdditionalUrls(MergedAnnotation<ClassPathOverrides> annotation) {\n\t\tif (!annotation.isPresent()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn resolveCoordinates(annotation.getStringArray(MergedAnnotation.VALUE));\n\t}\n\n\tprivate static List<URL> resolveCoordinates(String[] coordinates) {\n\t\tException latestFailure = null;\n\t\tDefaultServiceLocator serviceLocator = MavenRepositorySystemUtils.newServiceLocator();\n\t\tserviceLocator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);\n\t\tserviceLocator.addService(TransporterFactory.class, HttpTransporterFactory.class);\n\t\tRepositorySystem repositorySystem = serviceLocator.getService(RepositorySystem.class);\n\t\tDefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();\n\t\tLocalRepository localRepository = new LocalRepository(System.getProperty(\"user.home\") + \"/.m2/repository\");\n\t\tRemoteRepository remoteRepository = new RemoteRepository.Builder(\"central\", \"default\",\n\t\t\t\t\"https://repo.maven.apache.org/maven2\")\n\t\t\t.build();\n\t\tsession.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager(session, localRepository));\n\t\tfor (int i = 0; i < MAX_RESOLUTION_ATTEMPTS; i++) {\n\t\t\tCollectRequest collectRequest = new CollectRequest(null, Arrays.asList(remoteRepository));\n\t\t\tcollectRequest.setDependencies(createDependencies(coordinates));\n\t\t\tDependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);\n\t\t\ttry {\n\t\t\t\tDependencyResult result = repositorySystem.resolveDependencies(session, dependencyRequest);\n\t\t\t\tList<URL> resolvedArtifacts = new ArrayList<>();\n\t\t\t\tfor (ArtifactResult artifact : result.getArtifactResults()) {\n\t\t\t\t\tresolvedArtifacts.add(artifact.getArtifact().getFile().toURI().toURL());\n\t\t\t\t}\n\t\t\t\treturn resolvedArtifacts;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tlatestFailure = ex;\n\t\t\t}\n\t\t}\n\t\tthrow new IllegalStateException(\"Resolution failed after \" + MAX_RESOLUTION_ATTEMPTS + \" attempts\",\n\t\t\t\tlatestFailure);\n\t}\n\n\tprivate static List<Dependency> createDependencies(String[] allCoordinates) {\n\t\tList<Dependency> dependencies = new ArrayList<>();\n\t\tfor (String coordinate : allCoordinates) {\n\t\t\tdependencies.add(new Dependency(new DefaultArtifact(coordinate), null));\n\t\t}\n\t\treturn dependencies;\n\t}\n\n\t/**\n\t * Filter for class path entries.\n\t */\n\tprivate static final class ClassPathEntryFilter {\n\n\t\tprivate final List<String> exclusions;\n\n\t\tprivate final AntPathMatcher matcher = new AntPathMatcher();\n\n\t\tprivate ClassPathEntryFilter(MergedAnnotation<ClassPathExclusions> annotation) {\n\t\t\tthis.exclusions = annotation.getValue(MergedAnnotation.VALUE, String[].class)\n\t\t\t\t.map(Arrays::asList)\n\t\t\t\t.orElse(Collections.emptyList());\n\t\t}\n\n\t\tprivate boolean isExcluded(URL url) {\n\t\t\tif (\"file\".equals(url.getProtocol())) {\n\t\t\t\ttry {\n\t\t\t\t\tString name = new File(url.toURI()).getName();\n\t\t\t\t\tfor (String exclusion : this.exclusions) {\n\t\t\t\t\t\tif (this.matcher.match(exclusion, name)) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (URISyntaxException ex) {\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/test/support/ModifiedClassPathExtension.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.support;\n\nimport java.lang.reflect.Method;\nimport java.net.URLClassLoader;\n\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.ReflectiveInvocationContext;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.junit.platform.launcher.listeners.SummaryGeneratingListener;\nimport org.junit.platform.launcher.listeners.TestExecutionSummary;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.ReflectionUtils;\n\n/**\n * A custom {@link Extension} that runs tests using a modified class path. Entries are\n * excluded from the class path using {@link ClassPathExclusions @ClassPathExclusions} and\n * overridden using {@link ClassPathOverrides @ClassPathOverrides} on the test class. For\n * an unchanged copy of the class path {@link ForkedClassPath @ForkedClassPath} can be\n * used. A class loader is created with the customized class path and is used both to load\n * the test class and as the thread context class loader while the test is being run.\n *\n * @author Christoph Dreis\n */\nclass ModifiedClassPathExtension implements InvocationInterceptor {\n\n\t@Override\n\tpublic void interceptBeforeAllMethod(Invocation<Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tintercept(invocation, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptBeforeEachMethod(Invocation<Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tintercept(invocation, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptAfterEachMethod(Invocation<Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tintercept(invocation, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptAfterAllMethod(Invocation<Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tintercept(invocation, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptTestMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext,\n\t\t\tExtensionContext extensionContext) throws Throwable {\n\t\tif (isModifiedClassPathClassLoader(extensionContext)) {\n\t\t\tinvocation.proceed();\n\t\t\treturn;\n\t\t}\n\t\tinvocation.skip();\n\t\trunTestWithModifiedClassPath(invocationContext, extensionContext);\n\t}\n\n\tprivate void runTestWithModifiedClassPath(ReflectiveInvocationContext<Method> invocationContext,\n\t\t\tExtensionContext extensionContext) throws Throwable {\n\t\tClass<?> testClass = extensionContext.getRequiredTestClass();\n\t\tMethod testMethod = invocationContext.getExecutable();\n\t\tClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\tURLClassLoader modifiedClassLoader = ModifiedClassPathClassLoader.get(testClass);\n\t\tThread.currentThread().setContextClassLoader(modifiedClassLoader);\n\t\ttry {\n\t\t\trunTest(modifiedClassLoader, testClass.getName(), testMethod.getName());\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(originalClassLoader);\n\t\t}\n\t}\n\n\tprivate void runTest(ClassLoader classLoader, String testClassName, String testMethodName) throws Throwable {\n\t\tClass<?> testClass = classLoader.loadClass(testClassName);\n\t\tMethod testMethod = findMethod(testClass, testMethodName);\n\t\tLauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()\n\t\t\t.selectors(DiscoverySelectors.selectMethod(testClass, testMethod))\n\t\t\t.build();\n\t\tLauncher launcher = LauncherFactory.create();\n\t\tTestPlan testPlan = launcher.discover(request);\n\t\tSummaryGeneratingListener listener = new SummaryGeneratingListener();\n\t\tlauncher.registerTestExecutionListeners(listener);\n\t\tlauncher.execute(testPlan);\n\t\tTestExecutionSummary summary = listener.getSummary();\n\t\tif (!CollectionUtils.isEmpty(summary.getFailures())) {\n\t\t\tthrow summary.getFailures().get(0).getException();\n\t\t}\n\t}\n\n\tprivate Method findMethod(Class<?> testClass, String testMethodName) {\n\t\tMethod method = ReflectionUtils.findMethod(testClass, testMethodName);\n\t\tif (method == null) {\n\t\t\tMethod[] methods = ReflectionUtils.getUniqueDeclaredMethods(testClass);\n\t\t\tfor (Method candidate : methods) {\n\t\t\t\tif (candidate.getName().equals(testMethodName)) {\n\t\t\t\t\treturn candidate;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tAssert.state(method != null, () -> \"Unable to find \" + testClass + \".\" + testMethodName);\n\t\treturn method;\n\t}\n\n\tprivate void intercept(Invocation<Void> invocation, ExtensionContext extensionContext) throws Throwable {\n\t\tif (isModifiedClassPathClassLoader(extensionContext)) {\n\t\t\tinvocation.proceed();\n\t\t\treturn;\n\t\t}\n\t\tinvocation.skip();\n\t}\n\n\tprivate boolean isModifiedClassPathClassLoader(ExtensionContext extensionContext) {\n\t\tClass<?> testClass = extensionContext.getRequiredTestClass();\n\t\tClassLoader classLoader = testClass.getClassLoader();\n\t\treturn classLoader.getClass().getName().equals(ModifiedClassPathClassLoader.class.getName());\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/java/org/springframework/security/test/web/servlet/RequestCacheResultMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet;\n\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.test.web.servlet.ResultMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Ensures that the MockMvcResult redirects to the saved RequestCache.getRedirectUrl().\n */\npublic final class RequestCacheResultMatcher {\n\n\t/**\n\t * Verifies that the MockMvcResult redirects to the saved\n\t * RequestCache.getRedirectUrl().\n\t * @return a ResultMatcher that performs the verification.\n\t */\n\tpublic static ResultMatcher redirectToCachedRequest() {\n\t\treturn (mvcResult) -> {\n\t\t\tRequestCache requestCache = new HttpSessionRequestCache();\n\t\t\tMockHttpServletResponse response = mvcResult.getResponse();\n\t\t\tSavedRequest savedRequest = requestCache.getRequest(mvcResult.getRequest(), response);\n\t\t\tassertThat(savedRequest).describedAs(\"savedRequest cannot be null\").isNotNull();\n\t\t\tString cachedRedirectUrl = savedRequest.getRedirectUrl();\n\t\t\tassertThat(response.getRedirectedUrl()).isEqualTo(cachedRedirectUrl);\n\t\t};\n\t}\n\n\tprivate RequestCacheResultMatcher() {\n\t}\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/method/configuration/KotlinEnableReactiveMethodSecurityTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration\n\nimport io.mockk.Called\nimport io.mockk.clearAllMocks\nimport io.mockk.coEvery\nimport io.mockk.coVerify\nimport io.mockk.mockk\nimport io.mockk.verify\nimport kotlinx.coroutines.flow.collect\nimport kotlinx.coroutines.flow.flow\nimport kotlinx.coroutines.flow.toList\nimport kotlinx.coroutines.runBlocking\nimport org.assertj.core.api.Assertions.assertThat\nimport org.assertj.core.api.Assertions.assertThatExceptionOfType\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.access.AccessDeniedException\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\nclass KotlinEnableReactiveMethodSecurityTests {\n\n    private lateinit var delegate: KotlinReactiveMessageService\n\n    @Autowired\n    var messageService: KotlinReactiveMessageService? = null\n\n    @AfterEach\n    fun cleanup() {\n        clearAllMocks()\n    }\n\n    @Autowired\n    fun setConfig(config: Config) {\n        this.delegate = config.delegate\n    }\n\n    @Test\n    fun `suspendingNoAuth always success`() {\n        runBlocking {\n            assertThat(messageService!!.suspendingNoAuth()).isEqualTo(\"success\")\n        }\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"ROLE_ADMIN\"])\n    fun `suspendingPreAuthorizeHasRole when user has role then success`() {\n        runBlocking {\n            assertThat(messageService!!.suspendingPreAuthorizeHasRole()).isEqualTo(\"admin\")\n        }\n    }\n\n    @Test\n    fun `suspendingPreAuthorizeHasRole when user does not have role then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingPreAuthorizeHasRole()\n            }\n        }\n    }\n\n    @Test\n    @WithMockUser\n    fun `suspendingPreAuthorizeBean when authorized then success`() {\n        runBlocking {\n            assertThat(messageService!!.suspendingPreAuthorizeBean(true)).isEqualTo(\"check\")\n        }\n    }\n\n    @Test\n    fun `suspendingPreAuthorizeBean when not authorized then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingPreAuthorizeBean(false)\n            }\n        }\n    }\n\n    @Test\n    @WithMockUser(\"user\")\n    fun `suspendingPostAuthorize when authorized then success`() {\n        runBlocking {\n            assertThat(messageService!!.suspendingPostAuthorizeContainsName()).isEqualTo(\"user\")\n        }\n    }\n\n    @Test\n    @WithMockUser(\"other-user\")\n    fun `suspendingPostAuthorize when not authorized then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingPostAuthorizeContainsName()\n            }\n        }\n    }\n\n    @Test\n    fun `suspendingPreAuthorizeDelegate when user does not have role then delegate not called`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingPreAuthorizeDelegate()\n            }\n        }\n        verify { delegate wasNot Called }\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"ROLE_ADMIN\"])\n    fun `suspendingPreAuthorizeDelegate when user has role then delegate called`() {\n        coEvery { delegate.suspendingPreAuthorizeHasRole() } returns \"ok\"\n        runBlocking {\n            messageService!!.suspendingPreAuthorizeDelegate()\n        }\n        coVerify(exactly = 1) { delegate.suspendingPreAuthorizeHasRole() }\n    }\n\n    @Test\n    @WithMockUser\n    fun `suspendingPrePostAuthorizeHasRoleContainsName when not pre authorized then delegate not called`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingPrePostAuthorizeHasRoleContainsName()\n            }\n        }\n        verify { delegate wasNot Called }\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"ROLE_ADMIN\"])\n    fun `suspendingPrePostAuthorizeHasRoleContainsName when not post authorized then exception`() {\n        coEvery { delegate.suspendingPrePostAuthorizeHasRoleContainsName() } returns \"wrong\"\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingPrePostAuthorizeHasRoleContainsName()\n            }\n        }\n        coVerify(exactly = 1) { delegate.suspendingPrePostAuthorizeHasRoleContainsName() }\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"ROLE_ADMIN\"])\n    fun `suspendingPrePostAuthorizeHasRoleContainsName when authorized then success`() {\n        coEvery { delegate.suspendingPrePostAuthorizeHasRoleContainsName() } returns \"user\"\n        runBlocking {\n            assertThat(messageService!!.suspendingPrePostAuthorizeHasRoleContainsName()).contains(\"user\")\n        }\n        coVerify(exactly = 1) { delegate.suspendingPrePostAuthorizeHasRoleContainsName() }\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"ROLE_ADMIN\"])\n    fun `suspendingFlowPreAuthorize when user has role then success`() {\n        runBlocking {\n            assertThat(messageService!!.suspendingFlowPreAuthorize().toList()).containsExactly(1, 2, 3)\n        }\n    }\n\n    @Test\n    fun `suspendingFlowPreAuthorize when user does not have role then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingFlowPreAuthorize().collect()\n            }\n        }\n    }\n\n    @Test\n    fun `suspendingFlowPostAuthorize when authorized then success`() {\n        runBlocking {\n            assertThat(messageService!!.suspendingFlowPostAuthorize(true).toList()).containsExactly(1, 2, 3)\n        }\n    }\n\n    @Test\n    fun `suspendingFlowPostAuthorize when not authorized then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingFlowPostAuthorize(false).collect()\n            }\n        }\n    }\n\n    @Test\n    fun `suspendingFlowPreAuthorizeDelegate when not authorized then delegate not called`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingFlowPreAuthorizeDelegate().collect()\n            }\n        }\n        verify { delegate wasNot Called }\n    }\n\n    @Test\n    fun `suspendingFlowPrePostAuthorizeBean when not pre authorized then delegate not called`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingFlowPrePostAuthorizeBean(true).collect()\n            }\n        }\n    }\n\n    @Test\n    @WithMockUser(roles = [\"ADMIN\"])\n    fun `suspendingFlowPrePostAuthorizeBean when not post authorized then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.suspendingFlowPrePostAuthorizeBean(false).collect()\n            }\n        }\n    }\n\n    @Test\n    @WithMockUser(roles = [\"ADMIN\"])\n    fun `suspendingFlowPrePostAuthorizeBean when authorized then success`() {\n        runBlocking {\n            assertThat(messageService!!.suspendingFlowPrePostAuthorizeBean(true).toList()).containsExactly(1, 2, 3)\n        }\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"ROLE_ADMIN\"])\n    fun `suspendingFlowPreAuthorizeDelegate when user has role then delegate called`() {\n        coEvery { delegate.flowPreAuthorize() } returns flow { }\n        runBlocking {\n            messageService!!.suspendingFlowPreAuthorizeDelegate().collect()\n        }\n        coVerify(exactly = 1) { delegate.flowPreAuthorize() }\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"ROLE_ADMIN\"])\n    fun `flowPreAuthorize when user has role then success`() {\n        runBlocking {\n            assertThat(messageService!!.flowPreAuthorize().toList()).containsExactly(1, 2, 3)\n        }\n    }\n\n    @Test\n    fun `flowPreAuthorize when user does not have role then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.flowPreAuthorize().collect()\n            }\n        }\n    }\n\n    @Test\n    fun `flowPostAuthorize when authorized then success`() {\n        runBlocking {\n            assertThat(messageService!!.flowPostAuthorize(true).toList()).containsExactly(1, 2, 3)\n        }\n    }\n\n    @Test\n    fun `flowPostAuthorize when not authorized then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.flowPostAuthorize(false).collect()\n            }\n        }\n    }\n\n    @Test\n    fun `flowPreAuthorizeDelegate when user does not have role then delegate not called`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.flowPreAuthorizeDelegate().collect()\n            }\n        }\n        verify { delegate wasNot Called }\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"ROLE_ADMIN\"])\n    fun `flowPreAuthorizeDelegate when user has role then delegate called`() {\n        coEvery { delegate.flowPreAuthorize() } returns flow { }\n        runBlocking {\n            messageService!!.flowPreAuthorizeDelegate().collect()\n        }\n        coVerify(exactly = 1) { delegate.flowPreAuthorize() }\n    }\n\n    @Test\n    fun `flowPrePostAuthorize when not pre authorized then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.flowPrePostAuthorize(true).collect()\n            }\n        }\n    }\n\n    @Test\n    @WithMockUser(roles = [\"ADMIN\"])\n    fun `flowPrePostAuthorize when not post authorized then denied`() {\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            runBlocking {\n                messageService!!.flowPrePostAuthorize(false).collect()\n            }\n        }\n    }\n\n    @Test\n    @WithMockUser(roles = [\"ADMIN\"])\n    fun `flowPrePostAuthorize when authorized then success`() {\n        runBlocking {\n            assertThat(messageService!!.flowPrePostAuthorize(true).toList()).containsExactly(1, 2, 3)\n        }\n    }\n\n    @Configuration\n    @EnableReactiveMethodSecurity\n    open class Config {\n        var delegate = mockk<KotlinReactiveMessageService>()\n\n        @Bean\n        open fun messageService(): KotlinReactiveMessageServiceImpl {\n            return KotlinReactiveMessageServiceImpl(this.delegate)\n        }\n\n        @Bean\n        open fun authz(): Authz {\n            return Authz()\n        }\n\n        open class Authz {\n            fun check(r: Boolean): Boolean {\n                return r\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/method/configuration/KotlinReactiveMessageService.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration\n\nimport kotlinx.coroutines.flow.Flow\n\ninterface KotlinReactiveMessageService {\n\n    suspend fun suspendingNoAuth(): String\n\n    suspend fun suspendingPreAuthorizeHasRole(): String\n\n    suspend fun suspendingPreAuthorizeBean(id: Boolean): String\n\n    suspend fun suspendingPostAuthorizeContainsName(): String\n\n    suspend fun suspendingPreAuthorizeDelegate(): String\n\n    suspend fun suspendingPrePostAuthorizeHasRoleContainsName(): String\n\n    suspend fun suspendingFlowPreAuthorize(): Flow<Int>\n\n    suspend fun suspendingFlowPostAuthorize(id: Boolean): Flow<Int>\n\n    suspend fun suspendingFlowPreAuthorizeDelegate(): Flow<Int>\n\n    suspend fun suspendingFlowPrePostAuthorizeBean(id: Boolean): Flow<Int>\n\n    fun flowPreAuthorize(): Flow<Int>\n\n    fun flowPostAuthorize(id: Boolean): Flow<Int>\n\n    fun flowPreAuthorizeDelegate(): Flow<Int>\n\n    fun flowPrePostAuthorize(id: Boolean): Flow<Int>\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/method/configuration/KotlinReactiveMessageServiceImpl.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.method.configuration\n\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.flow\nimport org.springframework.security.access.prepost.PostAuthorize\nimport org.springframework.security.access.prepost.PreAuthorize\n\nclass KotlinReactiveMessageServiceImpl(val delegate: KotlinReactiveMessageService) : KotlinReactiveMessageService {\n\n    override suspend fun suspendingNoAuth(): String {\n        delay(1)\n        return \"success\"\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    override suspend fun suspendingPreAuthorizeHasRole(): String {\n        delay(1)\n        return \"admin\"\n    }\n\n    @PreAuthorize(\"@authz.check(#id)\")\n    override suspend fun suspendingPreAuthorizeBean(id: Boolean): String {\n        delay(1)\n        return \"check\"\n    }\n\n    @PostAuthorize(\"returnObject?.contains(authentication?.name)\")\n    override suspend fun suspendingPostAuthorizeContainsName(): String {\n        delay(1)\n        return \"user\"\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    @PostAuthorize(\"returnObject?.contains(authentication?.name)\")\n    override suspend fun suspendingPrePostAuthorizeHasRoleContainsName(): String {\n        return delegate.suspendingPrePostAuthorizeHasRoleContainsName()\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    override suspend fun suspendingPreAuthorizeDelegate(): String {\n        return delegate.suspendingPreAuthorizeHasRole()\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    override suspend fun suspendingFlowPreAuthorize(): Flow<Int> {\n        delay(1)\n        return flow {\n            for (i in 1..3) {\n                delay(1)\n                emit(i)\n            }\n        }\n    }\n\n    @PostAuthorize(\"@authz.check(#id)\")\n    override suspend fun suspendingFlowPostAuthorize(id: Boolean): Flow<Int> {\n        delay(1)\n        return flow {\n            for (i in 1..3) {\n                delay(1)\n                emit(i)\n            }\n        }\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    override suspend fun suspendingFlowPreAuthorizeDelegate(): Flow<Int> {\n        delay(1)\n        return delegate.flowPreAuthorize()\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    @PostAuthorize(\"@authz.check(#id)\")\n    override suspend fun suspendingFlowPrePostAuthorizeBean(id: Boolean): Flow<Int> {\n        delay(1)\n        return flow {\n            for (i in 1..3) {\n                delay(1)\n                emit(i)\n            }\n        }\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    override fun flowPreAuthorize(): Flow<Int> {\n        return flow {\n            for (i in 1..3) {\n                delay(1)\n                emit(i)\n            }\n        }\n    }\n\n    @PostAuthorize(\"@authz.check(#id)\")\n    override fun flowPostAuthorize(id: Boolean): Flow<Int> {\n        return flow {\n            for (i in 1..3) {\n                delay(1)\n                emit(i)\n            }\n        }\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    override fun flowPreAuthorizeDelegate(): Flow<Int> {\n        return delegate.flowPreAuthorize()\n    }\n\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    @PostAuthorize(\"@authz.check(#id)\")\n    override fun flowPrePostAuthorize(id: Boolean): Flow<Int> {\n        return flow {\n            for (i in 1..3) {\n                delay(1)\n                emit(i)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/AnonymousDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authentication.AnonymousAuthenticationToken\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.annotation.AuthenticationPrincipal\nimport org.springframework.security.core.authority.SimpleGrantedAuthority\nimport org.springframework.security.core.context.SecurityContextHolder\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\nimport java.util.*\n\n/**\n * Tests for [AnonymousDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass AnonymousDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `anonymous when custom principal then custom principal used`() {\n        this.spring.register(PrincipalConfig::class.java, PrincipalController::class.java).autowire()\n\n        this.mockMvc.get(\"/principal\")\n                .andExpect {\n                    content { string(\"principal\") }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class PrincipalConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                anonymous {\n                    principal = \"principal\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `anonymous when custom key then custom key used`() {\n        this.spring.register(KeyConfig::class.java, PrincipalController::class.java).autowire()\n\n        this.mockMvc.get(\"/key\")\n                .andExpect {\n                    content { string(\"key\".hashCode().toString()) }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class KeyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                anonymous {\n                    key = \"key\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `anonymous when disabled then responds with forbidden`() {\n        this.spring.register(AnonymousDisabledConfig::class.java, PrincipalController::class.java).autowire()\n\n        this.mockMvc.get(\"/principal\")\n                .andExpect {\n                    content { string(\"\") }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AnonymousDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                anonymous {\n                    disable()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `anonymous when custom authorities then authorities used`() {\n        this.spring.register(AnonymousAuthoritiesConfig::class.java, PrincipalController::class.java).autowire()\n\n        this.mockMvc.get(\"/principal\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AnonymousAuthoritiesConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                anonymous {\n                    authorities = listOf(SimpleGrantedAuthority(\"TEST\"))\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, hasAuthority(\"TEST\"))\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @RestController\n    internal class PrincipalController {\n        @GetMapping(\"/principal\")\n        fun principal(@AuthenticationPrincipal principal: String?): String? {\n            return principal\n        }\n\n        @GetMapping(\"/key\")\n        fun key(): String {\n            return anonymousToken()\n                    .map { it.keyHash }\n                    .map { it.toString() }\n                    .orElse(null)\n        }\n\n        private fun anonymousToken(): Optional<AnonymousAuthenticationToken> {\n            return Optional.of(SecurityContextHolder.getContext())\n                    .map { it.authentication }\n                    .filter { it is AnonymousAuthenticationToken }\n                    .map { AnonymousAuthenticationToken::class.java.cast(it) }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeHttpRequestsDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.verify\nimport jakarta.servlet.DispatcherType\nimport org.aopalliance.intercept.MethodInvocation\nimport org.assertj.core.api.Assertions.assertThatThrownBy\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.UnsatisfiedDependencyException\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.beans.factory.getBean\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpMethod\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl\nimport org.springframework.security.authentication.RememberMeAuthenticationToken\nimport org.springframework.security.authentication.TestAuthentication\nimport org.springframework.security.authorization.AuthorizationDecision\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.core.GrantedAuthorityDefaults\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.authority.AuthorityUtils\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext\nimport org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher\nimport org.springframework.security.web.util.matcher.RegexRequestMatcher\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.post\nimport org.springframework.test.web.servlet.put\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.PathVariable\nimport org.springframework.web.bind.annotation.RequestMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\nimport org.springframework.web.util.WebUtils\nimport java.util.function.Supplier\n\n/**\n * Tests for [AuthorizeHttpRequestsDsl]\n *\n * @author Yuriy Savchenko\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass AuthorizeHttpRequestsDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `request when secured by regex matcher then responds with forbidden`() {\n        this.spring.register(AuthorizeHttpRequestsByRegexConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/private\")\n            .andExpect {\n                status { isForbidden() }\n            }\n    }\n\n    @Test\n    fun `request when allowed by regex matcher then responds with ok`() {\n        this.spring.register(AuthorizeHttpRequestsByRegexConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/path\")\n            .andExpect {\n                status { isOk() }\n            }\n    }\n\n    @Test\n    fun `request when allowed by regex matcher with http method then responds based on method`() {\n        this.spring.register(AuthorizeHttpRequestsByRegexConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/onlyPostPermitted\") { with(csrf()) }\n            .andExpect {\n                status { isOk() }\n            }\n\n        this.mockMvc.get(\"/onlyPostPermitted\")\n            .andExpect {\n                status { isForbidden() }\n            }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthorizeHttpRequestsByRegexConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(RegexRequestMatcher(\"/path\", null), permitAll)\n                    authorize(RegexRequestMatcher(\"/onlyPostPermitted\", \"POST\"), permitAll)\n                    authorize(RegexRequestMatcher(\"/onlyPostPermitted\", \"GET\"), denyAll)\n                    authorize(RegexRequestMatcher(\".*\", null), authenticated)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path(): String {\n                return \"ok\"\n            }\n\n            @RequestMapping(\"/onlyPostPermitted\")\n            fun onlyPostPermitted():String {\n                return \"ok\"\n            }\n        }\n    }\n\n    @Test\n    fun `request when secured by mvc then responds with forbidden`() {\n        this.spring.register(AuthorizeHttpRequestsByMvcConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/private\")\n            .andExpect {\n                status { isForbidden() }\n            }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AuthorizeHttpRequestsByMvcConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/path\", permitAll)\n                    authorize(\"/**\", authenticated)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n    }\n\n    @Test\n    fun `request when secured by mvc path variables then responds based on path variable value`() {\n        this.spring.register(MvcMatcherPathVariablesConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/user/user\")\n            .andExpect {\n                status { isOk() }\n            }\n\n        this.mockMvc.get(\"/user/deny\")\n            .andExpect {\n                status { isForbidden() }\n            }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class MvcMatcherPathVariablesConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val access = AuthorizationManager { _: Supplier<out Authentication>, context: RequestAuthorizationContext ->\n                AuthorizationDecision(context.variables[\"userName\"] == \"user\")\n            }\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/user/{userName}\", access)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/user/{user}\")\n            fun path(@PathVariable user: String) {\n            }\n        }\n    }\n\n    @Test\n    fun `request when user has allowed role then responds with OK`() {\n        this.spring.register(HasRoleConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"admin\", \"password\"))\n        }.andExpect {\n            status { isOk() }\n        }\n    }\n\n    @Test\n    fun `request when user does not have allowed role then responds with forbidden`() {\n        this.spring.register(HasRoleConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"user\", \"password\"))\n        }.andExpect {\n            status { isForbidden() }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class HasRoleConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/**\", hasRole(\"ADMIN\"))\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @GetMapping(\"/\")\n            fun index(): String {\n                return \"ok\"\n            }\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .roles(\"USER\")\n                .build()\n            val adminDetails = User.withDefaultPasswordEncoder()\n                .username(\"admin\")\n                .password(\"password\")\n                .roles(\"ADMIN\")\n                .build()\n            return InMemoryUserDetailsManager(userDetails, adminDetails)\n        }\n    }\n\n    @Test\n    fun `request when user has some allowed roles then responds with OK`() {\n        this.spring.register(HasAnyRoleConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"user\", \"password\"))\n        }.andExpect {\n            status { isOk() }\n        }\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"admin\", \"password\"))\n        }.andExpect {\n            status { isOk() }\n        }\n    }\n\n    @Test\n    fun `request when user does not have any allowed roles then responds with forbidden`() {\n        this.spring.register(HasAnyRoleConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"other\", \"password\"))\n        }.andExpect {\n            status { isForbidden() }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class HasAnyRoleConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/**\", hasAnyRole(\"ADMIN\", \"USER\"))\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @GetMapping(\"/\")\n            fun index(): String {\n                return \"ok\"\n            }\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .roles(\"USER\")\n                .build()\n            val admin1Details = User.withDefaultPasswordEncoder()\n                .username(\"admin\")\n                .password(\"password\")\n                .roles(\"ADMIN\")\n                .build()\n            val admin2Details = User.withDefaultPasswordEncoder()\n                .username(\"other\")\n                .password(\"password\")\n                .roles(\"OTHER\")\n                .build()\n            return InMemoryUserDetailsManager(userDetails, admin1Details, admin2Details)\n        }\n    }\n\n    @Test\n    fun `request when user has allowed authority then responds with OK`() {\n        this.spring.register(HasAuthorityConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"admin\", \"password\"))\n        }.andExpect {\n            status { isOk() }\n        }\n    }\n\n    @Test\n    fun `request when user does not have allowed authority then responds with forbidden`() {\n        this.spring.register(HasAuthorityConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"user\", \"password\"))\n        }.andExpect {\n            status { isForbidden() }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class HasAuthorityConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/**\", hasAuthority(\"ROLE_ADMIN\"))\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @GetMapping(\"/\")\n            fun index(): String {\n                return \"ok\"\n            }\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .roles(\"USER\")\n                .build()\n            val adminDetails = User.withDefaultPasswordEncoder()\n                .username(\"admin\")\n                .password(\"password\")\n                .roles(\"ADMIN\")\n                .build()\n            return InMemoryUserDetailsManager(userDetails, adminDetails)\n        }\n    }\n\n    @Test\n    fun `request when user has some allowed authorities then responds with OK`() {\n        this.spring.register(HasAnyAuthorityConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"user\", \"password\"))\n        }.andExpect {\n            status { isOk() }\n        }\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"admin\", \"password\"))\n        }.andExpect {\n            status { isOk() }\n        }\n    }\n\n    @Test\n    fun `request when user does not have any allowed authorities then responds with forbidden`() {\n        this.spring.register(HasAnyAuthorityConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"other\", \"password\"))\n        }.andExpect {\n            status { isForbidden() }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class HasAnyAuthorityConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/**\", hasAnyAuthority(\"ROLE_ADMIN\", \"ROLE_USER\"))\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @GetMapping(\"/\")\n            fun index(): String {\n                return  \"ok\"\n            }\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"ROLE_USER\")\n                .build()\n            val admin1Details = User.withDefaultPasswordEncoder()\n                .username(\"admin\")\n                .password(\"password\")\n                .authorities(\"ROLE_ADMIN\")\n                .build()\n            val admin2Details = User.withDefaultPasswordEncoder()\n                .username(\"other\")\n                .password(\"password\")\n                .authorities(\"ROLE_OTHER\")\n                .build()\n            return InMemoryUserDetailsManager(userDetails, admin1Details, admin2Details)\n        }\n    }\n\n    @Test\n    fun `request when secured by mvc with servlet path then responds based on servlet path`() {\n        this.spring.register(MvcMatcherServletPathConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/spring/path\")\n            .with { request ->\n                request.servletPath = \"/spring\"\n                request\n            })\n            .andExpect(status().isForbidden)\n\n        this.mockMvc.perform(get(\"/other/path\")\n            .with { request ->\n                request.servletPath = \"/other\"\n                request\n            })\n            .andExpect(status().isForbidden)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class MvcMatcherServletPathConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/path\", \"/spring\", denyAll)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n    }\n\n    @Test\n    fun `request when secured by mvc with http method then responds based on http method`() {\n        this.spring.register(AuthorizeRequestsByMvcConfigWithHttpMethod::class.java).autowire()\n\n        this.mockMvc.get(\"/path\")\n            .andExpect {\n                status { isOk() }\n            }\n\n        this.mockMvc.put(\"/path\") { with(csrf()) }\n            .andExpect {\n                status { isForbidden() }\n            }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AuthorizeRequestsByMvcConfigWithHttpMethod {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(HttpMethod.GET, \"/path\", permitAll)\n                    authorize(HttpMethod.PUT, \"/path\", denyAll)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n    }\n\n    @Test\n    fun `request when secured by mvc with servlet path and http method then responds based on path and method`() {\n        this.spring.register(MvcMatcherServletPathHttpMethodConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/spring/path\")\n            .with { request ->\n                request.apply {\n                    servletPath = \"/spring\"\n                }\n            })\n            .andExpect(status().isForbidden)\n\n        this.mockMvc.perform(put(\"/spring/path\")\n            .with { request ->\n                request.apply {\n                    servletPath = \"/spring\"\n                    csrf()\n                }\n            })\n            .andExpect(status().isForbidden)\n\n        this.mockMvc.perform(get(\"/other/path\")\n            .with { request ->\n                request.apply {\n                    servletPath = \"/other\"\n                }\n            })\n            .andExpect(status().isForbidden)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class MvcMatcherServletPathHttpMethodConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(HttpMethod.GET, \"/path\", \"/spring\", denyAll)\n                    authorize(HttpMethod.PUT, \"/path\", \"/spring\", denyAll)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n    }\n\n    @Test\n    fun `request when shouldFilterAllDispatcherTypes and denyAll and ERROR then responds with forbidden`() {\n        this.spring.register(ShouldFilterAllDispatcherTypesTrueDenyAllConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/path\")\n            .with { request ->\n                request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\")\n                request.apply {\n                    dispatcherType = DispatcherType.ERROR\n                }\n            })\n            .andExpect(status().isForbidden)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class ShouldFilterAllDispatcherTypesTrueDenyAllConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, denyAll)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n\n    }\n\n    @Test\n    fun `request when shouldFilterAllDispatcherTypes and permitAll and ERROR then responds with ok`() {\n        this.spring.register(ShouldFilterAllDispatcherTypesTruePermitAllConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/path\")\n            .with { request ->\n                request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\")\n                request.apply {\n                    dispatcherType = DispatcherType.ERROR\n                }\n            })\n            .andExpect(status().isOk)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class ShouldFilterAllDispatcherTypesTruePermitAllConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, permitAll)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n\n    }\n\n    @Test\n    fun `request when shouldFilterAllDispatcherTypes false and ERROR dispatcher then responds with ok`() {\n        this.spring.register(ShouldFilterAllDispatcherTypesFalseAndDenyAllConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/path\")\n            .with { request ->\n                request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\")\n                request.apply {\n                    dispatcherType = DispatcherType.ERROR\n                }\n            })\n            .andExpect(status().isOk)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class ShouldFilterAllDispatcherTypesFalseAndDenyAllConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(DispatcherTypeRequestMatcher(DispatcherType.ERROR), permitAll)\n                    authorize(DispatcherTypeRequestMatcher(DispatcherType.ASYNC), permitAll)\n                    authorize(anyRequest, denyAll)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n\n    }\n\n    @Test\n    fun `request when shouldFilterAllDispatcherTypes omitted and ERROR dispatcher then responds with forbidden`() {\n        this.spring.register(ShouldFilterAllDispatcherTypesOmittedAndDenyAllConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/path\")\n            .with { request ->\n                request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\")\n                request.apply {\n                    dispatcherType = DispatcherType.ERROR\n                }\n            })\n            .andExpect(status().isForbidden)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class ShouldFilterAllDispatcherTypesOmittedAndDenyAllConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, denyAll)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n\n    }\n\n    @Test\n    fun `request when ip address does not match then responds with forbidden`() {\n        this.spring.register(HasIpAddressConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/path\")\n            .with { request ->\n                request.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\")\n                request.apply {\n                    dispatcherType = DispatcherType.ERROR\n                }\n            })\n            .andExpect(status().isForbidden)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class HasIpAddressConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, hasIpAddress(\"10.0.0.0/24\"))\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n    }\n\n    @Test\n    fun `hasRole when prefixed by configured role prefix should fail to configure`() {\n        assertThatThrownBy { this.spring.register(RoleValidationConfig::class.java).autowire() }\n            .isInstanceOf(UnsatisfiedDependencyException::class.java)\n            .hasRootCauseInstanceOf(IllegalArgumentException::class.java)\n            .hasMessageContaining(\n                \"ROLE_JUNIPER should not start with ROLE_ since ROLE_ is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.\"\n            )\n        assertThatThrownBy { this.spring.register(RoleValidationConfig::class.java, GrantedAuthorityDefaultsConfig::class.java).autowire() }\n            .isInstanceOf(UnsatisfiedDependencyException::class.java)\n            .hasRootCauseInstanceOf(IllegalArgumentException::class.java)\n            .hasMessageContaining(\n                \"CUSTOM_JUNIPER should not start with CUSTOM_ since CUSTOM_ is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.\"\n            )\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class RoleValidationConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/role\", hasAnyRole(\"ROLE_JUNIPER\"))\n                    authorize(\"/custom\", hasRole(\"CUSTOM_JUNIPER\"))\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class GrantedAuthorityDefaultsConfig {\n        @Bean\n        open fun grantedAuthorityDefaults(): GrantedAuthorityDefaults {\n            return GrantedAuthorityDefaults(\"CUSTOM_\")\n        }\n    }\n\n    @Test\n    fun `hasRole when role hierarchy configured then honor hierarchy`() {\n        this.spring.register(RoleHierarchyConfig::class.java).autowire()\n        this.mockMvc.get(\"/protected\") {\n            with(httpBasic(\"admin\", \"password\"))\n        }.andExpect {\n            status {\n                isOk()\n            }\n        }\n        this.mockMvc.get(\"/protected\") {\n            with(httpBasic(\"user\", \"password\"))\n        }.andExpect {\n            status {\n                isOk()\n            }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class RoleHierarchyConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/protected\", hasRole(\"USER\"))\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun roleHierarchy(): RoleHierarchy {\n            return RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > ROLE_USER\")\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .roles(\"USER\")\n                .build()\n            val admin = User.withDefaultPasswordEncoder()\n                .username(\"admin\")\n                .password(\"password\")\n                .roles(\"ADMIN\")\n                .build()\n            return InMemoryUserDetailsManager(user, admin)\n        }\n\n        @RestController\n        internal class PathController {\n\n            @RequestMapping(\"/protected\")\n            fun path() {\n            }\n\n        }\n\n    }\n\n    @Test\n    fun `request when fully authenticated configured then responds ok`() {\n        this.spring.register(FullyAuthenticatedConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/path\") {\n            with(user(\"user\").roles(\"USER\"))\n        }.andExpect {\n            status {\n                isOk()\n            }\n        }\n    }\n\n    @Test\n    fun `request when fully authenticated configured and remember-me token then responds unauthorized`() {\n        this.spring.register(FullyAuthenticatedConfig::class.java).autowire()\n        val rememberMe = RememberMeAuthenticationToken(\"key\", \"user\",\n                AuthorityUtils.createAuthorityList(\"ROLE_USER\"))\n\n        this.mockMvc.get(\"/path\") {\n            with(user(\"user\").roles(\"USER\"))\n            with(authentication(rememberMe))\n        }.andExpect {\n            status {\n                isUnauthorized()\n            }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class FullyAuthenticatedConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/path\", fullyAuthenticated)\n                }\n                httpBasic {  }\n                rememberMe {  }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService = InMemoryUserDetailsManager(TestAuthentication.user())\n\n        @RestController\n        internal class PathController {\n            @GetMapping(\"/path\")\n            fun path(): String {\n                return \"ok\"\n            }\n        }\n    }\n\n    @Test\n    fun `custom AuthorizationManagerFactory of RequestAuthorizationContext`() {\n        this.spring.register(AuthorizationManagerFactoryRequestAuthorizationContextConfig::class.java).autowire()\n        val authzManagerFactory =\n            this.spring.context.getBean<AuthorizationManagerFactory<RequestAuthorizationContext>>()\n        val authzManager = this.spring.context.getBean<AuthorizationManagerFactoryRequestAuthorizationContextConfig>().authorizationManager\n        every { authzManager.authorize(any(), any()) } returns AuthorizationDecision(true)\n\n        verify { authzManagerFactory.authenticated() }\n        verify { authzManagerFactory.denyAll() }\n        verify { authzManagerFactory.fullyAuthenticated() }\n        verify { authzManagerFactory.hasAllAuthorities(\"USER\", \"ADMIN\") }\n        verify { authzManagerFactory.hasAllRoles(\"USER\", \"ADMIN\") }\n        verify { authzManagerFactory.hasAnyAuthority(\"USER\", \"ADMIN\") }\n        verify { authzManagerFactory.hasAnyRole(\"USER\", \"ADMIN\") }\n        verify { authzManagerFactory.hasAuthority(\"USER\") }\n        verify { authzManagerFactory.hasRole(\"USER\") }\n        verify { authzManagerFactory.permitAll() }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AuthorizationManagerFactoryRequestAuthorizationContextConfig {\n        val authorizationManager: AuthorizationManager<RequestAuthorizationContext> = mockk()\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/authenticated\", authenticated)\n                    authorize(\"/denyAll\", denyAll)\n                    authorize(\"/fullyAuthenticated\", fullyAuthenticated)\n                    authorize(\"/hasAllAuthorities/user_admin\", hasAllAuthorities(\"USER\", \"ADMIN\"))\n                    authorize(\"/hasAllRoles/user_admin\", hasAllRoles(\"USER\", \"ADMIN\"))\n                    authorize(\"/hasAnyAuthority/user_admin\", hasAnyAuthority(\"USER\", \"ADMIN\"))\n                    authorize(\"/hasAnyRole/user_admin\", hasAnyRole(\"USER\", \"ADMIN\"))\n                    authorize(\"/hasAuthority/user\", hasAuthority(\"USER\"))\n                    authorize(\"/hasRole/user\", hasRole(\"USER\"))\n                    authorize(\"/permitAll\", authenticated)\n                }\n                httpBasic { }\n                rememberMe { }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun authorizationManagerFactory(): AuthorizationManagerFactory<RequestAuthorizationContext> {\n            val factory: AuthorizationManagerFactory<RequestAuthorizationContext> = mockk()\n            every { factory.authenticated() } returns this.authorizationManager\n            every { factory.denyAll() } returns this.authorizationManager\n            every { factory.fullyAuthenticated() } returns this.authorizationManager\n            every { factory.hasAllAuthorities(\"USER\", \"ADMIN\") } returns this.authorizationManager\n            every { factory.hasAllRoles(\"USER\", \"ADMIN\") } returns this.authorizationManager\n            every { factory.hasAnyAuthority(\"USER\", \"ADMIN\") } returns this.authorizationManager\n            every { factory.hasAnyRole(\"USER\", \"ADMIN\") } returns this.authorizationManager\n            every { factory.hasAuthority(any()) } returns this.authorizationManager\n            every { factory.hasRole(any()) } returns this.authorizationManager\n            every { factory.permitAll() } returns this.authorizationManager\n\n            return factory\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService = InMemoryUserDetailsManager(TestAuthentication.user())\n\n        @RestController\n        internal class OkController {\n            @GetMapping(\"/**\")\n            fun ok(): String {\n                return \"ok\"\n            }\n        }\n\n    }\n\n    @Test\n    fun `custom AuthorizationManagerFactory of Object`() {\n        this.spring.register(AuthorizationManagerFactoryObjectConfig::class.java).autowire()\n        val authzManagerFactory =\n            this.spring.context.getBean<AuthorizationManagerFactory<Object>>()\n        val authzManager = this.spring.context.getBean<AuthorizationManagerFactoryObjectConfig>().authorizationManager\n        every { authzManager.authorize(any(), any()) } returns AuthorizationDecision(true)\n\n        verify { authzManagerFactory.authenticated() }\n        verify { authzManagerFactory.denyAll() }\n        verify { authzManagerFactory.fullyAuthenticated() }\n        verify { authzManagerFactory.hasAllAuthorities(\"USER\", \"ADMIN\") }\n        verify { authzManagerFactory.hasAllRoles(\"USER\", \"ADMIN\") }\n        verify { authzManagerFactory.hasAnyAuthority(\"USER\", \"ADMIN\") }\n        verify { authzManagerFactory.hasAnyRole(\"USER\", \"ADMIN\") }\n        verify { authzManagerFactory.hasAuthority(\"USER\") }\n        verify { authzManagerFactory.hasRole(\"USER\") }\n        verify { authzManagerFactory.permitAll() }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AuthorizationManagerFactoryObjectConfig {\n        val authorizationManager: AuthorizationManager<Object> = mockk()\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/authenticated\", authenticated)\n                    authorize(\"/denyAll\", denyAll)\n                    authorize(\"/fullyAuthenticated\", fullyAuthenticated)\n                    authorize(\"/hasAllAuthorities/user_admin\", hasAllAuthorities(\"USER\", \"ADMIN\"))\n                    authorize(\"/hasAllRoles/user_admin\", hasAllRoles(\"USER\", \"ADMIN\"))\n                    authorize(\"/hasAnyAuthority/user_admin\", hasAnyAuthority(\"USER\", \"ADMIN\"))\n                    authorize(\"/hasAnyRole/user_admin\", hasAnyRole(\"USER\", \"ADMIN\"))\n                    authorize(\"/hasAuthority/user\", hasAuthority(\"USER\"))\n                    authorize(\"/hasRole/user\", hasRole(\"USER\"))\n                    authorize(\"/permitAll\", authenticated)\n                }\n                httpBasic {  }\n                rememberMe {  }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun authorizationManagerFactory(): AuthorizationManagerFactory<Object> {\n            val factory: AuthorizationManagerFactory<Object> = mockk()\n            every { factory.authenticated() } returns this.authorizationManager\n            every { factory.denyAll() } returns this.authorizationManager\n            every { factory.fullyAuthenticated() } returns this.authorizationManager\n            every { factory.hasAllAuthorities(\"USER\", \"ADMIN\") } returns this.authorizationManager\n            every { factory.hasAllRoles(\"USER\", \"ADMIN\") } returns this.authorizationManager\n            every { factory.hasAnyAuthority(\"USER\", \"ADMIN\") } returns this.authorizationManager\n            every { factory.hasAnyRole(\"USER\", \"ADMIN\") } returns this.authorizationManager\n            every { factory.hasAuthority(any()) } returns this.authorizationManager\n            every { factory.hasRole(any()) } returns this.authorizationManager\n            every { factory.permitAll() } returns this.authorizationManager\n\n            return factory\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService = InMemoryUserDetailsManager(TestAuthentication.user())\n\n        @RestController\n        internal class OkController {\n            @GetMapping(\"/**\")\n            fun ok(): String {\n                return \"ok\"\n            }\n        }\n    }\n\n    @Test\n    fun `custom AuthorizationManagerFactory of MethodInvocation`() {\n        this.spring.register(AuthorizationManagerFactoryMethodInvocationConfig::class.java).autowire()\n        val authzManagerFactory =\n            this.spring.context.getBean<AuthorizationManagerFactory<MethodInvocation>>()\n\n        verify(exactly = 0) { authzManagerFactory.authenticated() }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AuthorizationManagerFactoryMethodInvocationConfig {\n        val authorizationManager: AuthorizationManager<MethodInvocation> = mockk()\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/authenticated\", authenticated)\n                }\n                httpBasic {  }\n                rememberMe {  }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun authorizationManagerFactory(): AuthorizationManagerFactory<MethodInvocation> {\n            val factory: AuthorizationManagerFactory<MethodInvocation> = mockk()\n            every { factory.authenticated() } returns this.authorizationManager\n\n            return factory\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService = InMemoryUserDetailsManager(TestAuthentication.user())\n\n        @RestController\n        internal class OkController {\n            @GetMapping(\"/**\")\n            fun ok(): String {\n                return \"ok\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/CorsDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.assertj.core.api.Assertions.assertThatThrownBy\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.BeanCreationException\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.config.Customizer.withDefaults\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RequestMethod\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.cors.CorsConfiguration\nimport org.springframework.web.cors.CorsConfigurationSource\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [CorsDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass CorsDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `CORS when no MVC then exception`() {\n        assertThatThrownBy { this.spring.register(DefaultCorsConfig::class.java).autowire() }\n                .isInstanceOf(BeanCreationException::class.java)\n                .hasMessageContaining(\"Please ensure that you are using `@EnableWebMvc`, are publishing a `WebMvcConfigurer`, \" +\n                        \"or are publishing a `CorsConfigurationSource` bean.\")\n\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DefaultCorsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                cors { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `CORS when CORS configuration source bean then responds with CORS header`() {\n        this.spring.register(CorsCrossOriginBeanConfig::class.java, HomeController::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n        {\n            header(HttpHeaders.ORIGIN, \"https://example.com\")\n        }.andExpect {\n            header { exists(\"Access-Control-Allow-Origin\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebMvc\n    @EnableWebSecurity\n    open class CorsCrossOriginBeanConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                cors { }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun corsConfigurationSource(): CorsConfigurationSource {\n            val source = UrlBasedCorsConfigurationSource()\n            val corsConfiguration = CorsConfiguration()\n            corsConfiguration.allowedOrigins = listOf(\"*\")\n            corsConfiguration.allowedMethods = listOf(\n                    RequestMethod.GET.name,\n                    RequestMethod.POST.name)\n            source.registerCorsConfiguration(\"/**\", corsConfiguration)\n            return source\n        }\n    }\n\n    @Test\n    fun `CORS when disabled then response does not include CORS header`() {\n        this.spring.register(CorsDisabledConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n        {\n            header(HttpHeaders.ORIGIN, \"https://example.com\")\n        }.andExpect {\n            header { doesNotExist(\"Access-Control-Allow-Origin\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebMvc\n    @EnableWebSecurity\n    open class CorsDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http.cors(withDefaults())\n            http {\n                cors {\n                    disable()\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun corsConfigurationSource(): CorsConfigurationSource {\n            val source = UrlBasedCorsConfigurationSource()\n            val corsConfiguration = CorsConfiguration()\n            corsConfiguration.allowedOrigins = listOf(\"*\")\n            corsConfiguration.allowedMethods = listOf(\n                    RequestMethod.GET.name,\n                    RequestMethod.POST.name)\n            source.registerCorsConfiguration(\"/**\", corsConfiguration)\n            return source\n        }\n    }\n\n    @Test\n    fun `CORS when CORS configuration source dsl then responds with CORS header`() {\n        this.spring.register(CorsCrossOriginBeanConfig::class.java, HomeController::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n        {\n            header(HttpHeaders.ORIGIN, \"https://example.com\")\n        }.andExpect {\n            header { exists(\"Access-Control-Allow-Origin\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebMvc\n    @EnableWebSecurity\n    open class CorsCrossOriginSourceConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val source = UrlBasedCorsConfigurationSource()\n            val corsConfiguration = CorsConfiguration()\n            corsConfiguration.allowedOrigins = listOf(\"*\")\n            corsConfiguration.allowedMethods = listOf(\n                    RequestMethod.GET.name,\n                    RequestMethod.POST.name)\n            source.registerCorsConfiguration(\"/**\", corsConfiguration)\n            http {\n                cors {\n                    configurationSource = source\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @RestController\n    private class HomeController {\n        @GetMapping(\"/\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/CsrfDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport jakarta.servlet.http.HttpServletRequest\nimport jakarta.servlet.http.HttpServletResponse\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy\nimport org.springframework.security.web.csrf.*\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.post\nimport org.springframework.web.bind.annotation.PostMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [CsrfDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass CsrfDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `POST when CSRF enabled and no CSRF token then forbidden`() {\n        this.spring.register(DefaultCsrfConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n    }\n\n    @Test\n    fun `POST when CSRF enabled and CSRF token then status OK`() {\n        this.spring.register(DefaultCsrfConfig::class.java, BasicController::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\") {\n            with(csrf())\n        }.andExpect {\n            status { isOk() }\n        }\n\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DefaultCsrfConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                csrf { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `POST when CSRF disabled and no CSRF token then status OK`() {\n        this.spring.register(CsrfDisabledConfig::class.java, BasicController::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CsrfDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                csrf {\n                    disable()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `CSRF when custom CSRF token repository then repo used`() {\n        this.spring.register(CustomRepositoryConfig::class.java).autowire()\n        mockkObject(CustomRepositoryConfig.REPO)\n        every {\n            CustomRepositoryConfig.REPO.loadToken(any())\n        } returns DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"token\")\n\n\t\tthis.mockMvc.post(\"/test1\")\n\n\t\tverify(exactly = 1) { CustomRepositoryConfig.REPO.loadToken(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomRepositoryConfig {\n\n        companion object {\n            val REPO: CsrfTokenRepository = HttpSessionCsrfTokenRepository()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                csrf {\n                    csrfTokenRepository = REPO\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `CSRF when require CSRF protection matcher then CSRF protection on matching requests`() {\n        this.spring.register(RequireCsrfProtectionMatcherConfig::class.java, BasicController::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n\n        this.mockMvc.post(\"/test2\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RequireCsrfProtectionMatcherConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                csrf {\n                    requireCsrfProtectionMatcher = PathPatternRequestMatcher.pathPattern(\"/test1\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `CSRF when custom session authentication strategy then strategy used`() {\n        this.spring.register(CustomStrategyConfig::class.java).autowire()\n        mockkObject(CustomStrategyConfig.STRATEGY)\n        every { CustomStrategyConfig.STRATEGY.onAuthentication(any(), any(), any()) } returns Unit\n\n        this.mockMvc.perform(formLogin())\n\n        verify(exactly = 1) { CustomStrategyConfig.STRATEGY.onAuthentication(any(), any(), any()) }\n\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomStrategyConfig {\n\n        companion object {\n            var STRATEGY: SessionAuthenticationStrategy = NullAuthenticatedSessionStrategy()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin { }\n                csrf {\n                    sessionAuthenticationStrategy = STRATEGY\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n\n    @Test\n    fun `CSRF when ignoring request matchers then CSRF disabled on matching requests`() {\n        this.spring.register(IgnoringRequestMatchersConfig::class.java, BasicController::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n\n        this.mockMvc.post(\"/test2\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class IgnoringRequestMatchersConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                csrf {\n                    requireCsrfProtectionMatcher = PathPatternRequestMatcher.pathPattern(\"/**\")\n                    ignoringRequestMatchers(PathPatternRequestMatcher.pathPattern(\"/test2\"))\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `CSRF when ignoring request matchers pattern then CSRF disabled on matching requests`() {\n        this.spring.register(IgnoringRequestMatchersPatternConfig::class.java, BasicController::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\")\n            .andExpect {\n                status { isForbidden() }\n            }\n\n        this.mockMvc.post(\"/test2\")\n            .andExpect {\n                status { isOk() }\n            }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class IgnoringRequestMatchersPatternConfig {\n\n        @Bean\n        open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                csrf {\n                    requireCsrfProtectionMatcher = PathPatternRequestMatcher.pathPattern(\"/**\")\n                    ignoringRequestMatchers(\"/test2\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @RestController\n    internal class BasicController {\n        @PostMapping(\"/test1\")\n        fun test1():String {\n            return \"ok\"\n        }\n\n        @PostMapping(\"/test2\")\n        fun test2():String {\n            return \"ok\"\n        }\n    }\n\n    @Test\n    fun `CSRF when custom csrf token request handler then handler used`() {\n        this.spring.register(RequestHandlerConfig::class.java).autowire()\n        mockkObject(RequestHandlerConfig.HANDLER)\n        every { RequestHandlerConfig.HANDLER.handle(any(), any(), any()) } returns Unit\n\n        this.mockMvc.get(\"/test1\")\n\n        verify(exactly = 1) { RequestHandlerConfig.HANDLER.handle(any(), any(), any()) }\n    }\n\n    @Test\n    fun `POST when custom csrf token request handler then handler used`() {\n        this.spring.register(RequestHandlerConfig::class.java).autowire()\n        mockkObject(RequestHandlerConfig.HANDLER)\n        every { RequestHandlerConfig.HANDLER.handle(any(), any(), any()) } answers {\n            val request: HttpServletRequest = firstArg()\n            val response: HttpServletResponse = secondArg()\n            // Required for LazyCsrfTokenRepository\n            request.setAttribute(HttpServletResponse::class.java.name, response)\n        }\n        every { RequestHandlerConfig.HANDLER.resolveCsrfTokenValue(any(), any()) } returns \"token\"\n\n        this.mockMvc.post(\"/test2\")\n\n        verify(exactly = 1) { RequestHandlerConfig.HANDLER.handle(any(), any(), any()) }\n        verify(exactly = 1) { RequestHandlerConfig.HANDLER.resolveCsrfTokenValue(any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RequestHandlerConfig {\n\n        companion object {\n            var HANDLER: CsrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()\n        }\n\n        @Bean\n        open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                csrf {\n                    csrfTokenRequestHandler = HANDLER\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.assertj.core.api.Assertions.assertThatExceptionOfType\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.access.AccessDeniedException\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.User.withUsername\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [ExceptionHandlingDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ExceptionHandlingDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `request when exception handling enabled then returns forbidden`() {\n        this.spring.register(ExceptionHandlingConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class ExceptionHandlingConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                exceptionHandling { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `request when exception handling disabled then throws exception`() {\n        this.spring.register(ExceptionHandlingDisabledConfig::class.java).autowire()\n\n        assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n            this.mockMvc.get(\"/\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ExceptionHandlingDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                exceptionHandling {\n                    disable()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `exception handling when custom access denied page then redirects to custom page`() {\n        this.spring.register(AccessDeniedPageConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/admin\") {\n            with(user(withUsername(\"user\").password(\"password\").roles(\"USER\").build()))\n        }.andExpect {\n            status { isForbidden() }\n            forwardedUrl(\"/access-denied\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AccessDeniedPageConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/admin\", hasAuthority(\"ROLE_ADMIN\"))\n                    authorize(anyRequest, authenticated)\n                }\n                exceptionHandling {\n                    accessDeniedPage = \"/access-denied\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `exception handling when custom access denied handler then handler used`() {\n        this.spring.register(AccessDeniedHandlerConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/admin\") {\n            with(user(withUsername(\"user\").password(\"password\").roles(\"USER\").build()))\n        }.andExpect {\n            status { isForbidden() }\n            forwardedUrl(\"/access-denied\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AccessDeniedHandlerConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val customAccessDeniedHandler = AccessDeniedHandlerImpl()\n            customAccessDeniedHandler.setErrorPage(\"/access-denied\")\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/admin\", hasAuthority(\"ROLE_ADMIN\"))\n                    authorize(anyRequest, authenticated)\n                }\n                exceptionHandling {\n                    accessDeniedHandler = customAccessDeniedHandler\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `exception handling when default access denied handler for page then handlers used`() {\n        this.spring.register(AccessDeniedHandlerForConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/admin1\") {\n            with(user(withUsername(\"user\").password(\"password\").roles(\"USER\").build()))\n        }.andExpect {\n            status { isForbidden() }\n            forwardedUrl(\"/access-denied1\")\n        }\n\n        this.mockMvc.get(\"/admin2\") {\n            with(user(withUsername(\"user\").password(\"password\").roles(\"USER\").build()))\n        }.andExpect {\n            status { isForbidden() }\n            forwardedUrl(\"/access-denied2\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AccessDeniedHandlerForConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val customAccessDeniedHandler1 = AccessDeniedHandlerImpl()\n            customAccessDeniedHandler1.setErrorPage(\"/access-denied1\")\n            val customAccessDeniedHandler2 = AccessDeniedHandlerImpl()\n            customAccessDeniedHandler2.setErrorPage(\"/access-denied2\")\n            val builder = PathPatternRequestMatcher.withDefaults()\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/admin1\", hasAuthority(\"ROLE_ADMIN\"))\n                    authorize(\"/admin2\", hasAuthority(\"ROLE_ADMIN\"))\n                    authorize(anyRequest, authenticated)\n                }\n                exceptionHandling {\n                    defaultAccessDeniedHandlerFor(customAccessDeniedHandler1, builder.matcher(\"/admin1\"))\n                    defaultAccessDeniedHandlerFor(customAccessDeniedHandler2, builder.matcher(\"/admin2\"))\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `exception handling when custom authentication entry point then entry point used`() {\n        this.spring.register(AuthenticationEntryPointConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    status { isFound() }\n                    redirectedUrl(\"/custom-login\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AuthenticationEntryPointConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                exceptionHandling {\n                    authenticationEntryPoint = LoginUrlAuthenticationEntryPoint(\"/custom-login\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `exception handling when authentication entry point for page then entry points used`() {\n        this.spring.register(AuthenticationEntryPointForConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/secured1\")\n                .andExpect {\n                    status { isFound() }\n                    redirectedUrl(\"/custom-login1\")\n                }\n\n        this.mockMvc.get(\"/secured2\")\n                .andExpect {\n                    status { isFound() }\n                    redirectedUrl(\"/custom-login2\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class AuthenticationEntryPointForConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val customAuthenticationEntryPoint1 = LoginUrlAuthenticationEntryPoint(\"/custom-login1\")\n            val customAuthenticationEntryPoint2 = LoginUrlAuthenticationEntryPoint(\"/custom-login2\")\n            val builder = PathPatternRequestMatcher.withDefaults();\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                exceptionHandling {\n                    defaultAuthenticationEntryPointFor(customAuthenticationEntryPoint1, builder.matcher(\"/secured1\"))\n                    defaultAuthenticationEntryPointFor(customAuthenticationEntryPoint2, builder.matcher(\"/secured2\"))\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.Customizer.withDefaults\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource\nimport org.springframework.stereotype.Controller\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.post\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [FormLoginDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass FormLoginDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `login page when form login configured then default login page created`() {\n        this.spring.register(FormLoginConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/login\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Test\n    fun `login when success then redirects to home`() {\n        this.spring.register(FormLoginConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin())\n                .andExpect {\n                    status().isFound\n                    redirectedUrl(\"/\")\n                }\n    }\n\n    @Test\n    fun `login when failure then redirects to login page with error`() {\n        this.spring.register(FormLoginConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin().password(\"invalid\"))\n                .andExpect {\n                    status().isFound\n                    redirectedUrl(\"/login?error\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FormLoginConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `request when formLogin disabled does not provide login page`() {\n        this.spring.register(DisabledConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/login\")\n            .andExpect {\n                status { isNotFound() }\n            }\n\n        this.mockMvc.post(\"/login\") {\n            with(csrf())\n        }.andExpect {\n            status { isNotFound() }\n        }\n    }\n\n    @Configuration\n    @EnableWebMvc\n    @EnableWebSecurity\n    open class DisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http.formLogin(withDefaults())\n            http {\n                formLogin {\n                    disable()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `request when secure then redirects to default login page`() {\n        this.spring.register(AllSecuredConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    status { isFound() }\n                    redirectedUrl(\"/login\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AllSecuredConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `request when secure and custom login page then redirects to custom login page`() {\n        this.spring.register(LoginPageConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    status { isFound() }\n                    redirectedUrl(\"/log-in\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class LoginPageConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    loginPage = \"/log-in\"\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login when custom success handler then used`() {\n        this.spring.register(SuccessHandlerConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin())\n                .andExpect {\n                    status().isFound\n                    redirectedUrl(\"/success\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class SuccessHandlerConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    authenticationSuccessHandler = SimpleUrlAuthenticationSuccessHandler(\"/success\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login when custom failure handler then used`() {\n        this.spring.register(FailureHandlerConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin().password(\"invalid\"))\n                .andExpect {\n                    status().isFound\n                    redirectedUrl(\"/failure\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FailureHandlerConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    authenticationFailureHandler = SimpleUrlAuthenticationFailureHandler(\"/failure\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login when custom failure url then used`() {\n        this.spring.register(FailureHandlerConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin().password(\"invalid\"))\n                .andExpect {\n                    status().isFound\n                    redirectedUrl(\"/failure\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FailureUrlConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    failureUrl = \"/failure\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login when custom login processing url then used`() {\n        this.spring.register(LoginProcessingUrlConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin(\"/custom\"))\n                .andExpect {\n                    status().isFound\n                    redirectedUrl(\"/\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class LoginProcessingUrlConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    loginProcessingUrl = \"/custom\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login when default success url then redirected to url`() {\n        this.spring.register(DefaultSuccessUrlConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin())\n                .andExpect {\n                    status().isFound\n                    redirectedUrl(\"/custom\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DefaultSuccessUrlConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    defaultSuccessUrl(\"/custom\", true)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login when permit all then login page not protected`() {\n        this.spring.register(PermitAllConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/custom/login\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class PermitAllConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                formLogin {\n                    loginPage = \"/custom/login\"\n                    permitAll()\n                }\n            }\n            return http.build()\n        }\n\n        @Controller\n        class LoginController {\n            @GetMapping(\"/custom/login\")\n            fun loginPage() {}\n        }\n    }\n\n    @Test\n    fun `form login when custom authentication details source then used`() {\n        this.spring\n            .register(CustomAuthenticationDetailsSourceConfig::class.java, UserConfig::class.java)\n            .autowire()\n        mockkObject(CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE)\n        every {\n            CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE.buildDetails(any())\n        } returns mockk()\n\n        this.mockMvc.perform(formLogin())\n            .andExpect {\n                status().isFound\n                redirectedUrl(\"/\")\n            }\n\n        verify(exactly = 1) { CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE.buildDetails(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomUsernameParameterConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    usernameParameter = \"custom-username\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `form login when custom username parameter then used`() {\n        this.spring.register(CustomUsernameParameterConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin().userParameter(\"custom-username\"))\n                .andExpect(authenticated())\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomPasswordParameterConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    passwordParameter = \"custom-password\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `form login when custom password parameter then used`() {\n        this.spring.register(CustomPasswordParameterConfig::class.java, UserConfig::class.java).autowire()\n\n        this.mockMvc.perform(formLogin().passwordParam(\"custom-password\"))\n                .andExpect(authenticated())\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomAuthenticationDetailsSourceConfig {\n\n        companion object {\n            val AUTHENTICATION_DETAILS_SOURCE = WebAuthenticationDetailsSource()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {\n                    authenticationDetailsSource = AUTHENTICATION_DETAILS_SOURCE\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class UserConfig {\n        @Autowired\n        fun configureGlobal(auth: AuthenticationManagerBuilder) {\n            auth\n                    .inMemoryAuthentication()\n                    .withUser(User.withDefaultPasswordEncoder().username(\"user\").password(\"password\").roles(\"USER\"))\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/HeadersDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.header.writers.StaticHeadersWriter\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [HeadersDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass HeadersDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `headers when defaults enabled then default headers in response`() {\n        this.spring.register(DefaultHeadersConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, \"nosniff\") }\n            header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) }\n            header { string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\") }\n            header { string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\") }\n            header { string(HttpHeaders.EXPIRES, \"0\") }\n            header { string(HttpHeaders.PRAGMA, \"no-cache\") }\n            header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DefaultHeadersConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when feature policy configured then header in response`() {\n        this.spring.register(FeaturePolicyConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(\"Feature-Policy\", \"geolocation 'self'\") }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @Suppress(\"DEPRECATION\")\n    open class FeaturePolicyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    featurePolicy(policyDirectives = \"geolocation 'self'\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when permissions policy configured then header in response`() {\n        this.spring.register(PermissionsPolicyConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(\"Permissions-Policy\", \"geolocation=(self)\") }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class PermissionsPolicyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    permissionsPolicy {\n                        policy = \"geolocation=(self)\"\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `request when headers disabled then no security headers are in the response`() {\n        this.spring.register(HeadersDisabledConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { doesNotExist(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS) }\n                    header { doesNotExist(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS) }\n                    header { doesNotExist(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY) }\n                    header { doesNotExist(HttpHeaders.CACHE_CONTROL) }\n                    header { doesNotExist(HttpHeaders.EXPIRES) }\n                    header { doesNotExist(HttpHeaders.PRAGMA) }\n                    header { doesNotExist(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION) }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HeadersDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    disable()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `request when custom header writer then custom header in response`() {\n        this.spring.register(HeaderWriterConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(\"custom-header\", \"custom-value\") }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HeaderWriterConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    addHeaderWriter(StaticHeadersWriter(\"custom-header\", \"custom-value\"))\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpBasicDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic\nimport org.springframework.security.web.AuthenticationEntryPoint\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.HttpStatusEntryPoint\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests for [HttpBasicDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass HttpBasicDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `http basic when configured then insecure request cannot access`() {\n        this.spring.register(HttpBasicConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    status { isUnauthorized() }\n                }\n    }\n\n    @Test\n    fun `http basic when configured then response includes basic challenge`() {\n        this.spring.register(HttpBasicConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(\"WWW-Authenticate\", \"Basic realm=\\\"Realm\\\", charset=\\\"UTF-8\\\"\") }\n                }\n    }\n\n    @Test\n    fun `http basic when valid user then permitted`() {\n        this.spring.register(HttpBasicConfig::class.java, UserConfig::class.java, MainController::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"user\", \"password\"))\n        }.andExpect {\n            status { isOk() }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HttpBasicConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                httpBasic {}\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun httpBasicWhenCustomRealmThenUsed() {\n        this.spring.register(CustomRealmConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(\"WWW-Authenticate\", \"Basic realm=\\\"Custom Realm\\\", charset=\\\"UTF-8\\\"\") }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomRealmConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                httpBasic {\n                    realmName = \"Custom Realm\"\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `http basic when custom authentication entry point then used`() {\n        this.spring.register(CustomAuthenticationEntryPointConfig::class.java).autowire()\n        mockkObject(CustomAuthenticationEntryPointConfig.ENTRY_POINT)\n        every { CustomAuthenticationEntryPointConfig.ENTRY_POINT.commence(any(), any(), any()) } returns Unit\n\n        this.mockMvc.get(\"/\")\n\n        verify(exactly = 1) { CustomAuthenticationEntryPointConfig.ENTRY_POINT.commence(any(), any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomAuthenticationEntryPointConfig {\n\n        companion object {\n            val ENTRY_POINT: AuthenticationEntryPoint = HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                httpBasic {\n                    authenticationEntryPoint = ENTRY_POINT\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `http basic when custom authentication details source then used`() {\n        this.spring\n            .register(CustomAuthenticationDetailsSourceConfig::class.java, UserConfig::class.java, MainController::class.java)\n            .autowire()\n        mockkObject(CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE)\n        every {\n            CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE.buildDetails(any())\n        } returns mockk()\n\n        this.mockMvc.get(\"/\") {\n            with(httpBasic(\"username\", \"password\"))\n        }\n\n        verify(exactly = 1) { CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE.buildDetails(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomAuthenticationDetailsSourceConfig {\n\n        companion object {\n            val AUTHENTICATION_DETAILS_SOURCE = WebAuthenticationDetailsSource()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                httpBasic {\n                    authenticationDetailsSource = AUTHENTICATION_DETAILS_SOURCE\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class UserConfig {\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n\n    @RestController\n    class MainController {\n        @GetMapping(\"/\")\n        fun main():String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport jakarta.servlet.Filter\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ValueSource\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.authentication.ProviderManager\nimport org.springframework.security.authentication.TestingAuthenticationProvider\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic\nimport org.springframework.security.web.FilterChainProxy\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter\nimport org.springframework.security.web.util.matcher.RegexRequestMatcher\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.post\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [HttpSecurityDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass HttpSecurityDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `post when default security configured then CSRF prevents the request`() {\n        this.spring.register(DefaultSecurityConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n    }\n\n    @Test\n    fun `when default security configured then default headers are in the response`() {\n        this.spring.register(DefaultSecurityConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header {\n                string(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, \"nosniff\")\n            }\n            header {\n                string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name)\n            }\n            header {\n                string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\")\n            }\n            header {\n                string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\")\n            }\n            header {\n                string(HttpHeaders.EXPIRES, \"0\")\n            }\n            header {\n                string(HttpHeaders.PRAGMA, \"no-cache\")\n            }\n            header {\n                string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\")\n            }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DefaultSecurityConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            return http.build()\n        }\n\n        @Configuration\n        open class UserConfig {\n            @Bean\n            open fun userDetailsService(): UserDetailsService {\n                val userDetails = User.withDefaultPasswordEncoder()\n                        .username(\"user\")\n                        .password(\"password\")\n                        .roles(\"USER\")\n                        .build()\n                return InMemoryUserDetailsManager(userDetails)\n            }\n        }\n    }\n\n    @ParameterizedTest\n    @ValueSource(classes = [\n        SecurityRequestMatcherRequestsConfig::class,\n        SecurityRequestMatcherHttpRequestsConfig::class\n    ])\n    fun `request when it does not match the security request matcher then the security rules do not apply`(config: Class<*>) {\n        this.spring.register(config).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    status { isNotFound() }\n                }\n    }\n\n    @ParameterizedTest\n    @ValueSource(classes = [\n        SecurityRequestMatcherRequestsConfig::class,\n        SecurityRequestMatcherHttpRequestsConfig::class\n    ])\n    fun `request when it matches the security request matcher then the security rules apply`(config: Class<*>) {\n        this.spring.register(config).autowire()\n\n        this.mockMvc.get(\"/path\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class SecurityRequestMatcherRequestsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                securityMatcher(RegexRequestMatcher(\"/path\", null))\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class SecurityRequestMatcherHttpRequestsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                securityMatcher(RegexRequestMatcher(\"/path\", null))\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @ParameterizedTest\n    @ValueSource(classes = [\n        SecurityPatternMatcherRequestsConfig::class,\n        SecurityPatternMatcherHttpRequestsConfig::class\n    ])\n    fun `request when it does not match the security pattern matcher then the security rules do not apply`(config: Class<*>) {\n        this.spring.register(config).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    status { isNotFound() }\n                }\n    }\n\n    @ParameterizedTest\n    @ValueSource(classes = [\n        SecurityPatternMatcherRequestsConfig::class,\n        SecurityPatternMatcherHttpRequestsConfig::class\n    ])\n    fun `request when it matches the security pattern matcher then the security rules apply`(config: Class<*>) {\n        this.spring.register(config).autowire()\n\n        this.mockMvc.get(\"/path\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class SecurityPatternMatcherRequestsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                securityMatcher(\"/path\")\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class SecurityPatternMatcherHttpRequestsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                securityMatcher(\"/path\")\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @ParameterizedTest\n    @ValueSource(classes = [\n        MultiMatcherRequestsConfig::class,\n        MultiMatcherHttpRequestsConfig::class\n    ])\n    fun `security pattern matcher when used with security request matcher then both apply`(config: Class<*>) {\n        this.spring.register(config).autowire()\n\n        this.mockMvc.get(\"/path1\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n\n        this.mockMvc.get(\"/path2\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n\n        this.mockMvc.get(\"/path3\")\n                .andExpect {\n                    status { isNotFound() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class MultiMatcherRequestsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                securityMatcher(\"/path1\")\n                securityMatcher(RegexRequestMatcher(\"/path2\", null))\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class MultiMatcherHttpRequestsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                securityMatcher(\"/path1\")\n                securityMatcher(RegexRequestMatcher(\"/path2\", null))\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @ParameterizedTest\n    @ValueSource(classes = [\n        AuthenticationManagerRequestsConfig::class,\n        AuthenticationManagerHttpRequestsConfig::class\n    ])\n    fun `authentication manager when configured in DSL then used`(config: Class<*>) {\n        this.spring.register(config).autowire()\n        mockkObject(AuthenticationManagerConfig.AUTHENTICATION_MANAGER)\n        every {\n            AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any())\n        } returns TestingAuthenticationToken(\"user\", \"test\", \"ROLE_USER\")\n        val request = MockMvcRequestBuilders.get(\"/\")\n            .with(httpBasic(\"user\", \"password\"))\n        this.mockMvc.perform(request)\n        verify(exactly = 1) { AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }\n    }\n\n    object AuthenticationManagerConfig {\n        val AUTHENTICATION_MANAGER: AuthenticationManager = ProviderManager(TestingAuthenticationProvider())\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthenticationManagerRequestsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authenticationManager = AuthenticationManagerConfig.AUTHENTICATION_MANAGER\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthenticationManagerHttpRequestsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authenticationManager = AuthenticationManagerConfig.AUTHENTICATION_MANAGER\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `HTTP security when custom filter configured then custom filter added to filter chain`() {\n        this.spring.register(CustomFilterConfig::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filters: List<Filter>? = filterChain.getFilters(\"/\")\n\n        assertThat(filters).anyMatch { it is CustomFilter }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomFilterConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                addFilterAt(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java)\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `HTTP security when custom filter configured with reified variant then custom filter added to filter chain`() {\n        this.spring.register(CustomFilterConfigReified::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filters: List<Filter>? = filterChain.getFilters(\"/\")\n\n        assertThat(filters).anyMatch { it is CustomFilter }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomFilterConfigReified {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                addFilterAt<UsernamePasswordAuthenticationFilter>(CustomFilter())\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `HTTP security when custom filter configured then custom filter added after specific filter to filter chain`() {\n        this.spring.register(CustomFilterAfterConfig::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filters: List<Class<out Filter>> = filterChain.getFilters(\"/\")!!.map { it.javaClass }\n\n        assertThat(filters).containsSubsequence(\n            UsernamePasswordAuthenticationFilter::class.java,\n            CustomFilter::class.java\n        )\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomFilterAfterConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                addFilterAfter(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java)\n                formLogin {}\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `HTTP security when custom filter configured with reified variant then custom filter added after specific filter to filter chain`() {\n        this.spring.register(CustomFilterAfterConfigReified::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filterClasses: List<Class<out Filter>> = filterChain.getFilters(\"/\")!!.map { it.javaClass }\n\n        assertThat(filterClasses).containsSubsequence(\n            UsernamePasswordAuthenticationFilter::class.java,\n            CustomFilter::class.java\n        )\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomFilterAfterConfigReified{\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                addFilterAfter<UsernamePasswordAuthenticationFilter>(CustomFilter())\n                formLogin { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `HTTP security when custom filter configured then custom filter added before specific filter to filter chain`() {\n        this.spring.register(CustomFilterBeforeConfig::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filters: List<Class<out Filter>> = filterChain.getFilters(\"/\")!!.map { it.javaClass }\n\n        assertThat(filters).containsSubsequence(\n            CustomFilter::class.java,\n            UsernamePasswordAuthenticationFilter::class.java\n        )\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomFilterBeforeConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                addFilterBefore(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java)\n                formLogin {}\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `HTTP security when custom filter configured with reified variant then custom filter added before specific filter to filter chain`() {\n        this.spring.register(CustomFilterBeforeConfigReified::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filterClasses: List<Class<out Filter>> = filterChain.getFilters(\"/\")!!.map { it.javaClass }\n\n        assertThat(filterClasses).containsSubsequence(\n            CustomFilter::class.java,\n            UsernamePasswordAuthenticationFilter::class.java\n        )\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomFilterBeforeConfigReified{\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                addFilterBefore<UsernamePasswordAuthenticationFilter>(CustomFilter())\n                formLogin { }\n            }\n            return http.build()\n        }\n    }\n\n    class CustomFilter : UsernamePasswordAuthenticationFilter()\n\n    @Test\n    fun `HTTP security when apply custom security configurer then custom filter added to filter chain`() {\n        this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filterClasses: List<Class<out Filter>> = filterChain.getFilters(\"/\")!!.map { it.javaClass }\n\n        assertThat(filterClasses).contains(\n            CustomFilter::class.java\n        )\n    }\n\n    @Test\n    fun `HTTP security when apply custom security configurer using with then custom filter added to filter chain`() {\n        this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filterClasses: List<Class<out Filter>> = filterChain.getFilters(\"/\")!!.map { it.javaClass }\n\n        assertThat(filterClasses).contains(\n            CustomFilter::class.java\n        )\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomSecurityConfigurerConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                apply(CustomSecurityConfigurer<HttpSecurity>()) {\n                    filter = CustomFilter()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomSecurityConfigurerUsingWithConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                with(CustomSecurityConfigurer<HttpSecurity>()) {\n                    filter = CustomFilter()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    class CustomSecurityConfigurer<H : HttpSecurityBuilder<H>> : AbstractHttpConfigurer<CustomSecurityConfigurer<H>, H>() {\n        var filter: Filter? = null\n        override fun init(builder: H) {\n            filter = filter ?: UsernamePasswordAuthenticationFilter()\n        }\n\n        override fun configure(builder: H) {\n            builder.addFilterBefore(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java)\n        }\n    }\n\n    @Test\n    fun `HTTP security when apply form login using with from custom security configurer then filter added to filter chain`() {\n        this.spring.register(CustomDslUsingWithConfig::class.java).autowire()\n\n        val filterChain = spring.context.getBean(FilterChainProxy::class.java)\n        val filterClasses: List<Class<out Filter>> = filterChain.getFilters(\"/\")!!.map { it.javaClass }\n\n        assertThat(filterClasses).contains(\n            UsernamePasswordAuthenticationFilter::class.java\n        )\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class CustomDslUsingWithConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                with(CustomDslFormLogin()) {\n                    formLogin = true\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n    }\n\n    class CustomDslFormLogin: AbstractHttpConfigurer<CustomDslFormLogin, HttpSecurity>() {\n\n        var formLogin = false\n\n        override fun init(builder: HttpSecurity) {\n            if (formLogin) {\n                builder.formLogin {  }\n            }\n        }\n\n    }\n\n    @Test\n    fun `HTTP security when Dsl Bean`() {\n        this.spring.register(DslBeanConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n            .andExpect {\n                header {\n                    string(\"Content-Security-Policy\", \"object-src 'none'\")\n                }\n            }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class DslBeanConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                httpBasic { }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun headersDsl(): HeadersDsl.() -> Unit {\n            return {\n                contentSecurityPolicy {\n                    policyDirectives = \"object-src 'none'\"\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpsRedirectDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean\nimport org.springframework.security.web.PortMapperImpl\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\nimport java.net.URI\nimport java.util.*\n\n/**\n * Tests for [HttpsRedirectDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass HttpsRedirectDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `request when matches redirect to HTTPS matcher then redirects to HTTPS`() {\n        this.spring.register(HttpRedirectMatcherConfig::class.java, UsePathPatternConfig::class.java).autowire()\n\n        val result = this.mockMvc.get(\"/secure\")\n            .andExpect {\n                status { is3xxRedirection() }\n            }.andReturn()\n\n        val location = result.response.getHeader(HttpHeaders.LOCATION)?.let { URI.create(it) }\n        assertThat(location?.scheme).isEqualTo(\"https\")\n    }\n\n    @Test\n    fun `request when does not match redirect to HTTPS matcher then does not redirect`() {\n        this.spring.register(HttpRedirectMatcherConfig::class.java, UsePathPatternConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n            .andExpect {\n                status { isNotFound() }\n            }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class HttpRedirectMatcherConfig {\n        @Bean\n        open fun springFilterChain(http: HttpSecurity, path: PathPatternRequestMatcher.Builder): SecurityFilterChain {\n            http {\n                redirectToHttps {\n                    requestMatchers = arrayOf(path.matcher(\"/secure\"))\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class UsePathPatternConfig {\n        @Bean\n        open fun requestMatcherBuilder() = PathPatternRequestMatcherBuilderFactoryBean()\n    }\n\n    @Test\n    fun `request when port mapper configured then redirected to HTTPS port`() {\n        this.spring.register(PortMapperConfig::class.java).autowire()\n\n        val result = this.mockMvc.get(\"http://localhost:543\")\n            .andExpect {\n                status {\n                    is3xxRedirection()\n                }\n            }.andReturn()\n\n        val location = result.response.getHeader(HttpHeaders.LOCATION)?.let { URI.create(it) }\n        assertThat(location?.scheme).isEqualTo(\"https\")\n        assertThat(location?.port).isEqualTo(123)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class PortMapperConfig {\n        @Bean\n        open fun springFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val customPortMapper = PortMapperImpl()\n            customPortMapper.setPortMappings(Collections.singletonMap(\"543\", \"123\"))\n            http {\n                portMapper {\n                    portMapper = customPortMapper\n                }\n                redirectToHttps { }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/LogoutDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport jakarta.servlet.http.HttpServletRequest\nimport jakarta.servlet.http.HttpServletResponse\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.mock.web.MockHttpSession\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.context.SecurityContextHolder\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.logout.LogoutHandler\nimport org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.post\n\n/**\n * Tests for [LogoutDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass LogoutDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `logout when custom logout url then custom url used`() {\n        this.spring.register(CustomLogoutUrlConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/custom/logout\") {\n            with(csrf())\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login?logout\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomLogoutUrlConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    logoutUrl = \"/custom/logout\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when custom logout request matcher then custom request matcher used`() {\n        this.spring.register(CustomLogoutRequestMatcherConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/custom/logout\") {\n            with(csrf())\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login?logout\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomLogoutRequestMatcherConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    logoutRequestMatcher = PathPatternRequestMatcher.pathPattern(\"/custom/logout\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when custom success url then redirects to success url`() {\n        this.spring.register(SuccessUrlConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/logout\") {\n            with(csrf())\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class SuccessUrlConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    logoutSuccessUrl = \"/login\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when custom success handler then redirects to success url`() {\n        this.spring.register(SuccessHandlerConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/logout\") {\n            with(csrf())\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class SuccessHandlerConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    logoutSuccessHandler = SimpleUrlLogoutSuccessHandler()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when permit all then logout allowed`() {\n        this.spring.register(PermitAllConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/custom/logout\") {\n            with(csrf())\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login?logout\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class PermitAllConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                logout {\n                    logoutUrl = \"/custom/logout\"\n                    permitAll()\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when clear authentication false then authentication not cleared`() {\n        this.spring.register(ClearAuthenticationFalseConfig::class.java).autowire()\n        val currentContext = SecurityContextHolder.createEmptyContext()\n        currentContext.authentication = TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\")\n        val currentSession = MockHttpSession()\n        currentSession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, currentContext)\n\n        this.mockMvc.post(\"/logout\") {\n            with(csrf())\n            session = currentSession\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login?logout\")\n        }\n\n        assertThat(currentContext.authentication).isNotNull\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ClearAuthenticationFalseConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    clearAuthentication = false\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when invalidate http session false then session not invalidated`() {\n        this.spring.register(InvalidateHttpSessionFalseConfig::class.java).autowire()\n        val currentSession = MockHttpSession()\n\n        this.mockMvc.post(\"/logout\") {\n            with(csrf())\n            session = currentSession\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login?logout\")\n        }\n\n        assertThat(currentSession.isInvalid).isFalse()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class InvalidateHttpSessionFalseConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    invalidateHttpSession = false\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when delete cookies then cookies are cleared`() {\n        this.spring.register(DeleteCookiesConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/logout\") {\n            with(csrf())\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login?logout\")\n            cookie { maxAge(\"remove\", 0) }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DeleteCookiesConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    deleteCookies(\"remove\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when default logout success handler for request then custom handler used`() {\n        this.spring.register(DefaultLogoutSuccessHandlerForConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/logout/default\") {\n            with(csrf())\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login?logout\")\n        }\n\n        this.mockMvc.post(\"/logout/custom\") {\n            with(csrf())\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DefaultLogoutSuccessHandlerForConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    logoutRequestMatcher = PathPatternRequestMatcher.pathPattern(\"/logout/**\")\n                    defaultLogoutSuccessHandlerFor(SimpleUrlLogoutSuccessHandler(),\n                        PathPatternRequestMatcher.pathPattern(\"/logout/custom\"))\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `logout when custom logout handler then custom handler used`() {\n        this.spring.register(CustomLogoutHandlerConfig::class.java).autowire()\n       mockkObject(CustomLogoutHandlerConfig.HANDLER)\n        every { CustomLogoutHandlerConfig.HANDLER.logout(any(), any(), any()) } returns Unit\n\n        this.mockMvc.post(\"/logout\") {\n            with(csrf())\n        }\n\n        verify(exactly = 1) { CustomLogoutHandlerConfig.HANDLER.logout(any(), any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomLogoutHandlerConfig {\n\n        companion object {\n            val HANDLER: LogoutHandler = NoopLogoutHandler()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                logout {\n                    addLogoutHandler(HANDLER)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    class NoopLogoutHandler: LogoutHandler {\n        override fun logout(\n            request: HttpServletRequest,\n            response: HttpServletResponse,\n            authentication: Authentication?\n        ) { }\n\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/OAuth2ClientDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest\nimport org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository\nimport org.springframework.security.oauth2.core.OAuth2AccessToken\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [OAuth2ClientDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass OAuth2ClientDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `oauth2Client when custom client registration repository then bean is not required`() {\n        this.spring.register(ClientRepoConfig::class.java).autowire()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ClientRepoConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Client {\n                    clientRegistrationRepository = InMemoryClientRegistrationRepository(\n                            CommonOAuth2Provider.GOOGLE\n                                    .getBuilder(\"google\").clientId(\"clientId\").clientSecret(\"clientSecret\")\n                                    .build()\n                    )\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `oauth2Client when custom authorized client repository then repository used`() {\n        this.spring.register(ClientRepositoryConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(ClientRepositoryConfig.REQUEST_REPOSITORY)\n        mockkObject(ClientRepositoryConfig.CLIENT)\n        mockkObject(ClientRepositoryConfig.CLIENT_REPOSITORY)\n        val authorizationRequest = OAuth2AuthorizationRequest\n                .authorizationCode()\n                .state(\"test\")\n                .clientId(\"clientId\")\n                .authorizationUri(\"https://test\")\n                .redirectUri(\"http://localhost/callback\")\n                .attributes(mapOf(Pair(OAuth2ParameterNames.REGISTRATION_ID, \"registrationId\")))\n                .build()\n        every {\n            ClientRepositoryConfig.REQUEST_REPOSITORY.loadAuthorizationRequest(any())\n        } returns authorizationRequest\n        every {\n            ClientRepositoryConfig.REQUEST_REPOSITORY.removeAuthorizationRequest(any(), any())\n        } returns authorizationRequest\n        every {\n            ClientRepositoryConfig.CLIENT.getTokenResponse(any())\n        } returns OAuth2AccessTokenResponse\n            .withToken(\"token\")\n            .tokenType(OAuth2AccessToken.TokenType.BEARER)\n            .build()\n        every {\n            ClientRepositoryConfig.CLIENT_REPOSITORY.saveAuthorizedClient(any(), any(), any(), any())\n        } returns Unit\n\n        this.mockMvc.get(\"/callback\") {\n            param(\"state\", \"test\")\n            param(\"code\", \"123\")\n        }\n\n        verify(exactly = 1) { ClientRepositoryConfig.CLIENT_REPOSITORY.saveAuthorizedClient(any(), any(), any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ClientRepositoryConfig {\n\n        companion object {\n            val REQUEST_REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> =\n                HttpSessionOAuth2AuthorizationRequestRepository()\n            val CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> =\n                RestClientAuthorizationCodeTokenResponseClient()\n            val CLIENT_REPOSITORY: OAuth2AuthorizedClientRepository = HttpSessionOAuth2AuthorizedClientRepository()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Client {\n                    authorizedClientRepository = CLIENT_REPOSITORY\n                    authorizationCodeGrant {\n                        authorizationRequestRepository = REQUEST_REPOSITORY\n                        accessTokenResponseClient = CLIENT\n                    }\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ClientRegistrationRepository {\n            return InMemoryClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\")\n                            .registrationId(\"registrationId\")\n                            .clientId(\"clientId\")\n                            .clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/OAuth2LoginDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.post\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource\n\n/**\n * Tests for [OAuth2LoginDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass OAuth2LoginDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `oauth2Login when custom client registration repository then bean is not required`() {\n        this.spring.register(ClientRepoConfig::class.java).autowire()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ClientRepoConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login {\n                    clientRegistrationRepository = InMemoryClientRegistrationRepository(\n                            CommonOAuth2Provider.GOOGLE\n                                    .getBuilder(\"google\").clientId(\"clientId\").clientSecret(\"clientSecret\")\n                                    .build()\n                    )\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login page when oAuth2Login configured then default login page created`() {\n        this.spring.register(OAuth2LoginConfig::class.java, ClientConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/login\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class OAuth2LoginConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login page when custom login page then redirected to custom page`() {\n        this.spring.register(LoginPageConfig::class.java, ClientConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/custom-login\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class LoginPageConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login {\n                    loginPage = \"/custom-login\"\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        class LoginController {\n            @GetMapping(\"/custom-login\")\n            fun loginPage():String {\n                return \"ok\"\n            }\n        }\n    }\n\n    @Test\n    fun `oauth2Login when custom authentication details source then used`() {\n        this.spring\n            .register(CustomAuthenticationDetailsSourceConfig::class.java, ClientConfig::class.java)\n            .autowire()\n        mockkObject(CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE)\n        every {\n            CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE.buildDetails(any())\n        } returns mockk()\n        mockkObject(CustomAuthenticationDetailsSourceConfig.AUTHORIZATION_REQUEST_REPOSITORY)\n        every {\n            CustomAuthenticationDetailsSourceConfig.AUTHORIZATION_REQUEST_REPOSITORY.removeAuthorizationRequest(any(), any())\n        } returns OAuth2AuthorizationRequest.authorizationCode()\n            .authorizationUri(\"/\")\n            .clientId(\"clientId\")\n            .redirectUri(\"/\")\n            .attributes { attributes -> attributes[OAuth2ParameterNames.REGISTRATION_ID] = \"google\" }\n            .build()\n\n        this.mockMvc.post(\"/login/oauth2/code/google\") {\n            param(OAuth2ParameterNames.CODE, \"code\")\n            param(OAuth2ParameterNames.STATE, \"state\")\n            with(csrf())\n        }\n        .andExpect {\n            status { is3xxRedirection() }\n        }\n\n        verify(exactly = 1) { CustomAuthenticationDetailsSourceConfig.AUTHENTICATION_DETAILS_SOURCE.buildDetails(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomAuthenticationDetailsSourceConfig {\n\n        companion object {\n            val AUTHENTICATION_DETAILS_SOURCE = WebAuthenticationDetailsSource()\n            val AUTHORIZATION_REQUEST_REPOSITORY = HttpSessionOAuth2AuthorizationRequestRepository()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login {\n                    authenticationDetailsSource = AUTHENTICATION_DETAILS_SOURCE\n                    authorizationEndpoint {\n                        authorizationRequestRepository = AUTHORIZATION_REQUEST_REPOSITORY\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ClientRegistrationRepository {\n            return InMemoryClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\").clientId(\"clientId\").clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/OAuth2ResourceServerDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport jakarta.servlet.http.HttpServletRequest\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.BeanCreationException\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.authentication.AuthenticationManagerResolver\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames.SUB\nimport org.springframework.security.oauth2.jwt.Jwt\nimport org.springframework.security.oauth2.jwt.JwtDecoder\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken\nimport org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver\nimport org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver\nimport org.springframework.security.web.AuthenticationEntryPoint\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.access.AccessDeniedHandler\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl\nimport org.springframework.security.web.authentication.HttpStatusEntryPoint\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [OAuth2ResourceServerDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass OAuth2ResourceServerDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    private val JWT: Jwt = Jwt.withTokenValue(\"token\")\n            .header(\"alg\", \"none\")\n            .claim(SUB, \"user\")\n            .build()\n\n    @Test\n    fun `oauth2Resource server when custom entry point then entry point used`() {\n        this.spring.register(EntryPointConfig::class.java).autowire()\n        mockkObject(EntryPointConfig.ENTRY_POINT)\n        every { EntryPointConfig.ENTRY_POINT.commence(any(), any(), any()) } returns Unit\n\n        this.mockMvc.get(\"/\")\n\n        verify(exactly = 1) { EntryPointConfig.ENTRY_POINT.commence(any(), any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class EntryPointConfig {\n\n        companion object {\n            val ENTRY_POINT: AuthenticationEntryPoint = HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    authenticationEntryPoint = ENTRY_POINT\n                    jwt { }\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun jwtDecoder(): JwtDecoder = mockk()\n    }\n\n    @Test\n    fun `oauth2Resource server when custom bearer token resolver then resolver used`() {\n        this.spring.register(BearerTokenResolverConfig::class.java).autowire()\n        mockkObject(BearerTokenResolverConfig.RESOLVER)\n        mockkObject(BearerTokenResolverConfig.DECODER)\n        every { BearerTokenResolverConfig.RESOLVER.resolve(any()) } returns \"anything\"\n        every { BearerTokenResolverConfig.DECODER.decode(any()) } returns JWT\n\n        this.mockMvc.get(\"/\")\n\n        verify(exactly = 1) { BearerTokenResolverConfig.RESOLVER.resolve(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class BearerTokenResolverConfig {\n\n        companion object {\n            val RESOLVER: BearerTokenResolver = DefaultBearerTokenResolver()\n            val DECODER: JwtDecoder = MockJwtDecoder()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    bearerTokenResolver = RESOLVER\n                    jwt { }\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun jwtDecoder(): JwtDecoder = DECODER\n    }\n\n    class MockJwtDecoder: JwtDecoder {\n        override fun decode(token: String): Jwt {\n            return Jwt.withTokenValue(\"token\")\n                .header(\"alg\", \"none\")\n                .claim(SUB, \"user\")\n                .build()\n        }\n\n    }\n\n    @Test\n    fun `oauth2Resource server when custom access denied handler then handler used`() {\n        this.spring.register(AccessDeniedHandlerConfig::class.java).autowire()\n        mockkObject(AccessDeniedHandlerConfig.DENIED_HANDLER)\n        mockkObject(AccessDeniedHandlerConfig.DECODER)\n        every {\n            AccessDeniedHandlerConfig.DECODER.decode(any())\n        } returns JWT\n        every {\n            AccessDeniedHandlerConfig.DENIED_HANDLER.handle(any(), any(), any())\n        } returns Unit\n\n        this.mockMvc.get(\"/\") {\n            header(\"Authorization\", \"Bearer token\")\n        }\n\n        verify(exactly = 1) { AccessDeniedHandlerConfig.DENIED_HANDLER.handle(any(), any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AccessDeniedHandlerConfig {\n\n        companion object {\n            val DECODER: JwtDecoder = MockJwtDecoder()\n            val DENIED_HANDLER: AccessDeniedHandler = AccessDeniedHandlerImpl()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, denyAll)\n                }\n                oauth2ResourceServer {\n                    accessDeniedHandler = DENIED_HANDLER\n                    jwt { }\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun jwtDecoder(): JwtDecoder = DECODER\n    }\n\n    @Test\n    fun `oauth2Resource server when custom authentication manager resolver then resolver used`() {\n        this.spring.register(AuthenticationManagerResolverConfig::class.java).autowire()\n        mockkObject(AuthenticationManagerResolverConfig.RESOLVER)\n        every {\n            AuthenticationManagerResolverConfig.RESOLVER.resolve(any())\n        } returns MockAuthenticationManager(JwtAuthenticationToken(JWT))\n\n        this.mockMvc.get(\"/\") {\n            header(\"Authorization\", \"Bearer token\")\n        }\n\n        verify(exactly = 1) { AuthenticationManagerResolverConfig.RESOLVER.resolve(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthenticationManagerResolverConfig {\n\n        companion object {\n            val RESOLVER: AuthenticationManagerResolver<HttpServletRequest> =\n                JwtIssuerAuthenticationManagerResolver.fromTrustedIssuers (\"issuer\")\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    authenticationManagerResolver = RESOLVER\n                }\n            }\n            return http.build()\n        }\n    }\n\n    class MockAuthenticationManager(var authentication: Authentication) : AuthenticationManager {\n\n        override fun authenticate(authentication: Authentication): Authentication {\n            return this.authentication\n        }\n\n    }\n\n    @Test\n    fun `oauth2Resource server when custom authentication manager resolver and opaque then exception`() {\n        Assertions.assertThatExceptionOfType(BeanCreationException::class.java)\n                .isThrownBy { spring.register(AuthenticationManagerResolverAndOpaqueConfig::class.java).autowire() }\n                .withMessageContaining(\"authenticationManagerResolver\")\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthenticationManagerResolverAndOpaqueConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    authenticationManagerResolver = mockk()\n                    opaqueToken { }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcBackChannelLogoutHandler\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry\nimport org.springframework.security.oauth2.client.registration.ClientRegistration\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.logout.LogoutHandler\nimport org.springframework.test.util.ReflectionTestUtils\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.post\n\n/**\n * Tests for [OAuth2ClientDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass OidcLogoutDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `oidcLogout when invalid token then errors`() {\n        this.spring.register(ClientRepositoryConfig::class.java).autowire()\n        val clientRegistration = this.spring.context.getBean(ClientRegistration::class.java)\n        this.mockMvc.post(\"/logout/connect/back-channel/\" + clientRegistration.registrationId) {\n            param(\"logout_token\", \"token\")\n        }.andExpect { status { isBadRequest() } }\n        val chain: SecurityFilterChain = this.spring.context.getBean(SecurityFilterChain::class.java)\n        for (filter in chain.filters) {\n            if (filter.javaClass.simpleName.equals(\"OidcBackChannelLogoutFilter\")) {\n                val logoutHandler = ReflectionTestUtils.getField(filter, \"logoutHandler\") as LogoutHandler\n                val backChannelLogoutHandler = ReflectionTestUtils.getField(logoutHandler, \"left\") as LogoutHandler\n                var cookieName = ReflectionTestUtils.getField(backChannelLogoutHandler, \"sessionCookieName\") as String\n                assert(cookieName.equals(\"SESSION\"))\n            }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ClientRepositoryConfig {\n\n        private val sessionRegistry = InMemoryOidcSessionRegistry()\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login { }\n                oidcLogout {\n                    backChannel { }\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun oidcLogoutHandler(): OidcBackChannelLogoutHandler {\n            val logoutHandler = OidcBackChannelLogoutHandler(this.sessionRegistry)\n            logoutHandler.setSessionCookieName(\"SESSION\");\n            return logoutHandler;\n        }\n\n        @Bean\n        open fun clientRegistration(): ClientRegistration {\n            return TestClientRegistrations.clientRegistration().build()\n        }\n\n        @Bean\n        open fun clientRegistrationRepository(clientRegistration: ClientRegistration): ClientRegistrationRepository {\n            return InMemoryClientRegistrationRepository(clientRegistration)\n        }\n    }\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/OneTimeTokenLoginDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.justRun\nimport io.mockk.mockk\nimport io.mockk.verify\nimport jakarta.servlet.http.HttpServletRequest\nimport jakarta.servlet.http.HttpServletResponse\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.context.annotation.Import\nimport org.springframework.security.authentication.ott.DefaultOneTimeToken\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest\nimport org.springframework.security.authentication.ott.OneTimeToken\nimport org.springframework.security.authentication.ott.OneTimeTokenService\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.PasswordEncodedUser\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler\nimport org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport java.time.Duration\nimport java.time.Instant\n\n/**\n * Tests for [OneTimeTokenLoginDsl]\n *\n * @author Max Batischev\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass OneTimeTokenLoginDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    private lateinit var mockMvc: MockMvc\n\n    @Autowired(required = false)\n    private lateinit var resolver: GenerateOneTimeTokenRequestResolver\n\n    @Autowired(required = false)\n    private lateinit var tokenService: OneTimeTokenService\n\n    @Autowired(required = false)\n    private lateinit var tokenGenerationSuccessHandler: OneTimeTokenGenerationSuccessHandler\n\n    @Test\n    fun `oneTimeToken when correct token then can authenticate`() {\n        spring.register(OneTimeTokenConfig::class.java).autowire()\n        this.mockMvc.perform(\n            MockMvcRequestBuilders.post(\"/ott/generate\").param(\"username\", \"user\")\n                .with(SecurityMockMvcRequestPostProcessors.csrf())\n        ).andExpectAll(\n            MockMvcResultMatchers\n                .status()\n                .isFound(),\n            MockMvcResultMatchers\n                .redirectedUrl(\"/login/ott\")\n        )\n\n        val token = getLastToken().tokenValue\n\n        this.mockMvc.perform(\n            MockMvcRequestBuilders.post(\"/login/ott\").param(\"token\", token)\n                .with(SecurityMockMvcRequestPostProcessors.csrf())\n        )\n            .andExpectAll(\n                MockMvcResultMatchers.status().isFound(),\n                MockMvcResultMatchers.redirectedUrl(\"/\"),\n                SecurityMockMvcResultMatchers.authenticated()\n            )\n    }\n\n    @Test\n    fun `oneTimeToken when different authentication urls then can authenticate`() {\n        spring.register(OneTimeTokenDifferentUrlsConfig::class.java).autowire()\n        this.mockMvc.perform(\n            MockMvcRequestBuilders.post(\"/generateurl\").param(\"username\", \"user\")\n                .with(SecurityMockMvcRequestPostProcessors.csrf())\n        )\n            .andExpectAll(MockMvcResultMatchers.status().isFound(), MockMvcResultMatchers.redirectedUrl(\"/redirected\"))\n\n        val token = getLastToken().tokenValue\n\n        this.mockMvc.perform(\n            MockMvcRequestBuilders.post(\"/loginprocessingurl\").param(\"token\", token)\n                .with(SecurityMockMvcRequestPostProcessors.csrf())\n        )\n            .andExpectAll(\n                MockMvcResultMatchers.status().isFound(),\n                MockMvcResultMatchers.redirectedUrl(\"/authenticated\"),\n                SecurityMockMvcResultMatchers.authenticated()\n            )\n    }\n\n    @Test\n    fun `oneTimeToken when custom impls set then used`() {\n        spring.register(OneTimeTokenConfigWithCustomImpls::class.java).autowire()\n        val expectedGenerateRequest = GenerateOneTimeTokenRequest(\"username-123\", Duration.ofMinutes(10));\n        val ott = DefaultOneTimeToken(\"token-123\", expectedGenerateRequest.username, Instant.now().plus(expectedGenerateRequest.expiresIn))\n        every { resolver.resolve(any()) } returns expectedGenerateRequest\n        every { tokenService.generate(expectedGenerateRequest) } returns ott\n        justRun { tokenGenerationSuccessHandler.handle(any(), any(), eq(ott)) }\n        this.mockMvc.perform(\n                MockMvcRequestBuilders.post(\"/ott/generate\").param(\"username\", \"user\")\n                        .with(SecurityMockMvcRequestPostProcessors.csrf())\n        )\n\n        verify { resolver.resolve(any()) }\n        verify { tokenService.generate(expectedGenerateRequest) }\n        verify { tokenGenerationSuccessHandler.handle(any(), any(), eq(ott)) }\n\n    }\n\n    private fun getLastToken(): OneTimeToken {\n        val lastToken = spring.context\n            .getBean(TestOneTimeTokenGenerationSuccessHandler::class.java).lastToken\n        return lastToken!!\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @Import(UserDetailsServiceConfig::class)\n    open class OneTimeTokenConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity, ottSuccessHandler: OneTimeTokenGenerationSuccessHandler): SecurityFilterChain {\n            // @formatter:off\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oneTimeTokenLogin {\n                    oneTimeTokenGenerationSuccessHandler = ottSuccessHandler\n                }\n            }\n            // @formatter:on\n            return http.build()\n        }\n\n        @Bean\n        open fun ottSuccessHandler(): TestOneTimeTokenGenerationSuccessHandler {\n            return TestOneTimeTokenGenerationSuccessHandler()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @Import(UserDetailsServiceConfig::class)\n    open class OneTimeTokenConfigWithCustomImpls {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity,\n                                     ottRequestResolver: GenerateOneTimeTokenRequestResolver,\n                                     ottService: OneTimeTokenService,\n                                     ottSuccessHandler: OneTimeTokenGenerationSuccessHandler): SecurityFilterChain {\n            // @formatter:off\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oneTimeTokenLogin {\n                    generateRequestResolver = ottRequestResolver\n                    tokenService = ottService\n                    oneTimeTokenGenerationSuccessHandler = ottSuccessHandler\n                }\n            }\n            // @formatter:on\n            return http.build()\n        }\n\n        @Bean\n        open fun ottRequestResolver(): GenerateOneTimeTokenRequestResolver {\n            return mockk()\n        }\n\n        @Bean\n        open fun ottService(): OneTimeTokenService {\n            return mockk()\n        }\n\n        @Bean\n        open fun ottSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n            return mockk()\n        }\n\n    }\n\n    @EnableWebSecurity\n    @Configuration(proxyBeanMethods = false)\n    @Import(UserDetailsServiceConfig::class)\n    open class OneTimeTokenDifferentUrlsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity, ottSuccessHandler: OneTimeTokenGenerationSuccessHandler): SecurityFilterChain {\n            // @formatter:off\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oneTimeTokenLogin {\n                    tokenGeneratingUrl = \"/generateurl\"\n                    oneTimeTokenGenerationSuccessHandler = ottSuccessHandler\n                    loginProcessingUrl = \"/loginprocessingurl\"\n                    authenticationSuccessHandler = SimpleUrlAuthenticationSuccessHandler(\"/authenticated\")\n                }\n            }\n            // @formatter:on\n            return http.build()\n        }\n\n        @Bean\n        open fun ottSuccessHandler(): TestOneTimeTokenGenerationSuccessHandler {\n            return TestOneTimeTokenGenerationSuccessHandler(\"/redirected\")\n        }\n    }\n\n    @Configuration(proxyBeanMethods = false)\n    open class UserDetailsServiceConfig {\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService =\n            InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin())\n    }\n\n    class TestOneTimeTokenGenerationSuccessHandler :\n        OneTimeTokenGenerationSuccessHandler {\n        private val delegate: OneTimeTokenGenerationSuccessHandler\n        var lastToken: OneTimeToken? = null\n\n        constructor() {\n            this.delegate =\n                RedirectOneTimeTokenGenerationSuccessHandler(\n                    \"/login/ott\"\n                )\n        }\n\n        constructor(redirectUrl: String) {\n            this.delegate =\n                RedirectOneTimeTokenGenerationSuccessHandler(\n                    redirectUrl\n                )\n        }\n\n        override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {\n            this.lastToken = oneTimeToken\n            delegate.handle(request, response, oneTimeToken)\n        }\n\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/PasswordManagementDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [PasswordManagementDsl].\n *\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass PasswordManagementDslTests {\n\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `when change password page not set then default change password page used`() {\n        this.spring.register(PasswordManagementWithDefaultChangePasswordPageConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/.well-known/change-password\")\n                .andExpect {\n                    status { isFound() }\n                    redirectedUrl(\"/change-password\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class PasswordManagementWithDefaultChangePasswordPageConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                passwordManagement {}\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `when change password page set then specified change password page used`() {\n        this.spring.register(PasswordManagementWithCustomChangePasswordPageConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/.well-known/change-password\")\n                .andExpect {\n                    status { isFound() }\n                    redirectedUrl(\"/custom-change-password-page\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class PasswordManagementWithCustomChangePasswordPageConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                passwordManagement {\n                    changePasswordPage = \"/custom-change-password-page\"\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/PortMapperDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.PortMapperImpl\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport java.util.*\n\n/**\n * Tests for [PortMapperDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass PortMapperDslTests  {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `port mapper when specifying map then redirects to https port`() {\n        this.spring.register(PortMapperMapConfig::class.java).autowire()\n\n        this.mockMvc.get(\"http://localhost:543\")\n                .andExpect {\n                    redirectedUrl(\"https://localhost:123\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class PortMapperMapConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                requiresChannel {\n                    secure(anyRequest, requiresSecure)\n                }\n                portMapper {\n                    map(543, 123)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `port mapper when specifying custom mapper then redirects to https port`() {\n        this.spring.register(CustomPortMapperConfig::class.java).autowire()\n\n        this.mockMvc.get(\"http://localhost:543\")\n                .andExpect {\n                    redirectedUrl(\"https://localhost:123\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomPortMapperConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val customPortMapper = PortMapperImpl()\n            customPortMapper.setPortMappings(Collections.singletonMap(\"543\", \"123\"))\n            http {\n                requiresChannel {\n                    secure(anyRequest, requiresSecure)\n                }\n                portMapper {\n                    portMapper = customPortMapper\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.*\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.fail\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.annotation.Order\nimport org.springframework.mock.web.MockHttpSession\nimport org.springframework.security.authentication.RememberMeAuthenticationToken\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.authority.AuthorityUtils\nimport org.springframework.security.core.userdetails.PasswordEncodedUser\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder\nimport org.springframework.security.crypto.password.PasswordEncoder\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler\nimport org.springframework.security.web.authentication.NullRememberMeServices\nimport org.springframework.security.web.authentication.RememberMeServices\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler\nimport org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices\nimport org.springframework.security.web.authentication.rememberme.PersistentTokenRepository\nimport org.springframework.test.web.servlet.MockHttpServletRequestDsl\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.post\n\n/**\n * Tests for [RememberMeDsl]\n *\n * @author Ivan Pavlov\n */\ninternal class RememberMeDslTests {\n\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    private val mockAuthentication: Authentication = mockk()\n\n    @Test\n    fun `Remember Me login when remember me true then responds with remember me cookie`() {\n        this.spring.register(RememberMeConfig::class.java).autowire()\n        mockMvc.post(\"/login\")\n        {\n            loginRememberMeRequest()\n        }.andExpect {\n            cookie {\n                exists(\"remember-me\")\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me get when remember me cookie then authentication is remember me authentication token`() {\n        this.spring.register(RememberMeConfig::class.java).autowire()\n        val mvcResult = mockMvc.post(\"/login\")\n        {\n            loginRememberMeRequest()\n        }.andReturn()\n        val rememberMeCookie = mvcResult.response.getCookie(\"remember-me\")\n                ?: fail { \"Missing remember-me cookie in login response\" }\n        mockMvc.get(\"/abc\")\n        {\n            cookie(rememberMeCookie)\n        }.andExpect {\n            val rememberMeAuthentication = SecurityMockMvcResultMatchers.authenticated()\n                    .withAuthentication { assertThat(it).isInstanceOf(RememberMeAuthenticationToken::class.java) }\n            match(rememberMeAuthentication)\n        }\n    }\n\n    @Test\n    fun `Remember Me logout when remember me cookie then authentication is remember me cookie expired`() {\n        this.spring.register(RememberMeConfig::class.java).autowire()\n        val mvcResult = mockMvc.post(\"/login\")\n        {\n            loginRememberMeRequest()\n        }.andReturn()\n        val rememberMeCookie = mvcResult.response.getCookie(\"remember-me\")\n                ?: fail { \"Missing remember-me cookie in login response\" }\n        val mockSession = mvcResult.request.session as MockHttpSession\n        mockMvc.post(\"/logout\")\n        {\n            with(csrf())\n            cookie(rememberMeCookie)\n            session = mockSession\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login?logout\")\n            cookie {\n                maxAge(\"remember-me\", 0)\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me get when remember me cookie and logged out then redirects to login`() {\n        this.spring.register(RememberMeConfig::class.java).autowire()\n        mockMvc.perform(formLogin())\n        val mvcResult = mockMvc.post(\"/login\")\n        {\n            loginRememberMeRequest()\n        }.andReturn()\n        val rememberMeCookie = mvcResult.response.getCookie(\"remember-me\")\n                ?: fail { \"Missing remember-me cookie in login request\" }\n        val mockSession = mvcResult.request.session as MockHttpSession\n        val logoutMvcResult = mockMvc.post(\"/logout\")\n        {\n            with(csrf())\n            cookie(rememberMeCookie)\n            session = mockSession\n        }.andReturn()\n        val expiredRememberMeCookie = logoutMvcResult.response.getCookie(\"remember-me\")\n                ?: fail { \"Missing remember-me cookie in logout response\" }\n        mockMvc.get(\"/abc\")\n        {\n            with(csrf())\n            cookie(expiredRememberMeCookie)\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login\")\n        }\n    }\n\n    @Test\n    fun `Remember Me login when remember me domain then remember me cookie has domain`() {\n        this.spring.register(RememberMeDomainConfig::class.java).autowire()\n        mockMvc.post(\"/login\")\n        {\n            loginRememberMeRequest()\n        }.andExpect {\n            cookie {\n                domain(\"remember-me\", \"spring.io\")\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me when remember me services then uses`() {\n        this.spring.register(RememberMeServicesRefConfig::class.java).autowire()\n        mockkObject(RememberMeServicesRefConfig.REMEMBER_ME_SERVICES)\n        every {\n            RememberMeServicesRefConfig.REMEMBER_ME_SERVICES.autoLogin(any(),any())\n        } returns mockAuthentication\n        every {\n            RememberMeServicesRefConfig.REMEMBER_ME_SERVICES.loginFail(any(), any())\n        } returns Unit\n        every {\n            RememberMeServicesRefConfig.REMEMBER_ME_SERVICES.loginSuccess(any(), any(), any())\n        } returns Unit\n\n        mockMvc.get(\"/\")\n\n        verify(exactly = 1) { RememberMeServicesRefConfig.REMEMBER_ME_SERVICES.autoLogin(any(),any()) }\n        mockMvc.post(\"/login\") {\n            with(csrf())\n        }\n        verify(exactly = 2) { RememberMeServicesRefConfig.REMEMBER_ME_SERVICES.loginFail(any(), any()) }\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }\n        verify(exactly = 1) { RememberMeServicesRefConfig.REMEMBER_ME_SERVICES.loginSuccess(any(), any(), any()) }\n    }\n\n    @Test\n    fun `Remember Me when authentication success handler then uses`() {\n        this.spring.register(RememberMeSuccessHandlerConfig::class.java).autowire()\n        mockkObject(RememberMeSuccessHandlerConfig.SUCCESS_HANDLER)\n        justRun {\n            RememberMeSuccessHandlerConfig.SUCCESS_HANDLER.onAuthenticationSuccess(any(), any(), any())\n        }\n        val mvcResult = mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }.andReturn()\n\n        val rememberMeCookie = mvcResult.response.getCookie(\"remember-me\")\n                ?: fail { \"Missing remember-me cookie in login response\" }\n        mockMvc.get(\"/abc\") {\n            cookie(rememberMeCookie)\n        }\n\n        verify(exactly = 1) { RememberMeSuccessHandlerConfig.SUCCESS_HANDLER.onAuthenticationSuccess(any(), any(), any()) }\n    }\n\n    @Test\n    fun `Remember Me when key then remember me works only for matching routes`() {\n        this.spring.register(WithAndWithoutKeyConfig::class.java).autowire()\n        val withoutKeyMvcResult = mockMvc.post(\"/without-key/login\") {\n            loginRememberMeRequest()\n        }.andReturn()\n        val withoutKeyRememberMeCookie = withoutKeyMvcResult.response.getCookie(\"remember-me\")\n                ?: fail { \"Missing remember-me cookie in without key login response\" }\n        mockMvc.get(\"/abc\") {\n            cookie(withoutKeyRememberMeCookie)\n        }.andExpect {\n            status { isFound() }\n            redirectedUrl(\"/login\")\n        }\n        val keyMvcResult = mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }.andReturn()\n        val keyRememberMeCookie = keyMvcResult.response.getCookie(\"remember-me\")\n                ?: fail { \"Missing remember-me cookie in key login response\" }\n        mockMvc.get(\"/abc\") {\n            cookie(keyRememberMeCookie)\n        }.andExpect {\n            status { isNotFound() }\n        }\n    }\n\n    @Test\n    fun `Remember Me when token repository then uses`() {\n        this.spring.register(RememberMeTokenRepositoryConfig::class.java).autowire()\n        mockkObject(RememberMeTokenRepositoryConfig.TOKEN_REPOSITORY)\n        every {\n            RememberMeTokenRepositoryConfig.TOKEN_REPOSITORY.createNewToken(any())\n        } returns Unit\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }\n        verify(exactly = 1) { RememberMeTokenRepositoryConfig.TOKEN_REPOSITORY.createNewToken(any()) }\n    }\n\n    @Test\n    fun `Remember Me when token validity seconds then cookie max age`() {\n        this.spring.register(RememberMeTokenValidityConfig::class.java).autowire()\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }.andExpect {\n            cookie {\n                maxAge(\"remember-me\", 42)\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me when using defaults then cookie max age`() {\n        this.spring.register(RememberMeConfig::class.java).autowire()\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }.andExpect {\n            cookie {\n                maxAge(\"remember-me\", AbstractRememberMeServices.TWO_WEEKS_S)\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me when use secure cookie then cookie secure`() {\n        this.spring.register(RememberMeUseSecureCookieConfig::class.java).autowire()\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }.andExpect {\n            cookie {\n                secure(\"remember-me\", true)\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me when using defaults then cookie secure`() {\n        this.spring.register(RememberMeConfig::class.java).autowire()\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n            secure = true\n        }.andExpect {\n            cookie {\n                secure(\"remember-me\", true)\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me when parameter then responds with remember me cookie`() {\n        this.spring.register(RememberMeParameterConfig::class.java).autowire()\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest(\"rememberMe\")\n        }.andExpect {\n            cookie {\n                exists(\"remember-me\")\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me when cookie name then responds with remember me cookie with such name`() {\n        this.spring.register(RememberMeCookieNameConfig::class.java).autowire()\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }.andExpect {\n            cookie {\n                exists(\"rememberMe\")\n            }\n        }\n    }\n\n    @Test\n    fun `Remember Me when global user details service then uses`() {\n        this.spring.register(RememberMeDefaultUserDetailsServiceConfig::class.java).autowire()\n        mockkObject(RememberMeDefaultUserDetailsServiceConfig.USER_DETAIL_SERVICE)\n        val user = User(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"))\n        every {\n            RememberMeDefaultUserDetailsServiceConfig.USER_DETAIL_SERVICE.loadUserByUsername(\"user\")\n        } returns user\n\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }\n\n        verify(exactly = 1) { RememberMeDefaultUserDetailsServiceConfig.USER_DETAIL_SERVICE.loadUserByUsername(\"user\") }\n    }\n\n    @Test\n    fun `Remember Me when user details service then uses`() {\n        this.spring.register(RememberMeUserDetailsServiceConfig::class.java).autowire()\n        mockkObject(RememberMeUserDetailsServiceConfig.USER_DETAIL_SERVICE)\n        val user = User(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"))\n        every {\n            RememberMeUserDetailsServiceConfig.USER_DETAIL_SERVICE.loadUserByUsername(\"user\")\n        } returns user\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest()\n        }\n        verify(exactly = 1) { RememberMeUserDetailsServiceConfig.USER_DETAIL_SERVICE.loadUserByUsername(\"user\") }\n    }\n\n    @Test\n    fun `Remember Me when always remember then remembers without HTTP parameter`() {\n        this.spring.register(RememberMeAlwaysRememberConfig::class.java).autowire()\n        mockMvc.post(\"/login\") {\n            loginRememberMeRequest(rememberMeValue = null)\n        }.andExpect {\n            cookie {\n                exists(\"remember-me\")\n            }\n        }\n    }\n\n    private fun MockHttpServletRequestDsl.loginRememberMeRequest(\n        rememberMeParameter: String = \"remember-me\",\n        rememberMeValue: Boolean? = true\n    ) {\n        with(csrf())\n        param(\"username\", \"user\")\n        param(\"password\", \"password\")\n        rememberMeValue?.also {\n            param(rememberMeParameter, rememberMeValue.toString())\n        }\n    }\n\n    @Configuration\n    open class DefaultUserConfig {\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            return InMemoryUserDetailsManager(PasswordEncodedUser.user())\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeConfig : DefaultUserConfig() {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, hasRole(\"USER\"))\n                }\n                formLogin {}\n                rememberMe {}\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeDomainConfig : DefaultUserConfig() {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, hasRole(\"USER\"))\n                }\n                formLogin {}\n                rememberMe {\n                    rememberMeCookieDomain = \"spring.io\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeServicesRefConfig : DefaultUserConfig() {\n\n        companion object {\n            val REMEMBER_ME_SERVICES: RememberMeServices = NullRememberMeServices()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    rememberMeServices = REMEMBER_ME_SERVICES\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeSuccessHandlerConfig : DefaultUserConfig() {\n\n        companion object {\n            val SUCCESS_HANDLER: AuthenticationSuccessHandler = SimpleUrlAuthenticationSuccessHandler()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    authenticationSuccessHandler = SUCCESS_HANDLER\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class WithAndWithoutKeyConfig : DefaultUserConfig() {\n        @Bean\n        @Order(0)\n        open fun securityFilterChainWithoutKey(http: HttpSecurity): SecurityFilterChain {\n            http {\n                securityMatcher(\"/without-key/**\")\n                formLogin {\n                    loginProcessingUrl = \"/without-key/login\"\n                }\n                rememberMe {}\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun securityFilterChainWithKey(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                formLogin {}\n                rememberMe {\n                    key = \"RememberMeKey\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeTokenRepositoryConfig : DefaultUserConfig() {\n\n        companion object {\n            val TOKEN_REPOSITORY: PersistentTokenRepository = mockk()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    tokenRepository = TOKEN_REPOSITORY\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeTokenValidityConfig : DefaultUserConfig() {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    tokenValiditySeconds = 42\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeUseSecureCookieConfig : DefaultUserConfig() {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    useSecureCookie = true\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeParameterConfig : DefaultUserConfig() {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    rememberMeParameter = \"rememberMe\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeCookieNameConfig : DefaultUserConfig() {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    rememberMeCookieName = \"rememberMe\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeDefaultUserDetailsServiceConfig {\n\n        companion object {\n            val USER_DETAIL_SERVICE: UserDetailsService = InMemoryUserDetailsManager(\n                User(\"username\", \"password\", emptyList())\n            )\n            val PASSWORD_ENCODER: PasswordEncoder = BCryptPasswordEncoder()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {}\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            return USER_DETAIL_SERVICE\n        }\n\n        @Bean\n        open fun delegatingPasswordEncoder(): PasswordEncoder = PASSWORD_ENCODER\n\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeUserDetailsServiceConfig : DefaultUserConfig() {\n\n        companion object {\n            val USER_DETAIL_SERVICE: UserDetailsService = InMemoryUserDetailsManager(\n                User(\"username\", \"password\", emptyList())\n            )\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    userDetailsService = USER_DETAIL_SERVICE\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RememberMeAlwaysRememberConfig : DefaultUserConfig() {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                formLogin {}\n                rememberMe {\n                    alwaysRemember = true\n                }\n            }\n            return http.build()\n        }\n    }\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/RequestCacheDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.savedrequest.NullRequestCache\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl\n\n/**\n * Tests for [RequestCacheDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass RequestCacheDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `GET when request cache enabled then redirected to cached page`() {\n        this.spring.register(RequestCacheConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/test\")\n\n        this.mockMvc.perform(formLogin())\n                .andExpect {\n                    redirectedUrl(\"http://localhost/test\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RequestCacheConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                requestCache { }\n                formLogin { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `GET when custom request cache then custom request cache used`() {\n        this.spring.register(CustomRequestCacheConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/test\")\n\n        this.mockMvc.perform(formLogin())\n                .andExpect {\n                    redirectedUrl(\"/\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomRequestCacheConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                requestCache {\n                    requestCache = NullRequestCache()\n                }\n                formLogin { }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/RequiresChannelDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.access.ConfigAttribute\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.FilterInvocation\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.access.channel.ChannelProcessor\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.web.bind.annotation.RequestMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [RequiresChannelDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass RequiresChannelDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `requires channel when requires secure then redirects to https`() {\n        this.spring.register(RequiresSecureConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    redirectedUrl(\"https://localhost/\")\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RequiresSecureConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                requiresChannel {\n                    secure(anyRequest, requiresSecure)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `request when channel matches mvc with servlet path then redirects based on servlet path`() {\n        this.spring.register(MvcMatcherServletPathConfig::class.java).autowire()\n\n        this.mockMvc.perform(MockMvcRequestBuilders.get(\"/spring/path\")\n                .servletPath(\"/spring\"))\n                .andExpect(status().isFound)\n                .andExpect(redirectedUrl(\"https://localhost/spring/path\"))\n\n        this.mockMvc.perform(MockMvcRequestBuilders.get(\"/other/path\")\n                .servletPath(\"/other\"))\n                .andExpect(MockMvcResultMatchers.status().isOk)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    @EnableWebMvc\n    open class MvcMatcherServletPathConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                requiresChannel {\n                    secure(\"/path\",\n                            \"/spring\",\n                            requiresSecure)\n                }\n            }\n            return http.build()\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/path\")\n            fun path() {\n            }\n        }\n    }\n\n    @Test\n    fun `requires channel when channel processors configured then channel processors used`() {\n        this.spring.register(ChannelProcessorsConfig::class.java).autowire()\n        mockkObject(ChannelProcessorsConfig.CHANNEL_PROCESSOR)\n\n        this.mockMvc.get(\"/\")\n\n        verify(exactly = 0) {  ChannelProcessorsConfig.CHANNEL_PROCESSOR.supports(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ChannelProcessorsConfig {\n\n        companion object {\n            val CHANNEL_PROCESSOR: ChannelProcessor = object : ChannelProcessor {\n                override fun decide(invocation: FilterInvocation, config: MutableCollection<ConfigAttribute>) {}\n                override fun supports(attribute: ConfigAttribute): Boolean = true\n            }\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                requiresChannel {\n                    channelProcessors = listOf(CHANNEL_PROCESSOR)\n                    secure(anyRequest, requiresSecure)\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/Saml2DslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.BeanCreationException\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.authentication.ProviderManager\nimport org.springframework.security.authentication.TestingAuthenticationProvider\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.saml2.core.Saml2X509Credential\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.MvcResult\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateFactory\nimport java.util.*\n\n/**\n * Tests for [Saml2Dsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass Saml2DslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `saml2Login when no relying party registration repository then exception`() {\n        Assertions.assertThatThrownBy { this.spring.register(Saml2LoginNoRelyingPArtyRegistrationRepoConfig::class.java).autowire() }\n                .isInstanceOf(BeanCreationException::class.java)\n                .hasMessageContaining(\"relyingPartyRegistrationRepository cannot be null\")\n\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class Saml2LoginNoRelyingPArtyRegistrationRepoConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                saml2Login { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `login page when saml2Configured then default login page created`() {\n        this.spring.register(Saml2LoginConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/login\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class Saml2LoginConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                saml2Login {\n                    relyingPartyRegistrationRepository =\n                            InMemoryRelyingPartyRegistrationRepository(\n                                    RelyingPartyRegistration.withRegistrationId(\"samlId\")\n                                            .assertionConsumerServiceLocation(\"{baseUrl}\" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI)\n                                            .assertingPartyMetadata { a -> a\n                                                .verificationX509Credentials { c -> c\n                                                    .add(Saml2X509Credential(loadCert(\"rod.cer\"), Saml2X509Credential.Saml2X509CredentialType.VERIFICATION))\n                                                }\n                                            }\n                                            .assertingPartyMetadata { c -> c.singleSignOnServiceLocation(\"ssoUrl\") }\n                                            .assertingPartyMetadata { c -> c.entityId(\"entityId\") }\n                                            .build()\n                            )\n                }\n            }\n            return http.build()\n        }\n\n        private fun <T : Certificate> loadCert(location: String): T {\n            ClassPathResource(location).inputStream.use { inputStream ->\n                val certFactory = CertificateFactory.getInstance(\"X.509\")\n                return certFactory.generateCertificate(inputStream) as T\n            }\n        }\n    }\n\n    @Test\n    fun `authenticate when custom AuthenticationManager then used`() {\n        this.spring.register(Saml2LoginCustomAuthenticationManagerConfig::class.java).autowire()\n        mockkObject(Saml2LoginCustomAuthenticationManagerConfig.AUTHENTICATION_MANAGER)\n        val  request = MockMvcRequestBuilders.post(\"/login/saml2/sso/id\")\n            .param(\"SAMLResponse\", Base64.getEncoder().encodeToString(\"saml2-xml-response-object\".toByteArray()))\n        this.mockMvc.perform(request)\n        verify(exactly = 1) { Saml2LoginCustomAuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun authenticationRequestWhenCustomAuthenticationRequestPathRepositoryThenUses() {\n        this.spring.register(CustomAuthenticationRequestUriQuery::class.java).autowire()\n        val registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n        val request = MockMvcRequestBuilders.get(\"/custom/auth/sso\")\n        this.mockMvc.perform(request)\n            .andExpect(MockMvcResultMatchers.status().isFound())\n            .andExpect(MockMvcResultMatchers.redirectedUrl(\"/custom/auth/sso?entityId=simplesamlphp\"))\n        request.queryParam(\"entityId\", registration.registrationId)\n        val result: MvcResult =\n            this.mockMvc.perform(request).andExpect(MockMvcResultMatchers.status().isFound()).andReturn()\n        val redirectedUrl = result.response.redirectedUrl\n        Assertions.assertThat(redirectedUrl)\n            .startsWith(registration.assertingPartyMetadata.singleSignOnServiceLocation)\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class Saml2LoginCustomAuthenticationManagerConfig {\n        companion object {\n            val AUTHENTICATION_MANAGER: AuthenticationManager = ProviderManager(TestingAuthenticationProvider())\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                saml2Login {\n                    authenticationManager = AUTHENTICATION_MANAGER\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun relyingPartyRegistrationRepository(): RelyingPartyRegistrationRepository? {\n            val repository: RelyingPartyRegistrationRepository = mockk()\n            every {\n                repository.findByRegistrationId(any())\n            } returns TestRelyingPartyRegistrations.relyingPartyRegistration().build()\n            return repository\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomAuthenticationRequestUriQuery {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                saml2Login {\n                    authenticationRequestUriQuery = \"/custom/auth/sso?entityId={registrationId}\"\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun relyingPartyRegistrationRepository(): RelyingPartyRegistrationRepository? {\n            return InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.relyingPartyRegistration().build())\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/Saml2LogoutDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.BeanCreationException\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authentication.TestAuthentication\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.AuthorityUtils\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.MvcResult\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\n\n/**\n * Tests for [Saml2LogoutDsl]\n *\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass Saml2LogoutDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `saml2Logout when no relying party registration repository then exception`() {\n        Assertions.assertThatThrownBy { this.spring.register(Saml2LogoutNoRelyingPartyRegistrationRepoConfig::class.java).autowire() }\n                .isInstanceOf(BeanCreationException::class.java)\n                .hasMessageContaining(\"relyingPartyRegistrationRepository cannot be null\")\n\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun `saml2Logout when defaults and not saml login then default logout`() {\n        this.spring.register(Saml2LogoutDefaultsConfig::class.java).autowire()\n        val user = TestAuthentication.authenticatedUser()\n        val result: MvcResult = this.mockMvc.perform(\n            MockMvcRequestBuilders.post(\"/logout\").with(SecurityMockMvcRequestPostProcessors.authentication(user))\n                .with(SecurityMockMvcRequestPostProcessors.csrf()))\n            .andExpect(MockMvcResultMatchers.status().isFound())\n            .andReturn()\n        val location = result.response.getHeader(\"Location\")\n        Assertions.assertThat(location).isEqualTo(\"/login?logout\")\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun saml2LogoutWhenDefaultsThenLogsOutAndSendsLogoutRequest() {\n        this.spring.register(Saml2LogoutDefaultsConfig::class.java).autowire()\n        val principal = DefaultSaml2AuthenticatedPrincipal(\"user\", emptyMap())\n        principal.relyingPartyRegistrationId = \"registration-id\"\n        val user = Saml2Authentication(principal, \"response\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"))\n        val result: MvcResult = this.mockMvc.perform(MockMvcRequestBuilders.post(\"/logout\")\n            .with(SecurityMockMvcRequestPostProcessors.authentication(user))\n            .with(SecurityMockMvcRequestPostProcessors.csrf()))\n            .andExpect(MockMvcResultMatchers.status().isFound())\n            .andReturn()\n        val location = result.response.getHeader(\"Location\")\n        Assertions.assertThat(location).startsWith(\"https://ap.example.org/logout/saml2/request\")\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class Saml2LogoutNoRelyingPartyRegistrationRepoConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                saml2Logout { }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class Saml2LogoutDefaultsConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                saml2Logout { }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun registrations(): RelyingPartyRegistrationRepository =\n            InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.full().build())\n\n    }\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/Saml2MetadataDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.verify\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.BeanCreationException\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateFactory\n\n/**\n * Tests for [Saml2Dsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass Saml2MetadataDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `saml2Metadat when no relying party registration repository then exception`() {\n        Assertions.assertThatThrownBy { this.spring.register(Saml2MetadataNoRelyingPartyRegistrationRepoConfig::class.java).autowire() }\n                .isInstanceOf(BeanCreationException::class.java)\n                .hasMessageContaining(\"relyingPartyRegistrationRepository cannot be null\")\n\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class Saml2MetadataNoRelyingPartyRegistrationRepoConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                saml2Metadata { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `metadata endpoint when saml2Metadata configured then metadata returned`() {\n        this.spring.register(Saml2MetadataConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/saml2/metadata\")\n                .andExpect {\n                    status { isOk() }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class Saml2MetadataConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                saml2Metadata { }\n            }\n            return http.build()\n        }\n\n\t\t@Bean\n\t\topen fun registrations(): RelyingPartyRegistrationRepository {\n\t\t\treturn InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.full().build())\n\t\t}\n\n        private fun <T : Certificate> loadCert(location: String): T {\n            ClassPathResource(location).inputStream.use { inputStream ->\n                val certFactory = CertificateFactory.getInstance(\"X.509\")\n                return certFactory.generateCertificate(inputStream) as T\n            }\n        }\n    }\n\n    @Test\n    fun `metadata endpoint when url customized then used`() {\n        this.spring.register(Saml2LoginCustomEndpointConfig::class.java).autowire()\n\t\tthis.mockMvc.get(\"/saml/metadata\")\n\t\t\t.andExpect {\n\t\t\t\tstatus { isOk() }\n\t\t\t}\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class Saml2LoginCustomEndpointConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                saml2Metadata {\n                    metadataUrl = \"/saml/metadata\"\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun relyingPartyRegistrationRepository(): RelyingPartyRegistrationRepository? {\n\t\t\treturn InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.full().build())\n        }\n    }\n\n\t@Test\n\tfun `metadata endpoint when resolver customized then used`() {\n\t\tthis.spring.register(Saml2LoginCustomMetadataResolverConfig::class.java).autowire()\n\t\tval mocked = this.spring.context.getBean(Saml2MetadataResponseResolver::class.java)\n\t\tevery {\n\t\t\tmocked.resolve(any())\n\t\t} returns Saml2MetadataResponse(\"metadata\", \"file\")\n\t\tthis.mockMvc.get(\"/saml2/metadata\")\n\t\t\t.andExpect {\n\t\t\t\tstatus { isOk() }\n\t\t\t}\n\t\tverify(exactly = 1) { mocked.resolve(any()) }\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\topen class Saml2LoginCustomMetadataResolverConfig {\n\n\t\tprivate val metadataResponseResolver: Saml2MetadataResponseResolver = mockk()\n\n\t\t@Bean\n\t\topen fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\t\thttp {\n\t\t\t\tsaml2Metadata {}\n\t\t\t}\n\t\t\treturn http.build()\n\t\t}\n\n\t\t@Bean\n\t\topen fun metadataResponseResolver(): Saml2MetadataResponseResolver? {\n\t\t\treturn this.metadataResponseResolver\n\t\t}\n\n\t\t@Bean\n\t\topen fun relyingPartyRegistrationRepository(): RelyingPartyRegistrationRepository? {\n\t\t\treturn InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.full().build())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/SecurityContextDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.context.SecurityContext\nimport org.springframework.security.core.userdetails.PasswordEncodedUser\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders\nimport org.springframework.security.web.FilterChainProxy\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.context.HttpRequestResponseHolder\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository\nimport org.springframework.security.web.context.NullSecurityContextRepository\nimport org.springframework.security.web.context.SecurityContextHolderFilter\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\n\n@ExtendWith(SpringTestContextExtension::class)\nclass SecurityContextDslTests {\n\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mvc: MockMvc\n\n    @Test\n    fun `security context when invoked twice then uses original security context repository`() {\n        spring.register(DuplicateDoesNotOverrideConfig::class.java).autowire()\n        mockkObject(DuplicateDoesNotOverrideConfig.SECURITY_CONTEXT_REPOSITORY)\n        every { DuplicateDoesNotOverrideConfig.SECURITY_CONTEXT_REPOSITORY.loadContext(any<HttpRequestResponseHolder>()) } returns mockk<SecurityContext>(\n            relaxed = true\n        )\n        mvc.perform(get(\"/\"))\n        verify(exactly = 1) { DuplicateDoesNotOverrideConfig.SECURITY_CONTEXT_REPOSITORY.loadContext(any<HttpRequestResponseHolder>()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DuplicateDoesNotOverrideConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            // @formatter:off\n            http {\n                securityContext {\n                    securityContextRepository = SECURITY_CONTEXT_REPOSITORY\n                }\n                securityContext { }\n            }\n            // @formatter:on\n            return http.build()\n        }\n\n        companion object {\n            val SECURITY_CONTEXT_REPOSITORY = NullSecurityContextRepository()\n        }\n    }\n\n    @Test\n    fun `security context when require explicit save is true then configure SecurityContextHolderFilter`() {\n        val repository = HttpSessionSecurityContextRepository()\n        val testContext = spring.register(RequireExplicitSaveConfig::class.java)\n        testContext.autowire()\n        val filterChainProxy = testContext.context.getBean(FilterChainProxy::class.java)\n        // @formatter:off\n        val filterTypes = filterChainProxy.getFilters(\"/\")!!.toList()\n\n        assertThat(filterTypes)\n                .anyMatch { it is SecurityContextHolderFilter }\n                .noneMatch { it is SecurityContextPersistenceFilter }\n        // @formatter:on\n        val mvcResult = mvc.perform(SecurityMockMvcRequestBuilders.formLogin()).andReturn()\n        val securityContext = repository\n            .loadContext(HttpRequestResponseHolder(mvcResult.request, mvcResult.response))\n        assertThat(securityContext.authentication).isNotNull\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RequireExplicitSaveConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            // @formatter:off\n            http {\n                formLogin { }\n                securityContext {\n                    requireExplicitSave = true\n                }\n            }\n            // @formatter:on\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            return InMemoryUserDetailsManager(PasswordEncodedUser.user())\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/SessionManagementDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.*\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.mock.web.MockHttpSession\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.http.SessionCreationPolicy\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler\nimport org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy\nimport org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\n\n/**\n * Tests for [SessionManagementDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass SessionManagementDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `session management when invalid session url then redirected to url`() {\n        this.spring.register(InvalidSessionUrlConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/\")\n            .with { request ->\n                request.isRequestedSessionIdValid = false\n                request.requestedSessionId = \"id\"\n                request\n            })\n            .andExpect(status().isFound)\n            .andExpect(redirectedUrl(\"/invalid\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class InvalidSessionUrlConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    invalidSessionUrl = \"/invalid\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session management when invalid session strategy then strategy used`() {\n        this.spring.register(InvalidSessionStrategyConfig::class.java).autowire()\n\n        this.mockMvc.perform(get(\"/\")\n            .with { request ->\n                request.isRequestedSessionIdValid = false\n                request.requestedSessionId = \"id\"\n                request\n            })\n            .andExpect(status().isFound)\n            .andExpect(redirectedUrl(\"/invalid\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class InvalidSessionStrategyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    invalidSessionStrategy = SimpleRedirectInvalidSessionStrategy(\"/invalid\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session management when session authentication error url then redirected to url`() {\n        this.spring.register(SessionAuthenticationErrorUrlConfig::class.java).autowire()\n        val authentication: Authentication = TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\")\n        val session: MockHttpSession = mockk(relaxed = true)\n        every { session.changeSessionId() } throws SessionAuthenticationException(\"any SessionAuthenticationException\")\n        every<Any?> { session.getAttribute(any()) } returns null\n\n        this.mockMvc.perform(get(\"/\")\n            .with(authentication(authentication))\n            .session(session))\n            .andExpect(status().isFound)\n            .andExpect(redirectedUrl(\"/session-auth-error\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class SessionAuthenticationErrorUrlConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                sessionManagement {\n                    sessionAuthenticationErrorUrl = \"/session-auth-error\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session management when session authentication failure handler then handler used`() {\n        this.spring.register(SessionAuthenticationFailureHandlerConfig::class.java).autowire()\n        val authentication: Authentication = TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\")\n        val session: MockHttpSession = mockk(relaxed = true)\n        every { session.changeSessionId() } throws SessionAuthenticationException(\"any SessionAuthenticationException\")\n        every<Any?> { session.getAttribute(any()) } returns null\n\n        this.mockMvc.perform(get(\"/\")\n            .with(authentication(authentication))\n            .session(session))\n            .andExpect(status().isFound)\n            .andExpect(redirectedUrl(\"/session-auth-error\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class SessionAuthenticationFailureHandlerConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                sessionManagement {\n                    sessionAuthenticationFailureHandler = SimpleUrlAuthenticationFailureHandler(\"/session-auth-error\")\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session management when stateless policy then does not store session`() {\n        this.spring.register(StatelessSessionManagementConfig::class.java).autowire()\n\n        val result = this.mockMvc.perform(get(\"/\"))\n            .andReturn()\n\n        assertThat(result.request.getSession(false)).isNull()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class StatelessSessionManagementConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                sessionManagement {\n                    sessionCreationPolicy = SessionCreationPolicy.STATELESS\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session management when session authentication strategy then strategy used`() {\n        this.spring.register(SessionAuthenticationStrategyConfig::class.java).autowire()\n        mockkObject(SessionAuthenticationStrategyConfig.STRATEGY)\n        val authentication: Authentication = TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\")\n        val session: MockHttpSession = mockk(relaxed = true)\n        every { session.changeSessionId() } throws SessionAuthenticationException(\"any SessionAuthenticationException\")\n        every<Any?> { session.getAttribute(any()) } returns null\n        justRun {  SessionAuthenticationStrategyConfig.STRATEGY.onAuthentication(any(), any(), any()) }\n\n        this.mockMvc.perform(get(\"/\")\n            .with(authentication(authentication))\n            .session(session))\n\n        verify(exactly = 1) { SessionAuthenticationStrategyConfig.STRATEGY.onAuthentication(any(), any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class SessionAuthenticationStrategyConfig {\n\n        companion object {\n            val STRATEGY: SessionAuthenticationStrategy = NullAuthenticatedSessionStrategy()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                sessionManagement {\n                    sessionAuthenticationStrategy = STRATEGY\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy = STRATEGY\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/WebAuthnDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport org.hamcrest.Matchers\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.post\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\n\n/**\n * Tests for [WebAuthnDsl]\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass WebAuthnDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `default configuration`() {\n        this.spring.register(WebauthnConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\")\n                .andExpect {\n                    status { isForbidden() }\n                }\n    }\n\n    @Test\n    fun `explicit PublicKeyCredentialCreationOptionsRepository`() {\n        this.spring.register(ExplicitPublicKeyCredentialCreationOptionsRepositoryConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\")\n            .andExpect {\n                status { isForbidden() }\n            }\n    }\n\n    @Test\n    fun `explicit HttpMessageConverter`() {\n        this.spring.register(ExplicitHttpMessageConverterConfig::class.java).autowire()\n\n        this.mockMvc.post(\"/test1\")\n            .andExpect {\n                status { isForbidden() }\n            }\n    }\n\n    @Test\n    fun `webauthn and formLogin configured with default registration page`() {\n        spring.register(DefaultWebauthnConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/login/webauthn.js\")\n                .andExpect {\n                    MockMvcResultMatchers.status().isOk\n                    header {\n                        string(\"content-type\", \"text/javascript;charset=UTF-8\")\n                    }\n                    content {\n                        string(Matchers.containsString(\"async function authenticate(\"))\n                    }\n                }\n    }\n\n    @Test\n    fun `webauthn and formLogin configured with disabled default registration page`() {\n        spring.register(FormLoginAndNoDefaultRegistrationPageConfiguration::class.java).autowire()\n\n        this.mockMvc.get(\"/login/webauthn.js\")\n                .andExpect {\n                    MockMvcResultMatchers.status().isOk\n                    header {\n                        string(\"content-type\", \"text/javascript;charset=UTF-8\")\n                    }\n                    content {\n                        string(Matchers.containsString(\"async function authenticate(\"))\n                    }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FormLoginAndNoDefaultRegistrationPageConfiguration {\n        @Bean\n        open fun userDetailsService(): UserDetailsService  =\n                InMemoryUserDetailsManager()\n\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http{\n                formLogin { }\n                webAuthn {\n                    rpId = \"spring.io\"\n                    rpName = \"spring\"\n                    disableDefaultRegistrationPage = true\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DefaultWebauthnConfig {\n        @Bean\n        open fun userDetailsService(): UserDetailsService  =\n                InMemoryUserDetailsManager()\n\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http{\n                formLogin { }\n                webAuthn {\n                    rpId = \"spring.io\"\n                    rpName = \"spring\"\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ExplicitPublicKeyCredentialCreationOptionsRepositoryConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                webAuthn {\n                    rpName = \"Spring Security Relying Party\"\n                    rpId = \"example.com\"\n                    allowedOrigins = setOf(\"https://example.com\")\n                    creationOptionsRepository = HttpSessionPublicKeyCredentialCreationOptionsRepository()\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"rod\")\n                .password(\"password\")\n                .roles(\"USER\")\n                .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ExplicitHttpMessageConverterConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                webAuthn {\n                    rpName = \"Spring Security Relying Party\"\n                    rpId = \"example.com\"\n                    allowedOrigins = setOf(\"https://example.com\")\n                    messageConverter = MappingJackson2HttpMessageConverter()\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"rod\")\n                .password(\"password\")\n                .roles(\"USER\")\n                .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class WebauthnConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                webAuthn {\n                    rpName = \"Spring Security Relying Party\"\n                    rpId = \"example.com\"\n                    allowedOrigins = setOf(\"https://example.com\")\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"rod\")\n                .password(\"password\")\n                .roles(\"USER\")\n                .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/X509DslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web\n\nimport io.mockk.mockk\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken\nimport org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\n\n/**\n * Tests for [X509Dsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass X509DslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `x509 when configured with defaults then user authenticated`() {\n        this.spring.register(X509Config::class.java).autowire()\n        val certificate = loadCert<X509Certificate>(\"rod.cer\")\n\n        this.mockMvc.perform(get(\"/\")\n                .with(x509(certificate)))\n                .andExpect(authenticated().withUsername(\"rod\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class X509Config {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                x509 { }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                    .username(\"rod\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n\n    @Test\n    fun `x509 when configured with regex then user authenticated`() {\n        this.spring.register(X509RegexConfig::class.java).autowire()\n        val certificate = loadCert<X509Certificate>(\"rodatexampledotcom.cer\")\n\n        this.mockMvc.perform(get(\"/\")\n                .with(x509(certificate)))\n                .andExpect(authenticated().withUsername(\"rod\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class X509RegexConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                x509 {\n                    subjectPrincipalRegex = \"CN=(.*?)@example.com(?:,|$)\"\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                    .username(\"rod\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n\n    @Test\n    fun `x509 when user details service configured then user details service used`() {\n        this.spring.register(UserDetailsServiceConfig::class.java).autowire()\n        val certificate = loadCert<X509Certificate>(\"rod.cer\")\n\n        this.mockMvc.perform(get(\"/\")\n                .with(x509(certificate)))\n                .andExpect(authenticated().withUsername(\"rod\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class UserDetailsServiceConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val userDetails = User.withDefaultPasswordEncoder()\n                    .username(\"rod\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            val customUserDetailsService = InMemoryUserDetailsManager(userDetails)\n            http {\n                x509 {\n                    userDetailsService = customUserDetailsService\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService = mockk()\n    }\n\n    @Test\n    fun `x509 when authentication user details service configured then custom user details service used`() {\n        this.spring.register(AuthenticationUserDetailsServiceConfig::class.java).autowire()\n        val certificate = loadCert<X509Certificate>(\"rod.cer\")\n\n        this.mockMvc.perform(get(\"/\")\n                .with(x509(certificate)))\n                .andExpect(authenticated().withUsername(\"rod\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthenticationUserDetailsServiceConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val userDetails = User.withDefaultPasswordEncoder()\n                    .username(\"rod\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            val customUserDetailsService = InMemoryUserDetailsManager(userDetails)\n            val customSource = UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>()\n            customSource.setUserDetailsService(customUserDetailsService)\n            http {\n                x509 {\n                    authenticationUserDetailsService = customSource\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService = mockk()\n    }\n\n    @Test\n    fun `x509 when configured with principal extractor then principal extractor used`() {\n        this.spring.register(X509PrincipalExtractorConfig::class.java).autowire()\n        val certificate = loadCert<X509Certificate>(\"rodatexampledotcom.cer\")\n\n        this.mockMvc.perform(get(\"/\")\n                .with(x509(certificate)))\n                .andExpect(authenticated().withUsername(\"rod\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class X509PrincipalExtractorConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val principalExtractor = SubjectDnX509PrincipalExtractor()\n            principalExtractor.setSubjectDnRegex(\"CN=(.*?)@example.com(?:,|$)\")\n            http {\n                x509 {\n                    x509PrincipalExtractor = principalExtractor\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                    .username(\"rod\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n\n    private fun <T : Certificate> loadCert(location: String): T {\n        ClassPathResource(location).inputStream.use { inputStream ->\n            val certFactory = CertificateFactory.getInstance(\"X.509\")\n            return certFactory.generateCertificate(inputStream) as T\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/CacheControlDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [CacheControlDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass CacheControlDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `headers when cache control configured then cache control headers in response`() {\n        this.spring.register(CacheControlConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\") }\n                    header { string(HttpHeaders.EXPIRES, \"0\") }\n                    header { string(HttpHeaders.PRAGMA, \"no-cache\") }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CacheControlConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    cacheControl { }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when cache control disabled then no cache control headers in response`() {\n        this.spring.register(CacheControlDisabledConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { doesNotExist(HttpHeaders.CACHE_CONTROL) }\n                    header { doesNotExist(HttpHeaders.EXPIRES) }\n                    header { doesNotExist(HttpHeaders.PRAGMA) }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CacheControlDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    cacheControl {\n                        disable()\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/ContentSecurityPolicyDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [ContentSecurityPolicyDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ContentSecurityPolicyDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `headers when content security policy configured then header in response`() {\n        this.spring.register(ContentSecurityPolicyConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY, \"default-src 'self'\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ContentSecurityPolicyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    contentSecurityPolicy { }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when content security policy configured with custom policy directives then custom directives in header`() {\n        this.spring.register(CustomPolicyDirectivesConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY, \"default-src 'self'; script-src trustedscripts.example.com\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomPolicyDirectivesConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    contentSecurityPolicy {\n                        policyDirectives = \"default-src 'self'; script-src trustedscripts.example.com\"\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when report only content security policy report only header in response`() {\n        this.spring.register(ReportOnlyConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY_REPORT_ONLY, \"default-src 'self'\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ReportOnlyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    contentSecurityPolicy {\n                        reportOnly = true\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/ContentTypeOptionsDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [ContentTypeOptionsDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ContentTypeOptionsDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `headers when content type options configured then X-Content-Type-Options header in response`() {\n        this.spring.register(ContentTypeOptionsConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, \"nosniff\") }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ContentTypeOptionsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    contentTypeOptions { }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when content type options disabled then X-Content-Type-Options header not in response`() {\n        this.spring.register(ContentTypeOptionsDisabledConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { doesNotExist(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS) }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ContentTypeOptionsDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    contentTypeOptions {\n                        disable()\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/FrameOptionsDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [FrameOptionsDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass FrameOptionsDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `headers when frame options configured then frame options deny header`() {\n        this.spring.register(FrameOptionsConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FrameOptionsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    frameOptions { }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when frame options deny configured then frame options deny header`() {\n        this.spring.register(FrameOptionsDenyConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FrameOptionsDenyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    frameOptions {\n                        deny = true\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when frame options same origin configured then frame options same origin header`() {\n        this.spring.register(FrameOptionsSameOriginConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN.name) }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FrameOptionsSameOriginConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    frameOptions {\n                        sameOrigin = true\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when frame options same origin and deny configured then frame options deny header`() {\n        this.spring.register(FrameOptionsSameOriginAndDenyConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name) }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FrameOptionsSameOriginAndDenyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    frameOptions {\n                        sameOrigin = true\n                        deny = true\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when frame options disabled then no frame options header in response`() {\n        this.spring.register(FrameOptionsDisabledConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { doesNotExist(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS) }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class FrameOptionsDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    frameOptions {\n                        disable()\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/HttpPublicKeyPinningDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [HttpPublicKeyPinningDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass HttpPublicKeyPinningDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    private val HPKP_RO_HEADER_NAME = \"Public-Key-Pins-Report-Only\"\n    private val HPKP_HEADER_NAME = \"Public-Key-Pins\"\n\n    @Test\n    fun `headers when HPKP configured and no pin then no headers in response`() {\n        this.spring.register(HpkpNoPinConfig::class.java).autowire()\n\n        val result = this.mockMvc.get(\"/\") {\n            secure = true\n        }.andReturn()\n\n        Assertions.assertThat(result.response.headerNames).isEmpty()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HpkpNoPinConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpPublicKeyPinning { }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when HPKP configured with pin then header in response`() {\n        this.spring.register(HpkpPinConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(HPKP_RO_HEADER_NAME, \"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HpkpPinConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpPublicKeyPinning {\n                        pins = mapOf(Pair(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\"))\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when HPKP configured with maximum age then maximum age in header`() {\n        this.spring.register(HpkpMaxAgeConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(HPKP_RO_HEADER_NAME, \"max-age=604800 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HpkpMaxAgeConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpPublicKeyPinning {\n                        pins = mapOf(Pair(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\"))\n                        maxAgeInSeconds = 604800\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when HPKP configured with report only false then public key pins header in response`() {\n        this.spring.register(HpkpReportOnlyFalseConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(HPKP_HEADER_NAME, \"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HpkpReportOnlyFalseConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpPublicKeyPinning {\n                        pins = mapOf(Pair(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\"))\n                        reportOnly = false\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when HPKP configured with include subdomains then include subdomains in header`() {\n        this.spring.register(HpkpIncludeSubdomainsConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header {\n                string(HPKP_RO_HEADER_NAME,\n                        \"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; includeSubDomains\")\n            }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HpkpIncludeSubdomainsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpPublicKeyPinning {\n                        pins = mapOf(Pair(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\"))\n                        includeSubDomains = true\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when HPKP configured with report uri then report uri in header`() {\n        this.spring.register(HpkpReportUriConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header {\n                string(HPKP_RO_HEADER_NAME,\n                        \"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; report-uri=\\\"https://example.com\\\"\")\n            }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HpkpReportUriConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpPublicKeyPinning {\n                        pins = mapOf(Pair(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\"))\n                        reportUri = \"https://example.com\"\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when HPKP disabled then no HPKP header in response`() {\n        this.spring.register(HpkpDisabledConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header {\n                doesNotExist(HPKP_RO_HEADER_NAME)\n            }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HpkpDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    httpPublicKeyPinning {\n                        disable()\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/HttpStrictTransportSecurityDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [HttpStrictTransportSecurityDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass HttpStrictTransportSecurityDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `headers when hsts configured then headers in response`() {\n        this.spring.register(HstsConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HstsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpStrictTransportSecurity { }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when hsts configured with preload then preload in header`() {\n        this.spring.register(HstsPreloadConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains ; preload\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HstsPreloadConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpStrictTransportSecurity {\n                        preload = true\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when hsts configured with max age then max age in header`() {\n        this.spring.register(HstsMaxAgeConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=1 ; includeSubDomains\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HstsMaxAgeConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpStrictTransportSecurity {\n                        maxAgeInSeconds = 1\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when hsts configured and does not match then hsts header not in response`() {\n        this.spring.register(HstsCustomMatcherConfig::class.java).autowire()\n\n        val result = this.mockMvc.get(\"/\") {\n            secure = true\n        }.andReturn()\n\n        Assertions.assertThat(result.response.headerNames).isEmpty()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HstsCustomMatcherConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    httpStrictTransportSecurity {\n                        requestMatcher = PathPatternRequestMatcher.pathPattern(\"/secure/**\")\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `request when hsts disabled then hsts header not in response`() {\n        this.spring.register(HstsDisabledConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { doesNotExist(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY) }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class HstsDisabledConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    httpStrictTransportSecurity {\n                        disable()\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/ReferrerPolicyDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [ReferrerPolicyDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ReferrerPolicyDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `headers when referrer policy configured then header in response`() {\n        this.spring.register(ReferrerPolicyConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(\"Referrer-Policy\", ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFERRER.policy) }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ReferrerPolicyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    referrerPolicy { }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when referrer policy configured with custom policy then custom policy in header`() {\n        this.spring.register(ReferrerPolicyCustomPolicyConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n                .andExpect {\n                    header { string(\"Referrer-Policy\", ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN.policy) }\n                }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ReferrerPolicyCustomPolicyConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    referrerPolicy {\n                        policy = ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/headers/XssProtectionConfigDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.headers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.header.writers.XXssProtectionHeaderWriter\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [XssProtectionConfigDsl]\n *\n * @author Eleftheria Stein\n * @author Daniel Garnier-Moiroux\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass XssProtectionConfigDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `headers when XSS protection configured then header in response`() {\n        this.spring.register(XssProtectionConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class XssProtectionConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    xssProtection { }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when XSS protection disabled then X-XSS-Protection header not in response`() {\n        this.spring.register(XssProtectionDisabledFunctionConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { doesNotExist(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION) }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class XssProtectionDisabledFunctionConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    xssProtection {\n                        disable()\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when XSS protection header value enabled then X-XSS-Protection header is 1`() {\n        this.spring.register(XssProtectionHeaderValueEnabledFunctionConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"1\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class XssProtectionHeaderValueEnabledFunctionConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    xssProtection {\n                        headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `headers when XSS protection header value enabled_mode_block then X-XSS-Protection header is 1 and mode=block`() {\n        this.spring.register(XssProtectionHeaderValueEnabledModeBlockFunctionConfig::class.java).autowire()\n\n        this.mockMvc.get(\"/\") {\n            secure = true\n        }.andExpect {\n            header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"1; mode=block\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class XssProtectionHeaderValueEnabledModeBlockFunctionConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                headers {\n                    defaultsDisabled = true\n                    xssProtection {\n                        headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/oauth2/client/AuthorizationCodeGrantDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.client\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest\nimport org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver\nimport org.springframework.security.oauth2.core.OAuth2AccessToken\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames\nimport org.springframework.security.web.DefaultRedirectStrategy\nimport org.springframework.security.web.RedirectStrategy\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [AuthorizationCodeGrantDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass AuthorizationCodeGrantDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `oauth2Client when custom authorization request repository then repository used`() {\n        this.spring.register(RequestRepositoryConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(RequestRepositoryConfig.REQUEST_REPOSITORY)\n        val authorizationRequest = getOAuth2AuthorizationRequest()\n        every {\n            RequestRepositoryConfig.REQUEST_REPOSITORY.loadAuthorizationRequest(any())\n        } returns authorizationRequest\n        every {\n            RequestRepositoryConfig.REQUEST_REPOSITORY.removeAuthorizationRequest(any(), any())\n        } returns authorizationRequest\n\n        this.mockMvc.get(\"/callback\") {\n            param(\"state\", \"test\")\n            param(\"code\", \"123\")\n        }\n\n        verify(exactly = 1) { RequestRepositoryConfig.REQUEST_REPOSITORY.loadAuthorizationRequest(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RequestRepositoryConfig {\n\n        companion object {\n            val REQUEST_REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> =\n                HttpSessionOAuth2AuthorizationRequestRepository()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Client {\n                    authorizationCodeGrant {\n                        authorizationRequestRepository = REQUEST_REPOSITORY\n                    }\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `oauth2Client when custom authorization redirect strategy then redirect strategy used`() {\n        this.spring.register(RedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(RedirectStrategyConfig.REDIRECT_STRATEGY)\n        every { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }\n\n        this.mockMvc.get(\"/oauth2/authorization/registrationId\")\n\n        verify(exactly = 1) { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }\n    }\n\n    @EnableWebSecurity\n    open class RedirectStrategyConfig {\n\n        companion object {\n            val REDIRECT_STRATEGY: RedirectStrategy = DefaultRedirectStrategy()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Client {\n                    authorizationCodeGrant {\n                        authorizationRedirectStrategy = REDIRECT_STRATEGY\n                    }\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `oauth2Client when custom access token response client then client used`() {\n        this.spring.register(AuthorizedClientConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthorizedClientConfig.REQUEST_REPOSITORY)\n        mockkObject(AuthorizedClientConfig.CLIENT)\n        val authorizationRequest = getOAuth2AuthorizationRequest()\n        every {\n            AuthorizedClientConfig.REQUEST_REPOSITORY.loadAuthorizationRequest(any())\n        } returns authorizationRequest\n        every {\n            AuthorizedClientConfig.REQUEST_REPOSITORY.removeAuthorizationRequest(any(), any())\n        } returns authorizationRequest\n        every {\n            AuthorizedClientConfig.CLIENT.getTokenResponse(any())\n        } returns OAuth2AccessTokenResponse\n            .withToken(\"token\")\n            .tokenType(OAuth2AccessToken.TokenType.BEARER)\n            .build()\n\n        this.mockMvc.get(\"/callback\") {\n            param(\"state\", \"test\")\n            param(\"code\", \"123\")\n        }\n\n        verify(exactly = 1) { AuthorizedClientConfig.CLIENT.getTokenResponse(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthorizedClientConfig {\n        companion object {\n            val REQUEST_REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> =\n                HttpSessionOAuth2AuthorizationRequestRepository()\n            val CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> =\n                RestClientAuthorizationCodeTokenResponseClient()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Client {\n                    authorizationCodeGrant {\n                        authorizationRequestRepository = REQUEST_REPOSITORY\n                        accessTokenResponseClient = CLIENT\n                    }\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `oauth2Client when custom authorization request resolver then request resolver used`() {\n        this.spring.register(RequestResolverConfig::class.java, ClientConfig::class.java).autowire()\n        val requestResolverConfig = this.spring.context.getBean(RequestResolverConfig::class.java)\n        val authorizationRequest = getOAuth2AuthorizationRequest()\n        every {\n            requestResolverConfig.requestResolver.resolve(any())\n        } returns authorizationRequest\n\n        this.mockMvc.get(\"/callback\") {\n            param(\"state\", \"test\")\n            param(\"code\", \"123\")\n        }\n\n        verify(exactly = 1) { requestResolverConfig.requestResolver.resolve(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RequestResolverConfig {\n\n        val requestResolver: OAuth2AuthorizationRequestResolver = mockk()\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Client {\n                    authorizationCodeGrant {\n                        authorizationRequestResolver = requestResolver\n                    }\n                }\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ClientRegistrationRepository {\n            return InMemoryClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\")\n                            .registrationId(\"registrationId\")\n                            .clientId(\"clientId\")\n                            .clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n\n    private fun getOAuth2AuthorizationRequest(): OAuth2AuthorizationRequest? {\n        return OAuth2AuthorizationRequest\n                .authorizationCode()\n                .state(\"test\")\n                .clientId(\"clientId\")\n                .authorizationUri(\"https://test\")\n                .redirectUri(\"http://localhost/callback\")\n                .attributes(mapOf(Pair(OAuth2ParameterNames.REGISTRATION_ID, \"registrationId\")))\n                .build()\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/oauth2/login/AuthorizationEndpointDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport jakarta.servlet.http.HttpServletRequest\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.web.DefaultRedirectStrategy\nimport org.springframework.security.web.RedirectStrategy\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [AuthorizationEndpointDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass AuthorizationEndpointDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `oauth2Login when custom client registration repository then repository used`() {\n        this.spring.register(ResolverConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(ResolverConfig.RESOLVER)\n        every { ResolverConfig.RESOLVER.resolve(any()) }\n\n        this.mockMvc.get(\"/oauth2/authorization/google\")\n\n        verify(exactly = 1) { ResolverConfig.RESOLVER.resolve(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ResolverConfig {\n\n        companion object {\n            val RESOLVER: OAuth2AuthorizationRequestResolver = object : OAuth2AuthorizationRequestResolver {\n                override fun resolve(request: HttpServletRequest) =\n                    OAuth2AuthorizationRequest.authorizationCode().build()\n\n                override fun resolve(request: HttpServletRequest, clientRegistrationId: String) =\n                    OAuth2AuthorizationRequest.authorizationCode().build()\n            }\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login {\n                    authorizationEndpoint {\n                        authorizationRequestResolver = RESOLVER\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `oauth2Login when custom authorization request repository then repository used`() {\n        this.spring.register(RequestRepoConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(RequestRepoConfig.REPOSITORY)\n        every { RequestRepoConfig.REPOSITORY.saveAuthorizationRequest(any(), any(), any()) }\n\n        this.mockMvc.get(\"/oauth2/authorization/google\")\n\n        verify(exactly = 1) { RequestRepoConfig.REPOSITORY.saveAuthorizationRequest(any(), any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class RequestRepoConfig {\n\n        companion object {\n            val REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> =\n                HttpSessionOAuth2AuthorizationRequestRepository()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login {\n                    authorizationEndpoint {\n                        authorizationRequestRepository = REPOSITORY\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `oauth2Login when custom authorization redirect strategy then redirect strategy used`() {\n        this.spring.register(RedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(RedirectStrategyConfig.REDIRECT_STRATEGY)\n        every { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }\n\n        this.mockMvc.get(\"/oauth2/authorization/google\")\n\n        verify(exactly = 1) { RedirectStrategyConfig.REDIRECT_STRATEGY.sendRedirect(any(), any(), any()) }\n    }\n\n    @EnableWebSecurity\n    open class RedirectStrategyConfig {\n\n        companion object {\n            val REDIRECT_STRATEGY: RedirectStrategy = DefaultRedirectStrategy()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login {\n                    authorizationEndpoint {\n                        authorizationRedirectStrategy = REDIRECT_STRATEGY\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `oauth2Login when custom authorization uri repository then uri used`() {\n        this.spring.register(AuthorizationUriConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthorizationUriConfig.REPOSITORY)\n\n        this.mockMvc.get(\"/connect/google\")\n\n        verify(exactly = 1) { AuthorizationUriConfig.REPOSITORY.saveAuthorizationRequest(any(), any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthorizationUriConfig {\n\n        companion object {\n            val REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> =\n                HttpSessionOAuth2AuthorizationRequestRepository()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2Login {\n                    authorizationEndpoint {\n                        authorizationRequestRepository = REPOSITORY\n                        baseUri = \"/connect\"\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ClientRegistrationRepository {\n            return InMemoryClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\").clientId(\"clientId\").clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/oauth2/login/RedirectionEndpointDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.SimpleGrantedAuthority\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest\nimport org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository\nimport org.springframework.security.oauth2.core.OAuth2AccessToken\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User\nimport org.springframework.security.oauth2.core.user.OAuth2User\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [RedirectionEndpointDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass RedirectionEndpointDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `oauth2Login when redirection endpoint configured then custom redirection endpoing used`() {\n        this.spring.register(UserServiceConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(UserServiceConfig.REPOSITORY)\n        mockkObject(UserServiceConfig.CLIENT)\n        mockkObject(UserServiceConfig.USER_SERVICE)\n\n        val registrationId = \"registrationId\"\n        val attributes = HashMap<String, Any>()\n        attributes[OAuth2ParameterNames.REGISTRATION_ID] = registrationId\n        val authorizationRequest = OAuth2AuthorizationRequest\n                .authorizationCode()\n                .state(\"test\")\n                .clientId(\"clientId\")\n                .authorizationUri(\"https://test\")\n                .redirectUri(\"http://localhost/callback\")\n                .attributes(attributes)\n                .build()\n        every {\n            UserServiceConfig.REPOSITORY.removeAuthorizationRequest(any(), any())\n        } returns authorizationRequest\n        every {\n            UserServiceConfig.CLIENT.getTokenResponse(any())\n        } returns OAuth2AccessTokenResponse\n            .withToken(\"token\")\n            .tokenType(OAuth2AccessToken.TokenType.BEARER)\n            .build()\n        every {\n            UserServiceConfig.USER_SERVICE.loadUser(any())\n        } returns DefaultOAuth2User(listOf(SimpleGrantedAuthority(\"ROLE_USER\")), mapOf(Pair(\"user\", \"user\")), \"user\")\n\n        this.mockMvc.get(\"/callback\") {\n            param(\"code\", \"auth-code\")\n            param(\"state\", \"test\")\n        }.andExpect {\n            redirectedUrl(\"/\")\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class UserServiceConfig {\n\n        companion object {\n            val REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> =\n                HttpSessionOAuth2AuthorizationRequestRepository()\n            val CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> =\n                RestClientAuthorizationCodeTokenResponseClient()\n            val USER_SERVICE: OAuth2UserService<OAuth2UserRequest, OAuth2User> = DefaultOAuth2UserService()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2Login {\n                    userInfoEndpoint {\n                        userService = USER_SERVICE\n                    }\n                    tokenEndpoint {\n                        accessTokenResponseClient = CLIENT\n                    }\n                    authorizationEndpoint {\n                        authorizationRequestRepository = REPOSITORY\n                    }\n                    redirectionEndpoint {\n                        baseUri = \"/callback\"\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ClientRegistrationRepository {\n            return InMemoryClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\")\n                            .registrationId(\"registrationId\")\n                            .clientId(\"clientId\")\n                            .clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/oauth2/login/TokenEndpointDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest\nimport org.springframework.security.oauth2.client.endpoint.RestClientAuthorizationCodeTokenResponseClient\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository\nimport org.springframework.security.oauth2.core.OAuth2AccessToken\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [TokenEndpointDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass TokenEndpointDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `oauth2Login when custom access token response client then client used`() {\n        this.spring.register(TokenConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(TokenConfig.REPOSITORY)\n        mockkObject(TokenConfig.CLIENT)\n\n        val registrationId = \"registrationId\"\n        val attributes = HashMap<String, Any>()\n        attributes[OAuth2ParameterNames.REGISTRATION_ID] = registrationId\n        val authorizationRequest = OAuth2AuthorizationRequest\n                .authorizationCode()\n                .state(\"test\")\n                .clientId(\"clientId\")\n                .authorizationUri(\"https://test\")\n                .redirectUri(\"/login/oauth2/code/google\")\n                .attributes(attributes)\n                .build()\n        every {\n            TokenConfig.REPOSITORY.removeAuthorizationRequest(any(), any())\n        } returns authorizationRequest\n        every {\n            TokenConfig.CLIENT.getTokenResponse(any())\n        } returns OAuth2AccessTokenResponse\n            .withToken(\"token\")\n            .tokenType(OAuth2AccessToken.TokenType.BEARER)\n            .build()\n\n        this.mockMvc.get(\"/login/oauth2/code/google\") {\n            param(\"code\", \"auth-code\")\n            param(\"state\", \"test\")\n        }\n\n        verify(exactly = 1) { TokenConfig.CLIENT.getTokenResponse(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class TokenConfig {\n\n        companion object {\n            val REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> =\n                HttpSessionOAuth2AuthorizationRequestRepository()\n            val CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> =\n                RestClientAuthorizationCodeTokenResponseClient()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2Login {\n                    tokenEndpoint {\n                        accessTokenResponseClient = CLIENT\n                    }\n                    authorizationEndpoint {\n                        authorizationRequestRepository = REPOSITORY\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ClientRegistrationRepository {\n            return InMemoryClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\")\n                            .registrationId(\"registrationId\")\n                            .clientId(\"clientId\")\n                            .clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/oauth2/login/UserInfoEndpointDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.login\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.SimpleGrantedAuthority\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\nimport org.springframework.security.oauth2.core.OAuth2AccessToken\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User\nimport org.springframework.security.oauth2.core.user.OAuth2User\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n * Tests for [UserInfoEndpointDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass UserInfoEndpointDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `oauth2Login when custom user service then user service used`() {\n        this.spring.register(UserServiceConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(UserServiceConfig.REPOSITORY)\n        mockkObject(UserServiceConfig.CLIENT)\n        mockkObject(UserServiceConfig.USER_SERVICE)\n\n        val registrationId = \"registrationId\"\n        val attributes = HashMap<String, Any>()\n        attributes[OAuth2ParameterNames.REGISTRATION_ID] = registrationId\n        val authorizationRequest = OAuth2AuthorizationRequest\n                .authorizationCode()\n                .state(\"test\")\n                .clientId(\"clientId\")\n                .authorizationUri(\"https://test\")\n                .redirectUri(\"/login/oauth2/code/google\")\n                .attributes(attributes)\n                .build()\n        every {\n            UserServiceConfig.REPOSITORY.removeAuthorizationRequest(any(), any())\n        } returns authorizationRequest\n        every {\n            UserServiceConfig.CLIENT.getTokenResponse(any())\n        } returns OAuth2AccessTokenResponse\n            .withToken(\"token\")\n            .tokenType(OAuth2AccessToken.TokenType.BEARER)\n            .build()\n        every {\n            UserServiceConfig.USER_SERVICE.loadUser(any())\n        } returns DefaultOAuth2User(listOf(SimpleGrantedAuthority(\"ROLE_USER\")), mapOf(Pair(\"user\", \"user\")), \"user\")\n\n        this.mockMvc.get(\"/login/oauth2/code/google\") {\n            param(\"code\", \"auth-code\")\n            param(\"state\", \"test\")\n        }\n\n        verify(exactly = 1) { UserServiceConfig.USER_SERVICE.loadUser(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class UserServiceConfig {\n\n         companion object {\n             val REPOSITORY: AuthorizationRequestRepository<OAuth2AuthorizationRequest> = mockk()\n             val CLIENT: OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> = mockk()\n             val USER_SERVICE: OAuth2UserService<OAuth2UserRequest, OAuth2User> = mockk()\n         }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2Login {\n                    userInfoEndpoint {\n                        userService = USER_SERVICE\n                    }\n                    tokenEndpoint {\n                        accessTokenResponseClient = CLIENT\n                    }\n                    authorizationEndpoint {\n                        authorizationRequestRepository = REPOSITORY\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ClientRegistrationRepository {\n            return InMemoryClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\")\n                            .registrationId(\"registrationId\")\n                            .clientId(\"clientId\")\n                            .clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/oauth2/resourceserver/JwtDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.resourceserver\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.convert.converter.Converter\nimport org.springframework.security.authentication.*\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames\nimport org.springframework.security.oauth2.jwt.Jwt\nimport org.springframework.security.oauth2.jwt.JwtDecoder\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests for [JwtDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass JwtDslTests {\n\n    private val jwtAuthenticationToken: Authentication = JwtAuthenticationToken(\n        Jwt.withTokenValue(\"token\")\n            .header(\"alg\", \"none\")\n            .claim(IdTokenClaimNames.SUB, \"user\")\n            .subject(\"mock-test-subject\")\n            .build(),\n        emptyList()\n    )\n\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `JWT when custom JWT decoder then bean not required`() {\n        this.spring.register(CustomJwtDecoderConfig::class.java).autowire()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomJwtDecoderConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2ResourceServer {\n                    jwt {\n                        jwtDecoder = mockk()\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `JWT when custom jwkSetUri then bean not required`() {\n        this.spring.register(CustomJwkSetUriConfig::class.java).autowire()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomJwkSetUriConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                oauth2ResourceServer {\n                    jwt {\n                        jwkSetUri = \"https://jwk-uri\"\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `JWT when custom JWT authentication converter then converter used`() {\n        this.spring.register(CustomJwtAuthenticationConverterConfig::class.java).autowire()\n        mockkObject(CustomJwtAuthenticationConverterConfig.CONVERTER)\n        mockkObject(CustomJwtAuthenticationConverterConfig.DECODER)\n        every {\n            CustomJwtAuthenticationConverterConfig.DECODER.decode(any())\n        } returns Jwt.withTokenValue(\"token\")\n            .header(\"alg\", \"none\")\n            .claim(IdTokenClaimNames.SUB, \"user\")\n            .build()\n        every {\n            CustomJwtAuthenticationConverterConfig.CONVERTER.convert(any())\n        } returns TestingAuthenticationToken(\"test\", \"this\", \"ROLE\")\n        this.mockMvc.get(\"/\") {\n            header(\"Authorization\", \"Bearer token\")\n        }\n\n        verify(exactly = 1) { CustomJwtAuthenticationConverterConfig.CONVERTER.convert(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomJwtAuthenticationConverterConfig {\n\n        companion object {\n            val CONVERTER: Converter<Jwt, out AbstractAuthenticationToken> = MockConverter()\n            val DECODER: JwtDecoder = MockJwtDecoder()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    jwt {\n                        jwtAuthenticationConverter = CONVERTER\n                    }\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun jwtDecoder(): JwtDecoder = DECODER\n    }\n\n    class MockConverter: Converter<Jwt, AbstractAuthenticationToken> {\n        override fun convert(source: Jwt): AbstractAuthenticationToken {\n            return TestingAuthenticationToken(\"a\", \"b\",  \"c\")\n        }\n    }\n\n    @Test\n    fun `JWT when custom JWT decoder set after jwkSetUri then decoder used`() {\n        this.spring.register(JwtDecoderAfterJwkSetUriConfig::class.java).autowire()\n        mockkObject(JwtDecoderAfterJwkSetUriConfig.DECODER)\n        every {\n            JwtDecoderAfterJwkSetUriConfig.DECODER.decode(any())\n        } returns Jwt.withTokenValue(\"token\")\n            .header(\"alg\", \"none\")\n            .claim(IdTokenClaimNames.SUB, \"user\")\n            .build()\n\n        this.mockMvc.get(\"/\") {\n            header(\"Authorization\", \"Bearer token\")\n        }\n\n        verify(exactly = 1) { JwtDecoderAfterJwkSetUriConfig.DECODER.decode(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class JwtDecoderAfterJwkSetUriConfig {\n\n        companion object {\n            val DECODER: JwtDecoder = MockJwtDecoder()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    jwt {\n                        jwkSetUri = \"https://jwk-uri\"\n                        jwtDecoder = DECODER\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    class MockJwtDecoder: JwtDecoder {\n        override fun decode(token: String): Jwt {\n            return Jwt.withTokenValue(\"some tokenValue\").build()\n        }\n    }\n\n    @Test\n    fun `JWT when custom authentication manager configured then used`() {\n        this.spring.register(AuthenticationManagerConfig::class.java, AuthenticationController::class.java).autowire()\n        mockkObject(AuthenticationManagerConfig.AUTHENTICATION_MANAGER)\n        every {\n            AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any())\n        } returns this.jwtAuthenticationToken\n\n        this.mockMvc.get(\"/authenticated\") {\n            header(\"Authorization\", \"Bearer token\")\n        }.andExpect {\n            status { isOk() }\n            content { string(\"mock-test-subject\") }\n        }\n\n        verify(exactly = 1) { AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthenticationManagerConfig {\n\n        companion object {\n            val AUTHENTICATION_MANAGER: AuthenticationManager = ProviderManager(TestingAuthenticationProvider())\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    jwt {\n                        authenticationManager = AUTHENTICATION_MANAGER\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @RestController\n    class AuthenticationController {\n        @GetMapping(\"/authenticated\")\n        fun authenticated(authentication: Authentication): String {\n            return authentication.name\n        }\n    }\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/oauth2/resourceserver/OpaqueTokenDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.oauth2.resourceserver\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.ParameterizedTypeReference\nimport org.springframework.http.HttpHeaders\nimport org.springframework.http.HttpStatus\nimport org.springframework.http.MediaType\nimport org.springframework.http.ResponseEntity\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.authentication.ProviderManager\nimport org.springframework.security.authentication.TestingAuthenticationProvider\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens\nimport org.springframework.security.oauth2.jwt.JwtClaimNames\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector\nimport org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.client.RestOperations\nimport org.springframework.web.client.RestTemplate\n\n/**\n * Tests for [OpaqueTokenDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass OpaqueTokenDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    private val introspectionAuthenticationToken = BearerTokenAuthentication(\n        DefaultOAuth2AuthenticatedPrincipal(\n            mapOf(\n                Pair(\n                    JwtClaimNames.SUB,\n                    \"mock-test-subject\"\n                )\n            ), emptyList()\n        ),\n        TestOAuth2AccessTokens.noScopes(), emptyList()\n    )\n\n    @Test\n    fun `opaque token when defaults then uses introspection`() {\n        this.spring.register(DefaultOpaqueConfig::class.java, AuthenticationController::class.java).autowire()\n        mockkObject(DefaultOpaqueConfig.REST)\n        val headers = HttpHeaders().apply {\n            contentType = MediaType.APPLICATION_JSON\n        }\n        val responseBody: Map<String, Any> = mapOf(\n            \"active\" to true,\n            \"sub\" to \"test-subject\",\n            \"scope\" to \"message:read\",\n            \"exp\" to 4683883211\n        )\n        every {\n            DefaultOpaqueConfig.REST.exchange(any(), any<ParameterizedTypeReference<Map<String, Any>>>())\n        } returns ResponseEntity(responseBody, headers, HttpStatus.OK)\n\n        this.mockMvc.get(\"/authenticated\") {\n            header(\"Authorization\", \"Bearer token\")\n        }.andExpect {\n            status { isOk() }\n            content { string(\"test-subject\") }\n        }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class DefaultOpaqueConfig {\n\n        companion object {\n            val REST: RestOperations = RestTemplate()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    opaqueToken { }\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun rest(): RestOperations = REST\n\n        @Bean\n        open fun tokenIntrospectionClient(): OpaqueTokenIntrospector {\n            return SpringOpaqueTokenIntrospector(\"https://example.org/introspect\", REST)\n        }\n    }\n\n    @Test\n    fun `opaque token when custom introspector set then introspector used`() {\n        this.spring.register(CustomIntrospectorConfig::class.java, AuthenticationController::class.java).autowire()\n        mockkObject(CustomIntrospectorConfig.INTROSPECTOR)\n\n        every {\n            CustomIntrospectorConfig.INTROSPECTOR.introspect(any())\n        } returns DefaultOAuth2AuthenticatedPrincipal(mapOf(Pair(JwtClaimNames.SUB, \"mock-subject\")), emptyList())\n\n        this.mockMvc.get(\"/authenticated\") {\n            header(\"Authorization\", \"Bearer token\")\n        }\n\n        verify(exactly = 1) { CustomIntrospectorConfig.INTROSPECTOR.introspect(\"token\") }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class CustomIntrospectorConfig {\n\n        companion object {\n            val INTROSPECTOR: OpaqueTokenIntrospector = SpringOpaqueTokenIntrospector(\"uri\", \"clientId\", \"clientSecret\")\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    opaqueToken {\n                        introspector = INTROSPECTOR\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `opaque token when custom introspector set after client credentials then introspector used`() {\n        this.spring.register(IntrospectorAfterClientCredentialsConfig::class.java, AuthenticationController::class.java).autowire()\n        mockkObject(IntrospectorAfterClientCredentialsConfig.INTROSPECTOR)\n        every {\n            IntrospectorAfterClientCredentialsConfig.INTROSPECTOR.introspect(any())\n        } returns DefaultOAuth2AuthenticatedPrincipal(mapOf(Pair(JwtClaimNames.SUB, \"mock-subject\")), emptyList())\n\n        this.mockMvc.get(\"/authenticated\") {\n            header(\"Authorization\", \"Bearer token\")\n        }\n\n        verify(exactly = 1) { IntrospectorAfterClientCredentialsConfig.INTROSPECTOR.introspect(\"token\") }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class IntrospectorAfterClientCredentialsConfig {\n\n        companion object {\n            val INTROSPECTOR: OpaqueTokenIntrospector = SpringOpaqueTokenIntrospector(\"uri\", \"clientId\", \"clientSecret\")\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    opaqueToken {\n                        introspectionUri = \"/introspect\"\n                        introspectionClientCredentials(\"clientId\", \"clientSecret\")\n                        introspector = INTROSPECTOR\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `opaque token when custom authentication manager configured then used`() {\n        this.spring.register(AuthenticationManagerConfig::class.java, AuthenticationController::class.java).autowire()\n        mockkObject(AuthenticationManagerConfig.AUTHENTICATION_MANAGER)\n        every {\n            AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any())\n        } returns this.introspectionAuthenticationToken\n\n        this.mockMvc.get(\"/authenticated\") {\n            header(\"Authorization\", \"Bearer token\")\n        }.andExpect {\n            status { isOk() }\n            content { string(\"mock-test-subject\") }\n        }\n\n        verify(exactly = 1) { AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class AuthenticationManagerConfig {\n\n        companion object {\n            val AUTHENTICATION_MANAGER: AuthenticationManager = ProviderManager(TestingAuthenticationProvider())\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(anyRequest, authenticated)\n                }\n                oauth2ResourceServer {\n                    opaqueToken {\n                        authenticationManager = AUTHENTICATION_MANAGER\n                    }\n                }\n            }\n            return http.build()\n        }\n    }\n\n    @RestController\n    class AuthenticationController {\n        @GetMapping(\"/authenticated\")\n        fun authenticated(authentication: Authentication): String {\n            return authentication.name\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/session/SessionConcurrencyDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.session\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.mock.web.MockHttpSession\nimport org.springframework.security.authorization.AuthorityAuthorizationManager\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.session.SessionInformation\nimport org.springframework.security.core.session.SessionRegistry\nimport org.springframework.security.core.session.SessionRegistryImpl\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport java.util.*\n\n/**\n * Tests for [SessionConcurrencyDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass SessionConcurrencyDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `session concurrency when maximum sessions then no more sessions allowed`() {\n        this.spring.register(MaximumSessionsConfig::class.java, UserDetailsConfig::class.java).autowire()\n\n        this.mockMvc.perform(post(\"/login\")\n                .with(csrf())\n                .param(\"username\", \"user\")\n                .param(\"password\", \"password\"))\n\n        this.mockMvc.perform(post(\"/login\")\n                .with(csrf())\n                .param(\"username\", \"user\")\n                .param(\"password\", \"password\"))\n                .andExpect(status().isFound)\n                .andExpect(redirectedUrl(\"/login?error\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class MaximumSessionsConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    sessionConcurrency {\n                        maximumSessions = 1\n                        maxSessionsPreventsLogin = true\n                    }\n                }\n                formLogin { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session concurrency when expired url then redirects to url`() {\n        this.spring.register(ExpiredUrlConfig::class.java).autowire()\n        mockkObject(ExpiredUrlConfig.SESSION_REGISTRY)\n\n        val session = MockHttpSession()\n        val sessionInformation = SessionInformation(\"\", session.id, Date(0))\n        sessionInformation.expireNow()\n        every { ExpiredUrlConfig.SESSION_REGISTRY.getSessionInformation(any()) } returns sessionInformation\n\n        this.mockMvc.perform(get(\"/\").session(session))\n                .andExpect(redirectedUrl(\"/expired-session\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ExpiredUrlConfig {\n\n        companion object {\n            val SESSION_REGISTRY: SessionRegistry = SessionRegistryImpl()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    sessionConcurrency {\n                        maximumSessions = 1\n                        expiredUrl = \"/expired-session\"\n                        sessionRegistry = SESSION_REGISTRY\n                    }\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun sessionRegistry(): SessionRegistry = SESSION_REGISTRY\n    }\n\n    @Test\n    fun `session concurrency when expired session strategy then strategy used`() {\n        this.spring.register(ExpiredSessionStrategyConfig::class.java).autowire()\n        mockkObject(ExpiredSessionStrategyConfig.SESSION_REGISTRY)\n\n        val session = MockHttpSession()\n        val sessionInformation = SessionInformation(\"\", session.id, Date(0))\n        sessionInformation.expireNow()\n        every { ExpiredSessionStrategyConfig.SESSION_REGISTRY.getSessionInformation(any()) } returns sessionInformation\n\n        this.mockMvc.perform(get(\"/\").session(session))\n                .andExpect(redirectedUrl(\"/expired-session\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ExpiredSessionStrategyConfig {\n\n        companion object {\n            val SESSION_REGISTRY: SessionRegistry = SessionRegistryImpl()\n        }\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    sessionConcurrency {\n                        maximumSessions = 1\n                        expiredSessionStrategy = SimpleRedirectSessionInformationExpiredStrategy(\"/expired-session\")\n                        sessionRegistry = SESSION_REGISTRY\n                    }\n                }\n            }\n            return http.build()\n        }\n\n        @Bean\n        open fun sessionRegistry(): SessionRegistry = SESSION_REGISTRY\n    }\n\n    @Test\n    fun `session concurrency when session limit then no more sessions allowed`() {\n        this.spring.register(MaximumSessionsFunctionConfig::class.java, UserDetailsConfig::class.java).autowire()\n\n        this.mockMvc.perform(post(\"/login\")\n            .with(csrf())\n            .param(\"username\", \"user\")\n            .param(\"password\", \"password\"))\n\n        this.mockMvc.perform(post(\"/login\")\n            .with(csrf())\n            .param(\"username\", \"user\")\n            .param(\"password\", \"password\"))\n            .andExpect(status().isFound)\n            .andExpect(redirectedUrl(\"/login?error\"))\n\n        this.mockMvc.perform(post(\"/login\")\n            .with(csrf())\n            .param(\"username\", \"admin\")\n            .param(\"password\", \"password\"))\n            .andExpect(status().isFound)\n            .andExpect(redirectedUrl(\"/\"))\n\n        this.mockMvc.perform(post(\"/login\")\n            .with(csrf())\n            .param(\"username\", \"admin\")\n            .param(\"password\", \"password\"))\n            .andExpect(status().isFound)\n            .andExpect(redirectedUrl(\"/\"))\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class MaximumSessionsFunctionConfig {\n\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            val isAdmin: AuthorizationManager<Any> = AuthorityAuthorizationManager.hasRole(\"ADMIN\")\n            http {\n                sessionManagement {\n                    sessionConcurrency {\n                        maximumSessions {\n                            authentication -> if (isAdmin.authorize({ authentication }, \"\")!!.isGranted) -1 else 1\n                        }\n                        maxSessionsPreventsLogin = true\n                    }\n                }\n                formLogin { }\n            }\n            return http.build()\n        }\n\n    }\n\n    @Configuration\n    open class UserDetailsConfig {\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            val admin = User.withDefaultPasswordEncoder()\n                .username(\"admin\")\n                .password(\"password\")\n                .roles(\"ADMIN\")\n                .build()\n            return InMemoryUserDetailsManager(user, admin)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/annotation/web/session/SessionFixationDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.annotation.web.session\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.mock.web.MockHttpSession\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\n\n/**\n * Tests for [SessionFixationDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass SessionFixationDslTests {\n    @JvmField\n    var spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `session fixation when strategy is new session then new session created and attributes are not preserved`() {\n        this.spring.register(NewSessionConfig::class.java, UserDetailsConfig::class.java).autowire()\n        val givenSession = MockHttpSession()\n        val givenSessionId = givenSession.id\n        givenSession.clearAttributes()\n        givenSession.setAttribute(\"name\", \"value\")\n\n        val result = this.mockMvc.perform(MockMvcRequestBuilders.get(\"/\")\n                .with(httpBasic(\"user\", \"password\"))\n                .session(givenSession))\n                .andReturn()\n\n        val resultingSession = result.request.getSession(false)\n        assertThat(resultingSession).isNotEqualTo(givenSession)\n        assertThat(resultingSession!!.id).isNotEqualTo(givenSessionId)\n        assertThat(resultingSession.getAttribute(\"name\")).isNull()\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class NewSessionConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    requireExplicitAuthenticationStrategy = false\n                    sessionFixation {\n                        newSession()\n                    }\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session fixation when strategy is migrate session then new session created and attributes are preserved`() {\n        this.spring.register(MigrateSessionConfig::class.java, UserDetailsConfig::class.java).autowire()\n        val givenSession = MockHttpSession()\n        val givenSessionId = givenSession.id\n        givenSession.clearAttributes()\n        givenSession.setAttribute(\"name\", \"value\")\n\n        val result = this.mockMvc.perform(MockMvcRequestBuilders.get(\"/\")\n                .with(httpBasic(\"user\", \"password\"))\n                .session(givenSession))\n                .andReturn()\n\n        val resultingSession = result.request.getSession(false)\n        assertThat(resultingSession).isNotEqualTo(givenSession)\n        assertThat(resultingSession!!.id).isNotEqualTo(givenSessionId)\n        assertThat(resultingSession.getAttribute(\"name\")).isEqualTo(\"value\")\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class MigrateSessionConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    requireExplicitAuthenticationStrategy = false\n                    sessionFixation {\n                        migrateSession()\n                    }\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session fixation when strategy is change session id then session id changes and attributes preserved`() {\n        this.spring.register(ChangeSessionIdConfig::class.java, UserDetailsConfig::class.java).autowire()\n        val givenSession = MockHttpSession()\n        val givenSessionId = givenSession.id\n        givenSession.clearAttributes()\n        givenSession.setAttribute(\"name\", \"value\")\n\n        val result = this.mockMvc.perform(MockMvcRequestBuilders.get(\"/\")\n                .with(httpBasic(\"user\", \"password\"))\n                .session(givenSession))\n                .andReturn()\n\n        val resultingSession = result.request.getSession(false)\n        assertThat(resultingSession).isEqualTo(givenSession)\n        assertThat(resultingSession!!.id).isNotEqualTo(givenSessionId)\n        assertThat(resultingSession.getAttribute(\"name\")).isEqualTo(\"value\")\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class ChangeSessionIdConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    requireExplicitAuthenticationStrategy = false\n                    sessionFixation {\n                        changeSessionId()\n                    }\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n    }\n\n    @Test\n    fun `session fixation when strategy is none then session does not change`() {\n        this.spring.register(NoneConfig::class.java, UserDetailsConfig::class.java).autowire()\n        val givenSession = MockHttpSession()\n        val givenSessionId = givenSession.id\n        givenSession.clearAttributes()\n        givenSession.setAttribute(\"name\", \"value\")\n\n        val result = this.mockMvc.perform(MockMvcRequestBuilders.get(\"/\")\n                .with(httpBasic(\"user\", \"password\"))\n                .session(givenSession))\n                .andReturn()\n\n        val resultingSession = result.request.getSession(false)\n        assertThat(resultingSession).isEqualTo(givenSession)\n        assertThat(resultingSession!!.id).isEqualTo(givenSessionId)\n        assertThat(resultingSession.getAttribute(\"name\")).isEqualTo(\"value\")\n    }\n\n    @Configuration\n    @EnableWebSecurity\n    open class NoneConfig {\n        @Bean\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                sessionManagement {\n                    sessionFixation {\n                        none()\n                    }\n                }\n                httpBasic { }\n            }\n            return http.build()\n        }\n    }\n\n    @Configuration\n    open class UserDetailsConfig {\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val userDetails = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return InMemoryUserDetailsManager(userDetails)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/AuthorizeExchangeDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.bind.annotation.RequestMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport java.util.Base64\n\n/**\n * Tests for [AuthorizeExchangeDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass AuthorizeExchangeDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when secured by matcher then responds with unauthorized`() {\n        this.spring.register(MatcherAuthenticatedConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isUnauthorized\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class MatcherAuthenticatedConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when allowed by matcher then responds with ok`() {\n        this.spring.register(MatcherPermitAllConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class MatcherPermitAllConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, permitAll)\n                }\n            }\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/\")\n            fun path() {\n            }\n        }\n    }\n\n    @Test\n    fun `request when secured by pattern then responds with unauthorized`() {\n        this.spring.register(PatternAuthenticatedConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isUnauthorized\n    }\n\n    @Test\n    fun `request when allowed by pattern then responds with ok`() {\n        this.spring.register(PatternAuthenticatedConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/public\")\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PatternAuthenticatedConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(\"/public\", permitAll)\n                    authorize(\"/**\", authenticated)\n                }\n            }\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/public\")\n            fun public() {\n            }\n        }\n    }\n\n    @Test\n    fun `request when missing required role then responds with forbidden`() {\n        this.spring.register(HasRoleConfig::class.java).autowire()\n        this.client\n                .get()\n                .uri(\"/\")\n                .header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString(\"user:password\".toByteArray()))\n                .exchange()\n                .expectStatus().isForbidden\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HasRoleConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, hasRole(\"ADMIN\"))\n                }\n                httpBasic { }\n            }\n        }\n\n        @Bean\n        open fun userDetailsService(): MapReactiveUserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return MapReactiveUserDetailsService(user)\n        }\n    }\n\n    @Test\n    fun `request when ip address does not match then responds with forbidden`() {\n        this.spring.register(HasIpAddressConfig::class.java).autowire()\n\n        this.client\n            .get()\n            .uri(\"/\")\n            .header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString(\"user:password\".toByteArray()))\n            .exchange()\n            .expectStatus().isForbidden\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HasIpAddressConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, hasIpAddress(\"10.0.0.0/24\"))\n                }\n                httpBasic { }\n            }\n        }\n\n        @Bean\n        open fun userDetailsService(): MapReactiveUserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .roles(\"USER\")\n                .build()\n            return MapReactiveUserDetailsService(user)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerAnonymousDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authentication.AnonymousAuthenticationToken\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.core.annotation.AuthenticationPrincipal\nimport org.springframework.security.core.authority.SimpleGrantedAuthority\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.test.web.reactive.server.expectBody\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerAnonymousDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerAnonymousDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `authentication when anonymous enabled then is of type anonymous authentication`() {\n        this.spring.register(AnonymousConfig::class.java, HttpMeController::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/principal\")\n                .exchange()\n                .expectStatus().isOk\n                .expectBody<String>().isEqualTo(\"anonymousUser\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AnonymousConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                anonymous { }\n            }\n        }\n    }\n\n    @Test\n    fun `anonymous when custom principal specified then custom principal is used`() {\n        this.spring.register(CustomPrincipalConfig::class.java, HttpMeController::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/principal\")\n                .exchange()\n                .expectStatus().isOk\n                .expectBody<String>().isEqualTo(\"anon\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomPrincipalConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                anonymous {\n                    principal = \"anon\"\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `anonymous when disabled then principal is null`() {\n        this.spring.register(AnonymousDisabledConfig::class.java, HttpMeController::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/principal\")\n                .exchange()\n                .expectStatus().isOk\n                .expectBody<String>().consumeWith { body -> assertThat(body.responseBody).isNull() }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AnonymousDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                anonymous {\n                    disable()\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `anonymous when custom key specified then custom key used`() {\n        this.spring.register(CustomKeyConfig::class.java, HttpMeController::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/key\")\n                .exchange()\n                .expectStatus().isOk\n                .expectBody<String>().isEqualTo(\"key\".hashCode().toString())\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomKeyConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                anonymous {\n                    key = \"key\"\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `anonymous when custom authorities specified then custom authorities used`() {\n        this.spring.register(CustomAuthoritiesConfig::class.java, HttpMeController::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/principal\")\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomAuthoritiesConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                anonymous {\n                    authorities = listOf(SimpleGrantedAuthority(\"TEST\"))\n                }\n                authorizeExchange {\n                    authorize(anyExchange, hasAuthority(\"TEST\"))\n                }\n            }\n        }\n    }\n\n    @RestController\n    class HttpMeController {\n        @GetMapping(\"/principal\")\n        fun principal(@AuthenticationPrincipal principal: String?): String? {\n            return principal\n        }\n\n        @GetMapping(\"/key\")\n        fun key(@AuthenticationPrincipal principal: Mono<AnonymousAuthenticationToken>): Mono<String> {\n            return principal\n                    .map { it.keyHash }\n                    .map { it.toString() }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerCacheControlDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerCacheControlDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerCacheControlDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when cache control configured then cache headers in response`() {\n        this.spring.register(CacheControlConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\")\n                .expectHeader().valueEquals(HttpHeaders.EXPIRES, \"0\")\n                .expectHeader().valueEquals(HttpHeaders.PRAGMA, \"no-cache\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CacheControlConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    cache { }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when cache control disabled then no cache headers in response`() {\n        this.spring.register(CacheControlDisabledConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().doesNotExist(HttpHeaders.CACHE_CONTROL)\n                .expectHeader().doesNotExist(HttpHeaders.EXPIRES)\n                .expectHeader().doesNotExist(HttpHeaders.PRAGMA)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CacheControlDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    cache {\n                        disable()\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerContentSecurityPolicyDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerContentSecurityPolicyDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerContentSecurityPolicyDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when content security policy configured then content security policy header in response`() {\n        this.spring.register(ContentSecurityPolicyConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY, \"default-src 'self'\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class ContentSecurityPolicyConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    contentSecurityPolicy { }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when custom policy directives then custom policy directive in response header`() {\n        this.spring.register(CustomPolicyDirectivesConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY, \"default-src 'self'; script-src trustedscripts.example.com\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomPolicyDirectivesConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    contentSecurityPolicy {\n                        policyDirectives = \"default-src 'self'; script-src trustedscripts.example.com\"\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when report only configured then content security policy report only header in response`() {\n        this.spring.register(ReportOnlyConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY_REPORT_ONLY, \"default-src 'self'\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class ReportOnlyConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    contentSecurityPolicy {\n                        reportOnly = true\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerContentTypeOptionsDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerContentTypeOptionsDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerContentTypeOptionsDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when content type options configured then header in response`() {\n        this.spring.register(ContentTypeOptionsConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, \"nosniff\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class ContentTypeOptionsConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    contentTypeOptions { }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when content type options disabled then no content type options header in response`() {\n        this.spring.register(ContentTypeOptionsDisabledConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().doesNotExist(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class ContentTypeOptionsDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    contentTypeOptions {\n                        disable()\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerCorsDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.cors.CorsConfiguration\nimport org.springframework.web.cors.reactive.CorsConfigurationSource\nimport org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerCorsDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerCorsDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when CORS configured using bean then Access-Control-Allow-Origin header in response`() {\n        this.spring.register(CorsBeanConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .header(HttpHeaders.ORIGIN, \"https://origin.example.com\")\n                .exchange()\n                .expectHeader().valueEquals(\"Access-Control-Allow-Origin\", \"*\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CorsBeanConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                cors { }\n            }\n        }\n\n        @Bean\n        open fun corsConfigurationSource(): CorsConfigurationSource {\n            val source = UrlBasedCorsConfigurationSource()\n            val corsConfiguration = CorsConfiguration()\n            corsConfiguration.allowedOrigins = listOf(\"*\")\n            source.registerCorsConfiguration(\"/**\", corsConfiguration)\n            return source\n        }\n    }\n\n    @Test\n    fun `request when CORS configured using source then Access-Control-Allow-Origin header in response`() {\n        this.spring.register(CorsSourceConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .header(HttpHeaders.ORIGIN, \"https://origin.example.com\")\n                .exchange()\n                .expectHeader().valueEquals(\"Access-Control-Allow-Origin\", \"*\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CorsSourceConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            val source = UrlBasedCorsConfigurationSource()\n            val corsConfiguration = CorsConfiguration()\n            corsConfiguration.allowedOrigins = listOf(\"*\")\n            source.registerCorsConfiguration(\"/**\", corsConfiguration)\n            return http {\n                cors {\n                    configurationSource = source\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when CORS disabled then no Access-Control-Allow-Origin header in response`() {\n        this.spring.register(CorsDisabledConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .header(HttpHeaders.ORIGIN, \"https://origin.example.com\")\n                .exchange()\n                .expectHeader().doesNotExist(\"Access-Control-Allow-Origin\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CorsDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                cors {\n                    disable()\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerCsrfDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.http.MediaType\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest\nimport org.springframework.mock.web.server.MockServerWebExchange\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler\nimport org.springframework.security.web.server.authorization.ServerAccessDeniedHandler\nimport org.springframework.security.web.server.csrf.CsrfToken\nimport org.springframework.security.web.server.csrf.DefaultCsrfToken\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRepository\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRequestAttributeHandler\nimport org.springframework.security.web.server.csrf.ServerCsrfTokenRequestHandler\nimport org.springframework.security.web.server.csrf.WebSessionServerCsrfTokenRepository\nimport org.springframework.security.web.server.csrf.XorServerCsrfTokenRequestAttributeHandler\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.bind.annotation.PostMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport org.springframework.web.reactive.function.BodyInserters.fromMultipartData\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerCsrfDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerCsrfDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private val token: CsrfToken = DefaultCsrfToken(\"csrf\", \"CSRF\", \"a\")\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `post when CSRF protection enabled then requires CSRF token`() {\n        this.spring.register(CsrfConfig::class.java).autowire()\n\n        this.client.post()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isForbidden\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CsrfConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                csrf { }\n            }\n        }\n    }\n\n    @Test\n    fun `post when CSRF protection disabled then CSRF token is not required`() {\n        this.spring.register(CsrfDisabledConfig::class.java).autowire()\n\n        this.client.post()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CsrfDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                csrf {\n                    disable()\n                }\n            }\n        }\n\n        @RestController\n        internal class TestController {\n            @PostMapping(\"/\")\n            fun home() {\n            }\n        }\n    }\n\n    @Test\n    fun `post when request matches CSRF matcher then CSRF token required`() {\n        this.spring.register(CsrfMatcherConfig::class.java).autowire()\n\n        this.client.post()\n                .uri(\"/csrf\")\n                .exchange()\n                .expectStatus().isForbidden\n    }\n\n    @Test\n    fun `post when request does not match CSRF matcher then CSRF token is not required`() {\n        this.spring.register(CsrfMatcherConfig::class.java).autowire()\n\n        this.client.post()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CsrfMatcherConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                csrf {\n                    requireCsrfProtectionMatcher = PathPatternParserServerWebExchangeMatcher(\"/csrf\")\n                }\n            }\n        }\n\n        @RestController\n        internal class TestController {\n            @PostMapping(\"/\")\n            fun home() {\n            }\n\n            @PostMapping(\"/csrf\")\n            fun csrf() {\n            }\n        }\n    }\n\n    @Test\n    fun `csrf when custom access denied handler then handler used`() {\n        this.spring.register(CustomAccessDeniedHandlerConfig::class.java).autowire()\n        mockkObject(CustomAccessDeniedHandlerConfig.ACCESS_DENIED_HANDLER)\n\n        this.client.post()\n                .uri(\"/\")\n                .exchange()\n\n        verify(exactly = 1) { CustomAccessDeniedHandlerConfig.ACCESS_DENIED_HANDLER.handle(any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomAccessDeniedHandlerConfig {\n        companion object {\n            val ACCESS_DENIED_HANDLER: ServerAccessDeniedHandler = HttpStatusServerAccessDeniedHandler(HttpStatus.FORBIDDEN)\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                csrf {\n                    accessDeniedHandler = ACCESS_DENIED_HANDLER\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `csrf when custom token repository then repository used`() {\n        this.spring.register(CustomCsrfTokenRepositoryConfig::class.java).autowire()\n        mockkObject(CustomCsrfTokenRepositoryConfig.TOKEN_REPOSITORY)\n        every {\n            CustomCsrfTokenRepositoryConfig.TOKEN_REPOSITORY.loadToken(any())\n        } returns Mono.just(this.token)\n\n        this.client.post()\n                .uri(\"/\")\n                .exchange()\n\n        verify(exactly = 1) { CustomCsrfTokenRepositoryConfig.TOKEN_REPOSITORY.loadToken(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomCsrfTokenRepositoryConfig {\n        companion object {\n            val TOKEN_REPOSITORY: ServerCsrfTokenRepository = WebSessionServerCsrfTokenRepository()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                csrf {\n                    csrfTokenRepository = TOKEN_REPOSITORY\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `csrf when multipart form data and not enabled then denied`() {\n        this.spring.register(MultipartFormDataNotEnabledConfig::class.java).autowire()\n        mockkObject(MultipartFormDataNotEnabledConfig.TOKEN_REPOSITORY)\n        every {\n            MultipartFormDataNotEnabledConfig.TOKEN_REPOSITORY.loadToken(any())\n        } returns Mono.just(this.token)\n        every {\n            MultipartFormDataNotEnabledConfig.TOKEN_REPOSITORY.generateToken(any())\n        } returns Mono.just(this.token)\n\n        this.client.post()\n                .uri(\"/\")\n                .contentType(MediaType.MULTIPART_FORM_DATA)\n                .body(fromMultipartData(this.token.parameterName, this.token.token))\n                .exchange()\n                .expectStatus().isForbidden\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class MultipartFormDataNotEnabledConfig {\n        companion object {\n            val TOKEN_REPOSITORY: ServerCsrfTokenRepository = WebSessionServerCsrfTokenRepository()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                csrf {\n                    csrfTokenRepository = TOKEN_REPOSITORY\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `csrf when multipart form data and enabled then granted`() {\n        this.spring.register(MultipartFormDataEnabledConfig::class.java).autowire()\n        mockkObject(MultipartFormDataEnabledConfig.TOKEN_REPOSITORY)\n        every {\n            MultipartFormDataEnabledConfig.TOKEN_REPOSITORY.loadToken(any())\n        } returns Mono.just(this.token)\n        every {\n            MultipartFormDataEnabledConfig.TOKEN_REPOSITORY.generateToken(any())\n        } returns Mono.just(this.token)\n\n        val csrfToken = createXorCsrfToken()\n        this.client.post()\n                .uri(\"/\")\n                .contentType(MediaType.MULTIPART_FORM_DATA)\n                .body(fromMultipartData(csrfToken.parameterName, csrfToken.token))\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    private fun createXorCsrfToken(): CsrfToken {\n        val handler = XorServerCsrfTokenRequestAttributeHandler()\n        val exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"))\n        handler.handle(exchange, Mono.just(this.token))\n        val deferredCsrfToken: Mono<CsrfToken>? = exchange.getAttribute(CsrfToken::class.java.name)\n        return deferredCsrfToken?.block()!!\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class MultipartFormDataEnabledConfig {\n        companion object {\n            val TOKEN_REPOSITORY: ServerCsrfTokenRepository = WebSessionServerCsrfTokenRepository()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                csrf {\n                    csrfTokenRepository = TOKEN_REPOSITORY\n                    csrfTokenRequestHandler = XorServerCsrfTokenRequestAttributeHandler().apply {\n                        setTokenFromMultipartDataEnabled(true)\n                    }\n                }\n            }\n        }\n\n        @RestController\n        internal class TestController {\n            @PostMapping(\"/\")\n            fun home() {\n            }\n        }\n    }\n\n    @Test\n    fun `csrf when custom request handler then handler used`() {\n        this.spring.register(CustomRequestHandlerConfig::class.java).autowire()\n        mockkObject(CustomRequestHandlerConfig.REPOSITORY)\n        every {\n            CustomRequestHandlerConfig.REPOSITORY.loadToken(any())\n        } returns Mono.just(this.token)\n        mockkObject(CustomRequestHandlerConfig.HANDLER)\n        every {\n            CustomRequestHandlerConfig.HANDLER.handle(any(), any())\n        } returns Unit\n        every {\n            CustomRequestHandlerConfig.HANDLER.resolveCsrfTokenValue(any(), any())\n        } returns Mono.just(this.token.token)\n\n        this.client.post()\n            .uri(\"/\")\n            .exchange()\n            .expectStatus().isOk\n        verify(exactly = 2) { CustomRequestHandlerConfig.REPOSITORY.loadToken(any()) }\n        verify(exactly = 1) { CustomRequestHandlerConfig.HANDLER.resolveCsrfTokenValue(any(), any()) }\n        verify(exactly = 1) { CustomRequestHandlerConfig.HANDLER.handle(any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomRequestHandlerConfig {\n        companion object {\n            val REPOSITORY: ServerCsrfTokenRepository = WebSessionServerCsrfTokenRepository()\n            val HANDLER: ServerCsrfTokenRequestHandler = ServerCsrfTokenRequestAttributeHandler()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                csrf {\n                    csrfTokenRepository = REPOSITORY\n                    csrfTokenRequestHandler = HANDLER\n                }\n            }\n        }\n\n        @RestController\n        internal class TestController {\n            @PostMapping(\"/\")\n            fun home() {\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerExceptionHandlingDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint\nimport org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport java.util.*\n\n/**\n * Tests for [ServerExceptionHandlingDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerExceptionHandlingDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `unauthenticated request when custom entry point then directed to custom entry point`() {\n        this.spring.register(EntryPointConfig::class.java).autowire()\n\n        val result = this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            Assertions.assertThat(result.responseHeaders.location).hasPath(\"/auth\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class EntryPointConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                exceptionHandling {\n                    authenticationEntryPoint = RedirectServerAuthenticationEntryPoint(\"/auth\")\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `unauthorized request when custom access denied handler then directed to custom access denied handler`() {\n        this.spring.register(AccessDeniedHandlerConfig::class.java).autowire()\n\n        this.client\n                .get()\n                .uri(\"/\")\n                .header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString(\"user:password\".toByteArray()))\n                .exchange()\n                .expectStatus().isSeeOther\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AccessDeniedHandlerConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, hasRole(\"ADMIN\"))\n                }\n                httpBasic { }\n                exceptionHandling {\n                    accessDeniedHandler = HttpStatusServerAccessDeniedHandler(HttpStatus.SEE_OTHER)\n                }\n            }\n        }\n\n        @Bean\n        open fun userDetailsService(): MapReactiveUserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return MapReactiveUserDetailsService(user)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerFormLoginDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpMethod\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers\nimport org.springframework.test.web.reactive.server.FluxExchangeResult\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.util.LinkedMultiValueMap\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport org.springframework.web.reactive.function.BodyInserters\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerFormLoginDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerFormLoginDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when form login enabled then redirects to default login page`() {\n        this.spring.register(DefaultFormLoginConfig::class.java, UserDetailsConfig::class.java).autowire()\n\n        val result: FluxExchangeResult<String> = this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasPath(\"/login\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class DefaultFormLoginConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin { }\n            }\n        }\n    }\n\n    @Test\n    fun `request when custom login page then redirects to custom login page`() {\n        this.spring.register(CustomLoginPageConfig::class.java, UserDetailsConfig::class.java).autowire()\n\n        val result: FluxExchangeResult<String> = this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasPath(\"/log-in\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomLoginPageConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin {\n                    loginPage = \"/log-in\"\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `form login when custom authentication manager then manager used`() {\n        this.spring.register(CustomAuthenticationManagerConfig::class.java).autowire()\n        mockkObject(CustomAuthenticationManagerConfig.AUTHENTICATION_MANAGER)\n        val data = LinkedMultiValueMap<String, String>().apply {\n            add(\"username\", \"user\")\n            add(\"password\", \"password\")\n        }\n\n        this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/login\")\n                .body(BodyInserters.fromFormData(data))\n                .exchange()\n\n        verify(exactly = 1) { CustomAuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomAuthenticationManagerConfig {\n\n        companion object {\n            val AUTHENTICATION_MANAGER: ReactiveAuthenticationManager = NoopReactiveAuthenticationManager()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin {\n                    authenticationManager = AUTHENTICATION_MANAGER\n                }\n            }\n        }\n    }\n\n    class NoopReactiveAuthenticationManager: ReactiveAuthenticationManager {\n        override fun authenticate(authentication: Authentication): Mono<Authentication> {\n            return Mono.empty()\n        }\n    }\n\n    @Test\n    fun `form login when custom authentication entry point then entry point used`() {\n        this.spring.register(CustomConfig::class.java, UserDetailsConfig::class.java).autowire()\n\n        val result = this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasPath(\"/entry\")\n        }\n    }\n\n    @Test\n    fun `form login when custom requires authentication matcher then matching request logs in`() {\n        this.spring.register(CustomConfig::class.java, UserDetailsConfig::class.java).autowire()\n        val data = LinkedMultiValueMap<String, String>().apply {\n            add(\"username\", \"user\")\n            add(\"password\", \"password\")\n        }\n\n        val result = this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/log-in\")\n                .body(BodyInserters.fromFormData(data))\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasPath(\"/\")\n        }\n    }\n\n    @Test\n    fun `invalid login when custom failure handler then failure handler used`() {\n        this.spring.register(CustomConfig::class.java, UserDetailsConfig::class.java).autowire()\n\n        val result = this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/log-in\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasPath(\"/log-in-error\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin {\n                    authenticationEntryPoint = RedirectServerAuthenticationEntryPoint(\"/entry\")\n                    requiresAuthenticationMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, \"/log-in\")\n                    authenticationFailureHandler = RedirectServerAuthenticationFailureHandler(\"/log-in-error\")\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `login when custom success handler then success handler used`() {\n        this.spring.register(CustomSuccessHandlerConfig::class.java, UserDetailsConfig::class.java).autowire()\n        val data = LinkedMultiValueMap<String, String>().apply {\n            add(\"username\", \"user\")\n            add(\"password\", \"password\")\n        }\n\n        val result = this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/login\")\n                .body(BodyInserters.fromFormData(data))\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasPath(\"/success\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomSuccessHandlerConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin {\n                    authenticationSuccessHandler = RedirectServerAuthenticationSuccessHandler(\"/success\")\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `form login when custom security context repository then repository used`() {\n        this.spring.register(CustomSecurityContextRepositoryConfig::class.java, UserDetailsConfig::class.java).autowire()\n        mockkObject(CustomSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY)\n        val data = LinkedMultiValueMap<String, String>().apply {\n            add(\"username\", \"user\")\n            add(\"password\", \"password\")\n        }\n\n        this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/login\")\n                .body(BodyInserters.fromFormData(data))\n                .exchange()\n\n        verify(exactly = 1) { CustomSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY.save(any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomSecurityContextRepositoryConfig {\n\n        companion object {\n            val SECURITY_CONTEXT_REPOSITORY: ServerSecurityContextRepository = WebSessionServerSecurityContextRepository()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin {\n                    securityContextRepository = SECURITY_CONTEXT_REPOSITORY\n                }\n            }\n        }\n    }\n\n    @Configuration\n    open class UserDetailsConfig {\n        @Bean\n        open fun userDetailsService(): MapReactiveUserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return MapReactiveUserDetailsService(user)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerFrameOptionsDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerFrameOptionsDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerFrameOptionsDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when frame options configured then header in response`() {\n        this.spring.register(FrameOptionsConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class FrameOptionsConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    frameOptions { }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when frame options disabled then no frame options header in response`() {\n        this.spring.register(FrameOptionsDisabledConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().doesNotExist(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class FrameOptionsDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    frameOptions {\n                        disable()\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when frame options mode set then frame options response header has mode value`() {\n        this.spring.register(CustomModeConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN.name)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomModeConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    frameOptions {\n                        mode = XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerHeadersDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerHeadersDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerHeadersDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when default headers configured then default headers are in the response`() {\n        this.spring.register(DefaultHeadersConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, \"nosniff\")\n                .expectHeader().valueEquals(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name)\n                .expectHeader().valueEquals(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\")\n                .expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\")\n                .expectHeader().valueEquals(HttpHeaders.EXPIRES, \"0\")\n                .expectHeader().valueEquals(HttpHeaders.PRAGMA, \"no-cache\")\n                .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class DefaultHeadersConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers { }\n            }\n        }\n    }\n\n    @Test\n    fun `request when headers disabled then no security headers are in the response`() {\n        this.spring.register(HeadersDisabledConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().doesNotExist(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS)\n                .expectHeader().doesNotExist(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS)\n                .expectHeader().doesNotExist(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY)\n                .expectHeader().doesNotExist(HttpHeaders.CACHE_CONTROL)\n                .expectHeader().doesNotExist(HttpHeaders.EXPIRES)\n                .expectHeader().doesNotExist(HttpHeaders.PRAGMA)\n                .expectHeader().doesNotExist(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HeadersDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    disable()\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when feature policy configured then feature policy header in response`() {\n        this.spring.register(FeaturePolicyConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(\"Feature-Policy\", \"geolocation 'self'\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    @Suppress(\"DEPRECATION\")\n    open class FeaturePolicyConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    featurePolicy(\"geolocation 'self'\")\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when no cross-origin policies configured then does not write cross-origin policies headers in response`() {\n        this.spring.register(CrossOriginPoliciesConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().doesNotExist(\"Cross-Origin-Opener-Policy\")\n                .expectHeader().doesNotExist(\"Cross-Origin-Embedder-Policy\")\n                .expectHeader().doesNotExist(\"Cross-Origin-Resource-Policy\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CrossOriginPoliciesConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers { }\n            }\n        }\n    }\n\n    @Test\n    fun `request when cross-origin custom policies configured then cross-origin custom policies headers in response`() {\n        this.spring.register(CrossOriginPoliciesCustomConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(\"Cross-Origin-Opener-Policy\", \"same-origin\")\n                .expectHeader().valueEquals(\"Cross-Origin-Embedder-Policy\", \"require-corp\")\n                .expectHeader().valueEquals(\"Cross-Origin-Resource-Policy\", \"same-origin\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CrossOriginPoliciesCustomConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    crossOriginOpenerPolicy {\n                        policy = CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN\n                    }\n                    crossOriginEmbedderPolicy {\n                        policy = CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP\n                    }\n                    crossOriginResourcePolicy {\n                        policy = CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when custom server http headers writer configured then custom http headers added`() {\n        this.spring.register(ServerHttpHeadersWriterCustomConfig::class.java).autowire()\n\n        this.client.get()\n            .uri(\"/\")\n            .exchange()\n            .expectHeader().valueEquals(\"CUSTOM-HEADER-1\", \"CUSTOM-VALUE-1\")\n            .expectHeader().valueEquals(\"CUSTOM-HEADER-2\", \"CUSTOM-VALUE-2\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class ServerHttpHeadersWriterCustomConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    writer { exchange ->\n                        Mono.just(exchange)\n                            .doOnNext {\n                                it.response.headers.add(\n                                    \"CUSTOM-HEADER-1\",\n                                    \"CUSTOM-VALUE-1\"\n                                )\n                            }\n                            .then()\n                    }\n                    writer { exchange ->\n                        Mono.just(exchange)\n                            .doOnNext {\n                                it.response.headers.add(\n                                    \"CUSTOM-HEADER-2\",\n                                    \"CUSTOM-VALUE-2\"\n                                )\n                            }\n                            .then()\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpBasicDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.AuthenticationException\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint\nimport org.springframework.security.web.server.WebFilterExchange\nimport org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.bind.annotation.RequestMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport reactor.core.publisher.Mono\nimport java.util.*\n\n/**\n * Tests for [ServerHttpBasicDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerHttpBasicDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `http basic when no authorization header then responds with unauthorized`() {\n        this.spring.register(HttpBasicConfig::class.java, UserDetailsConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isUnauthorized\n    }\n\n    @Test\n    fun `http basic when valid authorization header then responds with ok`() {\n        this.spring.register(HttpBasicConfig::class.java, UserDetailsConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString(\"user:password\".toByteArray()))\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HttpBasicConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                httpBasic { }\n            }\n        }\n\n        @RestController\n        internal class PathController {\n            @RequestMapping(\"/\")\n            fun path() {\n            }\n        }\n    }\n\n    @Test\n    fun `http basic when custom authentication manager then manager used`() {\n        this.spring.register(CustomAuthenticationManagerConfig::class.java).autowire()\n        mockkObject(CustomAuthenticationManagerConfig.AUTHENTICATION_MANAGER)\n        every {\n            CustomAuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any())\n        } returns Mono.just<Authentication>(TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"))\n\n        this.client.get()\n                .uri(\"/\")\n                .header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString(\"user:password\".toByteArray()))\n                .exchange()\n\n        verify(exactly = 1) { CustomAuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomAuthenticationManagerConfig {\n\n        companion object {\n            val AUTHENTICATION_MANAGER: ReactiveAuthenticationManager = NoopReactiveAuthenticationManager()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                httpBasic {\n                    authenticationManager = AUTHENTICATION_MANAGER\n                }\n            }\n        }\n    }\n\n    class NoopReactiveAuthenticationManager: ReactiveAuthenticationManager {\n        override fun authenticate(authentication: Authentication): Mono<Authentication> {\n            return Mono.empty()\n        }\n    }\n\n    @Test\n    fun `http basic when custom security context repository then repository used`() {\n        this.spring.register(CustomSecurityContextRepositoryConfig::class.java, UserDetailsConfig::class.java).autowire()\n        mockkObject(CustomSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY)\n        every {\n            CustomSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY.save(any(), any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri(\"/\")\n                .header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString(\"user:password\".toByteArray()))\n                .exchange()\n\n        verify(exactly = 1) { CustomSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY.save(any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomSecurityContextRepositoryConfig {\n\n        companion object {\n            val SECURITY_CONTEXT_REPOSITORY: ServerSecurityContextRepository = WebSessionServerSecurityContextRepository()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                httpBasic {\n                    securityContextRepository = SECURITY_CONTEXT_REPOSITORY\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `http basic when custom authentication entry point then entry point used`() {\n        this.spring.register(CustomAuthenticationEntryPointConfig::class.java, UserDetailsConfig::class.java).autowire()\n        mockkObject(CustomAuthenticationEntryPointConfig.ENTRY_POINT)\n        every {\n            CustomAuthenticationEntryPointConfig.ENTRY_POINT.commence(any(), any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n\n        verify(exactly = 1) { CustomAuthenticationEntryPointConfig.ENTRY_POINT.commence(any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomAuthenticationEntryPointConfig {\n\n        companion object {\n            val ENTRY_POINT: ServerAuthenticationEntryPoint = HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                httpBasic {\n                    authenticationEntryPoint = ENTRY_POINT\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `http basic when custom authentication failure handler then failure handler used`() {\n        this.spring.register(CustomAuthenticationFailureHandlerConfig::class.java, UserDetailsConfig::class.java).autowire()\n        mockkObject(CustomAuthenticationFailureHandlerConfig.FAILURE_HANDLER)\n        every {\n            CustomAuthenticationFailureHandlerConfig.FAILURE_HANDLER.onAuthenticationFailure(any(), any())\n        } returns Mono.empty()\n\n        this.client.get()\n            .uri(\"/\")\n            .header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString(\"user:wrong\".toByteArray()))\n            .exchange()\n\n        verify(exactly = 1) { CustomAuthenticationFailureHandlerConfig.FAILURE_HANDLER.onAuthenticationFailure(any(), any()) }\n    }\n\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomAuthenticationFailureHandlerConfig {\n\n        companion object {\n            val FAILURE_HANDLER: ServerAuthenticationFailureHandler = MockServerAuthenticationFailureHandler()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                httpBasic {\n                    authenticationFailureHandler = FAILURE_HANDLER\n                }\n            }\n        }\n    }\n\n    open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {\n        override fun onAuthenticationFailure(\n            webFilterExchange: WebFilterExchange,\n            exception: AuthenticationException\n        ): Mono<Void> {\n            return Mono.empty()\n        }\n\n    }\n\n    @Configuration\n    open class UserDetailsConfig {\n        @Bean\n        open fun userDetailsService(): MapReactiveUserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return MapReactiveUserDetailsService(user)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.context.NoOpServerSecurityContextRepository\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository\nimport org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport org.springframework.web.server.ServerWebExchange\nimport org.springframework.web.server.WebFilter\nimport org.springframework.web.server.WebFilterChain\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerHttpSecurityDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerHttpSecurityDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when it does not match the security matcher then the security rules do not apply`() {\n        this.spring.register(PatternMatcherConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isNotFound\n    }\n\n    @Test\n    fun `request when it matches the security matcher then the security rules apply`() {\n        this.spring.register(PatternMatcherConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/api\")\n                .exchange()\n                .expectStatus().isUnauthorized\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PatternMatcherConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                securityMatcher(PathPatternParserServerWebExchangeMatcher(\"/api/**\"))\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `post when default security configured then CSRF prevents the request`() {\n        this.spring.register(DefaultSecurityConfig::class.java).autowire()\n\n        this.client.post()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isForbidden\n    }\n\n    @Test\n    fun `request when default security configured then default headers are in the response`() {\n        this.spring.register(DefaultSecurityConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, \"nosniff\")\n                .expectHeader().valueEquals(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name)\n                .expectHeader().valueEquals(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\")\n                .expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\")\n                .expectHeader().valueEquals(HttpHeaders.EXPIRES, \"0\")\n                .expectHeader().valueEquals(HttpHeaders.PRAGMA, \"no-cache\")\n                .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class DefaultSecurityConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n            }\n        }\n    }\n\n    @Test\n    fun `add filter at applies custom at specified filter position`() {\n        this.spring.register(CustomWebFilterAtConfig::class.java).autowire()\n        val filterChain = this.spring.context.getBean(SecurityWebFilterChain::class.java)\n        val filters = filterChain.webFilters.collectList().block()\n\n        assertThat(filters).last().isExactlyInstanceOf(CustomWebFilter::class.java)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomWebFilterAtConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                addFilterAt(CustomWebFilter(), SecurityWebFiltersOrder.LAST)\n            }\n        }\n    }\n\n    @Test\n    fun `add filter before applies custom before specified filter position`() {\n        this.spring.register(CustomWebFilterBeforeConfig::class.java).autowire()\n        val filterChain = this.spring.context.getBean(SecurityWebFilterChain::class.java)\n        val filters: List<Class<out WebFilter>>? = filterChain.webFilters.map { it.javaClass }.collectList().block()\n\n        assertThat(filters).containsSubsequence(\n                CustomWebFilter::class.java,\n                SecurityContextServerWebExchangeWebFilter::class.java\n        )\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomWebFilterBeforeConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                addFilterBefore(CustomWebFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)\n            }\n        }\n    }\n\n    @Test\n    fun `add filter after applies custom after specified filter position`() {\n        this.spring.register(CustomWebFilterAfterConfig::class.java).autowire()\n        val filterChain = this.spring.context.getBean(SecurityWebFilterChain::class.java)\n        val filters: List<Class<out WebFilter>>? = filterChain.webFilters.map { it.javaClass }.collectList().block()\n\n        assertThat(filters).containsSubsequence(\n                SecurityContextServerWebExchangeWebFilter::class.java,\n                CustomWebFilter::class.java\n        )\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomWebFilterAfterConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                addFilterAfter(CustomWebFilter(), SecurityWebFiltersOrder.SECURITY_CONTEXT_SERVER_WEB_EXCHANGE)\n            }\n        }\n    }\n\n    class CustomWebFilter : WebFilter {\n        override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> = Mono.empty()\n    }\n\n    @Test\n    fun `authentication manager when configured in DSL then used`() {\n        this.spring.register(AuthenticationManagerConfig::class.java).autowire()\n        mockkObject(AuthenticationManagerConfig.AUTHENTICATION_MANAGER)\n        every {\n            AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any())\n        } returns Mono.just(TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"))\n        this.client.get().uri(\"/\").headers { headers ->\n            headers.setBasicAuth(\"user\", \"password\")\n        }.exchange()\n        verify(exactly = 1) { AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }\n    }\n\n    @Configuration\n    @EnableWebFlux\n    @EnableWebFluxSecurity\n    open class AuthenticationManagerConfig {\n        companion object {\n            val AUTHENTICATION_MANAGER: ReactiveAuthenticationManager = NoopReactiveAuthenticationManager()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authenticationManager = AUTHENTICATION_MANAGER\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                httpBasic { }\n            }\n        }\n    }\n\n    class NoopReactiveAuthenticationManager: ReactiveAuthenticationManager {\n        override fun authenticate(authentication: Authentication): Mono<Authentication> {\n            return Mono.empty()\n        }\n    }\n\n    @Test\n    fun `security context repository when configured in DSL then used`() {\n        this.spring.register(SecurityContextRepositoryConfig::class.java).autowire()\n        mockkObject(SecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY)\n        every {\n            SecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY.load(any())\n        } returns Mono.empty()\n        this.client.get().uri(\"/\").exchange()\n        verify(exactly = 1) { SecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY.load(any()) }\n    }\n\n    @Configuration\n    @EnableWebFlux\n    @EnableWebFluxSecurity\n    open class SecurityContextRepositoryConfig {\n        companion object {\n            val SECURITY_CONTEXT_REPOSITORY: ServerSecurityContextRepository = NoOpServerSecurityContextRepository.getInstance()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                securityContextRepository = SECURITY_CONTEXT_REPOSITORY\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpStrictTransportSecurityDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server.headers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.config.web.server.ServerHttpSecurity\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport java.time.Duration\n\n/**\n * Tests for [ServerReferrerPolicyDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerHttpStrictTransportSecurityDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when hsts configured then hsts header in response`() {\n        this.spring.register(HstsConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HstsConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    hsts { }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when hsts disabled then no hsts header in response`() {\n        this.spring.register(HstsDisabledConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().doesNotExist(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HstsDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    hsts {\n                        disable()\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when max age set then max age in response header`() {\n        this.spring.register(MaxAgeConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=1 ; includeSubDomains\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class MaxAgeConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    hsts {\n                        maxAge = Duration.ofSeconds(1)\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when includeSubdomains false then includeSubdomains not in response header`() {\n        this.spring.register(IncludeSubdomainsConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class IncludeSubdomainsConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    hsts {\n                        includeSubdomains = false\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when preload true then preload included in response header`() {\n        this.spring.register(PreloadConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"https://example.com\")\n                .exchange()\n                .expectHeader().valueEquals(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, \"max-age=31536000 ; includeSubDomains ; preload\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PreloadConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    hsts {\n                        preload = true\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpsRedirectDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.PortMapperImpl\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport java.util.*\n\n/**\n * Tests for [ServerHttpsRedirectDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerHttpsRedirectDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when matches redirect to HTTPS matcher then redirects to HTTPS`() {\n        this.spring.register(HttpRedirectMatcherConfig::class.java).autowire()\n\n        val result = this.client.get()\n                .uri(\"/secure\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasScheme(\"https\")\n        }\n    }\n\n    @Test\n    fun `request when does not match redirect to HTTPS matcher then does not redirect`() {\n        this.spring.register(HttpRedirectMatcherConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isNotFound\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HttpRedirectMatcherConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                redirectToHttps {\n                    httpsRedirectWhen(PathPatternParserServerWebExchangeMatcher(\"/secure\"))\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when matches redirect to HTTPS function then redirects to HTTPS`() {\n        this.spring.register(HttpRedirectFunctionConfig::class.java).autowire()\n\n        val result = this.client.get()\n                .uri(\"/\")\n                .header(\"X-Requires-Https\", \"required\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasScheme(\"https\")\n        }\n    }\n\n    @Test\n    fun `request when does not match redirect to HTTPS function then does not redirect`() {\n        this.spring.register(HttpRedirectFunctionConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isNotFound\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HttpRedirectFunctionConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                redirectToHttps {\n                    httpsRedirectWhen {\n                        it.request.headers.headerNames().contains(\"X-Requires-Https\")\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when multiple rules configured then only the last rule applies`() {\n        this.spring.register(HttpRedirectMatcherAndFunctionConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/secure\")\n                .exchange()\n                .expectStatus().isNotFound\n\n        val result = this.client.get()\n                .uri(\"/\")\n                .header(\"X-Requires-Https\", \"required\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasScheme(\"https\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class HttpRedirectMatcherAndFunctionConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                redirectToHttps {\n                    httpsRedirectWhen(PathPatternParserServerWebExchangeMatcher(\"/secure\"))\n                    httpsRedirectWhen {\n                        it.request.headers.headerNames().contains(\"X-Requires-Https\")\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when port mapper configured then redirected to HTTPS port`() {\n        this.spring.register(PortMapperConfig::class.java).autowire()\n\n        val result = this.client.get()\n                .uri(\"http://localhost:543\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location).hasScheme(\"https\").hasPort(123)\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PortMapperConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            val customPortMapper = PortMapperImpl()\n            customPortMapper.setPortMappings(Collections.singletonMap(\"543\", \"123\"))\n            return http {\n                redirectToHttps {\n                    portMapper = customPortMapper\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerJwtDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport java.math.BigInteger\nimport java.security.KeyFactory\nimport java.security.interfaces.RSAPublicKey\nimport java.security.spec.RSAPublicKeySpec\nimport jakarta.annotation.PreDestroy\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.convert.converter.Converter\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.authentication.AbstractAuthenticationToken\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames\nimport org.springframework.security.oauth2.jwt.Jwt\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerJwtDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerJwtDslTests {\n\n    private val expired = \"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE1MzUwMzc4OTd9.jqZDDjfc2eysX44lHXEIr9XFd2S8vjIZHCccZU-dRWMRJNsQ1QN5VNnJGklqJBXJR4qgla6cmVqPOLkUHDb0sL0nxM5XuzQaG5ZzKP81RV88shFyAiT0fD-6nl1k-Fai-Fu-VkzSpNXgeONoTxDaYhdB-yxmgrgsApgmbOTE_9AcMk-FQDXQ-pL9kynccFGV0lZx4CA7cyknKN7KBxUilfIycvXODwgKCjj_1WddLTCNGYogJJSg__7NoxzqbyWd3udbHVjqYq7GsMMrGB4_2kBD4CkghOSNcRHbT_DIXowxfAVT7PAg7Q0E5ruZsr2zPZacEUDhJ6-wbvlA0FAOUg\"\n    private val messageReadToken = \"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJtb2NrLXN1YmplY3QiLCJzY29wZSI6Im1lc3NhZ2U6cmVhZCIsImV4cCI6NDY4ODY0MTQxM30.cRl1bv_dDYcAN5U4NlIVKj8uu4mLMwjABF93P4dShiq-GQ-owzaqTSlB4YarNFgV3PKQvT9wxN1jBpGribvISljakoC0E8wDV-saDi8WxN-qvImYsn1zLzYFiZXCfRIxCmonJpydeiAPRxMTPtwnYDS9Ib0T_iA80TBGd-INhyxUUfrwRW5sqKRbjUciRJhpp7fW2ZYXmi9iPt3HDjRQA4IloJZ7f4-spt5Q9wl5HcQTv1t4XrX4eqhVbE5cCoIkFQnKPOc-jhVM44_eazLU6Xk-CCXP8C_UT5pX0luRS2cJrVFfHp2IR_AWxC-shItg6LNEmNFD4Zc-JLZcr0Q86Q\"\n    private val jwkSet = \"{\\n\" +\n            \"  \\\"keys\\\":[\\n\" +\n            \"    {\\n\" +\n            \"      \\\"kty\\\":\\\"RSA\\\",\\n\" +\n            \"      \\\"e\\\":\\\"AQAB\\\",\\n\" +\n            \"      \\\"use\\\":\\\"sig\\\",\\n\" +\n            \"      \\\"kid\\\":\\\"one\\\",\\n\" +\n            \"      \\\"n\\\":\\\"0IUjrPZDz-3z0UE4ppcKU36v7hnh8FJjhu3lbJYj0qj9eZiwEJxi9HHUfSK1DhUQG7mJBbYTK1tPYCgre5EkfKh-64VhYUa-vz17zYCmuB8fFj4XHE3MLkWIG-AUn8hNbPzYYmiBTjfGnMKxLHjsbdTiF4mtn-85w366916R6midnAuiPD4HjZaZ1PAsuY60gr8bhMEDtJ8unz81hoQrozpBZJ6r8aR1PrsWb1OqPMloK9kAIutJNvWYKacp8WYAp2WWy72PxQ7Fb0eIA1br3A5dnp-Cln6JROJcZUIRJ-QvS6QONWeS2407uQmS-i-lybsqaH0ldYC7NBEBA5inPQ\\\"\\n\" +\n            \"    }\\n\" +\n            \"  ]\\n\" +\n            \"}\\n\"\n\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when JWT configured with public key and valid token then responds with ok`() {\n        this.spring.register(PublicKeyConfig::class.java, BaseController::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .headers { headers: HttpHeaders -> headers.setBearerAuth(messageReadToken) }\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    @Test\n    fun `request when JWT configured with public key and expired token then responds with unauthorized`() {\n        this.spring.register(PublicKeyConfig::class.java, BaseController::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .headers { headers: HttpHeaders -> headers.setBearerAuth(expired) }\n                .exchange()\n                .expectStatus().isUnauthorized\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PublicKeyConfig {\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    jwt {\n                        publicKey = publicKey()\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `jwt when using custom JWT decoded then custom decoded used`() {\n        this.spring.register(CustomDecoderConfig::class.java).autowire()\n        mockkObject(CustomDecoderConfig.JWT_DECODER)\n        every {\n            CustomDecoderConfig.JWT_DECODER.decode(\"token\")\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri(\"/\")\n                .headers { headers: HttpHeaders -> headers.setBearerAuth(\"token\") }\n                .exchange()\n\n        verify(exactly = 1) { CustomDecoderConfig.JWT_DECODER.decode(\"token\") }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomDecoderConfig {\n\n        companion object {\n            val JWT_DECODER: ReactiveJwtDecoder = NullReactiveJwtDecoder()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    jwt {\n                        jwtDecoder = JWT_DECODER\n                    }\n                }\n            }\n        }\n    }\n\n    class NullReactiveJwtDecoder: ReactiveJwtDecoder {\n        override fun decode(token: String): Mono<Jwt> {\n            return Mono.empty()\n        }\n    }\n\n    @Test\n    fun `jwt when using custom JWK Set URI then custom URI used`() {\n        this.spring.register(CustomJwkSetUriConfig::class.java).autowire()\n\n        CustomJwkSetUriConfig.MOCK_WEB_SERVER.enqueue(MockResponse().setBody(jwkSet))\n\n        this.client.get()\n                .uri(\"/\")\n                .headers { headers: HttpHeaders -> headers.setBearerAuth(messageReadToken) }\n                .exchange()\n\n        val recordedRequest = CustomJwkSetUriConfig.MOCK_WEB_SERVER.takeRequest()\n        assertThat(recordedRequest.path).isEqualTo(\"/.well-known/jwks.json\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomJwkSetUriConfig {\n\n        companion object {\n            var MOCK_WEB_SERVER: MockWebServer = MockWebServer()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    jwt {\n                        jwkSetUri = mockWebServer().url(\"/.well-known/jwks.json\").toString()\n                    }\n                }\n            }\n        }\n\n        @Bean\n        open fun mockWebServer(): MockWebServer {\n            return MOCK_WEB_SERVER\n        }\n\n        @PreDestroy\n        open fun shutdown() {\n            MOCK_WEB_SERVER.shutdown()\n        }\n    }\n\n\n    @Test\n    fun `opaque token when custom JWT authentication converter then converter used`() {\n        this.spring.register(CustomJwtAuthenticationConverterConfig::class.java).autowire()\n        mockkObject(CustomJwtAuthenticationConverterConfig.CONVERTER)\n        mockkObject(CustomJwtAuthenticationConverterConfig.DECODER)\n        every {\n            CustomJwtAuthenticationConverterConfig.DECODER.decode(any())\n        } returns Mono.just(Jwt.withTokenValue(\"token\")\n            .header(\"alg\", \"none\")\n            .claim(IdTokenClaimNames.SUB, \"user\")\n            .build())\n        every {\n            CustomJwtAuthenticationConverterConfig.CONVERTER.convert(any())\n        } returns Mono.just(TestingAuthenticationToken(\"test\", \"this\", \"ROLE\"))\n\n        this.client.get()\n                .uri(\"/\")\n                .headers { headers: HttpHeaders -> headers.setBearerAuth(\"token\") }\n                .exchange()\n\n        verify(exactly = 1) { CustomJwtAuthenticationConverterConfig.CONVERTER.convert(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomJwtAuthenticationConverterConfig {\n\n        companion object {\n            val CONVERTER: Converter<Jwt, out Mono<AbstractAuthenticationToken>> = NullConverter()\n            val DECODER: ReactiveJwtDecoder = NullReactiveJwtDecoder()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    jwt {\n                        jwtAuthenticationConverter = CONVERTER\n                    }\n                }\n            }\n        }\n\n        @Bean\n        open fun jwtDecoder(): ReactiveJwtDecoder = DECODER\n    }\n\n    class NullConverter: Converter<Jwt, Mono<AbstractAuthenticationToken>> {\n        override fun convert(source: Jwt): Mono<AbstractAuthenticationToken> {\n            return Mono.empty()\n        }\n\n    }\n\n    @RestController\n    internal class BaseController {\n        @GetMapping(\"/\")\n        fun index() {\n        }\n    }\n\n    companion object {\n        private fun publicKey(): RSAPublicKey {\n            val modulus = \"26323220897278656456354815752829448539647589990395639665273015355787577386000316054335559633864476469390247312823732994485311378484154955583861993455004584140858982659817218753831620205191028763754231454775026027780771426040997832758235764611119743390612035457533732596799927628476322029280486807310749948064176545712270582940917249337311592011920620009965129181413510845780806191965771671528886508636605814099711121026468495328702234901200169245493126030184941412539949521815665744267183140084667383643755535107759061065656273783542590997725982989978433493861515415520051342321336460543070448417126615154138673620797\"\n            val exponent = \"65537\"\n            val spec = RSAPublicKeySpec(BigInteger(modulus), BigInteger(exponent))\n            val factory = KeyFactory.getInstance(\"RSA\")\n            return factory.generatePublic(spec) as RSAPublicKey\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerLogoutDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.authentication.logout.HttpStatusReturningServerLogoutSuccessHandler\nimport org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerLogoutDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerLogoutDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `logout when defaults used then redirects to login page`() {\n        this.spring.register(LogoutConfig::class.java).autowire()\n\n        val result = this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/logout\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location)\n                    .hasPath(\"/login\")\n                    .hasParameter(\"logout\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class LogoutConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                logout { }\n            }\n        }\n    }\n\n    @Test\n    fun `logout when custom logout URL then custom URL redirects to login page`() {\n        this.spring.register(CustomUrlConfig::class.java).autowire()\n\n        val result = this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/custom-logout\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location)\n                    .hasPath(\"/login\")\n                    .hasParameter(\"logout\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomUrlConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                logout {\n                    logoutUrl = \"/custom-logout\"\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `logout when custom requires logout matcher then matching request redirects to login page`() {\n        this.spring.register(RequiresLogoutConfig::class.java).autowire()\n\n        val result = this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/custom-logout\")\n                .exchange()\n                .expectStatus().is3xxRedirection\n                .returnResult(String::class.java)\n\n        result.assertWithDiagnostics {\n            assertThat(result.responseHeaders.location)\n                    .hasPath(\"/login\")\n                    .hasParameter(\"logout\")\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class RequiresLogoutConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                logout {\n                    requiresLogout = PathPatternParserServerWebExchangeMatcher(\"/custom-logout\")\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `logout when custom logout handler then custom handler invoked`() {\n        this.spring.register(CustomLogoutHandlerConfig::class.java).autowire()\n        mockkObject(CustomLogoutHandlerConfig.LOGOUT_HANDLER)\n        every { CustomLogoutHandlerConfig.LOGOUT_HANDLER.logout(any(), any()) } returns Mono.empty()\n\n        this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/logout\")\n                .exchange()\n\n        verify(exactly = 1) { CustomLogoutHandlerConfig.LOGOUT_HANDLER.logout(any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomLogoutHandlerConfig {\n\n        companion object {\n            val LOGOUT_HANDLER: ServerLogoutHandler = SecurityContextServerLogoutHandler()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                logout {\n                    logoutHandler = LOGOUT_HANDLER\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `logout when custom logout success handler then custom handler invoked`() {\n        this.spring.register(CustomLogoutSuccessHandlerConfig::class.java).autowire()\n        mockkObject(CustomLogoutSuccessHandlerConfig.LOGOUT_HANDLER)\n        every {\n            CustomLogoutSuccessHandlerConfig.LOGOUT_HANDLER.onLogoutSuccess(any(), any())\n        } returns Mono.empty()\n\n        this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/logout\")\n                .exchange()\n\n        verify(exactly = 1) { CustomLogoutSuccessHandlerConfig.LOGOUT_HANDLER.onLogoutSuccess(any(), any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomLogoutSuccessHandlerConfig {\n\n        companion object {\n            val LOGOUT_HANDLER: ServerLogoutSuccessHandler = HttpStatusReturningServerLogoutSuccessHandler(HttpStatus.OK)\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                logout {\n                    logoutSuccessHandler = LOGOUT_HANDLER\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `logout when disabled then logout URL not found`() {\n        this.spring.register(LogoutDisabledConfig::class.java).autowire()\n\n        this.client\n                .mutateWith(csrf())\n                .post()\n                .uri(\"/logout\")\n                .exchange()\n                .expectStatus().isNotFound\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class LogoutDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, permitAll)\n                }\n                logout {\n                    disable()\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerOAuth2ClientDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames\nimport org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.ServerRedirectStrategy\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerOAuth2ClientDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerOAuth2ClientDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `OAuth2 client when custom client registration repository then bean is not required`() {\n        this.spring.register(ClientRepoConfig::class.java).autowire()\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class ClientRepoConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2Client {\n                    clientRegistrationRepository = InMemoryReactiveClientRegistrationRepository(\n                            CommonOAuth2Provider.GOOGLE\n                                    .getBuilder(\"google\").clientId(\"clientId\").clientSecret(\"clientSecret\")\n                                    .build()\n                    )\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `OAuth2 client when authorization request repository configured then custom repository used`() {\n        this.spring.register(AuthorizationRequestRepositoryConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthorizationRequestRepositoryConfig.AUTHORIZATION_REQUEST_REPOSITORY)\n        every {\n            AuthorizationRequestRepositoryConfig.AUTHORIZATION_REQUEST_REPOSITORY.loadAuthorizationRequest(any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri {\n                    it.path(\"/\")\n                            .queryParam(OAuth2ParameterNames.CODE, \"code\")\n                            .queryParam(OAuth2ParameterNames.STATE, \"state\")\n                            .build()\n                }\n                .exchange()\n\n        verify(exactly = 1) {\n            AuthorizationRequestRepositoryConfig.AUTHORIZATION_REQUEST_REPOSITORY.loadAuthorizationRequest(any())\n        }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthorizationRequestRepositoryConfig {\n\n        companion object {\n            val AUTHORIZATION_REQUEST_REPOSITORY : ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> = WebSessionOAuth2ServerAuthorizationRequestRepository()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Client {\n                    authorizationRequestRepository = AUTHORIZATION_REQUEST_REPOSITORY\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `OAuth2 client when authorization redirect strategy configured then custom redirect strategy used`() {\n        this.spring.register(AuthorizationRedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY)\n        every {\n            AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())\n        } returns Mono.empty()\n\n        this.client.get()\n            .uri(\"/oauth2/authorization/google\")\n            .exchange()\n\n        verify(exactly = 1) {\n            AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())\n        }\n    }\n\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthorizationRedirectStrategyConfig {\n\n        companion object {\n            val AUTHORIZATION_REDIRECT_STRATEGY : ServerRedirectStrategy = DefaultServerRedirectStrategy()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Client {\n                    authorizationRedirectStrategy = AUTHORIZATION_REDIRECT_STRATEGY\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `OAuth2 client when authentication converter configured then custom converter used`() {\n        this.spring.register(AuthenticationConverterConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthenticationConverterConfig.AUTHORIZATION_REQUEST_REPOSITORY)\n        mockkObject(AuthenticationConverterConfig.AUTHENTICATION_CONVERTER)\n        every {\n            AuthenticationConverterConfig.AUTHORIZATION_REQUEST_REPOSITORY.loadAuthorizationRequest(any())\n        } returns Mono.just(OAuth2AuthorizationRequest.authorizationCode()\n            .authorizationUri(\"https://example.com/login/oauth/authorize\")\n            .clientId(\"clientId\")\n            .redirectUri(\"/authorize/oauth2/code/google\")\n            .build())\n        every {\n            AuthenticationConverterConfig.AUTHENTICATION_CONVERTER.convert(any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri {\n                    it.path(\"/authorize/oauth2/code/google\")\n                            .queryParam(OAuth2ParameterNames.CODE, \"code\")\n                            .queryParam(OAuth2ParameterNames.STATE, \"state\")\n                            .build()\n                }\n                .exchange()\n\n        verify(exactly = 1) { AuthenticationConverterConfig.AUTHENTICATION_CONVERTER.convert(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthenticationConverterConfig {\n\n        companion object {\n            val AUTHORIZATION_REQUEST_REPOSITORY: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> = WebSessionOAuth2ServerAuthorizationRequestRepository()\n            val AUTHENTICATION_CONVERTER: ServerAuthenticationConverter =\n\t\t\t\tServerBearerTokenAuthenticationConverter()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Client {\n                    authorizationRequestRepository = AUTHORIZATION_REQUEST_REPOSITORY\n                    authenticationConverter = AUTHENTICATION_CONVERTER\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `OAuth2 client when authentication manager configured then custom manager used`() {\n        this.spring.register(AuthenticationManagerConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthenticationManagerConfig.AUTHORIZATION_REQUEST_REPOSITORY)\n        mockkObject(AuthenticationManagerConfig.AUTHENTICATION_CONVERTER)\n        mockkObject(AuthenticationManagerConfig.AUTHENTICATION_MANAGER)\n        every {\n            AuthenticationManagerConfig.AUTHORIZATION_REQUEST_REPOSITORY.loadAuthorizationRequest(any())\n        } returns Mono.just(OAuth2AuthorizationRequest.authorizationCode()\n            .authorizationUri(\"https://example.com/login/oauth/authorize\")\n            .clientId(\"clientId\")\n            .redirectUri(\"/authorize/oauth2/code/google\")\n            .build())\n        every {\n            AuthenticationManagerConfig.AUTHENTICATION_CONVERTER.convert(any())\n        } returns Mono.just(TestingAuthenticationToken(\"a\", \"b\", \"c\"))\n        every {\n            AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri {\n                    it.path(\"/authorize/oauth2/code/google\")\n                            .queryParam(OAuth2ParameterNames.CODE, \"code\")\n                            .queryParam(OAuth2ParameterNames.STATE, \"state\")\n                            .build()\n                }\n                .exchange()\n\n        verify(exactly = 1) { AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthenticationManagerConfig {\n\n        companion object {\n            val AUTHORIZATION_REQUEST_REPOSITORY: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> = WebSessionOAuth2ServerAuthorizationRequestRepository()\n            val AUTHENTICATION_CONVERTER: ServerAuthenticationConverter =\n\t\t\t\tServerBearerTokenAuthenticationConverter()\n            val AUTHENTICATION_MANAGER: ReactiveAuthenticationManager = NoopReactiveAuthenticationManager()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Client {\n                    authorizationRequestRepository = AUTHORIZATION_REQUEST_REPOSITORY\n                    authenticationConverter = AUTHENTICATION_CONVERTER\n                    authenticationManager = AUTHENTICATION_MANAGER\n                }\n            }\n        }\n    }\n\n    class NoopReactiveAuthenticationManager: ReactiveAuthenticationManager {\n        override fun authenticate(authentication: Authentication): Mono<Authentication> {\n            return Mono.empty()\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {\n            return InMemoryReactiveClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\").clientId(\"clientId\").clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerOAuth2LoginDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository\nimport org.springframework.security.oauth2.client.web.server.ServerAuthorizationRequestRepository\nimport org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\nimport org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.ServerRedirectStrategy\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter\nimport org.springframework.security.web.server.util.matcher.IpAddressServerWebExchangeMatcher\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerOAuth2LoginDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerOAuth2LoginDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `oauth2Login when custom client registration repository then bean is not required`() {\n        this.spring.register(ClientRepoConfig::class.java).autowire()\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class ClientRepoConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2Login {\n                    clientRegistrationRepository = InMemoryReactiveClientRegistrationRepository(\n                            CommonOAuth2Provider.GOOGLE\n                                    .getBuilder(\"google\").clientId(\"clientId\").clientSecret(\"clientSecret\")\n                                    .build()\n                    )\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `login page when OAuth2 login configured then default login page created`() {\n        this.spring.register(OAuth2LoginConfig::class.java, ClientConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/login\")\n                .exchange()\n                .expectStatus().isOk\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class OAuth2LoginConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Login { }\n            }\n        }\n    }\n\n    @Test\n    fun `login page when OAuth2 login configured with login page then default login page does not exist`() {\n        this.spring.register(OAuth2LoginConfigWithLoginPage::class.java, ClientConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/login\")\n                .exchange()\n                .expectStatus().isNotFound\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class OAuth2LoginConfigWithLoginPage {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Login {\n                    loginPage = \"/login\"\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `OAuth2 login when authorization request repository configured then custom repository used`() {\n        this.spring.register(AuthorizationRequestRepositoryConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthorizationRequestRepositoryConfig.AUTHORIZATION_REQUEST_REPOSITORY)\n        every {\n            AuthorizationRequestRepositoryConfig.AUTHORIZATION_REQUEST_REPOSITORY.removeAuthorizationRequest(any())\n        } returns Mono.empty()\n        this.client.get()\n                .uri(\"/login/oauth2/code/google\")\n                .exchange()\n\n        verify(exactly = 1) { AuthorizationRequestRepositoryConfig.AUTHORIZATION_REQUEST_REPOSITORY.removeAuthorizationRequest(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthorizationRequestRepositoryConfig {\n\n        companion object {\n            val AUTHORIZATION_REQUEST_REPOSITORY: ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> = WebSessionOAuth2ServerAuthorizationRequestRepository()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Login {\n                    authorizationRequestRepository = AUTHORIZATION_REQUEST_REPOSITORY\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `OAuth2 login when authorization redirect strategy configured then custom redirect strategy used`() {\n        this.spring.register(AuthorizationRedirectStrategyConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY)\n        every {\n            AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any())\n        } returns Mono.empty()\n        this.client.get()\n            .uri(\"/oauth2/authorization/google\")\n            .exchange()\n\n        verify(exactly = 1) { AuthorizationRedirectStrategyConfig.AUTHORIZATION_REDIRECT_STRATEGY.sendRedirect(any(), any()) }\n    }\n\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthorizationRedirectStrategyConfig {\n\n        companion object {\n            val AUTHORIZATION_REDIRECT_STRATEGY : ServerRedirectStrategy = DefaultServerRedirectStrategy()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Login {\n                    authorizationRedirectStrategy = AUTHORIZATION_REDIRECT_STRATEGY\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `OAuth2 login when authentication matcher configured then custom matcher used`() {\n        this.spring.register(AuthenticationMatcherConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthenticationMatcherConfig.AUTHENTICATION_MATCHER)\n        every {\n            AuthenticationMatcherConfig.AUTHENTICATION_MATCHER.matches(any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n\n        verify(exactly = 1) { AuthenticationMatcherConfig.AUTHENTICATION_MATCHER.matches(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthenticationMatcherConfig {\n\n        companion object {\n            val AUTHENTICATION_MATCHER: ServerWebExchangeMatcher = IpAddressServerWebExchangeMatcher(\"127.0.0.1\")\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Login {\n                    authenticationMatcher = AUTHENTICATION_MATCHER\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `OAuth2 login when authentication converter configured then custom converter used`() {\n        this.spring.register(AuthenticationConverterConfig::class.java, ClientConfig::class.java).autowire()\n        mockkObject(AuthenticationConverterConfig.AUTHENTICATION_CONVERTER)\n        every {\n            AuthenticationConverterConfig.AUTHENTICATION_CONVERTER.convert(any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri(\"/login/oauth2/code/google\")\n                .exchange()\n\n        verify(exactly = 1) { AuthenticationConverterConfig.AUTHENTICATION_CONVERTER.convert(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthenticationConverterConfig {\n\n        companion object {\n            val AUTHENTICATION_CONVERTER: ServerAuthenticationConverter =\n\t\t\t\tServerBearerTokenAuthenticationConverter()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Login {\n                    authenticationConverter = AUTHENTICATION_CONVERTER\n                }\n            }\n        }\n    }\n\n    @Configuration\n    open class ClientConfig {\n        @Bean\n        open fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {\n            return InMemoryReactiveClientRegistrationRepository(\n                    CommonOAuth2Provider.GOOGLE\n                            .getBuilder(\"google\").clientId(\"clientId\").clientSecret(\"clientSecret\")\n                            .build()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerOAuth2ResourceServerDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.AuthenticationException\nimport org.springframework.security.oauth2.server.resource.authentication.JwtIssuerReactiveAuthenticationManagerResolver\nimport org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.WebFilterExchange\nimport org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler\nimport org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport org.springframework.web.server.ServerWebExchange\nimport reactor.core.publisher.Mono\nimport java.math.BigInteger\nimport java.security.KeyFactory\nimport java.security.interfaces.RSAPublicKey\nimport java.security.spec.RSAPublicKeySpec\n\n/**\n * Tests for [ServerOAuth2ResourceServerDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerOAuth2ResourceServerDslTests {\n    private val validJwt = \"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJtb2NrLXN1YmplY3QiLCJzY29wZSI6Im1lc3NhZ2U6cmVhZCIsImV4cCI6NDY4ODY0MTQxM30.cRl1bv_dDYcAN5U4NlIVKj8uu4mLMwjABF93P4dShiq-GQ-owzaqTSlB4YarNFgV3PKQvT9wxN1jBpGribvISljakoC0E8wDV-saDi8WxN-qvImYsn1zLzYFiZXCfRIxCmonJpydeiAPRxMTPtwnYDS9Ib0T_iA80TBGd-INhyxUUfrwRW5sqKRbjUciRJhpp7fW2ZYXmi9iPt3HDjRQA4IloJZ7f4-spt5Q9wl5HcQTv1t4XrX4eqhVbE5cCoIkFQnKPOc-jhVM44_eazLU6Xk-CCXP8C_UT5pX0luRS2cJrVFfHp2IR_AWxC-shItg6LNEmNFD4Zc-JLZcr0Q86Q\"\n\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when custom access denied handler configured then custom handler used`() {\n        this.spring.register(AccessDeniedHandlerConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .headers { it.setBearerAuth(validJwt) }\n                .exchange()\n                .expectStatus().isSeeOther\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AccessDeniedHandlerConfig {\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, hasAuthority(\"ADMIN\"))\n                }\n                oauth2ResourceServer {\n                    accessDeniedHandler = HttpStatusServerAccessDeniedHandler(HttpStatus.SEE_OTHER)\n                    jwt {\n                        publicKey = publicKey()\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when custom entry point configured then custom entry point used`() {\n        this.spring.register(AuthenticationEntryPointConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectStatus().isSeeOther\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthenticationEntryPointConfig {\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    authenticationEntryPoint = HttpStatusServerEntryPoint(HttpStatus.SEE_OTHER)\n                    jwt {\n                        publicKey = publicKey()\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `http basic when custom authentication failure handler then failure handler used`() {\n        this.spring.register(AuthenticationFailureHandlerConfig::class.java).autowire()\n        mockkObject(AuthenticationFailureHandlerConfig.FAILURE_HANDLER)\n        every {\n            AuthenticationFailureHandlerConfig.FAILURE_HANDLER.onAuthenticationFailure(any(), any())\n        } returns Mono.empty()\n\n        this.client.get()\n            .uri(\"/\")\n            .header(\"Authorization\", \"Bearer token\")\n            .exchange()\n            .expectStatus().isOk\n\n        verify(exactly = 1) { AuthenticationFailureHandlerConfig.FAILURE_HANDLER.onAuthenticationFailure(any(), any()) }\n    }\n\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthenticationFailureHandlerConfig {\n\n        companion object {\n            val FAILURE_HANDLER: ServerAuthenticationFailureHandler = MockServerAuthenticationFailureHandler()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    authenticationFailureHandler = FAILURE_HANDLER\n                    jwt {\n                        publicKey = publicKey()\n                    }\n                }\n            }\n        }\n    }\n\n    open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {\n        override fun onAuthenticationFailure(\n            webFilterExchange: WebFilterExchange,\n            exception: AuthenticationException\n        ): Mono<Void> {\n            return Mono.empty()\n        }\n\n    }\n\n    @Test\n    fun `request when custom bearer token converter configured then custom converter used`() {\n        this.spring.register(BearerTokenConverterConfig::class.java).autowire()\n        mockkObject(BearerTokenConverterConfig.CONVERTER)\n        every {\n            BearerTokenConverterConfig.CONVERTER.convert(any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri(\"/\")\n                .headers { it.setBearerAuth(validJwt) }\n                .exchange()\n\n        verify(exactly = 1) { BearerTokenConverterConfig.CONVERTER.convert(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class BearerTokenConverterConfig {\n\n        companion object {\n            val CONVERTER: ServerBearerTokenAuthenticationConverter =\n\t\t\t\tServerBearerTokenAuthenticationConverter()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    bearerTokenConverter = CONVERTER\n                    jwt {\n                        publicKey = publicKey()\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when custom authentication manager resolver configured then custom resolver used`() {\n        this.spring.register(AuthenticationManagerResolverConfig::class.java).autowire()\n        mockkObject(AuthenticationManagerResolverConfig.RESOLVER)\n        every {\n            AuthenticationManagerResolverConfig.RESOLVER.resolve(any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri(\"/\")\n                .headers { it.setBearerAuth(validJwt) }\n                .exchange()\n\n        verify(exactly = 1) { AuthenticationManagerResolverConfig.RESOLVER.resolve(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthenticationManagerResolverConfig {\n\n        companion object {\n            val RESOLVER: ReactiveAuthenticationManagerResolver<ServerWebExchange> = JwtIssuerReactiveAuthenticationManagerResolver.fromTrustedIssuers(\"issuer\")\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    authenticationManagerResolver = RESOLVER\n                }\n            }\n        }\n    }\n\n    companion object {\n        private fun publicKey(): RSAPublicKey {\n            val modulus = \"26323220897278656456354815752829448539647589990395639665273015355787577386000316054335559633864476469390247312823732994485311378484154955583861993455004584140858982659817218753831620205191028763754231454775026027780771426040997832758235764611119743390612035457533732596799927628476322029280486807310749948064176545712270582940917249337311592011920620009965129181413510845780806191965771671528886508636605814099711121026468495328702234901200169245493126030184941412539949521815665744267183140084667383643755535107759061065656273783542590997725982989978433493861515415520051342321336460543070448417126615154138673620797\"\n            val exponent = \"65537\"\n            val spec = RSAPublicKeySpec(BigInteger(modulus), BigInteger(exponent))\n            val factory = KeyFactory.getInstance(\"RSA\")\n            return factory.generatePublic(spec) as RSAPublicKey\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.client.oidc.server.session.InMemoryReactiveOidcSessionRegistry\nimport org.springframework.security.oauth2.client.registration.ClientRegistration\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations\nimport org.springframework.security.web.authentication.logout.LogoutHandler\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.util.ReflectionTestUtils\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport org.springframework.web.reactive.function.BodyInserters\nimport org.springframework.web.server.WebFilter\n\n/**\n * Tests for [ServerOidcLogoutDsl]\n *\n * @author Josh Cummings\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerOidcLogoutDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `oidcLogout when invalid token then errors`() {\n        this.spring.register(ClientRepositoryConfig::class.java).autowire()\n        val clientRegistration = this.spring.context.getBean(ClientRegistration::class.java)\n        this.client.post()\n                .uri(\"/logout/connect/back-channel/\" + clientRegistration.registrationId)\n                .body(BodyInserters.fromFormData(\"logout_token\", \"token\"))\n                .exchange()\n                .expectStatus().isBadRequest\n        val chain: SecurityWebFilterChain = this.spring.context.getBean(SecurityWebFilterChain::class.java)\n        chain.webFilters.doOnNext({ filter: WebFilter ->\n            if (filter.javaClass.simpleName.equals(\"OidcBackChannelLogoutWebFilter\")) {\n                val logoutHandler = ReflectionTestUtils.getField(filter, \"logoutHandler\") as LogoutHandler\n                val backChannelLogoutHandler = ReflectionTestUtils.getField(logoutHandler, \"left\") as LogoutHandler\n                var cookieName = ReflectionTestUtils.getField(backChannelLogoutHandler, \"sessionCookieName\") as String\n                assert(cookieName.equals(\"SESSION\"))\n            }\n        })\n    }\n\n    @Configuration\n    @EnableWebFlux\n    @EnableWebFluxSecurity\n    open class ClientRepositoryConfig {\n\n        private val sessionRegistry = InMemoryReactiveOidcSessionRegistry()\n\n        @Bean\n        open fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                oauth2Login { }\n                oidcLogout {\n                    backChannel { }\n                }\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n            }\n        }\n\n        @Bean\n        open fun oidcLogoutHandler(): OidcBackChannelServerLogoutHandler {\n            val logoutHandler = OidcBackChannelServerLogoutHandler(this.sessionRegistry)\n            logoutHandler.setSessionCookieName(\"SESSION\");\n            return logoutHandler;\n        }\n\n        @Bean\n        open fun clientRegistration(): ClientRegistration {\n            return TestClientRegistrations.clientRegistration().build()\n        }\n\n        @Bean\n        open fun clientRegistrationRepository(clientRegistration: ClientRegistration): ReactiveClientRegistrationRepository {\n            return InMemoryReactiveClientRegistrationRepository(clientRegistration)\n        }\n    }\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerOneTimeTokenLoginDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.mockito.ArgumentMatchers\nimport org.mockito.Mockito\nimport org.mockito.Mockito.verify\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.context.annotation.Import\nimport org.springframework.http.MediaType\nimport org.springframework.security.authentication.ott.OneTimeToken\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler\nimport org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver\nimport org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver\nimport org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.server.authentication.ott.ServerRedirectOneTimeTokenGenerationSuccessHandler\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport org.springframework.web.reactive.function.BodyInserters\nimport org.springframework.web.server.ServerWebExchange\nimport org.springframework.web.util.UriBuilder\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerOneTimeTokenLoginDsl]\n *\n * @author Max Batischev\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerOneTimeTokenLoginDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n            .bindToApplicationContext(context)\n            .configureClient()\n            .build()\n    }\n\n    @Test\n    fun `oneTimeToken when correct token then can authenticate`() {\n        spring.register(OneTimeTokenConfig::class.java).autowire()\n\n        // @formatter:off\n        client.mutateWith(SecurityMockServerConfigurers.csrf())\n            .post()\n            .uri{ uriBuilder: UriBuilder -> uriBuilder\n                .path(\"/ott/generate\")\n                .build()\n            }\n            .contentType(MediaType.APPLICATION_FORM_URLENCODED)\n            .body(BodyInserters.fromFormData(\"username\", \"user\"))\n            .exchange()\n            .expectStatus()\n            .is3xxRedirection()\n            .expectHeader().valueEquals(\"Location\", \"/login/ott\")\n\n        client.mutateWith(SecurityMockServerConfigurers.csrf())\n            .post()\n            .uri{ uriBuilder:UriBuilder -> uriBuilder\n                .path(\"/ott/generate\")\n                .build()\n            }\n            .contentType(MediaType.APPLICATION_FORM_URLENCODED)\n            .body(BodyInserters.fromFormData(\"username\", \"user\"))\n            .exchange()\n            .expectStatus()\n            .is3xxRedirection()\n            .expectHeader().valueEquals(\"Location\", \"/login/ott\")\n\n        val token = lastToken()!!.tokenValue\n\n        client.mutateWith(SecurityMockServerConfigurers.csrf())\n            .post()\n            .uri{ uriBuilder:UriBuilder -> uriBuilder\n                .path(\"/login/ott\")\n                .queryParam(\"token\", token)\n                .build()\n            }\n            .exchange()\n            .expectStatus()\n            .is3xxRedirection()\n            .expectHeader().valueEquals(\"Location\", \"/\")\n        // @formatter:on\n    }\n\n    @Test\n    fun `oneTimeToken when different authentication urls then can authenticate`() {\n        spring.register(OneTimeTokenDifferentUrlsConfig::class.java).autowire()\n\n        // @formatter:off\n        client.mutateWith(SecurityMockServerConfigurers.csrf())\n            .post()\n            .uri{ uriBuilder: UriBuilder -> uriBuilder\n                .path(\"/generateurl\")\n                .build()\n            }\n            .contentType(MediaType.APPLICATION_FORM_URLENCODED)\n            .body(BodyInserters.fromFormData(\"username\", \"user\"))\n            .exchange()\n            .expectStatus()\n            .is3xxRedirection()\n            .expectHeader().valueEquals(\"Location\", \"/redirected\")\n\n        val token = lastToken()!!.tokenValue\n\n        client.mutateWith(SecurityMockServerConfigurers.csrf())\n            .post()\n            .uri{ uriBuilder: UriBuilder -> uriBuilder\n                .path(\"/loginprocessingurl\")\n                .build()\n            }\n            .contentType(MediaType.APPLICATION_FORM_URLENCODED)\n            .body(BodyInserters.fromFormData(\"token\", token!!))\n            .exchange()\n            .expectStatus()\n            .is3xxRedirection()\n            .expectHeader().valueEquals(\"Location\", \"/authenticated\")\n        // @formatter:on\n    }\n\n    @Test\n    fun `oneTimeToken when custom request resolver set then custom resolver use`() {\n        spring.register(OneTimeTokenConfigWithCustomRequestResolver::class.java).autowire()\n\n        // @formatter:off\n        client.mutateWith(SecurityMockServerConfigurers.csrf())\n                .post()\n                .uri{ uriBuilder: UriBuilder -> uriBuilder\n                        .path(\"/ott/generate\")\n                        .build()\n                }\n                .contentType(MediaType.APPLICATION_FORM_URLENCODED)\n                .body(BodyInserters.fromFormData(\"username\", \"user\"))\n                .exchange()\n                .expectStatus()\n                .is3xxRedirection()\n                .expectHeader().valueEquals(\"Location\", \"/login/ott\")\n\n        val resolver = spring.context\n                .getBean(ServerGenerateOneTimeTokenRequestResolver::class.java)\n\n        verify(resolver, Mockito.times(1))\n                .resolve(ArgumentMatchers.any(ServerWebExchange::class.java))\n        // @formatter:on\n    }\n\n    private fun lastToken():OneTimeToken? =\n        spring.context.getBean(TestServerOneTimeTokenGenerationSuccessHandler::class.java)\n                .lastToken\n\n\n    @Configuration\n    @EnableWebFlux\n    @EnableWebFluxSecurity\n    @Import(UserDetailsServiceConfig::class)\n    open class OneTimeTokenConfig {\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity,\n                                      ottSuccessHandler: ServerOneTimeTokenGenerationSuccessHandler): SecurityWebFilterChain {\n            // @formatter:off\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oneTimeTokenLogin {\n                    tokenGenerationSuccessHandler = ottSuccessHandler\n                }\n            }\n            // @formatter:on\n        }\n\n        @Bean\n        open fun ottSuccessHandler(): ServerOneTimeTokenGenerationSuccessHandler =\n                TestServerOneTimeTokenGenerationSuccessHandler()\n    }\n\n    @Configuration\n    @EnableWebFlux\n    @EnableWebFluxSecurity\n    @Import(UserDetailsServiceConfig::class)\n    open class OneTimeTokenDifferentUrlsConfig {\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity,\n                                      ottSuccessHandler: ServerOneTimeTokenGenerationSuccessHandler): SecurityWebFilterChain {\n            // @formatter:off\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oneTimeTokenLogin {\n                    tokenGeneratingUrl = \"/generateurl\"\n                    tokenGenerationSuccessHandler = ottSuccessHandler\n                    loginProcessingUrl = \"/loginprocessingurl\"\n                    authenticationSuccessHandler = RedirectServerAuthenticationSuccessHandler(\"/authenticated\")\n                }\n            }\n            // @formatter:on\n        }\n\n        @Bean\n        open fun ottSuccessHandler(): ServerOneTimeTokenGenerationSuccessHandler =\n                TestServerOneTimeTokenGenerationSuccessHandler(\"/redirected\")\n    }\n\n    @Configuration(proxyBeanMethods = false)\n    open class UserDetailsServiceConfig {\n\n        @Bean\n        open fun userDetailsService(): ReactiveUserDetailsService =\n            MapReactiveUserDetailsService(User(\"user\", \"password\", listOf()))\n    }\n\n    @Configuration(proxyBeanMethods = false)\n    @EnableWebFlux\n    @EnableWebFluxSecurity\n    @Import(OneTimeTokenLoginSpecTests.UserDetailsServiceConfig::class)\n    open class OneTimeTokenConfigWithCustomRequestResolver {\n        @Bean\n        open fun securityWebFilterChain(http: ServerHttpSecurity,\n                                        ottSuccessHandler: ServerOneTimeTokenGenerationSuccessHandler): SecurityWebFilterChain {\n            // @formatter:off\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oneTimeTokenLogin {\n                    tokenGenerationSuccessHandler = ottSuccessHandler\n                }\n            }\n        }\n\n        @Bean\n        open fun resolver(): ServerGenerateOneTimeTokenRequestResolver =\n                Mockito.spy(DefaultServerGenerateOneTimeTokenRequestResolver())\n\n        @Bean\n        open fun ottSuccessHandler(): ServerOneTimeTokenGenerationSuccessHandler =\n                TestServerOneTimeTokenGenerationSuccessHandler()\n    }\n\n    private class TestServerOneTimeTokenGenerationSuccessHandler: ServerOneTimeTokenGenerationSuccessHandler {\n        private var delegate: ServerRedirectOneTimeTokenGenerationSuccessHandler? = null\n        var lastToken: OneTimeToken? = null\n\n        constructor() {\n            this.delegate = ServerRedirectOneTimeTokenGenerationSuccessHandler(\"/login/ott\")\n        }\n\n        constructor(redirectUrl: String) {\n            this.delegate = ServerRedirectOneTimeTokenGenerationSuccessHandler(redirectUrl)\n        }\n\n        override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {\n            lastToken = oneTimeToken\n            return delegate!!.handle(exchange, oneTimeToken)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerOpaqueTokenDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.http.HttpHeaders\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector\nimport org.springframework.security.oauth2.server.resource.introspection.SpringReactiveOpaqueTokenIntrospector\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport jakarta.annotation.PreDestroy\nimport org.springframework.context.annotation.Configuration\n\n/**\n * Tests for [ServerOpaqueTokenDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerOpaqueTokenDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `opaque token when using defaults then uses introspector bean`() {\n        this.spring.register(IntrospectorBeanConfig::class.java).autowire()\n\n        IntrospectorBeanConfig.MOCK_WEB_SERVER.enqueue(MockResponse())\n\n        this.client.get()\n                .uri(\"/\")\n                .header(HttpHeaders.AUTHORIZATION, \"Bearer token\")\n                .exchange()\n\n        val recordedRequest = IntrospectorBeanConfig.MOCK_WEB_SERVER.takeRequest()\n        assertThat(recordedRequest.path).isEqualTo(\"/introspect\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class IntrospectorBeanConfig {\n        companion object {\n            var MOCK_WEB_SERVER: MockWebServer = MockWebServer()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    opaqueToken { }\n                }\n            }\n        }\n\n        @Bean\n        open fun mockWebServer(): MockWebServer {\n            return MOCK_WEB_SERVER\n        }\n\n        @PreDestroy\n        open fun shutdown() {\n            MOCK_WEB_SERVER.shutdown()\n        }\n\n        @Bean\n        open fun tokenIntrospectionClient(): ReactiveOpaqueTokenIntrospector {\n            return SpringReactiveOpaqueTokenIntrospector(mockWebServer().url(\"/introspect\").toString(), \"client\", \"secret\")\n        }\n    }\n\n    @Test\n    fun `opaque token when using custom introspector then introspector used`() {\n        this.spring.register(CustomIntrospectorConfig::class.java).autowire()\n\n        CustomIntrospectorConfig.MOCK_WEB_SERVER.enqueue(MockResponse())\n\n        this.client.get()\n                .uri(\"/\")\n                .header(HttpHeaders.AUTHORIZATION, \"Bearer token\")\n                .exchange()\n\n        val recordedRequest = CustomIntrospectorConfig.MOCK_WEB_SERVER.takeRequest()\n        assertThat(recordedRequest.path).isEqualTo(\"/introspector\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomIntrospectorConfig {\n        companion object {\n            var MOCK_WEB_SERVER: MockWebServer = MockWebServer()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    opaqueToken {\n                        introspector = SpringReactiveOpaqueTokenIntrospector(mockWebServer().url(\"/introspector\").toString(), \"client\", \"secret\")\n                    }\n                }\n            }\n        }\n\n        @Bean\n        open fun mockWebServer(): MockWebServer {\n            return MOCK_WEB_SERVER\n        }\n\n        @PreDestroy\n        open fun shutdown() {\n            MOCK_WEB_SERVER.shutdown()\n        }\n    }\n\n    @Test\n    fun `opaque token when using custom introspection URI and credentials then custom used`() {\n        this.spring.register(CustomIntrospectionUriAndCredentialsConfig::class.java).autowire()\n\n        CustomIntrospectionUriAndCredentialsConfig.MOCK_WEB_SERVER.enqueue(MockResponse())\n\n        this.client.get()\n                .uri(\"/\")\n                .header(HttpHeaders.AUTHORIZATION, \"Bearer token\")\n                .exchange()\n\n        val recordedRequest = CustomIntrospectionUriAndCredentialsConfig.MOCK_WEB_SERVER.takeRequest()\n        assertThat(recordedRequest.path).isEqualTo(\"/introspection-uri\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomIntrospectionUriAndCredentialsConfig {\n        companion object {\n            var MOCK_WEB_SERVER: MockWebServer = MockWebServer()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                oauth2ResourceServer {\n                    opaqueToken {\n                        introspectionUri = mockWebServer().url(\"/introspection-uri\").toString()\n                        introspectionClientCredentials(\"client\", \"secret\")\n                    }\n                }\n            }\n        }\n\n        @Bean\n        open fun mockWebServer(): MockWebServer {\n            return MOCK_WEB_SERVER\n        }\n\n        @PreDestroy\n        open fun shutdown() {\n            MOCK_WEB_SERVER.shutdown()\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerPasswordManagementDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.apache.http.HttpHeaders\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerPasswordManagementDsl].\n *\n * @author Evgeniy Cheban\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerPasswordManagementDslTests {\n\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `when change password page not set then default change password page used`() {\n        this.spring.register(PasswordManagementWithDefaultChangePasswordPageConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/.well-known/change-password\")\n                .exchange()\n                .expectStatus().isFound\n                .expectHeader().valueEquals(HttpHeaders.LOCATION, \"/change-password\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PasswordManagementWithDefaultChangePasswordPageConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                passwordManagement {}\n            }\n        }\n    }\n\n    @Test\n    fun `when change password page set then specified change password page used`() {\n        this.spring.register(PasswordManagementWithCustomChangePasswordPageConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/.well-known/change-password\")\n                .exchange()\n                .expectStatus().isFound\n                .expectHeader().valueEquals(HttpHeaders.LOCATION, \"/custom-change-password-page\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PasswordManagementWithCustomChangePasswordPageConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                passwordManagement {\n                    changePasswordPage = \"/custom-change-password-page\"\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerPermissionsPolicyDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerPermissionsPolicyDsl]\n *\n * @author Christophe Gilles\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerPermissionsPolicyDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when permissions policy configured then permissions policy header in response`() {\n        this.spring.register(PermissionsPolicyConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().doesNotExist(\"Permissions-Policy\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PermissionsPolicyConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    permissionsPolicy { }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when custom policy configured then custom policy in response header`() {\n        this.spring.register(CustomPolicyConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(\"Permissions-Policy\", \"geolocation=(self)\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomPolicyConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    permissionsPolicy {\n                        policy = \"geolocation=(self)\"\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerReferrerPolicyDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerReferrerPolicyDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerReferrerPolicyDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when referrer policy configured then referrer policy header in response`() {\n        this.spring.register(ReferrerPolicyConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(\"Referrer-Policy\", ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFERRER.policy)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class ReferrerPolicyConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    referrerPolicy { }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when custom policy configured then custom policy in response header`() {\n        this.spring.register(CustomPolicyConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(\"Referrer-Policy\", ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.SAME_ORIGIN.policy)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class CustomPolicyConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    referrerPolicy {\n                        policy = ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.SAME_ORIGIN\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerRequestCacheDslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache\nimport org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport reactor.core.publisher.Mono\n\n/**\n * Tests for [ServerRequestCacheDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerRequestCacheDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `GET when request cache enabled then redirected to cached page`() {\n        this.spring.register(RequestCacheConfig::class.java, UserDetailsConfig::class.java).autowire()\n        mockkObject(RequestCacheConfig.REQUEST_CACHE)\n        every {\n            RequestCacheConfig.REQUEST_CACHE.removeMatchingRequest(any())\n        } returns Mono.empty()\n\n        this.client.get()\n                .uri(\"/test\")\n                .exchange()\n\n        verify(exactly = 1) { RequestCacheConfig.REQUEST_CACHE.saveRequest(any()) }\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class RequestCacheConfig {\n\n        companion object {\n            val REQUEST_CACHE: ServerRequestCache = WebSessionServerRequestCache()\n        }\n\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin { }\n                requestCache {\n                    requestCache = REQUEST_CACHE\n                }\n            }\n        }\n    }\n\n    @Configuration\n    open class UserDetailsConfig {\n        @Bean\n        open fun userDetailsService(): MapReactiveUserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                    .username(\"user\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return MapReactiveUserDetailsService(user)\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerSessionManagementDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.context.annotation.Import\nimport org.springframework.http.MediaType\nimport org.springframework.http.ResponseCookie\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration\nimport org.springframework.security.core.session.InMemoryReactiveSessionRegistry\nimport org.springframework.security.core.session.ReactiveSessionRegistry\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.authentication.InvalidateLeastUsedServerMaximumSessionsExceededHandler\nimport org.springframework.security.web.server.authentication.PreventLoginServerMaximumSessionsExceededHandler\nimport org.springframework.security.web.server.authentication.SessionLimit\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.util.LinkedMultiValueMap\nimport org.springframework.util.MultiValueMap\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport org.springframework.web.reactive.function.BodyInserters\nimport org.springframework.web.server.adapter.WebHttpHandlerBuilder\nimport org.springframework.web.server.session.DefaultWebSessionManager\n\n/**\n * Tests for [ServerSessionManagementDsl]\n *\n * @author Marcus da Coregio\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerSessionManagementDslTests {\n\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n            .bindToApplicationContext(context)\n            .configureClient()\n            .build()\n    }\n\n    @Test\n    fun `login when max sessions prevent login then second login fails`() {\n        this.spring.register(ConcurrentSessionsMaxSessionPreventsLoginTrueConfig::class.java).autowire()\n\n        val data: MultiValueMap<String, String> = LinkedMultiValueMap()\n        data.add(\"username\", \"user\")\n        data.add(\"password\", \"password\")\n\n        val firstLoginSessionCookie = loginReturningCookie(data)\n\n        // second login should fail\n        this.client.mutateWith(SecurityMockServerConfigurers.csrf())\n            .post()\n            .uri(\"/login\")\n            .contentType(MediaType.MULTIPART_FORM_DATA)\n            .body(BodyInserters.fromFormData(data))\n            .exchange()\n            .expectHeader()\n            .location(\"/login?error\")\n\n        // first login should still be valid\n        this.client.mutateWith(SecurityMockServerConfigurers.csrf())\n            .get()\n            .uri(\"/\")\n            .cookie(firstLoginSessionCookie!!.name, firstLoginSessionCookie.value)\n            .exchange()\n            .expectStatus()\n            .isOk()\n    }\n\n    @Test\n    fun `login when max sessions does not prevent login then seconds login succeeds and first session is invalidated`() {\n        ConcurrentSessionsMaxSessionPreventsLoginFalseConfig.maxSessions = 1\n        this.spring.register(SessionManagementSpecTests.ConcurrentSessionsMaxSessionPreventsLoginFalseConfig::class.java)\n            .autowire()\n\n        val data: MultiValueMap<String, String> = LinkedMultiValueMap()\n        data.add(\"username\", \"user\")\n        data.add(\"password\", \"password\")\n\n        val firstLoginSessionCookie = loginReturningCookie(data)\n        val secondLoginSessionCookie = loginReturningCookie(data)\n\n        // first login should not be valid\n        this.client.get()\n            .uri(\"/\")\n            .cookie(firstLoginSessionCookie!!.name, firstLoginSessionCookie.value)\n            .exchange()\n            .expectStatus()\n            .isFound()\n            .expectHeader()\n            .location(\"/login\")\n\n        // second login should be valid\n        this.client.get()\n            .uri(\"/\")\n            .cookie(secondLoginSessionCookie!!.name, secondLoginSessionCookie.value)\n            .exchange()\n            .expectStatus()\n            .isOk()\n    }\n\n    @Test\n    fun `login when max sessions does not prevent login then least recently used session is invalidated`() {\n        ConcurrentSessionsMaxSessionPreventsLoginFalseConfig.maxSessions = 2\n        this.spring.register(ConcurrentSessionsMaxSessionPreventsLoginFalseConfig::class.java).autowire()\n        val data: MultiValueMap<String, String> = LinkedMultiValueMap()\n        data.add(\"username\", \"user\")\n        data.add(\"password\", \"password\")\n        val firstLoginSessionCookie = loginReturningCookie(data)\n        val secondLoginSessionCookie = loginReturningCookie(data)\n\n        // update last access time for first request\n        this.client.get()\n            .uri(\"/\")\n            .cookie(firstLoginSessionCookie!!.name, firstLoginSessionCookie.value)\n            .exchange()\n            .expectStatus()\n            .isOk()\n        val thirdLoginSessionCookie = loginReturningCookie(data)\n\n        // second login should be invalid, it is the least recently used session\n        this.client.get()\n            .uri(\"/\")\n            .cookie(secondLoginSessionCookie!!.name, secondLoginSessionCookie.value)\n            .exchange()\n            .expectStatus()\n            .isFound()\n            .expectHeader()\n            .location(\"/login\")\n\n        // first login should be valid\n        this.client.get()\n            .uri(\"/\")\n            .cookie(firstLoginSessionCookie.name, firstLoginSessionCookie.value)\n            .exchange()\n            .expectStatus()\n            .isOk()\n\n        // third login should be valid\n        this.client.get()\n            .uri(\"/\")\n            .cookie(thirdLoginSessionCookie!!.name, thirdLoginSessionCookie.value)\n            .exchange()\n            .expectStatus()\n            .isOk()\n    }\n\n    private fun loginReturningCookie(data: MultiValueMap<String, String>): ResponseCookie? {\n        return login(data).expectCookie()\n            .exists(\"SESSION\")\n            .returnResult(Void::class.java)\n            .responseCookies\n            .getFirst(\"SESSION\")\n    }\n\n    private fun login(data: MultiValueMap<String, String>): WebTestClient.ResponseSpec {\n        return client.mutateWith(SecurityMockServerConfigurers.csrf())\n            .post()\n            .uri(\"/login\")\n            .contentType(MediaType.MULTIPART_FORM_DATA)\n            .body(BodyInserters.fromFormData(data))\n            .exchange()\n            .expectStatus()\n            .is3xxRedirection()\n            .expectHeader()\n            .location(\"/\")\n    }\n\n    @Configuration\n    @EnableWebFlux\n    @EnableWebFluxSecurity\n    @Import(Config::class)\n    open class ConcurrentSessionsMaxSessionPreventsLoginFalseConfig {\n\n        companion object {\n            var maxSessions = 1\n        }\n\n        @Bean\n        open fun springSecurity(http: ServerHttpSecurity, webSessionManager: DefaultWebSessionManager): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin { }\n                sessionManagement {\n                    sessionConcurrency {\n                        maximumSessions = SessionLimit.of(maxSessions)\n                        maximumSessionsExceededHandler = InvalidateLeastUsedServerMaximumSessionsExceededHandler(webSessionManager.sessionStore)\n                    }\n                }\n            }\n        }\n\n    }\n\n    @Configuration\n    @EnableWebFlux\n    @EnableWebFluxSecurity\n    @Import(Config::class)\n    open class ConcurrentSessionsMaxSessionPreventsLoginTrueConfig {\n\n        @Bean\n        open fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                }\n                formLogin { }\n                sessionManagement {\n                    sessionConcurrency {\n                        maximumSessions = SessionLimit.of(1)\n                        maximumSessionsExceededHandler =\n                            PreventLoginServerMaximumSessionsExceededHandler()\n                    }\n                }\n            }\n        }\n\n    }\n\n    @Configuration\n    @Import(\n        ReactiveAuthenticationTestConfiguration::class,\n        DefaultController::class\n    )\n    open class Config {\n\n        @Bean(WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME)\n        open fun webSessionManager(): DefaultWebSessionManager {\n            return DefaultWebSessionManager()\n        }\n\n        @Bean\n        open fun reactiveSessionRegistry(): ReactiveSessionRegistry {\n            return InMemoryReactiveSessionRegistry()\n        }\n\n    }\n\n    @RestController\n    open class DefaultController {\n\n        @GetMapping(\"/\")\n        fun index(): String {\n            return \"ok\"\n        }\n\n    }\n\n\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerX509DslTests.kt",
    "content": "@file:Suppress(\"DEPRECATION\", \"PLATFORM_CLASS_MAPPED_TO_KOTLIN\", \"UNCHECKED_CAST\")\n\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.annotation.AuthenticationPrincipal\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.authentication.ReactivePreAuthenticatedAuthenticationManager\nimport org.springframework.test.web.reactive.server.UserWebTestClientConfigurer.x509\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.test.web.reactive.server.expectBody\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.reactive.config.EnableWebFlux\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\n\n/**\n * Tests for [ServerX509Dsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerX509DslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `x509 when configured with defaults then user authenticated with expected username`() {\n        this.spring\n                .register(X509DefaultConfig::class.java, UserDetailsConfig::class.java, UsernameController::class.java)\n                .autowire()\n        val certificate = loadCert<X509Certificate>(\"rod.cer\")\n\n        this.client\n                .mutateWith(x509(certificate))\n                .get()\n                .uri(\"/username\")\n                .exchange()\n                .expectStatus().isOk\n                .expectBody<String>().isEqualTo(\"rod\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class X509DefaultConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                x509 { }\n            }\n        }\n    }\n\n    @Test\n    fun `x509 when principal extractor customized then custom principal extractor used`() {\n        this.spring\n                .register(PrincipalExtractorConfig::class.java, UserDetailsConfig::class.java, UsernameController::class.java)\n                .autowire()\n        val certificate = loadCert<X509Certificate>(\"rodatexampledotcom.cer\")\n\n        this.client\n                .mutateWith(x509(certificate))\n                .get()\n                .uri(\"/username\")\n                .exchange()\n                .expectStatus().isOk\n                .expectBody<String>().isEqualTo(\"rod\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class PrincipalExtractorConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            val customPrincipalExtractor = SubjectDnX509PrincipalExtractor()\n            customPrincipalExtractor.setSubjectDnRegex(\"CN=(.*?)@example.com(?:,|$)\")\n            return http {\n                x509 {\n                    principalExtractor = customPrincipalExtractor\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `x509 when authentication manager customized then custom authentication manager used`() {\n        this.spring\n                .register(AuthenticationManagerConfig::class.java, UsernameController::class.java)\n                .autowire()\n        val certificate = loadCert<X509Certificate>(\"rod.cer\")\n\n        this.client\n                .mutateWith(x509(certificate))\n                .get()\n                .uri(\"/username\")\n                .exchange()\n                .expectStatus().isOk\n                .expectBody<String>().isEqualTo(\"rod\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class AuthenticationManagerConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                x509 {\n                    authenticationManager = ReactivePreAuthenticatedAuthenticationManager(userDetailsService())\n                }\n            }\n        }\n\n        fun userDetailsService(): MapReactiveUserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                    .username(\"rod\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return MapReactiveUserDetailsService(user)\n        }\n    }\n\n    @RestController\n    class UsernameController {\n        @GetMapping(\"/username\")\n        fun principal(@AuthenticationPrincipal user: User?): String {\n            return user!!.username\n        }\n    }\n\n    @Configuration\n    open class UserDetailsConfig {\n        @Bean\n        open fun userDetailsService(): MapReactiveUserDetailsService {\n            val user = User.withDefaultPasswordEncoder()\n                    .username(\"rod\")\n                    .password(\"password\")\n                    .roles(\"USER\")\n                    .build()\n            return MapReactiveUserDetailsService(user)\n        }\n    }\n\n    private fun <T : Certificate> loadCert(location: String): T {\n        ClassPathResource(location).inputStream.use { inputStream ->\n            val certFactory = CertificateFactory.getInstance(\"X.509\")\n            return certFactory.generateCertificate(inputStream) as T\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/kotlin/org/springframework/security/config/web/server/ServerXssProtectionDslTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.config.web.server\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Tests for [ServerXssProtectionDsl]\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerXssProtectionDslTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    private lateinit var client: WebTestClient\n\n    @Autowired\n    fun setup(context: ApplicationContext) {\n        this.client = WebTestClient\n                .bindToApplicationContext(context)\n                .configureClient()\n                .build()\n    }\n\n    @Test\n    fun `request when xss protection configured then xss header in response`() {\n        this.spring.register(XssConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"0\")\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class XssConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    xssProtection { }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when xss protection disabled then no xss header in response`() {\n        this.spring.register(XssDisabledConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().doesNotExist(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)\n    }\n\n    @Configuration\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class XssDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    xssProtection {\n                        disable()\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    fun `request when xss protection value disabled then xss header in response`() {\n        this.spring.register(XssValueDisabledConfig::class.java).autowire()\n\n        this.client.get()\n                .uri(\"/\")\n                .exchange()\n                .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, \"1\")\n    }\n\n    @EnableWebFluxSecurity\n    @EnableWebFlux\n    open class XssValueDisabledConfig {\n        @Bean\n        open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n            return http {\n                headers {\n                    xssProtection {\n                        headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "config/src/test/resources/CustomJdbcUserServiceSampleConfig.sql",
    "content": "create table users(principal varchar_ignorecase(50) not null primary key,credentials varchar_ignorecase(50) not null);\ncreate table roles (principal varchar_ignorecase(50) not null,role varchar_ignorecase(50) not null,constraint fk_roles_users foreign key(principal) references users(principal));\ncreate unique index ix_auth_principal on roles (principal,role);\ncreate table groups (id bigint generated by default as identity(start with 0) primary key,group_name varchar_ignorecase(50) not null);\ncreate table group_authorities (group_id bigint not null,authority varchar(50) not null,constraint fk_group_authorities_group foreign key(group_id) references groups(id));\ncreate table group_members (id bigint generated by default as identity(start with 0) primary key,username varchar(50) not null,group_id bigint not null,constraint fk_group_members_group foreign key(group_id) references groups(id));\n\ninsert into users values('user','{noop}password');\ninsert into roles values('user','USER');\n\ninsert into groups values(1,'OPERATIONS');\ninsert into group_authorities values(1,'DBA');\ninsert into group_members values(1,'user',1);"
  },
  {
    "path": "config/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/configuration/AutowireBeanFactoryObjectPostProcessorTests-aopconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\n\txmlns:p=\"http://www.springframework.org/schema/p\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.2.xsd\n\t\thttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.2.xsd\">\n\n\t<context:annotation-config/>\n\n\t<bean class=\"org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessorTests$WithBeanNameAutoProxyCreatorConfig\"/>\n\n\t<bean id=\"interceptor\" class=\"org.springframework.security.config.annotation.configuration.AroundMethodInterceptor\"/>\n\t<bean id=\"myBean\" class=\"org.springframework.security.config.annotation.configuration.MyAdvisedBean\"/>\n\t<bean id=\"autoProxy\" class=\"org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator\"\n\t\tp:beanNames=\"myBean\"\n\t\tp:interceptorNames=\"interceptor\"/>\n\t   <bean id=\"pointcutAdvisor\" class=\"org.springframework.aop.support.NameMatchMethodPointcutAdvisor\"\n\t\t   p:mappedName=\"doStuff\"\n\t\t   p:advice-ref=\"interceptor\"/>\n</beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configuration/simple.priv",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMk7CKSTfu3QoV\nHoPVXxwZO+qweztd36cVWYqGOZinrOR2crWFu50AgR2CsdIH0+cqo7F4Vx7/3O8i\nRpYYZPe2VoO5sumzJt8P6fS80/TAKjhJDAqgZKRJTgGN8KxCM6p/aJli1ZeDBqiV\nv7vJJe+ZgJuPGRS+HMNa/wPxEkqqXsglcJcQV1ZEtfKXSHB7jizKpRL38185SyAC\npwyjvBu6Cmm1URfhQo88mf239ONh4dZ2HoDfzN1q6Ssu4F4hgutxr9B0DVLDP5u+\nWFrm3nsJ76zf99uJ+ntMUHJ+bY+gOjSlVWIVBIZeAaEGKCNWRk/knjvjbijpvm3U\nacGlgdL3AgMBAAECggEACxxxS7zVyu91qI2s5eSKmAQAXMqgup6+2hUluc47nqUv\nuZz/c/6MPkn2Ryo+65d4IgqmMFjSfm68B/2ER5FTcvoLl1Xo2twrrVpUmcg3BClS\nIZPuExdhVNnxjYKEWwcyZrehyAoR261fDdcFxLRW588efIUC+rPTTRHzAc7sT+Ln\nt/uFeYNWJm3LaegOLoOmlMAhJ5puAWSN1F0FxtRf/RVgzbLA9QC975SKHJsfWCSr\nIZyPsdeaqomKaF65l8nfqlE0Ua2L35gIOGKjUwb7uUE8nI362RWMtYdoi3zDDyoY\nhSFbgjylCHDM0u6iSh6KfqOHtkYyJ8tUYgVWl787wQKBgQDYO3wL7xuDdD101Lyl\nAnaDdFB9fxp83FG1cWr+t7LYm9YxGfEUsKHAJXN6TIayDkOOoVwIl+Gz0T3Z06Bm\neBGLrB9mrVA7+C7NJwu5gTMlzP6HxUR9zKJIQ/VB1NUGM77LSmvOFbHc9Q0+z8EH\nX5WO516a3Z7lNtZJcCoPOtu2rwKBgQCmbj41Fh+SSEUApCEKms5ETRpe7LXQlJgx\nyW7zcJNNuIb1C3vBLPxjiOTMgYKOeMg5rtHTGLT43URHLh9ArjawasjSAr4AM3J4\nxpoi/sKGDdiKOsuDWIGfzdYL8qyTHSdpZLQsCTMRiRYgAHZFPgNa7SLZRfZicGlr\nGHN1rJW6OQKBgEjiM/upyrJSWeypUDSmUeAZMpA6aWkwsfHgmtnkfUn5rQa74cDB\nkKO9e+D7LmOR3z+SL/1NhGwh2SE07dncGr3jdGodfO/ZxZyszozmeaECKcEFwwJM\nGV8WWPKplGwUwPiwywmZ0mvRxXcoe73KgBS88+xrSwWjqDL0tZiQlEJNAoGATkei\nGMQMG3jEg9Wu+NbxV6zQT3+U0MNjhl9RQU1c63x0dcNt9OFc4NAdlZcAulRTENaK\nOHjxffBM0hH+fySx8m53gFfr2BpaqDX5f6ZGBlly1SlsWZ4CchCVsc71nshipi7I\nk8HL9F5/OpQdDNprJ5RMBNfkWE65Nrcsb1e6oPkCgYAxwgdiSOtNg8PjDVDmAhwT\nMxj0Dtwi2fAqQ76RVrrXpNp3uCOIAu4CfruIb5llcJ3uak0ZbnWri32AxSgk80y3\nEWiRX/WEDu5znejF+5O3pI02atWWcnxifEKGGlxwkcMbQdA67MlrJLFaSnnGpNXo\nyPfcul058SOqhafIZQMEKQ==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configuration/simple.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\nUWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\nHUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\no2kQ+X5xK9cipRgEKwIDAQAB\n-----END PUBLIC KEY-----\n\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-Active.json",
    "content": "{\n  \"active\" : true,\n  \"sub\": \"test-subject\",\n  \"scope\": \"message:read\",\n  \"exp\": 4683883211\n}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-ActiveNoScopes.json",
    "content": "{\n  \"active\" : true,\n  \"sub\": \"test-subject\",\n  \"exp\": 4683883211\n}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-Default.jwks",
    "content": "{\"keys\":[{\"p\":\"49neceJFs8R6n7WamRGy45F5Tv0YM-R2ODK3eSBUSLOSH2tAqjEVKOkLE5fiNA3ygqq15NcKRadB2pTVf-Yb5ZIBuKzko8bzYIkIqYhSh_FAdEEr0vHF5fq_yWSvc6swsOJGqvBEtuqtJY027u-G2gAQasCQdhyejer68zsTn8M\",\"kty\":\"RSA\",\"q\":\"tWR-ysspjZ73B6p2vVRVyHwP3KQWL5KEQcdgcmMOE_P_cPs98vZJfLhxobXVmvzuEWBpRSiqiuyKlQnpstKt94Cy77iO8m8ISfF3C9VyLWXi9HUGAJb99irWABFl3sNDff5K2ODQ8CmuXLYM25OwN3ikbrhEJozlXg_NJFSGD4E\",\"d\":\"FkZHYZlw5KSoqQ1i2RA2kCUygSUOf1OqMt3uomtXuUmqKBm_bY7PCOhmwbvbn4xZYEeHuTR8Xix-0KpHe3NKyWrtRjkq1T_un49_1LLVUhJ0dL-9_x0xRquVjhl_XrsRXaGMEHs8G9pLTvXQ1uST585gxIfmCe0sxPZLvwoic-bXf64UZ9BGRV3lFexWJQqCZp2S21HfoU7wiz6kfLRNi-K4xiVNB1gswm_8o5lRuY7zB9bRARQ3TS2G4eW7p5sxT3CgsGiQD3_wPugU8iDplqAjgJ5ofNJXZezoj0t6JMB_qOpbrmAM1EnomIPebSLW7Ky9SugEd6KMdL5lW6AuAQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"wdkFu_tV2V1l_PWUUimG516Zvhqk2SWDw1F7uNDD-Lvrv_WNRIJVzuffZ8WYiPy8VvYQPJUrT2EXL8P0ocqwlaSTuXctrORcbjwgxDQDLsiZE0C23HYzgi0cofbScsJdhcBg7d07LAf7cdJWG0YVl1FkMCsxUlZ2wTwHfKWf-v4\",\"dp\":\"uwnPxqC-IxG4r33-SIT02kZC1IqC4aY7PWq0nePiDEQMQWpjjNH50rlq9EyLzbtdRdIouo-jyQXB01K15-XXJJ60dwrGLYNVqfsTd0eGqD1scYJGHUWG9IDgCsxyEnuG3s0AwbW2UolWVSsU2xMZGb9PurIUZECeD1XDZwMp2s0\",\"dq\":\"hra786AunB8TF35h8PpROzPoE9VJJMuLrc6Esm8eZXMwopf0yhxfN2FEAvUoTpLJu93-UH6DKenCgi16gnQ0_zt1qNNIVoRfg4rw_rjmsxCYHTVL3-RDeC8X_7TsEySxW0EgFTHh-nr6I6CQrAJjPM88T35KHtdFATZ7BCBB8AE\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-Empty.jwks",
    "content": "{\"keys\":[]}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-Expired.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE1MzAyMzE3MTB9.c8vXYFwe1cBuglaZbmZFXJOmLsu_IQf-OsOiiOGhEJYOzu6h6v_qEzf2xxbu5TSvwAERmDITUSK41UIIvgU75WebtgilNnTR83B_gPM-7_FI2FLzlgVH7WayzvbYTQqepE_ZUMLFkGkK4r-dRiOyB9_cfl6jq_b5hE_biH1qrgPQrjlEhU8YxeK2EE05wsARLzyjoIYifkStjPC6rC-MLFIVk5JoITNzkTh7zYYSWtKWEgwd8S_vluVtJaPk-yKPb4tXcFRzCFl_qd7aCF8_LHyhw-4wvhWRIi8DmQmRU_a1RxR0mi-UCp0jMwmBZxxkSdqJ4l_EHI1yVqpgnbMLDw\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-ExpiresAt4687177990.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjQ2ODcxNzc5OTB9.RRQvqIZzLweq0iwWUZk1Dpiz6iUmT4bAVhGWqvWNWK3UwJ6aBIYsCRhdVeKQp-g1TxXovMALeAu_2oPmV0wOEEanesAKxjKYcJZQIe8HnVqgug6Ibs04uQ1mJ4RgfntPM-ebsJs-2tjFFkLEYJSkpq2o6SEFW9jBJyW8b8C5UJJahqynonA-Dw5GH1nin5bhhliLuFOmu0Ityt0uJ1Y_vuGsSA-ltVcY52jE4x6GH9NQxLX4ceO1bHSOmdspBoGsE_yo9-zsQw0g1_Iy7uqEjos3xrrboH6Z_u7pRL7AQJ7GNzZlinjYYPANQbYknieZD6beddTK7lvr4DYiPBmXzA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-Inactive.json",
    "content": "{\n  \"active\" : false\n}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-Kid.token",
    "content": "eyJraWQiOiJvbmUiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjQ2ODM4ODM1NzJ9.UhukjNEowC5lLCccvdjCUJad5J9FGNModegMZGe9qKIbXxmfseTttZUNn3_K_6aNCfimtmRktCRbw3fUTcje2TFJOJ6SmomLcQyjq7S41Wq6oBSA2fdqOOU4vNvrk8_pSExsSyN9bfWiJ51I8Agzbq5eUDNo_HEpaJZimrIe9f2_njU1GxvAWsq_h4UhHEgPPb3kY9kN9hVYX_oShhh7JxbLJBnfsKBOKGEWOsE65GlmDgQV4om6RGjJaz6jFHKJTCpH08ADA3j2dqT0LNy4PrUmbnjPjWVtSQJkGcgUkcQW6qz0K86ZfJZZng_iB2VadRm5qO-99ySKmlxa5A-_Iw\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-MalformedPayload.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJuYmYiOnt9LCJleHAiOjQ2ODM4OTEyMTl9.kpdv6ZXyYszZUzA4mJpviCBPzPftk6tIbIn5OoMuM09MKZCUCAFD8Y1tDmjzbWdkR_5CYiFMvSLq6DzAlugtGRAShc93dmDlyZmhcct2G477FxWaRKbtmFDjzuCjGyn7xHWpS7Wz6-Ngb-JyGI2m7FxXCgCpiYYBl-4-ONTuAT0fArJi_voA8K6YLnnjEjEprI3wsQRoS3Twa_fVdGkpMNlOGsQOqmlfjDrXpyfiANOe_ZztHxbDtJEZ9zfELxx9fzkZgTL1fD2Sj6HueDU-tMt-6IaGpBCLsg7d85RK001-U9u3Ph9awQC4QZK-8-F9OUUCY5RNcRJ57KEh9PjUfA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-TooEarly.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJuYmYiOjQ2ODM4OTI2NTUsImV4cCI6NDY4Mzg5MjY1NX0.MIaECJrmYjAByKNJoWHlP5ewg2xiW7GIxL8Vepp3ZIKf_jjM2OSMQlAWGmfD3Kf3bfesvSI7glw5qg_ZIv4FdIPaTvnmLRjWQkpk-QiLTJr_HM2wWeNbUJ1zciGWQlWAvabtQuyeGt1dsfQq53QLVNpvuioYdVg-gz_76uwDTxCKQU_99ksQhMMJsYJVDA_-uWGTzBANszcZykqwWFMaoXF4lkVPK4U68n18ISBB761wFusUCtyGWzwevX7wBAEJxcRy6ZVk3h7GyxZBsbRAd5fPn3dPMxNvL_CEp5jUYSAH-arAdDkvAph5Vk1yXof7FFRcffJpAy76HC66hR2JQA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-TwoKeys.jwks",
    "content": "{\"keys\":[{\"p\":\"49neceJFs8R6n7WamRGy45F5Tv0YM-R2ODK3eSBUSLOSH2tAqjEVKOkLE5fiNA3ygqq15NcKRadB2pTVf-Yb5ZIBuKzko8bzYIkIqYhSh_FAdEEr0vHF5fq_yWSvc6swsOJGqvBEtuqtJY027u-G2gAQasCQdhyejer68zsTn8M\",\"kty\":\"RSA\",\"q\":\"tWR-ysspjZ73B6p2vVRVyHwP3KQWL5KEQcdgcmMOE_P_cPs98vZJfLhxobXVmvzuEWBpRSiqiuyKlQnpstKt94Cy77iO8m8ISfF3C9VyLWXi9HUGAJb99irWABFl3sNDff5K2ODQ8CmuXLYM25OwN3ikbrhEJozlXg_NJFSGD4E\",\"d\":\"FkZHYZlw5KSoqQ1i2RA2kCUygSUOf1OqMt3uomtXuUmqKBm_bY7PCOhmwbvbn4xZYEeHuTR8Xix-0KpHe3NKyWrtRjkq1T_un49_1LLVUhJ0dL-9_x0xRquVjhl_XrsRXaGMEHs8G9pLTvXQ1uST585gxIfmCe0sxPZLvwoic-bXf64UZ9BGRV3lFexWJQqCZp2S21HfoU7wiz6kfLRNi-K4xiVNB1gswm_8o5lRuY7zB9bRARQ3TS2G4eW7p5sxT3CgsGiQD3_wPugU8iDplqAjgJ5ofNJXZezoj0t6JMB_qOpbrmAM1EnomIPebSLW7Ky9SugEd6KMdL5lW6AuAQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"wdkFu_tV2V1l_PWUUimG516Zvhqk2SWDw1F7uNDD-Lvrv_WNRIJVzuffZ8WYiPy8VvYQPJUrT2EXL8P0ocqwlaSTuXctrORcbjwgxDQDLsiZE0C23HYzgi0cofbScsJdhcBg7d07LAf7cdJWG0YVl1FkMCsxUlZ2wTwHfKWf-v4\",\"dp\":\"uwnPxqC-IxG4r33-SIT02kZC1IqC4aY7PWq0nePiDEQMQWpjjNH50rlq9EyLzbtdRdIouo-jyQXB01K15-XXJJ60dwrGLYNVqfsTd0eGqD1scYJGHUWG9IDgCsxyEnuG3s0AwbW2UolWVSsU2xMZGb9PurIUZECeD1XDZwMp2s0\",\"dq\":\"hra786AunB8TF35h8PpROzPoE9VJJMuLrc6Esm8eZXMwopf0yhxfN2FEAvUoTpLJu93-UH6DKenCgi16gnQ0_zt1qNNIVoRfg4rw_rjmsxCYHTVL3-RDeC8X_7TsEySxW0EgFTHh-nr6I6CQrAJjPM88T35KHtdFATZ7BCBB8AE\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"},{\"p\":\"_CI5g5In9T4ZgakV1i62UU6yjorEr5t2URHfRYqxN7S4aKsQOzggcPoqa78xRj8PAPuf3P0ArPEAHdS6bFK7RLrFXdvyEmSNTJa1gcLCf2Zmep8bsrhrCvh6seZNvfrSMV0ULmk0B75Fs8mqE7nwcIbPtBYkinlSIw-sKRv62DM\",\"kty\":\"RSA\",\"q\":\"pqfexT3HBAagH-iydGsWbjG6CcYyvSQZdFtUu4LIOBCYVA0dvkN9s7uU1eoevHN_ksf-hfrF5AQH0a5P0dIJ2pp1bFa9uo9DJ7khU9sIBk9_o8nST2QLHwPQmGTW8vVlcSF7Vffvzm2fV3cQ3dfI5lvtkqfX_Z3WkF8UjFjADe8\",\"d\":\"FzB5xChO8e89JisxSueY5j1RUBmatIAs_8Z3LUHOw16GlAhBhbSNl-7bXkbcUWLq9M1zTLCD91SSZXBohf9j1ebqWnbjMqQmdkxlQcVRoKcnMJ5YBabCTMBXghQnJetUMh6x6hXRnR1CSBNRdZPf-K2bnxL3xRNRSfY_7bjpb_q5pyUsK66ugSKwuEOUDNf1ttOZi4PBTsxWMDyXi_7fNFjl-B831uWNDVwdY4j68PVwGPT87zjZYjZRTZXB4ILUP11ztw4s3s_bU1Lj0PeZJsA5rmjU1iBzqCNdzgYxNlfV7M62VCkE1Wtd6M97jtysiT-5wQUMxNugoOTc9thc1Q\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"two\",\"qi\":\"bnGriiVGVea9vSaN_48YYTEoKYM1kF7TrCRKERkMWdi4EHF7pZNWBv8arxaLUzElllvtGlVTNwkZlG0gOhXBoLYbcfqVikDklkBxtsuZEBKgvX7zFlDIBlNjh98lcZqDqz7Rqwr-tavxTCq2LNNlK6x-dYL61Agw_LOilYqbSfA\",\"dp\":\"MmT4z-ZnnCn0WSkdlziw8iFjqP_tfhf5lwyWbsTg1PyHG0yNqvh1637k-bI2PA8ghZbFhhr_hpGI7210cXA7w-n8xtzOToTQhS1eS_hMfcBO3VVt6NPZeVDe3S3l_gHi_0DWZsxaPO336o51MwooF6WqYBlI5nCHTUC1rWXNRmc\",\"dq\":\"dd_ybywc4boV87vQzQsZWGOPpG4tYR5xap1WtzHvj8gdFgYY7YQrGr8orIzlpIFE0Hroibcv1PEM3sAd8NhQ4--v8isAEz5VT3lgG0Gm0V_VdfG_8StfulYmakOYzUvIrlXyOIIfebCLrX-nzGFd1aFbzgktelLzejXmAMadQL0\",\"n\":\"pCOHBsaoxlt9-qVE_INhrbkmxm7WqwEeqUBBIgHvm_JzXbmJ4iQzVF5tzAbRayxUmPbZ4E80R5HlIC2CQ7yyweTbIIWIw_TcQzXR4u3twEN1awP4s1n-00Eeurr-s9c_txZQQiDkyrCMYc9vlmsneFfubyoTvg9h_rckd8w34AyE8-wxgBRqUbm1x4ozcVmUJHkaPbQfbhIighl7osoQ4t_wXjAhTN_c9XttVjXlRwqVYPFNYUcC9GoaXWJRHjydHNFeBboOZY3E8ND6DbJ4nVtxydpUQSjTC-N-wQmhKmtYadd2hh2yywvtXpL5Q98XSphrrIHK-GWY0j8kimpunQ\"}]}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-Unsigned.token",
    "content": "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJuYmYiOjE1MzAzMDA4MzgsImV4cCI6MjE0NjAwMzE5OSwiaWF0IjoxNTMwMzAwODM4LCJ0eXAiOiJKV1QifQ.\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-ValidMessageReadScope.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJzY29wZSI6Im1lc3NhZ2U6cmVhZCIsImV4cCI6NDY4Mzg4MzIxMX0.cM7Eq9H20503czYVy1aVo8MqTQd8YsYGpv_lAV4PKr3y8NgvvosNjCSUs8rrGjQ0Sp3c4iXK6UVXq8pOJVeWXbSZa1IKAsIhiMIcg2xPFM6e71MVdX4bo255Yh8Nuh0p3xxP9isK_iAKNdMuVBOGfe9KATlmp2dOi0OpAjwSmxPJD1A7AC5f62YIe3Yx2gO6mbfANZJWQ7TxlUuCT_D5FEqg2FfYFqlFaluqWd_2X-esIsiDTxa1R9oF5XwgT6tsgvS7iYSiJw_uNKX0yU4eyLzYuIhnN_hVsr4jOZqPlsqCrkEohOGZg_Jir-7tLxZu0PqoH4ejC24FeDtC9xVa0w\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-ValidMessageReadScp.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJzY3AiOlsibWVzc2FnZTpyZWFkIl0sImV4cCI6NDY4Mzg5Nzc3Nn0.LtMVtIiRIwSyc3aX35Zl0JVwLTcQZAB3dyBOMHNaHCKUljwMrf20a_gT79LfhjDzE_fUVUmFiAO32W1vFnYpZSVaMDUgeIOIOpxfoe9shj_uYenAwIS-_UxqGVIJiJoXNZh_MK80ShNpvsQwamxWEEOAMBtpWNiVYNDMdfgho9n3o5_Z7Gjy8RLBo1tbDREbO9kTFwGIxm_EYpezmRCRq4w1DdS6UDW321hkwMxPnCMSWOvp-hRpmgY2yjzLgPJ6Aucmg9TJ8jloAP1DjJoF1gRR7NTAk8LOGkSjTzVYDYMbCF51YdpojhItSk80YzXiEsv1mTz4oMM49jXBmfXFMA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-ValidMessageWriteScp.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJzY3AiOlsibWVzc2FnZTp3cml0ZSJdLCJleHAiOjQ2ODM4OTY0OTl9.mxAFzoNjjo-7E4D_XYVme69Y7F-J--q41x6lHDTSOxzVNfQqtJ-U-N4pn7St5jElm9y3mSUxTtmwCnukaVVZkeI8aJjUc8V8nxUAsiZIDvQWjr9uW4xUIcE6MiwC0A9rhY-3I87u6No-KBTxyT80zLnCjtS2XpTId-NSd3vcYmM7Vzn4-8KoR_m-7XrjvrO69HlRrH2uUAXGnr1sn6vLp7YruupqKrHqa0e9pIpN-VRzC8Bx2LQP9mVMlQy4b1hx5MdjOTV3HUSnWiT-93z4rTMOoHScKDwmzFYoS7e00F5hyd4jzbpHdpDKnjLdwPQYz_HCmQ5MV21-Q4Q1jparIg\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-ValidNoScopes.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjQ2ODM4Mjg2NzR9.LV_i9lzN_gAB2MUuZHJKm2tOfa3xWq_qfE2lx67eoYJZsY_20Ma98A3Hh2k0wnb_mNn6jfQhXbqvUy1llmQtsx3gMNhN2Axfe3UccSKYEb2Ow5OFlrMFYby1d_D4GfXKUFKq8jyMWVlrjk_XrfJyfzeo0MyZVzURSOXv1Ehbl5-xAS_N72jiAI7cIHlHGm93Hwdk8h7Tkkf_5t2dOMJM0mh0fOT9ou3J2_ngaNDfvlAmBLxHQiJ6JrFH5njqe4lSBTxJocDcgZwGVKd0WvV4W-jwA267tZjssDFmS3xZ9hoDO_M-EjlOiEPuWLd9nQCGJpBJ3z3WeC4qrKYghHTNLA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-WrongAlgorithm.token",
    "content": "eyJhbGciOiJSUzUxMiJ9.eyJleHAiOjQ2ODczNDQ2NzN9.hvVUW_xwUXd7nGm27E5tLTZ21x64YjP0o-TMW6t_bOkfG1Vp1AMEX8fXvSqeG0vK8TWiB2_keOGtH-eFmAGBEYXq1o1zj1BgMHeaZAVio9n-77DkTzQ7CiOF5M1M7B_Ng4K8ra4DpieZZXVjHTWsuOiU1hWoI1tIna8VucAxZln-oh7PkrYmgwFTlsL2Z9aZZYN_X7ECyRQDf3lRrLwr4Go_XpJ5i9F-GT5LvUYa42uggGjvq_frfb0t5wcmPgjtqiE6l2mnrYFjjKTq1nQRYrJ5wFWOHUTRxNsGS8PwrNxzh6JW1ZZTS0n_JIOvSh__w0WAB241QLoKBx4AETMLQA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests-WrongSignature.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjQ2ODczNDQ0MTd9.jfqDyHvpRXWF6KaRQS3cGT0HUSix09xwTPvUCtg9UJ2QR1Rx4MclGCli3yIHNm0vsRed4s-gZduVGfbj7enyKnpXCZE7dNxZENfm7P54OfJmlyJY3DvhzlyH_rtuOD4c_Q88J9uELd_pghikLlMtu8090UzTtwRfdo_JsDfMRAcDeYq7TTaL60w3AVarStwZAAy_dpi6bTEanm5hwkz4-deA4Bz4KentpvlcwB01IXw9DVYrW1lpzLgycwk_VbCK_LA1hjFnnjc3OnQaxvqydrBAlFD3ziklVAxGnKnrYzppixdwwztuga4XS36OhicIGXEkMf3oT3nzgcR309DP_A\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-bean.xml",
    "content": "<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<b:bean id=\"passwordEncoder\" class=\"org.springframework.security.crypto.password.NoOpPasswordEncoder\" factory-method=\"getInstance\"/>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic />\n\t</http>\n\n\t<authentication-manager>\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"user\" password=\"password\" authorities=\"ROLE_USER\" />\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/authentication/PasswordEncoderParserTests-default.xml",
    "content": "<b:beans xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic />\n\t</http>\n\n\t<authentication-manager>\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\" />\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/core/GrantedAuthorityDefaultsXmlTests-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:c=\"http://www.springframework.org/schema/c\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\">\n\n\t<http use-expressions=\"true\">\n\t\t<intercept-url access=\"hasRole('USER')\" pattern=\"/**\" />\n\t\t<form-login/>\n\t\t<logout/>\n\t</http>\n\n\t<authentication-manager>\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"user\" password=\"password\" authorities=\"USER\"/>\n\t\t\t\t<user name=\"admin\" password=\"password\" authorities=\"USER,ADMIN\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n\t<global-method-security jsr250-annotations=\"enabled\" pre-post-annotations=\"enabled\"/>\n\n\t<b:bean class=\"org.springframework.security.config.core.HelloWorldMessageService\"/>\n\t<b:bean class=\"org.springframework.security.config.core.GrantedAuthorityDefaults\"\n\t\tc:rolePrefix=\"\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/debug/SecurityDebugBeanFactoryPostProcessorTests-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans\n\txmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<debug/>\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<authentication-manager>\n\t\t<authentication-provider ref=\"authProvider\"/>\n\t</authentication-manager>\n\n\t<context:component-scan base-package=\"org.springframework.security.config.debug\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/AccessDeniedConfigTests-AccessDeniedHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"true\">\n\t\t<access-denied-handler ref=\"adh\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t</http>\n\n\t<b:bean name=\"adh\"\n\t\t\tclass=\"org.springframework.security.config.http.AccessDeniedConfigTests.GoneAccessDeniedHandler\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/AccessDeniedConfigTests-NoLeadingSlash.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<access-denied-handler error-page=\"noLeadingSlash\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/AccessDeniedConfigTests-UsesPathAndRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<access-denied-handler error-page=\"/go-away\" ref=\"adh\"/>\n\t</http>\n\n\t<b:bean name=\"adh\"\n\t\t\tclass=\"org.springframework.security.config.http.AccessDeniedConfigTests.GoneAccessDeniedHandler\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfBeanDefinitionParserTests-RegisterDataValueProcessorOnyIfNotRegistered.xml",
    "content": "<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t   xmlns:security=\"http://www.springframework.org/schema/security\"\n\t   xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\n\n\nhttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<security:http pattern=\"/foo/**\">\n\t\t<security:intercept-url pattern=\"/**\" access=\"hasRole('FOO')\" />\n\t\t<security:http-basic/>\n\t</security:http>\n\n\t<security:http pattern=\"/bar/**\">\n\t\t<security:intercept-url pattern=\"/**\" access=\"hasRole('BAR')\" />\n\t\t<security:http-basic/>\n\t</security:http>\n\n\t<security:authentication-manager >\n\t\t<security:authentication-provider>\n\t\t\t<security:user-service>\n\t\t\t\t<security:user name=\"gnu\" password=\"{noop}gnat\" authorities=\"ROLE_FOO\" />\n\t\t\t</security:user-service>\n\t\t</security:authentication-provider>\n\t</security:authentication-manager>\n\n</beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-AutoConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xmlns:p=\"http://www.springframework.org/schema/p\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http-firewall ref=\"firewall\"/>\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n\n\t<b:bean id=\"firewall\" class=\"org.springframework.security.web.firewall.StrictHttpFirewall\"\n\t\t\tp:unsafeAllowAnyHttpMethod=\"true\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-CsrfDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-CsrfEnabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xmlns:p=\"http://www.springframework.org/schema/p\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http-firewall ref=\"firewall\"/>\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t\t<csrf/>\n\t</http>\n\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n\n\t<b:bean id=\"firewall\" class=\"org.springframework.security.web.firewall.StrictHttpFirewall\"\n\t\t\tp:unsafeAllowAnyHttpMethod=\"true\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-WithAccessDeniedHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<access-denied-handler ref=\"accessDeniedHandler\"/>\n\t\t<csrf/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-WithRequestAttrName.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:p=\"http://www.springframework.org/schema/p\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<csrf request-handler-ref=\"requestHandler\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean id=\"requestHandler\" class=\"org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler\"\n\t\tp:csrfRequestAttributeName=\"csrf-attribute-name\"/>\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-WithRequestMatcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<csrf request-matcher-ref=\"requestMatcher\"/>\n\t</http>\n\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-WithSessionManagement.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<session-management invalid-session-url=\"/error/sessionError\"/>\n\t\t<csrf/>\n\t</http>\n\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-WithXorCsrfTokenRequestAttributeHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:p=\"http://www.springframework.org/schema/p\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<csrf request-handler-ref=\"requestHandler\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean id=\"requestHandler\" class=\"org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler\"\n\t\tp:csrfRequestAttributeName=\"_csrf\"/>\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-csrf-token-repository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean id=\"csrfTokenRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.csrf.CsrfTokenRepository\" type=\"java.lang.Class\"/>\n\t\t<b:constructor-arg value=\"csrfTokenRepository\" type=\"java.lang.String\"/>\n\t</b:bean>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-request-matcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean id=\"requestMatcher\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.util.matcher.RequestMatcher\" type=\"java.lang.Class\"/>\n\t\t<b:constructor-arg value=\"requestMatcher\" type=\"java.lang.String\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-shared-access-denied-handler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean id=\"accessDeniedHandler\" class=\"org.springframework.security.config.http.CsrfConfigTests.TeapotAccessDeniedHandler\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-shared-controllers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<mvc:annotation-driven>\n\t\t<mvc:argument-resolvers>\n\t\t\t<b:bean class=\"org.springframework.security.web.method.annotation.CsrfTokenArgumentResolver\"/>\n\t\t</mvc:argument-resolvers>\n\t</mvc:annotation-driven>\n\n\t<b:bean class=\"org.springframework.security.config.http.CsrfConfigTests.RootController\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-shared-csrf-token-repository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean id=\"csrfTokenRepository\" class=\"org.springframework.security.config.http.CsrfConfigTests.TeapotCsrfTokenRepository\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-shared-userservice.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns=\"http://www.springframework.org/schema/security\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\t<user-service>\n\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/DeferHttpSessionTests-Explicit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:p=\"http://www.springframework.org/schema/p\"\n\t\t xmlns:c=\"http://www.springframework.org/schema/c\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security pre-post-enabled=\"true\" />\n\t<b:bean class=\"org.springframework.security.config.http.DeferHttpSessionXmlConfigTests$Service\" />\n\n\t<http auto-config=\"true\"\n\t\t\tuse-authorization-manager=\"true\">\n\t\t<intercept-url  pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"CsrfConfigTests-shared-userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests-AutoConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests-Simple.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests-WithAuthenticationFailureForwardUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<form-login\n\t\t\t\tauthentication-failure-forward-url=\"/failure_forward_url\"/>\n\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests-WithAuthenticationSuccessForwardUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<form-login\n\t\t\t\tauthentication-success-forward-url=\"/success_forward_url\"/>\n\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests-WithCustomAttributes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<form-login\n\t\t\t\tlogin-processing-url=\"/signin\"\n\t\t\t\tusername-parameter=\"custom_user\"\n\t\t\t\tpassword-parameter=\"custom_pass\"/>\n\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-ForSec2919.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<form-login login-page=\"/login\"/>\n\t</http>\n\n\t<b:bean name=\"login\" class=\"org.springframework.security.config.http.FormLoginConfigTests.LoginController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-ForSec3147.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<form-login login-page=\"/login\"/>\n\t</http>\n\n\t<b:bean name=\"login\" class=\"org.springframework.security.config.http.FormLoginConfigTests.LoginController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-NoLeadingSlashDefaultTargetUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<form-login default-target-url=\"noLeadingSlash\"/>\n\t</http>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-NoLeadingSlashLoginPage.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<form-login login-page=\"noLeadingSlash\"/>\n\t</http>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-UsingSpel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_USER\"/>\n\t\t<form-login\n\t\t\t\tdefault-target-url=\"#{T(org.springframework.security.config.http.WebConfigUtilsTests).URL}/default\"\n\t\t\t\tauthentication-failure-url=\"#{T(org.springframework.security.config.http.WebConfigUtilsTests).URL}/failure\"\n\t\t\t\tlogin-page=\"#{T(org.springframework.security.config.http.WebConfigUtilsTests).URL}/login\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithCsrfDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithCsrfEnabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<csrf disabled=\"false\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithCustomSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" security-context-holder-strategy-ref=\"ref\">\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithDefaultTargetUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_USER\"/>\n\t\t<form-login always-use-default-target=\"true\" default-target-url=\"/default\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithRequestMatcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_USER\"/>\n\t\t<form-login/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithSuccessAndFailureHandlers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_USER\"/>\n\t\t<form-login authentication-success-handler-ref=\"fsh\" authentication-failure-handler-ref=\"fsh\"/>\n\t</http>\n\n\t<b:bean name=\"fsh\" class=\"org.springframework.security.config.http.FormLoginConfigTests.TeapotAuthenticationHandler\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithUsernameAndPasswordParameters.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<form-login username-parameter=\"xname\" password-parameter=\"xpass\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-AuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" authorization-manager-ref=\"ref\">\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.authorization.AuthorizationManager\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-Minimal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_USER\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-MinimalAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"hasRole('USER')\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-WithObservationRegistry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" observation-registry-ref=\"ref\" use-authorization-manager=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"hasRole('USER')\"/>\n\t</http>\n\n\t<b:bean name=\"handler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"io.micrometer.observation.ObservationHandler\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean name=\"ref\" class=\"org.springframework.security.config.http.HttpConfigTests.MockObservationRegistry\">\n\t\t<b:property name=\"handler\" ref=\"handler\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-RequiresMvc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<cors/>\n\t</http>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\n\t<mvc:annotation-driven/>\n\n\t<http entry-point-ref=\"ep\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<cors/>\n\t</http>\n\n\t<b:bean name=\"ep\" class=\"org.springframework.security.web.authentication.HttpStatusEntryPoint\">\n\t\t<b:constructor-arg value=\"I_AM_A_TEAPOT\"/>\n\t</b:bean>\n\n\t<b:bean name=\"corsController\" class=\"org.springframework.security.config.http.HttpCorsConfigTests.CorsController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsConfigurationSource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http entry-point-ref=\"ep\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<cors configuration-source-ref=\"ccs\"/>\n\t</http>\n\n\t<b:bean name=\"ep\" class=\"org.springframework.security.web.authentication.HttpStatusEntryPoint\">\n\t\t<b:constructor-arg value=\"I_AM_A_TEAPOT\"/>\n\t</b:bean>\n\n\t<b:bean name=\"ccs\" class=\"org.springframework.security.config.http.HttpCorsConfigTests.MyCorsConfigurationSource\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsFilter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http entry-point-ref=\"ep\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<cors ref=\"cf\"/>\n\t</http>\n\n\t<b:bean name=\"ep\" class=\"org.springframework.security.web.authentication.HttpStatusEntryPoint\">\n\t\t<b:constructor-arg value=\"I_AM_A_TEAPOT\"/>\n\t</b:bean>\n\n\t<b:bean name=\"cf\" class=\"org.springframework.web.filter.CorsFilter\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.http.HttpCorsConfigTests.MyCorsConfigurationSource\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-CacheControlDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<cache-control disabled=\"true\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-ContentSecurityPolicyWithEmptyDirectives.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<headers>\n\t\t\t<content-security-policy policy-directives=\"\"/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-ContentSecurityPolicyWithPolicyDirectives.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<headers>\n\t\t\t<content-security-policy policy-directives=\"default-src 'self'\"/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-ContentSecurityPolicyWithReportOnly.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<headers>\n\t\t\t<content-security-policy\n\t\t\t\t\tpolicy-directives=\"default-src https:; report-uri https://example.org/\"\n\t\t\t\t\treport-only=\"true\"/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-ContentTypeOptionsDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<content-type-options disabled=\"true\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCacheControl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<cache-control/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithContentSecurityPolicy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<content-security-policy policy-directives=\"default-src 'self'\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithContentTypeOptions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t\t<content-type-options/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginEmbedderPolicy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<cross-origin-embedder-policy policy=\"require-corp\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginOpenerPolicy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<cross-origin-opener-policy policy=\"same-origin-allow-popups\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginPolicies.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<cross-origin-opener-policy policy=\"same-origin\"/>\n\t\t\t<cross-origin-embedder-policy policy=\"require-corp\"/>\n\t\t\t<cross-origin-resource-policy policy=\"same-origin\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCrossOriginResourcePolicy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<cross-origin-resource-policy policy=\"same-origin\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCustomHeader.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<header name=\"a\" value=\"b\"/>\n\t\t\t<header name=\"c\" value=\"d\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCustomHeaderWriter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<header ref=\"static\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"static\" class=\"org.springframework.security.web.header.writers.StaticHeadersWriter\">\n\t\t<b:constructor-arg value=\"abc\"/>\n\t\t<b:constructor-arg value=\"def\"/>\n\t</b:bean>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithCustomHstsRequestMatcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hsts include-subdomains=\"false\" max-age-seconds=\"1\" request-matcher-ref=\"any\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"any\" class=\"org.springframework.security.web.util.matcher.AnyRequestMatcher\"/>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithEmptyHpkp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hpkp/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithEmptyPins.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hpkp><pins/></hpkp>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithFrameOptions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<frame-options/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithFrameOptionsAllowFrom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<frame-options policy=\"ALLOW-FROM\" strategy=\"static\" value=\"https://example.org\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithFrameOptionsAllowFromBlankOrigin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<frame-options policy=\"ALLOW-FROM\" strategy=\"static\" value=\" \"/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithFrameOptionsAllowFromNoOrigin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<frame-options policy=\"ALLOW-FROM\" strategy=\"static\"/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithFrameOptionsAllowFromWhitelist.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<frame-options policy=\"ALLOW-FROM\" strategy=\"whitelist\" value=\"https://example.org\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithFrameOptionsDeny.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<frame-options policy=\"DENY\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithFrameOptionsSameOrigin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<frame-options policy=\"SAMEORIGIN\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithHpkp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hpkp>\n\t\t\t\t<pins>\n\t\t\t\t\t<pin algorithm=\"sha256\">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>\n\t\t\t\t</pins>\n\t\t\t</hpkp>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithHpkpDefaults.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hpkp>\n\t\t\t\t<pins>\n\t\t\t\t\t<pin>d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>\n\t\t\t\t</pins>\n\t\t\t</hpkp>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithHpkpIncludeSubdomains.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hpkp include-subdomains=\"true\">\n\t\t\t\t<pins>\n\t\t\t\t\t<pin>d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>\n\t\t\t\t</pins>\n\t\t\t</hpkp>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithHpkpMaxAge.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hpkp max-age-seconds=\"604800\">\n\t\t\t\t<pins>\n\t\t\t\t\t<pin>d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>\n\t\t\t\t</pins>\n\t\t\t</hpkp>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithHpkpReport.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hpkp report-only=\"false\">\n\t\t\t\t<pins>\n\t\t\t\t\t<pin>d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>\n\t\t\t\t</pins>\n\t\t\t</hpkp>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithHpkpReportUri.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hpkp report-uri=\"https://example.net/pkp-report\">\n\t\t\t\t<pins>\n\t\t\t\t\t<pin>d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>\n\t\t\t\t</pins>\n\t\t\t</hpkp>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithHsts.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<hsts/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithNoOverride.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithOnlyHeaderName.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<header name=\"a\"/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithOnlyHeaderValue.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<header value=\"b\"/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithPermissionsPolicy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<permissions-policy policy=\"geolocation=(self)\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithPlaceholder.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"${security.headers.defaults.disabled}\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithReferrerPolicy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<referrer-policy/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithReferrerPolicySameOrigin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<referrer-policy policy=\"same-origin\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtection.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<xss-protection/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionHeaderValueOne.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<xss-protection header-value=\"1\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionHeaderValueOneModeBlock.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<xss-protection header-value=\"1; mode=block\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithXssProtectionHeaderValueZero.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers defaults-disabled=\"true\">\n\t\t\t<xss-protection header-value=\"0\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsSessionManagementConcurrencyControlMaxSessions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<session-management>\n\t\t\t<concurrency-control max-sessions=\"${security.session-management.concurrency-control.max-sessions}\"\n\t\t\t\t\t\t\t\t error-if-maximum-exceeded=\"true\"/>\n\t\t</session-management>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsSessionManagementConcurrencyControlMaxSessionsRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<session-management>\n\t\t\t<concurrency-control max-sessions-ref=\"customSessionLimit\"\n\t\t\t\t\t\t\t\t error-if-maximum-exceeded=\"true\"/>\n\t\t</session-management>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:bean name=\"customSessionLimit\"\n\t\t\tclass=\"org.springframework.security.config.http.HttpHeadersConfigTests.CustomSessionLimit\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsSessionManagementConcurrencyControlWithInvalidMaxSessionsConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<session-management>\n\t\t\t<concurrency-control max-sessions=\"1\"\n\t\t\t\t\t\t\t\t max-sessions-ref=\"customSessionLimit\"\n\t\t\t\t\t\t\t\t error-if-maximum-exceeded=\"true\"/>\n\t\t</session-management>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:bean name=\"customSessionLimit\"\n\t\t\tclass=\"org.springframework.security.config.http.HttpHeadersConfigTests.CustomSessionLimit\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DisabledWithPlaceholder.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers disabled=\"${security.headers.disabled}\" />\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-FrameOptionsDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<frame-options disabled=\"true\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-FrameOptionsDisabledSpecifyingPolicy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<frame-options disabled=\"true\" policy=\"DENY\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HeadersDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<headers disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HeadersDisabledHavingChildElement.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<headers disabled=\"true\">\n\t\t\t<content-type-options/>\n\t\t</headers>\n\t</http>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HeadersDisabledWithContentSecurityPolicy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<headers disabled=\"true\">\n\t\t\t<content-security-policy policy-directives=\"default-src 'self'\"/>\n\t\t</headers>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HeadersEnabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HpkpDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<hpkp disabled=\"true\">\n\t\t\t\t<pins>\n\t\t\t\t\t<pin algorithm=\"sha256\">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>\n\t\t\t\t</pins>\n\t\t\t</hpkp>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HstsDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<hsts disabled=\"true\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HstsDisabledSpecifyingIncludeSubdomains.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<hsts disabled=\"true\" include-subdomains=\"true\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HstsDisabledSpecifyingMaxAge.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<hsts disabled=\"true\" max-age-seconds=\"1\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-HstsDisabledSpecifyingRequestMatcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<hsts disabled=\"true\" request-matcher-ref=\"dave\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-WithFrameOptions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<frame-options policy=\"SAMEORIGIN\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-XssProtectionDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<xss-protection disabled=\"true\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-XssProtectionDisabledSpecifyingHeaderValue.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<headers>\n\t\t\t<xss-protection disabled=\"true\" header-value=\"1\"/>\n\t\t</headers>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"simple\" class=\"org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/HttpInterceptUrlTests-interceptUrlWhenRequestMatcherRefThenWorks.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:c=\"http://www.springframework.org/schema/c\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<http-basic/>\n\t\t<intercept-url request-matcher-ref=\"matcherRef\" access=\"denyAll\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<user-service>\n\t\t<user name=\"user\" password=\"password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n\n\t<b:bean id=\"mvcPatternParser\" class=\"org.springframework.web.util.pattern.PathPatternParser\">\n\t\t<b:property name=\"caseSensitive\" value=\"false\"/>\n\t</b:bean>\n\n\t<b:bean id=\"matcherRef\" class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\"\n\t\tc:pattern=\"/foo\" c:method=\"GET\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-AntMatcherServletPath.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http request-matcher=\"path\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-AntMatcherServletPathAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http request-matcher=\"path\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-AuthorizationManagerFilterAllDispatcherTypes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url request-matcher-ref=\"pathErrorRequestMatcher\" access=\"permitAll()\" />\n\t\t<intercept-url request-matcher-ref=\"errorRequestMatcher\" access=\"authenticated\" />\n\t\t<intercept-url pattern=\"/**\" access=\"hasRole('USER')\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\t<b:bean name=\"error\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.ErrorController\"/>\n\n\t<b:bean name=\"errorRequestMatcher\" class=\"org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher\">\n\t\t<b:constructor-arg value=\"ERROR\"/>\n\t</b:bean>\n\n\t<b:bean name=\"errorPathRequestMatcher\" class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\">\n\t\t<b:constructor-arg value=\"/error\"/>\n\t</b:bean>\n\n\t<b:bean name=\"pathErrorRequestMatcher\" class=\"org.springframework.security.web.util.matcher.AndRequestMatcher\">\n\t\t<b:constructor-arg>\n\t\t\t<b:list>\n\t\t\t\t<b:ref bean=\"errorRequestMatcher\"/>\n\t\t\t\t<b:ref bean=\"errorPathRequestMatcher\"/>\n\t\t\t</b:list>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-CamelCasePathVariables.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path/{userName}/**\" access=\"#userName == authentication.name\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-CamelCasePathVariablesAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/path/{userName}/**\" access=\"#userName == authentication.name\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-CiRegexMatcherServletPath.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http request-matcher=\"ciRegex\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-CiRegexMatcherServletPathAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http request-matcher=\"ciRegex\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-DefaultMatcherNoIntrospectorBean.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-DefaultMatcherServletPath.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-DefaultMatcherServletPathAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-EmptyAccess.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/admin/**\" access=\"\" />\n\t</http>\n\n\t<user-service>\n\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-EmptyAccessLegacy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/admin/**\" access=\"\"/>\n\t</http>\n\n\t<user-service>\n\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-HasAnyRole.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"hasAnyRole('ROLE_DEVELOPER', 'ROLE_USER')\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-HasAnyRoleAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"hasAnyRole('ROLE_DEVELOPER', 'ROLE_USER')\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-MissingAccess.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/admin/**\"/>\n\t</http>\n\n\t<user-service>\n\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-MissingAccessLegacy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/admin/**\"/>\n\t</http>\n\n\t<user-service>\n\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-MvcMatchers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\" request-matcher=\"path\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<mvc:annotation-driven>\n\t\t<mvc:path-matching />\n\t</mvc:annotation-driven>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-MvcMatchersAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\" request-matcher=\"path\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<mvc:annotation-driven>\n\t\t<mvc:path-matching />\n\t</mvc:annotation-driven>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-MvcMatchersPathVariables.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path/{un}/**\" access=\"#un == 'user'\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<mvc:annotation-driven/>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-MvcMatchersPathVariablesAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/path/{un}/**\" access=\"#un == 'user'\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<mvc:annotation-driven/>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-MvcMatchersServletPath.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\" request-matcher=\"path\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<mvc:annotation-driven>\n\t\t<mvc:path-matching />\n\t</mvc:annotation-driven>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-MvcMatchersServletPathAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\" request-matcher=\"path\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<mvc:annotation-driven>\n\t\t<mvc:path-matching />\n\t</mvc:annotation-driven>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-PatchMethod.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" method=\"PATCH\" access=\"hasRole('ADMIN')\"/>\n\t\t<http-basic/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-PatchMethodAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" method=\"PATCH\" access=\"hasRole('ADMIN')\"/>\n\t\t<http-basic/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-PathVariables.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path/{un}/**\" access=\"#un == authentication.name\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-PathVariablesAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/path/{un}/**\" access=\"#un == authentication.name\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-RegexMatcherServletPath.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http request-matcher=\"regex\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-RegexMatcherServletPathAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http request-matcher=\"regex\">\n\t\t<intercept-url pattern=\"/path\" access=\"denyAll\" servlet-path=\"/spring\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-Sec2256.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path\" access=\"hasRole('USER')\"/>\n\t\t<intercept-url pattern=\"/path\" method=\"GET\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-Sec2256AuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/path\" access=\"hasRole('USER')\"/>\n\t\t<intercept-url pattern=\"/path\" method=\"GET\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-TypeConversionPathVariables.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/path/{un}/**\" access=\"@id.isOne(#un)\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\t<b:bean name=\"id\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.Id\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-TypeConversionPathVariablesAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/path/{un}/**\" access=\"@id.isOne(#un)\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"denyAll\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"path\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.PathController\"/>\n\t<b:bean name=\"id\" class=\"org.springframework.security.config.http.InterceptUrlConfigTests.Id\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/InterceptUrlConfigTests-ValidAccess.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/admin/**\" access=\"hasRole('ADMIN')\" />\n\t\t<http-basic />\n\t</http>\n\n\t<user-service>\n\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-AnonymousCustomAttributes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<anonymous username=\"josh\" granted-authority=\"ROLE_ANON\" key=\"myCustomKey\"/>\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/protected\" access=\"hasRole('ANON')\"/>\n\t\t<intercept-url pattern=\"/customKey\" access=\"anonymous\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.security.config.http.MiscHttpConfigTests.CustomKeyController\"/>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-AnonymousDisabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<anonymous enabled=\"false\"/>\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/unprotected\" access=\"anonymous\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-AnonymousEndpoints.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/unprotected\" access=\"anonymous\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-AnonymousMultipleAuthorities.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<anonymous username=\"josh\" granted-authority=\"ROLE_ANON,ROLE_KEY\" key=\"myCustomKey\"/>\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/protected\" access=\"hasRole('ANON')\"/>\n\t\t<intercept-url pattern=\"/customKey\" access=\"hasRole('KEY')\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.security.config.http.MiscHttpConfigTests.CustomKeyController\"/>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-AuthenticationManagerEraseCredentials.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\n\t<authentication-manager erase-credentials=\"true\">\n\t\t<authentication-provider>\n\t\t\t<user-service id=\"us\">\n\t\t\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t\t\t\t<user name=\"admin\" password=\"{noop}password\" authorities=\"ROLE_ADMIN\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-AuthenticationManagerRefKeepCredentials.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http authentication-manager-ref=\"authMgr\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\n\t<authentication-manager id=\"authMgr\" erase-credentials=\"false\">\n\t\t<authentication-provider>\n\t\t\t<user-service id=\"us\">\n\t\t\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t\t\t\t<user name=\"admin\" password=\"{noop}password\" authorities=\"ROLE_ADMIN\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-AuthenticationManagerRefNotProviderManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http authentication-manager-ref=\"authMgr\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean id=\"authMgr\" class=\"org.springframework.security.config.http.MiscHttpConfigTests.MockAuthenticationManager\"/>\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-AutoConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CiRegexSecurityPattern.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<debug/>\n\n\t<http pattern=\"\\A\\/[a-z]{10,}\" security=\"none\" request-matcher=\"ciRegex\"/>\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CollidingFilters.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<custom-filter ref=\"userFilter\" position=\"LOGOUT_FILTER\"/>\n\t</http>\n\n\t<b:bean name=\"userFilter\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"jakarta.servlet.Filter\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomAccessDecisionManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http access-decision-manager-ref=\"accessDecisionManager\" use-authorization-manager=\"false\">\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"accessDecisionManager\"\n\t\t\tclass=\"org.springframework.security.config.http.MiscHttpConfigTests.MockAccessDecisionManager\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomAuthenticationDetailsSourceRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic authentication-details-source-ref=\"authenticationDetailsSource\"/>\n\t\t<form-login authentication-details-source-ref=\"authenticationDetailsSource\"/>\n\t\t<x509 subject-principal-regex=\"OU=(.*?)(?:,|$)\" authentication-details-source-ref=\"authenticationDetailsSource\"/>\n\t</http>\n\n\t<b:bean name=\"authenticationDetailsSource\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.authentication.AuthenticationDetailsSource\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomFilters.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<custom-filter ref=\"${customFilterRef}\" position=\"FIRST\"/>\n\t\t<custom-filter ref=\"userFilter\" before=\"SECURITY_CONTEXT_FILTER\"/>\n\t\t<custom-filter ref=\"userFilter\" after=\"LOGOUT_FILTER\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"userFilter\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"jakarta.servlet.Filter\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomHttpBasicEntryPointRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<http-basic entry-point-ref=\"entryPoint\"/>\n\t\t<intercept-url pattern=\"/protected\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean name=\"entryPoint\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.AuthenticationEntryPoint\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomRequestMatcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<debug/>\n\n\t<http request-matcher-ref=\"matcher\" security=\"none\"/>\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"matcher\" class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\">\n\t\t<b:constructor-arg value=\"/unprotected\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-DeleteCookies.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<logout delete-cookies=\"JSESSIONID, mycookie\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-DisableUrlRewriting-NullSecurityContextRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" disable-url-rewriting=\"true\" security-context-repository-ref=\"securityContextRepository\">\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\t<b:bean id=\"securityContextRepository\" class=\"org.springframework.security.web.context.NullSecurityContextRepository\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-DisableUrlRewriting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" disable-url-rewriting=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-EntryPoint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http entry-point-ref=\"entryPoint\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean name=\"entryPoint\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.AuthenticationEntryPoint\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExplicitSave.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http security-context-explicit-save=\"true\">\n\t\t<form-login/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExplicitSaveAndExplicitRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http create-session=\"always\" security-context-repository-ref=\"repo\" security-context-explicit-save=\"true\">\n\t\t<form-login/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean name=\"repo\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.context.SecurityContextRepository\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExpressionHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"hasPermission('AnyObject','R')\"/>\n\t\t<expression-handler ref=\"expressionHandler\"/>\n\t</http>\n\n\t<b:bean name=\"expressionHandler\"\n\t\t\tclass=\"org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler\">\n\t\t<b:property name=\"permissionEvaluator\" ref=\"permissionEvaluator\"/>\n\t</b:bean>\n\n\t<b:bean name=\"permissionEvaluator\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.access.PermissionEvaluator\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-HttpBasic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-HttpFirewall.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http-firewall ref=\"firewall\"/>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:bean name=\"firewall\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.firewall.HttpFirewall\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-InterceptUrlExpressions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/protected\" access=\"hasAnyRole('ROLE_ADMIN', 'ROLE_UNOBTAINIUM')\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-InterceptUrlMethod.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/protected*\" method=\"POST\" access=\"ROLE_POST,ROLE_ADMIN\"/>\n\t\t<intercept-url pattern=\"/protected*\" method=\"DELETE\" access=\"ROLE_ADMIN\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_USER,ROLE_POST,ROLE_ADMIN\"/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\n\t<user-service>\n\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t\t<user name=\"admin\" password=\"{noop}password\" authorities=\"ROLE_ADMIN\"/>\n\t\t<user name=\"poster\" password=\"{noop}password\" authorities=\"ROLE_POST\"/>\n\t</user-service>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-InterceptUrlMethodRequiresHttps.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/protected\" method=\"GET\" access=\"hasRole('ADMIN')\" requires-channel=\"https\"/>\n\t\t<intercept-url pattern=\"/protected\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-InterceptUrlMethodRequiresHttpsAny.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/**\" method=\"GET\" access=\"hasRole('ADMIN')\" requires-channel=\"https\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-InvalidLogoutSuccessUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<logout logout-success-url=\"noLeadingSlash\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Jaas.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" jaas-api-provision=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.security.config.http.MiscHttpConfigTests.JaasController\"/>\n\n\t<b:bean id=\"jaasAuthenticationProvider\"\n\t\t  class=\"org.springframework.security.authentication.jaas.JaasAuthenticationProvider\">\n\t\t<b:property name=\"loginConfig\" value=\"classpath:org/springframework/security/config/http/jaas-login.conf\"/>\n\t\t<b:property name=\"loginContextName\" value=\"JAASTest\"/>\n\t\t<b:property name=\"callbackHandlers\">\n\t\t\t<b:list>\n\t\t\t\t<b:bean class=\"org.springframework.security.authentication.jaas.JaasNameCallbackHandler\"/>\n\t\t\t\t<b:bean class=\"org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler\"/>\n\t\t\t</b:list>\n\t\t</b:property>\n\t\t<b:property name=\"authorityGranters\">\n\t\t\t<b:list>\n\t\t\t\t<b:ref bean=\"jaasAuthorityGranter\"/>\n\t\t\t</b:list>\n\t\t</b:property>\n\t</b:bean>\n\n\t<b:bean name=\"jaasAuthorityGranter\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.authentication.jaas.AuthorityGranter\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<authentication-manager id=\"authenticationManager\">\n\t\t<authentication-provider ref=\"jaasAuthenticationProvider\"/>\n\t</authentication-manager>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-JeeFilter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<jee mappable-roles=\"admin,user\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-JeeFilterWithSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http security-context-holder-strategy-ref=\"ref\" use-authorization-manager=\"false\">\n\t\t<jee mappable-roles=\"admin,user\"/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-LogoutSuccessHandlerRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<logout success-handler-ref=\"logoutSuccessEndpoint\"/>\n\t</http>\n\n\t<b:bean name=\"logoutSuccessEndpoint\" class=\"org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler\">\n\t\t<b:property name=\"defaultTargetUrl\" value=\"/logoutSuccessEndpoint\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-MinimalConfiguration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-MissingUserDetailsService.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-NoAuthProviders.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<form-login/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<anonymous enabled=\"false\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-NoInternalAuthenticationProviders.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<form-login/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<anonymous enabled=\"false\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-NoSecurityForPattern.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<debug/>\n\n\t<http pattern=\"/unprotected\" security=\"none\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-OncePerRequest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http once-per-request=\"false\" use-authorization-manager=\"false\">\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/protected\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/unprotected-forwards-to-protected\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-OncePerRequestTrue.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http once-per-request=\"true\" use-authorization-manager=\"false\">\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/protected\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/unprotected-forwards-to-protected\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-PortsMappedInterceptUrlMethodRequiresAny.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<http-basic/>\n\t\t<port-mappings>\n\t\t\t<port-mapping http=\"9080\" https=\"9443\"/>\n\t\t</port-mappings>\n\t\t<intercept-url pattern=\"/**\" method=\"GET\" access=\"permitAll\" requires-channel=\"https\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-PortsMappedRequiresHttps.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<port-mappings>\n\t\t\t<port-mapping http=\"9080\" https=\"9443\"/>\n\t\t</port-mappings>\n\t\t<intercept-url pattern=\"/**\" method=\"GET\" access=\"authenticated\" requires-channel=\"https\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ProtectedLoginPage.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http use-authorization-manager=\"false\">\n\t\t<form-login login-page=\"/login\"/>\n\t\t<intercept-url pattern=\"/login*\" access=\"hasRole('ROLE_A')\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ProtectedLoginPageAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<form-login login-page=\"/login\"/>\n\t\t<intercept-url pattern=\"/login*\" access=\"hasRole('ROLE_A')\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RedirectToHttpsRequiresHttpsAny.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:util=\"http://www.springframework.org/schema/util\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/util\n\t\t\thttps://www.springframework.org/schema/util/spring-util.xsd\n\t\t\t\">\n\n\t<http redirect-to-https-request-matcher-ref=\"any\">\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/**\" method=\"GET\" access=\"hasRole('ADMIN')\" requires-channel=\"https\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\t<util:constant id=\"any\" static-field=\"org.springframework.security.web.util.matcher.AnyRequestMatcher.INSTANCE\" />\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RegexSecurityPattern.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<debug/>\n\n\t<http pattern=\"\\A\\/[a-z]{10,}\" security=\"none\" request-matcher=\"regex\"/>\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestCache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<request-cache ref=\"requestCache\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean name=\"requestCache\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.savedrequest.RequestCache\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestRejectedHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"MiscHttpConfigTests-HttpFirewall.xml\"/>\n\n\t<b:bean id=\"requestRejectedHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.firewall.RequestRejectedHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Sec750.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<authentication-manager>\n\t\t<authentication-provider user-service-ref=\"userService\"/>\n\t\t<authentication-provider ref=\"authenticationProvider\"/>\n\t</authentication-manager>\n\n\t<b:bean name=\"authenticationProvider\" class=\"org.springframework.security.authentication.dao.DaoAuthenticationProvider\">\n\t\t<b:constructor-arg name=\"userDetailsService\" ref=\"userService\"/>\n\t</b:bean>\n\n\t<b:bean name=\"userService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.core.userdetails.UserDetailsService\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean name=\"beanNameCollectingPostProcessor\" class=\"org.springframework.security.BeanNameCollectingPostProcessor\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Sec934.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/protected\" access=\"hasRole('ROLE_ADMIN')\"/>\n\t\t<intercept-url pattern=\"/protected\" access=\"hasRole('ROLE_USER')\"/>\n\t</http>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-SecurityContextRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http create-session=\"always\" security-context-repository-ref=\"repo\">\n\t\t<http-basic/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean name=\"repo\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.context.SecurityContextRepository\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-WithSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\" security-context-holder-strategy-ref=\"ref\">\n\t\t<intercept-url request-matcher-ref=\"dispatcherTypeMatcher\" access=\"permitAll\" />\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean id=\"dispatcherTypeMatcher\" class=\"org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher\">\n\t\t<b:constructor-arg value=\"ASYNC\"/>\n\t</b:bean>\n\n\t<mvc:annotation-driven>\n\t\t<mvc:argument-resolvers>\n\t\t\t<b:bean class=\"org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver\">\n\t\t\t\t<b:property name=\"securityContextHolderStrategy\" ref=\"ref\"/>\n\t\t\t</b:bean>\n\t\t</mvc:argument-resolvers>\n\t</mvc:annotation-driven>\n\n\t<b:bean class=\"org.springframework.security.config.http.MiscHttpConfigTests.AuthenticationController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<x509 subject-principal-regex=\"${subject_principal_regex:(.*)}\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:p=\"http://www.springframework.org/schema/p\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<x509 principal-extractor-ref=\"principalExtractor\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<user-service id=\"us\">\n\t\t<user name=\"luke@monkeymachine\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n\n\t<b:bean name=\"principalExtractor\" class=\"org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor\"\n\t\tp:extractPrincipalNameFromEmail=\"true\"/>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRefAndSubjectPrincipalRegex.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:p=\"http://www.springframework.org/schema/p\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<x509 principal-extractor-ref=\"principalExtractor\" subject-principal-regex=\"(.*)\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<user-service id=\"us\">\n\t\t<user name=\"luke@monkeymachine\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t</user-service>\n\n\t<b:bean name=\"principalExtractor\" class=\"org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor\"\n\t\tp:extractPrincipalNameFromEmail=\"true\"/>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509WithSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http security-context-holder-strategy-ref=\"ref\">\n\t\t<x509 subject-principal-regex=\"${subject_principal_regex:(.*)}\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-certificate.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDfTCCAmWgAwIBAgIJAISE1BLq+ZUNMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMQ0wCwYDVQQIDARVdGFoMRcwFQYDVQQHDA5TYWx0IExha2UgQ2l0eTEP\nMA0GA1UECgwGU3ByaW5nMQ0wCwYDVQQLDAR1c2VyMB4XDTE4MTAwNjAwMjMyMVoX\nDTQ2MDIyMTAwMjMyMVowVTELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFV0YWgxFzAV\nBgNVBAcMDlNhbHQgTGFrZSBDaXR5MQ8wDQYDVQQKDAZTcHJpbmcxDTALBgNVBAsM\nBHVzZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1pXMFNiDXHWkl\n59GMRBW7RsOyqpMzU5PqgLc8josbty4wjvxlM9td00+s94fS/S9a6m/thJi8E4pb\nMwQgckHhaUyiAYW1N3nv3Tj/3+/vhXGKoclTt2NNgTj8eFNx0x20+q0H8nyx0tJu\n7I5rFLWXf3uaOg3DPWGxxLWwN8GNDKMeusjx7/mMpoYnxYRWhcekUHxrLY4gsE6E\nrxJqa0DjmPAeaVMqBsXKBb0JKbyVI4P9t+tIGoNX0hWtudr/R14b+rnAugkfBOUH\n3GgKXS5RjtwIJsRy7RhzoZBISuNQuZsS2N3kJp6lkmveN+trr6HX0COZA5JNs8FH\n26+NVjYpAgMBAAGjUDBOMB0GA1UdDgQWBBSV8HXGF2favXivbmNoqPNDvMiwEDAf\nBgNVHSMEGDAWgBSV8HXGF2favXivbmNoqPNDvMiwEDAMBgNVHRMEBTADAQH/MA0G\nCSqGSIb3DQEBCwUAA4IBAQCMq2XZR2q7IcKyt6lRD+OabprBQapiwmVokovf9s23\nlVD2twEpSW27AIa3V2SpZau1lqy7Rk/KepBKfaXa2FJcxhqLtyaPwDxzYichrl4e\n0zbrpE7sHnTxPtAaefpWZog6Q+HQ4KrJcBsCKwAaol+COzrLebWa9oIn6o1MYmRj\ncNWM+uClUDmjoIRuwPCnEWHadxUcoKxnKiAmisOuer49DsN4dsskaT3Bc1MXrtqf\n5+ZzdHjFKAtLMBPikdj6zG4B6r/1Ytdm/dQv56WWbs2L0iz0BRA5ul8bx94ZZeYb\noT/9kQxvDGRTykKR5MrQDZXW9Hsg/pQnp0f2aNNhMCw9\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-controllers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<mvc:annotation-driven>\n\t\t<mvc:argument-resolvers>\n\t\t\t<b:bean class=\"org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver\"/>\n\t\t</mvc:argument-resolvers>\n\t</mvc:annotation-driven>\n\n\t<b:bean class=\"org.springframework.security.config.http.MiscHttpConfigTests.BasicController\"/>\n\t<b:bean class=\"org.springframework.security.config.http.MiscHttpConfigTests.AuthenticationController\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MultiHttpBlockConfigTests-DistinctHttpElements.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\t\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http pattern=\"/first/**\" create-session=\"stateless\">\n\t\t<intercept-url pattern=\"/first/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<http pattern=\"/second/**\">\n\t\t<intercept-url pattern=\"/second/**\" access=\"authenticated\"/>\n\t\t<form-login login-processing-url=\"/second/login\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\" class=\"org.springframework.security.config.http.MultiHttpBlockConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MultiHttpBlockConfigTests-IdenticalHttpElements.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\t\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http create-session=\"stateless\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<form-login/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MultiHttpBlockConfigTests-IdenticallyPatternedHttpElements.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\t\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http pattern=\"/first/**\" create-session=\"stateless\">\n\t\t<intercept-url pattern=\"/first/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<http pattern=\"/first/**\">\n\t\t<intercept-url pattern=\"/first/**\" access=\"authenticated\"/>\n\t\t<form-login/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/MultiHttpBlockConfigTests-Sec1937.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\t\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http authentication-manager-ref=\"firstAuthenticationManager\" pattern=\"/first/**\" create-session=\"stateless\">\n\t\t<intercept-url pattern=\"/first/**\" access=\"authenticated\"/>\n\t\t<http-basic/>\n\t</http>\n\n\t<http authentication-manager-ref=\"secondAuthenticationManager\" pattern=\"/second/**\">\n\t\t<intercept-url pattern=\"/second/**\" access=\"authenticated\"/>\n\t\t<form-login login-processing-url=\"/second/login\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\" class=\"org.springframework.security.config.http.MultiHttpBlockConfigTests.BasicController\"/>\n\n\t<authentication-manager id=\"firstAuthenticationManager\">\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"first\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n\t<authentication-manager id=\"secondAuthenticationManager\">\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"second\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-clients.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n\n\t<b:bean class=\"org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\t\t\tfactory-method=\"getClientRegistrations\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"refreshTokenAccessTokenResponseClient\"/>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"clientCredentialsAccessTokenResponseClient\"/>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"jwtBearerAccessTokenResponseClient\"/>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"tokenExchangeAccessTokenResponseClient\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-minimal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n\n\t<b:bean class=\"org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\t\t\tfactory-method=\"getClientRegistrations\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-providers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n\n\t<b:bean class=\"org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\t\t\tfactory-method=\"getClientRegistrations\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"authorizationCode\"/>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"refreshToken\"/>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"clientCredentials\"/>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"jwtBearer\"/>\n\n\t<b:bean class=\"org.springframework.security.config.http.OAuth2AuthorizedClientManagerRegistrarTests\"\n\t\t\tfactory-method=\"tokenExchange\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client authorized-client-repository-ref=\"authorizedClientRepository\" />\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<mvc:annotation-driven />\n\n\t<client-registrations>\n\t\t<client-registration registration-id=\"google\"\n\t\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t\t redirect-uri=\"http://localhost/callback/google\"\n\t\t\t\t\t\t\t scope=\"scope1,scope2\"\n\t\t\t\t\t\t\t provider-id=\"google\"/>\n\t</client-registrations>\n\n\t<b:bean id=\"authorizedClientRepository\" class=\"org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository\"/>\n\n\t<b:bean name=\"authorizedClientController\" class=\"org.springframework.security.config.http.OAuth2ClientBeanDefinitionParserTests.AuthorizedClientController\" />\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomAuthorizationRedirectStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client>\n\t\t\t<authorization-code-grant\n\t\t\t\t\tauthorization-redirect-strategy-ref=\"authorizationRedirectStrategy\"/>\n\t\t</oauth2-client>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean id=\"authorizationRedirectStrategy\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.RedirectStrategy\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<client-registrations>\n\t\t<client-registration registration-id=\"google\"\n\t\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t\t redirect-uri=\"http://localhost/callback/google\"\n\t\t\t\t\t\t\t scope=\"scope1,scope2\"\n\t\t\t\t\t\t\t provider-id=\"google\"/>\n\t</client-registrations>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomAuthorizedClientService.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client\n\t\t\t\tauthorized-client-service-ref=\"authorizedClientService\">\n\t\t\t<authorization-code-grant\n\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t\tauthorization-request-resolver-ref=\"authorizationRequestResolver\"\n\t\t\t\t\taccess-token-response-client-ref=\"accessTokenResponseClient\"/>\n\t\t</oauth2-client>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<client-registrations>\n\t\t<client-registration registration-id=\"google\"\n\t\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t\t redirect-uri=\"http://localhost/callback/google\"\n\t\t\t\t\t\t\t scope=\"scope1,scope2\"\n\t\t\t\t\t\t\t provider-id=\"google\"/>\n\t</client-registrations>\n\n\t<b:bean id=\"authorizedClientService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.OAuth2AuthorizedClientService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomClientRegistrationRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client client-registration-repository-ref=\"clientRegistrationRepository\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<b:bean id=\"clientRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomConfiguration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client\n\t\t\t\tauthorized-client-repository-ref=\"authorizedClientRepository\">\n\t\t\t<authorization-code-grant\n\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t\tauthorization-request-resolver-ref=\"authorizationRequestResolver\"\n\t\t\t\t\taccess-token-response-client-ref=\"accessTokenResponseClient\"/>\n\t\t</oauth2-client>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<client-registrations>\n\t\t<client-registration registration-id=\"google\"\n\t\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t\t redirect-uri=\"http://localhost/callback/google\"\n\t\t\t\t\t\t\t scope=\"scope1,scope2\"\n\t\t\t\t\t\t\t provider-id=\"google\"/>\n\t</client-registrations>\n\n\t<b:bean id=\"authorizedClientRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-Minimal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<oauth2-client/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t</http>\n\n\t<client-registrations>\n\t\t<client-registration registration-id=\"google\"\n\t\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t\t redirect-uri=\"http://localhost/callback/google\"\n\t\t\t\t\t\t\t scope=\"scope1,scope2\"\n\t\t\t\t\t\t\t provider-id=\"google\"/>\n\t</client-registrations>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login authorized-client-repository-ref=\"authorizedClientRepository\" />\n\t</http>\n\n\t<mvc:annotation-driven />\n\n\t<b:bean id=\"authorizedClientRepository\" class=\"org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository\"/>\n\n\t<b:bean name=\"authorizedClientController\" class=\"org.springframework.security.config.http.OAuth2LoginBeanDefinitionParserTests.AuthorizedClientController\" />\n\n\t<b:import resource=\"../oauth2/client/google-github-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login access-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t\tuser-service-ref=\"oauth2UserService\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t\t\tauthentication-success-handler-ref=\"authenticationSuccessHandler\"/>\n\t</http>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oauth2UserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.userinfo.OAuth2UserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationSuccessListener\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.context.ApplicationListener\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationSuccessHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationSuccessHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../oauth2/client/google-github-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login access-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t\tuser-service-ref=\"oauth2UserService\"\n\t\t\t\t\t  \toidc-user-service-ref=\"oidcUserService\"\n\t\t\t\t\t\tuser-authorities-mapper-ref=\"userAuthoritiesMapper\"\n\t\t\t\t\t\tjwt-decoder-factory-ref=\"jwtDecoderFactory\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t\t\tauthentication-success-handler-ref=\"authenticationSuccessHandler\"/>\n\t</http>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oauth2UserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.userinfo.OAuth2UserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oidcUserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t<b:constructor-arg value=\"org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"userAuthoritiesMapper\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationSuccessHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationSuccessHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"jwtDecoderFactory\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.jwt.JwtDecoderFactory\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../oauth2/client/google-github-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login access-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t\tuser-service-ref=\"oauth2UserService\"\n\t\t\t\t\t\tlogin-processing-url=\"/login/oauth2/*\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t\t\tauthentication-success-handler-ref=\"authenticationSuccessHandler\" />\n\t</http>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oauth2UserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.userinfo.OAuth2UserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationSuccessHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationSuccessHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../oauth2/client/google-github-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login/>\n\t</http>\n\n\t<b:import resource=\"../oauth2/client/google-github-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login authentication-failure-handler-ref=\"authenticationFailureHandler\"/>\n\t</http>\n\n\t<b:bean id=\"authenticationFailureHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationFailureHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../oauth2/client/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRedirectStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login authorization-redirect-strategy-ref=\"authorizationRedirectStrategy\"/>\n\t</http>\n\n\t<b:bean id=\"authorizationRedirectStrategy\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.RedirectStrategy\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../oauth2/client/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login authorization-request-resolver-ref=\"authorizationRequestResolver\"/>\n\t</http>\n\n\t<b:bean id=\"authorizationRequestResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../oauth2/client/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomLoginPage.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login  login-page=\"/custom-login\"/>\n\t</http>\n\n\t<b:import resource=\"../oauth2/client/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithFormLogin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login/>\n\t\t<form-login/>\n\t</http>\n\n\t<b:import resource=\"../oauth2/client/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login access-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t\tjwt-decoder-factory-ref=\"jwtDecoderFactory\"\n\t\t\t\t\t\tuser-authorities-mapper-ref=\"userAuthoritiesMapper\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t\t\tauthentication-success-handler-ref=\"authenticationSuccessHandler\"/>\n\t</http>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"jwtDecoderFactory\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.jwt.JwtDecoderFactory\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"userAuthoritiesMapper\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationSuccessHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationSuccessHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../oauth2/client/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login access-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t  \toidc-user-service-ref=\"oidcUserService\"\n\t\t\t\t\t\tjwt-decoder-factory-ref=\"jwtDecoderFactory\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"/>\n\t\t<request-cache ref=\"requestCache\" />\n\t</http>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oidcUserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t<b:constructor-arg value=\"org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"jwtDecoderFactory\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.jwt.JwtDecoderFactory\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"requestCache\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.savedrequest.RequestCache\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../oauth2/client/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login/>\n\t\t<request-cache ref=\"requestCache\" />\n\t</http>\n\n\t<client-registrations>\n\t\t<client-registration registration-id=\"google-login\"\n\t\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t\t client-authentication-method=\"client_secret_basic\"\n\t\t\t\t\t\t\t authorization-grant-type=\"authorization_code\"\n\t\t\t\t\t\t\t redirect-uri=\"{baseUrl}/login/oauth2/code/{registrationId}\"\n\t\t\t\t\t\t\t scope=\"openid,profile,email\"\n\t\t\t\t\t\t\t client-name=\"Google\"\n\t\t\t\t\t\t\t provider-id=\"google\"/>\n\t\t<provider provider-id=\"google\"\n\t\t\t\t  authorization-uri=\"https://accounts.google.com/o/oauth2/v2/auth\"\n\t\t\t\t  token-uri=\"https://www.googleapis.com/oauth2/v4/token\"\n\t\t\t\t  user-info-uri=\"https://www.googleapis.com/oauth2/v3/userinfo\"\n\t\t\t\t  user-info-authentication-method=\"header\"\n\t\t\t\t  user-info-user-name-attribute=\"sub\"\n\t\t\t\t  jwk-set-uri=\"https://www.googleapis.com/oauth2/v3/certs\"/>\n\t</client-registrations>\n\n\t<b:bean id=\"requestCache\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.savedrequest.RequestCache\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login client-registration-repository-ref=\"clientRegistrationRepository\"\n\t\t\t\t\t\tauthorized-client-repository-ref=\"authorizedClientRepository\"\n\t\t\t\t\t\taccess-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t\tuser-service-ref=\"oauth2UserService\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"/>\n\t</http>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oauth2UserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.userinfo.OAuth2UserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"clientRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizedClientRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login client-registration-repository-ref=\"clientRegistrationRepository\"\n\t\t\t\t\t\taccess-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t\tuser-service-ref=\"oauth2UserService\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t\t\tauthorized-client-service-ref=\"authorizedClientService\"/>\n\t</http>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oauth2UserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.userinfo.OAuth2UserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"clientRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizedClientService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.OAuth2AuthorizedClientService\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login client-registration-repository-ref=\"clientRegistrationRepository\"\n\t\t\t\t\t\taccess-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t\tuser-service-ref=\"oauth2UserService\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"/>\n\t</http>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oauth2UserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.userinfo.OAuth2UserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"clientRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" security-context-holder-strategy-ref=\"ref\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-login client-registration-repository-ref=\"clientRegistrationRepository\"\n\t\t\t\t\t\taccess-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t\t\tuser-service-ref=\"oauth2UserService\"\n\t\t\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t\t\tauthorized-client-service-ref=\"authorizedClientService\"/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean id=\"accessTokenResponseClient\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"oauth2UserService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.userinfo.OAuth2UserService\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.web.AuthorizationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"clientRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authorizedClientService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.oauth2.client.OAuth2AuthorizedClientService\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AccessDeniedHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"accessDeniedHandler\"\n\t\t\tclass=\"org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler\">\n\t\t<b:property name=\"realmName\" value=\"myRealm\"/>\n\t</b:bean>\n\n\t<http use-expressions=\"true\">\n\t\t<access-denied-handler ref=\"accessDeniedHandler\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"hasAuthority('SCOPE_read')\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Active.json",
    "content": "{\n  \"active\" : true,\n  \"sub\": \"test-subject\",\n  \"scope\": \"message:read\",\n  \"exp\": 4683883211\n}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ActiveNoScopes.json",
    "content": "{\n  \"active\" : true,\n  \"sub\": \"test-subject\",\n  \"exp\": 4683883211\n}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AllowBearerTokenInBody.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean id=\"bearerTokenResolver\"\n\t\t  class=\"org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver\">\n\t\t<b:property name=\"allowFormEncodedBodyParameter\" value=\"true\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server bearer-token-resolver-ref=\"bearerTokenResolver\">\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AllowBearerTokenInQuery.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean id=\"bearerTokenResolver\"\n\t\t  class=\"org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver\">\n\t\t<b:property name=\"allowUriQueryParameter\" value=\"true\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server bearer-token-resolver-ref=\"bearerTokenResolver\">\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AlwaysSessionCreation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http create-session=\"always\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationConverter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server authentication-converter-ref=\"authenticationConverter\">\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationConverterAndBearerTokenResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server authentication-converter-ref=\"authenticationConverter\" bearer-token-resolver-ref=\"bearerTokenResolver\">\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationEntryPoint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"authenticationEntryPoint\"\n\t\t\tclass=\"org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint\">\n\t\t<b:property name=\"realmName\" value=\"myRealm\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server entry-point-ref=\"authenticationEntryPoint\">\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"authenticationManagerResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.authentication.AuthenticationManagerResolver\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<oauth2-resource-server authentication-manager-resolver-ref=\"authenticationManagerResolver\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolverPlusOtherConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"authenticationManagerResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.authentication.AuthenticationManagerResolver\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\t<b:bean name=\"decoder\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.oauth2.jwt.JwtDecoder\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<oauth2-resource-server authentication-manager-resolver-ref=\"authenticationManagerResolver\">\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-BasicAndResourceServer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<http-basic/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-BearerTokenResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server bearer-token-resolver-ref=\"bearerTokenResolver\">\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Default.jwks",
    "content": "{\"keys\":[{\"p\":\"49neceJFs8R6n7WamRGy45F5Tv0YM-R2ODK3eSBUSLOSH2tAqjEVKOkLE5fiNA3ygqq15NcKRadB2pTVf-Yb5ZIBuKzko8bzYIkIqYhSh_FAdEEr0vHF5fq_yWSvc6swsOJGqvBEtuqtJY027u-G2gAQasCQdhyejer68zsTn8M\",\"kty\":\"RSA\",\"q\":\"tWR-ysspjZ73B6p2vVRVyHwP3KQWL5KEQcdgcmMOE_P_cPs98vZJfLhxobXVmvzuEWBpRSiqiuyKlQnpstKt94Cy77iO8m8ISfF3C9VyLWXi9HUGAJb99irWABFl3sNDff5K2ODQ8CmuXLYM25OwN3ikbrhEJozlXg_NJFSGD4E\",\"d\":\"FkZHYZlw5KSoqQ1i2RA2kCUygSUOf1OqMt3uomtXuUmqKBm_bY7PCOhmwbvbn4xZYEeHuTR8Xix-0KpHe3NKyWrtRjkq1T_un49_1LLVUhJ0dL-9_x0xRquVjhl_XrsRXaGMEHs8G9pLTvXQ1uST585gxIfmCe0sxPZLvwoic-bXf64UZ9BGRV3lFexWJQqCZp2S21HfoU7wiz6kfLRNi-K4xiVNB1gswm_8o5lRuY7zB9bRARQ3TS2G4eW7p5sxT3CgsGiQD3_wPugU8iDplqAjgJ5ofNJXZezoj0t6JMB_qOpbrmAM1EnomIPebSLW7Ky9SugEd6KMdL5lW6AuAQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"wdkFu_tV2V1l_PWUUimG516Zvhqk2SWDw1F7uNDD-Lvrv_WNRIJVzuffZ8WYiPy8VvYQPJUrT2EXL8P0ocqwlaSTuXctrORcbjwgxDQDLsiZE0C23HYzgi0cofbScsJdhcBg7d07LAf7cdJWG0YVl1FkMCsxUlZ2wTwHfKWf-v4\",\"dp\":\"uwnPxqC-IxG4r33-SIT02kZC1IqC4aY7PWq0nePiDEQMQWpjjNH50rlq9EyLzbtdRdIouo-jyQXB01K15-XXJJ60dwrGLYNVqfsTd0eGqD1scYJGHUWG9IDgCsxyEnuG3s0AwbW2UolWVSsU2xMZGb9PurIUZECeD1XDZwMp2s0\",\"dq\":\"hra786AunB8TF35h8PpROzPoE9VJJMuLrc6Esm8eZXMwopf0yhxfN2FEAvUoTpLJu93-UH6DKenCgi16gnQ0_zt1qNNIVoRfg4rw_rjmsxCYHTVL3-RDeC8X_7TsEySxW0EgFTHh-nr6I6CQrAJjPM88T35KHtdFATZ7BCBB8AE\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Empty.jwks",
    "content": "{\"keys\":[]}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Expired.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE1MzAyMzE3MTB9.c8vXYFwe1cBuglaZbmZFXJOmLsu_IQf-OsOiiOGhEJYOzu6h6v_qEzf2xxbu5TSvwAERmDITUSK41UIIvgU75WebtgilNnTR83B_gPM-7_FI2FLzlgVH7WayzvbYTQqepE_ZUMLFkGkK4r-dRiOyB9_cfl6jq_b5hE_biH1qrgPQrjlEhU8YxeK2EE05wsARLzyjoIYifkStjPC6rC-MLFIVk5JoITNzkTh7zYYSWtKWEgwd8S_vluVtJaPk-yKPb4tXcFRzCFl_qd7aCF8_LHyhw-4wvhWRIi8DmQmRU_a1RxR0mi-UCp0jMwmBZxxkSdqJ4l_EHI1yVqpgnbMLDw\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ExpiredJwtClockSkew.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"clock\" class=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests.ClockFactoryBean\">\n\t\t<b:property name=\"millis\" value=\"4687181595000\"/>\n\t</b:bean>\n\n\t<b:bean name=\"jwtValidator\" class=\"org.springframework.security.oauth2.jwt.JwtTimestampValidator\">\n\t\t<b:constructor-arg value=\"#{T(java.time.Duration).ofHours(1)}\"/>\n\t\t<b:property name=\"clock\" ref=\"clock\"/>\n\t</b:bean>\n\n\t<b:bean name=\"rest\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.web.client.RestOperations\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean name=\"decoder\"\n\t\t\tclass=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests$JwtDecoderFactoryBean\">\n\t\t<b:property name=\"jwtValidator\" ref=\"jwtValidator\"/>\n\t\t<b:property name=\"rest\" ref=\"rest\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ExpiresAt4687177990.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjQ2ODcxNzc5OTB9.RRQvqIZzLweq0iwWUZk1Dpiz6iUmT4bAVhGWqvWNWK3UwJ6aBIYsCRhdVeKQp-g1TxXovMALeAu_2oPmV0wOEEanesAKxjKYcJZQIe8HnVqgug6Ibs04uQ1mJ4RgfntPM-ebsJs-2tjFFkLEYJSkpq2o6SEFW9jBJyW8b8C5UJJahqynonA-Dw5GH1nin5bhhliLuFOmu0Ityt0uJ1Y_vuGsSA-ltVcY52jE4x6GH9NQxLX4ceO1bHSOmdspBoGsE_yo9-zsQw0g1_Iy7uqEjos3xrrboH6Z_u7pRL7AQJ7GNzZlinjYYPANQbYknieZD6beddTK7lvr4DYiPBmXzA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-FormAndResourceServer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<form-login/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Inactive.json",
    "content": "{\n  \"active\" : false\n}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-IntrospectionUri.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:c=\"http://www.springframework.org/schema/context\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd\">\n\n\t<b:bean name=\"web\" class=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests.MockWebServerFactoryBean\"/>\n\t<b:bean name=\"webProperties\" class=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests.MockWebServerPropertiesFactoryBean\">\n\t\t<b:constructor-arg ref=\"web\"/>\n\t</b:bean>\n\t<c:property-placeholder properties-ref=\"webProperties\" local-override=\"true\"/>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<opaque-token client-id=\"client\" client-secret=\"secret\"\n\t\t\t\t\t\t  introspection-uri=\"${introspection-uri:https://idp.example.org}\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwkSetUri.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:c=\"http://www.springframework.org/schema/context\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd\">\n\n\t<c:property-placeholder local-override=\"true\"/>\n\n\t<b:bean id=\"bearerTokenResolver\"\n\t\t\tclass=\"org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver\">\n\t\t<b:property name=\"allowUriQueryParameter\" value=\"true\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<oauth2-resource-server bearer-token-resolver-ref=\"bearerTokenResolver\">\n\t\t\t<jwt jwk-set-uri=\"${jwk-set-uri:https://idp.example.org}\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Jwt.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\n\n\t<http>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtAuthenticationConverter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt decoder-ref=\"decoder\" jwt-authentication-converter-ref=\"jwtAuthenticationConverter\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtCustomSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\n\n\t<http security-context-holder-strategy-ref=\"ref\">\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtDecoderAndJwkSetUri.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:c=\"http://www.springframework.org/schema/context\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd\">\n\n\t<c:component-scan base-package=\"org.springframework.security.config.http\"/>\n\t<c:property-placeholder local-override=\"true\"/>\n\t<b:bean name=\"decoder\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.oauth2.jwt.JwtDecoder\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt jwk-set-uri=\"${jwk-set-uri:https://idp.example.org}\" decoder-ref=\"decoder\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtHalfConfigured.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<jwt/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtRestOperations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"rest\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.web.client.RestOperations\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean name=\"decoder\"\n\t\t\tclass=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests$JwtDecoderFactoryBean\">\n\t\t<b:property name=\"rest\" ref=\"rest\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Jwtless.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<oauth2-resource-server/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Kid.token",
    "content": "eyJraWQiOiJvbmUiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjQ2ODM4ODM1NzJ9.UhukjNEowC5lLCccvdjCUJad5J9FGNModegMZGe9qKIbXxmfseTttZUNn3_K_6aNCfimtmRktCRbw3fUTcje2TFJOJ6SmomLcQyjq7S41Wq6oBSA2fdqOOU4vNvrk8_pSExsSyN9bfWiJ51I8Agzbq5eUDNo_HEpaJZimrIe9f2_njU1GxvAWsq_h4UhHEgPPb3kY9kN9hVYX_oShhh7JxbLJBnfsKBOKGEWOsE65GlmDgQV4om6RGjJaz6jFHKJTCpH08ADA3j2dqT0LNy4PrUmbnjPjWVtSQJkGcgUkcQW6qz0K86ZfJZZng_iB2VadRm5qO-99ySKmlxa5A-_Iw\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MalformedPayload.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJuYmYiOnt9LCJleHAiOjQ2ODM4OTEyMTl9.kpdv6ZXyYszZUzA4mJpviCBPzPftk6tIbIn5OoMuM09MKZCUCAFD8Y1tDmjzbWdkR_5CYiFMvSLq6DzAlugtGRAShc93dmDlyZmhcct2G477FxWaRKbtmFDjzuCjGyn7xHWpS7Wz6-Ngb-JyGI2m7FxXCgCpiYYBl-4-ONTuAT0fArJi_voA8K6YLnnjEjEprI3wsQRoS3Twa_fVdGkpMNlOGsQOqmlfjDrXpyfiANOe_ZztHxbDtJEZ9zfELxx9fzkZgTL1fD2Sj6HueDU-tMt-6IaGpBCLsg7d85RK001-U9u3Ph9awQC4QZK-8-F9OUUCY5RNcRJ57KEh9PjUfA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockAuthenticationConverter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"authenticationConverter\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationConverter\" type=\"java.lang.Class\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockBearerTokenResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"bearerTokenResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.oauth2.server.resource.web.BearerTokenResolver\" type=\"java.lang.Class\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwkSetUri.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:c=\"http://www.springframework.org/schema/context\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd\">\n\n\t<b:bean name=\"web\" class=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests.MockWebServerFactoryBean\"/>\n\t<b:bean name=\"webProperties\" class=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests.MockWebServerPropertiesFactoryBean\">\n\t\t<b:constructor-arg ref=\"web\"/>\n\t</b:bean>\n\t<c:property-placeholder properties-ref=\"webProperties\" local-override=\"true\"/>\n</b:beans>\n\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtAuthenticationConverter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"jwtAuthenticationConverter\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.core.convert.converter.Converter\" type=\"java.lang.Class\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtDecoder.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"decoder\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.oauth2.jwt.JwtDecoder\" type=\"java.lang.Class\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtValidator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"jwtValidator\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.oauth2.core.OAuth2TokenValidator\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean name=\"rest\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.web.client.RestOperations\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean name=\"decoder\"\n\t\t\tclass=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests$JwtDecoderFactoryBean\">\n\t\t<b:property name=\"jwtValidator\" ref=\"jwtValidator\"/>\n\t\t<b:property name=\"rest\" ref=\"rest\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockOpaqueTokenIntrospector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"introspector\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector\" type=\"java.lang.Class\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MultipleIssuers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"authenticationManagerResolver\"\n\t\t\tclass=\"org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver\"\n\t\t\tfactory-method=\"fromTrustedIssuers\">\n\t\t<b:constructor-arg type=\"java.lang.String[]\">\n\t\t\t<b:list>\n\t\t\t\t<b:value>${issuer-one}</b:value>\n\t\t\t\t<b:value>${issuer-two}</b:value>\n\t\t\t</b:list>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<oauth2-resource-server authentication-manager-resolver-ref=\"authenticationManagerResolver\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueToken.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<opaque-token introspector-ref=\"introspector\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndAuthenticationConverter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"authentication-converter\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/requires-read-scope\" access=\"hasAuthority('SCOPE_message:read')\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<opaque-token introspector-ref=\"introspector\" authentication-converter-ref=\"authentication-converter\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndIntrospectionUri.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"introspector\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<opaque-token introspector-ref=\"introspector\" introspection-uri=\"https://idp.example.org\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenHalfConfigured.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<oauth2-resource-server>\n\t\t\t<opaque-token introspection-uri=\"https://idp.example.org\"/>\n\t\t</oauth2-resource-server>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenRestOperations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"rest\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.web.client.RestOperations\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean name=\"introspector\"\n\t\t\tclass=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests$OpaqueTokenIntrospectorFactoryBean\">\n\t\t<b:property name=\"rest\" ref=\"rest\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-SingleKey.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoXJ8OyOv/eRnce4akdan\nR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2\nUrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48O\ncz06PGF3lhbz4t5UEZtdF4rIe7u+977QwHuh7yRPBQ3sII+cVoOUMgaXB9SHcGF2\niZCtPzL/IffDUcfhLQteGebhW8A6eUHgpD5A1PQ+JCw/G7UOzZAjjDjtNM2eqm8j\n+Ms/gqnm4MiCZ4E+9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1Hu\nQwIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-SingleKey.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean class=\"org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor\"/>\n\n\t<b:bean name=\"decoder\"\n\t\t\tclass=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests$JwtDecoderFactoryBean\">\n\t\t<b:property name=\"key\" value=\"classpath:org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-SingleKey.pub\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-TooEarly.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJuYmYiOjQ2ODM4OTI2NTUsImV4cCI6NDY4Mzg5MjY1NX0.MIaECJrmYjAByKNJoWHlP5ewg2xiW7GIxL8Vepp3ZIKf_jjM2OSMQlAWGmfD3Kf3bfesvSI7glw5qg_ZIv4FdIPaTvnmLRjWQkpk-QiLTJr_HM2wWeNbUJ1zciGWQlWAvabtQuyeGt1dsfQq53QLVNpvuioYdVg-gz_76uwDTxCKQU_99ksQhMMJsYJVDA_-uWGTzBANszcZykqwWFMaoXF4lkVPK4U68n18ISBB761wFusUCtyGWzwevX7wBAEJxcRy6ZVk3h7GyxZBsbRAd5fPn3dPMxNvL_CEp5jUYSAH-arAdDkvAph5Vk1yXof7FFRcffJpAy76HC66hR2JQA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-TwoKeys.jwks",
    "content": "{\"keys\":[{\"p\":\"49neceJFs8R6n7WamRGy45F5Tv0YM-R2ODK3eSBUSLOSH2tAqjEVKOkLE5fiNA3ygqq15NcKRadB2pTVf-Yb5ZIBuKzko8bzYIkIqYhSh_FAdEEr0vHF5fq_yWSvc6swsOJGqvBEtuqtJY027u-G2gAQasCQdhyejer68zsTn8M\",\"kty\":\"RSA\",\"q\":\"tWR-ysspjZ73B6p2vVRVyHwP3KQWL5KEQcdgcmMOE_P_cPs98vZJfLhxobXVmvzuEWBpRSiqiuyKlQnpstKt94Cy77iO8m8ISfF3C9VyLWXi9HUGAJb99irWABFl3sNDff5K2ODQ8CmuXLYM25OwN3ikbrhEJozlXg_NJFSGD4E\",\"d\":\"FkZHYZlw5KSoqQ1i2RA2kCUygSUOf1OqMt3uomtXuUmqKBm_bY7PCOhmwbvbn4xZYEeHuTR8Xix-0KpHe3NKyWrtRjkq1T_un49_1LLVUhJ0dL-9_x0xRquVjhl_XrsRXaGMEHs8G9pLTvXQ1uST585gxIfmCe0sxPZLvwoic-bXf64UZ9BGRV3lFexWJQqCZp2S21HfoU7wiz6kfLRNi-K4xiVNB1gswm_8o5lRuY7zB9bRARQ3TS2G4eW7p5sxT3CgsGiQD3_wPugU8iDplqAjgJ5ofNJXZezoj0t6JMB_qOpbrmAM1EnomIPebSLW7Ky9SugEd6KMdL5lW6AuAQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"wdkFu_tV2V1l_PWUUimG516Zvhqk2SWDw1F7uNDD-Lvrv_WNRIJVzuffZ8WYiPy8VvYQPJUrT2EXL8P0ocqwlaSTuXctrORcbjwgxDQDLsiZE0C23HYzgi0cofbScsJdhcBg7d07LAf7cdJWG0YVl1FkMCsxUlZ2wTwHfKWf-v4\",\"dp\":\"uwnPxqC-IxG4r33-SIT02kZC1IqC4aY7PWq0nePiDEQMQWpjjNH50rlq9EyLzbtdRdIouo-jyQXB01K15-XXJJ60dwrGLYNVqfsTd0eGqD1scYJGHUWG9IDgCsxyEnuG3s0AwbW2UolWVSsU2xMZGb9PurIUZECeD1XDZwMp2s0\",\"dq\":\"hra786AunB8TF35h8PpROzPoE9VJJMuLrc6Esm8eZXMwopf0yhxfN2FEAvUoTpLJu93-UH6DKenCgi16gnQ0_zt1qNNIVoRfg4rw_rjmsxCYHTVL3-RDeC8X_7TsEySxW0EgFTHh-nr6I6CQrAJjPM88T35KHtdFATZ7BCBB8AE\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"},{\"p\":\"_CI5g5In9T4ZgakV1i62UU6yjorEr5t2URHfRYqxN7S4aKsQOzggcPoqa78xRj8PAPuf3P0ArPEAHdS6bFK7RLrFXdvyEmSNTJa1gcLCf2Zmep8bsrhrCvh6seZNvfrSMV0ULmk0B75Fs8mqE7nwcIbPtBYkinlSIw-sKRv62DM\",\"kty\":\"RSA\",\"q\":\"pqfexT3HBAagH-iydGsWbjG6CcYyvSQZdFtUu4LIOBCYVA0dvkN9s7uU1eoevHN_ksf-hfrF5AQH0a5P0dIJ2pp1bFa9uo9DJ7khU9sIBk9_o8nST2QLHwPQmGTW8vVlcSF7Vffvzm2fV3cQ3dfI5lvtkqfX_Z3WkF8UjFjADe8\",\"d\":\"FzB5xChO8e89JisxSueY5j1RUBmatIAs_8Z3LUHOw16GlAhBhbSNl-7bXkbcUWLq9M1zTLCD91SSZXBohf9j1ebqWnbjMqQmdkxlQcVRoKcnMJ5YBabCTMBXghQnJetUMh6x6hXRnR1CSBNRdZPf-K2bnxL3xRNRSfY_7bjpb_q5pyUsK66ugSKwuEOUDNf1ttOZi4PBTsxWMDyXi_7fNFjl-B831uWNDVwdY4j68PVwGPT87zjZYjZRTZXB4ILUP11ztw4s3s_bU1Lj0PeZJsA5rmjU1iBzqCNdzgYxNlfV7M62VCkE1Wtd6M97jtysiT-5wQUMxNugoOTc9thc1Q\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"two\",\"qi\":\"bnGriiVGVea9vSaN_48YYTEoKYM1kF7TrCRKERkMWdi4EHF7pZNWBv8arxaLUzElllvtGlVTNwkZlG0gOhXBoLYbcfqVikDklkBxtsuZEBKgvX7zFlDIBlNjh98lcZqDqz7Rqwr-tavxTCq2LNNlK6x-dYL61Agw_LOilYqbSfA\",\"dp\":\"MmT4z-ZnnCn0WSkdlziw8iFjqP_tfhf5lwyWbsTg1PyHG0yNqvh1637k-bI2PA8ghZbFhhr_hpGI7210cXA7w-n8xtzOToTQhS1eS_hMfcBO3VVt6NPZeVDe3S3l_gHi_0DWZsxaPO336o51MwooF6WqYBlI5nCHTUC1rWXNRmc\",\"dq\":\"dd_ybywc4boV87vQzQsZWGOPpG4tYR5xap1WtzHvj8gdFgYY7YQrGr8orIzlpIFE0Hroibcv1PEM3sAd8NhQ4--v8isAEz5VT3lgG0Gm0V_VdfG_8StfulYmakOYzUvIrlXyOIIfebCLrX-nzGFd1aFbzgktelLzejXmAMadQL0\",\"n\":\"pCOHBsaoxlt9-qVE_INhrbkmxm7WqwEeqUBBIgHvm_JzXbmJ4iQzVF5tzAbRayxUmPbZ4E80R5HlIC2CQ7yyweTbIIWIw_TcQzXR4u3twEN1awP4s1n-00Eeurr-s9c_txZQQiDkyrCMYc9vlmsneFfubyoTvg9h_rckd8w34AyE8-wxgBRqUbm1x4ozcVmUJHkaPbQfbhIighl7osoQ4t_wXjAhTN_c9XttVjXlRwqVYPFNYUcC9GoaXWJRHjydHNFeBboOZY3E8ND6DbJ4nVtxydpUQSjTC-N-wQmhKmtYadd2hh2yywvtXpL5Q98XSphrrIHK-GWY0j8kimpunQ\"}]}\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-UnexpiredJwtClockSkew.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean name=\"clock\" class=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests.ClockFactoryBean\">\n\t\t<b:property name=\"millis\" value=\"4687181540000\"/>\n\t</b:bean>\n\n\t<b:bean name=\"jwtValidator\" class=\"org.springframework.security.oauth2.jwt.JwtTimestampValidator\">\n\t\t<b:constructor-arg value=\"#{T(java.time.Duration).ofHours(1)}\"/>\n\t\t<b:property name=\"clock\" ref=\"clock\"/>\n\t</b:bean>\n\n\t<b:bean name=\"rest\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.web.client.RestOperations\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean name=\"decoder\"\n\t\t\tclass=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests$JwtDecoderFactoryBean\">\n\t\t<b:property name=\"jwtValidator\" ref=\"jwtValidator\"/>\n\t\t<b:property name=\"rest\" ref=\"rest\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-Unsigned.token",
    "content": "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJuYmYiOjE1MzAzMDA4MzgsImV4cCI6MjE0NjAwMzE5OSwiaWF0IjoxNTMwMzAwODM4LCJ0eXAiOiJKV1QifQ.\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ValidMessageReadScope.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJzY29wZSI6Im1lc3NhZ2U6cmVhZCIsImV4cCI6NDY4Mzg4MzIxMX0.cM7Eq9H20503czYVy1aVo8MqTQd8YsYGpv_lAV4PKr3y8NgvvosNjCSUs8rrGjQ0Sp3c4iXK6UVXq8pOJVeWXbSZa1IKAsIhiMIcg2xPFM6e71MVdX4bo255Yh8Nuh0p3xxP9isK_iAKNdMuVBOGfe9KATlmp2dOi0OpAjwSmxPJD1A7AC5f62YIe3Yx2gO6mbfANZJWQ7TxlUuCT_D5FEqg2FfYFqlFaluqWd_2X-esIsiDTxa1R9oF5XwgT6tsgvS7iYSiJw_uNKX0yU4eyLzYuIhnN_hVsr4jOZqPlsqCrkEohOGZg_Jir-7tLxZu0PqoH4ejC24FeDtC9xVa0w\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ValidMessageWriteScp.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJzY3AiOlsibWVzc2FnZTp3cml0ZSJdLCJleHAiOjQ2ODM4OTY0OTl9.mxAFzoNjjo-7E4D_XYVme69Y7F-J--q41x6lHDTSOxzVNfQqtJ-U-N4pn7St5jElm9y3mSUxTtmwCnukaVVZkeI8aJjUc8V8nxUAsiZIDvQWjr9uW4xUIcE6MiwC0A9rhY-3I87u6No-KBTxyT80zLnCjtS2XpTId-NSd3vcYmM7Vzn4-8KoR_m-7XrjvrO69HlRrH2uUAXGnr1sn6vLp7YruupqKrHqa0e9pIpN-VRzC8Bx2LQP9mVMlQy4b1hx5MdjOTV3HUSnWiT-93z4rTMOoHScKDwmzFYoS7e00F5hyd4jzbpHdpDKnjLdwPQYz_HCmQ5MV21-Q4Q1jparIg\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ValidNoScopes.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjQ2ODM4Mjg2NzR9.LV_i9lzN_gAB2MUuZHJKm2tOfa3xWq_qfE2lx67eoYJZsY_20Ma98A3Hh2k0wnb_mNn6jfQhXbqvUy1llmQtsx3gMNhN2Axfe3UccSKYEb2Ow5OFlrMFYby1d_D4GfXKUFKq8jyMWVlrjk_XrfJyfzeo0MyZVzURSOXv1Ehbl5-xAS_N72jiAI7cIHlHGm93Hwdk8h7Tkkf_5t2dOMJM0mh0fOT9ou3J2_ngaNDfvlAmBLxHQiJ6JrFH5njqe4lSBTxJocDcgZwGVKd0WvV4W-jwA267tZjssDFmS3xZ9hoDO_M-EjlOiEPuWLd9nQCGJpBJ3z3WeC4qrKYghHTNLA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-WebServer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:c=\"http://www.springframework.org/schema/context\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd\">\n\n\t<b:bean name=\"web\" class=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests.MockWebServerFactoryBean\"/>\n\t<b:bean name=\"webProperties\" class=\"org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParserTests.MockWebServerPropertiesFactoryBean\">\n\t\t<b:constructor-arg ref=\"web\"/>\n\t</b:bean>\n\t<c:property-placeholder properties-ref=\"webProperties\" local-override=\"true\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-WrongAlgorithm.token",
    "content": "eyJhbGciOiJSUzUxMiJ9.eyJleHAiOjQ2ODczNDQ2NzN9.hvVUW_xwUXd7nGm27E5tLTZ21x64YjP0o-TMW6t_bOkfG1Vp1AMEX8fXvSqeG0vK8TWiB2_keOGtH-eFmAGBEYXq1o1zj1BgMHeaZAVio9n-77DkTzQ7CiOF5M1M7B_Ng4K8ra4DpieZZXVjHTWsuOiU1hWoI1tIna8VucAxZln-oh7PkrYmgwFTlsL2Z9aZZYN_X7ECyRQDf3lRrLwr4Go_XpJ5i9F-GT5LvUYa42uggGjvq_frfb0t5wcmPgjtqiE6l2mnrYFjjKTq1nQRYrJ5wFWOHUTRxNsGS8PwrNxzh6JW1ZZTS0n_JIOvSh__w0WAB241QLoKBx4AETMLQA\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-WrongSignature.token",
    "content": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjQ2ODczNDQ0MTd9.jfqDyHvpRXWF6KaRQS3cGT0HUSix09xwTPvUCtg9UJ2QR1Rx4MclGCli3yIHNm0vsRed4s-gZduVGfbj7enyKnpXCZE7dNxZENfm7P54OfJmlyJY3DvhzlyH_rtuOD4c_Q88J9uELd_pghikLlMtu8090UzTtwRfdo_JsDfMRAcDeYq7TTaL60w3AVarStwZAAy_dpi6bTEanm5hwkz4-deA4Bz4KentpvlcwB01IXw9DVYrW1lpzLgycwk_VbCK_LA1hjFnnjc3OnQaxvqydrBAlFD3ziklVAxGnKnrYzppixdwwztuga4XS36OhicIGXEkMf3oT3nzgcR309DP_A\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/PlaceHolderAndELConfigTests-AccessDeniedPage.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/secured\" access=\"ROLE_NUNYA\"/>\n\t\t<access-denied-handler error-page=\"${accessDenied}\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"sc\" class=\"org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/PlaceHolderAndELConfigTests-AccessDeniedPageWithSpEL.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/secured\" access=\"ROLE_NUNYA\"/>\n\t\t<access-denied-handler error-page=\"#{'/go' + '-away'}\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"sc\" class=\"org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/PlaceHolderAndELConfigTests-InterceptUrlAndFormLogin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"${secure.Url}\" access=\"${secure.role}\"/>\n\t\t<form-login\n\t\t\t\tlogin-page=\"${login.page}\"\n\t\t\t\tlogin-processing-url=\"${login.page}\"\n\t\t\t\tdefault-target-url=\"${default.target}\"\n\t\t\t\tauthentication-failure-url=\"${auth.failure}\"/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"unsecured\" class=\"org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/PlaceHolderAndELConfigTests-InterceptUrlAndFormLoginWithSpEL.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url\n\t\t\t\tpattern=\"#{systemProperties['secure.url']}\"\n\t\t\t\taccess=\"#{systemProperties['secure.role']}\"/>\n\n\t\t<form-login\n\t\t\t\tlogin-page=\"#{systemProperties['login.page']}\"\n\t\t\t\tlogin-processing-url=\"#{systemProperties['login.page']}\"\n\t\t\t\tdefault-target-url=\"#{systemProperties['default.target']}\"\n\t\t\t\tauthentication-failure-url=\"#{systemProperties['auth.failure']}\"/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"sc\" class=\"org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/PlaceHolderAndELConfigTests-PortMapping.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/secured\" access=\"ROLE_USER\" requires-channel=\"https\"/>\n\t\t<intercept-url pattern=\"/unsecured\" access=\"ROLE_USER\" requires-channel=\"http\"/>\n\n\t\t<port-mappings>\n\t\t\t<port-mapping http=\"#{systemProperties.http}\" https=\"${https}\"/>\n\t\t</port-mappings>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"sc\" class=\"org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/PlaceHolderAndELConfigTests-RequiresChannel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"${secure.url}\" access=\"ROLE_USER\" requires-channel=\"${required.channel}\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"sc\" class=\"org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/PlaceHolderAndELConfigTests-UnsecuredPattern.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http pattern=\"${pattern.nofilters}\" security=\"none\"/>\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_NUNYA\"/>\n\t</http>\n\n\t<b:bean name=\"propertyPlaceholderConfigurer\" class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\"/>\n\n\t<b:bean name=\"unsecured\" class=\"org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-DefaultConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-NegativeTokenValidity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me\n\t\t\t\tkey=\"ourkey\"\n\t\t\t\ttoken-validity-seconds=\"-1\"/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-NegativeTokenValidityWithDataSource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me\n\t\t\t\tkey=\"ourkey\"\n\t\t\t\ttoken-validity-seconds=\"-1\"\n\t\t\t\tdata-source-ref=\"dataSource\"/>\n\t</http>\n\n\t<b:bean name=\"dataSource\" class=\"org.springframework.security.TestDataSource\">\n\t\t<b:constructor-arg value=\"tokendb\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-NegativeTokenValidityWithPersistentRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me\n\t\t\t\tkey=\"ourkey\"\n\t\t\t\ttoken-validity-seconds=\"-1\"\n\t\t\t\ttoken-repository-ref=\"tokenRepository\"/>\n\t</http>\n\n\t<b:bean name=\"tokenRepository\"\n\t\t\tclass=\"org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-Sec1827.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me\n\t\t\t\tkey=\"ourkey\"\n\t\t\t\tuse-secure-cookie=\"false\"/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-Sec2165.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me\n\t\t\t\tkey=\"ourkey\"\n\t\t\t\ttoken-validity-seconds=\"${security.rememberme.ttl}\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\">\n\t\t<b:property name=\"properties\" value=\"security.rememberme.ttl=30\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-Sec742.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<form-login login-page=\"/login.jsp\" default-target-url=\"/messageList.html\"/>\n\t\t<logout logout-success-url=\"/login.jsp\"/>\n\t\t<anonymous username=\"guest\" granted-authority=\"guest\"/>\n\t\t<remember-me/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-SecureCookie.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me\n\t\t\t\tkey=\"ourkey\"\n\t\t\t\tuse-secure-cookie=\"true\"/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-TokenValidity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me\n\t\t\t\tkey=\"ourkey\"\n\t\t\t\ttoken-validity-seconds=\"10000\"/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithAuthenticationSuccessHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me authentication-success-handler-ref=\"authenticationSuccessHandler\"\n\t\t\t\tdata-source-ref=\"dataSource\"/>\n\t</http>\n\n\t<b:bean name=\"authenticationSuccessHandler\" class=\"org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler\">\n\t\t<b:constructor-arg value=\"/target\"/>\n\t</b:bean>\n\n\t<b:bean name=\"dataSource\" class=\"org.springframework.security.TestDataSource\">\n\t\t<b:constructor-arg value=\"tokendb\"/>\n\t</b:bean>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithDataSource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me data-source-ref=\"dataSource\"/>\n\t</http>\n\n\t<b:bean name=\"dataSource\" class=\"org.springframework.security.TestDataSource\">\n\t\t<b:constructor-arg value=\"tokendb\"/>\n\t</b:bean>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithRememberMeCookie.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me remember-me-cookie=\"custom-remember-me-cookie\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithRememberMeCookieAndServicesRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me remember-me-cookie=\"custom-remember-me-cookie\" services-ref=\"services\"/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithRememberMeParameter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me remember-me-parameter=\"custom-remember-me-parameter\"/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithRememberMeParameterAndServicesRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me remember-me-parameter=\"custom-remember-me-parameter\" services-ref=\"services\"/>\n\t</http>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" security-context-holder-strategy-ref=\"ref\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithServicesRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\n\t\t<!-- SEC-1281 - using key with external services -->\n\t\t<remember-me\n\t\t\t\tkey=\"#{'our' + 'key'}\"\n\t\t\t\tservices-ref=\"services\"/>\n\t</http>\n\n\t<b:bean name=\"services\" class=\"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices\">\n\t\t<b:constructor-arg value=\"ourkey\"/>\n\t\t<b:constructor-arg ref=\"us\"/>\n\t\t<b:property name=\"tokenValiditySeconds\" value=\"5000\"/>\n\t</b:bean>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithTokenRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me token-repository-ref=\"tokenRepository\"/>\n\t</http>\n\n\t<b:bean name=\"jdbcTemplate\" class=\"org.springframework.jdbc.core.JdbcTemplate\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean name=\"dataSource\" class=\"org.springframework.security.TestDataSource\">\n\t\t\t\t<b:constructor-arg value=\"tokendb\"/>\n\t\t\t</b:bean>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean name=\"tokenRepository\"\n\t\t\tclass=\"org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl\">\n\t\t<b:property name=\"createTableOnStartup\" value=\"true\"/>\n\t\t<b:property name=\"jdbcTemplate\" ref=\"jdbcTemplate\"/>\n\t</b:bean>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/RememberMeConfigTests-WithUserDetailsService.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/authenticated\" access=\"authenticated\"/>\n\t\t<remember-me user-service-ref=\"userDetailsService\"/>\n\t</http>\n\n\t<authentication-manager>\n\t\t<authentication-provider user-service-ref=\"userDetailsService\"/>\n\t</authentication-manager>\n\n\t<b:bean name=\"userDetailsService\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.core.userdetails.UserDetailsService\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<b:bean\n\t\tname=\"basicController\"\n\t\tclass=\"org.springframework.security.config.http.RememberMeConfigTests.BasicController\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-MultiRelyingPartyRegistration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n\t<b:import resource=\"../saml2/google-custom-registration.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration-WithCustomAuthenticationFailureHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login authentication-failure-handler-ref=\"authenticationFailureHandler\"/>\n\t</http>\n\n\t<b:bean id=\"authenticationFailureHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationFailureHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n\t<b:import resource=\"../saml2/google-registration.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login/>\n\t\t<request-cache ref=\"requestCache\" />\n\t</http>\n\n\t<relying-party-registrations>\n\t\t<relying-party-registration registration-id=\"one\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tentity-id=\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tassertion-consumer-service-location=\"{baseUrl}/login/saml2/sso/{registrationId}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tassertion-consumer-service-binding=\"REDIRECT\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tasserting-party-id=\"google\"/>\n\n\t\t<asserting-party asserting-party-id=\"google\" entity-id=\"https://accounts.google.com/o/saml2/idp/entity-id\"\n\t\t\t\t\t\t\t\t\t\t want-authn-requests-signed=\"true\"\n\t\t\t\t\t\t\t\t\t\t single-sign-on-service-location=\"https://accounts.google.com/o/saml2/idp/sso-url\"\n\t\t\t\t\t\t\t\t\t\t single-sign-on-service-binding=\"POST\">\n\t\t\t<verification-credential\n\t\t\t\t\tcertificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n\t\t\t\t\tprivate-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n\t\t\t<encryption-credential\n\t\t\t\t\tcertificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n\t\t\t\t\tprivate-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n\t\t</asserting-party>\n\t</relying-party-registrations>\n\n\t<b:bean id=\"requestCache\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.savedrequest.RequestCache\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomLoginProcessingUrl-WithCustomAuthenticationConverter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login login-processing-url=\"/my/custom/url\" authentication-converter-ref=\"authenticationConverter\"/>\n\t</http>\n\n\t<b:bean id=\"authenticationConverter\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationConverter\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"../saml2/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomLoginProcessingUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login login-processing-url=\"/my/custom/url\"/>\n\t</http>\n\n\t<b:import resource=\"../saml2/google-registration.xml\"/>\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationConverter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login relying-party-registration-repository-ref=\"relyingPartyRegistrationRepository\" authentication-converter-ref=\"authenticationConverter\"/>\n\t</http>\n\n\t<b:bean id=\"relyingPartyRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationConverter\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationConverter\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login relying-party-registration-repository-ref=\"relyingPartyRegistrationRepository\" authentication-manager-ref=\"customAuthenticationManager\"/>\n\t</http>\n\n\t<b:bean id=\"relyingPartyRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"customAuthenticationManager\" name=\"customAuthenticationManager\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.authentication.AuthenticationManager\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationRequestResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login relying-party-registration-repository-ref=\"relyingPartyRegistrationRepository\"\n\t\t\t\t\t\t\t\t authentication-request-resolver-ref=\"authenticationRequestResolver\"/>\n\t</http>\n\n\t<b:bean id=\"relyingPartyRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationRequestResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthnRequestRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login relying-party-registration-repository-ref=\"relyingPartyRegistrationRepository\" authentication-request-repository-ref=\"authenticationRequestRepository\"/>\n\t</http>\n\n\t<b:bean id=\"relyingPartyRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login authentication-success-handler-ref=\"authenticationSuccessHandler\" relying-party-registration-repository-ref=\"relyingPartyRegistrationRepository\"/>\n\t</http>\n\n\t<b:bean id=\"authenticationSuccessListener\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.context.ApplicationListener\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationSuccessHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationSuccessHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"relyingPartyRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" security-context-holder-strategy-ref=\"ref\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login authentication-success-handler-ref=\"authenticationSuccessHandler\" relying-party-registration-repository-ref=\"relyingPartyRegistrationRepository\"/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean id=\"authenticationSuccessListener\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.context.ApplicationListener\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"authenticationSuccessHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.AuthenticationSuccessHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\t<b:bean id=\"relyingPartyRegistrationRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CsrfDisabled-MockLogoutSuccessHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<logout success-handler-ref=\"logoutSuccessHandler\"/>\n\t\t<saml2-login/>\n\t\t<saml2-logout/>\n\t</http>\n\n\t<b:bean id=\"logoutSuccessHandler\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.web.authentication.logout.LogoutSuccessHandler\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n\t<b:import resource=\"../saml2/logout-registrations.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CustomComponents.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login/>\n\t\t<saml2-logout logout-request-resolver-ref=\"logoutRequestResolver\" logout-request-repository-ref=\"logoutRequestRepository\"\n\t\t\t\tlogout-request-validator-ref=\"logoutRequestValidator\" logout-response-validator-ref=\"logoutResponseValidator\" logout-response-resolver-ref=\"logoutResponseResolver\"/>\n\t</http>\n\n\t<b:bean id=\"logoutRequestResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:bean id=\"logoutRequestRepository\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:bean id=\"logoutRequestValidator\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:bean id=\"logoutResponseValidator\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:bean id=\"logoutResponseResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver\" type=\"java.lang.Class\"/>\n</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n\t<b:import resource=\"../saml2/logout-registrations.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-Default.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login/>\n\t\t<saml2-logout/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n\t<b:import resource=\"../saml2/logout-registrations.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-LogoutSuccessHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<logout success-handler-ref=\"logoutSuccessEndpoint\"/>\n\t\t<saml2-login/>\n\t\t<saml2-logout/>\n\t</http>\n\n\t<b:bean name=\"logoutSuccessEndpoint\" class=\"org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler\">\n\t\t<b:property name=\"defaultTargetUrl\" value=\"/logoutSuccessEndpoint\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n\t<b:import resource=\"../saml2/logout-registrations.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-WithSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" security-context-holder-strategy-ref=\"ref\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<saml2-login/>\n\t\t<saml2-logout/>\n\t</http>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n\t<b:import resource=\"../saml2/logout-registrations.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-FormLogin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<form-login/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.security.config.http.SecurityContextHolderAwareRequestConfigTests.ServletAuthenticatedController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-HttpBasic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<http-basic entry-point-ref=\"ep\"/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.security.config.http.SecurityContextHolderAwareRequestConfigTests.ServletAuthenticatedController\"/>\n\n\t<b:bean name=\"ep\" class=\"org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint\">\n\t\t<b:property name=\"realmName\" value=\"discworld\"/>\n\t</b:bean>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-Logout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http>\n\t\t<form-login login-page=\"/signin\"/>\n\t\t<logout invalidate-session=\"false\" delete-cookies=\"JSESSIONID\"/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.security.config.http.SecurityContextHolderAwareRequestConfigTests.ServletAuthenticatedController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-MultiHttp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http authentication-manager-ref=\"authManager2\" pattern=\"/v2/**\">\n\t\t<form-login login-page=\"/login2\"/>\n\t\t<logout invalidate-session=\"true\"/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<http authentication-manager-ref=\"authManager\">\n\t\t<form-login login-page=\"/login\"/>\n\t\t<logout invalidate-session=\"false\"/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.security.config.http.SecurityContextHolderAwareRequestConfigTests.ServletAuthenticatedController\"/>\n\n\t<authentication-manager id=\"authManager2\">\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"user2\" password=\"{noop}password2\" authorities=\"ROLE_USER\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n\t<authentication-manager id=\"authManager\">\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests-Simple.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean class=\"org.springframework.security.config.http.SecurityContextHolderAwareRequestConfigTests.ServletAuthenticatedController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-ConcurrencyControlCustomLogoutHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-authorization-manager=\"false\">\n\t\t<session-management\n\t\t\tauthentication-strategy-explicit-invocation=\"false\">\n\t\t\t<concurrency-control/>\n\t\t</session-management>\n\t\t<remember-me services-ref=\"customRememberMeServices\"/>\n\t</http>\n\n\t<b:bean name=\"customRememberMeServices\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.CustomRememberMeServices\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-ConcurrencyControlExpiredUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:mvc=\"http://www.springframework.org/schema/mvc\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\n\t\t\thttp://www.springframework.org/schema/mvc\n\t\t\thttps://www.springframework.org/schema/mvc/spring-mvc.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<session-management>\n\t\t\t<concurrency-control expired-url=\"/expired\"/>\n\t\t</session-management>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<mvc:annotation-driven/>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-ConcurrencyControlLogoutAndRememberMeHandlers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<session-management>\n\t\t\t<concurrency-control/>\n\t\t</session-management>\n\t\t<logout invalidate-session=\"false\" delete-cookies=\"testCookie\"/>\n\t\t<remember-me remember-me-cookie=\"rememberMeCookie\"/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-ConcurrencyControlMaxSessions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<session-management session-authentication-error-url=\"/max-exceeded\">\n\t\t\t<concurrency-control max-sessions=\"2\" error-if-maximum-exceeded=\"true\"/>\n\t\t</session-management>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-ConcurrencyControlMaxSessionsPlaceHolder.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns=\"http://www.springframework.org/schema/security\"\n\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<session-management\n\t\t\tsession-authentication-error-url=\"/max-exceeded\">\n\t\t\t<concurrency-control\n\t\t\t\tmax-sessions=\"${sessionManagement.maxSessions}\"\n\t\t\t\terror-if-maximum-exceeded=\"true\" />\n\t\t</session-management>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-ConcurrencyControlRememberMeHandler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<session-management>\n\t\t\t<concurrency-control/>\n\t\t</session-management>\n\t\t<remember-me remember-me-cookie=\"rememberMeCookie\"/>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-ConcurrencyControlSessionRegistryAlias.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<session-management\n\t\t\tauthentication-strategy-explicit-invocation=\"false\">\n\t\t\t<concurrency-control session-registry-alias=\"sessionRegistry\"/>\n\t\t</session-management>\n\t\t<csrf disabled=\"true\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-ConcurrencyControlSessionRegistryRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<session-management\n\t\t\tauthentication-strategy-explicit-invocation=\"false\">\n\t\t\t<concurrency-control session-registry-ref=\"sessionRegistry\"/>\n\t\t</session-management>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"sessionRegistry\" class=\"org.springframework.security.core.session.SessionRegistryImpl\"/>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-CreateSessionAlways.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" create-session=\"always\">\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-CreateSessionIfRequired.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" create-session=\"ifRequired\" use-expressions=\"true\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-CreateSessionNever.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" create-session=\"never\" use-expressions=\"true\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-CreateSessionStateless.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" create-session=\"stateless\" use-expressions=\"true\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<intercept-url pattern=\"/**\" access=\"permitAll\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-NoSessionManagementFilter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"true\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<session-management session-fixation-protection=\"none\"/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-Sec1208.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" create-session=\"ifRequired\" use-expressions=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<session-management\n\t\t\tauthentication-strategy-explicit-invocation=\"false\">\n\t\t\t<concurrency-control max-sessions=\"1\" error-if-maximum-exceeded=\"true\"/>\n\t\t</session-management>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-Sec2137.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" create-session=\"ifRequired\" use-expressions=\"true\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<session-management session-fixation-protection=\"none\">\n\t\t\t<concurrency-control max-sessions=\"1\" error-if-maximum-exceeded=\"true\"/>\n\t\t</session-management>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-SessionAuthenticationStrategyRef.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"true\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<session-management\n\t\t\tauthentication-strategy-explicit-invocation=\"false\"\n\t\t\tsession-authentication-strategy-ref=\"teapotSessionAuthenticationStrategy\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:bean name=\"teapotSessionAuthenticationStrategy\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.TeapotSessionAuthenticationStrategy\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-SessionFixationProtectionMigrateSession.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"true\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<session-management\n\t\t\tauthentication-strategy-explicit-invocation=\"false\"\n\t\t\tsession-fixation-protection=\"migrateSession\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-SessionFixationProtectionNone.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"true\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<session-management session-fixation-protection=\"none\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTests-SessionFixationProtectionNoneWithInvalidSessionUrl.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" use-expressions=\"true\">\n\t\t<intercept-url pattern=\"/auth/**\" access=\"authenticated\"/>\n\t\t<session-management session-fixation-protection=\"none\" invalid-session-url=\"/timeoutUrl\"/>\n\t</http>\n\n\t<b:bean name=\"basicController\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTests.BasicController\"/>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTransientAuthenticationTests-CreateSessionAlwaysWithTransientAuthentication.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\" create-session=\"always\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"transientAuthenticationProvider\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTransientAuthenticationTests.TransientAuthenticationProvider\"/>\n\n\t<authentication-manager>\n\t\t<authentication-provider ref=\"transientAuthenticationProvider\"/>\n\t</authentication-manager>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/SessionManagementConfigTransientAuthenticationTests-WithTransientAuthentication.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<b:bean name=\"transientAuthenticationProvider\"\n\t\t\tclass=\"org.springframework.security.config.http.SessionManagementConfigTransientAuthenticationTests.TransientAuthenticationProvider\"/>\n\n\t<authentication-manager>\n\t\t<authentication-provider ref=\"transientAuthenticationProvider\"/>\n\t</authentication-manager>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParserTests-CustomChangePasswordPage.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<password-management change-password-page=\"/custom-change-password-page\"/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParserTests-DefaultChangePasswordPage.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<http auto-config=\"true\">\n\t\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t\t<password-management/>\n\t</http>\n\n\t<b:import resource=\"userservice.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/jaas-login.conf",
    "content": "JAASTest {\n\torg.springframework.security.config.http.MiscHttpConfigTests$JaasLoginModule required;\n};\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC1pXMFNiDXHWkl\n59GMRBW7RsOyqpMzU5PqgLc8josbty4wjvxlM9td00+s94fS/S9a6m/thJi8E4pb\nMwQgckHhaUyiAYW1N3nv3Tj/3+/vhXGKoclTt2NNgTj8eFNx0x20+q0H8nyx0tJu\n7I5rFLWXf3uaOg3DPWGxxLWwN8GNDKMeusjx7/mMpoYnxYRWhcekUHxrLY4gsE6E\nrxJqa0DjmPAeaVMqBsXKBb0JKbyVI4P9t+tIGoNX0hWtudr/R14b+rnAugkfBOUH\n3GgKXS5RjtwIJsRy7RhzoZBISuNQuZsS2N3kJp6lkmveN+trr6HX0COZA5JNs8FH\n26+NVjYpAgMBAAECggEAdj9RRBg7gq3jsEhb0krUNsjXRqziDGyhAuxt3F8S0aUK\nzZOXXK5IZKjV2kx9P+2P8UhiOqWx4+V4kOwCCLk4h+vLdj81coW9vFcv7uoxwKQH\nCO9PJ09ftqmjlj8iWxvK3/C7yUuivwP19z3JbI3btNbJeNnH8cw4l8ftWtzBA/cW\n3F87sjM3J59KFMjaNwDmgpCx6xVfk1E+R1e2tcgbjGdy5h14BUkS5kMHSgk13bj4\n1Pg9QZBeV7L3oui/PPz/wt7MDY7qerI8+gJ1vR3wqbX30F+o+K1rJ4ZEYM3OaxnF\ncpdvfUGNXApZD3mdSZenDahtGhlVcLUID9IHaKjGTQKBgQDuiwlKLxPBWDpFzXU5\nWaLRInyULq0E9jj4CcMX509vl6fbRf8vsULDzg9nD3bjWF8dakJBl811iSvHsOQv\n7SaIS6aTW4cyhEpSGqWyJrv6C5EnWxrgbtED85OVcFi2HIoPpQgke+ORNur4ceZp\n33BXI2wqYF2FM/4n4MakLnu8JwKBgQDC8HwYnifV/syxhaZXHyq1P61F4C6yYqHP\n19PsE/NAiYHMz9HVKRWQznX1HuSQPdMCNKtGrmzSO9PAIt0VgOiKNUQa5Wx9s/4f\nqs8z/6FKHgw96u7T/6erzCyz/TCqJCIZpqiurJh1yibXWcv3S3KxwXHcMxuIM2UO\nQfHItOfdLwKBgHPIZI/vXcVgz5gF67oUeeTMum8qYKyh3nD2PA2kJnhhwgW6aex6\nO4/SusMpTGl5AWHDq7kut3kvRWK9x2RD1YoePhIUQQESGQjVCkv4ZREvABt8KwV4\nhFdIqP/F8ikinRiO3+7le0WGxHImxtHotBx3gw7miz4WSGyOu5wBO+nNAoGAdA8N\nMvLmMKe0YnR5piDuIl644IMqChZi1AVhLIpsJp43YyLPgKMnLcFdxQQRfDbyq7uD\nxV8PdtMbrKaVDbpjt/UiiQjSYLyKrXvaRNEKy7+79hkq+5iX0NaMPtSc5o8Apljs\n0KH9WxNxsIDLyH1o2Z+cFEdxOtOudKCaPK5H5Q0CgYBsCK4rCO3pXU+bWiwn401E\ni5SaYHGH33eBYgle+CAyzj0dNKogow+5Yfo6AkiXyng/PKY95DRXsSH3z6hGzU65\nNjbuNK2YdfCem2sdY3PRLJdIu7JK6fW3q3nQLZ6G4QUXH+9mmRDsrh9fNCNfOStl\nV6tGbgS0ay+H6oMRfUQfNw==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/http/userservice.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\t<user-service id=\"us\">\n\t\t<user name=\"user\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n\t\t<user name=\"admin\" password=\"{noop}password\" authorities=\"ROLE_ADMIN\"/>\n\t</user-service>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-AspectJMethodSecurityServiceEnabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security mode=\"aspectj\" secured-enabled=\"true\" jsr250-enabled=\"true\"/>\n\n\t<b:bean class=\"org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-BusinessService.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security jsr250-enabled=\"true\"/>\n\n\t<b:bean class=\"org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-CustomAuthorizationManagerAfterAdvice.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:aop=\"http://www.springframework.org/schema/aop\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd\">\n\n\t<method-security/>\n\n\t<b:bean id=\"pattern\" class=\"org.springframework.aop.support.JdkRegexpMethodPointcut\">\n\t\t<b:property name=\"pattern\" value=\".*MethodSecurityServiceImpl.*securedUser\"/>\n\t</b:bean>\n\n\t<b:bean id=\"rule\" class=\"org.springframework.security.config.method.MethodSecurityBeanDefinitionParserTests.MyAdvice\"/>\n\n\t<aop:config>\n\t\t<aop:advisor pointcut-ref=\"pattern\" advice-ref=\"rule\" order=\"#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).POST_FILTER.getOrder() + 1}\"/>\n\t</aop:config>\n\n\t<b:bean id=\"methodSecurityService\" class=\"org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-CustomAuthorizationManagerBeforeAdvice.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:aop=\"http://www.springframework.org/schema/aop\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd\">\n\n\t<method-security/>\n\n\t<aop:config/>\n\n\t<b:bean id=\"myAdvice\" class=\"org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.aop.support.JdkRegexpMethodPointcut\">\n\t\t\t\t<b:property name=\"pattern\" value=\".*MethodSecurityServiceImpl.*securedUser\"/>\n\t\t\t</b:bean>\n\t\t</b:constructor-arg>\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.method.MethodSecurityBeanDefinitionParserTests.MyAuthorizationManager\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean id=\"methodSecurityService\" class=\"org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-CustomGrantedAuthorityDefaults.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security/>\n\n\t<b:bean class=\"org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl\"/>\n\n\t<b:bean id=\"grantedAuthorityDefaults\" class=\"org.springframework.security.config.core.GrantedAuthorityDefaults\">\n\t\t<b:constructor-arg value=\"PREFIX_\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-CustomPermissionEvaluator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security>\n\t\t<expression-handler ref=\"expressionHandler\"/>\n\t</method-security>\n\n\t<b:bean class=\"org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl\"/>\n\n\t<b:bean id=\"expressionHandler\" class=\"org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler\">\n\t\t<b:property name=\"permissionEvaluator\">\n\t\t\t<b:bean class=\"org.springframework.security.config.method.MethodSecurityBeanDefinitionParserTests.MyPermissionEvaluator\"/>\n\t\t</b:property>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-Jsr250.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security pre-post-enabled=\"false\" jsr250-enabled=\"true\"/>\n\n\t<b:bean class=\"org.springframework.security.access.annotation.Jsr250BusinessServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-MethodSecurityService.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security/>\n\n\t<b:bean class=\"org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-MethodSecurityServiceEnabled.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security secured-enabled=\"true\" jsr250-enabled=\"true\"/>\n\n\t<b:bean class=\"org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security secured-enabled=\"true\" jsr250-enabled=\"true\" security-context-holder-strategy-ref=\"ref\"/>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<b:bean class=\"org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-ProtectPointcut.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean class=\"org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl\"/>\n\n\t<method-security>\n\t\t<protect-pointcut expression=\"execution(* org.springframework.security.access.annotation.BusinessService.someOther(String))\" access=\"hasRole('ADMIN')\"/>\n\t\t<protect-pointcut expression=\"execution(* org.springframework.security.access.annotation.BusinessService.*(..))\" access=\"hasRole('USER')\"/>\n\t</method-security>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-ProtectPointcutBoolean.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:bean class=\"org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl\"/>\n\n\t<method-security>\n\t\t<protect-pointcut expression=\"execution(* org.springframework.security.access.annotation.BusinessService.*(..)) and not execution(* org.springframework.security.access.annotation.BusinessService.someOther(String)))\" access=\"hasRole('USER')\"/>\n\t</method-security>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-Secured.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<method-security pre-post-enabled=\"false\" secured-enabled=\"true\"/>\n\n\t<b:bean class=\"org.springframework.security.access.annotation.BusinessServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/PreAuthorizeTests-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:c=\"http://www.springframework.org/schema/c\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<global-method-security pre-post-annotations=\"enabled\"/>\n\n\t<b:bean class=\"org.springframework.security.config.method.PreAuthorizeServiceImpl\"/>\n\n</b:beans>"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/SecuredTests-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:c=\"http://www.springframework.org/schema/c\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<global-method-security secured-annotations=\"enabled\"/>\n\n\t<b:bean class=\"org.springframework.security.config.method.SecuredServiceImpl\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/sec2136/sec2136.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns:jpa=\"http://www.springframework.org/schema/data/jpa\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txmlns:jdbc=\"http://www.springframework.org/schema/jdbc\"\n\txmlns:security=\"http://www.springframework.org/schema/security\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd\n\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd\n\t\thttp://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd\n\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.1.xsd\">\n\n\t<context:annotation-config />\n\n\t<bean id=\"entityManagerFactory\"\n\t\t  class=\"org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean\">\n\t\t<property name=\"dataSource\" ref=\"dataSource\" />\n\t\t<property name=\"packagesToScan\" value=\"entity\" />\n\t\t<property name=\"jpaVendorAdapter\">\n\t\t\t<bean class=\"org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter\">\n\t\t\t\t<property name=\"showSql\" value=\"true\" />\n\t\t\t\t<property name=\"generateDdl\" value=\"false\" />\n\t\t\t\t<property name=\"databasePlatform\" value=\"org.hibernate.dialect.HSQLDialect\" />\n\t\t\t</bean>\n\t\t</property>\n\t\t<property name=\"jpaProperties\">\n\t\t\t<props>\n\t\t\t\t<prop key=\"hibernate.hbm2ddl.auto\">create-drop</prop>\n\t\t\t\t<prop key=\"hibernate.id.new_generator_mappings\">true</prop>\n\t\t\t\t<prop key=\"hibernate.cache.use_second_level_cache\">false</prop>\n\t\t\t</props>\n\t\t</property>\n\t</bean>\n\n\t<!-- DataSource Configuration -->\n\t<jdbc:embedded-database id=\"dataSource\"/>\n\n\t<!-- Transaction Configuration -->\n\t<bean id=\"transactionManager\" class=\"org.springframework.orm.jpa.JpaTransactionManager\">\n\t\t<property name=\"entityManagerFactory\" ref=\"entityManagerFactory\" />\n\t</bean>\n\n\t<security:global-method-security pre-post-annotations=\"enabled\">\n\t\t<security:expression-handler ref=\"methodExpressionHandler\"/>\n\t</security:global-method-security>\n\n\t<bean id=\"methodExpressionHandler\"\n\t\tclass=\"org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler\">\n\t\t<property name=\"permissionEvaluator\" ref=\"jpaPermEvaluator\"/>\n\t</bean>\n\n\t<!-- Must be lazy init or inner bean to prevent eager loading of singletons -->\n\t<bean id=\"jpaPermEvaluator\" class=\"org.springframework.security.config.method.sec2136.JpaPermissionEvaluator\" autowire=\"byType\" lazy-init=\"true\"/>\n</beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/sec2499/child.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns=\"http://www.springframework.org/schema/security\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<global-method-security pre-post-annotations=\"enabled\">\n\t\t<expression-handler ref=\"expressionHandler\"/>\n\t</global-method-security>\n\n</b:beans>"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method/sec2499/parent.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<bean id=\"expressionHandler\"\n\t\tclass=\"org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler\"/>\n\n</beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/method-security.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\thttp://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd\n\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<tx:annotation-driven />\n\n\t<b:bean name=\"transactionManager\" class=\"org.springframework.security.config.MockTransactionManager\" />\n\n\t<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>\n\n\t<b:bean id=\"transactionalTarget\" class=\"org.springframework.security.config.TransactionalTestBusinessBean\">\n\t\t<intercept-methods use-authorization-manager=\"false\">\n\t\t\t<protect method=\"*\" access=\"ROLE_USER\" />\n\t\t</intercept-methods>\n\t</b:bean>\n\n\n\t<b:bean id=\"target\" class=\"org.springframework.security.config.TestBusinessBeanImpl\">\n\t\t<!-- This will add a security interceptor to the bean -->\n\t\t<intercept-methods use-authorization-manager=\"false\">\n\t\t\t<protect method=\"org.springframework.security.config.TestBusinessBean.set*\" access=\"${admin.role}\" />\n\t\t\t<protect method=\"get*\" access=\"${admin.role},ROLE_USER\" />\n\t\t\t<protect method=\"doSomething\" access=\"ROLE_USER\" />\n\t\t</intercept-methods>\n\t</b:bean>\n\n\t<b:bean id=\"transactionalTargetAuthorizationManager\" class=\"org.springframework.security.config.TransactionalTestBusinessBean\">\n\t\t<intercept-methods>\n\t\t\t<protect method=\"*\" access=\"hasRole('ROLE_USER')\" />\n\t\t</intercept-methods>\n\t</b:bean>\n\n\n\t<b:bean id=\"targetAuthorizationManager\" class=\"org.springframework.security.config.TestBusinessBeanImpl\">\n\t\t<!-- This will add a security interceptor to the bean -->\n\t\t<intercept-methods>\n\t\t\t<protect method=\"org.springframework.security.config.TestBusinessBean.set*\" access=\"hasRole('${admin.role}')\" />\n\t\t\t<protect method=\"get*\" access=\"hasAnyRole('${admin.role}','ROLE_USER')\" />\n\t\t\t<protect method=\"doSomething\" access=\"hasRole('ROLE_USER')\" />\n\t\t\t<protect method=\"*\" access=\"permitAll\"/>\n\t\t</intercept-methods>\n\t</b:bean>\n\n\t<b:bean id=\"targetCustomAuthorizationManager\" class=\"org.springframework.security.config.TestBusinessBeanImpl\">\n\t\t<!-- This will add a security interceptor to the bean -->\n\t\t<intercept-methods authorization-manager-ref=\"authorization-manager\">\n\t\t\t<protect method=\"*\" access=\"denyAll\"/>\n\t\t</intercept-methods>\n\t</b:bean>\n\n\t<authentication-manager>\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"bob\" password=\"bobspassword\" authorities=\"ROLE_A,ROLE_B\" />\n\t\t\t\t<user name=\"bill\" password=\"billspassword\" authorities=\"ROLE_A,ROLE_B,AUTH_OTHER\" />\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n\t<b:bean id=\"authorization-manager\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.security.authorization.AuthorizationManager\" type=\"java.lang.Class\"/>\n</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests-ClientPlaceholders.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\t<client-registrations>\n\t\t<client-registration registration-id=\"github\"\n\t\t\t\t\t\t\t client-id=\"${oauth2.client.id}\"\n\t\t\t\t\t\t\t client-secret=\"${oauth2.client.secret}\"\n\t\t\t\t\t\t\t provider-id=\"github\"/>\n\t</client-registrations>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests-MultiClientRegistration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\t<b:import resource=\"google-github-registration.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/oauth2/client/google-github-registration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\t<client-registrations>\n\t\t<client-registration registration-id=\"google-login\"\n\t\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t\t client-authentication-method=\"client_secret_basic\"\n\t\t\t\t\t\t\t authorization-grant-type=\"authorization_code\"\n\t\t\t\t\t\t\t redirect-uri=\"{baseUrl}/login/oauth2/code/{registrationId}\"\n\t\t\t\t\t\t\t scope=\"openid,profile,email\"\n\t\t\t\t\t\t\t client-name=\"Google\"\n\t\t\t\t\t\t\t provider-id=\"google\"/>\n\t\t<client-registration registration-id=\"github-login\"\n\t\t\t\t\t\t\t client-id=\"github-client-id\"\n\t\t\t\t\t\t\t client-secret=\"github-client-secret\"\n\t\t\t\t\t\t\t client-authentication-method=\"client_secret_basic\"\n\t\t\t\t\t\t\t authorization-grant-type=\"authorization_code\"\n\t\t\t\t\t\t\t redirect-uri=\"{baseUrl}/login/oauth2/code/{registrationId}\"\n\t\t\t\t\t\t\t scope=\"read:user\"\n\t\t\t\t\t\t\t client-name=\"Github\"\n\t\t\t\t\t\t\t provider-id=\"github\"/>\n\t\t<provider provider-id=\"google\"\n\t\t\t\t  authorization-uri=\"https://accounts.google.com/o/oauth2/v2/auth\"\n\t\t\t\t  token-uri=\"https://www.googleapis.com/oauth2/v4/token\"\n\t\t\t\t  user-info-uri=\"https://www.googleapis.com/oauth2/v3/userinfo\"\n\t\t\t\t  user-info-authentication-method=\"header\"\n\t\t\t\t  user-info-user-name-attribute=\"sub\"\n\t\t\t\t  jwk-set-uri=\"https://www.googleapis.com/oauth2/v3/certs\"/>\n\t\t<provider provider-id=\"github\"\n\t\t\t\t  authorization-uri=\"https://github.com/login/oauth/authorize\"\n\t\t\t\t  token-uri=\"https://github.com/login/oauth/access_token\"\n\t\t\t\t  user-info-uri=\"https://api.github.com/user\"\n\t\t\t\t  user-info-authentication-method=\"header\"\n\t\t\t\t  user-info-user-name-attribute=\"id\"/>\n\t</client-registrations>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/oauth2/client/google-registration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\t<client-registrations>\n\t\t<client-registration registration-id=\"google-login\"\n\t\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t\t client-authentication-method=\"client_secret_basic\"\n\t\t\t\t\t\t\t authorization-grant-type=\"authorization_code\"\n\t\t\t\t\t\t\t redirect-uri=\"{baseUrl}/login/oauth2/code/{registrationId}\"\n\t\t\t\t\t\t\t scope=\"openid,profile,email\"\n\t\t\t\t\t\t\t client-name=\"Google\"\n\t\t\t\t\t\t\t provider-id=\"google\"/>\n\t\t<provider provider-id=\"google\"\n\t\t\t\t  authorization-uri=\"https://accounts.google.com/o/oauth2/v2/auth\"\n\t\t\t\t  token-uri=\"https://www.googleapis.com/oauth2/v4/token\"\n\t\t\t\t  user-info-uri=\"https://www.googleapis.com/oauth2/v3/userinfo\"\n\t\t\t\t  user-info-authentication-method=\"header\"\n\t\t\t\t  user-info-user-name-attribute=\"sub\"\n\t\t\t\t  jwk-set-uri=\"https://www.googleapis.com/oauth2/v3/certs\"/>\n\t</client-registrations>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParserTests-MultiRegistration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n  <relying-party-registrations id=\"registrations\">\n    <relying-party-registration registration-id=\"one\"\n                                entity-id=\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\"\n                                assertion-consumer-service-location=\"{baseUrl}/login/saml2/sso/{registrationId}\"\n                                assertion-consumer-service-binding=\"REDIRECT\"\n                                asserting-party-id=\"google\"/>\n\n    <relying-party-registration registration-id=\"two\"\n                                entity-id=\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\"\n                                assertion-consumer-service-location=\"{baseUrl}/login/saml2/sso/{registrationId}\"\n                                assertion-consumer-service-binding=\"POST\"\n                                asserting-party-id=\"simple-saml\"/>\n\n    <asserting-party asserting-party-id=\"google\" entity-id=\"https://accounts.google.com/o/saml2/idp/entity-id\"\n                     want-authn-requests-signed=\"true\"\n                     single-sign-on-service-location=\"https://accounts.google.com/o/saml2/idp/sso-url\"\n                     single-sign-on-service-binding=\"POST\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n\n    <asserting-party asserting-party-id=\"simple-saml\"\n                     entity-id=\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\"\n                     single-sign-on-service-location=\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\"\n                     single-sign-on-service-binding=\"POST\"\n                     signing-algorithms=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha224,http://www.w3.org/2001/04/xmldsig-more#rsa-sha256,http://www.w3.org/2001/04/xmldsig-more#rsa-sha384\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n  </relying-party-registrations>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParserTests-PlaceholderRegistration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<relying-party-registrations>\n\t\t<relying-party-registration registration-id=\"${registration-id}\"\n\t                                entity-id=\"${entity-id}\"\n\t                                metadata-location=\"${metadata-location}\"\n\t                                assertion-consumer-service-location=\"${acs-location}\"\n\t                                single-logout-service-location=\"${slo-location}\"\n\t                                single-logout-service-response-location=\"${slo-response-location}\"/>\n\t</relying-party-registrations>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParserTests-RelayStateResolver.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n  <relying-party-registrations id=\"registrations\">\n    <relying-party-registration registration-id=\"one\"\n                                entity-id=\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\"\n                                assertion-consumer-service-location=\"{baseUrl}/login/saml2/sso/{registrationId}\"\n                                assertion-consumer-service-binding=\"REDIRECT\"\n                                asserting-party-id=\"google\">\n      <signing-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/rp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n\t</relying-party-registration>\n    <asserting-party asserting-party-id=\"google\" entity-id=\"https://accounts.google.com/o/saml2/idp/entity-id\"\n                     want-authn-requests-signed=\"true\"\n                     single-sign-on-service-location=\"https://accounts.google.com/o/saml2/idp/sso-url\"\n                     single-sign-on-service-binding=\"POST\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n  </relying-party-registrations>\n\n  <b:bean class=\"org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver\">\n    <b:constructor-arg ref=\"registrations\"/>\n    <b:property name=\"relayStateResolver\" ref=\"relayStateResolver\"/>\n  </b:bean>\n\n  <b:bean name=\"relayStateResolver\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n    <b:constructor-arg value=\"org.springframework.core.convert.converter.Converter\" type=\"java.lang.Class\"/>\n  </b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParserTests-SingleRegistration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n  <relying-party-registrations id=\"registrations\">\n    <relying-party-registration registration-id=\"one\"\n                                entity-id=\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\"\n                                assertion-consumer-service-location=\"{baseUrl}/login/saml2/sso/{registrationId}\"\n                                assertion-consumer-service-binding=\"REDIRECT\"\n                                asserting-party-id=\"google\"/>\n\n    <asserting-party asserting-party-id=\"google\" entity-id=\"https://accounts.google.com/o/saml2/idp/entity-id\"\n                     want-authn-requests-signed=\"true\"\n                     single-sign-on-service-location=\"https://accounts.google.com/o/saml2/idp/sso-url\"\n                     single-sign-on-service-binding=\"POST\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n  </relying-party-registrations>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/google-custom-registration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n  <relying-party-registrations>\n    <relying-party-registration registration-id=\"one\"\n                                entity-id=\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\"\n                                assertion-consumer-service-location=\"{baseUrl}/login/saml2/sso/{registrationId}\"\n                                assertion-consumer-service-binding=\"REDIRECT\"\n                                asserting-party-id=\"google\"/>\n\n    <relying-party-registration registration-id=\"two\"\n                                entity-id=\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\"\n                                assertion-consumer-service-location=\"{baseUrl}/login/saml2/sso/{registrationId}\"\n                                assertion-consumer-service-binding=\"POST\"\n                                asserting-party-id=\"simple-saml\"/>\n\n    <asserting-party asserting-party-id=\"google\" entity-id=\"https://accounts.google.com/o/saml2/idp/entity-id\"\n                     want-authn-requests-signed=\"true\"\n                     single-sign-on-service-location=\"https://accounts.google.com/o/saml2/idp/sso-url\"\n                     single-sign-on-service-binding=\"POST\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n\n    <asserting-party asserting-party-id=\"simple-saml\"\n                     entity-id=\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\"\n                     single-sign-on-service-location=\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\"\n                     single-sign-on-service-binding=\"POST\"\n                     signing-algorithms=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha224,http://www.w3.org/2001/04/xmldsig-more#rsa-sha256,http://www.w3.org/2001/04/xmldsig-more#rsa-sha384\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n  </relying-party-registrations>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/google-registration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n  <relying-party-registrations>\n    <relying-party-registration registration-id=\"one\"\n                                entity-id=\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\"\n                                assertion-consumer-service-location=\"{baseUrl}/login/saml2/sso/{registrationId}\"\n                                assertion-consumer-service-binding=\"REDIRECT\"\n                                asserting-party-id=\"google\"/>\n\n    <asserting-party asserting-party-id=\"google\" entity-id=\"https://accounts.google.com/o/saml2/idp/entity-id\"\n                     want-authn-requests-signed=\"true\"\n                     single-sign-on-service-location=\"https://accounts.google.com/o/saml2/idp/sso-url\"\n                     single-sign-on-service-binding=\"POST\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n  </relying-party-registrations>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/idp-certificate.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\nVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\nVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\nc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\naXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\nBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\nBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\nDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\nQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\nE1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\n2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\nRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\nnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\ncljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\niJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\nogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\nAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\nnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\nZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\nxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\nV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\nlx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/logout-registrations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n  <relying-party-registrations>\n    <relying-party-registration registration-id=\"registration-id\"\n                                entity-id=\"rp-entity-id\"\n                                assertion-consumer-service-location=\"https://rp.example.org/acs\"\n                                assertion-consumer-service-binding=\"REDIRECT\"\n                                single-logout-service-location=\"https://rp.example.org/logout/saml2/request\"\n                                single-logout-service-response-location=\"https://rp.example.org/logout/saml2/response\"\n                                asserting-party-id=\"ap\">\n      <signing-credential certificate-location=\"classpath:org/springframework/security/config/saml2/rp-certificate.crt\"\n                          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </relying-party-registration>\n\n    <relying-party-registration registration-id=\"get\"\n                                entity-id=\"rp-entity-id\"\n                                assertion-consumer-service-location=\"https://rp.example.org/acs\"\n                                assertion-consumer-service-binding=\"REDIRECT\"\n                                single-logout-service-location=\"https://rp.example.org/logout/saml2/request\"\n                                single-logout-service-response-location=\"https://rp.example.org/logout/saml2/response\"\n                                single-logout-service-binding=\"REDIRECT\"\n                                asserting-party-id=\"ap\">\n      <signing-credential certificate-location=\"classpath:org/springframework/security/config/saml2/rp-certificate.crt\"\n                          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </relying-party-registration>\n\n    <relying-party-registration registration-id=\"rp\"\n                                entity-id=\"ap-entity-id\"\n                                assertion-consumer-service-location=\"https://rp.example.org/acs\"\n                                assertion-consumer-service-binding=\"REDIRECT\"\n                                single-logout-service-location=\"https://rp.example.org/logout/saml2/request\"\n                                single-logout-service-response-location=\"https://rp.example.org/logout/saml2/response\"\n                                single-logout-service-binding=\"REDIRECT\"\n                                asserting-party-id=\"rp\"/>\n\n    <asserting-party asserting-party-id=\"ap\" entity-id=\"ap-entity-id\"\n                     single-sign-on-service-location=\"https://ap.example.org/sso\"\n                     single-logout-service-location=\"https://ap.example.org/logout/saml2/request\"\n                     single-logout-service-response-location=\"https://ap.example.org/logout/saml2/response\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n\n    <asserting-party asserting-party-id=\"rp\" entity-id=\"ap-entity-id\"\n                     single-sign-on-service-location=\"https://rp.example.org/sso\"\n                     single-logout-service-location=\"https://rp.example.org/logout/saml2/request\"\n                     single-logout-service-response-location=\"https://ap.example.org/logout/saml2/response\">\n      <verification-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n      <encryption-credential\n          certificate-location=\"classpath:org/springframework/security/config/saml2/idp-certificate.crt\"\n          private-key-location=\"classpath:org/springframework/security/config/saml2/rp-private.key\"/>\n    </asserting-party>\n  </relying-party-registrations>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/rp-certificate.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\nA1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\nDBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\nMTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\nMBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\nTDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\nvzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\n+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\ny3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\nXOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\nqK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\nRZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/saml2/rp-private.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\nVUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\ncDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\nQ2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\nx7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\nwJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\nvwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\n8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\noPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\nEGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\nKkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\nYX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\n9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\nINrtuLp4YHbgk1mi\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/users.properties",
    "content": "joe={noop}joespassword,ROLE_A\nbob={noop}bobspassword,ROLE_A,ROLE_B\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests-simple.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0IUjrPZDz+3z0UE4ppcKU36v7hnh8FJjhu3lbJYj0qj9eZiwEJxi9HHUfSK1DhUQG7mJBbYTK1tPYCgre5EkfKh+64VhYUa+vz17zYCmuB8fFj4XHE3MLkWIG+AUn8hNbPzYYmiBTjfGnMKxLHjsbdTiF4mtn+85w366916R6midnAuiPD4HjZaZ1PAsuY60gr8bhMEDtJ8unz81hoQrozpBZJ6r8aR1PrsWb1OqPMloK9kAIutJNvWYKacp8WYAp2WWy72PxQ7Fb0eIA1br3A5dnp+Cln6JROJcZUIRJ+QvS6QONWeS2407uQmS+i+lybsqaH0ldYC7NBEBA5inPQIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-ConnectAckInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"CONNECT_ACK\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-ConnectInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"CONNECT\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomAuthorizationManagerConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<b:bean name=\"authorizationManager\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.security.authorization.AuthorizationManager\" type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<websocket-message-broker authorization-manager-ref=\"authorizationManager\"/>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomCsrfInterceptor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<b:bean id=\"csrfChannelInterceptor\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<b:constructor-arg value=\"org.springframework.messaging.support.ChannelInterceptor\"  type=\"java.lang.Class\"/>\n\t</b:bean>\n\n\t<websocket-message-broker use-authorization-manager=\"false\">\n\t\t<intercept-message pattern=\"/**\" access=\"denyNile()\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomExpressionHandlerAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<b:bean name=\"expressionHandler\" class=\"org.springframework.security.messaging.access.expression.MessageAuthorizationContextSecurityExpressionHandler\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.DenyNileMessageSecurityExpressionHandler\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n\t<websocket-message-broker>\n\t\t<expression-handler ref=\"expressionHandler\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyNile()\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomExpressionHandlerConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<b:bean name=\"expressionHandler\" class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.DenyNileMessageSecurityExpressionHandler\"/>\n\n\t<websocket-message-broker use-authorization-manager=\"false\">\n\t\t<expression-handler ref=\"expressionHandler\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyNile()\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomInterceptorConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:websocket=\"http://www.springframework.org/schema/websocket\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\">\n\n\t<websocket:message-broker application-destination-prefix=\"/app\" user-destination-prefix=\"/user\">\n\t\t<websocket:transport/>\n\t\t<websocket:stomp-endpoint path=\"/foo\">\n\t\t\t<websocket:sockjs/>\n\t\t</websocket:stomp-endpoint>\n\t\t<websocket:simple-broker prefix=\"/queue, /topic\"/>\n\t\t<websocket:client-inbound-channel>\n\t\t\t<websocket:interceptors>\n\t\t\t\t<b:ref bean=\"eci\"/>\n\t\t\t</websocket:interceptors>\n\t\t</websocket:client-inbound-channel>\n\t</websocket:message-broker>\n\n\t<b:bean name=\"eci\" class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.ExceptingInterceptor\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/denyAll\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomPathMatcherAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:websocket=\"http://www.springframework.org/schema/websocket\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\n\t<websocket:message-broker path-matcher=\"pathMatcher\">\n\t\t<websocket:transport/>\n\t\t<websocket:stomp-endpoint path=\"/app\">\n\t\t\t<websocket:handshake-handler ref=\"testHandler\"/>\n\t\t</websocket:stomp-endpoint>\n\n\t\t<websocket:simple-broker prefix=\"/queue, /topic\"/>\n\t</websocket:message-broker>\n\n\t<b:bean name=\"pathMatcher\" class=\"org.springframework.util.AntPathMatcher\">\n\t\t<b:constructor-arg value=\".\"/>\n\t</b:bean>\n\n\t<b:bean name=\"testHandler\" class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/denyAll.*\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomPathMatcherConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:websocket=\"http://www.springframework.org/schema/websocket\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\n\t<websocket:message-broker path-matcher=\"pathMatcher\">\n\t\t<websocket:transport/>\n\t\t<websocket:stomp-endpoint path=\"/app\">\n\t\t\t<websocket:handshake-handler ref=\"testHandler\"/>\n\t\t</websocket:stomp-endpoint>\n\n\t\t<websocket:simple-broker prefix=\"/queue, /topic\"/>\n\t</websocket:message-broker>\n\n\t<b:bean name=\"pathMatcher\" class=\"org.springframework.util.AntPathMatcher\">\n\t\t<b:constructor-arg value=\".\"/>\n\t</b:bean>\n\n\t<b:bean name=\"testHandler\" class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler\"/>\n\n\t<websocket-message-broker use-authorization-manager=\"false\">\n\t\t<intercept-message pattern=\"/denyAll.*\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-DisconnectAckInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"DISCONNECT_ACK\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-DisconnectInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"HEARTBEAT\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-HeartbeatInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"HEARTBEAT\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-IdConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker id=\"inCsi\">\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-IdIntegratedConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:websocket=\"http://www.springframework.org/schema/websocket\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\">\n\n\t<websocket:message-broker>\n\t\t<websocket:transport/>\n\t\t<websocket:stomp-endpoint path=\"/app\">\n\t\t\t<websocket:sockjs/>\n\t\t</websocket:stomp-endpoint>\n\t\t<websocket:simple-broker prefix=\"/queue, /topic\"/>\n\t\t<websocket:client-inbound-channel>\n\t\t\t<websocket:interceptors>\n\t\t\t\t<b:ref bean=\"inCsi\"/>\n\t\t\t</websocket:interceptors>\n\t\t</websocket:client-inbound-channel>\n\t</websocket:message-broker>\n\n\t<websocket-message-broker id=\"inCsi\">\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-MessageInterceptTypeAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"MESSAGE\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-MessageInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker use-authorization-manager=\"false\">\n\t\t<intercept-message pattern=\"/permitAll\" type=\"MESSAGE\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-NoIdAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/denyAll\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-NoIdConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker use-authorization-manager=\"false\">\n\t\t<intercept-message pattern=\"/permitAll\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/denyAll\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-OtherInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"OTHER\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SubscribeInterceptTypeAuthorizationManager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"SUBSCRIBE\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SubscribeInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker use-authorization-manager=\"false\">\n\t\t<intercept-message pattern=\"/permitAll\" type=\"SUBSCRIBE\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SubscribeInterceptTypePathPattern.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"SUBSCRIBE\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n\t<b:bean class=\"org.springframework.security.config.web.messaging.PathPatternMessageMatcherBuilderFactoryBean\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SubscribeInterceptTypePathPatternParser.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"SUBSCRIBE\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n\t<b:bean name=\"pathPatternParser\" class=\"org.springframework.web.util.pattern.PathPatternParser\">\n\t\t<b:property name=\"caseSensitive\" value=\"false\"/>\n\t</b:bean>\n\n\t<b:bean class=\"org.springframework.security.config.web.messaging.PathPatternMessageMatcherBuilderFactoryBean\">\n\t\t<b:constructor-arg ref=\"pathPatternParser\"/>\n\t</b:bean>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/sync.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/**\" access=\"permitAll\"/>\n\t</websocket-message-broker>\n\n\t<b:bean name=\"annotationExpressionTemplateDefaults\" class=\"org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncCustomArgumentResolverConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:websocket=\"http://www.springframework.org/schema/websocket\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/security\"\n\t\txsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/sync.xml\"/>\n\n\t<websocket:message-broker>\n\t\t<websocket:transport/>\n\t\t<websocket:stomp-endpoint path=\"/app\">\n\t\t\t<websocket:handshake-handler ref=\"testHandler\"/>\n\t\t\t<websocket:handshake-interceptors>\n\t\t\t\t<b:bean class=\"org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor\"/>\n\t\t\t</websocket:handshake-interceptors>\n\t\t</websocket:stomp-endpoint>\n\n\t\t<websocket:simple-broker prefix=\"/queue, /topic\"/>\n\t\t<websocket:argument-resolvers>\n\t\t\t<b:ref bean=\"messageArgumentResolver\"/>\n\t\t</websocket:argument-resolvers>\n\t</websocket:message-broker>\n\n\t<b:bean name=\"testHandler\" class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler\"/>\n\t<b:bean name=\"messageArgumentResolver\" class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.MessageArgumentResolver\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/**\" access=\"permitAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncSameOriginDisabledConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/sync.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker same-origin-disabled=\"true\">\n\t\t<intercept-message pattern=\"/**\" access=\"permitAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-SyncSockJsConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/sync.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket-sockjs.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/**\" access=\"permitAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-UnsubscribeInterceptTypeConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker>\n\t\t<intercept-message pattern=\"/permitAll\" type=\"UNSUBSCRIBE\" access=\"permitAll\"/>\n\t\t<intercept-message pattern=\"/**\" access=\"denyAll\"/>\n\t</websocket-message-broker>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-WithSecurityContextHolderStrategy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/controllers.xml\"/>\n\t<b:import resource=\"classpath:org/springframework/security/config/websocket/websocket.xml\"/>\n\n\t<websocket-message-broker security-context-holder-strategy-ref=\"ref\">\n\t\t<intercept-message pattern=\"/authenticated\" access=\"authenticated\"/>\n\t</websocket-message-broker>\n\n\t<b:bean id=\"ref\" class=\"org.mockito.Mockito\" factory-method=\"spy\">\n\t\t<b:constructor-arg>\n\t\t\t<b:bean class=\"org.springframework.security.config.MockSecurityContextHolderStrategy\"/>\n\t\t</b:constructor-arg>\n\t</b:bean>\n\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/controllers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/beans\"\n\t\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<bean class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.MessageController\"/>\n\t<bean class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.MessageWithArgumentController\"/>\n\n</beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/sync.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/beans\"\n\t\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<bean class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.InboundExecutorPostProcessor\"/>\n</beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/websocket-sockjs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/websocket\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\">\n\n\t<message-broker>\n\t\t<transport/>\n\t\t<stomp-endpoint path=\"/app\">\n\t\t\t<handshake-handler ref=\"testHandler\"/>\n\t\t\t<handshake-interceptors>\n\t\t\t\t<b:bean class=\"org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor\"/>\n\t\t\t</handshake-interceptors>\n\t\t\t<sockjs/>\n\t\t</stomp-endpoint>\n\n\t\t<simple-broker prefix=\"/queue, /topic\"/>\n\t</message-broker>\n\n\t<b:bean name=\"testHandler\" class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/config/websocket/websocket.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns=\"http://www.springframework.org/schema/websocket\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/websocket https://www.springframework.org/schema/websocket/spring-websocket.xsd\">\n\n\t<message-broker>\n\t\t<transport/>\n\t\t<stomp-endpoint path=\"/app\">\n\t\t\t<handshake-handler ref=\"testHandler\"/>\n\t\t\t<handshake-interceptors>\n\t\t\t\t<b:bean class=\"org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor\"/>\n\t\t\t</handshake-interceptors>\n\t\t</stomp-endpoint>\n\n\t\t<simple-broker prefix=\"/queue, /topic\"/>\n\t</message-broker>\n\n\t<b:bean name=\"testHandler\" class=\"org.springframework.security.config.websocket.WebSocketMessageBrokerConfigTests.TestHandshakeHandler\"/>\n</b:beans>\n"
  },
  {
    "path": "config/src/test/resources/org/springframework/security/util/filtertest-valid.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<beans default-lazy-init=\"true\" xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:sec=\"http://www.springframework.org/schema/security\"\n\txmlns:util=\"http://www.springframework.org/schema/util\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\thttp://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-3.0.xsd\n\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<bean id=\"mockFilter\" class=\"org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter\"/>\n\n\t<bean id=\"mockFilter2\" class=\"org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter\"/>\n\n\t<!-- These are just here so we have filters of a specific type to check the ordering is as expected -->\n\t<bean id=\"sif\" class=\"org.springframework.security.web.context.SecurityContextPersistenceFilter\"/>\n\n\t<bean id=\"apf\" class=\"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter\">\n\t   <property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n\t</bean>\n\n\t<sec:authentication-manager alias=\"authenticationManager\">\n\t\t<sec:authentication-provider>\n\t\t\t<sec:user-service>\n\t\t\t\t<sec:user name=\"jemima\" password=\"password\" authorities=\"A\"/>\n\t\t\t</sec:user-service>\n\t\t</sec:authentication-provider>\n\t</sec:authentication-manager>\n\n\t<bean id=\"mockNotAFilter\" class=\"org.springframework.security.web.util.matcher.AnyRequestMatcher\"/>\n\n\t<bean id=\"fooMatcher\" class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\">\n\t\t<constructor-arg value=\"/foo/**\"/>\n\t</bean>\n\n\t<bean id=\"filterChain\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<constructor-arg>\n\t\t   <list>\n\t\t\t   <sec:filter-chain request-matcher-ref=\"fooMatcher\" filters=\"mockFilter\"/>\n\t\t\t   <sec:filter-chain pattern=\"/some/other/path/**\" filters=\"mockFilter\"/>\n\t\t\t   <sec:filter-chain pattern=\"/do/not/filter\" filters=\"none\"/>\n\t\t   </list>\n\t\t</constructor-arg>\n\t</bean>\n\n<!-- TODO: Refactor to replace the above (SEC-1034: 'new' is now the only valid syntax) -->\n\t<bean id=\"newFilterChainProxy\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<constructor-arg>\n\t\t   <util:list>\n\t\t\t\t<sec:filter-chain pattern=\"/foo/**\" filters=\"mockFilter\"/>\n\t\t\t\t<sec:filter-chain pattern=\"/some/other/path/**\"\n\t\t\t\t\tfilters=\"\n\t\t\t\t\tsif,\n\t\t\t\t\tmockFilter,\n\t\t\t\t\tmockFilter2\"\n\t\t\t\t\t/>\n\t\t\t\t<sec:filter-chain pattern=\"/do/not/filter\" filters=\"none\"/>\n\t\t\t\t<sec:filter-chain pattern=\"/**\" filters=\"sif,apf,mockFilter\"/>\n\t\t   </util:list>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<bean id=\"newFilterChainProxyNoDefaultPath\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<constructor-arg>\n\t\t   <util:list>\n\t\t\t\t<sec:filter-chain pattern=\"/foo/**\" filters=\"mockFilter\"/>\n\t\t\t\t<sec:filter-chain pattern=\"/*.bar\" filters=\"mockFilter,mockFilter2\"/>\n\t\t   </util:list>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<bean id=\"newFilterChainProxyWrongPathOrder\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<constructor-arg>\n\t\t   <util:list>\n\t\t\t<sec:filter-chain pattern=\"/foo/**\" filters=\"mockFilter\"/>\n\t\t\t<sec:filter-chain pattern=\"/**\" filters=\"\n\t\t\t\t\t\t\t\tsif,\n\t\t\t\t\t\t\t\tapf,\n\t\t\t\t\t\t\t\tmockFilter\"/>\n\t\t\t<sec:filter-chain pattern=\"/some/other/path/**\" filters=\"sif,mockFilter,mockFilter2\"/>\n\t\t   </util:list>\n\t\t</constructor-arg>\n\t\t<property name=\"filterChainValidator\" ref=\"fcv\" />\n\t</bean>\n\n\t<bean id=\"fcv\" class=\"org.springframework.security.config.http.DefaultFilterChainValidator\" />\n\n\t<bean id=\"newFilterChainProxyRegex\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<sec:filter-chain-map request-matcher=\"regex\">\n\t\t\t<sec:filter-chain pattern=\"\\A/foo/.*\\Z\" filters=\"mockFilter\"/>\n\t\t\t<sec:filter-chain pattern=\"\\A/s[oO]me/other/path/.*\\Z\" filters=\"sif,mockFilter,mockFilter2\"/>\n\t\t\t<sec:filter-chain pattern=\"\\A/do/not/filter\\Z\" filters=\"none\"/>\n\t\t\t<sec:filter-chain pattern=\"\\A/.*\\Z\" filters=\"sif,apf,mockFilter\"/>\n\t\t</sec:filter-chain-map>\n\t</bean>\n\n\t<bean class=\"org.springframework.context.support.PropertySourcesPlaceholderConfigurer\" />\n\n\t<bean id=\"sec1235FilterChainProxy\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<constructor-arg>\n\t\t   <util:list>\n\t\t\t\t<sec:filter-chain pattern=\"${sec1235.pattern1}*\" filters=\"sif,apf,mockFilter\"/>\n\t\t\t\t<sec:filter-chain pattern=\"${sec1235.pattern2}\" filters=\"mockFilter2\"/>\n\t\t\t\t<sec:filter-chain pattern=\"/**\" filters=\"sif\"/>\n\t\t   </util:list>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<bean id=\"newFilterChainProxyNonNamespace\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<constructor-arg>\n\t\t\t<list>\n\t\t\t\t<bean class=\"org.springframework.security.web.DefaultSecurityFilterChain\">\n\t\t\t\t\t<constructor-arg ref=\"fooMatcher\"/>\n\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t<list>\n\t\t\t\t\t\t  <ref bean=\"mockFilter\"/>\n\t\t\t\t\t\t</list>\n\t\t\t\t\t</constructor-arg>\n\t\t\t\t</bean>\n\t\t\t\t<bean class=\"org.springframework.security.web.DefaultSecurityFilterChain\">\n\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t<bean class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\">\n\t\t\t\t\t\t\t<constructor-arg value=\"/some/other/path/**\"/>\n\t\t\t\t\t\t</bean>\n\t\t\t\t\t</constructor-arg>\n\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t<list>\n\t\t\t\t\t\t\t<ref bean=\"sif\"/>\n\t\t\t\t\t\t\t<ref bean=\"mockFilter\"/>\n\t\t\t\t\t\t\t<ref bean=\"mockFilter2\"/>\n\t\t\t\t\t\t</list>\n\t\t\t\t\t</constructor-arg>\n\t\t\t\t</bean>\n\t\t\t\t<bean class=\"org.springframework.security.web.DefaultSecurityFilterChain\">\n\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t<bean class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\">\n\t\t\t\t\t\t\t<constructor-arg value=\"/do/not/filter*\"/>\n\t\t\t\t\t\t</bean>\n\t\t\t\t\t</constructor-arg>\n\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t<list />\n\t\t\t\t\t</constructor-arg>\n\t\t\t\t</bean>\n\t\t\t\t<bean class=\"org.springframework.security.web.DefaultSecurityFilterChain\">\n\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t<bean class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\">\n\t\t\t\t\t\t\t<constructor-arg value=\"/**\"/>\n\t\t\t\t\t\t</bean>\n\t\t\t\t\t</constructor-arg>\n\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t<list>\n\t\t\t\t\t\t  <ref bean=\"sif\"/>\n\t\t\t\t\t\t  <ref bean=\"apf\"/>\n\t\t\t\t\t\t  <ref bean=\"mockFilter\"/>\n\t\t\t\t\t\t</list>\n\t\t\t\t\t</constructor-arg>\n\t\t\t\t</bean>\n\t\t\t</list>\n\t\t</constructor-arg>\n\t</bean>\n\n</beans>\n"
  },
  {
    "path": "config/src/test/resources/resources/file.js",
    "content": "var f = 1;"
  },
  {
    "path": "config/src/test/resources/users.properties",
    "content": "#\n# /*\n#  * Copyright 2004-present the original author or authors.\n#  *\n#  * Licensed under the Apache License, Version 2.0 (the \"License\");\n#  * you may not use this file except in compliance with the License.\n#  * You may obtain a copy of the License at\n#  *\n#  *      https://www.apache.org/licenses/LICENSE-2.0\n#  *\n#  * Unless required by applicable law or agreed to in writing, software\n#  * distributed under the License is distributed on an \"AS IS\" BASIS,\n#  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#  * See the License for the specific language governing permissions and\n#  * limitations under the License.\n#  */\n#\n\nuser={noop}password,ROLE_USER\n"
  },
  {
    "path": "core/spring-security-core.gradle",
    "content": "import java.util.concurrent.Callable\n\nplugins {\n\tid 'javadoc-warnings-error'\n\tid 'security-nullability'\n}\n\napply plugin: 'io.spring.convention.spring-module'\napply plugin: 'security-kotlin'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-crypto')\n\tapi 'org.springframework:spring-aop'\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-expression'\n\tapi 'io.micrometer:micrometer-observation'\n\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\toptional 'io.micrometer:context-propagation'\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'jakarta.annotation:jakarta.annotation-api'\n\toptional 'org.aspectj:aspectjrt'\n\toptional 'org.springframework:spring-jdbc'\n\toptional 'org.springframework:spring-tx'\n\toptional 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor'\n\toptional 'tools.jackson.core:jackson-databind'\n\n\ttestImplementation 'commons-collections:commons-collections'\n\ttestImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'\n\ttestImplementation 'io.projectreactor:reactor-test'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-core-test\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation 'org.skyscreamer:jsonassert'\n\ttestImplementation 'org.springframework:spring-test'\n\ttestImplementation 'org.jetbrains.kotlin:kotlin-reflect'\n\ttestImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'\n\ttestImplementation 'io.mockk:mockk'\n\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\ntask springVersion(type: org.gradle.api.tasks.WriteProperties) {\n\tdestinationFile = file(\"${buildDir}/versions/spring-security.versions\")\n\tproperty(\"org.springframework:spring-core\", springVersion())\n}\n\ntasks.processResources {\n\tinto('META-INF') {\n\t\tfrom project.tasks.springVersion.outputs\n\t}\n}\n\nconfigure(project.tasks.withType(Test)) {\n\tdoFirst {\n\t\tsystemProperties['springSecurityVersion'] = version\n\t\tsystemProperties['springVersion'] = springVersion().call()\n\t}\n}\n\nCallable<String> springVersion() {\n\treturn  (Callable<String>) { project.configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts\n    .find { it.name == 'spring-core' }.moduleVersion.id.version }\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/AccessDeniedException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.io.Serial;\n\n/**\n * Thrown if an {@link org.springframework.security.core.Authentication Authentication}\n * object does not hold a required authority.\n *\n * @author Ben Alex\n */\npublic class AccessDeniedException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 6395817500121599533L;\n\n\t/**\n\t * Constructs an <code>AccessDeniedException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic AccessDeniedException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>AccessDeniedException</code> with the specified message and\n\t * root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic AccessDeniedException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/AuthorizationServiceException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.io.Serial;\n\n/**\n * Thrown if an authorization request could not be processed due to a system problem.\n * <p>\n * This might be thrown if an <code>AccessDecisionManager</code> implementation could not\n * locate a required method argument, for example.\n *\n * @author Ben Alex\n */\npublic class AuthorizationServiceException extends AccessDeniedException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 4817857292041606900L;\n\n\t/**\n\t * Constructs an <code>AuthorizationServiceException</code> with the specified\n\t * message.\n\t * @param msg the detail message\n\t */\n\tpublic AuthorizationServiceException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>AuthorizationServiceException</code> with the specified message\n\t * and root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic AuthorizationServiceException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/PermissionCacheOptimizer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.util.Collection;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Allows permissions to be pre-cached when using pre or post filtering with expressions\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic interface PermissionCacheOptimizer extends AopInfrastructureBean {\n\n\t/**\n\t * Optimises the permission cache for anticipated operation on the supplied collection\n\t * of objects. Usually this will entail batch loading of permissions for the objects\n\t * in the collection.\n\t * @param a the user for whom permissions should be obtained.\n\t * @param objects the (non-null) collection of domain objects for which permissions\n\t * should be retrieved.\n\t */\n\tvoid cachePermissionsFor(Authentication a, Collection<?> objects);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/PermissionEvaluator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Strategy used in expression evaluation to determine whether a user has a permission or\n * permissions for a given domain object.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface PermissionEvaluator extends AopInfrastructureBean {\n\n\t/**\n\t * @param authentication represents the user in question. Should not be null.\n\t * @param targetDomainObject the domain object for which permissions should be\n\t * checked. May be null in which case implementations should return false, as the null\n\t * condition can be checked explicitly in the expression.\n\t * @param permission a representation of the permission object as supplied by the\n\t * expression system. Not null.\n\t * @return true if the permission is granted, false otherwise\n\t */\n\tboolean hasPermission(Authentication authentication, @Nullable Object targetDomainObject, Object permission);\n\n\t/**\n\t * Alternative method for evaluating a permission where only the identifier of the\n\t * target object is available, rather than the target instance itself.\n\t * @param authentication represents the user in question. Should not be null.\n\t * @param targetId the identifier for the object instance (usually a Long)\n\t * @param targetType a String representing the target's type (usually a Java\n\t * classname). Not null.\n\t * @param permission a representation of the permission object as supplied by the\n\t * expression system. Not null.\n\t * @return true if the permission is granted, false otherwise\n\t */\n\tboolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/annotation/Secured.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Java 5 annotation for describing service layer security attributes.\n *\n * <p>\n * The <code>Secured</code> annotation is used to define a list of security configuration\n * attributes for business methods. This annotation can be used as a Java 5 alternative to\n * XML configuration.\n * <p>\n * For example:\n *\n * <pre>\n * &#064;Secured({ &quot;ROLE_USER&quot; })\n * public void create(Contact contact);\n *\n * &#064;Secured({ &quot;ROLE_USER&quot;, &quot;ROLE_ADMIN&quot; })\n * public void update(Contact contact);\n *\n * &#064;Secured({ &quot;ROLE_ADMIN&quot; })\n * public void delete(Contact contact);\n * </pre>\n *\n * @author Mark St.Godard\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface Secured {\n\n\t/**\n\t * Returns the list of security configuration attributes (e.g.&nbsp;ROLE_USER,\n\t * ROLE_ADMIN).\n\t * @return String[] The secure method attributes\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for JSR-250 and Spring Security {@code @Secured} annotations.\n */\n@NullMarked\npackage org.springframework.security.access.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.expression.BeanFactoryResolver;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Base implementation of the facade which isolates Spring Security's requirements for\n * evaluating security expressions from the implementation of the underlying expression\n * objects.\n *\n * @author Luke Taylor\n * @author Evgeniy Cheban\n * @author Steve Riesenberg\n * @since 3.1\n */\npublic abstract class AbstractSecurityExpressionHandler<T>\n\t\timplements SecurityExpressionHandler<T>, ApplicationContextAware {\n\n\tprivate ExpressionParser expressionParser = new SpelExpressionParser();\n\n\tprivate @Nullable BeanResolver beanResolver;\n\n\tprivate @Nullable RoleHierarchy roleHierarchy;\n\n\tprivate AuthorizationManagerFactory<T> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();\n\n\tprivate PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();\n\n\t@Override\n\tpublic final ExpressionParser getExpressionParser() {\n\t\treturn this.expressionParser;\n\t}\n\n\tpublic final void setExpressionParser(ExpressionParser expressionParser) {\n\t\tAssert.notNull(expressionParser, \"expressionParser cannot be null\");\n\t\tthis.expressionParser = expressionParser;\n\t}\n\n\t/**\n\t * Invokes the internal template methods to create {@code StandardEvaluationContext}\n\t * and {@code SecurityExpressionRoot} objects.\n\t * @param authentication the current authentication object\n\t * @param invocation the invocation (filter, method, channel)\n\t * @return the context object for use in evaluating the expression, populated with a\n\t * suitable root object.\n\t */\n\t@Override\n\tpublic final EvaluationContext createEvaluationContext(@Nullable Authentication authentication, T invocation) {\n\t\tSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, invocation);\n\t\tStandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);\n\t\tif (this.beanResolver != null) {\n\t\t\tctx.setBeanResolver(this.beanResolver);\n\t\t}\n\t\tctx.setRootObject(root);\n\t\treturn ctx;\n\t}\n\n\t/**\n\t * Override to create a custom instance of {@code StandardEvaluationContext}.\n\t * <p>\n\t * The returned object will have a {@code SecurityExpressionRootPropertyAccessor}\n\t * added, allowing beans in the {@code ApplicationContext} to be accessed via\n\t * expression properties.\n\t * @param authentication the current authentication object\n\t * @param invocation the invocation (filter, method, channel)\n\t * @return A {@code StandardEvaluationContext} or potentially a custom subclass if\n\t * overridden.\n\t */\n\tprotected StandardEvaluationContext createEvaluationContextInternal(@Nullable Authentication authentication,\n\t\t\tT invocation) {\n\t\treturn new StandardEvaluationContext();\n\t}\n\n\t/**\n\t * Implement in order to create a root object of the correct type for the supported\n\t * invocation type.\n\t * @param authentication the current authentication object\n\t * @param invocation the invocation (filter, method, channel)\n\t * @return the object\n\t */\n\tprotected abstract SecurityExpressionOperations createSecurityExpressionRoot(\n\t\t\t@Nullable Authentication authentication, T invocation);\n\n\t/**\n\t * Sets the {@link AuthorizationManagerFactory} to be used. The default is\n\t * {@link DefaultAuthorizationManagerFactory}.\n\t * @param authorizationManagerFactory the {@link AuthorizationManagerFactory} to use.\n\t * Cannot be null.\n\t * @since 7.0\n\t */\n\tpublic final void setAuthorizationManagerFactory(AuthorizationManagerFactory<T> authorizationManagerFactory) {\n\t\tAssert.notNull(authorizationManagerFactory, \"authorizationManagerFactory cannot be null\");\n\t\tthis.authorizationManagerFactory = authorizationManagerFactory;\n\t}\n\n\tprotected final AuthorizationManagerFactory<T> getAuthorizationManagerFactory() {\n\t\treturn this.authorizationManagerFactory;\n\t}\n\n\t/**\n\t * Allows accessing the {@link DefaultAuthorizationManagerFactory} for getting and\n\t * setting defaults. This method will be removed in Spring Security 8.\n\t * @return the {@link DefaultAuthorizationManagerFactory}\n\t * @throws IllegalStateException if a different {@link AuthorizationManagerFactory}\n\t * was already set\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tprotected final DefaultAuthorizationManagerFactory<T> getDefaultAuthorizationManagerFactory() {\n\t\tif (!(this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T> defaultAuthorizationManagerFactory)) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"authorizationManagerFactory must be an instance of DefaultAuthorizationManagerFactory\");\n\t\t}\n\n\t\treturn defaultAuthorizationManagerFactory;\n\t}\n\n\t/**\n\t * @deprecated Use {@link #getDefaultAuthorizationManagerFactory()} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tprotected @Nullable RoleHierarchy getRoleHierarchy() {\n\t\treturn this.roleHierarchy;\n\t}\n\n\t/**\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {\n\t\tif (roleHierarchy != null) {\n\t\t\tgetDefaultAuthorizationManagerFactory().setRoleHierarchy(roleHierarchy);\n\t\t}\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n\tprotected PermissionEvaluator getPermissionEvaluator() {\n\t\treturn this.permissionEvaluator;\n\t}\n\n\tpublic void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {\n\t\tthis.permissionEvaluator = permissionEvaluator;\n\t}\n\n\tprotected @Nullable BeanResolver getBeanResolver() {\n\t\treturn this.beanResolver;\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) {\n\t\tthis.beanResolver = new BeanFactoryResolver(applicationContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/DenyAllPermissionEvaluator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.core.Authentication;\n\n/**\n * A null PermissionEvaluator which denies all access. Used by default for situations when\n * permission evaluation should not be required.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class DenyAllPermissionEvaluator implements PermissionEvaluator {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t/**\n\t * @return false always\n\t */\n\t@Override\n\tpublic boolean hasPermission(Authentication authentication, @Nullable Object target, Object permission) {\n\t\tthis.logger.warn(LogMessage.format(\"Denying user %s permission '%s' on object %s\", authentication.getName(),\n\t\t\t\tpermission, target));\n\t\treturn false;\n\t}\n\n\t/**\n\t * @return false always\n\t */\n\t@Override\n\tpublic boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,\n\t\t\tObject permission) {\n\t\tthis.logger.warn(LogMessage.format(\"Denying user %s permission '%s' on object with Id %s\",\n\t\t\t\tauthentication.getName(), permission, targetId));\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/ExpressionUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.EvaluationException;\nimport org.springframework.expression.Expression;\n\npublic final class ExpressionUtils {\n\n\tprivate ExpressionUtils() {\n\t}\n\n\tpublic static boolean evaluateAsBoolean(Expression expr, EvaluationContext ctx) {\n\t\ttry {\n\t\t\tBoolean result = expr.getValue(ctx, Boolean.class);\n\t\t\tif (result == null) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Expression was null but expected boolean result '\" + expr.getExpressionString() + \"'\");\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\tcatch (EvaluationException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to evaluate expression '\" + expr.getExpressionString() + \"'\",\n\t\t\t\t\tex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/SecurityExpressionHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Facade which isolates Spring Security's requirements for evaluating security\n * expressions from the implementation of the underlying expression objects\n *\n * @author Luke Taylor\n * @author Evgeniy Cheban\n * @since 3.1\n */\npublic interface SecurityExpressionHandler<T> extends AopInfrastructureBean {\n\n\t/**\n\t * @return an expression parser for the expressions used by the implementation.\n\t */\n\tExpressionParser getExpressionParser();\n\n\t/**\n\t * Provides an evaluation context in which to evaluate security expressions for the\n\t * invocation type.\n\t */\n\tEvaluationContext createEvaluationContext(@Nullable Authentication authentication, T invocation);\n\n\t/**\n\t * Provides an evaluation context in which to evaluate security expressions for the\n\t * invocation type. You can override this method in order to provide a custom\n\t * implementation that uses lazy initialization of the {@link Authentication} object.\n\t * By default, this method uses eager initialization of the {@link Authentication}\n\t * object.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to use\n\t * @param invocation the {@link T} to use\n\t * @return the {@link EvaluationContext} to use\n\t * @since 5.8\n\t */\n\tdefault EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tT invocation) {\n\t\treturn createEvaluationContext(authentication.get(), invocation);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/SecurityExpressionOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Standard interface for expression root objects used with expression-based security.\n *\n * @author Andrei Stefan\n * @author Luke Taylor\n * @since 3.1.1\n */\npublic interface SecurityExpressionOperations {\n\n\t/**\n\t * Gets the {@link Authentication} used for evaluating the expressions\n\t * @return the {@link Authentication} for evaluating the expressions\n\t */\n\tAuthentication getAuthentication();\n\n\t/**\n\t * Determines if the {@link #getAuthentication()} has a particular authority within\n\t * {@link Authentication#getAuthorities()}.\n\t * @param authority the authority to test (i.e. \"ROLE_USER\")\n\t * @return true if the authority is found, else false\n\t */\n\tboolean hasAuthority(String authority);\n\n\t/**\n\t * Determines if the {@link #getAuthentication()} has any of the specified authorities\n\t * within {@link Authentication#getAuthorities()}.\n\t * @param authorities the authorities to test (i.e. \"ROLE_USER\", \"ROLE_ADMIN\")\n\t * @return true if any of the authorities is found, else false\n\t */\n\tboolean hasAnyAuthority(String... authorities);\n\n\t/**\n\t * <p>\n\t * Determines if the {@link #getAuthentication()} has a particular authority within\n\t * {@link Authentication#getAuthorities()}.\n\t * </p>\n\t * <p>\n\t * This is similar to {@link #hasAuthority(String)} except that this method implies\n\t * that the String passed in is a role. For example, if \"USER\" is passed in the\n\t * implementation may convert it to use \"ROLE_USER\" instead. The way in which the role\n\t * is converted may depend on the implementation settings.\n\t * </p>\n\t * @param role the authority to test (i.e. \"USER\")\n\t * @return true if the authority is found, else false\n\t */\n\tboolean hasRole(String role);\n\n\t/**\n\t * <p>\n\t * Determines if the {@link #getAuthentication()} has any of the specified authorities\n\t * within {@link Authentication#getAuthorities()}.\n\t * </p>\n\t * <p>\n\t * This is a similar to hasAnyAuthority except that this method implies that the\n\t * String passed in is a role. For example, if \"USER\" is passed in the implementation\n\t * may convert it to use \"ROLE_USER\" instead. The way in which the role is converted\n\t * may depend on the implementation settings.\n\t * </p>\n\t * @param roles the authorities to test (i.e. \"USER\", \"ADMIN\")\n\t * @return true if any of the authorities is found, else false\n\t */\n\tboolean hasAnyRole(String... roles);\n\n\t/**\n\t * Always grants access.\n\t * @return true\n\t */\n\tboolean permitAll();\n\n\t/**\n\t * Always denies access\n\t * @return false\n\t */\n\tboolean denyAll();\n\n\t/**\n\t * Determines if the {@link #getAuthentication()} is anonymous\n\t * @return true if the user is anonymous, else false\n\t */\n\tboolean isAnonymous();\n\n\t/**\n\t * Determines if the {@link #getAuthentication()} is authenticated\n\t * @return true if the {@link #getAuthentication()} is authenticated, else false\n\t */\n\tboolean isAuthenticated();\n\n\t/**\n\t * Determines if the {@link #getAuthentication()} was authenticated using remember me\n\t * @return true if the {@link #getAuthentication()} authenticated using remember me,\n\t * else false\n\t */\n\tboolean isRememberMe();\n\n\t/**\n\t * Determines if the {@link #getAuthentication()} authenticated without the use of\n\t * remember me\n\t * @return true if the {@link #getAuthentication()} authenticated without the use of\n\t * remember me, else false\n\t */\n\tboolean isFullyAuthenticated();\n\n\t/**\n\t * Determines if the {@link #getAuthentication()} has permission to access the target\n\t * given the permission\n\t * @param target the target domain object to check permission on\n\t * @param permission the permission to check on the domain object (i.e. \"read\",\n\t * \"write\", etc.).\n\t * @return true if permission is granted to the {@link #getAuthentication()}, else\n\t * false\n\t */\n\tboolean hasPermission(Object target, Object permission);\n\n\t/**\n\t * Determines if the {@link #getAuthentication()} has permission to access the domain\n\t * object with a given id, type, and permission.\n\t * @param targetId the identifier of the domain object to determine access\n\t * @param targetType the type (i.e. com.example.domain.Message)\n\t * @param permission the permission to check on the domain object (i.e. \"read\",\n\t * \"write\", etc.)\n\t * @return true if permission is granted to the {@link #getAuthentication()}, else\n\t * false\n\t */\n\tboolean hasPermission(Object targetId, String targetType, Object permission);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport java.io.Serializable;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.util.function.SingletonSupplier;\n\n/**\n * Base root object for use in Spring Security expression evaluations.\n *\n * @author Luke Taylor\n * @author Evgeniy Cheban\n * @author Steve Riesenberg\n * @author Ngoc Nhan\n * @since 3.0\n */\npublic abstract class SecurityExpressionRoot<T extends @Nullable Object> implements SecurityExpressionOperations {\n\n\tprivate final Supplier<Authentication> authentication;\n\n\tprivate String defaultRolePrefix = \"ROLE_\";\n\n\tprivate final T object;\n\n\tprivate AuthorizationManagerFactory<T> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();\n\n\t/**\n\t * Allows \"permitAll\" expression\n\t */\n\tpublic final boolean permitAll = true;\n\n\t/**\n\t * Allows \"denyAll\" expression\n\t */\n\tpublic final boolean denyAll = false;\n\n\tprivate PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();\n\n\tpublic final String read = \"read\";\n\n\tpublic final String write = \"write\";\n\n\tpublic final String create = \"create\";\n\n\tpublic final String delete = \"delete\";\n\n\tpublic final String admin = \"administration\";\n\n\t/**\n\t * Creates a new instance\n\t * @param authentication the {@link Authentication} to use. Cannot be null.\n\t * @deprecated Use {@link #SecurityExpressionRoot(Supplier, Object)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\t@SuppressWarnings(\"NullAway\")\n\tpublic SecurityExpressionRoot(@Nullable Authentication authentication) {\n\t\tthis(() -> authentication, null);\n\t}\n\n\t/**\n\t * Creates a new instance that uses lazy initialization of the {@link Authentication}\n\t * object.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to use.\n\t * Cannot be null.\n\t * @since 5.8\n\t * @deprecated Use {@link #SecurityExpressionRoot(Supplier, Object)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\t@SuppressWarnings(\"NullAway\")\n\tpublic SecurityExpressionRoot(Supplier<@Nullable Authentication> authentication) {\n\t\tthis(authentication, null);\n\t}\n\n\t/**\n\t * Creates a new instance that uses lazy initialization of the {@link Authentication}\n\t * object.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to use.\n\t * Cannot be null.\n\t * @param object the object being authorized\n\t * @since 7.0\n\t */\n\tpublic SecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication, T object) {\n\t\tthis.authentication = SingletonSupplier.of(() -> {\n\t\t\tAuthentication value = authentication.get();\n\t\t\tAssert.notNull(value, \"Authentication object cannot be null\");\n\t\t\treturn value;\n\t\t});\n\t\tthis.object = object;\n\t}\n\n\t@Override\n\tpublic final boolean hasAuthority(String authority) {\n\t\treturn isGranted(this.authorizationManagerFactory.hasAuthority(authority));\n\t}\n\n\t@Override\n\tpublic final boolean hasAnyAuthority(String... authorities) {\n\t\treturn isGranted(this.authorizationManagerFactory.hasAnyAuthority(authorities));\n\t}\n\n\tpublic final boolean hasAllAuthorities(String... authorities) {\n\t\tAuthorizationManager<T> manager = this.authorizationManagerFactory.hasAllAuthorities(authorities);\n\t\treturn isGranted(manager);\n\t}\n\n\t@Override\n\tpublic final boolean hasRole(String role) {\n\t\tif (this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T>) {\n\t\t\t// To provide passivity for old behavior where hasRole('ROLE_A') is allowed,\n\t\t\t// we strip the role prefix when found.\n\t\t\t// TODO: Remove in favor of fixing inconsistent behavior?\n\t\t\tString rolePrefix = this.defaultRolePrefix;\n\t\t\tif (role.startsWith(rolePrefix)) {\n\t\t\t\trole = role.substring(rolePrefix.length());\n\t\t\t}\n\t\t}\n\t\treturn isGranted(this.authorizationManagerFactory.hasRole(role));\n\t}\n\n\t@Override\n\tpublic final boolean hasAnyRole(String... roles) {\n\t\tif (this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T>) {\n\t\t\t// To provide passivity for old behavior where hasRole('ROLE_A') is allowed,\n\t\t\t// we strip the role prefix when found.\n\t\t\t// TODO: Remove in favor of fixing inconsistent behavior?\n\t\t\tString rolePrefix = this.defaultRolePrefix;\n\t\t\tfor (int index = 0; index < roles.length; index++) {\n\t\t\t\tString role = roles[index];\n\t\t\t\tif (role.startsWith(rolePrefix)) {\n\t\t\t\t\troles[index] = role.substring(rolePrefix.length());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn isGranted(this.authorizationManagerFactory.hasAnyRole(roles));\n\t}\n\n\tpublic final boolean hasAllRoles(String... roles) {\n\t\tAuthorizationManager<T> manager = this.authorizationManagerFactory.hasAllRoles(roles);\n\t\treturn isGranted(manager);\n\t}\n\n\t@Override\n\tpublic final Authentication getAuthentication() {\n\t\treturn this.authentication.get();\n\t}\n\n\t@Override\n\tpublic final boolean permitAll() {\n\t\treturn isGranted(this.authorizationManagerFactory.permitAll());\n\t}\n\n\t@Override\n\tpublic final boolean denyAll() {\n\t\treturn isGranted(this.authorizationManagerFactory.denyAll());\n\t}\n\n\t@Override\n\tpublic final boolean isAnonymous() {\n\t\treturn isGranted(this.authorizationManagerFactory.anonymous());\n\t}\n\n\t@Override\n\tpublic final boolean isAuthenticated() {\n\t\treturn isGranted(this.authorizationManagerFactory.authenticated());\n\t}\n\n\t@Override\n\tpublic final boolean isRememberMe() {\n\t\treturn isGranted(this.authorizationManagerFactory.rememberMe());\n\t}\n\n\t@Override\n\tpublic final boolean isFullyAuthenticated() {\n\t\treturn isGranted(this.authorizationManagerFactory.fullyAuthenticated());\n\t}\n\n\tprivate boolean isGranted(AuthorizationManager<T> authorizationManager) {\n\t\tAuthorizationResult authorizationResult = authorizationManager.authorize(this.authentication, this.object);\n\t\treturn (authorizationResult != null && authorizationResult.isGranted());\n\t}\n\n\t/**\n\t * Convenience method to access {@link Authentication#getPrincipal()} from\n\t * {@link #getAuthentication()}\n\t * @return the {@code Principal} being authenticated or the authenticated principal\n\t * after authentication.\n\t */\n\tpublic @Nullable Object getPrincipal() {\n\t\treturn getAuthentication().getPrincipal();\n\t}\n\n\t/**\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tgetDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);\n\t}\n\n\t/**\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {\n\t\tif (roleHierarchy != null) {\n\t\t\tgetDefaultAuthorizationManagerFactory().setRoleHierarchy(roleHierarchy);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the default prefix to be added to {@link #hasAnyRole(String...)} or\n\t * {@link #hasRole(String)}. For example, if hasRole(\"ADMIN\") or hasRole(\"ROLE_ADMIN\")\n\t * is passed in, then the role ROLE_ADMIN will be used when the defaultRolePrefix is\n\t * \"ROLE_\" (default).\n\t * </p>\n\t *\n\t * <p>\n\t * If null or empty, then no default role prefix is used.\n\t * </p>\n\t * @param defaultRolePrefix the default prefix to add to roles. Default \"ROLE_\".\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setDefaultRolePrefix(@Nullable String defaultRolePrefix) {\n\t\tif (defaultRolePrefix == null) {\n\t\t\tdefaultRolePrefix = \"\";\n\t\t}\n\t\tgetDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);\n\t\tthis.defaultRolePrefix = defaultRolePrefix;\n\t}\n\n\t/**\n\t * Sets the {@link AuthorizationManagerFactory} to use for creating instances of\n\t * {@link AuthorizationManager}.\n\t * @param authorizationManagerFactory the {@link AuthorizationManagerFactory} to use\n\t * @since 7.0\n\t */\n\tpublic void setAuthorizationManagerFactory(AuthorizationManagerFactory<T> authorizationManagerFactory) {\n\t\tAssert.notNull(authorizationManagerFactory, \"authorizationManagerFactory cannot be null\");\n\t\tthis.authorizationManagerFactory = authorizationManagerFactory;\n\t}\n\n\t/**\n\t * Allows accessing the {@link DefaultAuthorizationManagerFactory} for getting and\n\t * setting defaults. This method will be removed in Spring Security 8.\n\t * @return the {@link DefaultAuthorizationManagerFactory}\n\t * @throws IllegalStateException if a different {@link AuthorizationManagerFactory}\n\t * was already set\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\", forRemoval = true)\n\tprivate DefaultAuthorizationManagerFactory<T> getDefaultAuthorizationManagerFactory() {\n\t\tif (!(this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<T> defaultAuthorizationManagerFactory)) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"authorizationManagerFactory must be an instance of DefaultAuthorizationManagerFactory\");\n\t\t}\n\n\t\treturn defaultAuthorizationManagerFactory;\n\t}\n\n\t@Override\n\tpublic boolean hasPermission(Object target, Object permission) {\n\t\treturn this.permissionEvaluator.hasPermission(getAuthentication(), target, permission);\n\t}\n\n\t@Override\n\tpublic boolean hasPermission(Object targetId, String targetType, Object permission) {\n\t\treturn this.permissionEvaluator.hasPermission(getAuthentication(), (Serializable) targetId, targetType,\n\t\t\t\tpermission);\n\t}\n\n\tpublic void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {\n\t\tAssert.notNull(permissionEvaluator, \"permissionEvaluator cannot be null\");\n\t\tthis.permissionEvaluator = permissionEvaluator;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.lang.reflect.Array;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.TypedValue;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.access.PermissionCacheOptimizer;\nimport org.springframework.security.access.expression.AbstractSecurityExpressionHandler;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer;\nimport org.springframework.util.Assert;\n\n/**\n * The standard implementation of {@code MethodSecurityExpressionHandler}.\n * <p>\n * A single instance should usually be shared amongst the beans that require expression\n * support.\n *\n * @author Luke Taylor\n * @author Evgeniy Cheban\n * @author Blagoja Stamatovski\n * @author Steve Riesenberg\n * @since 3.0\n */\npublic class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>\n\t\timplements MethodSecurityExpressionHandler {\n\n\tprivate static final String DEFAULT_ROLE_PREFIX = \"ROLE_\";\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate ParameterNameDiscoverer parameterNameDiscoverer = new DefaultSecurityParameterNameDiscoverer();\n\n\tprivate @Nullable PermissionCacheOptimizer permissionCacheOptimizer = null;\n\n\tprivate String defaultRolePrefix = DEFAULT_ROLE_PREFIX;\n\n\tpublic DefaultMethodSecurityExpressionHandler() {\n\t}\n\n\t/**\n\t * Uses a {@link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt>\n\t * implementation.\n\t */\n\t@Override\n\tpublic StandardEvaluationContext createEvaluationContextInternal(@Nullable Authentication auth,\n\t\t\tMethodInvocation mi) {\n\t\treturn new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // FIXME: Dataflow analysis limitation\n\tpublic EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMethodInvocation mi) {\n\t\tMethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);\n\t\tMethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,\n\t\t\t\tgetParameterNameDiscoverer());\n\t\tOptional.ofNullable(getBeanResolver()).ifPresent(ctx::setBeanResolver);\n\t\treturn ctx;\n\t}\n\n\t/**\n\t * Creates the root object for expression evaluation.\n\t */\n\t@Override\n\tprotected MethodSecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,\n\t\t\tMethodInvocation invocation) {\n\t\treturn createSecurityExpressionRoot(() -> authentication, invocation);\n\t}\n\n\tprivate MethodSecurityExpressionOperations createSecurityExpressionRoot(\n\t\t\tSupplier<? extends @Nullable Authentication> authentication, MethodInvocation invocation) {\n\t\tMethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication, invocation);\n\t\troot.setThis(invocation.getThis());\n\t\troot.setAuthorizationManagerFactory(getAuthorizationManagerFactory());\n\t\troot.setPermissionEvaluator(getPermissionEvaluator());\n\t\tif (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {\n\t\t\t// Ensure SecurityExpressionRoot can strip the custom role prefix\n\t\t\troot.setDefaultRolePrefix(getDefaultRolePrefix());\n\t\t}\n\t\treturn root;\n\t}\n\n\t/**\n\t * Filters the {@code filterTarget} object (which must be either a {@link Collection},\n\t * {@code Array}, {@link Map} or {@link Stream}), by evaluating the supplied\n\t * expression.\n\t * <p>\n\t * Returns new instances of the same type as the supplied {@code filterTarget} object\n\t * @return The filtered {@link Collection}, {@code Array}, {@link Map} or\n\t * {@link Stream}\n\t */\n\t@Override\n\tpublic Object filter(@Nullable Object filterTarget, Expression filterExpression, EvaluationContext ctx) {\n\t\tMethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations) ctx.getRootObject()\n\t\t\t.getValue();\n\t\tAssert.notNull(rootObject, \"rootObject cannot be null\");\n\t\tthis.logger.debug(LogMessage.format(\"Filtering with expression: %s\", filterExpression.getExpressionString()));\n\t\tif (filterTarget instanceof Collection) {\n\t\t\treturn filterCollection((Collection<?>) filterTarget, filterExpression, ctx, rootObject);\n\t\t}\n\t\tif (filterTarget != null && filterTarget.getClass().isArray()) {\n\t\t\treturn filterArray((Object[]) filterTarget, filterExpression, ctx, rootObject);\n\t\t}\n\t\tif (filterTarget instanceof Map) {\n\t\t\treturn filterMap((Map<?, ?>) filterTarget, filterExpression, ctx, rootObject);\n\t\t}\n\t\tif (filterTarget instanceof Stream) {\n\t\t\treturn filterStream((Stream<?>) filterTarget, filterExpression, ctx, rootObject);\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Filter target must be a collection, array, map or stream type, but was \" + filterTarget);\n\t}\n\n\tprivate <T> Object filterCollection(Collection<T> filterTarget, Expression filterExpression, EvaluationContext ctx,\n\t\t\tMethodSecurityExpressionOperations rootObject) {\n\t\tthis.logger.debug(LogMessage.format(\"Filtering collection with %s elements\", filterTarget.size()));\n\t\tList<T> retain = new ArrayList<>(filterTarget.size());\n\t\tif (this.permissionCacheOptimizer != null) {\n\t\t\tthis.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(), filterTarget);\n\t\t}\n\t\tfor (T filterObject : filterTarget) {\n\t\t\trootObject.setFilterObject(filterObject);\n\t\t\tif (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {\n\t\t\t\tretain.add(filterObject);\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(LogMessage.format(\"Retaining elements: %s\", retain));\n\t\ttry {\n\t\t\tfilterTarget.clear();\n\t\t\tfilterTarget.addAll(retain);\n\t\t\treturn filterTarget;\n\t\t}\n\t\tcatch (UnsupportedOperationException readonly) {\n\t\t\tthis.logger.trace(LogMessage.format(\n\t\t\t\t\t\"Collection threw exception: %s. Will return a new instance instead of mutating its state.\",\n\t\t\t\t\treadonly.getMessage()));\n\t\t\treturn retain;\n\t\t}\n\t}\n\n\tprivate Object filterArray(Object[] filterTarget, Expression filterExpression, EvaluationContext ctx,\n\t\t\tMethodSecurityExpressionOperations rootObject) {\n\t\tList<Object> retain = new ArrayList<>(filterTarget.length);\n\t\tthis.logger.debug(LogMessage.format(\"Filtering array with %s elements\", filterTarget.length));\n\t\tif (this.permissionCacheOptimizer != null) {\n\t\t\tthis.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(),\n\t\t\t\t\tArrays.asList(filterTarget));\n\t\t}\n\t\tfor (Object filterObject : filterTarget) {\n\t\t\trootObject.setFilterObject(filterObject);\n\t\t\tif (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {\n\t\t\t\tretain.add(filterObject);\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(LogMessage.format(\"Retaining elements: %s\", retain));\n\t\tObject[] filtered = (Object[]) Array.newInstance(filterTarget.getClass().getComponentType(), retain.size());\n\t\tfor (int i = 0; i < retain.size(); i++) {\n\t\t\tfiltered[i] = retain.get(i);\n\t\t}\n\t\treturn filtered;\n\t}\n\n\tprivate <K, V> Object filterMap(Map<K, V> filterTarget, Expression filterExpression, EvaluationContext ctx,\n\t\t\tMethodSecurityExpressionOperations rootObject) {\n\t\tMap<K, V> retain = new LinkedHashMap<>(filterTarget.size());\n\t\tthis.logger.debug(LogMessage.format(\"Filtering map with %s elements\", filterTarget.size()));\n\t\tfor (Map.Entry<K, V> filterObject : filterTarget.entrySet()) {\n\t\t\trootObject.setFilterObject(filterObject);\n\t\t\tif (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {\n\t\t\t\tretain.put(filterObject.getKey(), filterObject.getValue());\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(LogMessage.format(\"Retaining elements: %s\", retain));\n\t\ttry {\n\t\t\tfilterTarget.clear();\n\t\t\tfilterTarget.putAll(retain);\n\t\t\treturn filterTarget;\n\t\t}\n\t\tcatch (UnsupportedOperationException readonly) {\n\t\t\tthis.logger.trace(LogMessage.format(\n\t\t\t\t\t\"Map threw exception: %s. Will return a new instance instead of mutating its state.\",\n\t\t\t\t\treadonly.getMessage()));\n\t\t\treturn retain;\n\t\t}\n\t}\n\n\tprivate Object filterStream(final Stream<?> filterTarget, Expression filterExpression, EvaluationContext ctx,\n\t\t\tMethodSecurityExpressionOperations rootObject) {\n\t\treturn filterTarget.filter((filterObject) -> {\n\t\t\trootObject.setFilterObject(filterObject);\n\t\t\treturn ExpressionUtils.evaluateAsBoolean(filterExpression, ctx);\n\t\t}).onClose(filterTarget::close);\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. The default is\n\t * {@link AuthenticationTrustResolverImpl}.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be\n\t * null.\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\tgetDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);\n\t\tthis.trustResolver = trustResolver;\n\t}\n\n\t/**\n\t * @return The current {@link AuthenticationTrustResolver}\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tprotected AuthenticationTrustResolver getTrustResolver() {\n\t\treturn this.trustResolver;\n\t}\n\n\t/**\n\t * Sets the {@link ParameterNameDiscoverer} to use. The default is\n\t * {@link DefaultSecurityParameterNameDiscoverer}.\n\t * @param parameterNameDiscoverer\n\t */\n\tpublic void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {\n\t\tthis.parameterNameDiscoverer = parameterNameDiscoverer;\n\t}\n\n\t/**\n\t * @return The current {@link ParameterNameDiscoverer}\n\t */\n\tprotected ParameterNameDiscoverer getParameterNameDiscoverer() {\n\t\treturn this.parameterNameDiscoverer;\n\t}\n\n\tpublic void setPermissionCacheOptimizer(PermissionCacheOptimizer permissionCacheOptimizer) {\n\t\tthis.permissionCacheOptimizer = permissionCacheOptimizer;\n\t}\n\n\t@Override\n\tpublic void setReturnObject(@Nullable Object returnObject, EvaluationContext ctx) {\n\t\tTypedValue rootObject = ctx.getRootObject();\n\t\tAssert.notNull(rootObject, \"rootObject cannot be null\");\n\t\tMethodSecurityExpressionOperations methodOperations = (MethodSecurityExpressionOperations) rootObject\n\t\t\t.getValue();\n\t\tAssert.notNull(methodOperations, \"MethodSecurityExpressionOperations cannot be null\");\n\t\tmethodOperations.setReturnObject(returnObject);\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the default prefix to be added to\n\t * {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasAnyRole(String...)}\n\t * or\n\t * {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasRole(String)}.\n\t * For example, if hasRole(\"ADMIN\") or hasRole(\"ROLE_ADMIN\") is passed in, then the\n\t * role ROLE_ADMIN will be used when the defaultRolePrefix is \"ROLE_\" (default).\n\t * </p>\n\t *\n\t * <p>\n\t * If null or empty, then no default role prefix is used.\n\t * </p>\n\t * @param defaultRolePrefix the default prefix to add to roles. Default \"ROLE_\".\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setDefaultRolePrefix(@Nullable String defaultRolePrefix) {\n\t\tif (defaultRolePrefix == null) {\n\t\t\tdefaultRolePrefix = \"\";\n\t\t}\n\t\tgetDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);\n\t\tthis.defaultRolePrefix = defaultRolePrefix;\n\t}\n\n\t/**\n\t * @return The default role prefix\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tprotected String getDefaultRolePrefix() {\n\t\treturn this.defaultRolePrefix;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityEvaluationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.framework.AopProxyUtils;\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.context.expression.MethodBasedEvaluationContext;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer;\n\n/**\n * Internal security-specific EvaluationContext implementation which lazily adds the\n * method parameter values as variables (with the corresponding parameter names) if and\n * when they are required.\n *\n * @author Luke Taylor\n * @author Daniel Bustamante\n * @author Evgeniy Cheban\n * @since 3.0\n */\nclass MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {\n\n\t/**\n\t * Intended for testing. Don't use in practice as it creates a new parameter resolver\n\t * for each instance. Use the constructor which takes the resolver, as an argument\n\t * thus allowing for caching.\n\t */\n\tMethodSecurityEvaluationContext(@Nullable Authentication user, MethodInvocation mi) {\n\t\tthis(user, mi, new DefaultSecurityParameterNameDiscoverer());\n\t}\n\n\tMethodSecurityEvaluationContext(@Nullable Authentication user, MethodInvocation mi,\n\t\t\tParameterNameDiscoverer parameterNameDiscoverer) {\n\t\tsuper(mi.getThis(), getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);\n\t}\n\n\tMethodSecurityEvaluationContext(MethodSecurityExpressionOperations root, MethodInvocation mi,\n\t\t\tParameterNameDiscoverer parameterNameDiscoverer) {\n\t\tsuper(root, getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);\n\t}\n\n\tprivate static Method getSpecificMethod(MethodInvocation mi) {\n\t\tClass<?> targetClass = (mi.getThis() != null) ? AopProxyUtils.ultimateTargetClass(mi.getThis()) : null;\n\t\treturn AopUtils.getMostSpecificMethod(mi.getMethod(), targetClass);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\n\n/**\n * Extended expression-handler facade which adds methods which are specific to securing\n * method invocations.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface MethodSecurityExpressionHandler extends SecurityExpressionHandler<MethodInvocation> {\n\n\t/**\n\t * Filters a target collection or array. Only applies to method invocations.\n\t * @param filterTarget the array or collection to be filtered.\n\t * @param filterExpression the expression which should be used as the filter\n\t * condition. If it returns false on evaluation, the object will be removed from the\n\t * returned collection\n\t * @param ctx the current evaluation context (as created through a call to\n\t * {@link #createEvaluationContext(org.springframework.security.core.Authentication, Object)}\n\t * @return the filtered collection or array\n\t */\n\tObject filter(@Nullable Object filterTarget, Expression filterExpression, EvaluationContext ctx);\n\n\t/**\n\t * Used to inform the expression system of the return object for the given evaluation\n\t * context. Only applies to method invocations.\n\t * @param returnObject the return object value\n\t * @param ctx the context within which the object should be set (as created through a\n\t * call to\n\t * {@link #createEvaluationContext(org.springframework.security.core.Authentication, Object)}\n\t */\n\tvoid setReturnObject(@Nullable Object returnObject, EvaluationContext ctx);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.expression.SecurityExpressionOperations;\n\n/**\n * Interface which must be implemented if you want to use filtering in method security\n * expressions.\n *\n * @author Luke Taylor\n * @since 3.1.1\n */\npublic interface MethodSecurityExpressionOperations extends SecurityExpressionOperations {\n\n\tvoid setFilterObject(Object filterObject);\n\n\t@Nullable Object getFilterObject();\n\n\tvoid setReturnObject(@Nullable Object returnObject);\n\n\t@Nullable Object getReturnObject();\n\n\t@Nullable Object getThis();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionRoot.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Extended expression root object which contains extra method-specific functionality.\n *\n * @author Luke Taylor\n * @author Evgeniy Cheban\n * @author Steve Riesenberg\n * @since 3.0\n */\nclass MethodSecurityExpressionRoot extends SecurityExpressionRoot<MethodInvocation>\n\t\timplements MethodSecurityExpressionOperations {\n\n\tprivate @Nullable Object filterObject;\n\n\tprivate @Nullable Object returnObject;\n\n\tprivate @Nullable Object target;\n\n\tMethodSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMethodInvocation methodInvocation) {\n\t\tsuper(authentication, methodInvocation);\n\t}\n\n\t@Override\n\tpublic void setFilterObject(Object filterObject) {\n\t\tthis.filterObject = filterObject;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getFilterObject() {\n\t\treturn this.filterObject;\n\t}\n\n\t@Override\n\tpublic void setReturnObject(@Nullable Object returnObject) {\n\t\tthis.returnObject = returnObject;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getReturnObject() {\n\t\treturn this.returnObject;\n\t}\n\n\t/**\n\t * Sets the \"this\" property for use in expressions. Typically this will be the \"this\"\n\t * property of the {@code JoinPoint} representing the method invocation which is being\n\t * protected.\n\t * @param target the target object on which the method in is being invoked.\n\t */\n\tvoid setThis(@Nullable Object target) {\n\t\tthis.target = target;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getThis() {\n\t\treturn this.target;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/method/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Implementation of expression-based method security.\n *\n * @since 3.0\n */\n@NullMarked\npackage org.springframework.security.access.expression.method;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/expression/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Expression handling code to support the use of Spring-EL based expressions in\n * {@code @PreAuthorize}, {@code @PreFilter}, {@code @PostAuthorize} and\n * {@code @PostFilter} annotations. Mainly for internal framework use and liable to\n * change.\n *\n * @since 3.0\n */\n@NullMarked\npackage org.springframework.security.access.expression;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/hierarchicalroles/CycleInRoleHierarchyException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\n/**\n * Exception that is thrown because of a cycle in the role hierarchy definition\n *\n * @author Michael Mayr\n */\npublic class CycleInRoleHierarchyException extends RuntimeException {\n\n\tprivate static final long serialVersionUID = -4970510612118296707L;\n\n\tpublic CycleInRoleHierarchyException() {\n\t\tsuper(\"Exception thrown because of a cycle in the role hierarchy definition!\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/hierarchicalroles/NullRoleHierarchy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\npublic final class NullRoleHierarchy implements RoleHierarchy {\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getReachableGrantedAuthorities(\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\treturn authorities;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * The simple interface of a role hierarchy.\n *\n * @author Michael Mayr\n */\npublic interface RoleHierarchy {\n\n\t/**\n\t * Returns an array of all reachable authorities.\n\t * <p>\n\t * Reachable authorities are the directly assigned authorities plus all authorities\n\t * that are (transitively) reachable from them in the role hierarchy.\n\t * <p>\n\t * Example:<br>\n\t * Role hierarchy: ROLE_A &gt; ROLE_B &gt; ROLE_C.<br>\n\t * Directly assigned authority: ROLE_A.<br>\n\t * Reachable authorities: ROLE_A, ROLE_B, ROLE_C.\n\t * @param authorities - List of the directly assigned authorities.\n\t * @return List of all reachable authorities given the assigned authorities.\n\t */\n\tCollection<? extends GrantedAuthority> getReachableGrantedAuthorities(\n\t\t\tCollection<? extends GrantedAuthority> authorities);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyAuthoritiesMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\n\n/**\n * @author Luke Taylor\n */\npublic class RoleHierarchyAuthoritiesMapper implements GrantedAuthoritiesMapper {\n\n\tprivate final RoleHierarchy roleHierarchy;\n\n\tpublic RoleHierarchyAuthoritiesMapper(RoleHierarchy roleHierarchy) {\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {\n\t\treturn this.roleHierarchy.getReachableGrantedAuthorities(authorities);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * This class defines a role hierarchy for use with various access checking components.\n *\n * <p>\n * Here is an example configuration of a role hierarchy (hint: read the \"&gt;\" sign as\n * \"includes\"):\n *\n * <pre>\n *     &lt;property name=\"hierarchy\"&gt;\n *         &lt;value&gt;\n *             ROLE_A &gt; ROLE_B\n *             ROLE_B &gt; ROLE_AUTHENTICATED\n *             ROLE_AUTHENTICATED &gt; ROLE_UNAUTHENTICATED\n *         &lt;/value&gt;\n *     &lt;/property&gt;\n * </pre>\n *\n * <p>\n * Explanation of the above:\n * <ul>\n * <li>In effect every user with ROLE_A also has ROLE_B, ROLE_AUTHENTICATED and\n * ROLE_UNAUTHENTICATED;</li>\n * <li>every user with ROLE_B also has ROLE_AUTHENTICATED and ROLE_UNAUTHENTICATED;</li>\n * <li>every user with ROLE_AUTHENTICATED also has ROLE_UNAUTHENTICATED.</li>\n * </ul>\n *\n * <p>\n * Hierarchical Roles will dramatically shorten your access rules (and also make the\n * access rules much more elegant).\n *\n * <p>\n * Consider this access rule for Spring Security's RoleVoter (background: every user that\n * is authenticated should be able to log out):\n * <pre>/logout.html=ROLE_A,ROLE_B,ROLE_AUTHENTICATED</pre>\n *\n * With hierarchical roles this can now be shortened to:\n * <pre>/logout.html=ROLE_AUTHENTICATED</pre>\n *\n * In addition to shorter rules this will also make your access rules more readable and\n * your intentions clearer.\n *\n * @author Michael Mayr\n * @author Josh Cummings\n */\npublic final class RoleHierarchyImpl implements RoleHierarchy {\n\n\tprivate static final Log logger = LogFactory.getLog(RoleHierarchyImpl.class);\n\n\t/**\n\t * {@code rolesReachableInOneOrMoreStepsMap} is a Map that under the key of a specific\n\t * role name contains a set of all roles reachable from this role in 1 or more steps\n\t */\n\tprivate final Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap;\n\n\tprivate RoleHierarchyImpl(Map<String, Set<GrantedAuthority>> hierarchy) {\n\t\tthis.rolesReachableInOneOrMoreStepsMap = buildRolesReachableInOneOrMoreStepsMap(hierarchy);\n\t}\n\n\t/**\n\t * Create a role hierarchy instance with the given definition, similar to the\n\t * following:\n\t *\n\t * <pre>\n\t *     ROLE_A &gt; ROLE_B\n\t *     ROLE_B &gt; ROLE_AUTHENTICATED\n\t *     ROLE_AUTHENTICATED &gt; ROLE_UNAUTHENTICATED\n\t * </pre>\n\t * @param hierarchy the role hierarchy to use\n\t * @return a {@link RoleHierarchyImpl} that uses the given {@code hierarchy}\n\t */\n\tpublic static RoleHierarchyImpl fromHierarchy(String hierarchy) {\n\t\treturn new RoleHierarchyImpl(buildRolesReachableInOneStepMap(hierarchy));\n\t}\n\n\t/**\n\t * Factory method that creates a {@link Builder} instance with the default role prefix\n\t * \"ROLE_\"\n\t * @return a {@link Builder} instance with the default role prefix \"ROLE_\"\n\t * @since 6.3\n\t */\n\tpublic static Builder withDefaultRolePrefix() {\n\t\treturn withRolePrefix(\"ROLE_\");\n\t}\n\n\t/**\n\t * Factory method that creates a {@link Builder} instance with the specified role\n\t * prefix.\n\t * @param rolePrefix the prefix to be used for the roles in the hierarchy.\n\t * @return a new {@link Builder} instance with the specified role prefix\n\t * @throws IllegalArgumentException if the provided role prefix is null\n\t * @since 6.3\n\t */\n\tpublic static Builder withRolePrefix(String rolePrefix) {\n\t\tAssert.notNull(rolePrefix, \"rolePrefix must not be null\");\n\t\treturn new Builder(rolePrefix);\n\t}\n\n\t@Override\n\tpublic Collection<GrantedAuthority> getReachableGrantedAuthorities(\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tif (authorities == null || authorities.isEmpty()) {\n\t\t\treturn AuthorityUtils.NO_AUTHORITIES;\n\t\t}\n\t\tSet<GrantedAuthority> reachableRoles = new HashSet<>();\n\t\tSet<String> processedNames = new HashSet<>();\n\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\t// Do not process authorities without string representation\n\t\t\tif (authority.getAuthority() == null) {\n\t\t\t\treachableRoles.add(authority);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Do not process already processed roles\n\t\t\tif (!processedNames.add(authority.getAuthority())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Add original authority\n\t\t\treachableRoles.add(authority);\n\t\t\t// Add roles reachable in one or more steps\n\t\t\tSet<GrantedAuthority> lowerRoles = this.rolesReachableInOneOrMoreStepsMap.get(authority.getAuthority());\n\t\t\tif (lowerRoles == null) {\n\t\t\t\tcontinue; // No hierarchy for the role\n\t\t\t}\n\t\t\tfor (GrantedAuthority role : lowerRoles) {\n\t\t\t\tif (processedNames.add(role.getAuthority())) {\n\t\t\t\t\treachableRoles.add(role);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlogger.debug(LogMessage.format(\n\t\t\t\t\"getReachableGrantedAuthorities() - From the roles %s one can reach %s in zero or more steps.\",\n\t\t\t\tauthorities, reachableRoles));\n\t\treturn new ArrayList<>(reachableRoles);\n\t}\n\n\t/**\n\t * Parse input and build the map for the roles reachable in one step: the higher role\n\t * will become a key that references a set of the reachable lower roles.\n\t */\n\tprivate static Map<String, Set<GrantedAuthority>> buildRolesReachableInOneStepMap(String hierarchy) {\n\t\tMap<String, Set<GrantedAuthority>> rolesReachableInOneStepMap = new HashMap<>();\n\t\tfor (String line : hierarchy.split(\"\\n\")) {\n\t\t\t// Split on > and trim excessive whitespace\n\t\t\tString[] roles = line.trim().split(\"\\\\s+>\\\\s+\");\n\t\t\tfor (int i = 1; i < roles.length; i++) {\n\t\t\t\tString higherRole = roles[i - 1];\n\t\t\t\tGrantedAuthority lowerRole = new SimpleGrantedAuthority(roles[i]);\n\t\t\t\tSet<GrantedAuthority> rolesReachableInOneStepSet;\n\t\t\t\tif (!rolesReachableInOneStepMap.containsKey(higherRole)) {\n\t\t\t\t\trolesReachableInOneStepSet = new HashSet<>();\n\t\t\t\t\trolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\trolesReachableInOneStepSet = rolesReachableInOneStepMap.get(higherRole);\n\t\t\t\t}\n\t\t\t\trolesReachableInOneStepSet.add(lowerRole);\n\t\t\t\tlogger.debug(LogMessage.format(\n\t\t\t\t\t\t\"buildRolesReachableInOneStepMap() - From role %s one can reach role %s in one step.\",\n\t\t\t\t\t\thigherRole, lowerRole));\n\t\t\t}\n\t\t}\n\t\treturn rolesReachableInOneStepMap;\n\t}\n\n\t/**\n\t * For every higher role from rolesReachableInOneStepMap store all roles that are\n\t * reachable from it in the map of roles reachable in one or more steps. (Or throw a\n\t * CycleInRoleHierarchyException if a cycle in the role hierarchy definition is\n\t * detected)\n\t */\n\tprivate static Map<String, Set<GrantedAuthority>> buildRolesReachableInOneOrMoreStepsMap(\n\t\t\tMap<String, Set<GrantedAuthority>> hierarchy) {\n\t\tMap<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = new HashMap<>();\n\t\t// iterate over all higher roles from rolesReachableInOneStepMap\n\t\tfor (String roleName : hierarchy.keySet()) {\n\t\t\tSet<GrantedAuthority> rolesToVisitSet = new HashSet<>(hierarchy.get(roleName));\n\t\t\tSet<GrantedAuthority> visitedRolesSet = new HashSet<>();\n\t\t\twhile (!rolesToVisitSet.isEmpty()) {\n\t\t\t\t// take a role from the rolesToVisit set\n\t\t\t\tGrantedAuthority lowerRole = rolesToVisitSet.iterator().next();\n\t\t\t\trolesToVisitSet.remove(lowerRole);\n\t\t\t\tif (!visitedRolesSet.add(lowerRole) || !hierarchy.containsKey(lowerRole.getAuthority())) {\n\t\t\t\t\tcontinue; // Already visited role or role with missing hierarchy\n\t\t\t\t}\n\t\t\t\telse if (roleName.equals(lowerRole.getAuthority())) {\n\t\t\t\t\tthrow new CycleInRoleHierarchyException();\n\t\t\t\t}\n\t\t\t\trolesToVisitSet.addAll(hierarchy.get(lowerRole.getAuthority()));\n\t\t\t}\n\t\t\trolesReachableInOneOrMoreStepsMap.put(roleName, visitedRolesSet);\n\t\t\tlogger.debug(LogMessage.format(\n\t\t\t\t\t\"buildRolesReachableInOneOrMoreStepsMap() - From role %s one can reach %s in one or more steps.\",\n\t\t\t\t\troleName, visitedRolesSet));\n\t\t}\n\t\treturn rolesReachableInOneOrMoreStepsMap;\n\t}\n\n\t/**\n\t * Builder class for constructing a {@link RoleHierarchyImpl} based on a hierarchical\n\t * role structure.\n\t *\n\t * @author Federico Herrera\n\t * @since 6.3\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final String rolePrefix;\n\n\t\tprivate final Map<String, Set<GrantedAuthority>> hierarchy;\n\n\t\tprivate Builder(String rolePrefix) {\n\t\t\tthis.rolePrefix = rolePrefix;\n\t\t\tthis.hierarchy = new LinkedHashMap<>();\n\t\t}\n\n\t\t/**\n\t\t * Creates a new hierarchy branch to define a role and its child roles.\n\t\t * @param role the highest role in this branch\n\t\t * @return a {@link ImpliedRoles} to define the child roles for the\n\t\t * <code>role</code>\n\t\t */\n\t\tpublic ImpliedRoles role(String role) {\n\t\t\tAssert.hasText(role, \"role must not be empty\");\n\t\t\treturn new ImpliedRoles(role);\n\t\t}\n\n\t\t/**\n\t\t * Builds and returns a {@link RoleHierarchyImpl} describing the defined role\n\t\t * hierarchy.\n\t\t * @return a {@link RoleHierarchyImpl}\n\t\t */\n\t\tpublic RoleHierarchyImpl build() {\n\t\t\treturn new RoleHierarchyImpl(this.hierarchy);\n\t\t}\n\n\t\tprivate Builder addHierarchy(String role, String... impliedRoles) {\n\t\t\tSet<GrantedAuthority> withPrefix = this.hierarchy.computeIfAbsent(this.rolePrefix.concat(role),\n\t\t\t\t\t(r) -> new HashSet<>());\n\t\t\tfor (String impliedRole : impliedRoles) {\n\t\t\t\twithPrefix.add(new SimpleGrantedAuthority(this.rolePrefix.concat(impliedRole)));\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builder class for constructing child roles within a role hierarchy branch.\n\t\t */\n\t\tpublic final class ImpliedRoles {\n\n\t\t\tprivate final String role;\n\n\t\t\tprivate ImpliedRoles(String role) {\n\t\t\t\tthis.role = role;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specifies implied role(s) for the current role in the hierarchy.\n\t\t\t * @param impliedRoles role name(s) implied by the role.\n\t\t\t * @return the same {@link Builder} instance\n\t\t\t * @throws IllegalArgumentException if <code>impliedRoles</code> is null,\n\t\t\t * empty or contains any null element.\n\t\t\t */\n\t\t\tpublic Builder implies(String... impliedRoles) {\n\t\t\t\tAssert.notEmpty(impliedRoles, \"at least one implied role must be provided\");\n\t\t\t\tAssert.noNullElements(impliedRoles, \"implied role name(s) cannot be empty\");\n\t\t\t\treturn Builder.this.addHierarchy(this.role, impliedRoles);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.util.Assert;\n\n/**\n * Utility methods for {@link RoleHierarchy}.\n *\n * @author Thomas Darimont\n * @since 4.2.0\n */\npublic final class RoleHierarchyUtils {\n\n\tprivate RoleHierarchyUtils() {\n\t}\n\n\t/**\n\t * Converts the supplied {@link Map} of role name to implied role name(s) to a string\n\t * representation understood by the role hierarchy parser. The map key is the role\n\t * name and the map value is a {@link List} of implied role name(s). * @param\n\t * roleHierarchyMap the mapping(s) of role name to implied role name(s)\n\t * @return a string representation of a role hierarchy\n\t * @throws IllegalArgumentException if roleHierarchyMap is null or empty, or if a role\n\t * name/implied role name is null or empty\n\t * @deprecated Use {@link RoleHierarchyImpl#fromHierarchy(String)} or the\n\t * builder-based approach instead of this manual conversion.\n\t */\n\t@Deprecated\n\tpublic static String roleHierarchyFromMap(Map<String, List<String>> roleHierarchyMap) {\n\t\tAssert.notEmpty(roleHierarchyMap, \"roleHierarchyMap cannot be empty\");\n\t\tStringWriter result = new StringWriter();\n\t\tPrintWriter writer = new PrintWriter(result);\n\t\troleHierarchyMap.forEach((role, impliedRoles) -> {\n\t\t\tAssert.hasLength(role, \"role name must be supplied\");\n\t\t\tAssert.notEmpty(impliedRoles, \"implied role name(s) cannot be empty\");\n\t\t\tfor (String impliedRole : impliedRoles) {\n\t\t\t\twriter.println(role + \" > \" + impliedRole);\n\t\t\t}\n\t\t});\n\t\treturn result.toString();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/hierarchicalroles/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Role hierarchy implementation.\n */\n@NullMarked\npackage org.springframework.security.access.hierarchicalroles;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core access-control related code, including security metadata related classes,\n * interception code, access control annotations, EL support, and implementations of the\n * central {@link org.springframework.security.authorization.AuthorizationManager\n * AuthorizationManager} interface.\n */\n@NullMarked\npackage org.springframework.security.access;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/prepost/PostAuthorize.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for specifying a method access-control expression which will be evaluated\n * after a method has been invoked.\n *\n * @author Luke Taylor\n * @since 3.0\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface PostAuthorize {\n\n\t/**\n\t * @return the Spring-EL expression to be evaluated after invoking the protected\n\t * method\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/prepost/PostFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for specifying a method filtering expression which will be evaluated after a\n * method has been invoked.\n *\n * @author Luke Taylor\n * @since 3.0\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface PostFilter {\n\n\t/**\n\t * @return the Spring-EL expression to be evaluated after invoking the protected\n\t * method\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/prepost/PreAuthorize.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for specifying a method access-control expression which will be evaluated to\n * decide whether a method invocation is allowed or not.\n *\n * @author Luke Taylor\n * @since 3.0\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface PreAuthorize {\n\n\t/**\n\t * @return the Spring-EL expression to be evaluated before invoking the protected\n\t * method\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/prepost/PreFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.prepost;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for specifying a method filtering expression which will be evaluated before\n * a method has been invoked. The name of the argument to be filtered is specified using\n * the <tt>filterTarget</tt> attribute. This must be a Java Collection implementation\n * which supports the {@link java.util.Collection#remove(Object) remove} method.\n * Pre-filtering isn't supported on array types and will fail if the value of named filter\n * target argument is null at runtime.\n * <p>\n * For methods which have a single argument which is a collection type, this argument will\n * be used as the filter target.\n * <p>\n * The annotation value contains the expression which will be evaluated for each element\n * in the collection. If the expression evaluates to false, the element will be removed.\n * The reserved name \"filterObject\" can be used within the expression to refer to the\n * current object which is being evaluated.\n *\n * @author Luke Taylor\n * @since 3.0\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface PreFilter {\n\n\t/**\n\t * @return the Spring-EL expression to be evaluated before invoking the protected\n\t * method\n\t */\n\tString value();\n\n\t/**\n\t * @return the name of the parameter which should be filtered (must be a non-null\n\t * collection instance) If the method contains a single collection argument, then this\n\t * attribute can be omitted.\n\t */\n\tString filterTarget() default \"\";\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/access/prepost/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Contains the infrastructure classes for handling the {@code @PreAuthorize},\n * {@code @PreFilter}, {@code @PostAuthorize} and {@code @PostFilter} annotations.\n * <p>\n * Other than the annotations themselves, the classes should be regarded as for internal\n * framework use and are liable to change without notice.\n */\n@NullMarked\npackage org.springframework.security.access.prepost;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/AuthorizeReturnObjectCoreHintsRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link SecurityHintsRegistrar} that scans all beans for methods that use\n * {@link AuthorizeReturnObject} and registers those return objects as\n * {@link org.springframework.aot.hint.TypeHint}s.\n *\n * <p>\n * It also traverses those found types for other return values.\n *\n * <p>\n * An instance of this class is published as an infrastructural bean by the\n * {@code spring-security-config} module. However, in the event you need to publish it\n * yourself, remember to publish it as an infrastructural bean like so:\n *\n * <pre>\n *\t&#064;Bean\n *\t&#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n *\tstatic SecurityHintsRegistrar proxyThese(AuthorizationProxyFactory proxyFactory) {\n *\t\treturn new AuthorizeReturnObjectHintsRegistrar(proxyFactory);\n *\t}\n * </pre>\n *\n * @author Josh Cummings\n * @since 6.4\n * @see AuthorizeReturnObjectHintsRegistrar\n * @see SecurityHintsAotProcessor\n */\npublic final class AuthorizeReturnObjectCoreHintsRegistrar implements SecurityHintsRegistrar {\n\n\tprivate final AuthorizationProxyFactory proxyFactory;\n\n\tprivate final SecurityAnnotationScanner<AuthorizeReturnObject> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(AuthorizeReturnObject.class);\n\n\tprivate final Set<Class<?>> visitedClasses = new HashSet<>();\n\n\tpublic AuthorizeReturnObjectCoreHintsRegistrar(AuthorizationProxyFactory proxyFactory) {\n\t\tAssert.notNull(proxyFactory, \"proxyFactory cannot be null\");\n\t\tthis.proxyFactory = proxyFactory;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ConfigurableListableBeanFactory beanFactory) {\n\t\tList<Class<?>> toProxy = new ArrayList<>();\n\t\tfor (String name : beanFactory.getBeanDefinitionNames()) {\n\t\t\tClass<?> clazz = beanFactory.getType(name, false);\n\t\t\tif (clazz == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (Method method : clazz.getDeclaredMethods()) {\n\t\t\t\tAuthorizeReturnObject annotation = this.scanner.scan(method, clazz);\n\t\t\t\tif (annotation == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttoProxy.add(method.getReturnType());\n\t\t\t}\n\t\t}\n\t\tnew AuthorizeReturnObjectHintsRegistrar(this.proxyFactory, toProxy).registerHints(hints, beanFactory);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/AuthorizeReturnObjectHintsRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.springframework.aop.SpringProxy;\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link SecurityHintsRegistrar} implementation that registers only the classes\n * provided in the constructor.\n *\n * <p>\n * It also traverses those found types for other return values.\n *\n * <p>\n * This may be used by an application to register specific Security-adjacent classes that\n * were otherwise missed by Spring Security's reachability scans.\n *\n * <p>\n * Remember to register this as an infrastructural bean like so:\n *\n * <pre>\n *\t&#064;Bean\n *\t&#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n *\tstatic SecurityHintsRegistrar proxyThese(AuthorizationProxyFactory proxyFactory) {\n *\t\treturn new AuthorizationProxyFactoryHintsRegistrar(proxyFactory, MyClass.class);\n *\t}\n * </pre>\n *\n * <p>\n * Note that no object graph traversal is performed in this class. As such, any classes\n * that need an authorization proxy that are missed by Security's default registrars\n * should be listed exhaustively in the constructor.\n *\n * @author Josh Cummings\n * @since 6.4\n * @see AuthorizeReturnObjectCoreHintsRegistrar\n */\npublic final class AuthorizeReturnObjectHintsRegistrar implements SecurityHintsRegistrar {\n\n\tprivate final AuthorizationProxyFactory proxyFactory;\n\n\tprivate final SecurityAnnotationScanner<AuthorizeReturnObject> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(AuthorizeReturnObject.class);\n\n\tprivate final Set<Class<?>> visitedClasses = new HashSet<>();\n\n\tprivate final List<Class<?>> classesToProxy;\n\n\tpublic AuthorizeReturnObjectHintsRegistrar(AuthorizationProxyFactory proxyFactory, Class<?>... classes) {\n\t\tAssert.notNull(proxyFactory, \"proxyFactory cannot be null\");\n\t\tAssert.noNullElements(classes, \"classes cannot contain null elements\");\n\t\tthis.proxyFactory = proxyFactory;\n\t\tthis.classesToProxy = new ArrayList(List.of(classes));\n\t}\n\n\t/**\n\t * Construct this registrar\n\t * @param proxyFactory the proxy factory to use to produce the proxy class\n\t * implementations to be registered\n\t * @param classes the classes to proxy\n\t */\n\tpublic AuthorizeReturnObjectHintsRegistrar(AuthorizationProxyFactory proxyFactory, List<Class<?>> classes) {\n\t\tthis.proxyFactory = proxyFactory;\n\t\tthis.classesToProxy = new ArrayList<>(classes);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ConfigurableListableBeanFactory beanFactory) {\n\t\tList<Class<?>> toProxy = new ArrayList<>();\n\t\tfor (Class<?> clazz : this.classesToProxy) {\n\t\t\ttoProxy.add(clazz);\n\t\t\ttraverseType(toProxy, clazz);\n\t\t}\n\t\tfor (Class<?> clazz : toProxy) {\n\t\t\tregisterProxy(hints, clazz);\n\t\t}\n\t}\n\n\tprivate void registerProxy(RuntimeHints hints, Class<?> clazz) {\n\t\tClass<?> proxied = this.proxyFactory.proxy(clazz);\n\t\tif (proxied == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (Proxy.isProxyClass(proxied)) {\n\t\t\thints.proxies().registerJdkProxy(proxied.getInterfaces());\n\t\t\treturn;\n\t\t}\n\t\tif (SpringProxy.class.isAssignableFrom(proxied)) {\n\t\t\thints.reflection()\n\t\t\t\t.registerType(clazz, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS)\n\t\t\t\t.registerType(proxied, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS);\n\t\t}\n\t}\n\n\tprivate void traverseType(List<Class<?>> toProxy, Class<?> clazz) {\n\t\tif (clazz == Object.class || this.visitedClasses.contains(clazz)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.visitedClasses.add(clazz);\n\t\tfor (Method m : clazz.getDeclaredMethods()) {\n\t\t\tAuthorizeReturnObject object = this.scanner.scan(m, clazz);\n\t\t\tif (object == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tClass<?> returnType = m.getReturnType();\n\t\t\ttoProxy.add(returnType);\n\t\t\ttraverseType(toProxy, returnType);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/CoreSecurityRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.security.access.expression.SecurityExpressionOperations;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.ProviderNotFoundException;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureCredentialsExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureDisabledEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureLockedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProviderNotFoundEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProxyUntrustedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureServiceExceptionEvent;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthentication;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;\n\n/**\n * {@link RuntimeHintsRegistrar} for core classes\n *\n * @author Marcus Da Coregio\n * @since 6.0\n */\nclass CoreSecurityRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\tregisterExceptionEventsHints(hints);\n\t\tregisterExpressionEvaluationHints(hints);\n\t\tregisterMethodSecurityHints(hints);\n\t\tregisterAdditionalAuthenticationTypes(hints);\n\t\thints.resources().registerResourceBundle(\"org.springframework.security.messages\");\n\t\tregisterDefaultJdbcSchemaFileHint(hints);\n\t\tregisterSecurityContextHints(hints);\n\t}\n\n\tprivate void registerMethodSecurityHints(RuntimeHints hints) {\n\t\thints.reflection()\n\t\t\t.registerType(\n\t\t\t\t\tTypeReference\n\t\t\t\t\t\t.of(\"org.springframework.security.access.expression.method.MethodSecurityExpressionRoot\"),\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));\n\t\thints.reflection()\n\t\t\t.registerType(AbstractAuthenticationToken.class,\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));\n\t}\n\n\tprivate void registerExpressionEvaluationHints(RuntimeHints hints) {\n\t\thints.reflection()\n\t\t\t.registerTypes(\n\t\t\t\t\tList.of(TypeReference.of(SecurityExpressionOperations.class),\n\t\t\t\t\t\t\tTypeReference.of(SecurityExpressionRoot.class)),\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.ACCESS_DECLARED_FIELDS,\n\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS));\n\t}\n\n\tprivate void registerExceptionEventsHints(RuntimeHints hints) {\n\t\thints.reflection()\n\t\t\t.registerTypes(getDefaultAuthenticationExceptionEventPublisherTypes(),\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));\n\t}\n\n\tprivate List<TypeReference> getDefaultAuthenticationExceptionEventPublisherTypes() {\n\t\treturn Stream\n\t\t\t.of(AuthenticationFailureBadCredentialsEvent.class, AuthenticationFailureCredentialsExpiredEvent.class,\n\t\t\t\t\tAuthenticationFailureDisabledEvent.class, AuthenticationFailureExpiredEvent.class,\n\t\t\t\t\tAuthenticationFailureLockedEvent.class, AuthenticationFailureProviderNotFoundEvent.class,\n\t\t\t\t\tAuthenticationFailureProxyUntrustedEvent.class, AuthenticationFailureServiceExceptionEvent.class,\n\t\t\t\t\tAuthenticationServiceException.class, AccountExpiredException.class, BadCredentialsException.class,\n\t\t\t\t\tCredentialsExpiredException.class, DisabledException.class, LockedException.class,\n\t\t\t\t\tUsernameNotFoundException.class, ProviderNotFoundException.class)\n\t\t\t.map(TypeReference::of)\n\t\t\t.toList();\n\t}\n\n\tprivate void registerDefaultJdbcSchemaFileHint(RuntimeHints hints) {\n\t\thints.resources().registerPattern(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION);\n\t}\n\n\tprivate void registerSecurityContextHints(RuntimeHints hints) {\n\t\thints.reflection()\n\t\t\t.registerType(SecurityContextImpl.class,\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));\n\t}\n\n\tprivate void registerAdditionalAuthenticationTypes(RuntimeHints hints) {\n\t\t// RememberMeAuthenticationToken can be stored in the HTTP session and\n\t\t// deserialized via Jackson (RememberMeAuthenticationTokenMixin exists for both\n\t\t// Jackson 2 and 3), so it needs reflection hints in all native image scenarios.\n\t\tStream\n\t\t\t.of(RememberMeAuthenticationToken.class, OneTimeTokenAuthentication.class,\n\t\t\t\t\tUsernamePasswordAuthenticationToken.class)\n\t\t\t.map(TypeReference::of)\n\t\t\t.forEach((it) -> hints.reflection()\n\t\t\t\t.registerType(it, (builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS)));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/OneTimeTokenRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenService;\n\n/**\n *\n * A JDBC implementation of an {@link OneTimeTokenService} that uses a\n * {@link JdbcOperations} for {@link OneTimeToken} persistence.\n *\n * @author Max Batischev\n * @since 6.4\n */\nclass OneTimeTokenRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\thints.resources().registerPattern(\"org/springframework/security/core/ott/jdbc/one-time-tokens-schema.sql\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/PrePostAuthorizeExpressionBeanHintsRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.expression.spel.SpelNode;\nimport org.springframework.expression.spel.ast.BeanReference;\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link SecurityHintsRegistrar} that scans all provided classes for methods that use\n * {@link PreAuthorize} or {@link PostAuthorize} and registers hints for the beans used\n * within the security expressions.\n *\n * <p>\n * It will also scan return types of methods annotated with {@link AuthorizeReturnObject}.\n *\n * <p>\n * This may be used by an application to register specific Security-adjacent classes that\n * were otherwise missed by Spring Security's reachability scans.\n *\n * <p>\n * Remember to register this as an infrastructural bean like so:\n *\n * <pre>\n *\t&#064;Bean\n *\t&#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n *\tstatic SecurityHintsRegistrar registerThese() {\n *\t\treturn new PrePostAuthorizeExpressionBeanHintsRegistrar(MyClass.class);\n *\t}\n * </pre>\n *\n * @author Marcus da Coregio\n * @since 6.4\n * @see SecurityHintsAotProcessor\n */\npublic final class PrePostAuthorizeExpressionBeanHintsRegistrar implements SecurityHintsRegistrar {\n\n\tprivate final SecurityAnnotationScanner<PreAuthorize> preAuthorizeScanner = SecurityAnnotationScanners\n\t\t.requireUnique(PreAuthorize.class);\n\n\tprivate final SecurityAnnotationScanner<PostAuthorize> postAuthorizeScanner = SecurityAnnotationScanners\n\t\t.requireUnique(PostAuthorize.class);\n\n\tprivate final SecurityAnnotationScanner<AuthorizeReturnObject> authorizeReturnObjectScanner = SecurityAnnotationScanners\n\t\t.requireUnique(AuthorizeReturnObject.class);\n\n\tprivate final SpelExpressionParser expressionParser = new SpelExpressionParser();\n\n\tprivate final Set<Class<?>> visitedClasses = new HashSet<>();\n\n\tprivate final List<Class<?>> toVisit;\n\n\tpublic PrePostAuthorizeExpressionBeanHintsRegistrar(Class<?>... toVisit) {\n\t\tthis(Arrays.asList(toVisit));\n\t}\n\n\tpublic PrePostAuthorizeExpressionBeanHintsRegistrar(List<Class<?>> toVisit) {\n\t\tAssert.notEmpty(toVisit, \"toVisit cannot be empty\");\n\t\tAssert.noNullElements(toVisit, \"toVisit cannot contain null elements\");\n\t\tthis.toVisit = toVisit;\n\t}\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ConfigurableListableBeanFactory beanFactory) {\n\t\tSet<String> expressions = new HashSet<>();\n\t\tfor (Class<?> bean : this.toVisit) {\n\t\t\texpressions.addAll(extractSecurityExpressions(bean));\n\t\t}\n\t\tSet<String> beanNamesToRegister = new HashSet<>();\n\t\tfor (String expression : expressions) {\n\t\t\tbeanNamesToRegister.addAll(extractBeanNames(expression));\n\t\t}\n\t\tfor (String toRegister : beanNamesToRegister) {\n\t\t\tClass<?> type = beanFactory.getType(toRegister, false);\n\t\t\tif (type == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\thints.reflection().registerType(TypeReference.of(type), MemberCategory.INVOKE_DECLARED_METHODS);\n\t\t}\n\t}\n\n\tprivate Set<String> extractSecurityExpressions(Class<?> clazz) {\n\t\tif (this.visitedClasses.contains(clazz)) {\n\t\t\treturn Collections.emptySet();\n\t\t}\n\t\tthis.visitedClasses.add(clazz);\n\t\tSet<String> expressions = new HashSet<>();\n\t\tfor (Method method : clazz.getDeclaredMethods()) {\n\t\t\tPreAuthorize preAuthorize = this.preAuthorizeScanner.scan(method, clazz);\n\t\t\tPostAuthorize postAuthorize = this.postAuthorizeScanner.scan(method, clazz);\n\t\t\tif (preAuthorize != null) {\n\t\t\t\texpressions.add(preAuthorize.value());\n\t\t\t}\n\t\t\tif (postAuthorize != null) {\n\t\t\t\texpressions.add(postAuthorize.value());\n\t\t\t}\n\t\t\tAuthorizeReturnObject authorizeReturnObject = this.authorizeReturnObjectScanner.scan(method, clazz);\n\t\t\tif (authorizeReturnObject != null) {\n\t\t\t\texpressions.addAll(extractSecurityExpressions(method.getReturnType()));\n\t\t\t}\n\t\t}\n\t\treturn expressions;\n\t}\n\n\tprivate Set<String> extractBeanNames(String rawExpression) {\n\t\tSpelExpression expression = this.expressionParser.parseRaw(rawExpression);\n\t\tSpelNode node = expression.getAST();\n\t\tSet<String> beanNames = new HashSet<>();\n\t\tresolveBeanNames(beanNames, node);\n\t\treturn beanNames;\n\t}\n\n\tprivate void resolveBeanNames(Set<String> beanNames, SpelNode node) {\n\t\tif (node instanceof BeanReference br) {\n\t\t\tbeanNames.add(br.getName());\n\t\t}\n\t\tint childCount = node.getChildCount();\n\t\tif (childCount == 0) {\n\t\t\treturn;\n\t\t}\n\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\tresolveBeanNames(beanNames, node.getChild(i));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/PrePostAuthorizeHintsRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.beans.factory.support.RegisteredBean;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * A {@link SecurityHintsRegistrar} that scans all beans for methods that use\n * {@link PreAuthorize} or {@link PostAuthorize} and registers appropriate hints for the\n * annotations.\n *\n * @author Marcus da Coregio\n * @since 6.4\n * @see SecurityHintsAotProcessor\n * @see PrePostAuthorizeExpressionBeanHintsRegistrar\n */\npublic final class PrePostAuthorizeHintsRegistrar implements SecurityHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ConfigurableListableBeanFactory beanFactory) {\n\t\tList<Class<?>> beans = Arrays.stream(beanFactory.getBeanDefinitionNames())\n\t\t\t.map((beanName) -> RegisteredBean.of(beanFactory, beanName).getBeanClass())\n\t\t\t.collect(Collectors.toList());\n\t\tnew PrePostAuthorizeExpressionBeanHintsRegistrar(beans).registerHints(hints, beanFactory);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/SecurityHintsAotProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport org.springframework.aot.generate.GenerationContext;\nimport org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;\nimport org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;\nimport org.springframework.beans.factory.aot.BeanFactoryInitializationCode;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\n\nfinal class SecurityHintsAotProcessor implements BeanFactoryInitializationAotProcessor {\n\n\t@Override\n\tpublic BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {\n\t\treturn new AuthorizationProxyFactoryAotContribution(beanFactory);\n\t}\n\n\tprivate static final class AuthorizationProxyFactoryAotContribution\n\t\t\timplements BeanFactoryInitializationAotContribution {\n\n\t\tprivate final ConfigurableListableBeanFactory beanFactory;\n\n\t\tprivate AuthorizationProxyFactoryAotContribution(ConfigurableListableBeanFactory beanFactory) {\n\t\t\tthis.beanFactory = beanFactory;\n\t\t}\n\n\t\t@Override\n\t\tpublic void applyTo(GenerationContext context, BeanFactoryInitializationCode code) {\n\t\t\tthis.beanFactory.getBeanProvider(SecurityHintsRegistrar.class)\n\t\t\t\t.forEach((provider) -> provider.registerHints(context.getRuntimeHints(), this.beanFactory));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/SecurityHintsRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\n\n/**\n * An interface for registering AOT hints.\n *\n * <p>\n * This interface is helpful because it allows for basing hints on Spring Security's\n * infrastructural beans like so:\n *\n * <pre>\n *\t&#064;Bean\n *\t&#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n *\tstatic SecurityHintsRegistrar proxyThese(AuthorizationProxyFactory proxyFactory) {\n *\t\treturn new AuthorizationProxyFactoryHintsRegistrar(proxyFactory, MyClass.class);\n *\t}\n * </pre>\n *\n * <p>\n * The collection of beans that implement {@link SecurityHintsRegistrar} are serially\n * invoked by {@link SecurityHintsAotProcessor}, a\n * {@link org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor}.\n *\n * <p>\n * Since this is used in a\n * {@link org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor},\n * the Spring Framework recommendation to only depend on infrastructural beans applies.\n *\n * <p>\n * If you do not need Security's infrastructural beans, consider either implementing\n * {@link org.springframework.aot.hint.RuntimeHintsRegistrar} or another AOT component as\n * indicated in the Spring Framework AOT reference documentation.\n *\n * @author Josh Cummings\n * @since 6.4\n * @see AuthorizeReturnObjectHintsRegistrar\n * @see SecurityHintsAotProcessor\n */\npublic interface SecurityHintsRegistrar {\n\n\t/**\n\t * Register hints after preparing them through Security's infrastructural beans\n\t * @param hints the registration target for any AOT hints\n\t * @param beanFactory the bean factory\n\t */\n\tvoid registerHints(RuntimeHints hints, ConfigurableListableBeanFactory beanFactory);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\nimport java.security.Principal;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.CredentialsContainer;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * Base class for <code>Authentication</code> objects.\n * <p>\n * Implementations which use this class should be immutable.\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3194696462184782834L;\n\n\tprivate final Collection<GrantedAuthority> authorities;\n\n\tprivate @Nullable Object details;\n\n\tprivate boolean authenticated = false;\n\n\t/**\n\t * Creates a token with the supplied array of authorities.\n\t * @param authorities the collection of <tt>GrantedAuthority</tt>s for the principal\n\t * represented by this authentication object.\n\t */\n\tpublic AbstractAuthenticationToken(@Nullable Collection<? extends GrantedAuthority> authorities) {\n\t\tif (authorities == null) {\n\t\t\tthis.authorities = AuthorityUtils.NO_AUTHORITIES;\n\t\t\treturn;\n\t\t}\n\t\tfor (GrantedAuthority a : authorities) {\n\t\t\tAssert.notNull(a, \"Authorities collection cannot contain any null elements\");\n\t\t}\n\t\tthis.authorities = Collections.unmodifiableList(new ArrayList<>(authorities));\n\t}\n\n\tprotected AbstractAuthenticationToken(AbstractAuthenticationBuilder<?> builder) {\n\t\tthis(builder.authorities);\n\t\tthis.authenticated = builder.authenticated;\n\t\tthis.details = builder.details;\n\t}\n\n\t@Override\n\tpublic Collection<GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\tif (this.getPrincipal() instanceof UserDetails userDetails) {\n\t\t\treturn userDetails.getUsername();\n\t\t}\n\t\tif (this.getPrincipal() instanceof AuthenticatedPrincipal authenticatedPrincipal) {\n\t\t\treturn authenticatedPrincipal.getName();\n\t\t}\n\t\tif (this.getPrincipal() instanceof Principal principal) {\n\t\t\treturn principal.getName();\n\t\t}\n\t\treturn (this.getPrincipal() == null) ? \"\" : this.getPrincipal().toString();\n\t}\n\n\t@Override\n\tpublic boolean isAuthenticated() {\n\t\treturn this.authenticated;\n\t}\n\n\t@Override\n\tpublic void setAuthenticated(boolean authenticated) {\n\t\tthis.authenticated = authenticated;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getDetails() {\n\t\treturn this.details;\n\t}\n\n\tpublic void setDetails(@Nullable Object details) {\n\t\tthis.details = details;\n\t}\n\n\t/**\n\t * Checks the {@code credentials}, {@code principal} and {@code details} objects,\n\t * invoking the {@code eraseCredentials} method on any which implement\n\t * {@link CredentialsContainer}.\n\t */\n\t@Override\n\tpublic void eraseCredentials() {\n\t\teraseSecret(getCredentials());\n\t\teraseSecret(getPrincipal());\n\t\teraseSecret(this.details);\n\t}\n\n\tprivate void eraseSecret(@Nullable Object secret) {\n\t\tif (secret instanceof CredentialsContainer container) {\n\t\t\tcontainer.eraseCredentials();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (!(obj instanceof AbstractAuthenticationToken test)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!this.authorities.equals(test.authorities)) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.details == null) && (test.getDetails() != null)) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.details != null) && (test.getDetails() == null)) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.details != null) && (!this.details.equals(test.getDetails()))) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.getCredentials() == null) && (test.getCredentials() != null)) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.getCredentials() != null) && !this.getCredentials().equals(test.getCredentials())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.getPrincipal() == null && test.getPrincipal() != null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.getPrincipal() != null && !this.getPrincipal().equals(test.getPrincipal())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.isAuthenticated() == test.isAuthenticated();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint code = 31;\n\t\tfor (GrantedAuthority authority : this.authorities) {\n\t\t\tcode ^= authority.hashCode();\n\t\t}\n\t\tif (this.getPrincipal() != null) {\n\t\t\tcode ^= this.getPrincipal().hashCode();\n\t\t}\n\t\tif (this.getCredentials() != null) {\n\t\t\tcode ^= this.getCredentials().hashCode();\n\t\t}\n\t\tif (this.getDetails() != null) {\n\t\t\tcode ^= this.getDetails().hashCode();\n\t\t}\n\t\tif (this.isAuthenticated()) {\n\t\t\tcode ^= -37;\n\t\t}\n\t\treturn code;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(getClass().getSimpleName()).append(\" [\");\n\t\tsb.append(\"Principal=\").append(getPrincipal()).append(\", \");\n\t\tsb.append(\"Credentials=[PROTECTED], \");\n\t\tsb.append(\"Authenticated=\").append(isAuthenticated()).append(\", \");\n\t\tsb.append(\"Details=\").append(getDetails()).append(\", \");\n\t\tsb.append(\"Granted Authorities=\").append(this.authorities);\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * A common abstract implementation of {@link Authentication.Builder}. It implements\n\t * the builder methods that correspond to the {@link Authentication} methods that\n\t * {@link AbstractAuthenticationToken} implements\n\t *\n\t * @param <B>\n\t * @since 7.0\n\t */\n\tprotected abstract static class AbstractAuthenticationBuilder<B extends AbstractAuthenticationBuilder<B>>\n\t\t\timplements Authentication.Builder<B> {\n\n\t\tprivate boolean authenticated;\n\n\t\tprivate @Nullable Object details;\n\n\t\tprivate final Collection<GrantedAuthority> authorities;\n\n\t\tprotected AbstractAuthenticationBuilder(AbstractAuthenticationToken token) {\n\t\t\tthis.authorities = new LinkedHashSet<>(token.getAuthorities());\n\t\t\tthis.authenticated = token.isAuthenticated();\n\t\t\tthis.details = token.getDetails();\n\t\t}\n\n\t\t@Override\n\t\tpublic B authenticated(boolean authenticated) {\n\t\t\tthis.authenticated = authenticated;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B details(@Nullable Object details) {\n\t\t\tthis.details = details;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B authorities(Consumer<Collection<GrantedAuthority>> authorities) {\n\t\t\tauthorities.accept(this.authorities);\n\t\t\tthis.authenticated = true;\n\t\t\treturn (B) this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Scheduler;\nimport reactor.core.scheduler.Schedulers;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.authentication.password.CompromisedPasswordChecker;\nimport org.springframework.security.authentication.password.CompromisedPasswordDecision;\nimport org.springframework.security.authentication.password.CompromisedPasswordException;\nimport org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.util.Assert;\n\n/**\n * A base {@link ReactiveAuthenticationManager} that allows subclasses to override and\n * work with {@link UserDetails} objects.\n *\n * <p>\n * Upon successful validation, a <code>UsernamePasswordAuthenticationToken</code> will be\n * created and returned to the caller. The token will include as its principal either a\n * <code>String</code> representation of the username, or the {@link UserDetails} that was\n * returned from the authentication repository.\n *\n * @author Eddú Meléndez\n * @since 5.2\n */\npublic abstract class AbstractUserDetailsReactiveAuthenticationManager\n\t\timplements ReactiveAuthenticationManager, MessageSourceAware {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\n\tprivate ReactiveUserDetailsPasswordService userDetailsPasswordService = ReactiveUserDetailsPasswordService.NOOP;\n\n\tprivate Scheduler scheduler = Schedulers.boundedElastic();\n\n\tprivate UserDetailsChecker preAuthenticationChecks = this::defaultPreAuthenticationChecks;\n\n\tprivate UserDetailsChecker postAuthenticationChecks = this::defaultPostAuthenticationChecks;\n\n\tprivate @Nullable ReactiveCompromisedPasswordChecker compromisedPasswordChecker;\n\n\tprivate void defaultPreAuthenticationChecks(UserDetails user) {\n\t\tif (!user.isAccountNonLocked()) {\n\t\t\tthis.logger.debug(\"User account is locked\");\n\t\t\tthrow new LockedException(this.messages.getMessage(\"AbstractUserDetailsAuthenticationProvider.locked\",\n\t\t\t\t\t\"User account is locked\"));\n\t\t}\n\t\tif (!user.isEnabled()) {\n\t\t\tthis.logger.debug(\"User account is disabled\");\n\t\t\tthrow new DisabledException(\n\t\t\t\t\tthis.messages.getMessage(\"AbstractUserDetailsAuthenticationProvider.disabled\", \"User is disabled\"));\n\t\t}\n\t\tif (!user.isAccountNonExpired()) {\n\t\t\tthis.logger.debug(\"User account is expired\");\n\t\t\tthrow new AccountExpiredException(this.messages\n\t\t\t\t.getMessage(\"AbstractUserDetailsAuthenticationProvider.expired\", \"User account has expired\"));\n\t\t}\n\t}\n\n\tprivate void defaultPostAuthenticationChecks(UserDetails user) {\n\t\tif (!user.isCredentialsNonExpired()) {\n\t\t\tthis.logger.debug(\"User account credentials have expired\");\n\t\t\tthrow new CredentialsExpiredException(this.messages.getMessage(\n\t\t\t\t\t\"AbstractUserDetailsAuthenticationProvider.credentialsExpired\", \"User credentials have expired\"));\n\t\t}\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\tString username = authentication.getName();\n\t\tString presentedPassword = (authentication.getCredentials() != null)\n\t\t\t\t? authentication.getCredentials().toString() : null;\n\t\t// @formatter:off\n\t\treturn retrieveUser(username)\n\t\t\t\t.doOnNext(this.preAuthenticationChecks::check)\n\t\t\t\t.publishOn(this.scheduler)\n\t\t\t\t.filter((userDetails) -> this.passwordEncoder.matches(presentedPassword, userDetails.getPassword()))\n\t\t\t\t.switchIfEmpty(Mono.defer(() -> Mono.error(new BadCredentialsException(\"Invalid Credentials\"))))\n\t\t\t\t.flatMap((userDetails) -> checkCompromisedPassword(presentedPassword).thenReturn(userDetails))\n\t\t\t\t.flatMap((userDetails) -> upgradeEncodingIfNecessary(userDetails, presentedPassword))\n\t\t\t\t.doOnNext(this.postAuthenticationChecks::check)\n\t\t\t\t.map(this::createUsernamePasswordAuthenticationToken);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Void> checkCompromisedPassword(@Nullable String password) {\n\t\tif (this.compromisedPasswordChecker == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\treturn this.compromisedPasswordChecker.check(password)\n\t\t\t.filter(CompromisedPasswordDecision::isCompromised)\n\t\t\t.flatMap((compromised) -> Mono.error(new CompromisedPasswordException(\n\t\t\t\t\t\"The provided password is compromised, please change your password\")));\n\t}\n\n\tprivate Mono<UserDetails> upgradeEncodingIfNecessary(UserDetails userDetails, @Nullable String presentedPassword) {\n\t\tString existingEncodedPassword = userDetails.getPassword();\n\t\tboolean upgradeEncoding = existingEncodedPassword != null\n\t\t\t\t&& this.passwordEncoder.upgradeEncoding(existingEncodedPassword);\n\t\tif (upgradeEncoding) {\n\t\t\tString newPassword = this.passwordEncoder.encode(presentedPassword);\n\t\t\treturn this.userDetailsPasswordService.updatePassword(userDetails, newPassword);\n\t\t}\n\t\treturn Mono.just(userDetails);\n\t}\n\n\tprivate UsernamePasswordAuthenticationToken createUsernamePasswordAuthenticationToken(UserDetails userDetails) {\n\t\treturn UsernamePasswordAuthenticationToken.authenticated(userDetails, userDetails.getPassword(),\n\t\t\t\tuserDetails.getAuthorities());\n\t}\n\n\t/**\n\t * The {@link PasswordEncoder} that is used for validating the password. The default\n\t * is {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}\n\t * @param passwordEncoder the {@link PasswordEncoder} to use. Cannot be null\n\t */\n\tpublic void setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder cannot be null\");\n\t\tthis.passwordEncoder = passwordEncoder;\n\t}\n\n\t/**\n\t * Sets the {@link Scheduler} used by the\n\t * {@link UserDetailsRepositoryReactiveAuthenticationManager}. The default is\n\t * {@code Schedulers.newParallel(String)} because modern password encoding is a CPU\n\t * intensive task that is non blocking. This means validation is bounded by the number\n\t * of CPUs. Some applications may want to customize the {@link Scheduler}. For\n\t * example, if users are stuck using the insecure\n\t * {@link org.springframework.security.crypto.password.NoOpPasswordEncoder} they might\n\t * want to leverage {@code Schedulers.immediate()}.\n\t * @param scheduler the {@link Scheduler} to use. Cannot be null.\n\t * @since 5.0.6\n\t */\n\tpublic void setScheduler(Scheduler scheduler) {\n\t\tAssert.notNull(scheduler, \"scheduler cannot be null\");\n\t\tthis.scheduler = scheduler;\n\t}\n\n\t/**\n\t * Sets the service to use for upgrading passwords on successful authentication.\n\t * @param userDetailsPasswordService the service to use\n\t */\n\tpublic void setUserDetailsPasswordService(ReactiveUserDetailsPasswordService userDetailsPasswordService) {\n\t\tAssert.notNull(userDetailsPasswordService, \"userDetailsPasswordService cannot be null\");\n\t\tthis.userDetailsPasswordService = userDetailsPasswordService;\n\t}\n\n\t/**\n\t * Sets the strategy which will be used to validate the loaded <tt>UserDetails</tt>\n\t * object after authentication occurs.\n\t * @param postAuthenticationChecks The {@link UserDetailsChecker}\n\t * @since 5.2\n\t */\n\tpublic void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks) {\n\t\tAssert.notNull(this.postAuthenticationChecks, \"postAuthenticationChecks cannot be null\");\n\t\tthis.postAuthenticationChecks = postAuthenticationChecks;\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t/**\n\t * Sets the {@link ReactiveCompromisedPasswordChecker} to be used before creating a\n\t * successful authentication. Defaults to {@code null}.\n\t * @param compromisedPasswordChecker the {@link CompromisedPasswordChecker} to use\n\t * @since 6.3\n\t */\n\tpublic void setCompromisedPasswordChecker(ReactiveCompromisedPasswordChecker compromisedPasswordChecker) {\n\t\tthis.compromisedPasswordChecker = compromisedPasswordChecker;\n\t}\n\n\t/**\n\t * Allows subclasses to retrieve the <code>UserDetails</code> from an\n\t * implementation-specific location.\n\t * @param username The username to retrieve\n\t * @return the user information. If authentication fails, a Mono error is returned.\n\t */\n\tprotected abstract Mono<UserDetails> retrieveUser(String username);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AccountExpiredException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\n/**\n * Thrown if an authentication request is rejected because the account has expired. Makes\n * no assertion as to whether or not the credentials were valid.\n *\n * @author Ben Alex\n */\npublic class AccountExpiredException extends AccountStatusException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 3732869526329993353L;\n\n\t/**\n\t * Constructs a <code>AccountExpiredException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic AccountExpiredException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs a <code>AccountExpiredException</code> with the specified message and\n\t * root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic AccountExpiredException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AccountStatusException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Base class for authentication exceptions which are caused by a particular user account\n * status (locked, disabled etc).\n *\n * @author Luke Taylor\n */\npublic abstract class AccountStatusException extends AuthenticationException {\n\n\tpublic AccountStatusException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic AccountStatusException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AccountStatusUserDetailsChecker.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.util.Assert;\n\n/**\n * @author Luke Taylor\n */\npublic class AccountStatusUserDetailsChecker implements UserDetailsChecker, MessageSourceAware {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\t@Override\n\tpublic void check(UserDetails user) {\n\t\tif (!user.isAccountNonLocked()) {\n\t\t\tthis.logger.debug(\"Failed to authenticate since user account is locked\");\n\t\t\tthrow new LockedException(\n\t\t\t\t\tthis.messages.getMessage(\"AccountStatusUserDetailsChecker.locked\", \"User account is locked\"));\n\t\t}\n\t\tif (!user.isEnabled()) {\n\t\t\tthis.logger.debug(\"Failed to authenticate since user account is disabled\");\n\t\t\tthrow new DisabledException(\n\t\t\t\t\tthis.messages.getMessage(\"AccountStatusUserDetailsChecker.disabled\", \"User is disabled\"));\n\t\t}\n\t\tif (!user.isAccountNonExpired()) {\n\t\t\tthis.logger.debug(\"Failed to authenticate since user account is expired\");\n\t\t\tthrow new AccountExpiredException(\n\t\t\t\t\tthis.messages.getMessage(\"AccountStatusUserDetailsChecker.expired\", \"User account has expired\"));\n\t\t}\n\t\tif (!user.isCredentialsNonExpired()) {\n\t\t\tthis.logger.debug(\"Failed to authenticate since user account credentials have expired\");\n\t\t\tthrow new CredentialsExpiredException(this.messages\n\t\t\t\t.getMessage(\"AccountStatusUserDetailsChecker.credentialsExpired\", \"User credentials have expired\"));\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.2\n\t */\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AnonymousAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation that validates\n * {@link AnonymousAuthenticationToken}s.\n * <p>\n * To be successfully validated, the {@link AnonymousAuthenticationToken#getKeyHash()}\n * must match this class' {@link #getKey()}.\n *\n * @author Ben Alex\n */\npublic class AnonymousAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate String key;\n\n\tpublic AnonymousAuthenticationProvider(String key) {\n\t\tAssert.hasLength(key, \"A Key is required\");\n\t\tthis.key = key;\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (!supports(authentication.getClass())) {\n\t\t\treturn null;\n\t\t}\n\t\tif (this.key.hashCode() != ((AnonymousAuthenticationToken) authentication).getKeyHash()) {\n\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"AnonymousAuthenticationProvider.incorrectKey\",\n\t\t\t\t\t\"The presented AnonymousAuthenticationToken does not contain the expected key\"));\n\t\t}\n\t\treturn authentication;\n\t}\n\n\tpublic String getKey() {\n\t\treturn this.key;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn (AnonymousAuthenticationToken.class.isAssignableFrom(authentication));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AnonymousAuthenticationToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serializable;\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Represents an anonymous <code>Authentication</code>.\n *\n * @author Ben Alex\n */\npublic class AnonymousAuthenticationToken extends AbstractAuthenticationToken implements Serializable {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final Object principal;\n\n\tprivate final int keyHash;\n\n\t/**\n\t * Constructor.\n\t * @param key to identify if this object made by an authorised client\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param authorities the authorities granted to the principal\n\t * @throws IllegalArgumentException if a <code>null</code> was passed\n\t */\n\tpublic AnonymousAuthenticationToken(String key, Object principal,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tthis(extractKeyHash(key), principal, authorities);\n\t}\n\n\t/**\n\t * Constructor helps in Jackson Deserialization\n\t * @param keyHash hashCode of provided Key, constructed by above constructor\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param authorities the authorities granted to the principal\n\t * @since 4.2\n\t */\n\tprivate AnonymousAuthenticationToken(Integer keyHash, Object principal,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tAssert.isTrue(principal != null && !\"\".equals(principal), \"principal cannot be null or empty\");\n\t\tAssert.notEmpty(authorities, \"authorities cannot be null or empty\");\n\t\tthis.keyHash = keyHash;\n\t\tthis.principal = principal;\n\t\tsetAuthenticated(true);\n\t}\n\n\tprivate static Integer extractKeyHash(String key) {\n\t\tAssert.hasLength(key, \"key cannot be empty or null\");\n\t\treturn key.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (!super.equals(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (obj instanceof AnonymousAuthenticationToken test) {\n\t\t\treturn (this.getKeyHash() == test.getKeyHash());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = super.hashCode();\n\t\tresult = 31 * result + this.keyHash;\n\t\treturn result;\n\t}\n\n\t/**\n\t * Always returns an empty <code>String</code>\n\t * @return an empty String\n\t */\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\tpublic int getKeyHash() {\n\t\treturn this.keyHash;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationCredentialsNotFoundException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Thrown if an authentication request is rejected because there is no\n * {@link Authentication} object in the\n * {@link org.springframework.security.core.context.SecurityContext SecurityContext}.\n *\n * @author Ben Alex\n */\npublic class AuthenticationCredentialsNotFoundException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 4153580041526791384L;\n\n\t/**\n\t * Constructs an <code>AuthenticationCredentialsNotFoundException</code> with the\n\t * specified message.\n\t * @param msg the detail message\n\t */\n\tpublic AuthenticationCredentialsNotFoundException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>AuthenticationCredentialsNotFoundException</code> with the\n\t * specified message and root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic AuthenticationCredentialsNotFoundException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationDetailsSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\n/**\n * Provides a {@link org.springframework.security.core.Authentication#getDetails()} object\n * for a given web request.\n *\n * @author Ben Alex\n */\npublic interface AuthenticationDetailsSource<C, T> {\n\n\t/**\n\t * Called by a class when it wishes a new authentication details instance to be\n\t * created.\n\t * @param context the request object, which may be used by the authentication details\n\t * object\n\t * @return a fully-configured authentication details instance\n\t */\n\tT buildDetails(C context);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationEventPublisher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface AuthenticationEventPublisher {\n\n\tvoid publishAuthenticationSuccess(Authentication authentication);\n\n\tvoid publishAuthenticationFailure(AuthenticationException exception, Authentication authentication);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationManager.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Processes an {@link Authentication} request.\n *\n * @author Ben Alex\n * @author KyeongHoon Lee\n */\n@FunctionalInterface\npublic interface AuthenticationManager {\n\n\t/**\n\t * Attempts to authenticate the passed {@link Authentication} object, returning a\n\t * fully populated <code>Authentication</code> object (including granted authorities)\n\t * if successful.\n\t * <p>\n\t * An <code>AuthenticationManager</code> must honour the following contract concerning\n\t * exceptions:\n\t * <ul>\n\t * <li>A {@link DisabledException} must be thrown if an account is disabled and the\n\t * <code>AuthenticationManager</code> can test for this state.</li>\n\t * <li>A {@link LockedException} must be thrown if an account is locked and the\n\t * <code>AuthenticationManager</code> can test for account locking.</li>\n\t * <li>A {@link BadCredentialsException} must be thrown if incorrect credentials are\n\t * presented. Whilst the above exceptions are optional, an\n\t * <code>AuthenticationManager</code> must <B>always</B> test credentials.</li>\n\t * </ul>\n\t * Exceptions should be tested for and if applicable thrown in the order expressed\n\t * above (i.e. if an account is disabled or locked, the authentication request is\n\t * immediately rejected and the credentials testing process is not performed). This\n\t * prevents credentials being tested against disabled or locked accounts.\n\t * @param authentication the authentication request object\n\t * @return a fully authenticated object including credentials\n\t * @throws AuthenticationException if authentication fails\n\t */\n\tAuthentication authenticate(Authentication authentication) throws AuthenticationException;\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationManagerResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\n/**\n * An interface for resolving an {@link AuthenticationManager} based on the provided\n * context\n *\n * @author Josh Cummings\n * @since 5.2\n */\npublic interface AuthenticationManagerResolver<C> {\n\n\t/**\n\t * Resolve an {@link AuthenticationManager} from a provided context\n\t * @param context\n\t * @return the {@link AuthenticationManager} to use\n\t */\n\tAuthenticationManager resolve(C context);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationObservationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport io.micrometer.observation.Observation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Observation.Context} used during authentications\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic class AuthenticationObservationContext extends Observation.Context {\n\n\tprivate @Nullable Authentication authenticationRequest;\n\n\tprivate @Nullable Class<?> authenticationManager;\n\n\tprivate @Nullable Authentication authenticationResult;\n\n\t/**\n\t * Get the {@link Authentication} request that was observed\n\t * @return the observed {@link Authentication} request\n\t */\n\tpublic @Nullable Authentication getAuthenticationRequest() {\n\t\treturn this.authenticationRequest;\n\t}\n\n\t/**\n\t * Set the {@link Authentication} request that was observed\n\t * @param authenticationRequest the observed {@link Authentication} request\n\t */\n\tpublic void setAuthenticationRequest(Authentication authenticationRequest) {\n\t\tAssert.notNull(authenticationRequest, \"authenticationRequest cannot be null\");\n\t\tthis.authenticationRequest = authenticationRequest;\n\t}\n\n\t/**\n\t * Get the {@link Authentication} result that was observed\n\t *\n\t * <p>\n\t * Note that if authentication failed, no {@link Authentication} result can be\n\t * observed. In that case, this returns {@code null}.\n\t * @return any observed {@link Authentication} result, {@code null} otherwise\n\t */\n\tpublic @Nullable Authentication getAuthenticationResult() {\n\t\treturn this.authenticationResult;\n\t}\n\n\t/**\n\t * Set the {@link Authentication} result that was observed\n\t * @param authenticationResult the observed {@link Authentication} result\n\t */\n\tpublic void setAuthenticationResult(Authentication authenticationResult) {\n\t\tthis.authenticationResult = authenticationResult;\n\t}\n\n\t/**\n\t * Get the {@link AuthenticationManager} class that processed the authentication\n\t * @return the observed {@link AuthenticationManager} class\n\t */\n\tpublic @Nullable Class<?> getAuthenticationManagerClass() {\n\t\treturn this.authenticationManager;\n\t}\n\n\t/**\n\t * Set the {@link AuthenticationManager} class that processed the authentication\n\t * @param authenticationManagerClass the observed {@link AuthenticationManager} class\n\t */\n\tpublic void setAuthenticationManagerClass(Class<?> authenticationManagerClass) {\n\t\tAssert.notNull(authenticationManagerClass, \"authenticationManagerClass class cannot be null\");\n\t\tthis.authenticationManager = authenticationManagerClass;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationObservationConvention.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Locale;\n\nimport io.micrometer.common.KeyValues;\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationConvention;\nimport org.jspecify.annotations.NonNull;\n\n/**\n * An {@link ObservationConvention} for translating authentications into\n * {@link KeyValues}.\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic final class AuthenticationObservationConvention\n\t\timplements ObservationConvention<AuthenticationObservationContext> {\n\n\tstatic final String OBSERVATION_NAME = \"spring.security.authentications\";\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn OBSERVATION_NAME;\n\t}\n\n\t@Override\n\tpublic String getContextualName(AuthenticationObservationContext context) {\n\t\tif (context.getAuthenticationRequest() != null) {\n\t\t\tString authenticationType = context.getAuthenticationRequest().getClass().getSimpleName();\n\t\t\tif (authenticationType.endsWith(\"Token\")) {\n\t\t\t\tauthenticationType = authenticationType.substring(0, authenticationType.lastIndexOf(\"Token\"));\n\t\t\t}\n\t\t\tif (authenticationType.endsWith(\"Authentication\")) {\n\t\t\t\tauthenticationType = authenticationType.substring(0, authenticationType.lastIndexOf(\"Authentication\"));\n\t\t\t}\n\t\t\treturn \"authenticate \" + authenticationType.toLowerCase(Locale.ENGLISH);\n\t\t}\n\t\treturn \"authenticate\";\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @NonNull KeyValues getLowCardinalityKeyValues(@NonNull AuthenticationObservationContext context) {\n\t\treturn KeyValues.of(\"authentication.request.type\", getAuthenticationType(context))\n\t\t\t.and(\"authentication.method\", getAuthenticationMethod(context))\n\t\t\t.and(\"authentication.result.type\", getAuthenticationResult(context))\n\t\t\t.and(\"authentication.failure.type\", getAuthenticationFailureType(context));\n\t}\n\n\tprivate String getAuthenticationType(AuthenticationObservationContext context) {\n\t\tif (context.getAuthenticationRequest() == null) {\n\t\t\treturn \"unknown\";\n\t\t}\n\t\treturn context.getAuthenticationRequest().getClass().getSimpleName();\n\t}\n\n\tprivate String getAuthenticationMethod(AuthenticationObservationContext context) {\n\t\tif (context.getAuthenticationManagerClass() == null) {\n\t\t\treturn \"unknown\";\n\t\t}\n\t\treturn context.getAuthenticationManagerClass().getSimpleName();\n\t}\n\n\tprivate String getAuthenticationResult(AuthenticationObservationContext context) {\n\t\tif (context.getAuthenticationResult() == null) {\n\t\t\treturn \"n/a\";\n\t\t}\n\t\treturn context.getAuthenticationResult().getClass().getSimpleName();\n\t}\n\n\tprivate String getAuthenticationFailureType(AuthenticationObservationContext context) {\n\t\tif (context.getError() == null) {\n\t\t\treturn \"n/a\";\n\t\t}\n\t\treturn context.getError().getClass().getSimpleName();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic boolean supportsContext(Observation.Context context) {\n\t\treturn context instanceof AuthenticationObservationContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Indicates a class can process a specific\n * {@link org.springframework.security.core.Authentication} implementation.\n *\n * @author Ben Alex\n */\npublic interface AuthenticationProvider {\n\n\t/**\n\t * Performs authentication with the same contract as\n\t * {@link org.springframework.security.authentication.AuthenticationManager#authenticate(Authentication)}\n\t * .\n\t * @param authentication the authentication request object.\n\t * @return a fully authenticated object including credentials. May return\n\t * <code>null</code> if the <code>AuthenticationProvider</code> is unable to support\n\t * authentication of the passed <code>Authentication</code> object. In such a case,\n\t * the next <code>AuthenticationProvider</code> that supports the presented\n\t * <code>Authentication</code> class will be tried.\n\t * @throws AuthenticationException if authentication fails.\n\t */\n\t@Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException;\n\n\t/**\n\t * Returns <code>true</code> if this <Code>AuthenticationProvider</code> supports the\n\t * indicated <Code>Authentication</code> object.\n\t * <p>\n\t * Returning <code>true</code> does not guarantee an\n\t * <code>AuthenticationProvider</code> will be able to authenticate the presented\n\t * <code>Authentication</code> object. It simply indicates it can support closer\n\t * evaluation of it. An <code>AuthenticationProvider</code> can still return\n\t * <code>null</code> from the {@link #authenticate(Authentication)} method to indicate\n\t * another <code>AuthenticationProvider</code> should be tried.\n\t * </p>\n\t * <p>\n\t * Selection of an <code>AuthenticationProvider</code> capable of performing\n\t * authentication is conducted at runtime by the <code>ProviderManager</code>.\n\t * </p>\n\t * @param authentication\n\t * @return <code>true</code> if the implementation can more closely evaluate the\n\t * <code>Authentication</code> class presented\n\t */\n\tboolean supports(Class<?> authentication);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationServiceException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Thrown if an authentication request could not be processed due to a system problem.\n * <p>\n * This might be thrown if a backend authentication repository is unavailable, for\n * example.\n *\n * @author Ben Alex\n * @see InternalAuthenticationServiceException\n */\npublic class AuthenticationServiceException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1591626195291329340L;\n\n\t/**\n\t * Constructs an <code>AuthenticationServiceException</code> with the specified\n\t * message.\n\t * @param msg the detail message\n\t */\n\tpublic AuthenticationServiceException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>AuthenticationServiceException</code> with the specified\n\t * message and root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic AuthenticationServiceException(@Nullable String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolver.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.lang.Contract;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Evaluates <code>Authentication</code> tokens\n *\n * @author Ben Alex\n */\npublic interface AuthenticationTrustResolver {\n\n\t/**\n\t * Indicates whether the passed <code>Authentication</code> token represents an\n\t * anonymous user. Typically the framework will call this method if it is trying to\n\t * decide whether an <code>AccessDeniedException</code> should result in a final\n\t * rejection (i.e. as would be the case if the principal was non-anonymous/fully\n\t * authenticated) or direct the principal to attempt actual authentication (i.e. as\n\t * would be the case if the <code>Authentication</code> was merely anonymous).\n\t * @param authentication to test (may be <code>null</code> in which case the method\n\t * will always return <code>false</code>)\n\t * @return <code>true</code> the passed authentication token represented an anonymous\n\t * principal, <code>false</code> otherwise\n\t */\n\tboolean isAnonymous(@Nullable Authentication authentication);\n\n\t/**\n\t * Indicates whether the passed <code>Authentication</code> token represents user that\n\t * has been remembered (i.e. not a user that has been fully authenticated).\n\t * <p>\n\t * The method is provided to assist with custom <code>AccessDecisionVoter</code>s and\n\t * the like that you might develop. Of course, you don't need to use this method\n\t * either and can develop your own \"trust level\" hierarchy instead.\n\t * @param authentication to test (may be <code>null</code> in which case the method\n\t * will always return <code>false</code>)\n\t * @return <code>true</code> the passed authentication token represented a principal\n\t * authenticated using a remember-me token, <code>false</code> otherwise\n\t */\n\tboolean isRememberMe(@Nullable Authentication authentication);\n\n\t/**\n\t * Indicates whether the passed <code>Authentication</code> token represents a fully\n\t * authenticated user (that is, neither anonymous or remember-me). This is a\n\t * composition of <code>isAnonymous</code> and <code>isRememberMe</code>\n\t * implementation\n\t * <p>\n\t * @param authentication to test (may be <code>null</code> in which case the method\n\t * will always return <code>false</code>)\n\t * @return <code>true</code> the passed authentication token represented an\n\t * authenticated user ({@link #isAuthenticated(Authentication)} and not\n\t * {@link #isRememberMe(Authentication)}, <code>false</code> otherwise\n\t * @since 6.1\n\t */\n\tdefault boolean isFullyAuthenticated(@Nullable Authentication authentication) {\n\t\treturn isAuthenticated(authentication) && !isRememberMe(authentication);\n\t}\n\n\t/**\n\t * Checks if the {@link Authentication} is not null, authenticated, and not anonymous.\n\t * @param authentication the {@link Authentication} to check.\n\t * @return true if the {@link Authentication} is not null,\n\t * {@link #isAnonymous(Authentication)} returns false and\n\t * {@link Authentication#isAuthenticated()} is true.\n\t * @since 6.1.7\n\t */\n\t@Contract(\"null -> false\")\n\tdefault boolean isAuthenticated(@Nullable Authentication authentication) {\n\t\treturn authentication != null && authentication.isAuthenticated() && !isAnonymous(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolverImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Basic implementation of {@link AuthenticationTrustResolver}.\n * <p>\n * Makes trust decisions based on whether the passed <code>Authentication</code> is an\n * instance of a defined class.\n * <p>\n * If {@link #anonymousClass} or {@link #rememberMeClass} is <code>null</code>, the\n * corresponding method will always return <code>false</code>.\n *\n * @author Ben Alex\n */\npublic class AuthenticationTrustResolverImpl implements AuthenticationTrustResolver {\n\n\tprivate Class<? extends Authentication> anonymousClass = AnonymousAuthenticationToken.class;\n\n\tprivate Class<? extends Authentication> rememberMeClass = RememberMeAuthenticationToken.class;\n\n\tClass<? extends Authentication> getAnonymousClass() {\n\t\treturn this.anonymousClass;\n\t}\n\n\tClass<? extends Authentication> getRememberMeClass() {\n\t\treturn this.rememberMeClass;\n\t}\n\n\t@Override\n\tpublic boolean isAnonymous(@Nullable Authentication authentication) {\n\t\tif ((this.anonymousClass == null) || (authentication == null)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.anonymousClass.isAssignableFrom(authentication.getClass());\n\t}\n\n\t@Override\n\tpublic boolean isRememberMe(@Nullable Authentication authentication) {\n\t\tif ((this.rememberMeClass == null) || (authentication == null)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.rememberMeClass.isAssignableFrom(authentication.getClass());\n\t}\n\n\tpublic void setAnonymousClass(Class<? extends Authentication> anonymousClass) {\n\t\tthis.anonymousClass = anonymousClass;\n\t}\n\n\tpublic void setRememberMeClass(Class<? extends Authentication> rememberMeClass) {\n\t\tthis.rememberMeClass = rememberMeClass;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/BadCredentialsException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Thrown if an authentication request is rejected because the credentials are invalid.\n * For this exception to be thrown, it means the account is neither locked nor disabled.\n *\n * @author Ben Alex\n */\npublic class BadCredentialsException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2742216069043066973L;\n\n\t/**\n\t * Constructs a <code>BadCredentialsException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic BadCredentialsException(@Nullable String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs a <code>BadCredentialsException</code> with the specified message and\n\t * root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic BadCredentialsException(@Nullable String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/CachingUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.cache.NullUserCache;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of {@link UserDetailsService} that utilizes caching through a\n * {@link UserCache}\n * <p>\n * If a null {@link UserDetails} instance is returned from\n * {@link UserCache#getUserFromCache(String)} to the {@link UserCache} got from\n * {@link #getUserCache()}, the user load is deferred to the {@link UserDetailsService}\n * provided during construction. Otherwise, the instance retrieved from the cache is\n * returned.\n * <p>\n * It is initialized with a {@link NullUserCache} by default, so it's strongly recommended\n * setting your own {@link UserCache} using {@link #setUserCache(UserCache)}, otherwise,\n * the delegate will be called every time.\n * <p>\n * Utilize this class by defining a {@link org.springframework.context.annotation.Bean}\n * that encapsulates an actual implementation of {@link UserDetailsService} and providing\n * a {@link UserCache} implementation.\n * </p>\n * For example: <pre>\n * &#64;Bean\n * public CachingUserDetailsService cachingUserDetailsService(UserCache userCache) {\n *     UserDetailsService delegate = ...;\n *     CachingUserDetailsService service = new CachingUserDetailsService(delegate);\n *     service.setUserCache(userCache);\n *     return service;\n * }\n * </pre>\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class CachingUserDetailsService implements UserDetailsService {\n\n\tprivate UserCache userCache = new NullUserCache();\n\n\tprivate final UserDetailsService delegate;\n\n\tpublic CachingUserDetailsService(UserDetailsService delegate) {\n\t\tthis.delegate = delegate;\n\t}\n\n\tpublic UserCache getUserCache() {\n\t\treturn this.userCache;\n\t}\n\n\tpublic void setUserCache(UserCache userCache) {\n\t\tthis.userCache = userCache;\n\t}\n\n\t@Override\n\tpublic UserDetails loadUserByUsername(String username) {\n\t\tUserDetails user = this.userCache.getUserFromCache(username);\n\t\tif (user == null) {\n\t\t\tuser = this.delegate.loadUserByUsername(username);\n\t\t}\n\t\tAssert.notNull(user, () -> \"UserDetailsService \" + this.delegate + \" returned null for username \" + username\n\t\t\t\t+ \". \" + \"This is an interface contract violation\");\n\t\tthis.userCache.putUserInCache(user);\n\t\treturn user;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/CredentialsExpiredException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\n/**\n * Thrown if an authentication request is rejected because the account's credentials have\n * expired. Makes no assertion as to whether or not the credentials were valid.\n *\n * @author Ben Alex\n */\npublic class CredentialsExpiredException extends AccountStatusException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3306615738048904753L;\n\n\t/**\n\t * Constructs a <code>CredentialsExpiredException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic CredentialsExpiredException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs a <code>CredentialsExpiredException</code> with the specified message\n\t * and root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic CredentialsExpiredException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/DefaultAuthenticationEventPublisher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.security.authentication.event.AbstractAuthenticationEvent;\nimport org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureCredentialsExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureDisabledEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureLockedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProviderNotFoundEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProxyUntrustedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureServiceExceptionEvent;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.util.Assert;\n\n/**\n * The default strategy for publishing authentication events.\n * <p>\n * Maps well-known <tt>AuthenticationException</tt> types to events and publishes them via\n * the application context. If configured as a bean, it will pick up the\n * <tt>ApplicationEventPublisher</tt> automatically. Otherwise, the constructor which\n * takes the publisher as an argument should be used.\n * <p>\n * The exception-mapping system can be fine-tuned by setting the\n * <tt>additionalExceptionMappings</tt> as a <code>java.util.Properties</code> object. In\n * the properties object, each of the keys represent the fully qualified classname of the\n * exception, and each of the values represent the name of an event class which subclasses\n * {@link org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent}\n * and provides its constructor. The <tt>additionalExceptionMappings</tt> will be merged\n * with the default ones.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class DefaultAuthenticationEventPublisher\n\t\timplements AuthenticationEventPublisher, ApplicationEventPublisherAware {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate ApplicationEventPublisher applicationEventPublisher;\n\n\tprivate final HashMap<String, Constructor<? extends AbstractAuthenticationEvent>> exceptionMappings = new HashMap<>();\n\n\tprivate @Nullable Constructor<? extends AbstractAuthenticationFailureEvent> defaultAuthenticationFailureEventConstructor;\n\n\tpublic DefaultAuthenticationEventPublisher() {\n\t\tthis((event) -> {\n\t\t});\n\t}\n\n\tpublic DefaultAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tAssert.notNull(applicationEventPublisher, \"applicationEventPublisher cannot be null\");\n\t\tthis.applicationEventPublisher = applicationEventPublisher;\n\t\taddMapping(BadCredentialsException.class.getName(), AuthenticationFailureBadCredentialsEvent.class);\n\t\taddMapping(UsernameNotFoundException.class.getName(), AuthenticationFailureBadCredentialsEvent.class);\n\t\taddMapping(AccountExpiredException.class.getName(), AuthenticationFailureExpiredEvent.class);\n\t\taddMapping(ProviderNotFoundException.class.getName(), AuthenticationFailureProviderNotFoundEvent.class);\n\t\taddMapping(DisabledException.class.getName(), AuthenticationFailureDisabledEvent.class);\n\t\taddMapping(LockedException.class.getName(), AuthenticationFailureLockedEvent.class);\n\t\taddMapping(AuthenticationServiceException.class.getName(), AuthenticationFailureServiceExceptionEvent.class);\n\t\taddMapping(CredentialsExpiredException.class.getName(), AuthenticationFailureCredentialsExpiredEvent.class);\n\t\taddMapping(\"org.springframework.security.authentication.cas.ProxyUntrustedException\",\n\t\t\t\tAuthenticationFailureProxyUntrustedEvent.class);\n\t\taddMapping(\"org.springframework.security.oauth2.server.resource.InvalidBearerTokenException\",\n\t\t\t\tAuthenticationFailureBadCredentialsEvent.class);\n\t}\n\n\t@Override\n\tpublic void publishAuthenticationSuccess(Authentication authentication) {\n\t\tif (this.applicationEventPublisher != null) {\n\t\t\tthis.applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent(authentication));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {\n\t\tConstructor<? extends AbstractAuthenticationEvent> constructor = getEventConstructor(exception);\n\t\tAbstractAuthenticationEvent event = null;\n\t\tif (constructor != null) {\n\t\t\ttry {\n\t\t\t\tevent = constructor.newInstance(authentication, exception);\n\t\t\t}\n\t\t\tcatch (IllegalAccessException | InvocationTargetException | InstantiationException ignored) {\n\t\t\t}\n\t\t}\n\t\tif (event != null) {\n\t\t\tif (this.applicationEventPublisher != null) {\n\t\t\t\tthis.applicationEventPublisher.publishEvent(event);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(\"No event was found for the exception \" + exception.getClass().getName());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate @Nullable Constructor<? extends AbstractAuthenticationEvent> getEventConstructor(\n\t\t\tAuthenticationException exception) {\n\t\tConstructor<? extends AbstractAuthenticationEvent> eventConstructor = this.exceptionMappings\n\t\t\t.get(exception.getClass().getName());\n\t\treturn (eventConstructor != null) ? eventConstructor : this.defaultAuthenticationFailureEventConstructor;\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tthis.applicationEventPublisher = applicationEventPublisher;\n\t}\n\n\t/**\n\t * Sets additional exception to event mappings. These are automatically merged with\n\t * the default exception to event mappings that <code>ProviderManager</code> defines.\n\t * @param additionalExceptionMappings where keys are the fully-qualified string name\n\t * of the exception class and the values are the fully-qualified string name of the\n\t * event class to fire.\n\t * @deprecated use {@link #setAdditionalExceptionMappings(Map)}\n\t */\n\t@Deprecated\n\t@SuppressWarnings({ \"unchecked\" })\n\tpublic void setAdditionalExceptionMappings(Properties additionalExceptionMappings) {\n\t\tAssert.notNull(additionalExceptionMappings, \"The exceptionMappings object must not be null\");\n\t\tfor (Object exceptionClass : additionalExceptionMappings.keySet()) {\n\t\t\tString eventClass = (String) additionalExceptionMappings.get(exceptionClass);\n\t\t\ttry {\n\t\t\t\tClass<?> clazz = getClass().getClassLoader().loadClass(eventClass);\n\t\t\t\tAssert.isAssignable(AbstractAuthenticationFailureEvent.class, clazz);\n\t\t\t\taddMapping((String) exceptionClass, (Class<? extends AbstractAuthenticationFailureEvent>) clazz);\n\t\t\t}\n\t\t\tcatch (ClassNotFoundException ex) {\n\t\t\t\tthrow new RuntimeException(\"Failed to load authentication event class \" + eventClass);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sets additional exception to event mappings. These are automatically merged with\n\t * the default exception to event mappings that <code>ProviderManager</code> defines.\n\t * @param mappings where keys are exception classes and values are event classes.\n\t * @since 5.3\n\t */\n\tpublic void setAdditionalExceptionMappings(\n\t\t\tMap<Class<? extends AuthenticationException>, Class<? extends AbstractAuthenticationFailureEvent>> mappings) {\n\t\tAssert.notEmpty(mappings, \"The mappings Map must not be empty nor null\");\n\t\tfor (Map.Entry<Class<? extends AuthenticationException>, Class<? extends AbstractAuthenticationFailureEvent>> entry : mappings\n\t\t\t.entrySet()) {\n\t\t\tClass<?> exceptionClass = entry.getKey();\n\t\t\tClass<?> eventClass = entry.getValue();\n\t\t\tAssert.notNull(exceptionClass, \"exceptionClass cannot be null\");\n\t\t\tAssert.notNull(eventClass, \"eventClass cannot be null\");\n\t\t\taddMapping(exceptionClass.getName(), (Class<? extends AbstractAuthenticationFailureEvent>) eventClass);\n\t\t}\n\t}\n\n\t/**\n\t * Sets a default authentication failure event as a fallback event for any unmapped\n\t * exceptions not mapped in the exception mappings.\n\t * @param defaultAuthenticationFailureEventClass is the authentication failure event\n\t * class to be fired for unmapped exceptions.\n\t */\n\tpublic void setDefaultAuthenticationFailureEvent(\n\t\t\tClass<? extends AbstractAuthenticationFailureEvent> defaultAuthenticationFailureEventClass) {\n\t\tAssert.notNull(defaultAuthenticationFailureEventClass,\n\t\t\t\t\"defaultAuthenticationFailureEventClass must not be null\");\n\t\ttry {\n\t\t\tthis.defaultAuthenticationFailureEventConstructor = defaultAuthenticationFailureEventClass\n\t\t\t\t.getConstructor(Authentication.class, AuthenticationException.class);\n\t\t}\n\t\tcatch (NoSuchMethodException ex) {\n\t\t\tthrow new RuntimeException(\"Default Authentication Failure event class \"\n\t\t\t\t\t+ defaultAuthenticationFailureEventClass.getName() + \" has no suitable constructor\");\n\t\t}\n\t}\n\n\tprivate void addMapping(String exceptionClass, Class<? extends AbstractAuthenticationFailureEvent> eventClass) {\n\t\ttry {\n\t\t\tConstructor<? extends AbstractAuthenticationEvent> constructor = eventClass\n\t\t\t\t.getConstructor(Authentication.class, AuthenticationException.class);\n\t\t\tthis.exceptionMappings.put(exceptionClass, constructor);\n\t\t}\n\t\tcatch (NoSuchMethodException ex) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"Authentication event class \" + eventClass.getName() + \" has no suitable constructor\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthenticationManager} that delegates to other\n * {@link ReactiveAuthenticationManager} instances. When {@code continueOnError} is\n * {@code true}, will continue until the first non-empty, non-error result; otherwise,\n * will continue only until the first non-empty result.\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic class DelegatingReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate final List<ReactiveAuthenticationManager> delegates;\n\n\tprivate boolean continueOnError = false;\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tpublic DelegatingReactiveAuthenticationManager(ReactiveAuthenticationManager... entryPoints) {\n\t\tthis(Arrays.asList(entryPoints));\n\t}\n\n\tpublic DelegatingReactiveAuthenticationManager(List<ReactiveAuthenticationManager> entryPoints) {\n\t\tAssert.notEmpty(entryPoints, \"entryPoints cannot be null\");\n\t\tthis.delegates = entryPoints;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\tFlux<ReactiveAuthenticationManager> result = Flux.fromIterable(this.delegates);\n\t\tFunction<ReactiveAuthenticationManager, Mono<Authentication>> logging = (m) -> m.authenticate(authentication)\n\t\t\t.doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication))\n\t\t\t.doOnError(this.logger::debug);\n\t\treturn ((this.continueOnError) ? result.concatMapDelayError(logging) : result.concatMap(logging)).next();\n\t}\n\n\t/**\n\t * Continue iterating when a delegate errors, defaults to {@code false}\n\t * @param continueOnError whether to continue when a delegate errors\n\t * @since 6.3\n\t */\n\tpublic void setContinueOnError(boolean continueOnError) {\n\t\tthis.continueOnError = continueOnError;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/DisabledException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\n/**\n * Thrown if an authentication request is rejected because the account is disabled. Makes\n * no assertion as to whether or not the credentials were valid.\n *\n * @author Ben Alex\n */\npublic class DisabledException extends AccountStatusException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2295984593872502361L;\n\n\t/**\n\t * Constructs a <code>DisabledException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic DisabledException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs a <code>DisabledException</code> with the specified message and root\n\t * cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic DisabledException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/InsufficientAuthenticationException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Thrown if an authentication request is rejected because the credentials are not\n * sufficiently trusted.\n * <p>\n * {@link org.springframework.security.authorization.AuthorizationManager}s will typically\n * throw this exception if they are dissatisfied with the level of the authentication,\n * such as if performed using a remember-me mechanism or anonymously. The\n * {@code ExceptionTranslationFilter} will then typically cause the\n * {@code AuthenticationEntryPoint} to be called, allowing the principal to authenticate\n * with a stronger level of authentication.\n *\n * @author Ben Alex\n */\npublic class InsufficientAuthenticationException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5514084346181236128L;\n\n\t/**\n\t * Constructs an <code>InsufficientAuthenticationException</code> with the specified\n\t * message.\n\t * @param msg the detail message\n\t */\n\tpublic InsufficientAuthenticationException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs an <code>InsufficientAuthenticationException</code> with the specified\n\t * message and root cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic InsufficientAuthenticationException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/InternalAuthenticationServiceException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * <p>\n * Thrown if an authentication request could not be processed due to a system problem that\n * occurred internally. It differs from {@link AuthenticationServiceException} in that it\n * would not be thrown if an external system has an internal error or failure. This\n * ensures that we can handle errors that are within our control distinctly from errors of\n * other systems. The advantage to this distinction is that the untrusted external system\n * should not be able to fill up logs and cause excessive IO. However, an internal system\n * should report errors.\n * </p>\n * <p>\n * This might be thrown if a backend authentication repository is unavailable, for\n * example. However, it would not be thrown in the event that an error occurred when\n * validating an OIDC response from an OIDC provider.\n * </p>\n *\n * @author Rob Winch\n *\n */\npublic class InternalAuthenticationServiceException extends AuthenticationServiceException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -6029644854192497840L;\n\n\tpublic InternalAuthenticationServiceException(@Nullable String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic InternalAuthenticationServiceException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/LockedException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\n/**\n * Thrown if an authentication request is rejected because the account is locked. Makes no\n * assertion as to whether or not the credentials were valid.\n *\n * @author Ben Alex\n */\npublic class LockedException extends AccountStatusException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 548864198455046567L;\n\n\t/**\n\t * Constructs a <code>LockedException</code> with the specified message.\n\t * @param msg the detail message.\n\t */\n\tpublic LockedException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs a <code>LockedException</code> with the specified message and root\n\t * cause.\n\t * @param msg the detail message.\n\t * @param cause root cause\n\t */\n\tpublic LockedException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ObservationAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationConvention;\nimport io.micrometer.observation.ObservationRegistry;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationManager} that observes the authentication\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic final class ObservationAuthenticationManager implements AuthenticationManager {\n\n\tprivate final ObservationRegistry registry;\n\n\tprivate final AuthenticationManager delegate;\n\n\tprivate ObservationConvention<AuthenticationObservationContext> convention = new AuthenticationObservationConvention();\n\n\tpublic ObservationAuthenticationManager(ObservationRegistry registry, AuthenticationManager delegate) {\n\t\tAssert.notNull(registry, \"observationRegistry cannot be null\");\n\t\tAssert.notNull(delegate, \"authenticationManager cannot be null\");\n\t\tthis.registry = registry;\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tAuthenticationObservationContext context = new AuthenticationObservationContext();\n\t\tcontext.setAuthenticationRequest(authentication);\n\t\tcontext.setAuthenticationManagerClass(this.delegate.getClass());\n\t\treturn Observation.createNotStarted(this.convention, () -> context, this.registry).observe(() -> {\n\t\t\tAuthentication result = this.delegate.authenticate(authentication);\n\t\t\tcontext.setAuthenticationResult(result);\n\t\t\treturn result;\n\t\t});\n\t}\n\n\t/**\n\t * Use the provided convention for reporting observation data\n\t * @param convention The provided convention\n\t *\n\t * @since 6.1\n\t */\n\tpublic void setObservationConvention(ObservationConvention<AuthenticationObservationContext> convention) {\n\t\tAssert.notNull(convention, \"The observation convention cannot be null\");\n\t\tthis.convention = convention;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ObservationReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationConvention;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link ReactiveAuthenticationManager} that observes the authentication\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic class ObservationReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate final ObservationRegistry registry;\n\n\tprivate final ReactiveAuthenticationManager delegate;\n\n\tprivate ObservationConvention<AuthenticationObservationContext> convention = new AuthenticationObservationConvention();\n\n\tpublic ObservationReactiveAuthenticationManager(ObservationRegistry registry,\n\t\t\tReactiveAuthenticationManager delegate) {\n\t\tthis.registry = registry;\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) throws AuthenticationException {\n\t\tAuthenticationObservationContext context = new AuthenticationObservationContext();\n\t\tcontext.setAuthenticationRequest(authentication);\n\t\tcontext.setAuthenticationManagerClass(this.delegate.getClass());\n\t\treturn Mono.deferContextual((contextView) -> {\n\t\t\tObservation observation = Observation.createNotStarted(this.convention, () -> context, this.registry)\n\t\t\t\t.parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null))\n\t\t\t\t.start();\n\t\t\treturn this.delegate.authenticate(authentication).doOnSuccess((result) -> {\n\t\t\t\tcontext.setAuthenticationResult(result);\n\t\t\t\tobservation.stop();\n\t\t\t}).doOnCancel(observation::stop).doOnError((t) -> {\n\t\t\t\tobservation.error(t);\n\t\t\t\tobservation.stop();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Use the provided convention for reporting observation data\n\t * @param convention The provided convention\n\t *\n\t * @since 6.1\n\t */\n\tpublic void setObservationConvention(ObservationConvention<AuthenticationObservationContext> convention) {\n\t\tAssert.notNull(convention, \"The observation convention cannot be null\");\n\t\tthis.convention = convention;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ProviderManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.CredentialsContainer;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Iterates an {@link Authentication} request through a list of\n * {@link AuthenticationProvider}s.\n *\n * <p>\n * <tt>AuthenticationProvider</tt>s are usually tried in order until one provides a\n * non-null response. A non-null response indicates the provider had authority to decide\n * on the authentication request and no further providers are tried. If a subsequent\n * provider successfully authenticates the request, the earlier authentication exception\n * is disregarded and the successful authentication will be used. If no subsequent\n * provider provides a non-null response, or a new <code>AuthenticationException</code>,\n * the last <code>AuthenticationException</code> received will be used. If no provider\n * returns a non-null response, or indicates it can even process an\n * <code>Authentication</code>, the <code>ProviderManager</code> will throw a\n * <code>ProviderNotFoundException</code>. A parent {@code AuthenticationManager} can also\n * be set, and this will also be tried if none of the configured providers can perform the\n * authentication. This is intended to support namespace configuration options though and\n * is not a feature that should normally be required.\n * <p>\n * The exception to this process is when a provider throws an\n * {@link AccountStatusException}, in which case no further providers in the list will be\n * queried.\n *\n * Post-authentication, the credentials will be cleared from the returned\n * {@code Authentication} object, if it implements the {@link CredentialsContainer}\n * interface. This behaviour can be controlled by modifying the\n * {@link #setEraseCredentialsAfterAuthentication(boolean)\n * eraseCredentialsAfterAuthentication} property.\n *\n * <h2>Event Publishing</h2>\n * <p>\n * Authentication event publishing is delegated to the configured\n * {@link AuthenticationEventPublisher} which defaults to a null implementation which\n * doesn't publish events, so if you are configuring the bean yourself you must inject a\n * publisher bean if you want to receive events. The standard implementation is\n * {@link DefaultAuthenticationEventPublisher} which maps common exceptions to events (in\n * the case of authentication failure) and publishes an\n * {@link org.springframework.security.authentication.event.AuthenticationSuccessEvent\n * AuthenticationSuccessEvent} if authentication succeeds. If you are using the namespace\n * then an instance of this bean will be used automatically by the <tt>&lt;http&gt;</tt>\n * configuration, so you will receive events from the web part of your application\n * automatically.\n * <p>\n * Note that the implementation also publishes authentication failure events when it\n * obtains an authentication result (or an exception) from the \"parent\"\n * {@code AuthenticationManager} if one has been set. So in this situation, the parent\n * should not generally be configured to publish events or there will be duplicates.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @see DefaultAuthenticationEventPublisher\n */\npublic class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {\n\n\tprivate static final Log logger = LogFactory.getLog(ProviderManager.class);\n\n\tprivate AuthenticationEventPublisher eventPublisher = new NullEventPublisher();\n\n\tprivate List<AuthenticationProvider> providers = Collections.emptyList();\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate @Nullable AuthenticationManager parent;\n\n\tprivate boolean eraseCredentialsAfterAuthentication = true;\n\n\t/**\n\t * Construct a {@link ProviderManager} using the given {@link AuthenticationProvider}s\n\t * @param providers the {@link AuthenticationProvider}s to use\n\t */\n\tpublic ProviderManager(AuthenticationProvider... providers) {\n\t\tthis(Arrays.asList(providers), null);\n\t}\n\n\t/**\n\t * Construct a {@link ProviderManager} using the given {@link AuthenticationProvider}s\n\t * @param providers the {@link AuthenticationProvider}s to use\n\t */\n\tpublic ProviderManager(List<AuthenticationProvider> providers) {\n\t\tthis(providers, null);\n\t}\n\n\t/**\n\t * Construct a {@link ProviderManager} using the provided parameters\n\t * @param providers the {@link AuthenticationProvider}s to use\n\t * @param parent a parent {@link AuthenticationManager} to fall back to\n\t */\n\tpublic ProviderManager(List<AuthenticationProvider> providers, @Nullable AuthenticationManager parent) {\n\t\tAssert.notNull(providers, \"providers list cannot be null\");\n\t\tthis.providers = providers;\n\t\tthis.parent = parent;\n\t\tcheckState();\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tcheckState();\n\t}\n\n\tprivate void checkState() {\n\t\tAssert.isTrue(this.parent != null || !this.providers.isEmpty(),\n\t\t\t\t\"A parent AuthenticationManager or a list of AuthenticationProviders is required\");\n\t\tAssert.isTrue(!CollectionUtils.contains(this.providers.iterator(), null),\n\t\t\t\t\"providers list cannot contain null values\");\n\t}\n\n\t/**\n\t * Attempts to authenticate the passed {@link Authentication} object.\n\t * <p>\n\t * The list of {@link AuthenticationProvider}s will be successively tried until an\n\t * <code>AuthenticationProvider</code> indicates it is capable of authenticating the\n\t * type of <code>Authentication</code> object passed. Authentication will then be\n\t * attempted with that <code>AuthenticationProvider</code>.\n\t * <p>\n\t * If more than one <code>AuthenticationProvider</code> supports the passed\n\t * <code>Authentication</code> object, the first one able to successfully authenticate\n\t * the <code>Authentication</code> object determines the <code>result</code>,\n\t * overriding any possible <code>AuthenticationException</code> thrown by earlier\n\t * supporting <code>AuthenticationProvider</code>s. On successful authentication, no\n\t * subsequent <code>AuthenticationProvider</code>s will be tried. If authentication\n\t * was not successful by any supporting <code>AuthenticationProvider</code> the last\n\t * thrown <code>AuthenticationException</code> will be rethrown.\n\t * @param authentication the authentication request object.\n\t * @return a fully authenticated object including credentials.\n\t * @throws AuthenticationException if authentication fails.\n\t */\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tClass<? extends Authentication> toTest = authentication.getClass();\n\t\tAuthenticationException lastException = null;\n\t\tAuthenticationException parentException = null;\n\t\tAuthentication result = null;\n\t\tAuthentication parentResult = null;\n\t\tint currentPosition = 0;\n\t\tint size = this.providers.size();\n\t\tfor (AuthenticationProvider provider : getProviders()) {\n\t\t\tif (!provider.supports(toTest)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(LogMessage.format(\"Authenticating request with %s (%d/%d)\",\n\t\t\t\t\t\tprovider.getClass().getSimpleName(), ++currentPosition, size));\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tresult = provider.authenticate(authentication);\n\t\t\t\tif (result != null) {\n\t\t\t\t\tcopyDetails(authentication, result);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (AccountStatusException ex) {\n\t\t\t\tprepareException(ex, authentication);\n\t\t\t\tlogger.debug(LogMessage.format(\"Authentication failed for user '%s' since their account status is %s\",\n\t\t\t\t\t\tauthentication.getName(), ex.getMessage()), ex);\n\t\t\t\t// SEC-546: Avoid polling additional providers if auth failure is due to\n\t\t\t\t// invalid account status\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tcatch (InternalAuthenticationServiceException ex) {\n\t\t\t\tprepareException(ex, authentication);\n\t\t\t\tlogger.debug(LogMessage.format(\"Authentication service failed internally for user '%s'\",\n\t\t\t\t\t\tauthentication.getName()), ex);\n\t\t\t\t// SEC-546: Avoid polling additional providers if auth failure is due to\n\t\t\t\t// invalid account status\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tcatch (AuthenticationException ex) {\n\t\t\t\tex.setAuthenticationRequest(authentication);\n\t\t\t\tlogger.debug(LogMessage.format(\"Authentication failed with provider %s since %s\",\n\t\t\t\t\t\tprovider.getClass().getSimpleName(), ex.getMessage()));\n\t\t\t\tlastException = ex;\n\t\t\t}\n\t\t}\n\t\tif (result == null && this.parent != null) {\n\t\t\t// Allow the parent to try.\n\t\t\ttry {\n\t\t\t\tparentResult = this.parent.authenticate(authentication);\n\t\t\t\tresult = parentResult;\n\t\t\t}\n\t\t\tcatch (ProviderNotFoundException ex) {\n\t\t\t\t// ignore as we will throw below if no other exception occurred prior to\n\t\t\t\t// calling parent and the parent\n\t\t\t\t// may throw ProviderNotFound even though a provider in the child already\n\t\t\t\t// handled the request\n\t\t\t}\n\t\t\tcatch (AuthenticationException ex) {\n\t\t\t\tparentException = ex;\n\t\t\t\tlastException = ex;\n\t\t\t}\n\t\t}\n\t\tif (result != null) {\n\t\t\tif (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {\n\t\t\t\t// Authentication is complete. Remove credentials and other secret data\n\t\t\t\t// from authentication\n\t\t\t\t((CredentialsContainer) result).eraseCredentials();\n\t\t\t}\n\t\t\t// If the parent AuthenticationManager was attempted and successful then it\n\t\t\t// will publish an AuthenticationSuccessEvent\n\t\t\t// This check prevents a duplicate AuthenticationSuccessEvent if the parent\n\t\t\t// AuthenticationManager already published it\n\t\t\tif (parentResult == null) {\n\t\t\t\tthis.eventPublisher.publishAuthenticationSuccess(result);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\t// Parent was null, or didn't authenticate (or throw an exception).\n\t\tif (lastException == null) {\n\t\t\tlastException = new ProviderNotFoundException(this.messages.getMessage(\"ProviderManager.providerNotFound\",\n\t\t\t\t\tnew Object[] { toTest.getName() }, \"No AuthenticationProvider found for {0}\"));\n\t\t}\n\t\t// If the parent AuthenticationManager was attempted and failed then it will\n\t\t// publish an AbstractAuthenticationFailureEvent\n\t\t// This check prevents a duplicate AbstractAuthenticationFailureEvent if the\n\t\t// parent AuthenticationManager already published it\n\t\tif (parentException == null) {\n\t\t\tprepareException(lastException, authentication);\n\t\t}\n\n\t\t// Ensure this message is not logged when authentication is attempted by\n\t\t// the parent provider\n\t\tif (this.parent != null) {\n\t\t\tlogger.debug(\"Denying authentication since all attempted providers failed\");\n\t\t}\n\n\t\tthrow lastException;\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate void prepareException(AuthenticationException ex, Authentication auth) {\n\t\tex.setAuthenticationRequest(auth);\n\t\tthis.eventPublisher.publishAuthenticationFailure(ex, auth);\n\t}\n\n\t/**\n\t * Copies the authentication details from a source Authentication object to a\n\t * destination one, provided the latter does not already have one set.\n\t * @param source source authentication\n\t * @param dest the destination authentication object\n\t */\n\tprivate void copyDetails(Authentication source, Authentication dest) {\n\t\tif ((dest instanceof AbstractAuthenticationToken token) && (dest.getDetails() == null)) {\n\t\t\ttoken.setDetails(source.getDetails());\n\t\t}\n\t}\n\n\tpublic List<AuthenticationProvider> getProviders() {\n\t\treturn this.providers;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\tpublic void setAuthenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {\n\t\tAssert.notNull(eventPublisher, \"AuthenticationEventPublisher cannot be null\");\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\t/**\n\t * If set to, a resulting {@code Authentication} which implements the\n\t * {@code CredentialsContainer} interface will have its\n\t * {@link CredentialsContainer#eraseCredentials() eraseCredentials} method called\n\t * before it is returned from the {@code authenticate()} method.\n\t * @param eraseSecretData set to {@literal false} to retain the credentials data in\n\t * memory. Defaults to {@literal true}.\n\t */\n\tpublic void setEraseCredentialsAfterAuthentication(boolean eraseSecretData) {\n\t\tthis.eraseCredentialsAfterAuthentication = eraseSecretData;\n\t}\n\n\tpublic boolean isEraseCredentialsAfterAuthentication() {\n\t\treturn this.eraseCredentialsAfterAuthentication;\n\t}\n\n\tprivate static final class NullEventPublisher implements AuthenticationEventPublisher {\n\n\t\t@Override\n\t\tpublic void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void publishAuthenticationSuccess(Authentication authentication) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ProviderNotFoundException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Thrown by {@link ProviderManager} if no {@link AuthenticationProvider} could be found\n * that supports the presented {@link org.springframework.security.core.Authentication}\n * object.\n *\n * @author Ben Alex\n */\npublic class ProviderNotFoundException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 8107665253214447614L;\n\n\t/**\n\t * Constructs a <code>ProviderNotFoundException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic ProviderNotFoundException(String msg) {\n\t\tsuper(msg);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Determines if the provided {@link Authentication} can be authenticated.\n *\n * @author Rob Winch\n * @since 5.0\n */\n@FunctionalInterface\npublic interface ReactiveAuthenticationManager {\n\n\t/**\n\t * Attempts to authenticate the provided {@link Authentication}\n\t * @param authentication the {@link Authentication} to test\n\t * @return if authentication is successful an {@link Authentication} is returned. If\n\t * authentication cannot be determined, an empty Mono is returned. If authentication\n\t * fails, a Mono error is returned.\n\t */\n\tMono<Authentication> authenticate(Authentication authentication);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ReactiveAuthenticationManagerAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Scheduler;\nimport reactor.core.scheduler.Schedulers;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Adapts an AuthenticationManager to the reactive APIs. This is somewhat necessary\n * because many of the ways that credentials are stored (i.e. JDBC, LDAP, etc) do not have\n * reactive implementations. What's more is it is generally considered best practice to\n * store passwords in a hash that is intentionally slow which would block ever request\n * from coming in unless it was put on another thread.\n *\n * @author Rob Winch\n * @author Tadaya Tsuyukubo\n * @since 5.0\n */\npublic class ReactiveAuthenticationManagerAdapter implements ReactiveAuthenticationManager {\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate Scheduler scheduler = Schedulers.boundedElastic();\n\n\tpublic ReactiveAuthenticationManagerAdapter(AuthenticationManager authenticationManager) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication token) {\n\t\t// @formatter:off\n\t\treturn Mono.just(token)\n\t\t\t\t.publishOn(this.scheduler)\n\t\t\t\t.flatMap(this::doAuthenticate)\n\t\t\t\t.filter(Authentication::isAuthenticated);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Authentication> doAuthenticate(Authentication authentication) {\n\t\ttry {\n\t\t\treturn Mono.just(this.authenticationManager.authenticate(authentication));\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\treturn Mono.error(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Set a scheduler that will be published on to perform the authentication logic.\n\t * @param scheduler a scheduler to be published on\n\t * @throws IllegalArgumentException if the scheduler is {@code null}\n\t */\n\tpublic void setScheduler(Scheduler scheduler) {\n\t\tAssert.notNull(scheduler, \"scheduler cannot be null\");\n\t\tthis.scheduler = scheduler;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ReactiveAuthenticationManagerResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport reactor.core.publisher.Mono;\n\n/**\n * An interface for resolving a {@link ReactiveAuthenticationManager} based on the\n * provided context\n *\n * @author Rafiullah Hamedy\n * @since 5.2\n */\n@FunctionalInterface\npublic interface ReactiveAuthenticationManagerResolver<C> {\n\n\tMono<ReactiveAuthenticationManager> resolve(C context);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation that validates\n * {@link RememberMeAuthenticationToken}s.\n * <p>\n * To be successfully validated, the {@link RememberMeAuthenticationToken#getKeyHash()}\n * must match this class' {@link #getKey()}.\n */\npublic class RememberMeAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate String key;\n\n\tpublic RememberMeAuthenticationProvider(String key) {\n\t\tAssert.hasLength(key, \"key must have a length\");\n\t\tthis.key = key;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.messages, \"A message source must be set\");\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (!supports(authentication.getClass())) {\n\t\t\treturn null;\n\t\t}\n\t\tif (this.key.hashCode() != ((RememberMeAuthenticationToken) authentication).getKeyHash()) {\n\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"RememberMeAuthenticationProvider.incorrectKey\",\n\t\t\t\t\t\"The presented RememberMeAuthenticationToken does not contain the expected key\"));\n\t\t}\n\t\treturn authentication;\n\t}\n\n\tpublic String getKey() {\n\t\treturn this.key;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn (RememberMeAuthenticationToken.class.isAssignableFrom(authentication));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Represents a remembered <code>Authentication</code>.\n * <p>\n * A remembered <code>Authentication</code> must provide a fully valid\n * <code>Authentication</code>, including the <code>GrantedAuthority</code>s that apply.\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic class RememberMeAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final Object principal;\n\n\tprivate final int keyHash;\n\n\t/**\n\t * Constructor.\n\t * @param key to identify if this object made by an authorised client\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param authorities the authorities granted to the principal\n\t * @throws IllegalArgumentException if a <code>null</code> was passed\n\t */\n\tpublic RememberMeAuthenticationToken(String key, Object principal,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tif ((key == null) || (\"\".equals(key)) || (principal == null) || \"\".equals(principal)) {\n\t\t\tthrow new IllegalArgumentException(\"Cannot pass null or empty values to constructor\");\n\t\t}\n\t\tthis.keyHash = key.hashCode();\n\t\tthis.principal = principal;\n\t\tsetAuthenticated(true);\n\t}\n\n\t/**\n\t * Private Constructor to help in Jackson deserialization.\n\t * @param keyHash hashCode of above given key.\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param authorities the authorities granted to the principal\n\t * @since 4.2\n\t */\n\tprivate RememberMeAuthenticationToken(Integer keyHash, Object principal,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tthis.keyHash = keyHash;\n\t\tthis.principal = principal;\n\t\tsetAuthenticated(true);\n\t}\n\n\tprotected RememberMeAuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.keyHash = builder.keyHash;\n\t\tthis.principal = builder.principal;\n\t}\n\n\t/**\n\t * Always returns an empty <code>String</code>\n\t * @return an empty String\n\t */\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\tpublic int getKeyHash() {\n\t\treturn this.keyHash;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (!super.equals(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (obj instanceof RememberMeAuthenticationToken other) {\n\t\t\treturn this.getKeyHash() == other.getKeyHash();\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = super.hashCode();\n\t\tresult = 31 * result + this.keyHash;\n\t\treturn result;\n\t}\n\n\t/**\n\t * A builder of {@link RememberMeAuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate Integer keyHash;\n\n\t\tprivate Object principal;\n\n\t\tprotected Builder(RememberMeAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.keyHash = token.getKeyHash();\n\t\t\tthis.principal = token.getPrincipal();\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Use this key\n\t\t * @param key the key to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic B key(String key) {\n\t\t\tthis.keyHash = key.hashCode();\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic RememberMeAuthenticationToken build() {\n\t\t\treturn new RememberMeAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/TestingAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * An {@link AuthenticationProvider} implementation for the\n * {@link TestingAuthenticationToken}.\n * <p>\n * It simply accepts as valid whatever is contained within the\n * <code>TestingAuthenticationToken</code>.\n * </p>\n * <p>\n * The purpose of this implementation is to facilitate unit testing. This provider should\n * <b>never be enabled on a production system</b>.\n *\n * @author Ben Alex\n */\npublic class TestingAuthenticationProvider implements AuthenticationProvider {\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\treturn authentication;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn TestingAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link org.springframework.security.core.Authentication} implementation that is\n * designed for use whilst unit testing.\n * <p>\n * The corresponding authentication provider is {@link TestingAuthenticationProvider}.\n *\n * @author Ben Alex\n */\npublic class TestingAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final @Nullable Object credentials;\n\n\tprivate final Object principal;\n\n\tpublic TestingAuthenticationToken(Object principal, @Nullable Object credentials) {\n\t\tsuper((Collection<? extends GrantedAuthority>) null);\n\t\tthis.principal = principal;\n\t\tthis.credentials = credentials;\n\t}\n\n\tpublic TestingAuthenticationToken(Object principal, @Nullable Object credentials, String... authorities) {\n\t\tthis(principal, credentials, AuthorityUtils.createAuthorityList(authorities));\n\t}\n\n\tpublic TestingAuthenticationToken(Object principal, @Nullable Object credentials, GrantedAuthority... authorities) {\n\t\tthis(principal, credentials, Arrays.asList(authorities));\n\t}\n\n\tpublic TestingAuthenticationToken(Object principal, @Nullable Object credentials,\n\t\t\tList<? extends GrantedAuthority> authorities) {\n\t\tthis(principal, credentials, (Collection<? extends GrantedAuthority>) authorities);\n\t}\n\n\tpublic TestingAuthenticationToken(Object principal, @Nullable Object credentials,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tthis.principal = principal;\n\t\tthis.credentials = credentials;\n\t\tsetAuthenticated(true);\n\t}\n\n\tprotected TestingAuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.principal = builder.principal;\n\t\tthis.credentials = builder.credentials;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link TestingAuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate Object principal;\n\n\t\tprivate @Nullable Object credentials;\n\n\t\tprotected Builder(TestingAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.principal;\n\t\t\tthis.credentials = token.credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic TestingAuthenticationToken build() {\n\t\t\treturn new TestingAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/UserDetailsRepositoryReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthenticationManager} that uses a {@link ReactiveUserDetailsService}\n * to validate the provided username and password.\n *\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 5.0\n */\npublic class UserDetailsRepositoryReactiveAuthenticationManager\n\t\textends AbstractUserDetailsReactiveAuthenticationManager {\n\n\tprivate ReactiveUserDetailsService userDetailsService;\n\n\tpublic UserDetailsRepositoryReactiveAuthenticationManager(ReactiveUserDetailsService userDetailsService) {\n\t\tAssert.notNull(userDetailsService, \"userDetailsService cannot be null\");\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t@Override\n\tprotected Mono<UserDetails> retrieveUser(String username) {\n\t\treturn this.userDetailsService.findByUsername(username);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link org.springframework.security.core.Authentication} implementation that is\n * designed for simple presentation of a username and password.\n * <p>\n * The <code>principal</code> and <code>credentials</code> should be set with an\n * <code>Object</code> that provides the respective property via its\n * <code>Object.toString()</code> method. The simplest such <code>Object</code> to use is\n * <code>String</code>.\n *\n * @author Ben Alex\n * @author Norbert Nowak\n */\npublic class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final @Nullable Object principal;\n\n\tprivate @Nullable Object credentials;\n\n\t/**\n\t * This constructor can be safely used by any code that wishes to create a\n\t * <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()}\n\t * will return <code>false</code>.\n\t *\n\t */\n\tpublic UsernamePasswordAuthenticationToken(@Nullable Object principal, @Nullable Object credentials) {\n\t\tsuper((Collection<? extends GrantedAuthority>) null);\n\t\tthis.principal = principal;\n\t\tthis.credentials = credentials;\n\t\tsetAuthenticated(false);\n\t}\n\n\t/**\n\t * This constructor should only be used by <code>AuthenticationManager</code> or\n\t * <code>AuthenticationProvider</code> implementations that are satisfied with\n\t * producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)\n\t * authentication token.\n\t * @param principal\n\t * @param credentials\n\t * @param authorities\n\t */\n\tpublic UsernamePasswordAuthenticationToken(Object principal, @Nullable Object credentials,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tthis.principal = principal;\n\t\tthis.credentials = credentials;\n\t\tsuper.setAuthenticated(true); // must use super, as we override\n\t}\n\n\tprotected UsernamePasswordAuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.principal = builder.principal;\n\t\tthis.credentials = builder.credentials;\n\t}\n\n\t/**\n\t * This factory method can be safely used by any code that wishes to create a\n\t * unauthenticated <code>UsernamePasswordAuthenticationToken</code>.\n\t * @param principal\n\t * @param credentials\n\t * @return UsernamePasswordAuthenticationToken with false isAuthenticated() result\n\t *\n\t * @since 5.7\n\t */\n\tpublic static UsernamePasswordAuthenticationToken unauthenticated(@Nullable Object principal,\n\t\t\t@Nullable Object credentials) {\n\t\treturn new UsernamePasswordAuthenticationToken(principal, credentials);\n\t}\n\n\t/**\n\t * This factory method can be safely used by any code that wishes to create a\n\t * authenticated <code>UsernamePasswordAuthenticationToken</code>.\n\t * @param principal\n\t * @param credentials\n\t * @return UsernamePasswordAuthenticationToken with true isAuthenticated() result\n\t *\n\t * @since 5.7\n\t */\n\tpublic static UsernamePasswordAuthenticationToken authenticated(Object principal, @Nullable Object credentials,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\treturn new UsernamePasswordAuthenticationToken(principal, credentials, authorities);\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n\t\tAssert.isTrue(!isAuthenticated,\n\t\t\t\t\"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead\");\n\t\tsuper.setAuthenticated(false);\n\t}\n\n\t@Override\n\tpublic void eraseCredentials() {\n\t\tsuper.eraseCredentials();\n\t\tthis.credentials = null;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link UsernamePasswordAuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate @Nullable Object principal;\n\n\t\tprivate @Nullable Object credentials;\n\n\t\tprotected Builder(UsernamePasswordAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.principal;\n\t\t\tthis.credentials = token.credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic UsernamePasswordAuthenticationToken build() {\n\t\t\treturn new UsernamePasswordAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.dao;\n\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.core.userdetails.cache.NullUserCache;\nimport org.springframework.util.Assert;\n\n/**\n * A base {@link AuthenticationProvider} that allows subclasses to override and work with\n * {@link org.springframework.security.core.userdetails.UserDetails} objects. The class is\n * designed to respond to {@link UsernamePasswordAuthenticationToken} authentication\n * requests.\n *\n * <p>\n * Upon successful validation, a <code>UsernamePasswordAuthenticationToken</code> will be\n * created and returned to the caller. The token will include as its principal either a\n * <code>String</code> representation of the username, or the {@link UserDetails} that was\n * returned from the authentication repository. Using <code>String</code> is appropriate\n * if a container adapter is being used, as it expects <code>String</code> representations\n * of the username. Using <code>UserDetails</code> is appropriate if you require access to\n * additional properties of the authenticated user, such as email addresses,\n * human-friendly names etc. As container adapters are not recommended to be used, and\n * <code>UserDetails</code> implementations provide additional flexibility, by default a\n * <code>UserDetails</code> is returned. To override this default, set the\n * {@link #setForcePrincipalAsString} to <code>true</code>.\n * <p>\n * Caching is handled by storing the <code>UserDetails</code> object being placed in the\n * {@link UserCache}. This ensures that subsequent requests with the same username can be\n * validated without needing to query the {@link UserDetailsService}. It should be noted\n * that if a user appears to present an incorrect password, the {@link UserDetailsService}\n * will be queried to confirm the most up-to-date password was used for comparison.\n * Caching is only likely to be required for stateless applications. In a normal web\n * application, for example, the <tt>SecurityContext</tt> is stored in the user's session\n * and the user isn't reauthenticated on each request. The default cache implementation is\n * therefore {@link NullUserCache}.\n *\n * @author Ben Alex\n */\npublic abstract class AbstractUserDetailsAuthenticationProvider\n\t\timplements AuthenticationProvider, InitializingBean, MessageSourceAware {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate UserCache userCache = new NullUserCache();\n\n\tprivate boolean forcePrincipalAsString = false;\n\n\tprotected boolean hideUserNotFoundExceptions = true;\n\n\tprivate UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks();\n\n\tprivate UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.PASSWORD_AUTHORITY;\n\n\t/**\n\t * Allows subclasses to perform any additional checks of a returned (or cached)\n\t * <code>UserDetails</code> for a given authentication request. Generally a subclass\n\t * will at least compare the {@link Authentication#getCredentials()} with a\n\t * {@link UserDetails#getPassword()}. If custom logic is needed to compare additional\n\t * properties of <code>UserDetails</code> and/or\n\t * <code>UsernamePasswordAuthenticationToken</code>, these should also appear in this\n\t * method.\n\t * @param userDetails as retrieved from the\n\t * {@link #retrieveUser(String, UsernamePasswordAuthenticationToken)} or\n\t * <code>UserCache</code>\n\t * @param authentication the current request that needs to be authenticated\n\t * @throws AuthenticationException AuthenticationException if the credentials could\n\t * not be validated (generally a <code>BadCredentialsException</code>, an\n\t * <code>AuthenticationServiceException</code>)\n\t */\n\tprotected abstract void additionalAuthenticationChecks(UserDetails userDetails,\n\t\t\tUsernamePasswordAuthenticationToken authentication) throws AuthenticationException;\n\n\t@Override\n\tpublic final void afterPropertiesSet() {\n\t\tAssert.notNull(this.userCache, \"A user cache must be set\");\n\t\tAssert.notNull(this.messages, \"A message source must be set\");\n\t\tAssert.notNull(this.preAuthenticationChecks, \"A pre authentication checks must be set\");\n\t\tAssert.notNull(this.postAuthenticationChecks, \"A post authentication checks must be set\");\n\t\tdoAfterPropertiesSet();\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tAssert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,\n\t\t\t\t() -> this.messages.getMessage(\"AbstractUserDetailsAuthenticationProvider.onlySupports\",\n\t\t\t\t\t\t\"Only UsernamePasswordAuthenticationToken is supported\"));\n\t\tString username = determineUsername(authentication);\n\t\tboolean cacheWasUsed = true;\n\t\tUserDetails user = this.userCache.getUserFromCache(username);\n\t\tif (user == null) {\n\t\t\tcacheWasUsed = false;\n\t\t\ttry {\n\t\t\t\tuser = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);\n\t\t\t}\n\t\t\tcatch (UsernameNotFoundException ex) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Failed to find user '%s'\", username));\n\t\t\t\tString message = this.messages.getMessage(\"AbstractUserDetailsAuthenticationProvider.badCredentials\",\n\t\t\t\t\t\t\"Bad credentials\");\n\t\t\t\tif (!this.hideUserNotFoundExceptions) {\n\t\t\t\t\tthrow ex;\n\t\t\t\t}\n\t\t\t\tthrow new BadCredentialsException(message, ex);\n\t\t\t}\n\t\t\tAssert.notNull(user, \"retrieveUser returned null - a violation of the interface contract\");\n\t\t}\n\t\ttry {\n\t\t\tthis.preAuthenticationChecks.check(user);\n\t\t\tadditionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);\n\t\t}\n\t\tcatch (AuthenticationException ex) {\n\t\t\tif (!cacheWasUsed) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\t// There was a problem, so try again after checking\n\t\t\t// we're using latest data (i.e. not from the cache)\n\t\t\tcacheWasUsed = false;\n\t\t\tuser = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);\n\t\t\tthis.preAuthenticationChecks.check(user);\n\t\t\tadditionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);\n\t\t}\n\t\tthis.postAuthenticationChecks.check(user);\n\t\tif (!cacheWasUsed) {\n\t\t\tthis.userCache.putUserInCache(user);\n\t\t}\n\t\tObject principalToReturn = user;\n\t\tif (this.forcePrincipalAsString) {\n\t\t\tprincipalToReturn = user.getUsername();\n\t\t}\n\t\treturn createSuccessAuthentication(principalToReturn, authentication, user);\n\t}\n\n\tprivate String determineUsername(Authentication authentication) {\n\t\treturn (authentication.getPrincipal() == null) ? \"NONE_PROVIDED\" : authentication.getName();\n\t}\n\n\t/**\n\t * Creates a successful {@link Authentication} object.\n\t * <p>\n\t * Protected so subclasses can override.\n\t * </p>\n\t * <p>\n\t * Subclasses will usually store the original credentials the user supplied (not\n\t * salted or encoded passwords) in the returned <code>Authentication</code> object.\n\t * </p>\n\t * @param principal that should be the principal in the returned object (defined by\n\t * the {@link #isForcePrincipalAsString()} method)\n\t * @param authentication that was presented to the provider for validation\n\t * @param user that was loaded by the implementation\n\t * @return the successful authentication token\n\t */\n\tprotected Authentication createSuccessAuthentication(Object principal, Authentication authentication,\n\t\t\tUserDetails user) {\n\t\t// Ensure we return the original credentials the user supplied,\n\t\t// so subsequent attempts are successful even with encoded passwords.\n\t\t// Also ensure we return the original getDetails(), so that future\n\t\t// authentication events after cache expiry contain the details\n\t\tCollection<GrantedAuthority> authorities = new LinkedHashSet<>(\n\t\t\t\tthis.authoritiesMapper.mapAuthorities(user.getAuthorities()));\n\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\tUsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(principal,\n\t\t\t\tauthentication.getCredentials(), authorities);\n\t\tresult.setDetails(authentication.getDetails());\n\t\tthis.logger.debug(\"Authenticated user\");\n\t\treturn result;\n\t}\n\n\tprotected void doAfterPropertiesSet() {\n\t}\n\n\tpublic UserCache getUserCache() {\n\t\treturn this.userCache;\n\t}\n\n\tpublic boolean isForcePrincipalAsString() {\n\t\treturn this.forcePrincipalAsString;\n\t}\n\n\tpublic boolean isHideUserNotFoundExceptions() {\n\t\treturn this.hideUserNotFoundExceptions;\n\t}\n\n\t/**\n\t * Allows subclasses to actually retrieve the <code>UserDetails</code> from an\n\t * implementation-specific location, with the option of throwing an\n\t * <code>AuthenticationException</code> immediately if the presented credentials are\n\t * incorrect (this is especially useful if it is necessary to bind to a resource as\n\t * the user in order to obtain or generate a <code>UserDetails</code>).\n\t * <p>\n\t * Subclasses are not required to perform any caching, as the\n\t * <code>AbstractUserDetailsAuthenticationProvider</code> will by default cache the\n\t * <code>UserDetails</code>. The caching of <code>UserDetails</code> does present\n\t * additional complexity as this means subsequent requests that rely on the cache will\n\t * need to still have their credentials validated, even if the correctness of\n\t * credentials was assured by subclasses adopting a binding-based strategy in this\n\t * method. Accordingly it is important that subclasses either disable caching (if they\n\t * want to ensure that this method is the only method that is capable of\n\t * authenticating a request, as no <code>UserDetails</code> will ever be cached) or\n\t * ensure subclasses implement\n\t * {@link #additionalAuthenticationChecks(UserDetails, UsernamePasswordAuthenticationToken)}\n\t * to compare the credentials of a cached <code>UserDetails</code> with subsequent\n\t * authentication requests.\n\t * </p>\n\t * <p>\n\t * Most of the time subclasses will not perform credentials inspection in this method,\n\t * instead performing it in\n\t * {@link #additionalAuthenticationChecks(UserDetails, UsernamePasswordAuthenticationToken)}\n\t * so that code related to credentials validation need not be duplicated across two\n\t * methods.\n\t * </p>\n\t * @param username The username to retrieve\n\t * @param authentication The authentication request, which subclasses <em>may</em>\n\t * need to perform a binding-based retrieval of the <code>UserDetails</code>\n\t * @return the user information (never <code>null</code> - instead an exception should\n\t * the thrown)\n\t * @throws AuthenticationException if the credentials could not be validated\n\t * (generally a <code>BadCredentialsException</code>, an\n\t * <code>AuthenticationServiceException</code> or\n\t * <code>UsernameNotFoundException</code>)\n\t */\n\tprotected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)\n\t\t\tthrows AuthenticationException;\n\n\tpublic void setForcePrincipalAsString(boolean forcePrincipalAsString) {\n\t\tthis.forcePrincipalAsString = forcePrincipalAsString;\n\t}\n\n\t/**\n\t * By default the <code>AbstractUserDetailsAuthenticationProvider</code> throws a\n\t * <code>BadCredentialsException</code> if a username is not found or the password is\n\t * incorrect. Setting this property to <code>false</code> will cause\n\t * <code>UsernameNotFoundException</code>s to be thrown instead for the former. Note\n\t * this is considered less secure than throwing <code>BadCredentialsException</code>\n\t * for both exceptions.\n\t * @param hideUserNotFoundExceptions set to <code>false</code> if you wish\n\t * <code>UsernameNotFoundException</code>s to be thrown instead of the non-specific\n\t * <code>BadCredentialsException</code> (defaults to <code>true</code>)\n\t */\n\tpublic void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {\n\t\tthis.hideUserNotFoundExceptions = hideUserNotFoundExceptions;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\tpublic void setUserCache(UserCache userCache) {\n\t\tthis.userCache = userCache;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));\n\t}\n\n\tprotected UserDetailsChecker getPreAuthenticationChecks() {\n\t\treturn this.preAuthenticationChecks;\n\t}\n\n\t/**\n\t * Sets the policy will be used to verify the status of the loaded\n\t * <tt>UserDetails</tt> <em>before</em> validation of the credentials takes place.\n\t * @param preAuthenticationChecks strategy to be invoked prior to authentication.\n\t */\n\tpublic void setPreAuthenticationChecks(UserDetailsChecker preAuthenticationChecks) {\n\t\tthis.preAuthenticationChecks = preAuthenticationChecks;\n\t}\n\n\tprotected UserDetailsChecker getPostAuthenticationChecks() {\n\t\treturn this.postAuthenticationChecks;\n\t}\n\n\tpublic void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks) {\n\t\tthis.postAuthenticationChecks = postAuthenticationChecks;\n\t}\n\n\tpublic void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\tprivate class DefaultPreAuthenticationChecks implements UserDetailsChecker {\n\n\t\t@Override\n\t\tpublic void check(UserDetails user) {\n\t\t\tif (!user.isAccountNonLocked()) {\n\t\t\t\tAbstractUserDetailsAuthenticationProvider.this.logger\n\t\t\t\t\t.debug(\"Failed to authenticate since user account is locked\");\n\t\t\t\tthrow new LockedException(AbstractUserDetailsAuthenticationProvider.this.messages\n\t\t\t\t\t.getMessage(\"AbstractUserDetailsAuthenticationProvider.locked\", \"User account is locked\"));\n\t\t\t}\n\t\t\tif (!user.isEnabled()) {\n\t\t\t\tAbstractUserDetailsAuthenticationProvider.this.logger\n\t\t\t\t\t.debug(\"Failed to authenticate since user account is disabled\");\n\t\t\t\tthrow new DisabledException(AbstractUserDetailsAuthenticationProvider.this.messages\n\t\t\t\t\t.getMessage(\"AbstractUserDetailsAuthenticationProvider.disabled\", \"User is disabled\"));\n\t\t\t}\n\t\t\tif (!user.isAccountNonExpired()) {\n\t\t\t\tAbstractUserDetailsAuthenticationProvider.this.logger\n\t\t\t\t\t.debug(\"Failed to authenticate since user account has expired\");\n\t\t\t\tthrow new AccountExpiredException(AbstractUserDetailsAuthenticationProvider.this.messages\n\t\t\t\t\t.getMessage(\"AbstractUserDetailsAuthenticationProvider.expired\", \"User account has expired\"));\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate class DefaultPostAuthenticationChecks implements UserDetailsChecker {\n\n\t\t@Override\n\t\tpublic void check(UserDetails user) {\n\t\t\tif (!user.isCredentialsNonExpired()) {\n\t\t\t\tAbstractUserDetailsAuthenticationProvider.this.logger\n\t\t\t\t\t.debug(\"Failed to authenticate since user account credentials have expired\");\n\t\t\t\tthrow new CredentialsExpiredException(AbstractUserDetailsAuthenticationProvider.this.messages\n\t\t\t\t\t.getMessage(\"AbstractUserDetailsAuthenticationProvider.credentialsExpired\",\n\t\t\t\t\t\t\t\"User credentials have expired\"));\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/dao/DaoAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.dao;\n\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.password.CompromisedPasswordChecker;\nimport org.springframework.security.authentication.password.CompromisedPasswordException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.util.Assert;\nimport org.springframework.util.function.SingletonSupplier;\n\n/**\n * An {@link AuthenticationProvider} implementation that retrieves user details from a\n * {@link UserDetailsService}.\n *\n * @author Ben Alex\n * @author Rob Winch\n * @author Andrey Litvitski\n */\npublic class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {\n\n\t/**\n\t * The plaintext password used to perform\n\t * {@link PasswordEncoder#matches(CharSequence, String)} on when the user is not found\n\t * to avoid SEC-2056.\n\t */\n\tprivate static final String USER_NOT_FOUND_PASSWORD = \"userNotFoundPassword\";\n\n\tprivate Supplier<PasswordEncoder> passwordEncoder = SingletonSupplier\n\t\t.of(PasswordEncoderFactories::createDelegatingPasswordEncoder);\n\n\t/**\n\t * The password used to perform {@link PasswordEncoder#matches(CharSequence, String)}\n\t * on when the user is not found to avoid SEC-2056. This is necessary, because some\n\t * {@link PasswordEncoder} implementations will short circuit if the password is not\n\t * in a valid format.\n\t */\n\tprivate volatile @Nullable String userNotFoundEncodedPassword;\n\n\tprivate final UserDetailsService userDetailsService;\n\n\tprivate UserDetailsPasswordService userDetailsPasswordService = UserDetailsPasswordService.NOOP;\n\n\tprivate @Nullable CompromisedPasswordChecker compromisedPasswordChecker;\n\n\tpublic DaoAuthenticationProvider(UserDetailsService userDetailsService) {\n\t\tAssert.notNull(userDetailsService, \"userDetailsService cannot be null\");\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t@Override\n\tprotected void additionalAuthenticationChecks(UserDetails userDetails,\n\t\t\tUsernamePasswordAuthenticationToken authentication) throws AuthenticationException {\n\t\tif (authentication.getCredentials() == null) {\n\t\t\tthis.logger.debug(\"Failed to authenticate since no credentials provided\");\n\t\t\tthrow new BadCredentialsException(this.messages\n\t\t\t\t.getMessage(\"AbstractUserDetailsAuthenticationProvider.badCredentials\", \"Bad credentials\"));\n\t\t}\n\t\tString presentedPassword = authentication.getCredentials().toString();\n\t\tif (!this.passwordEncoder.get().matches(presentedPassword, userDetails.getPassword())) {\n\t\t\tthis.logger.debug(\"Failed to authenticate since password does not match stored value\");\n\t\t\tthrow new BadCredentialsException(this.messages\n\t\t\t\t.getMessage(\"AbstractUserDetailsAuthenticationProvider.badCredentials\", \"Bad credentials\"));\n\t\t}\n\t}\n\n\t@Override\n\tprotected void doAfterPropertiesSet() {\n\t\tAssert.notNull(this.userDetailsService, \"A UserDetailsService must be set\");\n\t}\n\n\t@Override\n\tprotected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)\n\t\t\tthrows AuthenticationException {\n\t\tprepareTimingAttackProtection();\n\t\ttry {\n\t\t\tUserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);\n\t\t\tif (loadedUser == null) {\n\t\t\t\tthrow new InternalAuthenticationServiceException(\n\t\t\t\t\t\t\"UserDetailsService returned null, which is an interface contract violation\");\n\t\t\t}\n\t\t\treturn loadedUser;\n\t\t}\n\t\tcatch (UsernameNotFoundException ex) {\n\t\t\tmitigateAgainstTimingAttack(authentication);\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (InternalAuthenticationServiceException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new InternalAuthenticationServiceException(ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Authentication createSuccessAuthentication(Object principal, Authentication authentication,\n\t\t\tUserDetails user) {\n\t\tAssert.notNull(authentication.getCredentials(), \"Authentication.getCredentials() cannot be null\");\n\t\tString presentedPassword = authentication.getCredentials().toString();\n\t\tboolean isPasswordCompromised = this.compromisedPasswordChecker != null\n\t\t\t\t&& this.compromisedPasswordChecker.check(presentedPassword).isCompromised();\n\t\tif (isPasswordCompromised) {\n\t\t\tthrow new CompromisedPasswordException(\"The provided password is compromised, please change your password\");\n\t\t}\n\t\tString existingEncodedPassword = user.getPassword();\n\t\tboolean upgradeEncoding = existingEncodedPassword != null\n\t\t\t\t&& !Objects.equals(this.userDetailsPasswordService, UserDetailsPasswordService.NOOP)\n\t\t\t\t&& this.passwordEncoder.get().upgradeEncoding(existingEncodedPassword);\n\t\tif (upgradeEncoding) {\n\t\t\tString newPassword = this.passwordEncoder.get().encode(presentedPassword);\n\t\t\tuser = this.userDetailsPasswordService.updatePassword(user, newPassword);\n\t\t}\n\t\treturn super.createSuccessAuthentication(principal, authentication, user);\n\t}\n\n\tprivate void prepareTimingAttackProtection() {\n\t\tif (this.userNotFoundEncodedPassword == null) {\n\t\t\tthis.userNotFoundEncodedPassword = this.passwordEncoder.get().encode(USER_NOT_FOUND_PASSWORD);\n\t\t}\n\t}\n\n\tprivate void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {\n\t\tif (authentication.getCredentials() != null) {\n\t\t\tAssert.notNull(this.userNotFoundEncodedPassword, \"userNotFoundEncodedPassword cannot be null\");\n\t\t\tString presentedPassword = authentication.getCredentials().toString();\n\t\t\tthis.passwordEncoder.get().matches(presentedPassword, this.userNotFoundEncodedPassword);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the PasswordEncoder instance to be used to encode and validate passwords. If\n\t * not set, the password will be compared using\n\t * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}\n\t * @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}\n\t * types.\n\t */\n\tpublic void setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder cannot be null\");\n\t\tthis.passwordEncoder = () -> passwordEncoder;\n\t\tthis.userNotFoundEncodedPassword = null;\n\t}\n\n\tprotected PasswordEncoder getPasswordEncoder() {\n\t\treturn this.passwordEncoder.get();\n\t}\n\n\tprotected UserDetailsService getUserDetailsService() {\n\t\treturn this.userDetailsService;\n\t}\n\n\tpublic void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {\n\t\tAssert.notNull(userDetailsPasswordService, \"userDetailsPasswordService cannot be null\");\n\t\tthis.userDetailsPasswordService = userDetailsPasswordService;\n\t}\n\n\t/**\n\t * Sets the {@link CompromisedPasswordChecker} to be used before creating a successful\n\t * authentication. Defaults to {@code null}.\n\t * @param compromisedPasswordChecker the {@link CompromisedPasswordChecker} to use\n\t * @since 6.3\n\t */\n\tpublic void setCompromisedPasswordChecker(CompromisedPasswordChecker compromisedPasswordChecker) {\n\t\tAssert.notNull(compromisedPasswordChecker, \"compromisedPasswordChecker cannot be null\");\n\t\tthis.compromisedPasswordChecker = compromisedPasswordChecker;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/dao/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * An {@code AuthenticationProvider} which relies upon a data access object.\n */\n@NullMarked\npackage org.springframework.security.authentication.dao;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AbstractAuthenticationEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Represents an application authentication event.\n * <P>\n * The <code>ApplicationEvent</code>'s <code>source</code> will be the\n * <code>Authentication</code> object.\n * </p>\n *\n * @author Ben Alex\n */\npublic abstract class AbstractAuthenticationEvent extends ApplicationEvent {\n\n\tpublic AbstractAuthenticationEvent(Authentication authentication) {\n\t\tsuper(authentication);\n\t}\n\n\t/**\n\t * Getters for the <code>Authentication</code> request that caused the event. Also\n\t * available from <code>super.getSource()</code>.\n\t * @return the authentication request\n\t */\n\tpublic Authentication getAuthentication() {\n\t\treturn (Authentication) super.getSource();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AbstractAuthenticationFailureEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.util.Assert;\n\n/**\n * Abstract application event which indicates authentication failure for some reason.\n *\n * @author Ben Alex\n */\npublic abstract class AbstractAuthenticationFailureEvent extends AbstractAuthenticationEvent {\n\n\tprivate final AuthenticationException exception;\n\n\tpublic AbstractAuthenticationFailureEvent(Authentication authentication, AuthenticationException exception) {\n\t\tsuper(authentication);\n\t\tAssert.notNull(exception, \"AuthenticationException is required\");\n\t\tthis.exception = exception;\n\t}\n\n\tpublic AuthenticationException getException() {\n\t\treturn this.exception;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationFailureBadCredentialsEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Application event which indicates authentication failure due to invalid credentials\n * being presented.\n *\n * @author Ben Alex\n */\npublic class AuthenticationFailureBadCredentialsEvent extends AbstractAuthenticationFailureEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5245144711561130379L;\n\n\tpublic AuthenticationFailureBadCredentialsEvent(Authentication authentication, AuthenticationException exception) {\n\t\tsuper(authentication, exception);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationFailureCredentialsExpiredEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Application event which indicates authentication failure due to the user's credentials\n * having expired.\n *\n * @author Ben Alex\n */\npublic class AuthenticationFailureCredentialsExpiredEvent extends AbstractAuthenticationFailureEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7595086332769705203L;\n\n\tpublic AuthenticationFailureCredentialsExpiredEvent(Authentication authentication,\n\t\t\tAuthenticationException exception) {\n\t\tsuper(authentication, exception);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationFailureDisabledEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Application event which indicates authentication failure due to the user's account\n * being disabled.\n *\n * @author Ben Alex\n */\npublic class AuthenticationFailureDisabledEvent extends AbstractAuthenticationFailureEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 8037552364666766279L;\n\n\tpublic AuthenticationFailureDisabledEvent(Authentication authentication, AuthenticationException exception) {\n\t\tsuper(authentication, exception);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationFailureExpiredEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Application event which indicates authentication failure due to the user's account\n * having expired.\n *\n * @author Ben Alex\n */\npublic class AuthenticationFailureExpiredEvent extends AbstractAuthenticationFailureEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -8437264795214121718L;\n\n\tpublic AuthenticationFailureExpiredEvent(Authentication authentication, AuthenticationException exception) {\n\t\tsuper(authentication, exception);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationFailureLockedEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Application event which indicates authentication failure due to the user's account\n * having been locked.\n *\n * @author Ben Alex\n */\npublic class AuthenticationFailureLockedEvent extends AbstractAuthenticationFailureEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5126110096093568463L;\n\n\tpublic AuthenticationFailureLockedEvent(Authentication authentication, AuthenticationException exception) {\n\t\tsuper(authentication, exception);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationFailureProviderNotFoundEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Application event which indicates authentication failure due to there being no\n * registered <code>AuthenticationProvider</code> that can process the request.\n *\n * @author Ben Alex\n */\npublic class AuthenticationFailureProviderNotFoundEvent extends AbstractAuthenticationFailureEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 9122219669183263487L;\n\n\tpublic AuthenticationFailureProviderNotFoundEvent(Authentication authentication,\n\t\t\tAuthenticationException exception) {\n\t\tsuper(authentication, exception);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationFailureProxyUntrustedEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Application event which indicates authentication failure due to the CAS user's ticket\n * being generated by an untrusted proxy.\n *\n * @author Ben Alex\n */\npublic class AuthenticationFailureProxyUntrustedEvent extends AbstractAuthenticationFailureEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1801476426012753252L;\n\n\tpublic AuthenticationFailureProxyUntrustedEvent(Authentication authentication, AuthenticationException exception) {\n\t\tsuper(authentication, exception);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationFailureServiceExceptionEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Application event which indicates authentication failure due to there being a problem\n * internal to the <code>AuthenticationManager</code>.\n *\n * @author Ben Alex\n */\npublic class AuthenticationFailureServiceExceptionEvent extends AbstractAuthenticationFailureEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 5580062757249390756L;\n\n\tpublic AuthenticationFailureServiceExceptionEvent(Authentication authentication,\n\t\t\tAuthenticationException exception) {\n\t\tsuper(authentication, exception);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/AuthenticationSuccessEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Application event which indicates successful authentication.\n *\n * @author Ben Alex\n */\npublic class AuthenticationSuccessEvent extends AbstractAuthenticationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2537206344128673963L;\n\n\tpublic AuthenticationSuccessEvent(Authentication authentication) {\n\t\tsuper(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/InteractiveAuthenticationSuccessEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Indicates an interactive authentication was successful.\n * <P>\n * The <code>ApplicationEvent</code>'s <code>source</code> will be the\n * <code>Authentication</code> object.\n * </p>\n * <p>\n * This does not extend from <code>AuthenticationSuccessEvent</code> to avoid duplicate\n * <code>AuthenticationSuccessEvent</code>s being sent to any listeners.\n * </p>\n *\n * @author Ben Alex\n */\npublic class InteractiveAuthenticationSuccessEvent extends AbstractAuthenticationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1990271553478571709L;\n\n\tprivate final Class<?> generatedBy;\n\n\tpublic InteractiveAuthenticationSuccessEvent(Authentication authentication, Class<?> generatedBy) {\n\t\tsuper(authentication);\n\t\tAssert.notNull(generatedBy, \"generatedBy cannot be null\");\n\t\tthis.generatedBy = generatedBy;\n\t}\n\n\t/**\n\t * Getter for the <code>Class</code> that generated this event. This can be useful for\n\t * generating additional logging information.\n\t * @return the class\n\t */\n\tpublic Class<?> getGeneratedBy() {\n\t\treturn this.generatedBy;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/LoggerListener.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Outputs authentication-related application events to Commons Logging.\n * <p>\n * All authentication events are logged at the warning level.\n *\n * @author Ben Alex\n */\npublic class LoggerListener implements ApplicationListener<AbstractAuthenticationEvent> {\n\n\tprivate static final Log logger = LogFactory.getLog(LoggerListener.class);\n\n\t/**\n\t * If set to true, {@link InteractiveAuthenticationSuccessEvent} will be logged\n\t * (defaults to true)\n\t */\n\tprivate boolean logInteractiveAuthenticationSuccessEvents = true;\n\n\t@Override\n\tpublic void onApplicationEvent(AbstractAuthenticationEvent event) {\n\t\tif (!this.logInteractiveAuthenticationSuccessEvents && event instanceof InteractiveAuthenticationSuccessEvent) {\n\t\t\treturn;\n\t\t}\n\t\tlogger.warn(LogMessage.of(() -> getLogMessage(event)));\n\t}\n\n\tprivate String getLogMessage(AbstractAuthenticationEvent event) {\n\t\tStringBuilder builder = new StringBuilder();\n\t\tbuilder.append(\"Authentication event \");\n\t\tbuilder.append(ClassUtils.getShortName(event.getClass()));\n\t\tbuilder.append(\": \");\n\t\tbuilder.append(event.getAuthentication().getName());\n\t\tbuilder.append(\"; details: \");\n\t\tbuilder.append(event.getAuthentication().getDetails());\n\t\tif (event instanceof AbstractAuthenticationFailureEvent) {\n\t\t\tbuilder.append(\"; exception: \");\n\t\t\tbuilder.append(((AbstractAuthenticationFailureEvent) event).getException().getMessage());\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n\tpublic boolean isLogInteractiveAuthenticationSuccessEvents() {\n\t\treturn this.logInteractiveAuthenticationSuccessEvents;\n\t}\n\n\tpublic void setLogInteractiveAuthenticationSuccessEvents(boolean logInteractiveAuthenticationSuccessEvents) {\n\t\tthis.logInteractiveAuthenticationSuccessEvents = logInteractiveAuthenticationSuccessEvents;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/LogoutSuccessEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Application event which indicates successful logout\n *\n * @author Onur Kagan Ozcan\n * @since 5.2.0\n */\npublic class LogoutSuccessEvent extends AbstractAuthenticationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 5112491795571632311L;\n\n\tpublic LogoutSuccessEvent(Authentication authentication) {\n\t\tsuper(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/event/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Authentication success and failure events which can be published to the Spring\n * application context.\n *\n * The <code>ProviderManager</code> automatically publishes events to the application\n * context. These events are received by all registered Spring\n * <code>ApplicationListener</code>s.\n */\n@NullMarked\npackage org.springframework.security.authentication.event;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/AbstractJaasAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.io.IOException;\nimport java.security.Principal;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.auth.login.LoginContext;\nimport javax.security.auth.login.LoginException;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationFailedEvent;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationSuccessEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.session.SessionDestroyedEvent;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation that retrieves user details from a\n * JAAS login configuration.\n *\n * <p>\n * This <code>AuthenticationProvider</code> is capable of validating\n * {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}\n * requests contain the correct username and password.\n *\n * <p>\n * This implementation is backed by a\n * <a href=\"https://java.sun.com/j2se/1.5.0/docs/guide/security/jaas/JAASRefGuide.html\" >\n * JAAS</a> configuration that is provided by a subclass's implementation of\n * {@link #createLoginContext(CallbackHandler)}.\n *\n * <p>\n * When using JAAS login modules as the authentication source, sometimes the <a href=\n * \"https://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/login/LoginContext.html\"\n * > LoginContext</a> will require <i>CallbackHandler</i>s. The\n * AbstractJaasAuthenticationProvider uses an internal <a href=\n * \"https://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/callback/CallbackHandler.html\"\n * >CallbackHandler </a> to wrap the {@link JaasAuthenticationCallbackHandler}s configured\n * in the ApplicationContext. When the LoginContext calls the internal CallbackHandler,\n * control is passed to each {@link JaasAuthenticationCallbackHandler} for each Callback\n * passed.\n *\n * <p>\n * {@link JaasAuthenticationCallbackHandler}s are passed to the\n * AbstractJaasAuthenticationProvider through the\n * {@link #setCallbackHandlers(org.springframework.security.authentication.jaas.JaasAuthenticationCallbackHandler[])\n * callbackHandlers} property.\n *\n * <pre>\n * &lt;property name=\"callbackHandlers\"&gt;\n *   &lt;list&gt;\n *     &lt;bean class=\"org.springframework.security.authentication.jaas.TestCallbackHandler\"/&gt;\n *     &lt;bean class=\"{@link JaasNameCallbackHandler org.springframework.security.authentication.jaas.JaasNameCallbackHandler}\"/&gt;\n *     &lt;bean class=\"{@link JaasPasswordCallbackHandler org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler}\"/&gt;\n *  &lt;/list&gt;\n * &lt;/property&gt;\n * </pre>\n *\n * <p>\n * After calling LoginContext.login(), the AbstractJaasAuthenticationProvider will\n * retrieve the returned Principals from the Subject\n * (LoginContext.getSubject().getPrincipals). Each returned principal is then passed to\n * the configured {@link AuthorityGranter}s. An AuthorityGranter is a mapping between a\n * returned Principal, and a role name. If an AuthorityGranter wishes to grant an\n * Authorization a role, it returns that role name from it's\n * {@link AuthorityGranter#grant(java.security.Principal)} method. The returned role will\n * be applied to the Authorization object as a {@link GrantedAuthority}.\n *\n * <p>\n * AuthorityGranters are configured in spring xml as follows...\n *\n * <pre>\n * &lt;property name=\"authorityGranters\"&gt;\n *   &lt;list&gt;\n *     &lt;bean class=\"org.springframework.security.authentication.jaas.TestAuthorityGranter\"/&gt;\n *   &lt;/list&gt;\n *  &lt;/property&gt;\n * </pre>\n *\n * @author Ray Krueger\n * @author Rob Winch\n */\npublic abstract class AbstractJaasAuthenticationProvider implements AuthenticationProvider,\n\t\tApplicationEventPublisherAware, InitializingBean, ApplicationListener<SessionDestroyedEvent> {\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.PASSWORD_AUTHORITY;\n\n\tprivate ApplicationEventPublisher applicationEventPublisher = (event) -> {\n\t};\n\n\tprivate AuthorityGranter[] authorityGranters = new AuthorityGranter[0];\n\n\tprivate JaasAuthenticationCallbackHandler[] callbackHandlers = new JaasAuthenticationCallbackHandler[0];\n\n\tprotected final Log log = LogFactory.getLog(getClass());\n\n\tprivate LoginExceptionResolver loginExceptionResolver = new DefaultLoginExceptionResolver();\n\n\tprivate String loginContextName = \"SPRINGSECURITY\";\n\n\t/**\n\t * Validates the required properties are set. In addition, if\n\t * {@link #setCallbackHandlers(JaasAuthenticationCallbackHandler[])} has not been\n\t * called with valid handlers, initializes to use {@link JaasNameCallbackHandler} and\n\t * {@link JaasPasswordCallbackHandler}.\n\t */\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tAssert.hasLength(this.loginContextName, \"loginContextName cannot be null or empty\");\n\t\tAssert.notEmpty(this.authorityGranters, \"authorityGranters cannot be null or empty\");\n\t\tif (ObjectUtils.isEmpty(this.callbackHandlers)) {\n\t\t\tsetCallbackHandlers(new JaasAuthenticationCallbackHandler[] { new JaasNameCallbackHandler(),\n\t\t\t\t\tnew JaasPasswordCallbackHandler() });\n\t\t}\n\t\tAssert.notNull(this.loginExceptionResolver, \"loginExceptionResolver cannot be null\");\n\t}\n\n\t/**\n\t * Attempts to login the user given the Authentication objects principal and\n\t * credential\n\t * @param auth The Authentication object to be authenticated.\n\t * @return The authenticated Authentication object, with it's grantedAuthorities set.\n\t * @throws AuthenticationException This implementation does not handle 'locked' or\n\t * 'disabled' accounts. This method only throws a AuthenticationServiceException, with\n\t * the message of the LoginException that will be thrown, should the\n\t * loginContext.login() method fail.\n\t */\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication auth) throws AuthenticationException {\n\t\tif (!(auth instanceof UsernamePasswordAuthenticationToken request)) {\n\t\t\treturn null;\n\t\t}\n\t\tSet<GrantedAuthority> authorities;\n\t\ttry {\n\t\t\t// Create the LoginContext object, and pass our InternalCallbackHandler\n\t\t\tLoginContext loginContext = createLoginContext(new InternalCallbackHandler(auth));\n\t\t\t// Attempt to login the user, the LoginContext will call our\n\t\t\t// InternalCallbackHandler at this point.\n\t\t\tloginContext.login();\n\t\t\t// Get the subject principals and pass them to each of the AuthorityGranters\n\t\t\tSet<Principal> principals = loginContext.getSubject().getPrincipals();\n\t\t\t// Create a set to hold the authorities, and add any that have already been\n\t\t\t// applied.\n\t\t\tauthorities = getAuthorities(principals);\n\t\t\t// Convert the authorities set back to an array and apply it to the token.\n\t\t\tObject principal = request.getPrincipal();\n\t\t\tAssert.notNull(principal, \"The principal cannot be null\");\n\t\t\tJaasAuthenticationToken result = new JaasAuthenticationToken(principal, request.getCredentials(),\n\t\t\t\t\tnew ArrayList<>(authorities), loginContext);\n\t\t\t// Publish the success event\n\t\t\tpublishSuccessEvent(result);\n\t\t\t// we're done, return the token.\n\t\t\treturn result;\n\n\t\t}\n\t\tcatch (LoginException ex) {\n\t\t\tAuthenticationException resolvedException = this.loginExceptionResolver.resolveException(ex);\n\t\t\tpublishFailureEvent(request, resolvedException);\n\t\t\tthrow resolvedException;\n\t\t}\n\t}\n\n\tprivate Set<GrantedAuthority> getAuthorities(Set<Principal> principals) {\n\t\tSet<GrantedAuthority> authorities;\n\t\tauthorities = new HashSet<>();\n\t\tfor (Principal principal : principals) {\n\t\t\tfor (AuthorityGranter granter : this.authorityGranters) {\n\t\t\t\tSet<String> roles = granter.grant(principal);\n\t\t\t\t// If the granter doesn't wish to grant any authorities,\n\t\t\t\t// it should return null.\n\t\t\t\tif (!CollectionUtils.isEmpty(roles)) {\n\t\t\t\t\tfor (String role : roles) {\n\t\t\t\t\t\tauthorities.add(new JaasGrantedAuthority(role, principal));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\treturn authorities;\n\t}\n\n\t/**\n\t * Creates the LoginContext to be used for authentication.\n\t * @param handler The CallbackHandler that should be used for the LoginContext (never\n\t * <code>null</code>).\n\t * @return the LoginContext to use for authentication.\n\t * @throws LoginException\n\t */\n\tprotected abstract LoginContext createLoginContext(CallbackHandler handler) throws LoginException;\n\n\t/**\n\t * Handles the logout by getting the security contexts for the destroyed session and\n\t * invoking {@code LoginContext.logout()} for any which contain a\n\t * {@code JaasAuthenticationToken}.\n\t * @param event the session event which contains the current session\n\t */\n\tprotected void handleLogout(SessionDestroyedEvent event) {\n\t\tList<SecurityContext> contexts = event.getSecurityContexts();\n\t\tif (contexts.isEmpty()) {\n\t\t\tthis.log.debug(\"The destroyed session has no SecurityContexts\");\n\t\t\treturn;\n\t\t}\n\t\tfor (SecurityContext context : contexts) {\n\t\t\tAuthentication auth = context.getAuthentication();\n\t\t\tif ((auth instanceof JaasAuthenticationToken token)) {\n\t\t\t\ttry {\n\t\t\t\t\tLoginContext loginContext = token.getLoginContext();\n\t\t\t\t\tlogout(token, loginContext);\n\t\t\t\t}\n\t\t\t\tcatch (LoginException ex) {\n\t\t\t\t\tthis.log.warn(\"Error error logging out of LoginContext\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void logout(JaasAuthenticationToken token, LoginContext loginContext) throws LoginException {\n\t\tif (loginContext != null) {\n\t\t\tthis.log\n\t\t\t\t.debug(LogMessage.of(() -> \"Logging principal: [\" + token.getPrincipal() + \"] out of LoginContext\"));\n\t\t\tloginContext.logout();\n\t\t\treturn;\n\t\t}\n\t\tthis.log.debug(LogMessage.of(() -> \"Cannot logout principal: [\" + token.getPrincipal()\n\t\t\t\t+ \"] from LoginContext. The LoginContext is unavailable\"));\n\t}\n\n\t@Override\n\tpublic void onApplicationEvent(SessionDestroyedEvent event) {\n\t\thandleLogout(event);\n\t}\n\n\t/**\n\t * Publishes the {@link JaasAuthenticationFailedEvent}. Can be overridden by\n\t * subclasses for different functionality\n\t * @param token The authentication token being processed\n\t * @param ase The exception that caused the authentication failure\n\t */\n\tprotected void publishFailureEvent(UsernamePasswordAuthenticationToken token, AuthenticationException ase) {\n\t\tif (this.applicationEventPublisher != null) {\n\t\t\tthis.applicationEventPublisher.publishEvent(new JaasAuthenticationFailedEvent(token, ase));\n\t\t}\n\t}\n\n\t/**\n\t * Publishes the {@link JaasAuthenticationSuccessEvent}. Can be overridden by\n\t * subclasses for different functionality.\n\t * @param token The token being processed\n\t */\n\tprotected void publishSuccessEvent(UsernamePasswordAuthenticationToken token) {\n\t\tif (this.applicationEventPublisher != null) {\n\t\t\tthis.applicationEventPublisher.publishEvent(new JaasAuthenticationSuccessEvent(token));\n\t\t}\n\t}\n\n\t/**\n\t * Returns the AuthorityGranter array that was passed to the\n\t * {@link #setAuthorityGranters(AuthorityGranter[])} method, or null if it none were\n\t * ever set.\n\t * @return The AuthorityGranter array, or null\n\t *\n\t * @see #setAuthorityGranters(AuthorityGranter[])\n\t */\n\tAuthorityGranter[] getAuthorityGranters() {\n\t\treturn this.authorityGranters;\n\t}\n\n\t/**\n\t * Set the AuthorityGranters that should be consulted for role names to be granted to\n\t * the Authentication.\n\t * @param authorityGranters AuthorityGranter array\n\t *\n\t * @see JaasAuthenticationProvider\n\t */\n\tpublic void setAuthorityGranters(AuthorityGranter[] authorityGranters) {\n\t\tAssert.notNull(authorityGranters, \"authorityGranters cannot be null\");\n\t\tthis.authorityGranters = authorityGranters;\n\t}\n\n\t/**\n\t * Returns the current JaasAuthenticationCallbackHandler array, or null if none are\n\t * set.\n\t * @return the JAASAuthenticationCallbackHandlers.\n\t *\n\t * @see #setCallbackHandlers(JaasAuthenticationCallbackHandler[])\n\t */\n\tJaasAuthenticationCallbackHandler[] getCallbackHandlers() {\n\t\treturn this.callbackHandlers;\n\t}\n\n\t/**\n\t * Set the JAASAuthenticationCallbackHandler array to handle callback objects\n\t * generated by the LoginContext.login method.\n\t * @param callbackHandlers Array of JAASAuthenticationCallbackHandlers\n\t */\n\tpublic void setCallbackHandlers(JaasAuthenticationCallbackHandler[] callbackHandlers) {\n\t\tAssert.notNull(callbackHandlers, \"callbackHandlers cannot be null\");\n\t\tthis.callbackHandlers = callbackHandlers;\n\t}\n\n\tString getLoginContextName() {\n\t\treturn this.loginContextName;\n\t}\n\n\t/**\n\t * Set the loginContextName, this name is used as the index to the configuration\n\t * specified in the loginConfig property.\n\t * @param loginContextName\n\t */\n\tpublic void setLoginContextName(String loginContextName) {\n\t\tthis.loginContextName = loginContextName;\n\t}\n\n\tLoginExceptionResolver getLoginExceptionResolver() {\n\t\treturn this.loginExceptionResolver;\n\t}\n\n\tpublic void setLoginExceptionResolver(LoginExceptionResolver loginExceptionResolver) {\n\t\tthis.loginExceptionResolver = loginExceptionResolver;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> aClass) {\n\t\treturn UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tAssert.notNull(applicationEventPublisher, \"applicationEventPublisher cannot be null\");\n\t\tthis.applicationEventPublisher = applicationEventPublisher;\n\t}\n\n\tprotected ApplicationEventPublisher getApplicationEventPublisher() {\n\t\treturn this.applicationEventPublisher;\n\t}\n\n\t/**\n\t * Wrapper class for JAASAuthenticationCallbackHandlers\n\t */\n\tprivate class InternalCallbackHandler implements CallbackHandler {\n\n\t\tprivate final Authentication authentication;\n\n\t\tInternalCallbackHandler(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\tfor (JaasAuthenticationCallbackHandler handler : AbstractJaasAuthenticationProvider.this.callbackHandlers) {\n\t\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\t\thandler.handle(callback, this.authentication);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/AuthorityGranter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.security.Principal;\nimport java.util.Set;\n\n/**\n * The AuthorityGranter interface is used to map a given principal to role names.\n * <p>\n * If a Windows NT login module were to be used from JAAS, an AuthorityGranter\n * implementation could be created to map a NT Group Principal to a ROLE_USER role for\n * instance.\n *\n * @author Ray Krueger\n */\npublic interface AuthorityGranter {\n\n\t/**\n\t * The grant method is called for each principal returned from the LoginContext\n\t * subject. If the AuthorityGranter wishes to grant any authorities, it should return\n\t * a java.util.Set containing the role names it wishes to grant, such as ROLE_USER. If\n\t * the AuthorityGranter does not wish to grant any authorities it should return null.\n\t * <p>\n\t * The set may contain any object as all objects in the returned set will be passed to\n\t * the JaasGrantedAuthority constructor using toString().\n\t * @param principal One of the principals from the\n\t * LoginContext.getSubject().getPrincipals() method.\n\t * @return the role names to grant, or null, meaning no roles should be granted to the\n\t * principal.\n\t */\n\tSet<String> grant(Principal principal);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\nimport javax.security.auth.login.LoginException;\n\nimport org.springframework.security.authentication.jaas.memory.InMemoryConfiguration;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Creates a LoginContext using the Configuration provided to it. This allows the\n * configuration to be injected regardless of the value of\n * {@link Configuration#getConfiguration()}.\n * </p>\n * <p>\n * While not bound to any particular Configuration implementation, an in memory version of\n * a JAAS configuration can be represented using {@link InMemoryConfiguration}.\n * </p>\n * <p>\n * The following JAAS configuration:\n * </p>\n *\n * <pre>\n * SPRINGSECURITY {\n *    sample.SampleLoginModule required;\n *  };\n * </pre>\n *\n * <p>\n * Can be represented as follows:\n * </p>\n *\n * <pre>\n * &lt;bean id=&quot;jaasAuthProvider&quot; class=&quot;org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider&quot;&gt;\n *   &lt;property name=&quot;configuration&quot;&gt;\n *     &lt;bean class=&quot;org.springframework.security.authentication.jaas.memory.InMemoryConfiguration&quot;&gt;\n *       &lt;constructor-arg&gt;\n *         &lt;map&gt;\n *           &lt;!-- SPRINGSECURITY is the default loginContextName for AbstractJaasAuthenticationProvider--&gt;\n *           &lt;entry key=&quot;SPRINGSECURITY&quot;&gt;\n *             &lt;array&gt;\n *               &lt;bean class=&quot;javax.security.auth.login.AppConfigurationEntry&quot;&gt;\n *                 &lt;constructor-arg value=&quot;sample.SampleLoginModule&quot; /&gt;\n *                 &lt;constructor-arg&gt;\n *                   &lt;util:constant static-field=&quot;javax.security.auth.login.AppConfigurationEntry$LoginModuleControlFlag.REQUIRED&quot; /&gt;\n *                 &lt;/constructor-arg&gt;\n *                 &lt;constructor-arg&gt;\n *                   &lt;map&gt;&lt;/map&gt;\n *                 &lt;/constructor-arg&gt;\n *               &lt;/bean&gt;\n *             &lt;/array&gt;\n *           &lt;/entry&gt;\n *         &lt;/map&gt;\n *       &lt;/constructor-arg&gt;\n *     &lt;/bean&gt;\n *   &lt;/property&gt;\n *   &lt;property name=&quot;authorityGranters&quot;&gt;\n *     &lt;list&gt;\n *       &lt;!-- You will need to write your own implementation of AuthorityGranter --&gt;\n *       &lt;bean class=&quot;org.springframework.security.authentication.jaas.TestAuthorityGranter&quot;/&gt;\n *     &lt;/list&gt;\n *   &lt;/property&gt;\n * &lt;/bean&gt;\n * </pre>\n *\n * @author Rob Winch\n * @see AbstractJaasAuthenticationProvider\n * @see InMemoryConfiguration\n */\npublic class DefaultJaasAuthenticationProvider extends AbstractJaasAuthenticationProvider {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate Configuration configuration;\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tsuper.afterPropertiesSet();\n\t\tAssert.notNull(this.configuration, \"configuration cannot be null.\");\n\t}\n\n\t/**\n\t * Creates a LoginContext using the Configuration that was specified in\n\t * {@link #setConfiguration(Configuration)}.\n\t */\n\t@Override\n\tprotected LoginContext createLoginContext(CallbackHandler handler) throws LoginException {\n\t\treturn new LoginContext(getLoginContextName(), null, handler, getConfiguration());\n\t}\n\n\tprotected Configuration getConfiguration() {\n\t\treturn this.configuration;\n\t}\n\n\t/**\n\t * Sets the Configuration to use for Authentication.\n\t * @param configuration the Configuration that is used when\n\t * {@link #createLoginContext(CallbackHandler)} is called.\n\t */\n\tpublic void setConfiguration(Configuration configuration) {\n\t\tthis.configuration = configuration;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/DefaultLoginExceptionResolver.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport javax.security.auth.login.LoginException;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * This LoginExceptionResolver simply wraps the LoginException with an\n * AuthenticationServiceException.\n *\n * @author Ray Krueger\n */\npublic class DefaultLoginExceptionResolver implements LoginExceptionResolver {\n\n\t@Override\n\tpublic AuthenticationException resolveException(LoginException ex) {\n\t\treturn new AuthenticationServiceException(ex.getMessage(), ex);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationCallbackHandler.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.io.IOException;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * The JaasAuthenticationCallbackHandler is similar to the\n * javax.security.auth.callback.CallbackHandler interface in that it defines a handle\n * method. The JaasAuthenticationCallbackHandler is only asked to handle one Callback\n * instance at time rather than an array of all Callbacks, as the javax... CallbackHandler\n * defines.\n *\n * <p>\n * Before a JaasAuthenticationCallbackHandler is asked to 'handle' any callbacks, it is\n * first passed the Authentication object that the login attempt is for. NOTE: The\n * Authentication object has not been 'authenticated' yet.\n * </p>\n *\n * @author Ray Krueger\n * @see JaasNameCallbackHandler\n * @see JaasPasswordCallbackHandler\n * @see <a href=\n * \"https://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/Callback.html\">Callback</a>\n * @see <a href=\n * \"https://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/CallbackHandler.html\">\n * CallbackHandler</a>\n */\npublic interface JaasAuthenticationCallbackHandler {\n\n\t/**\n\t * Handle the <a href=\n\t * \"https://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/Callback.html\"\n\t * >Callback</a>. The handle method will be called for every callback instance sent\n\t * from the LoginContext. Meaning that The handle method may be called multiple times\n\t * for a given JaasAuthenticationCallbackHandler.\n\t * @param callback\n\t * @param auth The Authentication object currently being authenticated.\n\t *\n\t */\n\tvoid handle(Callback callback, Authentication auth) throws IOException, UnsupportedCallbackException;\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.security.Security;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\nimport javax.security.auth.login.LoginException;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationFailedEvent;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation that retrieves user details from a\n * JAAS login configuration.\n *\n * <p>\n * This <code>AuthenticationProvider</code> is capable of validating\n * {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}\n * requests contain the correct username and password.\n * </p>\n * <p>\n * This implementation is backed by a\n * <a href=\"https://java.sun.com/j2se/1.5.0/docs/guide/security/jaas/JAASRefGuide.html\" >\n * JAAS</a> configuration. The loginConfig property must be set to a given JAAS\n * configuration file. This setter accepts a Spring\n * {@link org.springframework.core.io.Resource} instance. It should point to a JAAS\n * configuration file containing an index matching the\n * {@link #setLoginContextName(java.lang.String) loginContextName} property.\n * </p>\n * <p>\n * For example: If this JaasAuthenticationProvider were configured in a Spring\n * WebApplicationContext the xml to set the loginConfiguration could be as follows...\n *\n * <pre>\n * &lt;property name=\"loginConfig\"&gt;\n *   &lt;value&gt;/WEB-INF/login.conf&lt;/value&gt;\n * &lt;/property&gt;\n * </pre>\n *\n * <p>\n * The loginContextName should coincide with a given index in the loginConfig specified.\n * The loginConfig file used in the JUnit tests appears as the following...\n *\n * <pre>\n * JAASTest {\n *   org.springframework.security.authentication.jaas.TestLoginModule required;\n * };\n * </pre>\n *\n * Using the example login configuration above, the loginContextName property would be set\n * as <i>JAASTest</i>...\n *\n * <pre>\n *  &lt;property name=\"loginContextName\"&gt; &lt;value&gt;JAASTest&lt;/value&gt; &lt;/property&gt;\n * </pre>\n *\n * <p>\n * When using JAAS login modules as the authentication source, sometimes the <a href=\n * \"https://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/login/LoginContext.html\"\n * > LoginContext</a> will require <i>CallbackHandler</i>s. The JaasAuthenticationProvider\n * uses an internal <a href=\n * \"https://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/callback/CallbackHandler.html\"\n * >CallbackHandler </a> to wrap the {@link JaasAuthenticationCallbackHandler}s configured\n * in the ApplicationContext. When the LoginContext calls the internal CallbackHandler,\n * control is passed to each {@link JaasAuthenticationCallbackHandler} for each Callback\n * passed.\n *\n * <p>\n * {@link JaasAuthenticationCallbackHandler}s are passed to the JaasAuthenticationProvider\n * through the\n * {@link #setCallbackHandlers(org.springframework.security.authentication.jaas.JaasAuthenticationCallbackHandler[])\n * callbackHandlers} property.\n *\n * <pre>\n * &lt;property name=\"callbackHandlers\"&gt;\n *   &lt;list&gt;\n *     &lt;bean class=\"org.springframework.security.authentication.jaas.TestCallbackHandler\"/&gt;\n *     &lt;bean class=\"{@link JaasNameCallbackHandler org.springframework.security.authentication.jaas.JaasNameCallbackHandler}\"/&gt;\n *     &lt;bean class=\"{@link JaasPasswordCallbackHandler org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler}\"/&gt;\n *  &lt;/list&gt;\n * &lt;/property&gt;\n * </pre>\n *\n * <p>\n * After calling LoginContext.login(), the JaasAuthenticationProvider will retrieve the\n * returned Principals from the Subject (LoginContext.getSubject().getPrincipals). Each\n * returned principal is then passed to the configured {@link AuthorityGranter}s. An\n * AuthorityGranter is a mapping between a returned Principal, and a role name. If an\n * AuthorityGranter wishes to grant an Authorization a role, it returns that role name\n * from it's {@link AuthorityGranter#grant(java.security.Principal)} method. The returned\n * role will be applied to the Authorization object as a {@link GrantedAuthority}.\n * </p>\n * <p>\n * AuthorityGranters are configured in spring xml as follows...\n *\n * <pre>\n * &lt;property name=\"authorityGranters\"&gt;\n *   &lt;list&gt;\n *     &lt;bean class=\"org.springframework.security.authentication.jaas.TestAuthorityGranter\"/&gt;\n *   &lt;/list&gt;\n *  &lt;/property&gt;\n * </pre>\n *\n * A configuration note: The JaasAuthenticationProvider uses the security properties\n * \"login.config.url.X\" to configure jaas. If you would like to customize the way Jaas\n * gets configured, create a subclass of this and override the\n * {@link #configureJaas(Resource)} method.\n *\n * @author Ray Krueger\n * @author Rob Winch\n */\npublic class JaasAuthenticationProvider extends AbstractJaasAuthenticationProvider {\n\n\t// exists for passivity\n\tprotected static final Log log = LogFactory.getLog(JaasAuthenticationProvider.class);\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate Resource loginConfig;\n\n\tprivate boolean refreshConfigurationOnStartup = true;\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\t// the superclass is not called because it does additional checks that are\n\t\t// non-passive\n\t\tAssert.hasLength(getLoginContextName(), () -> \"loginContextName must be set on \" + getClass());\n\t\tAssert.notNull(this.loginConfig, () -> \"loginConfig must be set on \" + getClass());\n\t\tconfigureJaas(this.loginConfig);\n\t\tAssert.notNull(Configuration.getConfiguration(),\n\t\t\t\t\"As per https://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/login/Configuration.html \"\n\t\t\t\t\t\t+ \"\\\"If a Configuration object was set via the Configuration.setConfiguration method, then that object is \"\n\t\t\t\t\t\t+ \"returned. Otherwise, a default Configuration object is returned\\\". Your JRE returned null to \"\n\t\t\t\t\t\t+ \"Configuration.getConfiguration().\");\n\t}\n\n\t@Override\n\tprotected LoginContext createLoginContext(CallbackHandler handler) throws LoginException {\n\t\treturn new LoginContext(getLoginContextName(), handler);\n\t}\n\n\t/**\n\t * Hook method for configuring Jaas.\n\t * @param loginConfig URL to Jaas login configuration\n\t * @throws IOException if there is a problem reading the config resource.\n\t */\n\tprotected void configureJaas(Resource loginConfig) throws IOException {\n\t\tconfigureJaasUsingLoop();\n\t\tif (this.refreshConfigurationOnStartup) {\n\t\t\t// Overcome issue in SEC-760\n\t\t\tConfiguration.getConfiguration().refresh();\n\t\t}\n\t}\n\n\t/**\n\t * Loops through the login.config.url.1,login.config.url.2 properties looking for the\n\t * login configuration. If it is not set, it will be set to the last available\n\t * login.config.url.X property.\n\t *\n\t */\n\tprivate void configureJaasUsingLoop() throws IOException {\n\t\tString loginConfigUrl = convertLoginConfigToUrl();\n\t\tboolean alreadySet = false;\n\t\tint n = 1;\n\t\tfinal String prefix = \"login.config.url.\";\n\t\tString existing;\n\t\twhile ((existing = Security.getProperty(prefix + n)) != null) {\n\t\t\talreadySet = existing.equals(loginConfigUrl);\n\t\t\tif (alreadySet) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tn++;\n\t\t}\n\t\tif (!alreadySet) {\n\t\t\tString key = prefix + n;\n\t\t\tlog.debug(LogMessage.format(\"Setting security property [%s] to: %s\", key, loginConfigUrl));\n\t\t\tSecurity.setProperty(key, loginConfigUrl);\n\t\t}\n\t}\n\n\tprivate String convertLoginConfigToUrl() throws IOException {\n\t\tString loginConfigPath;\n\t\ttry {\n\t\t\tloginConfigPath = this.loginConfig.getFile().getAbsolutePath().replace(File.separatorChar, '/');\n\t\t\tif (!loginConfigPath.startsWith(\"/\")) {\n\t\t\t\tloginConfigPath = \"/\" + loginConfigPath;\n\t\t\t}\n\t\t\treturn new URL(\"file\", \"\", loginConfigPath).toString();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\t// SEC-1700: May be inside a jar\n\t\t\treturn this.loginConfig.getURL().toString();\n\t\t}\n\t}\n\n\t/**\n\t * Publishes the {@link JaasAuthenticationFailedEvent}. Can be overridden by\n\t * subclasses for different functionality\n\t * @param token The authentication token being processed\n\t * @param ase The exception that caused the authentication failure\n\t */\n\t@Override\n\tprotected void publishFailureEvent(UsernamePasswordAuthenticationToken token, AuthenticationException ase) {\n\t\t// exists for passivity (the superclass does a null check before publishing)\n\t\tgetApplicationEventPublisher().publishEvent(new JaasAuthenticationFailedEvent(token, ase));\n\t}\n\n\tpublic Resource getLoginConfig() {\n\t\treturn this.loginConfig;\n\t}\n\n\t/**\n\t * Set the JAAS login configuration file.\n\t * @param loginConfig\n\t *\n\t * @see <a href=\n\t * \"https://java.sun.com/j2se/1.5.0/docs/guide/security/jaas/JAASRefGuide.html\">JAAS\n\t * Reference</a>\n\t */\n\tpublic void setLoginConfig(Resource loginConfig) {\n\t\tthis.loginConfig = loginConfig;\n\t}\n\n\t/**\n\t * If set, a call to {@code Configuration#refresh()} will be made by\n\t * {@code #configureJaas(Resource) } method. Defaults to {@code true}.\n\t * @param refresh set to {@code false} to disable reloading of the configuration. May\n\t * be useful in some environments.\n\t * @see <a href=\"https://jira.springsource.org/browse/SEC-1320\">SEC-1320</a>\n\t */\n\tpublic void setRefreshConfigurationOnStartup(boolean refresh) {\n\t\tthis.refreshConfigurationOnStartup = refresh;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationToken.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.util.List;\n\nimport javax.security.auth.login.LoginContext;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * UsernamePasswordAuthenticationToken extension to carry the Jaas LoginContext that the\n * user was logged into\n *\n * @author Ray Krueger\n */\npublic class JaasAuthenticationToken extends UsernamePasswordAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final transient LoginContext loginContext;\n\n\tpublic JaasAuthenticationToken(Object principal, @Nullable Object credentials, LoginContext loginContext) {\n\t\tsuper(principal, credentials);\n\t\tthis.loginContext = loginContext;\n\t}\n\n\tpublic JaasAuthenticationToken(Object principal, @Nullable Object credentials, List<GrantedAuthority> authorities,\n\t\t\tLoginContext loginContext) {\n\t\tsuper(principal, credentials, authorities);\n\t\tthis.loginContext = loginContext;\n\t}\n\n\tprotected JaasAuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.loginContext = builder.loginContext;\n\t}\n\n\tpublic LoginContext getLoginContext() {\n\t\treturn this.loginContext;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link JaasAuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends UsernamePasswordAuthenticationToken.Builder<B> {\n\n\t\tprivate LoginContext loginContext;\n\n\t\tprotected Builder(JaasAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.loginContext = token.getLoginContext();\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link LoginContext}\n\t\t * @param loginContext the {@link LoginContext} to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic B loginContext(LoginContext loginContext) {\n\t\t\tthis.loginContext = loginContext;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic JaasAuthenticationToken build() {\n\t\t\treturn new JaasAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/JaasGrantedAuthority.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.security.Principal;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * {@code GrantedAuthority} which, in addition to the assigned role, holds the principal\n * that an {@link AuthorityGranter} used as a reason to grant this authority.\n *\n * @author Ray Krueger\n * @see AuthorityGranter\n */\npublic final class JaasGrantedAuthority implements GrantedAuthority {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String role;\n\n\tprivate final Principal principal;\n\n\tpublic JaasGrantedAuthority(String role, Principal principal) {\n\t\tAssert.notNull(role, \"role cannot be null\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tthis.role = role;\n\t\tthis.principal = principal;\n\t}\n\n\tpublic Principal getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic String getAuthority() {\n\t\treturn this.role;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj instanceof JaasGrantedAuthority jga) {\n\t\t\treturn this.role.equals(jga.getAuthority()) && this.principal.equals(jga.getPrincipal());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.principal.hashCode();\n\t\tresult = 31 * result + this.role.hashCode();\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Jaas Authority [\" + this.role + \",\" + this.principal + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/JaasNameCallbackHandler.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.NameCallback;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * The most basic Callbacks to be handled when using a LoginContext from JAAS, are the\n * NameCallback and PasswordCallback. Spring Security provides the JaasNameCallbackHandler\n * specifically tailored to handling the NameCallback. <br>\n *\n * @author Ray Krueger\n * @see <a href=\n * \"https://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/Callback.html\">Callback</a>\n * @see <a href=\n * \"https://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/NameCallback.html\">NameCallback</a>\n */\npublic class JaasNameCallbackHandler implements JaasAuthenticationCallbackHandler {\n\n\t/**\n\t * If the callback passed to the 'handle' method is an instance of NameCallback, the\n\t * JaasNameCallbackHandler will call,\n\t * callback.setName(authentication.getPrincipal().toString()).\n\t * @param callback\n\t * @param authentication\n\t *\n\t */\n\t@Override\n\tpublic void handle(Callback callback, Authentication authentication) {\n\t\tif (callback instanceof NameCallback) {\n\t\t\t((NameCallback) callback).setName(getUserName(authentication));\n\t\t}\n\t}\n\n\tprivate String getUserName(Authentication authentication) {\n\t\tObject principal = authentication.getPrincipal();\n\t\tif (principal instanceof UserDetails) {\n\t\t\treturn ((UserDetails) principal).getUsername();\n\t\t}\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\treturn principal.toString();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/JaasPasswordCallbackHandler.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.PasswordCallback;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * The most basic Callbacks to be handled when using a LoginContext from JAAS, are the\n * NameCallback and PasswordCallback. Spring Security provides the\n * JaasPasswordCallbackHandler specifically tailored to handling the PasswordCallback.\n * <br>\n *\n * @author Ray Krueger\n * @see <a href=\n * \"https://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/Callback.html\">Callback</a>\n * @see <a href=\n * \"https://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/PasswordCallback.html\">\n * PasswordCallback</a>\n */\npublic class JaasPasswordCallbackHandler implements JaasAuthenticationCallbackHandler {\n\n\t/**\n\t * If the callback passed to the 'handle' method is an instance of PasswordCallback,\n\t * the JaasPasswordCallbackHandler will call,\n\t * callback.setPassword(authentication.getCredentials().toString()).\n\t * @param callback\n\t * @param auth\n\t *\n\t */\n\t@Override\n\tpublic void handle(Callback callback, Authentication auth) {\n\t\tif (callback instanceof PasswordCallback) {\n\t\t\tObject credentials = auth.getCredentials();\n\t\t\tif (credentials != null) {\n\t\t\t\t((PasswordCallback) callback).setPassword(credentials.toString().toCharArray());\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/LoginExceptionResolver.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport javax.security.auth.login.LoginException;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * The JaasAuthenticationProvider takes an instance of LoginExceptionResolver to resolve\n * LoginModule specific exceptions to Spring Security <tt>AuthenticationException</tt>s.\n * For instance, a configured login module could throw a ScrewedUpPasswordException that\n * extends LoginException, in this instance the LoginExceptionResolver implementation\n * would return a\n * {@link org.springframework.security.authentication.BadCredentialsException}.\n *\n * @author Ray Krueger\n */\npublic interface LoginExceptionResolver {\n\n\t/**\n\t * Translates a Jaas LoginException to an SpringSecurityException.\n\t * @param ex The LoginException thrown by the configured LoginModule.\n\t * @return The AuthenticationException that the JaasAuthenticationProvider should\n\t * throw.\n\t */\n\tAuthenticationException resolveException(LoginException ex);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/SecurityContextLoginModule.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.util.Map;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.login.LoginException;\nimport javax.security.auth.spi.LoginModule;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of {@link LoginModule} that uses a Spring Security\n * {@link org.springframework.security.core.context.SecurityContext SecurityContext} to\n * provide authentication.\n * <p>\n * This LoginModule provides opposite functionality to the\n * {@link JaasAuthenticationProvider} API, and should not really be used in conjunction\n * with it.\n * <p>\n * The {@link JaasAuthenticationProvider} allows Spring Security to authenticate against\n * Jaas.\n * <p>\n * The SecurityContextLoginModule allows a Jaas based application to authenticate against\n * Spring Security. If there is no Authentication in the {@link SecurityContextHolder} the\n * login() method will throw a LoginException by default. This functionality can be\n * changed with the <tt>ignoreMissingAuthentication</tt> option by setting it to \"true\".\n * Setting ignoreMissingAuthentication=true will tell the SecurityContextLoginModule to\n * simply return false and be ignored if the authentication is null.\n *\n * @author Brian Moseley\n * @author Ray Krueger\n */\npublic class SecurityContextLoginModule implements LoginModule {\n\n\tprivate static final Log log = LogFactory.getLog(SecurityContextLoginModule.class);\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable Authentication authen;\n\n\tprivate @Nullable Subject subject;\n\n\tprivate boolean ignoreMissingAuthentication = false;\n\n\t/**\n\t * Abort the authentication process by forgetting the Spring Security\n\t * <code>Authentication</code>.\n\t * @return true if this method succeeded, or false if this <code>LoginModule</code>\n\t * should be ignored.\n\t */\n\t@Override\n\tpublic boolean abort() {\n\t\tif (this.authen == null) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.authen = null;\n\t\treturn true;\n\t}\n\n\t/**\n\t * Authenticate the <code>Subject</code> (phase two) by adding the Spring Security\n\t * <code>Authentication</code> to the <code>Subject</code>'s principals.\n\t * @return true if this method succeeded, or false if this <code>LoginModule</code>\n\t * should be ignored.\n\t */\n\t@Override\n\tpublic boolean commit() {\n\t\tif (this.authen == null) {\n\t\t\treturn false;\n\t\t}\n\t\tAssert.notNull(this.subject, \"subject cannot be null\");\n\t\tthis.subject.getPrincipals().add(this.authen);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Nullable Authentication getAuthentication() {\n\t\treturn this.authen;\n\t}\n\n\t@Nullable Subject getSubject() {\n\t\treturn this.subject;\n\t}\n\n\t/**\n\t * Initialize this <code>LoginModule</code>. Ignores the callback handler, since the\n\t * code establishing the <code>LoginContext</code> likely won't provide one that\n\t * understands Spring Security. Also ignores the <code>sharedState</code> and\n\t * <code>options</code> parameters, since none are recognized.\n\t * @param subject the <code>Subject</code> to be authenticated.\n\t * @param callbackHandler is ignored\n\t * @param sharedState is ignored\n\t * @param options are ignored\n\t */\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {\n\t\tthis.subject = subject;\n\t\tif (options != null) {\n\t\t\tthis.ignoreMissingAuthentication = \"true\".equals(options.get(\"ignoreMissingAuthentication\"));\n\t\t}\n\t}\n\n\t/**\n\t * Authenticate the <code>Subject</code> (phase one) by extracting the Spring Security\n\t * <code>Authentication</code> from the current <code>SecurityContext</code>.\n\t * @return true if the authentication succeeded, or false if this\n\t * <code>LoginModule</code> should be ignored.\n\t * @throws LoginException if the authentication fails\n\t */\n\t@Override\n\tpublic boolean login() throws LoginException {\n\t\tthis.authen = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (this.authen != null) {\n\t\t\treturn true;\n\t\t}\n\t\tString msg = \"Login cannot complete, authentication not found in security context\";\n\t\tif (!this.ignoreMissingAuthentication) {\n\t\t\tthrow new LoginException(msg);\n\t\t}\n\t\tlog.warn(msg);\n\t\treturn false;\n\t}\n\n\t/**\n\t * Log out the <code>Subject</code>.\n\t * @return true if this method succeeded, or false if this <code>LoginModule</code>\n\t * should be ignored.\n\t */\n\t@Override\n\tpublic boolean logout() {\n\t\tif (this.authen == null) {\n\t\t\treturn false;\n\t\t}\n\t\tAssert.notNull(this.subject, \"subject cannot be null\");\n\t\tthis.subject.getPrincipals().remove(this.authen);\n\t\tthis.authen = null;\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/event/JaasAuthenticationEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas.event;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Parent class for events fired by the\n * {@link org.springframework.security.authentication.jaas.JaasAuthenticationProvider\n * JaasAuthenticationProvider}.\n *\n * @author Ray Krueger\n */\npublic abstract class JaasAuthenticationEvent extends ApplicationEvent {\n\n\t/**\n\t * The Authentication object is stored as the ApplicationEvent 'source'.\n\t * @param auth\n\t */\n\tpublic JaasAuthenticationEvent(Authentication auth) {\n\t\tsuper(auth);\n\t}\n\n\t/**\n\t * Pre-casted method that returns the 'source' of the event.\n\t * @return the Authentication\n\t */\n\tpublic Authentication getAuthentication() {\n\t\treturn (Authentication) this.source;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/event/JaasAuthenticationFailedEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Fired when LoginContext.login throws a LoginException, or if any other exception is\n * thrown during that time.\n *\n * @author Ray Krueger\n */\npublic class JaasAuthenticationFailedEvent extends JaasAuthenticationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -240510538971925002L;\n\n\tprivate final Exception exception;\n\n\tpublic JaasAuthenticationFailedEvent(Authentication auth, Exception exception) {\n\t\tsuper(auth);\n\t\tthis.exception = exception;\n\t}\n\n\tpublic Exception getException() {\n\t\treturn this.exception;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/event/JaasAuthenticationSuccessEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas.event;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Fired by the\n * {@link org.springframework.security.authentication.jaas.JaasAuthenticationProvider\n * JaasAuthenticationProvider} after successfully logging the user into the LoginContext,\n * handling all callbacks, and calling all AuthorityGranters.\n *\n * @author Ray Krueger\n */\npublic class JaasAuthenticationSuccessEvent extends JaasAuthenticationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2236826715750256181L;\n\n\tpublic JaasAuthenticationSuccessEvent(Authentication auth) {\n\t\tsuper(auth);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/event/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * JAAS authentication events which can be published to the Spring application context by\n * the JAAS authentication provider.\n */\n@NullMarked\npackage org.springframework.security.authentication.jaas.event;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/memory/InMemoryConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas.memory;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.Configuration;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * An in memory representation of a JAAS configuration. The constructor accepts a Map\n * where the key represents the name of the login context name and the value is an Array\n * of {@link AppConfigurationEntry} for that login context name. A default Array of\n * {@link AppConfigurationEntry}s can be specified which will be returned if a login\n * context is specified which is undefined.\n * </p>\n *\n * @author Rob Winch\n */\npublic class InMemoryConfiguration extends Configuration {\n\n\tprivate final AppConfigurationEntry @Nullable [] defaultConfiguration;\n\n\tprivate final Map<String, AppConfigurationEntry[]> mappedConfigurations;\n\n\t/**\n\t * Creates a new instance with only a defaultConfiguration. Any configuration name\n\t * will result in defaultConfiguration being returned.\n\t * @param defaultConfiguration The result for any calls to\n\t * {@link #getAppConfigurationEntry(String)}. Can be <code>null</code>.\n\t */\n\tpublic InMemoryConfiguration(AppConfigurationEntry[] defaultConfiguration) {\n\t\tthis(Collections.emptyMap(), defaultConfiguration);\n\t}\n\n\t/**\n\t * Creates a new instance with a mapping of login context name to an array of\n\t * {@link AppConfigurationEntry}s.\n\t * @param mappedConfigurations each key represents a login context name and each value\n\t * is an Array of {@link AppConfigurationEntry}s that should be used.\n\t */\n\tpublic InMemoryConfiguration(Map<String, AppConfigurationEntry[]> mappedConfigurations) {\n\t\tthis(mappedConfigurations, null);\n\t}\n\n\t/**\n\t * Creates a new instance with a mapping of login context name to an array of\n\t * {@link AppConfigurationEntry}s along with a default configuration that will be used\n\t * if no mapping is found for the given login context name.\n\t * @param mappedConfigurations each key represents a login context name and each value\n\t * is an Array of {@link AppConfigurationEntry}s that should be used.\n\t * @param defaultConfiguration The result for any calls to\n\t * {@link #getAppConfigurationEntry(String)}. Can be <code>null</code>.\n\t */\n\tpublic InMemoryConfiguration(Map<String, AppConfigurationEntry[]> mappedConfigurations,\n\t\t\tAppConfigurationEntry @Nullable [] defaultConfiguration) {\n\t\tAssert.notNull(mappedConfigurations, \"mappedConfigurations cannot be null.\");\n\t\tthis.mappedConfigurations = mappedConfigurations;\n\t\tthis.defaultConfiguration = defaultConfiguration;\n\t}\n\n\t@Override\n\tpublic AppConfigurationEntry @Nullable [] getAppConfigurationEntry(String name) {\n\t\tAppConfigurationEntry[] mappedResult = this.mappedConfigurations.get(name);\n\t\treturn (mappedResult != null) ? mappedResult : this.defaultConfiguration;\n\t}\n\n\t/**\n\t * Does nothing, but required for JDK5\n\t */\n\t@Override\n\tpublic void refresh() {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/memory/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * An in memory JAAS implementation.\n */\n@NullMarked\npackage org.springframework.security.authentication.jaas.memory;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/jaas/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * An authentication provider for JAAS.\n */\n@NullMarked\npackage org.springframework.security.authentication.jaas;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/DefaultOneTimeToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.io.Serial;\nimport java.time.Instant;\n\nimport org.springframework.util.Assert;\n\n/**\n * A default implementation of {@link OneTimeToken}\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic class DefaultOneTimeToken implements OneTimeToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1545822943352278549L;\n\n\tprivate final String token;\n\n\tprivate final String username;\n\n\tprivate final Instant expireAt;\n\n\tpublic DefaultOneTimeToken(String token, String username, Instant expireAt) {\n\t\tAssert.hasText(token, \"token cannot be empty\");\n\t\tAssert.hasText(username, \"username cannot be empty\");\n\t\tAssert.notNull(expireAt, \"expireAt cannot be null\");\n\t\tthis.token = token;\n\t\tthis.username = username;\n\t\tthis.expireAt = expireAt;\n\t}\n\n\t@Override\n\tpublic String getTokenValue() {\n\t\treturn this.token;\n\t}\n\n\t@Override\n\tpublic String getUsername() {\n\t\treturn this.username;\n\t}\n\n\tpublic Instant getExpiresAt() {\n\t\treturn this.expireAt;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/GenerateOneTimeTokenRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.time.Duration;\n\nimport org.springframework.util.Assert;\n\n/**\n * Class to store information related to an One-Time Token authentication request\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic class GenerateOneTimeTokenRequest {\n\n\tprivate static final Duration DEFAULT_EXPIRES_IN = Duration.ofMinutes(5);\n\n\tprivate final String username;\n\n\tprivate final Duration expiresIn;\n\n\tpublic GenerateOneTimeTokenRequest(String username) {\n\t\tthis(username, DEFAULT_EXPIRES_IN);\n\t}\n\n\tpublic GenerateOneTimeTokenRequest(String username, Duration expiresIn) {\n\t\tAssert.hasText(username, \"username cannot be empty\");\n\t\tAssert.notNull(expiresIn, \"expiresIn cannot be null\");\n\t\tthis.username = username;\n\t\tthis.expiresIn = expiresIn;\n\t}\n\n\tpublic String getUsername() {\n\t\treturn this.username;\n\t}\n\n\tpublic Duration getExpiresIn() {\n\t\treturn this.expiresIn;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/InMemoryOneTimeTokenService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Provides an in-memory implementation of the {@link OneTimeTokenService} interface that\n * uses a {@link ConcurrentHashMap} to store the generated {@link OneTimeToken}. A random\n * {@link UUID} is used as the token value. A clean-up of the expired tokens is made if\n * there is more or equal than 100 tokens stored in the map.\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic final class InMemoryOneTimeTokenService implements OneTimeTokenService {\n\n\tprivate final Map<String, OneTimeToken> oneTimeTokenByToken = new ConcurrentHashMap<>();\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t@Override\n\tpublic OneTimeToken generate(GenerateOneTimeTokenRequest request) {\n\t\tString token = UUID.randomUUID().toString();\n\t\tInstant expiresAt = this.clock.instant().plus(request.getExpiresIn());\n\t\tOneTimeToken ott = new DefaultOneTimeToken(token, request.getUsername(), expiresAt);\n\t\tthis.oneTimeTokenByToken.put(token, ott);\n\t\tcleanExpiredTokensIfNeeded();\n\t\treturn ott;\n\t}\n\n\t@Override\n\tpublic @Nullable OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {\n\t\tOneTimeToken ott = this.oneTimeTokenByToken.remove(authenticationToken.getTokenValue());\n\t\tif (ott == null || isExpired(ott)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn ott;\n\t}\n\n\tprivate void cleanExpiredTokensIfNeeded() {\n\t\tif (this.oneTimeTokenByToken.size() < 100) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Map.Entry<String, OneTimeToken> entry : this.oneTimeTokenByToken.entrySet()) {\n\t\t\tif (isExpired(entry.getValue())) {\n\t\t\t\tthis.oneTimeTokenByToken.remove(entry.getKey());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean isExpired(OneTimeToken ott) {\n\t\treturn this.clock.instant().isAfter(ott.getExpiresAt());\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used when generating one-time token and checking token\n\t * expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/InvalidOneTimeTokenException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * An {@link AuthenticationException} that indicates an invalid one-time token.\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic class InvalidOneTimeTokenException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3651018515682919943L;\n\n\tpublic InvalidOneTimeTokenException(String msg) {\n\t\tsuper(msg);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/JdbcOneTimeTokenService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.function.Function;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;\nimport org.springframework.scheduling.support.CronTrigger;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n *\n * A JDBC implementation of an {@link OneTimeTokenService} that uses a\n * {@link JdbcOperations} for {@link OneTimeToken} persistence.\n *\n * <p>\n * <b>NOTE:</b> This {@code JdbcOneTimeTokenService} depends on the table definition\n * described in\n * \"classpath:org/springframework/security/core/ott/jdbc/one-time-tokens-schema.sql\" and\n * therefore MUST be defined in the database schema.\n *\n * @author Max Batischev\n * @since 6.4\n */\npublic final class JdbcOneTimeTokenService implements OneTimeTokenService, DisposableBean, InitializingBean {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final JdbcOperations jdbcOperations;\n\n\tprivate Function<OneTimeToken, List<SqlParameterValue>> oneTimeTokenParametersMapper = new OneTimeTokenParametersMapper();\n\n\tprivate RowMapper<OneTimeToken> oneTimeTokenRowMapper = new OneTimeTokenRowMapper();\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\tprivate @Nullable ThreadPoolTaskScheduler taskScheduler;\n\n\tprivate static final String DEFAULT_CLEANUP_CRON = \"@hourly\";\n\n\tprivate static final String TABLE_NAME = \"one_time_tokens\";\n\n\t// @formatter:off\n\tprivate static final String COLUMN_NAMES = \"token_value, \"\n\t\t\t+ \"username, \"\n\t\t\t+ \"expires_at\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String SAVE_ONE_TIME_TOKEN_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?)\";\n\t// @formatter:on\n\n\tprivate static final String FILTER = \"token_value = ?\";\n\n\tprivate static final String DELETE_ONE_TIME_TOKEN_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + FILTER;\n\n\t// @formatter:off\n\tprivate static final String SELECT_ONE_TIME_TOKEN_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + FILTER;\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String DELETE_ONE_TIME_TOKENS_BY_EXPIRY_TIME_QUERY = \"DELETE FROM \"\n\t\t\t+ TABLE_NAME\n\t\t\t+ \" WHERE expires_at < ?\";\n\t// @formatter:on\n\n\t/**\n\t * Constructs a {@code JdbcOneTimeTokenService} using the provide parameters.\n\t * @param jdbcOperations the JDBC operations\n\t */\n\tpublic JdbcOneTimeTokenService(JdbcOperations jdbcOperations) {\n\t\tAssert.notNull(jdbcOperations, \"jdbcOperations cannot be null\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t\tthis.taskScheduler = createTaskScheduler(DEFAULT_CLEANUP_CRON);\n\t}\n\n\t/**\n\t * Sets the chron expression used for cleaning up expired tokens. The default is to\n\t * run hourly.\n\t *\n\t * For more advanced use cases the cleanupCron may be set to null which will disable\n\t * the built-in cleanup. Users can then invoke {@link #cleanupExpiredTokens()} using\n\t * custom logic.\n\t * @param cleanupCron the chron expression passed to {@link CronTrigger} used for\n\t * determining how frequent to perform cleanup. The default is \"@hourly\".\n\t * @see CronTrigger\n\t * @see #cleanupExpiredTokens()\n\t */\n\tpublic void setCleanupCron(String cleanupCron) {\n\t\tthis.taskScheduler = createTaskScheduler(cleanupCron);\n\t}\n\n\t@Override\n\tpublic OneTimeToken generate(GenerateOneTimeTokenRequest request) {\n\t\tAssert.notNull(request, \"generateOneTimeTokenRequest cannot be null\");\n\t\tString token = UUID.randomUUID().toString();\n\t\tInstant expiresAt = this.clock.instant().plus(request.getExpiresIn());\n\t\tOneTimeToken oneTimeToken = new DefaultOneTimeToken(token, request.getUsername(), expiresAt);\n\t\tinsertOneTimeToken(oneTimeToken);\n\t\treturn oneTimeToken;\n\t}\n\n\tprivate void insertOneTimeToken(OneTimeToken oneTimeToken) {\n\t\tList<SqlParameterValue> parameters = this.oneTimeTokenParametersMapper.apply(oneTimeToken);\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\tthis.jdbcOperations.update(SAVE_ONE_TIME_TOKEN_SQL, pss);\n\t}\n\n\t@Override\n\tpublic @Nullable OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {\n\t\tAssert.notNull(authenticationToken, \"authenticationToken cannot be null\");\n\n\t\tList<OneTimeToken> tokens = selectOneTimeToken(authenticationToken);\n\t\tif (CollectionUtils.isEmpty(tokens)) {\n\t\t\treturn null;\n\t\t}\n\t\tOneTimeToken token = tokens.get(0);\n\t\tdeleteOneTimeToken(token);\n\t\tif (isExpired(token)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn token;\n\t}\n\n\tprivate boolean isExpired(OneTimeToken ott) {\n\t\treturn this.clock.instant().isAfter(ott.getExpiresAt());\n\t}\n\n\tprivate List<OneTimeToken> selectOneTimeToken(OneTimeTokenAuthenticationToken authenticationToken) {\n\t\tList<SqlParameterValue> parameters = List\n\t\t\t.of(new SqlParameterValue(Types.VARCHAR, authenticationToken.getTokenValue()));\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\treturn this.jdbcOperations.query(SELECT_ONE_TIME_TOKEN_SQL, pss, this.oneTimeTokenRowMapper);\n\t}\n\n\tprivate void deleteOneTimeToken(OneTimeToken oneTimeToken) {\n\t\tList<SqlParameterValue> parameters = List\n\t\t\t.of(new SqlParameterValue(Types.VARCHAR, oneTimeToken.getTokenValue()));\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\tthis.jdbcOperations.update(DELETE_ONE_TIME_TOKEN_SQL, pss);\n\t}\n\n\tprivate @Nullable ThreadPoolTaskScheduler createTaskScheduler(String cleanupCron) {\n\t\tif (cleanupCron == null) {\n\t\t\treturn null;\n\t\t}\n\t\tThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();\n\t\ttaskScheduler.setThreadNamePrefix(\"spring-one-time-tokens-\");\n\t\ttaskScheduler.initialize();\n\t\ttaskScheduler.schedule(this::cleanupExpiredTokens, new CronTrigger(cleanupCron));\n\t\treturn taskScheduler;\n\t}\n\n\tpublic void cleanupExpiredTokens() {\n\t\tList<SqlParameterValue> parameters = List\n\t\t\t.of(new SqlParameterValue(Types.TIMESTAMP, Timestamp.from(Instant.now())));\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\tint deletedCount = this.jdbcOperations.update(DELETE_ONE_TIME_TOKENS_BY_EXPIRY_TIME_QUERY, pss);\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(\"Cleaned up \" + deletedCount + \" expired tokens\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tif (this.taskScheduler != null) {\n\t\t\tthis.taskScheduler.afterPropertiesSet();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void destroy() throws Exception {\n\t\tif (this.taskScheduler != null) {\n\t\t\tthis.taskScheduler.shutdown();\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used when generating one-time token and checking token\n\t * expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n\t/**\n\t * The default {@code Function} that maps {@link OneTimeToken} to a {@code List} of\n\t * {@link SqlParameterValue}.\n\t *\n\t * @author Max Batischev\n\t * @since 6.4\n\t */\n\tprivate static class OneTimeTokenParametersMapper implements Function<OneTimeToken, List<SqlParameterValue>> {\n\n\t\t@Override\n\t\tpublic List<SqlParameterValue> apply(OneTimeToken oneTimeToken) {\n\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, oneTimeToken.getTokenValue()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, oneTimeToken.getUsername()));\n\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP, Timestamp.from(oneTimeToken.getExpiresAt())));\n\t\t\treturn parameters;\n\t\t}\n\n\t}\n\n\t/**\n\t * The default {@link RowMapper} that maps the current row in\n\t * {@code java.sql.ResultSet} to {@link OneTimeToken}.\n\t *\n\t * @author Max Batischev\n\t * @since 6.4\n\t */\n\tprivate static class OneTimeTokenRowMapper implements RowMapper<OneTimeToken> {\n\n\t\t@Override\n\t\tpublic OneTimeToken mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\tString tokenValue = rs.getString(\"token_value\");\n\t\t\tString userName = rs.getString(\"username\");\n\t\t\tInstant expiresAt = rs.getTimestamp(\"expires_at\").toInstant();\n\t\t\treturn new DefaultOneTimeToken(tokenValue, userName, expiresAt);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/OneTimeToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.io.Serializable;\nimport java.time.Instant;\n\n/**\n * Represents a one-time use token with an associated username and expiration time.\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic interface OneTimeToken extends Serializable {\n\n\t/**\n\t * @return the one-time token value, never {@code null}\n\t */\n\tString getTokenValue();\n\n\t/**\n\t * @return the username associated with this token, never {@code null}\n\t */\n\tString getUsername();\n\n\t/**\n\t * @return the expiration time of the token\n\t */\n\tInstant getExpiresAt();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.io.Serial;\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * The result of a successful one-time-token authentication\n *\n * @author Josh Cummings\n * @since 7.0\n */\npublic class OneTimeTokenAuthentication extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1195893764725073959L;\n\n\tprivate final Object principal;\n\n\tpublic OneTimeTokenAuthentication(Object principal, Collection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tthis.principal = principal;\n\t\tsetAuthenticated(true);\n\t}\n\n\tprotected OneTimeTokenAuthentication(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.principal = builder.principal;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link OneTimeTokenAuthentication} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate Object principal;\n\n\t\tprotected Builder(OneTimeTokenAuthentication token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.principal;\n\t\t}\n\n\t\t/**\n\t\t * Use this principal.\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OneTimeTokenAuthentication build() {\n\t\t\treturn new OneTimeTokenAuthentication(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.util.Collection;\nimport java.util.HashSet;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} responsible for authenticating users based on\n * one-time tokens. It uses an {@link OneTimeTokenService} to consume tokens and an\n * {@link UserDetailsService} to fetch user authorities.\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic final class OneTimeTokenAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.OTT_AUTHORITY;\n\n\tprivate final OneTimeTokenService oneTimeTokenService;\n\n\tprivate final UserDetailsService userDetailsService;\n\n\tpublic OneTimeTokenAuthenticationProvider(OneTimeTokenService oneTimeTokenService,\n\t\t\tUserDetailsService userDetailsService) {\n\t\tAssert.notNull(oneTimeTokenService, \"oneTimeTokenService cannot be null\");\n\t\tAssert.notNull(userDetailsService, \"userDetailsService cannot be null\");\n\t\tthis.userDetailsService = userDetailsService;\n\t\tthis.oneTimeTokenService = oneTimeTokenService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOneTimeTokenAuthenticationToken otpAuthenticationToken = (OneTimeTokenAuthenticationToken) authentication;\n\t\tOneTimeToken consumed = this.oneTimeTokenService.consume(otpAuthenticationToken);\n\t\tif (consumed == null) {\n\t\t\tthrow new InvalidOneTimeTokenException(\"Invalid token\");\n\t\t}\n\t\ttry {\n\t\t\tUserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername());\n\t\t\tCollection<GrantedAuthority> authorities = new HashSet<>(user.getAuthorities());\n\t\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\t\tOneTimeTokenAuthentication authenticated = new OneTimeTokenAuthentication(user, authorities);\n\t\t\tauthenticated.setDetails(otpAuthenticationToken.getDetails());\n\t\t\treturn authenticated;\n\t\t}\n\t\tcatch (UsernameNotFoundException ex) {\n\t\t\tthrow new BadCredentialsException(\"Failed to authenticate the one-time token\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OneTimeTokenAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.io.Serial;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Represents a One-Time Token authentication that can be authenticated or not.\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -8691636031126328365L;\n\n\tprivate final @Nullable Object principal;\n\n\tprivate @Nullable String tokenValue;\n\n\t/**\n\t * @deprecated Please use constructor that takes a {@link String} instead\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tpublic OneTimeTokenAuthenticationToken(@Nullable Object principal, String tokenValue) {\n\t\tsuper(Collections.emptyList());\n\t\tthis.tokenValue = tokenValue;\n\t\tthis.principal = principal;\n\t}\n\n\tpublic OneTimeTokenAuthenticationToken(String tokenValue) {\n\t\tthis(null, tokenValue);\n\t}\n\n\t/**\n\t * @deprecated Please use {@link OneTimeTokenAuthentication} instead\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tpublic OneTimeTokenAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tthis.principal = principal;\n\t\tsetAuthenticated(true);\n\t}\n\n\t/**\n\t * Creates an unauthenticated token\n\t * @param tokenValue the one-time token value\n\t * @return an unauthenticated {@link OneTimeTokenAuthenticationToken}\n\t * @deprecated Please use constructor that takes a {@link String} instead\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tpublic static OneTimeTokenAuthenticationToken unauthenticated(@Nullable String tokenValue) {\n\t\treturn new OneTimeTokenAuthenticationToken(null, (tokenValue != null) ? tokenValue : \"\");\n\t}\n\n\t/**\n\t * Creates an unauthenticated token\n\t * @param principal the principal\n\t * @param tokenValue the one-time token value\n\t * @return an unauthenticated {@link OneTimeTokenAuthenticationToken}\n\t * @deprecated Please use constructor that takes a {@link String} instead\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tpublic static OneTimeTokenAuthenticationToken unauthenticated(Object principal, String tokenValue) {\n\t\treturn new OneTimeTokenAuthenticationToken(principal, tokenValue);\n\t}\n\n\t/**\n\t * Creates an unauthenticated token\n\t * @param principal the principal\n\t * @param authorities the principal authorities\n\t * @return an authenticated {@link OneTimeTokenAuthenticationToken}\n\t * @deprecated Please use {@link OneTimeTokenAuthentication} instead\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tpublic static OneTimeTokenAuthenticationToken authenticated(Object principal,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\treturn new OneTimeTokenAuthenticationToken(principal, authorities);\n\t}\n\n\t/**\n\t * Returns the one-time token value\n\t * @return\n\t */\n\tpublic @Nullable String getTokenValue() {\n\t\treturn this.tokenValue;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn this.tokenValue;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Interface for generating and consuming one-time tokens.\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic interface OneTimeTokenService {\n\n\t/**\n\t * Generates a one-time token based on the provided generate request.\n\t * @param request the generate request containing the necessary information to\n\t * generate the token\n\t * @return the generated {@link OneTimeToken}, never {@code null}.\n\t */\n\tOneTimeToken generate(GenerateOneTimeTokenRequest request);\n\n\t/**\n\t * Consumes a one-time token based on the provided authentication token.\n\t * @param authenticationToken the authentication token containing the one-time token\n\t * value to be consumed\n\t * @return the consumed {@link OneTimeToken} or {@code null} if the token is invalid\n\t */\n\t@Nullable OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.authentication.ott;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/reactive/InMemoryReactiveOneTimeTokenService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott.reactive;\n\nimport java.time.Clock;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.InMemoryOneTimeTokenService;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.util.Assert;\n\n/**\n * Reactive adapter for {@link InMemoryOneTimeTokenService}\n *\n * @author Max Batischev\n * @since 6.4\n * @see InMemoryOneTimeTokenService\n */\npublic final class InMemoryReactiveOneTimeTokenService implements ReactiveOneTimeTokenService {\n\n\tprivate final InMemoryOneTimeTokenService oneTimeTokenService = new InMemoryOneTimeTokenService();\n\n\t@Override\n\tpublic Mono<OneTimeToken> generate(GenerateOneTimeTokenRequest request) {\n\t\treturn Mono.just(request).map(this.oneTimeTokenService::generate);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<OneTimeToken> consume(OneTimeTokenAuthenticationToken authenticationToken) {\n\t\treturn Mono.just(authenticationToken).mapNotNull(this.oneTimeTokenService::consume);\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used when generating one-time token and checking token\n\t * expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.oneTimeTokenService.setClock(clock);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/reactive/OneTimeTokenReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott.reactive;\n\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ott.InvalidOneTimeTokenException;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthentication;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthenticationManager} for one time tokens.\n *\n * @author Max Batischev\n * @since 6.4\n */\npublic final class OneTimeTokenReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate final ReactiveOneTimeTokenService oneTimeTokenService;\n\n\tprivate final ReactiveUserDetailsService userDetailsService;\n\n\tpublic OneTimeTokenReactiveAuthenticationManager(ReactiveOneTimeTokenService oneTimeTokenService,\n\t\t\tReactiveUserDetailsService userDetailsService) {\n\t\tAssert.notNull(oneTimeTokenService, \"oneTimeTokenService cannot be null\");\n\t\tAssert.notNull(userDetailsService, \"userDetailsService cannot be null\");\n\t\tthis.oneTimeTokenService = oneTimeTokenService;\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\tif (!(authentication instanceof OneTimeTokenAuthenticationToken otpAuthenticationToken)) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\treturn this.oneTimeTokenService.consume(otpAuthenticationToken)\n\t\t\t.switchIfEmpty(Mono.defer(() -> Mono.error(new InvalidOneTimeTokenException(\"Invalid token\"))))\n\t\t\t.flatMap((consumed) -> this.userDetailsService.findByUsername(consumed.getUsername()))\n\t\t\t.map(onSuccess(otpAuthenticationToken));\n\t}\n\n\tprivate Function<UserDetails, OneTimeTokenAuthentication> onSuccess(OneTimeTokenAuthenticationToken token) {\n\t\treturn (user) -> {\n\t\t\tOneTimeTokenAuthentication authenticated = new OneTimeTokenAuthentication(user, user.getAuthorities());\n\t\t\tauthenticated.setDetails(token.getDetails());\n\t\t\treturn authenticated;\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/reactive/ReactiveOneTimeTokenService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott.reactive;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\n\n/**\n * Reactive interface for generating and consuming one-time tokens.\n *\n * @author Max Batischev\n * @since 6.4\n */\npublic interface ReactiveOneTimeTokenService {\n\n\t/**\n\t * Generates a one-time token based on the provided generate request.\n\t * @param request the generate request containing the necessary information to\n\t * generate the token\n\t * @return the generated {@link OneTimeToken}.\n\t */\n\tMono<OneTimeToken> generate(GenerateOneTimeTokenRequest request);\n\n\t/**\n\t * Consumes a one-time token based on the provided authentication token.\n\t * @param authenticationToken the authentication token containing the one-time token\n\t * value to be consumed\n\t * @return the consumed {@link OneTimeToken} or empty Mono if the token is invalid\n\t */\n\tMono<OneTimeToken> consume(OneTimeTokenAuthenticationToken authenticationToken);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/ott/reactive/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.authentication.ott.reactive;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core classes and interfaces related to user authentication, which are used throughout\n * Spring Security.\n * <p>\n * Of key importance is the\n * {@link org.springframework.security.authentication.AuthenticationManager\n * AuthenticationManager} and its default implementation\n * {@link org.springframework.security.authentication.ProviderManager ProviderManager},\n * which maintains a list\n * {@link org.springframework.security.authentication.AuthenticationProvider\n * AuthenticationProvider}s to which it delegates authentication requests.\n */\n@NullMarked\npackage org.springframework.security.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/password/CompromisedPasswordChecker.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.password;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An API for checking if a password has been compromised.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic interface CompromisedPasswordChecker {\n\n\t/**\n\t * Check whether the password is compromised. If password is null, then the return\n\t * value must be false for {@link CompromisedPasswordDecision#isCompromised()} since a\n\t * null password represents no password (e.g. the user leverages Passkeys instead).\n\t * @param password the password to check\n\t * @return a non-null {@link CompromisedPasswordDecision}\n\t */\n\tCompromisedPasswordDecision check(@Nullable String password);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/password/CompromisedPasswordDecision.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.password;\n\npublic class CompromisedPasswordDecision {\n\n\tprivate final boolean compromised;\n\n\tpublic CompromisedPasswordDecision(boolean compromised) {\n\t\tthis.compromised = compromised;\n\t}\n\n\tpublic boolean isCompromised() {\n\t\treturn this.compromised;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/password/CompromisedPasswordException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.password;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Indicates that the provided password is compromised\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic class CompromisedPasswordException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -885858958297842864L;\n\n\tpublic CompromisedPasswordException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic CompromisedPasswordException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/password/ReactiveCompromisedPasswordChecker.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.password;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\n/**\n * A Reactive API for checking if a password has been compromised.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic interface ReactiveCompromisedPasswordChecker {\n\n\t/**\n\t * Check whether the password is compromised. If password is null, then the return\n\t * value must be false for {@link CompromisedPasswordDecision#isCompromised()} since a\n\t * null password represents no password (e.g. the user leverages Passkeys instead).\n\t * @param password the password to check\n\t * @return a {@link Mono} containing the {@link CompromisedPasswordDecision}\n\t */\n\tMono<CompromisedPasswordDecision> check(@Nullable String password);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authentication/password/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.authentication.password;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AllAuthoritiesAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} that determines if the current user is authorized by\n * evaluating if the {@link Authentication} contains all the specified authorities.\n *\n * @author Rob Winch\n * @since 7.0\n * @see AuthorityAuthorizationManager\n */\npublic final class AllAuthoritiesAuthorizationManager<T> implements AuthorizationManager<T> {\n\n\tprivate static final String ROLE_PREFIX = \"ROLE_\";\n\n\tprivate RoleHierarchy roleHierarchy = new NullRoleHierarchy();\n\n\tprivate final List<String> requiredAuthorities;\n\n\t/**\n\t * Creates a new instance.\n\t * @param requiredAuthorities the authorities that are required.\n\t */\n\tprivate AllAuthoritiesAuthorizationManager(String... requiredAuthorities) {\n\t\tAssert.notEmpty(requiredAuthorities, \"requiredAuthorities cannot be empty\");\n\t\tthis.requiredAuthorities = Arrays.asList(requiredAuthorities);\n\t}\n\n\t/**\n\t * Sets the {@link RoleHierarchy} to be used. Default is {@link NullRoleHierarchy}.\n\t * Cannot be null.\n\t * @param roleHierarchy the {@link RoleHierarchy} to use\n\t */\n\tpublic void setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tAssert.notNull(roleHierarchy, \"roleHierarchy cannot be null\");\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n\t/**\n\t * Determines if the current user is authorized by evaluating if the\n\t * {@link Authentication} contains any of specified authorities.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param object the object to check authorization on (not used).\n\t * @return an {@link AuthorityAuthorizationDecision}\n\t */\n\t@Override\n\tpublic AuthorityAuthorizationDecision authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tT object) {\n\t\tList<String> authenticatedAuthorities = getGrantedAuthorities(authentication.get());\n\t\tList<String> missingAuthorities = new ArrayList<>(this.requiredAuthorities);\n\t\tmissingAuthorities.removeIf(authenticatedAuthorities::contains);\n\t\treturn new AuthorityAuthorizationDecision(missingAuthorities.isEmpty(),\n\t\t\t\tAuthorityUtils.createAuthorityList(missingAuthorities));\n\t}\n\n\tprivate List<String> getGrantedAuthorities(Authentication authentication) {\n\t\tif (authentication == null || !authentication.isAuthenticated()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn this.roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities())\n\t\t\t.stream()\n\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t.toList();\n\t}\n\n\t/**\n\t * Creates an instance of {@link AllAuthoritiesAuthorizationManager} with the provided\n\t * authorities.\n\t * @param roles the authorities to check for prefixed with \"ROLE_\". Each role should\n\t * not start with \"ROLE_\" since it is automatically prepended already.\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AllAuthoritiesAuthorizationManager<T> hasAllRoles(String... roles) {\n\t\treturn hasAllPrefixedAuthorities(ROLE_PREFIX, roles);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AllAuthoritiesAuthorizationManager} with the provided\n\t * authorities.\n\t * @param prefix the prefix for <code>authorities</code>\n\t * @param authorities the authorities to check for prefixed with <code>prefix</code>\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AllAuthoritiesAuthorizationManager<T> hasAllPrefixedAuthorities(String prefix,\n\t\t\tString... authorities) {\n\t\tAssert.notNull(prefix, \"rolePrefix cannot be null\");\n\t\tAssert.notEmpty(authorities, \"roles cannot be empty\");\n\t\tAssert.noNullElements(authorities, \"roles cannot contain null values\");\n\t\treturn hasAllAuthorities(toNamedRolesArray(prefix, authorities));\n\t}\n\n\t/**\n\t * Creates an instance of {@link AllAuthoritiesAuthorizationManager} with the provided\n\t * authorities.\n\t * @param authorities the authorities to check for\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AllAuthoritiesAuthorizationManager<T> hasAllAuthorities(String... authorities) {\n\t\tAssert.notEmpty(authorities, \"authorities cannot be empty\");\n\t\tAssert.noNullElements(authorities, \"authorities cannot contain null values\");\n\t\treturn new AllAuthoritiesAuthorizationManager<>(authorities);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AllAuthoritiesAuthorizationManager} with the provided\n\t * authorities.\n\t * @param authorities the authorities to check for\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AllAuthoritiesAuthorizationManager<T> hasAllAuthorities(List<String> authorities) {\n\t\tAssert.notEmpty(authorities, \"authorities cannot be empty\");\n\t\tAssert.noNullElements(authorities, \"authorities cannot contain null values\");\n\t\treturn new AllAuthoritiesAuthorizationManager<>(authorities.toArray(new String[0]));\n\t}\n\n\tprivate static String[] toNamedRolesArray(String rolePrefix, String[] roles) {\n\t\tString[] result = new String[roles.length];\n\t\tfor (int i = 0; i < roles.length; i++) {\n\t\t\tString role = roles[i];\n\t\t\tAssert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> role + \" should not start with \"\n\t\t\t\t\t+ rolePrefix + \" since \" + rolePrefix\n\t\t\t\t\t+ \" is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.\");\n\t\t\tresult[i] = rolePrefix + role;\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AllAuthoritiesReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthorizationManager} that determines if the current user is\n * authorized by evaluating if the {@link Authentication} contains all the specified\n * authorities.\n *\n * @author Rob Winch\n * @since 7.0\n * @see AuthorityReactiveAuthorizationManager\n */\npublic final class AllAuthoritiesReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {\n\n\tprivate static final String ROLE_PREFIX = \"ROLE_\";\n\n\tprivate RoleHierarchy roleHierarchy = new NullRoleHierarchy();\n\n\tprivate final List<String> requiredAuthorities;\n\n\tprivate final AuthorityAuthorizationDecision defaultDecision;\n\n\t/**\n\t * Creates a new instance.\n\t * @param requiredAuthorities the authorities that are required.\n\t */\n\tprivate AllAuthoritiesReactiveAuthorizationManager(String... requiredAuthorities) {\n\t\tAssert.notEmpty(requiredAuthorities, \"requiredAuthorities cannot be empty\");\n\t\tthis.requiredAuthorities = Arrays.asList(requiredAuthorities);\n\t\tthis.defaultDecision = new AuthorityAuthorizationDecision(false,\n\t\t\t\tAuthorityUtils.createAuthorityList(this.requiredAuthorities));\n\t}\n\n\t/**\n\t * Sets the {@link RoleHierarchy} to be used. Default is {@link NullRoleHierarchy}.\n\t * Cannot be null.\n\t * @param roleHierarchy the {@link RoleHierarchy} to use\n\t */\n\tpublic void setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tAssert.notNull(roleHierarchy, \"roleHierarchy cannot be null\");\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n\t/**\n\t * Determines if the current user is authorized by evaluating if the\n\t * {@link Authentication} contains any of specified authorities.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param object the object to check authorization on (not used).\n\t * @return an {@link AuthorityAuthorizationDecision}\n\t */\n\t@Override\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, T object) {\n\t\t// @formatter:off\n\t\treturn authentication\n\t\t\t.filter(Authentication::isAuthenticated)\n\t\t\t.map(this::getGrantedAuthorities)\n\t\t\t.defaultIfEmpty(Collections.emptyList())\n\t\t\t.map((authenticatedAuthorities) -> {\n\t\t\t\tList<String> missingAuthorities = new ArrayList<>(this.requiredAuthorities);\n\t\t\t\tmissingAuthorities.removeIf(authenticatedAuthorities::contains);\n\t\t\t\treturn new AuthorityAuthorizationDecision(missingAuthorities.isEmpty(),\n\t\t\t\t\t\tAuthorityUtils.createAuthorityList(missingAuthorities));\n\t\t\t});\n\t\t// @formatter:on\n\n\t}\n\n\tprivate List<String> getGrantedAuthorities(Authentication authentication) {\n\t\treturn this.roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities())\n\t\t\t.stream()\n\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t.toList();\n\t}\n\n\t/**\n\t * Creates an instance of {@link AllAuthoritiesReactiveAuthorizationManager} with the\n\t * provided authorities.\n\t * @param roles the authorities to check for prefixed with \"ROLE_\". Each role should\n\t * not start with \"ROLE_\" since it is automatically prepended already.\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AllAuthoritiesReactiveAuthorizationManager<T> hasAllRoles(String... roles) {\n\t\treturn hasAllPrefixedAuthorities(ROLE_PREFIX, roles);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AllAuthoritiesReactiveAuthorizationManager} with the\n\t * provided authorities.\n\t * @param prefix the prefix for <code>authorities</code>\n\t * @param authorities the authorities to check for prefixed with <code>prefix</code>\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AllAuthoritiesReactiveAuthorizationManager<T> hasAllPrefixedAuthorities(String prefix,\n\t\t\tString... authorities) {\n\t\tAssert.notNull(prefix, \"rolePrefix cannot be null\");\n\t\tAssert.notEmpty(authorities, \"roles cannot be empty\");\n\t\tAssert.noNullElements(authorities, \"roles cannot contain null values\");\n\t\treturn hasAllAuthorities(toNamedRolesArray(prefix, authorities));\n\t}\n\n\t/**\n\t * Creates an instance of {@link AllAuthoritiesReactiveAuthorizationManager} with the\n\t * provided authorities.\n\t * @param authorities the authorities to check for\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AllAuthoritiesReactiveAuthorizationManager<T> hasAllAuthorities(String... authorities) {\n\t\tAssert.notEmpty(authorities, \"authorities cannot be empty\");\n\t\tAssert.noNullElements(authorities, \"authorities cannot contain null values\");\n\t\treturn new AllAuthoritiesReactiveAuthorizationManager<>(authorities);\n\t}\n\n\tprivate static String[] toNamedRolesArray(String rolePrefix, String[] roles) {\n\t\tString[] result = new String[roles.length];\n\t\tfor (int i = 0; i < roles.length; i++) {\n\t\t\tString role = roles[i];\n\t\t\tAssert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> role + \" should not start with \"\n\t\t\t\t\t+ rolePrefix + \" since \" + rolePrefix\n\t\t\t\t\t+ \" is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.\");\n\t\t\tresult[i] = rolePrefix + role;\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AllRequiredFactorsAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} that determines if the current user is authorized by\n * evaluating if the {@link Authentication} contains a {@link FactorGrantedAuthority} that\n * is not expired for each {@link RequiredFactor}.\n *\n * @author Rob Winch\n * @since 7.0\n * @see AuthorityAuthorizationManager\n */\npublic final class AllRequiredFactorsAuthorizationManager<T> implements AuthorizationManager<T> {\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\tprivate final List<RequiredFactor> requiredFactors;\n\n\t/**\n\t * Creates a new instance.\n\t * @param requiredFactors the authorities that are required.\n\t */\n\tprivate AllRequiredFactorsAuthorizationManager(List<RequiredFactor> requiredFactors) {\n\t\tAssert.notEmpty(requiredFactors, \"requiredFactors cannot be empty\");\n\t\tAssert.noNullElements(requiredFactors, \"requiredFactors must not contain null elements\");\n\t\tthis.requiredFactors = Collections.unmodifiableList(requiredFactors);\n\t}\n\n\t/**\n\t * Sets the {@link Clock} to use.\n\t * @param clock the {@link Clock} to use. Cannot be null.\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n\t/**\n\t * For each {@link RequiredFactor} finds the first\n\t * {@link FactorGrantedAuthority#getAuthority()} that matches the\n\t * {@link RequiredFactor#getAuthority()}. The\n\t * {@link FactorGrantedAuthority#getIssuedAt()} must be more recent than\n\t * {@link RequiredFactor#getValidDuration()} (if non-null).\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param object the object to check authorization on (not used).\n\t * @return an {@link FactorAuthorizationDecision}\n\t */\n\t@Override\n\tpublic FactorAuthorizationDecision authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tT object) {\n\t\tList<GrantedAuthority> currentFactorAuthorities = getFactorGrantedAuthorities(authentication.get());\n\t\tList<RequiredFactorError> factorErrors = this.requiredFactors.stream()\n\t\t\t.map((factor) -> requiredFactorError(factor, currentFactorAuthorities))\n\t\t\t.filter(Objects::nonNull)\n\t\t\t.toList();\n\t\treturn new FactorAuthorizationDecision(factorErrors);\n\t}\n\n\t/**\n\t * Given the {@link RequiredFactor} and the current {@link FactorGrantedAuthority}\n\t * instances, returns {@link RequiredFactor} or null if granted.\n\t * @param requiredFactor the {@link RequiredFactor} to check.\n\t * @param currentFactors the current user's {@link FactorGrantedAuthority}.\n\t * @return the {@link RequiredFactor} or null if granted.\n\t */\n\tprivate @Nullable RequiredFactorError requiredFactorError(RequiredFactor requiredFactor,\n\t\t\tList<GrantedAuthority> currentFactors) {\n\t\tOptional<GrantedAuthority> matchingAuthority = currentFactors.stream()\n\t\t\t.filter((authority) -> Objects.equals(authority.getAuthority(), requiredFactor.getAuthority()))\n\t\t\t.findFirst();\n\t\tif (!matchingAuthority.isPresent()) {\n\t\t\treturn RequiredFactorError.createMissing(requiredFactor);\n\t\t}\n\t\treturn matchingAuthority.map((authority) -> {\n\t\t\tif (requiredFactor.getValidDuration() == null) {\n\t\t\t\t// granted (only requires authority to match)\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\telse if (authority instanceof FactorGrantedAuthority factorAuthority) {\n\t\t\t\tInstant now = this.clock.instant();\n\t\t\t\tInstant expiresAt = factorAuthority.getIssuedAt().plus(requiredFactor.getValidDuration());\n\t\t\t\tif (now.isBefore(expiresAt)) {\n\t\t\t\t\t// granted\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// denied (expired or no issuedAt to compare)\n\t\t\treturn RequiredFactorError.createExpired(requiredFactor);\n\t\t}).orElse(null);\n\t}\n\n\t/**\n\t * Extracts all of the {@link FactorGrantedAuthority} instances from\n\t * {@link Authentication#getAuthorities()}. If {@link Authentication} is null, or\n\t * {@link Authentication#isAuthenticated()} is false, then an empty {@link List} is\n\t * returned.\n\t * @param authentication the {@link Authentication} (possibly null).\n\t * @return all of the {@link FactorGrantedAuthority} instances from\n\t * {@link Authentication#getAuthorities()}.\n\t */\n\tprivate List<GrantedAuthority> getFactorGrantedAuthorities(@Nullable Authentication authentication) {\n\t\tif (authentication == null || !authentication.isAuthenticated()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\t// @formatter:off\n\t\treturn authentication.getAuthorities().stream()\n\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Creates a new {@link Builder}\n\t * @return\n\t */\n\tpublic static <T> Builder<T> builder() {\n\t\treturn new Builder<>();\n\t}\n\n\t/**\n\t * A builder for {@link AllRequiredFactorsAuthorizationManager}.\n\t *\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tpublic static final class Builder<T> {\n\n\t\tprivate List<RequiredFactor> requiredFactors = new ArrayList<>();\n\n\t\t/**\n\t\t * Allows the user to consume the {@link RequiredFactor.Builder} that is passed in\n\t\t * and then adds the result to the {@link #requireFactor(RequiredFactor)}.\n\t\t * @param requiredFactor the {@link Consumer} to invoke.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder<T> requireFactor(Consumer<RequiredFactor.Builder> requiredFactor) {\n\t\t\tAssert.notNull(requiredFactor, \"requiredFactor cannot be null\");\n\t\t\tRequiredFactor.Builder builder = RequiredFactor.builder();\n\t\t\trequiredFactor.accept(builder);\n\t\t\treturn requireFactor(builder.build());\n\t\t}\n\n\t\t/**\n\t\t * The {@link RequiredFactor} to add.\n\t\t * @param requiredFactor the requiredFactor to add. Cannot be null.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder<T> requireFactor(RequiredFactor requiredFactor) {\n\t\t\tAssert.notNull(requiredFactor, \"requiredFactor cannot be null\");\n\t\t\tthis.requiredFactors.add(requiredFactor);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link AllRequiredFactorsAuthorizationManager}.\n\t\t * @return the {@link AllRequiredFactorsAuthorizationManager}\n\t\t */\n\t\tpublic AllRequiredFactorsAuthorizationManager<T> build() {\n\t\t\tAssert.state(!this.requiredFactors.isEmpty(), \"requiredFactors cannot be empty\");\n\t\t\treturn new AllRequiredFactorsAuthorizationManager<T>(this.requiredFactors);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthenticatedAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} that determines if the current user is authenticated.\n *\n * @param <T> the type of object authorization is being performed against. This does not.\n * @author Evgeniy Cheban\n * @since 5.5\n */\npublic final class AuthenticatedAuthorizationManager<T> implements AuthorizationManager<T> {\n\n\tprivate final AbstractAuthorizationStrategy authorizationStrategy;\n\n\t/**\n\t * Creates an instance that determines if the current user is authenticated, this is\n\t * the same as calling {@link #authenticated()} factory method.\n\t *\n\t * @since 5.8\n\t * @see #authenticated()\n\t * @see #fullyAuthenticated()\n\t * @see #rememberMe()\n\t * @see #anonymous()\n\t */\n\tpublic AuthenticatedAuthorizationManager() {\n\t\tthis(new AuthenticatedAuthorizationStrategy());\n\t}\n\n\tprivate AuthenticatedAuthorizationManager(AbstractAuthorizationStrategy authorizationStrategy) {\n\t\tthis.authorizationStrategy = authorizationStrategy;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. Default is\n\t * {@link AuthenticationTrustResolverImpl}. Cannot be null.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use\n\t * @since 5.8\n\t */\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tthis.authorizationStrategy.setTrustResolver(trustResolver);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthenticatedAuthorizationManager}.\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthenticatedAuthorizationManager<T> authenticated() {\n\t\treturn new AuthenticatedAuthorizationManager<>();\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthenticatedAuthorizationManager} that determines if\n\t * the {@link Authentication} is authenticated without using remember me.\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t * @since 5.8\n\t */\n\tpublic static <T> AuthenticatedAuthorizationManager<T> fullyAuthenticated() {\n\t\treturn new AuthenticatedAuthorizationManager<>(new FullyAuthenticatedAuthorizationStrategy());\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthenticatedAuthorizationManager} that determines if\n\t * the {@link Authentication} is authenticated using remember me.\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t * @since 5.8\n\t */\n\tpublic static <T> AuthenticatedAuthorizationManager<T> rememberMe() {\n\t\treturn new AuthenticatedAuthorizationManager<>(new RememberMeAuthorizationStrategy());\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthenticatedAuthorizationManager} that determines if\n\t * the {@link Authentication} is anonymous.\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t * @since 5.8\n\t */\n\tpublic static <T> AuthenticatedAuthorizationManager<T> anonymous() {\n\t\treturn new AuthenticatedAuthorizationManager<>(new AnonymousAuthorizationStrategy());\n\t}\n\n\t/**\n\t * Determines if the current user is authorized according to the given strategy.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param object the {@link T} object to check\n\t * @return an {@link AuthorizationDecision}\n\t */\n\t@Override\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication, T object) {\n\t\tboolean granted = this.authorizationStrategy.isGranted(authentication.get());\n\t\treturn new AuthorizationDecision(granted);\n\t}\n\n\tprivate abstract static class AbstractAuthorizationStrategy {\n\n\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\t\tprivate void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\t\tthis.trustResolver = trustResolver;\n\t\t}\n\n\t\tabstract boolean isGranted(Authentication authentication);\n\n\t}\n\n\tprivate static class AuthenticatedAuthorizationStrategy extends AbstractAuthorizationStrategy {\n\n\t\t@Override\n\t\tboolean isGranted(Authentication authentication) {\n\t\t\treturn this.trustResolver.isAuthenticated(authentication);\n\t\t}\n\n\t}\n\n\tprivate static final class FullyAuthenticatedAuthorizationStrategy extends AuthenticatedAuthorizationStrategy {\n\n\t\t@Override\n\t\tboolean isGranted(Authentication authentication) {\n\t\t\treturn this.trustResolver.isFullyAuthenticated(authentication);\n\t\t}\n\n\t}\n\n\tprivate static final class AnonymousAuthorizationStrategy extends AbstractAuthorizationStrategy {\n\n\t\t@Override\n\t\tboolean isGranted(Authentication authentication) {\n\t\t\treturn this.trustResolver.isAnonymous(authentication);\n\t\t}\n\n\t}\n\n\tprivate static final class RememberMeAuthorizationStrategy extends AbstractAuthorizationStrategy {\n\n\t\t@Override\n\t\tboolean isGranted(Authentication authentication) {\n\t\t\treturn this.trustResolver.isRememberMe(authentication);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthenticatedReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\n\n/**\n * A {@link ReactiveAuthorizationManager} that determines if the current user is\n * authenticated.\n *\n * @param <T> The type of object authorization is being performed against. This does not\n * matter since the authorization decision does not use the object.\n * @author Rob Winch\n * @since 5.0\n */\npublic class AuthenticatedReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {\n\n\tprivate AuthenticationTrustResolver authTrustResolver = new AuthenticationTrustResolverImpl();\n\n\tAuthenticatedReactiveAuthorizationManager() {\n\t}\n\n\t@Override\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, T object) {\n\t\treturn authentication.filter(this::isNotAnonymous)\n\t\t\t.map(this::getAuthorizationDecision)\n\t\t\t.defaultIfEmpty(new AuthorizationDecision(false));\n\t}\n\n\tprivate AuthorizationResult getAuthorizationDecision(Authentication authentication) {\n\t\treturn new AuthorizationDecision(authentication.isAuthenticated());\n\t}\n\n\t/**\n\t * Verify (via {@link AuthenticationTrustResolver}) that the given authentication is\n\t * not anonymous.\n\t * @param authentication to be checked\n\t * @return <code>true</code> if not anonymous, otherwise <code>false</code>.\n\t */\n\tprivate boolean isNotAnonymous(Authentication authentication) {\n\t\treturn !this.authTrustResolver.isAnonymous(authentication);\n\t}\n\n\t/**\n\t * Gets an instance of {@link AuthenticatedReactiveAuthorizationManager}\n\t * @param <T>\n\t * @return\n\t */\n\tpublic static <T> AuthenticatedReactiveAuthorizationManager<T> authenticated() {\n\t\treturn new AuthenticatedReactiveAuthorizationManager<>();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthoritiesAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Collection;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} that determines if the current user is authorized by\n * evaluating if the {@link Authentication} contains any of the specified authorities.\n *\n * @author Evgeniy Cheban\n * @since 6.1\n */\npublic final class AuthoritiesAuthorizationManager implements AuthorizationManager<Collection<String>> {\n\n\tprivate RoleHierarchy roleHierarchy = new NullRoleHierarchy();\n\n\t/**\n\t * Sets the {@link RoleHierarchy} to be used. Default is {@link NullRoleHierarchy}.\n\t * Cannot be null.\n\t * @param roleHierarchy the {@link RoleHierarchy} to use\n\t */\n\tpublic void setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tAssert.notNull(roleHierarchy, \"roleHierarchy cannot be null\");\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n\t/**\n\t * Determines if the current user is authorized by evaluating if the\n\t * {@link Authentication} contains any of specified authorities.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param authorities the collection of authority strings to check\n\t * @return an {@link AuthorityAuthorizationDecision}\n\t */\n\t@Override\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tCollection<String> authorities) {\n\t\tboolean granted = isGranted(authentication.get(), authorities);\n\t\treturn new AuthorityAuthorizationDecision(granted, AuthorityUtils.createAuthorityList(authorities));\n\t}\n\n\tprivate boolean isGranted(Authentication authentication, Collection<String> authorities) {\n\t\treturn authentication != null && isAuthorized(authentication, authorities);\n\t}\n\n\tprivate boolean isAuthorized(Authentication authentication, Collection<String> authorities) {\n\t\tfor (GrantedAuthority grantedAuthority : getGrantedAuthorities(authentication)) {\n\t\t\tString authority = grantedAuthority.getAuthority();\n\t\t\tif (authority == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (authorities.contains(authority)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate Collection<? extends GrantedAuthority> getGrantedAuthorities(Authentication authentication) {\n\t\treturn this.roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationDecision.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.io.Serial;\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Represents an {@link AuthorizationDecision} based on a collection of authorities\n *\n * @author Marcus Da Coregio\n * @since 5.6\n */\npublic class AuthorityAuthorizationDecision extends AuthorizationDecision {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -8338309042331376592L;\n\n\tprivate final Collection<GrantedAuthority> authorities;\n\n\tpublic AuthorityAuthorizationDecision(boolean granted, Collection<GrantedAuthority> authorities) {\n\t\tsuper(granted);\n\t\tthis.authorities = authorities;\n\t}\n\n\tpublic Collection<GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \" [\" + \"granted=\" + isGranted() + \", authorities=\" + this.authorities + ']';\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} that determines if the current user is authorized by\n * evaluating if the {@link Authentication} contains a specified authority.\n *\n * @param <T> the type of object being authorized.\n * @author Evgeniy Cheban\n * @since 5.5\n * @see AllAuthoritiesAuthorizationManager\n */\npublic final class AuthorityAuthorizationManager<T> implements AuthorizationManager<T> {\n\n\tprivate static final String ROLE_PREFIX = \"ROLE_\";\n\n\tprivate final AuthoritiesAuthorizationManager delegate = new AuthoritiesAuthorizationManager();\n\n\tprivate final Set<String> authorities;\n\n\tprivate AuthorityAuthorizationManager(String... authorities) {\n\t\tthis.authorities = Set.of(authorities);\n\t}\n\n\t/**\n\t * Sets the {@link RoleHierarchy} to be used. Default is {@link NullRoleHierarchy}.\n\t * Cannot be null.\n\t * @param roleHierarchy the {@link RoleHierarchy} to use\n\t * @since 5.8\n\t */\n\tpublic void setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tthis.delegate.setRoleHierarchy(roleHierarchy);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityAuthorizationManager} with the provided\n\t * authority.\n\t * @param role the authority to check for prefixed with \"ROLE_\". Role should not start\n\t * with \"ROLE_\" since it is automatically prepended already.\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityAuthorizationManager<T> hasRole(String role) {\n\t\tAssert.notNull(role, \"role cannot be null\");\n\t\tAssert.isTrue(!role.startsWith(ROLE_PREFIX), () -> role + \" should not start with \" + ROLE_PREFIX + \" since \"\n\t\t\t\t+ ROLE_PREFIX + \" is automatically prepended when using hasRole. Consider using hasAuthority instead.\");\n\t\treturn hasAuthority(ROLE_PREFIX + role);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityAuthorizationManager} with the provided\n\t * authority.\n\t * @param authority the authority to check for\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityAuthorizationManager<T> hasAuthority(String authority) {\n\t\tAssert.notNull(authority, \"authority cannot be null\");\n\t\treturn new AuthorityAuthorizationManager<>(authority);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityAuthorizationManager} with the provided\n\t * authorities.\n\t * @param roles the authorities to check for prefixed with \"ROLE_\". Each role should\n\t * not start with \"ROLE_\" since it is automatically prepended already.\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityAuthorizationManager<T> hasAnyRole(String... roles) {\n\t\treturn hasAnyRole(ROLE_PREFIX, roles);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityAuthorizationManager} with the provided\n\t * authorities.\n\t * @param rolePrefix the role prefix for <code>roles</code>\n\t * @param roles the authorities to check for prefixed with <code>rolePrefix</code>\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityAuthorizationManager<T> hasAnyRole(String rolePrefix, String[] roles) {\n\t\tAssert.notNull(rolePrefix, \"rolePrefix cannot be null\");\n\t\tAssert.notEmpty(roles, \"roles cannot be empty\");\n\t\tAssert.noNullElements(roles, \"roles cannot contain null values\");\n\t\treturn hasAnyAuthority(toNamedRolesArray(rolePrefix, roles));\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityAuthorizationManager} with the provided\n\t * authorities.\n\t * @param authorities the authorities to check for\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityAuthorizationManager<T> hasAnyAuthority(String... authorities) {\n\t\tAssert.notEmpty(authorities, \"authorities cannot be empty\");\n\t\tAssert.noNullElements(authorities, \"authorities cannot contain null values\");\n\t\treturn new AuthorityAuthorizationManager<>(authorities);\n\t}\n\n\tprivate static String[] toNamedRolesArray(String rolePrefix, String[] roles) {\n\t\tString[] result = new String[roles.length];\n\t\tfor (int i = 0; i < roles.length; i++) {\n\t\t\tString role = roles[i];\n\t\t\tAssert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> role + \" should not start with \"\n\t\t\t\t\t+ rolePrefix + \" since \" + rolePrefix\n\t\t\t\t\t+ \" is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.\");\n\t\t\tresult[i] = rolePrefix + role;\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication, T object) {\n\t\treturn this.delegate.authorize(authentication, this.authorities);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AuthorityAuthorizationManager[authorities=\" + this.authorities + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthorizationManager} that determines if the current user is\n * authorized by evaluating if the {@link Authentication} contains a specified authority.\n *\n * @param <T> the type of object being authorized\n * @author Rob Winch\n * @author Robbie Martinus\n * @since 5.0\n */\npublic class AuthorityReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {\n\n\tprivate final List<GrantedAuthority> authorities;\n\n\tAuthorityReactiveAuthorizationManager(String... authorities) {\n\t\tthis.authorities = AuthorityUtils.createAuthorityList(authorities);\n\t}\n\n\t@Override\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, T object) {\n\t\t// @formatter:off\n\t\treturn authentication.filter(Authentication::isAuthenticated)\n\t\t\t\t.flatMapIterable(Authentication::getAuthorities)\n\t\t\t\t.mapNotNull((Function<GrantedAuthority, @Nullable String>) GrantedAuthority::getAuthority)\n\t\t\t\t.any((grantedAuthority) -> this.authorities.stream().anyMatch((authority) -> Objects.equals(authority.getAuthority(), grantedAuthority)))\n\t\t\t\t.map((granted) -> ((AuthorizationResult) new AuthorityAuthorizationDecision(granted, this.authorities)))\n\t\t\t\t.defaultIfEmpty(new AuthorityAuthorizationDecision(false, this.authorities));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityReactiveAuthorizationManager} with the\n\t * provided authority.\n\t * @param authority the authority to check for\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityReactiveAuthorizationManager<T> hasAuthority(String authority) {\n\t\tAssert.notNull(authority, \"authority cannot be null\");\n\t\treturn new AuthorityReactiveAuthorizationManager<>(authority);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityReactiveAuthorizationManager} with the\n\t * provided authorities.\n\t * @param authorities the authorities to check for\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityReactiveAuthorizationManager<T> hasAnyAuthority(String... authorities) {\n\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\tfor (String authority : authorities) {\n\t\t\tAssert.notNull(authority, \"authority cannot be null\");\n\t\t}\n\t\treturn new AuthorityReactiveAuthorizationManager<>(authorities);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityReactiveAuthorizationManager} with the\n\t * provided authority.\n\t * @param role the authority to check for prefixed with \"ROLE_\"\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityReactiveAuthorizationManager<T> hasRole(String role) {\n\t\tAssert.notNull(role, \"role cannot be null\");\n\t\treturn hasAuthority(\"ROLE_\" + role);\n\t}\n\n\t/**\n\t * Creates an instance of {@link AuthorityReactiveAuthorizationManager} with the\n\t * provided authorities.\n\t * @param roles the authorities to check for prefixed with \"ROLE_\"\n\t * @param <T> the type of object being authorized\n\t * @return the new instance\n\t */\n\tpublic static <T> AuthorityReactiveAuthorizationManager<T> hasAnyRole(String... roles) {\n\t\tAssert.notNull(roles, \"roles cannot be null\");\n\t\tfor (String role : roles) {\n\t\t\tAssert.notNull(role, \"role cannot be null\");\n\t\t}\n\t\treturn hasAnyAuthority(toNamedRolesArray(roles));\n\t}\n\n\tprivate static String[] toNamedRolesArray(String... roles) {\n\t\tString[] result = new String[roles.length];\n\t\tfor (int i = 0; i < roles.length; i++) {\n\t\t\tresult[i] = \"ROLE_\" + roles[i];\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationDecision.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.io.Serial;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class AuthorizationDecision implements AuthorizationResult {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3226018324649244416L;\n\n\tprivate final boolean granted;\n\n\tpublic AuthorizationDecision(boolean granted) {\n\t\tthis.granted = granted;\n\t}\n\n\t@Override\n\tpublic boolean isGranted() {\n\t\treturn this.granted;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \" [granted=\" + this.granted + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationDeniedException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.io.Serial;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AccessDeniedException} that contains the {@link AuthorizationResult}\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic class AuthorizationDeniedException extends AccessDeniedException implements AuthorizationResult {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 3227305845919610459L;\n\n\tprivate final AuthorizationResult result;\n\n\tpublic AuthorizationDeniedException(String msg, AuthorizationResult authorizationResult) {\n\t\tsuper(msg);\n\t\tAssert.notNull(authorizationResult, \"authorizationResult cannot be null\");\n\t\tAssert.isTrue(!authorizationResult.isGranted(), \"Granted authorization results are not supported\");\n\t\tthis.result = authorizationResult;\n\t}\n\n\tpublic AuthorizationDeniedException(String msg) {\n\t\tthis(msg, new AuthorizationDecision(false));\n\t}\n\n\tpublic AuthorizationResult getAuthorizationResult() {\n\t\treturn this.result;\n\t}\n\n\t@Override\n\tpublic boolean isGranted() {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.event.AuthorizationDeniedEvent;\nimport org.springframework.security.authorization.event.AuthorizationGrantedEvent;\nimport org.springframework.security.core.Authentication;\n\n/**\n * A contract for publishing authorization events\n *\n * @author Parikshit Dutta\n * @author Josh Cummings\n * @since 5.7\n * @see AuthorizationManager\n */\n@FunctionalInterface\npublic interface AuthorizationEventPublisher {\n\n\t/**\n\t * Publish the given details in the form of an event, typically\n\t * {@link AuthorizationGrantedEvent} or {@link AuthorizationDeniedEvent}.\n\t *\n\t * Note that success events can be very noisy if enabled by default. Because of this\n\t * implementations may choose to drop success events by default.\n\t * @param authentication a {@link Supplier} for the current user\n\t * @param object the secured object\n\t * @param result {@link AuthorizationResult} the result about whether the user may\n\t * access the secured object\n\t * @param <T> the secured object's type\n\t * @since 6.4\n\t */\n\t<T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,\n\t\t\t@Nullable AuthorizationResult result);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.core.Authentication;\n\n/**\n * An Authorization manager which can determine if an {@link Authentication} has access to\n * a specific object.\n *\n * @param <T> the type of object that the authorization check is being done on.\n * @author Evgeniy Cheban\n */\n@FunctionalInterface\npublic interface AuthorizationManager<T extends @Nullable Object> {\n\n\t/**\n\t * Determines if access should be granted for a specific authentication and object.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param object the {@link T} object to check\n\t * @throws AccessDeniedException if access is not granted\n\t */\n\tdefault void verify(Supplier<? extends @Nullable Authentication> authentication, T object) {\n\t\tAuthorizationResult result = authorize(authentication, object);\n\t\tif (result != null && !result.isGranted()) {\n\t\t\tthrow new AuthorizationDeniedException(\"Access Denied\", result);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if access is granted for a specific authentication and object.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to\n\t * authorize\n\t * @param object the {@link T} object to authorize\n\t * @return an {@link AuthorizationResult}\n\t * @since 6.4\n\t */\n\t@Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication, T object);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationManagerFactories.java",
    "content": "/*\n * Copyright 2002-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Creates common {@link AuthorizationManagerFactory} instances.\n *\n * @author Rob Winch\n * @since 7.0\n * @see DefaultAuthorizationManagerFactory\n */\npublic final class AuthorizationManagerFactories {\n\n\tprivate AuthorizationManagerFactories() {\n\t}\n\n\t/**\n\t * Creates a {@link AdditionalRequiredFactorsBuilder} that helps build an\n\t * {@link AuthorizationManager} to set on\n\t * {@link DefaultAuthorizationManagerFactory#setAdditionalAuthorization(AuthorizationManager)}\n\t * for multifactor authentication.\n\t * <p>\n\t * Does not affect {@code anonymous}, {@code permitAll}, or {@code denyAll}.\n\t * @param <T> the secured object type\n\t * @return a factory configured with the required authorities\n\t */\n\tpublic static <T> AdditionalRequiredFactorsBuilder<T> multiFactor() {\n\t\treturn new AdditionalRequiredFactorsBuilder<>();\n\t}\n\n\t/**\n\t * A builder that allows creating {@link DefaultAuthorizationManagerFactory} with\n\t * additional requirements for {@link RequiredFactor}s.\n\t *\n\t * @param <T> the type for the {@link DefaultAuthorizationManagerFactory}\n\t * @author Rob Winch\n\t */\n\tpublic static final class AdditionalRequiredFactorsBuilder<T> {\n\n\t\tprivate final AllRequiredFactorsAuthorizationManager.Builder<T> factors = AllRequiredFactorsAuthorizationManager\n\t\t\t.builder();\n\n\t\tprivate @Nullable Predicate<Authentication> whenCondition;\n\n\t\t/**\n\t\t * Apply the required factors only when the given condition is true for the\n\t\t * current {@link Authentication}. When the condition is false, no additional\n\t\t * factors are required (equivalent to permit-all for the additional\n\t\t * authorization). Implemented using\n\t\t * {@link ConditionalAuthorizationManager#when(java.util.function.Predicate)}.\n\t\t * @param condition the condition to evaluate (must not be null)\n\t\t * @return the {@link AdditionalRequiredFactorsBuilder} to further customize\n\t\t * @since 7.1\n\t\t */\n\t\tpublic AdditionalRequiredFactorsBuilder<T> when(Predicate<Authentication> condition) {\n\t\t\tAssert.notNull(condition, \"condition cannot be null\");\n\t\t\tthis.whenCondition = condition;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Customize the condition that determines if the required factors are evaluated.\n\t\t * @param condition a function that takes the current condition and returns the\n\t\t * new condition\n\t\t * @return the {@link AdditionalRequiredFactorsBuilder} to further customize\n\t\t * @since 7.1\n\t\t */\n\t\tpublic AdditionalRequiredFactorsBuilder<T> withWhen(\n\t\t\t\tFunction<@Nullable Predicate<Authentication>, @Nullable Predicate<Authentication>> condition) {\n\t\t\tAssert.notNull(condition, \"condition cannot be null\");\n\t\t\tthis.whenCondition = condition.apply(this.whenCondition);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add additional authorities that will be required.\n\t\t * @param additionalAuthorities the additional authorities.\n\t\t * @return the {@link AdditionalRequiredFactorsBuilder} to further customize.\n\t\t */\n\t\tpublic AdditionalRequiredFactorsBuilder<T> requireFactors(String... additionalAuthorities) {\n\t\t\trequireFactors((factors) -> {\n\t\t\t\tfor (String authority : additionalAuthorities) {\n\t\t\t\t\tfactors.requireFactor((factor) -> factor.authority(authority));\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic AdditionalRequiredFactorsBuilder<T> requireFactors(\n\t\t\t\tConsumer<AllRequiredFactorsAuthorizationManager.Builder<T>> factors) {\n\t\t\tfactors.accept(this.factors);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic AdditionalRequiredFactorsBuilder<T> requireFactor(Consumer<RequiredFactor.Builder> factor) {\n\t\t\tthis.factors.requireFactor(factor);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a {@link DefaultAuthorizationManagerFactory} that has the\n\t\t * {@link DefaultAuthorizationManagerFactory#setAdditionalAuthorization(AuthorizationManager)}\n\t\t * set.\n\t\t * @return the {@link DefaultAuthorizationManagerFactory}.\n\t\t */\n\t\tpublic DefaultAuthorizationManagerFactory<T> build() {\n\t\t\tDefaultAuthorizationManagerFactory<T> result = new DefaultAuthorizationManagerFactory<>();\n\t\t\tAuthorizationManager<T> additionalChecks = this.factors.build();\n\t\t\tif (this.whenCondition != null) {\n\t\t\t\tadditionalChecks = ConditionalAuthorizationManager.<T>when(this.whenCondition)\n\t\t\t\t\t.whenTrue(additionalChecks)\n\t\t\t\t\t.build();\n\t\t\t}\n\t\t\tresult.setAdditionalAuthorization(additionalChecks);\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate AdditionalRequiredFactorsBuilder() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationManagerFactory.java",
    "content": "/*\n * Copyright 2002-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A factory for creating different kinds of {@link AuthorizationManager} instances.\n *\n * @param <T> the type of object that the authorization check is being done on\n * @author Steve Riesenberg\n * @since 7.0\n */\npublic interface AuthorizationManagerFactory<T extends @Nullable Object> {\n\n\t/**\n\t * Create an {@link AuthorizationManager} that allows anyone.\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> permitAll() {\n\t\treturn SingleResultAuthorizationManager.permitAll();\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that does not allow anyone.\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> denyAll() {\n\t\treturn SingleResultAuthorizationManager.denyAll();\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that requires users to have the specified\n\t * role.\n\t * @param role the role (automatically prepended with ROLE_) that should be required\n\t * to allow access (i.e. USER, ADMIN, etc.)\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> hasRole(String role) {\n\t\treturn AuthorityAuthorizationManager.hasRole(role);\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that requires users to have one of many\n\t * roles.\n\t * @param roles the roles (automatically prepended with ROLE_) that the user should\n\t * have at least one of to allow access (i.e. USER, ADMIN, etc.)\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> hasAnyRole(String... roles) {\n\t\treturn AuthorityAuthorizationManager.hasAnyRole(roles);\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that requires users to have all the\n\t * provided roles.\n\t * @param roles the roles (automatically prepended with ROLE_) that the user must have\n\t * to allow access (i.e. USER, ADMIN, etc.)\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> hasAllRoles(String... roles) {\n\t\treturn AllAuthoritiesAuthorizationManager.hasAllRoles(roles);\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that requires users to have the specified\n\t * authority.\n\t * @param authority the authority that should be required to allow access (i.e.\n\t * ROLE_USER, ROLE_ADMIN, etc.)\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> hasAuthority(String authority) {\n\t\treturn AuthorityAuthorizationManager.hasAuthority(authority);\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that requires users to have one of many\n\t * authorities.\n\t * @param authorities the authorities that the user should have at least one of to\n\t * allow access (i.e. ROLE_USER, ROLE_ADMIN, etc.)\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> hasAnyAuthority(String... authorities) {\n\t\treturn AuthorityAuthorizationManager.hasAnyAuthority(authorities);\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that requires users to have all the\n\t * provided authorities.\n\t * @param authorities the authorities that the user must have to allow access (i.e.\n\t * USER, ADMIN, etc.)\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> hasAllAuthorities(String... authorities) {\n\t\treturn AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities);\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that allows any authenticated user.\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> authenticated() {\n\t\treturn AuthenticatedAuthorizationManager.authenticated();\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that allows users who have authenticated\n\t * and were not remembered.\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> fullyAuthenticated() {\n\t\treturn AuthenticatedAuthorizationManager.fullyAuthenticated();\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that allows users that have been\n\t * remembered.\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> rememberMe() {\n\t\treturn AuthenticatedAuthorizationManager.rememberMe();\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that allows only anonymous users.\n\t * @return A new {@link AuthorizationManager} instance\n\t */\n\tdefault AuthorizationManager<T> anonymous() {\n\t\treturn AuthenticatedAuthorizationManager.anonymous();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * A factory class to create an {@link AuthorizationManager} instances.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.8\n */\npublic final class AuthorizationManagers {\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that grants access if at least one\n\t * {@link AuthorizationManager} granted or abstained, if <code>managers</code> are\n\t * empty then denied decision is returned.\n\t * @param <T> the type of object that is being authorized\n\t * @param managers the {@link AuthorizationManager}s to use\n\t * @return the {@link AuthorizationManager} to use\n\t */\n\t@SafeVarargs\n\tpublic static <T> AuthorizationManager<T> anyOf(AuthorizationManager<T>... managers) {\n\t\treturn anyOf(new AuthorizationDecision(false), managers);\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that grants access if at least one\n\t * {@link AuthorizationManager} granted, if <code>managers</code> are empty or\n\t * abstained, a default {@link AuthorizationDecision} is returned.\n\t * @param <T> the type of object that is being authorized\n\t * @param allAbstainDefaultDecision the default decision if all\n\t * {@link AuthorizationManager}s abstained\n\t * @param managers the {@link AuthorizationManager}s to use\n\t * @return the {@link AuthorizationManager} to use\n\t * @since 6.3\n\t */\n\t@SafeVarargs\n\tpublic static <T> AuthorizationManager<T> anyOf(AuthorizationDecision allAbstainDefaultDecision,\n\t\t\tAuthorizationManager<T>... managers) {\n\t\treturn (AuthorizationManagerCheckAdapter<T>) (Supplier<? extends @Nullable Authentication> authentication,\n\t\t\t\tT object) -> {\n\t\t\tList<AuthorizationResult> results = new ArrayList<>();\n\t\t\tfor (AuthorizationManager<T> manager : managers) {\n\t\t\t\tAuthorizationResult result = manager.authorize(authentication, object);\n\t\t\t\tif (result == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (result.isGranted()) {\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t\tresults.add(result);\n\t\t\t}\n\t\t\tif (results.isEmpty()) {\n\t\t\t\treturn allAbstainDefaultDecision;\n\t\t\t}\n\t\t\treturn new CompositeAuthorizationDecision(false, results);\n\t\t};\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that grants access if all\n\t * {@link AuthorizationManager}s granted or abstained, if <code>managers</code> are\n\t * empty then granted decision is returned.\n\t * @param <T> the type of object that is being authorized\n\t * @param managers the {@link AuthorizationManager}s to use\n\t * @return the {@link AuthorizationManager} to use\n\t */\n\t@SafeVarargs\n\tpublic static <T> AuthorizationManager<T> allOf(AuthorizationManager<T>... managers) {\n\t\treturn allOf(new AuthorizationDecision(true), managers);\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that grants access if all\n\t * {@link AuthorizationManager}s granted, if <code>managers</code> are empty or\n\t * abstained, a default {@link AuthorizationDecision} is returned.\n\t * @param <T> the type of object that is being authorized\n\t * @param allAbstainDefaultDecision the default decision if all\n\t * {@link AuthorizationManager}s abstained\n\t * @param managers the {@link AuthorizationManager}s to use\n\t * @return the {@link AuthorizationManager} to use\n\t * @since 6.3\n\t */\n\t@SafeVarargs\n\tpublic static <T> AuthorizationManager<T> allOf(AuthorizationDecision allAbstainDefaultDecision,\n\t\t\tAuthorizationManager<T>... managers) {\n\t\treturn (AuthorizationManagerCheckAdapter<T>) (Supplier<? extends @Nullable Authentication> authentication,\n\t\t\t\tT object) -> {\n\t\t\tList<AuthorizationResult> results = new ArrayList<>();\n\t\t\tfor (AuthorizationManager<T> manager : managers) {\n\t\t\t\tAuthorizationResult result = manager.authorize(authentication, object);\n\t\t\t\tif (result == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!result.isGranted()) {\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t\tresults.add(result);\n\t\t\t}\n\t\t\tif (results.isEmpty()) {\n\t\t\t\treturn allAbstainDefaultDecision;\n\t\t\t}\n\t\t\treturn new CompositeAuthorizationDecision(true, results);\n\t\t};\n\t}\n\n\t/**\n\t * Creates an {@link AuthorizationManager} that reverses whatever decision the given\n\t * {@link AuthorizationManager} granted. If the given {@link AuthorizationManager}\n\t * abstains, then the returned manager also abstains.\n\t * @param <T> the type of object that is being authorized\n\t * @param manager the {@link AuthorizationManager} to reverse\n\t * @return the reversing {@link AuthorizationManager}\n\t * @since 6.3\n\t */\n\tpublic static <T> AuthorizationManager<T> not(AuthorizationManager<T> manager) {\n\t\treturn (Supplier<? extends @Nullable Authentication> authentication, T object) -> {\n\t\t\tAuthorizationResult result = manager.authorize(authentication, object);\n\t\t\tif (result == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new NotAuthorizationDecision(result);\n\t\t};\n\t}\n\n\tprivate AuthorizationManagers() {\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tprivate static final class CompositeAuthorizationDecision extends AuthorizationDecision {\n\n\t\tprivate final List<AuthorizationResult> results;\n\n\t\tprivate CompositeAuthorizationDecision(boolean granted, List<AuthorizationResult> results) {\n\t\t\tsuper(granted);\n\t\t\tthis.results = results;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"CompositeAuthorizationDecision [results=\" + this.results + ']';\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tprivate static final class NotAuthorizationDecision extends AuthorizationDecision {\n\n\t\tprivate final AuthorizationResult result;\n\n\t\tprivate NotAuthorizationDecision(AuthorizationResult result) {\n\t\t\tsuper(!result.isGranted());\n\t\t\tthis.result = result;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"NotAuthorizationDecision [result=\" + this.result + ']';\n\t\t}\n\n\t}\n\n\tprivate interface AuthorizationManagerCheckAdapter<T> extends AuthorizationManager<T> {\n\n\t\t@Override\n\t\tAuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication, T object);\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationObservationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport io.micrometer.observation.Observation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Observation.Context} used during authorizations\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic class AuthorizationObservationContext<T> extends Observation.Context {\n\n\t// FIXME: Should we make this non-null?\n\tprivate @Nullable Authentication authentication;\n\n\tprivate final T object;\n\n\t// FIXME: Should we make this non-null?\n\tprivate @Nullable AuthorizationResult authorizationResult;\n\n\tpublic AuthorizationObservationContext(T object) {\n\t\tAssert.notNull(object, \"object cannot be null\");\n\t\tthis.object = object;\n\t}\n\n\t/**\n\t * Get the observed {@link Authentication} for this authorization\n\t *\n\t * <p>\n\t * Note that if the authorization did not require inspecting the\n\t * {@link Authentication}, this will return {@code null}.\n\t * @return any observed {@link Authentication}, {@code null} otherwise\n\t */\n\tpublic @Nullable Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n\t/**\n\t * Set the observed {@link Authentication} for this authorization\n\t * @param authentication the observed {@link Authentication}\n\t */\n\tpublic void setAuthentication(Authentication authentication) {\n\t\tthis.authentication = authentication;\n\t}\n\n\t/**\n\t * Get the object for which access was requested\n\t * @return the requested object\n\t */\n\tpublic T getObject() {\n\t\treturn this.object;\n\t}\n\n\t/**\n\t * Get the observed {@link AuthorizationResult}\n\t * @return the observed {@link AuthorizationResult}\n\t * @since 6.4\n\t */\n\tpublic @Nullable AuthorizationResult getAuthorizationResult() {\n\t\treturn this.authorizationResult;\n\t}\n\n\t/**\n\t * Set the observed {@link AuthorizationResult}\n\t * @param authorizationResult the observed {@link AuthorizationResult}\n\t * @since 6.4\n\t */\n\tpublic void setAuthorizationResult(@Nullable AuthorizationResult authorizationResult) {\n\t\tthis.authorizationResult = authorizationResult;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationObservationConvention.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport io.micrometer.common.KeyValues;\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationConvention;\nimport org.aopalliance.intercept.MethodInvocation;\n\n/**\n * An {@link ObservationConvention} for translating authorizations into {@link KeyValues}.\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic final class AuthorizationObservationConvention\n\t\timplements ObservationConvention<AuthorizationObservationContext<?>> {\n\n\tstatic final String OBSERVATION_NAME = \"spring.security.authorizations\";\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn OBSERVATION_NAME;\n\t}\n\n\t@Override\n\tpublic String getContextualName(AuthorizationObservationContext<?> context) {\n\t\treturn \"authorize \" + getObjectType(context);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic KeyValues getLowCardinalityKeyValues(AuthorizationObservationContext<?> context) {\n\t\treturn KeyValues.of(\"spring.security.authentication.type\", getAuthenticationType(context))\n\t\t\t.and(\"spring.security.object\", getObjectType(context))\n\t\t\t.and(\"spring.security.authorization.decision\", getAuthorizationDecision(context));\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic KeyValues getHighCardinalityKeyValues(AuthorizationObservationContext<?> context) {\n\t\treturn KeyValues.of(\"spring.security.authentication.authorities\", getAuthorities(context))\n\t\t\t.and(\"spring.security.authorization.decision.details\", getDecisionDetails(context));\n\t}\n\n\t@Override\n\tpublic boolean supportsContext(Observation.Context context) {\n\t\treturn context instanceof AuthorizationObservationContext<?>;\n\t}\n\n\tprivate String getAuthenticationType(AuthorizationObservationContext<?> context) {\n\t\tif (context.getAuthentication() == null) {\n\t\t\treturn \"n/a\";\n\t\t}\n\t\treturn context.getAuthentication().getClass().getSimpleName();\n\t}\n\n\tprivate String getObjectType(AuthorizationObservationContext<?> context) {\n\t\tif (context.getObject() == null) {\n\t\t\treturn \"unknown\";\n\t\t}\n\t\tif (context.getObject() instanceof MethodInvocation) {\n\t\t\treturn \"method\";\n\t\t}\n\t\tString className = context.getObject().getClass().getSimpleName();\n\t\tif (className.contains(\"Method\")) {\n\t\t\treturn \"method\";\n\t\t}\n\t\tif (className.contains(\"Request\")) {\n\t\t\treturn \"request\";\n\t\t}\n\t\tif (className.contains(\"Message\")) {\n\t\t\treturn \"message\";\n\t\t}\n\t\tif (className.contains(\"Exchange\")) {\n\t\t\treturn \"exchange\";\n\t\t}\n\t\treturn className;\n\t}\n\n\tprivate String getAuthorizationDecision(AuthorizationObservationContext<?> context) {\n\t\tif (context.getAuthorizationResult() == null) {\n\t\t\treturn \"unknown\";\n\t\t}\n\t\treturn String.valueOf(context.getAuthorizationResult().isGranted());\n\t}\n\n\tprivate String getAuthorities(AuthorizationObservationContext<?> context) {\n\t\tif (context.getAuthentication() == null) {\n\t\t\treturn \"n/a\";\n\t\t}\n\t\treturn String.valueOf(context.getAuthentication().getAuthorities());\n\t}\n\n\tprivate String getDecisionDetails(AuthorizationObservationContext<?> context) {\n\t\tif (context.getAuthorizationResult() == null) {\n\t\t\treturn \"unknown\";\n\t\t}\n\t\tAuthorizationResult decision = context.getAuthorizationResult();\n\t\treturn String.valueOf(decision);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationProxyFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A factory for wrapping arbitrary objects in authorization-related advice\n *\n * @author Josh Cummings\n * @author daewon kim\n * @since 6.3\n * @see org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory\n */\npublic interface AuthorizationProxyFactory {\n\n\t/**\n\t * Wrap the given {@code object} in authorization-related advice.\n\t *\n\t * <p>\n\t * Please check the implementation for which kinds of objects it supports.\n\t * @param <T> the type of the object being proxied\n\t * @param object the object to proxy\n\t * @return the proxied object\n\t * @throws org.springframework.aop.framework.AopConfigException if a proxy cannot be\n\t * created\n\t */\n\t<T> @Nullable T proxy(@Nullable T object);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/AuthorizationResult.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.io.Serializable;\n\n/**\n * Represents an authorization result\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic interface AuthorizationResult extends Serializable {\n\n\t/**\n\t * @return whether the access has been granted\n\t */\n\tboolean isGranted();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/ConditionalAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} that delegates to one of two\n * {@link AuthorizationManager} instances based on a condition evaluated against the\n * current {@link Authentication}.\n * <p>\n * When {@link #authorize(Supplier, Object)} is invoked, the condition is evaluated. If\n * the {@link Authentication} is non-null and the condition returns {@code true}, the\n * {@code whenTrue} manager is used; otherwise the {@code whenFalse} manager is used.\n * <p>\n * This is useful for scenarios such as requiring multi-factor authentication only when\n * the user has registered a second factor, or applying different rules based on\n * authentication state.\n *\n * @param <T> the type of object that the authorization check is being performed on\n * @author Rob Winch\n * @since 7.1\n */\npublic final class ConditionalAuthorizationManager<T> implements AuthorizationManager<T> {\n\n\tprivate final Predicate<Authentication> condition;\n\n\tprivate final AuthorizationManager<T> whenTrue;\n\n\tprivate final AuthorizationManager<T> whenFalse;\n\n\t/**\n\t * Creates a {@link ConditionalAuthorizationManager} that delegates to\n\t * {@code whenTrue} when the condition holds for the current {@link Authentication},\n\t * and to {@code whenFalse} otherwise.\n\t * @param condition the condition to evaluate against the {@link Authentication} (must\n\t * not be null)\n\t * @param whenTrue the manager to use when the condition is true (must not be null)\n\t * @param whenFalse the manager to use when the condition is false (must not be null)\n\t */\n\tprivate ConditionalAuthorizationManager(Predicate<Authentication> condition, AuthorizationManager<T> whenTrue,\n\t\t\tAuthorizationManager<T> whenFalse) {\n\t\tAssert.notNull(condition, \"condition cannot be null\");\n\t\tAssert.notNull(whenTrue, \"whenTrue cannot be null\");\n\t\tAssert.notNull(whenFalse, \"whenFalse cannot be null\");\n\t\tthis.condition = condition;\n\t\tthis.whenTrue = whenTrue;\n\t\tthis.whenFalse = whenFalse;\n\t}\n\n\t/**\n\t * Creates a builder for a {@link ConditionalAuthorizationManager} with the given\n\t * condition.\n\t * @param <T> the type of object that the authorization check is being performed on\n\t * @param condition the condition to evaluate against the {@link Authentication} (must\n\t * not be null)\n\t * @return the builder\n\t */\n\tpublic static <T> Builder<T> when(Predicate<Authentication> condition) {\n\t\tAssert.notNull(condition, \"condition cannot be null\");\n\t\treturn new Builder<>(condition);\n\t}\n\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tT object) {\n\t\tAuthentication auth = authentication.get();\n\t\tif (auth != null && this.condition.test(auth)) {\n\t\t\treturn this.whenTrue.authorize(authentication, object);\n\t\t}\n\t\treturn this.whenFalse.authorize(authentication, object);\n\t}\n\n\t/**\n\t * A builder for {@link ConditionalAuthorizationManager}.\n\t *\n\t * @param <T> the type of object that the authorization check is being performed on\n\t * @author Rob Winch\n\t * @since 7.1\n\t */\n\tpublic static final class Builder<T> {\n\n\t\tprivate final Predicate<Authentication> condition;\n\n\t\tprivate @Nullable AuthorizationManager<T> whenTrue;\n\n\t\tprivate @Nullable AuthorizationManager<T> whenFalse;\n\n\t\tprivate Builder(Predicate<Authentication> condition) {\n\t\t\tthis.condition = condition;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link AuthorizationManager} to use when the condition is true.\n\t\t * @param whenTrue the manager to use when the condition is true (must not be\n\t\t * null)\n\t\t * @return the builder\n\t\t */\n\t\tpublic Builder<T> whenTrue(AuthorizationManager<T> whenTrue) {\n\t\t\tAssert.notNull(whenTrue, \"whenTrue cannot be null\");\n\t\t\tthis.whenTrue = whenTrue;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link AuthorizationManager} to use when the condition is false.\n\t\t * Defaults to {@link SingleResultAuthorizationManager#permitAll()} if not set.\n\t\t * @param whenFalse the manager to use when the condition is false (must not be\n\t\t * null)\n\t\t * @return the builder\n\t\t */\n\t\tpublic Builder<T> whenFalse(AuthorizationManager<T> whenFalse) {\n\t\t\tAssert.notNull(whenFalse, \"whenFalse cannot be null\");\n\t\t\tthis.whenFalse = whenFalse;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link ConditionalAuthorizationManager}.\n\t\t * @return the {@link ConditionalAuthorizationManager}\n\t\t */\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic ConditionalAuthorizationManager<T> build() {\n\t\t\tAssert.state(this.whenTrue != null, \"whenTrue is required\");\n\t\t\tAuthorizationManager<T> whenFalse = this.whenFalse;\n\t\t\tif (whenFalse == null) {\n\t\t\t\twhenFalse = (AuthorizationManager<T>) SingleResultAuthorizationManager.permitAll();\n\t\t\t}\n\t\t\treturn new ConditionalAuthorizationManager<>(this.condition, this.whenTrue, whenFalse);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/DefaultAuthorizationManagerFactory.java",
    "content": "/*\n * Copyright 2002-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.util.Assert;\n\n/**\n * A factory for creating different kinds of {@link AuthorizationManager} instances.\n *\n * @param <T> the type of object that the authorization check is being done on\n * @author Steve Riesenberg\n * @author Andrey Litvitski\n * @author Rob Winch\n * @since 7.0\n */\npublic final class DefaultAuthorizationManagerFactory<T extends @Nullable Object>\n\t\timplements AuthorizationManagerFactory<T> {\n\n\tprivate AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate RoleHierarchy roleHierarchy = new NullRoleHierarchy();\n\n\tprivate String rolePrefix = \"ROLE_\";\n\n\tprivate @Nullable AuthorizationManager<T> additionalAuthorization;\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} used to check the user's\n\t * authentication.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use\n\t */\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\tthis.trustResolver = trustResolver;\n\t}\n\n\t/**\n\t * Sets the {@link RoleHierarchy} used to discover reachable authorities.\n\t * @param roleHierarchy the {@link RoleHierarchy} to use\n\t */\n\tpublic void setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tAssert.notNull(roleHierarchy, \"roleHierarchy cannot be null\");\n\t\tthis.roleHierarchy = roleHierarchy;\n\t}\n\n\t/**\n\t * Sets the prefix used to create an authority name from a role name. Can be an empty\n\t * string.\n\t * @param rolePrefix the role prefix to use\n\t */\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tAssert.notNull(rolePrefix, \"rolePrefix cannot be null\");\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n\t/**\n\t * Sets additional authorization to be applied to the returned\n\t * {@link AuthorizationManager} for the following methods:\n\t *\n\t * <ul>\n\t * <li>{@link #hasRole(String)}</li>\n\t * <li>{@link #hasAnyRole(String...)}</li>\n\t * <li>{@link #hasAllRoles(String...)}</li>\n\t * <li>{@link #hasAuthority(String)}</li>\n\t * <li>{@link #hasAnyAuthority(String...)}</li>\n\t * <li>{@link #hasAllAuthorities(String...)}</li>\n\t * <li>{@link #authenticated()}</li>\n\t * <li>{@link #fullyAuthenticated()}</li>\n\t * <li>{@link #rememberMe()}</li>\n\t * </ul>\n\t *\n\t * <p>\n\t * This does not affect {@code anonymous}, {@code permitAll}, or {@code denyAll}.\n\t * </p>\n\t * @param additionalAuthorization the {@link AuthorizationManager} to be applied.\n\t * Default is null (no additional authorization).\n\t */\n\tpublic void setAdditionalAuthorization(@Nullable AuthorizationManager<T> additionalAuthorization) {\n\t\tthis.additionalAuthorization = additionalAuthorization;\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasRole(String role) {\n\t\treturn hasAnyRole(role);\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasAnyRole(String... roles) {\n\t\treturn createManager(AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, roles));\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasAllRoles(String... roles) {\n\t\treturn createManager(AllAuthoritiesAuthorizationManager.hasAllPrefixedAuthorities(this.rolePrefix, roles));\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasAuthority(String authority) {\n\t\treturn createManager(AuthorityAuthorizationManager.hasAuthority(authority));\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasAnyAuthority(String... authorities) {\n\t\treturn createManager(AuthorityAuthorizationManager.hasAnyAuthority(authorities));\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasAllAuthorities(String... authorities) {\n\t\treturn createManager(AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities));\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> authenticated() {\n\t\treturn createManager(AuthenticatedAuthorizationManager.authenticated());\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> fullyAuthenticated() {\n\t\treturn createManager(AuthenticatedAuthorizationManager.fullyAuthenticated());\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> rememberMe() {\n\t\treturn createManager(AuthenticatedAuthorizationManager.rememberMe());\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> anonymous() {\n\t\treturn createManager(AuthenticatedAuthorizationManager.anonymous());\n\t}\n\n\tprivate AuthorizationManager<T> createManager(AuthorityAuthorizationManager<T> authorizationManager) {\n\t\tauthorizationManager.setRoleHierarchy(this.roleHierarchy);\n\t\treturn withAdditionalAuthorization(authorizationManager);\n\t}\n\n\tprivate AuthorizationManager<T> createManager(AllAuthoritiesAuthorizationManager<T> authorizationManager) {\n\t\tauthorizationManager.setRoleHierarchy(this.roleHierarchy);\n\t\treturn withAdditionalAuthorization(authorizationManager);\n\t}\n\n\tprivate AuthorizationManager<T> createManager(AuthenticatedAuthorizationManager<T> authorizationManager) {\n\t\tauthorizationManager.setTrustResolver(this.trustResolver);\n\t\treturn withAdditionalAuthorization(authorizationManager);\n\t}\n\n\tprivate AuthorizationManager<T> withAdditionalAuthorization(AuthorizationManager<T> manager) {\n\t\tif (this.additionalAuthorization == null) {\n\t\t\treturn manager;\n\t\t}\n\t\treturn AuthorizationManagers.allOf(new AuthorizationDecision(false), this.additionalAuthorization, manager);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/ExpressionAuthorizationDecision.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.springframework.expression.Expression;\n\n/**\n * Represents an {@link AuthorizationDecision} based on a {@link Expression}\n *\n * @author Marcus Da Coregio\n * @since 5.8\n */\n@SuppressWarnings(\"serial\")\npublic class ExpressionAuthorizationDecision extends AuthorizationDecision {\n\n\tprivate final Expression expression;\n\n\tpublic ExpressionAuthorizationDecision(boolean granted, Expression expressionAttribute) {\n\t\tsuper(granted);\n\t\tthis.expression = expressionAttribute;\n\t}\n\n\tpublic Expression getExpression() {\n\t\treturn this.expression;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \" [\" + \"granted=\" + isGranted() + \", expressionAttribute=\"\n\t\t\t\t+ this.expression.getExpressionString() + ']';\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/FactorAuthorizationDecision.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationResult} that contains {@link RequiredFactorError}.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic class FactorAuthorizationDecision implements AuthorizationResult {\n\n\tprivate final List<RequiredFactorError> factorErrors;\n\n\t/**\n\t * Creates a new instance.\n\t * @param factorErrors the {@link RequiredFactorError}. If empty, {@link #isGranted()}\n\t * returns true. Cannot be null or contain empty values.\n\t */\n\tpublic FactorAuthorizationDecision(List<RequiredFactorError> factorErrors) {\n\t\tAssert.notNull(factorErrors, \"factorErrors cannot be null\");\n\t\tAssert.noNullElements(factorErrors, \"factorErrors must not contain null elements\");\n\t\tthis.factorErrors = Collections.unmodifiableList(factorErrors);\n\t}\n\n\t/**\n\t * The specified {@link RequiredFactorError}s\n\t * @return the errors. Cannot be null or contain null values.\n\t */\n\tpublic List<RequiredFactorError> getFactorErrors() {\n\t\treturn this.factorErrors;\n\t}\n\n\t/**\n\t * Returns {@code getFactorErrors().isEmpty()}.\n\t * @return\n\t */\n\t@Override\n\tpublic boolean isGranted() {\n\t\treturn this.factorErrors.isEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/MapRequiredAuthoritiesRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Map} based implementation of {@link RequiredAuthoritiesRepository}.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic final class MapRequiredAuthoritiesRepository implements RequiredAuthoritiesRepository {\n\n\tprivate final Map<String, List<String>> usernameToAuthorities = new ConcurrentHashMap<>();\n\n\t@Override\n\tpublic List<String> findRequiredAuthorities(String username) {\n\t\tAssert.hasText(username, \"username cannot be empty\");\n\t\treturn this.usernameToAuthorities.getOrDefault(username, Collections.emptyList());\n\t}\n\n\tpublic void saveRequiredAuthorities(String username, List<String> authorities) {\n\t\tAssert.hasText(username, \"username cannot be empty\");\n\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\tList<String> userAuthorities = new ArrayList<>(authorities);\n\t\tthis.usernameToAuthorities.put(username, userAuthorities);\n\t}\n\n\tpublic void deleteRequiredAuthorities(String username) {\n\t\tAssert.hasText(username, \"username cannot be empty\");\n\t\tthis.usernameToAuthorities.remove(username);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Supplier;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationConvention;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} that observes the authorization\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic final class ObservationAuthorizationManager<T>\n\t\timplements AuthorizationManager<T>, MessageSourceAware, MethodAuthorizationDeniedHandler {\n\n\tprivate final ObservationRegistry registry;\n\n\tprivate final AuthorizationManager<T> delegate;\n\n\tprivate ObservationConvention<AuthorizationObservationContext<?>> convention = new AuthorizationObservationConvention();\n\n\tprivate MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();\n\n\tpublic ObservationAuthorizationManager(ObservationRegistry registry, AuthorizationManager<T> delegate) {\n\t\tthis.registry = registry;\n\t\tthis.delegate = delegate;\n\t\tif (delegate instanceof MethodAuthorizationDeniedHandler h) {\n\t\t\tthis.handler = h;\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tT object) {\n\t\tAuthorizationObservationContext<T> context = new AuthorizationObservationContext<>(object);\n\t\tSupplier<@Nullable Authentication> wrapped = () -> {\n\t\t\tcontext.setAuthentication(authentication.get());\n\t\t\treturn context.getAuthentication();\n\t\t};\n\t\tObservation observation = Observation.createNotStarted(this.convention, () -> context, this.registry).start();\n\t\ttry (Observation.Scope scope = observation.openScope()) {\n\t\t\tAuthorizationResult result = this.delegate.authorize(wrapped, object);\n\t\t\tcontext.setAuthorizationResult(result);\n\t\t\tif (result != null && !result.isGranted()) {\n\t\t\t\tobservation.error(new AccessDeniedException(\n\t\t\t\t\t\tthis.messages.getMessage(\"AbstractAccessDecisionManager.accessDenied\", \"Access Denied\")));\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\tobservation.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t\tfinally {\n\t\t\tobservation.stop();\n\t\t}\n\t}\n\n\t/**\n\t * Use the provided convention for reporting observation data\n\t * @param convention The provided convention\n\t *\n\t * @since 6.1\n\t */\n\tpublic void setObservationConvention(ObservationConvention<AuthorizationObservationContext<?>> convention) {\n\t\tAssert.notNull(convention, \"The observation convention cannot be null\");\n\t\tthis.convention = convention;\n\t}\n\n\t/**\n\t * Set the MessageSource that this object runs in.\n\t * @param messageSource The message source to be used by this object\n\t * @since 6.2\n\t */\n\t@Override\n\tpublic void setMessageSource(final MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\treturn this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\treturn this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationConvention;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;\nimport org.springframework.security.authorization.method.MethodInvocationResult;\nimport org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link ReactiveAuthorizationManager} that observes the authentication\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic final class ObservationReactiveAuthorizationManager<T>\n\t\timplements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {\n\n\tprivate final ObservationRegistry registry;\n\n\tprivate final ReactiveAuthorizationManager<T> delegate;\n\n\tprivate ObservationConvention<AuthorizationObservationContext<?>> convention = new AuthorizationObservationConvention();\n\n\tprivate MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();\n\n\tpublic ObservationReactiveAuthorizationManager(ObservationRegistry registry,\n\t\t\tReactiveAuthorizationManager<T> delegate) {\n\t\tthis.registry = registry;\n\t\tthis.delegate = delegate;\n\t\tif (delegate instanceof MethodAuthorizationDeniedHandler h) {\n\t\t\tthis.handler = h;\n\t\t}\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, T object) {\n\t\tAuthorizationObservationContext<T> context = new AuthorizationObservationContext<>(object);\n\t\tMono<Authentication> wrapped = authentication.mapNotNull((auth) -> {\n\t\t\tcontext.setAuthentication(auth);\n\t\t\treturn context.getAuthentication();\n\t\t});\n\t\treturn Mono.deferContextual((contextView) -> {\n\t\t\tObservation observation = Observation.createNotStarted(this.convention, () -> context, this.registry)\n\t\t\t\t.parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null))\n\t\t\t\t.start();\n\t\t\treturn this.delegate.authorize(wrapped, object).doOnSuccess((result) -> {\n\t\t\t\tcontext.setAuthorizationResult(result);\n\t\t\t\tif (result == null || !result.isGranted()) {\n\t\t\t\t\tobservation.error(new AccessDeniedException(\"Access Denied\"));\n\t\t\t\t}\n\t\t\t\tobservation.stop();\n\t\t\t}).doOnCancel(observation::stop).doOnError((t) -> {\n\t\t\t\tobservation.error(t);\n\t\t\t\tobservation.stop();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Use the provided convention for reporting observation data\n\t * @param convention The provided convention\n\t *\n\t * @since 6.1\n\t */\n\tpublic void setObservationConvention(ObservationConvention<AuthorizationObservationContext<?>> convention) {\n\t\tAssert.notNull(convention, \"The observation convention cannot be null\");\n\t\tthis.convention = convention;\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\treturn this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\treturn this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/ReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.core.Authentication;\n\n/**\n * A reactive authorization manager which can determine if an {@link Authentication} has\n * access to a specific object.\n *\n * @param <T> the type of object that the authorization check is being done on.\n * @author Rob Winch\n * @since 5.0\n */\npublic interface ReactiveAuthorizationManager<@Nullable T> {\n\n\t/**\n\t * Determines if access should be granted for a specific authentication and object\n\t * @param authentication the Authentication to check\n\t * @param object the object to check\n\t * @return an empty Mono if authorization is granted or a Mono error if access is\n\t * denied\n\t */\n\tdefault Mono<Void> verify(Mono<Authentication> authentication, T object) {\n\t\t// @formatter:off\n\t\treturn authorize(authentication, object)\n\t\t\t\t.filter(AuthorizationResult::isGranted)\n\t\t\t\t.switchIfEmpty(Mono.defer(() -> Mono.error(new AccessDeniedException(\"Access Denied\"))))\n\t\t\t\t.flatMap((decision) -> Mono.empty());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Determines if access is granted for a specific authentication and object.\n\t * @param authentication the Authentication to authorize\n\t * @param object the object to check\n\t * @return an decision or empty Mono if no decision could be made.\n\t * @since 6.4\n\t */\n\tMono<AuthorizationResult> authorize(Mono<Authentication> authentication, T object);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/RequiredAuthoritiesAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} that requires all the authorities returned by a\n * {@link RequiredAuthoritiesRepository} implementation.\n *\n * @param <T> the type\n * @author Rob Winch\n * @since 7.0\n * @see AllAuthoritiesAuthorizationManager\n */\npublic class RequiredAuthoritiesAuthorizationManager<T> implements AuthorizationManager<T> {\n\n\tprivate final RequiredAuthoritiesRepository authorities;\n\n\t/**\n\t * Creates a new instance.\n\t * @param authorities the {@link RequiredAuthoritiesRepository} to use. Cannot be\n\t * null.\n\t */\n\tpublic RequiredAuthoritiesAuthorizationManager(RequiredAuthoritiesRepository authorities) {\n\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\tthis.authorities = authorities;\n\t}\n\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tT object) {\n\t\tList<String> authorities = findAuthorities(authentication.get());\n\t\tif (authorities.isEmpty()) {\n\t\t\treturn new AuthorizationDecision(true);\n\t\t}\n\t\tAllAuthoritiesAuthorizationManager<T> delegate = AllAuthoritiesAuthorizationManager\n\t\t\t.hasAllAuthorities(authorities);\n\t\treturn delegate.authorize(authentication, object);\n\t}\n\n\tprivate List<String> findAuthorities(@Nullable Authentication authentication) {\n\t\tif (authentication == null) {\n\t\t\treturn List.of();\n\t\t}\n\t\tString username = authentication.getName();\n\t\treturn this.authorities.findRequiredAuthorities(username);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/RequiredAuthoritiesRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.List;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Finds additional required authorities for the provided {@link Authentication#getName()}\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic interface RequiredAuthoritiesRepository {\n\n\t/**\n\t * Finds additional required {@link GrantedAuthority#getAuthority()}s for the provided\n\t * username.\n\t * @param username the username. Cannot be null or empty.\n\t * @return the additional authorities required.\n\t */\n\tList<String> findRequiredAuthorities(String username);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/RequiredFactor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.time.Duration;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * The requirements for an {@link GrantedAuthority} to be considered a valid factor.\n *\n * <ul>\n * <li>If the {@link #getAuthority()} is specified, then it must match\n * {@link GrantedAuthority#getAuthority()}</li>\n * <li>If {@link #getValidDuration()} is specified, the matching {@link GrantedAuthority}\n * must be of type {@link FactorGrantedAuthority} and\n * {@link FactorGrantedAuthority#getIssuedAt()} must be such that it is not considered\n * expired when compared to {@link #getValidDuration()}.</li>\n * </ul>\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic final class RequiredFactor {\n\n\tprivate final String authority;\n\n\tprivate final @Nullable Duration validDuration;\n\n\tprivate RequiredFactor(String authority, @Nullable Duration validDuration) {\n\t\tAssert.notNull(authority, \"authority cannot be null\");\n\t\tthis.authority = authority;\n\t\tthis.validDuration = validDuration;\n\t}\n\n\t/**\n\t * The expected {@link GrantedAuthority#getAuthority()}.\n\t * @return the authority.\n\t */\n\tpublic String getAuthority() {\n\t\treturn this.authority;\n\t}\n\n\t/**\n\t * How long the\n\t * {@link org.springframework.security.core.authority.FactorGrantedAuthority} is valid\n\t * for.\n\t * @return\n\t */\n\tpublic @Nullable Duration getValidDuration() {\n\t\treturn this.validDuration;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (!(o instanceof RequiredFactor that)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(this.authority, that.authority) && Objects.equals(this.validDuration, that.validDuration);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.authority, this.validDuration);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RequiredFactor [authority=\" + this.authority + \", validDuration=\" + this.validDuration + \"]\";\n\t}\n\n\t/**\n\t * Creates a {@link Builder} with the specified authority.\n\t * @param authority the authority.\n\t * @return the builder.\n\t */\n\tpublic static Builder withAuthority(String authority) {\n\t\treturn builder().authority(authority);\n\t}\n\n\t/**\n\t * Creates a new {@link Builder}.\n\t * @return\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * A builder for {@link RequiredFactor}.\n\t *\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tpublic static class Builder {\n\n\t\tprivate @Nullable String authority;\n\n\t\tprivate @Nullable Duration validDuration;\n\n\t\t/**\n\t\t * Sets the required authority.\n\t\t * @param authority the authority.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder authority(@Nullable String authority) {\n\t\t\tthis.authority = authority;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A convenience method for invoking {@link #authority(String)} with\n\t\t * {@link FactorGrantedAuthority#AUTHORIZATION_CODE_AUTHORITY}.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder authorizationCodeAuthority() {\n\t\t\treturn authority(FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY);\n\t\t}\n\n\t\t/**\n\t\t * A convenience method for invoking {@link #authority(String)} with\n\t\t * {@link FactorGrantedAuthority#BEARER_AUTHORITY}.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder bearerTokenAuthority() {\n\t\t\treturn authority(FactorGrantedAuthority.BEARER_AUTHORITY);\n\t\t}\n\n\t\t/**\n\t\t * A convenience method for invoking {@link #authority(String)} with\n\t\t * {@link FactorGrantedAuthority#CAS_AUTHORITY}.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder casAuthority() {\n\t\t\treturn authority(FactorGrantedAuthority.CAS_AUTHORITY);\n\t\t}\n\n\t\t/**\n\t\t * A convenience method for invoking {@link #authority(String)} with\n\t\t * {@link FactorGrantedAuthority#PASSWORD_AUTHORITY}.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder passwordAuthority() {\n\t\t\treturn authority(FactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t\t}\n\n\t\t/**\n\t\t * A convenience method for invoking {@link #authority(String)} with\n\t\t * {@link FactorGrantedAuthority#OTT_AUTHORITY}.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder ottAuthority() {\n\t\t\treturn authority(FactorGrantedAuthority.OTT_AUTHORITY);\n\t\t}\n\n\t\t/**\n\t\t * A convenience method for invoking {@link #authority(String)} with\n\t\t * {@link FactorGrantedAuthority#SAML_RESPONSE_AUTHORITY}.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder samlAuthority() {\n\t\t\treturn authority(FactorGrantedAuthority.SAML_RESPONSE_AUTHORITY);\n\t\t}\n\n\t\t/**\n\t\t * A convenience method for invoking {@link #authority(String)} with\n\t\t * {@link FactorGrantedAuthority#WEBAUTHN_AUTHORITY}.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder webauthnAuthority() {\n\t\t\treturn authority(FactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t\t}\n\n\t\t/**\n\t\t * A convenience method for invoking {@link #authority(String)} with\n\t\t * {@link FactorGrantedAuthority#X509_AUTHORITY}.\n\t\t * @return the builder.\n\t\t */\n\t\tpublic Builder x509Authority() {\n\t\t\treturn authority(FactorGrantedAuthority.X509_AUTHORITY);\n\t\t}\n\n\t\t/**\n\t\t * Sets the optional {@link Duration} of time that the {@link RequiredFactor} is\n\t\t * valid for.\n\t\t * @param validDuration the {@link Duration}.\n\t\t * @return\n\t\t */\n\t\tpublic Builder validDuration(@Nullable Duration validDuration) {\n\t\t\tthis.validDuration = validDuration;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new instance.\n\t\t * @return\n\t\t */\n\t\tpublic RequiredFactor build() {\n\t\t\tAssert.notNull(this.authority, \"authority cannot be null\");\n\t\t\treturn new RequiredFactor(this.authority, this.validDuration);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/RequiredFactorError.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Objects;\n\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * An error when the requirements of {@link RequiredFactor} are not met.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic class RequiredFactorError {\n\n\tprivate final RequiredFactor requiredFactor;\n\n\tprivate final Reason reason;\n\n\tRequiredFactorError(RequiredFactor requiredFactor, Reason reason) {\n\t\tAssert.notNull(requiredFactor, \"RequiredFactor must not be null\");\n\t\tAssert.notNull(reason, \"Reason must not be null\");\n\t\tif (reason == Reason.EXPIRED && requiredFactor.getValidDuration() == null) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"If expired, RequiredFactor.getValidDuration() must not be null. Got \" + requiredFactor);\n\t\t}\n\t\tthis.requiredFactor = requiredFactor;\n\t\tthis.reason = reason;\n\t}\n\n\tpublic RequiredFactor getRequiredFactor() {\n\t\treturn this.requiredFactor;\n\t}\n\n\t/**\n\t * True if not {@link #isMissing()} but was older than the\n\t * {@link RequiredFactor#getValidDuration()}.\n\t * @return true if expired, else false\n\t */\n\tpublic boolean isExpired() {\n\t\treturn this.reason == Reason.EXPIRED;\n\t}\n\n\t/**\n\t * True if no {@link FactorGrantedAuthority#getAuthority()} on the\n\t * {@link org.springframework.security.core.Authentication} matched\n\t * {@link RequiredFactor#getAuthority()}.\n\t * @return true if missing, else false.\n\t */\n\tpublic boolean isMissing() {\n\t\treturn this.reason == Reason.MISSING;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tRequiredFactorError that = (RequiredFactorError) o;\n\t\treturn Objects.equals(this.requiredFactor, that.requiredFactor) && this.reason == that.reason;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.requiredFactor, this.reason);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RequiredFactorError{\" + \"requiredFactor=\" + this.requiredFactor + \", reason=\" + this.reason + '}';\n\t}\n\n\tpublic static RequiredFactorError createMissing(RequiredFactor requiredFactor) {\n\t\treturn new RequiredFactorError(requiredFactor, Reason.MISSING);\n\t}\n\n\tpublic static RequiredFactorError createExpired(RequiredFactor requiredFactor) {\n\t\treturn new RequiredFactorError(requiredFactor, Reason.EXPIRED);\n\t}\n\n\t/**\n\t * The reason that the error occurred.\n\t *\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tprivate enum Reason {\n\n\t\t/**\n\t\t * The authority was missing.\n\t\t * @see #isMissing()\n\t\t */\n\t\tMISSING,\n\t\t/**\n\t\t * The authority was considered expired.\n\t\t * @see #isExpired()\n\t\t */\n\t\tEXPIRED\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/SingleResultAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} which creates permit-all and deny-all\n * {@link AuthorizationManager} instances.\n *\n * @author Max Batischev\n * @since 6.5\n */\npublic final class SingleResultAuthorizationManager<C> implements AuthorizationManager<C> {\n\n\tprivate static final SingleResultAuthorizationManager<?> DENY_MANAGER = new SingleResultAuthorizationManager<>(\n\t\t\tnew AuthorizationDecision(false));\n\n\tprivate static final SingleResultAuthorizationManager<?> PERMIT_MANAGER = new SingleResultAuthorizationManager<>(\n\t\t\tnew AuthorizationDecision(true));\n\n\tprivate final AuthorizationResult result;\n\n\tpublic SingleResultAuthorizationManager(AuthorizationResult result) {\n\t\tAssert.notNull(result, \"result cannot be null\");\n\t\tthis.result = result;\n\t}\n\n\t@Override\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication, C object) {\n\t\tif (!(this.result instanceof AuthorizationDecision)) {\n\t\t\tthrow new IllegalArgumentException(\"result should be AuthorizationDecision\");\n\t\t}\n\t\treturn this.result;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <C> SingleResultAuthorizationManager<C> denyAll() {\n\t\treturn (SingleResultAuthorizationManager<C>) DENY_MANAGER;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <C> SingleResultAuthorizationManager<C> permitAll() {\n\t\treturn (SingleResultAuthorizationManager<C>) PERMIT_MANAGER;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.authorization.event.AuthorizationDeniedEvent;\nimport org.springframework.security.authorization.event.AuthorizationGrantedEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of {@link AuthorizationEventPublisher} that uses Spring's event\n * publishing support.\n *\n * Because {@link AuthorizationGrantedEvent}s typically require additional business logic\n * to decide whether to publish, this implementation only publishes\n * {@link AuthorizationDeniedEvent}s.\n *\n * @author Parikshit Dutta\n * @author Josh Cummings\n * @since 5.7\n */\npublic final class SpringAuthorizationEventPublisher implements AuthorizationEventPublisher {\n\n\tprivate final ApplicationEventPublisher eventPublisher;\n\n\tprivate Predicate<AuthorizationResult> shouldPublishResult = (result) -> !result.isGranted();\n\n\t/**\n\t * Construct this publisher using Spring's {@link ApplicationEventPublisher}\n\t * @param eventPublisher\n\t */\n\tpublic SpringAuthorizationEventPublisher(ApplicationEventPublisher eventPublisher) {\n\t\tAssert.notNull(eventPublisher, \"eventPublisher cannot be null\");\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,\n\t\t\t@Nullable AuthorizationResult result) {\n\t\tif (result == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!this.shouldPublishResult.test(result)) {\n\t\t\treturn;\n\t\t}\n\t\tAuthorizationDeniedEvent<T> failure = new AuthorizationDeniedEvent<>(authentication, object, result);\n\t\tthis.eventPublisher.publishEvent(failure);\n\t}\n\n\t/**\n\t * Use this predicate to test whether to publish an event.\n\t *\n\t * <p>\n\t * Since you cannot publish a {@code null} event, checking for null is already\n\t * performed before this test is run\n\t * @param shouldPublishResult the test to perform on non-{@code null} events\n\t * @since 7.0\n\t */\n\tpublic void setShouldPublishResult(Predicate<AuthorizationResult> shouldPublishResult) {\n\t\tAssert.notNull(shouldPublishResult, \"shouldPublishResult cannot be null\");\n\t\tthis.shouldPublishResult = shouldPublishResult;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.event;\n\nimport java.util.function.Supplier;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.ResolvableTypeProvider;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\n/**\n * An {@link ApplicationEvent} which indicates failed authorization.\n *\n * @author Parikshit Dutta\n * @author Josh Cummings\n * @since 5.7\n */\n@SuppressWarnings(\"serial\")\npublic class AuthorizationDeniedEvent<T> extends AuthorizationEvent implements ResolvableTypeProvider {\n\n\t/**\n\t * @since 6.4\n\t */\n\tpublic AuthorizationDeniedEvent(Supplier<Authentication> authentication, T object, AuthorizationResult result) {\n\t\tsuper(authentication, object, result);\n\t}\n\n\t/**\n\t * Get the object to which access was requested\n\t * @return the object to which access was requested\n\t * @since 5.8\n\t */\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic T getObject() {\n\t\treturn (T) getSource();\n\t}\n\n\t/**\n\t * Get {@link ResolvableType} of this class.\n\t * @return {@link ResolvableType}\n\t * @since 6.5\n\t */\n\t@Override\n\tpublic ResolvableType getResolvableType() {\n\t\treturn ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getObject()));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/event/AuthorizationEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.event;\n\nimport java.io.Serial;\nimport java.util.function.Supplier;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * A parent class for {@link AuthorizationGrantedEvent} and\n * {@link AuthorizationDeniedEvent}.\n *\n * @author Josh Cummings\n * @since 5.8\n */\n@SuppressWarnings(\"serial\")\npublic class AuthorizationEvent extends ApplicationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -9053927371500241295L;\n\n\tprivate final Supplier<Authentication> authentication;\n\n\tprivate final AuthorizationResult result;\n\n\t/**\n\t * Construct an {@link AuthorizationEvent}\n\t * @param authentication the principal requiring access\n\t * @param object the object to which access was requested\n\t * @param result whether authorization was granted or denied\n\t */\n\tpublic AuthorizationEvent(Supplier<Authentication> authentication, Object object, AuthorizationDecision result) {\n\t\tsuper(object);\n\t\tAssert.notNull(authentication, \"authentication supplier cannot be null\");\n\t\tthis.authentication = authentication;\n\t\tthis.result = result;\n\t}\n\n\t/**\n\t * Construct an {@link AuthorizationEvent}\n\t * @param authentication the principal requiring access\n\t * @param object the object to which access was requested\n\t * @param result whether authorization was granted or denied\n\t */\n\tpublic AuthorizationEvent(Supplier<Authentication> authentication, Object object, AuthorizationResult result) {\n\t\tsuper(object);\n\t\tAssert.notNull(authentication, \"authentication supplier cannot be null\");\n\t\tthis.authentication = authentication;\n\t\tthis.result = result;\n\t}\n\n\t/**\n\t * Get the principal requiring access\n\t * @return the principal requiring access\n\t */\n\tpublic Supplier<Authentication> getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n\t/**\n\t * Get the object to which access was requested\n\t * @return the object to which access was requested\n\t */\n\tpublic Object getObject() {\n\t\treturn getSource();\n\t}\n\n\t/**\n\t * Get the response to the principal's request\n\t * @return the response to the principal's request\n\t * @since 6.4\n\t */\n\tpublic AuthorizationResult getAuthorizationResult() {\n\t\treturn this.result;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.event;\n\nimport java.io.Serial;\nimport java.util.function.Supplier;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.ResolvableTypeProvider;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\n/**\n * An {@link ApplicationEvent} which indicates successful authorization.\n *\n * @author Parikshit Dutta\n * @author Josh Cummings\n * @since 5.7\n */\npublic class AuthorizationGrantedEvent<T> extends AuthorizationEvent implements ResolvableTypeProvider {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -8690818228055810339L;\n\n\t/**\n\t * @since 6.4\n\t */\n\tpublic AuthorizationGrantedEvent(Supplier<Authentication> authentication, T object, AuthorizationResult result) {\n\t\tsuper(authentication, object, result);\n\t}\n\n\t/**\n\t * Get the object to which access was requested\n\t * @return the object to which access was requested\n\t * @since 5.8\n\t */\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic T getObject() {\n\t\treturn (T) getSource();\n\t}\n\n\t/**\n\t * Get {@link ResolvableType} of this class.\n\t * @return {@link ResolvableType}\n\t * @since 6.5\n\t */\n\t@Override\n\tpublic ResolvableType getResolvableType() {\n\t\treturn ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getObject()));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/event/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.authorization.event;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AbstractAuthorizationManagerRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodClassKey;\nimport org.springframework.security.authorization.AuthorizationManager;\n\n/**\n * For internal use only, as this contract is likely to change\n *\n * @author Evgeniy Cheban\n */\nabstract class AbstractAuthorizationManagerRegistry {\n\n\tstatic final AuthorizationManager<MethodInvocation> NULL_MANAGER = (a, o) -> null;\n\n\tprivate final Map<MethodClassKey, AuthorizationManager<MethodInvocation>> cachedManagers = new ConcurrentHashMap<>();\n\n\t/**\n\t * Returns an {@link AuthorizationManager} for the {@link MethodInvocation}.\n\t * @param methodInvocation the {@link MethodInvocation} to use\n\t * @return an {@link AuthorizationManager} to use\n\t */\n\tfinal AuthorizationManager<MethodInvocation> getManager(MethodInvocation methodInvocation) {\n\t\tMethod method = methodInvocation.getMethod();\n\t\tObject target = methodInvocation.getThis();\n\t\tClass<?> targetClass = (target != null) ? target.getClass() : null;\n\t\tMethodClassKey cacheKey = new MethodClassKey(method, targetClass);\n\t\treturn this.cachedManagers.computeIfAbsent(cacheKey, (k) -> resolveManager(method, targetClass));\n\t}\n\n\t/**\n\t * Subclasses should implement this method to provide the non-null\n\t * {@link AuthorizationManager} for the method and the target class.\n\t * @param method the method\n\t * @param targetClass the target class\n\t * @return the non-null {@link AuthorizationManager}\n\t */\n\tabstract AuthorizationManager<MethodInvocation> resolveManager(Method method, @Nullable Class<?> targetClass);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AbstractExpressionAttributeRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodClassKey;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.util.Assert;\n\n/**\n * For internal use only, as this contract is likely to change\n *\n * @author Evgeniy Cheban\n * @author DingHao\n */\nabstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> {\n\n\tprivate final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>();\n\n\tprivate MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\n\t/**\n\t * Returns an {@link ExpressionAttribute} for the {@link MethodInvocation}.\n\t * @param mi the {@link MethodInvocation} to use\n\t * @return the {@link ExpressionAttribute} to use\n\t */\n\tfinal T getAttribute(MethodInvocation mi) {\n\t\tMethod method = mi.getMethod();\n\t\tObject target = mi.getThis();\n\t\tClass<?> targetClass = (target != null) ? target.getClass() : null;\n\t\treturn getAttribute(method, targetClass);\n\t}\n\n\t/**\n\t * Returns an {@link ExpressionAttribute} for the method and the target class.\n\t * @param method the method\n\t * @param targetClass the target class\n\t * @return the {@link ExpressionAttribute} to use\n\t */\n\tfinal T getAttribute(Method method, @Nullable Class<?> targetClass) {\n\t\tMethodClassKey cacheKey = new MethodClassKey(method, targetClass);\n\t\treturn this.cachedAttributes.computeIfAbsent(cacheKey, (k) -> resolveAttribute(method, targetClass));\n\t}\n\n\t/**\n\t * Returns the {@link MethodSecurityExpressionHandler}.\n\t * @return the {@link MethodSecurityExpressionHandler} to use\n\t */\n\tMethodSecurityExpressionHandler getExpressionHandler() {\n\t\treturn this.expressionHandler;\n\t}\n\n\tvoid setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.expressionHandler = expressionHandler;\n\t}\n\n\tabstract void setTemplateDefaults(AnnotationTemplateExpressionDefaults adapter);\n\n\t/**\n\t * Subclasses should implement this method to provide the non-null\n\t * {@link ExpressionAttribute} for the method and the target class.\n\t * @param method the method\n\t * @param targetClass the target class\n\t * @return {@link ExpressionAttribute} or null if not found.\n\t */\n\tabstract @Nullable T resolveAttribute(Method method, @Nullable Class<?> targetClass);\n\n\tClass<?> targetClass(Method method, @Nullable Class<?> targetClass) {\n\t\treturn (targetClass != null) ? targetClass : method.getDeclaringClass();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationAdvisor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInterceptor;\n\nimport org.springframework.aop.PointcutAdvisor;\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.core.Ordered;\n\n/**\n * An interface that indicates method security advice\n *\n * @author Josh Cummings\n * @since 6.3\n * @see AuthorizationManagerBeforeMethodInterceptor\n * @see AuthorizationManagerAfterMethodInterceptor\n * @see PreFilterAuthorizationMethodInterceptor\n * @see PostFilterAuthorizationMethodInterceptor\n */\npublic interface AuthorizationAdvisor extends Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationAdvisorProxyFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.SortedMap;\nimport java.util.SortedSet;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.aop.Advisor;\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.framework.AopInfrastructureBean;\nimport org.springframework.aop.framework.ProxyFactory;\nimport org.springframework.beans.factory.SmartInitializingSingleton;\nimport org.springframework.core.annotation.AnnotationAwareOrderComparator;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * A proxy factory for applying authorization advice to an arbitrary object.\n *\n * <p>\n * For example, consider a non-Spring-managed object {@code Foo}: <pre>\n *     class Foo {\n *         &#064;PreAuthorize(\"hasAuthority('bar:read')\")\n *         String bar() { ... }\n *     }\n * </pre>\n *\n * Use {@link AuthorizationAdvisorProxyFactory} to wrap the instance in Spring Security's\n * {@link org.springframework.security.access.prepost.PreAuthorize} method interceptor\n * like so:\n *\n * <pre>\n *     AuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();\n *     Foo foo = new Foo();\n *     foo.bar(); // passes\n *     Foo securedFoo = proxyFactory.proxy(foo);\n *     securedFoo.bar(); // access denied!\n * </pre>\n *\n * @author Josh Cummings\n * @since 6.3\n */\npublic final class AuthorizationAdvisorProxyFactory implements AuthorizationProxyFactory,\n\t\tIterable<AuthorizationAdvisor>, AopInfrastructureBean, SmartInitializingSingleton {\n\n\tprivate static final boolean isReactivePresent = ClassUtils.isPresent(\"reactor.core.publisher.Mono\", null);\n\n\tprivate static final TargetVisitor DEFAULT_VISITOR = isReactivePresent\n\t\t\t? TargetVisitor.of(new ClassVisitor(), new ReactiveTypeVisitor(), new ContainerTypeVisitor())\n\t\t\t: TargetVisitor.of(new ClassVisitor(), new ContainerTypeVisitor());\n\n\tprivate static final TargetVisitor DEFAULT_VISITOR_SKIP_VALUE_TYPES = TargetVisitor.of(new ClassVisitor(),\n\t\t\tnew IgnoreValueTypeVisitor(), DEFAULT_VISITOR);\n\n\tprivate final AuthorizationProxyMethodInterceptor authorizationProxy = new AuthorizationProxyMethodInterceptor();\n\n\tprivate List<AuthorizationAdvisor> advisors;\n\n\tprivate TargetVisitor visitor = DEFAULT_VISITOR;\n\n\t/**\n\t * Construct an {@link AuthorizationAdvisorProxyFactory} with the provided advisors.\n\t * @param advisors the advisors to use\n\t * @since 6.4\n\t */\n\tpublic AuthorizationAdvisorProxyFactory(List<AuthorizationAdvisor> advisors) {\n\t\tthis.advisors = new ArrayList<>(advisors);\n\t\tfor (AuthorizationAdvisor advisor : this.advisors) {\n\t\t\tif (advisor instanceof AuthorizeReturnObjectMethodInterceptor interceptor) {\n\t\t\t\tinterceptor.setAuthorizationProxyFactory(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Construct an {@link AuthorizationAdvisorProxyFactory} with the defaults needed for\n\t * wrapping objects in Spring Security's pre-post method security support.\n\t * @return an {@link AuthorizationAdvisorProxyFactory} for adding pre-post method\n\t * security support\n\t */\n\tpublic static AuthorizationAdvisorProxyFactory withDefaults() {\n\t\tList<AuthorizationAdvisor> advisors = new ArrayList<>();\n\t\tadvisors.add(AuthorizationManagerBeforeMethodInterceptor.preAuthorize());\n\t\tadvisors.add(AuthorizationManagerAfterMethodInterceptor.postAuthorize());\n\t\tadvisors.add(new PreFilterAuthorizationMethodInterceptor());\n\t\tadvisors.add(new PostFilterAuthorizationMethodInterceptor());\n\t\tadvisors.add(new AuthorizeReturnObjectMethodInterceptor());\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = new AuthorizationAdvisorProxyFactory(advisors);\n\t\tAnnotationAwareOrderComparator.sort(proxyFactory.advisors);\n\t\treturn proxyFactory;\n\t}\n\n\t/**\n\t * Construct an {@link AuthorizationAdvisorProxyFactory} with the defaults needed for\n\t * wrapping objects in Spring Security's pre-post reactive method security support.\n\t * @return an {@link AuthorizationAdvisorProxyFactory} for adding pre-post reactive\n\t * method security support\n\t */\n\tpublic static AuthorizationAdvisorProxyFactory withReactiveDefaults() {\n\t\tList<AuthorizationAdvisor> advisors = new ArrayList<>();\n\t\tadvisors.add(AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize());\n\t\tadvisors.add(AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize());\n\t\tadvisors.add(new PreFilterAuthorizationReactiveMethodInterceptor());\n\t\tadvisors.add(new PostFilterAuthorizationReactiveMethodInterceptor());\n\t\tadvisors.add(new AuthorizeReturnObjectMethodInterceptor());\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = new AuthorizationAdvisorProxyFactory(advisors);\n\t\tAnnotationAwareOrderComparator.sort(proxyFactory.advisors);\n\t\treturn proxyFactory;\n\t}\n\n\t@Override\n\tpublic void afterSingletonsInstantiated() {\n\t\tAnnotationAwareOrderComparator.sort(this.advisors);\n\t}\n\n\t/**\n\t * Proxy an object to enforce authorization advice.\n\t *\n\t * <p>\n\t * Proxies any instance of a non-final class or a class that implements more than one\n\t * interface.\n\t *\n\t * <p>\n\t * If {@code target} is an {@link Iterator}, {@link Collection}, {@link Array},\n\t * {@link Map}, {@link Stream}, or {@link Optional}, then the element or value type is\n\t * proxied.\n\t *\n\t * <p>\n\t * If {@code target} is a {@link Class}, then {@link ProxyFactory#getProxyClass} is\n\t * invoked instead.\n\t * @param target the instance to proxy\n\t * @return the proxied instance\n\t */\n\t@Override\n\tpublic <T> @Nullable T proxy(@Nullable T target) {\n\t\tif (target == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (target instanceof AuthorizationProxy proxied) {\n\t\t\treturn (T) proxied;\n\t\t}\n\t\tObject proxied = this.visitor.visit(this, target);\n\t\tif (proxied != null) {\n\t\t\treturn (T) proxied;\n\t\t}\n\t\tProxyFactory factory = new ProxyFactory(target);\n\t\tfactory.addAdvisors(this.authorizationProxy);\n\t\tList<Advisor> advisors = new ArrayList<>(this.advisors);\n\t\tAnnotationAwareOrderComparator.sort(advisors);\n\t\tfactory.addAdvisors(advisors);\n\t\tfactory.addInterface(AuthorizationProxy.class);\n\t\tfactory.setOpaque(true);\n\t\tfactory.setProxyTargetClass(!Modifier.isFinal(target.getClass().getModifiers()));\n\t\treturn (T) factory.getProxy();\n\t}\n\n\t/**\n\t * Add advisors that should be included to each proxy created.\n\t *\n\t * <p>\n\t * All advisors are re-sorted by their advisor order.\n\t * @param advisors the advisors to add\n\t * @deprecated Please use {@link #addAdvisor} instead\n\t */\n\t@Deprecated\n\tpublic void setAdvisors(AuthorizationAdvisor... advisors) {\n\t\tthis.advisors = new ArrayList<>(List.of(advisors));\n\t}\n\n\t/**\n\t * Add advisors that should be included to each proxy created.\n\t *\n\t * <p>\n\t * All advisors are re-sorted by their advisor order.\n\t * @param advisors the advisors to add\n\t * @deprecated Please use {@link #addAdvisor} instead\n\t */\n\t@Deprecated\n\tpublic void setAdvisors(Collection<AuthorizationAdvisor> advisors) {\n\t\tthis.advisors = new ArrayList<>(advisors);\n\t}\n\n\t/**\n\t * Add an advisor that should be included to each proxy created.\n\t *\n\t * <p>\n\t * This method sorts the advisors based on the order in\n\t * {@link AuthorizationAdvisor#getOrder}. You can use the values in\n\t * {@link AuthorizationInterceptorsOrder}to ensure advisors are located where you need\n\t * them.\n\t * @param advisor\n\t * @since 6.4\n\t * @deprecated please provide all advisors in the constructor\n\t */\n\t@Deprecated\n\tpublic void addAdvisor(AuthorizationAdvisor advisor) {\n\t\tthis.advisors.add(advisor);\n\t}\n\n\t/**\n\t * Use this visitor to navigate the proxy target's hierarchy.\n\t *\n\t * <p>\n\t * This can be helpful when you want a specialized behavior for a type or set of\n\t * types. For example, if you want to have this factory skip primitives and wrappers,\n\t * then you can do:\n\t *\n\t * <pre>\n\t * \tAuthorizationAdvisorProxyFactory proxyFactory = new AuthorizationAdvisorProxyFactory();\n\t * \tproxyFactory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());\n\t * </pre>\n\t *\n\t * <p>\n\t * The default {@link TargetVisitor} proxies {@link Class} instances as well as\n\t * instances contained in reactive types (if reactor is present), collection types,\n\t * and other container types like {@link Optional} and {@link Supplier}.\n\t *\n\t * <p>\n\t * If you want to add support for another container type, you can do so in the\n\t * following way:\n\t *\n\t * <pre>\n\t * \tTargetVisitor functions = (factory, target) -> {\n\t *\t\tif (target instanceof Function function) {\n\t *\t\t\treturn (input) -> factory.proxy(function.apply(input));\n\t *\t\t}\n\t *\t\treturn null;\n\t * \t};\n\t * \tAuthorizationAdvisorProxyFactory proxyFactory = new AuthorizationAdvisorProxyFactory();\n\t * \tproxyFactory.setTargetVisitor(TargetVisitor.of(functions, TargetVisitor.defaultsSkipValueTypes()));\n\t * </pre>\n\t * @param visitor the visitor to use to introduce specialized behavior for a type\n\t * @see TargetVisitor#defaults\n\t */\n\tpublic void setTargetVisitor(TargetVisitor visitor) {\n\t\tAssert.notNull(visitor, \"delegate cannot be null\");\n\t\tthis.visitor = visitor;\n\t}\n\n\t@Override\n\tpublic Iterator<AuthorizationAdvisor> iterator() {\n\t\treturn this.advisors.iterator();\n\t}\n\n\t/**\n\t * An interface to handle how the {@link AuthorizationAdvisorProxyFactory} should step\n\t * through the target's object hierarchy.\n\t *\n\t * @author Josh Cummings\n\t * @since 6.3\n\t * @see AuthorizationAdvisorProxyFactory#setTargetVisitor\n\t */\n\tpublic interface TargetVisitor {\n\n\t\t/**\n\t\t * Visit and possibly proxy this object.\n\t\t *\n\t\t * <p>\n\t\t * Visiting may take the form of walking down this object's hierarchy and proxying\n\t\t * sub-objects.\n\t\t *\n\t\t * <p>\n\t\t * An example is a visitor that proxies the elements of a {@link List} instead of\n\t\t * the list itself\n\t\t *\n\t\t * <p>\n\t\t * Returning {@code null} implies that this visitor does not want to proxy this\n\t\t * object\n\t\t * @param proxyFactory the proxy factory to delegate proxying to for any\n\t\t * sub-objects\n\t\t * @param target the object to proxy\n\t\t * @return the visited (and possibly proxied) object\n\t\t */\n\t\t@Nullable Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target);\n\n\t\t/**\n\t\t * The default {@link TargetVisitor}, which will proxy {@link Class} instances as\n\t\t * well as instances contained in reactive types (if reactor is present),\n\t\t * collection types, and other container types like {@link Optional} and\n\t\t * {@link Supplier}\n\t\t */\n\t\tstatic TargetVisitor defaults() {\n\t\t\treturn AuthorizationAdvisorProxyFactory.DEFAULT_VISITOR;\n\t\t}\n\n\t\t/**\n\t\t * The default {@link TargetVisitor} that also skips any value types (for example,\n\t\t * {@link String}, {@link Integer}). This is handy for annotations like\n\t\t * {@link AuthorizeReturnObject} when used at the class level\n\t\t */\n\t\tstatic TargetVisitor defaultsSkipValueTypes() {\n\t\t\treturn AuthorizationAdvisorProxyFactory.DEFAULT_VISITOR_SKIP_VALUE_TYPES;\n\t\t}\n\n\t\t/**\n\t\t * Compose a set of visitors. This is helpful when you are customizing for a given\n\t\t * type and still want the defaults applied for the remaining types.\n\t\t *\n\t\t * <p>\n\t\t * The resulting visitor will execute the first visitor that returns a non-null\n\t\t * value.\n\t\t * @param visitors the set of visitors\n\t\t * @return a composite that executes the first visitor that returns a non-null\n\t\t * value\n\t\t */\n\t\tstatic TargetVisitor of(TargetVisitor... visitors) {\n\t\t\treturn (proxyFactory, target) -> {\n\t\t\t\tfor (TargetVisitor visitor : visitors) {\n\t\t\t\t\tObject result = visitor.visit(proxyFactory, target);\n\t\t\t\t\tif (result != null) {\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t};\n\t\t}\n\n\t}\n\n\tprivate static final class IgnoreValueTypeVisitor implements TargetVisitor {\n\n\t\t@Override\n\t\tpublic @Nullable Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object object) {\n\t\t\tif (ClassUtils.isSimpleValueType(object.getClass())) {\n\t\t\t\treturn object;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tprivate static final class ClassVisitor implements TargetVisitor {\n\n\t\tprivate final AuthorizationProxyMethodInterceptor authorizationProxy = new AuthorizationProxyMethodInterceptor();\n\n\t\t@Override\n\t\tpublic @Nullable Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object object) {\n\t\t\tif (object instanceof Class<?> targetClass) {\n\t\t\t\tif (AuthorizationProxy.class.isAssignableFrom(targetClass)) {\n\t\t\t\t\treturn targetClass;\n\t\t\t\t}\n\t\t\t\tProxyFactory factory = new ProxyFactory();\n\t\t\t\tfactory.setTargetClass(targetClass);\n\t\t\t\tfactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass));\n\t\t\t\tfactory.setOpaque(true);\n\t\t\t\tfactory.setProxyTargetClass(!Modifier.isFinal(targetClass.getModifiers()));\n\t\t\t\tfactory.addAdvisor(this.authorizationProxy);\n\t\t\t\tfor (Advisor advisor : proxyFactory) {\n\t\t\t\t\tfactory.addAdvisors(advisor);\n\t\t\t\t}\n\t\t\t\tfactory.addInterface(AuthorizationProxy.class);\n\t\t\t\treturn factory.getProxyClass(getClass().getClassLoader());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tprivate static final class ContainerTypeVisitor implements TargetVisitor {\n\n\t\t@Override\n\t\tpublic @Nullable Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {\n\t\t\tif (target instanceof Iterator<?> iterator) {\n\t\t\t\treturn proxyIterator(proxyFactory, iterator);\n\t\t\t}\n\t\t\tif (target instanceof Queue<?> queue) {\n\t\t\t\treturn proxyQueue(proxyFactory, queue);\n\t\t\t}\n\t\t\tif (target instanceof List<?> list) {\n\t\t\t\treturn proxyList(proxyFactory, list);\n\t\t\t}\n\t\t\tif (target instanceof SortedSet<?> set) {\n\t\t\t\treturn proxySortedSet(proxyFactory, set);\n\t\t\t}\n\t\t\tif (target instanceof Set<?> set) {\n\t\t\t\treturn proxySet(proxyFactory, set);\n\t\t\t}\n\t\t\tif (target.getClass().isArray()) {\n\t\t\t\treturn proxyArray(proxyFactory, (Object[]) target);\n\t\t\t}\n\t\t\tif (target instanceof SortedMap<?, ?> map) {\n\t\t\t\treturn proxySortedMap(proxyFactory, map);\n\t\t\t}\n\t\t\tif (target instanceof Iterable<?> iterable) {\n\t\t\t\treturn proxyIterable(proxyFactory, iterable);\n\t\t\t}\n\t\t\tif (target instanceof Map<?, ?> map) {\n\t\t\t\treturn proxyMap(proxyFactory, map);\n\t\t\t}\n\t\t\tif (target instanceof Stream<?> stream) {\n\t\t\t\treturn proxyStream(proxyFactory, stream);\n\t\t\t}\n\t\t\tif (target instanceof Optional<?> optional) {\n\t\t\t\treturn proxyOptional(proxyFactory, optional);\n\t\t\t}\n\t\t\tif (target instanceof Supplier<?> supplier) {\n\t\t\t\treturn proxySupplier(proxyFactory, supplier);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate <T> @Nullable T proxyCast(AuthorizationProxyFactory proxyFactory, T target) {\n\t\t\treturn proxyFactory.proxy(target);\n\t\t}\n\n\t\tprivate <T> Iterable<T> proxyIterable(AuthorizationProxyFactory proxyFactory, Iterable<T> iterable) {\n\t\t\treturn () -> proxyIterator(proxyFactory, iterable.iterator());\n\t\t}\n\n\t\tprivate <T> Iterator<T> proxyIterator(AuthorizationProxyFactory proxyFactory, Iterator<T> iterator) {\n\t\t\treturn new Iterator<>() {\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasNext() {\n\t\t\t\t\treturn iterator.hasNext();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic @Nullable T next() {\n\t\t\t\t\treturn proxyCast(proxyFactory, iterator.next());\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tprivate <T> SortedSet<T> proxySortedSet(AuthorizationProxyFactory proxyFactory, SortedSet<T> set) {\n\t\t\tSortedSet<T> proxies = new TreeSet<>(set.comparator());\n\t\t\tfor (T toProxy : set) {\n\t\t\t\tproxies.add(proxyCast(proxyFactory, toProxy));\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tset.clear();\n\t\t\t\tset.addAll(proxies);\n\t\t\t\treturn proxies;\n\t\t\t}\n\t\t\tcatch (UnsupportedOperationException ex) {\n\t\t\t\treturn Collections.unmodifiableSortedSet(proxies);\n\t\t\t}\n\t\t}\n\n\t\tprivate <T> Set<T> proxySet(AuthorizationProxyFactory proxyFactory, Set<T> set) {\n\t\t\tSet<T> proxies = new LinkedHashSet<>(set.size());\n\t\t\tfor (T toProxy : set) {\n\t\t\t\tproxies.add(proxyCast(proxyFactory, toProxy));\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tset.clear();\n\t\t\t\tset.addAll(proxies);\n\t\t\t\treturn proxies;\n\t\t\t}\n\t\t\tcatch (UnsupportedOperationException ex) {\n\t\t\t\treturn Collections.unmodifiableSet(proxies);\n\t\t\t}\n\t\t}\n\n\t\tprivate <T> Queue<T> proxyQueue(AuthorizationProxyFactory proxyFactory, Queue<T> queue) {\n\t\t\tQueue<T> proxies = new LinkedList<>();\n\t\t\tfor (T toProxy : queue) {\n\t\t\t\tproxies.add(proxyCast(proxyFactory, toProxy));\n\t\t\t}\n\t\t\tqueue.clear();\n\t\t\tqueue.addAll(proxies);\n\t\t\treturn proxies;\n\t\t}\n\n\t\tprivate <T> List<T> proxyList(AuthorizationProxyFactory proxyFactory, List<T> list) {\n\t\t\tList<T> proxies = new ArrayList<>(list.size());\n\t\t\tfor (T toProxy : list) {\n\t\t\t\tproxies.add(proxyCast(proxyFactory, toProxy));\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tlist.clear();\n\t\t\t\tlist.addAll(proxies);\n\t\t\t\treturn proxies;\n\t\t\t}\n\t\t\tcatch (UnsupportedOperationException ex) {\n\t\t\t\treturn Collections.unmodifiableList(proxies);\n\t\t\t}\n\t\t}\n\n\t\tprivate Object[] proxyArray(AuthorizationProxyFactory proxyFactory, Object[] objects) {\n\t\t\tList<Object> retain = new ArrayList<>(objects.length);\n\t\t\tfor (Object object : objects) {\n\t\t\t\tretain.add(proxyFactory.proxy(object));\n\t\t\t}\n\t\t\tObject[] proxies = (Object[]) Array.newInstance(objects.getClass().getComponentType(), retain.size());\n\t\t\tfor (int i = 0; i < retain.size(); i++) {\n\t\t\t\tproxies[i] = retain.get(i);\n\t\t\t}\n\t\t\treturn proxies;\n\t\t}\n\n\t\tprivate <K, V> SortedMap<K, V> proxySortedMap(AuthorizationProxyFactory proxyFactory, SortedMap<K, V> entries) {\n\t\t\tSortedMap<K, V> proxies = new TreeMap<>(entries.comparator());\n\t\t\tfor (Map.Entry<K, V> entry : entries.entrySet()) {\n\t\t\t\tproxies.put(entry.getKey(), proxyCast(proxyFactory, entry.getValue()));\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tentries.clear();\n\t\t\t\tentries.putAll(proxies);\n\t\t\t\treturn entries;\n\t\t\t}\n\t\t\tcatch (UnsupportedOperationException ex) {\n\t\t\t\treturn Collections.unmodifiableSortedMap(proxies);\n\t\t\t}\n\t\t}\n\n\t\tprivate <K, V> Map<K, V> proxyMap(AuthorizationProxyFactory proxyFactory, Map<K, V> entries) {\n\t\t\tMap<K, V> proxies = new LinkedHashMap<>(entries.size());\n\t\t\tfor (Map.Entry<K, V> entry : entries.entrySet()) {\n\t\t\t\tproxies.put(entry.getKey(), proxyCast(proxyFactory, entry.getValue()));\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tentries.clear();\n\t\t\t\tentries.putAll(proxies);\n\t\t\t\treturn entries;\n\t\t\t}\n\t\t\tcatch (UnsupportedOperationException ex) {\n\t\t\t\treturn Collections.unmodifiableMap(proxies);\n\t\t\t}\n\t\t}\n\n\t\tprivate Stream<?> proxyStream(AuthorizationProxyFactory proxyFactory, Stream<?> stream) {\n\t\t\treturn stream.map(proxyFactory::proxy).onClose(stream::close);\n\t\t}\n\n\t\t@SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n\t\tprivate Optional<?> proxyOptional(AuthorizationProxyFactory proxyFactory, Optional<?> optional) {\n\t\t\treturn optional.map(proxyFactory::proxy);\n\t\t}\n\n\t\tprivate Supplier<?> proxySupplier(AuthorizationProxyFactory proxyFactory, Supplier<?> supplier) {\n\t\t\treturn () -> proxyFactory.proxy(supplier.get());\n\t\t}\n\n\t}\n\n\tprivate static class ReactiveTypeVisitor implements TargetVisitor {\n\n\t\t@Override\n\t\t@SuppressWarnings(\"ReactiveStreamsUnusedPublisher\")\n\t\tpublic @Nullable Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {\n\t\t\tif (target instanceof Mono<?> mono) {\n\t\t\t\treturn proxyMono(proxyFactory, mono);\n\t\t\t}\n\t\t\tif (target instanceof Flux<?> flux) {\n\t\t\t\treturn proxyFlux(proxyFactory, flux);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\t\tprivate Mono<?> proxyMono(AuthorizationProxyFactory proxyFactory, Mono<?> mono) {\n\t\t\treturn mono.mapNotNull(proxyFactory::proxy);\n\t\t}\n\n\t\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\t\tprivate Flux<?> proxyFlux(AuthorizationProxyFactory proxyFactory, Flux<?> flux) {\n\t\t\treturn flux.mapNotNull(proxyFactory::proxy);\n\t\t}\n\n\t}\n\n\tprivate static final class AuthorizationProxyMethodInterceptor implements AuthorizationAdvisor {\n\n\t\tprivate static final Method GET_TARGET_METHOD = ClassUtils.getMethod(AuthorizationProxy.class,\n\t\t\t\t\"toAuthorizedTarget\");\n\n\t\t@Override\n\t\tpublic @Nullable Object invoke(MethodInvocation invocation) throws Throwable {\n\t\t\tif (invocation.getMethod().equals(GET_TARGET_METHOD)) {\n\t\t\t\treturn invocation.getThis();\n\t\t\t}\n\t\t\treturn invocation.proceed();\n\t\t}\n\n\t\t@Override\n\t\tpublic Pointcut getPointcut() {\n\t\t\treturn Pointcut.TRUE;\n\t\t}\n\n\t\t@Override\n\t\tpublic Advice getAdvice() {\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getOrder() {\n\t\t\treturn 0;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationInterceptorsOrder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.springframework.aop.Advisor;\n\n/**\n * Ordering of Spring Security's authorization {@link Advisor}s\n *\n * @author Josh Cummings\n * @since 5.6\n * @see PreAuthorizeAuthorizationManager\n * @see PostAuthorizeAuthorizationManager\n * @see SecuredAuthorizationManager\n * @see Jsr250AuthorizationManager\n */\npublic enum AuthorizationInterceptorsOrder {\n\n\tFIRST(Integer.MIN_VALUE),\n\n\t/**\n\t * {@link PreFilterAuthorizationMethodInterceptor}\n\t */\n\tPRE_FILTER,\n\n\tPRE_AUTHORIZE,\n\n\tSECURED,\n\n\tJSR250,\n\n\tSECURE_RESULT(450),\n\n\tPOST_AUTHORIZE(500),\n\n\t/**\n\t * {@link PostFilterAuthorizationMethodInterceptor}\n\t */\n\tPOST_FILTER(600),\n\n\tLAST(Integer.MAX_VALUE);\n\n\tprivate static final int INTERVAL = 100;\n\n\tprivate final int order;\n\n\tAuthorizationInterceptorsOrder() {\n\t\tthis.order = ordinal() * INTERVAL;\n\t}\n\n\tAuthorizationInterceptorsOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link MethodInterceptor} which can determine if an {@link Authentication} has access\n * to the result of an {@link MethodInvocation} using an {@link AuthorizationManager}\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class AuthorizationManagerAfterMethodInterceptor implements AuthorizationAdvisor {\n\n\tprivate Supplier<SecurityContextHolderStrategy> securityContextHolderStrategy = SecurityContextHolder::getContextHolderStrategy;\n\n\tprivate final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate final Pointcut pointcut;\n\n\tprivate final AuthorizationManager<MethodInvocationResult> authorizationManager;\n\n\tprivate final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();\n\n\tprivate int order;\n\n\tprivate AuthorizationEventPublisher eventPublisher = new NoOpAuthorizationEventPublisher();\n\n\t/**\n\t * Creates an instance.\n\t * @param pointcut the {@link Pointcut} to use\n\t * @param authorizationManager the {@link AuthorizationManager} to use\n\t */\n\tpublic AuthorizationManagerAfterMethodInterceptor(Pointcut pointcut,\n\t\t\tAuthorizationManager<MethodInvocationResult> authorizationManager) {\n\t\tAssert.notNull(pointcut, \"pointcut cannot be null\");\n\t\tAssert.notNull(authorizationManager, \"authorizationManager cannot be null\");\n\t\tthis.pointcut = pointcut;\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link PostAuthorize} annotation\n\t * @return the interceptor\n\t */\n\tpublic static AuthorizationManagerAfterMethodInterceptor postAuthorize() {\n\t\treturn postAuthorize(new PostAuthorizeAuthorizationManager());\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link PostAuthorize} annotation\n\t * @param authorizationManager the {@link PostAuthorizeAuthorizationManager} to use\n\t * @return the interceptor\n\t */\n\tpublic static AuthorizationManagerAfterMethodInterceptor postAuthorize(\n\t\t\tPostAuthorizeAuthorizationManager authorizationManager) {\n\t\tAuthorizationManagerAfterMethodInterceptor interceptor = new AuthorizationManagerAfterMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), authorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link PostAuthorize} annotation\n\t * @param authorizationManager the {@link AuthorizationManager} to use\n\t * @return the interceptor\n\t * @since 6.0\n\t */\n\tpublic static AuthorizationManagerAfterMethodInterceptor postAuthorize(\n\t\t\tAuthorizationManager<MethodInvocationResult> authorizationManager) {\n\t\tAuthorizationManagerAfterMethodInterceptor interceptor = new AuthorizationManagerAfterMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), authorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Determine if an {@link Authentication} has access to the {@link MethodInvocation}\n\t * using the {@link AuthorizationManager}.\n\t * @param mi the {@link MethodInvocation} to check\n\t * @throws AccessDeniedException if access is not granted\n\t */\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {\n\t\tObject result;\n\t\ttry {\n\t\t\tresult = mi.proceed();\n\t\t}\n\t\tcatch (AuthorizationDeniedException ex) {\n\t\t\tif (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {\n\t\t\t\treturn handler.handleDeniedInvocation(mi, ex);\n\t\t\t}\n\t\t\treturn this.defaultHandler.handleDeniedInvocation(mi, ex);\n\t\t}\n\t\treturn attemptAuthorization(mi, result);\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * Use this {@link AuthorizationEventPublisher} to publish the\n\t * {@link AuthorizationManager} result.\n\t * @param eventPublisher\n\t * @since 5.7\n\t */\n\tpublic void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {\n\t\tAssert.notNull(eventPublisher, \"eventPublisher cannot be null\");\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {\n\t\tthis.securityContextHolderStrategy = () -> strategy;\n\t}\n\n\tprivate @Nullable Object attemptAuthorization(MethodInvocation mi, @Nullable Object result) {\n\t\tthis.logger.debug(LogMessage.of(() -> \"Authorizing method invocation \" + mi));\n\t\tMethodInvocationResult object = new MethodInvocationResult(mi, result);\n\t\tAuthorizationResult authorizationResult = this.authorizationManager.authorize(this::getAuthentication, object);\n\t\tif (authorizationResult != null) {\n\t\t\tthis.eventPublisher.publishAuthorizationEvent(this::getAuthentication, object, authorizationResult);\n\t\t}\n\t\tif (authorizationResult != null && !authorizationResult.isGranted()) {\n\t\t\tthis.logger.debug(LogMessage.of(() -> \"Failed to authorize \" + mi + \" with authorization manager \"\n\t\t\t\t\t+ this.authorizationManager + \" and authorizationResult \" + authorizationResult));\n\t\t\treturn handlePostInvocationDenied(object, authorizationResult);\n\t\t}\n\t\tthis.logger.debug(LogMessage.of(() -> \"Authorized method invocation \" + mi));\n\t\treturn result;\n\t}\n\n\tprivate @Nullable Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationResult result) {\n\t\tif (this.authorizationManager instanceof MethodAuthorizationDeniedHandler deniedHandler) {\n\t\t\treturn deniedHandler.handleDeniedInvocationResult(mi, result);\n\t\t}\n\t\treturn this.defaultHandler.handleDeniedInvocationResult(mi, result);\n\t}\n\n\tprivate Authentication getAuthentication() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.get().getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tthrow new AuthenticationCredentialsNotFoundException(\n\t\t\t\t\t\"An Authentication object was not found in the SecurityContext\");\n\t\t}\n\t\treturn authentication;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\nimport java.util.function.Function;\n\nimport kotlinx.coroutines.reactive.ReactiveFlowKt;\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.core.publisher.Signal;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.core.KotlinDetector;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link MethodInterceptor} which can determine if an {@link Authentication} has access\n * to the returned object from the {@link MethodInvocation} using the configured\n * {@link ReactiveAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\npublic final class AuthorizationManagerAfterReactiveMethodInterceptor implements AuthorizationAdvisor {\n\n\tprivate static final String COROUTINES_FLOW_CLASS_NAME = \"kotlinx.coroutines.flow.Flow\";\n\n\tprivate static final int RETURN_TYPE_METHOD_PARAMETER_INDEX = -1;\n\n\tprivate final Pointcut pointcut;\n\n\tprivate final ReactiveAuthorizationManager<MethodInvocationResult> authorizationManager;\n\n\tprivate int order = AuthorizationInterceptorsOrder.LAST.getOrder();\n\n\tprivate final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();\n\n\t/**\n\t * Creates an instance for the {@link PostAuthorize} annotation.\n\t * @return the {@link AuthorizationManagerAfterReactiveMethodInterceptor} to use\n\t */\n\tpublic static AuthorizationManagerAfterReactiveMethodInterceptor postAuthorize() {\n\t\treturn postAuthorize(new PostAuthorizeReactiveAuthorizationManager());\n\t}\n\n\t/**\n\t * Creates an instance for the {@link PostAuthorize} annotation.\n\t * @param authorizationManager the {@link ReactiveAuthorizationManager} to use\n\t * @return the {@link AuthorizationManagerAfterReactiveMethodInterceptor} to use\n\t */\n\tpublic static AuthorizationManagerAfterReactiveMethodInterceptor postAuthorize(\n\t\t\tReactiveAuthorizationManager<MethodInvocationResult> authorizationManager) {\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), authorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Creates an instance.\n\t * @param pointcut the {@link Pointcut} to use\n\t * @param authorizationManager the {@link ReactiveAuthorizationManager} to use\n\t */\n\tpublic AuthorizationManagerAfterReactiveMethodInterceptor(Pointcut pointcut,\n\t\t\tReactiveAuthorizationManager<MethodInvocationResult> authorizationManager) {\n\t\tAssert.notNull(pointcut, \"pointcut cannot be null\");\n\t\tAssert.notNull(authorizationManager, \"authorizationManager cannot be null\");\n\t\tthis.pointcut = pointcut;\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t/**\n\t * Determines if an {@link Authentication} has access to the returned object from the\n\t * {@link MethodInvocation} using the configured {@link ReactiveAuthorizationManager}.\n\t * @param mi the {@link MethodInvocation} to use\n\t * @return the {@link Publisher} from the {@link MethodInvocation} or a\n\t * {@link Publisher} error if access is denied\n\t */\n\t@Override\n\tpublic Object invoke(MethodInvocation mi) throws Throwable {\n\t\tMethod method = mi.getMethod();\n\t\tClass<?> type = method.getReturnType();\n\t\tboolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);\n\t\tboolean hasFlowReturnType = COROUTINES_FLOW_CLASS_NAME\n\t\t\t.equals(new MethodParameter(method, RETURN_TYPE_METHOD_PARAMETER_INDEX).getParameterType().getName());\n\t\tboolean hasReactiveReturnType = Publisher.class.isAssignableFrom(type) || isSuspendingFunction\n\t\t\t\t|| hasFlowReturnType;\n\t\tAssert.state(hasReactiveReturnType,\n\t\t\t\t() -> \"The returnType \" + type + \" on \" + method\n\t\t\t\t\t\t+ \" must return an instance of org.reactivestreams.Publisher \"\n\t\t\t\t\t\t+ \"(for example, a Mono or Flux) or the function must be a Kotlin coroutine \"\n\t\t\t\t\t\t+ \"in order to support Reactor Context\");\n\t\tMono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();\n\t\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\t\tFunction<Signal<?>, Mono<?>> postAuthorize = (signal) -> {\n\t\t\tif (signal.isOnComplete()) {\n\t\t\t\treturn Mono.empty();\n\t\t\t}\n\t\t\tif (!signal.hasError()) {\n\t\t\t\treturn postAuthorize(authentication, mi, signal.get());\n\t\t\t}\n\t\t\tif (signal.getThrowable() instanceof AuthorizationDeniedException denied) {\n\t\t\t\treturn postProcess(denied, mi);\n\t\t\t}\n\t\t\t// getThrowable must be non-null because hasError() is true\n\t\t\treturn Mono.error(signal.getThrowable());\n\t\t};\n\t\tReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);\n\t\tif (hasFlowReturnType) {\n\t\t\tif (isSuspendingFunction) {\n\t\t\t\tPublisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);\n\t\t\t\tif (publisher == null) {\n\t\t\t\t\treturn Flux.empty();\n\t\t\t\t}\n\t\t\t\treturn Flux.from(publisher).materialize().flatMap(postAuthorize);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tAssert.state(adapter != null, () -> \"The returnType \" + type + \" on \" + method\n\t\t\t\t\t\t+ \" must have a org.springframework.core.ReactiveAdapter registered\");\n\t\t\t\tFlux<?> response = Flux.defer(() -> adapter.toPublisher(ReactiveMethodInvocationUtils.proceed(mi)))\n\t\t\t\t\t.materialize()\n\t\t\t\t\t.flatMap(postAuthorize);\n\t\t\t\treturn KotlinDelegate.asFlow(response);\n\t\t\t}\n\t\t}\n\t\tPublisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);\n\t\tif (publisher == null) {\n\t\t\treturn Flux.empty();\n\t\t}\n\t\tif (isMultiValue(type, adapter)) {\n\t\t\tFlux<?> flux = Flux.from(publisher).materialize().flatMap(postAuthorize);\n\t\t\treturn (adapter != null) ? adapter.fromPublisher(flux) : flux;\n\t\t}\n\t\tMono<?> mono = Mono.from(publisher).materialize().flatMap(postAuthorize);\n\t\treturn (adapter != null) ? adapter.fromPublisher(mono) : mono;\n\t}\n\n\tprivate boolean isMultiValue(Class<?> returnType, @Nullable ReactiveAdapter adapter) {\n\t\tif (Flux.class.isAssignableFrom(returnType)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn adapter != null && adapter.isMultiValue();\n\t}\n\n\tprivate Mono<Object> postAuthorize(Mono<Authentication> authentication, MethodInvocation mi,\n\t\t\t@Nullable Object result) {\n\t\tMethodInvocationResult invocationResult = new MethodInvocationResult(mi, result);\n\t\treturn this.authorizationManager.authorize(authentication, invocationResult)\n\t\t\t.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))\n\t\t\t.flatMap((decision) -> postProcess(decision, invocationResult));\n\t}\n\n\tprivate Mono<Object> postProcess(AuthorizationResult decision, MethodInvocationResult methodInvocationResult) {\n\t\tif (decision.isGranted()) {\n\t\t\treturn Mono.justOrEmpty(methodInvocationResult.getResult());\n\t\t}\n\t\treturn Mono.fromSupplier(() -> {\n\t\t\tif (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {\n\t\t\t\treturn handler.handleDeniedInvocationResult(methodInvocationResult, decision);\n\t\t\t}\n\t\t\treturn this.defaultHandler.handleDeniedInvocationResult(methodInvocationResult, decision);\n\t\t}).flatMap((processedResult) -> {\n\t\t\tif (Mono.class.isAssignableFrom(processedResult.getClass())) {\n\t\t\t\treturn (Mono<?>) processedResult;\n\t\t\t}\n\t\t\treturn Mono.justOrEmpty(processedResult);\n\t\t});\n\t}\n\n\tprivate Mono<Object> postProcess(AuthorizationResult decision, MethodInvocation methodInvocation) {\n\t\treturn Mono.fromSupplier(() -> {\n\t\t\tif (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {\n\t\t\t\treturn handler.handleDeniedInvocation(methodInvocation, decision);\n\t\t\t}\n\t\t\treturn this.defaultHandler.handleDeniedInvocation(methodInvocation, decision);\n\t\t}).flatMap((processedResult) -> {\n\t\t\tif (Mono.class.isAssignableFrom(processedResult.getClass())) {\n\t\t\t\treturn (Mono<?>) processedResult;\n\t\t\t}\n\t\t\treturn Mono.justOrEmpty(processedResult);\n\t\t});\n\t}\n\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * Inner class to avoid a hard dependency on Kotlin at runtime.\n\t */\n\tprivate static class KotlinDelegate {\n\n\t\tprivate static Object asFlow(Publisher<?> publisher) {\n\t\t\treturn ReactiveFlowKt.asFlow(publisher);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport jakarta.annotation.security.DenyAll;\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link MethodInterceptor} which uses a {@link AuthorizationManager} to determine if\n * an {@link Authentication} may invoke the given {@link MethodInvocation}\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class AuthorizationManagerBeforeMethodInterceptor implements AuthorizationAdvisor {\n\n\tprivate Supplier<SecurityContextHolderStrategy> securityContextHolderStrategy = SecurityContextHolder::getContextHolderStrategy;\n\n\tprivate final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate final Pointcut pointcut;\n\n\tprivate final AuthorizationManager<MethodInvocation> authorizationManager;\n\n\tprivate final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();\n\n\tprivate int order = AuthorizationInterceptorsOrder.FIRST.getOrder();\n\n\tprivate AuthorizationEventPublisher eventPublisher = new NoOpAuthorizationEventPublisher();\n\n\t/**\n\t * Creates an instance.\n\t * @param pointcut the {@link Pointcut} to use\n\t * @param authorizationManager the {@link AuthorizationManager} to use\n\t */\n\tpublic AuthorizationManagerBeforeMethodInterceptor(Pointcut pointcut,\n\t\t\tAuthorizationManager<MethodInvocation> authorizationManager) {\n\t\tAssert.notNull(pointcut, \"pointcut cannot be null\");\n\t\tAssert.notNull(authorizationManager, \"authorizationManager cannot be null\");\n\t\tthis.pointcut = pointcut;\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link PreAuthorize} annotation\n\t * @return the interceptor\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor preAuthorize() {\n\t\treturn preAuthorize(new PreAuthorizeAuthorizationManager());\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link PreAuthorize} annotation\n\t * @param authorizationManager the {@link PreAuthorizeAuthorizationManager} to use\n\t * @return the interceptor\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor preAuthorize(\n\t\t\tPreAuthorizeAuthorizationManager authorizationManager) {\n\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), authorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link PreAuthorize} annotation\n\t * @param authorizationManager the {@link AuthorizationManager} to use\n\t * @return the interceptor\n\t * @since 6.0\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor preAuthorize(\n\t\t\tAuthorizationManager<MethodInvocation> authorizationManager) {\n\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), authorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link Secured} annotation\n\t * @return the interceptor\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor secured() {\n\t\treturn secured(new SecuredAuthorizationManager());\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link Secured} annotation\n\t * @param authorizationManager the {@link SecuredAuthorizationManager} to use\n\t * @return the interceptor\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor secured(\n\t\t\tSecuredAuthorizationManager authorizationManager) {\n\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(Secured.class), authorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.SECURED.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Creates an interceptor for the {@link Secured} annotation\n\t * @param authorizationManager the {@link AuthorizationManager} to use\n\t * @return the interceptor\n\t * @since 6.0\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor secured(\n\t\t\tAuthorizationManager<MethodInvocation> authorizationManager) {\n\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(Secured.class), authorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.SECURED.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Creates an interceptor for the JSR-250 annotations\n\t * @return the interceptor\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor jsr250() {\n\t\treturn jsr250(new Jsr250AuthorizationManager());\n\t}\n\n\t/**\n\t * Creates an interceptor for the JSR-250 annotations\n\t * @param authorizationManager the {@link Jsr250AuthorizationManager} to use\n\t * @return the interceptor\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor jsr250(Jsr250AuthorizationManager authorizationManager) {\n\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(RolesAllowed.class, DenyAll.class, PermitAll.class),\n\t\t\t\tauthorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.JSR250.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Creates an interceptor for the JSR-250 annotations\n\t * @param authorizationManager the {@link AuthorizationManager} to use\n\t * @return the interceptor\n\t * @since 6.0\n\t */\n\tpublic static AuthorizationManagerBeforeMethodInterceptor jsr250(\n\t\t\tAuthorizationManager<MethodInvocation> authorizationManager) {\n\t\tAuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(RolesAllowed.class, DenyAll.class, PermitAll.class),\n\t\t\t\tauthorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.JSR250.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Determine if an {@link Authentication} has access to the {@link MethodInvocation}\n\t * using the configured {@link AuthorizationManager}.\n\t * @param mi the {@link MethodInvocation} to check\n\t * @throws AccessDeniedException if access is not granted\n\t */\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {\n\t\treturn attemptAuthorization(mi);\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * Use this {@link AuthorizationEventPublisher} to publish the\n\t * {@link AuthorizationManager} result.\n\t * @param eventPublisher\n\t * @since 5.7\n\t */\n\tpublic void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {\n\t\tAssert.notNull(eventPublisher, \"eventPublisher cannot be null\");\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.securityContextHolderStrategy = () -> securityContextHolderStrategy;\n\t}\n\n\tprivate @Nullable Object attemptAuthorization(MethodInvocation mi) throws Throwable {\n\t\tthis.logger.debug(LogMessage.of(() -> \"Authorizing method invocation \" + mi));\n\t\tAuthorizationResult result;\n\t\ttry {\n\t\t\tresult = this.authorizationManager.authorize(this::getAuthentication, mi);\n\t\t}\n\t\tcatch (AuthorizationDeniedException denied) {\n\t\t\treturn handle(mi, denied);\n\t\t}\n\t\tif (result != null) {\n\t\t\tthis.eventPublisher.publishAuthorizationEvent(this::getAuthentication, mi, result);\n\t\t}\n\t\tif (result != null && !result.isGranted()) {\n\t\t\tthis.logger.debug(LogMessage.of(() -> \"Failed to authorize \" + mi + \" with authorization manager \"\n\t\t\t\t\t+ this.authorizationManager + \" and result \" + result));\n\t\t\treturn handle(mi, result);\n\t\t}\n\t\tthis.logger.debug(LogMessage.of(() -> \"Authorized method invocation \" + mi));\n\t\treturn proceed(mi);\n\t}\n\n\tprivate @Nullable Object proceed(MethodInvocation mi) throws Throwable {\n\t\ttry {\n\t\t\treturn mi.proceed();\n\t\t}\n\t\tcatch (AuthorizationDeniedException ex) {\n\t\t\tif (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {\n\t\t\t\treturn handler.handleDeniedInvocation(mi, ex);\n\t\t\t}\n\t\t\treturn this.defaultHandler.handleDeniedInvocation(mi, ex);\n\t\t}\n\t}\n\n\tprivate @Nullable Object handle(MethodInvocation mi, AuthorizationDeniedException denied) {\n\t\tif (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {\n\t\t\treturn handler.handleDeniedInvocation(mi, denied);\n\t\t}\n\t\treturn this.defaultHandler.handleDeniedInvocation(mi, denied);\n\t}\n\n\tprivate @Nullable Object handle(MethodInvocation mi, AuthorizationResult result) {\n\t\tif (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {\n\t\t\treturn handler.handleDeniedInvocation(mi, result);\n\t\t}\n\t\treturn this.defaultHandler.handleDeniedInvocation(mi, result);\n\t}\n\n\tprivate Authentication getAuthentication() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.get().getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tthrow new AuthenticationCredentialsNotFoundException(\n\t\t\t\t\t\"An Authentication object was not found in the SecurityContext\");\n\t\t}\n\t\treturn authentication;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\n\nimport kotlinx.coroutines.reactive.ReactiveFlowKt;\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.core.KotlinDetector;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link MethodInterceptor} which can determine if an {@link Authentication} has access\n * to the {@link MethodInvocation} using the configured\n * {@link ReactiveAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.8\n */\npublic final class AuthorizationManagerBeforeReactiveMethodInterceptor implements AuthorizationAdvisor {\n\n\tprivate static final String COROUTINES_FLOW_CLASS_NAME = \"kotlinx.coroutines.flow.Flow\";\n\n\tprivate static final int RETURN_TYPE_METHOD_PARAMETER_INDEX = -1;\n\n\tprivate final Pointcut pointcut;\n\n\tprivate final ReactiveAuthorizationManager<MethodInvocation> authorizationManager;\n\n\tprivate int order = AuthorizationInterceptorsOrder.FIRST.getOrder();\n\n\tprivate final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();\n\n\t/**\n\t * Creates an instance for the {@link PreAuthorize} annotation.\n\t * @return the {@link AuthorizationManagerBeforeReactiveMethodInterceptor} to use\n\t */\n\tpublic static AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorize() {\n\t\treturn preAuthorize(new PreAuthorizeReactiveAuthorizationManager());\n\t}\n\n\t/**\n\t * Creates an instance for the {@link PreAuthorize} annotation.\n\t * @param authorizationManager the {@link ReactiveAuthorizationManager} to use\n\t * @return the {@link AuthorizationManagerBeforeReactiveMethodInterceptor} to use\n\t */\n\tpublic static AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorize(\n\t\t\tReactiveAuthorizationManager<MethodInvocation> authorizationManager) {\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tAuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), authorizationManager);\n\t\tinterceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder());\n\t\treturn interceptor;\n\t}\n\n\t/**\n\t * Creates an instance.\n\t * @param pointcut the {@link Pointcut} to use\n\t * @param authorizationManager the {@link ReactiveAuthorizationManager} to use\n\t */\n\tpublic AuthorizationManagerBeforeReactiveMethodInterceptor(Pointcut pointcut,\n\t\t\tReactiveAuthorizationManager<MethodInvocation> authorizationManager) {\n\t\tAssert.notNull(pointcut, \"pointcut cannot be null\");\n\t\tAssert.notNull(authorizationManager, \"authorizationManager cannot be null\");\n\t\tthis.pointcut = pointcut;\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t/**\n\t * Determines if an {@link Authentication} has access to the {@link MethodInvocation}\n\t * using the configured {@link ReactiveAuthorizationManager}.\n\t * @param mi the {@link MethodInvocation} to use\n\t * @return the {@link Publisher} from the {@link MethodInvocation} or a\n\t * {@link Publisher} error if access is denied\n\t */\n\t@Override\n\tpublic Object invoke(MethodInvocation mi) throws Throwable {\n\t\tMethod method = mi.getMethod();\n\t\tClass<?> type = method.getReturnType();\n\t\tboolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);\n\t\tboolean hasFlowReturnType = COROUTINES_FLOW_CLASS_NAME\n\t\t\t.equals(new MethodParameter(method, RETURN_TYPE_METHOD_PARAMETER_INDEX).getParameterType().getName());\n\t\tboolean hasReactiveReturnType = Publisher.class.isAssignableFrom(type) || isSuspendingFunction\n\t\t\t\t|| hasFlowReturnType;\n\t\tAssert.state(hasReactiveReturnType,\n\t\t\t\t() -> \"The returnType \" + type + \" on \" + method\n\t\t\t\t\t\t+ \" must return an instance of org.reactivestreams.Publisher \"\n\t\t\t\t\t\t+ \"(for example, a Mono or Flux) or the function must be a Kotlin coroutine \"\n\t\t\t\t\t\t+ \"in order to support Reactor Context\");\n\t\tReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);\n\t\tif (hasFlowReturnType) {\n\t\t\tif (isSuspendingFunction) {\n\t\t\t\treturn preAuthorized(mi, Flux.defer(() -> ReactiveMethodInvocationUtils.proceed(mi)));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tAssert.state(adapter != null, () -> \"The returnType \" + type + \" on \" + method\n\t\t\t\t\t\t+ \" must have a org.springframework.core.ReactiveAdapter registered\");\n\t\t\t\tFlux<Object> response = preAuthorized(mi,\n\t\t\t\t\t\tFlux.defer(() -> adapter.toPublisher(ReactiveMethodInvocationUtils.proceed(mi))));\n\t\t\t\treturn KotlinDelegate.asFlow(response);\n\t\t\t}\n\t\t}\n\t\tif (isMultiValue(type, adapter)) {\n\t\t\tFlux<?> result = preAuthorized(mi, Flux.defer(() -> ReactiveMethodInvocationUtils.proceed(mi)));\n\t\t\treturn (adapter != null) ? adapter.fromPublisher(result) : result;\n\t\t}\n\t\tMono<?> result = preAuthorized(mi, Mono.defer(() -> ReactiveMethodInvocationUtils.proceed(mi)));\n\t\treturn (adapter != null) ? adapter.fromPublisher(result) : result;\n\t}\n\n\tprivate Flux<Object> preAuthorized(MethodInvocation mi, Flux<Object> mapping) {\n\t\tMono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();\n\t\treturn this.authorizationManager.authorize(authentication, mi)\n\t\t\t.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))\n\t\t\t.flatMapMany((decision) -> {\n\t\t\t\tif (decision.isGranted()) {\n\t\t\t\t\treturn mapping.onErrorResume(AuthorizationDeniedException.class,\n\t\t\t\t\t\t\t(deniedEx) -> postProcess(deniedEx, mi));\n\t\t\t\t}\n\t\t\t\treturn postProcess(decision, mi);\n\t\t\t});\n\t}\n\n\tprivate Mono<Object> preAuthorized(MethodInvocation mi, Mono<Object> mapping) {\n\t\tMono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();\n\t\treturn this.authorizationManager.authorize(authentication, mi)\n\t\t\t.switchIfEmpty(Mono.just(new AuthorizationDecision(false)))\n\t\t\t.flatMap((decision) -> {\n\t\t\t\tif (decision.isGranted()) {\n\t\t\t\t\treturn mapping.onErrorResume(AuthorizationDeniedException.class,\n\t\t\t\t\t\t\t(deniedEx) -> postProcess(deniedEx, mi));\n\t\t\t\t}\n\t\t\t\treturn postProcess(decision, mi);\n\t\t\t});\n\t}\n\n\tprivate Mono<Object> postProcess(AuthorizationResult decision, MethodInvocation mi) {\n\t\treturn Mono.fromSupplier(() -> {\n\t\t\tif (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) {\n\t\t\t\treturn handler.handleDeniedInvocation(mi, decision);\n\t\t\t}\n\t\t\treturn this.defaultHandler.handleDeniedInvocation(mi, decision);\n\t\t}).flatMap((result) -> {\n\t\t\tif (Mono.class.isAssignableFrom(result.getClass())) {\n\t\t\t\treturn (Mono<?>) result;\n\t\t\t}\n\t\t\treturn Mono.justOrEmpty(result);\n\t\t});\n\t}\n\n\tprivate boolean isMultiValue(Class<?> returnType, @Nullable ReactiveAdapter adapter) {\n\t\tif (Flux.class.isAssignableFrom(returnType)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn adapter != null && adapter.isMultiValue();\n\t}\n\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * Inner class to avoid a hard dependency on Kotlin at runtime.\n\t */\n\tprivate static class KotlinDelegate {\n\n\t\tprivate static Object asFlow(Publisher<?> publisher) {\n\t\t\treturn ReactiveFlowKt.asFlow(publisher);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodPointcuts.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Annotation;\nimport java.util.Arrays;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.support.ComposablePointcut;\nimport org.springframework.aop.support.Pointcuts;\nimport org.springframework.aop.support.annotation.AnnotationMatchingPointcut;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.access.prepost.PreFilter;\n\n/**\n * @author Josh Cummings\n * @author Evgeniy Cheban\n */\nfinal class AuthorizationMethodPointcuts {\n\n\tstatic Pointcut forAllAnnotations() {\n\t\treturn forAnnotations(PreFilter.class, PreAuthorize.class, PostFilter.class, PostAuthorize.class);\n\t}\n\n\t@SafeVarargs\n\tstatic Pointcut forAnnotations(Class<? extends Annotation>... annotations) {\n\t\tComposablePointcut pointcut = null;\n\t\tfor (Class<? extends Annotation> annotation : annotations) {\n\t\t\tif (pointcut == null) {\n\t\t\t\tpointcut = new ComposablePointcut(classOrMethod(annotation));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tpointcut.union(classOrMethod(annotation));\n\t\t\t}\n\t\t}\n\t\tif (pointcut == null) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Unable to find a pointcut for annotations \" + Arrays.toString(annotations));\n\t\t}\n\t\treturn pointcut;\n\t}\n\n\tprivate static Pointcut classOrMethod(Class<? extends Annotation> annotation) {\n\t\treturn Pointcuts.union(new AnnotationMatchingPointcut(null, annotation, true),\n\t\t\t\tnew AnnotationMatchingPointcut(annotation, true));\n\t}\n\n\tprivate AuthorizationMethodPointcuts() {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizationProxy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.springframework.aop.RawTargetAccess;\n\n/**\n * An interface that is typically implemented by Spring Security's AOP support to identify\n * an instance as being proxied by Spring Security.\n *\n * <p>\n * Also provides a way to access the underlying target object, handy for working with the\n * object without invoking the authorization rules.\n *\n * <p>\n * This can be helpful when taking working with a proxied object and needing to hand it to\n * a layer of the application that should not invoke the rules, like a Spring Data\n * repository:\n *\n * <pre>\n *\tMyObject object = this.objectRepository.findById(123L); // now an authorized proxy\n *  object.setProtectedValue(...); // only works if authorized\n *  if (object instanceof AuthorizationProxy proxy) {\n *  \t// Spring Data wants to be able to persist the entire object\n *  \t// so we'll remove the proxy\n *      object = (MyObject) proxy.toAuthorizedTarget();\n *  }\n *  this.objectRepository.save(object);\n * </pre>\n *\n * @author Josh Cummings\n * @since 6.4\n */\npublic interface AuthorizationProxy extends RawTargetAccess {\n\n\t/**\n\t * Access underlying target object\n\t * @return the target object\n\t */\n\tObject toAuthorizedTarget();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizeReturnObject.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Wraps Spring Security method authorization advice around the return object of any\n * method this annotation is applied to.\n *\n * <p>\n * Placing this at the class level is semantically identical to placing it on each method\n * in that class.\n * </p>\n *\n * @author Josh Cummings\n * @since 6.3\n * @see AuthorizeReturnObjectMethodInterceptor\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\npublic @interface AuthorizeReturnObject {\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/AuthorizeReturnObjectMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\nimport java.util.function.Predicate;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.support.Pointcuts;\nimport org.springframework.aop.support.StaticMethodMatcherPointcut;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * A method interceptor that applies the given {@link AuthorizationProxyFactory} to any\n * return value annotated with {@link AuthorizeReturnObject}\n *\n * @author Josh Cummings\n * @since 6.3\n * @see AuthorizationAdvisorProxyFactory\n */\npublic final class AuthorizeReturnObjectMethodInterceptor implements AuthorizationAdvisor {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable AuthorizationProxyFactory authorizationProxyFactory;\n\n\tprivate Pointcut pointcut = Pointcuts.intersection(\n\t\t\tnew MethodReturnTypePointcut(Predicate.not(ClassUtils::isVoidType)),\n\t\t\tAuthorizationMethodPointcuts.forAnnotations(AuthorizeReturnObject.class));\n\n\tprivate int order = AuthorizationInterceptorsOrder.SECURE_RESULT.getOrder();\n\n\t/**\n\t * Construct the interceptor\n\t *\n\t * <p>\n\t * Using this constructor requires you to specify\n\t * {@link #setAuthorizationProxyFactory}\n\t * </p>\n\t * @since 6.5\n\t */\n\tpublic AuthorizeReturnObjectMethodInterceptor() {\n\n\t}\n\n\tpublic AuthorizeReturnObjectMethodInterceptor(AuthorizationProxyFactory authorizationProxyFactory) {\n\t\tAssert.notNull(authorizationProxyFactory, \"authorizationProxyFactory cannot be null\");\n\t\tthis.authorizationProxyFactory = authorizationProxyFactory;\n\t}\n\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {\n\t\tObject result = mi.proceed();\n\t\tif (result == null) {\n\t\t\treturn null;\n\t\t}\n\t\tAssert.notNull(this.authorizationProxyFactory, \"authorizationProxyFactory cannot be null\");\n\t\treturn this.authorizationProxyFactory.proxy(result);\n\t}\n\n\t/**\n\t * Use this {@link AuthorizationProxyFactory}\n\t * @param authorizationProxyFactory the proxy factory to use\n\t * @since 6.5\n\t */\n\tpublic void setAuthorizationProxyFactory(AuthorizationProxyFactory authorizationProxyFactory) {\n\t\tAssert.notNull(authorizationProxyFactory, \"authorizationProxyFactory cannot be null\");\n\t\tthis.authorizationProxyFactory = authorizationProxyFactory;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\tpublic void setPointcut(Pointcut pointcut) {\n\t\tthis.pointcut = pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\tstatic final class MethodReturnTypePointcut extends StaticMethodMatcherPointcut {\n\n\t\tprivate final Predicate<Class<?>> returnTypeMatches;\n\n\t\tMethodReturnTypePointcut(Predicate<Class<?>> returnTypeMatches) {\n\t\t\tthis.returnTypeMatches = returnTypeMatches;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(Method method, Class<?> targetClass) {\n\t\t\treturn this.returnTypeMatches.test(method.getReturnType());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/ExpressionAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.springframework.expression.Expression;\n\n/**\n * An {@link Expression} attribute.\n *\n * @author Evgeniy Cheban\n * @since 5.5\n */\nclass ExpressionAttribute {\n\n\tprivate final Expression expression;\n\n\t/**\n\t * Creates an instance.\n\t * @param expression the {@link Expression} to use\n\t */\n\tExpressionAttribute(Expression expression) {\n\t\tthis.expression = expression;\n\t}\n\n\t/**\n\t * Returns the {@link Expression}.\n\t * @return the {@link Expression} to use\n\t */\n\tExpression getExpression() {\n\t\treturn this.expression;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \" [Expression=\"\n\t\t\t\t+ ((this.expression != null) ? this.expression.getExpressionString() : null) + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/ExpressionUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.EvaluationException;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ExpressionAuthorizationDecision;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\nfinal class ExpressionUtils {\n\n\tprivate ExpressionUtils() {\n\t}\n\n\tstatic @Nullable AuthorizationResult evaluate(Expression expr, EvaluationContext ctx) {\n\t\treturn evaluate(expr, ctx, () -> null, null);\n\t}\n\n\tstatic <T> @Nullable AuthorizationResult evaluate(Expression expr, EvaluationContext ctx,\n\t\t\tSupplier<? extends @Nullable Authentication> authentication, @Nullable T context) {\n\t\ttry {\n\t\t\tObject result = expr.getValue(ctx);\n\t\t\tif (result instanceof AuthorizationManager<?> manager) {\n\t\t\t\tAssert.notNull(authentication, \"authentication supplier cannot be null\");\n\t\t\t\tAssert.notNull(context, \"context cannot be null\");\n\t\t\t\treturn ((AuthorizationManager<T>) manager).authorize(authentication, context);\n\t\t\t}\n\t\t\tif (result instanceof AuthorizationResult decision) {\n\t\t\t\treturn decision;\n\t\t\t}\n\t\t\tif (result instanceof Boolean granted) {\n\t\t\t\treturn new ExpressionAuthorizationDecision(granted, expr);\n\t\t\t}\n\t\t\tif (result == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"SpEL expression must return either a Boolean or an AuthorizationDecision\");\n\t\t}\n\t\tcatch (EvaluationException ex) {\n\t\t\tAuthorizationDeniedException denied = findAuthorizationException(ex);\n\t\t\tif (denied != null) {\n\t\t\t\tthrow denied;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"Failed to evaluate expression '\" + expr.getExpressionString() + \"'\",\n\t\t\t\t\tex);\n\t\t}\n\t}\n\n\tstatic @Nullable AuthorizationDeniedException findAuthorizationException(EvaluationException ex) {\n\t\tThrowable cause = ex.getCause();\n\t\twhile (cause != null) {\n\t\t\tif (cause instanceof AuthorizationDeniedException denied) {\n\t\t\t\treturn denied;\n\t\t\t}\n\t\t\tcause = cause.getCause();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/HandleAuthorizationDenied.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation for specifying handling behavior when an authorization denied happens in\n * method security or an\n * {@link org.springframework.security.authorization.AuthorizationDeniedException} is\n * thrown during method invocation\n *\n * @author Marcus da Coregio\n * @since 6.3\n * @see AuthorizationManagerAfterMethodInterceptor\n * @see AuthorizationManagerBeforeMethodInterceptor\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface HandleAuthorizationDenied {\n\n\t/**\n\t * The {@link MethodAuthorizationDeniedHandler} used to handle denied authorization\n\t * results\n\t * @return\n\t */\n\tClass<? extends MethodAuthorizationDeniedHandler> handlerClass() default ThrowingMethodAuthorizationDeniedHandler.class;\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport jakarta.annotation.security.DenyAll;\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthoritiesAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.SingleResultAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} which can determine if an {@link Authentication} may\n * invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}\n * contains a specified authority from the JSR-250 security annotations.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @author DingHao\n * @since 5.6\n */\npublic final class Jsr250AuthorizationManager implements AuthorizationManager<MethodInvocation> {\n\n\tprivate final Jsr250AuthorizationManagerRegistry registry = new Jsr250AuthorizationManagerRegistry();\n\n\tprivate AuthorizationManager<Collection<String>> authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();\n\n\tprivate String rolePrefix = \"ROLE_\";\n\n\t/**\n\t * Sets an {@link AuthorizationManager} that accepts a collection of authority\n\t * strings.\n\t * @param authoritiesAuthorizationManager the {@link AuthorizationManager} that\n\t * accepts a collection of authority strings to use\n\t * @since 6.2\n\t */\n\tpublic void setAuthoritiesAuthorizationManager(\n\t\t\tAuthorizationManager<Collection<String>> authoritiesAuthorizationManager) {\n\t\tAssert.notNull(authoritiesAuthorizationManager, \"authoritiesAuthorizationManager cannot be null\");\n\t\tthis.authoritiesAuthorizationManager = authoritiesAuthorizationManager;\n\t}\n\n\t/**\n\t * Sets the role prefix. Defaults to \"ROLE_\".\n\t * @param rolePrefix the role prefix to use\n\t */\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tAssert.notNull(rolePrefix, \"rolePrefix cannot be null\");\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMethodInvocation methodInvocation) {\n\t\tAuthorizationManager<MethodInvocation> delegate = this.registry.getManager(methodInvocation);\n\t\treturn delegate.authorize(authentication, methodInvocation);\n\t}\n\n\tprivate final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {\n\n\t\tprivate final SecurityAnnotationScanner<?> scanner = SecurityAnnotationScanners\n\t\t\t.requireUnique(List.of(DenyAll.class, PermitAll.class, RolesAllowed.class));\n\n\t\t@Override\n\t\tAuthorizationManager<MethodInvocation> resolveManager(Method method, @Nullable Class<?> targetClass) {\n\t\t\tAnnotation annotation = findJsr250Annotation(method, targetClass);\n\t\t\tif (annotation instanceof DenyAll) {\n\t\t\t\treturn SingleResultAuthorizationManager.denyAll();\n\t\t\t}\n\t\t\tif (annotation instanceof PermitAll) {\n\t\t\t\treturn SingleResultAuthorizationManager.permitAll();\n\t\t\t}\n\t\t\tif (annotation instanceof RolesAllowed rolesAllowed) {\n\t\t\t\treturn (Supplier<? extends @Nullable Authentication> a,\n\t\t\t\t\t\tMethodInvocation o) -> Jsr250AuthorizationManager.this.authoritiesAuthorizationManager\n\t\t\t\t\t\t\t.authorize(a, getAllowedRolesWithPrefix(rolesAllowed));\n\t\t\t}\n\t\t\treturn NULL_MANAGER;\n\t\t}\n\n\t\tprivate @Nullable Annotation findJsr250Annotation(Method method, @Nullable Class<?> targetClass) {\n\t\t\tClass<?> targetClassToUse = (targetClass != null) ? targetClass : method.getDeclaringClass();\n\t\t\treturn this.scanner.scan(method, targetClassToUse);\n\t\t}\n\n\t\tprivate Set<String> getAllowedRolesWithPrefix(RolesAllowed rolesAllowed) {\n\t\t\tSet<String> roles = new HashSet<>();\n\t\t\tfor (int i = 0; i < rolesAllowed.value().length; i++) {\n\t\t\t\troles.add(Jsr250AuthorizationManager.this.rolePrefix + rolesAllowed.value()[i]);\n\t\t\t}\n\t\t\treturn roles;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/MethodAuthorizationDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthorizationResult;\n\n/**\n * An interface used to define a strategy to handle denied method invocations\n *\n * @author Marcus da Coregio\n * @since 6.3\n * @see org.springframework.security.access.prepost.PreAuthorize\n * @see org.springframework.security.access.prepost.PostAuthorize\n */\npublic interface MethodAuthorizationDeniedHandler {\n\n\t/**\n\t * Handle denied method invocations, implementations might either throw an\n\t * {@link org.springframework.security.authorization.AuthorizationDeniedException} or\n\t * a replacement result instead of invoking the method, e.g. a masked value.\n\t * @param methodInvocation the {@link MethodInvocation} related to the authorization\n\t * denied\n\t * @param authorizationResult the authorization denied result\n\t * @return a replacement result for the denied method invocation, or null, or a\n\t * {@link reactor.core.publisher.Mono} for reactive applications\n\t */\n\t@Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult);\n\n\t/**\n\t * Handle denied method invocations, implementations might either throw an\n\t * {@link org.springframework.security.authorization.AuthorizationDeniedException} or\n\t * a replacement result instead of invoking the method, e.g. a masked value. By\n\t * default, this method invokes\n\t * {@link #handleDeniedInvocation(MethodInvocation, AuthorizationResult)}.\n\t * @param methodInvocationResult the object containing the {@link MethodInvocation}\n\t * and the result produced\n\t * @param authorizationResult the authorization denied result\n\t * @return a replacement result for the denied method invocation, or null, or a\n\t * {@link reactor.core.publisher.Mono} for reactive applications\n\t */\n\tdefault @Nullable Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\treturn handleDeniedInvocation(methodInvocationResult.getMethodInvocation(), authorizationResult);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/MethodExpressionAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ExpressionAuthorizationDecision;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An expression-based {@link AuthorizationManager} that determines the access by\n * evaluating the provided expression against the {@link MethodInvocation}.\n *\n * @author Josh Cummings\n * @since 5.8\n */\npublic final class MethodExpressionAuthorizationManager implements AuthorizationManager<MethodInvocation> {\n\n\tprivate SecurityExpressionHandler<MethodInvocation> expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\n\tprivate Expression expression;\n\n\t/**\n\t * Creates an instance.\n\t * @param expressionString the raw expression string to parse\n\t */\n\tpublic MethodExpressionAuthorizationManager(String expressionString) {\n\t\tAssert.hasText(expressionString, \"expressionString cannot be empty\");\n\t\tthis.expression = this.expressionHandler.getExpressionParser().parseExpression(expressionString);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityExpressionHandler} to be used. The default is\n\t * {@link DefaultMethodSecurityExpressionHandler}.\n\t * @param expressionHandler the {@link SecurityExpressionHandler} to use\n\t */\n\tpublic void setExpressionHandler(SecurityExpressionHandler<MethodInvocation> expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.expressionHandler = expressionHandler;\n\t\tthis.expression = expressionHandler.getExpressionParser()\n\t\t\t.parseExpression(this.expression.getExpressionString());\n\t}\n\n\t/**\n\t * Determines the access by evaluating the provided expression.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param context the {@link MethodInvocation} to check\n\t * @return an {@link ExpressionAuthorizationDecision} based on the evaluated\n\t * expression\n\t */\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // FIXME: Dataflow analysis limitation\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMethodInvocation context) {\n\t\tEvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, context);\n\t\tboolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx);\n\t\treturn new ExpressionAuthorizationDecision(granted, this.expression);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"WebExpressionAuthorizationManager[expression='\" + this.expression + \"']\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/MethodInvocationResult.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A context object that contains a {@link MethodInvocation} and the result of that\n * {@link MethodInvocation}.\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic class MethodInvocationResult {\n\n\tprivate final MethodInvocation methodInvocation;\n\n\tprivate final @Nullable Object result;\n\n\t/**\n\t * Construct a {@link MethodInvocationResult} with the provided parameters\n\t * @param methodInvocation the already-invoked {@link MethodInvocation}\n\t * @param result the value returned from the {@link MethodInvocation}\n\t */\n\tpublic MethodInvocationResult(MethodInvocation methodInvocation, @Nullable Object result) {\n\t\tAssert.notNull(methodInvocation, \"methodInvocation cannot be null\");\n\t\tthis.methodInvocation = methodInvocation;\n\t\tthis.result = result;\n\t}\n\n\t/**\n\t * Return the already-invoked {@link MethodInvocation}\n\t * @return the already-invoked {@link MethodInvocation}\n\t */\n\tpublic MethodInvocation getMethodInvocation() {\n\t\treturn this.methodInvocation;\n\t}\n\n\t/**\n\t * Return the result of the already-invoked {@link MethodInvocation}\n\t * @return the result\n\t */\n\tpublic @Nullable Object getResult() {\n\t\treturn this.result;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\n/**\n * An {@link AuthorizationEventPublisher} implementation that does nothing.\n *\n * @author Max Batischev\n * @since 6.4\n */\nfinal class NoOpAuthorizationEventPublisher implements AuthorizationEventPublisher {\n\n\t@Override\n\tpublic <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,\n\t\t\t@Nullable AuthorizationResult result) {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/NullReturningMethodAuthorizationDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationResult;\n\n/**\n * An implementation of {@link MethodAuthorizationDeniedHandler} that return {@code null}.\n *\n * @author Heejong Yoon\n * @since 6.5.0\n */\npublic final class NullReturningMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tif (authorizationResult instanceof AuthorizationDeniedException exception) {\n\t\t\tthrow exception;\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tif (authorizationResult instanceof AuthorizationDeniedException exception) {\n\t\t\tthrow exception;\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\n\n/**\n * An {@link AuthorizationManager} which can determine if an {@link Authentication} may\n * return the result from an invoked {@link MethodInvocation} by evaluating an expression\n * from the {@link PostAuthorize} annotation.\n *\n * @author Evgeniy Cheban\n * @since 5.6\n */\npublic final class PostAuthorizeAuthorizationManager\n\t\timplements AuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {\n\n\tprivate PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();\n\n\t/**\n\t * Use this the {@link MethodSecurityExpressionHandler}.\n\t * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use\n\t */\n\tpublic void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\tthis.registry.setExpressionHandler(expressionHandler);\n\t}\n\n\t/**\n\t * Configure pre/post-authorization template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param defaults - whether to resolve pre/post-authorization templates parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.registry.setTemplateDefaults(defaults);\n\t}\n\n\t/**\n\t * Invokes\n\t * {@link PostAuthorizeExpressionAttributeRegistry#setApplicationContext(ApplicationContext)}\n\t * with the provided {@link ApplicationContext}.\n\t * @param context the {@link ApplicationContext}\n\t * @since 6.3\n\t * @see PreAuthorizeExpressionAttributeRegistry#setApplicationContext(ApplicationContext)\n\t */\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tthis.registry.setApplicationContext(context);\n\t}\n\n\t/**\n\t * Determine if an {@link Authentication} has access to the returned object by\n\t * evaluating the {@link PostAuthorize} annotation that the {@link MethodInvocation}\n\t * specifies.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param mi the {@link MethodInvocationResult} to check\n\t * @return an {@link AuthorizationDecision} or {@code null} if the\n\t * {@link PostAuthorize} annotation is not present\n\t */\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMethodInvocationResult mi) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(mi.getMethodInvocation());\n\t\tif (attribute == null) {\n\t\t\treturn null;\n\t\t}\n\t\tMethodSecurityExpressionHandler expressionHandler = this.registry.getExpressionHandler();\n\t\tEvaluationContext ctx = expressionHandler.createEvaluationContext(authentication, mi.getMethodInvocation());\n\t\texpressionHandler.setReturnObject(mi.getResult(), ctx);\n\t\treturn ExpressionUtils.evaluate(attribute.getExpression(), ctx, authentication, mi);\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);\n\t\tPostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;\n\t\treturn postAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(methodInvocationResult.getMethodInvocation());\n\t\tPostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;\n\t\treturn postAuthorizeAttribute.getHandler()\n\t\t\t.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeExpressionAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link ExpressionAttribute} that carries additional properties for\n * {@code @PostAuthorize}.\n *\n * @author Marcus da Coregio\n */\nclass PostAuthorizeExpressionAttribute extends ExpressionAttribute {\n\n\tprivate final MethodAuthorizationDeniedHandler handler;\n\n\tPostAuthorizeExpressionAttribute(Expression expression, MethodAuthorizationDeniedHandler handler) {\n\t\tsuper(expression);\n\t\tAssert.notNull(handler, \"handler cannot be null\");\n\t\tthis.handler = handler;\n\t}\n\n\tMethodAuthorizationDeniedHandler getHandler() {\n\t\treturn this.handler;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeExpressionAttributeRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.util.Assert;\n\n/**\n * For internal use only, as this contract is likely to change.\n *\n * @author Evgeniy Cheban\n * @author DingHao\n * @since 5.8\n */\nfinal class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {\n\n\tprivate final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();\n\n\tprivate final SecurityAnnotationScanner<HandleAuthorizationDenied> handleAuthorizationDeniedScanner = SecurityAnnotationScanners\n\t\t.requireUnique(HandleAuthorizationDenied.class);\n\n\tprivate Function<Class<? extends MethodAuthorizationDeniedHandler>, MethodAuthorizationDeniedHandler> handlerResolver;\n\n\tprivate SecurityAnnotationScanner<PostAuthorize> postAuthorizeScanner = SecurityAnnotationScanners\n\t\t.requireUnique(PostAuthorize.class);\n\n\tPostAuthorizeExpressionAttributeRegistry() {\n\t\tthis.handlerResolver = (clazz) -> new ReflectiveMethodAuthorizationDeniedHandler(clazz,\n\t\t\t\tPostAuthorizeAuthorizationManager.class);\n\t}\n\n\t@Override\n\t@Nullable ExpressionAttribute resolveAttribute(Method method, @Nullable Class<?> targetClass) {\n\t\tPostAuthorize postAuthorize = findPostAuthorizeAnnotation(method, targetClass);\n\t\tif (postAuthorize == null) {\n\t\t\treturn null;\n\t\t}\n\t\tExpression expression = getExpressionHandler().getExpressionParser().parseExpression(postAuthorize.value());\n\t\tMethodAuthorizationDeniedHandler deniedHandler = resolveHandler(method, targetClass);\n\t\treturn new PostAuthorizeExpressionAttribute(expression, deniedHandler);\n\t}\n\n\tprivate MethodAuthorizationDeniedHandler resolveHandler(Method method, @Nullable Class<?> targetClass) {\n\t\tClass<?> targetClassToUse = targetClass(method, targetClass);\n\t\tHandleAuthorizationDenied deniedHandler = this.handleAuthorizationDeniedScanner.scan(method, targetClassToUse);\n\t\tif (deniedHandler != null) {\n\t\t\treturn this.handlerResolver.apply(deniedHandler.handlerClass());\n\t\t}\n\t\treturn this.defaultHandler;\n\t}\n\n\tprivate @Nullable PostAuthorize findPostAuthorizeAnnotation(Method method, @Nullable Class<?> targetClass) {\n\t\tClass<?> targetClassToUse = targetClass(method, targetClass);\n\t\treturn this.postAuthorizeScanner.scan(method, targetClassToUse);\n\t}\n\n\t/**\n\t * Uses the provided {@link ApplicationContext} to resolve the\n\t * {@link MethodAuthorizationDeniedHandler} from {@link PostAuthorize}\n\t * @param context the {@link ApplicationContext} to use\n\t */\n\tvoid setApplicationContext(ApplicationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tthis.handlerResolver = (clazz) -> resolveHandler(context, clazz);\n\t}\n\n\tvoid setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.postAuthorizeScanner = SecurityAnnotationScanners.requireUnique(PostAuthorize.class, templateDefaults);\n\t}\n\n\tprivate MethodAuthorizationDeniedHandler resolveHandler(ApplicationContext context,\n\t\t\tClass<? extends MethodAuthorizationDeniedHandler> handlerClass) {\n\t\tif (handlerClass == this.defaultHandler.getClass()) {\n\t\t\treturn this.defaultHandler;\n\t\t}\n\t\tString[] beanNames = context.getBeanNamesForType(handlerClass);\n\t\tif (beanNames.length == 0) {\n\t\t\tthrow new IllegalStateException(\"Could not find a bean of type \" + handlerClass.getName());\n\t\t}\n\t\tif (beanNames.length > 1) {\n\t\t\tthrow new IllegalStateException(\"Expected to find a single bean of type \" + handlerClass.getName()\n\t\t\t\t\t+ \" but found \" + Arrays.toString(beanNames));\n\t\t}\n\t\treturn context.getBean(beanNames[0], handlerClass);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthorizationManager} which can determine if an {@link Authentication}\n * has access to the returned object from the {@link MethodInvocation} by evaluating an\n * expression from the {@link PostAuthorize} annotation.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\npublic final class PostAuthorizeReactiveAuthorizationManager\n\t\timplements ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {\n\n\tprivate final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();\n\n\tpublic PostAuthorizeReactiveAuthorizationManager() {\n\t\tthis(new DefaultMethodSecurityExpressionHandler());\n\t}\n\n\tpublic PostAuthorizeReactiveAuthorizationManager(MethodSecurityExpressionHandler expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.registry.setExpressionHandler(expressionHandler);\n\t}\n\n\t/**\n\t * Configure pre/post-authorization template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param defaults - whether to resolve pre/post-authorization templates parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.registry.setTemplateDefaults(defaults);\n\t}\n\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tthis.registry.setApplicationContext(context);\n\t}\n\n\t/**\n\t * Determines if an {@link Authentication} has access to the returned object from the\n\t * {@link MethodInvocation} by evaluating an expression from the {@link PostAuthorize}\n\t * annotation.\n\t * @param authentication the {@link Mono} of the {@link Authentication} to check\n\t * @param result the {@link MethodInvocationResult} to check\n\t * @return a Mono of the {@link AuthorizationDecision} or an empty {@link Mono} if the\n\t * {@link PostAuthorize} annotation is not present\n\t */\n\t@Override\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, MethodInvocationResult result) {\n\t\tMethodInvocation mi = result.getMethodInvocation();\n\t\tExpressionAttribute attribute = this.registry.getAttribute(mi);\n\t\tif (attribute == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\tMethodSecurityExpressionHandler expressionHandler = this.registry.getExpressionHandler();\n\t\t// @formatter:off\n\t\treturn authentication\n\t\t\t\t.map((auth) -> expressionHandler.createEvaluationContext(auth, mi))\n\t\t\t\t.doOnNext((ctx) -> expressionHandler.setReturnObject(result.getResult(), ctx))\n\t\t\t\t.flatMap((ctx) -> ReactiveExpressionUtils.evaluate(attribute.getExpression(), ctx, authentication, result))\n\t\t\t\t.cast(AuthorizationResult.class);\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);\n\t\tPostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;\n\t\treturn postAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(methodInvocationResult.getMethodInvocation());\n\t\tPostAuthorizeExpressionAttribute postAuthorizeAttribute = (PostAuthorizeExpressionAttribute) attribute;\n\t\treturn postAuthorizeAttribute.getHandler()\n\t\t\t.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\n/**\n * A {@link MethodInterceptor} which filters a {@code returnedObject} from the\n * {@link MethodInvocation} by evaluating an expression from the {@link PostFilter}\n * annotation.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class PostFilterAuthorizationMethodInterceptor implements AuthorizationAdvisor {\n\n\tprivate Supplier<SecurityContextHolderStrategy> securityContextHolderStrategy = SecurityContextHolder::getContextHolderStrategy;\n\n\tprivate PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();\n\n\tprivate int order = AuthorizationInterceptorsOrder.POST_FILTER.getOrder();\n\n\tprivate final Pointcut pointcut;\n\n\t/**\n\t * Creates a {@link PostFilterAuthorizationMethodInterceptor} using the provided\n\t * parameters\n\t */\n\tpublic PostFilterAuthorizationMethodInterceptor() {\n\t\tthis.pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class);\n\t}\n\n\t/**\n\t * Use this {@link MethodSecurityExpressionHandler}.\n\t * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use\n\t */\n\tpublic void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\tthis.registry.setExpressionHandler(expressionHandler);\n\t}\n\n\t/**\n\t * Configure pre/post-authorization template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param defaults - whether to resolve pre/post-authorization templates parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.registry.setTemplateDefaults(defaults);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {\n\t\tthis.securityContextHolderStrategy = () -> strategy;\n\t}\n\n\t/**\n\t * Filter a {@code returnedObject} using the {@link PostFilter} annotation that the\n\t * {@link MethodInvocation} specifies.\n\t * @param mi the {@link MethodInvocation} to check check\n\t * @return filtered {@code returnedObject}\n\t */\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {\n\t\tObject returnedObject = mi.proceed();\n\t\tExpressionAttribute attribute = this.registry.getAttribute(mi);\n\t\tif (attribute == null) {\n\t\t\treturn returnedObject;\n\t\t}\n\t\tMethodSecurityExpressionHandler expressionHandler = this.registry.getExpressionHandler();\n\t\tEvaluationContext ctx = expressionHandler.createEvaluationContext(this::getAuthentication, mi);\n\t\treturn expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);\n\t}\n\n\tprivate Authentication getAuthentication() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.get().getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tthrow new AuthenticationCredentialsNotFoundException(\n\t\t\t\t\t\"An Authentication object was not found in the SecurityContext\");\n\t\t}\n\t\treturn authentication;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PostFilterAuthorizationReactiveMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.TypedValue;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link MethodInterceptor} which filters the returned object from the\n * {@link MethodInvocation} by evaluating an expression from the {@link PostFilter}\n * annotation.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\npublic final class PostFilterAuthorizationReactiveMethodInterceptor implements AuthorizationAdvisor {\n\n\tprivate final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();\n\n\tprivate final Pointcut pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class);\n\n\tprivate int order = AuthorizationInterceptorsOrder.POST_FILTER.getOrder();\n\n\t/**\n\t * Creates an instance.\n\t */\n\tpublic PostFilterAuthorizationReactiveMethodInterceptor() {\n\t\tthis(new DefaultMethodSecurityExpressionHandler());\n\t}\n\n\t/**\n\t * Creates an instance.\n\t */\n\tpublic PostFilterAuthorizationReactiveMethodInterceptor(MethodSecurityExpressionHandler expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.registry.setExpressionHandler(expressionHandler);\n\t}\n\n\t/**\n\t * Configure pre/post-authorization template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param defaults - whether to resolve pre/post-authorization templates parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.registry.setTemplateDefaults(defaults);\n\t}\n\n\t/**\n\t * Filters the returned object from the {@link MethodInvocation} by evaluating an\n\t * expression from the {@link PostFilter} annotation.\n\t * @param mi the {@link MethodInvocation} to use\n\t * @return the {@link Publisher} to use\n\t */\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(mi);\n\t\tif (attribute == null) {\n\t\t\treturn ReactiveMethodInvocationUtils.proceed(mi);\n\t\t}\n\t\tMono<EvaluationContext> toInvoke = ReactiveAuthenticationUtils.getAuthentication()\n\t\t\t.map((auth) -> this.registry.getExpressionHandler().createEvaluationContext(auth, mi));\n\t\tMethod method = mi.getMethod();\n\t\tClass<?> type = method.getReturnType();\n\t\tAssert\n\t\t\t.state(Publisher.class.isAssignableFrom(type),\n\t\t\t\t\t() -> String.format(\n\t\t\t\t\t\t\t\"The parameter type %s on %s must be an instance of org.reactivestreams.Publisher \"\n\t\t\t\t\t\t\t\t\t+ \"(for example, a Mono or Flux) in order to support Reactor Context\",\n\t\t\t\t\t\t\ttype, method));\n\t\tReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);\n\t\tif (isMultiValue(type, adapter)) {\n\t\t\tPublisher<?> publisher = Flux.defer(() -> ReactiveMethodInvocationUtils.proceed(mi));\n\t\t\tFlux<?> flux = toInvoke.flatMapMany((ctx) -> filterMultiValue(publisher, ctx, attribute));\n\t\t\treturn (adapter != null) ? adapter.fromPublisher(flux) : flux;\n\t\t}\n\t\tPublisher<?> publisher = Mono.defer(() -> ReactiveMethodInvocationUtils.proceed(mi));\n\t\tMono<?> mono = toInvoke.flatMap((ctx) -> filterSingleValue(publisher, ctx, attribute));\n\t\treturn (adapter != null) ? adapter.fromPublisher(mono) : mono;\n\t}\n\n\tprivate boolean isMultiValue(Class<?> returnType, @Nullable ReactiveAdapter adapter) {\n\t\tif (Flux.class.isAssignableFrom(returnType)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn adapter == null || adapter.isMultiValue();\n\t}\n\n\tprivate Mono<?> filterSingleValue(Publisher<?> publisher, EvaluationContext ctx, ExpressionAttribute attribute) {\n\t\treturn Mono.from(publisher)\n\t\t\t.doOnNext((result) -> setFilterObject(ctx, result))\n\t\t\t.flatMap((result) -> postFilter(ctx, result, attribute));\n\t}\n\n\tprivate Flux<?> filterMultiValue(Publisher<?> publisher, EvaluationContext ctx, ExpressionAttribute attribute) {\n\t\treturn Flux.from(publisher)\n\t\t\t.doOnNext((result) -> setFilterObject(ctx, result))\n\t\t\t.flatMap((result) -> postFilter(ctx, result, attribute));\n\t}\n\n\tprivate void setFilterObject(EvaluationContext ctx, Object result) {\n\t\tTypedValue rootObject = ctx.getRootObject();\n\t\tAssert.notNull(rootObject, \"rootObject cannot be null\");\n\t\tMethodSecurityExpressionOperations methodOperations = (MethodSecurityExpressionOperations) rootObject\n\t\t\t.getValue();\n\t\tAssert.notNull(methodOperations, \"methodOperations cannot be null\");\n\t\tmethodOperations.setFilterObject(result);\n\t}\n\n\tprivate Mono<?> postFilter(EvaluationContext ctx, Object result, ExpressionAttribute attribute) {\n\t\treturn ReactiveExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx)\n\t\t\t.flatMap((granted) -> granted ? Mono.just(result) : Mono.empty());\n\t}\n\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PostFilterExpressionAttributeRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\n\n/**\n * For internal use only, as this contract is likely to change.\n *\n * @author Evgeniy Cheban\n * @author DingHao\n * @since 5.8\n */\nfinal class PostFilterExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {\n\n\tprivate SecurityAnnotationScanner<PostFilter> scanner = SecurityAnnotationScanners.requireUnique(PostFilter.class);\n\n\t@Override\n\t@Nullable ExpressionAttribute resolveAttribute(Method method, @Nullable Class<?> targetClass) {\n\t\tPostFilter postFilter = findPostFilterAnnotation(method, targetClass);\n\t\tif (postFilter == null) {\n\t\t\treturn null;\n\t\t}\n\t\tExpression postFilterExpression = getExpressionHandler().getExpressionParser()\n\t\t\t.parseExpression(postFilter.value());\n\t\treturn new ExpressionAttribute(postFilterExpression);\n\t}\n\n\tvoid setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(PostFilter.class, defaults);\n\t}\n\n\tprivate @Nullable PostFilter findPostFilterAnnotation(Method method, @Nullable Class<?> targetClass) {\n\t\tClass<?> targetClassToUse = targetClass(method, targetClass);\n\t\treturn this.scanner.scan(method, targetClassToUse);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\n\n/**\n * An {@link AuthorizationManager} which can determine if an {@link Authentication} may\n * invoke the {@link MethodInvocation} by evaluating an expression from the\n * {@link PreAuthorize} annotation.\n *\n * @author Evgeniy Cheban\n * @since 5.6\n */\npublic final class PreAuthorizeAuthorizationManager\n\t\timplements AuthorizationManager<MethodInvocation>, MethodAuthorizationDeniedHandler {\n\n\tprivate PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry();\n\n\t/**\n\t * Sets the {@link MethodSecurityExpressionHandler}.\n\t * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use\n\t */\n\tpublic void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\tthis.registry.setExpressionHandler(expressionHandler);\n\t}\n\n\t/**\n\t * Configure pre/post-authorization template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param defaults - whether to resolve pre/post-authorization templates parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.registry.setTemplateDefaults(defaults);\n\t}\n\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tthis.registry.setApplicationContext(context);\n\t}\n\n\t/**\n\t * Determine if an {@link Authentication} has access to a method by evaluating an\n\t * expression from the {@link PreAuthorize} annotation that the\n\t * {@link MethodInvocation} specifies.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param mi the {@link MethodInvocation} to check\n\t * @return an {@link AuthorizationDecision} or {@code null} if the\n\t * {@link PreAuthorize} annotation is not present\n\t */\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMethodInvocation mi) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(mi);\n\t\tif (attribute == null) {\n\t\t\treturn null;\n\t\t}\n\t\tEvaluationContext ctx = this.registry.getExpressionHandler().createEvaluationContext(authentication, mi);\n\t\treturn ExpressionUtils.evaluate(attribute.getExpression(), ctx, authentication, mi);\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);\n\t\tPreAuthorizeExpressionAttribute preAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;\n\t\treturn preAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeExpressionAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link ExpressionAttribute} that carries additional properties for\n * {@code @PreAuthorize}.\n *\n * @author Marcus da Coregio\n */\nclass PreAuthorizeExpressionAttribute extends ExpressionAttribute {\n\n\tprivate final MethodAuthorizationDeniedHandler handler;\n\n\tPreAuthorizeExpressionAttribute(Expression expression, MethodAuthorizationDeniedHandler handler) {\n\t\tsuper(expression);\n\t\tAssert.notNull(handler, \"handler cannot be null\");\n\t\tthis.handler = handler;\n\t}\n\n\tMethodAuthorizationDeniedHandler getHandler() {\n\t\treturn this.handler;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeExpressionAttributeRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.util.Assert;\n\n/**\n * For internal use only, as this contract is likely to change.\n *\n * @author Evgeniy Cheban\n * @author DingHao\n * @since 5.8\n */\nfinal class PreAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {\n\n\tprivate final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();\n\n\tprivate final SecurityAnnotationScanner<HandleAuthorizationDenied> handleAuthorizationDeniedScanner = SecurityAnnotationScanners\n\t\t.requireUnique(HandleAuthorizationDenied.class);\n\n\tprivate Function<Class<? extends MethodAuthorizationDeniedHandler>, MethodAuthorizationDeniedHandler> handlerResolver;\n\n\tprivate SecurityAnnotationScanner<PreAuthorize> preAuthorizeScanner = SecurityAnnotationScanners\n\t\t.requireUnique(PreAuthorize.class);\n\n\tPreAuthorizeExpressionAttributeRegistry() {\n\t\tthis.handlerResolver = (clazz) -> new ReflectiveMethodAuthorizationDeniedHandler(clazz,\n\t\t\t\tPreAuthorizeAuthorizationManager.class);\n\t}\n\n\t@Override\n\t@Nullable ExpressionAttribute resolveAttribute(Method method, @Nullable Class<?> targetClass) {\n\t\tPreAuthorize preAuthorize = findPreAuthorizeAnnotation(method, targetClass);\n\t\tif (preAuthorize == null) {\n\t\t\treturn null;\n\t\t}\n\t\tExpression expression = getExpressionHandler().getExpressionParser().parseExpression(preAuthorize.value());\n\t\tMethodAuthorizationDeniedHandler handler = resolveHandler(method, targetClass);\n\t\treturn new PreAuthorizeExpressionAttribute(expression, handler);\n\t}\n\n\tprivate MethodAuthorizationDeniedHandler resolveHandler(Method method, @Nullable Class<?> targetClass) {\n\t\tClass<?> targetClassToUse = targetClass(method, targetClass);\n\t\tHandleAuthorizationDenied deniedHandler = this.handleAuthorizationDeniedScanner.scan(method, targetClassToUse);\n\t\tif (deniedHandler != null) {\n\t\t\treturn this.handlerResolver.apply(deniedHandler.handlerClass());\n\t\t}\n\t\treturn this.defaultHandler;\n\t}\n\n\tprivate @Nullable PreAuthorize findPreAuthorizeAnnotation(Method method, @Nullable Class<?> targetClass) {\n\t\tClass<?> targetClassToUse = targetClass(method, targetClass);\n\t\treturn this.preAuthorizeScanner.scan(method, targetClassToUse);\n\t}\n\n\t/**\n\t * Uses the provided {@link ApplicationContext} to resolve the\n\t * {@link MethodAuthorizationDeniedHandler} from {@link PreAuthorize}.\n\t * @param context the {@link ApplicationContext} to use\n\t */\n\tvoid setApplicationContext(ApplicationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tthis.handlerResolver = (clazz) -> resolveHandler(context, clazz);\n\t}\n\n\tvoid setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.preAuthorizeScanner = SecurityAnnotationScanners.requireUnique(PreAuthorize.class, defaults);\n\t}\n\n\tprivate MethodAuthorizationDeniedHandler resolveHandler(ApplicationContext context,\n\t\t\tClass<? extends MethodAuthorizationDeniedHandler> handlerClass) {\n\t\tif (handlerClass == this.defaultHandler.getClass()) {\n\t\t\treturn this.defaultHandler;\n\t\t}\n\t\tString[] beanNames = context.getBeanNamesForType(handlerClass);\n\t\tif (beanNames.length == 0) {\n\t\t\tthrow new IllegalStateException(\"Could not find a bean of type \" + handlerClass.getName());\n\t\t}\n\t\tif (beanNames.length > 1) {\n\t\t\tthrow new IllegalStateException(\"Expected to find a single bean of type \" + handlerClass.getName()\n\t\t\t\t\t+ \" but found \" + Arrays.toString(beanNames));\n\t\t}\n\t\treturn context.getBean(beanNames[0], handlerClass);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthorizationManager} which can determine if an {@link Authentication}\n * has access to the {@link MethodInvocation} by evaluating an expression from the\n * {@link PreAuthorize} annotation.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\npublic final class PreAuthorizeReactiveAuthorizationManager\n\t\timplements ReactiveAuthorizationManager<MethodInvocation>, MethodAuthorizationDeniedHandler {\n\n\tprivate final PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry();\n\n\tpublic PreAuthorizeReactiveAuthorizationManager() {\n\t\tthis(new DefaultMethodSecurityExpressionHandler());\n\t}\n\n\tpublic PreAuthorizeReactiveAuthorizationManager(MethodSecurityExpressionHandler expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.registry.setExpressionHandler(expressionHandler);\n\t}\n\n\t/**\n\t * Configure pre/post-authorization template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param defaults - whether to resolve pre/post-authorization templates parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.registry.setTemplateDefaults(defaults);\n\t}\n\n\tpublic void setApplicationContext(ApplicationContext context) {\n\t\tthis.registry.setApplicationContext(context);\n\t}\n\n\t/**\n\t * Determines if an {@link Authentication} has access to the {@link MethodInvocation}\n\t * by evaluating an expression from the {@link PreAuthorize} annotation.\n\t * @param authentication the {@link Mono} of the {@link Authentication} to check\n\t * @param mi the {@link MethodInvocation} to check\n\t * @return a {@link Mono} of the {@link AuthorizationResult} or an empty {@link Mono}\n\t * if the {@link PreAuthorize} annotation is not present\n\t */\n\t@Override\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, MethodInvocation mi) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(mi);\n\t\tif (attribute == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\t// @formatter:off\n\t\treturn authentication\n\t\t\t\t.map((auth) -> this.registry.getExpressionHandler().createEvaluationContext(auth, mi))\n\t\t\t\t.flatMap((ctx) -> ReactiveExpressionUtils.evaluate(attribute.getExpression(), ctx, authentication, mi))\n\t\t\t\t.cast(AuthorizationResult.class);\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tExpressionAttribute attribute = this.registry.getAttribute(methodInvocation);\n\t\tPreAuthorizeExpressionAttribute preAuthorizeAttribute = (PreAuthorizeExpressionAttribute) attribute;\n\t\treturn preAuthorizeAttribute.getHandler().handleDeniedInvocation(methodInvocation, authorizationResult);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link MethodInterceptor} which filters a method argument by evaluating an expression\n * from the {@link PreFilter} annotation.\n *\n * @author Evgeniy Cheban\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class PreFilterAuthorizationMethodInterceptor implements AuthorizationAdvisor {\n\n\tprivate Supplier<SecurityContextHolderStrategy> securityContextHolderStrategy = SecurityContextHolder::getContextHolderStrategy;\n\n\tprivate PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();\n\n\tprivate int order = AuthorizationInterceptorsOrder.PRE_FILTER.getOrder();\n\n\tprivate final Pointcut pointcut;\n\n\t/**\n\t * Creates a {@link PreFilterAuthorizationMethodInterceptor} using the provided\n\t * parameters\n\t */\n\tpublic PreFilterAuthorizationMethodInterceptor() {\n\t\tthis.pointcut = AuthorizationMethodPointcuts.forAnnotations(PreFilter.class);\n\t}\n\n\t/**\n\t * Use this {@link MethodSecurityExpressionHandler}\n\t * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use\n\t */\n\tpublic void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {\n\t\tthis.registry.setExpressionHandler(expressionHandler);\n\t}\n\n\t/**\n\t * Configure pre/post-authorization template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param defaults - whether to resolve pre/post-authorization templates parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.registry.setTemplateDefaults(defaults);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {\n\t\tthis.securityContextHolderStrategy = () -> strategy;\n\t}\n\n\t/**\n\t * Filter the method argument specified in the {@link PreFilter} annotation that\n\t * {@link MethodInvocation} specifies.\n\t * @param mi the {@link MethodInvocation} to check\n\t */\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {\n\t\tPreFilterExpressionAttributeRegistry.PreFilterExpressionAttribute attribute = this.registry.getAttribute(mi);\n\t\tif (attribute == null) {\n\t\t\treturn mi.proceed();\n\t\t}\n\t\tMethodSecurityExpressionHandler expressionHandler = this.registry.getExpressionHandler();\n\t\tEvaluationContext ctx = expressionHandler.createEvaluationContext(this::getAuthentication, mi);\n\t\tObject filterTarget = findFilterTarget(attribute.getFilterTarget(), ctx, mi);\n\t\texpressionHandler.filter(filterTarget, attribute.getExpression(), ctx);\n\t\treturn mi.proceed();\n\t}\n\n\tprivate Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation methodInvocation) {\n\t\tObject filterTarget;\n\t\tif (StringUtils.hasText(filterTargetName)) {\n\t\t\tfilterTarget = ctx.lookupVariable(filterTargetName);\n\t\t\tAssert.notNull(filterTarget, () -> \"Filter target was null, or no argument with name '\" + filterTargetName\n\t\t\t\t\t+ \"' found in method.\");\n\t\t}\n\t\telse {\n\t\t\t@Nullable Object[] arguments = methodInvocation.getArguments();\n\t\t\tAssert.state(arguments.length == 1,\n\t\t\t\t\t\"Unable to determine the method argument for filtering. Specify the filter target.\");\n\t\t\tfilterTarget = arguments[0];\n\t\t\tAssert.notNull(filterTarget,\n\t\t\t\t\t\"Filter target was null. Make sure you passing the correct value in the method argument.\");\n\t\t}\n\t\tAssert.state(!filterTarget.getClass().isArray(),\n\t\t\t\t\"Pre-filtering on array types is not supported. Using a Collection will solve this problem.\");\n\t\treturn filterTarget;\n\t}\n\n\tprivate Authentication getAuthentication() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.get().getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tthrow new AuthenticationCredentialsNotFoundException(\n\t\t\t\t\t\"An Authentication object was not found in the SecurityContext\");\n\t\t}\n\t\treturn authentication;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PreFilterAuthorizationReactiveMethodInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.aop.Advice;\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link MethodInterceptor} which filters a reactive method argument by evaluating an\n * expression from the {@link PreFilter} annotation.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\npublic final class PreFilterAuthorizationReactiveMethodInterceptor implements AuthorizationAdvisor {\n\n\tprivate final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();\n\n\tprivate final Pointcut pointcut = AuthorizationMethodPointcuts.forAnnotations(PreFilter.class);\n\n\tprivate ParameterNameDiscoverer parameterNameDiscoverer = new DefaultSecurityParameterNameDiscoverer();\n\n\tprivate int order = AuthorizationInterceptorsOrder.PRE_FILTER.getOrder();\n\n\tpublic PreFilterAuthorizationReactiveMethodInterceptor() {\n\t\tthis(new DefaultMethodSecurityExpressionHandler());\n\t}\n\n\t/**\n\t * Creates an instance.\n\t */\n\tpublic PreFilterAuthorizationReactiveMethodInterceptor(MethodSecurityExpressionHandler expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.registry.setExpressionHandler(expressionHandler);\n\t}\n\n\t/**\n\t * Configure pre/post-authorization template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param defaults - whether to resolve pre/post-authorization templates parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.registry.setTemplateDefaults(defaults);\n\t}\n\n\t/**\n\t * Sets the {@link ParameterNameDiscoverer}.\n\t * @param parameterNameDiscoverer the {@link ParameterNameDiscoverer} to use\n\t */\n\tpublic void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {\n\t\tAssert.notNull(parameterNameDiscoverer, \"parameterNameDiscoverer cannot be null\");\n\t\tthis.parameterNameDiscoverer = parameterNameDiscoverer;\n\t}\n\n\t/**\n\t * Filters a reactive method argument by evaluating an expression from the\n\t * {@link PreFilter} annotation.\n\t * @param mi the {@link MethodInvocation} to use\n\t * @return the {@link Publisher} to use\n\t */\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {\n\t\tPreFilterExpressionAttributeRegistry.PreFilterExpressionAttribute attribute = this.registry.getAttribute(mi);\n\t\tif (attribute == null) {\n\t\t\treturn ReactiveMethodInvocationUtils.proceed(mi);\n\t\t}\n\t\tFilterTarget filterTarget = findFilterTarget(attribute.getFilterTarget(), mi);\n\t\tMono<EvaluationContext> toInvoke = ReactiveAuthenticationUtils.getAuthentication()\n\t\t\t.map((auth) -> this.registry.getExpressionHandler().createEvaluationContext(auth, mi));\n\t\tMethod method = mi.getMethod();\n\t\tClass<?> type = filterTarget.value.getClass();\n\t\tAssert\n\t\t\t.state(Publisher.class.isAssignableFrom(type),\n\t\t\t\t\t() -> String.format(\n\t\t\t\t\t\t\t\"The parameter type %s on %s must be an instance of org.reactivestreams.Publisher \"\n\t\t\t\t\t\t\t\t\t+ \"(for example, a Mono or Flux) in order to support Reactor Context\",\n\t\t\t\t\t\t\ttype, method));\n\t\tReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);\n\t\tif (isMultiValue(type, adapter)) {\n\t\t\tFlux<?> result = toInvoke\n\t\t\t\t.flatMapMany((ctx) -> filterMultiValue(filterTarget.value, attribute.getExpression(), ctx));\n\t\t\tmi.getArguments()[filterTarget.index] = (adapter != null) ? adapter.fromPublisher(result) : result;\n\t\t}\n\t\telse {\n\t\t\tMono<?> result = toInvoke\n\t\t\t\t.flatMap((ctx) -> filterSingleValue(filterTarget.value, attribute.getExpression(), ctx));\n\t\t\tmi.getArguments()[filterTarget.index] = (adapter != null) ? adapter.fromPublisher(result) : result;\n\t\t}\n\t\treturn ReactiveMethodInvocationUtils.proceed(mi);\n\t}\n\n\tprivate FilterTarget findFilterTarget(String name, MethodInvocation mi) {\n\t\tObject value = null;\n\t\tint index = 0;\n\t\tif (StringUtils.hasText(name)) {\n\t\t\tObject target = mi.getThis();\n\t\t\tClass<?> targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;\n\t\t\tMethod specificMethod = AopUtils.getMostSpecificMethod(mi.getMethod(), targetClass);\n\t\t\t@Nullable String @Nullable [] parameterNames = this.parameterNameDiscoverer.getParameterNames(specificMethod);\n\t\t\tif (parameterNames != null && parameterNames.length > 0) {\n\t\t\t\t@Nullable Object[] arguments = mi.getArguments();\n\t\t\t\tfor (index = 0; index < parameterNames.length; index++) {\n\t\t\t\t\tif (name.equals(parameterNames[index])) {\n\t\t\t\t\t\tvalue = arguments[index];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tAssert.notNull(value,\n\t\t\t\t\t\t\"Filter target was null, or no argument with name '\" + name + \"' found in method.\");\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t@Nullable Object[] arguments = mi.getArguments();\n\t\t\tAssert.state(arguments.length == 1,\n\t\t\t\t\t\"Unable to determine the method argument for filtering. Specify the filter target.\");\n\t\t\tvalue = arguments[0];\n\t\t\tAssert.notNull(value,\n\t\t\t\t\t\"Filter target was null. Make sure you passing the correct value in the method argument.\");\n\t\t}\n\t\tAssert.state(value instanceof Publisher<?>, \"Filter target must be an instance of Publisher.\");\n\t\treturn new FilterTarget((Publisher<?>) value, index);\n\t}\n\n\tprivate boolean isMultiValue(Class<?> returnType, @Nullable ReactiveAdapter adapter) {\n\t\tif (Flux.class.isAssignableFrom(returnType)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn adapter == null || adapter.isMultiValue();\n\t}\n\n\tprivate Mono<?> filterSingleValue(Publisher<?> filterTarget, Expression filterExpression, EvaluationContext ctx) {\n\t\tMethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations) ctx.getRootObject()\n\t\t\t.getValue();\n\t\treturn Mono.from(filterTarget).filterWhen((filterObject) -> {\n\t\t\tif (rootObject != null) {\n\t\t\t\trootObject.setFilterObject(filterObject);\n\t\t\t}\n\t\t\treturn ReactiveExpressionUtils.evaluateAsBoolean(filterExpression, ctx);\n\t\t});\n\t}\n\n\tprivate Flux<?> filterMultiValue(Publisher<?> filterTarget, Expression filterExpression, EvaluationContext ctx) {\n\t\tMethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations) ctx.getRootObject()\n\t\t\t.getValue();\n\t\treturn Flux.from(filterTarget).filterWhen((filterObject) -> {\n\t\t\tif (rootObject != null) {\n\t\t\t\trootObject.setFilterObject(filterObject);\n\t\t\t}\n\t\t\treturn ReactiveExpressionUtils.evaluateAsBoolean(filterExpression, ctx);\n\t\t});\n\t}\n\n\t@Override\n\tpublic Pointcut getPointcut() {\n\t\treturn this.pointcut;\n\t}\n\n\t@Override\n\tpublic Advice getAdvice() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic boolean isPerInstance() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\tprivate static final class FilterTarget {\n\n\t\tprivate final Publisher<?> value;\n\n\t\tprivate final int index;\n\n\t\tprivate FilterTarget(Publisher<?> value, int index) {\n\t\t\tthis.value = value;\n\t\t\tthis.index = index;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/PreFilterExpressionAttributeRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\n\n/**\n * For internal use only, as this contract is likely to change.\n *\n * @author Evgeniy Cheban\n * @author DingHao\n * @since 5.8\n */\nfinal class PreFilterExpressionAttributeRegistry\n\t\textends AbstractExpressionAttributeRegistry<PreFilterExpressionAttributeRegistry.PreFilterExpressionAttribute> {\n\n\tprivate SecurityAnnotationScanner<PreFilter> scanner = SecurityAnnotationScanners.requireUnique(PreFilter.class);\n\n\t@Override\n\t@Nullable PreFilterExpressionAttribute resolveAttribute(Method method, @Nullable Class<?> targetClass) {\n\t\tPreFilter preFilter = findPreFilterAnnotation(method, targetClass);\n\t\tif (preFilter == null) {\n\t\t\treturn null;\n\t\t}\n\t\tExpression preFilterExpression = getExpressionHandler().getExpressionParser()\n\t\t\t.parseExpression(preFilter.value());\n\t\treturn new PreFilterExpressionAttribute(preFilterExpression, preFilter.filterTarget());\n\t}\n\n\tvoid setTemplateDefaults(AnnotationTemplateExpressionDefaults defaults) {\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(PreFilter.class, defaults);\n\t}\n\n\tprivate @Nullable PreFilter findPreFilterAnnotation(Method method, @Nullable Class<?> targetClass) {\n\t\tClass<?> targetClassToUse = targetClass(method, targetClass);\n\t\treturn this.scanner.scan(method, targetClassToUse);\n\t}\n\n\tstatic final class PreFilterExpressionAttribute extends ExpressionAttribute {\n\n\t\tprivate final String filterTarget;\n\n\t\tprivate PreFilterExpressionAttribute(Expression expression, String filterTarget) {\n\t\t\tsuper(expression);\n\t\t\tthis.filterTarget = filterTarget;\n\t\t}\n\n\t\tString getFilterTarget() {\n\t\t\treturn this.filterTarget;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/ReactiveAuthenticationUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * For internal use only, as this contract is likely to change.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\nfinal class ReactiveAuthenticationUtils {\n\n\tprivate static final Authentication ANONYMOUS = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tstatic Mono<Authentication> getAuthentication() {\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t.mapNotNull(SecurityContext::getAuthentication)\n\t\t\t.defaultIfEmpty(ANONYMOUS);\n\t}\n\n\tprivate ReactiveAuthenticationUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/ReactiveExpressionUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.EvaluationException;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ExpressionAuthorizationDecision;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * For internal use only, as this contract is likely to change.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\nfinal class ReactiveExpressionUtils {\n\n\tstatic Mono<AuthorizationResult> evaluate(Expression expr, EvaluationContext ctx) {\n\t\treturn evaluate(expr, ctx, Mono.empty(), null);\n\t}\n\n\tstatic <T> Mono<AuthorizationResult> evaluate(Expression expr, EvaluationContext ctx,\n\t\t\tMono<Authentication> authentication, @Nullable T context) {\n\t\treturn Mono.defer(() -> {\n\t\t\tObject value;\n\t\t\ttry {\n\t\t\t\tvalue = expr.getValue(ctx);\n\t\t\t}\n\t\t\tcatch (EvaluationException ex) {\n\t\t\t\treturn Mono.error(() -> new IllegalArgumentException(\n\t\t\t\t\t\t\"Failed to evaluate expression '\" + expr.getExpressionString() + \"'\", ex));\n\t\t\t}\n\t\t\tif (value instanceof ReactiveAuthorizationManager<?> manager) {\n\t\t\t\tAssert.notNull(context, \"context cannot be null\");\n\t\t\t\treturn ((ReactiveAuthorizationManager<T>) manager).authorize(authentication, context);\n\t\t\t}\n\t\t\tif (value instanceof Mono<?> mono) {\n\t\t\t\treturn mono.flatMap((data) -> adapt(expr, data));\n\t\t\t}\n\t\t\treturn adapt(expr, value);\n\t\t});\n\t}\n\n\tprivate static Mono<AuthorizationResult> adapt(Expression expr, @Nullable Object value) {\n\t\tif (value instanceof Boolean granted) {\n\t\t\treturn Mono.just(new ExpressionAuthorizationDecision(granted, expr));\n\t\t}\n\t\tif (value instanceof AuthorizationResult decision) {\n\t\t\treturn Mono.just(decision);\n\t\t}\n\t\treturn createInvalidReturnTypeMono(expr);\n\t}\n\n\tstatic Mono<Boolean> evaluateAsBoolean(Expression expr, EvaluationContext ctx) {\n\t\treturn Mono.defer(() -> {\n\t\t\tObject value;\n\t\t\ttry {\n\t\t\t\tvalue = expr.getValue(ctx);\n\t\t\t}\n\t\t\tcatch (EvaluationException ex) {\n\t\t\t\treturn Mono.error(() -> new IllegalArgumentException(\n\t\t\t\t\t\t\"Failed to evaluate expression '\" + expr.getExpressionString() + \"'\", ex));\n\t\t\t}\n\t\t\tif (value instanceof Boolean) {\n\t\t\t\treturn Mono.just((Boolean) value);\n\t\t\t}\n\t\t\tif (value instanceof Mono<?>) {\n\t\t\t\tMono<?> monoValue = (Mono<?>) value;\n\t\t\t\t// @formatter:off\n\t\t\t\treturn monoValue\n\t\t\t\t\t\t.filter(Boolean.class::isInstance)\n\t\t\t\t\t\t.map(Boolean.class::cast)\n\t\t\t\t\t\t.switchIfEmpty(createInvalidReturnTypeMono(expr));\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t\treturn createInvalidReturnTypeMono(expr);\n\t\t});\n\t}\n\n\tprivate static <T> Mono<T> createInvalidReturnTypeMono(Expression expr) {\n\t\treturn Mono.error(() -> new IllegalStateException(\"Expression: '\" + expr.getExpressionString()\n\t\t\t\t+ \"' must return boolean, Mono<Boolean>, AuthorizationResult, or Mono<AuthorizationResult>\"));\n\t}\n\n\tprivate ReactiveExpressionUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/ReactiveMethodInvocationUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.Exceptions;\n\n/**\n * For internal use only, as this contract is likely to change.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\nfinal class ReactiveMethodInvocationUtils {\n\n\tstatic @Nullable <T> T proceed(MethodInvocation mi) {\n\t\ttry {\n\t\t\treturn (T) mi.proceed();\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\tthrow Exceptions.propagate(ex);\n\t\t}\n\t}\n\n\tprivate ReactiveMethodInvocationUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/ReflectiveMethodAuthorizationDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthorizationResult;\n\nfinal class ReflectiveMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final Class<?> targetClass;\n\n\tprivate final Class<?> managerClass;\n\n\tReflectiveMethodAuthorizationDeniedHandler(Class<?> targetClass, Class<?> managerClass) {\n\t\tthis.logger.debug(\n\t\t\t\t\"Will attempt to instantiate handlerClass attributes using reflection since no application context was supplied to \"\n\t\t\t\t\t\t+ managerClass);\n\t\tthis.targetClass = targetClass;\n\t\tthis.managerClass = managerClass;\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\treturn constructMethodAuthorizationDeniedHandler().handleDeniedInvocation(methodInvocation,\n\t\t\t\tauthorizationResult);\n\t}\n\n\t@Override\n\tpublic @Nullable Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\treturn constructMethodAuthorizationDeniedHandler().handleDeniedInvocationResult(methodInvocationResult,\n\t\t\t\tauthorizationResult);\n\t}\n\n\tprivate MethodAuthorizationDeniedHandler constructMethodAuthorizationDeniedHandler() {\n\t\ttry {\n\t\t\treturn ((MethodAuthorizationDeniedHandler) this.targetClass.getConstructor().newInstance());\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to construct instance of \" + this.targetClass\n\t\t\t\t\t+ \". Please either add a public default constructor to the class \"\n\t\t\t\t\t+ \" or publish an instance of it as a Spring bean. If you publish it as a Spring bean, \"\n\t\t\t\t\t+ \" either add `@EnableMethodSecurity` to your configuration or \"\n\t\t\t\t\t+ \" provide the `ApplicationContext` directly to \" + this.managerClass, ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodClassKey;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.authorization.AuthoritiesAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} which can determine if an {@link Authentication} may\n * invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}\n * contains a specified authority from the Spring Security's {@link Secured} annotation.\n *\n * @author Evgeniy Cheban\n * @author DingHao\n * @since 5.6\n */\npublic final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {\n\n\tprivate AuthorizationManager<Collection<String>> authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();\n\n\tprivate final Map<MethodClassKey, Set<String>> cachedAuthorities = new ConcurrentHashMap<>();\n\n\tprivate final SecurityAnnotationScanner<Secured> scanner = SecurityAnnotationScanners.requireUnique(Secured.class);\n\n\t/**\n\t * Sets an {@link AuthorizationManager} that accepts a collection of authority\n\t * strings.\n\t * @param authoritiesAuthorizationManager the {@link AuthorizationManager} that\n\t * accepts a collection of authority strings to use\n\t * @since 6.1\n\t */\n\tpublic void setAuthoritiesAuthorizationManager(\n\t\t\tAuthorizationManager<Collection<String>> authoritiesAuthorizationManager) {\n\t\tAssert.notNull(authoritiesAuthorizationManager, \"authoritiesAuthorizationManager cannot be null\");\n\t\tthis.authoritiesAuthorizationManager = authoritiesAuthorizationManager;\n\t}\n\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMethodInvocation mi) {\n\t\tSet<String> authorities = getAuthorities(mi);\n\t\treturn authorities.isEmpty() ? null\n\t\t\t\t: this.authoritiesAuthorizationManager.authorize(authentication, authorities);\n\t}\n\n\tprivate Set<String> getAuthorities(MethodInvocation methodInvocation) {\n\t\tMethod method = methodInvocation.getMethod();\n\t\tObject target = methodInvocation.getThis();\n\t\tClass<?> targetClass = (target != null) ? target.getClass() : null;\n\t\tMethodClassKey cacheKey = new MethodClassKey(method, targetClass);\n\t\treturn this.cachedAuthorities.computeIfAbsent(cacheKey, (k) -> resolveAuthorities(method, targetClass));\n\t}\n\n\tprivate Set<String> resolveAuthorities(Method method, @Nullable Class<?> targetClass) {\n\t\tSecured secured = findSecuredAnnotation(method, targetClass);\n\t\treturn (secured != null) ? Set.of(secured.value()) : Collections.emptySet();\n\t}\n\n\tprivate @Nullable Secured findSecuredAnnotation(Method method, @Nullable Class<?> targetClass) {\n\t\tClass<?> targetClassToUse = (targetClass != null) ? targetClass : method.getDeclaringClass();\n\t\treturn this.scanner.scan(method, targetClassToUse);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/ThrowingMethodAuthorizationDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationResult;\n\n/**\n * An implementation of {@link MethodAuthorizationDeniedHandler} that throws\n * {@link AuthorizationDeniedException}\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic final class ThrowingMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {\n\n\t@Override\n\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {\n\t\tif (authorizationResult instanceof AuthorizationDeniedException denied) {\n\t\t\tthrow denied;\n\t\t}\n\t\tthrow new AuthorizationDeniedException(\"Access Denied\", authorizationResult);\n\t}\n\n\t@Override\n\tpublic Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,\n\t\t\tAuthorizationResult authorizationResult) {\n\t\tif (authorizationResult instanceof AuthorizationDeniedException denied) {\n\t\t\tthrow denied;\n\t\t}\n\t\tthrow new AuthorizationDeniedException(\"Access Denied\", authorizationResult);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/method/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.authorization.method;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/authorization/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.authorization;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextSupport.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Callable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * An internal support class that wraps {@link Callable} with\n * {@link DelegatingSecurityContextCallable} and {@link Runnable} with\n * {@link DelegatingSecurityContextRunnable}\n *\n * @author Rob Winch\n * @since 3.2\n */\nabstract class AbstractDelegatingSecurityContextSupport {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final @Nullable SecurityContext securityContext;\n\n\t/**\n\t * Creates a new {@link AbstractDelegatingSecurityContextSupport} that uses the\n\t * specified {@link SecurityContext}.\n\t * @param securityContext the {@link SecurityContext} to use for each\n\t * {@link DelegatingSecurityContextRunnable} and each\n\t * {@link DelegatingSecurityContextCallable} or null to default to the current\n\t * {@link SecurityContext}.\n\t */\n\tAbstractDelegatingSecurityContextSupport(@Nullable SecurityContext securityContext) {\n\t\tthis.securityContext = securityContext;\n\t}\n\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tprotected final Runnable wrap(Runnable delegate) {\n\t\treturn DelegatingSecurityContextRunnable.create(delegate, this.securityContext,\n\t\t\t\tthis.securityContextHolderStrategy);\n\t}\n\n\tprotected final <T> Callable<T> wrap(Callable<T> delegate) {\n\t\treturn DelegatingSecurityContextCallable.create(delegate, this.securityContext,\n\t\t\t\tthis.securityContextHolderStrategy);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Callable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Wraps a delegate {@link Callable} with logic for setting up a {@link SecurityContext}\n * before invoking the delegate {@link Callable} and then removing the\n * {@link SecurityContext} after the delegate has completed.\n * </p>\n * <p>\n * If there is a {@link SecurityContext} that already exists, it will be restored after\n * the {@link #call()} method is invoked.\n * </p>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class DelegatingSecurityContextCallable<V> implements Callable<V> {\n\n\tprivate final Callable<V> delegate;\n\n\tprivate final boolean explicitSecurityContextProvided;\n\n\t/**\n\t * The {@link SecurityContext} that the delegate {@link Callable} will be ran as.\n\t */\n\tprivate SecurityContext delegateSecurityContext;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t/**\n\t * The {@link SecurityContext} that was on the {@link SecurityContextHolder} prior to\n\t * being set to the delegateSecurityContext.\n\t */\n\tprivate @Nullable SecurityContext originalSecurityContext;\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextCallable} with a specific\n\t * {@link SecurityContext}.\n\t * @param delegate the delegate {@link DelegatingSecurityContextCallable} to run with\n\t * the specified {@link SecurityContext}. Cannot be null.\n\t * @param securityContext the {@link SecurityContext} to establish for the delegate\n\t * {@link Callable}. Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextCallable(Callable<V> delegate, SecurityContext securityContext) {\n\t\tthis(delegate, securityContext, true);\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextCallable} with the\n\t * {@link SecurityContext} from the {@link SecurityContextHolder}.\n\t * @param delegate the delegate {@link Callable} to run under the current\n\t * {@link SecurityContext}. Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextCallable(Callable<V> delegate) {\n\t\tthis(delegate, SecurityContextHolder.getContext(), false);\n\t}\n\n\tprivate DelegatingSecurityContextCallable(Callable<V> delegate, SecurityContext securityContext,\n\t\t\tboolean explicitSecurityContextProvided) {\n\t\tAssert.notNull(delegate, \"delegate cannot be null\");\n\t\tAssert.notNull(securityContext, \"securityContext cannot be null\");\n\t\tthis.delegate = delegate;\n\t\tthis.delegateSecurityContext = securityContext;\n\t\tthis.explicitSecurityContextProvided = explicitSecurityContextProvided;\n\t}\n\n\t@Override\n\tpublic V call() throws Exception {\n\t\tthis.originalSecurityContext = this.securityContextHolderStrategy.getContext();\n\t\ttry {\n\t\t\tthis.securityContextHolderStrategy.setContext(this.delegateSecurityContext);\n\t\t\treturn this.delegate.call();\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContext emptyContext = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\tif (emptyContext.equals(this.originalSecurityContext)) {\n\t\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.securityContextHolderStrategy.setContext(this.originalSecurityContext);\n\t\t\t}\n\t\t\tthis.originalSecurityContext = null;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\tif (!this.explicitSecurityContextProvided) {\n\t\t\tthis.delegateSecurityContext = securityContextHolderStrategy.getContext();\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.delegate.toString();\n\t}\n\n\t/**\n\t * Creates a {@link DelegatingSecurityContextCallable} and with the given\n\t * {@link Callable} and {@link SecurityContext}, but if the securityContext is null\n\t * will defaults to the current {@link SecurityContext} on the\n\t * {@link SecurityContextHolder}\n\t * @param delegate the delegate {@link DelegatingSecurityContextCallable} to run with\n\t * the specified {@link SecurityContext}. Cannot be null.\n\t * @param securityContext the {@link SecurityContext} to establish for the delegate\n\t * {@link Callable}. If null, defaults to {@link SecurityContextHolder#getContext()}\n\t * @return\n\t */\n\tpublic static <V> Callable<V> create(Callable<V> delegate, SecurityContext securityContext) {\n\t\treturn (securityContext != null) ? new DelegatingSecurityContextCallable<>(delegate, securityContext)\n\t\t\t\t: new DelegatingSecurityContextCallable<>(delegate);\n\t}\n\n\tstatic <V> Callable<V> create(Callable<V> delegate, @Nullable SecurityContext securityContext,\n\t\t\tSecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(delegate, \"delegate cannot be null\");\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tDelegatingSecurityContextCallable<V> callable = (securityContext != null)\n\t\t\t\t? new DelegatingSecurityContextCallable<>(delegate, securityContext)\n\t\t\t\t: new DelegatingSecurityContextCallable<>(delegate);\n\t\tcallable.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\treturn callable;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Executor;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Executor} which wraps each {@link Runnable} in a\n * {@link DelegatingSecurityContextRunnable}.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class DelegatingSecurityContextExecutor extends AbstractDelegatingSecurityContextSupport implements Executor {\n\n\tprivate final Executor delegate;\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextExecutor} that uses the specified\n\t * {@link SecurityContext}.\n\t * @param delegateExecutor the {@link Executor} to delegate to. Cannot be null.\n\t * @param securityContext the {@link SecurityContext} to use for each\n\t * {@link DelegatingSecurityContextRunnable} or null to default to the current\n\t * {@link SecurityContext}\n\t */\n\tpublic DelegatingSecurityContextExecutor(Executor delegateExecutor, @Nullable SecurityContext securityContext) {\n\t\tsuper(securityContext);\n\t\tAssert.notNull(delegateExecutor, \"delegateExecutor cannot be null\");\n\t\tthis.delegate = delegateExecutor;\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextExecutor} that uses the current\n\t * {@link SecurityContext} from the {@link SecurityContextHolder} at the time the task\n\t * is submitted.\n\t * @param delegate the {@link Executor} to delegate to. Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextExecutor(Executor delegate) {\n\t\tthis(delegate, null);\n\t}\n\n\t@Override\n\tpublic final void execute(Runnable task) {\n\t\tthis.delegate.execute(wrap(task));\n\t}\n\n\tprotected final Executor getDelegateExecutor() {\n\t\treturn this.delegate;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tsuper.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * An {@link ExecutorService} which wraps each {@link Runnable} in a\n * {@link DelegatingSecurityContextRunnable} and each {@link Callable} in a\n * {@link DelegatingSecurityContextCallable}.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class DelegatingSecurityContextExecutorService extends DelegatingSecurityContextExecutor\n\t\timplements ExecutorService {\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextExecutorService} that uses the\n\t * specified {@link SecurityContext}.\n\t * @param delegateExecutorService the {@link ExecutorService} to delegate to. Cannot\n\t * be null.\n\t * @param securityContext the {@link SecurityContext} to use for each\n\t * {@link DelegatingSecurityContextRunnable} and each\n\t * {@link DelegatingSecurityContextCallable}.\n\t */\n\tpublic DelegatingSecurityContextExecutorService(ExecutorService delegateExecutorService,\n\t\t\t@Nullable SecurityContext securityContext) {\n\t\tsuper(delegateExecutorService, securityContext);\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextExecutorService} that uses the\n\t * current {@link SecurityContext} from the {@link SecurityContextHolder}.\n\t * @param delegate the {@link ExecutorService} to delegate to. Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextExecutorService(ExecutorService delegate) {\n\t\tthis(delegate, null);\n\t}\n\n\t@Override\n\tpublic final void shutdown() {\n\t\tgetDelegate().shutdown();\n\t}\n\n\t@Override\n\tpublic final List<Runnable> shutdownNow() {\n\t\treturn getDelegate().shutdownNow();\n\t}\n\n\t@Override\n\tpublic final boolean isShutdown() {\n\t\treturn getDelegate().isShutdown();\n\t}\n\n\t@Override\n\tpublic final boolean isTerminated() {\n\t\treturn getDelegate().isTerminated();\n\t}\n\n\t@Override\n\tpublic final boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {\n\t\treturn getDelegate().awaitTermination(timeout, unit);\n\t}\n\n\t@Override\n\tpublic final <T> Future<T> submit(Callable<T> task) {\n\t\treturn getDelegate().submit(wrap(task));\n\t}\n\n\t@Override\n\tpublic final <T> Future<T> submit(Runnable task, T result) {\n\t\treturn getDelegate().submit(wrap(task), result);\n\t}\n\n\t@Override\n\tpublic final Future<?> submit(Runnable task) {\n\t\treturn getDelegate().submit(wrap(task));\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic final List invokeAll(Collection tasks) throws InterruptedException {\n\t\ttasks = createTasks(tasks);\n\t\treturn getDelegate().invokeAll(tasks);\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic final List invokeAll(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException {\n\t\ttasks = createTasks(tasks);\n\t\treturn getDelegate().invokeAll(tasks, timeout, unit);\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic final Object invokeAny(Collection tasks) throws InterruptedException, ExecutionException {\n\t\ttasks = createTasks(tasks);\n\t\treturn getDelegate().invokeAny(tasks);\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic final Object invokeAny(Collection tasks, long timeout, TimeUnit unit)\n\t\t\tthrows InterruptedException, ExecutionException, TimeoutException {\n\t\ttasks = createTasks(tasks);\n\t\treturn getDelegate().invokeAny(tasks, timeout, unit);\n\t}\n\n\tprivate <T> @Nullable Collection<Callable<T>> createTasks(Collection<Callable<T>> tasks) {\n\t\tif (tasks == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<Callable<T>> results = new ArrayList<>(tasks.size());\n\t\tfor (Callable<T> task : tasks) {\n\t\t\tresults.add(wrap(task));\n\t\t}\n\t\treturn results;\n\t}\n\n\tprivate ExecutorService getDelegate() {\n\t\treturn (ExecutorService) getDelegateExecutor();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Wraps a delegate {@link Runnable} with logic for setting up a {@link SecurityContext}\n * before invoking the delegate {@link Runnable} and then removing the\n * {@link SecurityContext} after the delegate has completed.\n * </p>\n * <p>\n * If there is a {@link SecurityContext} that already exists, it will be restored after\n * the {@link #run()} method is invoked.\n * </p>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class DelegatingSecurityContextRunnable implements Runnable {\n\n\tprivate final Runnable delegate;\n\n\tprivate final boolean explicitSecurityContextProvided;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t/**\n\t * The {@link SecurityContext} that the delegate {@link Runnable} will be ran as.\n\t */\n\tprivate SecurityContext delegateSecurityContext;\n\n\t/**\n\t * The {@link SecurityContext} that was on the {@link SecurityContextHolder} prior to\n\t * being set to the delegateSecurityContext.\n\t */\n\tprivate @Nullable SecurityContext originalSecurityContext;\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextRunnable} with a specific\n\t * {@link SecurityContext}.\n\t * @param delegate the delegate {@link Runnable} to run with the specified\n\t * {@link SecurityContext}. Cannot be null.\n\t * @param securityContext the {@link SecurityContext} to establish for the delegate\n\t * {@link Runnable}. Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextRunnable(Runnable delegate, SecurityContext securityContext) {\n\t\tthis(delegate, securityContext, true);\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextRunnable} with the\n\t * {@link SecurityContext} from the {@link SecurityContextHolder}.\n\t * @param delegate the delegate {@link Runnable} to run under the current\n\t * {@link SecurityContext}. Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextRunnable(Runnable delegate) {\n\t\tthis(delegate, SecurityContextHolder.getContext(), false);\n\t}\n\n\tprivate DelegatingSecurityContextRunnable(Runnable delegate, SecurityContext securityContext,\n\t\t\tboolean explicitSecurityContextProvided) {\n\t\tAssert.notNull(delegate, \"delegate cannot be null\");\n\t\tAssert.notNull(securityContext, \"securityContext cannot be null\");\n\t\tthis.delegate = delegate;\n\t\tthis.delegateSecurityContext = securityContext;\n\t\tthis.explicitSecurityContextProvided = explicitSecurityContextProvided;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tthis.originalSecurityContext = this.securityContextHolderStrategy.getContext();\n\t\ttry {\n\t\t\tthis.securityContextHolderStrategy.setContext(this.delegateSecurityContext);\n\t\t\tthis.delegate.run();\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContext emptyContext = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\tif (emptyContext.equals(this.originalSecurityContext)) {\n\t\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.securityContextHolderStrategy.setContext(this.originalSecurityContext);\n\t\t\t}\n\t\t\tthis.originalSecurityContext = null;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t\tif (!this.explicitSecurityContextProvided) {\n\t\t\tthis.delegateSecurityContext = this.securityContextHolderStrategy.getContext();\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.delegate.toString();\n\t}\n\n\t/**\n\t * Factory method for creating a {@link DelegatingSecurityContextRunnable}.\n\t * @param delegate the original {@link Runnable} that will be delegated to after\n\t * establishing a {@link SecurityContext} on the {@link SecurityContextHolder}. Cannot\n\t * have null.\n\t * @param securityContext the {@link SecurityContext} to establish before invoking the\n\t * delegate {@link Runnable}. If null, the current {@link SecurityContext} from the\n\t * {@link SecurityContextHolder} will be used.\n\t * @return\n\t */\n\tpublic static Runnable create(Runnable delegate, @Nullable SecurityContext securityContext) {\n\t\tAssert.notNull(delegate, \"delegate cannot be  null\");\n\t\treturn (securityContext != null) ? new DelegatingSecurityContextRunnable(delegate, securityContext)\n\t\t\t\t: new DelegatingSecurityContextRunnable(delegate);\n\t}\n\n\tstatic Runnable create(Runnable delegate, @Nullable SecurityContext securityContext,\n\t\t\tSecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(delegate, \"delegate cannot be  null\");\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tDelegatingSecurityContextRunnable runnable = (securityContext != null)\n\t\t\t\t? new DelegatingSecurityContextRunnable(delegate, securityContext)\n\t\t\t\t: new DelegatingSecurityContextRunnable(delegate);\n\t\trunnable.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\treturn runnable;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * An {@link ScheduledExecutorService} which wraps each {@link Runnable} in a\n * {@link DelegatingSecurityContextRunnable} and each {@link Callable} in a\n * {@link DelegatingSecurityContextCallable}.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class DelegatingSecurityContextScheduledExecutorService extends DelegatingSecurityContextExecutorService\n\t\timplements ScheduledExecutorService {\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextScheduledExecutorService} that uses\n\t * the specified {@link SecurityContext}.\n\t * @param delegateScheduledExecutorService the {@link ScheduledExecutorService} to\n\t * delegate to. Cannot be null.\n\t * @param securityContext the {@link SecurityContext} to use for each\n\t * {@link DelegatingSecurityContextRunnable} and each\n\t * {@link DelegatingSecurityContextCallable}.\n\t */\n\tpublic DelegatingSecurityContextScheduledExecutorService(ScheduledExecutorService delegateScheduledExecutorService,\n\t\t\t@Nullable SecurityContext securityContext) {\n\t\tsuper(delegateScheduledExecutorService, securityContext);\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextScheduledExecutorService} that uses\n\t * the current {@link SecurityContext} from the {@link SecurityContextHolder}.\n\t * @param delegate the {@link ScheduledExecutorService} to delegate to. Cannot be\n\t * null.\n\t */\n\tpublic DelegatingSecurityContextScheduledExecutorService(ScheduledExecutorService delegate) {\n\t\tthis(delegate, null);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {\n\t\treturn getDelegate().schedule(wrap(command), delay, unit);\n\t}\n\n\t@Override\n\tpublic <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {\n\t\treturn getDelegate().schedule(wrap(callable), delay, unit);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {\n\t\treturn getDelegate().scheduleAtFixedRate(wrap(command), initialDelay, period, unit);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {\n\t\treturn getDelegate().scheduleWithFixedDelay(wrap(command), initialDelay, delay, unit);\n\t}\n\n\tprivate ScheduledExecutorService getDelegate() {\n\t\treturn (ScheduledExecutorService) getDelegateExecutor();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/concurrent/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.concurrent;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/context/DelegatingApplicationListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.context;\n\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.SmartApplicationListener;\nimport org.springframework.util.Assert;\n\n/**\n * Used for delegating to a number of SmartApplicationListener instances. This is useful\n * when needing to register an SmartApplicationListener with the ApplicationContext\n * programmatically.\n *\n * @author Rob Winch\n */\npublic final class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent> {\n\n\tprivate List<SmartApplicationListener> listeners = new CopyOnWriteArrayList<>();\n\n\t@Override\n\tpublic void onApplicationEvent(ApplicationEvent event) {\n\t\tif (event == null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (SmartApplicationListener listener : this.listeners) {\n\t\t\tObject source = event.getSource();\n\t\t\tif (source != null && listener.supportsEventType(event.getClass())\n\t\t\t\t\t&& listener.supportsSourceType(source.getClass())) {\n\t\t\t\tlistener.onApplicationEvent(event);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Adds a new SmartApplicationListener to use.\n\t * @param smartApplicationListener the SmartApplicationListener to use. Cannot be\n\t * null.\n\t */\n\tpublic void addListener(SmartApplicationListener smartApplicationListener) {\n\t\tAssert.notNull(smartApplicationListener, \"smartApplicationListener cannot be null\");\n\t\tthis.listeners.add(smartApplicationListener);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/context/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.context;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/converter/RsaKeyConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.converter;\n\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.security.KeyFactory;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.jspecify.annotations.NonNull;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.util.Assert;\n\n/**\n * Used for creating {@link java.security.Key} converter instances\n *\n * @author Josh Cummings\n * @author Shazin Sadakath\n * @since 5.2\n */\npublic final class RsaKeyConverters {\n\n\tprivate static final String DASHES = \"-----\";\n\n\tprivate static final String PKCS8_PEM_HEADER = DASHES + \"BEGIN PRIVATE KEY\" + DASHES;\n\n\tprivate static final String PKCS8_PEM_FOOTER = DASHES + \"END PRIVATE KEY\" + DASHES;\n\n\tprivate static final String X509_PEM_HEADER = DASHES + \"BEGIN PUBLIC KEY\" + DASHES;\n\n\tprivate static final String X509_PEM_FOOTER = DASHES + \"END PUBLIC KEY\" + DASHES;\n\n\tprivate static final String X509_CERT_HEADER = DASHES + \"BEGIN CERTIFICATE\" + DASHES;\n\n\tprivate static final String X509_CERT_FOOTER = DASHES + \"END CERTIFICATE\" + DASHES;\n\n\tprivate RsaKeyConverters() {\n\t}\n\n\t/**\n\t * Construct a {@link Converter} for converting a PEM-encoded PKCS#8 RSA Private Key\n\t * into a {@link RSAPrivateKey}.\n\t *\n\t * Note that keys are often formatted in PKCS#1 and this can easily be identified by\n\t * the header. If the key file begins with \"-----BEGIN RSA PRIVATE KEY-----\", then it\n\t * is PKCS#1. If it is PKCS#8 formatted, then it begins with \"-----BEGIN PRIVATE\n\t * KEY-----\".\n\t *\n\t * This converter does not close the {@link InputStream} in order to avoid making\n\t * non-portable assumptions about the streams' origin and further use.\n\t * @return A {@link Converter} that can read a PEM-encoded PKCS#8 RSA Private Key and\n\t * return a {@link RSAPrivateKey}.\n\t */\n\tpublic static Converter<InputStream, RSAPrivateKey> pkcs8() {\n\t\tKeyFactory keyFactory = rsaFactory();\n\t\treturn (source) -> {\n\t\t\tList<String> lines = readAllLines(source);\n\t\t\tAssert.isTrue(!lines.isEmpty() && lines.get(0).startsWith(PKCS8_PEM_HEADER),\n\t\t\t\t\t\"Key is not in PEM-encoded PKCS#8 format, please check that the header begins with \"\n\t\t\t\t\t\t\t+ PKCS8_PEM_HEADER);\n\t\t\tStringBuilder base64Encoded = new StringBuilder();\n\t\t\tif (lines.size() == 1) {\n\t\t\t\tbase64Encoded.append(lines.get(0)\n\t\t\t\t\t.replace(PKCS8_PEM_HEADER, \"\")\n\t\t\t\t\t.replace(PKCS8_PEM_FOOTER, \"\")\n\t\t\t\t\t.replaceAll(\"\\\\s+\", \"\"));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfor (String line : lines) {\n\t\t\t\t\tif (RsaKeyConverters.isNotPkcs8Wrapper(line)) {\n\t\t\t\t\t\tbase64Encoded.append(line);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbyte[] pkcs8 = Base64.getDecoder().decode(base64Encoded.toString());\n\t\t\ttry {\n\t\t\t\treturn (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8));\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(ex);\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Construct a {@link Converter} for converting a PEM-encoded X.509 RSA Public Key or\n\t * X.509 Certificate into a {@link RSAPublicKey}.\n\t *\n\t * This converter does not close the {@link InputStream} in order to avoid making\n\t * non-portable assumptions about the streams' origin and further use.\n\t * @return A {@link Converter} that can read a PEM-encoded X.509 RSA Public Key and\n\t * return a {@link RSAPublicKey}.\n\t */\n\tpublic static Converter<InputStream, RSAPublicKey> x509() {\n\t\tX509PemDecoder pemDecoder = new X509PemDecoder(rsaFactory());\n\t\tX509CertificateDecoder certDecoder = new X509CertificateDecoder(x509CertificateFactory());\n\t\treturn (source) -> {\n\t\t\tList<String> lines = readAllLines(source);\n\t\t\tAssert.notEmpty(lines, \"Input stream is empty\");\n\t\t\tString encodingHint = lines.get(0);\n\t\t\tConverter<List<String>, RSAPublicKey> decoder = encodingHint.startsWith(X509_PEM_HEADER) ? pemDecoder\n\t\t\t\t\t: encodingHint.startsWith(X509_CERT_HEADER) ? certDecoder : null;\n\t\t\tAssert.notNull(decoder,\n\t\t\t\t\t\"Key is not in PEM-encoded X.509 format or a valid X.509 certificate, please check that the header begins with \"\n\t\t\t\t\t\t\t+ X509_PEM_HEADER + \" or \" + X509_CERT_HEADER);\n\t\t\treturn decoder.convert(lines);\n\t\t};\n\t}\n\n\tprivate static CertificateFactory x509CertificateFactory() {\n\t\ttry {\n\t\t\treturn CertificateFactory.getInstance(\"X.509\");\n\t\t}\n\t\tcatch (CertificateException ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static List<String> readAllLines(InputStream source) {\n\t\tBufferedReader reader = new BufferedReader(new InputStreamReader(source));\n\t\treturn reader.lines().collect(Collectors.toList());\n\t}\n\n\tprivate static KeyFactory rsaFactory() {\n\t\ttry {\n\t\t\treturn KeyFactory.getInstance(\"RSA\");\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\tprivate static boolean isNotPkcs8Wrapper(String line) {\n\t\treturn !PKCS8_PEM_HEADER.equals(line) && !PKCS8_PEM_FOOTER.equals(line);\n\t}\n\n\tprivate static class X509PemDecoder implements Converter<List<String>, RSAPublicKey> {\n\n\t\tprivate final KeyFactory keyFactory;\n\n\t\tX509PemDecoder(KeyFactory keyFactory) {\n\t\t\tthis.keyFactory = keyFactory;\n\t\t}\n\n\t\t@Override\n\t\tpublic @NonNull RSAPublicKey convert(List<String> lines) {\n\t\t\tStringBuilder base64Encoded = new StringBuilder();\n\t\t\tif (lines.size() == 1) {\n\t\t\t\tbase64Encoded.append(\n\t\t\t\t\t\tlines.get(0).replace(X509_PEM_HEADER, \"\").replace(X509_PEM_FOOTER, \"\").replaceAll(\"\\\\s+\", \"\"));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfor (String line : lines) {\n\t\t\t\t\tif (isNotX509PemWrapper(line)) {\n\t\t\t\t\t\tbase64Encoded.append(line);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbyte[] x509 = Base64.getDecoder().decode(base64Encoded.toString());\n\t\t\ttry {\n\t\t\t\treturn (RSAPublicKey) this.keyFactory.generatePublic(new X509EncodedKeySpec(x509));\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean isNotX509PemWrapper(String line) {\n\t\t\treturn !X509_PEM_HEADER.equals(line) && !X509_PEM_FOOTER.equals(line);\n\t\t}\n\n\t}\n\n\tprivate static class X509CertificateDecoder implements Converter<List<String>, RSAPublicKey> {\n\n\t\tprivate final CertificateFactory certificateFactory;\n\n\t\tX509CertificateDecoder(CertificateFactory certificateFactory) {\n\t\t\tthis.certificateFactory = certificateFactory;\n\t\t}\n\n\t\t@Override\n\t\tpublic @NonNull RSAPublicKey convert(List<String> lines) {\n\t\t\tStringBuilder base64Encoded = new StringBuilder();\n\t\t\tif (lines.size() == 1) {\n\t\t\t\tbase64Encoded.append(lines.get(0)\n\t\t\t\t\t.replace(X509_CERT_HEADER, \"\")\n\t\t\t\t\t.replace(X509_CERT_FOOTER, \"\")\n\t\t\t\t\t.replaceAll(\"\\\\s+\", \"\"));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfor (String line : lines) {\n\t\t\t\t\tif (isNotX509CertificateWrapper(line)) {\n\t\t\t\t\t\tbase64Encoded.append(line);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbyte[] x509 = Base64.getDecoder().decode(base64Encoded.toString());\n\t\t\ttry (InputStream x509CertStream = new ByteArrayInputStream(x509)) {\n\t\t\t\tX509Certificate certificate = (X509Certificate) this.certificateFactory\n\t\t\t\t\t.generateCertificate(x509CertStream);\n\t\t\t\treturn (RSAPublicKey) certificate.getPublicKey();\n\t\t\t}\n\t\t\tcatch (CertificateException | IOException ex) {\n\t\t\t\tthrow new IllegalArgumentException(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean isNotX509CertificateWrapper(String line) {\n\t\t\treturn !X509_CERT_HEADER.equals(line) && !X509_CERT_FOOTER.equals(line);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/converter/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.converter;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/AuthenticatedPrincipal.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport org.springframework.security.authentication.AuthenticationManager;\n\n/**\n * Representation of an authenticated <code>Principal</code> once an\n * {@link Authentication} request has been successfully authenticated by the\n * {@link AuthenticationManager#authenticate(Authentication)} method.\n *\n * Implementors typically provide their own representation of a <code>Principal</code>,\n * which usually contains information describing the <code>Principal</code> entity, such\n * as, first/middle/last name, address, email, phone, id, etc.\n *\n * This interface allows implementors to expose specific attributes of their custom\n * representation of <code>Principal</code> in a generic way.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see Authentication#getPrincipal()\n * @see org.springframework.security.core.userdetails.UserDetails\n */\npublic interface AuthenticatedPrincipal {\n\n\t/**\n\t * Returns the name of the authenticated <code>Principal</code>. Never\n\t * <code>null</code>.\n\t * @return the name of the authenticated <code>Principal</code>\n\t */\n\tString getName();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/Authentication.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.io.Serializable;\nimport java.security.Principal;\nimport java.util.Collection;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * Represents the token for an authentication request or for an authenticated principal\n * once the request has been processed by the\n * {@link AuthenticationManager#authenticate(Authentication)} method.\n * <p>\n * Once the request has been authenticated, the <tt>Authentication</tt> will usually be\n * stored in a thread-local <tt>SecurityContext</tt> managed by the\n * {@link SecurityContextHolder} by the authentication mechanism which is being used. An\n * explicit authentication can be achieved, without using one of Spring Security's\n * authentication mechanisms, by creating an <tt>Authentication</tt> instance and using\n * the code:\n *\n * <pre>\n * SecurityContext context = SecurityContextHolder.createEmptyContext();\n * context.setAuthentication(anAuthentication);\n * SecurityContextHolder.setContext(context);\n * </pre>\n *\n * Note that unless the <tt>Authentication</tt> has the <tt>authenticated</tt> property\n * set to <tt>true</tt>, it will still be authenticated by any security interceptor (for\n * method or web invocations) which encounters it.\n * <p>\n * In most cases, the framework transparently takes care of managing the security context\n * and authentication objects for you.\n *\n * @author Ben Alex\n */\npublic interface Authentication extends Principal, Serializable {\n\n\t/**\n\t * Set by an <code>AuthenticationManager</code> to indicate the authorities that the\n\t * principal has been granted. Note that classes should not rely on this value as\n\t * being valid unless it has been set by a trusted <code>AuthenticationManager</code>.\n\t * <p>\n\t * Implementations should ensure that modifications to the returned collection array\n\t * do not affect the state of the Authentication object, or use an unmodifiable\n\t * instance.\n\t * </p>\n\t * @return the authorities granted to the principal, or an empty collection if the\n\t * token has not been authenticated. Never null.\n\t */\n\tCollection<? extends GrantedAuthority> getAuthorities();\n\n\t/**\n\t * The credentials that prove the principal is correct. This is usually a password,\n\t * but could be anything relevant to the <code>AuthenticationManager</code>. Callers\n\t * are expected to populate the credentials.\n\t * @return the credentials that prove the identity of the <code>Principal</code>\n\t */\n\t@Nullable Object getCredentials();\n\n\t/**\n\t * Stores additional details about the authentication request. These might be an IP\n\t * address, certificate serial number etc.\n\t * @return additional details about the authentication request, or <code>null</code>\n\t * if not used\n\t */\n\t@Nullable Object getDetails();\n\n\t/**\n\t * The identity of the principal being authenticated. In the case of an authentication\n\t * request with username and password, this would be the username. Callers are\n\t * expected to populate the principal for an authentication request.\n\t * <p>\n\t * The <tt>AuthenticationManager</tt> implementation will often return an\n\t * <tt>Authentication</tt> containing richer information as the principal for use by\n\t * the application. Many of the authentication providers will create a\n\t * {@code UserDetails} object as the principal.\n\t * @return the <code>Principal</code> being authenticated or the authenticated\n\t * principal after authentication.\n\t */\n\t@Nullable Object getPrincipal();\n\n\t/**\n\t * Used to indicate to {@code AbstractSecurityInterceptor} whether it should present\n\t * the authentication token to the <code>AuthenticationManager</code>. Typically an\n\t * <code>AuthenticationManager</code> (or, more often, one of its\n\t * <code>AuthenticationProvider</code>s) will return an immutable authentication token\n\t * after successful authentication, in which case that token can safely return\n\t * <code>true</code> to this method. Returning <code>true</code> will improve\n\t * performance, as calling the <code>AuthenticationManager</code> for every request\n\t * will no longer be necessary.\n\t * <p>\n\t * For security reasons, implementations of this interface should be very careful\n\t * about returning <code>true</code> from this method unless they are either\n\t * immutable, or have some way of ensuring the properties have not been changed since\n\t * original creation.\n\t * @return true if the token has been authenticated and the\n\t * <code>AbstractSecurityInterceptor</code> does not need to present the token to the\n\t * <code>AuthenticationManager</code> again for re-authentication.\n\t */\n\tboolean isAuthenticated();\n\n\t/**\n\t * See {@link #isAuthenticated()} for a full description.\n\t * <p>\n\t * Implementations should <b>always</b> allow this method to be called with a\n\t * <code>false</code> parameter, as this is used by various classes to specify the\n\t * authentication token should not be trusted. If an implementation wishes to reject\n\t * an invocation with a <code>true</code> parameter (which would indicate the\n\t * authentication token is trusted - a potential security risk) the implementation\n\t * should throw an {@link IllegalArgumentException}.\n\t * @param isAuthenticated <code>true</code> if the token should be trusted (which may\n\t * result in an exception) or <code>false</code> if the token should not be trusted\n\t * @throws IllegalArgumentException if an attempt to make the authentication token\n\t * trusted (by passing <code>true</code> as the argument) is rejected due to the\n\t * implementation being immutable or implementing its own alternative approach to\n\t * {@link #isAuthenticated()}\n\t */\n\tvoid setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;\n\n\t/**\n\t * Return an {@link Builder} based on this instance. By default, returns a builder\n\t * that builds a {@link SimpleAuthentication}.\n\t * <p>\n\t * Although a {@code default} method, all {@link Authentication} implementations\n\t * should implement this. The reason is to ensure that the {@link Authentication} type\n\t * is preserved when {@link Builder#build} is invoked. This is especially important in\n\t * the event that your authentication implementation contains custom fields.\n\t * </p>\n\t * <p>\n\t * This isn't strictly necessary since it is recommended that applications code to the\n\t * {@link Authentication} interface and that custom information is often contained in\n\t * the {@link Authentication#getPrincipal} value.\n\t * </p>\n\t * @return an {@link Builder} for building a new {@link Authentication} based on this\n\t * instance\n\t * @since 7.0\n\t */\n\tdefault Builder<?> toBuilder() {\n\t\treturn new SimpleAuthentication.Builder(this);\n\t}\n\n\t/**\n\t * A builder based on a given {@link Authentication} instance\n\t *\n\t * @author Josh Cummings\n\t * @since 7.0\n\t */\n\tinterface Builder<B extends Builder<B>> {\n\n\t\t/**\n\t\t * Mutate the authorities with this {@link Consumer}.\n\t\t * <p>\n\t\t * Note that since a non-empty set of authorities implies an\n\t\t * {@link Authentication} is authenticated, this method also marks the\n\t\t * authentication as {@link #authenticated} by default.\n\t\t * </p>\n\t\t * @param authorities a consumer that receives the full set of authorities\n\t\t * @return the {@link Builder} for additional configuration\n\t\t * @see Authentication#getAuthorities\n\t\t */\n\t\tB authorities(Consumer<Collection<GrantedAuthority>> authorities);\n\n\t\t/**\n\t\t * Use this credential.\n\t\t * <p>\n\t\t * Note that since some credentials are insecure to store, this method is\n\t\t * implemented as unsupported by default. Only implement or use this method if you\n\t\t * support secure storage of the credential or if your implementation also\n\t\t * implements {@link CredentialsContainer} and the credentials are thereby erased.\n\t\t * </p>\n\t\t * @param credentials the credentials to use\n\t\t * @return the {@link Builder} for additional configuration\n\t\t * @see Authentication#getCredentials\n\t\t */\n\t\tdefault B credentials(@Nullable Object credentials) {\n\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\tString.format(\"%s does not store credentials\", this.getClass().getSimpleName()));\n\t\t}\n\n\t\t/**\n\t\t * Use this details object.\n\t\t * <p>\n\t\t * Implementations may choose to use these {@code details} in combination with any\n\t\t * principal from the pre-existing {@link Authentication} instance.\n\t\t * </p>\n\t\t * @param details the details to use\n\t\t * @return the {@link Builder} for additional configuration\n\t\t * @see Authentication#getDetails\n\t\t */\n\t\tB details(@Nullable Object details);\n\n\t\t/**\n\t\t * Use this principal.\n\t\t * <p>\n\t\t * Note that in many cases, the principal is strongly-typed. Implementations may\n\t\t * choose to do a type check and are not necessarily expected to allow any object\n\t\t * as a principal.\n\t\t * </p>\n\t\t * <p>\n\t\t * Implementations may choose to use this {@code principal} in combination with\n\t\t * any principal from the pre-existing {@link Authentication} instance.\n\t\t * </p>\n\t\t * @param principal the principal to use\n\t\t * @return the {@link Builder} for additional configuration\n\t\t * @see Authentication#getPrincipal\n\t\t */\n\t\tB principal(@Nullable Object principal);\n\n\t\t/**\n\t\t * Mark this authentication as authenticated or not\n\t\t * @param authenticated whether this is an authenticated {@link Authentication}\n\t\t * instance\n\t\t * @return the {@link Builder} for additional configuration\n\t\t * @see Authentication#isAuthenticated\n\t\t */\n\t\tB authenticated(boolean authenticated);\n\n\t\t/**\n\t\t * Build an {@link Authentication} instance\n\t\t * @return the {@link Authentication} instance\n\t\t */\n\t\tAuthentication build();\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/AuthenticationException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Abstract superclass for all exceptions related to an {@link Authentication} object\n * being invalid for whatever reason.\n *\n * @author Ben Alex\n */\npublic abstract class AuthenticationException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2018827803361503060L;\n\n\tprivate @Nullable Authentication authenticationRequest;\n\n\t/**\n\t * Constructs an {@code AuthenticationException} with the specified message and root\n\t * cause.\n\t * @param msg the detail message\n\t * @param cause the root cause\n\t */\n\tpublic AuthenticationException(@Nullable String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\t/**\n\t * Constructs an {@code AuthenticationException} with the specified message and no\n\t * root cause.\n\t * @param msg the detail message\n\t */\n\tpublic AuthenticationException(@Nullable String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Get the {@link Authentication} object representing the failed authentication\n\t * attempt.\n\t * <p>\n\t * This field captures the authentication request that was attempted but ultimately\n\t * failed, providing critical information for diagnosing the failure and facilitating\n\t * debugging\n\t * @since 6.5\n\t */\n\tpublic @Nullable Authentication getAuthenticationRequest() {\n\t\treturn this.authenticationRequest;\n\t}\n\n\t/**\n\t * Set the {@link Authentication} object representing the failed authentication\n\t * attempt.\n\t * <p>\n\t * The provided {@code authenticationRequest} should not be null\n\t * @param authenticationRequest the authentication request associated with the failed\n\t * authentication attempt\n\t * @since 6.5\n\t */\n\tpublic void setAuthenticationRequest(@Nullable Authentication authenticationRequest) {\n\t\tAssert.notNull(authenticationRequest, \"authenticationRequest cannot be null\");\n\t\tthis.authenticationRequest = authenticationRequest;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/ComparableVersion.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.math.BigInteger;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Deque;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Properties;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * <p>\n * Generic implementation of version comparison.\n * </p>\n *\n * NOTE: This is a copy from\n * https://github.com/apache/maven/blob/maven-3.6.3/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java\n *\n * Features:\n * <ul>\n * <li>mixing of '<code>-</code>' (hyphen) and '<code>.</code>' (dot) separators,</li>\n * <li>transition between characters and digits also constitutes a separator:\n * <code>1.0alpha1 =&gt; [1, 0, alpha, 1]</code></li>\n * <li>unlimited number of version components,</li>\n * <li>version components in the text can be digits or strings,</li>\n * <li>strings are checked for well-known qualifiers and the qualifier ordering is used\n * for version ordering. Well-known qualifiers (case insensitive) are:\n * <ul>\n * <li><code>alpha</code> or <code>a</code></li>\n * <li><code>beta</code> or <code>b</code></li>\n * <li><code>milestone</code> or <code>m</code></li>\n * <li><code>rc</code> or <code>cr</code></li>\n * <li><code>snapshot</code></li>\n * <li><code>(the empty string)</code> or <code>ga</code> or <code>final</code></li>\n * <li><code>sp</code></li>\n * </ul>\n * Unknown qualifiers are considered after known qualifiers, with lexical order (always\n * case insensitive),</li>\n * <li>a hyphen usually precedes a qualifier, and is always less important than something\n * preceded with a dot.</li>\n * </ul>\n *\n * @see <a href=\n * \"https://cwiki.apache.org/confluence/display/MAVENOLD/Versioning\">\"Versioning\" on Maven\n * Wiki</a>\n * @author <a href=\"mailto:kenney@apache.org\">Kenney Westerhof</a>\n * @author <a href=\"mailto:hboutemy@apache.org\">Hervé Boutemy</a>\n */\nclass ComparableVersion implements Comparable<ComparableVersion> {\n\n\tprivate static final int MAX_INT_ITEM_LENGTH = 9;\n\n\tprivate static final int MAX_LONG_ITEM_LENGTH = 18;\n\n\tprivate String value;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable String canonical;\n\n\tprivate ListItem items;\n\n\tprivate interface Item {\n\n\t\tint INT_ITEM = 3;\n\n\t\tint LONG_ITEM = 4;\n\n\t\tint BIGINTEGER_ITEM = 0;\n\n\t\tint STRING_ITEM = 1;\n\n\t\tint LIST_ITEM = 2;\n\n\t\tint compareTo(@Nullable Item item);\n\n\t\tint getType();\n\n\t\tboolean isNull();\n\n\t}\n\n\t/**\n\t * Represents a numeric item in the version item list that can be represented with an\n\t * int.\n\t */\n\tprivate static class IntItem implements Item {\n\n\t\tprivate final int value;\n\n\t\tpublic static final IntItem ZERO = new IntItem();\n\n\t\tprivate IntItem() {\n\t\t\tthis.value = 0;\n\t\t}\n\n\t\tIntItem(String str) {\n\t\t\tthis.value = Integer.parseInt(str);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn INT_ITEM;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isNull() {\n\t\t\treturn value == 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(@Nullable Item item) {\n\t\t\tif (item == null) {\n\t\t\t\treturn (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1\n\t\t\t}\n\n\t\t\treturn switch (item.getType()) {\n\t\t\t\tcase INT_ITEM -> Integer.compare(value, ((IntItem) item).value);\n\t\t\t\tcase LONG_ITEM, BIGINTEGER_ITEM -> -1;\n\t\t\t\tcase STRING_ITEM -> 1; // 1.1 > 1-sp\n\t\t\t\tcase LIST_ITEM -> 1; // 1.1 > 1-1\n\t\t\t\tdefault -> throw new IllegalStateException(\"invalid item: \" + item.getClass());\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tIntItem intItem = (IntItem) o;\n\n\t\t\treturn value == intItem.value;\n\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Integer.toString(value);\n\t\t}\n\n\t}\n\n\t/**\n\t * Represents a numeric item in the version item list that can be represented with a\n\t * long.\n\t */\n\tprivate static class LongItem implements Item {\n\n\t\tprivate final long value;\n\n\t\tLongItem(String str) {\n\t\t\tthis.value = Long.parseLong(str);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn LONG_ITEM;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isNull() {\n\t\t\treturn value == 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(@Nullable Item item) {\n\t\t\tif (item == null) {\n\t\t\t\treturn (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1\n\t\t\t}\n\n\t\t\treturn switch (item.getType()) {\n\t\t\t\tcase INT_ITEM -> 1;\n\t\t\t\tcase LONG_ITEM -> Long.compare(value, ((LongItem) item).value);\n\t\t\t\tcase BIGINTEGER_ITEM -> -1;\n\t\t\t\tcase STRING_ITEM -> 1; // 1.1 > 1-sp\n\t\t\t\tcase LIST_ITEM -> 1; // 1.1 > 1-1\n\t\t\t\tdefault -> throw new IllegalStateException(\"invalid item: \" + item.getClass());\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tLongItem longItem = (LongItem) o;\n\n\t\t\treturn value == longItem.value;\n\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn (int) (value ^ (value >>> 32));\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn Long.toString(value);\n\t\t}\n\n\t}\n\n\t/**\n\t * Represents a numeric item in the version item list.\n\t */\n\tprivate static class BigIntegerItem implements Item {\n\n\t\tprivate final BigInteger value;\n\n\t\tBigIntegerItem(String str) {\n\t\t\tthis.value = new BigInteger(str);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn BIGINTEGER_ITEM;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isNull() {\n\t\t\treturn BigInteger.ZERO.equals(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(@Nullable Item item) {\n\t\t\tif (item == null) {\n\t\t\t\treturn BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1\n\t\t\t}\n\n\t\t\treturn switch (item.getType()) {\n\t\t\t\tcase INT_ITEM, LONG_ITEM -> 1;\n\t\t\t\tcase BIGINTEGER_ITEM -> value.compareTo(((BigIntegerItem) item).value);\n\t\t\t\tcase STRING_ITEM -> 1; // 1.1 > 1-sp\n\t\t\t\tcase LIST_ITEM -> 1; // 1.1 > 1-1\n\t\t\t\tdefault -> throw new IllegalStateException(\"invalid item: \" + item.getClass());\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tBigIntegerItem that = (BigIntegerItem) o;\n\n\t\t\treturn value.equals(that.value);\n\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value.hashCode();\n\t\t}\n\n\t\tpublic String toString() {\n\t\t\treturn value.toString();\n\t\t}\n\n\t}\n\n\t/**\n\t * Represents a string in the version item list, usually a qualifier.\n\t */\n\tprivate static class StringItem implements Item {\n\n\t\tprivate static final List<String> QUALIFIERS = Arrays.asList(\"alpha\", \"beta\", \"milestone\", \"rc\", \"snapshot\", \"\",\n\t\t\t\t\"sp\");\n\n\t\tprivate static final Properties ALIASES = new Properties();\n\t\tstatic {\n\t\t\tALIASES.put(\"ga\", \"\");\n\t\t\tALIASES.put(\"final\", \"\");\n\t\t\tALIASES.put(\"release\", \"\");\n\t\t\tALIASES.put(\"cr\", \"rc\");\n\t\t}\n\n\t\t/**\n\t\t * A comparable value for the empty-string qualifier. This one is used to\n\t\t * determine if a given qualifier makes the version older than one without a\n\t\t * qualifier, or more recent.\n\t\t */\n\t\tprivate static final String RELEASE_VERSION_INDEX = String.valueOf(QUALIFIERS.indexOf(\"\"));\n\n\t\tprivate final String value;\n\n\t\tStringItem(String value, boolean followedByDigit) {\n\t\t\tif (followedByDigit && value.length() == 1) {\n\t\t\t\t// a1 = alpha-1, b1 = beta-1, m1 = milestone-1\n\t\t\t\tvalue = switch (value.charAt(0)) {\n\t\t\t\t\tcase 'a' -> \"alpha\";\n\t\t\t\t\tcase 'b' -> \"beta\";\n\t\t\t\t\tcase 'm' -> \"milestone\";\n\t\t\t\t\tdefault -> value;\n\t\t\t\t};\n\t\t\t}\n\t\t\tthis.value = ALIASES.getProperty(value, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn STRING_ITEM;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isNull() {\n\t\t\treturn (comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX) == 0);\n\t\t}\n\n\t\t/**\n\t\t * Returns a comparable value for a qualifier.\n\t\t *\n\t\t * This method takes into account the ordering of known qualifiers then unknown\n\t\t * qualifiers with lexical ordering.\n\t\t *\n\t\t * just returning an Integer with the index here is faster, but requires a lot of\n\t\t * if/then/else to check for -1 or QUALIFIERS.size and then resort to lexical\n\t\t * ordering. Most comparisons are decided by the first character, so this is still\n\t\t * fast. If more characters are needed then it requires a lexical sort anyway.\n\t\t * @param qualifier\n\t\t * @return an equivalent value that can be used with lexical comparison\n\t\t */\n\t\tpublic static String comparableQualifier(String qualifier) {\n\t\t\tint i = QUALIFIERS.indexOf(qualifier);\n\n\t\t\treturn i == -1 ? (QUALIFIERS.size() + \"-\" + qualifier) : String.valueOf(i);\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(@Nullable Item item) {\n\t\t\tif (item == null) {\n\t\t\t\t// 1-rc < 1, 1-ga > 1\n\t\t\t\treturn comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX);\n\t\t\t}\n\t\t\treturn switch (item.getType()) {\n\t\t\t\tcase INT_ITEM, LONG_ITEM, BIGINTEGER_ITEM -> -1; // 1.any < 1.1 ?\n\t\t\t\tcase STRING_ITEM ->\n\t\t\t\t\tcomparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value));\n\t\t\t\tcase LIST_ITEM -> -1; // 1.any < 1-1\n\t\t\t\tdefault -> throw new IllegalStateException(\"invalid item: \" + item.getClass());\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tStringItem that = (StringItem) o;\n\n\t\t\treturn value.equals(that.value);\n\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn value.hashCode();\n\t\t}\n\n\t\tpublic String toString() {\n\t\t\treturn value;\n\t\t}\n\n\t}\n\n\t/**\n\t * Represents a version list item. This class is used both for the global item list\n\t * and for sub-lists (which start with '-(number)' in the version specification).\n\t */\n\t@SuppressWarnings(\"serial\")\n\tprivate static class ListItem extends ArrayList<Item> implements Item {\n\n\t\t@Override\n\t\tpublic int getType() {\n\t\t\treturn LIST_ITEM;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isNull() {\n\t\t\treturn isEmpty();\n\t\t}\n\n\t\tvoid normalize() {\n\t\t\tfor (int i = size() - 1; i >= 0; i--) {\n\t\t\t\tItem lastItem = get(i);\n\n\t\t\t\tif (lastItem.isNull()) {\n\t\t\t\t\t// remove null trailing items: 0, \"\", empty list\n\t\t\t\t\tremove(i);\n\t\t\t\t}\n\t\t\t\telse if (!(lastItem instanceof ListItem)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(@Nullable Item item) {\n\t\t\tif (item == null) {\n\t\t\t\tif (isEmpty()) {\n\t\t\t\t\treturn 0; // 1-0 = 1- (normalize) = 1\n\t\t\t\t}\n\t\t\t\tItem first = get(0);\n\t\t\t\treturn first.compareTo(null);\n\t\t\t}\n\t\t\treturn switch (item.getType()) {\n\t\t\t\tcase INT_ITEM, LONG_ITEM, BIGINTEGER_ITEM -> -1; // 1-1 < 1.0.x\n\t\t\t\tcase STRING_ITEM -> 1; // 1-1 > 1-sp\n\t\t\t\tcase LIST_ITEM -> {\n\t\t\t\t\tIterator<Item> left = iterator();\n\t\t\t\t\tIterator<Item> right = ((ListItem) item).iterator();\n\t\t\t\t\twhile (left.hasNext() || right.hasNext()) {\n\t\t\t\t\t\tItem l = left.hasNext() ? left.next() : null;\n\t\t\t\t\t\tItem r = right.hasNext() ? right.next() : null;\n\n\t\t\t\t\t\t// if this is shorter, then invert the compare and mul with -1\n\t\t\t\t\t\tint result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r);\n\n\t\t\t\t\t\tif (result != 0) {\n\t\t\t\t\t\t\tyield result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tyield 0;\n\t\t\t\t}\n\t\t\t\tdefault -> throw new IllegalStateException(\"invalid item: \" + item.getClass());\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tStringBuilder buffer = new StringBuilder();\n\t\t\tfor (Item item : this) {\n\t\t\t\tif (buffer.length() > 0) {\n\t\t\t\t\tbuffer.append((item instanceof ListItem) ? '-' : '.');\n\t\t\t\t}\n\t\t\t\tbuffer.append(item);\n\t\t\t}\n\t\t\treturn buffer.toString();\n\t\t}\n\n\t}\n\n\tpublic ComparableVersion(String version) {\n\t\tparseVersion(version);\n\t}\n\n\t@SuppressWarnings(\"checkstyle:innerassignment\")\n\tpublic final void parseVersion(String version) {\n\t\tthis.value = version;\n\n\t\titems = new ListItem();\n\n\t\tversion = version.toLowerCase(Locale.ENGLISH);\n\n\t\tListItem list = items;\n\n\t\tDeque<Item> stack = new ArrayDeque<>();\n\t\tstack.push(list);\n\n\t\tboolean isDigit = false;\n\n\t\tint startIndex = 0;\n\n\t\tfor (int i = 0; i < version.length(); i++) {\n\t\t\tchar c = version.charAt(i);\n\n\t\t\tif (c == '.') {\n\t\t\t\tif (i == startIndex) {\n\t\t\t\t\tlist.add(IntItem.ZERO);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist.add(parseItem(isDigit, version.substring(startIndex, i)));\n\t\t\t\t}\n\t\t\t\tstartIndex = i + 1;\n\t\t\t}\n\t\t\telse if (c == '-') {\n\t\t\t\tif (i == startIndex) {\n\t\t\t\t\tlist.add(IntItem.ZERO);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlist.add(parseItem(isDigit, version.substring(startIndex, i)));\n\t\t\t\t}\n\t\t\t\tstartIndex = i + 1;\n\n\t\t\t\tlist.add(list = new ListItem());\n\t\t\t\tstack.push(list);\n\t\t\t}\n\t\t\telse if (Character.isDigit(c)) {\n\t\t\t\tif (!isDigit && i > startIndex) {\n\t\t\t\t\tlist.add(new StringItem(version.substring(startIndex, i), true));\n\t\t\t\t\tstartIndex = i;\n\n\t\t\t\t\tlist.add(list = new ListItem());\n\t\t\t\t\tstack.push(list);\n\t\t\t\t}\n\n\t\t\t\tisDigit = true;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (isDigit && i > startIndex) {\n\t\t\t\t\tlist.add(parseItem(true, version.substring(startIndex, i)));\n\t\t\t\t\tstartIndex = i;\n\n\t\t\t\t\tlist.add(list = new ListItem());\n\t\t\t\t\tstack.push(list);\n\t\t\t\t}\n\n\t\t\t\tisDigit = false;\n\t\t\t}\n\t\t}\n\n\t\tif (version.length() > startIndex) {\n\t\t\tlist.add(parseItem(isDigit, version.substring(startIndex)));\n\t\t}\n\n\t\twhile (!stack.isEmpty()) {\n\t\t\tlist = (ListItem) stack.pop();\n\t\t\tlist.normalize();\n\t\t}\n\t}\n\n\tprivate static Item parseItem(boolean isDigit, String buf) {\n\t\tif (isDigit) {\n\t\t\tbuf = stripLeadingZeroes(buf);\n\t\t\tif (buf.length() <= MAX_INT_ITEM_LENGTH) {\n\t\t\t\t// lower than 2^31\n\t\t\t\treturn new IntItem(buf);\n\t\t\t}\n\t\t\telse if (buf.length() <= MAX_LONG_ITEM_LENGTH) {\n\t\t\t\t// lower than 2^63\n\t\t\t\treturn new LongItem(buf);\n\t\t\t}\n\t\t\treturn new BigIntegerItem(buf);\n\t\t}\n\t\treturn new StringItem(buf, false);\n\t}\n\n\tprivate static String stripLeadingZeroes(String buf) {\n\t\tif (buf == null || buf.isEmpty()) {\n\t\t\treturn \"0\";\n\t\t}\n\t\tfor (int i = 0; i < buf.length(); ++i) {\n\t\t\tchar c = buf.charAt(i);\n\t\t\tif (c != '0') {\n\t\t\t\treturn buf.substring(i);\n\t\t\t}\n\t\t}\n\t\treturn buf;\n\t}\n\n\t@Override\n\tpublic int compareTo(ComparableVersion o) {\n\t\treturn items.compareTo(o.items);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn value;\n\t}\n\n\tpublic String getCanonical() {\n\t\tif (canonical == null) {\n\t\t\tcanonical = items.toString();\n\t\t}\n\t\treturn canonical;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\treturn (o instanceof ComparableVersion) && items.equals(((ComparableVersion) o).items);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn items.hashCode();\n\t}\n\n\t// CHECKSTYLE_OFF: LineLength\n\t/**\n\t * Main to test version parsing and comparison.\n\t * <p>\n\t * To check how \"1.2.7\" compares to \"1.2-SNAPSHOT\", for example, you can issue\n\t * <pre>java -jar ${maven.repo.local}/org/apache/maven/maven-artifact/${maven.version}/maven-artifact-${maven.version}.jar \"1.2.7\" \"1.2-SNAPSHOT\"</pre>\n\t * command to command line. Result of given command will be something like this: <pre>\n\t * Display parameters as parsed by Maven (in canonical form) and comparison result:\n\t * 1. 1.2.7 == 1.2.7\n\t *    1.2.7 &gt; 1.2-SNAPSHOT\n\t * 2. 1.2-SNAPSHOT == 1.2-snapshot\n\t * </pre>\n\t * @param args the version strings to parse and compare. You can pass arbitrary number\n\t * of version strings and always two adjacent will be compared\n\t */\n\t// CHECKSTYLE_ON: LineLength\n\tpublic static void main(String... args) {\n\t\tSystem.out.println(\"Display parameters as parsed by Maven (in canonical form) and comparison result:\");\n\t\tif (args.length == 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tComparableVersion prev = null;\n\t\tint i = 1;\n\t\tfor (String version : args) {\n\t\t\tComparableVersion c = new ComparableVersion(version);\n\n\t\t\tif (prev != null) {\n\t\t\t\tint compare = prev.compareTo(c);\n\t\t\t\tSystem.out.println(\"   \" + prev.toString() + ' ' + ((compare == 0) ? \"==\" : ((compare < 0) ? \"<\" : \">\"))\n\t\t\t\t\t\t+ ' ' + version);\n\t\t\t}\n\n\t\t\tSystem.out.println(String.valueOf(i++) + \". \" + version + \" == \" + c.getCanonical());\n\n\t\t\tprev = c;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/CredentialsContainer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\n/**\n * Indicates that the implementing object contains sensitive data, which can be erased\n * using the {@code eraseCredentials} method. Implementations are expected to invoke the\n * method on any internal objects which may also implement this interface.\n * <p>\n * For internal framework use only. Users who are writing their own\n * {@code AuthenticationProvider} implementations should create and return an appropriate\n * {@code Authentication} object there, minus any sensitive data, rather than using this\n * interface.\n *\n * @author Luke Taylor\n * @since 3.0.3\n */\npublic interface CredentialsContainer {\n\n\tvoid eraseCredentials();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/GrantedAuthority.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthorizationManager;\n\n/**\n * Represents an authority granted to an {@link Authentication} object.\n *\n * <p>\n * A <code>GrantedAuthority</code> must either represent itself as a <code>String</code>\n * or be specifically supported by an {@link AuthorizationManager}.\n *\n * @author Ben Alex\n */\npublic interface GrantedAuthority extends Serializable {\n\n\t/**\n\t * If the <code>GrantedAuthority</code> can be represented as a <code>String</code>\n\t * and that <code>String</code> is sufficient in precision to be relied upon for an\n\t * access control decision by an {@link AuthorizationManager} (or delegate), this\n\t * method should return such a <code>String</code>.\n\t * <p>\n\t * If the <code>GrantedAuthority</code> cannot be expressed with sufficient precision\n\t * as a <code>String</code>, <code>null</code> should be returned. Returning\n\t * <code>null</code> will require an <code>AccessDecisionManager</code> (or delegate)\n\t * to specifically support the <code>GrantedAuthority</code> implementation, so\n\t * returning <code>null</code> should be avoided unless actually required.\n\t * @return a representation of the granted authority (or <code>null</code> if the\n\t * granted authority cannot be expressed as a <code>String</code> with sufficient\n\t * precision).\n\t */\n\t@Nullable String getAuthority();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/SimpleAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.io.Serial;\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\n@Transient\nfinal class SimpleAuthentication implements Authentication {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 3194696462184782814L;\n\n\tprivate final @Nullable Object principal;\n\n\tprivate final @Nullable Object credentials;\n\n\tprivate final Collection<GrantedAuthority> authorities;\n\n\tprivate final @Nullable Object details;\n\n\tprivate final boolean authenticated;\n\n\tprivate SimpleAuthentication(Builder builder) {\n\t\tthis.principal = builder.principal;\n\t\tthis.credentials = builder.credentials;\n\t\tthis.authorities = builder.authorities;\n\t\tthis.details = builder.details;\n\t\tthis.authenticated = builder.authenticated;\n\t}\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getDetails() {\n\t\treturn this.details;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic boolean isAuthenticated() {\n\t\treturn this.authenticated;\n\t}\n\n\t@Override\n\tpublic void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Instead of calling this setter, please call toBuilder to create a new instance\");\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn (this.principal == null) ? \"\" : this.principal.toString();\n\t}\n\n\tstatic final class Builder implements Authentication.Builder<Builder> {\n\n\t\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t\tprivate final Collection<GrantedAuthority> authorities = new LinkedHashSet<>();\n\n\t\tprivate @Nullable Object principal;\n\n\t\tprivate @Nullable Object credentials;\n\n\t\tprivate @Nullable Object details;\n\n\t\tprivate boolean authenticated;\n\n\t\tBuilder(Authentication authentication) {\n\t\t\tthis.logger.debug(\"Creating a builder which will result in exchanging an authentication of type \"\n\t\t\t\t\t+ authentication.getClass() + \" for \" + SimpleAuthentication.class.getSimpleName() + \";\"\n\t\t\t\t\t+ \" consider implementing \" + authentication.getClass().getSimpleName() + \"#toBuilder\");\n\t\t\tthis.authorities.addAll(authentication.getAuthorities());\n\t\t\tthis.principal = authentication.getPrincipal();\n\t\t\tthis.credentials = authentication.getCredentials();\n\t\t\tthis.details = authentication.getDetails();\n\t\t\tthis.authenticated = authentication.isAuthenticated();\n\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder authorities(Consumer<Collection<GrantedAuthority>> authorities) {\n\t\t\tauthorities.accept(this.authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder details(@Nullable Object details) {\n\t\t\tthis.details = details;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder principal(@Nullable Object principal) {\n\t\t\tthis.principal = principal;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder credentials(@Nullable Object credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder authenticated(boolean authenticated) {\n\t\t\tthis.authenticated = authenticated;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication build() {\n\t\t\treturn new SimpleAuthentication(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/SpringSecurityCoreVersion.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Properties;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.SpringVersion;\nimport org.springframework.util.Assert;\n\n/**\n * Internal class used for checking version compatibility in a deployed application.\n *\n * @author Luke Taylor\n * @author Rob Winch\n */\npublic final class SpringSecurityCoreVersion {\n\n\tprivate static final String DISABLE_CHECKS = SpringSecurityCoreVersion.class.getName().concat(\".DISABLE_CHECKS\");\n\n\tprivate static final Log logger = LogFactory.getLog(SpringSecurityCoreVersion.class);\n\n\t/**\n\t * Global Serialization value for Spring Security classes.\n\t * @deprecated Please have each class use its own serialization version For more\n\t * details, refer to the {@code SpringSecurityCoreVersionSerializableTests} class.\n\t */\n\t@Deprecated(forRemoval = true)\n\tpublic static final long SERIAL_VERSION_UID = 620L;\n\n\tstatic final @Nullable String MIN_SPRING_VERSION = getSpringVersion();\n\n\tstatic {\n\t\tperformVersionChecks();\n\t}\n\n\tprivate SpringSecurityCoreVersion() {\n\t}\n\n\tprivate static void performVersionChecks() {\n\t\tperformVersionChecks(MIN_SPRING_VERSION);\n\t}\n\n\t/**\n\t * Perform version checks with specific min Spring Version\n\t * @param minSpringVersion\n\t */\n\tprivate static void performVersionChecks(@Nullable String minSpringVersion) {\n\t\tif (minSpringVersion == null) {\n\t\t\treturn;\n\t\t}\n\t\t// Check Spring Compatibility\n\t\tString springVersion = SpringVersion.getVersion();\n\t\tString version = getVersion();\n\t\tif (disableChecks(springVersion, version)) {\n\t\t\treturn;\n\t\t}\n\t\t// should be disabled if springVersion is null\n\t\tAssert.notNull(springVersion, \"springVersion cannot be null\");\n\t\tlogger.info(\"You are running with Spring Security Core \" + version);\n\t\tif (new ComparableVersion(springVersion).compareTo(new ComparableVersion(minSpringVersion)) < 0) {\n\t\t\tlogger.warn(\"**** You are advised to use Spring \" + minSpringVersion\n\t\t\t\t\t+ \" or later with this version. You are running: \" + springVersion);\n\t\t}\n\t}\n\n\tpublic static @Nullable String getVersion() {\n\t\tPackage pkg = SpringSecurityCoreVersion.class.getPackage();\n\t\treturn (pkg != null) ? pkg.getImplementationVersion() : null;\n\t}\n\n\t/**\n\t * Disable if springVersion and springSecurityVersion are the same to allow working\n\t * with Uber Jars.\n\t * @param springVersion\n\t * @param springSecurityVersion\n\t * @return\n\t */\n\tprivate static boolean disableChecks(@Nullable String springVersion, @Nullable String springSecurityVersion) {\n\t\tif (springVersion == null || springVersion.equals(springSecurityVersion)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn Boolean.getBoolean(DISABLE_CHECKS);\n\t}\n\n\t/**\n\t * Loads the spring version or null if it cannot be found.\n\t * @return\n\t */\n\tprivate static @Nullable String getSpringVersion() {\n\t\tProperties properties = new Properties();\n\t\ttry (InputStream is = SpringSecurityCoreVersion.class.getClassLoader()\n\t\t\t.getResourceAsStream(\"META-INF/spring-security.versions\")) {\n\t\t\tproperties.load(is);\n\t\t}\n\t\tcatch (IOException | NullPointerException ex) {\n\t\t\treturn null;\n\t\t}\n\t\treturn properties.getProperty(\"org.springframework:spring-core\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/SpringSecurityMessageSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.context.support.ResourceBundleMessageSource;\n\n/**\n * The default <code>MessageSource</code> used by Spring Security.\n * <p>\n * All Spring Security classes requiring message localization will by default use this\n * class. However, all such classes will also implement <code>MessageSourceAware</code> so\n * that the application context can inject an alternative message source. Therefore this\n * class is only used when the deployment environment has not specified an alternative\n * message source.\n * </p>\n *\n * @author Ben Alex\n */\npublic class SpringSecurityMessageSource extends ResourceBundleMessageSource {\n\n\tpublic SpringSecurityMessageSource() {\n\t\tsetBasename(\"org.springframework.security.messages\");\n\t}\n\n\tpublic static MessageSourceAccessor getAccessor() {\n\t\treturn new MessageSourceAccessor(new SpringSecurityMessageSource());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/Transient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * A marker for {@link Authentication}s that should never be stored across requests, for\n * example a bearer token authentication\n *\n * @author Josh Cummings\n * @since 5.1\n */\n@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface Transient {\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/AbstractSecurityAnnotationScanner.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.annotation.MergedAnnotation;\nimport org.springframework.util.Assert;\n\n/**\n * An abstract class to hide the {@link MergedAnnotation} implementation details.\n *\n * <p>\n * Also handy for allowing each scanner to delegate to another without needing to\n * synthesize twice.\n */\nabstract class AbstractSecurityAnnotationScanner<A extends Annotation> implements SecurityAnnotationScanner<A> {\n\n\t/**\n\t * {@inheritDoc}\n\t **/\n\t@Override\n\tpublic @Nullable A scan(Method method, Class<?> targetClass) {\n\t\tAssert.notNull(targetClass, \"targetClass cannot be null\");\n\t\tMergedAnnotation<A> annotation = merge(method, targetClass);\n\t\tif (annotation == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn annotation.synthesize();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t **/\n\t@Override\n\tpublic @Nullable A scan(Parameter parameter) {\n\t\tMergedAnnotation<A> annotation = merge(parameter, null);\n\t\tif (annotation == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn annotation.synthesize();\n\t}\n\n\tabstract @Nullable MergedAnnotation<A> merge(AnnotatedElement element, @Nullable Class<?> targetClass);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/AnnotationTemplateExpressionDefaults.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\n/**\n * A component for configuring the expression attribute template of the parsed Spring\n * Security annotation\n *\n * @author DingHao\n * @since 6.4\n * @see AuthenticationPrincipal\n * @see CurrentSecurityContext\n */\npublic class AnnotationTemplateExpressionDefaults {\n\n\tprivate boolean ignoreUnknown = true;\n\n\t/**\n\t * Whether template resolution should ignore placeholders it doesn't recognize.\n\t * <p>\n\t * By default, this value is <code>true</code>.\n\t */\n\tpublic boolean isIgnoreUnknown() {\n\t\treturn this.ignoreUnknown;\n\t}\n\n\t/**\n\t * Configure template resolution to ignore unknown placeholders. When set to\n\t * <code>false</code>, template resolution will throw an exception for unknown\n\t * placeholders.\n\t * <p>\n\t * By default, this value is <code>true</code>.\n\t * @param ignoreUnknown - whether to ignore unknown placeholders parameters\n\t */\n\tpublic void setIgnoreUnknown(boolean ignoreUnknown) {\n\t\tthis.ignoreUnknown = ignoreUnknown;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/AuthenticationPrincipal.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Annotation that is used to resolve {@link Authentication#getPrincipal()} to a method\n * argument.\n *\n * @author Rob Winch\n * @since 4.0\n *\n * See: <a href=\n * \"{@docRoot}/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.html\" >\n * AuthenticationPrincipalArgumentResolver </a>\n */\n@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface AuthenticationPrincipal {\n\n\t/**\n\t * True if a {@link ClassCastException} should be thrown when the current\n\t * {@link Authentication#getPrincipal()} is the incorrect type. Default is false.\n\t * @return\n\t */\n\tboolean errorOnInvalidType() default false;\n\n\t/**\n\t * If specified will use the provided SpEL expression to resolve the principal. This\n\t * is convenient if users need to transform the result.\n\t *\n\t * <p>\n\t * For example, perhaps the user wants to resolve a CustomUser object that is final\n\t * and is leveraging a UserDetailsService. This can be handled by returning an object\n\t * that looks like:\n\t * </p>\n\t *\n\t * <pre>\n\t * public class CustomUserUserDetails extends User {\n\t *     // ...\n\t *     public CustomUser getCustomUser() {\n\t *         return customUser;\n\t *     }\n\t * }\n\t * </pre>\n\t *\n\t * Then the user can specify an annotation that looks like:\n\t *\n\t * <pre>\n\t * &#64;AuthenticationPrincipal(expression = \"customUser\")\n\t * </pre>\n\t * @return the expression to use.\n\t */\n\tString expression() default \"\";\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/CurrentSecurityContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Annotation that is used to resolve the\n * {@link org.springframework.security.core.context.SecurityContext} as a method argument.\n *\n * @author Dan Zheng\n * @since 5.2\n *\n * <p>\n * See: <a href=\n * \"{@docRoot}/org/springframework/security/web/bind/support/CurrentSecurityContextArgumentResolver.html\" >\n * CurrentSecurityContextArgumentResolver</a> For Servlet\n * </p>\n *\n * <p>\n * See: <a href=\n * \"{@docRoot}/org/springframework/security/web/reactive/result/method/annotation/CurrentSecurityContextArgumentResolver.html\" >\n * CurrentSecurityContextArgumentResolver</a> For WebFlux\n * </p>\n */\n@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface CurrentSecurityContext {\n\n\t/**\n\t * True if a {@link ClassCastException} should be thrown when the current\n\t * {@link org.springframework.security.core.context.SecurityContext} is the incorrect\n\t * type. Default is false.\n\t * @return whether or not to error on an invalid type\n\t */\n\tboolean errorOnInvalidType() default false;\n\n\t/**\n\t * If specified, will use the provided SpEL expression to resolve the security\n\t * context. This is convenient if applications need to transform the result.\n\t *\n\t * For example, if an application needs to extract its custom {@code Authentication}\n\t * implementation, then it could specify the appropriate SpEL like so:\n\t *\n\t * <pre>\n\t * &#64;CurrentSecurityContext(expression = \"authentication\") CustomAuthentication authentication\n\t * </pre>\n\t * @return the expression to use\n\t */\n\tString expression() default \"\";\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScanner.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.annotation.MergedAnnotation;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.GenericConverter;\nimport org.springframework.core.convert.support.DefaultConversionService;\nimport org.springframework.util.Assert;\nimport org.springframework.util.PropertyPlaceholderHelper;\n\n/**\n * Searches for and synthesizes an annotation on a type, method, or method parameter into\n * an annotation of type {@code <A>}, resolving any placeholders in the annotation value.\n *\n * <p>\n * Note that in all cases, Spring Security does not allow for repeatable annotations. So\n * this class delegates to {@link UniqueSecurityAnnotationScanner} in order to error if a\n * repeat is discovered.\n *\n * <p>\n * It supports meta-annotations with placeholders, like the following:\n *\n * <pre>\n *\t&#64;PreAuthorize(\"hasRole({role})\")\n *\tpublic @interface HasRole {\n *\t\tString role();\n *\t}\n * </pre>\n *\n * <p>\n * In that case, you could use an {@link ExpressionTemplateSecurityAnnotationScanner} of\n * type {@link org.springframework.security.access.prepost.PreAuthorize} to synthesize any\n * {@code @HasRole} annotation found on a given {@link AnnotatedElement}.\n *\n * <p>\n * Meta-annotations that use enum values can use {@link ExpressionTemplateValueProvider}\n * to provide custom placeholder values.\n *\n * <p>\n * Since the process of synthesis is expensive, it is recommended to cache the synthesized\n * result to prevent multiple computations.\n *\n * @param <A> the annotation to search for and synthesize\n * @author Josh Cummings\n * @author DingHao\n * @author Mike Heath\n * @since 7.0\n */\nfinal class ExpressionTemplateSecurityAnnotationScanner<A extends Annotation>\n\t\textends AbstractSecurityAnnotationScanner<A> {\n\n\tprivate static final DefaultConversionService conversionService = new DefaultConversionService();\n\n\tstatic {\n\t\tconversionService.addConverter(new ClassToStringConverter());\n\t\tconversionService.addConverter(new ExpressionTemplateValueProviderConverter());\n\t}\n\n\tprivate final Class<A> type;\n\n\tprivate final UniqueSecurityAnnotationScanner<A> unique;\n\n\tprivate final AnnotationTemplateExpressionDefaults templateDefaults;\n\n\tExpressionTemplateSecurityAnnotationScanner(Class<A> type, AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tAssert.notNull(type, \"type cannot be null\");\n\t\tAssert.notNull(templateDefaults, \"templateDefaults cannot be null\");\n\t\tthis.type = type;\n\t\tthis.unique = new UniqueSecurityAnnotationScanner<>(type);\n\t\tthis.templateDefaults = templateDefaults;\n\t}\n\n\t@Override\n\t@Nullable MergedAnnotation<A> merge(AnnotatedElement element, @Nullable Class<?> targetClass) {\n\t\tif (element instanceof Parameter parameter) {\n\t\t\tMergedAnnotation<A> annotation = this.unique.merge(parameter, targetClass);\n\t\t\tif (annotation == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn resolvePlaceholders(annotation);\n\t\t}\n\t\tif (element instanceof Method method) {\n\t\t\tMergedAnnotation<A> annotation = this.unique.merge(method, targetClass);\n\t\t\tif (annotation == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn resolvePlaceholders(annotation);\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Unsupported element of type \" + element.getClass());\n\t}\n\n\tprivate MergedAnnotation<A> resolvePlaceholders(MergedAnnotation<A> mergedAnnotation) {\n\t\tif (this.templateDefaults == null) {\n\t\t\treturn mergedAnnotation;\n\t\t}\n\t\tif (mergedAnnotation.getMetaSource() == null) {\n\t\t\treturn mergedAnnotation;\n\t\t}\n\t\tPropertyPlaceholderHelper helper = new PropertyPlaceholderHelper(\"{\", \"}\", null, null,\n\t\t\t\tthis.templateDefaults.isIgnoreUnknown());\n\t\tMap<String, Object> properties = new HashMap<>(mergedAnnotation.asMap());\n\t\tMap<String, String> metaAnnotationProperties = extractMetaAnnotationProperties(mergedAnnotation);\n\t\tfor (Map.Entry<String, Object> annotationProperty : mergedAnnotation.asMap().entrySet()) {\n\t\t\tif (!(annotationProperty.getValue() instanceof String expression)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString value = helper.replacePlaceholders(expression, metaAnnotationProperties::get);\n\t\t\tproperties.put(annotationProperty.getKey(), value);\n\t\t}\n\t\tAnnotatedElement annotatedElement = (AnnotatedElement) mergedAnnotation.getSource();\n\t\treturn MergedAnnotation.of(annotatedElement, this.type, properties);\n\t}\n\n\tprivate Map<String, String> extractMetaAnnotationProperties(MergedAnnotation<A> mergedAnnotation) {\n\t\tMap<String, String> stringProperties = new HashMap<>();\n\t\tMap<String, Object> metaAnnotationProperties = new HashMap<>();\n\t\tMergedAnnotation<?> metaSource = mergedAnnotation.getMetaSource();\n\t\twhile (metaSource != null) {\n\t\t\tmetaAnnotationProperties.putAll(metaSource.asMap());\n\t\t\tmetaSource = metaSource.getMetaSource();\n\t\t}\n\t\tfor (Map.Entry<String, Object> property : metaAnnotationProperties.entrySet()) {\n\t\t\tObject value = property.getValue();\n\t\t\tString valueString = (value instanceof String) ? (String) value\n\t\t\t\t\t: conversionService.convert(value, String.class);\n\t\t\tstringProperties.put(property.getKey(), valueString);\n\t\t}\n\t\treturn stringProperties;\n\t}\n\n\tstatic class ClassToStringConverter implements GenericConverter {\n\n\t\t@Override\n\t\tpublic Set<ConvertiblePair> getConvertibleTypes() {\n\t\t\treturn Collections.singleton(new ConvertiblePair(Class.class, String.class));\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\t\treturn (source != null) ? source.toString() : null;\n\t\t}\n\n\t}\n\n\tstatic class ExpressionTemplateValueProviderConverter implements GenericConverter {\n\n\t\t@Override\n\t\tpublic Set<ConvertiblePair> getConvertibleTypes() {\n\t\t\treturn Collections.singleton(new ConvertiblePair(ExpressionTemplateValueProvider.class, String.class));\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\t\treturn (source != null) ? ((ExpressionTemplateValueProvider) source).getExpressionTemplateValue() : null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/ExpressionTemplateValueProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\n/**\n * Provides a mechanism for providing custom values from enum types used in security\n * meta-annotation expressions. For example:\n *\n * <pre>\n * enum Permission implements ExpressionTemplateValueProvider {\n *   READ,\n *   WRITE;\n *\n *   &#64;Override\n *   public String getExpressionTemplateValue() {\n *     return switch (this) {\n *       case READ -> \"user.permission-read\";\n *       case WRITE -> \"user.permission-write\";\n *     };\n *   }\n *\n * }\n * </pre>\n *\n * @author Mike Heath\n * @since 7.0\n */\npublic interface ExpressionTemplateValueProvider {\n\n\t/**\n\t * Returns the value to be used in an expression template.\n\t * @return the value to be used in an expression template\n\t */\n\tString getExpressionTemplateValue();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/SecurityAnnotationScanner.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An interface to scan for and synthesize an annotation on a type, method, or method\n * parameter into an annotation of type {@code <A>}.\n *\n * <p>\n * Implementations should support meta-annotations. This is usually by way of the\n * {@link org.springframework.core.annotation.MergedAnnotations} API.\n *\n * <p>\n * Synthesis generally refers to the process of taking an annotation's meta-annotations\n * and placeholders, resolving them, and then combining these elements into a facade of\n * the raw annotation instance.\n *\n * <p>\n * Since the process of synthesizing an annotation can be expensive, it's recommended to\n * cache the synthesized annotation to prevent multiple computations.\n * </p>\n *\n * @param <A> the annotation to search for and synthesize\n * @author Josh Cummings\n * @since 6.4\n * @see UniqueSecurityAnnotationScanner\n * @see ExpressionTemplateSecurityAnnotationScanner\n */\npublic interface SecurityAnnotationScanner<A extends Annotation> {\n\n\t/**\n\t * Scan for an annotation of type {@code A}, starting from the given method.\n\t *\n\t * <p>\n\t * Implementations should fail if they encounter more than one annotation of that type\n\t * attributable to the method.\n\t *\n\t * <p>\n\t * Implementations should describe their strategy for searching the element and any\n\t * surrounding class, interfaces, or super-class.\n\t * @param method the method to search from\n\t * @param targetClass the target class for the method\n\t * @return the synthesized annotation or {@code null} if not found\n\t */\n\t@Nullable A scan(Method method, Class<?> targetClass);\n\n\t/**\n\t * Scan for an annotation of type {@code A}, starting from the given method parameter.\n\t *\n\t * <p>\n\t * Implementations should fail if they encounter more than one annotation of that type\n\t * attributable to the parameter.\n\t *\n\t * <p>\n\t * Implementations should describe their strategy for searching the parameter and any\n\t * surrounding class, interfaces, or super-class.\n\t * @param parameter the parameter to search\n\t * @return the synthesized annotation or {@code null} if not found\n\t */\n\t@Nullable A scan(Parameter parameter);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/SecurityAnnotationScanners.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Factory for creating {@link SecurityAnnotationScanner} instances.\n *\n * @author Josh Cummings\n * @since 6.4\n */\npublic final class SecurityAnnotationScanners {\n\n\tprivate static final Map<Class<? extends Annotation>, SecurityAnnotationScanner<? extends Annotation>> uniqueTemplateScanners = new ConcurrentHashMap<>();\n\n\tprivate static final Map<List<Class<? extends Annotation>>, SecurityAnnotationScanner<? extends Annotation>> uniqueTypesScanners = new ConcurrentHashMap<>();\n\n\tprivate SecurityAnnotationScanners() {\n\t}\n\n\t/**\n\t * Create a {@link SecurityAnnotationScanner} that requires synthesized annotations to\n\t * be unique on the given {@link AnnotatedElement}.\n\t * @param type the annotation type\n\t * @param <A> the annotation type\n\t * @return the default {@link SecurityAnnotationScanner}\n\t */\n\tpublic static <A extends Annotation> SecurityAnnotationScanner<A> requireUnique(Class<A> type) {\n\t\treturn requireUnique(type, new AnnotationTemplateExpressionDefaults());\n\t}\n\n\t/**\n\t * Create a {@link SecurityAnnotationScanner} that requires synthesized annotations to\n\t * be unique on the given {@link AnnotatedElement}.\n\t *\n\t * <p>\n\t * When a {@link AnnotationTemplateExpressionDefaults} is provided, it will return a\n\t * scanner that supports placeholders in the annotation's attributes in addition to\n\t * the meta-annotation synthesizing provided by {@link #requireUnique(Class)}.\n\t * @param type the annotation type\n\t * @param templateDefaults the defaults for resolving placeholders in the annotation's\n\t * attributes\n\t * @param <A> the annotation type\n\t * @return the default {@link SecurityAnnotationScanner}\n\t */\n\tpublic static <A extends Annotation> SecurityAnnotationScanner<A> requireUnique(Class<A> type,\n\t\t\tAnnotationTemplateExpressionDefaults templateDefaults) {\n\t\treturn (SecurityAnnotationScanner<A>) uniqueTemplateScanners.computeIfAbsent(type,\n\t\t\t\t(t) -> new ExpressionTemplateSecurityAnnotationScanner<>(t, templateDefaults));\n\t}\n\n\t/**\n\t * Create a {@link SecurityAnnotationScanner} that requires synthesized annotations to\n\t * be unique on the given {@link AnnotatedElement}. Supplying multiple types implies\n\t * that the synthesized annotation must be unique across all specified types.\n\t * @param types the annotation types\n\t * @return the default {@link SecurityAnnotationScanner}\n\t */\n\tpublic static SecurityAnnotationScanner<Annotation> requireUnique(List<Class<? extends Annotation>> types) {\n\t\tList<Class<Annotation>> casted = new ArrayList<>();\n\t\ttypes.forEach((type) -> casted.add((Class<Annotation>) type));\n\t\treturn (SecurityAnnotationScanner<Annotation>) uniqueTypesScanners.computeIfAbsent(types,\n\t\t\t\t(t) -> new UniqueSecurityAnnotationScanner<>(casted));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/UniqueSecurityAnnotationScanner.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Parameter;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodClassKey;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.core.annotation.MergedAnnotation;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.core.annotation.RepeatableContainers;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Searches for and synthesizes annotations found on types, methods, or method parameters\n * into an annotation of type {@code <A>}, ensuring that there is a unique match.\n *\n * <p>\n * Note that in all cases, Spring Security does not allow for repeatable annotations. As\n * such, this class errors if a repeat is discovered.\n *\n * <p>\n * For example, if a class extends two interfaces, and each interface is annotated with\n * `@PreAuthorize(\"hasRole('ADMIN')\")` and `@PreAuthorize(\"hasRole('USER')\")`\n * respectively, it's not clear which of these should apply, and so this class will throw\n * an exception.\n *\n * <p>\n * If the given annotation can be applied to types or methods, this class will traverse\n * the type hierarchy, starting from the target class and method; in case of a method\n * parameter, it will only consider annotations on the parameter. In all cases, it will\n * consider meta-annotations in its traversal.\n *\n * <p>\n * When traversing the type hierarchy, this class will first look for annotations on the\n * given method, then on any methods that method overrides. If no annotations are found,\n * it will then search for annotations on the given class, then on any classes that class\n * extends and on any interfaces that class implements.\n *\n * <p>\n * It supports meta-annotations, like the following:\n *\n * <pre>\n *\t&#64;PreAuthorize(\"hasRole('ROLE_ADMIN')\")\n *\tpublic @annotation HasRole {\n *\t}\n * </pre>\n *\n * <p>\n * In that case, you can use an {@link UniqueSecurityAnnotationScanner} of type\n * {@link org.springframework.security.access.prepost.PreAuthorize} to synthesize any\n * {@code @HasRole} annotation found on a given method or class into its\n * {@link org.springframework.security.access.prepost.PreAuthorize} meta-annotation.\n *\n * <p>\n * Since the process of synthesis is expensive, it's recommended to cache the synthesized\n * result to prevent multiple computations.\n *\n * @param <A> the annotation to search for and synthesize\n * @author Josh Cummings\n * @author DingHao\n * @since 6.4\n */\nfinal class UniqueSecurityAnnotationScanner<A extends Annotation> extends AbstractSecurityAnnotationScanner<A> {\n\n\tprivate final List<Class<A>> types;\n\n\tprivate final Map<Parameter, MergedAnnotation<A>> uniqueParameterAnnotationCache = new ConcurrentHashMap<>();\n\n\tprivate final Map<MethodClassKey, MergedAnnotation<A>> uniqueMethodAnnotationCache = new ConcurrentHashMap<>();\n\n\tUniqueSecurityAnnotationScanner(Class<A> type) {\n\t\tAssert.notNull(type, \"type cannot be null\");\n\t\tthis.types = List.of(type);\n\t}\n\n\tUniqueSecurityAnnotationScanner(List<Class<A>> types) {\n\t\tAssert.notNull(types, \"types cannot be null\");\n\t\tthis.types = types;\n\t}\n\n\t@Override\n\tMergedAnnotation<A> merge(AnnotatedElement element, @Nullable Class<?> targetClass) {\n\t\tif (element instanceof Parameter parameter) {\n\t\t\treturn this.uniqueParameterAnnotationCache.computeIfAbsent(parameter, (p) -> {\n\t\t\t\tList<MergedAnnotation<A>> annotations = findParameterAnnotations(p);\n\t\t\t\treturn requireUnique(p, annotations);\n\t\t\t});\n\t\t}\n\t\tif (element instanceof Method method) {\n\t\t\treturn this.uniqueMethodAnnotationCache.computeIfAbsent(new MethodClassKey(method, targetClass), (k) -> {\n\t\t\t\tList<MergedAnnotation<A>> annotations = findMethodAnnotations(method, targetClass);\n\t\t\t\treturn requireUnique(method, annotations);\n\t\t\t});\n\t\t}\n\t\tthrow new AnnotationConfigurationException(\"Unsupported element of type \" + element.getClass());\n\t}\n\n\tprivate @Nullable MergedAnnotation<A> requireUnique(AnnotatedElement element,\n\t\t\tList<MergedAnnotation<A>> annotations) {\n\t\treturn switch (annotations.size()) {\n\t\t\tcase 0 -> null;\n\t\t\tcase 1 -> annotations.get(0);\n\t\t\tdefault -> {\n\t\t\t\tList<Annotation> synthesized = new ArrayList<>();\n\t\t\t\tfor (MergedAnnotation<A> annotation : annotations) {\n\t\t\t\t\tsynthesized.add(annotation.synthesize());\n\t\t\t\t}\n\t\t\t\tthrow new AnnotationConfigurationException(\"\"\"\n\t\t\t\t\t\tPlease ensure there is one unique annotation of type %s attributed to %s. \\\n\t\t\t\t\t\tFound %d competing annotations: %s\"\"\".formatted(this.types, element, annotations.size(),\n\t\t\t\t\t\tsynthesized));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate List<MergedAnnotation<A>> findParameterAnnotations(Parameter current) {\n\t\tList<MergedAnnotation<A>> directAnnotations = findDirectAnnotations(current);\n\t\tif (!directAnnotations.isEmpty()) {\n\t\t\treturn directAnnotations;\n\t\t}\n\t\tExecutable executable = current.getDeclaringExecutable();\n\t\tif (executable instanceof Method method) {\n\t\t\tdirectAnnotations = findClosestParameterAnnotations(method, method.getDeclaringClass(), current,\n\t\t\t\t\tnew HashSet<>());\n\t\t\tif (!directAnnotations.isEmpty()) {\n\t\t\t\treturn directAnnotations;\n\t\t\t}\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tprivate List<MergedAnnotation<A>> findClosestParameterAnnotations(Method method, Class<?> clazz, Parameter current,\n\t\t\tSet<Class<?>> visited) {\n\t\tif (clazz == null || clazz == Object.class || !visited.add(clazz)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<MergedAnnotation<A>> directAnnotations = findDirectParameterAnnotations(method, clazz, current);\n\t\tif (!directAnnotations.isEmpty()) {\n\t\t\treturn directAnnotations;\n\t\t}\n\t\tList<MergedAnnotation<A>> annotations = new ArrayList<>(\n\t\t\t\tfindClosestParameterAnnotations(method, clazz.getSuperclass(), current, visited));\n\t\tfor (Class<?> ifc : clazz.getInterfaces()) {\n\t\t\tannotations.addAll(findClosestParameterAnnotations(method, ifc, current, visited));\n\t\t}\n\t\treturn annotations;\n\t}\n\n\tprivate List<MergedAnnotation<A>> findDirectParameterAnnotations(Method method, Class<?> clazz, Parameter current) {\n\t\ttry {\n\t\t\tMethod methodToUse = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());\n\t\t\tfor (Parameter parameter : methodToUse.getParameters()) {\n\t\t\t\tif (parameter.getName().equals(current.getName())) {\n\t\t\t\t\tList<MergedAnnotation<A>> directAnnotations = findDirectAnnotations(parameter);\n\t\t\t\t\tif (!directAnnotations.isEmpty()) {\n\t\t\t\t\t\treturn directAnnotations;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (NoSuchMethodException ex) {\n\t\t\t// move on\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tprivate List<MergedAnnotation<A>> findMethodAnnotations(Method method, @Nullable Class<?> targetClass) {\n\t\t// The method may be on an interface, but we need attributes from the target\n\t\t// class.\n\t\t// If the target class is null, the method will be unchanged.\n\t\tMethod specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);\n\t\tList<MergedAnnotation<A>> annotations = findClosestMethodAnnotations(specificMethod,\n\t\t\t\tspecificMethod.getDeclaringClass(), new HashSet<>());\n\t\tif (!annotations.isEmpty()) {\n\t\t\treturn annotations;\n\t\t}\n\t\t// Check the original (e.g. interface) method\n\t\tif (specificMethod != method) {\n\t\t\tannotations = findClosestMethodAnnotations(method, method.getDeclaringClass(), new HashSet<>());\n\t\t\tif (!annotations.isEmpty()) {\n\t\t\t\treturn annotations;\n\t\t\t}\n\t\t}\n\t\t// Check the class-level (note declaringClass, not targetClass, which may not\n\t\t// actually implement the method)\n\t\tannotations = findClosestClassAnnotations(specificMethod.getDeclaringClass(), new HashSet<>());\n\t\tif (!annotations.isEmpty()) {\n\t\t\treturn annotations;\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\tprivate List<MergedAnnotation<A>> findClosestMethodAnnotations(Method method, Class<?> targetClass,\n\t\t\tSet<Class<?>> classesToSkip) {\n\t\tif (targetClass == null || classesToSkip.contains(targetClass) || targetClass == Object.class) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tclassesToSkip.add(targetClass);\n\t\tMethod methodToUse = findMethod(method, targetClass);\n\t\tif (methodToUse != null) {\n\t\t\tList<MergedAnnotation<A>> annotations = findDirectAnnotations(methodToUse);\n\t\t\tif (!annotations.isEmpty()) {\n\t\t\t\treturn annotations;\n\t\t\t}\n\t\t}\n\t\tList<MergedAnnotation<A>> annotations = new ArrayList<>(\n\t\t\t\tfindClosestMethodAnnotations(method, targetClass.getSuperclass(), classesToSkip));\n\t\tfor (Class<?> inter : targetClass.getInterfaces()) {\n\t\t\tannotations.addAll(findClosestMethodAnnotations(method, inter, classesToSkip));\n\t\t}\n\t\treturn annotations;\n\t}\n\n\tprivate List<MergedAnnotation<A>> findClosestClassAnnotations(Class<?> targetClass, Set<Class<?>> classesToSkip) {\n\t\tif (targetClass == null || classesToSkip.contains(targetClass) || targetClass == Object.class) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tclassesToSkip.add(targetClass);\n\t\tList<MergedAnnotation<A>> annotations = new ArrayList<>(findDirectAnnotations(targetClass));\n\t\tif (!annotations.isEmpty()) {\n\t\t\treturn annotations;\n\t\t}\n\t\tannotations.addAll(findClosestClassAnnotations(targetClass.getSuperclass(), classesToSkip));\n\t\tfor (Class<?> inter : targetClass.getInterfaces()) {\n\t\t\tannotations.addAll(findClosestClassAnnotations(inter, classesToSkip));\n\t\t}\n\t\treturn annotations;\n\t}\n\n\tprivate List<MergedAnnotation<A>> findDirectAnnotations(AnnotatedElement element) {\n\t\tMergedAnnotations mergedAnnotations = MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.DIRECT,\n\t\t\t\tRepeatableContainers.none());\n\t\treturn mergedAnnotations.stream()\n\t\t\t.filter((annotation) -> this.types.contains(annotation.getType()))\n\t\t\t.map((annotation) -> (MergedAnnotation<A>) annotation)\n\t\t\t.toList();\n\t}\n\n\tprivate static @Nullable Method findMethod(Method method, Class<?> targetClass) {\n\t\tfor (Method candidate : targetClass.getDeclaredMethods()) {\n\t\t\tif (candidate.equals(method)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t\tif (isOverride(method, candidate)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static boolean isOverride(Method rootMethod, Method candidateMethod) {\n\t\treturn (!Modifier.isPrivate(candidateMethod.getModifiers())\n\t\t\t\t&& candidateMethod.getName().equals(rootMethod.getName())\n\t\t\t\t&& hasSameParameterTypes(rootMethod, candidateMethod));\n\t}\n\n\tprivate static boolean hasSameParameterTypes(Method rootMethod, Method candidateMethod) {\n\t\tif (candidateMethod.getParameterCount() != rootMethod.getParameterCount()) {\n\t\t\treturn false;\n\t\t}\n\t\tClass<?>[] rootParameterTypes = rootMethod.getParameterTypes();\n\t\tClass<?>[] candidateParameterTypes = candidateMethod.getParameterTypes();\n\t\tif (Arrays.equals(candidateParameterTypes, rootParameterTypes)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn hasSameGenericTypeParameters(rootMethod, candidateMethod, rootParameterTypes);\n\t}\n\n\tprivate static boolean hasSameGenericTypeParameters(Method rootMethod, Method candidateMethod,\n\t\t\tClass<?>[] rootParameterTypes) {\n\n\t\tClass<?> sourceDeclaringClass = rootMethod.getDeclaringClass();\n\t\tClass<?> candidateDeclaringClass = candidateMethod.getDeclaringClass();\n\t\tif (!candidateDeclaringClass.isAssignableFrom(sourceDeclaringClass)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0; i < rootParameterTypes.length; i++) {\n\t\t\tClass<?> resolvedParameterType = ResolvableType.forMethodParameter(candidateMethod, i, sourceDeclaringClass)\n\t\t\t\t.toClass();\n\t\t\tif (rootParameterTypes[i] != resolvedParameterType) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.core.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/AuthorityUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Utility method for manipulating <tt>GrantedAuthority</tt> collections etc.\n * <p>\n * Mainly intended for internal use.\n *\n * @author Luke Taylor\n * @author Evgeniy Cheban\n */\npublic final class AuthorityUtils {\n\n\tpublic static final List<GrantedAuthority> NO_AUTHORITIES = Collections.emptyList();\n\n\tprivate AuthorityUtils() {\n\t}\n\n\t/**\n\t * Creates a array of GrantedAuthority objects from a comma-separated string\n\t * representation (e.g. \"ROLE_A, ROLE_B, ROLE_C\").\n\t * @param authorityString the comma-separated string\n\t * @return the authorities created by tokenizing the string\n\t */\n\tpublic static List<GrantedAuthority> commaSeparatedStringToAuthorityList(String authorityString) {\n\t\treturn createAuthorityList(StringUtils.tokenizeToStringArray(authorityString, \",\"));\n\t}\n\n\t/**\n\t * Converts an array of GrantedAuthority objects to a Set.\n\t * @return a Set of the Strings obtained from each call to\n\t * GrantedAuthority.getAuthority()\n\t */\n\tpublic static Set<String> authorityListToSet(Collection<? extends GrantedAuthority> userAuthorities) {\n\t\tAssert.notNull(userAuthorities, \"userAuthorities cannot be null\");\n\t\tSet<String> set = new HashSet<>(userAuthorities.size());\n\t\tfor (GrantedAuthority authority : userAuthorities) {\n\t\t\tset.add(authority.getAuthority());\n\t\t}\n\t\treturn set;\n\t}\n\n\t/**\n\t * Converts authorities into a List of GrantedAuthority objects.\n\t * @param authorities the authorities to convert\n\t * @return a List of GrantedAuthority objects\n\t */\n\tpublic static List<GrantedAuthority> createAuthorityList(String... authorities) {\n\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\tList<GrantedAuthority> grantedAuthorities = new ArrayList<>(authorities.length);\n\t\tfor (String authority : authorities) {\n\t\t\tgrantedAuthorities.add(new SimpleGrantedAuthority(authority));\n\t\t}\n\t\treturn grantedAuthorities;\n\t}\n\n\t/**\n\t * Converts authorities into a List of GrantedAuthority objects.\n\t * @param authorities the authorities to convert\n\t * @return a List of GrantedAuthority objects\n\t * @since 6.1\n\t */\n\tpublic static List<GrantedAuthority> createAuthorityList(Collection<String> authorities) {\n\t\tList<GrantedAuthority> grantedAuthorities = new ArrayList<>(authorities.size());\n\t\tfor (String authority : authorities) {\n\t\t\tgrantedAuthorities.add(new SimpleGrantedAuthority(authority));\n\t\t}\n\t\treturn grantedAuthorities;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/FactorGrantedAuthority.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority;\n\nimport java.time.Instant;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link GrantedAuthority} specifically used for indicating the factor used at time of\n * authentication.\n *\n * @author Yoobin Yoon\n * @author Rob Winch\n * @since 7.0\n */\npublic final class FactorGrantedAuthority implements GrantedAuthority {\n\n\t/**\n\t * The standard {@link GrantedAuthority#getAuthority()} that indicates that OAuth2\n\t * Authorization Code was used to authenticate.\n\t */\n\tpublic static final String AUTHORIZATION_CODE_AUTHORITY = \"FACTOR_AUTHORIZATION_CODE\";\n\n\t/**\n\t * The standard {@link GrantedAuthority#getAuthority()} that indicates that bearer\n\t * authentication was used to authenticate.\n\t */\n\tpublic static final String BEARER_AUTHORITY = \"FACTOR_BEARER\";\n\n\t/**\n\t * The standard {@link GrantedAuthority#getAuthority()} that indicates that CAS was\n\t * used to authenticate.\n\t */\n\tpublic static final String CAS_AUTHORITY = \"FACTOR_CAS\";\n\n\t/**\n\t * The standard {@link GrantedAuthority#getAuthority()} that indicates that one time\n\t * token was used to authenticate.\n\t */\n\tpublic static final String OTT_AUTHORITY = \"FACTOR_OTT\";\n\n\t/**\n\t * The standard {@link GrantedAuthority#getAuthority()} that indicates that a password\n\t * was used to authenticate.\n\t */\n\tpublic static final String PASSWORD_AUTHORITY = \"FACTOR_PASSWORD\";\n\n\t/**\n\t * The standard {@link GrantedAuthority#getAuthority()} that indicates that SAML was\n\t * used to authenticate.\n\t */\n\tpublic static final String SAML_RESPONSE_AUTHORITY = \"FACTOR_SAML_RESPONSE\";\n\n\t/**\n\t * The standard {@link GrantedAuthority#getAuthority()} that indicates that WebAuthn\n\t * was used to authenticate.\n\t */\n\tpublic static final String WEBAUTHN_AUTHORITY = \"FACTOR_WEBAUTHN\";\n\n\t/**\n\t * The standard {@link GrantedAuthority#getAuthority()} that indicates that X509 was\n\t * used to authenticate.\n\t */\n\tpublic static final String X509_AUTHORITY = \"FACTOR_X509\";\n\n\tprivate static final long serialVersionUID = 1998010439847123984L;\n\n\tprivate final String authority;\n\n\tprivate final Instant issuedAt;\n\n\tprivate FactorGrantedAuthority(String authority, Instant issuedAt) {\n\t\tAssert.notNull(authority, \"authority cannot be null\");\n\t\tAssert.notNull(issuedAt, \"issuedAt cannot be null\");\n\t\tthis.authority = authority;\n\t\tthis.issuedAt = issuedAt;\n\t}\n\n\t/**\n\t * Creates a new {@link Builder} with the specified authority.\n\t * @param authority the authority value (must not be null or empty)\n\t * @return a new {@link Builder}\n\t */\n\tpublic static Builder withAuthority(String authority) {\n\t\treturn new Builder(authority);\n\t}\n\n\t/**\n\t * Creates a new {@link Builder} with the specified factor which is automatically\n\t * prefixed with \"FACTOR_\".\n\t * @param factor the factor value which is automatically prefixed with \"FACTOR_\" (must\n\t * not be null or empty)\n\t * @return a new {@link Builder}\n\t */\n\tpublic static Builder withFactor(String factor) {\n\t\tAssert.hasText(factor, \"factor cannot be empty\");\n\t\tAssert.isTrue(!factor.startsWith(\"FACTOR_\"), () -> \"factor cannot start with 'FACTOR_' got '\" + factor + \"'\");\n\t\treturn withAuthority(\"FACTOR_\" + factor);\n\t}\n\n\t/**\n\t * Shortcut for {@code withAuthority(authority).build()}.\n\t * @param authority the authority value (must not be null or empty)\n\t * @return a new {@link FactorGrantedAuthority}\n\t */\n\tpublic static FactorGrantedAuthority fromAuthority(String authority) {\n\t\treturn withAuthority(authority).build();\n\t}\n\n\t/**\n\t * Shortcut for {@code withFactor(factor).build()}.\n\t * @param factor the factor value which is automatically prefixed with \"FACTOR_\" (must\n\t * not be null or empty)\n\t * @return a new {@link FactorGrantedAuthority}\n\t */\n\tpublic static FactorGrantedAuthority fromFactor(String factor) {\n\t\treturn withFactor(factor).build();\n\t}\n\n\t@Override\n\tpublic String getAuthority() {\n\t\treturn this.authority;\n\t}\n\n\t/**\n\t * Returns the instant when this authority was issued.\n\t * @return the issued-at instant\n\t */\n\tpublic Instant getIssuedAt() {\n\t\treturn this.issuedAt;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj instanceof FactorGrantedAuthority fga) {\n\t\t\treturn this.authority.equals(fga.authority) && this.issuedAt.equals(fga.issuedAt);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.authority, this.issuedAt);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"FactorGrantedAuthority [\");\n\t\tsb.append(\"authority=\").append(this.authority);\n\t\tsb.append(\", issuedAt=\").append(this.issuedAt);\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * Builder for {@link FactorGrantedAuthority}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final String authority;\n\n\t\tprivate @Nullable Instant issuedAt;\n\n\t\tprivate Builder(String authority) {\n\t\t\tAssert.hasText(authority, \"A granted authority textual representation is required\");\n\t\t\tthis.authority = authority;\n\t\t}\n\n\t\t/**\n\t\t * Sets the instant when this authority was issued.\n\t\t * @param issuedAt the issued-at instant\n\t\t * @return this builder\n\t\t */\n\t\tpublic Builder issuedAt(Instant issuedAt) {\n\t\t\tAssert.notNull(issuedAt, \"issuedAt cannot be null\");\n\t\t\tthis.issuedAt = issuedAt;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link FactorGrantedAuthority}.\n\t\t * <p>\n\t\t * If {@code issuedAt} is not set, it defaults to {@link Instant#now()}.\n\t\t * @return a new {@link FactorGrantedAuthority}\n\t\t * @throws IllegalArgumentException if temporal constraints are invalid\n\t\t */\n\t\tpublic FactorGrantedAuthority build() {\n\t\t\tif (this.issuedAt == null) {\n\t\t\t\tthis.issuedAt = Instant.now();\n\t\t\t}\n\n\t\t\treturn new FactorGrantedAuthority(this.authority, this.issuedAt);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/GrantedAuthoritiesContainer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority;\n\nimport java.io.Serializable;\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Indicates that a object stores GrantedAuthority objects.\n * <p>\n * Typically used in a pre-authenticated scenario when an AuthenticationDetails instance\n * may also be used to obtain user authorities.\n *\n * @author Ruud Senden\n * @author Luke Taylor\n * @since 2.0\n */\npublic interface GrantedAuthoritiesContainer extends Serializable {\n\n\tCollection<? extends GrantedAuthority> getGrantedAuthorities();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/SimpleGrantedAuthority.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Basic concrete implementation of a {@link GrantedAuthority}.\n *\n * <p>\n * Stores a {@code String} representation of an authority granted to the\n * {@link org.springframework.security.core.Authentication Authentication} object.\n *\n * @author Luke Taylor\n * @author Yanming Zhou\n */\npublic final class SimpleGrantedAuthority implements GrantedAuthority {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String role;\n\n\t/**\n\t * Constructs a {@code SimpleGrantedAuthority} using the provided authority.\n\t * @param authority The provided authority, including any prefix; for example,\n\t * {@code ROLE_ADMIN}\n\t */\n\tpublic SimpleGrantedAuthority(String authority) {\n\t\tAssert.hasText(authority, \"A granted authority textual representation is required\");\n\t\tthis.role = authority;\n\t}\n\n\t@Override\n\tpublic String getAuthority() {\n\t\treturn this.role;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj instanceof SimpleGrantedAuthority sga) {\n\t\t\treturn this.role.equals(sga.getAuthority());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.role.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.role;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/Attributes2GrantedAuthoritiesMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Interface to be implemented by classes that can map a list of security attributes (such\n * as roles or group names) to a collection of Spring Security {@code GrantedAuthority}s.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic interface Attributes2GrantedAuthoritiesMapper {\n\n\t/**\n\t * Implementations of this method should map the given collection of attributes to a\n\t * collection of Spring Security GrantedAuthorities. There are no restrictions for the\n\t * mapping process; a single attribute can be mapped to multiple Spring Security\n\t * GrantedAuthorities, all attributes can be mapped to a single Spring Security\n\t * {@code GrantedAuthority}, some attributes may not be mapped, etc.\n\t * @param attributes the attributes to be mapped\n\t * @return the collection of authorities created from the attributes\n\t */\n\tCollection<? extends GrantedAuthority> getGrantedAuthorities(Collection<String> attributes);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/GrantedAuthoritiesMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Mapping interface which can be injected into the authentication layer to convert the\n * authorities loaded from storage into those which will be used in the\n * {@code Authentication} object.\n *\n * @author Luke Taylor\n */\npublic interface GrantedAuthoritiesMapper {\n\n\tCollection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/MapBasedAttributes2GrantedAuthoritiesMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.StringTokenizer;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * This class implements the Attributes2GrantedAuthoritiesMapper and\n * MappableAttributesRetriever interfaces based on the supplied Map. It supports both\n * one-to-one and one-to-many mappings. The granted authorities to map to can be supplied\n * either as a String or as a GrantedAuthority object.\n *\n * @author Ruud Senden\n */\npublic class MapBasedAttributes2GrantedAuthoritiesMapper\n\t\timplements Attributes2GrantedAuthoritiesMapper, MappableAttributesRetriever, InitializingBean {\n\n\tprivate Map<String, Collection<GrantedAuthority>> attributes2grantedAuthoritiesMap = new HashMap<>();\n\n\tprivate String stringSeparator = \",\";\n\n\tprivate Set<String> mappableAttributes = new HashSet<>();\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.attributes2grantedAuthoritiesMap, \"attributes2grantedAuthoritiesMap must be set\");\n\t}\n\n\t/**\n\t * Map the given array of attributes to Spring Security GrantedAuthorities.\n\t */\n\t@Override\n\tpublic List<GrantedAuthority> getGrantedAuthorities(Collection<String> attributes) {\n\t\tArrayList<GrantedAuthority> result = new ArrayList<>();\n\t\tfor (String attribute : attributes) {\n\t\t\tCollection<GrantedAuthority> granted = this.attributes2grantedAuthoritiesMap.get(attribute);\n\t\t\tif (granted != null) {\n\t\t\t\tresult.addAll(granted);\n\t\t\t}\n\t\t}\n\t\tresult.trimToSize();\n\t\treturn result;\n\t}\n\n\t/**\n\t * @return Returns the attributes2grantedAuthoritiesMap.\n\t */\n\tpublic Map<String, Collection<GrantedAuthority>> getAttributes2grantedAuthoritiesMap() {\n\t\treturn this.attributes2grantedAuthoritiesMap;\n\t}\n\n\t/**\n\t * @param attributes2grantedAuthoritiesMap The attributes2grantedAuthoritiesMap to\n\t * set.\n\t */\n\tpublic void setAttributes2grantedAuthoritiesMap(final Map<?, ?> attributes2grantedAuthoritiesMap) {\n\t\tAssert.notEmpty(attributes2grantedAuthoritiesMap,\n\t\t\t\t\"A non-empty attributes2grantedAuthoritiesMap must be supplied\");\n\t\tthis.attributes2grantedAuthoritiesMap = preProcessMap(attributes2grantedAuthoritiesMap);\n\t\tthis.mappableAttributes = Collections.unmodifiableSet(this.attributes2grantedAuthoritiesMap.keySet());\n\t}\n\n\t/**\n\t * Preprocess the given map to convert all the values to GrantedAuthority collections\n\t * @param orgMap The map to process\n\t * @return the processed Map\n\t */\n\tprivate Map<String, Collection<GrantedAuthority>> preProcessMap(Map<?, ?> orgMap) {\n\t\tMap<String, Collection<GrantedAuthority>> result = new HashMap<>(orgMap.size());\n\t\tfor (Map.Entry<?, ?> entry : orgMap.entrySet()) {\n\t\t\tAssert.isInstanceOf(String.class, entry.getKey(),\n\t\t\t\t\t\"attributes2grantedAuthoritiesMap contains non-String objects as keys\");\n\t\t\tresult.put((String) entry.getKey(), getGrantedAuthorityCollection(entry.getValue()));\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Convert the given value to a collection of Granted Authorities\n\t * @param value The value to convert to a GrantedAuthority Collection\n\t * @return Collection containing the GrantedAuthority Collection\n\t */\n\tprivate Collection<GrantedAuthority> getGrantedAuthorityCollection(Object value) {\n\t\tCollection<GrantedAuthority> result = new ArrayList<>();\n\t\taddGrantedAuthorityCollection(result, value);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Convert the given value to a collection of Granted Authorities, adding the result\n\t * to the given result collection.\n\t * @param value The value to convert to a GrantedAuthority Collection\n\t */\n\tprivate void addGrantedAuthorityCollection(Collection<GrantedAuthority> result, Object value) {\n\t\tif (value == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (value instanceof Collection<?>) {\n\t\t\taddGrantedAuthorityCollection(result, (Collection<?>) value);\n\t\t}\n\t\telse if (value instanceof Object[]) {\n\t\t\taddGrantedAuthorityCollection(result, (Object[]) value);\n\t\t}\n\t\telse if (value instanceof String) {\n\t\t\taddGrantedAuthorityCollection(result, (String) value);\n\t\t}\n\t\telse if (value instanceof GrantedAuthority) {\n\t\t\tresult.add((GrantedAuthority) value);\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\"Invalid object type: \" + value.getClass().getName());\n\t\t}\n\t}\n\n\tprivate void addGrantedAuthorityCollection(Collection<GrantedAuthority> result, Collection<?> value) {\n\t\tfor (Object elt : value) {\n\t\t\taddGrantedAuthorityCollection(result, elt);\n\t\t}\n\t}\n\n\tprivate void addGrantedAuthorityCollection(Collection<GrantedAuthority> result, Object[] value) {\n\t\tfor (Object aValue : value) {\n\t\t\taddGrantedAuthorityCollection(result, aValue);\n\t\t}\n\t}\n\n\tprivate void addGrantedAuthorityCollection(Collection<GrantedAuthority> result, String value) {\n\t\tStringTokenizer tokenizer = new StringTokenizer(value, this.stringSeparator, false);\n\t\twhile (tokenizer.hasMoreTokens()) {\n\t\t\tString token = tokenizer.nextToken();\n\t\t\tif (StringUtils.hasText(token)) {\n\t\t\t\tresult.add(new SimpleGrantedAuthority(token));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @see org.springframework.security.core.authority.mapping.MappableAttributesRetriever#getMappableAttributes()\n\t */\n\t@Override\n\tpublic Set<String> getMappableAttributes() {\n\t\treturn this.mappableAttributes;\n\t}\n\n\t/**\n\t * @return Returns the stringSeparator.\n\t */\n\tpublic String getStringSeparator() {\n\t\treturn this.stringSeparator;\n\t}\n\n\t/**\n\t * @param stringSeparator The stringSeparator to set.\n\t */\n\tpublic void setStringSeparator(String stringSeparator) {\n\t\tAssert.notNull(stringSeparator, \"stringSeparator cannot be null\");\n\t\tthis.stringSeparator = stringSeparator;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/MappableAttributesRetriever.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.Set;\n\n/**\n * Interface to be implemented by classes that can retrieve a list of mappable security\n * attribute strings (for example the list of all available J2EE roles in a web or EJB\n * application).\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic interface MappableAttributesRetriever {\n\n\t/**\n\t * Implementations of this method should return a set of all string attributes which\n\t * can be mapped to <tt>GrantedAuthority</tt>s.\n\t * @return set of all mappable roles\n\t */\n\tSet<String> getMappableAttributes();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/NullAuthoritiesMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * @author Luke Taylor\n */\npublic class NullAuthoritiesMapper implements GrantedAuthoritiesMapper {\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {\n\t\treturn authorities;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAttributes2GrantedAuthoritiesMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * This class implements the Attributes2GrantedAuthoritiesMapper interface by doing a\n * one-to-one mapping from roles to Spring Security GrantedAuthorities. Optionally a\n * prefix can be added, and the attribute name can be converted to upper or lower case.\n * <p>\n * By default, the attribute is prefixed with \"ROLE_\" unless it already starts with\n * \"ROLE_\", and no case conversion is done.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic class SimpleAttributes2GrantedAuthoritiesMapper\n\t\timplements Attributes2GrantedAuthoritiesMapper, InitializingBean {\n\n\tprivate String attributePrefix = \"ROLE_\";\n\n\tprivate boolean convertAttributeToUpperCase = false;\n\n\tprivate boolean convertAttributeToLowerCase = false;\n\n\tprivate boolean addPrefixIfAlreadyExisting = false;\n\n\t/**\n\t * Check whether all properties have been set to correct values.\n\t */\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.isTrue(!(isConvertAttributeToUpperCase() && isConvertAttributeToLowerCase()),\n\t\t\t\t\"Either convertAttributeToUpperCase or convertAttributeToLowerCase can be set to true, but not both\");\n\t}\n\n\t/**\n\t * Map the given list of string attributes one-to-one to Spring Security\n\t * GrantedAuthorities.\n\t */\n\t@Override\n\tpublic List<GrantedAuthority> getGrantedAuthorities(Collection<String> attributes) {\n\t\tList<GrantedAuthority> result = new ArrayList<>(attributes.size());\n\t\tfor (String attribute : attributes) {\n\t\t\tresult.add(getGrantedAuthority(attribute));\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Map the given role one-on-one to a Spring Security GrantedAuthority, optionally\n\t * doing case conversion and/or adding a prefix.\n\t * @param attribute The attribute for which to get a GrantedAuthority\n\t * @return GrantedAuthority representing the given role.\n\t */\n\tprivate GrantedAuthority getGrantedAuthority(String attribute) {\n\t\tif (isConvertAttributeToLowerCase()) {\n\t\t\tattribute = attribute.toLowerCase(Locale.ROOT);\n\t\t}\n\t\telse if (isConvertAttributeToUpperCase()) {\n\t\t\tattribute = attribute.toUpperCase(Locale.ROOT);\n\t\t}\n\t\tif (isAddPrefixIfAlreadyExisting() || !attribute.startsWith(getAttributePrefix())) {\n\t\t\treturn new SimpleGrantedAuthority(getAttributePrefix() + attribute);\n\t\t}\n\t\telse {\n\t\t\treturn new SimpleGrantedAuthority(attribute);\n\t\t}\n\t}\n\n\tprivate boolean isConvertAttributeToLowerCase() {\n\t\treturn this.convertAttributeToLowerCase;\n\t}\n\n\tpublic void setConvertAttributeToLowerCase(boolean b) {\n\t\tthis.convertAttributeToLowerCase = b;\n\t}\n\n\tprivate boolean isConvertAttributeToUpperCase() {\n\t\treturn this.convertAttributeToUpperCase;\n\t}\n\n\tpublic void setConvertAttributeToUpperCase(boolean b) {\n\t\tthis.convertAttributeToUpperCase = b;\n\t}\n\n\tprivate String getAttributePrefix() {\n\t\treturn (this.attributePrefix != null) ? this.attributePrefix : \"\";\n\t}\n\n\tpublic void setAttributePrefix(String string) {\n\t\tthis.attributePrefix = string;\n\t}\n\n\tprivate boolean isAddPrefixIfAlreadyExisting() {\n\t\treturn this.addPrefixIfAlreadyExisting;\n\t}\n\n\tpublic void setAddPrefixIfAlreadyExisting(boolean b) {\n\t\tthis.addPrefixIfAlreadyExisting = b;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Simple one-to-one {@code GrantedAuthoritiesMapper} which allows for case conversion of\n * the authority name and the addition of a string prefix (which defaults to {@code ROLE_}\n * ).\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic final class SimpleAuthorityMapper implements GrantedAuthoritiesMapper, InitializingBean {\n\n\tprivate @Nullable GrantedAuthority defaultAuthority;\n\n\tprivate String prefix = \"ROLE_\";\n\n\tprivate boolean convertToUpperCase = false;\n\n\tprivate boolean convertToLowerCase = false;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.isTrue(!(this.convertToUpperCase && this.convertToLowerCase),\n\t\t\t\t\"Either convertToUpperCase or convertToLowerCase can be set to true, but not both\");\n\t}\n\n\t/**\n\t * Creates a mapping of the supplied authorities based on the case-conversion and\n\t * prefix settings. The mapping will be one-to-one unless duplicates are produced\n\t * during the conversion. If a default authority has been set, this will also be\n\t * assigned to each mapping.\n\t * @param authorities the original authorities\n\t * @return the converted set of authorities\n\t */\n\t@Override\n\tpublic Set<GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {\n\t\tHashSet<GrantedAuthority> mapped = new HashSet<>(authorities.size());\n\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\tString authorityStr = authority.getAuthority();\n\t\t\tif (authorityStr != null) {\n\t\t\t\tmapped.add(mapAuthority(authorityStr));\n\t\t\t}\n\t\t}\n\t\tif (this.defaultAuthority != null) {\n\t\t\tmapped.add(this.defaultAuthority);\n\t\t}\n\t\treturn mapped;\n\t}\n\n\tprivate GrantedAuthority mapAuthority(String name) {\n\t\tif (this.convertToUpperCase) {\n\t\t\tname = name.toUpperCase(Locale.ROOT);\n\t\t}\n\t\telse if (this.convertToLowerCase) {\n\t\t\tname = name.toLowerCase(Locale.ROOT);\n\t\t}\n\t\tif (this.prefix.length() > 0 && !name.startsWith(this.prefix)) {\n\t\t\tname = this.prefix + name;\n\t\t}\n\t\treturn new SimpleGrantedAuthority(name);\n\t}\n\n\t/**\n\t * Sets the prefix which should be added to the authority name (if it doesn't already\n\t * exist)\n\t * @param prefix the prefix, typically to satisfy the behaviour of an\n\t * {@code AccessDecisionVoter}.\n\t */\n\tpublic void setPrefix(String prefix) {\n\t\tAssert.notNull(prefix, \"prefix cannot be null\");\n\t\tthis.prefix = prefix;\n\t}\n\n\t/**\n\t * Whether to convert the authority value to upper case in the mapping.\n\t * @param convertToUpperCase defaults to {@code false}\n\t */\n\tpublic void setConvertToUpperCase(boolean convertToUpperCase) {\n\t\tthis.convertToUpperCase = convertToUpperCase;\n\t}\n\n\t/**\n\t * Whether to convert the authority value to lower case in the mapping.\n\t * @param convertToLowerCase defaults to {@code false}\n\t */\n\tpublic void setConvertToLowerCase(boolean convertToLowerCase) {\n\t\tthis.convertToLowerCase = convertToLowerCase;\n\t}\n\n\t/**\n\t * Sets a default authority to be assigned to all users\n\t * @param authority the name of the authority to be assigned to all users.\n\t */\n\tpublic void setDefaultAuthority(String authority) {\n\t\tAssert.hasText(authority, \"The authority name cannot be set to an empty value\");\n\t\tthis.defaultAuthority = new SimpleGrantedAuthority(authority);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/SimpleMappableAttributesRetriever.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * This class implements the MappableAttributesRetriever interface by just returning a\n * list of mappable attributes as previously set using the corresponding setter method.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic class SimpleMappableAttributesRetriever implements MappableAttributesRetriever {\n\n\tprivate Set<String> mappableAttributes = new HashSet<>();\n\n\t@Override\n\tpublic Set<String> getMappableAttributes() {\n\t\treturn this.mappableAttributes;\n\t}\n\n\tpublic void setMappableAttributes(Set<String> aMappableRoles) {\n\t\tthis.mappableAttributes = new HashSet<>();\n\t\tthis.mappableAttributes.addAll(aMappableRoles);\n\t\tthis.mappableAttributes = Collections.unmodifiableSet(this.mappableAttributes);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/mapping/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Strategies for mapping a list of attributes (such as roles or LDAP groups) to a list of\n * {@code GrantedAuthority}s.\n * <p>\n * Provides a layer of indirection between a security data repository and the logical\n * authorities required within an application.\n */\n@NullMarked\npackage org.springframework.security.core.authority.mapping;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/authority/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The default implementation of the {@code GrantedAuthority} interface.\n */\n@NullMarked\npackage org.springframework.security.core.authority;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/DeferredSecurityContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\n/**\n * An interface that allows delayed access to a {@link SecurityContext} that may be\n * generated.\n *\n * @author Steve Riesenberg\n * @since 5.8\n */\npublic interface DeferredSecurityContext extends Supplier<SecurityContext> {\n\n\t/**\n\t * Returns true if {@link #get()} refers to a generated {@link SecurityContext} or\n\t * false if it already existed.\n\t * @return true if {@link #get()} refers to a generated {@link SecurityContext} or\n\t * false if it already existed\n\t */\n\tboolean isGenerated();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/GlobalSecurityContextHolderStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A <code>static</code> field-based implementation of\n * {@link SecurityContextHolderStrategy}.\n * <p>\n * This means that all instances in the JVM share the same <code>SecurityContext</code>.\n * This is generally useful with rich clients, such as Swing.\n *\n * @author Ben Alex\n */\nfinal class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n\tprivate static @Nullable SecurityContext contextHolder;\n\n\t@Override\n\tpublic void clearContext() {\n\t\tcontextHolder = null;\n\t}\n\n\t@Override\n\tpublic SecurityContext getContext() {\n\t\tif (contextHolder == null) {\n\t\t\tcontextHolder = new SecurityContextImpl();\n\t\t}\n\t\treturn contextHolder;\n\t}\n\n\t@Override\n\tpublic void setContext(SecurityContext context) {\n\t\tAssert.notNull(context, \"Only non-null SecurityContext instances are permitted\");\n\t\tcontextHolder = context;\n\t}\n\n\t@Override\n\tpublic SecurityContext createEmptyContext() {\n\t\treturn new SecurityContextImpl();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/InheritableThreadLocalSecurityContextHolderStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\nimport org.springframework.util.Assert;\n\n/**\n * An <code>InheritableThreadLocal</code>-based implementation of\n * {@link org.springframework.security.core.context.SecurityContextHolderStrategy}.\n *\n * @author Ben Alex\n * @author Rob Winch\n * @see java.lang.ThreadLocal\n */\nfinal class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n\tprivate static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new InheritableThreadLocal<>();\n\n\t@Override\n\tpublic void clearContext() {\n\t\tcontextHolder.remove();\n\t}\n\n\t@Override\n\tpublic SecurityContext getContext() {\n\t\treturn getDeferredContext().get();\n\t}\n\n\t@Override\n\tpublic Supplier<SecurityContext> getDeferredContext() {\n\t\tSupplier<SecurityContext> result = contextHolder.get();\n\t\tif (result == null) {\n\t\t\tSecurityContext context = createEmptyContext();\n\t\t\tresult = () -> context;\n\t\t\tcontextHolder.set(result);\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void setContext(SecurityContext context) {\n\t\tAssert.notNull(context, \"Only non-null SecurityContext instances are permitted\");\n\t\tcontextHolder.set(() -> context);\n\t}\n\n\t@Override\n\tpublic void setDeferredContext(Supplier<SecurityContext> deferredContext) {\n\t\tAssert.notNull(deferredContext, \"Only non-null Supplier instances are permitted\");\n\t\tSupplier<SecurityContext> notNullDeferredContext = () -> {\n\t\t\tSecurityContext result = deferredContext.get();\n\t\t\tAssert.notNull(result, \"A Supplier<SecurityContext> returned null and is not allowed.\");\n\t\t\treturn result;\n\t\t};\n\t\tcontextHolder.set(notNullDeferredContext);\n\t}\n\n\t@Override\n\tpublic SecurityContext createEmptyContext() {\n\t\treturn new SecurityContextImpl();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/ListeningSecurityContextHolderStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Supplier;\n\nimport org.springframework.util.Assert;\n\n/**\n * An API for notifying when the {@link SecurityContext} changes.\n *\n * Note that this does not notify when the underlying authentication changes. To get\n * notified about authentication changes, ensure that you are using {@link #setContext}\n * when changing the authentication like so:\n *\n * <pre>\n *\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n *\tcontext.setAuthentication(authentication);\n *\tSecurityContextHolder.setContext(context);\n * </pre>\n *\n * To add a listener to the existing {@link SecurityContextHolder}, you can do:\n *\n * <pre>\n *  SecurityContextHolderStrategy original = SecurityContextHolder.getContextHolderStrategy();\n *  SecurityContextChangedListener listener = new YourListener();\n *  SecurityContextHolderStrategy strategy = new ListeningSecurityContextHolderStrategy(original, listener);\n *  SecurityContextHolder.setContextHolderStrategy(strategy);\n * </pre>\n *\n * NOTE: Any object that you supply to the {@link SecurityContextHolder} is now part of\n * the static context and as such will not get garbage collected. To remove the reference,\n * {@link SecurityContextHolder#setContextHolderStrategy reset the strategy} like so:\n *\n * <pre>\n *   SecurityContextHolder.setContextHolderStrategy(original);\n * </pre>\n *\n * This will then allow {@code YourListener} and its members to be garbage collected.\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class ListeningSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n\tprivate final Collection<SecurityContextChangedListener> listeners;\n\n\tprivate final SecurityContextHolderStrategy delegate;\n\n\t/**\n\t * Construct a {@link ListeningSecurityContextHolderStrategy} based on\n\t * {@link ThreadLocalSecurityContextHolderStrategy}\n\t * @param listeners the listeners that should be notified when the\n\t * {@link SecurityContext} is {@link #setContext(SecurityContext) set} or\n\t * {@link #clearContext() cleared}\n\t *\n\t * @since 5.7\n\t */\n\tpublic ListeningSecurityContextHolderStrategy(Collection<SecurityContextChangedListener> listeners) {\n\t\tthis(new ThreadLocalSecurityContextHolderStrategy(), listeners);\n\t}\n\n\t/**\n\t * Construct a {@link ListeningSecurityContextHolderStrategy} based on\n\t * {@link ThreadLocalSecurityContextHolderStrategy}\n\t * @param listeners the listeners that should be notified when the\n\t * {@link SecurityContext} is {@link #setContext(SecurityContext) set} or\n\t * {@link #clearContext() cleared}\n\t *\n\t * @since 5.7\n\t */\n\tpublic ListeningSecurityContextHolderStrategy(SecurityContextChangedListener... listeners) {\n\t\tthis(new ThreadLocalSecurityContextHolderStrategy(), listeners);\n\t}\n\n\t/**\n\t * Construct a {@link ListeningSecurityContextHolderStrategy}\n\t * @param listeners the listeners that should be notified when the\n\t * {@link SecurityContext} is {@link #setContext(SecurityContext) set} or\n\t * {@link #clearContext() cleared}\n\t * @param delegate the underlying {@link SecurityContextHolderStrategy}\n\t */\n\tpublic ListeningSecurityContextHolderStrategy(SecurityContextHolderStrategy delegate,\n\t\t\tCollection<SecurityContextChangedListener> listeners) {\n\t\tAssert.notNull(delegate, \"securityContextHolderStrategy cannot be null\");\n\t\tAssert.notNull(listeners, \"securityContextChangedListeners cannot be null\");\n\t\tAssert.notEmpty(listeners, \"securityContextChangedListeners cannot be empty\");\n\t\tAssert.noNullElements(listeners, \"securityContextChangedListeners cannot contain null elements\");\n\t\tthis.delegate = delegate;\n\t\tthis.listeners = listeners;\n\t}\n\n\t/**\n\t * Construct a {@link ListeningSecurityContextHolderStrategy}\n\t * @param listeners the listeners that should be notified when the\n\t * {@link SecurityContext} is {@link #setContext(SecurityContext) set} or\n\t * {@link #clearContext() cleared}\n\t * @param delegate the underlying {@link SecurityContextHolderStrategy}\n\t */\n\tpublic ListeningSecurityContextHolderStrategy(SecurityContextHolderStrategy delegate,\n\t\t\tSecurityContextChangedListener... listeners) {\n\t\tAssert.notNull(delegate, \"securityContextHolderStrategy cannot be null\");\n\t\tAssert.notNull(listeners, \"securityContextChangedListeners cannot be null\");\n\t\tAssert.notEmpty(listeners, \"securityContextChangedListeners cannot be empty\");\n\t\tAssert.noNullElements(listeners, \"securityContextChangedListeners cannot contain null elements\");\n\t\tthis.delegate = delegate;\n\t\tthis.listeners = Arrays.asList(listeners);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void clearContext() {\n\t\tSupplier<SecurityContext> deferred = this.delegate.getDeferredContext();\n\t\tthis.delegate.clearContext();\n\t\tpublish(new SecurityContextChangedEvent(deferred, SecurityContextChangedEvent.NO_CONTEXT));\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic SecurityContext getContext() {\n\t\treturn this.delegate.getContext();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Supplier<SecurityContext> getDeferredContext() {\n\t\treturn this.delegate.getDeferredContext();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void setContext(SecurityContext context) {\n\t\tsetDeferredContext(() -> context);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void setDeferredContext(Supplier<SecurityContext> deferredContext) {\n\t\tthis.delegate.setDeferredContext(new PublishOnceSupplier(getDeferredContext(), deferredContext));\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic SecurityContext createEmptyContext() {\n\t\treturn this.delegate.createEmptyContext();\n\t}\n\n\tprivate void publish(SecurityContextChangedEvent event) {\n\t\tfor (SecurityContextChangedListener listener : this.listeners) {\n\t\t\tlistener.securityContextChanged(event);\n\t\t}\n\t}\n\n\tclass PublishOnceSupplier implements Supplier<SecurityContext> {\n\n\t\tprivate final AtomicBoolean isPublished = new AtomicBoolean(false);\n\n\t\tprivate final Supplier<SecurityContext> old;\n\n\t\tprivate final Supplier<SecurityContext> updated;\n\n\t\tPublishOnceSupplier(Supplier<SecurityContext> old, Supplier<SecurityContext> updated) {\n\t\t\tif (old instanceof PublishOnceSupplier) {\n\t\t\t\tthis.old = ((PublishOnceSupplier) old).updated;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.old = old;\n\t\t\t}\n\t\t\tthis.updated = updated;\n\t\t}\n\n\t\t@Override\n\t\tpublic SecurityContext get() {\n\t\t\tSecurityContext updated = this.updated.get();\n\t\t\tif (this.isPublished.compareAndSet(false, true)) {\n\t\t\t\tSecurityContext old = this.old.get();\n\t\t\t\tif (old != updated) {\n\t\t\t\t\tpublish(new SecurityContextChangedEvent(old, updated));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn updated;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/ObservationSecurityContextChangedListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * A {@link SecurityContextChangedListener} that adds events to an existing\n * {@link Observation}\n *\n * If no {@link Observation} is present when an event is fired, then the event is\n * unrecorded.\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic final class ObservationSecurityContextChangedListener implements SecurityContextChangedListener {\n\n\tstatic final String SECURITY_CONTEXT_CREATED = \"spring.security.context.created\";\n\n\tstatic final String SECURITY_CONTEXT_CHANGED = \"spring.security.context.changed\";\n\n\tstatic final String SECURITY_CONTEXT_CLEARED = \"spring.security.context.cleared\";\n\n\tprivate final ObservationRegistry registry;\n\n\t/**\n\t * Create a {@link ObservationSecurityContextChangedListener}\n\t * @param registry the {@link ObservationRegistry} for looking up the surrounding\n\t * {@link Observation}\n\t */\n\tpublic ObservationSecurityContextChangedListener(ObservationRegistry registry) {\n\t\tthis.registry = registry;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tpublic void securityContextChanged(SecurityContextChangedEvent event) {\n\t\tObservation observation = this.registry.getCurrentObservation();\n\t\tif (observation == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (event.isCleared()) {\n\t\t\tobservation.event(Observation.Event.of(SECURITY_CONTEXT_CLEARED));\n\t\t\treturn;\n\t\t}\n\t\tAuthentication oldAuthentication = getAuthentication(event.getOldContext());\n\t\tAuthentication newAuthentication = getAuthentication(event.getNewContext());\n\t\tif (oldAuthentication == null && newAuthentication == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (oldAuthentication == null) {\n\t\t\t// NOTE: NullAway thinks that newAuthentication can be null, but it cannot due\n\t\t\t// to previous check\n\t\t\tobservation.event(Observation.Event.of(SECURITY_CONTEXT_CREATED, \"%s [%s]\")\n\t\t\t\t.format(SECURITY_CONTEXT_CREATED, newAuthentication.getClass().getSimpleName()));\n\t\t\treturn;\n\t\t}\n\t\tif (newAuthentication == null) {\n\t\t\t// NOTE: NullAway thinks that oldAuthentication can be null, but it cannot due\n\t\t\t// to previous check\n\t\t\tobservation.event(Observation.Event.of(SECURITY_CONTEXT_CLEARED, \"%s [%s]\")\n\t\t\t\t.format(SECURITY_CONTEXT_CLEARED, oldAuthentication.getClass().getSimpleName()));\n\t\t\treturn;\n\t\t}\n\t\tif (oldAuthentication.equals(newAuthentication)) {\n\t\t\treturn;\n\t\t}\n\t\tobservation.event(Observation.Event.of(SECURITY_CONTEXT_CHANGED, \"%s [%s] -> [%s]\")\n\t\t\t.format(SECURITY_CONTEXT_CHANGED, oldAuthentication.getClass().getSimpleName(),\n\t\t\t\t\tnewAuthentication.getClass().getSimpleName()));\n\t}\n\n\tprivate static @Nullable Authentication getAuthentication(SecurityContext context) {\n\t\tif (context == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn context.getAuthentication();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/ReactiveSecurityContextHolder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Allows getting and setting the Spring {@link SecurityContext} into a {@link Context}.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic final class ReactiveSecurityContextHolder {\n\n\tprivate static final Class<?> SECURITY_CONTEXT_KEY = SecurityContext.class;\n\n\tprivate ReactiveSecurityContextHolder() {\n\t}\n\n\t/**\n\t * Gets the {@code Mono<SecurityContext>} from Reactor {@link Context}\n\t * @return the {@code Mono<SecurityContext>}\n\t */\n\tpublic static Mono<SecurityContext> getContext() {\n\t\t// @formatter:off\n\t\treturn Mono.deferContextual(Mono::just)\n\t\t\t\t.cast(Context.class)\n\t\t\t\t.filter(ReactiveSecurityContextHolder::hasSecurityContext)\n\t\t\t\t.flatMap(ReactiveSecurityContextHolder::getSecurityContext);\n\t\t// @formatter:on\n\t}\n\n\tprivate static boolean hasSecurityContext(Context context) {\n\t\treturn context.hasKey(SECURITY_CONTEXT_KEY);\n\t}\n\n\tprivate static Mono<SecurityContext> getSecurityContext(Context context) {\n\t\treturn context.<Mono<SecurityContext>>get(SECURITY_CONTEXT_KEY);\n\t}\n\n\t/**\n\t * Clears the {@code Mono<SecurityContext>} from Reactor {@link Context}\n\t * @return Return a {@code Mono<Void>} which only replays complete and error signals\n\t * from clearing the context.\n\t */\n\tpublic static Function<Context, Context> clearContext() {\n\t\treturn (context) -> context.delete(SECURITY_CONTEXT_KEY);\n\t}\n\n\t/**\n\t * Creates a Reactor {@link Context} that contains the {@code Mono<SecurityContext>}\n\t * that can be merged into another {@link Context}\n\t * @param securityContext the {@code Mono<SecurityContext>} to set in the returned\n\t * Reactor {@link Context}\n\t * @return a Reactor {@link Context} that contains the {@code Mono<SecurityContext>}\n\t */\n\tpublic static Context withSecurityContext(Mono<? extends SecurityContext> securityContext) {\n\t\treturn Context.of(SECURITY_CONTEXT_KEY, securityContext);\n\t}\n\n\t/**\n\t * A shortcut for {@link #withSecurityContext(Mono)}\n\t * @param authentication the {@link Authentication} to be used\n\t * @return a Reactor {@link Context} that contains the {@code Mono<SecurityContext>}\n\t */\n\tpublic static Context withAuthentication(Authentication authentication) {\n\t\treturn withSecurityContext(Mono.just(new SecurityContextImpl(authentication)));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/ReactiveSecurityContextHolderThreadLocalAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport io.micrometer.context.ThreadLocalAccessor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ThreadLocalAccessor} for accessing a {@link SecurityContext} with the\n * {@link ReactiveSecurityContextHolder}.\n * <p>\n * This class adapts the {@link ReactiveSecurityContextHolder} to the\n * {@link ThreadLocalAccessor} contract to allow Micrometer Context Propagation to\n * automatically propagate a {@link SecurityContext} in Reactive applications. It is\n * automatically registered with the {@link io.micrometer.context.ContextRegistry} through\n * the {@link java.util.ServiceLoader} mechanism when context-propagation is on the\n * classpath.\n *\n * @author Steve Riesenberg\n * @since 6.5\n * @see io.micrometer.context.ContextRegistry\n */\npublic final class ReactiveSecurityContextHolderThreadLocalAccessor\n\t\timplements ThreadLocalAccessor<Mono<SecurityContext>> {\n\n\tprivate static final ThreadLocal<Mono<SecurityContext>> threadLocal = new ThreadLocal<>();\n\n\t@Override\n\tpublic Object key() {\n\t\treturn SecurityContext.class;\n\t}\n\n\t@Override\n\tpublic Mono<SecurityContext> getValue() {\n\t\treturn threadLocal.get();\n\t}\n\n\t@Override\n\tpublic void setValue(Mono<SecurityContext> securityContext) {\n\t\tAssert.notNull(securityContext, \"securityContext cannot be null\");\n\t\tthreadLocal.set(securityContext);\n\t}\n\n\t@Override\n\tpublic void setValue() {\n\t\tthreadLocal.remove();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/SecurityContext.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Interface defining the minimum security information associated with the current thread\n * of execution.\n *\n * <p>\n * The security context is stored in a {@link SecurityContextHolder}.\n * </p>\n *\n * @author Ben Alex\n */\npublic interface SecurityContext extends Serializable {\n\n\t/**\n\t * Obtains the currently authenticated principal, or an authentication request token.\n\t * @return the <code>Authentication</code> or <code>null</code> if no authentication\n\t * information is available\n\t */\n\t@Nullable Authentication getAuthentication();\n\n\t/**\n\t * Changes the currently authenticated principal, or removes the authentication\n\t * information.\n\t * @param authentication the new <code>Authentication</code> token, or\n\t * <code>null</code> if no further authentication information should be stored\n\t */\n\tvoid setAuthentication(@Nullable Authentication authentication);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/SecurityContextChangedEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\nimport org.springframework.context.ApplicationEvent;\n\n/**\n * An event that represents a change in {@link SecurityContext}\n *\n * @author Josh Cummings\n * @since 5.6\n */\n@SuppressWarnings(\"serial\")\npublic class SecurityContextChangedEvent extends ApplicationEvent {\n\n\tpublic static final Supplier<SecurityContext> NO_CONTEXT = () -> null;\n\n\tprivate final Supplier<SecurityContext> oldContext;\n\n\tprivate final Supplier<SecurityContext> newContext;\n\n\t/**\n\t * Construct an event\n\t * @param oldContext the old security context\n\t * @param newContext the new security context, use\n\t * {@link SecurityContextChangedEvent#NO_CONTEXT} for if the context is cleared\n\t * @since 5.8\n\t */\n\tpublic SecurityContextChangedEvent(Supplier<SecurityContext> oldContext, Supplier<SecurityContext> newContext) {\n\t\tsuper(SecurityContextHolder.class);\n\t\tthis.oldContext = oldContext;\n\t\tthis.newContext = newContext;\n\t}\n\n\t/**\n\t * Construct an event\n\t * @param oldContext the old security context\n\t * @param newContext the new security context\n\t */\n\tpublic SecurityContextChangedEvent(SecurityContext oldContext, SecurityContext newContext) {\n\t\tthis(() -> oldContext, (newContext != null) ? () -> newContext : NO_CONTEXT);\n\t}\n\n\t/**\n\t * Get the {@link SecurityContext} set on the {@link SecurityContextHolder}\n\t * immediately previous to this event\n\t * @return the previous {@link SecurityContext}\n\t */\n\tpublic SecurityContext getOldContext() {\n\t\treturn this.oldContext.get();\n\t}\n\n\t/**\n\t * Get the {@link SecurityContext} set on the {@link SecurityContextHolder} as of this\n\t * event\n\t * @return the current {@link SecurityContext}\n\t */\n\tpublic SecurityContext getNewContext() {\n\t\treturn this.newContext.get();\n\t}\n\n\t/**\n\t * Say whether the event is a context-clearing event.\n\t *\n\t * <p>\n\t * This method is handy for avoiding looking up the new context to confirm it is a\n\t * cleared event.\n\t * @return {@code true} if the new context is\n\t * {@link SecurityContextChangedEvent#NO_CONTEXT}\n\t * @since 5.8\n\t */\n\tpublic boolean isCleared() {\n\t\treturn this.newContext == NO_CONTEXT;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/SecurityContextChangedListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\n/**\n * A listener for {@link SecurityContextChangedEvent}s\n *\n * @author Josh Cummings\n * @since 5.6\n */\n@FunctionalInterface\npublic interface SecurityContextChangedListener {\n\n\tvoid securityContextChanged(SecurityContextChangedEvent event);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/SecurityContextHolder.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.lang.reflect.Constructor;\nimport java.util.function.Supplier;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Associates a given {@link SecurityContext} with the current execution thread.\n * <p>\n * This class provides a series of static methods that delegate to an instance of\n * {@link org.springframework.security.core.context.SecurityContextHolderStrategy}. The\n * purpose of the class is to provide a convenient way to specify the strategy that should\n * be used for a given JVM. This is a JVM-wide setting, since everything in this class is\n * <code>static</code> to facilitate ease of use in calling code.\n * <p>\n * To specify which strategy should be used, you must provide a mode setting. A mode\n * setting is one of the three valid <code>MODE_</code> settings defined as\n * <code>static final</code> fields, or a fully qualified classname to a concrete\n * implementation of\n * {@link org.springframework.security.core.context.SecurityContextHolderStrategy} that\n * provides a public no-argument constructor.\n * <p>\n * There are two ways to specify the desired strategy mode <code>String</code>. The first\n * is to specify it via the system property keyed on {@link #SYSTEM_PROPERTY}. The second\n * is to call {@link #setStrategyName(String)} before using the class. If neither approach\n * is used, the class will default to using {@link #MODE_THREADLOCAL}, which is backwards\n * compatible, has fewer JVM incompatibilities and is appropriate on servers (whereas\n * {@link #MODE_GLOBAL} is definitely inappropriate for server use).\n *\n * @author Ben Alex\n * @author Rob Winch\n *\n */\npublic class SecurityContextHolder {\n\n\tpublic static final String MODE_THREADLOCAL = \"MODE_THREADLOCAL\";\n\n\tpublic static final String MODE_INHERITABLETHREADLOCAL = \"MODE_INHERITABLETHREADLOCAL\";\n\n\tpublic static final String MODE_GLOBAL = \"MODE_GLOBAL\";\n\n\tprivate static final String MODE_PRE_INITIALIZED = \"MODE_PRE_INITIALIZED\";\n\n\tpublic static final String SYSTEM_PROPERTY = \"spring.security.strategy\";\n\n\tprivate static String strategyName = System.getProperty(SYSTEM_PROPERTY);\n\n\tprivate static SecurityContextHolderStrategy strategy = new ThreadLocalSecurityContextHolderStrategy();\n\n\tprivate static int initializeCount = 0;\n\n\tstatic {\n\t\tinitialize();\n\t}\n\n\tprivate static void initialize() {\n\t\tinitializeStrategy();\n\t\tinitializeCount++;\n\t}\n\n\tprivate static void initializeStrategy() {\n\t\tif (MODE_PRE_INITIALIZED.equals(strategyName)) {\n\t\t\tAssert.state(strategy != null, \"When using \" + MODE_PRE_INITIALIZED\n\t\t\t\t\t+ \", setContextHolderStrategy must be called with the fully constructed strategy\");\n\t\t\treturn;\n\t\t}\n\t\tif (!StringUtils.hasText(strategyName)) {\n\t\t\t// Set default\n\t\t\tstrategyName = MODE_THREADLOCAL;\n\t\t}\n\t\tif (strategyName.equals(MODE_THREADLOCAL)) {\n\t\t\tstrategy = new ThreadLocalSecurityContextHolderStrategy();\n\t\t\treturn;\n\t\t}\n\t\tif (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {\n\t\t\tstrategy = new InheritableThreadLocalSecurityContextHolderStrategy();\n\t\t\treturn;\n\t\t}\n\t\tif (strategyName.equals(MODE_GLOBAL)) {\n\t\t\tstrategy = new GlobalSecurityContextHolderStrategy();\n\t\t\treturn;\n\t\t}\n\t\t// Try to load a custom strategy\n\t\ttry {\n\t\t\tClass<?> clazz = Class.forName(strategyName);\n\t\t\tConstructor<?> customStrategy = clazz.getConstructor();\n\t\t\tstrategy = (SecurityContextHolderStrategy) customStrategy.newInstance();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tReflectionUtils.handleReflectionException(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Explicitly clears the context value from the current thread.\n\t */\n\tpublic static void clearContext() {\n\t\tstrategy.clearContext();\n\t}\n\n\t/**\n\t * Obtain the current <code>SecurityContext</code>.\n\t * @return the security context (never <code>null</code>)\n\t */\n\tpublic static SecurityContext getContext() {\n\t\treturn strategy.getContext();\n\t}\n\n\t/**\n\t * Obtains a {@link Supplier} that returns the current context.\n\t * @return a {@link Supplier} that returns the current context (never\n\t * <code>null</code> - create a default implementation if necessary)\n\t * @since 5.8\n\t */\n\tpublic static Supplier<SecurityContext> getDeferredContext() {\n\t\treturn strategy.getDeferredContext();\n\t}\n\n\t/**\n\t * Primarily for troubleshooting purposes, this method shows how many times the class\n\t * has re-initialized its <code>SecurityContextHolderStrategy</code>.\n\t * @return the count (should be one unless you've called\n\t * {@link #setStrategyName(String)} or\n\t * {@link #setContextHolderStrategy(SecurityContextHolderStrategy)} to switch to an\n\t * alternate strategy).\n\t */\n\tpublic static int getInitializeCount() {\n\t\treturn initializeCount;\n\t}\n\n\t/**\n\t * Associates a new <code>SecurityContext</code> with the current thread of execution.\n\t * @param context the new <code>SecurityContext</code> (may not be <code>null</code>)\n\t */\n\tpublic static void setContext(SecurityContext context) {\n\t\tstrategy.setContext(context);\n\t}\n\n\t/**\n\t * Sets a {@link Supplier} that will return the current context. Implementations can\n\t * override the default to avoid invoking {@link Supplier#get()}.\n\t * @param deferredContext a {@link Supplier} that returns the {@link SecurityContext}\n\t * @since 5.8\n\t */\n\tpublic static void setDeferredContext(Supplier<SecurityContext> deferredContext) {\n\t\tstrategy.setDeferredContext(deferredContext);\n\t}\n\n\t/**\n\t * Changes the preferred strategy. Do <em>NOT</em> call this method more than once for\n\t * a given JVM, as it will re-initialize the strategy and adversely affect any\n\t * existing threads using the old strategy.\n\t * @param strategyName the fully qualified class name of the strategy that should be\n\t * used.\n\t */\n\tpublic static void setStrategyName(String strategyName) {\n\t\tSecurityContextHolder.strategyName = strategyName;\n\t\tinitialize();\n\t}\n\n\t/**\n\t * Use this {@link SecurityContextHolderStrategy}.\n\t *\n\t * Call either {@link #setStrategyName(String)} or this method, but not both.\n\t *\n\t * This method is not thread safe. Changing the strategy while requests are in-flight\n\t * may cause race conditions.\n\t *\n\t * {@link SecurityContextHolder} maintains a static reference to the provided\n\t * {@link SecurityContextHolderStrategy}. This means that the strategy and its members\n\t * will not be garbage collected until you remove your strategy.\n\t *\n\t * To ensure garbage collection, remember the original strategy like so:\n\t *\n\t * <pre>\n\t *     SecurityContextHolderStrategy original = SecurityContextHolder.getContextHolderStrategy();\n\t *     SecurityContextHolder.setContextHolderStrategy(myStrategy);\n\t * </pre>\n\t *\n\t * And then when you are ready for {@code myStrategy} to be garbage collected you can\n\t * do:\n\t *\n\t * <pre>\n\t *     SecurityContextHolder.setContextHolderStrategy(original);\n\t * </pre>\n\t * @param strategy the {@link SecurityContextHolderStrategy} to use\n\t * @since 5.6\n\t */\n\tpublic static void setContextHolderStrategy(SecurityContextHolderStrategy strategy) {\n\t\tAssert.notNull(strategy, \"securityContextHolderStrategy cannot be null\");\n\t\tSecurityContextHolder.strategyName = MODE_PRE_INITIALIZED;\n\t\tSecurityContextHolder.strategy = strategy;\n\t\tinitialize();\n\t}\n\n\t/**\n\t * Allows retrieval of the context strategy. See SEC-1188.\n\t * @return the configured strategy for storing the security context.\n\t */\n\tpublic static SecurityContextHolderStrategy getContextHolderStrategy() {\n\t\treturn strategy;\n\t}\n\n\t/**\n\t * Delegates the creation of a new, empty context to the configured strategy.\n\t */\n\tpublic static SecurityContext createEmptyContext() {\n\t\treturn strategy.createEmptyContext();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SecurityContextHolder[strategy='\" + strategy.getClass().getSimpleName() + \"'; initializeCount=\"\n\t\t\t\t+ initializeCount + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/SecurityContextHolderStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\n/**\n * A strategy for storing security context information against a thread.\n *\n * <p>\n * The preferred strategy is loaded by {@link SecurityContextHolder}.\n *\n * @author Ben Alex\n * @author Rob Winch\n */\npublic interface SecurityContextHolderStrategy {\n\n\t/**\n\t * Clears the current context.\n\t */\n\tvoid clearContext();\n\n\t/**\n\t * Obtains the current context.\n\t * @return a context (never <code>null</code> - create a default implementation if\n\t * necessary)\n\t */\n\tSecurityContext getContext();\n\n\t/**\n\t * Obtains a {@link Supplier} that returns the current context.\n\t * @return a {@link Supplier} that returns the current context (never\n\t * <code>null</code> - create a default implementation if necessary)\n\t * @since 5.8\n\t */\n\tdefault Supplier<SecurityContext> getDeferredContext() {\n\t\treturn this::getContext;\n\t}\n\n\t/**\n\t * Sets the current context.\n\t * @param context to the new argument (should never be <code>null</code>, although\n\t * implementations must check if <code>null</code> has been passed and throw an\n\t * <code>IllegalArgumentException</code> in such cases)\n\t */\n\tvoid setContext(SecurityContext context);\n\n\t/**\n\t * Sets a {@link Supplier} that will return the current context. Implementations can\n\t * override the default to avoid invoking {@link Supplier#get()}.\n\t * @param deferredContext a {@link Supplier} that returns the {@link SecurityContext}\n\t * @since 5.8\n\t */\n\tdefault void setDeferredContext(Supplier<SecurityContext> deferredContext) {\n\t\tsetContext(deferredContext.get());\n\t}\n\n\t/**\n\t * Creates a new, empty context implementation, for use by\n\t * <tt>SecurityContextRepository</tt> implementations, when creating a new context for\n\t * the first time.\n\t * @return the empty context.\n\t */\n\tSecurityContext createEmptyContext();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/SecurityContextHolderThreadLocalAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport io.micrometer.context.ThreadLocalAccessor;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ThreadLocalAccessor} for accessing a {@link SecurityContext} with the\n * {@link SecurityContextHolder}.\n * <p>\n * This class adapts the {@link SecurityContextHolder} to the {@link ThreadLocalAccessor}\n * contract to allow Micrometer Context Propagation to automatically propagate a\n * {@link SecurityContext} in Servlet applications. It is automatically registered with\n * the {@link io.micrometer.context.ContextRegistry} through the\n * {@link java.util.ServiceLoader} mechanism when context-propagation is on the classpath.\n *\n * @author Steve Riesenberg\n * @since 6.5\n * @see io.micrometer.context.ContextRegistry\n */\npublic final class SecurityContextHolderThreadLocalAccessor implements ThreadLocalAccessor<SecurityContext> {\n\n\t@Override\n\tpublic Object key() {\n\t\treturn SecurityContext.class.getName();\n\t}\n\n\t@Override\n\tpublic @Nullable SecurityContext getValue() {\n\t\tSecurityContext securityContext = SecurityContextHolder.getContext();\n\t\tSecurityContext emptyContext = SecurityContextHolder.createEmptyContext();\n\n\t\treturn !securityContext.equals(emptyContext) ? securityContext : null;\n\t}\n\n\t@Override\n\tpublic void setValue(SecurityContext securityContext) {\n\t\tAssert.notNull(securityContext, \"securityContext cannot be null\");\n\t\tSecurityContextHolder.setContext(securityContext);\n\t}\n\n\t@Override\n\tpublic void setValue() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/SecurityContextImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * Base implementation of {@link SecurityContext}.\n * <p>\n * Used by default by {@link SecurityContextHolder} strategies.\n *\n * @author Ben Alex\n */\npublic class SecurityContextImpl implements SecurityContext {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate @Nullable Authentication authentication;\n\n\tpublic SecurityContextImpl() {\n\t}\n\n\tpublic SecurityContextImpl(Authentication authentication) {\n\t\tthis.authentication = authentication;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof SecurityContextImpl other) {\n\t\t\tif ((this.getAuthentication() == null) && (other.getAuthentication() == null)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif ((this.getAuthentication() != null) && (other.getAuthentication() != null)\n\t\t\t\t\t&& this.getAuthentication().equals(other.getAuthentication())) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn ObjectUtils.nullSafeHashCode(this.authentication);\n\t}\n\n\t@Override\n\tpublic void setAuthentication(@Nullable Authentication authentication) {\n\t\tthis.authentication = authentication;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(getClass().getSimpleName()).append(\" [\");\n\t\tif (this.authentication == null) {\n\t\t\tsb.append(\"Null authentication\");\n\t\t}\n\t\telse {\n\t\t\tsb.append(\"Authentication=\").append(this.authentication);\n\t\t}\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/ThreadLocalSecurityContextHolderStrategy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\nimport org.springframework.util.Assert;\n\n/**\n * A <code>ThreadLocal</code>-based implementation of\n * {@link SecurityContextHolderStrategy}.\n *\n * @author Ben Alex\n * @author Rob Winch\n * @see java.lang.ThreadLocal\n * @see org.springframework.security.core.context.web.SecurityContextPersistenceFilter\n */\nfinal class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n\tprivate static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new ThreadLocal<>();\n\n\t@Override\n\tpublic void clearContext() {\n\t\tcontextHolder.remove();\n\t}\n\n\t@Override\n\tpublic SecurityContext getContext() {\n\t\treturn getDeferredContext().get();\n\t}\n\n\t@Override\n\tpublic Supplier<SecurityContext> getDeferredContext() {\n\t\tSupplier<SecurityContext> result = contextHolder.get();\n\t\tif (result == null) {\n\t\t\tSecurityContext context = createEmptyContext();\n\t\t\tresult = () -> context;\n\t\t\tcontextHolder.set(result);\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void setContext(SecurityContext context) {\n\t\tAssert.notNull(context, \"Only non-null SecurityContext instances are permitted\");\n\t\tcontextHolder.set(() -> context);\n\t}\n\n\t@Override\n\tpublic void setDeferredContext(Supplier<SecurityContext> deferredContext) {\n\t\tAssert.notNull(deferredContext, \"Only non-null Supplier instances are permitted\");\n\t\tSupplier<SecurityContext> notNullDeferredContext = () -> {\n\t\t\tSecurityContext result = deferredContext.get();\n\t\t\tAssert.notNull(result, \"A Supplier<SecurityContext> returned null and is not allowed.\");\n\t\t\treturn result;\n\t\t};\n\t\tcontextHolder.set(notNullDeferredContext);\n\t}\n\n\t@Override\n\tpublic SecurityContext createEmptyContext() {\n\t\treturn new SecurityContextImpl();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/TransientSecurityContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.Transient;\n\n/**\n * A {@link SecurityContext} that is annotated with @{@link Transient} and thus should\n * never be stored across requests. This is useful in situations where one might run as a\n * different user for part of a request.\n *\n * @author Rob Winch\n * @since 5.7\n */\n@Transient\npublic class TransientSecurityContext extends SecurityContextImpl {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7925492364422193347L;\n\n\tpublic TransientSecurityContext() {\n\t}\n\n\tpublic TransientSecurityContext(Authentication authentication) {\n\t\tsuper(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/context/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes related to the establishment of a security context for the duration of a\n * request (such as an HTTP or RMI invocation).\n * <p>\n * A security context is usually associated with the current execution thread for the\n * duration of the request, making the authentication information it contains available\n * throughout all the layers of an application.\n * <p>\n * The {@link org.springframework.security.core.context.SecurityContext SecurityContext}\n * can be accessed at any point by calling the\n * {@link org.springframework.security.core.context.SecurityContextHolder\n * SecurityContextHolder}.\n */\n@NullMarked\npackage org.springframework.security.core.context;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core classes and interfaces related to user authentication and authorization, as well\n * as the maintenance of a security context.\n */\n@NullMarked\npackage org.springframework.security.core;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/parameters/AnnotationParameterNameDiscoverer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.parameters;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.BridgeMethodResolver;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.core.PrioritizedParameterNameDiscoverer;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ReflectionUtils;\n\n/**\n * Allows finding parameter names using the value attribute of any number of\n * {@link Annotation} instances. This is useful when needing to discover the parameter\n * names of interfaces with Spring Security's method level security. For example, consider\n * the following:\n *\n * <pre>\n * import org.springframework.security.access.method.P;\n *\n * {@code @PostAuthorize(\"#to == returnObject.to\")}\n * public Message findMessageByTo(@P(\"to\") String to);\n * </pre>\n *\n * We can make this possible using the following {@link AnnotationParameterNameDiscoverer}\n * :\n *\n * <pre>\n * ParameterAnnotationsNameDiscoverer discoverer = new ParameterAnnotationsNameDiscoverer(\n * \t\t&quot;org.springframework.security.access.method.P&quot;);\n * </pre>\n *\n * <p>\n * It is common for users to use {@link AnnotationParameterNameDiscoverer} in conjunction\n * with {@link PrioritizedParameterNameDiscoverer}. In fact, Spring Security's\n * {@link DefaultSecurityParameterNameDiscoverer} (which is used by default with method\n * level security) extends {@link PrioritizedParameterNameDiscoverer} and will\n * automatically support both {@link P} and Spring Data's Param annotation if it is found\n * on the classpath.\n * </p>\n *\n * <p>\n * It is important to note that if a single parameter name has a supported annotation on\n * it then all methods are resolved using {@link AnnotationParameterNameDiscoverer}. For\n * example, consider the following:\n * </p>\n *\n * <pre>\n * import org.springframework.security.access.method.P;\n *\n * {@code @PostAuthorize(\"#to == returnObject.to\")}\n * public Message findMessageByToAndFrom(@P(\"to\") User to, User from);\n * </pre>\n *\n * <p>\n * The result of finding parameters on the previous sample will be\n * <code>new String[] { \"to\", null}</code> since only a single parameter contains an\n * annotation. This is mostly due to the fact that the fallbacks for\n * {@link PrioritizedParameterNameDiscoverer} are an all or nothing operation.\n * </p>\n *\n * @author Rob Winch\n * @since 3.2\n * @see DefaultSecurityParameterNameDiscoverer\n */\npublic class AnnotationParameterNameDiscoverer implements ParameterNameDiscoverer {\n\n\tprivate static final ParameterNameFactory<Constructor<?>> CONSTRUCTOR_METHODPARAM_FACTORY = (\n\t\t\tconstructor) -> constructor.getParameterAnnotations();\n\n\tprivate static final ParameterNameFactory<Method> METHOD_METHODPARAM_FACTORY = Method::getParameterAnnotations;\n\n\tprivate final Set<String> annotationClassesToUse;\n\n\tpublic AnnotationParameterNameDiscoverer(String... annotationClassToUse) {\n\t\tthis(new HashSet<>(Arrays.asList(annotationClassToUse)));\n\t}\n\n\tpublic AnnotationParameterNameDiscoverer(Set<String> annotationClassesToUse) {\n\t\tAssert.notEmpty(annotationClassesToUse, \"annotationClassesToUse cannot be null or empty\");\n\t\tthis.annotationClassesToUse = annotationClassesToUse;\n\t}\n\n\t@Override\n\tpublic String @Nullable [] getParameterNames(Method method) {\n\t\tMethod originalMethod = BridgeMethodResolver.findBridgedMethod(method);\n\t\tString[] paramNames = lookupParameterNames(METHOD_METHODPARAM_FACTORY, originalMethod);\n\t\tif (paramNames != null) {\n\t\t\treturn paramNames;\n\t\t}\n\t\tClass<?> declaringClass = method.getDeclaringClass();\n\t\tClass<?>[] interfaces = declaringClass.getInterfaces();\n\t\tfor (Class<?> intrfc : interfaces) {\n\t\t\tMethod intrfcMethod = ReflectionUtils.findMethod(intrfc, method.getName(), method.getParameterTypes());\n\t\t\tif (intrfcMethod != null) {\n\t\t\t\treturn lookupParameterNames(METHOD_METHODPARAM_FACTORY, intrfcMethod);\n\t\t\t}\n\t\t}\n\t\treturn paramNames;\n\t}\n\n\t@Override\n\tpublic String @Nullable [] getParameterNames(Constructor<?> constructor) {\n\t\treturn lookupParameterNames(CONSTRUCTOR_METHODPARAM_FACTORY, constructor);\n\t}\n\n\t/**\n\t * Gets the parameter names or null if not found.\n\t * @param parameterNameFactory the {@link ParameterNameFactory} to use\n\t * @param t the {@link AccessibleObject} to find the parameter names on (i.e. Method\n\t * or Constructor)\n\t * @return the parameter names or null\n\t */\n\tprivate <T extends AccessibleObject> String @Nullable [] lookupParameterNames(\n\t\t\tParameterNameFactory<T> parameterNameFactory, T t) {\n\t\tAnnotation[][] parameterAnnotations = parameterNameFactory.findParameterAnnotations(t);\n\t\tint parameterCount = parameterAnnotations.length;\n\t\tString[] paramNames = new String[parameterCount];\n\t\tboolean found = false;\n\t\tfor (int i = 0; i < parameterCount; i++) {\n\t\t\tAnnotation[] annotations = parameterAnnotations[i];\n\t\t\tString parameterName = findParameterName(annotations);\n\t\t\tif (parameterName != null) {\n\t\t\t\tfound = true;\n\t\t\t\tparamNames[i] = parameterName;\n\t\t\t}\n\t\t}\n\t\treturn found ? paramNames : null;\n\t}\n\n\t/**\n\t * Finds the parameter name from the provided {@link Annotation}s or null if it could\n\t * not find it. The search is done by looking at the value property of the\n\t * {@link #annotationClassesToUse}.\n\t * @param parameterAnnotations the {@link Annotation}'s to search.\n\t * @return\n\t */\n\tprivate @Nullable String findParameterName(Annotation[] parameterAnnotations) {\n\t\tfor (Annotation paramAnnotation : parameterAnnotations) {\n\t\t\tif (this.annotationClassesToUse.contains(paramAnnotation.annotationType().getName())) {\n\t\t\t\treturn (String) AnnotationUtils.getValue(paramAnnotation, \"value\");\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Strategy interface for looking up the parameter names.\n\t *\n\t * @param <T> the type to inspect (i.e. {@link Method} or {@link Constructor})\n\t * @author Rob Winch\n\t * @since 3.2\n\t */\n\t@FunctionalInterface\n\tprivate interface ParameterNameFactory<T extends AccessibleObject> {\n\n\t\t/**\n\t\t * Gets the {@link Annotation}s at a specified index\n\t\t * @param t\n\t\t * @return\n\t\t */\n\t\tAnnotation[][] findParameterAnnotations(T t);\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscoverer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.parameters;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.springframework.core.DefaultParameterNameDiscoverer;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.core.PrioritizedParameterNameDiscoverer;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Spring Security's default {@link ParameterNameDiscoverer} which tries a number of\n * {@link ParameterNameDiscoverer} depending on what is found on the classpath.\n *\n * <ul>\n * <li>Will use an instance of {@link AnnotationParameterNameDiscoverer} with {@link P} as\n * a valid annotation. If, Spring Data is on the classpath will also add Param annotation.\n * </li>\n * </ul>\n *\n * @author Rob Winch\n * @since 3.2\n * @see AnnotationParameterNameDiscoverer\n */\npublic class DefaultSecurityParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {\n\n\tprivate static final String DATA_PARAM_CLASSNAME = \"org.springframework.data.repository.query.Param\";\n\n\tprivate static final boolean DATA_PARAM_PRESENT = ClassUtils.isPresent(DATA_PARAM_CLASSNAME,\n\t\t\tDefaultSecurityParameterNameDiscoverer.class.getClassLoader());\n\n\t/**\n\t * Creates a new instance with only the default {@link ParameterNameDiscoverer}\n\t * instances.\n\t */\n\tpublic DefaultSecurityParameterNameDiscoverer() {\n\t\tthis(Collections.emptyList());\n\t}\n\n\t/**\n\t * Creates a new instance that first tries the passed in\n\t * {@link ParameterNameDiscoverer} instances.\n\t * @param parameterNameDiscovers the {@link ParameterNameDiscoverer} before trying the\n\t * defaults. Cannot be null.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic DefaultSecurityParameterNameDiscoverer(List<? extends ParameterNameDiscoverer> parameterNameDiscovers) {\n\t\tAssert.notNull(parameterNameDiscovers, \"parameterNameDiscovers cannot be null\");\n\t\tfor (ParameterNameDiscoverer discover : parameterNameDiscovers) {\n\t\t\taddDiscoverer(discover);\n\t\t}\n\t\tSet<String> annotationClassesToUse = new HashSet<>(2);\n\t\tannotationClassesToUse.add(\"org.springframework.security.access.method.P\");\n\t\tannotationClassesToUse.add(P.class.getName());\n\t\tif (DATA_PARAM_PRESENT) {\n\t\t\tannotationClassesToUse.add(DATA_PARAM_CLASSNAME);\n\t\t}\n\t\taddDiscoverer(new AnnotationParameterNameDiscoverer(annotationClassesToUse));\n\t\taddDiscoverer(DefaultParameterNameDiscoverer.getSharedInstance());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/parameters/P.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.parameters;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * An annotation that can be used along with {@link AnnotationParameterNameDiscoverer} to\n * specify parameter names. This is useful for interfaces prior to JDK 8 which cannot\n * contain the parameter names.\n *\n * @see AnnotationParameterNameDiscoverer\n * @author Rob Winch\n * @since 5.0\n */\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface P {\n\n\t/**\n\t * The parameter name\n\t * @return\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/parameters/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.core.parameters;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/AbstractSessionEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.io.Serial;\n\nimport org.springframework.context.ApplicationEvent;\n\n/**\n * Abstract superclass for all session related events.\n *\n * @author Eleftheria Stein\n * @since 5.4\n */\npublic class AbstractSessionEvent extends ApplicationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -6878881229287231479L;\n\n\tpublic AbstractSessionEvent(Object source) {\n\t\tsuper(source);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/InMemoryReactiveSessionRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\n/**\n * Provides an in-memory implementation of {@link ReactiveSessionRegistry}.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic class InMemoryReactiveSessionRegistry implements ReactiveSessionRegistry {\n\n\tprivate final ConcurrentMap<Object, Set<String>> sessionIdsByPrincipal;\n\n\tprivate final Map<String, ReactiveSessionInformation> sessionById;\n\n\tpublic InMemoryReactiveSessionRegistry() {\n\t\tthis.sessionIdsByPrincipal = new ConcurrentHashMap<>();\n\t\tthis.sessionById = new ConcurrentHashMap<>();\n\t}\n\n\tpublic InMemoryReactiveSessionRegistry(ConcurrentMap<Object, Set<String>> sessionIdsByPrincipal,\n\t\t\tMap<String, ReactiveSessionInformation> sessionById) {\n\t\tthis.sessionIdsByPrincipal = sessionIdsByPrincipal;\n\t\tthis.sessionById = sessionById;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Flux<ReactiveSessionInformation> getAllSessions(Object principal) {\n\t\treturn Flux.fromIterable(this.sessionIdsByPrincipal.getOrDefault(principal, Collections.emptySet()))\n\t\t\t.mapNotNull(this.sessionById::get);\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveSessionInformation(ReactiveSessionInformation information) {\n\t\tthis.sessionById.put(information.getSessionId(), information);\n\t\tthis.sessionIdsByPrincipal.computeIfAbsent(information.getPrincipal(), (key) -> new CopyOnWriteArraySet<>())\n\t\t\t.add(information.getSessionId());\n\t\treturn Mono.empty();\n\t}\n\n\t@Override\n\tpublic Mono<ReactiveSessionInformation> getSessionInformation(String sessionId) {\n\t\treturn Mono.justOrEmpty(this.sessionById.get(sessionId));\n\t}\n\n\t@Override\n\tpublic Mono<ReactiveSessionInformation> removeSessionInformation(String sessionId) {\n\t\treturn getSessionInformation(sessionId).doOnNext((sessionInformation) -> {\n\t\t\tthis.sessionById.remove(sessionId);\n\t\t\tSet<String> sessionsUsedByPrincipal = this.sessionIdsByPrincipal.get(sessionInformation.getPrincipal());\n\t\t\tif (sessionsUsedByPrincipal != null) {\n\t\t\t\tsessionsUsedByPrincipal.remove(sessionId);\n\t\t\t\tif (sessionsUsedByPrincipal.isEmpty()) {\n\t\t\t\t\tthis.sessionIdsByPrincipal.remove(sessionInformation.getPrincipal());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic Mono<ReactiveSessionInformation> updateLastAccessTime(String sessionId) {\n\t\tReactiveSessionInformation session = this.sessionById.get(sessionId);\n\t\tif (session != null) {\n\t\t\treturn session.refreshLastRequest().thenReturn(session);\n\t\t}\n\t\treturn Mono.empty();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/ReactiveSessionInformation.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.time.Instant;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\n\npublic class ReactiveSessionInformation implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate Instant lastAccessTime;\n\n\tprivate final Object principal;\n\n\tprivate final String sessionId;\n\n\tprivate boolean expired = false;\n\n\tpublic ReactiveSessionInformation(Object principal, String sessionId, Instant lastAccessTime) {\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.hasText(sessionId, \"sessionId cannot be null\");\n\t\tAssert.notNull(lastAccessTime, \"lastAccessTime cannot be null\");\n\t\tthis.principal = principal;\n\t\tthis.sessionId = sessionId;\n\t\tthis.lastAccessTime = lastAccessTime;\n\t}\n\n\tpublic ReactiveSessionInformation withSessionId(String sessionId) {\n\t\treturn new ReactiveSessionInformation(this.principal, sessionId, this.lastAccessTime);\n\t}\n\n\tpublic Mono<Void> invalidate() {\n\t\treturn Mono.fromRunnable(() -> this.expired = true);\n\t}\n\n\tpublic Mono<Void> refreshLastRequest() {\n\t\tthis.lastAccessTime = Instant.now();\n\t\treturn Mono.empty();\n\t}\n\n\tpublic Instant getLastAccessTime() {\n\t\treturn this.lastAccessTime;\n\t}\n\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\tpublic String getSessionId() {\n\t\treturn this.sessionId;\n\t}\n\n\tpublic boolean isExpired() {\n\t\treturn this.expired;\n\t}\n\n\tpublic void setLastAccessTime(Instant lastAccessTime) {\n\t\tthis.lastAccessTime = lastAccessTime;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/ReactiveSessionRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\n/**\n * Maintains a registry of {@link ReactiveSessionInformation} instances.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic interface ReactiveSessionRegistry {\n\n\t/**\n\t * Gets all the known {@link ReactiveSessionInformation} instances for the specified\n\t * principal.\n\t * @param principal the principal\n\t * @return the {@link ReactiveSessionInformation} instances associated with the\n\t * principal\n\t */\n\tFlux<ReactiveSessionInformation> getAllSessions(Object principal);\n\n\t/**\n\t * Saves the {@link ReactiveSessionInformation}\n\t * @param information the {@link ReactiveSessionInformation} to save\n\t * @return a {@link Mono} that completes when the session is saved\n\t */\n\tMono<Void> saveSessionInformation(ReactiveSessionInformation information);\n\n\t/**\n\t * Gets the {@link ReactiveSessionInformation} for the specified session identifier.\n\t * @param sessionId the session identifier\n\t * @return the {@link ReactiveSessionInformation} for the session.\n\t */\n\tMono<ReactiveSessionInformation> getSessionInformation(String sessionId);\n\n\t/**\n\t * Removes the specified session from the registry.\n\t * @param sessionId the session identifier\n\t * @return a {@link Mono} that completes when the session is removed\n\t */\n\tMono<ReactiveSessionInformation> removeSessionInformation(String sessionId);\n\n\t/**\n\t * Updates the last accessed time of the {@link ReactiveSessionInformation}\n\t * @param sessionId the session identifier\n\t * @return a {@link Mono} that completes when the session is updated\n\t */\n\tMono<ReactiveSessionInformation> updateLastAccessTime(String sessionId);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/SessionCreationEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\n/**\n * Generic session creation event which indicates that a session (potentially represented\n * by a security context) has begun.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic abstract class SessionCreationEvent extends AbstractSessionEvent {\n\n\tpublic SessionCreationEvent(Object source) {\n\t\tsuper(source);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/SessionDestroyedEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.util.List;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Generic \"session termination\" event which indicates that a session (potentially\n * represented by a security context) has ended.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic abstract class SessionDestroyedEvent extends AbstractSessionEvent {\n\n\tpublic SessionDestroyedEvent(Object source) {\n\t\tsuper(source);\n\t}\n\n\t/**\n\t * Provides the {@code SecurityContext} instances which were associated with the\n\t * destroyed session. Usually there will be only one security context per session.\n\t * @return the {@code SecurityContext} instances which were stored in the current\n\t * session (an empty list if there are none).\n\t */\n\tpublic abstract List<SecurityContext> getSecurityContexts();\n\n\t/**\n\t * @return the identifier associated with the destroyed session.\n\t */\n\tpublic abstract String getId();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/SessionIdChangedEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\n/**\n * Generic \"session ID changed\" event which indicates that a session identifier\n * (potentially represented by a security context) has changed.\n *\n * @since 5.4\n */\npublic abstract class SessionIdChangedEvent extends AbstractSessionEvent {\n\n\tpublic SessionIdChangedEvent(Object source) {\n\t\tsuper(source);\n\t}\n\n\t/**\n\t * Returns the old session ID.\n\t * @return the identifier that was previously associated with the session.\n\t */\n\tpublic abstract String getOldSessionId();\n\n\t/**\n\t * Returns the new session ID.\n\t * @return the new identifier that is associated with the session.\n\t */\n\tpublic abstract String getNewSessionId();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/SessionInformation.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.springframework.util.Assert;\n\n/**\n * Represents a record of a session within the Spring Security framework.\n * <p>\n * This is primarily used for concurrent session support.\n * <p>\n * Sessions have three states: active, expired, and destroyed. A session can that is\n * invalidated by <code>session.invalidate()</code> or via Servlet Container management is\n * considered \"destroyed\". An \"expired\" session, on the other hand, is a session that\n * Spring Security wants to end because it was selected for removal for some reason\n * (generally as it was the least recently used session and the maximum sessions for the\n * user were reached). An \"expired\" session is removed as soon as possible by a\n * <code>Filter</code>.\n *\n * @author Ben Alex\n */\npublic class SessionInformation implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate Date lastRequest;\n\n\tprivate final Object principal;\n\n\tprivate final String sessionId;\n\n\tprivate boolean expired = false;\n\n\tpublic SessionInformation(Object principal, String sessionId, Date lastRequest) {\n\t\tAssert.notNull(principal, \"Principal required\");\n\t\tAssert.hasText(sessionId, \"SessionId required\");\n\t\tAssert.notNull(lastRequest, \"LastRequest required\");\n\t\tthis.principal = principal;\n\t\tthis.sessionId = sessionId;\n\t\tthis.lastRequest = lastRequest;\n\t}\n\n\tpublic void expireNow() {\n\t\tthis.expired = true;\n\t}\n\n\tpublic Date getLastRequest() {\n\t\treturn this.lastRequest;\n\t}\n\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\tpublic String getSessionId() {\n\t\treturn this.sessionId;\n\t}\n\n\tpublic boolean isExpired() {\n\t\treturn this.expired;\n\t}\n\n\t/**\n\t * Refreshes the internal lastRequest to the current date and time.\n\t */\n\tpublic void refreshLastRequest() {\n\t\tthis.lastRequest = new Date();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/SessionRegistry.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Maintains a registry of <code>SessionInformation</code> instances.\n *\n * @author Ben Alex\n */\npublic interface SessionRegistry {\n\n\t/**\n\t * Obtains all the known principals in the <code>SessionRegistry</code>.\n\t * @return each of the unique principals, which can then be presented to\n\t * {@link #getAllSessions(Object, boolean)}.\n\t */\n\tList<Object> getAllPrincipals();\n\n\t/**\n\t * Obtains all the known sessions for the specified principal. Sessions that have been\n\t * destroyed are not returned. Sessions that have expired may be returned, depending\n\t * on the passed argument.\n\t * @param principal to locate sessions for (should never be <code>null</code>)\n\t * @param includeExpiredSessions if <code>true</code>, the returned sessions will also\n\t * include those that have expired for the principal\n\t * @return the matching sessions for this principal (should not return null).\n\t */\n\tList<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions);\n\n\t/**\n\t * Obtains the session information for the specified <code>sessionId</code>. Even\n\t * expired sessions are returned (although destroyed sessions are never returned).\n\t * @param sessionId to lookup (should never be <code>null</code>)\n\t * @return the session information, or <code>null</code> if not found\n\t */\n\t@Nullable SessionInformation getSessionInformation(String sessionId);\n\n\t/**\n\t * Updates the given <code>sessionId</code> so its last request time is equal to the\n\t * present date and time. Silently returns if the given <code>sessionId</code> cannot\n\t * be found or the session is marked to expire.\n\t * @param sessionId for which to update the date and time of the last request (should\n\t * never be <code>null</code>)\n\t */\n\tvoid refreshLastRequest(String sessionId);\n\n\t/**\n\t * Registers a new session for the specified principal. The newly registered session\n\t * will not be marked for expiration.\n\t * @param sessionId to associate with the principal (should never be <code>null</code>\n\t * )\n\t * @param principal to associate with the session (should never be <code>null</code>)\n\t */\n\tvoid registerNewSession(String sessionId, Object principal);\n\n\t/**\n\t * Deletes all the session information being maintained for the specified\n\t * <code>sessionId</code>. If the <code>sessionId</code> is not found, the method\n\t * gracefully returns.\n\t * @param sessionId to delete information for (should never be <code>null</code>)\n\t */\n\tvoid removeSessionInformation(String sessionId);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\n\n/**\n * Default implementation of\n * {@link org.springframework.security.core.session.SessionRegistry SessionRegistry} which\n * listens for {@link org.springframework.security.core.session.SessionDestroyedEvent\n * SessionDestroyedEvent}s published in the Spring application context.\n * <p>\n * For this class to function correctly in a web application, it is important that you\n * register an <a href=\"\n * {@docRoot}/org/springframework/security/web/session/HttpSessionEventPublisher.html\">HttpSessionEventPublisher</a> in\n * the <tt>web.xml</tt> file so that this class is notified of sessions that expire.\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic class SessionRegistryImpl implements SessionRegistry, ApplicationListener<AbstractSessionEvent> {\n\n\tprotected final Log logger = LogFactory.getLog(SessionRegistryImpl.class);\n\n\t// <principal:Object,SessionIdSet>\n\tprivate final ConcurrentMap<Object, Set<String>> principals;\n\n\t// <sessionId:Object,SessionInformation>\n\tprivate final Map<String, SessionInformation> sessionIds;\n\n\tpublic SessionRegistryImpl() {\n\t\tthis.principals = new ConcurrentHashMap<>();\n\t\tthis.sessionIds = new ConcurrentHashMap<>();\n\t}\n\n\tpublic SessionRegistryImpl(ConcurrentMap<Object, Set<String>> principals,\n\t\t\tMap<String, SessionInformation> sessionIds) {\n\t\tthis.principals = principals;\n\t\tthis.sessionIds = sessionIds;\n\t}\n\n\t@Override\n\tpublic List<Object> getAllPrincipals() {\n\t\treturn new ArrayList<>(this.principals.keySet());\n\t}\n\n\t@Override\n\tpublic List<SessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions) {\n\t\tSet<String> sessionsUsedByPrincipal = this.principals.get(principal);\n\t\tif (sessionsUsedByPrincipal == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<SessionInformation> list = new ArrayList<>(sessionsUsedByPrincipal.size());\n\t\tfor (String sessionId : sessionsUsedByPrincipal) {\n\t\t\tSessionInformation sessionInformation = getSessionInformation(sessionId);\n\t\t\tif (sessionInformation == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (includeExpiredSessions || !sessionInformation.isExpired()) {\n\t\t\t\tlist.add(sessionInformation);\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic @Nullable SessionInformation getSessionInformation(String sessionId) {\n\t\tAssert.hasText(sessionId, \"SessionId required as per interface contract\");\n\t\treturn this.sessionIds.get(sessionId);\n\t}\n\n\t@Override\n\tpublic void onApplicationEvent(AbstractSessionEvent event) {\n\t\tif (event instanceof SessionDestroyedEvent sessionDestroyedEvent) {\n\t\t\tString sessionId = sessionDestroyedEvent.getId();\n\t\t\tremoveSessionInformation(sessionId);\n\t\t}\n\t\telse if (event instanceof SessionIdChangedEvent sessionIdChangedEvent) {\n\t\t\tString oldSessionId = sessionIdChangedEvent.getOldSessionId();\n\t\t\tif (this.sessionIds.containsKey(oldSessionId)) {\n\t\t\t\tObject principal = this.sessionIds.get(oldSessionId).getPrincipal();\n\t\t\t\tremoveSessionInformation(oldSessionId);\n\t\t\t\tregisterNewSession(sessionIdChangedEvent.getNewSessionId(), principal);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void refreshLastRequest(String sessionId) {\n\t\tAssert.hasText(sessionId, \"SessionId required as per interface contract\");\n\t\tSessionInformation info = getSessionInformation(sessionId);\n\t\tif (info != null) {\n\t\t\tinfo.refreshLastRequest();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void registerNewSession(String sessionId, Object principal) {\n\t\tAssert.hasText(sessionId, \"SessionId required as per interface contract\");\n\t\tAssert.notNull(principal, \"Principal required as per interface contract\");\n\t\tif (getSessionInformation(sessionId) != null) {\n\t\t\tremoveSessionInformation(sessionId);\n\t\t}\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Registering session %s, for principal %s\", sessionId, principal));\n\t\t}\n\t\tthis.sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));\n\t\tthis.principals.compute(principal, (key, sessionsUsedByPrincipal) -> {\n\t\t\tif (sessionsUsedByPrincipal == null) {\n\t\t\t\tsessionsUsedByPrincipal = new CopyOnWriteArraySet<>();\n\t\t\t}\n\t\t\tsessionsUsedByPrincipal.add(sessionId);\n\t\t\tthis.logger.trace(LogMessage.format(\"Sessions used by '%s' : %s\", principal, sessionsUsedByPrincipal));\n\t\t\treturn sessionsUsedByPrincipal;\n\t\t});\n\t}\n\n\t@Override\n\tpublic void removeSessionInformation(String sessionId) {\n\t\tAssert.hasText(sessionId, \"SessionId required as per interface contract\");\n\t\tSessionInformation info = getSessionInformation(sessionId);\n\t\tif (info == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.debug(\"Removing session \" + sessionId + \" from set of registered sessions\");\n\t\t}\n\t\tthis.sessionIds.remove(sessionId);\n\t\tthis.principals.computeIfPresent(info.getPrincipal(), (key, sessionsUsedByPrincipal) -> {\n\t\t\tthis.logger\n\t\t\t\t.debug(LogMessage.format(\"Removing session %s from principal's set of registered sessions\", sessionId));\n\t\t\tsessionsUsedByPrincipal.remove(sessionId);\n\t\t\tif (sessionsUsedByPrincipal.isEmpty()) {\n\t\t\t\t// No need to keep object in principals Map anymore\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Removing principal %s from registry\", info.getPrincipal()));\n\t\t\t\tsessionsUsedByPrincipal = null;\n\t\t\t}\n\t\t\tthis.logger\n\t\t\t\t.trace(LogMessage.format(\"Sessions used by '%s' : %s\", info.getPrincipal(), sessionsUsedByPrincipal));\n\t\t\treturn sessionsUsedByPrincipal;\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/session/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Session abstraction which is provided by the\n * {@code org.springframework.security.core.session.SessionInformation\n * SessionInformation} class. The\n * {@link org.springframework.security.core.session.SessionRegistry SessionRegistry} is a\n * core part of the web-based concurrent session control, but the code is not dependent on\n * any of the servlet APIs.\n */\n@NullMarked\npackage org.springframework.security.core.session;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/token/DefaultToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\nimport java.util.Date;\n\nimport org.springframework.util.Assert;\n\n/**\n * The default implementation of {@link Token}.\n *\n * @author Ben Alex\n * @since 2.0.1\n */\npublic class DefaultToken implements Token {\n\n\tprivate final String key;\n\n\tprivate final long keyCreationTime;\n\n\tprivate final String extendedInformation;\n\n\tpublic DefaultToken(String key, long keyCreationTime, String extendedInformation) {\n\t\tAssert.hasText(key, \"Key required\");\n\t\tAssert.notNull(extendedInformation, \"Extended information cannot be null\");\n\t\tthis.key = key;\n\t\tthis.keyCreationTime = keyCreationTime;\n\t\tthis.extendedInformation = extendedInformation;\n\t}\n\n\t@Override\n\tpublic String getKey() {\n\t\treturn this.key;\n\t}\n\n\t@Override\n\tpublic long getKeyCreationTime() {\n\t\treturn this.keyCreationTime;\n\t}\n\n\t@Override\n\tpublic String getExtendedInformation() {\n\t\treturn this.extendedInformation;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof DefaultToken rhs) {\n\t\t\treturn this.key.equals(rhs.key) && this.keyCreationTime == rhs.keyCreationTime\n\t\t\t\t\t&& this.extendedInformation.equals(rhs.extendedInformation);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint code = 979;\n\t\tcode = code * this.key.hashCode();\n\t\tcode = code * Long.valueOf(this.keyCreationTime).hashCode();\n\t\tcode = code * this.extendedInformation.hashCode();\n\t\treturn code;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DefaultToken[key=\" + this.key + \"; creation=\" + new Date(this.keyCreationTime) + \"; extended=\"\n\t\t\t\t+ this.extendedInformation + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/token/KeyBasedPersistenceTokenService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\nimport java.security.SecureRandom;\nimport java.util.Base64;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Basic implementation of {@link TokenService} that is compatible with clusters and\n * across machine restarts, without requiring database persistence.\n *\n * <p>\n * Keys are produced in the format:\n * </p>\n *\n * <p>\n * Base64(creationTime + \":\" + hex(pseudoRandomNumber) + \":\" + extendedInformation + \":\" +\n * Sha512Hex(creationTime + \":\" + hex(pseudoRandomNumber) + \":\" + extendedInformation +\n * \":\" + serverSecret) )\n * </p>\n *\n * <p>\n * In the above, <code>creationTime</code>, <code>tokenKey</code> and\n * <code>extendedInformation</code> are equal to that stored in {@link Token}. The\n * <code>Sha512Hex</code> includes the same payload, plus a <code>serverSecret</code>.\n * </p>\n *\n * <p>\n * The <code>serverSecret</code> varies every millisecond. It relies on two static\n * server-side secrets. The first is a password, and the second is a server integer. Both\n * of these must remain the same for any issued keys to subsequently be recognised. The\n * applicable <code>serverSecret</code> in any millisecond is computed by\n * <code>password</code> + \":\" + (<code>creationTime</code> % <code>serverInteger</code>).\n * This approach further obfuscates the actual server secret and renders attempts to\n * compute the server secret more limited in usefulness (as any false tokens would be\n * forced to have a <code>creationTime</code> equal to the computed hash). Recall that\n * framework features depending on token services should reject tokens that are relatively\n * old in any event.\n * </p>\n *\n * <p>\n * A further consideration of this class is the requirement for cryptographically strong\n * pseudo-random numbers. To this end, the use of {@link SecureRandomFactoryBean} is\n * recommended to inject the property.\n * </p>\n *\n * <p>\n * This implementation uses UTF-8 encoding internally for string manipulation.\n * </p>\n *\n * @author Ben Alex\n *\n */\npublic class KeyBasedPersistenceTokenService implements TokenService, InitializingBean {\n\n\tprivate int pseudoRandomNumberBytes = 32;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate String serverSecret;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate Integer serverInteger;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate SecureRandom secureRandom;\n\n\t@Override\n\tpublic Token allocateToken(String extendedInformation) {\n\t\tAssert.notNull(extendedInformation, \"Must provided non-null extendedInformation (but it can be empty)\");\n\t\tlong creationTime = System.currentTimeMillis();\n\t\tString serverSecret = computeServerSecretApplicableAt(creationTime);\n\t\tString pseudoRandomNumber = generatePseudoRandomNumber();\n\t\tString content = creationTime + \":\" + pseudoRandomNumber + \":\" + extendedInformation;\n\t\tString key = computeKey(serverSecret, content);\n\t\treturn new DefaultToken(key, creationTime, extendedInformation);\n\t}\n\n\tprivate String computeKey(String serverSecret, String content) {\n\t\tString sha512Hex = Sha512DigestUtils.shaHex(content + \":\" + serverSecret);\n\t\tString keyPayload = content + \":\" + sha512Hex;\n\t\treturn Utf8.decode(Base64.getEncoder().encode(Utf8.encode(keyPayload)));\n\t}\n\n\t@Override\n\tpublic @Nullable Token verifyToken(String key) {\n\t\tif (key == null || \"\".equals(key)) {\n\t\t\treturn null;\n\t\t}\n\t\tString[] tokens = StringUtils\n\t\t\t.delimitedListToStringArray(Utf8.decode(Base64.getDecoder().decode(Utf8.encode(key))), \":\");\n\t\tAssert.isTrue(tokens.length >= 4, () -> \"Expected 4 or more tokens but found \" + tokens.length);\n\t\tlong creationTime;\n\t\ttry {\n\t\t\tcreationTime = Long.decode(tokens[0]);\n\t\t}\n\t\tcatch (NumberFormatException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Expected number but found \" + tokens[0]);\n\t\t}\n\t\tString serverSecret = computeServerSecretApplicableAt(creationTime);\n\t\tString pseudoRandomNumber = tokens[1];\n\t\t// Permit extendedInfo to itself contain \":\" characters\n\t\tStringBuilder extendedInfo = new StringBuilder();\n\t\tfor (int i = 2; i < tokens.length - 1; i++) {\n\t\t\tif (i > 2) {\n\t\t\t\textendedInfo.append(\":\");\n\t\t\t}\n\t\t\textendedInfo.append(tokens[i]);\n\t\t}\n\t\tString sha1Hex = tokens[tokens.length - 1];\n\t\t// Verification\n\t\tString content = creationTime + \":\" + pseudoRandomNumber + \":\" + extendedInfo.toString();\n\t\tString expectedSha512Hex = Sha512DigestUtils.shaHex(content + \":\" + serverSecret);\n\t\tAssert.isTrue(expectedSha512Hex.equals(sha1Hex), \"Key verification failure\");\n\t\treturn new DefaultToken(key, creationTime, extendedInfo.toString());\n\t}\n\n\t/**\n\t * @return a pseudo random number (hex encoded)\n\t */\n\tprivate String generatePseudoRandomNumber() {\n\t\tbyte[] randomBytes = new byte[this.pseudoRandomNumberBytes];\n\t\tthis.secureRandom.nextBytes(randomBytes);\n\t\treturn new String(Hex.encode(randomBytes));\n\t}\n\n\tprivate String computeServerSecretApplicableAt(long time) {\n\t\treturn this.serverSecret + \":\" + Long.valueOf(time % this.serverInteger).intValue();\n\t}\n\n\t/**\n\t * @param serverSecret the new secret, which can contain a \":\" if desired (never being\n\t * sent to the client)\n\t */\n\tpublic void setServerSecret(String serverSecret) {\n\t\tthis.serverSecret = serverSecret;\n\t}\n\n\tpublic void setSecureRandom(SecureRandom secureRandom) {\n\t\tthis.secureRandom = secureRandom;\n\t}\n\n\t/**\n\t * @param pseudoRandomNumberBytes changes the number of bytes issued (must be &gt;= 0;\n\t * defaults to 256)\n\t */\n\tpublic void setPseudoRandomNumberBytes(int pseudoRandomNumberBytes) {\n\t\tAssert.isTrue(pseudoRandomNumberBytes >= 0, \"Must have a positive pseudo random number bit size\");\n\t\tthis.pseudoRandomNumberBytes = pseudoRandomNumberBytes;\n\t}\n\n\tpublic void setServerInteger(Integer serverInteger) {\n\t\tthis.serverInteger = serverInteger;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasText(this.serverSecret, \"Server secret required\");\n\t\tAssert.notNull(this.serverInteger, \"Server integer required\");\n\t\tAssert.notNull(this.secureRandom, \"SecureRandom instance required\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/token/SecureRandomFactoryBean.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\nimport java.io.InputStream;\nimport java.security.SecureRandom;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.core.io.Resource;\nimport org.springframework.util.Assert;\nimport org.springframework.util.FileCopyUtils;\n\n/**\n * Creates a {@link SecureRandom} instance.\n *\n * @author Ben Alex\n * @since 2.0.1\n */\npublic class SecureRandomFactoryBean implements FactoryBean<SecureRandom> {\n\n\tprivate String algorithm = \"SHA1PRNG\";\n\n\tprivate @Nullable Resource seed;\n\n\t@Override\n\tpublic SecureRandom getObject() throws Exception {\n\t\tSecureRandom random = SecureRandom.getInstance(this.algorithm);\n\t\t// Request the next bytes, thus eagerly incurring the expense of default\n\t\t// seeding and to prevent the see from replacing the entire state\n\t\trandom.nextBytes(new byte[1]);\n\t\tif (this.seed != null) {\n\t\t\t// Seed specified, so use it\n\t\t\tbyte[] seedBytes = FileCopyUtils.copyToByteArray(this.seed.getInputStream());\n\t\t\trandom.setSeed(seedBytes);\n\t\t}\n\t\treturn random;\n\t}\n\n\t@Override\n\tpublic Class<SecureRandom> getObjectType() {\n\t\treturn SecureRandom.class;\n\t}\n\n\t@Override\n\tpublic boolean isSingleton() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Allows the Pseudo Random Number Generator (PRNG) algorithm to be nominated.\n\t * Defaults to \"SHA1PRNG\".\n\t * @param algorithm to use (mandatory)\n\t */\n\tpublic void setAlgorithm(String algorithm) {\n\t\tAssert.hasText(algorithm, \"Algorithm required\");\n\t\tthis.algorithm = algorithm;\n\t}\n\n\t/**\n\t * Allows the user to specify a resource which will act as a seed for the\n\t * {@link SecureRandom} instance. Specifically, the resource will be read into an\n\t * {@link InputStream} and those bytes presented to the\n\t * {@link SecureRandom#setSeed(byte[])} method. Note that this will simply supplement,\n\t * rather than replace, the existing seed. As such, it is always safe to set a seed\n\t * using this method (it never reduces randomness).\n\t * @param seed to use, or <code>null</code> if no additional seeding is needed\n\t */\n\tpublic void setSeed(Resource seed) {\n\t\tthis.seed = seed;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/token/Sha512DigestUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\nimport org.springframework.security.crypto.codec.Hex;\n\n/**\n * Provides SHA512 digest methods.\n *\n * <p>\n * Based on Commons Codec, which does not presently provide SHA512 support.\n * </p>\n *\n * @author Ben Alex\n * @since 2.0.1\n *\n */\npublic abstract class Sha512DigestUtils {\n\n\t/**\n\t * Returns an SHA digest.\n\t * @return An SHA digest instance.\n\t * @throws RuntimeException when a {@link java.security.NoSuchAlgorithmException} is\n\t * caught.\n\t */\n\tprivate static MessageDigest getSha512Digest() {\n\t\ttry {\n\t\t\treturn MessageDigest.getInstance(\"SHA-512\");\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new RuntimeException(ex.getMessage());\n\t\t}\n\t}\n\n\t/**\n\t * Calculates the SHA digest and returns the value as a <code>byte[]</code>.\n\t * @param data Data to digest\n\t * @return SHA digest\n\t */\n\tpublic static byte[] sha(byte[] data) {\n\t\treturn getSha512Digest().digest(data);\n\t}\n\n\t/**\n\t * Calculates the SHA digest and returns the value as a <code>byte[]</code>.\n\t * @param data Data to digest\n\t * @return SHA digest\n\t */\n\tpublic static byte[] sha(String data) {\n\t\treturn sha(data.getBytes());\n\t}\n\n\t/**\n\t * Calculates the SHA digest and returns the value as a hex string.\n\t * @param data Data to digest\n\t * @return SHA digest as a hex string\n\t */\n\tpublic static String shaHex(byte[] data) {\n\t\treturn new String(Hex.encode(sha(data)));\n\t}\n\n\t/**\n\t * Calculates the SHA digest and returns the value as a hex string.\n\t * @param data Data to digest\n\t * @return SHA digest as a hex string\n\t */\n\tpublic static String shaHex(String data) {\n\t\treturn new String(Hex.encode(sha(data)));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/token/Token.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\n/**\n * A token issued by {@link TokenService}.\n *\n * <p>\n * It is important that the keys assigned to tokens are sufficiently randomised and\n * secured that they can serve as identifying a unique user session. Implementations of\n * {@link TokenService} are free to use encryption or encoding strategies of their choice.\n * It is strongly recommended that keys are of sufficient length to balance safety against\n * persistence cost. In relation to persistence cost, it is strongly recommended that\n * returned keys are small enough for encoding in a cookie.\n * </p>\n *\n * @author Ben Alex\n * @since 2.0.1\n */\npublic interface Token {\n\n\t/**\n\t * Obtains the randomised, secure key assigned to this token. Presentation of this\n\t * token to {@link TokenService} will always return a <code>Token</code> that is equal\n\t * to the original <code>Token</code> issued for that key.\n\t * @return a key with appropriate randomness and security.\n\t */\n\tString getKey();\n\n\t/**\n\t * The time the token key was initially created is available from this method. Note\n\t * that a given token must never have this creation time changed. If necessary, a new\n\t * token can be requested from the {@link TokenService} to replace the original token.\n\t * @return the time this token key was created, in the same format as specified by\n\t * {@link java.util.Date#getTime()}.\n\t */\n\tlong getKeyCreationTime();\n\n\t/**\n\t * Obtains the extended information associated within the token, which was presented\n\t * when the token was first created.\n\t * @return the user-specified extended information, if any\n\t */\n\tString getExtendedInformation();\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/token/TokenService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Provides a mechanism to allocate and rebuild secure, randomised tokens.\n *\n * <p>\n * Implementations are solely concern with issuing a new {@link Token} on demand. The\n * issued <code>Token</code> may contain user-specified extended information. The token\n * also contains a cryptographically strong, byte array-based key. This permits the token\n * to be used to identify a user session, if desired. The key can subsequently be\n * re-presented to the <code>TokenService</code> for verification and reconstruction of a\n * <code>Token</code> equal to the original <code>Token</code>.\n * </p>\n *\n * <p>\n * Given the tightly-focused behaviour provided by this interface, it can serve as a\n * building block for more sophisticated token-based solutions. For example,\n * authentication systems that depend on stateless session keys. These could, for\n * instance, place the username inside the user-specified extended information associated\n * with the key). It is important to recognise that we do not intend for this interface to\n * be expanded to provide such capabilities directly.\n * </p>\n *\n * @author Ben Alex\n * @since 2.0.1\n *\n */\npublic interface TokenService {\n\n\t/**\n\t * Forces the allocation of a new {@link Token}.\n\t * @param extendedInformation the extended information desired in the token (cannot be\n\t * <code>null</code>, but can be empty)\n\t * @return a new token that has not been issued previously, and is guaranteed to be\n\t * recognised by this implementation's {@link #verifyToken(String)} at any future\n\t * time.\n\t */\n\tToken allocateToken(String extendedInformation);\n\n\t/**\n\t * Permits verification the {@link Token#getKey()} was issued by this\n\t * <code>TokenService</code> and reconstructs the corresponding <code>Token</code>.\n\t * @param key as obtained from {@link Token#getKey()} and created by this\n\t * implementation\n\t * @return the token, or <code>null</code> if the token was not issued by this\n\t * <code>TokenService</code>\n\t */\n\t@Nullable Token verifyToken(String key);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/token/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A service for building secure random tokens.\n */\n@NullMarked\npackage org.springframework.security.core.token;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/AuthenticationUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Interface that allows for retrieving a UserDetails object based on an\n * <tt>Authentication</tt> object.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic interface AuthenticationUserDetailsService<T extends Authentication> {\n\n\t/**\n\t * @param token The pre-authenticated authentication token\n\t * @return UserDetails for the given authentication token, never null.\n\t * @throws UsernameNotFoundException if no user details can be found for the given\n\t * authentication token\n\t */\n\tUserDetails loadUserDetails(T token) throws UsernameNotFoundException;\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Map} based implementation of {@link ReactiveUserDetailsService}\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class MapReactiveUserDetailsService implements ReactiveUserDetailsService, ReactiveUserDetailsPasswordService {\n\n\tprivate final Map<String, UserDetails> users;\n\n\t/**\n\t * Creates a new instance using a {@link Map} that must be non blocking.\n\t * @param users a {@link Map} of users to use.\n\t */\n\tpublic MapReactiveUserDetailsService(Map<String, UserDetails> users) {\n\t\tthis.users = users;\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param users the {@link UserDetails} to use\n\t */\n\tpublic MapReactiveUserDetailsService(UserDetails... users) {\n\t\tthis(Arrays.asList(users));\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param users the {@link UserDetails} to use\n\t */\n\tpublic MapReactiveUserDetailsService(Collection<UserDetails> users) {\n\t\tAssert.notEmpty(users, \"users cannot be null or empty\");\n\t\tthis.users = new ConcurrentHashMap<>();\n\t\tfor (UserDetails user : users) {\n\t\t\tthis.users.put(getKey(user.getUsername()), user);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Mono<UserDetails> findByUsername(String username) {\n\t\tString key = getKey(username);\n\t\tUserDetails result = this.users.get(key);\n\t\treturn (result != null) ? Mono.just(User.withUserDetails(result).build()) : Mono.empty();\n\t}\n\n\t@Override\n\tpublic Mono<UserDetails> updatePassword(UserDetails user, @Nullable String newPassword) {\n\t\t// @formatter:off\n\t\treturn Mono.just(user)\n\t\t\t\t.map((userDetails) -> withNewPassword(userDetails, newPassword))\n\t\t\t\t.doOnNext((userDetails) -> {\n\t\t\t\t\tString key = getKey(user.getUsername());\n\t\t\t\t\tthis.users.put(key, userDetails);\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate UserDetails withNewPassword(UserDetails userDetails, @Nullable String newPassword) {\n\t\t// @formatter:off\n\t\treturn User.withUserDetails(userDetails)\n\t\t\t\t.password(newPassword)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate String getKey(String username) {\n\t\treturn username.toLowerCase(Locale.ROOT);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/ReactiveUserDetailsPasswordService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\n/**\n * An API for changing a {@link UserDetails} password.\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic interface ReactiveUserDetailsPasswordService {\n\n\tReactiveUserDetailsPasswordService NOOP = (user, newPassword) -> Mono.just(user);\n\n\t/**\n\t * Modify the specified user's password. This should change the user's password in the\n\t * persistent user repository (database, LDAP etc).\n\t * @param user the user to modify the password for\n\t * @param newPassword the password to change to\n\t * @return the updated UserDetails with the new password\n\t */\n\tMono<UserDetails> updatePassword(UserDetails user, @Nullable String newPassword);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/ReactiveUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport reactor.core.publisher.Mono;\n\n/**\n * An API for finding the {@link UserDetails} by username.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic interface ReactiveUserDetailsService {\n\n\t/**\n\t * Find the {@link UserDetails} by username.\n\t * @param username the username to look up\n\t * @return the {@link UserDetails}. Cannot be null\n\t */\n\tMono<UserDetails> findByUsername(String username);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/User.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.function.Function;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.CredentialsContainer;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.util.Assert;\n\n/**\n * Models core user information retrieved by a {@link UserDetailsService}.\n * <p>\n * Developers may use this class directly, subclass it, or write their own\n * {@link UserDetails} implementation from scratch.\n * <p>\n * {@code equals} and {@code hashcode} implementations are based on the {@code username}\n * property only, as the intention is that lookups of the same user principal object (in a\n * user registry, for example) will match where the objects represent the same user, not\n * just when all the properties (authorities, password for example) are the same.\n * <p>\n * Note that this implementation is not immutable. It implements the\n * {@code CredentialsContainer} interface, in order to allow the password to be erased\n * after authentication. This may cause side-effects if you are storing instances\n * in-memory and reusing them. If so, make sure you return a copy from your\n * {@code UserDetailsService} each time it is invoked.\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic class User implements UserDetails, CredentialsContainer {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate static final Log logger = LogFactory.getLog(User.class);\n\n\tprivate @Nullable String password;\n\n\tprivate final String username;\n\n\tprivate final Set<GrantedAuthority> authorities;\n\n\tprivate final boolean accountNonExpired;\n\n\tprivate final boolean accountNonLocked;\n\n\tprivate final boolean credentialsNonExpired;\n\n\tprivate final boolean enabled;\n\n\t/**\n\t * Calls the more complex constructor with all boolean arguments set to {@code true}.\n\t */\n\tpublic User(String username, @Nullable String password, Collection<? extends GrantedAuthority> authorities) {\n\t\tthis(username, password, true, true, true, true, authorities);\n\t}\n\n\t/**\n\t * Construct the <code>User</code> with the details required by\n\t * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider}.\n\t * @param username the username presented to the\n\t * <code>DaoAuthenticationProvider</code>\n\t * @param password the password that should be presented to the\n\t * <code>DaoAuthenticationProvider</code>\n\t * @param enabled set to <code>true</code> if the user is enabled\n\t * @param accountNonExpired set to <code>true</code> if the account has not expired\n\t * @param credentialsNonExpired set to <code>true</code> if the credentials have not\n\t * expired\n\t * @param accountNonLocked set to <code>true</code> if the account is not locked\n\t * @param authorities the authorities that should be granted to the caller if they\n\t * presented the correct username and password and the user is enabled. Not null.\n\t * @throws IllegalArgumentException if a <code>null</code> value was passed either as\n\t * a parameter or as an element in the <code>GrantedAuthority</code> collection\n\t */\n\tpublic User(String username, @Nullable String password, boolean enabled, boolean accountNonExpired,\n\t\t\tboolean credentialsNonExpired, boolean accountNonLocked,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tAssert.isTrue(username != null && !\"\".equals(username), \"Cannot pass null or empty values to constructor\");\n\t\tthis.username = username;\n\t\tthis.password = password;\n\t\tthis.enabled = enabled;\n\t\tthis.accountNonExpired = accountNonExpired;\n\t\tthis.credentialsNonExpired = credentialsNonExpired;\n\t\tthis.accountNonLocked = accountNonLocked;\n\t\tthis.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));\n\t}\n\n\t@Override\n\tpublic Collection<GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic @Nullable String getPassword() {\n\t\treturn this.password;\n\t}\n\n\t@Override\n\tpublic String getUsername() {\n\t\treturn this.username;\n\t}\n\n\t@Override\n\tpublic boolean isEnabled() {\n\t\treturn this.enabled;\n\t}\n\n\t@Override\n\tpublic boolean isAccountNonExpired() {\n\t\treturn this.accountNonExpired;\n\t}\n\n\t@Override\n\tpublic boolean isAccountNonLocked() {\n\t\treturn this.accountNonLocked;\n\t}\n\n\t@Override\n\tpublic boolean isCredentialsNonExpired() {\n\t\treturn this.credentialsNonExpired;\n\t}\n\n\t@Override\n\tpublic void eraseCredentials() {\n\t\tthis.password = null;\n\t}\n\n\tprivate static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {\n\t\tAssert.notNull(authorities, \"Cannot pass a null GrantedAuthority collection\");\n\t\t// Ensure array iteration order is predictable (as per\n\t\t// UserDetails.getAuthorities() contract and SEC-717)\n\t\tSortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<>(new AuthorityComparator());\n\t\tfor (GrantedAuthority grantedAuthority : authorities) {\n\t\t\tAssert.notNull(grantedAuthority, \"GrantedAuthority list cannot contain any null elements\");\n\t\t\tsortedAuthorities.add(grantedAuthority);\n\t\t}\n\t\treturn sortedAuthorities;\n\t}\n\n\t/**\n\t * Returns {@code true} if the supplied object is a {@code User} instance with the\n\t * same {@code username} value.\n\t * <p>\n\t * In other words, the objects are equal if they have the same username, representing\n\t * the same principal.\n\t */\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof User user) {\n\t\t\treturn this.username.equals(user.getUsername());\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns the hashcode of the {@code username}.\n\t */\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.username.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(getClass().getName()).append(\" [\");\n\t\tsb.append(\"Username=\").append(this.username).append(\", \");\n\t\tsb.append(\"Password=[PROTECTED], \");\n\t\tsb.append(\"Enabled=\").append(this.enabled).append(\", \");\n\t\tsb.append(\"AccountNonExpired=\").append(this.accountNonExpired).append(\", \");\n\t\tsb.append(\"CredentialsNonExpired=\").append(this.credentialsNonExpired).append(\", \");\n\t\tsb.append(\"AccountNonLocked=\").append(this.accountNonLocked).append(\", \");\n\t\tsb.append(\"Granted Authorities=\").append(this.authorities).append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * Creates a UserBuilder with a specified username\n\t * @param username the username to use\n\t * @return the UserBuilder\n\t */\n\tpublic static UserBuilder withUsername(String username) {\n\t\treturn builder().username(username);\n\t}\n\n\t/**\n\t * Creates a UserBuilder\n\t * @return the UserBuilder\n\t */\n\tpublic static UserBuilder builder() {\n\t\treturn new UserBuilder();\n\t}\n\n\t/**\n\t * <p>\n\t * <b>WARNING:</b> This method is considered unsafe for production and is only\n\t * intended for sample applications.\n\t * </p>\n\t * <p>\n\t * Creates a user and automatically encodes the provided password using\n\t * {@code PasswordEncoderFactories.createDelegatingPasswordEncoder()}. For example:\n\t * </p>\n\t *\n\t * <pre>\n\t * <code>\n\t * UserDetails user = User.withDefaultPasswordEncoder()\n\t *     .username(\"user\")\n\t *     .password(\"password\")\n\t *     .roles(\"USER\")\n\t *     .build();\n\t * // outputs {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n\t * System.out.println(user.getPassword());\n\t * </code> </pre>\n\t *\n\t * This is not safe for production (it is intended for getting started experience)\n\t * because the password \"password\" is compiled into the source code and then is\n\t * included in memory at the time of creation. This means there are still ways to\n\t * recover the plain text password making it unsafe. It does provide a slight\n\t * improvement to using plain text passwords since the UserDetails password is\n\t * securely hashed. This means if the UserDetails password is accidentally exposed,\n\t * the password is securely stored.\n\t *\n\t * In a production setting, it is recommended to hash the password ahead of time. For\n\t * example:\n\t *\n\t * <pre>\n\t * <code>\n\t * PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t * // outputs {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n\t * // remember the password that is printed out and use in the next step\n\t * System.out.println(encoder.encode(\"password\"));\n\t * </code> </pre>\n\t *\n\t * <pre>\n\t * <code>\n\t * UserDetails user = User.withUsername(\"user\")\n\t *     .password(\"{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\")\n\t *     .roles(\"USER\")\n\t *     .build();\n\t * </code> </pre>\n\t * @return a UserBuilder that automatically encodes the password with the default\n\t * PasswordEncoder\n\t * @deprecated Using this method is not considered safe for production, but is\n\t * acceptable for demos and getting started. For production purposes, ensure the\n\t * password is encoded externally. See the method Javadoc for additional details.\n\t * There are no plans to remove this support. It is deprecated to indicate that this\n\t * is considered insecure for production purposes.\n\t */\n\t@Deprecated\n\tpublic static UserBuilder withDefaultPasswordEncoder() {\n\t\tlogger.warn(\"User.withDefaultPasswordEncoder() is considered unsafe for production \"\n\t\t\t\t+ \"and is only intended for sample applications.\");\n\t\tPasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t\treturn builder().passwordEncoder(encoder::encode);\n\t}\n\n\tpublic static UserBuilder withUserDetails(UserDetails userDetails) {\n\t\t// @formatter:off\n\t\tUserBuilder result = withUsername(userDetails.getUsername())\n\t\t\t\t.accountExpired(!userDetails.isAccountNonExpired())\n\t\t\t\t.accountLocked(!userDetails.isAccountNonLocked())\n\t\t\t\t.authorities(userDetails.getAuthorities())\n\t\t\t\t.credentialsExpired(!userDetails.isCredentialsNonExpired())\n\t\t\t\t.disabled(!userDetails.isEnabled());\n\t\t// @formatter:on\n\t\tif (userDetails.getPassword() != null) {\n\t\t\tresult.password(userDetails.getPassword());\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {\n\n\t\tprivate static final long serialVersionUID = 620L;\n\n\t\t@Override\n\t\tpublic int compare(GrantedAuthority g1, GrantedAuthority g2) {\n\t\t\t// Neither should ever be null as each entry is checked before adding it to\n\t\t\t// the set. If the authority is null, it is a custom authority and should\n\t\t\t// precede others.\n\t\t\tif (g2.getAuthority() == null) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (g1.getAuthority() == null) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn g1.getAuthority().compareTo(g2.getAuthority());\n\t\t}\n\n\t}\n\n\t/**\n\t * Builds the user to be added. At minimum the username, password, and authorities\n\t * should provided. The remaining attributes have reasonable defaults.\n\t */\n\tpublic static final class UserBuilder {\n\n\t\tprivate @Nullable String username;\n\n\t\tprivate @Nullable String password;\n\n\t\tprivate List<GrantedAuthority> authorities = new ArrayList<>();\n\n\t\tprivate boolean accountExpired;\n\n\t\tprivate boolean accountLocked;\n\n\t\tprivate boolean credentialsExpired;\n\n\t\tprivate boolean disabled;\n\n\t\tprivate Function<@Nullable String, @Nullable String> passwordEncoder = (password) -> password;\n\n\t\t/**\n\t\t * Creates a new instance\n\t\t */\n\t\tprivate UserBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Populates the username. This attribute is required.\n\t\t * @param username the username. Cannot be null.\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserBuilder username(String username) {\n\t\t\tAssert.notNull(username, \"username cannot be null\");\n\t\t\tthis.username = username;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the password. This attribute is required.\n\t\t * @param password the password.\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserBuilder password(@Nullable String password) {\n\t\t\tthis.password = password;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Encodes the current password (if non-null) and any future passwords supplied to\n\t\t * {@link #password(String)}.\n\t\t * @param encoder the encoder to use\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserBuilder passwordEncoder(Function<@Nullable String, @Nullable String> encoder) {\n\t\t\tAssert.notNull(encoder, \"encoder cannot be null\");\n\t\t\tthis.passwordEncoder = encoder;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the roles. This method is a shortcut for calling\n\t\t * {@link #authorities(String...)}, but automatically prefixes each entry with\n\t\t * \"ROLE_\". This means the following:\n\t\t *\n\t\t * <code>\n\t\t *     builder.roles(\"USER\",\"ADMIN\");\n\t\t * </code>\n\t\t *\n\t\t * is equivalent to\n\t\t *\n\t\t * <code>\n\t\t *     builder.authorities(\"ROLE_USER\",\"ROLE_ADMIN\");\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * This attribute is required, but can also be populated with\n\t\t * {@link #authorities(String...)}.\n\t\t * </p>\n\t\t * @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null,\n\t\t * contain null values or start with \"ROLE_\"\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserBuilder roles(String... roles) {\n\t\t\tList<GrantedAuthority> authorities = new ArrayList<>(roles.length);\n\t\t\tfor (String role : roles) {\n\t\t\t\tAssert.isTrue(!role.startsWith(\"ROLE_\"),\n\t\t\t\t\t\t() -> role + \" cannot start with ROLE_ (it is automatically added)\");\n\t\t\t\tauthorities.add(new SimpleGrantedAuthority(\"ROLE_\" + role));\n\t\t\t}\n\t\t\treturn authorities(authorities);\n\t\t}\n\n\t\t/**\n\t\t * Populates the authorities. This attribute is required.\n\t\t * @param authorities the authorities for this user. Cannot be null, or contain\n\t\t * null values\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t * @see #roles(String...)\n\t\t */\n\t\tpublic UserBuilder authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\treturn authorities(Arrays.asList(authorities));\n\t\t}\n\n\t\t/**\n\t\t * Populates the authorities. This attribute is required.\n\t\t * @param authorities the authorities for this user. Cannot be null, or contain\n\t\t * null values\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t * @see #roles(String...)\n\t\t */\n\t\tpublic UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = new ArrayList<>(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the authorities. This attribute is required.\n\t\t * @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN,\n\t\t * etc). Cannot be null, or contain null values\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t * @see #roles(String...)\n\t\t */\n\t\tpublic UserBuilder authorities(String... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\treturn authorities(AuthorityUtils.createAuthorityList(authorities));\n\t\t}\n\n\t\t/**\n\t\t * Defines if the account is expired or not. Default is false.\n\t\t * @param accountExpired true if the account is expired, false otherwise\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserBuilder accountExpired(boolean accountExpired) {\n\t\t\tthis.accountExpired = accountExpired;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Defines if the account is locked or not. Default is false.\n\t\t * @param accountLocked true if the account is locked, false otherwise\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserBuilder accountLocked(boolean accountLocked) {\n\t\t\tthis.accountLocked = accountLocked;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Defines if the credentials are expired or not. Default is false.\n\t\t * @param credentialsExpired true if the credentials are expired, false otherwise\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserBuilder credentialsExpired(boolean credentialsExpired) {\n\t\t\tthis.credentialsExpired = credentialsExpired;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Defines if the account is disabled or not. Default is false.\n\t\t * @param disabled true if the account is disabled, false otherwise\n\t\t * @return the {@link UserBuilder} for method chaining (i.e. to populate\n\t\t * additional attributes for this user)\n\t\t */\n\t\tpublic UserBuilder disabled(boolean disabled) {\n\t\t\tthis.disabled = disabled;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic UserDetails build() {\n\t\t\tAssert.notNull(this.username, \"username cannot be null\");\n\t\t\tString encodedPassword = (this.password != null) ? this.passwordEncoder.apply(this.password) : null;\n\t\t\treturn new User(this.username, encodedPassword, !this.disabled, !this.accountExpired,\n\t\t\t\t\t!this.credentialsExpired, !this.accountLocked, this.authorities);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/UserCache.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Provides a cache of {@link UserDetails} objects.\n *\n * <p>\n * Implementations should provide appropriate methods to set their cache parameters (e.g.\n * time-to-live) and/or force removal of entities before their normal expiration. These\n * are not part of the <code>UserCache</code> interface contract because they vary\n * depending on the type of caching system used (in-memory, disk, cluster, hybrid etc.).\n * <p>\n * Caching is generally only required in applications which do not maintain server-side\n * state, such as remote clients or web services. The authentication credentials are then\n * presented on each invocation and the overhead of accessing a database or other\n * persistent storage mechanism to validate would be excessive. In this case, you would\n * configure a cache to store the <tt>UserDetails</tt> information rather than loading it\n * each time.\n *\n * @author Ben Alex\n * @see org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider\n */\npublic interface UserCache {\n\n\t/**\n\t * Obtains a {@link UserDetails} from the cache.\n\t * @param username the {@link User#getUsername()} used to place the user in the cache\n\t * @return the populated <code>UserDetails</code> or <code>null</code> if the user\n\t * could not be found or if the cache entry has expired\n\t */\n\t@Nullable UserDetails getUserFromCache(String username);\n\n\t/**\n\t * Places a {@link UserDetails} in the cache. The <code>username</code> is the key\n\t * used to subsequently retrieve the <code>UserDetails</code>.\n\t * @param user the fully populated <code>UserDetails</code> to place in the cache\n\t */\n\tvoid putUserInCache(UserDetails user);\n\n\t/**\n\t * Removes the specified user from the cache. The <code>username</code> is the key\n\t * used to remove the user. If the user is not found, the method should simply return\n\t * (not thrown an exception).\n\t * <p>\n\t * Some cache implementations may not support eviction from the cache, in which case\n\t * they should provide appropriate behaviour to alter the user in either its\n\t * documentation, via an exception, or through a log message.\n\t * @param username to be evicted from the cache\n\t */\n\tvoid removeUserFromCache(String username);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/UserDetails.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport java.io.Serializable;\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Provides core user information.\n *\n * <p>\n * Implementations are not used directly by Spring Security for security purposes. They\n * simply store user information which is later encapsulated into {@link Authentication}\n * objects. This allows non-security related user information (such as email addresses,\n * telephone numbers etc) to be stored in a convenient location.\n * <p>\n * Concrete implementations must take particular care to ensure the non-null contract\n * detailed for each method is enforced. See\n * {@link org.springframework.security.core.userdetails.User} for a reference\n * implementation (which you might like to extend or use in your code).\n *\n * @author Ben Alex\n * @see UserDetailsService\n * @see UserCache\n */\npublic interface UserDetails extends Serializable {\n\n\t/**\n\t * Returns the authorities granted to the user. Cannot return <code>null</code>.\n\t * @return the authorities, sorted by natural key (never <code>null</code>)\n\t */\n\tCollection<? extends GrantedAuthority> getAuthorities();\n\n\t/**\n\t * Returns the password used to authenticate the user. Can be null if the user has not\n\t * specified a password (e.g. the user Passkeys instead).\n\t * @return the password\n\t */\n\t@Nullable String getPassword();\n\n\t/**\n\t * Returns the username used to authenticate the user. Cannot return\n\t * <code>null</code>.\n\t * @return the username (never <code>null</code>)\n\t */\n\tString getUsername();\n\n\t/**\n\t * Indicates whether the user's account has expired. An expired account cannot be\n\t * authenticated.\n\t * @return <code>true</code> if the user's account is valid (ie non-expired),\n\t * <code>false</code> if no longer valid (ie expired)\n\t */\n\tdefault boolean isAccountNonExpired() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Indicates whether the user is locked or unlocked. A locked user cannot be\n\t * authenticated.\n\t * @return <code>true</code> if the user is not locked, <code>false</code> otherwise\n\t */\n\tdefault boolean isAccountNonLocked() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Indicates whether the user's credentials (password) has expired. Expired\n\t * credentials prevent authentication.\n\t * @return <code>true</code> if the user's credentials are valid (ie non-expired),\n\t * <code>false</code> if no longer valid (ie expired)\n\t */\n\tdefault boolean isCredentialsNonExpired() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Indicates whether the user is enabled or disabled. A disabled user cannot be\n\t * authenticated.\n\t * @return <code>true</code> if the user is enabled, <code>false</code> otherwise\n\t */\n\tdefault boolean isEnabled() {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/UserDetailsByNameServiceWrapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * This implementation for AuthenticationUserDetailsService wraps a regular Spring\n * Security UserDetailsService implementation, to retrieve a UserDetails object based on\n * the user name contained in an <tt>Authentication</tt> object.\n *\n * @author Ruud Senden\n * @author Scott Battaglia\n * @since 2.0\n */\npublic class UserDetailsByNameServiceWrapper<T extends Authentication>\n\t\timplements AuthenticationUserDetailsService<T>, InitializingBean {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate UserDetailsService userDetailsService;\n\n\t/**\n\t * Constructs an empty wrapper for compatibility with Spring Security 2.0.x's method\n\t * of using a setter.\n\t */\n\tpublic UserDetailsByNameServiceWrapper() {\n\t\t// constructor for backwards compatibility with 2.0\n\t}\n\n\t/**\n\t * Constructs a new wrapper using the supplied\n\t * {@link org.springframework.security.core.userdetails.UserDetailsService} as the\n\t * service to delegate to.\n\t * @param userDetailsService the UserDetailsService to delegate to.\n\t */\n\tpublic UserDetailsByNameServiceWrapper(final UserDetailsService userDetailsService) {\n\t\tAssert.notNull(userDetailsService, \"userDetailsService cannot be null.\");\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t/**\n\t * Check whether all required properties have been set.\n\t *\n\t * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()\n\t */\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.userDetailsService, \"UserDetailsService must be set\");\n\t}\n\n\t/**\n\t * Get the UserDetails object from the wrapped UserDetailsService implementation\n\t */\n\t@Override\n\tpublic UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException {\n\t\treturn this.userDetailsService.loadUserByUsername(authentication.getName());\n\t}\n\n\t/**\n\t * Set the wrapped UserDetailsService implementation\n\t * @param aUserDetailsService The wrapped UserDetailsService to set\n\t */\n\tpublic void setUserDetailsService(UserDetailsService aUserDetailsService) {\n\t\tthis.userDetailsService = aUserDetailsService;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/UserDetailsChecker.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\n/**\n * Called by classes which make use of a {@link UserDetailsService} to check the status of\n * the loaded <tt>UserDetails</tt> object. Typically this will involve examining the\n * various flags associated with the account and raising an exception if the information\n * cannot be used (for example if the user account is locked or disabled), but a custom\n * implementation could perform any checks it wished.\n * <p>\n * The intention is that this interface should only be used for checks on the persistent\n * data associated with the user. It should not involved in making any authentication\n * decisions based on a submitted authentication request.\n *\n * @author Luke Taylor\n * @since 2.0\n * @see org.springframework.security.authentication.AccountStatusUserDetailsChecker\n * @see org.springframework.security.authentication.AccountStatusException\n */\npublic interface UserDetailsChecker {\n\n\t/**\n\t * Examines the User\n\t * @param toCheck the UserDetails instance whose status should be checked.\n\t */\n\tvoid check(UserDetails toCheck);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/UserDetailsPasswordService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An API for changing a {@link UserDetails} password.\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic interface UserDetailsPasswordService {\n\n\tUserDetailsPasswordService NOOP = (user, newPassword) -> user;\n\n\t/**\n\t * Modify the specified user's password. This should change the user's password in the\n\t * persistent user repository (database, LDAP etc).\n\t * @param user the user to modify the password for\n\t * @param newPassword the password to change to, encoded by the configured\n\t * {@code PasswordEncoder}\n\t * @return the updated UserDetails with the new password\n\t */\n\tUserDetails updatePassword(UserDetails user, @Nullable String newPassword);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/UserDetailsService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\n/**\n * Core interface which loads user-specific data.\n * <p>\n * It is used throughout the framework as a user DAO and is the strategy used by the\n * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider\n * DaoAuthenticationProvider}.\n *\n * <p>\n * The interface requires only one read-only method, which simplifies support for new\n * data-access strategies.\n *\n * @author Ben Alex\n * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider\n * @see UserDetails\n */\npublic interface UserDetailsService {\n\n\t/**\n\t * Locates the user based on the username. In the actual implementation, the search\n\t * may possibly be case sensitive, or case insensitive depending on how the\n\t * implementation instance is configured. In this case, the <code>UserDetails</code>\n\t * object that comes back may have a username that is of a different case than what\n\t * was actually requested..\n\t * @param username the username identifying the user whose data is required.\n\t * @return a fully populated user record (never <code>null</code>)\n\t * @throws UsernameNotFoundException if the user could not be found or the user has no\n\t * GrantedAuthority\n\t */\n\tUserDetails loadUserByUsername(String username) throws UsernameNotFoundException;\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/UsernameNotFoundException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Thrown if an {@link UserDetailsService} implementation cannot locate a {@link User} by\n * its username.\n *\n * @author Ben Alex\n */\npublic class UsernameNotFoundException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1410688585992297006L;\n\n\tprivate static final String DEFAULT_USER_NOT_FOUND_MESSAGE = \"user not found\";\n\n\tprivate final @Nullable String name;\n\n\t/**\n\t * Constructs a <code>UsernameNotFoundException</code> with the specified message.\n\t * @param msg the detail message.\n\t */\n\tpublic UsernameNotFoundException(String msg) {\n\t\tsuper(msg);\n\t\tthis.name = null;\n\t}\n\n\t/**\n\t * Constructs a {@code UsernameNotFoundException} with the specified message and root\n\t * cause.\n\t * @param msg the detail message.\n\t * @param cause root cause\n\t */\n\tpublic UsernameNotFoundException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t\tthis.name = null;\n\t}\n\n\tprivate UsernameNotFoundException(String msg, String name) {\n\t\tsuper(msg);\n\t\tthis.name = name;\n\t}\n\n\tprivate UsernameNotFoundException(String msg, String name, Throwable cause) {\n\t\tsuper(msg, cause);\n\t\tthis.name = name;\n\t}\n\n\t/**\n\t * Construct an exception based on a specific username\n\t * @param username the invalid username\n\t * @return the {@link UsernameNotFoundException}\n\t * @since 7.0\n\t */\n\tpublic static UsernameNotFoundException fromUsername(String username) {\n\t\treturn new UsernameNotFoundException(DEFAULT_USER_NOT_FOUND_MESSAGE, username);\n\t}\n\n\t/**\n\t * Construct an exception based on a specific username\n\t * @param username the invalid username\n\t * @param cause any underlying cause\n\t * @return the {@link UsernameNotFoundException}\n\t * @since 7.0\n\t */\n\tpublic static UsernameNotFoundException fromUsername(String username, Throwable cause) {\n\t\treturn new UsernameNotFoundException(DEFAULT_USER_NOT_FOUND_MESSAGE, username, cause);\n\t}\n\n\t/**\n\t * Get the username that couldn't be found\n\t * @return the username\n\t * @since 7.0\n\t */\n\tpublic @Nullable String getName() {\n\t\treturn this.name;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/cache/NullUserCache.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.cache;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * Does not perform any caching.\n *\n * @author Ben Alex\n */\npublic class NullUserCache implements UserCache {\n\n\t@Override\n\tpublic @Nullable UserDetails getUserFromCache(String username) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void putUserInCache(UserDetails user) {\n\t}\n\n\t@Override\n\tpublic void removeUserFromCache(String username) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/cache/SpringCacheBasedUserCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.cache;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * Caches {@link UserDetails} instances in a Spring defined {@link Cache}.\n *\n * @author Marten Deinum\n * @since 3.2\n */\npublic class SpringCacheBasedUserCache implements UserCache {\n\n\tprivate static final Log logger = LogFactory.getLog(SpringCacheBasedUserCache.class);\n\n\tprivate final Cache cache;\n\n\tpublic SpringCacheBasedUserCache(Cache cache) {\n\t\tAssert.notNull(cache, \"cache mandatory\");\n\t\tthis.cache = cache;\n\t}\n\n\t@Override\n\tpublic @Nullable UserDetails getUserFromCache(String username) {\n\t\tCache.ValueWrapper element = (username != null) ? this.cache.get(username) : null;\n\t\tlogger.debug(LogMessage.of(() -> \"Cache hit: \" + (element != null) + \"; username: \" + username));\n\t\treturn (element != null) ? (UserDetails) element.get() : null;\n\t}\n\n\t@Override\n\tpublic void putUserInCache(UserDetails user) {\n\t\tlogger.debug(LogMessage.of(() -> \"Cache put: \" + user.getUsername()));\n\t\tthis.cache.put(user.getUsername(), user);\n\t}\n\n\tpublic void removeUserFromCache(UserDetails user) {\n\t\tlogger.debug(LogMessage.of(() -> \"Cache remove: \" + user.getUsername()));\n\t\tthis.removeUserFromCache(user.getUsername());\n\t}\n\n\t@Override\n\tpublic void removeUserFromCache(String username) {\n\t\tthis.cache.evict(username);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/cache/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Implementations of {@link org.springframework.security.core.userdetails.UserCache\n * UserCache}.\n */\n\n@NullMarked\npackage org.springframework.security.core.userdetails.cache;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/jdbc/JdbcDaoImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.jdbc;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.springframework.context.ApplicationContextException;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.support.JdbcDaoSupport;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.util.Assert;\n\n/**\n * <tt>UserDetailsService</tt> implementation which retrieves the user details (username,\n * password, enabled flag, and authorities) from a database using JDBC queries.\n *\n * <h3>Default Schema</h3> A default database schema is assumed, with two tables \"users\"\n * and \"authorities\".\n *\n * <h4>The Users table</h4>\n *\n * This table contains the login name, password and enabled status of the user.\n *\n * <table summary=\"The Users Table\">\n * <tr>\n * <th>Column</th>\n * </tr>\n * <tr>\n * <td>username</td>\n * </tr>\n * <tr>\n * <td>password</td>\n * </tr>\n * <tr>\n * <td>enabled</td>\n * </tr>\n * </table>\n *\n * <h4>The Authorities Table</h4>\n *\n * <table summary=\"The Authorities Table\">\n * <tr>\n * <th>Column</th>\n * </tr>\n * <tr>\n * <td>username</td>\n * </tr>\n * <tr>\n * <td>authority</td>\n * </tr>\n * </table>\n *\n * If you are using an existing schema you will have to set the queries\n * <tt>usersByUsernameQuery</tt> and <tt>authoritiesByUsernameQuery</tt> to match your\n * database setup (see {@link #DEF_USERS_BY_USERNAME_QUERY} and\n * {@link #DEF_AUTHORITIES_BY_USERNAME_QUERY}).\n *\n * <p>\n * In order to minimise backward compatibility issues, this implementation doesn't\n * recognise the expiration of user accounts or the expiration of user credentials.\n * However, it does recognise and honour the user enabled/disabled column. This should map\n * to a <tt>boolean</tt> type in the result set (the SQL type will depend on the database\n * you are using). All the other columns map to <tt>String</tt>s.\n *\n * <h3>Group Support</h3> Support for group-based authorities can be enabled by setting\n * the <tt>enableGroups</tt> property to <tt>true</tt> (you may also then wish to set\n * <tt>enableAuthorities</tt> to <tt>false</tt> to disable loading of authorities\n * directly). With this approach, authorities are allocated to groups and a user's\n * authorities are determined based on the groups they are a member of. The net result is\n * the same (a UserDetails containing a set of <tt>GrantedAuthority</tt>s is loaded), but\n * the different persistence strategy may be more suitable for the administration of some\n * applications.\n * <p>\n * When groups are being used, the tables \"groups\", \"group_members\" and\n * \"group_authorities\" are used. See {@link #DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY} for\n * the default query which is used to load the group authorities. Again you can customize\n * this by setting the <tt>groupAuthoritiesByUsernameQuery</tt> property, but the format\n * of the rows returned should match the default.\n *\n * @author Ben Alex\n * @author colin sampaleanu\n * @author Luke Taylor\n */\npublic class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService, MessageSourceAware {\n\n\tpublic static final String DEFAULT_USER_SCHEMA_DDL_LOCATION = \"org/springframework/security/core/userdetails/jdbc/users.ddl\";\n\n\t// @formatter:off\n\tpublic static final String DEF_USERS_BY_USERNAME_QUERY = \"select username,password,enabled \"\n\t\t\t+ \"from users \"\n\t\t\t+ \"where username = ?\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tpublic static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = \"select username,authority \"\n\t\t\t+ \"from authorities \"\n\t\t\t+ \"where username = ?\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tpublic static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY = \"select g.id, g.group_name, ga.authority \"\n\t\t\t+ \"from groups g, group_members gm, group_authorities ga \"\n\t\t\t+ \"where gm.username = ? \" + \"and g.id = ga.group_id \" + \"and g.id = gm.group_id\";\n\t// @formatter:on\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate String authoritiesByUsernameQuery;\n\n\tprivate String groupAuthoritiesByUsernameQuery;\n\n\tprivate String usersByUsernameQuery;\n\n\tprivate String rolePrefix = \"\";\n\n\tprivate boolean usernameBasedPrimaryKey = true;\n\n\tprivate boolean enableAuthorities = true;\n\n\tprivate boolean enableGroups;\n\n\tpublic JdbcDaoImpl() {\n\t\tthis.usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;\n\t\tthis.authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;\n\t\tthis.groupAuthoritiesByUsernameQuery = DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY;\n\t}\n\n\t/**\n\t * @return the messages\n\t */\n\tprotected MessageSourceAccessor getMessages() {\n\t\treturn this.messages;\n\t}\n\n\t/**\n\t * Allows subclasses to add their own granted authorities to the list to be returned\n\t * in the <tt>UserDetails</tt>.\n\t * @param username the username, for use by finder methods\n\t * @param authorities the current granted authorities, as populated from the\n\t * <code>authoritiesByUsername</code> mapping\n\t */\n\tprotected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) {\n\t}\n\n\tpublic String getUsersByUsernameQuery() {\n\t\treturn this.usersByUsernameQuery;\n\t}\n\n\t@Override\n\tprotected void initDao() throws ApplicationContextException {\n\t\tAssert.isTrue(this.enableAuthorities || this.enableGroups,\n\t\t\t\t\"Use of either authorities or groups must be enabled\");\n\t}\n\n\t@Override\n\tpublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n\t\tList<UserDetails> users = loadUsersByUsername(username);\n\t\tif (users.isEmpty()) {\n\t\t\tthis.logger.debug(\"Query returned no results for user '\" + username + \"'\");\n\t\t\tthrow new UsernameNotFoundException(this.messages.getMessage(\"JdbcDaoImpl.notFound\",\n\t\t\t\t\tnew Object[] { username }, \"Username {0} not found\"));\n\t\t}\n\t\tUserDetails user = users.get(0); // contains no GrantedAuthority[]\n\t\tSet<GrantedAuthority> dbAuthsSet = new HashSet<>();\n\t\tif (this.enableAuthorities) {\n\t\t\tdbAuthsSet.addAll(loadUserAuthorities(user.getUsername()));\n\t\t}\n\t\tif (this.enableGroups) {\n\t\t\tdbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));\n\t\t}\n\t\tList<GrantedAuthority> dbAuths = new ArrayList<>(dbAuthsSet);\n\t\taddCustomAuthorities(user.getUsername(), dbAuths);\n\t\tif (dbAuths.isEmpty()) {\n\t\t\tthis.logger.debug(\"User '\" + username + \"' has no authorities and will be treated as 'not found'\");\n\t\t\tthrow new UsernameNotFoundException(this.messages.getMessage(\"JdbcDaoImpl.noAuthority\",\n\t\t\t\t\tnew Object[] { username }, \"User {0} has no GrantedAuthority\"));\n\t\t}\n\t\treturn createUserDetails(username, user, dbAuths);\n\t}\n\n\t/**\n\t * Executes the SQL <tt>usersByUsernameQuery</tt> and returns a list of UserDetails\n\t * objects. There should normally only be one matching user.\n\t */\n\tprotected List<UserDetails> loadUsersByUsername(String username) {\n\t\t// @formatter:off\n\t\tRowMapper<UserDetails> mapper = (rs, rowNum) -> {\n\t\t\tString username1 = rs.getString(1);\n\t\t\tString password = rs.getString(2);\n\t\t\tboolean enabled = rs.getBoolean(3);\n\t\t\treturn new User(username1, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES);\n\t\t};\n\t\t// @formatter:on\n\t\treturn getJdbc().query(this.usersByUsernameQuery, mapper, username);\n\t}\n\n\t/**\n\t * Loads authorities by executing the SQL from <tt>authoritiesByUsernameQuery</tt>.\n\t * @return a list of GrantedAuthority objects for the user\n\t */\n\tprotected List<GrantedAuthority> loadUserAuthorities(String username) {\n\t\treturn getJdbc().query(this.authoritiesByUsernameQuery, (rs, rowNum) -> {\n\t\t\tString roleName = JdbcDaoImpl.this.rolePrefix + rs.getString(2);\n\t\t\treturn new SimpleGrantedAuthority(roleName);\n\t\t}, username);\n\t}\n\n\t/**\n\t * Loads authorities by executing the SQL from\n\t * <tt>groupAuthoritiesByUsernameQuery</tt>.\n\t * @return a list of GrantedAuthority objects for the user\n\t */\n\tprotected List<GrantedAuthority> loadGroupAuthorities(String username) {\n\t\treturn getJdbc().query(this.groupAuthoritiesByUsernameQuery, (rs, rowNum) -> {\n\t\t\tString roleName = getRolePrefix() + rs.getString(3);\n\t\t\treturn new SimpleGrantedAuthority(roleName);\n\t\t}, username);\n\t}\n\n\t/**\n\t * Can be overridden to customize the creation of the final UserDetailsObject which is\n\t * returned by the <tt>loadUserByUsername</tt> method.\n\t * @param username the name originally passed to loadUserByUsername\n\t * @param userFromUserQuery the object returned from the execution of the\n\t * @param combinedAuthorities the combined array of authorities from all the authority\n\t * loading queries.\n\t * @return the final UserDetails which should be used in the system.\n\t */\n\tprotected UserDetails createUserDetails(String username, UserDetails userFromUserQuery,\n\t\t\tList<GrantedAuthority> combinedAuthorities) {\n\t\tString returnUsername = userFromUserQuery.getUsername();\n\t\tif (!this.usernameBasedPrimaryKey) {\n\t\t\treturnUsername = username;\n\t\t}\n\t\treturn new User(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.isEnabled(),\n\t\t\t\tuserFromUserQuery.isAccountNonExpired(), userFromUserQuery.isCredentialsNonExpired(),\n\t\t\t\tuserFromUserQuery.isAccountNonLocked(), combinedAuthorities);\n\t}\n\n\t/**\n\t * Allows the default query string used to retrieve authorities based on username to\n\t * be overridden, if default table or column names need to be changed. The default\n\t * query is {@link #DEF_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query,\n\t * ensure that all returned columns are mapped back to the same column positions as in\n\t * the default query.\n\t * @param queryString The SQL query string to set\n\t */\n\tpublic void setAuthoritiesByUsernameQuery(String queryString) {\n\t\tthis.authoritiesByUsernameQuery = queryString;\n\t}\n\n\tprotected String getAuthoritiesByUsernameQuery() {\n\t\treturn this.authoritiesByUsernameQuery;\n\t}\n\n\t/**\n\t * Allows the default query string used to retrieve group authorities based on\n\t * username to be overridden, if default table or column names need to be changed. The\n\t * default query is {@link #DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY}; when modifying\n\t * this query, ensure that all returned columns are mapped back to the same column\n\t * positions as in the default query.\n\t * @param queryString The SQL query string to set\n\t */\n\tpublic void setGroupAuthoritiesByUsernameQuery(String queryString) {\n\t\tthis.groupAuthoritiesByUsernameQuery = queryString;\n\t}\n\n\t/**\n\t * Allows a default role prefix to be specified. If this is set to a non-empty value,\n\t * then it is automatically prepended to any roles read in from the db. This may for\n\t * example be used to add the <tt>ROLE_</tt> prefix expected to exist in role names\n\t * (by default) by some other Spring Security classes, in the case that the prefix is\n\t * not already present in the db.\n\t * @param rolePrefix the new prefix\n\t */\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n\tprotected String getRolePrefix() {\n\t\treturn this.rolePrefix;\n\t}\n\n\t/**\n\t * If <code>true</code> (the default), indicates the\n\t * {@link #getUsersByUsernameQuery()} returns a username in response to a query. If\n\t * <code>false</code>, indicates that a primary key is used instead. If set to\n\t * <code>true</code>, the class will use the database-derived username in the returned\n\t * <code>UserDetails</code>. If <code>false</code>, the class will use the\n\t * {@link #loadUserByUsername(String)} derived username in the returned\n\t * <code>UserDetails</code>.\n\t * @param usernameBasedPrimaryKey <code>true</code> if the mapping queries return the\n\t * username <code>String</code>, or <code>false</code> if the mapping returns a\n\t * database primary key.\n\t */\n\tpublic void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {\n\t\tthis.usernameBasedPrimaryKey = usernameBasedPrimaryKey;\n\t}\n\n\tprotected boolean isUsernameBasedPrimaryKey() {\n\t\treturn this.usernameBasedPrimaryKey;\n\t}\n\n\t/**\n\t * Allows the default query string used to retrieve users based on username to be\n\t * overridden, if default table or column names need to be changed. The default query\n\t * is {@link #DEF_USERS_BY_USERNAME_QUERY}; when modifying this query, ensure that all\n\t * returned columns are mapped back to the same column positions as in the default\n\t * query. If the 'enabled' column does not exist in the source database, a permanent\n\t * true value for this column may be returned by using a query similar to\n\t *\n\t * <pre>\n\t * &quot;select username,password,'true' as enabled from users where username = ?&quot;\n\t * </pre>\n\t * @param usersByUsernameQueryString The query string to set\n\t */\n\tpublic void setUsersByUsernameQuery(String usersByUsernameQueryString) {\n\t\tthis.usersByUsernameQuery = usersByUsernameQueryString;\n\t}\n\n\tprotected boolean getEnableAuthorities() {\n\t\treturn this.enableAuthorities;\n\t}\n\n\t/**\n\t * Enables loading of authorities (roles) from the authorities table. Defaults to true\n\t */\n\tpublic void setEnableAuthorities(boolean enableAuthorities) {\n\t\tthis.enableAuthorities = enableAuthorities;\n\t}\n\n\tprotected boolean getEnableGroups() {\n\t\treturn this.enableGroups;\n\t}\n\n\t/**\n\t * Enables support for group authorities. Defaults to false\n\t * @param enableGroups\n\t */\n\tpublic void setEnableGroups(boolean enableGroups) {\n\t\tthis.enableGroups = enableGroups;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\tprivate JdbcTemplate getJdbc() {\n\t\tJdbcTemplate template = getJdbcTemplate();\n\t\tAssert.notNull(template, \"JdbcTemplate cannot be null\");\n\t\treturn template;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/jdbc/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Exposes a JDBC-based authentication repository, implementing\n * {@code org.springframework.security.core.userdetails.UserDetailsService UserDetailsService}.\n */\n@NullMarked\npackage org.springframework.security.core.userdetails.jdbc;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/memory/UserAttribute.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.memory;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Vector;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\n/**\n * Used by {@link org.springframework.security.provisioning.InMemoryUserDetailsManager} to\n * temporarily store the attributes associated with a user.\n *\n * @author Ben Alex\n */\npublic class UserAttribute {\n\n\tprivate List<GrantedAuthority> authorities = new Vector<>();\n\n\tprivate @Nullable String password;\n\n\tprivate boolean enabled = true;\n\n\tpublic void addAuthority(GrantedAuthority newAuthority) {\n\t\tthis.authorities.add(newAuthority);\n\t}\n\n\tpublic List<GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t/**\n\t * Set all authorities for this user.\n\t * @param authorities {@link List} &lt;{@link GrantedAuthority}&gt;\n\t * @since 1.1\n\t */\n\tpublic void setAuthorities(List<GrantedAuthority> authorities) {\n\t\tthis.authorities = authorities;\n\t}\n\n\t/**\n\t * Set all authorities for this user from String values. It will create the necessary\n\t * {@link GrantedAuthority} objects.\n\t * @param authoritiesAsStrings {@link List} &lt;{@link String}&gt;\n\t * @since 1.1\n\t */\n\tpublic void setAuthoritiesAsString(List<String> authoritiesAsStrings) {\n\t\tsetAuthorities(new ArrayList<>(authoritiesAsStrings.size()));\n\t\tfor (String authority : authoritiesAsStrings) {\n\t\t\taddAuthority(new SimpleGrantedAuthority(authority));\n\t\t}\n\t}\n\n\tpublic @Nullable String getPassword() {\n\t\treturn this.password;\n\t}\n\n\tpublic boolean isEnabled() {\n\t\treturn this.enabled;\n\t}\n\n\tpublic boolean isValid() {\n\t\treturn (this.password != null) && (this.authorities.size() > 0);\n\t}\n\n\tpublic void setEnabled(boolean enabled) {\n\t\tthis.enabled = enabled;\n\t}\n\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/memory/UserAttributeEditor.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.memory;\n\nimport java.beans.PropertyEditorSupport;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.springframework.util.StringUtils;\n\n/**\n * Property editor that creates a {@link UserAttribute} from a comma separated list of\n * values.\n *\n * @author Ben Alex\n */\npublic class UserAttributeEditor extends PropertyEditorSupport {\n\n\t@Override\n\tpublic void setAsText(String s) throws IllegalArgumentException {\n\t\tif (!StringUtils.hasText(s)) {\n\t\t\tsetValue(null);\n\t\t\treturn;\n\t\t}\n\t\tString[] tokens = StringUtils.commaDelimitedListToStringArray(s);\n\t\tUserAttribute userAttrib = new UserAttribute();\n\t\tList<String> authoritiesAsStrings = new ArrayList<>();\n\t\tfor (int i = 0; i < tokens.length; i++) {\n\t\t\tString currentToken = tokens[i].trim();\n\t\t\tif (i == 0) {\n\t\t\t\tuserAttrib.setPassword(currentToken);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (currentToken.toLowerCase(Locale.ENGLISH).equals(\"enabled\")) {\n\t\t\t\t\tuserAttrib.setEnabled(true);\n\t\t\t\t}\n\t\t\t\telse if (currentToken.toLowerCase(Locale.ENGLISH).equals(\"disabled\")) {\n\t\t\t\t\tuserAttrib.setEnabled(false);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tauthoritiesAsStrings.add(currentToken);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tuserAttrib.setAuthoritiesAsString(authoritiesAsStrings);\n\t\tsetValue(userAttrib.isValid() ? userAttrib : null);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/memory/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Exposes an in-memory authentication repository.\n */\n@NullMarked\npackage org.springframework.security.core.userdetails.memory;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/core/userdetails/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The standard interfaces for implementing user data DAOs.\n * <p>\n * Can be the traditional\n * {@link org.springframework.security.core.userdetails.UserDetailsService\n * UserDetailsService} which uses a unique username to identify the user or, for more\n * complex requirements, the\n * {@link org.springframework.security.core.userdetails.AuthenticationUserDetailsService\n * AuthenticationUserDetailsService}.\n */\n@NullMarked\npackage org.springframework.security.core.userdetails;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/AnonymousAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * This is a Jackson mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.AnonymousAuthenticationToken} class.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CoreJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\nclass AnonymousAuthenticationTokenMixin {\n\n\t/**\n\t * Constructor used by Jackson to create object of\n\t * {@link org.springframework.security.authentication.AnonymousAuthenticationToken}.\n\t * @param keyHash hashCode of key provided at the time of token creation by using\n\t * {@link org.springframework.security.authentication.AnonymousAuthenticationToken#AnonymousAuthenticationToken(String, Object, Collection)}\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param authorities the authorities granted to the principal\n\t */\n\t@JsonCreator\n\tAnonymousAuthenticationTokenMixin(@JsonProperty(\"keyHash\") Integer keyHash,\n\t\t\t@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/BadCredentialsExceptionMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * This mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.BadCredentialsException} class.\n *\n * @author Sebastien Deleuze\n * @author Yannick Lombardi\n * @since 7.0\n * @see CoreJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonIgnoreProperties({ \"cause\", \"stackTrace\", \"authenticationRequest\" })\nclass BadCredentialsExceptionMixin {\n\n\t/**\n\t * Constructor used by Jackson to create\n\t * {@link org.springframework.security.authentication.BadCredentialsException} object.\n\t * @param message the detail message\n\t */\n\t@JsonCreator\n\tBadCredentialsExceptionMixin(@JsonProperty(\"message\") String message) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/CoreJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.cfg.DateTimeFeature;\nimport tools.jackson.databind.cfg.MapperBuilder;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthentication;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.User;\n\n/**\n * Jackson module for spring-security-core. This module register\n * {@link AnonymousAuthenticationTokenMixin}, {@link RememberMeAuthenticationTokenMixin},\n * {@link SimpleGrantedAuthorityMixin}, {@link FactorGrantedAuthorityMixin},\n * {{@link UserMixin}, {@link UsernamePasswordAuthenticationTokenMixin} and\n * {@link UsernamePasswordAuthenticationTokenMixin}.\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.O\n * @see SecurityJacksonModules\n */\n@SuppressWarnings(\"serial\")\npublic class CoreJacksonModule extends SecurityJacksonModule {\n\n\tpublic CoreJacksonModule() {\n\t\tsuper(CoreJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\tprotected CoreJacksonModule(String name, Version version) {\n\t\tsuper(name, version);\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(Instant.class)\n\t\t\t.allowIfSubType(Duration.class)\n\t\t\t.allowIfSubType(SimpleGrantedAuthority.class)\n\t\t\t.allowIfSubType(FactorGrantedAuthority.class)\n\t\t\t.allowIfSubType(UsernamePasswordAuthenticationToken.class)\n\t\t\t.allowIfSubType(RememberMeAuthenticationToken.class)\n\t\t\t.allowIfSubType(AnonymousAuthenticationToken.class)\n\t\t\t.allowIfSubType(User.class)\n\t\t\t.allowIfSubType(BadCredentialsException.class)\n\t\t\t.allowIfSubType(SecurityContextImpl.class)\n\t\t\t.allowIfSubType(TestingAuthenticationToken.class)\n\t\t\t.allowIfSubType(OneTimeTokenAuthentication.class)\n\t\t\t.allowIfSubType(\"java.util.Collections$UnmodifiableSet\")\n\t\t\t.allowIfSubType(\"java.util.Collections$UnmodifiableRandomAccessList\")\n\t\t\t.allowIfSubType(\"java.util.Collections$EmptyList\")\n\t\t\t.allowIfSubType(\"java.util.ArrayList\")\n\t\t\t.allowIfSubType(\"java.util.HashMap\")\n\t\t\t.allowIfSubType(\"java.util.Collections$EmptyMap\")\n\t\t\t.allowIfSubType(\"java.util.Date\")\n\t\t\t.allowIfSubType(\"java.util.Arrays$ArrayList\")\n\t\t\t.allowIfSubType(\"java.util.Collections$UnmodifiableMap\")\n\t\t\t.allowIfSubType(\"java.util.LinkedHashMap\")\n\t\t\t.allowIfSubType(\"java.util.Collections$SingletonList\")\n\t\t\t.allowIfSubType(\"java.util.TreeMap\")\n\t\t\t.allowIfSubType(\"java.util.HashSet\")\n\t\t\t.allowIfSubType(\"java.util.LinkedHashSet\");\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\t((MapperBuilder<?, ?>) context.getOwner()).enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS);\n\t\tcontext.setMixIn(AnonymousAuthenticationToken.class, AnonymousAuthenticationTokenMixin.class);\n\t\tcontext.setMixIn(RememberMeAuthenticationToken.class, RememberMeAuthenticationTokenMixin.class);\n\t\tcontext.setMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);\n\t\tcontext.setMixIn(FactorGrantedAuthority.class, FactorGrantedAuthorityMixin.class);\n\t\tcontext.setMixIn(User.class, UserMixin.class);\n\t\tcontext.setMixIn(UsernamePasswordAuthenticationToken.class, UsernamePasswordAuthenticationTokenMixin.class);\n\t\tcontext.setMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);\n\t\tcontext.setMixIn(BadCredentialsException.class, BadCredentialsExceptionMixin.class);\n\t\tcontext.setMixIn(OneTimeTokenAuthentication.class, OneTimeTokenAuthenticationMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/FactorGrantedAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.time.Instant;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.\n *\n * @author Sebastien Deleuze\n * @author Rob Winch\n * @since 7.0\n * @see CoreJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class FactorGrantedAuthorityMixin {\n\n\t/**\n\t * Mixin Constructor.\n\t * @param authority the authority\n\t */\n\t@JsonCreator\n\tFactorGrantedAuthorityMixin(@JsonProperty(\"authority\") String authority,\n\t\t\t@JsonProperty(\"issuedAt\") Instant issuedAt) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/OneTimeTokenAuthenticationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.ott.OneTimeTokenAuthentication}.\n *\n * <pre>\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t.addModules(new CoreJacksonModule())\n * \t\t\t.build();\n * </pre>\n *\n * @author Marcus Da Coregio\n * @since 7.1\n * @see CoreJacksonModule\n * @see SecurityJacksonModules\n *\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OneTimeTokenAuthenticationMixin {\n\n\t@JsonCreator\n\tOneTimeTokenAuthenticationMixin(@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/RememberMeAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * This mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.RememberMeAuthenticationToken}\n * class.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CoreJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\n@JsonIgnoreProperties(ignoreUnknown = true)\nclass RememberMeAuthenticationTokenMixin {\n\n\t/**\n\t * Constructor used by Jackson to create\n\t * {@link org.springframework.security.authentication.RememberMeAuthenticationToken}\n\t * object.\n\t * @param keyHash hashCode of above given key.\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param authorities the authorities granted to the principal\n\t */\n\t@JsonCreator\n\tRememberMeAuthenticationTokenMixin(@JsonProperty(\"keyHash\") Integer keyHash,\n\t\t\t@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/SecurityJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\nimport tools.jackson.databind.jsontype.PolymorphicTypeValidator;\nimport tools.jackson.databind.module.SimpleModule;\n\n/**\n * Jackson module allowing to contribute {@link PolymorphicTypeValidator} configuration.\n *\n * @author Sebastien Deleuze\n * @since 7.0\n */\npublic abstract class SecurityJacksonModule extends SimpleModule {\n\n\tpublic SecurityJacksonModule() {\n\t\tsuper();\n\t}\n\n\tpublic SecurityJacksonModule(String name, Version version) {\n\t\tsuper(name, version, null);\n\t}\n\n\tpublic abstract void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/SecurityJacksonModules.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.databind.DefaultTyping;\nimport tools.jackson.databind.JacksonModule;\nimport tools.jackson.databind.cfg.MapperBuilder;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\nimport tools.jackson.databind.jsontype.PolymorphicTypeValidator;\nimport tools.jackson.databind.module.SimpleModule;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.ClassUtils;\n\n/**\n * This utility class will find all the Jackson modules contributed by Spring Security in\n * the classpath (except {@code WebauthnJacksonModule}), enable automatic inclusion of\n * type information and configure a {@link PolymorphicTypeValidator} that handles the\n * validation of class names.\n *\n * <p>\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * If needed, you can add custom classes to the validation handling.\n * <p>\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()\n *     \t\t\t.allowIfSubType(MyCustomType.class);\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader, builder))\n * \t   \t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n */\npublic final class SecurityJacksonModules {\n\n\tprivate static final Log logger = LogFactory.getLog(SecurityJacksonModules.class);\n\n\tprivate static final List<String> securityJacksonModuleClasses = Arrays.asList(\n\t\t\t\"org.springframework.security.jackson.CoreJacksonModule\",\n\t\t\t\"org.springframework.security.web.jackson.WebJacksonModule\",\n\t\t\t\"org.springframework.security.web.server.jackson.WebServerJacksonModule\");\n\n\tprivate static final String webServletJacksonModuleClass = \"org.springframework.security.web.jackson.WebServletJacksonModule\";\n\n\tprivate static final String oauth2ClientJacksonModuleClass = \"org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule\";\n\n\tprivate static final String oauth2AuthorizationServerJacksonModuleClass = \"org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule\";\n\n\tprivate static final String ldapJacksonModuleClass = \"org.springframework.security.ldap.jackson.LdapJacksonModule\";\n\n\tprivate static final String saml2JacksonModuleClass = \"org.springframework.security.saml2.jackson.Saml2JacksonModule\";\n\n\tprivate static final String casJacksonModuleClass = \"org.springframework.security.cas.jackson.CasJacksonModule\";\n\n\tprivate static final boolean webServletPresent;\n\n\tprivate static final boolean oauth2ClientPresent;\n\n\tprivate static final boolean oauth2AuthorizationServerPresent;\n\n\tprivate static final boolean ldapJacksonPresent;\n\n\tprivate static final boolean saml2JacksonPresent;\n\n\tprivate static final boolean casJacksonPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = SecurityJacksonModules.class.getClassLoader();\n\t\twebServletPresent = ClassUtils.isPresent(\"jakarta.servlet.http.Cookie\", classLoader);\n\t\toauth2ClientPresent = ClassUtils.isPresent(\"org.springframework.security.oauth2.client.OAuth2AuthorizedClient\",\n\t\t\t\tclassLoader);\n\t\toauth2AuthorizationServerPresent = ClassUtils\n\t\t\t.isPresent(\"org.springframework.security.oauth2.server.authorization.OAuth2Authorization\", classLoader);\n\t\tldapJacksonPresent = ClassUtils.isPresent(ldapJacksonModuleClass, classLoader);\n\t\tsaml2JacksonPresent = ClassUtils.isPresent(saml2JacksonModuleClass, classLoader);\n\t\tcasJacksonPresent = ClassUtils.isPresent(casJacksonModuleClass, classLoader);\n\t}\n\n\tprivate SecurityJacksonModules() {\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static @Nullable SecurityJacksonModule loadAndGetInstance(String className, ClassLoader loader) {\n\t\ttry {\n\t\t\tClass<? extends SecurityJacksonModule> securityModule = (Class<? extends SecurityJacksonModule>) ClassUtils\n\t\t\t\t.forName(className, loader);\n\t\t\tlogger.debug(LogMessage.format(\"Loaded module %s, now registering\", className));\n\t\t\treturn securityModule.getConstructor().newInstance();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.debug(LogMessage.format(\"Cannot load module %s\", className), ex);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Return the list of available security modules in classpath, enable automatic\n\t * inclusion of type information and configure a default\n\t * {@link PolymorphicTypeValidator} that handles the validation of class names.\n\t * @param loader the ClassLoader to use\n\t * @return List of available security modules in classpath\n\t * @see #getModules(ClassLoader, BasicPolymorphicTypeValidator.Builder)\n\t */\n\tpublic static List<JacksonModule> getModules(ClassLoader loader) {\n\t\treturn getModules(loader, null);\n\t}\n\n\t/**\n\t * Return the list of available security modules in classpath, enable automatic\n\t * inclusion of type information and configure a default\n\t * {@link PolymorphicTypeValidator} customizable with the provided builder that\n\t * handles the validation of class names.\n\t * @param loader the ClassLoader to use\n\t * @param typeValidatorBuilder the builder to configure custom types allowed in\n\t * addition to Spring Security ones\n\t * @return List of available security modules in classpath.\n\t */\n\tpublic static List<JacksonModule> getModules(ClassLoader loader,\n\t\t\tBasicPolymorphicTypeValidator.@Nullable Builder typeValidatorBuilder) {\n\n\t\tList<JacksonModule> modules = new ArrayList<>();\n\t\tfor (String className : securityJacksonModuleClasses) {\n\t\t\taddToModulesList(loader, modules, className);\n\t\t}\n\t\tif (webServletPresent) {\n\t\t\taddToModulesList(loader, modules, webServletJacksonModuleClass);\n\t\t}\n\t\tif (oauth2ClientPresent) {\n\t\t\taddToModulesList(loader, modules, oauth2ClientJacksonModuleClass);\n\t\t}\n\t\tif (oauth2AuthorizationServerPresent) {\n\t\t\taddToModulesList(loader, modules, oauth2AuthorizationServerJacksonModuleClass);\n\t\t}\n\t\tif (ldapJacksonPresent) {\n\t\t\taddToModulesList(loader, modules, ldapJacksonModuleClass);\n\t\t}\n\t\tif (saml2JacksonPresent) {\n\t\t\taddToModulesList(loader, modules, saml2JacksonModuleClass);\n\t\t}\n\t\tif (casJacksonPresent) {\n\t\t\taddToModulesList(loader, modules, casJacksonModuleClass);\n\t\t}\n\t\tapplyPolymorphicTypeValidator(modules, typeValidatorBuilder);\n\t\treturn modules;\n\t}\n\n\tprivate static void applyPolymorphicTypeValidator(List<JacksonModule> modules,\n\t\t\tBasicPolymorphicTypeValidator.@Nullable Builder typeValidatorBuilder) {\n\n\t\tBasicPolymorphicTypeValidator.Builder builder = (typeValidatorBuilder != null) ? typeValidatorBuilder\n\t\t\t\t: BasicPolymorphicTypeValidator.builder();\n\t\tfor (JacksonModule module : modules) {\n\t\t\tif (module instanceof SecurityJacksonModule securityModule) {\n\t\t\t\tsecurityModule.configurePolymorphicTypeValidator(builder);\n\t\t\t}\n\t\t}\n\t\tmodules.add(new SimpleModule() {\n\t\t\t@Override\n\t\t\tpublic void setupModule(SetupContext context) {\n\t\t\t\t((MapperBuilder<?, ?>) context.getOwner()).activateDefaultTyping(builder.build(),\n\t\t\t\t\t\tDefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * @param loader the ClassLoader to use\n\t * @param modules list of the modules to add\n\t * @param className name of the class to instantiate\n\t */\n\tprivate static void addToModulesList(ClassLoader loader, List<JacksonModule> modules, String className) {\n\t\tSecurityJacksonModule module = loadAndGetInstance(className, loader);\n\t\tif (module != null) {\n\t\t\tmodules.add(module);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/SimpleGrantedAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CoreJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)\npublic abstract class SimpleGrantedAuthorityMixin {\n\n\t/**\n\t * Mixin Constructor.\n\t * @param role the role\n\t */\n\t@JsonCreator\n\tpublic SimpleGrantedAuthorityMixin(@JsonProperty(\"authority\") String role) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/TestingAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * This is a Jackson mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.AnonymousAuthenticationToken} class.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CoreJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\nclass TestingAuthenticationTokenMixin {\n\n\t/**\n\t * Constructor used by Jackson to create object of\n\t * {@link org.springframework.security.authentication.AnonymousAuthenticationToken}.\n\t * {@link org.springframework.security.authentication.AnonymousAuthenticationToken#AnonymousAuthenticationToken(String, Object, Collection)}\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param credentials the credentials\n\t * @param authorities the authorities granted to the principal\n\t */\n\t@JsonCreator\n\tTestingAuthenticationTokenMixin(@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"credentials\") Object credentials,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/UserDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.Set;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.ValueDeserializer;\nimport tools.jackson.databind.node.MissingNode;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\n/**\n * Custom Deserializer for {@link User} class. This is already registered with\n * {@link UserMixin}. You can also use it directly with your mixin class.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see UserMixin\n */\nclass UserDeserializer extends ValueDeserializer<User> {\n\n\tprivate static final TypeReference<Set<GrantedAuthority>> GRANTED_AUTHORITY_SET = new TypeReference<>() {\n\t};\n\n\t/**\n\t * This method will create {@link User} object. It will ensure successful object\n\t * creation even if password key is null in serialized json, because credentials may\n\t * be removed from the {@link User} by invoking {@link User#eraseCredentials()}. In\n\t * that case there won't be any password key in serialized json.\n\t * @param jp the JsonParser\n\t * @param ctxt the DeserializationContext\n\t * @return the user\n\t * @throws JacksonException if an error during JSON processing occurs\n\t */\n\t@Override\n\tpublic User deserialize(JsonParser jp, DeserializationContext ctxt) throws JacksonException {\n\t\tJsonNode jsonNode = ctxt.readTree(jp);\n\t\tJsonNode authoritiesNode = readJsonNode(jsonNode, \"authorities\");\n\t\tSet<GrantedAuthority> authorities = ctxt.readTreeAsValue(authoritiesNode,\n\t\t\t\tctxt.getTypeFactory().constructType(GRANTED_AUTHORITY_SET));\n\t\tJsonNode passwordNode = readJsonNode(jsonNode, \"password\");\n\t\tString username = readJsonNode(jsonNode, \"username\").asString();\n\t\tString password = (passwordNode.isMissingNode()) ? null : passwordNode.stringValue();\n\t\tboolean enabled = readJsonNode(jsonNode, \"enabled\").asBoolean();\n\t\tboolean accountNonExpired = readJsonNode(jsonNode, \"accountNonExpired\").asBoolean();\n\t\tboolean credentialsNonExpired = readJsonNode(jsonNode, \"credentialsNonExpired\").asBoolean();\n\t\tboolean accountNonLocked = readJsonNode(jsonNode, \"accountNonLocked\").asBoolean();\n\t\tUser result = new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked,\n\t\t\t\tauthorities);\n\t\tif (passwordNode.asString(null) == null) {\n\t\t\tresult.eraseCredentials();\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate JsonNode readJsonNode(JsonNode jsonNode, String field) {\n\t\treturn jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/UserMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * This mixin class helps in serialize/deserialize\n * {@link org.springframework.security.core.userdetails.User}. This class also register a\n * custom deserializer {@link UserDeserializer} to deserialize User object successfully.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see UserDeserializer\n * @see CoreJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = UserDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class UserMixin {\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/UsernamePasswordAuthenticationTokenDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.core.exc.StreamReadException;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.DatabindException;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.ValueDeserializer;\nimport tools.jackson.databind.node.MissingNode;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Custom deserializer for {@link UsernamePasswordAuthenticationToken}. At the time of\n * deserialization it will invoke suitable constructor depending on the value of\n * <b>authenticated</b> property. It will ensure that the token's state must not change.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @author Greg Turnquist\n * @author Onur Kagan Ozcan\n * @since 7.0\n * @see UsernamePasswordAuthenticationTokenMixin\n */\nclass UsernamePasswordAuthenticationTokenDeserializer extends ValueDeserializer<UsernamePasswordAuthenticationToken> {\n\n\tprivate static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<>() {\n\t};\n\n\t/**\n\t * This method construct {@link UsernamePasswordAuthenticationToken} object from\n\t * serialized json.\n\t * @param jp the JsonParser\n\t * @param ctxt the DeserializationContext\n\t * @return the user\n\t * @throws JacksonException if an error during JSON processing occurs\n\t */\n\t@Override\n\tpublic UsernamePasswordAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tJsonNode jsonNode = ctxt.readTree(jp);\n\t\tboolean authenticated = readJsonNode(jsonNode, \"authenticated\").asBoolean();\n\t\tJsonNode principalNode = readJsonNode(jsonNode, \"principal\");\n\t\tObject principal = getPrincipal(ctxt, principalNode);\n\t\tJsonNode credentialsNode = readJsonNode(jsonNode, \"credentials\");\n\t\tObject credentials = getCredentials(credentialsNode);\n\t\tJsonNode authoritiesNode = readJsonNode(jsonNode, \"authorities\");\n\t\tList<GrantedAuthority> authorities = ctxt.readTreeAsValue(authoritiesNode,\n\t\t\t\tctxt.getTypeFactory().constructType(GRANTED_AUTHORITY_LIST));\n\t\tUsernamePasswordAuthenticationToken token = (!authenticated)\n\t\t\t\t? UsernamePasswordAuthenticationToken.unauthenticated(principal, credentials)\n\t\t\t\t: UsernamePasswordAuthenticationToken.authenticated(principal, credentials, authorities);\n\t\tJsonNode detailsNode = readJsonNode(jsonNode, \"details\");\n\t\tif (detailsNode.isNull() || detailsNode.isMissingNode()) {\n\t\t\ttoken.setDetails(null);\n\t\t}\n\t\telse {\n\t\t\tObject details = ctxt.readTreeAsValue(detailsNode, Object.class);\n\t\t\ttoken.setDetails(details);\n\t\t}\n\t\treturn token;\n\t}\n\n\tprivate @Nullable Object getCredentials(JsonNode credentialsNode) {\n\t\tif (credentialsNode.isNull() || credentialsNode.isMissingNode()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn credentialsNode.asString();\n\t}\n\n\tprivate Object getPrincipal(DeserializationContext ctxt, JsonNode principalNode)\n\t\t\tthrows StreamReadException, DatabindException {\n\t\tif (principalNode.isObject()) {\n\t\t\treturn ctxt.readTreeAsValue(principalNode, Object.class);\n\t\t}\n\t\treturn principalNode.asString();\n\t}\n\n\tprivate JsonNode readJsonNode(JsonNode jsonNode, String field) {\n\t\treturn jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/UsernamePasswordAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * This mixin class is used to serialize / deserialize\n * {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}.\n * This class register a custom deserializer\n * {@link UsernamePasswordAuthenticationTokenDeserializer}.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CoreJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonDeserialize(using = UsernamePasswordAuthenticationTokenDeserializer.class)\nabstract class UsernamePasswordAuthenticationTokenMixin {\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 3+ serialization support.\n */\n@NullMarked\npackage org.springframework.security.jackson;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/AbstractUnmodifiableCollectionDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.ArrayNode;\n\n/**\n * Abstract base class for deserializers that create unmodifiable collections from JSON\n * data. Subclasses like {@link UnmodifiableListDeserializer} and\n * {@link UnmodifiableSetDeserializer} should implement the method to define the specific\n * collection type and handle the deserialization logic.\n *\n * @param <T> the type of the unmodifiable collection, such as {@link List} or\n * {@link Set}.\n * @author Hyunmin Choi\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.AbstractUnmodifiableCollectionDeserializer}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nabstract class AbstractUnmodifiableCollectionDeserializer<T> extends JsonDeserializer<T> {\n\n\t@Override\n\tpublic T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {\n\t\tObjectMapper mapper = (ObjectMapper) jp.getCodec();\n\t\tJsonNode node = mapper.readTree(jp);\n\t\tCollection<Object> values = new ArrayList<>();\n\t\tif (node instanceof ArrayNode arrayNode) {\n\t\t\tfor (JsonNode elementNode : arrayNode) {\n\t\t\t\tvalues.add(mapper.readValue(elementNode.traverse(mapper), Object.class));\n\t\t\t}\n\t\t}\n\t\telse if (node != null) {\n\t\t\tvalues.add(mapper.readValue(node.traverse(mapper), Object.class));\n\t\t}\n\t\treturn createUnmodifiableCollection(values);\n\t}\n\n\t/**\n\t * Creates an unmodifiable collection from the given JSON node.\n\t * @param values the values to add to the unmodifiable collection\n\t * @return an unmodifiable collection with the deserialized elements.\n\t * @throws IOException if an error occurs during deserialization.\n\t */\n\tabstract T createUnmodifiableCollection(Collection<Object> values) throws IOException;\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/AnonymousAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * This is a Jackson mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.AnonymousAuthenticationToken} class.\n * To use this class you need to register it with\n * {@link com.fasterxml.jackson.databind.ObjectMapper} and\n * {@link SimpleGrantedAuthorityMixin} because AnonymousAuthenticationToken contains\n * SimpleGrantedAuthority. <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * <i>Note: This class will save full class name into a property called @class</i>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.AnonymousAuthenticationTokenMixin} based on\n * Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@Deprecated(forRemoval = true)\nclass AnonymousAuthenticationTokenMixin {\n\n\t/**\n\t * Constructor used by Jackson to create object of\n\t * {@link org.springframework.security.authentication.AnonymousAuthenticationToken}.\n\t * @param keyHash hashCode of key provided at the time of token creation by using\n\t * {@link org.springframework.security.authentication.AnonymousAuthenticationToken#AnonymousAuthenticationToken(String, Object, Collection)}\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param authorities the authorities granted to the principal\n\t */\n\t@JsonCreator\n\tAnonymousAuthenticationTokenMixin(@JsonProperty(\"keyHash\") Integer keyHash,\n\t\t\t@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/BadCredentialsExceptionMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * This mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.BadCredentialsException} class. To\n * use this class you need to register it with\n * {@link com.fasterxml.jackson.databind.ObjectMapper}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * <i>Note: This class will save TypeInfo (full class name) into a property\n * called @class</i> <i>The cause and stackTrace are ignored in the serialization.</i>\n *\n * @author Yannick Lombardi\n * @since 5.0\n * @see CoreJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.BadCredentialsExceptionMixin} based on\n * Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonIgnoreProperties(ignoreUnknown = true, value = { \"cause\", \"stackTrace\", \"authenticationRequest\" })\n@Deprecated(forRemoval = true)\nclass BadCredentialsExceptionMixin {\n\n\t/**\n\t * Constructor used by Jackson to create\n\t * {@link org.springframework.security.authentication.BadCredentialsException} object.\n\t * @param message the detail message\n\t */\n\t@JsonCreator\n\tBadCredentialsExceptionMixin(@JsonProperty(\"message\") String message) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/CoreJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Collections;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\n/**\n * Jackson module for spring-security-core. This module register\n * {@link AnonymousAuthenticationTokenMixin}, {@link RememberMeAuthenticationTokenMixin},\n * {@link SimpleGrantedAuthorityMixin}, {@link UnmodifiableSetMixin}, {@link UserMixin}\n * and {@link UsernamePasswordAuthenticationTokenMixin}. If no default typing enabled by\n * default then it'll enable it because typing info is needed to properly\n * serialize/deserialize objects. In order to use this module just add this module into\n * your ObjectMapper configuration.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre> <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list\n * of all security modules.</b>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.jackson.CoreJacksonModule} based on Jackson 3\n */\n@SuppressWarnings({ \"serial\", \"removal\" })\n@Deprecated(forRemoval = true)\npublic class CoreJackson2Module extends SimpleModule {\n\n\tpublic CoreJackson2Module() {\n\t\tsuper(CoreJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tSecurityJackson2Modules.enableDefaultTyping(context.getOwner());\n\t\tcontext.setMixInAnnotations(AnonymousAuthenticationToken.class, AnonymousAuthenticationTokenMixin.class);\n\t\tcontext.setMixInAnnotations(RememberMeAuthenticationToken.class, RememberMeAuthenticationTokenMixin.class);\n\t\tcontext.setMixInAnnotations(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);\n\t\tcontext.setMixInAnnotations(FactorGrantedAuthority.class, FactorGrantedAuthorityMixin.class);\n\t\tcontext.setMixInAnnotations(Collections.unmodifiableSet(Collections.emptySet()).getClass(),\n\t\t\t\tUnmodifiableSetMixin.class);\n\t\tcontext.setMixInAnnotations(Collections.unmodifiableList(Collections.emptyList()).getClass(),\n\t\t\t\tUnmodifiableListMixin.class);\n\t\tcontext.setMixInAnnotations(Collections.unmodifiableMap(Collections.emptyMap()).getClass(),\n\t\t\t\tUnmodifiableMapMixin.class);\n\t\tcontext.setMixInAnnotations(User.class, UserMixin.class);\n\t\tcontext.setMixInAnnotations(UsernamePasswordAuthenticationToken.class,\n\t\t\t\tUsernamePasswordAuthenticationTokenMixin.class);\n\t\tcontext.setMixInAnnotations(BadCredentialsException.class, BadCredentialsExceptionMixin.class);\n\t\tcontext.setMixInAnnotations(OneTimeTokenAuthenticationToken.class, OneTimeTokenAuthenticationTokenMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/FactorGrantedAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.time.Instant;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Rob Winch\n * @since 7.0\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.FactorGrantedAuthorityMixin} based on\n * Jackson 3\n *\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class FactorGrantedAuthorityMixin {\n\n\t/**\n\t * Mixin Constructor.\n\t * @param authority the authority\n\t */\n\t@JsonCreator\n\tFactorGrantedAuthorityMixin(@JsonProperty(\"authority\") String authority,\n\t\t\t@JsonProperty(\"issuedAt\") Instant issuedAt) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/OneTimeTokenAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Marcus Da Coregio\n * @since 7.1\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n *\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OneTimeTokenAuthenticationTokenMixin {\n\n\t@JsonCreator\n\tOneTimeTokenAuthenticationTokenMixin(@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/RememberMeAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * This mixin class helps in serialize/deserialize\n * {@link org.springframework.security.authentication.RememberMeAuthenticationToken}\n * class. To use this class you need to register it with\n * {@link com.fasterxml.jackson.databind.ObjectMapper} and 2 more mixin classes.\n *\n * <ol>\n * <li>{@link SimpleGrantedAuthorityMixin}</li>\n * <li>{@link UserMixin}</li>\n * <li>{@link UnmodifiableSetMixin}</li>\n * </ol>\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * <i>Note: This class will save TypeInfo (full class name) into a property\n * called @class</i>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.RememberMeAuthenticationTokenMixin} based\n * on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@Deprecated(forRemoval = true)\nclass RememberMeAuthenticationTokenMixin {\n\n\t/**\n\t * Constructor used by Jackson to create\n\t * {@link org.springframework.security.authentication.RememberMeAuthenticationToken}\n\t * object.\n\t * @param keyHash hashCode of above given key.\n\t * @param principal the principal (typically a <code>UserDetails</code>)\n\t * @param authorities the authorities granted to the principal\n\t */\n\t@JsonCreator\n\tRememberMeAuthenticationTokenMixin(@JsonProperty(\"keyHash\") Integer keyHash,\n\t\t\t@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.annotation.JacksonAnnotation;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.DatabindContext;\nimport com.fasterxml.jackson.databind.DeserializationConfig;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.Module;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.cfg.MapperConfig;\nimport com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\nimport com.fasterxml.jackson.databind.jsontype.NamedType;\nimport com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;\nimport com.fasterxml.jackson.databind.jsontype.TypeIdResolver;\nimport com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.ClassUtils;\n\n/**\n * This utility class will find all the SecurityModules in classpath.\n *\n * <p>\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModules(SecurityJackson2Modules.getModules());\n * </pre> Above code is equivalent to\n * <p>\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);\n *     mapper.registerModule(new CoreJackson2Module());\n *     mapper.registerModule(new CasJackson2Module());\n *     mapper.registerModule(new WebJackson2Module());\n *     mapper.registerModule(new WebServletJackson2Module());\n *     mapper.registerModule(new WebServerJackson2Module());\n *     mapper.registerModule(new OAuth2ClientJackson2Module());\n *     mapper.registerModule(new Saml2Jackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.jackson.SecurityJacksonModules} based on Jackson 3\n */\n@Deprecated(forRemoval = true)\npublic final class SecurityJackson2Modules {\n\n\tprivate static final Log logger = LogFactory.getLog(SecurityJackson2Modules.class);\n\n\tprivate static final List<String> securityJackson2ModuleClasses = Arrays.asList(\n\t\t\t\"org.springframework.security.jackson2.CoreJackson2Module\",\n\t\t\t\"org.springframework.security.web.jackson2.WebJackson2Module\",\n\t\t\t\"org.springframework.security.web.server.jackson2.WebServerJackson2Module\");\n\n\tprivate static final String webServletJackson2ModuleClass = \"org.springframework.security.web.jackson2.WebServletJackson2Module\";\n\n\tprivate static final String oauth2ClientJackson2ModuleClass = \"org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module\";\n\n\tprivate static final String javaTimeJackson2ModuleClass = \"com.fasterxml.jackson.datatype.jsr310.JavaTimeModule\";\n\n\tprivate static final String ldapJackson2ModuleClass = \"org.springframework.security.ldap.jackson2.LdapJackson2Module\";\n\n\tprivate static final String saml2Jackson2ModuleClass = \"org.springframework.security.saml2.jackson2.Saml2Jackson2Module\";\n\n\tprivate static final String casJackson2ModuleClass = \"org.springframework.security.cas.jackson2.CasJackson2Module\";\n\n\tprivate static final boolean webServletPresent;\n\n\tprivate static final boolean oauth2ClientPresent;\n\n\tprivate static final boolean javaTimeJacksonPresent;\n\n\tprivate static final boolean ldapJacksonPresent;\n\n\tprivate static final boolean saml2JacksonPresent;\n\n\tprivate static final boolean casJacksonPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = SecurityJackson2Modules.class.getClassLoader();\n\t\twebServletPresent = ClassUtils.isPresent(\"jakarta.servlet.http.Cookie\", classLoader);\n\t\toauth2ClientPresent = ClassUtils.isPresent(\"org.springframework.security.oauth2.client.OAuth2AuthorizedClient\",\n\t\t\t\tclassLoader);\n\t\tjavaTimeJacksonPresent = ClassUtils.isPresent(javaTimeJackson2ModuleClass, classLoader);\n\t\tldapJacksonPresent = ClassUtils.isPresent(ldapJackson2ModuleClass, classLoader);\n\t\tsaml2JacksonPresent = ClassUtils.isPresent(saml2Jackson2ModuleClass, classLoader);\n\t\tcasJacksonPresent = ClassUtils.isPresent(casJackson2ModuleClass, classLoader);\n\t}\n\n\tprivate SecurityJackson2Modules() {\n\t}\n\n\tpublic static void enableDefaultTyping(ObjectMapper mapper) {\n\t\tif (mapper != null) {\n\t\t\tTypeResolverBuilder<?> typeBuilder = mapper.getDeserializationConfig().getDefaultTyper(null);\n\t\t\tif (typeBuilder == null) {\n\t\t\t\tmapper.setDefaultTyping(createAllowlistedDefaultTyping());\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static @Nullable Module loadAndGetInstance(String className, ClassLoader loader) {\n\t\ttry {\n\t\t\tClass<? extends Module> securityModule = (Class<? extends Module>) ClassUtils.forName(className, loader);\n\t\t\tif (securityModule != null) {\n\t\t\t\tlogger.debug(LogMessage.format(\"Loaded module %s, now registering\", className));\n\t\t\t\treturn securityModule.getConstructor().newInstance();\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.debug(LogMessage.format(\"Cannot load module %s\", className), ex);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * @param loader the ClassLoader to use\n\t * @return List of available security modules in classpath.\n\t */\n\tpublic static List<Module> getModules(ClassLoader loader) {\n\t\tList<Module> modules = new ArrayList<>();\n\t\tfor (String className : securityJackson2ModuleClasses) {\n\t\t\taddToModulesList(loader, modules, className);\n\t\t}\n\t\tif (webServletPresent) {\n\t\t\taddToModulesList(loader, modules, webServletJackson2ModuleClass);\n\t\t}\n\t\tif (oauth2ClientPresent) {\n\t\t\taddToModulesList(loader, modules, oauth2ClientJackson2ModuleClass);\n\t\t}\n\t\tif (javaTimeJacksonPresent) {\n\t\t\taddToModulesList(loader, modules, javaTimeJackson2ModuleClass);\n\t\t}\n\t\tif (ldapJacksonPresent) {\n\t\t\taddToModulesList(loader, modules, ldapJackson2ModuleClass);\n\t\t}\n\t\tif (saml2JacksonPresent) {\n\t\t\taddToModulesList(loader, modules, saml2Jackson2ModuleClass);\n\t\t}\n\t\tif (casJacksonPresent) {\n\t\t\taddToModulesList(loader, modules, casJackson2ModuleClass);\n\t\t}\n\t\treturn modules;\n\t}\n\n\t/**\n\t * @param loader the ClassLoader to use\n\t * @param modules list of the modules to add\n\t * @param className name of the class to instantiate\n\t */\n\tprivate static void addToModulesList(ClassLoader loader, List<Module> modules, String className) {\n\t\tModule module = loadAndGetInstance(className, loader);\n\t\tif (module != null) {\n\t\t\tmodules.add(module);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a TypeResolverBuilder that restricts allowed types.\n\t * @return a TypeResolverBuilder that restricts allowed types.\n\t */\n\tprivate static TypeResolverBuilder<? extends TypeResolverBuilder> createAllowlistedDefaultTyping() {\n\t\tTypeResolverBuilder<? extends TypeResolverBuilder> result = new AllowlistTypeResolverBuilder(\n\t\t\t\tObjectMapper.DefaultTyping.NON_FINAL);\n\t\tresult = result.init(JsonTypeInfo.Id.CLASS, null);\n\t\tresult = result.inclusion(JsonTypeInfo.As.PROPERTY);\n\t\treturn result;\n\t}\n\n\t/**\n\t * An implementation of {@link ObjectMapper.DefaultTypeResolverBuilder} that inserts\n\t * an {@code allow all} {@link PolymorphicTypeValidator} and overrides the\n\t * {@code TypeIdResolver}\n\t *\n\t * @author Rob Winch\n\t */\n\t@SuppressWarnings(\"serial\")\n\tstatic class AllowlistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {\n\n\t\tAllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {\n\t\t\tsuper(defaultTyping,\n\t\t\t\t\t// we do explicit validation in the TypeIdResolver\n\t\t\t\t\tBasicPolymorphicTypeValidator.builder().allowIfSubType(Object.class).build());\n\t\t}\n\n\t\t@Override\n\t\tprotected TypeIdResolver idResolver(MapperConfig<?> config, JavaType baseType,\n\t\t\t\tPolymorphicTypeValidator subtypeValidator, Collection<NamedType> subtypes, boolean forSer,\n\t\t\t\tboolean forDeser) {\n\t\t\tTypeIdResolver result = super.idResolver(config, baseType, subtypeValidator, subtypes, forSer, forDeser);\n\t\t\treturn new AllowlistTypeIdResolver(result);\n\t\t}\n\n\t}\n\n\t/**\n\t * A {@link TypeIdResolver} that delegates to an existing implementation and throws an\n\t * IllegalStateException if the class being looked up is not in the allowlist, does\n\t * not provide an explicit mixin, and is not annotated with Jackson mappings. See\n\t * https://github.com/spring-projects/spring-security/issues/4370\n\t */\n\tstatic class AllowlistTypeIdResolver implements TypeIdResolver {\n\n\t\tprivate static final Set<String> ALLOWLIST_CLASS_NAMES;\n\t\tstatic {\n\t\t\tSet<String> names = new HashSet<>();\n\t\t\tnames.add(\"java.util.ArrayList\");\n\t\t\tnames.add(\"java.util.Collections$EmptyList\");\n\t\t\tnames.add(\"java.util.Collections$EmptyMap\");\n\t\t\tnames.add(\"java.util.Collections$UnmodifiableRandomAccessList\");\n\t\t\tnames.add(\"java.util.Collections$SingletonList\");\n\t\t\tnames.add(\"java.util.Date\");\n\t\t\tnames.add(\"java.time.Instant\");\n\t\t\tnames.add(\"java.net.URL\");\n\t\t\tnames.add(\"java.util.TreeMap\");\n\t\t\tnames.add(\"java.util.HashMap\");\n\t\t\tnames.add(\"java.util.LinkedHashMap\");\n\t\t\tnames.add(\"org.springframework.security.core.context.SecurityContextImpl\");\n\t\t\tnames.add(\"java.util.Arrays$ArrayList\");\n\t\t\tALLOWLIST_CLASS_NAMES = Collections.unmodifiableSet(names);\n\t\t}\n\n\t\tprivate final TypeIdResolver delegate;\n\n\t\tAllowlistTypeIdResolver(TypeIdResolver delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic void init(JavaType baseType) {\n\t\t\tthis.delegate.init(baseType);\n\t\t}\n\n\t\t@Override\n\t\tpublic String idFromValue(Object value) {\n\t\t\treturn this.delegate.idFromValue(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String idFromValueAndType(Object value, Class<?> suggestedType) {\n\t\t\treturn this.delegate.idFromValueAndType(value, suggestedType);\n\t\t}\n\n\t\t@Override\n\t\tpublic String idFromBaseType() {\n\t\t\treturn this.delegate.idFromBaseType();\n\t\t}\n\n\t\t@Override\n\t\tpublic JavaType typeFromId(DatabindContext context, String id) throws IOException {\n\t\t\tDeserializationConfig config = (DeserializationConfig) context.getConfig();\n\t\t\tJavaType result = this.delegate.typeFromId(context, id);\n\t\t\tString className = result.getRawClass().getName();\n\t\t\tif (isInAllowlist(className)) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tboolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null;\n\t\t\tif (isExplicitMixin) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tJacksonAnnotation jacksonAnnotation = AnnotationUtils.findAnnotation(result.getRawClass(),\n\t\t\t\t\tJacksonAnnotation.class);\n\t\t\tif (jacksonAnnotation != null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"The class with \" + id + \" and name of \" + className\n\t\t\t\t\t+ \" is not in the allowlist. \"\n\t\t\t\t\t+ \"If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. \"\n\t\t\t\t\t+ \"If the serialization is only done by a trusted source, you can also enable default typing. \"\n\t\t\t\t\t+ \"See https://github.com/spring-projects/spring-security/issues/4370 for details\");\n\t\t}\n\n\t\tprivate boolean isInAllowlist(String id) {\n\t\t\treturn ALLOWLIST_CLASS_NAMES.contains(id);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDescForKnownTypeIds() {\n\t\t\treturn this.delegate.getDescForKnownTypeIds();\n\t\t}\n\n\t\t@Override\n\t\tpublic JsonTypeInfo.Id getMechanism() {\n\t\t\treturn this.delegate.getMechanism();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.SimpleGrantedAuthorityMixin} based on\n * Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tgetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@Deprecated(forRemoval = true)\npublic abstract class SimpleGrantedAuthorityMixin {\n\n\t/**\n\t * Mixin Constructor.\n\t * @param role the role\n\t */\n\t@JsonCreator\n\tpublic SimpleGrantedAuthorityMixin(@JsonProperty(\"authority\") String role) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Custom deserializer for {@link UnmodifiableListMixin}.\n *\n * @author Rob Winch\n * @author Hyunmin Choi\n * @since 5.0.2\n * @see UnmodifiableListMixin\n * @deprecated as of 7.0\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nclass UnmodifiableListDeserializer extends AbstractUnmodifiableCollectionDeserializer<List> {\n\n\t@Override\n\tList createUnmodifiableCollection(Collection<Object> values) {\n\t\treturn Collections.unmodifiableList(new ArrayList<>(values));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UnmodifiableListMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Set;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * This mixin class used to deserialize java.util.Collections$UnmodifiableRandomAccessList\n * and used with various AuthenticationToken implementation's mixin classes.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Rob Winch\n * @since 5.0.2\n * @see UnmodifiableListDeserializer\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonDeserialize(using = UnmodifiableListDeserializer.class)\n@Deprecated(forRemoval = true)\nclass UnmodifiableListMixin {\n\n\t/**\n\t * Mixin Constructor\n\t * @param s the Set\n\t */\n\t@JsonCreator\n\tUnmodifiableListMixin(Set<?> s) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UnmodifiableMapDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n/**\n * Custom deserializer for {@link UnmodifiableMapMixin}.\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see UnmodifiableMapMixin\n * @deprecated as of 7.0\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nclass UnmodifiableMapDeserializer extends JsonDeserializer<Map<?, ?>> {\n\n\t@Override\n\tpublic Map<?, ?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {\n\t\tObjectMapper mapper = (ObjectMapper) jp.getCodec();\n\t\tJsonNode node = mapper.readTree(jp);\n\n\t\tMap<String, Object> result = new LinkedHashMap<>();\n\t\tif (node != null && node.isObject()) {\n\t\t\tIterable<Map.Entry<String, JsonNode>> fields = node::fields;\n\t\t\tfor (Map.Entry<String, JsonNode> field : fields) {\n\t\t\t\tresult.put(field.getKey(), mapper.readValue(field.getValue().traverse(mapper), Object.class));\n\t\t\t}\n\t\t}\n\t\treturn Collections.unmodifiableMap(result);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UnmodifiableMapMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * This mixin class used to deserialize java.util.Collections$UnmodifiableMap and used\n * with various AuthenticationToken implementation's mixin classes.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see UnmodifiableMapDeserializer\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = UnmodifiableMapDeserializer.class)\n@Deprecated(forRemoval = true)\nclass UnmodifiableMapMixin {\n\n\t@JsonCreator\n\tUnmodifiableMapMixin(Map<?, ?> map) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UnmodifiableSetDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Custom deserializer for {@link UnmodifiableSetMixin}.\n *\n * @author Jitendra Singh\n * @author Hyunmin Choi\n * @since 4.2\n * @see UnmodifiableSetMixin\n * @deprecated as of 7.0\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nclass UnmodifiableSetDeserializer extends AbstractUnmodifiableCollectionDeserializer<Set> {\n\n\t@Override\n\tSet createUnmodifiableCollection(Collection<Object> values) {\n\t\treturn Collections.unmodifiableSet(new HashSet<>(values));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UnmodifiableSetMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Set;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * This mixin class used to deserialize java.util.Collections$UnmodifiableSet and used\n * with various AuthenticationToken implementation's mixin classes.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see UnmodifiableSetDeserializer\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonDeserialize(using = UnmodifiableSetDeserializer.class)\n@Deprecated(forRemoval = true)\nclass UnmodifiableSetMixin {\n\n\t/**\n\t * Mixin Constructor\n\t * @param s the Set\n\t */\n\t@JsonCreator\n\tUnmodifiableSetMixin(Set<?> s) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UserDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.MissingNode;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\n/**\n * Custom Deserializer for {@link User} class. This is already registered with\n * {@link UserMixin}. You can also use it directly with your mixin class.\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see UserMixin\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.UserDeserializer} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nclass UserDeserializer extends JsonDeserializer<User> {\n\n\tprivate static final TypeReference<Set<SimpleGrantedAuthority>> SIMPLE_GRANTED_AUTHORITY_SET = new TypeReference<>() {\n\t};\n\n\t/**\n\t * This method will create {@link User} object. It will ensure successful object\n\t * creation even if password key is null in serialized json, because credentials may\n\t * be removed from the {@link User} by invoking {@link User#eraseCredentials()}. In\n\t * that case there won't be any password key in serialized json.\n\t * @param jp the JsonParser\n\t * @param ctxt the DeserializationContext\n\t * @return the user\n\t * @throws IOException if a exception during IO occurs\n\t * @throws JsonProcessingException if an error during JSON processing occurs\n\t */\n\t@Override\n\tpublic User deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {\n\t\tObjectMapper mapper = (ObjectMapper) jp.getCodec();\n\t\tJsonNode jsonNode = mapper.readTree(jp);\n\t\tSet<? extends GrantedAuthority> authorities = mapper.convertValue(jsonNode.get(\"authorities\"),\n\t\t\t\tSIMPLE_GRANTED_AUTHORITY_SET);\n\t\tJsonNode passwordNode = readJsonNode(jsonNode, \"password\");\n\t\tString username = readJsonNode(jsonNode, \"username\").asText();\n\t\tString password = passwordNode.asText(\"\");\n\t\tboolean enabled = readJsonNode(jsonNode, \"enabled\").asBoolean();\n\t\tboolean accountNonExpired = readJsonNode(jsonNode, \"accountNonExpired\").asBoolean();\n\t\tboolean credentialsNonExpired = readJsonNode(jsonNode, \"credentialsNonExpired\").asBoolean();\n\t\tboolean accountNonLocked = readJsonNode(jsonNode, \"accountNonLocked\").asBoolean();\n\t\tUser result = new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked,\n\t\t\t\tauthorities);\n\t\tif (passwordNode.asText(null) == null) {\n\t\t\tresult.eraseCredentials();\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate JsonNode readJsonNode(JsonNode jsonNode, String field) {\n\t\treturn jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UserMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * This mixin class helps in serialize/deserialize\n * {@link org.springframework.security.core.userdetails.User}. This class also register a\n * custom deserializer {@link UserDeserializer} to deserialize User object successfully.\n * In order to use this mixin you need to register two more mixin classes in your\n * ObjectMapper configuration.\n * <ol>\n * <li>{@link SimpleGrantedAuthorityMixin}</li>\n * <li>{@link UnmodifiableSetMixin}</li>\n * </ol>\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see UserDeserializer\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.UserMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonDeserialize(using = UserDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@Deprecated(forRemoval = true)\nabstract class UserMixin {\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.MissingNode;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Custom deserializer for {@link UsernamePasswordAuthenticationToken}. At the time of\n * deserialization it will invoke suitable constructor depending on the value of\n * <b>authenticated</b> property. It will ensure that the token's state must not change.\n * <p>\n * This deserializer is already registered with\n * {@link UsernamePasswordAuthenticationTokenMixin} but you can also registered it with\n * your own mixin class.\n *\n * @author Jitendra Singh\n * @author Greg Turnquist\n * @author Onur Kagan Ozcan\n * @since 4.2\n * @see UsernamePasswordAuthenticationTokenMixin\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.UsernamePasswordAuthenticationTokenDeserializer}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nclass UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<UsernamePasswordAuthenticationToken> {\n\n\tprivate static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<>() {\n\t};\n\n\tprivate static final TypeReference<Object> OBJECT = new TypeReference<>() {\n\t};\n\n\t/**\n\t * This method construct {@link UsernamePasswordAuthenticationToken} object from\n\t * serialized json.\n\t * @param jp the JsonParser\n\t * @param ctxt the DeserializationContext\n\t * @return the user\n\t * @throws IOException if a exception during IO occurs\n\t * @throws JsonProcessingException if an error during JSON processing occurs\n\t */\n\t@Override\n\tpublic UsernamePasswordAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt)\n\t\t\tthrows IOException, JsonProcessingException {\n\t\tObjectMapper mapper = (ObjectMapper) jp.getCodec();\n\t\tJsonNode jsonNode = mapper.readTree(jp);\n\t\tboolean authenticated = readJsonNode(jsonNode, \"authenticated\").asBoolean();\n\t\tJsonNode principalNode = readJsonNode(jsonNode, \"principal\");\n\t\tObject principal = getPrincipal(mapper, principalNode);\n\t\tJsonNode credentialsNode = readJsonNode(jsonNode, \"credentials\");\n\t\tObject credentials = getCredentials(credentialsNode);\n\t\tList<GrantedAuthority> authorities = mapper.readValue(readJsonNode(jsonNode, \"authorities\").traverse(mapper),\n\t\t\t\tGRANTED_AUTHORITY_LIST);\n\t\tUsernamePasswordAuthenticationToken token = (!authenticated)\n\t\t\t\t? UsernamePasswordAuthenticationToken.unauthenticated(principal, credentials)\n\t\t\t\t: UsernamePasswordAuthenticationToken.authenticated(principal, credentials, authorities);\n\t\tJsonNode detailsNode = readJsonNode(jsonNode, \"details\");\n\t\tif (detailsNode.isNull() || detailsNode.isMissingNode()) {\n\t\t\ttoken.setDetails(null);\n\t\t}\n\t\telse {\n\t\t\tObject details = mapper.readValue(detailsNode.toString(), OBJECT);\n\t\t\ttoken.setDetails(details);\n\t\t}\n\t\treturn token;\n\t}\n\n\tprivate @Nullable Object getCredentials(JsonNode credentialsNode) {\n\t\tif (credentialsNode.isNull() || credentialsNode.isMissingNode()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn credentialsNode.asText();\n\t}\n\n\tprivate Object getPrincipal(ObjectMapper mapper, JsonNode principalNode)\n\t\t\tthrows IOException, JsonParseException, JsonMappingException {\n\t\tif (principalNode.isObject()) {\n\t\t\treturn mapper.readValue(principalNode.traverse(mapper), Object.class);\n\t\t}\n\t\treturn principalNode.asText();\n\t}\n\n\tprivate JsonNode readJsonNode(JsonNode jsonNode, String field) {\n\t\treturn jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * This mixin class is used to serialize / deserialize\n * {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}.\n * This class register a custom deserializer\n * {@link UsernamePasswordAuthenticationTokenDeserializer}.\n *\n * In order to use this mixin you'll need to add 3 more mixin classes.\n * <ol>\n * <li>{@link UnmodifiableSetMixin}</li>\n * <li>{@link SimpleGrantedAuthorityMixin}</li>\n * <li>{@link UserMixin}</li>\n * </ol>\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see CoreJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.jackson.UsernamePasswordAuthenticationTokenDeserializer}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = \"@class\")\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonDeserialize(using = UsernamePasswordAuthenticationTokenDeserializer.class)\n@Deprecated(forRemoval = true)\nabstract class UsernamePasswordAuthenticationTokenMixin {\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/jackson2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 2 serialization support.\n */\n@NullMarked\npackage org.springframework.security.jackson2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/provisioning/GroupManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.provisioning;\n\nimport java.util.List;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Allows management of groups of authorities and their members.\n * <p>\n * Typically this will be used to supplement the functionality of a\n * {@link UserDetailsManager} in situations where the organization of application granted\n * authorities into groups is preferred over a straight mapping of users to roles.\n * <p>\n * With this scenario, users are allocated to groups and take on the list of authorities\n * which are assigned to the group, providing more flexible administration options.\n *\n * @author Luke Taylor\n */\npublic interface GroupManager {\n\n\t/**\n\t * Returns the names of all groups that this group manager controls.\n\t */\n\tList<String> findAllGroups();\n\n\t/**\n\t * Locates the users who are members of a group\n\t * @param groupName the group whose members are required\n\t * @return the usernames of the group members\n\t */\n\tList<String> findUsersInGroup(String groupName);\n\n\t/**\n\t * Creates a new group with the specified list of authorities.\n\t * @param groupName the name for the new group\n\t * @param authorities the authorities which are to be allocated to this group.\n\t */\n\tvoid createGroup(String groupName, List<GrantedAuthority> authorities);\n\n\t/**\n\t * Removes a group, including all members and authorities.\n\t * @param groupName the group to remove.\n\t */\n\tvoid deleteGroup(String groupName);\n\n\t/**\n\t * Changes the name of a group without altering the assigned authorities or members.\n\t */\n\tvoid renameGroup(String oldName, String newName);\n\n\t/**\n\t * Makes a user a member of a particular group.\n\t * @param username the user to be given membership.\n\t * @param group the name of the group to which the user will be added.\n\t */\n\tvoid addUserToGroup(String username, String group);\n\n\t/**\n\t * Deletes a user's membership of a group.\n\t * @param username the user\n\t * @param groupName the group to remove them from\n\t */\n\tvoid removeUserFromGroup(String username, String groupName);\n\n\t/**\n\t * Obtains the list of authorities which are assigned to a group.\n\t */\n\tList<GrantedAuthority> findGroupAuthorities(String groupName);\n\n\t/**\n\t * Assigns a new authority to a group.\n\t */\n\tvoid addGroupAuthority(String groupName, GrantedAuthority authority);\n\n\t/**\n\t * Deletes an authority from those assigned to a group\n\t */\n\tvoid removeGroupAuthority(String groupName, GrantedAuthority authority);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/provisioning/InMemoryUserDetailsManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.provisioning;\n\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.CredentialsContainer;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.core.userdetails.memory.UserAttribute;\nimport org.springframework.security.core.userdetails.memory.UserAttributeEditor;\nimport org.springframework.util.Assert;\n\n/**\n * Non-persistent implementation of {@code UserDetailsManager} which is backed by an\n * in-memory map.\n * <p>\n * Mainly intended for testing and demonstration purposes, where a full blown persistent\n * system isn't required.\n *\n * @author Luke Taylor\n * @author Andrey Litvitski\n * @since 3.1\n */\npublic class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final Map<String, MutableUserDetails> users = new HashMap<>();\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable AuthenticationManager authenticationManager;\n\n\tpublic InMemoryUserDetailsManager() {\n\t}\n\n\tpublic InMemoryUserDetailsManager(Collection<UserDetails> users) {\n\t\tfor (UserDetails user : users) {\n\t\t\tcreateUser(user);\n\t\t}\n\t}\n\n\tpublic InMemoryUserDetailsManager(UserDetails... users) {\n\t\tfor (UserDetails user : users) {\n\t\t\tcreateUser(user);\n\t\t}\n\t}\n\n\tpublic InMemoryUserDetailsManager(Properties users) {\n\t\tEnumeration<?> names = users.propertyNames();\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\twhile (names.hasMoreElements()) {\n\t\t\tString name = (String) names.nextElement();\n\t\t\teditor.setAsText(users.getProperty(name));\n\t\t\tUserAttribute attr = (UserAttribute) editor.getValue();\n\t\t\tAssert.notNull(attr,\n\t\t\t\t\t() -> \"The entry with username '\" + name + \"' could not be converted to an UserDetails\");\n\t\t\tcreateUser(createUserDetails(name, attr));\n\t\t}\n\t}\n\n\tprivate User createUserDetails(String name, UserAttribute attr) {\n\t\treturn new User(name, attr.getPassword(), attr.isEnabled(), true, true, true, attr.getAuthorities());\n\t}\n\n\t@Override\n\tpublic void createUser(UserDetails user) {\n\t\tAssert.isTrue(!userExists(user.getUsername()), \"user should not exist\");\n\t\tif (user instanceof MutableUserDetails mutable) {\n\t\t\tthis.users.put(user.getUsername().toLowerCase(Locale.ROOT), mutable);\n\t\t}\n\t\telse {\n\t\t\tthis.users.put(user.getUsername().toLowerCase(Locale.ROOT), new MutableUser(user));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void deleteUser(String username) {\n\t\tthis.users.remove(username.toLowerCase(Locale.ROOT));\n\t}\n\n\t@Override\n\tpublic void updateUser(UserDetails user) {\n\t\tAssert.isTrue(userExists(user.getUsername()), \"user should exist\");\n\t\tif (user instanceof MutableUserDetails mutable) {\n\t\t\tthis.users.put(user.getUsername().toLowerCase(Locale.ROOT), mutable);\n\t\t}\n\t\telse {\n\t\t\tthis.users.put(user.getUsername().toLowerCase(Locale.ROOT), new MutableUser(user));\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean userExists(String username) {\n\t\treturn this.users.containsKey(username.toLowerCase(Locale.ROOT));\n\t}\n\n\t@Override\n\tpublic void changePassword(@Nullable String oldPassword, @Nullable String newPassword) {\n\t\tAuthentication currentUser = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (currentUser == null) {\n\t\t\t// This would indicate bad coding somewhere\n\t\t\tthrow new AccessDeniedException(\n\t\t\t\t\t\"Can't change password as no Authentication object found in context \" + \"for current user.\");\n\t\t}\n\t\tString username = currentUser.getName();\n\t\tthis.logger.debug(LogMessage.format(\"Changing password for user '%s'\", username));\n\t\t// If an authentication manager has been set, re-authenticate the user with the\n\t\t// supplied password.\n\t\tif (this.authenticationManager != null) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Reauthenticating user '%s' for password change request.\", username));\n\t\t\tthis.authenticationManager\n\t\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(username, oldPassword));\n\t\t}\n\t\telse {\n\t\t\tthis.logger.debug(\"No authentication manager set. Password won't be re-checked.\");\n\t\t}\n\t\tMutableUserDetails user = this.users.get(username);\n\t\tAssert.state(user != null, \"Current user doesn't exist in database.\");\n\t\tuser.setPassword(newPassword);\n\t}\n\n\t@Override\n\tpublic UserDetails updatePassword(UserDetails user, @Nullable String newPassword) {\n\t\tString username = user.getUsername();\n\t\tMutableUserDetails mutableUser = this.users.get(username.toLowerCase(Locale.ROOT));\n\t\tif (mutableUser == null) {\n\t\t\tthrow new RuntimeException(\"user '\" + username + \"' does not exist\");\n\t\t}\n\t\tmutableUser.setPassword(newPassword);\n\t\treturn mutableUser;\n\t}\n\n\t@Override\n\tpublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n\t\tUserDetails user = this.users.get(username.toLowerCase(Locale.ROOT));\n\t\tif (user == null) {\n\t\t\tthrow UsernameNotFoundException.fromUsername(username);\n\t\t}\n\t\tif (user instanceof CredentialsContainer) {\n\t\t\treturn user;\n\t\t}\n\t\treturn new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),\n\t\t\t\tuser.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tpublic void setAuthenticationManager(AuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/provisioning/JdbcUserDetailsManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.provisioning;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport javax.sql.DataSource;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContextException;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.dao.EmptyResultDataAccessException;\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.cache.NullUserCache;\nimport org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;\nimport org.springframework.util.Assert;\n\n/**\n * Jdbc user management service, based on the same table structure as its parent class,\n * <tt>JdbcDaoImpl</tt>.\n * <p>\n * Provides CRUD operations for both users and groups. Note that if the\n * {@link #setEnableAuthorities(boolean) enableAuthorities} property is set to false,\n * calls to createUser, updateUser and deleteUser will not store the authorities from the\n * <tt>UserDetails</tt> or delete authorities for the user. Since this class cannot\n * differentiate between authorities which were loaded for an individual or for a group of\n * which the individual is a member, it's important that you take this into account when\n * using this implementation for managing your users.\n *\n * @author Luke Taylor\n * @author Junhyeok Lee\n * @author Andrey Litvitski\n * @since 2.0\n */\npublic class JdbcUserDetailsManager extends JdbcDaoImpl\n\t\timplements UserDetailsManager, GroupManager, UserDetailsPasswordService {\n\n\tpublic static final String DEF_CREATE_USER_SQL = \"insert into users (username, password, enabled) values (?,?,?)\";\n\n\tpublic static final String DEF_DELETE_USER_SQL = \"delete from users where username = ?\";\n\n\tpublic static final String DEF_UPDATE_USER_SQL = \"update users set password = ?, enabled = ? where username = ?\";\n\n\tpublic static final String DEF_INSERT_AUTHORITY_SQL = \"insert into authorities (username, authority) values (?,?)\";\n\n\tpublic static final String DEF_DELETE_USER_AUTHORITIES_SQL = \"delete from authorities where username = ?\";\n\n\tpublic static final String DEF_USER_EXISTS_SQL = \"select count(*) from users where username = ?\";\n\n\tpublic static final String DEF_CHANGE_PASSWORD_SQL = \"update users set password = ? where username = ?\";\n\n\tpublic static final String DEF_FIND_GROUPS_SQL = \"select group_name from groups\";\n\n\tpublic static final String DEF_FIND_USERS_IN_GROUP_SQL = \"select username from group_members gm, groups g \"\n\t\t\t+ \"where gm.group_id = g.id and g.group_name = ?\";\n\n\tpublic static final String DEF_INSERT_GROUP_SQL = \"insert into groups (group_name) values (?)\";\n\n\tpublic static final String DEF_FIND_GROUP_ID_SQL = \"select id from groups where group_name = ?\";\n\n\tpublic static final String DEF_INSERT_GROUP_AUTHORITY_SQL = \"insert into group_authorities (group_id, authority) values (?,?)\";\n\n\tpublic static final String DEF_DELETE_GROUP_SQL = \"delete from groups where id = ?\";\n\n\tpublic static final String DEF_DELETE_GROUP_AUTHORITIES_SQL = \"delete from group_authorities where group_id = ?\";\n\n\tpublic static final String DEF_DELETE_GROUP_MEMBERS_SQL = \"delete from group_members where group_id = ?\";\n\n\tpublic static final String DEF_RENAME_GROUP_SQL = \"update groups set group_name = ? where group_name = ?\";\n\n\tpublic static final String DEF_INSERT_GROUP_MEMBER_SQL = \"insert into group_members (group_id, username) values (?,?)\";\n\n\tpublic static final String DEF_DELETE_GROUP_MEMBER_SQL = \"delete from group_members where group_id = ? and username = ?\";\n\n\tpublic static final String DEF_GROUP_AUTHORITIES_QUERY_SQL = \"select g.id, g.group_name, ga.authority \"\n\t\t\t+ \"from groups g, group_authorities ga \" + \"where g.group_name = ? \" + \"and g.id = ga.group_id \";\n\n\tpublic static final String DEF_DELETE_GROUP_AUTHORITY_SQL = \"delete from group_authorities where group_id = ? and authority = ?\";\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate String createUserSql = DEF_CREATE_USER_SQL;\n\n\tprivate String deleteUserSql = DEF_DELETE_USER_SQL;\n\n\tprivate String updateUserSql = DEF_UPDATE_USER_SQL;\n\n\tprivate String createAuthoritySql = DEF_INSERT_AUTHORITY_SQL;\n\n\tprivate String deleteUserAuthoritiesSql = DEF_DELETE_USER_AUTHORITIES_SQL;\n\n\tprivate String userExistsSql = DEF_USER_EXISTS_SQL;\n\n\tprivate String changePasswordSql = DEF_CHANGE_PASSWORD_SQL;\n\n\tprivate String findAllGroupsSql = DEF_FIND_GROUPS_SQL;\n\n\tprivate String findUsersInGroupSql = DEF_FIND_USERS_IN_GROUP_SQL;\n\n\tprivate String insertGroupSql = DEF_INSERT_GROUP_SQL;\n\n\tprivate String findGroupIdSql = DEF_FIND_GROUP_ID_SQL;\n\n\tprivate String insertGroupAuthoritySql = DEF_INSERT_GROUP_AUTHORITY_SQL;\n\n\tprivate String deleteGroupSql = DEF_DELETE_GROUP_SQL;\n\n\tprivate String deleteGroupAuthoritiesSql = DEF_DELETE_GROUP_AUTHORITIES_SQL;\n\n\tprivate String deleteGroupMembersSql = DEF_DELETE_GROUP_MEMBERS_SQL;\n\n\tprivate String renameGroupSql = DEF_RENAME_GROUP_SQL;\n\n\tprivate String insertGroupMemberSql = DEF_INSERT_GROUP_MEMBER_SQL;\n\n\tprivate String deleteGroupMemberSql = DEF_DELETE_GROUP_MEMBER_SQL;\n\n\tprivate String groupAuthoritiesSql = DEF_GROUP_AUTHORITIES_QUERY_SQL;\n\n\tprivate String deleteGroupAuthoritySql = DEF_DELETE_GROUP_AUTHORITY_SQL;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable AuthenticationManager authenticationManager;\n\n\tprivate UserCache userCache = new NullUserCache();\n\n\tprivate RowMapper<UserDetails> userDetailsMapper = this::mapToUser;\n\n\tprivate RowMapper<GrantedAuthority> grantedAuthorityMapper = this::mapToGrantedAuthority;\n\n\tprivate boolean enableUpdatePassword = false;\n\n\tpublic JdbcUserDetailsManager() {\n\t}\n\n\tpublic JdbcUserDetailsManager(DataSource dataSource) {\n\t\tsetDataSource(dataSource);\n\t}\n\n\t/**\n\t * Sets the {@code RowMapper} to convert each user result row into a\n\t * {@link UserDetails} object.\n\t *\n\t * The default mapper expects columns with names like 'username', 'password',\n\t * 'enabled', etc., and maps them directly to the corresponding UserDetails\n\t * properties.\n\t * @param mapper the {@code RowMapper} to use for mapping rows in the database, must\n\t * not be null\n\t * @since 6.5\n\t */\n\tpublic void setUserDetailsMapper(RowMapper<UserDetails> mapper) {\n\t\tAssert.notNull(mapper, \"userDetailsMapper cannot be null\");\n\t\tthis.userDetailsMapper = mapper;\n\t}\n\n\t/**\n\t * Sets the {@code RowMapper} to convert each authority result row into a\n\t * {@link GrantedAuthority} object.\n\t *\n\t * The default mapper expects columns with names like 'authority' or 'role', and maps\n\t * them directly to SimpleGrantedAuthority objects.\n\t * @param mapper the {@code RowMapper} to use for mapping rows in the database to\n\t * GrantedAuthority objects, must not be null\n\t * @since 6.5\n\t */\n\tpublic void setGrantedAuthorityMapper(RowMapper<GrantedAuthority> mapper) {\n\t\tAssert.notNull(mapper, \"grantedAuthorityMapper cannot be null\");\n\t\tthis.grantedAuthorityMapper = mapper;\n\t}\n\n\t@Override\n\tprotected void initDao() throws ApplicationContextException {\n\t\tif (this.authenticationManager == null) {\n\t\t\tthis.logger.info(\n\t\t\t\t\t\"No authentication manager set. Reauthentication of users when changing passwords will not be performed.\");\n\t\t}\n\t\tsuper.initDao();\n\t}\n\n\t/**\n\t * Executes the SQL <tt>usersByUsernameQuery</tt> and returns a list of UserDetails\n\t * objects. There should normally only be one matching user.\n\t */\n\t@Override\n\tprotected List<UserDetails> loadUsersByUsername(String username) {\n\t\treturn requireJdbcTemplate().query(getUsersByUsernameQuery(), this.userDetailsMapper, username);\n\t}\n\n\tprivate UserDetails mapToUser(ResultSet rs, int rowNum) throws SQLException {\n\t\tString userName = rs.getString(1);\n\t\tString password = rs.getString(2);\n\t\tboolean enabled = rs.getBoolean(3);\n\t\tboolean accLocked = false;\n\t\tboolean accExpired = false;\n\t\tboolean credsExpired = false;\n\t\tif (rs.getMetaData().getColumnCount() > 3) {\n\t\t\t// NOTE: acc_locked, acc_expired and creds_expired are also to be loaded\n\t\t\taccLocked = rs.getBoolean(4);\n\t\t\taccExpired = rs.getBoolean(5);\n\t\t\tcredsExpired = rs.getBoolean(6);\n\t\t}\n\t\treturn new User(userName, password, enabled, !accExpired, !credsExpired, !accLocked,\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t}\n\n\t@Override\n\tpublic void createUser(final UserDetails user) {\n\t\tvalidateUserDetails(user);\n\t\trequireJdbcTemplate().update(this.createUserSql, (ps) -> {\n\t\t\tps.setString(1, user.getUsername());\n\t\t\tps.setString(2, user.getPassword());\n\t\t\tps.setBoolean(3, user.isEnabled());\n\t\t\tint paramCount = ps.getParameterMetaData().getParameterCount();\n\t\t\tif (paramCount > 3) {\n\t\t\t\t// NOTE: acc_locked, acc_expired and creds_expired are also to be inserted\n\t\t\t\tps.setBoolean(4, !user.isAccountNonLocked());\n\t\t\t\tps.setBoolean(5, !user.isAccountNonExpired());\n\t\t\t\tps.setBoolean(6, !user.isCredentialsNonExpired());\n\t\t\t}\n\t\t});\n\t\tif (getEnableAuthorities()) {\n\t\t\tinsertUserAuthorities(user);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updateUser(final UserDetails user) {\n\t\tvalidateUserDetails(user);\n\t\trequireJdbcTemplate().update(this.updateUserSql, (ps) -> {\n\t\t\tps.setString(1, user.getPassword());\n\t\t\tps.setBoolean(2, user.isEnabled());\n\t\t\tint paramCount = ps.getParameterMetaData().getParameterCount();\n\t\t\tif (paramCount == 3) {\n\t\t\t\tps.setString(3, user.getUsername());\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// NOTE: acc_locked, acc_expired and creds_expired are also updated\n\t\t\t\tps.setBoolean(3, !user.isAccountNonLocked());\n\t\t\t\tps.setBoolean(4, !user.isAccountNonExpired());\n\t\t\t\tps.setBoolean(5, !user.isCredentialsNonExpired());\n\t\t\t\tps.setString(6, user.getUsername());\n\t\t\t}\n\t\t});\n\t\tif (getEnableAuthorities()) {\n\t\t\tdeleteUserAuthorities(user.getUsername());\n\t\t\tinsertUserAuthorities(user);\n\t\t}\n\t\tthis.userCache.removeUserFromCache(user.getUsername());\n\t}\n\n\tprivate void insertUserAuthorities(UserDetails user) {\n\t\tfor (GrantedAuthority auth : user.getAuthorities()) {\n\t\t\trequireJdbcTemplate().update(this.createAuthoritySql, user.getUsername(), auth.getAuthority());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void deleteUser(String username) {\n\t\tif (getEnableAuthorities()) {\n\t\t\tdeleteUserAuthorities(username);\n\t\t}\n\t\trequireJdbcTemplate().update(this.deleteUserSql, username);\n\t\tthis.userCache.removeUserFromCache(username);\n\t}\n\n\tprivate void deleteUserAuthorities(String username) {\n\t\trequireJdbcTemplate().update(this.deleteUserAuthoritiesSql, username);\n\t}\n\n\t@Override\n\tpublic void changePassword(@Nullable String oldPassword, @Nullable String newPassword)\n\t\t\tthrows AuthenticationException {\n\t\tAuthentication currentUser = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (currentUser == null) {\n\t\t\t// This would indicate bad coding somewhere\n\t\t\tthrow new AccessDeniedException(\n\t\t\t\t\t\"Can't change password as no Authentication object found in context \" + \"for current user.\");\n\t\t}\n\t\tString username = currentUser.getName();\n\t\t// If an authentication manager has been set, re-authenticate the user with the\n\t\t// supplied password.\n\t\tif (this.authenticationManager != null) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Reauthenticating user '%s' for password change request.\", username));\n\t\t\tthis.authenticationManager\n\t\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(username, oldPassword));\n\t\t}\n\t\telse {\n\t\t\tthis.logger.debug(\"No authentication manager set. Password won't be re-checked.\");\n\t\t}\n\t\tthis.logger.debug(\"Changing password for user '\" + username + \"'\");\n\t\trequireJdbcTemplate().update(this.changePasswordSql, newPassword, username);\n\t\tAuthentication authentication = createNewAuthentication(currentUser, newPassword);\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\tthis.userCache.removeUserFromCache(username);\n\t}\n\n\tprotected Authentication createNewAuthentication(Authentication currentAuth, @Nullable String newPassword) {\n\t\tUserDetails user = loadUserByUsername(currentAuth.getName());\n\t\tUsernamePasswordAuthenticationToken newAuthentication = UsernamePasswordAuthenticationToken.authenticated(user,\n\t\t\t\tnull, user.getAuthorities());\n\t\tnewAuthentication.setDetails(currentAuth.getDetails());\n\t\treturn newAuthentication;\n\t}\n\n\t@Override\n\tpublic boolean userExists(String username) {\n\t\t@SuppressWarnings(\"ConstantConditions\")\n\t\tInteger usersCount = requireJdbcTemplate().queryForObject(this.userExistsSql, Integer.class, username);\n\t\tif (usersCount == null || usersCount > 1) {\n\t\t\tthrow new IncorrectResultSizeDataAccessException(\n\t\t\t\t\t\"[\" + usersCount + \"] users found with name '\" + username + \"', expected 1\", 1);\n\t\t}\n\t\treturn usersCount == 1;\n\t}\n\n\t@Override\n\tpublic List<String> findAllGroups() {\n\t\t// @formatter:off\n\t\treturn requireJdbcTemplate().queryForList(this.findAllGroupsSql, String.class)\n\t\t\t.stream()\n\t\t\t.filter(Objects::nonNull)\n\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic List<String> findUsersInGroup(String groupName) {\n\t\tAssert.hasText(groupName, \"groupName should have text\");\n\t\t// @formatter:off\n\t\treturn requireJdbcTemplate().queryForList(this.findUsersInGroupSql, String.class, groupName)\n\t\t\t.stream()\n\t\t\t.filter(Objects::nonNull)\n\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic void createGroup(final String groupName, final List<GrantedAuthority> authorities) {\n\t\tAssert.hasText(groupName, \"groupName should have text\");\n\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\tthis.logger.debug(\"Creating new group '\" + groupName + \"' with authorities \"\n\t\t\t\t+ AuthorityUtils.authorityListToSet(authorities));\n\t\trequireJdbcTemplate().update(this.insertGroupSql, groupName);\n\t\tint groupId = findGroupId(groupName);\n\t\tfor (GrantedAuthority a : authorities) {\n\t\t\tString authority = a.getAuthority();\n\t\t\trequireJdbcTemplate().update(this.insertGroupAuthoritySql, (ps) -> {\n\t\t\t\tps.setInt(1, groupId);\n\t\t\t\tps.setString(2, authority);\n\t\t\t});\n\t\t}\n\t}\n\n\t@Override\n\tpublic void deleteGroup(String groupName) {\n\t\tthis.logger.debug(\"Deleting group '\" + groupName + \"'\");\n\t\tAssert.hasText(groupName, \"groupName should have text\");\n\t\tint id = findGroupId(groupName);\n\t\tPreparedStatementSetter groupIdPSS = (ps) -> ps.setInt(1, id);\n\t\trequireJdbcTemplate().update(this.deleteGroupMembersSql, groupIdPSS);\n\t\trequireJdbcTemplate().update(this.deleteGroupAuthoritiesSql, groupIdPSS);\n\t\trequireJdbcTemplate().update(this.deleteGroupSql, groupIdPSS);\n\t}\n\n\t@Override\n\tpublic void renameGroup(String oldName, String newName) {\n\t\tthis.logger.debug(\"Changing group name from '\" + oldName + \"' to '\" + newName + \"'\");\n\t\tAssert.hasText(oldName, \"oldName should have text\");\n\t\tAssert.hasText(newName, \"newName should have text\");\n\t\trequireJdbcTemplate().update(this.renameGroupSql, newName, oldName);\n\t}\n\n\t@Override\n\tpublic void addUserToGroup(final String username, final String groupName) {\n\t\tthis.logger.debug(\"Adding user '\" + username + \"' to group '\" + groupName + \"'\");\n\t\tAssert.hasText(username, \"username should have text\");\n\t\tAssert.hasText(groupName, \"groupName should have text\");\n\t\tint id = findGroupId(groupName);\n\t\trequireJdbcTemplate().update(this.insertGroupMemberSql, (ps) -> {\n\t\t\tps.setInt(1, id);\n\t\t\tps.setString(2, username);\n\t\t});\n\t\tthis.userCache.removeUserFromCache(username);\n\t}\n\n\t@Override\n\tpublic void removeUserFromGroup(final String username, final String groupName) {\n\t\tthis.logger.debug(\"Removing user '\" + username + \"' to group '\" + groupName + \"'\");\n\t\tAssert.hasText(username, \"username should have text\");\n\t\tAssert.hasText(groupName, \"groupName should have text\");\n\t\tint id = findGroupId(groupName);\n\t\trequireJdbcTemplate().update(this.deleteGroupMemberSql, (ps) -> {\n\t\t\tps.setInt(1, id);\n\t\t\tps.setString(2, username);\n\t\t});\n\t\tthis.userCache.removeUserFromCache(username);\n\t}\n\n\t@Override\n\tpublic List<GrantedAuthority> findGroupAuthorities(String groupName) {\n\t\tthis.logger.debug(\"Loading authorities for group '\" + groupName + \"'\");\n\t\tAssert.hasText(groupName, \"groupName should have text\");\n\t\treturn requireJdbcTemplate().query(this.groupAuthoritiesSql, this.grantedAuthorityMapper, groupName);\n\t}\n\n\tprivate GrantedAuthority mapToGrantedAuthority(ResultSet rs, int rowNum) throws SQLException {\n\t\tString roleName = getRolePrefix() + rs.getString(3);\n\t\treturn new SimpleGrantedAuthority(roleName);\n\t}\n\n\t@Override\n\tpublic void removeGroupAuthority(String groupName, final GrantedAuthority authority) {\n\t\tthis.logger.debug(\"Removing authority '\" + authority + \"' from group '\" + groupName + \"'\");\n\t\tAssert.hasText(groupName, \"groupName should have text\");\n\t\tAssert.notNull(authority, \"authority cannot be null\");\n\t\tint id = findGroupId(groupName);\n\t\trequireJdbcTemplate().update(this.deleteGroupAuthoritySql, (ps) -> {\n\t\t\tps.setInt(1, id);\n\t\t\tps.setString(2, authority.getAuthority());\n\t\t});\n\t}\n\n\t@Override\n\tpublic void addGroupAuthority(final String groupName, final GrantedAuthority authority) {\n\t\tthis.logger.debug(\"Adding authority '\" + authority + \"' to group '\" + groupName + \"'\");\n\t\tAssert.hasText(groupName, \"groupName should have text\");\n\t\tAssert.notNull(authority, \"authority cannot be null\");\n\t\tint id = findGroupId(groupName);\n\t\trequireJdbcTemplate().update(this.insertGroupAuthoritySql, (ps) -> {\n\t\t\tps.setInt(1, id);\n\t\t\tps.setString(2, authority.getAuthority());\n\t\t});\n\t}\n\n\tprivate int findGroupId(String group) {\n\t\tInteger groupId = requireJdbcTemplate().queryForObject(this.findGroupIdSql, Integer.class, group);\n\t\tif (groupId == null) {\n\t\t\tthrow new EmptyResultDataAccessException(\"Could not find required group '\" + group + \"'\", 1);\n\t\t}\n\t\treturn groupId;\n\t}\n\n\tprivate JdbcTemplate requireJdbcTemplate() {\n\t\tJdbcTemplate jdbc = getJdbcTemplate();\n\t\tAssert.notNull(jdbc, \"JdbcTemplate cannot be null\");\n\t\treturn jdbc;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tpublic void setAuthenticationManager(AuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\tpublic void setCreateUserSql(String createUserSql) {\n\t\tAssert.hasText(createUserSql, \"createUserSql should have text\");\n\t\tthis.createUserSql = createUserSql;\n\t}\n\n\tpublic void setDeleteUserSql(String deleteUserSql) {\n\t\tAssert.hasText(deleteUserSql, \"deleteUserSql should have text\");\n\t\tthis.deleteUserSql = deleteUserSql;\n\t}\n\n\tpublic void setUpdateUserSql(String updateUserSql) {\n\t\tAssert.hasText(updateUserSql, \"updateUserSql should have text\");\n\t\tthis.updateUserSql = updateUserSql;\n\t}\n\n\tpublic void setCreateAuthoritySql(String createAuthoritySql) {\n\t\tAssert.hasText(createAuthoritySql, \"createAuthoritySql should have text\");\n\t\tthis.createAuthoritySql = createAuthoritySql;\n\t}\n\n\tpublic void setDeleteUserAuthoritiesSql(String deleteUserAuthoritiesSql) {\n\t\tAssert.hasText(deleteUserAuthoritiesSql, \"deleteUserAuthoritiesSql should have text\");\n\t\tthis.deleteUserAuthoritiesSql = deleteUserAuthoritiesSql;\n\t}\n\n\tpublic void setUserExistsSql(String userExistsSql) {\n\t\tAssert.hasText(userExistsSql, \"userExistsSql should have text\");\n\t\tthis.userExistsSql = userExistsSql;\n\t}\n\n\tpublic void setChangePasswordSql(String changePasswordSql) {\n\t\tAssert.hasText(changePasswordSql, \"changePasswordSql should have text\");\n\t\tthis.changePasswordSql = changePasswordSql;\n\t}\n\n\tpublic void setFindAllGroupsSql(String findAllGroupsSql) {\n\t\tAssert.hasText(findAllGroupsSql, \"findAllGroupsSql should have text\");\n\t\tthis.findAllGroupsSql = findAllGroupsSql;\n\t}\n\n\tpublic void setFindUsersInGroupSql(String findUsersInGroupSql) {\n\t\tAssert.hasText(findUsersInGroupSql, \"findUsersInGroupSql should have text\");\n\t\tthis.findUsersInGroupSql = findUsersInGroupSql;\n\t}\n\n\tpublic void setInsertGroupSql(String insertGroupSql) {\n\t\tAssert.hasText(insertGroupSql, \"insertGroupSql should have text\");\n\t\tthis.insertGroupSql = insertGroupSql;\n\t}\n\n\tpublic void setFindGroupIdSql(String findGroupIdSql) {\n\t\tAssert.hasText(findGroupIdSql, \"findGroupIdSql should have text\");\n\t\tthis.findGroupIdSql = findGroupIdSql;\n\t}\n\n\tpublic void setInsertGroupAuthoritySql(String insertGroupAuthoritySql) {\n\t\tAssert.hasText(insertGroupAuthoritySql, \"insertGroupAuthoritySql should have text\");\n\t\tthis.insertGroupAuthoritySql = insertGroupAuthoritySql;\n\t}\n\n\tpublic void setDeleteGroupSql(String deleteGroupSql) {\n\t\tAssert.hasText(deleteGroupSql, \"deleteGroupSql should have text\");\n\t\tthis.deleteGroupSql = deleteGroupSql;\n\t}\n\n\tpublic void setDeleteGroupAuthoritiesSql(String deleteGroupAuthoritiesSql) {\n\t\tAssert.hasText(deleteGroupAuthoritiesSql, \"deleteGroupAuthoritiesSql should have text\");\n\t\tthis.deleteGroupAuthoritiesSql = deleteGroupAuthoritiesSql;\n\t}\n\n\tpublic void setDeleteGroupMembersSql(String deleteGroupMembersSql) {\n\t\tAssert.hasText(deleteGroupMembersSql, \"deleteGroupMembersSql should have text\");\n\t\tthis.deleteGroupMembersSql = deleteGroupMembersSql;\n\t}\n\n\tpublic void setRenameGroupSql(String renameGroupSql) {\n\t\tAssert.hasText(renameGroupSql, \"renameGroupSql should have text\");\n\t\tthis.renameGroupSql = renameGroupSql;\n\t}\n\n\tpublic void setInsertGroupMemberSql(String insertGroupMemberSql) {\n\t\tAssert.hasText(insertGroupMemberSql, \"insertGroupMemberSql should have text\");\n\t\tthis.insertGroupMemberSql = insertGroupMemberSql;\n\t}\n\n\tpublic void setDeleteGroupMemberSql(String deleteGroupMemberSql) {\n\t\tAssert.hasText(deleteGroupMemberSql, \"deleteGroupMemberSql should have text\");\n\t\tthis.deleteGroupMemberSql = deleteGroupMemberSql;\n\t}\n\n\tpublic void setGroupAuthoritiesSql(String groupAuthoritiesSql) {\n\t\tAssert.hasText(groupAuthoritiesSql, \"groupAuthoritiesSql should have text\");\n\t\tthis.groupAuthoritiesSql = groupAuthoritiesSql;\n\t}\n\n\tpublic void setDeleteGroupAuthoritySql(String deleteGroupAuthoritySql) {\n\t\tAssert.hasText(deleteGroupAuthoritySql, \"deleteGroupAuthoritySql should have text\");\n\t\tthis.deleteGroupAuthoritySql = deleteGroupAuthoritySql;\n\t}\n\n\t/**\n\t * Optionally sets the UserCache if one is in use in the application. This allows the\n\t * user to be removed from the cache after updates have taken place to avoid stale\n\t * data.\n\t * @param userCache the cache used by the AuthenticationManager.\n\t */\n\tpublic void setUserCache(UserCache userCache) {\n\t\tAssert.notNull(userCache, \"userCache cannot be null\");\n\t\tthis.userCache = userCache;\n\t}\n\n\t/**\n\t * Sets whether the {@link #updatePassword(UserDetails, String)} method should\n\t * actually update the password.\n\t * <p>\n\t * Defaults to {@code false} to prevent accidental password updates that might produce\n\t * passwords that are too large for the current database schema. Users must explicitly\n\t * set this to {@code true} to enable password updates.\n\t * @param enableUpdatePassword {@code true} to enable password updates, {@code false}\n\t * otherwise.\n\t * @since 7.0\n\t */\n\tpublic void setEnableUpdatePassword(boolean enableUpdatePassword) {\n\t\tthis.enableUpdatePassword = enableUpdatePassword;\n\t}\n\n\tprivate void validateUserDetails(UserDetails user) {\n\t\tAssert.hasText(user.getUsername(), \"Username may not be empty or null\");\n\t\tvalidateAuthorities(user.getAuthorities());\n\t}\n\n\tprivate void validateAuthorities(Collection<? extends GrantedAuthority> authorities) {\n\t\tAssert.notNull(authorities, \"Authorities list must not be null\");\n\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\tAssert.notNull(authority, \"Authorities list contains a null entry\");\n\t\t\tAssert.hasText(authority.getAuthority(), \"getAuthority() method must return a non-empty string\");\n\t\t}\n\t}\n\n\t/**\n\t * Conditionally updates password based on the setting from\n\t * {@link #setEnableUpdatePassword(boolean)}. {@inheritDoc}\n\t * @since 7.0\n\t */\n\t@Override\n\tpublic UserDetails updatePassword(UserDetails user, @Nullable String newPassword) {\n\t\tif (this.enableUpdatePassword) {\n\t\t\t// @formatter:off\n\t\t\tUserDetails updated = User.withUserDetails(user)\n\t\t\t\t.password(newPassword)\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tupdateUser(updated);\n\t\t\treturn updated;\n\t\t}\n\t\treturn user;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/provisioning/MutableUser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.provisioning;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * @author Luke Taylor\n * @since 3.1\n */\nclass MutableUser implements MutableUserDetails {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate @Nullable String password;\n\n\tprivate final UserDetails delegate;\n\n\tMutableUser(UserDetails user) {\n\t\tthis.delegate = user;\n\t\tthis.password = user.getPassword();\n\t}\n\n\t@Override\n\tpublic @Nullable String getPassword() {\n\t\treturn this.password;\n\t}\n\n\t@Override\n\tpublic void setPassword(@Nullable String password) {\n\t\tthis.password = password;\n\t}\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\treturn this.delegate.getAuthorities();\n\t}\n\n\t@Override\n\tpublic String getUsername() {\n\t\treturn this.delegate.getUsername();\n\t}\n\n\t@Override\n\tpublic boolean isAccountNonExpired() {\n\t\treturn this.delegate.isAccountNonExpired();\n\t}\n\n\t@Override\n\tpublic boolean isAccountNonLocked() {\n\t\treturn this.delegate.isAccountNonLocked();\n\t}\n\n\t@Override\n\tpublic boolean isCredentialsNonExpired() {\n\t\treturn this.delegate.isCredentialsNonExpired();\n\t}\n\n\t@Override\n\tpublic boolean isEnabled() {\n\t\treturn this.delegate.isEnabled();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/provisioning/MutableUserDetails.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.provisioning;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * @author Luke Taylor\n * @since 3.1\n */\ninterface MutableUserDetails extends UserDetails {\n\n\tvoid setPassword(@Nullable String password);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/provisioning/UserDetailsManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.provisioning;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\n/**\n * An extension of the {@link UserDetailsService} which provides the ability to create new\n * users and update existing ones.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic interface UserDetailsManager extends UserDetailsService {\n\n\t/**\n\t * Create a new user with the supplied details.\n\t */\n\tvoid createUser(UserDetails user);\n\n\t/**\n\t * Update the specified user.\n\t */\n\tvoid updateUser(UserDetails user);\n\n\t/**\n\t * Remove the user with the given login name from the system.\n\t */\n\tvoid deleteUser(String username);\n\n\t/**\n\t * Modify the current user's password. This should change the user's password in the\n\t * persistent user repository (database, LDAP etc).\n\t * @param oldPassword current password (for re-authentication if required)\n\t * @param newPassword the password to change to\n\t */\n\tvoid changePassword(@Nullable String oldPassword, @Nullable String newPassword);\n\n\t/**\n\t * Check if a user with the supplied login name exists in the system.\n\t */\n\tboolean userExists(String username);\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/provisioning/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Contains simple user and authority group account provisioning interfaces together with\n * a a JDBC-based implementation.\n */\n@NullMarked\npackage org.springframework.security.provisioning;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.scheduling;\n\nimport java.util.concurrent.Callable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.task.AsyncTaskExecutor;\nimport org.springframework.scheduling.SchedulingTaskExecutor;\nimport org.springframework.security.concurrent.DelegatingSecurityContextCallable;\nimport org.springframework.security.concurrent.DelegatingSecurityContextRunnable;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor;\n\n/**\n * An {@link SchedulingTaskExecutor} which wraps each {@link Runnable} in a\n * {@link DelegatingSecurityContextRunnable} and each {@link Callable} in a\n * {@link DelegatingSecurityContextCallable}.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class DelegatingSecurityContextSchedulingTaskExecutor extends DelegatingSecurityContextAsyncTaskExecutor\n\t\timplements SchedulingTaskExecutor {\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextSchedulingTaskExecutor} that uses the\n\t * specified {@link SecurityContext}.\n\t * @param delegateSchedulingTaskExecutor the {@link SchedulingTaskExecutor} to\n\t * delegate to. Cannot be null.\n\t * @param securityContext the {@link SecurityContext} to use for each\n\t * {@link DelegatingSecurityContextRunnable} and\n\t * {@link DelegatingSecurityContextCallable}\n\t */\n\tpublic DelegatingSecurityContextSchedulingTaskExecutor(SchedulingTaskExecutor delegateSchedulingTaskExecutor,\n\t\t\t@Nullable SecurityContext securityContext) {\n\t\tsuper(delegateSchedulingTaskExecutor, securityContext);\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextSchedulingTaskExecutor} that uses the\n\t * current {@link SecurityContext}.\n\t * @param delegateAsyncTaskExecutor the {@link AsyncTaskExecutor} to delegate to.\n\t * Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextSchedulingTaskExecutor(SchedulingTaskExecutor delegateAsyncTaskExecutor) {\n\t\tthis(delegateAsyncTaskExecutor, null);\n\t}\n\n\t@Override\n\tpublic boolean prefersShortLivedTasks() {\n\t\treturn getDelegate().prefersShortLivedTasks();\n\t}\n\n\tprivate SchedulingTaskExecutor getDelegate() {\n\t\treturn (SchedulingTaskExecutor) getDelegateExecutor();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/scheduling/DelegatingSecurityContextTaskScheduler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.scheduling;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Date;\nimport java.util.concurrent.ScheduledFuture;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.task.TaskExecutor;\nimport org.springframework.scheduling.TaskScheduler;\nimport org.springframework.scheduling.Trigger;\nimport org.springframework.security.concurrent.DelegatingSecurityContextRunnable;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of {@link TaskScheduler} invoking it whenever the trigger indicates a\n * next execution time.\n *\n * @author Richard Valdivieso\n * @since 5.1\n */\npublic class DelegatingSecurityContextTaskScheduler implements TaskScheduler {\n\n\tprivate final TaskScheduler delegate;\n\n\tprivate final @Nullable SecurityContext securityContext;\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextTaskScheduler} that uses the\n\t * specified {@link SecurityContext}.\n\t * @param delegateTaskScheduler the {@link TaskScheduler} to delegate to. Cannot be\n\t * null.\n\t * @param securityContext the {@link SecurityContext} to use for each\n\t * {@link DelegatingSecurityContextRunnable} or null to default to the current\n\t * {@link SecurityContext}\n\t * @since 5.6\n\t */\n\tpublic DelegatingSecurityContextTaskScheduler(TaskScheduler delegateTaskScheduler,\n\t\t\t@Nullable SecurityContext securityContext) {\n\t\tAssert.notNull(delegateTaskScheduler, \"delegateTaskScheduler cannot be null\");\n\t\tthis.delegate = delegateTaskScheduler;\n\t\tthis.securityContext = securityContext;\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextTaskScheduler} that uses the current\n\t * {@link SecurityContext} from the {@link SecurityContextHolder}.\n\t * @param delegate the {@link TaskExecutor} to delegate to. Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextTaskScheduler(TaskScheduler delegate) {\n\t\tthis(delegate, null);\n\t}\n\n\t@Override\n\tpublic @Nullable ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {\n\t\treturn this.delegate.schedule(wrap(task), trigger);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> schedule(Runnable task, Date startTime) {\n\t\treturn this.delegate.schedule(wrap(task), startTime);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {\n\t\treturn this.delegate.scheduleAtFixedRate(wrap(task), startTime, period);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {\n\t\treturn this.delegate.scheduleAtFixedRate(wrap(task), period);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) {\n\t\treturn this.delegate.scheduleWithFixedDelay(wrap(task), startTime, delay);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) {\n\t\treturn this.delegate.scheduleWithFixedDelay(wrap(task), delay);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> schedule(Runnable task, Instant startTime) {\n\t\treturn this.delegate.schedule(wrap(task), startTime);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {\n\t\treturn this.delegate.scheduleAtFixedRate(wrap(task), startTime, period);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {\n\t\treturn this.delegate.scheduleAtFixedRate(wrap(task), period);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) {\n\t\treturn this.delegate.scheduleWithFixedDelay(wrap(task), startTime, delay);\n\t}\n\n\t@Override\n\tpublic ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {\n\t\treturn this.delegate.scheduleWithFixedDelay(wrap(task), delay);\n\t}\n\n\t@Override\n\tpublic Clock getClock() {\n\t\treturn this.delegate.getClock();\n\t}\n\n\tprivate Runnable wrap(Runnable delegate) {\n\t\treturn DelegatingSecurityContextRunnable.create(delegate, this.securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/scheduling/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.scheduling;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.Future;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.task.AsyncTaskExecutor;\nimport org.springframework.security.concurrent.DelegatingSecurityContextCallable;\nimport org.springframework.security.concurrent.DelegatingSecurityContextRunnable;\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * An {@link AsyncTaskExecutor} which wraps each {@link Runnable} in a\n * {@link DelegatingSecurityContextRunnable} and each {@link Callable} in a\n * {@link DelegatingSecurityContextCallable}.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class DelegatingSecurityContextAsyncTaskExecutor extends DelegatingSecurityContextTaskExecutor\n\t\timplements AsyncTaskExecutor {\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextAsyncTaskExecutor} that uses the\n\t * specified {@link SecurityContext}.\n\t * @param delegateAsyncTaskExecutor the {@link AsyncTaskExecutor} to delegate to.\n\t * Cannot be null.\n\t * @param securityContext the {@link SecurityContext} to use for each\n\t * {@link DelegatingSecurityContextRunnable} and\n\t * {@link DelegatingSecurityContextCallable}\n\t */\n\tpublic DelegatingSecurityContextAsyncTaskExecutor(AsyncTaskExecutor delegateAsyncTaskExecutor,\n\t\t\t@Nullable SecurityContext securityContext) {\n\t\tsuper(delegateAsyncTaskExecutor, securityContext);\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextAsyncTaskExecutor} that uses the\n\t * current {@link SecurityContext}.\n\t * @param delegateAsyncTaskExecutor the {@link AsyncTaskExecutor} to delegate to.\n\t * Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextAsyncTaskExecutor(AsyncTaskExecutor delegateAsyncTaskExecutor) {\n\t\tthis(delegateAsyncTaskExecutor, null);\n\t}\n\n\t@Override\n\tpublic final void execute(Runnable task, long startTimeout) {\n\t\tgetDelegate().execute(wrap(task), startTimeout);\n\t}\n\n\t@Override\n\tpublic final Future<?> submit(Runnable task) {\n\t\treturn getDelegate().submit(wrap(task));\n\t}\n\n\t@Override\n\tpublic final <T> Future<T> submit(Callable<T> task) {\n\t\treturn getDelegate().submit(wrap(task));\n\t}\n\n\tprivate AsyncTaskExecutor getDelegate() {\n\t\treturn (AsyncTaskExecutor) getDelegateExecutor();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.task.TaskExecutor;\nimport org.springframework.security.concurrent.DelegatingSecurityContextExecutor;\nimport org.springframework.security.concurrent.DelegatingSecurityContextRunnable;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * An {@link TaskExecutor} which wraps each {@link Runnable} in a\n * {@link DelegatingSecurityContextRunnable}.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class DelegatingSecurityContextTaskExecutor extends DelegatingSecurityContextExecutor implements TaskExecutor {\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextTaskExecutor} that uses the specified\n\t * {@link SecurityContext}.\n\t * @param delegateTaskExecutor the {@link TaskExecutor} to delegate to. Cannot be\n\t * null.\n\t * @param securityContext the {@link SecurityContext} to use for each\n\t * {@link DelegatingSecurityContextRunnable}\n\t */\n\tpublic DelegatingSecurityContextTaskExecutor(TaskExecutor delegateTaskExecutor,\n\t\t\t@Nullable SecurityContext securityContext) {\n\t\tsuper(delegateTaskExecutor, securityContext);\n\t}\n\n\t/**\n\t * Creates a new {@link DelegatingSecurityContextTaskExecutor} that uses the current\n\t * {@link SecurityContext} from the {@link SecurityContextHolder}.\n\t * @param delegate the {@link TaskExecutor} to delegate to. Cannot be null.\n\t */\n\tpublic DelegatingSecurityContextTaskExecutor(TaskExecutor delegate) {\n\t\tthis(delegate, null);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/task/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.task;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/util/FieldUtils.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.lang.reflect.Field;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Offers static methods for directly manipulating fields.\n *\n * @author Ben Alex\n */\npublic final class FieldUtils {\n\n\tprivate FieldUtils() {\n\t}\n\n\t/**\n\t * Attempts to locate the specified field on the class.\n\t * @param clazz the class definition containing the field\n\t * @param fieldName the name of the field to locate\n\t * @return the Field (never null)\n\t * @throws IllegalStateException if field could not be found\n\t */\n\tpublic static Field getField(Class<?> clazz, String fieldName) throws IllegalStateException {\n\t\tAssert.notNull(clazz, \"Class required\");\n\t\tAssert.hasText(fieldName, \"Field name required\");\n\t\ttry {\n\t\t\treturn clazz.getDeclaredField(fieldName);\n\t\t}\n\t\tcatch (NoSuchFieldException ex) {\n\t\t\t// Try superclass\n\t\t\tif (clazz.getSuperclass() != null) {\n\t\t\t\treturn getField(clazz.getSuperclass(), fieldName);\n\t\t\t}\n\t\t\tthrow new IllegalStateException(\"Could not locate field '\" + fieldName + \"' on class \" + clazz);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the value of a (nested) field on a bean. Intended for testing.\n\t * @param bean the object\n\t * @param fieldName the field name, with \".\" separating nested properties\n\t * @return the value of the nested field\n\t */\n\tpublic static Object getFieldValue(Object bean, String fieldName) throws IllegalAccessException {\n\t\tAssert.notNull(bean, \"Bean cannot be null\");\n\t\tAssert.hasText(fieldName, \"Field name required\");\n\t\tString[] nestedFields = StringUtils.tokenizeToStringArray(fieldName, \".\");\n\t\tClass<?> componentClass = bean.getClass();\n\t\tObject value = bean;\n\t\tfor (String nestedField : nestedFields) {\n\t\t\tField field = getField(componentClass, nestedField);\n\t\t\tfield.setAccessible(true);\n\t\t\tvalue = field.get(value);\n\t\t\tif (value != null) {\n\t\t\t\tcomponentClass = value.getClass();\n\t\t\t}\n\t\t}\n\t\treturn value;\n\n\t}\n\n\tpublic static @Nullable Object getProtectedFieldValue(String protectedField, Object object) {\n\t\tField field = FieldUtils.getField(object.getClass(), protectedField);\n\t\ttry {\n\t\t\tfield.setAccessible(true);\n\t\t\treturn field.get(object);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tReflectionUtils.handleReflectionException(ex);\n\t\t\treturn null; // unreachable - previous line throws exception\n\t\t}\n\t}\n\n\tpublic static void setProtectedFieldValue(String protectedField, Object object, Object newValue) {\n\t\tField field = FieldUtils.getField(object.getClass(), protectedField);\n\t\ttry {\n\t\t\tfield.setAccessible(true);\n\t\t\tfield.set(object, newValue);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tReflectionUtils.handleReflectionException(ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/util/InMemoryResource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.util.Arrays;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.io.AbstractResource;\nimport org.springframework.util.Assert;\n\n/**\n * An in memory implementation of Spring's {@link org.springframework.core.io.Resource}\n * interface.\n * <p>\n * Used to create a bean factory from an XML string, rather than a file.\n * </p>\n *\n * @author Luke Taylor\n */\npublic class InMemoryResource extends AbstractResource {\n\n\tprivate final byte[] source;\n\n\tprivate final String description;\n\n\tpublic InMemoryResource(String source) {\n\t\tthis(source.getBytes());\n\t}\n\n\tpublic InMemoryResource(byte[] source) {\n\t\tthis(source, null);\n\t}\n\n\tpublic InMemoryResource(byte[] source, @Nullable String description) {\n\t\tAssert.notNull(source, \"source cannot be null\");\n\t\tthis.source = source;\n\t\tthis.description = (description != null) ? description : \"\";\n\t}\n\n\t@Override\n\tpublic String getDescription() {\n\t\treturn this.description;\n\t}\n\n\t@Override\n\tpublic InputStream getInputStream() {\n\t\treturn new ByteArrayInputStream(this.source);\n\t}\n\n\t@Override\n\tpublic boolean equals(@Nullable Object res) {\n\t\tif (!(res instanceof InMemoryResource)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Arrays.equals(this.source, ((InMemoryResource) res).source);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 1;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/util/MethodInvocationUtils.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aop.framework.Advised;\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.util.Assert;\n\n/**\n * Static utility methods for creating <code>MethodInvocation</code>s usable within Spring\n * Security.\n * <p>\n * All methods of this class return a\n * {@link org.springframework.security.util.SimpleMethodInvocation}.\n *\n * @author Ben Alex\n */\npublic final class MethodInvocationUtils {\n\n\tprivate MethodInvocationUtils() {\n\t}\n\n\t/**\n\t * Generates a <code>MethodInvocation</code> for specified <code>methodName</code> on\n\t * the passed object, using the <code>args</code> to locate the method.\n\t * @param object the object that will be used to find the relevant <code>Method</code>\n\t * @param methodName the name of the method to find\n\t * @param args arguments that are required as part of the method signature (can be\n\t * empty)\n\t * @return a <code>MethodInvocation</code>, or <code>null</code> if there was a\n\t * problem\n\t */\n\tpublic static @Nullable MethodInvocation create(Object object, String methodName, Object... args) {\n\t\tAssert.notNull(object, \"Object required\");\n\t\tClass<?>[] classArgs = null;\n\t\tif (args != null) {\n\t\t\tclassArgs = new Class<?>[args.length];\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tclassArgs[i] = args[i].getClass();\n\t\t\t}\n\t\t}\n\t\t// Determine the type that declares the requested method,\n\t\t// taking into account proxies\n\t\tClass<?> target = AopUtils.getTargetClass(object);\n\t\tif (object instanceof Advised a) {\n\t\t\tif (!a.isProxyTargetClass()) {\n\t\t\t\tClass<?>[] possibleInterfaces = a.getProxiedInterfaces();\n\t\t\t\tfor (Class<?> possibleInterface : possibleInterfaces) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tpossibleInterface.getMethod(methodName, classArgs);\n\t\t\t\t\t\t// to get here means no exception happened\n\t\t\t\t\t\ttarget = possibleInterface;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\t\t// try the next one\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn createFromClass(object, target, methodName, classArgs, args);\n\t}\n\n\t/**\n\t * Generates a <code>MethodInvocation</code> for the specified <code>methodName</code>\n\t * on the passed class.\n\t *\n\t * If a method with this name, taking no arguments does not exist, it will check\n\t * through the declared methods on the class, until one is found matching the supplied\n\t * name. If more than one method name matches, an <tt>IllegalArgumentException</tt>\n\t * will be raised.\n\t * @param clazz the class of object that will be used to find the relevant\n\t * <code>Method</code>\n\t * @param methodName the name of the method to find\n\t * @return a <code>MethodInvocation</code>, or <code>null</code> if there was a\n\t * problem\n\t */\n\tpublic static @Nullable MethodInvocation createFromClass(Class<?> clazz, String methodName) {\n\t\tMethodInvocation invocation = createFromClass(null, clazz, methodName, null, null);\n\t\tif (invocation == null) {\n\t\t\tfor (Method method : clazz.getDeclaredMethods()) {\n\t\t\t\tif (method.getName().equals(methodName)) {\n\t\t\t\t\tAssert.isTrue(invocation == null,\n\t\t\t\t\t\t\t() -> \"The class \" + clazz + \" has more than one method named\" + \" '\" + methodName + \"'\");\n\t\t\t\t\tinvocation = new SimpleMethodInvocation(null, method);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn invocation;\n\t}\n\n\t/**\n\t * Generates a <code>MethodInvocation</code> for specified <code>methodName</code> on\n\t * the passed class, using the <code>args</code> to locate the method.\n\t * @param targetObject the object being invoked\n\t * @param clazz the class of object that will be used to find the relevant\n\t * <code>Method</code>\n\t * @param methodName the name of the method to find\n\t * @param classArgs arguments that are required to locate the relevant method\n\t * signature\n\t * @param args the actual arguments that should be passed to SimpleMethodInvocation\n\t * @return a <code>MethodInvocation</code>, or <code>null</code> if there was a\n\t * problem\n\t */\n\tpublic static @Nullable MethodInvocation createFromClass(@Nullable Object targetObject, Class<?> clazz,\n\t\t\tString methodName, Class<?> @Nullable [] classArgs, Object @Nullable [] args) {\n\t\tAssert.notNull(clazz, \"Class required\");\n\t\tAssert.hasText(methodName, \"MethodName required\");\n\t\ttry {\n\t\t\tMethod method = clazz.getMethod(methodName, classArgs);\n\t\t\treturn new SimpleMethodInvocation(targetObject, method, args);\n\t\t}\n\t\tcatch (NoSuchMethodException ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/util/SimpleMethodInvocation.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Represents the AOP Alliance <code>MethodInvocation</code>.\n *\n * @author Ben Alex\n */\npublic class SimpleMethodInvocation implements MethodInvocation {\n\n\tprivate @Nullable Method method;\n\n\tprivate Object[] arguments;\n\n\tprivate @Nullable Object targetObject;\n\n\t// @formatter:off See https://github.com/spring-io/spring-javaformat/issues/\n\tpublic SimpleMethodInvocation(@Nullable Object targetObject, Method method, Object @Nullable... arguments) {\n\t\tthis.targetObject = targetObject;\n\t\tthis.method = method;\n\t\tthis.arguments = (arguments != null) ? arguments : new Object[0];\n\t}\n\t// @formatter:on\n\n\tpublic SimpleMethodInvocation() {\n\t\tthis.arguments = new Object[0];\n\t}\n\n\t@Override\n\tpublic Object[] getArguments() {\n\t\treturn this.arguments;\n\t}\n\n\t@Override\n\tpublic Method getMethod() {\n\t\tAssert.state(this.method != null, \"method cannot be null\");\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic AccessibleObject getStaticPart() {\n\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t}\n\n\t@Override\n\tpublic @Nullable Object getThis() {\n\t\treturn this.targetObject;\n\t}\n\n\t@Override\n\tpublic Object proceed() {\n\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"method invocation [\" + this.method + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "core/src/main/java/org/springframework/security/util/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * General utility classes used throughout the Spring Security framework. Intended for\n * internal use.\n * <p>\n * This package should be standalone - it should not have dependencies on other parts of\n * the framework, just on external libraries and the JDK.\n */\n@NullMarked\npackage org.springframework.security.util;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "core/src/main/resources/META-INF/services/io.micrometer.context.ThreadLocalAccessor",
    "content": "org.springframework.security.core.context.ReactiveSecurityContextHolderThreadLocalAccessor\norg.springframework.security.core.context.SecurityContextHolderThreadLocalAccessor\n"
  },
  {
    "path": "core/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.aot.hint.CoreSecurityRuntimeHints,\\\norg.springframework.security.aot.hint.OneTimeTokenRuntimeHints\n\norg.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\\\norg.springframework.security.aot.hint.SecurityHintsAotProcessor\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/core/ott/jdbc/one-time-tokens-schema.sql",
    "content": "create table one_time_tokens(\n    token_value varchar(36) not null primary key,\n    username    varchar_ignorecase(50) not null,\n    expires_at  timestamp   not null\n);\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/core/userdetails/jdbc/users.ddl",
    "content": "create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null);\ncreate table authorities (username varchar_ignorecase(50) not null,authority varchar_ignorecase(50) not null,constraint fk_authorities_users foreign key(username) references users(username));\ncreate unique index ix_auth_username on authorities (username,authority);"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=Access is denied\nAbstractLdapAuthenticationProvider.emptyPassword=Empty Password\nAbstractSecurityInterceptor.authenticationNotFound=An Authentication object was not found in the SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=Bad credentials\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=User credentials have expired\nAbstractUserDetailsAuthenticationProvider.disabled=User is disabled\nAbstractUserDetailsAuthenticationProvider.expired=User account has expired\nAbstractUserDetailsAuthenticationProvider.locked=User account is locked\nAbstractUserDetailsAuthenticationProvider.onlySupports=Only UsernamePasswordAuthenticationToken is supported\nAccountStatusUserDetailsChecker.credentialsExpired=User credentials have expired\nAccountStatusUserDetailsChecker.disabled=User is disabled\nAccountStatusUserDetailsChecker.expired=User account has expired\nAccountStatusUserDetailsChecker.locked=User account is locked\nAclEntryAfterInvocationProvider.noPermission=Authentication {0} has NO permissions to the domain object {1}\nAnonymousAuthenticationProvider.incorrectKey=The presented AnonymousAuthenticationToken does not contain the expected key\nBindAuthenticator.badCredentials=Bad credentials\nBindAuthenticator.emptyPassword=Empty Password\nCasAuthenticationProvider.incorrectKey=The presented CasAuthenticationToken does not contain the expected key\nCasAuthenticationProvider.noServiceTicket=Failed to provide a CAS service ticket to validate\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Maximum sessions of {0} for this principal exceeded\nDigestAuthenticationFilter.incorrectRealm=Response realm name {0} does not match system realm name of {1}\nDigestAuthenticationFilter.incorrectResponse=Incorrect response\nDigestAuthenticationFilter.missingAuth=Missing mandatory digest value for 'auth' QOP; received header {0}\nDigestAuthenticationFilter.missingMandatory=Missing mandatory digest value; received header {0}\nDigestAuthenticationFilter.nonceCompromised=Nonce token compromised {0}\nDigestAuthenticationFilter.nonceEncoding=Nonce is not encoded in Base64; received nonce {0}\nDigestAuthenticationFilter.nonceExpired=Nonce has expired/timed out\nDigestAuthenticationFilter.nonceNotNumeric=Nonce token should have yielded a numeric first token, but was {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce should have yielded two tokens but was {0}\nDigestAuthenticationFilter.usernameNotFound=Username {0} not found\nExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource\nJdbcDaoImpl.noAuthority=User {0} has no GrantedAuthority\nJdbcDaoImpl.notFound=User {0} not found\nLdapAuthenticationProvider.badCredentials=Bad credentials\nLdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=User credentials have expired\nLdapAuthenticationProvider.disabled=User is disabled\nLdapAuthenticationProvider.expired=User account has expired\nLdapAuthenticationProvider.locked=User account is locked\nLdapAuthenticationProvider.emptyUsername=Empty username not allowed\nLdapAuthenticationProvider.onlySupports=Only UsernamePasswordAuthenticationToken is supported\nPasswordComparisonAuthenticator.badCredentials=Bad credentials\nPersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=No AuthenticationProvider found for {0}\nRememberMeAuthenticationProvider.incorrectKey=The presented RememberMeAuthenticationToken does not contain the expected key\nRunAsImplAuthenticationProvider.incorrectKey=The presented RunAsUserToken does not contain the expected key\nSubjectDnX509PrincipalExtractor.noMatching=No matching pattern was found in subjectDN: {0}\nSwitchUserFilter.noCurrentUser=No current user associated with this request\nSwitchUserFilter.noOriginalAuthentication=Could not find original Authentication object\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_ca.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=S'ha denegat l'acc\\u00e9s\nAbstractLdapAuthenticationProvider.emptyPassword=La contrasenya \\u00e9s buida\nAbstractSecurityInterceptor.authenticationNotFound=No s'ha trobat l'objecte Authentication al SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=Credencials err\\u00f2nies\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Les credencials de l'usuari han expirat\nAbstractUserDetailsAuthenticationProvider.disabled=L'usuari est\\u00e0 deshabilitat\nAbstractUserDetailsAuthenticationProvider.expired=El compte d'usuari ha expirat\nAbstractUserDetailsAuthenticationProvider.locked=El compte d'usuari est\\u00e0 bloquejat\nAbstractUserDetailsAuthenticationProvider.onlySupports=Nom\\u00e9s est\\u00e0 suportat UsernamePasswordAuthenticationToken\nAccountStatusUserDetailsChecker.credentialsExpired=Les credencials de l'usuari han expirat\nAccountStatusUserDetailsChecker.disabled=L'usuari est\\u00e0 deshabilitat\nAccountStatusUserDetailsChecker.expired=El compte d'usuari ha expirat\nAccountStatusUserDetailsChecker.locked=El compte d'usuari est\\u00e0 bloquejat\nAclEntryAfterInvocationProvider.noPermission=L'Authentication {0} NO t\\u00e9 permisos per a l'objecte de domini {1}\nAnonymousAuthenticationProvider.incorrectKey=L'AnonymousAuthenticationToken que s'ha presentat no cont\\u00e9 la clau esperada\nBindAuthenticator.badCredentials=Credencials err\\u00f2nies\nBindAuthenticator.emptyPassword=La contrasenya \\u00e9s buida\nCasAuthenticationProvider.incorrectKey=El CasAuthenticationToken que s'ha presentat no cont\\u00e9 la clau esperada\nCasAuthenticationProvider.noServiceTicket=No s'ha pogut proporcionar un tiquet de servei CAS per validar\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=S'ha sobrepassat el m\\u00e0xim de {0} sessions per a aquesta identitat\nDigestAuthenticationFilter.incorrectRealm=El nom de reialme de la resposta {0} no coincideix amb el nom de reialme del sistema {1}\nDigestAuthenticationFilter.incorrectResponse=Resposta incorrecta\nDigestAuthenticationFilter.missingAuth=No s'ha trobat el valor del resum obligatori per al QOP de nivell 'auth'; s'ha rebut la cap\\u00e7alera {0}\nDigestAuthenticationFilter.missingMandatory=No s'ha trobat el valor del resum obligatori; s'ha rebut la cap\\u00e7alera {0}\nDigestAuthenticationFilter.nonceCompromised=S'ha comprom\\u00e9s el nonce token {0}\nDigestAuthenticationFilter.nonceEncoding=El nonce no est\\u00e0 codificat en Base64; s'ha rebut el nonce {0}\nDigestAuthenticationFilter.nonceExpired=El nonce ha expirat/ha arribat fora de temps\nDigestAuthenticationFilter.nonceNotNumeric=El nonce token haur\\u00eda d'haver produ\\u00eft un token num\\u00e8ric inicial, per\\u00f2 era {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=El nonce hauria de produ\\u00efr dos tokens, i no {0}\nDigestAuthenticationFilter.usernameNotFound=No s'ha trobat el nom d'usuari {0}\nExceptionTranslationFilter.insufficientAuthentication=Per accedir a aquest recurs cal autenticaci\\u00f3 completa\nJdbcDaoImpl.noAuthority=L'usuari {0} no t\\u00e9 GrantedAuthority\nJdbcDaoImpl.notFound=No s'ha trobat l'usuari {0}\nLdapAuthenticationProvider.badCredentials=Credencials err\\u00f2nies\nLdapAuthenticationProvider.badLdapConnection=Ha fallat la connexi\\u00f3 al servidor LDAP\nLdapAuthenticationProvider.credentialsExpired=Les credencials d'usuari han expirat\nLdapAuthenticationProvider.disabled=L'usuari est\\u00e0 deshabilitat\nLdapAuthenticationProvider.expired=El compte d'usuari ha expirat\nLdapAuthenticationProvider.locked=El compte d'usuari est\\u00e0 bloquejat\nLdapAuthenticationProvider.emptyUsername=No es permet un nom d'usuari buit\nLdapAuthenticationProvider.onlySupports=Nom\\u00e9s est\\u00e0 suportat UsernamePasswordAuthenticationToken\nPasswordComparisonAuthenticator.badCredentials=Credencials incorrectes\nPersistentTokenBasedRememberMeServices.cookieStolen=El token remember-me no \\u00e9s valid; discrep\\u00e0ncia (S\\u00e8rie/token). Implica un atac previ de robatori de cookie.\nProviderManager.providerNotFound=No s'ha trobat cap AuthenticationProvider per a {0}\nRememberMeAuthenticationProvider.incorrectKey=El RememberMeAuthenticationToken actual no cont\\u00e9 la clau esperada\nRunAsImplAuthenticationProvider.incorrectKey=El RunAsUserToken actual no cont\\u00e9 la clau esperada\nSubjectDnX509PrincipalExtractor.noMatching=No s'ha trobat cap patr\\u00f3 coincident a subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=No hi ha cap usuari associat amb aquesta petici\\u00f3\nSwitchUserFilter.noOriginalAuthentication=No s'ha trobat l'objecte Authentication original\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_cs_CZ.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=P\\u0159\\u00EDstup odep\\u0159en\nAbstractLdapAuthenticationProvider.emptyPassword=\\u0160patn\\u00E9 p\\u0159ihla\\u0161ovac\\u00ED \\u00FAdaje\nAbstractSecurityInterceptor.authenticationNotFound=Nebyl nalezen \\u017E\\u00E1dn\\u00FD Authentication objekt v SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=\\u0160patn\\u00E9 p\\u0159ihla\\u0161ovac\\u00ED \\u00FAdaje\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Platnost u\\u017Eivatelsk\\u00E9ho hesla vypr\\u0161ela\nAbstractUserDetailsAuthenticationProvider.disabled=U\\u017Eivatelsk\\u00FD \\u00FA\\u010Det nen\\u00ED aktivn\\u00ED\nAbstractUserDetailsAuthenticationProvider.expired=Platnost u\\u017Eivatelsk\\u00E9ho \\u00FA\\u010Dtu vypr\\u0161ela\nAbstractUserDetailsAuthenticationProvider.locked=U\\u017Eivatelsk\\u00FD \\u00FA\\u010Det je uzam\\u010Den\nAbstractUserDetailsAuthenticationProvider.onlySupports=Je podporov\\u00E1n pouze UsernamePasswordAuthenticationToken\nAccountStatusUserDetailsChecker.credentialsExpired=Platnost u\\u017Eivatelsk\\u00E9ho hesla vypr\\u0161ela\nAccountStatusUserDetailsChecker.disabled=U\\u017Eivatelsk\\u00FD \\u00FA\\u010Det nen\\u00ED aktivn\\u00ED\nAccountStatusUserDetailsChecker.expired=Platnost u\\u017Eivatelsk\\u00E9ho \\u00FA\\u010Dtu vypr\\u0161ela\nAccountStatusUserDetailsChecker.locked=U\\u017Eivatelsk\\u00FD \\u00FA\\u010Det je uzam\\u010Den\nAclEntryAfterInvocationProvider.noPermission=Autentizovan\\u00FD u\\u017Eivatel {0} nem\\u00E1 \\u017D\\u00C1DN\\u00C1 pr\\u00E1va k objektu {1}\nAnonymousAuthenticationProvider.incorrectKey=Pou\\u017Eit\\u00FD AnonymousAuthenticationToken neobsahuje o\\u010Dek\\u00E1van\\u00FD kl\\u00ED\\u010D\nBindAuthenticator.badCredentials=\\u0160patn\\u00E9 p\\u0159ihla\\u0161ovac\\u00ED \\u00FAdaje\nBindAuthenticator.emptyPassword=\\u0160patn\\u00E9 p\\u0159ihla\\u0161ovac\\u00ED \\u00FAdaje\nCasAuthenticationProvider.incorrectKey=Pou\\u017Eit\\u00FD CasAuthenticationToken neobsahuje o\\u010Dek\\u00E1van\\u00FD kl\\u00ED\\u010D\nCasAuthenticationProvider.noServiceTicket=Nepoda\\u0159ilo se z\\u00EDskat otisk CAS (centr\\u00E1ln\\u00ED autentiza\\u010Dn\\u00ED autority) k ov\\u011B\\u0159en\\u00ED autenticity u\\u017Eivatele\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Maxim\\u00E1ln\\u00ED po\\u010Det sou\\u010Dasn\\u00FDch p\\u0159ihl\\u00E1\\u0161en\\u00ED {0} tohoto u\\u017Eivatele je p\\u0159ekro\\u010Den\nDigestAuthenticationFilter.incorrectRealm=Oblast odpov\\u011Bdi {0} neodpov\\u00EDd\\u00E1 syst\\u00E9mov\\u00E9 oblasti {1}\nDigestAuthenticationFilter.incorrectResponse=Vadn\\u00E1 odpov\\u011B\\u010F\nDigestAuthenticationFilter.missingAuth=Chyb\\u00ED povinn\\u00E1 kl\\u00ED\\u010Dov\\u00E1 polo\\u017Eka 'auth' QOP (\\u00FArove\\u0148 bezpe\\u010Dnosti RFC 2617); p\\u0159ijat\\u00E1 hlavi\\u010Dka {0}\nDigestAuthenticationFilter.missingMandatory=Chyb\\u00ED povinn\\u00E1 kl\\u00ED\\u010Dov\\u00E1 polo\\u017Eka; p\\u0159ijat\\u00E1 hlavi\\u010Dka {0}\nDigestAuthenticationFilter.nonceCompromised=Kryptovan\\u00FD kl\\u00ED\\u010D (nonce) je znehodnocen\\u00FD {0}\nDigestAuthenticationFilter.nonceEncoding=Kryptovan\\u00FD kl\\u00ED\\u010D (nonce) nen\\u00ED p\\u0159ek\\u00E9dov\\u00E1n do Base60; p\\u0159ijat\\u00FD kl\\u00ED\\u010D {0}\nDigestAuthenticationFilter.nonceExpired=Kryptovan\\u00FD kl\\u00ED\\u010D (nonce) vypr\\u0161el\nDigestAuthenticationFilter.nonceNotNumeric=Kryptovan\\u00FD kl\\u00ED\\u010D (nonce) by m\\u011Bl m\\u00EDt prvn\\u00ED \\u010D\\u00E1st \\u010D\\u00EDselnou, ale je {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Kryptovan\\u00FD kl\\u00ED\\u010D (nonce) by m\\u011Bl b\\u00FDt slo\\u017Een ze dvou \\u010D\\u00E1st\\u00ED {0}\nDigestAuthenticationFilter.usernameNotFound=U\\u017Eivatelsk\\u00E9 jm\\u00E9no {0} nebylo nalezeno\n#ExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource\n#JdbcDaoImpl.noAuthority=User {0} has no GrantedAuthority\n#JdbcDaoImpl.notFound=User {0} not found\nLdapAuthenticationProvider.badCredentials=\\u0160patn\\u00E9 p\\u0159ihla\\u0161ovac\\u00ED \\u00FAdaje\nLdapAuthenticationProvider.credentialsExpired=Platnost u\\u017Eivatelsk\\u00E9ho hesla vypr\\u0161ela\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.disabled=U\\u017Eivatelsk\\u00FD \\u00FA\\u010Det nen\\u00ED aktivn\\u00ED\nLdapAuthenticationProvider.expired=Platnost u\\u017Eivatelsk\\u00E9ho \\u00FA\\u010Dtu vypr\\u0161ela\nLdapAuthenticationProvider.locked=U\\u017Eivatelsk\\u00FD \\u00FA\\u010Det je uzam\\u010Den\nLdapAuthenticationProvider.emptyUsername=Nen\\u00ED povoleno pr\\u00E1zdn\\u00E9 u\\u017Eivatelsk\\u00E9 jm\\u00E9no\nLdapAuthenticationProvider.onlySupports=Je podporov\\u00E1n pouze UsernamePasswordAuthenticationToken\nPasswordComparisonAuthenticator.badCredentials=\\u0160patn\\u00E9 p\\u0159ihla\\u0161ovac\\u00ED \\u00FAdaje\n#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=Nebyl nalezen \\u017E\\u00E1dn\\u00FD AuthenticationProvider pro {0}\nRememberMeAuthenticationProvider.incorrectKey=Pou\\u017Eit\\u00FD RememberMeAuthenticationToken neobsahuje o\\u010Dek\\u00E1van\\u00FD kl\\u00ED\\u010D\nRunAsImplAuthenticationProvider.incorrectKey=Pou\\u017Eit\\u00FD RunAsUserToken neobsahuje o\\u010Dek\\u00E1van\\u00FD kl\\u00ED\\u010D\nSubjectDnX509PrincipalExtractor.noMatching=V subjectDN nebyl nalezen \\u017E\\u00E1dn\\u00FD \\u0159et\\u011Bzec odpov\\u00EDdaj\\u00EDc\\u00ED vy\\u017Eadovan\\u00E9 masce\\: {0}\nSwitchUserFilter.noCurrentUser=\\u017D\\u00E1dn\\u00FD u\\u017Eivatel nen\\u00ED asociov\\u00E1n s t\\u00EDmto po\\u017Eadavkem\nSwitchUserFilter.noOriginalAuthentication=Nepoda\\u0159ilo se nal\\u00E9zt p\\u016Fvodn\\u00ED Authentication objekt\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_de.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=Zugriff verweigert\nAbstractLdapAuthenticationProvider.emptyPassword=Ung\\u00FCltige Anmeldedaten\nAbstractSecurityInterceptor.authenticationNotFound=Im SecurityContext wurde keine Authentifikation gefunden\nAbstractUserDetailsAuthenticationProvider.badCredentials=Ung\\u00FCltige Anmeldedaten\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Die G\\u00FCltigkeit der Anmeldedaten ist abgelaufen\nAbstractUserDetailsAuthenticationProvider.disabled=Der Benutzer ist deaktiviert\nAbstractUserDetailsAuthenticationProvider.expired=Die G\\u00FCltigkeit des Benutzerkontos ist abgelaufen\nAbstractUserDetailsAuthenticationProvider.locked=Das Benutzerkonto ist gesperrt\nAbstractUserDetailsAuthenticationProvider.onlySupports=Nur UsernamePasswordAuthenticationToken wird unterst\\u00FCtzt\nAccountStatusUserDetailsChecker.credentialsExpired=Die G\\u00FCltigkeit der Anmeldedaten ist abgelaufen\nAccountStatusUserDetailsChecker.disabled=Der Benutzer ist deaktiviert\nAccountStatusUserDetailsChecker.expired=Die G\\u00FCltigkeit des Benutzerkontos ist abgelaufen\nAccountStatusUserDetailsChecker.locked=Das Benutzerkonto ist gesperrt\nAclEntryAfterInvocationProvider.noPermission=Authentifikation {0} hat KEINE Berechtigungen bez\\u00FCglich des Dom\\u00E4nen-Objekts {1}\nAnonymousAuthenticationProvider.incorrectKey=Das angegebene AnonymousAuthenticationToken enth\\u00E4lt nicht den erwarteten Schl\\u00FCssel\nBindAuthenticator.badCredentials=Ung\\u00FCltige Anmeldedaten\nBindAuthenticator.emptyPassword=Ung\\u00FCltige Anmeldedaten\nCasAuthenticationProvider.incorrectKey=Das angegebene CasAuthenticationToken enth\\u00E4lt nicht den erwarteten Schl\\u00FCssel\nCasAuthenticationProvider.noServiceTicket=Es konnte kein CAS Service-Ticket zur Pr\\u00FCfung geliefert werden\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Die maximale Sitzungs-Anzahl von {0} f\\u00FCr diesen Nutzer wurde \\u00FCberschritten\nDigestAuthenticationFilter.incorrectRealm=Realm-Name in Antwort {0} entspricht nicht dem Namen des System-Realms {1}\nDigestAuthenticationFilter.incorrectResponse=Fehlerhafte Antwort\nDigestAuthenticationFilter.missingAuth=Erforderlicher Digest-Wert fehlt f\\u00FCr 'auth' QOP; Empfangener Header {0}\nDigestAuthenticationFilter.missingMandatory=Erforderlicher Digest-Wert fehlt; Empfangener Header {0}\nDigestAuthenticationFilter.nonceCompromised=Das Nonce-Element ist kompromittiert {0}\nDigestAuthenticationFilter.nonceEncoding=Die Nonce ist nicht in Base64 kodiert; Empfangene Nonce {0}\nDigestAuthenticationFilter.nonceExpired=Die Nonce ist nicht mehr g\\u00FCltig\nDigestAuthenticationFilter.nonceNotNumeric=Das erste Element der Nonce sollte numerisch sein. Gefundener Inhalt\\: {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce sollte zwei Elemente beinhalten. Gefundener Inhalt\\: {0}\nDigestAuthenticationFilter.usernameNotFound=Benutzername {0} wurde nicht gefunden\nExceptionTranslationFilter.insufficientAuthentication=Vollst\\u00E4ndige Authentifikation wird ben\\u00f6tigt um auf diese Resource zuzugreifen\n#JdbcDaoImpl.noAuthority=User {0} has no GrantedAuthority\n#JdbcDaoImpl.notFound=User {0} not found\nLdapAuthenticationProvider.badCredentials=Ung\\u00FCltige Anmeldedaten\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=Die G\\u00FCltigkeit der Anmeldedaten ist abgelaufen\nLdapAuthenticationProvider.disabled=Der Benutzer ist deaktiviert\nLdapAuthenticationProvider.expired=Die G\\u00FCltigkeit des Benutzerkontos ist abgelaufen\nLdapAuthenticationProvider.locked=Das Benutzerkonto ist gesperrt\nLdapAuthenticationProvider.emptyUsername=Ein leerer Benutzername ist nicht erlaubt\nLdapAuthenticationProvider.onlySupports=Nur UsernamePasswordAuthenticationToken wird unterst\\u00FCtzt\nPasswordComparisonAuthenticator.badCredentials=Ung\\u00FCltige Anmeldedaten\n#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=F\\u00FCr {0} wurde kein AuthenticationProvider gefunden\nRememberMeAuthenticationProvider.incorrectKey=Das angegebene RememberMeAuthenticationToken enth\\u00E4lt nicht den erwarteten Schl\\u00FCssel\nRunAsImplAuthenticationProvider.incorrectKey=Das angegebene RunAsUserToken enth\\u00E4lt nicht den erwarteten Schl\\u00FCssel\nSubjectDnX509PrincipalExtractor.noMatching=Kein passendes Muster gefunden in subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=Mit dieser Anfrage ist kein aktueller Benutzer assoziiert\nSwitchUserFilter.noOriginalAuthentication=Kann das urspr\\u00FCngliche Authentifikationsobjekt nicht finden\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_en.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=Access is denied\nAbstractLdapAuthenticationProvider.emptyPassword=Empty Password\nAbstractSecurityInterceptor.authenticationNotFound=An Authentication object was not found in the SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=Bad credentials\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=User credentials have expired\nAbstractUserDetailsAuthenticationProvider.disabled=User is disabled\nAbstractUserDetailsAuthenticationProvider.expired=User account has expired\nAbstractUserDetailsAuthenticationProvider.locked=User account is locked\nAbstractUserDetailsAuthenticationProvider.onlySupports=Only UsernamePasswordAuthenticationToken is supported\nAccountStatusUserDetailsChecker.credentialsExpired=User credentials have expired\nAccountStatusUserDetailsChecker.disabled=User is disabled\nAccountStatusUserDetailsChecker.expired=User account has expired\nAccountStatusUserDetailsChecker.locked=User account is locked\nAclEntryAfterInvocationProvider.noPermission=Authentication {0} has NO permissions to the domain object {1}\nAnonymousAuthenticationProvider.incorrectKey=The presented AnonymousAuthenticationToken does not contain the expected key\nBindAuthenticator.badCredentials=Bad credentials\nBindAuthenticator.emptyPassword=Empty Password\nCasAuthenticationProvider.incorrectKey=The presented CasAuthenticationToken does not contain the expected key\nCasAuthenticationProvider.noServiceTicket=Failed to provide a CAS service ticket to validate\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Maximum sessions of {0} for this principal exceeded\nDigestAuthenticationFilter.incorrectRealm=Response realm name {0} does not match system realm name of {1}\nDigestAuthenticationFilter.incorrectResponse=Incorrect response\nDigestAuthenticationFilter.missingAuth=Missing mandatory digest value for 'auth' QOP; received header {0}\nDigestAuthenticationFilter.missingMandatory=Missing mandatory digest value; received header {0}\nDigestAuthenticationFilter.nonceCompromised=Nonce token compromised {0}\nDigestAuthenticationFilter.nonceEncoding=Nonce is not encoded in Base64; received nonce {0}\nDigestAuthenticationFilter.nonceExpired=Nonce has expired/timed out\nDigestAuthenticationFilter.nonceNotNumeric=Nonce token should have yielded a numeric first token, but was {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce should have yielded two tokens but was {0}\nDigestAuthenticationFilter.usernameNotFound=Username {0} not found\nExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource\nJdbcDaoImpl.noAuthority=User {0} has no GrantedAuthority\nJdbcDaoImpl.notFound=User {0} not found\nLdapAuthenticationProvider.badCredentials=Bad credentials\nLdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=User credentials have expired\nLdapAuthenticationProvider.disabled=User is disabled\nLdapAuthenticationProvider.expired=User account has expired\nLdapAuthenticationProvider.locked=User account is locked\nLdapAuthenticationProvider.emptyUsername=Empty username not allowed\nLdapAuthenticationProvider.onlySupports=Only UsernamePasswordAuthenticationToken is supported\nPasswordComparisonAuthenticator.badCredentials=Bad credentials\nPersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=No AuthenticationProvider found for {0}\nRememberMeAuthenticationProvider.incorrectKey=The presented RememberMeAuthenticationToken does not contain the expected key\nRunAsImplAuthenticationProvider.incorrectKey=The presented RunAsUserToken does not contain the expected key\nSubjectDnX509PrincipalExtractor.noMatching=No matching pattern was found in subjectDN: {0}\nSwitchUserFilter.noCurrentUser=No current user associated with this request\nSwitchUserFilter.noOriginalAuthentication=Could not find original Authentication object\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_es_ES.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=Acceso denegado\nAbstractLdapAuthenticationProvider.emptyPassword=Credenciales err\\u00F3neas\nAbstractSecurityInterceptor.authenticationNotFound=El objeto Authentication no ha sido encontrado en el SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=Credenciales err\\u00F3neas\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Las credenciales del usuario han expirado\nAbstractUserDetailsAuthenticationProvider.disabled=El usuario est\\u00E1 deshabilitado\nAbstractUserDetailsAuthenticationProvider.expired=La cuenta del usuario ha expirado\nAbstractUserDetailsAuthenticationProvider.locked=La cuenta del usuario est\\u00E1 bloqueada\nAbstractUserDetailsAuthenticationProvider.onlySupports=S\\u00F3lo UsernamePasswordAuthenticationToken es soportada\nAccountStatusUserDetailsChecker.credentialsExpired=Las credenciales del usuario han expirado\nAccountStatusUserDetailsChecker.disabled=El usuario est\\u00E1 deshabilitado\nAccountStatusUserDetailsChecker.expired=La cuenta del usuario ha expirado\nAccountStatusUserDetailsChecker.locked=La cuenta del usuario est\\u00E1 bloqueada\nAclEntryAfterInvocationProvider.noPermission=Authentication {0} NO tiene permisos para el objeto de dominio {1}\nAnonymousAuthenticationProvider.incorrectKey=El actual AnonymousAuthenticationToken no contiene la clave esperada\nBindAuthenticator.badCredentials=Credenciales err\\u00F3neas\nBindAuthenticator.emptyPassword=Credenciales err\\u00F3neas\nCasAuthenticationProvider.incorrectKey=El actual CasAuthenticationToken no contiene la clave esperada\nCasAuthenticationProvider.noServiceTicket=No se ha podido proporcionar un billete de servicio CAS para validar\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Sesiones m\\u00E1ximas de {0} para esta Identificaci\\u00F3n excedidas\nDigestAuthenticationFilter.incorrectRealm=Respuesta realm de nombre {0} no coincide con realm del sistema de nombre {1}\nDigestAuthenticationFilter.incorrectResponse=Respuesta incorrecta\nDigestAuthenticationFilter.missingAuth=Valor digest obligatorio perdido para 'auth' QOP; header recibido {0}\nDigestAuthenticationFilter.missingMandatory=Valor digest obligatorio perdido; header recibido {0}\nDigestAuthenticationFilter.nonceCompromised=Nonce token comprometido {0}\nDigestAuthenticationFilter.nonceEncoding=Nonce no est\\u00E1 codificado en Base64; nonce recibido {0}\nDigestAuthenticationFilter.nonceExpired=Nonce ha expirado/fuera de tiempo\nDigestAuthenticationFilter.nonceNotNumeric=Nonce token deber\\u00EDa tener primero un token num\\u00E9rico, pero ten\\u00EDa {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce token deber\\u00EDa tener dos fichas pero ten\\u00EDa {0}\nDigestAuthenticationFilter.usernameNotFound=Usuario y nombre {0} no encontrado\nExceptionTranslationFilter.insufficientAuthentication=Para acceder a este recurso se requiere autenticaci\\u00f3n completa\nJdbcDaoImpl.noAuthority=Usuario {0} no tiene GrantedAuthority\nJdbcDaoImpl.notFound=Usuario {0} no encontrado\nLdapAuthenticationProvider.badCredentials=Credenciales err\\u00F3neas\nLdapAuthenticationProvider.badLdapConnection=Fall\\u00F3 la conexi\\u00F3n al servidor LDAP\nLdapAuthenticationProvider.credentialsExpired=Las credenciales del usuario han expirado\nLdapAuthenticationProvider.disabled=El usuario est\\u00E1 deshabilitado\nLdapAuthenticationProvider.expired=La cuenta del usuario ha expirado\nLdapAuthenticationProvider.locked=La cuenta del usuario est\\u00E1 bloqueada\nLdapAuthenticationProvider.emptyUsername=Usuario y nombre no permitido\nLdapAuthenticationProvider.onlySupports=S\\u00F3lo UsernamePasswordAuthenticationToken es soportada\nPasswordComparisonAuthenticator.badCredentials=Credenciales err\\u00F3neas\nPersistentTokenBasedRememberMeServices.cookieStolen=Discrepancia inv\\u00e1lida de token remember-me (Series/token). Implica un ataque de robo de cookie previo.\nProviderManager.providerNotFound=AuthenticationProvider no encontrado para {0}\nRememberMeAuthenticationProvider.incorrectKey=El actual RememberMeAuthenticationToken no contiene la clave esperada\nRunAsImplAuthenticationProvider.incorrectKey=El actual RunAsUserToken no contiene la clave esperada\nSubjectDnX509PrincipalExtractor.noMatching=No se ha encontrado un patr\\u00F3n coincidente en subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=No hay usuario actual asociado con esta petici\\u00F3n\nSwitchUserFilter.noOriginalAuthentication=No se puede encontrar el objeto Authentication original\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_fr.properties",
    "content": "# Spring security\n# Messages in French\n# Translation by Laurent Pireyn (laurent.pireyn@pisolutions.eu)\n# Translation by Valentin Crettaz (valentin.crettaz@consulthys.com)\nAbstractAccessDecisionManager.accessDenied=Acc\\u00E8s refus\\u00E9\nAbstractLdapAuthenticationProvider.emptyPassword=Le mot de passe est obligatoire\nAbstractSecurityInterceptor.authenticationNotFound=Aucun objet Authentication n''a \\u00E9t\\u00E9 trouv\\u00E9 dans le SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=Les identifications sont erron\\u00E9es\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Les identifications de l''utilisateur ont expir\\u00E9\nAbstractUserDetailsAuthenticationProvider.disabled=Le compte utilisateur est d\\u00E9sactiv\\u00E9\nAbstractUserDetailsAuthenticationProvider.expired=Le compte utilisateur a expir\\u00E9\nAbstractUserDetailsAuthenticationProvider.locked=Le compte utilisateur est bloqu\\u00E9\nAbstractUserDetailsAuthenticationProvider.onlySupports=Seul UsernamePasswordAuthenticationToken est pris en charge\nAccountStatusUserDetailsChecker.credentialsExpired=Les identifications de l''utilisateur ont expir\\u00E9\nAccountStatusUserDetailsChecker.disabled=Le compte utilisateur est d\\u00E9sactiv\\u00E9\nAccountStatusUserDetailsChecker.expired=Le compte utilisateur a expir\\u00E9\nAccountStatusUserDetailsChecker.locked=Le compte utilisateur est bloqu\\u00E9\nAclEntryAfterInvocationProvider.noPermission=L''authentification {0} n''a AUCUNE permission pour l''objet de domaine {1}\nAnonymousAuthenticationProvider.incorrectKey=L''AnonymousAuthenticationToken pr\\u00E9sent\\u00E9 ne contient pas la cl\\u00E9 attendue\nBindAuthenticator.badCredentials=Les identifications sont erron\\u00E9es\nBindAuthenticator.emptyPassword=Le mot de passe est obligatoire\nCasAuthenticationProvider.incorrectKey=Le CasAuthenticationToken pr\\u00E9sent\\u00E9 ne contient pas la cl\\u00E9 attendue\nCasAuthenticationProvider.noServiceTicket=Echec d''obtention d''un ticket CAS \\u00E0 valider\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Le maximum de {0} sessions a \\u00E9t\\u00E9 d\\u00E9pass\\u00E9 pour cet utilisateur\nDigestAuthenticationFilter.incorrectRealm=Le nom de domaine de la r\\u00E9ponse {0} ne correspond pas au nom de domaine du syst\\u00E8me {1}\nDigestAuthenticationFilter.incorrectResponse=R\\u00E9ponse incorrecte\nDigestAuthenticationFilter.missingAuth=Une valeur obligatoire manque au condens\\u00E9 pour ''auth'' QOP; re\\u00E7u l''ent\\u00EAte {0}\nDigestAuthenticationFilter.missingMandatory=Une valeur obligatoire manque au condens\\u00E9; re\\u00E7u l''ent\\u00EAte {0}\nDigestAuthenticationFilter.nonceCompromised=Le jeton nonce est compromis {0}\nDigestAuthenticationFilter.nonceEncoding=Le nonce n''est pas encod\\u00E9 en Base64; re\\u00E7u le nonce {0}\nDigestAuthenticationFilter.nonceExpired=Le nonce a expir\\u00E9\nDigestAuthenticationFilter.nonceNotNumeric=Le jeton nonce aurait d\\u00FB g\\u00E9n\\u00E9rer d''abord un jeton num\\u00E9rique, mais \\u00E9tait {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Le nonce aurait d\\u00FB g\\u00E9n\\u00E9rer deux jetons, mais \\u00E9tait {0}\nDigestAuthenticationFilter.usernameNotFound=Le nom d''utilisateur {0} n''a pas \\u00E9t\\u00E9 trouv\\u00E9\nJdbcDaoImpl.noAuthority=Le compte utilisateur {0} n''a pas de permission\nJdbcDaoImpl.notFound=Le nom d''utilisateur {0} n''a pas \\u00E9t\\u00E9 trouv\\u00E9\nLdapAuthenticationProvider.badCredentials=Les identifications sont erron\\u00E9es\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=Les identifications de l''utilisateur ont expir\\u00E9\nLdapAuthenticationProvider.disabled=Le compte utilisateur est d\\u00E9sactiv\\u00E9\nLdapAuthenticationProvider.expired=Le compte utilisateur a expir\\u00E9\nLdapAuthenticationProvider.locked=Le compte utilisateur est bloqu\\u00E9\nLdapAuthenticationProvider.emptyUsername=Le nom d''utilisateur est obligatoire\nLdapAuthenticationProvider.onlySupports=Seul UsernamePasswordAuthenticationToken est pris en charge\nPasswordComparisonAuthenticator.badCredentials=Les identifications sont erron\\u00E9es\nPersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=Aucun AuthenticationProvider n''a \\u00E9t\\u00E9 trouv\\u00E9 pour {0}\nRememberMeAuthenticationProvider.incorrectKey=Le RememberMeAuthenticationToken pr\\u00E9sent\\u00E9 ne contient pas la cl\\u00E9 attendue\nRunAsImplAuthenticationProvider.incorrectKey=Le RunAsUserToken pr\\u00E9sent\\u00E9 ne contient pas la cl\\u00E9 attendue\nSubjectDnX509PrincipalExtractor.noMatching=Aucun motif concordant n''a \\u00E9t\\u00E9 trouv\\u00E9 dans le subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=Aucun utilisateur n''est associ\\u00E9 \\u00E0 la requ\\u00EAte en cours\nSwitchUserFilter.noOriginalAuthentication=L''objet Authentication original n''a pas \\u00E9t\\u00E9 trouv\\u00E9\nExceptionTranslationFilter.insufficientAuthentication=Une authentification compl\\u00E8te est requise pour acc\\u00E9der \\u00E0 cette ressource\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_it.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=Accesso negato\n#AbstractLdapAuthenticationProvider.emptyPassword=Empty Password\nAbstractSecurityInterceptor.authenticationNotFound=Nessuna autenticazione trovata dentro il Security Context\nAbstractUserDetailsAuthenticationProvider.badCredentials=Credenziali non valide\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Credenziali dell'utente scadute\nAbstractUserDetailsAuthenticationProvider.disabled=Utente disabilitato\nAbstractUserDetailsAuthenticationProvider.expired=Account dell'utente scadute\nAbstractUserDetailsAuthenticationProvider.locked=Account dell'utente bloccato\nAbstractUserDetailsAuthenticationProvider.onlySupports=Solo UsernamePasswordAuthenticationToken \\u00E8 supportata\nAccountStatusUserDetailsChecker.credentialsExpired=Credenziali dell'utente scadute\nAccountStatusUserDetailsChecker.disabled=Utente disabilitato\nAccountStatusUserDetailsChecker.expired=Account dell'utente scaduto\nAccountStatusUserDetailsChecker.locked=Account dell'utente bloccato\nAclEntryAfterInvocationProvider.noPermission=L'autenticazione {0} non ha nessun permesso sull'oggetto di dominio {1}\nAnonymousAuthenticationProvider.incorrectKey=AnonymousAuthenticationToken non contiene la chiave prevista\nBindAuthenticator.badCredentials=Credenziali non valide\nBindAuthenticator.emptyPassword=Credenziali non valide\nCasAuthenticationProvider.incorrectKey=CasAuthenticationToken non contiene la chiave prevista\nCasAuthenticationProvider.noServiceTicket=Non \\u00E8 stato fornito un CAS service ticket valido\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Il numero massimo delle sessioni ({0}) per questo utente sono state superate\nDigestAuthenticationFilter.incorrectRealm=Il response realm name {0} non corrisponde al realm name di sistema {1}\nDigestAuthenticationFilter.incorrectResponse=Replica non corretta\nDigestAuthenticationFilter.missingAuth=Valore digest obbligatorio mancante per 'auth' QOP; ricevuto header {0}\nDigestAuthenticationFilter.missingMandatory=Valore digest obbligatorio mancante; ricevuto header {0}\nDigestAuthenticationFilter.nonceCompromised=Nonce token corrotto {0}\nDigestAuthenticationFilter.nonceEncoding=Nonce non codificato in Base64; ricevuto nonce {0}\nDigestAuthenticationFilter.nonceExpired=Nonce scaduto/in timeout\nDigestAuthenticationFilter.nonceNotNumeric=Nonce token avrebbe dovuto dare un primo token numerico invece di {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce avrebbe dovuto dare due token invece di {0}\nDigestAuthenticationFilter.usernameNotFound=Username {0} non trovato\nJdbcDaoImpl.noAuthority=L'utente {0} non ha permessi\nJdbcDaoImpl.notFound=Utente {0} non trovato\nLdapAuthenticationProvider.badCredentials=Credenziali non valide\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=Credenziali dell'utente scadute\nLdapAuthenticationProvider.disabled=Utente disabilitato\nLdapAuthenticationProvider.expired=Account dell'utente scadute\nLdapAuthenticationProvider.locked=Account dell'utente bloccato\nLdapAuthenticationProvider.emptyUsername=Username vuoto non consentito\nLdapAuthenticationProvider.onlySupports=Solo UsernamePasswordAuthenticationToken \\u00E8 supportata\nPasswordComparisonAuthenticator.badCredentials=Credenziali non valide\n#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=Nessun Provider di autenticazione trovato per {0}\nRememberMeAuthenticationProvider.incorrectKey=RememberMeAuthenticationToken non contiene la chiave prevista\nRunAsImplAuthenticationProvider.incorrectKey=RunAsUserToken non contiene la chiave prevista\nSubjectDnX509PrincipalExtractor.noMatching=Non \\u00E8 stato validato correttamente il subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=Nessun utente corrente associato con questa richiesta\nSwitchUserFilter.noOriginalAuthentication=Impossibile trovare l'Authentication object originale\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_ja.properties",
    "content": "# Spring security\n# Messages in Japanese\n# Translation by Akane Shimamuko\nAbstractAccessDecisionManager.accessDenied=\\u30a2\\u30af\\u30bb\\u30b9\\u304c\\u62d2\\u5426\\u3055\\u308c\\u307e\\u3057\\u305f\nAbstractLdapAuthenticationProvider.emptyPassword=\\u30e6\\u30fc\\u30b6\\u540d\\u304b\\u30d1\\u30b9\\u30ef\\u30fc\\u30c9\\u304c\\u6b63\\u3057\\u304f\\u3042\\u308a\\u307e\\u305b\\u3093\nAbstractSecurityInterceptor.authenticationNotFound=\\u8a8d\\u8a3c\\u30aa\\u30d6\\u30b8\\u30a7\\u30af\\u30c8\\u304cSecurityContext\\u5185\\u306b\\u898b\\u3064\\u304b\\u308a\\u307e\\u305b\\u3093\\u3067\\u3057\\u305f\nAbstractUserDetailsAuthenticationProvider.badCredentials=\\u30e6\\u30fc\\u30b6\\u540d\\u304b\\u30d1\\u30b9\\u30ef\\u30fc\\u30c9\\u304c\\u6b63\\u3057\\u304f\\u3042\\u308a\\u307e\\u305b\\u3093\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=\\u30e6\\u30fc\\u30b6\\u8a8d\\u8a3c\\u60c5\\u5831\\u306e\\u6709\\u52b9\\u671f\\u9650\\u304c\\u5207\\u308c\\u3066\\u3044\\u307e\\u3059\nAbstractUserDetailsAuthenticationProvider.disabled=\\u7121\\u52b9\\u306a\\u30e6\\u30fc\\u30b6\\u3067\\u3059\nAbstractUserDetailsAuthenticationProvider.expired=\\u30e6\\u30fc\\u30b6\\u30a2\\u30ab\\u30a6\\u30f3\\u30c8\\u306e\\u6709\\u52b9\\u671f\\u9650\\u304c\\u5207\\u308c\\u3066\\u3044\\u307e\\u3059\nAbstractUserDetailsAuthenticationProvider.locked=\\u30e6\\u30fc\\u30b6\\u30a2\\u30ab\\u30a6\\u30f3\\u30c8\\u304c\\u30ed\\u30c3\\u30af\\u3055\\u308c\\u3066\\u3044\\u307e\\u3059\nAbstractUserDetailsAuthenticationProvider.onlySupports=UsernamePasswordAuthenticationToken\\u306e\\u307f\\u30b5\\u30dd\\u30fc\\u30c8\\u3055\\u308c\\u3066\\u3044\\u307e\\u3059\nAccountStatusUserDetailsChecker.credentialsExpired=\\u30e6\\u30fc\\u30b6\\u8a8d\\u8a3c\\u60c5\\u5831\\u306e\\u6709\\u52b9\\u671f\\u9650\\u304c\\u5207\\u308c\\u3066\\u3044\\u307e\\u3059\nAccountStatusUserDetailsChecker.disabled=\\u7121\\u52b9\\u306a\\u30e6\\u30fc\\u30b6\\u3067\\u3059\nAccountStatusUserDetailsChecker.expired=\\u30e6\\u30fc\\u30b6\\u30a2\\u30ab\\u30a6\\u30f3\\u30c8\\u306e\\u6709\\u52b9\\u671f\\u9650\\u304c\\u5207\\u308c\\u3066\\u3044\\u307e\\u3059\nAccountStatusUserDetailsChecker.locked=\\u30e6\\u30fc\\u30b6\\u30a2\\u30ab\\u30a6\\u30f3\\u30c8\\u304c\\u30ed\\u30c3\\u30af\\u3055\\u308c\\u3066\\u3044\\u307e\\u3059\nAclEntryAfterInvocationProvider.noPermission={0}\\u306f{1}\\u306b\\u5bfe\\u3059\\u308b\\u6a29\\u9650\\u304c\\u3042\\u308a\\u307e\\u305b\\u3093\nAnonymousAuthenticationProvider.incorrectKey=\\u63d0\\u793a\\u3055\\u308c\\u305fAnonymousAuthenticationToken\\u306b\\u306f\\u671f\\u5f85\\u3055\\u308c\\u308b\\u30ad\\u30fc\\u304c\\u542b\\u307e\\u308c\\u3066\\u3044\\u307e\\u305b\\u3093\nBindAuthenticator.badCredentials=\\u30e6\\u30fc\\u30b6\\u540d\\u304b\\u30d1\\u30b9\\u30ef\\u30fc\\u30c9\\u304c\\u6b63\\u3057\\u304f\\u3042\\u308a\\u307e\\u305b\\u3093\nBindAuthenticator.emptyPassword=\\u30e6\\u30fc\\u30b6\\u540d\\u304b\\u30d1\\u30b9\\u30ef\\u30fc\\u30c9\\u304c\\u6b63\\u3057\\u304f\\u3042\\u308a\\u307e\\u305b\\u3093\nCasAuthenticationProvider.incorrectKey=\\u3053\\u306eCasAuthenticationToken\\u306b\\u306f\\u671f\\u5f85\\u3055\\u308c\\u308b\\u30ad\\u30fc\\u304c\\u542b\\u307e\\u308c\\u3066\\u3044\\u307e\\u305b\\u3093\nCasAuthenticationProvider.noServiceTicket=\\u691c\\u8a3c\\u5bfe\\u8c61\\u306eCAS\\u30b5\\u30fc\\u30d3\\u30b9\\u30c1\\u30b1\\u30c3\\u30c8\\u306e\\u30b9\\u30bf\\u30d6\\u304c\\u6b63\\u3057\\u304f\\u6307\\u5b9a\\u3055\\u308c\\u3066\\u3044\\u307e\\u305b\\u3093\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=\\u3053\\u306eprincipal\\u306b\\u8a31\\u53ef\\u3055\\u308c\\u308b\\u6700\\u5927\\u30bb\\u30c3\\u30b7\\u30e7\\u30f3\\u6570{0}\\u3092\\u8d85\\u3048\\u307e\\u3057\\u305f\nDigestAuthenticationFilter.incorrectRealm=\\u5fdc\\u7b54\\u7d50\\u679c\\u306e\\u30ec\\u30eb\\u30e0\\u540d:{0}\\u304c\\u30b7\\u30b9\\u30c6\\u30e0\\u30ec\\u30eb\\u30e0\\u540d:{1}\\u3068\\u4e00\\u81f4\\u3057\\u307e\\u305b\\u3093\nDigestAuthenticationFilter.incorrectResponse=\\u30a8\\u30e9\\u30fc\\u30ec\\u30b9\\u30dd\\u30f3\\u30b9\\u7d50\\u679c\nDigestAuthenticationFilter.missingAuth=qop\\u306e'auth'\\u306b\\u5fc5\\u8981\\u306a\\u30c0\\u30a4\\u30b8\\u30a7\\u30b9\\u30c8\\u5024\\u304c\\u3042\\u308a\\u307e\\u305b\\u3093\\u3002\\u53d7\\u3051\\u53d6\\u3063\\u305f\\u30d8\\u30c3\\u30c0\\u60c5\\u5831:{0}\nDigestAuthenticationFilter.missingMandatory=\\u5fc5\\u8981\\u306a\\u30c0\\u30a4\\u30b8\\u30a7\\u30b9\\u30c8\\u5024\\u304c\\u3042\\u308a\\u307e\\u305b\\u3093\\u3002\\u53d7\\u3051\\u53d6\\u3063\\u305f\\u30d8\\u30c3\\u30c0\\u60c5\\u5831:{0}\nDigestAuthenticationFilter.nonceCompromised=\\u30ef\\u30f3\\u30bf\\u30a4\\u30e0\\u30c8\\u30fc\\u30af\\u30f3{0}\\u306b\\u306f\\u554f\\u984c\\u304c\\u3042\\u308a\\u307e\\u3059\nDigestAuthenticationFilter.nonceEncoding=\\u30ef\\u30f3\\u30bf\\u30a4\\u30e0\\u30c8\\u30fc\\u30af\\u30f3\\u306fBase64\\u3067\\u30a8\\u30f3\\u30b3\\u30fc\\u30c9\\u3055\\u308c\\u3066\\u3044\\u307e\\u305b\\u3093\\u3002\\u53d7\\u3051\\u53d6\\u3063\\u305f\\u30ef\\u30f3\\u30bf\\u30a4\\u30e0\\u30c8\\u30fc\\u30af\\u30f3:{0}\nDigestAuthenticationFilter.nonceExpired=\\u30ef\\u30f3\\u30bf\\u30a4\\u30e0\\u30c8\\u30fc\\u30af\\u30f3\\u306f\\u671f\\u9650\\u5207\\u308c\\u304b\\u3001\\u30bf\\u30a4\\u30e0\\u30a2\\u30a6\\u30c8\\u3057\\u307e\\u3057\\u305f\nDigestAuthenticationFilter.nonceNotNumeric=\\u30ef\\u30f3\\u30bf\\u30a4\\u30e0\\u30c8\\u30fc\\u30af\\u30f3\\u306e\\u5148\\u982d\\u306f\\u6570\\u5b57\\u3067\\u306a\\u3051\\u308c\\u3070\\u306a\\u308a\\u307e\\u305b\\u3093\\u304c\\u3001\\u7d50\\u679c\\u306f{0}\\u3067\\u3057\\u305f\nDigestAuthenticationFilter.nonceNotTwoTokens=\\u30ef\\u30f3\\u30bf\\u30a4\\u30e0\\u30c8\\u30fc\\u30af\\u30f3\\u306f2\\u3064\\u751f\\u6210\\u3055\\u308c\\u308b\\u306f\\u305a\\u3067\\u3059\\u304c\\u3001\\u7d50\\u679c\\u306f{0}\\u3067\\u3057\\u305f\nDigestAuthenticationFilter.usernameNotFound=\\u30e6\\u30fc\\u30b6\\u540d:{0}\\u304c\\u898b\\u3064\\u304b\\u308a\\u307e\\u305b\\u3093\nExceptionTranslationFilter.insufficientAuthentication=\\u3053\\u306e\\u30ea\\u30bd\\u30fc\\u30b9\\u306b\\u30a2\\u30af\\u30bb\\u30b9\\u3059\\u308b\\u306b\\u306f\\u8a8d\\u8a3c\\u3092\\u3059\\u308b\\u5fc5\\u8981\\u304c\\u3042\\u308a\\u307e\\u3059\nJdbcDaoImpl.noAuthority=\\u30e6\\u30fc\\u30b6:{0}\\u306b\\u306f\\u6a29\\u9650\\u304c\\u3042\\u308a\\u307e\\u305b\\u3093\nJdbcDaoImpl.notFound=\\u30e6\\u30fc\\u30b6:{0}\\u304c\\u898b\\u3064\\u304b\\u308a\\u307e\\u305b\\u3093\nLdapAuthenticationProvider.badCredentials=\\u30e6\\u30fc\\u30b6\\u540d\\u304b\\u30d1\\u30b9\\u30ef\\u30fc\\u30c9\\u304c\\u6b63\\u3057\\u304f\\u3042\\u308a\\u307e\\u305b\\u3093\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=\\u30e6\\u30fc\\u30b6\\u8a8d\\u8a3c\\u60c5\\u5831\\u306e\\u6709\\u52b9\\u671f\\u9650\\u304c\\u5207\\u308c\\u3066\\u3044\\u307e\\u3059\nLdapAuthenticationProvider.disabled=\\u7121\\u52b9\\u306a\\u30e6\\u30fc\\u30b6\\u3067\\u3059\nLdapAuthenticationProvider.expired=\\u30e6\\u30fc\\u30b6\\u30a2\\u30ab\\u30a6\\u30f3\\u30c8\\u306e\\u6709\\u52b9\\u671f\\u9650\\u304c\\u5207\\u308c\\u3066\\u3044\\u307e\\u3059\nLdapAuthenticationProvider.locked=\\u30e6\\u30fc\\u30b6\\u30a2\\u30ab\\u30a6\\u30f3\\u30c8\\u304c\\u30ed\\u30c3\\u30af\\u3055\\u308c\\u3066\\u3044\\u307e\\u3059\nLdapAuthenticationProvider.emptyUsername=\\u30e6\\u30fc\\u30b6\\u540d\\u306f\\u7a7a\\u306b\\u3067\\u304d\\u307e\\u305b\\u3093\nLdapAuthenticationProvider.onlySupports=UsernamePasswordAuthenticationToken\\u306e\\u307f\\u30b5\\u30dd\\u30fc\\u30c8\\u3055\\u308c\\u3066\\u3044\\u307e\\u3059\nPasswordComparisonAuthenticator.badCredentials=\\u30e6\\u30fc\\u30b6\\u540d\\u304b\\u30d1\\u30b9\\u30ef\\u30fc\\u30c9\\u304c\\u6b63\\u3057\\u304f\\u3042\\u308a\\u307e\\u305b\\u3093\n#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound={0}\\u306eAuthenticationProvider\\u304c\\u898b\\u3064\\u304b\\u308a\\u307e\\u305b\\u3093\nRememberMeAuthenticationProvider.incorrectKey=\\u63d0\\u793a\\u3055\\u308c\\u305fRememberMeAuthenticationToken\\u306b\\u306f\\u671f\\u5f85\\u3055\\u308c\\u308b\\u30ad\\u30fc\\u304c\\u542b\\u307e\\u308c\\u3066\\u3044\\u307e\\u305b\\u3093\nRunAsImplAuthenticationProvider.incorrectKey=\\u63d0\\u793a\\u3055\\u308c\\u305fRunAsUserToken\\u306b\\u306f\\u671f\\u5f85\\u3055\\u308c\\u308b\\u30ad\\u30fc\\u304c\\u542b\\u307e\\u308c\\u3066\\u3044\\u307e\\u305b\\u3093\nSubjectDnX509PrincipalExtractor.noMatching=subjectDN:{0}\\u306b\\u4e00\\u81f4\\u3059\\u308b\\u30d1\\u30bf\\u30fc\\u30f3\\u304c\\u898b\\u3064\\u304b\\u308a\\u307e\\u305b\\u3093\nSwitchUserFilter.noCurrentUser=\\u3053\\u306e\\u30e6\\u30fc\\u30b6\\u306f\\u5b58\\u5728\\u3057\\u307e\\u305b\\u3093\nSwitchUserFilter.noOriginalAuthentication=\\u30aa\\u30ea\\u30b8\\u30ca\\u30eb\\u306e\\u8a8d\\u8a3c\\u30aa\\u30d6\\u30b8\\u30a7\\u30af\\u30c8\\u304c\\u898b\\u3064\\u304b\\u308a\\u307e\\u305b\\u3093\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_ko_KR.properties",
    "content": "AbstractAccessDecisionManager.accessDenied = \\uc811\\uadfc\\uc774 \\uac70\\ubd80\\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\nAbstractLdapAuthenticationProvider.emptyPassword = \\ube44\\ubc00\\ubc88\\ud638\\uac00 \\ub9de\\uc9c0 \\uc54a\\uc2b5\\ub2c8\\ub2e4.\nAbstractSecurityInterceptor.authenticationNotFound = SecurityContext\\uc5d0\\uc11c Authentication \\uac1d\\uccb4\\ub97c \\ucc3e\\uc744 \\uc218 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nAbstractUserDetailsAuthenticationProvider.badCredentials = \\uc790\\uaca9 \\uc99d\\uba85\\uc5d0 \\uc2e4\\ud328\\ud558\\uc600\\uc2b5\\ub2c8\\ub2e4.\nAbstractUserDetailsAuthenticationProvider.credentialsExpired = \\uc790\\uaca9 \\uc99d\\uba85 \\uc720\\ud6a8 \\uae30\\uac04\\uc774 \\ub9cc\\ub8cc\\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\nAbstractUserDetailsAuthenticationProvider.disabled = \\uc720\\ud6a8\\ud558\\uc9c0 \\uc54a\\uc740 \\uc0ac\\uc6a9\\uc790\\uc785\\ub2c8\\ub2e4.\nAbstractUserDetailsAuthenticationProvider.expired = \\uc0ac\\uc6a9\\uc790 \\uacc4\\uc815\\uc758 \\uc720\\ud6a8 \\uae30\\uac04\\uc774 \\ub9cc\\ub8cc \\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\nAbstractUserDetailsAuthenticationProvider.locked = \\uc0ac\\uc6a9\\uc790 \\uacc4\\uc815\\uc774 \\uc7a0\\uaca8 \\uc788\\uc2b5\\ub2c8\\ub2e4.\nAbstractUserDetailsAuthenticationProvider.onlySupports = UsernamePasswordAuthenticationToken\\ub9cc \\uc9c0\\uc6d0\\ud569\\ub2c8\\ub2e4.\nAccountStatusUserDetailsChecker.credentialsExpired = \\uc790\\uaca9 \\uc99d\\uba85 \\uc720\\ud6a8 \\uae30\\uac04\\uc774 \\ub9cc\\ub8cc\\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\nAccountStatusUserDetailsChecker.disabled = \\uc720\\ud6a8\\ud558\\uc9c0 \\uc54a\\uc740 \\uc0ac\\uc6a9\\uc790\\uc785\\ub2c8\\ub2e4.\nAccountStatusUserDetailsChecker.expired = \\uc0ac\\uc6a9\\uc790 \\uacc4\\uc815\\uc758 \\uc720\\ud6a8 \\uae30\\uac04\\uc774 \\ub9cc\\ub8cc \\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\nAccountStatusUserDetailsChecker.locked = \\uc0ac\\uc6a9\\uc790 \\uacc4\\uc815\\uc774 \\uc7a0\\uaca8 \\uc788\\uc2b5\\ub2c8\\ub2e4.\nAclEntryAfterInvocationProvider.noPermission = domain object {1}\\uc5d0 \\ub300\\ud55c \\uad8c\\ud55c\\uc774 Authentication {0}\\uc5d0 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nAnonymousAuthenticationProvider.incorrectKey = \\uc81c\\uacf5\\ub41c AnonymousAuthenticationToken\\uc5d0\\ub294 \\ud544\\uc694\\ub85c\\ud558\\ub294 key\\uac00 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nBindAuthenticator.badCredentials = \\uc790\\uaca9 \\uc99d\\uba85\\uc5d0 \\uc2e4\\ud328\\ud558\\uc600\\uc2b5\\ub2c8\\ub2e4.\nBindAuthenticator.emptyPassword = \\ube44\\ubc00\\ubc88\\ud638 \\ud56d\\ubaa9\\uc774 \\ube44\\uc5b4 \\uc788\\uc2b5\\ub2c8\\ub2e4.\nCasAuthenticationProvider.incorrectKey = \\uc81c\\uacf5\\ub41c CasAuthenticationToken\\uc5d0\\ub294 \\ud544\\uc694\\ub85c \\ud558\\ub294 key\\uac00 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nCasAuthenticationProvider.noServiceTicket = \\uac80\\uc99d\\uc744 \\uc704\\ud55c CAS \\uc11c\\ube44\\uc2a4 \\ud2f0\\ucf13\\uc744 \\uc81c\\uacf5\\ud560 \\uc218 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed = \\ucd5c\\ub300 \\uc138\\uc158 \\ud5c8\\uc6a9 \\uc218 {0}\\uac1c\\ub97c \\ucd08\\uacfc\\ud558\\uc600\\uc2b5\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.incorrectRealm = \\uc751\\ub2f5 realm \\uc774\\ub984 {0}\\uacfc \\uc2dc\\uc2a4\\ud15c realm \\uc774\\ub984 {1}\\uc774 \\uc77c\\uce58\\ud558\\uc9c0 \\uc54a\\uc2b5\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.incorrectResponse = \\uc751\\ub2f5\\uc774 \\uc815\\ud655\\ud558\\uc9c0 \\uc54a\\uc2b5\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.missingAuth = 'auth' QOP(quality of protection)\\ub97c \\uc704\\ud55c digest \\uac12\\uc740 \\ud544\\uc218 \\ud56d\\ubaa9\\uc785\\ub2c8\\ub2e4. \\ud604\\uc7ac header \\uac12\\uc740 {0}\\uc785\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.missingMandatory = digest \\uac12\\uc740 \\ud544\\uc218 \\ud56d\\ubaa9\\uc785\\ub2c8\\ub2e4. \\ud604\\uc7ac header \\uac12\\uc740 {0}\\uc785\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.nonceCompromised = Nonce \\ud1a0\\ud070\\uc774 \\uc190\\uc0c1\\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4. \\ud604\\uc7ac nonce \\uac12\\uc740 {0}\\uc785\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.nonceEncoding = Nonce \\uac12\\uc774 Base64\\ub85c \\uc778\\ucf54\\ub529 \\ub418\\uc5b4\\uc788\\uc9c0 \\uc54a\\uc2b5\\ub2c8\\ub2e4. \\ud604\\uc7ac nonce \\uac12\\uc740 {0}\\uc785\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.nonceExpired = Nonce\\uc758 \\uc720\\ud6a8 \\uae30\\uac04\\uc774 \\ub9cc\\ub8cc\\ub418\\uc5c8\\uac70\\ub098 \\uc2dc\\uac04\\uc774 \\ucd08\\uacfc\\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.nonceNotNumeric = Nonce \\ud1a0\\ud070\\uc758 \\uccab \\uae00\\uc790\\ub294 \\uc22b\\uc790\\ub85c \\uc2dc\\uc791\\ud574\\uc57c \\ud569\\ub2c8\\ub2e4. \\ud604\\uc7ac nonce \\uac12\\uc740 {0}\\uc785\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.nonceNotTwoTokens = Nonce\\ub294 \\ub450 \\uac1c\\uc758 \\ud1a0\\ud070\\uc744 \\ub9cc\\ub4e4\\uc5b4\\uc57c \\ud569\\ub2c8\\ub2e4. \\ud604\\uc7ac nonce \\uac12\\uc740 {0}\\uc785\\ub2c8\\ub2e4.\nDigestAuthenticationFilter.usernameNotFound = {0} ID\\ub97c \\ucc3e\\uc744 \\uc218 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nJdbcDaoImpl.noAuthority = {0} \\uc0ac\\uc6a9\\uc790\\ub294 \\uad8c\\ud55c\\uc774 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nJdbcDaoImpl.notFound = {0} \\uc0ac\\uc6a9\\uc790\\ub97c \\ucc3e\\uc744 \\uc218 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nLdapAuthenticationProvider.badCredentials = \\uc790\\uaca9 \\uc99d\\uba85\\uc5d0 \\uc2e4\\ud328\\ud558\\uc600\\uc2b5\\ub2c8\\ub2e4.\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired = \\uc790\\uaca9 \\uc99d\\uba85 \\uc720\\ud6a8 \\uae30\\uac04\\uc774 \\ub9cc\\ub8cc\\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\nLdapAuthenticationProvider.disabled = \\uc720\\ud6a8\\ud558\\uc9c0 \\uc54a\\uc740 \\uc0ac\\uc6a9\\uc790\\uc785\\ub2c8\\ub2e4.\nLdapAuthenticationProvider.expired = \\uc0ac\\uc6a9\\uc790 \\uacc4\\uc815\\uc758 \\uc720\\ud6a8 \\uae30\\uac04\\uc774 \\ub9cc\\ub8cc \\ub418\\uc5c8\\uc2b5\\ub2c8\\ub2e4.\nLdapAuthenticationProvider.locked = \\uc0ac\\uc6a9\\uc790 \\uacc4\\uc815\\uc774 \\uc7a0\\uaca8 \\uc788\\uc2b5\\ub2c8\\ub2e4.\nLdapAuthenticationProvider.emptyUsername = ID\\uc5d0 \\uacf5\\ubc31\\uc740 \\ud5c8\\uc6a9\\ub418\\uc9c0 \\uc54a\\uc2b5\\ub2c8\\ub2e4.\nLdapAuthenticationProvider.onlySupports = UsernamePasswordAuthenticationToken\\ub9cc \\uc9c0\\uc6d0\\ud569\\ub2c8\\ub2e4.\nPasswordComparisonAuthenticator.badCredentials = \\uc790\\uaca9 \\uc99d\\uba85\\uc5d0 \\uc2e4\\ud328\\ud558\\uc600\\uc2b5\\ub2c8\\ub2e4.\nPersistentTokenBasedRememberMeServices.cookieStolen = \\ub85c\\uadf8\\uc778 \\uc0c1\\ud0dc \\uc720\\uc9c0\\ub97c \\uc704\\ud55c \\ud1a0\\ud070\\uc774 \\uc77c\\uce58\\ud558\\uc9c0 \\uc54a\\uc2b5\\ub2c8\\ub2e4. \\uc774\\uc804\\uc5d0 \\uc0ac\\uc6a9\\ud55c \\ud1a0\\ud070\\uc774 \\ud0c0\\uc778\\uc73c\\ub85c\\ubd80\\ud130 \\ud0c8\\ucde8 \\ub2f9\\ud588\\uc744 \\uc218 \\uc788\\uc2b5\\ub2c8\\ub2e4.\nProviderManager.providerNotFound = {0}\\uc744 \\uc704\\ud55c AuthenticationProvider\\ub97c \\ucc3e\\uc744 \\uc218 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nRememberMeAuthenticationProvider.incorrectKey = \\uc81c\\uacf5\\ub41c RememberMeAuthenticationToken\\uc5d0\\ub294 \\ud544\\uc694\\ub85c \\ud558\\ub294 key\\uac00 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nRunAsImplAuthenticationProvider.incorrectKey = \\uc81c\\uacf5\\ub41c RunAsUserToken\\uc5d0\\ub294 \\ud544\\uc694\\ub85c \\ud558\\ub294 key\\uac00 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nSubjectDnX509PrincipalExtractor.noMatching = subjectDN\\: {0} \\ub0b4\\uc5d0 \\ub9e4\\uce6d\\ub418\\ub294 \\ud328\\ud134\\uc774 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nSwitchUserFilter.noCurrentUser = \\uc694\\uccad\\ud55c \\uc0ac\\uc6a9\\uc790\\ub97c \\ucc3e\\uc744 \\uc218 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\nSwitchUserFilter.noOriginalAuthentication = Authentication \\uac1d\\uccb4\\uc758 \\uc6d0\\ubcf8\\uc744 \\ucc3e\\uc744 \\uc218 \\uc5c6\\uc2b5\\ub2c8\\ub2e4.\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_lt.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=Pri\\u0117jimas neleid\\u017eiamas\nAbstractLdapAuthenticationProvider.emptyPassword=Tu\\u0161\\u010dias slapta\\u017eodis\nAbstractSecurityInterceptor.authenticationNotFound=Authentication objektas nerastas SecurityContext kontekste\nAbstractUserDetailsAuthenticationProvider.badCredentials=Blogi kredencialai\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Pasibaig\\u0117 vartotojo kredencial\\u0173 galiojimas\nAbstractUserDetailsAuthenticationProvider.disabled=Vartotojas neveiksnus\nAbstractUserDetailsAuthenticationProvider.expired=Pasibaig\\u0117 vartotojo paskyros galiojimas\nAbstractUserDetailsAuthenticationProvider.locked=Vartotojo paskyra u\\u017erakinta\nAbstractUserDetailsAuthenticationProvider.onlySupports=Priimamas tik UsernamePasswordAuthenticationToken\nAccountStatusUserDetailsChecker.credentialsExpired=Pasibaig\\u0117 vartotojo kredencial\\u0173 galiojimas\nAccountStatusUserDetailsChecker.disabled=Vartotojas neveiksnus\nAccountStatusUserDetailsChecker.expired=Pasibaig\\u0117 vartotojo paskyros galiojimas\nAccountStatusUserDetailsChecker.locked=Vartotojo paskyra u\\u017erakinta\nAclEntryAfterInvocationProvider.noPermission=Tapatumas {0} n\\u0117ra prileid\\u017eiamas prie domeno objekto {1}\nAnonymousAuthenticationProvider.incorrectKey=Duotasis AnonymousAuthenticationToken neturi laukiamo rakto\nBindAuthenticator.badCredentials=Blogi kredencialai\nBindAuthenticator.emptyPassword=Tu\\u0161\\u010dias slapta\\u017eodis\nCasAuthenticationProvider.incorrectKey=Duotasis CasAuthenticationToken neturi laukiamo rakto\nCasAuthenticationProvider.noServiceTicket=Nepavyko pateikti CAS paslaugos bilieto validavimui\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Vartotojas vir\\u0161ijo maksimal\\u0173 leid\\u017eiam\\u0105 {0} sesij\\u0173 skai\\u010di\\u0173\nDigestAuthenticationFilter.incorrectRealm=Atsako srities pavadinimas {0} nesutampa su sisteminiu srities pavadinimu {1}\nDigestAuthenticationFilter.incorrectResponse=Neteisingas atsakas\nDigestAuthenticationFilter.missingAuth=Tr\\u016bksta privalomos 'auth' QOP mai\\u0161os reik\\u0161m\\u0117s; gauta antra\\u0161t\\u0117 {0}\nDigestAuthenticationFilter.missingMandatory=Tr\\u016bksta privalomos mai\\u0161os reik\\u0161m\\u0117s; gauta antra\\u0161t\\u0117 {0}\nDigestAuthenticationFilter.nonceCompromised=Sukompromituotas \"Nonce\" prieigos raktas {0}\nDigestAuthenticationFilter.nonceEncoding=\"Nonce\" n\\u0117ra koduotas Base64; gauta reik\\u0161m\\u0117 {0}\nDigestAuthenticationFilter.nonceExpired=\"Nonce\" galiojimo laikas baig\\u0117si\nDigestAuthenticationFilter.nonceNotNumeric=\"Nonce\" atskleistas pirmasis prieigos raktas tur\\u0117jo b\\u016bti skai\\u010dius, o buvo {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=\"Nonce\" tur\\u0117jo atskleisti du prieigos raktus, o buvo {0}\nDigestAuthenticationFilter.usernameNotFound=Vartotojo vardas {0} nerastas\n#ExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource\nJdbcDaoImpl.noAuthority=Vartotojas {0} neturi GrantedAuthority\nJdbcDaoImpl.notFound=Vartotojas {0} nerastas\nLdapAuthenticationProvider.badCredentials=Blogi kredencialai\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\n#LdapAuthenticationProvider.credentialsExpired=User credentials have expired\n#LdapAuthenticationProvider.disabled=User is disabled\n#LdapAuthenticationProvider.expired=User account has expired\n#LdapAuthenticationProvider.locked=User account is locked\nLdapAuthenticationProvider.emptyUsername=Tu\\u0161\\u010dias vartotojo vardas neleid\\u017eiamas\nLdapAuthenticationProvider.onlySupports=Priimamas tik UsernamePasswordAuthenticationToken\nPasswordComparisonAuthenticator.badCredentials=Blogi kredencialai\nPersistentTokenBasedRememberMeServices.cookieStolen=Neleistinas \"prisimink mane\" prieigos rakto ir serijos nesutapimas. Numanoma ankstesn\\u0117 slapuko vagyst\\u0117s ataka.\nProviderManager.providerNotFound=Nerastas AuthenticationProvider, kuris atitikt\\u0173 {0}\nRememberMeAuthenticationProvider.incorrectKey=Duotasis RememberMeAuthenticationToken neturi laukiamo rakto\nRunAsImplAuthenticationProvider.incorrectKey=Duotasis RunAsUserToken neturi laukiamo rakto\nSubjectDnX509PrincipalExtractor.noMatching=Nerasta seka, kuri atitikt\\u0173 \"subjectDN\" lauk\\u0105: {0}\nSwitchUserFilter.noCurrentUser=Joks vartotojas n\\u0117ra susietas su \\u0161ia u\\u017eklausa\nSwitchUserFilter.noOriginalAuthentication=Nerastas originalus Authentication objektas\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_mn_MN.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=\\u042d\\u0440\\u0445 \\u0445\\u04af\\u0440\\u044d\\u043b\\u0446\\u044d\\u0445\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\nAbstractLdapAuthenticationProvider.emptyPassword=\\u041d\\u0443\\u0443\\u0446 \\u04af\\u0433\\u044d\\u044d \\u043e\\u0440\\u0443\\u0443\\u043b\\u043d\\u0430 \\u0443\\u0443\nAbstractSecurityInterceptor.authenticationNotFound=SecurityContext \\u0434\\u044d\\u044d\\u0440 \\u044f\\u043c\\u0430\\u0440 Authentication \\u043e\\u043b\\u0434\\u0441\\u043e\\u043d\\u0433\\u04af\\u0439\nAbstractUserDetailsAuthenticationProvider.badCredentials=\\u041d\\u044d\\u0432\\u0442\\u0440\\u044d\\u0445 \\u043d\\u044d\\u0440 \\u044d\\u0441\\u0432\\u044d\\u043b \\u043d\\u0443\\u0443\\u0446 \\u04af\\u0433 \\u0431\\u0443\\u0440\\u0443\\u0443 \\u0431\\u0430\\u0439\\u043d\\u0430\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=\\u0425\\u0430\\u043d\\u0434\\u0430\\u0445 \\u044d\\u0440\\u0445\\u0438\\u0439\\u043d \\u0445\\u0443\\u0433\\u0430\\u0446\\u0430\\u0430 \\u0434\\u0443\\u0443\\u0441\\u0441\\u0430\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nAbstractUserDetailsAuthenticationProvider.disabled=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u0433 \\u0445\\u043e\\u0440\\u0438\\u0433\\u043b\\u043e\\u0441\\u043e\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nAbstractUserDetailsAuthenticationProvider.expired=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u043d \\u0430\\u043a\\u043a\\u043e\\u0443\\u043d\\u0442\\u044b\\u043d \\u0445\\u0443\\u0433\\u0430\\u0446\\u0430\\u0430 \\u0434\\u0443\\u0443\\u0441\\u0441\\u0430\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nAbstractUserDetailsAuthenticationProvider.locked=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u043d \\u0430\\u043a\\u043a\\u043e\\u0443\\u043d\\u0442\\u044b\\u0433 \\u0442\\u04af\\u0433\\u0436\\u0441\\u044d\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nAbstractUserDetailsAuthenticationProvider.onlySupports=\\u0417\\u04e9\\u0432\\u0445\\u04e9\\u043d UsernamePasswordAuthenticationToken \\u0430\\u0448\\u0438\\u0433\\u043b\\u0430\\u0445 \\u0431\\u043e\\u043b\\u043e\\u043c\\u0436\\u0442\\u043e\\u0439\nAccountStatusUserDetailsChecker.credentialsExpired=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u043d \\u044d\\u0440\\u0445\\u0438\\u0439\\u043d \\u0445\\u0443\\u0433\\u0430\\u0446\\u0430\\u0430 \\u0434\\u0443\\u0443\\u0441\\u0441\\u0430\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nAccountStatusUserDetailsChecker.disabled=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u0433 \\u0445\\u043e\\u0440\\u0438\\u0433\\u043b\\u043e\\u0441\\u043e\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nAccountStatusUserDetailsChecker.expired=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u043d \\u0430\\u043a\\u043a\\u043e\\u0443\\u043d\\u0442\\u044b\\u043d \\u0445\\u0443\\u0433\\u0430\\u0446\\u0430\\u0430 \\u0434\\u0443\\u0443\\u0441\\u0441\\u0430\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nAccountStatusUserDetailsChecker.locked=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u0433 \\u0445\\u043e\\u0440\\u0438\\u0433\\u043b\\u043e\\u0441\\u043e\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nAclEntryAfterInvocationProvider.noPermission=\\u0422\\u0430\\u043d\\u0438\\u043b\\u0442 {0} \\u043d\\u044c {1} \\u0440\\u04af\\u04af \\u0445\\u0430\\u043d\\u0434\\u0430\\u0445 \\u044f\\u043c\\u0430\\u0440 \\u0447 \\u0437\\u04e9\\u0432\\u0448\\u04e9\\u04e9\\u0440\\u04e9\\u043b\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\nAnonymousAuthenticationProvider.incorrectKey=\\u04e8\\u0433\\u04e9\\u0433\\u0434\\u0441\\u04e9\\u043d AnonymousAuthenticationToken \\u0445\\u04af\\u0441\\u0441\\u044d\\u043d \\u0442\\u04af\\u043b\\u0445\\u04af\\u04af\\u0440\\u0438\\u0439\\u0433 \\u0430\\u0433\\u0443\\u0443\\u043b\\u0430\\u0445\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\nBindAuthenticator.badCredentials=\\u041d\\u044d\\u0440 \\u044d\\u0441\\u0432\\u044d\\u043b \\u043d\\u0443\\u0443\\u0446 \\u04af\\u0433 \\u0431\\u0443\\u0440\\u0443\\u0443 \\u0431\\u0430\\u0439\\u043d\\u0430\nBindAuthenticator.emptyPassword=\\u041d\\u0443\\u0443\\u0446 \\u04af\\u0433\\u044d\\u044d \\u043e\\u0440\\u0443\\u0443\\u043b\\u043d\\u0430 \\u0443\\u0443\nCasAuthenticationProvider.incorrectKey=\\u04e8\\u0433\\u04e9\\u0433\\u0434\\u0441\\u04e9\\u043d CasAuthenticationToken \\u0445\\u04af\\u043b\\u044d\\u044d\\u0436 \\u0431\\u0430\\u0439\\u0441\\u0430\\u043d \\u0442\\u04af\\u043b\\u0445\\u04af\\u04af\\u0440\\u0438\\u0439\\u0433 \\u0430\\u0433\\u0443\\u0443\\u043b\\u0430\\u0445\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\nCasAuthenticationProvider.noServiceTicket=\\u0428\\u0430\\u043b\\u0433\\u0430\\u0445 CAS \\u04af\\u0439\\u043b\\u0447\\u0438\\u043b\\u0433\\u044d\\u044d\\u043d\\u0438\\u0439 \\u0442\\u0430\\u0441\\u0430\\u043b\\u0431\\u0430\\u0440\\u044b\\u0433 \\u04af\\u0437\\u04af\\u04af\\u043b\\u0436 \\u0447\\u0430\\u0434\\u0441\\u0430\\u043d\\u0433\\u04af\\u0439\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed={0}-\\u0438\\u0439\\u043d \\u0434\\u0430\\u0432\\u0445\\u0430\\u0440 session-\\u0438\\u0439 \\u0442\\u043e\\u043e \\u0445\\u044d\\u0442\\u044d\\u0440\\u043b\\u044d\\u044d\nDigestAuthenticationFilter.incorrectRealm=\\u0425\\u0430\\u0440\\u0438\\u0443\\u043b\\u0442 \\u0440\\u0435\\u0430\\u043b\\u043c\\u0438\\u0439\\u043d \\u043d\\u044d\\u0440 {0} \\u0441\\u0438\\u0441\\u0442\\u0435\\u043c\\u0438\\u0439\\u043d  \\u0440\\u0435\\u0430\\u043b\\u043c \\u043d\\u044d\\u0440 {1}-\\u0442\\u044d\\u0439 \\u0442\\u0430\\u0430\\u0440\\u0430\\u0445\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\nDigestAuthenticationFilter.incorrectResponse=\\u0422\\u043e\\u0445\\u0438\\u0440\\u043e\\u0445\\u0433\\u04af\\u0439 \\u0445\\u0430\\u0440\\u0438\\u0443\\u043b\\u0442\nDigestAuthenticationFilter.missingAuth='auth' QOP-\\u0434 \\u0448\\u0430\\u0430\\u0440\\u0434\\u043b\\u0430\\u0433\\u0430\\u0442\\u0430\\u0439 digest \\u0443\\u0442\\u0433\\u0430 \\u0434\\u0443\\u0442\\u0430\\u0436 \\u0431\\u0430\\u0439\\u043d\\u0430; \\u0445\\u04af\\u043b\\u044d\\u044d\\u0436 \\u0430\\u0432\\u0441\\u0430\\u043d header {0}\nDigestAuthenticationFilter.missingMandatory=\\u0428\\u0430\\u0430\\u0440\\u0434\\u043b\\u0430\\u0433\\u0430\\u0442\\u0430\\u0439 digest \\u0443\\u0442\\u0433\\u0430 \\u0434\\u0443\\u0442\\u0430\\u0436 \\u0431\\u0430\\u0439\\u043d\\u0430; \\u0445\\u04af\\u043b\\u044d\\u044d\\u0436 \\u0430\\u0432\\u0441\\u0430\\u043d header {0}\nDigestAuthenticationFilter.nonceCompromised=Nonce \\u0442\\u043e\\u043a\\u0435\\u043d \\u0434\\u043e\\u0433\\u043e\\u043b\\u0434\\u0441\\u043e\\u043d {0}\nDigestAuthenticationFilter.nonceEncoding=Nonce Base64-\\u043e\\u043e\\u0440 \\u0445\\u0443\\u0432\\u0438\\u0440\\u0433\\u0430\\u0430\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430; \\u0445\\u04af\\u043b\\u044d\\u044d\\u0436 \\u0430\\u0432\\u0441\\u0430\\u043d nonce {0}\nDigestAuthenticationFilter.nonceExpired=Nonce-\\u0438\\u0439\\u043d \\u0445\\u0443\\u0433\\u0430\\u0446\\u0430\\u0430 \\u0434\\u0443\\u0443\\u0441\\u0441\\u0430\\u043d\nDigestAuthenticationFilter.nonceNotNumeric=Nonce token \\u043d\\u044c \\u0430\\u043d\\u0445\\u043d\\u044b \\u0442\\u043e\\u043e\\u043d \\u0442\\u043e\\u043a\\u0435\\u043d\\u043e\\u043e \\u04e9\\u0433\\u04e9\\u0445 \\u0431\\u0430\\u0439\\u0441\\u0430\\u043d. \\u0413\\u044d\\u0432\\u0447 {0} \\u0431\\u0430\\u0439\\u0432\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce \\u043d\\u044c \\u0445\\u043e\\u0451\\u0440 \\u0442\\u043e\\u043a\\u0435\\u043d \\u04e9\\u0433\\u04e9\\u0445 \\u0431\\u0430\\u0439\\u0441\\u0430\\u043d \\u0433\\u044d\\u0432\\u0447 {0} \\u04e9\\u0433\\u04e9\\u0432\nDigestAuthenticationFilter.usernameNotFound={0} \\u043d\\u044d\\u0440\\u0442\\u044d\\u0439 \\u0445\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447 \\u0431\\u0430\\u0439\\u0445\\u0433\\u04af\\u0439\n#ExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource\nJdbcDaoImpl.noAuthority=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447 {0}-\\u0434 \\u044f\\u043c\\u0430\\u0440 \\u0447 \\u0437\\u04e9\\u0432\\u0448\\u04e9\\u04e9\\u0440\\u04e9\\u043b \\u0431\\u0430\\u0439\\u0445\\u0433\\u04af\\u0439\nJdbcDaoImpl.notFound=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447 {0} \\u0431\\u0430\\u0439\\u0445\\u0433\\u04af\\u0439\nLdapAuthenticationProvider.badCredentials=\\u041d\\u044d\\u0432\\u0442\\u0440\\u044d\\u0445 \\u043d\\u044d\\u0440 \\u044d\\u0441\\u0432\\u044d\\u043b \\u043d\\u0443\\u0443\\u0446 \\u04af\\u0433 \\u0431\\u0443\\u0440\\u0443\\u0443 \\u0431\\u0430\\u0439\\u043d\\u0430\nLdapAuthenticationProvider.badLdapConnection=LDAP \\u0441\\u0435\\u0440\\u0432\\u0435\\u0440 \\u0440\\u04af\\u04af \\u0445\\u043e\\u043b\\u0431\\u043e\\u0433\\u0434\\u043e\\u0436 \\u0447\\u0430\\u0434\\u0441\\u0430\\u043d\\u0433\\u04af\\u0439\nLdapAuthenticationProvider.credentialsExpired=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u043d \\u0445\\u0443\\u0433\\u0430\\u0446\\u0430\\u0430 \\u0434\\u0443\\u0443\\u0441\\u0441\\u0430\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nLdapAuthenticationProvider.disabled=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u0433 \\u0445\\u043e\\u0440\\u0438\\u0433\\u043b\\u043e\\u0441\\u043e\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nLdapAuthenticationProvider.expired=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u043d \\u0430\\u043a\\u043a\\u043e\\u0443\\u043d\\u0442\\u044b\\u043d \\u0445\\u0443\\u0433\\u0430\\u0446\\u0430\\u0430 \\u0434\\u0443\\u0443\\u0441\\u0441\\u0430\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nLdapAuthenticationProvider.locked=\\u0425\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u043d \\u0430\\u043a\\u043a\\u043e\\u0443\\u043d\\u0442\\u044b\\u0433 \\u0442\\u04af\\u0433\\u0436\\u0441\\u044d\\u043d \\u0431\\u0430\\u0439\\u043d\\u0430\nLdapAuthenticationProvider.emptyUsername=\\u0425\\u043e\\u043e\\u0441\\u043e\\u043d \\u043d\\u044d\\u0432\\u0442\\u0440\\u044d\\u0445 \\u043d\\u044d\\u0440 \\u0437\\u04e9\\u0432\\u0448\\u04e9\\u04e9\\u0440\\u04e9\\u0433\\u0434\\u04e9\\u04e9\\u0433\\u04af\\u0439\nLdapAuthenticationProvider.onlySupports=\\u0417\\u04e9\\u0432\\u0445\\u04e9\\u043d UsernamePasswordAuthenticationToken \\u0442\\u043e\\u0445\\u0438\\u0440\\u043e\\u043c\\u0436\\u0442\\u043e\\u0439\nPasswordComparisonAuthenticator.badCredentials=\\u041d\\u044d\\u0432\\u0442\\u0440\\u044d\\u0445 \\u043d\\u044d\\u0440 \\u044d\\u0441\\u0432\\u044d\\u043b \\u043d\\u0443\\u0443\\u0446 \\u04af\\u0433 \\u0431\\u0443\\u0440\\u0443\\u0443 \\u0431\\u0430\\u0439\\u043d\\u0430\nPersistentTokenBasedRememberMeServices.cookieStolen=\\u0425\\u04af\\u0447\\u0438\\u043d\\u0433\\u04af\\u0439 remember-me \\u0442\\u043e\\u043a\\u0435\\u043d (Series/token) \\u0442\\u043e\\u0445\\u0438\\u0440\\u0441\\u043e\\u043d\\u0433\\u04af\\u0439. Cookie \\u0445\\u0443\\u043b\\u0433\\u0430\\u0439\\u043b\\u0430\\u0445 \\u0445\\u0430\\u043b\\u0434\\u043b\\u0430\\u0433\\u0430 \\u0433\\u044d\\u0436 \\u0442\\u0430\\u0430\\u043c\\u0430\\u0433\\u043b\\u0430\\u0436 \\u0431\\u0430\\u0439\\u043d\\u0430\nProviderManager.providerNotFound={0}-\\u0434 \\u0442\\u043e\\u0445\\u0438\\u0440\\u043e\\u0445 AuthenticationProvider \\u043e\\u043b\\u0434\\u0441\\u043e\\u043d\\u0433\\u04af\\u0439\nRememberMeAuthenticationProvider.incorrectKey=\\u04e8\\u0433\\u04e9\\u0433\\u0434\\u0441\\u04e9\\u043d RememberMeAuthenticationToken \\u0445\\u04af\\u0441\\u0441\\u044d\\u043d \\u0442\\u04af\\u043b\\u0445\\u04af\\u04af\\u0440\\u0438\\u0439\\u0433 \\u0430\\u0433\\u0443\\u0443\\u043b\\u0430\\u0445\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\nRunAsImplAuthenticationProvider.incorrectKey=\\u04e8\\u0433\\u04e9\\u0433\\u0434\\u0441\\u04e9\\u043d RunAsUserToken \\u0445\\u04af\\u0441\\u0441\\u044d\\u043d \\u0442\\u04af\\u043b\\u0445\\u04af\\u04af\\u0440\\u0438\\u0439\\u0433 \\u0430\\u0433\\u0443\\u0443\\u043b\\u0430\\u0445\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\nSubjectDnX509PrincipalExtractor.noMatching=subjectDN: {0}-\\u044d\\u044d\\u0441 \\u0442\\u043e\\u0445\\u0438\\u0440\\u043e\\u0445 \\u0437\\u0430\\u0433\\u0432\\u0430\\u0440 \\u043e\\u043b\\u0434\\u0441\\u043e\\u043d\\u0433\\u04af\\u0439\nSwitchUserFilter.noCurrentUser=\\u042d\\u043d\\u044d \\u0445\\u04af\\u0441\\u044d\\u043b\\u0442\\u0442\\u044d\\u0439 \\u0445\\u043e\\u043b\\u0431\\u043e\\u043e\\u0442\\u043e\\u0439 \\u0445\\u044d\\u0440\\u044d\\u0433\\u043b\\u044d\\u0433\\u0447\\u0438\\u0439\\u0433 \\u043e\\u043b\\u043e\\u0445\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\nSwitchUserFilter.noOriginalAuthentication=\\u0430\\u043d\\u0445\\u0434\\u0430\\u0433\\u0447 Authentication \\u043c\\u044d\\u0434\\u044d\\u044d\\u043b\\u044d\\u043b\\u0438\\u0439\\u0433 \\u043e\\u043b\\u043e\\u0445\\u0433\\u04af\\u0439 \\u0431\\u0430\\u0439\\u043d\\u0430\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_nl.properties",
    "content": ""
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_pl.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=Dost\\u0119p zabroniony\nAbstractLdapAuthenticationProvider.emptyPassword=Niepoprawne dane uwierzytelniaj\\u0105ce\nAbstractSecurityInterceptor.authenticationNotFound=Obiekt Authentication nie zosta\\u0142 odnaleziony w SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=Niepoprawne dane uwierzytelniaj\\u0105ce\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Wa\\u017Cno\\u015B\\u0107 danych uwierzytelniaj\\u0105cych wygas\\u0142a\nAbstractUserDetailsAuthenticationProvider.disabled=Konto u\\u017Cytkownika jest wy\\u0142\\u0105czone\nAbstractUserDetailsAuthenticationProvider.expired=Wa\\u017Cno\\u015B\\u0107 konta u\\u017Cytkownika wygas\\u0142a\nAbstractUserDetailsAuthenticationProvider.locked=Konto u\\u017Cytkownika jest zablokowane\nAbstractUserDetailsAuthenticationProvider.onlySupports=Tylko UsernamePasswordAuthenticationToken jest obs\\u0142ugiwany\nAccountStatusUserDetailsChecker.credentialsExpired=Wa\\u017Cno\\u015B\\u0107 danych uwierzytelniaj\\u0105cych wygas\\u0142a\nAccountStatusUserDetailsChecker.disabled=Konto u\\u017Cytkownika jest wy\\u0142\\u0105czone\nAccountStatusUserDetailsChecker.expired=Wa\\u017Cno\\u015B\\u0107 konta u\\u017Cytkownika wygas\\u0142a\nAccountStatusUserDetailsChecker.locked=Konto u\\u017Cytkownika jest zablokowane\nAclEntryAfterInvocationProvider.noPermission=U\\u017Cytkownik {0} nie posiada \\u017BADNYCH uprawnie\\u0144 do obiektu {1}\nAnonymousAuthenticationProvider.incorrectKey=Podany AnonymousAuthenticationToken nie zawiera oczekiwanego klucza\nBindAuthenticator.badCredentials=Niepoprawne dane uwierzytelniaj\\u0105ce\nBindAuthenticator.emptyPassword=Niepoprawne dane uwierzytelniaj\\u0105ce\nCasAuthenticationProvider.incorrectKey=Podany CasAuthenticationToken nie zawiera oczekiwanego klucza\nCasAuthenticationProvider.noServiceTicket=Dostarczenie biletu serwisu CAS do walidacji nie powiod\\u0142o si\\u0119\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Maksymalna liczba sesji ({0}) dla tego u\\u017Cytkownika zosta\\u0142a przekroczona\nDigestAuthenticationFilter.incorrectRealm=Nazwa domeny {0} w odpowiedzi nie jest zgodna z nazw\\u0105 domeny {1} w systemie\nDigestAuthenticationFilter.incorrectResponse=Niepoprawna odpowied\\u017A\nDigestAuthenticationFilter.missingAuth=Brakuje wymaganej warto\\u015Bci skr\\u00F3tu dla 'auth' QOP; otrzymany nag\\u0142\\u00F3wek {0}\nDigestAuthenticationFilter.missingMandatory=Brakuje wymaganej warto\\u015Bci skr\\u00F3tu; otrzymany nag\\u0142\\u00F3wek {0}\nDigestAuthenticationFilter.nonceCompromised=Niepoprawny kod jednorazowy (nonce) {0}\nDigestAuthenticationFilter.nonceEncoding=Kod jednorazowy (nonce) nie jest zakodowany w Base64; otrzymany kod {0}\nDigestAuthenticationFilter.nonceExpired=Wa\\u017Cno\\u015B\\u0107 kodu jednorazowego (nonce) wygas\\u0142a\nDigestAuthenticationFilter.nonceNotNumeric=Pierwsza warto\\u015B\\u0107 kodu jednorazowego (nonce) nie jest warto\\u015Bci\\u0105 numeryczn\\u0105\\: {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Kod jednorazowy (nonce) powinien zawiera\\u0107 dwie warto\\u015Bci {0}\nDigestAuthenticationFilter.usernameNotFound=Nazwa u\\u017Cytkownika {0} nie zosta\\u0142a odnaleziona\nExceptionTranslationFilter.insufficientAuthentication=Wymagane jest pe\\u0142ne uwierzytelnienie w celu uzyskania dost\\u0119pu do zasobu\nJdbcDaoImpl.noAuthority=U\\u017Cytkownik {0} nie posiada \\u017Cadnych uprawnie\\u0144 (GrantedAuthority)\nJdbcDaoImpl.notFound=Nazwa u\\u017Cytkownika {0} nie zosta\\u0142a odnaleziona\nLdapAuthenticationProvider.badCredentials=Niepoprawne dane uwierzytelniaj\\u0105ce\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=Wa\\u017Cno\\u015B\\u0107 danych uwierzytelniaj\\u0105cych wygas\\u0142a\nLdapAuthenticationProvider.disabled=Konto u\\u017Cytkownika jest wy\\u0142\\u0105czone\nLdapAuthenticationProvider.expired=Wa\\u017Cno\\u015B\\u0107 konta u\\u017Cytkownika wygas\\u0142a\nLdapAuthenticationProvider.locked=Konto u\\u017Cytkownika jest zablokowane\nLdapAuthenticationProvider.emptyUsername=Pusta nazwa u\\u017Cytkownika jest niedozwolona\nLdapAuthenticationProvider.onlySupports=Tylko UsernamePasswordAuthenticationToken jest obs\\u0142ugiwany\nPasswordComparisonAuthenticator.badCredentials=Niepoprawne dane uwierzytelniaj\\u0105ce\n#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=AuthenticationProvider dla {0} nie zosta\\u0142 znaleziony\nRememberMeAuthenticationProvider.incorrectKey=Podany RememberMeAuthenticationToken nie zawiera oczekiwanego klucza\nRunAsImplAuthenticationProvider.incorrectKey=Podany RunAsUserToken nie zawiera oczekiwanego klucza\nSubjectDnX509PrincipalExtractor.noMatching=Nie odnaleziono pasuj\\u0105cego wzorca w subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=\\u017Baden aktualny u\\u017Cytkownik nie jest powi\\u0105zany z tym zapytaniem\nSwitchUserFilter.noOriginalAuthentication=Nie mo\\u017Cna by\\u0142o odnale\\u017A\\u0107 oryginalnego obiektu Authentication\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_pt_BR.properties",
    "content": "# Spring security\n# Messages in Brazilian Portuguese\n# Translation by Leonardo Pinto (leoviveiros@gmail.com)\nAbstractAccessDecisionManager.accessDenied=Acesso negado\nAbstractLdapAuthenticationProvider.emptyPassword=Usu\\u00E1rio inexistente ou senha inv\\u00E1lida\nAbstractSecurityInterceptor.authenticationNotFound=Um objeto de autentica\\u00E7\\u00E3o n\\u00E3o foi encontrado no SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=Usu\\u00E1rio inexistente ou senha inv\\u00E1lida\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=Credenciais expiradas\nAbstractUserDetailsAuthenticationProvider.disabled=Usu\\u00E1rio desabilitado\nAbstractUserDetailsAuthenticationProvider.expired=Conta expirada\nAbstractUserDetailsAuthenticationProvider.locked=Conta bloqueada\nAbstractUserDetailsAuthenticationProvider.onlySupports=Somente UsernamePasswordAuthenticationToken \\u00E9 suportado\nAccountStatusUserDetailsChecker.credentialsExpired=Credenciais expiradas\nAccountStatusUserDetailsChecker.disabled=Usu\\u00E1rio desabilitado\nAccountStatusUserDetailsChecker.expired=Conta expirada\nAccountStatusUserDetailsChecker.locked=Conta bloqueada\nAclEntryAfterInvocationProvider.noPermission=Autentica\\u00E7\\u00E3o {0} n\\u00E3o possui permiss\\u00F5es no objeto de dom\\u00EDnio {1}\nAnonymousAuthenticationProvider.incorrectKey=O AnonymousAuthenticationToken apresentado n\\u00E3o cont\\u00E9m a chave esperada\nBindAuthenticator.badCredentials=Usu\\u00E1rio inexistente ou senha inv\\u00E1lida\nBindAuthenticator.emptyPassword=Usu\\u00E1rio inexistente ou senha inv\\u00E1lida\nCasAuthenticationProvider.incorrectKey=O CasAuthenticationToken apresentado n\\u00E3o cont\\u00E9m a chave esperada\nCasAuthenticationProvider.noServiceTicket=N\\u00E3o foi poss\\u00EDvel prover um ticket de servi\\u00E7o CAS para validar\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Excedidas as sess\\u00F5es m\\u00E1ximas de {0} para este usu\\u00E1rio\nDigestAuthenticationFilter.incorrectRealm=Resposta realm de nome {0} n\\u00E3o coincide com realm de sistema de nome {1}\nDigestAuthenticationFilter.incorrectResponse=Resposta incorreta\nDigestAuthenticationFilter.missingAuth=Valor digest obrigat\\u00F3rio perdido para 'auth' QOP; header recibido {0}\nDigestAuthenticationFilter.missingMandatory=Valor digest obrigat\\u00F3rio perdido; header recibido {0}\nDigestAuthenticationFilter.nonceCompromised=Nonce token comprometido {0}\nDigestAuthenticationFilter.nonceEncoding=Nonce n\\u00E3o est\\u00E1 codificado em Base64; nonce recibido {0}\nDigestAuthenticationFilter.nonceExpired=Nonce expirou/tempo esgotado\nDigestAuthenticationFilter.nonceNotNumeric=Nonce token deveria ter um primero token num\\u00E9rico, mas tem {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce token deveria ter dois tokens mas tem {0}\nDigestAuthenticationFilter.usernameNotFound=Usu\\u00E1rio {0} n\\u00E3o encontrado\nExceptionTranslationFilter.insufficientAuthentication=Autentica\\u00e7\\u00e3o completa \\u00e9 necess\\u00e1ria para acessar este recurso\nJdbcDaoImpl.noAuthority=Usu\\u00E1rio {0} n\\u00E3o tem permiss\\u00E3o\nJdbcDaoImpl.notFound=Usu\\u00E1rio {0} n\\u00E3o encontrado\nLdapAuthenticationProvider.badCredentials=Usu\\u00E1rio inexistente ou senha inv\\u00E1lida\nLdapAuthenticationProvider.badLdapConnection=Conex\\u00E3o com servidor LDAP falhou\nLdapAuthenticationProvider.credentialsExpired=Credenciais expiradas\nLdapAuthenticationProvider.disabled=Usu\\u00E1rio desabilitado\nLdapAuthenticationProvider.expired=Conta expirada\nLdapAuthenticationProvider.locked=Conta bloqueada\nLdapAuthenticationProvider.emptyUsername=Nome vazio n\\u00E3o permitido\nLdapAuthenticationProvider.onlySupports=Somente UsernamePasswordAuthenticationToken \\u00E9 suportado\nPasswordComparisonAuthenticator.badCredentials=Usu\\u00E1rio inexistente ou senha inv\\u00E1lida\nPersistentTokenBasedRememberMeServices.cookieStolen=Diverg\\u00eancia inv\\u00e1lida em remember-me token (Series/token). Implica um ataque pr\\u00e9vio de roubo de cookies.\nProviderManager.providerNotFound=Nenhum AuthenticationProvider encontrado para {0}\nRememberMeAuthenticationProvider.incorrectKey=O RememberMeAuthenticationToken apresentado n\\u00E3o cont\\u00E9m a chave esperada\nRunAsImplAuthenticationProvider.incorrectKey=O RunAsUserToken apresentado n\\u00E3o cont\\u00E9m a chave esperada\nSubjectDnX509PrincipalExtractor.noMatching=Nenhum padr\\u00E3o compat\\u00EDvel foi encontrado em subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=Nenhum usu\\u00E1rio associado a esta requisi\\u00E7\\u00E3o\nSwitchUserFilter.noOriginalAuthentication=N\\u00E3o foi poss\\u00EDvel encontrar o objeto Authentication original\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_pt_PT.properties",
    "content": "# Spring Security Portuguese Resource Bundle\n# Author: Jos Santos\nAbstractAccessDecisionManager.accessDenied=Acesso negado\nAbstractLdapAuthenticationProvider.emptyPassword=Credenciais inv\\u00E1lidas\nAbstractSecurityInterceptor.authenticationNotFound=Objecto Authentication n\\u00E3o encontrado em SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=Credenciais inv\\u00E1lidas\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=As credenciais do utilizador expiraram\nAbstractUserDetailsAuthenticationProvider.disabled=O utilizador est\\u00E1 desactivado\nAbstractUserDetailsAuthenticationProvider.expired=A conta de utilizador expirou\nAbstractUserDetailsAuthenticationProvider.locked=A conta de utilizador est\\u00E1 bloqueada\nAbstractUserDetailsAuthenticationProvider.onlySupports=Apenas UsernamePasswordAuthenticationToken \\u00E9 suportado\nAccountStatusUserDetailsChecker.credentialsExpired=As credenciais do utilizador expiraram\nAccountStatusUserDetailsChecker.disabled=O utilizador est\\u00E1 desactivado\nAccountStatusUserDetailsChecker.expired=A conta de utilizador expirou\nAccountStatusUserDetailsChecker.locked=A conta de utilizador est\\u00E1 bloqueada\nAclEntryAfterInvocationProvider.noPermission=A autentica\\u00E7\\u00E3o {0} n\\u00E3o tem QUALQUER permiss\\u00E3o para o objecto de dom\\u00EDnio {1}\nAnonymousAuthenticationProvider.incorrectKey=O AnonymousAuthenticationToken especificado n\\u00E3o cont\\u00E9m a chave esperada\nBindAuthenticator.badCredentials=Credenciais inv\\u00E1lidas\nBindAuthenticator.emptyPassword=Credenciais inv\\u00E1lidas\nCasAuthenticationProvider.incorrectKey=O CasAuthenticationToken especificado n\\u00E3o cont\\u00E9m a chave esperada\nCasAuthenticationProvider.noServiceTicket=Falha na obten\\u00E7\\u00E3o de um ticket de servi\\u00E7o CAS de valida\\u00E7\\u00E3o\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=N\\u00FAmero m\\u00E1ximo de sess\\u00F5es ({0}) excedido para este utilizador\nDigestAuthenticationFilter.incorrectRealm=Nome de dom\\u00EDnio na resposta {0} n\\u00E3o corresponde ao nome de dom\\u00EDnio do sistema {1}\nDigestAuthenticationFilter.incorrectResponse=Resposta incorrecta\nDigestAuthenticationFilter.missingAuth=Valor digest obrigat\\u00F3rio em falta para 'auth' QOP; cabe\\u00E7alho recebido {0}\nDigestAuthenticationFilter.missingMandatory=Valor digest obrigat\\u00F3rio em falta; cabe\\u00E7alho recebido {0}\nDigestAuthenticationFilter.nonceCompromised=Token nonce comprometido {0}\nDigestAuthenticationFilter.nonceEncoding=O nonce n\\u00E3o est\\u00E1 codificado em Base64; nonce recebido {0}\nDigestAuthenticationFilter.nonceExpired=O nonce expirou\nDigestAuthenticationFilter.nonceNotNumeric=O primeiro elemento do token nonce ({0}) deve ser num\\u00E9rico\nDigestAuthenticationFilter.nonceNotTwoTokens=O nonce deve ser constitu\\u00EDdo por 2 tokens, n\\u00E3o por {0}\nDigestAuthenticationFilter.usernameNotFound=Nome de utilizador {0} n\\u00E3o encontrado\n#ExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource\nJdbcDaoImpl.noAuthority=Utilizador {0} n\\u00E3o tem GrantedAuthority\nJdbcDaoImpl.notFound=Utilizador {0} n\\u00E3o encontrado\nLdapAuthenticationProvider.badCredentials=Credenciais inv\\u00E1lidas\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=As credenciais do utilizador expiraram\nLdapAuthenticationProvider.disabled=O utilizador est\\u00E1 desactivado\nLdapAuthenticationProvider.expired=A conta de utilizador expirou\nLdapAuthenticationProvider.locked=A conta de utilizador est\\u00E1 bloqueada\nLdapAuthenticationProvider.emptyUsername=Nome de utilizador vazio n\\u00E3o permitido\nLdapAuthenticationProvider.onlySupports=Apenas UsernamePasswordAuthenticationToken \\u00E9 suportado\nPasswordComparisonAuthenticator.badCredentials=Credenciais inv\\u00E1lidas\n#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=Nenhum AuthenticationProvider encontrado para {0}\nRememberMeAuthenticationProvider.incorrectKey=O RememberMeAuthenticationToken especificado n\\u00E3o cont\\u00E9m a chave esperada\nRunAsImplAuthenticationProvider.incorrectKey=O RunAsUserToken especificado n\\u00E3o cont\\u00E9m a chave esperada\nSubjectDnX509PrincipalExtractor.noMatching=Formato do subjectDN {0} incorrecto\nSwitchUserFilter.noCurrentUser=Nenhum utilizador associado a este pedido\nSwitchUserFilter.noOriginalAuthentication=Objecto Authentication original n\\u00E3o encontrado\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_ru.properties",
    "content": "# Spring security\n# Messages in Russian\n# Translation by Oleg Zhuravlev (olezhuravlev@gmail.com)\nAbstractAccessDecisionManager.accessDenied=\\u0414\\u043E\\u0441\\u0442\\u0443\\u043F \\u0437\\u0430\\u043F\\u0440\\u0435\\u0449\\u0435\\u043D\nAbstractLdapAuthenticationProvider.emptyPassword=\\u041F\\u0443\\u0441\\u0442\\u043E\\u0439 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C\nAbstractSecurityInterceptor.authenticationNotFound=\\u041E\\u0431\\u044A\\u0435\\u043A\\u0442 Authentication \\u0432 SecurityContext \\u043D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\nAbstractUserDetailsAuthenticationProvider.badCredentials=\\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u044B\\u0435 \\u0443\\u0447\\u0435\\u0442\\u043D\\u044B\\u0435 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=\\u0421\\u0440\\u043E\\u043A \\u0434\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u044F \\u0443\\u0447\\u0435\\u0442\\u043D\\u044B\\u0445 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0438\\u0441\\u0442\\u0435\\u043A\nAbstractUserDetailsAuthenticationProvider.disabled=\\u041F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044C \\u043E\\u0442\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\nAbstractUserDetailsAuthenticationProvider.expired=\\u0421\\u0440\\u043E\\u043A \\u0434\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u044F \\u0443\\u0447\\u0435\\u0442\\u043D\\u043E\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441\\u0438 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0438\\u0441\\u0442\\u0435\\u043A\nAbstractUserDetailsAuthenticationProvider.locked=\\u0423\\u0447\\u0435\\u0442\\u043D\\u0430\\u044F \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u0430\nAbstractUserDetailsAuthenticationProvider.onlySupports=\\u041F\\u043E\\u0434\\u0434\\u0435\\u0440\\u0436\\u0438\\u0432\\u0430\\u0435\\u0442\\u0441\\u044F \\u0442\\u043E\\u043B\\u044C\\u043A\\u043E UsernamePasswordAuthenticationToken\nAccountStatusUserDetailsChecker.credentialsExpired=\\u0421\\u0440\\u043E\\u043A \\u0434\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u044F \\u0443\\u0447\\u0435\\u0442\\u043D\\u044B\\u0445 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0438\\u0441\\u0442\\u0435\\u043A\nAccountStatusUserDetailsChecker.disabled=\\u041F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044C \\u043E\\u0442\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\nAccountStatusUserDetailsChecker.expired=\\u0421\\u0440\\u043E\\u043A \\u0434\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u044F \\u0443\\u0447\\u0435\\u0442\\u043D\\u043E\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441\\u0438 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0438\\u0441\\u0442\\u0435\\u043A\nAccountStatusUserDetailsChecker.locked=\\u0423\\u0447\\u0435\\u0442\\u043D\\u0430\\u044F \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u0430\nAclEntryAfterInvocationProvider.noPermission=Authentication {0} \\u041D\\u0415 \\u0438\\u043C\\u0435\\u0435\\u0442 \\u043F\\u0440\\u0430\\u0432 \\u0434\\u043E\\u0441\\u0442\\u0443\\u043F\\u0430 \\u043A \\u0434\\u043E\\u043C\\u0435\\u043D\\u043D\\u043E\\u043C\\u0443 \\u043E\\u0431\\u044A\\u0435\\u043A\\u0442\\u0443 {1}\nAnonymousAuthenticationProvider.incorrectKey=\\u041F\\u0440\\u0435\\u0434\\u044A\\u044F\\u0432\\u043B\\u0435\\u043D\\u043D\\u044B\\u0439 AnonymousAuthenticationToken \\u043D\\u0435 \\u0441\\u043E\\u0434\\u0435\\u0440\\u0436\\u0438\\u0442 \\u043E\\u0436\\u0438\\u0434\\u0430\\u0435\\u043C\\u043E\\u0433\\u043E \\u043A\\u043B\\u044E\\u0447\\u0430\nBindAuthenticator.badCredentials=\\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u044B\\u0435 \\u0443\\u0447\\u0435\\u0442\\u043D\\u044B\\u0435 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F\nBindAuthenticator.emptyPassword=\\u041F\\u0443\\u0441\\u0442\\u043E\\u0439 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C\nCasAuthenticationProvider.incorrectKey=\\u041F\\u0440\\u0435\\u0434\\u044A\\u044F\\u0432\\u043B\\u0435\\u043D\\u043D\\u044B\\u0439 CasAuthenticationToken \\u043D\\u0435 \\u0441\\u043E\\u0434\\u0435\\u0440\\u0436\\u0438\\u0442 \\u043E\\u0436\\u0438\\u0434\\u0430\\u0435\\u043C\\u043E\\u0433\\u043E \\u043A\\u043B\\u044E\\u0447\\u0430\nCasAuthenticationProvider.noServiceTicket=\\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u043F\\u0440\\u0435\\u0434\\u043E\\u0441\\u0442\\u0430\\u0432\\u0438\\u0442\\u044C \\u0434\\u043B\\u044F \\u0432\\u0430\\u043B\\u0438\\u0434\\u0430\\u0446\\u0438\\u0438 \\u0442\\u0438\\u043A\\u0435\\u0442 \\u0441\\u043B\\u0443\\u0436\\u0431\\u044B CAS\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=\\u041C\\u0430\\u043A\\u0441\\u0438\\u043C\\u0430\\u043B\\u044C\\u043D\\u043E\\u0435 \\u043A\\u043E\\u043B\\u0438\\u0447\\u0435\\u0441\\u0442\\u0432\\u043E \\u0441\\u0435\\u0441\\u0441\\u0438\\u0439 {0} \\u0434\\u043B\\u044F \\u0434\\u0430\\u043D\\u043D\\u043E\\u0433\\u043E \\u043F\\u0440\\u0438\\u043D\\u0446\\u0438\\u043F\\u0430\\u043B\\u0430 \\u043F\\u0440\\u0435\\u0432\\u044B\\u0448\\u0435\\u043D\\u043E\nDigestAuthenticationFilter.incorrectRealm=\\u0418\\u043C\\u044F \\u043E\\u0431\\u043B\\u0430\\u0441\\u0442\\u0438 \\u0432 \\u043E\\u0442\\u0432\\u0435\\u0442\\u0435 {0} \\u043D\\u0435 \\u0441\\u043E\\u0432\\u043F\\u0430\\u0434\\u0430\\u0435\\u0442 \\u0441 \\u0438\\u043C\\u0435\\u043D\\u0435\\u043C \\u043E\\u0431\\u043B\\u0430\\u0441\\u0442\\u0438 \\u0432 \\u0441\\u0438\\u0441\\u0442\\u0435\\u043C\\u0435 {1}\nDigestAuthenticationFilter.incorrectResponse=\\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u044B\\u0439 \\u043E\\u0442\\u0432\\u0435\\u0442\nDigestAuthenticationFilter.missingAuth=\\u041E\\u0442\\u0441\\u0443\\u0442\\u0441\\u0442\\u0432\\u0443\\u0435\\u0442 \\u043E\\u0431\\u044F\\u0437\\u0430\\u0442\\u0435\\u043B\\u044C\\u043D\\u043E\\u0435 \\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u0438\\u0435 \\u0434\\u0430\\u0439\\u0434\\u0436\\u0435\\u0441\\u0442\\u0430 \\u0434\\u043B\\u044F 'auth' QOP; \\u043F\\u043E\\u043B\\u0443\\u0447\\u0435\\u043D\\u043D\\u044B\\u0439 \\u0437\\u0430\\u0433\\u043E\\u043B\\u043E\\u0432\\u043E\\u043A {0}\nDigestAuthenticationFilter.missingMandatory=\\u041E\\u0442\\u0441\\u0443\\u0442\\u0441\\u0442\\u0432\\u0443\\u0435\\u0442 \\u043E\\u0431\\u044F\\u0437\\u0430\\u0442\\u0435\\u043B\\u044C\\u043D\\u043E\\u0435 \\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u0438\\u0435 \\u0434\\u0430\\u0439\\u0434\\u0436\\u0435\\u0441\\u0442\\u0430; \\u043F\\u043E\\u043B\\u0443\\u0447\\u0435\\u043D\\u043D\\u044B\\u0439 \\u0437\\u0430\\u0433\\u043E\\u043B\\u043E\\u0432\\u043E\\u043A {0}\nDigestAuthenticationFilter.nonceCompromised=\\u0422\\u043E\\u043A\\u0435\\u043D nonce-\\u043A\\u043E\\u0434\\u0430 \\u0441\\u043A\\u043E\\u043C\\u043F\\u0440\\u043E\\u043C\\u0435\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D {0}\nDigestAuthenticationFilter.nonceEncoding=Nonce-\\u043A\\u043E\\u0434 \\u043D\\u0435 \\u0437\\u0430\\u043A\\u043E\\u0434\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D \\u0441 \\u043F\\u043E\\u043C\\u043E\\u0449\\u044C\\u044E Base64; \\u043F\\u043E\\u043B\\u0443\\u0447\\u0435\\u043D\\u043D\\u044B\\u0439 nonce-\\u043A\\u043E\\u0434 {0}\nDigestAuthenticationFilter.nonceExpired=\\u0421\\u0440\\u043E\\u043A \\u0434\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u044F nonce-\\u043A\\u043E\\u0434\\u0430 \\u0438\\u0441\\u0442\\u0435\\u043A/\\u043F\\u0440\\u0435\\u0432\\u044B\\u0448\\u0435\\u043D\nDigestAuthenticationFilter.nonceNotNumeric=\\u0422\\u043E\\u043A\\u0435\\u043D nonce-\\u043A\\u043E\\u0434\\u0430 \\u0434\\u043E\\u043B\\u0436\\u0435\\u043D \\u043D\\u0430\\u0447\\u0438\\u043D\\u0430\\u0442\\u044C\\u0441\\u044F \\u0441 \\u0447\\u0438\\u0441\\u043B\\u043E\\u0432\\u043E\\u0433\\u043E \\u0442\\u043E\\u043A\\u0435\\u043D\\u0430, \\u043D\\u043E \\u043F\\u0440\\u0438\\u0441\\u0443\\u0442\\u0441\\u0442\\u0432\\u0443\\u0435\\u0442\\: {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce-\\u043A\\u043E\\u0434 \\u0434\\u043E\\u043B\\u0436\\u0435\\u043D \\u0441\\u043E\\u0441\\u0442\\u043E\\u044F\\u0442\\u044C \\u0438\\u0437 \\u0434\\u0432\\u0443\\u0445 \\u0442\\u043E\\u043A\\u0435\\u043D\\u043E\\u0432, \\u043D\\u043E \\u043F\\u0440\\u0438\\u0441\\u0443\\u0442\\u0441\\u0442\\u0432\\u0443\\u0435\\u0442 {0}\nDigestAuthenticationFilter.usernameNotFound=\\u0418\\u043C\\u044F \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F {0} \\u043D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u043E\n#ExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource\nJdbcDaoImpl.noAuthority=\\u041F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044C {0} \\u043D\\u0435 \\u043E\\u0431\\u043B\\u0430\\u0434\\u0430\\u0435\\u0442 GrantedAuthority\nJdbcDaoImpl.notFound=\\u041F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044C {0} \\u043D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\nLdapAuthenticationProvider.badCredentials=\\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u044B\\u0435 \\u0443\\u0447\\u0435\\u0442\\u043D\\u044B\\u0435 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=\\u0421\\u0440\\u043E\\u043A \\u0434\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u044F \\u0443\\u0447\\u0435\\u0442\\u043D\\u044B\\u0445 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0438\\u0441\\u0442\\u0435\\u043A\nLdapAuthenticationProvider.disabled=\\u041F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044C \\u043E\\u0442\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\nLdapAuthenticationProvider.expired=\\u0421\\u0440\\u043E\\u043A \\u0434\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u044F \\u0443\\u0447\\u0435\\u0442\\u043D\\u043E\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441\\u0438 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0438\\u0441\\u0442\\u0435\\u043A\nLdapAuthenticationProvider.locked=\\u0423\\u0447\\u0435\\u0442\\u043D\\u0430\\u044F \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u0430\nLdapAuthenticationProvider.emptyUsername=\\u041F\\u0443\\u0441\\u0442\\u043E\\u0435 \\u0438\\u043C\\u044F \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F \\u043D\\u0435\\u0434\\u043E\\u043F\\u0443\\u0441\\u0442\\u0438\\u043C\\u043E\nLdapAuthenticationProvider.onlySupports=\\u041F\\u043E\\u0434\\u0434\\u0435\\u0440\\u0436\\u0438\\u0432\\u0430\\u0435\\u0442\\u0441\\u044F \\u0442\\u043E\\u043B\\u044C\\u043A\\u043E UsernamePasswordAuthenticationToken\nPasswordComparisonAuthenticator.badCredentials=\\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u044B\\u0435 \\u0443\\u0447\\u0435\\u0442\\u043D\\u044B\\u0435 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F\n#PersistentTokenBasedRememberMeServices.cookieStolen=\\u041D\\u0435\\u0434\\u043E\\u043F\\u0443\\u0441\\u0442\\u0438\\u043C\\u043E\\u0435 \\u043D\\u0435\\u0441\\u043E\\u043E\\u0442\\u0432\\u0435\\u0442\\u0441\\u0442\\u0432\\u0438\\u0435 \\u0442\\u043E\\u043A\\u0435\\u043D\\u0430 remember-me (Series/token). \\u0412\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u0430\\u044F \\u0430\\u0442\\u0430\\u043A\\u0430 \\u043F\\u0443\\u0442\\u0435\\u043C \\u043A\\u0440\\u0430\\u0436\\u0438 \\u043F\\u0440\\u0435\\u0434\\u044B\\u0434\\u0443\\u0449\\u0435\\u0433\\u043E \\u043A\\u0443\\u043A\\u0438.\nProviderManager.providerNotFound=\\u041D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D AuthenticationProvider \\u0434\\u043B\\u044F {0}\nRememberMeAuthenticationProvider.incorrectKey=\\u041F\\u0440\\u0435\\u0434\\u044A\\u044F\\u0432\\u043B\\u0435\\u043D\\u043D\\u044B\\u0439 RememberMeAuthenticationToken \\u043D\\u0435 \\u0441\\u043E\\u0434\\u0435\\u0440\\u0436\\u0438\\u0442 \\u043E\\u0436\\u0438\\u0434\\u0430\\u0435\\u043C\\u043E\\u0433\\u043E \\u043A\\u043B\\u044E\\u0447\\u0430\nRunAsImplAuthenticationProvider.incorrectKey=\\u041F\\u0440\\u0435\\u0434\\u044A\\u044F\\u0432\\u043B\\u0435\\u043D\\u043D\\u044B\\u0439 RunAsUserToken \\u043D\\u0435 \\u0441\\u043E\\u0434\\u0435\\u0440\\u0436\\u0438\\u0442 \\u043E\\u0436\\u0438\\u0434\\u0430\\u0435\\u043C\\u043E\\u0433\\u043E \\u043A\\u043B\\u044E\\u0447\\u0430\nSubjectDnX509PrincipalExtractor.noMatching=\\u041D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D \\u0441\\u043E\\u043E\\u0442\\u0432\\u0435\\u0442\\u0441\\u0442\\u0432\\u0443\\u044E\\u0449\\u0438\\u0439 \\u043F\\u0430\\u0442\\u0442\\u0435\\u0440\\u043D \\u0432 subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=\\u041E\\u0442\\u0441\\u0443\\u0442\\u0441\\u0442\\u0432\\u0443\\u0435\\u0442 \\u0442\\u0435\\u043A\\u0443\\u0449\\u0438\\u0439 \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044C, \\u0430\\u0441\\u0441\\u043E\\u0446\\u0438\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u043D\\u044B\\u0439 \\u0441 \\u0434\\u0430\\u043D\\u043D\\u044B\\u043C \\u0437\\u0430\\u043F\\u0440\\u043E\\u0441\\u043E\\u043C\nSwitchUserFilter.noOriginalAuthentication=\\u041D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D \\u0438\\u0441\\u0445\\u043E\\u0434\\u043D\\u044B\\u0439 \\u043E\\u0431\\u044A\\u0435\\u043A\\u0442 Authentication\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_uk_UA.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=\\u0414\\u043E\\u0441\\u0442\\u0443\\u043F \\u0437\\u0430\\u0431\\u043E\\u0440\\u043E\\u043D\\u0435\\u043D\\u0438\\u0439\nAbstractLdapAuthenticationProvider.emptyPassword=\\u0414\\u0430\\u043D\\u0456 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u043D\\u0435\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u043D\\u0456\nAbstractSecurityInterceptor.authenticationNotFound=\\u041E\\u0431'\\u0454\\u043A\\u0442 Authentication \\u043D\\u0435 \\u0437\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u0438\\u0439 \\u0432 SecurityContext\nAbstractUserDetailsAuthenticationProvider.badCredentials=\\u0414\\u0430\\u043D\\u0456 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u043D\\u0435\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u043D\\u0456\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=\\u041F\\u043E\\u0432\\u043D\\u043E\\u0432\\u0430\\u0436\\u0435\\u043D\\u043D\\u044F \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0432\\u0438\\u0447\\u0435\\u0440\\u043F\\u0430\\u043B\\u0438 \\u0442\\u0435\\u0440\\u043C\\u0456\\u043D \\u0434\\u0456\\u0457\nAbstractUserDetailsAuthenticationProvider.disabled=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0437\\u0430\\u0431\\u043E\\u0440\\u043E\\u043D\\u0435\\u043D\\u0438\\u0439\nAbstractUserDetailsAuthenticationProvider.expired=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0432\\u0438\\u0447\\u0435\\u0440\\u043F\\u0430\\u0432 \\u0442\\u0435\\u0440\\u043C\\u0456\\u043D \\u0434\\u0456\\u0457\nAbstractUserDetailsAuthenticationProvider.locked=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u0438\\u0439\nAbstractUserDetailsAuthenticationProvider.onlySupports=\\u0422\\u0456\\u043B\\u044C\\u043A\\u0438 UsernamePasswordAuthenticationToken \\u043F\\u0456\\u0434\\u0442\\u0440\\u0438\\u043C\\u0443\\u0454\\u0442\\u044C\\u0441\\u044F\nAccountStatusUserDetailsChecker.credentialsExpired=\\u041F\\u043E\\u0432\\u043D\\u043E\\u0432\\u0430\\u0436\\u0435\\u043D\\u043D\\u044F \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0432\\u0438\\u0447\\u0435\\u0440\\u043F\\u0430\\u043B\\u0438 \\u0442\\u0435\\u0440\\u043C\\u0456\\u043D \\u0434\\u0456\\u0457\nAccountStatusUserDetailsChecker.disabled=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0437\\u0430\\u0431\\u043E\\u0440\\u043E\\u043D\\u0435\\u043D\\u0438\\u0439\nAccountStatusUserDetailsChecker.expired=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0432\\u0438\\u0447\\u0435\\u0440\\u043F\\u0430\\u0432 \\u0442\\u0435\\u0440\\u043C\\u0456\\u043D \\u0434\\u0456\\u0457\nAccountStatusUserDetailsChecker.locked=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u0438\\u0439\nAclEntryAfterInvocationProvider.noPermission=\\u0410\\u0443\\u0442\\u0435\\u043D\\u0442\\u0438\\u0444\\u0456\\u043A\\u0430\\u0446\\u0456\\u044F {0} \\u043D\\u0435 \\u043C\\u0430\\u0454 \\u0436\\u043E\\u0434\\u043D\\u043E\\u0433\\u043E \\u0434\\u043E\\u0437\\u0432\\u043E\\u043B\\u0443 \\u0434\\u043B\\u044F \\u0434\\u043E\\u0441\\u0442\\u0443\\u043F\\u0443 \\u0434\\u043E \\u043E\\u0431'\\u0454\\u043A\\u0442\\u0443 \\u0434\\u043E\\u043C\\u0435\\u043D\\u0443 {1}\nAnonymousAuthenticationProvider.incorrectKey=\\u0414\\u0430\\u043D\\u0438\\u0439 AnonymousAuthenticationToken \\u043D\\u0435 \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u044C \\u043E\\u0447\\u0456\\u043A\\u0443\\u0432\\u0430\\u043D\\u043E\\u0433\\u043E \\u043A\\u043B\\u044E\\u0447\\u0430\nBindAuthenticator.badCredentials=\\u0414\\u0430\\u043D\\u0456 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u043D\\u0435\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u043D\\u0456\nBindAuthenticator.emptyPassword=\\u0414\\u0430\\u043D\\u0456 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u043D\\u0435\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u043D\\u0456\nCasAuthenticationProvider.incorrectKey=\\u0414\\u0430\\u043D\\u0438\\u0439 CasAuthenticationToken \\u043D\\u0435 \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u044C \\u043E\\u0447\\u0456\\u043A\\u0443\\u0432\\u0430\\u043D\\u043E\\u0433\\u043E \\u043A\\u043B\\u044E\\u0447\\u0430\nCasAuthenticationProvider.noServiceTicket=\\u041D\\u0435 \\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u043D\\u0430\\u0434\\u0430\\u0442\\u0438 \\u0431\\u0456\\u043B\\u0435\\u0442 \\u0441\\u0435\\u0440\\u0432\\u0456\\u0441\\u0443 CAS \\u0434\\u043B\\u044F \\u0432\\u0430\\u043B\\u0456\\u0434\\u0430\\u0446\\u0456\\u0457\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=\\u041C\\u0430\\u043A\\u0441\\u0438\\u043C\\u0430\\u043B\\u044C\\u043D\\u0430 \\u043A\\u0456\\u043B\\u044C\\u043A\\u0456\\u0441\\u0442\\u044C \\u0441\\u0435\\u0441\\u0456\\u0439 ({0}) \\u0434\\u043B\\u044F \\u0446\\u044C\\u043E\\u0433\\u043E \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0432\\u0438\\u0447\\u0435\\u0440\\u043F\\u0430\\u043D\\u0430\nDigestAuthenticationFilter.incorrectRealm=\\u041D\\u0430\\u0437\\u0432\\u0430 \\u0434\\u043E\\u043C\\u0435\\u043D\\u0443 {0} \\u0443 \\u0432\\u0456\\u0434\\u043F\\u043E\\u0432\\u0456\\u0434\\u0456 \\u043D\\u0435 \\u0432\\u0456\\u0434\\u043F\\u043E\\u0432\\u0456\\u0434\\u0430\\u0454 \\u043D\\u0430\\u0437\\u0432\\u0456 \\u0434\\u043E\\u043C\\u0435\\u043D\\u0443 {1} \\u0432 \\u0441\\u0438\\u0441\\u0442\\u0435\\u043C\\u0456\nDigestAuthenticationFilter.incorrectResponse=\\u041D\\u0435\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u043D\\u0430 \\u0432\\u0456\\u0434\\u043F\\u043E\\u0432\\u0456\\u0434\\u044C\nDigestAuthenticationFilter.missingAuth=\\u0412\\u0456\\u0434\\u0441\\u0443\\u0442\\u043D\\u0454 \\u043E\\u0431\\u043E\\u0432'\\u044F\\u0437\\u043A\\u043E\\u0432\\u0435 \\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u043D\\u044F \\u0434\\u0430\\u0439\\u0434\\u0436\\u0435\\u0441\\u0442\\u0430 \\u0434\\u043B\\u044F 'auth' QOP; \\u043E\\u0442\\u0440\\u0438\\u043C\\u0430\\u043D\\u0438\\u0439 \\u0437\\u0430\\u0433\\u043E\\u043B\\u043E\\u0432\\u043E\\u043A {0}\nDigestAuthenticationFilter.missingMandatory=\\u0412\\u0456\\u0434\\u0441\\u0443\\u0442\\u043D\\u0454 \\u043E\\u0431\\u043E\\u0432'\\u044F\\u0437\\u043A\\u043E\\u0432\\u0435 \\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u043D\\u044F \\u0434\\u0430\\u0439\\u0434\\u0436\\u0435\\u0441\\u0442\\u0430; \\u043E\\u0442\\u0440\\u0438\\u043C\\u0430\\u043D\\u0438\\u0439 \\u0437\\u0430\\u0433\\u043E\\u043B\\u043E\\u0432\\u043E\\u043A {0}\nDigestAuthenticationFilter.nonceCompromised=\\u041A\\u043E\\u0434 (nonce) \\u0441\\u043A\\u043E\\u043C\\u043F\\u0440\\u043E\\u043C\\u0435\\u0442\\u043E\\u0432\\u0430\\u043D\\u0438\\u0439 {0}\nDigestAuthenticationFilter.nonceEncoding=\\u041A\\u043E\\u0434 (nonce) \\u043D\\u0435 \\u0437\\u0430\\u0448\\u0438\\u0444\\u0440\\u043E\\u0432\\u0430\\u043D\\u0438\\u0439 \\u0432 Base64; \\u043E\\u0442\\u0440\\u0438\\u043C\\u0430\\u043D\\u0438\\u0439 \\u043A\\u043E\\u0434 {0}\nDigestAuthenticationFilter.nonceExpired=\\u041A\\u043E\\u0434 (nonce) \\u0432\\u0438\\u0447\\u0435\\u0440\\u043F\\u0430\\u0432 \\u0442\\u0435\\u0440\\u043C\\u0456\\u043D \\u0434\\u0456\\u0457\nDigestAuthenticationFilter.nonceNotNumeric=\\u041F\\u0435\\u0440\\u0448\\u0438\\u0439 \\u0441\\u0438\\u043C\\u0432\\u043E\\u043B (token) \\u043A\\u043E\\u0434 \\u043F\\u043E\\u0432\\u0438\\u043D\\u0435\\u043D \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u0438 \\u0446\\u0438\\u0444\\u0440\\u043E\\u0432\\u0435 \\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u043D\\u044F, \\u0430 \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u044C\\: {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=\\u041A\\u043E\\u0434 (nonce) \\u043F\\u043E\\u0432\\u0438\\u043D\\u0435\\u043D \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u0438 \\u0434\\u0432\\u0430 \\u0441\\u0438\\u043C\\u0432\\u043E\\u043B\\u0438 (tokens), \\u0430\\u043B\\u0435 \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u044C {0}\nDigestAuthenticationFilter.usernameNotFound=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 {0} \\u043D\\u0435 \\u0437\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u0438\\u0439\n#ExceptionTranslationFilter.insufficientAuthentication=Full authentication is required to access this resource\nJdbcDaoImpl.noAuthority=\\u041A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447 {0} \\u043D\\u0435 \\u043C\\u0430\\u0454 \\u043F\\u043E\\u0432\\u043D\\u043E\\u0432\\u0430\\u0436\\u0435\\u043D\\u044C (GrantedAuthority)\nJdbcDaoImpl.notFound=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 {0} \\u043D\\u0435 \\u0437\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u0438\\u0439\nLdapAuthenticationProvider.badCredentials=\\u0414\\u0430\\u043D\\u0456 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u043D\\u0435\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u043D\\u0456\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=\\u041F\\u043E\\u0432\\u043D\\u043E\\u0432\\u0430\\u0436\\u0435\\u043D\\u043D\\u044F \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0432\\u0438\\u0447\\u0435\\u0440\\u043F\\u0430\\u043B\\u0438 \\u0442\\u0435\\u0440\\u043C\\u0456\\u043D \\u0434\\u0456\\u0457\nLdapAuthenticationProvider.disabled=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0437\\u0430\\u0431\\u043E\\u0440\\u043E\\u043D\\u0435\\u043D\\u0438\\u0439\nLdapAuthenticationProvider.expired=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0432\\u0438\\u0447\\u0435\\u0440\\u043F\\u0430\\u0432 \\u0442\\u0435\\u0440\\u043C\\u0456\\u043D \\u0434\\u0456\\u0457\nLdapAuthenticationProvider.locked=\\u041E\\u0431\\u043B\\u0456\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043A\\u043E\\u0440\\u0443\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u0438\\u0439\nLdapAuthenticationProvider.emptyUsername=\\u041F\\u043E\\u0440\\u043E\\u0436\\u043D\\u0454 \\u0456\\u043C'\\u044F \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u0454 \\u0437\\u0430\\u0431\\u043E\\u0440\\u043E\\u043D\\u0435\\u043D\\u0438\\u043C\nLdapAuthenticationProvider.onlySupports=\\u0422\\u0456\\u043B\\u044C\\u043A\\u0438 UsernamePasswordAuthenticationToken \\u043F\\u0456\\u0434\\u0442\\u0440\\u0438\\u043C\\u0443\\u0454\\u0442\\u044C\\u0441\\u044F\nPasswordComparisonAuthenticator.badCredentials=\\u0414\\u0430\\u043D\\u0456 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430 \\u043D\\u0435\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u043D\\u0456\n#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=AuthenticationProvider \\u0434\\u043B\\u044F {0} \\u043D\\u0435 \\u0437\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u0438\\u0439\nRememberMeAuthenticationProvider.incorrectKey=\\u0414\\u0430\\u043D\\u0438\\u0439 RememberMeAuthenticationToken \\u043D\\u0435 \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u044C \\u043E\\u0447\\u0456\\u043A\\u0443\\u0432\\u0430\\u043D\\u043E\\u0433\\u043E \\u043A\\u043B\\u044E\\u0447\\u0430\nRunAsImplAuthenticationProvider.incorrectKey=\\u0414\\u0430\\u043D\\u0438\\u0439 RunAsUserToken \\u043D\\u0435 \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u044C \\u043E\\u0447\\u0456\\u043A\\u0443\\u0432\\u0430\\u043D\\u043E\\u0433\\u043E \\u043A\\u043B\\u044E\\u0447\\u0430\nSubjectDnX509PrincipalExtractor.noMatching=\\u041D\\u0435 \\u0437\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u043E \\u0432\\u0456\\u0434\\u043F\\u043E\\u0432\\u0456\\u0434\\u043D\\u043E\\u0433\\u043E \\u0448\\u0430\\u0431\\u043B\\u043E\\u043D\\u0443 \\u0432 subjectDN\\: {0}\nSwitchUserFilter.noCurrentUser=\\u041D\\u0435\\u043C\\u0430\\u0454 \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430, \\u043F\\u043E\\u0432'\\u044F\\u0437\\u0430\\u043D\\u043E\\u0433\\u043E \\u0456\\u0437 \\u0434\\u0430\\u043D\\u0438\\u043C \\u0437\\u0430\\u043F\\u0438\\u0442\\u043E\\u043C\nSwitchUserFilter.noOriginalAuthentication=\\u041D\\u0435 \\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0437\\u043D\\u0430\\u0439\\u0442\\u0438 \\u043E\\u0440\\u0438\\u0433\\u0456\\u043D\\u0430\\u043B\\u044C\\u043D\\u0438\\u0439 \\u043E\\u0431'\\u0454\\u043A\\u0442 Authentication\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_zh_CN.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=\\u4E0D\\u5141\\u8BB8\\u8BBF\\u95EE\nAbstractLdapAuthenticationProvider.emptyPassword=\\u7528\\u6237\\u540d\\u6216\\u5bc6\\u7801\\u9519\\u8bef\nAbstractSecurityInterceptor.authenticationNotFound=\\u672A\\u5728SecurityContext\\u4E2D\\u67E5\\u627E\\u5230\\u8BA4\\u8BC1\\u5BF9\\u8C61\nAbstractUserDetailsAuthenticationProvider.badCredentials=\\u7528\\u6237\\u540d\\u6216\\u5bc6\\u7801\\u9519\\u8bef\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=\\u7528\\u6237\\u51ED\\u8BC1\\u5DF2\\u8FC7\\u671F\nAbstractUserDetailsAuthenticationProvider.disabled=\\u7528\\u6237\\u5DF2\\u5931\\u6548\nAbstractUserDetailsAuthenticationProvider.expired=\\u7528\\u6237\\u5E10\\u53F7\\u5DF2\\u8FC7\\u671F\nAbstractUserDetailsAuthenticationProvider.locked=\\u7528\\u6237\\u5E10\\u53F7\\u5DF2\\u88AB\\u9501\\u5B9A\nAbstractUserDetailsAuthenticationProvider.onlySupports=\\u4EC5\\u4EC5\\u652F\\u6301UsernamePasswordAuthenticationToken\nAccountStatusUserDetailsChecker.credentialsExpired=\\u7528\\u6237\\u51ED\\u8BC1\\u5DF2\\u8FC7\\u671F\nAccountStatusUserDetailsChecker.disabled=\\u7528\\u6237\\u5DF2\\u5931\\u6548\nAccountStatusUserDetailsChecker.expired=\\u7528\\u6237\\u5E10\\u53F7\\u5DF2\\u8FC7\\u671F\nAccountStatusUserDetailsChecker.locked=\\u7528\\u6237\\u5E10\\u53F7\\u5DF2\\u88AB\\u9501\\u5B9A\nAclEntryAfterInvocationProvider.noPermission=\\u7ED9\\u5B9A\\u7684Authentication\\u5BF9\\u8C61({0})\\u6839\\u672C\\u65E0\\u6743\\u64CD\\u63A7\\u9886\\u57DF\\u5BF9\\u8C61({1})\nAnonymousAuthenticationProvider.incorrectKey=\\u5C55\\u793A\\u7684AnonymousAuthenticationToken\\u4E0D\\u542B\\u6709\\u9884\\u671F\\u7684key\nBindAuthenticator.badCredentials=\\u7528\\u6237\\u540d\\u6216\\u5bc6\\u7801\\u9519\\u8bef\nBindAuthenticator.emptyPassword=\\u7528\\u6237\\u540d\\u6216\\u5bc6\\u7801\\u9519\\u8bef\nCasAuthenticationProvider.incorrectKey=\\u5C55\\u793A\\u7684CasAuthenticationToken\\u4E0D\\u542B\\u6709\\u9884\\u671F\\u7684key\nCasAuthenticationProvider.noServiceTicket=\\u672A\\u80FD\\u591F\\u6B63\\u786E\\u63D0\\u4F9B\\u5F85\\u9A8C\\u8BC1\\u7684CAS\\u670D\\u52A1\\u7968\\u6839\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=\\u5DF2\\u7ECF\\u8D85\\u8FC7\\u4E86\\u5F53\\u524D\\u4E3B\\u4F53({0})\\u88AB\\u5141\\u8BB8\\u7684\\u6700\\u5927\\u4F1A\\u8BDD\\u6570\\u91CF\nDigestAuthenticationFilter.incorrectRealm=\\u54CD\\u5E94\\u7ED3\\u679C\\u4E2D\\u7684Realm\\u540D\\u5B57({0})\\u540C\\u7CFB\\u7EDF\\u6307\\u5B9A\\u7684Realm\\u540D\\u5B57({1})\\u4E0D\\u543B\\u5408\nDigestAuthenticationFilter.incorrectResponse=\\u9519\\u8BEF\\u7684\\u54CD\\u5E94\\u7ED3\\u679C\nDigestAuthenticationFilter.missingAuth=\\u9057\\u6F0F\\u4E86\\u9488\\u5BF9'auth' QOP\\u7684\\u3001\\u5FC5\\u987B\\u7ED9\\u5B9A\\u7684\\u6458\\u8981\\u53D6\\u503C; \\u63A5\\u6536\\u5230\\u7684\\u5934\\u4FE1\\u606F\\u4E3A{0}\nDigestAuthenticationFilter.missingMandatory=\\u9057\\u6F0F\\u4E86\\u5FC5\\u987B\\u7ED9\\u5B9A\\u7684\\u6458\\u8981\\u53D6\\u503C; \\u63A5\\u6536\\u5230\\u7684\\u5934\\u4FE1\\u606F\\u4E3A{0}\nDigestAuthenticationFilter.nonceCompromised=Nonce\\u4EE4\\u724C\\u5DF2\\u7ECF\\u5B58\\u5728\\u95EE\\u9898\\u4E86\\uFF0C{0}\nDigestAuthenticationFilter.nonceEncoding=Nonce\\u672A\\u7ECF\\u8FC7Base64\\u7F16\\u7801; \\u76F8\\u5E94\\u7684nonce\\u53D6\\u503C\\u4E3A {0}\nDigestAuthenticationFilter.nonceExpired=Nonce\\u5DF2\\u7ECF\\u8FC7\\u671F/\\u8D85\\u65F6\nDigestAuthenticationFilter.nonceNotNumeric=Nonce\\u4EE4\\u724C\\u7684\\u7B2C1\\u90E8\\u5206\\u5E94\\u8BE5\\u662F\\u6570\\u5B57\\uFF0C\\u4F46\\u7ED3\\u679C\\u5374\\u662F{0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce\\u5E94\\u8BE5\\u7531\\u4E24\\u90E8\\u5206\\u53D6\\u503C\\u6784\\u6210\\uFF0C\\u4F46\\u7ED3\\u679C\\u5374\\u662F{0}\nDigestAuthenticationFilter.usernameNotFound=\\u7528\\u6237\\u540D{0}\\u672A\\u627E\\u5230\nExceptionTranslationFilter.insufficientAuthentication=\\u8bbf\\u95ee\\u6b64\\u8d44\\u6e90\\u9700\\u8981\\u5b8c\\u5168\\u8eab\\u4efd\\u9a8c\\u8bc1\nJdbcDaoImpl.noAuthority=\\u6CA1\\u6709\\u4E3A\\u7528\\u6237{0}\\u6307\\u5B9A\\u89D2\\u8272\nJdbcDaoImpl.notFound=\\u672A\\u627E\\u5230\\u7528\\u6237{0}\nLdapAuthenticationProvider.badCredentials=\\u7528\\u6237\\u540d\\u6216\\u5bc6\\u7801\\u9519\\u8bef\n#LdapAuthenticationProvider.badLdapConnection=Connection to LDAP server failed\nLdapAuthenticationProvider.credentialsExpired=\\u7528\\u6237\\u51ED\\u8BC1\\u5DF2\\u8FC7\\u671F\nLdapAuthenticationProvider.disabled=\\u7528\\u6237\\u5DF2\\u5931\\u6548\nLdapAuthenticationProvider.expired=\\u7528\\u6237\\u5E10\\u53F7\\u5DF2\\u8FC7\\u671F\nLdapAuthenticationProvider.locked=\\u7528\\u6237\\u5E10\\u53F7\\u5DF2\\u88AB\\u9501\\u5B9A\nLdapAuthenticationProvider.emptyUsername=\\u7528\\u6237\\u540D\\u4E0D\\u5141\\u8BB8\\u4E3A\\u7A7A\nLdapAuthenticationProvider.onlySupports=\\u4EC5\\u4EC5\\u652F\\u6301UsernamePasswordAuthenticationToken\nPasswordComparisonAuthenticator.badCredentials=\\u7528\\u6237\\u540d\\u6216\\u5bc6\\u7801\\u9519\\u8bef\n#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\nProviderManager.providerNotFound=\\u672A\\u67E5\\u627E\\u5230\\u9488\\u5BF9{0}\\u7684AuthenticationProvider\nRememberMeAuthenticationProvider.incorrectKey=\\u5C55\\u793ARememberMeAuthenticationToken\\u4E0D\\u542B\\u6709\\u9884\\u671F\\u7684key\nRunAsImplAuthenticationProvider.incorrectKey=\\u5C55\\u793A\\u7684RunAsUserToken\\u4E0D\\u542B\\u6709\\u9884\\u671F\\u7684key\nSubjectDnX509PrincipalExtractor.noMatching=\\u672A\\u5728subjectDN\\: {0}\\u4E2D\\u627E\\u5230\\u5339\\u914D\\u7684\\u6A21\\u5F0F\nSwitchUserFilter.noCurrentUser=\\u4E0D\\u5B58\\u5728\\u5F53\\u524D\\u7528\\u6237\nSwitchUserFilter.noOriginalAuthentication=\\u4E0D\\u80FD\\u591F\\u67E5\\u627E\\u5230\\u539F\\u5148\\u7684\\u5DF2\\u8BA4\\u8BC1\\u5BF9\\u8C61\n"
  },
  {
    "path": "core/src/main/resources/org/springframework/security/messages_zh_TW.properties",
    "content": "AbstractAccessDecisionManager.accessDenied=\\u62D2\\u7D55\\u5B58\\u53D6\nAbstractLdapAuthenticationProvider.emptyPassword=\\u6C92\\u6709\\u6307\\u5B9A\\u5BC6\\u78BC\nAbstractSecurityInterceptor.authenticationNotFound=SecurityContext \\u4E2D\\u6C92\\u6709 Authentication \\u7269\\u4EF6\nAbstractUserDetailsAuthenticationProvider.badCredentials=\\u6191\\u8B49\\u932F\\u8AA4\nAbstractUserDetailsAuthenticationProvider.credentialsExpired=\\u4F7F\\u7528\\u8005\\u6191\\u8B49\\u5DF2\\u904E\\u671F\nAbstractUserDetailsAuthenticationProvider.disabled=\\u4F7F\\u7528\\u8005\\u5DF2\\u88AB\\u505C\\u7528\nAbstractUserDetailsAuthenticationProvider.expired=\\u4F7F\\u7528\\u8005\\u5E33\\u865F\\u5DF2\\u904E\\u671F\nAbstractUserDetailsAuthenticationProvider.locked=\\u4F7F\\u7528\\u8005\\u5E33\\u865F\\u5DF2\\u88AB\\u9396\\u5B9A\nAbstractUserDetailsAuthenticationProvider.onlySupports=\\u53EA\\u652F\\u63F4 UsernamePasswordAuthenticationToken\nAccountStatusUserDetailsChecker.credentialsExpired=\\u4F7F\\u7528\\u8005\\u6191\\u8B49\\u5DF2\\u904E\\u671F\nAccountStatusUserDetailsChecker.disabled=\\u4F7F\\u7528\\u8005\\u5DF2\\u88AB\\u505C\\u7528\nAccountStatusUserDetailsChecker.expired=\\u4F7F\\u7528\\u8005\\u5E33\\u865F\\u5DF2\\u904E\\u671F\nAccountStatusUserDetailsChecker.locked=\\u4F7F\\u7528\\u8005\\u5E33\\u865F\\u5DF2\\u88AB\\u9396\\u5B9A\nAclEntryAfterInvocationProvider.noPermission=Authentication {0} \\u6C92\\u6709\\u5B58\\u53D6\\u8CC7\\u6599\\u7269\\u4EF6 {1} \\u7684\\u6B0A\\u9650\nAnonymousAuthenticationProvider.incorrectKey=AnonymousAuthenticationToken \\u4E2D\\u7684\\u91D1\\u9470\\u4E0D\\u7B26\\u9810\\u671F\nBindAuthenticator.badCredentials=\\u6191\\u8B49\\u932F\\u8AA4\nBindAuthenticator.emptyPassword=\\u6C92\\u6709\\u6307\\u5B9A\\u5BC6\\u78BC\nCasAuthenticationProvider.incorrectKey=CasAuthenticationToken \\u4E2D\\u7684\\u91D1\\u9470\\u4E0D\\u7B26\\u9810\\u671F\nCasAuthenticationProvider.noServiceTicket=\\u7121\\u6CD5\\u63D0\\u4F9B CAS \\u670D\\u52D9\\u7968\\u8B49\\u9032\\u884C\\u9A57\\u8B49\nConcurrentSessionControlAuthenticationStrategy.exceededAllowed=\\u5DF2\\u9054\\u6B64\\u5C0D\\u8C61\\u7684\\u5DE5\\u4F5C\\u968E\\u6BB5\\u4E0A\\u9650\\u6578 {0}\nDigestAuthenticationFilter.incorrectRealm=\\u56DE\\u61C9\\u7684\\u9818\\u57DF (Realm) \\u540D\\u7A31 {0} \\u8207\\u7CFB\\u7D71\\u9818\\u57DF\\u540D\\u7A31 {1} \\u4E0D\\u4E00\\u81F4\nDigestAuthenticationFilter.incorrectResponse=\\u932F\\u8AA4\\u7684\\u56DE\\u61C9\nDigestAuthenticationFilter.missingAuth=\\u627E\\u4E0D\\u5230 QOP \\u70BA 'auth' \\u6642\\u5FC5\\u8981\\u7684\\u6458\\u8981\\u503C\\uFF0C\\u6536\\u5230\\u7684\\u6A19\\u982D\\u70BA {0}\nDigestAuthenticationFilter.missingMandatory=\\u627E\\u4E0D\\u5230\\u5FC5\\u8981\\u7684\\u6458\\u8981\\u503C\\uFF0C\\u6536\\u5230\\u7684\\u6A19\\u982D\\u70BA {0}\nDigestAuthenticationFilter.nonceCompromised=Nonce token {0} \\u5DF2\\u6709\\u8CC7\\u5B89\\u7591\\u616E\nDigestAuthenticationFilter.nonceEncoding=Nonce \\u4E26\\u4E0D\\u662F\\u4EE5 Base64 \\u7DE8\\u78BC\\uFF0C\\u6536\\u5230\\u7684 nonce \\u70BA {0}\nDigestAuthenticationFilter.nonceExpired=Nonce \\u5DF2\\u903E\\u671F\nDigestAuthenticationFilter.nonceNotNumeric=Nonce token \\u7684\\u7B2C\\u4E00\\u90E8\\u5206\\u61C9\\u8A72\\u70BA\\u6578\\u5B57\\uFF0C\\u4F46\\u5BE6\\u969B\\u503C\\u70BA {0}\nDigestAuthenticationFilter.nonceNotTwoTokens=Nonce \\u61C9\\u8A72\\u5305\\u542B\\u5169\\u500B\\u90E8\\u5206\\uFF0C\\u4F46\\u5BE6\\u969B\\u503C\\u70BA {0}\nDigestAuthenticationFilter.usernameNotFound=\\u627E\\u4E0D\\u5230\\u4F7F\\u7528\\u8005\\u540D\\u7A31 {0}\nExceptionTranslationFilter.insufficientAuthentication=\\u8a2a\\u554f\\u6b64\\u8cc7\\u6e90\\u9700\\u8981\\u5b8c\\u5168\\u8eab\\u4efd\\u9a57\\u8b49\nJdbcDaoImpl.noAuthority=\\u4F7F\\u7528\\u8005 {0} \\u6C92\\u6709 GrantedAuthority\nJdbcDaoImpl.notFound=\\u627E\\u4E0D\\u5230\\u4F7F\\u7528\\u8005 {0}\nLdapAuthenticationProvider.badCredentials=\\u6191\\u8B49\\u932F\\u8AA4\nLdapAuthenticationProvider.badLdapConnection=\\u7121\\u6CD5\\u9023\\u7DDA\\u5230 LDAP \\u4F3A\\u670D\\u5668\nLdapAuthenticationProvider.credentialsExpired=\\u4F7F\\u7528\\u8005\\u6191\\u8B49\\u5DF2\\u904E\\u671F\nLdapAuthenticationProvider.disabled=\\u4F7F\\u7528\\u8005\\u5DF2\\u88AB\\u505C\\u7528\nLdapAuthenticationProvider.expired=\\u4F7F\\u7528\\u8005\\u5E33\\u865F\\u5DF2\\u904E\\u671F\nLdapAuthenticationProvider.locked=\\u4F7F\\u7528\\u8005\\u5E33\\u865F\\u5DF2\\u88AB\\u9396\\u5B9A\nLdapAuthenticationProvider.emptyUsername=\\u4E0D\\u5141\\u8A31\\u7A7A\\u767D\\u7684\\u4F7F\\u7528\\u8005\\u540D\\u7A31\nLdapAuthenticationProvider.onlySupports=\\u53EA\\u652F\\u63F4 UsernamePasswordAuthenticationToken\nPasswordComparisonAuthenticator.badCredentials=\\u6191\\u8B49\\u932F\\u8AA4\nPersistentTokenBasedRememberMeServices.cookieStolen=\\u7121\\u6548\\u7684 Remember-Me Token (\\u5E8F\\u865F\\u8207 token \\u4E0D\\u4E00\\u81F4)\\uFF0C\\u9019\\u4EE3\\u8868\\u60A8\\u5DF2\\u906D\\u53D7 Cookie \\u7ACA\\u53D6\\u653B\\u64CA\\u3002\nProviderManager.providerNotFound=\\u627E\\u4E0D\\u5230\\u80FD\\u652F\\u63F4 {0} \\u7684 AuthenticationProvider\nRememberMeAuthenticationProvider.incorrectKey=RememberMeAuthenticationToken \\u4E2D\\u7684\\u91D1\\u9470\\u4E0D\\u7B26\\u9810\\u671F\nRunAsImplAuthenticationProvider.incorrectKey=RunAsUserToken \\u4E2D\\u7684\\u91D1\\u9470\\u4E0D\\u7B26\\u9810\\u671F\nSubjectDnX509PrincipalExtractor.noMatching=\\u5728 subjectDN {0} \\u4E2D\\u627E\\u4E0D\\u5230\\u7B26\\u5408\\u898F\\u5247\\u904B\\u7B97\\u5F0F\\u7684\\u6A21\\u5F0F\nSwitchUserFilter.noCurrentUser=\\u627E\\u4E0D\\u5230\\u539F\\u4F7F\\u7528\\u8005\nSwitchUserFilter.noOriginalAuthentication=\\u627E\\u4E0D\\u5230\\u539F\\u6709 Authentication \\u7269\\u4EF6\n"
  },
  {
    "path": "core/src/site/site.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<project name=\"Spring Security Core\">\n\n\t<body>\n\t\t<menu ref=\"parent\"/>\n\t\t<menu ref=\"reports\"/>\n\t</body>\n\n</project>\n\n\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/DelegatingSecurityContextTestUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.BiConsumer;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport org.springframework.scheduling.TaskScheduler;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * @author Steve Riesenberg\n */\npublic final class DelegatingSecurityContextTestUtils {\n\n\tprivate DelegatingSecurityContextTestUtils() {\n\t}\n\n\tpublic static <T extends Executor> SecurityContext runAndReturn(ThreadFactory threadFactory,\n\t\t\tFunction<ScheduledExecutorService, T> factory, BiConsumer<T, Runnable> fn) throws Exception {\n\t\tCountDownLatch countDownLatch = new CountDownLatch(1);\n\t\tAtomicReference<SecurityContext> result = new AtomicReference<>();\n\t\tScheduledExecutorService delegate = Executors.newSingleThreadScheduledExecutor(threadFactory);\n\t\ttry {\n\t\t\tT executor = factory.apply(delegate);\n\t\t\tRunnable task = () -> {\n\t\t\t\tresult.set(SecurityContextHolder.getContext());\n\t\t\t\tcountDownLatch.countDown();\n\t\t\t};\n\t\t\tfn.accept(executor, task);\n\t\t\tcountDownLatch.await();\n\n\t\t\treturn result.get();\n\t\t}\n\t\tfinally {\n\t\t\tdelegate.shutdown();\n\t\t}\n\t}\n\n\tpublic static <T extends TaskScheduler> SecurityContext runAndReturn(ThreadFactory threadFactory,\n\t\t\tFunction<ScheduledExecutorService, T> factory, BiFunction<T, Runnable, ScheduledFuture<?>> fn)\n\t\t\tthrows Exception {\n\t\tCountDownLatch countDownLatch = new CountDownLatch(1);\n\t\tAtomicReference<SecurityContext> result = new AtomicReference<>();\n\t\tScheduledExecutorService delegate = Executors.newSingleThreadScheduledExecutor(threadFactory);\n\t\ttry {\n\t\t\tT taskScheduler = factory.apply(delegate);\n\t\t\tRunnable task = () -> {\n\t\t\t\tresult.set(SecurityContextHolder.getContext());\n\t\t\t\tcountDownLatch.countDown();\n\t\t\t};\n\t\t\tScheduledFuture<?> future = fn.apply(taskScheduler, task);\n\t\t\tcountDownLatch.await();\n\t\t\tfuture.cancel(false);\n\n\t\t\treturn result.get();\n\t\t}\n\t\tfinally {\n\t\t\tdelegate.shutdown();\n\t\t}\n\t}\n\n\tpublic static <T extends Executor> SecurityContext callAndReturn(ThreadFactory threadFactory,\n\t\t\tFunction<ScheduledExecutorService, T> factory,\n\t\t\tBiFunction<T, Callable<SecurityContext>, Future<SecurityContext>> fn) throws Exception {\n\t\tScheduledExecutorService delegate = Executors.newSingleThreadScheduledExecutor(threadFactory);\n\t\ttry {\n\t\t\tT executor = factory.apply(delegate);\n\t\t\tCallable<SecurityContext> task = SecurityContextHolder::getContext;\n\t\t\treturn fn.apply(executor, task).get();\n\t\t}\n\t\tfinally {\n\t\t\tdelegate.shutdown();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/ITargetObject.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\n/**\n * Represents the interface of a secured object.\n *\n * @author Ben Alex\n */\npublic interface ITargetObject {\n\n\tInteger computeHashCode(String input);\n\n\tint countLength(String input);\n\n\tString makeLowerCase(String input);\n\n\tString makeUpperCase(String input);\n\n\tString publicMakeLowerCase(String input);\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/OtherTargetObject.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\n/**\n * Simply extends {@link TargetObject} so we have a different object to put configuration\n * attributes against.\n * <P>\n * There is no different behaviour. We have to define each method so that\n * <code>Class.getMethod(methodName, args)</code> returns a <code>Method</code>\n * referencing this class rather than the parent class.\n * </p>\n * <P>\n * We need to implement <code>ITargetObject</code> again because the\n * <code>MethodDefinitionAttributes</code> only locates attributes on interfaces\n * explicitly defined by the intercepted class (not the interfaces defined by its parent\n * class or classes).\n * </p>\n *\n * @author Ben Alex\n */\npublic class OtherTargetObject extends TargetObject implements ITargetObject {\n\n\t@Override\n\tpublic String makeLowerCase(String input) {\n\t\treturn super.makeLowerCase(input);\n\t}\n\n\t@Override\n\tpublic String makeUpperCase(String input) {\n\t\treturn super.makeUpperCase(input);\n\t}\n\n\t@Override\n\tpublic String publicMakeLowerCase(String input) {\n\t\treturn super.publicMakeLowerCase(input);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/PopulatedDatabase.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport javax.sql.DataSource;\n\nimport org.springframework.jdbc.core.JdbcTemplate;\n\n/**\n * Singleton which provides a populated database connection for all JDBC-related unit\n * tests.\n *\n * @author Ben Alex\n */\npublic final class PopulatedDatabase {\n\n\tprivate static TestDataSource dataSource = null;\n\n\tprivate PopulatedDatabase() {\n\t}\n\n\tpublic static DataSource getDataSource() {\n\t\tif (dataSource == null) {\n\t\t\tsetupDataSource();\n\t\t}\n\t\treturn dataSource;\n\t}\n\n\tprivate static void setupDataSource() {\n\t\tdataSource = new TestDataSource(\"springsecuritytest\");\n\t\tJdbcTemplate template = new JdbcTemplate(dataSource);\n\t\ttemplate.execute(\n\t\t\t\t\"CREATE TABLE USERS(USERNAME VARCHAR_IGNORECASE(50) NOT NULL PRIMARY KEY,PASSWORD VARCHAR_IGNORECASE(500) NOT NULL,ENABLED BOOLEAN NOT NULL)\");\n\t\ttemplate.execute(\n\t\t\t\t\"CREATE TABLE AUTHORITIES(USERNAME VARCHAR_IGNORECASE(50) NOT NULL,AUTHORITY VARCHAR_IGNORECASE(50) NOT NULL,CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME))\");\n\t\ttemplate.execute(\"CREATE UNIQUE INDEX IX_AUTH_USERNAME ON AUTHORITIES(USERNAME,AUTHORITY)\");\n\t\ttemplate.execute(\n\t\t\t\t\"CREATE TABLE ACL_OBJECT_IDENTITY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 0)  NOT NULL PRIMARY KEY,OBJECT_IDENTITY VARCHAR_IGNORECASE(250) NOT NULL,PARENT_OBJECT BIGINT,ACL_CLASS VARCHAR_IGNORECASE(250) NOT NULL,CONSTRAINT UNIQUE_OBJECT_IDENTITY UNIQUE(OBJECT_IDENTITY),CONSTRAINT SYS_FK_3 FOREIGN KEY(PARENT_OBJECT) REFERENCES ACL_OBJECT_IDENTITY(ID))\");\n\t\ttemplate.execute(\n\t\t\t\t\"CREATE TABLE ACL_PERMISSION(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 0)  NOT NULL PRIMARY KEY,ACL_OBJECT_IDENTITY BIGINT NOT NULL,RECIPIENT VARCHAR_IGNORECASE(100) NOT NULL,MASK INTEGER NOT NULL,CONSTRAINT UNIQUE_RECIPIENT UNIQUE(ACL_OBJECT_IDENTITY,RECIPIENT),CONSTRAINT SYS_FK_7 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID))\");\n\t\ttemplate.execute(\"SET IGNORECASE TRUE\");\n\t\ttemplate.execute(\"INSERT INTO USERS VALUES('dianne','emu',TRUE)\");\n\t\ttemplate.execute(\"INSERT INTO USERS VALUES('rod','koala',TRUE)\");\n\t\ttemplate.execute(\"INSERT INTO USERS VALUES('peter','opal',FALSE)\");\n\t\ttemplate.execute(\"INSERT INTO USERS VALUES('scott','wombat',TRUE)\");\n\t\ttemplate.execute(\"INSERT INTO USERS VALUES('cooper','kookaburra',TRUE)\");\n\t\ttemplate.execute(\"INSERT INTO AUTHORITIES VALUES('rod','ROLE_TELLER')\");\n\t\ttemplate.execute(\"INSERT INTO AUTHORITIES VALUES('rod','ROLE_SUPERVISOR')\");\n\t\ttemplate.execute(\"INSERT INTO AUTHORITIES VALUES('dianne','ROLE_TELLER')\");\n\t\ttemplate.execute(\"INSERT INTO AUTHORITIES VALUES('scott','ROLE_TELLER')\");\n\t\ttemplate.execute(\"INSERT INTO AUTHORITIES VALUES('peter','ROLE_TELLER')\");\n\t\ttemplate.execute(\n\t\t\t\t\"INSERT INTO acl_object_identity VALUES (1, 'org.springframework.security.acl.DomainObject:1', null, 'org.springframework.security.acl.basic.SimpleAclEntry');\");\n\t\ttemplate.execute(\n\t\t\t\t\"INSERT INTO acl_object_identity VALUES (2, 'org.springframework.security.acl.DomainObject:2', 1, 'org.springframework.security.acl.basic.SimpleAclEntry');\");\n\t\ttemplate.execute(\n\t\t\t\t\"INSERT INTO acl_object_identity VALUES (3, 'org.springframework.security.acl.DomainObject:3', 1, 'org.springframework.security.acl.basic.SimpleAclEntry');\");\n\t\ttemplate.execute(\n\t\t\t\t\"INSERT INTO acl_object_identity VALUES (4, 'org.springframework.security.acl.DomainObject:4', 1, 'org.springframework.security.acl.basic.SimpleAclEntry');\");\n\t\ttemplate.execute(\n\t\t\t\t\"INSERT INTO acl_object_identity VALUES (5, 'org.springframework.security.acl.DomainObject:5', 3, 'org.springframework.security.acl.basic.SimpleAclEntry');\");\n\t\ttemplate.execute(\n\t\t\t\t\"INSERT INTO acl_object_identity VALUES (6, 'org.springframework.security.acl.DomainObject:6', 3, 'org.springframework.security.acl.basic.SimpleAclEntry');\");\n\t\t// ----- BEGIN deviation from normal sample data load script -----\n\t\ttemplate.execute(\n\t\t\t\t\"INSERT INTO acl_object_identity VALUES (7, 'org.springframework.security.acl.DomainObject:7', 3, 'some.invalid.acl.entry.class');\");\n\t\t// ----- FINISH deviation from normal sample data load script -----\n\t\ttemplate.execute(\"INSERT INTO acl_permission VALUES (null, 1, 'ROLE_SUPERVISOR', 1);\");\n\t\ttemplate.execute(\"INSERT INTO acl_permission VALUES (null, 2, 'ROLE_SUPERVISOR', 0);\");\n\t\ttemplate.execute(\"INSERT INTO acl_permission VALUES (null, 2, 'rod', 2);\");\n\t\ttemplate.execute(\"INSERT INTO acl_permission VALUES (null, 3, 'scott', 14);\");\n\t\ttemplate.execute(\"INSERT INTO acl_permission VALUES (null, 6, 'scott', 1);\");\n\t\tcreateGroupTables(template);\n\t\tinsertGroupData(template);\n\t}\n\n\tpublic static void createGroupTables(JdbcTemplate template) {\n\t\t// Group tables and data\n\t\ttemplate.execute(\n\t\t\t\t\"CREATE TABLE GROUPS(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY, GROUP_NAME VARCHAR_IGNORECASE(50) NOT NULL)\");\n\t\ttemplate.execute(\n\t\t\t\t\"CREATE TABLE GROUP_AUTHORITIES(GROUP_ID BIGINT NOT NULL, AUTHORITY VARCHAR(50) NOT NULL, CONSTRAINT FK_GROUP_AUTHORITIES_GROUP FOREIGN KEY(GROUP_ID) REFERENCES GROUPS(ID))\");\n\t\ttemplate.execute(\n\t\t\t\t\"CREATE TABLE GROUP_MEMBERS(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY, USERNAME VARCHAR(50) NOT NULL, GROUP_ID BIGINT NOT NULL, CONSTRAINT FK_GROUP_MEMBERS_GROUP FOREIGN KEY(GROUP_ID) REFERENCES GROUPS(ID))\");\n\t}\n\n\tpublic static void insertGroupData(JdbcTemplate template) {\n\t\ttemplate.execute(\"INSERT INTO USERS VALUES('jerry','password',TRUE)\");\n\t\ttemplate.execute(\"INSERT INTO USERS VALUES('tom','password',TRUE)\");\n\t\ttemplate.execute(\"INSERT INTO GROUPS VALUES (0, 'GROUP_0')\");\n\t\ttemplate.execute(\"INSERT INTO GROUPS VALUES (1, 'GROUP_1')\");\n\t\ttemplate.execute(\"INSERT INTO GROUPS VALUES (2, 'GROUP_2')\");\n\t\t// Group 3 isn't used\n\t\ttemplate.execute(\"INSERT INTO GROUPS VALUES (3, 'GROUP_3')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_AUTHORITIES VALUES (0, 'ROLE_A')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_AUTHORITIES VALUES (1, 'ROLE_B')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_AUTHORITIES VALUES (1, 'ROLE_C')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_AUTHORITIES VALUES (2, 'ROLE_A')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_AUTHORITIES VALUES (2, 'ROLE_B')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_AUTHORITIES VALUES (2, 'ROLE_C')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_AUTHORITIES VALUES (3, 'ROLE_D')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_AUTHORITIES VALUES (3, 'ROLE_E')\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_MEMBERS VALUES (0, 'jerry', 0)\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_MEMBERS VALUES (1, 'jerry', 1)\");\n\t\t// tom has groups with overlapping roles\n\t\ttemplate.execute(\"INSERT INTO GROUP_MEMBERS VALUES (2, 'tom', 1)\");\n\t\ttemplate.execute(\"INSERT INTO GROUP_MEMBERS VALUES (3, 'tom', 2)\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/TargetObject.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * Represents a secured object.\n *\n * @author Ben Alex\n */\npublic class TargetObject implements ITargetObject {\n\n\t@Override\n\tpublic Integer computeHashCode(String input) {\n\t\treturn input.hashCode();\n\t}\n\n\t@Override\n\tpublic int countLength(String input) {\n\t\treturn input.length();\n\t}\n\n\t/**\n\t * Returns the lowercase string, followed by security environment information.\n\t * @param input the message to make lowercase\n\t * @return the lowercase message, a space, the <code>Authentication</code> class that\n\t * was on the <code>SecurityContext</code> at the time of method invocation, and a\n\t * boolean indicating if the <code>Authentication</code> object is authenticated or\n\t * not\n\t */\n\t@Override\n\tpublic String makeLowerCase(String input) {\n\t\tAuthentication auth = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (auth == null) {\n\t\t\treturn input.toLowerCase() + \" Authentication empty\";\n\t\t}\n\t\telse {\n\t\t\treturn input.toLowerCase() + \" \" + auth.getClass().getName() + \" \" + auth.isAuthenticated();\n\t\t}\n\t}\n\n\t/**\n\t * Returns the uppercase string, followed by security environment information.\n\t * @param input the message to make uppercase\n\t * @return the uppercase message, a space, the <code>Authentication</code> class that\n\t * was on the <code>SecurityContext</code> at the time of method invocation, and a\n\t * boolean indicating if the <code>Authentication</code> object is authenticated or\n\t * not\n\t */\n\t@Override\n\tpublic String makeUpperCase(String input) {\n\t\tAuthentication auth = SecurityContextHolder.getContext().getAuthentication();\n\t\treturn input.toUpperCase() + \" \" + auth.getClass().getName() + \" \" + auth.isAuthenticated();\n\t}\n\n\t/**\n\t * Delegates through to the {@link #makeLowerCase(String)} method.\n\t * @param input the message to be made lower-case\n\t */\n\t@Override\n\tpublic String publicMakeLowerCase(String input) {\n\t\treturn this.makeLowerCase(input);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/TestDataSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DriverManagerDataSource;\n\n/**\n * A Datasource bean which starts an in-memory HSQL database with the supplied name and\n * shuts down the database when the application context it is defined in is closed.\n *\n * @author Luke Taylor\n */\npublic class TestDataSource extends DriverManagerDataSource implements DisposableBean {\n\n\tString name;\n\n\tpublic TestDataSource(String databaseName) {\n\t\tthis.name = databaseName;\n\t\tSystem.out.println(\"Creating database: \" + this.name);\n\t\tsetDriverClassName(\"org.hsqldb.jdbcDriver\");\n\t\tsetUrl(\"jdbc:hsqldb:mem:\" + databaseName);\n\t\tsetUsername(\"sa\");\n\t\tsetPassword(\"\");\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tSystem.out.println(\"Shutting down database: \" + this.name);\n\t\tnew JdbcTemplate(this).execute(\"SHUTDOWN\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\n@ExtendWith(MockitoExtension.class)\npublic class AbstractSecurityExpressionHandlerTests {\n\n\t@Mock\n\tprivate AuthorizationManagerFactory<Object> authorizationManagerFactory;\n\n\tprivate AbstractSecurityExpressionHandler<Object> handler;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.handler = new AbstractSecurityExpressionHandler<Object>() {\n\t\t\t@Override\n\t\t\tprotected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,\n\t\t\t\t\tObject o) {\n\t\t\t\treturn new SecurityExpressionRoot(authentication) {\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\t}\n\n\t@Test\n\tpublic void beanNamesAreCorrectlyResolved() {\n\t\tthis.handler.setApplicationContext(new AnnotationConfigApplicationContext(TestConfiguration.class));\n\t\tExpression expression = this.handler.getExpressionParser()\n\t\t\t.parseExpression(\"@number10.compareTo(@number20) < 0\");\n\t\tassertThat(expression.getValue(this.handler.createEvaluationContext(mock(Authentication.class), new Object())))\n\t\t\t.isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void setExpressionParserNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setExpressionParser(null));\n\t}\n\n\t@Test\n\tpublic void setExpressionParser() {\n\t\tSpelExpressionParser parser = new SpelExpressionParser();\n\t\tthis.handler.setExpressionParser(parser);\n\t\tassertThat(parser == this.handler.getExpressionParser()).isTrue();\n\t}\n\n\t@Test\n\tpublic void setAuthorizationManagerFactoryNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setAuthorizationManagerFactory(null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizationManagerFactory() {\n\t\tthis.handler.setAuthorizationManagerFactory(this.authorizationManagerFactory);\n\t\tassertThat(this.handler.getAuthorizationManagerFactory()).isSameAs(this.authorizationManagerFactory);\n\t}\n\n\t@Configuration\n\tstatic class TestConfiguration {\n\n\t\t@Bean\n\t\tInteger number10() {\n\t\t\treturn 10;\n\t\t}\n\n\t\t@Bean\n\t\tInteger number20() {\n\t\t\treturn 20;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/access/expression/ExpressionUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\nclass ExpressionUtilsTests {\n\n\t@Test\n\tvoid evaluateAsBooleanWhenExpressionNullThenFalse() {\n\t\tSpelExpressionParser parser = new SpelExpressionParser();\n\t\tExpression expression = parser.parseExpression(\"null\");\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(this);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ExpressionUtils.evaluateAsBoolean(expression, context));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/access/expression/SecurityExpressionRootTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.SingleResultAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\npublic class SecurityExpressionRootTests {\n\n\tstatic final Authentication JOE = new TestingAuthenticationToken(\"joe\", \"pass\", \"ROLE_A\", \"ROLE_B\");\n\n\tSecurityExpressionRoot root;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.root = new SecurityExpressionRoot(JOE) {\n\t\t};\n\t}\n\n\t@Test\n\tpublic void denyAllIsFalsePermitAllTrue() {\n\t\tassertThat(this.root.denyAll()).isFalse();\n\t\tassertThat(this.root.denyAll).isFalse();\n\t\tassertThat(this.root.permitAll()).isTrue();\n\t\tassertThat(this.root.permitAll).isTrue();\n\t}\n\n\t@Test\n\tpublic void rememberMeIsCorrectlyDetected() {\n\t\tAuthenticationTrustResolver atr = mock(AuthenticationTrustResolver.class);\n\t\tthis.root.setTrustResolver(atr);\n\t\tgiven(atr.isRememberMe(JOE)).willReturn(true);\n\t\tassertThat(this.root.isRememberMe()).isTrue();\n\t\tassertThat(this.root.isFullyAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void roleHierarchySupportIsCorrectlyUsedInEvaluatingRoles() {\n\t\tthis.root.setRoleHierarchy((authorities) -> AuthorityUtils.createAuthorityList(\"ROLE_C\"));\n\t\tassertThat(this.root.hasRole(\"C\")).isTrue();\n\t\tassertThat(this.root.hasAuthority(\"ROLE_C\")).isTrue();\n\t\tassertThat(this.root.hasRole(\"A\")).isFalse();\n\t\tassertThat(this.root.hasRole(\"B\")).isFalse();\n\t\tassertThat(this.root.hasAnyRole(\"C\", \"A\", \"B\")).isTrue();\n\t\tassertThat(this.root.hasAnyAuthority(\"ROLE_C\", \"ROLE_A\", \"ROLE_B\")).isTrue();\n\t\tassertThat(this.root.hasAnyRole(\"A\", \"B\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasRoleAddsDefaultPrefix() {\n\t\tassertThat(this.root.hasRole(\"A\")).isTrue();\n\t\tassertThat(this.root.hasRole(\"NO\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasRoleEmptyPrefixDoesNotAddsDefaultPrefix() {\n\t\tthis.root.setDefaultRolePrefix(\"\");\n\t\tassertThat(this.root.hasRole(\"A\")).isFalse();\n\t\tassertThat(this.root.hasRole(\"ROLE_A\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasRoleNullPrefixDoesNotAddsDefaultPrefix() {\n\t\tthis.root.setDefaultRolePrefix(null);\n\t\tassertThat(this.root.hasRole(\"A\")).isFalse();\n\t\tassertThat(this.root.hasRole(\"ROLE_A\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasRoleDoesNotAddDefaultPrefixForAlreadyPrefixedRoles() {\n\t\tSecurityExpressionRoot root = new SecurityExpressionRoot(JOE) {\n\t\t};\n\t\tassertThat(root.hasRole(\"ROLE_A\")).isTrue();\n\t\tassertThat(root.hasRole(\"ROLE_NO\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleAddsDefaultPrefix() {\n\t\tassertThat(this.root.hasAnyRole(\"NO\", \"A\")).isTrue();\n\t\tassertThat(this.root.hasAnyRole(\"NO\", \"NOT\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleDoesNotAddDefaultPrefixForAlreadyPrefixedRoles() {\n\t\tassertThat(this.root.hasAnyRole(\"ROLE_NO\", \"ROLE_A\")).isTrue();\n\t\tassertThat(this.root.hasAnyRole(\"ROLE_NO\", \"ROLE_NOT\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleEmptyPrefixDoesNotAddsDefaultPrefix() {\n\t\tthis.root.setDefaultRolePrefix(\"\");\n\t\tassertThat(this.root.hasRole(\"A\")).isFalse();\n\t\tassertThat(this.root.hasRole(\"ROLE_A\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleNullPrefixDoesNotAddsDefaultPrefix() {\n\t\tthis.root.setDefaultRolePrefix(null);\n\t\tassertThat(this.root.hasAnyRole(\"A\")).isFalse();\n\t\tassertThat(this.root.hasAnyRole(\"ROLE_A\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAllRoles() {\n\t\tassertThat(this.root.hasAllRoles(\"A\")).isTrue();\n\t\tassertThat(this.root.hasAllRoles(\"A\", \"B\")).isTrue();\n\t\tassertThat(this.root.hasAllRoles(\"NO\")).isFalse();\n\t\tassertThat(this.root.hasAllRoles(\"A\", \"NO\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAuthorityDoesNotAddDefaultPrefix() {\n\t\tassertThat(this.root.hasAuthority(\"A\")).isFalse();\n\t\tassertThat(this.root.hasAnyAuthority(\"NO\", \"A\")).isFalse();\n\t\tassertThat(this.root.hasAnyAuthority(\"ROLE_A\", \"NOT\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAllAuthorities() {\n\t\tassertThat(this.root.hasAllAuthorities(\"ROLE_A\")).isTrue();\n\t\tassertThat(this.root.hasAllAuthorities(\"ROLE_A\", \"ROLE_B\")).isTrue();\n\t\tassertThat(this.root.hasAllAuthorities(\"ROLE_NO\")).isFalse();\n\t\tassertThat(this.root.hasAllAuthorities(\"ROLE_A\", \"ROLE_NO\")).isFalse();\n\t}\n\n\t@Test\n\tvoid isAuthenticatedWhenAuthenticatedNullThenException() {\n\t\tthis.root = new SecurityExpressionRoot((Authentication) null) {\n\t\t};\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.root.isAuthenticated());\n\t}\n\n\t@Test\n\tvoid isAuthenticatedWhenTrustResolverFalseThenFalse() {\n\t\tAuthenticationTrustResolver atr = mock(AuthenticationTrustResolver.class);\n\t\tgiven(atr.isAuthenticated(JOE)).willReturn(false);\n\t\tthis.root.setTrustResolver(atr);\n\t\tassertThat(this.root.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tvoid isAuthenticatedWhenTrustResolverTrueThenTrue() {\n\t\tAuthenticationTrustResolver atr = mock(AuthenticationTrustResolver.class);\n\t\tgiven(atr.isAuthenticated(JOE)).willReturn(true);\n\t\tthis.root.setTrustResolver(atr);\n\t\tassertThat(this.root.isAuthenticated()).isTrue();\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void hasAuthorityDelegatesToAuthorizationManagerFactoryHasAuthority() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.hasAuthority(\"CUSTOM_AUTHORITY\")).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.hasAuthority(\"CUSTOM_AUTHORITY\")).isFalse();\n\t\tverify(factory).hasAuthority(\"CUSTOM_AUTHORITY\");\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void hasAnyAuthorityDelegatesToAuthorizationManagerFactoryHasAnyAuthority() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.hasAnyAuthority(\"CUSTOM_AUTHORITY\")).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.hasAnyAuthority(\"CUSTOM_AUTHORITY\")).isFalse();\n\t\tverify(factory).hasAnyAuthority(\"CUSTOM_AUTHORITY\");\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void hasAllAuthoritiesDelegatesToAuthorizationManagerFactoryHasAllAuthorities() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.hasAllAuthorities(\"A\", \"B\")).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.hasAllAuthorities(\"A\", \"B\")).isFalse();\n\t\tverify(factory).hasAllAuthorities(\"A\", \"B\");\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void hasRoleDelegatesToAuthorizationManagerFactoryHasRole() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.hasRole(\"CUSTOM_ROLE\")).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.hasRole(\"CUSTOM_ROLE\")).isFalse();\n\t\tverify(factory).hasRole(\"CUSTOM_ROLE\");\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void hasAnyRoleDelegatesToAuthorizationManagerFactoryHasAnyRole() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.hasAnyRole(\"A\", \"B\")).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.hasAnyRole(\"A\", \"B\")).isFalse();\n\t\tverify(factory).hasAnyRole(\"A\", \"B\");\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void hasAllRolesDelegatesToAuthorizationManagerFactoryHasAllRoles() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.hasAllRoles(\"A\", \"B\")).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.hasAllRoles(\"A\", \"B\")).isFalse();\n\t\tverify(factory).hasAllRoles(\"A\", \"B\");\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void permitAllDelegatesToAuthorizationManagerFactoryPermitAll() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.permitAll()).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.permitAll()).isFalse();\n\t\tverify(factory).permitAll();\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void denyAllDelegatesToAuthorizationManagerFactoryDenyAll() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.denyAll()).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.denyAll()).isFalse();\n\t\tverify(factory).denyAll();\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void isAnonymousDelegatesToAuthorizationManagerFactoryAnonymous() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.anonymous()).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.isAnonymous()).isFalse();\n\t\tverify(factory).anonymous();\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void isAuthenticatedDelegatesToAuthorizationManagerFactoryAuthenticated() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.authenticated()).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.isAuthenticated()).isFalse();\n\t\tverify(factory).authenticated();\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void isRememberMeDelegatesToAuthorizationManagerFactoryRememberMe() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.rememberMe()).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.isRememberMe()).isFalse();\n\t\tverify(factory).rememberMe();\n\t}\n\n\t// gh-18486\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void isFullyAuthenticatedDelegatesToAuthorizationManagerFactoryFullyAuthenticated() {\n\t\tAuthorizationManagerFactory<Object> factory = mock(AuthorizationManagerFactory.class);\n\t\tAuthorizationManager<Object> manager = SingleResultAuthorizationManager.denyAll();\n\t\tgiven(factory.fullyAuthenticated()).willReturn(manager);\n\t\tthis.root.setAuthorizationManagerFactory(factory);\n\t\tassertThat(this.root.isFullyAuthenticated()).isFalse();\n\t\tverify(factory).fullyAuthenticated();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/access/hierarchicalroles/HierarchicalRolesTestHelper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.commons.collections.CollectionUtils;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Test helper class for the hierarchical roles tests.\n *\n * @author Michael Mayr\n */\npublic abstract class HierarchicalRolesTestHelper {\n\n\tpublic static boolean containTheSameGrantedAuthorities(Collection<? extends GrantedAuthority> authorities1,\n\t\t\tCollection<? extends GrantedAuthority> authorities2) {\n\t\tif (authorities1 == null && authorities2 == null) {\n\t\t\treturn true;\n\t\t}\n\t\tif (authorities1 == null || authorities2 == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn CollectionUtils.isEqualCollection(authorities1, authorities2);\n\t}\n\n\tpublic static boolean containTheSameGrantedAuthoritiesCompareByAuthorityString(\n\t\t\tCollection<? extends GrantedAuthority> authorities1, Collection<? extends GrantedAuthority> authorities2) {\n\t\tif (authorities1 == null && authorities2 == null) {\n\t\t\treturn true;\n\t\t}\n\t\tif (authorities1 == null || authorities2 == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn CollectionUtils.isEqualCollection(toCollectionOfAuthorityStrings(authorities1),\n\t\t\t\ttoCollectionOfAuthorityStrings(authorities2));\n\t}\n\n\tpublic static List<String> toCollectionOfAuthorityStrings(Collection<? extends GrantedAuthority> authorities) {\n\t\tif (authorities == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<String> result = new ArrayList<>(authorities.size());\n\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\tresult.add(authority.getAuthority());\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static List<GrantedAuthority> createAuthorityList(final String... roles) {\n\t\tList<GrantedAuthority> authorities = new ArrayList<>(roles.length);\n\t\tfor (final String role : roles) {\n\t\t\t// Use non SimpleGrantedAuthority (SEC-863)\n\t\t\tauthorities.add((GrantedAuthority) () -> role);\n\t\t}\n\t\treturn authorities;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyAuthoritiesMapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\npublic class RoleHierarchyAuthoritiesMapperTests {\n\n\t@Test\n\tpublic void expectedAuthoritiesAreReturned() {\n\t\tRoleHierarchyImpl rh = RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_C\");\n\t\tRoleHierarchyAuthoritiesMapper mapper = new RoleHierarchyAuthoritiesMapper(rh);\n\t\tCollection<? extends GrantedAuthority> authorities = mapper\n\t\t\t.mapAuthorities(AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_D\"));\n\t\tassertThat(authorities).hasSize(4);\n\t\tmapper = new RoleHierarchyAuthoritiesMapper(new NullRoleHierarchy());\n\t\tauthorities = mapper.mapAuthorities(AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_D\"));\n\t\tassertThat(authorities).hasSize(2);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImplTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Tests for {@link RoleHierarchyImpl}.\n *\n * @author Michael Mayr\n */\npublic class RoleHierarchyImplTests {\n\n\t@Test\n\tpublic void testRoleHierarchyWithNullOrEmptyAuthorities() {\n\t\tList<GrantedAuthority> authorities0 = null;\n\t\tList<GrantedAuthority> authorities1 = new ArrayList<>();\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\");\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0)).isNotNull();\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities0)).isEmpty();\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1)).isNotNull();\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(authorities1)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void testSimpleRoleHierarchy() {\n\t\tList<GrantedAuthority> authorities0 = AuthorityUtils.createAuthorityList(\"ROLE_0\");\n\t\tList<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\");\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0))\n\t\t\t.isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))\n\t\t\t.isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities2), authorities2))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void testTransitiveRoleHierarchies() {\n\t\tList<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\", \"ROLE_C\");\n\t\tList<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\", \"ROLE_C\",\n\t\t\t\t\"ROLE_D\");\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_C\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))\n\t\t\t.isTrue();\n\t\troleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_C\\nROLE_C > ROLE_D\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities3))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void testComplexRoleHierarchy() {\n\t\tList<GrantedAuthority> authoritiesInput1 = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> authoritiesOutput1 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\", \"ROLE_C\",\n\t\t\t\t\"ROLE_D\");\n\t\tList<GrantedAuthority> authoritiesInput2 = AuthorityUtils.createAuthorityList(\"ROLE_B\");\n\t\tList<GrantedAuthority> authoritiesOutput2 = AuthorityUtils.createAuthorityList(\"ROLE_B\", \"ROLE_D\");\n\t\tList<GrantedAuthority> authoritiesInput3 = AuthorityUtils.createAuthorityList(\"ROLE_C\");\n\t\tList<GrantedAuthority> authoritiesOutput3 = AuthorityUtils.createAuthorityList(\"ROLE_C\", \"ROLE_D\");\n\t\tList<GrantedAuthority> authoritiesInput4 = AuthorityUtils.createAuthorityList(\"ROLE_D\");\n\t\tList<GrantedAuthority> authoritiesOutput4 = AuthorityUtils.createAuthorityList(\"ROLE_D\");\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl\n\t\t\t.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_A > ROLE_C\\nROLE_C > ROLE_D\\nROLE_B > ROLE_D\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput1), authoritiesOutput1))\n\t\t\t.isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput2), authoritiesOutput2))\n\t\t\t.isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput3), authoritiesOutput3))\n\t\t\t.isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authoritiesInput4), authoritiesOutput4))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void testCyclesInRoleHierarchy() {\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class)\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_A\"));\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class)\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_A\"));\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class)\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_C\\nROLE_C > ROLE_A\"));\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class).isThrownBy(() -> RoleHierarchyImpl\n\t\t\t.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_C\\nROLE_C > ROLE_E\\nROLE_E > ROLE_D\\nROLE_D > ROLE_B\"));\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class)\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy(\"ROLE_C > ROLE_B\\nROLE_B > ROLE_A\\nROLE_A > ROLE_B\"));\n\t}\n\n\t@Test\n\tpublic void testNoCyclesInRoleHierarchy() {\n\t\tassertThatNoException().isThrownBy(() -> RoleHierarchyImpl\n\t\t\t.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_A > ROLE_C\\nROLE_C > ROLE_D\\nROLE_B > ROLE_D\"));\n\t}\n\n\t// SEC-863\n\t@Test\n\tpublic void testSimpleRoleHierarchyWithCustomGrantedAuthorityImplementation() {\n\t\tList<GrantedAuthority> authorities0 = HierarchicalRolesTestHelper.createAuthorityList(\"ROLE_0\");\n\t\tList<GrantedAuthority> authorities1 = HierarchicalRolesTestHelper.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> authorities2 = HierarchicalRolesTestHelper.createAuthorityList(\"ROLE_A\", \"ROLE_B\");\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities0), authorities0))\n\t\t\t.isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))\n\t\t\t.isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities2), authorities2))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void testWhitespaceRoleHierarchies() {\n\t\tList<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList(\"ROLE A\");\n\t\tList<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList(\"ROLE A\", \"ROLE B\", \"ROLE>C\");\n\t\tList<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList(\"ROLE A\", \"ROLE B\", \"ROLE>C\",\n\t\t\t\t\"ROLE D\");\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"ROLE A > ROLE B\\nROLE B > ROLE>C\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities2))\n\t\t\t.isTrue();\n\t\troleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"ROLE A > ROLE B\\nROLE B > ROLE>C\\nROLE>C > ROLE D\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(\n\t\t\t\troleHierarchyImpl.getReachableGrantedAuthorities(authorities1), authorities3))\n\t\t\t.isTrue();\n\t}\n\n\t// gh-6954\n\t@Test\n\tpublic void testJavadoc() {\n\t\tList<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\",\n\t\t\t\t\"ROLE_AUTHENTICATED\", \"ROLE_UNAUTHENTICATED\");\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\n\t\t\t\t\"ROLE_A > ROLE_B\\n\" + \"ROLE_B > ROLE_AUTHENTICATED\\n\" + \"ROLE_AUTHENTICATED > ROLE_UNAUTHENTICATED\");\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))\n\t\t\t.containsExactlyInAnyOrderElementsOf(allAuthorities);\n\t}\n\n\t// gh-6954\n\t@Test\n\tpublic void testInterfaceJavadoc() {\n\t\tList<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_HIGHEST\");\n\t\tList<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_HIGHEST\", \"ROLE_HIGHER\",\n\t\t\t\t\"ROLE_LOW\", \"ROLE_LOWER\");\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl\n\t\t\t.fromHierarchy(\"ROLE_HIGHEST > ROLE_HIGHER\\n\" + \"ROLE_HIGHER > ROLE_LOW\\n\" + \"ROLE_LOW > ROLE_LOWER\");\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))\n\t\t\t.containsExactlyInAnyOrderElementsOf(allAuthorities);\n\t}\n\n\t// gh-6954\n\t@Test\n\tpublic void singleLineLargeHierarchy() {\n\t\tList<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_HIGHEST\");\n\t\tList<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_HIGHEST\", \"ROLE_HIGHER\",\n\t\t\t\t\"ROLE_LOW\", \"ROLE_LOWER\");\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl\n\t\t\t.fromHierarchy(\"ROLE_HIGHEST > ROLE_HIGHER > ROLE_LOW > ROLE_LOWER\");\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))\n\t\t\t.containsExactlyInAnyOrderElementsOf(allAuthorities);\n\t}\n\n\t@Test\n\tpublic void testFromHierarchyWithTextBlock() {\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.fromHierarchy(\"\"\"\n\t\t\t\tROLE_A > ROLE_B\n\t\t\t\tROLE_B > ROLE_C\n\t\t\t\tROLE_B > ROLE_D\n\t\t\t\t\"\"\");\n\t\tList<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\", \"ROLE_C\",\n\t\t\t\t\"ROLE_D\");\n\n\t\tassertThat(roleHierarchyImpl).isNotNull();\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))\n\t\t\t.containsExactlyInAnyOrderElementsOf(allAuthorities);\n\t}\n\n\t@Test\n\tpublic void testFromHierarchyNoCycles() {\n\t\tassertThatNoException().isThrownBy(() -> RoleHierarchyImpl\n\t\t\t.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_A > ROLE_C\\nROLE_C > ROLE_D\\nROLE_B > ROLE_D\"));\n\t}\n\n\t@Test\n\tpublic void testFromHierarchyCycles() {\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class)\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_A\"));\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class)\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_A\"));\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class)\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_C\\nROLE_C > ROLE_A\"));\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class).isThrownBy(() -> RoleHierarchyImpl\n\t\t\t.fromHierarchy(\"ROLE_A > ROLE_B\\nROLE_B > ROLE_C\\nROLE_C > ROLE_E\\nROLE_E > ROLE_D\\nROLE_D > ROLE_B\"));\n\t\tassertThatExceptionOfType(CycleInRoleHierarchyException.class)\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.fromHierarchy(\"ROLE_C > ROLE_B\\nROLE_B > ROLE_A\\nROLE_A > ROLE_B\"));\n\t}\n\n\t@Test\n\tpublic void testBuilderWithDefaultRolePrefix() {\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.withDefaultRolePrefix()\n\t\t\t.role(\"A\")\n\t\t\t.implies(\"B\")\n\t\t\t.role(\"B\")\n\t\t\t.implies(\"C\", \"D\")\n\t\t\t.build();\n\t\tList<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\", \"ROLE_C\",\n\t\t\t\t\"ROLE_D\");\n\n\t\tassertThat(roleHierarchyImpl).isNotNull();\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))\n\t\t\t.containsExactlyInAnyOrderElementsOf(allAuthorities);\n\t}\n\n\t@Test\n\tpublic void testBuilderWithRepeatedRoleBuilder() {\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.withDefaultRolePrefix()\n\t\t\t.role(\"A\")\n\t\t\t.implies(\"B\")\n\t\t\t.role(\"A\") // Adding more implied roles to the existing role 'A'\n\t\t\t.implies(\"C\", \"D\")\n\t\t\t.build();\n\n\t\tList<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\", \"ROLE_C\",\n\t\t\t\t\"ROLE_D\");\n\n\t\tassertThat(roleHierarchyImpl).isNotNull();\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))\n\t\t\t.containsExactlyInAnyOrderElementsOf(allAuthorities);\n\t}\n\n\t@Test\n\tpublic void testBuilderWithRolePrefix() {\n\t\tRoleHierarchyImpl roleHierarchyImpl = RoleHierarchyImpl.withRolePrefix(\"CUSTOM_PREFIX_\")\n\t\t\t.role(\"A\")\n\t\t\t.implies(\"B\")\n\t\t\t.build();\n\t\tList<GrantedAuthority> flatAuthorities = AuthorityUtils.createAuthorityList(\"CUSTOM_PREFIX_A\");\n\t\tList<GrantedAuthority> allAuthorities = AuthorityUtils.createAuthorityList(\"CUSTOM_PREFIX_A\",\n\t\t\t\t\"CUSTOM_PREFIX_B\");\n\n\t\tassertThat(roleHierarchyImpl).isNotNull();\n\t\tassertThat(roleHierarchyImpl.getReachableGrantedAuthorities(flatAuthorities))\n\t\t\t.containsExactlyInAnyOrderElementsOf(allAuthorities);\n\t}\n\n\t@Test\n\tpublic void testBuilderThrowIllegalArgumentExceptionWhenPrefixRoleNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> RoleHierarchyImpl.withRolePrefix(null));\n\t}\n\n\t@Test\n\tpublic void testBuilderThrowIllegalArgumentExceptionWhenRoleEmpty() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> RoleHierarchyImpl.withDefaultRolePrefix().role(\"\"));\n\t}\n\n\t@Test\n\tpublic void testBuilderThrowIllegalArgumentExceptionWhenRoleNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> RoleHierarchyImpl.withDefaultRolePrefix().role(null));\n\t}\n\n\t@Test\n\tpublic void testBuilderThrowIllegalArgumentExceptionWhenImpliedRolesNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.withDefaultRolePrefix().role(\"A\").implies((String) null));\n\t}\n\n\t@Test\n\tpublic void testBuilderThrowIllegalArgumentExceptionWhenImpliedRolesEmpty() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RoleHierarchyImpl.withDefaultRolePrefix().role(\"A\").implies());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link RoleHierarchyUtils}.\n *\n * @author Joe Grandja\n */\npublic class RoleHierarchyUtilsTests {\n\n\tprivate static final String EOL = System.lineSeparator();\n\n\t@Test\n\tpublic void roleHierarchyFromMapWhenMapValidThenConvertsCorrectly() {\n\t\t// @formatter:off\n\t\tString expectedRoleHierarchy = \"ROLE_A > ROLE_B\" + EOL +\n\t\t\t\t\"ROLE_A > ROLE_C\" + EOL +\n\t\t\t\t\"ROLE_B > ROLE_D\" + EOL +\n\t\t\t\t\"ROLE_C > ROLE_D\" + EOL;\n\t\t// @formatter:on\n\t\tMap<String, List<String>> roleHierarchyMap = new TreeMap<>();\n\t\troleHierarchyMap.put(\"ROLE_A\", Arrays.asList(\"ROLE_B\", \"ROLE_C\"));\n\t\troleHierarchyMap.put(\"ROLE_B\", Arrays.asList(\"ROLE_D\"));\n\t\troleHierarchyMap.put(\"ROLE_C\", Arrays.asList(\"ROLE_D\"));\n\t\tString roleHierarchy = RoleHierarchyUtils.roleHierarchyFromMap(roleHierarchyMap);\n\t\tassertThat(roleHierarchy).isEqualTo(expectedRoleHierarchy);\n\t}\n\n\t@Test\n\tpublic void roleHierarchyFromMapWhenMapNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> RoleHierarchyUtils.roleHierarchyFromMap(null));\n\t}\n\n\t@Test\n\tpublic void roleHierarchyFromMapWhenMapEmptyThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RoleHierarchyUtils.roleHierarchyFromMap(Collections.<String, List<String>>emptyMap()));\n\t}\n\n\t@Test\n\tpublic void roleHierarchyFromMapWhenRoleNullThenThrowsIllegalArgumentException() {\n\t\tMap<String, List<String>> roleHierarchyMap = new HashMap<>();\n\t\troleHierarchyMap.put(null, Arrays.asList(\"ROLE_B\", \"ROLE_C\"));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RoleHierarchyUtils.roleHierarchyFromMap(roleHierarchyMap));\n\t}\n\n\t@Test\n\tpublic void roleHierarchyFromMapWhenRoleEmptyThenThrowsIllegalArgumentException() {\n\t\tMap<String, List<String>> roleHierarchyMap = new HashMap<>();\n\t\troleHierarchyMap.put(\"\", Arrays.asList(\"ROLE_B\", \"ROLE_C\"));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RoleHierarchyUtils.roleHierarchyFromMap(roleHierarchyMap));\n\t}\n\n\t@Test\n\tpublic void roleHierarchyFromMapWhenImpliedRolesNullThenThrowsIllegalArgumentException() {\n\t\tMap<String, List<String>> roleHierarchyMap = new HashMap<>();\n\t\troleHierarchyMap.put(\"ROLE_A\", null);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RoleHierarchyUtils.roleHierarchyFromMap(roleHierarchyMap));\n\t}\n\n\t@Test\n\tpublic void roleHierarchyFromMapWhenImpliedRolesEmptyThenThrowsIllegalArgumentException() {\n\t\tMap<String, List<String>> roleHierarchyMap = new HashMap<>();\n\t\troleHierarchyMap.put(\"ROLE_A\", Collections.<String>emptyList());\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RoleHierarchyUtils.roleHierarchyFromMap(roleHierarchyMap));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/access/hierarchicalroles/TestHelperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.hierarchicalroles;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.commons.collections.CollectionUtils;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link HierarchicalRolesTestHelper}.\n *\n * @author Michael Mayr\n */\npublic class TestHelperTests {\n\n\t@Test\n\tpublic void testContainTheSameGrantedAuthorities() {\n\t\tList<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\");\n\t\tList<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList(\"ROLE_B\", \"ROLE_A\");\n\t\tList<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_C\");\n\t\tList<GrantedAuthority> authorities4 = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> authorities5 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_A\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, null)).isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities1)).isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities2)).isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities2, authorities1)).isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, authorities1)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, null)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities3)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities3, authorities1)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities4)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities1)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities5)).isFalse();\n\t}\n\n\t// SEC-863\n\t@Test\n\tpublic void testToListOfAuthorityStrings() {\n\t\tCollection<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\");\n\t\tCollection<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList(\"ROLE_B\", \"ROLE_A\");\n\t\tCollection<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_C\");\n\t\tCollection<GrantedAuthority> authorities4 = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tCollection<GrantedAuthority> authorities5 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_A\");\n\t\tList<String> authoritiesStrings1 = new ArrayList<>();\n\t\tauthoritiesStrings1.add(\"ROLE_A\");\n\t\tauthoritiesStrings1.add(\"ROLE_B\");\n\t\tList<String> authoritiesStrings2 = new ArrayList<>();\n\t\tauthoritiesStrings2.add(\"ROLE_B\");\n\t\tauthoritiesStrings2.add(\"ROLE_A\");\n\t\tList<String> authoritiesStrings3 = new ArrayList<>();\n\t\tauthoritiesStrings3.add(\"ROLE_A\");\n\t\tauthoritiesStrings3.add(\"ROLE_C\");\n\t\tList<String> authoritiesStrings4 = new ArrayList<>();\n\t\tauthoritiesStrings4.add(\"ROLE_A\");\n\t\tList<String> authoritiesStrings5 = new ArrayList<>();\n\t\tauthoritiesStrings5.add(\"ROLE_A\");\n\t\tauthoritiesStrings5.add(\"ROLE_A\");\n\t\tassertThat(CollectionUtils.isEqualCollection(\n\t\t\t\tHierarchicalRolesTestHelper.toCollectionOfAuthorityStrings(authorities1), authoritiesStrings1))\n\t\t\t.isTrue();\n\t\tassertThat(CollectionUtils.isEqualCollection(\n\t\t\t\tHierarchicalRolesTestHelper.toCollectionOfAuthorityStrings(authorities2), authoritiesStrings2))\n\t\t\t.isTrue();\n\t\tassertThat(CollectionUtils.isEqualCollection(\n\t\t\t\tHierarchicalRolesTestHelper.toCollectionOfAuthorityStrings(authorities3), authoritiesStrings3))\n\t\t\t.isTrue();\n\t\tassertThat(CollectionUtils.isEqualCollection(\n\t\t\t\tHierarchicalRolesTestHelper.toCollectionOfAuthorityStrings(authorities4), authoritiesStrings4))\n\t\t\t.isTrue();\n\t\tassertThat(CollectionUtils.isEqualCollection(\n\t\t\t\tHierarchicalRolesTestHelper.toCollectionOfAuthorityStrings(authorities5), authoritiesStrings5))\n\t\t\t.isTrue();\n\t}\n\n\t// SEC-863\n\t@Test\n\tpublic void testContainTheSameGrantedAuthoritiesCompareByAuthorityString() {\n\t\tList<GrantedAuthority> authorities1 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\");\n\t\tList<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList(\"ROLE_B\", \"ROLE_A\");\n\t\tList<GrantedAuthority> authorities3 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_C\");\n\t\tList<GrantedAuthority> authorities4 = AuthorityUtils.createAuthorityList(\"ROLE_A\");\n\t\tList<GrantedAuthority> authorities5 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_A\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, null)).isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities1)).isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities2)).isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities2, authorities1)).isTrue();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(null, authorities1)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, null)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities3)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities3, authorities1)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities1, authorities4)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities1)).isFalse();\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthorities(authorities4, authorities5)).isFalse();\n\t}\n\n\t// SEC-863\n\t@Test\n\tpublic void testContainTheSameGrantedAuthoritiesCompareByAuthorityStringWithAuthorityLists() {\n\t\tList<GrantedAuthority> authorities1 = HierarchicalRolesTestHelper.createAuthorityList(\"ROLE_A\", \"ROLE_B\");\n\t\tList<GrantedAuthority> authorities2 = AuthorityUtils.createAuthorityList(\"ROLE_A\", \"ROLE_B\");\n\t\tassertThat(HierarchicalRolesTestHelper.containTheSameGrantedAuthoritiesCompareByAuthorityString(authorities1,\n\t\t\t\tauthorities2))\n\t\t\t.isTrue();\n\t}\n\n\t// SEC-863\n\t@Test\n\tpublic void testCreateAuthorityList() {\n\t\tList<GrantedAuthority> authorities1 = HierarchicalRolesTestHelper.createAuthorityList(\"ROLE_A\");\n\t\tassertThat(authorities1).hasSize(1);\n\t\tassertThat(authorities1.get(0).getAuthority()).isEqualTo(\"ROLE_A\");\n\t\tList<GrantedAuthority> authorities2 = HierarchicalRolesTestHelper.createAuthorityList(\"ROLE_A\", \"ROLE_C\");\n\t\tassertThat(authorities2).hasSize(2);\n\t\tassertThat(authorities2.get(0).getAuthority()).isEqualTo(\"ROLE_A\");\n\t\tassertThat(authorities2.get(1).getAuthority()).isEqualTo(\"ROLE_C\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/aot/hint/AuthorizeReturnObjectCoreHintsRegistrarTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.spy;\n\n/**\n * Tests for {@link AuthorizeReturnObjectCoreHintsRegistrar}\n */\npublic class AuthorizeReturnObjectCoreHintsRegistrarTests {\n\n\tprivate final AuthorizationProxyFactory proxyFactory = spy(AuthorizationAdvisorProxyFactory.withDefaults());\n\n\tprivate final AuthorizeReturnObjectCoreHintsRegistrar registrar = new AuthorizeReturnObjectCoreHintsRegistrar(\n\t\t\tthis.proxyFactory);\n\n\t@Test\n\tpublic void registerHintsWhenUsingAuthorizeReturnObjectThenRegisters() {\n\t\tGenericApplicationContext context = new GenericApplicationContext();\n\t\tcontext.registerBean(MyService.class, MyService::new);\n\t\tcontext.registerBean(MyInterface.class, MyImplementation::new);\n\t\tcontext.refresh();\n\t\tRuntimeHints hints = new RuntimeHints();\n\t\tthis.registrar.registerHints(hints, context.getBeanFactory());\n\t\tassertThat(hints.reflection().typeHints().map((hint) -> hint.getType().getName())).containsOnly(\n\t\t\t\tcglibClassName(MyObject.class), cglibClassName(MySubObject.class), MyObject.class.getName(),\n\t\t\t\tMySubObject.class.getName());\n\t\tassertThat(hints.proxies()\n\t\t\t.jdkProxyHints()\n\t\t\t.flatMap((hint) -> hint.getProxiedInterfaces().stream())\n\t\t\t.map(TypeReference::getName)).contains(MyInterface.class.getName());\n\t}\n\n\tprivate static String cglibClassName(Class<?> clazz) {\n\t\treturn clazz.getName() + \"$$SpringCGLIB$$0\";\n\t}\n\n\tpublic static class MyService {\n\n\t\t@AuthorizeReturnObject\n\t\tMyObject get() {\n\t\t\treturn new MyObject();\n\t\t}\n\n\t}\n\n\tpublic interface MyInterface {\n\n\t\tMyObject get();\n\n\t}\n\n\t@AuthorizeReturnObject\n\tpublic static class MyImplementation implements MyInterface {\n\n\t\t@Override\n\t\tpublic MyObject get() {\n\t\t\treturn new MyObject();\n\t\t}\n\n\t}\n\n\tpublic static class MyObject {\n\n\t\t@AuthorizeReturnObject\n\t\tpublic MySubObject get() {\n\t\t\treturn new MySubObject();\n\t\t}\n\n\t\t@AuthorizeReturnObject\n\t\tpublic MyInterface getInterface() {\n\t\t\treturn new MyImplementation();\n\t\t}\n\n\t}\n\n\tpublic static class MySubObject {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/aot/hint/AuthorizeReturnObjectHintsRegistrarTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.spy;\n\n/**\n * Tests for {@link AuthorizeReturnObjectHintsRegistrar}\n */\npublic class AuthorizeReturnObjectHintsRegistrarTests {\n\n\tprivate final AuthorizationProxyFactory proxyFactory = spy(AuthorizationAdvisorProxyFactory.withDefaults());\n\n\t@Test\n\tpublic void registerHintsWhenSpecifiedThenRegisters() {\n\t\tAuthorizeReturnObjectHintsRegistrar registrar = new AuthorizeReturnObjectHintsRegistrar(this.proxyFactory,\n\t\t\t\tMyObject.class, MyInterface.class);\n\t\tRuntimeHints hints = new RuntimeHints();\n\t\tregistrar.registerHints(hints, null);\n\t\tassertThat(hints.reflection().typeHints().map((hint) -> hint.getType().getName()))\n\t\t\t.containsOnly(cglibClassName(MyObject.class), MyObject.class.getName());\n\t\tassertThat(hints.proxies()\n\t\t\t.jdkProxyHints()\n\t\t\t.flatMap((hint) -> hint.getProxiedInterfaces().stream())\n\t\t\t.map(TypeReference::getName)).contains(MyInterface.class.getName());\n\t}\n\n\tprivate static String cglibClassName(Class<?> clazz) {\n\t\treturn clazz.getName() + \"$$SpringCGLIB$$0\";\n\t}\n\n\tpublic interface MyInterface {\n\n\t\tMyObject get();\n\n\t}\n\n\tpublic static class MyObject {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/aot/hint/CoreSecurityRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.security.access.expression.SecurityExpressionOperations;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.ProviderNotFoundException;\nimport org.springframework.security.authentication.event.AbstractAuthenticationEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureCredentialsExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureDisabledEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureLockedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProviderNotFoundEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProxyUntrustedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureServiceExceptionEvent;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link CoreSecurityRuntimeHints}\n *\n * @author Marcus Da Coregio\n */\nclass CoreSecurityRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@Test\n\tvoid springSecurityMessagesBundleHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.resource().forBundle(\"org.springframework.security.messages\"))\n\t\t\t.accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid securityExpressionOperationsHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(SecurityExpressionOperations.class)\n\t\t\t.withMemberCategories(MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid securityExpressionRootHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(SecurityExpressionRoot.class)\n\t\t\t.withMemberCategories(MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.hints);\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getAuthenticationEvents\")\n\tvoid exceptionEventsHasHints(Class<? extends AbstractAuthenticationEvent> event) {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(event)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid methodSecurityExpressionRootHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.access.expression.method.MethodSecurityExpressionRoot\"))\n\t\t\t.withMemberCategories(MemberCategory.INVOKE_PUBLIC_METHODS)).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid abstractAuthenticationTokenHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(AbstractAuthenticationToken.class)\n\t\t\t.withMemberCategories(MemberCategory.INVOKE_PUBLIC_METHODS)).accepts(this.hints);\n\t}\n\n\tprivate static Stream<Class<? extends AbstractAuthenticationEvent>> getAuthenticationEvents() {\n\t\treturn Stream.of(AuthenticationFailureBadCredentialsEvent.class,\n\t\t\t\tAuthenticationFailureCredentialsExpiredEvent.class, AuthenticationFailureDisabledEvent.class,\n\t\t\t\tAuthenticationFailureExpiredEvent.class, AuthenticationFailureLockedEvent.class,\n\t\t\t\tAuthenticationFailureProviderNotFoundEvent.class, AuthenticationFailureProxyUntrustedEvent.class,\n\t\t\t\tAuthenticationFailureServiceExceptionEvent.class);\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getAuthenticationExceptions\")\n\tvoid exceptionHasHints(Class<? extends AuthenticationException> exception) {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(exception)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n\tprivate static Stream<Class<? extends AuthenticationException>> getAuthenticationExceptions() {\n\t\treturn Stream.of(AuthenticationServiceException.class, AccountExpiredException.class,\n\t\t\t\tBadCredentialsException.class, CredentialsExpiredException.class, DisabledException.class,\n\t\t\t\tLockedException.class, UsernameNotFoundException.class, ProviderNotFoundException.class);\n\t}\n\n\t@Test\n\tvoid defaultJdbcSchemaFileHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.resource()\n\t\t\t.forResource(\"org/springframework/security/core/userdetails/jdbc/users.ddl\")).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid securityContextHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(SecurityContextImpl.class)\n\t\t\t.withMemberCategories(MemberCategory.INVOKE_PUBLIC_METHODS)).accepts(this.hints);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/aot/hint/OneTimeTokenRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OneTimeTokenRuntimeHints}\n *\n * @author Max Batischev\n */\nclass OneTimeTokenRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getOneTimeTokensSqlFiles\")\n\tvoid oneTimeTokensSqlFilesHasHints(String schemaFile) {\n\t\tassertThat(RuntimeHintsPredicates.resource().forResource(schemaFile)).accepts(this.hints);\n\t}\n\n\tprivate static Stream<String> getOneTimeTokensSqlFiles() {\n\t\treturn Stream.of(\"org/springframework/security/core/ott/jdbc/one-time-tokens-schema.sql\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/aot/hint/PrePostAuthorizeHintsRegistrarTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.generate.GenerationContext;\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.aot.test.generate.TestGenerationContext;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\nclass PrePostAuthorizeHintsRegistrarTests {\n\n\tprivate final PrePostAuthorizeHintsRegistrar registrar = new PrePostAuthorizeHintsRegistrar();\n\n\tprivate final GenerationContext generationContext = new TestGenerationContext();\n\n\t@Test\n\tvoid registerHintsWhenPreAuthorizeOnTypeThenHintsRegistered() {\n\t\tprocess(Authz.class, PreAuthorizeOnClass.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenPostAuthorizeOnTypeThenHintsRegistered() {\n\t\tprocess(Authz.class, PostAuthorizeOnClass.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenPreAuthorizeOnMethodsThenHintsRegistered() {\n\t\tprocess(Authz.class, Foo.class, PreAuthorizeOnMethods.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Foo.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenPostAuthorizeOnMethodsThenHintsRegistered() {\n\t\tprocess(Authz.class, Foo.class, PostAuthorizeOnMethods.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Foo.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenPreAuthorizeExpressionWithMultipleBeansThenRegisterHintsForAllBeans() {\n\t\tprocess(Authz.class, Foo.class, PreAuthorizeMultipleBeans.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Foo.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenPostAuthorizeExpressionWithMultipleBeansThenRegisterHintsForAllBeans() {\n\t\tprocess(Authz.class, Foo.class, PostAuthorizeMultipleBeans.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Foo.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenPreAuthorizeOnTypeAndMethodThenRegisterHintsForBoth() {\n\t\tprocess(Authz.class, Foo.class, PreAuthorizeOnTypeAndMethod.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Foo.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenPostAuthorizeOnTypeAndMethodThenRegisterHintsForBoth() {\n\t\tprocess(Authz.class, Foo.class, PostAuthorizeOnTypeAndMethod.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Foo.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenSecurityAnnotationsInsideAuthorizeReturnObjectOnMethodThenRegisterHints() {\n\t\tprocess(AccountAuthz.class, Authz.class, PreAuthorizeInsideAuthorizeReturnObjectOnMethod.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(AccountAuthz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenSecurityAnnotationsInsideAuthorizeReturnObjectOnClassThenRegisterHints() {\n\t\tprocess(AccountAuthz.class, Authz.class, PreAuthorizeInsideAuthorizeReturnObjectOnClass.class);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(AccountAuthz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(Authz.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS))\n\t\t\t.accepts(this.generationContext.getRuntimeHints());\n\t}\n\n\t@Test\n\tvoid registerHintsWhenCyclicDependencyThenNoStackOverflowException() {\n\t\tassertThatNoException().isThrownBy(() -> process(AService.class));\n\t}\n\n\tprivate void process(Class<?>... beanClasses) {\n\t\tDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();\n\t\tfor (Class<?> beanClass : beanClasses) {\n\t\t\tbeanFactory.registerBeanDefinition(beanClass.getSimpleName().toLowerCase(),\n\t\t\t\t\tnew RootBeanDefinition(beanClass));\n\t\t}\n\t\tthis.registrar.registerHints(this.generationContext.getRuntimeHints(), beanFactory);\n\t}\n\n\t@PreAuthorize(\"@authz.check()\")\n\tstatic class PreAuthorizeOnClass {\n\n\t}\n\n\t@PostAuthorize(\"@authz.check()\")\n\tstatic class PostAuthorizeOnClass {\n\n\t}\n\n\tstatic class PreAuthorizeOnMethods {\n\n\t\t@PreAuthorize(\"@authz.check()\")\n\t\tvoid method1() {\n\t\t}\n\n\t\t@PreAuthorize(\"@foo.bar()\")\n\t\tvoid method2() {\n\t\t}\n\n\t}\n\n\tstatic class PostAuthorizeOnMethods {\n\n\t\t@PostAuthorize(\"@authz.check()\")\n\t\tvoid method1() {\n\t\t}\n\n\t\t@PostAuthorize(\"@foo.bar()\")\n\t\tvoid method2() {\n\t\t}\n\n\t}\n\n\tstatic class PreAuthorizeMultipleBeans {\n\n\t\t@PreAuthorize(\"@authz.check() ? true : @foo.bar()\")\n\t\tvoid method1() {\n\t\t}\n\n\t}\n\n\tstatic class PostAuthorizeMultipleBeans {\n\n\t\t@PostAuthorize(\"@authz.check() ? true : @foo.bar()\")\n\t\tvoid method1() {\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"@authz.check()\")\n\tstatic class PreAuthorizeOnTypeAndMethod {\n\n\t\t@PreAuthorize(\"@foo.bar()\")\n\t\tvoid method1() {\n\t\t}\n\n\t}\n\n\t@PostAuthorize(\"@authz.check()\")\n\tstatic class PostAuthorizeOnTypeAndMethod {\n\n\t\t@PostAuthorize(\"@foo.bar()\")\n\t\tvoid method1() {\n\t\t}\n\n\t}\n\n\tstatic class PreAuthorizeInsideAuthorizeReturnObjectOnMethod {\n\n\t\t@AuthorizeReturnObject\n\t\tAccount getAccount() {\n\t\t\treturn new Account(\"1234\");\n\t\t}\n\n\t}\n\n\t@AuthorizeReturnObject\n\tstatic class PreAuthorizeInsideAuthorizeReturnObjectOnClass {\n\n\t\tAccount getAccount() {\n\t\t\treturn new Account(\"1234\");\n\t\t}\n\n\t}\n\n\tstatic class Authz {\n\n\t\tboolean check() {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\tstatic class Foo {\n\n\t\tboolean bar() {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\tstatic class AccountAuthz {\n\n\t\tboolean canViewAccountNumber() {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\tstatic class Account {\n\n\t\tprivate final String accountNumber;\n\n\t\tAccount(String accountNumber) {\n\t\t\tthis.accountNumber = accountNumber;\n\t\t}\n\n\t\t@PreAuthorize(\"@accountauthz.canViewAccountNumber()\")\n\t\tString getAccountNumber() {\n\t\t\treturn this.accountNumber;\n\t\t}\n\n\t\t@AuthorizeReturnObject\n\t\tUser getUser() {\n\t\t\treturn new User(\"John Doe\");\n\t\t}\n\n\t}\n\n\tstatic class User {\n\n\t\tprivate final String fullName;\n\n\t\tUser(String fullName) {\n\t\t\tthis.fullName = fullName;\n\t\t}\n\n\t\t@PostAuthorize(\"@authz.check()\")\n\t\tString getFullName() {\n\t\t\treturn this.fullName;\n\t\t}\n\n\t}\n\n\tstatic class AService {\n\n\t\t@AuthorizeReturnObject\n\t\tA getA() {\n\t\t\treturn new A();\n\t\t}\n\n\t}\n\n\tstatic class A {\n\n\t\t@AuthorizeReturnObject\n\t\tB getB() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tstatic class B {\n\n\t\t@AuthorizeReturnObject\n\t\tA getA() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/aot/hint/SecurityHintsAotProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.aot.hint;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.generate.GenerationContext;\nimport org.springframework.aot.test.generate.TestGenerationContext;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Role;\nimport org.springframework.context.aot.ApplicationContextAotGenerator;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link SecurityHintsAotProcessor}\n */\npublic class SecurityHintsAotProcessorTests {\n\n\t@Test\n\tvoid applyToWhenSecurityHintsRegistrarThenInvokes() {\n\t\tGenerationContext generationContext = new TestGenerationContext();\n\t\tAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();\n\t\tcontext.register(AppConfig.class);\n\t\tApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();\n\t\tgenerator.processAheadOfTime(context, generationContext);\n\t\tverify(context.getBean(SecurityHintsRegistrar.class)).registerHints(any(), any());\n\t}\n\n\t@Configuration\n\tstatic class AppConfig {\n\n\t\t@Bean\n\t\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\t\tstatic SecurityHintsRegistrar hints() {\n\t\t\treturn mock(SecurityHintsRegistrar.class);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/AbstractAuthenticationBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass AbstractAuthenticationBuilderTests {\n\n\t@Test\n\tvoid applyWhenAuthoritiesThenAdds() {\n\t\tTestingAuthenticationToken factorOne = new TestingAuthenticationToken(\"user\", \"pass\", \"FACTOR_ONE\");\n\t\tTestingAuthenticationToken factorTwo = new TestingAuthenticationToken(\"user\", \"pass\", \"FACTOR_TWO\");\n\t\tTestAbstractAuthenticationBuilder builder = new TestAbstractAuthenticationBuilder(factorOne);\n\t\tAuthentication result = builder.authorities((a) -> a.addAll(factorTwo.getAuthorities())).build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n\tprivate static final class TestAbstractAuthenticationBuilder\n\t\t\textends TestingAuthenticationToken.Builder<TestAbstractAuthenticationBuilder> {\n\n\t\tprivate TestAbstractAuthenticationBuilder(TestingAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t}\n\n\t\t@Override\n\t\tpublic TestingAuthenticationToken build() {\n\t\t\treturn new TestingAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/AbstractAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link AbstractAuthenticationToken}.\n *\n * @author Ben Alex\n */\npublic class AbstractAuthenticationTokenTests {\n\n\tprivate List<GrantedAuthority> authorities = null;\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tthis.authorities = AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\");\n\t}\n\n\t@Test\n\tpublic void testAuthoritiesAreImmutable() {\n\t\tMockAuthenticationImpl token = new MockAuthenticationImpl(\"Test\", \"Password\", this.authorities);\n\t\tList<GrantedAuthority> gotAuthorities = (List<GrantedAuthority>) token.getAuthorities();\n\t\tassertThat(gotAuthorities).isNotSameAs(this.authorities);\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class)\n\t\t\t.isThrownBy(() -> gotAuthorities.set(0, new SimpleGrantedAuthority(\"ROLE_SUPER_USER\")));\n\t}\n\n\t@Test\n\tpublic void testGetters() {\n\t\tMockAuthenticationImpl token = new MockAuthenticationImpl(\"Test\", \"Password\", this.authorities);\n\t\tassertThat(token.getPrincipal()).isEqualTo(\"Test\");\n\t\tassertThat(token.getCredentials()).isEqualTo(\"Password\");\n\t\tassertThat(token.getName()).isEqualTo(\"Test\");\n\t}\n\n\t@Test\n\tpublic void testHashCode() {\n\t\tMockAuthenticationImpl token1 = new MockAuthenticationImpl(\"Test\", \"Password\", this.authorities);\n\t\tMockAuthenticationImpl token2 = new MockAuthenticationImpl(\"Test\", \"Password\", this.authorities);\n\t\tMockAuthenticationImpl token3 = new MockAuthenticationImpl(null, null, AuthorityUtils.NO_AUTHORITIES);\n\t\tassertThat(token2.hashCode()).isEqualTo(token1.hashCode());\n\t\tassertThat(token1.hashCode() != token3.hashCode()).isTrue();\n\t\ttoken2.setAuthenticated(true);\n\t\tassertThat(token1.hashCode() != token2.hashCode()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testObjectsEquals() {\n\t\tMockAuthenticationImpl token1 = new MockAuthenticationImpl(\"Test\", \"Password\", this.authorities);\n\t\tMockAuthenticationImpl token2 = new MockAuthenticationImpl(\"Test\", \"Password\", this.authorities);\n\t\tassertThat(token2).isEqualTo(token1);\n\t\tMockAuthenticationImpl token3 = new MockAuthenticationImpl(\"Test\", \"Password_Changed\", this.authorities);\n\t\tassertThat(!token1.equals(token3)).isTrue();\n\t\tMockAuthenticationImpl token4 = new MockAuthenticationImpl(\"Test_Changed\", \"Password\", this.authorities);\n\t\tassertThat(!token1.equals(token4)).isTrue();\n\t\tMockAuthenticationImpl token5 = new MockAuthenticationImpl(\"Test\", \"Password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO_CHANGED\"));\n\t\tassertThat(!token1.equals(token5)).isTrue();\n\t\tMockAuthenticationImpl token6 = new MockAuthenticationImpl(\"Test\", \"Password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\"));\n\t\tassertThat(!token1.equals(token6)).isTrue();\n\t\tMockAuthenticationImpl token7 = new MockAuthenticationImpl(\"Test\", \"Password\", null);\n\t\tassertThat(!token1.equals(token7)).isTrue();\n\t\tassertThat(!token7.equals(token1)).isTrue();\n\t\tassertThat(!token1.equals(100)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testSetAuthenticated() {\n\t\tMockAuthenticationImpl token = new MockAuthenticationImpl(\"Test\", \"Password\", this.authorities);\n\t\tassertThat(!token.isAuthenticated()).isTrue();\n\t\ttoken.setAuthenticated(true);\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testToStringWithAuthorities() {\n\t\tMockAuthenticationImpl token = new MockAuthenticationImpl(\"Test\", \"Password\", this.authorities);\n\t\tassertThat(token.toString().lastIndexOf(\"ROLE_TWO\") != -1).isTrue();\n\t}\n\n\t@Test\n\tpublic void testToStringWithNullAuthorities() {\n\t\tMockAuthenticationImpl token = new MockAuthenticationImpl(\"Test\", \"Password\", null);\n\t\tassertThat(token.toString().lastIndexOf(\"Granted Authorities=[]\") != -1).isTrue();\n\t}\n\n\t@Test\n\tpublic void testGetNameWhenPrincipalIsAuthenticatedPrincipal() {\n\t\tString principalName = \"test\";\n\t\tAuthenticatedPrincipal principal = mock(AuthenticatedPrincipal.class);\n\t\tgiven(principal.getName()).willReturn(principalName);\n\t\tMockAuthenticationImpl token = new MockAuthenticationImpl(principal, \"Password\", this.authorities);\n\t\tassertThat(token.getName()).isEqualTo(principalName);\n\t\tverify(principal, times(1)).getName();\n\t}\n\n\tprivate class MockAuthenticationImpl extends AbstractAuthenticationToken {\n\n\t\tprivate Object credentials;\n\n\t\tprivate Object principal;\n\n\t\tMockAuthenticationImpl(Object principal, Object credentials, List<GrantedAuthority> authorities) {\n\t\t\tsuper(authorities);\n\t\t\tthis.principal = principal;\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getCredentials() {\n\t\t\treturn this.credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getPrincipal() {\n\t\t\treturn this.principal;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/AuthenticationTrustResolverImplTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests\n * {@link org.springframework.security.authentication.AuthenticationTrustResolverImpl}.\n *\n * @author Ben Alex\n */\npublic class AuthenticationTrustResolverImplTests {\n\n\t@Test\n\tpublic void testCorrectOperationIsAnonymous() {\n\t\tAuthenticationTrustResolverImpl trustResolver = new AuthenticationTrustResolverImpl();\n\t\tassertThat(trustResolver.isAnonymous(\n\t\t\t\tnew AnonymousAuthenticationToken(\"ignored\", \"ignored\", AuthorityUtils.createAuthorityList(\"ignored\"))))\n\t\t\t.isTrue();\n\t\tassertThat(trustResolver.isAnonymous(\n\t\t\t\tnew TestingAuthenticationToken(\"ignored\", \"ignored\", AuthorityUtils.createAuthorityList(\"ignored\"))))\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void testCorrectOperationIsRememberMe() {\n\t\tAuthenticationTrustResolverImpl trustResolver = new AuthenticationTrustResolverImpl();\n\t\tassertThat(trustResolver.isRememberMe(\n\t\t\t\tnew RememberMeAuthenticationToken(\"ignored\", \"ignored\", AuthorityUtils.createAuthorityList(\"ignored\"))))\n\t\t\t.isTrue();\n\t\tassertThat(trustResolver.isAnonymous(\n\t\t\t\tnew TestingAuthenticationToken(\"ignored\", \"ignored\", AuthorityUtils.createAuthorityList(\"ignored\"))))\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tAuthenticationTrustResolverImpl trustResolver = new AuthenticationTrustResolverImpl();\n\t\tassertThat(AnonymousAuthenticationToken.class).isEqualTo(trustResolver.getAnonymousClass());\n\t\ttrustResolver.setAnonymousClass(TestingAuthenticationToken.class);\n\t\tassertThat(trustResolver.getAnonymousClass()).isEqualTo(TestingAuthenticationToken.class);\n\t\tassertThat(RememberMeAuthenticationToken.class).isEqualTo(trustResolver.getRememberMeClass());\n\t\ttrustResolver.setRememberMeClass(TestingAuthenticationToken.class);\n\t\tassertThat(trustResolver.getRememberMeClass()).isEqualTo(TestingAuthenticationToken.class);\n\t}\n\n\t@Test\n\tvoid isAuthenticatedWhenAuthenticationNullThenFalse() {\n\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\t\tAuthentication authentication = null;\n\t\tassertThat(trustResolver.isAuthenticated(authentication)).isFalse();\n\t}\n\n\t@Test\n\tvoid isAuthenticatedWhenAuthenticationNotAuthenticatedThenFalse() {\n\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tassertThat(trustResolver.isAuthenticated(authentication)).isFalse();\n\t}\n\n\t@Test\n\tvoid isAuthenticatedWhenAnonymousThenFalse() {\n\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\t\tAnonymousAuthenticationToken authentication = new AnonymousAuthenticationToken(\"key\", \"principal\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tassertThat(trustResolver.isAuthenticated(authentication)).isFalse();\n\t}\n\n\t@Test\n\tvoid isFullyAuthenticatedWhenAuthenticationNullThenFalse() {\n\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\t\tAuthentication authentication = null;\n\t\tassertThat(trustResolver.isFullyAuthenticated(authentication)).isFalse();\n\t}\n\n\t@Test\n\tvoid isFullyAuthenticatedWhenAuthenticationNotAuthenticatedThenFalse() {\n\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tassertThat(trustResolver.isFullyAuthenticated(authentication)).isFalse();\n\t}\n\n\t@Test\n\tvoid isFullyAuthenticatedWhenAnonymousThenFalse() {\n\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\t\tAnonymousAuthenticationToken authentication = new AnonymousAuthenticationToken(\"key\", \"principal\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tassertThat(trustResolver.isFullyAuthenticated(authentication)).isFalse();\n\t}\n\n\t@Test\n\tvoid isFullyAuthenticatedWhenRememberMeThenFalse() {\n\t\tAuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\t\tRememberMeAuthenticationToken authentication = new RememberMeAuthenticationToken(\"key\", \"user\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tassertThat(trustResolver.isFullyAuthenticated(authentication)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/DefaultAuthenticationEventPublisherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Properties;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureCredentialsExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureDisabledEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureExpiredEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureLockedEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureProviderNotFoundEvent;\nimport org.springframework.security.authentication.event.AuthenticationFailureServiceExceptionEvent;\nimport org.springframework.security.authentication.event.AuthenticationSuccessEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.isA;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Luke Taylor\n */\npublic class DefaultAuthenticationEventPublisherTests {\n\n\tDefaultAuthenticationEventPublisher publisher;\n\n\t@Test\n\tpublic void expectedDefaultMappingsAreSatisfied() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tApplicationEventPublisher appPublisher = mock(ApplicationEventPublisher.class);\n\t\tthis.publisher.setApplicationEventPublisher(appPublisher);\n\t\tAuthentication a = mock(Authentication.class);\n\t\tException cause = new Exception();\n\t\tObject extraInfo = new Object();\n\t\tthis.publisher.publishAuthenticationFailure(new BadCredentialsException(\"\"), a);\n\t\tthis.publisher.publishAuthenticationFailure(new BadCredentialsException(\"\", cause), a);\n\t\tverify(appPublisher, times(2)).publishEvent(isA(AuthenticationFailureBadCredentialsEvent.class));\n\t\treset(appPublisher);\n\t\tthis.publisher.publishAuthenticationFailure(new UsernameNotFoundException(\"\"), a);\n\t\tthis.publisher.publishAuthenticationFailure(new UsernameNotFoundException(\"\", cause), a);\n\t\tthis.publisher.publishAuthenticationFailure(new AccountExpiredException(\"\"), a);\n\t\tthis.publisher.publishAuthenticationFailure(new AccountExpiredException(\"\", cause), a);\n\t\tthis.publisher.publishAuthenticationFailure(new ProviderNotFoundException(\"\"), a);\n\t\tthis.publisher.publishAuthenticationFailure(new DisabledException(\"\"), a);\n\t\tthis.publisher.publishAuthenticationFailure(new DisabledException(\"\", cause), a);\n\t\tthis.publisher.publishAuthenticationFailure(new LockedException(\"\"), a);\n\t\tthis.publisher.publishAuthenticationFailure(new LockedException(\"\", cause), a);\n\t\tthis.publisher.publishAuthenticationFailure(new AuthenticationServiceException(\"\"), a);\n\t\tthis.publisher.publishAuthenticationFailure(new AuthenticationServiceException(\"\", cause), a);\n\t\tthis.publisher.publishAuthenticationFailure(new CredentialsExpiredException(\"\"), a);\n\t\tthis.publisher.publishAuthenticationFailure(new CredentialsExpiredException(\"\", cause), a);\n\t\tverify(appPublisher, times(2)).publishEvent(isA(AuthenticationFailureBadCredentialsEvent.class));\n\t\tverify(appPublisher, times(2)).publishEvent(isA(AuthenticationFailureExpiredEvent.class));\n\t\tverify(appPublisher).publishEvent(isA(AuthenticationFailureProviderNotFoundEvent.class));\n\t\tverify(appPublisher, times(2)).publishEvent(isA(AuthenticationFailureDisabledEvent.class));\n\t\tverify(appPublisher, times(2)).publishEvent(isA(AuthenticationFailureLockedEvent.class));\n\t\tverify(appPublisher, times(2)).publishEvent(isA(AuthenticationFailureServiceExceptionEvent.class));\n\t\tverify(appPublisher, times(2)).publishEvent(isA(AuthenticationFailureCredentialsExpiredEvent.class));\n\t\tverifyNoMoreInteractions(appPublisher);\n\t}\n\n\t@Test\n\tpublic void authenticationSuccessIsPublished() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tApplicationEventPublisher appPublisher = mock(ApplicationEventPublisher.class);\n\t\tthis.publisher.setApplicationEventPublisher(appPublisher);\n\t\tthis.publisher.publishAuthenticationSuccess(mock(Authentication.class));\n\t\tverify(appPublisher).publishEvent(isA(AuthenticationSuccessEvent.class));\n\t\tthis.publisher.setApplicationEventPublisher(null);\n\t\t// Should be ignored with null app publisher\n\t\tthis.publisher.publishAuthenticationSuccess(mock(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void additionalExceptionMappingsAreSupported() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tProperties p = new Properties();\n\t\tp.put(MockAuthenticationException.class.getName(), AuthenticationFailureDisabledEvent.class.getName());\n\t\tthis.publisher.setAdditionalExceptionMappings(p);\n\t\tApplicationEventPublisher appPublisher = mock(ApplicationEventPublisher.class);\n\t\tthis.publisher.setApplicationEventPublisher(appPublisher);\n\t\tthis.publisher.publishAuthenticationFailure(new MockAuthenticationException(\"test\"),\n\t\t\t\tmock(Authentication.class));\n\t\tverify(appPublisher).publishEvent(isA(AuthenticationFailureDisabledEvent.class));\n\t}\n\n\t@Test\n\tpublic void missingEventClassExceptionCausesException() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tProperties p = new Properties();\n\t\tp.put(MockAuthenticationException.class.getName(), \"NoSuchClass\");\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t.isThrownBy(() -> this.publisher.setAdditionalExceptionMappings(p));\n\t}\n\n\t@Test\n\tpublic void unknownFailureExceptionIsIgnored() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tProperties p = new Properties();\n\t\tp.put(MockAuthenticationException.class.getName(), AuthenticationFailureDisabledEvent.class.getName());\n\t\tthis.publisher.setAdditionalExceptionMappings(p);\n\t\tApplicationEventPublisher appPublisher = mock(ApplicationEventPublisher.class);\n\t\tthis.publisher.setApplicationEventPublisher(appPublisher);\n\t\tthis.publisher.publishAuthenticationFailure(new AuthenticationException(\"\") {\n\t\t}, mock(Authentication.class));\n\t\tverifyNoMoreInteractions(appPublisher);\n\t}\n\n\t@Test\n\tpublic void emptyMapCausesException() {\n\t\tMap<Class<? extends AuthenticationException>, Class<? extends AbstractAuthenticationFailureEvent>> mappings = new HashMap<>();\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.publisher.setAdditionalExceptionMappings(mappings));\n\t}\n\n\t@Test\n\tpublic void missingExceptionClassCausesException() {\n\t\tMap<Class<? extends AuthenticationException>, Class<? extends AbstractAuthenticationFailureEvent>> mappings = new HashMap<>();\n\t\tmappings.put(null, AuthenticationFailureLockedEvent.class);\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.publisher.setAdditionalExceptionMappings(mappings));\n\t}\n\n\t@Test\n\tpublic void missingEventClassAsMapValueCausesException() {\n\t\tMap<Class<? extends AuthenticationException>, Class<? extends AbstractAuthenticationFailureEvent>> mappings = new HashMap<>();\n\t\tmappings.put(LockedException.class, null);\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.publisher.setAdditionalExceptionMappings(mappings));\n\t}\n\n\t@Test\n\tpublic void additionalExceptionMappingsUsingMapAreSupported() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tMap<Class<? extends AuthenticationException>, Class<? extends AbstractAuthenticationFailureEvent>> mappings = new HashMap<>();\n\t\tmappings.put(MockAuthenticationException.class, AuthenticationFailureDisabledEvent.class);\n\t\tthis.publisher.setAdditionalExceptionMappings(mappings);\n\t\tApplicationEventPublisher appPublisher = mock(ApplicationEventPublisher.class);\n\t\tthis.publisher.setApplicationEventPublisher(appPublisher);\n\t\tthis.publisher.publishAuthenticationFailure(new MockAuthenticationException(\"test\"),\n\t\t\t\tmock(Authentication.class));\n\t\tverify(appPublisher).publishEvent(isA(AuthenticationFailureDisabledEvent.class));\n\t}\n\n\t@Test\n\tpublic void defaultAuthenticationFailureEventClassSetNullThen() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.publisher.setDefaultAuthenticationFailureEvent(null));\n\t}\n\n\t@Test\n\tpublic void defaultAuthenticationFailureEventIsPublished() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tthis.publisher.setDefaultAuthenticationFailureEvent(AuthenticationFailureBadCredentialsEvent.class);\n\t\tApplicationEventPublisher appPublisher = mock(ApplicationEventPublisher.class);\n\t\tthis.publisher.setApplicationEventPublisher(appPublisher);\n\t\tthis.publisher.publishAuthenticationFailure(new AuthenticationException(\"\") {\n\t\t}, mock(Authentication.class));\n\t\tverify(appPublisher).publishEvent(isA(AuthenticationFailureBadCredentialsEvent.class));\n\t}\n\n\t@Test\n\tpublic void defaultAuthenticationFailureEventMissingAppropriateConstructorThen() {\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher();\n\t\tassertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.publisher\n\t\t\t.setDefaultAuthenticationFailureEvent(AuthenticationFailureEventWithoutAppropriateConstructor.class));\n\t}\n\n\tprivate static final class AuthenticationFailureEventWithoutAppropriateConstructor\n\t\t\textends AbstractAuthenticationFailureEvent {\n\n\t\tAuthenticationFailureEventWithoutAppropriateConstructor(Authentication auth) {\n\t\t\tsuper(auth, new AuthenticationException(\"\") {\n\t\t\t});\n\t\t}\n\n\t}\n\n\tprivate static final class MockAuthenticationException extends AuthenticationException {\n\n\t\tMockAuthenticationException(String msg) {\n\t\t\tsuper(msg);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.time.Duration;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingReactiveAuthenticationManagerTests {\n\n\t@Mock\n\tReactiveAuthenticationManager delegate1;\n\n\t@Mock\n\tReactiveAuthenticationManager delegate2;\n\n\t@Mock\n\tAuthentication authentication;\n\n\t@Test\n\tpublic void authenticateWhenEmptyAndNotThenReturnsNotEmpty() {\n\t\tgiven(this.delegate1.authenticate(any())).willReturn(Mono.empty());\n\t\tgiven(this.delegate2.authenticate(any())).willReturn(Mono.just(this.authentication));\n\t\tDelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1,\n\t\t\t\tthis.delegate2);\n\t\tassertThat(manager.authenticate(this.authentication).block()).isEqualTo(this.authentication);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNotEmptyThenOtherDelegatesNotSubscribed() {\n\t\t// delay to try and force delegate2 to finish (i.e. make sure we didn't use\n\t\t// flatMap)\n\t\tgiven(this.delegate1.authenticate(any()))\n\t\t\t.willReturn(Mono.just(this.authentication).delayElement(Duration.ofMillis(100)));\n\t\tDelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1,\n\t\t\t\tthis.delegate2);\n\t\tStepVerifier.create(manager.authenticate(this.authentication)).expectNext(this.authentication).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenBadCredentialsThenDelegate2NotInvokedAndError() {\n\t\tgiven(this.delegate1.authenticate(any())).willReturn(Mono.error(new BadCredentialsException(\"Test\")));\n\t\tDelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1,\n\t\t\t\tthis.delegate2);\n\t\tStepVerifier.create(manager.authenticate(this.authentication))\n\t\t\t.expectError(BadCredentialsException.class)\n\t\t\t.verify();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenContinueOnErrorAndFirstBadCredentialsThenTriesSecond() {\n\t\tgiven(this.delegate1.authenticate(any())).willReturn(Mono.error(new BadCredentialsException(\"Test\")));\n\t\tgiven(this.delegate2.authenticate(any())).willReturn(Mono.just(this.authentication));\n\n\t\tDelegatingReactiveAuthenticationManager manager = managerWithContinueOnError();\n\n\t\tassertThat(manager.authenticate(this.authentication).block()).isEqualTo(this.authentication);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenContinueOnErrorAndBothDelegatesBadCredentialsThenError() {\n\t\tgiven(this.delegate1.authenticate(any())).willReturn(Mono.error(new BadCredentialsException(\"Test\")));\n\t\tgiven(this.delegate2.authenticate(any())).willReturn(Mono.error(new BadCredentialsException(\"Test\")));\n\n\t\tDelegatingReactiveAuthenticationManager manager = managerWithContinueOnError();\n\n\t\tStepVerifier.create(manager.authenticate(this.authentication))\n\t\t\t.expectError(BadCredentialsException.class)\n\t\t\t.verify();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenContinueOnErrorAndDelegate1NotEmptyThenReturnsNotEmpty() {\n\t\tgiven(this.delegate1.authenticate(any())).willReturn(Mono.just(this.authentication));\n\n\t\tDelegatingReactiveAuthenticationManager manager = managerWithContinueOnError();\n\n\t\tassertThat(manager.authenticate(this.authentication).block()).isEqualTo(this.authentication);\n\t}\n\n\t@Test\n\tvoid whenAccountStatusExceptionThenAuthenticationRequestIsIncluded() {\n\t\tAuthenticationException expected = new LockedException(\"\");\n\t\tgiven(this.delegate1.authenticate(any())).willReturn(Mono.error(expected));\n\t\tReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1);\n\t\tStepVerifier.create(manager.authenticate(this.authentication)).expectError(LockedException.class).verify();\n\t\tassertThat(expected.getAuthenticationRequest()).isEqualTo(this.authentication);\n\t}\n\n\tprivate DelegatingReactiveAuthenticationManager managerWithContinueOnError() {\n\t\tDelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1,\n\t\t\t\tthis.delegate2);\n\t\tmanager.setContinueOnError(true);\n\n\t\treturn manager;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/NonBuildableAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\npublic class NonBuildableAuthenticationToken extends TestingAuthenticationToken {\n\n\tpublic NonBuildableAuthenticationToken(String user, String password, String... authorities) {\n\t\tsuper(user, password, authorities);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ObservationAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link ObservationAuthenticationManager}\n */\npublic class ObservationAuthenticationManagerTests {\n\n\tprivate ObservationRegistry registry;\n\n\tprivate ObservationHandler<Observation.Context> handler;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate ObservationAuthenticationManager tested;\n\n\tprivate final Authentication token = new TestingAuthenticationToken(\"user\", \"pass\");\n\n\tprivate final Authentication authentication = new TestingAuthenticationToken(\"user\", \"pass\", \"app\");\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.handler = mock(ObservationHandler.class);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(this.handler);\n\t\tthis.registry = registry;\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.tested = new ObservationAuthenticationManager(this.registry, this.authenticationManager);\n\t}\n\n\t@Test\n\tvoid authenticateWhenDefaultsThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(this.authentication);\n\t\tthis.tested.authenticate(this.token);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthenticationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isNull();\n\t\tassertThat(captor.getValue()).isInstanceOf(AuthenticationObservationContext.class);\n\t\tAuthenticationObservationContext context = (AuthenticationObservationContext) captor.getValue();\n\t\tassertThat(context.getAuthenticationManagerClass()).isEqualTo(this.authenticationManager.getClass());\n\t\tassertThat(context.getAuthenticationRequest()).isEqualTo(this.token);\n\t\tassertThat(context.getAuthenticationResult()).isEqualTo(this.authentication);\n\n\t}\n\n\t@Test\n\tvoid authenticationWhenErrorsThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(BadCredentialsException.class);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.tested.authenticate(this.token));\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthenticationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isInstanceOf(AuthenticationException.class);\n\t\tassertThat(captor.getValue()).isInstanceOf(AuthenticationObservationContext.class);\n\t\tAuthenticationObservationContext context = (AuthenticationObservationContext) captor.getValue();\n\t\tassertThat(context.getAuthenticationManagerClass()).isEqualTo(this.authenticationManager.getClass());\n\t\tassertThat(context.getAuthenticationRequest()).isEqualTo(this.token);\n\t\tassertThat(context.getAuthenticationResult()).isNull();\n\t}\n\n\t@Test\n\tvoid setObservationConventionWhenNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.tested.setObservationConvention(null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ObservationReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link ObservationAuthenticationManager}\n */\npublic class ObservationReactiveAuthenticationManagerTests {\n\n\tprivate ObservationRegistry registry;\n\n\tprivate ObservationHandler<Observation.Context> handler;\n\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\tprivate ObservationReactiveAuthenticationManager tested;\n\n\tprivate final Authentication token = new TestingAuthenticationToken(\"user\", \"pass\");\n\n\tprivate final Authentication authentication = new TestingAuthenticationToken(\"user\", \"pass\", \"app\");\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.handler = mock(ObservationHandler.class);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(this.handler);\n\t\tthis.registry = registry;\n\t\tthis.authenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tthis.tested = new ObservationReactiveAuthenticationManager(this.registry, this.authenticationManager);\n\t}\n\n\t@Test\n\tvoid authenticateWhenDefaultsThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(Mono.just(this.authentication));\n\t\tthis.tested.authenticate(this.token).block();\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthenticationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isNull();\n\t\tassertThat(captor.getValue()).isInstanceOf(AuthenticationObservationContext.class);\n\t\tAuthenticationObservationContext context = (AuthenticationObservationContext) captor.getValue();\n\t\tassertThat(context.getAuthenticationManagerClass()).isEqualTo(this.authenticationManager.getClass());\n\t\tassertThat(context.getAuthenticationRequest()).isEqualTo(this.token);\n\t\tassertThat(context.getAuthenticationResult()).isEqualTo(this.authentication);\n\n\t}\n\n\t@Test\n\tvoid authenticationWhenErrorsThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.error(new BadCredentialsException(\"fail\")));\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> this.tested.authenticate(this.token).block());\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthenticationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isInstanceOf(AuthenticationException.class);\n\t\tassertThat(captor.getValue()).isInstanceOf(AuthenticationObservationContext.class);\n\t\tAuthenticationObservationContext context = (AuthenticationObservationContext) captor.getValue();\n\t\tassertThat(context.getAuthenticationManagerClass()).isEqualTo(this.authenticationManager.getClass());\n\t\tassertThat(context.getAuthenticationRequest()).isEqualTo(this.token);\n\t\tassertThat(context.getAuthenticationResult()).isNull();\n\t}\n\n\t@Test\n\tvoid setObservationConventionWhenNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.tested.setObservationConvention(null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ProviderManagerTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link ProviderManager}.\n *\n * @author Ben Alex\n */\npublic class ProviderManagerTests {\n\n\t@Test\n\tvoid authenticationFailsWithUnsupportedToken() {\n\t\tAuthentication token = new AbstractAuthenticationToken((Collection<? extends GrantedAuthority>) null) {\n\t\t\t@Override\n\t\t\tpublic Object getCredentials() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object getPrincipal() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t};\n\t\tProviderManager mgr = makeProviderManager();\n\t\tmgr.setMessageSource(mock(MessageSource.class));\n\t\tassertThatExceptionOfType(ProviderNotFoundException.class).isThrownBy(() -> mgr.authenticate(token));\n\t}\n\n\t@Test\n\tvoid credentialsAreClearedByDefault() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"Test\",\n\t\t\t\t\"Password\");\n\t\tProviderManager mgr = makeProviderManager();\n\t\tAuthentication result = mgr.authenticate(token);\n\t\tassertThat(result.getCredentials()).isNull();\n\t\tmgr.setEraseCredentialsAfterAuthentication(false);\n\t\ttoken = UsernamePasswordAuthenticationToken.unauthenticated(\"Test\", \"Password\");\n\t\tresult = mgr.authenticate(token);\n\t\tassertThat(result.getCredentials()).isNotNull();\n\t}\n\n\t@Test\n\tvoid authenticationSucceedsWithSupportedTokenAndReturnsExpectedObject() {\n\t\tAuthentication a = new TestingAuthenticationToken(\"user\", \"pass\", \"FACTOR\");\n\t\tProviderManager mgr = new ProviderManager(createProviderWhichReturns(a));\n\t\tAuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);\n\t\tmgr.setAuthenticationEventPublisher(publisher);\n\t\tAuthentication result = mgr.authenticate(a);\n\t\tassertThat(result.getPrincipal()).isEqualTo(a.getPrincipal());\n\t\tverify(publisher).publishAuthenticationSuccess(result);\n\t}\n\n\t@Test\n\tvoid authenticationSucceedsWhenFirstProviderReturnsNullButSecondAuthenticates() {\n\t\tAuthentication a = new TestingAuthenticationToken(\"user\", \"pass\", \"FACTOR\");\n\t\tProviderManager mgr = new ProviderManager(\n\t\t\t\tArrays.asList(createProviderWhichReturns(null), createProviderWhichReturns(a)));\n\t\tAuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);\n\t\tmgr.setAuthenticationEventPublisher(publisher);\n\t\tAuthentication result = mgr.authenticate(a);\n\t\tassertThat(result.getPrincipal()).isEqualTo(a.getPrincipal());\n\t\tverify(publisher).publishAuthenticationSuccess(result);\n\t}\n\n\t@Test\n\tvoid testStartupFailsIfProvidersNotSetAsList() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ProviderManager((List<AuthenticationProvider>) null));\n\t}\n\n\t@Test\n\tvoid testStartupFailsIfProvidersNotSetAsVarargs() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ProviderManager((AuthenticationProvider) null));\n\t}\n\n\t@Test\n\tvoid testStartupFailsIfProvidersContainNullElement() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ProviderManager(Arrays.asList(mock(AuthenticationProvider.class), null)));\n\t}\n\n\t// gh-8689\n\t@Test\n\tvoid constructorWhenUsingListOfThenNoException() {\n\t\tList<AuthenticationProvider> providers = spy(ArrayList.class);\n\t\t// List.of(null) in JDK 9 throws a NullPointerException\n\t\tgiven(providers.contains(eq(null))).willThrow(NullPointerException.class);\n\t\tproviders.add(mock(AuthenticationProvider.class));\n\t\tnew ProviderManager(providers);\n\t}\n\n\t@Test\n\tvoid detailsAreNotSetOnAuthenticationTokenIfAlreadySetByProvider() {\n\t\tObject requestDetails = \"(Request Details)\";\n\t\tfinal Object resultDetails = \"(Result Details)\";\n\t\t// A provider which sets the details object\n\t\tAuthenticationProvider provider = new AuthenticationProvider() {\n\t\t\t@Override\n\t\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\t\t((TestingAuthenticationToken) authentication).setDetails(resultDetails);\n\t\t\t\treturn authentication;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\tProviderManager authMgr = new ProviderManager(provider);\n\t\tTestingAuthenticationToken request = createAuthenticationToken();\n\t\trequest.setDetails(requestDetails);\n\t\tAuthentication result = authMgr.authenticate(request);\n\t\tassertThat(result.getDetails()).isEqualTo(resultDetails);\n\t}\n\n\t@Test\n\tvoid detailsAreSetOnAuthenticationTokenIfNotAlreadySetByProvider() {\n\t\tObject details = new Object();\n\t\tProviderManager authMgr = makeProviderManager();\n\t\tTestingAuthenticationToken request = createAuthenticationToken();\n\t\trequest.setDetails(details);\n\t\tAuthentication result = authMgr.authenticate(request);\n\t\tassertThat(result.getCredentials()).isNotNull();\n\t\tassertThat(result.getDetails()).isSameAs(details);\n\t}\n\n\t// gh-18027\n\t@Test\n\tvoid authenticationIsSameWhenDetailsSetAndAuthenticationToBuilderIsDefault() {\n\t\tAuthentication customAuthentication = new DefaultToBuilderAuthentication();\n\t\tAuthenticationProvider provider = mock(AuthenticationProvider.class);\n\t\tgiven(provider.supports(any())).willReturn(true);\n\t\tgiven(provider.authenticate(any())).willReturn(customAuthentication);\n\t\tTestingAuthenticationToken request = createAuthenticationToken();\n\t\trequest.setDetails(new Object());\n\t\tProviderManager authMgr = new ProviderManager(provider);\n\t\tAuthentication result = authMgr.authenticate(request);\n\t\tassertThat(result).isSameAs(customAuthentication);\n\t}\n\n\t@Test\n\tvoid authenticationExceptionIsIgnoredIfLaterProviderAuthenticates() {\n\t\tAuthentication result = new TestingAuthenticationToken(\"user\", \"pass\", \"FACTOR\");\n\t\tProviderManager mgr = new ProviderManager(\n\t\t\t\tcreateProviderWhichThrows(new BadCredentialsException(\"\", new Throwable())),\n\t\t\t\tcreateProviderWhichReturns(result));\n\t\tAuthentication request = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\tassertThat(mgr.authenticate(request).getPrincipal()).isEqualTo(result.getPrincipal());\n\t}\n\n\t@Test\n\tvoid authenticationExceptionIsRethrownIfNoLaterProviderAuthenticates() {\n\t\tProviderManager mgr = new ProviderManager(Arrays\n\t\t\t.asList(createProviderWhichThrows(new BadCredentialsException(\"\")), createProviderWhichReturns(null)));\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> mgr.authenticate(mock(Authentication.class)));\n\t}\n\n\t// SEC-546\n\t@Test\n\tvoid accountStatusExceptionPreventsCallsToSubsequentProviders() {\n\t\tAuthenticationProvider iThrowAccountStatusException = createProviderWhichThrows(new AccountStatusException(\"\") {\n\t\t});\n\t\tAuthenticationProvider otherProvider = mock(AuthenticationProvider.class);\n\t\tProviderManager authMgr = new ProviderManager(Arrays.asList(iThrowAccountStatusException, otherProvider));\n\t\tassertThatExceptionOfType(AccountStatusException.class)\n\t\t\t.isThrownBy(() -> authMgr.authenticate(mock(Authentication.class)));\n\t\tverifyNoInteractions(otherProvider);\n\t}\n\n\t@Test\n\tvoid parentAuthenticationIsUsedIfProvidersDontAuthenticate() {\n\t\tAuthenticationManager parent = mock(AuthenticationManager.class);\n\t\tAuthentication authReq = mock(Authentication.class);\n\t\tgiven(parent.authenticate(authReq)).willReturn(authReq);\n\t\tProviderManager mgr = new ProviderManager(List.of(mock(AuthenticationProvider.class)), parent);\n\t\tassertThat(mgr.authenticate(authReq)).isSameAs(authReq);\n\t}\n\n\t@Test\n\tvoid parentIsNotCalledIfAccountStatusExceptionIsThrown() {\n\t\tAuthenticationProvider iThrowAccountStatusException = createProviderWhichThrows(\n\t\t\t\tnew AccountStatusException(\"\", new Throwable()) {\n\t\t\t\t});\n\t\tAuthenticationManager parent = mock(AuthenticationManager.class);\n\t\tProviderManager mgr = new ProviderManager(List.of(iThrowAccountStatusException), parent);\n\t\tassertThatExceptionOfType(AccountStatusException.class)\n\t\t\t.isThrownBy(() -> mgr.authenticate(mock(Authentication.class)));\n\t\tverifyNoInteractions(parent);\n\t}\n\n\t@Test\n\tvoid providerNotFoundFromParentIsIgnored() {\n\t\tfinal Authentication authReq = mock(Authentication.class);\n\t\tAuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);\n\t\tAuthenticationManager parent = mock(AuthenticationManager.class);\n\t\tgiven(parent.authenticate(authReq)).willThrow(new ProviderNotFoundException(\"\"));\n\t\t// Set a provider that throws an exception - this is the exception we expect to be\n\t\t// propagated\n\t\tProviderManager mgr = new ProviderManager(List.of(createProviderWhichThrows(new BadCredentialsException(\"\"))),\n\t\t\t\tparent);\n\t\tmgr.setAuthenticationEventPublisher(publisher);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> mgr.authenticate(authReq))\n\t\t\t.satisfies((ex) -> verify(publisher).publishAuthenticationFailure(ex, authReq));\n\t}\n\n\t@Test\n\tvoid authenticationExceptionFromParentOverridesPreviousOnes() {\n\t\tAuthenticationManager parent = mock(AuthenticationManager.class);\n\t\tProviderManager mgr = new ProviderManager(List.of(createProviderWhichThrows(new BadCredentialsException(\"\"))),\n\t\t\t\tparent);\n\t\tAuthentication authReq = mock(Authentication.class);\n\t\tAuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);\n\t\tmgr.setAuthenticationEventPublisher(publisher);\n\t\t// Set a provider that throws an exception - this is the exception we expect to be\n\t\t// propagated\n\t\tBadCredentialsException expected = new BadCredentialsException(\"I'm the one from the parent\");\n\t\tgiven(parent.authenticate(authReq)).willThrow(expected);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> mgr.authenticate(authReq))\n\t\t\t.isSameAs(expected);\n\t}\n\n\t@Test\n\tvoid statusExceptionIsPublished() {\n\t\tAuthenticationManager parent = mock(AuthenticationManager.class);\n\t\tLockedException expected = new LockedException(\"\");\n\t\tProviderManager mgr = new ProviderManager(List.of(createProviderWhichThrows(expected)), parent);\n\t\tAuthentication authReq = mock(Authentication.class);\n\t\tAuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);\n\t\tmgr.setAuthenticationEventPublisher(publisher);\n\t\tassertThatExceptionOfType(LockedException.class).isThrownBy(() -> mgr.authenticate(authReq));\n\t\tverify(publisher).publishAuthenticationFailure(expected, authReq);\n\t}\n\n\t@Test\n\tvoid whenAccountStatusExceptionThenAuthenticationRequestIsIncluded() {\n\t\tAuthenticationException expected = new LockedException(\"\");\n\t\tProviderManager mgr = new ProviderManager(createProviderWhichThrows(expected));\n\t\tAuthentication authReq = mock(Authentication.class);\n\t\tassertThatExceptionOfType(LockedException.class).isThrownBy(() -> mgr.authenticate(authReq));\n\t\tassertThat(expected.getAuthenticationRequest()).isEqualTo(authReq);\n\t}\n\n\t@Test\n\tvoid whenInternalServiceAuthenticationExceptionThenAuthenticationRequestIsIncluded() {\n\t\tAuthenticationException expected = new InternalAuthenticationServiceException(\"\");\n\t\tProviderManager mgr = new ProviderManager(createProviderWhichThrows(expected));\n\t\tAuthentication authReq = mock(Authentication.class);\n\t\tassertThatExceptionOfType(InternalAuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> mgr.authenticate(authReq));\n\t\tassertThat(expected.getAuthenticationRequest()).isEqualTo(authReq);\n\t}\n\n\t@Test\n\tvoid whenAuthenticationExceptionThenAuthenticationRequestIsIncluded() {\n\t\tAuthenticationException expected = new BadCredentialsException(\"\");\n\t\tProviderManager mgr = new ProviderManager(createProviderWhichThrows(expected));\n\t\tAuthentication authReq = mock(Authentication.class);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> mgr.authenticate(authReq));\n\t\tassertThat(expected.getAuthenticationRequest()).isEqualTo(authReq);\n\t}\n\n\t// SEC-2367\n\t@Test\n\tvoid providerThrowsInternalAuthenticationServiceException() {\n\t\tInternalAuthenticationServiceException expected = new InternalAuthenticationServiceException(\"Expected\");\n\t\tProviderManager mgr = new ProviderManager(Arrays.asList(createProviderWhichThrows(expected),\n\t\t\t\tcreateProviderWhichThrows(new BadCredentialsException(\"Oops\"))), null);\n\t\tAuthentication authReq = mock(Authentication.class);\n\t\tassertThatExceptionOfType(InternalAuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> mgr.authenticate(authReq));\n\t}\n\n\t// gh-6281\n\t@Test\n\tvoid authenticateWhenFailsInParentAndPublishesThenChildDoesNotPublish() {\n\t\tBadCredentialsException badCredentialsExParent = new BadCredentialsException(\"Bad Credentials in parent\");\n\t\tProviderManager parentMgr = new ProviderManager(createProviderWhichThrows(badCredentialsExParent));\n\t\tProviderManager childMgr = new ProviderManager(\n\t\t\t\tList.of(createProviderWhichThrows(new BadCredentialsException(\"Bad Credentials in child\"))), parentMgr);\n\t\tAuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);\n\t\tparentMgr.setAuthenticationEventPublisher(publisher);\n\t\tchildMgr.setAuthenticationEventPublisher(publisher);\n\t\tAuthentication authReq = mock(Authentication.class);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> childMgr.authenticate(authReq))\n\t\t\t.isSameAs(badCredentialsExParent);\n\t\tverify(publisher).publishAuthenticationFailure(badCredentialsExParent, authReq); // Parent\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// publishes\n\t\tverifyNoMoreInteractions(publisher); // Child should not publish (duplicate event)\n\t}\n\n\tprivate AuthenticationProvider createProviderWhichThrows(final AuthenticationException ex) {\n\t\tAuthenticationProvider provider = mock(AuthenticationProvider.class);\n\t\tgiven(provider.supports(any(Class.class))).willReturn(true);\n\t\tgiven(provider.authenticate(any(Authentication.class))).willThrow(ex);\n\t\treturn provider;\n\t}\n\n\tprivate AuthenticationProvider createProviderWhichReturns(final Authentication a) {\n\t\tAuthenticationProvider provider = mock(AuthenticationProvider.class);\n\t\tgiven(provider.supports(any(Class.class))).willReturn(true);\n\t\tgiven(provider.authenticate(any(Authentication.class))).willReturn(a);\n\t\treturn provider;\n\t}\n\n\tprivate TestingAuthenticationToken createAuthenticationToken() {\n\t\treturn new TestingAuthenticationToken(\"name\", \"password\", new ArrayList<>(0));\n\t}\n\n\tprivate ProviderManager makeProviderManager() {\n\t\tMockProvider provider = new MockProvider();\n\t\treturn new ProviderManager(provider);\n\t}\n\n\tprivate static class MockProvider implements AuthenticationProvider {\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\tif (supports(authentication.getClass())) {\n\t\t\t\treturn authentication;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new AuthenticationServiceException(\"Don't support this class\");\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supports(Class<?> authentication) {\n\t\t\treturn TestingAuthenticationToken.class.isAssignableFrom(authentication)\n\t\t\t\t\t|| UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);\n\t\t}\n\n\t}\n\n\t/**\n\t * Represents a custom {@link Authentication} that does not override\n\t * {@link #toBuilder()}. We should remain passive to previous versions of Spring\n\t * Security and not change the {@link Authentication} type.\n\t */\n\tprivate static final class DefaultToBuilderAuthentication implements Authentication {\n\n\t\t@Override\n\t\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\t\treturn List.of();\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object getCredentials() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object getDetails() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object getPrincipal() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAuthenticated() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ReactiveAuthenticationManagerAdapterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class ReactiveAuthenticationManagerAdapterTests {\n\n\t@Mock\n\tAuthenticationManager delegate;\n\n\t@Mock\n\tAuthentication authentication;\n\n\tReactiveAuthenticationManagerAdapter manager;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.manager = new ReactiveAuthenticationManagerAdapter(this.delegate);\n\t}\n\n\t@Test\n\tpublic void constructorNullAuthenticationManager() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ReactiveAuthenticationManagerAdapter(null));\n\t}\n\n\t@Test\n\tpublic void setSchedulerNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.manager.setScheduler(null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSuccessThenSuccess() {\n\t\tgiven(this.delegate.authenticate(any())).willReturn(this.authentication);\n\t\tgiven(this.authentication.isAuthenticated()).willReturn(true);\n\t\tAuthentication result = this.manager.authenticate(this.authentication).block();\n\t\tassertThat(result).isEqualTo(this.authentication);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenReturnNotAuthenticatedThenError() {\n\t\tgiven(this.delegate.authenticate(any())).willReturn(this.authentication);\n\t\tAuthentication result = this.manager.authenticate(this.authentication).block();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenBadCredentialsThenError() {\n\t\tgiven(this.delegate.authenticate(any())).willThrow(new BadCredentialsException(\"Failed\"));\n\t\tMono<Authentication> result = this.manager.authenticate(this.authentication);\n\t\t// @formatter:off\n\t\tStepVerifier.create(result)\n\t\t\t\t.expectError(BadCredentialsException.class)\n\t\t\t\t.verify();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ReactiveUserDetailsServiceAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class ReactiveUserDetailsServiceAuthenticationManagerTests {\n\n\t@Mock\n\tReactiveUserDetailsService repository;\n\n\t@Mock\n\tPasswordEncoder passwordEncoder;\n\n\tUserDetailsRepositoryReactiveAuthenticationManager manager;\n\n\tString username;\n\n\tString password;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.manager = new UserDetailsRepositoryReactiveAuthenticationManager(this.repository);\n\t\tthis.username = \"user\";\n\t\tthis.password = \"pass\";\n\t}\n\n\t@Test\n\tpublic void constructorNullUserDetailsService() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new UserDetailsRepositoryReactiveAuthenticationManager(null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUserNotFoundThenBadCredentials() {\n\t\tgiven(this.repository.findByUsername(this.username)).willReturn(Mono.empty());\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.username,\n\t\t\t\tthis.password);\n\t\tMono<Authentication> authentication = this.manager.authenticate(token);\n\t\t// @formatter:off\n\t\tStepVerifier.create(authentication)\n\t\t\t\t.expectError(BadCredentialsException.class)\n\t\t\t\t.verify();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPasswordNotEqualThenBadCredentials() {\n\t\t// @formatter:off\n\t\tUserDetails user = PasswordEncodedUser.withUsername(this.username)\n\t\t\t.password(this.password)\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.repository.findByUsername(user.getUsername())).willReturn(Mono.just(user));\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.username,\n\t\t\t\tthis.password + \"INVALID\");\n\t\tMono<Authentication> authentication = this.manager.authenticate(token);\n\t\t// @formatter:off\n\t\tStepVerifier.create(authentication)\n\t\t\t\t.expectError(BadCredentialsException.class)\n\t\t\t\t.verify();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSuccessThenSuccess() {\n\t\t// @formatter:off\n\t\tUserDetails user = PasswordEncodedUser.withUsername(this.username)\n\t\t\t.password(this.password)\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.repository.findByUsername(user.getUsername())).willReturn(Mono.just(user));\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.username,\n\t\t\t\tthis.password);\n\t\tAuthentication authentication = this.manager.authenticate(token).block();\n\t\tassertThat(authentication).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPasswordEncoderAndSuccessThenSuccess() {\n\t\tthis.manager.setPasswordEncoder(this.passwordEncoder);\n\t\tgiven(this.passwordEncoder.matches(any(), any())).willReturn(true);\n\t\tUser user = new User(this.username, this.password, AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tgiven(this.repository.findByUsername(user.getUsername())).willReturn(Mono.just(user));\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.username,\n\t\t\t\tthis.password);\n\t\tAuthentication authentication = this.manager.authenticate(token).block();\n\t\tassertThat(authentication).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPasswordEncoderAndFailThenFail() {\n\t\tthis.manager.setPasswordEncoder(this.passwordEncoder);\n\t\tgiven(this.passwordEncoder.matches(any(), any())).willReturn(false);\n\t\tUser user = new User(this.username, this.password, AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tgiven(this.repository.findByUsername(user.getUsername())).willReturn(Mono.just(user));\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.username,\n\t\t\t\tthis.password);\n\t\tMono<Authentication> authentication = this.manager.authenticate(token);\n\t\t// @formatter:off\n\t\tStepVerifier.create(authentication)\n\t\t\t\t.expectError(BadCredentialsException.class)\n\t\t\t\t.verify();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/SecurityAssertions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.assertj.core.api.AbstractObjectAssert;\nimport org.assertj.core.api.Assertions;\nimport org.assertj.core.api.CollectionAssert;\nimport org.assertj.core.api.Condition;\nimport org.assertj.core.api.ObjectAssert;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\n@NullMarked\npublic final class SecurityAssertions {\n\n\tprivate SecurityAssertions() {\n\n\t}\n\n\tpublic static AuthenticationAssert assertThat(@Nullable Authentication authentication) {\n\t\tAssertions.assertThat(authentication).isNotNull();\n\t\treturn new AuthenticationAssert(authentication);\n\t}\n\n\tpublic static final class AuthenticationAssert extends AbstractObjectAssert<AuthenticationAssert, Authentication> {\n\n\t\tprivate final Authentication authentication;\n\n\t\tprivate AuthenticationAssert(Authentication authentication) {\n\t\t\tsuper(authentication, AuthenticationAssert.class);\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t\tpublic AuthenticationAssert name(String name) {\n\t\t\tAssertions.assertThat(this.authentication.getName()).isEqualTo(name);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ObjectAssert<GrantedAuthority> hasAuthority(String authority) {\n\t\t\tCollection<? extends GrantedAuthority> actual = this.authentication.getAuthorities();\n\t\t\tfor (GrantedAuthority element : actual) {\n\t\t\t\tif (element.getAuthority().equals(authority)) {\n\t\t\t\t\treturn new ObjectAssert<>(element);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new AssertionError(actual + \" does not contain \" + authority + \" as expected\");\n\t\t}\n\n\t\tpublic CollectionAssert<GrantedAuthority> hasAuthorities(String... authorities) {\n\t\t\tHasAuthoritiesPredicate test = new HasAuthoritiesPredicate(authorities);\n\t\t\treturn authorities().has(new Condition<>(test, \"contains %s\", Arrays.toString(authorities)));\n\t\t}\n\n\t\tpublic CollectionAssert<GrantedAuthority> roles() {\n\t\t\treturn authorities().filteredOn((authority) -> authority.getAuthority().startsWith(\"ROLE_\"));\n\t\t}\n\n\t\tpublic CollectionAssert<GrantedAuthority> authorities() {\n\t\t\treturn new CollectionAssert<>(this.authentication.getAuthorities());\n\t\t}\n\n\t}\n\n\tprivate static final class HasAuthoritiesPredicate implements Predicate<Collection<? extends GrantedAuthority>> {\n\n\t\tprivate final Collection<String> expected;\n\n\t\tprivate HasAuthoritiesPredicate(String... expected) {\n\t\t\tthis.expected = List.of(expected);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean test(Collection<? extends GrantedAuthority> actual) {\n\t\t\tSet<String> asString = AuthorityUtils.authorityListToSet(actual);\n\t\t\treturn asString.containsAll(this.expected);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/TestAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.function.Consumer;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 5.0\n */\npublic class TestAuthentication extends PasswordEncodedUser {\n\n\tprivate static final Authentication ANONYMOUS = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tprivate static final RememberMeAuthenticationToken REMEMBER_ME = new RememberMeAuthenticationToken(\"key\", \"user\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\n\tpublic static Authentication authenticatedAdmin() {\n\t\treturn authenticated(admin());\n\t}\n\n\tpublic static Authentication authenticatedUser() {\n\t\treturn authenticated(user());\n\t}\n\n\tpublic static Authentication authenticatedUser(Consumer<User.UserBuilder> consumer) {\n\t\tUser.UserBuilder builder = withUsername(\"user\");\n\t\tconsumer.accept(builder);\n\t\treturn authenticated(builder.build());\n\t}\n\n\tpublic static Authentication authenticated(UserDetails user) {\n\t\treturn UsernamePasswordAuthenticationToken.authenticated(user, null, user.getAuthorities());\n\t}\n\n\tpublic static Authentication anonymousUser() {\n\t\treturn ANONYMOUS;\n\t}\n\n\tpublic static Authentication rememberMeUser() {\n\t\treturn REMEMBER_ME;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/TestingAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link TestingAuthenticationProvider}.\n *\n * @author Ben Alex\n */\npublic class TestingAuthenticationProviderTests {\n\n\t@Test\n\tpublic void testAuthenticates() {\n\t\tTestingAuthenticationProvider provider = new TestingAuthenticationProvider();\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"Test\", \"Password\", \"ROLE_ONE\", \"ROLE_TWO\");\n\t\tAuthentication result = provider.authenticate(token);\n\t\tassertThat(result instanceof TestingAuthenticationToken).isTrue();\n\t\tTestingAuthenticationToken castResult = (TestingAuthenticationToken) result;\n\t\tassertThat(castResult.getPrincipal()).isEqualTo(\"Test\");\n\t\tassertThat(castResult.getCredentials()).isEqualTo(\"Password\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(castResult.getAuthorities())).contains(\"ROLE_ONE\", \"ROLE_TWO\");\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tTestingAuthenticationProvider provider = new TestingAuthenticationProvider();\n\t\tassertThat(provider.supports(TestingAuthenticationToken.class)).isTrue();\n\t\tassertThat(!provider.supports(String.class)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/TestingAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Arrays;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Josh Cummings\n */\npublic class TestingAuthenticationTokenTests {\n\n\t@Test\n\tpublic void constructorWhenNoAuthoritiesThenUnauthenticated() {\n\t\tTestingAuthenticationToken unauthenticated = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tassertThat(unauthenticated.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void constructorWhenArityAuthoritiesThenAuthenticated() {\n\t\tTestingAuthenticationToken authenticated = new TestingAuthenticationToken(\"principal\", \"credentials\",\n\t\t\t\t\"authority\");\n\t\tassertThat(authenticated.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void constructorWhenCollectionAuthoritiesThenAuthenticated() {\n\t\tTestingAuthenticationToken authenticated = new TestingAuthenticationToken(\"principal\", \"credentials\",\n\t\t\t\tArrays.asList(new SimpleGrantedAuthority(\"authority\")));\n\t\tassertThat(authenticated.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void toBuilderWhenApplyThenCopies() {\n\t\tTestingAuthenticationToken factorOne = new TestingAuthenticationToken(\"alice\", \"pass\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"));\n\t\tTestingAuthenticationToken factorTwo = new TestingAuthenticationToken(\"bob\", \"ssap\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"));\n\t\tTestingAuthenticationToken result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.credentials(factorTwo.getCredentials())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(result.getCredentials()).isSameAs(factorTwo.getCredentials());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n\t@Test\n\tvoid constructorObjectObjectStringVargsWhenNullAuthorities() {\n\t\tString[] authorities = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new TestingAuthenticationToken(\"user\", \"password\", authorities));\n\t}\n\n\t@Test\n\tvoid constructorObjectObjectStringVargsWhenValid() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tassertThat(auth.isAuthenticated()).isTrue();\n\t\tassertThat(auth.getPrincipal()).isEqualTo(\"user\");\n\t\tassertThat(auth.getCredentials()).isEqualTo(\"password\");\n\t\tassertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly(\"ROLE_USER\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/UserDetailsRepositoryReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Scheduler;\nimport reactor.core.scheduler.Schedulers;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.security.authentication.password.CompromisedPasswordDecision;\nimport org.springframework.security.authentication.password.CompromisedPasswordException;\nimport org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class UserDetailsRepositoryReactiveAuthenticationManagerTests {\n\n\t@Mock\n\tprivate ReactiveUserDetailsService userDetailsService;\n\n\t@Mock\n\tprivate PasswordEncoder encoder;\n\n\t@Mock\n\tprivate ReactiveUserDetailsPasswordService userDetailsPasswordService;\n\n\t@Mock\n\tprivate Scheduler scheduler;\n\n\t@Mock\n\tprivate UserDetailsChecker postAuthenticationChecks;\n\n\t// @formatter:off\n\tprivate UserDetails user = User.withUsername(\"user\")\n\t\t.password(\"password\")\n\t\t.roles(\"USER\")\n\t\t.build();\n\t// @formatter:on\n\tprivate UserDetailsRepositoryReactiveAuthenticationManager manager;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.manager = new UserDetailsRepositoryReactiveAuthenticationManager(this.userDetailsService);\n\t}\n\n\t@Test\n\tpublic void setSchedulerWhenNullThenIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.manager.setScheduler(null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomSchedulerThenUsed() {\n\t\tgiven(this.scheduler.schedule(any())).willAnswer((a) -> {\n\t\t\tRunnable r = a.getArgument(0);\n\t\t\treturn Schedulers.immediate().schedule(r);\n\t\t});\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(this.user));\n\t\tgiven(this.encoder.matches(any(), any())).willReturn(true);\n\t\tthis.manager.setScheduler(this.scheduler);\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.user,\n\t\t\t\tthis.user.getPassword());\n\t\tAuthentication result = this.manager.authenticate(token).block();\n\t\tverify(this.scheduler).schedule(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPasswordServiceThenUpdated() {\n\t\tString encodedPassword = \"encoded\";\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(this.user));\n\t\tgiven(this.encoder.matches(any(), any())).willReturn(true);\n\t\tgiven(this.encoder.upgradeEncoding(any())).willReturn(true);\n\t\tgiven(this.encoder.encode(any())).willReturn(encodedPassword);\n\t\tgiven(this.userDetailsPasswordService.updatePassword(any(), any())).willReturn(Mono.just(this.user));\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\tthis.manager.setUserDetailsPasswordService(this.userDetailsPasswordService);\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.user,\n\t\t\t\tthis.user.getPassword());\n\t\tAuthentication result = this.manager.authenticate(token).block();\n\t\tverify(this.encoder).encode(this.user.getPassword());\n\t\tverify(this.userDetailsPasswordService).updatePassword(eq(this.user), eq(encodedPassword));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPasswordServiceAndBadCredentialsThenNotUpdated() {\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(this.user));\n\t\tgiven(this.encoder.matches(any(), any())).willReturn(false);\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\tthis.manager.setUserDetailsPasswordService(this.userDetailsPasswordService);\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.user,\n\t\t\t\tthis.user.getPassword());\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(token).block());\n\t\tverifyNoMoreInteractions(this.userDetailsPasswordService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPasswordServiceAndUpgradeFalseThenNotUpdated() {\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(this.user));\n\t\tgiven(this.encoder.matches(any(), any())).willReturn(true);\n\t\tgiven(this.encoder.upgradeEncoding(any())).willReturn(false);\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\tthis.manager.setUserDetailsPasswordService(this.userDetailsPasswordService);\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.user,\n\t\t\t\tthis.user.getPassword());\n\t\tAuthentication result = this.manager.authenticate(token).block();\n\t\tverifyNoMoreInteractions(this.userDetailsPasswordService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPostAuthenticationChecksFail() {\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(this.user));\n\t\twillThrow(new LockedException(\"account is locked\")).given(this.postAuthenticationChecks).check(any());\n\t\tgiven(this.encoder.matches(any(), any())).willReturn(true);\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\tthis.manager.setPostAuthenticationChecks(this.postAuthenticationChecks);\n\t\tassertThatExceptionOfType(LockedException.class)\n\t\t\t.isThrownBy(() -> this.manager\n\t\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(this.user, this.user.getPassword()))\n\t\t\t\t.block())\n\t\t\t.withMessage(\"account is locked\");\n\t\tverify(this.postAuthenticationChecks).check(eq(this.user));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPostAuthenticationChecksNotSet() {\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(this.user));\n\t\tgiven(this.encoder.matches(any(), any())).willReturn(true);\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(this.user,\n\t\t\t\tthis.user.getPassword());\n\t\tthis.manager.authenticate(token).block();\n\t\tverifyNoMoreInteractions(this.postAuthenticationChecks);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccountExpiredThenException() {\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\t// @formatter:off\n\t\tUserDetails expiredUser = User.withUsername(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.accountExpired(true)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(expiredUser));\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(expiredUser,\n\t\t\t\texpiredUser.getPassword());\n\t\tassertThatExceptionOfType(AccountExpiredException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(token).block());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccountLockedThenException() {\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\t// @formatter:off\n\t\tUserDetails lockedUser = User.withUsername(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.accountLocked(true)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(lockedUser));\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(lockedUser,\n\t\t\t\tlockedUser.getPassword());\n\t\tassertThatExceptionOfType(LockedException.class).isThrownBy(() -> this.manager.authenticate(token).block());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccountDisabledThenException() {\n\t\tthis.manager.setPasswordEncoder(this.encoder);\n\t\t// @formatter:off\n\t\tUserDetails disabledUser = User.withUsername(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.disabled(true)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(disabledUser));\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(disabledUser,\n\t\t\t\tdisabledUser.getPassword());\n\t\tassertThatExceptionOfType(DisabledException.class).isThrownBy(() -> this.manager.authenticate(token).block());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPasswordCompromisedThenException() {\n\t\t// @formatter:off\n\t\tUserDetails user = User.withUsername(\"user\")\n\t\t\t\t.password(\"{noop}password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(user));\n\t\tthis.manager.setCompromisedPasswordChecker(new TestReactivePasswordChecker());\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(user,\n\t\t\t\t\"password\");\n\t\tStepVerifier.create(this.manager.authenticate(token))\n\t\t\t.expectErrorSatisfies((ex) -> assertThat(ex).isInstanceOf(CompromisedPasswordException.class)\n\t\t\t\t.withFailMessage(\"The provided password is compromised, please change your password\"))\n\t\t\t.verify();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPasswordNotCompromisedThenSuccess() {\n\t\t// @formatter:off\n\t\tUserDetails user = User.withUsername(\"user\")\n\t\t\t\t.password(\"{noop}notcompromised\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.userDetailsService.findByUsername(any())).willReturn(Mono.just(user));\n\t\tthis.manager.setCompromisedPasswordChecker(new TestReactivePasswordChecker());\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(user,\n\t\t\t\t\"notcompromised\");\n\t\tStepVerifier.create(this.manager.authenticate(token))\n\t\t\t.assertNext((authentication) -> assertThat(authentication.getPrincipal()).isEqualTo(user))\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.manager.setMessageSource(null));\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNotNullThenCanGet() {\n\t\tMessageSource source = mock(MessageSource.class);\n\t\tthis.manager.setMessageSource(source);\n\t\tString code = \"code\";\n\t\tthis.manager.messages.getMessage(code);\n\t\tverify(source).getMessage(eq(code), any(), any());\n\t}\n\n\tstatic class TestReactivePasswordChecker implements ReactiveCompromisedPasswordChecker {\n\n\t\t@Override\n\t\tpublic Mono<CompromisedPasswordDecision> check(String password) {\n\t\t\tif (\"password\".equals(password)) {\n\t\t\t\treturn Mono.just(new CompromisedPasswordDecision(true));\n\t\t\t}\n\t\t\treturn Mono.just(new CompromisedPasswordDecision(false));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/UsernamePasswordAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link UsernamePasswordAuthenticationToken}.\n *\n * @author Ben Alex\n */\npublic class UsernamePasswordAuthenticationTokenTests {\n\n\t@Test\n\tpublic void authenticatedPropertyContractIsSatisfied() {\n\t\tUsernamePasswordAuthenticationToken grantedToken = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.NO_AUTHORITIES);\n\t\t// check default given we passed some GrantedAuthority[]s (well, we passed empty\n\t\t// list)\n\t\tassertThat(grantedToken.isAuthenticated()).isTrue();\n\t\t// check explicit set to untrusted (we can safely go from trusted to untrusted,\n\t\t// but not the reverse)\n\t\tgrantedToken.setAuthenticated(false);\n\t\tassertThat(!grantedToken.isAuthenticated()).isTrue();\n\t\t// Now let's create a UsernamePasswordAuthenticationToken without any\n\t\t// GrantedAuthority[]s (different constructor)\n\t\tUsernamePasswordAuthenticationToken noneGrantedToken = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(\"Test\", \"Password\");\n\t\tassertThat(!noneGrantedToken.isAuthenticated()).isTrue();\n\t\t// check we're allowed to still set it to untrusted\n\t\tnoneGrantedToken.setAuthenticated(false);\n\t\tassertThat(!noneGrantedToken.isAuthenticated()).isTrue();\n\t\t// check denied changing it to trusted\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> noneGrantedToken.setAuthenticated(true));\n\t}\n\n\t@Test\n\tpublic void gettersReturnCorrectData() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tassertThat(token.getPrincipal()).isEqualTo(\"Test\");\n\t\tassertThat(token.getCredentials()).isEqualTo(\"Password\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(token.getAuthorities())).contains(\"ROLE_ONE\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(token.getAuthorities())).contains(\"ROLE_TWO\");\n\t}\n\n\t@Test\n\tpublic void testNoArgConstructorDoesntExist() throws Exception {\n\t\tClass<?> clazz = UsernamePasswordAuthenticationToken.class;\n\t\tassertThatExceptionOfType(NoSuchMethodException.class)\n\t\t\t.isThrownBy(() -> clazz.getDeclaredConstructor((Class[]) null));\n\t}\n\n\t@Test\n\tpublic void unauthenticatedFactoryMethodResultsUnauthenticatedToken() {\n\t\tUsernamePasswordAuthenticationToken grantedToken = UsernamePasswordAuthenticationToken.unauthenticated(\"Test\",\n\t\t\t\t\"Password\");\n\t\tassertThat(grantedToken.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticatedFactoryMethodResultsAuthenticatedToken() {\n\t\tUsernamePasswordAuthenticationToken grantedToken = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", AuthorityUtils.NO_AUTHORITIES);\n\t\tassertThat(grantedToken.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void toBuilderWhenApplyThenCopies() {\n\t\tUsernamePasswordAuthenticationToken factorOne = new UsernamePasswordAuthenticationToken(\"alice\", \"pass\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"));\n\t\tUsernamePasswordAuthenticationToken factorTwo = new UsernamePasswordAuthenticationToken(\"bob\", \"ssap\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"));\n\t\tUsernamePasswordAuthenticationToken result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.credentials(factorTwo.getCredentials())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isEqualTo(\"bob\");\n\t\tassertThat(result.getCredentials()).isEqualTo(\"ssap\");\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/anonymous/AnonymousAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.anonymous;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationProvider;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AnonymousAuthenticationProvider}.\n *\n * @author Ben Alex\n */\npublic class AnonymousAuthenticationProviderTests {\n\n\t@Test\n\tpublic void testDetectsAnInvalidKey() {\n\t\tAnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(\"qwerty\");\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(\"WRONG_KEY\", \"Test\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> aap.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testDetectsMissingKey() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousAuthenticationProvider(null));\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tAnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(\"qwerty\");\n\t\tassertThat(aap.getKey()).isEqualTo(\"qwerty\");\n\t}\n\n\t@Test\n\tpublic void testIgnoresClassesItDoesNotSupport() {\n\t\tAnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(\"qwerty\");\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_A\");\n\t\tassertThat(aap.supports(TestingAuthenticationToken.class)).isFalse();\n\t\t// Try it anyway\n\t\tassertThat(aap.authenticate(token)).isNull();\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() {\n\t\tAnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(\"qwerty\");\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(\"qwerty\", \"Test\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tAuthentication result = aap.authenticate(token);\n\t\tassertThat(token).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tAnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(\"qwerty\");\n\t\tassertThat(aap.supports(AnonymousAuthenticationToken.class)).isTrue();\n\t\tassertThat(aap.supports(TestingAuthenticationToken.class)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/anonymous/AnonymousAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.anonymous;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AnonymousAuthenticationToken}.\n *\n * @author Ben Alex\n */\npublic class AnonymousAuthenticationTokenTests {\n\n\tprivate static final List<GrantedAuthority> ROLES_12 = AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\");\n\n\t@Test\n\tpublic void testConstructorRejectsNulls() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousAuthenticationToken(null, \"Test\", ROLES_12));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousAuthenticationToken(\"key\", null, ROLES_12));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousAuthenticationToken(\"key\", \"Test\", null));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AnonymousAuthenticationToken(\"key\", \"Test\", AuthorityUtils.NO_AUTHORITIES));\n\t}\n\n\t@Test\n\tpublic void testEqualsWhenEqual() {\n\t\tAnonymousAuthenticationToken token1 = new AnonymousAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tAnonymousAuthenticationToken token2 = new AnonymousAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tassertThat(token2).isEqualTo(token1);\n\t}\n\n\t@Test\n\tpublic void testGetters() {\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tassertThat(token.getKeyHash()).isEqualTo(\"key\".hashCode());\n\t\tassertThat(token.getPrincipal()).isEqualTo(\"Test\");\n\t\tassertThat(token.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(token.getAuthorities())).contains(\"ROLE_ONE\", \"ROLE_TWO\");\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNoArgConstructorDoesntExist() {\n\t\tassertThatExceptionOfType(NoSuchMethodException.class)\n\t\t\t.isThrownBy(() -> AnonymousAuthenticationToken.class.getDeclaredConstructor((Class[]) null));\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToAbstractParentEqualsCheck() {\n\t\tAnonymousAuthenticationToken token1 = new AnonymousAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tAnonymousAuthenticationToken token2 = new AnonymousAuthenticationToken(\"key\", \"DIFFERENT_PRINCIPAL\", ROLES_12);\n\t\tassertThat(token1.equals(token2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToDifferentAuthenticationClass() {\n\t\tAnonymousAuthenticationToken token1 = new AnonymousAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tUsernamePasswordAuthenticationToken token2 = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", ROLES_12);\n\t\tassertThat(token1.equals(token2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToKey() {\n\t\tAnonymousAuthenticationToken token1 = new AnonymousAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tAnonymousAuthenticationToken token2 = new AnonymousAuthenticationToken(\"DIFFERENT_KEY\", \"Test\", ROLES_12);\n\t\tassertThat(token1.equals(token2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testSetAuthenticatedIgnored() {\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t\ttoken.setAuthenticated(false);\n\t\tassertThat(!token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullAuthoritiesThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AnonymousAuthenticationToken(\"key\", \"principal\", null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenEmptyAuthoritiesThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new AnonymousAuthenticationToken(\"key\", \"principal\", Collections.<GrantedAuthority>emptyList()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrincipalIsEmptyStringThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousAuthenticationToken(\"key\", \"\", ROLES_12));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/dao/DaoAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.dao;\n\nimport java.security.SecureRandom;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.password.CompromisedPasswordChecker;\nimport org.springframework.security.authentication.password.CompromisedPasswordDecision;\nimport org.springframework.security.authentication.password.CompromisedPasswordException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsPasswordService;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.core.userdetails.cache.NullUserCache;\nimport org.springframework.security.core.userdetails.cache.SpringCacheBasedUserCache;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isA;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link DaoAuthenticationProvider}.\n *\n * @author Ben Alex\n * @author Rob Winch\n */\npublic class DaoAuthenticationProviderTests {\n\n\tprivate static final List<GrantedAuthority> ROLES_12 = AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\");\n\n\t@Test\n\tpublic void testAuthenticateFailsForIncorrectPasswordCase() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"KOala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testReceivedBadCredentialsWhenCredentialsNotProvided() {\n\t\t// Test related to SEC-434\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tUsernamePasswordAuthenticationToken authenticationToken = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(\"rod\", null);\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> provider.authenticate(authenticationToken));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsIfAccountExpired() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"peter\",\n\t\t\t\t\"opal\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserPeterAccountExpired());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(AccountExpiredException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsIfAccountLocked() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"peter\",\n\t\t\t\t\"opal\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserPeterAccountLocked());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(LockedException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsIfCredentialsExpired() {\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserPeterCredentialsExpired());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(CredentialsExpiredException.class).isThrownBy(\n\t\t\t\t() -> provider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"peter\", \"opal\")));\n\t\t// Check that wrong password causes BadCredentialsException, rather than\n\t\t// CredentialsExpiredException\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"peter\", \"wrong_password\")));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsIfUserDisabled() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"peter\",\n\t\t\t\t\"opal\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserPeter());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(DisabledException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsWhenAuthenticationDaoHasBackendFailure() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceSimulateBackendError());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(InternalAuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsWithEmptyUsername() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(null, \"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsWithInvalidPassword() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\",\n\t\t\t\t\"INVALID_PASSWORD\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsWithInvalidUsernameAndHideUserNotFoundExceptionFalse() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"INVALID_USER\",\n\t\t\t\t\"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setHideUserNotFoundExceptions(false); // we want\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// UsernameNotFoundExceptions\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsWithInvalidUsernameAndHideUserNotFoundExceptionsWithDefaultOfTrue() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"INVALID_USER\",\n\t\t\t\t\"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tassertThat(provider.isHideUserNotFoundExceptions()).isTrue();\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsWithInvalidUsernameAndChangePasswordEncoder() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"INVALID_USER\",\n\t\t\t\t\"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tassertThat(provider.isHideUserNotFoundExceptions()).isTrue();\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t\tprovider.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticateFailsWithMixedCaseUsernameIfDefaultChanged() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"RoD\", \"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testAuthenticates() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\");\n\t\ttoken.setDetails(\"192.168.0.1\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tAuthentication result = provider.authenticate(token);\n\t\tif (!(result instanceof UsernamePasswordAuthenticationToken)) {\n\t\t\tfail(\"Should have returned instance of UsernamePasswordAuthenticationToken\");\n\t\t}\n\t\tUsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;\n\t\tassertThat(castResult.getPrincipal().getClass()).isEqualTo(User.class);\n\t\tassertThat(castResult.getCredentials()).isEqualTo(\"koala\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(castResult.getAuthorities())).contains(\"ROLE_ONE\", \"ROLE_TWO\");\n\t\tassertThat(castResult.getDetails()).isEqualTo(\"192.168.0.1\");\n\t}\n\n\t@Test\n\tpublic void testAuthenticatesASecondTime() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tAuthentication result = provider.authenticate(token);\n\t\tif (!(result instanceof UsernamePasswordAuthenticationToken)) {\n\t\t\tfail(\"Should have returned instance of UsernamePasswordAuthenticationToken\");\n\t\t}\n\t\t// Now try to authenticate with the previous result (with its UserDetails)\n\t\tAuthentication result2 = provider.authenticate(result);\n\t\tif (!(result2 instanceof UsernamePasswordAuthenticationToken)) {\n\t\t\tfail(\"Should have returned instance of UsernamePasswordAuthenticationToken\");\n\t\t}\n\t\tassertThat(result2.getCredentials()).isEqualTo(result.getCredentials());\n\t}\n\n\t@Test\n\tpublic void testAuthenticatesWithForcePrincipalAsString() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tprovider.setForcePrincipalAsString(true);\n\t\tAuthentication result = provider.authenticate(token);\n\t\tif (!(result instanceof UsernamePasswordAuthenticationToken)) {\n\t\t\tfail(\"Should have returned instance of UsernamePasswordAuthenticationToken\");\n\t\t}\n\t\tUsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;\n\t\tassertThat(castResult.getPrincipal().getClass()).isEqualTo(String.class);\n\t\tassertThat(castResult.getPrincipal()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSuccessAndPasswordManagerThenUpdates() {\n\t\tString password = \"password\";\n\t\tString encodedPassword = \"encoded\";\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"user\",\n\t\t\t\tpassword);\n\t\tPasswordEncoder encoder = mock(PasswordEncoder.class);\n\t\tUserDetailsService userDetailsService = mock(UserDetailsService.class);\n\t\tUserDetailsPasswordService passwordManager = mock(UserDetailsPasswordService.class);\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);\n\t\tprovider.setPasswordEncoder(encoder);\n\t\tprovider.setUserDetailsPasswordService(passwordManager);\n\t\tUserDetails user = PasswordEncodedUser.user();\n\t\tgiven(encoder.matches(any(), any())).willReturn(true);\n\t\tgiven(encoder.upgradeEncoding(any())).willReturn(true);\n\t\tgiven(encoder.encode(any())).willReturn(encodedPassword);\n\t\tgiven(userDetailsService.loadUserByUsername(any())).willReturn(user);\n\t\tgiven(passwordManager.updatePassword(any(), any())).willReturn(user);\n\t\tAuthentication result = provider.authenticate(token);\n\t\tverify(encoder).encode(password);\n\t\tverify(passwordManager).updatePassword(eq(user), eq(encodedPassword));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenBadCredentialsAndPasswordManagerThenNoUpdate() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"user\",\n\t\t\t\t\"password\");\n\t\tPasswordEncoder encoder = mock(PasswordEncoder.class);\n\t\tUserDetailsService userDetailsService = mock(UserDetailsService.class);\n\t\tUserDetailsPasswordService passwordManager = mock(UserDetailsPasswordService.class);\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);\n\t\tprovider.setPasswordEncoder(encoder);\n\t\tprovider.setUserDetailsPasswordService(passwordManager);\n\t\tUserDetails user = PasswordEncodedUser.user();\n\t\tgiven(encoder.matches(any(), any())).willReturn(false);\n\t\tgiven(userDetailsService.loadUserByUsername(any())).willReturn(user);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(token));\n\t\tverifyNoMoreInteractions(passwordManager);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNotUpgradeAndPasswordManagerThenNoUpdate() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"user\",\n\t\t\t\t\"password\");\n\t\tPasswordEncoder encoder = mock(PasswordEncoder.class);\n\t\tUserDetailsService userDetailsService = mock(UserDetailsService.class);\n\t\tUserDetailsPasswordService passwordManager = mock(UserDetailsPasswordService.class);\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);\n\t\tprovider.setPasswordEncoder(encoder);\n\t\tprovider.setUserDetailsPasswordService(passwordManager);\n\t\tUserDetails user = PasswordEncodedUser.user();\n\t\tgiven(encoder.matches(any(), any())).willReturn(true);\n\t\tgiven(encoder.upgradeEncoding(any())).willReturn(false);\n\t\tgiven(userDetailsService.loadUserByUsername(any())).willReturn(user);\n\t\tAuthentication result = provider.authenticate(token);\n\t\tverifyNoMoreInteractions(passwordManager);\n\t}\n\n\t@Test\n\tpublic void testDetectsNullBeingReturnedFromAuthenticationDao() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\");\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceReturnsNull());\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class).isThrownBy(() -> provider.authenticate(token))\n\t\t\t.withMessage(\"UserDetailsService returned null, which is an interface contract violation\");\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setPasswordEncoder(new BCryptPasswordEncoder());\n\t\tassertThat(provider.getPasswordEncoder().getClass()).isEqualTo(BCryptPasswordEncoder.class);\n\t\tprovider.setUserCache(new SpringCacheBasedUserCache(mock(Cache.class)));\n\t\tassertThat(provider.getUserCache().getClass()).isEqualTo(SpringCacheBasedUserCache.class);\n\t\tassertThat(provider.isForcePrincipalAsString()).isFalse();\n\t\tprovider.setForcePrincipalAsString(true);\n\t\tassertThat(provider.isForcePrincipalAsString()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testGoesBackToAuthenticationDaoToObtainLatestPasswordIfCachedPasswordSeemsIncorrect() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\");\n\t\tMockUserDetailsServiceUserRod authenticationDao = new MockUserDetailsServiceUserRod();\n\t\tMockUserCache cache = new MockUserCache();\n\t\tDaoAuthenticationProvider provider = createProvider(authenticationDao);\n\t\tprovider.setUserCache(cache);\n\t\t// This will work, as password still \"koala\"\n\t\tprovider.authenticate(token);\n\t\t// Check \"rod = koala\" ended up in the cache\n\t\tassertThat(cache.getUserFromCache(\"rod\").getPassword()).isEqualTo(\"koala\");\n\t\t// Now change the password the AuthenticationDao will return\n\t\tauthenticationDao.setPassword(\"easternLongNeckTurtle\");\n\t\t// Now try authentication again, with the new password\n\t\ttoken = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"easternLongNeckTurtle\");\n\t\tprovider.authenticate(token);\n\t\t// To get this far, the new password was accepted\n\t\t// Check the cache was updated\n\t\tassertThat(cache.getUserFromCache(\"rod\").getPassword()).isEqualTo(\"easternLongNeckTurtle\");\n\t}\n\n\t@Test\n\tpublic void testStartupFailsIfNoUserCacheSet() throws Exception {\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tassertThat(provider.getUserCache().getClass()).isEqualTo(NullUserCache.class);\n\t\tprovider.setUserCache(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(provider::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testStartupSuccess() throws Exception {\n\t\tUserDetailsService userDetailsService = new MockUserDetailsServiceUserRod();\n\t\tDaoAuthenticationProvider provider = createProvider(userDetailsService);\n\t\tprovider.setUserCache(new MockUserCache());\n\t\tassertThat(provider.getUserDetailsService()).isEqualTo(userDetailsService);\n\t\tprovider.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tassertThat(provider.supports(UsernamePasswordAuthenticationToken.class)).isTrue();\n\t\tassertThat(!provider.supports(TestingAuthenticationToken.class)).isTrue();\n\t}\n\n\t// SEC-2056\n\t@Test\n\tpublic void testUserNotFoundEncodesPassword() throws Exception {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"missing\",\n\t\t\t\t\"koala\");\n\t\tPasswordEncoder encoder = mock(PasswordEncoder.class);\n\t\tgiven(encoder.encode(anyString())).willReturn(\"koala\");\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setHideUserNotFoundExceptions(false);\n\t\tprovider.setPasswordEncoder(encoder);\n\t\tprovider.afterPropertiesSet();\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(token));\n\t\t// ensure encoder invoked w/ non-null strings since PasswordEncoder impls may fail\n\t\t// if encoded password is null\n\t\tverify(encoder).matches(isA(String.class), isA(String.class));\n\t}\n\n\t@Test\n\tpublic void testUserNotFoundBCryptPasswordEncoder() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"missing\",\n\t\t\t\t\"koala\");\n\t\tPasswordEncoder encoder = new BCryptPasswordEncoder();\n\t\tMockUserDetailsServiceUserRod userDetailsService = new MockUserDetailsServiceUserRod();\n\t\tuserDetailsService.password = encoder.encode((CharSequence) token.getCredentials());\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);\n\t\tprovider.setHideUserNotFoundExceptions(false);\n\t\tprovider.setPasswordEncoder(encoder);\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testUserNotFoundDefaultEncoder() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"missing\",\n\t\t\t\tnull);\n\t\tDaoAuthenticationProvider provider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setHideUserNotFoundExceptions(false);\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void constructWhenPasswordEncoderProvidedThenSets() {\n\t\tDaoAuthenticationProvider daoAuthenticationProvider = createProvider(new MockUserDetailsServiceUserRod());\n\t\tdaoAuthenticationProvider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());\n\t\tassertThat(daoAuthenticationProvider.getPasswordEncoder()).isSameAs(NoOpPasswordEncoder.getInstance());\n\t}\n\n\t/**\n\t * This is an explicit test for SEC-2056. It is intentionally ignored since this test\n\t * is not deterministic and {@link #testUserNotFoundEncodesPassword()} ensures that\n\t * SEC-2056 is fixed.\n\t */\n\tpublic void IGNOREtestSec2056() {\n\t\tUsernamePasswordAuthenticationToken foundUser = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\",\n\t\t\t\t\"koala\");\n\t\tUsernamePasswordAuthenticationToken notFoundUser = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(\"notFound\", \"koala\");\n\t\tPasswordEncoder encoder = new BCryptPasswordEncoder(10, new SecureRandom());\n\t\tMockUserDetailsServiceUserRod userDetailsService = new MockUserDetailsServiceUserRod();\n\t\tuserDetailsService.password = encoder.encode((CharSequence) foundUser.getCredentials());\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);\n\t\tprovider.setHideUserNotFoundExceptions(false);\n\t\tprovider.setPasswordEncoder(encoder);\n\t\tint sampleSize = 100;\n\t\tList<Long> userFoundTimes = new ArrayList<>(sampleSize);\n\t\tfor (int i = 0; i < sampleSize; i++) {\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\tprovider.authenticate(foundUser);\n\t\t\tuserFoundTimes.add(System.currentTimeMillis() - start);\n\t\t}\n\t\tList<Long> userNotFoundTimes = new ArrayList<>(sampleSize);\n\t\tfor (int i = 0; i < sampleSize; i++) {\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\tassertThatExceptionOfType(UsernameNotFoundException.class)\n\t\t\t\t.isThrownBy(() -> provider.authenticate(notFoundUser));\n\t\t\tuserNotFoundTimes.add(System.currentTimeMillis() - start);\n\t\t}\n\t\tdouble userFoundAvg = avg(userFoundTimes);\n\t\tdouble userNotFoundAvg = avg(userNotFoundTimes);\n\t\tassertThat(Math.abs(userNotFoundAvg - userFoundAvg) <= 3)\n\t\t\t.withFailMessage(\"User not found average \" + userNotFoundAvg\n\t\t\t\t\t+ \" should be within 3ms of user found average \" + userFoundAvg)\n\t\t\t.isTrue();\n\t}\n\n\tprivate double avg(List<Long> counts) {\n\t\treturn counts.stream().mapToLong(Long::longValue).average().orElse(0);\n\t}\n\n\t@Test\n\tpublic void testUserNotFoundNullCredentials() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"missing\",\n\t\t\t\tnull);\n\t\tPasswordEncoder encoder = mock(PasswordEncoder.class);\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(new MockUserDetailsServiceUserRod());\n\t\tprovider.setHideUserNotFoundExceptions(false);\n\t\tprovider.setPasswordEncoder(encoder);\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(token));\n\t\tverify(encoder, times(0)).matches(anyString(), anyString());\n\t}\n\n\t@Test\n\tvoid authenticateWhenPasswordLeakedThenException() {\n\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(withUsers(user));\n\t\tprovider.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());\n\t\tprovider.setCompromisedPasswordChecker(new TestCompromisedPasswordChecker());\n\t\tassertThatExceptionOfType(CompromisedPasswordException.class).isThrownBy(\n\t\t\t\t() -> provider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\")))\n\t\t\t.withMessage(\"The provided password is compromised, please change your password\");\n\t}\n\n\t@Test\n\tvoid authenticateWhenPasswordNotLeakedThenNoException() {\n\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"strongpassword\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(withUsers(user));\n\t\tprovider.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());\n\t\tprovider.setCompromisedPasswordChecker(new TestCompromisedPasswordChecker());\n\t\tAuthentication authentication = provider\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"strongpassword\"));\n\t\tassertThat(authentication).isNotNull();\n\t}\n\n\t@Test\n\tvoid authenticateWhenSuccessThenIssuesFactor() {\n\t\tUserDetails user = PasswordEncodedUser.user();\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(withUsers(user));\n\t\tAuthentication request = new UsernamePasswordAuthenticationToken(\"user\", \"password\");\n\t\tAuthentication result = provider.authenticate(request);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t}\n\n\tprivate UserDetailsService withUsers(UserDetails... users) {\n\t\treturn new InMemoryUserDetailsManager(users);\n\t}\n\n\tprivate DaoAuthenticationProvider createProvider(UserDetailsService userDetailsService) {\n\t\tDaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);\n\t\tprovider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());\n\t\treturn provider;\n\t}\n\n\tprivate class MockUserDetailsServiceReturnsNull implements UserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tprivate class MockUserDetailsServiceSimulateBackendError implements UserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) {\n\t\t\tthrow new DataRetrievalFailureException(\"This mock simulator is designed to fail\");\n\t\t}\n\n\t}\n\n\tprivate class MockUserDetailsServiceUserRod implements UserDetailsService {\n\n\t\tprivate String password = \"koala\";\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) {\n\t\t\tif (\"rod\".equals(username)) {\n\t\t\t\treturn new User(\"rod\", this.password, true, true, true, true, ROLES_12);\n\t\t\t}\n\t\t\tthrow new UsernameNotFoundException(\"Could not find: \" + username);\n\t\t}\n\n\t\tvoid setPassword(String password) {\n\t\t\tthis.password = password;\n\t\t}\n\n\t}\n\n\tprivate class MockUserDetailsServiceUserPeter implements UserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) {\n\t\t\tif (\"peter\".equals(username)) {\n\t\t\t\treturn new User(\"peter\", \"opal\", false, true, true, true, ROLES_12);\n\t\t\t}\n\t\t\tthrow new UsernameNotFoundException(\"Could not find: \" + username);\n\t\t}\n\n\t}\n\n\tprivate class MockUserDetailsServiceUserPeterAccountExpired implements UserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) {\n\t\t\tif (\"peter\".equals(username)) {\n\t\t\t\treturn new User(\"peter\", \"opal\", true, false, true, true, ROLES_12);\n\t\t\t}\n\t\t\tthrow new UsernameNotFoundException(\"Could not find: \" + username);\n\t\t}\n\n\t}\n\n\tprivate class MockUserDetailsServiceUserPeterAccountLocked implements UserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) {\n\t\t\tif (\"peter\".equals(username)) {\n\t\t\t\treturn new User(\"peter\", \"opal\", true, true, true, false, ROLES_12);\n\t\t\t}\n\t\t\tthrow new UsernameNotFoundException(\"Could not find: \" + username);\n\t\t}\n\n\t}\n\n\tprivate class MockUserDetailsServiceUserPeterCredentialsExpired implements UserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) {\n\t\t\tif (\"peter\".equals(username)) {\n\t\t\t\treturn new User(\"peter\", \"opal\", true, true, false, true, ROLES_12);\n\t\t\t}\n\t\t\tthrow new UsernameNotFoundException(\"Could not find: \" + username);\n\t\t}\n\n\t}\n\n\tprivate static class TestCompromisedPasswordChecker implements CompromisedPasswordChecker {\n\n\t\t@Override\n\t\tpublic CompromisedPasswordDecision check(String password) {\n\t\t\tif (\"password\".equals(password)) {\n\t\t\t\treturn new CompromisedPasswordDecision(true);\n\t\t\t}\n\t\t\treturn new CompromisedPasswordDecision(false);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/dao/MockUserCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.dao;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\n\npublic class MockUserCache implements UserCache {\n\n\tprivate Map<String, UserDetails> cache = new HashMap<>();\n\n\t@Override\n\tpublic UserDetails getUserFromCache(String username) {\n\t\treturn this.cache.get(username);\n\t}\n\n\t@Override\n\tpublic void putUserInCache(UserDetails user) {\n\t\tthis.cache.put(user.getUsername(), user);\n\t}\n\n\t@Override\n\tpublic void removeUserFromCache(String username) {\n\t\tthis.cache.remove(username);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/event/AuthenticationEventTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link AbstractAuthenticationEvent} and its subclasses.\n *\n * @author Ben Alex\n */\npublic class AuthenticationEventTests {\n\n\tprivate Authentication getAuthentication() {\n\t\tUsernamePasswordAuthenticationToken authentication = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(\"Principal\", \"Credentials\");\n\t\tauthentication.setDetails(\"127.0.0.1\");\n\t\treturn authentication;\n\t}\n\n\t@Test\n\tpublic void testAbstractAuthenticationEvent() {\n\t\tAuthentication auth = getAuthentication();\n\t\tAbstractAuthenticationEvent event = new AuthenticationSuccessEvent(auth);\n\t\tassertThat(event.getAuthentication()).isEqualTo(auth);\n\t}\n\n\t@Test\n\tpublic void testAbstractAuthenticationFailureEvent() {\n\t\tAuthentication auth = getAuthentication();\n\t\tAuthenticationException exception = new DisabledException(\"TEST\");\n\t\tAbstractAuthenticationFailureEvent event = new AuthenticationFailureDisabledEvent(auth, exception);\n\t\tassertThat(event.getAuthentication()).isEqualTo(auth);\n\t\tassertThat(event.getException()).isEqualTo(exception);\n\t}\n\n\t@Test\n\tpublic void testRejectsNullAuthentication() {\n\t\tAuthenticationException exception = new DisabledException(\"TEST\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AuthenticationFailureDisabledEvent(null, exception));\n\t}\n\n\t@Test\n\tpublic void testRejectsNullAuthenticationException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthenticationFailureDisabledEvent(getAuthentication(), null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/event/LoggerListenerTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.event;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\n/**\n * Tests {@link LoggerListener}.\n *\n * @author Ben Alex\n */\npublic class LoggerListenerTests {\n\n\tprivate Authentication getAuthentication() {\n\t\tUsernamePasswordAuthenticationToken authentication = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(\"Principal\", \"Credentials\");\n\t\tauthentication.setDetails(\"127.0.0.1\");\n\t\treturn authentication;\n\t}\n\n\t@Test\n\tpublic void testLogsEvents() {\n\t\tAuthenticationFailureDisabledEvent event = new AuthenticationFailureDisabledEvent(getAuthentication(),\n\t\t\t\tnew LockedException(\"TEST\"));\n\t\tLoggerListener listener = new LoggerListener();\n\t\tlistener.onApplicationEvent(event);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\nimport javax.security.auth.login.LoginException;\n\nimport org.apache.commons.logging.Log;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationFailedEvent;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationSuccessEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.session.SessionDestroyedEvent;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isA;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\npublic class DefaultJaasAuthenticationProviderTests {\n\n\tprivate DefaultJaasAuthenticationProvider provider;\n\n\tprivate UsernamePasswordAuthenticationToken token;\n\n\tprivate ApplicationEventPublisher publisher;\n\n\tprivate Log log;\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tConfiguration configuration = mock(Configuration.class);\n\t\tthis.publisher = mock(ApplicationEventPublisher.class);\n\t\tthis.log = mock(Log.class);\n\t\tthis.provider = new DefaultJaasAuthenticationProvider();\n\t\tthis.provider.setConfiguration(configuration);\n\t\tthis.provider.setApplicationEventPublisher(this.publisher);\n\t\tthis.provider.setAuthorityGranters(new AuthorityGranter[] { new TestAuthorityGranter() });\n\t\tthis.provider.afterPropertiesSet();\n\t\tAppConfigurationEntry[] aces = new AppConfigurationEntry[] {\n\t\t\t\tnew AppConfigurationEntry(TestLoginModule.class.getName(), LoginModuleControlFlag.REQUIRED,\n\t\t\t\t\t\tCollections.<String, Object>emptyMap()) };\n\t\tgiven(configuration.getAppConfigurationEntry(this.provider.getLoginContextName())).willReturn(aces);\n\t\tthis.token = UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\");\n\t\tReflectionTestUtils.setField(this.provider, \"log\", this.log);\n\t}\n\n\t@Test\n\tpublic void afterPropertiesSetNullConfiguration() throws Exception {\n\t\tthis.provider.setConfiguration(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(this.provider::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void afterPropertiesSetNullAuthorityGranters() throws Exception {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.provider.setAuthorityGranters(null));\n\t}\n\n\t@Test\n\tpublic void authenticateUnsupportedAuthentication() {\n\t\tassertThat(this.provider.authenticate(new TestingAuthenticationToken(\"user\", \"password\"))).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateSuccess() {\n\t\tAuthentication auth = this.provider.authenticate(this.token);\n\t\tassertThat(auth.getPrincipal()).isEqualTo(this.token.getPrincipal());\n\t\tassertThat(auth.getCredentials()).isEqualTo(this.token.getCredentials());\n\t\tassertThat(auth.isAuthenticated()).isEqualTo(true);\n\t\tassertThat(auth.getAuthorities().isEmpty()).isEqualTo(false);\n\t\tverify(this.publisher).publishEvent(isA(JaasAuthenticationSuccessEvent.class));\n\t\tverifyNoMoreInteractions(this.publisher);\n\t}\n\n\t@Test\n\tpublic void authenticateBadPassword() {\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(\n\t\t\t\t() -> this.provider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"asdf\")));\n\t\tverifyFailedLogin();\n\t}\n\n\t@Test\n\tpublic void authenticateBadUser() {\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(() -> this.provider\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"asdf\", \"password\")));\n\t\tverifyFailedLogin();\n\t}\n\n\t@Test\n\tpublic void logout() throws Exception {\n\t\tSessionDestroyedEvent event = mock(SessionDestroyedEvent.class);\n\t\tSecurityContext securityContext = mock(SecurityContext.class);\n\t\tJaasAuthenticationToken token = mock(JaasAuthenticationToken.class);\n\t\tLoginContext context = mock(LoginContext.class);\n\t\tgiven(event.getSecurityContexts()).willReturn(Arrays.asList(securityContext));\n\t\tgiven(securityContext.getAuthentication()).willReturn(token);\n\t\tgiven(token.getLoginContext()).willReturn(context);\n\t\tthis.provider.onApplicationEvent(event);\n\t\tverify(event).getSecurityContexts();\n\t\tverify(securityContext).getAuthentication();\n\t\tverify(token).getLoginContext();\n\t\tverify(context).logout();\n\t\tverifyNoMoreInteractions(event, securityContext, token, context);\n\t}\n\n\t@Test\n\tpublic void logoutNullSession() {\n\t\tSessionDestroyedEvent event = mock(SessionDestroyedEvent.class);\n\t\tthis.provider.handleLogout(event);\n\t\tverify(event).getSecurityContexts();\n\t\tverify(this.log).debug(anyString());\n\t\tverifyNoMoreInteractions(event);\n\t}\n\n\t@Test\n\tpublic void logoutNullAuthentication() {\n\t\tSessionDestroyedEvent event = mock(SessionDestroyedEvent.class);\n\t\tSecurityContext securityContext = mock(SecurityContext.class);\n\t\tgiven(event.getSecurityContexts()).willReturn(Arrays.asList(securityContext));\n\t\tthis.provider.handleLogout(event);\n\t\tverify(event).getSecurityContexts();\n\t\tverify(event).getSecurityContexts();\n\t\tverify(securityContext).getAuthentication();\n\t\tverifyNoMoreInteractions(event, securityContext);\n\t}\n\n\t@Test\n\tpublic void logoutNonJaasAuthentication() {\n\t\tSessionDestroyedEvent event = mock(SessionDestroyedEvent.class);\n\t\tSecurityContext securityContext = mock(SecurityContext.class);\n\t\tgiven(event.getSecurityContexts()).willReturn(Arrays.asList(securityContext));\n\t\tgiven(securityContext.getAuthentication()).willReturn(this.token);\n\t\tthis.provider.handleLogout(event);\n\t\tverify(event).getSecurityContexts();\n\t\tverify(event).getSecurityContexts();\n\t\tverify(securityContext).getAuthentication();\n\t\tverifyNoMoreInteractions(event, securityContext);\n\t}\n\n\t@Test\n\tpublic void logoutNullLoginContext() {\n\t\tSessionDestroyedEvent event = mock(SessionDestroyedEvent.class);\n\t\tSecurityContext securityContext = mock(SecurityContext.class);\n\t\tJaasAuthenticationToken token = mock(JaasAuthenticationToken.class);\n\t\tgiven(event.getSecurityContexts()).willReturn(Arrays.asList(securityContext));\n\t\tgiven(securityContext.getAuthentication()).willReturn(token);\n\t\tthis.provider.onApplicationEvent(event);\n\t\tverify(event).getSecurityContexts();\n\t\tverify(securityContext).getAuthentication();\n\t\tverify(token).getLoginContext();\n\t\tverifyNoMoreInteractions(event, securityContext, token);\n\t}\n\n\t@Test\n\tpublic void logoutLoginException() throws Exception {\n\t\tSessionDestroyedEvent event = mock(SessionDestroyedEvent.class);\n\t\tSecurityContext securityContext = mock(SecurityContext.class);\n\t\tJaasAuthenticationToken token = mock(JaasAuthenticationToken.class);\n\t\tLoginContext context = mock(LoginContext.class);\n\t\tLoginException loginException = new LoginException(\"Failed Login\");\n\t\tgiven(event.getSecurityContexts()).willReturn(Arrays.asList(securityContext));\n\t\tgiven(securityContext.getAuthentication()).willReturn(token);\n\t\tgiven(token.getLoginContext()).willReturn(context);\n\t\twillThrow(loginException).given(context).logout();\n\t\tthis.provider.onApplicationEvent(event);\n\t\tverify(event).getSecurityContexts();\n\t\tverify(securityContext).getAuthentication();\n\t\tverify(token).getLoginContext();\n\t\tverify(context).logout();\n\t\tverify(this.log).warn(anyString(), eq(loginException));\n\t\tverifyNoMoreInteractions(event, securityContext, token, context);\n\t}\n\n\t@Test\n\tpublic void publishNullPublisher() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.provider.setApplicationEventPublisher(null));\n\t}\n\n\t@Test\n\tpublic void javadocExample() {\n\t\tString resName = \"/\" + getClass().getName().replace('.', '/') + \".xml\";\n\t\tClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(resName);\n\t\ttry (context) {\n\t\t\tcontext.registerShutdownHook();\n\t\t\tthis.provider = context.getBean(DefaultJaasAuthenticationProvider.class);\n\t\t\tAuthentication auth = this.provider.authenticate(this.token);\n\t\t\tassertThat(auth.isAuthenticated()).isEqualTo(true);\n\t\t\tassertThat(auth.getPrincipal()).isEqualTo(this.token.getPrincipal());\n\t\t}\n\t}\n\n\tprivate void verifyFailedLogin() {\n\t\tArgumentCaptor<JaasAuthenticationFailedEvent> event = ArgumentCaptor\n\t\t\t.forClass(JaasAuthenticationFailedEvent.class);\n\t\tverify(this.publisher).publishEvent(event.capture());\n\t\tassertThat(event.getValue()).isInstanceOf(JaasAuthenticationFailedEvent.class);\n\t\tassertThat(event.getValue().getException()).isNotNull();\n\t\tverifyNoMoreInteractions(this.publisher);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/JaasAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.PrintWriter;\nimport java.net.URL;\nimport java.security.Security;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Set;\n\nimport javax.security.auth.login.LoginContext;\nimport javax.security.auth.login.LoginException;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.session.SessionDestroyedEvent;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for the JaasAuthenticationProvider\n *\n * @author Ray Krueger\n */\npublic class JaasAuthenticationProviderTests {\n\n\tprivate ApplicationContext context;\n\n\tprivate JaasAuthenticationProvider jaasProvider;\n\n\tprivate JaasEventCheck eventCheck;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tString resName = \"/\" + getClass().getName().replace('.', '/') + \".xml\";\n\t\tthis.context = new ClassPathXmlApplicationContext(resName);\n\t\tthis.eventCheck = (JaasEventCheck) this.context.getBean(\"eventCheck\");\n\t\tthis.jaasProvider = (JaasAuthenticationProvider) this.context.getBean(\"jaasAuthenticationProvider\");\n\t}\n\n\t@Test\n\tpublic void testBadPassword() {\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(() -> this.jaasProvider\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"asdf\")));\n\t\tassertThat(this.eventCheck.failedEvent).as(\"Failure event not fired\").isNotNull();\n\t\tassertThat(this.eventCheck.failedEvent.getException()).withFailMessage(\"Failure event exception was null\")\n\t\t\t.isNotNull();\n\t\tassertThat(this.eventCheck.successEvent).as(\"Success event was fired\").isNull();\n\t}\n\n\t@Test\n\tpublic void testBadUser() {\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(() -> this.jaasProvider\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"asdf\", \"password\")));\n\t\tassertThat(this.eventCheck.failedEvent).as(\"Failure event not fired\").isNotNull();\n\t\tassertThat(this.eventCheck.failedEvent.getException()).withFailMessage(\"Failure event exception was null\")\n\t\t\t.isNotNull();\n\t\tassertThat(this.eventCheck.successEvent).as(\"Success event was fired\").isNull();\n\t}\n\n\t@Test\n\tpublic void testConfigurationLoop() throws Exception {\n\t\tString resName = \"/\" + getClass().getName().replace('.', '/') + \".conf\";\n\t\tURL url = getClass().getResource(resName);\n\t\tSecurity.setProperty(\"login.config.url.1\", url.toString());\n\t\tsetUp();\n\t\ttestFull();\n\t}\n\n\t@Test\n\tpublic void detectsMissingLoginConfig() throws Exception {\n\t\tJaasAuthenticationProvider myJaasProvider = new JaasAuthenticationProvider();\n\t\tmyJaasProvider.setApplicationEventPublisher(this.context);\n\t\tmyJaasProvider.setAuthorityGranters(this.jaasProvider.getAuthorityGranters());\n\t\tmyJaasProvider.setCallbackHandlers(this.jaasProvider.getCallbackHandlers());\n\t\tmyJaasProvider.setLoginContextName(this.jaasProvider.getLoginContextName());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> myJaasProvider.afterPropertiesSet())\n\t\t\t.withMessageStartingWith(\"loginConfig must be set on\");\n\t}\n\n\t// SEC-1239\n\t@Test\n\tpublic void spacesInLoginConfigPathAreAccepted() throws Exception {\n\t\tFile configFile;\n\t\t// Create temp directory with a space in the name\n\t\tFile configDir = new File(System.getProperty(\"java.io.tmpdir\") + File.separator + \"jaas test\");\n\t\tconfigDir.deleteOnExit();\n\t\tif (configDir.exists()) {\n\t\t\tconfigDir.delete();\n\t\t}\n\t\tconfigDir.mkdir();\n\t\tconfigFile = File.createTempFile(\"login\", \"conf\", configDir);\n\t\tconfigFile.deleteOnExit();\n\t\tFileOutputStream fos = new FileOutputStream(configFile);\n\t\tPrintWriter pw = new PrintWriter(fos);\n\t\tpw.append(\n\t\t\t\t\"JAASTestBlah {\" + \"org.springframework.security.authentication.jaas.TestLoginModule required;\" + \"};\");\n\t\tpw.flush();\n\t\tpw.close();\n\t\tJaasAuthenticationProvider myJaasProvider = new JaasAuthenticationProvider();\n\t\tmyJaasProvider.setApplicationEventPublisher(this.context);\n\t\tmyJaasProvider.setLoginConfig(new FileSystemResource(configFile));\n\t\tmyJaasProvider.setAuthorityGranters(this.jaasProvider.getAuthorityGranters());\n\t\tmyJaasProvider.setCallbackHandlers(this.jaasProvider.getCallbackHandlers());\n\t\tmyJaasProvider.setLoginContextName(this.jaasProvider.getLoginContextName());\n\t\tmyJaasProvider.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void detectsMissingLoginContextName() throws Exception {\n\t\tJaasAuthenticationProvider myJaasProvider = new JaasAuthenticationProvider();\n\t\tmyJaasProvider.setApplicationEventPublisher(this.context);\n\t\tmyJaasProvider.setAuthorityGranters(this.jaasProvider.getAuthorityGranters());\n\t\tmyJaasProvider.setCallbackHandlers(this.jaasProvider.getCallbackHandlers());\n\t\tmyJaasProvider.setLoginConfig(this.jaasProvider.getLoginConfig());\n\t\tmyJaasProvider.setLoginContextName(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(myJaasProvider::afterPropertiesSet)\n\t\t\t.withMessageStartingWith(\"loginContextName must be set on\");\n\t\tmyJaasProvider.setLoginContextName(\"\");\n\t\tassertThatIllegalArgumentException().isThrownBy(myJaasProvider::afterPropertiesSet)\n\t\t\t.withMessageStartingWith(\"loginContextName must be set on\");\n\t}\n\n\t@Test\n\tpublic void testFull() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"user\",\n\t\t\t\t\"password\", AuthorityUtils.createAuthorityList(\"ROLE_ONE\"));\n\t\tassertThat(this.jaasProvider.supports(UsernamePasswordAuthenticationToken.class)).isTrue();\n\t\tAuthentication auth = this.jaasProvider.authenticate(token);\n\t\tassertThat(this.jaasProvider.getAuthorityGranters()).isNotNull();\n\t\tassertThat(this.jaasProvider.getCallbackHandlers()).isNotNull();\n\t\tassertThat(this.jaasProvider.getLoginConfig()).isNotNull();\n\t\tassertThat(this.jaasProvider.getLoginContextName()).isNotNull();\n\t\tCollection<? extends GrantedAuthority> list = auth.getAuthorities();\n\t\tSet<String> set = AuthorityUtils.authorityListToSet(list);\n\t\tassertThat(set.contains(\"ROLE_ONE\")).withFailMessage(\"GrantedAuthorities should not contain ROLE_ONE\")\n\t\t\t.isFalse();\n\t\tassertThat(set.contains(\"ROLE_TEST1\")).withFailMessage(\"GrantedAuthorities should contain ROLE_TEST1\").isTrue();\n\t\tassertThat(set.contains(\"ROLE_TEST2\")).withFailMessage(\"GrantedAuthorities should contain ROLE_TEST2\").isTrue();\n\t\tboolean foundit = false;\n\t\tfor (GrantedAuthority a : list) {\n\t\t\tif (a instanceof JaasGrantedAuthority grant) {\n\t\t\t\tassertThat(grant.getPrincipal()).withFailMessage(\"Principal was null on JaasGrantedAuthority\")\n\t\t\t\t\t.isNotNull();\n\t\t\t\tfoundit = true;\n\t\t\t}\n\t\t}\n\t\tassertThat(foundit).as(\"Could not find a JaasGrantedAuthority\").isTrue();\n\t\tassertThat(this.eventCheck.successEvent).as(\"Success event should be fired\").isNotNull();\n\t\tassertThat(this.eventCheck.successEvent.getAuthentication()).withFailMessage(\"Auth objects should be equal\")\n\t\t\t.isEqualTo(auth);\n\t\tassertThat(this.eventCheck.failedEvent).as(\"Failure event should not be fired\").isNull();\n\t}\n\n\t@Test\n\tpublic void testGetApplicationEventPublisher() {\n\t\tassertThat(this.jaasProvider.getApplicationEventPublisher()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testLoginExceptionResolver() {\n\t\tassertThat(this.jaasProvider.getLoginExceptionResolver()).isNotNull();\n\t\tthis.jaasProvider.setLoginExceptionResolver((e) -> new LockedException(\"This is just a test!\"));\n\t\ttry {\n\t\t\tthis.jaasProvider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\t}\n\t\tcatch (LockedException ex) {\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tfail(\"LockedException should have been thrown and caught\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testLogout() throws Exception {\n\t\tMockLoginContext loginContext = new MockLoginContext(this.jaasProvider.getLoginContextName());\n\t\tJaasAuthenticationToken token = new JaasAuthenticationToken(null, null, loginContext);\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tcontext.setAuthentication(token);\n\t\tSessionDestroyedEvent event = mock(SessionDestroyedEvent.class);\n\t\tgiven(event.getSecurityContexts()).willReturn(Arrays.asList(context));\n\t\tthis.jaasProvider.handleLogout(event);\n\t\tassertThat(loginContext.loggedOut).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNullDefaultAuthorities() {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"user\",\n\t\t\t\t\"password\");\n\t\tassertThat(this.jaasProvider.supports(UsernamePasswordAuthenticationToken.class)).isTrue();\n\t\tAuthentication auth = this.jaasProvider.authenticate(token);\n\t\tSecurityAssertions.assertThat(auth)\n\t\t\t.roles()\n\t\t\t.withFailMessage(\"Only ROLE_TEST1 and ROLE_TEST2 should have been returned\")\n\t\t\t.hasSize(2);\n\t}\n\n\t@Test\n\tpublic void testUnsupportedAuthenticationObjectReturnsNull() {\n\t\tassertThat(this.jaasProvider\n\t\t\t.authenticate(new TestingAuthenticationToken(\"foo\", \"bar\", AuthorityUtils.NO_AUTHORITIES))).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSuccessThenIssuesFactor() {\n\t\tUsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(\"user\", \"password\");\n\t\tAuthentication result = this.jaasProvider.authenticate(token);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t}\n\n\tprivate static class MockLoginContext extends LoginContext {\n\n\t\tboolean loggedOut = false;\n\n\t\tMockLoginContext(String loginModule) throws LoginException {\n\t\t\tsuper(loginModule);\n\t\t}\n\n\t\t@Override\n\t\tpublic void logout() {\n\t\t\tthis.loggedOut = true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/JaasAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.util.Set;\n\nimport javax.security.auth.login.LoginContext;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nclass JaasAuthenticationTokenTests {\n\n\t@Test\n\tvoid toBuilderWhenApplyThenCopies() {\n\t\tJaasAuthenticationToken factorOne = new JaasAuthenticationToken(\"alice\", \"pass\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"), mock(LoginContext.class));\n\t\tJaasAuthenticationToken factorTwo = new JaasAuthenticationToken(\"bob\", \"ssap\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"), mock(LoginContext.class));\n\t\tJaasAuthenticationToken result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.credentials(factorTwo.getCredentials())\n\t\t\t.loginContext(factorTwo.getLoginContext())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(result.getCredentials()).isSameAs(factorTwo.getCredentials());\n\t\tassertThat(result.getLoginContext()).isSameAs(factorTwo.getLoginContext());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/JaasEventCheck.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationEvent;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationFailedEvent;\nimport org.springframework.security.authentication.jaas.event.JaasAuthenticationSuccessEvent;\n\n/**\n * @author Ray Krueger\n */\npublic class JaasEventCheck implements ApplicationListener<JaasAuthenticationEvent> {\n\n\tJaasAuthenticationFailedEvent failedEvent;\n\n\tJaasAuthenticationSuccessEvent successEvent;\n\n\t@Override\n\tpublic void onApplicationEvent(JaasAuthenticationEvent event) {\n\t\tif (event instanceof JaasAuthenticationFailedEvent) {\n\t\t\tthis.failedEvent = (JaasAuthenticationFailedEvent) event;\n\t\t}\n\t\tif (event instanceof JaasAuthenticationSuccessEvent) {\n\t\t\tthis.successEvent = (JaasAuthenticationSuccessEvent) event;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/JaasGrantedAuthorityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Clement Ng\n *\n */\npublic class JaasGrantedAuthorityTests {\n\n\t@Test\n\tpublic void authorityWithNullRoleFailsAssertion() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new JaasGrantedAuthority(null, null))\n\t\t\t.withMessageContaining(\"role cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorityWithNullPrincipleFailsAssertion() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new JaasGrantedAuthority(\"role\", null))\n\t\t\t.withMessageContaining(\"principal cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/Sec760Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests bug reported in SEC-760.\n *\n * @author Ben Alex\n *\n */\npublic class Sec760Tests {\n\n\tpublic String resolveConfigFile(String filename) {\n\t\tString resName = \"/\" + getClass().getPackage().getName().replace('.', '/') + filename;\n\t\treturn resName;\n\t}\n\n\tprivate void testConfigureJaasCase(JaasAuthenticationProvider p1, JaasAuthenticationProvider p2) throws Exception {\n\t\tp1.setLoginConfig(new ClassPathResource(resolveConfigFile(\"/test1.conf\")));\n\t\tp1.setLoginContextName(\"test1\");\n\t\tp1.setCallbackHandlers(new JaasAuthenticationCallbackHandler[] { new TestCallbackHandler(),\n\t\t\t\tnew JaasNameCallbackHandler(), new JaasPasswordCallbackHandler() });\n\t\tp1.setAuthorityGranters(new AuthorityGranter[] { new TestAuthorityGranter() });\n\t\tp1.afterPropertiesSet();\n\t\ttestAuthenticate(p1);\n\t\tp2.setLoginConfig(new ClassPathResource(resolveConfigFile(\"/test2.conf\")));\n\t\tp2.setLoginContextName(\"test2\");\n\t\tp2.setCallbackHandlers(new JaasAuthenticationCallbackHandler[] { new TestCallbackHandler(),\n\t\t\t\tnew JaasNameCallbackHandler(), new JaasPasswordCallbackHandler() });\n\t\tp2.setAuthorityGranters(new AuthorityGranter[] { new TestAuthorityGranter() });\n\t\tp2.afterPropertiesSet();\n\t\ttestAuthenticate(p2);\n\t}\n\n\tprivate void testAuthenticate(JaasAuthenticationProvider p1) {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(\"user\",\n\t\t\t\t\"password\", AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tAuthentication auth = p1.authenticate(token);\n\t\tassertThat(auth).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testConfigureJaas() throws Exception {\n\t\ttestConfigureJaasCase(new JaasAuthenticationProvider(), new JaasAuthenticationProvider());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/SecurityContextLoginModuleTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.login.LoginException;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests SecurityContextLoginModule\n *\n * @author Ray Krueger\n */\npublic class SecurityContextLoginModuleTests {\n\n\tprivate SecurityContextLoginModule module = null;\n\n\tprivate Subject subject = new Subject(false, new HashSet<>(), new HashSet<>(), new HashSet<>());\n\n\tprivate UsernamePasswordAuthenticationToken auth = UsernamePasswordAuthenticationToken.unauthenticated(\"principal\",\n\t\t\t\"credentials\");\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.module = new SecurityContextLoginModule();\n\t\tthis.module.initialize(this.subject, null, null, null);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.module = null;\n\t}\n\n\t@Test\n\tpublic void testAbort() throws Exception {\n\t\tassertThat(this.module.abort()).as(\"Should return false, no auth is set\").isFalse();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tthis.module.login();\n\t\tthis.module.commit();\n\t\tassertThat(this.module.abort()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testLoginException() {\n\t\tassertThatExceptionOfType(LoginException.class).isThrownBy(this.module::login);\n\t}\n\n\t@Test\n\tpublic void testLoginSuccess() throws Exception {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tassertThat(this.module.login()).as(\"Login should succeed, there is an authentication set\").isTrue();\n\t\tassertThat(this.module.commit()).withFailMessage(\"The authentication is not null, this should return true\")\n\t\t\t.isTrue();\n\t\tassertThat(this.subject.getPrincipals().contains(this.auth))\n\t\t\t.withFailMessage(\"Principals should contain the authentication\")\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void loginWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(securityContextHolderStrategy.getContext()).willReturn(new SecurityContextImpl(this.auth));\n\t\tthis.module.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tassertThat(this.module.login()).as(\"Login should succeed, there is an authentication set\").isTrue();\n\t\tassertThat(this.module.commit()).withFailMessage(\"The authentication is not null, this should return true\")\n\t\t\t.isTrue();\n\t\tassertThat(this.subject.getPrincipals().contains(this.auth))\n\t\t\t.withFailMessage(\"Principals should contain the authentication\")\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void testLogout() throws Exception {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tthis.module.login();\n\t\tassertThat(this.module.logout()).as(\"Should return true as it succeeds\").isTrue();\n\t\tassertThat(this.module.getAuthentication()).as(\"Authentication should be null\").isNull();\n\t\tassertThat(this.subject.getPrincipals().contains(this.auth))\n\t\t\t.withFailMessage(\"Principals should not contain the authentication after logout\")\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void testNullAuthenticationInSecurityContext() {\n\t\tSecurityContextHolder.getContext().setAuthentication(null);\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(this.module::login);\n\t}\n\n\t@Test\n\tpublic void testNullAuthenticationInSecurityContextIgnored() throws Exception {\n\t\tthis.module = new SecurityContextLoginModule();\n\t\tMap<String, String> options = new HashMap<>();\n\t\toptions.put(\"ignoreMissingAuthentication\", \"true\");\n\t\tthis.module.initialize(this.subject, null, null, options);\n\t\tSecurityContextHolder.getContext().setAuthentication(null);\n\t\tassertThat(this.module.login()).as(\"Should return false and ask to be ignored\").isFalse();\n\t}\n\n\t@Test\n\tpublic void testNullLogout() throws Exception {\n\t\tassertThat(this.module.logout()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/TestAuthorityGranter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.security.Principal;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author Ray Krueger\n */\npublic class TestAuthorityGranter implements AuthorityGranter {\n\n\t@Override\n\tpublic Set<String> grant(Principal principal) {\n\t\tSet<String> rtnSet = new HashSet<>();\n\t\tif (principal.getName().equals(\"TEST_PRINCIPAL\")) {\n\t\t\trtnSet.add(\"ROLE_TEST1\");\n\t\t\trtnSet.add(\"ROLE_TEST2\");\n\t\t}\n\t\treturn rtnSet;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/TestCallbackHandler.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.TextInputCallback;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * TestCallbackHandler\n *\n * @author Ray Krueger\n */\npublic class TestCallbackHandler implements JaasAuthenticationCallbackHandler {\n\n\t@Override\n\tpublic void handle(Callback callback, Authentication auth) {\n\t\tif (callback instanceof TextInputCallback tic) {\n\t\t\ttic.setText(auth.getPrincipal().toString());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/TestLoginModule.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas;\n\nimport java.util.Map;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.PasswordCallback;\nimport javax.security.auth.callback.TextInputCallback;\nimport javax.security.auth.login.LoginException;\nimport javax.security.auth.spi.LoginModule;\n\n/**\n * @author Ray Krueger\n */\npublic class TestLoginModule implements LoginModule {\n\n\tprivate String password;\n\n\tprivate String user;\n\n\tprivate Subject subject;\n\n\t@Override\n\tpublic boolean abort() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean commit() {\n\t\treturn true;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {\n\t\tthis.subject = subject;\n\t\ttry {\n\t\t\tTextInputCallback textCallback = new TextInputCallback(\"prompt\");\n\t\t\tNameCallback nameCallback = new NameCallback(\"prompt\");\n\t\t\tPasswordCallback passwordCallback = new PasswordCallback(\"prompt\", false);\n\t\t\tcallbackHandler.handle(new Callback[] { textCallback, nameCallback, passwordCallback });\n\t\t\tthis.password = new String(passwordCallback.getPassword());\n\t\t\tthis.user = nameCallback.getName();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean login() throws LoginException {\n\t\tif (!this.user.equals(\"user\")) {\n\t\t\tthrow new LoginException(\"Bad User\");\n\t\t}\n\t\tif (!this.password.equals(\"password\")) {\n\t\t\tthrow new LoginException(\"Bad Password\");\n\t\t}\n\t\tthis.subject.getPrincipals().add(() -> \"TEST_PRINCIPAL\");\n\t\tthis.subject.getPrincipals().add(() -> \"NULL_PRINCIPAL\");\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean logout() {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/jaas/memory/InMemoryConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.jaas.memory;\n\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.jaas.TestLoginModule;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link InMemoryConfiguration}.\n *\n * @author Rob Winch\n */\npublic class InMemoryConfigurationTests {\n\n\tprivate AppConfigurationEntry[] defaultEntries;\n\n\tprivate Map<String, AppConfigurationEntry[]> mappedEntries;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.defaultEntries = new AppConfigurationEntry[] { new AppConfigurationEntry(TestLoginModule.class.getName(),\n\t\t\t\tLoginModuleControlFlag.REQUIRED, Collections.<String, Object>emptyMap()) };\n\t\tthis.mappedEntries = Collections.<String, AppConfigurationEntry[]>singletonMap(\"name\",\n\t\t\t\tnew AppConfigurationEntry[] { new AppConfigurationEntry(TestLoginModule.class.getName(),\n\t\t\t\t\t\tLoginModuleControlFlag.OPTIONAL, Collections.<String, Object>emptyMap()) });\n\t}\n\n\t@Test\n\tpublic void constructorNullDefault() {\n\t\tassertThat(new InMemoryConfiguration((AppConfigurationEntry[]) null).getAppConfigurationEntry(\"name\")).isNull();\n\t}\n\n\t@Test\n\tpublic void constructorNullMapped() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryConfiguration((Map<String, AppConfigurationEntry[]>) null));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyMap() {\n\t\tassertThat(new InMemoryConfiguration(Collections.<String, AppConfigurationEntry[]>emptyMap())\n\t\t\t.getAppConfigurationEntry(\"name\")).isNull();\n\t}\n\n\t@Test\n\tpublic void constructorEmptyMapNullDefault() {\n\t\tassertThat(new InMemoryConfiguration(Collections.<String, AppConfigurationEntry[]>emptyMap(), null)\n\t\t\t.getAppConfigurationEntry(\"name\")).isNull();\n\t}\n\n\t@Test\n\tpublic void constructorNullMapNullDefault() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new InMemoryConfiguration(null, null));\n\t}\n\n\t@Test\n\tpublic void nonnullDefault() {\n\t\tInMemoryConfiguration configuration = new InMemoryConfiguration(this.defaultEntries);\n\t\tassertThat(configuration.getAppConfigurationEntry(\"name\")).isEqualTo(this.defaultEntries);\n\t}\n\n\t@Test\n\tpublic void mappedNonnullDefault() {\n\t\tInMemoryConfiguration configuration = new InMemoryConfiguration(this.mappedEntries, this.defaultEntries);\n\t\tassertThat(this.defaultEntries).isEqualTo(configuration.getAppConfigurationEntry(\"missing\"));\n\t\tassertThat(this.mappedEntries).containsEntry(\"name\", configuration.getAppConfigurationEntry(\"name\"));\n\t}\n\n\t@Test\n\tpublic void jdk5Compatable() throws Exception {\n\t\tMethod method = InMemoryConfiguration.class.getDeclaredMethod(\"refresh\");\n\t\tassertThat(method.getDeclaringClass()).isEqualTo(InMemoryConfiguration.class);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ott/InMemoryOneTimeTokenServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.time.ZoneOffset;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.UUID;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Tests for {@link InMemoryOneTimeTokenService}\n *\n * @author Marcus da Coregio\n */\nclass InMemoryOneTimeTokenServiceTests {\n\n\tInMemoryOneTimeTokenService oneTimeTokenService = new InMemoryOneTimeTokenService();\n\n\t@Test\n\tvoid generateThenTokenValueShouldBeValidUuidAndProvidedUsernameIsUsed() {\n\t\tGenerateOneTimeTokenRequest request = new GenerateOneTimeTokenRequest(\"user\");\n\t\tOneTimeToken oneTimeToken = this.oneTimeTokenService.generate(request);\n\t\tassertThatNoException().isThrownBy(() -> UUID.fromString(oneTimeToken.getTokenValue()));\n\t\tassertThat(request.getUsername()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenDoesNotExistsThenNull() {\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(\"123\");\n\t\tOneTimeToken oneTimeToken = this.oneTimeTokenService.consume(authenticationToken);\n\t\tassertThat(oneTimeToken).isNull();\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenExistsThenReturnItself() {\n\t\tGenerateOneTimeTokenRequest request = new GenerateOneTimeTokenRequest(\"user\");\n\t\tOneTimeToken generated = this.oneTimeTokenService.generate(request);\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(\n\t\t\t\tgenerated.getTokenValue());\n\t\tOneTimeToken consumed = this.oneTimeTokenService.consume(authenticationToken);\n\t\tassertThat(consumed.getTokenValue()).isEqualTo(generated.getTokenValue());\n\t\tassertThat(consumed.getUsername()).isEqualTo(generated.getUsername());\n\t\tassertThat(consumed.getExpiresAt()).isEqualTo(generated.getExpiresAt());\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenIsExpiredThenReturnNull() {\n\t\tGenerateOneTimeTokenRequest request = new GenerateOneTimeTokenRequest(\"user\");\n\t\tOneTimeToken generated = this.oneTimeTokenService.generate(request);\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(\n\t\t\t\tgenerated.getTokenValue());\n\t\tClock tenMinutesFromNow = Clock.fixed(Instant.now().plus(10, ChronoUnit.MINUTES), ZoneOffset.UTC);\n\t\tthis.oneTimeTokenService.setClock(tenMinutesFromNow);\n\t\tOneTimeToken consumed = this.oneTimeTokenService.consume(authenticationToken);\n\t\tassertThat(consumed).isNull();\n\t}\n\n\t@Test\n\tvoid generateWhenMoreThan100TokensThenClearExpired() {\n\t\t// @formatter:off\n\t\tList<OneTimeToken> toExpire = generate(50); // 50 tokens will expire in 5 minutes from now\n\t\tClock twoMinutesFromNow = Clock.fixed(Instant.now().plus(2, ChronoUnit.MINUTES), ZoneOffset.UTC);\n\t\tthis.oneTimeTokenService.setClock(twoMinutesFromNow);\n\t\tList<OneTimeToken> toKeep = generate(50); // 50 tokens will expire in 7 minutes from now\n\t\tClock sixMinutesFromNow = Clock.fixed(Instant.now().plus(6, ChronoUnit.MINUTES), ZoneOffset.UTC);\n\t\tthis.oneTimeTokenService.setClock(sixMinutesFromNow);\n\n\t\tassertThat(toExpire)\n\t\t\t.extracting(\n\t\t\t\t\t(token) -> this.oneTimeTokenService.consume(new OneTimeTokenAuthenticationToken(token.getTokenValue())))\n\t\t\t.containsOnlyNulls();\n\n\t\tassertThat(toKeep)\n\t\t\t.extracting(\n\t\t\t\t\t(token) -> this.oneTimeTokenService.consume(new OneTimeTokenAuthenticationToken(token.getTokenValue())))\n\t\t\t.noneMatch(Objects::isNull);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.oneTimeTokenService.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\tprivate List<OneTimeToken> generate(int howMany) {\n\t\tList<OneTimeToken> generated = new ArrayList<>(howMany);\n\t\tfor (int i = 0; i < howMany; i++) {\n\t\t\tOneTimeToken oneTimeToken = this.oneTimeTokenService\n\t\t\t\t.generate(new GenerateOneTimeTokenRequest(\"generated\" + i));\n\t\t\tgenerated.add(oneTimeToken);\n\t\t}\n\t\treturn generated;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ott/JdbcOneTimeTokenServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneOffset;\nimport java.time.temporal.ChronoUnit;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link JdbcOneTimeTokenService}.\n *\n * @author Max Batischev\n */\nclass JdbcOneTimeTokenServiceTests {\n\n\tprivate static final String USERNAME = \"user\";\n\n\tprivate static final String TOKEN_VALUE = \"1234\";\n\n\tprivate static final String ONE_TIME_TOKEN_SQL_RESOURCE = \"org/springframework/security/core/ott/jdbc/one-time-tokens-schema.sql\";\n\n\tprivate EmbeddedDatabase db;\n\n\tprivate JdbcOperations jdbcOperations;\n\n\tprivate JdbcOneTimeTokenService oneTimeTokenService;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.db = createDb();\n\t\tthis.jdbcOperations = new JdbcTemplate(this.db);\n\t\tthis.oneTimeTokenService = new JdbcOneTimeTokenService(this.jdbcOperations);\n\t}\n\n\t@AfterEach\n\tvoid tearDown() throws Exception {\n\t\tthis.db.shutdown();\n\t\tthis.oneTimeTokenService.destroy();\n\t}\n\n\tprivate static EmbeddedDatabase createDb() {\n\t\t// @formatter:off\n\t\treturn new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(ONE_TIME_TOKEN_SQL_RESOURCE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new JdbcOneTimeTokenService(null))\n\t\t\t\t.withMessage(\"jdbcOperations cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid generateWhenGenerateOneTimeTokenRequestIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.oneTimeTokenService.generate(null))\n\t\t\t\t.withMessage(\"generateOneTimeTokenRequest cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid consumeWhenAuthenticationTokenIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.oneTimeTokenService.consume(null))\n\t\t\t\t.withMessage(\"authenticationToken cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid generateThenTokenValueShouldBeValidUuidAndProvidedUsernameIsUsed() {\n\t\tOneTimeToken oneTimeToken = this.oneTimeTokenService.generate(new GenerateOneTimeTokenRequest(USERNAME));\n\n\t\tOneTimeToken persistedOneTimeToken = this.oneTimeTokenService\n\t\t\t.consume(new OneTimeTokenAuthenticationToken(oneTimeToken.getTokenValue()));\n\t\tassertThat(persistedOneTimeToken).isNotNull();\n\t\tassertThat(persistedOneTimeToken.getUsername()).isNotNull();\n\t\tassertThat(persistedOneTimeToken.getTokenValue()).isNotNull();\n\t\tassertThat(persistedOneTimeToken.getExpiresAt()).isNotNull();\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenExistsThenReturnItself() {\n\t\tOneTimeToken oneTimeToken = this.oneTimeTokenService.generate(new GenerateOneTimeTokenRequest(USERNAME));\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(\n\t\t\t\toneTimeToken.getTokenValue());\n\n\t\tOneTimeToken consumedOneTimeToken = this.oneTimeTokenService.consume(authenticationToken);\n\n\t\tassertThat(consumedOneTimeToken).isNotNull();\n\t\tassertThat(consumedOneTimeToken.getUsername()).isNotNull();\n\t\tassertThat(consumedOneTimeToken.getTokenValue()).isNotNull();\n\t\tassertThat(consumedOneTimeToken.getExpiresAt()).isNotNull();\n\t\tOneTimeToken persistedOneTimeToken = this.oneTimeTokenService\n\t\t\t.consume(new OneTimeTokenAuthenticationToken(consumedOneTimeToken.getTokenValue()));\n\t\tassertThat(persistedOneTimeToken).isNull();\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenDoesNotExistsThenReturnNull() {\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(TOKEN_VALUE);\n\n\t\tOneTimeToken consumedOneTimeToken = this.oneTimeTokenService.consume(authenticationToken);\n\n\t\tassertThat(consumedOneTimeToken).isNull();\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenIsExpiredThenReturnNull() {\n\t\tGenerateOneTimeTokenRequest request = new GenerateOneTimeTokenRequest(USERNAME);\n\t\tOneTimeToken generated = this.oneTimeTokenService.generate(request);\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(\n\t\t\t\tgenerated.getTokenValue());\n\t\tClock tenMinutesFromNow = Clock.fixed(Instant.now().plus(10, ChronoUnit.MINUTES), ZoneOffset.UTC);\n\t\tthis.oneTimeTokenService.setClock(tenMinutesFromNow);\n\n\t\tOneTimeToken consumed = this.oneTimeTokenService.consume(authenticationToken);\n\t\tassertThat(consumed).isNull();\n\t}\n\n\t@Test\n\tvoid cleanupExpiredTokens() {\n\t\tClock clock = mock(Clock.class);\n\t\tInstant tenMinutesAgo = Instant.now().minus(Duration.ofMinutes(10));\n\t\tgiven(clock.instant()).willReturn(tenMinutesAgo);\n\t\tthis.oneTimeTokenService.setClock(clock);\n\t\tOneTimeToken token1 = this.oneTimeTokenService.generate(new GenerateOneTimeTokenRequest(USERNAME));\n\t\tOneTimeToken token2 = this.oneTimeTokenService.generate(new GenerateOneTimeTokenRequest(USERNAME));\n\n\t\tthis.oneTimeTokenService.cleanupExpiredTokens();\n\n\t\tOneTimeToken deletedOneTimeToken1 = this.oneTimeTokenService\n\t\t\t.consume(new OneTimeTokenAuthenticationToken(token1.getTokenValue()));\n\t\tOneTimeToken deletedOneTimeToken2 = this.oneTimeTokenService\n\t\t\t.consume(new OneTimeTokenAuthenticationToken(token2.getTokenValue()));\n\t\tassertThat(deletedOneTimeToken1).isNull();\n\t\tassertThat(deletedOneTimeToken2).isNull();\n\t}\n\n\t@Test\n\tvoid setCleanupChronWhenNullThenNoException() {\n\t\tthis.oneTimeTokenService.setCleanupCron(null);\n\t}\n\n\t@Test\n\tvoid setCleanupChronWhenAlreadyNullThenNoException() {\n\t\tthis.oneTimeTokenService.setCleanupCron(null);\n\t\tthis.oneTimeTokenService.setCleanupCron(null);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.time.Instant;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.util.CollectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * Tests for {@link OneTimeTokenAuthenticationProvider}.\n *\n * @author Max Batischev\n */\n@ExtendWith(MockitoExtension.class)\npublic class OneTimeTokenAuthenticationProviderTests {\n\n\tprivate static final String TOKEN = \"token\";\n\n\tprivate static final String USERNAME = \"Max\";\n\n\tprivate static final String PASSWORD = \"password\";\n\n\t@Mock\n\tprivate OneTimeTokenService oneTimeTokenService;\n\n\t@Mock\n\tprivate UserDetailsService userDetailsService;\n\n\t@InjectMocks\n\tprivate OneTimeTokenAuthenticationProvider provider;\n\n\t@Test\n\tvoid authenticateWhenAuthenticationTokenIsPresentThenAuthenticates() {\n\t\tgiven(this.oneTimeTokenService.consume(any()))\n\t\t\t.willReturn(new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now().plusSeconds(120)));\n\t\tgiven(this.userDetailsService.loadUserByUsername(anyString()))\n\t\t\t.willReturn(new User(USERNAME, PASSWORD, List.of()));\n\t\tOneTimeTokenAuthenticationToken token = new OneTimeTokenAuthenticationToken(TOKEN);\n\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\n\t\tUser user = (User) authentication.getPrincipal();\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\tassertThat(user.getUsername()).isEqualTo(USERNAME);\n\t\tassertThat(user.getPassword()).isEqualTo(PASSWORD);\n\t\tassertThat(CollectionUtils.isEmpty(user.getAuthorities())).isTrue();\n\t}\n\n\t@Test\n\tvoid authenticateWhenOneTimeTokenIsNotFoundThenFails() {\n\t\tgiven(this.oneTimeTokenService.consume(any())).willReturn(null);\n\t\tOneTimeTokenAuthenticationToken token = new OneTimeTokenAuthenticationToken(TOKEN);\n\n\t\tassertThatExceptionOfType(InvalidOneTimeTokenException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token));\n\t}\n\n\t@Test\n\tvoid authenticateWhenUserIsNotFoundThenFails() {\n\t\tgiven(this.oneTimeTokenService.consume(any()))\n\t\t\t.willReturn(new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now().plusSeconds(120)));\n\t\tgiven(this.userDetailsService.loadUserByUsername(anyString())).willThrow(UsernameNotFoundException.class);\n\t\tOneTimeTokenAuthenticationToken token = new OneTimeTokenAuthenticationToken(TOKEN);\n\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(token));\n\t}\n\n\t@Test\n\tvoid authenticateWhenSuccessThenIssuesFactor() {\n\t\tgiven(this.oneTimeTokenService.consume(any()))\n\t\t\t.willReturn(new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now().plusSeconds(120)));\n\t\tgiven(this.userDetailsService.loadUserByUsername(anyString()))\n\t\t\t.willReturn(new User(USERNAME, PASSWORD, List.of()));\n\t\tOneTimeTokenAuthenticationToken token = new OneTimeTokenAuthenticationToken(TOKEN);\n\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\t\tSecurityAssertions.assertThat(authentication).hasAuthority(FactorGrantedAuthority.OTT_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid constructorWhenOneTimeTokenServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OneTimeTokenAuthenticationProvider(null, this.userDetailsService))\n\t\t\t\t.withMessage(\"oneTimeTokenService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid constructorWhenUserDetailsServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OneTimeTokenAuthenticationProvider(this.oneTimeTokenService, null))\n\t\t\t\t.withMessage(\"userDetailsService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson.SecurityJacksonModules;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass OneTimeTokenAuthenticationTests {\n\n\t@Test\n\tvoid toBuilderWhenApplyThenCopies() {\n\t\tOneTimeTokenAuthentication factorOne = new OneTimeTokenAuthentication(\"alice\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"));\n\t\tOneTimeTokenAuthentication factorTwo = new OneTimeTokenAuthentication(\"bob\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"));\n\t\tOneTimeTokenAuthentication result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n\t// gh-18095\n\t@Test\n\tvoid shouldBeAbleToDeserializeFromJsonWithDefaultTypingActivated() {\n\t\tJsonMapper mapper = JsonMapper.builder()\n\t\t\t.addModules(SecurityJacksonModules.getModules(getClass().getClassLoader()))\n\t\t\t.build();\n\t\tOneTimeTokenAuthentication oneTimeTokenAuthentication = new OneTimeTokenAuthentication(\"principal\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tbyte[] serialized = mapper.writeValueAsBytes(oneTimeTokenAuthentication);\n\t\tOneTimeTokenAuthentication deserialized = mapper.readValue(serialized, OneTimeTokenAuthentication.class);\n\t\tassertThat(deserialized.getPrincipal()).isEqualTo(oneTimeTokenAuthentication.getPrincipal());\n\t\tassertThat(deserialized.getAuthorities()).extracting(GrantedAuthority::getAuthority).contains(\"ROLE_USER\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass OneTimeTokenAuthenticationTokenTests {\n\n\t// gh-18095\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid shouldBeAbleToDeserializeFromJsonWithDefaultTypingActivated() throws IOException {\n\t\tObjectMapper mapper = new ObjectMapper();\n\t\tmapper.registerModules(SecurityJackson2Modules.getModules(getClass().getClassLoader()));\n\t\tOneTimeTokenAuthenticationToken oneTimeTokenAuthenticationToken = new OneTimeTokenAuthenticationToken(\n\t\t\t\t\"principal\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tbyte[] serialized = mapper.writeValueAsBytes(oneTimeTokenAuthenticationToken);\n\t\tOneTimeTokenAuthenticationToken deserialized = mapper.readValue(serialized,\n\t\t\t\tOneTimeTokenAuthenticationToken.class);\n\t\tassertThat(deserialized.getPrincipal()).isEqualTo(oneTimeTokenAuthenticationToken.getPrincipal());\n\t\tassertThat(deserialized.getAuthorities()).extracting(GrantedAuthority::getAuthority).contains(\"ROLE_USER\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ott/reactive/InMemoryReactiveOneTimeTokenServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott.reactive;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.time.ZoneOffset;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.UUID;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Tests for {@link InMemoryReactiveOneTimeTokenService}\n *\n * @author Max Batischev\n */\npublic class InMemoryReactiveOneTimeTokenServiceTests {\n\n\tprivate final InMemoryReactiveOneTimeTokenService oneTimeTokenService = new InMemoryReactiveOneTimeTokenService();\n\n\tprivate static final String USERNAME = \"user\";\n\n\tprivate static final String TOKEN = \"token\";\n\n\t@Test\n\tvoid generateThenTokenValueShouldBeValidUuidAndProvidedUsernameIsUsed() {\n\t\tGenerateOneTimeTokenRequest request = new GenerateOneTimeTokenRequest(USERNAME);\n\n\t\tOneTimeToken oneTimeToken = this.oneTimeTokenService.generate(request).block();\n\n\t\tassertThatNoException().isThrownBy(() -> UUID.fromString(oneTimeToken.getTokenValue()));\n\t\tassertThat(oneTimeToken.getUsername()).isEqualTo(USERNAME);\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenDoesNotExistThenNull() {\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(TOKEN);\n\n\t\tOneTimeToken oneTimeToken = this.oneTimeTokenService.consume(authenticationToken).block();\n\n\t\tassertThat(oneTimeToken).isNull();\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenExistsThenReturnItself() {\n\t\tGenerateOneTimeTokenRequest request = new GenerateOneTimeTokenRequest(USERNAME);\n\t\tOneTimeToken generated = this.oneTimeTokenService.generate(request).block();\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(\n\t\t\t\tgenerated.getTokenValue());\n\n\t\tOneTimeToken consumed = this.oneTimeTokenService.consume(authenticationToken).block();\n\n\t\tassertThat(consumed.getTokenValue()).isEqualTo(generated.getTokenValue());\n\t\tassertThat(consumed.getUsername()).isEqualTo(generated.getUsername());\n\t\tassertThat(consumed.getExpiresAt()).isEqualTo(generated.getExpiresAt());\n\t}\n\n\t@Test\n\tvoid consumeWhenTokenIsExpiredThenReturnNull() {\n\t\tGenerateOneTimeTokenRequest request = new GenerateOneTimeTokenRequest(USERNAME);\n\t\tOneTimeToken generated = this.oneTimeTokenService.generate(request).block();\n\t\tOneTimeTokenAuthenticationToken authenticationToken = new OneTimeTokenAuthenticationToken(\n\t\t\t\tgenerated.getTokenValue());\n\t\tClock tenMinutesFromNow = Clock.fixed(Instant.now().plus(10, ChronoUnit.MINUTES), ZoneOffset.UTC);\n\t\tthis.oneTimeTokenService.setClock(tenMinutesFromNow);\n\n\t\tOneTimeToken consumed = this.oneTimeTokenService.consume(authenticationToken).block();\n\n\t\tassertThat(consumed).isNull();\n\t}\n\n\t@Test\n\tvoid generateWhenMoreThan100TokensThenClearExpired() {\n\t\t// @formatter:off\n\t\tList<OneTimeToken> toExpire = generate(50); // 50 tokens will expire in 5 minutes from now\n\t\tClock twoMinutesFromNow = Clock.fixed(Instant.now().plus(2, ChronoUnit.MINUTES), ZoneOffset.UTC);\n\t\tthis.oneTimeTokenService.setClock(twoMinutesFromNow);\n\t\tList<OneTimeToken> toKeep = generate(50); // 50 tokens will expire in 7 minutes from now\n\t\tClock sixMinutesFromNow = Clock.fixed(Instant.now().plus(6, ChronoUnit.MINUTES), ZoneOffset.UTC);\n\t\tthis.oneTimeTokenService.setClock(sixMinutesFromNow);\n\n\t\tassertThat(toExpire)\n\t\t\t\t.extracting((token) -> this.oneTimeTokenService.consume(new OneTimeTokenAuthenticationToken(token.getTokenValue()))\n\t\t\t\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.containsOnlyNulls();\n\n\t\tassertThat(toKeep)\n\t\t\t\t.extracting((token) -> this.oneTimeTokenService.consume(new OneTimeTokenAuthenticationToken(token.getTokenValue()))\n\t\t\t\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.noneMatch(Objects::isNull);\n\t\t// @formatter:on\n\t}\n\n\tprivate List<OneTimeToken> generate(int howMany) {\n\t\tList<OneTimeToken> generated = new ArrayList<>(howMany);\n\t\tfor (int i = 0; i < howMany; i++) {\n\t\t\tOneTimeToken oneTimeToken = this.oneTimeTokenService\n\t\t\t\t.generate(new GenerateOneTimeTokenRequest(\"generated\" + i))\n\t\t\t\t.block();\n\t\t\tgenerated.add(oneTimeToken);\n\t\t}\n\t\treturn generated;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/ott/reactive/OneTimeTokenReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.ott.reactive;\n\nimport java.time.Instant;\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentMatchers;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.ott.DefaultOneTimeToken;\nimport org.springframework.security.authentication.ott.InvalidOneTimeTokenException;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.CollectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OneTimeTokenReactiveAuthenticationManager}\n *\n * @author Max Batischev\n */\npublic class OneTimeTokenReactiveAuthenticationManagerTests {\n\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\tprivate static final String USERNAME = \"user\";\n\n\tprivate static final String PASSWORD = \"password\";\n\n\tprivate static final String TOKEN = \"token\";\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void constructorWhenOneTimeTokenServiceNullThenIllegalArgumentException() {\n\t\tReactiveUserDetailsService userDetailsService = mock(ReactiveUserDetailsService.class);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OneTimeTokenReactiveAuthenticationManager(null, userDetailsService));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void constructorWhenUserDetailsServiceNullThenIllegalArgumentException() {\n\t\tReactiveOneTimeTokenService oneTimeTokenService = mock(ReactiveOneTimeTokenService.class);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OneTimeTokenReactiveAuthenticationManager(oneTimeTokenService, null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid authenticateWhenOneTimeTokenAuthenticationTokenIsPresentThenSuccess() {\n\t\tReactiveOneTimeTokenService oneTimeTokenService = mock(ReactiveOneTimeTokenService.class);\n\t\tgiven(oneTimeTokenService.consume(ArgumentMatchers.any(OneTimeTokenAuthenticationToken.class)))\n\t\t\t.willReturn(Mono.just(new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now())));\n\t\tReactiveUserDetailsService userDetailsService = mock(ReactiveUserDetailsService.class);\n\t\tUser testUser = new User(USERNAME, PASSWORD, AuthorityUtils.createAuthorityList(\"TEST\"));\n\t\tgiven(userDetailsService.findByUsername(eq(USERNAME))).willReturn(Mono.just(testUser));\n\n\t\tthis.authenticationManager = new OneTimeTokenReactiveAuthenticationManager(oneTimeTokenService,\n\t\t\t\tuserDetailsService);\n\n\t\tAuthentication token = this.authenticationManager\n\t\t\t.authenticate(OneTimeTokenAuthenticationToken.unauthenticated(TOKEN))\n\t\t\t.block();\n\n\t\tUserDetails user = (UserDetails) token.getPrincipal();\n\t\tCollection<? extends GrantedAuthority> authorities = token.getAuthorities();\n\n\t\tassertThat(user).isNotNull();\n\t\tassertThat(user.getUsername()).isEqualTo(USERNAME);\n\t\tassertThat(user.getPassword()).isEqualTo(PASSWORD);\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t\tassertThat(CollectionUtils.isEmpty(authorities)).isFalse();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid authenticateWhenInvalidOneTimeTokenAuthenticationTokenIsPresentThenFail() {\n\t\tReactiveOneTimeTokenService oneTimeTokenService = mock(ReactiveOneTimeTokenService.class);\n\t\tgiven(oneTimeTokenService.consume(ArgumentMatchers.any(OneTimeTokenAuthenticationToken.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tReactiveUserDetailsService userDetailsService = mock(ReactiveUserDetailsService.class);\n\n\t\tthis.authenticationManager = new OneTimeTokenReactiveAuthenticationManager(oneTimeTokenService,\n\t\t\t\tuserDetailsService);\n\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(InvalidOneTimeTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationManager.authenticate(OneTimeTokenAuthenticationToken.unauthenticated(TOKEN))\n\t\t\t\t\t\t.block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid authenticateWhenIncorrectTypeOfAuthenticationIsPresentThenFail() {\n\t\tReactiveOneTimeTokenService oneTimeTokenService = mock(ReactiveOneTimeTokenService.class);\n\t\tgiven(oneTimeTokenService.consume(ArgumentMatchers.any(OneTimeTokenAuthenticationToken.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tReactiveUserDetailsService userDetailsService = mock(ReactiveUserDetailsService.class);\n\n\t\tthis.authenticationManager = new OneTimeTokenReactiveAuthenticationManager(oneTimeTokenService,\n\t\t\t\tuserDetailsService);\n\n\t\t// @formatter:off\n\t\tAuthentication authentication = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(USERNAME, PASSWORD))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\n\t\tassertThat(authentication).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/rememberme/RememberMeAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.rememberme;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.RememberMeAuthenticationProvider;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link RememberMeAuthenticationProvider}.\n *\n * @author Ben Alex\n */\npublic class RememberMeAuthenticationProviderTests {\n\n\t@Test\n\tpublic void testDetectsAnInvalidKey() {\n\t\tRememberMeAuthenticationProvider aap = new RememberMeAuthenticationProvider(\"qwerty\");\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(\"WRONG_KEY\", \"Test\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> aap.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void testDetectsMissingKey() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RememberMeAuthenticationProvider(null));\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() throws Exception {\n\t\tRememberMeAuthenticationProvider aap = new RememberMeAuthenticationProvider(\"qwerty\");\n\t\taap.afterPropertiesSet();\n\t\tassertThat(aap.getKey()).isEqualTo(\"qwerty\");\n\t}\n\n\t@Test\n\tpublic void testIgnoresClassesItDoesNotSupport() {\n\t\tRememberMeAuthenticationProvider aap = new RememberMeAuthenticationProvider(\"qwerty\");\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_A\");\n\t\tassertThat(aap.supports(TestingAuthenticationToken.class)).isFalse();\n\t\t// Try it anyway\n\t\tassertThat(aap.authenticate(token)).isNull();\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() {\n\t\tRememberMeAuthenticationProvider aap = new RememberMeAuthenticationProvider(\"qwerty\");\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(\"qwerty\", \"Test\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tAuthentication result = aap.authenticate(token);\n\t\tassertThat(token).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void testSupports() {\n\t\tRememberMeAuthenticationProvider aap = new RememberMeAuthenticationProvider(\"qwerty\");\n\t\tassertThat(aap.supports(RememberMeAuthenticationToken.class)).isTrue();\n\t\tassertThat(aap.supports(TestingAuthenticationToken.class)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authentication/rememberme/RememberMeAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authentication.rememberme;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link RememberMeAuthenticationToken}.\n *\n * @author Ben Alex\n */\npublic class RememberMeAuthenticationTokenTests {\n\n\tprivate static final List<GrantedAuthority> ROLES_12 = AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\");\n\n\t@Test\n\tpublic void testConstructorRejectsNulls() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new RememberMeAuthenticationToken(null, \"Test\", ROLES_12));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RememberMeAuthenticationToken(\"key\", null, ROLES_12));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new RememberMeAuthenticationToken(\"key\", \"Test\", Arrays.asList((GrantedAuthority) null)));\n\t}\n\n\t@Test\n\tpublic void testEqualsWhenEqual() {\n\t\tRememberMeAuthenticationToken token1 = new RememberMeAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tRememberMeAuthenticationToken token2 = new RememberMeAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tassertThat(token2).isEqualTo(token1);\n\t}\n\n\t@Test\n\tpublic void testGetters() {\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tassertThat(token.getKeyHash()).isEqualTo(\"key\".hashCode());\n\t\tassertThat(token.getPrincipal()).isEqualTo(\"Test\");\n\t\tassertThat(token.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(token.getAuthorities())).contains(\"ROLE_ONE\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(token.getAuthorities())).contains(\"ROLE_TWO\");\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToAbstractParentEqualsCheck() {\n\t\tRememberMeAuthenticationToken token1 = new RememberMeAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tRememberMeAuthenticationToken token2 = new RememberMeAuthenticationToken(\"key\", \"DIFFERENT_PRINCIPAL\",\n\t\t\t\tROLES_12);\n\t\tassertThat(token1.equals(token2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToDifferentAuthenticationClass() {\n\t\tRememberMeAuthenticationToken token1 = new RememberMeAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tUsernamePasswordAuthenticationToken token2 = UsernamePasswordAuthenticationToken.authenticated(\"Test\",\n\t\t\t\t\"Password\", ROLES_12);\n\t\tassertThat(token1.equals(token2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testNotEqualsDueToKey() {\n\t\tRememberMeAuthenticationToken token1 = new RememberMeAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tRememberMeAuthenticationToken token2 = new RememberMeAuthenticationToken(\"DIFFERENT_KEY\", \"Test\", ROLES_12);\n\t\tassertThat(token1.equals(token2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testSetAuthenticatedIgnored() {\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(\"key\", \"Test\", ROLES_12);\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t\ttoken.setAuthenticated(false);\n\t\tassertThat(!token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void toBuilderWhenApplyThenCopies() {\n\t\tRememberMeAuthenticationToken factorOne = new RememberMeAuthenticationToken(\"key\", PasswordEncodedUser.user(),\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"));\n\t\tRememberMeAuthenticationToken factorTwo = new RememberMeAuthenticationToken(\"yek\", PasswordEncodedUser.admin(),\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"));\n\t\tRememberMeAuthenticationToken authentication = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.key(\"yek\")\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());\n\t\tassertThat(authentication.getKeyHash()).isEqualTo(factorTwo.getKeyHash());\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(factorTwo.getPrincipal());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AllAuthoritiesAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\nclass AllAuthoritiesAuthorizationManagerTests {\n\n\tpublic static final String ROLE_USER = \"ROLE_USER\";\n\n\tpublic static final String ROLE_ADMIN = \"ROLE_ADMIN\";\n\n\t@Mock\n\tprivate RoleHierarchy roleHierarchy;\n\n\t@Captor\n\tprivate ArgumentCaptor<Collection<? extends GrantedAuthority>> authoritiesCaptor;\n\n\t@Test\n\tvoid hasAllAuthoritiesWhenNullAuthoritiesThenIllegalArgumentException() {\n\t\tString[] requiredAuthorities = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AllAuthoritiesAuthorizationManager.hasAllAuthorities(requiredAuthorities));\n\t}\n\n\t@Test\n\tvoid hasAllAuthortiesWhenEmptyAuthoritiesThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AllAuthoritiesAuthorizationManager.hasAllAuthorities((new String[0])));\n\t}\n\n\t@Test\n\tvoid authorizeWhenGranted() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tAllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_USER);\n\t\tassertThat(manager.authorize(() -> authentication, \"\").isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenNotAuthenticated() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tauthentication.setAuthenticated(false);\n\t\tAllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_USER);\n\t\tassertThat(manager.authorize(() -> authentication, \"\").isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid hasAllRolesAuthorizeWhenGranted() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tAllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager.hasAllRoles(\"USER\");\n\t\tassertThat(manager.authorize(() -> authentication, \"\").isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid hasAllPrefixedAuthoritiesAuthorizeWhenGranted() {\n\t\tString prefix = \"PREFIX_\";\n\t\tString authority1 = \"AUTHORITY1\";\n\t\tString authority2 = \"AUTHORITY2\";\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", prefix + authority1,\n\t\t\t\tprefix + authority2);\n\t\tAllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager\n\t\t\t.hasAllPrefixedAuthorities(prefix, authority1, authority2);\n\t\tassertThat(manager.authorize(() -> authentication, \"\").isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenSingleMissingThenDenied() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tAllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_ADMIN);\n\t\tassertThat(manager.authorize(() -> authentication, \"\").isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid authorizeWhenMultipleMissingOneThenDenied() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tAllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_ADMIN, ROLE_USER);\n\t\tAuthorityAuthorizationDecision result = manager.authorize(() -> authentication, \"\");\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getAuthorities()).hasSize(1);\n\t\tassertThat(new ArrayList<>(result.getAuthorities()).get(0).getAuthority()).isEqualTo(ROLE_ADMIN);\n\t}\n\n\t@Test\n\tvoid setRoleHierarchyWhenNullThenIllegalArgumentException() {\n\t\tAllAuthoritiesAuthorizationManager<?> manager = AllAuthoritiesAuthorizationManager.hasAllAuthorities(ROLE_USER);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setRoleHierarchy(null));\n\t}\n\n\t@Test\n\tvoid setRoleHierarchyThenUsesResult() {\n\t\tCollection result = AuthorityUtils.createAuthorityList(ROLE_USER, ROLE_ADMIN);\n\t\tgiven(this.roleHierarchy.getReachableGrantedAuthorities(any())).willReturn(result);\n\t\tAllAuthoritiesAuthorizationManager<Object> manager = AllAuthoritiesAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_USER);\n\t\tmanager.setRoleHierarchy(this.roleHierarchy);\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\n\t\tAuthorityAuthorizationDecision authz = manager.authorize(() -> authentication, \"\");\n\t\tassertThat(authz.isGranted()).isTrue();\n\t\tverify(this.roleHierarchy).getReachableGrantedAuthorities(this.authoritiesCaptor.capture());\n\t\tassertThat(this.authoritiesCaptor.getValue()).map(GrantedAuthority::getAuthority).contains(ROLE_USER);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AllAuthoritiesReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\nclass AllAuthoritiesReactiveAuthorizationManagerTests {\n\n\tpublic static final String ROLE_USER = \"ROLE_USER\";\n\n\tpublic static final String ROLE_ADMIN = \"ROLE_ADMIN\";\n\n\t@Mock\n\tprivate RoleHierarchy roleHierarchy;\n\n\t@Captor\n\tprivate ArgumentCaptor<Collection<? extends GrantedAuthority>> authoritiesCaptor;\n\n\t@Test\n\tvoid hasAllAuthoritiesWhenNullAuthoritiesThenIllegalArgumentException() {\n\t\tString[] requiredAuthorities = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AllAuthoritiesReactiveAuthorizationManager.hasAllAuthorities(requiredAuthorities));\n\t}\n\n\t@Test\n\tvoid hasAllAuthortiesWhenEmptyAuthoritiesThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AllAuthoritiesReactiveAuthorizationManager.hasAllAuthorities((new String[0])));\n\t}\n\n\t@Test\n\tvoid authorizeWhenGranted() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tAllAuthoritiesReactiveAuthorizationManager<Object> manager = AllAuthoritiesReactiveAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_USER);\n\t\tassertThat(manager.authorize(Mono.just(authentication), \"\").block().isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenNotAuthenticated() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tauthentication.setAuthenticated(false);\n\t\tAllAuthoritiesReactiveAuthorizationManager<Object> manager = AllAuthoritiesReactiveAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_USER);\n\t\tassertThat(manager.authorize(Mono.just(authentication), \"\").block().isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid hasAllRolesAuthorizeWhenGranted() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tAllAuthoritiesReactiveAuthorizationManager<Object> manager = AllAuthoritiesReactiveAuthorizationManager\n\t\t\t.hasAllRoles(\"USER\");\n\t\tassertThat(manager.authorize(Mono.just(authentication), \"\").block().isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid hasAllPrefixedAuthoritiesAuthorizeWhenGranted() {\n\t\tString prefix = \"PREFIX_\";\n\t\tString authority1 = \"AUTHORITY1\";\n\t\tString authority2 = \"AUTHORITY2\";\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", prefix + authority1,\n\t\t\t\tprefix + authority2);\n\t\tAllAuthoritiesReactiveAuthorizationManager<Object> manager = AllAuthoritiesReactiveAuthorizationManager\n\t\t\t.hasAllPrefixedAuthorities(prefix, authority1, authority2);\n\t\tassertThat(manager.authorize(Mono.just(authentication), \"\").block().isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenSingleMissingThenDenied() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tAllAuthoritiesReactiveAuthorizationManager<Object> manager = AllAuthoritiesReactiveAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_ADMIN);\n\t\tassertThat(manager.authorize(Mono.just(authentication), \"\").block().isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid authorizeWhenMultipleMissingOneThenDenied() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\t\tAllAuthoritiesReactiveAuthorizationManager<Object> manager = AllAuthoritiesReactiveAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_ADMIN, ROLE_USER);\n\t\tAuthorityAuthorizationDecision result = manager.authorize(Mono.just(authentication), \"\")\n\t\t\t.cast(AuthorityAuthorizationDecision.class)\n\t\t\t.block();\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getAuthorities()).hasSize(1);\n\t\tassertThat(new ArrayList<>(result.getAuthorities()).get(0).getAuthority()).isEqualTo(ROLE_ADMIN);\n\t}\n\n\t@Test\n\tvoid setRoleHierarchyWhenNullThenIllegalArgumentException() {\n\t\tAllAuthoritiesReactiveAuthorizationManager<?> manager = AllAuthoritiesReactiveAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_USER);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setRoleHierarchy(null));\n\t}\n\n\t@Test\n\tvoid setRoleHierarchyThenUsesResult() {\n\t\tCollection result = AuthorityUtils.createAuthorityList(ROLE_USER, ROLE_ADMIN);\n\t\tgiven(this.roleHierarchy.getReachableGrantedAuthorities(any())).willReturn(result);\n\t\tAllAuthoritiesReactiveAuthorizationManager<Object> manager = AllAuthoritiesReactiveAuthorizationManager\n\t\t\t.hasAllAuthorities(ROLE_USER);\n\t\tmanager.setRoleHierarchy(this.roleHierarchy);\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", ROLE_USER);\n\n\t\tAuthorityAuthorizationDecision authz = manager.authorize(Mono.just(authentication), \"\")\n\t\t\t.cast(AuthorityAuthorizationDecision.class)\n\t\t\t.block();\n\t\tassertThat(authz.isGranted()).isTrue();\n\t\tverify(this.roleHierarchy).getReachableGrantedAuthorities(this.authoritiesCaptor.capture());\n\t\tassertThat(this.authoritiesCaptor.getValue()).map(GrantedAuthority::getAuthority).contains(ROLE_USER);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AllRequiredFactorsAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * Test {@link AllRequiredFactorsAuthorizationManager}.\n *\n * @author Rob Winch\n * @since 7.0\n */\nclass AllRequiredFactorsAuthorizationManagerTests {\n\n\tprivate static final Object DOES_NOT_MATTER = new Object();\n\n\tprivate static RequiredFactor REQUIRED_PASSWORD = RequiredFactor\n\t\t.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t.build();\n\n\tprivate static RequiredFactor EXPIRING_PASSWORD = RequiredFactor\n\t\t.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t.validDuration(Duration.ofHours(1))\n\t\t.build();\n\n\t@Test\n\tvoid authorizeWhenGranted() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(REQUIRED_PASSWORD)\n\t\t\t.build();\n\t\tFactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(REQUIRED_PASSWORD.getAuthority())\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", passwordFactor);\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenConsumerGranted() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor((required) -> required.authority(FactorGrantedAuthority.PASSWORD_AUTHORITY))\n\t\t\t.build();\n\t\tFactorGrantedAuthority passwordFactor = FactorGrantedAuthority\n\t\t\t.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", passwordFactor);\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenUnauthenticated() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(REQUIRED_PASSWORD)\n\t\t\t.build();\n\t\tFactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(REQUIRED_PASSWORD.getAuthority())\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.build();\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\", passwordFactor);\n\t\tauthentication.setAuthenticated(false);\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(REQUIRED_PASSWORD));\n\t}\n\n\t@Test\n\tvoid authorizeWhenNullAuthentication() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(EXPIRING_PASSWORD)\n\t\t\t.build();\n\t\tAuthentication authentication = null;\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(EXPIRING_PASSWORD));\n\t}\n\n\t@Test\n\tvoid authorizeWhenRequiredFactorHasNullDurationThenNullIssuedAtGranted() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(REQUIRED_PASSWORD)\n\t\t\t.build();\n\t\tFactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(REQUIRED_PASSWORD.getAuthority())\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", passwordFactor);\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenRequiredFactorHasDurationAndNotFactorGrantedAuthorityThenExpired() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(EXPIRING_PASSWORD)\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\tEXPIRING_PASSWORD.getAuthority());\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createExpired(EXPIRING_PASSWORD));\n\t}\n\n\t@Test\n\tvoid authorizeWhenFactorAuthorityMissingThenMissing() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(REQUIRED_PASSWORD)\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(REQUIRED_PASSWORD));\n\t}\n\n\t@Test\n\tvoid authorizeWhenGrantedAuthorityThenGranted() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(REQUIRED_PASSWORD)\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\tREQUIRED_PASSWORD.getAuthority());\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenExpired() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(EXPIRING_PASSWORD)\n\t\t\t.build();\n\t\tFactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(EXPIRING_PASSWORD.getAuthority())\n\t\t\t.issuedAt(Instant.now().minus(Duration.ofHours(2)))\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", passwordFactor);\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createExpired(EXPIRING_PASSWORD));\n\t}\n\n\t@Test\n\tvoid authorizeWhenJustExpired() {\n\t\tInstant now = Instant.now();\n\t\tDuration expiresIn = Duration.ofHours(1);\n\t\tInstant justExpired = now.minus(expiresIn);\n\t\tClock clock = Clock.fixed(now, ZoneId.systemDefault());\n\t\tRequiredFactor expiringPassword = RequiredFactor.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.validDuration(expiresIn)\n\t\t\t.build();\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(expiringPassword)\n\t\t\t.build();\n\t\tallFactors.setClock(clock);\n\t\tFactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(expiringPassword.getAuthority())\n\t\t\t.issuedAt(justExpired)\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", passwordFactor);\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createExpired(expiringPassword));\n\t}\n\n\t@Test\n\tvoid authorizeWhenAlmostExpired() {\n\t\tInstant now = Instant.now();\n\t\tDuration expiresIn = Duration.ofHours(1);\n\t\tInstant justExpired = now.minus(expiresIn).plus(Duration.ofNanos(1));\n\t\tClock clock = Clock.fixed(now, ZoneId.systemDefault());\n\t\tRequiredFactor expiringPassword = RequiredFactor.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.validDuration(expiresIn)\n\t\t\t.build();\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(expiringPassword)\n\t\t\t.build();\n\t\tallFactors.setClock(clock);\n\t\tFactorGrantedAuthority passwordFactor = FactorGrantedAuthority.withAuthority(expiringPassword.getAuthority())\n\t\t\t.issuedAt(justExpired)\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", passwordFactor);\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenDifferentFactorGrantedAuthorityThenMissing() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(REQUIRED_PASSWORD)\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\tFactorGrantedAuthority.fromAuthority(REQUIRED_PASSWORD.getAuthority()) + \"DIFFERENT\");\n\t\tFactorAuthorizationDecision result = allFactors.authorize(() -> authentication, DOES_NOT_MATTER);\n\t\tassertThat(result.isGranted()).isFalse();\n\t\tassertThat(result.getFactorErrors()).containsExactly(RequiredFactorError.createMissing(REQUIRED_PASSWORD));\n\t}\n\n\t@Test\n\tvoid setClockWhenNullThenIllegalArgumentException() {\n\t\tAllRequiredFactorsAuthorizationManager<Object> allFactors = AllRequiredFactorsAuthorizationManager.builder()\n\t\t\t.requireFactor(REQUIRED_PASSWORD)\n\t\t\t.build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> allFactors.setClock(null));\n\t}\n\n\t@Test\n\tvoid builderBuildWhenEmpty() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> AllRequiredFactorsAuthorizationManager.builder().build());\n\t}\n\n\t@Test\n\tvoid builderWhenNullRequiredFactor() {\n\t\tAllRequiredFactorsAuthorizationManager.Builder builder = AllRequiredFactorsAuthorizationManager.builder();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> builder.requireFactor((RequiredFactor) null));\n\t}\n\n\t@Test\n\tvoid builderWhenNullConsumerRequiredFactorBuilder() {\n\t\tAllRequiredFactorsAuthorizationManager.Builder builder = AllRequiredFactorsAuthorizationManager.builder();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> builder.requireFactor((Consumer<RequiredFactor.Builder>) null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthenticatedAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Collections;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link AuthenticatedAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class AuthenticatedAuthorizationManagerTests {\n\n\t@Test\n\tpublic void authenticatedWhenUserNotAnonymousAndAuthenticatedThenGrantedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\",\n\t\t\t\t\"ROLE_USER\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticatedWhenUserNullThenDeniedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();\n\t\tSupplier<Authentication> authentication = () -> null;\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticatedWhenUserAnonymousThenDeniedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();\n\t\tSupplier<Authentication> authentication = () -> new AnonymousAuthenticationToken(\"key\", \"principal\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticatedWhenUserNotAuthenticatedThenDeniedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\",\n\t\t\t\t\"ROLE_USER\");\n\t\tauthentication.setAuthenticated(false);\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(() -> authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticatedWhenUserRememberMeThenGrantedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();\n\t\tSupplier<Authentication> authentication = () -> new RememberMeAuthenticationToken(\"user\", \"password\",\n\t\t\t\tCollections.emptyList());\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedWhenUserNotAnonymousAndNotRememberMeThenGrantedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.fullyAuthenticated();\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\",\n\t\t\t\t\"ROLE_USER\");\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedWhenUserNullThenDeniedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.fullyAuthenticated();\n\t\tSupplier<Authentication> authentication = () -> null;\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedWhenUserRememberMeThenDeniedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.fullyAuthenticated();\n\t\tSupplier<Authentication> authentication = () -> new RememberMeAuthenticationToken(\"user\", \"password\",\n\t\t\t\tCollections.emptyList());\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedWhenUserAnonymousThenDeniedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.fullyAuthenticated();\n\t\tSupplier<Authentication> authentication = () -> new AnonymousAuthenticationToken(\"key\", \"principal\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void anonymousWhenUserAnonymousThenGrantedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.anonymous();\n\t\tSupplier<Authentication> authentication = () -> new AnonymousAuthenticationToken(\"key\", \"principal\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void anonymousWhenUserNotAnonymousThenDeniedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.anonymous();\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\",\n\t\t\t\t\"ROLE_USER\");\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenUserRememberMeThenGrantedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.rememberMe();\n\t\tSupplier<Authentication> authentication = () -> new RememberMeAuthenticationToken(\"user\", \"password\",\n\t\t\t\tCollections.emptyList());\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenUserNotRememberMeThenDeniedDecision() {\n\t\tAuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.rememberMe();\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\",\n\t\t\t\t\"ROLE_USER\");\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthenticatedReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthenticatedReactiveAuthorizationManagerTests {\n\n\t@Mock\n\tAuthentication authentication;\n\n\tAuthenticatedReactiveAuthorizationManager<Object> manager = AuthenticatedReactiveAuthorizationManager\n\t\t.authenticated();\n\n\t@Test\n\tpublic void checkWhenAuthenticatedThenReturnTrue() {\n\t\tgiven(this.authentication.isAuthenticated()).willReturn(true);\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenNotAuthenticatedThenReturnFalse() {\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenEmptyThenReturnFalse() {\n\t\tboolean granted = this.manager.authorize(Mono.empty(), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenAnonymousAuthenticatedThenReturnFalse() {\n\t\tAnonymousAuthenticationToken anonymousAuthenticationToken = mock(AnonymousAuthenticationToken.class);\n\t\tboolean granted = this.manager.authorize(Mono.just(anonymousAuthenticationToken), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenErrorThenError() {\n\t\tMono<AuthorizationResult> result = this.manager.authorize(Mono.error(new RuntimeException(\"ooops\")), null);\n\t\t// @formatter:off\n\t\tStepVerifier.create(result)\n\t\t\t\t.expectError()\n\t\t\t\t.verify();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthoritiesAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNullPointerException;\n\n/**\n * Tests for {@link AuthoritiesAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n * @author Khyojae\n */\nclass AuthoritiesAuthorizationManagerTests {\n\n\t@Test\n\tvoid setRoleHierarchyWhenNullThenIllegalArgumentException() {\n\t\tAuthoritiesAuthorizationManager manager = new AuthoritiesAuthorizationManager();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setRoleHierarchy(null))\n\t\t\t.withMessage(\"roleHierarchy cannot be null\");\n\t}\n\n\t@Test\n\tvoid setRoleHierarchyWhenNotNullThenVerifyRoleHierarchy() {\n\t\tAuthoritiesAuthorizationManager manager = new AuthoritiesAuthorizationManager();\n\t\tRoleHierarchy roleHierarchy = RoleHierarchyImpl.withDefaultRolePrefix().build();\n\t\tmanager.setRoleHierarchy(roleHierarchy);\n\t\tassertThat(manager).extracting(\"roleHierarchy\").isEqualTo(roleHierarchy);\n\t}\n\n\t@Test\n\tvoid getRoleHierarchyWhenNotSetThenDefaultsToNullRoleHierarchy() {\n\t\tAuthoritiesAuthorizationManager manager = new AuthoritiesAuthorizationManager();\n\t\tassertThat(manager).extracting(\"roleHierarchy\").isInstanceOf(NullRoleHierarchy.class);\n\t}\n\n\t@Test\n\tvoid checkWhenUserHasAnyAuthorityThenGrantedDecision() {\n\t\tAuthoritiesAuthorizationManager manager = new AuthoritiesAuthorizationManager();\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"USER\");\n\t\tassertThat(manager.authorize(authentication, Arrays.asList(\"ADMIN\", \"USER\")).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkWhenUserHasNotAnyAuthorityThenDeniedDecision() {\n\t\tAuthoritiesAuthorizationManager manager = new AuthoritiesAuthorizationManager();\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ANONYMOUS\");\n\t\tassertThat(manager.authorize(authentication, Arrays.asList(\"ADMIN\", \"USER\")).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkWhenRoleHierarchySetThenGreaterRoleTakesPrecedence() {\n\t\tAuthoritiesAuthorizationManager manager = new AuthoritiesAuthorizationManager();\n\t\tRoleHierarchyImpl roleHierarchy = RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > ROLE_USER\");\n\t\tmanager.setRoleHierarchy(roleHierarchy);\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_ADMIN\");\n\t\tassertThat(manager.authorize(authentication, Collections.singleton(\"ROLE_USER\")).isGranted()).isTrue();\n\t}\n\n\t@Test\n\t// gh-18543\n\tvoid authorizeWhenAuthorityIsNullThenDoesNotThrowNullPointerException() {\n\t\tAuthoritiesAuthorizationManager manager = new AuthoritiesAuthorizationManager();\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\tCollections.singletonList(() -> null));\n\n\t\tCollection<String> authoritiesContainsThrowsNPE = Set.of(\"ROLE_USER\");\n\n\t\t// must be Collection that throws NPE when .contains(null) is invoked\n\t\t// to replicate the issue in gh-18543\n\t\tassertThatNullPointerException().isThrownBy(() -> authoritiesContainsThrowsNPE.contains(null));\n\t\tassertThat(manager.authorize(() -> authentication, authoritiesContainsThrowsNPE).isGranted()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthorityAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Collections;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link AuthorityAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class AuthorityAuthorizationManagerTests {\n\n\t@Test\n\tpublic void hasRoleWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasRole(null))\n\t\t\t.withMessage(\"role cannot be null\");\n\t}\n\n\t@Test\n\tpublic void hasRoleWhenContainRoleWithRolePrefixThenException() {\n\t\tString ROLE_PREFIX = \"ROLE_\";\n\t\tString ROLE_USER = ROLE_PREFIX + \"USER\";\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasRole(ROLE_USER))\n\t\t\t.withMessage(ROLE_USER + \" should not start with \" + ROLE_PREFIX + \" since \" + ROLE_PREFIX\n\t\t\t\t\t+ \" is automatically prepended when using hasRole. Consider using hasAuthority instead.\");\n\t}\n\n\t@Test\n\tpublic void hasAuthorityWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAuthority(null))\n\t\t\t.withMessage(\"authority cannot be null\");\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole((String[]) null))\n\t\t\t.withMessage(\"roles cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenEmptyThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(new String[] {}))\n\t\t\t.withMessage(\"roles cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenContainNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(\"ADMIN\", null, \"USER\"))\n\t\t\t.withMessage(\"roles cannot contain null values\");\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenCustomRolePrefixNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(null, new String[] { \"ADMIN\", \"USER\" }))\n\t\t\t.withMessage(\"rolePrefix cannot be null\");\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenContainRoleWithRolePrefixThenException() {\n\t\tString ROLE_PREFIX = \"ROLE_\";\n\t\tString ROLE_USER = ROLE_PREFIX + \"USER\";\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(new String[] { ROLE_USER }))\n\t\t\t.withMessage(ROLE_USER + \" should not start with \" + ROLE_PREFIX + \" since \" + ROLE_PREFIX\n\t\t\t\t\t+ \" is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.\");\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityWhenNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority((String[]) null))\n\t\t\t.withMessage(\"authorities cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityWhenEmptyThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(new String[] {}))\n\t\t\t.withMessage(\"authorities cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityWhenContainNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(\"ADMIN\", null, \"USER\"))\n\t\t\t.withMessage(\"authorities cannot contain null values\");\n\t}\n\n\t@Test\n\tpublic void hasRoleWhenUserHasRoleThenGrantedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole(\"ADMIN\");\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\",\n\t\t\t\t\"ROLE_USER\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasRoleWhenUserHasNotRoleThenDeniedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole(\"ADMIN\");\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAuthorityWhenUserHasAuthorityThenGrantedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority(\"ADMIN\");\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ADMIN\",\n\t\t\t\t\"USER\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAuthorityWhenUserHasNotAuthorityThenDeniedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority(\"ADMIN\");\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"USER\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAuthorityWhenUserHasCustomAuthorityThenGrantedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority(\"ADMIN\");\n\t\tGrantedAuthority customGrantedAuthority = () -> \"ADMIN\";\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\tCollections.singletonList(customGrantedAuthority));\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAuthorityWhenUserHasNotCustomAuthorityThenDeniedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority(\"ADMIN\");\n\t\tGrantedAuthority customGrantedAuthority = () -> \"USER\";\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\tCollections.singletonList(customGrantedAuthority));\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenUserHasAnyRoleThenGrantedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole(\"ADMIN\", \"USER\");\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenUserHasNotAnyRoleThenDeniedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole(\"ADMIN\", \"USER\");\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_ANONYMOUS\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenCustomRolePrefixProvidedThenUseCustomRolePrefix() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole(\"CUSTOM_\",\n\t\t\t\tnew String[] { \"USER\" });\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"CUSTOM_USER\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityWhenUserHasAnyAuthorityThenGrantedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority(\"ADMIN\", \"USER\");\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"USER\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityWhenUserHasNotAnyAuthorityThenDeniedDecision() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority(\"ADMIN\", \"USER\");\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ANONYMOUS\");\n\t\tObject object = new Object();\n\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void setRoleHierarchyWhenNullThenIllegalArgumentException() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole(\"USER\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setRoleHierarchy(null))\n\t\t\t.withMessage(\"roleHierarchy cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setRoleHierarchyWhenNotNullThenVerifyRoleHierarchy() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole(\"USER\");\n\t\tRoleHierarchy roleHierarchy = RoleHierarchyImpl.withDefaultRolePrefix().build();\n\t\tmanager.setRoleHierarchy(roleHierarchy);\n\t\tassertThat(manager).extracting(\"delegate\").extracting(\"roleHierarchy\").isEqualTo(roleHierarchy);\n\t}\n\n\t@Test\n\tpublic void getRoleHierarchyWhenNotSetThenDefaultsToNullRoleHierarchy() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole(\"USER\");\n\t\tassertThat(manager).extracting(\"delegate\").extracting(\"roleHierarchy\").isInstanceOf(NullRoleHierarchy.class);\n\t}\n\n\t@Test\n\tpublic void hasRoleWhenRoleHierarchySetThenGreaterRoleTakesPrecedence() {\n\t\tAuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole(\"USER\");\n\t\tRoleHierarchyImpl roleHierarchy = RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > ROLE_USER\");\n\t\tmanager.setRoleHierarchy(roleHierarchy);\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_ADMIN\");\n\t\tObject object = new Object();\n\t\tassertThat(manager.authorize(authentication, object).isGranted()).isTrue();\n\t}\n\n\t// gh-13079\n\t@Test\n\tvoid hasAnyRoleWhenEmptyRolePrefixThenNoException() {\n\t\tAuthorityAuthorizationManager.hasAnyRole(\"\", new String[] { \"USER\" });\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthorityReactiveAuthorizationManagerTests {\n\n\t@Mock\n\tAuthentication authentication;\n\n\tAuthorityReactiveAuthorizationManager<Object> manager = AuthorityReactiveAuthorizationManager.hasAuthority(\"ADMIN\");\n\n\t@Test\n\tpublic void checkWhenHasAuthorityAndNotAuthenticatedThenReturnFalse() {\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasAuthorityAndEmptyThenReturnFalse() {\n\t\tboolean granted = this.manager.authorize(Mono.empty(), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasAuthorityAndErrorThenError() {\n\t\tMono<AuthorizationResult> result = this.manager.authorize(Mono.error(new RuntimeException(\"ooops\")), null);\n\t\t// @formatter:off\n\t\tStepVerifier.create(result)\n\t\t\t\t.expectError()\n\t\t\t\t.verify();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void checkWhenHasAuthorityAndAuthenticatedAndNoAuthoritiesThenReturnFalse() {\n\t\tgiven(this.authentication.isAuthenticated()).willReturn(true);\n\t\tgiven(this.authentication.getAuthorities()).willReturn(Collections.emptyList());\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasAuthorityAndAuthenticatedAndWrongAuthoritiesThenReturnFalse() {\n\t\tthis.authentication = new TestingAuthenticationToken(\"rob\", \"secret\", \"ROLE_ADMIN\");\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasAuthorityAndAuthorizedThenReturnTrue() {\n\t\tthis.authentication = new TestingAuthenticationToken(\"rob\", \"secret\", \"ADMIN\");\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasCustomAuthorityAndAuthorizedThenReturnTrue() {\n\t\tGrantedAuthority customGrantedAuthority = () -> \"ADMIN\";\n\t\tthis.authentication = new TestingAuthenticationToken(\"rob\", \"secret\",\n\t\t\t\tCollections.singletonList(customGrantedAuthority));\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasCustomAuthorityAndAuthenticatedAndWrongAuthoritiesThenReturnFalse() {\n\t\tGrantedAuthority customGrantedAuthority = () -> \"USER\";\n\t\tthis.authentication = new TestingAuthenticationToken(\"rob\", \"secret\",\n\t\t\t\tCollections.singletonList(customGrantedAuthority));\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasRoleAndAuthorizedThenReturnTrue() {\n\t\tthis.manager = AuthorityReactiveAuthorizationManager.hasRole(\"ADMIN\");\n\t\tthis.authentication = new TestingAuthenticationToken(\"rob\", \"secret\", \"ROLE_ADMIN\");\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasRoleAndNotAuthorizedThenReturnFalse() {\n\t\tthis.manager = AuthorityReactiveAuthorizationManager.hasRole(\"ADMIN\");\n\t\tthis.authentication = new TestingAuthenticationToken(\"rob\", \"secret\", \"ADMIN\");\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasAnyRoleAndAuthorizedThenReturnTrue() {\n\t\tthis.manager = AuthorityReactiveAuthorizationManager.hasAnyRole(\"GENERAL\", \"USER\", \"TEST\");\n\t\tthis.authentication = new TestingAuthenticationToken(\"rob\", \"secret\", \"ROLE_USER\", \"ROLE_AUDITING\",\n\t\t\t\t\"ROLE_ADMIN\");\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasAnyRoleAndNotAuthorizedThenReturnFalse() {\n\t\tthis.manager = AuthorityReactiveAuthorizationManager.hasAnyRole(\"GENERAL\", \"USER\", \"TEST\");\n\t\tthis.authentication = new TestingAuthenticationToken(\"rob\", \"secret\", \"USER\", \"AUDITING\", \"ADMIN\");\n\t\tboolean granted = this.manager.authorize(Mono.just(this.authentication), null).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasRoleWhenNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityReactiveAuthorizationManager.hasRole((String) null));\n\t}\n\n\t@Test\n\tpublic void hasAuthorityWhenNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityReactiveAuthorizationManager.hasAuthority((String) null));\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityReactiveAuthorizationManager.hasAnyRole((String) null));\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityWhenNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityReactiveAuthorizationManager.hasAnyAuthority((String) null));\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenOneIsNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityReactiveAuthorizationManager.hasAnyRole(\"ROLE_ADMIN\", (String) null));\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityWhenOneIsNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorityReactiveAuthorizationManager.hasAnyAuthority(\"ADMIN\", (String) null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthorizationAdvisorProxyFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.SortedMap;\nimport java.util.SortedSet;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.core.annotation.AnnotationAwareOrderComparator;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.method.AuthorizationAdvisor;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;\nimport org.springframework.security.authorization.method.AuthorizationProxy;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\npublic class AuthorizationAdvisorProxyFactoryTests {\n\n\tprivate final Authentication user = TestAuthentication.authenticatedUser();\n\n\tprivate final Authentication admin = TestAuthentication.authenticatedAdmin();\n\n\tprivate final Flight flight = new Flight();\n\n\tprivate final User alan = new User(\"alan\", \"alan\", \"turing\");\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tFlight flight = new Flight();\n\t\tassertThat(flight.getAltitude()).isEqualTo(35000d);\n\t\tFlight secured = proxy(factory, flight);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(secured::getAltitude);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeOnInterfaceThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tassertThat(this.alan.getFirstName()).isEqualTo(\"alan\");\n\t\tUser secured = proxy(factory, this.alan);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(secured::getFirstName);\n\t\tSecurityContextHolder.getContext().setAuthentication(authenticated(\"alan\"));\n\t\tassertThat(secured.getFirstName()).isEqualTo(\"alan\");\n\t\tSecurityContextHolder.getContext().setAuthentication(this.admin);\n\t\tassertThat(secured.getFirstName()).isEqualTo(\"alan\");\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeOnRecordThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tHasSecret repo = new Repository(\"secret\");\n\t\tassertThat(repo.secret()).isEqualTo(\"secret\");\n\t\tHasSecret secured = proxy(factory, repo);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(secured::secret);\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tassertThat(repo.secret()).isEqualTo(\"secret\");\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenImmutableListThenReturnsSecuredImmutableList() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tList<Flight> flights = List.of(this.flight);\n\t\tList<Flight> secured = proxy(factory, flights);\n\t\tsecured.forEach(\n\t\t\t\t(flight) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude));\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(secured::clear);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenImmutableSetThenReturnsSecuredImmutableSet() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tSet<Flight> flights = Set.of(this.flight);\n\t\tSet<Flight> secured = proxy(factory, flights);\n\t\tsecured.forEach(\n\t\t\t\t(flight) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude));\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(secured::clear);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenQueueThenReturnsSecuredQueue() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tQueue<Flight> flights = new LinkedList<>(List.of(this.flight));\n\t\tQueue<Flight> secured = proxy(factory, flights);\n\t\tassertThat(flights.size()).isEqualTo(secured.size());\n\t\tsecured.forEach(\n\t\t\t\t(flight) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude));\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenImmutableSortedSetThenReturnsSecuredImmutableSortedSet() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tSortedSet<User> users = Collections.unmodifiableSortedSet(new TreeSet<>(Set.of(this.alan)));\n\t\tSortedSet<User> secured = proxy(factory, users);\n\t\tsecured\n\t\t\t.forEach((user) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(user::getFirstName));\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(secured::clear);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenImmutableSortedMapThenReturnsSecuredImmutableSortedMap() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tSortedMap<String, User> users = Collections\n\t\t\t.unmodifiableSortedMap(new TreeMap<>(Map.of(this.alan.getId(), this.alan)));\n\t\tSortedMap<String, User> secured = proxy(factory, users);\n\t\tsecured.forEach(\n\t\t\t\t(id, user) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(user::getFirstName));\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(secured::clear);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenImmutableMapThenReturnsSecuredImmutableMap() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tMap<String, User> users = Map.of(this.alan.getId(), this.alan);\n\t\tMap<String, User> secured = proxy(factory, users);\n\t\tsecured.forEach(\n\t\t\t\t(id, user) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(user::getFirstName));\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(secured::clear);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenMutableListThenReturnsSecuredMutableList() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tList<Flight> flights = new ArrayList<>(List.of(this.flight));\n\t\tList<Flight> secured = proxy(factory, flights);\n\t\tsecured.forEach(\n\t\t\t\t(flight) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude));\n\t\tsecured.clear();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenMutableSetThenReturnsSecuredMutableSet() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tSet<Flight> flights = new HashSet<>(Set.of(this.flight));\n\t\tSet<Flight> secured = proxy(factory, flights);\n\t\tsecured.forEach(\n\t\t\t\t(flight) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(flight::getAltitude));\n\t\tsecured.clear();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenMutableSortedSetThenReturnsSecuredMutableSortedSet() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tSortedSet<User> users = new TreeSet<>(Set.of(this.alan));\n\t\tSortedSet<User> secured = proxy(factory, users);\n\t\tsecured.forEach((u) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(u::getFirstName));\n\t\tsecured.clear();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenMutableSortedMapThenReturnsSecuredMutableSortedMap() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tSortedMap<String, User> users = new TreeMap<>(Map.of(this.alan.getId(), this.alan));\n\t\tSortedMap<String, User> secured = proxy(factory, users);\n\t\tsecured.forEach((id, u) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(u::getFirstName));\n\t\tsecured.clear();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenMutableMapThenReturnsSecuredMutableMap() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tMap<String, User> users = new HashMap<>(Map.of(this.alan.getId(), this.alan));\n\t\tMap<String, User> secured = proxy(factory, users);\n\t\tsecured.forEach((id, u) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(u::getFirstName));\n\t\tsecured.clear();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeForOptionalThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tOptional<Flight> flights = Optional.of(this.flight);\n\t\tassertThat(flights.get().getAltitude()).isEqualTo(35000d);\n\t\tOptional<Flight> secured = proxy(factory, flights);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> secured.ifPresent(Flight::getAltitude));\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeForSupplierThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tSupplier<Flight> flights = () -> this.flight;\n\t\tassertThat(flights.get().getAltitude()).isEqualTo(35000d);\n\t\tSupplier<Flight> secured = proxy(factory, flights);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> secured.get().getAltitude());\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeForStreamThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tStream<Flight> flights = Stream.of(this.flight);\n\t\tStream<Flight> secured = proxy(factory, flights);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> secured.forEach(Flight::getAltitude));\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeForArrayThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tFlight[] flights = { this.flight };\n\t\tFlight[] secured = proxy(factory, flights);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(secured[0]::getAltitude);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeForIteratorThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tIterator<Flight> flights = List.of(this.flight).iterator();\n\t\tIterator<Flight> secured = proxy(factory, flights);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> secured.next().getAltitude());\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeForIterableThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tIterable<User> users = new UserRepository();\n\t\tIterable<User> secured = proxy(factory, users);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> secured.forEach(User::getFirstName));\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeForClassThenHonors() {\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tClass<Flight> clazz = proxy(factory, Flight.class);\n\t\tassertThat(clazz.getSimpleName()).contains(\"SpringCGLIB$$\");\n\t\tFlight secured = proxy(factory, this.flight);\n\t\tassertThat(secured.getClass()).isSameAs(clazz);\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(secured::getAltitude);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void setAdvisorsWhenProxyThenVisits() {\n\t\tAuthorizationAdvisor advisor = mock(AuthorizationAdvisor.class);\n\t\tgiven(advisor.getAdvice()).willReturn(advisor);\n\t\tgiven(advisor.getPointcut()).willReturn(Pointcut.TRUE);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tfactory.setAdvisors(advisor);\n\t\tFlight flight = proxy(factory, this.flight);\n\t\tflight.getAltitude();\n\t\tverify(advisor, atLeastOnce()).getPointcut();\n\t}\n\n\t@Test\n\tpublic void setTargetVisitorThenUses() {\n\t\tTargetVisitor visitor = mock(TargetVisitor.class);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tfactory.setTargetVisitor(visitor);\n\t\tfactory.proxy(new Flight());\n\t\tverify(visitor).visit(any(), any());\n\t}\n\n\t@Test\n\tpublic void setTargetVisitorIgnoreValueTypesThenIgnores() {\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tassertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> factory.proxy(35).intValue());\n\t\tfactory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());\n\t\tassertThat(factory.proxy(35)).isEqualTo(35);\n\t}\n\n\t// TODO Find why callbacks property is serialized with Jackson 3, not with Jackson 2\n\t// FIXME: https://github.com/spring-projects/spring-security/issues/18077\n\t@Disabled(\"callbacks property is serialized with Jackson 3, not with Jackson 2\")\n\t@Test\n\tpublic void serializeWhenAuthorizationProxyObjectThenOnlyIncludesProxiedProperties() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.admin);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tUser user = proxy(factory, this.alan);\n\t\tJsonMapper mapper = new JsonMapper();\n\t\tString serialized = mapper.writeValueAsString(user);\n\t\tMap<String, Object> properties = mapper.readValue(serialized, Map.class);\n\t\tassertThat(properties).hasSize(3).containsKeys(\"id\", \"firstName\", \"lastName\");\n\t}\n\n\t@Test\n\tpublic void proxyWhenDefaultsThenInstanceOfAuthorizationProxy() {\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tFlight flight = proxy(factory, this.flight);\n\t\tassertThat(flight).isInstanceOf(AuthorizationProxy.class);\n\t\tFlight target = (Flight) ((AuthorizationProxy) flight).toAuthorizedTarget();\n\t\tassertThat(target).isSameAs(this.flight);\n\t}\n\n\t// gh-16819\n\t@Test\n\tvoid advisorsWhenWithDefaultsThenAreSorted() {\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();\n\t\tAnnotationAwareOrderComparator comparator = AnnotationAwareOrderComparator.INSTANCE;\n\t\tAuthorizationAdvisor previous = null;\n\t\tfor (AuthorizationAdvisor advisor : proxyFactory) {\n\t\t\tboolean ordered = previous == null || comparator.compare(previous, advisor) < 0;\n\t\t\tassertThat(ordered).isTrue();\n\t\t\tprevious = advisor;\n\t\t}\n\t}\n\n\t// gh-16819\n\t@Test\n\tvoid advisorsWhenWithReactiveDefaultsThenAreSorted() {\n\t\tAuthorizationAdvisorProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();\n\t\tAnnotationAwareOrderComparator comparator = AnnotationAwareOrderComparator.INSTANCE;\n\t\tAuthorizationAdvisor previous = null;\n\t\tfor (AuthorizationAdvisor advisor : proxyFactory) {\n\t\t\tboolean ordered = previous == null || comparator.compare(previous, advisor) < 0;\n\t\t\tassertThat(ordered).isTrue();\n\t\t\tprevious = advisor;\n\t\t}\n\t}\n\n\tprivate Authentication authenticated(String user, String... authorities) {\n\t\treturn TestAuthentication.authenticated(TestAuthentication.withUsername(user).authorities(authorities).build());\n\t}\n\n\tprivate <T> T proxy(AuthorizationProxyFactory factory, Object target) {\n\t\treturn (T) factory.proxy(target);\n\t}\n\n\tstatic class Flight {\n\n\t\t@PreAuthorize(\"hasRole('PILOT')\")\n\t\tDouble getAltitude() {\n\t\t\treturn 35000d;\n\t\t}\n\n\t}\n\n\tinterface Identifiable {\n\n\t\t@PreAuthorize(\"authentication.name == this.id || hasRole('ADMIN')\")\n\t\tString getFirstName();\n\n\t\t@PreAuthorize(\"authentication.name == this.id || hasRole('ADMIN')\")\n\t\tString getLastName();\n\n\t}\n\n\tpublic static class User implements Identifiable, Comparable<User> {\n\n\t\tprivate final String id;\n\n\t\tprivate final String firstName;\n\n\t\tprivate final String lastName;\n\n\t\tUser(String id, String firstName, String lastName) {\n\t\t\tthis.id = id;\n\t\t\tthis.firstName = firstName;\n\t\t\tthis.lastName = lastName;\n\t\t}\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getFirstName() {\n\t\t\treturn this.firstName;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getLastName() {\n\t\t\treturn this.lastName;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(User that) {\n\t\t\treturn this.id.compareTo(that.getId());\n\t\t}\n\n\t}\n\n\tstatic class UserRepository implements Iterable<User> {\n\n\t\tList<User> users = List.of(new User(\"1\", \"first\", \"last\"));\n\n\t\t@Override\n\t\tpublic Iterator<User> iterator() {\n\t\t\treturn this.users.iterator();\n\t\t}\n\n\t}\n\n\tinterface HasSecret {\n\n\t\tString secret();\n\n\t}\n\n\trecord Repository(@PreAuthorize(\"hasRole('ADMIN')\") String secret) implements HasSecret {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthorizationManagerFactoryTests.java",
    "content": "/*\n * Copyright 2002-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link AuthorizationManagerFactory}.\n *\n * @author Steve Riesenberg\n */\npublic class AuthorizationManagerFactoryTests {\n\n\t@Test\n\tpublic void permitAllReturnsSingleResultAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.permitAll();\n\t\tassertThat(authorizationManager).isInstanceOf(SingleResultAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void denyAllReturnsSingleResultAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.denyAll();\n\t\tassertThat(authorizationManager).isInstanceOf(SingleResultAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasRoleReturnsAuthorityAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasRole(\"USER\");\n\t\tassertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleReturnsAuthorityAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasAnyRole(\"USER\", \"ADMIN\");\n\t\tassertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasAllRolesReturnsAllAuthoritiesAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasAllRoles(\"authority1\", \"authority2\");\n\t\tassertThat(authorizationManager).isInstanceOf(AllAuthoritiesAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasAuthorityReturnsAuthorityAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasAuthority(\"authority1\");\n\t\tassertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityReturnsAuthorityAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasAnyAuthority(\"authority1\", \"authority2\");\n\t\tassertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasAllAuthoritiesReturnsAllAuthoritiesAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasAllAuthorities(\"authority1\", \"authority2\");\n\t\tassertThat(authorizationManager).isInstanceOf(AllAuthoritiesAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void authenticatedReturnsAuthenticatedAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.authenticated();\n\t\tassertThat(authorizationManager).isInstanceOf(AuthenticatedAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedReturnsAuthenticatedAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.fullyAuthenticated();\n\t\tassertThat(authorizationManager).isInstanceOf(AuthenticatedAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void rememberMeReturnsAuthenticatedAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.rememberMe();\n\t\tassertThat(authorizationManager).isInstanceOf(AuthenticatedAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void anonymousReturnsAuthenticatedAuthorizationManagerByDefault() {\n\t\tAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.anonymous();\n\t\tassertThat(authorizationManager).isInstanceOf(AuthenticatedAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void anonymousWhenAdditionalAuthorizationThenNotInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tfactory.anonymous();\n\n\t\tverifyNoInteractions(additional);\n\t}\n\n\t@Test\n\tpublic void permitAllWhenAdditionalAuthorizationThenNotInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tfactory.permitAll();\n\n\t\tverifyNoInteractions(additional);\n\t}\n\n\t@Test\n\tpublic void denyAllAllWhenAdditionalAuthorizationThenNotInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tfactory.permitAll();\n\n\t\tverifyNoInteractions(additional);\n\t}\n\n\t@Test\n\tpublic void hasRoleWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertUserGranted(factory.hasRole(\"USER\"));\n\t\tassertUserDenied(factory.hasRole(\"USER\"));\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertUserGranted(factory.hasAnyRole(\"USER\"));\n\t\tassertUserDenied(factory.hasAnyRole(\"USER\"));\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\n\t}\n\n\t@Test\n\tpublic void hasAllRolesWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertUserGranted(factory.hasAllRoles(\"USER\"));\n\t\tassertUserDenied(factory.hasAllRoles(\"USER\"));\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\n\t}\n\n\t@Test\n\tpublic void hasAuthorityWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertUserGranted(factory.hasAuthority(\"ROLE_USER\"));\n\t\tassertUserDenied(factory.hasAuthority(\"ROLE_USER\"));\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertUserGranted(factory.hasAnyAuthority(\"ROLE_USER\"));\n\t\tassertUserDenied(factory.hasAnyAuthority(\"ROLE_USER\"));\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\n\t}\n\n\t@Test\n\tpublic void hasAllAuthoritiesWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertUserGranted(factory.hasAllAuthorities(\"ROLE_USER\"));\n\t\tassertUserDenied(factory.hasAllAuthorities(\"ROLE_USER\"));\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void authenticatedWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertUserGranted(factory.authenticated());\n\t\tassertUserDenied(factory.authenticated());\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertUserGranted(factory.fullyAuthenticated());\n\t\tassertUserDenied(factory.fullyAuthenticated());\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenAdditionalAuthorizationThenInvoked() {\n\t\tAuthorizationManager<String> additional = mock(AuthorizationManager.class);\n\t\tgiven(additional.authorize(any(), any())).willReturn(new AuthorizationDecision(true),\n\t\t\t\tnew AuthorizationDecision(false));\n\t\tDefaultAuthorizationManagerFactory<String> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setAdditionalAuthorization(additional);\n\n\t\tassertThat(factory.rememberMe().authorize(() -> TestAuthentication.rememberMeUser(), \"\").isGranted()).isTrue();\n\t\tassertThat(factory.rememberMe().authorize(() -> TestAuthentication.rememberMeUser(), \"\").isGranted()).isFalse();\n\n\t\tverify(additional, times(2)).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void builderWhenEmptyAdditionalAuthoritiesThenIllegalStateException() {\n\t\tAuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object> builder = AuthorizationManagerFactories\n\t\t\t.multiFactor();\n\t\tassertThatIllegalStateException().isThrownBy(() -> builder.build());\n\t}\n\n\t@Test\n\tpublic void builderWhenAdditionalAuthorityThenRequired() {\n\t\tAuthorizationManagerFactory<String> factory = AuthorizationManagerFactories.<String>multiFactor()\n\t\t\t.requireFactors(\"ROLE_ADMIN\")\n\t\t\t.build();\n\t\tassertUserDenied(factory.hasRole(\"USER\"));\n\t\tassertThat(factory.hasRole(\"USER\").authorize(() -> TestAuthentication.authenticatedAdmin(), \"\").isGranted())\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void builderWhenAdditionalAuthoritiesThenRequired() {\n\t\tAuthorizationManagerFactory<String> factory = AuthorizationManagerFactories.<String>multiFactor()\n\t\t\t.requireFactors(\"ROLE_ADMIN\", \"ROLE_USER\")\n\t\t\t.build();\n\t\tassertUserDenied(factory.hasRole(\"USER\"));\n\t\tassertThat(factory.hasRole(\"USER\").authorize(() -> TestAuthentication.authenticatedAdmin(), \"\").isGranted())\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void builderWhenWhenConditionThenAdditionalFactorsRequiredOnlyWhenConditionTrue() {\n\t\tAuthorizationManagerFactory<String> factory = AuthorizationManagerFactories.<String>multiFactor()\n\t\t\t.requireFactors(\"ROLE_ADMIN\")\n\t\t\t.when((auth) -> \"admin\".equals(auth.getName()))\n\t\t\t.build();\n\t\t// When condition is true (admin user), ROLE_ADMIN is required in addition to\n\t\t// hasRole(\"USER\")\n\t\tassertThat(factory.hasRole(\"USER\").authorize(() -> TestAuthentication.authenticatedAdmin(), \"\").isGranted())\n\t\t\t.isTrue();\n\t\t// When condition is false (non-admin user), additional factors are not required\n\t\tassertUserGranted(factory.hasRole(\"USER\"));\n\t}\n\n\t@Test\n\tpublic void builderWhenWhenConditionFalseThenUserWithoutRequiredFactorGranted() {\n\t\tAuthorizationManagerFactory<String> factory = AuthorizationManagerFactories.<String>multiFactor()\n\t\t\t.requireFactors(\"ROLE_ADMIN\")\n\t\t\t.when((auth) -> \"admin\".equals(auth.getName()))\n\t\t\t.build();\n\t\t// Non-admin user does not need ROLE_ADMIN for hasRole(\"USER\")\n\t\tassertThat(factory.hasRole(\"USER\").authorize(() -> TestAuthentication.authenticatedUser(), \"\").isGranted())\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void builderWhenWithWhenConditionThenConditionIsCustomized() {\n\t\tAuthorizationManagerFactory<String> factory = AuthorizationManagerFactories.<String>multiFactor()\n\t\t\t.requireFactors(\"ROLE_ADMIN\")\n\t\t\t.when((auth) -> \"admin\".equals(auth.getName()))\n\t\t\t.withWhen((current) -> (auth) -> current != null && current.test(auth) && auth.isAuthenticated())\n\t\t\t.build();\n\t\t// When condition is true (admin user and authenticated), ROLE_ADMIN is required\n\t\tassertThat(factory.hasRole(\"USER\").authorize(() -> TestAuthentication.authenticatedAdmin(), \"\").isGranted())\n\t\t\t.isTrue();\n\t\t// When condition is false (admin user but not authenticated), additional factors\n\t\t// are not required\n\t\tTestingAuthenticationToken unauthenticatedAdmin = new TestingAuthenticationToken(\"admin\", \"password\",\n\t\t\t\t\"ROLE_USER\");\n\t\tunauthenticatedAdmin.setAuthenticated(false);\n\t\tassertThat(factory.hasRole(\"USER\").authorize(() -> unauthenticatedAdmin, \"\").isGranted()).isTrue();\n\t\t// When condition is false (non-admin user), additional factors are not required\n\t\tassertUserGranted(factory.hasRole(\"USER\"));\n\t}\n\n\t@Test\n\tpublic void builderWhenWithWhenNullThenIllegalArgumentException() {\n\t\tAuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object> builder = AuthorizationManagerFactories\n\t\t\t.multiFactor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> builder.withWhen(null))\n\t\t\t.withMessage(\"condition cannot be null\");\n\t}\n\n\tprivate void assertUserGranted(AuthorizationManager<String> manager) {\n\t\tassertThat(manager.authorize(() -> TestAuthentication.authenticatedUser(), \"\").isGranted()).isTrue();\n\t}\n\n\tprivate void assertUserDenied(AuthorizationManager<String> manager) {\n\t\tassertThat(manager.authorize(() -> TestAuthentication.authenticatedUser(), \"\").isGranted()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link AuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class AuthorizationManagerTests {\n\n\t@Test\n\tpublic void verifyWhenCheckReturnedGrantedDecisionThenPasses() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_1\", \"ROLE_2\");\n\t\tObject object = new Object();\n\n\t\tSingleResultAuthorizationManager.permitAll().verify(() -> authentication, object);\n\t}\n\n\t@Test\n\tpublic void verifyWhenCheckReturnedNullThenPasses() {\n\t\tAuthorizationManager<Object> manager = (a, o) -> null;\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_1\", \"ROLE_2\");\n\t\tObject object = new Object();\n\n\t\tmanager.verify(() -> authentication, object);\n\t}\n\n\t@Test\n\tpublic void verifyWhenCheckReturnedDeniedDecisionThenAccessDeniedException() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_1\", \"ROLE_2\");\n\t\tObject object = new Object();\n\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> SingleResultAuthorizationManager.denyAll().verify(() -> authentication, object))\n\t\t\t.withMessage(\"Access Denied\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/AuthorizationManagersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link AuthorizationManagers}.\n *\n * @author Evgeniy Cheban\n */\nclass AuthorizationManagersTests {\n\n\t@Test\n\tvoid checkAnyOfWhenOneGrantedThenGrantedDecision() {\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf(SingleResultAuthorizationManager.permitAll(),\n\t\t\t\tSingleResultAuthorizationManager.permitAll());\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWithAllAbstainDefaultDecisionWhenOneGrantedThenGrantedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision,\n\t\t\t\t(a, o) -> new AuthorizationDecision(false), (a, o) -> new AuthorizationDecision(true));\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t// gh-13069\n\t@Test\n\tvoid checkAnyOfWhenAllNonAbstainingDeniesThenDeniedDecision() {\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf((a, o) -> new AuthorizationDecision(false),\n\t\t\t\t(a, o) -> null);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWhenEmptyThenDeniedDecision() {\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf();\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWithAllAbstainDefaultDecisionIsDeniedWhenEmptyThenDeniedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWithAllAbstainDefaultDecisionIsGrantedWhenEmptyThenGrantedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWithAllAbstainDefaultDecisionIsAbstainWhenEmptyThenAbstainDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = null;\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWhenAllAbstainDefaultDecisionIsGrantedAndAllManagersAbstainThenGrantedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision, (a, o) -> null);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWhenAllAbstainDefaultDecisionIsDeniedAndAllManagersAbstainThenDeniedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision, (a, o) -> null);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWhenAllAbstainDefaultDecisionIsAbstainAndAllManagersAbstainThenAbstainDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = null;\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.anyOf(allAbstainDefaultDecision, (a, o) -> null);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenAllGrantedThenGrantedDecision() {\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(SingleResultAuthorizationManager.permitAll(),\n\t\t\t\tSingleResultAuthorizationManager.permitAll());\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAllOfWithAllAbstainDefaultDecisionWhenAllGrantedThenGrantedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(allAbstainDefaultDecision,\n\t\t\t\t(a, o) -> new AuthorizationDecision(true), (a, o) -> new AuthorizationDecision(true));\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t// gh-13069\n\t@Test\n\tvoid checkAllOfWhenAllNonAbstainingGrantsThenGrantedDecision() {\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf((a, o) -> new AuthorizationDecision(true),\n\t\t\t\t(a, o) -> null);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenOneDeniedThenDeniedDecision() {\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf((a, o) -> new AuthorizationDecision(true),\n\t\t\t\t(a, o) -> new AuthorizationDecision(false));\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAllOfWithAllAbstainDefaultDecisionWhenOneDeniedThenDeniedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(allAbstainDefaultDecision,\n\t\t\t\tSingleResultAuthorizationManager.permitAll(), SingleResultAuthorizationManager.denyAll());\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenEmptyThenGrantedDecision() {\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf();\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAllOfWithAllAbstainDefaultDecisionIsDeniedWhenEmptyThenDeniedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(allAbstainDefaultDecision);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAllOfWithAllAbstainDefaultDecisionIsGrantedWhenEmptyThenGrantedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(allAbstainDefaultDecision);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAllOfWithAllAbstainDefaultDecisionIsAbstainWhenEmptyThenAbstainDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = null;\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(allAbstainDefaultDecision);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenAllAbstainDefaultDecisionIsDeniedAndAllManagersAbstainThenDeniedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(false);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(allAbstainDefaultDecision, (a, o) -> null);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenAllAbstainDefaultDecisionIsGrantedAndAllManagersAbstainThenGrantedDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = new AuthorizationDecision(true);\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(allAbstainDefaultDecision, (a, o) -> null);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenAllAbstainDefaultDecisionIsAbstainAndAllManagersAbstainThenAbstainDecision() {\n\t\tAuthorizationDecision allAbstainDefaultDecision = null;\n\t\tAuthorizationManager<?> composed = AuthorizationManagers.allOf(allAbstainDefaultDecision, (a, o) -> null);\n\t\tAuthorizationResult decision = composed.authorize(null, null);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tvoid checkNotWhenEmptyThenAbstainedDecision() {\n\t\tAuthorizationManager<?> negated = AuthorizationManagers.not((a, o) -> null);\n\t\tAuthorizationResult decision = negated.authorize(null, null);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tvoid checkNotWhenGrantedThenDeniedDecision() {\n\t\tAuthorizationManager<?> negated = AuthorizationManagers.not((a, o) -> new AuthorizationDecision(true));\n\t\tAuthorizationResult decision = negated.authorize(null, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/ConditionalAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * Tests for {@link ConditionalAuthorizationManager}.\n *\n * @author Rob Winch\n */\npublic class ConditionalAuthorizationManagerTests {\n\n\t@Test\n\tvoid authorizeWhenAuthenticationIsNullThenUsesWhenFalse() {\n\t\tConditionalAuthorizationManager<Object> manager = ConditionalAuthorizationManager.when((auth) -> true)\n\t\t\t.whenTrue(SingleResultAuthorizationManager.denyAll())\n\t\t\t.whenFalse(SingleResultAuthorizationManager.permitAll())\n\t\t\t.build();\n\n\t\tAuthorizationResult result = manager.authorize(() -> null, new Object());\n\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenConditionIsTrueThenUsesWhenTrue() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tConditionalAuthorizationManager<Object> manager = ConditionalAuthorizationManager.when((auth) -> true)\n\t\t\t.whenTrue(SingleResultAuthorizationManager.permitAll())\n\t\t\t.whenFalse(SingleResultAuthorizationManager.denyAll())\n\t\t\t.build();\n\n\t\tAuthorizationResult result = manager.authorize(() -> authentication, new Object());\n\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenConditionIsFalseThenUsesWhenFalse() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tConditionalAuthorizationManager<Object> manager = ConditionalAuthorizationManager.when((auth) -> false)\n\t\t\t.whenTrue(SingleResultAuthorizationManager.permitAll())\n\t\t\t.whenFalse(SingleResultAuthorizationManager.denyAll())\n\t\t\t.build();\n\n\t\tAuthorizationResult result = manager.authorize(() -> authentication, new Object());\n\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid authorizeWhenConditionDependsOnAuthenticationThenEvaluatesCorrectly() {\n\t\tAuthentication admin = new TestingAuthenticationToken(\"admin\", \"password\", \"ROLE_ADMIN\");\n\t\tAuthentication user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tConditionalAuthorizationManager<Object> manager = ConditionalAuthorizationManager\n\t\t\t.when((auth) -> auth.getAuthorities().stream().anyMatch((a) -> \"ROLE_ADMIN\".equals(a.getAuthority())))\n\t\t\t.whenTrue(SingleResultAuthorizationManager.permitAll())\n\t\t\t.whenFalse(SingleResultAuthorizationManager.denyAll())\n\t\t\t.build();\n\n\t\tassertThat(manager.authorize(() -> admin, new Object()).isGranted()).isTrue();\n\t\tassertThat(manager.authorize(() -> user, new Object()).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid buildWhenWhenFalseNotSetThenDefaultsToPermitAll() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tConditionalAuthorizationManager<Object> manager = ConditionalAuthorizationManager.when((auth) -> false)\n\t\t\t.whenTrue(SingleResultAuthorizationManager.denyAll())\n\t\t\t.build();\n\n\t\tAuthorizationResult result = manager.authorize(() -> authentication, new Object());\n\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid whenWhenConditionIsNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ConditionalAuthorizationManager.when(null))\n\t\t\t.withMessage(\"condition cannot be null\");\n\t}\n\n\t@Test\n\tvoid buildWhenWhenTrueNotSetThenThrowsException() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> ConditionalAuthorizationManager.when((auth) -> true).build())\n\t\t\t.withMessage(\"whenTrue is required\");\n\t}\n\n\t@Test\n\tvoid builderWhenWhenTrueIsNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> ConditionalAuthorizationManager.when((auth) -> true).whenTrue(null))\n\t\t\t.withMessage(\"whenTrue cannot be null\");\n\t}\n\n\t@Test\n\tvoid builderWhenWhenFalseIsNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> ConditionalAuthorizationManager.when((auth) -> true)\n\t\t\t\t.whenTrue(SingleResultAuthorizationManager.permitAll())\n\t\t\t\t.whenFalse(null))\n\t\t\t.withMessage(\"whenFalse cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/FactorAuthorizationDecisionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link FactorAuthorizationDecision}.\n *\n * @author Rob Winch\n * @since 7.0\n */\nclass FactorAuthorizationDecisionTests {\n\n\t@Test\n\tvoid isGrantedWhenEmptyThenTrue() {\n\t\tFactorAuthorizationDecision decision = new FactorAuthorizationDecision(List.of());\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid isGrantedWhenNotEmptyThenFalse() {\n\t\tRequiredFactor requiredPassword = RequiredFactor.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.build();\n\t\tRequiredFactorError missingPassword = RequiredFactorError.createMissing(requiredPassword);\n\t\tFactorAuthorizationDecision decision = new FactorAuthorizationDecision(List.of(missingPassword));\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid getFactorErrors() {\n\t\tRequiredFactor requiredPassword = RequiredFactor.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.build();\n\t\tRequiredFactorError missingPassword = RequiredFactorError.createMissing(requiredPassword);\n\t\tList<RequiredFactorError> factorErrors = List.of(missingPassword);\n\t\tFactorAuthorizationDecision decision = new FactorAuthorizationDecision(factorErrors);\n\t\tassertThat(decision.getFactorErrors()).isEqualTo(factorErrors);\n\t}\n\n\t@Test\n\tvoid constructorWhenNullThenThrowIllegalArgumentException() {\n\t\tList<RequiredFactorError> factorErrors = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new FactorAuthorizationDecision(factorErrors));\n\t}\n\n\t@Test\n\tvoid constructorWhenContainsNullThenThrowIllegalArgumentException() {\n\t\tRequiredFactor requiredPassword = RequiredFactor.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.build();\n\t\tRequiredFactorError missingPassword = RequiredFactorError.createMissing(requiredPassword);\n\t\tList<RequiredFactorError> hasNullValue = Arrays.asList(missingPassword, null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new FactorAuthorizationDecision(hasNullValue));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/MapRequiredAuthoritiesRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Test {@link MapRequiredAuthoritiesRepository}.\n *\n * @author Rob Winch\n * @since 7.0\n */\nclass MapRequiredAuthoritiesRepositoryTests {\n\n\tprivate MapRequiredAuthoritiesRepository repository = new MapRequiredAuthoritiesRepository();\n\n\tprivate String username = \"user\";\n\n\tprivate List<String> authorities = List.of(FactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\tFactorGrantedAuthority.OTT_AUTHORITY);\n\n\t@Test\n\tvoid workflow() {\n\t\tthis.repository.saveRequiredAuthorities(this.username, this.authorities);\n\t\tassertThat(this.repository.findRequiredAuthorities(this.username))\n\t\t\t.containsExactlyInAnyOrderElementsOf(this.authorities);\n\t\tList<String> otherAuthorities = List.of(FactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\tFactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t\tthis.repository.saveRequiredAuthorities(this.username, otherAuthorities);\n\t\tassertThat(this.repository.findRequiredAuthorities(this.username))\n\t\t\t.containsExactlyInAnyOrderElementsOf(otherAuthorities);\n\t\tthis.repository.deleteRequiredAuthorities(this.username);\n\t\tassertThat(this.repository.findRequiredAuthorities(this.username)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findRequiredAuthoritiesWhenNullUsernameThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repository.findRequiredAuthorities(null));\n\t}\n\n\t@Test\n\tvoid findRequiredAuthoritiesWhenEmptyUsernameThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repository.findRequiredAuthorities(\"\"));\n\t}\n\n\t@Test\n\tvoid saveRequiredAuthoritiesWhenNullUsernameThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.repository.saveRequiredAuthorities(null, this.authorities));\n\t}\n\n\t@Test\n\tvoid saveRequiredAuthoritiesWhenEmptyUsernameThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.repository.saveRequiredAuthorities(\"\", this.authorities));\n\t}\n\n\t@Test\n\tvoid saveRequiredAuthoritiesWhenNullAuthoritiesThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.repository.saveRequiredAuthorities(this.username, null));\n\t}\n\n\t@Test\n\tvoid deleteRequiredAuthoritiesWhenNullUsernameThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repository.deleteRequiredAuthorities(null));\n\t}\n\n\t@Test\n\tvoid deleteRequiredAuthoritiesWhenEmptyUsernameThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repository.deleteRequiredAuthorities(\"\"));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link ObservationAuthorizationManager}\n */\npublic class ObservationAuthorizationManagerTests {\n\n\tprivate ObservationRegistry registry;\n\n\tprivate ObservationHandler<Observation.Context> handler;\n\n\tprivate AuthorizationManager<Object> authorizationManager;\n\n\tprivate ObservationAuthorizationManager<Object> tested;\n\n\tprivate final Supplier<Authentication> token = () -> new TestingAuthenticationToken(\"user\", \"pass\");\n\n\tprivate final Object object = new Object();\n\n\tprivate final AuthorizationDecision grant = new AuthorizationDecision(true);\n\n\tprivate final AuthorizationDecision deny = new AuthorizationDecision(false);\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.handler = mock(ObservationHandler.class);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(this.handler);\n\t\tthis.registry = registry;\n\t\tthis.authorizationManager = mock(AuthorizationManager.class);\n\t\tthis.tested = new ObservationAuthorizationManager<>(this.registry, this.authorizationManager);\n\t}\n\n\t@Test\n\tvoid verifyWhenDefaultsThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(this.grant);\n\t\tthis.tested.verify(this.token, this.object);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isNull();\n\t\tassertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class);\n\t\tAuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();\n\t\tassertThat(context.getAuthentication()).isNull();\n\t\tassertThat(context.getObject()).isEqualTo(this.object);\n\t\tassertThat(context.getAuthorizationResult()).isEqualTo(this.grant);\n\t}\n\n\t@Test\n\tvoid verifyWhenErrorsThenObserves() {\n\t\tMessageSource source = mock(MessageSource.class);\n\t\tthis.tested.setMessageSource(source);\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(this.deny);\n\t\tgiven(source.getMessage(eq(\"AbstractAccessDecisionManager.accessDenied\"), any(), any(), any()))\n\t\t\t.willReturn(\"accessDenied\");\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.tested.verify(this.token, this.object));\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isInstanceOf(AccessDeniedException.class);\n\t\tassertThat(Optional.ofNullable(captor.getValue().getError()).map(Throwable::getMessage).orElse(\"\"))\n\t\t\t.isEqualTo(\"accessDenied\");\n\t\tassertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class);\n\t\tAuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();\n\t\tassertThat(context.getAuthentication()).isNull();\n\t\tassertThat(context.getObject()).isEqualTo(this.object);\n\t\tassertThat(context.getAuthorizationResult()).isEqualTo(this.deny);\n\t}\n\n\t@Test\n\tvoid verifyWhenLooksUpAuthenticationThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willAnswer((invocation) -> {\n\t\t\t((Supplier<Authentication>) invocation.getArgument(0)).get();\n\t\t\treturn this.grant;\n\t\t});\n\t\tthis.tested.verify(this.token, this.object);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isNull();\n\t\tAuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();\n\t\tassertThat(context.getAuthentication()).isEqualTo(this.token.get());\n\t\tassertThat(context.getObject()).isEqualTo(this.object);\n\t\tassertThat(context.getAuthorizationResult()).isEqualTo(this.grant);\n\t}\n\n\t@Test\n\tvoid setObservationConventionWhenNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.tested.setObservationConvention(null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link ObservationAuthorizationManager}\n */\npublic class ObservationReactiveAuthorizationManagerTests {\n\n\tprivate ObservationRegistry registry;\n\n\tprivate ObservationHandler<Observation.Context> handler;\n\n\tprivate ReactiveAuthorizationManager<Object> authorizationManager;\n\n\tprivate ObservationReactiveAuthorizationManager<Object> tested;\n\n\tprivate final Mono<Authentication> token = Mono.just(new TestingAuthenticationToken(\"user\", \"pass\"));\n\n\tprivate final Object object = new Object();\n\n\tprivate final AuthorizationDecision grant = new AuthorizationDecision(true);\n\n\tprivate final AuthorizationDecision deny = new AuthorizationDecision(false);\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.handler = mock(ObservationHandler.class);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(this.handler);\n\t\tthis.registry = registry;\n\t\tthis.authorizationManager = mock(ReactiveAuthorizationManager.class);\n\t\tthis.tested = new ObservationReactiveAuthorizationManager<>(this.registry, this.authorizationManager);\n\t}\n\n\t@Test\n\tvoid verifyWhenDefaultsThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(Mono.just(this.grant));\n\t\tthis.tested.verify(this.token, this.object).block();\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isNull();\n\t\tassertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class);\n\t\tAuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();\n\t\tassertThat(context.getAuthentication()).isNull();\n\t\tassertThat(context.getObject()).isEqualTo(this.object);\n\t\tassertThat(context.getAuthorizationResult()).isEqualTo(this.grant);\n\t}\n\n\t@Test\n\tvoid verifyWhenErrorsThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(Mono.just(this.deny));\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.tested.verify(this.token, this.object).block());\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isInstanceOf(AccessDeniedException.class);\n\t\tassertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class);\n\t\tAuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();\n\t\tassertThat(context.getAuthentication()).isNull();\n\t\tassertThat(context.getObject()).isEqualTo(this.object);\n\t\tassertThat(context.getAuthorizationResult()).isEqualTo(this.deny);\n\t}\n\n\t@Test\n\tvoid verifyWhenLooksUpAuthenticationThenObserves() {\n\t\tgiven(this.handler.supportsContext(any())).willReturn(true);\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willAnswer((invocation) -> {\n\t\t\t((Mono<Authentication>) invocation.getArgument(0)).block();\n\t\t\treturn Mono.just(this.grant);\n\t\t});\n\t\tthis.tested.verify(this.token, this.object).block();\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(this.handler).onStart(captor.capture());\n\t\tassertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME);\n\t\tassertThat(captor.getValue().getError()).isNull();\n\t\tAuthorizationObservationContext<?> context = (AuthorizationObservationContext<?>) captor.getValue();\n\t\tassertThat(context.getAuthentication()).isEqualTo(this.token.block());\n\t\tassertThat(context.getObject()).isEqualTo(this.object);\n\t\tassertThat(context.getAuthorizationResult()).isEqualTo(this.grant);\n\t}\n\n\t@Test\n\tvoid setObservationConventionWhenNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.tested.setObservationConvention(null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/ReactiveAuthorizationAdvisorProxyFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.method.AuthorizationAdvisor;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\npublic class ReactiveAuthorizationAdvisorProxyFactoryTests {\n\n\tprivate final Authentication user = TestAuthentication.authenticatedUser();\n\n\tprivate final Authentication admin = TestAuthentication.authenticatedAdmin();\n\n\tprivate final Flight flight = new Flight();\n\n\tprivate final User alan = new User(\"alan\", \"alan\", \"turing\");\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeThenHonors() {\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();\n\t\tFlight flight = new Flight();\n\t\tStepVerifier\n\t\t\t.create(flight.getAltitude().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))\n\t\t\t.expectNext(35000d)\n\t\t\t.verifyComplete();\n\t\tFlight secured = proxy(factory, flight);\n\t\tStepVerifier\n\t\t\t.create(secured.getAltitude().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeOnInterfaceThenHonors() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();\n\t\tStepVerifier\n\t\t\t.create(this.alan.getFirstName().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))\n\t\t\t.expectNext(\"alan\")\n\t\t\t.verifyComplete();\n\t\tUser secured = proxy(factory, this.alan);\n\t\tStepVerifier\n\t\t\t.create(secured.getFirstName().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t\tStepVerifier\n\t\t\t.create(secured.getFirstName()\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authenticated(\"alan\"))))\n\t\t\t.expectNext(\"alan\")\n\t\t\t.verifyComplete();\n\t\tStepVerifier\n\t\t\t.create(secured.getFirstName().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.admin)))\n\t\t\t.expectNext(\"alan\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeOnRecordThenHonors() {\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();\n\t\tHasSecret repo = new Repository(Mono.just(\"secret\"));\n\t\tStepVerifier.create(repo.secret().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))\n\t\t\t.expectNext(\"secret\")\n\t\t\t.verifyComplete();\n\t\tHasSecret secured = proxy(factory, repo);\n\t\tStepVerifier.create(secured.secret().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t\tStepVerifier.create(secured.secret().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.admin)))\n\t\t\t.expectNext(\"secret\")\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeOnFluxThenHonors() {\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();\n\t\tFlux<Flight> flights = Flux.just(this.flight);\n\t\tFlux<Flight> secured = proxy(factory, flights);\n\t\tStepVerifier\n\t\t\t.create(secured.flatMap(Flight::getAltitude)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void proxyWhenPreAuthorizeForClassThenHonors() {\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();\n\t\tClass<Flight> clazz = proxy(factory, Flight.class);\n\t\tassertThat(clazz.getSimpleName()).contains(\"SpringCGLIB$$\");\n\t\tFlight secured = proxy(factory, this.flight);\n\t\tStepVerifier\n\t\t\t.create(secured.getAltitude().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void setAdvisorsWhenProxyThenVisits() {\n\t\tAuthorizationAdvisor advisor = mock(AuthorizationAdvisor.class);\n\t\tgiven(advisor.getAdvice()).willReturn(advisor);\n\t\tgiven(advisor.getPointcut()).willReturn(Pointcut.TRUE);\n\t\tAuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();\n\t\tfactory.setAdvisors(advisor);\n\t\tFlight flight = proxy(factory, this.flight);\n\t\tflight.getAltitude();\n\t\tverify(advisor, atLeastOnce()).getPointcut();\n\t}\n\n\tprivate Authentication authenticated(String user, String... authorities) {\n\t\treturn TestAuthentication.authenticated(TestAuthentication.withUsername(user).authorities(authorities).build());\n\t}\n\n\tprivate <T> T proxy(AuthorizationProxyFactory factory, Object target) {\n\t\treturn (T) factory.proxy(target);\n\t}\n\n\tstatic class Flight {\n\n\t\t@PreAuthorize(\"hasRole('PILOT')\")\n\t\tMono<Double> getAltitude() {\n\t\t\treturn Mono.just(35000d);\n\t\t}\n\n\t}\n\n\tinterface Identifiable {\n\n\t\t@PreAuthorize(\"authentication.name == this.id || hasRole('ADMIN')\")\n\t\tMono<String> getFirstName();\n\n\t\t@PreAuthorize(\"authentication.name == this.id || hasRole('ADMIN')\")\n\t\tMono<String> getLastName();\n\n\t}\n\n\tpublic static class User implements Identifiable, Comparable<User> {\n\n\t\tprivate final String id;\n\n\t\tprivate final String firstName;\n\n\t\tprivate final String lastName;\n\n\t\tUser(String id, String firstName, String lastName) {\n\t\t\tthis.id = id;\n\t\t\tthis.firstName = firstName;\n\t\t\tthis.lastName = lastName;\n\t\t}\n\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<String> getFirstName() {\n\t\t\treturn Mono.just(this.firstName);\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<String> getLastName() {\n\t\t\treturn Mono.just(this.lastName);\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(User that) {\n\t\t\treturn this.id.compareTo(that.getId());\n\t\t}\n\n\t}\n\n\tstatic class UserRepository implements Iterable<User> {\n\n\t\tList<User> users = List.of(new User(\"1\", \"first\", \"last\"));\n\n\t\tFlux<User> findAll() {\n\t\t\treturn Flux.fromIterable(this.users);\n\t\t}\n\n\t\t@Override\n\t\tpublic Iterator<User> iterator() {\n\t\t\treturn this.users.iterator();\n\t\t}\n\n\t}\n\n\tinterface HasSecret {\n\n\t\tMono<String> secret();\n\n\t}\n\n\trecord Repository(@PreAuthorize(\"hasRole('ADMIN')\") Mono<String> secret) implements HasSecret {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/RequiredAuthoritiesAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * Tests for {@link RequiredAuthoritiesAuthorizationManager}.\n *\n * @author Rob Winch\n * @since 7.0\n */\n@ExtendWith(MockitoExtension.class)\nclass RequiredAuthoritiesAuthorizationManagerTests {\n\n\t@Mock\n\tprivate RequiredAuthoritiesRepository repository;\n\n\tprivate static final Object DOES_NOT_MATTER = \"\";\n\n\tprivate RequiredAuthoritiesAuthorizationManager<Object> manager;\n\n\tprivate Supplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\"ROLE_USER\", \"ROLE_ADMIN\");\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.manager = new RequiredAuthoritiesAuthorizationManager<>(this.repository);\n\t}\n\n\t@Test\n\tvoid constructorWhenNullRepositoryThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RequiredAuthoritiesAuthorizationManager(null));\n\t}\n\n\t@Test\n\tvoid authorizeWhenNoResults() {\n\t\treturnAuthorities(Collections.emptyList());\n\t\tassertGranted();\n\t}\n\n\t@Test\n\tvoid authorizeWhenAdditionalAuthoriteisAndGranted() {\n\t\treturnAuthorities(\n\t\t\t\tthis.authentication.get().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList());\n\t\tassertGranted();\n\t}\n\n\t@Test\n\tvoid authorizeWhenAdditionalAuthoriteisAndDenied() {\n\t\treturnAuthorities(List.of(\"NOT_FOUND\"));\n\t\tassertDenied();\n\t}\n\n\t@Test\n\tvoid authorizeWhenOneFoundAndDenied() {\n\t\treturnAuthorities(List.of(\"ROLE_USER\", \"NOT_FOUND\"));\n\t\tassertDenied();\n\t}\n\n\tprivate void returnAuthorities(List<String> authorities) {\n\t\tgiven(this.repository.findRequiredAuthorities(any())).willReturn(authorities);\n\t}\n\n\tprivate void assertGranted() {\n\t\tAuthorizationResult authz = this.manager.authorize(this.authentication, DOES_NOT_MATTER);\n\t\tassertThat(authz.isGranted()).isTrue();\n\t}\n\n\tprivate void assertDenied() {\n\t\tAuthorizationResult authz = this.manager.authorize(this.authentication, DOES_NOT_MATTER);\n\t\tassertThat(authz.isGranted()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/RequiredFactorErrorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.time.Duration;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link RequiredFactorError}.\n *\n * @author Rob Winch\n * @since 7.0\n */\nclass RequiredFactorErrorTests {\n\n\tpublic static final RequiredFactor REQUIRED_FACTOR = RequiredFactor\n\t\t.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t.validDuration(Duration.ofHours(1))\n\t\t.build();\n\n\t@Test\n\tvoid createMissing() {\n\t\tRequiredFactorError error = RequiredFactorError.createMissing(REQUIRED_FACTOR);\n\t\tassertThat(error.isMissing()).isTrue();\n\t\tassertThat(error.isExpired()).isFalse();\n\t\tassertThat(error.getRequiredFactor()).isEqualTo(REQUIRED_FACTOR);\n\t}\n\n\t@Test\n\tvoid createExpired() {\n\t\tRequiredFactorError error = RequiredFactorError.createExpired(REQUIRED_FACTOR);\n\t\tassertThat(error.isMissing()).isFalse();\n\t\tassertThat(error.isExpired()).isTrue();\n\t\tassertThat(error.getRequiredFactor()).isEqualTo(REQUIRED_FACTOR);\n\t}\n\n\t@Test\n\tvoid createExpiredWhenNullValidDurationThenIllegalArgumentException() {\n\t\tRequiredFactor requiredPassword = RequiredFactor.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> RequiredFactorError.createExpired(requiredPassword));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/RequiredFactorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.time.Duration;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link RequiredFactor}.\n *\n * @author Rob Winch\n * @since 7.0\n */\nclass RequiredFactorTests {\n\n\t@Test\n\tvoid builderWhenNullAuthorityIllegalArgumentException() {\n\t\tRequiredFactor.Builder builder = RequiredFactor.builder();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> builder.build());\n\t}\n\n\t@Test\n\tvoid withAuthorityThenEquals() {\n\t\tRequiredFactor requiredPassword = RequiredFactor.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.build();\n\t\tassertThat(requiredPassword.getAuthority()).isEqualTo(FactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t\tassertThat(requiredPassword.getValidDuration()).isNull();\n\t}\n\n\t@Test\n\tvoid builderValidDurationThenEquals() {\n\t\tDuration validDuration = Duration.ofMinutes(1);\n\t\tRequiredFactor requiredPassword = RequiredFactor.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t.validDuration(validDuration)\n\t\t\t.build();\n\t\tassertThat(requiredPassword.getAuthority()).isEqualTo(FactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t\tassertThat(requiredPassword.getValidDuration()).isEqualTo(validDuration);\n\t}\n\n\t@Test\n\tvoid builderAuthorizationCodeAuthority() {\n\t\tassertBuilderSetsAuthority(RequiredFactor.Builder::authorizationCodeAuthority,\n\t\t\t\tFactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid builderBearerTokenAuthority() {\n\t\tassertBuilderSetsAuthority(RequiredFactor.Builder::bearerTokenAuthority,\n\t\t\t\tFactorGrantedAuthority.BEARER_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid builderCasAuthority() {\n\t\tassertBuilderSetsAuthority(RequiredFactor.Builder::casAuthority, FactorGrantedAuthority.CAS_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid builderPasswordAuthority() {\n\t\tassertBuilderSetsAuthority(RequiredFactor.Builder::passwordAuthority,\n\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid builderOttAuthority() {\n\t\tassertBuilderSetsAuthority(RequiredFactor.Builder::ottAuthority, FactorGrantedAuthority.OTT_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid builderSamlAuthority() {\n\t\tassertBuilderSetsAuthority(RequiredFactor.Builder::samlAuthority,\n\t\t\t\tFactorGrantedAuthority.SAML_RESPONSE_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid builderWebauthnAuthority() {\n\t\tassertBuilderSetsAuthority(RequiredFactor.Builder::webauthnAuthority,\n\t\t\t\tFactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t}\n\n\t@Test\n\tvoid builderX509Authority() {\n\t\tassertBuilderSetsAuthority(RequiredFactor.Builder::x509Authority, FactorGrantedAuthority.X509_AUTHORITY);\n\t}\n\n\tprivate static void assertBuilderSetsAuthority(Consumer<RequiredFactor.Builder> configure, String expected) {\n\t\tRequiredFactor.Builder builder = RequiredFactor.builder();\n\t\tconfigure.accept(builder);\n\t\tRequiredFactor requiredFactor = builder.build();\n\t\tassertThat(requiredFactor.getAuthority()).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/SingleResultAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link SingleResultAuthorizationManager}.\n *\n * @author Max Batischev\n */\npublic class SingleResultAuthorizationManagerTests {\n\n\tprivate SingleResultAuthorizationManager<?> manager;\n\n\t@Test\n\tvoid authorizeWhenManagerWithGrantedAuthorizationResultIsCreatedThenAuthorizes() {\n\t\tthis.manager = new SingleResultAuthorizationManager<>(new AuthorizationDecision(true));\n\n\t\tAuthorizationResult result = this.manager.authorize(null, null);\n\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkWhenManagerWithGrantedDecisionIsCreatedThenAuthorizes() {\n\t\tthis.manager = new SingleResultAuthorizationManager<>(new AuthorizationDecision(true));\n\n\t\tAuthorizationResult result = this.manager.authorize(null, null);\n\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkWhenManagerWithGrantedCustomAuthorizationResultIsCreatedThenFails() {\n\t\tthis.manager = new SingleResultAuthorizationManager<>((AuthorizationResult) () -> true);\n\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.manager.authorize(null, null));\n\t}\n\n\t@Test\n\tvoid authorizeWhenPermitManagerUsesThenAuthorize() {\n\t\tAuthorizationResult result = SingleResultAuthorizationManager.permitAll().authorize(null, null);\n\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenDenyManagerUsesThenDeny() {\n\t\tAuthorizationResult result = SingleResultAuthorizationManager.denyAll().authorize(null, null);\n\n\t\tassertThat(result.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid constructWhenNullResultIsPresentThenFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SingleResultAuthorizationManager<>(null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/SpringAuthorizationEventPublisherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization;\n\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.event.AuthorizationDeniedEvent;\nimport org.springframework.security.core.Authentication;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.isA;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link SpringAuthorizationEventPublisher}\n *\n * @author Parikshit Dutta\n */\npublic class SpringAuthorizationEventPublisherTests {\n\n\tSupplier<Authentication> authentication = TestAuthentication::authenticatedUser;\n\n\tApplicationEventPublisher applicationEventPublisher;\n\n\tSpringAuthorizationEventPublisher authorizationEventPublisher;\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tthis.applicationEventPublisher = mock(ApplicationEventPublisher.class);\n\t\tthis.authorizationEventPublisher = new SpringAuthorizationEventPublisher(this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void testAuthenticationSuccessIsNotPublished() {\n\t\tAuthorizationDecision decision = new AuthorizationDecision(true);\n\t\tthis.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), decision);\n\t\tverifyNoInteractions(this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void testAuthenticationFailureIsPublished() {\n\t\tAuthorizationDecision decision = new AuthorizationDecision(false);\n\t\tthis.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), decision);\n\t\tverify(this.applicationEventPublisher).publishEvent(isA(AuthorizationDeniedEvent.class));\n\t}\n\n\t@Test\n\tpublic void publishWhenPredicateMatchesThenEvent() {\n\t\tPredicate<AuthorizationResult> test = mock(Predicate.class);\n\t\tgiven(test.test(any())).willReturn(true, false);\n\t\tthis.authorizationEventPublisher.setShouldPublishResult(test);\n\t\tAuthorizationResult result = new AuthorizationDecision(false);\n\t\tthis.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), result);\n\t\tverify(this.applicationEventPublisher).publishEvent(isA(AuthorizationDeniedEvent.class));\n\t\tthis.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), result);\n\t\tverifyNoMoreInteractions(this.applicationEventPublisher);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link AuthorizationManagerAfterMethodInterceptor}.\n *\n * @author Evgeniy Cheban\n * @author Gengwu Zhao\n */\npublic class AuthorizationManagerAfterMethodInterceptorTests {\n\n\t@Test\n\tpublic void instantiateWhenMethodMatcherNullThenException() {\n\t\tAuthorizationManager<MethodInvocationResult> mockAuthorizationManager = mock(AuthorizationManager.class);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(null, mockAuthorizationManager))\n\t\t\t.withMessage(\"pointcut cannot be null\");\n\t}\n\n\t@Test\n\tpublic void instantiateWhenAuthorizationManagerNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(mock(Pointcut.class), null))\n\t\t\t.withMessage(\"authorizationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void beforeWhenMockAuthorizationManagerThenCheckAndReturnedObject() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = mock(MethodInvocation.class);\n\t\tMethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(result.getResult());\n\t\tAuthorizationManager<MethodInvocationResult> mockAuthorizationManager = mock(AuthorizationManager.class);\n\t\tAuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockAuthorizationManager);\n\t\tObject returnedObject = advice.invoke(mockMethodInvocation);\n\t\tassertThat(returnedObject).isEqualTo(result.getResult());\n\t\tverify(mockAuthorizationManager).authorize(any(Supplier.class), any(MethodInvocationResult.class));\n\t}\n\n\t@Test\n\tpublic void afterWhenMockSecurityContextHolderStrategyThenUses() throws Throwable {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tSecurityContextHolderStrategy strategy = mockSecurityContextHolderStrategy(\n\t\t\t\tnew SecurityContextImpl(authentication));\n\t\tMethodInvocation invocation = mock(MethodInvocation.class);\n\t\tAuthorizationManager<MethodInvocationResult> authorizationManager = AuthenticatedAuthorizationManager\n\t\t\t.authenticated();\n\t\tAuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(\n\t\t\t\tPointcut.TRUE, authorizationManager);\n\t\tadvice.setSecurityContextHolderStrategy(strategy);\n\t\tadvice.invoke(invocation);\n\t\tverify(strategy).getContext();\n\t}\n\n\t// gh-12877\n\t@Test\n\tpublic void afterWhenStaticSecurityContextHolderStrategyAfterConstructorThenUses() throws Throwable {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"john\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"authority\"));\n\t\tSecurityContextHolderStrategy strategy = mockSecurityContextHolderStrategy(\n\t\t\t\tnew SecurityContextImpl(authentication));\n\t\tMethodInvocation invocation = mock(MethodInvocation.class);\n\t\tAuthorizationManager<MethodInvocationResult> authorizationManager = AuthenticatedAuthorizationManager\n\t\t\t.authenticated();\n\t\tAuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(\n\t\t\t\tPointcut.TRUE, authorizationManager);\n\t\tSecurityContextHolderStrategy saved = SecurityContextHolder.getContextHolderStrategy();\n\t\tSecurityContextHolder.setContextHolderStrategy(strategy);\n\t\tadvice.invoke(invocation);\n\t\tverify(strategy).getContext();\n\t\tSecurityContextHolder.setContextHolderStrategy(saved);\n\t}\n\n\t@Test\n\tpublic void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {\n\t\tAuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(\n\t\t\t\tPointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> advice.setAuthorizationEventPublisher(null))\n\t\t\t.withMessage(\"eventPublisher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable {\n\t\tAuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(\n\t\t\t\tPointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());\n\t\tAuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);\n\t\tadvice.setAuthorizationEventPublisher(eventPublisher);\n\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMethodInvocation mockMethodInvocation = mock(MethodInvocation.class);\n\t\tMethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(result.getResult());\n\n\t\tadvice.invoke(mockMethodInvocation);\n\t\tverify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocationResult.class),\n\t\t\t\tany(AuthorizationDecision.class));\n\t}\n\n\t@Test\n\tpublic void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() throws Throwable {\n\t\tMethodInvocation mi = mock(MethodInvocation.class);\n\t\tgiven(mi.proceed()).willReturn(\"ok\");\n\t\tAuthorizationManager<MethodInvocationResult> manager = mock(AuthorizationManager.class);\n\t\tgiven(manager.authorize(any(), any()))\n\t\t\t.willThrow(new MyAuthzDeniedException(\"denied\", new AuthorizationDecision(false)));\n\t\tAuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(\n\t\t\t\tPointcut.TRUE, manager);\n\t\tassertThatExceptionOfType(MyAuthzDeniedException.class).isThrownBy(() -> advice.invoke(mi));\n\t}\n\n\tprivate SecurityContextHolderStrategy mockSecurityContextHolderStrategy(SecurityContextImpl securityContextImpl) {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(securityContextImpl);\n\t\treturn strategy;\n\t}\n\n\tstatic class MyAuthzDeniedException extends AuthorizationDeniedException {\n\n\t\tMyAuthzDeniedException(String msg, AuthorizationResult authorizationResult) {\n\t\t\tsuper(msg, authorizationResult);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.invocation.InvocationOnMock;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link AuthorizationManagerAfterReactiveMethodInterceptor}.\n *\n * @author Evgeniy Cheban\n */\npublic class AuthorizationManagerAfterReactiveMethodInterceptorTests {\n\n\t@Test\n\tpublic void instantiateWhenPointcutNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerAfterReactiveMethodInterceptor(null,\n\t\t\t\t\tmock(ReactiveAuthorizationManager.class)))\n\t\t\t.withMessage(\"pointcut cannot be null\");\n\t}\n\n\t@Test\n\tpublic void instantiateWhenAuthorizationManagerNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerAfterReactiveMethodInterceptor(mock(Pointcut.class), null))\n\t\t\t.withMessage(\"authorizationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenMockReactiveAuthorizationManagerThenVerify() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tReactiveAuthorizationManager<MethodInvocationResult> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any()))\n\t\t\t.willReturn(Mono.just(new AuthorizationDecision(true)));\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t.extracting(Mono::block)\n\t\t\t.isEqualTo(\"john\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeFluxWhenMockReactiveAuthorizationManagerThenVerify() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"flux\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Flux.just(\"john\", \"bob\"));\n\t\tReactiveAuthorizationManager<MethodInvocationResult> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any()))\n\t\t\t.willReturn(Mono.just(new AuthorizationDecision(true)));\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Flux.class))\n\t\t\t.extracting(Flux::collectList)\n\t\t\t.extracting(Mono::block, InstanceOfAssertFactories.list(String.class))\n\t\t\t.containsExactly(\"john\", \"bob\");\n\t\tverify(mockReactiveAuthorizationManager, times(2)).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeWhenMockReactiveAuthorizationManagerDeniedThenAccessDeniedException() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tReactiveAuthorizationManager<MethodInvocationResult> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any()))\n\t\t\t.willReturn(Mono.just(new AuthorizationDecision(false)));\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> assertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t\t.extracting(Mono::block))\n\t\t\t.withMessage(\"Access Denied\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeFluxWhenAllValuesDeniedAndPostProcessorThenPostProcessorAppliedToEachValueEmitted()\n\t\t\tthrows Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"flux\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Flux.just(\"john\", \"bob\"));\n\t\tHandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(\n\t\t\t\tHandlingReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))\n\t\t\t.willAnswer(this::masking);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any())).willReturn(Mono.empty());\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Flux.class))\n\t\t\t.extracting(Flux::collectList)\n\t\t\t.extracting(Mono::block, InstanceOfAssertFactories.list(String.class))\n\t\t\t.containsExactly(\"john-masked\", \"bob-masked\");\n\t\tverify(mockReactiveAuthorizationManager, times(2)).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeFluxWhenOneValueDeniedAndPostProcessorThenPostProcessorAppliedToDeniedValue() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"flux\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Flux.just(\"john\", \"bob\"));\n\t\tHandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(\n\t\t\t\tHandlingReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))\n\t\t\t.willAnswer((invocation) -> {\n\t\t\t\tMethodInvocationResult argument = invocation.getArgument(0);\n\t\t\t\tif (!\"john\".equals(argument.getResult())) {\n\t\t\t\t\treturn monoMasking(invocation);\n\t\t\t\t}\n\t\t\t\treturn Mono.just(argument.getResult());\n\t\t\t});\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any())).willReturn(Mono.empty());\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Flux.class))\n\t\t\t.extracting(Flux::collectList)\n\t\t\t.extracting(Mono::block, InstanceOfAssertFactories.list(String.class))\n\t\t\t.containsExactly(\"john\", \"bob-masked\");\n\t\tverify(mockReactiveAuthorizationManager, times(2)).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenPostProcessableDecisionThenPostProcess() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tHandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(\n\t\t\t\tHandlingReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))\n\t\t\t.willAnswer(this::masking);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any())).willReturn(Mono.empty());\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t.extracting(Mono::block)\n\t\t\t.isEqualTo(\"john-masked\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenPostProcessableDecisionAndPostProcessResultIsMonoThenPostProcessWorks() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tHandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(\n\t\t\t\tHandlingReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))\n\t\t\t.willAnswer(this::monoMasking);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any())).willReturn(Mono.empty());\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t.extracting(Mono::block)\n\t\t\t.isEqualTo(\"john-masked\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenPostProcessableDecisionAndPostProcessResultIsNullThenPostProcessWorks() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tHandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(\n\t\t\t\tHandlingReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class)))\n\t\t\t.willReturn(null);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any())).willReturn(Mono.empty());\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t.extracting(Mono::block)\n\t\t\t.isEqualTo(null);\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenEmptyDecisionThenUseDefaultPostProcessor() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tReactiveAuthorizationManager<MethodInvocationResult> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), any())).willReturn(Mono.empty());\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThatExceptionOfType(AuthorizationDeniedException.class)\n\t\t\t.isThrownBy(() -> assertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t\t.extracting(Mono::block))\n\t\t\t.withMessage(\"Access Denied\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"ok\"));\n\t\tReactiveAuthorizationManager<MethodInvocationResult> manager = mock(ReactiveAuthorizationManager.class);\n\t\tgiven(manager.authorize(any(), any()))\n\t\t\t.willReturn(Mono.error(new MyAuthzDeniedException(\"denied\", new AuthorizationDecision(false))));\n\t\tAuthorizationManagerAfterReactiveMethodInterceptor advice = new AuthorizationManagerAfterReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, manager);\n\t\tassertThatExceptionOfType(MyAuthzDeniedException.class)\n\t\t\t.isThrownBy(() -> ((Mono<?>) advice.invoke(mockMethodInvocation)).block());\n\t}\n\n\tprivate Object masking(InvocationOnMock invocation) {\n\t\tMethodInvocationResult result = invocation.getArgument(0);\n\t\treturn result.getResult() + \"-masked\";\n\t}\n\n\tprivate Object monoMasking(InvocationOnMock invocation) {\n\t\tMethodInvocationResult result = invocation.getArgument(0);\n\t\treturn Mono.just(result.getResult() + \"-masked\");\n\t}\n\n\tinterface HandlingReactiveAuthorizationManager\n\t\t\textends ReactiveAuthorizationManager<MethodInvocationResult>, MethodAuthorizationDeniedHandler {\n\n\t}\n\n\tclass Sample {\n\n\t\tMono<String> mono() {\n\t\t\treturn Mono.just(\"john\");\n\t\t}\n\n\t\tFlux<String> flux() {\n\t\t\treturn Flux.just(\"john\", \"bob\");\n\t\t}\n\n\t}\n\n\tstatic class MyAuthzDeniedException extends AuthorizationDeniedException {\n\n\t\tMyAuthzDeniedException(String msg, AuthorizationResult authorizationResult) {\n\t\t\tsuper(msg, authorizationResult);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link AuthorizationManagerBeforeMethodInterceptor}.\n *\n * @author Evgeniy Cheban\n * @author Gengwu Zhao\n */\npublic class AuthorizationManagerBeforeMethodInterceptorTests {\n\n\t@Test\n\tpublic void instantiateWhenMethodMatcherNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerBeforeMethodInterceptor(null, mock(AuthorizationManager.class)))\n\t\t\t.withMessage(\"pointcut cannot be null\");\n\t}\n\n\t@Test\n\tpublic void instantiateWhenAuthorizationManagerNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerBeforeMethodInterceptor(mock(Pointcut.class), null))\n\t\t\t.withMessage(\"authorizationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void beforeWhenMockAuthorizationManagerThenCheck() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = mock(MethodInvocation.class);\n\t\tAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class);\n\t\tAuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockAuthorizationManager);\n\t\tadvice.invoke(mockMethodInvocation);\n\t\tverify(mockAuthorizationManager).authorize(any(Supplier.class), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void beforeWhenMockSecurityContextHolderStrategyThenUses() throws Throwable {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"authority\"));\n\t\tSecurityContextHolderStrategy strategy = mockSecurityContextHolderStrategy(\n\t\t\t\tnew SecurityContextImpl(authentication));\n\t\tMethodInvocation invocation = mock(MethodInvocation.class);\n\t\tAuthorizationManager<MethodInvocation> authorizationManager = AuthenticatedAuthorizationManager.authenticated();\n\t\tAuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tPointcut.TRUE, authorizationManager);\n\t\tadvice.setSecurityContextHolderStrategy(strategy);\n\t\tadvice.invoke(invocation);\n\t\tverify(strategy).getContext();\n\t}\n\n\t// gh-12877\n\t@Test\n\tpublic void beforeWhenStaticSecurityContextHolderStrategyAfterConstructorThenUses() throws Throwable {\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"john\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"authority\"));\n\t\tSecurityContextHolderStrategy strategy = mockSecurityContextHolderStrategy(\n\t\t\t\tnew SecurityContextImpl(authentication));\n\t\tMethodInvocation invocation = mock(MethodInvocation.class);\n\t\tAuthorizationManager<MethodInvocation> authorizationManager = AuthenticatedAuthorizationManager.authenticated();\n\t\tAuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tPointcut.TRUE, authorizationManager);\n\t\tSecurityContextHolderStrategy saved = SecurityContextHolder.getContextHolderStrategy();\n\t\tSecurityContextHolder.setContextHolderStrategy(strategy);\n\t\tadvice.invoke(invocation);\n\t\tverify(strategy).getContext();\n\t\tSecurityContextHolder.setContextHolderStrategy(saved);\n\t}\n\n\t@Test\n\tpublic void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {\n\t\tAuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tPointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> advice.setAuthorizationEventPublisher(null))\n\t\t\t.withMessage(\"eventPublisher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable {\n\t\tAuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tPointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());\n\t\tAuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);\n\t\tadvice.setAuthorizationEventPublisher(eventPublisher);\n\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMethodInvocation mockMethodInvocation = mock(MethodInvocation.class);\n\t\tMethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(result.getResult());\n\n\t\tadvice.invoke(mockMethodInvocation);\n\t\tverify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocation.class),\n\t\t\t\tany(AuthorizationDecision.class));\n\t}\n\n\t@Test\n\tpublic void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() {\n\t\tAuthorizationManager<MethodInvocation> manager = mock(AuthorizationManager.class);\n\t\tgiven(manager.authorize(any(), any()))\n\t\t\t.willThrow(new MyAuthzDeniedException(\"denied\", new AuthorizationDecision(false)));\n\t\tAuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(\n\t\t\t\tPointcut.TRUE, manager);\n\t\tassertThatExceptionOfType(MyAuthzDeniedException.class).isThrownBy(() -> advice.invoke(null));\n\t}\n\n\tprivate SecurityContextHolderStrategy mockSecurityContextHolderStrategy(SecurityContextImpl securityContextImpl) {\n\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(securityContextImpl);\n\t\treturn strategy;\n\t}\n\n\tstatic class MyAuthzDeniedException extends AuthorizationDeniedException {\n\n\t\tMyAuthzDeniedException(String msg, AuthorizationResult authorizationResult) {\n\t\t\tsuper(msg, authorizationResult);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link AuthorizationManagerBeforeReactiveMethodInterceptor}.\n *\n * @author Evgeniy Cheban\n */\npublic class AuthorizationManagerBeforeReactiveMethodInterceptorTests {\n\n\t@Test\n\tpublic void instantiateWhenPointcutNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerBeforeReactiveMethodInterceptor(null,\n\t\t\t\t\tmock(ReactiveAuthorizationManager.class)))\n\t\t\t.withMessage(\"pointcut cannot be null\");\n\n\t}\n\n\t@Test\n\tpublic void instantiateWhenAuthorizationManagerNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerBeforeReactiveMethodInterceptor(mock(Pointcut.class), null))\n\t\t\t.withMessage(\"authorizationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenMockReactiveAuthorizationManagerThenVerify() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tReactiveAuthorizationManager<MethodInvocation> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), eq(mockMethodInvocation)))\n\t\t\t.willReturn(Mono.just(new AuthorizationDecision(true)));\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t.extracting(Mono::block)\n\t\t\t.isEqualTo(\"john\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void invokeFluxWhenMockReactiveAuthorizationManagerThenVerify() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"flux\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Flux.just(\"john\", \"bob\"));\n\t\tReactiveAuthorizationManager<MethodInvocation> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), eq(mockMethodInvocation)))\n\t\t\t.willReturn(Mono.just(new AuthorizationDecision((true))));\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Flux.class))\n\t\t\t.extracting(Flux::collectList)\n\t\t\t.extracting(Mono::block, InstanceOfAssertFactories.list(String.class))\n\t\t\t.containsExactly(\"john\", \"bob\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void invokeWhenMockReactiveAuthorizationManagerDeniedThenAccessDeniedException() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tReactiveAuthorizationManager<MethodInvocation> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), eq(mockMethodInvocation)))\n\t\t\t.willReturn(Mono.just(new AuthorizationDecision(false)));\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> assertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t\t.extracting(Mono::block))\n\t\t\t.withMessage(\"Access Denied\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenDeniedAndPostProcessorThenInvokePostProcessor() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tHandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(\n\t\t\t\tHandlingReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());\n\t\tgiven(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))\n\t\t\t.willReturn(\"***\");\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t.extracting(Mono::block)\n\t\t\t.isEqualTo(\"***\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenDeniedAndMonoPostProcessorThenInvokePostProcessor() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tHandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(\n\t\t\t\tHandlingReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());\n\t\tgiven(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))\n\t\t\t.willReturn(Mono.just(\"***\"));\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t.extracting(Mono::block)\n\t\t\t.isEqualTo(\"***\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void invokeFluxWhenDeniedAndPostProcessorThenInvokePostProcessor() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"flux\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Flux.just(\"john\", \"bob\"));\n\t\tHandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock(\n\t\t\t\tHandlingReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());\n\t\tgiven(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class)))\n\t\t\t.willReturn(Mono.just(\"***\"));\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Flux.class))\n\t\t\t.extracting(Flux::collectList)\n\t\t\t.extracting(Mono::block, InstanceOfAssertFactories.list(String.class))\n\t\t\t.containsExactly(\"***\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void invokeMonoWhenEmptyDecisionThenInvokeDefaultPostProcessor() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"mono\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Mono.just(\"john\"));\n\t\tReactiveAuthorizationManager<MethodInvocation> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThatExceptionOfType(AuthorizationDeniedException.class)\n\t\t\t.isThrownBy(() -> assertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class))\n\t\t\t\t.extracting(Mono::block))\n\t\t\t.withMessage(\"Access Denied\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void invokeFluxWhenEmptyDecisionThenInvokeDefaultPostProcessor() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"flux\")));\n\t\tgiven(mockMethodInvocation.proceed()).willReturn(Flux.just(\"john\", \"bob\"));\n\t\tReactiveAuthorizationManager<MethodInvocation> mockReactiveAuthorizationManager = mock(\n\t\t\t\tReactiveAuthorizationManager.class);\n\t\tgiven(mockReactiveAuthorizationManager.authorize(any(), eq(mockMethodInvocation))).willReturn(Mono.empty());\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, mockReactiveAuthorizationManager);\n\t\tObject result = interceptor.invoke(mockMethodInvocation);\n\t\tassertThatExceptionOfType(AuthorizationDeniedException.class)\n\t\t\t.isThrownBy(() -> assertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Flux.class))\n\t\t\t\t.extracting(Flux::blockFirst))\n\t\t\t.withMessage(\"Access Denied\");\n\t\tverify(mockReactiveAuthorizationManager).authorize(any(), eq(mockMethodInvocation));\n\t}\n\n\t@Test\n\tpublic void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() throws Throwable {\n\t\tMethodInvocation mockMethodInvocation = spy(\n\t\t\t\tnew MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod(\"flux\")));\n\t\tReactiveAuthorizationManager<MethodInvocation> manager = mock(ReactiveAuthorizationManager.class);\n\t\tgiven(manager.authorize(any(), any()))\n\t\t\t.willThrow(new MyAuthzDeniedException(\"denied\", new AuthorizationDecision(false)));\n\t\tAuthorizationManagerBeforeReactiveMethodInterceptor advice = new AuthorizationManagerBeforeReactiveMethodInterceptor(\n\t\t\t\tPointcut.TRUE, manager);\n\t\tassertThatExceptionOfType(MyAuthzDeniedException.class)\n\t\t\t.isThrownBy(() -> ((Mono<?>) advice.invoke(mockMethodInvocation)).block());\n\t}\n\n\tinterface HandlingReactiveAuthorizationManager\n\t\t\textends ReactiveAuthorizationManager<MethodInvocation>, MethodAuthorizationDeniedHandler {\n\n\t}\n\n\tclass Sample {\n\n\t\tMono<String> mono() {\n\t\t\treturn Mono.just(\"john\");\n\t\t}\n\n\t\tFlux<String> flux() {\n\t\t\treturn Flux.just(\"john\", \"bob\");\n\t\t}\n\n\t}\n\n\tstatic class MyAuthzDeniedException extends AuthorizationDeniedException {\n\n\t\tMyAuthzDeniedException(String msg, AuthorizationResult authorizationResult) {\n\t\t\tsuper(msg, authorizationResult);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/AuthorizationMethodPointcutsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.Pointcut;\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link AuthorizationMethodPointcuts}\n */\npublic class AuthorizationMethodPointcutsTests {\n\n\t@Test\n\tpublic void forAnnotationsWhenAnnotationThenClassBasedAnnotationPointcut() {\n\t\tPointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);\n\t\tassertThat(AopUtils.canApply(preAuthorize, ClassController.class)).isTrue();\n\t\tassertThat(AopUtils.canApply(preAuthorize, NoController.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic void forAnnotationsWhenAnnotationThenMethodBasedAnnotationPointcut() {\n\t\tPointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);\n\t\tassertThat(AopUtils.canApply(preAuthorize, MethodController.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void forAnnotationsWhenAnnotationThenClassInheritancePointcut() {\n\t\tPointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);\n\t\tassertThat(AopUtils.canApply(preAuthorize, InterfacedClassController.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void forAnnotationsWhenAnnotationThenMethodInheritancePointcut() {\n\t\tPointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);\n\t\tassertThat(AopUtils.canApply(preAuthorize, InterfacedMethodController.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void forAnnotationsWhenAnnotationThenAnnotationClassInheritancePointcut() {\n\t\tPointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);\n\t\tassertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationClassController.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void forAnnotationsWhenAnnotationThenAnnotationMethodInheritancePointcut() {\n\t\tPointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);\n\t\tassertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationMethodController.class)).isTrue();\n\t}\n\n\t@PreAuthorize(\"hasAuthority('APP')\")\n\tpublic static class ClassController {\n\n\t\tString methodOne(String paramOne) {\n\t\t\treturn \"value\";\n\t\t}\n\n\t}\n\n\tpublic static class MethodController {\n\n\t\t@PreAuthorize(\"hasAuthority('APP')\")\n\t\tString methodOne(String paramOne) {\n\t\t\treturn \"value\";\n\t\t}\n\n\t}\n\n\tpublic static class NoController {\n\n\t\tString methodOne(String paramOne) {\n\t\t\treturn \"value\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"hasAuthority('APP')\")\n\tpublic interface ClassControllerInterface {\n\n\t\tString methodOne(String paramOne);\n\n\t}\n\n\tpublic static class InterfacedClassController implements ClassControllerInterface {\n\n\t\tpublic String methodOne(String paramOne) {\n\t\t\treturn \"value\";\n\t\t}\n\n\t}\n\n\tpublic interface MethodControllerInterface {\n\n\t\t@PreAuthorize(\"hasAuthority('APP')\")\n\t\tString methodOne(String paramOne);\n\n\t}\n\n\tpublic static class InterfacedMethodController implements MethodControllerInterface {\n\n\t\tpublic String methodOne(String paramOne) {\n\t\t\treturn \"value\";\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasAuthority('APP')\")\n\t@interface MyAnnotation {\n\n\t}\n\n\t@MyAnnotation\n\tpublic interface ClassAnnotationControllerInterface {\n\n\t\tString methodOne(String paramOne);\n\n\t}\n\n\tpublic static class InterfacedAnnotationClassController implements ClassAnnotationControllerInterface {\n\n\t\tpublic String methodOne(String paramOne) {\n\t\t\treturn \"value\";\n\t\t}\n\n\t}\n\n\tpublic interface MethodAnnotationControllerInterface {\n\n\t\t@MyAnnotation\n\t\tString methodOne(String paramOne);\n\n\t}\n\n\tpublic static class InterfacedAnnotationMethodController implements MethodAnnotationControllerInterface {\n\n\t\tpublic String methodOne(String paramOne) {\n\t\t\treturn \"value\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/BusinessService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n */\n@Secured({ \"ROLE_USER\" })\n@PermitAll\npublic interface BusinessService extends Serializable {\n\n\t@Secured({ \"ROLE_ADMIN\" })\n\t@RolesAllowed({ \"ROLE_ADMIN\" })\n\t@PreAuthorize(\"hasRole('ROLE_ADMIN')\")\n\tvoid someAdminMethod();\n\n\t@Secured({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\t@RolesAllowed({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\tvoid someUserAndAdminMethod();\n\n\t@Secured({ \"ROLE_USER\" })\n\t@RolesAllowed({ \"ROLE_USER\" })\n\tvoid someUserMethod1();\n\n\t@Secured({ \"ROLE_USER\" })\n\t@RolesAllowed({ \"ROLE_USER\" })\n\tvoid someUserMethod2();\n\n\t@RolesAllowed({ \"USER\" })\n\tvoid rolesAllowedUser();\n\n\tint someOther(String s);\n\n\tint someOther(int input);\n\n\tList<?> methodReturningAList(List<?> someList);\n\n\tObject[] methodReturningAnArray(Object[] someArray);\n\n\tList<?> methodReturningAList(String userName, String extraParam);\n\n\t@RequireAdminRole\n\t@RequireUserRole\n\tdefault void repeatedAnnotations() {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/ExpressionUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.ExpressionAuthorizationDecision;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\npublic class ExpressionUtilsTests {\n\n\tprivate final Object details = new Object();\n\n\t@Test\n\tpublic void evaluateWhenAuthorizationDecisionThenReturns() {\n\t\tSpelExpressionParser parser = new SpelExpressionParser();\n\t\tExpression expression = parser.parseExpression(\"#root.returnDecision()\");\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(this);\n\t\tassertThat(ExpressionUtils.evaluate(expression, context)).isInstanceOf(AuthorizationDecisionDetails.class)\n\t\t\t.extracting(\"details\")\n\t\t\t.isEqualTo(this.details);\n\t}\n\n\t@Test\n\tpublic void evaluateWhenBooleanThenReturnsExpressionAuthorizationDecision() {\n\t\tSpelExpressionParser parser = new SpelExpressionParser();\n\t\tExpression expression = parser.parseExpression(\"#root.returnResult()\");\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(this);\n\t\tassertThat(ExpressionUtils.evaluate(expression, context)).isInstanceOf(ExpressionAuthorizationDecision.class);\n\t}\n\n\t@Test\n\tpublic void evaluateWhenExpressionThrowsAuthorizationDeniedExceptionThenPropagates() {\n\t\tSpelExpressionParser parser = new SpelExpressionParser();\n\t\tExpression expression = parser.parseExpression(\"#root.throwException()\");\n\t\tStandardEvaluationContext context = new StandardEvaluationContext(this);\n\t\tassertThatExceptionOfType(AuthorizationDeniedException.class)\n\t\t\t.isThrownBy(() -> ExpressionUtils.evaluate(expression, context));\n\t}\n\n\tpublic AuthorizationDecision returnDecision() {\n\t\treturn new AuthorizationDecisionDetails(false, this.details);\n\t}\n\n\tpublic Object throwException() {\n\t\tthrow new AuthorizationDeniedException(\"denied\", new AuthorizationDecision(false));\n\t}\n\n\tpublic boolean returnResult() {\n\t\treturn false;\n\t}\n\n\tstatic final class AuthorizationDecisionDetails extends AuthorizationDecision {\n\n\t\tfinal Object details;\n\n\t\tAuthorizationDecisionDetails(boolean granted, Object details) {\n\t\t\tsuper(granted);\n\t\t\tthis.details = details;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.Collection;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport jakarta.annotation.security.DenyAll;\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link Jsr250AuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class Jsr250AuthorizationManagerTests {\n\n\t@Test\n\tpublic void rolePrefixWhenNotSetThenDefaultsToRole() {\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tassertThat(manager).extracting(\"rolePrefix\").isEqualTo(\"ROLE_\");\n\t}\n\n\t@Test\n\tpublic void setRolePrefixWhenNullThenException() {\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setRolePrefix(null))\n\t\t\t.withMessage(\"rolePrefix cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setRolePrefixWhenNotNullThenSets() {\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tmanager.setRolePrefix(\"CUSTOM_\");\n\t\tassertThat(manager).extracting(\"rolePrefix\").isEqualTo(\"CUSTOM_\");\n\t}\n\n\t@Test\n\tpublic void setAuthoritiesAuthorizationManagerWhenNullThenException() {\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setAuthoritiesAuthorizationManager(null))\n\t\t\t.withMessage(\"authoritiesAuthorizationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthoritiesAuthorizationManagerWhenNotNullThenVerifyUsage() throws Exception {\n\t\tAuthorizationManager<Collection<String>> authoritiesAuthorizationManager = mock(AuthorizationManager.class);\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tmanager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"rolesAllowedAdmin\");\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_ADMIN\");\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision).isNull();\n\t\tverify(authoritiesAuthorizationManager).authorize(authentication, Set.of(\"ROLE_ADMIN\"));\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingWhenNoJsr250AnnotationsThenNullDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomething\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tpublic void checkPermitAllWhenRoleUserThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, \"permitAll\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkDenyAllWhenRoleAdminThenDeniedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, \"denyAll\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkRolesAllowedUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"rolesAllowedUserOrAdmin\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkRolesAllowedUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"rolesAllowedUserOrAdmin\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkRolesAllowedUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_ANONYMOUS\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"rolesAllowedUserOrAdmin\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkMultipleMethodAnnotationsWhenInvokedThenAnnotationConfigurationException() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_ANONYMOUS\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"multipleAnnotations\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, methodInvocation));\n\t}\n\n\t@Test\n\tpublic void checkMultipleClassAnnotationsWhenInvokedThenAnnotationConfigurationException() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelIllegalAnnotations(),\n\t\t\t\tClassLevelIllegalAnnotations.class, \"inheritedAnnotations\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, methodInvocation));\n\t}\n\n\t@Test\n\tpublic void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"rolesAllowedAdmin\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t\tauthentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tdecision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkDeniedWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"denyAll\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"rolesAllowedUser\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isTrue();\n\t\tauthentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tdecision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"inheritedAnnotations\");\n\t\tJsr250AuthorizationManager manager = new Jsr250AuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, methodInvocation));\n\t}\n\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t\t@DenyAll\n\t\tpublic void denyAll() {\n\n\t\t}\n\n\t\t@PermitAll\n\t\tpublic void permitAll() {\n\n\t\t}\n\n\t\t@RolesAllowed({ \"USER\", \"ADMIN\" })\n\t\tpublic void rolesAllowedUserOrAdmin() {\n\n\t\t}\n\n\t\t@RolesAllowed(\"USER\")\n\t\t@DenyAll\n\t\tpublic void multipleAnnotations() {\n\n\t\t}\n\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\t@RolesAllowed(\"USER\")\n\tpublic static class ClassLevelAnnotations implements InterfaceAnnotationsThree {\n\n\t\t@RolesAllowed(\"ADMIN\")\n\t\tpublic void rolesAllowedAdmin() {\n\n\t\t}\n\n\t\t@DenyAll\n\t\tpublic void denyAll() {\n\n\t\t}\n\n\t\tpublic void rolesAllowedUser() {\n\n\t\t}\n\n\t\t@Override\n\t\t@PermitAll\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\t@MyIllegalRolesAllowed\n\tpublic static class ClassLevelIllegalAnnotations {\n\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@RolesAllowed(\"ADMIN\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@MyRolesAllowed\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@DenyAll\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@RolesAllowed(\"USER\")\n\tpublic @interface MyRolesAllowed {\n\n\t}\n\n\t@DenyAll\n\t@RolesAllowed(\"USER\")\n\t@Retention(RetentionPolicy.RUNTIME)\n\tpublic @interface MyIllegalRolesAllowed {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/MethodExpressionAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.util.SimpleMethodInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nclass MethodExpressionAuthorizationManagerTests {\n\n\t@Test\n\tvoid instantiateWhenExpressionStringNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MethodExpressionAuthorizationManager(null))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionStringEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MethodExpressionAuthorizationManager(\"\"))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionStringBlankThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MethodExpressionAuthorizationManager(\" \"))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionHandlerNotSetThenDefaultUsed() {\n\t\tMethodExpressionAuthorizationManager manager = new MethodExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tassertThat(manager).extracting(\"expressionHandler\").isInstanceOf(DefaultMethodSecurityExpressionHandler.class);\n\t}\n\n\t@Test\n\tvoid setExpressionHandlerWhenNullThenIllegalArgumentException() {\n\t\tMethodExpressionAuthorizationManager manager = new MethodExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tvoid setExpressionHandlerWhenNotNullThenVerifyExpressionHandler() {\n\t\tString expressionString = \"hasRole('ADMIN')\";\n\t\tMethodExpressionAuthorizationManager manager = new MethodExpressionAuthorizationManager(expressionString);\n\t\tDefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tExpressionParser mockExpressionParser = mock(ExpressionParser.class);\n\t\tExpression mockExpression = mock(Expression.class);\n\t\tgiven(mockExpressionParser.parseExpression(expressionString)).willReturn(mockExpression);\n\t\texpressionHandler.setExpressionParser(mockExpressionParser);\n\t\tmanager.setExpressionHandler(expressionHandler);\n\t\tassertThat(manager).extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t\tassertThat(manager).extracting(\"expression\").isEqualTo(mockExpression);\n\t\tverify(mockExpressionParser).parseExpression(expressionString);\n\t}\n\n\t@Test\n\tvoid checkWhenExpressionHasRoleAdminConfiguredAndRoleAdminThenGrantedDecision() {\n\t\tMethodExpressionAuthorizationManager manager = new MethodExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin,\n\t\t\t\tnew SimpleMethodInvocation(new Object(),\n\t\t\t\t\t\tReflectionUtils.getRequiredMethod(BusinessService.class, \"someAdminMethod\")));\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkWhenExpressionHasRoleAdminConfiguredAndRoleUserThenDeniedDecision() {\n\t\tMethodExpressionAuthorizationManager manager = new MethodExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser,\n\t\t\t\tnew SimpleMethodInvocation(new Object(),\n\t\t\t\t\t\tReflectionUtils.getRequiredMethod(BusinessService.class, \"someAdminMethod\")));\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/MockMethodInvocation.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Method;\n\nimport org.aopalliance.intercept.MethodInvocation;\n\n@SuppressWarnings(\"unchecked\")\npublic class MockMethodInvocation implements MethodInvocation {\n\n\tprivate Method method;\n\n\tprivate Object targetObject;\n\n\tprivate Object[] arguments = new Object[0];\n\n\tpublic MockMethodInvocation(Object targetObject, Class clazz, String methodName, Class[] parameterTypes,\n\t\t\tObject[] arguments) throws NoSuchMethodException {\n\t\tthis(targetObject, clazz, methodName, parameterTypes);\n\t\tthis.arguments = arguments;\n\t}\n\n\tpublic MockMethodInvocation(Object targetObject, Class clazz, String methodName, Class... parameterTypes)\n\t\t\tthrows NoSuchMethodException {\n\t\tthis(targetObject, clazz.getMethod(methodName, parameterTypes));\n\t\tthis.targetObject = targetObject;\n\t}\n\n\tpublic MockMethodInvocation(Object targetObject, Method method) {\n\t\tthis.targetObject = targetObject;\n\t\tthis.method = method;\n\t}\n\n\t@Override\n\tpublic Object[] getArguments() {\n\t\treturn this.arguments;\n\t}\n\n\t@Override\n\tpublic Method getMethod() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic AccessibleObject getStaticPart() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Object getThis() {\n\t\treturn this.targetObject;\n\t}\n\n\t@Override\n\tpublic Object proceed() {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/NullReturningMethodAuthorizationDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authorization.AuthorizationDeniedException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link NullReturningMethodAuthorizationDeniedHandler}.\n *\n * @author Heejong Yoon\n */\nclass NullReturningMethodAuthorizationDeniedHandlerTests {\n\n\t@Test\n\tvoid handleNullReturningMethod() {\n\t\tassertThat(new NullReturningMethodAuthorizationDeniedHandler().handleDeniedInvocation(null, null)).isNull();\n\t}\n\n\t@Test\n\tvoid handleNullReturningMethodWithException() {\n\t\tassertThatExceptionOfType(AuthorizationDeniedException.class)\n\t\t\t.isThrownBy(() -> new NullReturningMethodAuthorizationDeniedHandler().handleDeniedInvocation(null,\n\t\t\t\t\tnew AuthorizationDeniedException(\"test\")));\n\t}\n\n\t@Test\n\tvoid handleNullReturningMethodWithInvocationResult() {\n\t\tassertThat(new NullReturningMethodAuthorizationDeniedHandler().handleDeniedInvocationResult(null, null))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tvoid handleNullReturningMethodWithInvocationResultWithException() {\n\t\tassertThatExceptionOfType(AuthorizationDeniedException.class)\n\t\t\t.isThrownBy(() -> new NullReturningMethodAuthorizationDeniedHandler().handleDeniedInvocationResult(null,\n\t\t\t\t\tnew AuthorizationDeniedException(\"test\")));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PostAuthorizeAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class PostAuthorizeAuthorizationManagerTests {\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {\n\t\tMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tmanager.setExpressionHandler(expressionHandler);\n\t\tassertThat(manager).extracting(\"registry\").extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNullThenException() {\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomething\", new Class[] {}, new Object[] {});\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, result);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"grant\" });\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, result);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"deny\" });\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, result);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingListWhenReturnObjectContainsGrantThenGrantedDecision() throws Exception {\n\t\tList<String> list = Arrays.asList(\"grant\", \"deny\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingList\", new Class[] { List.class }, new Object[] { list });\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, list);\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, result);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingListWhenReturnObjectNotContainsGrantThenDeniedDecision() throws Exception {\n\t\tList<String> list = Collections.singletonList(\"deny\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingList\", new Class[] { List.class }, new Object[] { list });\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, list);\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, result);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedAdmin\");\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, result);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t\tauthentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tdecision = manager.authorize(authentication, result);\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedUser\");\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, result);\n\t\tassertThat(decision.isGranted()).isTrue();\n\t\tauthentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tdecision = manager.authorize(authentication, result);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),\n\t\t\t\tConflictingAnnotations.class, \"inheritedAnnotations\");\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, result));\n\t}\n\n\t@Test\n\tpublic void checkWhenHandlerDeniedNoApplicationContextThenReflectivelyConstructs() throws Exception {\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tassertThat(handleDeniedInvocationResult(\"methodOne\", manager)).isNull();\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> handleDeniedInvocationResult(\"methodTwo\", manager));\n\t}\n\n\t@Test\n\tpublic void checkWhenHandlerDeniedApplicationContextThenLooksForBean() throws Exception {\n\t\tGenericApplicationContext context = new GenericApplicationContext();\n\t\tcontext.registerBean(NoDefaultConstructorHandler.class, () -> new NoDefaultConstructorHandler(new Object()));\n\t\tcontext.refresh();\n\t\tPostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();\n\t\tmanager.setApplicationContext(context);\n\t\tassertThat(handleDeniedInvocationResult(\"methodTwo\", manager)).isNull();\n\t\tassertThatExceptionOfType(IllegalStateException.class)\n\t\t\t.isThrownBy(() -> handleDeniedInvocationResult(\"methodOne\", manager));\n\t}\n\n\tprivate Object handleDeniedInvocationResult(String methodName, PostAuthorizeAuthorizationManager manager)\n\t\t\tthrows Exception {\n\t\tMethodInvocation invocation = new MockMethodInvocation(new UsingHandleDeniedAuthorization(),\n\t\t\t\tUsingHandleDeniedAuthorization.class, methodName);\n\t\tMethodInvocationResult result = new MethodInvocationResult(invocation, null);\n\t\treturn manager.handleDeniedInvocationResult(result, null);\n\t}\n\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t\t@PostAuthorize(\"#s == 'grant'\")\n\t\tpublic String doSomethingString(String s) {\n\t\t\treturn s;\n\t\t}\n\n\t\t@PostAuthorize(\"returnObject.contains('grant')\")\n\t\tpublic List<String> doSomethingList(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\t@PostAuthorize(\"hasRole('USER')\")\n\tpublic static class ClassLevelAnnotations implements InterfaceAnnotationsThree {\n\n\t\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t\tpublic void securedAdmin() {\n\n\t\t}\n\n\t\tpublic void securedUser() {\n\n\t\t}\n\n\t\t@Override\n\t\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic static class ConflictingAnnotations implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@PostAuthorize(\"hasRole('USER')\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MyPostAuthorize\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PostAuthorize(\"hasRole('USER')\")\n\tpublic @interface MyPostAuthorize {\n\n\t}\n\n\tpublic static final class UsingHandleDeniedAuthorization {\n\n\t\t@HandleAuthorizationDenied(handlerClass = NullHandler.class)\n\t\t@PostAuthorize(\"denyAll()\")\n\t\tpublic String methodOne() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t\t@HandleAuthorizationDenied(handlerClass = NoDefaultConstructorHandler.class)\n\t\t@PostAuthorize(\"denyAll()\")\n\t\tpublic String methodTwo() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tpublic static final class NullHandler implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tpublic static final class NoDefaultConstructorHandler implements MethodAuthorizationDeniedHandler {\n\n\t\tprivate NoDefaultConstructorHandler(Object parameter) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/PostAuthorizeReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostAuthorize;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PostAuthorizeReactiveAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class PostAuthorizeReactiveAuthorizationManagerTests {\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {\n\t\tMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager(\n\t\t\t\texpressionHandler);\n\t\tassertThat(manager).extracting(\"registry\").extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PostAuthorizeReactiveAuthorizationManager(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomething\", new Class[] {}, new Object[] {});\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tAuthorizationResult decision = manager.authorize(ReactiveAuthenticationUtils.getAuthentication(), result)\n\t\t\t.block();\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"grant\" });\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tAuthorizationResult decision = manager.authorize(ReactiveAuthenticationUtils.getAuthentication(), result)\n\t\t\t.block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"deny\" });\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(ReactiveAuthenticationUtils.getAuthentication(), result)\n\t\t\t.block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingListWhenReturnObjectContainsGrantThenGrantedDecision() throws Exception {\n\t\tList<String> list = Arrays.asList(\"grant\", \"deny\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingList\", new Class[] { List.class }, new Object[] { list });\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, list);\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(ReactiveAuthenticationUtils.getAuthentication(), result)\n\t\t\t.block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingListWhenReturnObjectNotContainsGrantThenDeniedDecision() throws Exception {\n\t\tList<String> list = Collections.singletonList(\"deny\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingList\", new Class[] { List.class }, new Object[] { list });\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, list);\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(ReactiveAuthenticationUtils.getAuthentication(), result)\n\t\t\t.block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {\n\t\tMono<Authentication> authentication = Mono\n\t\t\t.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedAdmin\");\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, result).block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t\tauthentication = Mono.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\"));\n\t\tdecision = manager.authorize(authentication, result).block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {\n\t\tMono<Authentication> authentication = Mono\n\t\t\t.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedUser\");\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, result).block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t\tauthentication = Mono.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\"));\n\t\tdecision = manager.authorize(authentication, result).block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tMono<Authentication> authentication = Mono\n\t\t\t.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),\n\t\t\t\tConflictingAnnotations.class, \"inheritedAnnotations\");\n\t\tMethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);\n\t\tPostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, result));\n\t}\n\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t\t@PostAuthorize(\"#s == 'grant'\")\n\t\tpublic String doSomethingString(String s) {\n\t\t\treturn s;\n\t\t}\n\n\t\t@PostAuthorize(\"returnObject.contains('grant')\")\n\t\tpublic List<String> doSomethingList(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\t@PostAuthorize(\"hasRole('USER')\")\n\tpublic static class ClassLevelAnnotations implements InterfaceAnnotationsThree {\n\n\t\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t\tpublic void securedAdmin() {\n\n\t\t}\n\n\t\tpublic void securedUser() {\n\n\t\t}\n\n\t\t@Override\n\t\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic static class ConflictingAnnotations implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@PostAuthorize(\"hasRole('ADMIN')\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@PostAuthorize(\"hasRole('USER')\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MyPostAuthorize\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PostAuthorize(\"hasRole('USER')\")\n\tpublic @interface MyPostAuthorize {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.MethodMatcher;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostFilter;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link PostFilterAuthorizationMethodInterceptor}.\n *\n * @author Evgeniy Cheban\n * @author Gengwu Zhao\n */\npublic class PostFilterAuthorizationMethodInterceptorTests {\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tSecurityContextHolder.getContext().setAuthentication(TestAuthentication.authenticatedUser());\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {\n\t\tMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tPostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();\n\t\tadvice.setExpressionHandler(expressionHandler);\n\t\tassertThat(advice).extracting(\"registry\").extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNullThenException() {\n\t\tPostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception {\n\t\tPostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();\n\t\tMethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();\n\t\tassertThat(methodMatcher.matches(NoPostFilterClass.class.getMethod(\"doSomething\"), NoPostFilterClass.class))\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception {\n\t\tPostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();\n\t\tMethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();\n\t\tassertThat(\n\t\t\t\tmethodMatcher.matches(TestClass.class.getMethod(\"doSomethingArray\", String[].class), TestClass.class))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void afterWhenArrayNotNullThenFilteredArray() throws Throwable {\n\t\tString[] array = { \"john\", \"bob\" };\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingArrayClassLevel\", new Class[] { String[].class }, new Object[] { array }) {\n\t\t\t@Override\n\t\t\tpublic Object proceed() {\n\t\t\t\treturn array;\n\t\t\t}\n\t\t};\n\t\tPostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();\n\t\tObject result = advice.invoke(methodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly(\"john\");\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),\n\t\t\t\tConflictingAnnotations.class, \"inheritedAnnotations\");\n\t\tPostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> advice.invoke(methodInvocation));\n\t}\n\n\t@Test\n\tpublic void postFilterWhenMockSecurityContextHolderStrategyThenUses() throws Throwable {\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"john\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"authority\"));\n\t\tSecurityContextHolderStrategy strategy = mockSecurityContextHolderStrategy(\n\t\t\t\tnew SecurityContextImpl(authentication));\n\t\tString[] array = { \"john\", \"bob\" };\n\t\tMockMethodInvocation invocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingArrayAuthentication\", new Class[] { String[].class }, new Object[] { array }) {\n\t\t\t@Override\n\t\t\tpublic Object proceed() {\n\t\t\t\treturn array;\n\t\t\t}\n\t\t};\n\t\tPostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();\n\t\tadvice.setSecurityContextHolderStrategy(strategy);\n\t\tadvice.invoke(invocation);\n\t\tverify(strategy).getContext();\n\t}\n\n\t// gh-12877\n\t@Test\n\tpublic void postFilterWhenStaticSecurityContextHolderStrategyAfterConstructorThenUses() throws Throwable {\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"john\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"authority\"));\n\t\tSecurityContextHolderStrategy strategy = mockSecurityContextHolderStrategy(\n\t\t\t\tnew SecurityContextImpl(authentication));\n\t\tString[] array = { \"john\", \"bob\" };\n\t\tMockMethodInvocation invocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingArrayAuthentication\", new Class[] { String[].class }, new Object[] { array }) {\n\t\t\t@Override\n\t\t\tpublic Object proceed() {\n\t\t\t\treturn array;\n\t\t\t}\n\t\t};\n\t\tPostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();\n\t\tSecurityContextHolderStrategy saved = SecurityContextHolder.getContextHolderStrategy();\n\t\tSecurityContextHolder.setContextHolderStrategy(strategy);\n\t\tadvice.invoke(invocation);\n\t\tverify(strategy).getContext();\n\t\tSecurityContextHolder.setContextHolderStrategy(saved);\n\t}\n\n\tprivate SecurityContextHolderStrategy mockSecurityContextHolderStrategy(SecurityContextImpl securityContextImpl) {\n\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(securityContextImpl);\n\t\treturn strategy;\n\t}\n\n\t@PostFilter(\"filterObject == 'john'\")\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@PostFilter(\"filterObject == 'john'\")\n\t\tpublic String[] doSomethingArray(String[] array) {\n\t\t\treturn array;\n\t\t}\n\n\t\tpublic String[] doSomethingArrayClassLevel(String[] array) {\n\t\t\treturn array;\n\t\t}\n\n\t\t@PostFilter(\"filterObject == authentication.name\")\n\t\tpublic String[] doSomethingArrayAuthentication(String[] array) {\n\t\t\treturn array;\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic static class NoPostFilterClass {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t}\n\n\tpublic static class ConflictingAnnotations implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@PostFilter(\"filterObject == 'jim'\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@PostFilter(\"filterObject == 'jane'\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MyPostFilter\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PostFilter(\"filterObject == 'john'\")\n\tpublic @interface MyPostFilter {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/PostFilterAuthorizationReactiveMethodInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PostFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PostFilterAuthorizationReactiveMethodInterceptor}.\n *\n * @author Evgeniy Cheban\n */\npublic class PostFilterAuthorizationReactiveMethodInterceptorTests {\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {\n\t\tMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tPostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor(\n\t\t\t\texpressionHandler);\n\t\tassertThat(interceptor).extracting(\"registry\").extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new PostFilterAuthorizationReactiveMethodInterceptor(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception {\n\t\tPostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThat(interceptor.getPointcut()\n\t\t\t.getMethodMatcher()\n\t\t\t.matches(NoPostFilterClass.class.getMethod(\"doSomething\"), NoPostFilterClass.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception {\n\t\tPostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThat(interceptor.getPointcut()\n\t\t\t.getMethodMatcher()\n\t\t\t.matches(TestClass.class.getMethod(\"doSomethingFlux\", Flux.class), TestClass.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void invokeWhenMonoThenFilteredMono() throws Throwable {\n\t\tMono<String> mono = Mono.just(\"bob\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingMono\", new Class[] { Mono.class }, new Object[] { mono }) {\n\t\t\t@Override\n\t\t\tpublic Object proceed() {\n\t\t\t\treturn mono;\n\t\t\t}\n\t\t};\n\t\tPostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor();\n\t\tObject result = interceptor.invoke(methodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class)).extracting(Mono::block).isNull();\n\t}\n\n\t@Test\n\tpublic void invokeWhenFluxThenFilteredFlux() throws Throwable {\n\t\tFlux<String> flux = Flux.just(\"john\", \"bob\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingFluxClassLevel\", new Class[] { Flux.class }, new Object[] { flux }) {\n\t\t\t@Override\n\t\t\tpublic Object proceed() {\n\t\t\t\treturn flux;\n\t\t\t}\n\t\t};\n\t\tPostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor();\n\t\tObject result = interceptor.invoke(methodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Flux.class))\n\t\t\t.extracting(Flux::collectList)\n\t\t\t.extracting(Mono::block, InstanceOfAssertFactories.list(String.class))\n\t\t\t.containsOnly(\"john\");\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),\n\t\t\t\tConflictingAnnotations.class, \"inheritedAnnotations\");\n\t\tPostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> interceptor.invoke(methodInvocation));\n\t}\n\n\t@PostFilter(\"filterObject == 'john'\")\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@PostFilter(\"filterObject == 'john'\")\n\t\tpublic Flux<String> doSomethingFlux(Flux<String> flux) {\n\t\t\treturn flux;\n\t\t}\n\n\t\tpublic Flux<String> doSomethingFluxClassLevel(Flux<String> flux) {\n\t\t\treturn flux;\n\t\t}\n\n\t\t@PostFilter(\"filterObject == 'john'\")\n\t\tpublic Mono<String> doSomethingMono(Mono<String> mono) {\n\t\t\treturn mono;\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic static class NoPostFilterClass {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t}\n\n\tpublic static class ConflictingAnnotations implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@PostFilter(\"filterObject == 'jim'\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@PostFilter(\"filterObject == 'jane'\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MyPostFilter\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PostFilter(\"filterObject == 'john'\")\n\tpublic @interface MyPostFilter {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.function.Supplier;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.TargetClassAware;\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PreAuthorizeAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class PreAuthorizeAuthorizationManagerTests {\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {\n\t\tMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tmanager.setExpressionHandler(expressionHandler);\n\t\tassertThat(manager).extracting(\"registry\").extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNullThenException() {\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomething\", new Class[] {}, new Object[] {});\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"grant\" });\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"deny\" });\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedAdmin\");\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t\tauthentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tdecision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedUser\");\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isTrue();\n\t\tauthentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tdecision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),\n\t\t\t\tConflictingAnnotations.class, \"inheritedAnnotations\");\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, methodInvocation));\n\t}\n\n\t@Test\n\tpublic void checkTargetClassAwareWhenInterfaceLevelAnnotationsThenApplies() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestTargetClassAware(),\n\t\t\t\tTestTargetClassAware.class, \"doSomething\");\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t\tdecision = manager.authorize(TestAuthentication::authenticatedAdmin, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenHandlerDeniedNoApplicationContextThenReflectivelyConstructs() throws Exception {\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tassertThat(handleDeniedInvocationResult(\"methodOne\", manager)).isNull();\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> handleDeniedInvocationResult(\"methodTwo\", manager));\n\t}\n\n\t@Test\n\tpublic void checkWhenHandlerDeniedApplicationContextThenLooksForBean() throws Exception {\n\t\tGenericApplicationContext context = new GenericApplicationContext();\n\t\tcontext.registerBean(NoDefaultConstructorHandler.class, () -> new NoDefaultConstructorHandler(new Object()));\n\t\tcontext.refresh();\n\t\tPreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();\n\t\tmanager.setApplicationContext(context);\n\t\tassertThat(handleDeniedInvocationResult(\"methodTwo\", manager)).isNull();\n\t\tassertThatExceptionOfType(IllegalStateException.class)\n\t\t\t.isThrownBy(() -> handleDeniedInvocationResult(\"methodOne\", manager));\n\t}\n\n\tprivate Object handleDeniedInvocationResult(String methodName, PreAuthorizeAuthorizationManager manager)\n\t\t\tthrows Exception {\n\t\tMethodInvocation invocation = new MockMethodInvocation(new UsingHandleDeniedAuthorization(),\n\t\t\t\tUsingHandleDeniedAuthorization.class, methodName);\n\t\treturn manager.handleDeniedInvocation(invocation, null);\n\t}\n\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t\t@PreAuthorize(\"#s == 'grant'\")\n\t\tpublic String doSomethingString(String s) {\n\t\t\treturn s;\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"hasRole('USER')\")\n\tpublic static class ClassLevelAnnotations implements InterfaceAnnotationsThree {\n\n\t\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t\tpublic void securedAdmin() {\n\n\t\t}\n\n\t\tpublic void securedUser() {\n\n\t\t}\n\n\t\t@Override\n\t\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic static class ConflictingAnnotations implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@PreAuthorize(\"hasRole('USER')\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MyPreAuthorize\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasRole('USER')\")\n\tpublic @interface MyPreAuthorize {\n\n\t}\n\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tpublic interface InterfaceLevelAnnotations {\n\n\t}\n\n\tpublic static class TestTargetClassAware extends TestClass implements TargetClassAware, InterfaceLevelAnnotations {\n\n\t\t@Override\n\t\tpublic Class<?> getTargetClass() {\n\t\t\treturn TestClass.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic void doSomething() {\n\t\t\tsuper.doSomething();\n\t\t}\n\n\t\t@Override\n\t\tpublic String doSomethingString(String s) {\n\t\t\treturn super.doSomethingString(s);\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t\tsuper.inheritedAnnotations();\n\t\t}\n\n\t}\n\n\tpublic static final class UsingHandleDeniedAuthorization {\n\n\t\t@HandleAuthorizationDenied(handlerClass = NullHandler.class)\n\t\t@PreAuthorize(\"denyAll()\")\n\t\tpublic String methodOne() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t\t@HandleAuthorizationDenied(handlerClass = NoDefaultConstructorHandler.class)\n\t\t@PreAuthorize(\"denyAll()\")\n\t\tpublic String methodTwo() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tpublic static final class NullHandler implements MethodAuthorizationDeniedHandler {\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tpublic static final class NoDefaultConstructorHandler implements MethodAuthorizationDeniedHandler {\n\n\t\tprivate NoDefaultConstructorHandler(Object parameter) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic Object handleDeniedInvocation(MethodInvocation methodInvocation,\n\t\t\t\tAuthorizationResult authorizationResult) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/PreAuthorizeReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PreAuthorizeReactiveAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class PreAuthorizeReactiveAuthorizationManagerTests {\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {\n\t\tMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tPreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager(\n\t\t\t\texpressionHandler);\n\t\tassertThat(manager).extracting(\"registry\").extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PreAuthorizeReactiveAuthorizationManager(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomething\", new Class[] {}, new Object[] {});\n\t\tPreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager\n\t\t\t.authorize(ReactiveAuthenticationUtils.getAuthentication(), methodInvocation)\n\t\t\t.block();\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"grant\" });\n\t\tPreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager\n\t\t\t.authorize(ReactiveAuthenticationUtils.getAuthentication(), methodInvocation)\n\t\t\t.block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingString\", new Class[] { String.class }, new Object[] { \"deny\" });\n\t\tPreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager\n\t\t\t.authorize(ReactiveAuthenticationUtils.getAuthentication(), methodInvocation)\n\t\t\t.block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {\n\t\tMono<Authentication> authentication = Mono\n\t\t\t.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedAdmin\");\n\t\tPreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation).block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t\tauthentication = Mono.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\"));\n\t\tdecision = manager.authorize(authentication, methodInvocation).block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {\n\t\tMono<Authentication> authentication = Mono\n\t\t\t.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedUser\");\n\t\tPreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation).block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t\tauthentication = Mono.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\"));\n\t\tdecision = manager.authorize(authentication, methodInvocation).block();\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tMono<Authentication> authentication = Mono\n\t\t\t.just(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),\n\t\t\t\tConflictingAnnotations.class, \"inheritedAnnotations\");\n\t\tPreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, methodInvocation));\n\t}\n\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t\t@PreAuthorize(\"#s == 'grant'\")\n\t\tpublic String doSomethingString(String s) {\n\t\t\treturn s;\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"hasRole('USER')\")\n\tpublic static class ClassLevelAnnotations implements InterfaceAnnotationsThree {\n\n\t\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t\tpublic void securedAdmin() {\n\n\t\t}\n\n\t\tpublic void securedUser() {\n\n\t\t}\n\n\t\t@Override\n\t\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic static class ConflictingAnnotations implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@PreAuthorize(\"hasRole('ADMIN')\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@PreAuthorize(\"hasRole('USER')\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MyPreAuthorize\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreAuthorize(\"hasRole('USER')\")\n\tpublic @interface MyPreAuthorize {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.MethodMatcher;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link PreFilterAuthorizationMethodInterceptor}.\n *\n * @author Evgeniy Cheban\n * @author Gengwu Zhao\n */\npublic class PreFilterAuthorizationMethodInterceptorTests {\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tSecurityContextHolder.getContext().setAuthentication(TestAuthentication.authenticatedUser());\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {\n\t\tMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tadvice.setExpressionHandler(expressionHandler);\n\t\tassertThat(advice).extracting(\"registry\").extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNullThenException() {\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception {\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tMethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();\n\t\tassertThat(methodMatcher.matches(NoPreFilterClass.class.getMethod(\"doSomething\"), NoPreFilterClass.class))\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception {\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tMethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();\n\t\tassertThat(methodMatcher.matches(TestClass.class.getMethod(\"doSomethingListFilterTargetMatch\", List.class),\n\t\t\t\tTestClass.class))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingListFilterTargetNotMatch\", new Class[] { List.class }, new Object[] { new ArrayList<>() });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> advice.invoke(methodInvocation))\n\t\t\t.withMessage(\"Filter target was null, or no argument with name 'filterTargetNotMatch' found in method.\");\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameProvidedAndMatchAndNullThenException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingListFilterTargetMatch\", new Class[] { List.class }, new Object[] { null });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> advice.invoke(methodInvocation))\n\t\t\t.withMessage(\"Filter target was null, or no argument with name 'list' found in method.\");\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Throwable {\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(\"john\");\n\t\tlist.add(\"bob\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingListFilterTargetMatch\", new Class[] { List.class }, new Object[] { list });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tadvice.invoke(methodInvocation);\n\t\tassertThat(list).hasSize(1);\n\t\tassertThat(list.get(0)).isEqualTo(\"john\");\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingListFilterTargetNotProvided\", new Class[] { List.class }, new Object[] { null });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> advice.invoke(methodInvocation))\n\t\t\t.withMessage(\"Filter target was null. Make sure you passing the correct value in the method argument.\");\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Throwable {\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(\"john\");\n\t\tlist.add(\"bob\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingListFilterTargetNotProvided\", new Class[] { List.class }, new Object[] { list });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tadvice.invoke(methodInvocation);\n\t\tassertThat(list).hasSize(1);\n\t\tassertThat(list.get(0)).isEqualTo(\"john\");\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameNotProvidedAndSingleArgArrayThenException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingArrayFilterTargetNotProvided\", new Class[] { String[].class },\n\t\t\t\tnew Object[] { new String[] {} });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tassertThatIllegalStateException().isThrownBy(() -> advice.invoke(methodInvocation))\n\t\t\t.withMessage(\"Pre-filtering on array types is not supported. Using a Collection will solve this problem.\");\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameNotProvidedAndNotSingleArgThenException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingTwoArgsFilterTargetNotProvided\", new Class[] { String.class, List.class },\n\t\t\t\tnew Object[] { \"\", new ArrayList<>() });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tassertThatIllegalStateException().isThrownBy(() -> advice.invoke(methodInvocation))\n\t\t\t.withMessage(\"Unable to determine the method argument for filtering. Specify the filter target.\");\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),\n\t\t\t\tConflictingAnnotations.class, \"inheritedAnnotations\");\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> advice.invoke(methodInvocation));\n\t}\n\n\t@Test\n\tpublic void preFilterWhenMockSecurityContextHolderStrategyThenUses() throws Throwable {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"john\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"authority\"));\n\t\tSecurityContextHolderStrategy strategy = mockSecurityContextHolderStrategy(\n\t\t\t\tnew SecurityContextImpl(authentication));\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(\"john\");\n\t\tlist.add(\"bob\");\n\t\tMockMethodInvocation invocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingArrayFilterAuthentication\", new Class[] { List.class }, new Object[] { list });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tadvice.setSecurityContextHolderStrategy(strategy);\n\t\tadvice.invoke(invocation);\n\t\tverify(strategy).getContext();\n\t}\n\n\t// gh-12877\n\t@Test\n\tpublic void preFilterWhenStaticSecurityContextHolderStrategyAfterConstructorThenUses() throws Throwable {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"john\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"authority\"));\n\t\tSecurityContextHolderStrategy strategy = mockSecurityContextHolderStrategy(\n\t\t\t\tnew SecurityContextImpl(authentication));\n\t\tList<String> list = new ArrayList<>();\n\t\tlist.add(\"john\");\n\t\tlist.add(\"bob\");\n\t\tMockMethodInvocation invocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingArrayFilterAuthentication\", new Class[] { List.class }, new Object[] { list });\n\t\tPreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();\n\t\tSecurityContextHolderStrategy saved = SecurityContextHolder.getContextHolderStrategy();\n\t\tSecurityContextHolder.setContextHolderStrategy(strategy);\n\t\tadvice.invoke(invocation);\n\t\tverify(strategy).getContext();\n\t\tSecurityContextHolder.setContextHolderStrategy(saved);\n\t}\n\n\tprivate SecurityContextHolderStrategy mockSecurityContextHolderStrategy(SecurityContextImpl securityContextImpl) {\n\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(securityContextImpl);\n\t\treturn strategy;\n\t}\n\n\t@PreFilter(\"filterObject == 'john'\")\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@PreFilter(value = \"filterObject == 'john'\", filterTarget = \"filterTargetNotMatch\")\n\t\tpublic List<String> doSomethingListFilterTargetNotMatch(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@PreFilter(value = \"filterObject == 'john'\", filterTarget = \"list\")\n\t\tpublic List<String> doSomethingListFilterTargetMatch(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@PreFilter(\"filterObject == 'john'\")\n\t\tpublic List<String> doSomethingListFilterTargetNotProvided(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@PreFilter(\"filterObject == 'john'\")\n\t\tpublic String[] doSomethingArrayFilterTargetNotProvided(String[] array) {\n\t\t\treturn array;\n\t\t}\n\n\t\tpublic List<String> doSomethingTwoArgsFilterTargetNotProvided(String s, List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@PreFilter(value = \"filterObject == authentication.name\", filterTarget = \"list\")\n\t\tpublic List<String> doSomethingArrayFilterAuthentication(List<String> list) {\n\t\t\treturn list;\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic static class NoPreFilterClass {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t}\n\n\tpublic static class ConflictingAnnotations implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@PreFilter(\"filterObject == 'jim'\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@PreFilter(\"filterObject == 'jane'\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MyPreFilter\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreFilter(\"filterObject == 'john'\")\n\tpublic @interface MyPreFilter {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/PreFilterAuthorizationReactiveMethodInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;\nimport org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;\nimport org.springframework.security.access.prepost.PreFilter;\nimport org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PreFilterAuthorizationReactiveMethodInterceptor}.\n *\n * @author Evgeniy Cheban\n */\npublic class PreFilterAuthorizationReactiveMethodInterceptorTests {\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {\n\t\tMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor(\n\t\t\t\texpressionHandler);\n\t\tassertThat(interceptor).extracting(\"registry\").extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t}\n\n\t@Test\n\tpublic void setExpressionHandlerWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PreFilterAuthorizationReactiveMethodInterceptor(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setParameterNameDiscovererWhenNotNullThenSetsParameterNameDiscoverer() {\n\t\tParameterNameDiscoverer parameterNameDiscoverer = new DefaultSecurityParameterNameDiscoverer();\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tinterceptor.setParameterNameDiscoverer(parameterNameDiscoverer);\n\t\tassertThat(interceptor).extracting(\"parameterNameDiscoverer\").isEqualTo(parameterNameDiscoverer);\n\t}\n\n\t@Test\n\tpublic void setParameterNameDiscovererWhenNullThenException() {\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> interceptor.setParameterNameDiscoverer(null))\n\t\t\t.withMessage(\"parameterNameDiscoverer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception {\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThat(interceptor.getPointcut()\n\t\t\t.getMethodMatcher()\n\t\t\t.matches(NoPreFilterClass.class.getMethod(\"doSomething\"), NoPreFilterClass.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception {\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThat(interceptor.getPointcut()\n\t\t\t.getMethodMatcher()\n\t\t\t.matches(TestClass.class.getMethod(\"doSomethingFluxFilterTargetMatch\", Flux.class), TestClass.class))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingFluxFilterTargetNotMatch\", new Class[] { Flux.class }, new Object[] { Flux.empty() });\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> interceptor.invoke(methodInvocation))\n\t\t\t.withMessage(\"Filter target was null, or no argument with name 'filterTargetNotMatch' found in method.\");\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameProvidedAndMatchAndNullThenException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingFluxFilterTargetMatch\", new Class[] { Flux.class }, new Object[] { null });\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> interceptor.invoke(methodInvocation))\n\t\t\t.withMessage(\"Filter target was null, or no argument with name 'flux' found in method.\");\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameNotProvidedAndSingleArgMonoThenFiltersMono() throws Throwable {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingMonoFilterTargetNotProvided\", new Class[] { Mono.class },\n\t\t\t\tnew Object[] { Mono.just(\"bob\") }) {\n\t\t\t@Override\n\t\t\tpublic Object proceed() {\n\t\t\t\treturn getArguments()[0];\n\t\t\t}\n\t\t};\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tObject result = interceptor.invoke(methodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Mono.class)).extracting(Mono::block).isNull();\n\t}\n\n\t@Test\n\tpublic void findFilterTargetWhenNameNotProvidedAndSingleArgFluxThenFiltersFlux() throws Throwable {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomethingFluxFilterTargetNotProvided\", new Class[] { Flux.class },\n\t\t\t\tnew Object[] { Flux.just(\"john\", \"bob\") }) {\n\t\t\t@Override\n\t\t\tpublic Object proceed() {\n\t\t\t\treturn getArguments()[0];\n\t\t\t}\n\t\t};\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tObject result = interceptor.invoke(methodInvocation);\n\t\tassertThat(result).asInstanceOf(InstanceOfAssertFactories.type(Flux.class))\n\t\t\t.extracting(Flux::collectList)\n\t\t\t.extracting(Mono::block, InstanceOfAssertFactories.list(String.class))\n\t\t\t.containsOnly(\"john\");\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),\n\t\t\t\tConflictingAnnotations.class, \"inheritedAnnotations\");\n\t\tPreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> interceptor.invoke(methodInvocation));\n\t}\n\n\t@PreFilter(\"filterObject == 'john'\")\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@PreFilter(value = \"filterObject == 'john'\", filterTarget = \"filterTargetNotMatch\")\n\t\tpublic Flux<String> doSomethingFluxFilterTargetNotMatch(Flux<String> flux) {\n\t\t\treturn flux;\n\t\t}\n\n\t\t@PreFilter(value = \"filterObject == 'john'\", filterTarget = \"flux\")\n\t\tpublic Flux<String> doSomethingFluxFilterTargetMatch(Flux<String> flux) {\n\t\t\treturn flux;\n\t\t}\n\n\t\t@PreFilter(\"filterObject == 'john'\")\n\t\tpublic Flux<String> doSomethingFluxFilterTargetNotProvided(Flux<String> flux) {\n\t\t\treturn flux;\n\t\t}\n\n\t\t@PreFilter(\"filterObject == 'john'\")\n\t\tpublic Mono<String> doSomethingMonoFilterTargetNotProvided(Mono<String> mono) {\n\t\t\treturn mono;\n\t\t}\n\n\t\tpublic Flux<String> doSomethingTwoArgsFilterTargetNotProvided(String s, Flux<String> flux) {\n\t\t\treturn flux;\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic static class NoPreFilterClass {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t}\n\n\tpublic static class ConflictingAnnotations implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@PreFilter(\"filterObject == 'jim'\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@PreFilter(\"filterObject == 'jane'\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MyPreFilter\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@PreFilter(\"filterObject == 'john'\")\n\tpublic @interface MyPreFilter {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/RequireAdminRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('ADMIN')\")\n@RolesAllowed(\"ADMIN\")\n@Secured(\"ADMIN\")\npublic @interface RequireAdminRole {\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/RequireUserRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('USER')\")\n@RolesAllowed(\"USER\")\n@Secured(\"USER\")\npublic @interface RequireUserRole {\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/authorization/method/SecuredAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.authorization.method;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.Collection;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.TargetClassAware;\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link SecuredAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\npublic class SecuredAuthorizationManagerTests {\n\n\t@Test\n\tpublic void setAuthoritiesAuthorizationManagerWhenNullThenException() {\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setAuthoritiesAuthorizationManager(null))\n\t\t\t.withMessage(\"authoritiesAuthorizationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthoritiesAuthorizationManagerWhenNotNullThenVerifyUsage() throws Exception {\n\t\tAuthorizationManager<Collection<String>> authoritiesAuthorizationManager = mock(AuthorizationManager.class);\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tmanager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"securedUserOrAdmin\");\n\t\tSupplier<Authentication> authentication = TestAuthentication::authenticatedUser;\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision).isNull();\n\t\tverify(authoritiesAuthorizationManager).authorize(authentication, Set.of(\"ROLE_USER\", \"ROLE_ADMIN\"));\n\t}\n\n\t@Test\n\tpublic void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"doSomething\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNull();\n\t}\n\n\t@Test\n\tpublic void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"securedUserOrAdmin\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"securedUserOrAdmin\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_ANONYMOUS\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"securedUserOrAdmin\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedAdmin\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t\tauthentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tdecision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),\n\t\t\t\tClassLevelAnnotations.class, \"securedUser\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isTrue();\n\t\tauthentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tdecision = manager.authorize(authentication, methodInvocation);\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenDuplicatedThenAnnotationConfigurationException() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"inheritedAnnotations\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, methodInvocation));\n\t}\n\n\t@Test\n\tpublic void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,\n\t\t\t\t\"inheritedAnnotations\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> manager.authorize(authentication, methodInvocation));\n\t}\n\n\t@Test\n\tpublic void checkTargetClassAwareWhenInterfaceLevelAnnotationsThenApplies() throws Exception {\n\t\tMockMethodInvocation methodInvocation = new MockMethodInvocation(new TestTargetClassAware(),\n\t\t\t\tTestTargetClassAware.class, \"doSomething\");\n\t\tSecuredAuthorizationManager manager = new SecuredAuthorizationManager();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t\tdecision = manager.authorize(TestAuthentication::authenticatedAdmin, methodInvocation);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\tpublic static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {\n\n\t\tpublic void doSomething() {\n\n\t\t}\n\n\t\t@Secured({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\t\tpublic void securedUserOrAdmin() {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\t@Secured(\"ROLE_USER\")\n\tpublic static class ClassLevelAnnotations implements InterfaceAnnotationsThree {\n\n\t\t@Secured(\"ROLE_ADMIN\")\n\t\tpublic void securedAdmin() {\n\n\t\t}\n\n\t\tpublic void securedUser() {\n\n\t\t}\n\n\t\t@Override\n\t\t@Secured(\"ROLE_ADMIN\")\n\t\tpublic void inheritedAnnotations() {\n\n\t\t}\n\n\t}\n\n\tpublic interface InterfaceAnnotationsOne {\n\n\t\t@Secured(\"ROLE_ADMIN\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsTwo {\n\n\t\t@Secured(\"ROLE_USER\")\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\tpublic interface InterfaceAnnotationsThree {\n\n\t\t@MySecured\n\t\tvoid inheritedAnnotations();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Secured(\"ROLE_USER\")\n\tpublic @interface MySecured {\n\n\t}\n\n\t@Secured(\"ROLE_ADMIN\")\n\tpublic interface InterfaceLevelAnnotations {\n\n\t}\n\n\tpublic static class TestTargetClassAware extends TestClass implements TargetClassAware, InterfaceLevelAnnotations {\n\n\t\t@Override\n\t\tpublic Class<?> getTargetClass() {\n\t\t\treturn TestClass.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic void doSomething() {\n\t\t\tsuper.doSomething();\n\t\t}\n\n\t\t@Override\n\t\tpublic void securedUserOrAdmin() {\n\t\t\tsuper.securedUserOrAdmin();\n\t\t}\n\n\t\t@Override\n\t\tpublic void inheritedAnnotations() {\n\t\t\tsuper.inheritedAnnotations();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Abstract class for testing {@link DelegatingSecurityContextExecutorService} which\n * allows customization of how {@link DelegatingSecurityContextExecutorService} and its\n * mocks are created.\n *\n * @author Rob Winch\n * @since 3.2\n * @see CurrentDelegatingSecurityContextExecutorServiceTests\n * @see ExplicitDelegatingSecurityContextExecutorServiceTests\n */\npublic abstract class AbstractDelegatingSecurityContextExecutorServiceTests\n\t\textends AbstractDelegatingSecurityContextExecutorTests {\n\n\t@Mock\n\tprivate Future<Object> expectedFutureObject;\n\n\t@Mock\n\tprivate Object resultArg;\n\n\tprotected DelegatingSecurityContextExecutorService executor;\n\n\t@BeforeEach\n\tpublic final void setUpExecutorService() {\n\t\tthis.executor = create();\n\t}\n\n\t@Override\n\t@Test\n\tpublic void constructorNullDelegate() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingSecurityContextExecutorService(null));\n\t}\n\n\t@Test\n\tpublic void shutdown() {\n\t\tthis.executor.shutdown();\n\t\tverify(this.delegate).shutdown();\n\t}\n\n\t@Test\n\tpublic void shutdownNow() {\n\t\tList<Runnable> result = this.executor.shutdownNow();\n\t\tverify(this.delegate).shutdownNow();\n\t\tassertThat(result).isEqualTo(this.delegate.shutdownNow()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void isShutdown() {\n\t\tboolean result = this.executor.isShutdown();\n\t\tverify(this.delegate).isShutdown();\n\t\tassertThat(result).isEqualTo(this.delegate.isShutdown()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void isTerminated() {\n\t\tboolean result = this.executor.isTerminated();\n\t\tverify(this.delegate).isTerminated();\n\t\tassertThat(result).isEqualTo(this.delegate.isTerminated()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void awaitTermination() throws InterruptedException {\n\t\tboolean result = this.executor.awaitTermination(1, TimeUnit.SECONDS);\n\t\tverify(this.delegate).awaitTermination(1, TimeUnit.SECONDS);\n\t\tassertThat(result).isEqualTo(this.delegate.awaitTermination(1, TimeUnit.SECONDS)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void submitCallable() {\n\t\tgiven(this.delegate.submit(this.wrappedCallable)).willReturn(this.expectedFutureObject);\n\t\tFuture<Object> result = this.executor.submit(this.callable);\n\t\tverify(this.delegate).submit(this.wrappedCallable);\n\t\tassertThat(result).isEqualTo(this.expectedFutureObject);\n\t}\n\n\t@Test\n\tpublic void submitRunnableWithResult() {\n\t\tgiven(this.delegate.submit(this.wrappedRunnable, this.resultArg)).willReturn(this.expectedFutureObject);\n\t\tFuture<Object> result = this.executor.submit(this.runnable, this.resultArg);\n\t\tverify(this.delegate).submit(this.wrappedRunnable, this.resultArg);\n\t\tassertThat(result).isEqualTo(this.expectedFutureObject);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void submitRunnable() {\n\t\tgiven((Future<Object>) this.delegate.submit(this.wrappedRunnable)).willReturn(this.expectedFutureObject);\n\t\tFuture<?> result = this.executor.submit(this.runnable);\n\t\tverify(this.delegate).submit(this.wrappedRunnable);\n\t\tassertThat(result).isEqualTo(this.expectedFutureObject);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void invokeAll() throws Exception {\n\t\tList<Future<Object>> exectedResult = Arrays.asList(this.expectedFutureObject);\n\t\tList<Callable<Object>> wrappedCallables = Arrays.asList(this.wrappedCallable);\n\t\tgiven(this.delegate.invokeAll(wrappedCallables)).willReturn(exectedResult);\n\t\tList<Future<Object>> result = this.executor.invokeAll(Arrays.asList(this.callable));\n\t\tverify(this.delegate).invokeAll(wrappedCallables);\n\t\tassertThat(result).isEqualTo(exectedResult);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void invokeAllTimeout() throws Exception {\n\t\tList<Future<Object>> exectedResult = Arrays.asList(this.expectedFutureObject);\n\t\tList<Callable<Object>> wrappedCallables = Arrays.asList(this.wrappedCallable);\n\t\tgiven(this.delegate.invokeAll(wrappedCallables, 1, TimeUnit.SECONDS)).willReturn(exectedResult);\n\t\tList<Future<Object>> result = this.executor.invokeAll(Arrays.asList(this.callable), 1, TimeUnit.SECONDS);\n\t\tverify(this.delegate).invokeAll(wrappedCallables, 1, TimeUnit.SECONDS);\n\t\tassertThat(result).isEqualTo(exectedResult);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void invokeAny() throws Exception {\n\t\tList<Future<Object>> exectedResult = Arrays.asList(this.expectedFutureObject);\n\t\tList<Callable<Object>> wrappedCallables = Arrays.asList(this.wrappedCallable);\n\t\tgiven(this.delegate.invokeAny(wrappedCallables)).willReturn(exectedResult);\n\t\tObject result = this.executor.invokeAny(Arrays.asList(this.callable));\n\t\tverify(this.delegate).invokeAny(wrappedCallables);\n\t\tassertThat(result).isEqualTo(exectedResult);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void invokeAnyTimeout() throws Exception {\n\t\tList<Future<Object>> exectedResult = Arrays.asList(this.expectedFutureObject);\n\t\tList<Callable<Object>> wrappedCallables = Arrays.asList(this.wrappedCallable);\n\t\tgiven(this.delegate.invokeAny(wrappedCallables, 1, TimeUnit.SECONDS)).willReturn(exectedResult);\n\t\tObject result = this.executor.invokeAny(Arrays.asList(this.callable), 1, TimeUnit.SECONDS);\n\t\tverify(this.delegate).invokeAny(wrappedCallables, 1, TimeUnit.SECONDS);\n\t\tassertThat(result).isEqualTo(exectedResult);\n\t}\n\n\t@Override\n\tprotected abstract DelegatingSecurityContextExecutorService create();\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.ScheduledExecutorService;\n\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Abstract class for testing {@link DelegatingSecurityContextExecutor} which allows\n * customization of how {@link DelegatingSecurityContextExecutor} and its mocks are\n * created.\n *\n * @author Rob Winch\n * @since 3.2\n * @see CurrentDelegatingSecurityContextExecutorTests\n * @see ExplicitDelegatingSecurityContextExecutorTests\n */\npublic abstract class AbstractDelegatingSecurityContextExecutorTests\n\t\textends AbstractDelegatingSecurityContextTestSupport {\n\n\t@Mock\n\tprotected ScheduledExecutorService delegate;\n\n\tprivate DelegatingSecurityContextExecutor executor;\n\n\t@Test\n\tpublic void constructorNullDelegate() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingSecurityContextExecutor(null));\n\t}\n\n\t@Test\n\tpublic void execute() {\n\t\tthis.executor = create();\n\t\tthis.executor.execute(this.runnable);\n\t\tverify(getExecutor()).execute(this.wrappedRunnable);\n\t}\n\n\tprotected Executor getExecutor() {\n\t\treturn this.delegate;\n\t}\n\n\tprotected abstract DelegatingSecurityContextExecutor create();\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextScheduledExecutorServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\n\nimport static org.assertj.core.api.Assertions.assertThatObject;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Abstract class for testing {@link DelegatingSecurityContextScheduledExecutorService}\n * which allows customization of how\n * {@link DelegatingSecurityContextScheduledExecutorService} and its mocks are created.\n *\n * @author Rob Winch\n * @since 3.2\n * @see CurrentDelegatingSecurityContextScheduledExecutorServiceTests\n * @see ExplicitDelegatingSecurityContextScheduledExecutorServiceTests\n */\npublic abstract class AbstractDelegatingSecurityContextScheduledExecutorServiceTests\n\t\textends AbstractDelegatingSecurityContextExecutorServiceTests {\n\n\t@Mock\n\tprivate ScheduledFuture<Object> expectedResult;\n\n\tprivate DelegatingSecurityContextScheduledExecutorService executor;\n\n\t@BeforeEach\n\tpublic final void setUpExecutor() {\n\t\tthis.executor = create();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void scheduleRunnable() {\n\t\tgiven((ScheduledFuture<Object>) this.delegate.schedule(this.wrappedRunnable, 1, TimeUnit.SECONDS))\n\t\t\t.willReturn(this.expectedResult);\n\t\tScheduledFuture<?> result = this.executor.schedule(this.runnable, 1, TimeUnit.SECONDS);\n\t\tassertThatObject(result).isEqualTo(this.expectedResult);\n\t\tverify(this.delegate).schedule(this.wrappedRunnable, 1, TimeUnit.SECONDS);\n\t}\n\n\t@Test\n\tpublic void scheduleCallable() {\n\t\tgiven(this.delegate.schedule(this.wrappedCallable, 1, TimeUnit.SECONDS)).willReturn(this.expectedResult);\n\t\tScheduledFuture<Object> result = this.executor.schedule(this.callable, 1, TimeUnit.SECONDS);\n\t\tassertThatObject(result).isEqualTo(this.expectedResult);\n\t\tverify(this.delegate).schedule(this.wrappedCallable, 1, TimeUnit.SECONDS);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void scheduleAtFixedRate() {\n\t\tgiven((ScheduledFuture<Object>) this.delegate.scheduleAtFixedRate(this.wrappedRunnable, 1, 2, TimeUnit.SECONDS))\n\t\t\t.willReturn(this.expectedResult);\n\t\tScheduledFuture<?> result = this.executor.scheduleAtFixedRate(this.runnable, 1, 2, TimeUnit.SECONDS);\n\t\tassertThatObject(result).isEqualTo(this.expectedResult);\n\t\tverify(this.delegate).scheduleAtFixedRate(this.wrappedRunnable, 1, 2, TimeUnit.SECONDS);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void scheduleWithFixedDelay() {\n\t\tgiven((ScheduledFuture<Object>) this.delegate.scheduleWithFixedDelay(this.wrappedRunnable, 1, 2,\n\t\t\t\tTimeUnit.SECONDS))\n\t\t\t.willReturn(this.expectedResult);\n\t\tScheduledFuture<?> result = this.executor.scheduleWithFixedDelay(this.runnable, 1, 2, TimeUnit.SECONDS);\n\t\tassertThatObject(result).isEqualTo(this.expectedResult);\n\t\tverify(this.delegate).scheduleWithFixedDelay(this.wrappedRunnable, 1, 2, TimeUnit.SECONDS);\n\t}\n\n\t@Override\n\tprotected abstract DelegatingSecurityContextScheduledExecutorService create();\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextTestSupport.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Callable;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isNull;\n\n/**\n * Abstract base class for testing classes that extend\n * {@link AbstractDelegatingSecurityContextSupport}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic abstract class AbstractDelegatingSecurityContextTestSupport {\n\n\t@Mock\n\tprotected SecurityContext securityContext;\n\n\t@Mock\n\tprotected SecurityContext currentSecurityContext;\n\n\t@Captor\n\tprotected ArgumentCaptor<SecurityContext> securityContextCaptor;\n\n\t@Mock\n\tprotected Callable<Object> callable;\n\n\t@Mock\n\tprotected Callable<Object> wrappedCallable;\n\n\t@Mock\n\tprotected Runnable runnable;\n\n\t@Mock\n\tprotected Runnable wrappedRunnable;\n\n\t@Mock\n\tprotected MockedStatic<DelegatingSecurityContextCallable> delegatingSecurityContextCallable;\n\n\t@Mock\n\tprotected MockedStatic<DelegatingSecurityContextRunnable> delegatingSecurityContextRunnable;\n\n\tpublic final void explicitSecurityContextSetup() throws Exception {\n\t\tthis.delegatingSecurityContextCallable\n\t\t\t.when(() -> DelegatingSecurityContextCallable.create(eq(this.callable),\n\t\t\t\t\tthis.securityContextCaptor.capture(), any()))\n\t\t\t.thenReturn(this.wrappedCallable);\n\t\tthis.delegatingSecurityContextRunnable\n\t\t\t.when(() -> DelegatingSecurityContextRunnable.create(eq(this.runnable),\n\t\t\t\t\tthis.securityContextCaptor.capture(), any()))\n\t\t\t.thenReturn(this.wrappedRunnable);\n\t}\n\n\tpublic final void currentSecurityContextSetup() throws Exception {\n\t\tthis.delegatingSecurityContextCallable\n\t\t\t.when(() -> DelegatingSecurityContextCallable.create(eq(this.callable), isNull(), any()))\n\t\t\t.thenReturn(this.wrappedCallable);\n\t\tthis.delegatingSecurityContextRunnable\n\t\t\t.when(() -> DelegatingSecurityContextRunnable.create(eq(this.runnable), isNull(), any()))\n\t\t\t.thenReturn(this.wrappedRunnable);\n\t}\n\n\t@BeforeEach\n\tpublic final void setContext() {\n\t\tSecurityContextHolder.setContext(this.currentSecurityContext);\n\t}\n\n\t@AfterEach\n\tpublic final void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests using the current {@link SecurityContext} on\n * {@link DelegatingSecurityContextExecutorService}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class CurrentDelegatingSecurityContextExecutorServiceTests\n\t\textends AbstractDelegatingSecurityContextExecutorServiceTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tsuper.currentSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextExecutorService create() {\n\t\treturn new DelegatingSecurityContextExecutorService(this.delegate);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests using the current {@link SecurityContext} on\n * {@link DelegatingSecurityContextExecutor}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class CurrentDelegatingSecurityContextExecutorTests extends AbstractDelegatingSecurityContextExecutorTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tsuper.currentSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextExecutor create() {\n\t\treturn new DelegatingSecurityContextExecutor(getExecutor());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextScheduledExecutorServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests using the current {@link SecurityContext} on\n * {@link DelegatingSecurityContextScheduledExecutorService}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class CurrentDelegatingSecurityContextScheduledExecutorServiceTests\n\t\textends AbstractDelegatingSecurityContextScheduledExecutorServiceTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tthis.currentSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextScheduledExecutorService create() {\n\t\treturn new DelegatingSecurityContextScheduledExecutorService(this.delegate);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.internal.stubbing.answers.Returns;\nimport org.mockito.invocation.InvocationOnMock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.core.context.MockSecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 3.2\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingSecurityContextCallableTests {\n\n\t@Mock\n\tprivate Callable<Object> delegate;\n\n\t@Mock\n\tprivate SecurityContext securityContext;\n\n\t@Mock\n\tprivate Object callableResult;\n\n\tprivate Callable<Object> callable;\n\n\tprivate ExecutorService executor;\n\n\tprivate SecurityContext originalSecurityContext;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"serial\")\n\tpublic void setUp() throws Exception {\n\t\tthis.originalSecurityContext = SecurityContextHolder.createEmptyContext();\n\t\tthis.executor = Executors.newFixedThreadPool(1);\n\t}\n\n\tprivate void givenDelegateCallWillAnswerWithCurrentSecurityContext() throws Exception {\n\t\tgivenDelegateCallWillAnswerWithCurrentSecurityContext(SecurityContextHolder.getContextHolderStrategy());\n\t}\n\n\tprivate void givenDelegateCallWillAnswerWithCurrentSecurityContext(SecurityContextHolderStrategy strategy)\n\t\t\tthrows Exception {\n\t\tgiven(this.delegate.call()).willAnswer(new Returns(this.callableResult) {\n\t\t\t@Override\n\t\t\tpublic Object answer(InvocationOnMock invocation) throws Throwable {\n\t\t\t\tassertThat(strategy.getContext())\n\t\t\t\t\t.isEqualTo(DelegatingSecurityContextCallableTests.this.securityContext);\n\t\t\t\treturn super.answer(invocation);\n\t\t\t}\n\t\t});\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorNullDelegate() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingSecurityContextCallable<>(null));\n\t}\n\n\t@Test\n\tpublic void constructorNullDelegateNonNullSecurityContext() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingSecurityContextCallable<>(null, this.securityContext));\n\t}\n\n\t@Test\n\tpublic void constructorNullDelegateAndSecurityContext() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingSecurityContextCallable<>(null, null));\n\t}\n\n\t@Test\n\tpublic void constructorNullSecurityContext() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingSecurityContextCallable<>(this.delegate, null));\n\t}\n\n\t@Test\n\tpublic void call() throws Exception {\n\t\tgivenDelegateCallWillAnswerWithCurrentSecurityContext();\n\t\tthis.callable = new DelegatingSecurityContextCallable<>(this.delegate, this.securityContext);\n\t\tassertWrapped(this.callable);\n\t}\n\n\t@Test\n\tpublic void callDefaultSecurityContext() throws Exception {\n\t\tgivenDelegateCallWillAnswerWithCurrentSecurityContext();\n\t\tSecurityContextHolder.setContext(this.securityContext);\n\t\tthis.callable = new DelegatingSecurityContextCallable<>(this.delegate);\n\t\t// ensure callable is what sets up the SecurityContextHolder\n\t\tSecurityContextHolder.clearContext();\n\t\tassertWrapped(this.callable);\n\t}\n\n\t@Test\n\tpublic void callDefaultSecurityContextWithCustomSecurityContextHolderStrategy() throws Exception {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = spy(new MockSecurityContextHolderStrategy());\n\t\tgivenDelegateCallWillAnswerWithCurrentSecurityContext(securityContextHolderStrategy);\n\t\tsecurityContextHolderStrategy.setContext(this.securityContext);\n\t\tDelegatingSecurityContextCallable<Object> callable = new DelegatingSecurityContextCallable<>(this.delegate);\n\t\tcallable.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tthis.callable = callable;\n\t\t// ensure callable is what sets up the SecurityContextHolder\n\t\tsecurityContextHolderStrategy.clearContext();\n\t\tassertWrapped(this.callable);\n\t\tverify(securityContextHolderStrategy, atLeastOnce()).getContext();\n\t}\n\n\t// SEC-3031\n\t@Test\n\tpublic void callOnSameThread() throws Exception {\n\t\tgivenDelegateCallWillAnswerWithCurrentSecurityContext();\n\t\tthis.originalSecurityContext = this.securityContext;\n\t\tSecurityContextHolder.setContext(this.originalSecurityContext);\n\t\tthis.callable = new DelegatingSecurityContextCallable<>(this.delegate, this.securityContext);\n\t\tassertWrapped(this.callable.call());\n\t}\n\n\t@Test\n\tpublic void createNullDelegate() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> DelegatingSecurityContextCallable.create(null, this.securityContext));\n\t}\n\n\t@Test\n\tpublic void createNullDelegateAndSecurityContext() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> DelegatingSecurityContextRunnable.create(null, null));\n\t}\n\n\t@Test\n\tpublic void createNullSecurityContext() throws Exception {\n\t\tgivenDelegateCallWillAnswerWithCurrentSecurityContext();\n\t\tSecurityContextHolder.setContext(this.securityContext);\n\t\tthis.callable = DelegatingSecurityContextCallable.create(this.delegate, null);\n\t\t// ensure callable is what sets up the SecurityContextHolder\n\t\tSecurityContextHolder.clearContext();\n\t\tassertWrapped(this.callable);\n\t}\n\n\t@Test\n\tpublic void create() throws Exception {\n\t\tgivenDelegateCallWillAnswerWithCurrentSecurityContext();\n\t\tthis.callable = DelegatingSecurityContextCallable.create(this.delegate, this.securityContext);\n\t\tassertWrapped(this.callable);\n\t}\n\n\t// SEC-2682\n\t@Test\n\tpublic void toStringDelegates() {\n\t\tthis.callable = new DelegatingSecurityContextCallable<>(this.delegate, this.securityContext);\n\t\tassertThat(this.callable.toString()).isEqualTo(this.delegate.toString());\n\t}\n\n\tprivate void assertWrapped(Callable<Object> callable) throws Exception {\n\t\tFuture<Object> submit = this.executor.submit(callable);\n\t\tassertWrapped(submit.get());\n\t}\n\n\tprivate void assertWrapped(Object callableResult) throws Exception {\n\t\tverify(this.delegate).call();\n\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(this.originalSecurityContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorIntegrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.security.DelegatingSecurityContextTestUtils;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Steve Riesenberg\n */\npublic class DelegatingSecurityContextExecutorIntegrationTests {\n\n\t@Test\n\tpublic void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tExecutor::execute\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate DelegatingSecurityContextExecutor createExecutor(ScheduledExecutorService delegate) {\n\t\treturn new DelegatingSecurityContextExecutor(delegate, securityContext());\n\t}\n\n\tprivate static SecurityContext securityContext() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\n\t\treturn securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorServiceIntegrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.security.DelegatingSecurityContextTestUtils;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Steve Riesenberg\n */\npublic class DelegatingSecurityContextExecutorServiceIntegrationTests {\n\n\t@Test\n\tpublic void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tExecutorService::execute\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void submitWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void submitWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext submitAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.callAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tExecutorService::submit\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate DelegatingSecurityContextExecutorService createExecutor(ScheduledExecutorService delegate) {\n\t\treturn new DelegatingSecurityContextExecutorService(delegate, securityContext());\n\t}\n\n\tprivate static SecurityContext securityContext() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\n\t\treturn securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnableTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.core.task.SyncTaskExecutor;\nimport org.springframework.core.task.support.ExecutorServiceAdapter;\nimport org.springframework.security.core.context.MockSecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 3.2\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingSecurityContextRunnableTests {\n\n\t@Mock\n\tprivate Runnable delegate;\n\n\t@Mock\n\tprivate SecurityContext securityContext;\n\n\t@Mock\n\tprivate Object callableResult;\n\n\tprivate Runnable runnable;\n\n\tprivate ExecutorService executor;\n\n\tprivate SecurityContext originalSecurityContext;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.originalSecurityContext = SecurityContextHolder.createEmptyContext();\n\t\tthis.executor = Executors.newFixedThreadPool(1);\n\t}\n\n\tprivate void givenDelegateRunWillAnswerWithCurrentSecurityContext() {\n\t\twillAnswer((Answer<Object>) (invocation) -> {\n\t\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(this.securityContext);\n\t\t\treturn null;\n\t\t}).given(this.delegate).run();\n\t}\n\n\tprivate void givenDelegateRunWillAnswerWithCurrentSecurityContext(SecurityContextHolderStrategy strategy) {\n\t\twillAnswer((Answer<Object>) (invocation) -> {\n\t\t\tassertThat(strategy.getContext()).isEqualTo(this.securityContext);\n\t\t\treturn null;\n\t\t}).given(this.delegate).run();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorNullDelegate() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingSecurityContextRunnable(null));\n\t}\n\n\t@Test\n\tpublic void constructorNullDelegateNonNullSecurityContext() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingSecurityContextRunnable(null, this.securityContext));\n\t}\n\n\t@Test\n\tpublic void constructorNullDelegateAndSecurityContext() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingSecurityContextRunnable(null, null));\n\t}\n\n\t@Test\n\tpublic void constructorNullSecurityContext() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingSecurityContextRunnable(this.delegate, null));\n\t}\n\n\t@Test\n\tpublic void call() throws Exception {\n\t\tgivenDelegateRunWillAnswerWithCurrentSecurityContext();\n\t\tthis.runnable = new DelegatingSecurityContextRunnable(this.delegate, this.securityContext);\n\t\tassertWrapped(this.runnable);\n\t}\n\n\t@Test\n\tpublic void callDefaultSecurityContext() throws Exception {\n\t\tgivenDelegateRunWillAnswerWithCurrentSecurityContext();\n\t\tSecurityContextHolder.setContext(this.securityContext);\n\t\tthis.runnable = new DelegatingSecurityContextRunnable(this.delegate);\n\t\tSecurityContextHolder.clearContext(); // ensure runnable is what sets up the\n\t\t\t\t\t\t\t\t\t\t\t\t// SecurityContextHolder\n\t\tassertWrapped(this.runnable);\n\t}\n\n\t@Test\n\tpublic void callDefaultSecurityContextWithCustomSecurityContextHolderStrategy() throws Exception {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = spy(new MockSecurityContextHolderStrategy());\n\t\tgivenDelegateRunWillAnswerWithCurrentSecurityContext(securityContextHolderStrategy);\n\t\tsecurityContextHolderStrategy.setContext(this.securityContext);\n\t\tDelegatingSecurityContextRunnable runnable = new DelegatingSecurityContextRunnable(this.delegate);\n\t\trunnable.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tthis.runnable = runnable;\n\t\t// ensure callable is what sets up the SecurityContextHolder\n\t\tsecurityContextHolderStrategy.clearContext();\n\t\tassertWrapped(this.runnable);\n\t\tverify(securityContextHolderStrategy, atLeastOnce()).getContext();\n\t}\n\n\t// SEC-3031\n\t@Test\n\tpublic void callOnSameThread() throws Exception {\n\t\tgivenDelegateRunWillAnswerWithCurrentSecurityContext();\n\t\tthis.originalSecurityContext = this.securityContext;\n\t\tSecurityContextHolder.setContext(this.originalSecurityContext);\n\t\tthis.executor = synchronousExecutor();\n\t\tthis.runnable = new DelegatingSecurityContextRunnable(this.delegate, this.securityContext);\n\t\tassertWrapped(this.runnable);\n\t}\n\n\t@Test\n\tpublic void createNullDelegate() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> DelegatingSecurityContextRunnable.create(null, this.securityContext));\n\t}\n\n\t@Test\n\tpublic void createNullDelegateAndSecurityContext() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> DelegatingSecurityContextRunnable.create(null, null));\n\t}\n\n\t@Test\n\tpublic void createNullSecurityContext() throws Exception {\n\t\tgivenDelegateRunWillAnswerWithCurrentSecurityContext();\n\t\tSecurityContextHolder.setContext(this.securityContext);\n\t\tthis.runnable = DelegatingSecurityContextRunnable.create(this.delegate, null);\n\t\tSecurityContextHolder.clearContext(); // ensure runnable is what sets up the\n\t\t\t\t\t\t\t\t\t\t\t\t// SecurityContextHolder\n\t\tassertWrapped(this.runnable);\n\t}\n\n\t@Test\n\tpublic void create() throws Exception {\n\t\tgivenDelegateRunWillAnswerWithCurrentSecurityContext();\n\t\tthis.runnable = DelegatingSecurityContextRunnable.create(this.delegate, this.securityContext);\n\t\tassertWrapped(this.runnable);\n\t}\n\n\t// SEC-2682\n\t@Test\n\tpublic void toStringDelegates() {\n\t\tthis.runnable = new DelegatingSecurityContextRunnable(this.delegate, this.securityContext);\n\t\tassertThat(this.runnable.toString()).isEqualTo(this.delegate.toString());\n\t}\n\n\tprivate void assertWrapped(Runnable runnable) throws Exception {\n\t\tFuture<?> submit = this.executor.submit(runnable);\n\t\tsubmit.get();\n\t\tverify(this.delegate).run();\n\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(this.originalSecurityContext);\n\t}\n\n\tprivate static ExecutorService synchronousExecutor() {\n\t\treturn new ExecutorServiceAdapter(new SyncTaskExecutor());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorServiceIntegrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.TimeUnit;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.security.DelegatingSecurityContextTestUtils;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Steve Riesenberg\n */\npublic class DelegatingSecurityContextScheduledExecutorServiceIntegrationTests {\n\n\t@Test\n\tpublic void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tScheduledExecutorService::execute\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void submitWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void submitWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext submitAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.callAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tScheduledExecutorService::submit\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void scheduleWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = scheduleAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void scheduleWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = scheduleAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext scheduleAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.callAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\t(executor, task) -> executor.schedule(task, 50, TimeUnit.MILLISECONDS)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate DelegatingSecurityContextScheduledExecutorService createExecutor(ScheduledExecutorService delegate) {\n\t\treturn new DelegatingSecurityContextScheduledExecutorService(delegate, securityContext());\n\t}\n\n\tprivate static SecurityContext securityContext() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\n\t\treturn securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextSupportTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.context.SecurityContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class DelegatingSecurityContextSupportTests extends AbstractDelegatingSecurityContextTestSupport {\n\n\tprivate AbstractDelegatingSecurityContextSupport support;\n\n\t@Test\n\tpublic void wrapCallable() throws Exception {\n\t\texplicitSecurityContextSetup();\n\t\tthis.support = new ConcreteDelegatingSecurityContextSupport(this.securityContext);\n\t\tassertThat(this.support.wrap(this.callable)).isSameAs(this.wrappedCallable);\n\t\tassertThat(this.securityContextCaptor.getValue()).isSameAs(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void wrapCallableNullSecurityContext() throws Exception {\n\t\tcurrentSecurityContextSetup();\n\t\tthis.support = new ConcreteDelegatingSecurityContextSupport(null);\n\t\tassertThat(this.support.wrap(this.callable)).isSameAs(this.wrappedCallable);\n\t}\n\n\t@Test\n\tpublic void wrapRunnable() throws Exception {\n\t\texplicitSecurityContextSetup();\n\t\tthis.support = new ConcreteDelegatingSecurityContextSupport(this.securityContext);\n\t\tassertThat(this.support.wrap(this.runnable)).isSameAs(this.wrappedRunnable);\n\t\tassertThat(this.securityContextCaptor.getValue()).isSameAs(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void wrapRunnableNullSecurityContext() throws Exception {\n\t\tcurrentSecurityContextSetup();\n\t\tthis.support = new ConcreteDelegatingSecurityContextSupport(null);\n\t\tassertThat(this.support.wrap(this.runnable)).isSameAs(this.wrappedRunnable);\n\t}\n\n\tprivate static class ConcreteDelegatingSecurityContextSupport extends AbstractDelegatingSecurityContextSupport {\n\n\t\tConcreteDelegatingSecurityContextSupport(SecurityContext securityContext) {\n\t\t\tsuper(securityContext);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests Explicitly specifying the {@link SecurityContext} on\n * {@link DelegatingSecurityContextExecutorService}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class ExplicitDelegatingSecurityContextExecutorServiceTests\n\t\textends AbstractDelegatingSecurityContextExecutorServiceTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tsuper.explicitSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextExecutorService create() {\n\t\treturn new DelegatingSecurityContextExecutorService(this.delegate, this.securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests Explicitly specifying the {@link SecurityContext} on\n * {@link DelegatingSecurityContextExecutor}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class ExplicitDelegatingSecurityContextExecutorTests extends AbstractDelegatingSecurityContextExecutorTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tsuper.explicitSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextExecutor create() {\n\t\treturn new DelegatingSecurityContextExecutor(getExecutor(), this.securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextScheduledExecutorServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests Explicitly specifying the {@link SecurityContext} on\n * {@link DelegatingSecurityContextScheduledExecutorService}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class ExplicitDelegatingSecurityContextScheduledExecutorServiceTests\n\t\textends AbstractDelegatingSecurityContextScheduledExecutorServiceTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tthis.explicitSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextScheduledExecutorService create() {\n\t\treturn new DelegatingSecurityContextScheduledExecutorService(this.delegate, this.securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/context/DelegatingApplicationListenerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.context;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.event.SmartApplicationListener;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingApplicationListenerTests {\n\n\t@Mock\n\tSmartApplicationListener delegate;\n\n\tApplicationEvent event;\n\n\tDelegatingApplicationListener listener;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.event = new ApplicationEvent(this) {\n\t\t};\n\t\tthis.listener = new DelegatingApplicationListener();\n\t\tthis.listener.addListener(this.delegate);\n\t}\n\n\t@Test\n\tpublic void processEventNull() {\n\t\tthis.listener.onApplicationEvent(null);\n\t\tverify(this.delegate, never()).onApplicationEvent(any(ApplicationEvent.class));\n\t}\n\n\t@Test\n\tpublic void processEventSuccess() {\n\t\tgiven(this.delegate.supportsEventType(this.event.getClass())).willReturn(true);\n\t\tgiven(this.delegate.supportsSourceType(this.event.getSource().getClass())).willReturn(true);\n\t\tthis.listener.onApplicationEvent(this.event);\n\t\tverify(this.delegate).onApplicationEvent(this.event);\n\t}\n\n\t@Test\n\tpublic void processEventEventTypeNotSupported() {\n\t\tthis.listener.onApplicationEvent(this.event);\n\t\tverify(this.delegate, never()).onApplicationEvent(any(ApplicationEvent.class));\n\t}\n\n\t@Test\n\tpublic void processEventSourceTypeNotSupported() {\n\t\tgiven(this.delegate.supportsEventType(this.event.getClass())).willReturn(true);\n\t\tthis.listener.onApplicationEvent(this.event);\n\t\tverify(this.delegate, never()).onApplicationEvent(any(ApplicationEvent.class));\n\t}\n\n\t@Test\n\tpublic void addNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.listener.addListener(null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/converter/RsaKeyConvertersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.converter;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.interfaces.RSAPrivateCrtKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\n\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link RsaKeyConverters}\n */\npublic class RsaKeyConvertersTests {\n\n\t// @formatter:off\n\tprivate static final String PKCS8_PRIVATE_KEY = \"-----BEGIN PRIVATE KEY-----\\n\"\n\t\t\t+ \"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMk7CKSTfu3QoV\\n\"\n\t\t\t+ \"HoPVXxwZO+qweztd36cVWYqGOZinrOR2crWFu50AgR2CsdIH0+cqo7F4Vx7/3O8i\\n\"\n\t\t\t+ \"RpYYZPe2VoO5sumzJt8P6fS80/TAKjhJDAqgZKRJTgGN8KxCM6p/aJli1ZeDBqiV\\n\"\n\t\t\t+ \"v7vJJe+ZgJuPGRS+HMNa/wPxEkqqXsglcJcQV1ZEtfKXSHB7jizKpRL38185SyAC\\n\"\n\t\t\t+ \"pwyjvBu6Cmm1URfhQo88mf239ONh4dZ2HoDfzN1q6Ssu4F4hgutxr9B0DVLDP5u+\\n\"\n\t\t\t+ \"WFrm3nsJ76zf99uJ+ntMUHJ+bY+gOjSlVWIVBIZeAaEGKCNWRk/knjvjbijpvm3U\\n\"\n\t\t\t+ \"acGlgdL3AgMBAAECggEACxxxS7zVyu91qI2s5eSKmAQAXMqgup6+2hUluc47nqUv\\n\"\n\t\t\t+ \"uZz/c/6MPkn2Ryo+65d4IgqmMFjSfm68B/2ER5FTcvoLl1Xo2twrrVpUmcg3BClS\\n\"\n\t\t\t+ \"IZPuExdhVNnxjYKEWwcyZrehyAoR261fDdcFxLRW588efIUC+rPTTRHzAc7sT+Ln\\n\"\n\t\t\t+ \"t/uFeYNWJm3LaegOLoOmlMAhJ5puAWSN1F0FxtRf/RVgzbLA9QC975SKHJsfWCSr\\n\"\n\t\t\t+ \"IZyPsdeaqomKaF65l8nfqlE0Ua2L35gIOGKjUwb7uUE8nI362RWMtYdoi3zDDyoY\\n\"\n\t\t\t+ \"hSFbgjylCHDM0u6iSh6KfqOHtkYyJ8tUYgVWl787wQKBgQDYO3wL7xuDdD101Lyl\\n\"\n\t\t\t+ \"AnaDdFB9fxp83FG1cWr+t7LYm9YxGfEUsKHAJXN6TIayDkOOoVwIl+Gz0T3Z06Bm\\n\"\n\t\t\t+ \"eBGLrB9mrVA7+C7NJwu5gTMlzP6HxUR9zKJIQ/VB1NUGM77LSmvOFbHc9Q0+z8EH\\n\"\n\t\t\t+ \"X5WO516a3Z7lNtZJcCoPOtu2rwKBgQCmbj41Fh+SSEUApCEKms5ETRpe7LXQlJgx\\n\"\n\t\t\t+ \"yW7zcJNNuIb1C3vBLPxjiOTMgYKOeMg5rtHTGLT43URHLh9ArjawasjSAr4AM3J4\\n\"\n\t\t\t+ \"xpoi/sKGDdiKOsuDWIGfzdYL8qyTHSdpZLQsCTMRiRYgAHZFPgNa7SLZRfZicGlr\\n\"\n\t\t\t+ \"GHN1rJW6OQKBgEjiM/upyrJSWeypUDSmUeAZMpA6aWkwsfHgmtnkfUn5rQa74cDB\\n\"\n\t\t\t+ \"kKO9e+D7LmOR3z+SL/1NhGwh2SE07dncGr3jdGodfO/ZxZyszozmeaECKcEFwwJM\\n\"\n\t\t\t+ \"GV8WWPKplGwUwPiwywmZ0mvRxXcoe73KgBS88+xrSwWjqDL0tZiQlEJNAoGATkei\\n\"\n\t\t\t+ \"GMQMG3jEg9Wu+NbxV6zQT3+U0MNjhl9RQU1c63x0dcNt9OFc4NAdlZcAulRTENaK\\n\"\n\t\t\t+ \"OHjxffBM0hH+fySx8m53gFfr2BpaqDX5f6ZGBlly1SlsWZ4CchCVsc71nshipi7I\\n\"\n\t\t\t+ \"k8HL9F5/OpQdDNprJ5RMBNfkWE65Nrcsb1e6oPkCgYAxwgdiSOtNg8PjDVDmAhwT\\n\"\n\t\t\t+ \"Mxj0Dtwi2fAqQ76RVrrXpNp3uCOIAu4CfruIb5llcJ3uak0ZbnWri32AxSgk80y3\\n\"\n\t\t\t+ \"EWiRX/WEDu5znejF+5O3pI02atWWcnxifEKGGlxwkcMbQdA67MlrJLFaSnnGpNXo\\n\"\n\t\t\t+ \"yPfcul058SOqhafIZQMEKQ==\\n\"\n\t\t\t+ \"-----END PRIVATE KEY-----\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String PKCS1_PRIVATE_KEY = \"-----BEGIN RSA PRIVATE KEY-----\\n\"\n\t\t\t+ \"MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw\\n\"\n\t\t\t+ \"33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW\\n\"\n\t\t\t+ \"+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB\\n\"\n\t\t\t+ \"AoGAD+onAtVye4ic7VR7V50DF9bOnwRwNXrARcDhq9LWNRrRGElESYYTQ6EbatXS\\n\"\n\t\t\t+ \"3MCyjjX2eMhu/aF5YhXBwkppwxg+EOmXeh+MzL7Zh284OuPbkglAaGhV9bb6/5Cp\\n\"\n\t\t\t+ \"uGb1esyPbYW+Ty2PC0GSZfIXkXs76jXAu9TOBvD0ybc2YlkCQQDywg2R/7t3Q2OE\\n\"\n\t\t\t+ \"2+yo382CLJdrlSLVROWKwb4tb2PjhY4XAwV8d1vy0RenxTB+K5Mu57uVSTHtrMK0\\n\"\n\t\t\t+ \"GAtFr833AkEA6avx20OHo61Yela/4k5kQDtjEf1N0LfI+BcWZtxsS3jDM3i1Hp0K\\n\"\n\t\t\t+ \"Su5rsCPb8acJo5RO26gGVrfAsDcIXKC+bQJAZZ2XIpsitLyPpuiMOvBbzPavd4gY\\n\"\n\t\t\t+ \"6Z8KWrfYzJoI/Q9FuBo6rKwl4BFoToD7WIUS+hpkagwWiz+6zLoX1dbOZwJACmH5\\n\"\n\t\t\t+ \"fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523\\n\"\n\t\t\t+ \"Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP\\n\"\n\t\t\t+ \"FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==\\n\"\n\t\t\t+ \"-----END RSA PRIVATE KEY-----\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String X509_PUBLIC_KEY = \"-----BEGIN PUBLIC KEY-----\\n\"\n\t\t\t+ \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\\n\"\n\t\t\t+ \"UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\\n\"\n\t\t\t+ \"HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\\n\"\n\t\t\t+ \"o2kQ+X5xK9cipRgEKwIDAQAB\\n\"\n\t\t\t+ \"-----END PUBLIC KEY-----\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String X509_CERTIFICATE = \"-----BEGIN CERTIFICATE-----\\n\" +\n\t\t\t\"MIIBqDCCARECBgF5zJA6MjANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9TaGF6\\n\" +\n\t\t\t\"aW4gU2FkYWthdGgwHhcNMjEwNjAxMTE1MTE0WhcNMjEwNTE3MjAwOTI1WjAaMRgw\\n\" +\n\t\t\t\"FgYDVQQDEw9TaGF6aW4gU2FkYWthdGgwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ\\n\" +\n\t\t\t\"AoGBAKsKpS6sliNSri3koOAgzS7Nz2cpl0tGpNP3GPuUYVMP4MA0LJ2+blxjxUcn\\n\" +\n\t\t\t\"oIajtaf9HljFetKVjyARp1zjZ3Oxm//lfmyqqI5KDUjqe5J2rdtbdFCH9FXUEoGD\\n\" +\n\t\t\t\"mu2ameR9lAfxtaGI58DGS9uJ5hvGJoIvLiaDUfv1qZ+kIwG7AgMBAAEwDQYJKoZI\\n\" +\n\t\t\t\"hvcNAQELBQADgYEAWdIIi4cGPod5O/V7K0QSTXZRLRIKFQ7qhn5XTNlMUnFnwp7c\\n\" +\n\t\t\t\"8O8EsOiCKAZeVvgRnurFkxAlVnpxmdktZ9j+mv2mrMGKJxYkZcBkFh++DRixpY8N\\n\" +\n\t\t\t\"zBLbxZJ9kcOHWWDA602FMbNIEL1OiHrfggsPk3sckSaSg4d7UoP9T6+uqq8=\\n\" +\n\t\t\t\"-----END CERTIFICATE-----\";\n\t// @formatter:on\n\n\tprivate static final String MALFORMED_X509_KEY = \"malformed\";\n\n\tprivate final Converter<InputStream, RSAPublicKey> x509 = RsaKeyConverters.x509();\n\n\tprivate final Converter<InputStream, RSAPrivateKey> pkcs8 = RsaKeyConverters.pkcs8();\n\n\t@Test\n\tpublic void pkcs8WhenConvertingPkcs8PrivateKeyThenOk() {\n\t\tRSAPrivateKey key = this.pkcs8.convert(toInputStream(PKCS8_PRIVATE_KEY));\n\t\tAssertions.assertThat(key).isInstanceOf(RSAPrivateCrtKey.class);\n\t\tAssertions.assertThat(key.getModulus().bitLength()).isEqualTo(2048);\n\t}\n\n\t@Test\n\tpublic void pkcs8WhenConvertingSingleLinePkcs8PrivateKeyThenOk() {\n\t\tRSAPrivateKey key = this.pkcs8.convert(toInputStream(PKCS8_PRIVATE_KEY.replace(\"\\n\", \"\")));\n\t\tAssertions.assertThat(key).isInstanceOf(RSAPrivateCrtKey.class);\n\t\tAssertions.assertThat(key.getModulus().bitLength()).isEqualTo(2048);\n\t}\n\n\t@Test\n\tpublic void pkcs8WhenConvertingPkcs1PrivateKeyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.pkcs8.convert(toInputStream(PKCS1_PRIVATE_KEY)));\n\t}\n\n\t@Test\n\tpublic void x509WhenConvertingX509PublicKeyThenOk() {\n\t\tRSAPublicKey key = this.x509.convert(toInputStream(X509_PUBLIC_KEY));\n\t\tAssertions.assertThat(key.getModulus().bitLength()).isEqualTo(1024);\n\t}\n\n\t@Test\n\tpublic void x509WhenConvertingSingleLineX509PublicKeyThenOk() {\n\t\tRSAPublicKey key = this.x509.convert(toInputStream(X509_PUBLIC_KEY.replace(\"\\n\", \"\")));\n\t\tAssertions.assertThat(key.getModulus().bitLength()).isEqualTo(1024);\n\t}\n\n\t@Test\n\tpublic void x509WhenConvertingX509CertificateThenOk() {\n\t\tRSAPublicKey key = this.x509.convert(toInputStream(X509_CERTIFICATE));\n\t\tAssertions.assertThat(key.getModulus().bitLength()).isEqualTo(1024);\n\t}\n\n\t@Test\n\tpublic void x509WhenConvertingX509SingleLineCertificateThenOk() {\n\t\tRSAPublicKey key = this.x509.convert(toInputStream(X509_CERTIFICATE.replace(\"\\n\", \"\")));\n\t\tAssertions.assertThat(key.getModulus().bitLength()).isEqualTo(1024);\n\t}\n\n\t@Test\n\tpublic void x509WhenConvertingDerEncodedX509PublicKeyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.x509.convert(toInputStream(MALFORMED_X509_KEY)));\n\t}\n\n\tprivate static InputStream toInputStream(String string) {\n\t\treturn new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/JavaVersionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.io.DataInputStream;\nimport java.io.InputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\npublic class JavaVersionTests {\n\n\tprivate static final int JDK17_CLASS_VERSION = 61;\n\n\t@Test\n\tpublic void authenticationWhenJdk17ThenCorrectJdkCompatibility() throws Exception {\n\t\tassertClassVersion(Authentication.class, JDK17_CLASS_VERSION);\n\t}\n\n\tprivate void assertClassVersion(Class<?> clazz, int classVersion) throws Exception {\n\t\tString classResourceName = clazz.getName().replaceAll(\"\\\\.\", \"/\") + \".class\";\n\t\ttry (InputStream input = Thread.currentThread()\n\t\t\t.getContextClassLoader()\n\t\t\t.getResourceAsStream(classResourceName)) {\n\t\t\tDataInputStream data = new DataInputStream(input);\n\t\t\tdata.readInt();\n\t\t\tdata.readShort(); // minor\n\t\t\tint major = data.readShort();\n\t\t\tassertThat(major).isEqualTo(classVersion);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/SpringSecurityCoreVersionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Answers;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.SpringVersion;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Checks that the embedded version information is up to date.\n *\n * @author Luke Taylor\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class SpringSecurityCoreVersionTests {\n\n\t@Mock\n\tprivate Log logger;\n\n\t@Mock(answer = Answers.CALLS_REAL_METHODS)\n\tprivate MockedStatic<SpringVersion> springVersion;\n\n\t@Mock(answer = Answers.CALLS_REAL_METHODS)\n\tprivate MockedStatic<SpringSecurityCoreVersion> springSecurityCoreVersion;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tField logger = ReflectionUtils.findField(SpringSecurityCoreVersion.class, \"logger\");\n\t\tStaticFinalReflectionUtils.setField(logger, this.logger);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tSystem.clearProperty(getDisableChecksProperty());\n\t\tField logger = ReflectionUtils.findField(SpringSecurityCoreVersion.class, \"logger\");\n\t\tStaticFinalReflectionUtils.setField(logger, LogFactory.getLog(SpringSecurityCoreVersion.class));\n\t}\n\n\t@Test\n\tpublic void springVersionIsUpToDate() {\n\t\t// Property is set by the build script\n\t\tString springVersion = System.getProperty(\"springVersion\");\n\t\tassertThat(SpringSecurityCoreVersion.MIN_SPRING_VERSION).isEqualTo(springVersion);\n\t}\n\n\t@Test\n\t@Disabled(\"Since 6.3. See gh-3737\")\n\tpublic void serialVersionMajorAndMinorVersionMatchBuildVersion() {\n\t\tString version = System.getProperty(\"springSecurityVersion\");\n\t\t// Strip patch version\n\t\tString serialVersion = String.valueOf(SpringSecurityCoreVersion.SERIAL_VERSION_UID).substring(0, 2);\n\t\tassertThat(serialVersion.charAt(0)).isEqualTo(version.charAt(0));\n\t\tassertThat(serialVersion.charAt(1)).isEqualTo(version.charAt(2));\n\t}\n\n\t// SEC-2295\n\t@Test\n\tpublic void noLoggingIfVersionsAreEqual() throws Exception {\n\t\tString version = \"1\";\n\t\texpectSpringSecurityVersionThenReturn(version);\n\t\texpectSpringVersionThenReturn(version);\n\t\tperformChecks();\n\t\tverifyNoMoreInteractions(this.logger);\n\t}\n\n\t@Test\n\tpublic void noLoggingIfSpringVersionNull() throws Exception {\n\t\tString version = \"1\";\n\t\texpectSpringSecurityVersionThenReturn(version);\n\t\texpectSpringVersionThenReturn(null);\n\t\tperformChecks();\n\t\tverifyNoMoreInteractions(this.logger);\n\t}\n\n\t@Test\n\tpublic void warnIfSpringVersionTooSmall() throws Exception {\n\t\texpectSpringSecurityVersionThenReturn(\"3\");\n\t\texpectSpringVersionThenReturn(\"2\");\n\t\tperformChecks();\n\t\tverify(this.logger, times(1)).warn(any());\n\t}\n\n\t@Test\n\tpublic void noWarnIfSpringVersionLarger() throws Exception {\n\t\tString version = \"4.0.0.RELEASE\";\n\t\texpectSpringSecurityVersionThenReturn(version);\n\t\texpectSpringVersionThenReturn(version);\n\t\tperformChecks();\n\t\tverify(this.logger, never()).warn(any());\n\t}\n\n\t// SEC-2697\n\t@Test\n\tpublic void noWarnIfSpringPatchVersionDoubleDigits() throws Exception {\n\t\tString minSpringVersion = \"3.2.8.RELEASE\";\n\t\texpectSpringSecurityVersionThenReturn(\"3.2.0.RELEASE\");\n\t\texpectSpringVersionThenReturn(\"3.2.10.RELEASE\");\n\t\tperformChecks(minSpringVersion);\n\t\tverify(this.logger, never()).warn(any());\n\t}\n\n\t@Test\n\tpublic void noLoggingIfPropertySet() throws Exception {\n\t\texpectSpringSecurityVersionThenReturn(\"3\");\n\t\texpectSpringVersionThenReturn(\"2\");\n\t\tSystem.setProperty(getDisableChecksProperty(), Boolean.TRUE.toString());\n\t\tperformChecks();\n\t\tverifyNoMoreInteractions(this.logger);\n\t}\n\n\tprivate String getDisableChecksProperty() {\n\t\treturn SpringSecurityCoreVersion.class.getName().concat(\".DISABLE_CHECKS\");\n\t}\n\n\tprivate void performChecks() {\n\t\tMethod method = ReflectionUtils.findMethod(SpringSecurityCoreVersion.class, \"performVersionChecks\");\n\t\tReflectionUtils.makeAccessible(method);\n\t\tReflectionUtils.invokeMethod(method, null);\n\t}\n\n\tprivate void performChecks(String minSpringVersion) {\n\t\tMethod method = ReflectionUtils.findMethod(SpringSecurityCoreVersion.class, \"performVersionChecks\",\n\t\t\t\tString.class);\n\t\tReflectionUtils.makeAccessible(method);\n\t\tReflectionUtils.invokeMethod(method, null, minSpringVersion);\n\t}\n\n\tprivate void expectSpringSecurityVersionThenReturn(String version) {\n\t\tthis.springSecurityCoreVersion.when(SpringSecurityCoreVersion::getVersion).thenReturn(version);\n\t}\n\n\tprivate void expectSpringVersionThenReturn(String version) {\n\t\tthis.springVersion.when(SpringVersion::getVersion).thenReturn(version);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/SpringSecurityMessageSourceTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.util.Locale;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.i18n.LocaleContextHolder;\nimport org.springframework.context.support.MessageSourceAccessor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link org.springframework.security.core.SpringSecurityMessageSource}.\n */\npublic class SpringSecurityMessageSourceTests {\n\n\t@Test\n\tpublic void testOperation() {\n\t\tSpringSecurityMessageSource msgs = new SpringSecurityMessageSource();\n\t\tassertThat(\"\\u4E0D\\u5141\\u8BB8\\u8BBF\\u95EE\")\n\t\t\t.isEqualTo(msgs.getMessage(\"AbstractAccessDecisionManager.accessDenied\", null, Locale.SIMPLIFIED_CHINESE));\n\t}\n\n\t@Test\n\tpublic void testReplacableLookup() {\n\t\t// Change Locale to English\n\t\tLocale before = LocaleContextHolder.getLocale();\n\t\tLocaleContextHolder.setLocale(Locale.FRENCH);\n\t\t// Cause a message to be generated\n\t\tMessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\t\tassertThat(\"Le jeton nonce est compromis FOOBAR\").isEqualTo(messages.getMessage(\n\t\t\t\t\"DigestAuthenticationFilter.nonceCompromised\", new Object[] { \"FOOBAR\" }, \"ERROR - FAILED TO LOOKUP\"));\n\t\t// Revert to original Locale\n\t\tLocaleContextHolder.setLocale(before);\n\t}\n\n\t// SEC-3013\n\t@Test\n\tpublic void germanSystemLocaleWithEnglishLocaleContextHolder() {\n\t\tLocale beforeSystem = Locale.getDefault();\n\t\tLocale.setDefault(Locale.GERMAN);\n\t\tLocale beforeHolder = LocaleContextHolder.getLocale();\n\t\tLocaleContextHolder.setLocale(Locale.US);\n\t\tMessageSourceAccessor msgs = SpringSecurityMessageSource.getAccessor();\n\t\tassertThat(\"Access is denied\")\n\t\t\t.isEqualTo(msgs.getMessage(\"AbstractAccessDecisionManager.accessDenied\", \"Ooops\"));\n\t\t// Revert to original Locale\n\t\tLocale.setDefault(beforeSystem);\n\t\tLocaleContextHolder.setLocale(beforeHolder);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/StaticFinalReflectionUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\n\nimport sun.misc.Unsafe;\n\nimport org.springframework.objenesis.instantiator.util.UnsafeUtils;\n\n/**\n * Used for setting static variables even if they are private static final.\n *\n * The code in this class has been adopted from Powermock's <a href=\n * \"https://github.com/noushadali/powermock/blob/powermock-1.5.4/reflect/src/main/java/org/powermock/reflect/internal/WhiteboxImpl.java#L326\">WhiteboxImpl</a>.\n *\n * @author Rob Winch\n */\nfinal class StaticFinalReflectionUtils {\n\n\t/**\n\t * Used to support setting static fields that are final using Java's Unsafe. If the\n\t * field is not static final, use\n\t * {@link org.springframework.test.util.ReflectionTestUtils}.\n\t * @param field the field to set\n\t * @param newValue the new value\n\t */\n\tstatic void setField(final Field field, final Object newValue) {\n\t\ttry {\n\t\t\tfield.setAccessible(true);\n\t\t\tint fieldModifiersMask = field.getModifiers();\n\t\t\tboolean isFinalModifierPresent = (fieldModifiersMask & Modifier.FINAL) == Modifier.FINAL;\n\t\t\tif (isFinalModifierPresent) {\n\t\t\t\tAccessController.doPrivileged(new PrivilegedAction<Object>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Object run() {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tUnsafe unsafe = UnsafeUtils.getUnsafe();\n\t\t\t\t\t\t\tlong offset = unsafe.staticFieldOffset(field);\n\t\t\t\t\t\t\tObject base = unsafe.staticFieldBase(field);\n\t\t\t\t\t\t\tsetFieldUsingUnsafe(base, field.getType(), offset, newValue, unsafe);\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Throwable thrown) {\n\t\t\t\t\t\t\tthrow new RuntimeException(thrown);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfield.set(null, newValue);\n\t\t\t}\n\t\t}\n\t\tcatch (SecurityException | IllegalAccessException | IllegalArgumentException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprivate static void setFieldUsingUnsafe(Object base, Class type, long offset, Object newValue, Unsafe unsafe) {\n\t\tif (type == Integer.TYPE) {\n\t\t\tunsafe.putInt(base, offset, ((Integer) newValue));\n\t\t}\n\t\telse if (type == Short.TYPE) {\n\t\t\tunsafe.putShort(base, offset, ((Short) newValue));\n\t\t}\n\t\telse if (type == Long.TYPE) {\n\t\t\tunsafe.putLong(base, offset, ((Long) newValue));\n\t\t}\n\t\telse if (type == Byte.TYPE) {\n\t\t\tunsafe.putByte(base, offset, ((Byte) newValue));\n\t\t}\n\t\telse if (type == Boolean.TYPE) {\n\t\t\tunsafe.putBoolean(base, offset, ((Boolean) newValue));\n\t\t}\n\t\telse if (type == Float.TYPE) {\n\t\t\tunsafe.putFloat(base, offset, ((Float) newValue));\n\t\t}\n\t\telse if (type == Double.TYPE) {\n\t\t\tunsafe.putDouble(base, offset, ((Double) newValue));\n\t\t}\n\t\telse if (type == Character.TYPE) {\n\t\t\tunsafe.putChar(base, offset, ((Character) newValue));\n\t\t}\n\t\telse {\n\t\t\tunsafe.putObject(base, offset, newValue);\n\t\t}\n\t}\n\n\tprivate StaticFinalReflectionUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScannerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ExpressionTemplateSecurityAnnotationScanner}\n *\n * @author DingHao\n */\npublic class ExpressionTemplateSecurityAnnotationScannerTests {\n\n\tprivate ExpressionTemplateSecurityAnnotationScanner<PreAuthorize> scanner = new ExpressionTemplateSecurityAnnotationScanner<>(\n\t\t\tPreAuthorize.class, new AnnotationTemplateExpressionDefaults());\n\n\t@Test\n\tvoid parseMultipleMetaSourceAnnotationParameter() throws Exception {\n\t\tMethod method = MessageService.class.getDeclaredMethod(\"sayHello\", String.class);\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"check(#name)\");\n\t}\n\n\t@Test\n\tvoid parseMultipleMetaSourceAnnotationParameterWithAliasFor() throws Exception {\n\t\tMethod method = MessageService.class.getDeclaredMethod(\"save\", String.class);\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"check(#name)\");\n\t}\n\n\t@Test\n\tvoid parseMetaSourceAnnotationWithEnumImplementingExpressionTemplateValueProvider() throws Exception {\n\t\tMethod method = MessageService.class.getDeclaredMethod(\"process\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"hasAnyAuthority('user.READ','user.WRITE')\");\n\t}\n\n\tenum Permission implements ExpressionTemplateValueProvider {\n\n\t\tREAD, WRITE;\n\n\t\t@Override\n\t\tpublic String getExpressionTemplateValue() {\n\t\t\treturn switch (this) {\n\t\t\t\tcase READ -> \"'user.READ'\";\n\t\t\t\tcase WRITE -> \"'user.WRITE'\";\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@Documented\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@PreAuthorize(\"hasAnyAuthority({permissions})\")\n\t@interface HasAnyCustomPermissions {\n\n\t\tPermission[] permissions();\n\n\t}\n\n\t@Documented\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@HasAnyCustomPermissions(permissions = { Permission.READ, Permission.WRITE })\n\t@interface HasAllCustomPermissions {\n\n\t}\n\n\t@Documented\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@PreAuthorize(\"check({object})\")\n\t@interface HasPermission {\n\n\t\tString object();\n\n\t}\n\n\t@Documented\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@HasPermission(object = \"{value}\")\n\t@interface HasReadPermission {\n\n\t\tString value();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@HasPermission(object = \"{value}\")\n\t@interface HasWritePermission {\n\n\t\t@AliasFor(annotation = HasPermission.class, value = \"object\")\n\t\tString value();\n\n\t}\n\n\tprivate interface MessageService {\n\n\t\t@HasAllCustomPermissions\n\t\tvoid process();\n\n\t\t@HasReadPermission(\"#name\")\n\t\tString sayHello(String name);\n\n\t\t@HasWritePermission(\"#name\")\n\t\tvoid save(String name);\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/annotation/UniqueSecurityAnnotationScannerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link UniqueSecurityAnnotationScanner}\n */\npublic class UniqueSecurityAnnotationScannerTests {\n\n\tprivate UniqueSecurityAnnotationScanner<PreAuthorize> scanner = new UniqueSecurityAnnotationScanner<>(\n\t\t\tPreAuthorize.class);\n\n\tprivate UniqueSecurityAnnotationScanner<CustomParameterAnnotation> parameterScanner = new UniqueSecurityAnnotationScanner<>(\n\t\t\tCustomParameterAnnotation.class);\n\n\t@Test\n\tvoid scanWhenAnnotationOnInterfaceThenResolves() throws Exception {\n\t\tMethod method = AnnotationOnInterface.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"one\");\n\t}\n\n\t@Test\n\tvoid scanWhenAnnotationOnMethodThenResolves() throws Exception {\n\t\tMethod method = AnnotationOnInterfaceMethod.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"three\");\n\t}\n\n\t@Test\n\tvoid scanWhenAnnotationOnClassThenResolves() throws Exception {\n\t\tMethod method = AnnotationOnClass.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"five\");\n\t}\n\n\t@Test\n\tvoid scanWhenAnnotationOnClassMethodThenResolves() throws Exception {\n\t\tMethod method = AnnotationOnClassMethod.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"six\");\n\t}\n\n\t@Test\n\tvoid scanWhenInterfaceOverridingAnnotationOnInterfaceThenResolves() throws Exception {\n\t\tMethod method = InterfaceMethodOverridingAnnotationOnInterface.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"eight\");\n\t}\n\n\t@Test\n\tvoid scanWhenInterfaceOverridingMultipleInterfaceInheritanceThenResolves() throws Exception {\n\t\tMethod method = ClassInheritingInterfaceOverridingMultipleInterfaceInheritance.class\n\t\t\t.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"ten\");\n\t}\n\n\t@Test\n\tvoid scanWhenInterfaceMethodOverridingAnnotationOnInterfaceThenResolves() throws Exception {\n\t\tMethod method = InterfaceMethodOverridingMultipleInterfaceInheritance.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"eleven\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassMultipleInheritanceThenException() throws Exception {\n\t\tMethod method = ClassAttemptingMultipleInterfaceInheritance.class.getDeclaredMethod(\"method\");\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.scanner.scan(method, method.getDeclaringClass()));\n\t}\n\n\t// gh-15097\n\t@Test\n\tvoid scanWhenClassOverridingMultipleInterfaceInheritanceThenResolves() throws Exception {\n\t\tMethod method = ClassOverridingMultipleInterfaceInheritance.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"thirteen\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassMethodOverridingMultipleInterfaceInheritanceThenResolves() throws Exception {\n\t\tMethod method = ClassMethodOverridingMultipleInterfaceInheritance.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"fourteen\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassInheritingInterfaceOverridingInterfaceAnnotationThenResolves() throws Exception {\n\t\tMethod method = ClassInheritingInterfaceOverridingInterfaceAnnotation.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"seven\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassOverridingGrandparentInterfaceAnnotationThenResolves() throws Exception {\n\t\tMethod method = ClassOverridingGrandparentInterfaceAnnotation.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"sixteen\");\n\t}\n\n\t@Test\n\tvoid scanWhenMethodOverridingGrandparentInterfaceAnnotationThenResolves() throws Exception {\n\t\tMethod method = MethodOverridingGrandparentInterfaceAnnotation.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"seventeen\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassInheritingMethodOverriddenAnnotationThenResolves() throws Exception {\n\t\tMethod method = ClassInheritingMethodOverriddenAnnotation.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"eight\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassOverridingMethodOverriddenAnnotationThenResolves() throws Exception {\n\t\tMethod method = ClassOverridingMethodOverriddenAnnotation.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"eight\");\n\t}\n\n\t@Test\n\tvoid scanWhenMethodOverridingMethodOverriddenAnnotationThenResolves() throws Exception {\n\t\tMethod method = MethodOverridingMethodOverriddenAnnotation.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"twenty\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassInheritingMultipleInheritanceThenException() throws Exception {\n\t\tMethod method = ClassInheritingMultipleInheritance.class.getDeclaredMethod(\"method\");\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.scanner.scan(method, method.getDeclaringClass()));\n\t}\n\n\t@Test\n\tvoid scanWhenClassOverridingMultipleInheritanceThenResolves() throws Exception {\n\t\tMethod method = ClassOverridingMultipleInheritance.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"twentytwo\");\n\t}\n\n\t@Test\n\tvoid scanWhenMethodOverridingMultipleInheritanceThenResolves() throws Exception {\n\t\tMethod method = MethodOverridingMultipleInheritance.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"twentythree\");\n\t}\n\n\t@Test\n\tvoid scanWhenInheritingInterfaceAndMethodAnnotationsThenResolves() throws Exception {\n\t\tMethod method = InheritingInterfaceAndMethodAnnotations.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"three\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassOverridingInterfaceAndMethodInheritanceThenResolves() throws Exception {\n\t\tMethod method = ClassOverridingInterfaceAndMethodInheritance.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"three\");\n\t}\n\n\t@Test\n\tvoid scanWhenMethodOverridingInterfaceAndMethodInheritanceThenResolves() throws Exception {\n\t\tMethod method = MethodOverridingInterfaceAndMethodInheritance.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"twentysix\");\n\t}\n\n\t@Test\n\tvoid scanWhenMultipleMethodInheritanceThenException() throws Exception {\n\t\tMethod method = MultipleMethodInheritance.class.getDeclaredMethod(\"method\");\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.scanner.scan(method, method.getDeclaringClass()));\n\t}\n\n\t// gh-13234\n\t@Test\n\tvoid scanWhenClassInheritingInterfaceAnnotationThenResolves() throws Exception {\n\t\tMethod method = ClassInheritingInterfaceMethodAnnotation.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"three\");\n\t}\n\n\t@Test\n\tvoid scanWhenMethodInheritingMethodOverridingInterfaceAndMethodInheritanceThenResolves() throws Exception {\n\t\tMethod method = MethodInheritingMethodOverridingInterfaceAndMethodInheritance.class.getMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"twentysix\");\n\t}\n\n\t@Test\n\tvoid scanWhenClassOverridingMethodOverridingInterfaceAndMethodInheritanceThenResolves() throws Exception {\n\t\tMethod method = ClassOverridingMethodOverridingInterfaceAndMethodInheritance.class.getMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method,\n\t\t\t\tClassOverridingMethodOverridingInterfaceAndMethodInheritance.class);\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"twentysix\");\n\t}\n\n\t@Test\n\tvoid scanWhenInterfaceInheritingAnnotationsAtDifferentLevelsThenException() throws Exception {\n\t\tMethod method = InterfaceInheritingAnnotationsAtDifferentLevels.class.getMethod(\"method\");\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.scanner.scan(method, method.getDeclaringClass()));\n\t}\n\n\t@Test\n\tvoid scanWhenClassMethodOverridingAnnotationOnMethodThenResolves() throws Exception {\n\t\tMethod method = ClassMethodOverridingAnnotationOnMethod.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"twentyeight\");\n\t}\n\n\t// gh-13490\n\t@Test\n\tvoid scanWhenClassInheritingInterfaceInheritingInterfaceMethodAnnotationThenResolves() throws Exception {\n\t\tMethod method = ClassInheritingInterfaceInheritingInterfaceMethodAnnotation.class.getDeclaredMethod(\"method\");\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(preAuthorize.value()).isEqualTo(\"three\");\n\t}\n\n\t// gh-15352\n\t@Test\n\tvoid scanWhenClassInheritingAbstractClassNoAnnotationsThenNoAnnotation() throws Exception {\n\t\tMethod method = ClassInheritingAbstractClassNoAnnotations.class.getMethod(\"otherMethod\");\n\t\tClass<?> targetClass = ClassInheritingAbstractClassNoAnnotations.class;\n\t\tPreAuthorize preAuthorize = this.scanner.scan(method, targetClass);\n\t\tassertThat(preAuthorize).isNull();\n\t}\n\n\t@Test\n\tvoid scanParameterAnnotationWhenAnnotationOnInterface() throws Exception {\n\t\tParameter parameter = UserService.class.getDeclaredMethod(\"add\", String.class).getParameters()[0];\n\t\tCustomParameterAnnotation customParameterAnnotation = this.parameterScanner.scan(parameter);\n\t\tassertThat(customParameterAnnotation.value()).isEqualTo(\"one\");\n\t}\n\n\t@Test\n\tvoid scanParameterAnnotationWhenClassInheritingInterfaceAnnotation() throws Exception {\n\t\tParameter parameter = UserServiceImpl.class.getDeclaredMethod(\"add\", String.class).getParameters()[0];\n\t\tCustomParameterAnnotation customParameterAnnotation = this.parameterScanner.scan(parameter);\n\t\tassertThat(customParameterAnnotation.value()).isEqualTo(\"one\");\n\t}\n\n\t@Test\n\tvoid scanParameterAnnotationWhenClassOverridingMethodOverridingInterface() throws Exception {\n\t\tParameter parameter = UserServiceImpl.class.getDeclaredMethod(\"get\", String.class).getParameters()[0];\n\t\tCustomParameterAnnotation customParameterAnnotation = this.parameterScanner.scan(parameter);\n\t\tassertThat(customParameterAnnotation.value()).isEqualTo(\"five\");\n\t}\n\n\t@Test\n\tvoid scanParameterAnnotationWhenMultipleMethodInheritanceThenException() throws Exception {\n\t\tParameter parameter = UserServiceImpl.class.getDeclaredMethod(\"list\", String.class).getParameters()[0];\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.parameterScanner.scan(parameter));\n\t}\n\n\t@Test\n\tvoid scanParameterAnnotationWhenInterfaceNoAnnotationsThenException() throws Exception {\n\t\tParameter parameter = UserServiceImpl.class.getDeclaredMethod(\"delete\", String.class).getParameters()[0];\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.parameterScanner.scan(parameter));\n\t}\n\n\t// gh-16751\n\t@Test\n\tvoid scanWhenAnnotationOnParameterizedInterfaceTheLocates() throws Exception {\n\t\tMethod method = MyServiceImpl.class.getDeclaredMethod(\"get\", String.class);\n\t\tPreAuthorize pre = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(pre).isNotNull();\n\t}\n\n\t// gh-16751\n\t@Test\n\tvoid scanWhenAnnotationOnParameterizedSuperClassThenLocates() throws Exception {\n\t\tMethod method = MyServiceImpl.class.getDeclaredMethod(\"getExt\", Long.class);\n\t\tPreAuthorize pre = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(pre).isNotNull();\n\t}\n\n\t// gh-16751\n\t@Test\n\tvoid scanWhenAnnotationOnParameterizedMethodThenLocates() throws Exception {\n\t\tMethod method = MyServiceImpl.class.getDeclaredMethod(\"getExtByClass\", Class.class, Long.class);\n\t\tPreAuthorize pre = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(pre).isNotNull();\n\t}\n\n\t@Test\n\tvoid scanParameterAnnotationWhenPresentInParentAndInterfaceThenException() throws Exception {\n\t\tParameter parameter = DefaultUserService.class.getDeclaredMethod(\"batch\", String[].class).getParameters()[0];\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class)\n\t\t\t.isThrownBy(() -> this.parameterScanner.scan(parameter));\n\t}\n\n\t// gh-17898\n\t@Test\n\tvoid scanWhenAnnotationOnParameterizedUndeclaredMethodAndThenLocates() throws Exception {\n\t\tMethod method = ClassUtils.getMethod(GenericInterfaceImpl.class, \"processOneAndTwo\", Long.class, Object.class);\n\t\tPreAuthorize pre = this.scanner.scan(method, method.getDeclaringClass());\n\t\tassertThat(pre).isNotNull();\n\t}\n\n\tinterface UserService {\n\n\t\tvoid add(@CustomParameterAnnotation(\"one\") String user);\n\n\t\tList<String> list(@CustomParameterAnnotation(\"two\") String user);\n\n\t\tString get(@CustomParameterAnnotation(\"three\") String user);\n\n\t\tvoid delete(@CustomParameterAnnotation(\"five\") String user);\n\n\t}\n\n\tinterface OtherUserService {\n\n\t\tList<String> list(@CustomParameterAnnotation(\"four\") String user);\n\n\t}\n\n\tinterface ThirdPartyUserService {\n\n\t\tvoid delete(@CustomParameterAnnotation(\"five\") String user);\n\n\t}\n\n\tinterface RemoteUserService extends ThirdPartyUserService {\n\n\t\tvoid batch(@CustomParameterAnnotation(\"six\") String... user);\n\n\t}\n\n\tstatic class UserServiceImpl implements UserService, OtherUserService, RemoteUserService {\n\n\t\t@Override\n\t\tpublic void add(String user) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic List<String> list(String user) {\n\t\t\treturn List.of(user);\n\t\t}\n\n\t\t@Override\n\t\tpublic String get(@CustomParameterAnnotation(\"five\") String user) {\n\t\t\treturn user;\n\t\t}\n\n\t\t@Override\n\t\tpublic void delete(String user) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void batch(@CustomParameterAnnotation(\"seven\") String... user) {\n\n\t\t}\n\n\t}\n\n\tstatic class DefaultUserService extends UserServiceImpl implements RemoteUserService {\n\n\t\t@Override\n\t\tpublic void batch(String... user) {\n\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface CustomParameterAnnotation {\n\n\t\tString value();\n\n\t}\n\n\t@PreAuthorize(\"one\")\n\tprivate interface AnnotationOnInterface {\n\n\t\tString method();\n\n\t}\n\n\t@PreAuthorize(\"two\")\n\tprivate interface AlsoAnnotationOnInterface {\n\n\t\tString method();\n\n\t}\n\n\tprivate interface AnnotationOnInterfaceMethod {\n\n\t\t@PreAuthorize(\"three\")\n\t\tString method();\n\n\t}\n\n\tprivate interface AlsoAnnotationOnInterfaceMethod {\n\n\t\t@PreAuthorize(\"four\")\n\t\tString method();\n\n\t}\n\n\t@PreAuthorize(\"five\")\n\tprivate static class AnnotationOnClass {\n\n\t\tString method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class AnnotationOnClassMethod {\n\n\t\t@PreAuthorize(\"six\")\n\t\tString method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"seven\")\n\tprivate interface InterfaceOverridingAnnotationOnInterface extends AnnotationOnInterface {\n\n\t}\n\n\tprivate interface InterfaceMethodOverridingAnnotationOnInterface extends AnnotationOnInterface {\n\n\t\t@PreAuthorize(\"eight\")\n\t\tString method();\n\n\t}\n\n\tprivate interface InterfaceAttemptingMultipleInterfaceInheritance\n\t\t\textends AnnotationOnInterface, AlsoAnnotationOnInterface {\n\n\t}\n\n\t@PreAuthorize(\"ten\")\n\tprivate interface InterfaceOverridingMultipleInterfaceInheritance\n\t\t\textends AnnotationOnInterface, AlsoAnnotationOnInterface {\n\n\t}\n\n\tprivate static class ClassInheritingInterfaceOverridingMultipleInterfaceInheritance\n\t\t\timplements InterfaceOverridingMultipleInterfaceInheritance {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate interface InterfaceMethodOverridingMultipleInterfaceInheritance\n\t\t\textends AnnotationOnInterface, AlsoAnnotationOnInterface {\n\n\t\t@PreAuthorize(\"eleven\")\n\t\tString method();\n\n\t}\n\n\tprivate static class ClassAttemptingMultipleInterfaceInheritance\n\t\t\timplements AnnotationOnInterface, AlsoAnnotationOnInterface {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"thirteen\")\n\tprivate static class ClassOverridingMultipleInterfaceInheritance\n\t\t\timplements AnnotationOnInterface, AlsoAnnotationOnInterface {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class ClassMethodOverridingMultipleInterfaceInheritance\n\t\t\timplements AnnotationOnInterface, AlsoAnnotationOnInterface {\n\n\t\t@Override\n\t\t@PreAuthorize(\"fourteen\")\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class ClassInheritingInterfaceOverridingInterfaceAnnotation\n\t\t\timplements InterfaceOverridingAnnotationOnInterface {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"sixteen\")\n\tprivate static class ClassOverridingGrandparentInterfaceAnnotation\n\t\t\timplements InterfaceOverridingAnnotationOnInterface {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class MethodOverridingGrandparentInterfaceAnnotation\n\t\t\timplements InterfaceOverridingAnnotationOnInterface {\n\n\t\t@Override\n\t\t@PreAuthorize(\"seventeen\")\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t} // unambiguously seventeen\n\n\t}\n\n\tprivate static class ClassInheritingMethodOverriddenAnnotation\n\t\t\timplements InterfaceMethodOverridingAnnotationOnInterface {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"nineteen\")\n\tprivate static class ClassOverridingMethodOverriddenAnnotation\n\t\t\timplements InterfaceMethodOverridingAnnotationOnInterface {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class MethodOverridingMethodOverriddenAnnotation\n\t\t\timplements InterfaceMethodOverridingAnnotationOnInterface {\n\n\t\t@Override\n\t\t@PreAuthorize(\"twenty\")\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class ClassInheritingMultipleInheritance implements InterfaceAttemptingMultipleInterfaceInheritance {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"twentytwo\")\n\tprivate static class ClassOverridingMultipleInheritance implements InterfaceAttemptingMultipleInterfaceInheritance {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class MethodOverridingMultipleInheritance\n\t\t\timplements InterfaceAttemptingMultipleInterfaceInheritance {\n\n\t\t@Override\n\t\t@PreAuthorize(\"twentythree\")\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class InheritingInterfaceAndMethodAnnotations\n\t\t\timplements AnnotationOnInterface, AnnotationOnInterfaceMethod {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"twentyfive\")\n\tprivate static class ClassOverridingInterfaceAndMethodInheritance\n\t\t\timplements AnnotationOnInterface, AnnotationOnInterfaceMethod {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class MethodOverridingInterfaceAndMethodInheritance\n\t\t\timplements AnnotationOnInterface, AnnotationOnInterfaceMethod {\n\n\t\t@Override\n\t\t@PreAuthorize(\"twentysix\")\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class MultipleMethodInheritance\n\t\t\timplements AnnotationOnInterfaceMethod, AlsoAnnotationOnInterfaceMethod {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate interface InterfaceInheritingInterfaceAnnotation extends AnnotationOnInterface {\n\n\t}\n\n\tprivate static class ClassInheritingInterfaceMethodAnnotation implements AnnotationOnInterfaceMethod {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class MethodInheritingMethodOverridingInterfaceAndMethodInheritance\n\t\t\textends MethodOverridingInterfaceAndMethodInheritance {\n\n\t}\n\n\t@PreAuthorize(\"twentyseven\")\n\tprivate static class ClassOverridingMethodOverridingInterfaceAndMethodInheritance\n\t\t\textends MethodOverridingInterfaceAndMethodInheritance {\n\n\t}\n\n\tprivate static class InterfaceInheritingAnnotationsAtDifferentLevels\n\t\t\timplements InterfaceInheritingInterfaceAnnotation, AlsoAnnotationOnInterface {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate static class ClassMethodOverridingAnnotationOnMethod implements AnnotationOnInterfaceMethod {\n\n\t\t@Override\n\t\t@PreAuthorize(\"twentyeight\")\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tprivate interface InterfaceInheritingInterfaceMethodAnnotation extends AnnotationOnInterfaceMethod {\n\n\t}\n\n\tprivate static class ClassInheritingInterfaceInheritingInterfaceMethodAnnotation\n\t\t\timplements InterfaceInheritingInterfaceMethodAnnotation {\n\n\t\t@Override\n\t\tpublic String method() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\tpublic abstract static class AbstractClassNoAnnotations {\n\n\t\tpublic String otherMethod() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n\t@PreAuthorize(\"twentynine\")\n\tprivate static class ClassInheritingAbstractClassNoAnnotations extends AbstractClassNoAnnotations {\n\n\t}\n\n\tinterface MyService<C, U> {\n\n\t\t@PreAuthorize(\"thirty\")\n\t\tC get(U u);\n\n\t}\n\n\tabstract static class MyServiceExt<T> implements MyService<Integer, String> {\n\n\t\t@PreAuthorize(\"thirtyone\")\n\t\tabstract T getExt(T t);\n\n\t\t@PreAuthorize(\"thirtytwo\")\n\t\tabstract <S extends Number> S getExtByClass(Class<S> clazz, T t);\n\n\t}\n\n\tstatic class MyServiceImpl extends MyServiceExt<Long> {\n\n\t\t@Override\n\t\tpublic Integer get(final String s) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tLong getExt(Long o) {\n\t\t\treturn 0L;\n\t\t}\n\n\t\t@Override\n\t\t<S extends Number> S getExtByClass(Class<S> clazz, Long l) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tinterface GenericInterface<A, B> {\n\n\t\t@PreAuthorize(\"hasAuthority('thirtythree')\")\n\t\tvoid processOneAndTwo(A value1, B value2);\n\n\t}\n\n\tabstract static class GenericAbstractSuperclass<C> implements GenericInterface<Long, C> {\n\n\t\t@Override\n\t\tpublic void processOneAndTwo(Long value1, C value2) {\n\t\t}\n\n\t}\n\n\tstatic class GenericInterfaceImpl extends GenericAbstractSuperclass<String> {\n\n\t\t// The compiler does not require us to declare a concrete\n\t\t// processOneAndTwo(Long, String) method, and we intentionally\n\t\t// do not declare one here.\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/authority/AuthorityUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n * @author Evgeniy Cheban\n */\npublic class AuthorityUtilsTests {\n\n\t@Test\n\tpublic void commaSeparatedStringIsParsedCorrectly() {\n\t\tList<GrantedAuthority> authorityArray = AuthorityUtils\n\t\t\t.commaSeparatedStringToAuthorityList(\" ROLE_A, B, C, ROLE_D\\n,\\n E \");\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(authorityArray);\n\t\tassertThat(authorities).contains(\"B\");\n\t\tassertThat(authorities).contains(\"C\");\n\t\tassertThat(authorities).contains(\"E\");\n\t\tassertThat(authorities).contains(\"ROLE_A\");\n\t\tassertThat(authorities).contains(\"ROLE_D\");\n\t}\n\n\t@Test\n\tpublic void createAuthorityList() {\n\t\tList<GrantedAuthority> authorities = AuthorityUtils\n\t\t\t.createAuthorityList(Arrays.asList(\"ROLE_A\", \"ROLE_B\", \"ROLE_C\"));\n\t\tassertThat(authorities).hasSize(3);\n\t\tassertThat(authorities).element(0).extracting(GrantedAuthority::getAuthority).isEqualTo(\"ROLE_A\");\n\t\tassertThat(authorities).element(1).extracting(GrantedAuthority::getAuthority).isEqualTo(\"ROLE_B\");\n\t\tassertThat(authorities).element(2).extracting(GrantedAuthority::getAuthority).isEqualTo(\"ROLE_C\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/authority/FactorGrantedAuthorityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority;\n\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link FactorGrantedAuthority}.\n *\n * @author Yoobin Yoon\n * @author Rob Winch\n */\npublic class FactorGrantedAuthorityTests {\n\n\t@Test\n\tpublic void buildWhenOnlyAuthorityThenDefaultsIssuedAtToNow() {\n\t\tInstant before = Instant.now();\n\n\t\tFactorGrantedAuthority authority = FactorGrantedAuthority.withAuthority(\"profile:read\").build();\n\n\t\tInstant after = Instant.now();\n\n\t\tassertThat(authority.getAuthority()).isEqualTo(\"profile:read\");\n\t\tassertThat(authority.getIssuedAt()).isBetween(before, after);\n\t}\n\n\t@Test\n\tpublic void buildWhenAllFieldsSetThenCreatesCorrectly() {\n\t\tInstant issuedAt = Instant.now();\n\n\t\tFactorGrantedAuthority authority = FactorGrantedAuthority.withAuthority(\"admin:write\")\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.build();\n\n\t\tassertThat(authority.getAuthority()).isEqualTo(\"admin:write\");\n\t\tassertThat(authority.getIssuedAt()).isEqualTo(issuedAt);\n\t}\n\n\t@Test\n\tpublic void buildWhenNullAuthorityThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> FactorGrantedAuthority.withAuthority(null))\n\t\t\t.withMessage(\"A granted authority textual representation is required\");\n\t}\n\n\t@Test\n\tpublic void buildWhenEmptyAuthorityThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> FactorGrantedAuthority.withAuthority(\"\"))\n\t\t\t.withMessage(\"A granted authority textual representation is required\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/authority/SimpleGrantedAuthorityTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link SimpleGrantedAuthority}.\n *\n * @author Ben Alex\n */\npublic class SimpleGrantedAuthorityTests {\n\n\t@Test\n\tpublic void equalsBehavesAsExpected() {\n\t\tSimpleGrantedAuthority auth1 = new SimpleGrantedAuthority(\"TEST\");\n\t\tassertThat(auth1).isEqualTo(auth1);\n\t\tassertThat(new SimpleGrantedAuthority(\"TEST\")).isEqualTo(auth1);\n\t\tassertThat(auth1.equals(\"TEST\")).isFalse();\n\t\tSimpleGrantedAuthority auth3 = new SimpleGrantedAuthority(\"NOT_EQUAL\");\n\t\tassertThat(!auth1.equals(auth3)).isTrue();\n\t\tassertThat(auth1.equals(mock(GrantedAuthority.class))).isFalse();\n\t\tassertThat(auth1.equals(222)).isFalse();\n\t}\n\n\t@Test\n\tpublic void toStringReturnsAuthorityValue() {\n\t\tSimpleGrantedAuthority auth = new SimpleGrantedAuthority(\"TEST\");\n\t\tassertThat(auth.toString()).isEqualTo(\"TEST\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/authority/mapping/MapBasedAttributes2GrantedAuthoritiesMapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Ruud Senden\n */\n@SuppressWarnings(\"unchecked\")\npublic class MapBasedAttributes2GrantedAuthoritiesMapperTests {\n\n\t@Test\n\tpublic void testAfterPropertiesSetEmptyMap() throws Exception {\n\t\tMapBasedAttributes2GrantedAuthoritiesMapper mapper = new MapBasedAttributes2GrantedAuthoritiesMapper();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> mapper.setAttributes2grantedAuthoritiesMap(new HashMap()));\n\t}\n\n\t@Test\n\tpublic void testAfterPropertiesSetInvalidKeyTypeMap() throws Exception {\n\t\tMapBasedAttributes2GrantedAuthoritiesMapper mapper = new MapBasedAttributes2GrantedAuthoritiesMapper();\n\t\tHashMap m = new HashMap();\n\t\tm.put(new Object(), \"ga1\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> mapper.setAttributes2grantedAuthoritiesMap(m));\n\t}\n\n\t@Test\n\tpublic void testAfterPropertiesSetInvalidValueTypeMap1() throws Exception {\n\t\tMapBasedAttributes2GrantedAuthoritiesMapper mapper = new MapBasedAttributes2GrantedAuthoritiesMapper();\n\t\tHashMap m = new HashMap();\n\t\tm.put(\"role1\", new Object());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> mapper.setAttributes2grantedAuthoritiesMap(m));\n\t}\n\n\t@Test\n\tpublic void testAfterPropertiesSetInvalidValueTypeMap2() throws Exception {\n\t\tMapBasedAttributes2GrantedAuthoritiesMapper mapper = new MapBasedAttributes2GrantedAuthoritiesMapper();\n\t\tHashMap m = new HashMap();\n\t\tm.put(\"role1\", new Object[] { new String[] { \"ga1\", \"ga2\" }, new Object() });\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> mapper.setAttributes2grantedAuthoritiesMap(m));\n\t}\n\n\t@Test\n\tpublic void testAfterPropertiesSetValidMap() throws Exception {\n\t\tMapBasedAttributes2GrantedAuthoritiesMapper mapper = new MapBasedAttributes2GrantedAuthoritiesMapper();\n\t\tHashMap m = getValidAttributes2GrantedAuthoritiesMap();\n\t\tmapper.setAttributes2grantedAuthoritiesMap(m);\n\t\tmapper.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void testMapping1() throws Exception {\n\t\tString[] roles = { \"role1\" };\n\t\tString[] expectedGas = { \"ga1\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping2() throws Exception {\n\t\tString[] roles = { \"role2\" };\n\t\tString[] expectedGas = { \"ga2\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping3() throws Exception {\n\t\tString[] roles = { \"role3\" };\n\t\tString[] expectedGas = { \"ga3\", \"ga4\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping4() throws Exception {\n\t\tString[] roles = { \"role4\" };\n\t\tString[] expectedGas = { \"ga5\", \"ga6\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping5() throws Exception {\n\t\tString[] roles = { \"role5\" };\n\t\tString[] expectedGas = { \"ga7\", \"ga8\", \"ga9\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping6() throws Exception {\n\t\tString[] roles = { \"role6\" };\n\t\tString[] expectedGas = { \"ga10\", \"ga11\", \"ga12\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping7() throws Exception {\n\t\tString[] roles = { \"role7\" };\n\t\tString[] expectedGas = { \"ga13\", \"ga14\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping8() throws Exception {\n\t\tString[] roles = { \"role8\" };\n\t\tString[] expectedGas = { \"ga13\", \"ga14\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping9() throws Exception {\n\t\tString[] roles = { \"role9\" };\n\t\tString[] expectedGas = {};\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping10() throws Exception {\n\t\tString[] roles = { \"role10\" };\n\t\tString[] expectedGas = {};\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMapping11() throws Exception {\n\t\tString[] roles = { \"role11\" };\n\t\tString[] expectedGas = {};\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testNonExistingMapping() throws Exception {\n\t\tString[] roles = { \"nonExisting\" };\n\t\tString[] expectedGas = {};\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic void testMappingCombination() throws Exception {\n\t\tString[] roles = { \"role1\", \"role2\", \"role3\", \"role4\", \"role5\", \"role6\", \"role7\", \"role8\", \"role9\", \"role10\",\n\t\t\t\t\"role11\" };\n\t\tString[] expectedGas = { \"ga1\", \"ga2\", \"ga3\", \"ga4\", \"ga5\", \"ga6\", \"ga7\", \"ga8\", \"ga9\", \"ga10\", \"ga11\", \"ga12\",\n\t\t\t\t\"ga13\", \"ga14\" };\n\t\ttestGetGrantedAuthorities(getDefaultMapper(), roles, expectedGas);\n\t}\n\n\tprivate HashMap getValidAttributes2GrantedAuthoritiesMap() {\n\t\tHashMap m = new HashMap();\n\t\tm.put(\"role1\", \"ga1\");\n\t\tm.put(\"role2\", new SimpleGrantedAuthority(\"ga2\"));\n\t\tm.put(\"role3\", Arrays.asList(\"ga3\", new SimpleGrantedAuthority(\"ga4\")));\n\t\tm.put(\"role4\", \"ga5,ga6\");\n\t\tm.put(\"role5\", Arrays.asList(\"ga7\", \"ga8\", new Object[] { new SimpleGrantedAuthority(\"ga9\") }));\n\t\tm.put(\"role6\", new Object[] { \"ga10\", \"ga11\", new Object[] { new SimpleGrantedAuthority(\"ga12\") } });\n\t\tm.put(\"role7\", new String[] { \"ga13\", \"ga14\" });\n\t\tm.put(\"role8\", new String[] { \"ga13\", \"ga14\", null });\n\t\tm.put(\"role9\", null);\n\t\tm.put(\"role10\", new Object[] {});\n\t\tm.put(\"role11\", Arrays.asList(new Object[] { null }));\n\t\treturn m;\n\t}\n\n\tprivate MapBasedAttributes2GrantedAuthoritiesMapper getDefaultMapper() throws Exception {\n\t\tMapBasedAttributes2GrantedAuthoritiesMapper mapper = new MapBasedAttributes2GrantedAuthoritiesMapper();\n\t\tmapper.setAttributes2grantedAuthoritiesMap(getValidAttributes2GrantedAuthoritiesMap());\n\t\tmapper.afterPropertiesSet();\n\t\treturn mapper;\n\t}\n\n\tprivate void testGetGrantedAuthorities(MapBasedAttributes2GrantedAuthoritiesMapper mapper, String[] roles,\n\t\t\tString[] expectedGas) {\n\t\tList<GrantedAuthority> result = mapper.getGrantedAuthorities(Arrays.asList(roles));\n\t\tCollection resultColl = new ArrayList(result.size());\n\t\tfor (GrantedAuthority auth : result) {\n\t\t\tresultColl.add(auth.getAuthority());\n\t\t}\n\t\tCollection expectedColl = Arrays.asList(expectedGas);\n\t\tassertThat(resultColl.containsAll(expectedColl))\n\t\t\t.withFailMessage(\"Role collections should match; result: \" + resultColl + \", expected: \" + expectedColl)\n\t\t\t.isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/authority/mapping/SimpleAuthoritiesMapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Luke Taylor\n */\npublic class SimpleAuthoritiesMapperTests {\n\n\t@Test\n\tpublic void rejectsInvalidCaseConversionFlags() {\n\t\tSimpleAuthorityMapper mapper = new SimpleAuthorityMapper();\n\t\tmapper.setConvertToLowerCase(true);\n\t\tmapper.setConvertToUpperCase(true);\n\t\tassertThatIllegalArgumentException().isThrownBy(mapper::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void defaultPrefixIsCorrectlyApplied() {\n\t\tSimpleAuthorityMapper mapper = new SimpleAuthorityMapper();\n\t\tSet<String> mapped = AuthorityUtils\n\t\t\t.authorityListToSet(mapper.mapAuthorities(AuthorityUtils.createAuthorityList(\"AaA\", \"ROLE_bbb\")));\n\t\tassertThat(mapped).contains(\"ROLE_AaA\");\n\t\tassertThat(mapped).contains(\"ROLE_bbb\");\n\t}\n\n\t@Test\n\tpublic void caseIsConvertedCorrectly() {\n\t\tSimpleAuthorityMapper mapper = new SimpleAuthorityMapper();\n\t\tmapper.setPrefix(\"\");\n\t\tList<GrantedAuthority> toMap = AuthorityUtils.createAuthorityList(\"AaA\", \"Bbb\");\n\t\tSet<String> mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap));\n\t\tassertThat(mapped).hasSize(2);\n\t\tassertThat(mapped).contains(\"AaA\");\n\t\tassertThat(mapped).contains(\"Bbb\");\n\t\tmapper.setConvertToLowerCase(true);\n\t\tmapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap));\n\t\tassertThat(mapped).hasSize(2);\n\t\tassertThat(mapped).contains(\"aaa\");\n\t\tassertThat(mapped).contains(\"bbb\");\n\t\tmapper.setConvertToLowerCase(false);\n\t\tmapper.setConvertToUpperCase(true);\n\t\tmapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap));\n\t\tassertThat(mapped).hasSize(2);\n\t\tassertThat(mapped).contains(\"AAA\");\n\t\tassertThat(mapped).contains(\"BBB\");\n\t}\n\n\t@Test\n\tpublic void duplicatesAreRemoved() {\n\t\tSimpleAuthorityMapper mapper = new SimpleAuthorityMapper();\n\t\tmapper.setConvertToUpperCase(true);\n\t\tSet<String> mapped = AuthorityUtils\n\t\t\t.authorityListToSet(mapper.mapAuthorities(AuthorityUtils.createAuthorityList(\"AaA\", \"AAA\")));\n\t\tassertThat(mapped).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void defaultAuthorityIsAssignedIfSet() {\n\t\tSimpleAuthorityMapper mapper = new SimpleAuthorityMapper();\n\t\tmapper.setDefaultAuthority(\"ROLE_USER\");\n\t\tSet<String> mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(AuthorityUtils.NO_AUTHORITIES));\n\t\tassertThat(mapped).hasSize(1);\n\t\tassertThat(mapped).contains(\"ROLE_USER\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/authority/mapping/SimpleMappableRolesRetrieverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author TSARDD\n * @since 18-okt-2007\n */\npublic class SimpleMappableRolesRetrieverTests {\n\n\t@Test\n\tpublic final void testGetSetMappableRoles() {\n\t\tSet<String> roles = StringUtils.commaDelimitedListToSet(\"Role1,Role2\");\n\t\tSimpleMappableAttributesRetriever r = new SimpleMappableAttributesRetriever();\n\t\tr.setMappableAttributes(roles);\n\t\tSet<String> result = r.getMappableAttributes();\n\t\tassertThat(roles.containsAll(result) && result.containsAll(roles))\n\t\t\t.withFailMessage(\"Role collections do not match; result: \" + result + \", expected: \" + roles)\n\t\t\t.isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/authority/mapping/SimpleRoles2GrantedAuthoritiesMapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.authority.mapping;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * @author TSARDD\n * @since 18-okt-2007\n */\npublic class SimpleRoles2GrantedAuthoritiesMapperTests {\n\n\t@Test\n\tpublic final void testAfterPropertiesSetConvertToUpperAndLowerCase() {\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = new SimpleAttributes2GrantedAuthoritiesMapper();\n\t\tmapper.setConvertAttributeToLowerCase(true);\n\t\tmapper.setConvertAttributeToUpperCase(true);\n\t\tassertThatIllegalArgumentException().isThrownBy(mapper::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic final void testAfterPropertiesSet() {\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = new SimpleAttributes2GrantedAuthoritiesMapper();\n\t\tassertThatNoException().isThrownBy(mapper::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic final void testGetGrantedAuthoritiesNoConversion() {\n\t\tString[] roles = { \"Role1\", \"Role2\" };\n\t\tString[] expectedGas = { \"Role1\", \"Role2\" };\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();\n\t\ttestGetGrantedAuthorities(mapper, roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic final void testGetGrantedAuthoritiesToUpperCase() {\n\t\tString[] roles = { \"Role1\", \"Role2\" };\n\t\tString[] expectedGas = { \"ROLE1\", \"ROLE2\" };\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();\n\t\tmapper.setConvertAttributeToUpperCase(true);\n\t\ttestGetGrantedAuthorities(mapper, roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic final void testGetGrantedAuthoritiesToLowerCase() {\n\t\tString[] roles = { \"Role1\", \"Role2\" };\n\t\tString[] expectedGas = { \"role1\", \"role2\" };\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();\n\t\tmapper.setConvertAttributeToLowerCase(true);\n\t\ttestGetGrantedAuthorities(mapper, roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic final void testGetGrantedAuthoritiesAddPrefixIfAlreadyExisting() {\n\t\tString[] roles = { \"Role1\", \"Role2\", \"ROLE_Role3\" };\n\t\tString[] expectedGas = { \"ROLE_Role1\", \"ROLE_Role2\", \"ROLE_ROLE_Role3\" };\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();\n\t\tmapper.setAddPrefixIfAlreadyExisting(true);\n\t\tmapper.setAttributePrefix(\"ROLE_\");\n\t\ttestGetGrantedAuthorities(mapper, roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic final void testGetGrantedAuthoritiesDontAddPrefixIfAlreadyExisting1() {\n\t\tString[] roles = { \"Role1\", \"Role2\", \"ROLE_Role3\" };\n\t\tString[] expectedGas = { \"ROLE_Role1\", \"ROLE_Role2\", \"ROLE_Role3\" };\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();\n\t\tmapper.setAddPrefixIfAlreadyExisting(false);\n\t\tmapper.setAttributePrefix(\"ROLE_\");\n\t\ttestGetGrantedAuthorities(mapper, roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic final void testGetGrantedAuthoritiesDontAddPrefixIfAlreadyExisting2() {\n\t\tString[] roles = { \"Role1\", \"Role2\", \"role_Role3\" };\n\t\tString[] expectedGas = { \"ROLE_Role1\", \"ROLE_Role2\", \"ROLE_role_Role3\" };\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();\n\t\tmapper.setAddPrefixIfAlreadyExisting(false);\n\t\tmapper.setAttributePrefix(\"ROLE_\");\n\t\ttestGetGrantedAuthorities(mapper, roles, expectedGas);\n\t}\n\n\t@Test\n\tpublic final void testGetGrantedAuthoritiesCombination1() {\n\t\tString[] roles = { \"Role1\", \"Role2\", \"role_Role3\" };\n\t\tString[] expectedGas = { \"ROLE_ROLE1\", \"ROLE_ROLE2\", \"ROLE_ROLE3\" };\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = getDefaultMapper();\n\t\tmapper.setAddPrefixIfAlreadyExisting(false);\n\t\tmapper.setConvertAttributeToUpperCase(true);\n\t\tmapper.setAttributePrefix(\"ROLE_\");\n\t\ttestGetGrantedAuthorities(mapper, roles, expectedGas);\n\t}\n\n\tprivate void testGetGrantedAuthorities(SimpleAttributes2GrantedAuthoritiesMapper mapper, String[] roles,\n\t\t\tString[] expectedGas) {\n\t\tList<GrantedAuthority> result = mapper.getGrantedAuthorities(Arrays.asList(roles));\n\t\tCollection<String> resultColl = new ArrayList<>(result.size());\n\t\tfor (GrantedAuthority grantedAuthority : result) {\n\t\t\tresultColl.add(grantedAuthority.getAuthority());\n\t\t}\n\t\tCollection<String> expectedColl = Arrays.asList(expectedGas);\n\t\tassertThat(expectedColl.containsAll(resultColl) && resultColl.containsAll(expectedColl))\n\t\t\t.withFailMessage(\"Role collections do not match; result: \" + resultColl + \", expected: \" + expectedColl)\n\t\t\t.isTrue();\n\t}\n\n\tprivate SimpleAttributes2GrantedAuthoritiesMapper getDefaultMapper() {\n\t\tSimpleAttributes2GrantedAuthoritiesMapper mapper = new SimpleAttributes2GrantedAuthoritiesMapper();\n\t\tmapper.setAttributePrefix(\"\");\n\t\tmapper.setConvertAttributeToLowerCase(false);\n\t\tmapper.setConvertAttributeToUpperCase(false);\n\t\tmapper.setAddPrefixIfAlreadyExisting(false);\n\t\treturn mapper;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/InheritableThreadLocalSecurityContextHolderStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nclass InheritableThreadLocalSecurityContextHolderStrategyTests {\n\n\tInheritableThreadLocalSecurityContextHolderStrategy strategy = new InheritableThreadLocalSecurityContextHolderStrategy();\n\n\t@AfterEach\n\tvoid clearContext() {\n\t\tthis.strategy.clearContext();\n\t}\n\n\t@Test\n\tvoid deferredNotInvoked() {\n\t\tSupplier<SecurityContext> deferredContext = mock(Supplier.class);\n\t\tthis.strategy.setDeferredContext(deferredContext);\n\t\tverifyNoInteractions(deferredContext);\n\t}\n\n\t@Test\n\tvoid deferredContext() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tSupplier<SecurityContext> deferredContext = () -> new SecurityContextImpl(authentication);\n\t\tthis.strategy.setDeferredContext(deferredContext);\n\t\tassertThat(this.strategy.getDeferredContext().get()).isEqualTo(deferredContext.get());\n\t\tassertThat(this.strategy.getContext()).isEqualTo(deferredContext.get());\n\t}\n\n\t@Test\n\tvoid deferredContextValidates() {\n\t\tthis.strategy.setDeferredContext(() -> null);\n\t\tSupplier<SecurityContext> deferredContext = this.strategy.getDeferredContext();\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> deferredContext.get());\n\t}\n\n\t@Test\n\tvoid context() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tSecurityContext context = new SecurityContextImpl(authentication);\n\t\tthis.strategy.setContext(context);\n\t\tassertThat(this.strategy.getContext()).isEqualTo(context);\n\t\tassertThat(this.strategy.getDeferredContext().get()).isEqualTo(context);\n\t}\n\n\t@Test\n\tvoid contextValidates() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.strategy.setContext(null));\n\t}\n\n\t@Test\n\tvoid getContextWhenEmptyThenReturnsSameInstance() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tthis.strategy.getContext().setAuthentication(authentication);\n\t\tassertThat(this.strategy.getContext().getAuthentication()).isEqualTo(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/ListeningSecurityContextHolderStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\npublic class ListeningSecurityContextHolderStrategyTests {\n\n\t@Test\n\tpublic void setContextWhenInvokedThenListenersAreNotified() {\n\t\tSecurityContextHolderStrategy delegate = spy(new MockSecurityContextHolderStrategy());\n\t\tSecurityContextChangedListener one = mock(SecurityContextChangedListener.class);\n\t\tSecurityContextChangedListener two = mock(SecurityContextChangedListener.class);\n\t\tSecurityContextHolderStrategy strategy = new ListeningSecurityContextHolderStrategy(delegate, one, two);\n\t\tgiven(delegate.createEmptyContext()).willReturn(new SecurityContextImpl());\n\t\tSecurityContext context = strategy.createEmptyContext();\n\t\tstrategy.setContext(context);\n\t\tstrategy.getContext();\n\t\tverify(one).securityContextChanged(any());\n\t\tverify(two).securityContextChanged(any());\n\t}\n\n\t@Test\n\tpublic void setContextWhenNoChangeToContextThenListenersAreNotNotified() {\n\t\tSecurityContextHolderStrategy delegate = mock(SecurityContextHolderStrategy.class);\n\t\tSecurityContextChangedListener listener = mock(SecurityContextChangedListener.class);\n\t\tSecurityContextHolderStrategy strategy = new ListeningSecurityContextHolderStrategy(delegate, listener);\n\t\tSecurityContext context = new SecurityContextImpl();\n\t\tgiven(delegate.getContext()).willReturn(context);\n\t\tstrategy.setContext(strategy.getContext());\n\t\tstrategy.getContext();\n\t\tverifyNoInteractions(listener);\n\t}\n\n\t@Test\n\tpublic void clearContextWhenNoGetContextThenContextIsNotRead() {\n\t\tSecurityContextHolderStrategy delegate = mock(SecurityContextHolderStrategy.class);\n\t\tSecurityContextChangedListener listener = mock(SecurityContextChangedListener.class);\n\t\tSecurityContextHolderStrategy strategy = new ListeningSecurityContextHolderStrategy(delegate, listener);\n\t\tSupplier<SecurityContext> context = mock(Supplier.class);\n\t\tArgumentCaptor<SecurityContextChangedEvent> event = ArgumentCaptor.forClass(SecurityContextChangedEvent.class);\n\t\tgiven(delegate.getDeferredContext()).willReturn(context);\n\t\tgiven(delegate.getContext()).willAnswer((invocation) -> context.get());\n\t\tstrategy.clearContext();\n\t\tverifyNoInteractions(context);\n\t\tverify(listener).securityContextChanged(event.capture());\n\t\tassertThat(event.getValue().isCleared()).isTrue();\n\t\tstrategy.getContext();\n\t\tverify(context).get();\n\t\tstrategy.clearContext();\n\t\tverifyNoMoreInteractions(context);\n\t}\n\n\t@Test\n\tpublic void getContextWhenCalledMultipleTimesThenEventPublishedOnce() {\n\t\tSecurityContextHolderStrategy delegate = new MockSecurityContextHolderStrategy();\n\t\tSecurityContextChangedListener listener = mock(SecurityContextChangedListener.class);\n\t\tSecurityContextHolderStrategy strategy = new ListeningSecurityContextHolderStrategy(delegate, listener);\n\t\tstrategy.setContext(new SecurityContextImpl());\n\t\tverifyNoInteractions(listener);\n\t\tstrategy.getContext();\n\t\tverify(listener).securityContextChanged(any());\n\t\tstrategy.getContext();\n\t\tverifyNoMoreInteractions(listener);\n\t}\n\n\t@Test\n\tpublic void setContextWhenCalledMultipleTimesThenPublishedEventsAlign() {\n\t\tSecurityContextHolderStrategy delegate = new MockSecurityContextHolderStrategy();\n\t\tSecurityContextChangedListener listener = mock(SecurityContextChangedListener.class);\n\t\tSecurityContextHolderStrategy strategy = new ListeningSecurityContextHolderStrategy(delegate, listener);\n\t\tSecurityContext one = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"pass\"));\n\t\tSecurityContext two = new SecurityContextImpl(new TestingAuthenticationToken(\"admin\", \"pass\"));\n\t\tArgumentCaptor<SecurityContextChangedEvent> event = ArgumentCaptor.forClass(SecurityContextChangedEvent.class);\n\t\tstrategy.setContext(one);\n\t\tstrategy.setContext(two);\n\t\tverifyNoInteractions(listener);\n\t\tstrategy.getContext();\n\t\tverify(listener).securityContextChanged(event.capture());\n\t\tassertThat(event.getValue().getOldContext()).isEqualTo(one);\n\t\tassertThat(event.getValue().getNewContext()).isEqualTo(two);\n\t\tstrategy.getContext();\n\t\tverifyNoMoreInteractions(listener);\n\t\tstrategy.setContext(one);\n\t\tverifyNoMoreInteractions(listener);\n\t\treset(listener);\n\t\tstrategy.getContext();\n\t\tverify(listener).securityContextChanged(event.capture());\n\t\tassertThat(event.getValue().getOldContext()).isEqualTo(two);\n\t\tassertThat(event.getValue().getNewContext()).isEqualTo(one);\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullDelegateThenIllegalArgument() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(\n\t\t\t\t() -> new ListeningSecurityContextHolderStrategy((SecurityContextHolderStrategy) null, (event) -> {\n\t\t\t\t}));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullListenerThenIllegalArgument() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new ListeningSecurityContextHolderStrategy(new ThreadLocalSecurityContextHolderStrategy(),\n\t\t\t\t\t(SecurityContextChangedListener) null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/MockSecurityContextHolderStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\npublic class MockSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n\tprivate Supplier<SecurityContext> context = () -> null;\n\n\t@Override\n\tpublic void clearContext() {\n\t\tthis.context = () -> null;\n\t}\n\n\t@Override\n\tpublic SecurityContext getContext() {\n\t\treturn this.context.get();\n\t}\n\n\t@Override\n\tpublic Supplier<SecurityContext> getDeferredContext() {\n\t\treturn this.context;\n\t}\n\n\t@Override\n\tpublic void setContext(SecurityContext context) {\n\t\tthis.context = () -> context;\n\t}\n\n\t@Override\n\tpublic void setDeferredContext(Supplier<SecurityContext> deferredContext) {\n\t\tthis.context = deferredContext;\n\t}\n\n\t@Override\n\tpublic SecurityContext createEmptyContext() {\n\t\treturn new SecurityContextImpl();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/ObservationSecurityContextChangedListenerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link ObservationSecurityContextChangedListener}\n */\npublic class ObservationSecurityContextChangedListenerTests {\n\n\tprivate SecurityContext one = new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"pass\"));\n\n\tprivate SecurityContext two = new SecurityContextImpl(new TestingAuthenticationToken(\"admin\", \"pass\"));\n\n\tprivate ObservationRegistry observationRegistry;\n\n\tprivate ObservationSecurityContextChangedListener tested;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.observationRegistry = mock(ObservationRegistry.class);\n\t\tthis.tested = new ObservationSecurityContextChangedListener(this.observationRegistry);\n\t}\n\n\t@Test\n\tvoid securityContextChangedWhenNoObservationThenNoEvents() {\n\t\tgiven(this.observationRegistry.getCurrentObservation()).willReturn(null);\n\t\tthis.tested.securityContextChanged(new SecurityContextChangedEvent(this.one, this.two));\n\t}\n\n\t@Test\n\tvoid securityContextChangedWhenClearedEventThenAddsClearEventToObservation() {\n\t\tObservation observation = mock(Observation.class);\n\t\tgiven(this.observationRegistry.getCurrentObservation()).willReturn(observation);\n\t\tSupplier<SecurityContext> one = mock(Supplier.class);\n\t\tthis.tested\n\t\t\t.securityContextChanged(new SecurityContextChangedEvent(one, SecurityContextChangedEvent.NO_CONTEXT));\n\t\tArgumentCaptor<Observation.Event> event = ArgumentCaptor.forClass(Observation.Event.class);\n\t\tverify(observation).event(event.capture());\n\t\tassertThat(event.getValue().getName())\n\t\t\t.isEqualTo(ObservationSecurityContextChangedListener.SECURITY_CONTEXT_CLEARED);\n\t\tverifyNoInteractions(one);\n\t}\n\n\t@Test\n\tvoid securityContextChangedWhenNoChangeThenNoEventAddedToObservation() {\n\t\tObservation observation = mock(Observation.class);\n\t\tgiven(this.observationRegistry.getCurrentObservation()).willReturn(observation);\n\t\tthis.tested.securityContextChanged(new SecurityContextChangedEvent(this.one, this.one));\n\t\tverifyNoInteractions(observation);\n\t}\n\n\t@Test\n\tvoid securityContextChangedWhenChangedEventThenAddsChangeEventToObservation() {\n\t\tObservation observation = mock(Observation.class);\n\t\tgiven(this.observationRegistry.getCurrentObservation()).willReturn(observation);\n\t\tthis.tested.securityContextChanged(new SecurityContextChangedEvent(this.one, this.two));\n\t\tArgumentCaptor<Observation.Event> event = ArgumentCaptor.forClass(Observation.Event.class);\n\t\tverify(observation).event(event.capture());\n\t\tassertThat(event.getValue().getName())\n\t\t\t.isEqualTo(ObservationSecurityContextChangedListener.SECURITY_CONTEXT_CHANGED);\n\t}\n\n\t@Test\n\tvoid securityContextChangedWhenCreatedEventThenAddsCreatedEventToObservation() {\n\t\tObservation observation = mock(Observation.class);\n\t\tgiven(this.observationRegistry.getCurrentObservation()).willReturn(observation);\n\t\tthis.tested.securityContextChanged(new SecurityContextChangedEvent(null, this.one));\n\t\tArgumentCaptor<Observation.Event> event = ArgumentCaptor.forClass(Observation.Event.class);\n\t\tverify(observation).event(event.capture());\n\t\tassertThat(event.getValue().getName())\n\t\t\t.isEqualTo(ObservationSecurityContextChangedListener.SECURITY_CONTEXT_CREATED);\n\t}\n\n\t@Test\n\tvoid securityContextChangedWhenClearedEventThenAddsClearedEventToObservation() {\n\t\tObservation observation = mock(Observation.class);\n\t\tgiven(this.observationRegistry.getCurrentObservation()).willReturn(observation);\n\t\tthis.tested.securityContextChanged(new SecurityContextChangedEvent(this.one, new SecurityContextImpl()));\n\t\tArgumentCaptor<Observation.Event> event = ArgumentCaptor.forClass(Observation.Event.class);\n\t\tverify(observation).event(event.capture());\n\t\tassertThat(event.getValue().getName())\n\t\t\t.isEqualTo(ObservationSecurityContextChangedListener.SECURITY_CONTEXT_CLEARED);\n\t}\n\n\t@Test\n\tvoid securityContextChangedWhenBothNullThenNoNullPointerException() {\n\t\tObservation observation = mock(Observation.class);\n\t\tgiven(this.observationRegistry.getCurrentObservation()).willReturn(observation);\n\t\tthis.tested.securityContextChanged(\n\t\t\t\tnew SecurityContextChangedEvent(new SecurityContextImpl(), new SecurityContextImpl()));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/ReactiveSecurityContextHolderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class ReactiveSecurityContextHolderTests {\n\n\t@Test\n\tpublic void getContextWhenEmpty() {\n\t\tMono<SecurityContext> context = ReactiveSecurityContextHolder.getContext();\n\t\t// @formatter:off\n\t\tStepVerifier.create(context)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setContextAndGetContextThenEmitsContext() {\n\t\tSecurityContext expectedContext = new SecurityContextImpl(\n\t\t\t\tnew TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMono<SecurityContext> context = Mono.deferContextual(Mono::just)\n\t\t\t.flatMap((c) -> ReactiveSecurityContextHolder.getContext())\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(expectedContext)));\n\t\t// @formatter:off\n\t\tStepVerifier.create(context)\n\t\t\t\t.expectNext(expectedContext)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void demo() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\t// @formatter:off\n\t\tMono<String> messageByUsername = ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.map(Authentication::getName)\n\t\t\t\t.flatMap(this::findMessageByUsername)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));\n\t\tStepVerifier.create(messageByUsername)\n\t\t\t\t.expectNext(\"Hi user\")\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<String> findMessageByUsername(String username) {\n\t\treturn Mono.just(\"Hi \" + username);\n\t}\n\n\t@Test\n\tpublic void setContextAndClearAndGetContextThenEmitsEmpty() {\n\t\tSecurityContext expectedContext = new SecurityContextImpl(\n\t\t\t\tnew TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\t// @formatter:off\n\t\tMono<SecurityContext> context = Mono.deferContextual(Mono::just)\n\t\t\t\t.flatMap((c) -> ReactiveSecurityContextHolder.getContext())\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.clearContext())\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(expectedContext)));\n\t\tStepVerifier.create(context)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationAndGetContextThenEmitsContext() {\n\t\tAuthentication expectedAuthentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\t// @formatter:off\n\t\tMono<Authentication> authentication = Mono.deferContextual(Mono::just)\n\t\t\t\t.flatMap((c) -> ReactiveSecurityContextHolder.getContext())\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(expectedAuthentication));\n\t\tStepVerifier.create(authentication)\n\t\t\t\t.expectNext(expectedAuthentication)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getContextWhenThreadFactoryIsPlatformThenPropagated() {\n\t\tverifySecurityContextIsPropagated(Executors.defaultThreadFactory());\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void getContextWhenThreadFactoryIsVirtualThenPropagated() {\n\t\tverifySecurityContextIsPropagated(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t}\n\n\tprivate static void verifySecurityContextIsPropagated(ThreadFactory threadFactory) {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", null);\n\n\t\t// @formatter:off\n\t\tMono<Authentication> publisher = ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.contextWrite((context) -> ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.subscribeOn(Schedulers.newSingle(threadFactory));\n\t\t// @formatter:on\n\n\t\tStepVerifier.create(publisher).expectNext(authentication).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void clearContextWhenThreadFactoryIsPlatformThenCleared() {\n\t\tverifySecurityContextIsCleared(Executors.defaultThreadFactory());\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void clearContextWhenThreadFactoryIsVirtualThenCleared() {\n\t\tverifySecurityContextIsCleared(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t}\n\n\tprivate static void verifySecurityContextIsCleared(ThreadFactory threadFactory) {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", null);\n\n\t\t// @formatter:off\n\t\tMono<Authentication> publisher = ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.clearContext())\n\t\t\t\t.contextWrite((context) -> ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.subscribeOn(Schedulers.newSingle(threadFactory));\n\t\t// @formatter:on\n\n\t\tStepVerifier.create(publisher).verifyComplete();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/ReactiveSecurityContextHolderThreadLocalAccessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.concurrent.CountDownLatch;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.task.SimpleAsyncTaskExecutor;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ReactiveSecurityContextHolderThreadLocalAccessor}.\n *\n * @author Steve Riesenberg\n */\npublic class ReactiveSecurityContextHolderThreadLocalAccessorTests {\n\n\tprivate ReactiveSecurityContextHolderThreadLocalAccessor threadLocalAccessor;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.threadLocalAccessor = new ReactiveSecurityContextHolderThreadLocalAccessor();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.threadLocalAccessor.setValue();\n\t}\n\n\t@Test\n\tpublic void keyAlwaysReturnsSecurityContextClass() {\n\t\tassertThat(this.threadLocalAccessor.key()).isEqualTo(SecurityContext.class);\n\t}\n\n\t@Test\n\tpublic void getValueWhenThreadLocalNotSetThenReturnsNull() {\n\t\tassertThat(this.threadLocalAccessor.getValue()).isNull();\n\t}\n\n\t@Test\n\tpublic void getValueWhenThreadLocalSetThenReturnsSecurityContextMono() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\tMono<SecurityContext> mono = Mono.just(securityContext);\n\t\tthis.threadLocalAccessor.setValue(mono);\n\n\t\tassertThat(this.threadLocalAccessor.getValue()).isSameAs(mono);\n\t}\n\n\t@Test\n\tpublic void getValueWhenThreadLocalSetOnAnotherThreadThenReturnsNull() throws InterruptedException {\n\t\tCountDownLatch threadLocalSet = new CountDownLatch(1);\n\t\tCountDownLatch threadLocalRead = new CountDownLatch(1);\n\t\tCountDownLatch threadLocalCleared = new CountDownLatch(1);\n\n\t\tRunnable task = () -> {\n\t\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\t\tMono<SecurityContext> mono = Mono.just(securityContext);\n\t\t\tthis.threadLocalAccessor.setValue(mono);\n\t\t\tthreadLocalSet.countDown();\n\t\t\ttry {\n\t\t\t\tthreadLocalRead.await();\n\t\t\t}\n\t\t\tcatch (InterruptedException ignored) {\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tthis.threadLocalAccessor.setValue();\n\t\t\t\tthreadLocalCleared.countDown();\n\t\t\t}\n\t\t};\n\t\ttry (SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor()) {\n\t\t\ttaskExecutor.execute(task);\n\t\t\tthreadLocalSet.await();\n\t\t\tassertThat(this.threadLocalAccessor.getValue()).isNull();\n\t\t\tthreadLocalRead.countDown();\n\t\t\tthreadLocalCleared.await();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void setValueWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.threadLocalAccessor.setValue(null))\n\t\t\t.withMessage(\"securityContext cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setValueWhenThreadLocalSetThenClearsThreadLocal() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\tMono<SecurityContext> mono = Mono.just(securityContext);\n\t\tthis.threadLocalAccessor.setValue(mono);\n\t\tassertThat(this.threadLocalAccessor.getValue()).isSameAs(mono);\n\n\t\tthis.threadLocalAccessor.setValue();\n\t\tassertThat(this.threadLocalAccessor.getValue()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/SecurityContextHolderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link SecurityContextHolder}.\n *\n * @author Ben Alex\n */\npublic class SecurityContextHolderTests {\n\n\t@BeforeEach\n\tpublic final void setUp() {\n\t\tSecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);\n\t}\n\n\t@Test\n\tpublic void testContextHolderGetterSetterClearer() {\n\t\tSecurityContext sc = new SecurityContextImpl();\n\t\tsc.setAuthentication(UsernamePasswordAuthenticationToken.unauthenticated(\"Foobar\", \"pass\"));\n\t\tSecurityContextHolder.setContext(sc);\n\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(sc);\n\t\tSecurityContextHolder.clearContext();\n\t\tassertThat(SecurityContextHolder.getContext()).isNotSameAs(sc);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testNeverReturnsNull() {\n\t\tassertThat(SecurityContextHolder.getContext()).isNotNull();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testRejectsNulls() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> SecurityContextHolder.setContext(null));\n\t}\n\n\t@Test\n\tpublic void setContextHolderStrategyWhenCalledThenUsed() {\n\t\tSecurityContextHolderStrategy original = SecurityContextHolder.getContextHolderStrategy();\n\t\ttry {\n\t\t\tSecurityContextHolderStrategy delegate = mock(SecurityContextHolderStrategy.class);\n\t\t\tSecurityContextHolder.setContextHolderStrategy(delegate);\n\t\t\tSecurityContextHolder.getContext();\n\t\t\tverify(delegate).getContext();\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContextHolder.setContextHolderStrategy(original);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/SecurityContextHolderThreadLocalAccessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link SecurityContextHolderThreadLocalAccessor}.\n *\n * @author Steve Riesenberg\n */\npublic class SecurityContextHolderThreadLocalAccessorTests {\n\n\tprivate SecurityContextHolderThreadLocalAccessor threadLocalAccessor;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.threadLocalAccessor = new SecurityContextHolderThreadLocalAccessor();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.threadLocalAccessor.setValue();\n\t}\n\n\t@Test\n\tpublic void keyAlwaysReturnsSecurityContextClassName() {\n\t\tassertThat(this.threadLocalAccessor.key()).isEqualTo(SecurityContext.class.getName());\n\t}\n\n\t@Test\n\tpublic void getValueWhenSecurityContextHolderNotSetThenReturnsNull() {\n\t\tassertThat(this.threadLocalAccessor.getValue()).isNull();\n\t}\n\n\t@Test\n\tpublic void getValueWhenSecurityContextHolderSetThenReturnsSecurityContext() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tassertThat(this.threadLocalAccessor.getValue()).isSameAs(securityContext);\n\t}\n\n\t@Test\n\tpublic void setValueWhenSecurityContextThenSetsSecurityContextHolder() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\tthis.threadLocalAccessor.setValue(securityContext);\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(securityContext);\n\t}\n\n\t@Test\n\tpublic void setValueWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.threadLocalAccessor.setValue(null))\n\t\t\t.withMessage(\"securityContext cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setValueWhenSecurityContextSetThenClearsSecurityContextHolder() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tthis.threadLocalAccessor.setValue();\n\n\t\tSecurityContext emptyContext = SecurityContextHolder.createEmptyContext();\n\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(emptyContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/SecurityContextImplTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link SecurityContextImpl}.\n *\n * @author Ben Alex\n */\npublic class SecurityContextImplTests {\n\n\t@Test\n\tpublic void testEmptyObjectsAreEquals() {\n\t\tSecurityContextImpl obj1 = new SecurityContextImpl();\n\t\tSecurityContextImpl obj2 = new SecurityContextImpl();\n\t\tassertThat(obj1.equals(obj2)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testSecurityContextCorrectOperation() {\n\t\tSecurityContext context = new SecurityContextImpl();\n\t\tAuthentication auth = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\", \"koala\");\n\t\tcontext.setAuthentication(auth);\n\t\tassertThat(context.getAuthentication()).isEqualTo(auth);\n\t\tassertThat(context.toString().lastIndexOf(\"rod\") != -1).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/context/ThreadLocalSecurityContextHolderStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.context;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nclass ThreadLocalSecurityContextHolderStrategyTests {\n\n\tThreadLocalSecurityContextHolderStrategy strategy = new ThreadLocalSecurityContextHolderStrategy();\n\n\t@AfterEach\n\tvoid clearContext() {\n\t\tthis.strategy.clearContext();\n\t}\n\n\t@Test\n\tvoid deferredNotInvoked() {\n\t\tSupplier<SecurityContext> deferredContext = mock(Supplier.class);\n\t\tthis.strategy.setDeferredContext(deferredContext);\n\t\tverifyNoInteractions(deferredContext);\n\t}\n\n\t@Test\n\tvoid deferredContext() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tSupplier<SecurityContext> deferredContext = () -> new SecurityContextImpl(authentication);\n\t\tthis.strategy.setDeferredContext(deferredContext);\n\t\tassertThat(this.strategy.getDeferredContext().get()).isEqualTo(deferredContext.get());\n\t\tassertThat(this.strategy.getContext()).isEqualTo(deferredContext.get());\n\t}\n\n\t@Test\n\tvoid deferredContextValidates() {\n\t\tthis.strategy.setDeferredContext(() -> null);\n\t\tSupplier<SecurityContext> deferredContext = this.strategy.getDeferredContext();\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(deferredContext::get);\n\t}\n\n\t@Test\n\tvoid context() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tSecurityContext context = new SecurityContextImpl(authentication);\n\t\tthis.strategy.setContext(context);\n\t\tassertThat(this.strategy.getContext()).isEqualTo(context);\n\t\tassertThat(this.strategy.getDeferredContext().get()).isEqualTo(context);\n\t}\n\n\t@Test\n\tvoid contextValidates() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.strategy.setContext(null));\n\t}\n\n\t@Test\n\tvoid getContextWhenEmptyThenReturnsSameInstance() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tthis.strategy.getContext().setAuthentication(authentication);\n\t\tassertThat(this.strategy.getContext().getAuthentication()).isEqualTo(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/parameters/AnnotationParameterNameDiscovererTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.parameters;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class AnnotationParameterNameDiscovererTests {\n\n\tprivate AnnotationParameterNameDiscoverer discoverer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.discoverer = new AnnotationParameterNameDiscoverer(P.class.getName());\n\t}\n\n\t@Test\n\tpublic void getParameterNamesInterfaceSingleParam() {\n\t\tassertThat(this.discoverer\n\t\t\t.getParameterNames(ReflectionUtils.findMethod(Dao.class, \"findMessageByTo\", String.class)))\n\t\t\t.isEqualTo(new String[] { \"to\" });\n\t}\n\n\t@Test\n\tpublic void getParameterNamesInterfaceSingleParamAnnotatedWithMultiParams() {\n\t\tassertThat(this.discoverer.getParameterNames(\n\t\t\t\tReflectionUtils.findMethod(Dao.class, \"findMessageByToAndFrom\", String.class, String.class)))\n\t\t\t.isEqualTo(new String[] { \"to\", null });\n\t}\n\n\t@Test\n\tpublic void getParameterNamesInterfaceNoAnnotation() {\n\t\tassertThat(this.discoverer\n\t\t\t.getParameterNames(ReflectionUtils.findMethod(Dao.class, \"findMessageByIdNoAnnotation\", String.class)))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void getParameterNamesClassSingleParam() {\n\t\tassertThat(this.discoverer\n\t\t\t.getParameterNames(ReflectionUtils.findMethod(Dao.class, \"findMessageByTo\", String.class)))\n\t\t\t.isEqualTo(new String[] { \"to\" });\n\t}\n\n\t@Test\n\tpublic void getParameterNamesClassSingleParamAnnotatedWithMultiParams() {\n\t\tassertThat(this.discoverer.getParameterNames(\n\t\t\t\tReflectionUtils.findMethod(Dao.class, \"findMessageByToAndFrom\", String.class, String.class)))\n\t\t\t.isEqualTo(new String[] { \"to\", null });\n\t}\n\n\t@Test\n\tpublic void getParameterNamesClassNoAnnotation() {\n\t\tassertThat(this.discoverer\n\t\t\t.getParameterNames(ReflectionUtils.findMethod(Dao.class, \"findMessageByIdNoAnnotation\", String.class)))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void getParameterNamesConstructor() throws Exception {\n\t\tassertThat(this.discoverer.getParameterNames(Impl.class.getDeclaredConstructor(String.class)))\n\t\t\t.isEqualTo(new String[] { \"id\" });\n\t}\n\n\t@Test\n\tpublic void getParameterNamesConstructorNoAnnotation() throws Exception {\n\t\tassertThat(this.discoverer.getParameterNames(Impl.class.getDeclaredConstructor(Long.class))).isNull();\n\t}\n\n\t@Test\n\tpublic void getParameterNamesClassAnnotationOnInterface() {\n\t\tassertThat(this.discoverer\n\t\t\t.getParameterNames(ReflectionUtils.findMethod(DaoImpl.class, \"findMessageByTo\", String.class)))\n\t\t\t.isEqualTo(new String[] { \"to\" });\n\t\tassertThat(this.discoverer\n\t\t\t.getParameterNames(ReflectionUtils.findMethod(Dao.class, \"findMessageByTo\", String.class)))\n\t\t\t.isEqualTo(new String[] { \"to\" });\n\t}\n\n\t@Test\n\tpublic void getParameterNamesClassAnnotationOnImpl() {\n\t\tassertThat(this.discoverer.getParameterNames(\n\t\t\t\tReflectionUtils.findMethod(Dao.class, \"findMessageByToAndFrom\", String.class, String.class)))\n\t\t\t.isEqualTo(new String[] { \"to\", null });\n\t\tassertThat(this.discoverer.getParameterNames(\n\t\t\t\tReflectionUtils.findMethod(DaoImpl.class, \"findMessageByToAndFrom\", String.class, String.class)))\n\t\t\t.isEqualTo(new String[] { \"to\", \"from\" });\n\t}\n\n\t@Test\n\tpublic void getParameterNamesClassAnnotationOnBaseClass() {\n\t\tassertThat(this.discoverer\n\t\t\t.getParameterNames(ReflectionUtils.findMethod(Dao.class, \"findMessageByIdNoAnnotation\", String.class)))\n\t\t\t.isNull();\n\t\tassertThat(this.discoverer\n\t\t\t.getParameterNames(ReflectionUtils.findMethod(DaoImpl.class, \"findMessageByIdNoAnnotation\", String.class)))\n\t\t\t.isEqualTo(new String[] { \"id\" });\n\t}\n\n\tinterface Dao {\n\n\t\tString findMessageByTo(@P(\"to\") String to);\n\n\t\tString findMessageByToAndFrom(@P(\"to\") String to, String from);\n\n\t\tString findMessageByIdNoAnnotation(String id);\n\n\t}\n\n\tstatic class BaseDaoImpl {\n\n\t\tpublic String findMessageByIdNoAnnotation(@P(\"id\") String id) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tstatic class DaoImpl extends BaseDaoImpl implements Dao {\n\n\t\t@Override\n\t\tpublic String findMessageByTo(String to) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String findMessageByToAndFrom(@P(\"to\") String to, @P(\"from\") String from) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tstatic class Impl {\n\n\t\tImpl(Long dataSourceId) {\n\t\t}\n\n\t\tImpl(@P(\"id\") String dataSourceId) {\n\t\t}\n\n\t\tString findMessageByTo(@P(\"to\") String to) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString findMessageByToAndFrom(@P(\"to\") String to, String from) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString findMessageByIdNoAnnotation(String id) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscovererTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.parameters;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.DefaultParameterNameDiscoverer;\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.core.StandardReflectionParameterNameDiscoverer;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 3.2\n */\n@SuppressWarnings(\"unchecked\")\npublic class DefaultSecurityParameterNameDiscovererTests {\n\n\tprivate DefaultSecurityParameterNameDiscoverer discoverer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.discoverer = new DefaultSecurityParameterNameDiscoverer();\n\t}\n\n\t@Test\n\tpublic void constructorDefault() {\n\t\tList<ParameterNameDiscoverer> discoverers = (List<ParameterNameDiscoverer>) ReflectionTestUtils\n\t\t\t.getField(this.discoverer, \"parameterNameDiscoverers\");\n\t\tassertThat(discoverers).hasSize(2);\n\t\tParameterNameDiscoverer annotationDisc = discoverers.get(0);\n\t\tassertThat(annotationDisc).isInstanceOf(AnnotationParameterNameDiscoverer.class);\n\t\tSet<String> annotationsToUse = (Set<String>) ReflectionTestUtils.getField(annotationDisc,\n\t\t\t\t\"annotationClassesToUse\");\n\t\tassertThat(annotationsToUse).containsOnly(\"org.springframework.security.access.method.P\", P.class.getName());\n\t\tassertThat(discoverers.get(1).getClass()).isEqualTo(DefaultParameterNameDiscoverer.class);\n\t}\n\n\t@Test\n\tpublic void constructorDiscoverers() {\n\t\tthis.discoverer = new DefaultSecurityParameterNameDiscoverer(\n\t\t\t\tArrays.asList(new StandardReflectionParameterNameDiscoverer()));\n\t\tList<ParameterNameDiscoverer> discoverers = (List<ParameterNameDiscoverer>) ReflectionTestUtils\n\t\t\t.getField(this.discoverer, \"parameterNameDiscoverers\");\n\t\tassertThat(discoverers).hasSize(3);\n\t\tassertThat(discoverers.get(0)).isInstanceOf(StandardReflectionParameterNameDiscoverer.class);\n\t\tParameterNameDiscoverer annotationDisc = discoverers.get(1);\n\t\tassertThat(annotationDisc).isInstanceOf(AnnotationParameterNameDiscoverer.class);\n\t\tSet<String> annotationsToUse = (Set<String>) ReflectionTestUtils.getField(annotationDisc,\n\t\t\t\t\"annotationClassesToUse\");\n\t\tassertThat(annotationsToUse).containsOnly(\"org.springframework.security.access.method.P\", P.class.getName());\n\t\tassertThat(discoverers.get(2)).isInstanceOf(DefaultParameterNameDiscoverer.class);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.util.Date;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link SessionInformation}.\n *\n * @author Ben Alex\n */\npublic class SessionInformationTests {\n\n\t@Test\n\tpublic void testObject() throws Exception {\n\t\tObject principal = \"Some principal object\";\n\t\tString sessionId = \"1234567890\";\n\t\tDate currentDate = new Date();\n\t\tSessionInformation info = new SessionInformation(principal, sessionId, currentDate);\n\t\tassertThat(info.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(info.getSessionId()).isEqualTo(sessionId);\n\t\tassertThat(info.getLastRequest()).isEqualTo(currentDate);\n\t\tThread.sleep(10);\n\t\tinfo.refreshLastRequest();\n\t\tassertThat(info.getLastRequest().after(currentDate)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.session;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.context.SecurityContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link SessionRegistryImpl}.\n *\n * @author Ben Alex\n */\npublic class SessionRegistryImplTests {\n\n\tprivate SessionRegistryImpl sessionRegistry;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.sessionRegistry = new SessionRegistryImpl();\n\t}\n\n\t@Test\n\tpublic void sessionDestroyedEventRemovesSessionFromRegistry() {\n\t\tObject principal = \"Some principal object\";\n\t\tfinal String sessionId = \"zzzz\";\n\t\t// Register new Session\n\t\tthis.sessionRegistry.registerNewSession(sessionId, principal);\n\t\t// De-register session via an ApplicationEvent\n\t\tthis.sessionRegistry.onApplicationEvent(new SessionDestroyedEvent(\"\") {\n\t\t\t@Override\n\t\t\tpublic String getId() {\n\t\t\t\treturn sessionId;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic List<SecurityContext> getSecurityContexts() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\t\t// Check attempts to retrieve cleared session return null\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId)).isNull();\n\t}\n\n\t@Test\n\tpublic void sessionIdChangedEventRemovesOldSessionAndAddsANewSession() {\n\t\tObject principal = \"Some principal object\";\n\t\tfinal String sessionId = \"zzzz\";\n\t\tfinal String newSessionId = \"123\";\n\t\t// Register new Session\n\t\tthis.sessionRegistry.registerNewSession(sessionId, principal);\n\t\t// De-register session via an ApplicationEvent\n\t\tthis.sessionRegistry.onApplicationEvent(new SessionIdChangedEvent(\"\") {\n\t\t\t@Override\n\t\t\tpublic String getOldSessionId() {\n\t\t\t\treturn sessionId;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String getNewSessionId() {\n\t\t\t\treturn newSessionId;\n\t\t\t}\n\t\t});\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId)).isNull();\n\t\tassertThat(this.sessionRegistry.getSessionInformation(newSessionId)).isNotNull();\n\t\tassertThat(this.sessionRegistry.getSessionInformation(newSessionId).getPrincipal()).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void testMultiplePrincipals() {\n\t\tObject principal1 = \"principal_1\";\n\t\tObject principal2 = \"principal_2\";\n\t\tString sessionId1 = \"1234567890\";\n\t\tString sessionId2 = \"9876543210\";\n\t\tString sessionId3 = \"5432109876\";\n\t\tthis.sessionRegistry.registerNewSession(sessionId1, principal1);\n\t\tthis.sessionRegistry.registerNewSession(sessionId2, principal1);\n\t\tthis.sessionRegistry.registerNewSession(sessionId3, principal2);\n\t\tassertThat(this.sessionRegistry.getAllPrincipals()).hasSize(2);\n\t\tassertThat(this.sessionRegistry.getAllPrincipals()).contains(principal1);\n\t\tassertThat(this.sessionRegistry.getAllPrincipals()).contains(principal2);\n\t}\n\n\t@Test\n\tpublic void testSessionInformationLifecycle() throws Exception {\n\t\tObject principal = \"Some principal object\";\n\t\tString sessionId = \"1234567890\";\n\t\t// Register new Session\n\t\tthis.sessionRegistry.registerNewSession(sessionId, principal);\n\t\t// Retrieve existing session by session ID\n\t\tDate currentDateTime = this.sessionRegistry.getSessionInformation(sessionId).getLastRequest();\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId).getPrincipal()).isEqualTo(principal);\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId).getSessionId()).isEqualTo(sessionId);\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId).getLastRequest()).isNotNull();\n\t\t// Retrieve existing session by principal\n\t\tassertThat(this.sessionRegistry.getAllSessions(principal, false)).hasSize(1);\n\t\t// Sleep to ensure SessionRegistryImpl will update time\n\t\tThread.sleep(1000);\n\t\t// Update request date/time\n\t\tthis.sessionRegistry.refreshLastRequest(sessionId);\n\t\tDate retrieved = this.sessionRegistry.getSessionInformation(sessionId).getLastRequest();\n\t\tassertThat(retrieved.after(currentDateTime)).isTrue();\n\t\t// Check it retrieves correctly when looked up via principal\n\t\tassertThat(this.sessionRegistry.getAllSessions(principal, false).get(0).getLastRequest()).isCloseTo(retrieved,\n\t\t\t\t2000L);\n\t\t// Clear session information\n\t\tthis.sessionRegistry.removeSessionInformation(sessionId);\n\t\t// Check attempts to retrieve cleared session return null\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId)).isNull();\n\t\tassertThat(this.sessionRegistry.getAllSessions(principal, false)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void testTwoSessionsOnePrincipalExpiring() {\n\t\tObject principal = \"Some principal object\";\n\t\tString sessionId1 = \"1234567890\";\n\t\tString sessionId2 = \"9876543210\";\n\t\tthis.sessionRegistry.registerNewSession(sessionId1, principal);\n\t\tList<SessionInformation> sessions = this.sessionRegistry.getAllSessions(principal, false);\n\t\tassertThat(sessions).hasSize(1);\n\t\tassertThat(contains(sessionId1, principal)).isTrue();\n\t\tthis.sessionRegistry.registerNewSession(sessionId2, principal);\n\t\tsessions = this.sessionRegistry.getAllSessions(principal, false);\n\t\tassertThat(sessions).hasSize(2);\n\t\tassertThat(contains(sessionId2, principal)).isTrue();\n\t\t// Expire one session\n\t\tSessionInformation session = this.sessionRegistry.getSessionInformation(sessionId2);\n\t\tsession.expireNow();\n\t\t// Check retrieval still correct\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId2).isExpired()).isTrue();\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId1).isExpired()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testTwoSessionsOnePrincipalHandling() {\n\t\tObject principal = \"Some principal object\";\n\t\tString sessionId1 = \"1234567890\";\n\t\tString sessionId2 = \"9876543210\";\n\t\tthis.sessionRegistry.registerNewSession(sessionId1, principal);\n\t\tList<SessionInformation> sessions = this.sessionRegistry.getAllSessions(principal, false);\n\t\tassertThat(sessions).hasSize(1);\n\t\tassertThat(contains(sessionId1, principal)).isTrue();\n\t\tthis.sessionRegistry.registerNewSession(sessionId2, principal);\n\t\tsessions = this.sessionRegistry.getAllSessions(principal, false);\n\t\tassertThat(sessions).hasSize(2);\n\t\tassertThat(contains(sessionId2, principal)).isTrue();\n\t\tthis.sessionRegistry.removeSessionInformation(sessionId1);\n\t\tsessions = this.sessionRegistry.getAllSessions(principal, false);\n\t\tassertThat(sessions).hasSize(1);\n\t\tassertThat(contains(sessionId2, principal)).isTrue();\n\t\tthis.sessionRegistry.removeSessionInformation(sessionId2);\n\t\tassertThat(this.sessionRegistry.getSessionInformation(sessionId2)).isNull();\n\t\tassertThat(this.sessionRegistry.getAllSessions(principal, false)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void sessionIdChangedEventWhenSessionIdNotSavedThenDoesNothing() {\n\t\tfinal String oldSessionId = \"old-session-id\";\n\t\tfinal String newSessionId = \"new-session-id\";\n\t\tthis.sessionRegistry.onApplicationEvent(new SessionIdChangedEvent(\"\") {\n\t\t\t@Override\n\t\t\tpublic String getOldSessionId() {\n\t\t\t\treturn oldSessionId;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String getNewSessionId() {\n\t\t\t\treturn newSessionId;\n\t\t\t}\n\t\t});\n\t\tassertThat(this.sessionRegistry.getSessionInformation(oldSessionId)).isNull();\n\t\tassertThat(this.sessionRegistry.getSessionInformation(newSessionId)).isNull();\n\t}\n\n\tprivate boolean contains(String sessionId, Object principal) {\n\t\tList<SessionInformation> info = this.sessionRegistry.getAllSessions(principal, false);\n\t\tfor (SessionInformation sessionInformation : info) {\n\t\t\tif (sessionId.equals(sessionInformation.getSessionId())) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/token/DefaultTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\nimport java.util.Date;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link DefaultToken}.\n *\n * @author Ben Alex\n *\n */\npublic class DefaultTokenTests {\n\n\t@Test\n\tpublic void testEquality() {\n\t\tString key = \"key\";\n\t\tlong created = new Date().getTime();\n\t\tString extendedInformation = \"extended\";\n\t\tDefaultToken t1 = new DefaultToken(key, created, extendedInformation);\n\t\tDefaultToken t2 = new DefaultToken(key, created, extendedInformation);\n\t\tassertThat(t2).isEqualTo(t1);\n\t}\n\n\t@Test\n\tpublic void testRejectsNullExtendedInformation() {\n\t\tString key = \"key\";\n\t\tlong created = new Date().getTime();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DefaultToken(key, created, null));\n\t}\n\n\t@Test\n\tpublic void testEqualityWithDifferentExtendedInformation3() {\n\t\tString key = \"key\";\n\t\tlong created = new Date().getTime();\n\t\tDefaultToken t1 = new DefaultToken(key, created, \"length1\");\n\t\tDefaultToken t2 = new DefaultToken(key, created, \"longerLength2\");\n\t\tassertThat(t1).isNotEqualTo(t2);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/token/KeyBasedPersistenceTokenServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\nimport java.security.SecureRandom;\nimport java.util.Date;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link KeyBasedPersistenceTokenService}.\n *\n * @author Ben Alex\n */\npublic class KeyBasedPersistenceTokenServiceTests {\n\n\tprivate KeyBasedPersistenceTokenService getService() {\n\t\tSecureRandomFactoryBean fb = new SecureRandomFactoryBean();\n\t\tKeyBasedPersistenceTokenService service = new KeyBasedPersistenceTokenService();\n\t\tservice.setServerSecret(\"MY:SECRET$$$#\");\n\t\tservice.setServerInteger(454545);\n\t\ttry {\n\t\t\tSecureRandom rnd = fb.getObject();\n\t\t\tservice.setSecureRandom(rnd);\n\t\t\tservice.afterPropertiesSet();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t\treturn service;\n\t}\n\n\t@Test\n\tpublic void testOperationWithSimpleExtendedInformation() {\n\t\tKeyBasedPersistenceTokenService service = getService();\n\t\tToken token = service.allocateToken(\"Hello world\");\n\t\tToken result = service.verifyToken(token.getKey());\n\t\tassertThat(result).isEqualTo(token);\n\t}\n\n\t@Test\n\tpublic void testOperationWithComplexExtendedInformation() {\n\t\tKeyBasedPersistenceTokenService service = getService();\n\t\tToken token = service.allocateToken(\"Hello:world:::\");\n\t\tToken result = service.verifyToken(token.getKey());\n\t\tassertThat(result).isEqualTo(token);\n\t}\n\n\t@Test\n\tpublic void testOperationWithEmptyRandomNumber() {\n\t\tKeyBasedPersistenceTokenService service = getService();\n\t\tservice.setPseudoRandomNumberBytes(0);\n\t\tToken token = service.allocateToken(\"Hello:world:::\");\n\t\tToken result = service.verifyToken(token.getKey());\n\t\tassertThat(result).isEqualTo(token);\n\t}\n\n\t@Test\n\tpublic void testOperationWithNoExtendedInformation() {\n\t\tKeyBasedPersistenceTokenService service = getService();\n\t\tToken token = service.allocateToken(\"\");\n\t\tToken result = service.verifyToken(token.getKey());\n\t\tassertThat(result).isEqualTo(token);\n\t}\n\n\t@Test\n\tpublic void testOperationWithMissingKey() {\n\t\tKeyBasedPersistenceTokenService service = getService();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tToken token = new DefaultToken(\"\", new Date().getTime(), \"\");\n\t\t\tservice.verifyToken(token.getKey());\n\t\t});\n\t}\n\n\t@Test\n\tpublic void testOperationWithTamperedKey() {\n\t\tKeyBasedPersistenceTokenService service = getService();\n\t\tToken goodToken = service.allocateToken(\"\");\n\t\tString fake = goodToken.getKey().toUpperCase();\n\t\tToken token = new DefaultToken(fake, new Date().getTime(), \"\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> service.verifyToken(token.getKey()));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/token/SecureRandomFactoryBeanTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.token;\n\nimport java.security.SecureRandom;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link SecureRandomFactoryBean}.\n *\n * @author Ben Alex\n *\n */\npublic class SecureRandomFactoryBeanTests {\n\n\t@Test\n\tpublic void testObjectType() {\n\t\tSecureRandomFactoryBean factory = new SecureRandomFactoryBean();\n\t\tassertThat(factory.getObjectType()).isEqualTo(SecureRandom.class);\n\t}\n\n\t@Test\n\tpublic void testIsSingleton() {\n\t\tSecureRandomFactoryBean factory = new SecureRandomFactoryBean();\n\t\tassertThat(factory.isSingleton()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testCreatesUsingDefaults() throws Exception {\n\t\tSecureRandomFactoryBean factory = new SecureRandomFactoryBean();\n\t\tObject result = factory.getObject();\n\t\tassertThat(result).isInstanceOf(SecureRandom.class);\n\t\tint rnd = ((SecureRandom) result).nextInt();\n\t\tassertThat(rnd).isNotEqualTo(0);\n\t}\n\n\t@Test\n\tpublic void testCreatesUsingSeed() throws Exception {\n\t\tSecureRandomFactoryBean factory = new SecureRandomFactoryBean();\n\t\tResource resource = new ClassPathResource(\n\t\t\t\t\"org/springframework/security/core/token/SecureRandomFactoryBeanTests.class\");\n\t\tassertThat(resource).isNotNull();\n\t\tfactory.setSeed(resource);\n\t\tSecureRandom first = factory.getObject();\n\t\tSecureRandom second = factory.getObject();\n\t\tassertThat(first.nextInt()).isNotEqualTo(0).isNotEqualTo(second.nextInt());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/MapReactiveUserDetailsServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\npublic class MapReactiveUserDetailsServiceTests {\n\n\t// @formatter:off\n\tprivate static final UserDetails USER_DETAILS = User.withUsername(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t// @formatter:on\n\tprivate MapReactiveUserDetailsService users = new MapReactiveUserDetailsService(Arrays.asList(USER_DETAILS));\n\n\t@Test\n\tpublic void constructorNullUsers() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new MapReactiveUserDetailsService((Collection<UserDetails>) null));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyUsers() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new MapReactiveUserDetailsService(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void constructorCaseIntensiveKey() {\n\t\tUserDetails userDetails = User.withUsername(\"USER\").password(\"password\").roles(\"USER\").build();\n\t\tMapReactiveUserDetailsService userDetailsService = new MapReactiveUserDetailsService(userDetails);\n\t\tassertThat(userDetailsService.findByUsername(\"user\").block()).isEqualTo(userDetails);\n\t}\n\n\t@Test\n\tpublic void findByUsernameWhenFoundThenReturns() {\n\t\tassertThat((this.users.findByUsername(USER_DETAILS.getUsername()).block())).isEqualTo(USER_DETAILS);\n\t}\n\n\t@Test\n\tpublic void findByUsernameWhenDifferentCaseThenReturns() {\n\t\tassertThat((this.users.findByUsername(\"uSeR\").block())).isEqualTo(USER_DETAILS);\n\t}\n\n\t@Test\n\tpublic void findByUsernameWhenClearCredentialsThenFindByUsernameStillHasCredentials() {\n\t\tUser foundUser = this.users.findByUsername(USER_DETAILS.getUsername()).cast(User.class).block();\n\t\tassertThat(foundUser.getPassword()).isNotEmpty();\n\t\tfoundUser.eraseCredentials();\n\t\tassertThat(foundUser.getPassword()).isNull();\n\t\tfoundUser = this.users.findByUsername(USER_DETAILS.getUsername()).cast(User.class).block();\n\t\tassertThat(foundUser.getPassword()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void findByUsernameWhenNotFoundThenEmpty() {\n\t\tassertThat((this.users.findByUsername(\"notfound\"))).isEqualTo(Mono.empty());\n\t}\n\n\t@Test\n\tpublic void updatePassword() {\n\t\tthis.users.updatePassword(USER_DETAILS, \"new\").block();\n\t\tassertThat(this.users.findByUsername(USER_DETAILS.getUsername()).block().getPassword()).isEqualTo(\"new\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/MockUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\n/**\n * A test UserDetailsService containing a set of standard usernames corresponding to their\n * account status: valid, locked, disabled, credentialsExpired, expired. All passwords are\n * \"\".\n *\n * @author Luke Taylor\n */\npublic class MockUserDetailsService implements UserDetailsService {\n\n\tprivate Map<String, User> users = new HashMap<>();\n\n\tprivate List<GrantedAuthority> auths = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\n\tpublic MockUserDetailsService() {\n\t\tthis.users.put(\"valid\", new User(\"valid\", \"\", true, true, true, true, this.auths));\n\t\tthis.users.put(\"locked\", new User(\"locked\", \"\", true, true, true, false, this.auths));\n\t\tthis.users.put(\"disabled\", new User(\"disabled\", \"\", false, true, true, true, this.auths));\n\t\tthis.users.put(\"credentialsExpired\", new User(\"credentialsExpired\", \"\", true, true, false, true, this.auths));\n\t\tthis.users.put(\"expired\", new User(\"expired\", \"\", true, false, true, true, this.auths));\n\t}\n\n\t@Override\n\tpublic UserDetails loadUserByUsername(String username) {\n\t\tif (this.users.get(username) == null) {\n\t\t\tthrow new UsernameNotFoundException(\"User not found: \" + username);\n\t\t}\n\t\treturn this.users.get(username);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/PasswordEncodedUser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport java.util.function.Function;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class PasswordEncodedUser {\n\n\tprivate static final UserDetails USER = withUsername(\"user\").password(\"password\").roles(\"USER\").build();\n\n\tprivate static final UserDetails ADMIN = withUsername(\"admin\").password(\"password\").roles(\"USER\", \"ADMIN\").build();\n\n\tpublic static UserDetails user() {\n\t\treturn User.withUserDetails(USER).build();\n\t}\n\n\tpublic static UserDetails admin() {\n\t\treturn User.withUserDetails(ADMIN).build();\n\t}\n\n\tpublic static User.UserBuilder builder() {\n\t\treturn User.builder().passwordEncoder(passwordEncoder());\n\t}\n\n\tpublic static User.UserBuilder withUsername(String username) {\n\t\treturn builder().username(username);\n\t}\n\n\tpublic static User.UserBuilder withUserDetails(UserDetails userDetails) {\n\t\t// @formatter:off\n\t\treturn User\n\t\t\t\t.withUserDetails(userDetails)\n\t\t\t\t.passwordEncoder(passwordEncoder());\n\t\t// @formatter:on\n\t}\n\n\tprivate static Function<String, String> passwordEncoder() {\n\t\treturn (rawPassword) -> \"{noop}\" + rawPassword;\n\t}\n\n\tprotected PasswordEncodedUser() {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/UserDetailsByNameServiceWrapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author TSARDD\n * @since 18-okt-2007\n */\n@SuppressWarnings(\"unchecked\")\npublic class UserDetailsByNameServiceWrapperTests {\n\n\t@Test\n\tpublic final void testAfterPropertiesSet() {\n\t\tUserDetailsByNameServiceWrapper svc = new UserDetailsByNameServiceWrapper();\n\t\tassertThatIllegalArgumentException().isThrownBy(svc::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic final void testGetUserDetails() throws Exception {\n\t\tUserDetailsByNameServiceWrapper svc = new UserDetailsByNameServiceWrapper();\n\t\tfinal User user = new User(\"dummy\", \"dummy\", true, true, true, true, AuthorityUtils.NO_AUTHORITIES);\n\t\tsvc.setUserDetailsService((name) -> {\n\t\t\tif (user != null && user.getUsername().equals(name)) {\n\t\t\t\treturn user;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\t\tsvc.afterPropertiesSet();\n\t\tUserDetails result1 = svc.loadUserDetails(new TestingAuthenticationToken(\"dummy\", \"dummy\"));\n\t\tassertThat(result1).as(\"Result doesn't match original user\").isEqualTo(user);\n\t\tUserDetails result2 = svc.loadUserDetails(new TestingAuthenticationToken(\"dummy2\", \"dummy\"));\n\t\tassertThat(result2).as(\"Result should have been null\").isNull();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/UserTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.ObjectOutputStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.NullSource;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link User}.\n *\n * @author Ben Alex\n * @author Ilya Starchenko\n */\npublic class UserTests {\n\n\tprivate static final List<GrantedAuthority> ROLE_12 = AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\");\n\n\t@Test\n\tpublic void equalsReturnsTrueIfUsernamesAreTheSame() {\n\t\tUser user1 = new User(\"rod\", \"koala\", true, true, true, true, ROLE_12);\n\t\tassertThat(user1).isNotNull();\n\t\tassertThat(user1).isNotEqualTo(\"A STRING\");\n\t\tassertThat(user1).isEqualTo(user1);\n\t\tassertThat(user1).isEqualTo((new User(\"rod\", \"notthesame\", true, true, true, true, ROLE_12)));\n\t}\n\n\t@Test\n\tpublic void hashLookupOnlyDependsOnUsername() {\n\t\tUser user1 = new User(\"rod\", \"koala\", true, true, true, true, ROLE_12);\n\t\tSet<UserDetails> users = new HashSet<>();\n\t\tusers.add(user1);\n\t\tassertThat(users).contains(new User(\"rod\", \"koala\", true, true, true, true, ROLE_12));\n\t\tassertThat(users).contains(new User(\"rod\", \"anotherpass\", false, false, false, false,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_X\")));\n\t\tassertThat(users).doesNotContain(new User(\"bod\", \"koala\", true, true, true, true, ROLE_12));\n\t}\n\n\t@Test\n\tpublic void testNoArgConstructorDoesntExist() {\n\t\tassertThatExceptionOfType(NoSuchMethodException.class)\n\t\t\t.isThrownBy(() -> User.class.getDeclaredConstructor((Class[]) null));\n\t}\n\n\t@Test\n\tpublic void testBuildUserWithNoAuthorities() {\n\t\tUserDetails user = User.builder().username(\"user\").password(\"password\").build();\n\t\tassertThat(user.getAuthorities()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void testNullWithinUserAuthoritiesIsRejected() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> User.builder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.authorities((Collection<? extends GrantedAuthority>) null)\n\t\t\t.build());\n\t\tList<GrantedAuthority> authorities = new ArrayList<>();\n\t\tauthorities.add(null);\n\t\tauthorities.add(null);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> User.builder().username(\"user\").password(\"password\").authorities(authorities).build());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> User.builder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.authorities((GrantedAuthority[]) null)\n\t\t\t.build());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> User.builder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.authorities(new GrantedAuthority[] { null, null })\n\t\t\t.build());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> User.builder().username(\"user\").password(\"password\").authorities((String[]) null).build());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> User.builder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.authorities(new String[] { null, null })\n\t\t\t.build());\n\t}\n\n\t// gh-12533\n\t@ParameterizedTest\n\t@NullSource\n\t@ValueSource(strings = { \"ROLE_USER,ROLE_ADMIN,read\", \"read\" })\n\tpublic void withUserDetailsWhenAuthoritiesThenOverridesPreviousAuthorities(String arg) {\n\t\t// @formatter:off\n\t\tUserDetails parent = User.builder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.authorities(\"one\", \"two\", \"three\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tString[] authorities = (arg != null) ? arg.split(\",\") : new String[0];\n\t\tUser.UserBuilder builder = User.withUserDetails(parent);\n\t\tUserDetails user = builder.build();\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).containsOnly(\"one\", \"two\", \"three\");\n\t\tuser = builder.authorities(authorities).build();\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).containsOnly(authorities);\n\t\tuser = builder.authorities(AuthorityUtils.createAuthorityList(authorities)).build();\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).containsOnly(authorities);\n\t\tuser = builder.authorities(AuthorityUtils.createAuthorityList(authorities).toArray(GrantedAuthority[]::new))\n\t\t\t.build();\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).containsOnly(authorities);\n\t}\n\n\t@Test\n\tpublic void testNullValuesRejected() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new User(null, \"koala\", true, true, true, true, ROLE_12));\n\t\tList<GrantedAuthority> auths = AuthorityUtils.createAuthorityList(\"ROLE_ONE\");\n\t\tauths.add(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new User(\"rod\", \"koala\", true, true, true, true, auths));\n\t}\n\n\t/**\n\t * This is allowed because the password can become null when\n\t * {@link User#eraseCredentials()} is called.\n\t */\n\t@Test\n\tpublic void constructorStringStringBooleanBooleanBooleanBooleanListWhenNullPasswordThenNullPassword() {\n\t\tList<GrantedAuthority> auths = AuthorityUtils.createAuthorityList(\"ROLE_ONE\");\n\t\tUser rod = new User(\"rod\", null, true, true, true, true, auths);\n\t\tassertThat(rod.getPassword()).isNull();\n\t}\n\n\t/**\n\t * This is allowed because the password can become null when\n\t * {@link User#eraseCredentials()} is called.\n\t */\n\t@Test\n\tpublic void constructorStringStringListWhenNullPasswordThenNoException() {\n\t\tList<GrantedAuthority> auths = AuthorityUtils.createAuthorityList(\"ROLE_ONE\");\n\t\tUser rod = new User(\"rod\", null, auths);\n\t\tassertThat(rod.getPassword()).isNull();\n\t}\n\n\t@Test\n\tpublic void testNullWithinGrantedAuthorityElementIsRejected() {\n\t\tList<GrantedAuthority> auths = AuthorityUtils.createAuthorityList(\"ROLE_ONE\");\n\t\tauths.add(null);\n\t\tauths.add(new SimpleGrantedAuthority(\"ROLE_THREE\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new User(null, \"koala\", true, true, true, true, auths));\n\t}\n\n\t@Test\n\tpublic void testUserGettersSetter() {\n\t\tUserDetails user = new User(\"rod\", \"koala\", true, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_TWO\", \"ROLE_ONE\"));\n\t\tassertThat(user.getUsername()).isEqualTo(\"rod\");\n\t\tassertThat(user.getPassword()).isEqualTo(\"koala\");\n\t\tassertThat(user.isEnabled()).isTrue();\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_ONE\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_TWO\");\n\t\tassertThat(user.toString()).contains(\"rod\");\n\t}\n\n\t@Test\n\tpublic void enabledFlagIsFalseForDisabledAccount() {\n\t\tUserDetails user = new User(\"rod\", \"koala\", false, true, true, true, ROLE_12);\n\t\tassertThat(user.isEnabled()).isFalse();\n\t}\n\n\t@Test\n\tpublic void useIsSerializable() throws Exception {\n\t\tUserDetails user = new User(\"rod\", \"koala\", false, true, true, true, ROLE_12);\n\t\t// Serialize to a byte array\n\t\tByteArrayOutputStream bos = new ByteArrayOutputStream();\n\t\tObjectOutputStream out = new ObjectOutputStream(bos);\n\t\tout.writeObject(user);\n\t\tout.close();\n\t}\n\n\t@Test\n\tpublic void withUserDetailsWhenAllEnabled() {\n\t\tUser expected = new User(\"rob\", \"pass\", true, true, true, true, ROLE_12);\n\t\tUserDetails actual = User.withUserDetails(expected).build();\n\t\tassertThat(actual.getUsername()).isEqualTo(expected.getUsername());\n\t\tassertThat(actual.getPassword()).isEqualTo(expected.getPassword());\n\t\tassertThat(actual.getAuthorities()).isEqualTo(expected.getAuthorities());\n\t\tassertThat(actual.isAccountNonExpired()).isEqualTo(expected.isAccountNonExpired());\n\t\tassertThat(actual.isAccountNonLocked()).isEqualTo(expected.isAccountNonLocked());\n\t\tassertThat(actual.isCredentialsNonExpired()).isEqualTo(expected.isCredentialsNonExpired());\n\t\tassertThat(actual.isEnabled()).isEqualTo(expected.isEnabled());\n\t}\n\n\t@Test\n\tpublic void withUserDetailsWhenAllDisabled() {\n\t\tUser expected = new User(\"rob\", \"pass\", false, false, false, false, ROLE_12);\n\t\tUserDetails actual = User.withUserDetails(expected).build();\n\t\tassertThat(actual.getUsername()).isEqualTo(expected.getUsername());\n\t\tassertThat(actual.getPassword()).isEqualTo(expected.getPassword());\n\t\tassertThat(actual.getAuthorities()).isEqualTo(expected.getAuthorities());\n\t\tassertThat(actual.isAccountNonExpired()).isEqualTo(expected.isAccountNonExpired());\n\t\tassertThat(actual.isAccountNonLocked()).isEqualTo(expected.isAccountNonLocked());\n\t\tassertThat(actual.isCredentialsNonExpired()).isEqualTo(expected.isCredentialsNonExpired());\n\t\tassertThat(actual.isEnabled()).isEqualTo(expected.isEnabled());\n\t}\n\n\t@Test\n\tpublic void withUserWhenDetailsPasswordEncoderThenEncodes() {\n\t\tUserDetails userDetails = User.withUsername(\"user\").password(\"password\").roles(\"USER\").build();\n\t\tUserDetails withEncodedPassword = User.withUserDetails(userDetails)\n\t\t\t.passwordEncoder((p) -> p + \"encoded\")\n\t\t\t.build();\n\t\tassertThat(withEncodedPassword.getPassword()).isEqualTo(\"passwordencoded\");\n\t}\n\n\t@Test\n\tpublic void withUsernameWhenPasswordEncoderAndPasswordThenEncodes() {\n\t\tUserDetails withEncodedPassword = User.withUsername(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.passwordEncoder((p) -> p + \"encoded\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\tassertThat(withEncodedPassword.getPassword()).isEqualTo(\"passwordencoded\");\n\t}\n\n\t@Test\n\tpublic void withUsernameWhenPasswordAndPasswordEncoderThenEncodes() {\n\t\t// @formatter:off\n\t\tUserDetails withEncodedPassword = User.withUsername(\"user\")\n\t\t\t.passwordEncoder((p) -> p + \"encoded\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(withEncodedPassword.getPassword()).isEqualTo(\"passwordencoded\");\n\t}\n\n\t@Test\n\tpublic void withUsernameWhenPasswordAndPasswordEncoderTwiceThenEncodesOnce() {\n\t\tFunction<String, String> encoder = (p) -> p + \"encoded\";\n\t\t// @formatter:off\n\t\tUserDetails withEncodedPassword = User.withUsername(\"user\")\n\t\t\t.passwordEncoder(encoder)\n\t\t\t.password(\"password\")\n\t\t\t.passwordEncoder(encoder)\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(withEncodedPassword.getPassword()).isEqualTo(\"passwordencoded\");\n\t}\n\n\t/**\n\t * This is allowed because the password can become null when\n\t * {@link User#eraseCredentials()} is called.\n\t */\n\t@Test\n\tpublic void withUsernameWhenNullPasswordThenNoException() {\n\t\tassertThat(User.withUsername(\"user\").build().getPassword()).isNull();\n\t}\n\n\t@Test\n\tpublic void withUsernameWhenPasswordNullAndEncoderThenEncoderNotUsed() {\n\t\tFunction<String, String> encoder = (p) -> \"encoded\";\n\t\t// @formatter:off\n\t\tUserDetails withEncodedPassword = User.withUsername(\"user\")\n\t\t\t\t.passwordEncoder(encoder)\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(withEncodedPassword.getPassword()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/cache/NullUserCacheTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.cache;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link NullUserCache}.\n *\n * @author Ben Alex\n */\npublic class NullUserCacheTests {\n\n\tprivate User getUser() {\n\t\treturn new User(\"john\", \"password\", true, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t}\n\n\t@Test\n\tpublic void testCacheOperation() {\n\t\tNullUserCache cache = new NullUserCache();\n\t\tcache.putUserInCache(getUser());\n\t\tassertThat(cache.getUserFromCache(null)).isNull();\n\t\tcache.removeUserFromCache(null);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/cache/SpringCacheBasedUserCacheTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.cache;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.concurrent.ConcurrentMapCacheManager;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests\n * {@link org.springframework.security.core.userdetails.cache.SpringCacheBasedUserCache}.\n *\n * @author Marten Deinum\n * @since 3.2\n *\n */\npublic class SpringCacheBasedUserCacheTests {\n\n\tprivate static CacheManager cacheManager;\n\n\t@BeforeAll\n\tpublic static void initCacheManaer() {\n\t\tcacheManager = new ConcurrentMapCacheManager();\n\t\tcacheManager.getCache(\"springbasedusercachetests\");\n\t}\n\n\t@AfterAll\n\tpublic static void shutdownCacheManager() {\n\t}\n\n\tprivate Cache getCache() {\n\t\tCache cache = cacheManager.getCache(\"springbasedusercachetests\");\n\t\tcache.clear();\n\t\treturn cache;\n\t}\n\n\tprivate User getUser() {\n\t\treturn new User(\"john\", \"password\", true, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t}\n\n\t@Test\n\tpublic void cacheOperationsAreSuccessful() throws Exception {\n\t\tSpringCacheBasedUserCache cache = new SpringCacheBasedUserCache(getCache());\n\t\t// Check it gets stored in the cache\n\t\tcache.putUserInCache(getUser());\n\t\tassertThat(getUser().getPassword()).isEqualTo(cache.getUserFromCache(getUser().getUsername()).getPassword());\n\t\t// Check it gets removed from the cache\n\t\tcache.removeUserFromCache(getUser());\n\t\tassertThat(cache.getUserFromCache(getUser().getUsername())).isNull();\n\t\t// Check it doesn't return values for null or unknown users\n\t\tassertThat(cache.getUserFromCache(null)).isNull();\n\t\tassertThat(cache.getUserFromCache(\"UNKNOWN_USER\")).isNull();\n\t}\n\n\t@Test\n\tpublic void startupDetectsMissingCache() throws Exception {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SpringCacheBasedUserCache(null));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/jdbc/JdbcDaoImplTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.jdbc;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.security.PopulatedDatabase;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link JdbcDaoImpl}.\n *\n * @author Ben Alex\n * @author Eddú Meléndez\n */\npublic class JdbcDaoImplTests {\n\n\tprivate JdbcDaoImpl makePopulatedJdbcDao() {\n\t\tJdbcDaoImpl dao = new JdbcDaoImpl();\n\t\tdao.setDataSource(PopulatedDatabase.getDataSource());\n\t\tdao.afterPropertiesSet();\n\t\treturn dao;\n\t}\n\n\tprivate JdbcDaoImpl makePopulatedJdbcDaoWithRolePrefix() {\n\t\tJdbcDaoImpl dao = new JdbcDaoImpl();\n\t\tdao.setDataSource(PopulatedDatabase.getDataSource());\n\t\tdao.setRolePrefix(\"ARBITRARY_PREFIX_\");\n\t\tdao.afterPropertiesSet();\n\t\treturn dao;\n\t}\n\n\t@Test\n\tpublic void testCheckDaoAccessUserSuccess() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDao();\n\t\tUserDetails user = dao.loadUserByUsername(\"rod\");\n\t\tassertThat(user.getUsername()).isEqualTo(\"rod\");\n\t\tassertThat(user.getPassword()).isEqualTo(\"koala\");\n\t\tassertThat(user.isEnabled()).isTrue();\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_TELLER\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_SUPERVISOR\");\n\t}\n\n\t@Test\n\tpublic void testCheckDaoOnlyReturnsGrantedAuthoritiesGrantedToUser() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDao();\n\t\tUserDetails user = dao.loadUserByUsername(\"scott\");\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_TELLER\");\n\t}\n\n\t@Test\n\tpublic void testCheckDaoReturnsCorrectDisabledProperty() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDao();\n\t\tUserDetails user = dao.loadUserByUsername(\"peter\");\n\t\tassertThat(user.isEnabled()).isFalse();\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tJdbcDaoImpl dao = new JdbcDaoImpl();\n\t\tdao.setAuthoritiesByUsernameQuery(\"SELECT * FROM FOO\");\n\t\tassertThat(dao.getAuthoritiesByUsernameQuery()).isEqualTo(\"SELECT * FROM FOO\");\n\t\tdao.setUsersByUsernameQuery(\"SELECT USERS FROM FOO\");\n\t\tassertThat(dao.getUsersByUsernameQuery()).isEqualTo(\"SELECT USERS FROM FOO\");\n\t}\n\n\t@Test\n\tpublic void testLookupFailsIfUserHasNoGrantedAuthorities() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDao();\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> dao.loadUserByUsername(\"cooper\"));\n\t}\n\n\t@Test\n\tpublic void testLookupFailsWithWrongUsername() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDao();\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class)\n\t\t\t.isThrownBy(() -> dao.loadUserByUsername(\"UNKNOWN_USER\"));\n\t}\n\n\t@Test\n\tpublic void testLookupSuccessWithMixedCase() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDao();\n\t\tassertThat(dao.loadUserByUsername(\"rod\").getPassword()).isEqualTo(\"koala\");\n\t\tassertThat(dao.loadUserByUsername(\"ScOTt\").getPassword()).isEqualTo(\"wombat\");\n\t}\n\n\t@Test\n\tpublic void testRolePrefixWorks() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDaoWithRolePrefix();\n\t\tassertThat(dao.getRolePrefix()).isEqualTo(\"ARBITRARY_PREFIX_\");\n\t\tUserDetails user = dao.loadUserByUsername(\"rod\");\n\t\tassertThat(user.getUsername()).isEqualTo(\"rod\");\n\t\tassertThat(user.getAuthorities()).hasSize(2);\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ARBITRARY_PREFIX_ROLE_TELLER\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities()))\n\t\t\t.contains(\"ARBITRARY_PREFIX_ROLE_SUPERVISOR\");\n\t}\n\n\t@Test\n\tpublic void testGroupAuthoritiesAreLoadedCorrectly() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDao();\n\t\tdao.setEnableAuthorities(false);\n\t\tdao.setEnableGroups(true);\n\t\tUserDetails jerry = dao.loadUserByUsername(\"jerry\");\n\t\tassertThat(jerry.getAuthorities()).hasSize(3);\n\t}\n\n\t@Test\n\tpublic void testDuplicateGroupAuthoritiesAreRemoved() throws Exception {\n\t\tJdbcDaoImpl dao = makePopulatedJdbcDao();\n\t\tdao.setEnableAuthorities(false);\n\t\tdao.setEnableGroups(true);\n\t\t// Tom has roles A, B, C and B, C duplicates\n\t\tUserDetails tom = dao.loadUserByUsername(\"tom\");\n\t\tassertThat(tom.getAuthorities()).hasSize(3);\n\t}\n\n\t@Test\n\tpublic void testStartupFailsIfDataSourceNotSet() {\n\t\tJdbcDaoImpl dao = new JdbcDaoImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(dao::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testStartupFailsIfUserMapSetToNull() {\n\t\tJdbcDaoImpl dao = new JdbcDaoImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tdao.setDataSource(null);\n\t\t\tdao.afterPropertiesSet();\n\t\t});\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNullThenThrowsException() {\n\t\tJdbcDaoImpl dao = new JdbcDaoImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> dao.setMessageSource(null));\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNotNullThenCanGet() {\n\t\tMessageSource source = mock(MessageSource.class);\n\t\tJdbcDaoImpl dao = new JdbcDaoImpl();\n\t\tdao.setMessageSource(source);\n\t\tString code = \"code\";\n\t\tdao.getMessages().getMessage(code);\n\t\tverify(source).getMessage(eq(code), any(), any());\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/core/userdetails/memory/UserAttributeEditorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.core.userdetails.memory;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link UserAttributeEditor} and associated {@link UserAttribute}.\n *\n * @author Ben Alex\n */\npublic class UserAttributeEditorTests {\n\n\t@Test\n\tpublic void testCorrectOperationWithTrailingSpaces() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(\"password ,ROLE_ONE,ROLE_TWO \");\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user.getPassword()).isEqualTo(\"password\");\n\t\tassertThat(user.getAuthorities()).hasSize(2);\n\t\tassertThat(user.getAuthorities().get(0).getAuthority()).isEqualTo(\"ROLE_ONE\");\n\t\tassertThat(user.getAuthorities().get(1).getAuthority()).isEqualTo(\"ROLE_TWO\");\n\t}\n\n\t@Test\n\tpublic void testCorrectOperationWithoutEnabledDisabledKeyword() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(\"password,ROLE_ONE,ROLE_TWO\");\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user.isValid()).isTrue();\n\t\tassertThat(user.isEnabled()).isTrue(); // default\n\t\tassertThat(user.getPassword()).isEqualTo(\"password\");\n\t\tassertThat(user.getAuthorities()).hasSize(2);\n\t\tassertThat(user.getAuthorities().get(0).getAuthority()).isEqualTo(\"ROLE_ONE\");\n\t\tassertThat(user.getAuthorities().get(1).getAuthority()).isEqualTo(\"ROLE_TWO\");\n\t}\n\n\t@Test\n\tpublic void testDisabledKeyword() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(\"password,disabled,ROLE_ONE,ROLE_TWO\");\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user.isValid()).isTrue();\n\t\tassertThat(!user.isEnabled()).isTrue();\n\t\tassertThat(user.getPassword()).isEqualTo(\"password\");\n\t\tassertThat(user.getAuthorities()).hasSize(2);\n\t\tassertThat(user.getAuthorities().get(0).getAuthority()).isEqualTo(\"ROLE_ONE\");\n\t\tassertThat(user.getAuthorities().get(1).getAuthority()).isEqualTo(\"ROLE_TWO\");\n\t}\n\n\t@Test\n\tpublic void testEmptyStringReturnsNull() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(\"\");\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user == null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testEnabledKeyword() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(\"password,ROLE_ONE,enabled,ROLE_TWO\");\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user.isValid()).isTrue();\n\t\tassertThat(user.isEnabled()).isTrue();\n\t\tassertThat(user.getPassword()).isEqualTo(\"password\");\n\t\tassertThat(user.getAuthorities()).hasSize(2);\n\t\tassertThat(user.getAuthorities().get(0).getAuthority()).isEqualTo(\"ROLE_ONE\");\n\t\tassertThat(user.getAuthorities().get(1).getAuthority()).isEqualTo(\"ROLE_TWO\");\n\t}\n\n\t@Test\n\tpublic void testMalformedStringReturnsNull() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(\"MALFORMED_STRING\");\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user == null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNoPasswordOrRolesReturnsNull() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(\"disabled\");\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user == null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNoRolesReturnsNull() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(\"password,enabled\");\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user == null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNullReturnsNull() {\n\t\tUserAttributeEditor editor = new UserAttributeEditor();\n\t\teditor.setAsText(null);\n\t\tUserAttribute user = (UserAttribute) editor.getValue();\n\t\tassertThat(user == null).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/AbstractMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport tools.jackson.databind.json.JsonMapper;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\n\n/**\n * @author Jitenra Singh\n * @since 4.2\n */\npublic abstract class AbstractMixinTests {\n\n\tprotected JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tBasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()\n\t\t\t.allowIfSubType(\n\t\t\t\t\t\"org.springframework.security.jackson.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal\");\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader, builder)).build();\n\t}\n\n\tUser createDefaultUser() {\n\t\treturn createUser(\"admin\", \"1234\", \"ROLE_USER\");\n\t}\n\n\tUser createUser(String username, String password, String authority) {\n\t\treturn new User(username, password, AuthorityUtils.createAuthorityList(authority));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/AnonymousAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.exc.ValueInstantiationException;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class AnonymousAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\tprivate static final String HASH_KEY = \"key\";\n\n\t// @formatter:off\n\tprivate static final String ANONYMOUS_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.AnonymousAuthenticationToken\\\", \"\n\t\t+ \"\\\"details\\\": null,\"\n\t\t+ \"\\\"principal\\\": \" + UserDeserializerTests.USER_JSON + \",\"\n\t\t+ \"\\\"authenticated\\\": true, \"\n\t\t+ \"\\\"keyHash\\\": \" + HASH_KEY.hashCode() + \",\"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeAnonymousAuthenticationTokenTest() throws JSONException {\n\t\tUser user = createDefaultUser();\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(HASH_KEY, user, user.getAuthorities());\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(ANONYMOUS_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeAnonymousAuthenticationTokenTest() {\n\t\tAnonymousAuthenticationToken token = this.mapper.readValue(ANONYMOUS_JSON, AnonymousAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getKeyHash()).isEqualTo(HASH_KEY.hashCode());\n\t\tassertThat(token.getAuthorities()).isNotNull().hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void deserializeAnonymousAuthenticationTokenWithoutAuthoritiesTest() {\n\t\tString jsonString = \"{\\\"@class\\\": \\\"org.springframework.security.authentication.AnonymousAuthenticationToken\\\", \\\"details\\\": null,\"\n\t\t\t\t+ \"\\\"principal\\\": \\\"user\\\", \\\"authenticated\\\": true, \\\"keyHash\\\": \" + HASH_KEY.hashCode() + \",\"\n\t\t\t\t+ \"\\\"authorities\\\": [\\\"java.util.ArrayList\\\", []]}\";\n\t\tassertThatExceptionOfType(ValueInstantiationException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(jsonString, AnonymousAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void serializeAnonymousAuthenticationTokenMixinAfterEraseCredentialTest() throws JSONException {\n\t\tUser user = createDefaultUser();\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(HASH_KEY, user, user.getAuthorities());\n\t\ttoken.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(ANONYMOUS_JSON.replace(UserDeserializerTests.USER_PASSWORD, \"null\"), actualJson, true);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/BadCredentialsExceptionMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.BadCredentialsException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Yannick Lombardi\n * @since 5.0\n */\npublic class BadCredentialsExceptionMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String EXCEPTION_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.BadCredentialsException\\\",\"\n\t\t+ \"\\\"localizedMessage\\\": \\\"message\\\", \"\n\t\t+ \"\\\"message\\\": \\\"message\\\", \"\n\t\t+ \"\\\"suppressed\\\": [\\\"[Ljava.lang.Throwable;\\\",[]]\"\n\t\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeBadCredentialsExceptionMixinTest() throws JsonProcessingException, JSONException {\n\t\tBadCredentialsException exception = new BadCredentialsException(\"message\");\n\t\tString serializedJson = this.mapper.writeValueAsString(exception);\n\t\tJSONAssert.assertEquals(EXCEPTION_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeBadCredentialsExceptionMixinTest() throws IOException {\n\t\tBadCredentialsException exception = this.mapper.readValue(EXCEPTION_JSON, BadCredentialsException.class);\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getCause()).isNull();\n\t\tassertThat(exception.getMessage()).isEqualTo(\"message\");\n\t\tassertThat(exception.getLocalizedMessage()).isEqualTo(\"message\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/FactorGrantedAuthorityMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.time.Instant;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 7.0\n */\nclass FactorGrantedAuthorityMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tpublic static final String AUTHORITY_JSON = \"{\\\"@class\\\": \\\"org.springframework.security.core.authority.FactorGrantedAuthority\\\", \\\"authority\\\": \\\"FACTOR_PASSWORD\\\", \\\"issuedAt\\\": 1759177143.043000000 }\";\n\n\tprivate Instant issuedAt = Instant.ofEpochMilli(1759177143043L);\n\n\t// @formatter:on\n\n\t@Test\n\tvoid serializeSimpleGrantedAuthorityTest() throws JSONException {\n\t\tGrantedAuthority authority = FactorGrantedAuthority.withAuthority(\"FACTOR_PASSWORD\")\n\t\t\t.issuedAt(this.issuedAt)\n\t\t\t.build();\n\t\tString serializeJson = this.mapper.writeValueAsString(authority);\n\t\tJSONAssert.assertEquals(AUTHORITY_JSON, serializeJson, true);\n\t}\n\n\t@Test\n\tvoid deserializeGrantedAuthorityTest() {\n\t\tFactorGrantedAuthority authority = (FactorGrantedAuthority) this.mapper.readValue(AUTHORITY_JSON, Object.class);\n\t\tassertThat(authority).isNotNull();\n\t\tassertThat(authority.getAuthority()).isEqualTo(\"FACTOR_PASSWORD\");\n\t\tassertThat(authority.getIssuedAt()).isEqualTo(this.issuedAt);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/RememberMeAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.io.IOException;\nimport java.util.Collections;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class RememberMeAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\tprivate static final String REMEMBERME_KEY = \"rememberMe\";\n\n\t// @formatter:off\n\tprivate static final String REMEMBERME_AUTH_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.RememberMeAuthenticationToken\\\", \"\n\t\t+ \"\\\"keyHash\\\": \" + REMEMBERME_KEY.hashCode() + \", \"\n\t\t+ \"\\\"authenticated\\\": true, \\\"details\\\": null\" + \", \"\n\t\t+ \"\\\"principal\\\": \" + UserDeserializerTests.USER_JSON + \", \"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON\n\t+ \"}\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String REMEMBERME_AUTH_STRINGPRINCIPAL_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.RememberMeAuthenticationToken\\\",\"\n\t\t+ \"\\\"keyHash\\\": \" + REMEMBERME_KEY.hashCode() + \", \"\n\t\t+ \"\\\"authenticated\\\": true, \"\n\t\t+ \"\\\"details\\\": null,\"\n\t\t+ \"\\\"principal\\\": \\\"admin\\\", \"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON\n\t+ \"}\";\n\t// @formatter:on\n\n\t@Test\n\tpublic void testWithNullPrincipal() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new RememberMeAuthenticationToken(\"key\", null, Collections.<GrantedAuthority>emptyList()));\n\t}\n\n\t@Test\n\tpublic void testWithNullKey() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new RememberMeAuthenticationToken(null, \"principal\", Collections.<GrantedAuthority>emptyList()));\n\t}\n\n\t@Test\n\tpublic void serializeRememberMeAuthenticationToken() throws JsonProcessingException, JSONException {\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, \"admin\",\n\t\t\t\tCollections.singleton(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(REMEMBERME_AUTH_STRINGPRINCIPAL_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeRememberMeAuthenticationWithUserToken() throws JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, user,\n\t\t\t\tuser.getAuthorities());\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(String.format(REMEMBERME_AUTH_JSON, \"\\\"password\\\"\"), actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeRememberMeAuthenticationWithUserTokenAfterEraseCredential()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, user,\n\t\t\t\tuser.getAuthorities());\n\t\ttoken.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(REMEMBERME_AUTH_JSON.replace(UserDeserializerTests.USER_PASSWORD, \"null\"), actualJson,\n\t\t\t\ttrue);\n\t}\n\n\t@Test\n\tpublic void deserializeRememberMeAuthenticationToken() throws IOException {\n\t\tRememberMeAuthenticationToken token = this.mapper.readValue(REMEMBERME_AUTH_STRINGPRINCIPAL_JSON,\n\t\t\t\tRememberMeAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isEqualTo(\"admin\").isEqualTo(token.getName());\n\t\tassertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void deserializeRememberMeAuthenticationTokenWithUserTest() throws IOException {\n\t\tRememberMeAuthenticationToken token = this.mapper.readValue(String.format(REMEMBERME_AUTH_JSON, \"\\\"password\\\"\"),\n\t\t\t\tRememberMeAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(((User) token.getPrincipal()).getUsername()).isEqualTo(\"admin\");\n\t\tassertThat(((User) token.getPrincipal()).getPassword()).isEqualTo(\"1234\");\n\t\tassertThat(((User) token.getPrincipal()).getAuthorities()).hasSize(1)\n\t\t\t.contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(((User) token.getPrincipal()).isEnabled()).isEqualTo(true);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/SecurityContextMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class SecurityContextMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tpublic static final String SECURITY_CONTEXT_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.core.context.SecurityContextImpl\\\", \"\n\t\t+ \"\\\"authentication\\\": \" + UsernamePasswordAuthenticationTokenMixinTests.AUTHENTICATED_STRINGPRINCIPAL_JSON\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void securityContextSerializeTest() throws JsonProcessingException, JSONException {\n\t\tSecurityContext context = new SecurityContextImpl();\n\t\tcontext.setAuthentication(UsernamePasswordAuthenticationToken.authenticated(\"admin\", \"1234\",\n\t\t\t\tCollections.singleton(new SimpleGrantedAuthority(\"ROLE_USER\"))));\n\t\tString actualJson = this.mapper.writeValueAsString(context);\n\t\tJSONAssert.assertEquals(SECURITY_CONTEXT_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void securityContextDeserializeTest() throws IOException {\n\t\tSecurityContext context = this.mapper.readValue(SECURITY_CONTEXT_JSON, SecurityContextImpl.class);\n\t\tassertThat(context).isNotNull();\n\t\tassertThat(context.getAuthentication()).isNotNull().isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getPrincipal()).isEqualTo(\"admin\");\n\t\tassertThat(context.getAuthentication().getCredentials()).isEqualTo(\"1234\");\n\t\tassertThat(context.getAuthentication().isAuthenticated()).isTrue();\n\t\tCollection authorities = context.getAuthentication().getAuthorities();\n\t\tassertThat(authorities).hasSize(1);\n\t\tassertThat(authorities).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/SecurityJacksonModulesTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.databind.JacksonModule;\nimport tools.jackson.databind.json.JsonMapper;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class SecurityJacksonModulesTests {\n\n\t@Test\n\tpublic void addModulesWithNoTypeValidatorBuilder() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tList<JacksonModule> modules = SecurityJacksonModules.getModules(loader);\n\t\tJsonMapper mapper = JsonMapper.builder().addModules(modules).build();\n\t\tUser user = new User(\"user\", null, List.of(new SimpleGrantedAuthority(\"SCOPE_message:read\")));\n\t\tString json = mapper.writeValueAsString(user);\n\t\tUser deserializedUer = mapper.readerFor(User.class).readValue(json);\n\t\tassertThat(deserializedUer).isEqualTo(user);\n\t}\n\n\t@Test\n\tpublic void addModulesWithDefaultTypeValidatorBuilder() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tList<JacksonModule> modules = SecurityJacksonModules.getModules(loader,\n\t\t\t\tBasicPolymorphicTypeValidator.builder());\n\t\tJsonMapper mapper = JsonMapper.builder().addModules(modules).build();\n\t\tUser user = new User(\"user\", null, List.of(new SimpleGrantedAuthority(\"SCOPE_message:read\")));\n\t\tString json = mapper.writeValueAsString(user);\n\t\tUser deserializedUer = mapper.readerFor(User.class).readValue(json);\n\t\tassertThat(deserializedUer).isEqualTo(user);\n\t}\n\n\t@Test\n\tpublic void addModulesWithCustomTypeValidator() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tBasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()\n\t\t\t.allowIfSubType(TestGrantedAuthority.class);\n\t\tList<JacksonModule> modules = SecurityJacksonModules.getModules(loader, builder);\n\t\tJsonMapper mapper = JsonMapper.builder().addModules(modules).build();\n\t\tUser user = new User(\"user\", null, List.of(new TestGrantedAuthority()));\n\t\tString json = mapper.writeValueAsString(user);\n\t\tUser deserializedUer = mapper.readerFor(User.class).readValue(json);\n\t\tassertThat(deserializedUer).isEqualTo(user);\n\t}\n\n\t@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n\tprivate static class TestGrantedAuthority implements GrantedAuthority {\n\n\t\t@Override\n\t\tpublic String getAuthority() {\n\t\t\treturn \"test\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/SimpleGrantedAuthorityMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.exc.ValueInstantiationException;\n\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class SimpleGrantedAuthorityMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tpublic static final String AUTHORITY_JSON = \"{\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\", \\\"authority\\\": \\\"ROLE_USER\\\"}\";\n\tpublic static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", [\" + AUTHORITY_JSON + \"]]\";\n\tpublic static final String AUTHORITIES_SET_JSON = \"[\\\"java.util.Collections$UnmodifiableSet\\\", [\" + AUTHORITY_JSON + \"]]\";\n\tpublic static final String NO_AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", []]\";\n\tpublic static final String EMPTY_AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$EmptyList\\\", []]\";\n\tpublic static final String NO_AUTHORITIES_SET_JSON = \"[\\\"java.util.Collections$UnmodifiableSet\\\", []]\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeSimpleGrantedAuthorityTest() throws JsonProcessingException, JSONException {\n\t\tSimpleGrantedAuthority authority = new SimpleGrantedAuthority(\"ROLE_USER\");\n\t\tString serializeJson = this.mapper.writeValueAsString(authority);\n\t\tJSONAssert.assertEquals(AUTHORITY_JSON, serializeJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeGrantedAuthorityTest() throws IOException {\n\t\tSimpleGrantedAuthority authority = this.mapper.readValue(AUTHORITY_JSON, SimpleGrantedAuthority.class);\n\t\tassertThat(authority).isNotNull();\n\t\tassertThat(authority.getAuthority()).isNotNull().isEqualTo(\"ROLE_USER\");\n\t}\n\n\t@Test\n\tpublic void deserializeGrantedAuthorityWithoutRoleTest() throws IOException {\n\t\tString json = \"{\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\"}\";\n\t\tassertThatExceptionOfType(ValueInstantiationException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(json, SimpleGrantedAuthority.class));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/TestingAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link TestingAuthenticationTokenMixin}.\n *\n * @author Rob Winch\n * @since 7.0\n */\nclass TestingAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\tprivate static final String EXPECTED_JSON = \"\"\"\n\t\t\t{\n\t\t\t\t\"@class\": \"org.springframework.security.authentication.TestingAuthenticationToken\",\n\t\t\t\t\"authorities\": [\n\t\t\t\t  \"java.util.Collections$UnmodifiableRandomAccessList\",\n\t\t\t\t  [\n\t\t\t\t    {\n\t\t\t\t      \"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\",\n\t\t\t\t      \"authority\": \"ROLE_A\"\n\t\t\t\t    },\n\t\t\t\t    {\n\t\t\t\t      \"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\",\n\t\t\t\t      \"authority\": \"ROLE_B\"\n\t\t\t\t    }\n\t\t\t\t  ]\n\t\t\t\t],\n\t\t\t\t\"details\": null,\n\t\t\t\t\"authenticated\": true,\n\t\t\t\t\"credentials\": null,\n\t\t\t\t\"principal\": \"principal\"\n\t\t\t}\"\"\";\n\n\tprivate TestingAuthenticationToken expectedToken = new TestingAuthenticationToken(\"principal\", null, \"ROLE_A\",\n\t\t\t\"ROLE_B\");\n\n\t@Test\n\tvoid serialize() throws Exception {\n\t\tString json = this.mapper.writeValueAsString(this.expectedToken);\n\t\tJSONAssert.assertEquals(EXPECTED_JSON, json, true);\n\t}\n\n\t@Test\n\tvoid deserialize() {\n\t\tTestingAuthenticationToken actual = (TestingAuthenticationToken) this.mapper.readValue(EXPECTED_JSON,\n\t\t\t\tObject.class);\n\t\tassertThat(actual).isEqualTo(this.expectedToken);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/UnmodifiableMapTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass UnmodifiableMapTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String DEFAULT_MAP_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\"\n\t\t\t+ \"\\\"Key\\\": \\\"Value\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tString mapJson = mapper\n\t\t\t.writeValueAsString(Collections.unmodifiableMap(Collections.singletonMap(\"Key\", \"Value\")));\n\n\t\tJSONAssert.assertEquals(DEFAULT_MAP_JSON, mapJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tMap<String, String> map = mapper.readValue(DEFAULT_MAP_JSON,\n\t\t\t\tCollections.unmodifiableMap(Collections.emptyMap()).getClass());\n\n\t\tassertThat(map).isNotNull()\n\t\t\t.isInstanceOf(Collections.unmodifiableMap(Collections.emptyMap()).getClass())\n\t\t\t.containsAllEntriesOf(Collections.singletonMap(\"Key\", \"Value\"));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/UserDeserializerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.regex.Pattern;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.exc.MismatchedInputException;\nimport tools.jackson.databind.json.JsonMapper;\nimport tools.jackson.databind.node.ObjectNode;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class UserDeserializerTests extends AbstractMixinTests {\n\n\tpublic static final String USER_PASSWORD = \"\\\"1234\\\"\";\n\n\t// @formatter:off\n\tpublic static final String USER_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.core.userdetails.User\\\", \"\n\t\t+ \"\\\"username\\\": \\\"admin\\\",\"\n\t\t+ \" \\\"password\\\": \" + USER_PASSWORD + \", \"\n\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t+ \"\\\"enabled\\\": true, \"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeUserTest() throws JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tString userJson = this.mapper.writeValueAsString(user);\n\t\tJSONAssert.assertEquals(userWithPasswordJson(user.getPassword()), userJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeUserWithoutAuthority() throws JsonProcessingException, JSONException {\n\t\tUser user = new User(\"admin\", \"1234\", Collections.<GrantedAuthority>emptyList());\n\t\tString userJson = this.mapper.writeValueAsString(user);\n\t\tJSONAssert.assertEquals(userWithNoAuthoritiesJson(), userJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeUserWithNullPasswordEmptyAuthorityTest() throws IOException {\n\t\tString userJsonWithoutPasswordString = USER_JSON.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,\n\t\t\t\t\"[]\");\n\t\tassertThatExceptionOfType(MismatchedInputException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(userJsonWithoutPasswordString, User.class));\n\t}\n\n\t@Test\n\tpublic void deserializeUserWithNullPasswordNoAuthorityTest() throws Exception {\n\t\tString userJsonWithoutPasswordString = removeNode(userWithNoAuthoritiesJson(), this.mapper, \"password\");\n\t\tUser user = this.mapper.readValue(userJsonWithoutPasswordString, User.class);\n\t\tassertThat(user).isNotNull();\n\t\tassertThat(user.getUsername()).isEqualTo(\"admin\");\n\t\tassertThat(user.getPassword()).isNull();\n\t\tassertThat(user.getAuthorities()).isEmpty();\n\t\tassertThat(user.isEnabled()).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void deserializeUserWithNoClassIdInAuthoritiesTest() throws Exception {\n\t\tString userJson = USER_JSON.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,\n\t\t\t\t\"[{\\\"authority\\\": \\\"ROLE_USER\\\"}]\");\n\t\tassertThatExceptionOfType(MismatchedInputException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(userJson, User.class));\n\t}\n\n\t@Test\n\tpublic void deserializeUserWithClassIdInAuthoritiesTest() {\n\t\tUser user = this.mapper.readValue(userJson(), User.class);\n\t\tassertThat(user).isNotNull();\n\t\tassertThat(user.getUsername()).isEqualTo(\"admin\");\n\t\tassertThat(user.getPassword()).isEqualTo(\"1234\");\n\t\tassertThat(user.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n\tprivate String removeNode(String json, JsonMapper mapper, String toRemove) throws Exception {\n\t\tObjectNode node = mapper.createParser(json).readValueAsTree();\n\t\tnode.remove(toRemove);\n\t\tString result = mapper.writeValueAsString(node);\n\t\tJSONAssert.assertNotEquals(json, result, false);\n\t\treturn result;\n\t}\n\n\tpublic static String userJson() {\n\t\treturn USER_JSON;\n\t}\n\n\tpublic static String userWithPasswordJson(String password) {\n\t\treturn userJson().replaceAll(Pattern.quote(USER_PASSWORD), \"\\\"\" + password + \"\\\"\");\n\t}\n\n\tpublic static String userWithNoAuthoritiesJson() {\n\t\treturn userJson().replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,\n\t\t\t\tSimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_SET_JSON);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson/UsernamePasswordAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\n\nimport com.fasterxml.jackson.annotation.JsonClassDescription;\nimport com.fasterxml.jackson.annotation.JsonInclude.Include;\nimport com.fasterxml.jackson.annotation.JsonInclude.Value;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @author Greg Turnquist\n * @author Onur Kagan Ozcan\n * @since 4.2\n */\npublic class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\tprivate static final String AUTHENTICATED_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\\\",\"\n\t\t\t+ \"\\\"principal\\\": \" + UserDeserializerTests.USER_JSON + \", \" + \"\\\"credentials\\\": \\\"1234\\\", \"\n\t\t\t+ \"\\\"authenticated\\\": true, \" + \"\\\"details\\\": null, \" + \"\\\"authorities\\\": \"\n\t\t\t+ SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON + \"}\";\n\n\tpublic static final String AUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_JSON\n\t\t.replace(UserDeserializerTests.USER_JSON, \"\\\"admin\\\"\");\n\n\tprivate static final String NON_USER_PRINCIPAL_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.jackson.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal\\\", \"\n\t\t\t+ \"\\\"username\\\": \\\"admin\\\"\" + \"}\";\n\n\tprivate static final String AUTHENTICATED_STRINGDETAILS_JSON = AUTHENTICATED_JSON.replace(\"\\\"details\\\": null, \",\n\t\t\t\"\\\"details\\\": \\\"details\\\", \");\n\n\tprivate static final String AUTHENTICATED_NON_USER_PRINCIPAL_JSON = AUTHENTICATED_JSON\n\t\t.replace(UserDeserializerTests.USER_JSON, NON_USER_PRINCIPAL_JSON)\n\t\t.replaceAll(UserDeserializerTests.USER_PASSWORD, \"null\")\n\t\t.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON,\n\t\t\t\tSimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_ARRAYLIST_JSON);\n\n\tprivate static final String UNAUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_STRINGPRINCIPAL_JSON\n\t\t.replace(\"\\\"authenticated\\\": true, \", \"\\\"authenticated\\\": false, \")\n\t\t.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON,\n\t\t\t\tSimpleGrantedAuthorityMixinTests.EMPTY_AUTHORITIES_ARRAYLIST_JSON);\n\n\t@Test\n\tpublic void serializeUnauthenticatedUsernamePasswordAuthenticationTokenMixinTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"admin\",\n\t\t\t\t\"1234\");\n\t\tString serializedJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(UNAUTHENTICATED_STRINGPRINCIPAL_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken\n\t\t\t.authenticated(user.getUsername(), user.getPassword(), user.getAuthorities());\n\t\tString serializedJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(AUTHENTICATED_STRINGPRINCIPAL_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeUnauthenticatedUsernamePasswordAuthenticationTokenMixinTest() {\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(UNAUTHENTICATED_STRINGPRINCIPAL_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.isAuthenticated()).isEqualTo(false);\n\t\tassertThat(token.getAuthorities()).isNotNull().hasSize(0);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest() {\n\t\tUsernamePasswordAuthenticationToken expectedToken = createToken();\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_STRINGPRINCIPAL_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t\tassertThat(token.getAuthorities()).isEqualTo(expectedToken.getAuthorities());\n\t}\n\n\t@Test\n\tpublic void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithUserTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUsernamePasswordAuthenticationToken token = createToken();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(AUTHENTICATED_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithUserTest() throws IOException {\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(((User) token.getPrincipal()).getAuthorities()).isNotNull()\n\t\t\t.hasSize(1)\n\t\t\t.contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(token.isAuthenticated()).isEqualTo(true);\n\t\tassertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinAfterEraseCredentialInvoked()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUsernamePasswordAuthenticationToken token = createToken();\n\t\ttoken.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(AUTHENTICATED_JSON.replaceAll(UserDeserializerTests.USER_PASSWORD, \"null\"), actualJson,\n\t\t\t\ttrue);\n\t}\n\n\t@Test\n\tpublic void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithNonUserPrincipalTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tNonUserPrincipal principal = new NonUserPrincipal();\n\t\tprincipal.setUsername(\"admin\");\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(principal, null,\n\t\t\t\tnew ArrayList<>());\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(AUTHENTICATED_NON_USER_PRINCIPAL_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithNonUserPrincipalTest()\n\t\t\tthrows IOException {\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_NON_USER_PRINCIPAL_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(NonUserPrincipal.class);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithDetailsTest() {\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_STRINGDETAILS_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(((User) token.getPrincipal()).getAuthorities()).isNotNull()\n\t\t\t.hasSize(1)\n\t\t\t.contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(token.isAuthenticated()).isEqualTo(true);\n\t\tassertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(token.getDetails()).isExactlyInstanceOf(String.class).isEqualTo(\"details\");\n\t}\n\n\t@Test\n\tpublic void serializingThenDeserializingWithNoCredentialsOrDetailsShouldWork() {\n\t\tUsernamePasswordAuthenticationToken original = UsernamePasswordAuthenticationToken.unauthenticated(\"Frodo\",\n\t\t\t\tnull);\n\t\tString serialized = this.mapper.writeValueAsString(original);\n\t\tUsernamePasswordAuthenticationToken deserialized = this.mapper.readValue(serialized,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(deserialized).isEqualTo(original);\n\t}\n\n\t@Test\n\tpublic void serializingThenDeserializingWithConfiguredJsontMapperShouldWork() {\n\t\tJsonMapper jsonMapper = this.mapper.rebuild()\n\t\t\t.changeDefaultPropertyInclusion((p) -> Value.construct(Include.NON_ABSENT, Include.NON_ABSENT))\n\t\t\t.build();\n\n\t\tUsernamePasswordAuthenticationToken original = UsernamePasswordAuthenticationToken.unauthenticated(\"Frodo\",\n\t\t\t\tnull);\n\t\tString serialized = jsonMapper.writeValueAsString(original);\n\t\tUsernamePasswordAuthenticationToken deserialized = jsonMapper.readValue(serialized,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(deserialized).isEqualTo(original);\n\t}\n\n\tprivate UsernamePasswordAuthenticationToken createToken() {\n\t\tUser user = createDefaultUser();\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(user,\n\t\t\t\tuser.getPassword(), user.getAuthorities());\n\t\treturn token;\n\t}\n\n\t@JsonClassDescription\n\tpublic static class NonUserPrincipal {\n\n\t\tprivate String username;\n\n\t\tpublic String getUsername() {\n\t\t\treturn this.username;\n\t\t}\n\n\t\tpublic void setUsername(String username) {\n\t\t\tthis.username = username;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/AbstractMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\n\n/**\n * @author Jitenra Singh\n * @since 4.2\n */\n@SuppressWarnings(\"removal\")\npublic abstract class AbstractMixinTests {\n\n\tprotected ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\tUser createDefaultUser() {\n\t\treturn createUser(\"admin\", \"1234\", \"ROLE_USER\");\n\t}\n\n\tUser createUser(String username, String password, String authority) {\n\t\treturn new User(username, password, AuthorityUtils.createAuthorityList(authority));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/AnonymousAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class AnonymousAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\tprivate static final String HASH_KEY = \"key\";\n\n\t// @formatter:off\n\tprivate static final String ANONYMOUS_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.AnonymousAuthenticationToken\\\", \"\n\t\t+ \"\\\"details\\\": null,\"\n\t\t+ \"\\\"principal\\\": \" + UserDeserializerTests.USER_JSON + \",\"\n\t\t+ \"\\\"authenticated\\\": true, \"\n\t\t+ \"\\\"keyHash\\\": \" + HASH_KEY.hashCode() + \",\"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeAnonymousAuthenticationTokenTest() throws JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(HASH_KEY, user, user.getAuthorities());\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(ANONYMOUS_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeAnonymousAuthenticationTokenTest() throws IOException {\n\t\tAnonymousAuthenticationToken token = this.mapper.readValue(ANONYMOUS_JSON, AnonymousAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getKeyHash()).isEqualTo(HASH_KEY.hashCode());\n\t\tassertThat(token.getAuthorities()).isNotNull().hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void deserializeAnonymousAuthenticationTokenWithoutAuthoritiesTest() throws IOException {\n\t\tString jsonString = \"{\\\"@class\\\": \\\"org.springframework.security.authentication.AnonymousAuthenticationToken\\\", \\\"details\\\": null,\"\n\t\t\t\t+ \"\\\"principal\\\": \\\"user\\\", \\\"authenticated\\\": true, \\\"keyHash\\\": \" + HASH_KEY.hashCode() + \",\"\n\t\t\t\t+ \"\\\"authorities\\\": [\\\"java.util.ArrayList\\\", []]}\";\n\t\tassertThatExceptionOfType(JsonMappingException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(jsonString, AnonymousAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void serializeAnonymousAuthenticationTokenMixinAfterEraseCredentialTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(HASH_KEY, user, user.getAuthorities());\n\t\ttoken.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(ANONYMOUS_JSON.replace(UserDeserializerTests.USER_PASSWORD, \"null\"), actualJson, true);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/BadCredentialsExceptionMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.BadCredentialsException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Yannick Lombardi\n * @since 5.0\n */\npublic class BadCredentialsExceptionMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String EXCEPTION_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.BadCredentialsException\\\",\"\n\t\t+ \"\\\"localizedMessage\\\": \\\"message\\\", \"\n\t\t+ \"\\\"message\\\": \\\"message\\\", \"\n\t\t+ \"\\\"suppressed\\\": [\\\"[Ljava.lang.Throwable;\\\",[]]\"\n\t\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeBadCredentialsExceptionMixinTest() throws JsonProcessingException, JSONException {\n\t\tBadCredentialsException exception = new BadCredentialsException(\"message\");\n\t\tString serializedJson = this.mapper.writeValueAsString(exception);\n\t\tJSONAssert.assertEquals(EXCEPTION_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeBadCredentialsExceptionMixinTest() throws IOException {\n\t\tBadCredentialsException exception = this.mapper.readValue(EXCEPTION_JSON, BadCredentialsException.class);\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getCause()).isNull();\n\t\tassertThat(exception.getMessage()).isEqualTo(\"message\");\n\t\tassertThat(exception.getLocalizedMessage()).isEqualTo(\"message\");\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/FactorGrantedAuthorityMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.time.Instant;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 7.0\n */\nclass FactorGrantedAuthorityMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tpublic static final String AUTHORITY_JSON = \"{\\\"@class\\\": \\\"org.springframework.security.core.authority.FactorGrantedAuthority\\\", \\\"authority\\\": \\\"FACTOR_PASSWORD\\\", \\\"issuedAt\\\": 1759177143.043000000 }\";\n\n\tprivate Instant issuedAt = Instant.ofEpochMilli(1759177143043L);\n\n\t// @formatter:on\n\n\t@Test\n\tvoid serializeSimpleGrantedAuthorityTest() throws JsonProcessingException, JSONException {\n\t\tGrantedAuthority authority = FactorGrantedAuthority.withAuthority(\"FACTOR_PASSWORD\")\n\t\t\t.issuedAt(this.issuedAt)\n\t\t\t.build();\n\t\tString serializeJson = this.mapper.writeValueAsString(authority);\n\t\tJSONAssert.assertEquals(AUTHORITY_JSON, serializeJson, true);\n\t}\n\n\t@Test\n\tvoid deserializeGrantedAuthorityTest() throws IOException {\n\t\tFactorGrantedAuthority authority = (FactorGrantedAuthority) this.mapper.readValue(AUTHORITY_JSON, Object.class);\n\t\tassertThat(authority).isNotNull();\n\t\tassertThat(authority.getAuthority()).isEqualTo(\"FACTOR_PASSWORD\");\n\t\tassertThat(authority.getIssuedAt()).isEqualTo(this.issuedAt);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/RememberMeAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.Collections;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class RememberMeAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\tprivate static final String REMEMBERME_KEY = \"rememberMe\";\n\n\t// @formatter:off\n\tprivate static final String REMEMBERME_AUTH_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.RememberMeAuthenticationToken\\\", \"\n\t\t+ \"\\\"keyHash\\\": \" + REMEMBERME_KEY.hashCode() + \", \"\n\t\t+ \"\\\"authenticated\\\": true, \\\"details\\\": null\" + \", \"\n\t\t+ \"\\\"principal\\\": \" + UserDeserializerTests.USER_JSON + \", \"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON\n\t+ \"}\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String REMEMBERME_AUTH_STRINGPRINCIPAL_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.RememberMeAuthenticationToken\\\",\"\n\t\t+ \"\\\"keyHash\\\": \" + REMEMBERME_KEY.hashCode() + \", \"\n\t\t+ \"\\\"authenticated\\\": true, \"\n\t\t+ \"\\\"details\\\": null,\"\n\t\t+ \"\\\"principal\\\": \\\"admin\\\", \"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON\n\t+ \"}\";\n\t// @formatter:on\n\n\t@Test\n\tpublic void testWithNullPrincipal() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new RememberMeAuthenticationToken(\"key\", null, Collections.<GrantedAuthority>emptyList()));\n\t}\n\n\t@Test\n\tpublic void testWithNullKey() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new RememberMeAuthenticationToken(null, \"principal\", Collections.<GrantedAuthority>emptyList()));\n\t}\n\n\t@Test\n\tpublic void serializeRememberMeAuthenticationToken() throws JsonProcessingException, JSONException {\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, \"admin\",\n\t\t\t\tCollections.singleton(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(REMEMBERME_AUTH_STRINGPRINCIPAL_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeRememberMeAuthenticationWithUserToken() throws JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, user,\n\t\t\t\tuser.getAuthorities());\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(String.format(REMEMBERME_AUTH_JSON, \"\\\"password\\\"\"), actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeRememberMeAuthenticationWithUserTokenAfterEraseCredential()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tRememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, user,\n\t\t\t\tuser.getAuthorities());\n\t\ttoken.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(REMEMBERME_AUTH_JSON.replace(UserDeserializerTests.USER_PASSWORD, \"null\"), actualJson,\n\t\t\t\ttrue);\n\t}\n\n\t@Test\n\tpublic void deserializeRememberMeAuthenticationToken() throws IOException {\n\t\tRememberMeAuthenticationToken token = this.mapper.readValue(REMEMBERME_AUTH_STRINGPRINCIPAL_JSON,\n\t\t\t\tRememberMeAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isEqualTo(\"admin\").isEqualTo(token.getName());\n\t\tassertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void deserializeRememberMeAuthenticationTokenWithUserTest() throws IOException {\n\t\tRememberMeAuthenticationToken token = this.mapper.readValue(String.format(REMEMBERME_AUTH_JSON, \"\\\"password\\\"\"),\n\t\t\t\tRememberMeAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(((User) token.getPrincipal()).getUsername()).isEqualTo(\"admin\");\n\t\tassertThat(((User) token.getPrincipal()).getPassword()).isEqualTo(\"1234\");\n\t\tassertThat(((User) token.getPrincipal()).getAuthorities()).hasSize(1)\n\t\t\t.contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(((User) token.getPrincipal()).isEnabled()).isEqualTo(true);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/SecurityContextMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class SecurityContextMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tpublic static final String SECURITY_CONTEXT_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.core.context.SecurityContextImpl\\\", \"\n\t\t+ \"\\\"authentication\\\": \" + UsernamePasswordAuthenticationTokenMixinTests.AUTHENTICATED_STRINGPRINCIPAL_JSON\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void securityContextSerializeTest() throws JsonProcessingException, JSONException {\n\t\tSecurityContext context = new SecurityContextImpl();\n\t\tcontext.setAuthentication(UsernamePasswordAuthenticationToken.authenticated(\"admin\", \"1234\",\n\t\t\t\tCollections.singleton(new SimpleGrantedAuthority(\"ROLE_USER\"))));\n\t\tString actualJson = this.mapper.writeValueAsString(context);\n\t\tJSONAssert.assertEquals(SECURITY_CONTEXT_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void securityContextDeserializeTest() throws IOException {\n\t\tSecurityContext context = this.mapper.readValue(SECURITY_CONTEXT_JSON, SecurityContextImpl.class);\n\t\tassertThat(context).isNotNull();\n\t\tassertThat(context.getAuthentication()).isNotNull().isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getPrincipal()).isEqualTo(\"admin\");\n\t\tassertThat(context.getAuthentication().getCredentials()).isEqualTo(\"1234\");\n\t\tassertThat(context.getAuthentication().isAuthenticated()).isTrue();\n\t\tCollection authorities = context.getAuthentication().getAuthorities();\n\t\tassertThat(authorities).hasSize(1);\n\t\tassertThat(authorities).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/SecurityJackson2ModulesTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.HashMap;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonIgnoreType;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@SuppressWarnings(\"removal\")\npublic class SecurityJackson2ModulesTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tSecurityJackson2Modules.enableDefaultTyping(this.mapper);\n\t}\n\n\t@Test\n\tpublic void readValueWhenNotAllowedOrMappedThenThrowsException() {\n\t\tString content = \"{\\\"@class\\\":\\\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\\\",\\\"property\\\":\\\"bar\\\"}\";\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t\t.isThrownBy(() -> this.mapper.readValue(content, Object.class))\n\t\t\t\t.withStackTraceContaining(\"allowlist\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void readValueWhenExplicitDefaultTypingAfterSecuritySetupThenReadsAsSpecificType() throws Exception {\n\t\tthis.mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);\n\t\tString content = \"{\\\"@class\\\":\\\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\\\",\\\"property\\\":\\\"bar\\\"}\";\n\t\tassertThat(this.mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);\n\t}\n\n\t@Test\n\tpublic void readValueWhenExplicitDefaultTypingBeforeSecuritySetupThenReadsAsSpecificType() throws Exception {\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);\n\t\tSecurityJackson2Modules.enableDefaultTyping(this.mapper);\n\t\tString content = \"{\\\"@class\\\":\\\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\\\",\\\"property\\\":\\\"bar\\\"}\";\n\t\tassertThat(this.mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);\n\t}\n\n\t@Test\n\tpublic void readValueWhenAnnotatedThenReadsAsSpecificType() throws Exception {\n\t\tString content = \"{\\\"@class\\\":\\\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlistedButAnnotated\\\",\\\"property\\\":\\\"bar\\\"}\";\n\t\tassertThat(this.mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlistedButAnnotated.class);\n\t}\n\n\t@Test\n\tpublic void readValueWhenMixinProvidedThenReadsAsSpecificType() throws Exception {\n\t\tthis.mapper.addMixIn(NotAllowlisted.class, NotAllowlistedMixin.class);\n\t\tString content = \"{\\\"@class\\\":\\\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\\\",\\\"property\\\":\\\"bar\\\"}\";\n\t\tassertThat(this.mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);\n\t}\n\n\t@Test\n\tpublic void readValueWhenHashMapThenReadsAsSpecificType() throws Exception {\n\t\tthis.mapper.addMixIn(NotAllowlisted.class, NotAllowlistedMixin.class);\n\t\tString content = \"{\\\"@class\\\":\\\"java.util.HashMap\\\"}\";\n\t\tassertThat(this.mapper.readValue(content, Object.class)).isInstanceOf(HashMap.class);\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Documented\n\tpublic @interface NotJacksonAnnotation {\n\n\t}\n\n\t@NotJacksonAnnotation\n\tstatic class NotAllowlisted {\n\n\t\tprivate String property = \"bar\";\n\n\t\tString getProperty() {\n\t\t\treturn this.property;\n\t\t}\n\n\t\tvoid setProperty(String property) {\n\t\t}\n\n\t}\n\n\t@JsonIgnoreType(false)\n\tstatic class NotAllowlistedButAnnotated {\n\n\t\tprivate String property = \"bar\";\n\n\t\tString getProperty() {\n\t\t\treturn this.property;\n\t\t}\n\n\t\tvoid setProperty(String property) {\n\t\t}\n\n\t}\n\n\t@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n\t@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n\t@JsonIgnoreProperties(ignoreUnknown = true)\n\tabstract class NotAllowlistedMixin {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class SimpleGrantedAuthorityMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tpublic static final String AUTHORITY_JSON = \"{\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\", \\\"authority\\\": \\\"ROLE_USER\\\"}\";\n\tpublic static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", [\" + AUTHORITY_JSON + \"]]\";\n\tpublic static final String AUTHORITIES_SET_JSON = \"[\\\"java.util.Collections$UnmodifiableSet\\\", [\" + AUTHORITY_JSON + \"]]\";\n\tpublic static final String NO_AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", []]\";\n\tpublic static final String EMPTY_AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$EmptyList\\\", []]\";\n\tpublic static final String NO_AUTHORITIES_SET_JSON = \"[\\\"java.util.Collections$UnmodifiableSet\\\", []]\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeSimpleGrantedAuthorityTest() throws JsonProcessingException, JSONException {\n\t\tSimpleGrantedAuthority authority = new SimpleGrantedAuthority(\"ROLE_USER\");\n\t\tString serializeJson = this.mapper.writeValueAsString(authority);\n\t\tJSONAssert.assertEquals(AUTHORITY_JSON, serializeJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeGrantedAuthorityTest() throws IOException {\n\t\tSimpleGrantedAuthority authority = this.mapper.readValue(AUTHORITY_JSON, SimpleGrantedAuthority.class);\n\t\tassertThat(authority).isNotNull();\n\t\tassertThat(authority.getAuthority()).isNotNull().isEqualTo(\"ROLE_USER\");\n\t}\n\n\t@Test\n\tpublic void deserializeGrantedAuthorityWithoutRoleTest() throws IOException {\n\t\tString json = \"{\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\"}\";\n\t\tassertThatExceptionOfType(JsonMappingException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(json, SimpleGrantedAuthority.class));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/UnmodifiableMapDeserializerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass UnmodifiableMapDeserializerTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String DEFAULT_MAP_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\"\n\t\t\t+ \"\\\"Key\\\": \\\"Value\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tString mapJson = mapper\n\t\t\t.writeValueAsString(Collections.unmodifiableMap(Collections.singletonMap(\"Key\", \"Value\")));\n\n\t\tJSONAssert.assertEquals(DEFAULT_MAP_JSON, mapJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tMap<String, String> map = mapper.readValue(DEFAULT_MAP_JSON,\n\t\t\t\tCollections.unmodifiableMap(Collections.emptyMap()).getClass());\n\n\t\tassertThat(map).isNotNull()\n\t\t\t.isInstanceOf(Collections.unmodifiableMap(Collections.emptyMap()).getClass())\n\t\t\t.containsAllEntriesOf(Collections.singletonMap(\"Key\", \"Value\"));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/UserDeserializerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.regex.Pattern;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class UserDeserializerTests extends AbstractMixinTests {\n\n\tpublic static final String USER_PASSWORD = \"\\\"1234\\\"\";\n\n\t// @formatter:off\n\tpublic static final String USER_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.core.userdetails.User\\\", \"\n\t\t+ \"\\\"username\\\": \\\"admin\\\",\"\n\t\t+ \" \\\"password\\\": \" + USER_PASSWORD + \", \"\n\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t+ \"\\\"enabled\\\": true, \"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeUserTest() throws JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tString userJson = this.mapper.writeValueAsString(user);\n\t\tJSONAssert.assertEquals(userWithPasswordJson(user.getPassword()), userJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeUserWithoutAuthority() throws JsonProcessingException, JSONException {\n\t\tUser user = new User(\"admin\", \"1234\", Collections.<GrantedAuthority>emptyList());\n\t\tString userJson = this.mapper.writeValueAsString(user);\n\t\tJSONAssert.assertEquals(userWithNoAuthoritiesJson(), userJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeUserWithNullPasswordEmptyAuthorityTest() throws IOException {\n\t\tString userJsonWithoutPasswordString = USER_JSON.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,\n\t\t\t\t\"[]\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.mapper.readValue(userJsonWithoutPasswordString, User.class));\n\t}\n\n\t@Test\n\tpublic void deserializeUserWithNullPasswordNoAuthorityTest() throws Exception {\n\t\tString userJsonWithoutPasswordString = removeNode(userWithNoAuthoritiesJson(), this.mapper, \"password\");\n\t\tUser user = this.mapper.readValue(userJsonWithoutPasswordString, User.class);\n\t\tassertThat(user).isNotNull();\n\t\tassertThat(user.getUsername()).isEqualTo(\"admin\");\n\t\tassertThat(user.getPassword()).isNull();\n\t\tassertThat(user.getAuthorities()).isEmpty();\n\t\tassertThat(user.isEnabled()).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void deserializeUserWithNoClassIdInAuthoritiesTest() throws Exception {\n\t\tString userJson = USER_JSON.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,\n\t\t\t\t\"[{\\\"authority\\\": \\\"ROLE_USER\\\"}]\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.mapper.readValue(userJson, User.class));\n\t}\n\n\t@Test\n\tpublic void deserializeUserWithClassIdInAuthoritiesTest() throws IOException {\n\t\tUser user = this.mapper.readValue(userJson(), User.class);\n\t\tassertThat(user).isNotNull();\n\t\tassertThat(user.getUsername()).isEqualTo(\"admin\");\n\t\tassertThat(user.getPassword()).isEqualTo(\"1234\");\n\t\tassertThat(user.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n\tprivate String removeNode(String json, ObjectMapper mapper, String toRemove) throws Exception {\n\t\tObjectNode node = mapper.getFactory().createParser(json).readValueAsTree();\n\t\tnode.remove(toRemove);\n\t\tString result = mapper.writeValueAsString(node);\n\t\tJSONAssert.assertNotEquals(json, result, false);\n\t\treturn result;\n\t}\n\n\tpublic static String userJson() {\n\t\treturn USER_JSON;\n\t}\n\n\tpublic static String userWithPasswordJson(String password) {\n\t\treturn userJson().replaceAll(Pattern.quote(USER_PASSWORD), \"\\\"\" + password + \"\\\"\");\n\t}\n\n\tpublic static String userWithNoAuthoritiesJson() {\n\t\treturn userJson().replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,\n\t\t\t\tSimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_SET_JSON);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.jackson2;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\n\nimport com.fasterxml.jackson.annotation.JsonClassDescription;\nimport com.fasterxml.jackson.annotation.JsonInclude.Include;\nimport com.fasterxml.jackson.annotation.JsonInclude.Value;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @author Greg Turnquist\n * @author Onur Kagan Ozcan\n * @since 4.2\n */\npublic class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\tprivate static final String AUTHENTICATED_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\\\",\"\n\t\t\t+ \"\\\"principal\\\": \" + UserDeserializerTests.USER_JSON + \", \" + \"\\\"credentials\\\": \\\"1234\\\", \"\n\t\t\t+ \"\\\"authenticated\\\": true, \" + \"\\\"details\\\": null, \" + \"\\\"authorities\\\": \"\n\t\t\t+ SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON + \"}\";\n\n\tpublic static final String AUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_JSON\n\t\t.replace(UserDeserializerTests.USER_JSON, \"\\\"admin\\\"\");\n\n\tprivate static final String NON_USER_PRINCIPAL_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.jackson2.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal\\\", \"\n\t\t\t+ \"\\\"username\\\": \\\"admin\\\"\" + \"}\";\n\n\tprivate static final String AUTHENTICATED_STRINGDETAILS_JSON = AUTHENTICATED_JSON.replace(\"\\\"details\\\": null, \",\n\t\t\t\"\\\"details\\\": \\\"details\\\", \");\n\n\tprivate static final String AUTHENTICATED_NON_USER_PRINCIPAL_JSON = AUTHENTICATED_JSON\n\t\t.replace(UserDeserializerTests.USER_JSON, NON_USER_PRINCIPAL_JSON)\n\t\t.replaceAll(UserDeserializerTests.USER_PASSWORD, \"null\")\n\t\t.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON,\n\t\t\t\tSimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_ARRAYLIST_JSON);\n\n\tprivate static final String UNAUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_STRINGPRINCIPAL_JSON\n\t\t.replace(\"\\\"authenticated\\\": true, \", \"\\\"authenticated\\\": false, \")\n\t\t.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON,\n\t\t\t\tSimpleGrantedAuthorityMixinTests.EMPTY_AUTHORITIES_ARRAYLIST_JSON);\n\n\t@Test\n\tpublic void serializeUnauthenticatedUsernamePasswordAuthenticationTokenMixinTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\"admin\",\n\t\t\t\t\"1234\");\n\t\tString serializedJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(UNAUTHENTICATED_STRINGPRINCIPAL_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUser user = createDefaultUser();\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken\n\t\t\t.authenticated(user.getUsername(), user.getPassword(), user.getAuthorities());\n\t\tString serializedJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(AUTHENTICATED_STRINGPRINCIPAL_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeUnauthenticatedUsernamePasswordAuthenticationTokenMixinTest() throws IOException {\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(UNAUTHENTICATED_STRINGPRINCIPAL_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.isAuthenticated()).isEqualTo(false);\n\t\tassertThat(token.getAuthorities()).isNotNull().hasSize(0);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest() throws IOException {\n\t\tUsernamePasswordAuthenticationToken expectedToken = createToken();\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_STRINGPRINCIPAL_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t\tassertThat(token.getAuthorities()).isEqualTo(expectedToken.getAuthorities());\n\t}\n\n\t@Test\n\tpublic void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithUserTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUsernamePasswordAuthenticationToken token = createToken();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(AUTHENTICATED_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithUserTest() throws IOException {\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(((User) token.getPrincipal()).getAuthorities()).isNotNull()\n\t\t\t.hasSize(1)\n\t\t\t.contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(token.isAuthenticated()).isEqualTo(true);\n\t\tassertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinAfterEraseCredentialInvoked()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tUsernamePasswordAuthenticationToken token = createToken();\n\t\ttoken.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(AUTHENTICATED_JSON.replaceAll(UserDeserializerTests.USER_PASSWORD, \"null\"), actualJson,\n\t\t\t\ttrue);\n\t}\n\n\t@Test\n\tpublic void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithNonUserPrincipalTest()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tNonUserPrincipal principal = new NonUserPrincipal();\n\t\tprincipal.setUsername(\"admin\");\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(principal, null,\n\t\t\t\tnew ArrayList<>());\n\t\tString actualJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(AUTHENTICATED_NON_USER_PRINCIPAL_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithNonUserPrincipalTest()\n\t\t\tthrows IOException {\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_NON_USER_PRINCIPAL_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(NonUserPrincipal.class);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithDetailsTest() throws IOException {\n\t\tUsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_STRINGDETAILS_JSON,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);\n\t\tassertThat(((User) token.getPrincipal()).getAuthorities()).isNotNull()\n\t\t\t.hasSize(1)\n\t\t\t.contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(token.isAuthenticated()).isEqualTo(true);\n\t\tassertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority(\"ROLE_USER\"));\n\t\tassertThat(token.getDetails()).isExactlyInstanceOf(String.class).isEqualTo(\"details\");\n\t}\n\n\t@Test\n\tpublic void serializingThenDeserializingWithNoCredentialsOrDetailsShouldWork() throws IOException {\n\t\tUsernamePasswordAuthenticationToken original = UsernamePasswordAuthenticationToken.unauthenticated(\"Frodo\",\n\t\t\t\tnull);\n\t\tString serialized = this.mapper.writeValueAsString(original);\n\t\tUsernamePasswordAuthenticationToken deserialized = this.mapper.readValue(serialized,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(deserialized).isEqualTo(original);\n\t}\n\n\t@Test\n\tpublic void serializingThenDeserializingWithConfiguredObjectMapperShouldWork() throws IOException {\n\t\tthis.mapper.setDefaultPropertyInclusion(Value.construct(Include.ALWAYS, Include.NON_NULL))\n\t\t\t.setSerializationInclusion(Include.NON_ABSENT);\n\t\tUsernamePasswordAuthenticationToken original = UsernamePasswordAuthenticationToken.unauthenticated(\"Frodo\",\n\t\t\t\tnull);\n\t\tString serialized = this.mapper.writeValueAsString(original);\n\t\tUsernamePasswordAuthenticationToken deserialized = this.mapper.readValue(serialized,\n\t\t\t\tUsernamePasswordAuthenticationToken.class);\n\t\tassertThat(deserialized).isEqualTo(original);\n\t}\n\n\tprivate UsernamePasswordAuthenticationToken createToken() {\n\t\tUser user = createDefaultUser();\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(user,\n\t\t\t\tuser.getPassword(), user.getAuthorities());\n\t\treturn token;\n\t}\n\n\t@JsonClassDescription\n\tpublic static class NonUserPrincipal {\n\n\t\tprivate String username;\n\n\t\tpublic String getUsername() {\n\t\t\treturn this.username;\n\t\t}\n\n\t\tpublic void setUsername(String username) {\n\t\t\tthis.username = username;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/provisioning/InMemoryUserDetailsManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.provisioning;\n\nimport java.util.Collection;\nimport java.util.Properties;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.dao.DaoAuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.CredentialsContainer;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatException;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class InMemoryUserDetailsManagerTests {\n\n\tprivate final UserDetails user = PasswordEncodedUser.user();\n\n\tprivate InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(this.user);\n\n\t@Test\n\tpublic void changePassword() {\n\t\tString newPassword = \"newPassword\";\n\t\tthis.manager.updatePassword(this.user, newPassword);\n\t\tassertThat(this.manager.loadUserByUsername(this.user.getUsername()).getPassword()).isEqualTo(newPassword);\n\t}\n\n\t@Test\n\tpublic void changePasswordWhenUsernameIsNotInLowercase() {\n\t\tUserDetails userNotLowerCase = User.withUserDetails(PasswordEncodedUser.user()).username(\"User\").build();\n\t\tString newPassword = \"newPassword\";\n\t\tthis.manager.updatePassword(userNotLowerCase, newPassword);\n\t\tassertThat(this.manager.loadUserByUsername(userNotLowerCase.getUsername()).getPassword())\n\t\t\t.isEqualTo(newPassword);\n\t}\n\n\t@Test\n\tpublic void changePasswordWhenUserNotFoundThenAppropriateException() {\n\t\tString newPassword = \"newPassword\";\n\t\tthis.manager = new InMemoryUserDetailsManager();\n\t\tassertThatException().isThrownBy(() -> this.manager.updatePassword(this.user, newPassword))\n\t\t\t.isNotInstanceOf(NullPointerException.class)\n\t\t\t// this is not failure to authenticate and UsernamePasswordNotFoundException\n\t\t\t// extends AuthenticationException\n\t\t\t.isNotInstanceOf(UsernameNotFoundException.class);\n\t}\n\n\t@Test\n\tpublic void constructorWhenUserPropertiesThenCreate() {\n\t\tProperties properties = new Properties();\n\t\tproperties.setProperty(\"joe\", \"{noop}joespassword,ROLE_A\");\n\t\tproperties.setProperty(\"bob\", \"{noop}bobspassword,ROLE_A,ROLE_B\");\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(properties);\n\t\tassertThat(manager.userExists(\"joe\")).isTrue();\n\t\tassertThat(manager.userExists(\"bob\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void constructorWhenUserPropertiesWithEmptyValueThenException() {\n\t\tProperties properties = new Properties();\n\t\tproperties.setProperty(\"joe\", \"\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new InMemoryUserDetailsManager(properties))\n\t\t\t.withMessage(\"The entry with username 'joe' could not be converted to an UserDetails\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenUserPropertiesNoRolesThenException() {\n\t\tProperties properties = new Properties();\n\t\tproperties.setProperty(\"joe\", \"{noop}joespassword\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new InMemoryUserDetailsManager(properties))\n\t\t\t.withMessage(\"The entry with username 'joe' could not be converted to an UserDetails\");\n\t}\n\n\t@Test\n\tpublic void changePasswordWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager((User) authentication.getPrincipal());\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(authentication));\n\t\tmanager.setSecurityContextHolderStrategy(strategy);\n\t\tmanager.changePassword(\"password\", \"newpassword\");\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void createUserWhenUserAlreadyExistsThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.manager.createUser(this.user))\n\t\t\t.withMessage(\"user should not exist\");\n\t}\n\n\t@Test\n\tpublic void createUserWhenInstanceOfMutableUserDetailsThenChangePasswordWorks() {\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();\n\t\tCustomUser user = new CustomUser(User.withUserDetails(PasswordEncodedUser.user()).build());\n\t\tAuthentication authentication = TestAuthentication.authenticated(user);\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(authentication));\n\t\tmanager.setSecurityContextHolderStrategy(strategy);\n\t\tmanager.createUser(user);\n\t\tString newPassword = \"newPassword\";\n\t\tmanager.changePassword(user.getPassword(), newPassword);\n\t\tassertThat(manager.loadUserByUsername(user.getUsername()).getPassword()).isEqualTo(newPassword);\n\t}\n\n\t@Test\n\tpublic void updateUserWhenUserDoesNotExistThenException() {\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.updateUser(this.user))\n\t\t\t.withMessage(\"user should exist\");\n\t}\n\n\t@Test\n\tpublic void loadUserByUsernameWhenUserNullThenException() {\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class)\n\t\t\t.isThrownBy(() -> manager.loadUserByUsername(this.user.getUsername()));\n\t}\n\n\t@Test\n\tpublic void loadUserByUsernameWhenNotInstanceOfCredentialsContainerThenReturnInstanceOfCredentialsContainer() {\n\t\tMutableUser user = new MutableUser(User.withUserDetails(PasswordEncodedUser.user()).build());\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(user);\n\t\tassertThat(user).isNotInstanceOf(CredentialsContainer.class);\n\t\tassertThat(manager.loadUserByUsername(user.getUsername())).isInstanceOf(CredentialsContainer.class);\n\t}\n\n\t@Test\n\tpublic void loadUserByUsernameWhenInstanceOfCredentialsContainerThenReturnInstance() {\n\t\tCustomUser user = new CustomUser(User.withUserDetails(PasswordEncodedUser.user()).build());\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(user);\n\t\tassertThat(manager.loadUserByUsername(user.getUsername())).isSameAs(user);\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"authenticationErrorCases\")\n\tvoid authenticateWhenInvalidMissingOrMalformedIdThenException(String username, String password,\n\t\t\tString expectedMessage) {\n\t\tUserDetails user = User.builder().username(username).password(password).roles(\"USER\").build();\n\t\tInMemoryUserDetailsManager userManager = new InMemoryUserDetailsManager(user);\n\n\t\tDaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(userManager);\n\t\tauthenticationProvider.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());\n\n\t\tAuthenticationManager authManager = new ProviderManager(authenticationProvider);\n\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> authManager.authenticate(new UsernamePasswordAuthenticationToken(username, \"password\")))\n\t\t\t.withMessage(expectedMessage);\n\t}\n\n\tprivate static Stream<Arguments> authenticationErrorCases() {\n\t\treturn Stream.of(Arguments\n\t\t\t.of(\"user\", \"password\", \"Given that there is no default password encoder configured, each \"\n\t\t\t\t\t+ \"password must have a password encoding prefix. Please either prefix this password with '{noop}' or set a default password encoder in `DelegatingPasswordEncoder`.\"),\n\t\t\t\tArguments.of(\"user\", \"bycrpt}password\",\n\t\t\t\t\t\t\"The name of the password encoder is improperly formatted or incomplete. The format should be '{ENCODER}password'.\"),\n\t\t\t\tArguments.of(\"user\", \"{bycrptpassword\",\n\t\t\t\t\t\t\"The name of the password encoder is improperly formatted or incomplete. The format should be '{ENCODER}password'.\"),\n\t\t\t\tArguments.of(\"user\", \"{ren&stimpy}password\",\n\t\t\t\t\t\t\"There is no password encoder mapped for the id 'ren&stimpy'. Check your configuration to ensure it matches one of the registered encoders.\"));\n\t}\n\n\tstatic class CustomUser implements MutableUserDetails, CredentialsContainer {\n\n\t\tprivate final UserDetails delegate;\n\n\t\tprivate String password;\n\n\t\tCustomUser(UserDetails user) {\n\t\t\tthis.delegate = user;\n\t\t\tthis.password = user.getPassword();\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\t\treturn this.delegate.getAuthorities();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPassword() {\n\t\t\treturn this.password;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setPassword(final String password) {\n\t\t\tthis.password = password;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getUsername() {\n\t\t\treturn this.delegate.getUsername();\n\t\t}\n\n\t\t@Override\n\t\tpublic void eraseCredentials() {\n\t\t\tthis.password = null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/provisioning/JdbcUserDetailsManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.provisioning;\n\nimport java.sql.SQLException;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.security.PopulatedDatabase;\nimport org.springframework.security.TestDataSource;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatException;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.mockito.BDDMockito.verify;\n\n/**\n * Tests for {@link JdbcUserDetailsManager}\n *\n * @author Luke Taylor\n * @author dae won\n * @author Junhyeok Lee\n */\npublic class JdbcUserDetailsManagerTests {\n\n\tprivate static final String SELECT_JOE_SQL = \"select * from users where username = 'joe'\";\n\n\tprivate static final String SELECT_JOE_AUTHORITIES_SQL = \"select * from authorities where username = 'joe'\";\n\n\tprivate static final UserDetails joe = new User(\"joe\", \"password\", true, true, true, true,\n\t\t\tAuthorityUtils.createAuthorityList(\"A\", \"C\", \"B\"));\n\n\tprivate static TestDataSource dataSource;\n\n\tprivate JdbcUserDetailsManager manager;\n\n\tprivate MockUserCache cache;\n\n\tprivate JdbcTemplate template;\n\n\t@BeforeAll\n\tpublic static void createDataSource() {\n\t\tdataSource = new TestDataSource(\"jdbcusermgrtest\");\n\t}\n\n\t@AfterAll\n\tpublic static void clearDataSource() throws Exception {\n\t\tdataSource.destroy();\n\t\tdataSource = null;\n\t}\n\n\t@BeforeEach\n\tpublic void initializeManagerAndCreateTables() {\n\t\tthis.manager = new JdbcUserDetailsManager();\n\t\tthis.cache = new MockUserCache();\n\t\tthis.manager.setUserCache(this.cache);\n\t\tthis.manager.setDataSource(dataSource);\n\t\tthis.manager.setCreateUserSql(JdbcUserDetailsManager.DEF_CREATE_USER_SQL);\n\t\tthis.manager.setUpdateUserSql(JdbcUserDetailsManager.DEF_UPDATE_USER_SQL);\n\t\tthis.manager.setUserExistsSql(JdbcUserDetailsManager.DEF_USER_EXISTS_SQL);\n\t\tthis.manager.setCreateAuthoritySql(JdbcUserDetailsManager.DEF_INSERT_AUTHORITY_SQL);\n\t\tthis.manager.setDeleteUserAuthoritiesSql(JdbcUserDetailsManager.DEF_DELETE_USER_AUTHORITIES_SQL);\n\t\tthis.manager.setDeleteUserSql(JdbcUserDetailsManager.DEF_DELETE_USER_SQL);\n\t\tthis.manager.setChangePasswordSql(JdbcUserDetailsManager.DEF_CHANGE_PASSWORD_SQL);\n\t\tthis.manager.initDao();\n\t\tthis.template = this.manager.getJdbcTemplate();\n\t\tthis.template.execute(\"create table users(username varchar(20) not null primary key,\"\n\t\t\t\t+ \"password varchar(20) not null, enabled boolean not null)\");\n\t\tthis.template\n\t\t\t.execute(\"create table authorities (username varchar(20) not null, authority varchar(20) not null, \"\n\t\t\t\t\t+ \"constraint fk_authorities_users foreign key(username) references users(username))\");\n\t\tPopulatedDatabase.createGroupTables(this.template);\n\t\tPopulatedDatabase.insertGroupData(this.template);\n\t}\n\n\t@AfterEach\n\tpublic void dropTablesAndClearContext() {\n\t\tthis.template.execute(\"drop table authorities\");\n\t\tthis.template.execute(\"drop table users\");\n\t\tthis.template.execute(\"drop table group_authorities\");\n\t\tthis.template.execute(\"drop table group_members\");\n\t\tthis.template.execute(\"drop table groups\");\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\tprivate void setUpAccLockingColumns() {\n\t\tthis.template.execute(\"alter table users add column acc_locked boolean default false not null\");\n\t\tthis.template.execute(\"alter table users add column acc_expired boolean default false not null\");\n\t\tthis.template.execute(\"alter table users add column creds_expired boolean default false not null\");\n\t\tthis.manager.setUsersByUsernameQuery(\n\t\t\t\t\"select username,password,enabled, acc_locked, acc_expired, creds_expired from users where username = ?\");\n\t\tthis.manager.setCreateUserSql(\n\t\t\t\t\"insert into users (username, password, enabled, acc_locked, acc_expired, creds_expired) values (?,?,?,?,?,?)\");\n\t\tthis.manager.setUpdateUserSql(\n\t\t\t\t\"update users set password = ?, enabled = ?, acc_locked=?, acc_expired=?, creds_expired=? where username = ?\");\n\t}\n\n\t@Test\n\tpublic void createUserInsertsCorrectData() {\n\t\tthis.manager.createUser(joe);\n\t\tUserDetails joe2 = this.manager.loadUserByUsername(\"joe\");\n\t\tassertThat(joe2).isEqualTo(joe);\n\t}\n\n\t@Test\n\tpublic void createUserInsertsCorrectDataWithLocking() {\n\t\tsetUpAccLockingColumns();\n\t\tUserDetails user = new User(\"joe\", \"pass\", true, false, true, false,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"A\", \"B\"));\n\t\tthis.manager.createUser(user);\n\t\tUserDetails user2 = this.manager.loadUserByUsername(user.getUsername());\n\t\tassertThat(user2).usingRecursiveComparison().isEqualTo(user);\n\t}\n\n\t@Test\n\tpublic void deleteUserRemovesUserDataAndAuthoritiesAndClearsCache() {\n\t\tinsertJoe();\n\t\tthis.manager.deleteUser(\"joe\");\n\t\tassertThat(this.template.queryForList(SELECT_JOE_SQL)).isEmpty();\n\t\tassertThat(this.template.queryForList(SELECT_JOE_AUTHORITIES_SQL)).isEmpty();\n\t\tassertThat(this.cache.getUserMap().containsKey(\"joe\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void updateUserChangesDataCorrectlyAndClearsCache() {\n\t\tinsertJoe();\n\t\tUser newJoe = new User(\"joe\", \"newpassword\", false, true, true, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(new String[] { \"D\", \"F\", \"E\" }));\n\t\tthis.manager.updateUser(newJoe);\n\t\tUserDetails joe = this.manager.loadUserByUsername(\"joe\");\n\t\tassertThat(joe).isEqualTo(newJoe);\n\t\tassertThat(this.cache.getUserMap().containsKey(\"joe\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void updateUserChangesDataCorrectlyAndClearsCacheWithLocking() {\n\t\tsetUpAccLockingColumns();\n\t\tinsertJoe();\n\t\tUser newJoe = new User(\"joe\", \"newpassword\", false, false, false, true,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"D\", \"F\", \"E\"));\n\t\tthis.manager.updateUser(newJoe);\n\t\tUserDetails joe = this.manager.loadUserByUsername(newJoe.getUsername());\n\t\tassertThat(joe).usingRecursiveComparison().isEqualTo(newJoe);\n\t\tassertThat(this.cache.getUserMap().containsKey(newJoe.getUsername())).isFalse();\n\t}\n\n\t@Test\n\tpublic void userExistsReturnsFalseForNonExistentUsername() {\n\t\tassertThat(this.manager.userExists(\"joe\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void userExistsReturnsFalseForNullUsername() {\n\t\tassertThat(this.manager.userExists(null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void userExistsReturnsTrueForExistingUsername() {\n\t\tinsertJoe();\n\t\tassertThat(this.manager.userExists(\"joe\")).isTrue();\n\t\tassertThat(this.cache.getUserMap()).containsKey(\"joe\");\n\t}\n\n\t@Test\n\tpublic void changePasswordFailsForUnauthenticatedUser() {\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.manager.changePassword(\"password\", \"newPassword\"));\n\t}\n\n\t@Test\n\tpublic void changePasswordSucceedsWithAuthenticatedUserAndNoAuthenticationManagerSet() {\n\t\tinsertJoe();\n\t\tauthenticateJoe();\n\t\tthis.manager.changePassword(\"wrongpassword\", \"newPassword\");\n\t\tUserDetails newJoe = this.manager.loadUserByUsername(\"joe\");\n\t\tassertThat(newJoe.getPassword()).isEqualTo(\"newPassword\");\n\t\tassertThat(this.cache.getUserMap().containsKey(\"joe\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void changePasswordWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tinsertJoe();\n\t\tAuthentication authentication = authenticateJoe();\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(authentication));\n\t\tgiven(strategy.createEmptyContext()).willReturn(new SecurityContextImpl());\n\t\tthis.manager.setSecurityContextHolderStrategy(strategy);\n\t\tthis.manager.changePassword(\"wrongpassword\", \"newPassword\");\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void changePasswordSucceedsWithIfReAuthenticationSucceeds() {\n\t\tinsertJoe();\n\t\tAuthentication currentAuth = authenticateJoe();\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(currentAuth)).willReturn(currentAuth);\n\t\tthis.manager.setAuthenticationManager(am);\n\t\tthis.manager.changePassword(\"password\", \"newPassword\");\n\t\tUserDetails newJoe = this.manager.loadUserByUsername(\"joe\");\n\t\tassertThat(newJoe.getPassword()).isEqualTo(\"newPassword\");\n\t\t// The password in the context should also be altered\n\t\tAuthentication newAuth = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(newAuth.getName()).isEqualTo(\"joe\");\n\t\tassertThat(newAuth.getDetails()).isEqualTo(currentAuth.getDetails());\n\t\tassertThat(newAuth.getCredentials()).isNull();\n\t\tassertThat(this.cache.getUserMap().containsKey(\"joe\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void changePasswordFailsIfReAuthenticationFails() {\n\t\tinsertJoe();\n\t\tauthenticateJoe();\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class))).willThrow(new BadCredentialsException(\"\"));\n\t\tthis.manager.setAuthenticationManager(am);\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> this.manager.changePassword(\"password\", \"newPassword\"));\n\t\t// Check password hasn't changed.\n\t\tUserDetails newJoe = this.manager.loadUserByUsername(\"joe\");\n\t\tassertThat(newJoe.getPassword()).isEqualTo(\"password\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo(\"password\");\n\t\tassertThat(this.cache.getUserMap()).containsKey(\"joe\");\n\t}\n\n\t@Test\n\tpublic void findAllGroupsReturnsExpectedGroupNames() {\n\t\tList<String> groups = this.manager.findAllGroups();\n\t\tassertThat(groups).hasSize(4);\n\t\tCollections.sort(groups);\n\t\tassertThat(groups.get(0)).isEqualTo(\"GROUP_0\");\n\t\tassertThat(groups.get(1)).isEqualTo(\"GROUP_1\");\n\t\tassertThat(groups.get(2)).isEqualTo(\"GROUP_2\");\n\t\tassertThat(groups.get(3)).isEqualTo(\"GROUP_3\");\n\t}\n\n\t@Test\n\tpublic void findGroupMembersReturnsCorrectData() {\n\t\tList<String> groupMembers = this.manager.findUsersInGroup(\"GROUP_0\");\n\t\tassertThat(groupMembers).hasSize(1);\n\t\tassertThat(groupMembers.get(0)).isEqualTo(\"jerry\");\n\t\tgroupMembers = this.manager.findUsersInGroup(\"GROUP_1\");\n\t\tassertThat(groupMembers).hasSize(2);\n\t}\n\n\t@Test\n\tpublic void createGroupInsertsCorrectData() {\n\t\tthis.manager.createGroup(\"TEST_GROUP\", AuthorityUtils.createAuthorityList(\"ROLE_X\", \"ROLE_Y\"));\n\t\tList<?> roles = this.template.queryForList(\"select ga.authority from groups g, group_authorities ga \"\n\t\t\t\t+ \"where ga.group_id = g.id \" + \"and g.group_name = 'TEST_GROUP'\");\n\t\tassertThat(roles).hasSize(2);\n\t}\n\n\t@Test\n\tpublic void deleteGroupRemovesData() {\n\t\tthis.manager.deleteGroup(\"GROUP_0\");\n\t\tthis.manager.deleteGroup(\"GROUP_1\");\n\t\tthis.manager.deleteGroup(\"GROUP_2\");\n\t\tthis.manager.deleteGroup(\"GROUP_3\");\n\t\tassertThat(this.template.queryForList(\"select * from group_authorities\")).isEmpty();\n\t\tassertThat(this.template.queryForList(\"select * from group_members\")).isEmpty();\n\t\tassertThat(this.template.queryForList(\"select id from groups\")).isEmpty();\n\t}\n\n\t@Test\n\tpublic void deleteGroupWhenNotFound() {\n\t\tint groupCount = this.template.queryForList(\"select id from groups\").size();\n\t\tassertThatException().isThrownBy(() -> this.manager.deleteGroup(\"MISSING\"))\n\t\t\t.isNotInstanceOf(NullPointerException.class);\n\t\tassertThat(this.template.queryForList(\"select * from group_members\")).hasSize(groupCount);\n\t}\n\n\t@Test\n\tpublic void renameGroupIsSuccessful() {\n\t\tthis.manager.renameGroup(\"GROUP_0\", \"GROUP_X\");\n\t\tassertThat(this.template.queryForObject(\"select id from groups where group_name = 'GROUP_X'\", Integer.class))\n\t\t\t.isZero();\n\t}\n\n\t@Test\n\tpublic void addingGroupUserSetsCorrectData() {\n\t\tthis.manager.addUserToGroup(\"tom\", \"GROUP_0\");\n\t\tassertThat(this.template.queryForList(\"select username from group_members where group_id = 0\")).hasSize(2);\n\t}\n\n\t@Test\n\tpublic void removeUserFromGroupDeletesGroupMemberRow() {\n\t\tthis.manager.removeUserFromGroup(\"jerry\", \"GROUP_1\");\n\t\tassertThat(this.template.queryForList(\"select group_id from group_members where username = 'jerry'\"))\n\t\t\t.hasSize(1);\n\t}\n\n\t@Test\n\tpublic void findGroupAuthoritiesReturnsCorrectAuthorities() {\n\t\tassertThat(AuthorityUtils.createAuthorityList(\"ROLE_A\"))\n\t\t\t.isEqualTo(this.manager.findGroupAuthorities(\"GROUP_0\"));\n\t}\n\n\t@Test\n\tpublic void addGroupAuthorityInsertsCorrectGroupAuthorityRow() {\n\t\tGrantedAuthority auth = new SimpleGrantedAuthority(\"ROLE_X\");\n\t\tthis.manager.addGroupAuthority(\"GROUP_0\", auth);\n\t\tthis.template.queryForObject(\n\t\t\t\t\"select authority from group_authorities where authority = 'ROLE_X' and group_id = 0\", String.class);\n\t}\n\n\t@Test\n\tpublic void deleteGroupAuthorityRemovesCorrectRows() {\n\t\tGrantedAuthority auth = new SimpleGrantedAuthority(\"ROLE_A\");\n\t\tthis.manager.removeGroupAuthority(\"GROUP_0\", auth);\n\t\tassertThat(this.template.queryForList(\"select authority from group_authorities where group_id = 0\")).isEmpty();\n\t\tthis.manager.removeGroupAuthority(\"GROUP_2\", auth);\n\t\tassertThat(this.template.queryForList(\"select authority from group_authorities where group_id = 2\")).hasSize(2);\n\t}\n\n\t// SEC-1156\n\t@Test\n\tpublic void createUserDoesNotSaveAuthoritiesIfEnableAuthoritiesIsFalse() {\n\t\tthis.manager.setEnableAuthorities(false);\n\t\tthis.manager.createUser(joe);\n\t\tassertThat(this.template.queryForList(SELECT_JOE_AUTHORITIES_SQL)).isEmpty();\n\t}\n\n\t// SEC-1156\n\t@Test\n\tpublic void updateUserDoesNotSaveAuthoritiesIfEnableAuthoritiesIsFalse() {\n\t\tthis.manager.setEnableAuthorities(false);\n\t\tinsertJoe();\n\t\tthis.template.execute(\"delete from authorities where username='joe'\");\n\t\tthis.manager.updateUser(joe);\n\t\tassertThat(this.template.queryForList(SELECT_JOE_AUTHORITIES_SQL)).isEmpty();\n\t}\n\n\t// SEC-2166\n\t@Test\n\tpublic void createNewAuthenticationUsesNullPasswordToKeepPasswordSave() {\n\t\tinsertJoe();\n\t\tUsernamePasswordAuthenticationToken currentAuth = UsernamePasswordAuthenticationToken.authenticated(\"joe\", null,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tAuthentication updatedAuth = this.manager.createNewAuthentication(currentAuth, \"new\");\n\t\tassertThat(updatedAuth.getCredentials()).isNull();\n\t}\n\n\t@Test\n\tpublic void setUserDetailsMapperWithNullMapperThrowsException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.manager.setUserDetailsMapper(null))\n\t\t\t.withMessage(\"userDetailsMapper cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setUserDetailsMapperWithMockMapper() throws SQLException {\n\t\tRowMapper<UserDetails> mockMapper = mock(RowMapper.class);\n\t\tgiven(mockMapper.mapRow(any(), anyInt())).willReturn(joe);\n\t\tthis.manager.setUserDetailsMapper(mockMapper);\n\t\tinsertJoe();\n\t\tUserDetails newJoe = this.manager.loadUserByUsername(\"joe\");\n\t\tassertThat(joe).isEqualTo(newJoe);\n\t\tverify(mockMapper).mapRow(any(), anyInt());\n\t}\n\n\t@Test\n\tpublic void setGrantedAuthorityMapperWithNullMapperThrowsException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.manager.setGrantedAuthorityMapper(null))\n\t\t\t.withMessage(\"grantedAuthorityMapper cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setGrantedAuthorityMapperWithMockMapper() throws SQLException {\n\t\tRowMapper<GrantedAuthority> mockMapper = mock(RowMapper.class);\n\t\tGrantedAuthority mockAuthority = new SimpleGrantedAuthority(\"ROLE_MOCK\");\n\t\tgiven(mockMapper.mapRow(any(), anyInt())).willReturn(mockAuthority);\n\t\tthis.manager.setGrantedAuthorityMapper(mockMapper);\n\t\tList<GrantedAuthority> authGroup = this.manager.findGroupAuthorities(\"GROUP_0\");\n\t\tassertThat(authGroup.get(0)).isEqualTo(mockAuthority);\n\t\tverify(mockMapper).mapRow(any(), anyInt());\n\t}\n\n\t@Test\n\tvoid updatePasswordWhenDisabledReturnOriginalUser() {\n\t\tinsertJoe();\n\t\tthis.manager.updatePassword(joe, \"new\");\n\t\tUserDetails newJoe = this.manager.loadUserByUsername(\"joe\");\n\t\tassertThat(newJoe.getPassword()).isEqualTo(\"password\");\n\t}\n\n\t@Test\n\tvoid updatePasswordWhenEnabledShouldUpdatePassword() {\n\t\tinsertJoe();\n\t\tthis.manager.setEnableUpdatePassword(true);\n\t\tthis.manager.updatePassword(joe, \"new\");\n\t\tUserDetails newJoe = this.manager.loadUserByUsername(\"joe\");\n\t\tassertThat(newJoe.getPassword()).isEqualTo(\"new\");\n\t}\n\n\tprivate Authentication authenticateJoe() {\n\t\tUsernamePasswordAuthenticationToken auth = UsernamePasswordAuthenticationToken.authenticated(\"joe\", \"password\",\n\t\t\t\tjoe.getAuthorities());\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\treturn auth;\n\t}\n\n\tprivate void insertJoe() {\n\t\tthis.template.execute(\"insert into users (username, password, enabled) values ('joe','password','true')\");\n\t\tthis.template.execute(\"insert into authorities (username, authority) values ('joe','A')\");\n\t\tthis.template.execute(\"insert into authorities (username, authority) values ('joe','B')\");\n\t\tthis.template.execute(\"insert into authorities (username, authority) values ('joe','C')\");\n\t\tthis.cache.putUserInCache(joe);\n\t}\n\n\tprivate static class MockUserCache implements UserCache {\n\n\t\tprivate final Map<String, UserDetails> cache = new HashMap<>();\n\n\t\t@Override\n\t\tpublic UserDetails getUserFromCache(String username) {\n\t\t\treturn this.cache.get(username);\n\t\t}\n\n\t\t@Override\n\t\tpublic void putUserInCache(UserDetails user) {\n\t\t\tthis.cache.put(user.getUsername(), user);\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeUserFromCache(String username) {\n\t\t\tthis.cache.remove(username);\n\t\t}\n\n\t\tMap<String, UserDetails> getUserMap() {\n\t\t\treturn this.cache;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/scheduling/AbstractSecurityContextSchedulingTaskExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.scheduling;\n\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\n\nimport org.springframework.scheduling.SchedulingTaskExecutor;\nimport org.springframework.security.task.AbstractDelegatingSecurityContextAsyncTaskExecutorTests;\n\nimport static org.mockito.Mockito.verify;\n\n/**\n * Abstract class for testing {@link DelegatingSecurityContextSchedulingTaskExecutor}\n * which allows customization of how\n * {@link DelegatingSecurityContextSchedulingTaskExecutor} and its mocks are created.\n *\n * @author Rob Winch\n * @since 3.2\n * @see CurrentSecurityContextSchedulingTaskExecutorTests\n * @see ExplicitSecurityContextSchedulingTaskExecutorTests\n */\npublic abstract class AbstractSecurityContextSchedulingTaskExecutorTests\n\t\textends AbstractDelegatingSecurityContextAsyncTaskExecutorTests {\n\n\t@Mock\n\tprotected SchedulingTaskExecutor taskExecutorDelegate;\n\n\tprivate DelegatingSecurityContextSchedulingTaskExecutor executor;\n\n\t@Test\n\tpublic void prefersShortLivedTasks() {\n\t\tthis.executor = create();\n\t\tthis.executor.prefersShortLivedTasks();\n\t\tverify(this.taskExecutorDelegate).prefersShortLivedTasks();\n\t}\n\n\t@Override\n\tprotected SchedulingTaskExecutor getExecutor() {\n\t\treturn this.taskExecutorDelegate;\n\t}\n\n\t@Override\n\tprotected abstract DelegatingSecurityContextSchedulingTaskExecutor create();\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/scheduling/CurrentSecurityContextSchedulingTaskExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.scheduling;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests using the current {@link SecurityContext} on\n * {@link DelegatingSecurityContextSchedulingTaskExecutor}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class CurrentSecurityContextSchedulingTaskExecutorTests\n\t\textends AbstractSecurityContextSchedulingTaskExecutorTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tcurrentSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextSchedulingTaskExecutor create() {\n\t\treturn new DelegatingSecurityContextSchedulingTaskExecutor(this.taskExecutorDelegate);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutorIntegrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.scheduling;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.scheduling.SchedulingTaskExecutor;\nimport org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;\nimport org.springframework.security.DelegatingSecurityContextTestUtils;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Steve Riesenberg\n */\npublic class DelegatingSecurityContextSchedulingTaskExecutorIntegrationTests {\n\n\t@Test\n\tpublic void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tSchedulingTaskExecutor::execute\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void executeCompletableWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeCompletableAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void executeCompletableWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeCompletableAndReturn(\n\t\t\t\tnew VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext executeCompletableAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tSchedulingTaskExecutor::submitCompletable\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void submitWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void submitWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext submitAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.callAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tSchedulingTaskExecutor::submit\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void submitCompletableWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitCompletableAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void submitCompletableWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitCompletableAndReturn(\n\t\t\t\tnew VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext submitCompletableAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.callAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tSchedulingTaskExecutor::submitCompletable\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate DelegatingSecurityContextSchedulingTaskExecutor createExecutor(ScheduledExecutorService delegate) {\n\t\treturn new DelegatingSecurityContextSchedulingTaskExecutor(new ConcurrentTaskExecutor(delegate),\n\t\t\t\tsecurityContext());\n\t}\n\n\tprivate static SecurityContext securityContext() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\n\t\treturn securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextTaskSchedulerIntegrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.scheduling;\n\nimport java.time.Duration;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;\nimport org.springframework.scheduling.support.PeriodicTrigger;\nimport org.springframework.security.DelegatingSecurityContextTestUtils;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Steve Riesenberg\n */\npublic class DelegatingSecurityContextTaskSchedulerIntegrationTests {\n\n\t@Test\n\tpublic void scheduleWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = scheduleAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void scheduleWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = scheduleAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext scheduleAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createTaskScheduler,\n\t\t\t(taskScheduler, task) -> taskScheduler.schedule(task, new PeriodicTrigger(Duration.ofMillis(50)))\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void scheduleAtFixedRateWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = scheduleAtFixedRateAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void scheduleAtFixedRateWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = scheduleAtFixedRateAndReturn(\n\t\t\t\tnew VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext scheduleAtFixedRateAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createTaskScheduler,\n\t\t\t(taskScheduler, task) -> taskScheduler.scheduleAtFixedRate(task, Duration.ofMillis(50))\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void scheduleWithFixedDelayWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = scheduleWithFixedDelayAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void scheduleWithFixedDelayWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = scheduleWithFixedDelayAndReturn(\n\t\t\t\tnew VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext scheduleWithFixedDelayAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(\n\t\t\tthreadFactory,\n\t\t\tthis::createTaskScheduler,\n\t\t\t(taskScheduler, task) -> taskScheduler.scheduleWithFixedDelay(task, Duration.ofMillis(50))\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate DelegatingSecurityContextTaskScheduler createTaskScheduler(ScheduledExecutorService delegate) {\n\t\treturn new DelegatingSecurityContextTaskScheduler(new ConcurrentTaskScheduler(delegate), securityContext());\n\t}\n\n\tprivate static SecurityContext securityContext() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\n\t\treturn securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextTaskSchedulerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.scheduling;\n\nimport java.time.Instant;\nimport java.util.Date;\nimport java.util.concurrent.ScheduledFuture;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\n\nimport org.springframework.scheduling.TaskScheduler;\nimport org.springframework.scheduling.Trigger;\nimport org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isA;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Test An implementation of {@link TaskScheduler} invoking it whenever the trigger\n * indicates a next execution time.\n *\n * @author Richard Valdivieso\n * @since 5.1\n */\npublic class DelegatingSecurityContextTaskSchedulerTests {\n\n\t@Mock\n\tprivate TaskScheduler scheduler;\n\n\t@Mock\n\tprivate SecurityContext securityContext;\n\n\t@Mock\n\tprivate Runnable runnable;\n\n\t@Mock\n\tprivate Trigger trigger;\n\n\tprivate SecurityContext originalSecurityContext;\n\n\tprivate DelegatingSecurityContextTaskScheduler delegatingSecurityContextTaskScheduler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tthis.originalSecurityContext = SecurityContextHolder.createEmptyContext();\n\t\tthis.delegatingSecurityContextTaskScheduler = new DelegatingSecurityContextTaskScheduler(this.scheduler,\n\t\t\t\tthis.securityContext);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.delegatingSecurityContextTaskScheduler = null;\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingSecurityContextTaskScheduler(null));\n\t}\n\n\t@Test\n\tpublic void scheduleWhenDefaultThenCurrentSecurityContextPropagated() throws Exception {\n\t\twillAnswer((invocation) -> {\n\t\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(this.originalSecurityContext);\n\t\t\treturn null;\n\t\t}).given(this.runnable).run();\n\t\tTaskScheduler delegateTaskScheduler = new ConcurrentTaskScheduler();\n\t\tthis.delegatingSecurityContextTaskScheduler = new DelegatingSecurityContextTaskScheduler(delegateTaskScheduler);\n\t\tassertWrapped(this.runnable);\n\t}\n\n\t@Test\n\tpublic void scheduleWhenSecurityContextThenSecurityContextPropagated() throws Exception {\n\t\twillAnswer((invocation) -> {\n\t\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(this.securityContext);\n\t\t\treturn null;\n\t\t}).given(this.runnable).run();\n\t\tTaskScheduler delegateTaskScheduler = new ConcurrentTaskScheduler();\n\t\tthis.delegatingSecurityContextTaskScheduler = new DelegatingSecurityContextTaskScheduler(delegateTaskScheduler,\n\t\t\t\tthis.securityContext);\n\t\tassertWrapped(this.runnable);\n\t}\n\n\tprivate void assertWrapped(Runnable runnable) throws Exception {\n\t\tScheduledFuture<?> schedule = this.delegatingSecurityContextTaskScheduler.schedule(runnable, new Date());\n\t\tschedule.get();\n\t\tverify(this.runnable).run();\n\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(this.originalSecurityContext);\n\t}\n\n\t@Test\n\tpublic void scheduleWhenRunnableTriggerThenDelegates() {\n\t\tthis.delegatingSecurityContextTaskScheduler.schedule(this.runnable, this.trigger);\n\t\tverify(this.scheduler).schedule(any(Runnable.class), any(Trigger.class));\n\t}\n\n\t@Test\n\tpublic void scheduleWhenRunnableDateThenDelegates() {\n\t\tInstant date = Instant.now();\n\t\tthis.delegatingSecurityContextTaskScheduler.schedule(this.runnable, date);\n\t\tverify(this.scheduler).schedule(any(Runnable.class), any(Instant.class));\n\t}\n\n\t@Test\n\tpublic void scheduleAtFixedRateWhenRunnableDateLongThenDelegates() {\n\t\tDate date = new Date(1544751374L);\n\t\tthis.delegatingSecurityContextTaskScheduler.scheduleAtFixedRate(this.runnable, date, 1000L);\n\t\tverify(this.scheduler).scheduleAtFixedRate(isA(Runnable.class), isA(Date.class), eq(1000L));\n\t}\n\n\t@Test\n\tpublic void scheduleAtFixedRateWhenRunnableLongThenDelegates() {\n\t\tthis.delegatingSecurityContextTaskScheduler.scheduleAtFixedRate(this.runnable, 1000L);\n\t\tverify(this.scheduler).scheduleAtFixedRate(isA(Runnable.class), eq(1000L));\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/scheduling/ExplicitSecurityContextSchedulingTaskExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.scheduling;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests Explicitly specifying the {@link SecurityContext} on\n * {@link DelegatingSecurityContextSchedulingTaskExecutor}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class ExplicitSecurityContextSchedulingTaskExecutorTests\n\t\textends AbstractSecurityContextSchedulingTaskExecutorTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\texplicitSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextSchedulingTaskExecutor create() {\n\t\treturn new DelegatingSecurityContextSchedulingTaskExecutor(this.taskExecutorDelegate, this.securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/task/AbstractDelegatingSecurityContextAsyncTaskExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\n\nimport org.springframework.core.task.AsyncTaskExecutor;\nimport org.springframework.security.concurrent.AbstractDelegatingSecurityContextExecutorTests;\n\nimport static org.mockito.Mockito.verify;\n\n/**\n * Abstract class for testing {@link DelegatingSecurityContextAsyncTaskExecutor} which\n * allows customization of how {@link DelegatingSecurityContextAsyncTaskExecutor} and its\n * mocks are created.\n *\n * @author Rob Winch\n * @since 3.2\n * @see CurrentDelegatingSecurityContextAsyncTaskExecutorTests\n * @see ExplicitDelegatingSecurityContextAsyncTaskExecutorTests\n */\npublic abstract class AbstractDelegatingSecurityContextAsyncTaskExecutorTests\n\t\textends AbstractDelegatingSecurityContextExecutorTests {\n\n\t@Mock\n\tprotected AsyncTaskExecutor taskExecutorDelegate;\n\n\tprivate DelegatingSecurityContextAsyncTaskExecutor executor;\n\n\t@BeforeEach\n\tpublic final void setUpExecutor() {\n\t\tthis.executor = create();\n\t}\n\n\t@Test\n\tpublic void executeStartTimeout() {\n\t\tthis.executor.execute(this.runnable, 1);\n\t\tverify(getExecutor()).execute(this.wrappedRunnable, 1);\n\t}\n\n\t@Test\n\tpublic void submit() {\n\t\tthis.executor.submit(this.runnable);\n\t\tverify(getExecutor()).submit(this.wrappedRunnable);\n\t}\n\n\t@Test\n\tpublic void submitCallable() {\n\t\tthis.executor.submit(this.callable);\n\t\tverify(getExecutor()).submit(this.wrappedCallable);\n\t}\n\n\t@Override\n\tprotected AsyncTaskExecutor getExecutor() {\n\t\treturn this.taskExecutorDelegate;\n\t}\n\n\t@Override\n\tprotected abstract DelegatingSecurityContextAsyncTaskExecutor create();\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextAsyncTaskExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests using the current {@link SecurityContext} on\n * {@link DelegatingSecurityContextAsyncTaskExecutor}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class CurrentDelegatingSecurityContextAsyncTaskExecutorTests\n\t\textends AbstractDelegatingSecurityContextAsyncTaskExecutorTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tcurrentSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextAsyncTaskExecutor create() {\n\t\treturn new DelegatingSecurityContextAsyncTaskExecutor(this.taskExecutorDelegate);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextTaskExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport java.util.concurrent.Executor;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.mockito.Mock;\n\nimport org.springframework.core.task.TaskExecutor;\nimport org.springframework.security.concurrent.AbstractDelegatingSecurityContextExecutorTests;\nimport org.springframework.security.concurrent.DelegatingSecurityContextExecutor;\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests using the current {@link SecurityContext} on\n * {@link DelegatingSecurityContextExecutor}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class CurrentDelegatingSecurityContextTaskExecutorTests extends AbstractDelegatingSecurityContextExecutorTests {\n\n\t@Mock\n\tprivate TaskExecutor taskExecutorDelegate;\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tcurrentSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected Executor getExecutor() {\n\t\treturn this.taskExecutorDelegate;\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextExecutor create() {\n\t\treturn new DelegatingSecurityContextTaskExecutor(this.taskExecutorDelegate);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutorIntegrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport org.springframework.core.task.AsyncTaskExecutor;\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.core.task.support.TaskExecutorAdapter;\nimport org.springframework.security.DelegatingSecurityContextTestUtils;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Steve Riesenberg\n */\npublic class DelegatingSecurityContextAsyncTaskExecutorIntegrationTests {\n\n\t@Test\n\tpublic void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(threadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tAsyncTaskExecutor::execute\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void executeCompletableWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeCompletableAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void executeCompletableWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeCompletableAndReturn(\n\t\t\t\tnew VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext executeCompletableAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(threadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tAsyncTaskExecutor::submitCompletable\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void submitWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void submitWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext submitAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.callAndReturn(threadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tAsyncTaskExecutor::submit\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void submitCompletableWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitCompletableAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void submitCompletableWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = submitCompletableAndReturn(\n\t\t\t\tnew VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext submitCompletableAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.callAndReturn(threadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tAsyncTaskExecutor::submitCompletable\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate DelegatingSecurityContextAsyncTaskExecutor createExecutor(ScheduledExecutorService delegate) {\n\t\treturn new DelegatingSecurityContextAsyncTaskExecutor(new TaskExecutorAdapter(delegate), securityContext());\n\t}\n\n\tprivate static SecurityContext securityContext() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\n\t\treturn securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutorIntegrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport org.springframework.core.task.TaskExecutor;\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.core.task.support.TaskExecutorAdapter;\nimport org.springframework.security.DelegatingSecurityContextTestUtils;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Steve Riesenberg\n */\npublic class DelegatingSecurityContextTaskExecutorIntegrationTests {\n\n\t@Test\n\tpublic void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception {\n\t\tSecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory());\n\t\tassertThat(securityContext.getAuthentication()).isNotNull();\n\t}\n\n\tprivate SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception {\n\t\t// @formatter:off\n\t\treturn DelegatingSecurityContextTestUtils.runAndReturn(threadFactory,\n\t\t\tthis::createExecutor,\n\t\t\tTaskExecutor::execute\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate DelegatingSecurityContextTaskExecutor createExecutor(ScheduledExecutorService delegate) {\n\t\treturn new DelegatingSecurityContextTaskExecutor(new TaskExecutorAdapter(delegate), securityContext());\n\t}\n\n\tprivate static SecurityContext securityContext() {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\n\t\treturn securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextAsyncTaskExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests using an explicit {@link SecurityContext} on\n * {@link DelegatingSecurityContextAsyncTaskExecutor}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class ExplicitDelegatingSecurityContextAsyncTaskExecutorTests\n\t\textends AbstractDelegatingSecurityContextAsyncTaskExecutorTests {\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\texplicitSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextAsyncTaskExecutor create() {\n\t\treturn new DelegatingSecurityContextAsyncTaskExecutor(this.taskExecutorDelegate, this.securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextTaskExecutorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.task;\n\nimport java.util.concurrent.Executor;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.mockito.Mock;\n\nimport org.springframework.core.task.TaskExecutor;\nimport org.springframework.security.concurrent.AbstractDelegatingSecurityContextExecutorTests;\nimport org.springframework.security.concurrent.DelegatingSecurityContextExecutor;\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * Tests using an explicit {@link SecurityContext} on\n * {@link DelegatingSecurityContextExecutor}\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic class ExplicitDelegatingSecurityContextTaskExecutorTests extends AbstractDelegatingSecurityContextExecutorTests {\n\n\t@Mock\n\tprivate TaskExecutor taskExecutorDelegate;\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\texplicitSecurityContextSetup();\n\t}\n\n\t@Override\n\tprotected Executor getExecutor() {\n\t\treturn this.taskExecutorDelegate;\n\t}\n\n\t@Override\n\tprotected DelegatingSecurityContextExecutor create() {\n\t\treturn new DelegatingSecurityContextTaskExecutor(this.taskExecutorDelegate, this.securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/util/BusinessService.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport jakarta.annotation.security.PermitAll;\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n */\n@Secured({ \"ROLE_USER\" })\n@PermitAll\npublic interface BusinessService extends Serializable {\n\n\t@Secured({ \"ROLE_ADMIN\" })\n\t@RolesAllowed({ \"ROLE_ADMIN\" })\n\t@PreAuthorize(\"hasRole('ROLE_ADMIN')\")\n\tvoid someAdminMethod();\n\n\t@Secured({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\t@RolesAllowed({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\tvoid someUserAndAdminMethod();\n\n\t@Secured({ \"ROLE_USER\" })\n\t@RolesAllowed({ \"ROLE_USER\" })\n\tvoid someUserMethod1();\n\n\t@Secured({ \"ROLE_USER\" })\n\t@RolesAllowed({ \"ROLE_USER\" })\n\tvoid someUserMethod2();\n\n\t@RolesAllowed({ \"USER\" })\n\tvoid rolesAllowedUser();\n\n\tint someOther(String s);\n\n\tint someOther(int input);\n\n\tList<?> methodReturningAList(List<?> someList);\n\n\tObject[] methodReturningAnArray(Object[] someArray);\n\n\tList<?> methodReturningAList(String userName, String extraParam);\n\n\t@RequireAdminRole\n\t@RequireUserRole\n\tdefault void repeatedAnnotations() {\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/util/BusinessServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.authorization.method.BusinessService;\n\n/**\n * @author Joe Scalise\n */\n@SuppressWarnings(\"serial\")\npublic class BusinessServiceImpl<E extends Entity> implements BusinessService {\n\n\t@Override\n\t@Secured({ \"ROLE_USER\" })\n\tpublic void someUserMethod1() {\n\t}\n\n\t@Override\n\t@Secured({ \"ROLE_USER\" })\n\tpublic void someUserMethod2() {\n\t}\n\n\t@Override\n\t@Secured({ \"ROLE_USER\", \"ROLE_ADMIN\" })\n\tpublic void someUserAndAdminMethod() {\n\t}\n\n\t@Override\n\t@Secured({ \"ROLE_ADMIN\" })\n\tpublic void someAdminMethod() {\n\t}\n\n\tpublic E someUserMethod3(final E entity) {\n\t\treturn entity;\n\t}\n\n\t@Override\n\tpublic int someOther(String s) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int someOther(int input) {\n\t\treturn input;\n\t}\n\n\t@Override\n\tpublic List<?> methodReturningAList(List<?> someList) {\n\t\treturn someList;\n\t}\n\n\t@Override\n\tpublic List<Object> methodReturningAList(String userName, String arg2) {\n\t\treturn new ArrayList<>();\n\t}\n\n\t@Override\n\tpublic Object[] methodReturningAnArray(Object[] someArray) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void rolesAllowedUser() {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/util/Entity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\n/**\n * Class to act as a superclass for annotations testing.\n *\n * @author Ben Alex\n *\n */\npublic class Entity {\n\n\tpublic Entity(String someParameter) {\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/util/FieldUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * @author Luke Taylor\n */\npublic class FieldUtilsTests {\n\n\t@Test\n\tpublic void gettingAndSettingProtectedFieldIsSuccessful() throws Exception {\n\t\tObject tc = new TestClass();\n\t\tassertThat(FieldUtils.getProtectedFieldValue(\"protectedField\", tc)).isEqualTo(\"x\");\n\t\tassertThat(FieldUtils.getFieldValue(tc, \"nested.protectedField\")).isEqualTo(\"z\");\n\t\tFieldUtils.setProtectedFieldValue(\"protectedField\", tc, \"y\");\n\t\tassertThat(FieldUtils.getProtectedFieldValue(\"protectedField\", tc)).isEqualTo(\"y\");\n\t\tassertThatIllegalStateException().isThrownBy(() -> FieldUtils.getProtectedFieldValue(\"nonExistentField\", tc));\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tstatic class TestClass {\n\n\t\tprivate String protectedField = \"x\";\n\n\t\tprivate Nested nested = new Nested();\n\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tstatic class Nested {\n\n\t\tprivate String protectedField = \"z\";\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/util/InMemoryResourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\npublic class InMemoryResourceTests {\n\n\t@Test\n\tpublic void resourceContainsExpectedData() throws Exception {\n\t\tInMemoryResource resource = new InMemoryResource(\"blah\");\n\t\tassertThat(resource.getDescription()).isEmpty();\n\t\tassertThat(resource.hashCode()).isEqualTo(1);\n\t\tassertThat(resource.getInputStream()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void resourceIsEqualToOneWithSameContent() {\n\t\tassertThat(new InMemoryResource(\"xxx\")).isEqualTo(new InMemoryResource(\"xxx\"));\n\t\tassertThat(new InMemoryResource(\"xxx\").equals(new InMemoryResource(\"xxxx\"))).isFalse();\n\t\tassertThat(new InMemoryResource(\"xxx\").equals(new Object())).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/util/MethodInvocationUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.io.Serializable;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aop.framework.AdvisedSupport;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Luke Taylor\n */\npublic class MethodInvocationUtilsTests {\n\n\t@Test\n\tpublic void createFromClassReturnsMethodWithNoArgInfoForMethodWithNoArgs() {\n\t\tMethodInvocation mi = MethodInvocationUtils.createFromClass(String.class, \"length\");\n\t\tassertThat(mi).isNotNull();\n\t}\n\n\t@Test\n\tpublic void createFromClassReturnsMethodIfArgInfoOmittedAndMethodNameIsUnique() {\n\t\tMethodInvocation mi = MethodInvocationUtils.createFromClass(BusinessServiceImpl.class,\n\t\t\t\t\"methodReturningAnArray\");\n\t\tassertThat(mi).isNotNull();\n\t}\n\n\t@Test\n\tpublic void exceptionIsRaisedIfArgInfoOmittedAndMethodNameIsNotUnique() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> MethodInvocationUtils.createFromClass(BusinessServiceImpl.class, \"methodReturningAList\"));\n\t}\n\n\t@Test\n\tpublic void createFromClassReturnsMethodIfGivenArgInfoForMethodWithArgs() {\n\t\tMethodInvocation mi = MethodInvocationUtils.createFromClass(null, String.class, \"compareTo\",\n\t\t\t\tnew Class<?>[] { String.class }, new Object[] { \"\" });\n\t\tassertThat(mi).isNotNull();\n\t}\n\n\t@Test\n\tpublic void createFromObjectLocatesExistingMethods() {\n\t\tAdvisedTarget t = new AdvisedTarget();\n\t\t// Just lie about interfaces\n\t\tt.setInterfaces(new Class[] { Serializable.class, MethodInvocation.class, Blah.class });\n\t\tMethodInvocation mi = MethodInvocationUtils.create(t, \"blah\");\n\t\tassertThat(mi).isNotNull();\n\t\tt.setProxyTargetClass(true);\n\t\tmi = MethodInvocationUtils.create(t, \"blah\");\n\t\tassertThat(mi).isNotNull();\n\t\tassertThat(MethodInvocationUtils.create(t, \"blah\", \"non-existent arg\")).isNull();\n\t}\n\n\tinterface Blah {\n\n\t\tvoid blah();\n\n\t}\n\n\tclass AdvisedTarget extends AdvisedSupport implements Blah {\n\n\t\t@Override\n\t\tpublic void blah() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/util/RequireAdminRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('ADMIN')\")\n@RolesAllowed(\"ADMIN\")\n@Secured(\"ADMIN\")\npublic @interface RequireAdminRole {\n\n}\n"
  },
  {
    "path": "core/src/test/java/org/springframework/security/util/RequireUserRole.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.util;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport jakarta.annotation.security.RolesAllowed;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('USER')\")\n@RolesAllowed(\"USER\")\n@Secured(\"USER\")\npublic @interface RequireUserRole {\n\n}\n"
  },
  {
    "path": "core/src/test/kotlin/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandlerKotlinTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.access.expression.method\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport org.aopalliance.intercept.MethodInvocation\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.springframework.expression.EvaluationContext\nimport org.springframework.expression.Expression\nimport org.springframework.security.core.Authentication\nimport java.util.stream.Stream\nimport kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType\nimport kotlin.reflect.jvm.javaMethod\n\n/**\n * @author Blagoja Stamatovski\n */\nclass DefaultMethodSecurityExpressionHandlerKotlinTests {\n    private object Foo {\n        fun bar() {\n        }\n    }\n\n    private lateinit var authentication: Authentication\n    private lateinit var methodInvocation: MethodInvocation\n\n    private val handler: MethodSecurityExpressionHandler = DefaultMethodSecurityExpressionHandler()\n\n    @BeforeEach\n    fun setUp()  {\n        authentication = mockk()\n        methodInvocation = mockk()\n\n        every { methodInvocation.`this` } returns { Foo }\n        every { methodInvocation.method } answers { Foo::bar.javaMethod!! }\n        every { methodInvocation.arguments } answers { arrayOf<JvmType.Object>() }\n    }\n\n    @Test\n    fun `filters non-empty maps`() {\n        val expression: Expression = handler.expressionParser.parseExpression(\"filterObject.key eq 'key2'\")\n        val context: EvaluationContext = handler.createEvaluationContext(\n            /* authentication = */ authentication,\n            /* invocation = */ methodInvocation,\n        )\n        val nonEmptyMap: Map<String, String> = mapOf(\n            \"key1\" to \"value1\",\n            \"key2\" to \"value2\",\n            \"key3\" to \"value3\",\n        )\n\n        val filtered: Any = handler.filter(\n            /* filterTarget = */ nonEmptyMap,\n            /* filterExpression = */ expression,\n            /* ctx = */ context,\n        )\n\n        assertThat(filtered).isInstanceOf(Map::class.java)\n        @Suppress(\"UNCHECKED_CAST\")\n        val result = filtered as Map<String, String>\n        assertThat(result).hasSize(1)\n        assertThat(result).containsKey(\"key2\")\n        assertThat(result).containsValue(\"value2\")\n    }\n\n    @Test\n    fun `filters empty maps`() {\n        val expression: Expression = handler.expressionParser.parseExpression(\"filterObject.key eq 'key2'\")\n        val context: EvaluationContext = handler.createEvaluationContext(\n            /* authentication = */ authentication,\n            /* invocation = */ methodInvocation,\n        )\n        val emptyMap: Map<String, String> = emptyMap()\n\n        val filtered: Any = handler.filter(\n            /* filterTarget = */ emptyMap,\n            /* filterExpression = */ expression,\n            /* ctx = */ context,\n        )\n\n        assertThat(filtered).isInstanceOf(Map::class.java)\n        @Suppress(\"UNCHECKED_CAST\")\n        val result = filtered as Map<String, String>\n        assertThat(result).hasSize(0)\n    }\n\n    @Test\n    fun `filters non-empty collections`() {\n        val expression: Expression = handler.expressionParser.parseExpression(\"filterObject eq 'string2'\")\n        val context: EvaluationContext = handler.createEvaluationContext(\n            /* authentication = */ authentication,\n            /* invocation = */ methodInvocation,\n        )\n        val nonEmptyCollection: Collection<String> = listOf(\n            \"string1\",\n            \"string2\",\n            \"string1\",\n        )\n\n        val filtered: Any = handler.filter(\n            /* filterTarget = */ nonEmptyCollection,\n            /* filterExpression = */ expression,\n            /* ctx = */ context,\n        )\n\n        assertThat(filtered).isInstanceOf(Collection::class.java)\n        @Suppress(\"UNCHECKED_CAST\")\n        val result = filtered as Collection<String>\n        assertThat(result).hasSize(1)\n        assertThat(result).contains(\"string2\")\n    }\n\n    @Test\n    fun `filters empty collections`() {\n        val expression: Expression = handler.expressionParser.parseExpression(\"filterObject eq 'string2'\")\n        val context: EvaluationContext = handler.createEvaluationContext(\n            /* authentication = */ authentication,\n            /* invocation = */ methodInvocation,\n        )\n        val emptyCollection: Collection<String> = emptyList()\n\n        val filtered: Any = handler.filter(\n            /* filterTarget = */ emptyCollection,\n            /* filterExpression = */ expression,\n            /* ctx = */ context,\n        )\n\n        assertThat(filtered).isInstanceOf(Collection::class.java)\n        @Suppress(\"UNCHECKED_CAST\")\n        val result = filtered as Collection<String>\n        assertThat(result).hasSize(0)\n    }\n\n    @Test\n    fun `filters non-empty arrays`() {\n        val expression: Expression = handler.expressionParser.parseExpression(\"filterObject eq 'string2'\")\n        val context: EvaluationContext = handler.createEvaluationContext(\n            /* authentication = */ authentication,\n            /* invocation = */ methodInvocation,\n        )\n        val nonEmptyArray: Array<String> = arrayOf(\n            \"string1\",\n            \"string2\",\n            \"string1\",\n        )\n\n        val filtered: Any = handler.filter(\n            /* filterTarget = */ nonEmptyArray,\n            /* filterExpression = */ expression,\n            /* ctx = */ context,\n        )\n\n        assertThat(filtered).isInstanceOf(Array<String>::class.java)\n        @Suppress(\"UNCHECKED_CAST\")\n        val result = filtered as Array<String>\n        assertThat(result).hasSize(1)\n        assertThat(result).contains(\"string2\")\n    }\n\n    @Test\n    fun `filters empty arrays`() {\n        val expression: Expression = handler.expressionParser.parseExpression(\"filterObject eq 'string2'\")\n        val context: EvaluationContext = handler.createEvaluationContext(\n            /* authentication = */ authentication,\n            /* invocation = */ methodInvocation,\n        )\n        val emptyArray: Array<String> = emptyArray()\n\n        val filtered: Any = handler.filter(\n            /* filterTarget = */ emptyArray,\n            /* filterExpression = */ expression,\n            /* ctx = */ context,\n        )\n\n        assertThat(filtered).isInstanceOf(Array<String>::class.java)\n        @Suppress(\"UNCHECKED_CAST\")\n        val result = filtered as Array<String>\n        assertThat(result).hasSize(0)\n    }\n\n    @Test\n    fun `filters non-empty streams`() {\n        val expression: Expression = handler.expressionParser.parseExpression(\"filterObject eq 'string2'\")\n        val context: EvaluationContext = handler.createEvaluationContext(\n            /* authentication = */ authentication,\n            /* invocation = */ methodInvocation,\n        )\n        val nonEmptyStream: Stream<String> = listOf(\n            \"string1\",\n            \"string2\",\n            \"string1\",\n        ).stream()\n\n        val filtered: Any = handler.filter(\n            /* filterTarget = */ nonEmptyStream,\n            /* filterExpression = */ expression,\n            /* ctx = */ context,\n        )\n\n        assertThat(filtered).isInstanceOf(Stream::class.java)\n        @Suppress(\"UNCHECKED_CAST\")\n        val result = (filtered as Stream<String>).toList()\n        assertThat(result).hasSize(1)\n        assertThat(result).contains(\"string2\")\n    }\n\n    @Test\n    fun `filters empty streams`() {\n        val expression: Expression = handler.expressionParser.parseExpression(\"filterObject eq 'string2'\")\n        val context: EvaluationContext = handler.createEvaluationContext(\n            /* authentication = */ authentication,\n            /* invocation = */ methodInvocation,\n        )\n        val emptyStream: Stream<String> = emptyList<String>().stream()\n\n        val filtered: Any = handler.filter(\n            /* filterTarget = */ emptyStream,\n            /* filterExpression = */ expression,\n            /* ctx = */ context,\n        )\n\n        assertThat(filtered).isInstanceOf(Stream::class.java)\n        @Suppress(\"UNCHECKED_CAST\")\n        val result = (filtered as Stream<String>).toList()\n        assertThat(result).hasSize(0)\n    }\n}\n"
  },
  {
    "path": "core/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "core/src/test/resources/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProviderTests.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:util=\"http://www.springframework.org/schema/util\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\thttp://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-3.0.xsd\">\n\n\t<bean id=\"jaasAuthProvider\"\n\t\tclass=\"org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider\">\n\t\t<property name=\"configuration\">\n\t\t\t<bean\n\t\t\t\tclass=\"org.springframework.security.authentication.jaas.memory.InMemoryConfiguration\">\n\t\t\t\t<constructor-arg>\n\t\t\t\t\t<map>\n\t\t\t\t\t\t<entry key=\"SPRINGSECURITY\">\n\t\t\t\t\t\t\t<array>\n\t\t\t\t\t\t\t\t<bean\n\t\t\t\t\t\t\t\t\tclass=\"javax.security.auth.login.AppConfigurationEntry\">\n\t\t\t\t\t\t\t\t\t<constructor-arg\n\t\t\t\t\t\t\t\t\t\tvalue=\"org.springframework.security.authentication.jaas.TestLoginModule\" />\n\t\t\t\t\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t\t\t\t\t<util:constant\n\t\t\t\t\t\t\t\t\t\t\tstatic-field=\"javax.security.auth.login.AppConfigurationEntry$LoginModuleControlFlag.REQUIRED\" />\n\t\t\t\t\t\t\t\t\t</constructor-arg>\n\t\t\t\t\t\t\t\t\t<constructor-arg>\n\t\t\t\t\t\t\t\t\t\t<map></map>\n\t\t\t\t\t\t\t\t\t</constructor-arg>\n\t\t\t\t\t\t\t\t</bean>\n\t\t\t\t\t\t\t</array>\n\t\t\t\t\t\t</entry>\n\t\t\t\t\t</map>\n\t\t\t\t</constructor-arg>\n\t\t\t</bean>\n\t\t</property>\n\t\t<property name=\"authorityGranters\">\n\t\t\t<list>\n\t\t\t\t<bean\n\t\t\t\t\tclass=\"org.springframework.security.authentication.jaas.TestAuthorityGranter\" />\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n</beans>\n"
  },
  {
    "path": "core/src/test/resources/org/springframework/security/authentication/jaas/JaasAuthenticationProviderTests.conf",
    "content": "JAASTest2 {\n    org.springframework.security.authentication.jaas.TestLoginModule required;\n};"
  },
  {
    "path": "core/src/test/resources/org/springframework/security/authentication/jaas/JaasAuthenticationProviderTests.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN//EN\" \"https://www.springframework.org/dtd/spring-beans.dtd\">\n\n\n<beans>\n\n\t<bean id=\"eventCheck\" class=\"org.springframework.security.authentication.jaas.JaasEventCheck\"/>\n\n\t<bean id=\"jaasAuthenticationProvider\" class=\"org.springframework.security.authentication.jaas.JaasAuthenticationProvider\">\n\t\t<property name=\"loginContextName\">\n\t\t\t<value>JAASTest</value>\n\t\t</property>\n\t\t<property name=\"loginConfig\">\n\t\t\t<value>classpath:org/springframework/security/authentication/jaas/login.conf</value>\n\t\t</property>\n\t\t<property name=\"callbackHandlers\">\n\t\t\t<list>\n\t\t\t\t<bean class=\"org.springframework.security.authentication.jaas.TestCallbackHandler\"/>\n\t\t\t\t<bean class=\"org.springframework.security.authentication.jaas.JaasNameCallbackHandler\"/>\n\t\t\t\t<bean class=\"org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler\"/>\n\t\t\t</list>\n\t\t</property>\n\t\t<property name=\"authorityGranters\">\n\t\t\t<list>\n\t\t\t\t<bean class=\"org.springframework.security.authentication.jaas.TestAuthorityGranter\"/>\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n</beans>\n"
  },
  {
    "path": "core/src/test/resources/org/springframework/security/authentication/jaas/login.conf",
    "content": "JAASTest {\n    org.springframework.security.authentication.jaas.TestLoginModule required;\n};"
  },
  {
    "path": "core/src/test/resources/org/springframework/security/authentication/jaas/test1.conf",
    "content": "test1 {\n    org.springframework.security.authentication.jaas.TestLoginModule required;\n};"
  },
  {
    "path": "core/src/test/resources/org/springframework/security/authentication/jaas/test2.conf",
    "content": "test2 {\n    org.springframework.security.authentication.jaas.TestLoginModule required;\n};"
  },
  {
    "path": "core/src/test/resources/org/springframework/security/converter/simple.priv",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMk7CKSTfu3QoV\nHoPVXxwZO+qweztd36cVWYqGOZinrOR2crWFu50AgR2CsdIH0+cqo7F4Vx7/3O8i\nRpYYZPe2VoO5sumzJt8P6fS80/TAKjhJDAqgZKRJTgGN8KxCM6p/aJli1ZeDBqiV\nv7vJJe+ZgJuPGRS+HMNa/wPxEkqqXsglcJcQV1ZEtfKXSHB7jizKpRL38185SyAC\npwyjvBu6Cmm1URfhQo88mf239ONh4dZ2HoDfzN1q6Ssu4F4hgutxr9B0DVLDP5u+\nWFrm3nsJ76zf99uJ+ntMUHJ+bY+gOjSlVWIVBIZeAaEGKCNWRk/knjvjbijpvm3U\nacGlgdL3AgMBAAECggEACxxxS7zVyu91qI2s5eSKmAQAXMqgup6+2hUluc47nqUv\nuZz/c/6MPkn2Ryo+65d4IgqmMFjSfm68B/2ER5FTcvoLl1Xo2twrrVpUmcg3BClS\nIZPuExdhVNnxjYKEWwcyZrehyAoR261fDdcFxLRW588efIUC+rPTTRHzAc7sT+Ln\nt/uFeYNWJm3LaegOLoOmlMAhJ5puAWSN1F0FxtRf/RVgzbLA9QC975SKHJsfWCSr\nIZyPsdeaqomKaF65l8nfqlE0Ua2L35gIOGKjUwb7uUE8nI362RWMtYdoi3zDDyoY\nhSFbgjylCHDM0u6iSh6KfqOHtkYyJ8tUYgVWl787wQKBgQDYO3wL7xuDdD101Lyl\nAnaDdFB9fxp83FG1cWr+t7LYm9YxGfEUsKHAJXN6TIayDkOOoVwIl+Gz0T3Z06Bm\neBGLrB9mrVA7+C7NJwu5gTMlzP6HxUR9zKJIQ/VB1NUGM77LSmvOFbHc9Q0+z8EH\nX5WO516a3Z7lNtZJcCoPOtu2rwKBgQCmbj41Fh+SSEUApCEKms5ETRpe7LXQlJgx\nyW7zcJNNuIb1C3vBLPxjiOTMgYKOeMg5rtHTGLT43URHLh9ArjawasjSAr4AM3J4\nxpoi/sKGDdiKOsuDWIGfzdYL8qyTHSdpZLQsCTMRiRYgAHZFPgNa7SLZRfZicGlr\nGHN1rJW6OQKBgEjiM/upyrJSWeypUDSmUeAZMpA6aWkwsfHgmtnkfUn5rQa74cDB\nkKO9e+D7LmOR3z+SL/1NhGwh2SE07dncGr3jdGodfO/ZxZyszozmeaECKcEFwwJM\nGV8WWPKplGwUwPiwywmZ0mvRxXcoe73KgBS88+xrSwWjqDL0tZiQlEJNAoGATkei\nGMQMG3jEg9Wu+NbxV6zQT3+U0MNjhl9RQU1c63x0dcNt9OFc4NAdlZcAulRTENaK\nOHjxffBM0hH+fySx8m53gFfr2BpaqDX5f6ZGBlly1SlsWZ4CchCVsc71nshipi7I\nk8HL9F5/OpQdDNprJ5RMBNfkWE65Nrcsb1e6oPkCgYAxwgdiSOtNg8PjDVDmAhwT\nMxj0Dtwi2fAqQ76RVrrXpNp3uCOIAu4CfruIb5llcJ3uak0ZbnWri32AxSgk80y3\nEWiRX/WEDu5znejF+5O3pI02atWWcnxifEKGGlxwkcMbQdA67MlrJLFaSnnGpNXo\nyPfcul058SOqhafIZQMEKQ==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "crypto/spring-security-crypto.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n\tid 'java-test-fixtures'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\toptional 'org.springframework:spring-core'\n\toptional 'org.bouncycastle:bcpkix-jdk18on'\n\toptional libs.com.password4j.password4j\n\n\ttestFixturesImplementation \"org.assertj:assertj-core\"\n\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2EncodingUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.argon2;\n\nimport java.util.Base64;\n\nimport org.bouncycastle.crypto.params.Argon2Parameters;\nimport org.bouncycastle.util.Arrays;\n\n/**\n * Utility for encoding and decoding Argon2 hashes.\n *\n * Used by {@link Argon2PasswordEncoder}.\n *\n * @author Simeon Macke\n * @since 5.3\n */\nfinal class Argon2EncodingUtils {\n\n\tprivate static final Base64.Encoder b64encoder = Base64.getEncoder().withoutPadding();\n\n\tprivate static final Base64.Decoder b64decoder = Base64.getDecoder();\n\n\tprivate Argon2EncodingUtils() {\n\t}\n\n\t/**\n\t * Encodes a raw Argon2-hash and its parameters into the standard Argon2-hash-string\n\t * as specified in the reference implementation\n\t * (https://github.com/P-H-C/phc-winner-argon2/blob/master/src/encoding.c#L244):\n\t *\n\t * {@code $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<bin>$<bin>}\n\t *\n\t * where {@code <T>} is either 'd', 'id', or 'i', {@code <num>} is a decimal integer\n\t * (positive, fits in an 'unsigned long'), and {@code <bin>} is Base64-encoded data\n\t * (no '=' padding characters, no newline or whitespace).\n\t *\n\t * The last two binary chunks (encoded in Base64) are, in that order, the salt and the\n\t * output. If no salt has been used, the salt will be omitted.\n\t * @param hash the raw Argon2 hash in binary format\n\t * @param parameters the Argon2 parameters that were used to create the hash\n\t * @return the encoded Argon2-hash-string as described above\n\t * @throws IllegalArgumentException if the Argon2Parameters are invalid\n\t */\n\tstatic String encode(byte[] hash, Argon2Parameters parameters) throws IllegalArgumentException {\n\t\tStringBuilder stringBuilder = new StringBuilder();\n\t\tString type = switch (parameters.getType()) {\n\t\t\tcase Argon2Parameters.ARGON2_d -> \"$argon2d\";\n\t\t\tcase Argon2Parameters.ARGON2_i -> \"$argon2i\";\n\t\t\tcase Argon2Parameters.ARGON2_id -> \"$argon2id\";\n\t\t\tdefault -> throw new IllegalArgumentException(\"Invalid algorithm type: \" + parameters.getType());\n\t\t};\n\t\tstringBuilder.append(type);\n\t\tstringBuilder.append(\"$v=\")\n\t\t\t.append(parameters.getVersion())\n\t\t\t.append(\"$m=\")\n\t\t\t.append(parameters.getMemory())\n\t\t\t.append(\",t=\")\n\t\t\t.append(parameters.getIterations())\n\t\t\t.append(\",p=\")\n\t\t\t.append(parameters.getLanes());\n\t\tif (parameters.getSalt() != null) {\n\t\t\tstringBuilder.append(\"$\").append(b64encoder.encodeToString(parameters.getSalt()));\n\t\t}\n\t\tstringBuilder.append(\"$\").append(b64encoder.encodeToString(hash));\n\t\treturn stringBuilder.toString();\n\t}\n\n\t/**\n\t * Decodes an Argon2 hash string as specified in the reference implementation\n\t * (https://github.com/P-H-C/phc-winner-argon2/blob/master/src/encoding.c#L244) into\n\t * the raw hash and the used parameters.\n\t *\n\t * The hash has to be formatted as follows:\n\t * {@code $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<bin>$<bin>}\n\t *\n\t * where {@code <T>} is either 'd', 'id', or 'i', {@code <num>} is a decimal integer\n\t * (positive, fits in an 'unsigned long'), and {@code <bin>} is Base64-encoded data\n\t * (no '=' padding characters, no newline or whitespace).\n\t *\n\t * The last two binary chunks (encoded in Base64) are, in that order, the salt and the\n\t * output. Both are required. The binary salt length and the output length must be in\n\t * the allowed ranges defined in argon2.h.\n\t * @param encodedHash the Argon2 hash string as described above\n\t * @return an {@link Argon2Hash} object containing the raw hash and the\n\t * {@link Argon2Parameters}.\n\t * @throws IllegalArgumentException if the encoded hash is malformed\n\t */\n\tstatic Argon2Hash decode(String encodedHash) throws IllegalArgumentException {\n\t\tArgon2Parameters.Builder paramsBuilder;\n\t\tString[] parts = encodedHash.split(\"\\\\$\");\n\t\tif (parts.length < 4) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid encoded Argon2-hash\");\n\t\t}\n\t\tint currentPart = 1;\n\t\tparamsBuilder = switch (parts[currentPart++]) {\n\t\t\tcase \"argon2d\" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_d);\n\t\t\tcase \"argon2i\" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_i);\n\t\t\tcase \"argon2id\" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id);\n\t\t\tdefault -> throw new IllegalArgumentException(\"Invalid algorithm type: \" + parts[1]);\n\t\t};\n\t\tif (parts[currentPart].startsWith(\"v=\")) {\n\t\t\tparamsBuilder.withVersion(Integer.parseInt(parts[currentPart].substring(2)));\n\t\t\tcurrentPart++;\n\t\t}\n\t\tString[] performanceParams = parts[currentPart++].split(\",\");\n\t\tif (performanceParams.length != 3) {\n\t\t\tthrow new IllegalArgumentException(\"Amount of performance parameters invalid\");\n\t\t}\n\t\tif (!performanceParams[0].startsWith(\"m=\")) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid memory parameter\");\n\t\t}\n\t\tparamsBuilder.withMemoryAsKB(Integer.parseInt(performanceParams[0].substring(2)));\n\t\tif (!performanceParams[1].startsWith(\"t=\")) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid iterations parameter\");\n\t\t}\n\t\tparamsBuilder.withIterations(Integer.parseInt(performanceParams[1].substring(2)));\n\t\tif (!performanceParams[2].startsWith(\"p=\")) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid parallelity parameter\");\n\t\t}\n\t\tparamsBuilder.withParallelism(Integer.parseInt(performanceParams[2].substring(2)));\n\t\tparamsBuilder.withSalt(b64decoder.decode(parts[currentPart++]));\n\t\treturn new Argon2Hash(b64decoder.decode(parts[currentPart]), paramsBuilder.build());\n\t}\n\n\tpublic static class Argon2Hash {\n\n\t\tprivate byte[] hash;\n\n\t\tprivate Argon2Parameters parameters;\n\n\t\tArgon2Hash(byte[] hash, Argon2Parameters parameters) {\n\t\t\tthis.hash = Arrays.clone(hash);\n\t\t\tthis.parameters = parameters;\n\t\t}\n\n\t\tpublic byte[] getHash() {\n\t\t\treturn Arrays.clone(this.hash);\n\t\t}\n\n\t\tpublic void setHash(byte[] hash) {\n\t\t\tthis.hash = Arrays.clone(hash);\n\t\t}\n\n\t\tpublic Argon2Parameters getParameters() {\n\t\t\treturn this.parameters;\n\t\t}\n\n\t\tpublic void setParameters(Argon2Parameters parameters) {\n\t\t\tthis.parameters = parameters;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.argon2;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.bouncycastle.crypto.generators.Argon2BytesGenerator;\nimport org.bouncycastle.crypto.params.Argon2Parameters;\n\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;\n\n/**\n * <p>\n * Implementation of PasswordEncoder that uses the Argon2 hashing function. Clients can\n * optionally supply the length of the salt to use, the length of the generated hash, a\n * cpu cost parameter, a memory cost parameter and a parallelization parameter.\n * </p>\n *\n * <p>\n * Note:\n * </p>\n * <p>\n * The currently implementation uses Bouncy castle which does not exploit\n * parallelism/optimizations that password crackers will, so there is an unnecessary\n * asymmetry between attacker and defender.\n * </p>\n *\n * @author Simeon Macke\n * @since 5.3\n */\npublic class Argon2PasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final int DEFAULT_SALT_LENGTH = 16;\n\n\tprivate static final int DEFAULT_HASH_LENGTH = 32;\n\n\tprivate static final int DEFAULT_PARALLELISM = 1;\n\n\tprivate static final int DEFAULT_MEMORY = 1 << 14;\n\n\tprivate static final int DEFAULT_ITERATIONS = 2;\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final int hashLength;\n\n\tprivate final int parallelism;\n\n\tprivate final int memory;\n\n\tprivate final int iterations;\n\n\tprivate final BytesKeyGenerator saltGenerator;\n\n\t/**\n\t * Constructs an Argon2 password encoder with the provided parameters.\n\t * @param saltLength the salt length (in bytes)\n\t * @param hashLength the hash length (in bytes)\n\t * @param parallelism the parallelism\n\t * @param memory the memory cost\n\t * @param iterations the number of iterations\n\t */\n\tpublic Argon2PasswordEncoder(int saltLength, int hashLength, int parallelism, int memory, int iterations) {\n\t\tthis.hashLength = hashLength;\n\t\tthis.parallelism = parallelism;\n\t\tthis.memory = memory;\n\t\tthis.iterations = iterations;\n\t\tthis.saltGenerator = KeyGenerators.secureRandom(saltLength);\n\t}\n\n\t/**\n\t * Constructs an Argon2 password encoder with a salt length of 16 bytes, a hash length\n\t * of 32 bytes, parallelism of 1, memory cost of {@code 1 << 12} and 3 iterations.\n\t * @return the {@link Argon2PasswordEncoder}\n\t * @since 5.8\n\t * @deprecated Use {@link #defaultsForSpringSecurity_v5_8()} instead\n\t */\n\t@Deprecated\n\tpublic static Argon2PasswordEncoder defaultsForSpringSecurity_v5_2() {\n\t\treturn new Argon2PasswordEncoder(16, 32, 1, 1 << 12, 3);\n\t}\n\n\t/**\n\t * Constructs an Argon2 password encoder with a salt length of 16 bytes, a hash length\n\t * of 32 bytes, parallelism of 1, memory cost of {@code 1 << 14} and 2 iterations.\n\t * @return the {@link Argon2PasswordEncoder}\n\t * @since 5.8\n\t */\n\tpublic static Argon2PasswordEncoder defaultsForSpringSecurity_v5_8() {\n\t\treturn new Argon2PasswordEncoder(DEFAULT_SALT_LENGTH, DEFAULT_HASH_LENGTH, DEFAULT_PARALLELISM, DEFAULT_MEMORY,\n\t\t\t\tDEFAULT_ITERATIONS);\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tbyte[] salt = this.saltGenerator.generateKey();\n\t\tbyte[] hash = new byte[this.hashLength];\n\t\t// @formatter:off\n\t\tArgon2Parameters params = new Argon2Parameters\n\t\t\t\t.Builder(Argon2Parameters.ARGON2_id)\n\t\t\t\t.withSalt(salt)\n\t\t\t\t.withParallelism(this.parallelism)\n\t\t\t\t.withMemoryAsKB(this.memory)\n\t\t\t\t.withIterations(this.iterations)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tArgon2BytesGenerator generator = new Argon2BytesGenerator();\n\t\tgenerator.init(params);\n\t\tgenerator.generateBytes(rawPassword.toString().toCharArray(), hash);\n\t\treturn Argon2EncodingUtils.encode(hash, params);\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tArgon2EncodingUtils.Argon2Hash decoded;\n\t\ttry {\n\t\t\tdecoded = Argon2EncodingUtils.decode(encodedPassword);\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\tthis.logger.warn(\"Malformed password hash\", ex);\n\t\t\treturn false;\n\t\t}\n\t\tbyte[] hashBytes = new byte[decoded.getHash().length];\n\t\tArgon2BytesGenerator generator = new Argon2BytesGenerator();\n\t\tgenerator.init(decoded.getParameters());\n\t\tgenerator.generateBytes(rawPassword.toString().toCharArray(), hashBytes);\n\t\treturn constantTimeArrayEquals(decoded.getHash(), hashBytes);\n\t}\n\n\t@Override\n\tprotected boolean upgradeEncodingNonNull(String encodedPassword) {\n\t\tArgon2Parameters parameters = Argon2EncodingUtils.decode(encodedPassword).getParameters();\n\t\treturn parameters.getMemory() < this.memory || parameters.getIterations() < this.iterations;\n\t}\n\n\tprivate static boolean constantTimeArrayEquals(byte[] expected, byte[] actual) {\n\t\tif (expected.length != actual.length) {\n\t\t\treturn false;\n\t\t}\n\t\tint result = 0;\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tresult |= expected[i] ^ actual[i];\n\t\t}\n\t\treturn result == 0;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/argon2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.argon2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java",
    "content": "// Copyright (c) 2006 Damien Miller <djm@mindrot.org>\n//\n// Permission to use, copy, modify, and distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\npackage org.springframework.security.crypto.bcrypt;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.SecureRandom;\nimport java.util.Arrays;\n\n/**\n * BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in\n * \"A Future-Adaptable Password Scheme\" by Niels Provos and David Mazieres.\n * <p>\n * This password hashing system tries to thwart off-line password cracking using a\n * computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher.\n * The work factor of the algorithm is parameterised, so it can be increased as computers\n * get faster.\n * <p>\n * Usage is really simple. To hash a password for the first time, call the hashpw method\n * with a random salt, like this:\n * <p>\n * <code>\n * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br />\n * </code>\n * <p>\n * To check whether a plaintext password matches one that has been hashed previously, use\n * the checkpw method:\n * <p>\n * <code>\n * if (BCrypt.checkpw(candidate_password, stored_hash))<br />\n * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(\"It matches\");<br />\n * else<br />\n * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(\"It does not match\");<br />\n * </code>\n * <p>\n * The gensalt() method takes an optional parameter (log_rounds) that determines the\n * computational complexity of the hashing:\n * <p>\n * <code>\n * String strong_salt = BCrypt.gensalt(10)<br />\n * String stronger_salt = BCrypt.gensalt(12)<br />\n * </code>\n * <p>\n * The amount of work increases exponentially (2**log_rounds), so each increment is twice\n * as much work. The default log_rounds is 10, and the valid range is 4 to 31.\n *\n * @author Damien Miller\n * @version 0.3\n */\npublic class BCrypt {\n\n\t// BCrypt parameters\n\tprivate static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;\n\n\tprivate static final int BCRYPT_SALT_LEN = 16;\n\n\t// Blowfish parameters\n\tprivate static final int BLOWFISH_NUM_ROUNDS = 16;\n\n\t// Initial contents of key schedule\n\tprivate static final int P_orig[] = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,\n\t\t\t0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5,\n\t\t\t0xb5470917, 0x9216d5d9, 0x8979fb1b };\n\n\tprivate static final int S_orig[] = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,\n\t\t\t0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3,\n\t\t\t0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,\n\t\t\t0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1,\n\t\t\t0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,\n\t\t\t0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16,\n\t\t\t0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,\n\t\t\t0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81,\n\t\t\t0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,\n\t\t\t0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050,\n\t\t\t0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,\n\t\t\t0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724,\n\t\t\t0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,\n\t\t\t0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb,\n\t\t\t0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,\n\t\t\t0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe,\n\t\t\t0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,\n\t\t\t0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6,\n\t\t\t0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,\n\t\t\t0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada,\n\t\t\t0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,\n\t\t\t0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191,\n\t\t\t0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,\n\t\t\t0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065,\n\t\t\t0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,\n\t\t\t0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2,\n\t\t\t0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,\n\t\t\t0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521,\n\t\t\t0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944,\n\t\t\t0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c,\n\t\t\t0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,\n\t\t\t0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f,\n\t\t\t0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c,\n\t\t\t0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58,\n\t\t\t0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,\n\t\t\t0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38,\n\t\t\t0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810,\n\t\t\t0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc,\n\t\t\t0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,\n\t\t\t0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1,\n\t\t\t0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1,\n\t\t\t0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978,\n\t\t\t0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,\n\t\t\t0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef,\n\t\t\t0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170,\n\t\t\t0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b,\n\t\t\t0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,\n\t\t\t0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a,\n\t\t\t0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263,\n\t\t\t0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61,\n\t\t\t0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,\n\t\t\t0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2,\n\t\t\t0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d,\n\t\t\t0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1,\n\t\t\t0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,\n\t\t\t0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3,\n\t\t\t0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a,\n\t\t\t0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e,\n\t\t\t0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,\n\t\t\t0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3,\n\t\t\t0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900,\n\t\t\t0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399,\n\t\t\t0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,\n\t\t\t0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a,\n\t\t\t0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9,\n\t\t\t0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f,\n\t\t\t0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,\n\t\t\t0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0,\n\t\t\t0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd,\n\t\t\t0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab,\n\t\t\t0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,\n\t\t\t0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88,\n\t\t\t0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b,\n\t\t\t0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8,\n\t\t\t0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,\n\t\t\t0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33,\n\t\t\t0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2,\n\t\t\t0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79,\n\t\t\t0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,\n\t\t\t0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d,\n\t\t\t0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa,\n\t\t\t0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa,\n\t\t\t0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,\n\t\t\t0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578,\n\t\t\t0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4,\n\t\t\t0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737,\n\t\t\t0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,\n\t\t\t0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee,\n\t\t\t0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,\n\t\t\t0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550,\n\t\t\t0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,\n\t\t\t0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a,\n\t\t\t0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,\n\t\t\t0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44,\n\t\t\t0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,\n\t\t\t0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711,\n\t\t\t0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,\n\t\t\t0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd,\n\t\t\t0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,\n\t\t\t0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463,\n\t\t\t0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,\n\t\t\t0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b,\n\t\t\t0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,\n\t\t\t0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a,\n\t\t\t0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,\n\t\t\t0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065,\n\t\t\t0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,\n\t\t\t0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54,\n\t\t\t0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,\n\t\t\t0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b,\n\t\t\t0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,\n\t\t\t0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c,\n\t\t\t0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,\n\t\t\t0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3,\n\t\t\t0x3ac372e6 };\n\n\t// bcrypt IV: \"OrpheanBeholderScryDoubt\"\n\tstatic private final int bf_crypt_ciphertext[] = { 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944,\n\t\t\t0x6f756274 };\n\n\t// Table for Base64 encoding\n\tstatic private final char base64_code[] = { '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',\n\t\t\t'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',\n\t\t\t'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1',\n\t\t\t'2', '3', '4', '5', '6', '7', '8', '9' };\n\n\t// Table for Base64 decoding\n\tstatic private final byte index_64[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n\t\t\t-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n\t\t\t0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,\n\t\t\t12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32,\n\t\t\t33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1 };\n\tstatic final int MIN_LOG_ROUNDS = 4;\n\tstatic final int MAX_LOG_ROUNDS = 31;\n\n\t// Expanded Blowfish key\n\tprivate int P[] = new int[0];\n\n\tprivate int S[] = new int[0];\n\n\t/**\n\t * Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note\n\t * that this is <strong>not</strong> compatible with the standard MIME-base64\n\t * encoding.\n\t * @param d the byte array to encode\n\t * @param len the number of bytes to encode\n\t * @param rs the destination buffer for the base64-encoded string\n\t * @exception IllegalArgumentException if the length is invalid\n\t */\n\tstatic void encode_base64(byte d[], int len, StringBuilder rs) throws IllegalArgumentException {\n\t\tint off = 0;\n\t\tint c1, c2;\n\n\t\tif (len <= 0 || len > d.length) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid len\");\n\t\t}\n\n\t\twhile (off < len) {\n\t\t\tc1 = d[off++] & 0xff;\n\t\t\trs.append(base64_code[(c1 >> 2) & 0x3f]);\n\t\t\tc1 = (c1 & 0x03) << 4;\n\t\t\tif (off >= len) {\n\t\t\t\trs.append(base64_code[c1 & 0x3f]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tc2 = d[off++] & 0xff;\n\t\t\tc1 |= (c2 >> 4) & 0x0f;\n\t\t\trs.append(base64_code[c1 & 0x3f]);\n\t\t\tc1 = (c2 & 0x0f) << 2;\n\t\t\tif (off >= len) {\n\t\t\t\trs.append(base64_code[c1 & 0x3f]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tc2 = d[off++] & 0xff;\n\t\t\tc1 |= (c2 >> 6) & 0x03;\n\t\t\trs.append(base64_code[c1 & 0x3f]);\n\t\t\trs.append(base64_code[c2 & 0x3f]);\n\t\t}\n\t}\n\n\t/**\n\t * Look up the 3 bits base64-encoded by the specified character, range-checking againt\n\t * conversion table\n\t * @param x the base64-encoded value\n\t * @return the decoded value of x\n\t */\n\tprivate static byte char64(char x) {\n\t\tif (x < 0 || x >= index_64.length) {\n\t\t\treturn -1;\n\t\t}\n\t\treturn index_64[x];\n\t}\n\n\t/**\n\t * Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that\n\t * this is *not* compatible with the standard MIME-base64 encoding.\n\t * @param s the string to decode\n\t * @param maxolen the maximum number of bytes to decode\n\t * @return an array containing the decoded bytes\n\t * @throws IllegalArgumentException if maxolen is invalid\n\t */\n\tstatic byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException {\n\t\tStringBuilder rs = new StringBuilder();\n\t\tint off = 0, slen = s.length(), olen = 0;\n\t\tbyte ret[];\n\t\tbyte c1, c2, c3, c4, o;\n\n\t\tif (maxolen <= 0) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid maxolen\");\n\t\t}\n\n\t\twhile (off < slen - 1 && olen < maxolen) {\n\t\t\tc1 = char64(s.charAt(off++));\n\t\t\tc2 = char64(s.charAt(off++));\n\t\t\tif (c1 == -1 || c2 == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\to = (byte) (c1 << 2);\n\t\t\to |= (c2 & 0x30) >> 4;\n\t\t\trs.append((char) o);\n\t\t\tif (++olen >= maxolen || off >= slen) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tc3 = char64(s.charAt(off++));\n\t\t\tif (c3 == -1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\to = (byte) ((c2 & 0x0f) << 4);\n\t\t\to |= (c3 & 0x3c) >> 2;\n\t\t\trs.append((char) o);\n\t\t\tif (++olen >= maxolen || off >= slen) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tc4 = char64(s.charAt(off++));\n\t\t\to = (byte) ((c3 & 0x03) << 6);\n\t\t\to |= c4;\n\t\t\trs.append((char) o);\n\t\t\t++olen;\n\t\t}\n\n\t\tret = new byte[olen];\n\t\tfor (off = 0; off < olen; off++) {\n\t\t\tret[off] = (byte) rs.charAt(off);\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Blowfish encipher a single 64-bit block encoded as two 32-bit halves\n\t * @param lr an array containing the two 32-bit half blocks\n\t * @param off the position in the array of the blocks\n\t */\n\tprivate void encipher(int lr[], int off) {\n\t\tint i, n, l = lr[off], r = lr[off + 1];\n\n\t\tl ^= this.P[0];\n\t\tfor (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {\n\t\t\t// Feistel substitution on left word\n\t\t\tn = this.S[(l >> 24) & 0xff];\n\t\t\tn += this.S[0x100 | ((l >> 16) & 0xff)];\n\t\t\tn ^= this.S[0x200 | ((l >> 8) & 0xff)];\n\t\t\tn += this.S[0x300 | (l & 0xff)];\n\t\t\tr ^= n ^ this.P[++i];\n\n\t\t\t// Feistel substitution on right word\n\t\t\tn = this.S[(r >> 24) & 0xff];\n\t\t\tn += this.S[0x100 | ((r >> 16) & 0xff)];\n\t\t\tn ^= this.S[0x200 | ((r >> 8) & 0xff)];\n\t\t\tn += this.S[0x300 | (r & 0xff)];\n\t\t\tl ^= n ^ this.P[++i];\n\t\t}\n\t\tlr[off] = r ^ this.P[BLOWFISH_NUM_ROUNDS + 1];\n\t\tlr[off + 1] = l;\n\t}\n\n\t/**\n\t * Cycically extract a word of key material\n\t * @param data the string to extract the data from\n\t * @param offp a \"pointer\" (as a one-entry array) to the current offset into data\n\t * @param signp a \"pointer\" (as a one-entry array) to the cumulative flag for\n\t * non-benign sign extension\n\t * @return correct and buggy next word of material from data as int[2]\n\t */\n\tprivate static int[] streamtowords(byte data[], int offp[], int signp[]) {\n\t\tint i;\n\t\tint words[] = { 0, 0 };\n\t\tint off = offp[0];\n\t\tint sign = signp[0];\n\n\t\tfor (i = 0; i < 4; i++) {\n\t\t\twords[0] = (words[0] << 8) | (data[off] & 0xff);\n\t\t\twords[1] = (words[1] << 8) | data[off]; // sign extension bug\n\t\t\tif (i > 0) {\n\t\t\t\tsign |= words[1] & 0x80;\n\t\t\t}\n\t\t\toff = (off + 1) % data.length;\n\t\t}\n\n\t\toffp[0] = off;\n\t\tsignp[0] = sign;\n\t\treturn words;\n\t}\n\n\t/**\n\t * Cycically extract a word of key material\n\t * @param data the string to extract the data from\n\t * @param offp a \"pointer\" (as a one-entry array) to the current offset into data\n\t * @return the next word of material from data\n\t */\n\tprivate static int streamtoword(byte data[], int offp[]) {\n\t\tint signp[] = { 0 };\n\t\treturn streamtowords(data, offp, signp)[0];\n\t}\n\n\t/**\n\t * Cycically extract a word of key material, with sign-extension bug\n\t * @param data the string to extract the data from\n\t * @param offp a \"pointer\" (as a one-entry array) to the current offset into data\n\t * @return the next word of material from data\n\t */\n\tprivate static int streamtoword_bug(byte data[], int offp[]) {\n\t\tint signp[] = { 0 };\n\t\treturn streamtowords(data, offp, signp)[1];\n\t}\n\n\t/**\n\t * Initialise the Blowfish key schedule\n\t */\n\tprivate void init_key() {\n\t\tthis.P = P_orig.clone();\n\t\tthis.S = S_orig.clone();\n\t}\n\n\t/**\n\t * Key the Blowfish cipher\n\t * @param key an array containing the key\n\t * @param sign_ext_bug true to implement the 2x bug\n\t * @param safety bit 16 is set when the safety measure is requested\n\t */\n\tprivate void key(byte key[], boolean sign_ext_bug, int safety) {\n\t\tint i;\n\t\tint koffp[] = { 0 };\n\t\tint lr[] = { 0, 0 };\n\t\tint plen = this.P.length, slen = this.S.length;\n\n\t\tfor (i = 0; i < plen; i++) {\n\t\t\tif (!sign_ext_bug) {\n\t\t\t\tthis.P[i] = this.P[i] ^ streamtoword(key, koffp);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.P[i] = this.P[i] ^ streamtoword_bug(key, koffp);\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0; i < plen; i += 2) {\n\t\t\tencipher(lr, 0);\n\t\t\tthis.P[i] = lr[0];\n\t\t\tthis.P[i + 1] = lr[1];\n\t\t}\n\n\t\tfor (i = 0; i < slen; i += 2) {\n\t\t\tencipher(lr, 0);\n\t\t\tthis.S[i] = lr[0];\n\t\t\tthis.S[i + 1] = lr[1];\n\t\t}\n\t}\n\n\t/**\n\t * Perform the \"enhanced key schedule\" step described by Provos and Mazieres in \"A\n\t * Future-Adaptable Password Scheme\" https://www.openbsd.org/papers/bcrypt-paper.ps\n\t * @param data salt information\n\t * @param key password information\n\t * @param sign_ext_bug true to implement the 2x bug\n\t * @param safety bit 16 is set when the safety measure is requested\n\t */\n\tprivate void ekskey(byte data[], byte key[], boolean sign_ext_bug, int safety) {\n\t\tint i;\n\t\tint koffp[] = { 0 }, doffp[] = { 0 };\n\t\tint lr[] = { 0, 0 };\n\t\tint plen = this.P.length, slen = this.S.length;\n\t\tint signp[] = { 0 }; // non-benign sign-extension flag\n\t\tint diff = 0; // zero iff correct and buggy are same\n\n\t\tfor (i = 0; i < plen; i++) {\n\t\t\tint words[] = streamtowords(key, koffp, signp);\n\t\t\tdiff |= words[0] ^ words[1];\n\t\t\tthis.P[i] = this.P[i] ^ words[sign_ext_bug ? 1 : 0];\n\t\t}\n\n\t\tint sign = signp[0];\n\n\t\t/*\n\t\t * At this point, \"diff\" is zero iff the correct and buggy algorithms produced\n\t\t * exactly the same result. If so and if \"sign\" is non-zero, which indicates that\n\t\t * there was a non-benign sign extension, this means that we have a collision\n\t\t * between the correctly computed hash for this password and a set of passwords\n\t\t * that could be supplied to the buggy algorithm. Our safety measure is meant to\n\t\t * protect from such many-buggy to one-correct collisions, by deviating from the\n\t\t * correct algorithm in such cases. Let's check for this.\n\t\t */\n\t\tdiff |= diff >> 16; /* still zero iff exact match */\n\t\tdiff &= 0xffff; /* ditto */\n\t\tdiff += 0xffff; /* bit 16 set iff \"diff\" was non-zero (on non-match) */\n\t\tsign <<= 9; /* move the non-benign sign extension flag to bit 16 */\n\t\tsign &= ~diff & safety; /* action needed? */\n\n\t\t/*\n\t\t * If we have determined that we need to deviate from the correct algorithm, flip\n\t\t * bit 16 in initial expanded key. (The choice of 16 is arbitrary, but let's stick\n\t\t * to it now. It came out of the approach we used above, and it's not any worse\n\t\t * than any other choice we could make.)\n\t\t *\n\t\t * It is crucial that we don't do the same to the expanded key used in the main\n\t\t * Eksblowfish loop. By doing it to only one of these two, we deviate from a state\n\t\t * that could be directly specified by a password to the buggy algorithm (and to\n\t\t * the fully correct one as well, but that's a side-effect).\n\t\t */\n\t\tthis.P[0] ^= sign;\n\n\t\tfor (i = 0; i < plen; i += 2) {\n\t\t\tlr[0] ^= streamtoword(data, doffp);\n\t\t\tlr[1] ^= streamtoword(data, doffp);\n\t\t\tencipher(lr, 0);\n\t\t\tthis.P[i] = lr[0];\n\t\t\tthis.P[i + 1] = lr[1];\n\t\t}\n\n\t\tfor (i = 0; i < slen; i += 2) {\n\t\t\tlr[0] ^= streamtoword(data, doffp);\n\t\t\tlr[1] ^= streamtoword(data, doffp);\n\t\t\tencipher(lr, 0);\n\t\t\tthis.S[i] = lr[0];\n\t\t\tthis.S[i + 1] = lr[1];\n\t\t}\n\t}\n\n\tstatic long roundsForLogRounds(int log_rounds) {\n\t\tif (log_rounds < 4 || log_rounds > 31) {\n\t\t\tthrow new IllegalArgumentException(\"Bad number of rounds\");\n\t\t}\n\t\treturn 1L << log_rounds;\n\t}\n\n\t/**\n\t * Perform the central password hashing step in the bcrypt scheme\n\t * @param password the password to hash\n\t * @param salt the binary salt to hash with the password\n\t * @param log_rounds the binary logarithm of the number of rounds of hashing to apply\n\t * @param sign_ext_bug true to implement the 2x bug\n\t * @param safety bit 16 is set when the safety measure is requested\n\t * @return an array containing the binary hashed password\n\t */\n\tprivate byte[] crypt_raw(byte password[], byte salt[], int log_rounds, boolean sign_ext_bug, int safety,\n\t\t\tboolean for_check) {\n\t\tint cdata[] = bf_crypt_ciphertext.clone();\n\t\tint clen = cdata.length;\n\n\t\tlong rounds;\n\t\tif (log_rounds < 4 || log_rounds > 31) {\n\t\t\tif (!for_check) {\n\t\t\t\tthrow new IllegalArgumentException(\"Bad number of rounds\");\n\t\t\t}\n\t\t\tif (log_rounds != 0) {\n\t\t\t\tthrow new IllegalArgumentException(\"Bad number of rounds\");\n\t\t\t}\n\t\t\trounds = 0;\n\t\t}\n\t\telse {\n\t\t\trounds = roundsForLogRounds(log_rounds);\n\t\t\tif (rounds < 16 || rounds > 2147483648L) {\n\t\t\t\tthrow new IllegalArgumentException(\"Bad number of rounds\");\n\t\t\t}\n\t\t}\n\n\t\tif (salt.length != BCRYPT_SALT_LEN) {\n\t\t\tthrow new IllegalArgumentException(\"Bad salt length\");\n\t\t}\n\n\t\tinit_key();\n\t\tekskey(salt, password, sign_ext_bug, safety);\n\t\tfor (int i = 0; i < rounds; i++) {\n\t\t\tkey(password, sign_ext_bug, safety);\n\t\t\tkey(salt, false, safety);\n\t\t}\n\n\t\tfor (int i = 0; i < 64; i++) {\n\t\t\tfor (int j = 0; j < (clen >> 1); j++) {\n\t\t\t\tencipher(cdata, j << 1);\n\t\t\t}\n\t\t}\n\n\t\tbyte[] ret = new byte[clen * 4];\n\t\tfor (int i = 0, j = 0; i < clen; i++) {\n\t\t\tret[j++] = (byte) ((cdata[i] >> 24) & 0xff);\n\t\t\tret[j++] = (byte) ((cdata[i] >> 16) & 0xff);\n\t\t\tret[j++] = (byte) ((cdata[i] >> 8) & 0xff);\n\t\t\tret[j++] = (byte) (cdata[i] & 0xff);\n\t\t}\n\t\treturn ret;\n\t}\n\n\tprivate static String hashpwforcheck(byte[] passwordb, String salt) {\n\t\treturn hashpw(passwordb, salt, true);\n\t}\n\n\t/**\n\t * Hash a password using the OpenBSD bcrypt scheme\n\t * @param password the password to hash\n\t * @param salt the salt to hash with (perhaps generated using BCrypt.gensalt)\n\t * @return the hashed password\n\t */\n\tpublic static String hashpw(String password, String salt) {\n\t\tbyte passwordb[];\n\n\t\tpasswordb = password.getBytes(StandardCharsets.UTF_8);\n\n\t\treturn hashpw(passwordb, salt);\n\t}\n\n\t/**\n\t * Hash a password using the OpenBSD bcrypt scheme\n\t * @param passwordb the password to hash, as a byte array\n\t * @param salt the salt to hash with (perhaps generated using BCrypt.gensalt)\n\t * @return the hashed password\n\t */\n\tpublic static String hashpw(byte passwordb[], String salt) {\n\t\treturn hashpw(passwordb, salt, false);\n\t}\n\n\tprivate static String hashpw(byte passwordb[], String salt, boolean for_check) {\n\t\tBCrypt B;\n\t\tString real_salt;\n\t\tbyte saltb[], hashed[];\n\t\tchar minor = (char) 0;\n\t\tint rounds, off;\n\t\tStringBuilder rs = new StringBuilder();\n\n\t\t// Enforce max length for new passwords only\n\t\tif (!for_check && passwordb.length > 72) {\n\t\t\tthrow new IllegalArgumentException(\"password cannot be more than 72 bytes\");\n\t\t}\n\t\tif (salt == null) {\n\t\t\tthrow new IllegalArgumentException(\"salt cannot be null\");\n\t\t}\n\n\t\tint saltLength = salt.length();\n\n\t\tif (saltLength < 28) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid salt\");\n\t\t}\n\n\t\tif (salt.charAt(0) != '$' || salt.charAt(1) != '2') {\n\t\t\tthrow new IllegalArgumentException(\"Invalid salt version\");\n\t\t}\n\t\tif (salt.charAt(2) == '$') {\n\t\t\toff = 3;\n\t\t}\n\t\telse {\n\t\t\tminor = salt.charAt(2);\n\t\t\tif ((minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b') || salt.charAt(3) != '$') {\n\t\t\t\tthrow new IllegalArgumentException(\"Invalid salt revision\");\n\t\t\t}\n\t\t\toff = 4;\n\t\t}\n\n\t\t// Extract number of rounds\n\t\tif (salt.charAt(off + 2) > '$') {\n\t\t\tthrow new IllegalArgumentException(\"Missing salt rounds\");\n\t\t}\n\n\t\tif (off == 4 && saltLength < 29) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid salt\");\n\t\t}\n\t\trounds = Integer.parseInt(salt.substring(off, off + 2));\n\n\t\treal_salt = salt.substring(off + 3, off + 25);\n\t\tsaltb = decode_base64(real_salt, BCRYPT_SALT_LEN);\n\n\t\tif (minor >= 'a') {\n\t\t\tpasswordb = Arrays.copyOf(passwordb, passwordb.length + 1);\n\t\t}\n\n\t\tB = new BCrypt();\n\t\thashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0, for_check);\n\n\t\trs.append(\"$2\");\n\t\tif (minor >= 'a') {\n\t\t\trs.append(minor);\n\t\t}\n\t\trs.append(\"$\");\n\t\tif (rounds < 10) {\n\t\t\trs.append(\"0\");\n\t\t}\n\t\trs.append(rounds);\n\t\trs.append(\"$\");\n\t\tencode_base64(saltb, saltb.length, rs);\n\t\tencode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);\n\t\treturn rs.toString();\n\t}\n\n\t/**\n\t * Generate a salt for use with the BCrypt.hashpw() method\n\t * @param prefix the prefix value (default $2a)\n\t * @param log_rounds the log2 of the number of rounds of hashing to apply - the work\n\t * factor therefore increases as 2**log_rounds.\n\t * @param random an instance of SecureRandom to use\n\t * @return an encoded salt value\n\t * @exception IllegalArgumentException if prefix or log_rounds is invalid\n\t */\n\tpublic static String gensalt(String prefix, int log_rounds, SecureRandom random) throws IllegalArgumentException {\n\t\tStringBuilder rs = new StringBuilder();\n\t\tbyte rnd[] = new byte[BCRYPT_SALT_LEN];\n\n\t\tif (!prefix.startsWith(\"$2\")\n\t\t\t\t|| (prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y' && prefix.charAt(2) != 'b')) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid prefix\");\n\t\t}\n\t\tif (log_rounds < 4 || log_rounds > 31) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid log_rounds\");\n\t\t}\n\n\t\trandom.nextBytes(rnd);\n\n\t\trs.append(\"$2\");\n\t\trs.append(prefix.charAt(2));\n\t\trs.append(\"$\");\n\t\tif (log_rounds < 10) {\n\t\t\trs.append(\"0\");\n\t\t}\n\t\trs.append(log_rounds);\n\t\trs.append(\"$\");\n\t\tencode_base64(rnd, rnd.length, rs);\n\t\treturn rs.toString();\n\t}\n\n\t/**\n\t * Generate a salt for use with the BCrypt.hashpw() method\n\t * @param prefix the prefix value (default $2a)\n\t * @param log_rounds the log2 of the number of rounds of hashing to apply - the work\n\t * factor therefore increases as 2**log_rounds.\n\t * @return an encoded salt value\n\t * @exception IllegalArgumentException if prefix or log_rounds is invalid\n\t */\n\tpublic static String gensalt(String prefix, int log_rounds) throws IllegalArgumentException {\n\t\treturn gensalt(prefix, log_rounds, new SecureRandom());\n\t}\n\n\t/**\n\t * Generate a salt for use with the BCrypt.hashpw() method\n\t * @param log_rounds the log2 of the number of rounds of hashing to apply - the work\n\t * factor therefore increases as 2**log_rounds.\n\t * @param random an instance of SecureRandom to use\n\t * @return an encoded salt value\n\t * @exception IllegalArgumentException if log_rounds is invalid\n\t */\n\tpublic static String gensalt(int log_rounds, SecureRandom random) throws IllegalArgumentException {\n\t\treturn gensalt(\"$2a\", log_rounds, random);\n\t}\n\n\t/**\n\t * Generate a salt for use with the BCrypt.hashpw() method\n\t * @param log_rounds the log2 of the number of rounds of hashing to apply - the work\n\t * factor therefore increases as 2**log_rounds.\n\t * @return an encoded salt value\n\t * @exception IllegalArgumentException if log_rounds is invalid\n\t */\n\tpublic static String gensalt(int log_rounds) throws IllegalArgumentException {\n\t\treturn gensalt(log_rounds, new SecureRandom());\n\t}\n\n\tpublic static String gensalt(String prefix) {\n\t\treturn gensalt(prefix, GENSALT_DEFAULT_LOG2_ROUNDS);\n\t}\n\n\t/**\n\t * Generate a salt for use with the BCrypt.hashpw() method, selecting a reasonable\n\t * default for the number of hashing rounds to apply\n\t * @return an encoded salt value\n\t */\n\tpublic static String gensalt() {\n\t\treturn gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);\n\t}\n\n\t/**\n\t * Check that a plaintext password matches a previously hashed one\n\t * @param plaintext the plaintext password to verify\n\t * @param hashed the previously-hashed password\n\t * @return true if the passwords match, false otherwise\n\t */\n\tpublic static boolean checkpw(String plaintext, String hashed) {\n\t\tbyte[] passwordb = plaintext.getBytes(StandardCharsets.UTF_8);\n\t\treturn equalsNoEarlyReturn(hashed, hashpwforcheck(passwordb, hashed));\n\t}\n\n\t/**\n\t * Check that a password (as a byte array) matches a previously hashed one\n\t * @param passwordb the password to verify, as a byte array\n\t * @param hashed the previously-hashed password\n\t * @return true if the passwords match, false otherwise\n\t * @since 5.3\n\t */\n\tpublic static boolean checkpw(byte[] passwordb, String hashed) {\n\t\treturn equalsNoEarlyReturn(hashed, hashpwforcheck(passwordb, hashed));\n\t}\n\n\tstatic boolean equalsNoEarlyReturn(String a, String b) {\n\t\treturn MessageDigest.isEqual(a.getBytes(StandardCharsets.UTF_8), b.getBytes(StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.bcrypt;\n\nimport java.security.SecureRandom;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;\n\n/**\n * Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients\n * can optionally supply a \"version\" ($2a, $2b, $2y) and a \"strength\" (a.k.a. log rounds\n * in BCrypt) and a SecureRandom instance. The larger the strength parameter the more work\n * will have to be done (exponentially) to hash the passwords. The default value is 10.\n *\n * @author Dave Syer\n */\npublic class BCryptPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate Pattern BCRYPT_PATTERN = Pattern.compile(\"\\\\A\\\\$2(a|y|b)?\\\\$(\\\\d\\\\d)\\\\$[./0-9A-Za-z]{53}\");\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final int strength;\n\n\tprivate final BCryptVersion version;\n\n\tprivate final @Nullable SecureRandom random;\n\n\tpublic BCryptPasswordEncoder() {\n\t\tthis(-1);\n\t}\n\n\t/**\n\t * @param strength the log rounds to use, between 4 and 31\n\t */\n\tpublic BCryptPasswordEncoder(int strength) {\n\t\tthis(strength, null);\n\t}\n\n\t/**\n\t * @param version the version of bcrypt, can be 2a,2b,2y\n\t */\n\tpublic BCryptPasswordEncoder(BCryptVersion version) {\n\t\tthis(version, null);\n\t}\n\n\t/**\n\t * @param version the version of bcrypt, can be 2a,2b,2y\n\t * @param random the secure random instance to use\n\t */\n\tpublic BCryptPasswordEncoder(BCryptVersion version, @Nullable SecureRandom random) {\n\t\tthis(version, -1, random);\n\t}\n\n\t/**\n\t * @param strength the log rounds to use, between 4 and 31\n\t * @param random the secure random instance to use\n\t */\n\tpublic BCryptPasswordEncoder(int strength, @Nullable SecureRandom random) {\n\t\tthis(BCryptVersion.$2A, strength, random);\n\t}\n\n\t/**\n\t * @param version the version of bcrypt, can be 2a,2b,2y\n\t * @param strength the log rounds to use, between 4 and 31\n\t */\n\tpublic BCryptPasswordEncoder(BCryptVersion version, int strength) {\n\t\tthis(version, strength, null);\n\t}\n\n\t/**\n\t * @param version the version of bcrypt, can be 2a,2b,2y\n\t * @param strength the log rounds to use, between 4 and 31\n\t * @param random the secure random instance to use\n\t */\n\tpublic BCryptPasswordEncoder(BCryptVersion version, int strength, @Nullable SecureRandom random) {\n\t\tif (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {\n\t\t\tthrow new IllegalArgumentException(\"Bad strength\");\n\t\t}\n\t\tthis.version = version;\n\t\tthis.strength = (strength == -1) ? 10 : strength;\n\t\tthis.random = random;\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tString salt = getSalt();\n\t\treturn BCrypt.hashpw(rawPassword.toString(), salt);\n\t}\n\n\tprivate String getSalt() {\n\t\tif (this.random != null) {\n\t\t\treturn BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);\n\t\t}\n\t\treturn BCrypt.gensalt(this.version.getVersion(), this.strength);\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tif (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {\n\t\t\tthis.logger.warn(\"Encoded password does not look like BCrypt\");\n\t\t\treturn false;\n\t\t}\n\t\treturn BCrypt.checkpw(rawPassword.toString(), encodedPassword);\n\t}\n\n\t@Override\n\tprotected boolean upgradeEncodingNonNull(String encodedPassword) {\n\t\tMatcher matcher = this.BCRYPT_PATTERN.matcher(encodedPassword);\n\t\tif (!matcher.matches()) {\n\t\t\tthrow new IllegalArgumentException(\"Encoded password does not look like BCrypt: \" + encodedPassword);\n\t\t}\n\t\tint strength = Integer.parseInt(matcher.group(2));\n\t\treturn strength < this.strength;\n\t}\n\n\t/**\n\t * Stores the default bcrypt version for use in configuration.\n\t *\n\t * @author Lin Feng\n\t */\n\tpublic enum BCryptVersion {\n\n\t\t$2A(\"$2a\"),\n\n\t\t$2Y(\"$2y\"),\n\n\t\t$2B(\"$2b\");\n\n\t\tprivate final String version;\n\n\t\tBCryptVersion(String version) {\n\t\t\tthis.version = version;\n\t\t}\n\n\t\tpublic String getVersion() {\n\t\t\treturn this.version;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/bcrypt/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.bcrypt;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/codec/Hex.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.codec;\n\n/**\n * Hex data encoder. Converts byte arrays (such as those obtained from message digests)\n * into hexadecimal string representation.\n * <p>\n * For internal use only.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic final class Hex {\n\n\tprivate static final char[] HEX = \"0123456789abcdef\".toCharArray();\n\n\tprivate Hex() {\n\t}\n\n\tpublic static char[] encode(byte[] bytes) {\n\t\tfinal int nBytes = bytes.length;\n\t\tchar[] result = new char[2 * nBytes];\n\t\tint j = 0;\n\t\tfor (byte aByte : bytes) {\n\t\t\t// Char for top 4 bits\n\t\t\tresult[j++] = HEX[(0xF0 & aByte) >>> 4];\n\t\t\t// Bottom 4\n\t\t\tresult[j++] = HEX[(0x0F & aByte)];\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static byte[] decode(CharSequence s) {\n\t\tint nChars = s.length();\n\t\tif (nChars % 2 != 0) {\n\t\t\tthrow new IllegalArgumentException(\"Hex-encoded string must have an even number of characters\");\n\t\t}\n\t\tbyte[] result = new byte[nChars / 2];\n\t\tfor (int i = 0; i < nChars; i += 2) {\n\t\t\tint msb = Character.digit(s.charAt(i), 16);\n\t\t\tint lsb = Character.digit(s.charAt(i + 1), 16);\n\t\t\tif (msb < 0 || lsb < 0) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Detected a Non-hex character at \" + (i + 1) + \" or \" + (i + 2) + \" position\");\n\t\t\t}\n\t\t\tresult[i / 2] = (byte) ((msb << 4) | lsb);\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/codec/Utf8.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.codec;\n\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.CharacterCodingException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * UTF-8 Charset encoder/decoder.\n * <p>\n * For internal use only.\n *\n * @author Luke Taylor\n */\npublic final class Utf8 {\n\n\tprivate static final Charset CHARSET = StandardCharsets.UTF_8;\n\n\tprivate Utf8() {\n\t}\n\n\t/**\n\t * Get the bytes of the String in UTF-8 encoded form.\n\t */\n\tpublic static byte[] encode(CharSequence string) {\n\t\tif (string == null) {\n\t\t\tthrow new IllegalArgumentException(\"String cannot be null\");\n\t\t}\n\t\ttry {\n\t\t\tByteBuffer bytes = CHARSET.newEncoder().encode(CharBuffer.wrap(string));\n\t\t\tbyte[] bytesCopy = new byte[bytes.limit()];\n\t\t\tSystem.arraycopy(bytes.array(), 0, bytesCopy, 0, bytes.limit());\n\t\t\treturn bytesCopy;\n\t\t}\n\t\tcatch (CharacterCodingException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Encoding failed\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Decode the bytes in UTF-8 form into a String.\n\t */\n\tpublic static String decode(byte[] bytes) {\n\t\ttry {\n\t\t\treturn CHARSET.newDecoder().decode(ByteBuffer.wrap(bytes)).toString();\n\t\t}\n\t\tcatch (CharacterCodingException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Decoding failed\", ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/codec/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal codec classes. Only intended for use within the framework.\n */\n@NullMarked\npackage org.springframework.security.crypto.codec;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.security.spec.AlgorithmParameterSpec;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.GCMParameterSpec;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.PBEKeySpec;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.util.EncodingUtils;\n\n/**\n * Encryptor that uses AES encryption.\n *\n * @author Keith Donald\n * @author Dave Syer\n */\npublic final class AesBytesEncryptor implements BytesEncryptor {\n\n\tprivate final SecretKey secretKey;\n\n\tprivate final Cipher encryptor;\n\n\tprivate final Cipher decryptor;\n\n\tprivate final BytesKeyGenerator ivGenerator;\n\n\tprivate CipherAlgorithm alg;\n\n\tprivate static final String AES_CBC_ALGORITHM = \"AES/CBC/PKCS5Padding\";\n\n\tprivate static final String AES_GCM_ALGORITHM = \"AES/GCM/NoPadding\";\n\n\t/**\n\t * Constructs an encryptor that uses AES encryption. Example: <code>\n\t * AesBytesEncryptor encryptor = new AesBytesEncryptor(yourPassword, 5c0744940b5c369b);\n\t * </code> Constructed encryptor uses a 16-byte IV and CBC mode encryption. To specify\n\t * a custom length IV, use\n\t * {@link AesBytesEncryptor#AesBytesEncryptor(String, CharSequence, BytesKeyGenerator)\n\t * AesBytesEncryptor(String, CharSequence, BytesKeyGenerator)}. To specify both, a\n\t * custom length IV and a different encryption mode, use\n\t * {@link AesBytesEncryptor#AesBytesEncryptor(String, CharSequence, BytesKeyGenerator, CipherAlgorithm)\n\t * AesBytesEncryptor(String, CharSequence, BytesKeyGenerator, CipherAlgorithm)}.\n\t * @param password the password value\n\t * @param salt the hex-encoded salt value\n\t */\n\tpublic AesBytesEncryptor(String password, CharSequence salt) {\n\t\tthis(password, salt, null);\n\t}\n\n\t/**\n\t * Constructs an encryptor that uses AES encryption. Example: <code>\n\t * AesBytesEncryptor encryptor =\n\t * \t\tnew AesBytesEncryptor(yourPassword, 5c0744940b5c369b, KeyGenerators.secureRandom(16));\n\t * </code> Constructed encryptor uses CBC mode encryption. To specify a different\n\t * encryption mode, use\n\t * {@link AesBytesEncryptor#AesBytesEncryptor(String, CharSequence, BytesKeyGenerator, CipherAlgorithm)\n\t * AesBytesEncryptor(String, CharSequence, BytesKeyGenerator, CipherAlgorithm)}.\n\t * @param password the password value\n\t * @param salt the hex-encoded salt value\n\t * @param ivGenerator the generator used to generate the initialization vector\n\t */\n\tpublic AesBytesEncryptor(String password, CharSequence salt, @Nullable BytesKeyGenerator ivGenerator) {\n\t\tthis(password, salt, ivGenerator, CipherAlgorithm.CBC);\n\t}\n\n\t/**\n\t * Constructs an encryptor that uses AES encryption. Example: <code>\n\t * AesBytesEncryptor encryptor =\n\t * \t\tnew AesBytesEncryptor(yourPassword, 5c0744940b5c369b, KeyGenerators.secureRandom(16), CipherAlgorithm.GCM);\n\t * </code>\n\t * @param password the password value\n\t * @param salt the hex-encoded salt value\n\t * @param ivGenerator the generator used to generate the initialization vector\n\t * @param alg the {@link CipherAlgorithm} to be used\n\t */\n\tpublic AesBytesEncryptor(String password, CharSequence salt, @Nullable BytesKeyGenerator ivGenerator,\n\t\t\tCipherAlgorithm alg) {\n\t\tthis(CipherUtils.newSecretKey(\"PBKDF2WithHmacSHA1\",\n\t\t\t\tnew PBEKeySpec(password.toCharArray(), Hex.decode(salt), 1024, 256)), ivGenerator, alg);\n\t}\n\n\t/**\n\t * Constructs an encryptor that uses AES encryption.\n\t * @param secretKey the secret (symmetric) key\n\t * @param ivGenerator the generator used to generate the initialization vector. If\n\t * null, then a default algorithm will be used based on the provided\n\t * {@link CipherAlgorithm}\n\t * @param alg the {@link CipherAlgorithm} to be used\n\t */\n\tpublic AesBytesEncryptor(SecretKey secretKey, @Nullable BytesKeyGenerator ivGenerator, CipherAlgorithm alg) {\n\t\tthis.secretKey = new SecretKeySpec(secretKey.getEncoded(), \"AES\");\n\t\tthis.alg = alg;\n\t\tthis.encryptor = alg.createCipher();\n\t\tthis.decryptor = alg.createCipher();\n\t\tthis.ivGenerator = (ivGenerator != null) ? ivGenerator : alg.defaultIvGenerator();\n\t}\n\n\t@Override\n\tpublic byte[] encrypt(byte[] bytes) {\n\t\tsynchronized (this.encryptor) {\n\t\t\tbyte[] iv = this.ivGenerator.generateKey();\n\t\t\tCipherUtils.initCipher(this.encryptor, Cipher.ENCRYPT_MODE, this.secretKey, this.alg.getParameterSpec(iv));\n\t\t\tbyte[] encrypted = CipherUtils.doFinal(this.encryptor, bytes);\n\t\t\treturn (this.ivGenerator != NULL_IV_GENERATOR) ? EncodingUtils.concatenate(iv, encrypted) : encrypted;\n\t\t}\n\t}\n\n\t@Override\n\tpublic byte[] decrypt(byte[] encryptedBytes) {\n\t\tsynchronized (this.decryptor) {\n\t\t\tbyte[] iv = iv(encryptedBytes);\n\t\t\tCipherUtils.initCipher(this.decryptor, Cipher.DECRYPT_MODE, this.secretKey, this.alg.getParameterSpec(iv));\n\t\t\treturn CipherUtils.doFinal(this.decryptor,\n\t\t\t\t\t(this.ivGenerator != NULL_IV_GENERATOR) ? encrypted(encryptedBytes, iv.length) : encryptedBytes);\n\t\t}\n\t}\n\n\tprivate byte[] iv(byte[] encrypted) {\n\t\treturn (this.ivGenerator != NULL_IV_GENERATOR)\n\t\t\t\t? EncodingUtils.subArray(encrypted, 0, this.ivGenerator.getKeyLength())\n\t\t\t\t: NULL_IV_GENERATOR.generateKey();\n\t}\n\n\tprivate byte[] encrypted(byte[] encryptedBytes, int ivLength) {\n\t\treturn EncodingUtils.subArray(encryptedBytes, ivLength, encryptedBytes.length);\n\t}\n\n\tprivate static final BytesKeyGenerator NULL_IV_GENERATOR = new BytesKeyGenerator() {\n\n\t\tprivate final byte[] VALUE = new byte[16];\n\n\t\t@Override\n\t\tpublic int getKeyLength() {\n\t\t\treturn this.VALUE.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic byte[] generateKey() {\n\t\t\treturn this.VALUE;\n\t\t}\n\n\t};\n\n\tpublic enum CipherAlgorithm {\n\n\t\tCBC(AES_CBC_ALGORITHM, NULL_IV_GENERATOR),\n\n\t\tGCM(AES_GCM_ALGORITHM, KeyGenerators.secureRandom(16));\n\n\t\tprivate BytesKeyGenerator ivGenerator;\n\n\t\tprivate String name;\n\n\t\tCipherAlgorithm(String name, BytesKeyGenerator ivGenerator) {\n\t\t\tthis.name = name;\n\t\t\tthis.ivGenerator = ivGenerator;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\tpublic AlgorithmParameterSpec getParameterSpec(byte[] iv) {\n\t\t\treturn (this != CBC) ? new GCMParameterSpec(128, iv) : new IvParameterSpec(iv);\n\t\t}\n\n\t\tpublic Cipher createCipher() {\n\t\t\treturn CipherUtils.newCipher(this.toString());\n\t\t}\n\n\t\tpublic BytesKeyGenerator defaultIvGenerator() {\n\t\t\treturn this.ivGenerator;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport org.bouncycastle.crypto.PBEParametersGenerator;\nimport org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;\nimport org.bouncycastle.crypto.params.KeyParameter;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\n\n/**\n * Base class for AES-256 encryption using Bouncy Castle.\n *\n * @author William Tran\n *\n */\nabstract class BouncyCastleAesBytesEncryptor implements BytesEncryptor {\n\n\tfinal KeyParameter secretKey;\n\n\tfinal BytesKeyGenerator ivGenerator;\n\n\tBouncyCastleAesBytesEncryptor(String password, CharSequence salt) {\n\t\tthis(password, salt, KeyGenerators.secureRandom(16));\n\t}\n\n\tBouncyCastleAesBytesEncryptor(String password, CharSequence salt, BytesKeyGenerator ivGenerator) {\n\t\tif (ivGenerator.getKeyLength() != 16) {\n\t\t\tthrow new IllegalArgumentException(\"ivGenerator key length != block size 16\");\n\t\t}\n\t\tthis.ivGenerator = ivGenerator;\n\t\tPBEParametersGenerator keyGenerator = new PKCS5S2ParametersGenerator();\n\t\tbyte[] pkcs12PasswordBytes = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password.toCharArray());\n\t\tkeyGenerator.init(pkcs12PasswordBytes, Hex.decode(salt), 1024);\n\t\tthis.secretKey = (KeyParameter) keyGenerator.generateDerivedParameters(256);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport org.bouncycastle.crypto.BufferedBlockCipher;\nimport org.bouncycastle.crypto.InvalidCipherTextException;\nimport org.bouncycastle.crypto.engines.AESEngine;\nimport org.bouncycastle.crypto.modes.CBCBlockCipher;\nimport org.bouncycastle.crypto.modes.CBCModeCipher;\nimport org.bouncycastle.crypto.paddings.PKCS7Padding;\nimport org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;\nimport org.bouncycastle.crypto.params.ParametersWithIV;\n\nimport org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgorithm;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.util.EncodingUtils;\n\n/**\n * An Encryptor equivalent to {@link AesBytesEncryptor} using {@link CipherAlgorithm#CBC}\n * that uses Bouncy Castle instead of JCE. The algorithm is equivalent to\n * \"AES/CBC/PKCS5Padding\".\n *\n * @author William Tran\n */\npublic class BouncyCastleAesCbcBytesEncryptor extends BouncyCastleAesBytesEncryptor {\n\n\tpublic BouncyCastleAesCbcBytesEncryptor(String password, CharSequence salt) {\n\t\tsuper(password, salt);\n\t}\n\n\tpublic BouncyCastleAesCbcBytesEncryptor(String password, CharSequence salt, BytesKeyGenerator ivGenerator) {\n\t\tsuper(password, salt, ivGenerator);\n\t}\n\n\t@Override\n\tpublic byte[] encrypt(byte[] bytes) {\n\t\tbyte[] iv = this.ivGenerator.generateKey();\n\t\tCBCModeCipher cbcModeCipher = CBCBlockCipher.newInstance(AESEngine.newInstance());\n\t\tPaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(cbcModeCipher, new PKCS7Padding());\n\t\tblockCipher.init(true, new ParametersWithIV(this.secretKey, iv));\n\t\tbyte[] encrypted = process(blockCipher, bytes);\n\t\treturn (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted;\n\t}\n\n\t@Override\n\tpublic byte[] decrypt(byte[] encryptedBytes) {\n\t\tCBCModeCipher cbcModeCipher = CBCBlockCipher.newInstance(AESEngine.newInstance());\n\t\tbyte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength());\n\t\tencryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length);\n\t\tPaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(cbcModeCipher, new PKCS7Padding());\n\t\tblockCipher.init(false, new ParametersWithIV(this.secretKey, iv));\n\t\treturn process(blockCipher, encryptedBytes);\n\t}\n\n\tprivate byte[] process(BufferedBlockCipher blockCipher, byte[] in) {\n\t\tbyte[] buf = new byte[blockCipher.getOutputSize(in.length)];\n\t\tint bytesWritten = blockCipher.processBytes(in, 0, in.length, buf, 0);\n\t\ttry {\n\t\t\tbytesWritten += blockCipher.doFinal(buf, bytesWritten);\n\t\t}\n\t\tcatch (InvalidCipherTextException ex) {\n\t\t\tthrow new IllegalStateException(\"unable to encrypt/decrypt\", ex);\n\t\t}\n\t\tif (bytesWritten == buf.length) {\n\t\t\treturn buf;\n\t\t}\n\t\tbyte[] out = new byte[bytesWritten];\n\t\tSystem.arraycopy(buf, 0, out, 0, bytesWritten);\n\t\treturn out;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport org.bouncycastle.crypto.InvalidCipherTextException;\nimport org.bouncycastle.crypto.engines.AESEngine;\nimport org.bouncycastle.crypto.modes.AEADBlockCipher;\nimport org.bouncycastle.crypto.modes.GCMBlockCipher;\nimport org.bouncycastle.crypto.params.AEADParameters;\n\nimport org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgorithm;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.util.EncodingUtils;\n\n/**\n * An Encryptor equivalent to {@link AesBytesEncryptor} using {@link CipherAlgorithm#GCM}\n * that uses Bouncy Castle instead of JCE. The algorithm is equivalent to\n * \"AES/GCM/NoPadding\".\n *\n * @author William Tran\n *\n */\npublic class BouncyCastleAesGcmBytesEncryptor extends BouncyCastleAesBytesEncryptor {\n\n\tpublic BouncyCastleAesGcmBytesEncryptor(String password, CharSequence salt) {\n\t\tsuper(password, salt);\n\t}\n\n\tpublic BouncyCastleAesGcmBytesEncryptor(String password, CharSequence salt, BytesKeyGenerator ivGenerator) {\n\t\tsuper(password, salt, ivGenerator);\n\t}\n\n\t@Override\n\tpublic byte[] encrypt(byte[] bytes) {\n\t\tbyte[] iv = this.ivGenerator.generateKey();\n\t\tAEADBlockCipher blockCipher = GCMBlockCipher.newInstance(AESEngine.newInstance());\n\t\tblockCipher.init(true, new AEADParameters(this.secretKey, 128, iv, null));\n\t\tbyte[] encrypted = process(blockCipher, bytes);\n\t\treturn (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted;\n\t}\n\n\t@Override\n\tpublic byte[] decrypt(byte[] encryptedBytes) {\n\t\tbyte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength());\n\t\tencryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length);\n\t\tAEADBlockCipher blockCipher = GCMBlockCipher.newInstance(AESEngine.newInstance());\n\t\tblockCipher.init(false, new AEADParameters(this.secretKey, 128, iv, null));\n\t\treturn process(blockCipher, encryptedBytes);\n\t}\n\n\tprivate byte[] process(AEADBlockCipher blockCipher, byte[] in) {\n\t\tbyte[] buf = new byte[blockCipher.getOutputSize(in.length)];\n\t\tint bytesWritten = blockCipher.processBytes(in, 0, in.length, buf, 0);\n\t\ttry {\n\t\t\tbytesWritten += blockCipher.doFinal(buf, bytesWritten);\n\t\t}\n\t\tcatch (InvalidCipherTextException ex) {\n\t\t\tthrow new IllegalStateException(\"unable to encrypt/decrypt\", ex);\n\t\t}\n\t\tif (bytesWritten == buf.length) {\n\t\t\treturn buf;\n\t\t}\n\t\tbyte[] out = new byte[bytesWritten];\n\t\tSystem.arraycopy(buf, 0, out, 0, bytesWritten);\n\t\treturn out;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/BytesEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\n/**\n * Service interface for symmetric data encryption.\n *\n * @author Keith Donald\n */\npublic interface BytesEncryptor {\n\n\t/**\n\t * Encrypt the byte array.\n\t */\n\tbyte[] encrypt(byte[] byteArray);\n\n\t/**\n\t * Decrypt the byte array.\n\t */\n\tbyte[] decrypt(byte[] encryptedByteArray);\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/CipherUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.spec.AlgorithmParameterSpec;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.InvalidParameterSpecException;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.Cipher;\nimport javax.crypto.IllegalBlockSizeException;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.SecretKey;\nimport javax.crypto.SecretKeyFactory;\nimport javax.crypto.spec.PBEKeySpec;\nimport javax.crypto.spec.PBEParameterSpec;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Static helper for working with the Cipher API.\n *\n * @author Keith Donald\n */\nfinal class CipherUtils {\n\n\tprivate CipherUtils() {\n\t}\n\n\t/**\n\t * Generates a SecretKey.\n\t */\n\tstatic SecretKey newSecretKey(String algorithm, String password) {\n\t\treturn newSecretKey(algorithm, new PBEKeySpec(password.toCharArray()));\n\t}\n\n\t/**\n\t * Generates a SecretKey.\n\t */\n\tstatic SecretKey newSecretKey(String algorithm, PBEKeySpec keySpec) {\n\t\ttry {\n\t\t\tSecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);\n\t\t\treturn factory.generateSecret(keySpec);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Not a valid encryption algorithm\", ex);\n\t\t}\n\t\tcatch (InvalidKeySpecException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Not a valid secret key\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Constructs a new Cipher.\n\t */\n\tstatic Cipher newCipher(String algorithm) {\n\t\ttry {\n\t\t\treturn Cipher.getInstance(algorithm);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Not a valid encryption algorithm\", ex);\n\t\t}\n\t\tcatch (NoSuchPaddingException ex) {\n\t\t\tthrow new IllegalStateException(\"Should not happen\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Initializes the Cipher for use.\n\t */\n\tstatic <T extends AlgorithmParameterSpec> T getParameterSpec(Cipher cipher, Class<T> parameterSpecClass) {\n\t\ttry {\n\t\t\treturn cipher.getParameters().getParameterSpec(parameterSpecClass);\n\t\t}\n\t\tcatch (InvalidParameterSpecException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Unable to access parameter\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Initializes the Cipher for use.\n\t */\n\tstatic void initCipher(Cipher cipher, int mode, SecretKey secretKey) {\n\t\tinitCipher(cipher, mode, secretKey, null);\n\t}\n\n\t/**\n\t * Initializes the Cipher for use.\n\t */\n\tstatic void initCipher(Cipher cipher, int mode, SecretKey secretKey, byte[] salt, int iterationCount) {\n\t\tinitCipher(cipher, mode, secretKey, new PBEParameterSpec(salt, iterationCount));\n\t}\n\n\t/**\n\t * Initializes the Cipher for use.\n\t */\n\tstatic void initCipher(Cipher cipher, int mode, SecretKey secretKey,\n\t\t\t@Nullable AlgorithmParameterSpec parameterSpec) {\n\t\ttry {\n\t\t\tif (parameterSpec != null) {\n\t\t\t\tcipher.init(mode, secretKey, parameterSpec);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcipher.init(mode, secretKey);\n\t\t\t}\n\t\t}\n\t\tcatch (InvalidKeyException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Unable to initialize due to invalid secret key\", ex);\n\t\t}\n\t\tcatch (InvalidAlgorithmParameterException ex) {\n\t\t\tthrow new IllegalStateException(\"Unable to initialize due to invalid decryption parameter spec\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Invokes the Cipher to perform encryption or decryption (depending on the\n\t * initialized mode).\n\t */\n\tstatic byte[] doFinal(Cipher cipher, byte[] input) {\n\t\ttry {\n\t\t\treturn cipher.doFinal(input);\n\t\t}\n\t\tcatch (IllegalBlockSizeException ex) {\n\t\t\tthrow new IllegalStateException(\"Unable to invoke Cipher due to illegal block size\", ex);\n\t\t}\n\t\tcatch (BadPaddingException ex) {\n\t\t\tthrow new IllegalStateException(\"Unable to invoke Cipher due to bad padding\", ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgorithm;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\n\n/**\n * Factory for commonly used encryptors. Defines the public API for constructing\n * {@link BytesEncryptor} and {@link TextEncryptor} implementations.\n *\n * @author Keith Donald\n */\npublic final class Encryptors {\n\n\tprivate Encryptors() {\n\t}\n\n\t/**\n\t * Creates a standard password-based bytes encryptor using 256 bit AES encryption with\n\t * Galois Counter Mode (GCM). Derives the secret key using PKCS #5's PBKDF2\n\t * (Password-Based Key Derivation Function #2). Salts the password to prevent\n\t * dictionary attacks against the key. The provided salt is expected to be\n\t * hex-encoded; it should be random and at least 8 bytes in length. Also applies a\n\t * random 16-byte initialization vector to ensure each encrypted message will be\n\t * unique. Requires Java 6.\n\t * @param password the password used to generate the encryptor's secret key; should\n\t * not be shared\n\t * @param salt a hex-encoded, random, site-global salt value to use to generate the\n\t * key\n\t */\n\tpublic static BytesEncryptor stronger(CharSequence password, CharSequence salt) {\n\t\treturn new AesBytesEncryptor(password.toString(), salt, KeyGenerators.secureRandom(16), CipherAlgorithm.GCM);\n\t}\n\n\t/**\n\t * Creates a standard password-based bytes encryptor using 256 bit AES encryption.\n\t * Derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation\n\t * Function #2). Salts the password to prevent dictionary attacks against the key. The\n\t * provided salt is expected to be hex-encoded; it should be random and at least 8\n\t * bytes in length. Also applies a random 16-byte initialization vector to ensure each\n\t * encrypted message will be unique. Requires Java 6. NOTE: This mode is not\n\t * <a href=\"https://en.wikipedia.org/wiki/Authenticated_encryption\">authenticated</a>\n\t * and does not provide any guarantees about the authenticity of the data. For a more\n\t * secure alternative, users should prefer\n\t * {@link #stronger(CharSequence, CharSequence)}.\n\t * @param password the password used to generate the encryptor's secret key; should\n\t * not be shared\n\t * @param salt a hex-encoded, random, site-global salt value to use to generate the\n\t * key\n\t *\n\t * @see Encryptors#stronger(CharSequence, CharSequence)\n\t */\n\tpublic static BytesEncryptor standard(CharSequence password, CharSequence salt) {\n\t\treturn new AesBytesEncryptor(password.toString(), salt, KeyGenerators.secureRandom(16));\n\t}\n\n\t/**\n\t * Creates a text encryptor that uses \"stronger\" password-based encryption. Encrypted\n\t * text is hex-encoded.\n\t * @param password the password used to generate the encryptor's secret key; should\n\t * not be shared\n\t * @see Encryptors#stronger(CharSequence, CharSequence)\n\t */\n\tpublic static TextEncryptor delux(CharSequence password, CharSequence salt) {\n\t\treturn new HexEncodingTextEncryptor(stronger(password, salt));\n\t}\n\n\t/**\n\t * Creates a text encryptor that uses \"standard\" password-based encryption. Encrypted\n\t * text is hex-encoded.\n\t * @param password the password used to generate the encryptor's secret key; should\n\t * not be shared\n\t * @see Encryptors#standard(CharSequence, CharSequence)\n\t */\n\tpublic static TextEncryptor text(CharSequence password, CharSequence salt) {\n\t\treturn new HexEncodingTextEncryptor(standard(password, salt));\n\t}\n\n\t/**\n\t * Creates a text encryptor that performs no encryption. Useful for developer testing\n\t * environments where working with plain text strings is desired for simplicity.\n\t */\n\tpublic static TextEncryptor noOpText() {\n\t\treturn NoOpTextEncryptor.INSTANCE;\n\t}\n\n\tprivate static final class NoOpTextEncryptor implements TextEncryptor {\n\n\t\tstatic final TextEncryptor INSTANCE = new NoOpTextEncryptor();\n\n\t\t@Override\n\t\tpublic String encrypt(String text) {\n\t\t\treturn text;\n\t\t}\n\n\t\t@Override\n\t\tpublic String decrypt(String encryptedText) {\n\t\t\treturn encryptedText;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/HexEncodingTextEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.codec.Utf8;\n\n/**\n * Delegates to an {@link BytesEncryptor} to encrypt text strings. Raw text strings are\n * UTF-8 encoded before being passed to the encryptor. Encrypted strings are returned\n * hex-encoded.\n *\n * @author Keith Donald\n */\nfinal class HexEncodingTextEncryptor implements TextEncryptor {\n\n\tprivate final BytesEncryptor encryptor;\n\n\tHexEncodingTextEncryptor(BytesEncryptor encryptor) {\n\t\tthis.encryptor = encryptor;\n\t}\n\n\t@Override\n\tpublic String encrypt(String text) {\n\t\treturn new String(Hex.encode(this.encryptor.encrypt(Utf8.encode(text))));\n\t}\n\n\t@Override\n\tpublic String decrypt(String encryptedText) {\n\t\treturn Utf8.decode(this.encryptor.decrypt(Hex.decode(encryptedText)));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/KeyStoreKeyFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.io.InputStream;\nimport java.security.KeyFactory;\nimport java.security.KeyPair;\nimport java.security.KeyStore;\nimport java.security.PublicKey;\nimport java.security.cert.Certificate;\nimport java.security.interfaces.RSAPrivateCrtKey;\nimport java.security.spec.RSAPublicKeySpec;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.io.Resource;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Dave Syer\n * @author Tim Ysewyn\n * @since 6.3\n */\npublic class KeyStoreKeyFactory {\n\n\tprivate final Resource resource;\n\n\tprivate final char[] password;\n\n\tprivate @Nullable KeyStore store;\n\n\tprivate final Object lock = new Object();\n\n\tprivate final String type;\n\n\tpublic KeyStoreKeyFactory(Resource resource, char[] password) {\n\t\tthis(resource, password, type(resource));\n\t}\n\n\tprivate static String type(Resource resource) {\n\t\tString ext = StringUtils.getFilenameExtension(resource.getFilename());\n\t\treturn (ext != null) ? ext : \"jks\";\n\t}\n\n\tpublic KeyStoreKeyFactory(Resource resource, char[] password, String type) {\n\t\tthis.resource = resource;\n\t\tthis.password = password;\n\t\tthis.type = type;\n\t}\n\n\tpublic KeyPair getKeyPair(String alias) {\n\t\treturn getKeyPair(alias, this.password);\n\t}\n\n\tpublic KeyPair getKeyPair(String alias, char[] password) {\n\t\ttry {\n\t\t\tsynchronized (this.lock) {\n\t\t\t\tif (this.store == null) {\n\t\t\t\t\tsynchronized (this.lock) {\n\t\t\t\t\t\tthis.store = KeyStore.getInstance(this.type);\n\t\t\t\t\t\ttry (InputStream stream = this.resource.getInputStream()) {\n\t\t\t\t\t\t\tthis.store.load(stream, this.password);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tRSAPrivateCrtKey key = (RSAPrivateCrtKey) this.store.getKey(alias, password);\n\t\t\tCertificate certificate = this.store.getCertificate(alias);\n\t\t\tPublicKey publicKey = null;\n\t\t\tif (certificate != null) {\n\t\t\t\tpublicKey = certificate.getPublicKey();\n\t\t\t}\n\t\t\telse if (key != null) {\n\t\t\t\tRSAPublicKeySpec spec = new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent());\n\t\t\t\tpublicKey = KeyFactory.getInstance(\"RSA\").generatePublic(spec);\n\t\t\t}\n\t\t\treturn new KeyPair(publicKey, key);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(\"Cannot load keys from store: \" + this.resource, ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaAlgorithm.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\n/**\n * @author Dave Syer\n * @since 6.3\n */\npublic enum RsaAlgorithm {\n\n\tDEFAULT(\"RSA\", 117), OAEP(\"RSA/ECB/OAEPPadding\", 86);\n\n\tprivate final String name;\n\n\tprivate final int maxLength;\n\n\tRsaAlgorithm(String name, int maxLength) {\n\t\tthis.name = name;\n\t\tthis.maxLength = maxLength;\n\t}\n\n\tpublic String getJceName() {\n\t\treturn this.name;\n\t}\n\n\tpublic int getMaxLength() {\n\t\treturn this.maxLength;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaKeyHelper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.math.BigInteger;\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.CharacterCodingException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.security.KeyFactory;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.KeySpec;\nimport java.security.spec.RSAPrivateCrtKeySpec;\nimport java.security.spec.RSAPublicKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.bouncycastle.asn1.ASN1Sequence;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Reads RSA key pairs using BC provider classes but without the need to specify a crypto\n * provider or have BC added as one.\n *\n * @author Luke Taylor\n * @author Dave Syer\n */\nfinal class RsaKeyHelper {\n\n\tprivate static final Charset UTF8 = StandardCharsets.UTF_8;\n\n\tprivate static final String BEGIN = \"-----BEGIN\";\n\n\tprivate static final Pattern PEM_DATA = Pattern.compile(\".*-----BEGIN (.*)-----(.*)-----END (.*)-----\",\n\t\t\tPattern.DOTALL);\n\n\tprivate static final byte[] PREFIX = new byte[] { 0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a' };\n\n\tprivate RsaKeyHelper() {\n\t}\n\n\tstatic KeyPair parseKeyPair(String pemData) {\n\t\tMatcher m = PEM_DATA.matcher(pemData.replaceAll(\"\\n *\", \"\").trim());\n\n\t\tif (!m.matches()) {\n\t\t\ttry {\n\t\t\t\tRSAPublicKey publicValue = extractPublicKey(pemData);\n\t\t\t\tif (publicValue != null) {\n\t\t\t\t\treturn new KeyPair(publicValue, null);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\t// Ignore\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"String is not PEM encoded data, nor a public key encoded for ssh\");\n\t\t}\n\n\t\tString type = m.group(1);\n\t\tfinal byte[] content = base64Decode(m.group(2));\n\n\t\tPublicKey publicKey;\n\t\tPrivateKey privateKey = null;\n\n\t\ttry {\n\t\t\tKeyFactory fact = KeyFactory.getInstance(\"RSA\");\n\t\t\tswitch (type) {\n\t\t\t\tcase \"RSA PRIVATE KEY\" -> {\n\t\t\t\t\tASN1Sequence seq = ASN1Sequence.getInstance(content);\n\t\t\t\t\tif (seq.size() != 9) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"Invalid RSA Private Key ASN1 sequence.\");\n\t\t\t\t\t}\n\t\t\t\t\torg.bouncycastle.asn1.pkcs.RSAPrivateKey key = org.bouncycastle.asn1.pkcs.RSAPrivateKey\n\t\t\t\t\t\t.getInstance(seq);\n\t\t\t\t\tRSAPublicKeySpec pubSpec = new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent());\n\t\t\t\t\tRSAPrivateCrtKeySpec privSpec = new RSAPrivateCrtKeySpec(key.getModulus(), key.getPublicExponent(),\n\t\t\t\t\t\t\tkey.getPrivateExponent(), key.getPrime1(), key.getPrime2(), key.getExponent1(),\n\t\t\t\t\t\t\tkey.getExponent2(), key.getCoefficient());\n\t\t\t\t\tpublicKey = fact.generatePublic(pubSpec);\n\t\t\t\t\tprivateKey = fact.generatePrivate(privSpec);\n\t\t\t\t}\n\t\t\t\tcase \"PUBLIC KEY\" -> {\n\t\t\t\t\tKeySpec keySpec = new X509EncodedKeySpec(content);\n\t\t\t\t\tpublicKey = fact.generatePublic(keySpec);\n\t\t\t\t}\n\t\t\t\tcase \"RSA PUBLIC KEY\" -> {\n\t\t\t\t\tASN1Sequence seq = ASN1Sequence.getInstance(content);\n\t\t\t\t\torg.bouncycastle.asn1.pkcs.RSAPublicKey key = org.bouncycastle.asn1.pkcs.RSAPublicKey\n\t\t\t\t\t\t.getInstance(seq);\n\t\t\t\t\tRSAPublicKeySpec pubSpec = new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent());\n\t\t\t\t\tpublicKey = fact.generatePublic(pubSpec);\n\t\t\t\t}\n\t\t\t\tdefault -> throw new IllegalArgumentException(type + \" is not a supported format\");\n\t\t\t}\n\n\t\t\treturn new KeyPair(publicKey, privateKey);\n\t\t}\n\t\tcatch (InvalidKeySpecException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\tprivate static byte[] base64Decode(String string) {\n\t\ttry {\n\t\t\tByteBuffer bytes = UTF8.newEncoder().encode(CharBuffer.wrap(string));\n\t\t\tbyte[] bytesCopy = new byte[bytes.limit()];\n\t\t\tSystem.arraycopy(bytes.array(), 0, bytesCopy, 0, bytes.limit());\n\t\t\treturn Base64.getDecoder().decode(bytesCopy);\n\t\t}\n\t\tcatch (CharacterCodingException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tstatic String base64Encode(byte[] bytes) {\n\t\ttry {\n\t\t\treturn UTF8.newDecoder().decode(ByteBuffer.wrap(Base64.getEncoder().encode(bytes))).toString();\n\t\t}\n\t\tcatch (CharacterCodingException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tstatic KeyPair generateKeyPair() {\n\t\ttry {\n\t\t\tfinal KeyPairGenerator keyGen = KeyPairGenerator.getInstance(\"RSA\");\n\t\t\tkeyGen.initialize(1024);\n\t\t\treturn keyGen.generateKeyPair();\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\n\t}\n\n\tprivate static final Pattern SSH_PUB_KEY = Pattern.compile(\"ssh-(rsa|dsa) ([A-Za-z0-9/+]+=*) (.*)\");\n\n\tprivate static @Nullable RSAPublicKey extractPublicKey(String key) {\n\n\t\tMatcher m = SSH_PUB_KEY.matcher(key);\n\n\t\tif (m.matches()) {\n\t\t\tString alg = m.group(1);\n\t\t\tString encKey = m.group(2);\n\t\t\t// String id = m.group(3);\n\n\t\t\tif (!\"rsa\".equalsIgnoreCase(alg)) {\n\t\t\t\tthrow new IllegalArgumentException(\"Only RSA is currently supported, but algorithm was \" + alg);\n\t\t\t}\n\n\t\t\treturn parseSSHPublicKey(encKey);\n\t\t}\n\t\telse if (!key.startsWith(BEGIN)) {\n\t\t\t// Assume it's the plain Base64 encoded ssh key without the\n\t\t\t// \"ssh-rsa\" at the start\n\t\t\treturn parseSSHPublicKey(key);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tstatic RSAPublicKey parsePublicKey(String key) {\n\n\t\tRSAPublicKey publicKey = extractPublicKey(key);\n\n\t\tif (publicKey != null) {\n\t\t\treturn publicKey;\n\t\t}\n\n\t\tKeyPair kp = parseKeyPair(key);\n\n\t\tif (kp.getPublic() == null) {\n\t\t\tthrow new IllegalArgumentException(\"Key data does not contain a public key\");\n\t\t}\n\n\t\treturn (RSAPublicKey) kp.getPublic();\n\n\t}\n\n\tstatic String encodePublicKey(RSAPublicKey key, String id) {\n\t\tStringWriter output = new StringWriter();\n\t\toutput.append(\"ssh-rsa \");\n\t\tByteArrayOutputStream stream = new ByteArrayOutputStream();\n\t\ttry {\n\t\t\tstream.write(PREFIX);\n\t\t\twriteBigInteger(stream, key.getPublicExponent());\n\t\t\twriteBigInteger(stream, key.getModulus());\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new IllegalStateException(\"Cannot encode key\", ex);\n\t\t}\n\t\toutput.append(base64Encode(stream.toByteArray()));\n\t\toutput.append(\" \" + id);\n\t\treturn output.toString();\n\t}\n\n\tprivate static RSAPublicKey parseSSHPublicKey(String encKey) {\n\t\tByteArrayInputStream in = new ByteArrayInputStream(base64Decode(encKey));\n\n\t\tbyte[] prefix = new byte[11];\n\n\t\ttry {\n\t\t\tif (in.read(prefix) != 11 || !Arrays.equals(PREFIX, prefix)) {\n\t\t\t\tthrow new IllegalArgumentException(\"SSH key prefix not found\");\n\t\t\t}\n\n\t\t\tBigInteger e = new BigInteger(readBigInteger(in));\n\t\t\tBigInteger n = new BigInteger(readBigInteger(in));\n\n\t\t\treturn createPublicKey(n, e);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tstatic RSAPublicKey createPublicKey(BigInteger n, BigInteger e) {\n\t\ttry {\n\t\t\treturn (RSAPublicKey) KeyFactory.getInstance(\"RSA\").generatePublic(new RSAPublicKeySpec(n, e));\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprivate static void writeBigInteger(ByteArrayOutputStream stream, BigInteger num) throws IOException {\n\t\tint length = num.toByteArray().length;\n\t\tbyte[] data = new byte[4];\n\t\tdata[0] = (byte) ((length >> 24) & 0xFF);\n\t\tdata[1] = (byte) ((length >> 16) & 0xFF);\n\t\tdata[2] = (byte) ((length >> 8) & 0xFF);\n\t\tdata[3] = (byte) (length & 0xFF);\n\t\tstream.write(data);\n\t\tstream.write(num.toByteArray());\n\t}\n\n\tprivate static byte[] readBigInteger(ByteArrayInputStream in) throws IOException {\n\t\tbyte[] b = new byte[4];\n\n\t\tif (in.read(b) != 4) {\n\t\t\tthrow new IOException(\"Expected length data as 4 bytes\");\n\t\t}\n\n\t\tint l = ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | (b[3] & 0xFF);\n\n\t\tb = new byte[l];\n\n\t\tif (in.read(b) != l) {\n\t\t\tthrow new IOException(\"Expected \" + l + \" key bytes\");\n\t\t}\n\n\t\treturn b;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaKeyHolder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\n/**\n * @author Dave Syer\n * @since 6.3\n */\npublic interface RsaKeyHolder {\n\n\tString getPublicKey();\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaRawEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.io.ByteArrayOutputStream;\nimport java.nio.charset.Charset;\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.interfaces.RSAKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.Base64;\n\nimport javax.crypto.Cipher;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @author Dave Syer\n * @since 6.3\n */\npublic class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHolder {\n\n\tprivate static final String DEFAULT_ENCODING = \"UTF-8\";\n\n\tprivate RsaAlgorithm algorithm = RsaAlgorithm.DEFAULT;\n\n\tprivate Charset charset;\n\n\tprivate RSAPublicKey publicKey;\n\n\tprivate @Nullable RSAPrivateKey privateKey;\n\n\tprivate Charset defaultCharset;\n\n\tpublic RsaRawEncryptor(RsaAlgorithm algorithm) {\n\t\tthis(RsaKeyHelper.generateKeyPair(), algorithm);\n\t}\n\n\tpublic RsaRawEncryptor() {\n\t\tthis(RsaKeyHelper.generateKeyPair());\n\t}\n\n\tpublic RsaRawEncryptor(KeyPair keyPair, RsaAlgorithm algorithm) {\n\t\tthis(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm);\n\t}\n\n\tpublic RsaRawEncryptor(KeyPair keyPair) {\n\t\tthis(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate());\n\t}\n\n\tpublic RsaRawEncryptor(String pemData) {\n\t\tthis(RsaKeyHelper.parseKeyPair(pemData));\n\t}\n\n\tpublic RsaRawEncryptor(PublicKey publicKey) {\n\t\tthis(DEFAULT_ENCODING, publicKey, null);\n\t}\n\n\tpublic RsaRawEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey) {\n\t\tthis(encoding, publicKey, privateKey, RsaAlgorithm.DEFAULT);\n\t}\n\n\tpublic RsaRawEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey,\n\t\t\tRsaAlgorithm algorithm) {\n\t\tthis.charset = Charset.forName(encoding);\n\t\tthis.publicKey = (RSAPublicKey) publicKey;\n\t\tthis.privateKey = (RSAPrivateKey) privateKey;\n\t\tthis.defaultCharset = Charset.forName(DEFAULT_ENCODING);\n\t\tthis.algorithm = algorithm;\n\t}\n\n\t@Override\n\tpublic String getPublicKey() {\n\t\treturn RsaKeyHelper.encodePublicKey(this.publicKey, \"application\");\n\t}\n\n\t@Override\n\tpublic String encrypt(String text) {\n\t\treturn new String(Base64.getEncoder().encode(encrypt(text.getBytes(this.charset))), this.defaultCharset);\n\t}\n\n\t@Override\n\tpublic String decrypt(String encryptedText) {\n\t\tif (this.privateKey == null) {\n\t\t\tthrow new IllegalStateException(\"Private key must be provided for decryption\");\n\t\t}\n\t\treturn new String(decrypt(Base64.getDecoder().decode(encryptedText.getBytes(this.defaultCharset))),\n\t\t\t\tthis.charset);\n\t}\n\n\t@Override\n\tpublic byte[] encrypt(byte[] byteArray) {\n\t\treturn encrypt(byteArray, this.publicKey, this.algorithm);\n\t}\n\n\t@Override\n\tpublic byte[] decrypt(byte[] encryptedByteArray) {\n\t\treturn decrypt(encryptedByteArray, this.privateKey, this.algorithm);\n\t}\n\n\tprivate static byte[] encrypt(byte[] text, PublicKey key, RsaAlgorithm alg) {\n\t\tByteArrayOutputStream output = new ByteArrayOutputStream(text.length);\n\t\ttry {\n\t\t\tfinal Cipher cipher = Cipher.getInstance(alg.getJceName());\n\t\t\tint limit = Math.min(text.length, alg.getMaxLength());\n\t\t\tint pos = 0;\n\t\t\twhile (pos < text.length) {\n\t\t\t\tcipher.init(Cipher.ENCRYPT_MODE, key);\n\t\t\t\tcipher.update(text, pos, limit);\n\t\t\t\tpos += limit;\n\t\t\t\tlimit = Math.min(text.length - pos, alg.getMaxLength());\n\t\t\t\tbyte[] buffer = cipher.doFinal();\n\t\t\t\toutput.write(buffer, 0, buffer.length);\n\t\t\t}\n\t\t\treturn output.toByteArray();\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(\"Cannot encrypt\", ex);\n\t\t}\n\t}\n\n\tprivate static byte[] decrypt(byte[] text, @Nullable RSAPrivateKey key, RsaAlgorithm alg) {\n\t\tByteArrayOutputStream output = new ByteArrayOutputStream(text.length);\n\t\ttry {\n\t\t\tfinal Cipher cipher = Cipher.getInstance(alg.getJceName());\n\t\t\tint maxLength = getByteLength(key);\n\t\t\tint pos = 0;\n\t\t\twhile (pos < text.length) {\n\t\t\t\tint limit = Math.min(text.length - pos, maxLength);\n\t\t\t\tcipher.init(Cipher.DECRYPT_MODE, key);\n\t\t\t\tcipher.update(text, pos, limit);\n\t\t\t\tpos += limit;\n\t\t\t\tbyte[] buffer = cipher.doFinal();\n\t\t\t\toutput.write(buffer, 0, buffer.length);\n\t\t\t}\n\t\t\treturn output.toByteArray();\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(\"Cannot decrypt\", ex);\n\t\t}\n\t}\n\n\t// copied from sun.security.rsa.RSACore.getByteLength(java.math.BigInteger)\n\tpublic static int getByteLength(@Nullable RSAKey key) {\n\t\tif (key == null) {\n\t\t\tthrow new IllegalArgumentException(\"key cannot be null\");\n\t\t}\n\t\tint n = key.getModulus().bitLength();\n\t\treturn (n + 7) >> 3;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaSecretEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.Base64;\n\nimport javax.crypto.Cipher;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\n\n/**\n * @author Dave Syer\n * @since 6.3\n */\npublic class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHolder {\n\n\tprivate static final String DEFAULT_ENCODING = \"UTF-8\";\n\n\t// The secret for encryption is random (so dictionary attack is not a danger)\n\tprivate static final String DEFAULT_SALT = \"deadbeef\";\n\n\tprivate final String salt;\n\n\tprivate RsaAlgorithm algorithm = RsaAlgorithm.DEFAULT;\n\n\tprivate final Charset charset;\n\n\tprivate final PublicKey publicKey;\n\n\tprivate final @Nullable PrivateKey privateKey;\n\n\tprivate final Charset defaultCharset;\n\n\tprivate final boolean gcm;\n\n\tpublic RsaSecretEncryptor(RsaAlgorithm algorithm, String salt, boolean gcm) {\n\t\tthis(RsaKeyHelper.generateKeyPair(), algorithm, salt, gcm);\n\t}\n\n\tpublic RsaSecretEncryptor(RsaAlgorithm algorithm, String salt) {\n\t\tthis(RsaKeyHelper.generateKeyPair(), algorithm, salt);\n\t}\n\n\tpublic RsaSecretEncryptor(RsaAlgorithm algorithm, boolean gcm) {\n\t\tthis(RsaKeyHelper.generateKeyPair(), algorithm, DEFAULT_SALT, gcm);\n\t}\n\n\tpublic RsaSecretEncryptor(RsaAlgorithm algorithm) {\n\t\tthis(RsaKeyHelper.generateKeyPair(), algorithm);\n\t}\n\n\tpublic RsaSecretEncryptor() {\n\t\tthis(RsaKeyHelper.generateKeyPair());\n\t}\n\n\tpublic RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm, String salt, boolean gcm) {\n\t\tthis(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm, salt, gcm);\n\t}\n\n\tpublic RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm, String salt) {\n\t\tthis(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm, salt, false);\n\t}\n\n\tpublic RsaSecretEncryptor(KeyPair keyPair, RsaAlgorithm algorithm) {\n\t\tthis(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate(), algorithm);\n\t}\n\n\tpublic RsaSecretEncryptor(KeyPair keyPair) {\n\t\tthis(DEFAULT_ENCODING, keyPair.getPublic(), keyPair.getPrivate());\n\t}\n\n\tpublic RsaSecretEncryptor(String pemData, RsaAlgorithm algorithm, String salt) {\n\t\tthis(RsaKeyHelper.parseKeyPair(pemData), algorithm, salt);\n\t}\n\n\tpublic RsaSecretEncryptor(String pemData, RsaAlgorithm algorithm) {\n\t\tthis(RsaKeyHelper.parseKeyPair(pemData), algorithm);\n\t}\n\n\tpublic RsaSecretEncryptor(String pemData) {\n\t\tthis(RsaKeyHelper.parseKeyPair(pemData));\n\t}\n\n\tpublic RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm, String salt, boolean gcm) {\n\t\tthis(DEFAULT_ENCODING, publicKey, null, algorithm, salt, gcm);\n\t}\n\n\tpublic RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm, String salt) {\n\t\tthis(DEFAULT_ENCODING, publicKey, null, algorithm, salt, false);\n\t}\n\n\tpublic RsaSecretEncryptor(PublicKey publicKey, RsaAlgorithm algorithm) {\n\t\tthis(DEFAULT_ENCODING, publicKey, null, algorithm);\n\t}\n\n\tpublic RsaSecretEncryptor(PublicKey publicKey) {\n\t\tthis(DEFAULT_ENCODING, publicKey, null);\n\t}\n\n\tpublic RsaSecretEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey) {\n\t\tthis(encoding, publicKey, privateKey, RsaAlgorithm.DEFAULT);\n\t}\n\n\tpublic RsaSecretEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey,\n\t\t\tRsaAlgorithm algorithm) {\n\t\tthis(encoding, publicKey, privateKey, algorithm, DEFAULT_SALT, false);\n\t}\n\n\tpublic RsaSecretEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey,\n\t\t\tRsaAlgorithm algorithm, String salt, boolean gcm) {\n\t\tthis.charset = Charset.forName(encoding);\n\t\tthis.publicKey = publicKey;\n\t\tthis.privateKey = privateKey;\n\t\tthis.defaultCharset = Charset.forName(DEFAULT_ENCODING);\n\t\tthis.algorithm = algorithm;\n\t\tthis.salt = isHex(salt) ? salt : new String(Hex.encode(salt.getBytes(this.defaultCharset)));\n\t\tthis.gcm = gcm;\n\t}\n\n\t@Override\n\tpublic String getPublicKey() {\n\t\treturn RsaKeyHelper.encodePublicKey((RSAPublicKey) this.publicKey, \"application\");\n\t}\n\n\t@Override\n\tpublic String encrypt(String text) {\n\t\treturn new String(Base64.getEncoder().encode(encrypt(text.getBytes(this.charset))), this.defaultCharset);\n\t}\n\n\t@Override\n\tpublic String decrypt(String encryptedText) {\n\t\tif (!canDecrypt()) {\n\t\t\tthrow new IllegalStateException(\"Encryptor is not configured for decryption\");\n\t\t}\n\t\treturn new String(decrypt(Base64.getDecoder().decode(encryptedText.getBytes(this.defaultCharset))),\n\t\t\t\tthis.charset);\n\t}\n\n\t@Override\n\tpublic byte[] encrypt(byte[] byteArray) {\n\t\treturn encrypt(byteArray, this.publicKey, this.algorithm, this.salt, this.gcm);\n\t}\n\n\t@Override\n\tpublic byte[] decrypt(byte[] encryptedByteArray) {\n\t\tif (!canDecrypt()) {\n\t\t\tthrow new IllegalStateException(\"Encryptor is not configured for decryption\");\n\t\t}\n\t\treturn decrypt(encryptedByteArray, this.privateKey, this.algorithm, this.salt, this.gcm);\n\t}\n\n\tprivate static byte[] encrypt(byte[] text, PublicKey key, RsaAlgorithm alg, String salt, boolean gcm) {\n\t\tbyte[] random = KeyGenerators.secureRandom(16).generateKey();\n\t\tBytesEncryptor aes = gcm ? Encryptors.stronger(new String(Hex.encode(random)), salt)\n\t\t\t\t: Encryptors.standard(new String(Hex.encode(random)), salt);\n\t\ttry {\n\t\t\tfinal Cipher cipher = Cipher.getInstance(alg.getJceName());\n\t\t\tcipher.init(Cipher.ENCRYPT_MODE, key);\n\t\t\tbyte[] secret = cipher.doFinal(random);\n\t\t\tByteArrayOutputStream result = new ByteArrayOutputStream(text.length + 20);\n\t\t\twriteInt(result, secret.length);\n\t\t\tresult.write(secret);\n\t\t\tresult.write(aes.encrypt(text));\n\t\t\treturn result.toByteArray();\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(\"Cannot encrypt\", ex);\n\t\t}\n\t}\n\n\tprivate static void writeInt(ByteArrayOutputStream result, int length) throws IOException {\n\t\tbyte[] data = new byte[2];\n\t\tdata[0] = (byte) ((length >> 8) & 0xFF);\n\t\tdata[1] = (byte) (length & 0xFF);\n\t\tresult.write(data);\n\t}\n\n\tprivate static int readInt(ByteArrayInputStream result) throws IOException {\n\t\tbyte[] b = new byte[2];\n\t\tresult.read(b);\n\t\treturn ((b[0] & 0xFF) << 8) | (b[1] & 0xFF);\n\t}\n\n\tprivate static byte[] decrypt(byte[] text, @Nullable PrivateKey key, RsaAlgorithm alg, String salt, boolean gcm) {\n\t\tByteArrayInputStream input = new ByteArrayInputStream(text);\n\t\tByteArrayOutputStream output = new ByteArrayOutputStream(text.length);\n\t\ttry {\n\t\t\tint length = readInt(input);\n\t\t\tbyte[] random = new byte[length];\n\t\t\tinput.read(random);\n\t\t\tfinal Cipher cipher = Cipher.getInstance(alg.getJceName());\n\t\t\tcipher.init(Cipher.DECRYPT_MODE, key);\n\t\t\tString secret = new String(Hex.encode(cipher.doFinal(random)));\n\t\t\tbyte[] buffer = new byte[text.length - random.length - 2];\n\t\t\tinput.read(buffer);\n\t\t\tBytesEncryptor aes = gcm ? Encryptors.stronger(secret, salt) : Encryptors.standard(secret, salt);\n\t\t\toutput.write(aes.decrypt(buffer));\n\t\t\treturn output.toByteArray();\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(\"Cannot decrypt\", ex);\n\t\t}\n\t}\n\n\tprivate static boolean isHex(String input) {\n\t\ttry {\n\t\t\tHex.decode(input);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic boolean canDecrypt() {\n\t\treturn this.privateKey != null;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/TextEncryptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\n/**\n * Service interface for symmetric encryption of text strings.\n *\n * @author Keith Donald\n */\npublic interface TextEncryptor {\n\n\t/**\n\t * Encrypt the raw text string.\n\t */\n\tString encrypt(String text);\n\n\t/**\n\t * Decrypt the encrypted text string.\n\t */\n\tString decrypt(String encryptedText);\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/encrypt/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.encrypt;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.factory;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.security.crypto.argon2.Argon2PasswordEncoder;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.DelegatingPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;\nimport org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;\n\n/**\n * Used for creating {@link PasswordEncoder} instances\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic final class PasswordEncoderFactories {\n\n\tprivate PasswordEncoderFactories() {\n\t}\n\n\t/**\n\t * Creates a {@link DelegatingPasswordEncoder} with default mappings. Additional\n\t * mappings may be added and the encoding will be updated to conform with best\n\t * practices. However, due to the nature of {@link DelegatingPasswordEncoder} the\n\t * updates should not impact users. The mappings current are:\n\t *\n\t * <ul>\n\t * <li>bcrypt - {@link BCryptPasswordEncoder} (Also used for encoding)</li>\n\t * <li>ldap -\n\t * {@link org.springframework.security.crypto.password.LdapShaPasswordEncoder}</li>\n\t * <li>MD4 -\n\t * {@link org.springframework.security.crypto.password.Md4PasswordEncoder}</li>\n\t * <li>MD5 - {@code new MessageDigestPasswordEncoder(\"MD5\")}</li>\n\t * <li>noop -\n\t * {@link org.springframework.security.crypto.password.NoOpPasswordEncoder}</li>\n\t * <li>pbkdf2 - {@link Pbkdf2PasswordEncoder#defaultsForSpringSecurity_v5_5()}</li>\n\t * <li>pbkdf2@SpringSecurity_v5_8 -\n\t * {@link Pbkdf2PasswordEncoder#defaultsForSpringSecurity_v5_8()}</li>\n\t * <li>scrypt - {@link SCryptPasswordEncoder#defaultsForSpringSecurity_v4_1()}</li>\n\t * <li>scrypt@SpringSecurity_v5_8 -\n\t * {@link SCryptPasswordEncoder#defaultsForSpringSecurity_v5_8()}</li>\n\t * <li>SHA-1 - {@code new MessageDigestPasswordEncoder(\"SHA-1\")}</li>\n\t * <li>SHA-256 - {@code new MessageDigestPasswordEncoder(\"SHA-256\")}</li>\n\t * <li>sha256 -\n\t * {@link org.springframework.security.crypto.password.StandardPasswordEncoder}</li>\n\t * <li>argon2 - {@link Argon2PasswordEncoder#defaultsForSpringSecurity_v5_2()}</li>\n\t * <li>argon2@SpringSecurity_v5_8 -\n\t * {@link Argon2PasswordEncoder#defaultsForSpringSecurity_v5_8()}</li>\n\t * </ul>\n\t * @return the {@link PasswordEncoder} to use\n\t */\n\t@SuppressWarnings(\"deprecation\")\n\tpublic static PasswordEncoder createDelegatingPasswordEncoder() {\n\t\tString encodingId = \"bcrypt\";\n\t\tMap<String, PasswordEncoder> encoders = new HashMap<>();\n\t\tencoders.put(encodingId, new BCryptPasswordEncoder());\n\t\tencoders.put(\"ldap\", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());\n\t\tencoders.put(\"MD4\", new org.springframework.security.crypto.password.Md4PasswordEncoder());\n\t\tencoders.put(\"MD5\", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder(\"MD5\"));\n\t\tencoders.put(\"noop\", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());\n\t\tencoders.put(\"pbkdf2\", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());\n\t\tencoders.put(\"pbkdf2@SpringSecurity_v5_8\", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());\n\t\tencoders.put(\"scrypt\", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());\n\t\tencoders.put(\"scrypt@SpringSecurity_v5_8\", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());\n\t\tencoders.put(\"SHA-1\", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder(\"SHA-1\"));\n\t\tencoders.put(\"SHA-256\",\n\t\t\t\tnew org.springframework.security.crypto.password.MessageDigestPasswordEncoder(\"SHA-256\"));\n\t\tencoders.put(\"sha256\", new org.springframework.security.crypto.password.StandardPasswordEncoder());\n\t\tencoders.put(\"argon2\", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());\n\t\tencoders.put(\"argon2@SpringSecurity_v5_8\", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());\n\t\treturn new DelegatingPasswordEncoder(encodingId, encoders);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/factory/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.factory;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/keygen/Base64StringKeyGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\nimport java.util.Base64;\n\n/**\n * A StringKeyGenerator that generates base64-encoded String keys. Delegates to a\n * {@link BytesKeyGenerator} for the actual key generation.\n *\n * @author Joe Grandja\n * @author Rob Winch\n * @author Andrey Litvitski\n * @since 5.0\n */\npublic class Base64StringKeyGenerator implements StringKeyGenerator {\n\n\tprivate static final int DEFAULT_KEY_LENGTH = 32;\n\n\tprivate final BytesKeyGenerator keyGenerator;\n\n\tprivate final Base64.Encoder encoder;\n\n\t/**\n\t * Creates an instance with keyLength of 32 bytes and standard Base64 encoding.\n\t */\n\tpublic Base64StringKeyGenerator() {\n\t\tthis(DEFAULT_KEY_LENGTH);\n\t}\n\n\t/**\n\t * Creates an instance with the provided key length in bytes and standard Base64\n\t * encoding.\n\t * @param keyLength the key length in bytes\n\t */\n\tpublic Base64StringKeyGenerator(int keyLength) {\n\t\tthis(Base64.getEncoder(), keyLength);\n\t}\n\n\t/**\n\t * Creates an instance with keyLength of 32 bytes and the provided encoder.\n\t * @param encoder the encoder to use\n\t */\n\tpublic Base64StringKeyGenerator(Base64.Encoder encoder) {\n\t\tthis(encoder, DEFAULT_KEY_LENGTH);\n\t}\n\n\t/**\n\t * Creates an instance with the provided key length and encoder.\n\t * @param encoder the encoder to use\n\t * @param keyLength the key length to use\n\t */\n\tpublic Base64StringKeyGenerator(Base64.Encoder encoder, int keyLength) {\n\t\tif (encoder == null) {\n\t\t\tthrow new IllegalArgumentException(\"encode cannot be null\");\n\t\t}\n\t\tif (keyLength <= 0) {\n\t\t\tthrow new IllegalArgumentException(\"keyLength must be greater than 0\");\n\t\t}\n\t\tthis.encoder = encoder;\n\t\tthis.keyGenerator = KeyGenerators.secureRandom(keyLength);\n\t}\n\n\t@Override\n\tpublic String generateKey() {\n\t\tbyte[] key = this.keyGenerator.generateKey();\n\t\tbyte[] base64EncodedKey = this.encoder.encode(key);\n\t\treturn new String(base64EncodedKey);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/keygen/BytesKeyGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\n/**\n * A generator for unique byte array-based keys.\n *\n * @author Keith Donald\n */\npublic interface BytesKeyGenerator {\n\n\t/**\n\t * Get the length, in bytes, of keys created by this generator. Most unique keys are\n\t * at least 8 bytes in length.\n\t */\n\tint getKeyLength();\n\n\t/**\n\t * Generate a new key.\n\t */\n\tbyte[] generateKey();\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/keygen/HexEncodingStringKeyGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\nimport org.springframework.security.crypto.codec.Hex;\n\n/**\n * A StringKeyGenerator that generates hex-encoded String keys. Delegates to a\n * {@link BytesKeyGenerator} for the actual key generation.\n *\n * @author Keith Donald\n */\nfinal class HexEncodingStringKeyGenerator implements StringKeyGenerator {\n\n\tprivate final BytesKeyGenerator keyGenerator;\n\n\tHexEncodingStringKeyGenerator(BytesKeyGenerator keyGenerator) {\n\t\tthis.keyGenerator = keyGenerator;\n\t}\n\n\t@Override\n\tpublic String generateKey() {\n\t\treturn new String(Hex.encode(this.keyGenerator.generateKey()));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/keygen/KeyGenerators.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\nimport java.security.SecureRandom;\n\n/**\n * Factory for commonly used key generators. Public API for constructing a\n * {@link BytesKeyGenerator} or {@link StringKeyGenerator}.\n *\n * @author Keith Donald\n */\npublic final class KeyGenerators {\n\n\tprivate KeyGenerators() {\n\t}\n\n\t/**\n\t * Create a {@link BytesKeyGenerator} that uses a {@link SecureRandom} to generate\n\t * keys of 8 bytes in length.\n\t */\n\tpublic static BytesKeyGenerator secureRandom() {\n\t\treturn new SecureRandomBytesKeyGenerator();\n\t}\n\n\t/**\n\t * Create a {@link BytesKeyGenerator} that uses a {@link SecureRandom} to generate\n\t * keys of a custom length.\n\t * @param keyLength the key length in bytes, e.g. 16, for a 16 byte key.\n\t */\n\tpublic static BytesKeyGenerator secureRandom(int keyLength) {\n\t\treturn new SecureRandomBytesKeyGenerator(keyLength);\n\t}\n\n\t/**\n\t * Create a {@link BytesKeyGenerator} that returns a single, shared\n\t * {@link SecureRandom} key of a custom length.\n\t * @param keyLength the key length in bytes, e.g. 16, for a 16 byte key.\n\t */\n\tpublic static BytesKeyGenerator shared(int keyLength) {\n\t\treturn new SharedKeyGenerator(secureRandom(keyLength).generateKey());\n\t}\n\n\t/**\n\t * Creates a {@link StringKeyGenerator} that hex-encodes {@link SecureRandom} keys of\n\t * 8 bytes in length. The hex-encoded string is keyLength * 2 characters in length.\n\t */\n\tpublic static StringKeyGenerator string() {\n\t\treturn new HexEncodingStringKeyGenerator(secureRandom());\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/keygen/SecureRandomBytesKeyGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\nimport java.security.SecureRandom;\n\n/**\n * A KeyGenerator that uses {@link SecureRandom} to generate byte array-based keys.\n * <p>\n * No specific provider is used for the {@code SecureRandom}, so the platform default will\n * be used.\n *\n * @author Keith Donald\n */\nfinal class SecureRandomBytesKeyGenerator implements BytesKeyGenerator {\n\n\tprivate static final int DEFAULT_KEY_LENGTH = 8;\n\n\tprivate final SecureRandom random;\n\n\tprivate final int keyLength;\n\n\t/**\n\t * Creates a secure random key generator using the defaults.\n\t */\n\tSecureRandomBytesKeyGenerator() {\n\t\tthis(DEFAULT_KEY_LENGTH);\n\t}\n\n\t/**\n\t * Creates a secure random key generator with a custom key length.\n\t */\n\tSecureRandomBytesKeyGenerator(int keyLength) {\n\t\tthis.random = new SecureRandom();\n\t\tthis.keyLength = keyLength;\n\t}\n\n\t@Override\n\tpublic int getKeyLength() {\n\t\treturn this.keyLength;\n\t}\n\n\t@Override\n\tpublic byte[] generateKey() {\n\t\tbyte[] bytes = new byte[this.keyLength];\n\t\tthis.random.nextBytes(bytes);\n\t\treturn bytes;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/keygen/SharedKeyGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\n/**\n * Key generator that simply returns the same key every time.\n *\n * @author Keith Donald\n * @author Annabelle Donald\n * @author Corgan Donald\n */\nfinal class SharedKeyGenerator implements BytesKeyGenerator {\n\n\tprivate byte[] sharedKey;\n\n\tSharedKeyGenerator(byte[] sharedKey) {\n\t\tthis.sharedKey = sharedKey;\n\t}\n\n\t@Override\n\tpublic int getKeyLength() {\n\t\treturn this.sharedKey.length;\n\t}\n\n\t@Override\n\tpublic byte[] generateKey() {\n\t\treturn this.sharedKey;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/keygen/StringKeyGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\n/**\n * A generator for unique string keys.\n *\n * @author Keith Donald\n */\npublic interface StringKeyGenerator {\n\n\tString generateKey();\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/keygen/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.keygen;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/AbstractPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.security.MessageDigest;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.util.EncodingUtils;\n\n/**\n * Abstract base class for password encoders\n *\n * @author Rob Worsnop\n */\npublic abstract class AbstractPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate final BytesKeyGenerator saltGenerator;\n\n\tprotected AbstractPasswordEncoder() {\n\t\tthis.saltGenerator = KeyGenerators.secureRandom();\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tbyte[] salt = this.saltGenerator.generateKey();\n\t\tbyte[] encoded = encodeAndConcatenate(rawPassword, salt);\n\t\treturn String.valueOf(Hex.encode(encoded));\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tbyte[] digested = Hex.decode(encodedPassword);\n\t\tbyte[] salt = EncodingUtils.subArray(digested, 0, this.saltGenerator.getKeyLength());\n\t\treturn matchesNonNull(digested, encodeAndConcatenate(rawPassword, salt));\n\t}\n\n\tprotected abstract byte[] encodedNonNullPassword(CharSequence rawPassword, byte[] salt);\n\n\tprotected byte[] encodeAndConcatenate(CharSequence rawPassword, byte[] salt) {\n\t\treturn EncodingUtils.concatenate(salt, encodedNonNullPassword(rawPassword, salt));\n\t}\n\n\t/**\n\t * Constant time comparison to prevent against timing attacks.\n\t */\n\tprotected static boolean matchesNonNull(byte[] expected, byte[] actual) {\n\t\treturn MessageDigest.isEqual(expected, actual);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/AbstractValidatingPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.lang.Contract;\nimport org.springframework.util.StringUtils;\n\n/**\n * An abstract {@link PasswordEncoder} that implementers can use for expecting the\n * password to be non-{@code null}. Each common password API method is accompanied with an\n * abstract method with a {@code NonNull} prefix. By implementing this, the concrete class\n * is specifying what to do with the password when it is non-{@code null}, allowing this\n * class to handle the {@code null} case.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic abstract class AbstractValidatingPasswordEncoder implements PasswordEncoder {\n\n\t@Override\n\t@Contract(\"!null -> !null; null -> null\")\n\tpublic final @Nullable String encode(@Nullable CharSequence rawPassword) {\n\t\tif (rawPassword == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn encodeNonNullPassword(rawPassword.toString());\n\t}\n\n\tprotected abstract String encodeNonNullPassword(String rawPassword);\n\n\t@Override\n\tpublic final boolean matches(@Nullable CharSequence rawPassword, @Nullable String encodedPassword) {\n\t\tif (!StringUtils.hasLength(rawPassword) || !StringUtils.hasLength(encodedPassword)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn matchesNonNull(rawPassword.toString(), encodedPassword);\n\t}\n\n\tprotected abstract boolean matchesNonNull(String rawPassword, String encodedPassword);\n\n\t@Override\n\tpublic final boolean upgradeEncoding(@Nullable String encodedPassword) {\n\t\tif (!StringUtils.hasLength(encodedPassword)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn upgradeEncodingNonNull(encodedPassword);\n\t}\n\n\tprotected boolean upgradeEncodingNonNull(String encodedPassword) {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A password encoder that delegates to another PasswordEncoder based upon a prefixed\n * identifier.\n *\n * <h2>Constructing an instance</h2>\n *\n * You can easily construct an instance using\n * {@link org.springframework.security.crypto.factory.PasswordEncoderFactories}.\n * Alternatively, you may create your own custom instance. For example:\n *\n * <pre>\n * String idForEncode = \"bcrypt\";\n * Map&lt;String,PasswordEncoder&gt; encoders = new HashMap&lt;&gt;();\n * encoders.put(idForEncode, new BCryptPasswordEncoder());\n * encoders.put(\"noop\", NoOpPasswordEncoder.getInstance());\n * encoders.put(\"pbkdf2\", new Pbkdf2PasswordEncoder());\n * encoders.put(\"scrypt\", new SCryptPasswordEncoder());\n * encoders.put(\"sha256\", new StandardPasswordEncoder());\n *\n * PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);\n * </pre>\n *\n *\n * <h2>Password Storage Format</h2>\n *\n * The general format for a password is:\n *\n * <pre>\n * {id}encodedPassword\n * </pre>\n *\n * Such that \"id\" is an identifier used to look up which {@link PasswordEncoder} should be\n * used and \"encodedPassword\" is the original encoded password for the selected\n * {@link PasswordEncoder}. The \"id\" must be at the beginning of the password, start with\n * \"{\" (id prefix) and end with \"}\" (id suffix). Both id prefix and id suffix can be\n * customized via {@link #DelegatingPasswordEncoder(String, Map, String, String)}. If the\n * \"id\" cannot be found, the \"id\" will be null.\n *\n * For example, the following might be a list of passwords encoded using different \"id\".\n * All of the original passwords are \"password\".\n *\n * <pre>\n * {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n * {noop}password\n * {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc\n * {scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=\n * {sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0\n * </pre>\n *\n * For the DelegatingPasswordEncoder that we constructed above:\n *\n * <ol>\n * <li>The first password would have a {@code PasswordEncoder} id of \"bcrypt\" and\n * encodedPassword of \"$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\". When\n * matching it would delegate to\n * {@link org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder}</li>\n * <li>The second password would have a {@code PasswordEncoder} id of \"noop\" and\n * encodedPassword of \"password\". When matching it would delegate to\n * {@link NoOpPasswordEncoder}</li>\n * <li>The third password would have a {@code PasswordEncoder} id of \"pbkdf2\" and\n * encodedPassword of\n * \"5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc\".\n * When matching it would delegate to {@link Pbkdf2PasswordEncoder}</li>\n * <li>The fourth password would have a {@code PasswordEncoder} id of \"scrypt\" and\n * encodedPassword of\n * \"$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=\"\n * When matching it would delegate to\n * {@link org.springframework.security.crypto.scrypt.SCryptPasswordEncoder}</li>\n * <li>The final password would have a {@code PasswordEncoder} id of \"sha256\" and\n * encodedPassword of\n * \"97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0\".\n * When matching it would delegate to {@link StandardPasswordEncoder}</li>\n * </ol>\n *\n * <h2>Password Encoding</h2>\n *\n * The {@code idForEncode} passed into the constructor determines which\n * {@link PasswordEncoder} will be used for encoding passwords. In the\n * {@code DelegatingPasswordEncoder} we constructed above, that means that the result of\n * encoding \"password\" would be delegated to {@code BCryptPasswordEncoder} and be prefixed\n * with \"{bcrypt}\". The end result would look like:\n *\n * <pre>\n * {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n * </pre>\n *\n * <h2>Password Matching</h2>\n *\n * Matching is done based upon the \"id\" and the mapping of the \"id\" to the\n * {@link PasswordEncoder} provided in the constructor. Our example in \"Password Storage\n * Format\" provides a working example of how this is done.\n *\n * By default the result of invoking {@link #matches(CharSequence, String)} with a\n * password with an \"id\" that is not mapped (including a null id) will result in an\n * {@link IllegalArgumentException}. This behavior can be customized using\n * {@link #setDefaultPasswordEncoderForMatches(PasswordEncoder)}.\n *\n * @author Rob Winch\n * @author Michael Simons\n * @author heowc\n * @author Jihoon Cha\n * @since 5.0\n * @see org.springframework.security.crypto.factory.PasswordEncoderFactories\n */\npublic class DelegatingPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final String DEFAULT_ID_PREFIX = \"{\";\n\n\tprivate static final String DEFAULT_ID_SUFFIX = \"}\";\n\n\tprivate static final String NO_PASSWORD_ENCODER_MAPPED = \"There is no password encoder mapped for the id '%s'. \"\n\t\t\t+ \"Check your configuration to ensure it matches one of the registered encoders.\";\n\n\tprivate static final String NO_PASSWORD_ENCODER_PREFIX = \"Given that there is no default password encoder configured, each password must have a password encoding prefix. \"\n\t\t\t+ \"Please either prefix this password with '{noop}' or set a default password encoder in `DelegatingPasswordEncoder`.\";\n\n\tprivate static final String MALFORMED_PASSWORD_ENCODER_PREFIX = \"The name of the password encoder is improperly \"\n\t\t\t+ \"formatted or incomplete. The format should be '%sENCODER%spassword'.\";\n\n\tprivate final String idPrefix;\n\n\tprivate final String idSuffix;\n\n\tprivate final String idForEncode;\n\n\tprivate final PasswordEncoder passwordEncoderForEncode;\n\n\tprivate final Map<@Nullable String, PasswordEncoder> idToPasswordEncoder;\n\n\tprivate PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();\n\n\t/**\n\t * Creates a new instance\n\t * @param idForEncode the id used to lookup which {@link PasswordEncoder} should be\n\t * used for {@link #encode(CharSequence)}\n\t * @param idToPasswordEncoder a Map of id to {@link PasswordEncoder} used to determine\n\t * which {@link PasswordEncoder} should be used for\n\t * {@link #matches(CharSequence, String)}\n\t */\n\tpublic DelegatingPasswordEncoder(String idForEncode, Map<String, PasswordEncoder> idToPasswordEncoder) {\n\t\tthis(idForEncode, idToPasswordEncoder, DEFAULT_ID_PREFIX, DEFAULT_ID_SUFFIX);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param idForEncode the id used to lookup which {@link PasswordEncoder} should be\n\t * used for {@link #encode(CharSequence)}\n\t * @param idToPasswordEncoder a Map of id to {@link PasswordEncoder} used to determine\n\t * which {@link PasswordEncoder} should be used for\n\t * @param idPrefix the prefix that denotes the start of the id in the encoded results\n\t * @param idSuffix the suffix that denotes the end of an id in the encoded results\n\t * {@link #matches(CharSequence, String)}\n\t */\n\tpublic DelegatingPasswordEncoder(String idForEncode, Map<String, PasswordEncoder> idToPasswordEncoder,\n\t\t\tString idPrefix, String idSuffix) {\n\t\tif (idForEncode == null) {\n\t\t\tthrow new IllegalArgumentException(\"idForEncode cannot be null\");\n\t\t}\n\t\tif (idPrefix == null) {\n\t\t\tthrow new IllegalArgumentException(\"prefix cannot be null\");\n\t\t}\n\t\tif (idSuffix == null || idSuffix.isEmpty()) {\n\t\t\tthrow new IllegalArgumentException(\"suffix cannot be empty\");\n\t\t}\n\t\tif (idPrefix.contains(idSuffix)) {\n\t\t\tthrow new IllegalArgumentException(\"idPrefix \" + idPrefix + \" cannot contain idSuffix \" + idSuffix);\n\t\t}\n\n\t\tif (!idToPasswordEncoder.containsKey(idForEncode)) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"idForEncode \" + idForEncode + \"is not found in idToPasswordEncoder \" + idToPasswordEncoder);\n\t\t}\n\t\tfor (String id : idToPasswordEncoder.keySet()) {\n\t\t\tif (id == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!idPrefix.isEmpty() && id.contains(idPrefix)) {\n\t\t\t\tthrow new IllegalArgumentException(\"id \" + id + \" cannot contain \" + idPrefix);\n\t\t\t}\n\t\t\tif (id.contains(idSuffix)) {\n\t\t\t\tthrow new IllegalArgumentException(\"id \" + id + \" cannot contain \" + idSuffix);\n\t\t\t}\n\t\t}\n\t\tthis.idForEncode = idForEncode;\n\t\tthis.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode);\n\t\tthis.idToPasswordEncoder = new HashMap<>(idToPasswordEncoder);\n\t\tthis.idPrefix = idPrefix;\n\t\tthis.idSuffix = idSuffix;\n\t}\n\n\t/**\n\t * Sets the {@link PasswordEncoder} to delegate to for\n\t * {@link #matches(CharSequence, String)} if the id is not mapped to a\n\t * {@link PasswordEncoder}.\n\t *\n\t * <p>\n\t * The encodedPassword provided will be the full password passed in including the\n\t * {\"id\"} portion.* For example, if the password of \"{notmapped}foobar\" was used, the\n\t * \"id\" would be \"notmapped\" and the encodedPassword passed into the\n\t * {@link PasswordEncoder} would be \"{notmapped}foobar\".\n\t * </p>\n\t * @param defaultPasswordEncoderForMatches the encoder to use. The default is to throw\n\t * an {@link IllegalArgumentException}\n\t */\n\tpublic void setDefaultPasswordEncoderForMatches(PasswordEncoder defaultPasswordEncoderForMatches) {\n\t\tif (defaultPasswordEncoderForMatches == null) {\n\t\t\tthrow new IllegalArgumentException(\"defaultPasswordEncoderForMatches cannot be null\");\n\t\t}\n\t\tthis.defaultPasswordEncoderForMatches = defaultPasswordEncoderForMatches;\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\treturn this.idPrefix + this.idForEncode + this.idSuffix + this.passwordEncoderForEncode.encode(rawPassword);\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String prefixEncodedPassword) {\n\t\tString id = extractId(prefixEncodedPassword);\n\t\tPasswordEncoder delegate = this.idToPasswordEncoder.get(id);\n\t\tif (delegate == null) {\n\t\t\treturn this.defaultPasswordEncoderForMatches.matches(rawPassword, prefixEncodedPassword);\n\t\t}\n\t\tString encodedPassword = extractEncodedPassword(prefixEncodedPassword);\n\t\treturn delegate.matches(rawPassword, encodedPassword);\n\t}\n\n\tprivate @Nullable String extractId(@Nullable String prefixEncodedPassword) {\n\t\tif (prefixEncodedPassword == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint start = prefixEncodedPassword.indexOf(this.idPrefix);\n\t\tif (start != 0) {\n\t\t\treturn null;\n\t\t}\n\t\tint end = prefixEncodedPassword.indexOf(this.idSuffix, start);\n\t\tif (end < 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn prefixEncodedPassword.substring(start + this.idPrefix.length(), end);\n\t}\n\n\t@Override\n\tprotected boolean upgradeEncodingNonNull(String prefixEncodedPassword) {\n\t\tString id = extractId(prefixEncodedPassword);\n\t\tif (!this.idForEncode.equalsIgnoreCase(id)) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\tString encodedPassword = extractEncodedPassword(prefixEncodedPassword);\n\t\t\treturn this.passwordEncoderForEncode.upgradeEncoding(encodedPassword);\n\t\t}\n\t}\n\n\tprivate String extractEncodedPassword(String prefixEncodedPassword) {\n\t\tint start = prefixEncodedPassword.indexOf(this.idSuffix);\n\t\treturn prefixEncodedPassword.substring(start + this.idSuffix.length());\n\t}\n\n\t/**\n\t * Default {@link PasswordEncoder} that throws an exception telling that a suitable\n\t * {@link PasswordEncoder} for the id could not be found.\n\t */\n\tprivate class UnmappedIdPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\t\t@Override\n\t\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\t\tthrow new UnsupportedOperationException(\"encode is not supported\");\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean matchesNonNull(String rawPassword, String prefixEncodedPassword) {\n\t\t\tString id = extractId(prefixEncodedPassword);\n\t\t\tif (id != null && !id.isBlank()) {\n\t\t\t\tthrow new IllegalArgumentException(String.format(NO_PASSWORD_ENCODER_MAPPED, id));\n\t\t\t}\n\t\t\tif (prefixEncodedPassword != null && !prefixEncodedPassword.isBlank()) {\n\t\t\t\tint start = prefixEncodedPassword.indexOf(DelegatingPasswordEncoder.this.idPrefix);\n\t\t\t\tint end = prefixEncodedPassword.indexOf(DelegatingPasswordEncoder.this.idSuffix, start);\n\t\t\t\tif (start < 0 && end < 0) {\n\t\t\t\t\tthrow new IllegalArgumentException(NO_PASSWORD_ENCODER_PREFIX);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(String.format(MALFORMED_PASSWORD_ENCODER_PREFIX,\n\t\t\t\t\tDelegatingPasswordEncoder.this.idPrefix, DelegatingPasswordEncoder.this.idSuffix));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/Digester.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\n/**\n * Helper for working with the MessageDigest API.\n *\n * Performs the configured number of iterations of the hashing algorithm per digest to aid\n * in protecting against brute force attacks.\n *\n * @author Keith Donald\n * @author Luke Taylor\n */\nfinal class Digester {\n\n\tprivate final String algorithm;\n\n\tprivate int iterations;\n\n\t/**\n\t * Create a new Digester.\n\t * @param algorithm the digest algorithm; for example, \"SHA-1\" or \"SHA-256\".\n\t * @param iterations the number of times to apply the digest algorithm to the input\n\t */\n\tDigester(String algorithm, int iterations) {\n\t\t// eagerly validate the algorithm\n\t\tcreateDigest(algorithm);\n\t\tthis.algorithm = algorithm;\n\t\tsetIterations(iterations);\n\t}\n\n\tbyte[] digest(byte[] value) {\n\t\tMessageDigest messageDigest = createDigest(this.algorithm);\n\t\tfor (int i = 0; i < this.iterations; i++) {\n\t\t\tvalue = messageDigest.digest(value);\n\t\t}\n\t\treturn value;\n\t}\n\n\tvoid setIterations(int iterations) {\n\t\tif (iterations <= 0) {\n\t\t\tthrow new IllegalArgumentException(\"Iterations value must be greater than zero\");\n\t\t}\n\t\tthis.iterations = iterations;\n\t}\n\n\tprivate static MessageDigest createDigest(String algorithm) {\n\t\ttry {\n\t\t\treturn MessageDigest.getInstance(algorithm);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(\"No such hashing algorithm\", ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/LdapShaPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.security.MessageDigest;\nimport java.util.Base64;\nimport java.util.Locale;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\n\n/**\n * This {@link PasswordEncoder} is provided for legacy purposes only and is not considered\n * secure.\n *\n * A version of {@link PasswordEncoder} which supports Ldap SHA and SSHA (salted-SHA)\n * encodings. The values are base-64 encoded and have the label \"{SHA}\" (or \"{SSHA}\")\n * prepended to the encoded hash. These can be made lower-case in the encoded password, if\n * required, by setting the <tt>forceLowerCasePrefix</tt> property to true.\n *\n * Also supports plain text passwords, so can safely be used in cases when both encoded\n * and non-encoded passwords are in use or when a null implementation is required.\n *\n * @author Luke Taylor\n * @deprecated Digest based password encoding is not considered secure. Instead use an\n * adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or\n * SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports\n * password upgrades. There are no plans to remove this support. It is deprecated to\n * indicate that this is a legacy implementation and using it is considered insecure.\n */\n@Deprecated\npublic class LdapShaPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\t/** The number of bytes in a SHA hash */\n\tprivate static final int SHA_LENGTH = 20;\n\n\tprivate static final String SSHA_PREFIX = \"{SSHA}\";\n\n\tprivate static final String SSHA_PREFIX_LC = SSHA_PREFIX.toLowerCase(Locale.ENGLISH);\n\n\tprivate static final String SHA_PREFIX = \"{SHA}\";\n\n\tprivate static final String SHA_PREFIX_LC = SHA_PREFIX.toLowerCase(Locale.ENGLISH);\n\n\tprivate BytesKeyGenerator saltGenerator;\n\n\tprivate boolean forceLowerCasePrefix;\n\n\tpublic LdapShaPasswordEncoder() {\n\t\tthis(KeyGenerators.secureRandom());\n\t}\n\n\tpublic LdapShaPasswordEncoder(BytesKeyGenerator saltGenerator) {\n\t\tif (saltGenerator == null) {\n\t\t\tthrow new IllegalArgumentException(\"saltGenerator cannot be null\");\n\t\t}\n\t\tthis.saltGenerator = saltGenerator;\n\t}\n\n\tprivate byte[] combineHashAndSalt(byte[] hash, byte @Nullable [] salt) {\n\t\tif (salt == null) {\n\t\t\treturn hash;\n\t\t}\n\t\tbyte[] hashAndSalt = new byte[hash.length + salt.length];\n\t\tSystem.arraycopy(hash, 0, hashAndSalt, 0, hash.length);\n\t\tSystem.arraycopy(salt, 0, hashAndSalt, hash.length, salt.length);\n\t\treturn hashAndSalt;\n\t}\n\n\t/**\n\t * Calculates the hash of password (and salt bytes, if supplied) and returns a base64\n\t * encoded concatenation of the hash and salt, prefixed with {SHA} (or {SSHA} if salt\n\t * was used).\n\t * @param rawPassword the password to be encoded.\n\t * @return the encoded password in the specified format\n\t *\n\t */\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tbyte[] salt = this.saltGenerator.generateKey();\n\t\treturn encode(rawPassword, salt);\n\t}\n\n\tprivate String encode(CharSequence rawPassword, byte @Nullable [] salt) {\n\t\tMessageDigest sha = getSha(rawPassword);\n\t\tif (salt != null) {\n\t\t\tsha.update(salt);\n\t\t}\n\t\tbyte[] hash = combineHashAndSalt(sha.digest(), salt);\n\t\tString prefix = getPrefix(salt);\n\t\treturn prefix + Utf8.decode(Base64.getEncoder().encode(hash));\n\t}\n\n\tprivate MessageDigest getSha(CharSequence rawPassword) {\n\t\ttry {\n\t\t\tMessageDigest sha = MessageDigest.getInstance(\"SHA\");\n\t\t\tsha.update(Utf8.encode(rawPassword));\n\t\t\treturn sha;\n\t\t}\n\t\tcatch (java.security.NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(\"No SHA implementation available!\");\n\t\t}\n\t}\n\n\tprivate String getPrefix(byte @Nullable [] salt) {\n\t\tif (salt == null || salt.length == 0) {\n\t\t\treturn this.forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;\n\t\t}\n\t\treturn this.forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;\n\t}\n\n\tprivate byte[] extractSalt(String encPass) {\n\t\tString encPassNoLabel = encPass.substring(6);\n\t\tbyte[] hashAndSalt = Base64.getDecoder().decode(encPassNoLabel.getBytes());\n\t\tint saltLength = hashAndSalt.length - SHA_LENGTH;\n\t\tbyte[] salt = new byte[saltLength];\n\t\tSystem.arraycopy(hashAndSalt, SHA_LENGTH, salt, 0, saltLength);\n\t\treturn salt;\n\t}\n\n\t/**\n\t * Checks the validity of an unencoded password against an encoded one in the form\n\t * \"{SSHA}sQuQF8vj8Eg2Y1hPdh3bkQhCKQBgjhQI\".\n\t * @param rawPassword unencoded password to be verified.\n\t * @param encodedPassword the actual SSHA or SHA encoded password\n\t * @return true if they match (independent of the case of the prefix).\n\t */\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tString prefix = extractPrefix(encodedPassword);\n\t\tif (prefix == null) {\n\t\t\treturn PasswordEncoderUtils.equals(encodedPassword, rawPassword);\n\t\t}\n\t\tbyte[] salt = getSalt(encodedPassword, prefix);\n\t\tint startOfHash = prefix.length();\n\t\tString encodedRawPass = encode(rawPassword, salt).substring(startOfHash);\n\t\treturn PasswordEncoderUtils.equals(encodedRawPass, encodedPassword.substring(startOfHash));\n\t}\n\n\tprivate byte @Nullable [] getSalt(String encodedPassword, String prefix) {\n\t\tif (prefix.equals(SSHA_PREFIX) || prefix.equals(SSHA_PREFIX_LC)) {\n\t\t\treturn extractSalt(encodedPassword);\n\t\t}\n\t\tif (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) {\n\t\t\tthrow new IllegalArgumentException(\"Unsupported password prefix '\" + prefix + \"'\");\n\t\t}\n\t\t// Standard SHA\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the hash prefix or null if there isn't one.\n\t */\n\tprivate @Nullable String extractPrefix(String encPass) {\n\t\tif (!encPass.startsWith(\"{\")) {\n\t\t\treturn null;\n\t\t}\n\t\tint secondBrace = encPass.lastIndexOf('}');\n\t\tif (secondBrace < 0) {\n\t\t\tthrow new IllegalArgumentException(\"Couldn't find closing brace for SHA prefix\");\n\t\t}\n\t\treturn encPass.substring(0, secondBrace + 1);\n\t}\n\n\tpublic void setForceLowerCasePrefix(boolean forceLowerCasePrefix) {\n\t\tthis.forceLowerCasePrefix = forceLowerCasePrefix;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/Md4.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\n/**\n * Implementation of the MD4 message digest derived from the RSA Data Security, Inc, MD4\n * Message-Digest Algorithm.\n *\n * @author Alan Stewart\n */\nclass Md4 {\n\n\tprivate static final int BLOCK_SIZE = 64;\n\n\tprivate static final int HASH_SIZE = 16;\n\n\tprivate final byte[] buffer = new byte[BLOCK_SIZE];\n\n\tprivate int bufferOffset;\n\n\tprivate long byteCount;\n\n\tprivate final int[] state = new int[4];\n\n\tprivate final int[] tmp = new int[16];\n\n\tMd4() {\n\t\treset();\n\t}\n\n\tvoid reset() {\n\t\tthis.bufferOffset = 0;\n\t\tthis.byteCount = 0;\n\t\tthis.state[0] = 0x67452301;\n\t\tthis.state[1] = 0xEFCDAB89;\n\t\tthis.state[2] = 0x98BADCFE;\n\t\tthis.state[3] = 0x10325476;\n\t}\n\n\tbyte[] digest() {\n\t\tbyte[] resBuf = new byte[HASH_SIZE];\n\t\tdigest(resBuf, 0, HASH_SIZE);\n\t\treturn resBuf;\n\t}\n\n\tprivate void digest(byte[] buffer, int off) {\n\t\tfor (int i = 0; i < 4; i++) {\n\t\t\tfor (int j = 0; j < 4; j++) {\n\t\t\t\tbuffer[off + (i * 4 + j)] = (byte) (this.state[i] >>> (8 * j));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void digest(byte[] buffer, int offset, int len) {\n\t\tthis.buffer[this.bufferOffset++] = (byte) 0x80;\n\t\tint lenOfBitLen = 8;\n\t\tint C = BLOCK_SIZE - lenOfBitLen;\n\t\tif (this.bufferOffset > C) {\n\t\t\twhile (this.bufferOffset < BLOCK_SIZE) {\n\t\t\t\tthis.buffer[this.bufferOffset++] = (byte) 0x00;\n\t\t\t}\n\t\t\tupdate(this.buffer, 0);\n\t\t\tthis.bufferOffset = 0;\n\t\t}\n\t\twhile (this.bufferOffset < C) {\n\t\t\tthis.buffer[this.bufferOffset++] = (byte) 0x00;\n\t\t}\n\t\tlong bitCount = this.byteCount * 8;\n\t\tfor (int i = 0; i < 64; i += 8) {\n\t\t\tthis.buffer[this.bufferOffset++] = (byte) (bitCount >>> (i));\n\t\t}\n\t\tupdate(this.buffer, 0);\n\t\tdigest(buffer, offset);\n\t}\n\n\tvoid update(byte[] input, int offset, int length) {\n\t\tthis.byteCount += length;\n\t\tint todo;\n\t\twhile (length >= (todo = BLOCK_SIZE - this.bufferOffset)) {\n\t\t\tSystem.arraycopy(input, offset, this.buffer, this.bufferOffset, todo);\n\t\t\tupdate(this.buffer, 0);\n\t\t\tlength -= todo;\n\t\t\toffset += todo;\n\t\t\tthis.bufferOffset = 0;\n\t\t}\n\n\t\tSystem.arraycopy(input, offset, this.buffer, this.bufferOffset, length);\n\t\tthis.bufferOffset += length;\n\t}\n\n\tprivate void update(byte[] block, int offset) {\n\t\tfor (int i = 0; i < 16; i++) {\n\t\t\tthis.tmp[i] = (block[offset++] & 0xFF) | (block[offset++] & 0xFF) << 8 | (block[offset++] & 0xFF) << 16\n\t\t\t\t\t| (block[offset++] & 0xFF) << 24;\n\t\t}\n\n\t\tint A = this.state[0];\n\t\tint B = this.state[1];\n\t\tint C = this.state[2];\n\t\tint D = this.state[3];\n\n\t\tA = FF(A, B, C, D, this.tmp[0], 3);\n\t\tD = FF(D, A, B, C, this.tmp[1], 7);\n\t\tC = FF(C, D, A, B, this.tmp[2], 11);\n\t\tB = FF(B, C, D, A, this.tmp[3], 19);\n\t\tA = FF(A, B, C, D, this.tmp[4], 3);\n\t\tD = FF(D, A, B, C, this.tmp[5], 7);\n\t\tC = FF(C, D, A, B, this.tmp[6], 11);\n\t\tB = FF(B, C, D, A, this.tmp[7], 19);\n\t\tA = FF(A, B, C, D, this.tmp[8], 3);\n\t\tD = FF(D, A, B, C, this.tmp[9], 7);\n\t\tC = FF(C, D, A, B, this.tmp[10], 11);\n\t\tB = FF(B, C, D, A, this.tmp[11], 19);\n\t\tA = FF(A, B, C, D, this.tmp[12], 3);\n\t\tD = FF(D, A, B, C, this.tmp[13], 7);\n\t\tC = FF(C, D, A, B, this.tmp[14], 11);\n\t\tB = FF(B, C, D, A, this.tmp[15], 19);\n\n\t\tA = GG(A, B, C, D, this.tmp[0], 3);\n\t\tD = GG(D, A, B, C, this.tmp[4], 5);\n\t\tC = GG(C, D, A, B, this.tmp[8], 9);\n\t\tB = GG(B, C, D, A, this.tmp[12], 13);\n\t\tA = GG(A, B, C, D, this.tmp[1], 3);\n\t\tD = GG(D, A, B, C, this.tmp[5], 5);\n\t\tC = GG(C, D, A, B, this.tmp[9], 9);\n\t\tB = GG(B, C, D, A, this.tmp[13], 13);\n\t\tA = GG(A, B, C, D, this.tmp[2], 3);\n\t\tD = GG(D, A, B, C, this.tmp[6], 5);\n\t\tC = GG(C, D, A, B, this.tmp[10], 9);\n\t\tB = GG(B, C, D, A, this.tmp[14], 13);\n\t\tA = GG(A, B, C, D, this.tmp[3], 3);\n\t\tD = GG(D, A, B, C, this.tmp[7], 5);\n\t\tC = GG(C, D, A, B, this.tmp[11], 9);\n\t\tB = GG(B, C, D, A, this.tmp[15], 13);\n\n\t\tA = HH(A, B, C, D, this.tmp[0], 3);\n\t\tD = HH(D, A, B, C, this.tmp[8], 9);\n\t\tC = HH(C, D, A, B, this.tmp[4], 11);\n\t\tB = HH(B, C, D, A, this.tmp[12], 15);\n\t\tA = HH(A, B, C, D, this.tmp[2], 3);\n\t\tD = HH(D, A, B, C, this.tmp[10], 9);\n\t\tC = HH(C, D, A, B, this.tmp[6], 11);\n\t\tB = HH(B, C, D, A, this.tmp[14], 15);\n\t\tA = HH(A, B, C, D, this.tmp[1], 3);\n\t\tD = HH(D, A, B, C, this.tmp[9], 9);\n\t\tC = HH(C, D, A, B, this.tmp[5], 11);\n\t\tB = HH(B, C, D, A, this.tmp[13], 15);\n\t\tA = HH(A, B, C, D, this.tmp[3], 3);\n\t\tD = HH(D, A, B, C, this.tmp[11], 9);\n\t\tC = HH(C, D, A, B, this.tmp[7], 11);\n\t\tB = HH(B, C, D, A, this.tmp[15], 15);\n\n\t\tthis.state[0] += A;\n\t\tthis.state[1] += B;\n\t\tthis.state[2] += C;\n\t\tthis.state[3] += D;\n\t}\n\n\tprivate int FF(int a, int b, int c, int d, int x, int s) {\n\t\tint t = a + ((b & c) | (~b & d)) + x;\n\t\treturn t << s | t >>> (32 - s);\n\t}\n\n\tprivate int GG(int a, int b, int c, int d, int x, int s) {\n\t\tint t = a + ((b & (c | d)) | (c & d)) + x + 0x5A827999;\n\t\treturn t << s | t >>> (32 - s);\n\t}\n\n\tprivate int HH(int a, int b, int c, int d, int x, int s) {\n\t\tint t = a + (b ^ c ^ d) + x + 0x6ED9EBA1;\n\t\treturn t << s | t >>> (32 - s);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/Md4PasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.util.Base64;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\n\n/**\n * This {@link PasswordEncoder} is provided for legacy purposes only and is not considered\n * secure.\n *\n * Encodes passwords using MD4. The general format of the password is:\n *\n * <pre>\n * s = salt == null ? \"\" : \"{\" + salt + \"}\"\n * s + md4(password + s)\n * </pre>\n *\n * Such that \"salt\" is the salt, md4 is the digest method, and password is the actual\n * password. For example with a password of \"password\", and a salt of \"thisissalt\":\n *\n * <pre>\n * String s = salt == null ? \"\" : \"{\" + salt + \"}\";\n * s + md4(password + s)\n * \"{thisissalt}\" + md4(password + \"{thisissalt}\")\n * \"{thisissalt}6cc7924dad12ade79dfb99e424f25260\"\n * </pre>\n *\n * If the salt does not exist, then omit \"{salt}\" like this:\n *\n * <pre>\n * md4(password)\n * </pre>\n *\n * If the salt is an empty String, then only use \"{}\" like this:\n *\n * <pre>\n * \"{}\" + md4(password + \"{}\")\n * </pre>\n *\n * The format is intended to work with the Md4PasswordEncoder that was found in the Spring\n * Security core module. However, the passwords will need to be migrated to include any\n * salt with the password since this API provides Salt internally vs making it the\n * responsibility of the user. To migrate passwords from the SaltSource use the following:\n *\n * <pre>\n * String salt = saltSource.getSalt(user);\n * String s = salt == null ? null : \"{\" + salt + \"}\";\n * String migratedPassword = s + user.getPassword();\n * </pre>\n *\n * @author Ray Krueger\n * @author Luke Taylor\n * @author Rob winch\n * @since 5.0\n * @deprecated Digest based password encoding is not considered secure. Instead use an\n * adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or\n * SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports\n * password upgrades. There are no plans to remove this support. It is deprecated to\n * indicate that this is a legacy implementation and using it is considered insecure.\n */\n@Deprecated\npublic class Md4PasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final String PREFIX = \"{\";\n\n\tprivate static final String SUFFIX = \"}\";\n\n\tprivate StringKeyGenerator saltGenerator = new Base64StringKeyGenerator();\n\n\tprivate boolean encodeHashAsBase64;\n\n\tpublic void setEncodeHashAsBase64(boolean encodeHashAsBase64) {\n\t\tthis.encodeHashAsBase64 = encodeHashAsBase64;\n\t}\n\n\t/**\n\t * Encodes the rawPass using a MessageDigest. If a salt is specified it will be merged\n\t * with the password before encoding.\n\t * @param rawPassword The plain text password\n\t * @return Hex string of password digest (or base64 encoded string if\n\t * encodeHashAsBase64 is enabled.\n\t */\n\t@Override\n\tpublic String encodeNonNullPassword(String rawPassword) {\n\t\tString salt = PREFIX + this.saltGenerator.generateKey() + SUFFIX;\n\t\treturn digest(salt, rawPassword);\n\t}\n\n\tprivate String digest(String salt, CharSequence rawPassword) {\n\t\tif (rawPassword == null) {\n\t\t\trawPassword = \"\";\n\t\t}\n\t\tString saltedPassword = rawPassword + salt;\n\t\tbyte[] saltedPasswordBytes = Utf8.encode(saltedPassword);\n\t\tMd4 md4 = new Md4();\n\t\tmd4.update(saltedPasswordBytes, 0, saltedPasswordBytes.length);\n\t\tbyte[] digest = md4.digest();\n\t\tString encoded = encodedNonNullPassword(digest);\n\t\treturn salt + encoded;\n\t}\n\n\tprivate String encodedNonNullPassword(byte[] digest) {\n\t\tif (this.encodeHashAsBase64) {\n\t\t\treturn Utf8.decode(Base64.getEncoder().encode(digest));\n\t\t}\n\t\treturn new String(Hex.encode(digest));\n\t}\n\n\t/**\n\t * Takes a previously encoded password and compares it with a rawpassword after mixing\n\t * in the salt and encoding that value\n\t * @param rawPassword plain text password\n\t * @param encodedPassword previously encoded password\n\t * @return true or false\n\t */\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tString salt = extractSalt(encodedPassword);\n\t\tString rawPasswordEncoded = digest(salt, rawPassword);\n\t\treturn PasswordEncoderUtils.equals(encodedPassword.toString(), rawPasswordEncoded);\n\t}\n\n\tprivate String extractSalt(String prefixEncodedPassword) {\n\t\tint start = prefixEncodedPassword.indexOf(PREFIX);\n\t\tif (start != 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tint end = prefixEncodedPassword.indexOf(SUFFIX, start);\n\t\tif (end < 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn prefixEncodedPassword.substring(start, end + 1);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/MessageDigestPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.security.MessageDigest;\nimport java.util.Base64;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\n\n/**\n * This {@link PasswordEncoder} is provided for legacy purposes only and is not considered\n * secure.\n *\n * Encodes passwords using the passed in {@link MessageDigest}.\n *\n * The general format of the password is:\n *\n * <pre>\n * s = salt == null ? \"\" : \"{\" + salt + \"}\"\n * s + digest(password + s)\n * </pre>\n *\n * Such that \"salt\" is the salt, digest is the digest method, and password is the actual\n * password. For example when using MD5, a password of \"password\", and a salt of\n * \"thisissalt\":\n *\n * <pre>\n * String s = salt == null ? \"\" : \"{\" + salt + \"}\";\n * s + md5(password + s)\n * \"{thisissalt}\" + md5(password + \"{thisissalt}\")\n * \"{thisissalt}2a4e7104c2780098f50ed5a84bb2323d\"\n * </pre>\n *\n * If the salt does not exist, then omit \"{salt}\" like this:\n *\n * <pre>\n * digest(password)\n * </pre>\n *\n * If the salt is an empty String, then only use \"{}\" like this:\n *\n * <pre>\n * \"{}\" + digest(password + \"{}\")\n * </pre>\n *\n * The format is intended to work with the DigestPasswordEncoder that was found in the\n * Spring Security core module. However, the passwords will need to be migrated to include\n * any salt with the password since this API provides Salt internally vs making it the\n * responsibility of the user. To migrate passwords from the SaltSource use the following:\n *\n * <pre>\n * String salt = saltSource.getSalt(user);\n * String s = salt == null ? null : \"{\" + salt + \"}\";\n * String migratedPassword = s + user.getPassword();\n * </pre>\n *\n * @author Ray Krueger\n * @author Luke Taylor\n * @author Rob Winch\n * @since 5.0\n * @deprecated Digest based password encoding is not considered secure. Instead use an\n * adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or\n * SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports\n * password upgrades. There are no plans to remove this support. It is deprecated to\n * indicate that this is a legacy implementation and using it is considered insecure.\n */\n@Deprecated\npublic class MessageDigestPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final String PREFIX = \"{\";\n\n\tprivate static final String SUFFIX = \"}\";\n\n\tprivate StringKeyGenerator saltGenerator = new Base64StringKeyGenerator();\n\n\tprivate boolean encodeHashAsBase64;\n\n\tprivate Digester digester;\n\n\t/**\n\t * The digest algorithm to use Supports the named\n\t * <a href=\"https://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html#AppA\">\n\t * Message Digest Algorithms</a> in the Java environment.\n\t * @param algorithm\n\t */\n\tpublic MessageDigestPasswordEncoder(String algorithm) {\n\t\tthis.digester = new Digester(algorithm, 1);\n\t}\n\n\tpublic void setEncodeHashAsBase64(boolean encodeHashAsBase64) {\n\t\tthis.encodeHashAsBase64 = encodeHashAsBase64;\n\t}\n\n\t/**\n\t * Encodes the rawPass using a MessageDigest. If a salt is specified it will be merged\n\t * with the password before encoding.\n\t * @param rawPassword The plain text password\n\t * @return Hex string of password digest (or base64 encoded string if\n\t * encodeHashAsBase64 is enabled.\n\t */\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tString salt = PREFIX + this.saltGenerator.generateKey() + SUFFIX;\n\t\treturn digest(salt, rawPassword);\n\t}\n\n\tprivate String digest(String salt, CharSequence rawPassword) {\n\t\tString saltedPassword = rawPassword + salt;\n\t\tbyte[] digest = this.digester.digest(Utf8.encode(saltedPassword));\n\t\tString encoded = encodedNonNullPassword(digest);\n\t\treturn salt + encoded;\n\t}\n\n\tprivate String encodedNonNullPassword(byte[] digest) {\n\t\tif (this.encodeHashAsBase64) {\n\t\t\treturn Utf8.decode(Base64.getEncoder().encode(digest));\n\t\t}\n\t\treturn new String(Hex.encode(digest));\n\t}\n\n\t/**\n\t * Takes a previously encoded password and compares it with a rawpassword after mixing\n\t * in the salt and encoding that value\n\t * @param rawPassword plain text password\n\t * @param encodedPassword previously encoded password\n\t * @return true or false\n\t */\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tString salt = extractSalt(encodedPassword);\n\t\tString rawPasswordEncoded = digest(salt, rawPassword);\n\t\treturn PasswordEncoderUtils.equals(encodedPassword.toString(), rawPasswordEncoded);\n\t}\n\n\t/**\n\t * Sets the number of iterations for which the calculated hash value should be\n\t * \"stretched\". If this is greater than one, the initial digest is calculated, the\n\t * digest function will be called repeatedly on the result for the additional number\n\t * of iterations.\n\t * @param iterations the number of iterations which will be executed on the hashed\n\t * password/salt value. Defaults to 1.\n\t */\n\tpublic void setIterations(int iterations) {\n\t\tthis.digester.setIterations(iterations);\n\t}\n\n\tprivate String extractSalt(String prefixEncodedPassword) {\n\t\tint start = prefixEncodedPassword.indexOf(PREFIX);\n\t\tif (start != 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tint end = prefixEncodedPassword.indexOf(SUFFIX, start);\n\t\tif (end < 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn prefixEncodedPassword.substring(start, end + 1);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/NoOpPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\n/**\n * This {@link PasswordEncoder} is provided for legacy and testing purposes only and is\n * not considered secure.\n *\n * A password encoder that does nothing. Useful for testing where working with plain text\n * passwords may be preferred.\n *\n * @author Keith Donald\n * @deprecated This PasswordEncoder is not secure. Instead use an adaptive one way\n * function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or SCryptPasswordEncoder.\n * Even better use {@link DelegatingPasswordEncoder} which supports password upgrades.\n * There are no plans to remove this support. It is deprecated to indicate that this is a\n * legacy implementation and using it is considered insecure.\n */\n@Deprecated\npublic final class NoOpPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final PasswordEncoder INSTANCE = new NoOpPasswordEncoder();\n\n\tprivate NoOpPasswordEncoder() {\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\treturn rawPassword.toString();\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\treturn rawPassword.toString().equals(encodedPassword);\n\t}\n\n\t/**\n\t * Get the singleton {@link NoOpPasswordEncoder}.\n\t */\n\tpublic static PasswordEncoder getInstance() {\n\t\treturn INSTANCE;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.lang.Contract;\n\n/**\n * Service interface for encoding passwords.\n *\n * The preferred implementation is {@code BCryptPasswordEncoder}.\n *\n * @author Keith Donald\n * @author Rob Winch\n */\npublic interface PasswordEncoder {\n\n\t/**\n\t * Encode the raw password. Generally, a good encoding algorithm uses an adaptive one\n\t * way function.\n\t * @param rawPassword a password that has not been encoded. The value can be null in\n\t * the event that the user has no password; in which case the result must be null.\n\t * @return A non-null encoded password, unless the rawPassword was null in which case\n\t * the result must be null.\n\t */\n\t@Contract(\"!null -> !null; null -> null\")\n\t@Nullable String encode(@Nullable CharSequence rawPassword);\n\n\t/**\n\t * Verify the encoded password obtained from storage matches the submitted raw\n\t * password after it too is encoded. Returns true if the passwords match, false if\n\t * they do not. The stored password itself is never decoded. Never true if either\n\t * rawPassword or encodedPassword is null or an empty String.\n\t * @param rawPassword the raw password to encode and match.\n\t * @param encodedPassword the encoded password from storage to compare with.\n\t * @return true if the raw password, after encoding, matches the encoded password from\n\t * storage.\n\t */\n\tboolean matches(@Nullable CharSequence rawPassword, @Nullable String encodedPassword);\n\n\t/**\n\t * Returns true if the encoded password should be encoded again for better security,\n\t * else false. The default implementation always returns false.\n\t * @param encodedPassword the encoded password to check. Possibly null if the user did\n\t * not have a password.\n\t * @return true if the encoded password should be encoded again for better security,\n\t * else false. If encodedPassword is null (the user didn't have a password), then\n\t * always false.\n\t */\n\tdefault boolean upgradeEncoding(@Nullable String encodedPassword) {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoderUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.security.MessageDigest;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.codec.Utf8;\n\n/**\n * Utility for constant time comparison to prevent against timing attacks.\n *\n * @author Rob Winch\n */\nfinal class PasswordEncoderUtils {\n\n\tprivate PasswordEncoderUtils() {\n\t}\n\n\t/**\n\t * Constant time comparison to prevent against timing attacks.\n\t * @param expected\n\t * @param actual\n\t * @return\n\t */\n\tstatic boolean equals(String expected, @Nullable String actual) {\n\t\tbyte[] expectedBytes = bytesUtf8(expected);\n\t\tbyte[] actualBytes = bytesUtf8(actual);\n\t\treturn MessageDigest.isEqual(expectedBytes, actualBytes);\n\t}\n\n\tprivate static byte @Nullable [] bytesUtf8(@Nullable String s) {\n\t\t// need to check if Utf8.encode() runs in constant time (probably not).\n\t\t// This may leak length of string.\n\t\treturn (s != null) ? Utf8.encode(s) : null;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.security.GeneralSecurityException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\n\nimport javax.crypto.SecretKeyFactory;\nimport javax.crypto.spec.PBEKeySpec;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.util.EncodingUtils;\n\n/**\n * A {@link PasswordEncoder} implementation that uses PBKDF2 with :\n * <ul>\n * <li>a configurable random salt value length (default is {@value #DEFAULT_SALT_LENGTH}\n * bytes)</li>\n * <li>a configurable number of iterations (default is {@value #DEFAULT_ITERATIONS})</li>\n * <li>a configurable key derivation function (see {@link SecretKeyFactoryAlgorithm})</li>\n * <li>a configurable secret appended to the random salt (default is empty)</li>\n * </ul>\n * The algorithm is invoked on the concatenated bytes of the salt, secret and password.\n *\n * @author Rob Worsnop\n * @author Rob Winch\n * @author Loïc Guibert\n * @since 4.1\n */\npublic class Pbkdf2PasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final int DEFAULT_SALT_LENGTH = 16;\n\n\tprivate static final SecretKeyFactoryAlgorithm DEFAULT_ALGORITHM = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256;\n\n\tprivate static final int DEFAULT_HASH_WIDTH = 256; // SHA-256\n\n\tprivate static final int DEFAULT_ITERATIONS = 310000;\n\n\tprivate final BytesKeyGenerator saltGenerator;\n\n\tprivate final byte[] secret;\n\n\tprivate final int iterations;\n\n\tprivate String algorithm = DEFAULT_ALGORITHM.name();\n\n\tprivate int hashWidth = DEFAULT_HASH_WIDTH;\n\n\t// @formatter:off\n\t/*\n\tThe length of the hash should be derived from the hashing algorithm.\n\n\tFor example:\n\t\tSHA-1 - 160 bits (20 bytes)\n\t\tSHA-256 - 256 bits (32 bytes)\n\t\tSHA-512 - 512 bits (64 bytes)\n\n\tHowever, the original configuration for PBKDF2 was hashWidth=256 and algorithm=SHA-1, which is incorrect.\n\tThe default configuration has been updated to hashWidth=256 and algorithm=SHA-256 (see gh-10506).\n\tIn order to preserve backwards compatibility, the variable 'overrideHashWidth' has been introduced\n\tto indicate usage of the deprecated constructor that honors the hashWidth parameter.\n\t */\n\t// @formatter:on\n\tprivate boolean overrideHashWidth = true;\n\n\tprivate boolean encodeHashAsBase64;\n\n\t/**\n\t * Constructs a PBKDF2 password encoder with a secret value as well as salt length,\n\t * iterations and hash width.\n\t * @param secret the secret\n\t * @param saltLength the salt length (in bytes)\n\t * @param iterations the number of iterations. Users should aim for taking about .5\n\t * seconds on their own system.\n\t * @param hashWidth the size of the hash (in bits)\n\t * @since 5.5\n\t * @deprecated Use\n\t * {@link #Pbkdf2PasswordEncoder(CharSequence, int, int, SecretKeyFactoryAlgorithm)}\n\t * instead\n\t */\n\t@Deprecated\n\tpublic Pbkdf2PasswordEncoder(CharSequence secret, int saltLength, int iterations, int hashWidth) {\n\t\tthis.secret = Utf8.encode(secret);\n\t\tthis.saltGenerator = KeyGenerators.secureRandom(saltLength);\n\t\tthis.iterations = iterations;\n\t\tthis.hashWidth = hashWidth;\n\t\tthis.algorithm = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name();\n\t\tthis.overrideHashWidth = false; // Honor 'hashWidth' to preserve backwards\n\t\t\t\t\t\t\t\t\t\t// compatibility\n\t}\n\n\t/**\n\t * Constructs a PBKDF2 password encoder with a secret value as well as salt length,\n\t * iterations and algorithm.\n\t * @param secret the secret\n\t * @param saltLength the salt length (in bytes)\n\t * @param iterations the number of iterations. Users should aim for taking about .5\n\t * seconds on their own system.\n\t * @param secretKeyFactoryAlgorithm the algorithm to use\n\t * @since 5.8\n\t */\n\tpublic Pbkdf2PasswordEncoder(CharSequence secret, int saltLength, int iterations,\n\t\t\tSecretKeyFactoryAlgorithm secretKeyFactoryAlgorithm) {\n\t\tthis.secret = Utf8.encode(secret);\n\t\tthis.saltGenerator = KeyGenerators.secureRandom(saltLength);\n\t\tthis.iterations = iterations;\n\t\tsetAlgorithm(secretKeyFactoryAlgorithm);\n\t}\n\n\t/**\n\t * Constructs a PBKDF2 password encoder with no additional secret value. There will be\n\t * a salt length of 8 bytes, 185,000 iterations, SHA-1 algorithm and a hash length of\n\t * 256 bits. The default is based upon aiming for .5 seconds to validate the password\n\t * when this class was added. Users should tune password verification to their own\n\t * systems.\n\t * @return the {@link Pbkdf2PasswordEncoder}\n\t * @since 5.8\n\t * @deprecated Use {@link #defaultsForSpringSecurity_v5_8()} instead\n\t */\n\t@Deprecated\n\tpublic static Pbkdf2PasswordEncoder defaultsForSpringSecurity_v5_5() {\n\t\treturn new Pbkdf2PasswordEncoder(\"\", 8, 185000, 256);\n\t}\n\n\t/**\n\t * Constructs a PBKDF2 password encoder with no additional secret value. There will be\n\t * a salt length of 16 bytes, 310,000 iterations, SHA-256 algorithm and a hash length\n\t * of 256 bits. The default is based upon aiming for .5 seconds to validate the\n\t * password when this class was added. Users should tune password verification to\n\t * their own systems.\n\t * @return the {@link Pbkdf2PasswordEncoder}\n\t * @since 5.8\n\t */\n\tpublic static Pbkdf2PasswordEncoder defaultsForSpringSecurity_v5_8() {\n\t\treturn new Pbkdf2PasswordEncoder(\"\", DEFAULT_SALT_LENGTH, DEFAULT_ITERATIONS, DEFAULT_ALGORITHM);\n\t}\n\n\t/**\n\t * Sets the algorithm to use. See <a href=\n\t * \"https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory\">SecretKeyFactory\n\t * Algorithms</a>\n\t * @param secretKeyFactoryAlgorithm the algorithm to use (i.e.\n\t * {@code SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1},\n\t * {@code SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256},\n\t * {@code SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA512})\n\t * @since 5.0\n\t */\n\tpublic void setAlgorithm(SecretKeyFactoryAlgorithm secretKeyFactoryAlgorithm) {\n\t\tif (secretKeyFactoryAlgorithm == null) {\n\t\t\tthrow new IllegalArgumentException(\"secretKeyFactoryAlgorithm cannot be null\");\n\t\t}\n\t\tString algorithmName = secretKeyFactoryAlgorithm.name();\n\t\ttry {\n\t\t\tSecretKeyFactory.getInstance(algorithmName);\n\t\t\tthis.algorithm = algorithmName;\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid algorithm '\" + algorithmName + \"'.\", ex);\n\t\t}\n\t\tif (this.overrideHashWidth) {\n\t\t\tthis.hashWidth = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.equals(secretKeyFactoryAlgorithm) ? 160\n\t\t\t\t\t: SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256.equals(secretKeyFactoryAlgorithm) ? 256 : 512;\n\t\t}\n\t}\n\n\t/**\n\t * Sets if the resulting hash should be encoded as Base64. The default is false which\n\t * means it will be encoded in Hex.\n\t * @param encodeHashAsBase64 true if encode as Base64, false if should use Hex\n\t * (default)\n\t */\n\tpublic void setEncodeHashAsBase64(boolean encodeHashAsBase64) {\n\t\tthis.encodeHashAsBase64 = encodeHashAsBase64;\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tbyte[] salt = this.saltGenerator.generateKey();\n\t\tbyte[] encoded = encodedNonNullPassword(rawPassword, salt);\n\t\treturn encodedNonNullPassword(encoded);\n\t}\n\n\tprivate String encodedNonNullPassword(byte[] bytes) {\n\t\tif (this.encodeHashAsBase64) {\n\t\t\treturn Base64.getEncoder().encodeToString(bytes);\n\t\t}\n\t\treturn String.valueOf(Hex.encode(bytes));\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tbyte[] digested = decode(encodedPassword);\n\t\tbyte[] salt = EncodingUtils.subArray(digested, 0, this.saltGenerator.getKeyLength());\n\t\treturn MessageDigest.isEqual(digested, encodedNonNullPassword(rawPassword, salt));\n\t}\n\n\tprivate byte[] decode(String encodedBytes) {\n\t\tif (this.encodeHashAsBase64) {\n\t\t\treturn Base64.getDecoder().decode(encodedBytes);\n\t\t}\n\t\treturn Hex.decode(encodedBytes);\n\t}\n\n\tprivate byte[] encodedNonNullPassword(CharSequence rawPassword, byte[] salt) {\n\t\ttry {\n\t\t\tPBEKeySpec spec = new PBEKeySpec(rawPassword.toString().toCharArray(),\n\t\t\t\t\tEncodingUtils.concatenate(salt, this.secret), this.iterations, this.hashWidth);\n\t\t\tSecretKeyFactory skf = SecretKeyFactory.getInstance(this.algorithm);\n\t\t\treturn EncodingUtils.concatenate(salt, skf.generateSecret(spec).getEncoded());\n\t\t}\n\t\tcatch (GeneralSecurityException ex) {\n\t\t\tthrow new IllegalStateException(\"Could not create hash\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * The Algorithm used for creating the {@link SecretKeyFactory}\n\t *\n\t * @since 5.0\n\t */\n\tpublic enum SecretKeyFactoryAlgorithm {\n\n\t\tPBKDF2WithHmacSHA1, PBKDF2WithHmacSHA256, PBKDF2WithHmacSHA512\n\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/StandardPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.security.MessageDigest;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.util.EncodingUtils;\n\n/**\n * This {@link PasswordEncoder} is provided for legacy purposes only and is not considered\n * secure.\n *\n * A standard {@code PasswordEncoder} implementation that uses SHA-256 hashing with 1024\n * iterations and a random 8-byte random salt value. It uses an additional system-wide\n * secret value to provide additional protection.\n * <p>\n * The digest algorithm is invoked on the concatenated bytes of the salt, secret and\n * password.\n * <p>\n * If you are developing a new system,\n * {@link org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder} is a better\n * choice both in terms of security and interoperability with other languages.\n *\n * @author Keith Donald\n * @author Luke Taylor\n * @deprecated Digest based password encoding is not considered secure. Instead use an\n * adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or\n * SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports\n * password upgrades. There are no plans to remove this support. It is deprecated to\n * indicate that this is a legacy implementation and using it is considered insecure.\n */\n@Deprecated\npublic final class StandardPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final int DEFAULT_ITERATIONS = 1024;\n\n\tprivate final Digester digester;\n\n\tprivate final byte[] secret;\n\n\tprivate final BytesKeyGenerator saltGenerator;\n\n\t/**\n\t * Constructs a standard password encoder with no additional secret value.\n\t */\n\tpublic StandardPasswordEncoder() {\n\t\tthis(\"\");\n\t}\n\n\t/**\n\t * Constructs a standard password encoder with a secret value which is also included\n\t * in the password hash.\n\t * @param secret the secret key used in the encoding process (should not be shared)\n\t */\n\tpublic StandardPasswordEncoder(CharSequence secret) {\n\t\tthis(\"SHA-256\", secret);\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\treturn encodedNonNullPassword(rawPassword, this.saltGenerator.generateKey());\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tbyte[] digested = decode(encodedPassword);\n\t\tbyte[] salt = EncodingUtils.subArray(digested, 0, this.saltGenerator.getKeyLength());\n\t\treturn MessageDigest.isEqual(digested, digest(rawPassword, salt));\n\t}\n\n\tprivate StandardPasswordEncoder(String algorithm, CharSequence secret) {\n\t\tthis.digester = new Digester(algorithm, DEFAULT_ITERATIONS);\n\t\tthis.secret = Utf8.encode(secret);\n\t\tthis.saltGenerator = KeyGenerators.secureRandom();\n\t}\n\n\tprivate String encodedNonNullPassword(CharSequence rawPassword, byte[] salt) {\n\t\tbyte[] digest = digest(rawPassword, salt);\n\t\treturn new String(Hex.encode(digest));\n\t}\n\n\tprivate byte[] digest(CharSequence rawPassword, byte[] salt) {\n\t\tbyte[] digest = this.digester.digest(EncodingUtils.concatenate(salt, this.secret, Utf8.encode(rawPassword)));\n\t\treturn EncodingUtils.concatenate(salt, digest);\n\t}\n\n\tprivate byte[] decode(CharSequence encodedPassword) {\n\t\treturn Hex.decode(encodedPassword);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.password;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password4j/Argon2Password4jPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.Argon2Function;\n\n/**\n * Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}\n * that uses the Password4j library with Argon2 hashing algorithm.\n *\n * <p>\n * Argon2 is the winner of the Password Hashing Competition (2015) and is recommended for\n * new applications. It provides excellent resistance against GPU-based attacks and\n * includes built-in salt generation. This implementation leverages Password4j's Argon2\n * support which properly includes the salt in the output hash.\n * </p>\n *\n * <p>\n * This implementation is thread-safe and can be shared across multiple threads.\n * </p>\n *\n * <p>\n * <strong>Usage Examples:</strong>\n * </p>\n * <pre>{@code\n * // Using default Argon2 settings (recommended)\n * PasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n *\n * // Using custom Argon2 configuration\n * PasswordEncoder customEncoder = new Argon2Password4jPasswordEncoder(\n *     Argon2Function.getInstance(65536, 3, 4, 32, Argon2.ID));\n * }</pre>\n *\n * @author Mehrdad Bozorgmehr\n * @since 7.0\n * @see Argon2Function\n * @see AlgorithmFinder#getArgon2Instance()\n */\npublic class Argon2Password4jPasswordEncoder extends Password4jPasswordEncoder {\n\n\t/**\n\t * Constructs an Argon2 password encoder using the default Argon2 configuration from\n\t * Password4j's AlgorithmFinder.\n\t */\n\tpublic Argon2Password4jPasswordEncoder() {\n\t\tsuper(AlgorithmFinder.getArgon2Instance());\n\t}\n\n\t/**\n\t * Constructs an Argon2 password encoder with a custom Argon2 function.\n\t * @param argon2Function the Argon2 function to use for encoding passwords, must not\n\t * be null\n\t * @throws IllegalArgumentException if argon2Function is null\n\t */\n\tpublic Argon2Password4jPasswordEncoder(Argon2Function argon2Function) {\n\t\tsuper(argon2Function);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password4j/BalloonHashingPassword4jPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport java.security.SecureRandom;\nimport java.util.Base64;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.BalloonHashingFunction;\nimport com.password4j.Hash;\nimport com.password4j.Password;\n\nimport org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}\n * that uses the Password4j library with Balloon hashing algorithm.\n *\n * <p>\n * Balloon hashing is a memory-hard password hashing algorithm designed to be resistant to\n * both time-memory trade-off attacks and side-channel attacks. This implementation\n * handles the salt management explicitly since Password4j's Balloon hashing\n * implementation does not include the salt in the output hash.\n * </p>\n *\n * <p>\n * The encoded password format is: {salt}:{hash} where both salt and hash are Base64\n * encoded.\n * </p>\n *\n * <p>\n * This implementation is thread-safe and can be shared across multiple threads.\n * </p>\n *\n * <p>\n * <strong>Usage Examples:</strong>\n * </p>\n * <pre>{@code\n * // Using default Balloon hashing settings (recommended)\n * PasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n *\n * // Using custom Balloon hashing function\n * PasswordEncoder customEncoder = new BalloonHashingPassword4jPasswordEncoder(\n *     BalloonHashingFunction.getInstance(1024, 3, 4, \"SHA-256\"));\n * }</pre>\n *\n * @author Mehrdad Bozorgmehr\n * @since 7.0\n * @see BalloonHashingFunction\n * @see AlgorithmFinder#getBalloonHashingInstance()\n */\npublic class BalloonHashingPassword4jPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final String DELIMITER = \":\";\n\n\tprivate static final int DEFAULT_SALT_LENGTH = 32;\n\n\tprivate final BalloonHashingFunction balloonHashingFunction;\n\n\tprivate final SecureRandom secureRandom;\n\n\tprivate final int saltLength;\n\n\t/**\n\t * Constructs a Balloon hashing password encoder using the default Balloon hashing\n\t * configuration from Password4j's AlgorithmFinder.\n\t */\n\tpublic BalloonHashingPassword4jPasswordEncoder() {\n\t\tthis(AlgorithmFinder.getBalloonHashingInstance());\n\t}\n\n\t/**\n\t * Constructs a Balloon hashing password encoder with a custom Balloon hashing\n\t * function.\n\t * @param balloonHashingFunction the Balloon hashing function to use for encoding\n\t * passwords, must not be null\n\t * @throws IllegalArgumentException if balloonHashingFunction is null\n\t */\n\tpublic BalloonHashingPassword4jPasswordEncoder(BalloonHashingFunction balloonHashingFunction) {\n\t\tthis(balloonHashingFunction, DEFAULT_SALT_LENGTH);\n\t}\n\n\t/**\n\t * Constructs a Balloon hashing password encoder with a custom Balloon hashing\n\t * function and salt length.\n\t * @param balloonHashingFunction the Balloon hashing function to use for encoding\n\t * passwords, must not be null\n\t * @param saltLength the length of the salt in bytes, must be positive\n\t * @throws IllegalArgumentException if balloonHashingFunction is null or saltLength is\n\t * not positive\n\t */\n\tpublic BalloonHashingPassword4jPasswordEncoder(BalloonHashingFunction balloonHashingFunction, int saltLength) {\n\t\tAssert.notNull(balloonHashingFunction, \"balloonHashingFunction cannot be null\");\n\t\tAssert.isTrue(saltLength > 0, \"saltLength must be positive\");\n\t\tthis.balloonHashingFunction = balloonHashingFunction;\n\t\tthis.saltLength = saltLength;\n\t\tthis.secureRandom = new SecureRandom();\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tbyte[] salt = new byte[this.saltLength];\n\t\tthis.secureRandom.nextBytes(salt);\n\n\t\tHash hash = Password.hash(rawPassword).addSalt(salt).with(this.balloonHashingFunction);\n\t\tString encodedSalt = Base64.getEncoder().encodeToString(salt);\n\t\tString encodedHash = hash.getResult();\n\n\t\treturn encodedSalt + DELIMITER + encodedHash;\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tif (!encodedPassword.contains(DELIMITER)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tString[] parts = encodedPassword.split(DELIMITER, 2);\n\t\tif (parts.length != 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tbyte[] salt = Base64.getDecoder().decode(parts[0]);\n\t\t\tString expectedHash = parts[1];\n\n\t\t\tHash hash = Password.hash(rawPassword).addSalt(salt).with(this.balloonHashingFunction);\n\t\t\treturn expectedHash.equals(hash.getResult());\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\t// Invalid Base64 encoding\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tprotected boolean upgradeEncodingNonNull(String encodedPassword) {\n\t\t// For now, we'll return false to maintain existing behavior\n\t\t// This could be enhanced in the future to check if the encoding parameters\n\t\t// match the current configuration\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password4j/BcryptPassword4jPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.BcryptFunction;\n\n/**\n * Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}\n * that uses the Password4j library with BCrypt hashing algorithm.\n *\n * <p>\n * BCrypt is a well-established password hashing algorithm that includes built-in salt\n * generation and is resistant to rainbow table attacks. This implementation leverages\n * Password4j's BCrypt support which properly includes the salt in the output hash.\n * </p>\n *\n * <p>\n * This implementation is thread-safe and can be shared across multiple threads.\n * </p>\n *\n * <p>\n * <strong>Usage Examples:</strong>\n * </p>\n * <pre>{@code\n * // Using default BCrypt settings (recommended)\n * PasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n *\n * // Using custom round count\n * PasswordEncoder customEncoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(12));\n * }</pre>\n *\n * @author Mehrdad Bozorgmehr\n * @since 7.0\n * @see BcryptFunction\n * @see AlgorithmFinder#getBcryptInstance()\n */\npublic class BcryptPassword4jPasswordEncoder extends Password4jPasswordEncoder {\n\n\t/**\n\t * Constructs a BCrypt password encoder using the default BCrypt configuration from\n\t * Password4j's AlgorithmFinder.\n\t */\n\tpublic BcryptPassword4jPasswordEncoder() {\n\t\tsuper(AlgorithmFinder.getBcryptInstance());\n\t}\n\n\t/**\n\t * Constructs a BCrypt password encoder with a custom BCrypt function.\n\t * @param bcryptFunction the BCrypt function to use for encoding passwords, must not\n\t * be null\n\t * @throws IllegalArgumentException if bcryptFunction is null\n\t */\n\tpublic BcryptPassword4jPasswordEncoder(BcryptFunction bcryptFunction) {\n\t\tsuper(bcryptFunction);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password4j/Password4jPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.Hash;\nimport com.password4j.HashingFunction;\nimport com.password4j.Password;\n\nimport org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;\nimport org.springframework.util.Assert;\n\n/**\n * Abstract base class for Password4j-based password encoders. This class provides the\n * common functionality for password encoding and verification using the Password4j\n * library.\n *\n * <p>\n * This class is package-private and should not be used directly. Instead, use the\n * specific public subclasses that support verified hashing algorithms such as BCrypt,\n * Argon2, and SCrypt implementations.\n * </p>\n *\n * <p>\n * This implementation is thread-safe and can be shared across multiple threads.\n * </p>\n *\n * @author Mehrdad Bozorgmehr\n * @since 7.0\n */\nabstract class Password4jPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate final HashingFunction hashingFunction;\n\n\t/**\n\t * Constructs a Password4j password encoder with the specified hashing function. This\n\t * constructor is package-private and intended for use by subclasses only.\n\t * @param hashingFunction the hashing function to use for encoding passwords, must not\n\t * be null\n\t * @throws IllegalArgumentException if hashingFunction is null\n\t */\n\tPassword4jPasswordEncoder(HashingFunction hashingFunction) {\n\t\tAssert.notNull(hashingFunction, \"hashingFunction cannot be null\");\n\t\tthis.hashingFunction = hashingFunction;\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tHash hash = Password.hash(rawPassword).with(this.hashingFunction);\n\t\treturn hash.getResult();\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\treturn Password.check(rawPassword, encodedPassword).with(this.hashingFunction);\n\t}\n\n\t@Override\n\tprotected boolean upgradeEncodingNonNull(String encodedPassword) {\n\t\t// Password4j handles upgrade detection internally for most algorithms\n\t\t// For now, we'll return false to maintain existing behavior\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password4j/Pbkdf2Password4jPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport java.security.SecureRandom;\nimport java.util.Base64;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.Hash;\nimport com.password4j.PBKDF2Function;\nimport com.password4j.Password;\n\nimport org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}\n * that uses the Password4j library with PBKDF2 hashing algorithm.\n *\n * <p>\n * PBKDF2 is a key derivation function designed to be computationally expensive to thwart\n * dictionary and brute force attacks. This implementation handles the salt management\n * explicitly since Password4j's PBKDF2 implementation does not include the salt in the\n * output hash.\n * </p>\n *\n * <p>\n * The encoded password format is: {salt}:{hash} where both salt and hash are Base64\n * encoded.\n * </p>\n *\n * <p>\n * This implementation is thread-safe and can be shared across multiple threads.\n * </p>\n *\n * <p>\n * <strong>Usage Examples:</strong>\n * </p>\n * <pre>{@code\n * // Using default PBKDF2 settings (recommended)\n * PasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n *\n * // Using custom PBKDF2 function\n * PasswordEncoder customEncoder = new Pbkdf2Password4jPasswordEncoder(\n *     PBKDF2Function.getInstance(Algorithm.HMAC_SHA256, 100000, 256));\n * }</pre>\n *\n * @author Mehrdad Bozorgmehr\n * @since 7.0\n * @see PBKDF2Function\n * @see AlgorithmFinder#getPBKDF2Instance()\n */\npublic class Pbkdf2Password4jPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final String DELIMITER = \":\";\n\n\tprivate static final int DEFAULT_SALT_LENGTH = 32;\n\n\tprivate final PBKDF2Function pbkdf2Function;\n\n\tprivate final SecureRandom secureRandom;\n\n\tprivate final int saltLength;\n\n\t/**\n\t * Constructs a PBKDF2 password encoder using the default PBKDF2 configuration from\n\t * Password4j's AlgorithmFinder.\n\t */\n\tpublic Pbkdf2Password4jPasswordEncoder() {\n\t\tthis(AlgorithmFinder.getPBKDF2Instance());\n\t}\n\n\t/**\n\t * Constructs a PBKDF2 password encoder with a custom PBKDF2 function.\n\t * @param pbkdf2Function the PBKDF2 function to use for encoding passwords, must not\n\t * be null\n\t * @throws IllegalArgumentException if pbkdf2Function is null\n\t */\n\tpublic Pbkdf2Password4jPasswordEncoder(PBKDF2Function pbkdf2Function) {\n\t\tthis(pbkdf2Function, DEFAULT_SALT_LENGTH);\n\t}\n\n\t/**\n\t * Constructs a PBKDF2 password encoder with a custom PBKDF2 function and salt length.\n\t * @param pbkdf2Function the PBKDF2 function to use for encoding passwords, must not\n\t * be null\n\t * @param saltLength the length of the salt in bytes, must be positive\n\t * @throws IllegalArgumentException if pbkdf2Function is null or saltLength is not\n\t * positive\n\t */\n\tpublic Pbkdf2Password4jPasswordEncoder(PBKDF2Function pbkdf2Function, int saltLength) {\n\t\tAssert.notNull(pbkdf2Function, \"pbkdf2Function cannot be null\");\n\t\tAssert.isTrue(saltLength > 0, \"saltLength must be positive\");\n\t\tthis.pbkdf2Function = pbkdf2Function;\n\t\tthis.saltLength = saltLength;\n\t\tthis.secureRandom = new SecureRandom();\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\tbyte[] salt = new byte[this.saltLength];\n\t\tthis.secureRandom.nextBytes(salt);\n\n\t\tHash hash = Password.hash(rawPassword).addSalt(salt).with(this.pbkdf2Function);\n\t\tString encodedSalt = Base64.getEncoder().encodeToString(salt);\n\t\tString encodedHash = hash.getResult();\n\n\t\treturn encodedSalt + DELIMITER + encodedHash;\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\tif (!encodedPassword.contains(DELIMITER)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tString[] parts = encodedPassword.split(DELIMITER, 2);\n\t\tif (parts.length != 2) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tbyte[] salt = Base64.getDecoder().decode(parts[0]);\n\t\t\tString expectedHash = parts[1];\n\n\t\t\tHash hash = Password.hash(rawPassword).addSalt(salt).with(this.pbkdf2Function);\n\t\t\treturn expectedHash.equals(hash.getResult());\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\t// Invalid Base64 encoding\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tprotected boolean upgradeEncodingNonNull(String encodedPassword) {\n\t\t// For now, we'll return false to maintain existing behavior\n\t\t// This could be enhanced in the future to check if the encoding parameters\n\t\t// match the current configuration\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password4j/ScryptPassword4jPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.ScryptFunction;\n\n/**\n * Implementation of {@link org.springframework.security.crypto.password.PasswordEncoder}\n * that uses the Password4j library with SCrypt hashing algorithm.\n *\n * <p>\n * SCrypt is a memory-hard password hashing algorithm designed to be resistant to hardware\n * brute-force attacks. It includes built-in salt generation and is particularly effective\n * against ASIC and GPU-based attacks. This implementation leverages Password4j's SCrypt\n * support which properly includes the salt in the output hash.\n * </p>\n *\n * <p>\n * This implementation is thread-safe and can be shared across multiple threads.\n * </p>\n *\n * <p>\n * <strong>Usage Examples:</strong>\n * </p>\n * <pre>{@code\n * // Using default SCrypt settings (recommended)\n * PasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n *\n * // Using custom SCrypt configuration\n * PasswordEncoder customEncoder = new ScryptPassword4jPasswordEncoder(\n *     ScryptFunction.getInstance(32768, 8, 1, 32));\n * }</pre>\n *\n * @author Mehrdad Bozorgmehr\n * @since 7.0\n * @see ScryptFunction\n * @see AlgorithmFinder#getScryptInstance()\n */\npublic class ScryptPassword4jPasswordEncoder extends Password4jPasswordEncoder {\n\n\t/**\n\t * Constructs an SCrypt password encoder using the default SCrypt configuration from\n\t * Password4j's AlgorithmFinder.\n\t */\n\tpublic ScryptPassword4jPasswordEncoder() {\n\t\tsuper(AlgorithmFinder.getScryptInstance());\n\t}\n\n\t/**\n\t * Constructs an SCrypt password encoder with a custom SCrypt function.\n\t * @param scryptFunction the SCrypt function to use for encoding passwords, must not\n\t * be null\n\t * @throws IllegalArgumentException if scryptFunction is null\n\t */\n\tpublic ScryptPassword4jPasswordEncoder(ScryptFunction scryptFunction) {\n\t\tsuper(scryptFunction);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/password4j/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.password4j;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.scrypt;\n\nimport java.security.MessageDigest;\nimport java.util.Base64;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.bouncycastle.crypto.generators.SCrypt;\n\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;\n\n/**\n * <p>\n * Implementation of PasswordEncoder that uses the SCrypt hashing function. Clients can\n * optionally supply a cpu cost parameter, a memory cost parameter and a parallelization\n * parameter.\n * </p>\n *\n * <p>\n * A few <a href=\n * \"http://bouncy-castle.1462172.n4.nabble.com/Java-Bouncy-Castle-scrypt-implementation-td4656832.html\">\n * warnings</a>:\n * </p>\n *\n * <ul>\n * <li>The currently implementation uses Bouncy castle which does not exploit\n * parallelism/optimizations that password crackers will, so there is an unnecessary\n * asymmetry between attacker and defender.</li>\n * <li>Scrypt is based on Salsa20 which performs poorly in Java (on par with AES) but\n * performs awesome (~4-5x faster) on SIMD capable platforms</li>\n * <li>While there are some that would disagree, consider reading -\n * <a href=\"https://blog.ircmaxell.com/2014/03/why-i-dont-recommend-scrypt.html\"> Why I\n * Don't Recommend Scrypt</a> (for password storage)</li>\n * </ul>\n *\n * @author Shazin Sadakath\n * @author Rob Winch\n *\n */\npublic class SCryptPasswordEncoder extends AbstractValidatingPasswordEncoder {\n\n\tprivate static final int DEFAULT_CPU_COST = 65536;\n\n\tprivate static final int DEFAULT_MEMORY_COST = 8;\n\n\tprivate static final int DEFAULT_PARALLELISM = 1;\n\n\tprivate static final int DEFAULT_KEY_LENGTH = 32;\n\n\tprivate static final int DEFAULT_SALT_LENGTH = 16;\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final int cpuCost;\n\n\tprivate final int memoryCost;\n\n\tprivate final int parallelization;\n\n\tprivate final int keyLength;\n\n\tprivate final BytesKeyGenerator saltGenerator;\n\n\t/**\n\t * Constructs a SCrypt password encoder with the provided parameters.\n\t * @param cpuCost cpu cost of the algorithm (as defined in scrypt this is N). must be\n\t * power of 2 greater than 1. Default is currently 65,536 or 2^16)\n\t * @param memoryCost memory cost of the algorithm (as defined in scrypt this is r)\n\t * Default is currently 8.\n\t * @param parallelization the parallelization of the algorithm (as defined in scrypt\n\t * this is p) Default is currently 1. Note that the implementation does not currently\n\t * take advantage of parallelization.\n\t * @param keyLength key length for the algorithm (as defined in scrypt this is dkLen).\n\t * The default is currently 32.\n\t * @param saltLength salt length (as defined in scrypt this is the length of S). The\n\t * default is currently 16.\n\t */\n\tpublic SCryptPasswordEncoder(int cpuCost, int memoryCost, int parallelization, int keyLength, int saltLength) {\n\t\tif (cpuCost <= 1) {\n\t\t\tthrow new IllegalArgumentException(\"Cpu cost parameter must be > 1.\");\n\t\t}\n\t\tif (memoryCost == 1 && cpuCost > 65536) {\n\t\t\tthrow new IllegalArgumentException(\"Cpu cost parameter must be > 1 and < 65536.\");\n\t\t}\n\t\tif (memoryCost < 1) {\n\t\t\tthrow new IllegalArgumentException(\"Memory cost must be >= 1.\");\n\t\t}\n\t\tint maxParallel = Integer.MAX_VALUE / (128 * memoryCost * 8);\n\t\tif (parallelization < 1 || parallelization > maxParallel) {\n\t\t\tthrow new IllegalArgumentException(\"Parallelisation parameter p must be >= 1 and <= \" + maxParallel\n\t\t\t\t\t+ \" (based on block size r of \" + memoryCost + \")\");\n\t\t}\n\t\tif (keyLength < 1 || keyLength > Integer.MAX_VALUE) {\n\t\t\tthrow new IllegalArgumentException(\"Key length must be >= 1 and <= \" + Integer.MAX_VALUE);\n\t\t}\n\t\tif (saltLength < 1 || saltLength > Integer.MAX_VALUE) {\n\t\t\tthrow new IllegalArgumentException(\"Salt length must be >= 1 and <= \" + Integer.MAX_VALUE);\n\t\t}\n\t\tthis.cpuCost = cpuCost;\n\t\tthis.memoryCost = memoryCost;\n\t\tthis.parallelization = parallelization;\n\t\tthis.keyLength = keyLength;\n\t\tthis.saltGenerator = KeyGenerators.secureRandom(saltLength);\n\t}\n\n\t/**\n\t * Constructs a SCrypt password encoder with cpu cost of 16,384, memory cost of 8,\n\t * parallelization of 1, a key length of 32 and a salt length of 64 bytes.\n\t * @return the {@link SCryptPasswordEncoder}\n\t * @since 5.8\n\t * @deprecated Use {@link #defaultsForSpringSecurity_v5_8()} instead\n\t */\n\t@Deprecated\n\tpublic static SCryptPasswordEncoder defaultsForSpringSecurity_v4_1() {\n\t\treturn new SCryptPasswordEncoder(16384, 8, 1, 32, 64);\n\t}\n\n\t/**\n\t * Constructs a SCrypt password encoder with cpu cost of 65,536, memory cost of 8,\n\t * parallelization of 1, a key length of 32 and a salt length of 16 bytes.\n\t * @return the {@link SCryptPasswordEncoder}\n\t * @since 5.8\n\t */\n\tpublic static SCryptPasswordEncoder defaultsForSpringSecurity_v5_8() {\n\t\treturn new SCryptPasswordEncoder(DEFAULT_CPU_COST, DEFAULT_MEMORY_COST, DEFAULT_PARALLELISM, DEFAULT_KEY_LENGTH,\n\t\t\t\tDEFAULT_SALT_LENGTH);\n\t}\n\n\t@Override\n\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\treturn digest(rawPassword, this.saltGenerator.generateKey());\n\t}\n\n\t@Override\n\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\treturn decodeAndCheckMatches(rawPassword, encodedPassword);\n\t}\n\n\t@Override\n\tprotected boolean upgradeEncodingNonNull(String encodedPassword) {\n\t\tString[] parts = encodedPassword.split(\"\\\\$\");\n\t\tif (parts.length != 4) {\n\t\t\tthrow new IllegalArgumentException(\"Encoded password does not look like SCrypt: \" + encodedPassword);\n\t\t}\n\t\tlong params = Long.parseLong(parts[1], 16);\n\t\tint cpuCost = (int) Math.pow(2, params >> 16 & 0xffff);\n\t\tint memoryCost = (int) params >> 8 & 0xff;\n\t\tint parallelization = (int) params & 0xff;\n\t\treturn cpuCost < this.cpuCost || memoryCost < this.memoryCost || parallelization < this.parallelization;\n\t}\n\n\tprivate boolean decodeAndCheckMatches(CharSequence rawPassword, String encodedPassword) {\n\t\tString[] parts = encodedPassword.split(\"\\\\$\");\n\t\tif (parts.length != 4) {\n\t\t\treturn false;\n\t\t}\n\t\tlong params = Long.parseLong(parts[1], 16);\n\t\tbyte[] salt = decodePart(parts[2]);\n\t\tbyte[] derived = decodePart(parts[3]);\n\t\tint cpuCost = (int) Math.pow(2, params >> 16 & 0xffff);\n\t\tint memoryCost = (int) params >> 8 & 0xff;\n\t\tint parallelization = (int) params & 0xff;\n\t\tbyte[] generated = SCrypt.generate(Utf8.encode(rawPassword), salt, cpuCost, memoryCost, parallelization,\n\t\t\t\tthis.keyLength);\n\t\treturn MessageDigest.isEqual(derived, generated);\n\t}\n\n\tprivate String digest(CharSequence rawPassword, byte[] salt) {\n\t\tbyte[] derived = SCrypt.generate(Utf8.encode(rawPassword), salt, this.cpuCost, this.memoryCost,\n\t\t\t\tthis.parallelization, this.keyLength);\n\t\tString params = Long.toString(\n\t\t\t\t((int) (Math.log(this.cpuCost) / Math.log(2)) << 16L) | this.memoryCost << 8 | this.parallelization,\n\t\t\t\t16);\n\t\tStringBuilder sb = new StringBuilder((salt.length + derived.length) * 2);\n\t\tsb.append(\"$\").append(params).append('$');\n\t\tsb.append(encodePart(salt)).append('$');\n\t\tsb.append(encodePart(derived));\n\t\treturn sb.toString();\n\t}\n\n\tprivate byte[] decodePart(String part) {\n\t\treturn Base64.getDecoder().decode(Utf8.encode(part));\n\t}\n\n\tprivate String encodePart(byte[] part) {\n\t\treturn Utf8.decode(Base64.getEncoder().encode(part));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/scrypt/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.scrypt;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/util/EncodingUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.util;\n\n/**\n * Static helper for encoding data.\n * <p>\n * For internal use only.\n *\n * @author Keith Donald\n */\npublic final class EncodingUtils {\n\n\tprivate EncodingUtils() {\n\t}\n\n\t/**\n\t * Combine the individual byte arrays into one array.\n\t */\n\tpublic static byte[] concatenate(byte[]... arrays) {\n\t\tint length = 0;\n\t\tfor (byte[] array : arrays) {\n\t\t\tlength += array.length;\n\t\t}\n\t\tbyte[] newArray = new byte[length];\n\t\tint destPos = 0;\n\t\tfor (byte[] array : arrays) {\n\t\t\tSystem.arraycopy(array, 0, newArray, destPos, array.length);\n\t\t\tdestPos += array.length;\n\t\t}\n\t\treturn newArray;\n\t}\n\n\t/**\n\t * Extract a sub array of bytes out of the byte array.\n\t * @param array the byte array to extract from\n\t * @param beginIndex the beginning index of the sub array, inclusive\n\t * @param endIndex the ending index of the sub array, exclusive\n\t */\n\tpublic static byte[] subArray(byte[] array, int beginIndex, int endIndex) {\n\t\tint length = endIndex - beginIndex;\n\t\tbyte[] subarray = new byte[length];\n\t\tSystem.arraycopy(array, beginIndex, subarray, 0, length);\n\t\treturn subarray;\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/main/java/org/springframework/security/crypto/util/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.crypto.util;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/argon2/Argon2EncodingUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.argon2;\n\nimport java.util.Base64;\n\nimport org.bouncycastle.crypto.params.Argon2Parameters;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Simeon Macke\n */\npublic class Argon2EncodingUtilsTests {\n\n\tprivate final Base64.Decoder decoder = Base64.getDecoder();\n\n\tprivate TestDataEntry testDataEntry1 = new TestDataEntry(\n\t\t\t\"$argon2i$v=19$m=1024,t=3,p=2$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\",\n\t\t\tnew Argon2EncodingUtils.Argon2Hash(this.decoder.decode(\"cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"),\n\t\t\t\t\t(new Argon2Parameters.Builder(Argon2Parameters.ARGON2_i)).withVersion(19)\n\t\t\t\t\t\t.withMemoryAsKB(1024)\n\t\t\t\t\t\t.withIterations(3)\n\t\t\t\t\t\t.withParallelism(2)\n\t\t\t\t\t\t.withSalt(\"cRdFbCw23gz2Mlxk\".getBytes())\n\t\t\t\t\t\t.build()));\n\n\tprivate TestDataEntry testDataEntry2 = new TestDataEntry(\n\t\t\t\"$argon2id$v=19$m=333,t=5,p=2$JDR8N3k1QWx0$+PrEoHOHsWkU9lnsxqnOFrWTVEuOh7ZRIUIbe2yUG8FgTYNCWJfHQI09JAAFKzr2JAvoejEpTMghUt0WsntQYA\",\n\t\t\tnew Argon2EncodingUtils.Argon2Hash(\n\t\t\t\t\tthis.decoder.decode(\n\t\t\t\t\t\t\t\"+PrEoHOHsWkU9lnsxqnOFrWTVEuOh7ZRIUIbe2yUG8FgTYNCWJfHQI09JAAFKzr2JAvoejEpTMghUt0WsntQYA\"),\n\t\t\t\t\t(new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)).withVersion(19)\n\t\t\t\t\t\t.withMemoryAsKB(333)\n\t\t\t\t\t\t.withIterations(5)\n\t\t\t\t\t\t.withParallelism(2)\n\t\t\t\t\t\t.withSalt(\"$4|7y5Alt\".getBytes())\n\t\t\t\t\t\t.build()));\n\n\t@Test\n\tpublic void decodeWhenValidEncodedHashWithIThenDecodeCorrectly() {\n\t\tassertArgon2HashEquals(this.testDataEntry1.decoded, Argon2EncodingUtils.decode(this.testDataEntry1.encoded));\n\t}\n\n\t@Test\n\tpublic void decodeWhenValidEncodedHashWithIDThenDecodeCorrectly() {\n\t\tassertArgon2HashEquals(this.testDataEntry2.decoded, Argon2EncodingUtils.decode(this.testDataEntry2.encoded));\n\t}\n\n\t@Test\n\tpublic void encodeWhenValidArgumentsWithIThenEncodeToCorrectHash() {\n\t\tassertThat(Argon2EncodingUtils.encode(this.testDataEntry1.decoded.getHash(),\n\t\t\t\tthis.testDataEntry1.decoded.getParameters()))\n\t\t\t.isEqualTo(this.testDataEntry1.encoded);\n\t}\n\n\t@Test\n\tpublic void encodeWhenValidArgumentsWithID2ThenEncodeToCorrectHash() {\n\t\tassertThat(Argon2EncodingUtils.encode(this.testDataEntry2.decoded.getHash(),\n\t\t\t\tthis.testDataEntry2.decoded.getParameters()))\n\t\t\t.isEqualTo(this.testDataEntry2.encoded);\n\t}\n\n\t@Test\n\tpublic void encodeWhenNonexistingAlgorithmThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils.encode(new byte[] { 0, 1, 2, 3 },\n\t\t\t\t(new Argon2Parameters.Builder(3)).withVersion(19)\n\t\t\t\t\t.withMemoryAsKB(333)\n\t\t\t\t\t.withIterations(5)\n\t\t\t\t\t.withParallelism(2)\n\t\t\t\t\t.build()));\n\t}\n\n\t@Test\n\tpublic void decodeWhenNotAnArgon2HashThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils.decode(\"notahash\"));\n\t}\n\n\t@Test\n\tpublic void decodeWhenNonexistingAlgorithmThenThrowException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> Argon2EncodingUtils.decode(\n\t\t\t\t\t\"$argon2x$v=19$m=1024,t=3,p=2$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"))\n\t\t\t.withMessageContaining(\"argon2x\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenIllegalVersionParameterThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils\n\t\t\t.decode(\"$argon2i$v=x$m=1024,t=3,p=2$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"));\n\t}\n\n\t@Test\n\tpublic void decodeWhenIllegalMemoryParameterThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils\n\t\t\t.decode(\"$argon2i$v=19$m=x,t=3,p=2$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"));\n\t}\n\n\t@Test\n\tpublic void decodeWhenIllegalIterationsParameterThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils\n\t\t\t.decode(\"$argon2i$v=19$m=1024,t=x,p=2$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"));\n\t}\n\n\t@Test\n\tpublic void decodeWhenIllegalParallelityParameterThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils\n\t\t\t.decode(\"$argon2i$v=19$m=1024,t=3,p=x$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"));\n\t}\n\n\t@Test\n\tpublic void decodeWhenMissingVersionParameterThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils\n\t\t\t.decode(\"$argon2i$m=1024,t=3,p=x$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"));\n\t}\n\n\t@Test\n\tpublic void decodeWhenMissingMemoryParameterThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils\n\t\t\t.decode(\"$argon2i$v=19$t=3,p=2$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"));\n\t}\n\n\t@Test\n\tpublic void decodeWhenMissingIterationsParameterThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils\n\t\t\t.decode(\"$argon2i$v=19$m=1024,p=2$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"));\n\t}\n\n\t@Test\n\tpublic void decodeWhenMissingParallelityParameterThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Argon2EncodingUtils\n\t\t\t.decode(\"$argon2i$v=19$m=1024,t=3$Y1JkRmJDdzIzZ3oyTWx4aw$cGE5Cbd/cx7micVhXVBdH5qTr66JI1iUyuNNVAnErXs\"));\n\t}\n\n\tprivate void assertArgon2HashEquals(Argon2EncodingUtils.Argon2Hash expected,\n\t\t\tArgon2EncodingUtils.Argon2Hash actual) {\n\t\tassertThat(actual.getHash()).isEqualTo(expected.getHash());\n\t\tassertThat(actual.getParameters().getSalt()).isEqualTo(expected.getParameters().getSalt());\n\t\tassertThat(actual.getParameters().getType()).isEqualTo(expected.getParameters().getType());\n\t\tassertThat(actual.getParameters().getVersion()).isEqualTo(expected.getParameters().getVersion());\n\t\tassertThat(actual.getParameters().getMemory()).isEqualTo(expected.getParameters().getMemory());\n\t\tassertThat(actual.getParameters().getIterations()).isEqualTo(expected.getParameters().getIterations());\n\t\tassertThat(actual.getParameters().getLanes()).isEqualTo(expected.getParameters().getLanes());\n\t}\n\n\tprivate static class TestDataEntry {\n\n\t\tString encoded;\n\n\t\tArgon2EncodingUtils.Argon2Hash decoded;\n\n\t\tTestDataEntry(String encoded, Argon2EncodingUtils.Argon2Hash decoded) {\n\t\t\tthis.encoded = encoded;\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.argon2;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.password.AbstractPasswordEncoderValidationTests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Simeon Macke\n */\n@ExtendWith(MockitoExtension.class)\npublic class Argon2PasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@Mock\n\tprivate BytesKeyGenerator keyGeneratorMock;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tsetEncoder(Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordDoesNotEqualPassword() {\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(result).isNotEqualTo(\"password\");\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenEqualPasswordThenMatches() {\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenEqualWithUnicodeThenMatches() {\n\t\tString result = getEncoder().encode(\"passw\\u9292rd\");\n\t\tassertThat(getEncoder().matches(\"pass\\u9292\\u9292rd\", result)).isFalse();\n\t\tassertThat(getEncoder().matches(\"passw\\u9292rd\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenNotEqualThenNotMatches() {\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"bogus\", result)).isFalse();\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenEqualPasswordWithCustomParamsThenMatches() {\n\t\tsetEncoder(new Argon2PasswordEncoder(20, 64, 4, 256, 4));\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenRanTwiceThenResultsNotEqual() {\n\t\tString password = \"secret\";\n\t\tassertThat(getEncoder().encode(password)).isNotEqualTo(getEncoder().encode(password));\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenRanTwiceWithCustomParamsThenNotEquals() {\n\t\tsetEncoder(new Argon2PasswordEncoder(20, 64, 4, 256, 4));\n\t\tString password = \"secret\";\n\t\tassertThat(getEncoder().encode(password)).isNotEqualTo(getEncoder().encode(password));\n\t}\n\n\t@Test\n\tpublic void matchesWhenGeneratedWithDifferentEncoderThenTrue() {\n\t\tArgon2PasswordEncoder oldEncoder = new Argon2PasswordEncoder(20, 64, 4, 256, 4);\n\t\tArgon2PasswordEncoder newEncoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2();\n\t\tString password = \"secret\";\n\t\tString oldEncodedPassword = oldEncoder.encode(password);\n\t\tassertThat(newEncoder.matches(password, oldEncodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenEncodedPassIsNullThenFalse() {\n\t\tassertThat(getEncoder().matches(\"password\", null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenEncodedPassIsEmptyThenFalse() {\n\t\tassertThat(getEncoder().matches(\"password\", \"\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenEncodedPassIsBogusThenFalse() {\n\t\tassertThat(getEncoder().matches(\"password\", \"012345678901234567890123456789\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenUsingPredictableSaltThenEqualTestHash() throws Exception {\n\t\tinjectPredictableSaltGen();\n\t\tString hash = getEncoder().encode(\"sometestpassword\");\n\t\tassertThat(hash).isEqualTo(\n\t\t\t\t\"$argon2id$v=19$m=4096,t=3,p=1$QUFBQUFBQUFBQUFBQUFBQQ$hmmTNyJlwbb6HAvFoHFWF+u03fdb0F2qA+39oPlcAqo\");\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenUsingPredictableSaltWithCustomParamsThenEqualTestHash() throws Exception {\n\t\tsetEncoder(new Argon2PasswordEncoder(16, 32, 4, 512, 5));\n\t\tinjectPredictableSaltGen();\n\t\tString hash = getEncoder().encode(\"sometestpassword\");\n\t\tassertThat(hash).isEqualTo(\n\t\t\t\t\"$argon2id$v=19$m=512,t=5,p=4$QUFBQUFBQUFBQUFBQUFBQQ$PNv4C3K50bz3rmON+LtFpdisD7ePieLNq+l5iUHgc1k\");\n\t}\n\n\t@Test\n\tpublic void encodedNonNullPasswordWhenUsingPredictableSaltWithDefaultsForSpringSecurity_v5_8ThenEqualTestHash()\n\t\t\tthrows Exception {\n\t\tsetEncoder(Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());\n\t\tinjectPredictableSaltGen();\n\t\tString hash = getEncoder().encode(\"sometestpassword\");\n\t\tassertThat(hash).isEqualTo(\n\t\t\t\t\"$argon2id$v=19$m=16384,t=2,p=1$QUFBQUFBQUFBQUFBQUFBQQ$zGt5MiNPSUOo4/7jBcJMayCPfcsLJ4c0WUxhwGDIYPw\");\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenSameEncodingThenFalse() {\n\t\tString hash = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().upgradeEncoding(hash)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenSameStandardParamsThenFalse() {\n\t\tArgon2PasswordEncoder newEncoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2();\n\t\tString hash = getEncoder().encode(\"password\");\n\t\tassertThat(newEncoder.upgradeEncoding(hash)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenSameCustomParamsThenFalse() {\n\t\tArgon2PasswordEncoder oldEncoder = new Argon2PasswordEncoder(20, 64, 4, 256, 4);\n\t\tArgon2PasswordEncoder newEncoder = new Argon2PasswordEncoder(20, 64, 4, 256, 4);\n\t\tString hash = oldEncoder.encode(\"password\");\n\t\tassertThat(newEncoder.upgradeEncoding(hash)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenHashHasLowerMemoryThenTrue() {\n\t\tArgon2PasswordEncoder oldEncoder = new Argon2PasswordEncoder(20, 64, 4, 256, 4);\n\t\tArgon2PasswordEncoder newEncoder = new Argon2PasswordEncoder(20, 64, 4, 512, 4);\n\t\tString hash = oldEncoder.encode(\"password\");\n\t\tassertThat(newEncoder.upgradeEncoding(hash)).isTrue();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenHashHasLowerIterationsThenTrue() {\n\t\tArgon2PasswordEncoder oldEncoder = new Argon2PasswordEncoder(20, 64, 4, 256, 4);\n\t\tArgon2PasswordEncoder newEncoder = new Argon2PasswordEncoder(20, 64, 4, 256, 5);\n\t\tString hash = oldEncoder.encode(\"password\");\n\t\tassertThat(newEncoder.upgradeEncoding(hash)).isTrue();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenHashHasHigherParamsThenFalse() {\n\t\tArgon2PasswordEncoder oldEncoder = new Argon2PasswordEncoder(20, 64, 4, 256, 4);\n\t\tArgon2PasswordEncoder newEncoder = new Argon2PasswordEncoder(20, 64, 4, 128, 3);\n\t\tString hash = oldEncoder.encode(\"password\");\n\t\tassertThat(newEncoder.upgradeEncoding(hash)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenEncodedPassIsNullThenFalse() {\n\t\tassertThat(getEncoder().upgradeEncoding(null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenEncodedPassIsEmptyThenFalse() {\n\t\tassertThat(getEncoder().upgradeEncoding(\"\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenEncodedPassIsBogusThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> getEncoder().upgradeEncoding(\"thisIsNoValidHash\"));\n\t}\n\n\tprivate void injectPredictableSaltGen() throws Exception {\n\t\tbyte[] bytes = new byte[16];\n\t\tArrays.fill(bytes, (byte) 0x41);\n\t\tMockito.when(this.keyGeneratorMock.generateKey()).thenReturn(bytes);\n\t\t// we can't use the @InjectMock-annotation because the salt-generator is set in\n\t\t// the constructor\n\t\t// and Mockito will only inject mocks if they are null\n\t\tField saltGen = getEncoder().getClass().getDeclaredField(\"saltGenerator\");\n\t\tsaltGen.setAccessible(true);\n\t\tsaltGen.set(getEncoder(), this.keyGeneratorMock);\n\t\tsaltGen.setAccessible(false);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/assertions/CryptoAssertionsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.assertions;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link CryptoAssertions} and {@link CryptoStringAssert}.\n */\nclass CryptoAssertionsTests {\n\n\t@Test\n\tvoid doesNotDecryptToPassesWhenSupplierThrows() {\n\t\tCryptoAssertions.assertThat((() -> {\n\t\t\tthrow new RuntimeException(\"decrypt failed\");\n\t\t})).doesNotDecryptTo(\"any\");\n\t}\n\n\t@Test\n\tvoid doesNotDecryptToPassesWhenResultDiffersFromExpected() {\n\t\tCryptoAssertions.assertThat(() -> \"other\").doesNotDecryptTo(\"plaintext\");\n\t}\n\n\t@Test\n\tvoid doesNotDecryptToFailsWhenResultEqualsExpected() {\n\t\tassertThatExceptionOfType(AssertionError.class)\n\t\t\t.isThrownBy(() -> CryptoAssertions.assertThat(() -> \"plaintext\").doesNotDecryptTo(\"plaintext\"))\n\t\t\t.withMessageContaining(\"Expected supplier not to return <plaintext> but it did\");\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.bcrypt;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.SecureRandom;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.password.AbstractPasswordEncoderValidationTests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * @author Dave Syer\n *\n */\npublic class BCryptPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tsetEncoder(new BCryptPasswordEncoder());\n\t}\n\n\t@Test\n\t// gh-5548\n\tpublic void emptyRawPasswordDoesNotMatchPassword() {\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"\", result)).isFalse();\n\t}\n\n\t@Test\n\tpublic void $2yMatches() {\n\t\t// $2y is default version\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(result.equals(\"password\")).isFalse();\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void $2aMatches() {\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(result.equals(\"password\")).isFalse();\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void $2bMatches() {\n\t\tsetEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B));\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(result.equals(\"password\")).isFalse();\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void $2yUnicode() {\n\t\t// $2y is default version\n\t\tString result = getEncoder().encode(\"passw\\u9292rd\");\n\t\tassertThat(getEncoder().matches(\"pass\\u9292\\u9292rd\", result)).isFalse();\n\t\tassertThat(getEncoder().matches(\"passw\\u9292rd\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void $2aUnicode() {\n\t\tsetEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A));\n\t\tString result = getEncoder().encode(\"passw\\u9292rd\");\n\t\tassertThat(getEncoder().matches(\"pass\\u9292\\u9292rd\", result)).isFalse();\n\t\tassertThat(getEncoder().matches(\"passw\\u9292rd\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void $2bUnicode() {\n\t\tsetEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B));\n\t\tString result = getEncoder().encode(\"passw\\u9292rd\");\n\t\tassertThat(getEncoder().matches(\"pass\\u9292\\u9292rd\", result)).isFalse();\n\t\tassertThat(getEncoder().matches(\"passw\\u9292rd\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void $2yNotMatches() {\n\t\t// $2y is default version\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"bogus\", result)).isFalse();\n\t}\n\n\t@Test\n\tpublic void $2aNotMatches() {\n\t\tsetEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A));\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"bogus\", result)).isFalse();\n\t}\n\n\t@Test\n\tpublic void $2bNotMatches() {\n\t\tsetEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B));\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"bogus\", result)).isFalse();\n\t}\n\n\t@Test\n\tpublic void $2yCustomStrength() {\n\t\tsetEncoder(new BCryptPasswordEncoder(8));\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void $2aCustomStrength() {\n\t\tsetEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A, 8));\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void $2bCustomStrength() {\n\t\tsetEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B, 8));\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void badLowCustomStrength() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new BCryptPasswordEncoder(3));\n\t}\n\n\t@Test\n\tpublic void badHighCustomStrength() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new BCryptPasswordEncoder(32));\n\t}\n\n\t@Test\n\tpublic void customRandom() {\n\t\tsetEncoder(new BCryptPasswordEncoder(8, new SecureRandom()));\n\t\tString result = getEncoder().encode(\"password\");\n\t\tassertThat(getEncoder().matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void doesntMatchNullEncodedValue() {\n\t\tsetEncoder(new BCryptPasswordEncoder());\n\t\tassertThat(getEncoder().matches(\"password\", null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void doesntMatchEmptyEncodedValue() {\n\t\tassertThat(getEncoder().matches(\"password\", \"\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void doesntMatchBogusEncodedValue() {\n\t\tassertThat(getEncoder().matches(\"password\", \"012345678901234567890123456789\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeFromLowerStrength() {\n\t\tBCryptPasswordEncoder weakEncoder = new BCryptPasswordEncoder(5);\n\t\tBCryptPasswordEncoder strongEncoder = new BCryptPasswordEncoder(15);\n\t\tString weakPassword = weakEncoder.encode(\"password\");\n\t\tString strongPassword = strongEncoder.encode(\"password\");\n\t\tassertThat(weakEncoder.upgradeEncoding(strongPassword)).isFalse();\n\t\tassertThat(strongEncoder.upgradeEncoding(weakPassword)).isTrue();\n\t}\n\n\t/**\n\t * @see <a href=\n\t * \"https://github.com/spring-projects/spring-security/pull/7042#issuecomment-506755496\">https://github.com/spring-projects/spring-security/pull/7042#issuecomment-506755496</a>\n\t */\n\t@Test\n\tpublic void upgradeFromNullOrEmpty() {\n\t\tassertThat(getEncoder().upgradeEncoding(null)).isFalse();\n\t\tassertThat(getEncoder().upgradeEncoding(\"\")).isFalse();\n\t}\n\n\t/**\n\t * @see <a href=\n\t * \"https://github.com/spring-projects/spring-security/pull/7042#issuecomment-506755496\">https://github.com/spring-projects/spring-security/pull/7042#issuecomment-506755496</a>\n\t */\n\t@Test\n\tpublic void upgradeFromNonBCrypt() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> getEncoder().upgradeEncoding(\"not-a-bcrypt-password\"));\n\t}\n\n\t@Test\n\tpublic void upgradeWhenNoRoundsThenTrue() {\n\t\tassertThat(getEncoder().upgradeEncoding(\"$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue\"))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenNoRoundsThenTrue() {\n\t\tassertThat(getEncoder().matches(\"password\", \"$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue\"))\n\t\t\t.isTrue();\n\t\tassertThat(getEncoder().matches(\"wrong\", \"$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue\"))\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void encodeWhenPasswordOverMaxLengthThenThrowIllegalArgumentException() {\n\t\tString password72chars = \"123456789012345678901234567890123456789012345678901234567890123456789012\";\n\t\tgetEncoder().encode(password72chars);\n\n\t\tString password73chars = password72chars + \"3\";\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> getEncoder().encode(password73chars));\n\t}\n\n\t@Test\n\tpublic void matchesWhenPasswordOverMaxLengthThenAllowToMatch() {\n\t\tString password71chars = \"12345678901234567890123456789012345678901234567890123456789012345678901\";\n\t\tString encodedPassword71chars = \"$2a$10$jx3x2FaF.iX5QZ9i3O424Os2Ou5P5JrnedmWYHuDyX8JKA4Unp4xq\";\n\t\tassertThat(getEncoder().matches(password71chars, encodedPassword71chars)).isTrue();\n\n\t\tString password72chars = password71chars + \"2\";\n\t\tString encodedPassword72chars = \"$2a$10$oXYO6/UvbsH5rQEraBkl6uheccBqdB3n.RaWbrimog9hS2GX4lo/O\";\n\t\tassertThat(getEncoder().matches(password72chars, encodedPassword72chars)).isTrue();\n\n\t\t// Max length is 72 bytes, however, we need to ensure backwards compatibility\n\t\t// for previously encoded passwords that are greater than 72 bytes and allow the\n\t\t// match to be performed.\n\t\tString password73chars = password72chars + \"3\";\n\t\tString encodedPassword73chars = \"$2a$10$1l9.kvQTsqNLiCYFqmKtQOHkp.BrgIrwsnTzWo9jdbQRbuBYQ/AVK\";\n\t\tassertThat(getEncoder().matches(password73chars, encodedPassword73chars)).isTrue();\n\t}\n\n\t/**\n\t * Fixes gh-18133\n\t * @author StringManolo\n\t */\n\t@Test\n\tvoid passwordLargerThan72BytesShouldThrowIllegalArgumentException() {\n\t\tBCryptPasswordEncoder encoder = new BCryptPasswordEncoder();\n\t\tString singleByteChars = \"a\".repeat(68);\n\t\tString password72Bytes = singleByteChars + \"😀\";\n\t\tassertThat(password72Bytes.length()).isEqualTo(70);\n\t\tassertThat(password72Bytes.getBytes(StandardCharsets.UTF_8).length).isEqualTo(72);\n\t\tassertThatNoException().isThrownBy(() -> encoder.encode(password72Bytes));\n\t\tString singleByteCharsTooLong = \"a\".repeat(69);\n\t\tString password73Bytes = singleByteCharsTooLong + \"😀\";\n\t\tassertThat(password73Bytes.getBytes(StandardCharsets.UTF_8).length).isEqualTo(73);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> encoder.encode(password73Bytes))\n\t\t\t.withMessageContaining(\"password cannot be more than 72 bytes\");\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java",
    "content": "// Copyright (c) 2006 Damien Miller <djm@mindrot.org>\n//\n// Permission to use, copy, modify, and distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\npackage org.springframework.security.crypto.bcrypt;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * JUnit unit tests for BCrypt routines\n *\n * @author Damien Miller\n */\npublic class BCryptTests {\n\n\tprivate static class TestObject<T> {\n\n\t\tprivate final T password;\n\n\t\tprivate final String salt;\n\n\t\tprivate final String expected;\n\n\t\tprivate TestObject(T password, String salt, String expected) {\n\t\t\tthis.password = password;\n\t\t\tthis.salt = salt;\n\t\t\tthis.expected = expected;\n\t\t}\n\n\t}\n\n\tprivate static void print(String s) {\n\t\t// System.out.print(s);\n\t}\n\n\tprivate static void println(String s) {\n\t\t// System.out.println(s);\n\t}\n\n\tprivate static List<TestObject<String>> testObjectsString;\n\n\tprivate static List<TestObject<byte[]>> testObjectsByteArray;\n\n\t@BeforeAll\n\tpublic static void setupTestObjects() {\n\t\ttestObjectsString = new ArrayList<>();\n\t\ttestObjectsString.add(new TestObject<>(\"\", \"$2a$06$DCq7YPn5Rq63x1Lad4cll.\",\n\t\t\t\t\"$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.\"));\n\t\ttestObjectsString.add(new TestObject<>(\"\", \"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.\",\n\t\t\t\t\"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye\"));\n\t\ttestObjectsString.add(new TestObject<>(\"\", \"$2a$10$k1wbIrmNyFAPwPVPSVa/ze\",\n\t\t\t\t\"$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW\"));\n\t\ttestObjectsString.add(new TestObject<>(\"\", \"$2a$12$k42ZFHFWqBp3vWli.nIn8u\",\n\t\t\t\t\"$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO\"));\n\t\ttestObjectsString.add(new TestObject<>(\"\", \"$2b$06$8eVN9RiU8Yki430X.wBvN.\",\n\t\t\t\t\"$2b$06$8eVN9RiU8Yki430X.wBvN.LWaqh2962emLVSVXVZIXJvDYLsV0oFu\"));\n\t\ttestObjectsString.add(new TestObject<>(\"\", \"$2b$06$NlgfNgpIc6GlHciCkMEW8u\",\n\t\t\t\t\"$2b$06$NlgfNgpIc6GlHciCkMEW8uKOBsyvAp7QwlHpysOlKdtyEw50WQua2\"));\n\t\ttestObjectsString.add(new TestObject<>(\"\", \"$2y$06$mFDtkz6UN7B3GZ2qi2hhaO\",\n\t\t\t\t\"$2y$06$mFDtkz6UN7B3GZ2qi2hhaO3OFWzNEdcY84ELw6iHCPruuQfSAXBLK\"));\n\t\ttestObjectsString.add(new TestObject<>(\"\", \"$2y$06$88kSqVttBx.e9iXTPCLa5u\",\n\t\t\t\t\"$2y$06$88kSqVttBx.e9iXTPCLa5uFPrVFjfLH4D.KcO6pBiAmvUkvdg0EYy\"));\n\t\ttestObjectsString.add(new TestObject<>(\"a\", \"$2a$06$m0CrhHm10qJ3lXRY.5zDGO\",\n\t\t\t\t\"$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe\"));\n\t\ttestObjectsString.add(new TestObject<>(\"a\", \"$2a$08$cfcvVd2aQ8CMvoMpP2EBfe\",\n\t\t\t\t\"$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V.\"));\n\t\ttestObjectsString.add(new TestObject<>(\"a\", \"$2a$10$k87L/MF28Q673VKh8/cPi.\",\n\t\t\t\t\"$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u\"));\n\t\ttestObjectsString.add(new TestObject<>(\"a\", \"$2a$12$8NJH3LsPrANStV6XtBakCe\",\n\t\t\t\t\"$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS\"));\n\t\ttestObjectsString.add(new TestObject<>(\"a\", \"$2b$06$ehKGYiS4wt2HAr7KQXS5z.\",\n\t\t\t\t\"$2b$06$ehKGYiS4wt2HAr7KQXS5z.OaRjB4jHO7rBHJKlGXbqEH3QVJfO7iO\"));\n\t\ttestObjectsString.add(new TestObject<>(\"a\", \"$2b$06$PWxFFHA3HiCD46TNOZh30e\",\n\t\t\t\t\"$2b$06$PWxFFHA3HiCD46TNOZh30eNto1hg5uM9tHBlI4q/b03SW/gGKUYk6\"));\n\t\ttestObjectsString.add(new TestObject<>(\"a\", \"$2y$06$LUdD6/aD0e/UbnxVAVbvGu\",\n\t\t\t\t\"$2y$06$LUdD6/aD0e/UbnxVAVbvGuUmIoJ3l/OK94ThhadpMWwKC34LrGEey\"));\n\t\ttestObjectsString.add(new TestObject<>(\"a\", \"$2y$06$eqgY.T2yloESMZxgp76deO\",\n\t\t\t\t\"$2y$06$eqgY.T2yloESMZxgp76deOROa7nzXDxbO0k.PJvuClTa.Vu1AuemG\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abc\", \"$2a$06$If6bvum7DFjUnE9p2uDeDu\",\n\t\t\t\t\"$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abc\", \"$2a$08$Ro0CUfOqk6cXEKf3dyaM7O\",\n\t\t\t\t\"$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abc\", \"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.\",\n\t\t\t\t\"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abc\", \"$2a$12$EXRkfkdmXn2gzds2SSitu.\",\n\t\t\t\t\"$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abc\", \"$2b$06$5FyQoicpbox1xSHFfhhdXu\",\n\t\t\t\t\"$2b$06$5FyQoicpbox1xSHFfhhdXuR2oxLpO1rYsQh5RTkI/9.RIjtoF0/ta\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abc\", \"$2b$06$1kJyuho8MCVP3HHsjnRMkO\",\n\t\t\t\t\"$2b$06$1kJyuho8MCVP3HHsjnRMkO1nvCOaKTqLnjG2TX1lyMFbXH/aOkgc.\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abc\", \"$2y$06$ACfku9dT6.H8VjdKb8nhlu\",\n\t\t\t\t\"$2y$06$ACfku9dT6.H8VjdKb8nhluaoBmhJyK7GfoNScEfOfrJffUxoUeCjK\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abc\", \"$2y$06$9JujYcoWPmifvFA3RUP90e\",\n\t\t\t\t\"$2y$06$9JujYcoWPmifvFA3RUP90e5rSEHAb5Ye6iv3.G9ikiHNv5cxjNEse\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abcdefghijklmnopqrstuvwxyz\", \"$2a$06$.rCVZVOThsIa97pEDOxvGu\",\n\t\t\t\t\"$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abcdefghijklmnopqrstuvwxyz\", \"$2a$08$aTsUwsyowQuzRrDqFflhge\",\n\t\t\t\t\"$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz.\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abcdefghijklmnopqrstuvwxyz\", \"$2a$10$fVH8e28OQRj9tqiDXs1e1u\",\n\t\t\t\t\"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abcdefghijklmnopqrstuvwxyz\", \"$2a$12$D4G5f18o7aMMfwasBL7Gpu\",\n\t\t\t\t\"$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abcdefghijklmnopqrstuvwxyz\", \"$2b$06$O8E89AQPj1zJQA05YvIAU.\",\n\t\t\t\t\"$2b$06$O8E89AQPj1zJQA05YvIAU.hMpj25BXri1bupl/Q7CJMlpLwZDNBoO\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abcdefghijklmnopqrstuvwxyz\", \"$2b$06$PDqIWr./o/P3EE/P.Q0A/u\",\n\t\t\t\t\"$2b$06$PDqIWr./o/P3EE/P.Q0A/uFg86WL/PXTbaW267TDALEwDylqk00Z.\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abcdefghijklmnopqrstuvwxyz\", \"$2y$06$34MG90ZLah8/ZNr3ltlHCu\",\n\t\t\t\t\"$2y$06$34MG90ZLah8/ZNr3ltlHCuz6bachF8/3S5jTuzF1h2qg2cUk11sFW\"));\n\t\ttestObjectsString.add(new TestObject<>(\"abcdefghijklmnopqrstuvwxyz\", \"$2y$06$AK.hSLfMyw706iEW24i68u\",\n\t\t\t\t\"$2y$06$AK.hSLfMyw706iEW24i68uKAc2yorPTrB0cimvjJHEBUrPkOq7VvG\"));\n\t\ttestObjectsString.add(new TestObject<>(\"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"$2a$06$fPIsBO8qRqkjj273rfaOI.\",\n\t\t\t\t\"$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO\"));\n\t\ttestObjectsString.add(new TestObject<>(\"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"$2a$08$Eq2r4G/76Wv39MzSX262hu\",\n\t\t\t\t\"$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW\"));\n\t\ttestObjectsString.add(new TestObject<>(\"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"$2a$10$LgfYWkbzEvQ4JakH7rOvHe\",\n\t\t\t\t\"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS\"));\n\t\ttestObjectsString.add(new TestObject<>(\"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"$2a$12$WApznUOJfkEGSmYRfnkrPO\",\n\t\t\t\t\"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC\"));\n\t\ttestObjectsString.add(new TestObject<>(\"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu\",\n\t\t\t\t\"$2b$06$FGWA8OlY6RtQhXBXuCJ8WusVipRI15cWOgJK8MYpBHEkktMfbHRIG\"));\n\t\ttestObjectsString.add(new TestObject<>(\"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"$2b$06$G6aYU7UhUEUDJBdTgq3CRe\",\n\t\t\t\t\"$2b$06$G6aYU7UhUEUDJBdTgq3CRekiopCN4O4sNitFXrf5NUscsVZj3a2r6\"));\n\t\ttestObjectsString.add(new TestObject<>(\"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"$2y$06$sYDFHqOcXTjBgOsqC0WCKe\",\n\t\t\t\t\"$2y$06$sYDFHqOcXTjBgOsqC0WCKeMd3T1UhHuWQSxncLGtXDLMrcE6vFDti\"));\n\t\ttestObjectsString.add(new TestObject<>(\"~!@#$%^&*()      ~!@#$%^&*()PNBFRD\", \"$2y$06$6Xm0gCw4g7ZNDCEp4yTise\",\n\t\t\t\t\"$2y$06$6Xm0gCw4g7ZNDCEp4yTisez0kSdpXEl66MvdxGidnmChIe8dFmMnq\"));\n\t\ttestObjectsByteArray = new ArrayList<>();\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] {}, \"$2a$06$fPIsBO8qRqkjj273rfaOI.\",\n\t\t\t\t\"$2a$06$fPIsBO8qRqkjj273rfaOI.uiVGfgi6Z1Iz.vZr11mi/38o09TUVCy\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] {}, \"$2a$08$Eq2r4G/76Wv39MzSX262hu\",\n\t\t\t\t\"$2a$08$Eq2r4G/76Wv39MzSX262hu2lrqIItOWKIkPsMMvm5LAFD.iVB7Nmm\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] {}, \"$2a$10$LgfYWkbzEvQ4JakH7rOvHe\",\n\t\t\t\t\"$2a$10$LgfYWkbzEvQ4JakH7rOvHeU6pINYiHnazYxe4GikGWx9MaUr27Vpa\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] {}, \"$2a$12$WApznUOJfkEGSmYRfnkrPO\",\n\t\t\t\t\"$2a$12$WApznUOJfkEGSmYRfnkrPONS3wcUvmKuh3LpjxSs6g78T77gZta3W\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] {}, \"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu\",\n\t\t\t\t\"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu5oPJaT8BeCRmS273I6cpp5RwwjAWn7S\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] {}, \"$2b$06$G6aYU7UhUEUDJBdTgq3CRe\",\n\t\t\t\t\"$2b$06$G6aYU7UhUEUDJBdTgq3CRebzUYAyG8MCS3WdBk0CcPb9bfj1.3cSG\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] {}, \"$2y$06$sYDFHqOcXTjBgOsqC0WCKe\",\n\t\t\t\t\"$2y$06$sYDFHqOcXTjBgOsqC0WCKeOv88fqPKkuV1yGVh./TROmn1mL8gYh2\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] {}, \"$2y$06$6Xm0gCw4g7ZNDCEp4yTise\",\n\t\t\t\t\"$2y$06$6Xm0gCw4g7ZNDCEp4yTisecBqTHmLJBHxTNZa8w2hupJKsIhPWOgG\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { -11 }, \"$2a$06$fPIsBO8qRqkjj273rfaOI.\",\n\t\t\t\t\"$2a$06$fPIsBO8qRqkjj273rfaOI.AyMTPwvUEmZ2EdJM/p0S0eP3UQpBas.\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { -11 }, \"$2a$08$Eq2r4G/76Wv39MzSX262hu\",\n\t\t\t\t\"$2a$08$Eq2r4G/76Wv39MzSX262huG.pmfTOWNaSXeVmr8y6qut1BpUiou6m\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { -11 }, \"$2a$10$LgfYWkbzEvQ4JakH7rOvHe\",\n\t\t\t\t\"$2a$10$LgfYWkbzEvQ4JakH7rOvHeNm5INR.iq7bbwMewV0Tydrmqq3mZ5IK\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { -11 }, \"$2a$12$WApznUOJfkEGSmYRfnkrPO\",\n\t\t\t\t\"$2a$12$WApznUOJfkEGSmYRfnkrPOi2qWwoWBJvfFzMrkqJwDedE3poicqwO\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { -11 }, \"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu\",\n\t\t\t\t\"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wuwip8vUd9WHq9onEGUjOS6CBHFkM./IG\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { -11 }, \"$2b$06$G6aYU7UhUEUDJBdTgq3CRe\",\n\t\t\t\t\"$2b$06$G6aYU7UhUEUDJBdTgq3CRe6RQpRSN.PQ28XtDFT7zUVvpXNbg.K4i\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { -11 }, \"$2y$06$sYDFHqOcXTjBgOsqC0WCKe\",\n\t\t\t\t\"$2y$06$sYDFHqOcXTjBgOsqC0WCKeduM9n5k0YfzTlgg69FIgGpw4ChTQNu2\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { -11 }, \"$2y$06$6Xm0gCw4g7ZNDCEp4yTise\",\n\t\t\t\t\"$2y$06$6Xm0gCw4g7ZNDCEp4yTisetcxOr0uSWmFiVtNpDxjd5iaFWs/tyjG\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { 76, -56, -12, 9, -116 }, \"$2a$06$fPIsBO8qRqkjj273rfaOI.\",\n\t\t\t\t\"$2a$06$fPIsBO8qRqkjj273rfaOI.5m8yX4eGfjqx/tyHtmte7/HbWtUS9u.\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { 76, -56, -12, 9, -116 }, \"$2a$08$Eq2r4G/76Wv39MzSX262hu\",\n\t\t\t\t\"$2a$08$Eq2r4G/76Wv39MzSX262hu0Vc3YdKF53qtdTtZJKD7uQfsPeGfkP6\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { 76, -56, -12, 9, -116 }, \"$2a$10$LgfYWkbzEvQ4JakH7rOvHe\",\n\t\t\t\t\"$2a$10$LgfYWkbzEvQ4JakH7rOvHeQBR1Mm2USNr//tnItwdVSZFNZfR/L9.\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { 76, -56, -12, 9, -116 }, \"$2a$12$WApznUOJfkEGSmYRfnkrPO\",\n\t\t\t\t\"$2a$12$WApznUOJfkEGSmYRfnkrPO2WxEe4rN3gMECOFt21H8ozd661HB8Za\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { 76, -56, -12, 9, -116 }, \"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu\",\n\t\t\t\t\"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu5SNpYypZvM0j3zTq7vSCtzqOllUArQW\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { 76, -56, -12, 9, -116 }, \"$2b$06$G6aYU7UhUEUDJBdTgq3CRe\",\n\t\t\t\t\"$2b$06$G6aYU7UhUEUDJBdTgq3CRejcZ96XDmofwo2r3O/Lw0hoDHQy/Utxq\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { 76, -56, -12, 9, -116 }, \"$2y$06$sYDFHqOcXTjBgOsqC0WCKe\",\n\t\t\t\t\"$2y$06$sYDFHqOcXTjBgOsqC0WCKej6.o3knVxc7obV8y47GTTFc9uUWC4OO\"));\n\t\ttestObjectsByteArray.add(new TestObject<>(new byte[] { 76, -56, -12, 9, -116 }, \"$2y$06$6Xm0gCw4g7ZNDCEp4yTise\",\n\t\t\t\t\"$2y$06$6Xm0gCw4g7ZNDCEp4yTiseKCvXMhtv0IrQPu9d36a893DjJ880Vb6\"));\n\t}\n\n\t/**\n\t * Test method for 'BCrypt.hashpw(String, String)'\n\t */\n\t@Test\n\tpublic void testHashpw() {\n\t\tprint(\"BCrypt.hashpw(): \");\n\t\tfor (TestObject<String> test : testObjectsString) {\n\t\t\tString hashed = BCrypt.hashpw(test.password, test.salt);\n\t\t\tassertThat(hashed).isEqualTo(test.expected);\n\t\t\tprint(\".\");\n\t\t}\n\t\tprintln(\"\");\n\t}\n\n\t/**\n\t * Test method for 'BCrypt.hashpw(byte[], String)'\n\t */\n\t@Test\n\tpublic void testHashpwByteArray() {\n\t\tfor (TestObject<byte[]> test : testObjectsByteArray) {\n\t\t\tString hashed = BCrypt.hashpw(test.password, test.salt);\n\t\t\tassertThat(hashed).isEqualTo(test.expected);\n\t\t}\n\t}\n\n\t/**\n\t * Test method for 'BCrypt.gensalt(int)'\n\t */\n\t@Test\n\tpublic void testGensaltInt() {\n\t\tprint(\"BCrypt.gensalt(log_rounds):\");\n\t\tfor (int i = 4; i <= 12; i++) {\n\t\t\tprint(\" \" + Integer.toString(i) + \":\");\n\t\t\tfor (int j = 0; j < testObjectsString.size(); j += 4) {\n\t\t\t\tString plain = testObjectsString.get(j).password;\n\t\t\t\tString salt = BCrypt.gensalt(i);\n\t\t\t\tString hashed1 = BCrypt.hashpw(plain, salt);\n\t\t\t\tString hashed2 = BCrypt.hashpw(plain, hashed1);\n\t\t\t\tassertThat(hashed2).isEqualTo(hashed1);\n\t\t\t\tprint(\".\");\n\t\t\t}\n\t\t}\n\t\tprintln(\"\");\n\t}\n\n\t/**\n\t * Test method for 'BCrypt.gensalt()'\n\t */\n\t@Test\n\tpublic void testGensalt() {\n\t\tprint(\"BCrypt.gensalt(): \");\n\t\tfor (int i = 0; i < testObjectsString.size(); i += 4) {\n\t\t\tString plain = testObjectsString.get(i).password;\n\t\t\tString salt = BCrypt.gensalt();\n\t\t\tString hashed1 = BCrypt.hashpw(plain, salt);\n\t\t\tString hashed2 = BCrypt.hashpw(plain, hashed1);\n\t\t\tassertThat(hashed2).isEqualTo(hashed1);\n\t\t\tprint(\".\");\n\t\t}\n\t\tprintln(\"\");\n\t}\n\n\t/**\n\t * Test method for 'BCrypt.checkpw(String, String)' expecting success\n\t */\n\t@Test\n\tpublic void testCheckpw_success() {\n\t\tprint(\"BCrypt.checkpw w/ good passwords: \");\n\t\tfor (TestObject<String> test : testObjectsString) {\n\t\t\tassertThat(BCrypt.checkpw(test.password, test.expected)).isTrue();\n\t\t\tprint(\".\");\n\t\t}\n\t\tprintln(\"\");\n\t}\n\n\t/**\n\t * Test method for 'BCrypt.checkpw(byte[], String)' expecting success\n\t */\n\t@Test\n\tpublic void testCheckpwByteArray_success() {\n\t\tfor (TestObject<byte[]> test : testObjectsByteArray) {\n\t\t\tassertThat(BCrypt.checkpw(test.password, test.expected)).isTrue();\n\t\t}\n\t}\n\n\t/**\n\t * Test method for 'BCrypt.checkpw(String, String)' expecting failure\n\t */\n\t@Test\n\tpublic void testCheckpw_failure() {\n\t\tprint(\"BCrypt.checkpw w/ bad passwords: \");\n\t\tfor (int i = 0; i < testObjectsString.size(); i++) {\n\t\t\tint broken_index = (i + 8) % testObjectsString.size();\n\t\t\tString plain = testObjectsString.get(i).password;\n\t\t\tString expected = testObjectsString.get(broken_index).expected;\n\t\t\tassertThat(BCrypt.checkpw(plain, expected)).isFalse();\n\t\t\tprint(\".\");\n\t\t}\n\t\tprintln(\"\");\n\t}\n\n\t/**\n\t * Test method for 'BCrypt.checkpw(byte[], String)' expecting failure\n\t */\n\t@Test\n\tpublic void testCheckpwByteArray_failure() {\n\t\tfor (int i = 0; i < testObjectsByteArray.size(); i++) {\n\t\t\tint broken_index = (i + 8) % testObjectsByteArray.size();\n\t\t\tbyte[] plain = testObjectsByteArray.get(i).password;\n\t\t\tString expected = testObjectsByteArray.get(broken_index).expected;\n\t\t\tassertThat(BCrypt.checkpw(plain, expected)).isFalse();\n\t\t}\n\t}\n\n\t/**\n\t * Test for correct hashing of non-US-ASCII passwords\n\t */\n\t@Test\n\tpublic void testInternationalChars() {\n\t\tprint(\"BCrypt.hashpw w/ international chars: \");\n\t\tString pw1 = \"ππππππππ\";\n\t\tString pw2 = \"????????\";\n\t\tString h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());\n\t\tassertThat(BCrypt.checkpw(pw2, h1)).isFalse();\n\t\tprint(\".\");\n\t\tString h2 = BCrypt.hashpw(pw2, BCrypt.gensalt());\n\t\tassertThat(BCrypt.checkpw(pw1, h2)).isFalse();\n\t\tprint(\".\");\n\t\tprintln(\"\");\n\t}\n\n\t@Test\n\tpublic void roundsForDoesNotOverflow() {\n\t\tassertThat(BCrypt.roundsForLogRounds(10)).isEqualTo(1024);\n\t\tassertThat(BCrypt.roundsForLogRounds(31)).isEqualTo(0x80000000L);\n\t}\n\n\t@Test\n\tpublic void emptyByteArrayCannotBeEncoded() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> BCrypt.encode_base64(new byte[0], 0, new StringBuilder()));\n\t}\n\n\t@Test\n\tpublic void moreBytesThanInTheArrayCannotBeEncoded() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> BCrypt.encode_base64(new byte[1], 2, new StringBuilder()));\n\t}\n\n\t@Test\n\tpublic void decodingMustRequestMoreThanZeroBytes() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> BCrypt.decode_base64(\"\", 0));\n\t}\n\n\tprivate static String encode_base64(byte d[], int len) throws IllegalArgumentException {\n\t\tStringBuilder rs = new StringBuilder();\n\t\tBCrypt.encode_base64(d, len, rs);\n\t\treturn rs.toString();\n\t}\n\n\t@Test\n\tpublic void testBase64EncodeSimpleByteArrays() {\n\t\tassertThat(encode_base64(new byte[] { 0 }, 1)).isEqualTo(\"..\");\n\t\tassertThat(encode_base64(new byte[] { 0, 0 }, 2)).isEqualTo(\"...\");\n\t\tassertThat(encode_base64(new byte[] { 0, 0, 0 }, 3)).isEqualTo(\"....\");\n\t}\n\n\t@Test\n\tpublic void decodingCharsOutsideAsciiGivesNoResults() {\n\t\tbyte[] ba = BCrypt.decode_base64(\"ππππππππ\", 1);\n\t\tassertThat(ba).isEmpty();\n\t}\n\n\t@Test\n\tpublic void decodingStopsWithFirstInvalidCharacter() {\n\t\tassertThat(BCrypt.decode_base64(\"....\", 1)).hasSize(1);\n\t\tassertThat(BCrypt.decode_base64(\" ....\", 1)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void decodingOnlyProvidesAvailableBytes() {\n\t\tassertThat(BCrypt.decode_base64(\"\", 1)).isEmpty();\n\t\tassertThat(BCrypt.decode_base64(\"......\", 3)).hasSize(3);\n\t\tassertThat(BCrypt.decode_base64(\"......\", 4)).hasSize(4);\n\t\tassertThat(BCrypt.decode_base64(\"......\", 5)).hasSize(4);\n\t}\n\n\t/**\n\t * Encode and decode each byte value in each position.\n\t */\n\t@Test\n\tpublic void testBase64EncodeDecode() {\n\t\tbyte[] ba = new byte[3];\n\t\tfor (int b = 0; b <= 0xFF; b++) {\n\t\t\tfor (int i = 0; i < ba.length; i++) {\n\t\t\t\tArrays.fill(ba, (byte) 0);\n\t\t\t\tba[i] = (byte) b;\n\t\t\t\tString s = encode_base64(ba, 3);\n\t\t\t\tassertThat(s).hasSize(4);\n\t\t\t\tbyte[] decoded = BCrypt.decode_base64(s, 3);\n\t\t\t\tassertThat(decoded).isEqualTo(ba);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void genSaltFailsWithTooFewRounds() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> BCrypt.gensalt(3));\n\t}\n\n\t@Test\n\tpublic void genSaltFailsWithTooManyRounds() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> BCrypt.gensalt(32));\n\t}\n\n\t@Test\n\tpublic void genSaltGeneratesCorrectSaltPrefix() {\n\t\tassertThat(BCrypt.gensalt(4)).startsWith(\"$2a$04$\");\n\t\tassertThat(BCrypt.gensalt(31)).startsWith(\"$2a$31$\");\n\t}\n\n\t@Test\n\tpublic void hashpwFailsWhenSaltIsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> BCrypt.hashpw(\"password\", null));\n\t}\n\n\t@Test\n\tpublic void hashpwFailsWhenSaltSpecifiesTooFewRounds() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> BCrypt.hashpw(\"password\", \"$2a$03$......................\"));\n\t}\n\n\t@Test\n\tpublic void hashpwFailsWhenSaltSpecifiesTooManyRounds() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> BCrypt.hashpw(\"password\", \"$2a$32$......................\"));\n\t}\n\n\t@Test\n\tpublic void saltLengthIsChecked() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> BCrypt.hashpw(\"\", \"\"));\n\t}\n\n\t@Test\n\tpublic void hashpwWorksWithOldRevision() {\n\t\tassertThat(BCrypt.hashpw(\"password\", \"$2$05$......................\"))\n\t\t\t.isEqualTo(\"$2$05$......................bvpG2UfzdyW/S0ny/4YyEZrmczoJfVm\");\n\t}\n\n\t@Test\n\tpublic void hashpwFailsWhenSaltIsTooShort() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> BCrypt.hashpw(\"password\", \"$2a$10$123456789012345678901\"));\n\t}\n\n\t@Test\n\tpublic void equalsOnStringsIsCorrect() {\n\t\tassertThat(BCrypt.equalsNoEarlyReturn(\"\", \"\")).isTrue();\n\t\tassertThat(BCrypt.equalsNoEarlyReturn(\"test\", \"test\")).isTrue();\n\t\tassertThat(BCrypt.equalsNoEarlyReturn(\"test\", \"\")).isFalse();\n\t\tassertThat(BCrypt.equalsNoEarlyReturn(\"\", \"test\")).isFalse();\n\t\tassertThat(BCrypt.equalsNoEarlyReturn(\"test\", \"pass\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkpwWhenZeroRoundsThenMatches() {\n\t\tString password = \"$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue\";\n\t\tassertThat(BCrypt.checkpw(\"password\", password)).isTrue();\n\t\tassertThat(BCrypt.checkpw(\"wrong\", password)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/codec/HexTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.codec;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Test cases for {@link Hex}.\n *\n * @author Kazuki Shimizu\n */\npublic class HexTests {\n\n\t@Test\n\tpublic void encode() {\n\t\tassertThat(Hex.encode(new byte[] { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D' }))\n\t\t\t.isEqualTo(new char[] { '4', '1', '4', '2', '4', '3', '4', '4' });\n\t}\n\n\t@Test\n\tpublic void encodeEmptyByteArray() {\n\t\tassertThat(Hex.encode(new byte[] {})).isEmpty();\n\t}\n\n\t@Test\n\tpublic void decode() {\n\t\tassertThat(Hex.decode(\"41424344\")).isEqualTo(new byte[] { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D' });\n\t}\n\n\t@Test\n\tpublic void decodeEmptyString() {\n\t\tassertThat(Hex.decode(\"\")).isEmpty();\n\t}\n\n\t@Test\n\tpublic void decodeNotEven() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Hex.decode(\"414243444\"))\n\t\t\t.withMessage(\"Hex-encoded string must have an even number of characters\");\n\t}\n\n\t@Test\n\tpublic void decodeExistNonHexCharAtFirst() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Hex.decode(\"G0\"))\n\t\t\t.withMessage(\"Detected a Non-hex character at 1 or 2 position\");\n\t}\n\n\t@Test\n\tpublic void decodeExistNonHexCharAtSecond() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Hex.decode(\"410G\"))\n\t\t\t.withMessage(\"Detected a Non-hex character at 3 or 4 position\");\n\t}\n\n\t@Test\n\tpublic void decodeExistNonHexCharAtBoth() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Hex.decode(\"4142GG\"))\n\t\t\t.withMessage(\"Detected a Non-hex character at 5 or 6 position\");\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/codec/Utf8Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.codec;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\npublic class Utf8Tests {\n\n\t// SEC-1752\n\t@Test\n\tpublic void utf8EncodesAndDecodesCorrectly() throws Exception {\n\t\tbyte[] bytes = Utf8.encode(\"6048b75ed560785c\");\n\t\tassertThat(bytes).hasSize(16);\n\t\tassertThat(Arrays.equals(\"6048b75ed560785c\".getBytes(\"UTF-8\"), bytes)).isTrue();\n\t\tString decoded = Utf8.decode(bytes);\n\t\tassertThat(decoded).isEqualTo(\"6048b75ed560785c\");\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/AesBytesEncryptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.PBEKeySpec;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgorithm;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.password.Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link AesBytesEncryptor}\n */\npublic class AesBytesEncryptorTests {\n\n\tprivate String secret = \"value\";\n\n\tprivate String password = \"password\";\n\n\tprivate String hexSalt = \"deadbeef\";\n\n\tprivate BytesKeyGenerator generator;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.generator = mock(BytesKeyGenerator.class);\n\t\tgiven(this.generator.generateKey()).willReturn(Hex.decode(\"4b0febebd439db7ca77153cb254520c3\"));\n\t\tgiven(this.generator.getKeyLength()).willReturn(16);\n\t}\n\n\t@Test\n\tpublic void roundtripWhenUsingDefaultsThenEncryptsAndDecrypts() {\n\t\tCryptoAssumptions.assumeCBCJCE();\n\t\tAesBytesEncryptor encryptor = new AesBytesEncryptor(this.password, this.hexSalt);\n\t\tbyte[] encryption = encryptor.encrypt(this.secret.getBytes());\n\t\tbyte[] decryption = encryptor.decrypt(encryption);\n\t\tassertThat(new String(decryption)).isEqualTo(this.secret);\n\t}\n\n\t@Test\n\tpublic void roundtripWhenUsingDefaultCipherThenEncryptsAndDecrypts() {\n\t\tCryptoAssumptions.assumeCBCJCE();\n\t\tAesBytesEncryptor encryptor = new AesBytesEncryptor(this.password, this.hexSalt, this.generator);\n\t\tbyte[] encryption = encryptor.encrypt(this.secret.getBytes());\n\t\tassertThat(new String(Hex.encode(encryption)))\n\t\t\t.isEqualTo(\"4b0febebd439db7ca77153cb254520c3b7232ac29355d07869433f1ecf55fe94\");\n\t\tbyte[] decryption = encryptor.decrypt(encryption);\n\t\tassertThat(new String(decryption)).isEqualTo(this.secret);\n\t}\n\n\t@Test\n\tpublic void roundtripWhenUsingGcmThenEncryptsAndDecrypts() {\n\t\tCryptoAssumptions.assumeGCMJCE();\n\t\tAesBytesEncryptor encryptor = new AesBytesEncryptor(this.password, this.hexSalt, this.generator,\n\t\t\t\tCipherAlgorithm.GCM);\n\t\tbyte[] encryption = encryptor.encrypt(this.secret.getBytes());\n\t\tassertThat(new String(Hex.encode(encryption)))\n\t\t\t.isEqualTo(\"4b0febebd439db7ca77153cb254520c3e4d61ae38207b4e42b820d311dc3d4e0e2f37ed5ee\");\n\t\tbyte[] decryption = encryptor.decrypt(encryption);\n\t\tassertThat(new String(decryption)).isEqualTo(this.secret);\n\t}\n\n\t@Test\n\tpublic void roundtripWhenUsingSecretKeyThenEncryptsAndDecrypts() {\n\t\tCryptoAssumptions.assumeGCMJCE();\n\t\tPBEKeySpec keySpec = new PBEKeySpec(this.password.toCharArray(), Hex.decode(this.hexSalt), 1024, 256);\n\t\tSecretKey secretKey = CipherUtils.newSecretKey(SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(), keySpec);\n\t\tAesBytesEncryptor encryptor = new AesBytesEncryptor(secretKey, this.generator, CipherAlgorithm.GCM);\n\t\tbyte[] encryption = encryptor.encrypt(this.secret.getBytes());\n\t\tassertThat(new String(Hex.encode(encryption)))\n\t\t\t.isEqualTo(\"4b0febebd439db7ca77153cb254520c3e4d61ae38207b4e42b820d311dc3d4e0e2f37ed5ee\");\n\t\tbyte[] decryption = encryptor.decrypt(encryption);\n\t\tassertThat(new String(decryption)).isEqualTo(this.secret);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.security.SecureRandom;\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Random;\nimport java.util.UUID;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgorithm;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class BouncyCastleAesBytesEncryptorEquivalencyTests {\n\n\tprivate byte[] testData;\n\n\tprivate String password;\n\n\tprivate String salt;\n\n\tprivate SecureRandom secureRandom = new SecureRandom();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// generate random password, salt, and test data\n\t\tthis.password = UUID.randomUUID().toString();\n\t\t/** insecure salt byte, recommend 64 or larger than 64 */\n\t\tbyte[] saltBytes = new byte[16];\n\t\tthis.secureRandom.nextBytes(saltBytes);\n\t\tthis.salt = new String(Hex.encode(saltBytes));\n\t}\n\n\t@Test\n\tpublic void bouncyCastleAesCbcWithPredictableIvEquivalent() throws Exception {\n\t\tCryptoAssumptions.assumeCBCJCE();\n\t\tBytesEncryptor bcEncryptor = new BouncyCastleAesCbcBytesEncryptor(this.password, this.salt,\n\t\t\t\tnew PredictableRandomBytesKeyGenerator(16));\n\t\tBytesEncryptor jceEncryptor = new AesBytesEncryptor(this.password, this.salt,\n\t\t\t\tnew PredictableRandomBytesKeyGenerator(16));\n\t\ttestEquivalence(bcEncryptor, jceEncryptor);\n\t}\n\n\t@Test\n\tpublic void bouncyCastleAesCbcWithSecureIvCompatible() throws Exception {\n\t\tCryptoAssumptions.assumeCBCJCE();\n\t\tBytesEncryptor bcEncryptor = new BouncyCastleAesCbcBytesEncryptor(this.password, this.salt,\n\t\t\t\tKeyGenerators.secureRandom(16));\n\t\tBytesEncryptor jceEncryptor = new AesBytesEncryptor(this.password, this.salt, KeyGenerators.secureRandom(16));\n\t\ttestCompatibility(bcEncryptor, jceEncryptor);\n\t}\n\n\t@Test\n\tpublic void bouncyCastleAesGcmWithPredictableIvEquivalent() throws Exception {\n\t\tCryptoAssumptions.assumeGCMJCE();\n\t\tBytesEncryptor bcEncryptor = new BouncyCastleAesGcmBytesEncryptor(this.password, this.salt,\n\t\t\t\tnew PredictableRandomBytesKeyGenerator(16));\n\t\tBytesEncryptor jceEncryptor = new AesBytesEncryptor(this.password, this.salt,\n\t\t\t\tnew PredictableRandomBytesKeyGenerator(16), CipherAlgorithm.GCM);\n\t\ttestEquivalence(bcEncryptor, jceEncryptor);\n\t}\n\n\t@Test\n\tpublic void bouncyCastleAesGcmWithSecureIvCompatible() throws Exception {\n\t\tCryptoAssumptions.assumeGCMJCE();\n\t\tBytesEncryptor bcEncryptor = new BouncyCastleAesGcmBytesEncryptor(this.password, this.salt,\n\t\t\t\tKeyGenerators.secureRandom(16));\n\t\tBytesEncryptor jceEncryptor = new AesBytesEncryptor(this.password, this.salt, KeyGenerators.secureRandom(16),\n\t\t\t\tCipherAlgorithm.GCM);\n\t\ttestCompatibility(bcEncryptor, jceEncryptor);\n\t}\n\n\tprivate void testEquivalence(BytesEncryptor left, BytesEncryptor right) {\n\t\tfor (int size = 1; size < 2048; size++) {\n\t\t\tthis.testData = new byte[size];\n\t\t\tthis.secureRandom.nextBytes(this.testData);\n\t\t\t// tests that right and left generate the same encrypted bytes\n\t\t\t// and can decrypt back to the original input\n\t\t\tbyte[] leftEncrypted = left.encrypt(this.testData);\n\t\t\tbyte[] rightEncrypted = right.encrypt(this.testData);\n\t\t\tassertThat(rightEncrypted).containsExactly(leftEncrypted);\n\t\t\tbyte[] leftDecrypted = left.decrypt(leftEncrypted);\n\t\t\tbyte[] rightDecrypted = right.decrypt(rightEncrypted);\n\t\t\tassertThat(leftDecrypted).containsExactly(this.testData);\n\t\t\tassertThat(rightDecrypted).containsExactly(this.testData);\n\t\t}\n\t}\n\n\tprivate void testCompatibility(BytesEncryptor left, BytesEncryptor right) {\n\t\t// tests that right can decrypt what left encrypted and vice versa\n\t\t// and that the decrypted data is the same as the original\n\t\tfor (int size = 1; size < 2048; size++) {\n\t\t\tthis.testData = new byte[size];\n\t\t\tthis.secureRandom.nextBytes(this.testData);\n\t\t\tbyte[] leftEncrypted = left.encrypt(this.testData);\n\t\t\tbyte[] rightEncrypted = right.encrypt(this.testData);\n\t\t\tbyte[] leftDecrypted = left.decrypt(rightEncrypted);\n\t\t\tbyte[] rightDecrypted = right.decrypt(leftEncrypted);\n\t\t\tassertThat(leftDecrypted).containsExactly(this.testData);\n\t\t\tassertThat(rightDecrypted).containsExactly(this.testData);\n\t\t}\n\t}\n\n\tprivate long testSpeed(BytesEncryptor bytesEncryptor) {\n\t\tlong start = System.nanoTime();\n\t\tfor (int size = 0; size < 2048; size++) {\n\t\t\tthis.testData = new byte[size];\n\t\t\tthis.secureRandom.nextBytes(this.testData);\n\t\t\tbyte[] encrypted = bytesEncryptor.encrypt(this.testData);\n\t\t\tbyte[] decrypted = bytesEncryptor.decrypt(encrypted);\n\t\t\tassertThat(decrypted).containsExactly(this.testData);\n\t\t}\n\t\treturn System.nanoTime() - start;\n\t}\n\n\tprivate String nanosToReadableString(String label, long nanos) {\n\t\tDuration duration = Duration.ofNanos(nanos);\n\t\tDuration millis = duration.truncatedTo(ChronoUnit.MILLIS);\n\t\tDuration micros = duration.minus(millis).dividedBy(1000);\n\t\treturn \"%s: %dms %dμs\".formatted(label, duration.toMillis(), micros.toNanos());\n\t}\n\n\t/**\n\t * A BytesKeyGenerator that always generates the same sequence of values\n\t */\n\tprivate static class PredictableRandomBytesKeyGenerator implements BytesKeyGenerator {\n\n\t\tprivate final Random random;\n\n\t\tprivate final int keyLength;\n\n\t\tPredictableRandomBytesKeyGenerator(int keyLength) {\n\t\t\tthis.random = new Random(1);\n\t\t\tthis.keyLength = keyLength;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getKeyLength() {\n\t\t\treturn this.keyLength;\n\t\t}\n\n\t\t@Override\n\t\tpublic byte[] generateKey() {\n\t\t\tbyte[] bytes = new byte[this.keyLength];\n\t\t\tthis.random.nextBytes(bytes);\n\t\t\treturn bytes;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.security.SecureRandom;\nimport java.util.UUID;\n\nimport org.bouncycastle.util.Arrays;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\npublic class BouncyCastleAesBytesEncryptorTests {\n\n\tprivate byte[] testData;\n\n\tprivate String password;\n\n\tprivate String salt;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// generate random password, salt, and test data\n\t\tSecureRandom secureRandom = new SecureRandom();\n\t\tthis.password = UUID.randomUUID().toString();\n\t\tbyte[] saltBytes = new byte[16];\n\t\tsecureRandom.nextBytes(saltBytes);\n\t\tthis.salt = new String(Hex.encode(saltBytes));\n\t\tthis.testData = new byte[1024 * 1024];\n\t\tsecureRandom.nextBytes(this.testData);\n\t}\n\n\t@Test\n\tpublic void bcCbcWithSecureIvGeneratesDifferentMessages() {\n\t\tBytesEncryptor bcEncryptor = new BouncyCastleAesCbcBytesEncryptor(this.password, this.salt);\n\t\tgeneratesDifferentCipherTexts(bcEncryptor);\n\t}\n\n\t@Test\n\tpublic void bcGcmWithSecureIvGeneratesDifferentMessages() {\n\t\tBytesEncryptor bcEncryptor = new BouncyCastleAesGcmBytesEncryptor(this.password, this.salt);\n\t\tgeneratesDifferentCipherTexts(bcEncryptor);\n\t}\n\n\tprivate void generatesDifferentCipherTexts(BytesEncryptor bcEncryptor) {\n\t\tbyte[] encrypted1 = bcEncryptor.encrypt(this.testData);\n\t\tbyte[] encrypted2 = bcEncryptor.encrypt(this.testData);\n\t\tassertThat(Arrays.areEqual(encrypted1, encrypted2)).isFalse();\n\t\tbyte[] decrypted1 = bcEncryptor.decrypt(encrypted1);\n\t\tbyte[] decrypted2 = bcEncryptor.decrypt(encrypted2);\n\t\tassertThat(decrypted1).containsExactly(this.testData);\n\t\tassertThat(decrypted2).containsExactly(this.testData);\n\t}\n\n\t@Test\n\tpublic void bcCbcWithWrongLengthIv() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new BouncyCastleAesCbcBytesEncryptor(this.password, this.salt, KeyGenerators.secureRandom(8)));\n\t}\n\n\t@Test\n\tpublic void bcGcmWithWrongLengthIv() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new BouncyCastleAesGcmBytesEncryptor(this.password, this.salt, KeyGenerators.secureRandom(8)));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/CryptoAssumptions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.security.NoSuchAlgorithmException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.NoSuchPaddingException;\n\nimport org.junit.jupiter.api.Assumptions;\nimport org.opentest4j.TestAbortedException;\n\nimport org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgorithm;\n\npublic final class CryptoAssumptions {\n\n\tprivate CryptoAssumptions() {\n\t}\n\n\tpublic static void assumeGCMJCE() {\n\t\tassumeAes256(CipherAlgorithm.GCM);\n\t}\n\n\tpublic static void assumeCBCJCE() {\n\t\tassumeAes256(CipherAlgorithm.CBC);\n\t}\n\n\tprivate static void assumeAes256(CipherAlgorithm cipherAlgorithm) {\n\t\tboolean aes256Available = false;\n\t\ttry {\n\t\t\tCipher.getInstance(cipherAlgorithm.toString());\n\t\t\taes256Available = Cipher.getMaxAllowedKeyLength(\"AES\") >= 256;\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new TestAbortedException(cipherAlgorithm + \" not available, skipping test\", ex);\n\t\t}\n\t\tcatch (NoSuchPaddingException ex) {\n\t\t\tthrow new TestAbortedException(cipherAlgorithm + \" padding not available, skipping test\", ex);\n\t\t}\n\t\tAssumptions.assumeTrue(aes256Available, \"AES key length of 256 not allowed, skipping test\");\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/EncryptorsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class EncryptorsTests {\n\n\t@Test\n\tpublic void stronger() throws Exception {\n\t\tCryptoAssumptions.assumeGCMJCE();\n\t\tBytesEncryptor encryptor = Encryptors.stronger(\"password\", \"5c0744940b5c369b\");\n\t\tbyte[] result = encryptor.encrypt(\"text\".getBytes(\"UTF-8\"));\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(new String(result).equals(\"text\")).isFalse();\n\t\tassertThat(new String(encryptor.decrypt(result))).isEqualTo(\"text\");\n\t\tassertThat(new String(result)).isNotEqualTo(new String(encryptor.encrypt(\"text\".getBytes())));\n\t}\n\n\t@Test\n\tpublic void standard() throws Exception {\n\t\tCryptoAssumptions.assumeCBCJCE();\n\t\tBytesEncryptor encryptor = Encryptors.standard(\"password\", \"5c0744940b5c369b\");\n\t\tbyte[] result = encryptor.encrypt(\"text\".getBytes(\"UTF-8\"));\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(new String(result).equals(\"text\")).isFalse();\n\t\tassertThat(new String(encryptor.decrypt(result))).isEqualTo(\"text\");\n\t\tassertThat(new String(result)).isNotEqualTo(new String(encryptor.encrypt(\"text\".getBytes())));\n\t}\n\n\t@Test\n\tpublic void preferred() {\n\t\tCryptoAssumptions.assumeGCMJCE();\n\t\tTextEncryptor encryptor = Encryptors.delux(\"password\", \"5c0744940b5c369b\");\n\t\tString result = encryptor.encrypt(\"text\");\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.equals(\"text\")).isFalse();\n\t\tassertThat(encryptor.decrypt(result)).isEqualTo(\"text\");\n\t\tassertThat(result.equals(encryptor.encrypt(\"text\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void text() {\n\t\tCryptoAssumptions.assumeCBCJCE();\n\t\tTextEncryptor encryptor = Encryptors.text(\"password\", \"5c0744940b5c369b\");\n\t\tString result = encryptor.encrypt(\"text\");\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.equals(\"text\")).isFalse();\n\t\tassertThat(encryptor.decrypt(result)).isEqualTo(\"text\");\n\t\tassertThat(result.equals(encryptor.encrypt(\"text\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void noOpText() {\n\t\tTextEncryptor encryptor = Encryptors.noOpText();\n\t\tassertThat(encryptor.encrypt(\"text\")).isEqualTo(\"text\");\n\t\tassertThat(encryptor.decrypt(\"text\")).isEqualTo(\"text\");\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/KeyStoreKeyFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnOs;\nimport org.junit.jupiter.api.condition.OS;\n\nimport org.springframework.core.io.ClassPathResource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Dave Syer\n *\n */\n@DisabledOnOs(OS.WINDOWS)\npublic class KeyStoreKeyFactoryTests {\n\n\t@Test\n\tpublic void initializeEncryptorFromKeyStore() {\n\t\tchar[] password = \"foobar\".toCharArray();\n\t\tKeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource(\"keystore.jks\"), password);\n\t\tRsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair(\"test\"));\n\t\tassertThat(encryptor.canDecrypt()).as(\"Should be able to decrypt\").isTrue();\n\t\tassertThat(encryptor.decrypt(encryptor.encrypt(\"foo\"))).isEqualTo(\"foo\");\n\t}\n\n\t@Test\n\tpublic void initializeEncryptorFromPkcs12KeyStore() {\n\t\tchar[] password = \"letmein\".toCharArray();\n\t\tKeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource(\"keystore.pkcs12\"), password);\n\t\tRsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair(\"mytestkey\"));\n\t\tassertThat(encryptor.canDecrypt()).as(\"Should be able to decrypt\").isTrue();\n\t\tassertThat(encryptor.decrypt(encryptor.encrypt(\"foo\"))).isEqualTo(\"foo\");\n\t}\n\n\t@Test\n\tpublic void initializeEncryptorFromTrustedCertificateInKeyStore() {\n\t\tchar[] password = \"foobar\".toCharArray();\n\t\tKeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource(\"keystore.jks\"), password);\n\t\tRsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair(\"testcertificate\"));\n\t\tassertThat(encryptor.canDecrypt()).as(\"Should not be able to decrypt\").isFalse();\n\t\tassertThat(encryptor.encrypt(\"foo\")).isNotEqualTo(\"foo\");\n\t}\n\n\t@Test\n\tpublic void initializeEncryptorFromTrustedCertificateInPkcs12KeyStore() {\n\t\tchar[] password = \"letmein\".toCharArray();\n\t\tKeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource(\"keystore.pkcs12\"), password);\n\t\tRsaSecretEncryptor encryptor = new RsaSecretEncryptor(factory.getKeyPair(\"mytestcertificate\"));\n\t\tassertThat(encryptor.canDecrypt()).as(\"Should not be able to decrypt\").isFalse();\n\t\tassertThat(encryptor.encrypt(\"foo\")).isNotEqualTo(\"foo\");\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/RsaKeyHelperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.KeyPair;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnOs;\nimport org.junit.jupiter.api.condition.OS;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.util.StreamUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@DisabledOnOs(OS.WINDOWS)\npublic class RsaKeyHelperTests {\n\n\t@Test\n\tpublic void parsePrivateKey() throws Exception {\n\t\t// ssh-keygen -m pem -b 1024 -f src/test/resources/fake.pem\n\t\tString pem = StreamUtils.copyToString(new ClassPathResource(\"/fake.pem\", getClass()).getInputStream(),\n\t\t\t\tStandardCharsets.UTF_8);\n\t\tKeyPair result = RsaKeyHelper.parseKeyPair(pem);\n\t\tassertThat(result.getPrivate().getEncoded().length > 0).isTrue();\n\t\tassertThat(result.getPrivate().getAlgorithm()).isEqualTo(\"RSA\");\n\t}\n\n\t@Test\n\tpublic void parseSpaceyKey() throws Exception {\n\t\tString pem = StreamUtils.copyToString(new ClassPathResource(\"/spacey.pem\", getClass()).getInputStream(),\n\t\t\t\tStandardCharsets.UTF_8);\n\t\tKeyPair result = RsaKeyHelper.parseKeyPair(pem);\n\t\tassertThat(result.getPrivate().getEncoded().length > 0).isTrue();\n\t\tassertThat(result.getPrivate().getAlgorithm()).isEqualTo(\"RSA\");\n\t}\n\n\t@Test\n\tpublic void parseBadKey() throws Exception {\n\t\t// ssh-keygen -m pem -b 1024 -f src/test/resources/fake.pem\n\t\tString pem = StreamUtils.copyToString(new ClassPathResource(\"/bad.pem\", getClass()).getInputStream(),\n\t\t\t\tStandardCharsets.UTF_8);\n\t\ttry {\n\t\t\tRsaKeyHelper.parseKeyPair(pem);\n\t\t\tthrow new IllegalStateException(\"Expected IllegalArgumentException\");\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\tassertThat(ex.getMessage().contains(\"PEM\")).isTrue();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/RsaRawEncryptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Dave Syer\n *\n */\npublic class RsaRawEncryptorTests {\n\n\tprivate RsaRawEncryptor encryptor = new RsaRawEncryptor();\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tLONG_STRING = SHORT_STRING + SHORT_STRING + SHORT_STRING + SHORT_STRING;\n\t\tfor (int i = 0; i < 4; i++) {\n\t\t\tLONG_STRING = LONG_STRING + LONG_STRING;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void roundTrip() {\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripOeap() {\n\t\tthis.encryptor = new RsaRawEncryptor(RsaAlgorithm.OAEP);\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripLongString() {\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))).isEqualTo(LONG_STRING);\n\t}\n\n\t@Test\n\tpublic void roundTripLongStringOeap() {\n\t\tthis.encryptor = new RsaRawEncryptor(RsaAlgorithm.OAEP);\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))).isEqualTo(LONG_STRING);\n\t}\n\n\t@Test\n\tpublic void roundTrip2048Key() {\n\t\tString pemData = \"-----BEGIN RSA PRIVATE KEY-----\"\n\t\t\t\t+ \"MIIEpQIBAAKCAQEA5KHEkCudAHCKIUHKyW6Z8dMyQsKrLbpDe0wDzx9MBARcOoS9\"\n\t\t\t\t+ \"ZUjzXwK6p/0RM6aCp+b9kkr37QKQ9K/Am13sr0z8Mkn1Q2cvXiL5gbnY1nYGk8/m\"\n\t\t\t\t+ \"CBX3QEhH2UII4yJsDVx1xmcSorZaWmeNKor7Zl3SZaQpWTvlkMgQKwY8DZL6PPxt\"\n\t\t\t\t+ \"JRPeKmuUY6B59u5okh1G6Y9OnT2dVxAkqT8WgLHu6StxBmueJ272x2sUWUzoDhnP\"\n\t\t\t\t+ \"7JRqa7h7t6fml3o3Op1iCywCOFzCIcK6G/oG/WZ7tbBYkwQdDjn/9VMdKkkPufwq\"\n\t\t\t\t+ \"zt4S75NJygXDwDnNPiTVoaOwrRrL8ahgw6bFCQIDAQABAoIBAECIMHUI+l2fZj2Q\"\n\t\t\t\t+ \"1m4Ym7cYB320eKCFjHqGsCSMDuarXGTgBp1KA/dzS8ASvAI6I3LEzhm2s1fge420\"\n\t\t\t\t+ \"9cZksmOgdSa0nVeTDlmhwY8OJ9gQpDagXas2l/066Zy2+M8zbhAvYsbHXQk0MziF\"\n\t\t\t\t+ \"NeEmLWNtY+9wcINRVrCQ549dSSIDK6UX21oU6d1mrlnF5/bbbdDIM3dKok355jwx\"\n\t\t\t\t+ \"0HFY0tJIs1zArsBVoz3Ccu1MQEfnxEFM1LLPi5rE6cuHIOBinbD1OQ2R/HM2aukG\"\n\t\t\t\t+ \"Rk2m6F3wAieJ7zpt5yaHuuIedn8p8m2NVulXAjgkY2oQl3GGiDH/H7eZlrvQRg6E\"\n\t\t\t\t+ \"D8Bq+ykCgYEA+AfPXVeeVg3Qu0KsNrACek/o92BMY9g3GyPVGULGvq9seoNB86hj\"\n\t\t\t\t+ \"nXasqngBfTlOfJFiahoEzRBB9hIyo1zMw4x99pR8nGxhR3aU+v8EGftMABGHWsB9\"\n\t\t\t\t+ \"Jxj4YQH4fhi57iBa72QmNPbu/1o7y3SEe68E5PJ8KY3jc4xos8Vl658CgYEA6/pk\"\n\t\t\t\t+ \"t6WZII+9lpxQfePQDIlBWAphiQceh995bGXfDmX3vOVmPozix9/fUtF1TeKS/ypw\"\n\t\t\t\t+ \"u++Qmvj5oMsBVrjCyoOYfHKE2vGrLoEzkX/sPO65IsV00geZZoyCEKEE3USJfY46\"\n\t\t\t\t+ \"u0hs61oP8HJyLhLiYiGcFTzZ4nEvvEbiM4E/DlcCgYEA6S0OecZhiK08SpAHrvIR\"\n\t\t\t\t+ \"okN11PqnVkZyqAUr1a+9gI8TAKpdWmA4JlTnRuvDGqLBcsKLLwx+7voVyOyaxpH7\"\n\t\t\t\t+ \"vutZkHNQIw6Q9co5jS4qAPMLJBVWlq7X+eWzvB9KKeG9Cm1IkD4q3Sg4z79Y75D+\"\n\t\t\t\t+ \"6/hCNarxp29JIdwior81bikCgYEApp1P+b7pxGzZPvs1df2hCwjqY0BJJ5goPWVT\"\n\t\t\t\t+ \"dW7kNGVYqz4JmAafpOJz6yTLP2fHxHRxzrBSmKlMj/RmCJZBqv2Jb+zn0zMpW5eM\"\n\t\t\t\t+ \"EqKQ6WDgxSVH23fUHuz8dMNMDPL0ZPtEirGTfgVEFdCov9FDmGgErZYefVzPiI8/\"\n\t\t\t\t+ \"7X/HRtcCgYEApQ2YS+0DLPqaM0cC6/6hDr/jmHLFhHaV6DZR7M9HHDnMN2uMlOEa\"\n\t\t\t\t+ \"RYvXRMBjyQ7LQkwOj6K5k8MVrsDDM5dbekTBgcJMHfM9uViDkB0VPYULORmDJ20N\"\n\t\t\t\t+ \"MLowIAiSon2B2/isatY80YtFq+bRyvPOzjGvinHN3MU1GH/gFuS0fiw=\" + \"-----END RSA PRIVATE KEY-----\";\n\t\tRsaRawEncryptor encryptor_2048 = new RsaRawEncryptor(pemData);\n\t\tassertThat(encryptor_2048.decrypt(encryptor_2048.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTrip4096Key() {\n\t\tString pemData = \"-----BEGIN RSA PRIVATE KEY-----\"\n\t\t\t\t+ \"MIIJKAIBAAKCAgEAw/OIcO1pv8t/lhXwzc+CqCqAE8+2+BTWd6fHy8P2oGKZK0s3\"\n\t\t\t\t+ \"jxPWdZEbp1soGZobCIjEIuYuuPeinrTFOxtnf/JVfmzGnixRjWzQK0UiM/4z8GW6\"\n\t\t\t\t+ \"7+dzB0+QZlU+PGCL6xra4d3+5EsPQwTDjPJ4OhcA66hWACd3UJpvE2C14YdFkCP/\"\n\t\t\t\t+ \"CUxubz1l+8rFwEtMcw2bVUL/Mt+Sx1CHPFer17VK/sT4urwNG7y9R8WWvNQXgEwg\"\n\t\t\t\t+ \"0im+iJ0zf1u0SdUVj+Q1LwgNRoIx4vec2xAJ6xdqSx3Y3g2twWqUXUBb5K09ajIW\"\n\t\t\t\t+ \"Vuko5kWJVyx1x8LazU+0wQRLVJRYAiUOPLg7PdPAJWaAWmagnkAvl5bqCKi6sIc8\"\n\t\t\t\t+ \"+vKyrPx4VJH5KLsHx8020Wgch/LfHl/vvoHE7Oa81hnyMVsApvNCJdFbiMJ6r2z/\"\n\t\t\t\t+ \"eHqzjY8lzBQHNxh1XJys5teTJsi6N06gCc+OQRyw1FQ8KLgFlLPHNamfMnP5Ju0d\"\n\t\t\t\t+ \"Jv8GzQiMFjudjEYhkh2GPmRus1VYWDwDWhXwp28koWAanfih+Ujc2ZqNUS23hGWz\"\n\t\t\t\t+ \"KbCxRaAwSLqn3vkoYBeDyWWs1r0HnB6gACFaZIk38aiGyg7GjF0286Aq7USqNwKu\"\n\t\t\t\t+ \"Izm4kzIPFrHIbywKq7804J7wXUlaAgf0pNSndMD5OnwudzD+JHLTuOGFNdUCAwEA\"\n\t\t\t\t+ \"AQKCAgBYh2mIY6rYTS9adpUx1uPX6EOvL7QhhwCSVMoupF2Dfqhm5/e0+6hzu1h8\"\n\t\t\t\t+ \"FvIaBwbZpzi977MCPFdLTq6hErODGdBIawqdIbbCp3uxYO2gAeQjY0K+6pmMnwTF\"\n\t\t\t\t+ \"RxP0IUZ1tM9ZJnvnVoYRqFBVGKL607PFxGr+bNY6I1u1rIbf2sax5aFu6Qon1dyC\"\n\t\t\t\t+ \"ks0fIKXsgSRBtCAqMtpUlGxU9eMcdLrqOcGKVDWz52S4zWtZ6pSnkT1u1g9QF33R\"\n\t\t\t\t+ \"t3PPu6afOOJSWlftGBtDyM0kJ63jedO7FkQJprJu5SEctFwQB7jshq6TG4ov5xCy\"\n\t\t\t\t+ \"wtJ/quhBxBYM8ky6bL8KUQWKp02Tyfq0Fo+iwuLxM4N6LxVPFZ6R6jwvazm+ka4S\"\n\t\t\t\t+ \"sZAW/hnH3FdJEAyFcxzhelLdLUrjwrsWjmJBk0pMP5cEleYR8PQh2sHM8ZOX1T5f\"\n\t\t\t\t+ \"4zfyR66+tl1O81T7anbma8l1Wm/QSNZz+8QAM1iNuV+uLsWvmxLAc7NRgjDmiAMn\"\n\t\t\t\t+ \"8VhfUtl0ooOZYkDexqSNaWvIQG+S8Pl28gNxVXkXrXqBGPJn2ptROEJ1/AN1h4cv\"\n\t\t\t\t+ \"2CktVylRFpEI/hxXvKMaAu/tXtvoakvaTA8msl8Otrldsy3EGhgHrDTYIJUg/rRT\"\n\t\t\t\t+ \"TlbRkN/ycaOhA0d4HAewOGul3ss+EtBz+SQBzaWm2Inr8XOJoQKCAQEA4LwW7eGm\"\n\t\t\t\t+ \"MOYspFUbn2tMlnJAng9HKK42o2m6ShYAaQAoLX7LIkQYVS++9CiGCPpoSlwIJWE3\"\n\t\t\t\t+ \"N/qGx0i7REDm+wNu0/4acaMFI+qYtvjKiWwtMOBH3bw1C4/Isc60tFPkI7FEFCiF\"\n\t\t\t\t+ \"SiW3c+Z8B0/IRMb/YF5tZeuWUlAl7PQJ1rMcPUE4O4LXM4BG29hghVGGnp39YsOY\"\n\t\t\t\t+ \"b/6oBApTgdxCaSZhmhDwTMu97n75CK0xzA2vDtHn2Gu3zf4j6bsNot6/7wRtQBMg\"\n\t\t\t\t+ \"1e3kXuwGUZ08QZ7OqATUIZdCeK1PfxypontVh+0LeNjiDU8pW3Q8IMlDT96Fd5U+\"\n\t\t\t\t+ \"BgtjfHmwHXeBmQKCAQEA3zZS619O/IUoWN3rWT4hUSJE3S+FXXcaBaJ7H6r897cl\"\n\t\t\t\t+ \"ju+HSS2CLp/C9ftcQ9ef+pG2arLRZpONd5KhfRyjo0pNp3SwxklnIhNS9abBBCnN\"\n\t\t\t\t+ \"ojeYcVHOcSfmWGlUCQAvv5LeBPSS02pbCE5t/qadglvgKhHqSb2u+FgkdKrV0Mme\"\n\t\t\t\t+ \"sbVy+tyd4F1oBIS0wg1p3mHKvKfb4MEnUDvIvG8rCBUMvAWQmTiuyqFUiuqSwEMy\"\n\t\t\t\t+ \"LANFFV/ZoJ5194ruTXdelcoZjXhd128JJFNp6Jh4eg5OWoBS7e08QHbvUYBppDYO\"\n\t\t\t\t+ \"Iz0N1TipVK9uCqHHtbwIqqxyPVev3QJUYkpl5/tznQKCAQB9izV38F2J5Zu8tbq3\"\n\t\t\t\t+ \"pRZk2TCV280RwbjOMysZZg8WmTrYp4NNAiNhu0l+VgEClPibyavXTeauA+s0+sF6\"\n\t\t\t\t+ \"kJM4WKOaE9Kr9rjRZqWnWXazrFXWfwRGr3QmoE0qX2H9dvv0oHt6k2RalpVUTsas\"\n\t\t\t\t+ \"wvoKyewx5q5QiHoyQ4ncRDwWz3oQEhYa0K3tnFR5TfglofSFOZcqjD/lGKq9jxM1\"\n\t\t\t\t+ \"cVk8Km/NxHapQAw7Zn0yRqaR6ncH3WUaNpq4nadsU817Vdp86MkrSURHnhy8lje1\"\n\t\t\t\t+ \"chQOSGwD2qaymTBN/+twBBATr7iJNXf6K5akfruI1nccjbJntNR0iE/cypHqIISt\"\n\t\t\t\t+ \"AWzJAoIBAFDV5ZWkAIDm4EO+qpq5K2usk2/e49eDaIMd4qUHUXGMfCeVi1LvDjRA\"\n\t\t\t\t+ \"W2Sl0TYogqFF3+AoPjl9uj/RdHZQxto98H1yfwpwTs9CXErmRwRw9y2GIMj5LWBB\"\n\t\t\t\t+ \"aOQf0PUpgiFI2OrGf93cqHcLoD4WrPgmubnCnyxxa0o48Yrmy2Q/gB8vbSJ4fxxf\"\n\t\t\t\t+ \"92mbfbLBFNQaakeEKtbsXIZsADhtshHNPb1h7onuwy5S2sEsTlUegK77yCsDeVb3\"\n\t\t\t\t+ \"zBUH1WFsl257sGFRc/qvFYp4QuSfQxJA2BNiYaYUwjs+V1EWxitYACq206miSYCH\"\n\t\t\t\t+ \"v7xN9ntUS3cz2HNqrB/H1jN6aglnQOkCggEBAJb5FYvQCvw5PJM44nR6/U1cSlr4\"\n\t\t\t\t+ \"lRWcuFp7Xv5kWxSwM5115qic14fByh7DbaTHxxoPEhEA4aJ2QcDa7YWvabVc/VEV\"\n\t\t\t\t+ \"VacAAdg44+WSw6FNni18K53oOKAONgzSQlYUm/jgENIXi+5L0Yq7qAbnldiC6jXr\"\n\t\t\t\t+ \"yqbEwZjmpt8xsBLnl37k/LSLG1GUaYV8AK3s9UDs9/jv5RUrV96jiXed+7pYrjmj\"\n\t\t\t\t+ \"o1yJ4WAqouYHmOQCI3SeFCLT8GCdQ+uE74G5q+Yte6YT9jqSiGDjrst0bjtN640v\"\n\t\t\t\t+ \"YKRG3XK4AE9i4Oinnv/Ua95ql0syphn+CPW2ksmGon5/0mbK5qYsg47Hdls=\" + \"-----END RSA PRIVATE KEY-----\";\n\t\tRsaRawEncryptor encryptor_4096 = new RsaRawEncryptor(pemData);\n\t\tassertThat(encryptor_4096.decrypt(encryptor_4096.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\tprivate static final String SHORT_STRING = \"Bacon ipsum dolor sit amet tail pork loin pork chop filet mignon flank fatback tenderloin boudin shankle corned beef t-bone short ribs. Meatball capicola ball tip short loin beef ribs shoulder, kielbasa pork chop meatloaf biltong porchetta bresaola t-bone spare ribs. Andouille t-bone sausage ground round frankfurter venison. Ground round meatball chicken ribeye doner tongue porchetta.\";\n\n\tprivate static String LONG_STRING;\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/encrypt/RsaSecretEncryptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.encrypt;\n\nimport java.security.PublicKey;\nimport java.security.interfaces.RSAPublicKey;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.assertions.CryptoAssertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Dave Syer\n *\n */\npublic class RsaSecretEncryptorTests {\n\n\tprivate RsaSecretEncryptor encryptor = new RsaSecretEncryptor();\n\n\t@BeforeEach\n\tpublic void init() {\n\t\tLONG_STRING = SHORT_STRING + SHORT_STRING + SHORT_STRING + SHORT_STRING;\n\t\tfor (int i = 0; i < 4; i++) {\n\t\t\tLONG_STRING = LONG_STRING + LONG_STRING;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void roundTripKey() {\n\t\tPublicKey key = RsaKeyHelper.generateKeyPair().getPublic();\n\t\tString encoded = RsaKeyHelper.encodePublicKey((RSAPublicKey) key, \"application\");\n\t\tassertThat(RsaKeyHelper.parsePublicKey(encoded)).isEqualTo(key);\n\t}\n\n\t@Test\n\tpublic void roundTrip() {\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripWithSalt() {\n\t\tthis.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, \"somesalt\");\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripWithHexSalt() {\n\t\tthis.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, \"beefea\");\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripWithLongSalt() {\n\t\tthis.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, \"somesaltsomesaltsomesaltsomesaltsomesalt\");\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripOaep() {\n\t\tthis.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP);\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripOaepGcm() {\n\t\tthis.encryptor = new RsaSecretEncryptor(RsaAlgorithm.OAEP, true);\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripWithMixedAlgorithm() {\n\t\tRsaSecretEncryptor oaep = new RsaSecretEncryptor(RsaAlgorithm.OAEP);\n\t\tCryptoAssertions.assertThat(() -> oaep.decrypt(this.encryptor.encrypt(\"encryptor\")))\n\t\t\t.doesNotDecryptTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripWithMixedSalt() {\n\t\tRsaSecretEncryptor other = new RsaSecretEncryptor(this.encryptor.getPublicKey(), RsaAlgorithm.DEFAULT, \"salt\");\n\t\tCryptoAssertions.assertThat(() -> this.encryptor.decrypt(other.encrypt(\"encryptor\")))\n\t\t\t.doesNotDecryptTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripWithPublicKeyEncryption() {\n\t\tRsaSecretEncryptor encryptor = new RsaSecretEncryptor(this.encryptor.getPublicKey());\n\t\tRsaSecretEncryptor decryptor = this.encryptor;\n\t\tassertThat(decryptor.decrypt(encryptor.encrypt(\"encryptor\"))).isEqualTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void publicKeyCannotDecrypt() {\n\t\tRsaSecretEncryptor encryptor = new RsaSecretEncryptor(this.encryptor.getPublicKey());\n\t\tassertThat(encryptor.canDecrypt()).as(\"Encryptor schould not be able to decrypt\").isFalse();\n\t\tCryptoAssertions.assertThat(() -> encryptor.decrypt(encryptor.encrypt(\"encryptor\")))\n\t\t\t.doesNotDecryptTo(\"encryptor\");\n\t}\n\n\t@Test\n\tpublic void roundTripLongString() {\n\t\tassertThat(this.encryptor.decrypt(this.encryptor.encrypt(LONG_STRING))).isEqualTo(LONG_STRING);\n\t}\n\n\tprivate static final String SHORT_STRING = \"Bacon ipsum dolor sit amet tail pork loin pork chop filet mignon flank fatback tenderloin boudin shankle corned beef t-bone short ribs. Meatball capicola ball tip short loin beef ribs shoulder, kielbasa pork chop meatloaf biltong porchetta bresaola t-bone spare ribs. Andouille t-bone sausage ground round frankfurter venison. Ground round meatball chicken ribeye doner tongue porchetta.\";\n\n\tprivate static String LONG_STRING;\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/factory/PasswordEncoderFactoriesTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.factory;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class PasswordEncoderFactoriesTests {\n\n\tprivate PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\n\tprivate String rawPassword = \"password\";\n\n\t@Test\n\tpublic void encodeWhenDefaultThenBCryptUsed() {\n\t\tString encodedPassword = this.encoder.encode(this.rawPassword);\n\t\tassertThat(encodedPassword).startsWith(\"{bcrypt}\");\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenBCryptThenWorks() {\n\t\tString encodedPassword = \"{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenLdapThenWorks() {\n\t\tString encodedPassword = \"{ldap}{SSHA}igvD9lOiTXm16dmOw0YWRb9OjK2ThZvdQku2EQ==\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenMd4ThenWorks() {\n\t\tString encodedPassword = \"{MD4}{KYp8/QErWyQemYazZQ8UnWWfbGbkYkVC8qMi0duoA84=}152ce09d3261d2b53cac55b2ea4d1c7a\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenMd5ThenWorks() {\n\t\tString encodedPassword = \"{MD5}{aRYR+Yp2xSqtgF+vtjH6jNda6M083iEbP+zCFjLt9IA=}905e382a25eed53e22224223b3581092\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenNoopThenWorks() {\n\t\tString encodedPassword = \"{noop}password\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenPbkdf2ThenWorks() {\n\t\tString encodedPassword = \"{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenPbkdf2SpringSecurity_v5_8ThenWorks() {\n\t\tString encodedPassword = \"{pbkdf2@SpringSecurity_v5_8}fefe5120467e5d4ccff442dbb2fa86d276262d97435c0c54e5eebced51ffd144fcb05eb53fea2677216c4f3250010006\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenSCryptThenWorks() {\n\t\tString encodedPassword = \"{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenSCryptSpringSecurity_v5_8ThenWorks() {\n\t\tString encodedPassword = \"{scrypt@SpringSecurity_v5_8}$e0801$vSriIassJwvdNBF1vpSoCenqBxvpT4e+NcLKVsrOVpaZfyRfpUJ6KctkpmketuacWelLU5njpILXM9LLkMXLMw==$vIQQljL257HOcnumyiy1hJBGYHmoXgENIh+NkFvmrGY=\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenSHA1ThenWorks() {\n\t\tString encodedPassword = \"{SHA-1}{6581QepZz2qd8jVrT2QYPVtK8DuM2n45dVslmc3UTWc=}4f31573948ddbfb8ac9dd80107dfad13fd8f2454\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenSHA256ThenWorks() {\n\t\tString encodedPassword = \"{SHA-256}{UisHp3pFSMqcqrhQsrhR+hspIG0SyMDyDW/XtY+t6nA=}a98efbaf59277bfd1837c33fd4fde67de5bcfd2205bcba0992f6fc32b03a8f88\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenSha256ThenWorks() {\n\t\tString encodedPassword = \"{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenArgon2ThenWorks() {\n\t\tString encodedPassword = \"{argon2}$argon2d$v=19$m=1024,t=1,p=1$c29tZXNhbHQ$Li5eBf5XrCz0cuzQRe9oflYqmA/VAzmzichw4ZYrvEU\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenArgon2SpringSecurity_v5_8ThenWorks() {\n\t\tString encodedPassword = \"{argon2@SpringSecurity_v5_8}$argon2id$v=19$m=16384,t=2,p=1$v7fN5p91BQbdbA2HfdSPRg$MULpa02CO/6FKfqwuerCFvS7OhMxGFCKUOoWfzt86Rc\";\n\t\tassertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/keygen/Base64StringKeyGeneratorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @author Andrey Litvitski\n * @since 5.0\n */\npublic class Base64StringKeyGeneratorTests {\n\n\t@Test\n\tpublic void constructorIntWhenEqual0ThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Base64StringKeyGenerator(0));\n\t}\n\n\t@Test\n\tpublic void constructorEncoderWhenEncoderNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Base64StringKeyGenerator(null));\n\t}\n\n\t@Test\n\tpublic void generateKeyWhenDefaultConstructorThen32Bytes() {\n\t\tString result = new Base64StringKeyGenerator().generateKey();\n\t\tassertThat(Base64.getDecoder().decode(result.getBytes())).hasSize(32);\n\t}\n\n\t@Test\n\tpublic void generateKeyWhenCustomKeySizeThen32Bytes() {\n\t\tint size = 40;\n\t\tString result = new Base64StringKeyGenerator(size).generateKey();\n\t\tassertThat(Base64.getDecoder().decode(result.getBytes())).hasSize(size);\n\t}\n\n\t@Test\n\tpublic void generateKeyWhenBase64Then32Bytes() {\n\t\tString result = new Base64StringKeyGenerator(Base64.getUrlEncoder()).generateKey();\n\t\tassertThat(Base64.getUrlDecoder().decode(result.getBytes())).hasSize(32);\n\t}\n\n\t@Test\n\tpublic void generateKeyWhenBase64AndCustomKeySizeThen32Bytes() {\n\t\tint size = 40;\n\t\tString result = new Base64StringKeyGenerator(Base64.getUrlEncoder(), size).generateKey();\n\t\tassertThat(Base64.getUrlDecoder().decode(result.getBytes())).hasSize(size);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/keygen/KeyGeneratorsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.keygen;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.codec.Hex;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class KeyGeneratorsTests {\n\n\t@Test\n\tpublic void secureRandom() {\n\t\tBytesKeyGenerator keyGenerator = KeyGenerators.secureRandom();\n\t\tassertThat(keyGenerator.getKeyLength()).isEqualTo(8);\n\t\tbyte[] key = keyGenerator.generateKey();\n\t\tassertThat(key).hasSize(8);\n\t\tbyte[] key2 = keyGenerator.generateKey();\n\t\tassertThat(Arrays.equals(key, key2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void secureRandomCustomLength() {\n\t\tBytesKeyGenerator keyGenerator = KeyGenerators.secureRandom(21);\n\t\tassertThat(keyGenerator.getKeyLength()).isEqualTo(21);\n\t\tbyte[] key = keyGenerator.generateKey();\n\t\tassertThat(key).hasSize(21);\n\t\tbyte[] key2 = keyGenerator.generateKey();\n\t\tassertThat(Arrays.equals(key, key2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void shared() {\n\t\tBytesKeyGenerator keyGenerator = KeyGenerators.shared(21);\n\t\tassertThat(keyGenerator.getKeyLength()).isEqualTo(21);\n\t\tbyte[] key = keyGenerator.generateKey();\n\t\tassertThat(key).hasSize(21);\n\t\tbyte[] key2 = keyGenerator.generateKey();\n\t\tassertThat(Arrays.equals(key, key2)).isTrue();\n\t}\n\n\t@Test\n\tpublic void string() {\n\t\tStringKeyGenerator keyGenerator = KeyGenerators.string();\n\t\tString hexStringKey = keyGenerator.generateKey();\n\t\tassertThat(hexStringKey).hasSize(16);\n\t\tassertThat(Hex.decode(hexStringKey)).hasSize(8);\n\t\tString hexStringKey2 = keyGenerator.generateKey();\n\t\tassertThat(hexStringKey.equals(hexStringKey2)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/AbstractPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.BeforeEach;\n\n/**\n * Test {@link AbstractPasswordEncoder} (not intended to be extended).\n *\n * @author Rob Winch\n * @see AbstractPasswordEncoderValidationTests\n */\nfinal class AbstractPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tsetEncoder(new AbstractPasswordEncoder() {\n\n\t\t\t@Override\n\t\t\tprotected byte[] encodedNonNullPassword(CharSequence rawPassword, byte[] salt) {\n\t\t\t\treturn new byte[0];\n\t\t\t}\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/AbstractPasswordEncoderValidationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * A base class for other tests to perform validation of the arguments to\n * {@link PasswordEncoder} instances in a consistent way.\n *\n * @author Rob Winch\n */\npublic abstract class AbstractPasswordEncoderValidationTests {\n\n\tprivate PasswordEncoder encoder;\n\n\tprotected void setEncoder(PasswordEncoder encoder) {\n\t\tthis.encoder = encoder;\n\t}\n\n\tprotected <T extends PasswordEncoder> T getEncoder(Class<T> clazz) {\n\t\treturn getEncoder();\n\t}\n\n\tprotected <T extends PasswordEncoder> T getEncoder() {\n\t\treturn (T) this.encoder;\n\t}\n\n\t@Test\n\tvoid encodeWhenNullThenNull() {\n\t\tassertThat(this.encoder.encode(null)).isNull();\n\t}\n\n\t@Test\n\tvoid matchesWhenEncodedPasswordNullThenFalse() {\n\t\tassertThat(this.encoder.matches(\"raw\", null)).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenEncodedPasswordEmptyThenFalse() {\n\t\tassertThat(this.encoder.matches(\"raw\", \"\")).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenRawPasswordNullThenFalse() {\n\t\tassertThat(this.encoder.matches(null, this.encoder.encode(\"password\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenRawPasswordEmptyThenFalse() {\n\t\tassertThat(this.encoder.matches(\"\", this.encoder.encode(\"password\"))).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/AbstractValidatingPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.BeforeEach;\n\n/**\n * Test {@link AbstractValidatingPasswordEncoder}.\n *\n * @author Rob Winch\n */\nclass AbstractValidatingPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tsetEncoder(new AbstractValidatingPasswordEncoder() {\n\t\t\t@Override\n\t\t\tprotected String encodeNonNullPassword(String rawPassword) {\n\t\t\t\treturn \"\";\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected boolean matchesNonNull(String rawPassword, String encodedPassword) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/DelegatingPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.util.HashMap;\nimport java.util.Hashtable;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @author Michael Simons\n * @author heowc\n * @author Jihoon Cha\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@Mock\n\tprivate PasswordEncoder bcrypt;\n\n\t@Mock\n\tprivate PasswordEncoder noop;\n\n\t@Mock\n\tprivate PasswordEncoder invalidId;\n\n\tprivate String bcryptId = \"bcrypt\";\n\n\tprivate String rawPassword = \"password\";\n\n\tprivate String encodedPassword = \"ENCODED-PASSWORD\";\n\n\tprivate String bcryptEncodedPassword = \"{bcrypt}\" + this.encodedPassword;\n\n\tprivate String noopEncodedPassword = \"{noop}\" + this.encodedPassword;\n\n\tprivate Map<String, PasswordEncoder> delegates;\n\n\tprivate DelegatingPasswordEncoder onlySuffixPasswordEncoder;\n\n\tprivate static final String NO_PASSWORD_ENCODER_MAPPED = \"There is no password encoder mapped for the id 'unmapped'. \"\n\t\t\t+ \"Check your configuration to ensure it matches one of the registered encoders.\";\n\n\tprivate static final String NO_PASSWORD_ENCODER_PREFIX = \"Given that there is no default password encoder configured, \"\n\t\t\t+ \"each password must have a password encoding prefix. Please either prefix this password with '{noop}' or set a default password encoder in `DelegatingPasswordEncoder`.\";\n\n\tprivate static final String MALFORMED_PASSWORD_ENCODER_PREFIX = \"The name of the password encoder is improperly formatted or incomplete. The format should be '{ENCODER}password'.\";\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.delegates = new HashMap<>();\n\t\tthis.delegates.put(this.bcryptId, this.bcrypt);\n\t\tthis.delegates.put(\"noop\", this.noop);\n\t\tsetEncoder(new DelegatingPasswordEncoder(this.bcryptId, this.delegates));\n\t\tthis.onlySuffixPasswordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates, \"\", \"$\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenIdForEncodeNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingPasswordEncoder(null, this.delegates));\n\t}\n\n\t@Test\n\tpublic void constructorWhenIdForEncodeDoesNotExistThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingPasswordEncoder(this.bcryptId + \"INVALID\", this.delegates));\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrefixIsNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingPasswordEncoder(this.bcryptId, this.delegates, null, \"$\"));\n\t}\n\n\t@Test\n\tpublic void constructorWhenSuffixIsNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingPasswordEncoder(this.bcryptId, this.delegates, \"$\", null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrefixIsEmpty() {\n\t\tassertThat(new DelegatingPasswordEncoder(this.bcryptId, this.delegates, \"\", \"$\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void constructorWhenSuffixIsEmpty() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingPasswordEncoder(this.bcryptId, this.delegates, \"$\", \"\"));\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrefixAndSuffixAreEmpty() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingPasswordEncoder(this.bcryptId, this.delegates, \"\", \"\"));\n\t}\n\n\t@Test\n\tpublic void constructorWhenIdContainsPrefixThenIllegalArgumentException() {\n\t\tthis.delegates.put('{' + this.bcryptId, this.bcrypt);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingPasswordEncoder(this.bcryptId, this.delegates));\n\t}\n\n\t@Test\n\tpublic void constructorWhenIdContainsSuffixThenIllegalArgumentException() {\n\t\tthis.delegates.put(this.bcryptId + '$', this.bcrypt);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingPasswordEncoder(this.bcryptId, this.delegates, \"\", \"$\"));\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrefixContainsSuffixThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingPasswordEncoder(this.bcryptId, this.delegates, \"$\", \"$\"));\n\t}\n\n\t@Test\n\tpublic void setDefaultPasswordEncoderForMatchesWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> getEncoder(DelegatingPasswordEncoder.class).setDefaultPasswordEncoderForMatches(null));\n\t}\n\n\t@Test\n\tpublic void matchesWhenCustomDefaultPasswordEncoderForMatchesThenDelegates() {\n\t\tString encodedPassword = \"{unmapped}\" + this.rawPassword;\n\t\tgetEncoder(DelegatingPasswordEncoder.class).setDefaultPasswordEncoderForMatches(this.invalidId);\n\t\tassertThat(getEncoder().matches(this.rawPassword, encodedPassword)).isFalse();\n\t\tverify(this.invalidId).matches(this.rawPassword, encodedPassword);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n\t@Test\n\tpublic void encodeWhenValidThenUsesIdForEncode() {\n\t\tgiven(this.bcrypt.encode(this.rawPassword)).willReturn(this.encodedPassword);\n\t\tassertThat(getEncoder().encode(this.rawPassword)).isEqualTo(this.bcryptEncodedPassword);\n\t}\n\n\t@Test\n\tpublic void encodeWhenValidBySpecifyDelegatingPasswordEncoderThenUsesIdForEncode() {\n\t\tgiven(this.bcrypt.encode(this.rawPassword)).willReturn(this.encodedPassword);\n\t\tassertThat(this.onlySuffixPasswordEncoder.encode(this.rawPassword)).isEqualTo(\"bcrypt$\" + this.encodedPassword);\n\t}\n\n\t@Test\n\tpublic void matchesWhenBCryptThenDelegatesToBCrypt() {\n\t\tgiven(this.bcrypt.matches(this.rawPassword, this.encodedPassword)).willReturn(true);\n\t\tassertThat(getEncoder().matches(this.rawPassword, this.bcryptEncodedPassword)).isTrue();\n\t\tverify(this.bcrypt).matches(this.rawPassword, this.encodedPassword);\n\t\tverifyNoMoreInteractions(this.noop);\n\t}\n\n\t@Test\n\tpublic void matchesWhenBCryptBySpecifyDelegatingPasswordEncoderThenDelegatesToBCrypt() {\n\t\tgiven(this.bcrypt.matches(this.rawPassword, this.encodedPassword)).willReturn(true);\n\t\tassertThat(this.onlySuffixPasswordEncoder.matches(this.rawPassword, \"bcrypt$\" + this.encodedPassword)).isTrue();\n\t\tverify(this.bcrypt).matches(this.rawPassword, this.encodedPassword);\n\t\tverifyNoMoreInteractions(this.noop);\n\t}\n\n\t@Test\n\tpublic void matchesWhenNoopThenDelegatesToNoop() {\n\t\tgiven(this.noop.matches(this.rawPassword, this.encodedPassword)).willReturn(true);\n\t\tassertThat(getEncoder().matches(this.rawPassword, this.noopEncodedPassword)).isTrue();\n\t\tverify(this.noop).matches(this.rawPassword, this.encodedPassword);\n\t\tverifyNoMoreInteractions(this.bcrypt);\n\t}\n\n\t@Test\n\tpublic void matchesWhenUnMappedThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> getEncoder().matches(this.rawPassword, \"{unmapped}\" + this.rawPassword))\n\t\t\t.withMessage(NO_PASSWORD_ENCODER_MAPPED);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n\t@Test\n\tpublic void matchesWhenNoClosingPrefixStringThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> getEncoder().matches(this.rawPassword, \"{bcrypt\" + this.rawPassword))\n\t\t\t.withMessage(MALFORMED_PASSWORD_ENCODER_PREFIX);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n\t@Test\n\tpublic void matchesWhenNoStartingPrefixStringThenFalse() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> getEncoder().matches(this.rawPassword, \"bcrypt}\" + this.rawPassword))\n\t\t\t.withMessage(MALFORMED_PASSWORD_ENCODER_PREFIX);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n\t@Test\n\tpublic void matchesWhenNoIdStringThenFalse() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> getEncoder().matches(this.rawPassword, \"{}\" + this.rawPassword))\n\t\t\t.withMessage(MALFORMED_PASSWORD_ENCODER_PREFIX);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n\t@Test\n\tpublic void matchesWhenPrefixInMiddleThenFalse() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> getEncoder().matches(this.rawPassword, \"invalid\" + this.bcryptEncodedPassword))\n\t\t\t.isInstanceOf(IllegalArgumentException.class)\n\t\t\t.withMessage(MALFORMED_PASSWORD_ENCODER_PREFIX);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n\t@Test\n\tpublic void matchesWhenIdIsNullThenFalse() {\n\t\tthis.delegates = new Hashtable<>(this.delegates);\n\t\tDelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> passwordEncoder.matches(this.rawPassword, this.rawPassword))\n\t\t\t.withMessage(NO_PASSWORD_ENCODER_PREFIX);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n\t@Test\n\tpublic void matchesWhenNullIdThenDelegatesToInvalidId() {\n\t\tthis.delegates.put(null, this.invalidId);\n\t\tsetEncoder(new DelegatingPasswordEncoder(this.bcryptId, this.delegates));\n\t\tgiven(this.invalidId.matches(this.rawPassword, this.encodedPassword)).willReturn(true);\n\t\tassertThat(getEncoder().matches(this.rawPassword, this.encodedPassword)).isTrue();\n\t\tverify(this.invalidId).matches(this.rawPassword, this.encodedPassword);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenEncodedPasswordNullThenFalse() {\n\t\tassertThat(getEncoder().upgradeEncoding(null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenNullIdThenTrue() {\n\t\tassertThat(getEncoder().upgradeEncoding(this.encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenIdInvalidFormatThenTrue() {\n\t\tassertThat(getEncoder().upgradeEncoding(\"{bcrypt\" + this.encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenSameIdAndEncoderFalseThenEncoderDecidesFalse() {\n\t\tassertThat(getEncoder().upgradeEncoding(this.bcryptEncodedPassword)).isFalse();\n\t\tverify(this.bcrypt).upgradeEncoding(this.encodedPassword);\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenSameIdAndEncoderTrueThenEncoderDecidesTrue() {\n\t\tgiven(this.bcrypt.upgradeEncoding(any())).willReturn(true);\n\t\tassertThat(getEncoder().upgradeEncoding(this.bcryptEncodedPassword)).isTrue();\n\t\tverify(this.bcrypt).upgradeEncoding(this.encodedPassword);\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenDifferentIdThenTrue() {\n\t\tassertThat(getEncoder().upgradeEncoding(this.noopEncodedPassword)).isTrue();\n\t\tverifyNoMoreInteractions(this.bcrypt);\n\t}\n\n\t@Test\n\tvoid matchesShouldThrowIllegalArgumentExceptionWhenNoPasswordEncoderIsMappedForTheId() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> getEncoder().matches(\"rawPassword\", \"prefixEncodedPassword\"))\n\t\t\t.isInstanceOf(IllegalArgumentException.class)\n\t\t\t.withMessage(NO_PASSWORD_ENCODER_PREFIX);\n\t\tverifyNoMoreInteractions(this.bcrypt, this.noop);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/DigesterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.codec.Utf8;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class DigesterTests {\n\n\t@Test\n\tpublic void digestIsCorrectFor3Iterations() {\n\t\tDigester digester = new Digester(\"SHA-1\", 3);\n\t\tbyte[] result = digester.digest(Utf8.encode(\"text\"));\n\t\t// echo -n text | openssl sha1 -binary | openssl sha1 -binary | openssl sha1\n\t\tassertThat(new String(Hex.encode(result))).isEqualTo(\"3cfa28da425eca5b894f0af2b158adf7001e000f\");\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/LdapShaPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.keygen.KeyGenerators;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link LdapShaPasswordEncoder}.\n *\n * @author Luke Taylor\n */\n@SuppressWarnings(\"deprecation\")\npublic class LdapShaPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tsetEncoder(new LdapShaPasswordEncoder());\n\t}\n\n\t@Test\n\tpublic void invalidPasswordFails() {\n\t\tassertThat(getEncoder().matches(\"wrongpassword\", \"{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void invalidSaltedPasswordFails() {\n\t\tassertThat(getEncoder().matches(\"wrongpassword\", \"{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX\")).isFalse();\n\t\tassertThat(getEncoder().matches(\"wrongpassword\", \"{SSHA}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd\")).isFalse();\n\t}\n\n\t/**\n\t * Test values generated by 'slappasswd -h {SHA} -s boabspasswurd'\n\t */\n\t@Test\n\tpublic void validPasswordSucceeds() {\n\t\tLdapShaPasswordEncoder ldap = getEncoder();\n\t\tldap.setForceLowerCasePrefix(false);\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=\")).isTrue();\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=\")).isTrue();\n\t\tldap.setForceLowerCasePrefix(true);\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=\")).isTrue();\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=\")).isTrue();\n\t}\n\n\t/**\n\t * Test values generated by 'slappasswd -s boabspasswurd'\n\t */\n\t@Test\n\tpublic void validSaltedPasswordSucceeds() {\n\t\tLdapShaPasswordEncoder ldap = getEncoder();\n\t\tldap.setForceLowerCasePrefix(false);\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX\")).isTrue();\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd\")).isTrue();\n\t\tldap.setForceLowerCasePrefix(true);\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX\")).isTrue();\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd\")).isTrue();\n\t}\n\n\t@Test\n\t// SEC-1031\n\tpublic void fullLengthOfHashIsUsedInComparison() {\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX\")).isTrue();\n\t\t// Change the first hash character from '2' to '3'\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{SSHA}35ro4PKC8jhQZ26jVsozhX/xaP0suHgX\")).isFalse();\n\t\t// Change the last hash character from 'X' to 'Y'\n\t\tassertThat(getEncoder().matches(\"boabspasswurd\", \"{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgY\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void correctPrefixCaseIsUsed() {\n\t\tLdapShaPasswordEncoder ldap = getEncoder();\n\t\tldap.setForceLowerCasePrefix(false);\n\t\tassertThat(ldap.encode(\"somepassword\").startsWith(\"{SSHA}\"));\n\t\tldap.setForceLowerCasePrefix(true);\n\t\tassertThat(ldap.encode(\"somepassword\").startsWith(\"{ssha}\"));\n\t\tsetEncoder(new LdapShaPasswordEncoder(KeyGenerators.shared(0)));\n\t\tldap.setForceLowerCasePrefix(false);\n\t\tassertThat(getEncoder().encode(\"somepassword\").startsWith(\"{SHA}\"));\n\t\tldap.setForceLowerCasePrefix(true);\n\t\tassertThat(getEncoder().encode(\"somepassword\").startsWith(\"{SSHA}\"));\n\t}\n\n\t@Test\n\tpublic void invalidPrefixIsRejected() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> getEncoder().matches(\"somepassword\", \"{MD9}xxxxxxxxxx\"));\n\t}\n\n\t@Test\n\tpublic void malformedPrefixIsRejected() {\n\t\t// No right brace\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> getEncoder().matches(\"somepassword\", \"{SSHA25ro4PKC8jhQZ26jVsozhX/xaP0suHgX\"));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/Md4PasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"deprecation\")\npublic class Md4PasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tsetEncoder(new Md4PasswordEncoder());\n\t}\n\n\t@Test\n\tpublic void matchesWhenEncodedPasswordNullThenFalse() {\n\t\tassertThat(getEncoder().matches(\"raw\", null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenEncodedPasswordEmptyThenFalse() {\n\t\tassertThat(getEncoder().matches(\"raw\", \"\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void testEncodeUnsaltedPassword() {\n\t\tMd4PasswordEncoder md4 = getEncoder();\n\t\tmd4.setEncodeHashAsBase64(true);\n\t\tassertThat(md4.matches(\"ww_uni123\", \"8zobtq72iAt0W6KNqavGwg==\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void testEncodeSaltedPassword() {\n\t\tMd4PasswordEncoder md4 = getEncoder();\n\t\tmd4.setEncodeHashAsBase64(true);\n\t\tassertThat(md4.matches(\"ww_uni123\", \"{Alan K Stewart}ZplT6P5Kv6Rlu6W4FIoYNA==\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void testNonAsciiPasswordHasCorrectHash() {\n\t\tassertThat(getEncoder().matches(\"\\u4F60\\u597d\", \"a7f1196539fd1f85f754ffd185b16e6e\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void testEncodedMatches() {\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = getEncoder().encode(rawPassword);\n\t\tassertThat(getEncoder().matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void javadocWhenHasSaltThenMatches() {\n\t\tassertThat(getEncoder().matches(\"password\", \"{thisissalt}6cc7924dad12ade79dfb99e424f25260\"));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/MessageDigestPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * <p>\n * TestCase for Md5PasswordEncoder.\n * </p>\n *\n * @author colin sampaleanu\n * @author Ben Alex\n * @author Ray Krueger\n * @author Luke Taylor\n */\n@SuppressWarnings(\"deprecation\")\npublic class MessageDigestPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tsetEncoder(new MessageDigestPasswordEncoder(\"MD5\"));\n\t}\n\n\t@Test\n\tpublic void md5BasicFunctionality() {\n\t\tString raw = \"abc123\";\n\t\tassertThat(getEncoder().matches(raw, \"{THIS_IS_A_SALT}a68aafd90299d0b137de28fb4bb68573\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void md5NonAsciiPasswordHasCorrectHash() {\n\t\t// $ echo -n \"??\" | md5\n\t\t// 7eca689f0d3389d9dea66ae112e5cfd7\n\t\tassertThat(getEncoder().matches(\"\\u4F60\\u597d\", \"7eca689f0d3389d9dea66ae112e5cfd7\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void md5Base64() {\n\t\tMessageDigestPasswordEncoder pe = getEncoder();\n\t\tpe.setEncodeHashAsBase64(true);\n\t\tassertThat(getEncoder().matches(\"abc123\", \"{THIS_IS_A_SALT}poqv2QKZ0LE33ij7S7aFcw==\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void md5StretchFactorIsProcessedCorrectly() {\n\t\tMessageDigestPasswordEncoder pe = getEncoder();\n\t\tpe.setIterations(2);\n\t\t// Calculate value using:\n\t\t// echo -n password{salt} | openssl md5 -binary | openssl md5\n\t\tassertThat(getEncoder().matches(\"password\", \"{salt}eb753fb0c370582b4ee01b30f304b9fc\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void md5MatchesWhenNullSalt() {\n\t\tassertThat(getEncoder().matches(\"password\", \"5f4dcc3b5aa765d61d8327deb882cf99\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void md5MatchesWhenEmptySalt() {\n\t\tassertThat(getEncoder().matches(\"password\", \"{}f1026a66095fc2058c1f8771ed05d6da\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void md5MatchesWhenHasSalt() {\n\t\tassertThat(getEncoder().matches(\"password\", \"{salt}ce421738b1c5540836bdc8ff707f1572\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void md5EncodeThenMatches() {\n\t\tString rawPassword = \"password\";\n\t\tString encode = getEncoder().encode(rawPassword);\n\t\tassertThat(getEncoder().matches(rawPassword, encode)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testBasicFunctionality() {\n\t\tsetEncoder(new MessageDigestPasswordEncoder(\"SHA-1\"));\n\t\tString raw = \"abc123\";\n\t\tassertThat(getEncoder().matches(raw, \"{THIS_IS_A_SALT}b2f50ffcbd3407fe9415c062d55f54731f340d32\"));\n\t}\n\n\t@Test\n\tpublic void testBase64() {\n\t\tMessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder(\"SHA-1\");\n\t\tpe.setEncodeHashAsBase64(true);\n\t\tString raw = \"abc123\";\n\t\tassertThat(getEncoder().matches(raw, \"{THIS_IS_A_SALT}b2f50ffcbd3407fe9415c062d55f54731f340d32\"));\n\t}\n\n\t@Test\n\tpublic void test256() {\n\t\tMessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder(\"SHA-1\");\n\t\tString raw = \"abc123\";\n\t\tassertThat(getEncoder().matches(raw,\n\t\t\t\t\"{THIS_IS_A_SALT}4b79b7de23eb23b78cc5ede227d532b8a51f89b2ec166f808af76b0dbedc47d7\"));\n\t}\n\n\t@Test\n\tpublic void testInvalidStrength() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> new MessageDigestPasswordEncoder(\"SHA-666\"));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/NoOpPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.BeforeEach;\n\n/**\n * Test {@link NoOpPasswordEncoder}.\n *\n * @author Rob Winch\n */\nclass NoOpPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tsetEncoder(NoOpPasswordEncoder.getInstance());\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/PasswordEncoderUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class PasswordEncoderUtilsTests {\n\n\t@Test\n\tpublic void equalsWhenDifferentLengthThenFalse() {\n\t\tassertThat(PasswordEncoderUtils.equals(\"abc\", \"a\")).isFalse();\n\t\tassertThat(PasswordEncoderUtils.equals(\"a\", \"abc\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void equalsWhenNullAndNotEmptyThenFalse() {\n\t\tassertThat(PasswordEncoderUtils.equals(null, \"a\")).isFalse();\n\t\tassertThat(PasswordEncoderUtils.equals(\"a\", null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void equalsWhenNullAndNullThenTrue() {\n\t\tassertThat(PasswordEncoderUtils.equals(null, null)).isTrue();\n\t}\n\n\t@Test\n\tpublic void equalsWhenNullAndEmptyThenFalse() {\n\t\tassertThat(PasswordEncoderUtils.equals(null, \"\")).isFalse();\n\t\tassertThat(PasswordEncoderUtils.equals(\"\", null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void equalsWhenNotEmptyAndEmptyThenFalse() {\n\t\tassertThat(PasswordEncoderUtils.equals(\"abc\", \"\")).isFalse();\n\t\tassertThat(PasswordEncoderUtils.equals(\"\", \"abc\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void equalsWhenEmptyAndEmptyThenTrue() {\n\t\tassertThat(PasswordEncoderUtils.equals(\"\", \"\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void equalsWhenDifferentCaseThenFalse() {\n\t\tassertThat(PasswordEncoderUtils.equals(\"aBc\", \"abc\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void equalsWhenSameThenTrue() {\n\t\tassertThat(PasswordEncoderUtils.equals(\"abcdef\", \"abcdef\")).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\npublic class Pbkdf2PasswordEncoderTests {\n\n\tprivate Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder(\"secret\", 8, 185000, 256);\n\n\tprivate Pbkdf2PasswordEncoder encoderSalt16 = new Pbkdf2PasswordEncoder(\"\", 16, 185000, 256);\n\n\tprivate Pbkdf2PasswordEncoder[] encoders = new Pbkdf2PasswordEncoder[] { this.encoder, this.encoderSalt16 };\n\n\t@Test\n\tpublic void encodedLengthSuccess() {\n\t\t// encode output is an hex coded String so with 2 chars per encoding result byte\n\t\t// (ie. 1 char for 4 bits).\n\t\t// The encoding result size is : (saltLength * 8) bits + hashWith bits.\n\t\tassertThat(this.encoder.encode(\"password\")).hasSize((8 * 8 + 256) / 4);\n\t\tassertThat(this.encoderSalt16.encode(\"password\")).hasSize((16 * 8 + 256) / 4);\n\t}\n\n\t@Test\n\tpublic void matches() {\n\t\tString result = this.encoder.encode(\"password\");\n\t\tassertThat(result.equals(\"password\")).isFalse();\n\t\tassertThat(this.encoder.matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenCustomSaltLengthThenSuccess() {\n\t\tString result = this.encoderSalt16.encode(\"password\");\n\t\tassertThat(result.equals(\"password\")).isFalse();\n\t\tassertThat(this.encoderSalt16.matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesLengthChecked() {\n\t\tString result = this.encoder.encode(\"password\");\n\t\tassertThat(this.encoder.matches(\"password\", result.substring(0, result.length() - 2))).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesLengthCheckedWhenCustomSaltLengthThenSuccess() {\n\t\tString result = this.encoderSalt16.encode(\"password\");\n\t\tassertThat(this.encoderSalt16.matches(\"password\", result.substring(0, result.length() - 2))).isFalse();\n\t}\n\n\t@Test\n\tpublic void notMatches() {\n\t\tString result = this.encoder.encode(\"password\");\n\t\tassertThat(this.encoder.matches(\"bogus\", result)).isFalse();\n\t}\n\n\t@Test\n\tpublic void notMatchesWhenCustomSaltLengthThenSuccess() {\n\t\tString result = this.encoderSalt16.encode(\"password\");\n\t\tassertThat(this.encoderSalt16.matches(\"bogus\", result)).isFalse();\n\t}\n\n\t@Test\n\tpublic void encodeSamePasswordMultipleTimesDiffers() {\n\t\tString password = \"password\";\n\t\tString encodeFirst = this.encoder.encode(password);\n\t\tString encodeSecond = this.encoder.encode(password);\n\t\tassertThat(encodeFirst).isNotEqualTo(encodeSecond);\n\t}\n\n\t@Test\n\tpublic void encodeSamePasswordMultipleTimesWhenCustomSaltLengthThenDiffers() {\n\t\tString password = \"password\";\n\t\tString encodeFirst = this.encoderSalt16.encode(password);\n\t\tString encodeSecond = this.encoderSalt16.encode(password);\n\t\tassertThat(encodeFirst).isNotEqualTo(encodeSecond);\n\t}\n\n\t@Test\n\tpublic void passivity() {\n\t\tString encodedPassword = \"ab1146a8458d4ce4e65789e5a3f60e423373cfa10b01abd23739e5ae2fdc37f8e9ede4ae6da65264\";\n\t\tString rawPassword = \"password\";\n\t\tassertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void passivityWhenCustomSaltLengthThenSuccess() {\n\t\tString encodedPassword = \"0123456789abcdef0123456789abcdef10d883c2a0e653c97175c4a2583a7f1fd3301b319a7657d95f75365ea7c04ce1\";\n\t\tString rawPassword = \"password\";\n\t\tassertThat(this.encoderSalt16.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void migrate() {\n\t\tfinal int saltLength = KeyGenerators.secureRandom().getKeyLength();\n\t\tString encodedPassword = \"ab1146a8458d4ce4e65789e5a3f60e423373cfa10b01abd23739e5ae2fdc37f8e9ede4ae6da65264\";\n\t\tString originalEncodedPassword = \"ab1146a8458d4ce4ab1146a8458d4ce4e65789e5a3f60e423373cfa10b01abd23739e5ae2fdc37f8e9ede4ae6da65264\";\n\t\tbyte[] originalBytes = Hex.decode(originalEncodedPassword);\n\t\tbyte[] fixedBytes = Arrays.copyOfRange(originalBytes, saltLength, originalBytes.length);\n\t\tString fixedHex = String.valueOf(Hex.encode(fixedBytes));\n\t\tassertThat(fixedHex).isEqualTo(encodedPassword);\n\t}\n\n\t@Test\n\tpublic void encodeAndMatchWhenBase64ThenSuccess() {\n\t\tthis.encoder.setEncodeHashAsBase64(true);\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = this.encoder.encode(rawPassword);\n\t\tassertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void encodeAndMatchWhenBase64AndCustomSaltLengthThenSuccess() {\n\t\tthis.encoderSalt16.setEncodeHashAsBase64(true);\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = this.encoderSalt16.encode(rawPassword);\n\t\tassertThat(this.encoderSalt16.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void encodeWhenBase64ThenBase64DecodeSuccess() {\n\t\tassertThat(this.encoders).allSatisfy((pe) -> {\n\t\t\tpe.setEncodeHashAsBase64(true);\n\t\t\tString encodedPassword = pe.encode(\"password\");\n\t\t\t// validate can decode as Base64\n\t\t\tassertThatNoException().isThrownBy(() -> java.util.Base64.getDecoder().decode(encodedPassword));\n\t\t});\n\t}\n\n\t@Test\n\tpublic void matchWhenBase64ThenSuccess() {\n\t\tthis.encoder.setEncodeHashAsBase64(true);\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = \"3FOwOMcDgxP+z1x/sv184LFY2WVD+ZGMgYP3LPOSmCcDmk1XPYvcCQ==\";\n\t\tassertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();\n\t\tjava.util.Base64.getDecoder().decode(encodedPassword); // validate can decode as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Base64\n\t}\n\n\t@Test\n\tpublic void matchWhenBase64AndCustomSaltLengthThenSuccess() {\n\t\tthis.encoderSalt16.setEncodeHashAsBase64(true);\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = \"ASNFZ4mrze8BI0VniavN7xDYg8Kg5lPJcXXEolg6fx/TMBsxmnZX2V91Nl6nwEzh\";\n\t\tassertThat(this.encoderSalt16.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void encodeAndMatchWhenSha256ThenSuccess() {\n\t\tthis.encoder.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = this.encoder.encode(rawPassword);\n\t\tassertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void encodeAndMatchWhenSha256AndCustomSaltLengthThenSuccess() {\n\t\tthis.encoderSalt16.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = this.encoderSalt16.encode(rawPassword);\n\t\tassertThat(this.encoderSalt16.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenSha256ThenSuccess() {\n\t\tthis.encoder.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = \"821447f994e2b04c5014e31fa9fca4ae1cc9f2188c4ed53d3ddb5ba7980982b51a0ecebfc0b81a79\";\n\t\tassertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenSha256AndCustomSaltLengthThenSuccess() {\n\t\tthis.encoderSalt16.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = \"0123456789abcdef0123456789abcdefc7cfc96cd26b854d096ccbb3308fad860d719eb552ed52ef8352935539158287\";\n\t\tassertThat(this.encoderSalt16.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenDefaultsForSpringSecurity_v5_8ThenSuccess() {\n\t\tPbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();\n\t\tString rawPassword = \"password\";\n\t\tString encodedPassword = \"fefe5120467e5d4ccff442dbb2fa86d276262d97435c0c54e5eebced51ffd144fcb05eb53fea2677216c4f3250010006\";\n\t\tassertThat(encoder.matches(rawPassword, encodedPassword)).isTrue();\n\t}\n\n\t/**\n\t * Used to find the iteration count that takes .5 seconds.\n\t */\n\tpublic void findDefaultIterationCount() {\n\t\t// warm up\n\t\trun(180000, 10);\n\t\t// find the default\n\t\trun(165000, 10);\n\t}\n\n\tprivate void run(int iterations, int count) {\n\t\tlong HALF_SECOND = 500L;\n\t\tlong avg = 0;\n\t\twhile (avg < HALF_SECOND) {\n\t\t\titerations += 10000;\n\t\t\tPbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder(\"\", 8, iterations, 256);\n\t\t\tString encoded = encoder.encode(\"password\");\n\t\t\tSystem.out.println(\"Trying \" + iterations);\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tencoder.matches(\"password\", encoded);\n\t\t\t}\n\t\t\tlong end = System.currentTimeMillis();\n\t\t\tlong diff = end - start;\n\t\t\tavg = diff / count;\n\t\t\tSystem.out.println(\"Avgerage \" + avg);\n\t\t}\n\t\tSystem.out.println(\"Iterations \" + iterations);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password/StandardPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"deprecation\")\npublic class StandardPasswordEncoderTests {\n\n\tprivate StandardPasswordEncoder encoder = new StandardPasswordEncoder(\"secret\");\n\n\t@Test\n\tpublic void matches() {\n\t\tString result = this.encoder.encode(\"password\");\n\t\tassertThat(result).isNotEqualTo(\"password\");\n\t\tassertThat(this.encoder.matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesLengthChecked() {\n\t\tString result = this.encoder.encode(\"password\");\n\t\tassertThat(this.encoder.matches(\"password\", result.substring(0, result.length() - 2))).isFalse();\n\t}\n\n\t@Test\n\tpublic void notMatches() {\n\t\tString result = this.encoder.encode(\"password\");\n\t\tassertThat(this.encoder.matches(\"bogus\", result)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password4j/Argon2Password4jPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.Argon2Function;\nimport com.password4j.types.Argon2;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link Argon2Password4jPasswordEncoder}.\n *\n * @author Mehrdad Bozorgmehr\n */\nclass Argon2Password4jPasswordEncoderTests {\n\n\tprivate static final String PASSWORD = \"password\";\n\n\tprivate static final String LONG_PASSWORD = \"a\".repeat(1000);\n\n\tprivate static final String SPECIAL_CHARS_PASSWORD = \"p@ssw0rd!#$%^&*()_+-=[]{}|;':\\\",./<>?\";\n\n\tprivate static final String UNICODE_PASSWORD = \"пароль密码パスワード🔐\";\n\n\t@Test\n\tvoid defaultConstructorShouldCreateWorkingEncoder() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoded).startsWith(\"$argon2\"); // Argon2 hash format\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid constructorWithNullArgon2FunctionShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Argon2Password4jPasswordEncoder(null))\n\t\t\t.withMessage(\"hashingFunction cannot be null\");\n\t}\n\n\t@Test\n\tvoid constructorWithCustomArgon2FunctionShouldWork() {\n\t\tArgon2Function customFunction = Argon2Function.getInstance(4096, 3, 1, 32, Argon2.ID);\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder(customFunction);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoded).startsWith(\"$argon2id\");\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource(Argon2.class)\n\tvoid encodingShouldWorkWithDifferentArgon2Types(Argon2 type) {\n\t\tArgon2Function function = Argon2Function.getInstance(4096, 3, 1, 32, type);\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder(function);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoded).startsWith(\"$argon2\" + type.name().toLowerCase());\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid encodingShouldGenerateDifferentHashesForSamePassword() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\n\t\tString hash1 = encoder.encode(PASSWORD);\n\t\tString hash2 = encoder.encode(PASSWORD);\n\n\t\tassertThat(hash1).isNotEqualTo(hash2);\n\t\tassertThat(encoder.matches(PASSWORD, hash1)).isTrue();\n\t\tassertThat(encoder.matches(PASSWORD, hash2)).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldHandleLongPasswords() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(LONG_PASSWORD);\n\n\t\tassertThat(encoder.matches(LONG_PASSWORD, encoded)).isTrue();\n\t\tassertThat(encoder.matches(\"wrong\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldHandleSpecialCharacters() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(SPECIAL_CHARS_PASSWORD);\n\n\t\tassertThat(encoder.matches(SPECIAL_CHARS_PASSWORD, encoded)).isTrue();\n\t\tassertThat(encoder.matches(\"wrong\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldHandleUnicodeCharacters() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(UNICODE_PASSWORD);\n\n\t\tassertThat(encoder.matches(UNICODE_PASSWORD, encoded)).isTrue();\n\t\tassertThat(encoder.matches(\"wrong\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldRejectIncorrectPasswords() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.matches(\"wrongpassword\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"PASSWORD\", encoded)).isFalse(); // Case sensitive\n\t\tassertThat(encoder.matches(\"password \", encoded)).isFalse(); // Trailing space\n\t\tassertThat(encoder.matches(\" password\", encoded)).isFalse(); // Leading space\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseForNullOrEmptyInputs() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.matches(null, encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, null)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"\")).isFalse();\n\t\tassertThat(encoder.matches(null, null)).isFalse();\n\t\tassertThat(encoder.matches(\"\", \"\")).isFalse();\n\t}\n\n\t@Test\n\tvoid encodeNullShouldReturnNull() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\n\t\tassertThat(encoder.encode(null)).isNull();\n\t}\n\n\t@Test\n\tvoid upgradeEncodingShouldReturnFalse() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.upgradeEncoding(encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldWorkWithAlgorithmFinderDefaults() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder(\n\t\t\t\tAlgorithmFinder.getArgon2Instance());\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldRejectMalformedHashes() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\n\t\t// For Argon2, Password4j may throw BadParametersException on malformed hashes.\n\t\t// We treat either an exception or a false return as a successful rejection.\n\t\tassertMalformedRejected(encoder, PASSWORD, \"invalid_hash\");\n\t\tassertMalformedRejected(encoder, PASSWORD, \"$argon2id$invalid\");\n\t\tassertMalformedRejected(encoder, PASSWORD, \"\");\n\t}\n\n\tprivate void assertMalformedRejected(Argon2Password4jPasswordEncoder encoder, String raw, String malformed) {\n\t\tboolean rejected = false;\n\t\ttry {\n\t\t\trejected = !encoder.matches(raw, malformed);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\t// Accept exception as valid rejection path for malformed input\n\t\t\trejected = true;\n\t\t}\n\t\tassertThat(rejected).as(\"Malformed hash should not validate: \" + malformed).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldHandleEmptyStringPassword() {\n\t\tArgon2Password4jPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(\"\");\n\n\t\tassertThat(encoded).isNotNull();\n\t\tboolean emptyStringMatches;\n\t\ttry {\n\t\t\temptyStringMatches = encoder.matches(\"\", encoded);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\temptyStringMatches = false; // treat exception as non-match but still\n\t\t\t\t\t\t\t\t\t\t// acceptable behavior\n\t\t}\n\n\t\tif (emptyStringMatches) {\n\t\t\tassertThat(encoder.matches(\"\", encoded)).isTrue();\n\t\t}\n\t\telse {\n\t\t\tassertThat(encoded).isNotEmpty();\n\t\t}\n\t\tassertThat(encoder.matches(\"notEmpty\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldHandleCustomMemoryAndIterationParameters() {\n\t\t// Test with different memory and iteration parameters\n\t\tArgon2Function lowMemory = Argon2Function.getInstance(1024, 2, 1, 16, Argon2.ID);\n\t\tArgon2Function highMemory = Argon2Function.getInstance(65536, 4, 2, 64, Argon2.ID);\n\n\t\tArgon2Password4jPasswordEncoder lowEncoder = new Argon2Password4jPasswordEncoder(lowMemory);\n\t\tArgon2Password4jPasswordEncoder highEncoder = new Argon2Password4jPasswordEncoder(highMemory);\n\n\t\tString lowEncoded = lowEncoder.encode(PASSWORD);\n\t\tString highEncoded = highEncoder.encode(PASSWORD);\n\n\t\tassertThat(lowEncoder.matches(PASSWORD, lowEncoded)).isTrue();\n\t\tassertThat(highEncoder.matches(PASSWORD, highEncoded)).isTrue();\n\n\t\t// Each encoder should work with hashes generated by the same parameters\n\t\tassertThat(lowEncoded).isNotEqualTo(highEncoded);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password4j/BalloonHashingPassword4jPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.BalloonHashingFunction;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link BalloonHashingPassword4jPasswordEncoder}.\n *\n * @author Mehrdad Bozorgmehr\n */\nclass BalloonHashingPassword4jPasswordEncoderTests {\n\n\tprivate static final String PASSWORD = \"password\";\n\n\tprivate static final String DIFFERENT_PASSWORD = \"differentpassword\";\n\n\t@Test\n\tvoid constructorWithNullFunctionShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new BalloonHashingPassword4jPasswordEncoder(null))\n\t\t\t.withMessage(\"balloonHashingFunction cannot be null\");\n\t}\n\n\t@Test\n\tvoid constructorWithInvalidSaltLengthShouldThrowException() {\n\t\tBalloonHashingFunction function = AlgorithmFinder.getBalloonHashingInstance();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new BalloonHashingPassword4jPasswordEncoder(function, 0))\n\t\t\t.withMessage(\"saltLength must be positive\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new BalloonHashingPassword4jPasswordEncoder(function, -1))\n\t\t\t.withMessage(\"saltLength must be positive\");\n\t}\n\n\t@Test\n\tvoid defaultConstructorShouldWork() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().isNotEqualTo(PASSWORD);\n\t\tassertThat(encoded).contains(\":\");\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid customFunctionConstructorShouldWork() {\n\t\tBalloonHashingFunction customFunction = BalloonHashingFunction.getInstance(\"SHA-256\", 512, 2, 3);\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder(customFunction);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().isNotEqualTo(PASSWORD);\n\t\tassertThat(encoded).contains(\":\");\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid customSaltLengthConstructorShouldWork() {\n\t\tBalloonHashingFunction function = AlgorithmFinder.getBalloonHashingInstance();\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder(function, 16);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().isNotEqualTo(PASSWORD);\n\t\tassertThat(encoded).contains(\":\");\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid encodeShouldIncludeSaltInOutput() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).contains(\":\");\n\t\tString[] parts = encoded.split(\":\");\n\t\tassertThat(parts).hasSize(2);\n\t\tassertThat(parts[0]).isNotEmpty(); // salt part\n\t\tassertThat(parts[1]).isNotEmpty(); // hash part\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnTrueForCorrectPassword() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean matches = encoder.matches(PASSWORD, encoded);\n\n\t\tassertThat(matches).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseForIncorrectPassword() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean matches = encoder.matches(DIFFERENT_PASSWORD, encoded);\n\n\t\tassertThat(matches).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseForMalformedEncodedPassword() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\n\t\tassertThat(encoder.matches(PASSWORD, \"malformed\")).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"no:delimiter:in:wrong:places\")).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"invalid_base64!:hash\")).isFalse();\n\t}\n\n\t@Test\n\tvoid multipleEncodingsShouldProduceDifferentHashesButAllMatch() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\n\t\tString encoded1 = encoder.encode(PASSWORD);\n\t\tString encoded2 = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded1).isNotEqualTo(encoded2); // Different salts should produce\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// different results\n\t\tassertThat(encoder.matches(PASSWORD, encoded1)).isTrue();\n\t\tassertThat(encoder.matches(PASSWORD, encoded2)).isTrue();\n\t}\n\n\t@Test\n\tvoid upgradeEncodingShouldReturnFalse() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean shouldUpgrade = encoder.upgradeEncoding(encoded);\n\n\t\tassertThat(shouldUpgrade).isFalse();\n\t}\n\n\t@Test\n\tvoid encodeNullShouldReturnNull() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\n\t\tassertThat(encoder.encode(null)).isNull();\n\t}\n\n\t@Test\n\tvoid matchesWithNullOrEmptyValuesShouldReturnFalse() {\n\t\tBalloonHashingPassword4jPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.matches(null, encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, null)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"\")).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password4j/BcryptPassword4jPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.BcryptFunction;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link BcryptPassword4jPasswordEncoder}.\n *\n * @author Mehrdad Bozorgmehr\n */\nclass BcryptPassword4jPasswordEncoderTests {\n\n\tprivate static final String PASSWORD = \"password\";\n\n\tprivate static final String LONG_PASSWORD = \"a\".repeat(72); // BCrypt max length\n\n\tprivate static final String VERY_LONG_PASSWORD = \"a\".repeat(100); // Beyond BCrypt max\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// length\n\n\tprivate static final String SPECIAL_CHARS_PASSWORD = \"p@ssw0rd!#$%^&*()_+-=[]{}|;':\\\",./<>?\";\n\n\tprivate static final String UNICODE_PASSWORD = \"пароль密码パスワード🔐\";\n\n\t@Test\n\tvoid defaultConstructorShouldCreateWorkingEncoder() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().matches(\"^\\\\$2[aby]?\\\\$.*\");\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid constructorWithNullBcryptFunctionShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new BcryptPassword4jPasswordEncoder(null))\n\t\t\t.withMessage(\"hashingFunction cannot be null\");\n\t}\n\n\t@Test\n\tvoid constructorWithCustomBcryptFunctionShouldWork() {\n\t\tBcryptFunction customFunction = BcryptFunction.getInstance(6);\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(customFunction);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().contains(\"$06$\"); // 6 rounds\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(ints = { 4, 6, 8, 10, 12 })\n\tvoid encodingShouldWorkWithDifferentRounds(int rounds) {\n\t\tBcryptFunction function = BcryptFunction.getInstance(rounds);\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(function);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().contains(String.format(\"$%02d$\", rounds));\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid encodingShouldGenerateDifferentHashesForSamePassword() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\n\t\tString hash1 = encoder.encode(PASSWORD);\n\t\tString hash2 = encoder.encode(PASSWORD);\n\n\t\tassertThat(hash1).isNotEqualTo(hash2);\n\t\tassertThat(encoder.matches(PASSWORD, hash1)).isTrue();\n\t\tassertThat(encoder.matches(PASSWORD, hash2)).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldHandleLongPasswords() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\n\t\tString encodedLong = encoder.encode(LONG_PASSWORD);\n\t\tString encodedVeryLong = encoder.encode(VERY_LONG_PASSWORD);\n\n\t\tassertThat(encoder.matches(LONG_PASSWORD, encodedLong)).isTrue();\n\t\tassertThat(encoder.matches(VERY_LONG_PASSWORD, encodedVeryLong)).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldHandleSpecialCharacters() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(SPECIAL_CHARS_PASSWORD);\n\n\t\tassertThat(encoder.matches(SPECIAL_CHARS_PASSWORD, encoded)).isTrue();\n\t\tassertThat(encoder.matches(\"wrong\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldHandleUnicodeCharacters() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(UNICODE_PASSWORD);\n\n\t\tassertThat(encoder.matches(UNICODE_PASSWORD, encoded)).isTrue();\n\t\tassertThat(encoder.matches(\"wrong\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldRejectIncorrectPasswords() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.matches(\"wrongpassword\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"PASSWORD\", encoded)).isFalse(); // Case sensitive\n\t\tassertThat(encoder.matches(\"password \", encoded)).isFalse(); // Trailing space\n\t\tassertThat(encoder.matches(\" password\", encoded)).isFalse(); // Leading space\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseForNullOrEmptyInputs() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.matches(null, encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, null)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"\")).isFalse();\n\t\tassertThat(encoder.matches(null, null)).isFalse();\n\t\tassertThat(encoder.matches(\"\", \"\")).isFalse();\n\t}\n\n\t@Test\n\tvoid encodeNullShouldReturnNull() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\n\t\tassertThat(encoder.encode(null)).isNull();\n\t}\n\n\t@Test\n\tvoid upgradeEncodingShouldReturnFalse() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.upgradeEncoding(encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldWorkWithAlgorithmFinderDefaults() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(\n\t\t\t\tAlgorithmFinder.getBcryptInstance());\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldRejectMalformedHashes() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\t\tassertMalformedRejected(encoder, PASSWORD, \"invalid_hash\");\n\t\tassertMalformedRejected(encoder, PASSWORD, \"$2a$10$invalid\");\n\t\tassertMalformedRejected(encoder, PASSWORD, \"\");\n\t}\n\n\tprivate void assertMalformedRejected(BcryptPassword4jPasswordEncoder encoder, String raw, String malformed) {\n\t\tboolean rejected;\n\t\ttry {\n\t\t\trejected = !encoder.matches(raw, malformed);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\trejected = true; // exception is acceptable rejection\n\t\t}\n\t\tassertThat(rejected).as(\"Malformed hash should not validate: \" + malformed).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldHandleEmptyStringPassword() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(\"\");\n\t\tassertThat(encoded).isNotNull();\n\t\tboolean emptyMatches;\n\t\ttry {\n\t\t\temptyMatches = encoder.matches(\"\", encoded);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\temptyMatches = false; // treat as non-match if library rejects empty raw\n\t\t}\n\t\t// Either behavior acceptable; if it matches, verify; if not, still ensure other\n\t\t// mismatches remain false.\n\t\tif (emptyMatches) {\n\t\t\tassertThat(encoder.matches(\"\", encoded)).isTrue();\n\t\t}\n\t\tassertThat(encoder.matches(\"notEmpty\", encoded)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password4j/Password4jPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.BcryptFunction;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Base functionality tests for {@link Password4jPasswordEncoder} implementations. These\n * tests verify the common behavior across all concrete password encoder subclasses.\n *\n * @author Mehrdad Bozorgmehr\n */\nclass Password4jPasswordEncoderTests {\n\n\tprivate static final String PASSWORD = \"password\";\n\n\tprivate static final String WRONG_PASSWORD = \"wrongpassword\";\n\n\t// Test abstract class behavior through concrete implementation\n\t@Test\n\tvoid encodeShouldReturnNonNullHashedPassword() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(4));\n\n\t\tString result = encoder.encode(PASSWORD);\n\n\t\tassertThat(result).isNotNull().isNotEqualTo(PASSWORD);\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnTrueForValidPassword() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(4));\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean result = encoder.matches(PASSWORD, encoded);\n\n\t\tassertThat(result).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseForInvalidPassword() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(4));\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean result = encoder.matches(WRONG_PASSWORD, encoded);\n\n\t\tassertThat(result).isFalse();\n\t}\n\n\t@Test\n\tvoid encodeNullPasswordShouldReturnNull() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(4));\n\n\t\tassertThat(encoder.encode(null)).isNull();\n\t}\n\n\t@Test\n\tvoid multipleEncodesProduceDifferentHashesButAllMatch() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(4));\n\n\t\tString encoded1 = encoder.encode(PASSWORD);\n\t\tString encoded2 = encoder.encode(PASSWORD);\n\t\t// Bcrypt should produce different salted hashes for the same raw password\n\t\tassertThat(encoded1).isNotEqualTo(encoded2);\n\t\tassertThat(encoder.matches(PASSWORD, encoded1)).isTrue();\n\t\tassertThat(encoder.matches(PASSWORD, encoded2)).isTrue();\n\t}\n\n\t@Test\n\tvoid upgradeEncodingShouldReturnFalse() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(4));\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean result = encoder.upgradeEncoding(encoded);\n\n\t\tassertThat(result).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseWhenRawOrEncodedNullOrEmpty() {\n\t\tBcryptPassword4jPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(BcryptFunction.getInstance(4));\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tassertThat(encoder.matches(null, encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, null)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"\")).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password4j/PasswordCompatibilityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.Argon2Function;\nimport com.password4j.BcryptFunction;\nimport com.password4j.ScryptFunction;\nimport com.password4j.types.Argon2;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.argon2.Argon2PasswordEncoder;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;\nimport org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests compatibility between existing Spring Security password encoders and\n * Password4j-based password encoders.\n *\n * @author Mehrdad Bozorgmehr\n */\nclass PasswordCompatibilityTests {\n\n\tprivate static final String PASSWORD = \"password\";\n\n\t// BCrypt Compatibility Tests\n\t@Test\n\tvoid bcryptEncodedWithSpringSecurityShouldMatchWithPassword4j() {\n\t\tBCryptPasswordEncoder springEncoder = new BCryptPasswordEncoder(10);\n\t\tBcryptPassword4jPasswordEncoder password4jEncoder = new BcryptPassword4jPasswordEncoder(\n\t\t\t\tBcryptFunction.getInstance(10));\n\n\t\tString encodedBySpring = springEncoder.encode(PASSWORD);\n\t\tboolean matchedByPassword4j = password4jEncoder.matches(PASSWORD, encodedBySpring);\n\n\t\tassertThat(matchedByPassword4j).isTrue();\n\t}\n\n\t@Test\n\tvoid bcryptEncodedWithPassword4jShouldMatchWithSpringSecurity() {\n\t\tBcryptPassword4jPasswordEncoder password4jEncoder = new BcryptPassword4jPasswordEncoder(\n\t\t\t\tBcryptFunction.getInstance(10));\n\t\tBCryptPasswordEncoder springEncoder = new BCryptPasswordEncoder(10);\n\n\t\tString encodedByPassword4j = password4jEncoder.encode(PASSWORD);\n\t\tboolean matchedBySpring = springEncoder.matches(PASSWORD, encodedByPassword4j);\n\n\t\tassertThat(matchedBySpring).isTrue();\n\t}\n\n\t// Argon2 Compatibility Tests\n\t@Test\n\tvoid argon2EncodedWithSpringSecurityShouldMatchWithPassword4j() {\n\t\tArgon2PasswordEncoder springEncoder = new Argon2PasswordEncoder(16, 32, 1, 4096, 3);\n\t\tArgon2Password4jPasswordEncoder password4jEncoder = new Argon2Password4jPasswordEncoder(\n\t\t\t\tArgon2Function.getInstance(4096, 3, 1, 32, Argon2.ID));\n\n\t\tString encodedBySpring = springEncoder.encode(PASSWORD);\n\t\tboolean matchedByPassword4j = password4jEncoder.matches(PASSWORD, encodedBySpring);\n\n\t\tassertThat(matchedByPassword4j).isTrue();\n\t}\n\n\t@Test\n\tvoid argon2EncodedWithPassword4jShouldMatchWithSpringSecurity() {\n\t\tArgon2Password4jPasswordEncoder password4jEncoder = new Argon2Password4jPasswordEncoder(\n\t\t\t\tArgon2Function.getInstance(4096, 3, 1, 32, Argon2.ID));\n\t\tArgon2PasswordEncoder springEncoder = new Argon2PasswordEncoder(16, 32, 1, 4096, 3);\n\n\t\tString encodedByPassword4j = password4jEncoder.encode(PASSWORD);\n\t\tboolean matchedBySpring = springEncoder.matches(PASSWORD, encodedByPassword4j);\n\n\t\tassertThat(matchedBySpring).isTrue();\n\t}\n\n\t// SCrypt Compatibility Tests\n\t@Test\n\tvoid scryptEncodedWithSpringSecurityShouldMatchWithPassword4j() {\n\t\tSCryptPasswordEncoder springEncoder = new SCryptPasswordEncoder(16384, 8, 1, 32, 64);\n\t\tScryptPassword4jPasswordEncoder password4jEncoder = new ScryptPassword4jPasswordEncoder(\n\t\t\t\tScryptFunction.getInstance(16384, 8, 1, 32));\n\n\t\tString encodedBySpring = springEncoder.encode(PASSWORD);\n\t\tboolean matchedByPassword4j = password4jEncoder.matches(PASSWORD, encodedBySpring);\n\n\t\tassertThat(matchedByPassword4j).isTrue();\n\t}\n\n\t@Test\n\tvoid scryptEncodedWithPassword4jShouldMatchWithSpringSecurity() {\n\t\tScryptPassword4jPasswordEncoder password4jEncoder = new ScryptPassword4jPasswordEncoder(\n\t\t\t\tScryptFunction.getInstance(16384, 8, 1, 32));\n\t\tSCryptPasswordEncoder springEncoder = new SCryptPasswordEncoder(16384, 8, 1, 32, 64);\n\n\t\tString encodedByPassword4j = password4jEncoder.encode(PASSWORD);\n\t\tboolean matchedBySpring = springEncoder.matches(PASSWORD, encodedByPassword4j);\n\n\t\tassertThat(matchedBySpring).isTrue();\n\t}\n\n\t// PBKDF2 Compatibility Tests\n\t@Test\n\tvoid pbkdf2EncodedWithSpringSecurityCannotMatchWithPassword4j() {\n\t\t// Note: Direct compatibility between Spring Security's Pbkdf2PasswordEncoder\n\t\t// and Password4j's PBKDF2 implementation is not possible because they use\n\t\t// different output formats. Spring Security uses hex encoding with a specific\n\t\t// format,\n\t\t// while our Password4jPasswordEncoder uses salt:hash format with Base64 encoding.\n\t\tPbkdf2PasswordEncoder springEncoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();\n\t\tPbkdf2Password4jPasswordEncoder password4jEncoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tString encodedBySpring = springEncoder.encode(PASSWORD);\n\t\tString encodedByPassword4j = password4jEncoder.encode(PASSWORD);\n\n\t\t// These should NOT match due to different formats\n\t\t// Spring Security will throw an exception when trying to decode Password4j\n\t\t// format,\n\t\t// which should be treated as a non-match\n\t\tboolean password4jCanMatchSpring = password4jEncoder.matches(PASSWORD, encodedBySpring);\n\t\tboolean springCanMatchPassword4j;\n\t\ttry {\n\t\t\tspringCanMatchPassword4j = springEncoder.matches(PASSWORD, encodedByPassword4j);\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\t// Expected exception due to format incompatibility - treat as non-match\n\t\t\tspringCanMatchPassword4j = false;\n\t\t}\n\n\t\tassertThat(password4jCanMatchSpring).isFalse();\n\t\tassertThat(springCanMatchPassword4j).isFalse();\n\n\t\t// But each should match its own encoding\n\t\tassertThat(springEncoder.matches(PASSWORD, encodedBySpring)).isTrue();\n\t\tassertThat(password4jEncoder.matches(PASSWORD, encodedByPassword4j)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password4j/Pbkdf2Password4jPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.PBKDF2Function;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link Pbkdf2Password4jPasswordEncoder}.\n *\n * @author Mehrdad Bozorgmehr\n */\nclass Pbkdf2Password4jPasswordEncoderTests {\n\n\tprivate static final String PASSWORD = \"password\";\n\n\tprivate static final String DIFFERENT_PASSWORD = \"differentpassword\";\n\n\t@Test\n\tvoid constructorWithNullFunctionShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Pbkdf2Password4jPasswordEncoder(null))\n\t\t\t.withMessage(\"pbkdf2Function cannot be null\");\n\t}\n\n\t@Test\n\tvoid constructorWithInvalidSaltLengthShouldThrowException() {\n\t\tPBKDF2Function function = AlgorithmFinder.getPBKDF2Instance();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Pbkdf2Password4jPasswordEncoder(function, 0))\n\t\t\t.withMessage(\"saltLength must be positive\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Pbkdf2Password4jPasswordEncoder(function, -1))\n\t\t\t.withMessage(\"saltLength must be positive\");\n\t}\n\n\t@Test\n\tvoid defaultConstructorShouldWork() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().isNotEqualTo(PASSWORD).contains(\":\");\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid customFunctionConstructorShouldWork() {\n\t\tPBKDF2Function customFunction = AlgorithmFinder.getPBKDF2Instance();\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder(customFunction);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().isNotEqualTo(PASSWORD).contains(\":\");\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid customSaltLengthConstructorShouldWork() {\n\t\tPBKDF2Function function = AlgorithmFinder.getPBKDF2Instance();\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder(function, 16);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull().isNotEqualTo(PASSWORD).contains(\":\");\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid encodeShouldIncludeSaltInOutput() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).contains(\":\");\n\t\tString[] parts = encoded.split(\":\");\n\t\tassertThat(parts).hasSize(2);\n\t\tassertThat(parts[0]).isNotEmpty(); // salt part\n\t\tassertThat(parts[1]).isNotEmpty(); // hash part\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnTrueForCorrectPassword() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean matches = encoder.matches(PASSWORD, encoded);\n\n\t\tassertThat(matches).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseForIncorrectPassword() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean matches = encoder.matches(DIFFERENT_PASSWORD, encoded);\n\n\t\tassertThat(matches).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseForMalformedEncodedPassword() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tassertThat(encoder.matches(PASSWORD, \"malformed\")).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"no:delimiter:in:wrong:places\")).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"invalid_base64!:hash\")).isFalse();\n\t}\n\n\t@Test\n\tvoid multipleEncodingsShouldProduceDifferentHashesButAllMatch() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tString encoded1 = encoder.encode(PASSWORD);\n\t\tString encoded2 = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded1).isNotEqualTo(encoded2); // Different salts should produce\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// different results\n\t\tassertThat(encoder.matches(PASSWORD, encoded1)).isTrue();\n\t\tassertThat(encoder.matches(PASSWORD, encoded2)).isTrue();\n\t}\n\n\t@Test\n\tvoid upgradeEncodingShouldReturnFalse() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\t\tboolean shouldUpgrade = encoder.upgradeEncoding(encoded);\n\n\t\tassertThat(shouldUpgrade).isFalse();\n\t}\n\n\t@Test\n\tvoid encodeNullShouldReturnNull() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\n\t\tassertThat(encoder.encode(null)).isNull();\n\t}\n\n\t@Test\n\tvoid matchesWithNullOrEmptyValuesShouldReturnFalse() {\n\t\tPbkdf2Password4jPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.matches(null, encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, null)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"\")).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/password4j/ScryptPassword4jPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.password4j;\n\nimport com.password4j.AlgorithmFinder;\nimport com.password4j.ScryptFunction;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ScryptPassword4jPasswordEncoder}.\n *\n * @author Mehrdad Bozorgmehr\n */\nclass ScryptPassword4jPasswordEncoderTests {\n\n\tprivate static final String PASSWORD = \"password\";\n\n\tprivate static final String LONG_PASSWORD = \"a\".repeat(1000);\n\n\tprivate static final String SPECIAL_CHARS_PASSWORD = \"p@ssw0rd!#$%^&*()_+-=[]{}|;':\\\",./<>?\";\n\n\tprivate static final String UNICODE_PASSWORD = \"пароль密码パスワード🔐\";\n\n\t@Test\n\tvoid defaultConstructorShouldCreateWorkingEncoder() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\t// Password4j scrypt format differs from classic $s0$; accept generic multi-part\n\t\t// format\n\t\tassertThat(encoded.split(\"\\\\$\").length).isGreaterThanOrEqualTo(3);\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid constructorWithNullScryptFunctionShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ScryptPassword4jPasswordEncoder(null))\n\t\t\t.withMessage(\"hashingFunction cannot be null\");\n\t}\n\n\t@Test\n\tvoid constructorWithCustomScryptFunctionShouldWork() {\n\t\tScryptFunction customFunction = ScryptFunction.getInstance(16384, 8, 1, 32);\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder(customFunction);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoded.split(\"\\\\$\").length).isGreaterThanOrEqualTo(3);\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ \"1024, 8, 1, 16\", \"4096, 8, 1, 32\", \"16384, 8, 1, 32\", \"32768, 8, 1, 64\" })\n\tvoid encodingShouldWorkWithDifferentParameters(int N, int r, int p, int dkLen) {\n\t\tScryptFunction function = ScryptFunction.getInstance(N, r, p, dkLen);\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder(function);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoded.split(\"\\\\$\").length).isGreaterThanOrEqualTo(3);\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid encodingShouldGenerateDifferentHashesForSamePassword() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\n\t\tString hash1 = encoder.encode(PASSWORD);\n\t\tString hash2 = encoder.encode(PASSWORD);\n\n\t\tassertThat(hash1).isNotEqualTo(hash2);\n\t\tassertThat(encoder.matches(PASSWORD, hash1)).isTrue();\n\t\tassertThat(encoder.matches(PASSWORD, hash2)).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldHandleLongPasswords() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(LONG_PASSWORD);\n\n\t\tassertThat(encoder.matches(LONG_PASSWORD, encoded)).isTrue();\n\t\tassertThat(encoder.matches(\"wrong\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldHandleSpecialCharacters() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(SPECIAL_CHARS_PASSWORD);\n\n\t\tassertThat(encoder.matches(SPECIAL_CHARS_PASSWORD, encoded)).isTrue();\n\t\tassertThat(encoder.matches(\"wrong\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldHandleUnicodeCharacters() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\n\t\tString encoded = encoder.encode(UNICODE_PASSWORD);\n\n\t\tassertThat(encoder.matches(UNICODE_PASSWORD, encoded)).isTrue();\n\t\tassertThat(encoder.matches(\"wrong\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldRejectIncorrectPasswords() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.matches(\"wrongpassword\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"PASSWORD\", encoded)).isFalse(); // Case sensitive\n\t\tassertThat(encoder.matches(\"password \", encoded)).isFalse(); // Trailing space\n\t\tassertThat(encoder.matches(\" password\", encoded)).isFalse(); // Leading space\n\t}\n\n\t@Test\n\tvoid matchesShouldReturnFalseForNullOrEmptyInputs() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.matches(null, encoded)).isFalse();\n\t\tassertThat(encoder.matches(\"\", encoded)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, null)).isFalse();\n\t\tassertThat(encoder.matches(PASSWORD, \"\")).isFalse();\n\t\tassertThat(encoder.matches(null, null)).isFalse();\n\t\tassertThat(encoder.matches(\"\", \"\")).isFalse();\n\t}\n\n\t@Test\n\tvoid encodeNullShouldReturnNull() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\n\t\tassertThat(encoder.encode(null)).isNull();\n\t}\n\n\t@Test\n\tvoid upgradeEncodingShouldReturnFalse() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoder.upgradeEncoding(encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldWorkWithAlgorithmFinderDefaults() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder(\n\t\t\t\tAlgorithmFinder.getScryptInstance());\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldRejectMalformedHashes() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\t\tassertMalformedRejected(encoder, PASSWORD, \"invalid_hash\");\n\t\tassertMalformedRejected(encoder, PASSWORD, \"$s0$invalid\");\n\t\tassertMalformedRejected(encoder, PASSWORD, \"\");\n\t}\n\n\tprivate void assertMalformedRejected(ScryptPassword4jPasswordEncoder encoder, String raw, String malformed) {\n\t\tboolean rejected;\n\t\ttry {\n\t\t\trejected = !encoder.matches(raw, malformed);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\trejected = true; // exception path acceptable\n\t\t}\n\t\tassertThat(rejected).as(\"Malformed hash should not validate: \" + malformed).isTrue();\n\t}\n\n\t@Test\n\tvoid shouldHandleEmptyStringPassword() {\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\t\tString encoded = encoder.encode(\"\");\n\t\tassertThat(encoded).isNotNull();\n\t\tboolean emptyMatches;\n\t\ttry {\n\t\t\temptyMatches = encoder.matches(\"\", encoded);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\temptyMatches = false;\n\t\t}\n\t\tif (emptyMatches) {\n\t\t\tassertThat(encoder.matches(\"\", encoded)).isTrue();\n\t\t}\n\t\tassertThat(encoder.matches(\"notEmpty\", encoded)).isFalse();\n\t}\n\n\t@Test\n\tvoid shouldHandleCustomCostParameters() {\n\t\t// Test with low cost parameters for speed\n\t\tScryptFunction lowCost = ScryptFunction.getInstance(1024, 1, 1, 16);\n\t\t// Test with higher cost parameters\n\t\tScryptFunction highCost = ScryptFunction.getInstance(32768, 8, 2, 64);\n\n\t\tScryptPassword4jPasswordEncoder lowEncoder = new ScryptPassword4jPasswordEncoder(lowCost);\n\t\tScryptPassword4jPasswordEncoder highEncoder = new ScryptPassword4jPasswordEncoder(highCost);\n\n\t\tString lowEncoded = lowEncoder.encode(PASSWORD);\n\t\tString highEncoded = highEncoder.encode(PASSWORD);\n\n\t\tassertThat(lowEncoder.matches(PASSWORD, lowEncoded)).isTrue();\n\t\tassertThat(highEncoder.matches(PASSWORD, highEncoded)).isTrue();\n\n\t\t// Each encoder should work with hashes generated by the same parameters\n\t\tassertThat(lowEncoded).isNotEqualTo(highEncoded);\n\t}\n\n\t@Test\n\tvoid shouldHandleEdgeCaseParameters() {\n\t\t// Test with minimum practical parameters\n\t\tScryptFunction minParams = ScryptFunction.getInstance(2, 1, 1, 1);\n\t\tScryptPassword4jPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder(minParams);\n\n\t\tString encoded = encoder.encode(PASSWORD);\n\n\t\tassertThat(encoded).isNotNull();\n\t\tassertThat(encoder.matches(PASSWORD, encoded)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/scrypt/SCryptPasswordEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.scrypt;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Shazin Sadakath\n *\n */\npublic class SCryptPasswordEncoderTests {\n\n\t@Test\n\tpublic void matches() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tString result = encoder.encode(\"password\");\n\t\tassertThat(result).isNotEqualTo(\"password\");\n\t\tassertThat(encoder.matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void unicode() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tString result = encoder.encode(\"passw\\u9292rd\");\n\t\tassertThat(encoder.matches(\"pass\\u9292\\u9292rd\", result)).isFalse();\n\t\tassertThat(encoder.matches(\"passw\\u9292rd\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void notMatches() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tString result = encoder.encode(\"password\");\n\t\tassertThat(encoder.matches(\"bogus\", result)).isFalse();\n\t}\n\n\t@Test\n\tpublic void customParameters() {\n\t\tSCryptPasswordEncoder encoder = new SCryptPasswordEncoder(512, 8, 4, 32, 16);\n\t\tString result = encoder.encode(\"password\");\n\t\tassertThat(result).isNotEqualTo(\"password\");\n\t\tassertThat(encoder.matches(\"password\", result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void differentPasswordHashes() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tString password = \"secret\";\n\t\tassertThat(encoder.encode(password)).isNotEqualTo(encoder.encode(password));\n\t}\n\n\t@Test\n\tpublic void samePasswordWithDifferentParams() {\n\t\tSCryptPasswordEncoder oldEncoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tSCryptPasswordEncoder newEncoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();\n\t\tString password = \"secret\";\n\t\tString oldEncodedPassword = oldEncoder.encode(password);\n\t\tassertThat(newEncoder.matches(password, oldEncodedPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void doesntMatchNullEncodedValue() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tassertThat(encoder.matches(\"password\", null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void doesntMatchEmptyEncodedValue() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tassertThat(encoder.matches(\"password\", \"\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void doesntMatchBogusEncodedValue() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tassertThat(encoder.matches(\"password\", \"012345678901234567890123456789\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void invalidCpuCostParameter() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SCryptPasswordEncoder(Integer.MIN_VALUE, 16, 2, 32, 16));\n\t}\n\n\t@Test\n\tpublic void invalidMemoryCostParameter() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SCryptPasswordEncoder(2, Integer.MAX_VALUE, 2, 32, 16));\n\t}\n\n\t@Test\n\tpublic void invalidParallelizationParameter() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SCryptPasswordEncoder(2, 8, Integer.MAX_VALUE, 32, 16));\n\t}\n\n\t@Test\n\tpublic void invalidSaltLengthParameter() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SCryptPasswordEncoder(2, 8, 1, 16, -1));\n\t}\n\n\t@Test\n\tpublic void invalidKeyLengthParameter() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SCryptPasswordEncoder(2, 8, 1, -1, 16));\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenNullThenFalse() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tassertThat(encoder.upgradeEncoding(null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenEmptyThenFalse() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tassertThat(encoder.upgradeEncoding(\"\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenSameEncoderThenFalse() {\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1();\n\t\tString encoded = encoder.encode(\"password\");\n\t\tassertThat(encoder.upgradeEncoding(encoded)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenWeakerToStrongerThenFalse() {\n\t\tSCryptPasswordEncoder weakEncoder = new SCryptPasswordEncoder((int) Math.pow(2, 10), 4, 1, 32, 64);\n\t\tSCryptPasswordEncoder strongEncoder = new SCryptPasswordEncoder((int) Math.pow(2, 16), 8, 1, 32, 64);\n\t\tString weakPassword = weakEncoder.encode(\"password\");\n\t\tString strongPassword = strongEncoder.encode(\"password\");\n\t\tassertThat(weakEncoder.upgradeEncoding(strongPassword)).isFalse();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenStrongerToWeakerThenTrue() {\n\t\tSCryptPasswordEncoder weakEncoder = new SCryptPasswordEncoder((int) Math.pow(2, 10), 4, 1, 32, 64);\n\t\tSCryptPasswordEncoder strongEncoder = new SCryptPasswordEncoder((int) Math.pow(2, 16), 8, 1, 32, 64);\n\t\tString weakPassword = weakEncoder.encode(\"password\");\n\t\tString strongPassword = strongEncoder.encode(\"password\");\n\t\tassertThat(strongEncoder.upgradeEncoding(weakPassword)).isTrue();\n\t}\n\n\t@Test\n\tpublic void upgradeEncodingWhenInvalidInputThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1().upgradeEncoding(\"not-a-scrypt-password\"));\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/java/org/springframework/security/crypto/util/EncodingUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.util;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.codec.Hex;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class EncodingUtilsTests {\n\n\t@Test\n\tpublic void hexEncode() {\n\t\tbyte[] bytes = new byte[] { (byte) 0x01, (byte) 0xFF, (byte) 65, (byte) 66, (byte) 67, (byte) 0xC0, (byte) 0xC1,\n\t\t\t\t(byte) 0xC2 };\n\t\tString result = new String(Hex.encode(bytes));\n\t\tassertThat(result).isEqualTo(\"01ff414243c0c1c2\");\n\t}\n\n\t@Test\n\tpublic void hexDecode() {\n\t\tbyte[] bytes = new byte[] { (byte) 0x01, (byte) 0xFF, (byte) 65, (byte) 66, (byte) 67, (byte) 0xC0, (byte) 0xC1,\n\t\t\t\t(byte) 0xC2 };\n\t\tbyte[] result = Hex.decode(\"01ff414243c0c1c2\");\n\t\tassertThat(Arrays.equals(bytes, result)).isTrue();\n\t}\n\n\t@Test\n\tpublic void concatenate() {\n\t\tbyte[] bytes = new byte[] { (byte) 0x01, (byte) 0xFF, (byte) 65, (byte) 66, (byte) 67, (byte) 0xC0, (byte) 0xC1,\n\t\t\t\t(byte) 0xC2 };\n\t\tbyte[] one = new byte[] { (byte) 0x01 };\n\t\tbyte[] two = new byte[] { (byte) 0xFF, (byte) 65, (byte) 66 };\n\t\tbyte[] three = new byte[] { (byte) 67, (byte) 0xC0, (byte) 0xC1, (byte) 0xC2 };\n\t\tassertThat(Arrays.equals(bytes, EncodingUtils.concatenate(one, two, three))).isTrue();\n\t}\n\n\t@Test\n\tpublic void subArray() {\n\t\tbyte[] bytes = new byte[] { (byte) 0x01, (byte) 0xFF, (byte) 65, (byte) 66, (byte) 67, (byte) 0xC0, (byte) 0xC1,\n\t\t\t\t(byte) 0xC2 };\n\t\tbyte[] two = new byte[] { (byte) 0xFF, (byte) 65, (byte) 66 };\n\t\tbyte[] subArray = EncodingUtils.subArray(bytes, 1, 4);\n\t\tassertThat(subArray).hasSize(3);\n\t\tassertThat(Arrays.equals(two, subArray)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/test/resources/bad.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwClFgrRa/PUHPIJr9gvIPL6g6Rjp/TVZmVNOf2fL96DYbkj5"
  },
  {
    "path": "crypto/src/test/resources/fake.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQDMWnfaQ0yLFXelprq2S8UurnaGvxFNUdbmTyJeycem5vGLycEY\nT4KcdVCTU5491cjbk5GcHjoj2efRSO0y0aXIlUJpLofDdML/SuGLZWp/GbEv978M\npZIztK8iaIm7D/D7by8aws1RJyD9T+lZDAGY7eFfMp0EQyHOcEL0NGFLuwIDAQAB\nAoGAWwC6uO8ZaiKwOouqQD4z3FsDG3SA/v7ABaYd9zpCd9gGnyrEm8/kqUoxDLrD\nEGRg4y+vO2fWmlqSuoeQYf4spf+vi2di+mGIb6nGe7TpMLPa7lFLOSQHZRx5M5H6\nJDhfhAHlKmF9gLGvDHbpyErzn5YXjcu0PoFiNC1y445D8iECQQDvJzkGbJ9l9vb0\noRyGXRDpddUcVMECLLB9NKmTl/zKy/qVPD+zYNoi87ePBJFbgmAXRjhhTk2uSBRP\nNtVaMoXLAkEA2r+ugzjsLZQIYz/9gxdzdbKWDgpSPbhKCR4bOmrDgJMcOVjtwW+n\n+liaX6zwI0QEgCAWLzCbbYDmj3kJrRwT0QJAaowg/dm7EmR7FfYJjVs9Q6X5skuY\nSe27G60wt88JExjZpU9YWgSWaugGKbOxRwHI6dWhHMkUFseKNNiLKUpFDQJALIGP\nahdsxiE2S6s7Uy60SSAas6SZ8wDJ320GsS4DtOc5eNmFFjQ3gxH/5rNy8FnoaIEe\nwl8rYG43er1voI7z4QJAB4qaqBo7eeiRgnUVIccaSZkNIMSrZ9QUjVFRgfLwAXDO\nAe+t6V+eB0oaIXczA+BLj3Oe6D3iHRGHrxGlcvDdHw==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "crypto/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "crypto/src/test/resources/spacey.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwClFgrRa/PUHPIJr9gvIPL6g6Rjp/TVZmVNOf2fL96DYbkj5\n4YbrwfKwjoTjk1M6gLQpOA4Blocx6zN5OnICnVGlVM9xymWxTxxCfc2tE2Fai9I1wchULCChhwm/UU5ZNi3KpXinlyamSYw+lMQkZ8gTXCgOEvs2j9E1quF4pvy1BZKvbD8tUnUQlyiKRnI6gOxQL8B6OAYPRdaa9FVNmrs1B4eDPG918L2f1pT090P1n+tw\niejNgQvtSD78/A88qt89OhzscsufALTrBjycn89kkfBd0zbVLF0W6+ZVLZrf97/y\nLCoGSCcZL9LFPNvNqxOnleviDco7aOs4stQ9jQIDAQABAoIBAQC1TbthyN0YUe+T\n        7dIDAbbZaVrU00biOtXgzjMADmTprP7Hf18UpIIIKfzfWw6FUD+gc1t4oe5pogE9\nUwGMXUmOORxu2pMYTb5vT9CEdexYnsAZsCo8PdD9GYSNrmquQef2MFpEqYQmHrdC\n                KWpaXn2i1ak+iCRPUGp4YwHpynZVxfE8z/AIsPn6NPDh6SnCXb1rTgQe2UCfXm93\nUJe5F/OR2kQi5KFO+dxLmCOBCwr6SGCLH+VotGpuxCVRUd9sJ/d4QpDZEgjuf7Ug\neQHfgMDS/tc09B9rl0dwKnEa31kcQ9X9KLkKP+w0Pqhh0Emny20eg9jS6XNayg61\np/LQtW9BAoGBAO5veKMIcXfZmuh11WIIhdhLKkNtYyt5NDmrV8/IVScLFvjB0ftt\n8PAtXo/ekOHkyITyIumQ9l4VCvacNw7DyV9FYk4WvrvVYOCL8aZi+O5+12NT67eO\nRr/voGlRoV05X7+inc90qbbYJ8lRmLSqvzmsm98mkuhw/FKGRhVZIfAJAoGBAM5R\n            I5vK6cJxOwXQOEGOd5/8B9JMFXyuendXo/N2/NxSQsbx4pc3v2rv/eGJYaY7Nx/y\n2M/vdWYkpG59PAS3k2TrCA/0SGmyVqY+c8BomKisU5VaBlIPfGuec9tDPgWCp8Ur\n3Jjt/2sVoa0vMkqymUqMb9HyH9tdI9oyh7EOOrplAoGAR6DlNNUMgVy11K/Rcqns\ny5WJFMh/ykeXENwQfTNJoXkLZZ+UXVwhzYVTqxTJoZMBSi8TnecWnBzmNj+nqp/W\n    lvBZH+xlUDhB6jMgXUPOVJd2TTigz3vGdVKfdgQ33bGmugM4NWJuuacmDKyem2fQ\n    GptoGBmWeI24v3HnC/LC50ECgYAz0iN8hRnz0db+Xc9TgAJB997LDnszJuvxv9yZ\n    UWCvwiWtrKG6U7FLnd4J4STayPLOnoOgrsexETEP43rIwIdQCMysnTH3AmlLNlKC\n    mIMHksknsUX3JJaevVziTOBuJ+QV3S96ZgUKk5NZWYprQrLIC8AmXodr5NgVfS2h\n    5i4QFQKBgFfbYHiMw5AAUQrBNkrAjLd1wIaO/6qS3w4OsCWKowhfaJLEXAbIRV7s\nvAtgtlCovdasVj4RRLXFf+73naVTQjBZI+3jWHHyFk3+Zy86mQCSGv9WuDVV1IhS\nh8InTVvK8wgdgX7qiw3pvU0roqNW4/j4j8OqJO3Zt4KO2iX8htsO\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "crypto/src/testFixtures/java/org/springframework/security/crypto/assertions/CryptoAssertions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.assertions;\n\nimport java.util.function.Supplier;\n\n/**\n * AssertJ entry point for crypto-related assertions. Use {@link #assertThat(Supplier)} to\n * assert on a {@link Supplier}&lt;{@link String}&gt; (e.g. a decryption lambda).\n * <p>\n * Example: <pre>\n * assertThat(() -&gt; encryptor.decrypt(ciphertext)).doesNotDecryptTo(\"plaintext\");\n * </pre>\n */\npublic final class CryptoAssertions {\n\n\tprivate CryptoAssertions() {\n\t}\n\n\t/**\n\t * Create assertions for the given supplier (e.g. a decryption expression).\n\t * @param actual the supplier to assert on\n\t * @return assertion object with methods like\n\t * {@link CryptoStringAssert#doesNotDecryptTo(String)}\n\t */\n\tpublic static CryptoStringAssert assertThat(Supplier<String> actual) {\n\t\treturn new CryptoStringAssert(actual);\n\t}\n\n}\n"
  },
  {
    "path": "crypto/src/testFixtures/java/org/springframework/security/crypto/assertions/CryptoStringAssert.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.crypto.assertions;\n\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\nimport org.assertj.core.api.AbstractObjectAssert;\n\n/**\n * AssertJ assertion for {@link Supplier}&lt;{@link String}&gt;, supporting\n * decryption-related checks such as {@link #doesNotDecryptTo(String)}.\n */\npublic final class CryptoStringAssert extends AbstractObjectAssert<CryptoStringAssert, Supplier<String>> {\n\n\tCryptoStringAssert(Supplier<String> actual) {\n\t\tsuper(actual, CryptoStringAssert.class);\n\t}\n\n\t/**\n\t * Asserts that either the supplier throws an exception when invoked, or the value it\n\t * returns is not equal to the given string. Use this to assert that a decryption\n\t * attempt does not yield a specific plaintext (e.g. wrong key or tampered\n\t * ciphertext).\n\t * @param expected the value that the supplier must not return\n\t * @return this assertion for chaining\n\t */\n\tpublic CryptoStringAssert doesNotDecryptTo(String expected) {\n\t\tisNotNull();\n\t\ttry {\n\t\t\tString result = this.actual.get();\n\t\t\tif (Objects.equals(result, expected)) {\n\t\t\t\tfailWithMessage(\"Expected supplier not to return <%s> but it did\", expected);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// Exception thrown: supplier does not \"decrypt to\" the expected value\n\t\t}\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "data/spring-security-data.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\napply plugin: 'compile-warnings-error'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi 'jakarta.xml.bind:jakarta.xml.bind-api'\n\tapi 'org.springframework.data:spring-data-commons'\n\tapi 'org.springframework:spring-core'\n\t\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n\n}\n"
  },
  {
    "path": "data/src/main/java/org/springframework/security/data/aot/hint/AuthorizeReturnObjectDataHintsRegistrar.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.data.aot.hint;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;\nimport org.springframework.security.aot.hint.AuthorizeReturnObjectCoreHintsRegistrar;\nimport org.springframework.security.aot.hint.AuthorizeReturnObjectHintsRegistrar;\nimport org.springframework.security.aot.hint.SecurityHintsRegistrar;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link SecurityHintsRegistrar} that scans all beans for implementations of\n * {@link RepositoryFactoryBeanSupport}, registering the corresponding entity class as a\n * {@link org.springframework.aot.hint.TypeHint} should any if that repository's method\n * use {@link AuthorizeReturnObject}.\n *\n * <p>\n * It also traverses those found types for other return values.\n *\n * <p>\n * An instance of this class is published as an infrastructural bean by the\n * {@code spring-security-config} module. However, in the event you need to publish it\n * yourself, remember to publish it as an infrastructural bean like so:\n *\n * <pre>\n *\t&#064;Bean\n *\t&#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n *\tstatic SecurityHintsRegistrar proxyThese(AuthorizationProxyFactory proxyFactory) {\n *\t\treturn new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory);\n *\t}\n * </pre>\n *\n * @author Josh Cummings\n * @since 6.4\n * @see AuthorizeReturnObjectCoreHintsRegistrar\n * @see AuthorizeReturnObjectHintsRegistrar\n */\npublic final class AuthorizeReturnObjectDataHintsRegistrar implements SecurityHintsRegistrar {\n\n\tprivate final AuthorizationProxyFactory proxyFactory;\n\n\tprivate final SecurityAnnotationScanner<AuthorizeReturnObject> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(AuthorizeReturnObject.class);\n\n\tprivate final Set<Class<?>> visitedClasses = new HashSet<>();\n\n\tpublic AuthorizeReturnObjectDataHintsRegistrar(AuthorizationProxyFactory proxyFactory) {\n\t\tthis.proxyFactory = proxyFactory;\n\t}\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ConfigurableListableBeanFactory beanFactory) {\n\t\tList<Class<?>> toProxy = new ArrayList<>();\n\t\tfor (String name : beanFactory.getBeanDefinitionNames()) {\n\t\t\tResolvableType type = beanFactory.getBeanDefinition(name).getResolvableType();\n\t\t\tif (!RepositoryFactoryBeanSupport.class.isAssignableFrom(type.toClass())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t@Nullable Class<?>[] generics = type.resolveGenerics();\n\t\t\t@Nullable Class<?> entity = generics[1];\n\t\t\tAuthorizeReturnObject authorize = beanFactory.findAnnotationOnBean(name, AuthorizeReturnObject.class);\n\t\t\tif (authorize != null) {\n\t\t\t\ttoProxy.add(entity);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t@Nullable Class<?> repository = generics[0];\n\t\t\tAssert.state(repository != null, \"Repository resolved from \" + type + \" cannot be null\");\n\t\t\tfor (Method method : repository.getDeclaredMethods()) {\n\t\t\t\tAuthorizeReturnObject returnObject = this.scanner.scan(method, repository);\n\t\t\t\tif (returnObject == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// optimistically assume that the entity needs wrapping if any of the\n\t\t\t\t// repository methods use @AuthorizeReturnObject\n\t\t\t\ttoProxy.add(entity);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tnew AuthorizeReturnObjectHintsRegistrar(this.proxyFactory, toProxy).registerHints(hints, beanFactory);\n\t}\n\n}\n"
  },
  {
    "path": "data/src/main/java/org/springframework/security/data/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * AOT integration for Spring Security's Data integration.\n */\n@NullMarked\npackage org.springframework.security.data.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "data/src/main/java/org/springframework/security/data/repository/query/SecurityEvaluationContextExtension.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.data.repository.query;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.data.spel.spi.EvaluationContextExtension;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.expression.DenyAllPermissionEvaluator;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * By defining this object as a Bean, Spring Security is exposed as SpEL expressions for\n * creating Spring Data queries.\n *\n * <p>\n * With Java based configuration, we can define the bean using the following:\n *\n * <p>\n * For example, if you return a UserDetails that extends the following User object:\n *\n * <pre>\n * &#064;Entity\n * public class User {\n *     &#064;GeneratedValue(strategy = GenerationType.AUTO)\n *     &#064;Id\n *     private Long id;\n *\n *     ...\n * }\n * </pre>\n *\n * <p>\n * And you have a Message object that looks like the following:\n *\n * <pre>\n * &#064;Entity\n * public class Message {\n *     &#064;Id\n *     &#064;GeneratedValue(strategy = GenerationType.AUTO)\n *     private Long id;\n *\n *     &#064;OneToOne\n *     private User to;\n *\n *     ...\n * }\n * </pre>\n *\n * You can use the following {@code Query} annotation to search for only messages that are\n * to the current user:\n *\n * <pre>\n * &#064;Repository\n * public interface SecurityMessageRepository extends MessageRepository {\n *\n * \t&#064;Query(&quot;select m from Message m where m.to.id = ?#{ principal?.id }&quot;)\n * \tList&lt;Message&gt; findAll();\n * }\n * </pre>\n *\n * This works because the principal in this instance is a User which has an id field on\n * it.\n *\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 4.0\n */\npublic class SecurityEvaluationContextExtension implements EvaluationContextExtension {\n\n\tprivate static final String DEFAULT_ROLE_PREFIX = \"ROLE_\";\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable Authentication authentication;\n\n\tprivate AuthorizationManagerFactory<Object> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();\n\n\tprivate PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();\n\n\tprivate String defaultRolePrefix = DEFAULT_ROLE_PREFIX;\n\n\t/**\n\t * Creates a new instance that uses the current {@link Authentication} found on the\n\t * {@link org.springframework.security.core.context.SecurityContextHolder}.\n\t */\n\tpublic SecurityEvaluationContextExtension() {\n\t}\n\n\t/**\n\t * Creates a new instance that always uses the same {@link Authentication} object.\n\t * @param authentication the {@link Authentication} to use\n\t */\n\tpublic SecurityEvaluationContextExtension(@Nullable Authentication authentication) {\n\t\tthis.authentication = authentication;\n\t}\n\n\t@Override\n\tpublic String getExtensionId() {\n\t\treturn \"security\";\n\t}\n\n\t@Override\n\tpublic SecurityExpressionRoot<Object> getRootObject() {\n\t\tAuthentication authentication = getAuthentication();\n\t\tSecurityExpressionRoot<Object> root = new SecurityExpressionRoot<>(() -> authentication, new Object()) {\n\t\t};\n\t\troot.setAuthorizationManagerFactory(this.authorizationManagerFactory);\n\t\troot.setPermissionEvaluator(this.permissionEvaluator);\n\t\tif (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {\n\t\t\t// Ensure SecurityExpressionRoot can strip the custom role prefix\n\t\t\troot.setDefaultRolePrefix(this.defaultRolePrefix);\n\t\t}\n\t\treturn root;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t * @param securityContextHolderStrategy the {@link SecurityContextHolderStrategy} to\n\t * use. Cannot be null.\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tprivate @Nullable Authentication getAuthentication() {\n\t\tif (this.authentication != null) {\n\t\t\treturn this.authentication;\n\t\t}\n\t\tSecurityContext context = this.securityContextHolderStrategy.getContext();\n\t\treturn context.getAuthentication();\n\t}\n\n\t/**\n\t * Sets the {@link AuthorizationManagerFactory} to be used. The default is\n\t * {@link DefaultAuthorizationManagerFactory}.\n\t * @param authorizationManagerFactory the {@link AuthorizationManagerFactory} to use.\n\t * Cannot be null.\n\t * @since 7.0\n\t */\n\tpublic void setAuthorizationManagerFactory(AuthorizationManagerFactory<Object> authorizationManagerFactory) {\n\t\tAssert.notNull(authorizationManagerFactory, \"authorizationManagerFactory cannot be null\");\n\t\tthis.authorizationManagerFactory = authorizationManagerFactory;\n\t}\n\n\t/**\n\t * Allows accessing the {@link DefaultAuthorizationManagerFactory} for getting and\n\t * setting defaults. This method will be removed in Spring Security 8.\n\t * @return the {@link DefaultAuthorizationManagerFactory}\n\t * @throws IllegalStateException if a different {@link AuthorizationManagerFactory}\n\t * was already set\n\t */\n\tprivate DefaultAuthorizationManagerFactory<Object> getDefaultAuthorizationManagerFactory() {\n\t\tif (!(this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<Object> defaultAuthorizationManagerFactory)) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"authorizationManagerFactory must be an instance of DefaultAuthorizationManagerFactory\");\n\t\t}\n\n\t\treturn defaultAuthorizationManagerFactory;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. Default is\n\t * {@link AuthenticationTrustResolverImpl}. Cannot be null.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use\n\t * @since 5.8\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\tgetDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);\n\t}\n\n\t/**\n\t * Sets the {@link RoleHierarchy} to be used. Default is {@link NullRoleHierarchy}.\n\t * Cannot be null.\n\t * @param roleHierarchy the {@link RoleHierarchy} to use\n\t * @since 5.8\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setRoleHierarchy(RoleHierarchy roleHierarchy) {\n\t\tAssert.notNull(roleHierarchy, \"roleHierarchy cannot be null\");\n\t\tgetDefaultAuthorizationManagerFactory().setRoleHierarchy(roleHierarchy);\n\t}\n\n\t/**\n\t * Sets the {@link PermissionEvaluator} to be used. Default is\n\t * {@link DenyAllPermissionEvaluator}. Cannot be null.\n\t * @param permissionEvaluator the {@link PermissionEvaluator} to use\n\t * @since 5.8\n\t */\n\tpublic void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {\n\t\tAssert.notNull(permissionEvaluator, \"permissionEvaluator cannot be null\");\n\t\tthis.permissionEvaluator = permissionEvaluator;\n\t}\n\n\t/**\n\t * Sets the default prefix to be added to\n\t * {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasAnyRole(String...)}\n\t * or\n\t * {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasRole(String)}.\n\t * For example, if hasRole(\"ADMIN\") or hasRole(\"ROLE_ADMIN\") is passed in, then the\n\t * role ROLE_ADMIN will be used when the defaultRolePrefix is \"ROLE_\" (default).\n\t * @param defaultRolePrefix the default prefix to add to roles. The default is\n\t * \"ROLE_\".\n\t * @since 5.8\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setDefaultRolePrefix(String defaultRolePrefix) {\n\t\tgetDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);\n\t\tthis.defaultRolePrefix = defaultRolePrefix;\n\t}\n\n}\n"
  },
  {
    "path": "data/src/main/java/org/springframework/security/data/repository/query/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security extensions for Spring Data queries.\n */\n@NullMarked\npackage org.springframework.security.data.repository.query;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "data/src/test/java/org/springframework/security/data/aot/hint/AuthorizeReturnObjectDataHintsRegistrarTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.data.aot.hint;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.data.repository.CrudRepository;\nimport org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;\nimport org.springframework.data.repository.core.support.RepositoryFactorySupport;\nimport org.springframework.security.aot.hint.SecurityHintsRegistrar;\nimport org.springframework.security.authorization.AuthorizationProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;\nimport org.springframework.security.authorization.method.AuthorizeReturnObject;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\n\n/**\n * Tests for {@link AuthorizeReturnObjectDataHintsRegistrar}\n */\npublic class AuthorizeReturnObjectDataHintsRegistrarTests {\n\n\tprivate final AuthorizationProxyFactory proxyFactory = spy(AuthorizationAdvisorProxyFactory.withDefaults());\n\n\tprivate final SecurityHintsRegistrar registrar = new AuthorizeReturnObjectDataHintsRegistrar(this.proxyFactory);\n\n\t@Test\n\tpublic void registerHintsWhenUsingAuthorizeReturnObjectThenRegisters() {\n\t\tGenericApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);\n\t\tRuntimeHints hints = new RuntimeHints();\n\t\tthis.registrar.registerHints(hints, context.getBeanFactory());\n\t\tassertThat(hints.reflection().typeHints().map((hint) -> hint.getType().getName())).containsOnly(\n\t\t\t\tcglibClassName(MyObject.class), cglibClassName(MySubObject.class), MyObject.class.getName(),\n\t\t\t\tMySubObject.class.getName());\n\t}\n\n\tprivate static String cglibClassName(Class<?> clazz) {\n\t\treturn clazz.getName() + \"$$SpringCGLIB$$0\";\n\t}\n\n\t@AuthorizeReturnObject\n\tpublic interface MyInterface extends CrudRepository<MyObject, Long> {\n\n\t\tList<MyObject> findAll();\n\n\t}\n\n\tpublic static class MyObject {\n\n\t\t@AuthorizeReturnObject\n\t\tpublic MySubObject get() {\n\t\t\treturn new MySubObject();\n\t\t}\n\n\t}\n\n\tpublic static class MySubObject {\n\n\t}\n\n\t@Configuration\n\tstatic class AppConfig {\n\n\t\t@Bean\n\t\tRepositoryFactoryBeanSupport<MyInterface, MyObject, Long> bean() {\n\t\t\treturn new RepositoryFactoryBeanSupport<>(MyInterface.class) {\n\t\t\t\t@Override\n\t\t\t\tpublic MyInterface getObject() {\n\t\t\t\t\treturn mock(MyInterface.class);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Class<? extends MyInterface> getObjectType() {\n\t\t\t\t\treturn MyInterface.class;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void afterPropertiesSet() {\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected RepositoryFactorySupport createRepositoryFactory() {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "data/src/test/java/org/springframework/security/data/repository/query/SecurityEvaluationContextExtensionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.data.repository.query;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.expression.DenyAllPermissionEvaluator;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchy;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\npublic class SecurityEvaluationContextExtensionTests {\n\n\tSecurityEvaluationContextExtension securityExtension;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void getRootObjectSecurityContextHolderAuthenticationNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> getRoot().getAuthentication());\n\t}\n\n\t@Test\n\tpublic void getRootObjectSecurityContextHolderAuthentication() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tassertThat(getRoot().getAuthentication()).isSameAs(authentication);\n\t}\n\n\t@Test\n\tpublic void getRootObjectUseSecurityContextHolderStrategy() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(authentication));\n\t\tthis.securityExtension.setSecurityContextHolderStrategy(strategy);\n\t\tassertThat(getRoot().getAuthentication()).isSameAs(authentication);\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void getRootObjectExplicitAuthenticationOverridesSecurityContextHolder() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tassertThat(getRoot().getAuthentication()).isSameAs(explicit);\n\t}\n\n\t@Test\n\tpublic void getRootObjectExplicitAuthentication() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tassertThat(getRoot().getAuthentication()).isSameAs(explicit);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setTrustResolverWhenNullThenIllegalArgumentException() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.securityExtension.setTrustResolver(null))\n\t\t\t.withMessage(\"trustResolver cannot be null\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setTrustResolverWhenNotNullThenVerifyRootObject() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tAuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class);\n\t\tgiven(trustResolver.isAuthenticated(explicit)).willReturn(true);\n\t\tthis.securityExtension.setTrustResolver(trustResolver);\n\t\tassertThat(getRoot().isAuthenticated()).isTrue();\n\t\tverify(trustResolver).isAuthenticated(explicit);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setRoleHierarchyWhenNullThenIllegalArgumentException() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.securityExtension.setRoleHierarchy(null))\n\t\t\t.withMessage(\"roleHierarchy cannot be null\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setRoleHierarchyWhenNotNullThenVerifyRootObject() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_PARENT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tRoleHierarchy roleHierarchy = RoleHierarchyImpl.fromHierarchy(\"ROLE_PARENT > ROLE_EXPLICIT\");\n\t\tthis.securityExtension.setRoleHierarchy(roleHierarchy);\n\t\tassertThat(getRoot().hasRole(\"EXPLICIT\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void setPermissionEvaluatorWhenNullThenIllegalArgumentException() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.securityExtension.setPermissionEvaluator(null))\n\t\t\t.withMessage(\"permissionEvaluator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setPermissionEvaluatorWhenNotNullThenVerifyRootObject() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tPermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();\n\t\tthis.securityExtension.setPermissionEvaluator(permissionEvaluator);\n\t\tassertThat(getRoot()).extracting(\"permissionEvaluator\").isEqualTo(permissionEvaluator);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void setDefaultRolePrefixWhenCustomThenVerifyRootObject() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"CUSTOM_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tString defaultRolePrefix = \"CUSTOM_\";\n\t\tthis.securityExtension.setDefaultRolePrefix(defaultRolePrefix);\n\t\tassertThat(getRoot().hasRole(\"EXPLICIT\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void setAuthorizationManagerFactoryWithTrustResolverThenVerifyRootObject() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tAuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class);\n\t\tgiven(trustResolver.isAuthenticated(explicit)).willReturn(true);\n\t\tDefaultAuthorizationManagerFactory<Object> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setTrustResolver(trustResolver);\n\t\tthis.securityExtension.setAuthorizationManagerFactory(factory);\n\t\tassertThat(getRoot().isAuthenticated()).isTrue();\n\t\tverify(trustResolver).isAuthenticated(explicit);\n\t}\n\n\t@Test\n\tpublic void setAuthorizationManagerFactoryWithRoleHierarchyThenVerifyRootObject() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_PARENT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tRoleHierarchy roleHierarchy = RoleHierarchyImpl.fromHierarchy(\"ROLE_PARENT > ROLE_EXPLICIT\");\n\t\tDefaultAuthorizationManagerFactory<Object> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setRoleHierarchy(roleHierarchy);\n\t\tthis.securityExtension.setAuthorizationManagerFactory(factory);\n\t\tassertThat(getRoot().hasRole(\"EXPLICIT\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void setAuthorizationManagerFactoryWithRolePrefixThenVerifyRootObject() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"CUSTOM_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tString customRolePrefix = \"CUSTOM_\";\n\t\tDefaultAuthorizationManagerFactory<Object> factory = new DefaultAuthorizationManagerFactory<>();\n\t\tfactory.setRolePrefix(customRolePrefix);\n\t\tthis.securityExtension.setAuthorizationManagerFactory(factory);\n\t\tassertThat(getRoot().hasRole(\"EXPLICIT\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void getRootObjectWhenAdditionalFieldsNotSetThenVerifyDefaults() {\n\t\tTestingAuthenticationToken explicit = new TestingAuthenticationToken(\"explicit\", \"password\", \"ROLE_EXPLICIT\");\n\t\tthis.securityExtension = new SecurityEvaluationContextExtension(explicit);\n\t\tSecurityExpressionRoot<?> securityExpressionRoot = getRoot();\n\t\tassertThat(securityExpressionRoot.isAuthenticated()).isTrue();\n\t\tassertThat(securityExpressionRoot.hasRole(\"PARENT\")).isFalse();\n\t\tassertThat(securityExpressionRoot.hasRole(\"EXPLICIT\")).isTrue();\n\t\tassertThat(securityExpressionRoot.hasPermission(new Object(), \"read\")).isFalse();\n\t}\n\n\tprivate SecurityExpressionRoot<?> getRoot() {\n\t\treturn this.securityExtension.getRootObject();\n\t}\n\n}\n"
  },
  {
    "path": "data/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "dependencies/spring-security-dependencies.gradle",
    "content": "plugins {\n\tid 'java-platform'\n\tid 'compile-warnings-error'\n}\n\njavaPlatform {\n\tallowDependencies()\n}\n\ndependencies {\n\tif (project.hasProperty(\"isOverrideVersionCatalog\")) {\n\t\tdef springFrameworkVersion = project.property(\"springFrameworkVersion\")\n\t\tdef reactorVersion = project.property(\"reactorVersion\")\n\t\tdef springDataVersion = project.property(\"springDataVersion\")\n\t\tapi platform(\"org.springframework:spring-framework-bom:$springFrameworkVersion\")\n\t\tapi platform(\"io.projectreactor:reactor-bom:$reactorVersion\")\n\t\tapi platform(\"org.springframework.data:spring-data-bom:$springDataVersion\")\n\t} else {\n\t\tapi platform(libs.org.springframework.spring.framework.bom)\n\t\tapi platform(libs.io.projectreactor.reactor.bom)\n\t\tapi platform(libs.org.springframework.data.spring.data.bom)\n\t}\n\tapi platform(libs.io.rsocket.rsocket.bom)\n\tapi platform(libs.org.junit.junit.bom)\n\tapi platform(libs.org.mockito.mockito.bom)\n\tapi platform(libs.org.jetbrains.kotlin.kotlin.bom)\n\tapi platform(libs.org.jetbrains.kotlinx.kotlinx.coroutines.bom)\n\tapi platform(libs.com.fasterxml.jackson.jackson.bom)\n\tapi platform(libs.tools.jackson.jackson.bom)\n\tconstraints {\n\t\tapi libs.ch.qos.logback.logback.classic\n\t\tapi libs.com.google.inject.guice\n\t\tapi libs.com.nimbusds.nimbus.jose.jwt\n\t\tapi libs.com.nimbusds.oauth2.oidc.sdk\n\t\tapi libs.com.squareup.okhttp3.mockwebserver\n\t\tapi libs.com.squareup.okhttp3.okhttp\n\t\tapi libs.com.unboundid.unboundid.ldapsdk\n\t\tapi libs.commons.collections\n\t\tapi libs.io.mockk\n\t\tapi libs.io.micrometer.context.propagation\n\t\tapi libs.io.micrometer.micrometer.observation\n\t\tapi libs.jakarta.annotation.jakarta.annotation.api\n\t\tapi libs.jakarta.inject.jakarta.inject.api\n\t\tapi libs.jakarta.servlet.jsp.jstl.jakarta.servlet.jsp.jstl.api\n\t\tapi libs.jakarta.servlet.jsp.jakarta.servlet.jsp.api\n\t\tapi libs.jakarta.servlet.jakarta.servlet.api\n\t\tapi libs.jakarta.xml.bind.jakarta.xml.bind.api\n\t\tapi libs.jakarta.persistence.jakarta.persistence.api\n\t\tapi libs.jakarta.websocket.jakarta.websocket.api\n\t\tapi libs.jakarta.websocket.jakarta.websocket.client.api\n\t\tapi libs.ldapsdk\n\t\tapi libs.net.sourceforge.htmlunit\n\t\tapi libs.org.htmlunit.htmlunit\n\t\tapi libs.org.apache.httpcomponents.httpclient\n\t\tapi libs.org.aspectj.aspectjrt\n\t\tapi libs.org.aspectj.aspectjweaver\n\t\tapi libs.org.assertj.assertj.core\n\t\tapi libs.org.bouncycastle.bcpkix.jdk15on\n\t\tapi libs.org.bouncycastle.bcprov.jdk15on\n\t\tapi libs.org.eclipse.jetty.jetty.server\n\t\tapi libs.org.eclipse.jetty.jetty.servlet\n\t\tapi libs.org.hamcrest\n\t\tapi libs.org.hibernate.orm.hibernate.core\n\t\tapi libs.org.hsqldb\n\t\tapi libs.com.jayway.jsonpath.json.path\n\t\tapi libs.org.apereo.cas.client.cas.client.core\n\t\tapi libs.org.opensaml.opensaml5.saml.api\n\t\tapi libs.org.opensaml.opensaml5.saml.impl\n\t\tapi libs.org.python.jython\n\t\tapi libs.org.seleniumhq.selenium.htmlunit.driver\n\t\tapi libs.org.seleniumhq.selenium.selenium.java\n\t\tapi libs.org.seleniumhq.selenium.selenium.support\n\t\tapi libs.org.skyscreamer.jsonassert\n\t\tapi libs.org.slf4j.log4j.over.slf4j\n\t\tapi libs.org.slf4j.slf4j.api\n\t\tapi libs.org.springframework.ldap.spring.ldap.core\n\t\tapi libs.org.synchronoss.cloud.nio.multipart.parser\n\t\tapi libs.org.apache.maven.resolver.maven.resolver.connector.basic\n\t\tapi libs.org.apache.maven.resolver.maven.resolver.impl\n\t\tapi libs.org.apache.maven.resolver.maven.resolver.transport.http\n\t\tapi libs.org.apache.maven.maven.resolver.provider\n\t\tapi libs.org.instancio.instancio.junit\n\t\tapi libs.com.password4j.password4j\n\t}\n}\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "/*-antora-playbook.yml\n"
  },
  {
    "path": "docs/antora-playbook.yml",
    "content": "antora:\n  extensions:\n    - require: '@springio/antora-extensions'\n      root_component_name: 'security'\nsite:\n  title: Spring Security\n  url: https://docs.spring.io/spring-security/reference\n  robots: allow\ngit:\n  ensure_git_suffix: false\ncontent:\n  sources:\n    - url: https://github.com/spring-projects/spring-security\n      branches: [main, '5.{{6..9},{1..9}+({0..9})}.x', '6.+({0..9}).x']\n      tags: ['5.{{6..9},{1..9}+({0..9})}.{0..99}?(-RC+({0..9}))', '6.+({0..9}).+({0..9})?(-{RC,M}*)','!(5.6.{0..10}*)', '!(5.7.{0..8}*)', '!(5.8.{0..3}?({-RC,-M}+({0..9})))','!(6.0.{0..3}*)','!(6.1.0*)']\n      start_path: docs\nasciidoc:\n  attributes:\n    page-stackoverflow-url: https://stackoverflow.com/tags/spring-security\n    page-related-doc-categories: security\n    page-related-doc-projects: framework,graphql\n    hide-uri-scheme: '@'\n    tabs-sync-option: '@'\n  extensions:\n    - '@asciidoctor/tabs'\n    - '@springio/asciidoctor-extensions'\n    - '@springio/asciidoctor-extensions/javadoc-extension'\nurls:\n  latest_version_segment_strategy: redirect:to\n  latest_version_segment: ''\n  redirect_facility: httpd\nui:\n  bundle:\n    url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.26/ui-bundle.zip\n    snapshot: true\nruntime:\n  log:\n    failure_level: warn\n"
  },
  {
    "path": "docs/antora.yml",
    "content": "name: security\nversion: true\ntitle: Spring Security\nnav:\n- modules/ROOT/nav.adoc\next:\n  collector:\n    run:\n      command: gradlew -q -PbuildSrc.skipTests=true :spring-security-docs:generateAntoraResources\n      local: true\n    scan:\n      dir: ./build/generated-antora-resources\nasciidoc:\n  attributes:\n    icondir: icons\n    gh-old-samples-url: 'https://github.com/spring-projects/spring-security/tree/5.4.x/samples'\n    gh-samples-url: \"https://github.com/spring-projects/spring-security-samples/tree/{gh-tag}\"\n    gh-url: \"https://github.com/spring-projects/spring-security/tree/{gh-tag}\"\n    include-java: 'example$docs-src/test/java/org/springframework/security/docs'\n    include-kotlin: 'example$docs-src/test/kotlin/org/springframework/security/kt/docs'\n    include-xml: 'example$docs-src/test/resources/org/springframework/security/docs'\n"
  },
  {
    "path": "docs/articles/src/docbook/codebase-structure.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?oxygen RNGSchema=\"https://www.oasis-open.org/docbook/xml/5.0/rng/docbook.rng\" type=\"xml\"?>\n<article xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\tversion=\"5.0\">\n\t<info>\n\t\t<title>The Spring Security 3.0 Codebase</title>\n\t\t<subtitle>Why have the packages changed in Spring Security 3.0?</subtitle>\n\t\t<author>\n\t\t\t<personname>Luke Taylor</personname>\n\t\t\t<affiliation><orgname>SpringSource</orgname></affiliation></author>\n\t\t<abstract>\n\t\t\t<para>A quick introduction to the code modules and package structure of the Spring\n\t\t\t\tSecurity 3.0 codebase.</para>\n\t\t</abstract>\n\t</info>\n\t<sect1>\n\t\t<title>Introduction</title>\n\t\t<para>In versions prior to 3.0, most of Spring Security's code was contained in the\n\t\t\t\t<filename>spring-security-core</filename> jar<footnote>\n\t\t\t\t<para>There was also an additional <filename>spring-security-core-tiger</filename>\n\t\t\t\t\tjar which contained the Java 5 specific code. In Spring Security 3.0, Java 5 is\n\t\t\t\t\tthe minimum supported platform, so this code is now part of the core.</para>\n\t\t\t</footnote>. Over the years, as more features have been added, it has become more\n\t\t\tdifficult to track the dependencies both within the codebase itself and also on third\n\t\t\tparty libraries. For example, it's hard for a user to determine which of the listed\n\t\t\tdependencies in the core Maven <filename>pom.xml</filename> are required for a\n\t\t\tparticular set of features within the framework.</para>\n\t\t<para>In addition, the original package structure and class names have been around since the\n\t\t\tframework's origins as Acegi Security in 2004, when only a few basic authentication\n\t\t\tmechanisms were supported. As the amount of code has increased and the feature set has\n\t\t\texpanded, this package structure has begun to show its age.</para>\n\t\t<figure xml:id=\"structure-2.0.4\">\n\t\t\t<title>Spring Security 2.0.4 Package Structure</title>\n\t\t\t<mediaobject>\n\t\t\t\t<imageobject>\n\t\t\t\t\t<imagedata fileref=\"images/spring-security-2.0.4.png\" scale=\"80\" align=\"center\"\n\t\t\t\t\t/>\n\t\t\t\t</imageobject>\n\t\t\t</mediaobject>\n\t\t</figure>\n\t\t<para>\n\t\t\t<xref linkend=\"structure-2.0.4\"/> shows the high-level package diagram of the core,\n\t\t\tcore-tiger, cas-client and acl jars in the 2.0.4 release, as produced by the\n\t\t\tStructure101 tool<footnote>\n\t\t\t\t<para>Structure101 is an excellent tool for analyzing your own code or for\n\t\t\t\t\tunderstanding someone else's. It is developed by <link\n\t\t\t\t\t\txlink:href=\"https://www.headwaysoftware.com\">Headway Software</link>. </para>\n\t\t\t</footnote>. You don't have to be an expert in code structure to realise that there is a\n\t\t\tbit of a problem here. There are a lot of circular references and no clear overall\n\t\t\tdependency structure within the packages. There are also some issues with packages being\n\t\t\tsplit across jar boundaries, which can cause problems with OSGi, for example.<footnote>\n\t\t\t\t<para>For more information on how to structure a large codebase, Juergen Hoeller's\n\t\t\t\t\t\t<quote>Organization of Large Code Bases</quote> is an excellent overview of\n\t\t\t\t\tthe topic where he shares some of the insights gained from maintaining the\n\t\t\t\t\tSpring Framework through multiple versions. You can find him discussing the\n\t\t\t\t\ttopic in an online interview <link\n\t\t\t\t\t\txlink:href=\"https://www.se-radio.net/transcript-82-organization-large-code-bases-juergen-hoeller\"\n\t\t\t\t\t\t>transcript</link> and an <link\n\t\t\t\t\t\txlink:href=\"https://www.infoq.com/presentations/code-organization-large-projects\"\n\t\t\t\t\t\t>InfoQ video</link>. </para>\n\t\t\t</footnote>. This fragility in the code structure would likely have caused a maintenance\n\t\t\toverhead as Spring Security evolved, so the decision was made to restructure the code\n\t\t\tfor the 3.0 release to give us a stable base for future development. </para>\n\t\t<para>Let's take a look at how things are now organised.</para>\n\t</sect1>\n\t<sect1>\n\t\t<title>Spring Security 3.0</title>\n\t\t<sect2>\n\t\t\t<title>Project Jars</title>\n\t\t\t<para>The first thing we did was split the core out into several jars. The\n\t\t\t\t\t<filename>spring-security-core</filename> jar now contains only basic\n\t\t\t\tauthentication and access-control code and is much cleaner. It has no dependencies\n\t\t\t\ton LDAP or the servlet API, for example, and there are now separate jars for\n\t\t\t\tweb-specific code and for LDAP. We've also split out the namespace parsing code out\n\t\t\t\tint a separate jar, as it depends on most of the other jars and doesn't expose any\n\t\t\t\tpublic APIs that you are likely to use directly in your application. You only need\n\t\t\t\tto use it if you are using Spring Security namespace configuration in your\n\t\t\t\tapplication context XML files. The main project jars are shown in the following\n\t\t\t\t\ttable.<table xml:id=\"jar-files-3.0\">\n\t\t\t\t\t<title>Spring Security Jars</title>\n\t\t\t\t\t<tgroup cols=\"3\" align=\"left\">\n\t\t\t\t\t\t<colspec colnum=\"1\" colname=\"c1\" colwidth=\"0.59*\"/>\n\t\t\t\t\t\t<colspec colnum=\"2\" colname=\"c2\" colwidth=\"0.92*\"/>\n\t\t\t\t\t\t<colspec colnum=\"3\" colname=\"c3\" colwidth=\"0.88*\"/>\n\t\t\t\t\t\t<colspec colnum=\"4\" colname=\"c4\" colwidth=\"1.61*\"/>\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t<entry align=\"center\">Jar Name</entry>\n\t\t\t\t\t\t\t\t<entry align=\"center\">Description</entry>\n\t\t\t\t\t\t\t\t<entry align=\"center\">When to use</entry>\n\t\t\t\t\t\t\t\t<entry align=\"center\">Root Package(s)</entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t<entry valign=\"middle\">spring-security-core</entry>\n\t\t\t\t\t\t\t\t<entry>Core authentication and access-control classes and interfaces.\n\t\t\t\t\t\t\t\t\tRemoting support and basic provisioning APIs.</entry>\n\t\t\t\t\t\t\t\t<entry>Required by any application which uses Spring Security.\n\t\t\t\t\t\t\t\t\tSupports standalone applications, remote clients, method\n\t\t\t\t\t\t\t\t\t(service layer) security and JDBC user provisioning.</entry>\n\t\t\t\t\t\t\t\t<entry>\n\t\t\t\t\t\t\t\t\t<literal>org.springframework.security.core</literal>,\n\t\t\t\t\t\t\t\t\t\t<literal>org.springframework.security.access</literal>,\n\t\t\t\t\t\t\t\t\t\t<literal>org.springframework.security.authentication</literal>,\n\t\t\t\t\t\t\t\t\t\t<literal>org.springframework.security.provisioning</literal>,\n\t\t\t\t\t\t\t\t\t\t<literal>org.springframework.security.remoting</literal>\n\t\t\t\t\t\t\t\t</entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t<entry valign=\"middle\">spring-security-web</entry>\n\t\t\t\t\t\t\t\t<entry>Filters and other web-security infrastructure and related\n\t\t\t\t\t\t\t\t\tcode. Anything with a servlet API dependency.</entry>\n\t\t\t\t\t\t\t\t<entry>If you require Spring Security web authentication services\n\t\t\t\t\t\t\t\t\tand URL-based access-control</entry>\n\t\t\t\t\t\t\t\t<entry><literal>org.springframework.security.web</literal></entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t<entry valign=\"middle\">spring-security-config</entry>\n\t\t\t\t\t\t\t\t<entry>Namespace parsing code.</entry>\n\t\t\t\t\t\t\t\t<entry>If you are using the Spring Security XML namespace.</entry>\n\t\t\t\t\t\t\t\t<entry><literal>org.springframework.security.config</literal></entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t<entry valign=\"middle\">spring-security-ldap</entry>\n\t\t\t\t\t\t\t\t<entry>LDAP authentication and provisioning code.</entry>\n\t\t\t\t\t\t\t\t<entry>If you need to use LDAP authentication or manage LDAP user\n\t\t\t\t\t\t\t\t\tentries.</entry>\n\t\t\t\t\t\t\t\t<entry><literal>org.springframework.security.ldap</literal></entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t<entry valign=\"middle\">spring-security-acl</entry>\n\t\t\t\t\t\t\t\t<entry>Domain object ACL implementation.</entry>\n\t\t\t\t\t\t\t\t<entry>If you need to apply security to specific domain object\n\t\t\t\t\t\t\t\t\tinstances within your application.</entry>\n\t\t\t\t\t\t\t\t<entry><literal>org.springframework.security.acls</literal></entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t<entry valign=\"middle\">spring-security-cas-client</entry>\n\t\t\t\t\t\t\t\t<entry>Spring Security's CAS client integration.</entry>\n\t\t\t\t\t\t\t\t<entry>If you want to use Spring Security web authentication with a\n\t\t\t\t\t\t\t\t\tCAS single sign-on server.</entry>\n\t\t\t\t\t\t\t\t<entry><literal>org.springframework.security.cas</literal></entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t\t<row>\n\t\t\t\t\t\t\t\t<entry valign=\"middle\">spring-security-openid</entry>\n\t\t\t\t\t\t\t\t<entry>OpenID web authentication support.</entry>\n\t\t\t\t\t\t\t\t<entry>If you need to authenticate users against an external OpenID\n\t\t\t\t\t\t\t\t\tserver. (Deprecated)</entry>\n\t\t\t\t\t\t\t\t<entry><literal>org.springframework.security.openid</literal></entry>\n\t\t\t\t\t\t\t</row>\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</tgroup>\n\t\t\t\t</table></para>\n\t\t\t<para>There is now a clearer separation of concerns at the jar level. For example, you\n\t\t\t\tonly need the web jar (and its transitive dependencies) if you are writing a web\n\t\t\t\tapplication. This also makes the code easier to navigate and understand. The\n\t\t\t\tdependencies between the 3.0 jars which now make up the same code set of code we\n\t\t\t\tlooked at for version 2.0.4 are shown in <xref linkend=\"jar-deps-3.0\"/>. <figure\n\t\t\t\t\txml:id=\"jar-deps-3.0\">\n\t\t\t\t\t<title>Inter-Jar Dependencies</title>\n\t\t\t\t\t<mediaobject>\n\t\t\t\t\t\t<imageobject>\n\t\t\t\t\t\t\t<imagedata fileref=\"images/spring-security-3.0.0.M2-jars.png\"\n\t\t\t\t\t\t\t\talign=\"center\"/>\n\t\t\t\t\t\t</imageobject>\n\t\t\t\t\t</mediaobject>\n\t\t\t\t</figure></para>\n\t\t</sect2>\n\t\t<sect2>\n\t\t\t<title>Package Structure</title>\n\t\t\t<para>The package layout in 3.0 is show in <xref linkend=\"structure-3.0\"/>. As you can\n\t\t\t\tsee, there are no longer any circular references and the structure is much clearer.\n\t\t\t\tThe <filename>core</filename> package and sub packages contain the basic classes and\n\t\t\t\tinterfaces which are used throughout the framework and the other two main packages\n\t\t\t\twithin the core jar are <filename>authentication</filename> and\n\t\t\t\t\t<filename>access</filename>. The <filename>access</filename> package contains\n\t\t\t\taccess-control/authorization code such as the\n\t\t\t\t\t<interfacename>AccessDecisionManager</interfacename> and related voter-based\n\t\t\t\timplementations, the interception and method security infrastructure, annotation\n\t\t\t\tclasses and support for Spring Security 3.0's expression-based access control. The\n\t\t\t\t\t<filename>authentication</filename> package contains the\n\t\t\t\t\t<interfacename>AuthenticationManager</interfacename> and related classes (such\n\t\t\t\tas authentication exception classes), the simple DAO-based authentication provider\n\t\t\t\tand password-encoders. <figure xml:id=\"structure-3.0\">\n\t\t\t\t\t<title>Spring Security 3.0.0.M1 Package Structure</title>\n\t\t\t\t\t<mediaobject>\n\t\t\t\t\t\t<imageobject>\n\t\t\t\t\t\t\t<imagedata fileref=\"images/spring-security-3.0.0.M1.png\" align=\"center\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</imageobject>\n\t\t\t\t\t</mediaobject>\n\t\t\t\t</figure></para>\n\t\t</sect2>\n\t</sect1>\n\t<sect1>\n\t\t<title>How will these changes affect you?</title>\n\t\t<para>If you are developing a new application then obviously you won't be affected, other\n\t\t\tthan by starting out with new package names. But what if you are upgrading an existing\n\t\t\tapplication or another framework to use Spring Security 3.0. The first thing is that you\n\t\t\twill obviously need to update build paths and dependency lists to take account of the\n\t\t\tnew jar modules, but the divisions there are straightforward (see the table above). How\n\t\t\tmuch the package restructuring will affect you will depend on how much you use the\n\t\t\tframework classes directly or in explicit bean configurations (if you are only using the\n\t\t\tnamespace for configuration then it will hide the changes from you). Your IDE should be\n\t\t\table to help with changing imports and finding out where classes have moved to (a simple\n\t\t\t\t<command>Ctrl-Shift-T</command>or <command>Ctrl-Shift-O</command> in Eclipse can do\n\t\t\twonders).</para>\n\t\t<para>There are other changes in 3.0 that will affect some users who want to upgrade but for\n\t\t\tthe most part, the underlying architecture is unchanged.</para>\n\t\t<para>We hope you enjoy using Spring Security 3.0.</para>\n\t</sect1>\n</article>\n"
  },
  {
    "path": "docs/modules/ROOT/examples/kerberos/AuthProviderConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.docs;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;\nimport org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;\nimport org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;\nimport org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;\nimport org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;\nimport org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\n\n//tag::snippetA[]\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Value(\"${app.service-principal}\")\n\tprivate String servicePrincipal;\n\n\t@Value(\"${app.keytab-location}\")\n\tprivate String keytabLocation;\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\tKerberosAuthenticationProvider kerberosAuthenticationProvider = kerberosAuthenticationProvider();\n\t\tKerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider = kerberosServiceAuthenticationProvider();\n\t\tProviderManager providerManager = new ProviderManager(kerberosAuthenticationProvider,\n\t\t\t\tkerberosServiceAuthenticationProvider);\n\n\t\thttp\n\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t.requestMatchers(\"/\", \"/home\").permitAll()\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.exceptionHandling()\n\t\t\t\t.authenticationEntryPoint(spnegoEntryPoint())\n\t\t\t\t.and()\n\t\t\t.formLogin()\n\t\t\t\t.loginPage(\"/login\").permitAll()\n\t\t\t\t.and()\n\t\t\t.logout()\n\t\t\t\t.permitAll()\n\t\t\t\t.and()\n\t\t\t.authenticationProvider(kerberosAuthenticationProvider())\n\t\t\t.authenticationProvider(kerberosServiceAuthenticationProvider())\n\t\t\t.addFilterBefore(spnegoAuthenticationProcessingFilter(providerManager),\n\t\t\t\t\tBasicAuthenticationFilter.class);\n\t\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic KerberosAuthenticationProvider kerberosAuthenticationProvider() {\n\t\tKerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();\n\t\tSunJaasKerberosClient client = new SunJaasKerberosClient();\n\t\tclient.setDebug(true);\n\t\tprovider.setKerberosClient(client);\n\t\tprovider.setUserDetailsService(dummyUserDetailsService());\n\t\treturn provider;\n\t}\n\n\t@Bean\n\tpublic SpnegoEntryPoint spnegoEntryPoint() {\n\t\treturn new SpnegoEntryPoint(\"/login\");\n\t}\n\n\tpublic SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(\n\t\t\tAuthenticationManager authenticationManager) {\n\t\tSpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();\n\t\tfilter.setAuthenticationManager(authenticationManager);\n\t\treturn filter;\n\t}\n\n\t@Bean\n\tpublic KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {\n\t\tKerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();\n\t\tprovider.setTicketValidator(sunJaasKerberosTicketValidator());\n\t\tprovider.setUserDetailsService(dummyUserDetailsService());\n\t\treturn provider;\n\t}\n\n\t@Bean\n\tpublic SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {\n\t\tSunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();\n\t\tticketValidator.setServicePrincipal(servicePrincipal);\n\t\tticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));\n\t\tticketValidator.setDebug(true);\n\t\treturn ticketValidator;\n\t}\n\n\t@Bean\n\tpublic DummyUserDetailsService dummyUserDetailsService() {\n\t\treturn new DummyUserDetailsService();\n\t}\n}\n//end::snippetA[]\n"
  },
  {
    "path": "docs/modules/ROOT/examples/kerberos/AuthProviderConfigTest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.docs;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@ContextConfiguration(locations= {\"AuthProviderConfig.xml\"})\npublic class AuthProviderConfigTest {\n\n\t@Test\n\tpublic void configLoads() {}\n}\n"
  },
  {
    "path": "docs/modules/ROOT/examples/kerberos/DummyUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.docs;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\n\n//tag::snippetA[]\npublic class DummyUserDetailsService implements UserDetailsService {\n\n    @Override\n    public UserDetails loadUserByUsername(String username)\n            throws UsernameNotFoundException {\n        return new User(username, \"notUsed\", true, true, true, true,\n                AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n    }\n\n}\n//end::snippetA[]\n"
  },
  {
    "path": "docs/modules/ROOT/examples/kerberos/KerberosLdapContextSourceConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.client.docs;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;\nimport org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource;\nimport org.springframework.security.ldap.search.FilterBasedLdapUserSearch;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsService;\n\npublic class KerberosLdapContextSourceConfig {\n\n//tag::snippetA[]\n\t@Value(\"${app.ad-server}\")\n\tprivate String adServer;\n\n\t@Value(\"${app.service-principal}\")\n\tprivate String servicePrincipal;\n\n\t@Value(\"${app.keytab-location}\")\n\tprivate String keytabLocation;\n\n\t@Value(\"${app.ldap-search-base}\")\n\tprivate String ldapSearchBase;\n\n\t@Value(\"${app.ldap-search-filter}\")\n\tprivate String ldapSearchFilter;\n\n\t@Bean\n\tpublic KerberosLdapContextSource kerberosLdapContextSource() {\n\t\tKerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer);\n\t\tSunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();\n\t\tloginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));\n\t\tloginConfig.setServicePrincipal(servicePrincipal);\n\t\tloginConfig.setDebug(true);\n\t\tloginConfig.setIsInitiator(true);\n\t\tcontextSource.setLoginConfig(loginConfig);\n\t\treturn contextSource;\n\t}\n\n\t@Bean\n\tpublic LdapUserDetailsService ldapUserDetailsService() {\n\t\tFilterBasedLdapUserSearch userSearch =\n\t\t\t\tnew FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, kerberosLdapContextSource());\n\t\tLdapUserDetailsService service = new LdapUserDetailsService(userSearch);\n\t\tservice.setUserDetailsMapper(new LdapUserDetailsMapper());\n\t\treturn service;\n\t}\n//end::snippetA[]\n\n}\n"
  },
  {
    "path": "docs/modules/ROOT/examples/kerberos/KerberosRestTemplateConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.client.docs;\n\nimport org.springframework.security.kerberos.client.KerberosRestTemplate;\n\npublic class KerberosRestTemplateConfig {\n\n//tag::snippetA[]\n    public void doWithTicketCache() {\n        KerberosRestTemplate restTemplate =\n                new KerberosRestTemplate();\n        restTemplate.getForObject(\"http://neo.example.org:8080/hello\", String.class);\n    }\n//end::snippetA[]\n\n//tag::snippetB[]\n    public void doWithKeytabFile() {\n        KerberosRestTemplate restTemplate =\n                new KerberosRestTemplate(\"/tmp/user2.keytab\", \"user2@EXAMPLE.ORG\");\n        restTemplate.getForObject(\"http://neo.example.org:8080/hello\", String.class);\n    }\n//end::snippetB[]\n\n}\n"
  },
  {
    "path": "docs/modules/ROOT/examples/kerberos/SpnegoConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.docs;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.ProviderManager;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;\nimport org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;\nimport org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;\nimport org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource;\nimport org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;\nimport org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;\nimport org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;\nimport org.springframework.security.ldap.search.FilterBasedLdapUserSearch;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsService;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\n\n//tag::snippetA[]\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Value(\"${app.ad-domain}\")\n\tprivate String adDomain;\n\n\t@Value(\"${app.ad-server}\")\n\tprivate String adServer;\n\n\t@Value(\"${app.service-principal}\")\n\tprivate String servicePrincipal;\n\n\t@Value(\"${app.keytab-location}\")\n\tprivate String keytabLocation;\n\n\t@Value(\"${app.ldap-search-base}\")\n\tprivate String ldapSearchBase;\n\n\t@Value(\"${app.ldap-search-filter}\")\n\tprivate String ldapSearchFilter;\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\tKerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider = kerberosServiceAuthenticationProvider();\n\t\tActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = activeDirectoryLdapAuthenticationProvider();\n\t\tProviderManager providerManager = new ProviderManager(kerberosServiceAuthenticationProvider,\n\t\t\t\tactiveDirectoryLdapAuthenticationProvider);\n\n\t\thttp\n\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t.requestMatchers(\"/\", \"/home\").permitAll()\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.exceptionHandling()\n\t\t\t\t.authenticationEntryPoint(spnegoEntryPoint())\n\t\t\t\t.and()\n\t\t\t.formLogin()\n\t\t\t\t.loginPage(\"/login\").permitAll()\n\t\t\t\t.and()\n\t\t\t.logout()\n\t\t\t\t.permitAll()\n\t\t\t\t.and()\n\t\t\t.authenticationProvider(activeDirectoryLdapAuthenticationProvider())\n\t\t\t.authenticationProvider(kerberosServiceAuthenticationProvider())\n\t\t\t.addFilterBefore(spnegoAuthenticationProcessingFilter(providerManager),\n\t\t\t\tBasicAuthenticationFilter.class);\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {\n\t\treturn new ActiveDirectoryLdapAuthenticationProvider(adDomain, adServer);\n\t}\n\n\t@Bean\n\tpublic SpnegoEntryPoint spnegoEntryPoint() {\n\t\treturn new SpnegoEntryPoint(\"/login\");\n\t}\n\n\tpublic SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(\n\t\t\tAuthenticationManager authenticationManager) {\n\t\tSpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();\n\t\tfilter.setAuthenticationManager(authenticationManager);\n\t\treturn filter;\n\t}\n\n\tpublic KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() throws Exception {\n\t\tKerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();\n\t\tprovider.setTicketValidator(sunJaasKerberosTicketValidator());\n\t\tprovider.setUserDetailsService(ldapUserDetailsService());\n\t\treturn provider;\n\t}\n\n\t@Bean\n\tpublic SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {\n\t\tSunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();\n\t\tticketValidator.setServicePrincipal(servicePrincipal);\n\t\tticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));\n\t\tticketValidator.setDebug(true);\n\t\treturn ticketValidator;\n\t}\n\n\t@Bean\n\tpublic KerberosLdapContextSource kerberosLdapContextSource() throws Exception {\n\t\tKerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer);\n\t\tcontextSource.setLoginConfig(loginConfig());\n\t\treturn contextSource;\n\t}\n\n\tpublic SunJaasKrb5LoginConfig loginConfig() throws Exception {\n\t\tSunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();\n\t\tloginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));\n\t\tloginConfig.setServicePrincipal(servicePrincipal);\n\t\tloginConfig.setDebug(true);\n\t\tloginConfig.setIsInitiator(true);\n\t\tloginConfig.afterPropertiesSet();\n\t\treturn loginConfig;\n\t}\n\n\t@Bean\n\tpublic LdapUserDetailsService ldapUserDetailsService() throws Exception {\n\t\tFilterBasedLdapUserSearch userSearch =\n\t\t\t\tnew FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, kerberosLdapContextSource());\n\t\tLdapUserDetailsService service =\n\t\t\t\tnew LdapUserDetailsService(userSearch, new ActiveDirectoryLdapAuthoritiesPopulator());\n\t\tservice.setUserDetailsMapper(new LdapUserDetailsMapper());\n\t\treturn service;\n\t}\n}\n//end::snippetA[]\n"
  },
  {
    "path": "docs/modules/ROOT/nav.adoc",
    "content": "* xref:index.adoc[Overview]\n* xref:prerequisites.adoc[Prerequisites]\n* xref:community.adoc[Community]\n* xref:whats-new.adoc[What's New]\n* xref:migration-8/index.adoc[Preparing for 8.0]\n* xref:migration/index.adoc[Migrating to 7]\n** xref:migration/servlet/index.adoc[Servlet]\n*** xref:migration/servlet/authorization.adoc[Authorization]\n*** xref:migration/servlet/oauth2.adoc[OAuth 2.0]\n*** xref:migration/servlet/saml2.adoc[SAML 2.0]\n** xref:migration/reactive.adoc[Reactive]\n* xref:getting-spring-security.adoc[Getting Spring Security]\n* xref:attachment$api/java/index.html[Javadoc]\n* xref:features/index.adoc[Features]\n** xref:features/authentication/index.adoc[Authentication]\n*** xref:features/authentication/password-storage.adoc[Password Storage]\n** xref:features/authorization/index.adoc[Authorization]\n** xref:features/exploits/index.adoc[Protection Against Exploits]\n*** xref:features/exploits/csrf.adoc[CSRF]\n*** xref:features/exploits/headers.adoc[HTTP Headers]\n*** xref:features/exploits/http.adoc[HTTP Requests]\n** xref:features/integrations/index.adoc[Integrations]\n*** REST Client\n**** xref:features/integrations/rest/http-service-client.adoc[HTTP Service Clients]\n*** xref:features/integrations/cryptography.adoc[Cryptography]\n*** xref:features/integrations/data.adoc[Spring Data]\n*** xref:features/integrations/concurrency.adoc[Java's Concurrency APIs]\n*** xref:features/integrations/jackson.adoc[Jackson]\n*** xref:features/integrations/localization.adoc[Localization]\n* xref:modules.adoc[Project Modules]\n* xref:samples.adoc[Samples]\n* xref:servlet/index.adoc[Servlet Applications]\n** xref:servlet/getting-started.adoc[Getting Started]\n** xref:servlet/architecture.adoc[Architecture]\n** xref:servlet/authentication/index.adoc[Authentication]\n*** xref:servlet/authentication/architecture.adoc[Authentication Architecture]\n*** xref:servlet/authentication/passwords/index.adoc[Username/Password]\n**** xref:servlet/authentication/passwords/input.adoc[Reading Username/Password]\n***** xref:servlet/authentication/passwords/form.adoc[Form]\n***** xref:servlet/authentication/passwords/basic.adoc[Basic]\n***** xref:servlet/authentication/passwords/digest.adoc[Digest]\n**** **** xref:servlet/authentication/passwords/storage.adoc[Password Storage]\n***** xref:servlet/authentication/passwords/in-memory.adoc[In Memory]\n***** xref:servlet/authentication/passwords/jdbc.adoc[JDBC]\n***** xref:servlet/authentication/passwords/user-details.adoc[UserDetails]\n***** xref:servlet/authentication/passwords/credentials-container.adoc[CredentialsContainer]\n***** xref:servlet/authentication/passwords/erasure.adoc[Password Erasure]\n***** xref:servlet/authentication/passwords/user-details-service.adoc[UserDetailsService]\n***** xref:servlet/authentication/passwords/password-encoder.adoc[PasswordEncoder]\n***** xref:servlet/authentication/passwords/dao-authentication-provider.adoc[DaoAuthenticationProvider]\n***** xref:servlet/authentication/passwords/ldap.adoc[LDAP]\n*** xref:servlet/authentication/mfa.adoc[Multi-Factor Authentication]\n*** xref:servlet/authentication/persistence.adoc[Persistence]\n*** xref:servlet/authentication/passkeys.adoc[Passkeys]\n*** xref:servlet/authentication/onetimetoken.adoc[One-Time Token]\n*** xref:servlet/authentication/session-management.adoc[Session Management]\n*** xref:servlet/authentication/rememberme.adoc[Remember Me]\n*** xref:servlet/authentication/anonymous.adoc[Anonymous]\n*** xref:servlet/authentication/preauth.adoc[Pre-Authentication]\n*** xref:servlet/authentication/jaas.adoc[JAAS]\n*** xref:servlet/authentication/cas.adoc[CAS]\n*** xref:servlet/authentication/x509.adoc[X509]\n*** xref:servlet/authentication/runas.adoc[Run-As]\n*** xref:servlet/authentication/logout.adoc[Logout]\n*** xref:servlet/authentication/events.adoc[Authentication Events]\n** xref:servlet/authentication/kerberos/index.adoc[Kerberos]\n*** xref:servlet/authentication/kerberos/introduction.adoc[Introduction]\n*** xref:servlet/authentication/kerberos/ssk.adoc[Reference]\n*** xref:servlet/authentication/kerberos/samples.adoc[Samples]\n*** xref:servlet/authentication/kerberos/appendix.adoc[Appendices]\n** xref:servlet/authorization/index.adoc[Authorization]\n*** xref:servlet/authorization/architecture.adoc[Authorization Architecture]\n*** xref:servlet/authorization/authorize-http-requests.adoc[Authorize HTTP Requests]\n*** xref:servlet/authorization/method-security.adoc[Method Security]\n*** xref:servlet/authorization/acls.adoc[Domain Object Security ACLs]\n*** xref:servlet/authorization/events.adoc[Authorization Events]\n** xref:servlet/oauth2/index.adoc[OAuth2]\n*** xref:servlet/oauth2/login/index.adoc[OAuth2 Log In]\n**** xref:servlet/oauth2/login/core.adoc[Core Configuration]\n**** xref:servlet/oauth2/login/advanced.adoc[Advanced Configuration]\n**** xref:servlet/oauth2/login/logout.adoc[OIDC Logout]\n*** xref:servlet/oauth2/client/index.adoc[OAuth2 Client]\n**** xref:servlet/oauth2/client/core.adoc[Core Interfaces and Classes]\n**** xref:servlet/oauth2/client/authorization-grants.adoc[OAuth2 Authorization Grants]\n**** xref:servlet/oauth2/client/client-authentication.adoc[OAuth2 Client Authentication]\n**** xref:servlet/oauth2/client/authorized-clients.adoc[OAuth2 Authorized Clients]\n*** xref:servlet/oauth2/resource-server/index.adoc[OAuth2 Resource Server]\n**** xref:servlet/oauth2/resource-server/jwt.adoc[JWT]\n**** xref:servlet/oauth2/resource-server/opaque-token.adoc[Opaque Token]\n**** xref:servlet/oauth2/resource-server/multitenancy.adoc[Multitenancy]\n**** xref:servlet/oauth2/resource-server/bearer-tokens.adoc[Bearer Tokens]\n**** xref:servlet/oauth2/resource-server/dpop-tokens.adoc[DPoP-bound Access Tokens]\n**** xref:servlet/oauth2/resource-server/protected-resource-metadata.adoc[Protected Resource Metadata]\n*** xref:servlet/oauth2/authorization-server/index.adoc[OAuth2 Authorization Server]\n**** xref:servlet/oauth2/authorization-server/getting-started.adoc[Getting Started]\n**** xref:servlet/oauth2/authorization-server/configuration-model.adoc[Configuration Model]\n**** xref:servlet/oauth2/authorization-server/core-model-components.adoc[Core Model / Components]\n**** xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc[Protocol Endpoints]\n** xref:servlet/saml2/index.adoc[SAML2]\n*** xref:servlet/saml2/login/index.adoc[SAML2 Log In]\n**** xref:servlet/saml2/login/overview.adoc[SAML2 Log In Overview]\n**** xref:servlet/saml2/login/authentication-requests.adoc[SAML2 Authentication Requests]\n**** xref:servlet/saml2/login/authentication.adoc[SAML2 Authentication Responses]\n*** xref:servlet/saml2/logout.adoc[SAML2 Logout]\n*** xref:servlet/saml2/metadata.adoc[SAML2 Metadata]\n*** xref:servlet/saml2/saml-extension-migration.adoc[Migrating from Spring Security SAML Extension]\n** xref:servlet/exploits/index.adoc[Protection Against Exploits]\n*** xref:servlet/exploits/csrf.adoc[]\n*** xref:servlet/exploits/headers.adoc[]\n*** xref:servlet/exploits/http.adoc[]\n*** xref:servlet/exploits/firewall.adoc[]\n** xref:servlet/integrations/index.adoc[Integrations]\n*** xref:servlet/integrations/concurrency.adoc[Concurrency]\n*** xref:servlet/integrations/localization.adoc[Localization]\n*** xref:servlet/integrations/servlet-api.adoc[Servlet APIs]\n*** xref:servlet/integrations/data.adoc[Spring Data]\n*** xref:servlet/integrations/mvc.adoc[Spring MVC]\n*** xref:servlet/integrations/websocket.adoc[WebSocket]\n*** xref:servlet/integrations/cors.adoc[Spring's CORS Support]\n*** xref:servlet/integrations/jsp-taglibs.adoc[JSP Taglib]\n*** xref:servlet/integrations/observability.adoc[Observability]\n** Configuration\n*** xref:servlet/configuration/java.adoc[Java Configuration]\n*** xref:servlet/configuration/kotlin.adoc[Kotlin Configuration]\n*** xref:servlet/configuration/xml-namespace.adoc[Namespace Configuration]\n** xref:servlet/test/index.adoc[Testing]\n*** xref:servlet/test/method.adoc[Method Security]\n*** xref:servlet/test/mockmvc/index.adoc[MockMvc Support]\n*** xref:servlet/test/mockmvc/setup.adoc[MockMvc Setup]\n*** xref:servlet/test/mockmvc/request-post-processors.adoc[Security RequestPostProcessors]\n**** xref:servlet/test/mockmvc/authentication.adoc[Mocking Users]\n**** xref:servlet/test/mockmvc/csrf.adoc[Mocking CSRF]\n**** xref:servlet/test/mockmvc/form-login.adoc[Mocking Form Login]\n**** xref:servlet/test/mockmvc/http-basic.adoc[Mocking HTTP Basic]\n**** xref:servlet/test/mockmvc/oauth2.adoc[Mocking OAuth2]\n**** xref:servlet/test/mockmvc/logout.adoc[Mocking Logout]\n*** xref:servlet/test/mockmvc/request-builders.adoc[Security RequestBuilders]\n*** xref:servlet/test/mockmvc/result-matchers.adoc[Security ResultMatchers]\n*** xref:servlet/test/mockmvc/result-handlers.adoc[Security ResultHandlers]\n** xref:servlet/appendix/index.adoc[Appendix]\n*** xref:servlet/appendix/database-schema.adoc[Database Schemas]\n*** xref:servlet/appendix/namespace/index.adoc[XML Namespace]\n**** xref:servlet/appendix/namespace/authentication-manager.adoc[Authentication Services]\n**** xref:servlet/appendix/namespace/http.adoc[Web Security]\n**** xref:servlet/appendix/namespace/method-security.adoc[Method Security]\n**** xref:servlet/appendix/namespace/ldap.adoc[LDAP Security]\n**** xref:servlet/appendix/namespace/websocket.adoc[WebSocket Security]\n*** xref:servlet/appendix/proxy-server.adoc[Proxy Server Configuration]\n*** xref:servlet/appendix/faq.adoc[FAQ]\n* xref:reactive/index.adoc[Reactive Applications]\n** xref:reactive/getting-started.adoc[Getting Started]\n** xref:reactive/authentication/index.adoc[Authentication]\n*** xref:reactive/authentication/x509.adoc[X.509 Authentication]\n*** xref:reactive/authentication/logout.adoc[Logout]\n*** Session Management\n**** xref:reactive/authentication/concurrent-sessions-control.adoc[Concurrent Sessions Control]\n** Authorization\n*** xref:reactive/authorization/authorize-http-requests.adoc[Authorize HTTP Requests]\n*** xref:reactive/authorization/method.adoc[EnableReactiveMethodSecurity]\n** xref:reactive/oauth2/index.adoc[OAuth2]\n*** xref:reactive/oauth2/login/index.adoc[OAuth2 Log In]\n**** xref:reactive/oauth2/login/core.adoc[Core Configuration]\n**** xref:reactive/oauth2/login/advanced.adoc[Advanced Configuration]\n**** xref:reactive/oauth2/login/logout.adoc[OIDC Logout]\n*** xref:reactive/oauth2/client/index.adoc[OAuth2 Client]\n**** xref:reactive/oauth2/client/core.adoc[Core Interfaces and Classes]\n**** xref:reactive/oauth2/client/authorization-grants.adoc[OAuth2 Authorization Grants]\n**** xref:reactive/oauth2/client/client-authentication.adoc[OAuth2 Client Authentication]\n**** xref:reactive/oauth2/client/authorized-clients.adoc[OAuth2 Authorized Clients]\n*** xref:reactive/oauth2/resource-server/index.adoc[OAuth2 Resource Server]\n**** xref:reactive/oauth2/resource-server/jwt.adoc[JWT]\n**** xref:reactive/oauth2/resource-server/opaque-token.adoc[Opaque Token]\n**** xref:reactive/oauth2/resource-server/multitenancy.adoc[Multitenancy]\n**** xref:reactive/oauth2/resource-server/bearer-tokens.adoc[Bearer Tokens]\n** xref:reactive/exploits/index.adoc[Protection Against Exploits]\n*** xref:reactive/exploits/csrf.adoc[CSRF]\n*** xref:reactive/exploits/headers.adoc[Headers]\n*** xref:reactive/exploits/http.adoc[HTTP Requests]\n*** xref:reactive/exploits/firewall.adoc[]\n** Integrations\n*** xref:reactive/integrations/cors.adoc[CORS]\n*** xref:reactive/integrations/rsocket.adoc[RSocket]\n*** xref:reactive/integrations/observability.adoc[Observability]\n** xref:reactive/test/index.adoc[Testing]\n*** xref:reactive/test/method.adoc[Testing Method Security]\n*** xref:reactive/test/web/index.adoc[Testing Web Security]\n**** xref:reactive/test/web/setup.adoc[WebTestClient Setup]\n**** xref:reactive/test/web/authentication.adoc[Testing Authentication]\n**** xref:reactive/test/web/csrf.adoc[Testing CSRF]\n**** xref:reactive/test/web/oauth2.adoc[Testing OAuth 2.0]\n**** xref:reactive/test/web/x509.adoc[Testing X509]\n** xref:reactive/configuration/webflux.adoc[WebFlux Security]\n* xref:native-image/index.adoc[GraalVM Native Image Support]\n** xref:native-image/method-security.adoc[Method Security]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/community.adoc",
    "content": "[[community]]\n= Spring Security Community\n\nWelcome to the Spring Security Community!\nThis section discusses how you can make the most of our vast community.\n\n\n[[community-help]]\n== Getting Help\nIf you need help with Spring Security, we are here to help.\nThe following are some of the best ways to get help:\n\n* Read through this documentation.\n* Try one of our many xref:samples.adoc#samples[sample applications].\n* Ask a question on https://stackoverflow.com/questions/tagged/spring-security[https://stackoverflow.com] with the `spring-security` tag.\n* Report bugs and enhancement requests at https://github.com/spring-projects/spring-security/issues\n\n[[community-becoming-involved]]\n== Becoming Involved\nWe welcome your involvement in the Spring Security project.\nThere are many ways to contribute, including answering questions on Stack Overflow, writing new code, improving existing code, assisting with documentation, developing samples or tutorials, reporting bugs, or simply making suggestions.\nFor more information, see our https://github.com/spring-projects/spring-security/blob/main/CONTRIBUTING.adoc[Contributing] documentation.\n\n[[community-source]]\n== Source Code\n\nYou can find Spring Security's source code on GitHub at https://github.com/spring-projects/spring-security/\n\n[[community-license]]\n== Apache 2 License\n\nSpring Security is Open Source software released under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].\n\n== Social Media\n\nYou can follow https://twitter.com/SpringSecurity[@SpringSecurity] and the https://twitter.com/SpringSecurity/lists/team[Spring Security team] on Twitter to stay up to date with the latest news.\nYou can also follow https://twitter.com/SpringCentral[@SpringCentral] to keep up to date with the entire Spring portfolio.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/authentication/index.adoc",
    "content": "[[authentication]]\n= Authentication\n\nSpring Security provides comprehensive support for https://en.wikipedia.org/wiki/Authentication[authentication].\nAuthentication is how we verify the identity of who is trying to access a particular resource.\nA common way to authenticate users is by requiring the user to enter a username and password.\nOnce authentication is performed we know the identity and can perform authorization.\n\nSpring Security provides built-in support for authenticating users.\nThis section is dedicated to generic authentication support that applies in both Servlet and WebFlux environments.\nRefer to the sections on authentication for xref:servlet/authentication/index.adoc[Servlet] and xref:reactive/authentication/index.adoc[WebFlux] for details on what is supported for each stack.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/authentication/password-storage.adoc",
    "content": "[[authentication-password-storage]]\n= Password Storage\n\nSpring Security's `PasswordEncoder` interface is used to perform a one-way transformation of a password to let the password be stored securely.\nGiven `PasswordEncoder` is a one-way transformation, it is not useful when the password transformation needs to be two-way (such as storing credentials used to authenticate to a database).\nTypically, `PasswordEncoder` is used for storing a password that needs to be compared to a user-provided password at the time of authentication.\n\n[[authentication-password-storage-history]]\n== Password Storage History\n\nThroughout the years, the standard mechanism for storing passwords has evolved.\nIn the beginning, passwords were stored in plaintext.\nThe passwords were assumed to be safe because the data store the passwords were saved in required credentials to access it.\nHowever, malicious users were able to find ways to get large \"`data dumps`\" of usernames and passwords by using attacks such as SQL Injection.\nAs more and more user credentials became public, security experts realized that we needed to do more to protect users' passwords.\n\nDevelopers were then encouraged to store passwords after running them through a one way hash, such as SHA-256.\nWhen a user tried to authenticate, the hashed password would be compared to the hash of the password that they typed.\nThis meant that the system only needed to store the one-way hash of the password.\nIf a breach occurred, only the one-way hashes of the passwords were exposed.\nSince the hashes were one-way and it was computationally difficult to guess the passwords given the hash, it would not be worth the effort to figure out each password in the system.\nTo defeat this new system, malicious users decided to create lookup tables known as https://en.wikipedia.org/wiki/Rainbow_table[Rainbow Tables].\nRather than doing the work of guessing each password every time, they computed the password once and stored it in a lookup table.\n\nTo mitigate the effectiveness of Rainbow Tables, developers were encouraged to use salted passwords.\nInstead of using just the password as input to the hash function, random bytes (known as salt) would be generated for every user's password.\nThe salt and the user's password would be run through the hash function to produce a unique hash.\nThe salt would be stored alongside the user's password in clear text.\nThen when a user tried to authenticate, the hashed password would be compared to the hash of the stored salt and the password that they typed.\nThe unique salt meant that Rainbow Tables were no longer effective because the hash was different for every salt and password combination.\n\nIn modern times, we realize that cryptographic hashes (like SHA-256) are no longer secure.\nThe reason is that with modern hardware we can perform billions of hash calculations a second.\nThis means that we can crack each password individually with ease.\n\nDevelopers are now encouraged to leverage adaptive one-way functions to store a password.\nValidation of passwords with adaptive one-way functions are intentionally resource-intensive (they intentionally use a lot of CPU, memory, or other resources).\nAn adaptive one-way function allows configuring a \"`work factor`\" that can grow as hardware gets better.\nWe recommend that the \"`work factor`\" be tuned to take about one second to verify a password on your system.\nThis trade off is to make it difficult for attackers to crack the password, but not so costly that it puts excessive burden on your own system or irritates users.\nSpring Security has attempted to provide a good starting point for the \"`work factor`\", but we encourage users to customize the \"`work factor`\" for their own system, since the performance varies drastically from system to system.\nExamples of adaptive one-way functions that should be used include <<authentication-password-storage-bcrypt,bcrypt>>, <<authentication-password-storage-pbkdf2,PBKDF2>>, <<authentication-password-storage-scrypt,scrypt>>, and <<authentication-password-storage-argon2,argon2>>.\n\nBecause adaptive one-way functions are intentionally resource intensive, validating a username and password for every request can significantly degrade the performance of an application.\nThere is nothing Spring Security (or any other library) can do to speed up the validation of the password, since security is gained by making the validation resource intensive.\nUsers are encouraged to exchange the long term credentials (that is, username and password) for a short term credential (such as a session, and OAuth Token, and so on).\nThe short term credential can be validated quickly without any loss in security.\n\n\n[[authentication-password-storage-dpe]]\n== DelegatingPasswordEncoder\n\nPrior to Spring Security 5.0, the default `PasswordEncoder` was `NoOpPasswordEncoder`, which required plain-text passwords.\nBased on the <<authentication-password-storage-history,Password History>> section, you might expect that the default `PasswordEncoder` would now be something like `BCryptPasswordEncoder`.\nHowever, this ignores three real world problems:\n\n- Many applications use old password encodings that cannot easily migrate.\n- The best practice for password storage will change again.\n- As a framework, Spring Security cannot make breaking changes frequently.\n\nInstead Spring Security introduces `DelegatingPasswordEncoder`, which solves all of the problems by:\n\n- Ensuring that passwords are encoded by using the current password storage recommendations\n- Allowing for validating passwords in modern and legacy formats\n- Allowing for upgrading the encoding in the future\n\nYou can easily construct an instance of `DelegatingPasswordEncoder` by using `PasswordEncoderFactories`:\n\n.Create Default DelegatingPasswordEncoder\ninclude-code::./DelegatingPasswordEncoderUsage[tag=createDefaultPasswordEncoder,indent=0]\n\nAlternatively, you can create your own custom instance:\n\n.Create Custom DelegatingPasswordEncoder\ninclude-code::./DelegatingPasswordEncoderUsage[tag=createCustomPasswordEncoder,indent=0]\n\n[[authentication-password-storage-dpe-format]]\n=== Password Storage Format\n\nThe general format for a password is:\n\n.DelegatingPasswordEncoder Storage Format\n[source,text,attrs=\"-attributes\"]\n----\n{id}encodedPassword\n----\n\n`id` is an identifier that is used to look up which `PasswordEncoder` should be used and `encodedPassword` is the original encoded password for the selected `PasswordEncoder`.\nThe `id` must be at the beginning of the password, start with `{`, and end with `}`.\nIf the `id` cannot be found, the `id` is set to null.\nFor example, the following might be a list of passwords encoded using different `id` values.\nAll of the original passwords are `password`.\n\n.DelegatingPasswordEncoder Encoded Passwords Example\n[source,text,attrs=\"-attributes\"]\n----\n{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG // <1>\n{noop}password // <2>\n{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc // <3>\n{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=  // <4>\n{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0 // <5>\n----\n\n<1> The first password has a `PasswordEncoder` id of `bcrypt` and an `encodedPassword` value of `$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG`.\nWhen matching, it would delegate to `BCryptPasswordEncoder`\n<2> The second password has a `PasswordEncoder` id of `noop` and `encodedPassword` value of `password`.\nWhen matching, it would delegate to `NoOpPasswordEncoder`\n<3> The third password has a `PasswordEncoder` id of `pbkdf2` and `encodedPassword` value of `5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc`.\nWhen matching, it would delegate to `Pbkdf2PasswordEncoder`\n<4> The fourth password has a `PasswordEncoder` id of `scrypt` and `encodedPassword` value of `$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=`\nWhen matching, it would delegate to `SCryptPasswordEncoder`\n<5> The final password has a `PasswordEncoder` id of `sha256` and `encodedPassword` value of `97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0`.\nWhen matching, it would delegate to `StandardPasswordEncoder`\n\n[NOTE]\n====\nSome users might be concerned that the storage format is provided for a potential hacker.\nThis is not a concern because the storage of the password does not rely on the algorithm being a secret.\nAdditionally, most formats are easy for an attacker to figure out without the prefix.\nFor example, BCrypt passwords often start with `$2a$`.\n====\n\n[[authentication-password-storage-dpe-encoding]]\n=== Password Encoding\n\nThe `idForEncode` passed into the constructor determines which `PasswordEncoder` is used for encoding passwords.\nIn the `DelegatingPasswordEncoder` we constructed earlier, that means that the result of encoding `password` is delegated to `BCryptPasswordEncoder` and be prefixed with `+{bcrypt}+`.\nThe end result looks like the following example:\n\n.DelegatingPasswordEncoder Encode Example\n[source,text,attrs=\"-attributes\"]\n----\n{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n----\n\n[[authentication-password-storage-dpe-matching]]\n=== Password Matching\n\nMatching is based upon the `+{id}+` and the mapping of the `id` to the `PasswordEncoder` provided in the constructor.\nOur example in <<authentication-password-storage-dpe-format,Password Storage Format>> provides a working example of how this is done.\nBy default, the result of invoking `matches(CharSequence, String)` with a password and an `id` that is not mapped (including a null id) results in an `IllegalArgumentException`.\nThis behavior can be customized by using `DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(PasswordEncoder)`.\n\nBy using the `id`, we can match on any password encoding but encode passwords by using the most modern password encoding.\nThis is important, because unlike encryption, password hashes are designed so that there is no simple way to recover the plaintext.\nSince there is no way to recover the plaintext, it is difficult to migrate the passwords.\nWhile it is simple for users to migrate `NoOpPasswordEncoder`, we chose to include it by default to make it simple for the getting-started experience.\n\n[[authentication-password-storage-dep-getting-started]]\n=== Getting Started Experience\n\nIf you are putting together a demo or a sample, it is a bit cumbersome to take time to hash the passwords of your users.\nThere are convenience mechanisms to make this easier, but this is still not intended for production.\n\n.withDefaultPasswordEncoder Example\ninclude-code::./WithDefaultPasswordEncoderUsage[tag=createSingleUser,indent=0]\n\nIf you are creating multiple users, you can also reuse the builder:\n\n.withDefaultPasswordEncoder Reusing the Builder\ninclude-code::./WithDefaultPasswordEncoderUsage[tag=createMultipleUsers,indent=0]\n\nThis does hash the password that is stored, but the passwords are still exposed in memory and in the compiled source code.\nTherefore, it is still not considered secure for a production environment.\nFor production, you should <<authentication-password-storage-boot-cli,hash your passwords externally>>.\n\n[[authentication-password-storage-boot-cli]]\n=== Encode with Spring Boot CLI\n\nThe easiest way to properly encode your password is to use the {spring-boot-reference-url}cli/index.html[Spring Boot CLI].\n\nFor example, the following example encodes the password of `password` for use with <<authentication-password-storage-dpe>>:\n\n.Spring Boot CLI encodepassword Example\n[source,attrs=\"-attributes\"]\n----\nspring encodepassword password\n{bcrypt}$2a$10$X5wFBtLrL/kHcmrOGGTrGufsBX8CJ0WpQpF3pgeuxBB/H73BK1DW6\n----\n\n[[authentication-password-storage-dpe-troubleshoot]]\n=== Troubleshooting\n\nThe following error occurs when one of the passwords that are stored has no `id`, as described in <<authentication-password-storage-dpe-format>>.\n\n----\njava.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id \"null\"\n\tat org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:233)\n\tat org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:196)\n----\n\nThe easiest way to resolve it is to figure out how your passwords are currently being stored and explicitly provide the correct `PasswordEncoder`.\n\nIf you are migrating from Spring Security 4.2.x, you can revert to the previous behavior by <<authentication-password-storage-configuration,exposing a `NoOpPasswordEncoder` bean>>.\n\nAlternatively, you can prefix all of your passwords with the correct `id` and continue to use `DelegatingPasswordEncoder`.\nFor example, if you are using BCrypt, you would migrate your password from something like:\n\n----\n$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n----\n\nto\n\n[source,attrs=\"-attributes\"]\n----\n{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n----\n\nFor a complete listing of the mappings, see the Javadoc for javadoc:org.springframework.security.crypto.factory.PasswordEncoderFactories[].\n\n[[authentication-password-storage-bcrypt]]\n== BCryptPasswordEncoder\n\nThe `BCryptPasswordEncoder` implementation uses the widely supported https://en.wikipedia.org/wiki/Bcrypt[bcrypt] algorithm to hash the passwords.\nTo make it more resistant to password cracking, bcrypt is deliberately slow.\nLike other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.\nThe default implementation of `BCryptPasswordEncoder` uses strength 10 as mentioned in the Javadoc of javadoc:org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder[]. You are encouraged to\ntune and test the strength parameter on your own system so that it takes roughly 1 second to verify a password.\n\n.BCryptPasswordEncoder\ninclude-code::./BCryptPasswordEncoderUsage[tag=bcryptPasswordEncoder,indent=0]\n\n[[authentication-password-storage-argon2]]\n== Argon2PasswordEncoder\n\nThe `Argon2PasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/Argon2[Argon2] algorithm to hash the passwords.\nArgon2 is the winner of the https://en.wikipedia.org/wiki/Password_Hashing_Competition[Password Hashing Competition].\nTo defeat password cracking on custom hardware, Argon2 is a deliberately slow algorithm that requires large amounts of memory.\nLike other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.\nThe current implementation of the `Argon2PasswordEncoder` requires BouncyCastle.\n\n.Argon2PasswordEncoder\ninclude-code::./Argon2PasswordEncoderUsage[tag=argon2PasswordEncoder,indent=0]\n\n[[authentication-password-storage-pbkdf2]]\n== Pbkdf2PasswordEncoder\n\nThe `Pbkdf2PasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/PBKDF2[PBKDF2] algorithm to hash the passwords.\nTo defeat password cracking PBKDF2 is a deliberately slow algorithm.\nLike other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.\nThis algorithm is a good choice when FIPS certification is required.\n\n.Pbkdf2PasswordEncoder\ninclude-code::./Pbkdf2PasswordEncoderUsage[tag=pbkdf2PasswordEncoder,indent=0]\n\n[[authentication-password-storage-scrypt]]\n== SCryptPasswordEncoder\n\nThe `SCryptPasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/Scrypt[scrypt] algorithm to hash the passwords.\nTo defeat password cracking on custom hardware, scrypt is a deliberately slow algorithm that requires large amounts of memory.\nLike other adaptive one-way functions, it should be tuned to take about 1 second to verify a password on your system.\n\n.SCryptPasswordEncoder\ninclude-code::./SCryptPasswordEncoderUsage[tag=sCryptPasswordEncoder,indent=0]\n\n[[authentication-password-storage-other]]\n== Other ``PasswordEncoder``s\n\nThere are a significant number of other `PasswordEncoder` implementations that exist entirely for backward compatibility.\nThey are all deprecated to indicate that they are no longer considered secure.\nHowever, there are no plans to remove them, since it is difficult to migrate existing legacy systems.\n\n[[password4j]]\n== Password4j-based Password Encoders\n\nSpring Security 7.0 introduces alternative password encoder implementations based on the https://github.com/Password4j/password4j[Password4j] library.\nThese encoders provide additional options for popular hashing algorithms and can be used as alternatives to the existing Spring Security implementations.\n\nThe Password4j library is a Java cryptographic library that focuses on password hashing with support for multiple algorithms.\nThese encoders are particularly useful when you need specific algorithm configurations or want to leverage Password4j's optimizations.\n\nAll Password4j-based encoders are thread-safe and can be shared across multiple threads.\n\n[[password4j-argon2]]\n=== Argon2Password4jPasswordEncoder\n\nThe `Argon2Password4jPasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/Argon2[Argon2] algorithm via the Password4j library to hash passwords.\nThis provides an alternative to Spring Security's built-in `Argon2PasswordEncoder` with different configuration options and potential performance characteristics.\n\nArgon2 is the winner of the https://en.wikipedia.org/wiki/Password_Hashing_Competition[Password Hashing Competition] and is recommended for new applications.\nThis implementation leverages Password4j's Argon2 support which properly includes the salt in the output hash.\n\nCreate an encoder with default settings:\n\n.Argon2Password4jPasswordEncoder\ninclude-code::./Argon2UsageTests[tag=default-params,indent=0]\n\nCreate an encoder with custom Argon2 parameters:\n\n.Argon2Password4jPasswordEncoder Custom\ninclude-code::./Argon2UsageTests[tag=custom-params,indent=0]\n\n[[password4j-bcrypt]]\n=== BcryptPassword4jPasswordEncoder\n\nThe `BcryptPassword4jPasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/Bcrypt[BCrypt] algorithm via the Password4j library to hash passwords.\nThis provides an alternative to Spring Security's built-in `BCryptPasswordEncoder` with Password4j's implementation characteristics.\n\nBCrypt is a well-established password hashing algorithm that includes built-in salt generation and is resistant to rainbow table attacks.\nThis implementation leverages Password4j's BCrypt support which properly includes the salt in the output hash.\n\nCreate an encoder with default settings:\n\n.BcryptPassword4jPasswordEncoder\ninclude-code::./BcryptUsageTests[tag=default-params,indent=0]\n\nCreate an encoder with custom bcrypt parameters:\n\n.BcryptPassword4jPasswordEncoder Custom\ninclude-code::./BcryptUsageTests[tag=custom-params,indent=0]\n\n[[password4j-scrypt]]\n=== ScryptPassword4jPasswordEncoder\n\nThe `ScryptPassword4jPasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/Scrypt[SCrypt] algorithm via the Password4j library to hash passwords.\nThis provides an alternative to Spring Security's built-in `SCryptPasswordEncoder` with Password4j's implementation characteristics.\n\nSCrypt is a memory-hard password hashing algorithm designed to be resistant to hardware brute-force attacks.\nThis implementation leverages Password4j's SCrypt support which properly includes the salt in the output hash.\n\n\nCreate an encoder with default settings:\n\n.ScryptPassword4jPasswordEncoder\ninclude-code::./ScryptUsageTests[tag=default-params,indent=0]\n\nCreate an encoder with custom scrypt parameters:\n\n.ScryptPassword4jPasswordEncoder Custom\ninclude-code::./ScryptUsageTests[tag=custom-params,indent=0]\n\n[[password4j-pbkdf2]]\n=== Pbkdf2Password4jPasswordEncoder\n\nThe `Pbkdf2Password4jPasswordEncoder` implementation uses the https://en.wikipedia.org/wiki/PBKDF2[PBKDF2] algorithm via the Password4j library to hash passwords.\nThis provides an alternative to Spring Security's built-in `Pbkdf2PasswordEncoder` with explicit salt management.\n\nPBKDF2 is a key derivation function designed to be computationally expensive to thwart dictionary and brute force attacks.\nThis implementation handles salt management explicitly since Password4j's PBKDF2 implementation does not include the salt in the output hash.\nThe encoded password format is: `+{salt}:{hash}+` where both salt and hash are Base64 encoded.\n\nCreate an encoder with default settings:\n\n.Pbkdf2Password4jPasswordEncoder\ninclude-code::./Pbkdf2UsageTests[tag=default-params,indent=0]\n\nCreate an encoder with custom PBKDF2 parameters:\n\n.Pbkdf2Password4jPasswordEncoder Custom\ninclude-code::./Pbkdf2UsageTests[tag=custom-params,indent=0]\n\n[[password4j-ballooning]]\n=== BalloonHashingPassword4jPasswordEncoder\n\nThe `BalloonHashingPassword4jPasswordEncoder` implementation uses the Balloon hashing algorithm via the Password4j library to hash passwords.\nBalloon hashing is a memory-hard password hashing algorithm designed to be resistant to both time-memory trade-off attacks and side-channel attacks.\n\nThis implementation handles salt management explicitly since Password4j's Balloon hashing implementation does not include the salt in the output hash.\nThe encoded password format is: `+{salt}:{hash}+` where both salt and hash are Base64 encoded.\n\n\nCreate an encoder with default settings:\n\n.BalloonHashingPassword4jPasswordEncoder\ninclude-code::./BallooningHashingUsageTests[tag=default-params,indent=0]\n\nCreate an encoder with custom parameters:\n\n.BalloonHashingPassword4jPasswordEncoder Custom\ninclude-code::./BallooningHashingUsageTests[tag=custom-params,indent=0]\n\n[[authentication-password-storage-configuration]]\n== Password Storage Configuration\n\nSpring Security uses <<authentication-password-storage-dpe>> by default.\nHowever, you can customize this by exposing a `PasswordEncoder` as a Spring bean.\n\n\nIf you are migrating from Spring Security 4.2.x, you can revert to the previous behavior by exposing a `NoOpPasswordEncoder` bean.\n\n[WARNING]\n====\nReverting to `NoOpPasswordEncoder` is not considered to be secure.\nYou should instead migrate to using `DelegatingPasswordEncoder` to support secure password encoding.\n====\n\n.NoOpPasswordEncoder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic static NoOpPasswordEncoder passwordEncoder() {\n    return NoOpPasswordEncoder.getInstance();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<b:bean id=\"passwordEncoder\"\n        class=\"org.springframework.security.crypto.password.NoOpPasswordEncoder\" factory-method=\"getInstance\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun passwordEncoder(): PasswordEncoder {\n    return NoOpPasswordEncoder.getInstance();\n}\n----\n======\n\n[NOTE]\n====\nXML Configuration requires the `NoOpPasswordEncoder` bean name to be `passwordEncoder`.\n====\n\n[[authentication-change-password-configuration]]\n== Change Password Configuration\n\nMost applications that allow a user to specify a password also require a feature for updating that password.\n\nhttps://w3c.github.io/webappsec-change-password-url/[A Well-Known URL for Changing Passwords] indicates a mechanism by which password managers can discover the password update endpoint for a given application.\n\nYou can configure Spring Security to provide this discovery endpoint.\nFor example, if the change password endpoint in your application is `/change-password`, then you can configure Spring Security like so:\n\n.Default Change Password Endpoint\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .passwordManagement(Customizer.withDefaults())\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:password-management/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    passwordManagement { }\n}\n----\n======\n\nThen, when a password manager navigates to `/.well-known/change-password` then Spring Security will redirect your endpoint, `/change-password`.\n\nOr, if your endpoint is something other than `/change-password`, you can also specify that like so:\n\n.Change Password Endpoint\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .passwordManagement((management) -> management\n        .changePasswordPage(\"/update-password\")\n    )\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:password-management change-password-page=\"/update-password\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    passwordManagement {\n        changePasswordPage = \"/update-password\"\n    }\n}\n----\n======\n\nWith the above configuration, when a password manager navigates to `/.well-known/change-password`, then Spring Security will redirect to `/update-password`.\n\n[[authentication-compromised-password-check]]\n== Compromised Password Checking\n\nThere are some scenarios where you need to check whether a password has been compromised, for example, if you are creating an application that deals with sensitive data, it is often needed that you perform some check on user's passwords in order to assert its reliability.\nOne of these checks can be if the password has been compromised, usually because it has been found in a https://wikipedia.org/wiki/Data_breach[data breach].\n\nTo facilitate that, Spring Security provides integration with the https://haveibeenpwned.com/API/v3#PwnedPasswords[Have I Been Pwned API] via the javadoc:org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker[] implementation of the javadoc:org.springframework.security.authentication.password.CompromisedPasswordChecker[] interface.\n\nYou can either use the `CompromisedPasswordChecker` API by yourself or, if you are using xref:servlet/authentication/passwords/dao-authentication-provider.adoc[the `DaoAuthenticationProvider]` via xref:servlet/authentication/passwords/index.adoc[Spring Security authentication mechanisms], you can provide a `CompromisedPasswordChecker` bean, and it will be automatically picked up by Spring Security configuration.\n\nBy doing that, when you try to authenticate via Form Login using a weak password, let's say `123456`, you will receive a 401 or be redirected to the `/login?error` page (depending on your user-agent).\nHowever, just a 401 or the redirect is not so useful in that case, it will cause some confusion because the user provided the right password and still was not allowed to log in.\nIn such cases, you can handle the `CompromisedPasswordException` via the `AuthenticationFailureHandler` to perform your desired logic, like redirecting the user-agent to `/reset-password`, for example:\n\n.Using CompromisedPasswordChecker\ninclude-code::./CompromisedPasswordCheckerUsage[tag=configuration,indent=0]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/authorization/index.adoc",
    "content": "[[authorization]]\n= Authorization\n\nSpring Security provides comprehensive support for https://en.wikipedia.org/wiki/Authorization[authorization].\nAuthorization is determining who is allowed to access a particular resource.\nSpring Security provides https://en.wikipedia.org/wiki/Defense_in_depth_(computing)[defense in depth] by allowing for request based authorization and method based authorization.\n\n[[authorization-request]]\n== Request Based Authorization\n\nSpring Security provides authorization based upon the request for both xref:servlet/authorization/authorize-http-requests.adoc[Servlet] and xref:reactive/authorization/authorize-http-requests.adoc[WebFlux] environments.\n\n[[authorization-method]]\n== Method Based Authorization\n\nSpring Security provides authorization based on the method invocation for both xref:servlet/authorization/method-security.adoc[Servlet] and xref:reactive/authorization/method.adoc[WebFlux] environments.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/exploits/csrf.adoc",
    "content": "// FIXME: Add links to Servlet and WebFlux support\n\n[[csrf]]\n= Cross Site Request Forgery (CSRF)\n\nSpring provides comprehensive support for protecting against https://en.wikipedia.org/wiki/Cross-site_request_forgery[Cross Site Request Forgery (CSRF)] attacks.\nIn the following sections, we explore:\n\n* <<csrf-explained>>\n* <<csrf-protection>>\n* <<csrf-considerations>>\n\n// FIXME: Add WebFlux csrf documentation (the link below is broken)\n[NOTE]\n====\nThis portion of the documentation discusses the general topic of CSRF protection.\nSee the relevant sections for specific information on CSRF protection for xref:servlet/exploits/csrf.adoc#servlet-csrf[servlet] and xref:reactive/exploits/csrf.adoc#webflux-csrf[WebFlux] based applications.\n====\n\n[[csrf-explained]]\n== What is a CSRF Attack?\nThe best way to understand a CSRF attack is by taking a look at a concrete example.\n\nAssume that your bank's website provides a form that allows transferring money from the currently logged in user to another bank account.\nFor example, the transfer form might look like:\n\n.Transfer form\n[source,html]\n----\n<form method=\"post\"\n\taction=\"/transfer\">\n<input type=\"text\"\n\tname=\"amount\"/>\n<input type=\"text\"\n\tname=\"routingNumber\"/>\n<input type=\"text\"\n\tname=\"account\"/>\n<input type=\"submit\"\n\tvalue=\"Transfer\"/>\n</form>\n----\n\nThe corresponding HTTP request might look like:\n\n.Transfer HTTP request\n[source]\n----\nPOST /transfer HTTP/1.1\nHost: bank.example.com\nCookie: JSESSIONID=randomid\nContent-Type: application/x-www-form-urlencoded\n\namount=100.00&routingNumber=1234&account=9876\n----\n\nNow pretend you authenticate to your bank's website and then, without logging out, visit an evil website.\nThe evil website contains an HTML page with the following form:\n\n.Evil transfer form\n[source,html]\n----\n<form method=\"post\"\n\taction=\"https://bank.example.com/transfer\">\n<input type=\"hidden\"\n\tname=\"amount\"\n\tvalue=\"100.00\"/>\n<input type=\"hidden\"\n\tname=\"routingNumber\"\n\tvalue=\"evilsRoutingNumber\"/>\n<input type=\"hidden\"\n\tname=\"account\"\n\tvalue=\"evilsAccountNumber\"/>\n<input type=\"submit\"\n\tvalue=\"Win Money!\"/>\n</form>\n----\n\nYou like to win money, so you click on the submit button.\nIn the process, you have unintentionally transferred $100 to a malicious user.\nThis happens because, while the evil website cannot see your cookies, the cookies associated with your bank are still sent along with the request.\n\nWorse yet, this whole process could have been automated by using JavaScript.\nThis means you did not even need to click on the button.\nFurthermore, it could just as easily happen when visiting an honest site that is a victim of a https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)[XSS attack].\nSo how do we protect our users from such attacks?\n\n[[csrf-protection]]\n== Protecting Against CSRF Attacks\nThe reason that a CSRF attack is possible is that the HTTP request from the victim's website and the request from the attacker's website are exactly the same.\nThis means there is no way to reject requests coming from the evil website and allow only requests coming from the bank's website.\nTo protect against CSRF attacks, we need to ensure there is something in the request that the evil site is unable to provide so we can differentiate the two requests.\n\nSpring provides two mechanisms to protect against CSRF attacks:\n\n* The <<Synchronizer Token Pattern>>\n* Specifying the <<SameSite Attribute>> on your session cookie\n\n[NOTE]\n====\nBoth protections require that <<csrf-protection-read-only,Safe Methods be Read-only>>.\n====\n\n[[csrf-protection-read-only]]\n=== Safe Methods Must be Read-only\n\nFor <<csrf-protection,either protection>> against CSRF to work, the application must ensure that https://tools.ietf.org/html/rfc7231#section-4.2.1[\"safe\" HTTP methods are read-only].\nThis means that requests with the HTTP `GET`, `HEAD`, `OPTIONS`, and `TRACE` methods should not change the state of the application.\n\n[[csrf-protection-stp]]\n=== Synchronizer Token Pattern\nThe predominant and most comprehensive way to protect against CSRF attacks is to use the https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#synchronizer-token-pattern[Synchronizer Token Pattern].\nThis solution is to ensure that each HTTP request requires, in addition to our session cookie, a secure random generated value called a CSRF token be present in the HTTP request.\n\nWhen an HTTP request is submitted, the server must look up the expected CSRF token and compare it against the actual CSRF token in the HTTP request.\nIf the values do not match, the HTTP request should be rejected.\n\nThe key to this working is that the actual CSRF token should be in a part of the HTTP request that is not automatically included by the browser.\nFor example, requiring the actual CSRF token in an HTTP parameter or an HTTP header will protect against CSRF attacks.\nRequiring the actual CSRF token in a cookie does not work because cookies are automatically included in the HTTP request by the browser.\n\nWe can relax the expectations to require only the actual CSRF token for each HTTP request that updates the state of the application.\nFor that to work, our application must ensure that <<csrf-protection-read-only,safe HTTP methods are read-only>>.\nThis improves usability, since we want to allow linking to our website from external sites.\nAdditionally, we do not want to include the random token in HTTP GET, as this can cause the tokens to be leaked.\n\nConsider how <<csrf-explained,our example>> would change when we use the Synchronizer Token Pattern.\nAssume that the actual CSRF token is required to be in an HTTP parameter named `_csrf`.\nOur application's transfer form would look like:\n\n.Synchronizer Token Form\n[source,html]\n----\n<form method=\"post\"\n\taction=\"/transfer\">\n<input type=\"hidden\"\n\tname=\"_csrf\"\n\tvalue=\"4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\"/>\n<input type=\"text\"\n\tname=\"amount\"/>\n<input type=\"text\"\n\tname=\"routingNumber\"/>\n<input type=\"hidden\"\n\tname=\"account\"/>\n<input type=\"submit\"\n\tvalue=\"Transfer\"/>\n</form>\n----\n\nThe form now contains a hidden input with the value of the CSRF token.\nExternal sites cannot read the CSRF token since the same origin policy ensures the evil site cannot read the response.\n\nThe corresponding HTTP request to transfer money would look like this:\n\n.Synchronizer Token request\n[source]\n----\nPOST /transfer HTTP/1.1\nHost: bank.example.com\nCookie: JSESSIONID=randomid\nContent-Type: application/x-www-form-urlencoded\n\namount=100.00&routingNumber=1234&account=9876&_csrf=4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\n----\n\n\nYou will notice that the HTTP request now contains the `_csrf` parameter with a secure random value.\nThe evil website will not be able to provide the correct value for the `_csrf` parameter (which must be explicitly provided on the evil website) and the transfer will fail when the server compares the actual CSRF token to the expected CSRF token.\n\n[[csrf-protection-ssa]]\n=== SameSite Attribute\nAn emerging way to protect against <<csrf,CSRF Attacks>> is to specify the https://tools.ietf.org/html/draft-west-first-party-cookies[SameSite Attribute] on cookies.\nA server can specify the `SameSite` attribute when setting a cookie to indicate that the cookie should not be sent when coming from external sites.\n\n[NOTE]\n====\nSpring Security does not directly control the creation of the session cookie, so it does not provide support for the SameSite attribute.\nhttps://spring.io/projects/spring-session[Spring Session] provides support for the `SameSite` attribute in servlet-based applications.\nSpring Framework's {spring-framework-api-url}org/springframework/web/server/session/CookieWebSessionIdResolver.html[`CookieWebSessionIdResolver`] provides out of the box support for the `SameSite` attribute in WebFlux-based applications.\n====\n\nAn example, of an HTTP response header with the `SameSite` attribute might look like:\n\n.SameSite HTTP response\n[source]\n----\nSet-Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly; SameSite=Lax\n----\n\nValid values for the `SameSite` attribute are:\n\n* `Strict`: When specified, any request coming from the https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-2.1[same-site] includes the cookie.\nOtherwise, the cookie is not included in the HTTP request.\n* `Lax`: When specified, cookies are sent when coming from the https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-2.1[same-site] or when the request comes from top-level navigations and the <<Safe Methods Must be Read-only,method is read-only>>.\nOtherwise, the cookie is not included in the HTTP request.\n\nConsider how <<csrf-explained,our example>> could be protected using the `SameSite` attribute.\nThe bank application can protect against CSRF by specifying the `SameSite` attribute on the session cookie.\n\nWith the `SameSite` attribute set on our session cookie, the browser continues to send the `JSESSIONID` cookie with requests coming from the banking website.\nHowever, the browser no longer sends the `JSESSIONID` cookie with a transfer request coming from the evil website.\nSince the session is no longer present in the transfer request coming from the evil website, the application is protected from the CSRF attack.\n\nThere are some important https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-5[considerations] to be aware of when using `SameSite` attribute to protect against CSRF attacks.\n\nSetting the `SameSite` attribute to `Strict` provides a stronger defense but can confuse users.\nConsider a user who stays logged into a social media site hosted at https://social.example.com.\nThe user receives an email at https://email.example.org that includes a link to the social media site.\nIf the user clicks on the link, they would rightfully expect to be authenticated to the social media site.\nHowever, if the `SameSite` attribute is `Strict`, the cookie would not be sent and so the user would not be authenticated.\n\nAnother obvious consideration is that, in order for the `SameSite` attribute to protect users, the browser must support the `SameSite` attribute.\nMost modern browsers do https://developer.mozilla.org/en-US/docs/Web/HTTP/headers/Set-Cookie#Browser_compatibility[support the SameSite attribute].\nHowever, older browsers that are still in use may not.\n\nFor this reason, we generally recommend using the `SameSite` attribute as a defense in depth rather than the sole protection against CSRF attacks.\n\n[[csrf-when]]\n== When to use CSRF protection\nWhen should you use CSRF protection?\nOur recommendation is to use CSRF protection for any request that could be processed by a browser by normal users.\nIf you are creating a service that is used only by non-browser clients, you likely want to disable CSRF protection.\n\n[[csrf-when-json]]\n=== CSRF protection and JSON\nA common question is \"`do I need to protect JSON requests made by JavaScript?`\"\nThe short answer is: It depends.\nHowever, you must be very careful, as there are CSRF exploits that can impact JSON requests.\nFor example, a malicious user can create a http://blog.opensecurityresearch.com/2012/02/json-csrf-with-parameter-padding.html[CSRF with JSON by using the following form]:\n\n.CSRF with JSON form\n[source,html]\n----\n<form action=\"https://bank.example.com/transfer\" method=\"post\" enctype=\"text/plain\">\n\t<input name='{\"amount\":100,\"routingNumber\":\"evilsRoutingNumber\",\"account\":\"evilsAccountNumber\", \"ignore_me\":\"' value='test\"}' type='hidden'>\n\t<input type=\"submit\"\n\t\tvalue=\"Win Money!\"/>\n</form>\n----\n\n\nThis produces the following JSON structure\n\n.CSRF with JSON request\n[source,javascript]\n----\n{ \"amount\": 100,\n\"routingNumber\": \"evilsRoutingNumber\",\n\"account\": \"evilsAccountNumber\",\n\"ignore_me\": \"=test\"\n}\n----\n\nIf an application were not validating the `Content-Type` header, it would be exposed to this exploit.\nDepending on the setup, a Spring MVC application that validates the Content-Type could still be exploited by updating the URL suffix to end with `.json`, as follows:\n\n.CSRF with JSON Spring MVC form\n[source,html]\n----\n<form action=\"https://bank.example.com/transfer.json\" method=\"post\" enctype=\"text/plain\">\n\t<input name='{\"amount\":100,\"routingNumber\":\"evilsRoutingNumber\",\"account\":\"evilsAccountNumber\", \"ignore_me\":\"' value='test\"}' type='hidden'>\n\t<input type=\"submit\"\n\t\tvalue=\"Win Money!\"/>\n</form>\n----\n\n[[csrf-when-stateless]]\n=== CSRF and Stateless Browser Applications\nWhat if my application is stateless?\nThat does not necessarily mean you are protected.\nIn fact, if a user does not need to perform any actions in the web browser for a given request, they are likely still vulnerable to CSRF attacks.\n\nFor example, consider an application that uses a custom cookie that contains all the state within it for authentication (instead of the JSESSIONID).\nWhen the CSRF attack is made, the custom cookie is sent with the request in the same manner that the JSESSIONID cookie was sent in our previous example.\nThis application is vulnerable to CSRF attacks.\n\nApplications that use basic authentication are also vulnerable to CSRF attacks.\nThe application is vulnerable since the browser automatically includes the username and password in any requests in the same manner that the JSESSIONID cookie was sent in our previous example.\n\n[[csrf-considerations]]\n== CSRF Considerations\nThere are a few special considerations to consider when implementing protection against CSRF attacks.\n\n// FIXME: Document rotating the CSRF token at log in to avoid a fixation attack\n\n[[csrf-considerations-login]]\n=== Logging In\n\nTo protect against https://en.wikipedia.org/wiki/Cross-site_request_forgery#Forging_login_requests[forging login requests], the login HTTP request should be protected against CSRF attacks.\nProtecting against forging login requests is necessary so that a malicious user cannot read a victim's sensitive information.\nThe attack is performed as follows:\n\n. A malicious user performs a CSRF login with the malicious user's credentials.\nThe victim is now authenticated as the malicious user.\n. The malicious user then tricks the victim into visiting the compromised website and entering sensitive information.\n. The information is associated to the malicious user's account so the malicious user can log in with their own credentials and view the victim's sensitive information.\n\nA possible complication to ensuring login HTTP requests are protected against CSRF attacks is that the user might experience a session timeout that causes the request to be rejected.\nA session timeout is surprising to users who do not expect to need to have a session to log in.\nFor more information refer to <<csrf-considerations-timeouts>>.\n\n[[csrf-considerations-logout]]\n=== Logging Out\n\nTo protect against forging logout requests, the logout HTTP request should be protected against CSRF attacks.\nProtecting against forging logout requests is necessary so that a malicious user cannot read a victim's sensitive information.\nFor details on the attack, see https://labs.detectify.com/2017/03/15/loginlogout-csrf-time-to-reconsider/[this blog post].\n\nA possible complication to ensuring logout HTTP requests are protected against CSRF attacks is that the user might experience a session timeout that causes the request to be rejected.\nA session timeout is surprising to users who do not expect to have a session to log out.\nFor more information, see <<csrf-considerations-timeouts>>.\n\n[[csrf-considerations-timeouts]]\n=== CSRF and Session Timeouts\nMore often than not, the expected CSRF token is stored in the session.\nThis means that, as soon as the session expires, the server does not find an expected CSRF token and rejects the HTTP request.\nThere are a number of options (each of which come with trade offs) to solve timeouts:\n\n* The best way to mitigate the timeout is by using JavaScript to request a CSRF token on form submission.\nThe form is then updated with the CSRF token and submitted.\n* Another option is to have some JavaScript that lets the user know their session is about to expire.\nThe user can click a button to continue and refresh the session.\n* Finally, the expected CSRF token could be stored in a cookie.\nThis lets the expected CSRF token outlive the session.\n+\nOne might ask why the expected CSRF token is not stored in a cookie by default.\nThis is because there are known exploits in which headers (for example, to specify the cookies) can be set by another domain.\nThis is the same reason Ruby on Rails https://rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails[no longer skips a CSRF checks when the header X-Requested-With is present].\nSee https://web.archive.org/web/20210221120355/https://lists.webappsec.org/pipermail/websecurity_lists.webappsec.org/2011-February/007533.html[this webappsec.org thread] for details on how to perform the exploit.\nAnother disadvantage is that by removing the state (that is, the timeout), you lose the ability to forcibly invalidate the token if it is compromised.\n\n// FIXME: Document timeout with lengthy form expire. We do not want to automatically replay that request because it can lead to an exploit.\n\n[[csrf-considerations-multipart]]\n=== Multipart (file upload)\n\nProtecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken or the egg] problem.\nTo prevent a CSRF attack from occurring, the body of the HTTP request must be read to obtain the actual CSRF token.\nHowever, reading the body means that the file is uploaded, which means an external site can upload a file.\n\nThere are three options to using CSRF protection with multipart/form-data:\n\n* <<csrf-considerations-multipart-header,Include CSRF Token in an HTTP Request Header>>\n* <<csrf-considerations-multipart-body,Place CSRF Token in the Body>>\n* <<csrf-considerations-multipart-url,Place CSRF Token in the URL>>\n\nEach option has its trade-offs.\n\n[NOTE]\n====\nBefore you integrate Spring Security's CSRF protection with multipart file upload, you should first ensure that you can upload without the CSRF protection.\nMore information about using multipart forms with Spring, see the {spring-framework-reference-url}web/webmvc/mvc-servlet/multipart.html[Multipart Resolver] section of the Spring reference and the {spring-framework-api-url}org/springframework/web/multipart/support/MultipartFilter.html[`MultipartFilter` Javadoc].\n====\n\n[[csrf-considerations-multipart-header]]\n==== Include CSRF Token in an HTTP Request Header\nWhen JavaScript is available, you can submit multipart requests by sending the CSRF token in an HTTP request header.\nThis avoids placing the token in the URL and avoids processing multipart request bodies before CSRF validation.\nThis is generally the preferred approach for browser applications with JavaScript clients.\n\nSee xref:servlet/exploits/csrf.adoc#csrf-integration-javascript-other[JavaScript applications] for Servlet applications and xref:reactive/exploits/csrf.adoc#webflux-csrf-include-ajax[AJAX and JSON Requests] for Reactive applications.\n\n[[csrf-considerations-multipart-body]]\n==== Place CSRF Token in the Body\nAnother option is to include the actual CSRF token in the body of the request.\nBy placing the CSRF token in the body, the body is read before authorization is performed.\nThis means that anyone can place temporary files on your server.\nHowever, only authorized users can submit a file that is processed by your application.\nIn general, this is the recommended approach, because the temporary file upload should have a negligible impact on most servers.\n\n[[csrf-considerations-multipart-url]]\n==== Include CSRF Token in URL\nIf letting unauthorized users upload temporary files is not acceptable and JavaScript is not available, an alternative is to include the expected CSRF token as a query parameter in the action attribute of the form.\nThe disadvantage to this approach is that query parameters can be leaked.\nMore generally, it is considered best practice to place sensitive data within the body or headers to ensure it is not leaked.\nYou can find additional information in https://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3[RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI's].\n\n[[csrf-considerations-override-method]]\n==== HiddenHttpMethodFilter\nSome applications can use a form parameter to override the HTTP method.\nFor example, the following form can treat the HTTP method as a `delete` rather than a `post`.\n\n.CSRF Hidden HTTP Method Form\n[source,html]\n----\n<form action=\"/process\"\n\tmethod=\"post\">\n\t<!-- ... -->\n\t<input type=\"hidden\"\n\t\tname=\"_method\"\n\t\tvalue=\"delete\"/>\n</form>\n----\n\n\nOverriding the HTTP method occurs in a filter.\nThat filter must be placed before Spring Security's support.\nNote that overriding happens only on a `post`, so this is actually unlikely to cause any real problems.\nHowever, it is still best practice to ensure that it is placed before Spring Security's filters.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/exploits/headers.adoc",
    "content": "[[headers]]\n= Security HTTP Response Headers\n\n[NOTE]\n====\nThis portion of the documentation discusses the general topic of Security HTTP Response Headers.\nSee the relevant sections for specific information on Security HTTP Response Headers in xref:servlet/exploits/headers.adoc#servlet-headers[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers[WebFlux]-based applications.\n====\n\nYou can use https://owasp.org/www-project-secure-headers/#div-headers[HTTP response headers] in many ways to increase the security of web applications.\nThis section is dedicated to the various HTTP response headers for which Spring Security provides explicit support for.\nIf necessary, you can also configure Spring Security to provide <<headers-custom,custom headers>>.\n\n[[headers-default]]\n== Default Security Headers\n\n[NOTE]\n====\nSee the relevant sections for how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-default[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers-default[webflux]-based applications.\n====\n\nSpring Security provides a default set of security related HTTP response headers to provide secure defaults.\n\nThe default for Spring Security is to include the following headers:\n\n.Default Security HTTP Response Headers\n[source,http]\n----\nCache-Control: no-cache, no-store, max-age=0, must-revalidate\nPragma: no-cache\nExpires: 0\nX-Content-Type-Options: nosniff\nStrict-Transport-Security: max-age=31536000 ; includeSubDomains\nX-Frame-Options: DENY\nX-XSS-Protection: 0\n----\n\n[NOTE]\n====\nStrict-Transport-Security is added only on HTTPS requests\n====\n\nIf the defaults do not meet your needs, you can easily remove, modify, or add headers from these defaults.\nFor additional details on each of these headers, see the corresponding sections:\n\n* <<headers-cache-control,Cache Control>>\n* <<headers-content-type-options,Content Type Options>>\n* <<headers-hsts,HTTP Strict Transport Security>>\n* <<headers-frame-options,X-Frame-Options>>\n* <<headers-xss-protection,X-XSS-Protection>>\n\n[[headers-cache-control]]\n== Cache Control\n\n[NOTE]\n====\nSee to the relevant sections for how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-cache-control[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers-cache-control[webflux]-based applications.\n====\n\nSpring Security's default is to disable caching to protect the user's content.\n\nIf a user authenticates to view sensitive information and then logs out, we do not want a malicious user to be able to click the back button to view the sensitive information.\nThe cache control headers that are sent by default are:\n\n.Default Cache Control HTTP Response Headers\n[source]\n----\nCache-Control: no-cache, no-store, max-age=0, must-revalidate\nPragma: no-cache\nExpires: 0\n----\n\nTo be secure by default, Spring Security adds these headers by default.\nHowever, if your application provides its own cache control headers, Spring Security backs out of the way.\nThis allows for applications to ensure that static resources (such as CSS and JavaScript) can be cached.\n\n\n[[headers-content-type-options]]\n== Content Type Options\n\n[NOTE]\n====\nRefer to the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-content-type-options[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-content-type-options[webflux] based applications.\n====\n\nHistorically, browsers, including Internet Explorer, would try to guess the content type of a request by using https://en.wikipedia.org/wiki/Content_sniffing[content sniffing].\nThis allowed browsers to improve the user experience by guessing the content type on resources that had not specified the content type.\nFor example, if a browser encountered a JavaScript file that did not have the content type specified, it would be able to guess the content type and then run it.\n\n[NOTE]\n====\nThere are many additional things one should do (such as only display the document in a distinct domain, ensure Content-Type header is set, sanitize the document, and others) when allowing content to be uploaded.\nHowever, these measures are out of the scope of what Spring Security provides.\nIt is also important to point out that, when disabling content sniffing, you must specify the content type in order for things to work properly.\n====\n\nThe problem with content sniffing is that this allowed malicious users to use polyglots (that is, a file that is valid as multiple content types) to perform XSS attacks.\nFor example, some sites may allow users to submit a valid postscript document to a website and view it.\nA malicious user might create a http://webblaze.cs.berkeley.edu/papers/barth-caballero-song.pdf[postscript document that is also a valid JavaScript file] and perform an XSS attack with it.\n\nBy default, Spring Security disables content sniffing by adding the following header to HTTP responses:\n\n.nosniff HTTP Response Header\n[source,http]\n----\nX-Content-Type-Options: nosniff\n----\n\n[[headers-hsts]]\n== HTTP Strict Transport Security (HSTS)\n\n[NOTE]\n====\nRefer to the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-hsts[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-hsts[webflux] based applications.\n====\n\nWhen you type in your bank's website, do you enter `mybank.example.com` or do you enter `\\https://mybank.example.com`?\nIf you omit the `https` protocol, you are potentially vulnerable to https://en.wikipedia.org/wiki/Man-in-the-middle_attack[Man-in-the-Middle attacks].\nEven if the website performs a redirect to `\\https://mybank.example.com`, a malicious user could intercept the initial HTTP request and manipulate the response (for example, redirect to `\\https://mibank.example.com` and steal their credentials).\n\nMany users omit the `https` protocol, and this is why https://tools.ietf.org/html/rfc6797[HTTP Strict Transport Security (HSTS)] was created.\nOnce `mybank.example.com` is added as a https://tools.ietf.org/html/rfc6797#section-5.1[HSTS host], a browser can know ahead of time that any request to mybank.example.com should be interpreted as `\\https://mybank.example.com`.\nThis greatly reduces the possibility of a Man-in-the-Middle attack occurring.\n\n[NOTE]\n====\nIn accordance with https://tools.ietf.org/html/rfc6797#section-7.2[RFC6797], the HSTS header is injected only into HTTPS responses.\nFor the browser to acknowledge the header, the browser must first trust the CA that signed the SSL certificate used to make the connection (not just the SSL certificate).\n====\n\nOne way for a site to be marked as a HSTS host is to have the host preloaded into the browser.\nAnother way is to add the `Strict-Transport-Security` header to the response.\nFor example, Spring Security's default behavior is to add the following header, which instructs the browser to treat the domain as an HSTS host for a year (there are 31536000 seconds in a non-leap year):\n\n\n.Strict Transport Security HTTP Response Header\n[source]\n----\nStrict-Transport-Security: max-age=31536000 ; includeSubDomains ; preload\n----\n\nThe optional `includeSubDomains` directive instructs the browser that subdomains (such as `secure.mybank.example.com`) should also be treated as an HSTS domain.\n\nThe optional `preload` directive instructs the browser that the domain should be preloaded in browser as an HSTS domain.\nFor more details on HSTS preload, see https://hstspreload.org.\n\n[[headers-hpkp]]\n== HTTP Public Key Pinning (HPKP)\n\n[NOTE]\n====\nTo remain passive, Spring Security still provides xref:servlet/exploits/headers.adoc#servlet-headers-hpkp[support for HPKP in servlet environments].\nHowever, for the reasons listed earlier, HPKP is no longer recommended by the Spring Security team.\n====\n\nhttps://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning[HTTP Public Key Pinning (HPKP)] specifies to a web client which public key to use with a certain web server to prevent Man-in-the-Middle (MITM) attacks with forged certificates.\nWhen used correctly, HPKP could add additional layers of protection against compromised certificates.\nHowever, due to the complexity of HPKP, many experts no longer recommend using it and https://www.chromestatus.com/feature/5903385005916160[Chrome has even removed support] for it.\n\n[[headers-hpkp-deprecated]]\nFor additional details around why HPKP is no longer recommended, read https://blog.qualys.com/ssllabs/2016/09/06/is-http-public-key-pinning-dead[Is HTTP Public Key Pinning Dead?] and https://scotthelme.co.uk/im-giving-up-on-hpkp/[I'm giving up on HPKP].\n\n[[headers-frame-options]]\n== X-Frame-Options\n\n[NOTE]\n====\nSee the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-frame-options[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-frame-options[webflux] based applications.\n====\n\nLetting your website be added to a frame can be a security issue.\nFor example, by using clever CSS styling, users could be tricked into clicking on something that they were not intending.\nFor example, a user that is logged into their bank might click a button that grants access to other users.\nThis sort of attack is known as https://en.wikipedia.org/wiki/Clickjacking[Clickjacking].\n\n[NOTE]\n====\nAnother modern approach to dealing with clickjacking is to use <<headers-csp>>.\n====\n\nThere are a number ways to mitigate clickjacking attacks.\nFor example, to protect legacy browsers from clickjacking attacks, you can use https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet#Best-for-now_Legacy_Browser_Frame_Breaking_Script[frame breaking code].\nWhile not perfect, the frame breaking code is the best you can do for the legacy browsers.\n\nA more modern approach to address clickjacking is to use https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options[X-Frame-Options] header.\nBy default, Spring Security disables rendering pages within an iframe by using with the following header:\n\n[source]\n----\nX-Frame-Options: DENY\n----\n\n[[headers-xss-protection]]\n== X-XSS-Protection\n\n[NOTE]\n====\nSee the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-xss-protection[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers-xss-protection[webflux]-based applications.\n====\n\nSome browsers have built-in support for filtering out https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_(OWASP-DV-001)[reflected XSS attacks].\nThe filter has been deprecated in major browsers, and https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-xss-protection[current OWASP recommendation] is to explicitly set the header to 0.\n\nBy default, Spring Security blocks the content by using the following header:\n\n[source]\n----\nX-XSS-Protection: 0\n----\n\n\n[[headers-csp]]\n== Content Security Policy (CSP)\n\n[NOTE]\n====\nSee the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-csp[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers-csp[webflux]-based applications.\n====\n\nhttps://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] is a mechanism that web applications can use to mitigate content injection vulnerabilities, such as cross-site scripting (XSS).\nCSP is a declarative policy that provides a facility for web application authors to declare and ultimately inform the client (user-agent) about the sources from which the web application expects to load resources.\n\n[NOTE]\n====\nContent Security Policy is not intended to solve all content injection vulnerabilities.\nInstead, you can use CSP to help reduce the harm caused by content injection attacks.\nAs a first line of defense, web application authors should validate their input and encode their output.\n====\n\nA web application can use CSP by including one of the following HTTP headers in the response:\n\n* `Content-Security-Policy`\n* `Content-Security-Policy-Report-Only`\n\nEach of these headers are used as a mechanism to deliver a security policy to the client.\nA security policy contains a set of security policy directives, each responsible for declaring the restrictions for a particular resource representation.\n\nFor example, a web application can declare that it expects to load scripts from specific, trusted sources by including the following header in the response:\n\n.Content Security Policy Example\n[source]\n----\nContent-Security-Policy: script-src https://trustedscripts.example.com\n----\n\nAn attempt to load a script from another source other than what is declared in the `script-src` directive is blocked by the user-agent.\nAdditionally, if the https://www.w3.org/TR/CSP2/#directive-report-uri[report-uri] directive is declared in the security policy, the violation will be reported by the user-agent to the declared URL.\n\nFor example, if a web application violates the declared security policy, the following response header instructs the user-agent to send violation reports to the URL specified in the policy's `report-uri` directive.\n\n.Content Security Policy with report-uri\n[source]\n----\nContent-Security-Policy: script-src https://trustedscripts.example.com; report-uri /csp-report-endpoint/\n----\n\nhttps://www.w3.org/TR/CSP2/#violation-reports[Violation reports] are standard JSON structures that can be captured either by the web application's own API or by a publicly hosted CSP violation reporting service, such as https://report-uri.io/.\n\nThe `Content-Security-Policy-Report-Only` header provides the capability for web application authors and administrators to monitor security policies rather than enforce them.\nThis header is typically used when experimenting or developing security policies for a site.\nWhen a policy is deemed effective, it can be enforced by using the `Content-Security-Policy` header field instead.\n\nGiven the following response header, the policy declares that scripts can be loaded from one of two possible sources.\n\n.Content Security Policy Report Only\n[source]\n----\nContent-Security-Policy-Report-Only: script-src 'self' https://trustedscripts.example.com; report-uri /csp-report-endpoint/\n----\n\nIf the site violates this policy, by attempting to load a script from `evil.example.com`, the user-agent sends a violation report to the declared URL specified by the `report-uri` directive but still lets the violating resource load.\n\nApplying Content Security Policy to a web application is often a non-trivial undertaking.\nThe following resources may provide further assistance in developing effective security policies for your site:\n\nhttps://www.html5rocks.com/en/tutorials/security/content-security-policy/[An Introduction to Content Security Policy]\n\nhttps://developer.mozilla.org/en-US/docs/Web/Security/CSP[CSP Guide - Mozilla Developer Network]\n\nhttps://www.w3.org/TR/CSP2/[W3C Candidate Recommendation]\n\n[[headers-referrer]]\n== Referrer Policy\n\n[NOTE]\n====\nSee the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-referrer[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers-referrer[webflux]-based applications.\n====\n\nhttps://www.w3.org/TR/referrer-policy[Referrer Policy] is a mechanism that web applications can use to manage the referrer field, which contains the last\npage the user was on.\n\nSpring Security's approach is to use the https://www.w3.org/TR/referrer-policy/[Referrer Policy] header, which provides different https://www.w3.org/TR/referrer-policy/#referrer-policies[policies]:\n\n.Referrer Policy Example\n[source]\n----\nReferrer-Policy: same-origin\n----\n\nThe Referrer-Policy response header instructs the browser to let the destination knows the source where the user was previously.\n\n[[headers-feature]]\n== Feature Policy\n\n[NOTE]\n====\nSee the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-feature[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers-feature[webflux]-based applications.\n====\n\nhttps://wicg.github.io/feature-policy/[Feature Policy] is a mechanism that lets web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.\n\n.Feature Policy Example\n[source]\n----\nFeature-Policy: geolocation 'self'\n----\n\nWith Feature Policy, developers can opt-in to a set of \"`policies`\" for the browser to enforce on specific features used throughout your site.\nThese policies restrict what APIs the site can access or modify the browser's default behavior for certain features.\n\n\n[[headers-permissions]]\n== Permissions Policy\n\n[NOTE]\n====\nSee the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-permissions[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers-permissions[webflux]-based applications.\n====\n\nhttps://w3c.github.io/webappsec-permissions-policy/[Permissions Policy] is a mechanism that lets web developers selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.\n\n.Permissions Policy Example\n[source]\n----\nPermissions-Policy: geolocation=(self)\n----\n\nWith Permissions Policy, developers can opt-in to a set of \"policies\" for the browser to enforce on specific features used throughout your site.\nThese policies restrict what APIs the site can access or modify the browser's default behavior for certain features.\n\n\n[[headers-clear-site-data]]\n== Clear Site Data\n\n[NOTE]\n====\nSee the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-clear-site-data[servlet]- and xref:reactive/exploits/headers.adoc#webflux-headers-clear-site-data[webflux]- based applications.\n====\n\nhttps://www.w3.org/TR/clear-site-data/[Clear Site Data] is a mechanism by which any browser-side data (cookies, local storage, and the like) can be removed when an HTTP response contains this header:\n\n[source]\n----\nClear-Site-Data: \"cache\", \"cookies\", \"storage\", \"executionContexts\"\n----\n\nThis is a nice clean-up action to perform on logout.\n\n\n[[headers-custom]]\n== Custom Headers\n\n[NOTE]\n====\nSee the relevant section to see how to configure xref:servlet/exploits/headers.adoc#servlet-headers-custom[servlet] based applications.\n====\n\nSpring Security has mechanisms to make it convenient to add the more common security headers to your application.\nHowever, it also provides hooks to enable adding custom headers.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/exploits/http.adoc",
    "content": "[[http]]\n= HTTP\n\nAll HTTP-based communication, including https://www.troyhunt.com/heres-why-your-static-website-needs-https/[static resources], should be protected by https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html[using TLS].\n\nAs a framework, Spring Security does not handle HTTP connections and thus does not provide support for HTTPS directly.\nHowever, it does provide a number of features that help with HTTPS usage.\n\n[[http-redirect]]\n== Redirect to HTTPS\n\nWhen a client uses HTTP, you can configure Spring Security to redirect to HTTPS in both xref:servlet/exploits/http.adoc#servlet-http-redirect[Servlet] and xref:reactive/exploits/http.adoc#webflux-http-redirect[WebFlux] environments.\n\n[[http-hsts]]\n== Strict Transport Security\n\nSpring Security provides support for xref:features/exploits/headers.adoc#headers-hsts[Strict Transport Security] and enables it by default.\n\n[[http-proxy-server]]\n== Proxy Server Configuration\n\nWhen using a proxy server, it is important to ensure that you have configured your application properly.\nFor example, many applications have a load balancer that responds to request for `\\https://example.com/` by forwarding the request to an application server at `\\https://192.168.0.107`\nWithout proper configuration, the application server can not know that the load balancer exists and treats the request as though `\\https://192.168.0.107:8080` was requested by the client.\n\nTo fix this, you can use https://tools.ietf.org/html/rfc7239[RFC 7239] to specify that a load balancer is being used.\nTo make the application aware of this, you need to configure your application server to be aware of the X-Forwarded headers.\nFor example, Tomcat uses https://tomcat.apache.org/tomcat-10.1-doc/api/org/apache/catalina/valves/RemoteIpValve.html[`RemoteIpValve`] and Jetty uses https://eclipse.dev/jetty/javadoc/jetty-11/org/eclipse/jetty/server/ForwardedRequestCustomizer.html[`ForwardedRequestCustomizer`].\nAlternatively, Spring users can use {spring-framework-reference-url}web/webmvc/filters.html#filters-forwarded-headers[`ForwardedHeaderFilter`] with the Servlet stack or {spring-framework-reference-url}web/webflux/reactive-spring.html#webflux-forwarded-headers[`ForwardedHeaderTransformer`] with the Reactive stack.\n\nSpring Boot users can use the `server.forward-headers-strategy` property to configure the application.\nSee the {spring-boot-reference-url}how-to/webserver.html#howto.webserver.use-behind-a-proxy-server[Spring Boot documentation] for further details.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/exploits/index.adoc",
    "content": "[[exploits]]\n= Protection Against Exploits\n:page-section-summary-toc: 1\n\nSpring Security provides protection against common exploits.\nWhenever possible, the protection is enabled by default.\nThis section describes the various exploits that Spring Security protects against.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/index.adoc",
    "content": "[[features]]\n= Features\n:page-section-summary-toc: 1\n\nSpring Security provides comprehensive support for xref:features/authentication/index.adoc[authentication], xref:features/authorization/index.adoc[authorization], and protection against xref:features/exploits/index.adoc#exploits[common exploits].\nIt also provides integration with other libraries to simplify its usage.\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/integrations/concurrency.adoc",
    "content": "[[concurrency]]\n= Concurrency Support\n\nIn most environments, Security is stored on a per `Thread` basis.\nThis means that when work is done on a new `Thread`, the `SecurityContext` is lost.\nSpring Security provides some infrastructure to help make this much easier for users.\nSpring Security provides low level abstractions for working with Spring Security in multi-threaded environments.\nIn fact, this is what Spring Security builds on to integration with xref:servlet/integrations/servlet-api.adoc#servletapi-start-runnable[AsyncContext.start(Runnable)] and xref:servlet/integrations/mvc.adoc#mvc-async[Spring MVC Async Integration].\n\n== DelegatingSecurityContextRunnable\n\nOne of the most fundamental building blocks within Spring Security's concurrency support is the `DelegatingSecurityContextRunnable`.\nIt wraps a delegate `Runnable` in order to initialize the `SecurityContextHolder` with a specified `SecurityContext` for the delegate.\nIt then invokes the delegate Runnable ensuring to clear the `SecurityContextHolder` afterwards.\nThe `DelegatingSecurityContextRunnable` looks something like this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic void run() {\ntry {\n\tSecurityContextHolder.setContext(securityContext);\n\tdelegate.run();\n} finally {\n\tSecurityContextHolder.clearContext();\n}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nfun run() {\n    try {\n        SecurityContextHolder.setContext(securityContext)\n        delegate.run()\n    } finally {\n        SecurityContextHolder.clearContext()\n    }\n}\n----\n======\n\nWhile very simple, it makes it seamless to transfer the SecurityContext from one Thread to another.\nThis is important since, in most cases, the SecurityContextHolder acts on a per Thread basis.\nFor example, you might have used Spring Security's xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[<global-method-security>] support to secure one of your services.\nYou can now easily transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service.\nAn example of how you might do this can be found below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nRunnable originalRunnable = new Runnable() {\npublic void run() {\n\t// invoke secured service\n}\n};\n\nSecurityContext context = SecurityContextHolder.getContext();\nDelegatingSecurityContextRunnable wrappedRunnable =\n\tnew DelegatingSecurityContextRunnable(originalRunnable, context);\n\nnew Thread(wrappedRunnable).start();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval originalRunnable = Runnable {\n    // invoke secured service\n}\nval context: SecurityContext = SecurityContextHolder.getContext()\nval wrappedRunnable = DelegatingSecurityContextRunnable(originalRunnable, context)\n\nThread(wrappedRunnable).start()\n----\n======\n\nThe code above performs the following steps:\n\n* Creates a `Runnable` that will be invoking our secured service.\nNotice that it is not aware of Spring Security\n* Obtains the `SecurityContext` that we wish to use from the `SecurityContextHolder` and initializes the `DelegatingSecurityContextRunnable`\n* Use the `DelegatingSecurityContextRunnable` to create a Thread\n* Start the Thread we created\n\nSince it is quite common to create a `DelegatingSecurityContextRunnable` with the `SecurityContext` from the `SecurityContextHolder` there is a shortcut constructor for it.\nThe following code is the same as the code above:\n\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nRunnable originalRunnable = new Runnable() {\npublic void run() {\n\t// invoke secured service\n}\n};\n\nDelegatingSecurityContextRunnable wrappedRunnable =\n\tnew DelegatingSecurityContextRunnable(originalRunnable);\n\nnew Thread(wrappedRunnable).start();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval originalRunnable = Runnable {\n    // invoke secured service\n}\n\nval wrappedRunnable = DelegatingSecurityContextRunnable(originalRunnable)\n\nThread(wrappedRunnable).start()\n----\n======\n\nThe code we have is simple to use, but it still requires knowledge that we are using Spring Security.\nIn the next section we will take a look at how we can utilize `DelegatingSecurityContextExecutor` to hide the fact that we are using Spring Security.\n\n== DelegatingSecurityContextExecutor\n\nIn the previous section we found that it was easy to use the `DelegatingSecurityContextRunnable`, but it was not ideal since we had to be aware of Spring Security in order to use it.\nLet's take a look at how `DelegatingSecurityContextExecutor` can shield our code from any knowledge that we are using Spring Security.\n\nThe design of `DelegatingSecurityContextExecutor` is very similar to that of `DelegatingSecurityContextRunnable` except it accepts a delegate `Executor` instead of a delegate `Runnable`.\nYou can see an example of how it might be used below:\n\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nSecurityContext context = SecurityContextHolder.createEmptyContext();\nAuthentication authentication =\n\tUsernamePasswordAuthenticationToken.authenticated(\"user\",\"doesnotmatter\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\ncontext.setAuthentication(authentication);\n\nSimpleAsyncTaskExecutor delegateExecutor =\n\tnew SimpleAsyncTaskExecutor();\nDelegatingSecurityContextExecutor executor =\n\tnew DelegatingSecurityContextExecutor(delegateExecutor, context);\n\nRunnable originalRunnable = new Runnable() {\npublic void run() {\n\t// invoke secured service\n}\n};\n\nexecutor.execute(originalRunnable);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval context: SecurityContext = SecurityContextHolder.createEmptyContext()\nval authentication: Authentication =\n    UsernamePasswordAuthenticationToken(\"user\", \"doesnotmatter\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"))\ncontext.authentication = authentication\n\nval delegateExecutor = SimpleAsyncTaskExecutor()\nval executor = DelegatingSecurityContextExecutor(delegateExecutor, context)\n\nval originalRunnable = Runnable {\n    // invoke secured service\n}\n\nexecutor.execute(originalRunnable)\n----\n======\n\nThe code performs the following steps:\n\n* Creates the `SecurityContext` to be used for our `DelegatingSecurityContextExecutor`.\nNote that in this example we simply create the `SecurityContext` by hand.\nHowever, it does not matter where or how we get the `SecurityContext` (i.e. we could obtain it from the `SecurityContextHolder` if we wanted).\n* Creates a delegateExecutor that is in charge of executing submitted ``Runnable``s\n* Finally we create a `DelegatingSecurityContextExecutor` which is in charge of wrapping any Runnable that is passed into the execute method with a `DelegatingSecurityContextRunnable`.\nIt then passes the wrapped Runnable to the delegateExecutor.\nIn this instance, the same `SecurityContext` will be used for every Runnable submitted to our `DelegatingSecurityContextExecutor`.\nThis is nice if we are running background tasks that need to be run by a user with elevated privileges.\n* At this point you may be asking yourself \"How does this shield my code of any knowledge of Spring Security?\" Instead of creating the `SecurityContext` and the `DelegatingSecurityContextExecutor` in our own code, we can inject an already initialized instance of `DelegatingSecurityContextExecutor`.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nprivate Executor executor; // becomes an instance of our DelegatingSecurityContextExecutor\n\npublic void submitRunnable() {\nRunnable originalRunnable = new Runnable() {\n\tpublic void run() {\n\t// invoke secured service\n\t}\n};\nexecutor.execute(originalRunnable);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nlateinit var executor: Executor // becomes an instance of our DelegatingSecurityContextExecutor\n\nfun submitRunnable() {\n    val originalRunnable = Runnable {\n        // invoke secured service\n    }\n    executor.execute(originalRunnable)\n}\n----\n======\n\nNow our code is unaware that the `SecurityContext` is being propagated to the `Thread`, then the `originalRunnable` is run, and then the `SecurityContextHolder` is cleared out.\nIn this example, the same user is being used to run each thread.\nWhat if we wanted to use the user from `SecurityContextHolder` at the time we invoked `executor.execute(Runnable)` (i.e. the currently logged in user) to process ``originalRunnable``?\nThis can be done by removing the `SecurityContext` argument from our `DelegatingSecurityContextExecutor` constructor.\nFor example:\n\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nSimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();\nDelegatingSecurityContextExecutor executor =\n\tnew DelegatingSecurityContextExecutor(delegateExecutor);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval delegateExecutor = SimpleAsyncTaskExecutor()\nval executor = DelegatingSecurityContextExecutor(delegateExecutor)\n----\n======\n\nNow anytime `executor.execute(Runnable)` is executed the `SecurityContext` is first obtained by the `SecurityContextHolder` and then that `SecurityContext` is used to create our `DelegatingSecurityContextRunnable`.\nThis means that we are running our `Runnable` with the same user that was used to invoke the `executor.execute(Runnable)` code.\n\n== Spring Security Concurrency Classes\n\nRefer to the Javadoc for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.\nThey are quite self-explanatory once you understand the previous code.\n\n* `DelegatingSecurityContextCallable`\n* `DelegatingSecurityContextExecutor`\n* `DelegatingSecurityContextExecutorService`\n* `DelegatingSecurityContextRunnable`\n* `DelegatingSecurityContextScheduledExecutorService`\n* `DelegatingSecurityContextSchedulingTaskExecutor`\n* `DelegatingSecurityContextAsyncTaskExecutor`\n* `DelegatingSecurityContextTaskExecutor`\n* `DelegatingSecurityContextTaskScheduler`\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/integrations/cryptography.adoc",
    "content": "[[crypto]]\n= Spring Security Crypto Module\n\n[[spring-security-crypto-introduction]]\nThe Spring Security Crypto module provides support for symmetric encryption, key generation, and password encoding.\nThe code is distributed as part of the core module but has no dependencies on any other Spring Security (or Spring) code.\n\n\n[[spring-security-crypto-encryption]]\n== Encryptors\nThe javadoc:org.springframework.security.crypto.encrypt.Encryptors[] class provides factory methods for constructing symmetric encryptors.\nThis class lets you create javadoc:org.springframework.security.crypto.encrypt.BytesEncryptor[] instances to encrypt data in raw `byte[]` form.\nYou can also construct javadoc:org.springframework.security.crypto.encrypt.TextEncryptor[] instances to encrypt text strings.\nEncryptors are thread-safe.\n\n[NOTE]\n====\nBoth `BytesEncryptor` and `TextEncryptor` are interfaces. `BytesEncryptor` has multiple implementations.\n====\n\n[[spring-security-crypto-encryption-bytes]]\n=== BytesEncryptor\nYou can use the `Encryptors.stronger` factory method to construct a `BytesEncryptor`:\n\n.BytesEncryptor\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nEncryptors.stronger(\"password\", \"salt\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nEncryptors.stronger(\"password\", \"salt\")\n----\n======\n\nThe `stronger` encryption method creates an encryptor by using 256-bit AES encryption with\nGalois Counter Mode (GCM).\nIt derives the secret key by using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2).\nThis method requires Java 6.\nThe password used to generate the `SecretKey` should be kept in a secure place and should not be shared.\nThe salt is used to prevent dictionary attacks against the key in the event that your encrypted data is compromised.\nA 16-byte random initialization vector is also applied so that each encrypted message is unique.\n\nThe provided salt should be in hex-encoded String form, be random, and be at least 8 bytes in length.\nYou can generate such a salt by using a `KeyGenerator`:\n\n.Generating a key\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nString salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval salt = KeyGenerators.string().generateKey() // generates a random 8-byte salt that is then hex-encoded\n----\n======\n\nYou can also use the `standard` encryption method, which is 256-bit AES in Cipher Block Chaining (CBC) Mode.\nThis mode is not https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated] and does not provide any\nguarantees about the authenticity of the data.\nFor a more secure alternative, use `Encryptors.stronger`.\n\n[[spring-security-crypto-encryption-text]]\n=== TextEncryptor\nYou can use the `Encryptors.text` factory method to construct a standard TextEncryptor:\n\n.TextEncryptor\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nEncryptors.text(\"password\", \"salt\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nEncryptors.text(\"password\", \"salt\")\n----\n======\n\nA `TextEncryptor` uses a standard `BytesEncryptor` to encrypt text data.\nEncrypted results are returned as hex-encoded strings for easy storage on the filesystem or in a database.\n\n[[spring-security-crypto-keygenerators]]\n== Key Generators\nThe javadoc:org.springframework.security.crypto.keygen.KeyGenerators[] class provides a number of convenience factory methods for constructing different types of key generators.\nBy using this class, you can create a javadoc:org.springframework.security.crypto.keygen.BytesKeyGenerator[] to generate `byte[]` keys.\nYou can also construct a javadoc:org.springframework.security.crypto.keygen.StringKeyGenerator[] to generate string keys.\n`KeyGenerators` is a thread-safe class.\n\n=== BytesKeyGenerator\nYou can use the `KeyGenerators.secureRandom` factory methods to generate a `BytesKeyGenerator` backed by a `SecureRandom` instance:\n\n.BytesKeyGenerator\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nBytesKeyGenerator generator = KeyGenerators.secureRandom();\nbyte[] key = generator.generateKey();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval generator = KeyGenerators.secureRandom()\nval key = generator.generateKey()\n----\n======\n\nThe default key length is 8 bytes.\nA `KeyGenerators.secureRandom` variant provides control over the key length:\n\n.KeyGenerators.secureRandom\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nKeyGenerators.secureRandom(16);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nKeyGenerators.secureRandom(16)\n----\n======\n\nUse the `KeyGenerators.shared` factory method to construct a BytesKeyGenerator that always returns the same key on every invocation:\n\n.KeyGenerators.shared\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nKeyGenerators.shared(16);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nKeyGenerators.shared(16)\n----\n======\n\n=== StringKeyGenerator\nYou can use the `KeyGenerators.string` factory method to construct an 8-byte, `SecureRandom` `KeyGenerator` that hex-encodes each key as a `String`:\n\n.StringKeyGenerator\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nKeyGenerators.string();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nKeyGenerators.string()\n----\n======\n\n[[spring-security-crypto-passwordencoders]]\n== Password Encoding\nThe password package of the `spring-security-crypto` module provides support for encoding passwords.\n`PasswordEncoder` is the central service interface and has the following signature:\n\n[source,java]\n----\npublic interface PasswordEncoder {\n\tString encode(CharSequence rawPassword);\n\n\tboolean matches(CharSequence rawPassword, String encodedPassword);\n\n\tdefault boolean upgradeEncoding(String encodedPassword) {\n\t\treturn false;\n\t}\n}\n----\n\nThe `matches` method returns true if the `rawPassword`, once encoded, equals the `encodedPassword`.\nThis method is designed to support password-based authentication schemes.\n\nThe `BCryptPasswordEncoder` implementation uses the widely supported \"`bcrypt`\" algorithm to hash the passwords.\nBcrypt uses a random 16-byte salt value and is a deliberately slow algorithm, to hinder password crackers.\nYou can tune the amount of work it does by using the `strength` parameter, which takes a value from 4 to 31.\nThe higher the value, the more work has to be done to calculate the hash.\nThe default value is `10`.\nYou can change this value in your deployed system without affecting existing passwords, as the value is also stored in the encoded hash.\nThe following example uses the `BCryptPasswordEncoder`:\n\n.BCryptPasswordEncoder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n\n// Create an encoder with strength 16\nBCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);\nString result = encoder.encode(\"myPassword\");\nassertTrue(encoder.matches(\"myPassword\", result));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n\n// Create an encoder with strength 16\nval encoder = BCryptPasswordEncoder(16)\nval result: String = encoder.encode(\"myPassword\")\nassertTrue(encoder.matches(\"myPassword\", result))\n----\n======\n\nThe `Pbkdf2PasswordEncoder` implementation uses PBKDF2 algorithm to hash the passwords.\nTo defeat password cracking, PBKDF2 is a deliberately slow algorithm and should be tuned to take about .5 seconds to verify a password on your system.\nThe following system uses the `Pbkdf2PasswordEncoder`:\n\n\n.Pbkdf2PasswordEncoder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Create an encoder with all the defaults\nPbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();\nString result = encoder.encode(\"myPassword\");\nassertTrue(encoder.matches(\"myPassword\", result));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Create an encoder with all the defaults\nval encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()\nval result: String = encoder.encode(\"myPassword\")\nassertTrue(encoder.matches(\"myPassword\", result))\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/integrations/data.adoc",
    "content": "[[data]]\n= Spring Data Integration\n\nSpring Security provides Spring Data integration that allows referring to the current user within your queries.\nIt is not only useful but necessary to include the user in the queries to support paged results since filtering the results afterwards would not scale.\n\n[[data-configuration]]\n== Spring Data & Spring Security Configuration\n\nTo use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`.\nIn Java Configuration, this would look like:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityEvaluationContextExtension securityEvaluationContextExtension() {\n\treturn new SecurityEvaluationContextExtension();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun securityEvaluationContextExtension(): SecurityEvaluationContextExtension {\n    return SecurityEvaluationContextExtension()\n}\n----\n======\n\nIn XML Configuration, this would look like:\n\n[source,xml]\n----\n<bean class=\"org.springframework.security.data.repository.query.SecurityEvaluationContextExtension\"/>\n----\n\n[[data-query]]\n== Security Expressions within @Query\n\nNow Spring Security can be used within your queries.\nFor example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Repository\npublic interface MessageRepository extends PagingAndSortingRepository<Message,Long> {\n\t@Query(\"select m from Message m where m.to.id = ?#{ principal?.id }\")\n\tPage<Message> findInbox(Pageable pageable);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Repository\ninterface MessageRepository : PagingAndSortingRepository<Message?, Long?> {\n    @Query(\"select m from Message m where m.to.id = ?#{ principal?.id }\")\n    fun findInbox(pageable: Pageable?): Page<Message?>?\n}\n----\n======\n\nThis checks to see if the `Authentication.getPrincipal().getId()` is equal to the recipient of the `Message`.\nNote that this example assumes you have customized the principal to be an Object that has an id property.\nBy exposing the `SecurityEvaluationContextExtension` bean, all of the xref:servlet/authorization/method-security.adoc#authorization-expressions[Common Security Expressions] are available within the Query.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/integrations/index.adoc",
    "content": "[[integrations]]\n= Integrations\n:page-section-summary-toc: 1\n\nSpring Security provides integrations with numerous frameworks and APIs.\nIn this section, we discuss generic integrations that are not specific to Servlet or Reactive environments.\nTo see specific integrations, refer to the xref:servlet/integrations/index.adoc[Servlet] and xref:reactive/integrations/cors.adoc[Reactive] Integrations sections.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/integrations/jackson.adoc",
    "content": "[[jackson]]\n= Jackson Support\n\nSpring Security provides Jackson 3 support for persisting Spring Security related classes.\nThis can improve the performance of serializing Spring Security related classes when working with distributed sessions (i.e. session replication, Spring Session, etc).\n\n[NOTE]\n====\nJackson 2 support is still available but deprecated for removal, so you are encouraged to migrate to Jackson 3.\n====\n\nTo use it, register `SecurityJacksonModules.getModules(ClassLoader)` with `JsonMapper.Builder` (https://github.com/FasterXML/jackson-databind[jackson-databind]):\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nClassLoader loader = getClass().getClassLoader();\nJsonMapper mapper = JsonMapper.builder()\n        .addModules(SecurityJacksonModules.getModules(loader))\n        .build();\n\n// ... use JsonMapper as normally ...\nSecurityContext context = new SecurityContextImpl();\n// ...\nString json = mapper.writeValueAsString(context);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval loader = javaClass.classLoader\nval mapper = JsonMapper.builder()\n    .addModules(SecurityJacksonModules.getModules(loader))\n    .build()\n\n// ... use JsonMapper as normally ...\nval context: SecurityContext = SecurityContextImpl()\n// ...\nval json: String = mapper.writeValueAsString(context)\n----\n======\n\n[NOTE]\n====\nUsing `SecurityJacksonModules` as above enables automatic inclusion of type information and configure a\n`PolymorphicTypeValidator` that handles the validation of class names.\n====\n\nIf needed, you can add custom classes to the validation handling.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nClassLoader loader = getClass().getClassLoader();\nBasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()\n        .allowIfSubType(MyCustomType.class);\nJsonMapper mapper = JsonMapper.builder()\n        .addModules(SecurityJacksonModules.getModules(loader, builder))\n        .build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval loader = javaClass.classLoader\nval builder = BasicPolymorphicTypeValidator.builder()\n        .allowIfSubType(MyCustomType::class)\nval mapper = JsonMapper.builder()\n    .addModules(SecurityJacksonModules.getModules(loader, builder))\n    .build()\n----\n======\n\n[NOTE]\n====\nThe following Spring Security modules provide Jackson support:\n\n- spring-security-core (javadoc:org.springframework.security.jackson.CoreJacksonModule[])\n- spring-security-web (javadoc:org.springframework.security.web.jackson.WebJacksonModule[], javadoc:org.springframework.security.web.jackson.WebServletJacksonModule[], javadoc:org.springframework.security.web.server.jackson.WebServerJacksonModule[])\n- spring-security-oauth2-client (javadoc:org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule[])\n- spring-security-oauth2-authorization-server (javadoc:org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule[])\n- spring-security-cas (javadoc:org.springframework.security.cas.jackson.CasJacksonModule[])\n- spring-security-ldap (javadoc:org.springframework.security.ldap.jackson.LdapJacksonModule[])\n- spring-security-saml2 (javadoc:org.springframework.security.saml2.jackson.Saml2JacksonModule[])\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/integrations/localization.adoc",
    "content": "[[localization]]\n= Localization\nSpring Security supports localization of exception messages that end users are likely to see.\nIf your application is designed for English-speaking users, you don't need to do anything as by default all Security messages are in English.\nIf you need to support other locales, everything you need to know is contained in this section.\n\nAll exception messages can be localized, including messages related to authentication failures and access being denied (authorization failures).\nExceptions and logging messages that are focused on developers or system deployers (including incorrect attributes, interface contract violations, using incorrect constructors, startup time validation, debug-level logging) are not localized and instead are hard-coded in English within Spring Security's code.\n\nShipping in the `spring-security-core-xx.jar` you will find an `org.springframework.security` package that in turn contains a `messages.properties` file, as well as localized versions for some common languages.\nThis should be referred to by your `ApplicationContext`, as Spring Security classes implement Spring's `MessageSourceAware` interface and expect the message resolver to be dependency injected at application context startup time.\nUsually all you need to do is register a bean inside your application context to refer to the messages.\nAn example is shown below:\n\n[source,xml]\n----\n<bean id=\"messageSource\"\n\tclass=\"org.springframework.context.support.ReloadableResourceBundleMessageSource\">\n<property name=\"basename\" value=\"classpath:org/springframework/security/messages\"/>\n</bean>\n----\n\nThe `messages.properties` is named in accordance with standard resource bundles and represents the default language supported by Spring Security messages.\nThis default file is in English.\n\nIf you wish to customize the `messages.properties` file, or support other languages, you should copy the file, rename it accordingly, and register it inside the above bean definition.\nThere are not a large number of message keys inside this file, so localization should not be considered a major initiative.\nIf you do perform localization of this file, please consider sharing your work with the community by logging a JIRA task and attaching your appropriately-named localized version of `messages.properties`.\n\nSpring Security relies on Spring's localization support in order to actually lookup the appropriate message.\nIn order for this to work, you have to make sure that the locale from the incoming request is stored in Spring's `org.springframework.context.i18n.LocaleContextHolder`.\nSpring MVC's `DispatcherServlet` does this for your application automatically, but since Spring Security's filters are invoked before this, the `LocaleContextHolder` needs to be set up to contain the correct `Locale` before the filters are called.\nYou can either do this in a filter yourself (which must come before the Spring Security filters in `web.xml`) or you can use Spring's `RequestContextFilter`.\nPlease refer to the Spring Framework documentation for further details on using localization with Spring.\n\nThe \"contacts\" sample application is set up to use localized messages.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/features/integrations/rest/http-service-client.adoc",
    "content": "= HTTP Service Clients Integration\n\nSpring Security's OAuth Support can integrate with `RestClient` and `WebClient` {spring-framework-reference-url}integration/rest-clients.html#rest-http-service-client[HTTP Service Clients].\n\n\n[[configuration]]\n== Configuration\nAfter xref:features/integrations/rest/http-service-client.adoc#configuration-restclient[RestClient] or xref:features/integrations/rest/http-service-client.adoc#configuration-webclient[WebClient] specific configuration, usage of xref:features/integrations/rest/http-service-client.adoc[] only requires adding a xref:features/integrations/rest/http-service-client.adoc#client-registration-id[`@ClientRegistrationId`] to methods that require OAuth or their declaring HTTP interface.\n\nSince the presence of xref:features/integrations/rest/http-service-client.adoc#client-registration-id[`@ClientRegistrationId`] determines if and how the OAuth token will be resolved, it is safe to add Spring Security's OAuth support any configuration.\n\n[[configuration-restclient]]\n=== RestClient Configuration\n\nSpring Security's OAuth Support can integrate with {spring-framework-reference-url}integration/rest-clients.html#rest-http-service-client[HTTP Service Clients] backed by `RestClient`.\nThe first step is to xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[create an `OAuthAuthorizedClientManager` Bean].\n\nNext you must configure `HttpServiceProxyFactory` and `RestClient` to be aware of xref:./http-service-client.adoc#client-registration-id[@ClientRegistrationId]\nTo simplify this configuration, use javadoc:org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer[].\n\ninclude-code::./RestClientHttpInterfaceIntegrationConfiguration[tag=config,indent=0]\n\nThe configuration:\n\n- Adds xref:features/integrations/rest/http-service-client.adoc#client-registration-id-processor[`ClientRegistrationIdProcessor`] to {spring-framework-reference-url}integration/rest-clients.html#rest-http-service-client[`HttpServiceProxyFactory`]\n- Adds xref:servlet/oauth2/client/authorized-clients.adoc#oauth2-client-rest-client[`OAuth2ClientHttpRequestInterceptor`] to the `RestClient`\n\n[[configuration-webclient]]\n=== WebClient Configuration\n\nSpring Security's OAuth Support can integrate with {spring-framework-reference-url}integration/rest-clients.html#rest-http-service-client[HTTP Service Clients] backed by `WebClient`.\nThe first step is to xref:reactive/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[create an `ReactiveOAuthAuthorizedClientManager` Bean].\n\nNext you must configure `HttpServiceProxyFactory` and `WebRestClient` to be aware of xref:./http-service-client.adoc#client-registration-id[@ClientRegistrationId]\nTo simplify this configuration, use javadoc:org.springframework.security.oauth2.client.web.reactive.function.client.support.OAuth2WebClientHttpServiceGroupConfigurer[].\n\ninclude-code::./ServerWebClientHttpInterfaceIntegrationConfiguration[tag=config,indent=0]\n\nThe configuration:\n\n- Adds xref:features/integrations/rest/http-service-client.adoc#client-registration-id-processor[`ClientRegistrationIdProcessor`] to {spring-framework-reference-url}/integration/rest-clients.html#rest-http-service-client[`HttpServiceProxyFactory`]\n- Adds xref:reactive/oauth2/client/authorized-clients.adoc#oauth2-client-web-client[`ServerOAuth2AuthorizedClientExchangeFilterFunction`] to the `WebClient`\n\n\n[[client-registration-id]]\n== @ClientRegistrationId\n\nYou can add the javadoc:org.springframework.security.oauth2.client.annotation.ClientRegistrationId[] on the HTTP Service to specify which javadoc:org.springframework.security.oauth2.client.registration.ClientRegistration[] to use.\n\ninclude-code::./UserService[tag=getAuthenticatedUser]\n\nThe xref:features/integrations/rest/http-service-client.adoc#client-registration-id[`@ClientRegistrationId`] will be processed by xref:features/integrations/rest/http-service-client.adoc#client-registration-id-processor[`ClientRegistrationIdProcessor`]\n\n[[type]]\n=== Type Level Declarations\n\n`@ClientRegistrationId` can also be added at the type level to avoid repeating the declaration on every method.\n\ninclude-code::./UserService[tag=type]\n\n[[client-registration-id-processor]]\n== `ClientRegistrationIdProcessor`\n\nThe xref:features/integrations/rest/http-service-client.adoc#configuration[configured] javadoc:org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor[] will:\n\n- Automatically invoke javadoc:org.springframework.security.oauth2.client.web.ClientAttributes#clientRegistrationId(java.lang.String)[] for each xref:features/integrations/rest/http-service-client.adoc#client-registration-id[`@ClientRegistrationId`].\n- This adds the javadoc:org.springframework.security.oauth2.client.registration.ClientRegistration#getId()[] to the attributes\n\nThe `id` is then processed by:\n\n- `OAuth2ClientHttpRequestInterceptor` for xref:servlet/oauth2/client/authorized-clients.adoc#oauth2-client-rest-client[RestClient Integration]\n- xref:servlet/oauth2/client/authorized-clients.adoc#oauth2-client-web-client[`ServletOAuth2AuthorizedClientExchangeFilterFunction`] (servlets) or xref:servlet/oauth2/client/authorized-clients.adoc#oauth2-client-web-client[`ServerOAuth2AuthorizedClientExchangeFilterFunction`] (reactive environments) for `WebClient`.\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/getting-spring-security.adoc",
    "content": "[[getting]]\n[[getting-gradle]]\n= Getting Spring Security\n\nThis section describes how to get the Spring Security binaries.\nSee xref:community.adoc#community-source[Source Code] for how to obtain the source code.\n\n== Release Numbering\n\nSpring Security versions are formatted as MAJOR.MINOR.PATCH such that:\n\n* MAJOR versions may contain breaking changes.\nTypically, these are done to provide improved security to match modern security practices.\n* MINOR versions contain enhancements but are considered passive updates.\n* PATCH level should be perfectly compatible, forwards and backwards, with the possible exception of changes that fix bugs.\n\n\n[[maven]]\n== Usage\n\nAs most open source projects, Spring Security deploys its dependencies as Maven artifacts, which makes them compatible with both Maven and Gradle. The following sections demonstrate how to integrate Spring Security with these build tools, with examples for Spring Boot and standalone usage.\n\n[[getting-maven-boot]]\n[[getting-gradle-boot]]\n=== Spring Boot\n\nSpring Boot provides a `spring-boot-starter-security` starter that aggregates Spring Security-related dependencies.\nThe simplest and preferred way to use the starter is to use https://docs.spring.io/initializr/docs/current/reference/html/[Spring Initializr] by using an IDE integration in (https://joshlong.com/jl/blogPost/tech_tip_geting_started_with_spring_boot.html[Eclipse] or https://www.jetbrains.com/help/idea/spring-boot.html#d1489567e2[IntelliJ], https://github.com/AlexFalappa/nb-springboot/wiki/Quick-Tour[NetBeans]) or through https://start.spring.io.\nAlternatively, you can manually add the starter, as the following example shows:\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml,subs=\"verbatim,attributes\"]\n----\n<dependencies>\n\t<!-- ... other dependency elements ... -->\n\t<dependency>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-security</artifactId>\n\t</dependency>\n</dependencies>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n[subs=\"verbatim,attributes\"]\n----\ndependencies {\n\timplementation \"org.springframework.boot:spring-boot-starter-security\"\n}\n----\n======\n\nSince Spring Boot provides a Maven BOM to manage dependency versions, you do not need to specify a version.\nIf you wish to override the Spring Security version, you can do so with a build property as shown below:\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml,subs=\"verbatim,attributes\"]\n----\n<properties>\n\t<!-- ... -->\n\t<spring-security.version>{spring-security-version}</spring-security.version>\n</properties>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n[subs=\"verbatim,attributes\"]\n----\next['spring-security.version']='{spring-security-version}'\n----\n======\n\nSince Spring Security makes breaking changes only in major releases, you can safely use a newer version of Spring Security with Spring Boot.\nHowever, at times, you may need to update the version of Spring Framework as well.\nYou can do so by adding a build property like so:\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml,subs=\"verbatim,attributes\"]\n----\n<properties>\n\t<!-- ... -->\n\t<spring.version>{spring-core-version}</spring.version>\n</properties>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n[subs=\"verbatim,attributes\"]\n----\next['spring.version']='{spring-core-version}'\n----\n======\n\nIf you use additional features (such as LDAP, OAuth 2, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies].\n\n[[getting-maven-no-boot]]\n=== Standalone Usage (Without Spring Boot)\n\nWhen you use Spring Security without Spring Boot, the preferred way is to use Spring Security's BOM to ensure that a consistent version of Spring Security is used throughout the entire project.\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml,ubs=\"verbatim,attributes\"]\n----\n<dependencyManagement>\n\t<dependencies>\n\t\t<!-- ... other dependency elements ... -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.security</groupId>\n\t\t\t<artifactId>spring-security-bom</artifactId>\n\t\t\t<version>{spring-security-version}</version>\n\t\t\t<type>pom</type>\n\t\t\t<scope>import</scope>\n\t\t</dependency>\n\t</dependencies>\n</dependencyManagement>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n[subs=\"verbatim,attributes\"]\n----\nplugins {\n\tid \"io.spring.dependency-management\" version \"1.0.6.RELEASE\"\n}\n\ndependencyManagement {\n\timports {\n\t\tmavenBom 'org.springframework.security:spring-security-bom:{spring-security-version}'\n\t}\n}\n----\n======\n\n[TIP]\nSpring provides a https://github.com/spring-gradle-plugins/dependency-management-plugin[`Dependency Management Plugin`] for Gradle\n\nA minimal Spring Security Maven set of dependencies typically looks like the following example:\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml,subs=\"verbatim,attributes\"]\n----\n<dependencies>\n\t<!-- ... other dependency elements ... -->\n\t<dependency>\n\t\t<groupId>org.springframework.security</groupId>\n\t\t<artifactId>spring-security-web</artifactId>\n\t</dependency>\n\t<dependency>\n\t\t<groupId>org.springframework.security</groupId>\n\t\t<artifactId>spring-security-config</artifactId>\n\t</dependency>\n</dependencies>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n[subs=\"verbatim,attributes\"]\n----\ndependencies {\n\timplementation \"org.springframework.security:spring-security-web\"\n\timplementation \"org.springframework.security:spring-security-config\"\n}\n----\n======\n\nIf you use additional features (such as LDAP, OAuth 2, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies].\n\nSpring Security builds against Spring Framework {spring-core-version} but should generally work with any newer version of Spring Framework 5.x.\nMany users are likely to run afoul of the fact that Spring Security's transitive dependencies resolve Spring Framework {spring-core-version}, which can cause strange classpath problems.\nThe easiest way to resolve this is to use the `spring-framework-bom` within the `<dependencyManagement>` section of your `pom.xml` or your `dependencyManagement` section of your `build.gradle`.\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml,subs=\"verbatim,attributes\"]\n----\n<dependencyManagement>\n\t<dependencies>\n\t\t<!-- ... other dependency elements ... -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-framework-bom</artifactId>\n\t\t\t<version>{spring-core-version}</version>\n\t\t\t<type>pom</type>\n\t\t\t<scope>import</scope>\n\t\t</dependency>\n\t</dependencies>\n</dependencyManagement>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n[subs=\"verbatim,attributes\"]\n----\nplugins {\n\tid \"io.spring.dependency-management\" version \"1.0.6.RELEASE\"\n}\n\ndependencyManagement {\n\timports {\n\t\tmavenBom 'org.springframework:spring-framework-bom:{spring-core-version}'\n\t}\n}\n----\n======\n\n[TIP]\nSpring provides a https://github.com/spring-gradle-plugins/dependency-management-plugin[`Dependency Management Plugin`] for Gradle\n\nThe preceding example ensures that all the transitive dependencies of Spring Security use the Spring {spring-core-version} modules.\n\n[NOTE]\n====\nThis approach uses Maven's \"`bill of materials`\" (BOM) concept and is only available in Maven 2.0.9+.\nFor additional details about how dependencies are resolved, see https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html[Maven's Introduction to the Dependency Mechanism documentation].\n====\n\n[[maven-repositories]]\n=== Maven Repositories\nAll https://github.com/spring-projects/spring-security/wiki/Release-Schedule-Guidelines[GA releases] are deployed to Maven Central, so you need not declare additional Maven repositories in your build configuration.\n\nFor Gradle using the `mavenCentral()` repository is sufficient for GA releases.\n\n.build.gradle\n[source,groovy]\n----\nrepositories {\n\tmavenCentral()\n}\n----\n\nIf you use a SNAPSHOT version, you need to ensure that you have the Spring Snapshot repository defined:\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml]\n----\n<repositories>\n\t<!-- ... possibly other repository elements ... -->\n\t<repository>\n\t\t<id>spring-snapshot</id>\n\t\t<name>Spring Snapshot Repository</name>\n\t\t<url>https://repo.spring.io/snapshot</url>\n\t</repository>\n</repositories>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n----\nrepositories {\n\tmaven { url 'https://repo.spring.io/snapshot' }\n}\n----\n======\n\nIf you use a milestone or release candidate version, you need to ensure that you have the Spring Milestone repository defined, as the following example shows:\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml]\n----\n<repositories>\n\t<!-- ... possibly other repository elements ... -->\n\t<repository>\n\t\t<id>spring-milestone</id>\n\t\t<name>Spring Milestone Repository</name>\n\t\t<url>https://repo.spring.io/milestone</url>\n\t</repository>\n</repositories>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n----\nrepositories {\n\tmaven { url 'https://repo.spring.io/milestone' }\n}\n----\n======\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/index.adoc",
    "content": "= Spring Security\n\n[NOTE]\n====\nSpring Security's documentation can be {site-url}/spring-security-docs.zip[downloaded] as a zip file.\n====\n\nSpring Security is a framework that provides xref:features/authentication/index.adoc[authentication], xref:features/authorization/index.adoc[authorization], and xref:features/exploits/index.adoc[protection against common attacks].\nWith first class support for securing both xref:servlet/index.adoc[imperative] and xref:reactive/index.adoc[reactive] applications, it is the de-facto standard for securing Spring-based applications.\n\nFor a complete list of features, see the xref:features/index.adoc[Features] section of the reference.\n\n== Getting Started\n\nIf you are ready to start securing an application see the Getting Started sections for xref:servlet/getting-started.adoc[servlet] and xref:reactive/getting-started.adoc[reactive]. These sections will walk you through creating your first Spring Security applications.\n\nIf you want to understand how Spring Security works, you can refer to the xref:servlet/architecture.adoc[Architecture] section.\n// FIXME add link to reactive architecture\n\nIf you are already familiar with Spring Security or are upgrading, check out xref:whats-new.adoc[what's new in the latest release].\n\nIf you have any questions, there is a wonderful xref:community.adoc[community] that would love to help you!\n"
  },
  {
    "path": "docs/modules/ROOT/pages/migration/index.adoc",
    "content": "[[migration]]\n= Migrating to 7.0\n\nSpring Security 6.5 is the last release in the 6.x generation of Spring Security.\nIt provides strategies for configuring breaking changes to use the 7.0 way before updating.\nWe recommend you use 6.5 and {site-url}/6.5/migration-7/index.html[its preparation steps] to simplify updating to 7.0.\n\nAfter updating to 6.5, follow this guide to perform any remaining migration or cleanup steps.\n\nAnd recall that if you run into trouble, the preparation guide includes opt-out steps to revert to 5.x behaviors.\n\n== Update to Spring Security 7\n\nThe first step is to ensure you are the latest patch release of Spring Boot 4.0.\nNext, you should ensure you are on the latest patch release of Spring Security 7.\nFor directions, on how to update to Spring Security 7 visit the xref:getting-spring-security.adoc[] section of the reference guide.\n\n=== Migrate from Jackson 2 to Jackson 3\n\nThe configuration of Jackson 2 `ObjectMapper` with `SecurityJackson2Modules` should be replaced by the configuration of\nJackson 3 `JsonMapper.Builder` with `SecurityJacksonModules`. See the\nhttps://github.com/FasterXML/jackson/blob/main/jackson3/MIGRATING_TO_JACKSON_3.md[Jackson 3 Migration Guide] for more details.\n\nIt is recommended to replace the configuration of\nindividual modules like `CoreJacksonModule` by the module detection from `SecurityJacksonModules` as it enables\nautomatic inclusion of type information and configure a `PolymorphicTypeValidator` that handles the validation of class\nnames.\n\nThe Jackson 3 support uses a format compatible with the now deprecated Jackson 2 one, so class instances serialized with\nJackson 2 should be deserializable with the Jackson 3 support.\n\n`spring-security-oauth2-authorization-server` now uses Jackson 3 by default. If you want to continue\nto use the deprecated Jackson 2 support, the transitive dependency on Jackson 3 (`tools.jackson.core:jackson-databind`)\nshould be excluded and a dependency on Jackson 2 (`com.fasterxml.jackson.core:jackson-databind`) should be added.\n\n== Perform Application-Specific Steps\n\nNext, there are steps you need to perform based on whether it is a xref:migration/servlet/index.adoc[Servlet] or xref:migration/reactive.adoc[Reactive] application.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/migration/reactive.adoc",
    "content": "= Reactive\n\nIf you have already performed the xref:migration/index.adoc[initial migration steps] for your Reactive application, you're now ready to perform steps specific to Reactive applications.\n\n== Validate `typ` Header with `JwtTypeValidator`\n\nIf when following the 6.5 preparatory steps you set `validateTypes` to `false`, you can now remove it.\nYou can also remove explicitly adding `JwtTypeValidator` to the list of defaults.\n\nFor example, change this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n\tNimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(location)\n        .validateTypes(false) <1>\n        // ... your remaining configuration\n        .build();\n\tjwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\tnew JwtIssuerValidator(location), JwtTypeValidator.jwt())); <2>\n\treturn jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(location)\n        .validateTypes(false) <1>\n        // ... your remaining configuration\n        .build()\n    jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n        JwtIssuerValidator(location), JwtTypeValidator.jwt())) <2>\n    return jwtDecoder\n}\n----\n======\n<1> - Switch off Nimbus verifying the `typ`\n<2> - Add the default `typ` validator\n\nto this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nNimbusReactiveJwtDecoder jwtDecoder() {\n\tNimbusJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(location)\n        // ... your remaining configuration <1>\n        .build();\n\tjwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location)); <2>\n\treturn jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): NimbusReactiveJwtDecoder {\n    val jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(location)\n        // ... your remaining configuration\n        .build()\n    jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location)) <2>\n    return jwtDecoder\n}\n----\n======\n<1> - `validateTypes` now defaults to `false`\n<2> - `JwtTypeValidator#jwt` is added by all `createDefaultXXX` methods\n"
  },
  {
    "path": "docs/modules/ROOT/pages/migration/servlet/authorization.adoc",
    "content": "= Authorization Changes\n\n== If Using Access API, Add `spring-security-access`\n\nSpring Security 7 moves `AccessDecisionManager`, `AccessDecisionVoter`, and the related Access API to a legacy module, `spring-security-access`.\nThe Access API is deprecated in favor of the Authorization API as of Spring Security 5.\n\nYou can add the dependency like other Spring Security dependencies like so:\n\n[tabs]\n======\nMaven::\n+\n[source,xml,role=\"primary\"]\n----\n<dependency>\n    <groupId>org.springframework.security</groupId>\n    <artifactId>spring-security-access</artifactId>\n</dependency>\n----\n\nGradle::\n+\n[source,groovy,role=\"primary\"]\n----\nimplementation('org.springframework.security:spring-security-access')\n----\n======\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/migration/servlet/index.adoc",
    "content": "= Servlet Migrations\n:page-section-summary-toc: 1\n\nIf you have already performed the xref:migration/index.adoc[initial migration steps] for your Servlet application, you're now ready to perform steps specific to Servlet applications.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/migration/servlet/kerberos.adoc",
    "content": "= Kerberos Migrations\n\nFor users leveraging Spring Security's Kerberos support, the Maven  and Gradle Coordinates have been changed since the support was moved from an external module into Spring Security.\nSee the xref:servlet/authentication/kerberos/introduction.adoc[Keberos documentation] for the new Maven and Gradle coordinates.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/migration/servlet/oauth2.adoc",
    "content": "= OAuth 2.0 Migrations\n\n== Validate `typ` Header with `JwtTypeValidator`\n\nIf when following the 6.5 preparatory steps you set `validateTypes` to `false`, you can now remove it.\nYou can also remove explicitly adding `JwtTypeValidator` to the list of defaults.\n\nFor example, change this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)\n        .validateTypes(false) <1>\n        // ... your remaining configuration\n        .build();\n\tjwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\tnew JwtIssuerValidator(location), JwtTypeValidator.jwt())); <2>\n\treturn jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)\n        .validateTypes(false) <1>\n        // ... your remaining configuration\n        .build()\n    jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n        JwtIssuerValidator(location), JwtTypeValidator.jwt())) <2>\n    return jwtDecoder\n}\n----\n======\n<1> - Switch off Nimbus verifying the `typ`\n<2> - Add the default `typ` validator\n\nto this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)\n        // ... your remaining configuration <1>\n        .build();\n\tjwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location)); <2>\n\treturn jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)\n        // ... your remaining configuration\n        .build()\n    jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location)) <2>\n    return jwtDecoder\n}\n----\n======\n<1> - `validateTypes` now defaults to `false`\n<2> - `JwtTypeValidator#jwt` is added by all `createDefaultXXX` methods\n\n== Provide an AuthenticationConverter to BearerTokenAuthenticationFilter\n\nIn Spring Security 7, `BearerTokenAuthenticationFilter#setBearerTokenResolver` and `#setAuthenticaionDetailsSource` are deprecated in favor of configuring those on `BearerTokenAuthenticationConverter`.\n\nThe `oauth2ResourceServer` DSL addresses most use cases and you need to nothing.\n\nIf you are setting a `BearerTokenResolver` or `AuthenticationDetailsSource` directly on `BearerTokenAuthenticationFilter` similar to the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nBearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(authenticationManager);\nfilter.setBearerTokenResolver(myBearerTokenResolver);\nfilter.setAuthenticationDetailsSource(myAuthenticationDetailsSource);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval filter = BearerTokenAuthenticationFilter(authenticationManager)\nfilter.setBearerTokenResolver(myBearerTokenResolver)\nfilter.setAuthenticationDetailsSource(myAuthenticationDetailsSource)\n----\n======\n\nyou are encouraged to use `BearerTokenAuthenticationConverter` to specify both:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nBearerTokenAuthenticationConverter authenticationConverter =\n    new BearerTokenAuthenticationConverter();\nauthenticationConverter.setBearerTokenResolver(myBearerTokenResolver);\nauthenticationConverter.setAuthenticationDetailsSource(myAuthenticationDetailsSource);\nBearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(authenticationManager, authenicationConverter);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval authenticationConverter = BearerTokenAuthenticationConverter()\nauthenticationConverter.setBearerTokenResolver(myBearerTokenResolver)\nauthenticationConverter.setAuthenticationDetailsSource(myAuthenticationDetailsSource)\nval filter = BearerTokenAuthenticationFilter(authenticationManager, authenticationConverter)\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/migration/servlet/saml2.adoc",
    "content": "= SAML 2.0 Migrations\n\n== Expect `<saml2:LogoutResponse>` When `<saml2:LogoutRequest>` Validation Fails\n\nSAML identity providers expect service providers to return an error `<saml2:LogoutResponse>` if it fails to process the `<saml2:LogoutRequest>`.\n\nPast versions of Spring Security returned a 401 in some cases, breaking the chain of logout requests and responses from each relying party.\n\nIn Spring Security 7, this behavior is repaired, and you need do nothing.\n\nHowever, if this gives you trouble, you can revert back to the old behavior by publishing a `Saml2LogoutRequestResolver` that returns `null` when an error `<saml2:LogoutRequest>` is needed.\nYou can create a delegate like this one:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSaml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) {\n    OpenSaml5LogoutResponseResolver delegate = new OpenSaml5LogoutResponseResolver(registrations);\n    return new Saml2LogoutResponseResolver() {\n        @Override\n        public void resolve(HttpServletRequest request, Authentication authentication) {\n            delegate.resolve(request, authentication);\n        }\n\n        @Override\n        public void resolve(HttpServletRequest request, Authentication authentication, Saml2AuthenticationException error) {\n            return null;\n        }\n    };\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?): Saml2LogoutResponseResolver {\n    val delegate = OpenSaml5LogoutResponseResolver(registrations)\n    return object : Saml2LogoutResponseResolver() {\n        override fun resolve(request: HttpServletRequest?, authentication: Authentication?) {\n            delegate.resolve(request, authentication)\n        }\n\n        override fun resolve(request: HttpServletRequest?, authentication: Authentication?, error: Saml2AuthenticationException?) {\n            return null\n        }\n    }\n}\n----\n======\n\n== Favor `Saml2ResponseAuthenticationAccessor` over `Saml2AuthenticatedPrincipal`\n\nSpring Security 7 separates `<saml2:Assertion>` details from the principal.\nThis allows Spring Security to retrieve needed assertion details to perform Single Logout.\n\nThis deprecates `Saml2AuthenticatedPrincipal`.\nYou no longer need to implement it to use `Saml2Authentication`.\n\nInstead, the credential implements `Saml2ResponseAssertionAccessor`, which Spring Security 7 favors when determining the appropriate action based on the authentication.\n\nThis change is made automatically for you when using the defaults.\n\nIf this causes you trouble when upgrading, you can publish a custom `ResponseAuhenticationConverter` to return a `Saml2Authentication` instead of returning a `Saml2AssertionAuthentication` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nOpenSaml5AuthenticationProvider authenticationProvider() {\n\tOpenSaml5AuthenticationProvider authenticationProvider =\n\t\tnew OpenSaml5AuthenticationProvider();\n\tResponseAuthenticationConverter defaults = new ResponseAuthenticationConverter();\n\tauthenticationProvider.setResponseAuthenticationConverter(\n\t\tdefaults.andThen((authentication) -> new Saml2Authentication(\n\t\t\tauthentication.getPrincipal(),\n\t\t\tauthentication.getSaml2Response(),\n\t\t\tauthentication.getAuthorities())));\n\treturn authenticationProvider;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationProvider(): OpenSaml5AuthenticationProvider {\n\tval authenticationProvider = OpenSaml5AuthenticationProvider()\n\tval defaults = ResponseAuthenticationConverter()\n\tauthenticationProvider.setResponseAuthenticationConverter(\n\t\tdefaults.andThen { authentication ->\n\t\t\tSaml2Authentication(authentication.getPrincipal(),\n\t\t\t\tauthentication.getSaml2Response(),\n\t\t\t\tauthentication.getAuthorities())\n\t\t})\n\treturn authenticationProvider\n}\n----\n======\n\nIf you are constructing a `Saml2Authentication` instance yourself, consider changing to `Saml2AssertionAuthentication` to get the same benefit as the current default.\n\n== Do Not Process `<saml2:Response>` GET Requests with `Saml2AuthenticationTokenConverter`\n\nSpring Security does not support processing `<saml2:Response>` payloads over GET as this is not supported by the SAML 2.0 spec.\n\nTo better comply with this, `Saml2AuthenticationTokenConverter` and `OpenSaml5AuthenticationTokenConverter` will not process GET requests by default as of Spring Security 8.\nTo prepare for this, the property `shouldConvertGetRequests` is available.\nTo use it, publish your own converter like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nOpenSaml5AuthenticationTokenConverter authenticationConverter(RelyingPartyRegistrationRepository registrations) {\n\tOpenSaml5AuthenticationTokenConverter authenticationConverter = new OpenSaml5AuthenticationTokenConverter(registrations);\n\tauthenticationConverter.setShouldConvertGetRequests(false);\n\treturn authenticationConverter;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationConverter(val registrations: RelyingPartyRegistrationRepository): Saml2AuthenticationTokenConverter {\n\tval authenticationConverter = Saml2AuthenticationTokenConverter(registrations)\n\tauthenticationConverter.setShouldConvertGetRequests(false)\n\treturn authenticationConverter\n}\n----\n======\n\nIf you must continue using `Saml2AuthenticationTokenConverter` or `OpenSaml5AuthenticationTokenConverter` to process GET requests, you can call `setShouldConvertGetRequests` to `true.`\n"
  },
  {
    "path": "docs/modules/ROOT/pages/migration-8/index.adoc",
    "content": "[[preparing]]\n= Preparing for 8.0\n:page-section-summary-toc: 1\n\nWhile Spring Security 8.0 does not have a release date yet, it is important to start preparing for it now.\n\nThis preparation guide is designed to summarize the biggest changes in Spring Security 8.0 and provide steps to prepare for them.\n\nIt is important to keep your application up to date with the latest Spring Security 7 and Spring Boot 4 releases.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/modules.adoc",
    "content": "// FIXME: This might make sense in Getting Spring Security along with the artifact information\n\n[[modules]]\n= Project Modules and Dependencies\nIn Spring Security 3.0, the codebase was sub-divided into separate jars which more clearly separate different functionality areas and third-party dependencies.\nIf you use Maven to build your project, these are the modules you should add to your `pom.xml`.\nEven if you do not use Maven, we recommend that you consult the `pom.xml` files to get an idea of third-party dependencies and versions.\nAnother good idea is to examine the libraries that are included in the sample applications.\n\nThis section provides a reference of the modules in Spring Security and the additional dependencies that they require in order to function in a running application.\nWe do not include dependencies that are used only when building or testing Spring Security itself.\nNor do we include transitive dependencies that are required by external dependencies.\n\nThe version of Spring required is listed on the project website, so the specific versions are omitted for Spring dependencies in the examples.\nNote that some of the dependencies listed as \"`optional`\" in the examples may still be required for other non-security functionality in a Spring application\nAlso dependencies listed as \"`optional`\" may not actually be marked as such in the project's Maven POM files if they are used in most applications.\nThey are \"`optional`\" only in the sense that you do not need them unless you use the specified functionality.\n\nWhere a module depends on another Spring Security module, the non-optional dependencies of the module it depends on are also assumed to be required and are not listed separately.\n\n\n[[spring-security-core]]\n== Core -- `spring-security-core.jar`\nThis module contains core authentication and access-control classes and interfaces, remoting support, and basic provisioning APIs.\nIt is required by any application that uses Spring Security.\nIt supports standalone applications, remote clients, method (service layer) security, and JDBC user provisioning.\nIt contains the following top-level packages:\n\n* `org.springframework.security.core`\n* `org.springframework.security.access`\n* `org.springframework.security.authentication`\n* `org.springframework.security.provisioning`\n\n.Core Dependencies\n|===\n| Dependency | Version | Description\n\n| ehcache\n| 1.6.2\n| Required if the Ehcache-based user cache implementation is used (optional).\n\n| spring-aop\n|\n| Method security is based on Spring AOP\n\n| spring-beans\n|\n| Required for Spring configuration\n\n| spring-expression\n|\n| Required for expression-based method security (optional)\n\n| spring-jdbc\n|\n| Required if using a database to store user data (optional).\n\n| spring-tx\n|\n| Required if using a database to store user data (optional).\n\n| aspectjrt\n| 1.6.10\n| Required if using AspectJ support (optional).\n\n| jsr250-api\n| 1.0\n| Required if you are using JSR-250 method-security annotations (optional).\n|===\n\n\n[[spring-security-remoting]]\n== Remoting -- `spring-security-remoting.jar`\nThis module provides integration with Spring Remoting.\nYou do not need this unless you are writing a remote client that uses Spring Remoting.\nThe main package is `org.springframework.security.remoting`.\n\n.Remoting Dependencies\n|===\n| Dependency | Version | Description\n\n| spring-security-core\n|\n|\n\n| spring-web\n|\n| Required for clients which use HTTP remoting support.\n|===\n\n[[spring-security-web]]\n== Web -- `spring-security-web.jar`\nThis module contains filters and related web-security infrastructure code.\nIt contains anything with a servlet API dependency.\nYou need it if you require Spring Security web authentication services and URL-based access-control.\nThe main package is `org.springframework.security.web`.\n\n.Web Dependencies\n|===\n| Dependency | Version | Description\n\n| spring-security-core\n|\n|\n\n| spring-web\n|\n| Required for clients that use HTTP remoting support.\n\n| spring-jdbc\n|\n| Required for a JDBC-based persistent remember-me token repository (optional).\n\n| spring-tx\n|\n| Required by remember-me persistent token repository implementations (optional).\n|===\n\n[[spring-security-config]]\n== Config -- `spring-security-config.jar`\nThis module contains the security namespace parsing code and Java configuration code.\nYou need it if you use the Spring Security XML namespace for configuration or Spring Security's Java Configuration support.\nThe main package is `org.springframework.security.config`.\nNone of the classes are intended for direct use in an application.\n\n.Config Dependencies\n|===\n| Dependency | Version | Description\n\n| spring-security-core\n|\n|\n\n| spring-security-web\n|\n| Required if you are using any web-related namespace configuration (optional).\n\n| spring-security-ldap\n|\n| Required if you are using the LDAP namespace options (optional).\n\n| aspectjweaver\n| 1.6.10\n| Required if using the protect-pointcut namespace syntax (optional).\n|===\n\n[[spring-security-ldap]]\n== LDAP -- `spring-security-ldap.jar`\nThis module provides LDAP authentication and provisioning code.\nIt is required if you need to use LDAP authentication or manage LDAP user entries.\nThe top-level package is `org.springframework.security.ldap`.\n\n.LDAP Dependencies\n|===\n| Dependency | Version | Description\n\n| spring-security-core\n|\n|\n\n| spring-ldap-core\n| 1.3.0\n| LDAP support is based on Spring LDAP.\n\n| spring-tx\n|\n| Data exception classes are required.\n\n| com.unboundid:unboundid-ldapsdk\n|\n| Required if using an embedded LDAP server\n\n| ldapsdk\n| 4.1\n| Mozilla LdapSDK.\nUsed for decoding LDAP password policy controls if you are using password-policy functionality with OpenLDAP, for example.\n|===\n\n[[spring-security-oauth2-core]]\n== OAuth 2.0 Core -- `spring-security-oauth2-core.jar`\n`spring-security-oauth2-core.jar` contains core classes and interfaces that provide support for the OAuth 2.0 Authorization Framework and for OpenID Connect Core 1.0.\nIt is required by applications that use OAuth 2.0 or OpenID Connect Core 1.0, such as client, resource server, and authorization server.\nThe top-level package is `org.springframework.security.oauth2.core`.\n\n\n[[spring-security-oauth2-client]]\n== OAuth 2.0 Client -- `spring-security-oauth2-client.jar`\n`spring-security-oauth2-client.jar` contains Spring Security's client support for OAuth 2.0 Authorization Framework and OpenID Connect Core 1.0.\nIt is required by applications that use OAuth 2.0 or OpenID Connect Core 1.0, such as the client, the resource server, and the authorization server.\nThe top-level package is `org.springframework.security.oauth2.core`.\n\n\n[[spring-security-oauth2-jose]]\n== OAuth 2.0 JOSE -- `spring-security-oauth2-jose.jar`\n`spring-security-oauth2-jose.jar` contains Spring Security's support for the JOSE (Javascript Object Signing and Encryption) framework.\nThe JOSE framework is intended to provide a method to securely transfer claims between parties.\nIt is built from a collection of specifications:\n\n* JSON Web Token (JWT)\n* JSON Web Signature (JWS)\n* JSON Web Encryption (JWE)\n* JSON Web Key (JWK)\n\nIt contains the following top-level packages:\n\n* `org.springframework.security.oauth2.jwt`\n* `org.springframework.security.oauth2.jose`\n\n[[spring-security-oauth2-resource-server]]\n== OAuth 2.0 Resource Server -- `spring-security-oauth2-resource-server.jar`\n`spring-security-oauth2-resource-server.jar` contains Spring Security's support for OAuth 2.0 Resource Servers.\nIt is used to protect APIs by using OAuth 2.0 Bearer Tokens.\nThe top-level package is `org.springframework.security.oauth2.server.resource`.\n\n[[spring-security-acl]]\n== ACL -- `spring-security-acl.jar`\nThis module contains a specialized domain object ACL implementation.\nIt is used to apply security to specific domain object instances within your application.\nThe top-level package is `org.springframework.security.acls`.\n\n.ACL Dependencies\n|===\n| Dependency | Version | Description\n\n| spring-security-core\n|\n|\n\n| ehcache\n| 1.6.2\n| Required if the Ehcache-based ACL cache implementation is used (optional if you use your own implementation).\n\n| spring-jdbc\n|\n| Required if you are using the default JDBC-based AclService (optional if you implement your own).\n\n| spring-tx\n|\n| Required if you are using the default JDBC-based AclService (optional if you implement your own).\n|===\n\n[[spring-security-cas]]\n== CAS -- `spring-security-cas.jar`\nThis module contains Spring Security's CAS client integration.\nYou should use it if you want to use Spring Security web authentication with a CAS single sign-on server.\nThe top-level package is `org.springframework.security.cas`.\n\n.CAS Dependencies\n|===\n| Dependency | Version | Description\n\n| spring-security-core\n|\n|\n\n| spring-security-web\n|\n|\n\n| cas-client-core\n| 3.1.12\n| The JA-SIG CAS Client.\nThis is the basis of the Spring Security integration.\n\n| ehcache\n| 1.6.2\n| Required if you are using the Ehcache-based ticket cache (optional).\n|===\n\n\n[[spring-security-test]]\n== Test -- `spring-security-test.jar`\nThis module contains support for testing with Spring Security.\n\n[[spring-security-taglibs]]\n== Taglibs -- `spring-security-taglibs.jar`\nProvides Spring Security's JSP tag implementations.\n\n.Taglib Dependencies\n|===\n| Dependency | Version | Description\n\n| spring-security-core\n|\n|\n\n| spring-security-web\n|\n|\n\n| spring-security-acl\n|\n| Required if you are using the `accesscontrollist` tag or `hasPermission()` expressions with ACLs (optional).\n\n| spring-expression\n|\n| Required if you are using SPEL expressions in your tag access constraints.\n|===\n"
  },
  {
    "path": "docs/modules/ROOT/pages/native-image/index.adoc",
    "content": "= GraalVM Native Image Support\n\nSpring Boot 3.0 provides {spring-boot-reference-url}reference/packaging/native-image/introducing-graalvm-native-images.html[support for generating native images with GraalVM].\nSpring Security integrates with that support and provides its features ready for native images.\n\nHowever, as mentioned in the {spring-boot-reference-url}reference/packaging/native-image/introducing-graalvm-native-images.html#packaging.native-image.introducing-graalvm-native-images.understanding-aot-processing[Spring Boot documentation], there are some cases where we need to provide hints to be used by GraalVM.\n\nThis section aims to provide guidance in some Spring Security features that likely need to have additional hints provided by the application.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/native-image/method-security.adoc",
    "content": "[[native-image-method-security]]\n= Method Security in GraalVM Native Image\n\nAlthough xref:servlet/authorization/method-security.adoc[Method Security] is supported in GraalVM Native Image, there are some use cases that need additional hints provided by the application.\n\n== Using `@PreAuthorize` and `@PostAuthorize` Annotations\n\nUsing `@PreAuthorize` and `@PostAuthorize` annotations require additional hints if you have a custom implementation of `UserDetails` or `Authentication` classes.\n\nLet's take an example where you have a custom implementation of `UserDetails` class as follows and that implementation is returned by your `UserDetailsService`:\n\n.Custom Implementation of UserDetails\n[source,java]\n----\npublic class CustomUserDetails implements UserDetails {\n\n    private final String username;\n\n    private final String password;\n\n    private final Collection<? extends GrantedAuthority> authorities;\n\n    public boolean isAdmin() {\n        return this.authorities.contains(new SimpleGrantedAuthority(\"ROLE_ADMIN\"));\n    }\n\n    // constructors, getters and setters\n}\n----\n\nAnd you want to use the `isAdmin()` method inside a `@PreAuthorize` annotation as follows:\n\n.Using isAdmin() to secure a method\n[source,java]\n----\n@PreAuthorize(\"principal?.isAdmin()\")\npublic String hello() {\n    return \"Hello!\";\n}\n----\n\n[NOTE]\n====\nRemember that you need to xref:servlet/authorization/method-security.adoc#jc-enable-method-security[add `@EnableMethodSecurity` annotation] to your configuration class to enable method security annotations.\n====\n\nIf you {spring-boot-reference-url}how-to/native-image/developing-your-first-application.html[run the native image] of your application with the above configuration, you will get an error similar to the following when trying to invoke the `hello()` method:\n\n[source]\n----\nfailed: java.lang.IllegalArgumentException: Failed to evaluate expression 'principal?.isAdmin()' with root cause\norg.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method isAdmin() cannot be found on type com.mypackage.CustomUserDetails\n----\n\nWhich means that the `isAdmin()` method cannot be found on the `CustomUserDetails` class.\nThis is because Spring Security uses reflection to invoke the `isAdmin()` method and GraalVM Native Image does not support reflection by default.\n\nTo fix this issue, you need to give hints to GraalVM Native Image to allow reflection on the `CustomUserDetails#isAdmin()` method.\nWe can do that by providing a {spring-boot-reference-url}reference/packaging/native-image/advanced-topics.html#packaging.native-image.advanced.custom-hints[custom hint].\nIn this example we are going to use {spring-framework-reference-url}core/aot.html#aot.hints.register-reflection[the `@RegisterReflectionForBinding` annotation].\n\n[NOTE]\n====\nYou might need to register all your classes that you want to use in your `@PreAuthorize` and `@PostAuthorize` annotations.\n====\n\n.Using @RegisterReflectionForBinding\n[source,java]\n----\n@Configuration\n@RegisterReflectionForBinding(CustomUserDetails.class)\npublic class MyConfiguration {\n    //...\n}\n----\n\nAnd that's it, now you can run the native image of your application and it should work as expected.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/prerequisites.adoc",
    "content": "[[prerequisites]]\n= Prerequisites\n\nSpring Security requires a Java 17 or higher Runtime Environment.\n\nAs Spring Security aims to operate in a self-contained manner, you do not need to place any special configuration files in your Java Runtime Environment.\nIn particular, you do not need to configure a special Java Authentication and Authorization Service (JAAS) policy file or place Spring Security in common classpath locations.\n\nSimilarly, if you use an EJB Container or Servlet Container, you do not need to put any special configuration files anywhere nor include Spring Security in a server classloader.\nAll the required files are contained within your application.\n\nThis design offers maximum deployment time flexibility, as you can copy your target artifact (be it a JAR, WAR, or EAR) from one system to another and it immediately works.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/authentication/concurrent-sessions-control.adoc",
    "content": "[[reactive-concurrent-sessions-control]]\n= Concurrent Sessions Control\n\nSimilar to xref:servlet/authentication/session-management.adoc#ns-concurrent-sessions[Servlet's Concurrent Sessions Control], Spring Security also provides support to limit the number of concurrent sessions a user can have in a Reactive application.\n\nWhen you set up Concurrent Sessions Control in Spring Security, it monitors authentications carried out through Form Login, xref:reactive/oauth2/login/index.adoc[OAuth 2.0 Login], and HTTP Basic authentication by hooking into the way those authentication mechanisms handle authentication success.\nMore specifically, the session management DSL will add the javadoc:org.springframework.security.web.server.authentication.ConcurrentSessionControlServerAuthenticationSuccessHandler[] and the javadoc:org.springframework.security.web.server.authentication.RegisterSessionServerAuthenticationSuccessHandler[] to the list of `ServerAuthenticationSuccessHandler` used by the authentication filter.\n\nThe following sections contains examples of how to configure Concurrent Sessions Control.\n\n* <<reactive-concurrent-sessions-control-limit,I want to limit the number of concurrent sessions a user can have>>\n* <<concurrent-sessions-control-custom-strategy,I want to customize the strategy used when the maximum number of sessions is exceeded>>\n* <<reactive-concurrent-sessions-control-specify-session-registry,I want to know how to specify a `ReactiveSessionRegistry`>>\n* <<concurrent-sessions-control-sample,I want to see a sample application that uses Concurrent Sessions Control>>\n* <<disabling-for-authentication-filters,I want to know how to disable it for some authentication filter>>\n\n[[reactive-concurrent-sessions-control-limit]]\n== Limiting Concurrent Sessions\n\nBy default, Spring Security will allow any number of concurrent sessions for a user.\nTo limit the number of concurrent sessions, you can use the `maximumSessions` DSL method:\n\n.Configuring one session for any user\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n    http\n        // ...\n        .sessionManagement((sessions) -> sessions\n            .concurrentSessions((concurrency) -> concurrency\n                .maximumSessions(SessionLimit.of(1))\n            )\n        );\n    return http.build();\n}\n\n@Bean\nReactiveSessionRegistry reactiveSessionRegistry() {\n    return new InMemoryReactiveSessionRegistry();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = SessionLimit.of(1)\n            }\n        }\n    }\n}\n@Bean\nopen fun reactiveSessionRegistry(): ReactiveSessionRegistry {\n    return InMemoryReactiveSessionRegistry()\n}\n----\n======\n\nThe above configuration allows one session for any user.\nSimilarly, you can also allow unlimited sessions by using the `SessionLimit#UNLIMITED` constant:\n\n.Configuring unlimited sessions\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n    http\n        // ...\n        .sessionManagement((sessions) -> sessions\n            .concurrentSessions((concurrency) -> concurrency\n                .maximumSessions(SessionLimit.UNLIMITED))\n        );\n    return http.build();\n}\n\n@Bean\nReactiveSessionRegistry reactiveSessionRegistry() {\n    return new InMemoryReactiveSessionRegistry();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = SessionLimit.UNLIMITED\n            }\n        }\n    }\n}\n@Bean\nopen fun reactiveSessionRegistry(webSessionManager: WebSessionManager): ReactiveSessionRegistry {\n    return InMemoryReactiveSessionRegistry()\n}\n----\n======\n\nSince the `maximumSessions` method accepts a `SessionLimit` interface, which in turn extends `Function<Authentication, Mono<Integer>>`, you can have a more complex logic to determine the maximum number of sessions based on the user's authentication:\n\n.Configuring maximumSessions based on `Authentication`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n    http\n        // ...\n        .sessionManagement((sessions) -> sessions\n            .concurrentSessions((concurrency) -> concurrency\n                .maximumSessions(maxSessions()))\n        );\n    return http.build();\n}\n\nprivate SessionLimit maxSessions() {\n    return (authentication) -> {\n        if (authentication.getAuthorities().contains(new SimpleGrantedAuthority(\"ROLE_UNLIMITED_SESSIONS\"))) {\n            return Mono.empty(); // allow unlimited sessions for users with ROLE_UNLIMITED_SESSIONS\n        }\n        if (authentication.getAuthorities().contains(new SimpleGrantedAuthority(\"ROLE_ADMIN\"))) {\n            return Mono.just(2); // allow two sessions for admins\n        }\n        return Mono.just(1); // allow one session for every other user\n    };\n}\n\n@Bean\nReactiveSessionRegistry reactiveSessionRegistry() {\n    return new InMemoryReactiveSessionRegistry();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = maxSessions()\n            }\n        }\n    }\n}\n\nfun maxSessions(): SessionLimit {\n    return { authentication ->\n        if (authentication.authorities.contains(SimpleGrantedAuthority(\"ROLE_UNLIMITED_SESSIONS\"))) Mono.empty\n        if (authentication.authorities.contains(SimpleGrantedAuthority(\"ROLE_ADMIN\"))) Mono.just(2)\n        Mono.just(1)\n    }\n}\n\n@Bean\nopen fun reactiveSessionRegistry(): ReactiveSessionRegistry {\n    return InMemoryReactiveSessionRegistry()\n}\n----\n======\n\nWhen the maximum number of sessions is exceeded, by default, the least recently used session(s) will be expired.\nIf you want to change that behavior, you can <<concurrent-sessions-control-custom-strategy,customize the strategy used when the maximum number of sessions is exceeded>>.\n\n[IMPORTANT]\n====\nThe Concurrent Session Management is not aware if there is another session in some Identity Provider that you might use via xref:reactive/oauth2/login/index.adoc[OAuth 2 Login] for example.\nIf you also need to invalidate the session against the Identity Provider you must <<concurrent-sessions-control-custom-strategy,include your own implementation of `ServerMaximumSessionsExceededHandler`>>.\n====\n\n[[concurrent-sessions-control-custom-strategy]]\n== Handling Maximum Number of Sessions Exceeded\n\nBy default, when the maximum number of sessions is exceeded, the least recently used session(s) will be expired by using the javadoc:org.springframework.security.web.server.authentication.InvalidateLeastUsedServerMaximumSessionsExceededHandler[].\nSpring Security also provides another implementation that prevents the user from creating new sessions by using the javadoc:org.springframework.security.web.server.authentication.PreventLoginServerMaximumSessionsExceededHandler[].\nIf you want to use your own strategy, you can provide a different implementation of javadoc:org.springframework.security.web.server.authentication.ServerMaximumSessionsExceededHandler[].\n\n.Configuring maximumSessionsExceededHandler\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n    http\n        // ...\n        .sessionManagement((sessions) -> sessions\n            .concurrentSessions((concurrency) -> concurrency\n                .maximumSessions(SessionLimit.of(1))\n                .maximumSessionsExceededHandler(new PreventLoginMaximumSessionsExceededHandler())\n            )\n        );\n    return http.build();\n}\n\n@Bean\nReactiveSessionRegistry reactiveSessionRegistry() {\n    return new InMemoryReactiveSessionRegistry();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = SessionLimit.of(1)\n                maximumSessionsExceededHandler = PreventLoginMaximumSessionsExceededHandler()\n            }\n        }\n    }\n}\n\n@Bean\nopen fun reactiveSessionRegistry(): ReactiveSessionRegistry {\n    return InMemoryReactiveSessionRegistry()\n}\n----\n======\n\n[[reactive-concurrent-sessions-control-specify-session-registry]]\n== Specifying a `ReactiveSessionRegistry`\n\nIn order to keep track of the user's sessions, Spring Security uses a javadoc:org.springframework.security.core.session.ReactiveSessionRegistry[], and, every time a user logs in, their session information is saved.\n\nSpring Security ships with javadoc:org.springframework.security.core.session.InMemoryReactiveSessionRegistry[] implementation of `ReactiveSessionRegistry`.\n\nTo specify a `ReactiveSessionRegistry` implementation you can either declare it as a bean:\n\n.ReactiveSessionRegistry as a Bean\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n    http\n        // ...\n        .sessionManagement((sessions) -> sessions\n            .concurrentSessions((concurrency) -> concurrency\n                .maximumSessions(SessionLimit.of(1))\n            )\n        );\n    return http.build();\n}\n\n@Bean\nReactiveSessionRegistry reactiveSessionRegistry() {\n    return new MyReactiveSessionRegistry();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = SessionLimit.of(1)\n            }\n        }\n    }\n}\n\n@Bean\nopen fun reactiveSessionRegistry(): ReactiveSessionRegistry {\n    return MyReactiveSessionRegistry()\n}\n----\n======\n\nor you can use the `sessionRegistry` DSL method:\n\n.ReactiveSessionRegistry using sessionRegistry DSL method\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n    http\n        // ...\n        .sessionManagement((sessions) -> sessions\n            .concurrentSessions((concurrency) -> concurrency\n                .maximumSessions(SessionLimit.of(1))\n                .sessionRegistry(new MyReactiveSessionRegistry())\n            )\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = SessionLimit.of(1)\n                sessionRegistry = MyReactiveSessionRegistry()\n            }\n        }\n    }\n}\n----\n======\n\n[[reactive-concurrent-sessions-control-manually-invalidating-sessions]]\n== Invalidating Registered User's Sessions\n\nAt times, it is handy to be able to invalidate all or some of a user's sessions.\nFor example, when a user changes their password, you may want to invalidate all of their sessions so that they are forced to log in again.\nTo do that, you can use the `ReactiveSessionRegistry` bean to retrieve all the user's sessions, invalidate them, and them remove them from the `WebSessionStore`:\n\n.Using ReactiveSessionRegistry to invalidate sessions manually\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class SessionControl {\n    private final ReactiveSessionRegistry reactiveSessionRegistry;\n\n    private final WebSessionStore webSessionStore;\n\n    public Mono<Void> invalidateSessions(String username) {\n        return this.reactiveSessionRegistry.getAllSessions(username)\n            .flatMap((session) -> session.invalidate().thenReturn(session))\n            .flatMap((session) -> this.webSessionStore.removeSession(session.getSessionId()))\n            .then();\n    }\n}\n----\n======\n\n[[disabling-for-authentication-filters]]\n== Disabling It for Some Authentication Filters\n\nBy default, Concurrent Sessions Control will be configured automatically for Form Login, OAuth 2.0 Login, and HTTP Basic authentication as long as they do not specify an `ServerAuthenticationSuccessHandler` themselves.\nFor example, the following configuration will disable Concurrent Sessions Control for Form Login:\n\n.Disabling Concurrent Sessions Control for Form Login\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n    http\n        // ...\n        .formLogin((login) -> login\n            .authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler(\"/\"))\n        )\n        .sessionManagement((sessions) -> sessions\n            .concurrentSessions((concurrency) -> concurrency\n                .maximumSessions(SessionLimit.of(1))\n            )\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        formLogin {\n            authenticationSuccessHandler = RedirectServerAuthenticationSuccessHandler(\"/\")\n        }\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = SessionLimit.of(1)\n            }\n        }\n    }\n}\n----\n======\n\n=== Adding Additional Success Handlers Without Disabling Concurrent Sessions Control\n\nYou can also include additional `ServerAuthenticationSuccessHandler` instances to the list of handlers used by the authentication filter without disabling Concurrent Sessions Control.\nTo do that you can use the `authenticationSuccessHandler(Consumer<List<ServerAuthenticationSuccessHandler>>)` method:\n\n.Adding additional handlers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n    http\n        // ...\n        .formLogin((login) -> login\n            .authenticationSuccessHandler((handlers) -> handlers.add(new MyAuthenticationSuccessHandler()))\n        )\n        .sessionManagement((sessions) -> sessions\n            .concurrentSessions((concurrency) -> concurrency\n                .maximumSessions(SessionLimit.of(1))\n            )\n        );\n    return http.build();\n}\n----\n======\n\n[[concurrent-sessions-control-sample]]\n== Checking a Sample Application\n\nYou can check the {gh-samples-url}/reactive/webflux/java/session-management/maximum-sessions[sample application here].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/authentication/index.adoc",
    "content": "[[webflux-authentication]]\n= Authentication\n:page-section-summary-toc: 1"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/authentication/logout.adoc",
    "content": "[[reactive-logout]]\n= Logout\n\nSpring Security provides a logout endpoint by default.\nOnce logged in, you can `GET /logout` to see a default logout confirmation page, or you can `POST /logout` to initiate logout.\nThis will:\n\n- clear the `ServerCsrfTokenRepository`, `ServerSecurityContextRepository`, and\n- redirect back to the login page\n\nOften, you will want to also invalidate the session on logout.\nTo achieve this, you can add the `WebSessionServerLogoutHandler` to your logout configuration, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain http(ServerHttpSecurity http) throws Exception {\n    DelegatingServerLogoutHandler logoutHandler = new DelegatingServerLogoutHandler(\n            new SecurityContextServerLogoutHandler(), new WebSessionServerLogoutHandler()\n    );\n\n    http\n        .authorizeExchange((authorize) -> authorize.anyExchange().authenticated())\n        .logout((logout) -> logout.logoutHandler(logoutHandler));\n\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun http(http: ServerHttpSecurity): SecurityWebFilterChain {\n    val customLogoutHandler = DelegatingServerLogoutHandler(\n        SecurityContextServerLogoutHandler(), WebSessionServerLogoutHandler()\n    )\n\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        logout {\n            logoutHandler = customLogoutHandler\n        }\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/authentication/onetimetoken.adoc",
    "content": "[[one-time-token-login]]\n= One-Time Token Login\n\nSpring Security offers support for One-Time Token (OTT) authentication via the `oneTimeTokenLogin()` DSL.\nBefore diving into implementation details, it's important to clarify the scope of the OTT feature within the framework, highlighting what is supported and what isn't.\n\n== Understanding One-Time Tokens vs. One-Time Passwords\n\nIt's common to confuse One-Time Tokens (OTT) with https://en.wikipedia.org/wiki/One-time_password[One-Time Passwords] (OTP), but in Spring Security, these concepts differ in several key ways.\nFor clarity, we'll assume OTP refers to https://en.wikipedia.org/wiki/Time-based_one-time_password[TOTP] (Time-Based One-Time Password) or https://en.wikipedia.org/wiki/HMAC-based_one-time_password[HOTP] (HMAC-Based One-Time Password).\n\n=== Setup Requirements\n\n- OTT: No initial setup is required. The user doesn't need to configure anything in advance.\n- OTP: Typically requires setup, such as generating and sharing a secret key with an external tool to produce the one-time passwords.\n\n=== Token Delivery\n\n- OTT: Usually a custom javadoc:org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler[] must be implemented, responsible for delivering the token to the end user.\n- OTP: The token is often generated by an external tool, so there's no need to send it to the user via the application.\n\n=== Token Generation\n\n- OTT: The javadoc:org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService#generate(org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest)[] method requires a javadoc:org.springframework.security.authentication.ott.OneTimeToken[], wrapped in Mono, to be returned, emphasizing server-side generation.\n- OTP: The token is not necessarily generated on the server side, it's often created by the client using the shared secret.\n\nIn summary, One-Time Tokens (OTT) provide a way to authenticate users without additional account setup, differentiating them from One-Time Passwords (OTP), which typically involve a more complex setup process and rely on external tools for token generation.\n\nThe One-Time Token Login works in two major steps.\n\n1. User requests a token by submitting their user identifier, usually the username, and the token is delivered to them, often as a Magic Link, via e-mail, SMS, etc.\n2. User submits the token to the one-time token login endpoint and, if valid, the user gets logged in.\n\nIn the following sections we will explore how to configure OTT Login for your needs.\n\n- <<default-pages,Understanding the integration with the default generated login page>>\n- <<sending-token-to-user,Sending the token to the user>>\n- <<changing-submit-page-url,Configuring the One-Time Token submit page>>\n- <<changing-generate-url,Changing the One-Time Token generate URL>>\n- <<customize-generate-consume-token,Customize how to generate and consume tokens>>\n\n[[default-pages]]\n== Default Login Page and Default One-Time Token Submit Page\n\nThe `oneTimeTokenLogin()` DSL can be used in conjunction with `formLogin()`, which will produce an additional One-Time Token Request Form in the xref:servlet/authentication/passwords/form.adoc[default generated login page].\nIt will also set up the javadoc:org.springframework.security.web.server.ui.OneTimeTokenSubmitPageGeneratingWebFilter[] to generate a default One-Time Token submit page.\n\n[[sending-token-to-user]]\n== Sending the Token to the User\n\nIt is not possible for Spring Security to reasonably determine the way the token should be delivered to your users.\nTherefore, a custom javadoc:org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler[] must be provided to deliver the token to the user based on your needs.\nOne of the most common delivery strategies is a Magic Link, via e-mail, SMS, etc.\nIn the following example, we are going to create a magic link and sent it to the user's email.\n\n.One-Time Token Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin(Customizer.withDefaults());\n        return http.build();\n    }\n\n}\n\nimport org.springframework.mail.SimpleMailMessage;\nimport org.springframework.mail.javamail.JavaMailSender;\n\n@Component <1>\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {\n\n    private final MailSender mailSender;\n\n    private final ServerOneTimeTokenGenerationSuccessHandler redirectHandler = new ServerRedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\n    // constructor omitted\n\n    @Override\n    public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {\n        return Mono.just(exchange.getRequest())\n\t\t\t\t.map((request) ->\n\t\t\t\t\tUriComponentsBuilder.fromUri(request.getURI())\n\t\t\t\t\t\t.replacePath(request.getPath().contextPath().value())\n\t\t\t\t\t\t.replaceQuery(null)\n\t\t\t\t\t\t.fragment(null)\n\t\t\t\t\t\t.path(\"/login/ott\")\n\t\t\t\t\t\t.queryParam(\"token\", oneTimeToken.getTokenValue())\n\t\t\t\t\t\t.toUriString() <2>\n\t\t\t\t\t)\n\t\t\t.flatMap((uri) -> this.mailSender.send(getUserEmail(oneTimeToken.getUsername()), <3>\n\t\t\t\t\t\"Use the following link to sign in into the application: \" + magicLink)) <4>\n\t\t\t.then(this.redirectHandler.handle(exchange, oneTimeToken)); <5>\n    }\n\n    private String getUserEmail() {\n        // ...\n    }\n\n}\n\n@Controller\nclass PageController {\n\n    @GetMapping(\"/ott/sent\")\n    String ottSent() {\n        return \"my-template\";\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n         open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n             return http {\n                authorizeExchange {\n                    authorize(anyExchange, authenticated)\n                 }\n                 oneTimeTokenLogin { }\n             }\n         }\n\n}\n\n@Component (1)\nclass MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {\n\n    private val redirectStrategy: ServerRedirectStrategy = DefaultServerRedirectStrategy()\n\n    override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {\n        val builder = UriComponentsBuilder.fromUri(exchange.request.uri)\n            .replacePath(null)\n            .replaceQuery(null)\n            .fragment(null)\n            .path(\"/login/ott\")\n            .queryParam(\"token\", oneTimeToken.getTokenValue()) (2)\n        val magicLink = builder.toUriString()\n        builder.replacePath(null)\n            .replaceQuery(null)\n            .path(\"/ott/sent\")\n        val redirectLink = builder.toUriString()\n        return this.mailSender.send(\n            getUserEmail(oneTimeToken.getUsername()), (3)\n            \"Use the following link to sign in into the application: $magicLink\") (4)\n        .then(this.redirectStrategy.sendRedirect(exchange, URI.create(redirectLink))) (5)\n    }\n\n        private String getUserEmail() {\n            // ...\n        }\n}\n\n@Controller\nclass PageController {\n\n    @GetMapping(\"/ott/sent\")\n    fun ottSent(): String {\n        return \"my-template\"\n    }\n}\n\n----\n======\n\n<1> Make the `MagicLinkOneTimeTokenGenerationSuccessHandler` a Spring bean\n<2> Create a login processing URL with the `token` as a query param\n<3> Retrieve the user's email based on the username\n<4> Use the `MailSender` API to send the email to the user with the magic link\n<5> Use the `ServerRedirectStrategy` to perform a redirect to your desired URL\n\nThe email content will look similar to:\n\n> Use the following link to sign in into the application: \\http://localhost:8080/login/ott?token=a830c444-29d8-4d98-9b46-6aba7b22fe5b\n\nThe default submit page will detect that the URL has the `token` query param and will automatically fill the form field with the token value.\n\n[[changing-generate-url]]\n== Changing the One-Time Token Generate URL\n\nBy default, the javadoc:org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter[] listens to `POST /ott/generate` requests.\nThat URL can be changed by using the `generateTokenUrl(String)` DSL method:\n\n.Changing the Generate URL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin((ott) -> ott\n                .generateTokenUrl(\"/ott/my-generate-url\")\n            );\n        return http.build();\n    }\n\n}\n\n@Component\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n         open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n             return http {\n                 // ...\n                 formLogin { }\n                 oneTimeTokenLogin {\n                    generateTokenUrl = \"/ott/my-generate-url\"\n                 }\n             }\n         }\n\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n\n----\n======\n\n[[changing-submit-page-url]]\n== Changing the Default Submit Page URL\n\nThe default One-Time Token submit page is generated by the javadoc:org.springframework.security.web.server.ui.OneTimeTokenSubmitPageGeneratingWebFilter[] and listens to `GET /login/ott`.\nThe URL can also be changed, like so:\n\n.Configuring the Default Submit Page URL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin((ott) -> ott\n                .submitPageUrl(\"/ott/submit\")\n            );\n        return http.build();\n    }\n\n}\n\n@Component\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n         open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n             return http {\n                 // ...\n                 formLogin { }\n                 oneTimeTokenLogin {\n                    submitPageUrl = \"/ott/submit\"\n                 }\n             }\n         }\n\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n\n----\n======\n\n[[disabling-default-submit-page]]\n== Disabling the Default Submit Page\n\nIf you want to use your own One-Time Token submit page, you can disable the default page and then provide your own endpoint.\n\n.Disabling the Default Submit Page\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n        http\n            .authorizeExchange((authorize) -> authorize\n                .pathMatchers(\"/my-ott-submit\").permitAll()\n                .anyExchange().authenticated()\n            )\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin((ott) -> ott\n                .showDefaultSubmitPage(false)\n            );\n        return http.build();\n    }\n\n}\n\n@Controller\npublic class MyController {\n\n    @GetMapping(\"/my-ott-submit\")\n    public String ottSubmitPage() {\n        return \"my-ott-submit\";\n    }\n\n}\n\n@Component\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n         open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n             return http {\n                authorizeExchange {\n                    authorize(pathMatchers(\"/my-ott-submit\"), permitAll)\n                    authorize(anyExchange, authenticated)\n                 }\n                 .formLogin { }\n                 oneTimeTokenLogin {\n                    showDefaultSubmitPage = false\n                 }\n             }\n         }\n\n}\n\n@Controller\nclass MyController {\n\n    @GetMapping(\"/my-ott-submit\")\n    fun ottSubmitPage(): String {\n        return \"my-ott-submit\"\n    }\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n\n----\n======\n\n[[customize-generate-consume-token]]\n== Customize How to Generate and Consume One-Time Tokens\n\nThe interface that define the common operations for generating and consuming one-time tokens is the javadoc:org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService[].\nSpring Security uses the javadoc:org.springframework.security.authentication.ott.reactive.InMemoryReactiveOneTimeTokenService[] as the default implementation of that interface, if none is provided.\n\nSome of the most common reasons to customize the `ReactiveOneTimeTokenService` are, but not limited to:\n\n- Changing the one-time token expire time\n- Storing more information from the generate token request\n- Changing how the token value is created\n- Additional validation when consuming a one-time token\n\nThere are two options to customize the `ReactiveOneTimeTokenService`.\nOne option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL:\n\n.Passing the ReactiveOneTimeTokenService as a Bean\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin(Customizer.withDefaults());\n        return http.build();\n    }\n\n    @Bean\n    public ReactiveOneTimeTokenService oneTimeTokenService() {\n        return new MyCustomReactiveOneTimeTokenService();\n    }\n\n}\n\n@Component\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n         open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n             return http {\n                 //..\n                 .formLogin { }\n                 oneTimeTokenLogin { }\n             }\n         }\n\n         @Bean\n         open fun oneTimeTokenService():ReactiveOneTimeTokenService {\n             return MyCustomReactiveOneTimeTokenService();\n         }\n\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n\n----\n======\n\nThe second option is to pass the `ReactiveOneTimeTokenService` instance to the DSL, which is useful if there are multiple ``SecurityWebFilterChain``s and a different ``ReactiveOneTimeTokenService``s is needed for each of them.\n\n.Passing the ReactiveOneTimeTokenService using the DSL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin((ott) -> ott\n                .oneTimeTokenService(new MyCustomReactiveOneTimeTokenService())\n            );\n        return http.build();\n    }\n\n}\n\n@Component\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n         open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n             return http {\n                 //..\n                 .formLogin { }\n                 oneTimeTokenLogin {\n                    oneTimeTokenService = MyCustomReactiveOneTimeTokenService()\n                 }\n             }\n         }\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n\n----\n======\n\n[[customize-generate-token-request]]\n== Customize GenerateOneTimeTokenRequest Instance\nThere are a number of reasons that you may want to adjust an GenerateOneTimeTokenRequest. For example, you may want expiresIn to be set to 10 mins, which Spring Security sets to 5 mins by default.\n\nYou can customize elements of GenerateOneTimeTokenRequest by publishing an ServerGenerateOneTimeTokenRequestResolver as a @Bean, like so:\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nServerGenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {\n    DefaultServerGenerateOneTimeTokenRequestResolver resolver = new DefaultServerGenerateOneTimeTokenRequestResolver();\n    resolver.setExpiresIn(Duration.ofSeconds(600));\n    return resolver;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun generateOneTimeTokenRequestResolver() : ServerGenerateOneTimeTokenRequestResolver {\n    return DefaultServerGenerateOneTimeTokenRequestResolver().apply {\n        this.setExpiresIn(Duration.ofMinutes(10))\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/authentication/x509.adoc",
    "content": "[[reactive-x509]]\n= Reactive X.509 Authentication\n\nSimilar to xref:servlet/authentication/x509.adoc#servlet-x509[Servlet X.509 authentication], the reactive x509 authentication filter allows extracting an authentication token from a certificate provided by a client.\n\nThe following example shows a reactive x509 security configuration:\n\ninclude-code::./DefaultX509Configuration[tag=springSecurity,indent=0]\n\nIn the preceding configuration, when neither `principalExtractor` nor `authenticationManager` is provided, defaults are used.\nThe default principal extractor is `SubjectX500PrincipalExtractor`, which extracts the CN (common name) field from a certificate provided by a client.\nThe default authentication manager is `ReactivePreAuthenticatedAuthenticationManager`, which performs user account validation, checking that a user account with a name extracted by `principalExtractor` exists and that it is not locked, disabled, or expired.\n\nThe following example demonstrates how these defaults can be overridden:\n\ninclude-code::./CustomX509Configuration[tag=springSecurity,indent=0]\n\nIn the previous example, a username is extracted from the `emailAddress` field of a client certificate instead of CN, and account lookup uses a custom `ReactiveAuthenticationManager` instance.\n\nFor an example of configuring Netty and `WebClient` or `curl` command-line tool to use mutual TLS and enable X.509 authentication, see https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/x509.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/authorization/authorize-http-requests.adoc",
    "content": "[[servlet-authorization-authorizationfilter]]\n= Authorize ServerHttpRequest\n\nSpring Security provides support for authorizing the incoming HTTP requests.\nBy default, Spring Security’s authorization will require all requests to be authenticated.\nThe explicit configuration looks like:\n\n.All Requests Require Authenticated User\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n    http\n        .authorizeExchange((authorize) -> authorize\n            .anyExchange().authenticated()\n        )\n        .httpBasic(withDefaults())\n        .formLogin(withDefaults());\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        formLogin { }\n        httpBasic { }\n    }\n}\n----\n======\n\n\nWe can configure Spring Security to have different rules by adding more rules in order of precedence.\n\n.Multiple Authorize Requests Rules\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.hasRole;\n// ...\n@Bean\nSecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {\n\t// @formatter:off\n\thttp\n\t\t// ...\n\t\t.authorizeExchange((authorize) -> authorize                          // <1>\n\t\t\t.pathMatchers(\"/resources/**\", \"/signup\", \"/about\").permitAll()  // <2>\n\t\t\t.pathMatchers(\"/admin/**\").hasRole(\"ADMIN\")                      // <3>\n\t\t\t.pathMatchers(\"/db/**\").access((authentication, context) ->      // <4>\n\t\t\t\thasRole(\"ADMIN\").check(authentication, context)\n\t\t\t\t\t.filter(decision -> !decision.isGranted())\n\t\t\t\t\t.switchIfEmpty(hasRole(\"DBA\").check(authentication, context))\n\t\t\t)\n\t\t\t.anyExchange().denyAll()                                         // <5>\n\t\t);\n\t// @formatter:on\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {                                                           // <1>\n            authorize(pathMatchers(\"/resources/**\", \"/signup\", \"/about\"), permitAll)  // <2>\n            authorize(\"/admin/**\", hasRole(\"ADMIN\"))                                  // <3>\n            authorize(\"/db/**\", { authentication, context ->                          // <4>\n                hasRole(\"ADMIN\").check(authentication, context)\n                    .filter({ decision -> !decision.isGranted() })\n                    .switchIfEmpty(hasRole(\"DBA\").check(authentication, context))\n            })\n            authorize(anyExchange, denyAll)                                           // <5>\n        }\n        // ...\n    }\n}\n----\n======\n\n<1> There are multiple authorization rules specified.\nEach rule is considered in the order they were declared.\n<2> We specified multiple URL patterns that any user can access.\nSpecifically, any user can access a request if the URL starts with \"/resources/\", equals \"/signup\", or equals \"/about\".\n<3> Any URL that starts with \"/admin/\" will be restricted to users who have the authority \"ROLE_ADMIN\".\nYou will notice that since we are invoking the `hasRole` method we do not need to specify the \"ROLE_\" prefix.\n<4> Any URL that starts with \"/db/\" requires the user to have both \"ROLE_ADMIN\" and \"ROLE_DBA\".\nThis demonstrates the flexibility of providing a custom `ReactiveAuthorizationManager` allowing us to implement arbitrary authorization logic.\nFor simplicity, the sample uses a lambda and delegate to the existing `AuthorityReactiveAuthorizationManager.hasRole` implementation.\nHowever, in a real world situation applications would likely implement the logic in a proper class implementing `ReactiveAuthorizationManager`.\n<5> Any URL that has not already been matched on is denied access.\nThis is a good strategy if you do not want to accidentally forget to update your authorization rules.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/authorization/method.adoc",
    "content": "[[jc-erms]]\n= EnableReactiveMethodSecurity\n\nSpring Security supports method security by using https://projectreactor.io/docs/core/release/reference/#context[Reactor's Context], which is set up by `ReactiveSecurityContextHolder`.\nThe following example shows how to retrieve the currently logged in user's message:\n\n[NOTE]\n====\nFor this example to work, the return type of the method must be a `org.reactivestreams.Publisher` (that is, a `Mono` or a `Flux`).\nThis is necessary to integrate with Reactor's `Context`.\n====\n\n[[jc-enable-reactive-method-security-authorization-manager]]\n== EnableReactiveMethodSecurity with AuthorizationManager\n\nIn Spring Security 5.8, we can enable annotation-based security using the `@EnableReactiveMethodSecurity(useAuthorizationManager=true)` annotation on any `@Configuration` instance.\n\nThis improves upon `@EnableReactiveMethodSecurity` in a number of ways. `@EnableReactiveMethodSecurity(useAuthorizationManager=true)`:\n\n1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters.\nThis simplifies reuse and customization.\n2. Supports reactive return types including Kotlin coroutines.\n3. Is built using native Spring AOP, removing abstractions and allowing you to use Spring AOP building blocks to customize\n4. Checks for conflicting annotations to ensure an unambiguous security configuration\n5. Complies with JSR-250\n\n[NOTE]\n====\nFor earlier versions, please read about similar support with <<jc-enable-reactive-method-security, @EnableReactiveMethodSecurity>>.\n====\n\nFor example, the following would enable Spring Security's `@PreAuthorize` annotation:\n\n.Method Security Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableReactiveMethodSecurity(useAuthorizationManager=true)\npublic class MethodSecurityConfig {\n\t// ...\n}\n----\n======\n\nAdding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.\nSpring Security's native annotation support defines a set of attributes for the method.\nThese will be passed to the various method interceptors, like `AuthorizationManagerBeforeReactiveMethodInterceptor`, for it to make the actual decision:\n\n.Method Security Annotation Usage\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic interface BankService {\n\t@PreAuthorize(\"hasRole('USER')\")\n\tMono<Account> readAccount(Long id);\n\n\t@PreAuthorize(\"hasRole('USER')\")\n\tFlux<Account> findAccounts();\n\n\t@PreAuthorize(\"@func.apply(#account)\")\n\tMono<Account> post(Account account, Double amount);\n}\n----\n======\n\nIn this case `hasRole` refers to the method found in `SecurityExpressionRoot` that gets invoked by the SpEL evaluation engine.\n\n`@bean` refers to a custom component you have defined, where `apply` can return `Boolean` or `Mono<Boolean>` to indicate the authorization decision.\nA bean like that might look something like this:\n\n.Method Security Reactive Boolean Expression\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic Function<Account, Mono<Boolean>> func() {\n    return (account) -> Mono.defer(() -> Mono.just(account.getId().equals(12)));\n}\n----\n======\n\nMethod authorization is a combination of before- and after-method authorization.\n\n[NOTE]\n====\nBefore-method authorization is performed before the method is invoked.\nIf that authorization denies access, the method is not invoked, and an `AccessDeniedException` is thrown.\nAfter-method authorization is performed after the method is invoked, but before the method returns to the caller.\nIf that authorization denies access, the value is not returned, and an `AccessDeniedException` is thrown\n====\n\nTo recreate what adding `@EnableReactiveMethodSecurity(useAuthorizationManager=true)` does by default, you would publish the following configuration:\n\n.Full Pre-post Method Security Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\nclass MethodSecurityConfig {\n\t@Bean\n\tBeanDefinitionRegistryPostProcessor aopConfig() {\n\t\treturn AopConfigUtils::registerAutoProxyCreatorIfNecessary;\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tPreFilterAuthorizationReactiveMethodInterceptor preFilterInterceptor() {\n\t\treturn new PreFilterAuthorizationReactiveMethodInterceptor();\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tAuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeInterceptor() {\n\t\treturn AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize();\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tAuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeInterceptor() {\n\t\treturn AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize();\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tPostFilterAuthorizationReactiveMethodInterceptor postFilterInterceptor() {\n\t\treturn new PostFilterAuthorizationReactiveMethodInterceptor();\n\t}\n}\n----\n======\n\nNotice that Spring Security's method security is built using Spring AOP.\n\n=== Customizing Authorization\n\nSpring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` ship with rich expression-based support.\n\n\n[[jc-reactive-method-security-custom-granted-authority-defaults]]\nAlso, for role-based authorization, Spring Security adds a default `ROLE_` prefix, which is uses when evaluating expressions like `hasRole`.\nYou can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so:\n\n.Custom GrantedAuthorityDefaults\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nstatic GrantedAuthorityDefaults grantedAuthorityDefaults() {\n\treturn new GrantedAuthorityDefaults(\"MYPREFIX_\");\n}\n----\n======\n\n[TIP]\n====\nWe expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes.\nSince the `GrantedAuthorityDefaults` bean is part of internal workings of Spring Security, we should also expose it as an infrastructural bean effectively avoiding some warnings related to bean post-processing (see https://github.com/spring-projects/spring-security/issues/14751[gh-14751]).\n====\n\n[[use-programmatic-authorization]]\n== Authorizing Methods Programmatically\n\nAs you've already seen, there are several ways that you can specify non-trivial authorization rules using xref:servlet/authorization/method-security.adoc#authorization-expressions[Method Security SpEL expressions].\n\nThere are a number of ways that you can instead allow your logic to be Java-based instead of SpEL-based.\nThis gives use access the entire Java language for increased testability and flow control.\n\n=== Using a Custom Bean in SpEL\n\nThe first way to authorize a method programmatically is a two-step process.\n\nFirst, declare a bean that has a method that takes a `MethodSecurityExpressionOperations` instance like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component(\"authz\")\npublic class AuthorizationLogic {\n    public decide(MethodSecurityExpressionOperations operations): Mono<Boolean> {\n        // ... authorization logic\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component(\"authz\")\nopen class AuthorizationLogic {\n    fun decide(val operations: MethodSecurityExpressionOperations): Mono<Boolean> {\n        // ... authorization logic\n    }\n}\n----\n======\n\nThen, reference that bean in your annotations in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class MyController {\n    @PreAuthorize(\"@authz.decide(#root)\")\n    @GetMapping(\"/endpoint\")\n    public Mono<String> endpoint() {\n        // ...\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nopen class MyController {\n    @PreAuthorize(\"@authz.decide(#root)\")\n    @GetMapping(\"/endpoint\")\n    fun endpoint(): Mono<String> {\n        // ...\n    }\n}\n----\n======\n\nSpring Security will invoke the given method on that bean for each method invocation.\n\nWhat's nice about this is all your authorization logic is in a separate class that can be independently unit tested and verified for correctness.\nIt also has access to the full Java language.\n\n[TIP]\nIn addition to returning a `Mono<Boolean>`, you can also return `Mono.empty()` to indicate that the code abstains from making a decision.\n\nIf you want to include more information about the nature of the decision, you can instead return a custom `AuthorizationDecision` like this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component(\"authz\")\npublic class AuthorizationLogic {\n    public Mono<AuthorizationDecision> decide(MethodSecurityExpressionOperations operations) {\n        // ... authorization logic\n        return Mono.just(new MyAuthorizationDecision(false, details));\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component(\"authz\")\nopen class AuthorizationLogic {\n    fun decide(val operations: MethodSecurityExpressionOperations): Mono<AuthorizationDecision> {\n        // ... authorization logic\n        return Mono.just(MyAuthorizationDecision(false, details))\n    }\n}\n----\n======\n\nOr throw a custom `AuthorizationDeniedException` instance.\nNote, though, that returning an object is preferred as this doesn't incur the expense of generating a stacktrace.\n\nThen, you can access the custom details when you xref:servlet/authorization/method-security.adoc#fallback-values-authorization-denied[customize how the authorization result is handled].\n\n[[jc-reactive-method-security-custom-authorization-manager]]\n[[custom-authorization-managers]]\n=== Using a Custom Authorization Manager\n\nThe second way to authorize a method programmatically is to create a custom xref:servlet/authorization/architecture.adoc#_the_authorizationmanager[`AuthorizationManager`].\n\nFirst, declare an authorization manager instance, perhaps like this one:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class MyPreAuthorizeAuthorizationManager implements ReactiveAuthorizationManager<MethodInvocation> {\n    @Override\n    public Mono<AuthorizationResult> authorize(Supplier<Authentication> authentication, MethodInvocation invocation) {\n        // ... authorization logic\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass MyPreAuthorizeAuthorizationManager : ReactiveAuthorizationManager<MethodInvocation> {\n    override fun authorize(authentication: Supplier<Authentication>, invocation: MethodInvocation): Mono<AuthorizationResult> {\n        // ... authorization logic\n    }\n\n}\n----\n======\n\nThen, publish the method interceptor with a pointcut that corresponds to when you want that `ReactiveAuthorizationManager` to run.\nFor example, you could replace how `@PreAuthorize` and `@PostAuthorize` work like so:\n\n.Only @PreAuthorize and @PostAuthorize Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableMethodSecurity(prePostEnabled = false)\nclass MethodSecurityConfig {\n    @Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tAdvisor preAuthorize(MyPreAuthorizeAuthorizationManager manager) {\n\t\treturn AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(manager);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tAdvisor postAuthorize(MyPostAuthorizeAuthorizationManager manager) {\n\t\treturn AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(manager);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableMethodSecurity(prePostEnabled = false)\nclass MethodSecurityConfig {\n   \t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tfun preAuthorize(val manager: MyPreAuthorizeAuthorizationManager) : Advisor {\n\t\treturn AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(manager)\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tfun postAuthorize(val manager: MyPostAuthorizeAuthorizationManager) : Advisor {\n\t\treturn AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(manager)\n\t}\n}\n----\n======\n\n[TIP]\n====\nYou can place your interceptor in between Spring Security method interceptors using the order constants specified in `AuthorizationInterceptorsOrder`.\n====\n\n[[customizing-expression-handling]]\n=== Customizing Expression Handling\n\nOr, third, you can customize how each SpEL expression is handled.\nTo do that, you can expose a custom `MethodSecurityExpressionHandler`, like so:\n\n.Custom MethodSecurityExpressionHandler\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nstatic MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {\n\tDefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();\n\thandler.setRoleHierarchy(roleHierarchy);\n\treturn handler;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ncompanion object {\n\t@Bean\n\tfun methodSecurityExpressionHandler(val roleHierarchy: RoleHierarchy) : MethodSecurityExpressionHandler {\n\t\tval handler = DefaultMethodSecurityExpressionHandler()\n\t\thandler.setRoleHierarchy(roleHierarchy)\n\t\treturn handler\n\t}\n}\n----\n======\n\n[TIP]\n====\nWe expose `MethodSecurityExpressionHandler` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes\n====\n\nYou can also subclass xref:servlet/authorization/method-security.adoc#subclass-defaultmethodsecurityexpressionhandler[`DefaultMessageSecurityExpressionHandler`] to add your own custom authorization expressions beyond the defaults.\n\n== EnableReactiveMethodSecurity\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\nMono<String> messageByUsername = ReactiveSecurityContextHolder.getContext()\n\t.map(SecurityContext::getAuthentication)\n\t.map(Authentication::getName)\n\t.flatMap(this::findMessageByUsername)\n\t// In a WebFlux application the `subscriberContext` is automatically setup using `ReactorContextWebFilter`\n\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));\n\nStepVerifier.create(messageByUsername)\n\t.expectNext(\"Hi user\")\n\t.verifyComplete();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval authentication: Authentication = TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\")\n\nval messageByUsername: Mono<String> = ReactiveSecurityContextHolder.getContext()\n\t.map(SecurityContext::getAuthentication)\n\t.map(Authentication::getName)\n\t.flatMap(this::findMessageByUsername) // In a WebFlux application the `subscriberContext` is automatically setup using `ReactorContextWebFilter`\n\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\nStepVerifier.create(messageByUsername)\n\t.expectNext(\"Hi user\")\n\t.verifyComplete()\n----\n======\n\nWhere `this::findMessageByUsername` is defined as:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMono<String> findMessageByUsername(String username) {\n\treturn Mono.just(\"Hi \" + username);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nfun findMessageByUsername(username: String): Mono<String> {\n\treturn Mono.just(\"Hi $username\")\n}\n----\n======\n\nThe following minimal method security configures method security in reactive applications:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableReactiveMethodSecurity\npublic class SecurityConfig {\n\t@Bean\n\tpublic MapReactiveUserDetailsService userDetailsService() {\n\t\tUser.UserBuilder userBuilder = User.withDefaultPasswordEncoder();\n\t\tUserDetails rob = userBuilder.username(\"rob\")\n\t\t\t.password(\"rob\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\tUserDetails admin = userBuilder.username(\"admin\")\n\t\t\t.password(\"admin\")\n\t\t\t.roles(\"USER\",\"ADMIN\")\n\t\t\t.build();\n\t\treturn new MapReactiveUserDetailsService(rob, admin);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableReactiveMethodSecurity\nclass SecurityConfig {\n\t@Bean\n\tfun userDetailsService(): MapReactiveUserDetailsService {\n\t\tval userBuilder: User.UserBuilder = User.withDefaultPasswordEncoder()\n\t\tval rob = userBuilder.username(\"rob\")\n\t\t\t.password(\"rob\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build()\n\t\tval admin = userBuilder.username(\"admin\")\n\t\t\t.password(\"admin\")\n\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t.build()\n\t\treturn MapReactiveUserDetailsService(rob, admin)\n\t}\n}\n----\n======\n\nConsider the following class:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class HelloWorldMessageService {\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tpublic Mono<String> findMessage() {\n\t\treturn Mono.just(\"Hello World!\");\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass HelloWorldMessageService {\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tfun findMessage(): Mono<String> {\n\t\treturn Mono.just(\"Hello World!\")\n\t}\n}\n----\n======\n\nAlternatively, the following class uses Kotlin coroutines:\n\n[tabs]\n======\nKotlin::\n+\n[source,kotlin,role=\"primary\"]\n----\n@Component\nclass HelloWorldMessageService {\n    @PreAuthorize(\"hasRole('ADMIN')\")\n    suspend fun findMessage(): String {\n        delay(10)\n        return \"Hello World!\"\n    }\n}\n----\n======\n\n\nCombined with our configuration above, `@PreAuthorize(\"hasRole('ADMIN')\")` ensures that `findByMessage` is invoked only by a user with the `ADMIN` role.\nNote that any of the expressions in standard method security work for `@EnableReactiveMethodSecurity`.\nHowever, at this time, we support only a return type of `Boolean` or `boolean` of the expression.\nThis means that the expression must not block.\n\nWhen integrating with xref:reactive/configuration/webflux.adoc#jc-webflux[WebFlux Security], the Reactor Context is automatically established by Spring Security according to the authenticated user:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\n@EnableReactiveMethodSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tSecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {\n\t\treturn http\n\t\t\t// Demonstrate that method security works\n\t\t\t// Best practice to use both for defense in depth\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().permitAll()\n\t\t\t)\n\t\t\t.httpBasic(withDefaults())\n\t\t\t.build();\n\t}\n\n\t@Bean\n\tMapReactiveUserDetailsService userDetailsService() {\n\t\tUser.UserBuilder userBuilder = User.withDefaultPasswordEncoder();\n\t\tUserDetails rob = userBuilder.username(\"rob\")\n\t\t\t.password(\"rob\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\tUserDetails admin = userBuilder.username(\"admin\")\n\t\t\t.password(\"admin\")\n\t\t\t.roles(\"USER\",\"ADMIN\")\n\t\t\t.build();\n\t\treturn new MapReactiveUserDetailsService(rob, admin);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\n@EnableReactiveMethodSecurity\nclass SecurityConfig {\n\t@Bean\n\topen fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n\t\treturn http {\n\t\t\tauthorizeExchange {\n\t\t\t\tauthorize(anyExchange, permitAll)\n\t\t\t}\n\t\t\thttpBasic { }\n\t\t}\n\t}\n\n\t@Bean\n\tfun userDetailsService(): MapReactiveUserDetailsService {\n\t\tval userBuilder: User.UserBuilder = User.withDefaultPasswordEncoder()\n\t\tval rob = userBuilder.username(\"rob\")\n\t\t\t.password(\"rob\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build()\n\t\tval admin = userBuilder.username(\"admin\")\n\t\t\t.password(\"admin\")\n\t\t\t.roles(\"USER\", \"ADMIN\")\n\t\t\t.build()\n\t\treturn MapReactiveUserDetailsService(rob, admin)\n\t}\n}\n----\n======\n\nYou can find a complete sample in {gh-samples-url}/reactive/webflux/java/method[hellowebflux-method].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/configuration/webflux.adoc",
    "content": "[[jc-webflux]]\n= WebFlux Security\n\nSpring Security's WebFlux support relies on a `WebFilter` and works the same for Spring WebFlux and Spring WebFlux.Fn.\nA few sample applications demonstrate the code:\n\n* Hello WebFlux {gh-samples-url}/reactive/webflux/java/hello-security[hellowebflux]\n* Hello WebFlux.Fn {gh-samples-url}/reactive/webflux-fn/hello-security[hellowebfluxfn]\n* Hello WebFlux Method {gh-samples-url}/reactive/webflux/java/method[hellowebflux-method]\n\n\n== Minimal WebFlux Security Configuration\n\nThe following listing shows a minimal WebFlux Security configuration:\n\n.Minimal WebFlux Security Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n-----\n@Configuration\n@EnableWebFluxSecurity\npublic class HelloWebfluxSecurityConfig {\n\n\t@Bean\n\tpublic MapReactiveUserDetailsService userDetailsService() {\n\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"user\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\treturn new MapReactiveUserDetailsService(user);\n\t}\n}\n-----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n-----\n@Configuration\n@EnableWebFluxSecurity\nclass HelloWebfluxSecurityConfig {\n\n    @Bean\n    fun userDetailsService(): ReactiveUserDetailsService {\n        val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"user\")\n                .roles(\"USER\")\n                .build()\n        return MapReactiveUserDetailsService(userDetails)\n    }\n}\n-----\n======\n\nThis configuration provides form and HTTP basic authentication, sets up authorization to require an authenticated user for accessing any page, sets up a default login page and a default logout page, sets up security related HTTP headers, adds CSRF protection, and more.\n\n== Explicit WebFlux Security Configuration\n\nThe following page shows an explicit version of the minimal WebFlux Security configuration:\n\n.Explicit WebFlux Security Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n-----\n@Configuration\n@EnableWebFluxSecurity\npublic class HelloWebfluxSecurityConfig {\n\n\t@Bean\n\tpublic MapReactiveUserDetailsService userDetailsService() {\n\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"user\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\treturn new MapReactiveUserDetailsService(user);\n\t}\n\n\t@Bean\n\tpublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t    .anyExchange().authenticated()\n\t\t\t)\n\t\t\t.httpBasic(withDefaults())\n\t\t\t.formLogin(withDefaults());\n\t\treturn http.build();\n\t}\n}\n-----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n-----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nclass HelloWebfluxSecurityConfig {\n\n    @Bean\n    fun userDetailsService(): ReactiveUserDetailsService {\n        val userDetails = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"user\")\n                .roles(\"USER\")\n                .build()\n        return MapReactiveUserDetailsService(userDetails)\n    }\n\n    @Bean\n    fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        return http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n            formLogin { }\n            httpBasic { }\n        }\n    }\n}\n-----\n======\n\n[NOTE]\nMake sure to import the `org.springframework.security.config.web.server.invoke` function to enable the Kotlin DSL in your class, as the IDE will not always auto-import the method, causing compilation issues.\n\nThis configuration explicitly sets up all the same things as our minimal configuration.\nFrom here, you can more easily make changes to the defaults.\n\nYou can find more examples of explicit configuration in unit tests, by searching for https://github.com/spring-projects/spring-security/search?q=path%3Aconfig%2Fsrc%2Ftest%2F+EnableWebFluxSecurity[`EnableWebFluxSecurity` in the `config/src/test/` directory].\n\n[[jc-webflux-multiple-filter-chains]]\n=== Multiple Chains Support\n\nYou can configure multiple `SecurityWebFilterChain` instances to separate configuration by `RequestMatcher` instances.\n\nFor example, you can isolate configuration for URLs that start with `/api`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nstatic class MultiSecurityHttpConfig {\n\n    @Order(Ordered.HIGHEST_PRECEDENCE)                                                      <1>\n    @Bean\n    SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {\n        http\n            .securityMatcher(new PathPatternParserServerWebExchangeMatcher(\"/api/**\"))      <2>\n            .authorizeExchange((authorize) -> authorize\n                .anyExchange().authenticated()\n            )\n            .oauth2ResourceServer(OAuth2ResourceServerSpec::jwt);                           <3>\n        return http.build();\n    }\n\n    @Bean\n    SecurityWebFilterChain webHttpSecurity(ServerHttpSecurity http) {                       <4>\n        http\n            .authorizeExchange((authorize) -> authorize\n                .anyExchange().authenticated()\n            )\n            .httpBasic(withDefaults());                                                     <5>\n        return http.build();\n    }\n\n    @Bean\n    ReactiveUserDetailsService userDetailsService() {\n        return new MapReactiveUserDetailsService(\n                PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nopen class MultiSecurityHttpConfig {\n    @Order(Ordered.HIGHEST_PRECEDENCE)                                                      <1>\n    @Bean\n    open fun apiHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n        return http {\n            securityMatcher(PathPatternParserServerWebExchangeMatcher(\"/api/**\"))           <2>\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n            oauth2ResourceServer {\n                jwt { }                                                                     <3>\n            }\n        }\n    }\n\n    @Bean\n    open fun webHttpSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {            <4>\n        return http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n            httpBasic { }                                                                   <5>\n        }\n    }\n\n    @Bean\n    open fun userDetailsService(): ReactiveUserDetailsService {\n        return MapReactiveUserDetailsService(\n            PasswordEncodedUser.user(), PasswordEncodedUser.admin()\n        )\n    }\n}\n----\n======\n\n<1> Configure a `SecurityWebFilterChain` with an `@Order` to specify which `SecurityWebFilterChain` Spring Security should consider first\n<2> Use `PathPatternParserServerWebExchangeMatcher` to state that this `SecurityWebFilterChain` will only apply to URL paths that start with `/api/`\n<3> Specify the authentication mechanisms that will be used for `/api/**` endpoints\n<4> Create another instance of `SecurityWebFilterChain` with lower precedence to match all other URLs\n<5> Specify the authentication mechanisms that will be used for the rest of the application\n\nSpring Security selects one `SecurityWebFilterChain` `@Bean` for each request.\nIt matches the requests in order by the `securityMatcher` definition.\n\nIn this case, that means that, if the URL path starts with `/api`, Spring Security uses `apiHttpSecurity`.\nIf the URL does not start with `/api`, Spring Security defaults to `webHttpSecurity`, which has an implied `securityMatcher` that matches any request.\n\n\n[[modular-serverhttpsecurity-configuration]]\n== Modular ServerHttpSecurity Configuration\n\nMany users prefer that their Spring Security configuration lives in a centralized place and will choose to configure it within the `SecurityWebFilterChain` Bean declaration.\nHowever, there are times that users may want to modularize the configuration.\nThis can be done using:\n\n* xref:#serverhttpsecurity-customizer-bean[Customizer<ServerHttpSecurity> Beans]\n* xref:#top-level-customizer-bean[Top Level ServerHttpSecurity Customizer Beans]\n\n// FIXME: this needs to link to appropriate spot\n// NOTE: If you are using Spring Security's xref:servlet/configuration/kotlin.adoc[], then you can also expose `*Dsl -> Unit` Beans as outlined in xref:./kotlin.adoc#modular-httpsecuritydsl-configuration[Modular HttpSecurityDsl Configuration].\n\n\n[[serverhttpsecurity-customizer-bean]]\n=== Customizer<ServerHttpSecurity> Beans\n\nIf you would like to modularize your security configuration you can place logic in a `Customizer<ServerHttpSecurity>` Bean.\nFor example, the following configuration will ensure all `ServerHttpSecurity` instances are configured to:\n\ninclude-code::./ServerHttpSecurityCustomizerBeanConfiguration[tag=httpSecurityCustomizer,indent=0]\n\n<1> Set the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] to `object-src 'none'`\n<2> xref:servlet/exploits/http.adoc#servlet-http-redirect[Redirect any request to https]\n\n\n[[top-level-customizer-bean]]\n=== Top Level ServerHttpSecurity Customizer Beans\n\nIf you prefer to have further modularization of your security configuration, Spring Security will automatically apply any top level `HttpSecurity` `Customizer` Beans.\n\nA top level `HttpSecurity` `Customizer` type can be summarized as any `Customizer<T>` that matches `public HttpSecurity.*(Customizer<T>)`.\nThis translates to any `Customizer<T>` that is a single argument to a public method on javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[].\n\nA few examples can help to clarify.\nIf `Customizer<ContentTypeOptionsConfig>` is published as a Bean, it will not be be automatically applied because it is an argument to javadoc:org.springframework.security.config.annotation.web.configurers.HeadersConfigurer#contentTypeOptions(org.springframework.security.config.Customizer)[] which is not a method defined on `HttpSecurity`.\nHowever, if `Customizer<HeadersConfigurer<HttpSecurity>>` is published as a Bean, it will be automatically applied because it is an argument to javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity#headers(org.springframework.security.config.Customizer)[].\n\nFor example, the following configuration will ensure that the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] is set to `object-src 'none'`:\n\ninclude-code::./TopLevelCustomizerBeanConfiguration[tag=headersCustomizer,indent=0]\n\n[[customizer-bean-ordering]]\n=== Customizer Bean Ordering\n\nFirst each xref:#httpsecurity-customizer-bean[Customizer<HttpSecurity> Bean] is applied using {spring-framework-api-url}org/springframework/beans/factory/ObjectProvider.html#orderedStream()[ObjectProvider#orderedStream()].\nThis means that if there are multiple `Customizer<HttpSecurity>` Beans, the {spring-framework-api-url}org/springframework/core/annotation/Order.html[@Order] annotation can be added to the Bean definitions to control the ordering.\n\nNext every xref:#top-level-customizer-bean[Top Level HttpSecurity Customizer Beans] type is looked up and each is is applied using `ObjectProvider#orderedStream()`.\nIf there is are two `Customizer<HeadersConfigurer<HttpSecurity>>` beans and two `Customizer<HttpsRedirectConfigurer<HttpSecurity>>` instances, the order that each `Customizer` type is invoked is undefined.\nHowever, the order that each instance of `Customizer<HttpsRedirectConfigurer<HttpSecurity>>` is defined by `ObjectProvider#orderedStream()` and can be controlled using `@Order` on the Bean the definitions.\n\nFinally, the `HttpSecurity` Bean is injected as a Bean.\nAll `Customizer` instances are applied before the `HttpSecurity` Bean is created.\nThis allows overriding the customizations provided by the `Customizer` Beans.\n\nYou can find an example below that illustrates the ordering:\n\ninclude-code::./CustomizerBeanOrderingConfiguration[tag=sample,indent=0]\n\n<1> First all `Customizer<HttpSecurity>` instances are applied.\nThe `adminAuthorization` Bean has the highest `@Order` so it is applied first.\nIf there are no `@Order` annotations on the `Customizer<HttpSecurity>` Beans or the `@Order` annotations had the same value, then the order that the `Customizer<HttpSecurity>` instances are applied is undefined.\n<2> The `userAuthorization` is applied next due to being an instance of `Customizer<HttpSecurity>`\n<3> The order that the `Customizer` types are undefined.\nIn this example, the order of `contentSecurityPolicy`, `contentTypeOptions`, and `httpsRedirect` are undefined.\nIf `@Order(Ordered.HIGHEST_PRECEDENCE)` was added to `contentTypeOptions`, then we would know that `contentTypeOptions` is before `contentSecurityPolicy` (they are the same type), but we do not know if `httpsRedirect` is before or after the `Customizer<HeadersConfigurer<HttpSecurity>>` Beans.\n<4> After all of the `Customizer` Beans are applied, the `HttpSecurity` is passed in as a Bean.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/exploits/csrf.adoc",
    "content": "[[webflux-csrf]]\n= Cross Site Request Forgery (CSRF) for WebFlux Environments\n\nThis section discusses Spring Security's xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for WebFlux environments.\n\n[[webflux-csrf-using]]\n== Using Spring Security CSRF Protection\nThe steps to using Spring Security's CSRF protection are outlined below:\n\n* <<webflux-csrf-read-only,Use proper HTTP verbs>>\n* <<webflux-csrf-configure,Configure CSRF Protection>>\n* <<webflux-csrf-include,Include the CSRF Token>>\n\n[[webflux-csrf-read-only]]\n=== Use Proper HTTP Verbs\nThe first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs.\nThis is covered in detail in xref:features/exploits/csrf.adoc#csrf-protection-read-only[Safe Methods Must be Read-only].\n\n[[webflux-csrf-configure]]\n=== Configure CSRF Protection\nThe next step is to configure Spring Security's CSRF protection within your application.\nBy default, Spring Security's CSRF protection is enabled, but you may need to customize the configuration.\nThe next few subsections cover a few common customizations.\n\n[[webflux-csrf-configure-custom-repository]]\n==== Custom CsrfTokenRepository\n\nBy default, Spring Security stores the expected CSRF token in the `WebSession` by using `WebSessionServerCsrfTokenRepository`.\nSometimes, you may need to configure a custom `ServerCsrfTokenRepository`.\nFor example, you may want to persist the `CsrfToken` in a cookie to <<webflux-csrf-include-ajax-auto,support a JavaScript-based application>>.\n\nBy default, the `CookieServerCsrfTokenRepository` writes to a cookie named `XSRF-TOKEN` and read its from a header named `X-XSRF-TOKEN` or the HTTP `_csrf` parameter.\nThese defaults come from https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS]\n\nYou can configure `CookieServerCsrfTokenRepository` in Java Configuration:\n\n.Store CSRF Token in a Cookie\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n-----\n@Bean\npublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.csrf((csrf) -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))\n\treturn http.build();\n}\n-----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n-----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        csrf {\n            csrfTokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse()\n        }\n    }\n}\n-----\n======\n\n[NOTE]\n====\nThe preceding sample explicitly sets `cookieHttpOnly=false`.\nThis is necessary to let JavaScript (in this case, AngularJS) to read it.\nIf you do not need the ability to read the cookie with JavaScript directly, we recommend to omitting `cookieHttpOnly=false` (by using `new CookieServerCsrfTokenRepository()` instead) to improve security.\n====\n\n[[webflux-csrf-configure-disable]]\n==== Disable CSRF Protection\nBy default, CSRF protection is enabled.\nHowever, you can disable CSRF protection if it xref:features/exploits/csrf.adoc#csrf-when[makes sense for your application].\n\nThe Java configuration below will disable CSRF protection.\n\n.Disable CSRF Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.csrf((csrf) -> csrf.disable()))\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n-----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        csrf {\n            disable()\n        }\n    }\n}\n-----\n======\n\n[[webflux-csrf-configure-request-handler]]\n==== Configure ServerCsrfTokenRequestHandler\n\nSpring Security's javadoc:org.springframework.security.web.server.csrf.CsrfWebFilter[] exposes a javadoc:org.springframework.security.web.server.csrf.CsrfToken[`Mono<CsrfToken>`] as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken` with the help of a javadoc:org.springframework.security.web.server.csrf.ServerCsrfTokenRequestHandler[].\nIn 5.8, the default implementation was `ServerCsrfTokenRequestAttributeHandler`, which simply makes the `Mono<CsrfToken>` available as an exchange attribute.\n\nAs of 6.0, the default implementation is `XorServerCsrfTokenRequestAttributeHandler`, which provides protection for BREACH (see https://github.com/spring-projects/spring-security/issues/4001[gh-4001]).\n\nIf you wish to disable BREACH protection of the `CsrfToken` and revert to the 5.8 default, you can configure `ServerCsrfTokenRequestAttributeHandler` using the following Java configuration:\n\n.Disable BREACH protection\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n-----\n@Bean\npublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.csrf((csrf) -> csrf\n\t\t\t.csrfTokenRequestHandler(new ServerCsrfTokenRequestAttributeHandler())\n\t\t)\n\treturn http.build();\n}\n-----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n-----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        csrf {\n            csrfTokenRequestHandler = ServerCsrfTokenRequestAttributeHandler()\n        }\n    }\n}\n-----\n======\n\n[[webflux-csrf-include]]\n=== Include the CSRF Token\n\nFor the xref:features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request.\nIt must be included in a part of the request (a form parameter, an HTTP header, or other option) that is not automatically included in the HTTP request by the browser.\n\n<<webflux-csrf-configure-request-handler,We've seen>> that the `Mono<CsrfToken>` is exposed as a `ServerWebExchange` attribute.\nThis means that any view technology can access the `Mono<CsrfToken>` to expose the expected token as either a <<webflux-csrf-include-form-attr,form>> or a <<webflux-csrf-include-ajax-meta,meta tag>>.\n\n[[webflux-csrf-include-subscribe]]\nIf your view technology does not provide a simple way to subscribe to the `Mono<CsrfToken>`, a common pattern is to use Spring's `@ControllerAdvice` to expose the `CsrfToken` directly.\nThe following example places the `CsrfToken` on the default attribute name (`_csrf`) used by Spring Security's <<webflux-csrf-include-form-auto,CsrfRequestDataValueProcessor>> to automatically include the CSRF token as a hidden input:\n\n.`CsrfToken` as `@ModelAttribute`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@ControllerAdvice\npublic class SecurityControllerAdvice {\n\t@ModelAttribute\n\tMono<CsrfToken> csrfToken(ServerWebExchange exchange) {\n\t\tMono<CsrfToken> csrfToken = exchange.getAttribute(CsrfToken.class.getName());\n\t\treturn csrfToken.doOnSuccess((token) -> token.getAttributes()\n\t\t\t\t.put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, token));\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@ControllerAdvice\nclass SecurityControllerAdvice {\n    @ModelAttribute\n    fun csrfToken(exchange: ServerWebExchange): Mono<CsrfToken> {\n        val csrfToken: Mono<CsrfToken>? = exchange.getAttribute(CsrfToken::class.java.name)\n        return csrfToken!!.doOnSuccess { token ->\n            exchange.attributes[CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME] = token\n        }\n    }\n}\n----\n======\n\nFortunately, Thymeleaf provides <<webflux-csrf-include-form-auto,integration>> that works without any additional work.\n\n[[webflux-csrf-include-form]]\n==== Form URL Encoded\nTo post an HTML form, the CSRF token must be included in the form as a hidden input.\nThe following example shows what the rendered HTML might look like:\n\n.CSRF Token HTML\n[source,html]\n----\n<input type=\"hidden\"\n\tname=\"_csrf\"\n\tvalue=\"4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\"/>\n----\n\nNext, we discuss various ways of including the CSRF token in a form as a hidden input.\n\n[[webflux-csrf-include-form-auto]]\n===== Automatic CSRF Token Inclusion\n\nSpring Security's CSRF support provides integration with Spring's {spring-framework-api-url}org/springframework/web/reactive/result/view/RequestDataValueProcessor.html[`RequestDataValueProcessor`] through its javadoc:org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor[].\nFor `CsrfRequestDataValueProcessor` to work, the `Mono<CsrfToken>` must be subscribed to and the `CsrfToken` must be <<webflux-csrf-include-subscribe,exposed as an attribute>> that matches javadoc:org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor#DEFAULT_CSRF_ATTR_NAME[].\n\nFortunately, Thymeleaf https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#integration-with-requestdatavalueprocessor[takes care of all the boilerplate] for you by integrating with `RequestDataValueProcessor` to ensure that forms that have an unsafe HTTP method (POST) automatically include the actual CSRF token.\n\n[[webflux-csrf-include-form-attr]]\n===== CsrfToken Request Attribute\n\nIf the <<webflux-csrf-include,other options>> for including the actual CSRF token in the request do not work, you can take advantage of the fact that the `Mono<CsrfToken>` <<webflux-csrf-include,is exposed>> as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken`.\n\nThe following Thymeleaf sample assumes that you <<webflux-csrf-include-subscribe,expose>> the `CsrfToken` on an attribute named `_csrf`:\n\n.CSRF Token in Form with Request Attribute\n[source,html]\n----\n<form th:action=\"@{/logout}\"\n\tmethod=\"post\">\n<input type=\"submit\"\n\tvalue=\"Log out\" />\n<input type=\"hidden\"\n\tth:name=\"${_csrf.parameterName}\"\n\tth:value=\"${_csrf.token}\"/>\n</form>\n----\n\n[[webflux-csrf-include-ajax]]\n==== Ajax and JSON Requests\nIf you use JSON, you cannot submit the CSRF token within an HTTP parameter.\nInstead, you can submit the token within a HTTP header.\n\nIn the following sections, we discuss various ways of including the CSRF token as an HTTP request header in JavaScript-based applications.\n\n[[webflux-csrf-include-ajax-auto]]\n===== Automatic Inclusion\n\nYou can <<webflux-csrf-configure-custom-repository,configure>> Spring Security to store the expected CSRF token in a cookie.\nBy storing the expected CSRF in a cookie, JavaScript frameworks, such as https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS], automatically include the actual CSRF token in the HTTP request headers.\n\n[[webflux-csrf-include-ajax-meta]]\n===== Meta Tags\n\nAn alternative pattern to <<webflux-csrf-include-form-auto,exposing the CSRF in a cookie>> is to include the CSRF token within your `meta` tags.\nThe HTML might look something like this:\n\n.CSRF meta tag HTML\n[source,html]\n----\n<html>\n<head>\n\t<meta name=\"_csrf\" content=\"4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\"/>\n\t<meta name=\"_csrf_header\" content=\"X-CSRF-TOKEN\"/>\n\t<!-- ... -->\n</head>\n<!-- ... -->\n----\n\nOnce the meta tags contain the CSRF token, the JavaScript code can read the meta tags and include the CSRF token as a header.\nIf you use jQuery, you could read the meta tags with the following code:\n\n.AJAX send CSRF Token\n[source,javascript]\n----\n$(function () {\n\tvar token = $(\"meta[name='_csrf']\").attr(\"content\");\n\tvar header = $(\"meta[name='_csrf_header']\").attr(\"content\");\n\t$(document).ajaxSend(function(e, xhr, options) {\n\t\txhr.setRequestHeader(header, token);\n\t});\n});\n----\n\nThe following sample assumes that you <<webflux-csrf-include-subscribe,expose>> the `CsrfToken` on an attribute named `_csrf`.\nThe following example does this with Thymeleaf:\n\n.CSRF meta tag JSP\n[source,html]\n----\n<html>\n<head>\n\t<meta name=\"_csrf\" th:content=\"${_csrf.token}\"/>\n\t<!-- default header name is X-CSRF-TOKEN -->\n\t<meta name=\"_csrf_header\" th:content=\"${_csrf.headerName}\"/>\n\t<!-- ... -->\n</head>\n<!-- ... -->\n----\n\n[[webflux-csrf-considerations]]\n== CSRF Considerations\nThere are a few special considerations to consider when implementing protection against CSRF attacks.\nThis section discusses those considerations as it pertains to WebFlux environments.\nSee xref:features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion.\n\n\n[[webflux-considerations-csrf-login]]\n=== Logging In\n\nYou should xref:features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for login] requests to protect against forged login attempts.\nSpring Security's WebFlux support automatically does this.\n\n[[webflux-considerations-csrf-logout]]\n=== Logging Out\n\nYou should xref:features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for logout] requests to protect against forging logout attempts.\nBy default, Spring Security's `LogoutWebFilter` only processes only HTTP post requests.\nThis ensures that logout requires a CSRF token and that a malicious user cannot forcibly log out your users.\n\nThe easiest approach is to use a form to log out.\nIf you really want a link, you can use JavaScript to have the link perform a POST (maybe on a hidden form).\nFor browsers with JavaScript that is disabled, you can optionally have the link take the user to a logout confirmation page that performs the POST.\n\nIf you really want to use HTTP GET with logout, you can do so, but remember that doing so is generally not recommended.\nFor example, the following Java Configuration logs out when the `/logout` URL is requested with any HTTP method:\n\n// FIXME: This should be a link to log out documentation\n\n.Log out with HTTP GET\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.logout((logout) -> logout.requiresLogout(new PathPatternParserServerWebExchangeMatcher(\"/logout\")))\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        logout {\n            requiresLogout = PathPatternParserServerWebExchangeMatcher(\"/logout\")\n        }\n    }\n}\n----\n======\n\n\n[[webflux-considerations-csrf-timeouts]]\n=== CSRF and Session Timeouts\n\nBy default, Spring Security stores the CSRF token in the `WebSession`.\nThis arrangement can lead to a situation where the session expires, which means that there is not an expected CSRF token to validate against.\n\nWe have already discussed xref:features/exploits/csrf.adoc#csrf-considerations-login[general solutions] to session timeouts.\nThis section discusses the specifics of CSRF timeouts as it pertains to the WebFlux support.\n\nYou can change storage of the expected CSRF token to be in a cookie.\nFor details, see the <<webflux-csrf-configure-custom-repository>> section.\n\n// FIXME: We should add a custom AccessDeniedHandler section in the reference and update the earlier links\n\n// FIXME: We need a WebFlux multipart body vs action story. WebFlux always has multipart enabled.\n[[webflux-csrf-considerations-multipart]]\n=== Multipart (file upload)\nWe have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem.\nWhen JavaScript is available, we _recommend_ including the CSRF token in an HTTP request header (see <<webflux-csrf-include-ajax,AJAX and JSON Requests>>) to side-step the issue.\nIf JavaScript is not available, this section discusses how to place the CSRF token in the <<webflux-csrf-considerations-multipart-body,body>> and <<webflux-csrf-considerations-multipart-url,url>> within a WebFlux application.\n\n[NOTE]\n====\nFor more information about using multipart forms with Spring, see the {spring-framework-reference-url}web/webflux/reactive-spring.html#webflux-multipart[Multipart Data] section of the Spring reference.\n====\n\n[[webflux-csrf-considerations-multipart-body]]\n==== Place CSRF Token in the Body\n\nWe have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the body.\n\nIn a WebFlux application, you can do so with the following configuration:\n\n.Enable obtaining CSRF token from multipart/form-data\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.csrf((csrf) -> csrf.tokenFromMultipartDataEnabled(true))\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n\t\t// ...\n        csrf {\n            tokenFromMultipartDataEnabled = true\n        }\n    }\n}\n----\n======\n\n[[webflux-csrf-considerations-multipart-url]]\n==== Include CSRF Token in URL\n\nWe have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the URL.\nSince the `CsrfToken` is exposed as an `ServerHttpRequest` <<webflux-csrf-include,request attribute>>, we can use that to create an `action` with the CSRF token in it.\nAn example with Thymeleaf is shown below:\n\n.CSRF Token in Action\n[source,html]\n----\n<form method=\"post\"\n\tth:action=\"@{/upload(${_csrf.parameterName}=${_csrf.token})}\"\n\tenctype=\"multipart/form-data\">\n----\n\n[[webflux-csrf-considerations-override-method]]\n=== HiddenHttpMethodFilter\nWe have xref:features/exploits/csrf.adoc#csrf-considerations-override-method[already discussed] overriding the HTTP method.\n\nIn a Spring WebFlux application, overriding the HTTP method is done by using {spring-framework-api-url}org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[`HiddenHttpMethodFilter`].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/exploits/firewall.adoc",
    "content": "[[webflux-serverwebexchangefirewall]]\n= ServerWebExchangeFirewall\n\nThere are various ways a request can be created by malicious users that can exploit applications.\nSpring Security provides the `ServerWebExchangeFirewall` to allow rejecting requests that look malicious.\nThe default implementation is `StrictServerWebExchangeFirewall` which rejects malicious requests.\n\nFor example a request could contain path-traversal sequences (such as `/../`) or multiple forward slashes (`//`) that could also cause pattern-matches to fail.\nSome containers normalize these out before performing the servlet mapping, but others do not.\nTo protect against issues like these, `WebFilterChainProxy` uses a `ServerWebExchangeFirewall` strategy to check and wrap the request.\nBy default, un-normalized requests are automatically rejected, and path parameters are removed for matching purposes.\n(So, for example, an original request path of `/secure;hack=1/somefile.html;hack=2` is returned as `/secure/somefile.html`.)\nIt is, therefore, essential that a `WebFilterChainProxy` is used.\n\nIn practice, we recommend that you use method security at your service layer, to control access to your application, rather than rely entirely on the use of security constraints defined at the web-application level.\nURLs change, and it is difficult to take into account all the possible URLs that an application might support and how requests might be manipulated.\nYou should restrict yourself to using a few simple patterns that are simple to understand.\nAlways try to use a \"`deny-by-default`\" approach, where you have a catch-all wildcard (`/**` or `**`) defined last to deny access.\n\nSecurity defined at the service layer is much more robust and harder to bypass, so you should always take advantage of Spring Security's method security options.\n\nYou can customize the `ServerWebExchangeFirewall` by exposing it as a Bean.\n\n.Allow Matrix Variables\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic StrictServerWebExchangeFirewall httpFirewall() {\n    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();\n    firewall.setAllowSemicolon(true);\n    return firewall;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun httpFirewall(): StrictServerWebExchangeFirewall {\n    val firewall = StrictServerWebExchangeFirewall()\n    firewall.setAllowSemicolon(true)\n    return firewall\n}\n----\n======\n\nTo protect against https://www.owasp.org/index.php/Cross_Site_Tracing[Cross Site Tracing (XST)] and https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)[HTTP Verb Tampering], the `StrictServerWebExchangeFirewall` provides an allowed list of valid HTTP methods that are allowed.\nThe default valid methods are `DELETE`, `GET`, `HEAD`, `OPTIONS`, `PATCH`, `POST`, and `PUT`.\nIf your application needs to modify the valid methods, you can configure a custom `StrictServerWebExchangeFirewall` bean.\nThe following example allows only HTTP `GET` and `POST` methods:\n\n\n.Allow Only GET & POST\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic StrictServerWebExchangeFirewall httpFirewall() {\n    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();\n    firewall.setAllowedHttpMethods(Arrays.asList(\"GET\", \"POST\"));\n    return firewall;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun httpFirewall(): StrictServerWebExchangeFirewall {\n    val firewall = StrictServerWebExchangeFirewall()\n    firewall.setAllowedHttpMethods(listOf(\"GET\", \"POST\"))\n    return firewall\n}\n----\n======\n\nIf you must allow any HTTP method (not recommended), you can use `StrictServerWebExchangeFirewall.setUnsafeAllowAnyHttpMethod(true)`.\nDoing so entirely disables validation of the HTTP method.\n\n\n[[webflux-serverwebexchangefirewall-headers-parameters]]\n`StrictServerWebExchangeFirewall` also checks header names and values and parameter names.\nIt requires that each character have a defined code point and not be a control character.\n\nThis requirement can be relaxed or adjusted as necessary by using the following methods:\n\n* `StrictServerWebExchangeFirewall#setAllowedHeaderNames(Predicate)`\n* `StrictServerWebExchangeFirewall#setAllowedHeaderValues(Predicate)`\n* `StrictServerWebExchangeFirewall#setAllowedParameterNames(Predicate)`\n\n[NOTE]\n====\nParameter values can be also controlled with `setAllowedParameterValues(Predicate)`.\n====\n\nFor example, to switch off this check, you can wire your `StrictServerWebExchangeFirewall` with `Predicate` instances that always return `true`:\n\n.Allow Any Header Name, Header Value, and Parameter Name\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic StrictServerWebExchangeFirewall httpFirewall() {\n    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();\n    firewall.setAllowedHeaderNames((header) -> true);\n    firewall.setAllowedHeaderValues((header) -> true);\n    firewall.setAllowedParameterNames((parameter) -> true);\n    return firewall;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun httpFirewall(): StrictServerWebExchangeFirewall {\n    val firewall = StrictServerWebExchangeFirewall()\n    firewall.setAllowedHeaderNames { true }\n    firewall.setAllowedHeaderValues { true }\n    firewall.setAllowedParameterNames { true }\n    return firewall\n}\n----\n======\n\nAlternatively, there might be a specific value that you need to allow.\n\nFor example, iPhone Xʀ uses a `User-Agent` that includes a character that is not in the ISO-8859-1 charset.\nDue to this fact, some application servers parse this value into two separate characters, the latter being an undefined character.\n\nYou can address this with the `setAllowedHeaderValues` method:\n\n.Allow Certain User Agents\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic StrictServerWebExchangeFirewall httpFirewall() {\n    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();\n    Pattern allowed = Pattern.compile(\"[\\\\p{IsAssigned}&&[^\\\\p{IsControl}]]*\");\n    Pattern userAgent = ...;\n    firewall.setAllowedHeaderValues((header) -> allowed.matcher(header).matches() || userAgent.matcher(header).matches());\n    return firewall;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun httpFirewall(): StrictServerWebExchangeFirewall {\n    val firewall = StrictServerWebExchangeFirewall()\n    val allowed = Pattern.compile(\"[\\\\p{IsAssigned}&&[^\\\\p{IsControl}]]*\")\n    val userAgent = Pattern.compile(...)\n    firewall.setAllowedHeaderValues { allowed.matcher(it).matches() || userAgent.matcher(it).matches() }\n    return firewall\n}\n----\n======\n\nIn the case of header values, you may instead consider parsing them as UTF-8 at verification time:\n\n.Parse Headers As UTF-8\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nfirewall.setAllowedHeaderValues((header) -> {\n    String parsed = new String(header.getBytes(ISO_8859_1), UTF_8);\n    return allowed.matcher(parsed).matches();\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nfirewall.setAllowedHeaderValues {\n    val parsed = String(header.getBytes(ISO_8859_1), UTF_8)\n    return allowed.matcher(parsed).matches()\n}\n----\n======\n\nThe `ServerExchangeRejectedHandler` interface is used to handle `ServerExchangeRejectedException` throw by Spring Security's `ServerWebExchangeFirewall`.\nBy default `HttpStatusExchangeRejectedHandler` is used to send an HTTP 400 response to clients when a request is rejected.\nTo customize the behavior, users can expose a `ServerExchangeRejectedHandler` Bean.\nFor example, the following will send an HTTP 404 when the request is rejected:\n\n\n.Send 404 on Request Rejected\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nServerExchangeRejectedHandler rejectedHandler() {\n\treturn new HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun rejectedHandler(): ServerExchangeRejectedHandler {\n    return HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND)\n}\n----\n======\n\nHandling can be completely customized by creating a custom `ServerExchangeRejectedHandler` implementation.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/exploits/headers.adoc",
    "content": "[[webflux-headers]]\n= Security HTTP Response Headers\n\nYou can use xref:features/exploits/headers.adoc#headers[Security HTTP Response Headers] to increase the security of web applications.\nThis section is dedicated to WebFlux-based support for Security HTTP Response Headers.\n\n[[webflux-headers-default]]\n== Default Security Headers\n\nSpring Security provides a xref:features/exploits/headers.adoc#headers-default[default set of Security HTTP Response Headers] to provide secure defaults.\nWhile each of these headers are considered best practice, it should be noted that not all clients use the headers, so additional testing is encouraged.\n\nYou can customize specific headers.\nFor example, assume that you want the defaults but you wish to specify `SAMEORIGIN` for xref:servlet/exploits/headers.adoc#servlet-headers-frame-options[`X-Frame-Options`].\n\nYou can do so with the following configuration:\n\n.Customize Default Security Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.frameOptions((frameOptions) -> frameOptions\n\t\t\t\t.mode(Mode.SAMEORIGIN)\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            frameOptions {\n                mode = Mode.SAMEORIGIN\n            }\n        }\n    }\n}\n----\n======\n\nIf you do not want the defaults to be added and want explicit control over what should be used, you can disable the defaults:\n\n\n.Disable HTTP Security Response Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers.disable());\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            disable()\n        }\n    }\n}\n----\n======\n\n[[webflux-headers-cache-control]]\n== Cache Control\n\nSpring Security includes xref:features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default.\n\nHowever, if you actually want to cache specific responses, your application can selectively add them to the {spring-framework-api-url}org/springframework/http/server/reactive/ServerHttpResponse.html[`ServerHttpResponse`] to override the header set by Spring Security.\nThis is useful to ensure that such things as CSS, JavaScript, and images are properly cached.\n\nWhen using Spring WebFlux, you typically do so within your configuration.\nYou can find details on how to do so in the {spring-framework-reference-url}web/webflux/config.html#webflux-config-static-resources[Static Resources] portion of the Spring Reference documentation.\n\nIf necessary, you can also disable Spring Security's cache control HTTP response headers.\n\n.Cache Control Disabled\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.cache((cache) -> cache.disable())\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            cache {\n                disable()\n            }\n        }\n    }\n}\n----\n======\n\n\n[[webflux-headers-content-type-options]]\n== Content Type Options\nBy default, Spring Security includes xref:features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers.\nHowever, you can disable it:\n\n.Content Type Options Disabled\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.contentTypeOptions((contentTypeOptions) -> contentTypeOptions.disable())\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            contentTypeOptions {\n                disable()\n            }\n        }\n    }\n}\n----\n======\n\n[[webflux-headers-hsts]]\n== HTTP Strict Transport Security (HSTS)\nBy default, Spring Security provides the xref:features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header.\nHowever, you can customize the results explicitly.\nFor example, the following example explicitly provides HSTS:\n\n.Strict Transport Security\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.hsts((hsts) -> hsts\n\t\t\t\t.includeSubdomains(true)\n\t\t\t\t.preload(true)\n\t\t\t\t.maxAge(Duration.ofDays(365))\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            hsts {\n                includeSubdomains = true\n                preload = true\n                maxAge = Duration.ofDays(365)\n            }\n        }\n    }\n}\n----\n======\n\n[[webflux-headers-frame-options]]\n== X-Frame-Options\nBy default, Spring Security disables rendering within an iframe by using xref:features/exploits/headers.adoc#headers-frame-options[`X-Frame-Options`].\n\nYou can customize frame options to use the same origin:\n\n.X-Frame-Options: SAMEORIGIN\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.frameOptions((frameOptions) -> frameOptions\n\t\t\t\t.mode(SAMEORIGIN)\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            frameOptions {\n                mode = SAMEORIGIN\n            }\n        }\n    }\n}\n----\n======\n\n[[webflux-headers-xss-protection]]\n== X-XSS-Protection\nBy default, Spring Security instructs browsers to disable the XSS Auditor by using <<headers-xss-protection,X-XSS-Protection header>.\nYou can disable the `X-XSS-Protection` header entirely:\n\n.X-XSS-Protection Customization\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.xssProtection((xssProtection) -> xssProtection.disable())\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            xssProtection {\n                disable()\n            }\n        }\n    }\n}\n----\n======\n\nYou can also change the header value:\n\n.X-XSS-Protection Explicit header value\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.xssProtection((xssProtection) -> xssProtection.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK))\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            xssProtection {\n                headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK\n            }\n        }\n    }\n}\n----\n======\n\n[[webflux-headers-csp]]\n== Content Security Policy (CSP)\nBy default, Spring Security does not add xref:features/exploits/headers.adoc#headers-csp[Content Security Policy], because a reasonable default is impossible to know without the context of the application.\nThe web application author must declare the security policies to enforce and/or monitor for the protected resources.\n\nFor example, consider the following security policy:\n\n.Content Security Policy Example\n[source,http]\n----\nContent-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\n----\n\nGiven the preceding policy, you can enable the CSP header:\n\n.Content Security Policy\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.contentSecurityPolicy((policy) -> policy\n\t\t\t\t.policyDirectives(\"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\")\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            contentSecurityPolicy {\n                policyDirectives = \"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\"\n            }\n        }\n    }\n}\n----\n======\n\nTo enable the CSP `report-only` header, provide the following configuration:\n\n.Content Security Policy Report Only\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.contentSecurityPolicy((policy) -> policy\n\t\t\t\t.policyDirectives(\"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\")\n\t\t\t\t.reportOnly()\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            contentSecurityPolicy {\n                policyDirectives = \"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\"\n                reportOnly = true\n            }\n        }\n    }\n}\n----\n======\n\n[[webflux-headers-referrer]]\n== Referrer Policy\n\nSpring Security adds the xref:features/exploits/headers.adoc#headers-referrer[Referrer Policy] header by default with the directive `no-referrer`.\nYou can change the Referrer Policy header using configuration as shown below:\n\n.Referrer Policy Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.referrerPolicy((referrer) -> referrer\n\t\t\t\t.policy(ReferrerPolicy.SAME_ORIGIN)\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            referrerPolicy {\n                policy = ReferrerPolicy.SAME_ORIGIN\n            }\n        }\n    }\n}\n----\n======\n\n\n[[webflux-headers-feature]]\n== Feature Policy\n\nBy default, Spring Security does not add xref:features/exploits/headers.adoc#headers-feature[Feature Policy] headers.\nConsider the following `Feature-Policy` header:\n\n.Feature-Policy Example\n[source]\n----\nFeature-Policy: geolocation 'self'\n----\n\nYou can enable the preceding Feature Policy header:\n\n.Feature-Policy Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.featurePolicy(\"geolocation 'self'\")\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            featurePolicy(\"geolocation 'self'\")\n        }\n    }\n}\n----\n======\n\n\n[[webflux-headers-permissions]]\n== Permissions Policy\n\nBy default, Spring Security does not add xref:features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers.\nConsider the following `Permissions-Policy` header:\n\n.Permissions-Policy Example\n[source]\n----\nPermissions-Policy: geolocation=(self)\n----\n\nYou can enable the preceding Permissions Policy header:\n\n.Permissions-Policy Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.headers((headers) -> headers\n\t\t\t.permissionsPolicy((permissions) -> permissions\n\t\t\t\t.policy(\"geolocation=(self)\")\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        headers {\n            permissionsPolicy {\n                policy = \"geolocation=(self)\"\n            }\n        }\n    }\n}\n----\n======\n\n\n[[webflux-headers-clear-site-data]]\n== Clear Site Data\n\nBy default, Spring Security does not add xref:features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers.\nConsider the following `Clear-Site-Data` header:\n\n.Clear-Site-Data Example\n----\nClear-Site-Data: \"cache\", \"cookies\"\n----\n\nYou can send the `Clear-Site-Data` header on logout:\n\n.Clear-Site-Data Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\tServerLogoutHandler securityContext = new SecurityContextServerLogoutHandler();\n\tClearSiteDataServerHttpHeadersWriter writer = new ClearSiteDataServerHttpHeadersWriter(CACHE, COOKIES);\n\tServerLogoutHandler clearSiteData = new HeaderWriterServerLogoutHandler(writer);\n\tDelegatingServerLogoutHandler logoutHandler = new DelegatingServerLogoutHandler(securityContext, clearSiteData);\n\n\thttp\n\t\t// ...\n\t\t.logout()\n\t\t\t.logoutHandler(logoutHandler);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    val securityContext: ServerLogoutHandler = SecurityContextServerLogoutHandler()\n    val writer = ClearSiteDataServerHttpHeadersWriter(CACHE, COOKIES)\n    val clearSiteData: ServerLogoutHandler = HeaderWriterServerLogoutHandler(writer)\n    val customLogoutHandler = DelegatingServerLogoutHandler(securityContext, clearSiteData)\n\n    return http {\n        // ...\n        logout {\n            logoutHandler = customLogoutHandler\n        }\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/exploits/http.adoc",
    "content": "[[webflux-http]]\n= HTTP\n\nAll HTTP-based communication should be protected with xref:features/exploits/http.adoc#http[using TLS].\n\nThis section covers details about using WebFlux-specific features that assist with HTTPS usage.\n\n[[webflux-http-redirect]]\n== Redirect to HTTPS\n\nIf a client makes a request using HTTP rather than HTTPS, you can configure Spring Security to redirect to HTTPS.\n\nThe following Java configuration redirects any HTTP requests to HTTPS:\n\n.Redirect to HTTPS\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.redirectToHttps(withDefaults());\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        redirectToHttps { }\n    }\n}\n----\n======\n\nYou can wrap the configuration can be wrapped around an `if` statement to be turned on only in production.\nAlternatively, you can enable it by looking for a property about the request that happens only in production.\nFor example, if the production environment adds a header named `X-Forwarded-Proto`, you should use the following Java Configuration:\n\n.Redirect to HTTPS when X-Forwarded\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.redirectToHttps((redirect) -> redirect\n\t\t\t.httpsRedirectWhen((e) -> e.getRequest().getHeaders().containsKey(\"X-Forwarded-Proto\"))\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        redirectToHttps {\n            httpsRedirectWhen {\n                it.request.headers.containsKey(\"X-Forwarded-Proto\")\n            }\n        }\n    }\n}\n----\n======\n\n[[webflux-hsts]]\n== Strict Transport Security\n\nSpring Security provides support for xref:servlet/exploits/headers.adoc#servlet-headers-hsts[Strict Transport Security] and enables it by default.\n\n[[webflux-http-proxy-server]]\n== Proxy Server Configuration\n\nSpring Security xref:features/exploits/http.adoc#http-proxy-server[integrates with proxy servers].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/exploits/index.adoc",
    "content": "= Protection Against Exploits\n\nSpring Security provides protection against numerous exploits.\nThis section discusses WebFlux specific support for:\n\n* xref:reactive/exploits/csrf.adoc[CSRF]\n* xref:reactive/exploits/headers.adoc[Headers]\n* xref:reactive/exploits/http.adoc[HTTP Requests]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/getting-started.adoc",
    "content": "[[getting-started]]\n= Getting Started with WebFlux Applications\n\nThis section covers the minimum setup for how to use Spring Security with Spring Boot in a reactive application.\n\n[NOTE]\n====\nThe completed application can be found {gh-samples-url}/reactive/webflux/java/hello-security[in our samples repository].\nFor your convenience, you can download a minimal Reactive Spring Boot + Spring Security application by https://start.spring.io/starter.zip?type=maven-project&language=java&packaging=jar&jvmVersion=1.8&groupId=example&artifactId=hello-security&name=hello-security&description=Hello%20Security&packageName=example.hello-security&dependencies=webflux,security[clicking here].\n====\n\n[[dependencies]]\n== Updating Dependencies\n\nYou can add Spring Security to your Spring Boot project by adding `spring-boot-starter-security`.\n\n[tabs]\n======\nMaven::\n+\n[source,xml,role=\"primary\"]\n----\n<dependency>\n    <groupId>org.springframework.boot</groupId>\n    <artifactId>spring-boot-starter-security</artifactId>\n</dependency>\n----\n\nGradle::\n+\n[source,groovy,role=\"secondary\"]\n----\n    implementation 'org.springframework.boot:spring-boot-starter-security'\n----\n======\n\n\n[[servlet-hello-starting]]\n== Starting Hello Spring Security Boot\n\nYou can now {spring-boot-reference-url}reference/using/running-your-application.html#using.running-your-application.with-the-maven-plugin[run the Spring Boot application] by using the Maven Plugin's `run` goal.\nThe following example shows how to do so (and the beginning of the output from doing so):\n\n\n.Running Spring Boot Application\n[tabs]\n======\nMaven::\n+\n[source,bash,role=\"primary\"]\n----\n$ ./mvnw spring-boot:run\n...\nINFO 23689 --- [  restartedMain] .s.s.UserDetailsServiceAutoConfiguration :\n\nUsing generated security password: 8e557245-73e2-4286-969a-ff57fe326336\n\n...\n----\n\nGradle::\n+\n[source,bash,role=\"secondary\"]\n----\n$ ./gradlew bootRun\n...\nINFO 23689 --- [  restartedMain] .s.s.UserDetailsServiceAutoConfiguration :\n\nUsing generated security password: 8e557245-73e2-4286-969a-ff57fe326336\n\n...\n----\n======\n\n[[authenticating]]\n== Authenticating\n\nYou can access the application at http://localhost:8080/ which will redirect the browser to the default log in page. You can provide the default username of `user` with the randomly generated password that is logged to the console. The browser is then taken to the originally requested page.\n\nTo log out you can visit http://localhost:8080/logout and then confirming you wish to log out.\n\n[[auto-configuration]]\n== Spring Boot Auto Configuration\n\nSpring Boot automatically adds Spring Security which requires all requests be authenticated. It also generates a user with a randomly generated password that is logged to the console which can be used to authenticate using form or basic authentication.\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/index.adoc",
    "content": "= Reactive Applications\n\nReactive applications work very differently than xref:servlet/index.adoc#servlet-applications[Servlet Applications].\nThis section discusses how Spring Security works with reactive applications which are typically written using Spring's WebFlux.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/integrations/cors.adoc",
    "content": "[[webflux-cors]]\n= CORS\n\nSpring Framework provides {spring-framework-reference-url}web/webflux-cors.html#webflux-cors-intro[first class support for CORS].\nCORS must be processed before Spring Security because the pre-flight request will not contain any cookies (i.e. the `JSESSIONID`).\nIf the request does not contain any cookies and Spring Security is first, the request will determine the user is not authenticated (since there are no cookies in the request) and reject it.\n\nThe easiest way to ensure that CORS is handled first is to use the `CorsWebFilter`.\nUsers can integrate the `CorsWebFilter` with Spring Security by providing a `CorsConfigurationSource`.\nFor example, the following will integrate CORS support within Spring Security:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nUrlBasedCorsConfigurationSource corsConfigurationSource() {\n\tCorsConfiguration configuration = new CorsConfiguration();\n\tconfiguration.setAllowedOrigins(Arrays.asList(\"https://example.com\"));\n\tconfiguration.setAllowedMethods(Arrays.asList(\"GET\",\"POST\"));\n\tUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n\tsource.registerCorsConfiguration(\"/**\", configuration);\n\treturn source;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun corsConfigurationSource(): UrlBasedCorsConfigurationSource {\n    val configuration = CorsConfiguration()\n    configuration.allowedOrigins = listOf(\"https://example.com\")\n    configuration.allowedMethods = listOf(\"GET\", \"POST\")\n    val source = UrlBasedCorsConfigurationSource()\n    source.registerCorsConfiguration(\"/**\", configuration)\n    return source\n}\n----\n======\n\nThe following will disable the CORS integration within Spring Security:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.cors((cors) -> cors.disable());\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        // ...\n        cors {\n            disable()\n        }\n    }\n}\n----\n======\n\n[WARNING]\n====\nCORS is a browser-based security feature.\nBy disabling CORS in Spring Security, you are not removing CORS protection from your browser.\nInstead, you are removing CORS support from Spring Security, and users will not be able to interact with your Spring backend from a cross-origin browser application.\nTo fix CORS errors in your application, you must enable CORS support, and provide an appropriate configuration source.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/integrations/observability.adoc",
    "content": "[[webflux-observability]]\n= Observability\n\nSpring Security integrates with Spring Observability out-of-the-box for tracing; though it's also quite simple to configure for gathering metrics.\n\n[[webflux-observability-tracing]]\n== Tracing\n\nWhen an `ObservationRegistry` bean is present, Spring Security creates traces for:\n\n* the filter chain\n* the `ReactiveAuthenticationManager`, and\n* the `ReactiveAuthorizationManager`\n\n[[webflux-observability-tracing-boot]]\n=== Boot Integration\n\nFor example, consider a simple Boot application:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@SpringBootApplication\npublic class MyApplication {\n\t@Bean\n\tpublic ReactiveUserDetailsService userDetailsService() {\n\t\treturn new MapReactiveUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tObservationRegistryCustomizer<ObservationRegistry> addTextHandler() {\n\t\treturn (registry) -> registry.observationConfig().observationHandler(new ObservationTextPublisher());\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ListenerSamplesApplication.class, args);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@SpringBootApplication\nclass MyApplication {\n\t@Bean\n\tfun userDetailsService(): ReactiveUserDetailsService {\n\t\tMapReactiveUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tfun addTextHandler(): ObservationRegistryCustomizer<ObservationRegistry> {\n\t\treturn registry: ObservationRegistry -> registry.observationConfig()\n\t\t\t\t.observationHandler(ObservationTextPublisher());\n\t}\n\n\tfun main(args: Array<String>) {\n\t\trunApplication<MyApplication>(*args)\n\t}\n}\n----\n======\n\nAnd a corresponding request:\n\n[source,bash]\n----\n?> http -a user:password :8080\n----\n\nWill produce the following output (indentation added for clarity):\n\n[source,bash]\n----\nSTART - name='http.server.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@5dfdb78', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.00191856, duration(nanos)=1918560.0, startTimeNanos=101177265022745}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@121549e0']\n\tSTART - name='spring.security.http.chains', contextualName='spring.security.http.chains.before', error='null', lowCardinalityKeyValues=[chain.size='14', filter.section='before'], highCardinalityKeyValues=[request.line='/'], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@3932a48c', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=4.65777E-4, duration(nanos)=465777.0, startTimeNanos=101177276300777}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@562db70f']\n\tSTOP - name='spring.security.http.chains', contextualName='spring.security.http.chains.before', error='null', lowCardinalityKeyValues=[chain.size='14', filter.section='before'], highCardinalityKeyValues=[request.line='/'], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@3932a48c', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.003733105, duration(nanos)=3733105.0, startTimeNanos=101177276300777}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@562db70f']\n\t\tSTART - name='spring.security.authentications', contextualName='null', error='null', lowCardinalityKeyValues=[authentication.failure.type='Optional', authentication.method='UserDetailsRepositoryReactiveAuthenticationManager', authentication.request.type='UsernamePasswordAuthenticationToken'], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@574ba6cd', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=3.21015E-4, duration(nanos)=321015.0, startTimeNanos=101177336038417}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@49202cc7']\n\t\tSTOP - name='spring.security.authentications', contextualName='null', error='null', lowCardinalityKeyValues=[authentication.failure.type='Optional', authentication.method='UserDetailsRepositoryReactiveAuthenticationManager', authentication.request.type='UsernamePasswordAuthenticationToken', authentication.result.type='UsernamePasswordAuthenticationToken'], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@574ba6cd', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.37574992, duration(nanos)=3.7574992E8, startTimeNanos=101177336038417}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@49202cc7']\n\t\tSTART - name='spring.security.authorizations', contextualName='null', error='null', lowCardinalityKeyValues=[object.type='SecurityContextServerWebExchange'], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@6f837332', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=2.65687E-4, duration(nanos)=265687.0, startTimeNanos=101177777941381}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@7f5bc7cb']\n\t\tSTOP - name='spring.security.authorizations', contextualName='null', error='null', lowCardinalityKeyValues=[authorization.decision='true', object.type='SecurityContextServerWebExchange'], highCardinalityKeyValues=[authentication.authorities='[app]', authorization.decision.details='AuthorizationDecision [granted=true]'], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@6f837332', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.039239047, duration(nanos)=3.9239047E7, startTimeNanos=101177777941381}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@7f5bc7cb']\n\t\tSTART - name='spring.security.http.secured.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@2f33dfae', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=3.1775E-4, duration(nanos)=317750.0, startTimeNanos=101177821377592}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@63b0d28f']\n\t\tSTOP - name='spring.security.http.secured.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@2f33dfae', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.219901971, duration(nanos)=2.19901971E8, startTimeNanos=101177821377592}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@63b0d28f']\n\tSTART - name='spring.security.http.chains', contextualName='spring.security.http.chains.after', error='null', lowCardinalityKeyValues=[chain.size='14', filter.section='after'], highCardinalityKeyValues=[request.line='/'], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@40b25623', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=3.25118E-4, duration(nanos)=325118.0, startTimeNanos=101178044824275}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@3b6cec2']\n\tSTOP - name='spring.security.http.chains', contextualName='spring.security.http.chains.after', error='null', lowCardinalityKeyValues=[chain.size='14', filter.section='after'], highCardinalityKeyValues=[request.line='/'], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@40b25623', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.001693146, duration(nanos)=1693146.0, startTimeNanos=101178044824275}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@3b6cec2']\nSTOP - name='http.server.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext='io.micrometer.tracing.handler.TracingObservationHandler$TracingContext@5dfdb78', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.784320641, duration(nanos)=7.84320641E8, startTimeNanos=101177265022745}', class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@121549e0']\n----\n\n[[webflux-observability-tracing-manual-configuration]]\n=== Manual Configuration\n\nFor a non-Spring Boot application, or to override the existing Boot configuration, you can publish your own `ObservationRegistry` and Spring Security will still pick it up.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@SpringBootApplication\npublic class MyApplication {\n\t@Bean\n\tpublic ReactiveUserDetailsService userDetailsService() {\n\t\treturn new MapReactiveUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tObservationRegistry observationRegistry() {\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(new ObservationTextPublisher());\n\t\treturn registry;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ListenerSamplesApplication.class, args);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@SpringBootApplication\nclass MyApplication {\n\t@Bean\n\tfun userDetailsService(): ReactiveUserDetailsService {\n\t\tMapReactiveUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tfun observationRegistry(): ObservationRegistry {\n\t\tObservationRegistry registry = ObservationRegistry.create()\n\t\tregistry.observationConfig().observationHandler(ObservationTextPublisher())\n\t\treturn registry\n\t}\n\n\tfun main(args: Array<String>) {\n\t\trunApplication<MyApplication>(*args)\n\t}\n}\n----\n\nXml::\n+\n[source,kotlin,role=\"secondary\"]\n----\n<sec:http auto-config=\"true\" observation-registry-ref=\"ref\">\n\t<sec:intercept-url pattern=\"/**\" access=\"authenticated\"/>\n</sec:http>\n\n<!-- define and configure ObservationRegistry bean -->\n----\n======\n\n[[webflux-observability-tracing-disable]]\n=== Disabling Observability\n\nIf you don't want any Spring Security observations, in a Spring Boot application you can publish a `ObservationRegistry.NOOP` `@Bean`.\nHowever, this may turn off observations for more than just Spring Security.\n\nInstead, you can publish a `SecurityObservationSettings` like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityObservationSettings noSpringSecurityObservations() {\n\treturn SecurityObservationSettings.noObservations();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun noSpringSecurityObservations(): SecurityObservationSettings {\n\treturn SecurityObservationSettings.noObservations()\n}\n----\n======\n\nand then Spring Security will not wrap any filter chains, authentications, or authorizations in their `ObservationXXX` counterparts.\n\n[TIP]\nThere is no facility for disabling observations with XML support.\nInstead, simply do not set the `observation-registry-ref` attribute.\n\nYou can also disable security for only a subset of Security's observations.\nFor example, the `SecurityObservationSettings` bean excludes the filter chain observations by default.\nSo, you can also do:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityObservationSettings defaultSpringSecurityObservations() {\n\treturn SecurityObservationSettings.withDefaults().build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun defaultSpringSecurityObservations(): SecurityObservationSettings {\n\treturn SecurityObservationSettings.withDefaults().build()\n}\n----\n======\n\nOr you can turn on and off observations individually, based on the defaults:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityObservationSettings allSpringSecurityObservations() {\n\treturn SecurityObservationSettings.withDefaults()\n            .shouldObserveFilterChains(true).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun allSpringSecurityObservations(): SecurityObservationSettings {\n    return SecurityObservabilityDefaults.builder()\n            .shouldObserveFilterChains(true).build()\n}\n----\n======\n\n[NOTE]\n=====\nFor backward compatibility, all Spring Security observations are made unless a `SecurityObservationSettings` is published.\n=====\n\n[[webflux-observability-tracing-listing]]\n=== Trace Listing\n\nSpring Security tracks the following spans on each request:\n\n1. `spring.security.http.requests` - a span that wraps the entire filter chain, including the request\n2. `spring.security.http.chains.before` - a span that wraps the receiving part of the security filters\n3. `spring.security.http.chains.after` - a span that wraps the returning part of the security filters\n4. `spring.security.http.secured.requests` - a span that wraps the now-secured application request\n5. `spring.security.http.unsecured.requests` - a span that wraps requests that Spring Security does not secure\n6. `spring.security.authentications` - a span that wraps authentication attempts\n7. `spring.security.authorizations` - a span that wraps authorization attempts\n\n[TIP]\n`spring.security.http.chains.before` + `spring.security.http.secured.requests` + `spring.security.http.chains.after` = `spring.security.http.requests` +\n`spring.security.http.chains.before` + `spring.security.http.chains.after` = Spring Security's part of the request\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/integrations/rsocket.adoc",
    "content": "[[rsocket]]\n= RSocket Security\n\nSpring Security's RSocket support relies on a `SocketAcceptorInterceptor`.\nThe main entry point into security is in `PayloadSocketAcceptorInterceptor`, which adapts the RSocket APIs to allow intercepting a `PayloadExchange` with `PayloadInterceptor` implementations.\n\nThe following example shows a minimal RSocket Security configuration:\n\n* Hello RSocket {gh-samples-url}/reactive/rsocket/hello-security[hellorsocket]\n* https://github.com/rwinch/spring-flights/tree/security[Spring Flights]\n\n\n== Minimal RSocket Security Configuration\n\nYou can find a minimal RSocket Security configuration below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableRSocketSecurity\npublic class HelloRSocketSecurityConfig {\n\n\t@Bean\n\tpublic MapReactiveUserDetailsService userDetailsService() {\n\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"user\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\treturn new MapReactiveUserDetailsService(user);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableRSocketSecurity\nopen class HelloRSocketSecurityConfig {\n    @Bean\n    open fun userDetailsService(): MapReactiveUserDetailsService {\n        val user = User.withDefaultPasswordEncoder()\n            .username(\"user\")\n            .password(\"user\")\n            .roles(\"USER\")\n            .build()\n        return MapReactiveUserDetailsService(user)\n    }\n}\n----\n======\n\nThis configuration enables <<rsocket-authentication-simple,simple authentication>> and sets up <<rsocket-authorization,rsocket-authorization>> to require an authenticated user for any request.\n\n== Adding SecuritySocketAcceptorInterceptor\n\nFor Spring Security to work, we need to apply `SecuritySocketAcceptorInterceptor` to the `ServerRSocketFactory`.\nDoing so connects our `PayloadSocketAcceptorInterceptor` with the RSocket infrastructure.\n\nSpring Boot registers it automatically in `RSocketSecurityAutoConfiguration` when you include {gh-samples-url}/reactive/rsocket/hello-security/build.gradle[the correct dependencies].\n\nOr, if you are not using Boot's auto-configuration, you can register it manually in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nRSocketServerCustomizer springSecurityRSocketSecurity(SecuritySocketAcceptorInterceptor interceptor) {\n    return (server) -> server.interceptors((registry) -> registry.forSocketAcceptor(interceptor));\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityRSocketSecurity(interceptor: SecuritySocketAcceptorInterceptor): RSocketServerCustomizer {\n    return RSocketServerCustomizer { server ->\n        server.interceptors { registry ->\n            registry.forSocketAcceptor(interceptor)\n        }\n    }\n}\n----\n======\n\nTo customize the interceptor itself, use `RSocketSecurity` to add <<rsocket-authentication,authentication>> and <<rsocket-authorization,authorization>>.\n\n[[rsocket-authentication]]\n== RSocket Authentication\n\nRSocket authentication is performed with `AuthenticationPayloadInterceptor`, which acts as a controller to invoke a `ReactiveAuthenticationManager` instance.\n\n[[rsocket-authentication-setup-vs-request]]\n=== Authentication at Setup versus Request Time\n\nGenerally, authentication can occur at setup time or at request time or both.\n\nAuthentication at setup time makes sense in a few scenarios.\nA common scenarios is when a single user (such as a mobile connection) uses an RSocket connection.\nIn this case, only a single user uses the connection, so authentication can be done once at connection time.\n\nIn a scenario where the RSocket connection is shared, it makes sense to send credentials on each request.\nFor example, a web application that connects to an RSocket server as a downstream service would make a single connection that all users use.\nIn this case, if the RSocket server needs to perform authorization based on the web application's users credentials, authentication for each request makes sense.\n\nIn some scenarios, authentication at both setup and for each request makes sense.\nConsider a web application, as described previously.\nIf we need to restrict the connection to the web application itself, we can provide a credential with a `SETUP` authority at connection time.\nThen each user can have different authorities but not the `SETUP` authority.\nThis means that individual users can make requests but not make additional connections.\n\n[[rsocket-authentication-simple]]\n=== Simple Authentication\n\nSpring Security has support for the https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Simple.md[Simple Authentication Metadata Extension].\n\n[NOTE]\n====\nBasic Authentication evolved into Simple Authentication and is only supported for backward compatibility.\nSee `RSocketSecurity.basicAuthentication(Customizer)` for setting it up.\n====\n\nThe RSocket receiver can decode the credentials by using `AuthenticationPayloadExchangeConverter`, which is automatically setup by using the `simpleAuthentication` portion of the DSL.\nThe following example shows an explicit configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nPayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n\trsocket\n\t\t.authorizePayload(authorize ->\n\t\t\tauthorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t\t.anyExchange().permitAll()\n\t\t)\n\t\t.simpleAuthentication(Customizer.withDefaults());\n\treturn rsocket.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInterceptor {\n    rsocket\n        .authorizePayload { authorize -> authorize\n                .anyRequest().authenticated()\n                .anyExchange().permitAll()\n        }\n        .simpleAuthentication(withDefaults())\n    return rsocket.build()\n}\n----\n======\n\nThe RSocket sender can send credentials by using `SimpleAuthenticationEncoder`, which you can add to Spring's `RSocketStrategies`.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nRSocketStrategies.Builder strategies = ...;\nstrategies.encoder(new SimpleAuthenticationEncoder());\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nvar strategies: RSocketStrategies.Builder = ...\nstrategies.encoder(SimpleAuthenticationEncoder())\n----\n======\n\nYou can then use it to send a username and password to the receiver in the setup:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMimeType authenticationMimeType =\n\tMimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());\nUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"user\", \"password\");\nMono<RSocketRequester> requester = RSocketRequester.builder()\n\t.setupMetadata(credentials, authenticationMimeType)\n\t.rsocketStrategies(strategies.build())\n\t.connectTcp(host, port);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval authenticationMimeType: MimeType =\n    MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)\nval credentials = UsernamePasswordMetadata(\"user\", \"password\")\nval requester: Mono<RSocketRequester> = RSocketRequester.builder()\n    .setupMetadata(credentials, authenticationMimeType)\n    .rsocketStrategies(strategies.build())\n    .connectTcp(host, port)\n----\n======\n\nAlternatively or additionally, a username and password can be sent in a request.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMono<RSocketRequester> requester;\nUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"user\", \"password\");\n\npublic Mono<AirportLocation> findRadar(String code) {\n\treturn this.requester.flatMap(req ->\n\t\treq.route(\"find.radar.{code}\", code)\n\t\t\t.metadata(credentials, authenticationMimeType)\n\t\t\t.retrieveMono(AirportLocation.class)\n\t);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.messaging.rsocket.retrieveMono\n\n// ...\n\nvar requester: Mono<RSocketRequester>? = null\nvar credentials = UsernamePasswordMetadata(\"user\", \"password\")\n\nopen fun findRadar(code: String): Mono<AirportLocation> {\n    return requester!!.flatMap { req ->\n        req.route(\"find.radar.{code}\", code)\n            .metadata(credentials, authenticationMimeType)\n            .retrieveMono<AirportLocation>()\n    }\n}\n----\n======\n\n[[rsocket-authentication-jwt]]\n=== JWT\n\nSpring Security has support for the https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Bearer.md[Bearer Token Authentication Metadata Extension].\nThe support comes in the form of authenticating a JWT (determining that the JWT is valid) and then using the JWT to make authorization decisions.\n\nThe RSocket receiver can decode the credentials by using `BearerPayloadExchangeConverter`, which is automatically setup by using the `jwt` portion of the DSL.\nThe following listing shows an example configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nPayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {\n\trsocket\n\t\t.authorizePayload(authorize ->\n\t\t\tauthorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t.anyExchange().permitAll()\n\t\t)\n\t\t.jwt(Customizer.withDefaults());\n\treturn rsocket.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun rsocketInterceptor(rsocket: RSocketSecurity): PayloadSocketAcceptorInterceptor {\n    rsocket\n        .authorizePayload { authorize -> authorize\n            .anyRequest().authenticated()\n            .anyExchange().permitAll()\n        }\n        .jwt(withDefaults())\n    return rsocket.build()\n}\n----\n======\n\nThe configuration above relies on the existence of a `ReactiveJwtDecoder` `@Bean` being present.\nAn example of creating one from the issuer can be found below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nReactiveJwtDecoder jwtDecoder() {\n\treturn ReactiveJwtDecoders\n\t\t.fromIssuerLocation(\"https://example.com/auth/realms/demo\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return ReactiveJwtDecoders\n        .fromIssuerLocation(\"https://example.com/auth/realms/demo\")\n}\n----\n======\n\nThe RSocket sender does not need to do anything special to send the token, because the value is a simple `String`.\nThe following example sends the token at setup time:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMimeType authenticationMimeType =\n\tMimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());\nBearerTokenMetadata token = ...;\nMono<RSocketRequester> requester = RSocketRequester.builder()\n\t.setupMetadata(token, authenticationMimeType)\n\t.connectTcp(host, port);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval authenticationMimeType: MimeType =\n    MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)\nval token: BearerTokenMetadata = ...\n\nval requester = RSocketRequester.builder()\n    .setupMetadata(token, authenticationMimeType)\n    .connectTcp(host, port)\n----\n======\n\nAlternatively or additionally, you can send the token in a request:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMimeType authenticationMimeType =\n\tMimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());\nMono<RSocketRequester> requester;\nBearerTokenMetadata token = ...;\n\npublic Mono<AirportLocation> findRadar(String code) {\n\treturn this.requester.flatMap(req ->\n\t\treq.route(\"find.radar.{code}\", code)\n\t        .metadata(token, authenticationMimeType)\n\t\t\t.retrieveMono(AirportLocation.class)\n\t);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval authenticationMimeType: MimeType =\n    MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.string)\nvar requester: Mono<RSocketRequester>? = null\nval token: BearerTokenMetadata = ...\n\nopen fun findRadar(code: String): Mono<AirportLocation> {\n    return this.requester!!.flatMap { req ->\n        req.route(\"find.radar.{code}\", code)\n            .metadata(token, authenticationMimeType)\n            .retrieveMono<AirportLocation>()\n    }\n}\n----\n======\n\n[[rsocket-authorization]]\n== RSocket Authorization\n\nRSocket authorization is performed with `AuthorizationPayloadInterceptor`, which acts as a controller to invoke a `ReactiveAuthorizationManager` instance.\nYou can use the DSL to set up authorization rules based upon the `PayloadExchange`.\nThe following listing shows an example configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nrsocket\n\t.authorizePayload(authz ->\n\t\tauthz\n\t\t\t.setup().hasRole(\"SETUP\") // <1>\n\t\t\t.route(\"fetch.profile.me\").authenticated() // <2>\n\t\t\t.matcher((payloadExchange) -> payloadExchange(payloadExchange)) // <3>\n\t\t\t\t.hasRole(\"CUSTOM\")\n\t\t\t.route(\"fetch.profile.{username}\") // <4>\n\t\t\t\t.access((authentication, context) -> checkFriends(authentication, context))\n\t\t\t.anyRequest().authenticated() // <5>\n\t\t\t.anyExchange().permitAll() // <6>\n\t);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nrsocket\n    .authorizePayload { authz ->\n        authz\n            .setup().hasRole(\"SETUP\") // <1>\n            .route(\"fetch.profile.me\").authenticated() // <2>\n            .matcher { payloadExchange -> isMatch(payloadExchange) } // <3>\n            .hasRole(\"CUSTOM\")\n            .route(\"fetch.profile.{username}\") // <4>\n            .access { authentication, context -> checkFriends(authentication, context) }\n            .anyRequest().authenticated() // <5>\n            .anyExchange().permitAll()\n    } // <6>\n----\n======\n<1> Setting up a connection requires the `ROLE_SETUP` authority.\n<2> If the route is `fetch.profile.me`, authorization only requires the user to be authenticated.\n<3> In this rule, we set up a custom matcher, where authorization requires the user to have the `ROLE_CUSTOM` authority.\n<4> This rule uses custom authorization.\nThe matcher expresses a variable with a name of `username` that is made available in the `context`.\nA custom authorization rule is exposed in the `checkFriends` method.\n<5> This rule ensures that a request that does not already have a rule requires the user to be authenticated.\nA request is where the metadata is included.\nIt would not include additional payloads.\n<6> This rule ensures that any exchange that does not already have a rule is allowed for anyone.\nIn this example, it means that payloads that have no metadata also have no authorization rules.\n\nNote that authorization rules are performed in order.\nOnly the first authorization rule that matches is invoked.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/client/authorization-grants.adoc",
    "content": "[[oauth2-client-authorization-grants]]\n= [[oauth2Client-auth-grant-support]]Authorization Grant Support\n\nThis section describes Spring Security's support for authorization grants.\n\n[[oauth2-client-authorization-code]]\n== [[oauth2Client-auth-code-grant]]Authorization Code\n\n[NOTE]\n====\nPlease refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.1[Authorization Code] grant.\n====\n\n[[oauth2-client-authorization-code-authorization]]\n=== Obtaining Authorization\n\n[NOTE]\n====\nPlease refer to the https://tools.ietf.org/html/rfc6749#section-4.1.1[Authorization Request/Response] protocol flow for the Authorization Code grant.\n====\n\n[[oauth2-client-authorization-code-authorization-request]]\n=== Initiating the Authorization Request\n\nThe `OAuth2AuthorizationRequestRedirectWebFilter` uses a `ServerOAuth2AuthorizationRequestResolver` to resolve an `OAuth2AuthorizationRequest` and initiate the Authorization Code grant flow by redirecting the end-user's user-agent to the Authorization Server's Authorization Endpoint.\n\nThe primary role of the `ServerOAuth2AuthorizationRequestResolver` is to resolve an `OAuth2AuthorizationRequest` from the provided web request.\nThe default implementation `DefaultServerOAuth2AuthorizationRequestResolver` matches on the (default) path `+/oauth2/authorization/{registrationId}+` extracting the `registrationId` and using it to build the `OAuth2AuthorizationRequest` for the associated `ClientRegistration`.\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml,attrs=\"-attributes\"]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            authorization-grant-type: authorization_code\n            redirect-uri: \"{baseUrl}/authorized/okta\"\n            scope: read, write\n        provider:\n          okta:\n            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize\n            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token\n----\n\nA request with the base path `/oauth2/authorization/okta` will initiate the Authorization Request redirect by the `OAuth2AuthorizationRequestRedirectWebFilter` and ultimately start the Authorization Code grant flow.\n\n[NOTE]\n====\nThe `AuthorizationCodeReactiveOAuth2AuthorizedClientProvider` is an implementation of `ReactiveOAuth2AuthorizedClientProvider` for the Authorization Code grant,\nwhich also initiates the Authorization Request redirect by the `OAuth2AuthorizationRequestRedirectWebFilter`.\n====\n\nIf the OAuth 2.0 Client is a https://tools.ietf.org/html/rfc6749#section-2.1[Public Client], then configure the OAuth 2.0 Client registration as follows:\n\n[source,yaml,attrs=\"-attributes\"]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-authentication-method: none\n            authorization-grant-type: authorization_code\n            redirect-uri: \"{baseUrl}/authorized/okta\"\n            # ...\n----\n\nPublic Clients are supported using https://tools.ietf.org/html/rfc7636[Proof Key for Code Exchange] (PKCE).\nIf the client is running in an untrusted environment (e.g. native application or web browser-based application) and therefore incapable of maintaining the confidentiality of its credentials, PKCE will automatically be used when the following conditions are true:\n\n. `client-secret` is omitted (or empty)\n. `client-authentication-method` is set to `none` (`ClientAuthenticationMethod.NONE`)\n\nor\n\n. When `ClientRegistration.clientSettings.requireProofKey` is `true` (in this case `ClientRegistration.authorizationGrantType` must be `authorization_code`)\n\n[TIP]\n====\nIf the OAuth 2.0 Provider doesn't support PKCE for https://tools.ietf.org/html/rfc6749#section-2.1[Confidential Clients], you need to disable it by setting `ClientRegistration.clientSettings.requireProofKey` to `false`.\n====\n\n[[oauth2-client-authorization-code-redirect-uri]]\n[[oauth2Client-auth-code-redirect-uri]]The `DefaultServerOAuth2AuthorizationRequestResolver` also supports `URI` template variables for the `redirect-uri` using `UriComponentsBuilder`.\n\nThe following configuration uses all the supported `URI` template variables:\n\n[source,yaml,attrs=\"-attributes\"]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            # ...\n            redirect-uri: \"{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}\"\n            # ...\n----\n\n[NOTE]\n====\n`+{baseUrl}+` resolves to `+{baseScheme}://{baseHost}{basePort}{basePath}+`\n====\n\nConfiguring the `redirect-uri` with `URI` template variables is especially useful when the OAuth 2.0 Client is running behind a xref:features/exploits/http.adoc#http-proxy-server[Proxy Server].\nThis ensures that the `X-Forwarded-*` headers are used when expanding the `redirect-uri`.\n\n[[oauth2-client-authorization-code-authorization-request-resolver]]\n=== Customizing the Authorization Request\n\nOne of the primary use cases a `ServerOAuth2AuthorizationRequestResolver` can realize is the ability to customize the Authorization Request with additional parameters above the standard parameters defined in the OAuth 2.0 Authorization Framework.\n\nFor example, OpenID Connect defines additional OAuth 2.0 request parameters for the https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest[Authorization Code Flow] extending from the standard parameters defined in the https://tools.ietf.org/html/rfc6749#section-4.1.1[OAuth 2.0 Authorization Framework].\nOne of those extended parameters is the `prompt` parameter.\n\n[NOTE]\n====\nThe `prompt` parameter is optional. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for re-authentication and consent. The defined values are: `none`, `login`, `consent`, and `select_account`.\n====\n\nThe following example shows how to configure the `DefaultServerOAuth2AuthorizationRequestResolver` with a `Consumer<OAuth2AuthorizationRequest.Builder>` that customizes the Authorization Request for `oauth2Login()`, by including the request parameter `prompt=consent`.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Autowired\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t.authorizationRequestResolver(\n\t\t\t\t\tauthorizationRequestResolver(this.clientRegistrationRepository)\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\n\t\tDefaultServerOAuth2AuthorizationRequestResolver authorizationRequestResolver =\n\t\t\t\tnew DefaultServerOAuth2AuthorizationRequestResolver(\n\t\t\t\t\t\tclientRegistrationRepository);\n\t\tauthorizationRequestResolver.setAuthorizationRequestCustomizer(\n\t\t\t\tauthorizationRequestCustomizer());\n\n\t\treturn  authorizationRequestResolver;\n\t}\n\n\tprivate Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {\n\t\treturn customizer -> customizer\n\t\t\t\t\t.additionalParameters((params) -> params.put(\"prompt\", \"consent\"));\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n    @Autowired\n    private lateinit var customClientRegistrationRepository: ReactiveClientRegistrationRepository\n\n    @Bean\n    fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n            oauth2Login {\n                authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)\n            }\n        }\n\n        return http.build()\n    }\n\n    private fun authorizationRequestResolver(\n            clientRegistrationRepository: ReactiveClientRegistrationRepository): ServerOAuth2AuthorizationRequestResolver {\n        val authorizationRequestResolver = DefaultServerOAuth2AuthorizationRequestResolver(\n                clientRegistrationRepository)\n        authorizationRequestResolver.setAuthorizationRequestCustomizer(\n                authorizationRequestCustomizer())\n        return authorizationRequestResolver\n    }\n\n    private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {\n        return Consumer { customizer ->\n            customizer\n                .additionalParameters { params -> params[\"prompt\"] = \"consent\" }\n        }\n    }\n}\n----\n======\n\nFor the simple use case, where the additional request parameter is always the same for a specific provider, it may be added directly in the `authorization-uri` property.\n\nFor example, if the value for the request parameter `prompt` is always `consent` for the provider `okta`, than simply configure as follows:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        provider:\n          okta:\n            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent\n----\n\nThe preceding example shows the common use case of adding a custom parameter on top of the standard parameters.\nAlternatively, if your requirements are more advanced, you can take full control in building the Authorization Request URI by simply overriding the `OAuth2AuthorizationRequest.authorizationRequestUri` property.\n\n[TIP]\n====\n`OAuth2AuthorizationRequest.Builder.build()` constructs the `OAuth2AuthorizationRequest.authorizationRequestUri`, which represents the Authorization Request URI including all query parameters using the `application/x-www-form-urlencoded` format.\n====\n\nThe following example shows a variation of `authorizationRequestCustomizer()` from the preceding example, and instead overrides the `OAuth2AuthorizationRequest.authorizationRequestUri` property.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprivate Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {\n\treturn customizer -> customizer\n\t\t\t.authorizationRequestUri((uriBuilder) -> uriBuilder\n\t\t\t\t\t.queryParam(\"prompt\", \"consent\").build());\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nprivate fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {\n    return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->\n        customizer\n                .authorizationRequestUri { uriBuilder: UriBuilder ->\n                    uriBuilder\n                            .queryParam(\"prompt\", \"consent\").build()\n                }\n    }\n}\n----\n======\n\n[[oauth2-client-authorization-code-authorization-request-repository]]\n=== Storing the Authorization Request\n\nThe `ServerAuthorizationRequestRepository` is responsible for the persistence of the `OAuth2AuthorizationRequest` from the time the Authorization Request is initiated to the time the Authorization Response is received (the callback).\n\n[TIP]\n====\nThe `OAuth2AuthorizationRequest` is used to correlate and validate the Authorization Response.\n====\n\nThe default implementation of `ServerAuthorizationRequestRepository` is `WebSessionOAuth2ServerAuthorizationRequestRepository`, which stores the `OAuth2AuthorizationRequest` in the `WebSession`.\n\nIf you have a custom implementation of `ServerAuthorizationRequestRepository`, you may configure it as shown in the following example:\n\n.ServerAuthorizationRequestRepository Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2ClientSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.oauth2Client((oauth2) -> oauth2\n\t\t\t\t.authorizationRequestRepository(this.authorizationRequestRepository())\n\t\t\t\t// ...\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2ClientSecurityConfig {\n\n    @Bean\n    fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Client {\n                authorizationRequestRepository = authorizationRequestRepository()\n            }\n        }\n\n        return http.build()\n    }\n}\n----\n======\n\n[[oauth2-client-authorization-code-access-token]]\n=== Requesting an Access Token\n\n[NOTE]\n====\nPlease refer to the https://tools.ietf.org/html/rfc6749#section-4.1.3[Access Token Request/Response] protocol flow for the Authorization Code grant.\n====\n\nThe default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Authorization Code grant is `WebClientReactiveAuthorizationCodeTokenResponseClient`, which uses a `WebClient` for exchanging an authorization code for an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: authorization-code\n:grant-type: Authorization Code\n:class-name: WebClientReactiveAuthorizationCodeTokenResponseClient\n:grant-request: OAuth2AuthorizationCodeGrantRequest\n:leveloffset: +1\ninclude::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-authorization-code-access-token-response-client-dsl]]\n=== Customize using the DSL\n\nWhether you customize `{class-name}` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the DSL (as an alternative to <<oauth2-client-authorization-code-access-token-response-client-bean,publishing a bean>>) as shown in the following example:\n\n.Access Token Response Configuration via DSL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2ClientSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.oauth2Client((oauth2) -> oauth2\n\t\t\t\t.authenticationManager(this.authorizationCodeAuthenticationManager())\n\t\t\t\t// ...\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate ReactiveAuthenticationManager authorizationCodeAuthenticationManager() {\n\t\tWebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\t\tnew WebClientReactiveAuthorizationCodeTokenResponseClient();\n\t\t// ...\n\n\t\treturn new OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2ClientSecurityConfig {\n\n    @Bean\n    fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Client {\n                authenticationManager = authorizationCodeAuthenticationManager()\n            }\n        }\n\n        return http.build()\n    }\n\n    private fun authorizationCodeAuthenticationManager(): ReactiveAuthenticationManager {\n        val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()\n        // ...\n\n        return OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient)\n    }\n}\n----\n======\n\n[[oauth2-client-refresh-token]]\n== [[oauth2Client-refresh-token-grant]]Refresh Token\n\n[NOTE]\n====\nPlease refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.5[Refresh Token].\n====\n\n[[oauth2-client-refresh-token-access-token]]\n=== Refreshing an Access Token\n\n[NOTE]\n====\nPlease refer to the https://tools.ietf.org/html/rfc6749#section-6[Access Token Request/Response] protocol flow for the Refresh Token grant.\n====\n\nThe default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Refresh Token grant is `WebClientReactiveRefreshTokenTokenResponseClient`, which uses a `WebClient` when refreshing an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: refresh-token\n:grant-type: Refresh Token\n:class-name: WebClientReactiveRefreshTokenTokenResponseClient\n:grant-request: OAuth2RefreshTokenGrantRequest\n:leveloffset: +1\ninclude::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-refresh-token-authorized-client-provider-builder]]\n=== Customize using the Builder\n\nWhether you customize `WebClientReactiveRefreshTokenTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-refresh-token-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via Builder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Customize\nReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...\n\nReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken((configurer) -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))\n\t\t\t\t.build();\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Customize\nval refreshTokenTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...\n\nval authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n        .authorizationCode()\n        .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }\n        .build()\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n----\n======\n\n[NOTE]\n====\n`ReactiveOAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenReactiveOAuth2AuthorizedClientProvider`,\nwhich is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Refresh Token grant.\n====\n\nThe `OAuth2RefreshToken` may optionally be returned in the Access Token Response for the `authorization_code` grant type.\nIf the `OAuth2AuthorizedClient.getRefreshToken()` is available and the `OAuth2AuthorizedClient.getAccessToken()` is expired, it will automatically be refreshed by the `RefreshTokenReactiveOAuth2AuthorizedClientProvider`.\n\n[[oauth2-client-client-credentials]]\n== [[oauth2Client-client-creds-grant]]Client Credentials\n\n[NOTE]\n====\nPlease refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.4[Client Credentials] grant.\n====\n\n[[oauth2-client-client-credentials-access-token]]\n=== Requesting an Access Token\n\n[NOTE]\n====\nPlease refer to the https://tools.ietf.org/html/rfc6749#section-4.4.2[Access Token Request/Response] protocol flow for the Client Credentials grant.\n====\n\nThe default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Client Credentials grant is `WebClientReactiveClientCredentialsTokenResponseClient`, which uses a `WebClient` when requesting an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: client-credentials\n:grant-type: Client Credentials\n:class-name: WebClientReactiveClientCredentialsTokenResponseClient\n:grant-request: OAuth2ClientCredentialsGrantRequest\n:leveloffset: +1\ninclude::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-client-credentials-authorized-client-provider-builder]]\n=== Customize using the Builder\n\nWhether you customize `WebClientReactiveClientCredentialsTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-client-credentials-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via Builder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Customize\nReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...\n\nReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.clientCredentials((configurer) -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))\n\t\t\t\t.build();\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Customize\nval clientCredentialsTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...\n\nval authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n        .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }\n        .build()\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n----\n======\n\n[NOTE]\n====\n`ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsReactiveOAuth2AuthorizedClientProvider`, which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Client Credentials grant.\n====\n\n[[oauth2-client-client-credentials-authorized-client-manager]]\n=== Using the Access Token\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            authorization-grant-type: client_credentials\n            scope: read, write\n        provider:\n          okta:\n            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token\n----\n\n...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.clientCredentials()\n\t\t\t\t\t.build();\n\n\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ReactiveClientRegistrationRepository,\n        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {\n    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n            .clientCredentials()\n            .build()\n    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n\nYou may obtain the `OAuth2AccessToken` as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class OAuth2ClientController {\n\n\t@Autowired\n\tprivate ReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@GetMapping(\"/\")\n\tpublic Mono<String> index(Authentication authentication, ServerWebExchange exchange) {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attribute(ServerWebExchange.class.getName(), exchange)\n\t\t\t\t.build();\n\n\t\treturn this.authorizedClientManager.authorize(authorizeRequest)\n\t\t\t\t.map(OAuth2AuthorizedClient::getAccessToken)\n\t\t\t\t// ...\n\t\t\t\t.thenReturn(\"index\");\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass OAuth2ClientController {\n\n    @Autowired\n    private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager\n\n    @GetMapping(\"/\")\n    fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {\n        val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n                .principal(authentication)\n                .attribute(ServerWebExchange::class.java.name, exchange)\n                .build()\n\n        return authorizedClientManager.authorize(authorizeRequest)\n                .map { it.accessToken }\n                // ...\n                .thenReturn(\"index\")\n    }\n}\n----\n======\n\n[NOTE]\n====\n`ServerWebExchange` is an OPTIONAL attribute.\nIf not provided, it will be obtained from the https://projectreactor.io/docs/core/release/reference/#context[Reactor's Context] via the key `ServerWebExchange.class`.\n====\n\n[[oauth2-client-jwt-bearer]]\n== JWT Bearer\n\n[NOTE]\n====\nPlease refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the https://datatracker.ietf.org/doc/html/rfc7523[JWT Bearer] grant.\n====\n\n[[oauth2-client-jwt-bearer-access-token]]\n=== Requesting an Access Token\n\n[NOTE]\n====\nPlease refer to the https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[Access Token Request/Response] protocol flow for the JWT Bearer grant.\n====\n\nThe default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the JWT Bearer grant is `WebClientReactiveJwtBearerTokenResponseClient`, which uses a `WebClient` when requesting an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: jwt-bearer\n:grant-type: JWT Bearer\n:class-name: WebClientReactiveJwtBearerTokenResponseClient\n:grant-request: JwtBearerGrantRequest\n:leveloffset: +1\ninclude::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-jwt-bearer-authorized-client-provider-builder]]\n=== Customize using the Builder\n\nWhether you customize `WebClientReactiveJwtBearerTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-jwt-bearer-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via Builder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Customize\nReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...\n\nJwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerReactiveOAuth2AuthorizedClientProvider();\njwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);\n\nReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t\t.build();\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Customize\nval jwtBearerTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...\n\nval jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()\njwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient)\n\nval authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n        .provider(jwtBearerAuthorizedClientProvider)\n        .build()\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n----\n======\n\n[[oauth2-client-jwt-bearer-authorized-client-manager]]\n=== Using the Access Token\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer\n            scope: read\n        provider:\n          okta:\n            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token\n----\n\n...and the `OAuth2AuthorizedClientManager` `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tJwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =\n\t\t\tnew JwtBearerReactiveOAuth2AuthorizedClientProvider();\n\n\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t\t\t.build();\n\n\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ReactiveClientRegistrationRepository,\n        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {\n    val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()\n    val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n            .provider(jwtBearerAuthorizedClientProvider)\n            .build()\n    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n\nYou may obtain the `OAuth2AccessToken` as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class OAuth2ResourceServerController {\n\n\t@Autowired\n\tprivate ReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@GetMapping(\"/resource\")\n\tpublic Mono<String> resource(JwtAuthenticationToken jwtAuthentication, ServerWebExchange exchange) {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n\t\t\t\t.principal(jwtAuthentication)\n\t\t\t\t.build();\n\n\t\treturn this.authorizedClientManager.authorize(authorizeRequest)\n\t\t\t\t.map(OAuth2AuthorizedClient::getAccessToken)\n\t\t\t\t// ...\n\t\t\t\t.thenReturn(\"index\");\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass OAuth2ResourceServerController {\n\n    @Autowired\n    private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager\n\n    @GetMapping(\"/resource\")\n    fun resource(jwtAuthentication: JwtAuthenticationToken, exchange: ServerWebExchange): Mono<String> {\n        val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n                .principal(jwtAuthentication)\n                .build()\n        return authorizedClientManager.authorize(authorizeRequest)\n                .map { it.accessToken }\n                // ...\n                .thenReturn(\"index\")\n    }\n}\n----\n======\n\n[NOTE]\n====\n`JwtBearerReactiveOAuth2AuthorizedClientProvider` resolves the `Jwt` assertion via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.\n====\n\n[TIP]\n====\nIf you need to resolve the `Jwt` assertion from a different source, you can provide `JwtBearerReactiveOAuth2AuthorizedClientProvider.setJwtAssertionResolver()` with a custom `Function<OAuth2AuthorizationContext, Mono<Jwt>>`.\n====\n\n[[oauth2-client-token-exchange]]\n== [[oauth2Client-token-exchange-grant]]Token Exchange\n\n[NOTE]\n====\nPlease refer to OAuth 2.0 Token Exchange for further details on the https://datatracker.ietf.org/doc/html/rfc8693[Token Exchange] grant.\n====\n\n[[oauth2-client-token-exchange-access-token]]\n=== Requesting an Access Token\n\n[NOTE]\n====\nPlease refer to the https://datatracker.ietf.org/doc/html/rfc8693#section-2[Token Exchange Request and Response] protocol flow for the Token Exchange grant.\n====\n\nThe default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Token Exchange grant is `WebClientReactiveTokenExchangeTokenResponseClient`, which uses a `WebClient` when requesting an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: token-exchange\n:grant-type: Token Exchange\n:class-name: WebClientReactiveTokenExchangeTokenResponseClient\n:grant-request: TokenExchangeGrantRequest\n:leveloffset: +1\ninclude::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-token-exchange-authorized-client-provider-builder]]\n=== Customize using the Builder\n\nWhether you customize `WebClientReactiveTokenExchangeTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-token-exchange-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via Builder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Customize\nReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient = ...\n\nTokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = new TokenExchangeReactiveOAuth2AuthorizedClientProvider();\ntokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient);\n\nReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.provider(tokenExchangeAuthorizedClientProvider)\n\t\t\t\t.build();\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Customize\nval tokenExchangeTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> = ...\n\nval tokenExchangeAuthorizedClientProvider = TokenExchangeReactiveOAuth2AuthorizedClientProvider()\ntokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient)\n\nval authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n        .provider(tokenExchangeAuthorizedClientProvider)\n        .build()\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n----\n======\n\n[[oauth2-client-token-exchange-authorized-client-manager]]\n=== Using the Access Token\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange\n            scope: read\n        provider:\n          okta:\n            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token\n----\n\n...and the `OAuth2AuthorizedClientManager` `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tTokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =\n\t\t\tnew TokenExchangeReactiveOAuth2AuthorizedClientProvider();\n\n\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.provider(tokenExchangeAuthorizedClientProvider)\n\t\t\t\t\t.build();\n\n\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ReactiveClientRegistrationRepository,\n        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {\n    val tokenExchangeAuthorizedClientProvider = TokenExchangeReactiveOAuth2AuthorizedClientProvider()\n    val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n            .provider(tokenExchangeAuthorizedClientProvider)\n            .build()\n    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n\nYou may obtain the `OAuth2AccessToken` as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class OAuth2ResourceServerController {\n\n\t@Autowired\n\tprivate ReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@GetMapping(\"/resource\")\n\tpublic Mono<String> resource(JwtAuthenticationToken jwtAuthentication) {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n\t\t\t\t.principal(jwtAuthentication)\n\t\t\t\t.build();\n\n\t\treturn this.authorizedClientManager.authorize(authorizeRequest)\n\t\t\t\t.map(OAuth2AuthorizedClient::getAccessToken)\n\t\t\t\t// ...\n\t\t\t\t.thenReturn(\"index\");\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass OAuth2ResourceServerController {\n\n    @Autowired\n    private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager\n\n    @GetMapping(\"/resource\")\n    fun resource(jwtAuthentication: JwtAuthenticationToken): Mono<String> {\n        val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n                .principal(jwtAuthentication)\n                .build()\n        return authorizedClientManager.authorize(authorizeRequest)\n                .map { it.accessToken }\n                // ...\n                .thenReturn(\"index\")\n    }\n}\n----\n======\n\n[NOTE]\n====\n`TokenExchangeReactiveOAuth2AuthorizedClientProvider` resolves the subject token (as an `OAuth2Token`) via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.\nAn actor token is not resolved by default.\n====\n\n[TIP]\n====\nIf you need to resolve the subject token from a different source, you can provide `TokenExchangeReactiveOAuth2AuthorizedClientProvider.setSubjectTokenResolver()` with a custom `Function<OAuth2AuthorizationContext, Mono<OAuth2Token>>`.\n====\n\n[TIP]\n====\nIf you need to resolve an actor token, you can provide `TokenExchangeReactiveOAuth2AuthorizedClientProvider.setActorTokenResolver()` with a custom `Function<OAuth2AuthorizationContext, Mono<OAuth2Token>>`.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/client/authorized-clients.adoc",
    "content": "[[oauth2-client-additional-features]]\n= [[oauth2Client-additional-features]]Authorized Client Features\n\nThis section covers additional features provided by Spring Security for OAuth2 Client.\n\n[[oauth2-client-registered-authorized-client]]\n== [[oauth2Client-registered-authorized-client]]Resolving an Authorized Client\n\nThe `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.\nThis is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `ReactiveOAuth2AuthorizedClientManager` or `ReactiveOAuth2AuthorizedClientService`.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class OAuth2ClientController {\n\n\t@GetMapping(\"/\")\n\tpublic Mono<String> index(@RegisteredOAuth2AuthorizedClient(\"okta\") OAuth2AuthorizedClient authorizedClient) {\n\t\treturn Mono.just(authorizedClient.getAccessToken())\n\t\t\t\t...\n\t\t\t\t.thenReturn(\"index\");\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass OAuth2ClientController {\n    @GetMapping(\"/\")\n    fun index(@RegisteredOAuth2AuthorizedClient(\"okta\") authorizedClient: OAuth2AuthorizedClient): Mono<String> {\n        return Mono.just(authorizedClient.accessToken)\n                ...\n                .thenReturn(\"index\")\n    }\n}\n----\n======\n\nThe `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses a xref:reactive/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[ReactiveOAuth2AuthorizedClientManager] and therefore inherits it's capabilities.\n\n[[oauth2-client-web-client]]\n== [[oauth2Client-webclient-webflux]]WebClient integration for Reactive Environments\n\nThe OAuth 2.0 Client support integrates with `WebClient` using an `ExchangeFilterFunction`.\n\nThe `ServerOAuth2AuthorizedClientExchangeFilterFunction` provides a simple mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.\nIt directly uses an xref:reactive/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[ReactiveOAuth2AuthorizedClientManager] and therefore inherits the following capabilities:\n\n* An `OAuth2AccessToken` will be requested if the client has not yet been authorized.\n** `authorization_code` - triggers the Authorization Request redirect to initiate the flow\n** `client_credentials` - the access token is obtained directly from the Token Endpoint\n* If the `OAuth2AccessToken` is expired, it will be refreshed (or renewed) if a `ReactiveOAuth2AuthorizedClientProvider` is available to perform the authorization\n\nThe following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nWebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {\n\tServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =\n\t\t\tnew ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\treturn WebClient.builder()\n\t\t\t.filter(oauth2Client)\n\t\t\t.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {\n    val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n    return WebClient.builder()\n            .filter(oauth2Client)\n            .build()\n}\n----\n======\n\n[[oauth2-client-web-client-authorized-client]]\n=== Providing the Authorized Client\n\nThe `ServerOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).\n\nThe following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/\")\npublic Mono<String> index(@RegisteredOAuth2AuthorizedClient(\"okta\") OAuth2AuthorizedClient authorizedClient) {\n\tString resourceUri = ...\n\n\treturn webClient\n\t\t\t.get()\n\t\t\t.uri(resourceUri)\n\t\t\t.attributes(oauth2AuthorizedClient(authorizedClient))   <1>\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t...\n\t\t\t.thenReturn(\"index\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/\")\nfun index(@RegisteredOAuth2AuthorizedClient(\"okta\") authorizedClient: OAuth2AuthorizedClient): Mono<String> {\n    val resourceUri: String = ...\n\n    return webClient\n            .get()\n            .uri(resourceUri)\n            .attributes(oauth2AuthorizedClient(authorizedClient)) <1>\n            .retrieve()\n            .bodyToMono<String>()\n            ...\n            .thenReturn(\"index\")\n}\n----\n======\n\n<1> `oauth2AuthorizedClient()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.\n\nThe following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/\")\npublic Mono<String> index() {\n\tString resourceUri = ...\n\n\treturn webClient\n\t\t\t.get()\n\t\t\t.uri(resourceUri)\n\t\t\t.attributes(clientRegistrationId(\"okta\"))   <1>\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t...\n\t\t\t.thenReturn(\"index\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/\")\nfun index(): Mono<String> {\n    val resourceUri: String = ...\n\n    return webClient\n            .get()\n            .uri(resourceUri)\n            .attributes(clientRegistrationId(\"okta\"))  <1>\n            .retrieve()\n            .bodyToMono<String>()\n            ...\n            .thenReturn(\"index\")\n}\n----\n======\n<1> `clientRegistrationId()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.\n\n[[oauth2-client-web-client-default-authorized-client]]\n=== Defaulting the Authorized Client\n\nIf neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServerOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use depending on it's configuration.\n\nIf `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated using `ServerHttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.\n\nThe following code shows the specific configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nWebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {\n\tServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =\n\t\t\tnew ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\toauth2Client.setDefaultOAuth2AuthorizedClient(true);\n\treturn WebClient.builder()\n\t\t\t.filter(oauth2Client)\n\t\t\t.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {\n    val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n    oauth2Client.setDefaultOAuth2AuthorizedClient(true)\n    return WebClient.builder()\n            .filter(oauth2Client)\n            .build()\n}\n----\n======\n\n[WARNING]\n====\nIt is recommended to be cautious with this feature since all HTTP requests will receive the access token.\n====\n\nAlternatively, if `setDefaultClientRegistrationId(\"okta\")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.\n\nThe following code shows the specific configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nWebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {\n\tServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =\n\t\t\tnew ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\toauth2Client.setDefaultClientRegistrationId(\"okta\");\n\treturn WebClient.builder()\n\t\t\t.filter(oauth2Client)\n\t\t\t.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {\n    val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n    oauth2Client.setDefaultClientRegistrationId(\"okta\")\n    return WebClient.builder()\n            .filter(oauth2Client)\n            .build()\n}\n----\n======\n\n[WARNING]\n====\nIt is recommended to be cautious with this feature since all HTTP requests will receive the access token.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/client/client-authentication.adoc",
    "content": "[[oauth2-client-authentication]]\n= [[oauth2Client-client-auth-support]]Client Authentication Support\n\n[[oauth2-client-authentication-client-credentials]]\n== [[oauth2Client-client-credentials-auth]]Client Credentials\n\n[[oauth2-client-authentication-client-credentials-client-secret-basic]]\n=== Authenticate using `client_secret_basic`\n\nClient Authentication with HTTP Basic is supported out of the box and no customization is necessary to enable it.\nThe default implementation is provided by `DefaultOAuth2TokenRequestHeadersConverter`.\n\nGiven the following Spring Boot properties for an OAuth 2.0 client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: client-id\n            client-secret: client-secret\n            client-authentication-method: client_secret_basic\n            authorization-grant-type: authorization_code\n            ...\n----\n\nThe following example shows how to configure `WebClientReactiveAuthorizationCodeTokenResponseClient` to disable URL encoding of the client credentials:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nDefaultOAuth2TokenRequestHeadersConverter<OAuth2AuthorizationCodeGrantRequest> headersConverter =\n\t\tnew DefaultOAuth2TokenRequestHeadersConverter<>();\nheadersConverter.setEncodeClientCredentials(false);\n\nWebClientReactiveAuthorizationCodeTokenResponseClient tokenResponseClient =\n\t\tnew WebClientReactiveAuthorizationCodeTokenResponseClient();\ntokenResponseClient.setHeadersConverter(headersConverter);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval headersConverter = DefaultOAuth2TokenRequestHeadersConverter<OAuth2AuthorizationCodeGrantRequest>()\nheadersConverter.setEncodeClientCredentials(false)\n\nval tokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()\ntokenResponseClient.setHeadersConverter(headersConverter)\n----\n======\n\n[[oauth2-client-authentication-client-credentials-client-secret-post]]\n=== Authenticate using `client_secret_post`\n\nClient Authentication with client credentials included in the request-body is supported out of the box and no customization is necessary to enable it.\n\nThe following Spring Boot properties for an OAuth 2.0 client registration demonstrate the configuration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: client-id\n            client-secret: client-secret\n            client-authentication-method: client_secret_post\n            authorization-grant-type: authorization_code\n            ...\n----\n\n[[oauth2-client-authentication-jwt-bearer]]\n== [[oauth2Client-jwt-bearer-auth]]JWT Bearer\n\n[NOTE]\n====\nPlease refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on https://datatracker.ietf.org/doc/html/rfc7523#section-2.2[JWT Bearer] Client Authentication.\n====\n\nThe default implementation for JWT Bearer Client Authentication is `NimbusJwtClientAuthenticationParametersConverter`,\nwhich is a `Converter` that customizes the Token Request parameters by adding\na signed JSON Web Token (JWS) in the `client_assertion` parameter.\n\nThe `java.security.PrivateKey` or `javax.crypto.SecretKey` used for signing the JWS\nis supplied by the `com.nimbusds.jose.jwk.JWK` resolver associated with `NimbusJwtClientAuthenticationParametersConverter`.\n\n[[oauth2-client-authentication-jwt-bearer-private-key-jwt]]\n=== Authenticate using `private_key_jwt`\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-authentication-method: private_key_jwt\n            authorization-grant-type: authorization_code\n            ...\n----\n\nThe following example shows how to configure `WebClientReactiveAuthorizationCodeTokenResponseClient`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nFunction<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {\n\tif (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {\n\t\t// Assuming RSA key type\n\t\tRSAPublicKey publicKey = ...\n\t\tRSAPrivateKey privateKey = ...\n\t\treturn new RSAKey.Builder(publicKey)\n\t\t\t\t.privateKey(privateKey)\n\t\t\t\t.keyID(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t}\n\treturn null;\n};\n\nWebClientReactiveAuthorizationCodeTokenResponseClient tokenResponseClient =\n\t\tnew WebClientReactiveAuthorizationCodeTokenResponseClient();\ntokenResponseClient.addParametersConverter(\n\t\tnew NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwkResolver: Function<ClientRegistration, JWK> =\n    Function<ClientRegistration, JWK> { clientRegistration ->\n        if (clientRegistration.clientAuthenticationMethod.equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {\n            // Assuming RSA key type\n            var publicKey: RSAPublicKey = ...\n            var privateKey: RSAPrivateKey = ...\n            RSAKey.Builder(publicKey)\n                    .privateKey(privateKey)\n                    .keyID(UUID.randomUUID().toString())\n                .build()\n        }\n        null\n    }\n\nval tokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()\ntokenResponseClient.addParametersConverter(\n    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)\n)\n----\n======\n\n[[oauth2-client-authentication-jwt-bearer-client-secret-jwt]]\n=== Authenticate using `client_secret_jwt`\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            client-authentication-method: client_secret_jwt\n            authorization-grant-type: client_credentials\n            ...\n----\n\nThe following example shows how to configure `WebClientReactiveClientCredentialsTokenResponseClient`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nFunction<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {\n\tif (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {\n\t\tSecretKeySpec secretKey = new SecretKeySpec(\n\t\t\t\tclientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\"HmacSHA256\");\n\t\treturn new OctetSequenceKey.Builder(secretKey)\n\t\t\t\t.keyID(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t}\n\treturn null;\n};\n\nWebClientReactiveClientCredentialsTokenResponseClient tokenResponseClient =\n\t\tnew WebClientReactiveClientCredentialsTokenResponseClient();\ntokenResponseClient.addParametersConverter(\n\t\tnew NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwkResolver = Function<ClientRegistration, JWK?> { clientRegistration: ClientRegistration ->\n    if (clientRegistration.clientAuthenticationMethod == ClientAuthenticationMethod.CLIENT_SECRET_JWT) {\n        val secretKey = SecretKeySpec(\n            clientRegistration.clientSecret.toByteArray(StandardCharsets.UTF_8),\n            \"HmacSHA256\"\n        )\n        OctetSequenceKey.Builder(secretKey)\n            .keyID(UUID.randomUUID().toString())\n            .build()\n    }\n    null\n}\n\nval tokenResponseClient = WebClientReactiveClientCredentialsTokenResponseClient()\ntokenResponseClient.addParametersConverter(\n    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)\n)\n----\n======\n\n[[oauth2-client-authentication-jwt-bearer-assertion]]\n=== Customizing the JWT assertion\n\nThe JWT produced by `NimbusJwtClientAuthenticationParametersConverter` contains the `iss`, `sub`, `aud`, `jti`, `iat` and `exp` claims by default. You can customize the headers and/or claims by providing a `Consumer<NimbusJwtClientAuthenticationParametersConverter.JwtClientAuthenticationContext<T>>` to `setJwtClientAssertionCustomizer()`. The following example shows how to customize claims of the JWT:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nFunction<ClientRegistration, JWK> jwkResolver = ...\n\nNimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> converter =\n\t\tnew NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver);\nconverter.setJwtClientAssertionCustomizer((context) -> {\n\tcontext.getHeaders().header(\"custom-header\", \"header-value\");\n\tcontext.getClaims().claim(\"custom-claim\", \"claim-value\");\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwkResolver = ...\n\nval converter: NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> =\n    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)\nconverter.setJwtClientAssertionCustomizer { context ->\n    context.headers.header(\"custom-header\", \"header-value\")\n    context.claims.claim(\"custom-claim\", \"claim-value\")\n}\n----\n======\n\n[[oauth2-client-authentication-public]]\n== [[oauth2Client-public-auth]]Public Authentication\n\nPublic Client Authentication is supported out of the box and no customization is necessary to enable it.\n\nThe following Spring Boot properties for an OAuth 2.0 client registration demonstrate the configuration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: client-id\n            client-authentication-method: none\n            authorization-grant-type: authorization_code\n            ...\n----\n\n[NOTE]\n====\nPublic Clients are supported using https://tools.ietf.org/html/rfc7636[Proof Key for Code Exchange] (PKCE).\nPKCE will automatically be used when `client-authentication-method` is set to \"none\" (`ClientAuthenticationMethod.NONE`).\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/client/core.adoc",
    "content": "[[oauth2Client-core-interface-class]]\n= Core Interfaces / Classes\n\n\n[[oauth2Client-client-registration]]\n== ClientRegistration\n\n`ClientRegistration` is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\nA client registration holds information, such as client id, client secret, authorization grant type, redirect URI, scope(s), authorization URI, token URI, and other details.\n\n`ClientRegistration` and its properties are defined as follows:\n\n[source,java]\n----\npublic final class ClientRegistration {\n\tprivate String registrationId;\t<1>\n\tprivate String clientId;\t<2>\n\tprivate String clientSecret;\t<3>\n\tprivate ClientAuthenticationMethod clientAuthenticationMethod;\t<4>\n\tprivate AuthorizationGrantType authorizationGrantType;\t<5>\n\tprivate String redirectUri;\t<6>\n\tprivate Set<String> scopes;\t<7>\n\tprivate ProviderDetails providerDetails;\n\tprivate String clientName;\t<8>\n\n\tpublic class ProviderDetails {\n\t\tprivate String authorizationUri;\t<9>\n\t\tprivate String tokenUri;\t<10>\n\t\tprivate UserInfoEndpoint userInfoEndpoint;\n\t\tprivate String jwkSetUri;\t<11>\n\t\tprivate String issuerUri;\t<12>\n\t\tprivate Map<String, Object> configurationMetadata;  <13>\n\n\t\tpublic class UserInfoEndpoint {\n\t\t\tprivate String uri;\t<14>\n\t\t\tprivate AuthenticationMethod authenticationMethod;  <15>\n\t\t\tprivate String userNameAttributeName;\t<16>\n\n\t\t}\n\t}\n\n\tpublic static final class ClientSettings {\n\t\tprivate boolean requireProofKey; // <17>\n\t}\n}\n----\n<1> `registrationId`: The ID that uniquely identifies the `ClientRegistration`.\n<2> `clientId`: The client identifier.\n<3> `clientSecret`: The client secret.\n<4> `clientAuthenticationMethod`: The method used to authenticate the Client with the Provider.\nThe supported values are *client_secret_basic*, *client_secret_post*, *private_key_jwt*, *client_secret_jwt* and *none* https://tools.ietf.org/html/rfc6749#section-2.1[(public clients)].\n<5> `authorizationGrantType`: The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types.\n The supported values are `authorization_code`, `client_credentials`, as well as, extension grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`.\n<6> `redirectUri`: The client's registered redirect URI that the _Authorization Server_ redirects the end-user's user-agent\n to after the end-user has authenticated and authorized access to the client.\n<7> `scopes`: The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n<8> `clientName`: A descriptive name used for the client.\nThe name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n<9> `authorizationUri`: The Authorization Endpoint URI for the Authorization Server.\n<10> `tokenUri`: The Token Endpoint URI for the Authorization Server.\n<11> `jwkSetUri`: The URI used to retrieve the https://tools.ietf.org/html/rfc7517[JSON Web Key (JWK)] Set from the Authorization Server,\n which contains the cryptographic key(s) used to verify the https://tools.ietf.org/html/rfc7515[JSON Web Signature (JWS)] of the ID Token and optionally the UserInfo Response.\n<12> `issuerUri`: Returns the issuer identifier uri for the OpenID Connect 1.0 provider or the OAuth 2.0 Authorization Server.\n<13> `configurationMetadata`: The https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[OpenID Provider Configuration Information].\n This information will only be available if the Spring Boot property `spring.security.oauth2.client.provider.[providerId].issuerUri` is configured.\n<14> `(userInfoEndpoint)uri`: The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n<15> `(userInfoEndpoint)authenticationMethod`: The authentication method used when sending the access token to the UserInfo Endpoint.\nThe supported values are *header*, *form* and *query*.\n<16> `userNameAttributeName`: The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n<17> [[oauth2Client-client-registration-requireProofKey]]`requireProofKey`: If `true` or if `clientAuthenticationMethod` is `none`, then PKCE will be enabled. Defaults to `true` for `authorization_code` grant type and `false` for other grant types.\n\nA `ClientRegistration` can be initially configured using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint].\n\n`ClientRegistrations` provides convenience methods for configuring a `ClientRegistration` in this way, as can be seen in the following example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nClientRegistration clientRegistration =\n\tClientRegistrations.fromIssuerLocation(\"https://idp.example.com/issuer\").build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval clientRegistration = ClientRegistrations.fromIssuerLocation(\"https://idp.example.com/issuer\").build()\n----\n======\n\nThe above code will query in series `https://idp.example.com/issuer/.well-known/openid-configuration`, and then `https://idp.example.com/.well-known/openid-configuration/issuer`, and finally `https://idp.example.com/.well-known/oauth-authorization-server/issuer`, stopping at the first to return a 200 response.\n\nAs an alternative, you can use `ClientRegistrations.fromOidcIssuerLocation()` to only query the OpenID Connect Provider's Configuration endpoint.\n\n[[oauth2Client-client-registration-repo]]\n== ReactiveClientRegistrationRepository\n\nThe `ReactiveClientRegistrationRepository` serves as a repository for OAuth 2.0 / OpenID Connect 1.0 `ClientRegistration`(s).\n\n[NOTE]\n====\nClient registration information is ultimately stored and owned by the associated Authorization Server.\nThis repository provides the ability to retrieve a sub-set of the primary client registration information, which is stored with the Authorization Server.\n====\n\nSpring Boot auto-configuration binds each of the properties under `spring.security.oauth2.client.registration._[registrationId]_` to an instance of `ClientRegistration` and then composes each of the `ClientRegistration` instance(s) within a `ReactiveClientRegistrationRepository`.\n\n[NOTE]\n====\nThe default implementation of `ReactiveClientRegistrationRepository` is `InMemoryReactiveClientRegistrationRepository`.\n====\n\nThe auto-configuration also registers the `ReactiveClientRegistrationRepository` as a `@Bean` in the `ApplicationContext` so that it is available for dependency-injection, if needed by the application.\n\nThe following listing shows an example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class OAuth2ClientController {\n\n\t@Autowired\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@GetMapping(\"/\")\n\tpublic Mono<String> index() {\n\t\treturn this.clientRegistrationRepository.findByRegistrationId(\"okta\")\n\t\t\t\t...\n\t\t\t\t.thenReturn(\"index\");\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass OAuth2ClientController {\n\n    @Autowired\n    private lateinit var clientRegistrationRepository: ReactiveClientRegistrationRepository\n\n    @GetMapping(\"/\")\n    fun index(): Mono<String> {\n        return this.clientRegistrationRepository.findByRegistrationId(\"okta\")\n            ...\n            .thenReturn(\"index\")\n    }\n}\n----\n======\n\n[[oauth2Client-authorized-client]]\n== OAuth2AuthorizedClient\n\n`OAuth2AuthorizedClient` is a representation of an Authorized Client.\nA client is considered to be authorized when the end-user (Resource Owner) has granted authorization to the client to access its protected resources.\n\n`OAuth2AuthorizedClient` serves the purpose of associating an `OAuth2AccessToken` (and optional `OAuth2RefreshToken`) to a `ClientRegistration` (client) and resource owner, who is the `Principal` end-user that granted the authorization.\n\n\n[[oauth2Client-authorized-repo-service]]\n== ServerOAuth2AuthorizedClientRepository / ReactiveOAuth2AuthorizedClientService\n\n`ServerOAuth2AuthorizedClientRepository` is responsible for persisting `OAuth2AuthorizedClient`(s) between web requests.\nWhereas, the primary role of `ReactiveOAuth2AuthorizedClientService` is to manage `OAuth2AuthorizedClient`(s) at the application-level.\n\nFrom a developer perspective, the `ServerOAuth2AuthorizedClientRepository` or `ReactiveOAuth2AuthorizedClientService` provides the capability to lookup an `OAuth2AccessToken` associated with a client so that it may be used to initiate a protected resource request.\n\nThe following listing shows an example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class OAuth2ClientController {\n\n\t@Autowired\n\tprivate ReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\t@GetMapping(\"/\")\n\tpublic Mono<String> index(Authentication authentication) {\n\t\treturn this.authorizedClientService.loadAuthorizedClient(\"okta\", authentication.getName())\n\t\t\t\t.map(OAuth2AuthorizedClient::getAccessToken)\n\t\t\t\t...\n\t\t\t\t.thenReturn(\"index\");\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass OAuth2ClientController {\n\n    @Autowired\n    private lateinit var authorizedClientService: ReactiveOAuth2AuthorizedClientService\n\n    @GetMapping(\"/\")\n    fun index(authentication: Authentication): Mono<String> {\n        return this.authorizedClientService.loadAuthorizedClient<OAuth2AuthorizedClient>(\"okta\", authentication.name)\n            .map { it.accessToken }\n            ...\n            .thenReturn(\"index\")\n    }\n}\n----\n======\n\n[NOTE]\n====\nSpring Boot auto-configuration registers an `ServerOAuth2AuthorizedClientRepository` and/or `ReactiveOAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext`.\nHowever, the application may choose to override and register a custom `ServerOAuth2AuthorizedClientRepository` or `ReactiveOAuth2AuthorizedClientService` `@Bean`.\n====\n\nThe default implementation of `ReactiveOAuth2AuthorizedClientService` is `InMemoryReactiveOAuth2AuthorizedClientService`, which stores `OAuth2AuthorizedClient`(s) in-memory.\n\nAlternatively, the R2DBC implementation `R2dbcReactiveOAuth2AuthorizedClientService` may be configured for persisting `OAuth2AuthorizedClient`(s) in a database.\n\n[NOTE]\n====\n`R2dbcReactiveOAuth2AuthorizedClientService` depends on the table definition described in xref:servlet/appendix/database-schema.adoc#dbschema-oauth2-client[ OAuth 2.0 Client Schema].\n====\n\n\n[[oauth2Client-authorized-manager-provider]]\n== ReactiveOAuth2AuthorizedClientManager / ReactiveOAuth2AuthorizedClientProvider\n\nThe `ReactiveOAuth2AuthorizedClientManager` is responsible for the overall management of `OAuth2AuthorizedClient`(s).\n\nThe primary responsibilities include:\n\n* Authorizing (or re-authorizing) an OAuth 2.0 Client, using a `ReactiveOAuth2AuthorizedClientProvider`.\n* Delegating the persistence of an `OAuth2AuthorizedClient`, typically using a `ReactiveOAuth2AuthorizedClientService` or `ServerOAuth2AuthorizedClientRepository`.\n* Delegating to a `ReactiveOAuth2AuthorizationSuccessHandler` when an OAuth 2.0 Client has been successfully authorized (or re-authorized).\n* Delegating to a `ReactiveOAuth2AuthorizationFailureHandler` when an OAuth 2.0 Client fails to authorize (or re-authorize).\n\nA `ReactiveOAuth2AuthorizedClientProvider` implements a strategy for authorizing (or re-authorizing) an OAuth 2.0 Client.\nImplementations will typically implement an authorization grant type, e.g. `authorization_code`, `client_credentials`, etc.\n\nThe default implementation of `ReactiveOAuth2AuthorizedClientManager` is `DefaultReactiveOAuth2AuthorizedClientManager`, which is associated with a `ReactiveOAuth2AuthorizedClientProvider` that may support multiple authorization grant types using a delegation-based composite.\nThe `ReactiveOAuth2AuthorizedClientProviderBuilder` may be used to configure and build the delegation-based composite.\n\nThe following code shows an example of how to configure and build a `ReactiveOAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token` and `client_credentials` authorization grant types:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.authorizationCode()\n\t\t\t\t\t.refreshToken()\n\t\t\t\t\t.clientCredentials()\n\t\t\t\t\t.build();\n\n\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ReactiveClientRegistrationRepository,\n        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {\n    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n            .authorizationCode()\n            .refreshToken()\n            .clientCredentials()\n            .build()\n    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n\nWhen an authorization attempt succeeds, the `DefaultReactiveOAuth2AuthorizedClientManager` will delegate to the `ReactiveOAuth2AuthorizationSuccessHandler`, which (by default) will save the `OAuth2AuthorizedClient` via the `ServerOAuth2AuthorizedClientRepository`.\nIn the case of a re-authorization failure, e.g. a refresh token is no longer valid, the previously saved `OAuth2AuthorizedClient` will be removed from the `ServerOAuth2AuthorizedClientRepository` via the `RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler`.\nThe default behaviour may be customized via `setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler)` and `setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler)`.\n\nThe `DefaultReactiveOAuth2AuthorizedClientManager` is also associated with a `contextAttributesMapper` of type `Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>>`, which is responsible for mapping attribute(s) from the `OAuth2AuthorizeRequest` to a `Map` of attributes to be associated to the `OAuth2AuthorizationContext`.\nThis can be useful when you need to supply a `ReactiveOAuth2AuthorizedClientProvider` with required (supported) attribute(s).\n\nThe following code shows an example of the `contextAttributesMapper`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.authorizationCode()\n\t\t\t\t\t.refreshToken()\n\t\t\t\t\t.build();\n\n\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t// Assuming the attributes are supplied as `ServerHttpRequest` parameters,\n\t// map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`\n\tauthorizedClientManager.setContextAttributesMapper(contextAttributesMapper());\n\n\treturn authorizedClientManager;\n}\n\nprivate Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {\n\treturn authorizeRequest -> {\n\t\tMap<String, Object> contextAttributes = Collections.emptyMap();\n\t\tServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());\n\t\tServerHttpRequest request = exchange.getRequest();\n\t\tString param1 = request.getQueryParams().getFirst(\"param1\");\n\t\tString param2 = request.getQueryParams().getFirst(\"param2\");\n\t\tif (StringUtils.hasText(param1) && StringUtils.hasText(param2)) {\n\t\t\tcontextAttributes = new HashMap<>();\n\t\t\tcontextAttributes.put(\"param1\", param1);\n\t\t\tcontextAttributes.put(\"param2\", param2);\n\t\t}\n\t\treturn Mono.just(contextAttributes);\n\t};\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ReactiveClientRegistrationRepository,\n        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {\n    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n            .authorizationCode()\n            .refreshToken()\n            .build()\n    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n\n\t// Assuming the attributes are supplied as `ServerHttpRequest` parameters,\n\t// map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`\n    authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())\n    return authorizedClientManager\n}\n\nprivate fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<MutableMap<String, Any>>> {\n    return Function { authorizeRequest ->\n        var contextAttributes: MutableMap<String, Any> = mutableMapOf()\n        val exchange: ServerWebExchange = authorizeRequest.getAttribute(ServerWebExchange::class.java.name)!!\n        val request: ServerHttpRequest = exchange.request\n        val param1: String? = request.queryParams.getFirst(\"param1\")\n        val param2: String? = request.queryParams.getFirst(\"param2\")\n        if (StringUtils.hasText(param1) && StringUtils.hasText(param2)) {\n            contextAttributes = hashMapOf()\n            contextAttributes[\"param1\"] = param1!!\n            contextAttributes[\"param2\"] = param2!!\n        }\n        Mono.just(contextAttributes)\n    }\n}\n----\n======\n\nThe `DefaultReactiveOAuth2AuthorizedClientManager` is designed to be used *_within_* the context of a `ServerWebExchange`.\nWhen operating *_outside_* of a `ServerWebExchange` context, use `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager` instead.\n\nA _service application_ is a common use case for when to use an `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager`.\nService applications often run in the background, without any user interaction, and typically run under a system-level account instead of a user account.\nAn OAuth 2.0 Client configured with the `client_credentials` grant type can be considered a type of service application.\n\nThe following code shows an example of how to configure an `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager` that provides support for the `client_credentials` grant type:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\tReactiveOAuth2AuthorizedClientService authorizedClientService) {\n\n\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.clientCredentials()\n\t\t\t\t\t.build();\n\n\tAuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientService);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ReactiveClientRegistrationRepository,\n        authorizedClientService: ReactiveOAuth2AuthorizedClientService): ReactiveOAuth2AuthorizedClientManager {\n    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n            .clientCredentials()\n            .build()\n    val authorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientService)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/client/index.adoc",
    "content": "[[webflux-oauth2-client]]\n= OAuth 2.0 Client\n:page-section-summary-toc: 1\n\nThe OAuth 2.0 Client features provide support for the Client role as defined in the https://tools.ietf.org/html/rfc6749#section-1.1[OAuth 2.0 Authorization Framework].\n\nAt a high-level, the core features available are:\n\n.Authorization Grant support\n* xref:reactive/oauth2/client/authorization-grants.adoc#oauth2-client-authorization-code[Authorization Code]\n* xref:reactive/oauth2/client/authorization-grants.adoc#oauth2-client-refresh-token[Refresh Token]\n* xref:reactive/oauth2/client/authorization-grants.adoc#oauth2-client-client-credentials[Client Credentials]\n* xref:reactive/oauth2/client/authorization-grants.adoc#oauth2-client-jwt-bearer[JWT Bearer]\n* xref:reactive/oauth2/client/authorization-grants.adoc#oauth2-client-token-exchange[Token Exchange]\n\n.Client Authentication support\n* xref:reactive/oauth2/client/client-authentication.adoc#oauth2-client-authentication-jwt-bearer[JWT Bearer]\n\n.HTTP Client support\n* xref:reactive/oauth2/client/authorized-clients.adoc#oauth2-client-web-client[`WebClient` integration for Reactive Environments] (for requesting protected resources)\n\nThe `ServerHttpSecurity.oauth2Client()` DSL provides a number of configuration options for customizing the core components used by OAuth 2.0 Client.\n\nThe following code shows the complete configuration options provided by the `ServerHttpSecurity.oauth2Client()` DSL:\n\n.OAuth2 Client Configuration Options\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2ClientSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.oauth2Client((oauth2) -> oauth2\n\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository())\n\t\t\t\t.authorizedClientRepository(this.authorizedClientRepository())\n\t\t\t\t.authorizationRequestRepository(this.authorizationRequestRepository())\n\t\t\t\t.authorizationRequestResolver(this.authorizationRequestResolver())\n\t\t\t\t.authenticationConverter(this.authenticationConverter())\n\t\t\t\t.authenticationManager(this.authenticationManager())\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2ClientSecurityConfig {\n\n    @Bean\n    fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Client {\n                clientRegistrationRepository = clientRegistrationRepository()\n                authorizedClientRepository = authorizedClientRepository()\n                authorizationRequestRepository = authorizedRequestRepository()\n                authorizationRequestResolver = authorizationRequestResolver()\n                authenticationConverter = authenticationConverter()\n                authenticationManager = authenticationManager()\n            }\n        }\n\n        return http.build()\n    }\n}\n----\n======\n\nThe `ReactiveOAuth2AuthorizedClientManager` is responsible for managing the authorization (or re-authorization) of an OAuth 2.0 Client, in collaboration with one or more `ReactiveOAuth2AuthorizedClientProvider`(s).\n\nThe following code shows an example of how to register a `ReactiveOAuth2AuthorizedClientManager` `@Bean` and associate it with a `ReactiveOAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token` and `client_credentials` authorization grant types:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.authorizationCode()\n\t\t\t\t\t.refreshToken()\n\t\t\t\t\t.clientCredentials()\n\t\t\t\t\t.build();\n\n\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ReactiveClientRegistrationRepository,\n        authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {\n    val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n            .authorizationCode()\n            .refreshToken()\n            .clientCredentials()\n            .build()\n    val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/index.adoc",
    "content": "[[webflux-oauth2]]\n= OAuth2 WebFlux\n\nSpring Security provides comprehensive OAuth 2.0 support.\nThis section discusses how to integrate OAuth 2.0 into your reactive application.\n\n[[oauth2-overview]]\n== Overview\n\nSpring Security's OAuth 2.0 support consists of two primary feature sets:\n\n* <<oauth2-resource-server>>\n* <<oauth2-client>>\n\n[NOTE]\n====\n<<oauth2-client-log-users-in,OAuth2 Login>> is a very powerful OAuth2 Client feature that deserves its own section in the reference documentation.\nHowever, it does not exist as a standalone feature and requires OAuth2 Client in order to function.\n====\n\nThese feature sets cover the _resource server_ and _client_ roles defined in the https://tools.ietf.org/html/rfc6749#section-1.1[OAuth 2.0 Authorization Framework], while the _authorization server_ role is covered by https://docs.spring.io/spring-authorization-server/reference/index.html[Spring Authorization Server], which is a separate project built on xref:index.adoc[Spring Security].\n\nThe _resource server_ and _client_ roles in OAuth2 are typically represented by one or more server-side applications.\nAdditionally, the _authorization server_ role can be represented by one or more third parties (as is the case when centralizing identity management and/or authentication within an organization) *-or-* it can be represented by an application (as is the case with Spring Authorization Server).\n\nFor example, a typical OAuth2-based microservices architecture might consist of a single user-facing client application, several backend resource servers providing REST APIs and a third party authorization server for managing users and authentication concerns.\nIt is also common to have a single application representing only one of these roles with the need to integrate with one or more third parties that are providing the other roles.\n\nSpring Security handles these scenarios and more.\nThe following sections cover the roles provided by Spring Security and contain examples for common scenarios.\n\n[[oauth2-resource-server]]\n== OAuth2 Resource Server\n\n[NOTE]\n====\nThis section contains a summary of OAuth2 Resource Server features with examples.\nSee xref:reactive/oauth2/resource-server/index.adoc[OAuth 2.0 Resource Server] for complete reference documentation.\n====\n\nTo get started, add the `spring-security-oauth2-resource-server` dependency to your project.\nWhen using Spring Boot, add the following starter:\n\n.OAuth2 Client with Spring Boot\n[tabs]\n======\nGradle::\n+\n[source,gradle,role=\"primary\"]\n----\nimplementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'\n----\n\nMaven::\n+\n[source,maven,role=\"secondary\"]\n----\n<dependency>\n\t<groupId>org.springframework.boot</groupId>\n\t<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>\n</dependency>\n----\n======\n\n[TIP]\n====\nSee xref:getting-spring-security.adoc[] for additional options when not using Spring Boot.\n====\n\nConsider the following use cases for OAuth2 Resource Server:\n\n* I want to <<oauth2-resource-server-access-token,protect access to the API using OAuth2>> (authorization server provides JWT or opaque access token)\n* I want to <<oauth2-resource-server-custom-jwt,protect access to the API using a JWT>> (custom token)\n\n[[oauth2-resource-server-access-token]]\n=== Protect Access with an OAuth2 Access Token\n\nIt is very common to protect access to an API using OAuth2 access tokens.\nIn most cases, Spring Security requires only minimal configuration to secure an application with OAuth2.\n\nThere are two types of `Bearer` tokens supported by Spring Security which each use a different component for validation:\n\n* <<oauth2-resource-server-access-token-jwt,JWT support>> uses a `ReactiveJwtDecoder` bean to validate signatures and decode tokens\n* <<oauth2-resource-server-access-token-opaque,Opaque token support>> uses a `ReactiveOpaqueTokenIntrospector` bean to introspect tokens\n\n[[oauth2-resource-server-access-token-jwt]]\n==== JWT Support\n\nThe following example configures a `ReactiveJwtDecoder` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          issuer-uri: https://my-auth-server.com\n----\n\nWhen using Spring Boot, this is all that is required.\nThe default arrangement provided by Spring Boot is equivalent to the following:\n\n.Configure Resource Server with JWTs\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t.jwt(Customizer.withDefaults())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ReactiveJwtDecoder jwtDecoder() {\n\t\treturn ReactiveJwtDecoders.fromIssuerLocation(\"https://my-auth-server.com\");\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n\t\treturn http {\n\t\t\tauthorizeExchange {\n\t\t\t\tauthorize(anyExchange, authenticated)\n\t\t\t}\n\t\t\toauth2ResourceServer {\n\t\t\t\tjwt { }\n\t\t\t}\n\t\t}\n\t}\n\n\t@Bean\n\tfun jwtDecoder(): ReactiveJwtDecoder {\n\t\treturn ReactiveJwtDecoders.fromIssuerLocation(\"https://my-auth-server.com\")\n\t}\n\n}\n----\n=====\n\n[[oauth2-resource-server-access-token-opaque]]\n==== Opaque Token Support\n\nThe following example configures an `ReactiveOpaqueTokenIntrospector` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        opaquetoken:\n          introspection-uri: https://my-auth-server.com/oauth2/introspect\n          client-id: my-client-id\n          client-secret: my-client-secret\n----\n\nWhen using Spring Boot, this is all that is required.\nThe default arrangement provided by Spring Boot is equivalent to the following:\n\n.Configure Resource Server with Opaque Tokens\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t.opaqueToken(Customizer.withDefaults())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ReactiveOpaqueTokenIntrospector opaqueTokenIntrospector() {\n\t\treturn new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\"https://my-auth-server.com/oauth2/introspect\", \"my-client-id\", \"my-client-secret\");\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n\t\treturn http {\n\t\t\tauthorizeExchange {\n\t\t\t\tauthorize(anyExchange, authenticated)\n\t\t\t}\n\t\t\toauth2ResourceServer {\n\t\t\t\topaqueToken { }\n\t\t\t}\n\t\t}\n\t}\n\n\t@Bean\n\tfun opaqueTokenIntrospector(): ReactiveOpaqueTokenIntrospector {\n\t\treturn SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\"https://my-auth-server.com/oauth2/introspect\", \"my-client-id\", \"my-client-secret\"\n\t\t)\n\t}\n\n}\n----\n=====\n\n[[oauth2-resource-server-custom-jwt]]\n=== Protect Access with a custom JWT\n\nIt is a fairly common goal to protect access to an API using JWTs, particularly when the frontend is developed as a single-page application.\nThe OAuth2 Resource Server support in Spring Security can be used for any type of `Bearer` token, including a custom JWT.\n\nAll that is required to protect an API using JWTs is a `ReactiveJwtDecoder` bean, which is used to validate signatures and decode tokens.\nSpring Security will automatically use the provided bean to configure protection within the `SecurityWebFilterChain`.\n\nThe following example configures a `ReactiveJwtDecoder` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          public-key-location: classpath:my-public-key.pub\n----\n\n[NOTE]\n====\nYou can provide the public key as a classpath resource (called `my-public-key.pub` in this example).\n====\n\nWhen using Spring Boot, this is all that is required.\nThe default arrangement provided by Spring Boot is equivalent to the following:\n\n.Configure Resource Server with Custom JWTs\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t.jwt(Customizer.withDefaults())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ReactiveJwtDecoder jwtDecoder() {\n\t\treturn NimbusReactiveJwtDecoder.withPublicKey(publicKey()).build();\n\t}\n\n\tprivate RSAPublicKey publicKey() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n\t\treturn http {\n\t\t\tauthorizeExchange {\n\t\t\t\tauthorize(anyExchange, authenticated)\n\t\t\t}\n\t\t\toauth2ResourceServer {\n\t\t\t\tjwt { }\n\t\t\t}\n\t\t}\n\t}\n\n\t@Bean\n\tfun jwtDecoder(): ReactiveJwtDecoder {\n\t\treturn NimbusReactiveJwtDecoder.withPublicKey(publicKey()).build()\n\t}\n\n\tprivate fun publicKey(): RSAPublicKey {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\n[NOTE]\n====\nSpring Security does not provide an endpoint for minting tokens.\nHowever, Spring Security does provide the `JwtEncoder` interface along with one implementation, which is `NimbusJwtEncoder`.\n====\n\n[[oauth2-client]]\n== OAuth2 Client\n\n[NOTE]\n====\nThis section contains a summary of OAuth2 Client features with examples.\nSee xref:reactive/oauth2/client/index.adoc[OAuth 2.0 Client] and xref:reactive/oauth2/login/index.adoc[OAuth 2.0 Login] for complete reference documentation.\n====\n\nTo get started, add the `spring-security-oauth2-client` dependency to your project.\nWhen using Spring Boot, add the following starter:\n\n.OAuth2 Client with Spring Boot\n[tabs]\n======\nGradle::\n+\n[source,gradle,role=\"primary\"]\n----\nimplementation 'org.springframework.boot:spring-boot-starter-oauth2-client'\n----\n\nMaven::\n+\n[source,maven,role=\"secondary\"]\n----\n<dependency>\n\t<groupId>org.springframework.boot</groupId>\n\t<artifactId>spring-boot-starter-oauth2-client</artifactId>\n</dependency>\n----\n======\n\n[TIP]\n====\nSee xref:getting-spring-security.adoc[] for additional options when not using Spring Boot.\n====\n\nConsider the following use cases for OAuth2 Client:\n\n* I want to <<oauth2-client-log-users-in,log users in using OAuth 2.0 or OpenID Connect 1.0>>\n* I want to <<oauth2-client-access-protected-resources,obtain an access token for users>> in order to access a third-party API\n* I want to <<oauth2-client-access-protected-resources-current-user,do both>> (log users in _and_ access a third-party API)\n* I want to <<oauth2-client-enable-extension-grant-type,enable an extension grant type>>\n* I want to <<oauth2-client-customize-existing-grant-type,customize an existing grant type>>\n* I want to <<oauth2-client-customize-request-parameters,customize token request parameters>>\n* I want to <<oauth2-client-customize-web-client,customize the `WebClient` used by OAuth2 Client components>>\n\n[[oauth2-client-log-users-in]]\n=== Log Users In with OAuth2\n\nIt is very common to require users to log in via OAuth2.\nhttps://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect 1.0] provides a special token called the `id_token` which is designed to provide an OAuth2 Client with the ability to perform user identity verification and log users in.\nIn certain cases, OAuth2 can be used directly to log users in (as is the case with popular social login providers that do not implement OpenID Connect such as GitHub and Facebook).\n\nThe following example configures the application to act as an OAuth2 Client capable of logging users in with OAuth2 or OpenID Connect:\n\n.Configure OAuth2 Login\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t// ...\n\t\t\t.oauth2Login(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n\t\treturn http {\n\t\t\t// ...\n\t\t\toauth2Login { }\n\t\t}\n\t}\n\n}\n----\n=====\n\nIn addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ReactiveClientRegistrationRepository` bean.\nThe following example configures an `InMemoryReactiveClientRegistrationRepository` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          my-oidc-client:\n            provider: my-oidc-provider\n            client-id: my-client-id\n            client-secret: my-client-secret\n            authorization-grant-type: authorization_code\n            scope: openid,profile\n        provider:\n          my-oidc-provider:\n            issuer-uri: https://my-oidc-provider.com\n----\n\nWith the above configuration, the application now supports two additional endpoints:\n\n1. The login endpoint (e.g. `/oauth2/authorization/my-oidc-client`) is used to initiate login and perform a redirect to the third party authorization server.\n2. The redirection endpoint (e.g. `/login/oauth2/code/my-oidc-client`) is used by the authorization server to redirect back to the client application, and will contain a `code` parameter used to obtain an `id_token` and/or `access_token` via the access token request.\n\n[NOTE]\n====\nThe presence of the `openid` scope in the above configuration indicates that OpenID Connect 1.0 should be used.\nThis instructs Spring Security to use OIDC-specific components (such as `OidcReactiveOAuth2UserService`) during request processing.\nWithout this scope, Spring Security will use OAuth2-specific components (such as `DefaultReactiveOAuth2UserService`) instead.\n====\n\n[[oauth2-client-access-protected-resources]]\n=== Access Protected Resources\n\nMaking requests to a third party API that is protected by OAuth2 is a core use case of OAuth2 Client.\nThis is accomplished by authorizing a client (represented by the `OAuth2AuthorizedClient` class in Spring Security) and accessing protected resources by placing a `Bearer` token in the `Authorization` header of an outbound request.\n\nThe following example configures the application to act as an OAuth2 Client capable of requesting protected resources from a third party API:\n\n.Configure OAuth2 Client\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t// ...\n\t\t\t.oauth2Client(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n\t\treturn http {\n\t\t\t// ...\n\t\t\toauth2Client { }\n\t\t}\n\t}\n\n}\n----\n=====\n\n[NOTE]\n====\nThe above example does not provide a way to log users in.\nYou can use any other login mechanism (such as `formLogin()`).\nSee the <<oauth2-client-access-protected-resources-current-user,next section>> for an example combining `oauth2Client()` with `oauth2Login()`.\n====\n\nIn addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ReactiveClientRegistrationRepository` bean.\nThe following example configures an `InMemoryReactiveClientRegistrationRepository` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          my-oauth2-client:\n            provider: my-auth-server\n            client-id: my-client-id\n            client-secret: my-client-secret\n            authorization-grant-type: authorization_code\n            scope: message.read,message.write\n        provider:\n          my-auth-server:\n            issuer-uri: https://my-auth-server.com\n----\n\nIn addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly.\nSpring Security provides implementations of `ReactiveOAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources.\n\n[TIP]\n====\nSpring Security registers a default `ReactiveOAuth2AuthorizedClientManager` bean for you when one does not exist.\n====\n\nThe easiest way to use a `ReactiveOAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`.\n\nThe following example uses the default `ReactiveOAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:\n\n.Configure `WebClient` with `ExchangeFilterFunction`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class WebClientConfig {\n\n\t@Bean\n\tpublic WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tServerOAuth2AuthorizedClientExchangeFilterFunction filter =\n\t\t\t\tnew ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\t\treturn WebClient.builder()\n\t\t\t\t.filter(filter)\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass WebClientConfig {\n\n\t@Bean\n\tfun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {\n\t\tval filter = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n\t\treturn WebClient.builder()\n\t\t\t.filter(filter)\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\nThis configured `WebClient` can be used as in the following example:\n\n[[oauth2-client-accessing-protected-resources-example]]\n.Use `WebClient` to Access Protected Resources\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;\n\n@RestController\npublic class MessagesController {\n\n\tprivate final WebClient webClient;\n\n\tpublic MessagesController(WebClient webClient) {\n\t\tthis.webClient = webClient;\n\t}\n\n\t@GetMapping(\"/messages\")\n\tpublic Mono<ResponseEntity<List<Message>>> messages() {\n\t\treturn this.webClient.get()\n\t\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t\t.attributes(clientRegistrationId(\"my-oauth2-client\"))\n\t\t\t\t.retrieve()\n\t\t\t\t.toEntityList(Message.class);\n\t}\n\n\tpublic record Message(String message) {\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId\n\n@RestController\nclass MessagesController(private val webClient: WebClient) {\n\n\t@GetMapping(\"/messages\")\n\tfun messages(): Mono<ResponseEntity<List<Message>>> {\n\t\treturn webClient.get()\n\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t.attributes(clientRegistrationId(\"my-oauth2-client\"))\n\t\t\t.retrieve()\n\t\t\t.toEntityList<Message>()\n\t}\n\n\tdata class Message(val message: String)\n\n}\n----\n=====\n\n[[oauth2-client-access-protected-resources-current-user]]\n=== Access Protected Resources for the Current User\n\nWhen a user is logged in via OAuth2 or OpenID Connect, the authorization server may provide an access token that can be used directly to access protected resources.\nThis is convenient because it only requires a single `ClientRegistration` to be configured for both use cases simultaneously.\n\n[NOTE]\n====\nThis section combines <<oauth2-client-log-users-in>> and <<oauth2-client-access-protected-resources>> into a single configuration.\nOther advanced scenarios exist, such as configuring one `ClientRegistration` for login and another for accessing protected resources.\nAll such scenarios would use the same basic configuration.\n====\n\nThe following example configures the application to act as an OAuth2 Client capable of logging the user in _and_ requesting protected resources from a third party API:\n\n.Configure OAuth2 Login and OAuth2 Client\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t// ...\n\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t.oauth2Client(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n\t\treturn http {\n\t\t\t// ...\n\t\t\toauth2Login { }\n\t\t\toauth2Client { }\n\t\t}\n\t}\n\n}\n----\n=====\n\nIn addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ReactiveClientRegistrationRepository` bean.\nThe following example configures an `InMemoryReactiveClientRegistrationRepository` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          my-combined-client:\n            provider: my-auth-server\n            client-id: my-client-id\n            client-secret: my-client-secret\n            authorization-grant-type: authorization_code\n            scope: openid,profile,message.read,message.write\n        provider:\n          my-auth-server:\n            issuer-uri: https://my-auth-server.com\n----\n\n[NOTE]\n====\nThe main difference between the previous examples (<<oauth2-client-log-users-in>>,  <<oauth2-client-access-protected-resources>>) and this one is what is configured via the `scope` property, which combines the standard scopes `openid` and `profile` with the custom scopes `message.read` and `message.write`.\n====\n\nIn addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly.\nSpring Security provides implementations of `ReactiveOAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources.\n\n[TIP]\n====\nSpring Security registers a default `ReactiveOAuth2AuthorizedClientManager` bean for you when one does not exist.\n====\n\nThe easiest way to use a `ReactiveOAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`.\n\nThe following example uses the default `ReactiveOAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:\n\n.Configure `WebClient` with `ExchangeFilterFunction`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class WebClientConfig {\n\n\t@Bean\n\tpublic WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tServerOAuth2AuthorizedClientExchangeFilterFunction filter =\n\t\t\t\tnew ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\t\treturn WebClient.builder()\n\t\t\t\t.filter(filter)\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass WebClientConfig {\n\n\t@Bean\n\tfun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {\n\t\tval filter = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n\t\treturn WebClient.builder()\n\t\t\t.filter(filter)\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\nThis configured `WebClient` can be used as in the following example:\n\n[[oauth2-client-accessing-protected-resources-current-user-example]]\n.Use `WebClient` to Access Protected Resources (Current User)\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class MessagesController {\n\n\tprivate final WebClient webClient;\n\n\tpublic MessagesController(WebClient webClient) {\n\t\tthis.webClient = webClient;\n\t}\n\n\t@GetMapping(\"/messages\")\n\tpublic Mono<ResponseEntity<List<Message>>> messages() {\n\t\treturn this.webClient.get()\n\t\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t\t.retrieve()\n\t\t\t\t.toEntityList(Message.class);\n\t}\n\n\tpublic record Message(String message) {\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RestController\nclass MessagesController(private val webClient: WebClient) {\n\n\t@GetMapping(\"/messages\")\n\tfun messages(): Mono<ResponseEntity<List<Message>>> {\n\t\treturn webClient.get()\n\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t.retrieve()\n\t\t\t.toEntityList<Message>()\n\t}\n\n\tdata class Message(val message: String)\n\n}\n----\n=====\n\n[NOTE]\n====\nUnlike the <<oauth2-client-accessing-protected-resources-example,previous example>>, notice that we do not need to tell Spring Security about the `clientRegistrationId` we'd like to use.\nThis is because it can be derived from the currently logged in user.\n====\n\n[[oauth2-client-enable-extension-grant-type]]\n=== Enable an Extension Grant Type\n\nA common use case involves enabling and/or configuring an extension grant type.\nFor example, Spring Security provides support for the `jwt-bearer` and `token-exchange` grant types, but does not enable them by default because they are not part of the core OAuth 2.0 specification.\n\nWith Spring Security 6.3 and later, we can simply publish a bean for one or more `ReactiveOAuth2AuthorizedClientProvider` and they will be picked up automatically.\nThe following example simply enables the `jwt-bearer` grant type:\n\n.Enable `jwt-bearer` Grant Type\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic ReactiveOAuth2AuthorizedClientProvider jwtBearer() {\n\t\treturn new JwtBearerReactiveOAuth2AuthorizedClientProvider();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun jwtBearer(): ReactiveOAuth2AuthorizedClientProvider {\n\t\treturn JwtBearerReactiveOAuth2AuthorizedClientProvider()\n\t}\n\n}\n----\n=====\n\nA default `ReactiveOAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided.\n\n[TIP]\n====\nAny custom `OAuth2AuthorizedClientProvider` bean will also be picked up and applied to the provided `ReactiveOAuth2AuthorizedClientManager` after the default grant types.\n====\n\nIn order to achieve the above configuration prior to Spring Security 6.3, we had to publish this bean ourselves and ensure we re-enabled default grant types as well.\nTo understand what is being configured behind the scenes, here's what the configuration might have looked like:\n\n.Enable `jwt-bearer` Grant Type (prior to 6.3)\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken()\n\t\t\t\t.clientCredentials()\n\t\t\t\t.provider(new JwtBearerReactiveOAuth2AuthorizedClientProvider())\n\t\t\t\t.build();\n\n\t\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t\treturn authorizedClientManager;\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun authorizedClientManager(\n\t\tclientRegistrationRepository: ReactiveClientRegistrationRepository,\n\t\tauthorizedClientRepository: ServerOAuth2AuthorizedClientRepository\n\t): ReactiveOAuth2AuthorizedClientManager {\n\t\tval authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken()\n\t\t\t.clientCredentials()\n\t\t\t.provider(JwtBearerReactiveOAuth2AuthorizedClientProvider())\n\t\t\t.build()\n\n\t\tval authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\tclientRegistrationRepository, authorizedClientRepository\n\t\t)\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n\n\t\treturn authorizedClientManager\n\t}\n\n}\n----\n=====\n\n[[oauth2-client-customize-existing-grant-type]]\n=== Customize an Existing Grant Type\n\nThe ability to <<oauth2-client-enable-extension-grant-type,enable extension grant types>> by publishing a bean also provides the opportunity for customizing an existing grant type without the need to re-define the defaults.\nFor example, if we want to customize the clock skew of the `ReactiveOAuth2AuthorizedClientProvider` for the `client_credentials` grant, we can simply publish a bean like so:\n\n.Customize Client Credentials Grant Type\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic ReactiveOAuth2AuthorizedClientProvider clientCredentials() {\n\t\tClientCredentialsReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\t\tnew ClientCredentialsReactiveOAuth2AuthorizedClientProvider();\n\t\tauthorizedClientProvider.setClockSkew(Duration.ofMinutes(5));\n\n\t\treturn authorizedClientProvider;\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun clientCredentials(): ReactiveOAuth2AuthorizedClientProvider {\n\t\tval authorizedClientProvider = ClientCredentialsReactiveOAuth2AuthorizedClientProvider()\n\t\tauthorizedClientProvider.setClockSkew(Duration.ofMinutes(5))\n\t\treturn authorizedClientProvider\n\t}\n\n}\n----\n=====\n\n[[oauth2-client-customize-request-parameters]]\n=== Customize Token Request Parameters\n\nThe need to customize request parameters when obtaining an access token is fairly common.\nFor example, let's say we want to add a custom `audience` parameter to the token request because the provider requires this parameter for the `authorization_code` grant.\n\nWe can simply publish a bean of type `ReactiveOAuth2AccessTokenResponseClient` with the generic type `OAuth2AuthorizationCodeGrantRequest` and it will be used by Spring Security to configure OAuth2 Client components.\n\nThe following example customizes token request parameters for the `authorization_code` grant:\n\n.Customize Token Request Parameters for Authorization Code Grant\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {\n\t\tWebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew WebClientReactiveAuthorizationCodeTokenResponseClient();\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\tprivate static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {\n\t\treturn (grantRequest) -> {\n\t\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\t\tparameters.set(\"audience\", \"xyz_value\");\n\n\t\t\treturn parameters;\n\t\t};\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun authorizationCodeAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {\n\t\tval accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\tprivate fun parametersConverter(): Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> {\n\t\treturn Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> { grantRequest ->\n\t\t\tLinkedMultiValueMap<String, String>().also { parameters ->\n\t\t\t\tparameters[\"audience\"] = \"xyz_value\"\n\t\t\t}\n\t\t}\n\t}\n\n}\n----\n=====\n\n[TIP]\n====\nNotice that we don't need to customize the `SecurityWebFilterChain` bean in this case, and can stick with the defaults.\nIf using Spring Boot with no additional customizations, we can actually omit the `SecurityWebFilterChain` bean entirely.\n====\n\nAs you can see, providing the `ReactiveOAuth2AccessTokenResponseClient` as a bean is quite convenient.\nWhen using the Spring Security DSL directly, we need to ensure that this customization is applied for both OAuth2 Login (if we are using this feature) and OAuth2 Client components.\nTo understand what is being configured behind the scenes, here's what the configuration would look like with the DSL:\n\n.Customize Token Request Parameters for Authorization Code Grant using the DSL\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\tWebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew WebClientReactiveAuthorizationCodeTokenResponseClient();\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter());\n\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login((oauth2Login) -> oauth2Login\n\t\t\t\t.authenticationManager(new DelegatingReactiveAuthenticationManager(\n\t\t\t\t\tnew OidcAuthorizationCodeReactiveAuthenticationManager(\n\t\t\t\t\t\taccessTokenResponseClient, new OidcReactiveOAuth2UserService()\n\t\t\t\t\t),\n\t\t\t\t\tnew OAuth2LoginReactiveAuthenticationManager(\n\t\t\t\t\t\taccessTokenResponseClient, new DefaultReactiveOAuth2UserService()\n\t\t\t\t\t)\n\t\t\t\t))\n\t\t\t)\n\t\t\t.oauth2Client((oauth2Client) -> oauth2Client\n\t\t\t\t.authenticationManager(new OAuth2AuthorizationCodeReactiveAuthenticationManager(\n\t\t\t\t\taccessTokenResponseClient\n\t\t\t\t))\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n\n\tprivate static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\n@EnableWebFluxSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n\t\tval accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter())\n\n\t\treturn http {\n\t\t\tauthorizeExchange {\n\t\t\t\tauthorize(anyExchange, authenticated)\n\t\t\t}\n\t\t\toauth2Login {\n\t\t\t\tauthenticationManager = DelegatingReactiveAuthenticationManager(\n\t\t\t\t\tOidcAuthorizationCodeReactiveAuthenticationManager(\n\t\t\t\t\t\taccessTokenResponseClient, OidcReactiveOAuth2UserService()\n\t\t\t\t\t),\n\t\t\t\t\tOAuth2LoginReactiveAuthenticationManager(\n\t\t\t\t\t\taccessTokenResponseClient, DefaultReactiveOAuth2UserService()\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t}\n\t\t\toauth2Client {\n\t\t\t\tauthenticationManager = OAuth2AuthorizationCodeReactiveAuthenticationManager(\n\t\t\t\t\taccessTokenResponseClient\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fun parametersConverter(): Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\nFor other grant types we can publish additional `ReactiveOAuth2AccessTokenResponseClient` beans to override the defaults.\nFor example, to customize token requests for the `client_credentials` grant we can publish the following bean:\n\n.Customize Token Request Parameters for Client Credentials Grant\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {\n\t\tWebClientReactiveClientCredentialsTokenResponseClient accessTokenResponseClient =\n\t\t\t\tnew WebClientReactiveClientCredentialsTokenResponseClient();\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\tprivate static Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun clientCredentialsAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {\n\t\tval accessTokenResponseClient = WebClientReactiveClientCredentialsTokenResponseClient()\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\tprivate fun parametersConverter(): Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\nSpring Security automatically resolves the following generic types of `ReactiveOAuth2AccessTokenResponseClient` beans:\n\n* `OAuth2AuthorizationCodeGrantRequest` (see `WebClientReactiveAuthorizationCodeTokenResponseClient`)\n* `OAuth2RefreshTokenGrantRequest` (see `WebClientReactiveRefreshTokenTokenResponseClient`)\n* `OAuth2ClientCredentialsGrantRequest` (see `WebClientReactiveClientCredentialsTokenResponseClient`)\n* `JwtBearerGrantRequest` (see `WebClientReactiveJwtBearerTokenResponseClient`)\n* `TokenExchangeGrantRequest` (see `WebClientReactiveTokenExchangeTokenResponseClient`)\n\n[TIP]\n====\nPublishing a bean of type `ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest>` will automatically enable the `jwt-bearer` grant type without the need to <<oauth2-client-enable-extension-grant-type,configure it separately>>.\n====\n\n[TIP]\n====\nPublishing a bean of type `ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest>` will automatically enable the `token-exchange` grant type without the need to <<oauth2-client-enable-extension-grant-type,configure it separately>>.\n====\n\n[[oauth2-client-customize-web-client]]\n=== Customize the `WebClient` used by OAuth2 Client Components\n\nAnother common use case is the need to customize the `WebClient` used when obtaining an access token.\nWe might need to do this to customize the underlying HTTP client library (via a custom `ClientHttpConnector`) to configure SSL settings or to apply proxy settings for a corporate network.\n\nWith Spring Security 6.3 and later, we can simply publish beans of type `ReactiveOAuth2AccessTokenResponseClient` and Spring Security will configure and publish a `ReactiveOAuth2AuthorizedClientManager` bean for us.\n\nThe following example customizes the `WebClient` for all of the supported grant types:\n\n.Customize `WebClient` for OAuth2 Client\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {\n\t\tWebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew WebClientReactiveAuthorizationCodeTokenResponseClient();\n\t\taccessTokenResponseClient.setWebClient(webClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {\n\t\tWebClientReactiveRefreshTokenTokenResponseClient accessTokenResponseClient =\n\t\t\tnew WebClientReactiveRefreshTokenTokenResponseClient();\n\t\taccessTokenResponseClient.setWebClient(webClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {\n\t\tWebClientReactiveClientCredentialsTokenResponseClient accessTokenResponseClient =\n\t\t\tnew WebClientReactiveClientCredentialsTokenResponseClient();\n\t\taccessTokenResponseClient.setWebClient(webClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {\n\t\tWebClientReactiveJwtBearerTokenResponseClient accessTokenResponseClient =\n\t\t\tnew WebClientReactiveJwtBearerTokenResponseClient();\n\t\taccessTokenResponseClient.setWebClient(webClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {\n\t\tWebClientReactiveTokenExchangeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew WebClientReactiveTokenExchangeTokenResponseClient();\n\t\taccessTokenResponseClient.setWebClient(webClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic WebClient webClient() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun authorizationCodeAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {\n\t\tval accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()\n\t\taccessTokenResponseClient.setWebClient(webClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun refreshTokenAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> {\n\t\tval accessTokenResponseClient = WebClientReactiveRefreshTokenTokenResponseClient()\n\t\taccessTokenResponseClient.setWebClient(webClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun clientCredentialsAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {\n\t\tval accessTokenResponseClient = WebClientReactiveClientCredentialsTokenResponseClient()\n\t\taccessTokenResponseClient.setWebClient(webClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun jwtBearerAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> {\n\t\tval accessTokenResponseClient = WebClientReactiveJwtBearerTokenResponseClient()\n\t\taccessTokenResponseClient.setWebClient(webClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun tokenExchangeAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> {\n\t\tval accessTokenResponseClient = WebClientReactiveTokenExchangeTokenResponseClient()\n\t\taccessTokenResponseClient.setWebClient(webClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun webClient(): WebClient {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\nA default `ReactiveOAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided.\n\n[TIP]\n====\nNotice that we don't need to customize the `SecurityWebFilterChain` bean in this case, and can stick with the defaults.\nIf using Spring Boot with no additional customizations, we can actually omit the `SecurityWebFilterChain` bean entirely.\n====\n\nPrior to Spring Security 6.3, we had to ensure this customization was applied to OAuth2 Client components ourselves.\nWhile we could publish a bean of type `ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>` for the `authorization_code` grant, we had to publish a bean of type `ReactiveOAuth2AuthorizedClientManager` for other grant types.\nTo understand what is being configured behind the scenes, here's what the configuration might have looked like:\n\n.Customize `WebClient` for OAuth2 Client (prior to 6.3)\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {\n\t\tWebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew WebClientReactiveAuthorizationCodeTokenResponseClient();\n\t\taccessTokenResponseClient.setWebClient(webClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2AuthorizedClientManager authorizedClientManager(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\t\tWebClientReactiveRefreshTokenTokenResponseClient refreshTokenAccessTokenResponseClient =\n\t\t\tnew WebClientReactiveRefreshTokenTokenResponseClient();\n\t\trefreshTokenAccessTokenResponseClient.setWebClient(webClient());\n\n\t\tWebClientReactiveClientCredentialsTokenResponseClient clientCredentialsAccessTokenResponseClient =\n\t\t\tnew WebClientReactiveClientCredentialsTokenResponseClient();\n\t\tclientCredentialsAccessTokenResponseClient.setWebClient(webClient());\n\n\t\tWebClientReactiveJwtBearerTokenResponseClient jwtBearerAccessTokenResponseClient =\n\t\t\tnew WebClientReactiveJwtBearerTokenResponseClient();\n\t\tjwtBearerAccessTokenResponseClient.setWebClient(webClient());\n\n\t\tJwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =\n\t\t\tnew JwtBearerReactiveOAuth2AuthorizedClientProvider();\n\t\tjwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient);\n\n\t\tWebClientReactiveTokenExchangeTokenResponseClient tokenExchangeAccessTokenResponseClient =\n\t\t\tnew WebClientReactiveTokenExchangeTokenResponseClient();\n\t\ttokenExchangeAccessTokenResponseClient.setWebClient(webClient());\n\n\t\tTokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =\n\t\t\tnew TokenExchangeReactiveOAuth2AuthorizedClientProvider();\n\t\ttokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient);\n\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken((refreshToken) -> refreshToken\n\t\t\t\t\t.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)\n\t\t\t\t)\n\t\t\t\t.clientCredentials((clientCredentials) -> clientCredentials\n\t\t\t\t\t.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)\n\t\t\t\t)\n\t\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t\t.provider(tokenExchangeAuthorizedClientProvider)\n\t\t\t\t.build();\n\n\t\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t\treturn authorizedClientManager;\n\t}\n\n\t@Bean\n\tpublic WebClient webClient() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.web.server.invoke\n\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun authorizationCodeAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {\n\t\tval accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()\n\t\taccessTokenResponseClient.setWebClient(webClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun authorizedClientManager(\n\t\tclientRegistrationRepository: ReactiveClientRegistrationRepository?,\n\t\tauthorizedClientRepository: ServerOAuth2AuthorizedClientRepository?\n\t): ReactiveOAuth2AuthorizedClientManager {\n\t\tval refreshTokenAccessTokenResponseClient = WebClientReactiveRefreshTokenTokenResponseClient()\n\t\trefreshTokenAccessTokenResponseClient.setWebClient(webClient())\n\n\t\tval clientCredentialsAccessTokenResponseClient = WebClientReactiveClientCredentialsTokenResponseClient()\n\t\tclientCredentialsAccessTokenResponseClient.setWebClient(webClient())\n\n\t\tval jwtBearerAccessTokenResponseClient = WebClientReactiveJwtBearerTokenResponseClient()\n\t\tjwtBearerAccessTokenResponseClient.setWebClient(webClient())\n\n\t\tval jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()\n\t\tjwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient)\n\n\t\tval tokenExchangeAccessTokenResponseClient = WebClientReactiveTokenExchangeTokenResponseClient()\n\t\ttokenExchangeAccessTokenResponseClient.setWebClient(webClient())\n\n\t\tval tokenExchangeAuthorizedClientProvider = TokenExchangeReactiveOAuth2AuthorizedClientProvider()\n\t\ttokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient)\n\n\t\tval authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken { refreshToken ->\n\t\t\t\trefreshToken.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)\n\t\t\t}\n\t\t\t.clientCredentials { clientCredentials ->\n\t\t\t\tclientCredentials.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)\n\t\t\t}\n\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t.provider(tokenExchangeAuthorizedClientProvider)\n\t\t\t.build()\n\n\t\tval authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\tclientRegistrationRepository, authorizedClientRepository\n\t\t)\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n\n\t\treturn authorizedClientManager\n\t}\n\n\t@Bean\n\tfun webClient(): WebClient {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\n\n[[further-reading]]\n== Further Reading\n\nThe preceding sections introduced Spring Security's support for OAuth2 with examples for common scenarios.\nYou can read more about OAuth2 Client and Resource Server in the following sections of the reference documentation:\n\n* xref:reactive/oauth2/login/index.adoc[]\n* xref:reactive/oauth2/client/index.adoc[]\n* xref:reactive/oauth2/resource-server/index.adoc[]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/login/advanced.adoc",
    "content": "[[webflux-oauth2-login-advanced]]\n= Advanced Configuration\n\nThe OAuth 2.0 Authorization Framework defines the https://tools.ietf.org/html/rfc6749#section-3[Protocol Endpoints] as follows:\n\nThe authorization process utilizes two authorization server endpoints (HTTP resources):\n\n* Authorization Endpoint: Used by the client to obtain authorization from the resource owner via user-agent redirection.\n* Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication.\n\nAs well as one client endpoint:\n\n* Redirection Endpoint: Used by the authorization server to return responses containing authorization credentials to the client via the resource owner user-agent.\n\nThe OpenID Connect Core 1.0 specification defines the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] as follows:\n\nThe UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user.\nTo obtain the requested claims about the end-user, the client makes a request to the UserInfo Endpoint by using an access token obtained through OpenID Connect Authentication.\nThese claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims.\n\n`ServerHttpSecurity.oauth2Login()` provides a number of configuration options for customizing OAuth 2.0 Login.\n\nThe following code shows the complete configuration options available for the `oauth2Login()` DSL:\n\n.OAuth2 Login Configuration Options\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t.authenticationConverter(this.authenticationConverter())\n\t\t\t\t.authenticationMatcher(this.authenticationMatcher())\n\t\t\t\t.authenticationManager(this.authenticationManager())\n\t\t\t\t.authenticationSuccessHandler(this.authenticationSuccessHandler())\n\t\t\t\t.authenticationFailureHandler(this.authenticationFailureHandler())\n\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository())\n\t\t\t\t.authorizedClientRepository(this.authorizedClientRepository())\n\t\t\t\t.authorizedClientService(this.authorizedClientService())\n\t\t\t\t.authorizationRequestResolver(this.authorizationRequestResolver())\n\t\t\t\t.authorizationRequestRepository(this.authorizationRequestRepository())\n\t\t\t\t.securityContextRepository(this.securityContextRepository())\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Login {\n                authenticationConverter = authenticationConverter()\n                authenticationMatcher = authenticationMatcher()\n                authenticationManager = authenticationManager()\n                authenticationSuccessHandler = authenticationSuccessHandler()\n                authenticationFailureHandler = authenticationFailureHandler()\n                clientRegistrationRepository = clientRegistrationRepository()\n                authorizedClientRepository = authorizedClientRepository()\n                authorizedClientService = authorizedClientService()\n                authorizationRequestResolver = authorizationRequestResolver()\n                authorizationRequestRepository = authorizationRequestRepository()\n                securityContextRepository = securityContextRepository()\n            }\n        }\n\n        return http.build()\n    }\n}\n----\n======\n\nThe following sections go into more detail on each of the configuration options available:\n\n* <<webflux-oauth2-login-advanced-login-page, OAuth 2.0 Login Page>>\n* <<webflux-oauth2-login-advanced-redirection-endpoint, Redirection Endpoint>>\n* <<webflux-oauth2-login-advanced-userinfo-endpoint, UserInfo Endpoint>>\n* <<webflux-oauth2-login-advanced-idtoken-verify, ID Token Signature Verification>>\n* <<webflux-oauth2-login-advanced-oidc-logout, OpenID Connect 1.0 Logout>>\n\n\n[[webflux-oauth2-login-advanced-login-page]]\n== OAuth 2.0 Login Page\n\nBy default, the OAuth 2.0 Login Page is auto-generated by the `LoginPageGeneratingWebFilter`.\nThe default login page shows each configured OAuth Client with its `ClientRegistration.clientName` as a link, which is capable of initiating the Authorization Request (or OAuth 2.0 Login).\n\n[NOTE]\nIn order for `LoginPageGeneratingWebFilter` to show links for configured OAuth Clients, the registered `ReactiveClientRegistrationRepository` needs to also implement `Iterable<ClientRegistration>`.\nSee `InMemoryReactiveClientRegistrationRepository` for reference.\n\nThe link's destination for each OAuth Client defaults to the following:\n\n`+\"/oauth2/authorization/{registrationId}\"+`\n\nThe following line shows an example:\n\n[source,html]\n----\n<a href=\"/oauth2/authorization/google\">Google</a>\n----\n\nTo override the default login page, configure the `exceptionHandling().authenticationEntryPoint()` and (optionally) `oauth2Login().authorizationRequestResolver()`.\n\nThe following listing shows an example:\n\n.OAuth2 Login Page Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"-attributes\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.exceptionHandling((exceptionHandling) -> exceptionHandling\n\t\t\t\t.authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint(\"/login/oauth2\"))\n\t\t\t)\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t.authorizationRequestResolver(this.authorizationRequestResolver())\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n\n\tprivate ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver() {\n\t\tServerWebExchangeMatcher authorizationRequestMatcher =\n\t\t\t\tnew PathPatternParserServerWebExchangeMatcher(\n\t\t\t\t\t\t\"/login/oauth2/authorization/{registrationId}\");\n\n\t\treturn new DefaultServerOAuth2AuthorizationRequestResolver(\n\t\t\t\tthis.clientRegistrationRepository(), authorizationRequestMatcher);\n\t}\n\n\t...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"-attributes\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            exceptionHandling {\n                authenticationEntryPoint = RedirectServerAuthenticationEntryPoint(\"/login/oauth2\")\n            }\n            oauth2Login {\n                authorizationRequestResolver = authorizationRequestResolver()\n            }\n        }\n\n        return http.build()\n    }\n\n    private fun authorizationRequestResolver(): ServerOAuth2AuthorizationRequestResolver {\n        val authorizationRequestMatcher: ServerWebExchangeMatcher = PathPatternParserServerWebExchangeMatcher(\n            \"/login/oauth2/authorization/{registrationId}\"\n        )\n\n        return DefaultServerOAuth2AuthorizationRequestResolver(\n            clientRegistrationRepository(), authorizationRequestMatcher\n        )\n    }\n\n    ...\n}\n----\n======\n\n[IMPORTANT]\nYou need to provide a `@Controller` with a `@RequestMapping(\"/login/oauth2\")` that is capable of rendering the custom login page.\n\n[TIP]\n====\nAs noted earlier, configuring `oauth2Login().authorizationRequestResolver()` is optional.\nHowever, if you choose to customize it, ensure the link to each OAuth Client matches the pattern provided through the `ServerWebExchangeMatcher`.\n\nThe following line shows an example:\n\n[source,html]\n----\n<a href=\"/login/oauth2/authorization/google\">Google</a>\n----\n====\n\n\n[[webflux-oauth2-login-advanced-redirection-endpoint]]\n== Redirection Endpoint\n\nThe Redirection Endpoint is used by the Authorization Server for returning the Authorization Response (which contains the authorization credentials) to the client via the Resource Owner user-agent.\n\n[TIP]\nOAuth 2.0 Login leverages the Authorization Code Grant.\nTherefore, the authorization credential is the authorization code.\n\nThe default Authorization Response redirection endpoint is `+/login/oauth2/code/{registrationId}+`.\n\nIf you would like to customize the Authorization Response redirection endpoint, configure it as shown in the following example:\n\n.Redirection Endpoint Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"-attributes\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t.authenticationMatcher(new PathPatternParserServerWebExchangeMatcher(\"/login/oauth2/callback/{registrationId}\"))\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"-attributes\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Login {\n                authenticationMatcher = PathPatternParserServerWebExchangeMatcher(\"/login/oauth2/callback/{registrationId}\")\n            }\n        }\n\n        return http.build()\n    }\n}\n----\n======\n\n[IMPORTANT]\n====\nYou also need to ensure the `ClientRegistration.redirectUri` matches the custom Authorization Response redirection endpoint.\n\nThe following listing shows an example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"-attributes\"]\n----\nreturn CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t.clientId(\"google-client-id\")\n\t.clientSecret(\"google-client-secret\")\n\t.redirectUri(\"{baseUrl}/login/oauth2/callback/{registrationId}\")\n\t.build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"-attributes\"]\n----\nreturn CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n    .clientId(\"google-client-id\")\n    .clientSecret(\"google-client-secret\")\n    .redirectUri(\"{baseUrl}/login/oauth2/callback/{registrationId}\")\n    .build()\n----\n======\n====\n\n\n[[webflux-oauth2-login-advanced-userinfo-endpoint]]\n== UserInfo Endpoint\n\nThe UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections:\n\n* <<webflux-oauth2-login-advanced-map-authorities, Mapping User Authorities>>\n* <<webflux-oauth2-login-advanced-oauth2-user-service, OAuth 2.0 UserService>>\n* <<webflux-oauth2-login-advanced-oidc-user-service, OpenID Connect 1.0 UserService>>\n\n\n[[webflux-oauth2-login-advanced-map-authorities]]\n=== Mapping User Authorities\n\nAfter the user successfully authenticates with the OAuth 2.0 Provider, the `OAuth2User.getAuthorities()` (or `OidcUser.getAuthorities()`) contains a list of granted authorities populated from `OAuth2UserRequest.getAccessToken().getScopes()` and prefixed with `SCOPE_`.\nThese granted authorities may be mapped to a new set of `GrantedAuthority` instances, which will be supplied to `OAuth2AuthenticationToken` when completing the authentication.\n\n[TIP]\n`OAuth2AuthenticationToken.getAuthorities()` is used for authorizing requests, such as in `hasRole('USER')` or `hasRole('ADMIN')`.\n\nThere are a couple of options to choose from when mapping user authorities:\n\n* <<webflux-oauth2-login-advanced-map-authorities-grantedauthoritiesmapper, Using a GrantedAuthoritiesMapper>>\n* <<webflux-oauth2-login-advanced-map-authorities-reactiveoauth2userservice, Delegation-based strategy with ReactiveOAuth2UserService>>\n\n\n[[webflux-oauth2-login-advanced-map-authorities-grantedauthoritiesmapper]]\n==== Using a GrantedAuthoritiesMapper\n\nThe `GrantedAuthoritiesMapper` is given a list of granted authorities which contains a special authority of type `OAuth2UserAuthority` and the authority string `OAUTH2_USER` (or `OidcUserAuthority` and the authority string `OIDC_USER`).\n\nRegister a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as shown in the following example:\n\n.Granted Authorities Mapper Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t...\n\t\t\t.oauth2Login(withDefaults());\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic GrantedAuthoritiesMapper userAuthoritiesMapper() {\n\t\treturn (authorities) -> {\n\t\t\tSet<GrantedAuthority> mappedAuthorities = new HashSet<>();\n\n\t\t\tauthorities.forEach(authority -> {\n\t\t\t\tif (OidcUserAuthority.class.isInstance(authority)) {\n\t\t\t\t\tOidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;\n\n\t\t\t\t\tOidcIdToken idToken = oidcUserAuthority.getIdToken();\n\t\t\t\t\tOidcUserInfo userInfo = oidcUserAuthority.getUserInfo();\n\n\t\t\t\t\t// Map the claims found in idToken and/or userInfo\n\t\t\t\t\t// to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t\t} else if (OAuth2UserAuthority.class.isInstance(authority)) {\n\t\t\t\t\tOAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;\n\n\t\t\t\t\tMap<String, Object> userAttributes = oauth2UserAuthority.getAttributes();\n\n\t\t\t\t\t// Map the attributes found in userAttributes\n\t\t\t\t\t// to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn mappedAuthorities;\n\t\t};\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Login { }\n        }\n\n        return http.build()\n    }\n\n    @Bean\n    fun userAuthoritiesMapper(): GrantedAuthoritiesMapper = GrantedAuthoritiesMapper { authorities: Collection<GrantedAuthority> ->\n        val mappedAuthorities = emptySet<GrantedAuthority>()\n\n        authorities.forEach { authority ->\n            if (authority is OidcUserAuthority) {\n                val idToken = authority.idToken\n                val userInfo = authority.userInfo\n                // Map the claims found in idToken and/or userInfo\n                // to one or more GrantedAuthority's and add it to mappedAuthorities\n            } else if (authority is OAuth2UserAuthority) {\n                val userAttributes = authority.attributes\n                // Map the attributes found in userAttributes\n                // to one or more GrantedAuthority's and add it to mappedAuthorities\n            }\n        }\n\n        mappedAuthorities\n    }\n}\n----\n======\n\n[[webflux-oauth2-login-advanced-map-authorities-reactiveoauth2userservice]]\n==== Delegation-based strategy with ReactiveOAuth2UserService\n\nThis strategy is advanced compared to using a `GrantedAuthoritiesMapper`, however, it's also more flexible as it gives you access to the `OAuth2UserRequest` and `OAuth2User` (when using an OAuth 2.0 UserService) or `OidcUserRequest` and `OidcUser` (when using an OpenID Connect 1.0 UserService).\n\nThe `OAuth2UserRequest` (and `OidcUserRequest`) provides you access to the associated `OAuth2AccessToken`, which is very useful in the cases where the _delegator_ needs to fetch authority information from a protected resource before it can map the custom authorities for the user.\n\nThe following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:\n\n.ReactiveOAuth2UserService Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t...\n\t\t\t.oauth2Login(withDefaults());\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {\n\t\tfinal OidcReactiveOAuth2UserService delegate = new OidcReactiveOAuth2UserService();\n\n\t\treturn (userRequest) -> {\n\t\t\t// Delegate to the default implementation for loading a user\n\t\t\treturn delegate.loadUser(userRequest)\n\t\t\t\t\t.flatMap((oidcUser) -> {\n\t\t\t\t\t\tOAuth2AccessToken accessToken = userRequest.getAccessToken();\n\t\t\t\t\t\tSet<GrantedAuthority> mappedAuthorities = new HashSet<>();\n\n\t\t\t\t\t\t// TODO\n\t\t\t\t\t\t// 1) Fetch the authority information from the protected resource using accessToken\n\t\t\t\t\t\t// 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t\t\t\t// 3) Create a copy of oidcUser but use the mappedAuthorities instead\n\t\t\t\t\t\tProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();\n\t\t\t\t\t\tString userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();\n\t\t\t\t\t\tif (StringUtils.hasText(userNameAttributeName)) {\n\t\t\t\t\t\t\toidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo(), userNameAttributeName);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\toidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn Mono.just(oidcUser);\n\t\t\t\t\t});\n\t\t};\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Login { }\n        }\n\n        return http.build()\n    }\n\n    @Bean\n    fun oidcUserService(): ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {\n        val delegate = OidcReactiveOAuth2UserService()\n\n        return ReactiveOAuth2UserService { userRequest ->\n            // Delegate to the default implementation for loading a user\n            delegate.loadUser(userRequest)\n                .flatMap { oidcUser ->\n                    val accessToken = userRequest.accessToken\n                    val mappedAuthorities = mutableSetOf<GrantedAuthority>()\n\n                    // TODO\n                    // 1) Fetch the authority information from the protected resource using accessToken\n                    // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities\n                    // 3) Create a copy of oidcUser but use the mappedAuthorities instead\n                    val providerDetails = userRequest.getClientRegistration().getProviderDetails()\n                    val userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName()\n                    val mappedOidcUser = if (StringUtils.hasText(userNameAttributeName)) {\n                        DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo, userNameAttributeName)\n                    } else {\n                        DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo)\n                    }\n\n                    Mono.just(mappedOidcUser)\n                }\n        }\n    }\n}\n----\n======\n\n\n[[webflux-oauth2-login-advanced-oauth2-user-service]]\n=== OAuth 2.0 UserService\n\n`DefaultReactiveOAuth2UserService` is an implementation of a `ReactiveOAuth2UserService` that supports standard OAuth 2.0 Provider's.\n\n[NOTE]\n`ReactiveOAuth2UserService` obtains the user attributes of the end-user (the resource owner) from the UserInfo Endpoint (by using the access token granted to the client during the authorization flow) and returns an `AuthenticatedPrincipal` in the form of an `OAuth2User`.\n\n`DefaultReactiveOAuth2UserService` uses a `WebClient` when requesting the user attributes at the UserInfo Endpoint.\n\nIf you need to customize the pre-processing of the UserInfo Request and/or the post-handling of the UserInfo Response, you will need to provide `DefaultReactiveOAuth2UserService.setWebClient()` with a custom configured `WebClient`.\n\nWhether you customize `DefaultReactiveOAuth2UserService` or provide your own implementation of `ReactiveOAuth2UserService`, you'll need to configure it as shown in the following example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t...\n\t\t\t.oauth2Login(withDefaults());\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {\n\t\t...\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Login { }\n        }\n\n        return http.build()\n    }\n\n    @Bean\n    fun oauth2UserService(): ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {\n        // ...\n    }\n}\n----\n======\n\n\n[[webflux-oauth2-login-advanced-oidc-user-service]]\n=== OpenID Connect 1.0 UserService\n\n`OidcReactiveOAuth2UserService` is an implementation of a `ReactiveOAuth2UserService` that supports OpenID Connect 1.0 Provider's.\n\nThe `OidcReactiveOAuth2UserService` leverages the `DefaultReactiveOAuth2UserService` when requesting the user attributes at the UserInfo Endpoint.\n\nIf you need to customize the pre-processing of the UserInfo Request and/or the post-handling of the UserInfo Response, you will need to provide `OidcReactiveOAuth2UserService.setOauth2UserService()` with a custom configured `ReactiveOAuth2UserService`.\n\nWhether you customize `OidcReactiveOAuth2UserService` or provide your own implementation of `ReactiveOAuth2UserService` for OpenID Connect 1.0 Provider's, you'll need to configure it as shown in the following example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t...\n\t\t\t.oauth2Login(withDefaults());\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {\n\t\t...\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            oauth2Login { }\n        }\n\n        return http.build()\n    }\n\n    @Bean\n    fun oidcUserService(): ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {\n        // ...\n    }\n}\n----\n======\n\n\n[[webflux-oauth2-login-advanced-idtoken-verify]]\n== ID Token Signature Verification\n\nOpenID Connect 1.0 Authentication introduces the https://openid.net/specs/openid-connect-core-1_0.html#IDToken[ID Token], which is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when used by a Client.\n\nThe ID Token is represented as a https://tools.ietf.org/html/rfc7519[JSON Web Token] (JWT) and MUST be signed using https://tools.ietf.org/html/rfc7515[JSON Web Signature] (JWS).\n\nThe `ReactiveOidcIdTokenDecoderFactory` provides a `ReactiveJwtDecoder` used for `OidcIdToken` signature verification. The default algorithm is `RS256` but may be different when assigned during client registration.\nFor these cases, a resolver may be configured to return the expected JWS algorithm assigned for a specific client.\n\nThe JWS algorithm resolver is a `Function` that accepts a `ClientRegistration` and returns the expected `JwsAlgorithm` for the client, e.g. `SignatureAlgorithm.RS256` or `MacAlgorithm.HS256`\n\nThe following code shows how to configure the `OidcIdTokenDecoderFactory` `@Bean` to default to `MacAlgorithm.HS256` for all `ClientRegistration`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveJwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {\n\tReactiveOidcIdTokenDecoderFactory idTokenDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();\n\tidTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> clientRegistration.HS256);\n\treturn idTokenDecoderFactory;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun idTokenDecoderFactory(): ReactiveJwtDecoderFactory<ClientRegistration> {\n    val idTokenDecoderFactory = ReactiveOidcIdTokenDecoderFactory()\n    idTokenDecoderFactory.setJwsAlgorithmResolver { MacAlgorithm.HS256 }\n    return idTokenDecoderFactory\n}\n----\n======\n\n[NOTE]\nFor MAC based algorithms such as `HS256`, `HS384` or `HS512`, the `client-secret` corresponding to the `client-id` is used as the symmetric key for signature verification.\n\n[TIP]\nIf more than one `ClientRegistration` is configured for OpenID Connect 1.0 Authentication, the JWS algorithm resolver may evaluate the provided `ClientRegistration` to determine which algorithm to return.\n\n[[webflux-oauth2-login-advanced-oidc-logout]]\nThen, you can proceed to configure xref:reactive/oauth2/login/logout.adoc[logout].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/login/core.adoc",
    "content": "= Core Configuration\n\n[[webflux-oauth2-login-sample]]\n== Spring Boot Sample\n\nSpring Boot brings full auto-configuration capabilities for OAuth 2.0 Login.\n\nThis section shows how to configure the {gh-samples-url}/boot/oauth2login-webflux[*OAuth 2.0 Login WebFlux sample*] by using _Google_ as the _Authentication Provider_ and covers the following topics:\n\n* <<webflux-oauth2-login-sample-setup>>\n* <<webflux-oauth2-login-sample-redirect>>\n* <<webflux-oauth2-login-sample-config>>\n* <<webflux-oauth2-login-sample-start>>\n\n\n[[webflux-oauth2-login-sample-setup]]\n=== Initial Setup\n\nTo use Google's OAuth 2.0 authentication system for login, you must set up a project in the Google API Console to obtain OAuth 2.0 credentials.\n\n[NOTE]\n====\nhttps://developers.google.com/identity/protocols/OpenIDConnect[Google's OAuth 2.0 implementation] for authentication conforms to the  https://openid.net/connect/[OpenID Connect 1.0] specification and is https://openid.net/certification/[OpenID Certified].\n====\n\nFollow the instructions on the https://developers.google.com/identity/protocols/OpenIDConnect[OpenID Connect] page, starting in the \"`Setting up OAuth 2.0`\" section.\n\nAfter completing the \"`Obtain OAuth 2.0 credentials`\" instructions, you should have a new OAuth Client with credentials that consist of a Client ID and a Client Secret.\n\n\n[[webflux-oauth2-login-sample-redirect]]\n=== Setting the Redirect URI\n\nThe redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Google and have been granted access to the OAuth Client (<<webflux-oauth2-login-sample-setup,created in the previous step>>) on the consent page.\n\nIn the \"`Set a redirect URI`\" sub-section, ensure that the *Authorized redirect URIs* field is set to `http://localhost:8080/login/oauth2/code/google`.\n\n[TIP]\n====\nThe default redirect URI template is `+{baseUrl}/login/oauth2/code/{registrationId}+`.\nThe *_registrationId_* is a unique identifier for the xref:reactive/oauth2/client/core.adoc#oauth2Client-client-registration[ClientRegistration].\nFor our example, the `registrationId` is `google`.\n====\n\n[IMPORTANT]\n====\nIf the OAuth Client is running behind a proxy server, it is recommended to check xref:features/exploits/http.adoc#http-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured.\nAlso, see the supported xref:reactive/oauth2/client/authorization-grants.adoc#oauth2Client-auth-code-redirect-uri[ `URI` template variables] for `redirect-uri`.\n====\n\n[[webflux-oauth2-login-sample-config]]\n=== Configure `application.yml`\n\nNow that you have a new OAuth Client with Google, you need to configure the application to use the OAuth Client for the _authentication flow_.\nTo do so:\n\n. Go to `application.yml` and set the following configuration:\n+\n.OAuth Client properties\n====\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\t<1>\n          google:\t<2>\n            client-id: google-client-id\n            client-secret: google-client-secret\n----\n\n<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties.\n<2> Following the base property prefix is the ID for the xref:reactive/oauth2/client/core.adoc#oauth2Client-client-registration[`ClientRegistration`], such as google.\n====\n\n. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier.\n\n\n[[webflux-oauth2-login-sample-start]]\n=== Boot the Application\n\nLaunch the Spring Boot sample and go to `http://localhost:8080`.\nYou are then redirected to the default _auto-generated_ login page, which displays a link for Google.\n\nClick on the Google link, and you are then redirected to Google for authentication.\n\nAfter authenticating with your Google account credentials, the next page presented to you is the Consent screen.\nThe Consent screen asks you to either allow or deny access to the OAuth Client you created earlier.\nClick *Allow* to authorize the OAuth Client to access your email address and basic profile information.\n\nAt this point, the OAuth Client retrieves your email address and basic profile information from the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session.\n\n\n[[oauth2login-boot-property-mappings]]\n== Spring Boot Property Mappings\n\nThe following table outlines the mapping of the Spring Boot OAuth Client properties to the xref:reactive/oauth2/client/core.adoc#oauth2Client-client-registration[ClientRegistration] properties.\n\n|===\n|Spring Boot |ClientRegistration\n\n|`spring.security.oauth2.client.registration._[registrationId]_`\n|`registrationId`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.client-id`\n|`clientId`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.client-secret`\n|`clientSecret`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.client-authentication-method`\n|`clientAuthenticationMethod`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.authorization-grant-type`\n|`authorizationGrantType`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.redirect-uri`\n|`redirectUri`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.scope`\n|`scopes`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.client-name`\n|`clientName`\n\n|`spring.security.oauth2.client.provider._[providerId]_.authorization-uri`\n|`providerDetails.authorizationUri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.token-uri`\n|`providerDetails.tokenUri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.jwk-set-uri`\n|`providerDetails.jwkSetUri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.issuer-uri`\n|`providerDetails.issuerUri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.user-info-uri`\n|`providerDetails.userInfoEndpoint.uri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.user-info-authentication-method`\n|`providerDetails.userInfoEndpoint.authenticationMethod`\n\n|`spring.security.oauth2.client.provider._[providerId]_.user-name-attribute`\n|`providerDetails.userInfoEndpoint.userNameAttributeName`\n|===\n\n[TIP]\nA `ClientRegistration` can be initially configured using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint], by specifying the `spring.security.oauth2.client.provider._[providerId]_.issuer-uri` property.\n\n\n[[webflux-oauth2-login-common-oauth2-provider]]\n== CommonOAuth2Provider\n\n`CommonOAuth2Provider` pre-defines a set of default client properties for a number of well known providers: Google, GitHub, Facebook, X, and Okta.\n\nFor example, the `authorization-uri`, `token-uri`, and `user-info-uri` do not change often for a Provider.\nTherefore, it makes sense to provide default values in order to reduce the required configuration.\n\nAs demonstrated previously, when we <<webflux-oauth2-login-sample-config,configured a Google client>>, only the `client-id` and `client-secret` properties are required.\n\nThe following listing shows an example:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          google:\n            client-id: google-client-id\n            client-secret: google-client-secret\n----\n\n[TIP]\nThe auto-defaulting of client properties works seamlessly here because the `registrationId` (`google`) matches the `GOOGLE` `enum` (case-insensitive) in `CommonOAuth2Provider`.\n\nFor cases where you may want to specify a different `registrationId`, such as `google-login`, you can still leverage auto-defaulting of client properties by configuring the `provider` property.\n\nThe following listing shows an example:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          google-login:\t<1>\n            provider: google\t<2>\n            client-id: google-client-id\n            client-secret: google-client-secret\n----\n<1> The `registrationId` is set to `google-login`.\n<2> The `provider` property is set to `google`, which will leverage the auto-defaulting of client properties set in `CommonOAuth2Provider.GOOGLE.getBuilder()`.\n\n\n[[webflux-oauth2-login-custom-provider-properties]]\n== Configuring Custom Provider Properties\n\nThere are some OAuth 2.0 Providers that support multi-tenancy, which results in different protocol endpoints for each tenant (or sub-domain).\n\nFor example, an OAuth Client registered with Okta is assigned to a specific sub-domain and have their own protocol endpoints.\n\nFor these cases, Spring Boot provides the following base property for configuring custom provider properties: `spring.security.oauth2.client.provider._[providerId]_`.\n\nThe following listing shows an example:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n        provider:\n          okta:\t<1>\n            authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize\n            token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token\n            user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo\n            user-name-attribute: sub\n            jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys\n----\n\n<1> The base property (`spring.security.oauth2.client.provider.okta`) allows for custom configuration of protocol endpoint locations.\n\n\n[[webflux-oauth2-login-override-boot-autoconfig]]\n== Overriding Spring Boot Auto-configuration\n\nThe Spring Boot auto-configuration class for OAuth Client support is `ReactiveOAuth2ClientAutoConfiguration`.\n\nIt performs the following tasks:\n\n* Registers a `ReactiveClientRegistrationRepository` `@Bean` composed of `ClientRegistration`(s) from the configured OAuth Client properties.\n* Registers a `SecurityWebFilterChain` `@Bean` and enables OAuth 2.0 Login through `serverHttpSecurity.oauth2Login()`.\n\nIf you need to override the auto-configuration based on your specific requirements, you may do so in the following ways:\n\n* <<webflux-oauth2-login-register-reactiveclientregistrationrepository-bean,Register a ReactiveClientRegistrationRepository @Bean>>\n* <<webflux-oauth2-login-register-securitywebfilterchain-bean,Register a SecurityWebFilterChain @Bean>>\n* <<webflux-oauth2-login-completely-override-autoconfiguration,Completely Override the Auto-configuration>>\n\n\n[[webflux-oauth2-login-register-reactiveclientregistrationrepository-bean]]\n=== Register a ReactiveClientRegistrationRepository @Bean\n\nThe following example shows how to register a `ReactiveClientRegistrationRepository` `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Configuration\npublic class OAuth2LoginConfig {\n\n\t@Bean\n\tpublic ReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\treturn new InMemoryReactiveClientRegistrationRepository(this.googleClientRegistration());\n\t}\n\n\tprivate ClientRegistration googleClientRegistration() {\n\t\treturn ClientRegistration.withRegistrationId(\"google\")\n\t\t\t\t.clientId(\"google-client-id\")\n\t\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n\t\t\t\t.scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n\t\t\t\t.authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n\t\t\t\t.tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n\t\t\t\t.userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n\t\t\t\t.userNameAttributeName(IdTokenClaimNames.SUB)\n\t\t\t\t.jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n\t\t\t\t.clientName(\"Google\")\n\t\t\t\t.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Configuration\nclass OAuth2LoginConfig {\n\n    @Bean\n    fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {\n        return InMemoryReactiveClientRegistrationRepository(googleClientRegistration())\n    }\n\n    private fun googleClientRegistration(): ClientRegistration {\n        return ClientRegistration.withRegistrationId(\"google\")\n                .clientId(\"google-client-id\")\n                .clientSecret(\"google-client-secret\")\n                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n                .redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n                .scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n                .authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n                .tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n                .userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n                .userNameAttributeName(IdTokenClaimNames.SUB)\n                .jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n                .clientName(\"Google\")\n                .build()\n    }\n}\n----\n======\n\n\n[[webflux-oauth2-login-register-securitywebfilterchain-bean]]\n=== Register a SecurityWebFilterChain @Bean\n\nThe following example shows how to register a `SecurityWebFilterChain` `@Bean` with `@EnableWebFluxSecurity` and enable OAuth 2.0 login through `serverHttpSecurity.oauth2Login()`:\n\n.OAuth2 Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults());\n\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n            oauth2Login { }\n        }\n\n        return http.build()\n    }\n}\n----\n======\n\n\n[[webflux-oauth2-login-completely-override-autoconfiguration]]\n=== Completely Override the Auto-configuration\n\nThe following example shows how to completely override the auto-configuration by registering a `ReactiveClientRegistrationRepository` `@Bean` and a `SecurityWebFilterChain` `@Bean`.\n\n.Overriding the auto-configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults());\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\treturn new InMemoryReactiveClientRegistrationRepository(this.googleClientRegistration());\n\t}\n\n\tprivate ClientRegistration googleClientRegistration() {\n\t\treturn ClientRegistration.withRegistrationId(\"google\")\n\t\t\t\t.clientId(\"google-client-id\")\n\t\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n\t\t\t\t.scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n\t\t\t\t.authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n\t\t\t\t.tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n\t\t\t\t.userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n\t\t\t\t.userNameAttributeName(IdTokenClaimNames.SUB)\n\t\t\t\t.jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n\t\t\t\t.clientName(\"Google\")\n\t\t\t\t.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n            oauth2Login { }\n        }\n\n        return http.build()\n    }\n\n    @Bean\n    fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {\n        return InMemoryReactiveClientRegistrationRepository(googleClientRegistration())\n    }\n\n    private fun googleClientRegistration(): ClientRegistration {\n        return ClientRegistration.withRegistrationId(\"google\")\n                .clientId(\"google-client-id\")\n                .clientSecret(\"google-client-secret\")\n                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n                .redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n                .scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n                .authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n                .tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n                .userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n                .userNameAttributeName(IdTokenClaimNames.SUB)\n                .jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n                .clientName(\"Google\")\n                .build()\n    }\n}\n----\n======\n\n\n[[webflux-oauth2-login-javaconfig-wo-boot]]\n== Java Configuration without Spring Boot\n\nIf you are not able to use Spring Boot and would like to configure one of the pre-defined providers in `CommonOAuth2Provider` (for example, Google), apply the following configuration:\n\n.OAuth2 Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginConfig {\n\n\t@Bean\n\tpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults());\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ReactiveClientRegistrationRepository clientRegistrationRepository() {\n\t\treturn new InMemoryReactiveClientRegistrationRepository(this.googleClientRegistration());\n\t}\n\n\t@Bean\n\tpublic ReactiveOAuth2AuthorizedClientService authorizedClientService(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\treturn new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);\n\t}\n\n\t@Bean\n\tpublic ServerOAuth2AuthorizedClientRepository authorizedClientRepository(\n\t\t\tReactiveOAuth2AuthorizedClientService authorizedClientService) {\n\t\treturn new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);\n\t}\n\n\tprivate ClientRegistration googleClientRegistration() {\n\t\treturn CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t\t\t\t.clientId(\"google-client-id\")\n\t\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t\t.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginConfig {\n\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n            oauth2Login { }\n        }\n\n        return http.build()\n    }\n\n    @Bean\n    fun clientRegistrationRepository(): ReactiveClientRegistrationRepository {\n        return InMemoryReactiveClientRegistrationRepository(googleClientRegistration())\n    }\n\n    @Bean\n    fun authorizedClientService(\n        clientRegistrationRepository: ReactiveClientRegistrationRepository\n    ): ReactiveOAuth2AuthorizedClientService {\n        return InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository)\n    }\n\n    @Bean\n    fun authorizedClientRepository(\n        authorizedClientService: ReactiveOAuth2AuthorizedClientService\n    ): ServerOAuth2AuthorizedClientRepository {\n        return AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService)\n    }\n\n    private fun googleClientRegistration(): ClientRegistration {\n        return CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n                .clientId(\"google-client-id\")\n                .clientSecret(\"google-client-secret\")\n                .build()\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/login/index.adoc",
    "content": "[[webflux-oauth2-login]]\n= OAuth 2.0 Login\n:page-section-summary-toc: 1\n\nThe OAuth 2.0 Login feature provides an application with the ability to have users log in to the application by using their existing account at an OAuth 2.0 Provider (such as GitHub) or OpenID Connect 1.0 Provider (such as Google).\nOAuth 2.0 Login implements the \"Login with Google\" or \"Login with GitHub\" use cases.\n\n[NOTE]\n====\nOAuth 2.0 Login is implemented by using the *Authorization Code Grant*, as specified in the https://tools.ietf.org/html/rfc6749#section-4.1[OAuth 2.0 Authorization Framework] and https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[OpenID Connect Core 1.0].\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/login/logout.adoc",
    "content": "= OIDC Logout\n\nOnce an end user is able to login to your application, it's important to consider how they will log out.\n\nGenerally speaking, there are three use cases for you to consider:\n\n1. I want to perform only a local logout\n2. I want to log out both my application and the OIDC Provider, initiated by my application\n3. I want to log out both my application and the OIDC Provider, initiated by the OIDC Provider\n\n[[configure-local-logout]]\n== Local Logout\n\nTo perform a local logout, no special OIDC configuration is needed.\nSpring Security automatically stands up a local logout endpoint, which you can xref:reactive/authentication/logout.adoc[configure through the `logout()` DSL].\n\n[[configure-client-initiated-oidc-logout]]\n[[oauth2login-advanced-oidc-logout]]\n== OpenID Connect 1.0 Client-Initiated Logout\n\nOpenID Connect Session Management 1.0 allows the ability to log out the end user at the Provider by using the Client.\nOne of the strategies available is https://openid.net/specs/openid-connect-rpinitiated-1_0.html[RP-Initiated Logout].\n\nIf the OpenID Provider supports both Session Management and https://openid.net/specs/openid-connect-discovery-1_0.html[Discovery], the client can obtain the `end_session_endpoint` `URL` from the OpenID Provider's https://openid.net/specs/openid-connect-session-1_0.html#OPMetadata[Discovery Metadata].\nYou can do so by configuring the `ClientRegistration` with the `issuer-uri`, as follows:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            ...\n        provider:\n          okta:\n            issuer-uri: https://dev-1234.oktapreview.com\n----\n\nAlso, you should configure `OidcClientInitiatedServerLogoutSuccessHandler`, which implements RP-Initiated Logout, as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Autowired\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Bean\n\tpublic SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults())\n\t\t\t.logout((logout) -> logout\n\t\t\t\t.logoutSuccessHandler(oidcLogoutSuccessHandler())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate ServerLogoutSuccessHandler oidcLogoutSuccessHandler() {\n\t\tOidcClientInitiatedServerLogoutSuccessHandler oidcLogoutSuccessHandler =\n\t\t\t\tnew OidcClientInitiatedServerLogoutSuccessHandler(this.clientRegistrationRepository);\n\n\t\t// Sets the location that the End-User's User Agent will be redirected to\n\t\t// after the logout has been performed at the Provider\n\t\toidcLogoutSuccessHandler.setPostLogoutRedirectUri(\"{baseUrl}\");\n\n\t\treturn oidcLogoutSuccessHandler;\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\nclass OAuth2LoginSecurityConfig {\n    @Autowired\n    private lateinit var clientRegistrationRepository: ReactiveClientRegistrationRepository\n\n    @Bean\n    open fun filterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n            oauth2Login { }\n            logout {\n                logoutSuccessHandler = oidcLogoutSuccessHandler()\n            }\n        }\n        return http.build()\n    }\n\n    private fun oidcLogoutSuccessHandler(): ServerLogoutSuccessHandler {\n        val oidcLogoutSuccessHandler = OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository)\n\n        // Sets the location that the End-User's User Agent will be redirected to\n        // after the logout has been performed at the Provider\n        oidcLogoutSuccessHandler.setPostLogoutRedirectUri(\"{baseUrl}\")\n        return oidcLogoutSuccessHandler\n    }\n}\n----\n======\n\n[NOTE]\n====\n`OidcClientInitiatedServerLogoutSuccessHandler` supports the `+{baseUrl}+` placeholder.\nIf used, the application's base URL, such as `https://app.example.org`, replaces it at request time.\n====\n\n[NOTE]\n====\nBy default, `OidcClientInitiatedServerLogoutSuccessHandler` redirects to the logout URL using a standard HTTP redirect with the `GET` method.\nTo perform the logout using a `POST` request, set the redirect strategy to `FormPostServerRedirectStrategy`, for example with `OidcClientInitiatedServerLogoutSuccessHandler.setRedirectStrategy(new ServerFormPostRedirectStrategy())`.\n====\n\n[[configure-provider-initiated-oidc-logout]]\n== OpenID Connect 1.0 Back-Channel Logout\n\nOpenID Connect Session Management 1.0 allows the ability to log out the end user at the Client by having the Provider make an API call to the Client.\nThis is referred to as https://openid.net/specs/openid-connect-backchannel-1_0.html[OIDC Back-Channel Logout].\n\nTo enable this, you can stand up the Back-Channel Logout endpoint in the DSL like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nOidcBackChannelServerLogoutHandler oidcLogoutHandler() {\n\treturn new OidcBackChannelServerLogoutHandler();\n}\n\n@Bean\npublic SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception {\n    http\n        .authorizeExchange((authorize) -> authorize\n            .anyExchange().authenticated()\n        )\n        .oauth2Login(withDefaults())\n        .oidcLogout((logout) -> logout\n            .backChannel(Customizer.withDefaults())\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun oidcLogoutHandler(): OidcBackChannelLogoutHandler {\n    return OidcBackChannelLogoutHandler()\n}\n\n@Bean\nopen fun filterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        oauth2Login { }\n        oidcLogout {\n            backChannel { }\n        }\n    }\n    return http.build()\n}\n----\n======\n\nAnd that's it!\n\nThis will stand up the endpoint `+/logout/connect/back-channel/{registrationId}+` which the OIDC Provider can request to invalidate a given session of an end user in your application.\n\n[NOTE]\n`oidcLogout` requires that `oauth2Login` also be configured.\n\n[NOTE]\n`oidcLogout` requires that the session cookie be called `JSESSIONID` in order to correctly log out each session through a backchannel.\n\n=== Back-Channel Logout Architecture\n\nConsider a `ClientRegistration` whose identifier is `registrationId`.\n\nThe overall flow for a Back-Channel logout is like this:\n\n1. At login time, Spring Security correlates the ID Token, CSRF Token, and Provider Session ID (if any) to your application's session id in its `ReactiveOidcSessionRegistry` implementation.\n2. Then at logout time, your OIDC Provider makes an API call to `/logout/connect/back-channel/registrationId` including a Logout Token that indicates either the `sub` (the End User) or the `sid` (the Provider Session ID) to logout.\n3. Spring Security validates the token's signature and claims.\n4. If the token contains a `sid` claim, then only the Client's session that correlates to that provider session is terminated.\n5. Otherwise, if the token contains a `sub` claim, then all that Client's sessions for that End User are terminated.\n\n[NOTE]\nRemember that Spring Security's OIDC support is multi-tenant.\nThis means that it will only terminate sessions whose Client matches the `aud` claim in the Logout Token.\n\n=== Customizing the Session Logout Endpoint\n\nWith `OidcBackChannelServerLogoutHandler` published, the session logout endpoint is `+{baseUrl}+/logout/connect/back-channel/+{registrationId}+`.\n\nIf `OidcBackChannelServerLogoutHandler` is not wired, then the URL is `+{baseUrl}+/logout/connect/back-channel/+{registrationId}+`, which is not recommended since it requires passing a CSRF token, which can be challenging depending on the kind of repository your application uses.\n\nIn the event that you need to customize the endpoint, you can provide the URL as follows:\n\n\n[tabs]\n======\nJava::\n+\n[source=java,role=\"primary\"]\n----\nhttp\n    // ...\n    .oidcLogout((oidc) -> oidc\n        .backChannel((backChannel) -> backChannel\n            .logoutUri(\"http://localhost:9000/logout/connect/back-channel/+{registrationId}+\")\n        )\n    );\n----\n\nKotlin::\n+\n[source=kotlin,role=\"secondary\"]\n----\nhttp {\n    oidcLogout {\n        backChannel {\n            logoutUri = \"http://localhost:9000/logout/connect/back-channel/+{registrationId}+\"\n        }\n    }\n}\n----\n======\n\n=== Customizing the Session Logout Cookie Name\n\nBy default, the session logout endpoint uses the `JSESSIONID` cookie to correlate the session to the corresponding `OidcSessionInformation`.\n\nHowever, the default cookie name in Spring Session is `SESSION`.\n\nYou can configure Spring Session's cookie name in the DSL like so:\n\n[tabs]\n======\nJava::\n+\n[source=java,role=\"primary\"]\n----\n@Bean\nOidcBackChannelServerLogoutHandler oidcLogoutHandler(ReactiveOidcSessionRegistry sessionRegistry) {\n    OidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler(sessionRegistry);\n    logoutHandler.setSessionCookieName(\"SESSION\");\n    return logoutHandler;\n}\n----\n\nKotlin::\n+\n[source=kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun oidcLogoutHandler(val sessionRegistry: ReactiveOidcSessionRegistry): OidcBackChannelServerLogoutHandler {\n    val logoutHandler = OidcBackChannelServerLogoutHandler(sessionRegistry)\n    logoutHandler.setSessionCookieName(\"SESSION\")\n    return logoutHandler\n}\n----\n======\n\n[[oidc-backchannel-logout-session-registry]]\n=== Customizing the OIDC Provider Session Registry\n\nBy default, Spring Security stores in-memory all links between the OIDC Provider session and the Client session.\n\nThere are a number of circumstances, like a clustered application, where it would be nice to store this instead in a separate location, like a database.\n\nYou can achieve this by configuring a custom `ReactiveOidcSessionRegistry`, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic final class MySpringDataOidcSessionRegistry implements ReactiveOidcSessionRegistry {\n    private final OidcProviderSessionRepository sessions;\n\n    // ...\n\n    @Override\n    public Mono<void> saveSessionInformation(OidcSessionInformation info) {\n        return this.sessions.save(info);\n    }\n\n    @Override\n    public Mono<OidcSessionInformation> removeSessionInformation(String clientSessionId) {\n       return this.sessions.removeByClientSessionId(clientSessionId);\n    }\n\n    @Override\n    public Flux<OidcSessionInformation> removeSessionInformation(OidcLogoutToken token) {\n        return token.getSessionId() != null ?\n            this.sessions.removeBySessionIdAndIssuerAndAudience(...) :\n            this.sessions.removeBySubjectAndIssuerAndAudience(...);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass MySpringDataOidcSessionRegistry: ReactiveOidcSessionRegistry {\n    val sessions: OidcProviderSessionRepository\n\n    // ...\n\n    @Override\n    fun saveSessionInformation(info: OidcSessionInformation): Mono<Void> {\n        return this.sessions.save(info)\n    }\n\n    @Override\n    fun removeSessionInformation(clientSessionId: String): Mono<OidcSessionInformation> {\n       return this.sessions.removeByClientSessionId(clientSessionId);\n    }\n\n    @Override\n    fun removeSessionInformation(token: OidcLogoutToken): Flux<OidcSessionInformation> {\n        return token.getSessionId() != null ?\n            this.sessions.removeBySessionIdAndIssuerAndAudience(...) :\n            this.sessions.removeBySubjectAndIssuerAndAudience(...);\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/resource-server/bearer-tokens.adoc",
    "content": "= OAuth 2.0 Resource Server Bearer Tokens\n\n[[webflux-oauth2resourceserver-bearertoken-resolver]]\n== Bearer Token Resolution\n\nBy default, Resource Server looks for a bearer token in the `Authorization` header.\nHowever, you can verify this token.\n\nFor example, you may have a need to read the bearer token from a custom header.\nTo do so, you can wire an instance of `ServerBearerTokenAuthenticationConverter` into the DSL:\n\n.Custom Bearer Token Header\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nServerBearerTokenAuthenticationConverter converter = new ServerBearerTokenAuthenticationConverter();\nconverter.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION);\nhttp\n    .oauth2ResourceServer((oauth2) -> oauth2\n        .bearerTokenConverter(converter)\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval converter = ServerBearerTokenAuthenticationConverter()\nconverter.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION)\nreturn http {\n    oauth2ResourceServer {\n        bearerTokenConverter = converter\n    }\n}\n----\n======\n\n== Bearer Token Propagation\n\nNow that you have a bearer token, you can pass that to downstream services.\nThis is possible with javadoc:org.springframework.security.oauth2.server.resource.web.reactive.function.client.ServerBearerExchangeFilterFunction[]:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic WebClient rest() {\n    return WebClient.builder()\n            .filter(new ServerBearerExchangeFilterFunction())\n            .build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun rest(): WebClient {\n    return WebClient.builder()\n            .filter(ServerBearerExchangeFilterFunction())\n            .build()\n}\n----\n======\n\nWhen the `WebClient` shown in the preceding example performs requests, Spring Security looks up the current `Authentication` and extract any javadoc:org.springframework.security.oauth2.core.AbstractOAuth2Token[] credential.\nThen, it propagates that token in the `Authorization` header -- for example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nthis.rest.get()\n        .uri(\"https://other-service.example.com/endpoint\")\n        .retrieve()\n        .bodyToMono(String.class)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nthis.rest.get()\n        .uri(\"https://other-service.example.com/endpoint\")\n        .retrieve()\n        .bodyToMono<String>()\n----\n======\n\nThe preceding example invokes the `https://other-service.example.com/endpoint`, adding the bearer token `Authorization` header for you.\n\nIn places where you need to override this behavior, you can supply the header yourself:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nthis.rest.get()\n        .uri(\"https://other-service.example.com/endpoint\")\n        .headers((headers) -> headers.setBearerAuth(overridingToken))\n        .retrieve()\n        .bodyToMono(String.class)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nrest.get()\n        .uri(\"https://other-service.example.com/endpoint\")\n        .headers { it.setBearerAuth(overridingToken) }\n        .retrieve()\n        .bodyToMono<String>()\n----\n======\n\nIn this case, the filter falls back and forwards the request onto the rest of the web filter chain.\n\n[NOTE]\n====\nUnlike the javadoc:org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction[OAuth 2.0 Client filter function], this filter function makes no attempt to renew the token, should it be expired.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/resource-server/index.adoc",
    "content": "[[webflux-oauth2-resource-server]]\n= OAuth 2.0 Resource Server\n\nSpring Security supports protecting endpoints by offering two forms of OAuth 2.0 https://tools.ietf.org/html/rfc6750.html[Bearer Tokens]:\n\n* https://tools.ietf.org/html/rfc7519[JWT]\n* Opaque Tokens\n\nThis is handy in circumstances where an application has delegated its authority management to an https://tools.ietf.org/html/rfc6749[authorization server] (for example, Okta or Ping Identity).\nResource servers can consult this authorization server to authorize requests.\n\n[NOTE]\n====\nA complete working example for {gh-samples-url}/reactive/webflux/java/oauth2/resource-server[JWT]  is available in the {gh-samples-url}[Spring Security repository].\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/resource-server/jwt.adoc",
    "content": "= OAuth 2.0 Resource Server JWT\n\n[[webflux-oauth2resourceserver-jwt-minimaldependencies]]\n== Minimal Dependencies for JWT\n\nMost Resource Server support is collected into `spring-security-oauth2-resource-server`.\nHowever, the support for decoding and verifying JWTs is in `spring-security-oauth2-jose`, meaning that both are necessary to have a working resource server that supports JWT-encoded Bearer Tokens.\n\n[[webflux-oauth2resourceserver-jwt-minimalconfiguration]]\n== Minimal Configuration for JWTs\n\nWhen using https://spring.io/projects/spring-boot[Spring Boot], configuring an application as a resource server consists of two basic steps.\nFirst, include the needed dependencies. Second, indicate the location of the authorization server.\n\n=== Specifying the Authorization Server\n\nIn a Spring Boot application, you need to specify which authorization server to use:\n\n[source,yml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          issuer-uri: https://idp.example.com/issuer\n----\n\nWhere `https://idp.example.com/issuer` is the value contained in the `iss` claim for JWT tokens that the authorization server issues.\nThis resource server uses this property to further self-configure, discover the authorization server's public keys, and subsequently validate incoming JWTs.\n\n[NOTE]\n====\nTo use the `issuer-uri` property, it must also be true that one of `https://idp.example.com/issuer/.well-known/openid-configuration`, `https://idp.example.com/.well-known/openid-configuration/issuer`, or `https://idp.example.com/.well-known/oauth-authorization-server/issuer` is a supported endpoint for the authorization server.\nThis endpoint is referred to as a https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Provider Configuration] endpoint or a https://tools.ietf.org/html/rfc8414#section-3[Authorization Server Metadata] endpoint.\n====\n\n=== Startup Expectations\n\nWhen this property and these dependencies are used, Resource Server automatically configures itself to validate JWT-encoded Bearer Tokens.\n\nIt achieves this through a deterministic discovery process it launches at the first request containing a JWT:\n\n. Hit the Provider Configuration or Authorization Server Metadata endpoint, processing the response for the `jwks_url` property.\n. Configure the validation strategy to query `jwks_url` for valid public keys.\n. Configure the validation strategy to validate each JWT's `iss` claim against `https://idp.example.com`.\n\nOne benefit of deferring this process is that Resource Server startup is not coupled to the authorization server's availability.\n\n[NOTE]\n====\nThis deferral is managed by javadoc:org.springframework.security.oauth2.jwt.SupplierReactiveJwtDecoder[`SupplierReactiveJwtDecoder`].\nConsider wrapping any <<webflux-oauth2resourceserver-decoder-bean,`JwtDecoder` `@Bean`>> you declare in order to preserve this behavior.\n====\n\n=== Runtime Expectations\n\nOnce the application is started up, Resource Server tries to process any request that contains an `Authorization: Bearer` header:\n\n[source,html]\n----\nGET / HTTP/1.1\nAuthorization: Bearer some-token-value # Resource Server will process this\n----\n\nSo long as this scheme is indicated, Resource Server tries to process the request according to the Bearer Token specification.\n\nGiven a well-formed JWT, Resource Server:\n\n. Validates its signature against a public key obtained from the `jwks_url` endpoint during startup and matched against the JWTs header.\n. Validates the JWTs `exp` and `nbf` timestamps and the JWTs `iss` claim.\n. Maps each scope to an authority with the prefix `SCOPE_`.\n\n[NOTE]\n====\nAs the authorization server makes available new keys, Spring Security automatically rotates the keys used to validate the JWT tokens.\n====\n\nBy default, the resulting `Authentication#getPrincipal` is a Spring Security `Jwt` object, and `Authentication#getName` maps to the JWT's `sub` property, if one is present.\n\nFrom here, consider jumping to:\n\n* <<webflux-oauth2resourceserver-jwt-jwkseturi,How to Configure without Tying Resource Server startup to an authorization server's availability>>\n* <<webflux-oauth2resourceserver-jwt-sansboot,How to Configure without Spring Boot>>\n\n[[webflux-oauth2resourceserver-jwt-jwkseturi]]\n=== Specifying the Authorization Server JWK Set Uri Directly\n\nIf the authorization server does not support any configuration endpoints, or if Resource Server must be able to initialize independently from the authorization server, you can supply `jwk-set-uri` as well:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          issuer-uri: https://idp.example.com\n          jwk-set-uri: https://idp.example.com/.well-known/jwks.json\n----\n\n[NOTE]\n====\nThe JWK Set uri is not standardized, but you can typically find it in the authorization server's documentation.\n====\n\nConsequently, Resource Server does not ping the authorization server at startup.\nWe still specify the `issuer-uri` so that Resource Server still validates the `iss` claim on incoming JWTs.\n\n[NOTE]\n====\nYou can supply this property directly on the <<webflux-oauth2resourceserver-jwt-jwkseturi-dsl,DSL>>.\n====\n\n[[webflux-oauth2resourceserver-jwt-sansboot]]\n=== Overriding or Replacing Boot Auto Configuration\n\nSpring Boot generates two `@Bean` objects on Resource Server's behalf.\n\nThe first bean is a `SecurityWebFilterChain` that configures the application as a resource server. When including `spring-security-oauth2-jose`, this `SecurityWebFilterChain` looks like:\n\n.Resource Server SecurityWebFilterChain\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t.anyExchange().authenticated()\n\t\t)\n\t\t.oauth2ResourceServer(OAuth2ResourceServerSpec::jwt)\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            jwt { }\n        }\n    }\n}\n----\n======\n\nIf the application does not expose a `SecurityWebFilterChain` bean, Spring Boot exposes the default one (shown in the preceding listing).\n\nTo replace it, expose the `@Bean` within the application:\n\n.Replacing SecurityWebFilterChain\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope;\n\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t.pathMatchers(\"/message/**\").access(hasScope(\"message:read\"))\n\t\t\t.anyExchange().authenticated()\n\t\t)\n\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t.jwt(withDefaults())\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope\n\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(\"/message/**\", hasScope(\"message:read\"))\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            jwt { }\n        }\n    }\n}\n----\n======\n\nThe preceding configuration requires the scope of `message:read` for any URL that starts with `/messages/`.\n\nMethods on the `oauth2ResourceServer` DSL also override or replace auto configuration.\n\nFor example, the second `@Bean` Spring Boot creates is a `ReactiveJwtDecoder`, which decodes `String` tokens into validated instances of `Jwt`:\n\n.ReactiveJwtDecoder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveJwtDecoder jwtDecoder() {\n    return ReactiveJwtDecoders.fromIssuerLocation(issuerUri);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return ReactiveJwtDecoders.fromIssuerLocation(issuerUri)\n}\n----\n======\n\n[NOTE]\n====\nCalling javadoc:org.springframework.security.oauth2.jwt.ReactiveJwtDecoders#fromIssuerLocation-java.lang.String-[ReactiveJwtDecoders#fromIssuerLocation] invokes the Provider Configuration or Authorization Server Metadata endpoint to derive the JWK Set URI.\nIf the application does not expose a `ReactiveJwtDecoder` bean, Spring Boot exposes the above default one.\n====\n\nIts configuration can be overridden by using `jwkSetUri()` or replaced by using `decoder()`.\n\n[[webflux-oauth2resourceserver-jwt-jwkseturi-dsl]]\n==== Using `jwkSetUri()`\n\nYou can configure an authorization server's JWK Set URI <<webflux-oauth2resourceserver-jwt-jwkseturi,as a configuration property>> or supply it in the DSL:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t.anyExchange().authenticated()\n\t\t)\n\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t.jwkSetUri(\"https://idp.example.com/.well-known/jwks.json\")\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            jwt {\n                jwkSetUri = \"https://idp.example.com/.well-known/jwks.json\"\n            }\n        }\n    }\n}\n----\n======\n\nUsing `jwkSetUri()` takes precedence over any configuration property.\n\n[[webflux-oauth2resourceserver-jwt-decoder-dsl]]\n==== Using `decoder()`\n\n`decoder()` is more powerful than `jwkSetUri()`, because it completely replaces any Spring Boot auto-configuration of `JwtDecoder`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t.anyExchange().authenticated()\n\t\t)\n\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t.decoder(myCustomDecoder())\n\t\t\t)\n\t\t);\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            jwt {\n                jwtDecoder = myCustomDecoder()\n            }\n        }\n    }\n}\n----\n======\n\nThis is handy when you need deeper configuration, such as <<webflux-oauth2resourceserver-jwt-validation,validation>>.\n\n[[webflux-oauth2resourceserver-decoder-bean]]\n==== Exposing a `ReactiveJwtDecoder` `@Bean`\n\nAlternately, exposing a `ReactiveJwtDecoder` `@Bean` has the same effect as `decoder()`:\nYou can construct one with a `jwkSetUri` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveJwtDecoder jwtDecoder() {\n    return NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build()\n}\n----\n======\n\nor you can use the issuer and have `NimbusReactiveJwtDecoder` look up the `jwkSetUri` when `build()` is invoked, like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveJwtDecoder jwtDecoder() {\n    return NimbusReactiveJwtDecoder.withIssuerLocation(issuer).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return NimbusReactiveJwtDecoder.withIssuerLocation(issuer).build()\n}\n----\n======\n\nOr, if the defaults work for you, you can also use `JwtDecoders`, which does the above in addition to configuring the decoder's validator:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveJwtDecoder jwtDecoder() {\n    return ReactiveJwtDecoders.fromIssuerLocation(issuer);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return ReactiveJwtDecoders.fromIssuerLocation(issuer)\n}\n----\n======\n\n[[webflux-oauth2resourceserver-jwt-decoder-algorithm]]\n== Configuring Trusted Algorithms\n\nBy default, `NimbusReactiveJwtDecoder`, and hence Resource Server, trust and verify only tokens that use `RS256`.\n\nYou can customize this behavior with <<webflux-oauth2resourceserver-jwt-boot-algorithm,Spring Boot>> or by using <<webflux-oauth2resourceserver-jwt-decoder-builder,the NimbusJwtDecoder builder>>.\n\n[[webflux-oauth2resourceserver-jwt-boot-algorithm]]\n=== Customizing Trusted Algorithms with Spring Boot\n\nThe simplest way to set the algorithm is as a property:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          jws-algorithms: RS512\n          jwk-set-uri: https://idp.example.org/.well-known/jwks.json\n----\n\n[[webflux-oauth2resourceserver-jwt-decoder-builder]]\n=== Customizing Trusted Algorithms by Using a Builder\n\nFor greater power, though, we can use a builder that ships with `NimbusReactiveJwtDecoder`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nReactiveJwtDecoder jwtDecoder() {\n    return NimbusReactiveJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithm(RS512).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return NimbusReactiveJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithm(RS512).build()\n}\n----\n======\n\nCalling `jwsAlgorithm` more than once configures `NimbusReactiveJwtDecoder` to trust more than one algorithm:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nReactiveJwtDecoder jwtDecoder() {\n    return NimbusReactiveJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithm(RS512).jwsAlgorithm(ES512).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return NimbusReactiveJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithm(RS512).jwsAlgorithm(ES512).build()\n}\n----\n======\n\nAlternately, you can call `jwsAlgorithms`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nReactiveJwtDecoder jwtDecoder() {\n    return NimbusReactiveJwtDecoder.withIssuerLocation(this.jwkSetUri)\n            .jwsAlgorithms(algorithms -> {\n                    algorithms.add(RS512);\n                    algorithms.add(ES512);\n            }).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return NimbusReactiveJwtDecoder.withIssuerLocation(this.jwkSetUri)\n            .jwsAlgorithms {\n                it.add(RS512)\n                it.add(ES512)\n            }\n            .build()\n}\n----\n======\n\n[[webflux-oauth2resourceserver-jwt-decoder-public-key]]\n=== Trusting a Single Asymmetric Key\n\nSimpler than backing a Resource Server with a JWK Set endpoint is to hard-code an RSA public key.\nThe public key can be provided with <<webflux-oauth2resourceserver-jwt-decoder-public-key-boot,Spring Boot>> or by <<webflux-oauth2resourceserver-jwt-decoder-public-key-builder,Using a Builder>>.\n\n[[webflux-oauth2resourceserver-jwt-decoder-public-key-boot]]\n==== Via Spring Boot\n\nYou can specify a key with Spring Boot:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          public-key-location: classpath:my-key.pub\n----\n\nAlternately, to allow for a more sophisticated lookup, you can post-process the `RsaKeyConversionServicePostProcessor`:\n\n.BeanFactoryPostProcessor\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nBeanFactoryPostProcessor conversionServiceCustomizer() {\n    return beanFactory ->\n        beanFactory.getBean(RsaKeyConversionServicePostProcessor.class)\n                .setResourceLoader(new CustomResourceLoader());\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun conversionServiceCustomizer(): BeanFactoryPostProcessor {\n    return BeanFactoryPostProcessor { beanFactory: ConfigurableListableBeanFactory ->\n        beanFactory.getBean<RsaKeyConversionServicePostProcessor>()\n                .setResourceLoader(CustomResourceLoader())\n    }\n}\n----\n======\n\nSpecify your key's location:\n\n[source,yaml]\n----\nkey.location: hfds://my-key.pub\n----\n\nThen autowire the value:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Value(\"${key.location}\")\nRSAPublicKey key;\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Value(\"\\${key.location}\")\nval key: RSAPublicKey? = null\n----\n======\n\n[[webflux-oauth2resourceserver-jwt-decoder-public-key-builder]]\n==== Using a Builder\n\nTo wire an `RSAPublicKey` directly, use the appropriate `NimbusReactiveJwtDecoder` builder:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveJwtDecoder jwtDecoder() {\n    return NimbusReactiveJwtDecoder.withPublicKey(this.key).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return NimbusReactiveJwtDecoder.withPublicKey(key).build()\n}\n----\n======\n\n[[webflux-oauth2resourceserver-jwt-decoder-secret-key]]\n=== Trusting a Single Symmetric Key\n\nYou can also use a single symmetric key.\nYou can load in your `SecretKey` and use the appropriate `NimbusReactiveJwtDecoder` builder:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveJwtDecoder jwtDecoder() {\n    return NimbusReactiveJwtDecoder.withSecretKey(this.key).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    return NimbusReactiveJwtDecoder.withSecretKey(this.key).build()\n}\n----\n======\n\n[[webflux-oauth2resourceserver-jwt-authorization]]\n=== Configuring Authorization\n\nA JWT that is issued from an OAuth 2.0 Authorization Server typically has either a `scope` or an `scp` attribute, indicating the scopes (or authorities) it has been granted -- for example:\n\n[source,json]\n----\n{ ..., \"scope\" : \"messages contacts\"}\n----\n\nWhen this is the case, Resource Server tries to coerce these scopes into a list of granted authorities, prefixing each scope with the string, `SCOPE_`.\n\nThis means that, to protect an endpoint or method with a scope derived from a JWT, the corresponding expressions should include this prefix:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope;\n\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t.mvcMatchers(\"/contacts/**\").access(hasScope(\"contacts\"))\n\t\t\t.mvcMatchers(\"/messages/**\").access(hasScope(\"messages\"))\n\t\t\t.anyExchange().authenticated()\n\t\t)\n\t\t.oauth2ResourceServer(OAuth2ResourceServerSpec::jwt);\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope\n\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(\"/contacts/**\", hasScope(\"contacts\"))\n            authorize(\"/messages/**\", hasScope(\"messages\"))\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            jwt { }\n        }\n    }\n}\n----\n======\n\nYou can do something similar with method security:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"hasAuthority('SCOPE_messages')\")\npublic Flux<Message> getMessages(...) {}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"hasAuthority('SCOPE_messages')\")\nfun getMessages(): Flux<Message> { }\n----\n======\n\n[[webflux-oauth2resourceserver-jwt-authorization-extraction]]\n==== Extracting Authorities Manually\n\nHowever, there are a number of circumstances where this default is insufficient.\nFor example, some authorization servers do not use the `scope` attribute. Instead, they have their own custom attribute.\nAt other times, the resource server may need to adapt the attribute or a composition of attributes into internalized authorities.\n\nTo this end, the DSL exposes `jwtAuthenticationConverter()`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t.anyExchange().authenticated()\n\t\t)\n\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t.jwt((jwt) -> jwt\n\t\t\t\t.jwtAuthenticationConverter(grantedAuthoritiesExtractor())\n\t\t\t)\n\t\t);\n\treturn http.build();\n}\n\nConverter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {\n    JwtAuthenticationConverter jwtAuthenticationConverter =\n            new JwtAuthenticationConverter();\n    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter\n            (new GrantedAuthoritiesExtractor());\n    return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            jwt {\n                jwtAuthenticationConverter = grantedAuthoritiesExtractor()\n            }\n        }\n    }\n}\n\nfun grantedAuthoritiesExtractor(): Converter<Jwt, Mono<AbstractAuthenticationToken>> {\n    val jwtAuthenticationConverter = JwtAuthenticationConverter()\n    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(GrantedAuthoritiesExtractor())\n    return ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter)\n}\n----\n======\n\n`jwtAuthenticationConverter()` is responsible for converting a `Jwt` into an `Authentication`.\nAs part of its configuration, we can supply a subsidiary converter to go from `Jwt` to a `Collection` of granted authorities.\n\nThat final converter might be something like the following `GrantedAuthoritiesExtractor`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nstatic class GrantedAuthoritiesExtractor\n        implements Converter<Jwt, Collection<GrantedAuthority>> {\n\n    public Collection<GrantedAuthority> convert(Jwt jwt) {\n        Collection<?> authorities = (Collection<?>)\n                jwt.getClaims().getOrDefault(\"mycustomclaim\", Collections.emptyList());\n\n        return authorities.stream()\n                .map(Object::toString)\n                .map(SimpleGrantedAuthority::new)\n                .collect(Collectors.toList());\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ninternal class GrantedAuthoritiesExtractor : Converter<Jwt, Collection<GrantedAuthority>> {\n    override fun convert(jwt: Jwt): Collection<GrantedAuthority> {\n        val authorities: List<Any> = jwt.claims\n                .getOrDefault(\"mycustomclaim\", emptyList<Any>()) as List<Any>\n        return authorities\n                .map { it.toString() }\n                .map { SimpleGrantedAuthority(it) }\n    }\n}\n----\n======\n\nFor more flexibility, the DSL supports entirely replacing the converter with any class that implements `Converter<Jwt, Mono<AbstractAuthenticationToken>>`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nstatic class CustomAuthenticationConverter implements Converter<Jwt, Mono<AbstractAuthenticationToken>> {\n    public AbstractAuthenticationToken convert(Jwt jwt) {\n        return Mono.just(jwt).map(this::doConversion);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ninternal class CustomAuthenticationConverter : Converter<Jwt, Mono<AbstractAuthenticationToken>> {\n    override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken> {\n        return Mono.just(jwt).map(this::doConversion)\n    }\n}\n----\n======\n\n[[webflux-oauth2resourceserver-jwt-validation]]\n=== Configuring Validation\n\nUsing <<webflux-oauth2resourceserver-jwt-minimalconfiguration,minimal Spring Boot configuration>>, indicating the authorization server's issuer URI, Resource Server defaults to verifying the `iss` claim as well as the `exp` and `nbf` timestamp claims.\n\nIn circumstances where you need to customize validation needs, Resource Server ships with two standard validators and also accepts custom `OAuth2TokenValidator` instances.\n\n[[webflux-oauth2resourceserver-jwt-validation-clockskew]]\n==== Customizing Timestamp Validation\n\nJWT instances typically have a window of validity, with the start of the window indicated in the `nbf` claim and the end indicated in the `exp` claim.\n\nHowever, every server can experience clock drift, which can cause tokens to appear to be expired to one server but not to another.\nThis can cause some implementation heartburn, as the number of collaborating servers increases in a distributed system.\n\nResource Server uses `JwtTimestampValidator` to verify a token's validity window, and you can configure it with a `clockSkew` to alleviate the clock drift problem:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nReactiveJwtDecoder jwtDecoder() {\n     NimbusReactiveJwtDecoder jwtDecoder = (NimbusReactiveJwtDecoder)\n             ReactiveJwtDecoders.fromIssuerLocation(issuerUri);\n\n     OAuth2TokenValidator<Jwt> withClockSkew = new DelegatingOAuth2TokenValidator<>(\n            new JwtTimestampValidator(Duration.ofSeconds(60)),\n            new IssuerValidator(issuerUri));\n\n     jwtDecoder.setJwtValidator(withClockSkew);\n\n     return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    val jwtDecoder = ReactiveJwtDecoders.fromIssuerLocation(issuerUri) as NimbusReactiveJwtDecoder\n    val withClockSkew: OAuth2TokenValidator<Jwt> = DelegatingOAuth2TokenValidator(\n            JwtTimestampValidator(Duration.ofSeconds(60)),\n            JwtIssuerValidator(issuerUri))\n    jwtDecoder.setJwtValidator(withClockSkew)\n    return jwtDecoder\n}\n----\n======\n\n[NOTE]\n====\nBy default, Resource Server configures a clock skew of 60 seconds.\n====\n\n[[webflux-oauth2resourceserver-validation-rfc9068]]\n=== Configuring RFC 9068 Validation\n\nIf you need to require tokens that meet https://datatracker.ietf.org/doc/rfc9068/[RFC 9068], you can configure validation in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n    NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuerUri)\n            .validateTypes(false).build();\n    jwtDecoder.setJwtValidator(JwtValidators.createAtJwtValidator()\n            .audience(\"https://audience.example.org\")\n            .clientId(\"client-identifier\")\n            .issuer(\"https://issuer.example.org\").build());\n     return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuerUri)\n            .validateTypes(false).build()\n    jwtDecoder.setJwtValidator(JwtValidators.createAtJwtValidator()\n            .audience(\"https://audience.example.org\")\n            .clientId(\"client-identifier\")\n            .issuer(\"https://issuer.example.org\").build())\n    return jwtDecoder\n}\n----\n======\n\n[[webflux-oauth2resourceserver-validation-custom]]\n==== Configuring a Custom Validator\n\nYou can Add a check for the `aud` claim with the `OAuth2TokenValidator` API:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class AudienceValidator implements OAuth2TokenValidator<Jwt> {\n    OAuth2Error error = new OAuth2Error(\"invalid_token\", \"The required audience is missing\", null);\n\n    public OAuth2TokenValidatorResult validate(Jwt jwt) {\n        if (jwt.getAudience().contains(\"messaging\")) {\n            return OAuth2TokenValidatorResult.success();\n        } else {\n            return OAuth2TokenValidatorResult.failure(error);\n        }\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass AudienceValidator : OAuth2TokenValidator<Jwt> {\n    var error: OAuth2Error = OAuth2Error(\"invalid_token\", \"The required audience is missing\", null)\n    override fun validate(jwt: Jwt): OAuth2TokenValidatorResult {\n        return if (jwt.audience.contains(\"messaging\")) {\n            OAuth2TokenValidatorResult.success()\n        } else {\n            OAuth2TokenValidatorResult.failure(error)\n        }\n    }\n}\n----\n======\n\nThen, to add into a resource server, you can specify the `ReactiveJwtDecoder` instance:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nReactiveJwtDecoder jwtDecoder() {\n    NimbusReactiveJwtDecoder jwtDecoder = (NimbusReactiveJwtDecoder)\n            ReactiveJwtDecoders.fromIssuerLocation(issuerUri);\n\n    OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator();\n    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);\n    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);\n\n    jwtDecoder.setJwtValidator(withAudience);\n\n    return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): ReactiveJwtDecoder {\n    val jwtDecoder = ReactiveJwtDecoders.fromIssuerLocation(issuerUri) as NimbusReactiveJwtDecoder\n    val audienceValidator: OAuth2TokenValidator<Jwt> = AudienceValidator()\n    val withIssuer: OAuth2TokenValidator<Jwt> = JwtValidators.createDefaultWithIssuer(issuerUri)\n    val withAudience: OAuth2TokenValidator<Jwt> = DelegatingOAuth2TokenValidator(withIssuer, audienceValidator)\n    jwtDecoder.setJwtValidator(withAudience)\n    return jwtDecoder\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/resource-server/multitenancy.adoc",
    "content": "= OAuth 2.0 Resource Server Multi-tenancy\n\n[[webflux-oauth2resourceserver-multitenancy]]\n== Multi-tenancy\n\nA resource server is considered multi-tenant when there are multiple strategies for verifying a bearer token, keyed by some tenant identifier.\n\nFor example, your resource server can accept bearer tokens from two different authorization servers.\nAlternately, your authorization server can represent a multiplicity of issuers.\n\nIn each case, two things need to be done and trade-offs are associated with how you choose to do them:\n\n. Resolve the tenant.\n. Propagate the tenant.\n\n=== Resolving the Tenant By Claim\n\nOne way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, you can do so with the `JwtIssuerReactiveAuthenticationManagerResolver`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n    .fromTrustedIssuers(\"https://idp.example.org/issuerOne\", \"https://idp.example.org/issuerTwo\");\n\nhttp\n    .authorizeExchange((authorize) -> authorize\n        .anyExchange().authenticated()\n    )\n    .oauth2ResourceServer((oauth2) -> oauth2\n        .authenticationManagerResolver(authenticationManagerResolver)\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n    .fromTrustedIssuers(\"https://idp.example.org/issuerOne\", \"https://idp.example.org/issuerTwo\")\n\nreturn http {\n    authorizeExchange {\n        authorize(anyExchange, authenticated)\n    }\n    oauth2ResourceServer {\n        authenticationManagerResolver = customAuthenticationManagerResolver\n    }\n}\n----\n======\n\nThis is nice because the issuer endpoints are loaded lazily.\nIn fact, the corresponding `JwtReactiveAuthenticationManager` is instantiated only when the first request with the corresponding issuer is sent.\nThis allows for an application startup that is independent from those authorization servers being up and available.\n\n==== Dynamic Tenants\n\nYou may not want to restart the application each time a new tenant is added.\nIn this case, you can configure the `JwtIssuerReactiveAuthenticationManagerResolver` with a repository of `ReactiveAuthenticationManager` instances, which you can edit at runtime:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprivate Mono<ReactiveAuthenticationManager> addManager(\n\t\tMap<String, ReactiveAuthenticationManager> authenticationManagers, String issuer) {\n\n\treturn Mono.fromCallable(() -> ReactiveJwtDecoders.fromIssuerLocation(issuer))\n            .subscribeOn(Schedulers.boundedElastic())\n            .map(JwtReactiveAuthenticationManager::new)\n            .doOnNext((authenticationManager) -> authenticationManager.put(issuer, authenticationManager));\n}\n\n// ...\n\nJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver =\n        new JwtIssuerReactiveAuthenticationManagerResolver(authenticationManagers::get);\n\nhttp\n    .authorizeExchange((authorize) -> authorize\n        .anyExchange().authenticated()\n    )\n    .oauth2ResourceServer((oauth2) -> oauth2\n        .authenticationManagerResolver(authenticationManagerResolver)\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nprivate fun addManager(\n        authenticationManagers: MutableMap<String, ReactiveAuthenticationManager>, issuer: String): Mono<JwtReactiveAuthenticationManager> {\n    return Mono.fromCallable { ReactiveJwtDecoders.fromIssuerLocation(issuer) }\n            .subscribeOn(Schedulers.boundedElastic())\n            .map { jwtDecoder: ReactiveJwtDecoder -> JwtReactiveAuthenticationManager(jwtDecoder) }\n            .doOnNext { authenticationManager: JwtReactiveAuthenticationManager -> authenticationManagers[issuer] = authenticationManager }\n}\n\n// ...\n\nvar customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver(authenticationManagers::get)\nreturn http {\n    authorizeExchange {\n        authorize(anyExchange, authenticated)\n    }\n    oauth2ResourceServer {\n        authenticationManagerResolver = customAuthenticationManagerResolver\n    }\n}\n----\n======\n\nIn this case, you construct `JwtIssuerReactiveAuthenticationManagerResolver` with a strategy for obtaining the `ReactiveAuthenticationManager` given to the issuer.\nThis approach lets us add and remove elements from the repository (shown as a `Map` in the preceding snippet) at runtime.\n\n[NOTE]\n====\nIt would be unsafe to simply take any issuer and construct an `ReactiveAuthenticationManager` from it.\nThe issuer should be one that the code can verify from a trusted source, such as an allowed list of issuers.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/oauth2/resource-server/opaque-token.adoc",
    "content": "= OAuth 2.0 Resource Server Opaque Token\n\n[[webflux-oauth2resourceserver-opaque-minimaldependencies]]\n== Minimal Dependencies for Introspection\nAs described in xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-minimaldependencies[Minimal Dependencies for JWT], most Resource Server support is collected in `spring-security-oauth2-resource-server`.\nHowever, unless you provide a custom <<webflux-oauth2resourceserver-opaque-introspector-bean,`ReactiveOpaqueTokenIntrospector`>>, the Resource Server falls back to `SpringReactiveOpaqueTokenIntrospector`.\nThis means that only `spring-security-oauth2-resource-server` is necessary to have a working minimal Resource Server that supports opaque Bearer Tokens.\n\n[[webflux-oauth2resourceserver-opaque-minimalconfiguration]]\n== Minimal Configuration for Introspection\n\nTypically, you can verify an opaque token with an https://tools.ietf.org/html/rfc7662[OAuth 2.0 Introspection Endpoint], hosted by the authorization server.\nThis can be handy when revocation is a requirement.\n\nWhen using https://spring.io/projects/spring-boot[Spring Boot], configuring an application as a resource server that uses introspection consists of two steps:\n\n. Include the needed dependencies.\n. Indicate the introspection endpoint details.\n\n[[webflux-oauth2resourceserver-opaque-introspectionuri]]\n=== Specifying the Authorization Server\n\nYou can specify where the introspection endpoint is:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        opaquetoken:\n          introspection-uri: https://idp.example.com/introspect\n          client-id: client\n          client-secret: secret\n----\n\nWhere `https://idp.example.com/introspect` is the introspection endpoint hosted by your authorization server and `client-id` and `client-secret` are the credentials needed to hit that endpoint.\n\nResource Server uses these properties to further self-configure and subsequently validate incoming JWTs.\n\n[NOTE]\n====\nIf the authorization server responses that the token is valid, then it is.\n====\n\n=== Startup Expectations\n\nWhen this property and these dependencies are used, Resource Server automatically configures itself to validate Opaque Bearer Tokens.\n\nThis startup process is quite a bit simpler than for JWTs, since no endpoints need to be discovered and no additional validation rules get added.\n\n=== Runtime Expectations\n\nOnce the application has started, Resource Server tries to process any request containing an `Authorization: Bearer` header:\n\n[source,http]\n----\nGET / HTTP/1.1\nAuthorization: Bearer some-token-value # Resource Server will process this\n----\n\nSo long as this scheme is indicated, Resource Server tries to process the request according to the Bearer Token specification.\n\nGiven an Opaque Token, Resource Server:\n\n. Queries the provided introspection endpoint by using the provided credentials and the token.\n. Inspects the response for an `{ 'active' : true }` attribute.\n. Maps each scope to an authority with a prefix of `SCOPE_`.\n\nBy default, the resulting `Authentication#getPrincipal` is a Spring Security javadoc:org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal[] object, and `Authentication#getName` maps to the token's `sub` property, if one is present.\n\nFrom here, you may want to jump to:\n\n* <<webflux-oauth2resourceserver-opaque-attributes>>\n* <<webflux-oauth2resourceserver-opaque-authorization-extraction>>\n* <<webflux-oauth2resourceserver-opaque-jwt-introspector>>\n\n[[webflux-oauth2resourceserver-opaque-attributes]]\n== Looking Up Attributes After Authentication\n\nOnce a token is authenticated, an instance of `BearerTokenAuthentication` is set in the `SecurityContext`.\n\nThis means that it is available in `@Controller` methods when you use `@EnableWebFlux` in your configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/foo\")\npublic Mono<String> foo(BearerTokenAuthentication authentication) {\n    return Mono.just(authentication.getTokenAttributes().get(\"sub\") + \" is the subject\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/foo\")\nfun foo(authentication: BearerTokenAuthentication): Mono<String> {\n    return Mono.just(authentication.tokenAttributes[\"sub\"].toString() + \" is the subject\")\n}\n----\n======\n\nSince `BearerTokenAuthentication` holds an `OAuth2AuthenticatedPrincipal`, that also means that it's available to controller methods, too:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/foo\")\npublic Mono<String> foo(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal) {\n    return Mono.just(principal.getAttribute(\"sub\") + \" is the subject\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/foo\")\nfun foo(@AuthenticationPrincipal principal: OAuth2AuthenticatedPrincipal): Mono<String> {\n    return Mono.just(principal.getAttribute<Any>(\"sub\").toString() + \" is the subject\")\n}\n----\n======\n\n=== Looking Up Attributes with SpEL\n\nYou can access attributes with the Spring Expression Language (SpEL).\n\nFor example, if you use `@EnableReactiveMethodSecurity` so that you can use `@PreAuthorize` annotations, you can do:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"principal?.attributes['sub'] = 'foo'\")\npublic Mono<String> forFoosEyesOnly() {\n    return Mono.just(\"foo\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"principal.attributes['sub'] = 'foo'\")\nfun forFoosEyesOnly(): Mono<String> {\n    return Mono.just(\"foo\")\n}\n----\n======\n\n[[webflux-oauth2resourceserver-opaque-sansboot]]\n== Overriding or Replacing Boot Auto Configuration\n\nSpring Boot generates two `@Bean` instances for Resource Server.\n\nThe first is a `SecurityWebFilterChain` that configures the application as a resource server.\nWhen you use an Opaque Token, this `SecurityWebFilterChain` looks like:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n\thttp\n\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t.anyExchange().authenticated()\n\t\t)\n\t\t.oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::opaqueToken)\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            opaqueToken { }\n        }\n    }\n}\n----\n======\n\nIf the application does not expose a `SecurityWebFilterChain` bean, Spring Boot exposes the default bean (shown in the preceding listing).\n\nYou can replace it by exposing the bean within the application:\n\n.Replacing SecurityWebFilterChain\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope;\n\n@Configuration\n@EnableWebFluxSecurity\npublic class MyCustomSecurityConfiguration {\n    @Bean\n    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n        http\n            .authorizeExchange((authorize) -> authorize\n                .pathMatchers(\"/messages/**\").access(hasScope(\"message:read\"))\n                .anyExchange().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .opaqueToken((opaqueToken) -> opaqueToken\n                    .introspector(myIntrospector())\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope\n\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(\"/messages/**\", hasScope(\"message:read\"))\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            opaqueToken {\n                introspector = myIntrospector()\n            }\n        }\n    }\n}\n----\n======\n\nThe preceding example requires the scope of `message:read` for any URL that starts with `/messages/`.\n\nMethods on the `oauth2ResourceServer` DSL also override or replace auto configuration.\n\nFor example, the second `@Bean` Spring Boot creates is a `ReactiveOpaqueTokenIntrospector`, which decodes `String` tokens into validated instances of `OAuth2AuthenticatedPrincipal`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOpaqueTokenIntrospector introspector() {\n    return SpringReactiveOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n        .clientId(clientId).clientSecret(clientSecret).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): ReactiveOpaqueTokenIntrospector {\n    return SpringReactiveOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n        .clientId(clientId).clientSecret(clientSecret).build()\n}\n----\n======\n\nIf the application does not expose a `ReactiveOpaqueTokenIntrospector` bean, Spring Boot exposes the default one (shown in the preceding listing).\n\nYou can override its configuration by using `introspectionUri()` and `introspectionClientCredentials()` or replace it by using `introspector()`.\n\n[[webflux-oauth2resourceserver-opaque-introspectionuri-dsl]]\n=== Using `introspectionUri()`\n\nYou can configure an authorization server's Introspection URI <<webflux-oauth2resourceserver-opaque-introspectionuri,as a configuration property>>, or you can supply in the DSL:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class DirectlyConfiguredIntrospectionUri {\n    @Bean\n    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n        http\n            .authorizeExchange((authorize) -> authorize\n                .anyExchange().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .opaqueToken((opaqueToken) -> opaqueToken\n                    .introspectionUri(\"https://idp.example.com/introspect\")\n                    .introspectionClientCredentials(\"client\", \"secret\")\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            opaqueToken {\n                introspectionUri = \"https://idp.example.com/introspect\"\n                introspectionClientCredentials(\"client\", \"secret\")\n            }\n        }\n    }\n}\n----\n======\n\nUsing `introspectionUri()` takes precedence over any configuration property.\n\n[[webflux-oauth2resourceserver-opaque-introspector-dsl]]\n=== Using `introspector()`\n\n`introspector()` is more powerful than `introspectionUri()`. It completely replaces any Boot auto-configuration of `ReactiveOpaqueTokenIntrospector`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebFluxSecurity\npublic class DirectlyConfiguredIntrospector {\n    @Bean\n    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n        http\n            .authorizeExchange((authorize) -> authorize\n                .anyExchange().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .opaqueToken((opaqueToken) -> opaqueToken\n                    .introspector(myCustomIntrospector())\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            opaqueToken {\n                introspector = myCustomIntrospector()\n            }\n        }\n    }\n}\n----\n======\n\nThis is handy when deeper configuration, such as <<webflux-oauth2resourceserver-opaque-authorization-extraction,authority mapping>> or <<webflux-oauth2resourceserver-opaque-jwt-introspector,JWT revocation>>, is necessary.\n\n[[webflux-oauth2resourceserver-opaque-introspector-bean]]\n=== Exposing a `ReactiveOpaqueTokenIntrospector` `@Bean`\n\nOr, exposing a `ReactiveOpaqueTokenIntrospector` `@Bean` has the same effect as `introspector()`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOpaqueTokenIntrospector introspector() {\n    return SpringReactiveOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n        .clientId(clientId).clientSecret(clientSecret).build()\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): ReactiveOpaqueTokenIntrospector {\n    return SpringReactiveOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n        .clientId(clientId).clientSecret(clientSecret).build()\n}\n----\n======\n\n[[webflux-oauth2resourceserver-opaque-authorization]]\n== Configuring Authorization\n\nAn OAuth 2.0 Introspection endpoint typically returns a `scope` attribute, indicating the scopes (or authorities) it has been granted -- for example:\n\n[source,json]\n----\n{ ..., \"scope\" : \"messages contacts\"}\n----\n\nWhen this is the case, Resource Server tries to coerce these scopes into a list of granted authorities, prefixing each scope with a string: `SCOPE_`.\n\nThis means that, to protect an endpoint or method with a scope derived from an Opaque Token, the corresponding expressions should include this prefix:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope;\n\n@Configuration\n@EnableWebFluxSecurity\npublic class MappedAuthorities {\n    @Bean\n    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n        http\n            .authorizeExchange((authorize) -> authorize\n                .pathMatchers(\"/contacts/**\").access(hasScope(\"contacts\"))\n                .pathMatchers(\"/messages/**\").access(hasScope(\"messages\"))\n                .anyExchange().authenticated()\n            )\n            .oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::opaqueToken);\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope\n\n@Bean\nfun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n    return http {\n        authorizeExchange {\n            authorize(\"/contacts/**\", hasScope(\"contacts\"))\n            authorize(\"/messages/**\", hasScope(\"messages\"))\n            authorize(anyExchange, authenticated)\n        }\n        oauth2ResourceServer {\n            opaqueToken { }\n        }\n    }\n}\n----\n======\n\nYou can do something similar with method security:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"hasAuthority('SCOPE_messages')\")\npublic Flux<Message> getMessages(...) {}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"hasAuthority('SCOPE_messages')\")\nfun getMessages(): Flux<Message> { }\n----\n======\n\n[[webflux-oauth2resourceserver-opaque-authorization-extraction]]\n=== Extracting Authorities Manually\n\nBy default, Opaque Token support extracts the scope claim from an introspection response and parses it into individual `GrantedAuthority` instances.\n\nConsider the following example:\n\n[source,json]\n----\n{\n    \"active\" : true,\n    \"scope\" : \"message:read message:write\"\n}\n----\n\nIf the introspection response were as the preceding example shows, Resource Server would generate an `Authentication` with two authorities, one for `message:read` and the other for `message:write`.\n\nYou can customize behavior by using a custom `ReactiveOpaqueTokenIntrospector` that looks at the attribute set and converts in its own way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class CustomAuthoritiesOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {\n    private ReactiveOpaqueTokenIntrospector delegate = SpringReactiveOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build();\n\n    public Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {\n        return this.delegate.introspect(token)\n                .map((principal) -> principal DefaultOAuth2AuthenticatedPrincipal(\n                        principal.getName(), principal.getAttributes(), extractAuthorities(principal)));\n    }\n\n    private Collection<GrantedAuthority> extractAuthorities(OAuth2AuthenticatedPrincipal principal) {\n        List<String> scopes = principal.getAttribute(OAuth2IntrospectionClaimNames.SCOPE);\n        return scopes.stream()\n                .map(SimpleGrantedAuthority::new)\n                .collect(Collectors.toList());\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass CustomAuthoritiesOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {\n    private val delegate: ReactiveOpaqueTokenIntrospector = SpringReactiveOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build()\n    override fun introspect(token: String): Mono<OAuth2AuthenticatedPrincipal> {\n        return delegate.introspect(token)\n                .map { principal: OAuth2AuthenticatedPrincipal ->\n                    DefaultOAuth2AuthenticatedPrincipal(\n                            principal.name, principal.attributes, extractAuthorities(principal))\n                }\n    }\n\n    private fun extractAuthorities(principal: OAuth2AuthenticatedPrincipal): Collection<GrantedAuthority> {\n        val scopes = principal.getAttribute<List<String>>(OAuth2IntrospectionClaimNames.SCOPE)\n        return scopes\n                .map { SimpleGrantedAuthority(it) }\n    }\n}\n----\n======\n\nThereafter, you can configure this custom introspector by exposing it as a `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOpaqueTokenIntrospector introspector() {\n    return new CustomAuthoritiesOpaqueTokenIntrospector();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): ReactiveOpaqueTokenIntrospector {\n    return CustomAuthoritiesOpaqueTokenIntrospector()\n}\n----\n======\n\n[[webflux-oauth2resourceserver-opaque-jwt-introspector]]\n== Using Introspection with JWTs\n\nA common question is whether or not introspection is compatible with JWTs.\nSpring Security's Opaque Token support has been designed to not care about the format of the token. It gladly passes any token to the provided introspection endpoint.\n\nSo, suppose you need to check with the authorization server on each request, in case the JWT has been revoked.\n\nEven though you are using the JWT format for the token, your validation method is introspection, meaning you would want to do:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        opaquetoken:\n          introspection-uri: https://idp.example.org/introspection\n          client-id: client\n          client-secret: secret\n----\n\nIn this case, the resulting `Authentication` would be `BearerTokenAuthentication`.\nAny attributes in the corresponding `OAuth2AuthenticatedPrincipal` would be whatever was returned by the introspection endpoint.\n\nHowever, suppose that, for whatever reason, the introspection endpoint returns only whether or not the token is active.\nNow what?\n\nIn this case, you can create a custom `ReactiveOpaqueTokenIntrospector` that still hits the endpoint but then updates the returned principal to have the JWTs claims as the attributes:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class JwtOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {\n\tprivate ReactiveOpaqueTokenIntrospector delegate = SpringReactiveOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build();\n\tprivate ReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(new ParseOnlyJWTProcessor());\n\n\tpublic Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {\n\t\treturn this.delegate.introspect(token)\n\t\t\t\t.flatMap((principal) -> principal.jwtDecoder.decode(token))\n\t\t\t\t.map((jwt) -> jwt DefaultOAuth2AuthenticatedPrincipal(jwt.getClaims(), NO_AUTHORITIES));\n\t}\n\n\tprivate static class ParseOnlyJWTProcessor implements Converter<JWT, Mono<JWTClaimsSet>> {\n\t\tpublic Mono<JWTClaimsSet> convert(JWT jwt) {\n\t\t\ttry {\n\t\t\t\treturn Mono.just(jwt.getJWTClaimsSet());\n\t\t\t} catch (Exception ex) {\n\t\t\t\treturn Mono.error(ex);\n\t\t\t}\n\t\t}\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass JwtOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {\n    private val delegate: ReactiveOpaqueTokenIntrospector = SpringReactiveOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build()\n    private val jwtDecoder: ReactiveJwtDecoder = NimbusReactiveJwtDecoder(ParseOnlyJWTProcessor())\n    override fun introspect(token: String): Mono<OAuth2AuthenticatedPrincipal> {\n        return delegate.introspect(token)\n                .flatMap { jwtDecoder.decode(token) }\n                .map { jwt: Jwt -> DefaultOAuth2AuthenticatedPrincipal(jwt.claims, NO_AUTHORITIES) }\n    }\n\n    private class ParseOnlyJWTProcessor : Converter<JWT, Mono<JWTClaimsSet>> {\n        override fun convert(jwt: JWT): Mono<JWTClaimsSet> {\n            return try {\n                Mono.just(jwt.jwtClaimsSet)\n            } catch (e: Exception) {\n                Mono.error(e)\n            }\n        }\n    }\n}\n----\n======\n\nThereafter, you can configure this custom introspector by exposing it as a `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic ReactiveOpaqueTokenIntrospector introspector() {\n    return new JwtOpaqueTokenIntropsector();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): ReactiveOpaqueTokenIntrospector {\n    return JwtOpaqueTokenIntrospector()\n}\n----\n======\n\n[[webflux-oauth2resourceserver-opaque-userinfo]]\n== Calling a `/userinfo` Endpoint\n\nGenerally speaking, a Resource Server does not care about the underlying user but, instead, cares about the authorities that have been granted.\n\nThat said, at times it can be valuable to tie the authorization statement back to a user.\n\nIf an application also uses `spring-security-oauth2-client`, having set up the appropriate `ClientRegistrationRepository`,  you can do so with a custom `OpaqueTokenIntrospector`.\nThe implementation in the next listing does three things:\n\n* Delegates to the introspection endpoint, to affirm the token's validity.\n* Looks up the appropriate client registration associated with the `/userinfo` endpoint.\n* Invokes and returns the response from the `/userinfo` endpoint.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {\n\tprivate final ReactiveOpaqueTokenIntrospector delegate = SpringReactiveOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build();\n\tprivate final ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService =\n\t\t\tnew DefaultReactiveOAuth2UserService();\n\n\tprivate final ReactiveClientRegistrationRepository repository;\n\n\t// ... constructor\n\n\t@Override\n\tpublic Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {\n\t\treturn Mono.zip(this.delegate.introspect(token), this.repository.findByRegistrationId(\"registration-id\"))\n\t\t\t\t.map(t -> {\n\t\t\t\t\tOAuth2AuthenticatedPrincipal authorized = t.getT1();\n\t\t\t\t\tClientRegistration clientRegistration = t.getT2();\n\t\t\t\t\tInstant issuedAt = authorized.getAttribute(ISSUED_AT);\n\t\t\t\t\tInstant expiresAt = authorized.getAttribute(OAuth2IntrospectionClaimNames.EXPIRES_AT);\n\t\t\t\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(BEARER, token, issuedAt, expiresAt);\n\t\t\t\t\treturn new OAuth2UserRequest(clientRegistration, accessToken);\n\t\t\t\t})\n\t\t\t\t.flatMap(this.oauth2UserService::loadUser);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {\n    private val delegate: ReactiveOpaqueTokenIntrospector = SpringReactiveOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build()\n    private val oauth2UserService: ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> = DefaultReactiveOAuth2UserService()\n    private val repository: ReactiveClientRegistrationRepository? = null\n\n    // ... constructor\n    override fun introspect(token: String?): Mono<OAuth2AuthenticatedPrincipal> {\n        return Mono.zip<OAuth2AuthenticatedPrincipal, ClientRegistration>(delegate.introspect(token), repository!!.findByRegistrationId(\"registration-id\"))\n                .map<OAuth2UserRequest> { t: Tuple2<OAuth2AuthenticatedPrincipal, ClientRegistration> ->\n                    val authorized = t.t1\n                    val clientRegistration = t.t2\n                    val issuedAt: Instant? = authorized.getAttribute(ISSUED_AT)\n                    val expiresAt: Instant? = authorized.getAttribute(OAuth2IntrospectionClaimNames.EXPIRES_AT)\n                    val accessToken = OAuth2AccessToken(BEARER, token, issuedAt, expiresAt)\n                    OAuth2UserRequest(clientRegistration, accessToken)\n                }\n                .flatMap { userRequest: OAuth2UserRequest -> oauth2UserService.loadUser(userRequest) }\n    }\n}\n----\n======\n\nIf you aren't using `spring-security-oauth2-client`, it's still quite simple.\nYou will simply need to invoke the `/userinfo` with your own instance of `WebClient`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class UserInfoOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {\n    private final ReactiveOpaqueTokenIntrospector delegate = SpringReactiveOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build();\n    private final WebClient rest = WebClient.create();\n\n    @Override\n    public Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {\n        return this.delegate.introspect(token)\n\t\t        .map(this::makeUserInfoRequest);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass UserInfoOpaqueTokenIntrospector : ReactiveOpaqueTokenIntrospector {\n    private val delegate: ReactiveOpaqueTokenIntrospector = SpringReactiveOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build()\n    private val rest: WebClient = WebClient.create()\n\n    override fun introspect(token: String): Mono<OAuth2AuthenticatedPrincipal> {\n        return delegate.introspect(token)\n                .map(this::makeUserInfoRequest)\n    }\n}\n----\n======\n\nEither way, having created your `ReactiveOpaqueTokenIntrospector`, you should publish it as a `@Bean` to override the defaults:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nReactiveOpaqueTokenIntrospector introspector() {\n    return new UserInfoOpaqueTokenIntrospector();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): ReactiveOpaqueTokenIntrospector {\n    return UserInfoOpaqueTokenIntrospector()\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/test/index.adoc",
    "content": "[[test-webflux]]\n= Reactive Test Support\n:page-section-summary-toc: 1\n\nSpring Security supports two basic modes for testing reactive applications.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/test/method.adoc",
    "content": "[[test-erms]]\n= Testing Method Security\n\nFor example, we can test our example from xref:reactive/authorization/method.adoc#jc-erms[EnableReactiveMethodSecurity] by using the same setup and annotations that we used in xref:servlet/test/method.adoc#test-method[Testing Method Security].\nThe following minimal sample shows what we can do:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = HelloWebfluxMethodApplication.class)\npublic class HelloWorldMessageServiceTests {\n\t@Autowired\n\tHelloWorldMessageService messages;\n\n\t@Test\n\tpublic void messagesWhenNotAuthenticatedThenDenied() {\n\t\tStepVerifier.create(this.messages.findMessage())\n\t\t\t.expectError(AccessDeniedException.class)\n\t\t\t.verify();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void messagesWhenUserThenDenied() {\n\t\tStepVerifier.create(this.messages.findMessage())\n\t\t\t.expectError(AccessDeniedException.class)\n\t\t\t.verify();\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic void messagesWhenAdminThenOk() {\n\t\tStepVerifier.create(this.messages.findMessage())\n\t\t\t.expectNext(\"Hello World!\")\n\t\t\t.verifyComplete();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = [HelloWebfluxMethodApplication::class])\nclass HelloWorldMessageServiceTests {\n    @Autowired\n    lateinit var messages: HelloWorldMessageService\n\n    @Test\n    fun messagesWhenNotAuthenticatedThenDenied() {\n        StepVerifier.create(messages.findMessage())\n            .expectError(AccessDeniedException::class.java)\n            .verify()\n    }\n\n    @Test\n    @WithMockUser\n    fun messagesWhenUserThenDenied() {\n        StepVerifier.create(messages.findMessage())\n            .expectError(AccessDeniedException::class.java)\n            .verify()\n    }\n\n    @Test\n    @WithMockUser(roles = [\"ADMIN\"])\n    fun messagesWhenAdminThenOk() {\n        StepVerifier.create(messages.findMessage())\n            .expectNext(\"Hello World!\")\n            .verifyComplete()\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/test/web/authentication.adoc",
    "content": "= Testing Authentication\n\nAfter xref:reactive/test/web/setup.adoc[applying the Spring Security support to `WebTestClient`], we can use either annotations or `mutateWith` support -- for example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser;\n\n@Test\npublic void messageWhenNotAuthenticated() throws Exception {\n\tthis.rest\n\t\t.get()\n\t\t.uri(\"/message\")\n\t\t.exchange()\n\t\t.expectStatus().isUnauthorized();\n}\n\n// --- WithMockUser ---\n\n@Test\n@WithMockUser\npublic void messageWhenWithMockUserThenForbidden() throws Exception {\n\tthis.rest\n\t\t.get()\n\t\t.uri(\"/message\")\n\t\t.exchange()\n\t\t.expectStatus().isEqualTo(HttpStatus.FORBIDDEN);\n}\n\n@Test\n@WithMockUser(roles = \"ADMIN\")\npublic void messageWhenWithMockAdminThenOk() throws Exception {\n\tthis.rest\n\t\t.get()\n\t\t.uri(\"/message\")\n\t\t.exchange()\n\t\t.expectStatus().isOk()\n\t\t.expectBody(String.class).isEqualTo(\"Hello World!\");\n}\n\n// --- mutateWith mockUser ---\n\n@Test\npublic void messageWhenMutateWithMockUserThenForbidden() throws Exception {\n\tthis.rest\n\t\t.mutateWith(mockUser())\n\t\t.get()\n\t\t.uri(\"/message\")\n\t\t.exchange()\n\t\t.expectStatus().isEqualTo(HttpStatus.FORBIDDEN);\n}\n\n@Test\npublic void messageWhenMutateWithMockAdminThenOk() throws Exception {\n\tthis.rest\n\t\t.mutateWith(mockUser().roles(\"ADMIN\"))\n\t\t.get()\n\t\t.uri(\"/message\")\n\t\t.exchange()\n\t\t.expectStatus().isOk()\n\t\t.expectBody(String.class).isEqualTo(\"Hello World!\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.test.web.reactive.server.expectBody\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser\n\n//...\n\n@Test\n@WithMockUser\nfun messageWhenWithMockUserThenForbidden() {\n    this.rest.get().uri(\"/message\")\n        .exchange()\n        .expectStatus().isEqualTo(HttpStatus.FORBIDDEN)\n}\n\n@Test\n@WithMockUser(roles = [\"ADMIN\"])\nfun messageWhenWithMockAdminThenOk() {\n    this.rest.get().uri(\"/message\")\n        .exchange()\n        .expectStatus().isOk\n        .expectBody<String>().isEqualTo(\"Hello World!\")\n\n}\n\n// --- mutateWith mockUser ---\n\n@Test\nfun messageWhenMutateWithMockUserThenForbidden() {\n    this.rest\n        .mutateWith(mockUser())\n        .get().uri(\"/message\")\n        .exchange()\n        .expectStatus().isEqualTo(HttpStatus.FORBIDDEN)\n}\n\n@Test\nfun messageWhenMutateWithMockAdminThenOk() {\n    this.rest\n        .mutateWith(mockUser().roles(\"ADMIN\"))\n        .get().uri(\"/message\")\n        .exchange()\n        .expectStatus().isOk\n        .expectBody<String>().isEqualTo(\"Hello World!\")\n}\n----\n======\n\nIn addition to `mockUser()`, Spring Security ships with several other convenience mutators for things like xref:reactive/test/web/csrf.adoc[CSRF] and xref:reactive/test/web/oauth2.adoc[OAuth 2.0].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/test/web/csrf.adoc",
    "content": "= Testing with CSRF\n\nSpring Security also provides support for CSRF testing with `WebTestClient` -- for example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;\n\nthis.rest\n\t// provide a valid CSRF token\n\t.mutateWith(csrf())\n\t.post()\n\t.uri(\"/login\")\n\t...\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf\n\nthis.rest\n    // provide a valid CSRF token\n    .mutateWith(csrf())\n    .post()\n    .uri(\"/login\")\n    ...\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/test/web/index.adoc",
    "content": "[[test-webtestclient]]\n= Testing Web Security\n:page-section-summary-toc: 1\n\nIn this section, we'll talk about testing web application endpoints.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/test/web/oauth2.adoc",
    "content": "[[webflux-testing-oauth2]]\n= Testing OAuth 2.0\n\nWhen it comes to OAuth 2.0, xref:reactive/test/method.adoc#test-erms[the same principles covered earlier still apply]: Ultimately, it depends on what your method under test is expecting to be in the `SecurityContextHolder`.\n\nConsider the following example of a controller:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(Principal user) {\n    return Mono.just(user.getName());\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(user: Principal): Mono<String> {\n    return Mono.just(user.name)\n}\n----\n======\n\nNothing about it is OAuth2-specific, so you can xref:reactive/test/method.adoc#test-erms[use `@WithMockUser`] and be fine.\n\nHowever, consider a case where your controller is bound to some aspect of Spring Security's OAuth 2.0 support:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(@AuthenticationPrincipal OidcUser user) {\n    return Mono.just(user.getIdToken().getSubject());\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@AuthenticationPrincipal user: OidcUser): Mono<String> {\n    return Mono.just(user.idToken.subject)\n}\n----\n======\n\nIn that case, Spring Security's test support is handy.\n\n[[webflux-testing-oidc-login]]\n== Testing OIDC Login\n\nTesting the method shown in the <<webflux-testing-oauth2,preceding section>> with `WebTestClient` requires simulating some kind of grant flow with an authorization server.\nThis is a daunting task, which is why Spring Security ships with support for removing this boilerplate.\n\nFor example, we can tell Spring Security to include a default `OidcUser` by using the `SecurityMockServerConfigurers#oidcLogin` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOidcLogin()).get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOidcLogin())\n    .get().uri(\"/endpoint\")\n    .exchange()\n----\n======\n\nThat line configures the associated `MockServerRequest` with an `OidcUser` that includes a simple `OidcIdToken`, an `OidcUserInfo`, and a `Collection` of granted authorities.\n\nSpecifically, it includes an `OidcIdToken` with a `sub` claim set to `user`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(user.getIdToken().getClaim(\"sub\")).isEqualTo(\"user\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.idToken.getClaim<String>(\"sub\")).isEqualTo(\"user\")\n----\n======\n\nIt also includes an `OidcUserInfo` with no claims set:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(user.getUserInfo().getClaims()).isEmpty();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.userInfo.claims).isEmpty()\n----\n======\n\nIt also includes a `Collection` of authorities with just one authority, `SCOPE_read`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(user.getAuthorities()).hasSize(1);\nassertThat(user.getAuthorities()).containsExactly(new SimpleGrantedAuthority(\"SCOPE_read\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.authorities).hasSize(1)\nassertThat(user.authorities).containsExactly(SimpleGrantedAuthority(\"SCOPE_read\"))\n----\n======\n\nSpring Security makes sure that the `OidcUser` instance is available forxref:servlet/integrations/mvc.adoc#mvc-authentication-principal[the `@AuthenticationPrincipal` annotation].\n\nFurther, it also links the `OidcUser` to a simple instance of `OAuth2AuthorizedClient` that it deposits into a mock `ServerOAuth2AuthorizedClientRepository`.\nThis can be handy if your tests <<webflux-testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>..\n\n[[webflux-testing-oidc-login-authorities]]\n=== Configuring Authorities\n\nIn many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.\n\nIn those cases, you can supply what granted authorities you need by using the `authorities()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOidcLogin()\n        .authorities(new SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOidcLogin()\n        .authorities(SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n[[webflux-testing-oidc-login-claims]]\n=== Configuring Claims\n\nWhile granted authorities are common across all of Spring Security, we also have claims in the case of OAuth 2.0.\n\nSuppose, for example, that you have a `user_id` claim that indicates the user's ID in your system.\nYou might access it as follows in a controller:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(@AuthenticationPrincipal OidcUser oidcUser) {\n    String userId = oidcUser.getIdToken().getClaim(\"user_id\");\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@AuthenticationPrincipal oidcUser: OidcUser): Mono<String> {\n    val userId = oidcUser.idToken.getClaim<String>(\"user_id\")\n    // ...\n}\n----\n======\n\nIn that case, you can specify that claim with the `idToken()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOidcLogin()\n        .idToken((token) -> token.claim(\"user_id\", \"1234\"))\n    )\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOidcLogin()\n        .idToken { token -> token.claim(\"user_id\", \"1234\") }\n    )\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nThat works because `OidcUser` collects its claims from `OidcIdToken`.\n\n[[webflux-testing-oidc-login-user]]\n=== Additional Configurations\n\nThere are additional methods, too, for further configuring the authentication, depending on what data your controller expects:\n\n* `userInfo(OidcUserInfo.Builder)`: Configures the `OidcUserInfo` instance\n* `clientRegistration(ClientRegistration)`: Configures the associated `OAuth2AuthorizedClient` with a given `ClientRegistration`\n* `oidcUser(OidcUser)`: Configures the complete `OidcUser` instance\n\nThat last one is handy if you:\n* Have your own implementation of `OidcUser` or\n* Need to change the name attribute\n\nFor example, suppose that your authorization server sends the principal name in the `user_name` claim instead of the `sub` claim.\nIn that case, you can configure an `OidcUser` by hand:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOidcUser oidcUser = new DefaultOidcUser(\n        AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"),\n        OidcIdToken.withTokenValue(\"id-token\").claim(\"user_name\", \"foo_user\").build(),\n        \"user_name\");\n\nclient\n    .mutateWith(mockOidcLogin().oidcUser(oidcUser))\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval oidcUser: OidcUser = DefaultOidcUser(\n    AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"),\n    OidcIdToken.withTokenValue(\"id-token\").claim(\"user_name\", \"foo_user\").build(),\n    \"user_name\"\n)\n\nclient\n    .mutateWith(mockOidcLogin().oidcUser(oidcUser))\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n[[webflux-testing-oauth2-login]]\n== Testing OAuth 2.0 Login\n\nAs with <<webflux-testing-oidc-login,testing OIDC login>>, testing OAuth 2.0 Login presents a similar challenge: mocking a grant flow.\nBecause of that, Spring Security also has test support for non-OIDC use cases.\n\nSuppose that we have a controller that gets the logged-in user as an `OAuth2User`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(@AuthenticationPrincipal OAuth2User oauth2User) {\n    return Mono.just(oauth2User.getAttribute(\"sub\"));\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@AuthenticationPrincipal oauth2User: OAuth2User): Mono<String> {\n    return Mono.just(oauth2User.getAttribute(\"sub\"))\n}\n----\n======\n\nIn that case, we can tell Spring Security to include a default `OAuth2User` by using the `SecurityMockServerConfigurers#oauth2User` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOAuth2Login())\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOAuth2Login())\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nThe preceding example configures the associated `MockServerRequest` with an `OAuth2User` that includes a simple `Map` of attributes and a `Collection` of granted authorities.\n\nSpecifically, it includes a `Map` with a key/value pair of `sub`/`user`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat((String) user.getAttribute(\"sub\")).isEqualTo(\"user\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.getAttribute<String>(\"sub\")).isEqualTo(\"user\")\n----\n======\n\nIt also includes a `Collection` of authorities with just one authority, `SCOPE_read`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(user.getAuthorities()).hasSize(1);\nassertThat(user.getAuthorities()).containsExactly(new SimpleGrantedAuthority(\"SCOPE_read\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.authorities).hasSize(1)\nassertThat(user.authorities).containsExactly(SimpleGrantedAuthority(\"SCOPE_read\"))\n----\n======\n\nSpring Security does the necessary work to make sure that the `OAuth2User` instance is available for xref:servlet/integrations/mvc.adoc#mvc-authentication-principal[the `@AuthenticationPrincipal` annotation].\n\nFurther, it also links that `OAuth2User` to a simple instance of `OAuth2AuthorizedClient` that it deposits in a mock `ServerOAuth2AuthorizedClientRepository`.\nThis can be handy if your tests <<webflux-testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>.\n\n[[webflux-testing-oauth2-login-authorities]]\n=== Configuring Authorities\n\nIn many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.\n\nIn this case, you can supply the granted authorities you need by using the `authorities()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOAuth2Login()\n        .authorities(new SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOAuth2Login()\n        .authorities(SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n[[webflux-testing-oauth2-login-claims]]\n=== Configuring Claims\n\nWhile granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.\n\nSuppose, for example, that you have a `user_id` attribute that indicates the user's ID in your system.\nYou might access it as follows in a controller:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(@AuthenticationPrincipal OAuth2User oauth2User) {\n    String userId = oauth2User.getAttribute(\"user_id\");\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@AuthenticationPrincipal oauth2User: OAuth2User): Mono<String> {\n    val userId = oauth2User.getAttribute<String>(\"user_id\")\n    // ...\n}\n----\n======\n\nIn that case, you can specify that attribute with the `attributes()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOAuth2Login()\n        .attributes((attrs) -> attrs.put(\"user_id\", \"1234\"))\n    )\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOAuth2Login()\n        .attributes { attrs -> attrs[\"user_id\"] = \"1234\" }\n    )\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n[[webflux-testing-oauth2-login-user]]\n=== Additional Configurations\n\nThere are additional methods, too, for further configuring the authentication, depending on what data your controller expects:\n\n* `clientRegistration(ClientRegistration)`: Configures the associated `OAuth2AuthorizedClient` with a given `ClientRegistration`\n* `oauth2User(OAuth2User)`: Configures the complete `OAuth2User` instance\n\nThat last one is handy if you:\n* Have your own implementation of `OAuth2User` or\n* Need to change the name attribute\n\nFor example, suppose that your authorization server sends the principal name in the `user_name` claim instead of the `sub` claim.\nIn that case, you can configure an `OAuth2User` by hand:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOAuth2User oauth2User = new DefaultOAuth2User(\n        AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"),\n        Collections.singletonMap(\"user_name\", \"foo_user\"),\n        \"user_name\");\n\nclient\n    .mutateWith(mockOAuth2Login().oauth2User(oauth2User))\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval oauth2User: OAuth2User = DefaultOAuth2User(\n    AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"),\n    mapOf(Pair(\"user_name\", \"foo_user\")),\n    \"user_name\"\n)\n\nclient\n    .mutateWith(mockOAuth2Login().oauth2User(oauth2User))\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n[[webflux-testing-oauth2-client]]\n== Testing OAuth 2.0 Clients\n\nIndependent of how your user authenticates, you may have other tokens and client registrations that are in play for the request you are testing.\nFor example, your controller may rely on the client credentials grant to get a token that is not associated with the user at all:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(@RegisteredOAuth2AuthorizedClient(\"my-app\") OAuth2AuthorizedClient authorizedClient) {\n    return this.webClient.get()\n        .attributes(oauth2AuthorizedClient(authorizedClient))\n        .retrieve()\n        .bodyToMono(String.class);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.web.reactive.function.client.bodyToMono\n\n// ...\n\n@GetMapping(\"/endpoint\")\nfun foo(@RegisteredOAuth2AuthorizedClient(\"my-app\") authorizedClient: OAuth2AuthorizedClient?): Mono<String> {\n    return this.webClient.get()\n        .attributes(oauth2AuthorizedClient(authorizedClient))\n        .retrieve()\n        .bodyToMono()\n}\n----\n======\n\nSimulating this handshake with the authorization server can be cumbersome.\nInstead, you can use `SecurityMockServerConfigurers#oauth2Client` to add a `OAuth2AuthorizedClient` to a mock `ServerOAuth2AuthorizedClientRepository`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOAuth2Client(\"my-app\"))\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOAuth2Client(\"my-app\"))\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nThis creates an `OAuth2AuthorizedClient` that has a simple `ClientRegistration`, a `OAuth2AccessToken`, and a resource owner name.\n\nSpecifically, it includes a `ClientRegistration` with a client ID of `test-client` and a client secret of `test-secret`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(authorizedClient.getClientRegistration().getClientId()).isEqualTo(\"test-client\");\nassertThat(authorizedClient.getClientRegistration().getClientSecret()).isEqualTo(\"test-secret\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(authorizedClient.clientRegistration.clientId).isEqualTo(\"test-client\")\nassertThat(authorizedClient.clientRegistration.clientSecret).isEqualTo(\"test-secret\")\n----\n======\n\nIt also includes a resource owner name of `user`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(authorizedClient.getPrincipalName()).isEqualTo(\"user\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(authorizedClient.principalName).isEqualTo(\"user\")\n----\n======\n\nIt also includes an `OAuth2AccessToken` with one scope, `read`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(authorizedClient.getAccessToken().getScopes()).hasSize(1);\nassertThat(authorizedClient.getAccessToken().getScopes()).containsExactly(\"read\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(authorizedClient.accessToken.scopes).hasSize(1)\nassertThat(authorizedClient.accessToken.scopes).containsExactly(\"read\")\n----\n======\n\nYou can then retrieve the client as usual by using `@RegisteredOAuth2AuthorizedClient` in a controller method.\n\n[[webflux-testing-oauth2-client-scopes]]\n=== Configuring Scopes\n\nIn many circumstances, the OAuth 2.0 access token comes with a set of scopes.\nConsider the following example of how a controller can inspect the scopes:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(@RegisteredOAuth2AuthorizedClient(\"my-app\") OAuth2AuthorizedClient authorizedClient) {\n    Set<String> scopes = authorizedClient.getAccessToken().getScopes();\n    if (scopes.contains(\"message:read\")) {\n        return this.webClient.get()\n            .attributes(oauth2AuthorizedClient(authorizedClient))\n            .retrieve()\n            .bodyToMono(String.class);\n    }\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.web.reactive.function.client.bodyToMono\n\n// ...\n\n@GetMapping(\"/endpoint\")\nfun foo(@RegisteredOAuth2AuthorizedClient(\"my-app\") authorizedClient: OAuth2AuthorizedClient): Mono<String> {\n    val scopes = authorizedClient.accessToken.scopes\n    if (scopes.contains(\"message:read\")) {\n        return webClient.get()\n            .attributes(oauth2AuthorizedClient(authorizedClient))\n            .retrieve()\n            .bodyToMono()\n    }\n    // ...\n}\n----\n======\n\nGiven a controller that inspects scopes, you can configure the scope by using the `accessToken()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOAuth2Client(\"my-app\")\n        .accessToken(new OAuth2AccessToken(BEARER, \"token\", null, null, Collections.singleton(\"message:read\")))\n    )\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOAuth2Client(\"my-app\")\n        .accessToken(OAuth2AccessToken(BEARER, \"token\", null, null, setOf(\"message:read\")))\n)\n.get().uri(\"/endpoint\").exchange()\n----\n======\n\n[[webflux-testing-oauth2-client-registration]]\n=== Additional Configurations\n\nYou can also use additional methods to further configure the authentication depending on what data your controller expects:\n\n* `principalName(String)`; Configures the resource owner name\n* `clientRegistration(Consumer<ClientRegistration.Builder>)`: Configures the associated `ClientRegistration`\n* `clientRegistration(ClientRegistration)`: Configures the complete `ClientRegistration`\n\nThat last one is handy if you want to use a real `ClientRegistration`\n\nFor example, suppose that you want to use one of your application's `ClientRegistration` definitions, as specified in your `application.yml`.\n\nIn that case, your test can autowire the `ReactiveClientRegistrationRepository` and look up the one your test needs:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nReactiveClientRegistrationRepository clientRegistrationRepository;\n\n// ...\n\nclient\n    .mutateWith(mockOAuth2Client()\n        .clientRegistration(this.clientRegistrationRepository.findByRegistrationId(\"facebook\").block())\n    )\n    .get().uri(\"/exchange\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nlateinit var clientRegistrationRepository: ReactiveClientRegistrationRepository\n\n// ...\n\nclient\n    .mutateWith(mockOAuth2Client()\n        .clientRegistration(this.clientRegistrationRepository.findByRegistrationId(\"facebook\").block())\n    )\n    .get().uri(\"/exchange\").exchange()\n----\n======\n\n[[webflux-testing-jwt]]\n== Testing JWT Authentication\n\nTo make an authorized request on a resource server, you need a bearer token.\nIf your resource server is configured for JWTs, the bearer token needs to be signed and then encoded according to the JWT specification.\nAll of this can be quite daunting, especially when this is not the focus of your test.\n\nFortunately, there are a number of simple ways in which you can overcome this difficulty and let your tests focus on authorization and not on representing bearer tokens.\nWe look at two of them in the next two subsections.\n\n=== `mockJwt() WebTestClientConfigurer`\n\nThe first way is with a `WebTestClientConfigurer`.\nThe simplest of these would be to use the `SecurityMockServerConfigurers#mockJwt` method like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockJwt()).get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockJwt()).get().uri(\"/endpoint\").exchange()\n----\n======\n\nThis example creates a mock `Jwt` and passes it through any authentication APIs so that it is available for your authorization mechanisms to verify.\n\nBy default, the `JWT` that it creates has the following characteristics:\n\n[source,json]\n----\n{\n  \"headers\" : { \"alg\" : \"none\" },\n  \"claims\" : {\n    \"sub\" : \"user\",\n    \"scope\" : \"read\"\n  }\n}\n----\n\nThe resulting `Jwt`, were it tested, would pass in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(jwt.getTokenValue()).isEqualTo(\"token\");\nassertThat(jwt.getHeaders().get(\"alg\")).isEqualTo(\"none\");\nassertThat(jwt.getSubject()).isEqualTo(\"sub\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(jwt.tokenValue).isEqualTo(\"token\")\nassertThat(jwt.headers[\"alg\"]).isEqualTo(\"none\")\nassertThat(jwt.subject).isEqualTo(\"sub\")\n----\n======\n\nNote that you configure these values.\n\nYou can also configure any headers or claims with their corresponding methods:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n\t.mutateWith(mockJwt().jwt((jwt) -> jwt.header(\"kid\", \"one\")\n\t\t.claim(\"iss\", \"https://idp.example.org\")))\n\t.get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockJwt().jwt { jwt -> jwt.header(\"kid\", \"one\")\n        .claim(\"iss\", \"https://idp.example.org\")\n    })\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n\t.mutateWith(mockJwt().jwt((jwt) -> jwt.claims((claims) -> claims.remove(\"scope\"))))\n\t.get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockJwt().jwt { jwt ->\n        jwt.claims { claims -> claims.remove(\"scope\") }\n    })\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nThe `scope` and `scp` claims are processed the same way here as they are in a normal bearer token request.\nHowever, this can be overridden simply by providing the list of `GrantedAuthority` instances that you need for your test:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n\t.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority(\"SCOPE_messages\")))\n\t.get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockJwt().authorities(SimpleGrantedAuthority(\"SCOPE_messages\")))\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nAlternatively, if you have a custom `Jwt` to `Collection<GrantedAuthority>` converter, you can also use that to derive the authorities:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n\t.mutateWith(mockJwt().authorities(new MyConverter()))\n\t.get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockJwt().authorities(MyConverter()))\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nYou can also specify a complete `Jwt`, for which javadoc:org.springframework.security.oauth2.jwt.Jwt$Builder[] is quite handy:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nJwt jwt = Jwt.withTokenValue(\"token\")\n    .header(\"alg\", \"none\")\n    .claim(\"sub\", \"user\")\n    .claim(\"scope\", \"read\")\n    .build();\n\nclient\n\t.mutateWith(mockJwt().jwt(jwt))\n\t.get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwt: Jwt = Jwt.withTokenValue(\"token\")\n    .header(\"alg\", \"none\")\n    .claim(\"sub\", \"user\")\n    .claim(\"scope\", \"read\")\n    .build()\n\nclient\n    .mutateWith(mockJwt().jwt(jwt))\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n=== `authentication()` and `WebTestClientConfigurer`\n\nThe second way is by using the `authentication()` `Mutator`.\nYou can instantiate your own `JwtAuthenticationToken` and provide it in your test:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nJwt jwt = Jwt.withTokenValue(\"token\")\n    .header(\"alg\", \"none\")\n    .claim(\"sub\", \"user\")\n    .build();\nCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"SCOPE_read\");\nJwtAuthenticationToken token = new JwtAuthenticationToken(jwt, authorities);\n\nclient\n\t.mutateWith(mockAuthentication(token))\n\t.get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwt = Jwt.withTokenValue(\"token\")\n    .header(\"alg\", \"none\")\n    .claim(\"sub\", \"user\")\n    .build()\nval authorities: Collection<GrantedAuthority> = AuthorityUtils.createAuthorityList(\"SCOPE_read\")\nval token = JwtAuthenticationToken(jwt, authorities)\n\nclient\n    .mutateWith(mockAuthentication<JwtMutator>(token))\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nNote that, as an alternative to these, you can also mock the `ReactiveJwtDecoder` bean itself with a `@MockBean` annotation.\n\n[[webflux-testing-opaque-token]]\n== Testing Opaque Token Authentication\n\nSimilar to <<webflux-testing-jwt,JWTs>>, opaque tokens require an authorization server in order to verify their validity, which can make testing more difficult.\nTo help with that, Spring Security has test support for opaque tokens.\n\nSuppose you have a controller that retrieves the authentication as a `BearerTokenAuthentication`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(BearerTokenAuthentication authentication) {\n    return Mono.just((String) authentication.getTokenAttributes().get(\"sub\"));\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(authentication: BearerTokenAuthentication): Mono<String?> {\n    return Mono.just(authentication.tokenAttributes[\"sub\"] as String?)\n}\n----\n======\n\nIn that case, you can tell Spring Security to include a default `BearerTokenAuthentication` by using the `SecurityMockServerConfigurers#opaqueToken` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOpaqueToken())\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOpaqueToken())\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nThis example configures the associated `MockHttpServletRequest` with a `BearerTokenAuthentication` that includes a simple `OAuth2AuthenticatedPrincipal`, a `Map` of attributes, and a `Collection` of granted authorities.\n\nSpecifically, it includes a `Map` with a key/value pair of `sub`/`user`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat((String) token.getTokenAttributes().get(\"sub\")).isEqualTo(\"user\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(token.tokenAttributes[\"sub\"] as String?).isEqualTo(\"user\")\n----\n======\n\nIt also includes a `Collection` of authorities with just one authority, `SCOPE_read`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(token.getAuthorities()).hasSize(1);\nassertThat(token.getAuthorities()).containsExactly(new SimpleGrantedAuthority(\"SCOPE_read\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(token.authorities).hasSize(1)\nassertThat(token.authorities).containsExactly(SimpleGrantedAuthority(\"SCOPE_read\"))\n----\n======\n\nSpring Security does the necessary work to make sure that the `BearerTokenAuthentication` instance is available for your controller methods.\n\n[[webflux-testing-opaque-token-authorities]]\n=== Configuring Authorities\n\nIn many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.\n\nIn this case, you can supply what granted authorities you need using the `authorities()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOpaqueToken()\n        .authorities(new SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOpaqueToken()\n        .authorities(SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n[[webflux-testing-opaque-token-attributes]]\n=== Configuring Claims\n\nWhile granted authorities are quite common across all of Spring Security, we also have attributes in the case of OAuth 2.0.\n\nSuppose, for example, that you have a `user_id` attribute that indicates the user's ID in your system.\nYou might access it as follows in a controller:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic Mono<String> foo(BearerTokenAuthentication authentication) {\n    String userId = (String) authentication.getTokenAttributes().get(\"user_id\");\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(authentication: BearerTokenAuthentication): Mono<String?> {\n    val userId = authentication.tokenAttributes[\"user_id\"] as String?\n    // ...\n}\n----\n======\n\nIn that case, you can specify that attribute with the `attributes()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclient\n    .mutateWith(mockOpaqueToken()\n        .attributes((attrs) -> attrs.put(\"user_id\", \"1234\"))\n    )\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclient\n    .mutateWith(mockOpaqueToken()\n        .attributes { attrs -> attrs[\"user_id\"] = \"1234\" }\n    )\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\n[[webflux-testing-opaque-token-principal]]\n=== Additional Configurations\n\nYou can also use additional methods to further configure the authentication, depending on what data your controller expects.\n\nOne such method is `principal(OAuth2AuthenticatedPrincipal)`, which you can use to configure the complete `OAuth2AuthenticatedPrincipal` instance that underlies the `BearerTokenAuthentication`.\n\nIt is handy if you:\n* Have your own implementation of `OAuth2AuthenticatedPrincipal` or\n* Want to specify a different principal name\n\nFor example, suppose that your authorization server sends the principal name in the `user_name` attribute instead of the `sub` attribute.\nIn that case, you can configure an `OAuth2AuthenticatedPrincipal` by hand:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMap<String, Object> attributes = Collections.singletonMap(\"user_name\", \"foo_user\");\nOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(\n        (String) attributes.get(\"user_name\"),\n        attributes,\n        AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"));\n\nclient\n    .mutateWith(mockOpaqueToken().principal(principal))\n    .get().uri(\"/endpoint\").exchange();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval attributes: Map<String, Any> = mapOf(Pair(\"user_name\", \"foo_user\"))\nval principal: OAuth2AuthenticatedPrincipal = DefaultOAuth2AuthenticatedPrincipal(\n    attributes[\"user_name\"] as String?,\n    attributes,\n    AuthorityUtils.createAuthorityList(\"SCOPE_message:read\")\n)\n\nclient\n    .mutateWith(mockOpaqueToken().principal(principal))\n    .get().uri(\"/endpoint\").exchange()\n----\n======\n\nNote that, as an alternative to using `mockOpaqueToken()` test support, you can also mock the `OpaqueTokenIntrospector` bean itself with a `@MockBean` annotation.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/test/web/setup.adoc",
    "content": "= WebTestClient Security Setup\n\nThe basic setup looks like this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;\nimport static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = HelloWebfluxMethodApplication.class)\npublic class HelloWebfluxMethodApplicationTests {\n\t@Autowired\n\tApplicationContext context;\n\n\tWebTestClient rest;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.rest = WebTestClient\n\t\t\t.bindToApplicationContext(this.context)\n\t\t\t// add Spring Security test Support\n\t\t\t.apply(springSecurity())\n\t\t\t.configureClient()\n\t\t\t.filter(basicAuthentication(\"user\", \"password\"))\n\t\t\t.build();\n\t}\n\t// ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration(classes = [HelloWebfluxMethodApplication::class])\nclass HelloWebfluxMethodApplicationTests {\n    @Autowired\n    lateinit var context: ApplicationContext\n\n    lateinit var rest: WebTestClient\n\n    @BeforeEach\n    fun setup() {\n        this.rest = WebTestClient\n            .bindToApplicationContext(this.context)\n            // add Spring Security test Support\n            .apply(springSecurity())\n            .configureClient()\n            .filter(basicAuthentication(\"user\", \"password\"))\n            .build()\n    }\n    // ...\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/reactive/test/web/x509.adoc",
    "content": "= X509\n\nSpring Framework provides first class support for testing X509 with `WebTestClient`.\nFor details refer to javadoc:{spring-framework-api-url}org.springframework.test.web.reactive.server.UserWebTestClientConfigurer[].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/samples.adoc",
    "content": "[[samples]]\n= Samples\n\nSpring Security includes many {gh-samples-url}[samples] applications.\n\n[NOTE]\n====\nThese samples are being migrated to a separate project, however, you can still find\nthe not migrated samples in an older branch of the {gh-old-samples-url}[Spring Security repository].\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/database-schema.adoc",
    "content": "[[appendix-schema]]\n= Security Database Schema\nThe framework uses various database schema. This appendix provides a single reference point to them all.\nYou need only provide the tables for the areas of functionality you require.\n\nDDL statements are given for the HSQLDB database.\nYou can use these as a guideline for defining the schema for the database you are using.\n\n\n== User Schema\nThe standard JDBC implementation of the `UserDetailsService` (`JdbcDaoImpl`) requires tables to load the password, account status (enabled or disabled) and a list of authorities (roles) for the user.\nYou can use these as a guideline for defining the schema for the database you use.\n\n[source]\n----\n\ncreate table users(\n\tusername varchar_ignorecase(50) not null primary key,\n\tpassword varchar_ignorecase(500) not null,\n\tenabled boolean not null\n);\n\ncreate table authorities (\n\tusername varchar_ignorecase(50) not null,\n\tauthority varchar_ignorecase(50) not null,\n\tconstraint fk_authorities_users foreign key(username) references users(username)\n);\ncreate unique index ix_auth_username on authorities (username,authority);\n----\n\n=== For Oracle database\n\nThe following listing shows the Oracle variant of the schema creation commands:\n\n[source]\n----\nCREATE TABLE USERS (\n    USERNAME NVARCHAR2(128) PRIMARY KEY,\n    PASSWORD NVARCHAR2(128) NOT NULL,\n    ENABLED CHAR(1) CHECK (ENABLED IN ('Y','N') ) NOT NULL\n);\n\n\nCREATE TABLE AUTHORITIES (\n    USERNAME NVARCHAR2(128) NOT NULL,\n    AUTHORITY NVARCHAR2(128) NOT NULL\n);\nALTER TABLE AUTHORITIES ADD CONSTRAINT AUTHORITIES_UNIQUE UNIQUE (USERNAME, AUTHORITY);\nALTER TABLE AUTHORITIES ADD CONSTRAINT AUTHORITIES_FK1 FOREIGN KEY (USERNAME) REFERENCES USERS (USERNAME) ENABLE;\n----\n\n=== Group Authorities\nSpring Security 2.0 introduced support for group authorities in `JdbcDaoImpl`.\nThe table structure if groups are enabled is as follows.\nYou need to adjust the following schema to match the database dialect you use:\n\n[source]\n----\n\ncreate table groups (\n\tid bigint generated by default as identity(start with 0) primary key,\n\tgroup_name varchar_ignorecase(50) not null\n);\n\ncreate table group_authorities (\n\tgroup_id bigint not null,\n\tauthority varchar(50) not null,\n\tconstraint fk_group_authorities_group foreign key(group_id) references groups(id)\n);\n\ncreate table group_members (\n\tid bigint generated by default as identity(start with 0) primary key,\n\tusername varchar(50) not null,\n\tgroup_id bigint not null,\n\tconstraint fk_group_members_group foreign key(group_id) references groups(id)\n);\n----\n\nRemember that these tables are required only if you use the provided JDBC `UserDetailsService` implementation.\nIf you write your own or choose to implement `AuthenticationProvider` without a `UserDetailsService`, you have complete freedom over how you store the data, as long as the interface contract is satisfied.\n\n\n== Persistent Login (Remember-Me) Schema\nThis table is used to store the data used by the more secure <<remember-me-persistent-token,persistent token>> remember-me implementation.\nIf you use `JdbcTokenRepositoryImpl` either directly or through the namespace, you need this table.\nRemember to adjust this schema to match the database dialect you use:\n\n[source]\n----\n\ncreate table persistent_logins (\n\tusername varchar(64) not null,\n\tseries varchar(64) primary key,\n\ttoken varchar(64) not null,\n\tlast_used timestamp not null\n);\n\n----\n\n[[dbschema-acl]]\n== ACL Schema\nThe Spring Security xref:servlet/authorization/acls.adoc#domain-acls[ACL] implementation uses four tables.\n\n* `acl_sid` stores the security identities recognised by the ACL system.\nThese can be unique principals or authorities, which may apply to multiple principals.\n* `acl_class` defines the domain object types to which ACLs apply.\nThe `class` column stores the Java class name of the object.\n* `acl_object_identity` stores the object identity definitions of specific domain objects.\n* `acl_entry` stores the ACL permissions, each of which applies to a specific object identity and security identity.\n\nWe assume that the database auto-generates the primary keys for each of the identities.\nThe `JdbcMutableAclService` has to be able to retrieve these when it has created a new row in the `acl_sid` or `acl_class` tables.\nIt has two properties that define the SQL needed to retrieve these values `classIdentityQuery` and `sidIdentityQuery`.\nBoth of these default to `call identity()`\n\nThe ACL artifact JAR contains files for creating the ACL schema in HyperSQL (HSQLDB), PostgreSQL, MySQL/MariaDB, Microsoft SQL Server, and Oracle Database.\nThese schemas are also demonstrated in the following sections.\n\n=== HyperSQL\nThe default schema works with the embedded HSQLDB database that is used in unit tests within the framework.\n\n[source,ddl]\n----\ncreate table acl_sid(\n\tid bigint generated by default as identity(start with 100) not null primary key,\n\tprincipal boolean not null,\n\tsid varchar_ignorecase(100) not null,\n\tconstraint unique_uk_1 unique(sid,principal)\n);\n\ncreate table acl_class(\n\tid bigint generated by default as identity(start with 100) not null primary key,\n\tclass varchar_ignorecase(100) not null,\n\tconstraint unique_uk_2 unique(class)\n);\n\ncreate table acl_object_identity(\n\tid bigint generated by default as identity(start with 100) not null primary key,\n\tobject_id_class bigint not null,\n\tobject_id_identity varchar_ignorecase(36) not null,\n\tparent_object bigint,\n\towner_sid bigint,\n\tentries_inheriting boolean not null,\n\tconstraint unique_uk_3 unique(object_id_class,object_id_identity),\n\tconstraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),\n\tconstraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),\n\tconstraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)\n);\n\ncreate table acl_entry(\n\tid bigint generated by default as identity(start with 100) not null primary key,\n\tacl_object_identity bigint not null,\n\tace_order int not null,\n\tsid bigint not null,\n\tmask integer not null,\n\tgranting boolean not null,\n\taudit_success boolean not null,\n\taudit_failure boolean not null,\n\tconstraint unique_uk_4 unique(acl_object_identity,ace_order),\n\tconstraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),\n\tconstraint foreign_fk_5 foreign key(sid) references acl_sid(id)\n);\n----\n\n=== PostgreSQL\n\nFor PostgreSQL, you have to set the `classIdentityQuery` and `sidIdentityQuery` properties of `JdbcMutableAclService` to the following values, respectively:\n\n* `select currval(pg_get_serial_sequence('acl_class', 'id'))`\n* `select currval(pg_get_serial_sequence('acl_sid', 'id'))`\n\n[source,ddl]\n----\ncreate table acl_sid(\n\tid bigserial not null primary key,\n\tprincipal boolean not null,\n\tsid varchar(100) not null,\n\tconstraint unique_uk_1 unique(sid,principal)\n);\n\ncreate table acl_class(\n\tid bigserial not null primary key,\n\tclass varchar(100) not null,\n\tconstraint unique_uk_2 unique(class)\n);\n\ncreate table acl_object_identity(\n\tid bigserial primary key,\n\tobject_id_class bigint not null,\n\tobject_id_identity varchar(36) not null,\n\tparent_object bigint,\n\towner_sid bigint,\n\tentries_inheriting boolean not null,\n\tconstraint unique_uk_3 unique(object_id_class,object_id_identity),\n\tconstraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),\n\tconstraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),\n\tconstraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)\n);\n\ncreate table acl_entry(\n\tid bigserial primary key,\n\tacl_object_identity bigint not null,\n\tace_order int not null,\n\tsid bigint not null,\n\tmask integer not null,\n\tgranting boolean not null,\n\taudit_success boolean not null,\n\taudit_failure boolean not null,\n\tconstraint unique_uk_4 unique(acl_object_identity,ace_order),\n\tconstraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),\n\tconstraint foreign_fk_5 foreign key(sid) references acl_sid(id)\n);\n----\n\n=== MySQL and MariaDB\n\n[source,ddl]\n----\nCREATE TABLE acl_sid (\n\tid BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n\tprincipal BOOLEAN NOT NULL,\n\tsid VARCHAR(100) NOT NULL,\n\tUNIQUE KEY unique_acl_sid (sid, principal)\n) ENGINE=InnoDB;\n\nCREATE TABLE acl_class (\n\tid BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n\tclass VARCHAR(100) NOT NULL,\n\tUNIQUE KEY uk_acl_class (class)\n) ENGINE=InnoDB;\n\nCREATE TABLE acl_object_identity (\n\tid BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n\tobject_id_class BIGINT UNSIGNED NOT NULL,\n\tobject_id_identity VARCHAR(36) NOT NULL,\n\tparent_object BIGINT UNSIGNED,\n\towner_sid BIGINT UNSIGNED,\n\tentries_inheriting BOOLEAN NOT NULL,\n\tUNIQUE KEY uk_acl_object_identity (object_id_class, object_id_identity),\n\tCONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),\n\tCONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),\n\tCONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)\n) ENGINE=InnoDB;\n\nCREATE TABLE acl_entry (\n\tid BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n\tacl_object_identity BIGINT UNSIGNED NOT NULL,\n\tace_order INTEGER NOT NULL,\n\tsid BIGINT UNSIGNED NOT NULL,\n\tmask INTEGER UNSIGNED NOT NULL,\n\tgranting BOOLEAN NOT NULL,\n\taudit_success BOOLEAN NOT NULL,\n\taudit_failure BOOLEAN NOT NULL,\n\tUNIQUE KEY unique_acl_entry (acl_object_identity, ace_order),\n\tCONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),\n\tCONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)\n) ENGINE=InnoDB;\n----\n\n=== Microsoft SQL Server\n\n[source,ddl]\n----\nCREATE TABLE acl_sid (\n\tid BIGINT NOT NULL IDENTITY PRIMARY KEY,\n\tprincipal BIT NOT NULL,\n\tsid VARCHAR(100) NOT NULL,\n\tCONSTRAINT unique_acl_sid UNIQUE (sid, principal)\n);\n\nCREATE TABLE acl_class (\n\tid BIGINT NOT NULL IDENTITY PRIMARY KEY,\n\tclass VARCHAR(100) NOT NULL,\n\tCONSTRAINT uk_acl_class UNIQUE (class)\n);\n\nCREATE TABLE acl_object_identity (\n\tid BIGINT NOT NULL IDENTITY PRIMARY KEY,\n\tobject_id_class BIGINT NOT NULL,\n\tobject_id_identity VARCHAR(36) NOT NULL,\n\tparent_object BIGINT,\n\towner_sid BIGINT,\n\tentries_inheriting BIT NOT NULL,\n\tCONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),\n\tCONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),\n\tCONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),\n\tCONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)\n);\n\nCREATE TABLE acl_entry (\n\tid BIGINT NOT NULL IDENTITY PRIMARY KEY,\n\tacl_object_identity BIGINT NOT NULL,\n\tace_order INTEGER NOT NULL,\n\tsid BIGINT NOT NULL,\n\tmask INTEGER NOT NULL,\n\tgranting BIT NOT NULL,\n\taudit_success BIT NOT NULL,\n\taudit_failure BIT NOT NULL,\n\tCONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),\n\tCONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),\n\tCONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)\n);\n----\n\n=== Oracle Database\n\n[source,ddl]\n----\nCREATE TABLE ACL_SID (\n    ID NUMBER(18) PRIMARY KEY,\n    PRINCIPAL NUMBER(1) NOT NULL CHECK (PRINCIPAL IN (0, 1 )),\n    SID NVARCHAR2(128) NOT NULL,\n    CONSTRAINT ACL_SID_UNIQUE UNIQUE (SID, PRINCIPAL)\n);\nCREATE SEQUENCE ACL_SID_SQ START WITH 1 INCREMENT BY 1 NOMAXVALUE;\nCREATE OR REPLACE TRIGGER ACL_SID_SQ_TR BEFORE INSERT ON ACL_SID FOR EACH ROW\nBEGIN\n    SELECT ACL_SID_SQ.NEXTVAL INTO :NEW.ID FROM DUAL;\nEND;\n\n\nCREATE TABLE ACL_CLASS (\n    ID NUMBER(18) PRIMARY KEY,\n    CLASS NVARCHAR2(128) NOT NULL,\n    CONSTRAINT ACL_CLASS_UNIQUE UNIQUE (CLASS)\n);\nCREATE SEQUENCE ACL_CLASS_SQ START WITH 1 INCREMENT BY 1 NOMAXVALUE;\nCREATE OR REPLACE TRIGGER ACL_CLASS_ID_TR BEFORE INSERT ON ACL_CLASS FOR EACH ROW\nBEGIN\n    SELECT ACL_CLASS_SQ.NEXTVAL INTO :NEW.ID FROM DUAL;\nEND;\n\n\nCREATE TABLE ACL_OBJECT_IDENTITY(\n    ID NUMBER(18) PRIMARY KEY,\n    OBJECT_ID_CLASS NUMBER(18) NOT NULL,\n    OBJECT_ID_IDENTITY NVARCHAR2(64) NOT NULL,\n    PARENT_OBJECT NUMBER(18),\n    OWNER_SID NUMBER(18),\n    ENTRIES_INHERITING NUMBER(1) NOT NULL CHECK (ENTRIES_INHERITING IN (0, 1)),\n    CONSTRAINT ACL_OBJECT_IDENTITY_UNIQUE UNIQUE (OBJECT_ID_CLASS, OBJECT_ID_IDENTITY),\n    CONSTRAINT ACL_OBJECT_IDENTITY_PARENT_FK FOREIGN KEY (PARENT_OBJECT) REFERENCES ACL_OBJECT_IDENTITY(ID),\n    CONSTRAINT ACL_OBJECT_IDENTITY_CLASS_FK FOREIGN KEY (OBJECT_ID_CLASS) REFERENCES ACL_CLASS(ID),\n    CONSTRAINT ACL_OBJECT_IDENTITY_OWNER_FK FOREIGN KEY (OWNER_SID) REFERENCES ACL_SID(ID)\n);\nCREATE SEQUENCE ACL_OBJECT_IDENTITY_SQ START WITH 1 INCREMENT BY 1 NOMAXVALUE;\nCREATE OR REPLACE TRIGGER ACL_OBJECT_IDENTITY_ID_TR BEFORE INSERT ON ACL_OBJECT_IDENTITY FOR EACH ROW\nBEGIN\n    SELECT ACL_OBJECT_IDENTITY_SQ.NEXTVAL INTO :NEW.ID FROM DUAL;\nEND;\n\n\nCREATE TABLE ACL_ENTRY (\n    ID NUMBER(18) NOT NULL PRIMARY KEY,\n    ACL_OBJECT_IDENTITY NUMBER(18) NOT NULL,\n    ACE_ORDER INTEGER NOT NULL,\n    SID NUMBER(18) NOT NULL,\n    MASK INTEGER NOT NULL,\n    GRANTING NUMBER(1) NOT NULL CHECK (GRANTING IN (0, 1)),\n    AUDIT_SUCCESS NUMBER(1) NOT NULL CHECK (AUDIT_SUCCESS IN (0, 1)),\n    AUDIT_FAILURE NUMBER(1) NOT NULL CHECK (AUDIT_FAILURE IN (0, 1)),\n    CONSTRAINT ACL_ENTRY_UNIQUE UNIQUE (ACL_OBJECT_IDENTITY, ACE_ORDER),\n    CONSTRAINT ACL_ENTRY_OBJECT_FK FOREIGN KEY (ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY (ID),\n    CONSTRAINT ACL_ENTRY_ACL_FK FOREIGN KEY (SID) REFERENCES ACL_SID(ID)\n);\nCREATE SEQUENCE ACL_ENTRY_SQ START WITH 1 INCREMENT BY 1 NOMAXVALUE;\nCREATE OR REPLACE TRIGGER ACL_ENTRY_ID_TRIGGER BEFORE INSERT ON ACL_ENTRY FOR EACH ROW\nBEGIN\n    SELECT ACL_ENTRY_SQ.NEXTVAL INTO :NEW.ID FROM DUAL;\nEND;\n----\n\n[[dbschema-oauth2-client]]\n== OAuth 2.0 Client Schema\nThe JDBC implementation of xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-repo-service[ `OAuth2AuthorizedClientService`] (`JdbcOAuth2AuthorizedClientService`) requires a table for persisting `OAuth2AuthorizedClient` instances.\nYou will need to adjust this schema to match the database dialect you use.\n\n[source,ddl]\n----\nCREATE TABLE oauth2_authorized_client (\n  client_registration_id varchar(100) NOT NULL,\n  principal_name varchar(200) NOT NULL,\n  access_token_type varchar(100) NOT NULL,\n  access_token_value blob NOT NULL,\n  access_token_issued_at timestamp NOT NULL,\n  access_token_expires_at timestamp NOT NULL,\n  access_token_scopes varchar(1000) DEFAULT NULL,\n  refresh_token_value blob DEFAULT NULL,\n  refresh_token_issued_at timestamp DEFAULT NULL,\n  created_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,\n  PRIMARY KEY (client_registration_id, principal_name)\n);\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/faq.adoc",
    "content": "[[appendix-faq]]\n= Spring Security FAQ\n\nThis FAQ has the following sections:\n\n* <<appendix-faq-general-questions>>\n* <<appendix-faq-common-problems>>\n* <<appendix-faq-architecture>>\n* <<appendix-faq-howto>>\n\n[[appendix-faq-general-questions]]\n== General Questions\n\nThis FAQ answers the following general questions:\n\n* <<appendix-faq-other-concerns>>\n* <<appendix-faq-web-xml>>\n* <<appendix-faq-requirements>>\n* <<appendix-faq-start-simple>>\n\n\n[[appendix-faq-other-concerns]]\n=== Can Spring Security take care of all my application security requirements?\n\nSpring Security provides you with a flexible framework for your authentication and authorization requirements, but there are many other considerations for building a secure application that are outside its scope.\nWeb applications are vulnerable to all kinds of attacks with which you should be familiar, preferably before you start development so that you can design and code with them in mind from the beginning.\nCheck out the https://www.owasp.org/[OWASP website] for information on the major issues that face web application developers and the countermeasures you can use against them.\n\n\n[[appendix-faq-web-xml]]\n=== Why Not Use web.xml Security?\n\nSuppose you are developing an enterprise application based on Spring.\nYou typically need to address four security concerns : authentication, web request security, service layer security (your methods that implement business logic), and domain object instance security (different domain objects can have different permissions). With these typical requirements in mind, we have the following considerations:\n\n* _Authentication_: The servlet specification provides an approach to authentication.\nHowever, you need to configure the container to perform authentication, which typically requires editing of container-specific \"`realm`\" settings.\nThis makes a non-portable configuration. Also, if you need to write an actual Java class to implement the container's authentication interface, it becomes even more non-portable.\nWith Spring Security, you achieve complete portability -- right down to the WAR level.\nAlso, Spring Security offers a choice of production-proven authentication providers and mechanisms, meaning you can switch your authentication approaches at deployment time.\nThis is particularly valuable for software vendors writing products that need to work in an unknown target environment.\n\n* _Web request security:_ The servlet specification provides an approach to secure your request URIs.\nHowever, these URIs can be expressed only in the servlet specification's own limited URI path format.\nSpring Security provides a far more comprehensive approach.\nFor instance, you can use Ant paths or regular expressions, you can consider parts of the URI other than simply the requested page (for example,\nyou can consider HTTP GET parameters), and you can implement your own runtime source of configuration data.\nThis means that you can dynamically change your web request security during the actual execution of your web application.\n\n* _Service layer and domain object security:_ The absence of support in the servlet specification for services layer security or domain object instance security represents serious limitations for multi-tiered applications.\nTypically, developers either ignore these requirements or implement security logic within their MVC controller code (or, even worse, inside the views). There are serious disadvantages with this approach:\n\n** _Separation of concerns:_ Authorization is a crosscutting concern and should be implemented as such.\nMVC controllers or views that implement authorization code makes it more difficult to test both the controller and the authorization logic, is more difficult to debug, and often leads to code duplication.\n\n** _Support for rich clients and web services:_ If an additional client type must ultimately be supported, any authorization code embedded within the web layer is non-reusable.\nIt should be considered that Spring remoting exporters export only service layer beans (not MVC controllers). As a result, authorization logic needs to be located in the services layer to support a multitude of client types.\n\n** _Layering issues:_ An MVC controller or view is the incorrect architectural layer in which to implement authorization decisions concerning services layer methods or domain object instances.\nWhile the principal may be passed to the services layer to enable it to make the authorization decision, doing so would introduce an additional argument on every services layer method.\nA more elegant approach is to use a `ThreadLocal` to hold the principal, although this would likely increase development time to a point where it would become more economical (on a cost-benefit basis) to use a dedicated security framework.\n\n** _Authorization code quality:_ It is often said of web frameworks that they \"`make it easier to do the right things, and harder to do the wrong things`\". Security frameworks are the same, because they are designed in an abstract manner for a wide range of purposes.\nWriting your own authorization code from scratch does not provide the \"`design check`\" a framework would offer, and in-house authorization code typically lacks the improvements that emerge from widespread deployment, peer review, and new versions.\n\nFor simple applications, servlet specification security may be enough.\nAlthough when considered within the context of web container portability, configuration requirements, limited web request security flexibility, and non-existent services layer and domain object instance security, it becomes clear why developers often look to alternative solutions.\n\n\n[[appendix-faq-requirements]]\n=== What Java and Spring Framework versions are required?\n\nSpring Security 3.0 and 3.1 require at least JDK 1.5 and also require Spring 3.0.3 as a minimum.\nIdeally, you should use the latest release versions to avoid problems.\n\nSpring Security 2.0.x requires a minimum JDK version of 1.4 and is built against Spring 2.0.x.\nIt should also be compatible with applications that use Spring 2.5.x.\n\n\n[[appendix-faq-start-simple]]\n==== I have a complex scenario. What could be wrong?\n\n(This answer address complex scenarios in general by dealing with a particular scenario.)\n\nSuppose you are new to Spring Security and need to build an application that supports CAS single sign-on over HTTPS while allowing basic authentication locally for certain URLs, authenticating against multiple back end user information sources (LDAP and JDBC). You have copied some configuration files but have found that it does not work. What could be wrong?\n\nYou need an understanding of the technologies you intend to use before you can successfully build applications with them.\nSecurity is complicated.\nSetting up a simple configuration by using a login form and some hard-coded users with Spring Security's namespace is reasonably straightforward.\nMoving to using a backed JDBC database is also easy enough.\nHowever, if you try to jump straight to a complicated deployment scenario like this scenario, you are almost certain to be frustrated.\nThere is a big jump in the learning curve required to set up systems such as CAS, configure LDAP servers, and install SSL certificates properly.\nSo you need to take things one step at a time.\n\nFrom a Spring Security perspective, the first thing you should do is follow the \"`Getting Started`\" guide on the website.\nThis will take you through a series of steps to get up and running and get some idea of how the framework operates.\nIf you use other technologies with which you are not familiar, you should do some research and try to make sure you can use them in isolation before combining them in a complex system.\n\n[[appendix-faq-common-problems]]\n== Common Problems\n\nThis section addresses the most common problems that people encounter when using Spring Security:\n\n* Authentication\n** <<appendix-faq-bad-credentials>>\n** <<appendix-faq-login-loop>>\n** <<appendix-faq-anon-access-denied>>\n** <<appendix-faq-cached-secure-page>>\n** <<auth-exception-credentials-not-found>>\n** <<appendix-faq-ldap-authentication>>\n* Session Management\n** <<appendix-faq-concurrent-session-same-browser>>\n** <<appendix-faq-new-session-on-authentication>>\n** <<appendix-faq-tomcat-https-session>>\n** <<appendix-faq-session-listener-missing>>\n** <<appendix-faq-unwanted-session-creation>>\n* Miscellaneous\n** <<appendix-faq-forbidden-csrf>>\n** <<appendix-faq-no-security-on-forward>>\n** <<appendix-faq-method-security-in-web-context>>\n** <<appendix-faq-no-filters-no-context>>\n** <<appendix-faq-method-security-with-taglib>>\n\n[[appendix-faq-bad-credentials]]\n=== When I try to log in, I get an error message that says, \"`Bad Credentials`\". What is wrong?\n\nThis means that authentication has failed.\nIt does not say why, as it is good practice to avoid giving details that might help an attacker guess account names or passwords.\n\nThis also means that, if you ask this question online, you should not expect an answer unless you provide additional information.\nAs with any issue, you should check the output from the debug log and note any exception stacktraces and related messages.\nYou should step through the code in a debugger to see where the authentication fails and why.\nYou should also write a test case which exercises your authentication configuration outside the application.\nIf you use hashed passwords, make sure the value stored in your database is _exactly_ the same as the value produced by the `PasswordEncoder` configured in your application.\n\n\n[[appendix-faq-login-loop]]\n=== My application goes into an \"`endless loop`\" when I try to log in. What is going on?\n\nA common user problem with infinite loop and redirecting to the login page is caused by accidentally configuring the login page as a \"`secured`\" resource.\nMake sure your configuration allows anonymous access to the login page.\nYou can do so with the xref:servlet/authorization/authorize-http-requests.adoc[`authorizeHttpRequests`] DSL.\n\n[TIP]\n====\nWhen you use namespace- or DSL-based configuration, a check is made on loading the application context and a warning message logged if your login page appears to be protected.\n====\n\n[[appendix-faq-anon-access-denied]]\n=== I get an exception with the message \"Access is denied (user is anonymous);\". What's wrong?\n\nThis is a debug level message which occurs the first time an anonymous user attempts to access a protected resource.\n\n[source]\n----\nDEBUG [ExceptionTranslationFilter] - Access is denied (user is anonymous); redirecting to authentication entry point\norg.springframework.security.AccessDeniedException: Access is denied\nat org.springframework.security.vote.AffirmativeBased.decide(AffirmativeBased.java:68)\nat org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:262)\n----\n\nIt is normal and shouldn't be anything to worry about.\n\n\n[[appendix-faq-cached-secure-page]]\n=== Why can I still see a secured page even after I have logged out of my application?\n\nThe most common reason for this is that your browser has cached the page, and you are seeing a copy that is being retrieved from the browsers cache.\nVerify this by checking whether the browser is actually sending the request (check your server access logs and the debug log or use a suitable browser debugging plugin, such as \"`Tamper Data`\" for Firefox). This has nothing to do with Spring Security, and you should configure your application or server to set the appropriate `Cache-Control` response headers.\nNote that SSL requests are never cached.\n\n\n[[auth-exception-credentials-not-found]]\n=== I get an exception with a message of \"An Authentication object was not found in the SecurityContext\". What is wrong?\n\nThe following listing shows another debug-level message that occurs the first time an anonymous user attempts to access a protected resource. However, this listing shows what happens when you do not have an `AnonymousAuthenticationFilter` in your filter chain configuration:\n\n[source]\n----\nDEBUG [ExceptionTranslationFilter] - Authentication exception occurred; redirecting to authentication entry point\norg.springframework.security.AuthenticationCredentialsNotFoundException:\n\t\t\t\t\t\t\tAn Authentication object was not found in the SecurityContext\nat org.springframework.security.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:342)\nat org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:254)\n----\n\nIt is normal and is not something to worry about.\n\n\n[[appendix-faq-ldap-authentication]]\n=== I can't get LDAP authentication to work. What's wrong with my configuration?\n\nNote that the permissions for an LDAP directory often do not let you read the password for a user.\nHence, it is often not possible to use the <<appendix-faq-what-is-userdetailservice>> where Spring Security compares the stored password with the one submitted by the user.\nThe most common approach is to use LDAP \"`bind`\", which is one of the operations supported by https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol[the LDAP protocol]. With this approach, Spring Security validates the password by trying to authenticate to the directory as the user.\n\nThe most common problem with LDAP authentication is a lack of knowledge of the directory server tree structure and configuration.\nThis differs from one company to another, so you have to find it out yourself.\nBefore adding a Spring Security LDAP configuration to an application, you should write a simple test by using standard Java LDAP code (without Spring Security involved) and make sure you can get that to work first.\nFor example, to authenticate a user, you could use the following code:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n\n@Test\npublic void ldapAuthenticationIsSuccessful() throws Exception {\n\t\tHashtable<String,String> env = new Hashtable<String,String>();\n\t\tenv.put(Context.SECURITY_AUTHENTICATION, \"simple\");\n\t\tenv.put(Context.SECURITY_PRINCIPAL, \"cn=joe,ou=users,dc=mycompany,dc=com\");\n\t\tenv.put(Context.PROVIDER_URL, \"ldap://mycompany.com:389/dc=mycompany,dc=com\");\n\t\tenv.put(Context.SECURITY_CREDENTIALS, \"joespassword\");\n\t\tenv.put(Context.INITIAL_CONTEXT_FACTORY, \"com.sun.jndi.ldap.LdapCtxFactory\");\n\n\t\tInitialLdapContext ctx = new InitialLdapContext(env, null);\n\n}\n\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Test\nfun ldapAuthenticationIsSuccessful() {\n    val env = Hashtable<String, String>()\n    env[Context.SECURITY_AUTHENTICATION] = \"simple\"\n    env[Context.SECURITY_PRINCIPAL] = \"cn=joe,ou=users,dc=mycompany,dc=com\"\n    env[Context.PROVIDER_URL] = \"ldap://mycompany.com:389/dc=mycompany,dc=com\"\n    env[Context.SECURITY_CREDENTIALS] = \"joespassword\"\n    env[Context.INITIAL_CONTEXT_FACTORY] = \"com.sun.jndi.ldap.LdapCtxFactory\"\n    val ctx = InitialLdapContext(env, null)\n}\n----\n======\n\n=== Session Management\n\nSession management issues are a common source of questions.\nIf you are developing Java web applications, you should understand how the session is maintained between the servlet container and the user's browser.\nYou should also understand the difference between secure and non-secure cookies and the implications of using HTTP and HTTPS and switching between the two.\nSpring Security has nothing to do with maintaining the session or providing session identifiers.\nThis is entirely handled by the servlet container.\n\n\n[[appendix-faq-concurrent-session-same-browser]]\n=== I am using Spring Security's concurrent session control to prevent users from logging in more than once at the same time. When I open another browser window after logging in, it does not stop me from logging in again. Why can I log in more than once?\n\nBrowsers generally maintain a single session per browser instance.\nYou cannot have two separate sessions at once.\nSo if you log in again in another window or tab you are just reauthenticating in the same session.\nSo, if you log in again in another window or tab, you are reauthenticating in the same session.\nThe server does not know anything about tabs, windows, or browser instances.\nAll it sees are HTTP requests, and it ties those to a particular session according to the value of the `JSESSIONID` cookie that they contain.\nWhen a user authenticates during a session, Spring Security's concurrent session control checks the number of _other authenticated sessions_ that they have.\nIf they are already authenticated with the same session, re-authenticating has no effect.\n\n\n[[appendix-faq-new-session-on-authentication]]\n=== Why does the session ID change when I authenticate through Spring Security?\n\nWith the default configuration, Spring Security changes the session ID when the user authenticates.\nIf you use a Servlet 3.1 or newer container, the session ID is simply changed.\nIf you use an older container, Spring Security invalidates the existing session, creates a new session, and transfers the session data to the new session.\nChanging the session identifier in this manner prevents \"`session-fixation`\" attacks.\nYou can find more about this online and in the reference manual.\n\n\n[[appendix-faq-tomcat-https-session]]\n=== I use Tomcat (or some other servlet container) and have enabled HTTPS for my login page, switching back to HTTP afterward. It does not work. I end up back at the login page after authenticating.\nIt doesn't work - I just end up back at the login page after authenticating.\n\nThis happens because sessions created under HTTPS, for which the session cookie is marked as \"`secure`\", cannot subsequently be used under HTTP. The browser does not send the cookie back to the server, and any session state (including the security context information) is lost. Starting a session in HTTP first should work, as the session cookie is not marked as secure.\nHowever, Spring Security's xref:servlet/authentication/session-management.adoc#ns-session-fixation[Session Fixation Protection] can interfere with this because it results in a new session ID cookie being sent back to the user's browser, usually with the secure flag.\nTo get around this, you can disable session fixation protection. However, in newer Servlet containers, you can also configure session cookies to never use the secure flag.\n\n\n[IMPORTANT]\n====\nSwitching between HTTP and HTTPS is not a good idea in general, as any application that uses HTTP at all is vulnerable to man-in-the-middle attacks.\nTo be truly secure, the user should begin accessing your site in HTTPS and continue using it until they log out.\nEven clicking on an HTTPS link from a page accessed over HTTP is potentially risky.\nIf you need more convincing, check out a tool like https://github.com/moxie0/sslstrip/[sslstrip].\n====\n\n\n===  I am not switching between HTTP and HTTPS, but my session is still lost. What happened?\n\nSessions are maintained either by exchanging a session cookie or by adding a `jsessionid` parameter to URLs (this happens automatically if you use JSTL to output URLs or if you call `HttpServletResponse.encodeUrl` on URLs (before a redirect, for example). If clients have cookies disabled, and you are not rewriting URLs to include the `jsessionid`, the session is lost.\nNote that the use of cookies is preferred for security reasons, as it does not expose the session information in the URL.\n\n[[appendix-faq-session-listener-missing]]\n===  I am trying to use the concurrent session-control support, but it does not let me log back in, even if I am sure I have logged out and have not exceeded the allowed sessions. What is wrong?\n\nMake sure you have added the listener to your `web.xml` file.\nIt is essential to make sure that the Spring Security session registry is notified when a session is destroyed.\nWithout it, the session information is not removed from the registry.\nThe following example adds a listener in a `web.xml` file:\n\n[source,xml]\n----\n<listener>\n\t\t<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>\n</listener>\n----\n\n[[appendix-faq-unwanted-session-creation]]\n=== Spring Security creates a session somewhere, even though I have configured it not to, by setting the create-session attribute to never. What is wrong?\n\nThis usually means that the user's application is creating a session somewhere but that they are not aware of it.\nThe most common culprit is a JSP. Many people are not aware that JSPs create sessions by default.\nTo prevent a JSP from creating a session, add the `<%@ page session=\"false\" %>` directive to the top of the page.\n\nIf you have trouble working out where a session is being created, you can add some debugging code to track down the location(s). One way to do this is to add a `javax.servlet.http.HttpSessionListener`, which calls `Thread.dumpStack()` in the `sessionCreated` method, to your application.\n\n[[appendix-faq-forbidden-csrf]]\n=== I get a 403 Forbidden when performing a POST. What is wrong?\n\nIf an HTTP 403 Forbidden error is returned for HTTP POST, but it works for HTTP GET, the issue is most likely related to xref:features/exploits/csrf.adoc#csrf[CSRF]. Either provide the CSRF Token or disable CSRF protection (the latter is not recommended).\n\n[[appendix-faq-no-security-on-forward]]\n=== I am forwarding a request to another URL by using the RequestDispatcher, but my security constraints are not being applied.\n\nBy default, filters are not applied to forwards or includes.\nIf you really want the security filters to be applied to forwards or includes, you have to configure these explicitly in your `web.xml` file by using the `<dispatcher>` element, which is a child element of the `<filter-mapping>` element.\n\n\n[[appendix-faq-method-security-in-web-context]]\n=== I have added Spring Security's <global-method-security> element to my application context, but, if I add security annotations to my Spring MVC controller beans (Struts actions etc.), they do not seem to have an effect. Why not?\n\nIn a Spring web application, the application context that holds the Spring MVC beans for the dispatcher servlet is often separate from the main application context.\nIt is often defined in a file called `myapp-servlet.xml`, where `myapp` is the name assigned to the Spring `DispatcherServlet` in the `web.xml` file. An application can have multiple `DispatcherServlet` instances, each with its own isolated application context.\nThe beans in these \"`child`\" contexts are not visible to the rest of the application.\nThe \"`parent`\" application context is loaded by the `ContextLoaderListener` you define in your `web.xml` file and is visible to all the child contexts.\nThis parent context is usually where you define your security configuration, including the `<global-method-security>` element. As a result, any security constraints applied to methods in these web beans are not enforced, since the beans cannot be seen from the `DispatcherServlet` context.\nYou need to either move the `<global-method-security>` declaration to the web context or move the beans you want secured into the main application context.\n\nGenerally, we recommend applying method security at the service layer rather than on individual web controllers.\n\n\n[[appendix-faq-no-filters-no-context]]\n=== I have a user who has definitely been authenticated, but, when I try to access the SecurityContextHolder during some requests, the Authentication is null. Why can I not see the user information?\nWhy can't I see the user information?\n\nIf you have excluded the request from the security filter chain by using the `filters='none'` attribute in the `<intercept-url>` element that matches the URL pattern, the `SecurityContextHolder` is not populated for that request.\nCheck the debug log to see whether the request is passing through the filter chain.\n(You are reading the debug log, right?)\n\n[[appendix-faq-method-security-with-taglib]]\n=== The authorize JSP Tag does not respect my method security annotations when using the URL attribute. Why not?\n\nMethod security does not hide links when using the `url` attribute in `<sec:authorize>`, because we cannot readily reverse engineer what URL is mapped to what controller endpoint. We are limited because controllers can rely on headers, the current user, and other details to determine what method to invoke.\n\n[[appendix-faq-architecture]]\n== Spring Security Architecture Questions\n\nThis section addresses common Spring Security architecture questions:\n\n. <<appendix-faq-where-is-class-x>>\n. <<appendix-faq-namespace-to-bean-mapping>>\n. <<appendix-faq-role-prefix>>\n. <<appendix-faq-what-dependencies>>\n. <<appendix-faq-unboundid-deps>>\n. <<appendix-faq-what-is-userdetailservice>>\n\n\n[[appendix-faq-where-is-class-x]]\n=== How do I know which package class X is in?\n\nThe best way of locating classes is by installing the Spring Security source in your IDE. The distribution includes source jars for each of the modules the project is divided up into.\nAdd these to your project source path, and then you can navigate directly to Spring Security classes (`Ctrl-Shift-T` in Eclipse). This also makes debugging easier and lets you troubleshoot exceptions by looking directly at the code where they occur to see what is going on there.\n\n[[appendix-faq-namespace-to-bean-mapping]]\n=== How do the namespace elements map to conventional bean configurations?\n\nThere is a general overview of what beans are created by the namespace in the namespace appendix of the reference guide.\nThere is also a detailed blog article called \"Behind the Spring Security Namespace\" on https://spring.io/blog/2010/03/06/behind-the-spring-security-namespace/[blog.springsource.com]. If you want to know the full details, then the code is in the `spring-security-config` module within the Spring Security 3.0 distribution.\nYou should probably read the chapters on namespace parsing in the standard Spring Framework reference documentation first.\n\n\n[[appendix-faq-role-prefix]]\n=== What does \"ROLE_\" mean?\n\n`ROLE_` is a way to identify the nature of a given authority.\nAn authority prefixed by `ROLE_` means that this authority is a role, likely derived from an RBAC authorization model.\n\nHaving a prefix allows for clear differentiation from OAuth 2.0 scopes (which use `SCOPE_`) and authorities granted from other sources as well.\n\nYou may choose to not prefix your authorities.\nModern Spring Security authorization components either allow you to supply the entire authority name, rendering the prefix unnecessary.\nAn example of this is how xref:servlet/authorization/authorize-http-requests.adoc[`authorizeHttpRequests`] and xref:servlet/authorization/method-security.adoc[`@PreAuthorize`] allow you to call `hasAuthority` or `hasRole`.\n\n[[appendix-faq-what-dependencies]]\n=== How do I know which dependencies to add to my application to work with Spring Security?\n\nIt depends on what features you are using and what type of application you are developing.\nWith Spring Security 3.0, the project jars are divided into clearly distinct areas of functionality, so it is straightforward to work out which Spring Security jars you need from your application requirements.\nAll applications need the `spring-security-core` jar.\nIf you are developing a web application, you need the `spring-security-web` jar.\nIf you are using security namespace configuration, you need the `spring-security-config` jar. For LDAP support, you need the `spring-security-ldap` jar. And so on.\n\nFor third-party jars, the situation is not always quite so obvious.\nA good starting point is to copy those from one of the pre-built sample applications `WEB-INF/lib` directories.\nFor a basic application, you can start with the tutorial sample.\nFor a basic application, you can start with the tutorial sample.\nIf you want to use LDAP with an embedded test server, use the LDAP sample as a starting point.\nThe reference manual also includes <<appendix-namespace,an appendix>> that lists the first-level dependencies for each Spring Security module, with some information on whether they are optional and when they are required.\n\nIf you build your project with Maven, adding the appropriate Spring Security modules as dependencies to your `pom.xml` file automatically pulls in the core jars that the framework requires.\nAny that are marked as \"`optional`\" in the Spring Security `pom.xml` files have to be added to your own `pom.xml` file if you need them.\n\n[[appendix-faq-unboundid-deps]]\n=== What dependencies are needed to run an embedded UnboundID LDAP server?\n\nYou need to add the following dependency to your project:\n\n[tabs]\n======\nMaven::\n+\n[source,maven,role=\"primary\"]\n----\n<dependency>\n\t\t<groupId>com.unboundid</groupId>\n\t\t<artifactId>unboundid-ldapsdk</artifactId>\n\t\t<version>7.0.1</version>\n\t\t<scope>runtime</scope>\n</dependency>\n----\n\nGradle::\n+\n[source,gradle,role=\"secondary\"]\n----\nimplementation 'com.unboundid:unboundid-ldapsdk:7.0.1'\n----\n======\n\n\n[[appendix-faq-apacheds-deps]]\n=== What dependencies are needed to run an embedded ApacheDS LDAP server?\n\nSpring Security 7 removes support for Apache DS.\nPlease use <<appendix-faq-unboundid-deps,UnboundID>> instead.\n\n[[appendix-faq-what-is-userdetailservice]]\n=== What is a UserDetailsService and do I need one?\n\n`UserDetailsService` is a DAO interface for loading data that is specific to a user account.\nIt has no function other than to load that data for use by other components within the framework.\nIt is not responsible for authenticating the user.\nAuthenticating a user with a username and password combination is most commonly performed by the `DaoAuthenticationProvider`, which is injected with a `UserDetailsService` to let it load the password (and other data) for a user, to compare it with the submitted value.\nNote that, if you use LDAP, <<appendix-faq-ldap-authentication,this approach may not work>>.\n\nIf you want to customize the authentication process, you should implement `AuthenticationProvider` yourself.\nSee this https://spring.io/blog/2010/08/02/spring-security-in-google-app-engine/[ blog article] for an example that integrate Spring Security authentication with Google App Engine.\n\n[[appendix-faq-howto]]\n== Common How-to Questions\n\nThis section addresses common how-to questions about Spring Security:\n\n. <<appendix-faq-extra-login-fields>>\n. <<appendix-faq-matching-url-fragments>>\n. <<appendix-faq-request-details-in-user-service>>\n. <<appendix-faq-access-session-from-user-service>>\n. <<appendix-faq-password-in-user-service>>\n. <<appendix-faq-dynamic-url-metadata>>\n. <<appendix-faq-ldap-authorities>>\n. <<appendix-faq-namespace-post-processor>>\n\n\n[[appendix-faq-extra-login-fields]]\n=== I need to log in with more information than just the username. How do I add support for extra login fields (such as a company name)?\n\nThis question comes up repeatedly, so you can find more information by searching online.\n\nThe submitted login information is processed by an instance of `UsernamePasswordAuthenticationFilter`. You need to customize this class to handle the extra data fields. One option is to use your own customized authentication token class (rather than the standard `UsernamePasswordAuthenticationToken`). Another option is to concatenate the extra fields with the username (for example, by using a `:` character as the separator) and pass them in the username property of `UsernamePasswordAuthenticationToken`.\n\nYou also need to customize the actual authentication process.\nIf you use a custom authentication token class, for example, you will have to write an `AuthenticationProvider` (or extend the standard `DaoAuthenticationProvider`) to handle it. If you have concatenated the fields, you can implement your own `UserDetailsService` to split them up and load the appropriate user data for authentication.\n\n[[appendix-faq-matching-url-fragments]]\n=== How do I apply different intercept-url constraints where only the fragment value of the requested URLs differs (such as /thing1#thing2 and /thing1#thing3)?\n\nYou cannot do this, since the fragment is not transmitted from the browser to the server.\nFrom the server's perspective, the URLs are identical.\nThis is a common question from GWT users.\n\n[[appendix-faq-request-details-in-user-service]]\n=== How do I access the user's IP Address (or other web-request data) in a UserDetailsService?\n\nYou cannot (without resorting to something like thread-local variables), since the only information supplied to the interface is the username.\nInstead of implementing `UserDetailsService`, you should implement `AuthenticationProvider` directly and extract the information from the supplied `Authentication` token.\n\nIn a standard web setup, the `getDetails()` method on the `Authentication` object will return an instance of `WebAuthenticationDetails`. If you need additional information, you can inject a custom `AuthenticationDetailsSource` into the authentication filter you are using.\nIf you are using the namespace, for example with the `<form-login>` element, then you should remove this element and replace it with a `<custom-filter>` declaration pointing to an explicitly configured `UsernamePasswordAuthenticationFilter`.\n\n\n[[appendix-faq-access-session-from-user-service]]\n=== How do I access the HttpSession from a UserDetailsService?\n\nYou cannot, since the `UserDetailsService` has no awareness of the servlet API. If you want to store custom user data, you should customize the `UserDetails` object that is returned.\nThis can then be accessed at any point, through the thread-local `SecurityContextHolder`. A call to `SecurityContextHolder.getContext().getAuthentication().getPrincipal()` returns this custom object.\n\nIf you really need to access the session, you must do so by customizing the web tier.\n\n\n[[appendix-faq-password-in-user-service]]\n=== How do I access the user's password in a UserDetailsService?\n\nYou cannot (and should not, even if you find a way to do so). You are probably misunderstanding its purpose.\nSee \"<<appendix-faq-what-is-userdetailservice,What is a UserDetailsService?>>\", earlier in the FAQ.\n\n\n[[appendix-faq-dynamic-url-metadata]]\n=== How do I dynamically define the secured URLs within an application?\n\nPeople often ask about how to store the mapping between secured URLs and security metadata attributes in a database rather than in the application context.\n\nThe first thing you should ask yourself is if you really need to do this.\nIf an application needs to be secure, it also requires that the security be tested thoroughly based on a defined policy.\nIt may require auditing and acceptance testing before being rolled out into a production environment.\nA security-conscious organization should be aware that the benefits of their diligent testing process could be wiped out instantly by letting the security settings be modified at runtime by changing a row or two in a configuration database.\nIf you have taken this into account (perhaps by using multiple layers of security within your application), Spring Security lets you fully customize the source of security metadata.\nYou can make it fully dynamic if you choose.\n\nBoth method and web security are protected by implementations of `AuthorizationManager`.\nFor web security, you can supply your own implementation of `AuthorizationManager<RequestAuthorizationContext>` and supply it to the filter chain DSL like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class DynamicAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {\n\tprivate final MyExternalAuthorizationService authz;\n\n\t// ...\n\n    @Override\n    public AuthorizationResult authorize(Supplier<Authentication> authentication, RequestAuthorizationContext context) {\n\t\t// query the external service\n    }\n}\n\n// ...\n\nhttp\n    .authorizeHttpRequests((authorize) -> authorize.anyRequest().access(dynamicAuthorizationManager))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass DynamicAuthorizationManager : AuthorizationManager<RequestAuthorizationContext?> {\n    private val rules: MyAuthorizationRulesRepository? = null\n\n    // ...\n\n    override fun authorize(authentication: Supplier<Authentication?>?, context: RequestAuthorizationContext?): AuthorizationResult {\n\t\t// look up rules from the database\n    }\n}\n\n// ...\n\nhttp {\n    authorizeHttpRequests {\n        authorize(anyRequest, dynamicAuthorizationManager)\n    }\n}\n----\n======\n\nFor method security, you can supply your own implementation of `AuthorizationManager<MethodInvocation>` and supply it to Spring AOP like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class DynamicAuthorizationManager implements AuthorizationManager<MethodInvocation> {\n\tprivate final MyExternalAuthorizationService authz;\n\n\t// ...\n\n    @Override\n    public AuthorizationResult authorize(Supplier<Authentication> authentication, MethodInvocation invocation) {\n\t\t// query the external service\n    }\n}\n\n// ...\n\n@Bean\nstatic Advisor securedAuthorizationAdvisor(DynamicAuthorizationManager dynamicAuthorizationManager) {\n    return AuthorizationManagerBeforeMethodInterceptor.secured(dynamicAuthorizationManager)\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass DynamicAuthorizationManager : AuthorizationManager<MethodInvocation?> {\n    private val authz: MyExternalAuthorizationService? = null\n\n     // ...\n    override fun authorize(authentication: Supplier<Authentication?>?, invocation: MethodInvocation?): AuthorizationResult {\n\t\t// query the external service\n    }\n}\n\ncompanion object {\n    @Bean\n    fun securedAuthorizationAdvisor(dynamicAuthorizationManager: DynamicAuthorizationManager): Advisor {\n        return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(dynamicAuthorizationManager)\n    }\n}\n----\n======\n\n\n[[appendix-faq-ldap-authorities]]\n=== How do I authenticate against LDAP but load user roles from a database?\n\nThe `LdapAuthenticationProvider` bean (which handles normal LDAP authentication in Spring Security) is configured with two separate strategy interfaces, one that performs the authentication and one that loads the user authorities, called `LdapAuthenticator` and `LdapAuthoritiesPopulator`, respectively.\nThe `DefaultLdapAuthoritiesPopulator` loads the user authorities from the LDAP directory and has various configuration parameters to let you specify how these should be retrieved.\n\nTo use JDBC instead, you can implement the interface yourself, by using whatever SQL is appropriate for your schema:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n\npublic class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {\n    @Autowired\n    JdbcTemplate template;\n\n    List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {\n        return template.query(\"select role from roles where username = ?\",\n                new String[] {username},\n                new RowMapper<GrantedAuthority>() {\n             /**\n             *  We're assuming here that you're using the standard convention of using the role\n             *  prefix \"ROLE_\" to mark attributes which are supported by Spring Security's RoleVoter.\n             */\n            @Override\n            public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {\n                return new SimpleGrantedAuthority(\"ROLE_\" + rs.getString(1));\n            }\n        });\n    }\n}\n\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass MyAuthoritiesPopulator : LdapAuthoritiesPopulator {\n    @Autowired\n    lateinit var template: JdbcTemplate\n\n    override fun getGrantedAuthorities(userData: DirContextOperations, username: String): MutableList<GrantedAuthority?> {\n        return template.query(\"select role from roles where username = ?\",\n            arrayOf(username)\n        ) { rs, _ ->\n            /**\n             * We're assuming here that you're using the standard convention of using the role\n             * prefix \"ROLE_\" to mark attributes which are supported by Spring Security's RoleVoter.\n             */\n            SimpleGrantedAuthority(\"ROLE_\" + rs.getString(1))\n        }\n    }\n}\n----\n======\n\nYou would then add a bean of this type to your application context and inject it into the `LdapAuthenticationProvider`. This is covered in the section on configuring LDAP by using explicit Spring beans in the LDAP chapter of the reference manual.\nNote that you cannot use the namespace for configuration in this case.\nYou should also consult the {security-api-url}[Javadoc] for the relevant classes and interfaces.\n\n\n[[appendix-faq-namespace-post-processor]]\n=== I want to modify the property of a bean that is created by the namespace, but there is nothing in the schema to support it. What can I do short of abandoning namespace use?\n\nThe namespace functionality is intentionally limited, so it does not cover everything that you can do with plain beans.\nIf you want to do something simple, such as modifying a bean or injecting a different dependency, you can do so by adding a `BeanPostProcessor` to your configuration.\nYou can find more information in the {spring-framework-reference-url}core/beans/factory-extension.html#beans-factory-extension-bpp[Spring Reference Manual]. To do so, you need to know a bit about which beans are created, so you should also read the blog article mentioned in the earlier question on <<appendix-faq-namespace-to-bean-mapping,how the namespace maps to Spring beans>>.\n\nNormally, you would add the functionality you require to the `postProcessBeforeInitialization` method of `BeanPostProcessor`. Suppose that you want to customize the `AuthenticationDetailsSource` used by the `UsernamePasswordAuthenticationFilter` (created by the `form-login` element). You want to extract a particular header called `CUSTOM_HEADER` from the request and use it while authenticating the user.\nThe processor class would look like the following listing:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class CustomBeanPostProcessor implements BeanPostProcessor {\n\n\t\tpublic Object postProcessAfterInitialization(Object bean, String name) {\n\t\t\t\tif (bean instanceof UsernamePasswordAuthenticationFilter) {\n\t\t\t\t\t\tSystem.out.println(\"********* Post-processing \" + name);\n\t\t\t\t\t\t((UsernamePasswordAuthenticationFilter)bean).setAuthenticationDetailsSource(\n\t\t\t\t\t\t\t\t\t\tnew AuthenticationDetailsSource() {\n\t\t\t\t\t\t\t\t\t\t\t\tpublic Object buildDetails(Object context) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn ((HttpServletRequest)context).getHeader(\"CUSTOM_HEADER\");\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn bean;\n\t\t}\n\n\t\tpublic Object postProcessBeforeInitialization(Object bean, String name) {\n\t\t\t\treturn bean;\n\t\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass CustomBeanPostProcessor : BeanPostProcessor {\n    override fun postProcessAfterInitialization(bean: Any, name: String): Any {\n        if (bean is UsernamePasswordAuthenticationFilter) {\n            println(\"********* Post-processing $name\")\n            bean.setAuthenticationDetailsSource(\n                AuthenticationDetailsSource<HttpServletRequest, Any?> { context -> context.getHeader(\"CUSTOM_HEADER\") })\n        }\n        return bean\n    }\n\n    override fun postProcessBeforeInitialization(bean: Any, name: String?): Any {\n        return bean\n    }\n}\n----\n======\n\nYou would then register this bean in your application context.\nSpring automatically invoke it on the beans defined in the application context.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/index.adoc",
    "content": "= Appendix\n\nThis is an appendix for Servlet based Spring Security.\nIt has the following sections:\n\n* xref:servlet/appendix/database-schema.adoc[Database Schemas]\n* xref:servlet/appendix/namespace/index.adoc[XML Namespace]\n* xref:servlet/appendix/faq.adoc[FAQ]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/namespace/authentication-manager.adoc",
    "content": "[[nsa-authentication]]\n= Authentication Services\nBefore Spring Security 3.0, an `AuthenticationManager` was automatically registered internally.\nNow you must register one explicitly using the `<authentication-manager>` element.\nThis creates an instance of Spring Security's `ProviderManager` class, which needs to be configured with a list of one or more `AuthenticationProvider` instances.\nThese can either be created using syntax elements provided by the namespace, or they can be standard bean definitions, marked for addition to the list using the `authentication-provider` element.\n\n\n[[nsa-authentication-manager]]\n== <authentication-manager>\nEvery Spring Security application which uses the namespace must have include this element somewhere.\nIt is responsible for registering the `AuthenticationManager` which provides authentication services to the application.\nAll elements which create `AuthenticationProvider` instances should be children of this element.\n\n\n[[nsa-authentication-manager-attributes]]\n=== <authentication-manager> Attributes\n\n\n[[nsa-authentication-manager-alias]]\n* **alias**\nThis attribute allows you to define an alias name for the internal instance for use in your own configuration.\n\n\n[[nsa-authentication-manager-erase-credentials]]\n* **erase-credentials**\nIf set to true, the AuthenticationManager will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.\nLiterally it maps to the `eraseCredentialsAfterAuthentication` property of the xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`ProviderManager`].\n\n[[nsa-authentication-manager-observation-registry-ref]]\n* **observation-registry-ref**\nA reference to the `ObservationRegistry` used for the `FilterChain` and related components\n\n[[nsa-authentication-manager-id]]\n* **id**\nThis attribute allows you to define an id for the internal instance for use in your own configuration.\nIt is the same as the alias element, but provides a more consistent experience with elements that use the id attribute.\n\n\n[[nsa-authentication-manager-children]]\n=== Child Elements of <authentication-manager>\n\n\n* <<nsa-authentication-provider,authentication-provider>>\n* xref:servlet/appendix/namespace/ldap.adoc#nsa-ldap-authentication-provider[ldap-authentication-provider]\n\n\n\n[[nsa-authentication-provider]]\n== <authentication-provider>\nUnless used with a `ref` attribute, this element is shorthand for configuring a `DaoAuthenticationProvider`.\n`DaoAuthenticationProvider` loads user information from a `UserDetailsService` and compares the username/password combination with the values supplied at login.\nThe `UserDetailsService` instance can be defined either by using an available namespace element (`jdbc-user-service` or by using the `user-service-ref` attribute to point to a bean defined elsewhere in the application context).\n\n\n\n[[nsa-authentication-provider-parents]]\n=== Parent Elements of <authentication-provider>\n\n\n* <<nsa-authentication-manager,authentication-manager>>\n\n\n\n[[nsa-authentication-provider-attributes]]\n=== <authentication-provider> Attributes\n\n\n[[nsa-authentication-provider-ref]]\n* **ref**\nDefines a reference to a Spring bean that implements `AuthenticationProvider`.\n\nIf you have written your own `AuthenticationProvider` implementation (or want to configure one of Spring Security's own implementations as a traditional bean for some reason, then you can use the following syntax to add it to the internal list of `ProviderManager`:\n\n[source,xml]\n----\n\n<security:authentication-manager>\n  <security:authentication-provider ref=\"myAuthenticationProvider\" />\n</security:authentication-manager>\n<bean id=\"myAuthenticationProvider\" class=\"com.something.MyAuthenticationProvider\"/>\n\n----\n\n\n\n\n[[nsa-authentication-provider-user-service-ref]]\n* **user-service-ref**\nA reference to a bean that implements UserDetailsService that may be created using the standard bean element or the custom user-service element.\n\n\n[[nsa-authentication-provider-children]]\n=== Child Elements of <authentication-provider>\n\n\n* <<nsa-jdbc-user-service,jdbc-user-service>>\n* xref:servlet/appendix/namespace/ldap.adoc#nsa-ldap-user-service[ldap-user-service]\n* <<nsa-password-encoder,password-encoder>>\n* <<nsa-user-service,user-service>>\n\n\n\n[[nsa-jdbc-user-service]]\n== <jdbc-user-service>\nCauses creation of a JDBC-based UserDetailsService.\n\n\n[[nsa-jdbc-user-service-attributes]]\n=== <jdbc-user-service> Attributes\n\n\n[[nsa-jdbc-user-service-authorities-by-username-query]]\n* **authorities-by-username-query**\nAn SQL statement to query for a user's granted authorities given a username.\n\nThe default is\n\n[source]\n----\nselect username, authority from authorities where username = ?\n----\n\n\n\n\n[[nsa-jdbc-user-service-cache-ref]]\n* **cache-ref**\nDefines a reference to a cache for use with a UserDetailsService.\n\n\n[[nsa-jdbc-user-service-data-source-ref]]\n* **data-source-ref**\nThe bean ID of the DataSource which provides the required tables.\n\n\n[[nsa-jdbc-user-service-group-authorities-by-username-query]]\n* **group-authorities-by-username-query**\nAn SQL statement to query user's group authorities given a username.\nThe default is\n\n+\n\n[source]\n----\nselect\ng.id, g.group_name, ga.authority\nfrom\ngroups g, group_members gm, group_authorities ga\nwhere\ngm.username = ? and g.id = ga.group_id and g.id = gm.group_id\n----\n\n\n\n\n[[nsa-jdbc-user-service-id]]\n* **id**\nA bean identifier, used for referring to the bean elsewhere in the context.\n\n\n[[nsa-jdbc-user-service-role-prefix]]\n* **role-prefix**\nA non-empty string prefix that will be added to role strings loaded from persistent storage (default is \"ROLE_\").\nUse the value \"none\" for no prefix in cases where the default is non-empty.\n\n\n[[nsa-jdbc-user-service-users-by-username-query]]\n* **users-by-username-query**\nAn SQL statement to query a username, password, and enabled status given a username.\nThe default is\n\n+\n\n[source]\n----\nselect username, password, enabled from users where username = ?\n----\n\n\n\n\n[[nsa-password-encoder]]\n== <password-encoder>\nAuthentication providers can optionally be configured to use a password encoder as described in the xref:features/authentication/password-storage.adoc#authentication-password-storage[Password Storage].\nThis will result in the bean being injected with the appropriate `PasswordEncoder` instance.\n\n\n[[nsa-password-encoder-parents]]\n=== Parent Elements of <password-encoder>\n\n\n* <<nsa-authentication-provider,authentication-provider>>\n* xref:servlet/appendix/namespace/authentication-manager.adoc#nsa-password-compare[password-compare]\n\n\n\n[[nsa-password-encoder-attributes]]\n=== <password-encoder> Attributes\n\n\n[[nsa-password-encoder-hash]]\n* **hash**\nDefines the hashing algorithm used on user passwords.\nWe recommend strongly against using MD4, as it is a very weak hashing algorithm.\n\n\n[[nsa-password-encoder-ref]]\n* **ref**\nDefines a reference to a Spring bean that implements `PasswordEncoder`.\n\n\n[[nsa-user-service]]\n== <user-service>\nCreates an in-memory UserDetailsService from a properties file or a list of \"user\" child elements.\nUsernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.\n\n\n[[nsa-user-service-attributes]]\n=== <user-service> Attributes\n\n\n[[nsa-user-service-id]]\n* **id**\nA bean identifier, used for referring to the bean elsewhere in the context.\n\n\n[[nsa-user-service-properties]]\n* **properties**\nThe location of a Properties file where each line is in the format of\n\n+\n\n[source]\n----\nusername=password,grantedAuthority[,grantedAuthority][,enabled|disabled]\n----\n\n\n\n\n[[nsa-user-service-children]]\n=== Child Elements of <user-service>\n\n\n* <<nsa-user,user>>\n\n\n\n[[nsa-user]]\n== <user>\nRepresents a user in the application.\n\n\n[[nsa-user-parents]]\n=== Parent Elements of <user>\n\n\n* <<nsa-user-service,user-service>>\n\n\n\n[[nsa-user-attributes]]\n=== <user> Attributes\n\n\n[[nsa-user-authorities]]\n* **authorities**\nOne of more authorities granted to the user.\nSeparate authorities with a comma (but no space).\nFor example, \"ROLE_USER,ROLE_ADMINISTRATOR\"\n\n\n[[nsa-user-disabled]]\n* **disabled**\nCan be set to \"true\" to mark an account as disabled and unusable.\n\n\n[[nsa-user-locked]]\n* **locked**\nCan be set to \"true\" to mark an account as locked and unusable.\n\n\n[[nsa-user-name]]\n* **name**\nThe username assigned to the user.\n\n\n[[nsa-user-password]]\n* **password**\nThe password assigned to the user.\nThis may be hashed if the corresponding authentication provider supports hashing (remember to set the \"hash\" attribute of the \"user-service\" element).\nThis attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities.\nIf omitted, the namespace will generate a random value, preventing its accidental use for authentication.\nCannot be empty.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc",
    "content": "[[nsa-web]]\n= Web Application Security\n\n[[nsa-debug]]\n== <debug>\nEnables Spring Security debugging infrastructure.\nThis will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters.\nThis may include sensitive information, such as request parameters or headers, and should only be used in a development environment.\n\n[[nsa-http]]\n== <http>\nIf you use an `<http>` element within your application, a `FilterChainProxy` bean named \"springSecurityFilterChain\" is created and the configuration within the element is used to build a filter chain within\n`FilterChainProxy`.\nAs of Spring Security 3.1, additional `http` elements can be used to add extra filter chains footnote:[See the pass:specialcharacters,macros[xref:servlet/configuration/xml-namespace.adoc#ns-web-xml[introductory chapter]] for how to set up the mapping from your `web.xml` ].\nSome core filters are always created in a filter chain and others will be added to the stack depending on the attributes and child elements which are present.\nThe positions of the standard filters are fixed (see\nxref:servlet/configuration/xml-namespace.adoc#filter-stack[the filter order table] in the namespace introduction), removing a common source of errors with previous versions of the framework when users had to configure the filter chain explicitly in the\n`FilterChainProxy` bean.\nYou can, of course, still do this if you need full control of the configuration.\n\n\nAll filters which require a reference to the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] will be automatically injected with the internal instance created by the namespace configuration.\n\nEach `<http>` namespace block always creates an `SecurityContextPersistenceFilter`, an `ExceptionTranslationFilter` and a `FilterSecurityInterceptor`.\nThese are fixed and cannot be replaced with alternatives.\n\n\n[[nsa-http-attributes]]\n=== <http> Attributes\nThe attributes on the `<http>` element control some of the properties on the core filters.\n\n[[nsa-http-use-authorization-manager]]\n* **use-authorization-manager**\nUse AuthorizationManager API instead of SecurityMetadataSource (defaults to true)\n\n[[nsa-http-authorization-manager-ref]]\n* **authorization-manager-ref**\nUse this AuthorizationManager instead of deriving one from <intercept-url> elements\n\n[[nsa-http-access-decision-manager-ref]]\n* **access-decision-manager-ref**\nOptional attribute specifying the ID of the `AccessDecisionManager` implementation which should be used for authorizing HTTP requests.\nBy default an `AffirmativeBased` implementation is used for with a `RoleVoter` and an `AuthenticatedVoter`.\n\n\n[[nsa-http-authentication-manager-ref]]\n* **authentication-manager-ref**\nA reference to the `AuthenticationManager` used for the `FilterChain` created by this http element.\n\n[[nsa-http-observation-registry-ref]]\n* **observation-registry-ref**\nA reference to the `ObservationRegistry` used for the `FilterChain` and related components\n\n[[nsa-http-auto-config]]\n* **auto-config**\nAutomatically registers a login form, BASIC authentication, logout services.\nIf set to \"true\", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element).\nIf unspecified, defaults to \"false\".\nUse of this attribute is not recommended.\nUse explicit configuration elements instead to avoid confusion.\n\n\n[[nsa-http-create-session]]\n* **create-session**\nControls the eagerness with which an HTTP session is created by Spring Security classes.\nOptions include:\n\n** `always` - Spring Security will proactively create a session if one does not exist.\n** `ifRequired` - Spring Security will only create a session only if one is required (default value).\n** `never` - Spring Security will never create a session, but will make use of one if the application does.\n** `stateless` - Spring Security will not create a session and ignore the session for obtaining a Spring `Authentication`.\n\n[[nsa-http-disable-url-rewriting]]\n* **disable-url-rewriting**\nPrevents session IDs from being appended to URLs in the application.\nClients must use cookies if this attribute is set to `true`.\nThe default is `true`.\n\n\n[[nsa-http-entry-point-ref]]\n* **entry-point-ref**\nNormally the `AuthenticationEntryPoint` used will be set depending on which authentication mechanisms have been configured.\nThis attribute allows this behaviour to be overridden by defining a customized `AuthenticationEntryPoint` bean which will start the authentication process.\n\n\n[[nsa-http-jaas-api-provision]]\n* **jaas-api-provision**\nIf available, runs the request as the `Subject` acquired from the `JaasAuthenticationToken` which is implemented by adding a `JaasApiIntegrationFilter` bean to the stack.\nDefaults to `false`.\n\n\n[[nsa-http-name]]\n* **name**\nA bean identifier, used for referring to the bean elsewhere in the context.\n\n\n[[nsa-http-once-per-request]]\n* **once-per-request**\nCorresponds to the `observeOncePerRequest` property of `FilterSecurityInterceptor`.\nDefaults to `false`.\n\n\n[[nsa-http-filter-all-dispatcher-types]]\n* **filter-all-dispatcher-types**\nCorresponds to the `shouldFilterAllDispatcherTypes` property of the `AuthorizationFilter`. Does not work when `use-authorization-manager=false`.\nDefaults to `true`.\n\n\n[[nsa-http-pattern]]\n* **pattern**\nDefining a pattern for the <<nsa-http,http>> element controls the requests which will be filtered through the list of filters which it defines.\nThe interpretation is dependent on the configured <<nsa-http-request-matcher,request-matcher>>.\nIf no pattern is defined, all requests will be matched, so the most specific patterns should be declared first.\n\n\n[[nsa-http-realm]]\n* **realm**\nSets the realm name used for basic authentication (if enabled).\nCorresponds to the `realmName` property on `BasicAuthenticationEntryPoint`.\n\n[[nsa-http-redirect-to-https-request-matcher-ref]]\n* **redirect-to-https-request-matcher-ref**\nA reference to a bean that implements `RequestMatcher` that will determine which requests must redirect to HTTPS.\nThis is helpful when, for example, wanting to run HTTP locally and HTTPS in production using a request header.\n\n[[nsa-http-request-matcher]]\n* **request-matcher**\nDefines the `RequestMatcher` strategy used in the `FilterChainProxy` and the beans created by the `intercept-url` to match incoming requests.\nOptions are currently `mvc`, `ant`, `regex` and `ciRegex`, for Spring MVC, ant, regular-expression and case-insensitive regular-expression respectively.\nA separate instance is created for each <<nsa-intercept-url,intercept-url>> element using its <<nsa-intercept-url-pattern,pattern>>, <<nsa-intercept-url-method,method>> and <<nsa-intercept-url-servlet-path,servlet-path>> attributes.\nBy default, paths are matched using a `PathPatternRequestMatcher`; however, regular expressions are matched using a `RegexRequestMatcher`.\nSee the Javadoc for these classes for more details on exactly how the matching is performed.\nMVC is the default strategy if Spring MVC is present in the classpath, if not, Ant paths are used.\n\n\n[[nsa-http-request-matcher-ref]]\n* **request-matcher-ref**\nA reference to a bean that implements `RequestMatcher` that will determine if this `FilterChain` should be used.\nThis is a more powerful alternative to <<nsa-http-pattern,pattern>>.\n\n\n[[nsa-http-security]]\n* **security**\nA request pattern can be mapped to an empty filter chain, by setting this attribute to `none`.\nNo security will be applied and none of Spring Security's features will be available.\n\n[[nsa-http-security-context-holder-strategy-ref]]\n* **security-context-repository-ref**\nAllows injection of a custom `SecurityContextHolderStrategy` into `SecurityContextPersistenceFilter`, `SecurityContextHolderFilter`, `BasicAuthenticationFilter`, `UsernamePasswordAuthenticationFilter`, `ExceptionTranslationFilter`, `LogoutFilter`, and others.\n\n[[nsa-http-security-context-explicit-save]]\n* **security-context-explicit-save**\nIf true, use `SecurityContextHolderFilter` instead of `SecurityContextPersistenceFilter`.\nRequires explicit save\n\n\n[[nsa-http-security-context-repository-ref]]\n* **security-context-repository-ref**\nAllows injection of a custom `SecurityContextRepository` into the `SecurityContextPersistenceFilter`.\n\n\n[[nsa-http-servlet-api-provision]]\n* **servlet-api-provision**\nProvides versions of `HttpServletRequest` security methods such as `isUserInRole()` and `getPrincipal()` which are implemented by adding a `SecurityContextHolderAwareRequestFilter` bean to the stack.\nDefaults to `true`.\n\n\n[[nsa-http-use-expressions]]\n* **use-expressions**\nEnables EL-expressions in the `access` attribute, as described in the chapter on xref:servlet/authorization/authorize-http-requests.adoc#authorization-expressions[expression-based access-control].\nThe default value is true.\n\n\n[[nsa-http-children]]\n=== Child Elements of <http>\n* <<nsa-access-denied-handler,access-denied-handler>>\n* <<nsa-anonymous,anonymous>>\n* <<nsa-cors,cors>>\n* <<nsa-csrf,csrf>>\n* <<nsa-custom-filter,custom-filter>>\n* <<nsa-expression-handler,expression-handler>>\n* <<nsa-form-login,form-login>>\n* <<nsa-headers,headers>>\n* <<nsa-http-basic,http-basic>>\n* <<nsa-intercept-url,intercept-url>>\n* <<nsa-jee,jee>>\n* <<nsa-logout,logout>>\n* <<nsa-oauth2-client,oauth2-client>>\n* <<nsa-oauth2-login,oauth2-login>>\n* <<nsa-oauth2-resource-server,oauth2-resource-server>>\n* <<nsa-password-management,password-management>>\n* <<nsa-port-mappings,port-mappings>>\n* <<nsa-remember-me,remember-me>>\n* <<nsa-request-cache,request-cache>>\n* <<nsa-saml2-login,saml2-login>>\n* <<nsa-saml2-logout,saml2-logout>>\n* <<nsa-session-management,session-management>>\n* <<nsa-x509,x509>>\n\n\n[[nsa-access-denied-handler]]\n== <access-denied-handler>\nThis element allows you to set the `errorPage` property for the default `AccessDeniedHandler` used by the `ExceptionTranslationFilter`, using the <<nsa-access-denied-handler-error-page,error-page>> attribute, or to supply your own implementation using the <<nsa-access-denied-handler-ref,ref>> attribute.\nThis is discussed in more detail in the section on the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[ExceptionTranslationFilter].\n\n\n[[nsa-access-denied-handler-parents]]\n=== Parent Elements of <access-denied-handler>\n\n* <<nsa-http,http>>\n\n[[nsa-access-denied-handler-attributes]]\n=== <access-denied-handler> Attributes\n\n\n[[nsa-access-denied-handler-error-page]]\n* **error-page**\nThe access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.\n\n\n[[nsa-access-denied-handler-ref]]\n* **ref**\nDefines a reference to a Spring bean of type `AccessDeniedHandler`.\n\n\n[[nsa-cors]]\n== <cors>\nThis element allows for configuring a `CorsFilter`.\nEither a `CorsFilter` or a `CorsConfigurationSource` must be specified.\nIf Spring MVC is present, then it will attempt to look up its `CorsConfigurationSource`.\n\n[[nsa-cors-attributes]]\n=== <cors> Attributes\nThe attributes on the `<cors>` element control the headers element.\n\n[[nsa-cors-ref]]\n* **ref**\nOptional attribute that specifies the bean name of a `CorsFilter`.\n\n[[nsa-cors-configuration-source-ref]]\n* **cors-configuration-source-ref**\nOptional attribute that specifies the bean name of a `CorsConfigurationSource` to be injected into a `CorsFilter` created by the XML namespace.\n\n[[nsa-cors-parents]]\n=== Parent Elements of <cors>\n\n* <<nsa-http,http>>\n\n[[nsa-headers]]\n== <headers>\nThis element allows for configuring additional (security) headers to be sent with the response.\nIt enables easy configuration for several headers and also allows for setting custom headers through the <<nsa-header,header>> element.\nAdditional information, can be found in the xref:features/exploits/headers.adoc#headers[Security Headers] section of the reference.\n\n** `Cache-Control`, `Pragma`, and `Expires` - Can be set using the <<nsa-cache-control,cache-control>> element.\nThis ensures that the browser does not cache your secured pages.\n** `Strict-Transport-Security` - Can be set using the <<nsa-hsts,hsts>> element.\nThis ensures that the browser automatically requests HTTPS for future requests.\n** `X-Frame-Options` - Can be set using the <<nsa-frame-options,frame-options>> element.\nThe https://en.wikipedia.org/wiki/Clickjacking#X-Frame-Options[X-Frame-Options] header can be used to prevent clickjacking attacks.\n** `X-XSS-Protection` - Can be set using the <<nsa-xss-protection,xss-protection>> element.\nThe https://en.wikipedia.org/wiki/Cross-site_scripting[X-XSS-Protection ] header can be used by browser to do basic control.\n** `X-Content-Type-Options` - Can be set using the <<nsa-content-type-options,content-type-options>> element.\nThe https://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx[X-Content-Type-Options] header prevents Internet Explorer from MIME-sniffing a response away from the declared content-type.\nThis also applies to Google Chrome, when downloading extensions.\n** `Public-Key-Pinning` or `Public-Key-Pinning-Report-Only` - Can be set using the <<nsa-hpkp,hpkp>> element.\nThis allows HTTPS websites to resist impersonation by attackers using mis-issued or otherwise fraudulent certificates.\n** `Content-Security-Policy` or `Content-Security-Policy-Report-Only` - Can be set using the <<nsa-content-security-policy,content-security-policy>> element.\nhttps://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS).\n** `Referrer-Policy` - Can be set using the <<nsa-referrer-policy,referrer-policy>> element, https://www.w3.org/TR/referrer-policy/[Referrer-Policy] is a mechanism that web applications can leverage to manage the referrer field, which contains the last page the user was on.\n** `Feature-Policy` - Can be set using the <<nsa-feature-policy,feature-policy>> element, https://wicg.github.io/feature-policy/[Feature-Policy] is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.\n** `Cross-Origin-Opener-Policy` - Can be set using the <<nsa-cross-origin-opener-policy,cross-origin-opener-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy[Cross-Origin-Opener-Policy] is a mechanism that allows you to ensure a top-level document does not share a browsing context group with cross-origin documents.\n** `Cross-Origin-Embedder-Policy` - Can be set using the <<nsa-cross-origin-embedder-policy,cross-origin-embedder-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy[Cross-Origin-Embedder-Policy] is a mechanism that prevents a document from loading any cross-origin resources that don't explicitly grant the document permission.\n** `Cross-Origin-Resource-Policy` - Can be set using the <<nsa-cross-origin-resource-policy,cross-origin-resource-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy[Cross-Origin-Resource-Policy] is a mechanism that conveys a desire that the browser blocks no-cors cross-origin/cross-site requests to the given resource.\n\n[[nsa-headers-attributes]]\n=== <headers> Attributes\nThe attributes on the `<headers>` element control the headers element.\n\n\n[[nsa-headers-defaults-disabled]]\n* **defaults-disabled**\nOptional attribute that specifies to disable the default Spring Security's HTTP response headers.\nThe default is false (the default headers are included).\n\n[[nsa-headers-disabled]]\n* **disabled**\nOptional attribute that specifies to disable Spring Security's HTTP response headers.\nThe default is false (the headers are enabled).\n\n\n[[nsa-headers-parents]]\n=== Parent Elements of <headers>\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-headers-children]]\n=== Child Elements of <headers>\n\n\n* <<nsa-cache-control,cache-control>>\n* <<nsa-content-security-policy,content-security-policy>>\n* <<nsa-content-type-options,content-type-options>>\n* <<nsa-cross-origin-embedder-policy,cross-origin-embedder-policy>>\n* <<nsa-cross-origin-opener-policy,cross-origin-opener-policy>>\n* <<nsa-cross-origin-resource-policy,cross-origin-resource-policy>>\n* <<nsa-feature-policy,feature-policy>>\n* <<nsa-frame-options,frame-options>>\n* <<nsa-header,header>>\n* <<nsa-hpkp,hpkp>>\n* <<nsa-hsts,hsts>>\n* <<nsa-permissions-policy,permission-policy>>\n* <<nsa-referrer-policy,referrer-policy>>\n* <<nsa-xss-protection,xss-protection>>\n\n\n\n[[nsa-cache-control]]\n== <cache-control>\nAdds `Cache-Control`, `Pragma`, and `Expires` headers to ensure that the browser does not cache your secured pages.\n\n\n[[nsa-cache-control-attributes]]\n=== <cache-control> Attributes\n\n[[nsa-cache-control-disabled]]\n* **disabled**\nSpecifies if Cache Control should be disabled.\nDefault false.\n\n\n[[nsa-cache-control-parents]]\n=== Parent Elements of <cache-control>\n\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-hsts]]\n== <hsts>\nWhen enabled adds the https://tools.ietf.org/html/rfc6797[Strict-Transport-Security] header to the response for any secure request.\nThis allows the server to instruct browsers to automatically use HTTPS for future requests.\n\n\n[[nsa-hsts-attributes]]\n=== <hsts> Attributes\n\n[[nsa-hsts-disabled]]\n* **disabled**\nSpecifies if Strict-Transport-Security should be disabled.\nDefault false.\n\n[[nsa-hsts-include-subdomains]]\n* **include-sub-domains**\nSpecifies if subdomains should be included.\nDefault true.\n\n\n[[nsa-hsts-max-age-seconds]]\n* **max-age-seconds**\nSpecifies the maximum amount of time the host should be considered a Known HSTS Host.\nDefault one year.\n\n\n[[nsa-hsts-request-matcher-ref]]\n* **request-matcher-ref**\nThe RequestMatcher instance to be used to determine if the header should be set.\nDefault is if HttpServletRequest.isSecure() is true.\n\n[[nsa-hsts-preload]]\n* **preload**\nSpecifies if preload should be included.\nDefault false.\n\n[[nsa-hsts-parents]]\n=== Parent Elements of <hsts>\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-hpkp]]\n== <hpkp>\nWhen enabled adds the https://tools.ietf.org/html/rfc7469[Public Key Pinning Extension for HTTP] header to the response for any secure request.\nThis allows HTTPS websites to resist impersonation by attackers using mis-issued or otherwise fraudulent certificates.\n\n\n[[nsa-hpkp-attributes]]\n=== <hpkp> Attributes\n\n[[nsa-hpkp-disabled]]\n* **disabled**\nSpecifies if HTTP Public Key Pinning (HPKP) should be disabled.\nDefault true.\n\n[[nsa-hpkp-include-subdomains]]\n* **include-sub-domains**\nSpecifies if subdomains should be included.\nDefault false.\n\n\n[[nsa-hpkp-max-age-seconds]]\n* **max-age-seconds**\nSets the value for the max-age directive of the Public-Key-Pins header.\nDefault 60 days.\n\n\n[[nsa-hpkp-report-only]]\n* **report-only**\nSpecifies if the browser should only report pin validation failures.\nDefault true.\n\n\n[[nsa-hpkp-report-uri]]\n* **report-uri**\nSpecifies the URI to which the browser should report pin validation failures.\n\n\n[[nsa-hpkp-parents]]\n=== Parent Elements of <hpkp>\n\n* <<nsa-headers,headers>>\n\n\n[[nsa-pins]]\n== <pins>\nThe list of pins\n\n\n[[nsa-pins-children]]\n=== Child Elements of <pins>\n\n* <<nsa-pin,pin>>\n\n\n[[nsa-pin]]\n== <pin>\nA pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute\n\n[[nsa-pin-attributes]]\n=== <pin> Attributes\n\n[[nsa-pin-algorithm]]\n* **algorithm**\nThe cryptographic hash algorithm.\nDefault is SHA256.\n\n\n[[nsa-pin-parents]]\n=== Parent Elements of <pin>\n\n* <<nsa-pins,pins>>\n\n\n\n[[nsa-content-security-policy]]\n== <content-security-policy>\nWhen enabled adds the https://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] header to the response.\nCSP is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS).\n\n[[nsa-content-security-policy-attributes]]\n=== <content-security-policy> Attributes\n\n[[nsa-content-security-policy-policy-directives]]\n* **policy-directives**\nThe security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used.\n\n[[nsa-content-security-policy-report-only]]\n* **report-only**\nSet to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only.\nDefaults to false.\n\n[[nsa-content-security-policy-parents]]\n=== Parent Elements of <content-security-policy>\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-referrer-policy]]\n== <referrer-policy>\nWhen enabled adds the https://www.w3.org/TR/referrer-policy/[Referrer Policy] header to the response.\n\n[[nsa-referrer-policy-attributes]]\n=== <referrer-policy> Attributes\n\n[[nsa-referrer-policy-policy]]\n* **policy**\nThe policy for the Referrer-Policy header.\nDefault \"no-referrer\".\n\n[[nsa-referrer-policy-parents]]\n=== Parent Elements of <referrer-policy>\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-feature-policy]]\n== <feature-policy>\nWhen enabled adds the https://wicg.github.io/feature-policy/[Feature Policy] header to the response.\n\n[[nsa-feature-policy-attributes]]\n=== <feature-policy> Attributes\n\n[[nsa-feature-policy-policy-directives]]\n* **policy-directives**\nThe security policy directive(s) for the Feature-Policy header.\n\n[[nsa-feature-policy-parents]]\n=== Parent Elements of <feature-policy>\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-frame-options]]\n== <frame-options>\nWhen enabled adds the https://tools.ietf.org/html/draft-ietf-websec-x-frame-options[X-Frame-Options header] to the response, this allows newer browsers to do some security checks and prevent https://en.wikipedia.org/wiki/Clickjacking[clickjacking] attacks.\n\n\n[[nsa-frame-options-attributes]]\n=== <frame-options> Attributes\n\n[[nsa-frame-options-disabled]]\n* **disabled**\nIf disabled, the X-Frame-Options header will not be included.\nDefault false.\n\n[[nsa-frame-options-policy]]\n* **policy**\n** `DENY` The page cannot be displayed in a frame, regardless of the site attempting to do so.\nThis is the default when frame-options-policy is specified.\n** `SAMEORIGIN` The page can only be displayed in a frame on the same origin as the page itself\n\n+\n\nIn other words, if you specify DENY, not only will attempts to load the page in a frame fail when loaded from other sites, attempts to do so will fail when loaded from the same site.\nOn the other hand, if you specify SAMEORIGIN, you can still use the page in a frame as long as the site including it in a frame it is the same as the one serving the page.\n\n\n\n[[nsa-frame-options-parents]]\n=== Parent Elements of <frame-options>\n\n* <<nsa-headers,headers>>\n\n[[nsa-permissions-policy]]\n== <permissions-policy>\nAdds the https://w3c.github.io/webappsec-permissions-policy/[Permissions-Policy header] to the response.\n\n[[nsa-permissions-policy-attributes]]\n=== <permissions-policy> Attributes\n\n[[nsa-permissions-policy-policy]]\n* **policy**\nThe policy value to write for the `Permissions-Policy` header\n\n[[nsa-permissions-policy-parents]]\n=== Parent Elements of <permissions-policy>\n\n* <<nsa-headers,headers>>\n\n[[nsa-xss-protection]]\n== <xss-protection>\nAdds the https://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx[X-XSS-Protection header] to the response to assist in protecting against https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] attacks.\nThis is in no-way a full protection to XSS attacks!\n\n\n[[nsa-xss-protection-attributes]]\n=== <xss-protection> Attributes\n\n\n[[nsa-xss-protection-disabled]]\n* **xss-protection-disabled**\nDo not include the header for https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] protection.\n\n\n[[nsa-xss-protection-header-value]]\n* **xss-protection-header-value**\nExplicitly set the value for https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] header.\nOne of: \"0\", \"1\", \"1; mode=block\". Defaults to \"0\".\n\n\n[[nsa-xss-protection-parents]]\n=== Parent Elements of <xss-protection>\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-content-type-options]]\n== <content-type-options>\nAdd the X-Content-Type-Options header with the value of nosniff to the response.\nThis https://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx[disables MIME-sniffing] for IE8+ and Chrome extensions.\n\n\n[[nsa-content-type-options-attributes]]\n=== <content-type-options> Attributes\n\n[[nsa-content-type-options-disabled]]\n* **disabled**\nSpecifies if Content Type Options should be disabled.\nDefault false.\n\n[[nsa-content-type-options-parents]]\n=== Parent Elements of <content-type-options>\n\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-cross-origin-embedder-policy]]\n==== <cross-origin-embedder-policy>\nWhen enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy[Cross-Origin-Embedder-Policy] header to the response.\n\n\n[[nsa-cross-origin-embedder-policy-attributes]]\n===== <cross-origin-embedder-policy> Attributes\n\n[[nsa-cross-origin-embedder-policy-policy]]\n* **policy**\nThe policy for the `Cross-Origin-Embedder-Policy` header.\n\n[[nsa-cross-origin-embedder-policy-parents]]\n===== Parent Elements of <cross-origin-embedder-policy>\n\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-cross-origin-opener-policy]]\n==== <cross-origin-opener-policy>\nWhen enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy[Cross-Origin-Opener-Policy] header to the response.\n\n\n[[nsa-cross-origin-opener-policy-attributes]]\n===== <cross-origin-opener-policy> Attributes\n\n[[nsa-cross-origin-opener-policy-policy]]\n* **policy**\nThe policy for the `Cross-Origin-Opener-Policy` header.\n\n[[nsa-cross-origin-opener-policy-parents]]\n===== Parent Elements of <cross-origin-opener-policy>\n\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-cross-origin-resource-policy]]\n==== <cross-origin-resource-policy>\nWhen enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy[Cross-Origin-Resource-Policy] header to the response.\n\n\n[[nsa-cross-origin-resource-policy-attributes]]\n===== <cross-origin-resource-policy> Attributes\n\n[[nsa-cross-origin-resource-policy-policy]]\n* **policy**\nThe policy for the `Cross-Origin-Resource-Policy` header.\n\n[[nsa-cross-origin-resource-policy-parents]]\n===== Parent Elements of <cross-origin-resource-policy>\n\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-header]]\n== <header>\nAdd additional headers to the response, both the name and value need to be specified.\n\n\n[[nsa-header-attributes]]\n=== <header-attributes> Attributes\n\n\n[[nsa-header-name]]\n* **header-name**\nThe `name` of the header.\n\n\n[[nsa-header-value]]\n* **value**\nThe `value` of the header to add.\n\n\n[[nsa-header-ref]]\n* **ref**\nReference to a custom implementation of the `HeaderWriter` interface.\n\n\n[[nsa-header-parents]]\n=== Parent Elements of <header>\n\n\n* <<nsa-headers,headers>>\n\n\n\n[[nsa-anonymous]]\n== <anonymous>\nAdds an `AnonymousAuthenticationFilter` to the stack and an `AnonymousAuthenticationProvider`.\nRequired if you are using the `IS_AUTHENTICATED_ANONYMOUSLY` attribute.\n\n\n[[nsa-anonymous-parents]]\n=== Parent Elements of <anonymous>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-anonymous-attributes]]\n=== <anonymous> Attributes\n\n\n[[nsa-anonymous-enabled]]\n* **enabled**\nWith the default namespace setup, the anonymous \"authentication\" facility is automatically enabled.\nYou can disable it using this property.\n\n\n[[nsa-anonymous-granted-authority]]\n* **granted-authority**\nThe granted authority that should be assigned to the anonymous request.\nCommonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions.\nIf unset, defaults to `ROLE_ANONYMOUS`.\n\n\n[[nsa-anonymous-key]]\n* **key**\nThe key shared between the provider and filter.\nThis generally does not need to be set.\nIf unset, it will default to a secure randomly generated value.\nThis means setting this value can improve startup time when using the anonymous functionality since secure random values can take a while to be generated.\n\n\n[[nsa-anonymous-username]]\n* **username**\nThe username that should be assigned to the anonymous request.\nThis allows the principal to be identified, which may be important for logging and auditing.\nif unset, defaults to `anonymousUser`.\n\n\n[[nsa-csrf]]\n== <csrf>\nThis element will add https://en.wikipedia.org/wiki/Cross-site_request_forgery[Cross Site Request Forger (CSRF)] protection to the application.\nIt also updates the default RequestCache to only replay \"GET\" requests upon successful authentication.\nAdditional information can be found in the xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] section of the reference.\n\n\n[[nsa-csrf-parents]]\n=== Parent Elements of <csrf>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-csrf-attributes]]\n=== <csrf> Attributes\n\n[[nsa-csrf-disabled]]\n* **disabled**\nOptional attribute that specifies to disable Spring Security's CSRF protection.\nThe default is false (CSRF protection is enabled).\nIt is highly recommended to leave CSRF protection enabled.\n\n[[nsa-csrf-token-repository-ref]]\n* **token-repository-ref**\nThe CsrfTokenRepository to use.\nThe default is `HttpSessionCsrfTokenRepository`.\n\n[[nsa-csrf-request-handler-ref]]\n* **request-handler-ref**\nThe optional `CsrfTokenRequestHandler` to use. The default is `CsrfTokenRequestAttributeHandler`.\n\n[[nsa-csrf-request-matcher-ref]]\n* **request-matcher-ref**\nThe RequestMatcher instance to be used to determine if CSRF should be applied.\nDefault is any HTTP method except \"GET\", \"TRACE\", \"HEAD\", \"OPTIONS\".\n\n\n[[nsa-custom-filter]]\n== <custom-filter>\nThis element is used to add a filter to the filter chain.\nIt doesn't create any additional beans but is used to select a bean of type `jakarta.servlet.Filter` which is already defined in the application context and add that at a particular position in the filter chain maintained by Spring Security.\nFull details can be found in the xref:servlet/configuration/xml-namespace.adoc#ns-custom-filters[ namespace chapter].\n\n\n[[nsa-custom-filter-parents]]\n=== Parent Elements of <custom-filter>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-custom-filter-attributes]]\n=== <custom-filter> Attributes\n\n\n[[nsa-custom-filter-after]]\n* **after**\nThe filter immediately after which the custom-filter should be placed in the chain.\nThis feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters.\nThe filter names map to specific Spring Security implementation filters.\n\n\n[[nsa-custom-filter-before]]\n* **before**\nThe filter immediately before which the custom-filter should be placed in the chain\n\n\n[[nsa-custom-filter-position]]\n* **position**\nThe explicit position at which the custom-filter should be placed in the chain.\nUse if you are replacing a standard filter.\n\n\n[[nsa-custom-filter-ref]]\n* **ref**\nDefines a reference to a Spring bean that implements `Filter`.\n\n\n[[nsa-expression-handler]]\n== <expression-handler>\nDefines the `SecurityExpressionHandler` instance which will be used if expression-based access-control is enabled.\nA default implementation (with no ACL support) will be used if not supplied.\n\n\n[[nsa-expression-handler-parents]]\n=== Parent Elements of <expression-handler>\n\n\n* xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[global-method-security]\n* <<nsa-http,http>>\n* xref:servlet/appendix/namespace/method-security.adoc#nsa-method-security[method-security]\n* xref:servlet/appendix/namespace/websocket.adoc#nsa-websocket-message-broker[websocket-message-broker]\n\n\n\n[[nsa-expression-handler-attributes]]\n=== <expression-handler> Attributes\n\n\n[[nsa-expression-handler-ref]]\n* **ref**\nDefines a reference to a Spring bean that implements `SecurityExpressionHandler`.\n\n\n[[nsa-form-login]]\n== <form-login>\nUsed to add an `UsernamePasswordAuthenticationFilter` to the filter stack and an `LoginUrlAuthenticationEntryPoint` to the application context to provide authentication on demand.\nThis will always take precedence over other namespace-created entry points.\nIf no attributes are supplied, a login page will be generated automatically at the URL \"/login\" footnote:[\nThis feature is really just provided for convenience and is not intended for production (where a view technology will have been chosen and can be used to render a customized login page).\nThe class `DefaultLoginPageGeneratingFilter` is responsible for rendering the login page and will provide login forms for both normal form login and/or OIDC if required.\n] The behaviour can be customized using the <<nsa-form-login-attributes, `<form-login>` Attributes>>.\n\n\n[[nsa-form-login-parents]]\n=== Parent Elements of <form-login>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-form-login-attributes]]\n=== <form-login> Attributes\n\n\n[[nsa-form-login-always-use-default-target]]\n* **always-use-default-target**\nIf set to `true`, the user will always start at the value given by <<nsa-form-login-default-target-url,default-target-url>>, regardless of how they arrived at the login page.\nMaps to the `alwaysUseDefaultTargetUrl` property of `UsernamePasswordAuthenticationFilter`.\nDefault value is `false`.\n\n\n[[nsa-form-login-authentication-details-source-ref]]\n* **authentication-details-source-ref**\nReference to an `AuthenticationDetailsSource` which will be used by the authentication filter\n\n\n[[nsa-form-login-authentication-failure-handler-ref]]\n* **authentication-failure-handler-ref**\nCan be used as an alternative to <<nsa-form-login-authentication-failure-url,authentication-failure-url>>, giving you full control over the navigation flow after an authentication failure.\nThe value should be the name of an `AuthenticationFailureHandler` bean in the application context.\n\n\n[[nsa-form-login-authentication-failure-url]]\n* **authentication-failure-url**\nMaps to the `authenticationFailureUrl` property of `UsernamePasswordAuthenticationFilter`.\nDefines the URL the browser will be redirected to on login failure.\nDefaults to `/login?error`, which will be automatically handled by the automatic login page generator, re-rendering the login page with an error message.\n\n\n[[nsa-form-login-authentication-success-handler-ref]]\n* **authentication-success-handler-ref**\nThis can be used as an alternative to <<nsa-form-login-default-target-url,default-target-url>> and <<nsa-form-login-always-use-default-target,always-use-default-target>>, giving you full control over the navigation flow after a successful authentication.\nThe value should be the name of an `AuthenticationSuccessHandler` bean in the application context.\nBy default, an implementation of `SavedRequestAwareAuthenticationSuccessHandler` is used and injected with the <<nsa-form-login-default-target-url,default-target-url >>.\n\n\n[[nsa-form-login-default-target-url]]\n* **default-target-url**\nMaps to the `defaultTargetUrl` property of `UsernamePasswordAuthenticationFilter`.\nIf not set, the default value is \"/\" (the application root).\nA user will be taken to this URL after logging in, provided they were not asked to login while attempting to access a secured resource, when they will be taken to the originally requested URL.\n\n\n[[nsa-form-login-login-page]]\n* **login-page**\nThe URL that should be used to render the login page.\nMaps to the `loginFormUrl` property of the `LoginUrlAuthenticationEntryPoint`.\nDefaults to \"/login\".\n\n\n[[nsa-form-login-login-processing-url]]\n* **login-processing-url**\nMaps to the `filterProcessesUrl` property of `UsernamePasswordAuthenticationFilter`.\nThe default value is \"/login\".\n\n\n[[nsa-form-login-password-parameter]]\n* **password-parameter**\nThe name of the request parameter which contains the password.\nDefaults to \"password\".\n\n\n[[nsa-form-login-username-parameter]]\n* **username-parameter**\nThe name of the request parameter which contains the username.\nDefaults to \"username\".\n\n[[nsa-form-login-authentication-success-forward-url]]\n* **authentication-success-forward-url**\nMaps a `ForwardAuthenticationSuccessHandler` to `authenticationSuccessHandler` property of `UsernamePasswordAuthenticationFilter`.\n\n\n[[nsa-form-login-authentication-failure-forward-url]]\n* **authentication-failure-forward-url**\nMaps a `ForwardAuthenticationFailureHandler` to `authenticationFailureHandler` property of `UsernamePasswordAuthenticationFilter`.\n\n\n[[nsa-oauth2-login]]\n== <oauth2-login>\nThe xref:servlet/oauth2/login/index.adoc#oauth2login[OAuth 2.0 Login] feature configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.\n\n\n[[nsa-oauth2-login-parents]]\n=== Parent Elements of <oauth2-login>\n\n* <<nsa-http,http>>\n\n[[nsa-oauth2-login-attributes]]\n=== <oauth2-login> Attributes\n\n\n[[nsa-oauth2-login-client-registration-repository-ref]]\n* **client-registration-repository-ref**\nReference to the `ClientRegistrationRepository`.\n\n\n[[nsa-oauth2-login-authorized-client-repository-ref]]\n* **authorized-client-repository-ref**\nReference to the `OAuth2AuthorizedClientRepository`.\n\n\n[[nsa-oauth2-login-authorized-client-service-ref]]\n* **authorized-client-service-ref**\nReference to the `OAuth2AuthorizedClientService`.\n\n\n[[nsa-oauth2-login-authorization-request-repository-ref]]\n* **authorization-request-repository-ref**\nReference to the `AuthorizationRequestRepository`.\n\n\n[[nsa-oauth2-login-authorization-request-resolver-ref]]\n* **authorization-request-resolver-ref**\nReference to the `OAuth2AuthorizationRequestResolver`.\n\n\n[[nsa-oauth2-login-authorization-redirect-strategy-ref]]\n* **authorization-redirect-strategy-ref**\nReference to the authorization `RedirectStrategy`.\n\n\n[[nsa-oauth2-login-access-token-response-client-ref]]\n* **access-token-response-client-ref**\nReference to the `OAuth2AccessTokenResponseClient`.\n\n\n[[nsa-oauth2-login-user-authorities-mapper-ref]]\n* **user-authorities-mapper-ref**\nReference to the `GrantedAuthoritiesMapper`.\n\n\n[[nsa-oauth2-login-user-service-ref]]\n* **user-service-ref**\nReference to the `OAuth2UserService`.\n\n\n[[nsa-oauth2-login-oidc-user-service-ref]]\n* **oidc-user-service-ref**\nReference to the OpenID Connect `OAuth2UserService`.\n\n\n[[nsa-oauth2-login-login-processing-url]]\n* **login-processing-url**\nThe URI where the filter processes authentication requests.\n\n\n[[nsa-oauth2-login-login-page]]\n* **login-page**\nThe URI to send users to login.\n\n\n[[nsa-oauth2-login-authentication-success-handler-ref]]\n* **authentication-success-handler-ref**\nReference to the `AuthenticationSuccessHandler`.\n\n\n[[nsa-oauth2-login-authentication-failure-handler-ref]]\n* **authentication-failure-handler-ref**\nReference to the `AuthenticationFailureHandler`.\n\n\n[[nsa-oauth2-login-jwt-decoder-factory-ref]]\n* **jwt-decoder-factory-ref**\nReference to the `JwtDecoderFactory` used by `OidcAuthorizationCodeAuthenticationProvider`.\n\n\n[[nsa-oauth2-client]]\n== <oauth2-client>\nConfigures xref:servlet/oauth2/client/index.adoc#oauth2client[OAuth 2.0 Client] support.\n\n\n[[nsa-oauth2-client-parents]]\n=== Parent Elements of <oauth2-client>\n\n* <<nsa-http,http>>\n\n[[nsa-oauth2-client-attributes]]\n=== <oauth2-client> Attributes\n\n\n[[nsa-oauth2-client-client-registration-repository-ref]]\n* **client-registration-repository-ref**\nReference to the `ClientRegistrationRepository`.\n\n\n[[nsa-oauth2-client-authorized-client-repository-ref]]\n* **authorized-client-repository-ref**\nReference to the `OAuth2AuthorizedClientRepository`.\n\n\n[[nsa-oauth2-client-authorized-client-service-ref]]\n* **authorized-client-service-ref**\nReference to the `OAuth2AuthorizedClientService`.\n\n\n[[nsa-oauth2-client-children]]\n=== Child Elements of <oauth2-client>\n\n* <<nsa-authorization-code-grant,authorization-code-grant>>\n\n\n[[nsa-authorization-code-grant]]\n== <authorization-code-grant>\nConfigures xref:servlet/oauth2/client/authorization-grants.adoc#oauth2Client-auth-grant-support[OAuth 2.0 Authorization Code Grant].\n\n\n[[nsa-authorization-code-grant-parents]]\n=== Parent Elements of <authorization-code-grant>\n\n* <<nsa-oauth2-client,oauth2-client>>\n\n\n[[nsa-authorization-code-grant-attributes]]\n=== <authorization-code-grant> Attributes\n\n\n[[nsa-authorization-code-grant-authorization-request-repository-ref]]\n* **authorization-request-repository-ref**\nReference to the `AuthorizationRequestRepository`.\n\n\n[[nsa-authorization-code-grant-authorization-redirect-strategy-ref]]\n* **authorization-redirect-strategy-ref**\nReference to the authorization `RedirectStrategy`.\n\n\n[[nsa-authorization-code-grant-authorization-request-resolver-ref]]\n* **authorization-request-resolver-ref**\nReference to the `OAuth2AuthorizationRequestResolver`.\n\n\n[[nsa-authorization-code-grant-access-token-response-client-ref]]\n* **access-token-response-client-ref**\nReference to the `OAuth2AccessTokenResponseClient`.\n\n\n[[nsa-client-registrations]]\n== <client-registrations>\nA container element for client(s) registered (xref:servlet/oauth2/client/index.adoc#oauth2Client-client-registration[ClientRegistration]) with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\n\n[[nsa-client-registrations-children]]\n=== Child Elements of <client-registrations>\n\n* <<nsa-client-registration,client-registration>>\n* <<nsa-provider,provider>>\n\n\n[[nsa-client-registration]]\n== <client-registration>\nRepresents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\n\n[[nsa-client-registration-parents]]\n=== Parent Elements of <client-registration>\n\n* <<nsa-client-registrations,client-registrations>>\n\n\n[[nsa-client-registration-attributes]]\n=== <client-registration> Attributes\n\n\n[[nsa-client-registration-registration-id]]\n* **registration-id**\nThe ID that uniquely identifies the `ClientRegistration`.\n\n\n[[nsa-client-registration-client-id]]\n* **client-id**\nThe client identifier.\n\n\n[[nsa-client-registration-client-secret]]\n* **client-secret**\nThe client secret.\n\n\n[[nsa-client-registration-client-authentication-method]]\n* **client-authentication-method**\nThe method used to authenticate the Client with the Provider.\nThe supported values are *client_secret_basic*, *client_secret_post*, *private_key_jwt*, *client_secret_jwt* and *none* https://tools.ietf.org/html/rfc6749#section-2.1[(public clients)].\n\n\n[[nsa-client-registration-authorization-grant-type]]\n* **authorization-grant-type**\nThe OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types.\nThe supported values are `authorization_code`, `client_credentials`, `password`, as well as, extension grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`.\n\n\n[[nsa-client-registration-redirect-uri]]\n* **redirect-uri**\nThe client's registered redirect URI that the _Authorization Server_ redirects the end-user's user-agent to after the end-user has authenticated and authorized access to the client.\n\n\n[[nsa-client-registration-scope]]\n* **scope**\nThe scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n\n\n[[nsa-client-registration-client-name]]\n* **client-name**\nA descriptive name used for the client.\nThe name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n\n[[nsa-client-registration-provider-id]]\n* **provider-id**\nA reference to the associated provider. May reference a `<provider>` element or use one of the common providers (google, github, facebook, okta).\n\n\n[[nsa-provider]]\n== <provider>\nThe configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\n\n[[nsa-provider-parents]]\n=== Parent Elements of <provider>\n\n* <<nsa-client-registrations,client-registrations>>\n\n\n[[nsa-provider-attributes]]\n=== <provider> Attributes\n\n\n[[nsa-provider-provider-id]]\n* **provider-id**\nThe ID that uniquely identifies the provider.\n\n\n[[nsa-provider-authorization-uri]]\n* **authorization-uri**\nThe Authorization Endpoint URI for the Authorization Server.\n\n\n[[nsa-provider-token-uri]]\n* **token-uri**\nThe Token Endpoint URI for the Authorization Server.\n\n\n[[nsa-provider-user-info-uri]]\n* **user-info-uri**\nThe UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.\n\n\n[[nsa-provider-user-info-authentication-method]]\n* **user-info-authentication-method**\nThe authentication method used when sending the access token to the UserInfo Endpoint.\nThe supported values are *header*, *form* and *query*.\n\n\n[[nsa-provider-user-info-user-name-attribute]]\n* **user-info-user-name-attribute**\nThe name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n\n\n[[nsa-provider-jwk-set-uri]]\n* **jwk-set-uri**\nThe URI used to retrieve the https://tools.ietf.org/html/rfc7517[JSON Web Key (JWK)] Set from the Authorization Server, which contains the cryptographic key(s) used to verify the https://tools.ietf.org/html/rfc7515[JSON Web Signature (JWS)] of the ID Token and optionally the UserInfo Response.\n\n\n[[nsa-provider-issuer-uri]]\n* **issuer-uri**\nThe URI used to initially configure a `ClientRegistration` using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint].\n\n[[nsa-oauth2-resource-server]]\n== <oauth2-resource-server>\nAdds a `BearerTokenAuthenticationFilter`, `BearerTokenAuthenticationEntryPoint`, and `BearerTokenAccessDeniedHandler` to the configuration.\nIn addition, either `<jwt>` or `<opaque-token>` must be specified.\n\n[[nsa-oauth2-resource-server-parents]]\n=== Parents Elements of <oauth2-resource-server>\n\n* <<nsa-http,http>>\n\n[[nsa-oauth2-resource-server-children]]\n=== Child Elements of <oauth2-resource-server>\n\n* <<nsa-jwt,jwt>>\n* <<nsa-opaque-token,opaque-token>>\n\n[[nsa-oauth2-resource-server-attributes]]\n=== <oauth2-resource-server> Attributes\n\n[[nsa-oauth2-resource-server-authentication-manager-resolver-ref]]\n* **authentication-manager-resolver-ref**\nReference to an `AuthenticationManagerResolver` which will resolve the `AuthenticationManager` at request time\n\n[[nsa-oauth2-resource-server-bearer-token-resolver-ref]]\n* **bearer-token-resolver-ref**\nReference to a `BearerTokenResolver` which will retrieve the bearer token from the request.\nThis cannot be used in conjunction with `authentication-converter-ref`\n\n[[nsa-oauth2-resource-server-entry-point-ref]]\n* **entry-point-ref**\nReference to a `AuthenticationEntryPoint` which will handle unauthorized requests\n\n[[nsa-oauth2-resource-server-authentication-converter-ref]]\n* **authentication-converter-ref**\nReference to a `AuthenticationConverter` which convert request to authentication.\nThis cannot be used in conjunction with `bearer-token-resolver-ref`\n\n[[nsa-jwt]]\n== <jwt>\nRepresents an OAuth 2.0 Resource Server that will authorize JWTs\n\n\n[[nsa-jwt-parents]]\n=== Parent Elements of <jwt>\n\n* <<nsa-oauth2-resource-server,oauth2-resource-server>>\n\n\n[[nsa-jwt-attributes]]\n=== <jwt> Attributes\n\n[[nsa-jwt-jwt-authentication-converter-ref]]\n* **jwt-authentication-converter-ref**\nReference to a `Converter<Jwt, AbstractAuthenticationToken>`\n\n[[nsa-jwt-decoder-ref]]\n* **jwt-decoder-ref**\nReference to a `JwtDecoder`. This is a larger component that overrides `jwk-set-uri`\n\n[[nsa-jwt-jwk-set-uri]]\n* **jwk-set-uri**\nThe JWK Set Uri used to load signing verification keys from an OAuth 2.0 Authorization Server\n\n[[nsa-opaque-token]]\n== <opaque-token>\nRepresents an OAuth 2.0 Resource Server that will authorize opaque tokens\n\n[[nsa-opaque-token-parents]]\n=== Parent Elements of <opaque-token>\n\n* <<nsa-oauth2-resource-server,oauth2-resource-server>>\n\n[[nsa-opaque-token-attributes]]\n=== <opaque-token> Attributes\n\n[[nsa-opaque-token-introspector-ref]]\n* **introspector-ref**\nReference to an `OpaqueTokenIntrospector`. This is a larger component that overrides `introspection-uri`, `client-id`, and `client-secret`.\n\n[[nsa-opaque-token-introspection-uri]]\n* **introspection-uri**\nThe Introspection Uri used to introspect the details of an opaque token. Should be accompanied with a `client-id` and `client-secret`.\n\n[[nsa-opaque-token-client-id]]\n* **client-id**\nThe Client Id to use for client authentication against the provided `introspection-uri`.\n\n[[nsa-opaque-token-client-secret]]\n* **client-secret**\nThe Client Secret to use for client authentication against the provided `introspection-uri`.\n\n[[nsa-opaque-token-authentication-converter-ref]]\n* **authentication-converter-ref**\nReference to an `OpaqueTokenAuthenticationConverter`. Responsible for converting successful introspection result into an `Authentication` instance.\n\n\n[[nsa-relying-party-registrations]]\n== <relying-party-registrations>\nThe container element for relying party(ies) registered (xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[ClientRegistration]) with a SAML 2.0 Identity Provider.\n\n[[nsa-relying-party-registrations-attributes]]\n=== <relying-party-registrations> Attributes\n\n[[nsa-relying-party-registrations-id]]\n* **id**\nThe ID that uniquely identifies the `RelyingPartyRegistrationRepository`.\n\n[[nsa-relying-party-registrations-children]]\n=== Child Elements of <relying-party-registrations>\n\n* <<nsa-asserting-party,asserting-party>>\n* <<nsa-relying-party-registration,relying-party-registration>>\n\n\n[[nsa-relying-party-registration]]\n== <relying-party-registration>\nRepresents a relying party registered with a SAML 2.0 Identity Provider\n\n\n[[nsa-relying-party-registration-parents]]\n=== Parent Elements of <relying-party-registration>\n\n* <<nsa-relying-party-registrations,relying-party-registrations>>\n\n\n[[nsa-relying-party-registration-attributes]]\n=== <relying-party-registration> Attributes\n\n\n[[nsa-relying-party-registration-registration-id]]\n* **registration-id**\nThe ID that uniquely identifies the `RelyingPartyRegistration`.\n\n[[nsa-relying-party-registration-metadata-location]]\n* **metadata-location**\nThe asserting party metadata location.\n\n[[nsa-relying-party-registration-entity-id]]\n* **client-id**\nThe relying party's https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor[EntityID].\n\n\n[[nsa-relying-party-registration-assertion-consumer-service-location]]\n* **assertion-consumer-service-location**\nThe AssertionConsumerService Location. Equivalent to the value found in `&lt;AssertionConsumerService Location=\"...\"/&gt;` in the relying party's `&lt;SPSSODescriptor&gt;`.\n\n\n[[nsa-relying-party-registration-assertion-consumer-service-binding]]\n* **assertion-consumer-service-binding**\nthe AssertionConsumerService Binding. Equivalent to the value found in `&lt;AssertionConsumerService Binding=\"...\"/&gt;` in the relying party's `&lt;SPSSODescriptor&gt;`.\nThe supported values are *POST* and *REDIRECT*.\n\n[[nsa-relying-party-registration-single-logout-service-location]]\n* **single-logout-service-location**\nThe SingleLogoutService Location. Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in the relying party's &lt;SPSSODescriptor&gt;.\n\n[[nsa-relying-party-registration-single-logout-service-response-location]]\n* **single-logout-service-response-location**\nThe SingleLogoutService ResponseLocation. Equivalent to the value found in &lt;SingleLogoutService ResponseLocation=\"...\"/&gt; in the relying party's &lt;SPSSODescriptor&gt;.\n\n[[nsa-relying-party-registration-single-logout-service-binding]]\n* **single-logout-service-binding**\nThe SingleLogoutService Binding. Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in the relying party's &lt;SPSSODescriptor&gt;.\nThe supported values are *POST* and *REDIRECT*.\n\n[[nsa-relying-party-registration-asserting-party-id]]\n* **asserting-party-id**\nA reference to the associated asserting party. Must reference an `<asserting-party>` element.\n\n[[nsa-relying-party-registration-children]]\n=== Child Elements of <relying-party-registration>\n\n* <<nsa-decryption-credential,decryption-credential>>\n* <<nsa-signing-credential,signing-credential>>\n\n\n[[nsa-decryption-credential]]\n== <decryption-credential>\nThe decryption credentials associated with the relying party.\n\n\n[[nsa-decryption-credential-parents]]\n=== Parent Elements of <decryption-credential>\n\n* <<nsa-relying-party-registration,relying-party-registration>>\n\n\n[[nsa-decryption-credential-attributes]]\n=== <decryption-credential> Attributes\n\n\n[[nsa-decryption-credential-certificate-location]]\n* **certificate-location**\nThe location to get the certificate\n\n[[nsa-decryption-credential-private-key-location]]\n* **private-key-location**\nThe location to get the Relying Party's private key\n\n\n[[nsa-signing-credential]]\n== <signing-credential>\nThe signing credentials associated with the relying party.\n\n\n[[nsa-signing-credential-parents]]\n=== Parent Elements of <verification-credential>\n\n* <<nsa-relying-party-registration,relying-party-registration>>\n\n\n[[nsa-signing-credential-attributes]]\n=== <verification-credential> Attributes\n\n\n[[nsa-signing-credential-certificate-location]]\n* **certificate-location**\nThe location to get this certificate\n\n[[nsa-signing-credential-private-key-location]]\n* **private-key-location**\nThe location to get the Relying Party's private key\n\n\n\n[[nsa-asserting-party]]\n== <asserting-party>\nThe configuration information for a SAML 2.0 Asserting Party.\n\n\n[[nsa-asserting-party-parents]]\n=== Parent Elements of <asserting-party>\n\n* <<nsa-relying-party-registrations,relying-party-registrations>>\n\n\n[[nsa-asserting-party-attributes]]\n=== <asserting-party> Attributes\n\n\n[[nsa-asserting-party-asserting-party-id]]\n* **asserting-party-id**\nThe ID that uniquely identifies the asserting party.\n\n\n[[nsa-asserting-party-entity-id]]\n* **entity-id**\nThe EntityID of the Asserting Party\n\n\n[[nsa-asserting-party-want-authn-requests-signed]]\n* **want-authn-requests-signed**\nThe `WantAuthnRequestsSigned` setting, indicating the asserting party's preference that relying parties should sign the `AuthnRequest` before sending.\n\n\n[[nsa-asserting-party-single-sign-on-service-location]]\n* **single-sign-on-service-location**\nThe https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint[SingleSignOnService] Location.\n\n\n[[nsa-asserting-party-single-sign-on-service-binding]]\n* **single-sign-on-service-binding**\nThe https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint[SingleSignOnService] Binding.\nThe supported values are *POST* and *REDIRECT*.\n\n\n[[nsa-asserting-party-signing-algorithms]]\n* **signing-algorithms**\nThe list of `org.opensaml.saml.ext.saml2alg.SigningMethod` Algorithms for this asserting party, in preference order.\n\n\n[[nsa-asserting-party-single-logout-service-location]]\n* **single-logout-service-location**\nThe SingleLogoutService Location. Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in the asserting party's &lt;IDPSSODescriptor&gt;.\n\n\n[[nsa-asserting-party-single-logout-service-response-location]]\n* **single-logout-service-response-location**\nThe SingleLogoutService ResponseLocation. Equivalent to the value found in &lt;SingleLogoutService ResponseLocation=\"...\"/&gt; in the asserting party's &lt;IDPSSODescriptor&gt;.\n\n\n[[nsa-asserting-party-single-logout-service-binding]]\n* **single-logout-service-binding**\nThe SingleLogoutService Binding. Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in the asserting party's &lt;IDPSSODescriptor&gt;.\nThe supported values are *POST* and *REDIRECT*.\n\n\n[[nsa-asserting-party-children]]\n=== Child Elements of <asserting-party>\n\n* <<nsa-encryption-credential,encryption-credential>>\n* <<nsa-verification-credential,verification-credential>>\n\n\n[[nsa-encryption-credential]]\n== <encryption-credential>\nThe encryption credentials associated with the asserting party.\n\n\n[[nsa-encryption-credential-parents]]\n=== Parent Elements of <encryption-credential>\n\n* <<nsa-asserting-party,asserting-party>>\n\n\n[[nsa-encryption-credential-attributes]]\n=== <encryption-credential> Attributes\n\n\n[[nsa-encryption-credential-certificate-location]]\n* **certificate-location**\nThe location to get the certificate\n\n[[nsa-encryption-credential-private-key-location]]\n* **private-key-location**\nThe location to get the Relying Party's private key\n\n\n[[nsa-verification-credential]]\n== <verification-credential>\nThe verification credentials associated with the asserting party.\n\n\n[[nsa-verification-credential-parents]]\n=== Parent Elements of <verification-credential>\n\n* <<nsa-asserting-party,asserting-party>>\n\n\n[[nsa-verification-credential-attributes]]\n=== <verification-credential> Attributes\n\n\n[[nsa-verification-credential-certificate-location]]\n* **certificate-location**\nThe location to get this certificate\n\n[[nsa-verification-credential-private-key-location]]\n* **private-key-location**\nThe location to get the Relying Party's private key\n\n\n\n[[nsa-http-basic]]\n== <http-basic>\nAdds a `BasicAuthenticationFilter` and `BasicAuthenticationEntryPoint` to the configuration.\nThe latter will only be used as the configuration entry point if form-based login is not enabled.\n\n\n[[nsa-http-basic-parents]]\n=== Parent Elements of <http-basic>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-http-basic-attributes]]\n=== <http-basic> Attributes\n\n\n[[nsa-http-basic-authentication-details-source-ref]]\n* **authentication-details-source-ref**\nReference to an `AuthenticationDetailsSource` which will be used by the authentication filter\n\n\n[[nsa-http-basic-entry-point-ref]]\n* **entry-point-ref**\nSets the `AuthenticationEntryPoint` which is used by the `BasicAuthenticationFilter`.\n\n\n[[nsa-http-firewall]]\n== <http-firewall> Element\nThis is a top-level element which can be used to inject a custom implementation of `HttpFirewall` into the `FilterChainProxy` created by the namespace.\nThe default implementation should be suitable for most applications.\n\n\n[[nsa-http-firewall-attributes]]\n=== <http-firewall> Attributes\n\n\n[[nsa-http-firewall-ref]]\n* **ref**\nDefines a reference to a Spring bean that implements `HttpFirewall`.\n\n\n[[nsa-intercept-url]]\n== <intercept-url>\nThis element is used to define the set of URL patterns that the application is interested in and to configure how they should be handled.\nIt is used to construct the `FilterInvocationSecurityMetadataSource` used by the `FilterSecurityInterceptor`.\nIt is also responsible for configuring a `ChannelProcessingFilter` if particular URLs need to be accessed by HTTPS, for example.\nWhen matching the specified patterns against an incoming request, the matching is done in the order in which the elements are declared.\nSo the most specific patterns should come first and the most general should come last.\n\n\n[[nsa-intercept-url-parents]]\n=== Parent Elements of <intercept-url>\n\n\n* <<nsa-filter-security-metadata-source,filter-security-metadata-source>>\n* <<nsa-http,http>>\n\n\n\n[[nsa-intercept-url-attributes]]\n=== <intercept-url> Attributes\n\n\n[[nsa-intercept-url-access]]\n* **access**\nLists the access attributes which will be stored in the `FilterInvocationSecurityMetadataSource` for the defined URL pattern/method combination.\nThis should be a comma-separated list of the security configuration attributes (such as role names).\n\n\n[[nsa-intercept-url-method]]\n* **method**\nThe HTTP Method which will be used in combination with the pattern and servlet path (optional) to match an incoming request.\nIf omitted, any method will match.\nIf an identical pattern is specified with and without a method, the method-specific match will take precedence.\n\n\n[[nsa-intercept-url-pattern]]\n* **pattern**\nThe pattern which defines the URL path.\nThe content will depend on the `request-matcher` attribute from the containing http element, so will default to MVC matcher if Spring MVC is in the classpath.\n\n\n[[nsa-intercept-url-request-matcher-ref]]\n* **request-matcher-ref**\nA reference to a `RequestMatcher` that will be used to determine if this `<intercept-url>` is used.\n\n\n[[nsa-intercept-url-requires-channel]]\n* **requires-channel**\nCan be \"http\" or \"https\" depending on whether a particular URL pattern should be accessed over HTTP or HTTPS respectively.\nAlternatively the value \"any\" can be used when there is no preference.\nIf this attribute is present on any `<intercept-url>` element, then a `ChannelProcessingFilter` will be added to the filter stack and its additional dependencies added to the application context.\n\nIf a `<port-mappings>` configuration is added, this will be used to by the `SecureChannelProcessor` and `InsecureChannelProcessor` beans to determine the ports used for redirecting to HTTP/HTTPS.\n\nNOTE: This property is invalid for <<nsa-filter-security-metadata-source,filter-security-metadata-source>>\n\n[[nsa-intercept-url-servlet-path]]\n* **servlet-path**\nThe servlet path which will be used in combination with the pattern and HTTP method to match an incoming request.\nThis attribute is only applicable when <<nsa-http-request-matcher,request-matcher>> is 'mvc'.\nIn addition, the value is only required in the following 2 use cases: 1) There are 2 or more `HttpServlet` 's registered in the `ServletContext` that have mappings starting with `'/'` and are different; 2) The pattern starts with the same value of a registered `HttpServlet` path, excluding the default (root) `HttpServlet` `'/'`.\n\nNOTE: This property is invalid for <<nsa-filter-security-metadata-source,filter-security-metadata-source>>\n\n\n[[nsa-jee]]\n== <jee>\nAdds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.\n\n\n[[nsa-jee-parents]]\n=== Parent Elements of <jee>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-jee-attributes]]\n=== <jee> Attributes\n\n\n[[nsa-jee-mappable-roles]]\n* **mappable-roles**\nA comma-separate list of roles to look for in the incoming HttpServletRequest.\n\n\n[[nsa-jee-user-service-ref]]\n* **user-service-ref**\nA reference to a user-service (or UserDetailsService bean) Id\n\n\n[[nsa-logout]]\n== <logout>\nAdds a `LogoutFilter` to the filter stack.\nThis is configured with a `SecurityContextLogoutHandler`.\n\n\n[[nsa-logout-parents]]\n=== Parent Elements of <logout>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-logout-attributes]]\n=== <logout> Attributes\n\n\n[[nsa-logout-delete-cookies]]\n* **delete-cookies**\nA comma-separated list of the names of cookies which should be deleted when the user logs out.\n\n\n[[nsa-logout-invalidate-session]]\n* **invalidate-session**\nMaps to the `invalidateHttpSession` of the `SecurityContextLogoutHandler`.\nDefaults to \"true\", so the session will be invalidated on logout.\n\n\n[[nsa-logout-logout-success-url]]\n* **logout-success-url**\nThe destination URL which the user will be taken to after logging out.\nDefaults to <form-login-login-page>/?logout (i.e. /login?logout)\n\n+\n\nSetting this attribute will inject the `SessionManagementFilter` with a `SimpleRedirectInvalidSessionStrategy` configured with the attribute value.\nWhen an invalid session ID is submitted, the strategy will be invoked, redirecting to the configured URL.\n\n\n[[nsa-logout-logout-url]]\n* **logout-url**\nThe URL which will cause a logout (i.e. which will be processed by the filter).\nDefaults to \"/logout\".\n\n\n[[nsa-logout-success-handler-ref]]\n* **success-handler-ref**\nMay be used to supply an instance of `LogoutSuccessHandler` which will be invoked to control the navigation after logging out.\n\n\n[[nsa-saml2-login]]\n== <saml2-login>\nThe xref:servlet/saml2/login/index.adoc#servlet-saml2login[SAML 2.0 Login] feature configures authentication support using an SAML 2.0 Service Provider.\n\n\n[[nsa-saml2-login-parents]]\n=== Parent Elements of <saml2-login>\n\n* <<nsa-http,http>>\n\n[[nsa-saml2-login-attributes]]\n=== <saml2-login> Attributes\n\n\n[[nsa-saml2-login-relying-party-registration-repository-ref]]\n* **relying-party-registration-repository-ref**\nReference to the `RelyingPartyRegistrationRepository`.\n\n\n[[nsa-saml2-login-authentication-request-repository-ref]]\n* **authentication-request-repository-ref**\nReference to the `Saml2AuthenticationRequestRepository`.\n\n\n[[nsa-saml2-login-authentication-request-resolver-ref]]\n* **authentication-request-context-resolver-ref**\nReference to the `Saml2AuthenticationRequestResolver`.\n\n\n[[nsa-saml2-login-authentication-converter-ref]]\n* **authentication-converter-ref**\nReference to the `AuthenticationConverter`.\n\n\n[[nsa-saml2-login-login-processing-url]]\n* **login-processing-url**\nThe URI where the filter processes authentication requests.\n\n\n[[nsa-saml2-login-login-page]]\n* **login-page**\nThe URI to send users to login.\n\n\n[[nsa-saml2-login-authentication-success-handler-ref]]\n* **authentication-success-handler-ref**\nReference to the `AuthenticationSuccessHandler`.\n\n\n[[nsa-saml2-login-authentication-failure-handler-ref]]\n* **authentication-failure-handler-ref**\nReference to the `AuthenticationFailureHandler`.\n\n\n[[nsa-saml2-login-authentication-manager-ref]]\n* **authentication-manager-ref**\nReference to the `AuthenticationManager`.\n\n\n[[nsa-saml2-logout]]\n== <saml2-logout>\nThe xref:servlet/saml2/logout.adoc#servlet-saml2login-logout[SAML 2.0 Single Logout] feature configures support for RP- and AP-initiated SAML 2.0 Single Logout.\n\n\n[[nsa-saml2-logout-parents]]\n=== Parent Elements of <saml2-logout>\n\n* <<nsa-http,http>>\n\n[[nsa-saml2-logout-attributes]]\n=== <saml2-logout> Attributes\n\n\n[[nsa-saml2-logout-logout-url]]\n* **logout-url**\nThe URL by which the relying or asserting party can trigger logout.\n\n\n[[nsa-saml2-logout-logout-request-url]]\n* **logout-request-url**\nThe URL by which the asserting party can send a SAML 2.0 Logout Request.\n\n\n[[nsa-saml2-logout-logout-response-url]]\n* **logout-response-url**\nThe URL by which the asserting party can send a SAML 2.0 Logout Response.\n\n\n[[nsa-saml2-logout-relying-party-registration-repository-ref]]\n* **relying-party-registration-repository-ref**\nReference to the `RelyingPartyRegistrationRepository`.\n\n\n[[nsa-saml2-logout-logout-request-validator-ref]]\n* **logout-request-validator-ref**\nReference to the `Saml2LogoutRequestValidator`.\n\n\n[[nsa-saml2-logout-logout-request-resolver-ref]]\n* **logout-request-resolver-ref**\nReference to the `Saml2LogoutRequestResolver`.\n\n\n[[nsa-saml2-logout-logout-request-repository-ref]]\n* **logout-request-repository-ref**\nReference to the `Saml2LogoutRequestRepository`.\n\n\n[[nsa-saml2-logout-logout-response-validator-ref]]\n* **logout-response-validator-ref**\nReference to the `Saml2LogoutResponseValidator`.\n\n\n[[nsa-saml2-logout-logout-response-resolver-ref]]\n* **logout-response-resolver-ref**\nReference to the `Saml2LogoutResponseResolver`.\n\n\n\n[[nsa-password-management]]\n== <password-management>\nThis element configures password management.\n\n[[nsa-password-management-parents]]\n=== Parent Elements of <password-management>\n\n* <<nsa-http,http>>\n\n[[nsa-password-management-attributes]]\n=== <password-management> Attributes\n\n[[nsa-password-management-change-password-page]]\n* **change-password-page**\nThe change password page. Defaults to \"/change-password\".\n\n[[nsa-port-mappings]]\n== <port-mappings>\nBy default, an instance of `PortMapperImpl` will be added to the configuration for use in redirecting to secure and insecure URLs.\nThis element can optionally be used to override the default mappings which that class defines.\nEach child `<port-mapping>` element defines a pair of HTTP:HTTPS ports.\nThe default mappings are 80:443 and 8080:8443.\nAn example of overriding these can be found in xref:servlet/exploits/http.adoc#servlet-http-redirect[Redirect to HTTPS].\n\n\n[[nsa-port-mappings-parents]]\n=== Parent Elements of <port-mappings>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-port-mappings-children]]\n=== Child Elements of <port-mappings>\n\n\n* <<nsa-port-mapping,port-mapping>>\n\n\n\n[[nsa-port-mapping]]\n== <port-mapping>\nProvides a method to map http ports to https ports when forcing a redirect.\n\n\n[[nsa-port-mapping-parents]]\n=== Parent Elements of <port-mapping>\n\n\n* <<nsa-port-mappings,port-mappings>>\n\n\n\n[[nsa-port-mapping-attributes]]\n=== <port-mapping> Attributes\n\n\n[[nsa-port-mapping-http]]\n* **http**\nThe http port to use.\n\n\n[[nsa-port-mapping-https]]\n* **https**\nThe https port to use.\n\n\n[[nsa-remember-me]]\n== <remember-me>\nAdds the `RememberMeAuthenticationFilter` to the stack.\nThis in turn will be configured with either a `TokenBasedRememberMeServices`, a `PersistentTokenBasedRememberMeServices` or a user-specified bean implementing `RememberMeServices` depending on the attribute settings.\n\n\n[[nsa-remember-me-parents]]\n=== Parent Elements of <remember-me>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-remember-me-attributes]]\n=== <remember-me> Attributes\n\n\n[[nsa-remember-me-authentication-success-handler-ref]]\n* **authentication-success-handler-ref**\nSets the `authenticationSuccessHandler` property on the `RememberMeAuthenticationFilter` if custom navigation is required.\nThe value should be the name of a `AuthenticationSuccessHandler` bean in the application context.\n\n\n[[nsa-remember-me-data-source-ref]]\n* **data-source-ref**\nA reference to a `DataSource` bean.\nIf this is set, `PersistentTokenBasedRememberMeServices` will be used and configured with a `JdbcTokenRepositoryImpl` instance.\n\n\n[[nsa-remember-me-remember-me-parameter]]\n* **remember-me-parameter**\nThe name of the request parameter which toggles remember-me authentication.\nDefaults to \"remember-me\".\nMaps to the \"parameter\" property of `AbstractRememberMeServices`.\n\n\n[[nsa-remember-me-remember-me-cookie]]\n* **remember-me-cookie**\nThe name of cookie which store the token for remember-me authentication.\nDefaults to \"remember-me\".\nMaps to the \"cookieName\" property of `AbstractRememberMeServices`.\n\n\n[[nsa-remember-me-key]]\n* **key**\nMaps to the \"key\" property of `AbstractRememberMeServices`.\nShould be set to a unique value to ensure that remember-me cookies are only valid within the one application footnote:[\nThis doesn't affect the use of `PersistentTokenBasedRememberMeServices`, where the tokens are stored on the server side.\n].\nIf this is not set a secure random value will be generated.\nSince generating secure random values can take a while, setting this value explicitly can help improve startup times when using the remember-me functionality.\n\n\n[[nsa-remember-me-services-alias]]\n* **services-alias**\nExports the internally defined `RememberMeServices` as a bean alias, allowing it to be used by other beans in the application context.\n\n\n[[nsa-remember-me-services-ref]]\n* **services-ref**\nAllows complete control of the `RememberMeServices` implementation that will be used by the filter.\nThe value should be the `id` of a bean in the application context which implements this interface.\nShould also implement `LogoutHandler` if a logout filter is in use.\n\n\n[[nsa-remember-me-token-repository-ref]]\n* **token-repository-ref**\nConfigures a `PersistentTokenBasedRememberMeServices` but allows the use of a custom `PersistentTokenRepository` bean.\n\n\n[[nsa-remember-me-token-validity-seconds]]\n* **token-validity-seconds**\nMaps to the `tokenValiditySeconds` property of `AbstractRememberMeServices`.\nSpecifies the period in seconds for which the remember-me cookie should be valid.\nBy default it will be valid for 14 days.\n\n\n[[nsa-remember-me-use-secure-cookie]]\n* **use-secure-cookie**\nIt is recommended that remember-me cookies are only submitted over HTTPS and thus should be flagged as \"secure\".\nBy default, a secure cookie will be used if the connection over which the login request is made is secure (as it should be).\nIf you set this property to `false`, secure cookies will not be used.\nSetting it to `true` will always set the secure flag on the cookie.\nThis attribute maps to the `useSecureCookie` property of `AbstractRememberMeServices`.\n\n\n[[nsa-remember-me-user-service-ref]]\n* **user-service-ref**\nThe remember-me services implementations require access to a `UserDetailsService`, so there has to be one defined in the application context.\nIf there is only one, it will be selected and used automatically by the namespace configuration.\nIf there are multiple instances, you can specify a bean `id` explicitly using this attribute.\n\n\n[[nsa-request-cache]]\n== <request-cache> Element\nSets the `RequestCache` instance which will be used by the `ExceptionTranslationFilter` to store request information before invoking an `AuthenticationEntryPoint`.\n\n\n[[nsa-request-cache-parents]]\n=== Parent Elements of <request-cache>\n\n* <<nsa-http,http>>\n\n[[nsa-request-cache-attributes]]\n=== <request-cache> Attributes\n\n\n[[nsa-request-cache-ref]]\n* **ref**\nDefines a reference to a Spring bean that is a `RequestCache`.\n\n\n[[nsa-session-management]]\n== <session-management>\nSession-management related functionality is implemented by the addition of a `SessionManagementFilter` to the filter stack.\n\n\n[[nsa-session-management-parents]]\n=== Parent Elements of <session-management>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-session-management-attributes]]\n=== <session-management> Attributes\n\n\n[[nsa-session-management-authentication-strategy-explicit-invocation]]\n* **authentication-strategy-explicit-invocation**\nSetting this attribute to true will mean that `SessionManagementFilter` will not be injected and explicit invocation of SessionAuthenticationStrategy is required.\n\n[[nsa-session-management-invalid-session-url]]\n* **invalid-session-url**\nSetting this attribute will inject the `SessionManagementFilter` with a `SimpleRedirectInvalidSessionStrategy` configured with the attribute value.\nWhen an invalid session ID is submitted, the strategy will be invoked, redirecting to the configured URL.\n\n[[nsa-session-management-invalid-session-strategy-ref]]\n* **invalid-session-url**\nAllows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter.\nUse either this or the `invalid-session-url` attribute but not both.\n\n[[nsa-session-management-session-authentication-error-url]]\n* **session-authentication-error-url**\nDefines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception.\nIf not set, an unauthorized (401) error code will be returned to the client.\nNote that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.\n\n\n[[nsa-session-management-session-authentication-strategy-ref]]\n* **session-authentication-strategy-ref**\nAllows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter\n\n\n[[nsa-session-management-session-fixation-protection]]\n* **session-fixation-protection**\nIndicates how session fixation protection will be applied when a user authenticates.\nIf set to \"none\", no protection will be applied.\n\"newSession\" will create a new empty session, with only Spring Security-related attributes migrated.\n\"migrateSession\" will create a new session and copy all session attributes to the new session.\nIn Servlet 3.1 (Java EE 7) and newer containers, specifying \"changeSessionId\" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()).\nDefaults to \"changeSessionId\" in Servlet 3.1 and newer containers, \"migrateSession\" in older containers.\nThrows an exception if \"changeSessionId\" is used in older containers.\n\n+\n\nIf session fixation protection is enabled, the `SessionManagementFilter` is injected with an appropriately configured `DefaultSessionAuthenticationStrategy`.\nSee the Javadoc for this class for more details.\n\n\n[[nsa-session-management-children]]\n=== Child Elements of <session-management>\n\n\n* <<nsa-concurrency-control,concurrency-control>>\n\n\n\n[[nsa-concurrency-control]]\n== <concurrency-control>\nAdds support for concurrent session control, allowing limits to be placed on the number of active sessions a user can have.\nA `ConcurrentSessionFilter` will be created, and a `ConcurrentSessionControlAuthenticationStrategy` will be used with the `SessionManagementFilter`.\nIf a `form-login` element has been declared, the strategy object will also be injected into the created authentication filter.\nAn instance of `SessionRegistry` (a `SessionRegistryImpl` instance unless the user wishes to use a custom bean) will be created for use by the strategy.\n\n\n[[nsa-concurrency-control-parents]]\n=== Parent Elements of <concurrency-control>\n\n\n* <<nsa-session-management,session-management>>\n\n\n\n[[nsa-concurrency-control-attributes]]\n=== <concurrency-control> Attributes\n\n\n[[nsa-concurrency-control-error-if-maximum-exceeded]]\n* **error-if-maximum-exceeded**\nIf set to \"true\" a `SessionAuthenticationException` will be raised when a user attempts to exceed the maximum allowed number of sessions.\nThe default behaviour is to expire the original session.\n\n\n[[nsa-concurrency-control-expired-url]]\n* **expired-url**\nThe URL a user will be redirected to if they attempt to use a session which has been \"expired\" by the concurrent session controller because the user has exceeded the number of allowed sessions and has logged in again elsewhere.\nShould be set unless `exception-if-maximum-exceeded` is set.\nIf no value is supplied, an expiry message will just be written directly back to the response.\n\n[[nsa-concurrency-control-expired-session-strategy-ref]]\n* **expired-url**\nAllows injection of the ExpiredSessionStrategy instance used by the ConcurrentSessionFilter\n\n[[nsa-concurrency-control-max-sessions]]\n* **max-sessions**\nMaps to the `maximumSessions` property of `ConcurrentSessionControlAuthenticationStrategy`.\nSpecify `-1` as the value to support unlimited sessions.\n\n[[nsa-concurrency-control-max-sessions-ref]]\n* **max-sessions-ref**\nAllows injection of the SessionLimit instance used by the ConcurrentSessionControlAuthenticationStrategy\n\n[[nsa-concurrency-control-session-registry-alias]]\n* **session-registry-alias**\nIt can also be useful to have a reference to the internal session registry for use in your own beans or an admin interface.\nYou can expose the internal bean using the `session-registry-alias` attribute, giving it a name that you can use elsewhere in your configuration.\n\n\n[[nsa-concurrency-control-session-registry-ref]]\n* **session-registry-ref**\nThe user can supply their own `SessionRegistry` implementation using the `session-registry-ref` attribute.\nThe other concurrent session control beans will be wired up to use it.\n\n\n[[nsa-x509]]\n== <x509>\nAdds support for X.509 authentication.\nAn `X509AuthenticationFilter` will be added to the stack and an `Http403ForbiddenEntryPoint` bean will be created.\nThe latter will only be used if no other authentication mechanisms are in use (its only functionality is to return an HTTP 403 error code).\nA `PreAuthenticatedAuthenticationProvider` will also be created which delegates the loading of user authorities to a `UserDetailsService`.\n\n\n[[nsa-x509-parents]]\n=== Parent Elements of <x509>\n\n\n* <<nsa-http,http>>\n\n\n\n[[nsa-x509-attributes]]\n=== <x509> Attributes\n\n\n[[nsa-x509-authentication-details-source-ref]]\n* **authentication-details-source-ref**\nA reference to an `AuthenticationDetailsSource`\n\n[[nsa-x509-principal-extractor-ref]]\n* **principal-extractor-ref**\nReference to an `X509PrincipalExtractor` which will be used by the authentication filter.\n\n\n[[nsa-x509-subject-principal-regex]]\n* **subject-principal-regex**\nDefines a regular expression which will be used to extract the username from the certificate (for use with the `UserDetailsService`).\n\n\n[[nsa-x509-user-service-ref]]\n* **user-service-ref**\nAllows a specific `UserDetailsService` to be used with X.509 in the case where multiple instances are configured.\nIf not set, an attempt will be made to locate a suitable instance automatically and use that.\n\n\n[[nsa-filter-chain-map]]\n== <filter-chain-map>\nUsed to explicitly configure a FilterChainProxy instance with a FilterChainMap\n\n\n[[nsa-filter-chain-map-attributes]]\n=== <filter-chain-map> Attributes\n\n\n[[nsa-filter-chain-map-request-matcher]]\n* **request-matcher**\nDefines the strategy to use for matching incoming requests.\nCurrently the options are 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\n\n[[nsa-filter-chain-map-children]]\n=== Child Elements of <filter-chain-map>\n\n\n* <<nsa-filter-chain,filter-chain>>\n\n\n\n[[nsa-filter-chain]]\n== <filter-chain>\nUsed within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern.\nWhen multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with most general ones at the bottom.\n\n\n[[nsa-filter-chain-parents]]\n=== Parent Elements of <filter-chain>\n\n\n* <<nsa-filter-chain-map,filter-chain-map>>\n\n\n\n[[nsa-filter-chain-attributes]]\n=== <filter-chain> Attributes\n\n\n[[nsa-filter-chain-filters]]\n* **filters**\nA comma separated list of references to Spring beans that implement `Filter`.\nThe value \"none\" means that no `Filter` should be used for this `FilterChain`.\n\n\n[[nsa-filter-chain-pattern]]\n* **pattern**\nA pattern that creates RequestMatcher in combination with the <<nsa-filter-chain-map-request-matcher,request-matcher>>\n\n\n[[nsa-filter-chain-request-matcher-ref]]\n* **request-matcher-ref**\nA reference to a `RequestMatcher` that will be used to determine if any `Filter` from the `filters` attribute should be invoked.\n\n\n[[nsa-filter-security-metadata-source]]\n== <filter-security-metadata-source>\nUsed to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor.\nUsually only needed if you are configuring a FilterChainProxy explicitly, rather than using the<http> element.\nThe intercept-url elements used should only contain pattern, method and access attributes.\nAny others will result in a configuration error.\n\n\n[[nsa-filter-security-metadata-source-attributes]]\n=== <filter-security-metadata-source> Attributes\n\n\n[[nsa-filter-security-metadata-source-id]]\n* **id**\nA bean identifier, used for referring to the bean elsewhere in the context.\n\n\n[[nsa-filter-security-metadata-source-request-matcher]]\n* **request-matcher**\nDefines the strategy use for matching incoming requests.\nCurrently the options are 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.\n\n\n[[nsa-filter-security-metadata-source-use-expressions]]\n* **use-expressions**\nEnables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes.\nDefaults to 'true'.\nIf enabled, each attribute should contain a single Boolean expression.\nIf the expression evaluates to 'true', access will be granted.\n\n\n[[nsa-filter-security-metadata-source-children]]\n=== Child Elements of <filter-security-metadata-source>\n\n\n* <<nsa-intercept-url,intercept-url>>\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/namespace/index.adoc",
    "content": "[[appendix-namespace]]\n= The Security Namespace\n:page-section-summary-toc: 1\n\nThis appendix provides a reference to the elements available in the security namespace and information on the underlying beans they create (a knowledge of the individual classes and how they work together is assumed - you can find more information in the project Javadoc and elsewhere in this document).\nIf you haven't used the namespace before, please read the xref:servlet/configuration/xml-namespace.adoc#ns-config[introductory chapter] on namespace configuration, as this is intended as a supplement to the information there.\nUsing a good quality XML editor while editing a configuration based on the schema is recommended as this will provide contextual information on which elements and attributes are available as well as comments explaining their purpose.\nThe namespace is written in https://relaxng.org/[RELAX NG] Compact format and later converted into an XSD schema.\nIf you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-7.0.rnc[schema file] directly.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/namespace/ldap.adoc",
    "content": "[[nsa-ldap]]\n= LDAP Namespace Options\nLDAP is covered in some details in xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap[its own chapter].\nWe will expand on that here with some explanation of how the namespace options map to Spring beans.\nThe LDAP implementation uses Spring LDAP extensively, so some familiarity with that project's API may be useful.\n\n\n[[nsa-ldap-server]]\n== Defining the LDAP Server using the\n`<ldap-server>` Element\nThis element sets up a Spring LDAP `ContextSource` for use by the other LDAP beans, defining the location of the LDAP server and other information (such as a username and password, if it doesn't allow anonymous access) for connecting to it.\nIt can also be used to create an embedded server for testing.\nDetails of the syntax for both options are covered in the xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap[LDAP chapter].\nThe actual `ContextSource` implementation is `DefaultSpringSecurityContextSource` which extends Spring LDAP's `LdapContextSource` class.\nThe `manager-dn` and `manager-password` attributes map to the latter's `userDn` and `password` properties respectively.\n\nIf you only have one server defined in your application context, the other LDAP namespace-defined beans will use it automatically.\nOtherwise, you can give the element an \"id\" attribute and refer to it from other namespace beans using the `server-ref` attribute.\nThis is actually the bean `id` of the `ContextSource` instance, if you want to use it in other traditional Spring beans.\n\n\n[[nsa-ldap-server-attributes]]\n=== <ldap-server> Attributes\n\n[[nsa-ldap-server-mode]]\n* **mode**\nExplicitly specifies which embedded ldap server should use. The only supported value is `unboundid`. By default, it will depends if the library is available in the classpath.\n\n[[nsa-ldap-server-id]]\n* **id**\nA bean identifier, used for referring to the bean elsewhere in the context.\n\n\n[[nsa-ldap-server-ldif]]\n* **ldif**\nExplicitly specifies an ldif file resource to load into an embedded LDAP server.\nThe ldif should be a Spring resource pattern (i.e. classpath:init.ldif).\nThe default is classpath*:*.ldif\n\n\n[[nsa-ldap-server-manager-dn]]\n* **manager-dn**\nUsername (DN) of the \"manager\" user identity which will be used to authenticate to a (non-embedded) LDAP server.\nIf omitted, anonymous access will be used.\n\n\n[[nsa-ldap-server-manager-password]]\n* **manager-password**\nThe password for the manager DN.\nThis is required if the manager-dn is specified.\n\n\n[[nsa-ldap-server-port]]\n* **port**\nSpecifies an IP port number.\nUsed to configure an embedded LDAP server, for example.\nThe default value is 33389.\n\n\n[[nsa-ldap-server-root]]\n* **root**\nOptional root suffix for the embedded LDAP server.\nDefault is \"dc=springframework,dc=org\"\n\n\n[[nsa-ldap-server-url]]\n* **url**\nSpecifies the ldap server URL when not using the embedded LDAP server.\n\n\n[[nsa-ldap-authentication-provider]]\n== <ldap-authentication-provider>\nThis element is shorthand for the creation of an `LdapAuthenticationProvider` instance.\nBy default this will be configured with a `BindAuthenticator` instance and a `DefaultAuthoritiesPopulator`.\nAs with all namespace authentication providers, it must be included as a child of the `authentication-provider` element.\n\n\n[[nsa-ldap-authentication-provider-parents]]\n=== Parent Elements of <ldap-authentication-provider>\n\n\n* xref:servlet/appendix/namespace/authentication-manager.adoc#nsa-authentication-manager[authentication-manager]\n\n\n\n[[nsa-ldap-authentication-provider-attributes]]\n=== <ldap-authentication-provider> Attributes\n\n\n[[nsa-ldap-authentication-provider-group-role-attribute]]\n* **group-role-attribute**\nThe LDAP attribute name which contains the role name which will be used within Spring Security.\nMaps to the ``DefaultLdapAuthoritiesPopulator``'s `groupRoleAttribute` property.\nDefaults to \"cn\".\n\n\n[[nsa-ldap-authentication-provider-group-search-base]]\n* **group-search-base**\nSearch base for group membership searches.\nMaps to the ``DefaultLdapAuthoritiesPopulator``'s `groupSearchBase` constructor argument.\nDefaults to \"\" (searching from the root).\n\n\n[[nsa-ldap-authentication-provider-group-search-filter]]\n* **group-search-filter**\nGroup search filter.\nMaps to the ``DefaultLdapAuthoritiesPopulator``'s `groupSearchFilter` property.\nDefaults to `+(uniqueMember={0})+`.\nThe substituted parameter is the DN of the user.\n\n\n[[nsa-ldap-authentication-provider-role-prefix]]\n* **role-prefix**\nA non-empty string prefix that will be added to role strings loaded from persistent.\nMaps to the ``DefaultLdapAuthoritiesPopulator``'s `rolePrefix` property.\nDefaults to \"ROLE_\".\nUse the value \"none\" for no prefix in cases where the default is non-empty.\n\n\n[[nsa-ldap-authentication-provider-server-ref]]\n* **server-ref**\nThe optional server to use.\nIf omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\n\n[[nsa-ldap-authentication-provider-user-context-mapper-ref]]\n* **user-context-mapper-ref**\nAllows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\n\n[[nsa-ldap-authentication-provider-user-details-class]]\n* **user-details-class**\nAllows the objectClass of the user entry to be specified.\nIf set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\n\n[[nsa-ldap-authentication-provider-user-dn-pattern]]\n* **user-dn-pattern**\nIf your users are at a fixed location in the directory (i.e. you can work out the DN directly from the username without doing a directory search), you can use this attribute to map directly to the DN.\nIt maps directly to the `userDnPatterns` property of `AbstractLdapAuthenticator`.\nThe value is a specific pattern used to build the user's DN, for example `+uid={0},ou=people+`.\nThe key `+{0}+` must be present and will be substituted with the username.\n\n\n[[nsa-ldap-authentication-provider-user-search-base]]\n* **user-search-base**\nSearch base for user searches.\nDefaults to \"\".\nOnly used with a 'user-search-filter'.\n\n+\n\nIf you need to perform a search to locate the user in the directory, then you can set these attributes to control the search.\nThe `BindAuthenticator` will be configured with a `FilterBasedLdapUserSearch` and the attribute values map directly to the first two arguments of that bean's constructor.\nIf these attributes aren't set and no `user-dn-pattern` has been supplied as an alternative, then the default search values of `+user-search-filter=\"(uid={0})\"+` and `user-search-base=\"\"` will be used.\n\n\n[[nsa-ldap-authentication-provider-user-search-filter]]\n* **user-search-filter**\nThe LDAP filter used to search for users (optional).\nFor example `+(uid={0})+`.\nThe substituted parameter is the user's login name.\n\n+\n\nIf you need to perform a search to locate the user in the directory, then you can set these attributes to control the search.\nThe `BindAuthenticator` will be configured with a `FilterBasedLdapUserSearch` and the attribute values map directly to the first two arguments of that bean's constructor.\nIf these attributes aren't set and no `user-dn-pattern` has been supplied as an alternative, then the default search values of `+user-search-filter=\"(uid={0})\"+` and `user-search-base=\"\"` will be used.\n\n\n[[nsa-ldap-authentication-provider-children]]\n=== Child Elements of <ldap-authentication-provider>\n\n\n* <<nsa-password-compare,password-compare>>\n\n\n\n[[nsa-password-compare]]\n== <password-compare>\nThis is used as child element to `<ldap-provider>` and switches the authentication strategy from `BindAuthenticator` to `PasswordComparisonAuthenticator`.\n\n\n[[nsa-password-compare-parents]]\n=== Parent Elements of <password-compare>\n\n\n* <<nsa-ldap-authentication-provider,ldap-authentication-provider>>\n\n\n\n[[nsa-password-compare-attributes]]\n=== <password-compare> Attributes\n\n\n[[nsa-password-compare-hash]]\n* **hash**\nDefines the hashing algorithm used on user passwords.\nWe recommend strongly against using MD4, as it is a very weak hashing algorithm.\n\n\n[[nsa-password-compare-password-attribute]]\n* **password-attribute**\nThe attribute in the directory which contains the user password.\nDefaults to \"userPassword\".\n\n\n[[nsa-password-compare-children]]\n=== Child Elements of <password-compare>\n\n\n* xref:servlet/appendix/namespace/authentication-manager.adoc#nsa-password-encoder[password-encoder]\n\n\n\n[[nsa-ldap-user-service]]\n== <ldap-user-service>\nThis element configures an LDAP `UserDetailsService`.\nThe class used is `LdapUserDetailsService` which is a combination of a `FilterBasedLdapUserSearch` and a `DefaultLdapAuthoritiesPopulator`.\nThe attributes it supports have the same usage as in `<ldap-provider>`.\n\n\n[[nsa-ldap-user-service-attributes]]\n=== <ldap-user-service> Attributes\n\n\n[[nsa-ldap-user-service-cache-ref]]\n* **cache-ref**\nDefines a reference to a cache for use with a UserDetailsService.\n\n\n[[nsa-ldap-user-service-group-role-attribute]]\n* **group-role-attribute**\nThe LDAP attribute name which contains the role name which will be used within Spring Security.\nDefaults to \"cn\".\n\n\n[[nsa-ldap-user-service-group-search-base]]\n* **group-search-base**\nSearch base for group membership searches.\nDefaults to \"\" (searching from the root).\n\n\n[[nsa-ldap-user-service-group-search-filter]]\n* **group-search-filter**\nGroup search filter.\nDefaults to `+(uniqueMember={0})+`.\nThe substituted parameter is the DN of the user.\n\n\n[[nsa-ldap-user-service-id]]\n* **id**\nA bean identifier, used for referring to the bean elsewhere in the context.\n\n\n[[nsa-ldap-user-service-role-prefix]]\n* **role-prefix**\nA non-empty string prefix that will be added to role strings loaded from persistent storage (e.g.\n\"ROLE_\").\nUse the value \"none\" for no prefix in cases where the default is non-empty.\n\n\n[[nsa-ldap-user-service-server-ref]]\n* **server-ref**\nThe optional server to use.\nIf omitted, and a default LDAP server is registered (using <ldap-server> with no Id), that server will be used.\n\n\n[[nsa-ldap-user-service-user-context-mapper-ref]]\n* **user-context-mapper-ref**\nAllows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry\n\n\n[[nsa-ldap-user-service-user-details-class]]\n* **user-details-class**\nAllows the objectClass of the user entry to be specified.\nIf set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object\n\n\n[[nsa-ldap-user-service-user-search-base]]\n* **user-search-base**\nSearch base for user searches.\nDefaults to \"\".\nOnly used with a 'user-search-filter'.\n\n\n[[nsa-ldap-user-service-user-search-filter]]\n* **user-search-filter**\nThe LDAP filter used to search for users (optional).\nFor example `+(uid={0})+`.\nThe substituted parameter is the user's login name.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/namespace/method-security.adoc",
    "content": "= Method Security\n\n[[nsa-method-security]]\n== <method-security>\nThis element is the primary means of adding support for securing methods on Spring Security beans.\nMethods can be secured by the use of annotations (defined at the interface or class level) or by defining a set of pointcuts.\n\n[[nsa-method-security-attributes]]\n=== <method-security> attributes\n\n[[nsa-method-security-pre-post-enabled]]\n* **pre-post-enabled**\nEnables Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) for this application context.\nDefaults to \"true\".\n\n[[nsa-method-security-secured-enabled]]\n* **secured-enabled**\nEnables Spring Security's @Secured annotation for this application context.\nDefaults to \"false\".\n\n[[nsa-method-security-jsr250-enabled]]\n* **jsr250-enabled**\nEnables JSR-250 authorization annotations (@RolesAllowed, @PermitAll, @DenyAll) for this application context.\nDefaults to \"false\".\n\n[[nsa-method-security-mode]]\n* **mode**\nIf set to \"aspectj\", then uses AspectJ to intercept method invocations.\n\n[[nsa-method-security-proxy-target-class]]\n* **proxy-target-class**\nIf true, class based proxying will be used instead of interface based proxying.\nDefaults to \"false\".\n\n[[nsa-method-security-security-context-holder-strategy-ref]]\n* **security-context-holder-strategy-ref**\nSpecifies a SecurityContextHolderStrategy to use when retrieving the SecurityContext.\nDefaults to the value returned by SecurityContextHolder.getContextHolderStrategy().\n\n[[nsa-method-security-observation-registry-ref]]\n* **observation-registry-ref**\nA reference to the `ObservationRegistry` used for the `FilterChain` and related components\n\n[[nsa-method-security-children]]\n=== Child Elements of <method-security>\n\n* xref:servlet/appendix/namespace/http.adoc#nsa-expression-handler[expression-handler]\n* <<nsa-protect-pointcut,protect-pointcut>>\n\n[[nsa-global-method-security]]\n== <global-method-security>\nThis element is the primary means of adding support for securing methods on Spring Security beans.\nMethods can be secured by the use of annotations (defined at the interface or class level) or by defining a set of pointcuts as child elements, using AspectJ syntax.\n\n\n[[nsa-global-method-security-attributes]]\n=== <global-method-security> Attributes\n\n[NOTE]\n=====\n`<global-method-security>` is deprecated in favor of `<method-security>`.\nIf you need to use `<global-method-security>`, please include the `spring-security-access` dependency in your build configuration.\n=====\n\n[[nsa-global-method-security-access-decision-manager-ref]]\n* **access-decision-manager-ref**\nMethod security uses the same `AccessDecisionManager` configuration as web security, but this can be overridden using this attribute.\nBy default an AffirmativeBased implementation is used for with a RoleVoter and an AuthenticatedVoter.\n\n\n[[nsa-global-method-security-authentication-manager-ref]]\n* **authentication-manager-ref**\nA reference to an `AuthenticationManager` that should be used for method security.\n\n\n[[nsa-global-method-security-jsr250-annotations]]\n* **jsr250-annotations**\nSpecifies whether JSR-250 style attributes are to be used (for example \"RolesAllowed\").\nThis will require the javax.annotation.security classes on the classpath.\nSetting this to true also adds a `Jsr250Voter` to the `AccessDecisionManager`, so you need to make sure you do this if you are using a custom implementation and want to use these annotations.\n\n\n[[nsa-global-method-security-metadata-source-ref]]\n* **metadata-source-ref**\nAn external `MethodSecurityMetadataSource` instance can be supplied which will take priority over other sources (such as the default annotations).\n\n\n[[nsa-global-method-security-mode]]\n* **mode**\nThis attribute can be set to \"aspectj\" to specify that AspectJ should be used instead of the default Spring AOP.\nSecured methods must be woven with the `AnnotationSecurityAspect` from the `spring-security-aspects` module.\n\nIt is important to note that AspectJ follows Java's rule that annotations on interfaces are not inherited.\nThis means that methods that define the Security annotations on the interface will not be secured.\nInstead, you must place the Security annotation on the class when using AspectJ.\n\n\n[[nsa-global-method-security-order]]\n* **order**\nAllows the advice \"order\" to be set for the method security interceptor.\n\n\n[[nsa-global-method-security-pre-post-annotations]]\n* **pre-post-annotations**\nSpecifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context.\nDefaults to \"disabled\".\n\n\n[[nsa-global-method-security-proxy-target-class]]\n* **proxy-target-class**\nIf true, class based proxying will be used instead of interface based proxying.\n\n\n[[nsa-global-method-security-run-as-manager-ref]]\n* **run-as-manager-ref**\nA reference to an optional `RunAsManager` implementation which will be used by the configured `MethodSecurityInterceptor`\n\n\n[[nsa-global-method-security-secured-annotations]]\n* **secured-annotations**\nSpecifies whether the use of Spring Security's @Secured annotations should be enabled for this application context.\nDefaults to \"disabled\".\n\n\n[[nsa-global-method-security-children]]\n=== Child Elements of <global-method-security>\n\n\n* <<nsa-after-invocation-provider,after-invocation-provider>>\n* xref:servlet/appendix/namespace/http.adoc#nsa-expression-handler[expression-handler]\n* <<nsa-pre-post-annotation-handling,pre-post-annotation-handling>>\n* <<nsa-protect-pointcut,protect-pointcut>>\n\n\n\n[[nsa-after-invocation-provider]]\n== <after-invocation-provider>\nThis element can be used to decorate an `AfterInvocationProvider` for use by the security interceptor maintained by the `<global-method-security>` namespace.\nYou can define zero or more of these within the `global-method-security` element, each with a `ref` attribute pointing to an `AfterInvocationProvider` bean instance within your application context.\n\n\n[[nsa-after-invocation-provider-parents]]\n=== Parent Elements of <after-invocation-provider>\n\n\n* <<nsa-global-method-security,global-method-security>>\n\n\n\n[[nsa-after-invocation-provider-attributes]]\n=== <after-invocation-provider> Attributes\n\n[NOTE]\n=====\n`<after-invocation-provider>` is deprecated in favor of `<method-security>` and xref:servlet/authorization/method-security.adoc[`@PostFilter` and `@PostAuthorize`].\nIf you need to use `<after-invocation-provider>`, please include the `spring-security-access` dependency in your build configuration while planning to migrate to a modern option.\n=====\n\n[[nsa-after-invocation-provider-ref]]\n* **ref**\nDefines a reference to a Spring bean that implements `AfterInvocationProvider`.\n\n\n[[nsa-pre-post-annotation-handling]]\n== <pre-post-annotation-handling>\nAllows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replaced entirely.\nOnly applies if these annotations are enabled.\n\n\n[[nsa-pre-post-annotation-handling-parents]]\n=== Parent Elements of <pre-post-annotation-handling>\n\n\n* <<nsa-global-method-security,global-method-security>>\n\n\n\n[[nsa-pre-post-annotation-handling-children]]\n=== Child Elements of <pre-post-annotation-handling>\n\n\n* <<nsa-invocation-attribute-factory,invocation-attribute-factory>>\n* <<nsa-post-invocation-advice,post-invocation-advice>>\n* <<nsa-pre-invocation-advice,pre-invocation-advice>>\n\n\n\n[[nsa-invocation-attribute-factory]]\n== <invocation-attribute-factory>\nDefines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods.\n\n[NOTE]\n=====\n`<invocation-attribute-factory>` is deprecated in favor of `<method-security>` and xref:servlet/authorization/method-security.adoc[`@PostFilter` and `@PostAuthorize`].\nIf you need to use `<invocation-attribute-factory>`, please include the `spring-security-access` dependency in your build configuration while planning to migrate to a modern option.\n=====\n\n[[nsa-invocation-attribute-factory-parents]]\n=== Parent Elements of <invocation-attribute-factory>\n\n\n* <<nsa-pre-post-annotation-handling,pre-post-annotation-handling>>\n\n\n\n[[nsa-invocation-attribute-factory-attributes]]\n=== <invocation-attribute-factory> Attributes\n\n\n[[nsa-invocation-attribute-factory-ref]]\n* **ref**\nDefines a reference to a Spring bean Id.\n\n\n[[nsa-post-invocation-advice]]\n== <post-invocation-advice>\nCustomizes the `PostInvocationAdviceProvider` with the ref as the `PostInvocationAuthorizationAdvice` for the <pre-post-annotation-handling> element.\n\n[NOTE]\n=====\n`<post-invocation-advice>` is deprecated in favor of `<method-security>` and xref:servlet/authorization/method-security.adoc[`@PostFilter` and `@PostAuthorize`].\nIf you need to use `<post-invocation-advice>`, please include the `spring-security-access` dependency in your build configuration while planning to migrate to a modern option.\n=====\n\n[[nsa-post-invocation-advice-parents]]\n=== Parent Elements of <post-invocation-advice>\n\n\n* <<nsa-pre-post-annotation-handling,pre-post-annotation-handling>>\n\n\n\n[[nsa-post-invocation-advice-attributes]]\n=== <post-invocation-advice> Attributes\n\n\n[[nsa-post-invocation-advice-ref]]\n* **ref**\nDefines a reference to a Spring bean Id.\n\n\n[[nsa-pre-invocation-advice]]\n== <pre-invocation-advice>\nCustomizes the `PreInvocationAuthorizationAdviceVoter` with the ref as the `PreInvocationAuthorizationAdviceVoter` for the <pre-post-annotation-handling> element.\n\n[NOTE]\n=====\n`<pre-invocation-advice>` is deprecated in favor of `<method-security>` and xref:servlet/authorization/method-security.adoc[`@PreFilter` and `@PreAuthorize`].\nIf you need to use `<pre-invocation-advice>`, please include the `spring-security-access` dependency in your build configuration while planning to migrate to a modern option.\n=====\n\n[[nsa-pre-invocation-advice-parents]]\n=== Parent Elements of <pre-invocation-advice>\n\n\n* <<nsa-pre-post-annotation-handling,pre-post-annotation-handling>>\n\n\n\n[[nsa-pre-invocation-advice-attributes]]\n=== <pre-invocation-advice> Attributes\n\n\n[[nsa-pre-invocation-advice-ref]]\n* **ref**\nDefines a reference to a Spring bean Id.\n\n\n[[nsa-protect-pointcut]]\n== Securing Methods using\n`<protect-pointcut>`\nRather than defining security attributes on an individual method or class basis using the `@Secured` annotation, you can define cross-cutting security constraints across whole sets of methods and interfaces in your service layer using the `<protect-pointcut>` element.\nYou can find an example in the xref:servlet/authorization/method-security.adoc#ns-protect-pointcut[namespace introduction].\n\n[[nsa-protect-pointcut-parents]]\n=== Parent Elements of <protect-pointcut>\n\n\n* <<nsa-global-method-security,global-method-security>>\n* <<nsa-method-security,method-security>>\n\n\n\n[[nsa-protect-pointcut-attributes]]\n=== <protect-pointcut> Attributes\n\n\n[[nsa-protect-pointcut-access]]\n* **access**\nAccess configuration attributes list that applies to all methods matching the pointcut, e.g.\n\"ROLE_A,ROLE_B\"\n\n\n[[nsa-protect-pointcut-expression]]\n* **expression**\nAn AspectJ expression, including the `execution` keyword.\nFor example, `execution(int com.foo.TargetObject.countLength(String))`.\n\n\n[[nsa-intercept-methods]]\n== <intercept-methods>\nCan be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods\n\n\n[[nsa-intercept-methods-attributes]]\n=== <intercept-methods> Attributes\n\n[[nsa-intercept-methods-use-authorization-manager]]\n* **use-authorization-manager**\nUse AuthorizationManager API instead of AccessDecisionManager (defaults to true)\n\n[[nsa-intercept-methods-authorization-manager-ref]]\n* **authorization-manager-ref**\nOptional AuthorizationManager bean ID to be used instead of the default (supersedes use-authorization-manager)\n\n[[nsa-intercept-methods-access-decision-manager-ref]]\n* **access-decision-manager-ref**\nOptional AccessDecisionManager bean ID to be used by the created method security interceptor.\n\n[[nsa-intercept-methods-children]]\n=== Child Elements of <intercept-methods>\n\n\n* <<nsa-protect,protect>>\n\n\n\n[[nsa-method-security-metadata-source]]\n== <method-security-metadata-source>\nCreates a MethodSecurityMetadataSource instance\n\n[NOTE]\n=====\n`<method-security-metadata-source>` is deprecated in favor of xref:servlet/authorization/method-security.adoc[`<method-security>`].\nIf you need to use `<method-security-metadata-source>`, please include the `spring-security-access` dependency in your build configuration while planning to migrate to a modern option.\n=====\n\n[[nsa-method-security-metadata-source-attributes]]\n=== <method-security-metadata-source> Attributes\n\n\n[[nsa-method-security-metadata-source-id]]\n* **id**\nA bean identifier, used for referring to the bean elsewhere in the context.\n\n\n[[nsa-method-security-metadata-source-use-expressions]]\n* **use-expressions**\nEnables the use of expressions in the 'access' attributes in <intercept-url> elements rather than the traditional list of configuration attributes.\nDefaults to 'false'.\nIf enabled, each attribute should contain a single Boolean expression.\nIf the expression evaluates to 'true', access will be granted.\n\n\n[[nsa-method-security-metadata-source-children]]\n=== Child Elements of <method-security-metadata-source>\n\n\n* <<nsa-protect,protect>>\n\n\n\n[[nsa-protect]]\n== <protect>\nDefines a protected method and the access control configuration attributes that apply to it.\nWe strongly advise you NOT to mix \"protect\" declarations with any services provided \"global-method-security\".\n\n\n[[nsa-protect-parents]]\n=== Parent Elements of <protect>\n\n\n* <<nsa-intercept-methods,intercept-methods>>\n* <<nsa-method-security-metadata-source,method-security-metadata-source>>\n\n\n\n[[nsa-protect-attributes]]\n=== <protect> Attributes\n\n\n[[nsa-protect-access]]\n* **access**\nAccess configuration attributes list that applies to the method, e.g.\n\"ROLE_A,ROLE_B\".\n\n\n[[nsa-protect-method]]\n* **method**\nA method name\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/namespace/websocket.adoc",
    "content": "[[nsa-websocket-security]]\n= WebSocket Security\n\nSpring Security 4.0+ provides support for authorizing messages.\nOne concrete example of where this is useful is to provide authorization in WebSocket based applications.\n\n[[nsa-websocket-message-broker]]\n== <websocket-message-broker>\n\nThe websocket-message-broker element has two different modes.\nIf the <<nsa-websocket-message-broker-id,websocket-message-broker@id>> is not specified, then it will do the following things:\n\n* Ensure that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver.\nThis allows the use of `@AuthenticationPrincipal` to resolve the principal of the current `Authentication`\n* Ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel.\nThis populates the SecurityContextHolder with the user that is found in the Message\n* Ensures that a ChannelSecurityInterceptor is registered with the clientInboundChannel.\nThis allows authorization rules to be specified for a message.\n* Ensures that a CsrfChannelInterceptor is registered with the clientInboundChannel.\nThis ensures that only requests from the original domain are enabled.\n* Ensures that a CsrfTokenHandshakeInterceptor is registered with WebSocketHttpRequestHandler, TransportHandlingSockJsService, or DefaultSockJsService.\nThis ensures that the expected CsrfToken from the HttpServletRequest is copied into the WebSocket Session attributes.\n\nIf additional control is necessary, the id can be specified and a ChannelSecurityInterceptor will be assigned to the specified id.\nAll the wiring with Spring's messaging infrastructure can then be done manually.\nThis is more cumbersome, but provides greater control over the configuration.\n\n\n[[nsa-websocket-message-broker-attributes]]\n=== <websocket-message-broker> Attributes\n\n[[nsa-websocket-message-broker-id]]\n* **id** A bean identifier, used for referring to the ChannelSecurityInterceptor bean elsewhere in the context.\nIf specified, Spring Security requires explicit configuration within Spring Messaging.\nIf not specified, Spring Security will automatically integrate with the messaging infrastructure as described in <<nsa-websocket-message-broker>>\n\n[[nsa-websocket-message-broker-same-origin-disabled]]\n* **same-origin-disabled** Disables the requirement for CSRF token to be present in the Stomp headers (default false).\nChanging the default is useful if it is necessary to allow other origins to make SockJS connections.\n\n[[nsa-websocket-message-broker-authorization-manager-ref]]\n* **authorization-manager-ref** Use this `AuthorizationManager` instance; when set, `use-authorization-manager` is ignored and assumed to be `true`\n\n[[nsa-websocket-message-broker-use-authorization-manager]]\n* **use-authorization-manager** Use `AuthorizationManager` API instead of `SecurityMetadataSource` API (defaults to true).\n\n[[nsa-websocket-message-broker-security-context-holder-strategy-ref]]\n* **security-context-holder-strategy-ref** Use this `SecurityContextHolderStrategy` (note only supported in conjunction with the `AuthorizationManager` API)\n\n[[nsa-websocket-message-broker-children]]\n=== Child Elements of <websocket-message-broker>\n\n\n* xref:servlet/appendix/namespace/http.adoc#nsa-expression-handler[expression-handler]\n* <<nsa-intercept-message,intercept-message>>\n\n[[nsa-intercept-message]]\n== <intercept-message>\n\nDefines an authorization rule for a message.\n\n\n[[nsa-intercept-message-parents]]\n=== Parent Elements of <intercept-message>\n\n\n* <<nsa-websocket-message-broker,websocket-message-broker>>\n\n\n[[nsa-intercept-message-attributes]]\n=== <intercept-message> Attributes\n\n[[nsa-intercept-message-pattern]]\n* **pattern** An ant based pattern that matches on the Message destination.\nFor example, \"/**\" matches any Message with a destination; \"/admin/**\" matches any Message that has a destination that starts with \"/admin/**\".\n\n[[nsa-intercept-message-type]]\n* **type** The type of message to match on.\nValid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER).\n\n[[nsa-intercept-message-access]]\n* **access** The expression used to secure the Message.\nFor example, \"denyAll\" will deny access to all of the matching Messages; \"permitAll\" will grant access to all of the matching Messages; \"hasRole('ADMIN') requires the current user to have the role 'ROLE_ADMIN' for the matching Messages.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/appendix/proxy-server.adoc",
    "content": "[[appendix-proxy-server]]\n= Proxy Server Configuration\n\nWhen using a proxy server it is important to ensure that you have configured your application properly.\nFor example, many applications will have a load balancer that responds to request for https://example.com/ by forwarding the request to an application server at https://192.168.1:8080\nWithout proper configuration, the application server will not know that the load balancer exists and treat the request as though https://192.168.1:8080 was requested by the client.\n\nTo fix this you can use https://tools.ietf.org/html/rfc7239[RFC 7239] to specify that a load balancer is being used.\nTo make the application aware of this, you need to either configure your application server aware of the X-Forwarded headers.\nFor example Tomcat uses the https://tomcat.apache.org/tomcat-10.1-doc/api/org/apache/catalina/valves/RemoteIpValve.html[RemoteIpValve] and Jetty uses https://eclipse.dev/jetty/javadoc/jetty-11/org/eclipse/jetty/server/ForwardedRequestCustomizer.html[ForwardedRequestCustomizer].\nAlternatively, Spring 4.3+ users can leverage https://github.com/spring-projects/spring-framework/blob/v4.3.3.RELEASE/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java[ForwardedHeaderFilter].\n\nSpring Boot users may use the `server.use-forward-headers` property to configure the application.\nSee the {spring-boot-reference-url}how-to/webserver.html#howto.webserver.use-behind-a-proxy-server[Spring Boot documentation] for further details.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/architecture.adoc",
    "content": "[[servlet-architecture]]\n= Architecture\n:figures: servlet/architecture\n\nThis section discusses Spring Security's high-level architecture within Servlet based applications.\nWe build on this high-level understanding within the xref:servlet/authentication/index.adoc#servlet-authentication[Authentication], xref:servlet/authorization/index.adoc#servlet-authorization[Authorization], and xref:servlet/exploits/index.adoc#servlet-exploits[Protection Against Exploits] sections of the reference.\n// FIXME: Add links to other sections of architecture\n\n[[servlet-filters-review]]\n== A Review of Filters\n\nSpring Security's Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first.\nThe following image shows the typical layering of the handlers for a single HTTP request.\n\n.FilterChain\n[[servlet-filterchain-figure]]\n[.invert-dark]\nimage::{figures}/filterchain.png[]\n\nThe client sends a request to the application, and the container creates a `FilterChain`, which contains the `Filter` instances and `Servlet` that should process the `HttpServletRequest`, based on the path of the request URI.\nIn a Spring MVC application, the `Servlet` is an instance of {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`].\nAt most, one `Servlet` can handle a single `HttpServletRequest` and `HttpServletResponse`.\nHowever, more than one `Filter` can be used to:\n\n* Prevent downstream `Filter` instances or the `Servlet` from being invoked.\nIn this case, the `Filter` typically writes the `HttpServletResponse`.\n* Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream `Filter` instances and the `Servlet`.\n\nThe power of the `Filter` comes from the `FilterChain` that is passed into it.\n\n.`FilterChain` Usage Example\ninclude-code::./FilterChainUsage[tag=dofilter,indent=0]\n\nSince a `Filter` impacts only downstream `Filter` instances and the `Servlet`, the order in which each `Filter` is invoked is extremely important.\n\n[[servlet-delegatingfilterproxy]]\n== DelegatingFilterProxy\n\nSpring provides a `Filter` implementation named {spring-framework-api-url}org/springframework/web/filter/DelegatingFilterProxy.html[`DelegatingFilterProxy`] that allows bridging between the Servlet container's lifecycle and Spring's `ApplicationContext`.\nThe Servlet container allows registering `Filter` instances by using its own standards, but it is not aware of Spring-defined Beans.\nYou can register `DelegatingFilterProxy` through the standard Servlet container mechanisms but delegate all the work to a Spring Bean that implements `Filter`.\n\nHere is a picture of how `DelegatingFilterProxy` fits into the <<servlet-filters-review,`Filter` instances and the `FilterChain`>>.\n\n.DelegatingFilterProxy\n[[servlet-delegatingfilterproxy-figure]]\n[.invert-dark]\nimage::{figures}/delegatingfilterproxy.png[]\n\n`DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__.\nThe following listing shows pseudo code of `DelegatingFilterProxy`:\n\n.`DelegatingFilterProxy` Pseudo Code\n\ninclude-code::./SampleDelegatingFilterProxy[tag=dofilter,indent=0]\n\n<1> Lazily get Filter that was registered as a Spring Bean.\nFor the example in <<servlet-delegatingfilterproxy-figure>> `delegate` is an instance of __Bean Filter~0~__.\n<2> Delegate work to the Spring Bean.\n\nAnother benefit of `DelegatingFilterProxy` is that it allows delaying looking up `Filter` bean instances.\nThis is important because the container needs to register the `Filter` instances before the container can start up.\nHowever, Spring typically uses a `ContextLoaderListener` to load the Spring Beans, which is not done until after the `Filter` instances need to be registered.\n\n[[servlet-filterchainproxy]]\n== FilterChainProxy\n\nSpring Security's Servlet support is contained within `FilterChainProxy`.\n`FilterChainProxy` is a special `Filter` provided by Spring Security that allows delegating to many `Filter` instances through <<servlet-securityfilterchain,`SecurityFilterChain`>>.\nSince `FilterChainProxy` is a Bean, it is typically wrapped in a <<servlet-delegatingfilterproxy>>.\n\nThe following image shows the role of `FilterChainProxy`.\n\n.FilterChainProxy\n[[servlet-filterchainproxy-figure]]\n[.invert-dark]\nimage::{figures}/filterchainproxy.png[]\n\n[[servlet-securityfilterchain]]\n== SecurityFilterChain\n\njavadoc:org.springframework.security.web.SecurityFilterChain[]  is used by <<servlet-filterchainproxy>> to determine which Spring Security `Filter` instances should be invoked for the current request.\n\nThe following image shows the role of `SecurityFilterChain`.\n\n.SecurityFilterChain\n[[servlet-securityfilterchain-figure]]\n[.invert-dark]\nimage::{figures}/securityfilterchain.png[]\n\nThe <<servlet-security-filters,Security Filters>> in `SecurityFilterChain` are typically Beans, but they are registered with `FilterChainProxy` instead of <<servlet-delegatingfilterproxy>>.\n`FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <<servlet-delegatingfilterproxy>>.\nFirst, it provides a starting point for all of Spring Security's Servlet support.\nFor that reason, if you try to troubleshoot Spring Security's Servlet support, adding a debug point in `FilterChainProxy` is a great place to start.\n\nSecond, since `FilterChainProxy` is central to Spring Security usage, it can perform tasks that are not viewed as optional.\n// FIXME: Add a link to SecurityContext\nFor example, it clears out the `SecurityContext` to avoid memory leaks.\nIt also applies Spring Security's xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`] to protect applications against certain types of attacks.\n\nIn addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked.\nIn a Servlet container, `Filter` instances are invoked based upon the URL alone.\n// FIXME: Link to RequestMatcher\nHowever, `FilterChainProxy` can determine invocation based upon anything in the `HttpServletRequest` by using the `RequestMatcher` interface.\n\nThe following image shows multiple `SecurityFilterChain` instances:\n\n.Multiple SecurityFilterChain\n[[servlet-multi-securityfilterchain-figure]]\n[.invert-dark]\nimage::{figures}/multi-securityfilterchain.png[]\n\nIn the <<servlet-multi-securityfilterchain-figure>> figure, `FilterChainProxy` decides which `SecurityFilterChain` should be used.\nOnly the first `SecurityFilterChain` that matches is invoked.\nIf a URL of `/api/messages/` is requested, it first matches on the `SecurityFilterChain~0~` pattern of `+/api/**+`, so only `SecurityFilterChain~0~` is invoked, even though it also matches on ``SecurityFilterChain~n~``.\nIf a URL of `/messages/` is requested, it does not match on the `SecurityFilterChain~0~` pattern of `+/api/**+`, so `FilterChainProxy` continues trying each `SecurityFilterChain`.\nAssuming that no other `SecurityFilterChain` instances match, `SecurityFilterChain~n~` is invoked.\n// FIXME: add link to pattern matching\n\nNotice that `SecurityFilterChain~0~` has only three security `Filter` instances configured.\nHowever, `SecurityFilterChain~n~` has four security `Filter` instances configured.\nIt is important to note that each `SecurityFilterChain` can be unique and can be configured in isolation.\nIn fact, a `SecurityFilterChain` might have zero security `Filter` instances if the application wants Spring Security to ignore certain requests.\n// FIXME: add link to configuring multiple `SecurityFilterChain` instances\n\n[[servlet-security-filters]]\n== Security Filters\n\nThe Security Filters are inserted into the <<servlet-filterchainproxy>> with the <<servlet-securityfilterchain>> API.\nThose filters can be used for a number of different purposes, like\nxref:servlet/exploits/index.adoc[exploit protection], xref:servlet/authentication/index.adoc[authentication], xref:servlet/authorization/index.adoc[authorization], and more.\nThe filters are executed in a specific order to guarantee that they are invoked at the right time, for example, the `Filter` that performs authentication should be invoked before the `Filter` that performs authorization.\nIt is typically not necessary to know the ordering of Spring Security's ``Filter``s.\nHowever, there are times that it is beneficial to know the ordering, if you want to know them, you can check the {gh-url}/config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java[`FilterOrderRegistration` code].\n\nThese security filters are most often declared using an javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[`HttpSecurity`] instance.\nTo exemplify the above paragraph, let's consider the following security configuration:\n\ninclude-code::./SecurityConfig[tag=snippet,indent=0]\n\nThe above configuration will result in the following `Filter` ordering:\n\n[cols=\"1,1\", options=\"header\"]\n|====\n| Filter | Added by\n| xref:servlet/exploits/csrf.adoc[CsrfFilter] | `HttpSecurity#csrf`\n| xref:servlet/authentication/passwords/basic.adoc[BasicAuthenticationFilter] | `HttpSecurity#httpBasic`\n| xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form[UsernamePasswordAuthenticationFilter] | `HttpSecurity#formLogin`\n| xref:servlet/authorization/authorize-http-requests.adoc[AuthorizationFilter] | `HttpSecurity#authorizeHttpRequests`\n|====\n\n1. First, the `CsrfFilter` is invoked to protect against xref:servlet/exploits/csrf.adoc[CSRF attacks].\n2. Second, xref:servlet/authentication/architecture.adoc[the authentication filters] are invoked to authenticate the request.\n3. Third, xref:servlet/authorization/authorize-http-requests.adoc[the `AuthorizationFilter`] is invoked to authorize the request.\n\n[NOTE]\n====\nThere might be other `Filter` instances that are not listed above.\nIf you want to see the list of filters invoked for a particular request, you can <<servlet-print-filters,print them>>.\n====\n\n[[servlet-print-filters]]\n=== Printing the Security Filters\n\nOften times, it is useful to see the list of security ``Filter``s that are invoked for a particular request.\nFor example, you want to make sure that the <<adding-filters-to-chain,filter you have added>> is in the list of the security filters.\n\nThe list of filters is printed at DEBUG level on the application startup, so you can see something like the following on the console output for example:\n\n[source,text,role=\"terminal\"]\n----\n2023-06-14T08:55:22.321-03:00  DEBUG 76975 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [ DisableEncodeUrlFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CsrfFilter, LogoutFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter]\n----\n\nAnd that will give a pretty good idea of the security filters that are configured for <<servlet-securityfilterchain,each filter chain>>.\n\nBut that is not all, you can also configure your application to print the invocation of each individual filter for each request.\nThat is helpful to see if the filter you have added is invoked for a particular request or to check where an exception is coming from.\nTo do that, you can configure your application to <<servlet-logging,log the security events>>.\n\n[[adding-filters-to-chain]]\n=== Adding Filters to the Filter Chain\n\nMost of the time, the default <<servlet-security-filters>> are enough to provide security to your application.\nHowever, there might be times that you want to add a custom `Filter` to the <<servlet-securityfilterchain>>.\n\njavadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[] comes with three methods for adding filters:\n\n* `#addFilterBefore(Filter, Class<?>)` adds your filter before another filter\n* `#addFilterAfter(Filter, Class<?>)` adds your filter after another filter\n* `#addFilterAt(Filter, Class<?>)` replaces another filter with your filter\n\n[[adding-custom-filter]]\n==== Adding a Custom Filter\n\nIf you are creating a filter of your own, you will need to determine its location in the filter chain.\nPlease take a look at the following key events that occur in the filter chain:\n\n1. xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContext`] is loaded from the session\n2. Request is protected from common exploits; xref:features/exploits/headers.adoc[secure headers], xref:servlet/integrations/cors.adoc[CORS], xref:servlet/exploits/csrf.adoc[CSRF]\n3. Request is xref:servlet/authentication/architecture.adoc[authenticated]\n4. Request is xref:servlet/authorization/architecture.adoc[authorized]\n\nConsider which events you need to have happened in order to locate your filter.\nThe following is a rule of thumb:\n\n[cols=\"1,1,1\"]\n|===\n| If your filter is a(n) | Then place it after | As these events have already occurred\n\n| exploit protection filter\n| SecurityContextHolderFilter\n| 1\n\n| authentication filter\n| LogoutFilter\n| 1, 2\n\n| authorization filter\n| AnonymousAuthenticationFilter\n| 1, 2, 3\n|===\n\n[TIP]\nMost commonly, applications add a custom authentication.\nThis means they should be placed after xref:servlet/authentication/logout.adoc[`LogoutFilter`].\n\nFor example, let's say that you want to add a `Filter` that gets a tenant id header and check if the current user has access to that tenant.\n\nFirst, let's create the `Filter`:\n\ninclude-code::./TenantFilter[tag=snippet,indent=0]\n\nThe sample code above does the following:\n\n<1> Get the tenant id from the request header.\n<2> Check if the current user has access to the tenant id.\n<3> If the user has access, then invoke the rest of the filters in the chain.\n<4> If the user does not have access, then throw an `AccessDeniedException`.\n\n[TIP]\n====\nInstead of implementing `Filter`, you can extend from {spring-framework-api-url}org/springframework/web/filter/OncePerRequestFilter.html[OncePerRequestFilter] which is a base class for filters that are only invoked once per request and provides a `doFilterInternal` method with the `HttpServletRequest` and `HttpServletResponse` parameters.\n====\n\nNow, you need to add the filter to the <<servlet-securityfilterchain>>.\nThe previous description already gives us a clue on where to add the filter, since we need to know the current user, we need to add it after the authentication filters.\n\nBased on the rule of thumb, add it after xref:servlet/authentication/anonymous.adoc[ `AnonymousAuthenticationFilter`], the last authentication filter in the chain, like so:\n\ninclude-code::./SecurityConfig[tag=snippet,indent=0]\n\n<1> Use `HttpSecurity#addFilterAfter` to add the `TenantFilter` after the `AnonymousAuthenticationFilter`.\n\nBy adding the filter after the xref:servlet/authentication/anonymous.adoc[`AnonymousAuthenticationFilter`] we are making sure that the `TenantFilter` is invoked after the authentication filters.\n\nAnd that's it, now the `TenantFilter` will be invoked in the filter chain and will check if the current user has access to the tenant id.\n\n==== Declaring Your Filter as a Bean\n\nWhen you declare a `Filter` as a Spring bean, either by annotating it with `@Component` or by declaring it as a bean in your configuration, Spring Boot automatically {spring-boot-reference-url}reference/web/servlet.html#web.servlet.embedded-container.servlets-filters-listeners.beans[registers it with the embedded container].\nThat may cause the filter to be invoked twice, once by the container and once by Spring Security and in a different order.\n\nBecause of that, filters are often not Spring beans.\n\nHowever, if your filter needs to be a Spring bean (to take advantage of dependency injection, for example) you can tell Spring Boot to not register it with the container by declaring a `FilterRegistrationBean` bean and setting its `enabled` property to `false`:\n\n[source,java]\n----\n@Bean\npublic FilterRegistrationBean<TenantFilter> tenantFilterRegistration(TenantFilter filter) {\n    FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean<>(filter);\n    registration.setEnabled(false);\n    return registration;\n}\n----\n\nThis makes so that `HttpSecurity` is the only one adding it.\n\n[[customizing-filter]]\n==== Customizing a Spring Security Filter\n\nGenerally, you can use a filter's DSL method to configure Spring Security's filters.\nFor example, the simplest way to add `BasicAuthenticationFilter` is by asking the DSL to do it:\n\ninclude-code::./CustomizingFilterTests[tag=basic-default,indent=0]\n\nHowever, in the event that you want to construct a Spring Security filter yourself, you specify it in the DSL using `addFilterAt` like so:\n\ninclude-code::./CustomizingFilterTests[tag=custom-filter,indent=0]\n\nNote that if that filter has already been added, then Spring Security will throw an exception.\nFor example, calling xref:servlet/authentication/passwords/basic.adoc[ `HttpSecurity#httpBasic`] adds a `BasicAuthenticationFilter` for you.\nSo, the following arrangement fails since there are two calls that are both trying to add `BasicAuthenticationFilter`:\n\ninclude-code::./CustomizingFilterTests[tag=incorrect,indent=0]\n\nIn this case, remove the call to `httpBasic` since you are constructing `BasicAuthenticationFilter` yourself.\n\n[TIP]\n====\nIn the event that you are unable to reconfigure `HttpSecurity` to not add a certain filter, you can typically disable the Spring Security filter by calling its DSL's `disable` method like so:\n\ninclude-code::./CustomizingFilterTests[tag=disable,indent=0]\n====\n\n[[servlet-exceptiontranslationfilter]]\n== Handling Security Exceptions\n\n\nThe javadoc:org.springframework.security.web.access.ExceptionTranslationFilter[] allows translation of javadoc:org.springframework.security.access.AccessDeniedException[] and javadoc:org.springframework.security.core.AuthenticationException[] into HTTP responses.\n\n`ExceptionTranslationFilter` is inserted into the <<servlet-filterchainproxy>> as one of the <<servlet-security-filters>>.\n\nThe following image shows the relationship of `ExceptionTranslationFilter` to other components:\n\n[.invert-dark]\nimage::{figures}/exceptiontranslationfilter.png[]\n\n\n* image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application.\n* image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__.\n** The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.\n** The `HttpServletRequest` is <<savedrequests,saved>> so that it can be used to replay the original request once authentication is successful.\n// FIXME: add link to authentication success\n** The `AuthenticationEntryPoint` is used to request credentials from the client.\nFor example, it might redirect to a log in page or send a `WWW-Authenticate` header.\n// FIXME: link to AuthenticationEntryPoint\n* image:{icondir}/number_3.png[] Otherwise, if it is an `AccessDeniedException`, then __Access Denied__.\nThe `AccessDeniedHandler` is invoked to handle access denied.\n// FIXME: link to AccessDeniedHandler\n\n[NOTE]\n====\nIf the application does not throw an `AccessDeniedException` or an `AuthenticationException`, then `ExceptionTranslationFilter` does not do anything.\n====\n\nThe pseudocode for `ExceptionTranslationFilter` looks something like this:\n\n.ExceptionTranslationFilter pseudocode\n[source,java]\n----\ntry {\n\tfilterChain.doFilter(request, response); // <1>\n} catch (AccessDeniedException | AuthenticationException ex) {\n\tif (!authenticated || ex instanceof AuthenticationException) {\n\t\tstartAuthentication(); // <2>\n\t} else {\n\t\taccessDenied(); // <3>\n\t}\n}\n----\n<1> As described in <<servlet-filters-review>>, invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application.\nThis means that if another part of the application, (xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] or method security) throws an `AuthenticationException` or `AccessDeniedException` it is caught and handled here.\n<2> If the user is not authenticated or it is an `AuthenticationException`, __Start Authentication__.\n<3> Otherwise, __Access Denied__\n\n[[savedrequests]]\n== Saving Requests Between Authentication\n\nAs illustrated in <<servlet-exceptiontranslationfilter>>, when a request has no authentication and is for a resource that requires authentication, there is a need to save the request for the authenticated resource to re-request after authentication is successful.\nIn Spring Security this is done by saving the `HttpServletRequest` using a <<requestcache,`RequestCache`>> implementation.\n\n[[requestcache]]\n=== RequestCache\n\nThe `HttpServletRequest` is saved in the javadoc:org.springframework.security.web.savedrequest.RequestCache[].\nWhen the user successfully authenticates, the `RequestCache` is used to replay the original request.\nThe <<requestcacheawarefilter,`RequestCacheAwareFilter`>> uses the `RequestCache` to get the saved `HttpServletRequest` after the user authenticates, while the `ExceptionTranslationFilter` uses the `RequestCache` to save the `HttpServletRequest` after it detects `AuthenticationException`, before redirecting the user to the login endpoint.\n\nBy default, an `HttpSessionRequestCache` is used.\nThe code below demonstrates how to customize the `RequestCache` implementation that is used to check the `HttpSession` for a saved request if the parameter named `continue` is present.\n\ninclude::partial$servlet/architecture/request-cache-continue.adoc[]\n\n[[requestcache-prevent-saved-request]]\n==== Prevent the Request From Being Saved\n\nThere are a number of reasons you may want to not store the user's unauthenticated request in the session.\nYou may want to offload that storage onto the user's browser or store it in a database.\nOr you may want to shut off this feature since you always want to redirect the user to the home page instead of the page they tried to visit before login.\n\nTo do that, you can use the javadoc:org.springframework.security.web.savedrequest.NullRequestCache[NullRequestCache] implementation.\n\n.Prevent the Request From Being Saved\ninclude-code::./SecurityConfig[tag=snippet,indent=0]\n\n[[requestcacheawarefilter]]\n=== RequestCacheAwareFilter\n\nThe javadoc:org.springframework.security.web.savedrequest.RequestCacheAwareFilter[] uses the <<requestcache,`RequestCache`>> to replay the original request.\n\n[[servlet-logging]]\n== Logging\n\nSpring Security provides comprehensive logging of all security related events at the DEBUG and TRACE level.\nThis can be very useful when debugging your application because for security measures Spring Security does not add any detail of why a request has been rejected to the response body.\nIf you come across a 401 or 403 error, it is very likely that you will find a log message that will help you understand what is going on.\n\nLet's consider an example where a user tries to make a `POST` request to a resource that has xref:servlet/exploits/csrf.adoc[CSRF protection] enabled without the CSRF token.\nWith no logs, the user will see a 403 error with no explanation of why the request was rejected.\nHowever, if you enable logging for Spring Security, you will see a log message like this:\n\n[source,text]\n----\n2023-06-14T09:44:25.797-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Securing POST /hello\n2023-06-14T09:44:25.797-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking DisableEncodeUrlFilter (1/15)\n2023-06-14T09:44:25.798-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking WebAsyncManagerIntegrationFilter (2/15)\n2023-06-14T09:44:25.800-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking SecurityContextHolderFilter (3/15)\n2023-06-14T09:44:25.801-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking HeaderWriterFilter (4/15)\n2023-06-14T09:44:25.802-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking CsrfFilter (5/15)\n2023-06-14T09:44:25.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://localhost:8080/hello\n2023-06-14T09:44:25.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.s.w.access.AccessDeniedHandlerImpl   : Responding with 403 status code\n2023-06-14T09:44:25.814-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match request to [Is Secure]\n----\n\nIt becomes clear that the CSRF token is missing and that is why the request is being denied.\n\nTo configure your application to log all the security events, you can add the following to your application:\n\n====\n.application.properties in Spring Boot\n[source,properties,role=\"primary\"]\n----\nlogging.level.org.springframework.security=TRACE\n----\n.logback.xml\n[source,xml,role=\"secondary\"]\n----\n<configuration>\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- ... -->\n    </appender>\n    <!-- ... -->\n    <logger name=\"org.springframework.security\" level=\"trace\" additivity=\"false\">\n        <appender-ref ref=\"Console\" />\n    </logger>\n</configuration>\n----\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc",
    "content": "[[anonymous]]\n= Anonymous Authentication\n\n\n[[anonymous-overview]]\n== Overview\nIt is generally considered good security practice to adopt a \"`deny-by-default`\" stance, where you explicitly specify what is allowed and disallow everything else.\nDefining what is accessible to unauthenticated users is a similar situation, particularly for web applications.\nMany sites require that users must be authenticated for anything other than a few URLs (for example the home and login pages).\nIn that case, it is easiest to define access configuration attributes for these specific URLs rather than for every secured resource.\nPut differently, sometimes it is nice to say `ROLE_SOMETHING` is required by default and allow only certain exceptions to this rule, such as for login, logout, and home pages of an application.\nYou could also omit these pages from the filter chain entirely, thus bypassing the access control checks, but this may be undesirable for other reasons, particularly if the pages behave differently for authenticated users.\n\nThis is what we mean by anonymous authentication.\nNote that there is no real conceptual difference between a user who is \"`anonymously authenticated`\" and an unauthenticated user.\nSpring Security's anonymous authentication just gives you a more convenient way to configure your access-control attributes.\nCalls to servlet API calls, such as `getCallerPrincipal`, still return null, even though there is actually an anonymous authentication object in the `SecurityContextHolder`.\n\nThere are other situations where anonymous authentication is useful, such as when an auditing interceptor queries the `SecurityContextHolder` to identify which principal was responsible for a given operation.\nClasses can be authored more robustly if they know the `SecurityContextHolder` always contains an `Authentication` object and never contains `null`.\n\n\n[[anonymous-config]]\n== Configuration\nAnonymous authentication support is provided automatically when you use the HTTP configuration (introduced in Spring Security 3.0).\nYou can customize (or disable) it by using the `<anonymous>` element.\nYou need not configure the beans described here unless you are using traditional bean configuration.\n\nThree classes work together to provide the anonymous authentication feature.\n`AnonymousAuthenticationToken` is an implementation of `Authentication` and stores the `GrantedAuthority` instances that apply to the anonymous principal.\nThere is a corresponding `AnonymousAuthenticationProvider`, which is chained into the `ProviderManager` so that `AnonymousAuthenticationToken` instances are accepted.\nFinally, an `AnonymousAuthenticationFilter` is chained after the normal authentication mechanisms and automatically adds an `AnonymousAuthenticationToken` to the `SecurityContextHolder` if there is no existing `Authentication` held there.\nThe filter and authentication provider is defined as follows:\n\n[source,xml]\n----\n<bean id=\"anonymousAuthFilter\"\n\tclass=\"org.springframework.security.web.authentication.AnonymousAuthenticationFilter\">\n<property name=\"key\" value=\"foobar\"/>\n<property name=\"userAttribute\" value=\"anonymousUser,ROLE_ANONYMOUS\"/>\n</bean>\n\n<bean id=\"anonymousAuthenticationProvider\"\n\tclass=\"org.springframework.security.authentication.AnonymousAuthenticationProvider\">\n<property name=\"key\" value=\"foobar\"/>\n</bean>\n----\n\n\n\nThe `key` is shared between the filter and authentication provider, so that tokens created by the former are accepted by the latter\n\n[NOTE]\n====\nThe use of the `key` property should not be regarded as providing any real security here.\nIt is merely a book-keeping exercise.\nIf you share a `ProviderManager` that contains an `AnonymousAuthenticationProvider` in a scenario where it is possible for an authenticating client to construct the `Authentication` object (such as with RMI invocations), then a malicious client could submit an `AnonymousAuthenticationToken` that it had created itself (with the chosen username and authority list).\nIf the `key` is guessable or can be found out, the token would be accepted by the anonymous provider.\nThis is not a problem with normal usage. However, if you use RMI, you should use a customized `ProviderManager` that omits the anonymous provider rather than sharing the one you use for your HTTP authentication mechanisms.\n====\n\nThe `userAttribute` is expressed in the form of `usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]`.\nThe same syntax is used after the equals sign for the `userMap` property of `InMemoryDaoImpl`.\n\nAs explained earlier, the benefit of anonymous authentication is that all URI patterns can have security applied to them, as the following example shows:\n\n[source,xml]\n----\n<bean id=\"filterSecurityInterceptor\"\n\tclass=\"org.springframework.security.web.access.intercept.FilterSecurityInterceptor\">\n<property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n<property name=\"accessDecisionManager\" ref=\"httpRequestAccessDecisionManager\"/>\n<property name=\"securityMetadata\">\n\t<security:filter-security-metadata-source>\n\t<security:intercept-url pattern='/index.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>\n\t<security:intercept-url pattern='/hello.htm' access='ROLE_ANONYMOUS,ROLE_USER'/>\n\t<security:intercept-url pattern='/logoff.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>\n\t<security:intercept-url pattern='/login.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>\n\t<security:intercept-url pattern='/**' access='ROLE_USER'/>\n\t</security:filter-security-metadata-source>\" +\n</property>\n</bean>\n----\n\n[[anonymous-auth-trust-resolver]]\n== AuthenticationTrustResolver\nRounding out the anonymous authentication discussion is the `AuthenticationTrustResolver` interface, with its corresponding `AuthenticationTrustResolverImpl` implementation.\nThis interface provides an `isAnonymous(Authentication)` method, which allows interested classes to take into account this special type of authentication status.\nThe `ExceptionTranslationFilter` uses this interface in processing `AccessDeniedException` instances.\nIf an `AccessDeniedException` is thrown and the authentication is of an anonymous type, instead of throwing a 403 (forbidden) response, the filter, instead, commences the `AuthenticationEntryPoint` so that the principal can authenticate properly.\nThis is a necessary distinction. Otherwise, principals would always be deemed \"`authenticated`\" and never be given an opportunity to login through form, basic, digest, or some other normal authentication mechanism.\n\nWe often see the `ROLE_ANONYMOUS` attribute in the earlier interceptor configuration replaced with `IS_AUTHENTICATED_ANONYMOUSLY`, which is effectively the same thing when defining access controls.\nThis is an example of the use of the `AuthenticatedVoter`, which we cover in the xref:servlet/authorization/architecture.adoc#authz-authenticated-voter[authorization chapter].\nIt uses an `AuthenticationTrustResolver` to process this particular configuration attribute and grant access to anonymous users.\nThe `AuthenticatedVoter` approach is more powerful, since it lets you differentiate between anonymous, remember-me, and fully authenticated users.\nIf you do not need this functionality, though, you can stick with `ROLE_ANONYMOUS`, which is processed by Spring Security's standard `RoleVoter`.\n\n[[anonymous-auth-mvc-controller]]\n== Getting Anonymous Authentications with Spring MVC\n\n{spring-framework-reference-url}web/webmvc/mvc-controller/ann-methods/arguments.html[Spring MVC resolves parameters of type `Principal`] using its own argument resolver.\n\nThis means that a construct like this one:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/\")\npublic String method(Authentication authentication) {\n\tif (authentication instanceof AnonymousAuthenticationToken) {\n\t\treturn \"anonymous\";\n\t} else {\n\t\treturn \"not anonymous\";\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/\")\nfun method(authentication: Authentication?): String {\n    return if (authentication is AnonymousAuthenticationToken) {\n        \"anonymous\"\n    } else {\n        \"not anonymous\"\n    }\n}\n----\n======\n\nwill always return \"not anonymous\", even for anonymous requests.\nThe reason is that Spring MVC resolves the parameter using `HttpServletRequest#getPrincipal`, which is `null` when the request is anonymous.\n\nIf you'd like to obtain the `Authentication` in anonymous requests, use\nxref:servlet/integrations/mvc.adoc#mvc-current-security-context[`@CurrentSecurityContext`] instead:\n\n.Use CurrentSecurityContext for Anonymous requests\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/\")\npublic String method(@CurrentSecurityContext SecurityContext context) {\n\tif (context.getAuthentication() instanceOf AnonymousAuthenticationToken) {\n\t\treturn \"anonymous\"\n\t} else {\n\t\treturn \"not anonymous\"\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/\")\nfun method(@CurrentSecurityContext context : SecurityContext) : String {\n   return if (context!!.authentication is AnonymousAuthenticationToken) {\n\t\"anonymous\"\n   } else {\n\t\"not anonymous\"\n   }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/architecture.adoc",
    "content": "[[servlet-authentication-architecture]]\n= Servlet Authentication Architecture\n:figures: servlet/authentication/architecture\n\nThis discussion expands on xref:servlet/architecture.adoc#servlet-architecture[Servlet Security: The Big Picture] to describe the main architectural components that Spring Security uses in Servlet authentication.\nIf you need concrete flows that explain how these pieces fit together, look at the xref:servlet/authentication/index.adoc#servlet-authentication-mechanisms[Authentication Mechanism] specific sections.\n\n* <<servlet-authentication-securitycontextholder>> - The `SecurityContextHolder` is where Spring Security stores the details of who is xref:features/authentication/index.adoc#authentication[authenticated].\n* <<servlet-authentication-securitycontext>> - is obtained from the `SecurityContextHolder` and contains the `Authentication` of the currently authenticated user.\n* <<servlet-authentication-authentication>> - Can be the input to `AuthenticationManager` to provide the credentials a user has provided to authenticate or the current user from the `SecurityContext`.\n* <<servlet-authentication-granted-authority>> - An authority that is granted to the principal on the `Authentication` (i.e. roles, scopes, etc.)\n* <<servlet-authentication-authenticationmanager>> -  the API that defines how Spring Security's Filters perform  xref:features/authentication/index.adoc#authentication[authentication].\n* <<servlet-authentication-providermanager>> -  the most common implementation of `AuthenticationManager`.\n* <<servlet-authentication-authenticationprovider>> - used by `ProviderManager` to perform a specific type of authentication.\n* <<servlet-authentication-authenticationentrypoint>> - used for requesting credentials from a client (i.e. redirecting to a log in page, sending a `WWW-Authenticate` response, etc.)\n* <<servlet-authentication-abstractprocessingfilter>> - a base `Filter` used for authentication.\nThis also gives a good idea of the high level flow of authentication and how pieces work together.\n\n[[servlet-authentication-securitycontextholder]]\n== SecurityContextHolder\n\nAt the heart of Spring Security's authentication model is the `SecurityContextHolder`.\nIt contains the <<servlet-authentication-securitycontext>>.\n\n[.invert-dark]\nimage::{figures}/securitycontextholder.png[]\n\nThe `SecurityContextHolder` is where Spring Security stores the details of who is xref:features/authentication/index.adoc#authentication[authenticated].\nSpring Security does not care how the `SecurityContextHolder` is populated.\nIf it contains a value, it is used as the currently authenticated user.\n\nThe simplest way to indicate a user is authenticated is to set the `SecurityContextHolder` directly:\n\n.Setting `SecurityContextHolder`\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nSecurityContext context = SecurityContextHolder.createEmptyContext(); // <1>\nAuthentication authentication =\n    new TestingAuthenticationToken(\"username\", \"password\", \"ROLE_USER\"); // <2>\ncontext.setAuthentication(authentication);\n\nSecurityContextHolder.setContext(context); // <3>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval context: SecurityContext = SecurityContextHolder.createEmptyContext() // <1>\nval authentication: Authentication = TestingAuthenticationToken(\"username\", \"password\", \"ROLE_USER\") // <2>\ncontext.authentication = authentication\n\nSecurityContextHolder.setContext(context) // <3>\n----\n======\n\n<1> We start by creating an empty `SecurityContext`.\nYou should create a new `SecurityContext` instance instead of using `SecurityContextHolder.getContext().setAuthentication(authentication)` to avoid race conditions across multiple threads.\n<2> Next, we create a new <<servlet-authentication-authentication,`Authentication`>> object.\nSpring Security does not care what type of `Authentication` implementation is set on the `SecurityContext`.\nHere, we use `TestingAuthenticationToken`, because it is very simple.\nA more common production scenario is `UsernamePasswordAuthenticationToken(userDetails, password, authorities)`.\n<3> Finally, we set the `SecurityContext` on the `SecurityContextHolder`.\nSpring Security uses this information for xref:servlet/authorization/index.adoc#servlet-authorization[authorization].\n\n\nTo obtain information about the authenticated principal, access the `SecurityContextHolder`.\n\n.Access Currently Authenticated User\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nSecurityContext context = SecurityContextHolder.getContext();\nAuthentication authentication = context.getAuthentication();\nString username = authentication.getName();\nObject principal = authentication.getPrincipal();\nCollection<? extends GrantedAuthority> authorities = authentication.getAuthorities();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval context = SecurityContextHolder.getContext()\nval authentication = context.authentication\nval username = authentication.name\nval principal = authentication.principal\nval authorities = authentication.authorities\n----\n======\n\nIn Spring MVC, you can resolve the current principal with\nxref:servlet/integrations/mvc.adoc#mvc-authentication-principal[`@AuthenticationPrincipal`]\nand the full `SecurityContext` with\nxref:servlet/integrations/mvc.adoc#mvc-current-security-context[`@CurrentSecurityContext`].\nFor Servlet API access, use `HttpServletRequest#getRemoteUser`.\n\nBy default, `SecurityContextHolder` uses a `ThreadLocal` to store these details, which means that the `SecurityContext` is always available to methods in the same thread, even if the `SecurityContext` is not explicitly passed around as an argument to those methods.\nUsing a `ThreadLocal` in this way is quite safe if you take care to clear the thread after the present principal's request is processed.\nSpring Security's xref:servlet/architecture.adoc#servlet-filterchainproxy[FilterChainProxy] ensures that the `SecurityContext` is always cleared.\n\nSome applications are not entirely suitable for using a `ThreadLocal`, because of the specific way they work with threads.\nFor example, a Swing client might want all threads in a Java Virtual Machine to use the same security context.\nYou can configure `SecurityContextHolder` with a strategy on startup to specify how you would like the context to be stored.\nFor a standalone application, you would use the `SecurityContextHolder.MODE_GLOBAL` strategy.\nOther applications might want to have threads spawned by the secure thread also assume the same security identity.\nYou can achieve this by using `SecurityContextHolder.MODE_INHERITABLETHREADLOCAL`.\nYou can change the mode from the default `SecurityContextHolder.MODE_THREADLOCAL` in two ways.\nThe first is to set a system property.\nThe second is to call a static method on `SecurityContextHolder`.\nMost applications need not change from the default.\nHowever, if you do, take a look at the JavaDoc for `SecurityContextHolder` to learn more.\n\n[[servlet-authentication-securitycontext]]\n== SecurityContext\n\nThe javadoc:org.springframework.security.core.context.SecurityContext[] is obtained from the <<servlet-authentication-securitycontextholder>>.\nThe `SecurityContext` contains an <<servlet-authentication-authentication>> object.\n\n[[servlet-authentication-authentication]]\n== Authentication\n\nThe javadoc:org.springframework.security.core.Authentication[] interface serves two main purposes within Spring Security:\n\n* An input to <<servlet-authentication-authenticationmanager,`AuthenticationManager`>> to provide the credentials a user has provided to authenticate.\nWhen used in this scenario, `isAuthenticated()` returns `false`.\n* Represent the currently authenticated user.\nYou can obtain the current `Authentication` from the <<servlet-authentication-securitycontext>>.\n\nThe `Authentication` contains:\n\n* `principal`: Identifies the user.\nWhen authenticating with a username/password this is often an instance of xref:servlet/authentication/passwords/user-details.adoc#servlet-authentication-userdetails[`UserDetails`].\n* `credentials`: Often a password.\nIn many cases, this is cleared after the user is authenticated, to ensure that it is not leaked.\n* `authorities`: The <<servlet-authentication-granted-authority,`GrantedAuthority`>> instances are high-level permissions the user is granted.\nTwo examples are roles and scopes.\n\nIt is also equipped with a `AdditionalRequiredFactorsBuilder` that allows you to mutate an existing `Authentication` instance and potentially merge it with another.\nThis is useful in scenarios like taking the authorities from one authentication step, like form login, and applying them to another, like one-time-token login, like so:\n\ninclude-code::./CopyAuthoritiesTests[tag=springSecurity,indent=0]\n\n[[servlet-authentication-granted-authority]]\n== GrantedAuthority\njavadoc:org.springframework.security.core.GrantedAuthority[] instances are high-level permissions that the user is granted.\nTwo examples are roles and scopes.\n\nYou can obtain `GrantedAuthority` instances from the <<servlet-authentication-authentication,`Authentication.getAuthorities()`>> method.\nThis method provides a `Collection` of `GrantedAuthority` objects.\nA `GrantedAuthority` is, not surprisingly, an authority that is granted to the principal.\nSuch authorities are usually \"`roles`\", such as `ROLE_ADMINISTRATOR` or `ROLE_HR_SUPERVISOR`.\nThese roles are later configured for web authorization, method authorization, and domain object authorization.\nOther parts of Spring Security interpret these authorities and expect them to be present.\nWhen using username/password based authentication `GrantedAuthority` instances are usually loaded by the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`].\n\nUsually, the `GrantedAuthority` objects are application-wide permissions.\nThey are not specific to a given domain object.\nThus, you would not likely have a `GrantedAuthority` to represent a permission to `Employee` object number 54, because if there are thousands of such authorities you would quickly run out of memory (or, at the very least, cause the application to take a long time to authenticate a user).\nOf course, Spring Security is expressly designed to handle this common requirement, but you should instead use the project's domain object security capabilities for this purpose.\n\n[[servlet-authentication-authenticationmanager]]\n== AuthenticationManager\n\njavadoc:org.springframework.security.authentication.AuthenticationManager[] is the API that defines how Spring Security's Filters perform  xref:features/authentication/index.adoc#authentication[authentication].\nThe <<servlet-authentication-authentication,`Authentication`>> that is returned is then set on the <<servlet-authentication-securitycontextholder>> by the controller (that is, by xref:servlet/architecture.adoc#servlet-security-filters[Spring Security's `Filters` instances]) that invoked the `AuthenticationManager`.\nIf you are not integrating with Spring Security's `Filters` instances, you can set the `SecurityContextHolder` directly and are not required to use an `AuthenticationManager`.\n\nWhile the implementation of `AuthenticationManager` could be anything, the most common implementation is <<servlet-authentication-providermanager,`ProviderManager`>>.\n// FIXME: add configuration\n\n[[servlet-authentication-providermanager]]\n== ProviderManager\n\njavadoc:org.springframework.security.authentication.ProviderManager[] is the most commonly used implementation of <<servlet-authentication-authenticationmanager,`AuthenticationManager`>>.\n`ProviderManager` delegates to a `List` of <<servlet-authentication-authenticationprovider,`AuthenticationProvider`>> instances.\nEach `AuthenticationProvider` has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream `AuthenticationProvider` to decide.\nIf none of the configured `AuthenticationProvider` instances can authenticate, authentication fails with a `ProviderNotFoundException`, which is a special `AuthenticationException` that indicates that the `ProviderManager` was not configured to support the type of `Authentication` that was passed into it.\n\n[.invert-dark]\nimage::{figures}/providermanager.png[]\n\nIn practice each `AuthenticationProvider` knows how to perform a specific type of authentication.\nFor example, one `AuthenticationProvider` might be able to validate a username/password, while another might be able to authenticate a SAML assertion.\nThis lets each `AuthenticationProvider` do a very specific type of authentication while supporting multiple types of authentication and expose only a single `AuthenticationManager` bean.\n\n`ProviderManager` also allows configuring an optional parent `AuthenticationManager`, which is consulted in the event that no `AuthenticationProvider` can perform authentication.\nThe parent can be any type of `AuthenticationManager`, but it is often an instance of `ProviderManager`.\n\n[.invert-dark]\nimage::{figures}/providermanager-parent.png[]\n\nIn fact, multiple `ProviderManager` instances might share the same parent `AuthenticationManager`.\nThis is somewhat common in scenarios where there are multiple xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] instances that have some authentication in common (the shared parent `AuthenticationManager`), but also different authentication mechanisms (the different `ProviderManager` instances).\n\n[.invert-dark]\nimage::{figures}/providermanagers-parent.png[]\n\n[[servlet-authentication-providermanager-erasing-credentials]]\nBy default, `ProviderManager` tries to clear any sensitive credentials information from the `Authentication` object that is returned by a successful authentication request.\nThis prevents information, such as passwords, being retained longer than necessary in the `HttpSession`.\n\n[NOTE]\n====\nThe `CredentialsContainer` interface plays a critical role in the authentication process.\nIt allows for the erasure of credential information once it is no longer needed, thereby enhancing security by ensuring sensitive data is not retained longer than necessary.\n====\n\nThis may cause issues when you use a cache of user objects, for example, to improve performance in a stateless application.\nIf the `Authentication` contains a reference to an object in the cache (such as a `UserDetails` instance) and this has its credentials removed, it is no longer possible to authenticate against the cached value.\nYou need to take this into account if you use a cache.\nAn obvious solution is to first make a copy of the object, either in the cache implementation or in the `AuthenticationProvider` that creates the returned `Authentication` object.\nAlternatively, you can disable the `eraseCredentialsAfterAuthentication` property on `ProviderManager`.\nSee the Javadoc for the javadoc:org.springframework.security.authentication.ProviderManager[] class.\n\n[[servlet-authentication-authenticationprovider]]\n== AuthenticationProvider\n\nYou can inject multiple javadoc:org.springframework.security.authentication.AuthenticationProvider[] instances into <<servlet-authentication-providermanager,`ProviderManager`>>.\nEach `AuthenticationProvider` performs a specific type of authentication.\nFor example, xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] supports username/password-based authentication, while `JwtAuthenticationProvider` supports authenticating a JWT token.\n\n[[servlet-authentication-authenticationentrypoint]]\n== Request Credentials with `AuthenticationEntryPoint`\n\njavadoc:org.springframework.security.web.AuthenticationEntryPoint[] is used to send an HTTP response that requests credentials from a client.\n\nSometimes, a client proactively includes credentials (such as a username and password) to request a resource.\nIn these cases, Spring Security does not need to provide an HTTP response that requests credentials from the client, since they are already included.\n\nIn other cases, a client makes an unauthenticated request to a resource that they are not authorized to access.\nIn this case, an implementation of `AuthenticationEntryPoint` is used to request credentials from the client.\nThe `AuthenticationEntryPoint` implementation might perform a xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form[redirect to a log in page], respond with an xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[WWW-Authenticate] header, or take other action.\n\n// FIXME: authenticationsuccesshandler\n// FIXME: authenticationfailurehandler\n\n[[servlet-authentication-abstractprocessingfilter]]\n== AbstractAuthenticationProcessingFilter\n\njavadoc:org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter[] is used as a base `Filter` for authenticating a user's credentials.\nBefore the credentials can be authenticated, Spring Security typically requests the credentials by using <<servlet-authentication-authenticationentrypoint,`AuthenticationEntryPoint`>>.\n\nNext, the `AbstractAuthenticationProcessingFilter` can authenticate any authentication requests that are submitted to it.\n\n[.invert-dark]\nimage::{figures}/abstractauthenticationprocessingfilter.png[]\n\nimage:{icondir}/number_1.png[] When the user submits their credentials, the `AbstractAuthenticationProcessingFilter` creates an <<servlet-authentication-authentication,`Authentication`>> from the `HttpServletRequest` to be authenticated.\nThe type of `Authentication` created depends on the subclass of `AbstractAuthenticationProcessingFilter`.\nFor example, xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] creates a `UsernamePasswordAuthenticationToken` from a __username__ and __password__ that are submitted in the `HttpServletRequest`.\n\nimage:{icondir}/number_2.png[] Next, the <<servlet-authentication-authentication,`Authentication`>> is passed into the <<servlet-authentication-authenticationmanager,`AuthenticationManager`>> to be authenticated.\n\nimage:{icondir}/number_3.png[] If authentication fails, then __Failure__.\n\n* The <<servlet-authentication-securitycontextholder>> is cleared out.\n* `RememberMeServices.loginFail` is invoked.\nIf remember me is not configured, this is a no-op.\nSee the javadoc:org.springframework.security.web.authentication.rememberme.package-summary[rememberme] package.\n* `AuthenticationFailureHandler` is invoked.\nSee the javadoc:org.springframework.security.web.authentication.AuthenticationFailureHandler[] interface.\n\nimage:{icondir}/number_4.png[] If authentication is successful, then __Success__.\n\n* `SessionAuthenticationStrategy` is notified of a new login.\nSee the javadoc:org.springframework.security.web.authentication.session.SessionAuthenticationStrategy[] interface.\n* Any already-authenticated `Authentication` in the <<servlet-authentication-securitycontextholder>> is loaded and its\nauthorities are added to the returned <<servlet-authentication-authentication>>.\n* The <<servlet-authentication-authentication>> is set on the <<servlet-authentication-securitycontextholder>>.\nLater, if you need to save the `SecurityContext` so that it can be automatically set on future requests, `SecurityContextRepository#saveContext` must be explicitly invoked.\nSee the javadoc:org.springframework.security.web.context.SecurityContextHolderFilter[] class.\n\n* `RememberMeServices.loginSuccess` is invoked.\nIf remember me is not configured, this is a no-op.\nSee the javadoc:org.springframework.security.web.authentication.rememberme.package-summary[rememberme] package.\n* `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`.\n* `AuthenticationSuccessHandler` is invoked.\nSee the javadoc:org.springframework.security.web.authentication.AuthenticationSuccessHandler[] interface.\n\n\n// daoauthenticationprovider (goes in username/password)\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/cas.adoc",
    "content": "[[servlet-cas]]\n= CAS Authentication\n\n[[cas-overview]]\n== Overview\nJA-SIG produces an enterprise-wide single sign on system known as CAS.\nUnlike other initiatives, JA-SIG's Central Authentication Service is open source, widely used, simple to understand, platform independent, and supports proxy capabilities.\nSpring Security fully supports CAS, and provides an easy migration path from single-application deployments of Spring Security through to multiple-application deployments secured by an enterprise-wide CAS server.\n\nYou can learn more about CAS at https://www.apereo.org.\nYou will also need to visit this site to download the CAS Server files.\n\n[[cas-how-it-works]]\n== How CAS Works\nWhilst the CAS web site contains documents that detail the architecture of CAS, we present the general overview again here within the context of Spring Security.\nSpring Security 3.x supports CAS 3.\nAt the time of writing, the CAS server was at version 3.4.\n\nSomewhere in your enterprise you will need to setup a CAS server.\nThe CAS server is simply a standard WAR file, so there isn't anything difficult about setting up your server.\nInside the WAR file you will customise the login and other single sign on pages displayed to users.\n\nWhen deploying a CAS 3.4 server, you will also need to specify an `AuthenticationHandler` in the `deployerConfigContext.xml` included with CAS.\nThe `AuthenticationHandler` has a simple method that returns a boolean as to whether a given set of Credentials is valid.\nYour `AuthenticationHandler` implementation will need to link into some type of backend authentication repository, such as an LDAP server or database.\nCAS itself includes numerous ``AuthenticationHandler``s out of the box to assist with this.\nWhen you download and deploy the server war file, it is set up to successfully authenticate users who enter a password matching their username, which is useful for testing.\n\nApart from the CAS server itself, the other key players are of course the secure web applications deployed throughout your enterprise.\nThese web applications are known as \"services\".\nThere are three types of services.\nThose that authenticate service tickets, those that can obtain proxy tickets, and those that authenticate proxy tickets.\nAuthenticating a proxy ticket differs because the list of proxies must be validated and often times a proxy ticket can be reused.\n\n\n[[cas-sequence]]\n=== Spring Security and CAS Interaction Sequence\nThe basic interaction between a web browser, CAS server and a Spring Security-secured service is as follows:\n\n* The web user is browsing the service's public pages.\nCAS or Spring Security is not involved.\n* The user eventually requests a page that is either secure or one of the beans it uses is secure.\nSpring Security's `ExceptionTranslationFilter` will detect the `AccessDeniedException` or `AuthenticationException`.\n* Because the user's `Authentication` object (or lack thereof) caused an `AuthenticationException`, the `ExceptionTranslationFilter` will call the configured `AuthenticationEntryPoint`.\nIf using CAS, this will be the `CasAuthenticationEntryPoint` class.\n* The `CasAuthenticationEntryPoint` will redirect the user's browser to the CAS server.\nIt will also indicate a `service` parameter, which is the callback URL for the Spring Security service (your application).\nFor example, the URL to which the browser is redirected might be https://my.company.com/cas/login?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Flogin/cas.\n* After the user's browser redirects to CAS, they will be prompted for their username and password.\nIf the user presents a session cookie which indicates they've previously logged on, they will not be prompted to login again (there is an exception to this procedure, which we'll cover later).\nCAS will use the `PasswordHandler` (or `AuthenticationHandler` if using CAS 3.0) discussed above to decide whether the username and password is valid.\n* Upon successful login, CAS will redirect the user's browser back to the original service.\nIt will also include a `ticket` parameter, which is an opaque string representing the \"service ticket\".\nContinuing our earlier example, the URL the browser is redirected to might be https://server3.company.com/webapp/login/cas?ticket=ST-0-ER94xMJmn6pha35CQRoZ.\n* Back in the service web application, the `CasAuthenticationFilter` is always listening for requests to `/login/cas` (this is configurable, but we'll use the defaults in this introduction).\nThe processing filter will construct a `UsernamePasswordAuthenticationToken` representing the service ticket.\nThe principal will be equal to `CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER`, whilst the credentials will be the service ticket opaque value.\nThis authentication request will then be handed to the configured `AuthenticationManager`.\n* The `AuthenticationManager` implementation will be the `ProviderManager`, which is in turn configured with the `CasAuthenticationProvider`.\nThe `CasAuthenticationProvider` only responds to ``UsernamePasswordAuthenticationToken``s containing the CAS-specific principal (such as `CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER`) and ``CasAuthenticationToken``s (discussed later).\n* `CasAuthenticationProvider` will validate the service ticket using a `TicketValidator` implementation.\nThis will typically be a `Cas20ServiceTicketValidator` which is one of the classes included in the CAS client library.\nIn the event the application needs to validate proxy tickets, the `Cas20ProxyTicketValidator` is used.\nThe `TicketValidator` makes an HTTPS request to the CAS server in order to validate the service ticket.\nIt may also include a proxy callback URL, which is included in this example: https://my.company.com/cas/proxyValidate?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Flogin/cas&ticket=ST-0-ER94xMJmn6pha35CQRoZ&pgtUrl=https://server3.company.com/webapp/login/cas/proxyreceptor.\n* Back on the CAS server, the validation request will be received.\nIf the presented service ticket matches the service URL the ticket was issued to, CAS will provide an affirmative response in XML indicating the username.\nIf any proxy was involved in the authentication (discussed below), the list of proxies is also included in the XML response.\n* [OPTIONAL] If the request to the CAS validation service included the proxy callback URL (in the `pgtUrl` parameter), CAS will include a `pgtIou` string in the XML response.\nThis `pgtIou` represents a proxy-granting ticket IOU.\nThe CAS server will then create its own HTTPS connection back to the `pgtUrl`.\nThis is to mutually authenticate the CAS server and the claimed service URL.\nThe HTTPS connection will be used to send a proxy granting ticket to the original web application.\nFor example, https://server3.company.com/webapp/login/cas/proxyreceptor?pgtIou=PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt&pgtId=PGT-1-si9YkkHLrtACBo64rmsi3v2nf7cpCResXg5MpESZFArbaZiOKH.\n* The `Cas20TicketValidator` will parse the XML received from the CAS server.\nIt will return to the `CasAuthenticationProvider` a `TicketResponse`, which includes the username (mandatory), proxy list (if any were involved), and proxy-granting ticket IOU (if the proxy callback was requested).\n* Next `CasAuthenticationProvider` will call a configured `CasProxyDecider`.\nThe `CasProxyDecider` indicates whether the proxy list in the `TicketResponse` is acceptable to the service.\nSeveral implementations are provided with Spring Security: `RejectProxyTickets`, `AcceptAnyCasProxy` and `NamedCasProxyDecider`.\nThese names are largely self-explanatory, except `NamedCasProxyDecider` which allows a `List` of trusted proxies to be provided.\n* `CasAuthenticationProvider` will next request a `AuthenticationUserDetailsService` to load the `GrantedAuthority` objects that apply to the user contained in the `Assertion`.\n* If there were no problems, `CasAuthenticationProvider` constructs a `CasAuthenticationToken` including the details contained in the `TicketResponse` and a set of ``GrantedAuthority``s that contains at least `FACTOR_BEARER`.\n* Control then returns to `CasAuthenticationFilter`, which places the created `CasAuthenticationToken` in the security context.\n* The user's browser is redirected to the original page that caused the `AuthenticationException` (or a custom destination depending on the configuration).\n\nIt's good that you're still here!\nLet's now look at how this is configured\n\n[[cas-client]]\n== Configuration of CAS Client\nThe web application side of CAS is made easy due to Spring Security.\nIt is assumed you already know the basics of using Spring Security, so these are not covered again below.\nWe'll assume a namespace based configuration is being used and add in the CAS beans as required.\nEach section builds upon the previous section.\nA full CAS sample application can be found in the Spring Security xref:samples.adoc#samples[Samples].\n\n\n[[cas-st]]\n=== Service Ticket Authentication\nThis section describes how to setup Spring Security to authenticate Service Tickets.\nOften times this is all a web application requires.\nYou will need to add a `ServiceProperties` bean to your application context.\nThis represents your CAS service:\n\n[source,xml]\n----\n<bean id=\"serviceProperties\"\n\tclass=\"org.springframework.security.cas.ServiceProperties\">\n<property name=\"service\"\n\tvalue=\"https://localhost:8443/cas-sample/login/cas\"/>\n<property name=\"sendRenew\" value=\"false\"/>\n</bean>\n----\n\nThe `service` must equal a URL that will be monitored by the `CasAuthenticationFilter`.\nThe `sendRenew` defaults to false, but should be set to true if your application is particularly sensitive.\nWhat this parameter does is tell the CAS login service that a single sign on login is unacceptable.\nInstead, the user will need to re-enter their username and password in order to gain access to the service.\n\nThe following beans should be configured to commence the CAS authentication process (assuming you're using a namespace configuration):\n\n[source,xml]\n----\n<security:http entry-point-ref=\"casEntryPoint\">\n...\n<security:custom-filter position=\"CAS_FILTER\" ref=\"casFilter\" />\n</security:http>\n\n<bean id=\"casFilter\"\n\tclass=\"org.springframework.security.cas.web.CasAuthenticationFilter\">\n<property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n</bean>\n\n<bean id=\"casEntryPoint\"\n\tclass=\"org.springframework.security.cas.web.CasAuthenticationEntryPoint\">\n<property name=\"loginUrl\" value=\"https://localhost:9443/cas/login\"/>\n<property name=\"serviceProperties\" ref=\"serviceProperties\"/>\n</bean>\n----\n\nFor CAS to operate, the `ExceptionTranslationFilter` must have its `authenticationEntryPoint` property set to the `CasAuthenticationEntryPoint` bean.\nThis can easily be done using xref:servlet/appendix/namespace/http.adoc#nsa-http-entry-point-ref[entry-point-ref] as is done in the example above.\nThe `CasAuthenticationEntryPoint` must refer to the `ServiceProperties` bean (discussed above), which provides the URL to the enterprise's CAS login server.\nThis is where the user's browser will be redirected.\n\nThe `CasAuthenticationFilter` has very similar properties to the `UsernamePasswordAuthenticationFilter` (used for form-based logins).\nYou can use these properties to customize things like behavior for authentication success and failure.\n\nNext you need to add a `CasAuthenticationProvider` and its collaborators:\n\n[source,xml,attrs=\"-attributes\"]\n----\n<security:authentication-manager alias=\"authenticationManager\">\n<security:authentication-provider ref=\"casAuthenticationProvider\" />\n</security:authentication-manager>\n\n<bean id=\"casAuthenticationProvider\"\n\tclass=\"org.springframework.security.cas.authentication.CasAuthenticationProvider\">\n<property name=\"authenticationUserDetailsService\">\n\t<bean class=\"org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper\">\n\t<constructor-arg ref=\"userService\" />\n\t</bean>\n</property>\n<property name=\"serviceProperties\" ref=\"serviceProperties\" />\n<property name=\"ticketValidator\">\n\t<bean class=\"org.apereo.cas.client.validation.Cas20ServiceTicketValidator\">\n\t<constructor-arg index=\"0\" value=\"https://localhost:9443/cas\" />\n\t</bean>\n</property>\n<property name=\"key\" value=\"an_id_for_this_auth_provider_only\"/>\n</bean>\n\n<security:user-service id=\"userService\">\n<!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that\nNoOpPasswordEncoder should be used.\nThis is not safe for production, but makes reading\nin samples easier.\nNormally passwords should be hashed using BCrypt -->\n<security:user name=\"joe\" password=\"{noop}joe\" authorities=\"ROLE_USER\" />\n...\n</security:user-service>\n----\n\nThe `CasAuthenticationProvider` uses a `UserDetailsService` instance to load the authorities for a user, once they have been authenticated by CAS.\nWe've shown a simple in-memory setup here.\nNote that the `CasAuthenticationProvider` does not actually use the password for authentication, but it does use the authorities.\n\nThe beans are all reasonably self-explanatory if you refer back to the <<cas-how-it-works,How CAS Works>> section.\n\nThis completes the most basic configuration for CAS.\nIf you haven't made any mistakes, your web application should happily work within the framework of CAS single sign on.\nNo other parts of Spring Security need to be concerned about the fact CAS handled authentication.\nIn the following sections we will discuss some (optional) more advanced configurations.\n\n\n[[cas-singlelogout]]\n=== Single Logout\nThe CAS protocol supports Single Logout and can be easily added to your Spring Security configuration.\nBelow are updates to the Spring Security configuration that handle Single Logout\n\n[source,xml]\n----\n<security:http entry-point-ref=\"casEntryPoint\">\n...\n<security:logout logout-success-url=\"/cas-logout.jsp\"/>\n<security:custom-filter ref=\"requestSingleLogoutFilter\" before=\"LOGOUT_FILTER\"/>\n<security:custom-filter ref=\"singleLogoutFilter\" before=\"CAS_FILTER\"/>\n</security:http>\n\n<!-- This filter handles a Single Logout Request from the CAS Server -->\n<bean id=\"singleLogoutFilter\" class=\"org.apereo.cas.client.session.SingleSignOutFilter\"/>\n\n<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->\n<bean id=\"requestSingleLogoutFilter\"\n\tclass=\"org.springframework.security.web.authentication.logout.LogoutFilter\">\n<constructor-arg value=\"https://localhost:9443/cas/logout\"/>\n<constructor-arg>\n\t<bean class=\n\t\t\"org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler\"/>\n</constructor-arg>\n<property name=\"filterProcessesUrl\" value=\"/logout/cas\"/>\n</bean>\n----\n\nThe `logout` element logs the user out of the local application, but does not end the session with the CAS server or any other applications that have been logged into.\nThe `requestSingleLogoutFilter` filter will allow the URL of `/spring_security_cas_logout` to be requested to redirect the application to the configured CAS Server logout URL.\nThen the CAS Server will send a Single Logout request to all the services that were signed into.\nThe `singleLogoutFilter` handles the Single Logout request by looking up the `HttpSession` in a static `Map` and then invalidating it.\n\nIt might be confusing why both the `logout` element and the `singleLogoutFilter` are needed.\nIt is considered best practice to logout locally first since the `SingleSignOutFilter` just stores the `HttpSession` in a static `Map` in order to call invalidate on it.\nWith the configuration above, the flow of logout would be:\n\n* The user requests `/logout` which would log the user out of the local application and send the user to the logout success page.\n* The logout success page, `/cas-logout.jsp`, should instruct the user to click a link pointing to `/logout/cas` in order to logout out of all applications.\n* When the user clicks the link, the user is redirected to the CAS single logout URL (https://localhost:9443/cas/logout).\n* On the CAS Server side, the CAS single logout URL then submits single logout requests to all the CAS Services.\nOn the CAS Service side, Apereo's `SingleSignOutFilter` processes the logout request by invalidating the original session.\n\n\n\nThe next step is to add the following to your web.xml\n\n[source,xml]\n----\n<filter>\n<filter-name>characterEncodingFilter</filter-name>\n<filter-class>\n\torg.springframework.web.filter.CharacterEncodingFilter\n</filter-class>\n<init-param>\n\t<param-name>encoding</param-name>\n\t<param-value>UTF-8</param-value>\n</init-param>\n</filter>\n<filter-mapping>\n<filter-name>characterEncodingFilter</filter-name>\n<url-pattern>/*</url-pattern>\n</filter-mapping>\n<listener>\n<listener-class>\n\torg.apereo.cas.client.session.SingleSignOutHttpSessionListener\n</listener-class>\n</listener>\n----\n\nWhen using the SingleSignOutFilter you might encounter some encoding issues.\nTherefore it is recommended to add the `CharacterEncodingFilter` to ensure that the character encoding is correct when using the `SingleSignOutFilter`.\nAgain, refer to Apereo CAS's documentation for details.\nThe `SingleSignOutHttpSessionListener` ensures that when an `HttpSession` expires, the mapping used for single logout is removed.\n\n\n[[cas-pt-client]]\n=== Authenticating to a Stateless Service with CAS\nThis section describes how to authenticate to a service using CAS.\nIn other words, this section discusses how to setup a client that uses a service that authenticates with CAS.\nThe next section describes how to setup a stateless service to Authenticate using CAS.\n\n\n[[cas-pt-client-config]]\n==== Configuring CAS to Obtain Proxy Granting Tickets\nIn order to authenticate to a stateless service, the application needs to obtain a proxy granting ticket (PGT).\nThis section describes how to configure Spring Security to obtain a PGT building upon thencas-st[Service Ticket Authentication] configuration.\n\nThe first step is to include a `ProxyGrantingTicketStorage` in your Spring Security configuration.\nThis is used to store PGT's that are obtained by the `CasAuthenticationFilter` so that they can be used to obtain proxy tickets.\nAn example configuration is shown below\n\n[source,xml]\n----\n<!--\nNOTE: In a real application you should not use an in memory implementation.\nYou will also want to ensure to clean up expired tickets by calling\nProxyGrantingTicketStorage.cleanup()\n-->\n<bean id=\"pgtStorage\" class=\"org.apereo.cas.client.proxy.ProxyGrantingTicketStorageImpl\"/>\n----\n\nThe next step is to update the `CasAuthenticationProvider` to be able to obtain proxy tickets.\nTo do this replace the `Cas20ServiceTicketValidator` with a `Cas20ProxyTicketValidator`.\nThe `proxyCallbackUrl` should be set to a URL that the application will receive PGT's at.\nLast, the configuration should also reference the `ProxyGrantingTicketStorage` so it can use a PGT to obtain proxy tickets.\nYou can find an example of the configuration changes that should be made below.\n\n[source,xml]\n----\n<bean id=\"casAuthenticationProvider\"\n\tclass=\"org.springframework.security.cas.authentication.CasAuthenticationProvider\">\n...\n<property name=\"ticketValidator\">\n\t<bean class=\"org.apereo.cas.client.validation.Cas20ProxyTicketValidator\">\n\t<constructor-arg value=\"https://localhost:9443/cas\"/>\n\t\t<property name=\"proxyCallbackUrl\"\n\t\tvalue=\"https://localhost:8443/cas-sample/login/cas/proxyreceptor\"/>\n\t<property name=\"proxyGrantingTicketStorage\" ref=\"pgtStorage\"/>\n\t</bean>\n</property>\n</bean>\n----\n\nThe last step is to update the `CasAuthenticationFilter` to accept PGT and to store them in the `ProxyGrantingTicketStorage`.\nIt is important the `proxyReceptorUrl` matches the `proxyCallbackUrl` of the `Cas20ProxyTicketValidator`.\nAn example configuration is shown below.\n\n[source,xml]\n----\n\n<bean id=\"casFilter\"\n\t\tclass=\"org.springframework.security.cas.web.CasAuthenticationFilter\">\n\t...\n\t<property name=\"proxyGrantingTicketStorage\" ref=\"pgtStorage\"/>\n\t<property name=\"proxyReceptorUrl\" value=\"/login/cas/proxyreceptor\"/>\n</bean>\n\n----\n\n[[cas-pt-client-sample]]\n==== Calling a Stateless Service Using a Proxy Ticket\nNow that Spring Security obtains PGTs, you can use them to create proxy tickets which can be used to authenticate to a stateless service.\nThe CAS xref:samples.adoc#samples[sample application] contains a working example in the `ProxyTicketSampleServlet`.\nExample code can be found below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprotected void doGet(HttpServletRequest request, HttpServletResponse response)\n\tthrows ServletException, IOException {\n// NOTE: The CasAuthenticationToken can also be obtained using\n// SecurityContextHolder.getContext().getAuthentication()\nfinal CasAuthenticationToken token = (CasAuthenticationToken) request.getUserPrincipal();\n// proxyTicket could be reused to make calls to the CAS service even if the\n// target url differs\nfinal String proxyTicket = token.getAssertion().getPrincipal().getProxyTicketFor(targetUrl);\n\n// Make a remote call using the proxy ticket\nfinal String serviceUrl = targetUrl+\"?ticket=\"+URLEncoder.encode(proxyTicket, \"UTF-8\");\nString proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, \"UTF-8\");\n...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nprotected fun doGet(request: HttpServletRequest, response: HttpServletResponse?) {\n    // NOTE: The CasAuthenticationToken can also be obtained using\n    // SecurityContextHolder.getContext().getAuthentication()\n    val token = request.userPrincipal as CasAuthenticationToken\n    // proxyTicket could be reused to make calls to the CAS service even if the\n    // target url differs\n    val proxyTicket = token.assertion.principal.getProxyTicketFor(targetUrl)\n\n    // Make a remote call using the proxy ticket\n    val serviceUrl: String = targetUrl + \"?ticket=\" + URLEncoder.encode(proxyTicket, \"UTF-8\")\n    val proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, \"UTF-8\")\n}\n----\n======\n\n[[cas-pt]]\n=== Proxy Ticket Authentication\nThe `CasAuthenticationProvider` distinguishes between stateful and stateless clients.\nA stateful client is considered any that submits to the `filterProcessesUrl` of the `CasAuthenticationFilter`.\nA stateless client is any that presents an authentication request to `CasAuthenticationFilter` on a URL other than the `filterProcessesUrl`.\n\nBecause remoting protocols have no way of presenting themselves within the context of an `HttpSession`, it isn't possible to rely on the default practice of storing the security context in the session between requests.\nFurthermore, because the CAS server invalidates a ticket after it has been validated by the `TicketValidator`, presenting the same proxy ticket on subsequent requests will not work.\n\nOne obvious option is to not use CAS at all for remoting protocol clients.\nHowever, this would eliminate many of the desirable features of CAS.\nAs a middle-ground, the `CasAuthenticationProvider` uses a `StatelessTicketCache`.\nThis is used solely for stateless clients which use a principal equal to `CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER`.\nWhat happens is the `CasAuthenticationProvider` will store the resulting `CasAuthenticationToken` in the `StatelessTicketCache`, keyed on the proxy ticket.\nAccordingly, remoting protocol clients can present the same proxy ticket and the `CasAuthenticationProvider` will not need to contact the CAS server for validation (aside from the first request).\nOnce authenticated, the proxy ticket could be used for URLs other than the original target service.\n\nThis section builds upon the previous sections to accommodate proxy ticket authentication.\nThe first step is to specify to authenticate all artifacts as shown below.\n\n[source,xml]\n----\n<bean id=\"serviceProperties\"\n\tclass=\"org.springframework.security.cas.ServiceProperties\">\n...\n<property name=\"authenticateAllArtifacts\" value=\"true\"/>\n</bean>\n----\n\nThe next step is to specify `serviceProperties` and the `authenticationDetailsSource` for the `CasAuthenticationFilter`.\nThe `serviceProperties` property instructs the `CasAuthenticationFilter` to attempt to authenticate all artifacts instead of only ones present on the `filterProcessesUrl`.\nThe `ServiceAuthenticationDetailsSource` creates a `ServiceAuthenticationDetails` that ensures the current URL, based upon the `HttpServletRequest`, is used as the service URL when validating the ticket.\nThe method for generating the service URL can be customized by injecting a custom `AuthenticationDetailsSource` that returns a custom `ServiceAuthenticationDetails`.\n\n[source,xml]\n----\n<bean id=\"casFilter\"\n\tclass=\"org.springframework.security.cas.web.CasAuthenticationFilter\">\n...\n<property name=\"serviceProperties\" ref=\"serviceProperties\"/>\n<property name=\"authenticationDetailsSource\">\n\t<bean class=\n\t\"org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource\">\n\t<constructor-arg ref=\"serviceProperties\"/>\n\t</bean>\n</property>\n</bean>\n----\n\nYou will also need to update the `CasAuthenticationProvider` to handle proxy tickets.\nTo do this replace the `Cas20ServiceTicketValidator` with a `Cas20ProxyTicketValidator`.\nYou will need to configure the `statelessTicketCache` and which proxies you want to accept.\nYou can find an example of the updates required to accept all proxies below.\n\n[source,xml]\n----\n\n<bean id=\"casAuthenticationProvider\"\n\tclass=\"org.springframework.security.cas.authentication.CasAuthenticationProvider\">\n...\n<property name=\"ticketValidator\">\n\t<bean class=\"org.apereo.cas.client.validation.Cas20ProxyTicketValidator\">\n\t<constructor-arg value=\"https://localhost:9443/cas\"/>\n\t<property name=\"acceptAnyProxy\" value=\"true\"/>\n\t</bean>\n</property>\n<property name=\"statelessTicketCache\">\n\t<bean class=\"org.springframework.security.cas.authentication.SpringCacheBasedTicketCache\">\n\t<property name=\"cache\">\n\t\t<bean class=\"net.sf.ehcache.Cache\"\n\t\t\tinit-method=\"initialise\" destroy-method=\"dispose\">\n\t\t<constructor-arg value=\"casTickets\"/>\n\t\t<constructor-arg value=\"50\"/>\n\t\t<constructor-arg value=\"true\"/>\n\t\t<constructor-arg value=\"false\"/>\n\t\t<constructor-arg value=\"3600\"/>\n\t\t<constructor-arg value=\"900\"/>\n\t\t</bean>\n\t</property>\n\t</bean>\n</property>\n</bean>\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/events.adoc",
    "content": "[[servlet-events]]\n= Authentication Events\n\nFor each authentication that succeeds or fails, a `AuthenticationSuccessEvent` or `AuthenticationFailureEvent`, respectively, is fired.\n\nTo listen for these events, you must first publish an `AuthenticationEventPublisher`.\nSpring Security's `DefaultAuthenticationEventPublisher` works fine for this purpose:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic AuthenticationEventPublisher authenticationEventPublisher\n        (ApplicationEventPublisher applicationEventPublisher) {\n    return new DefaultAuthenticationEventPublisher(applicationEventPublisher);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationEventPublisher\n        (applicationEventPublisher: ApplicationEventPublisher?): AuthenticationEventPublisher {\n    return DefaultAuthenticationEventPublisher(applicationEventPublisher)\n}\n----\n======\n\nThen you can use Spring's `@EventListener` support:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class AuthenticationEvents {\n\t@EventListener\n    public void onSuccess(AuthenticationSuccessEvent success) {\n\t\t// ...\n    }\n\n    @EventListener\n    public void onFailure(AbstractAuthenticationFailureEvent failures) {\n\t\t// ...\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass AuthenticationEvents {\n    @EventListener\n    fun onSuccess(success: AuthenticationSuccessEvent?) {\n        // ...\n    }\n\n    @EventListener\n    fun onFailure(failures: AbstractAuthenticationFailureEvent?) {\n        // ...\n    }\n}\n----\n======\n\nWhile similar to `AuthenticationSuccessHandler` and `AuthenticationFailureHandler`, these are nice in that they can be used independently from the servlet API.\n\n== Adding Exception Mappings\n\nBy default, `DefaultAuthenticationEventPublisher` publishes an `AuthenticationFailureEvent` for the following events:\n\n|============\n| Exception | Event\n| `BadCredentialsException` | `AuthenticationFailureBadCredentialsEvent`\n| `UsernameNotFoundException` | `AuthenticationFailureBadCredentialsEvent`\n| `AccountExpiredException` | `AuthenticationFailureExpiredEvent`\n| `ProviderNotFoundException` | `AuthenticationFailureProviderNotFoundEvent`\n| `DisabledException` | `AuthenticationFailureDisabledEvent`\n| `LockedException` | `AuthenticationFailureLockedEvent`\n| `AuthenticationServiceException` | `AuthenticationFailureServiceExceptionEvent`\n| `CredentialsExpiredException` | `AuthenticationFailureCredentialsExpiredEvent`\n| `InvalidBearerTokenException` | `AuthenticationFailureBadCredentialsEvent`\n|============\n\nThe publisher does an exact `Exception` match, which means that sub-classes of these exceptions do not also produce events.\n\nTo that end, you may want to supply additional mappings to the publisher through the `setAdditionalExceptionMappings` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic AuthenticationEventPublisher authenticationEventPublisher\n        (ApplicationEventPublisher applicationEventPublisher) {\n    Map<Class<? extends AuthenticationException>,\n        Class<? extends AbstractAuthenticationFailureEvent>> mapping =\n            Collections.singletonMap(FooException.class, FooEvent.class);\n    DefaultAuthenticationEventPublisher authenticationEventPublisher =\n        new DefaultAuthenticationEventPublisher(applicationEventPublisher);\n    authenticationEventPublisher.setAdditionalExceptionMappings(mapping);\n    return authenticationEventPublisher;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationEventPublisher\n        (applicationEventPublisher: ApplicationEventPublisher?): AuthenticationEventPublisher {\n    val mapping: Map<Class<out AuthenticationException>, Class<out AbstractAuthenticationFailureEvent>> =\n            mapOf(Pair(FooException::class.java, FooEvent::class.java))\n    val authenticationEventPublisher = DefaultAuthenticationEventPublisher(applicationEventPublisher)\n    authenticationEventPublisher.setAdditionalExceptionMappings(mapping)\n    return authenticationEventPublisher\n}\n----\n======\n\n== Default Event\n\nYou can also supply a catch-all event to fire in the case of any `AuthenticationException`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic AuthenticationEventPublisher authenticationEventPublisher\n        (ApplicationEventPublisher applicationEventPublisher) {\n    DefaultAuthenticationEventPublisher authenticationEventPublisher =\n        new DefaultAuthenticationEventPublisher(applicationEventPublisher);\n    authenticationEventPublisher.setDefaultAuthenticationFailureEvent\n        (AbstractAuthenticationFailureEvent.class);\n    return authenticationEventPublisher;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationEventPublisher\n        (applicationEventPublisher: ApplicationEventPublisher?): AuthenticationEventPublisher {\n    val authenticationEventPublisher = DefaultAuthenticationEventPublisher(applicationEventPublisher)\n    authenticationEventPublisher.setDefaultAuthenticationFailureEvent(AbstractAuthenticationFailureEvent::class.java)\n    return authenticationEventPublisher\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/index.adoc",
    "content": "[[servlet-authentication]]\n= Authentication\n\nSpring Security provides comprehensive support for xref:features/authentication/index.adoc#authentication[Authentication].\nWe start by discussing the overall xref:servlet/authentication/architecture.adoc[Servlet Authentication Architecture].\nAs you might expect, this section is more abstract describing the architecture without much discussion on how it applies to concrete flows.\n\nIf you prefer, you can refer to <<servlet-authentication-mechanisms,Authentication Mechanisms>> for concrete ways in which users can authenticate.\nThese sections focus on specific ways you may want to authenticate and point back at the architecture sections to describe how the specific flows work.\n\n[[servlet-authentication-mechanisms]]\n== Authentication Mechanisms\n\n// FIXME: brief description\n\n* xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd[Username and Password] - how to authenticate with a username/password\n* xref:servlet/oauth2/login/index.adoc#oauth2login[OAuth 2.0 Login] - OAuth 2.0 Log In with OpenID Connect and non-standard OAuth 2.0 Login (i.e. GitHub)\n* xref:servlet/saml2/index.adoc#servlet-saml2[SAML 2.0 Login] - SAML 2.0 Log In\n* xref:servlet/authentication/cas.adoc#servlet-cas[Central Authentication Server (CAS)] - Central Authentication Server (CAS) Support\n* xref:servlet/authentication/rememberme.adoc#servlet-rememberme[Remember Me] - how to remember a user past session expiration\n* xref:servlet/authentication/jaas.adoc#servlet-jaas[JAAS Authentication] - authenticate with JAAS\n* xref:servlet/authentication/preauth.adoc#servlet-preauth[Pre-Authentication Scenarios] - authenticate with an external mechanism such as https://www.siteminder.com/[SiteMinder] or Java EE security but still use Spring Security for authorization and protection against common exploits.\n* xref:servlet/authentication/x509.adoc#servlet-x509[X509 Authentication] - X509 Authentication\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/jaas.adoc",
    "content": "[[servlet-jaas]]\n= Java Authentication and Authorization Service (JAAS) Provider\n\nSpring Security provides a package to delegate authentication requests to the Java Authentication and Authorization Service (JAAS).\nThis section discusses that package.\n\n\n[[jaas-abstractjaasauthenticationprovider]]\n== AbstractJaasAuthenticationProvider\nThe `AbstractJaasAuthenticationProvider` class is the basis for the provided JAAS `AuthenticationProvider` implementations.\nSubclasses must implement a method that creates the `LoginContext`.\nThe `AbstractJaasAuthenticationProvider` has a number of dependencies that can be injected into it, as discussed in the remainder of this section.\n\n\n[[jaas-callbackhandler]]\n=== JAAS CallbackHandler\nMost JAAS `LoginModule` instances require a callback of some sort.\nThese callbacks are usually used to obtain the username and password from the user.\n\nIn a Spring Security deployment, Spring Security is responsible for this user interaction (through the authentication mechanism).\nThus, by the time the authentication request is delegated through to JAAS, Spring Security's authentication mechanism has already fully populated an `Authentication` object that contains all the information required by the JAAS `LoginModule`.\n\nTherefore, the JAAS package for Spring Security provides two default callback handlers: `JaasNameCallbackHandler` and `JaasPasswordCallbackHandler`.\nEach of these callback handlers implements `JaasAuthenticationCallbackHandler`.\nIn most cases, these callback handlers can be used without understanding the internal mechanics.\n\nFor those needing full control over the callback behavior, `AbstractJaasAuthenticationProvider` internally wraps these `JaasAuthenticationCallbackHandler` instances with an `InternalCallbackHandler`.\nThe `InternalCallbackHandler` is the class that actually implements the JAAS normal `CallbackHandler` interface.\nAny time that the JAAS `LoginModule` is used, it is passed a list of application contexts configured `InternalCallbackHandler` instances.\nIf the `LoginModule` requests a callback against the `InternalCallbackHandler` instances, the callback is, in turn, passed to the `JaasAuthenticationCallbackHandler` instances being wrapped.\n\n[[jaas-authoritygranter]]\n=== JAAS AuthorityGranter\nJAAS works with principals.\nEven \"`roles`\" are represented as principals in JAAS.\nSpring Security, on the other hand, works with `Authentication` objects.\nEach `Authentication` object contains a single principal and multiple `GrantedAuthority` instances.\nTo facilitate mapping between these different concepts, Spring Security's JAAS package includes an `AuthorityGranter` interface.\n\nAn `AuthorityGranter` is responsible for inspecting a JAAS principal and returning a set of `String` objects that represent the authorities assigned to the principal.\nFor each returned authority string, the `AbstractJaasAuthenticationProvider` creates a `JaasGrantedAuthority` (which implements Spring Security's `GrantedAuthority` interface) that contains the authority string and the JAAS principal that the `AuthorityGranter` was passed.\nThe `AbstractJaasAuthenticationProvider` obtains the JAAS principals by first successfully authenticating the user's credentials by using the JAAS `LoginModule` and then accessing the `LoginContext` it returns.\nA call to `LoginContext.getSubject().getPrincipals()` is made, with each resulting principal passed to each `AuthorityGranter` defined against the `AbstractJaasAuthenticationProvider.setAuthorityGranters(List)` property.\n\nSpring Security does not include any production `AuthorityGranter` instances, given that every JAAS principal has an implementation-specific meaning.\nHowever, Spring Security does issue the `FACTOR_PASSWORD` authority by default when authentication suceeds.\n\n[TIP]\n====\nThere is a `TestAuthorityGranter` in the unit tests that demonstrates a simple `AuthorityGranter` implementation.\n====\n\n[[jaas-defaultjaasauthenticationprovider]]\n== DefaultJaasAuthenticationProvider\nThe `DefaultJaasAuthenticationProvider` lets a JAAS `Configuration` object be injected into it as a dependency.\nIt then creates a `LoginContext` by using the injected JAAS `Configuration`.\nThis means that `DefaultJaasAuthenticationProvider` is not bound to any particular implementation of `Configuration`, as `JaasAuthenticationProvider` is.\n\n\n[[jaas-inmemoryconfiguration]]\n=== InMemoryConfiguration\nTo make it easy to inject a `Configuration` into `DefaultJaasAuthenticationProvider`, a default in-memory implementation named `InMemoryConfiguration` is provided.\nThe implementation constructor accepts a `Map` where each key represents a login configuration name, and the value represents an `Array` of `AppConfigurationEntry` instances.\n`InMemoryConfiguration` also supports a default `Array` of `AppConfigurationEntry` objects that is used if no mapping is found within the provided `Map`.\nFor details, see the Javadoc of javadoc:org.springframework.security.authentication.jaas.memory.InMemoryConfiguration[].\n\n\n[[jaas-djap-config]]\n=== DefaultJaasAuthenticationProvider Example Configuration\nWhile the Spring configuration for `InMemoryConfiguration` can be more verbose than the standard JAAS configuration files, using it in conjunction with `DefaultJaasAuthenticationProvider` is more flexible than `JaasAuthenticationProvider`, since it not dependent on the default `Configuration` implementation.\n\nThe next example provides a configuration of `DefaultJaasAuthenticationProvider` that uses `InMemoryConfiguration`.\nNote that custom implementations of `Configuration` can easily be injected into `DefaultJaasAuthenticationProvider` as well.\n\n[source,xml]\n----\n<bean id=\"jaasAuthProvider\"\nclass=\"org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider\">\n<property name=\"configuration\">\n<bean class=\"org.springframework.security.authentication.jaas.memory.InMemoryConfiguration\">\n<constructor-arg>\n\t<map>\n\t<!--\n\tSPRINGSECURITY is the default loginContextName\n\tfor AbstractJaasAuthenticationProvider\n\t-->\n\t<entry key=\"SPRINGSECURITY\">\n\t<array>\n\t<bean class=\"javax.security.auth.login.AppConfigurationEntry\">\n\t\t<constructor-arg value=\"sample.SampleLoginModule\" />\n\t\t<constructor-arg>\n\t\t<util:constant static-field=\n\t\t\t\"javax.security.auth.login.AppConfigurationEntry$LoginModuleControlFlag.REQUIRED\"/>\n\t\t</constructor-arg>\n\t\t<constructor-arg>\n\t\t<map></map>\n\t\t</constructor-arg>\n\t\t</bean>\n\t</array>\n\t</entry>\n\t</map>\n\t</constructor-arg>\n</bean>\n</property>\n<property name=\"authorityGranters\">\n<list>\n\t<!-- You will need to write your own implementation of AuthorityGranter -->\n\t<bean class=\"org.springframework.security.authentication.jaas.TestAuthorityGranter\"/>\n</list>\n</property>\n</bean>\n----\n\n\n[[jaas-jaasauthenticationprovider]]\n== JaasAuthenticationProvider\nThe `JaasAuthenticationProvider` assumes that the default `Configuration` is an instance of https://docs.oracle.com/javase/8/docs/jre/api/security/jaas/spec/com/sun/security/auth/login/ConfigFile.html[`ConfigFile`].\nThis assumption is made in order to try to update the `Configuration`.\nThe `JaasAuthenticationProvider` then uses the default `Configuration` to create the `LoginContext`.\n\nAssume that we have a JAAS login configuration file, `/WEB-INF/login.conf`, with the following contents:\n\n[source,txt]\n----\nJAASTest {\n\tsample.SampleLoginModule required;\n};\n----\n\nLike all Spring Security beans, the `JaasAuthenticationProvider` is configured through the application context.\nThe following definitions would correspond to the above JAAS login configuration file:\n\n[source,xml]\n----\n\n<bean id=\"jaasAuthenticationProvider\"\nclass=\"org.springframework.security.authentication.jaas.JaasAuthenticationProvider\">\n<property name=\"loginConfig\" value=\"/WEB-INF/login.conf\"/>\n<property name=\"loginContextName\" value=\"JAASTest\"/>\n<property name=\"callbackHandlers\">\n<list>\n<bean\n\tclass=\"org.springframework.security.authentication.jaas.JaasNameCallbackHandler\"/>\n<bean\n\tclass=\"org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler\"/>\n</list>\n</property>\n<property name=\"authorityGranters\">\n\t<list>\n\t<bean class=\"org.springframework.security.authentication.jaas.TestAuthorityGranter\"/>\n\t</list>\n</property>\n</bean>\n----\n\n[[jaas-apiprovision]]\n== Running as a Subject\nIf configured, the `JaasApiIntegrationFilter` tries to run as the `Subject` on the `JaasAuthenticationToken`.\nThis means that the `Subject` can be accessed using:\n\n[source,java]\n----\nSubject subject = Subject.getSubject(AccessController.getContext());\n----\n\nYou can configure this integration by using the xref:servlet/appendix/namespace/http.adoc#nsa-http-jaas-api-provision[jaas-api-provision] attribute.\nThis feature is useful when integrating with legacy or external API's that rely on the JAAS Subject being populated.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/kerberos/appendix.adoc",
    "content": "[[appendices]]\n= Appendices\n:figures: servlet/authentication/kerberos\n:numbered!:\n\n[appendix]\n== Material Used in this Document\nDummy UserDetailsService used in samples because we don't have a real\nuser source.\n\n[source,java,indent=0]\n----\ninclude::example$kerberos/DummyUserDetailsService.java[tags=snippetA]\n----\n\n[appendix]\n== Crash Course to Kerberos\nIn any authentication process there are usually a three parties\ninvolved.\n\nimage::{figures}/drawio-kerb-cc1.png[]\n\nFirst is a `client` which sometimes is a client computer but in most\nof the scenarios it is the actual user sitting on a computer and\ntrying to access resources. Then there is the `resource` user is trying\nto access. In this example it is a web server.\n\nThen there is a `Key Distribution Center` or `KDC`. In a case of\nWindows environment this would be a `Domain Controller`. `KDC` is the\none which really brings everything together and thus is the most\ncritical component in your environment. Because of this it is also\nconsidered as a single point of failure.\n\nInitially when `Kerberos` environment is setup and domain user\nprincipals created into a database, encryption keys are also\ncreated. These encryption keys are based on shared secrets(i.e. user\npassword) and actual passwords are never kept in a clear text.\nEffectively `KDC` has its own key and other keys for domain users.\n\nInterestingly there is no communication between a `resource` and a\n`KDC` during the authentication process.\n\nimage::{figures}/drawio-kerb-cc2.png[]\n\nWhen client wants to authenticate itself with a `resource` it first\nneeds to communicate with a `KDC`. `Client` will craft a special package\nwhich contains encrypted and unencrypted parts. Unencrypted part\ncontains i.e. information about a user and encrypted part other\ninformation which is part of a protocol. `Client` will encrypt package\ndata with its own key.\n\nWhen `KDC` receives this authentication package from a client it\nchecks who this `client` claims to be from an unencrypted part and based\non that information it uses `client` decryption key it already have in\nits database. If this decryption is successful `KDC` knows that this\n`client` is the one it claims to be.\n\nWhat KDC returns to a client is a ticket called `Ticket Granting\nTicket` which is signed by a KDC's own private key. Later when\n`client` sends back this ticket it can try to decrypt it and if that\noperation is successful it knows that it was a ticket it itself\noriginally signed and gave to a `client`.\n\nimage::{figures}/drawio-kerb-cc3.png[]\n\nWhen client wants to get a ticket which it can use to authenticate\nwith a service, `TGT` is sent to `KDC` which then signs a service ticket\nwith service's own key. This a moment when a trust between\n`client` and `service` is created. This service ticket contains data\nwhich only `service` itself is able to decrypt.\n\nimage::{figures}/drawio-kerb-cc4.png[]\n\nWhen `client` is authenticating with a service it sends previously\nreceived service ticket to a service which then thinks that I don't\nknow anything about this guy but he gave me an authentication ticket.\nWhat `service` can do next is try to decrypt that ticket and if that\noperation is successful it knows that only other party who knows my\ncredentials is the `KDC` and because I trust him I can also trust that\nthis client is a one he claims to be.\n\n[appendix]\n== Setup Kerberos Environments\nDoing a production setup of Kerberos environment is out of scope of\nthis document but this appendix provides some help to get you\nstarted for setting up needed components for development.\n\n[[setupmitkerberos]]\n=== Setup MIT Kerberos\nFirst action is to setup a new realm and a database.\n\n[source,text,indent=0]\n----\n# kdb5_util create -s -r EXAMPLE.ORG\nLoading random data\nInitializing database '/var/lib/krb5kdc/principal' for realm 'EXAMPLE.ORG',\nmaster key name 'K/M@EXAMPLE.ORG'\nYou will be prompted for the database Master Password.\nIt is important that you NOT FORGET this password.\nEnter KDC database master key:\nRe-enter KDC database master key to verify:\n----\n\n`kadmin` command can be used to administer Kerberos environment but\nyou can't yet use it because there are no admin users in a database.\n\n[source,text,indent=0]\n----\nroot@neo:/etc/krb5kdc# kadmin\nAuthenticating as principal root/admin@EXAMPLE.ORG with password.\nkadmin: Client not found in Kerberos database while initializing\nkadmin interface\n----\n\nLets use `kadmin.local` command to create one.\n\n[source,text,indent=0]\n----\nroot@neo:/etc/krb5kdc# kadmin.local\nAuthenticating as principal root/admin@EXAMPLE.ORG with password.\n\nkadmin.local:  listprincs\nK/M@EXAMPLE.ORG\nkadmin/admin@EXAMPLE.ORG\nkadmin/changepw@EXAMPLE.ORG\nkadmin/cypher@EXAMPLE.ORG\nkrbtgt/EXAMPLE.ORG@EXAMPLE.ORG\n\nkadmin.local:  addprinc root/admin@EXAMPLE.ORG\nWARNING: no policy specified for root/admin@EXAMPLE.ORG; defaulting to\nno policy\nEnter password for principal \"root/admin@EXAMPLE.ORG\":\nRe-enter password for principal \"root/admin@EXAMPLE.ORG\":\nPrincipal \"root/admin@EXAMPLE.ORG\" created.\n----\n\nThen enable admins by modifying `kadm5.acl` file and restart Kerberos\nservices.\n\n[source,text,indent=0]\n----\n# cat /etc/krb5kdc/kadm5.acl\n# This file Is the access control list for krb5 administration.\n*/admin *\n----\n\nNow you can use `kadmin` with previously created `root/admin`\nprincipal. Lets create our first user `user1`.\n\n[source,text,indent=0]\n----\nkadmin:  addprinc user1\nWARNING: no policy specified for user1@EXAMPLE.ORG; defaulting to no\npolicy\nEnter password for principal \"user1@EXAMPLE.ORG\":\nRe-enter password for principal \"user1@EXAMPLE.ORG\":\nPrincipal \"user1@EXAMPLE.ORG\" created.\n----\n\nLets create our second user `user2` and export a keytab file.\n\n[source,text,indent=0]\n----\nkadmin:  addprinc user2\nWARNING: no policy specified for user2@EXAMPLE.ORG; defaulting to no\npolicy\nEnter password for principal \"user2@EXAMPLE.ORG\":\nRe-enter password for principal \"user2@EXAMPLE.ORG\":\nPrincipal \"user2@EXAMPLE.ORG\" created.\n\nkadmin:  ktadd -k /tmp/user2.keytab user2@EXAMPLE.ORG\nEntry for principal user2@EXAMPLE.ORG with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/user2.keytab.\nEntry for principal user2@EXAMPLE.ORG with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/tmp/user2.keytab.\nEntry for principal user2@EXAMPLE.ORG with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/tmp/user2.keytab.\nEntry for principal user2@EXAMPLE.ORG with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/tmp/user2.keytab.\n----\n\nLets create a service ticket for tomcat and export credentials to a\nkeytab file named `tomcat.keytab`.\n\n[source,text,indent=0]\n----\nkadmin:  addprinc -randkey HTTP/neo.example.org@EXAMPLE.ORG\nWARNING: no policy specified for HTTP/neo.example.org@EXAMPLE.ORG;\ndefaulting to no policy\nPrincipal \"HTTP/neo.example.org@EXAMPLE.ORG\" created.\n\nkadmin:  ktadd -k /tmp/tomcat.keytab HTTP/neo.example.org@EXAMPLE.ORG\nEntry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/tomcat2.keytab.\nEntry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/tmp/tomcat2.keytab.\nEntry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/tmp/tomcat2.keytab.\nEntry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/tmp/tomcat2.keytab.\n----\n\n[[setupwinkerberos]]\n=== Setup Windows Domain Controller\n\nThis was tested using `Windows Server 2012 R2`\n\n[TIP]\n====\nInternet is full of good articles and videos how to setup Windows AD\nbut these two are quite usefull\nhttps://www.rackspace.com/knowledge_center/article/installing-active-directory-on-windows-server-2012[Rackspace] and\nhttps://social.technet.microsoft.com/wiki/contents/articles/12370.windows-server-2012-set-up-your-first-domain-controller-step-by-step.aspx[Microsoft\nTechnet].\n====\n\n- Normal domain controller and active directory setup was done.\n- Used dns domain `example.org` and windows domain `EXAMPLE`.\n- I created various domain users like `user1`, `user2`, `user3`,\n  `tomcat` and set passwords to `Password#`.\n\nI eventually also added all ip's of my vm's to AD's dns server for\nthat not to cause any trouble.\n\n[source,text]\n----\nName: WIN-EKBO0EQ7TS7.example.org\nAddress: 172.16.101.135\n\nName: win8vm.example.org\nAddress: 172.16.101.136\n\nName: neo.example.org\nAddress: 172.16.101.1\n----\n\nService Principal Name(SPN) needs to be setup with `HTTP` and a\nserver name `neo.example.org` where tomcat servlet container is run. This\nis used with `tomcat` domain user and its `keytab` is then used as a\nservice credential.\n\n[source,text]\n----\nPS C:\\> setspn -A HTTP/neo.example.org tomcat\n----\n\nI exported keytab file which is copied to linux server running tomcat.\n\n[source,text]\n----\nPS C:\\> ktpass /out c:\\tomcat.keytab /mapuser tomcat@EXAMPLE.ORG /princ HTTP/neo.example.org@EXAMPLE.ORG /pass Password# /ptype KRB5_NT_PRINCIPAL /crypto All\n Targeting domain controller: WIN-EKBO0EQ7TS7.example.org\n Using legacy password setting method\n Successfully mapped HTTP/neo.example.org to tomcat.\n----\n\n[appendix]\n== Troubleshooting\nThis appendix provides generic information about troubleshooting\nerrors and problems.\n\n[IMPORTANT]\n====\nIf you think environment and configuration is correctly setup, do\ndouble check and ask other person to check possible obvious mistakes\nor typos. Kerberos setup is generally very brittle and it is not\nalways very easy to debug where the problem lies.\n====\n\n.Cannot find key of appropriate type to decrypt\n\n[source,text]\n----\nGSSException: Failure unspecified at GSS-API level (Mechanism level:\nInvalid argument (400) - Cannot find key of appropriate type to\ndecrypt AP REP - RC4 with HMAC)\n----\n\nIf you see abore error indicating missing key type, this will happen\nwith two different use cases. Firstly your JVM may not support\nappropriate encryption type or it is disabled in your `krb5.conf`\nfile.\n\n[source,text]\n----\ndefault_tkt_enctypes = rc4-hmac\ndefault_tgs_enctypes = rc4-hmac\n----\n\nSecond case is less obvious and hard to track because it will lead\ninto same error. This specific `GSSException` is throws also if you\nsimply don't have a required encryption key which then may be caused\nby a misconfiguration in your kerberos server or a simply typo in your\nprincipal.\n\n.Using wrong kerberos configuration\n\n{zwsp} +\n\nIn most system all commands and libraries will search kerberos\nconfiguration either from a default locations or special locations\nlike JDKs. It's easy to get mixed up especially if working from unix\nsystems, which already may have default settings to work with MIT\nkerberos, towards Windows domains.\n\nThis is a specific example what happens with `ldapsearch` trying to\nquery Windows AD using kerberos authentication.\n\n[source,text]\n----\n$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b \"dc=example,dc=org\"\nSASL/GSSAPI authentication started\nldap_sasl_interactive_bind_s: Local error (-2)\n  additional info: SASL(-1): generic failure: GSSAPI Error:\n  Unspecified GSS failure.  Minor code may provide more information\n  (No Kerberos credentials available)\n----\n\nWell that doesn't look good and is a simple indication that I don't\nhave a valid kerberos tickets as shown below.\n\n[source,text]\n----\n$ klist\nklist: Credentials cache file '/tmp/krb5cc_1000' not found\n----\n\nWe already have a keytab file we exported from Windows AD to be used\nwith tomcat running on Linux. Lets try to use that to authenticate\nwith Windows AD.\n\nYou can have a dedicated config file which usually can be used with\nnative Linux commands and JVMs via system propertys.\n\n[source,text]\n----\n$ cat krb5.ini\n[libdefaults]\ndefault_realm = EXAMPLE.ORG\ndefault_keytab_name = /tmp/tomcat.keytab\nforwardable=true\n\n[realms]\nEXAMPLE.ORG = {\n  kdc = WIN-EKBO0EQ7TS7.example.org:88\n}\n\n[domain_realm]\nexample.org=EXAMPLE.ORG\n.example.org=EXAMPLE.ORG\n----\n\nLets use that config and a keytab to get initial credentials.\n\n[source,text]\n----\n$ env KRB5_CONFIG=/path/to/krb5.ini kinit -kt tomcat.keytab HTTP/neo.example.org@EXAMPLE.ORG\n\n$ klist\nTicket cache: FILE:/tmp/krb5cc_1000\nDefault principal: HTTP/neo.example.org@EXAMPLE.ORG\n\nValid starting     Expires            Service principal\n26/03/15 09:04:37  26/03/15 19:04:37  krbtgt/EXAMPLE.ORG@EXAMPLE.ORG\n  renew until 27/03/15 09:04:37\n----\n\nLets see what happens if we now try to do a simple query against\nWindows AD.\n\n[source,text]\n----\n$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b \"dc=example,dc=org\"\nSASL/GSSAPI authentication started\nldap_sasl_interactive_bind_s: Local error (-2)\n  additional info: SASL(-1): generic failure: GSSAPI Error:\n  Unspecified GSS failure.  Minor code may provide more information\n  (KDC returned error string: PROCESS_TGS)\n----\n\nThis may be simply because `ldapsearch` is getting confused and simply\nusing wrong configuration. You can tell `ldapsearch` to use a\ndifferent configuration via `KRB5_CONFIG` env variable just like we\ndid with `kinit`. You can also use `KRB5_TRACE=/dev/stderr` to get\nmore verbose output of what native libraries are doing.\n\n[source,text]\n----\n$ env KRB5_CONFIG=/path/to/krb5.ini ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b \"dc=example,dc=org\"\n\n$ klist\nTicket cache: FILE:/tmp/krb5cc_1000\nDefault principal: HTTP/neo.example.org@EXAMPLE.ORG\n\nValid starting     Expires            Service principal\n26/03/15 09:11:03  26/03/15 19:11:03  krbtgt/EXAMPLE.ORG@EXAMPLE.ORG\n  renew until 27/03/15 09:11:03\n  26/03/15 09:11:44  26/03/15 19:11:03\n  ldap/win-ekbo0eq7ts7.example.org@EXAMPLE.ORG\n    renew until 27/03/15 09:11:03\n----\n\nAbove you can see what happened if query was successful by looking\nkerberos tickets. Now you can experiment with further query commands\ni.e. if you are working with `KerberosLdapContextSource`.\n\n[source,text]\n----\n$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org \\\n-b \"dc=example,dc=org\" \\\n\"(| (userPrincipalName=user2@EXAMPLE.ORG)\n(sAMAccountName=user2@EXAMPLE.ORG))\" \\\ndn\n\n...\n# test user, example.org\ndn: CN=test user,DC=example,DC=org\n----\n\n[appendix]\n[[browserspnegoconfig]]\n== Configure Browsers for Spnego Negotiation\n\n=== Firefox\nComplete following steps to ensure that your Firefox browser is\nenabled to perform Spnego authentication.\n\n- Open Firefox.\n- At address field, type *about:config*.\n- In filter/search, type *negotiate*.\n- Parameter *network.negotiate-auth.trusted-uris* may be set to\n  default *https://* which doesn't work for you. Generally speaking\n  this parameter has to replaced with the server address if Kerberos\n  delegation is required.\n- It is recommended to use `https` for all communication.\n\n=== Chrome\n\nWith Google Chrome you generally need to set command-line parameters\norder to white list servers with Chrome will negotiate.\n\n- on Windows machines (clients): Chrome shares the configuration with\n  Internet Explorer so if all changes were applied to IE (as described\n  in E.3), nothing has to be passed via command-line parameters.\n- on Linux/Mac OS machines (clients): the command-line parameter\n  `--auth-negotiate-delegate-whitelist` should only be used if Kerberos\n  delegation is required (otherwise do not set this parameter).\n- It is recommended to use `https` for all communication.\n\n[source,text]\n----\n--auth-server-whitelist=\"*.example.com\"\n--auth-negotiate-delegate-whitelist=\"*.example.com\"\n----\n\nYou can see which policies are enable by typing *chrome://policy/*\ninto Chrome's address bar.\n\nWith Linux Chrome will also read policy files from\n`/etc/opt/chrome/policies/managed` directory.\n\n.mypolicy.json\n[source,json]\n----\n{\n  \"AuthServerWhitelist\" : \"*.example.org\",\n  \"AuthNegotiateDelegateWhitelist\" : \"*.example.org\",\n  \"DisableAuthNegotiateCnameLookup\" : true,\n  \"EnableAuthNegotiatePort\" : true\n}\n----\n\n=== Internet Explorer\nComplete following steps to ensure that your Internet Explorer browser\nis enabled to perform Spnego authentication.\n\n- Open Internet Explorer.\n- Click *Tools > Intenet Options > Security* tab.\n- In *Local intranet* section make sure your server is trusted by i.e.\n  adding it into a list.\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/kerberos/index.adoc",
    "content": "= Spring Security Kerberos\n\nSpring Security Kerberos adds the ability to work with Kerberos and Spring applications.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/kerberos/introduction.adoc",
    "content": "[[introduction]]\n= Introduction\n\nSpring Security Kerberos {spring-security-version} is built and tested with JDK 17,\nSpring Security {spring-security-version} and Spring Framework {spring-core-version}.\n\nThe dependency coordinates changed with Spring Security 7:\n\n[tabs]\n======\nMaven::\n+\n.pom.xml\n[source,xml,subs=\"verbatim,attributes\"]\n----\n<dependencies>\n\t<!-- ... other dependency elements ... -->\n\t<dependency>\n\t\t<groupId>org.springframework.security</groupId>\n\t\t<artifactId>spring-security-kerberos-core</artifactId>\n\t</dependency>\n\t<dependency>\n\t\t<groupId>org.springframework.security</groupId>\n\t\t<artifactId>spring-security-kerberos-web</artifactId>\n\t</dependency>\n</dependencies>\n----\n\nGradle::\n+\n.build.gradle\n[source,groovy]\n[subs=\"verbatim,attributes\"]\n----\ndependencies {\n\timplementation \"org.springframework.security:spring-security-kerberos-core\"\n\timplementation \"org.springframework.security:spring-security-kerberos-web\"\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/kerberos/samples.adoc",
    "content": "[[springsecuritykerberossamples]]\n= Spring Security Kerberos Samples\n:figures: servlet/authentication/kerberos\n\nThis part of the reference documentation is introducing samples\nprojects. Samples can be compiled manually by building main\ndistribution from\nhttps://github.com/spring-projects/spring-security-kerberos.\n\n[IMPORTANT]\n====\nIf you run sample as is it will not work until a correct configuration\nis applied. See notes below for specific samples.\n====\n\n<<samples-sec-server-win-auth>> sample for Windows environment\n\n<<samples-sec-server-client-auth>> sample using server side authenticator\n\n<<samples-sec-server-spnego-form-auth>> sample using ticket validation\nwith spnego and form\n\n<<samples-sec-client-rest-template>> sample for KerberosRestTemplate\n\n[[samples-sec-server-win-auth]]\n== Security Server Windows Auth Sample\nGoals of this sample:\n\n- In windows environment, User will be able to logon to application\n  with Windows Active directory Credential which has been entered\n  during log on to windows. There should not be any ask for\n  userid/password credentials.\n- In non-windows environment, User will be presented with a screen\n  to provide Active directory credentials.\n\n[source,yaml,indent=0]\n----\nserver:\n    port: 8080\n    app:\n        ad-domain: EXAMPLE.ORG\n        ad-server: ldap://WIN-EKBO0EQ7TS7.example.org/\n        service-principal: HTTP/neo.example.org@EXAMPLE.ORG\n        keytab-location: /tmp/tomcat.keytab\n        ldap-search-base: dc=example,dc=org\n        ldap-search-filter: \"(| (userPrincipalName={0}) (sAMAccountName={0}))\"\n----\nIn above you can see the default configuration for this sample. You\ncan override these settings using a normal Spring Boot tricks like\nusing command-line options or custom `application.yml` file.\n\nRun a server.\n[source,text,subs=\"attributes\"]\n----\n$ java -jar sec-server-win-auth-{spring-security-version}.jar\n----\n\n[IMPORTANT]\n====\nYou may need to use custom kerberos config with Linux either by using\n`-Djava.security.krb5.conf=/path/to/krb5.ini` or\n`GlobalSunJaasKerberosConfig` bean.\n====\n\n[NOTE]\n====\nSee xref:servlet/authentication/kerberos/appendix.adoc#setupwinkerberos[Setup Windows Domain Controller]\nfor more instructions how to work with windows kerberos environment.\n====\n\nLogin to `Windows 8.1` using domain credentials and access sample\n\nimage::{figures}/ie1.png[]\nimage::{figures}/ie2.png[]\n\nAccess sample application from a non windows vm and use domain\ncredentials manually.\n\nimage::{figures}/ff1.png[]\nimage::{figures}/ff2.png[]\nimage::{figures}/ff3.png[]\n\n\n[[samples-sec-server-client-auth]]\n== Security Server Side Auth Sample\nThis sample demonstrates how server is able to authenticate user\nagainst kerberos environment using his credentials passed in via a\nform login.\n\nRun a server.\n[source,text,subs=\"attributes\"]\n----\n$ java -jar sec-server-client-auth-{spring-security-version}.jar\n----\n\n[source,yaml,indent=0]\n----\nserver:\n    port: 8080\n----\n\n[[samples-sec-server-spnego-form-auth]]\n== Security Server Spnego and Form Auth Sample\nThis sample demonstrates how a server can be configured to accept a\nSpnego based negotiation from a browser while still being able to fall\nback to a form based authentication.\n\nUsing a `user1` principal xref:servlet/authentication/kerberos/appendix.adoc#setupmitkerberos[Setup MIT Kerberos],\ndo a kerberos login manually using credentials.\n[source,text]\n----\n$ kinit user1\nPassword for user1@EXAMPLE.ORG:\n\n$ klist\nTicket cache: FILE:/tmp/krb5cc_1000\nDefault principal: user1@EXAMPLE.ORG\n\nValid starting     Expires            Service principal\n10/03/15 17:18:45  11/03/15 03:18:45  krbtgt/EXAMPLE.ORG@EXAMPLE.ORG\n  renew until 11/03/15 17:18:40\n----\n\nor using a keytab file.\n\n[source,text]\n----\n$ kinit -kt user2.keytab user1\n\n$ klist\nTicket cache: FILE:/tmp/krb5cc_1000\nDefault principal: user2@EXAMPLE.ORG\n\nValid starting     Expires            Service principal\n10/03/15 17:25:03  11/03/15 03:25:03  krbtgt/EXAMPLE.ORG@EXAMPLE.ORG\n  renew until 11/03/15 17:25:03\n----\n\nRun a server.\n[source,text,subs=\"attributes\"]\n----\n$ java -jar sec-server-spnego-form-auth-{spring-security-version}.jar\n----\n\nNow you should be able to open your browser and let it do Spnego\nauthentication with existing ticket.\n\n[NOTE]\n====\nSee xref:servlet/authentication/kerberos/appendix.adoc#browserspnegoconfig[Configure Browsers for Spnego Negotiation]\nfor more instructions for configuring browsers to use Spnego.\n====\n\n[source,yaml,indent=0]\n----\nserver:\n    port: 8080\napp:\n    service-principal: HTTP/neo.example.org@EXAMPLE.ORG\n    keytab-location: /tmp/tomcat.keytab\n----\n\n[[samples-sec-client-rest-template]]\n== Security Client KerberosRestTemplate Sample\nThis is a sample using a Spring RestTemplate to access Kerberos\nprotected resource. You can use this together with\n<<samples-sec-server-spnego-form-auth>>.\n\nDefault application is configured as shown below.\n[source,yaml,indent=0]\n----\napp:\n    user-principal: user2@EXAMPLE.ORG\n    keytab-location: /tmp/user2.keytab\n    access-url: https://neo.example.org:8080/hello\n----\n\n\nUsing a `user1` principal xref:servlet/authentication/kerberos/appendix.adoc#setupmitkerberos[Setup MIT Kerberos],\ndo a kerberos login manually using credentials.\n[source,text,subs=\"attributes\"]\n----\n$ java -jar sec-client-rest-template-{spring-security-version}.jar --app.user-principal --app.keytab-location\n----\n\n[NOTE]\n====\nIn above we simply set `app.user-principal` and `app.keytab-location`\nto empty values which disables a use of keytab file.\n====\n\nIf operation is successful you should see below output with `user1@EXAMPLE.ORG`.\n[source,text]\n----\n<html xmlns=\"https://www.w3.org/1999/xhtml\"\n      xmlns:sec=\"https://www.thymeleaf.org/thymeleaf-extras-springsecurity3\">\n  <head>\n    <title>Spring Security Kerberos Example</title>\n  </head>\n  <body>\n    <h1>Hello user1@EXAMPLE.ORG!</h1>\n  </body>\n</html>\n----\n\nOr use a `user2` with a keytab file.\n[source,text,subs=\"attributes\"]\n----\n$ java -jar sec-client-rest-template-{spring-security-version}.jar\n----\n\nIf operation is succesful you should see below output with `user2@EXAMPLE.ORG`.\n[source,text]\n----\n<html xmlns=\"https://www.w3.org/1999/xhtml\"\n      xmlns:sec=\"https://www.thymeleaf.org/thymeleaf-extras-springsecurity3\">\n  <head>\n    <title>Spring Security Kerberos Example</title>\n  </head>\n  <body>\n    <h1>Hello user2@EXAMPLE.ORG!</h1>\n  </body>\n</html>\n----\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/kerberos/ssk.adoc",
    "content": "[[springsecuritykerberos]]\n= Spring and Spring Security Kerberos\n:figures: servlet/authentication/kerberos\n\nThis part of the reference documentation explains the core functionality\nthat Spring Security Kerberos provides to any Spring based application.\n\n<<ssk-authprovider>> describes the authentication provider support.\n\n<<ssk-spnego>> describes the spnego negotiate support.\n\n<<ssk-resttemplate>> describes the RestTemplate support.\n\n\n[[ssk-authprovider]]\n== Authentication Provider\n\nProvider configuration using JavaConfig.\n\n[source,java,indent=0]\n----\ninclude::example$kerberos/AuthProviderConfig.java[tags=snippetA]\n----\n\n[[ssk-spnego]]\n== Spnego Negotiate\n\nSpnego configuration using JavaConfig.\n\n[source,java,indent=0]\n----\ninclude::example$kerberos/SpnegoConfig.java[tags=snippetA]\n----\n\n[[ssk-resttemplate]]\n== Using KerberosRestTemplate\n\nIf there is a need to access Kerberos protected web resources\nprogrammatically we have `KerberosRestTemplate` which extends\n`RestTemplate` and does necessary login actions prior to delegating to\nactual RestTemplate methods. You basically have few options to\nconfigure this template.\n\n- Leave keyTabLocation and userPrincipal empty if you want to\n  use cached ticket.\n- Use keyTabLocation and userPrincipal if you want to use\n  keytab file.\n- Use loginOptions if you want to customise Krb5LoginModule options.\n- Use a customised httpClient.\n\nWith ticket cache.\n[source,java,indent=0]\n----\ninclude::example$kerberos/KerberosRestTemplateConfig.java[tags=snippetA]\n----\n\nWith keytab file.\n[source,java,indent=0]\n----\ninclude::example$kerberos/KerberosRestTemplateConfig.java[tags=snippetB]\n----\n\n[[ssk-kerberosldap]]\n== Authentication with LDAP Services\n\nWith most of your samples we're using `DummyUserDetailsService`\nbecause there is not necessarily need to query a real user details\nonce kerberos authentication is successful and we can use kerberos\nprincipal info to create that dummy user. However there is a way to\naccess kerberized LDAP services in a say way and query user details\nfrom there.\n\n`KerberosLdapContextSource` can be used to bind into LDAP via kerberos\nwhich is at least proven to work well with Windows AD services.\n\n[source,java,indent=0]\n----\ninclude::example$kerberos/KerberosLdapContextSourceConfig.java[tags=snippetA]\n----\n\n[TIP]\n====\nSample xref:servlet/authentication/kerberos/samples.adoc#samples-sec-server-win-auth[Security Server Windows Auth Sample]\nis currently configured to query user details from AD if authentication happen via kerberos.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/logout.adoc",
    "content": "[[jc-logout]]\n= Handling Logouts\n\nIn an application where end users can xref:servlet/authentication/index.adoc[login], they should also be able to logout.\n\nBy default, Spring Security stands up a `/logout` endpoint, so no additional code is necessary.\n\nThe rest of this section covers a number of use cases for you to consider:\n\n* I want to <<logout-java-configuration,understand logout's architecture>>\n* I want to <<customizing-logout-uris, customize the logout or logout success URI>>\n* I want to know when I need to <<permit-logout-endpoints, explicitly permit the `/logout` endpoint>>\n* I want to <<clear-all-site-data, clear cookies, storage, and/or cache>> when the user logs out\n* I am using OAuth 2.0 and I want to xref:servlet/oauth2/login/advanced.adoc#oauth2login-advanced-oidc-logout[coordinate logout with an Authorization Server]\n* I am using SAML 2.0 and I want to xref:servlet/saml2/logout.adoc[coordinate logout with an Identity Provider]\n* I am using CAS and I want to xref:servlet/authentication/cas.adoc#cas-singlelogout[coordinate logout with an Identity Provider]\n\n[[logout-architecture]]\n[[logout-java-configuration]]\n== Understanding Logout's Architecture\n\nWhen you include {spring-boot-reference-url}reference/using/build-systems.html#using.build-systems.starters[the `spring-boot-starter-security` dependency] or use the `@EnableWebSecurity` annotation, Spring Security will add its logout support and by default respond both to `GET /logout` and `POST /logout`.\n\nIf you request `GET /logout`, then Spring Security displays a logout confirmation page.\nAside from providing a valuable double-checking mechanism for the user, it also provides a simple way to provide xref:servlet/exploits/csrf.adoc[the needed CSRF token] to `POST /logout`.\n\nPlease note that if xref:servlet/exploits/csrf.adoc[CSRF protection] is disabled in configuration, no logout confirmation page is shown to the user and the logout is performed directly.\n\n[TIP]\nIn your application it is not necessary to use `GET /logout` to perform a logout.\nSo long as xref:servlet/exploits/csrf.adoc[the needed CSRF token] is present in the request, your application can simply `POST /logout` to induce a logout.\n\nIf you request `POST /logout`, then it will perform the following default operations using a series of javadoc:org.springframework.security.web.authentication.logout.LogoutHandler[] instances:\n\n- Invalidate the HTTP session (javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[])\n- Clear the xref:servlet/authentication/session-management.adoc#use-securitycontextholderstrategy[`SecurityContextHolderStrategy`] (javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[])\n- Clear the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] (javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[])\n- Clean up any xref:servlet/authentication/rememberme.adoc[RememberMe authentication] (`TokenRememberMeServices` / `PersistentTokenRememberMeServices`)\n- Clear out any saved xref:servlet/exploits/csrf.adoc[CSRF token] (javadoc:org.springframework.security.web.csrf.CsrfLogoutHandler[])\n- xref:servlet/authentication/events.adoc[Fire] a `LogoutSuccessEvent` (javadoc:org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler[])\n\nOnce completed, then it will exercise its default javadoc:org.springframework.security.web.authentication.logout.LogoutSuccessHandler[] which redirects to `/login?logout`.\n\n[[customizing-logout-uris]]\n== Customizing Logout URIs\n\nSince the `LogoutFilter` appears before xref:servlet/authorization/authorize-http-requests.adoc[the `AuthorizationFilter`] in xref:servlet/architecture.adoc#servlet-filterchain-figure[the filter chain], it is not necessary by default to explicitly permit the `/logout` endpoint.\nThus, only <<permit-logout-endpoints,custom logout endpoints>> that you create yourself generally require a `permitAll` configuration to be reachable.\n\nFor example, if you want to simply change the URI that Spring Security is matching, you can do so in the `logout` DSL in following way:\n\n.Custom Logout Uri\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .logout((logout) -> logout.logoutUrl(\"/my/logout/uri\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    logout {\n        logoutUrl = \"/my/logout/uri\"\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<logout logout-url=\"/my/logout/uri\"/>\n----\n======\n\nand no authorization changes are necessary since it simply adjusts the `LogoutFilter`.\n\n[[permit-logout-endpoints]]\nHowever, if you stand up your own logout success endpoint (or in a rare case, <<creating-custom-logout-endpoint, your own logout endpoint>>), say using {spring-framework-reference-url}web.html#spring-web[Spring MVC], you will need to permit it in Spring Security.\nThis is because Spring MVC processes your request after Spring Security does.\n\nYou can do this using `authorizeHttpRequests` or `<intercept-url>` like so:\n\n.Custom Logout Endpoint\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .requestMatchers(\"/my/success/endpoint\").permitAll()\n        // ...\n    )\n    .logout((logout) -> logout.logoutSuccessUrl(\"/my/success/endpoint\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(\"/my/success/endpoint\", permitAll)\n    }\n    logout {\n        logoutSuccessUrl = \"/my/success/endpoint\"\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <filter-url pattern=\"/my/success/endpoint\" access=\"permitAll\"/>\n    <logout logout-success-url=\"/my/success/endpoint\"/>\n</http>\n----\n======\n\nIn this example, you tell the `LogoutFilter` to redirect to `/my/success/endpoint` when it is done.\nAnd, you explicitly permit the `/my/success/endpoint` endpoint in xref:servlet/authorization/authorize-http-requests.adoc[the `AuthorizationFilter`].\n\nSpecifying it twice can be cumbersome, though.\nIf you are using Java configuration, you can instead set the `permitAll` property in the logout DSL like so:\n\n.Permitting Custom Logout Endpoints\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        // ...\n    )\n    .logout((logout) -> logout\n        .logoutSuccessUrl(\"/my/success/endpoint\")\n        .permitAll()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp\n    authorizeHttpRequests {\n        // ...\n    }\n    logout {\n        logoutSuccessUrl = \"/my/success/endpoint\"\n        permitAll = true\n    }\n----\n======\n\nwhich will add all logout URIs to the permit list for you.\n\n[[add-logout-handler]]\n== Adding Clean-up Actions\n\nIf you are using Java configuration, you can add clean up actions of your own by calling the `addLogoutHandler` method in the `logout` DSL, like so:\n\n.Custom Logout Handler\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nCookieClearingLogoutHandler cookies = new CookieClearingLogoutHandler(\"our-custom-cookie\");\nhttp\n    .logout((logout) -> logout.addLogoutHandler(cookies))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    logout {\n        addLogoutHandler(CookieClearingLogoutHandler(\"our-custom-cookie\"))\n    }\n}\n----\n======\n\n[NOTE]\nBecause javadoc:org.springframework.security.web.authentication.logout.LogoutHandler[] instances are for the purposes of cleanup, they should not throw exceptions.\n\n[TIP]\nSince javadoc:org.springframework.security.web.authentication.logout.LogoutHandler[] is a functional interface, you can provide a custom one as a lambda.\n\nSome logout handler configurations are common enough that they are exposed directly in the `logout` DSL and `<logout>` element.\nOne example is configuring session invalidation and another is which additional cookies should be deleted.\n\nFor example, you can configure the javadoc:org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler[] as seen above.\n\n[[delete-cookies]]\nOr you can instead set the appropriate configuration value like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .logout((logout) -> logout.deleteCookies(\"our-custom-cookie\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    logout {\n        deleteCookies(\"our-custom-cookie\")\n    }\n}\n----\n\nXml::\n+\n[source,kotlin,role=\"secondary\"]\n----\n<http>\n    <logout delete-cookies=\"our-custom-cookie\"/>\n</http>\n----\n======\n\n[NOTE]\nSpecifying that the `JSESSIONID` cookie is not necessary since javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[] removes it by virtue of invalidating the session.\n\n[[clear-all-site-data]]\n=== Using Clear-Site-Data to Log Out the User\n\nThe `Clear-Site-Data` HTTP header is one that browsers support as an instruction to clear cookies, storage, and cache that belong to the owning website.\nThis is a handy and secure way to ensure that everything, including the session cookie, is cleaned up on logout.\n\nYou can add configure Spring Security to write the `Clear-Site-Data` header on logout like so:\n\n.Using Clear-Site-Data\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nHeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(Directive.ALL));\nhttp\n    .logout((logout) -> logout.addLogoutHandler(clearSiteData))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(Directive.ALL))\nhttp {\n    logout {\n        addLogoutHandler(clearSiteData)\n    }\n}\n----\n======\n\nYou give the `ClearSiteDataHeaderWriter` constructor the list of things that you want to be cleared out.\n\nThe above configuration clears out all site data, but you can also configure it to remove just cookies like so:\n\n.Using Clear-Site-Data to Clear Cookies\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nHeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(Directive.COOKIES));\nhttp\n    .logout((logout) -> logout.addLogoutHandler(clearSiteData))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(Directive.COOKIES))\nhttp {\n    logout {\n        addLogoutHandler(clearSiteData)\n    }\n}\n----\n======\n\n[[customizing-logout-success]]\n== Customizing Logout Success\n\nWhile using `logoutSuccessUrl` will suffice for most cases, you may need to do something different from redirecting to a URL once logout is complete.\njavadoc:org.springframework.security.web.authentication.logout.LogoutSuccessHandler[] is the Spring Security component for customizing logout success actions.\n\nFor example, instead of redirecting, you may want to only return a status code.\nIn this case, you can provide a success handler instance, like so:\n\n.Customizing Logout Success to Return HTTP Status Code\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .logout((logout) -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    logout {\n        logoutSuccessHandler = HttpStatusReturningLogoutSuccessHandler()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<bean name=\"mySuccessHandlerBean\" class=\"org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler\"/>\n<http>\n    <logout success-handler-ref=\"mySuccessHandlerBean\"/>\n</http>\n----\n======\n\n[TIP]\nSince javadoc:org.springframework.security.web.authentication.logout.LogoutSuccessHandler[] is a functional interface, you can provide a custom one as a lambda.\n\n[[creating-custom-logout-endpoint]]\n== Creating a Custom Logout Endpoint\n\nIt is strongly recommended that you use the provided `logout` DSL to configure logout.\nOne reason is that its easy to forget to call the needed Spring Security components to ensure a proper and complete logout.\n\nIn fact, it is often simpler to <<add-logout-handler, register a custom `LogoutHandler`>> than create a {spring-framework-reference-url}web.html#spring-web[Spring MVC] endpoint for performing logout.\n\nThat said, if you find yourself in a circumstance where a custom logout endpoint is needed, like the following one:\n\n.Custom Logout Endpoint\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PostMapping(\"/my/logout\")\npublic String performLogout() {\n    // .. perform logout\n    return \"redirect:/home\";\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PostMapping(\"/my/logout\")\nfun performLogout(): String {\n    // .. perform logout\n    return \"redirect:/home\"\n}\n----\n======\n\nthen you will need to have that endpoint invoke Spring Security's javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[] to ensure a secure and complete logout.\nSomething like the following is needed at a minimum:\n\n.Custom Logout Endpoint\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nSecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();\n\n@PostMapping(\"/my/logout\")\npublic String performLogout(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {\n    // .. perform logout\n    this.logoutHandler.logout(request, response, authentication);\n    return \"redirect:/home\";\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval logoutHandler = SecurityContextLogoutHandler()\n\n@PostMapping(\"/my/logout\")\nfun performLogout(val authentication: Authentication, val request: HttpServletRequest, val response: HttpServletResponse): String {\n    // .. perform logout\n    this.logoutHandler.logout(request, response, authentication)\n    return \"redirect:/home\"\n}\n----\n======\n\nSuch will clear out the javadoc:org.springframework.security.core.context.SecurityContextHolderStrategy[] and javadoc:org.springframework.security.web.context.SecurityContextRepository[] as needed.\n\nAlso, you'll need to <<permit-logout-endpoints, explicitly permit the endpoint>>.\n\n[WARNING]\nFailing to call javadoc:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler[] means that xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[the `SecurityContext`] could still be available on subsequent requests, meaning that the user is not actually logged out.\n\n[[testing-logout]]\n== Testing Logout\nOnce you have logout configured you can test it using xref:servlet/test/mockmvc/logout.adoc[Spring Security's MockMvc support].\n\n[[jc-logout-references]]\n== Further Logout-Related References\n\n- xref:servlet/test/mockmvc/logout.adoc#test-logout[Testing Logout]\n- xref:servlet/integrations/servlet-api.adoc#servletapi-logout[HttpServletRequest.logout()]\n- xref:servlet/authentication/rememberme.adoc#remember-me-impls[Remember-Me Interfaces and Implementations]\n- xref:servlet/exploits/csrf.adoc#csrf-considerations-logout[Logging Out] in section CSRF Caveats\n- Section xref:servlet/authentication/cas.adoc#cas-singlelogout[Single Logout] (CAS protocol)\n- Documentation for the xref:servlet/appendix/namespace/http.adoc#nsa-logout[logout element] in the Spring Security XML Namespace section\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/mfa.adoc",
    "content": "= Multi-Factor Authentication\n\nhttps://cheatsheetseries.owasp.org/cheatsheets/Multifactor_Authentication_Cheat_Sheet.html[Multi-Factor Authentication (MFA)] requires that a user provide factors in order to authenticate.\nOWASP places factors into the following categories:\n\n- Something the user knows (e.g. a password)\n- Something that the user has (e.g. access to SMS or email)\n- Something you are (e.g. biometrics)\n- Somewhere you are (e.g. geolocation)\n- Something you do (e.g. Behavior Profiling)\n\n== `FactorGrantedAuthority`\n\nAt the time of authentication, Spring Security's authentication mechanisms add a javadoc:org.springframework.security.core.authority.FactorGrantedAuthority[].\nFor example, when a user authenticates using a password a `FactorGrantedAuthority` with the `authority` of `FactorGrantedAuthority.PASSWORD_AUTHORITY` is automatically added to the `Authentication`.\nIn order to require MFA with Spring Security you must:\n\n- Specify an authorization rule that requires multiple factors\n- Setup authentication for each of those factors\n\n[[emfa]]\n== @EnableMultiFactorAuthentication\n\njavadoc:org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication[format=annotation] makes it easy to enable multifactor authentication.\nBelow you can find a configuration that adds the requirement for both passwords and OTT to every authorization rule.\n\ninclude-code::./EnableMultiFactorAuthenticationConfiguration[tag=enable-mfa,indent=0]\n\nWe are now able to concisely create a configuration that always requires multiple factors.\n\ninclude-code::./EnableMultiFactorAuthenticationConfiguration[tag=httpSecurity,indent=0]\n<1> URLs that begin with `/admin/**` require the authorities `FACTOR_OTT`, `FACTOR_PASSWORD`, `ROLE_ADMIN`.\n<2> Every other URL requires the authorities `FACTOR_OTT`, `FACTOR_PASSWORD`\n<3> Set up the authentication mechanisms that can provide the required factors.\n\nSpring Security behind the scenes knows which endpoint to go to depending on which authority is missing.\nIf the user logged in initially with their username and password, then Spring Security redirects to the One-Time-Token Login page.\nIf the user logged in initially with a token, then Spring Security redirects to the Username/Password Login page.\n\n[[mfa-when-webauthn-registered]]\n=== Conditionally Requiring MFA for WebAuthn Users\n\nAt times, you may want to conditionally require MFA only for users who have registered a WebAuthn credential (passkey).\nYou can achieve this by specifying javadoc:org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication#when()[when = MultiFactorCondition.WEBAUTHN_REGISTERED].\n\ninclude-code::./WebAuthnConditionConfiguration[tag=enable-mfa-webauthn,indent=0]\n\nThis configuration requires `FACTOR_WEBAUTHN` and `FACTOR_PASSWORD` only when the user has registered a passkey.\nIt works by publishing a xref:./mfa.adoc#mfa-when-custom-conditions[`Customizer<AdditionalRequiredFactorsBuilder<Object>>`] that updates the xref:./mfa.adoc#programmatic-mfa[`withWhen`] method with the condition.\n\nNOTE: This condition requires both a javadoc:org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository[] bean and a javadoc:org.springframework.security.web.webauthn.management.UserCredentialRepository[] bean to be published in order to determine if the user has registered a WebAuthn credential.\n\n[[mfa-when-custom-conditions]]\n=== Custom MFA Conditions\n\nYou can also publish one or more `Customizer<AdditionalRequiredFactorsBuilder<Object>>` beans to customize the factory created by `@EnableMultiFactorAuthentication`.\nFor example, you can conditionally apply MFA for specific users:\n\ninclude-code::./CustomizerAuthorizationManagerFactoryConfiguration[tag=customizer,indent=0]\n\n[[authorization-manager-factory]]\n== AuthorizationManagerFactory\n\nThe `@EnableMultiFactorAuthentication` `authorities` property is just a shortcut for publishing an javadoc:org.springframework.security.authorization.AuthorizationManagerFactory[] Bean.\nWhen an `AuthorizationManagerFactory` Bean is available, it is used by Spring Security to create authorization rules, like `hasAnyRole(String)`, that are defined on the `AuthorizationManagerFactory` Bean interface.\nThe implementation published by `@EnableMultiFactorAuthentication` will ensure that each authorization is combined with the requirement of having the specified factors.\n\nThe `AuthorizationManagerFactory` Bean below is what is published in the previously discussed xref:./mfa.adoc#emfa[`@EnableMultiFactorAuthentication` example].\n\ninclude-code::./UseAuthorizationManagerFactoryConfiguration[tag=authorizationManagerFactoryBean,indent=0]\n\n\n[[selective-mfa]]\n== Selectively Requiring MFA\n\nWe have demonstrated how to configure an entire application to require MFA by using xref:./mfa.adoc#emfa[``@EnableMultiFactorAuthentication``s] `authorities` property.\nHowever, there are times that an application only wants parts of the application to require MFA.\nConsider the following requirements:\n\n- URLs that begin with `/admin/` should require the authorities `FACTOR_OTT`, `FACTOR_PASSWORD`, `ROLE_ADMIN`.\n- URLs that begin with `/user/settings` should require the authorities `FACTOR_OTT`, `FACTOR_PASSWORD`\n- Every other URL requires an authenticated user\n\nIn this case, some URLs require MFA while others do not.\nThis means that the global approach that we saw before does not work.\nFortunately, we can use what we learned in xref:./mfa.adoc#authorization-manager-factory[] to solve this in a concise manner.\n\nStart by specifying `@EnableMultiFactorAuthentication` without any authorities.\nBy doing so we enable MFA support, but no `AuthorizationManagerFactory` Bean is published.\n\ninclude-code::./SelectiveMfaConfiguration[tag=enable-mfa,indent=0]\n\nNext create an `AuthorizationManagerFactory` instance, but do not publish it as a Bean.\n\ninclude-code::./SelectiveMfaConfiguration[tag=httpSecurity,indent=0]\n<1> Create a `DefaultAuthorizationManagerFactory` as we did previously, but do not publish it as a Bean.\nBy not publishing it as a Bean, we are able to selectively use the `AuthorizationManagerFactory` instead of using it for every authorization rule.\n<2> Explicitly use `AuthorizationManagerFactory` so that URLs that begin with `/admin/**` require `FACTOR_OTT`, `FACTOR_PASSWORD`, and `ROLE_ADMIN`.\n<3> Explicitly use `AuthorizationManagerFactory` so that URLs that begin with `/user/settings` require `FACTOR_OTT` and `FACTOR_PASSWORD`\n<4> Otherwise, the request must be authenticated.\nThere is no MFA requirement, because the `AuthorizationManagerFactory` is not used.\n<5> Set up the authentication mechanisms that can provide the required factors.\n\n[[valid-duration]]\n== Specifying a Valid Duration\n\nAt times, we may want to define authorization rules based upon how recently we authenticated.\nFor example, an application may want to require that the user has authenticated within the last hour in order to allow access to the `/user/settings` endpoint.\n\nRemember at the time of authentication, a `FactorGrantedAuthority` is added to the `Authentication`.\nThe `FactorGrantedAuthority` specifies when it was `issuedAt`, but does not describe how long it is valid for.\nThis is intentional, because it allows a single `FactorGrantedAuthority` to be used with different ``validDuration``s.\n\nLet's take a look at an example that illustrates how to meet the following requirements:\n\n- URLs that begin with `/admin/` should require that a password has been provided within the last 30 minutes\n- URLs that being with `/user/settings` should require that a password has been provided within the last hour\n- Otherwise, authentication is required, but it does not care if it is a password or how long ago authentication occurred\n\ninclude-code::./ValidDurationConfiguration[tag=httpSecurity,indent=0]\n<1> First we define `passwordIn30m` as a requirement for a password within 30 minutes\n<2> Next, we define `passwordInHour` as a requirement for a password within an hour\n<3> We use `passwordIn30m` to require that URLs that begin with `/admin/` should require that a password has been provided in the last 30 minutes and that the user has the `ROLE_ADMIN` authority\n<4> We use `passwordInHour` to require that URLs that begin with `/user/settings` should require that a password has been provided in the last hour\n<5> Otherwise, authentication is required, but it does not care if it is a password or how long ago authentication occurred\n<6> Set up the authentication mechanisms that can provide the required factors.\n\n[[programmatic-mfa]]\n== Programmatic MFA\n\nIn our previous examples, MFA is a static decision per request.\nThere are times when we might want to require MFA for some users, but not others.\nDetermining if MFA is enabled per user can be achieved by using `AuthorizationManagerFactories.multiFactor().when` to conditionally require factors based upon the `Authentication`.\nThis is implemented using xref:servlet/authorization/architecture.adoc#authz-conditional-authorization-manager[`ConditionalAuthorizationManager`].\n\nTo enable the conditional MFA rules globally, we can publish an `AuthorizationManagerFactory` Bean.\n\ninclude-code::./AdminMfaAuthorizationManagerConfiguration[tag=authorizationManagerFactory,indent=0]\n<1> Require `FACTOR_OTT` and `FACTOR_PASSWORD`\n<2> Only apply the requirement if the username is `admin`. Otherwise, MFA is not required.\n<3> Return the `AuthorizationManagerFactory` using `.build()`. Since it is published as a Bean, it is used globally.\n\nThis should feel very similar to our previous example in xref:./mfa.adoc#authorization-manager-factory[].\nThe difference is that in the previous example, the `AuthorizationManagerFactories` creates an `AuthorizationManager` that always requires the same authorities.\n\nWe can now define our authorization rules which are combined with `AuthorizationManagerFactory`.\n\ninclude-code::./AdminMfaAuthorizationManagerConfiguration[tag=httpSecurity,indent=0]\n<1> URLs that begin with `/admin/**` require `ROLE_ADMIN`.\nIf the username is `admin`, then `FACTOR_OTT` and `FACTOR_PASSWORD` are also required.\n<2> Otherwise, the request must be authenticated.\nIf the username is `admin`, then `FACTOR_OTT` and `FACTOR_PASSWORD` are also required.\n\nNOTE: MFA is enabled by username and not role because that is how we implemented `RequiredAuthoritiesAuthorizationManagerConfiguration`.\nIf we preferred, we could change our logic to enable MFA based upon the roles rather than the username.\n\n[[raam-mfa]]\n== RequiredAuthoritiesAuthorizationManager\n\nWe've demonstrated how we can dynamically determine the authorities for a particular user in xref:./mfa.adoc#programmatic-mfa[] using `AuthorizationManagerFactories.multiFactor().when`.\nHowever, this is such a common scenario that Spring Security provides built in support using javadoc:org.springframework.security.authorization.RequiredAuthoritiesAuthorizationManager[] and javadoc:org.springframework.security.authorization.RequiredAuthoritiesRepository[].\n\nLet's implement the same requirement that we did in xref:./mfa.adoc#programmatic-mfa[] using the built-in support.\n\nWe start by creating the `RequiredAuthoritiesAuthorizationManager` Bean to use.\n\ninclude-code::./RequiredAuthoritiesAuthorizationManagerConfiguration[tag=authorizationManager,indent=0]\n<1> Create a javadoc:org.springframework.security.authorization.MapRequiredAuthoritiesRepository[] that maps users with the username `admin` to require MFA.\n<2> Return a `RequiredAuthoritiesAuthorizationManager` that is injected with the `MapRequiredAuthoritiesRepository`.\n\nNext we can define an `AuthorizationManagerFactory` that uses the `RequiredAuthoritiesAuthorizationManager`.\n\ninclude-code::./RequiredAuthoritiesAuthorizationManagerConfiguration[tag=authorizationManagerFactory,indent=0]\n<1> Inject the `RequiredAuthoritiesAuthorizationManager` as the javadoc:org.springframework.security.authorization.DefaultAuthorizationManagerFactory#setAdditionalAuthorization(org.springframework.security.authorization.AuthorizationManager)[DefaultAuthorization.additionalAuthorization].\nThis instructs `DefaultAuthorizationManagerFactory` that any authorization rule should apply `RequiredAuthoritiesAuthorizationManager` along with any authorization requirements defined by the application (e.g. `hasRole(\"ADMIN\")`).\n<2> Publish `DefaultAuthorizationManagerFactory` as a Bean, so it is used globally\n\nWe can now define our authorization rules which are combined with `RequiredAuthoritiesAuthorizationManager`.\n\ninclude-code::./RequiredAuthoritiesAuthorizationManagerConfiguration[tag=httpSecurity,indent=0]\n<1> URLs that begin with `/admin/**` require `ROLE_ADMIN`.\nIf the username is `admin`, then `FACTOR_OTT` and `FACTOR_PASSWORD` are also required.\n<2> Otherwise, the request must be authenticated.\nIf the username is `admin`, then `FACTOR_OTT` and `FACTOR_PASSWORD` are also required.\n\nOur example uses an in memory mapping of usernames to the additional required authorities.\nFor more dynamic use cases that can be determined by the username, a custom implementation of javadoc:org.springframework.security.authorization.RequiredAuthoritiesRepository[] can be created.\nPossible examples would be looking up if a user has enabled MFA in an explicit setting, determining if a user has registered a passkey, etc.\n\nFor cases that need to determine MFA based upon the `Authentication`, `AuthorizationManagerFactories.multiFactor().when` can be used as demonstrated in xref:./mfa.adoc#programmatic-mfa[].\n\n\n[[hasallauthorities]]\n== Using hasAllAuthorities\n\nWe've shown a lot of additional infrastructure for supporting MFA.\nHowever, for simple MFA use-cases, using `hasAllAuthorities` to require multiple factors is effective.\n\ninclude-code::./ListAuthoritiesConfiguration[tag=httpSecurity,indent=0]\n<1> Require `FACTOR_PASSWORD` and `FACTOR_OTT` for every request\n<2> Set up the authentication mechanisms that can provide the required factors.\n\nThe configuration above works well only for the most simple use-cases.\nIf you have lots of endpoints, you probably do not want to repeat the requirements for MFA in every authorization rule.\n\nFor example, consider the following configuration:\n\ninclude-code::./MultipleAuthorizationRulesConfiguration[tag=httpSecurity,indent=0]\n<1> For URLs that begin with `/admin/**`, the following authorities are required `FACTOR_OTT`, `FACTOR_PASSWORD`, `ROLE_ADMIN`.\n<2> For every other URL, the following authorities are required `FACTOR_OTT`, `FACTOR_PASSWORD`, `ROLE_USER`.\n<3> Set up the authentication mechanisms that can provide the required factors.\n\nThe configuration only specifies two authorization rules, but it is enough to see that the duplication is not desirable.\nCan you imagine what it would be like to declare hundreds of rules like this?\n\nWhat's more that it becomes difficult to express more complicated authorization rules.\nFor example, how would you require two factors and either `ROLE_ADMIN` or `ROLE_USER`?\n\nThe answer to these questions, as we have already seen, is to use xref:./mfa.adoc#emfa[]\n\n[[re-authentication]]\n== Re-authentication\n\nThe most common of these is re-authentication.\nImagine an application configured in the following way:\n\ninclude-code::./SimpleConfiguration[tag=httpSecurity,indent=0]\n\nBy default, this application has two authentication mechanisms that it allows, meaning that the user could use either one and be fully-authenticated.\n\nIf there is a set of endpoints that require a specific factor, we can specify that in `authorizeHttpRequests` as follows:\n\ninclude-code::./RequireOttConfiguration[tag=httpSecurity,indent=0]\n<1> States that all `/profile/**` endpoints require one-time-token login to be authorized\n\nGiven the above configuration, users can log in with any mechanism that you support.\nAnd, if they want to visit the profile page, then Spring Security will redirect them to the One-Time-Token Login page to obtain it.\n\nIn this way, the authority given to a user is directly proportional to the amount of proof given.\nThis adaptive approach allows users to give only the proof needed to perform their intended operations.\n\n\n[[obtaining-more-authorization]]\n== Authorizing More Scopes\n\nYou can also configure exception handling to direct Spring Security on how to obtain a missing scope.\n\nConsider an application that requires a specific OAuth 2.0 scope for a given endpoint:\n\ninclude-code::./ScopeConfiguration[tag=httpSecurity,indent=0]\n\nIf this is also configured with an `AuthorizationManagerFactory` bean like this one:\n\ninclude-code::./MissingAuthorityConfiguration[tag=authorizationManagerFactoryBean,indent=0]\n\nThen the application will require an X.509 certificate as well as authorization from an OAuth 2.0 authorization server.\n\nIn the event that the user does not consent to `profile:read`, this application as it stands will issue a 403.\nHowever, if you have a way for the application to re-ask for consent, then you can implement this in an `AuthenticationEntryPoint` like the following:\n\ninclude-code::./MissingAuthorityConfiguration[tag=authenticationEntryPoint,indent=0]\n\nThen, your filter chain declaration can bind this entry point to the given authority like so:\n\ninclude-code::./MissingAuthorityConfiguration[tag=httpSecurity,indent=0]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/onetimetoken.adoc",
    "content": "[[one-time-token-login]]\n= One-Time Token Login\n\nSpring Security offers support for One-Time Token (OTT) authentication via the `oneTimeTokenLogin()` DSL.\nBefore diving into implementation details, it's important to clarify the scope of the OTT feature within the framework, highlighting what is supported and what isn't.\n\n== Understanding One-Time Tokens vs. One-Time Passwords\n\nIt's common to confuse One-Time Tokens (OTT) with https://en.wikipedia.org/wiki/One-time_password[One-Time Passwords] (OTP), but in Spring Security, these concepts differ in several key ways.\nFor clarity, we'll assume OTP refers to https://en.wikipedia.org/wiki/Time-based_one-time_password[TOTP] (Time-Based One-Time Password) or https://en.wikipedia.org/wiki/HMAC-based_one-time_password[HOTP] (HMAC-Based One-Time Password).\n\n=== Setup Requirements\n\n- OTT: No initial setup is required. The user doesn't need to configure anything in advance.\n- OTP: Typically requires setup, such as generating and sharing a secret key with an external tool to produce the one-time passwords.\n\n=== Token Delivery\n\n- OTT: Usually a custom javadoc:org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler[] must be implemented, responsible for delivering the token to the end user.\n- OTP: The token is often generated by an external tool, so there's no need to send it to the user via the application.\n\n=== Token Generation\n\n- OTT: The javadoc:org.springframework.security.authentication.ott.OneTimeTokenService#generate(org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest)[] method requires a javadoc:org.springframework.security.authentication.ott.OneTimeToken[] to be returned, emphasizing server-side generation.\n- OTP: The token is not necessarily generated on the server side, it's often created by the client using the shared secret.\n\nIn summary, One-Time Tokens (OTT) provide a way to authenticate users without additional account setup, differentiating them from One-Time Passwords (OTP), which typically involve a more complex setup process and rely on external tools for token generation.\n\nThe One-Time Token Login works in two major steps.\n\n1. User requests a token by submitting their user identifier, usually the username, and the token is delivered to them, often as a Magic Link, via e-mail, SMS, etc.\n2. User submits the token to the one-time token login endpoint and, if valid, the user gets logged in.\n\nIn the following sections we will explore how to configure OTT Login for your needs.\n\n- <<default-pages,Understanding the integration with the default generated login page>>\n- <<sending-token-to-user,Sending the token to the user>>\n- <<changing-submit-page-url,Configuring the One-Time Token submit page>>\n- <<changing-generate-url,Changing the One-Time Token generate URL>>\n- <<customize-generate-consume-token,Customize how to generate and consume tokens>>\n\n[[default-pages]]\n== Default Login Page and Default One-Time Token Submit Page\n\nWhen the `oneTimeTokenLogin()` DSL is used, by default the One-Time Token Login Page is auto-generated by the org.springframework.security.web.authentication.ui:DefaultLoginPageGeneratingFilter[].\nThe DSL will also set up the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] to generate a default One-Time Token submit page.\n\n[[sending-token-to-user]]\n== Sending the Token to the User\n\nIt is not possible for Spring Security to reasonably determine the way the token should be delivered to your users.\nTherefore, a custom javadoc:org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler[] must be provided to deliver the token to the user based on your needs.\nOne of the most common delivery strategies is a Magic Link, via e-mail, SMS, etc.\nIn the following example, we are going to create a magic link and sent it to the user's email.\n\n.One-Time Token Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin(Customizer.withDefaults());\n        return http.build();\n    }\n\n}\n\nimport org.springframework.mail.SimpleMailMessage;\nimport org.springframework.mail.javamail.JavaMailSender;\n\n@Component <1>\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {\n\n    private final MailSender mailSender;\n\n    private final OneTimeTokenGenerationSuccessHandler redirectHandler = new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\n    // constructor omitted\n\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) throws IOException, ServletException {\n        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))\n                .replacePath(request.getContextPath())\n                .replaceQuery(null)\n                .fragment(null)\n                .path(\"/login/ott\")\n                .queryParam(\"token\", oneTimeToken.getTokenValue()); <2>\n        String magicLink = builder.toUriString();\n        String email = getUserEmail(oneTimeToken.getUsername()); <3>\n        this.mailSender.send(email, \"Your Spring Security One Time Token\", \"Use the following link to sign in into the application: \" + magicLink); <4>\n        this.redirectHandler.handle(request, response, oneTimeToken); <5>\n    }\n\n    private String getUserEmail() {\n        // ...\n    }\n\n}\n\n@Controller\nclass PageController {\n\n    @GetMapping(\"/ott/sent\")\n    String ottSent() {\n        return \"my-template\";\n    }\n\n}\n\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n        @Bean\n        open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n            http{\n                formLogin {}\n                oneTimeTokenLogin {  }\n            }\n            return http.build()\n        }\n}\n\nimport org.springframework.mail.SimpleMailMessage;\nimport org.springframework.mail.javamail.JavaMailSender;\n\n@Component (1)\nclass MagicLinkOneTimeTokenGenerationSuccessHandler(\n    private val mailSender: MailSender,\n    private val redirectHandler: OneTimeTokenGenerationSuccessHandler = RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n) : OneTimeTokenGenerationSuccessHandler {\n\n    override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {\n        val builder = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))\n            .replacePath(request.contextPath)\n            .replaceQuery(null)\n            .fragment(null)\n            .path(\"/login/ott\")\n            .queryParam(\"token\", oneTimeToken.getTokenValue()) (2)\n        val magicLink = builder.toUriString()\n        val email = getUserEmail(oneTimeToken.getUsername()) (3)\n        this.mailSender.send(email, \"Your Spring Security One Time Token\", \"Use the following link to sign in into the application: $magicLink\")(4)\n        this.redirectHandler.handle(request, response, oneTimeToken) (5)\n    }\n\n    private fun getUserEmail(): String {\n        // ...\n    }\n}\n\n@Controller\nclass PageController {\n\n    @GetMapping(\"/ott/sent\")\n    fun ottSent(): String {\n        return \"my-template\"\n    }\n}\n\n----\n======\n\n<1> Make the `MagicLinkOneTimeTokenGenerationSuccessHandler` a Spring bean\n<2> Create a login processing URL with the `token` as a query param\n<3> Retrieve the user's email based on the username\n<4> Use the `JavaMailSender` API to send the email to the user with the magic link\n<5> Use the `RedirectOneTimeTokenGenerationSuccessHandler` to perform a redirect to your desired URL\n\nThe email content will look similar to:\n\n> Use the following link to sign in into the application: \\http://localhost:8080/login/ott?token=a830c444-29d8-4d98-9b46-6aba7b22fe5b\n\nThe default submit page will detect that the URL has the `token` query param and will automatically fill the form field with the token value.\n\n[[changing-generate-url]]\n== Changing the One-Time Token Generate URL\n\nBy default, the javadoc:org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter[] listens to `POST /ott/generate` requests.\nThat URL can be changed by using the `generateTokenUrl(String)` DSL method:\n\n.Changing the Generate URL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin((ott) -> ott\n                .generateTokenUrl(\"/ott/my-generate-url\")\n            );\n        return http.build();\n    }\n\n}\n\n@Component\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n        @Bean\n        open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                //...\n                formLogin { }\n                oneTimeTokenLogin {\n                    generateTokenUrl = \"/ott/my-generate-url\"\n                }\n            }\n            return http.build()\n        }\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {\n     // ...\n}\n----\n======\n\n[[changing-submit-page-url]]\n== Changing the Default Submit Page URL\n\nThe default One-Time Token submit page is generated by the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] and listens to `GET /login/ott`.\nThe URL can also be changed, like so:\n\n.Configuring the Default Submit Page URL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin((ott) -> ott\n                .submitPageUrl(\"/ott/submit\")\n            );\n        return http.build();\n    }\n\n}\n\n@Component\npublic class MagicLinkGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n        @Bean\n        open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                //...\n                formLogin { }\n                oneTimeTokenLogin {\n                    submitPageUrl = \"/ott/submit\"\n                }\n            }\n            return http.build()\n        }\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {\n     // ...\n}\n----\n======\n\n[[disabling-default-submit-page]]\n== Disabling the Default Submit Page\n\nIf you want to use your own One-Time Token submit page, you can disable the default page and then provide your own endpoint.\n\n.Disabling the Default Submit Page\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .requestMatchers(\"/my-ott-submit\").permitAll()\n                .anyRequest().authenticated()\n            )\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin((ott) -> ott\n                .showDefaultSubmitPage(false)\n            );\n        return http.build();\n    }\n\n}\n\n@Controller\npublic class MyController {\n\n    @GetMapping(\"/my-ott-submit\")\n    public String ottSubmitPage() {\n        return \"my-ott-submit\";\n    }\n\n}\n\n@Component\npublic class OneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n   @Bean\n   open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                authorizeHttpRequests {\n                    authorize(\"/my-ott-submit\", authenticated)\n                    authorize(anyRequest, authenticated)\n                }\n                formLogin { }\n                oneTimeTokenLogin {\n                    showDefaultSubmitPage = false\n                }\n            }\n            return http.build()\n    }\n}\n\n@Controller\nclass MyController {\n\n   @GetMapping(\"/my-ott-submit\")\n   fun ottSubmitPage(): String {\n       return \"my-ott-submit\"\n   }\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {\n     // ...\n}\n----\n======\n\n[[customize-generate-consume-token]]\n== Customize How to Generate and Consume One-Time Tokens\n\nThe interface that define the common operations for generating and consuming one-time tokens is the javadoc:org.springframework.security.authentication.ott.OneTimeTokenService[].\nSpring Security uses the javadoc:org.springframework.security.authentication.ott.InMemoryOneTimeTokenService[] as the default implementation of that interface, if none is provided.\nFor production environments consider using javadoc:org.springframework.security.authentication.ott.JdbcOneTimeTokenService[].\n\nSome of the most common reasons to customize the `OneTimeTokenService` are, but not limited to:\n\n- Changing the one-time token expire time\n- Storing more information from the generate token request\n- Changing how the token value is created\n- Additional validation when consuming a one-time token\n\nThere are two options to customize the `OneTimeTokenService`.\nOne option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL:\n\n.Passing the OneTimeTokenService as a Bean\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin(Customizer.withDefaults());\n        return http.build();\n    }\n\n    @Bean\n    public OneTimeTokenService oneTimeTokenService() {\n        return new MyCustomOneTimeTokenService();\n    }\n\n}\n\n@Component\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            //...\n            formLogin { }\n            oneTimeTokenLogin { }\n        }\n        return http.build()\n    }\n\n    @Bean\n    open fun oneTimeTokenService(): OneTimeTokenService {\n        return MyCustomOneTimeTokenService()\n    }\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {\n     // ...\n}\n----\n======\n\nThe second option is to pass the `OneTimeTokenService` instance to the DSL, which is useful if there are multiple `SecurityFilterChain` and a different `OneTimeTokenService` is needed for each of them.\n\n.Passing the OneTimeTokenService using the DSL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) {\n        http\n            // ...\n            .formLogin(Customizer.withDefaults())\n            .oneTimeTokenLogin((ott) -> ott\n                .oneTimeTokenService(new MyCustomOneTimeTokenService())\n            );\n        return http.build();\n    }\n\n}\n\n@Component\npublic class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            //...\n            formLogin { }\n            oneTimeTokenLogin {\n                oneTimeTokenService = MyCustomOneTimeTokenService()\n            }\n        }\n        return http.build()\n    }\n\n}\n\n@Component\nclass MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {\n     // ...\n}\n----\n======\n\n[[customize-generate-token-request]]\n== Customize GenerateOneTimeTokenRequest Instance\nThere are a number of reasons that you may want to adjust an GenerateOneTimeTokenRequest. For example, you may want expiresIn to be set to 10 mins, which Spring Security sets to 5 mins by default.\n\nYou can customize elements of GenerateOneTimeTokenRequest by publishing an GenerateOneTimeTokenRequestResolver as a @Bean, like so:\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nGenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {\n    DefaultGenerateOneTimeTokenRequestResolver delegate = new DefaultGenerateOneTimeTokenRequestResolver();\n        return (request) -> {\n\t\t    GenerateOneTimeTokenRequest generate = delegate.resolve(request);\n\t\t    return new GenerateOneTimeTokenRequest(generate.getUsername(), Duration.ofSeconds(600));\n\t};\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun generateRequestResolver() : GenerateOneTimeTokenRequestResolver {\n    return DefaultGenerateOneTimeTokenRequestResolver().apply {\n        this.setExpiresIn(Duration.ofMinutes(10))\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passkeys.adoc",
    "content": "[[passkeys]]\n= Passkeys\n\nSpring Security provides support for https://www.passkeys.com[passkeys].\nPasskeys are a more secure method of authenticating than passwords and are built using https://www.w3.org/TR/webauthn-3/[WebAuthn].\n\nIn order to use a passkey to authenticate, a user must first xref:servlet/authentication/passkeys.adoc#passkeys-register[Register a New Credential].\nAfter the credential is registered, it can be used to authenticate by xref:servlet/authentication/passkeys.adoc#passkeys-verify[verifying an authentication assertion].\n\n[[passkeys-dependencies]]\n== Required Dependencies\n\nTo get started, add the `spring-security-webauthn` dependency to your project.\n\n[NOTE]\n====\nThis assumes that you are managing Spring Security's versions with Spring Boot or Spring Security's BOM as described in xref:getting-spring-security.adoc[].\n====\n\n.Passkeys Dependencies\n[tabs]\n======\nMaven::\n+\n[source,xml,role=\"primary\",subs=\"verbatim,attributes\"]\n----\n<dependency>\n    <groupId>org.springframework.security</groupId>\n    <artifactId>spring-security-webauthn</artifactId>\n</dependency>\n----\n\nGradle::\n+\n[source,groovy,role=\"secondary\",subs=\"verbatim,attributes\"]\n----\ndependencies {\n    implementation \"org.springframework.security:spring-security-webauthn\"\n}\n----\n======\n\n[[passkeys-configuration]]\n== Configuration\n\nThe following configuration enables passkey authentication.\nIt provides a way to xref:./passkeys.adoc#passkeys-register[] at `/webauthn/register` and a default log in page that allows xref:./passkeys.adoc#passkeys-verify[authenticating with passkeys].\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityFilterChain filterChain(HttpSecurity http) {\n\t// ...\n\thttp\n\t\t// ...\n\t\t.formLogin(withDefaults())\n\t\t.webAuthn((webAuthn) -> webAuthn\n\t\t\t.rpId(\"example.com\")\n\t\t\t.allowedOrigins(\"https://example.com\")\n\t\t\t// optional properties\n\t\t\t.creationOptionsRepository(new CustomPublicKeyCredentialCreationOptionsRepository())\n\t\t\t.messageConverter(new CustomHttpMessageConverter())\n\t\t);\n\treturn http.build();\n}\n\n@Bean\nUserDetailsService userDetailsService() {\n\tUserDetails userDetails = User.withDefaultPasswordEncoder()\n\t\t.username(\"user\")\n\t\t.password(\"password\")\n\t\t.roles(\"USER\")\n\t\t.build();\n\n\treturn new InMemoryUserDetailsManager(userDetails);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\t// ...\n\thttp {\n\t\twebAuthn {\n\t\t\trpId = \"example.com\"\n\t\t\tallowedOrigins = setOf(\"https://example.com\")\n\t\t\t// optional properties\n\t\t\tcreationOptionsRepository = CustomPublicKeyCredentialCreationOptionsRepository()\n\t\t\tmessageConverter = CustomHttpMessageConverter()\n\t\t}\n\t}\n}\n\n@Bean\nopen fun userDetailsService(): UserDetailsService {\n\tval userDetails = User.withDefaultPasswordEncoder()\n\t\t.username(\"user\")\n\t\t.password(\"password\")\n\t\t.roles(\"USER\")\n\t\t.build()\n\treturn InMemoryUserDetailsManager(userDetails)\n}\n----\n======\n\n\n[[passkeys-configuration-persistence]]\n=== JDBC & Custom Persistence\n\nWebAuthn performs persistence with javadoc:org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository[] and javadoc:org.springframework.security.web.webauthn.management.UserCredentialRepository[].\nThe default is to use in memory persistence, but JDBC persistence is support with javadoc:org.springframework.security.web.webauthn.management.JdbcPublicKeyCredentialUserEntityRepository[] and javadoc:org.springframework.security.web.webauthn.management.JdbcUserCredentialRepository[].\nTo configure JDBC based persistence, expose the repositories as a Bean:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJdbcPublicKeyCredentialUserEntityRepository jdbcPublicKeyCredentialRepository(JdbcOperations jdbc) {\n\treturn new JdbcPublicKeyCredentialUserEntityRepository(jdbc);\n}\n\n@Bean\nJdbcUserCredentialRepository jdbcUserCredentialRepository(JdbcOperations jdbc) {\n\treturn new JdbcUserCredentialRepository(jdbc);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jdbcPublicKeyCredentialRepository(jdbc: JdbcOperations): JdbcPublicKeyCredentialUserEntityRepository {\n    return JdbcPublicKeyCredentialUserEntityRepository(jdbc)\n}\n\n@Bean\nfun jdbcUserCredentialRepository(jdbc: JdbcOperations): JdbcUserCredentialRepository {\n    return JdbcUserCredentialRepository(jdbc)\n}\n----\n======\n\nIf JDBC does not meet your needs, you can create your own implementations of the interfaces and use them by exposing them as a Bean similar to the example above.\n\n[[passkeys-configuration-pkccor]]\n=== Custom PublicKeyCredentialCreationOptionsRepository\n\nThe `PublicKeyCredentialCreationOptionsRepository` is used to persist the `PublicKeyCredentialCreationOptions` between requests.\nThe default is to persist it the `HttpSession`, but at times users may need to customize this behavior.\nThis can be done by setting the optional property `creationOptionsRepository` demonstrated in xref:./passkeys.adoc#passkeys-configuration[Configuration] or by exposing a `PublicKeyCredentialCreationOptionsRepository` Bean:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nCustomPublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {\n\treturn new CustomPublicKeyCredentialCreationOptionsRepository();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun creationOptionsRepository(): CustomPublicKeyCredentialCreationOptionsRepository {\n\treturn CustomPublicKeyCredentialCreationOptionsRepository()\n}\n----\n======\n\n[[passkeys-register]]\n== Register a New Credential\n\nIn order to use a passkey, a user must first https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential[Register a New Credential].\n\nRegistering a new credential is composed of two steps:\n\n1. Requesting the Registration Options\n2. Registering the Credential\n\n[[passkeys-register-options]]\n=== Request the Registration Options\n\nThe first step in registration of a new credential is to request the registration options.\nIn Spring Security, a request for the registration options is typically done using JavaScript and looks like:\n\n[NOTE]\n====\nSpring Security provides a default registration page that can be used as a reference on how to register credentials.\n====\n\n.Request for Registration Options\n[source,http]\n----\nPOST /webauthn/register/options\nX-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\n----\n\nThe request above will obtain the registration options for the currently authenticated user.\nSince the challenge is persisted (state is changed) to be compared at the time of registration, the request must be a POST and include a CSRF token.\n\n.Response for Registration Options\n[source,json]\n----\n{\n  \"rp\": {\n    \"name\": \"SimpleWebAuthn Example\",\n    \"id\": \"example.localhost\"\n  },\n  \"user\": {\n    \"name\": \"user@example.localhost\",\n    \"id\": \"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\",\n    \"displayName\": \"user@example.localhost\"\n  },\n  \"challenge\": \"q7lCdd3SVQxdC-v8pnRAGEn1B2M-t7ZECWPwCAmhWvc\",\n  \"pubKeyCredParams\": [\n    {\n      \"type\": \"public-key\",\n      \"alg\": -8\n    },\n    {\n      \"type\": \"public-key\",\n      \"alg\": -7\n    },\n    {\n      \"type\": \"public-key\",\n      \"alg\": -257\n    }\n  ],\n  \"timeout\": 300000,\n  \"excludeCredentials\": [],\n  \"authenticatorSelection\": {\n    \"residentKey\": \"required\",\n    \"userVerification\": \"preferred\"\n  },\n  \"attestation\": \"none\",\n  \"extensions\": {\n    \"credProps\": true\n  }\n}\n----\n\n[[passkeys-register-create]]\n=== Registering the Credential\n\nAfter the registration options are obtained, they are used to create the credentials that are registered.\nTo register a new credential, the application should pass the options to https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-create[`navigator.credentials.create`] after base64url decoding the binary values such as `user.id`, `challenge`, and `excludeCredentials[].id`.\n\nThe returned value can then be sent to the server as a JSON request.\nAn example registration request can be found below:\n\n.Example Registration Request\n[source,http]\n----\nPOST /webauthn/register\nX-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\n\n{\n  \"publicKey\": { // <1>\n    \"credential\": {\n      \"id\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n      \"rawId\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n      \"response\": {\n        \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAALraVWanqkAfvZZFYZpVEg0AEHWBexBp0RRSF5KV4vV1NsKlAQIDJiABIVggQjmrekPGzyqtoKK9HPUH-8Z2FLpoqkklFpFPQVICQ3IiWCD6I9Jvmor685fOZOyGXqUd87tXfvJk8rxj9OhuZvUALA\",\n        \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSl9RTi10SFJYRWVKYjlNcUNrWmFPLUdOVmlibXpGVGVWMk43Z0ptQUdrQSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n        \"transports\": [\n          \"internal\",\n          \"hybrid\"\n        ]\n      },\n      \"type\": \"public-key\",\n      \"clientExtensionResults\": {},\n      \"authenticatorAttachment\": \"platform\"\n    },\n    \"label\": \"1password\" // <2>\n  }\n}\n----\n<1> The result of calling `navigator.credentials.create` with binary values base64url encoded.\n<2> A label that the user selects to have associated with this credential to help the user distinguish the credential.\n\n.Example Successful Registration Response\n[source,http]\n----\nHTTP/1.1 200 OK\n\n{\n  \"success\": true\n}\n----\n\n[[passkeys-verify]]\n== Verifying an Authentication Assertion\n\nAfter xref:./passkeys.adoc#passkeys-register[] the passkey can be https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion[verified] (authenticated).\n\nVerifying a credential is composed of two steps:\n\n1. Requesting the Verification Options\n2. Verifying the Credential\n\n[[passkeys-verify-options]]\n=== Request the Verification Options\n\nThe first step in verification of a credential is to request the verification options.\nIn Spring Security, a request for the verification options is typically done using JavaScript and looks like:\n\n[NOTE]\n====\nSpring Security provides a default log in page that can be used as a reference on how to verify credentials.\n====\n\n.Request for Verification Options\n[source,http]\n----\nPOST /webauthn/authenticate/options\nX-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\n----\n\nThe request above will obtain the verification options.\nSince the challenge is persisted (state is changed) to be compared at the time of authentication, the request must be a POST and include a CSRF token.\n\nThe response will contain the options for obtaining a credential with binary values such as `challenge` base64url encoded.\n\n.Example Response for Verification Options\n[source,json]\n----\n{\n  \"challenge\": \"cQfdGrj9zDg3zNBkOH3WPL954FTOShVy0-CoNgSewNM\",\n  \"timeout\": 300000,\n  \"rpId\": \"example.localhost\",\n  \"allowCredentials\": [],\n  \"userVerification\": \"preferred\",\n  \"extensions\": {}\n}\n----\n\n[[passkeys-verify-get]]\n=== Verifying the Credential\n\nAfter the verification options are obtained, they are used to get a credential.\nTo get a credential, the application should pass the options to https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-create[`navigator.credentials.get`] after base64url decoding the binary values such as `challenge`.\n\nThe returned value of `navigator.credentials.get` can then be sent to the server as a JSON request.\nBinary values such as `rawId` and `response.*` must be base64url encoded.\nAn example authentication request can be found below:\n\n.Example Authentication Request\n[source,http]\n----\nPOST /login/webauthn\nX-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\n\n{\n  \"id\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n  \"rawId\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n  \"response\": {\n    \"authenticatorData\": \"y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA\",\n    \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRFVsRzRDbU9naWhKMG1vdXZFcE9HdUk0ZVJ6MGRRWmxUQmFtbjdHQ1FTNCIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n    \"signature\": \"MEYCIQCW2BcUkRCAXDmGxwMi78jknenZ7_amWrUJEYoTkweldAIhAMD0EMp1rw2GfwhdrsFIeDsL7tfOXVPwOtfqJntjAo4z\",\n    \"userHandle\": \"Q3_0Xd64_HW0BlKRAJnVagJTpLKLgARCj8zjugpRnVo\"\n  },\n  \"clientExtensionResults\": {},\n  \"authenticatorAttachment\": \"platform\"\n}\n----\n\n.Example Successful Authentication Response\n[source,http]\n----\nHTTP/1.1 200 OK\n\n{\n  \"redirectUrl\": \"/\", // <1>\n  \"authenticated\": true // <2>\n}\n----\n<1> The URL to redirect to\n<2> Indicates that the user is authenticated\n\n.Example Authentication Failure Response\n[source,http]\n----\nHTTP/1.1 401 OK\n\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc",
    "content": "[[servlet-authentication-basic]]\n= Basic Authentication\n:figures: servlet/authentication/unpwd\n\nThis section provides details on how Spring Security provides support for https://tools.ietf.org/html/rfc7617[Basic HTTP Authentication] for servlet-based applications.\n// FIXME: describe authenticationentrypoint, authenticationfailurehandler, authenticationsuccesshandler\n\nThis section describes how HTTP Basic Authentication works within Spring Security.\nFirst, we see the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authenticate] header is sent back to an unauthenticated client:\n\n.Sending WWW-Authenticate Header\n[.invert-dark]\nimage::{figures}/basicauthenticationentrypoint.png[]\n\nThe preceding figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.\n\nimage:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized.\n\nimage:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.\n\nimage:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__.\nThe configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of javadoc:org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint[], which sends a WWW-Authenticate header.\nThe `RequestCache` is typically a `NullRequestCache` that does not save the request since the client is capable of replaying the requests it originally requested.\n\n[NOTE]\n====\nThe default HTTP Basic Auth Provider will suppress both Response body and `WWW-Authenticate` header in the 401 response when the request was made with a `X-Requested-With: XMLHttpRequest` header.\nThis allows frontends to implement their own authentication code, instead of triggering the browser login dialog.\nTo override, implement your own javadoc:org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint[].\n====\n\nWhen a client receives the `WWW-Authenticate` header, it knows it should retry with a username and password.\nThe following image shows the flow for the username and password being processed:\n\n[[servlet-authentication-basicauthenticationfilter]]\n.Authenticating Username and Password\n[.invert-dark]\nimage::{figures}/basicauthenticationfilter.png[]\n\nThe preceding figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.\n\n\nimage:{icondir}/number_1.png[] When the user submits their username and password, the `BasicAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken`, which is a type of xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`.\n\nimage:{icondir}/number_2.png[] Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated.\nThe details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd[user information is stored].\n\nimage:{icondir}/number_3.png[] If authentication fails, then __Failure__.\n\n. The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.\n. `RememberMeServices.loginFail` is invoked.\nIf remember me is not configured, this is a no-op.\nSee the javadoc:org.springframework.security.web.authentication.RememberMeServices[] interface in the Javadoc.\n. `AuthenticationEntryPoint` is invoked to trigger the WWW-Authenticate to be sent again.\nSee the javadoc:org.springframework.security.web.AuthenticationEntryPoint[] interface in the Javadoc.\n\nimage:{icondir}/number_4.png[] If authentication is successful, then __Success__.\n\n* Any already-authenticated `Authentication` in the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] is loaded and its\nauthorities are added to the returned xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`].\n. The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].\n. `RememberMeServices.loginSuccess` is invoked.\nIf remember me is not configured, this is a no-op.\nSee the javadoc:org.springframework.security.web.authentication.RememberMeServices[] interface in the Javadoc.\n. The `BasicAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic.\nSee the javadoc:org.springframework.security.web.authentication.www.BasicAuthenticationFilter[] Class in the Javadoc\n\nBy default, Spring Security's HTTP Basic Authentication support is enabled.\nHowever, as soon as any servlet based configuration is provided, HTTP Basic must be explicitly provided.\n\nThe following example shows a minimal, explicit configuration:\n\n.Explicit HTTP Basic Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.httpBasic(withDefaults());\n\treturn http.build();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<http-basic />\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\thttp {\n\t\t// ...\n\t\thttpBasic { }\n\t}\n\treturn http.build()\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/caching.adoc",
    "content": "[[servlet-authentication-caching-user-details]]\n= Caching `UserDetails`\n\nSpring Security provides support for caching `UserDetails` with <<servlet-authentication-caching-user-details-service,`CachingUserDetailsService`>>.\nAlternatively, you can use Spring Framework's <<servlet-authentication-caching-user-details-cacheable,`@Cacheable`>> annotation.\nIn either case, you will need to <<servlet-authentication-caching-user-details-credential-erasure,disable credential erasure>> in order to validate passwords retrieved from the cache.\n\n[[servlet-authentication-caching-user-details-service]]\n== `CachingUserDetailsService`\n\nSpring Security's `CachingUserDetailsService` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for caching `UserDetails`.\n`CachingUserDetailsService` provides caching support for `UserDetails` by delegating to the provided `UserDetailsService`.\nThe result is then stored in a `UserCache` to reduce computation in subsequent calls.\n\nThe following example simply defines a `@Bean` that encapsulates a concrete implementation of `UserDetailsService` and a `UserCache` for caching the `UserDetails`:\n\n.Provide a `CachingUserDetailsService` `@Bean`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic CachingUserDetailsService cachingUserDetailsService(UserCache userCache) {\n\tUserDetailsService delegate = ...;\n    CachingUserDetailsService service = new CachingUserDetailsService(delegate);\n    service.setUserCache(userCache);\n    return service;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun cachingUserDetailsService(userCache: UserCache): CachingUserDetailsService {\n    val delegate: UserDetailsService = ...\n    val service = CachingUserDetailsService(delegate)\n    service.userCache = userCache\n    return service\n}\n----\n======\n\n[[servlet-authentication-caching-user-details-cacheable]]\n== `@Cacheable`\n\nAn alternative approach would be to use Spring Framework's {spring-framework-reference-url}integration.html#cache-annotations-cacheable[`@Cacheable`] in your `UserDetailsService` implementation to cache `UserDetails` by `username`.\nThe benefit to this approach is simpler configuration, especially if you are already using caching elsewhere in your application.\n\nThe following example assumes caching is already configured, and annotates the `loadUserByUsername` with `@Cacheable`:\n\n.`UserDetailsService` annotated with `@Cacheable`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Service\npublic class MyCustomUserDetailsImplementation implements UserDetailsService {\n\n    @Override\n    @Cacheable\n    public UserDetails loadUserByUsername(String username) {\n        // some logic here to get the actual user details\n        return userDetails;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Service\nclass MyCustomUserDetailsImplementation : UserDetailsService {\n\n    @Cacheable\n    override fun loadUserByUsername(username: String): UserDetails {\n        // some logic here to get the actual user details\n        return userDetails\n    }\n}\n----\n======\n\n[[servlet-authentication-caching-user-details-credential-erasure]]\n== Disable Credential Erasure\n\nWhether you use <<servlet-authentication-caching-user-details-service,`CachingUserDetailsService`>> or <<servlet-authentication-caching-user-details-cacheable,`@Cacheable`>>, you will need to disable xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager-erasing-credentials[credential erasure] so that the `UserDetails` will contain a `password` to be validated when retrieved from the cache.\nThe following example disables credential erasure for the global `AuthenticationManager` by configuring the `AuthenticationManagerBuilder` provided by Spring Security:\n\n.Disable credential erasure for the global `AuthenticationManager`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// ...\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic UserDetailsService userDetailsService() {\n\t\t// Return a UserDetailsService that caches users\n\t\t// ...\n\t}\n\n\t@Autowired\n\tpublic void configure(AuthenticationManagerBuilder builder) {\n\t\tbuilder.eraseCredentials(false);\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\t// ...\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun userDetailsService(): UserDetailsService {\n\t\t// Return a UserDetailsService that caches users\n\t\t// ...\n\t}\n\n\t@Autowired\n\tfun configure(builder: AuthenticationManagerBuilder) {\n\t\tbuilder.eraseCredentials(false)\n\t}\n\n}\n----\n=====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/credentials-container.adoc",
    "content": "[[servlet-authentication-credentialscontainer]]\n= CredentialsContainer\n\nThe javadoc:org.springframework.security.core.CredentialsContainer[] interface indicates that the implementing object contains sensitive data, and is used internally by Spring Security to erase the authentication credentials after a successful authentication.\nThis interface is implemented by most of Spring Security internal domain classes, like javadoc:org.springframework.security.core.userdetails.User[] and javadoc:org.springframework.security.authentication.UsernamePasswordAuthenticationToken[].\n\nThe `ProviderManager` manager checks whether the returned `Authentication` implements this interface.\nIf so, xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager-erasing-credentials[it calls the `eraseCredentials` method] to remove the credentials from the object.\n\nIf you want your custom authentication objects to have their credentials erased after authentication, you should ensure that the classes implement the `CredentialsContainer` interface.\n\nUsers who are writing their own `AuthenticationProvider` implementations should create and return an appropriate `Authentication` object there, minus any sensitive data, rather than using this interface.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc",
    "content": "[[servlet-authentication-daoauthenticationprovider]]\n= DaoAuthenticationProvider\n:figures: servlet/authentication/unpwd\n\njavadoc:org.springframework.security.authentication.dao.DaoAuthenticationProvider[] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that uses a xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] and xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to authenticate a username and password.\n\nThis section examines how `DaoAuthenticationProvider` works within Spring Security.\nThe following figure explains the workings of the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from the xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] section.\n\n.`DaoAuthenticationProvider` Usage\n[.invert-dark]\nimage::{figures}/daoauthenticationprovider.png[]\n\nimage:{icondir}/number_1.png[] The authentication `Filter` from the xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] section passes a `UsernamePasswordAuthenticationToken` to the `AuthenticationManager`, which is implemented by xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`ProviderManager`].\n\nimage:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `DaoAuthenticationProvider`.\n\nimage:{icondir}/number_3.png[] `DaoAuthenticationProvider` looks up the `UserDetails` from the `UserDetailsService`.\n\nimage:{icondir}/number_4.png[] `DaoAuthenticationProvider` uses the xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to validate the password on the `UserDetails` returned in the previous step.\n\nimage:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService` and a set of authorities containing at least `FACTOR_PASSWORD`.\nUltimately, the returned `UsernamePasswordAuthenticationToken` is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/digest.adoc",
    "content": "[[servlet-authentication-digest]]\n= Digest Authentication\n\nThis section provides details on how Spring Security provides support for https://tools.ietf.org/html/rfc2617[Digest Authentication], which is provided `DigestAuthenticationFilter`.\n\n[WARNING]\n====\nYou should not use Digest Authentication in modern applications, because it is not considered to be secure.\nThe most obvious problem is that you must store your passwords in plaintext or an encrypted or MD5 format.\nAll of these storage formats are considered insecure.\nInstead, you should store credentials by using a one way adaptive password hash (bCrypt, PBKDF2, SCrypt, and others), which is not supported by Digest Authentication.\n====\n\nDigest Authentication tries to solve many of the weaknesses of xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[Basic authentication], specifically by ensuring credentials are never sent in clear text across the wire.\nMany https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest#Browser_compatibility[browsers support Digest Authentication].\n\nThe standard governing HTTP Digest Authentication is defined by https://tools.ietf.org/html/rfc2617[RFC 2617], which updates an earlier version of the Digest Authentication standard prescribed by https://tools.ietf.org/html/rfc2069[RFC 2069].\nMost user agents implement RFC 2617.\nSpring Security's Digest Authentication support is compatible with the \"`auth`\" quality of protection (`qop`) prescribed by RFC 2617, which also provides backward compatibility with RFC 2069.\nDigest Authentication was seen as a more attractive option if you need to use unencrypted HTTP (no TLS or HTTPS) and wish to maximize security of the authentication process.\nHowever, everyone should use xref:features/exploits/http.adoc#http[HTTPS].\n\nCentral to Digest Authentication is a \"`nonce`\".\nThis is a value the server generates.\nSpring Security's nonce adopts the following format:\n\n.Digest Syntax\n[source,txt]\n----\nbase64(expirationTime + \":\" + md5Hex(expirationTime + \":\" + key))\nexpirationTime:   The date and time when the nonce expires, expressed in milliseconds\nkey:              A private key to prevent modification of the nonce token\n----\n\nYou need to ensure that you xref:features/authentication/password-storage.adoc#authentication-password-storage-configuration[configure] insecure plain text xref:features/authentication/password-storage.adoc#authentication-password-storage[Password Storage] using `NoOpPasswordEncoder`.\n(See the javadoc:org.springframework.security.crypto.password.NoOpPasswordEncoder[] class in the Javadoc.)\nThe following provides an example of configuring Digest Authentication with Java Configuration:\n\n.Digest Authentication\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nUserDetailsService userDetailsService;\n\nDigestAuthenticationEntryPoint authenticationEntryPoint() {\n\tDigestAuthenticationEntryPoint result = new DigestAuthenticationEntryPoint();\n\tresult.setRealmName(\"My App Realm\");\n\tresult.setKey(\"3028472b-da34-4501-bfd8-a355c42bdf92\");\n\treturn result;\n}\n\nDigestAuthenticationFilter digestAuthenticationFilter() {\n\tDigestAuthenticationFilter result = new DigestAuthenticationFilter();\n\tresult.setUserDetailsService(userDetailsService);\n\tresult.setAuthenticationEntryPoint(authenticationEntryPoint());\n\treturn result;\n}\n\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t// ...\n\t\t.exceptionHandling((e) -> e.authenticationEntryPoint(authenticationEntryPoint()))\n\t\t.addFilter(digestAuthenticationFilter());\n\treturn http.build();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<b:bean id=\"digestFilter\"\n        class=\"org.springframework.security.web.authentication.www.DigestAuthenticationFilter\"\n    p:userDetailsService-ref=\"jdbcDaoImpl\"\n    p:authenticationEntryPoint-ref=\"digestEntryPoint\"\n/>\n\n<b:bean id=\"digestEntryPoint\"\n        class=\"org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint\"\n    p:realmName=\"My App Realm\"\n\tp:key=\"3028472b-da34-4501-bfd8-a355c42bdf92\"\n/>\n\n<http>\n\t<!-- ... -->\n\t<custom-filter ref=\"userFilter\" position=\"DIGEST_AUTH_FILTER\"/>\n</http>\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/erasure.adoc",
    "content": "== Password Erasure\n\nAfter successful authentication, it is a security best practice to erase credentials from memory to prevent them from being exposed to potential memory dump attacks.\n`ProviderManager` in Spring Security supports this practice through the `eraseCredentials` method, which should be invoked after the authentication process is complete.\n\n=== Best Practices\n\n* *Immediate Erasure*: Credentials should be erased immediately after they are no longer needed, which minimizes the window during which the credentials are exposed in memory.\n* *Automatic Erasure*: Configure `ProviderManager` to automatically erase credentials post-authentication by setting `eraseCredentialsAfterAuthentication` to `true` (the default).\n* *Custom Erasure Strategies*: Implement custom erasure strategies in custom `AuthenticationManager` implementations if the default erasure behavior does not meet specific security requirements.\n\n=== Risk Assessment\n\nFailure to properly erase credentials can lead to several risks:\n\n* *Memory Access Attacks*: Attackers can access raw credentials from memory through exploits like buffer overflow attacks or memory dumps.\n* *Insider Threats*: Malicious insiders with access to systems could potentially extract credentials from application memory.\n* *Accidental Exposure*: In multi-tenant environments, lingering credentials in memory could accidentally be exposed to other tenants.\n\n=== Implementation\n\n[source,java]\n----\npublic class CustomAuthenticationManager implements AuthenticationManager {\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authenticationRequest)\n\t\t\tthrows AuthenticationException {\n\n\t\tAuthentication authenticationResult;\n\t\t// TODO: Perform authentication checks...\n\n\t\t// Erase credentials post-check\n\t\tif (authenticationResult instanceof CredentialsContainer container) {\n\t\t\tcontainer.eraseCredentials();\n\t\t}\n\t}\n\n}\n----\n\nBy implementing these practices, organizations can significantly enhance the security of their authentication systems by ensuring that credentials are not left exposed in system memory.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/form.adoc",
    "content": "[[servlet-authentication-form]]\n= Form Login\n:figures: servlet/authentication/unpwd\n\nSpring Security provides support for username and password being provided through an HTML form.\nThis section provides details on how form based authentication works within Spring Security.\n// FIXME: describe authenticationentrypoint, authenticationfailurehandler, authenticationsuccesshandler\n\nThis section examines how form-based login works within Spring Security.\nFirst, we see how the user is redirected to the login form:\n\n.Redirecting to the Login Page\n[.invert-dark]\nimage::{figures}/loginurlauthenticationentrypoint.png[]\n\nThe preceding figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.\n\nimage:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource (`/private`) for which it is not authorized.\n\nimage:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.\n\nimage:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the login page with the configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`].\nIn most cases, the `AuthenticationEntryPoint` is an instance of javadoc:org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint[].\n\nimage:{icondir}/number_4.png[] The browser requests the login page to which it was redirected.\n\nimage:{icondir}/number_5.png[] Something within the application, must <<servlet-authentication-form-custom,render the login page>>.\n\n[[servlet-authentication-usernamepasswordauthenticationfilter]]\nWhen the username and password are submitted, the `UsernamePasswordAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication], by extracting the username and password from the `HttpServletRequest` instance.\nThe `UsernamePasswordAuthenticationFilter` extends xref:servlet/authentication/architecture.adoc#servlet-authentication-abstractprocessingfilter[AbstractAuthenticationProcessingFilter], so the following diagram should look pretty similar:\n\n.Authenticating Username and Password\n[.invert-dark]\nimage::{figures}/usernamepasswordauthenticationfilter.png[]\n\nThe figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.\n\n\nimage:{icondir}/number_1.png[] When the user submits their username and password, the `UsernamePasswordAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken`, which is a type of  xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`], by extracting the username and password from the `HttpServletRequest` instance.\n\nimage:{icondir}/number_2.png[]  Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` instance to be authenticated.\nThe details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-storage[user information is stored].\n\nimage:{icondir}/number_3.png[] If authentication fails, then __Failure__.\n\n. The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.\n. `RememberMeServices.loginFail` is invoked.\nIf remember me is not configured, this is a no-op.\nSee the javadoc:org.springframework.security.web.authentication.RememberMeServices[] interface in the Javadoc.\n. `AuthenticationFailureHandler` is invoked.\nSee the javadoc:org.springframework.security.web.authentication.AuthenticationFailureHandler[] class in the Javadoc\n\nimage:{icondir}/number_4.png[] If authentication is successful, then __Success__.\n\n. `SessionAuthenticationStrategy` is notified of a new login.\nSee the javadoc:org.springframework.security.web.authentication.session.SessionAuthenticationStrategy[] interface in the Javadoc.\n. The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].\nSee the javadoc:org.springframework.security.web.context.SecurityContextPersistenceFilter[] class in the Javadoc.\n. `RememberMeServices.loginSuccess` is invoked.\nIf remember me is not configured, this is a no-op.\nSee the javadoc:org.springframework.security.web.authentication.RememberMeServices[] interface in the Javadoc.\n. `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`.\n. The `AuthenticationSuccessHandler` is invoked. Typically, this is a `SimpleUrlAuthenticationSuccessHandler`, which redirects to a request saved by xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] when we redirect to the login page.\n\n[[servlet-authentication-form-min]]\nBy default, Spring Security form login is enabled.\nHowever, as soon as any servlet-based configuration is provided, form based login must be explicitly provided.\nThe following example shows a minimal, explicit Java configuration:\n\n.Form Login\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n\thttp\n\t\t.formLogin(withDefaults());\n\t// ...\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<form-login />\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\thttp {\n\t\tformLogin { }\n\t}\n\t// ...\n}\n----\n======\n\nIn the preceding configuration, Spring Security renders a default login page.\nMost production applications require a custom login form.\n\n[[servlet-authentication-form-custom]]\nThe following configuration demonstrates how to provide a custom login form.\n\n.Custom Login Form Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n\thttp\n\t\t.formLogin((form) -> form\n\t\t\t.loginPage(\"/login\")\n\t\t\t.permitAll()\n\t\t);\n\t// ...\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<intercept-url pattern=\"/login\" access=\"permitAll\" />\n\t<form-login login-page=\"/login\" />\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\thttp {\n\t\tformLogin {\n\t\t\tloginPage = \"/login\"\n\t\t\tpermitAll()\n\t\t}\n\t}\n\t// ...\n}\n----\n======\n\n[[servlet-authentication-form-custom-html]]\nWhen the login page is specified in the Spring Security configuration, you are responsible for rendering the page.\n// FIXME: default login page rendered by Spring Security\nThe following https://www.thymeleaf.org/[Thymeleaf] template produces an HTML login form that complies with a login page of `/login`.:\n\n.Login Form - src/main/resources/templates/login.html\n[source,xml]\n----\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:th=\"https://www.thymeleaf.org\">\n\t<head>\n\t\t<title>Please Log In</title>\n\t</head>\n\t<body>\n\t\t<h1>Please Log In</h1>\n\t\t<div th:if=\"${param.error}\">\n\t\t\tInvalid username and password.</div>\n\t\t<div th:if=\"${param.logout}\">\n\t\t\tYou have been logged out.</div>\n\t\t<form th:action=\"@{/login}\" method=\"post\">\n\t\t\t<div>\n\t\t\t<input type=\"text\" name=\"username\" placeholder=\"Username\"/>\n\t\t\t</div>\n\t\t\t<div>\n\t\t\t<input type=\"password\" name=\"password\" placeholder=\"Password\"/>\n\t\t\t</div>\n\t\t\t<input type=\"submit\" value=\"Log in\" />\n\t\t</form>\n\t</body>\n</html>\n----\n\nThere are a few key points about the default HTML form:\n\n* The form should perform a `post` to `/login`.\n* The form needs to include a xref:servlet/exploits/csrf.adoc#servlet-csrf[CSRF Token], which is xref:servlet/exploits/csrf.adoc#csrf-integration-form[automatically included] by Thymeleaf.\n* The form should specify the username in a parameter named `username`.\n* The form should specify the password in a parameter named `password`.\n* If the HTTP parameter named `error` is found, it indicates the user failed to provide a valid username or password.\n* If the HTTP parameter named `logout` is found, it indicates the user has logged out successfully.\n\nMany users do not need much more than to customize the login page.\nHowever, if needed, you can customize everything shown earlier with additional configuration.\n\n[[servlet-authentication-form-custom-controller]]\nIf you use Spring MVC, you need a controller that maps `GET /login` to the login template we created.\nThe following example shows a minimal `LoginController`:\n\n.LoginController\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\nclass LoginController {\n\t@GetMapping(\"/login\")\n\tString login() {\n\t\treturn \"login\";\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass LoginController {\n    @GetMapping(\"/login\")\n    fun login(): String {\n        return \"login\"\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/in-memory.adoc",
    "content": "[[servlet-authentication-inmemory]]\n= In-Memory Authentication\n\nSpring Security's `InMemoryUserDetailsManager` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for username/password based authentication that is stored in memory.\n`InMemoryUserDetailsManager` provides management of `UserDetails` by implementing the `UserDetailsManager` interface.\n`UserDetails`-based authentication is used by Spring Security when it is configured to xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[accept a username and password] for authentication.\n\nIn the following sample, we use xref:features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode a password value of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`:\n\n.InMemoryUserDetailsManager Java Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Bean\npublic UserDetailsService users() {\n\tUserDetails user = User.builder()\n\t\t.username(\"user\")\n\t\t.password(\"{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\")\n\t\t.roles(\"USER\")\n\t\t.build();\n\tUserDetails admin = User.builder()\n\t\t.username(\"admin\")\n\t\t.password(\"{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\")\n\t\t.roles(\"USER\", \"ADMIN\")\n\t\t.build();\n\treturn new InMemoryUserDetailsManager(user, admin);\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\",attrs=\"-attributes\"]\n----\n<user-service>\n\t<user name=\"user\"\n\t\tpassword=\"{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\"\n\t\tauthorities=\"ROLE_USER\" />\n\t<user name=\"admin\"\n\t\tpassword=\"{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\"\n\t\tauthorities=\"ROLE_USER,ROLE_ADMIN\" />\n</user-service>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Bean\nfun users(): UserDetailsService {\n    val user = User.builder()\n        .username(\"user\")\n        .password(\"{bcrypt}$2a$10\\$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\")\n        .roles(\"USER\")\n        .build()\n    val admin = User.builder()\n        .username(\"admin\")\n        .password(\"{bcrypt}$2a$10\\$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\")\n        .roles(\"USER\", \"ADMIN\")\n        .build()\n    return InMemoryUserDetailsManager(user, admin)\n}\n----\n======\n\nThe preceding samples store the passwords in a secure format but leave a lot to be desired in terms of a getting started experience.\n\nIn the following sample, we use xref:features/authentication/password-storage.adoc#authentication-password-storage-dep-getting-started[User.withDefaultPasswordEncoder] to ensure that the password stored in memory is protected.\nHowever, it does not protect against obtaining the password by decompiling the source code.\nFor this reason, `User.withDefaultPasswordEncoder` should only be used for \"`getting started`\" and is not intended for production.\n\n.InMemoryUserDetailsManager with User.withDefaultPasswordEncoder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic UserDetailsService users() {\n\t// The builder will ensure the passwords are encoded before saving in memory\n\tUserBuilder users = User.withDefaultPasswordEncoder();\n\tUserDetails user = users\n\t\t.username(\"user\")\n\t\t.password(\"password\")\n\t\t.roles(\"USER\")\n\t\t.build();\n\tUserDetails admin = users\n\t\t.username(\"admin\")\n\t\t.password(\"password\")\n\t\t.roles(\"USER\", \"ADMIN\")\n\t\t.build();\n\treturn new InMemoryUserDetailsManager(user, admin);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun users(): UserDetailsService {\n    // The builder will ensure the passwords are encoded before saving in memory\n    val users = User.withDefaultPasswordEncoder()\n    val user = users\n        .username(\"user\")\n        .password(\"password\")\n        .roles(\"USER\")\n        .build()\n    val admin = users\n        .username(\"admin\")\n        .password(\"password\")\n        .roles(\"USER\", \"ADMIN\")\n        .build()\n    return InMemoryUserDetailsManager(user, admin)\n}\n----\n======\n\nThere is no simple way to use `User.withDefaultPasswordEncoder` with XML-based configuration.\nFor demos or just getting started, you can choose to prefix the password with `+{noop}+` to indicate xref:features/authentication/password-storage.adoc#authentication-password-storage-dpe-format[no encoding should be used]:\n\n.<user-service> `+{noop}+` XML Configuration\n[source,xml,attrs=\"-attributes\"]\n----\n<user-service>\n\t<user name=\"user\"\n\t\tpassword=\"{noop}password\"\n\t\tauthorities=\"ROLE_USER\" />\n\t<user name=\"admin\"\n\t\tpassword=\"{noop}password\"\n\t\tauthorities=\"ROLE_USER,ROLE_ADMIN\" />\n</user-service>\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/index.adoc",
    "content": "[[servlet-authentication-unpwd]]\n= Username/Password Authentication\n:page-section-summary-toc: 1\n:figures: images/servlet/authentication/unpwd\n:icondir: images/icons\n\nOne of the most common ways to authenticate a user is by validating a username and password.\nSpring Security provides comprehensive support for authenticating with a username and password.\n\nYou can configure username and password authentication using the following:\n\n.Simple Username/Password Example\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t.formLogin(Customizer.withDefaults());\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic UserDetailsService userDetailsService() {\n\t\tUserDetails userDetails = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\n\t\treturn new InMemoryUserDetailsManager(userDetails);\n\t}\n\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t<form-login />\n\t<http-basic />\n\n\t<user-service>\n\t\t<user name=\"user\"\n\t\t\tpassword=\"{noop}password\"\n\t\t\tauthorities=\"ROLE_USER\" />\n\t</user-service>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, authenticated)\n\t\t\t}\n\t\t\tformLogin { }\n\t\t\thttpBasic { }\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun userDetailsService(): UserDetailsService {\n\t\tval user = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build()\n\n\t\treturn InMemoryUserDetailsManager(user)\n\t}\n\n}\n----\n=====\n\n[WARNING]\n`User#withDefaultPasswordEncoder` is considered unsafe for production and is only intended for sample applications. See javadoc:org.springframework.security.core.userdetails.User#withDefaultPasswordEncoder()[User#withDefaultPasswordEncoder] for more details.\n\nThe preceding configuration automatically registers an xref:servlet/authentication/passwords/in-memory.adoc[in-memory `UserDetailsService`] with the `SecurityFilterChain`, registers the xref:servlet/authentication/passwords/dao-authentication-provider.adoc[`DaoAuthenticationProvider`] with the default xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`], and enables xref:servlet/authentication/passwords/form.adoc[Form Login] and xref:servlet/authentication/passwords/basic.adoc[HTTP Basic] authentication.\n\nTo learn more about username/password authentication, consider the following use cases:\n\n* I want to xref:servlet/authentication/passwords/form.adoc[learn how Form Login works]\n* I want to xref:servlet/authentication/passwords/basic.adoc[learn how HTTP Basic authentication works]\n* I want to xref:servlet/authentication/passwords/dao-authentication-provider.adoc[learn how `DaoAuthenticationProvider` works]\n* I want to xref:servlet/authentication/passwords/in-memory.adoc[manage users in memory]\n* I want to xref:servlet/authentication/passwords/jdbc.adoc[manage users in a database]\n* I want to xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap-authentication[manage users in LDAP]\n* I want to <<publish-authentication-manager-bean,publish an `AuthenticationManager` bean>> for custom authentication\n* I want to <<customize-global-authentication-manager,customize the global `AuthenticationManager`>>\n\n[[publish-authentication-manager-bean]]\n== Publish an `AuthenticationManager` bean\n\nA fairly common requirement is publishing an `AuthenticationManager` bean to allow for custom authentication, such as in a `@Service` or Spring MVC `@Controller`.\nFor example, you may want to authenticate users via a REST API instead of using xref:servlet/authentication/passwords/form.adoc[Form Login].\n\nYou can publish such an `AuthenticationManager` for custom authentication scenarios using the following configuration:\n\n.Publish `AuthenticationManager` bean for Custom Authentication\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(\"/login\").permitAll()\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic AuthenticationManager authenticationManager(\n\t\t\tUserDetailsService userDetailsService,\n\t\t\tPasswordEncoder passwordEncoder) {\n\t\tDaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(userDetailsService);\n\t\tauthenticationProvider.setPasswordEncoder(passwordEncoder);\n\n\t\treturn new ProviderManager(authenticationProvider);\n\t}\n\n\t@Bean\n\tpublic UserDetailsService userDetailsService() {\n\t\tUserDetails userDetails = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\n\t\treturn new InMemoryUserDetailsManager(userDetails);\n\t}\n\n\t@Bean\n\tpublic PasswordEncoder passwordEncoder() {\n\t\treturn PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t}\n\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<intercept-url pattern=\"/login\" access=\"permitAll\"/>\n\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\n\t<bean id=\"authenticationManager\"\n\t\t\tclass=\"org.springframework.security.authentication.ProviderManager\">\n\t\t<constructor-arg>\n\t\t\t<bean class=\"org.springframework.security.authentication.dao.DaoAuthenticationProvider\">\n\t\t\t\t<constructor-arg name=\"userDetailsService\" ref=\"userDetailsService\" />\n\t\t\t\t<property name=\"passwordEncoder\" ref=\"passwordEncoder\" />\n\t\t\t</bean>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<user-service id=\"userDetailsService\">\n\t\t<user name=\"user\"\n\t\t\tpassword=\"{noop}password\"\n\t\t\tauthorities=\"ROLE_USER\" />\n\t</user-service>\n\n\t<bean id=\"passwordEncoder\"\n\t\t\tclass=\"org.springframework.security.crypto.factory.PasswordEncoderFactories\" factory-method=\"createDelegatingPasswordEncoder\"/>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(\"/login\", permitAll)\n\t\t\t\tauthorize(anyRequest, authenticated)\n\t\t\t}\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun authenticationManager(\n\t\t\tuserDetailsService: UserDetailsService,\n\t\t\tpasswordEncoder: PasswordEncoder): AuthenticationManager {\n\t\tval authenticationProvider = DaoAuthenticationProvider(userDetailsService)\n\t\tauthenticationProvider.setPasswordEncoder(passwordEncoder)\n\n\t\treturn ProviderManager(authenticationProvider)\n\t}\n\n\t@Bean\n\tfun userDetailsService(): UserDetailsService {\n\t\tval user = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build()\n\n\t\treturn InMemoryUserDetailsManager(user)\n\t}\n\n\t@Bean\n\tfun passwordEncoder(): PasswordEncoder {\n\t\treturn PasswordEncoderFactories.createDelegatingPasswordEncoder()\n\t}\n\n}\n----\n=====\n\nWith the preceding configuration in place, you can create a `@RestController` that uses the `AuthenticationManager` as follows:\n\n\n.Create a `@RestController` for Authentication\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class LoginController {\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tpublic LoginController(AuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t@PostMapping(\"/login\")\n\tpublic ResponseEntity<Void> login(@RequestBody LoginRequest loginRequest) {\n\t\tAuthentication authenticationRequest =\n\t\t\tUsernamePasswordAuthenticationToken.unauthenticated(loginRequest.username(), loginRequest.password());\n\t\tAuthentication authenticationResponse =\n\t\t\tthis.authenticationManager.authenticate(authenticationRequest);\n\t\t// ...\n\t}\n\n\tpublic record LoginRequest(String username, String password) {\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RestController\nclass LoginController(val authenticationManager: AuthenticationManager) {\n\n\t@PostMapping(\"/login\")\n\tfun login(@RequestBody loginRequest: LoginRequest): ResponseEntity<Void> {\n\t\tval authenticationRequest =\n\t\t\tUsernamePasswordAuthenticationToken.unauthenticated(\n\t\t\t\tloginRequest.username, loginRequest.password)\n\t\tval authenticationResponse =\n\t\t\tauthenticationManager.authenticate(authenticationRequest)\n\t\t// ...\n\t}\n\n\tdata class LoginRequest(val username: String, val password: String)\n\n}\n----\n=====\n\n[NOTE]\n====\nIn this example, it is your responsibility to save the authenticated user in the `SecurityContextRepository` if needed.\nFor example, if using the `HttpSession` to persist the `SecurityContext` between requests, you can use xref:servlet/authentication/persistence.adoc#httpsecuritycontextrepository[`HttpSessionSecurityContextRepository`].\n====\n\n[[customize-global-authentication-manager]]\n== Customize the `AuthenticationManager`\n\nNormally, Spring Security builds an `AuthenticationManager` internally composed of a `DaoAuthenticationProvider` for username/password authentication.\nIn certain cases, it may still be desired to customize the instance of `AuthenticationManager` used by Spring Security.\nFor example, you may need to simply disable xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager-erasing-credentials[credential erasure] for cached users.\n\nTo do this, you can take advantage of the fact that the `AuthenticationManagerBuilder` used to build Spring Security's global `AuthenticationManager` is published as a bean.\nYou can configure the builder as follows:\n\n.Configure global `AuthenticationManagerBuilder`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// ...\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic UserDetailsService userDetailsService() {\n\t\t// Return a UserDetailsService that caches users\n\t\t// ...\n\t}\n\n\t@Autowired\n\tpublic void configure(AuthenticationManagerBuilder builder) {\n\t\tbuilder.eraseCredentials(false);\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\t// ...\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun userDetailsService(): UserDetailsService {\n\t\t// Return a UserDetailsService that caches users\n\t\t// ...\n\t}\n\n\t@Autowired\n\tfun configure(builder: AuthenticationManagerBuilder) {\n\t\tbuilder.eraseCredentials(false)\n\t}\n\n}\n----\n=====\n\nAlternatively, you may configure a local `AuthenticationManager` to override the global one.\n\n.Configure local `AuthenticationManager` for Spring Security\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.authenticationManager(authenticationManager());\n\n\t\treturn http.build();\n\t}\n\n\tprivate AuthenticationManager authenticationManager() {\n\t\tDaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(userDetailsService());\n\t\tauthenticationProvider.setPasswordEncoder(passwordEncoder());\n\n\t\tProviderManager providerManager = new ProviderManager(authenticationProvider);\n\t\tproviderManager.setEraseCredentialsAfterAuthentication(false);\n\n\t\treturn providerManager;\n\t}\n\n\tprivate UserDetailsService userDetailsService() {\n\t\tUserDetails userDetails = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\n\t\treturn new InMemoryUserDetailsManager(userDetails);\n\t}\n\n\tprivate PasswordEncoder passwordEncoder() {\n\t\treturn PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t}\n\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http authentication-manager-ref=\"authenticationManager\">\n\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t<form-login />\n\t<http-basic />\n\n\t<bean id=\"authenticationManager\"\n\t\t\tclass=\"org.springframework.security.authentication.ProviderManager\">\n\t\t<constructor-arg>\n\t\t\t<bean class=\"org.springframework.security.authentication.dao.DaoAuthenticationProvider\">\n\t\t\t\t<constructor-arg name=\"userDetailsService\" ref=\"userDetailsService\" />\n\t\t\t\t<property name=\"passwordEncoder\" ref=\"passwordEncoder\" />\n\t\t\t</bean>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<user-service id=\"userDetailsService\">\n\t\t<user name=\"user\"\n\t\t\tpassword=\"{noop}password\"\n\t\t\tauthorities=\"ROLE_USER\" />\n\t</user-service>\n\n\t<bean id=\"passwordEncoder\"\n\t\t\tclass=\"org.springframework.security.crypto.factory.PasswordEncoderFactories\" factory-method=\"createDelegatingPasswordEncoder\"/>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, authenticated)\n\t\t\t}\n\t\t\tformLogin { }\n\t\t\thttpBasic { }\n\t\t\tauthenticationManager = authenticationManager()\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun authenticationManager(): AuthenticationManager {\n\t\tval authenticationProvider = DaoAuthenticationProvider(userDetailsService())\n\t\tauthenticationProvider.setPasswordEncoder(passwordEncoder())\n\n\t\tval providerManager = ProviderManager(authenticationProvider)\n\t\tproviderManager.eraseCredentialsAfterAuthentication = false\n\n\t\treturn providerManager\n\t}\n\n\tprivate fun userDetailsService(): UserDetailsService {\n\t\tval user = User.withDefaultPasswordEncoder()\n\t\t\t.username(\"user\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build()\n\n\t\treturn InMemoryUserDetailsManager(user)\n\t}\n\n\tprivate fun passwordEncoder(): PasswordEncoder {\n\t\treturn PasswordEncoderFactories.createDelegatingPasswordEncoder()\n\t}\n\n}\n----\n=====\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/input.adoc",
    "content": "[[servlet-authentication-unpwd-input]]\n= Reading the Username & Password\n:page-section-summary-toc: 1\n\nSpring Security provides the following built-in mechanisms for reading a username and password from `HttpServletRequest`:\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/jdbc.adoc",
    "content": "[[servlet-authentication-jdbc]]\n= JDBC Authentication\n\nSpring Security's `JdbcDaoImpl` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] to provide support for username-and-password-based authentication that is retrieved by using JDBC.\n`JdbcUserDetailsManager` extends `JdbcDaoImpl` to provide management of `UserDetails` through the `UserDetailsManager` interface.\n`UserDetails`-based authentication is used by Spring Security when it is configured to xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication.\n\nIn the following sections, we discuss:\n\n* The <<servlet-authentication-jdbc-schema>> used by Spring Security JDBC Authentication\n* <<servlet-authentication-jdbc-datasource>>\n* <<servlet-authentication-jdbc-bean>>\n\n[[servlet-authentication-jdbc-schema]]\n== Default Schema\n\nSpring Security provides default queries for JDBC-based authentication.\nThis section provides the corresponding default schemas used with the default queries.\nYou need to adjust the schema to match any customizations to the queries and the database dialect you use.\n\n[[servlet-authentication-jdbc-schema-user]]\n=== User Schema\n\n`JdbcDaoImpl` requires tables to load the password, account status (enabled or disabled) and a list of authorities (roles) for the user.\n\n[NOTE]\n====\nThe default schema is also exposed as a classpath resource named `org/springframework/security/core/userdetails/jdbc/users.ddl`.\n====\n\n.Default User Schema\n[source,sql]\n----\ncreate table users(\n\tusername varchar_ignorecase(50) not null primary key,\n\tpassword varchar_ignorecase(500) not null,\n\tenabled boolean not null\n);\n\ncreate table authorities (\n\tusername varchar_ignorecase(50) not null,\n\tauthority varchar_ignorecase(50) not null,\n\tconstraint fk_authorities_users foreign key(username) references users(username)\n);\ncreate unique index ix_auth_username on authorities (username,authority);\n----\n\nOracle is a popular database choice but requires a slightly different schema:\n\n.Default User Schema for Oracle Databases\n[source,sql]\n----\nCREATE TABLE USERS (\n    USERNAME NVARCHAR2(128) PRIMARY KEY,\n    PASSWORD NVARCHAR2(128) NOT NULL,\n    ENABLED CHAR(1) CHECK (ENABLED IN ('Y','N') ) NOT NULL\n);\n\n\nCREATE TABLE AUTHORITIES (\n    USERNAME NVARCHAR2(128) NOT NULL,\n    AUTHORITY NVARCHAR2(128) NOT NULL\n);\nALTER TABLE AUTHORITIES ADD CONSTRAINT AUTHORITIES_UNIQUE UNIQUE (USERNAME, AUTHORITY);\nALTER TABLE AUTHORITIES ADD CONSTRAINT AUTHORITIES_FK1 FOREIGN KEY (USERNAME) REFERENCES USERS (USERNAME) ENABLE;\n----\n\n[[servlet-authentication-jdbc-schema-group]]\n=== Group Schema\n\nIf your application uses groups, you need to provide the groups schema:\n\n.Default Group Schema\n[source,sql]\n----\ncreate table groups (\n\tid bigint generated by default as identity(start with 0) primary key,\n\tgroup_name varchar_ignorecase(50) not null\n);\n\ncreate table group_authorities (\n\tgroup_id bigint not null,\n\tauthority varchar(50) not null,\n\tconstraint fk_group_authorities_group foreign key(group_id) references groups(id)\n);\n\ncreate table group_members (\n\tid bigint generated by default as identity(start with 0) primary key,\n\tusername varchar(50) not null,\n\tgroup_id bigint not null,\n\tconstraint fk_group_members_group foreign key(group_id) references groups(id)\n);\n----\n\n[[servlet-authentication-jdbc-datasource]]\n== Setting up a DataSource\n\nBefore we configure `JdbcUserDetailsManager`, we must create a `DataSource`.\nIn our example, we set up an {spring-framework-reference-url}data-access/jdbc/embedded-database-support.html[embedded DataSource] that is initialized with the <<servlet-authentication-jdbc-schema,default user schema>>.\n\n.Embedded Data Source\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nDataSource dataSource() {\n\treturn new EmbeddedDatabaseBuilder()\n\t\t.setType(H2)\n\t\t.addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)\n\t\t.build();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<jdbc:embedded-database>\n\t<jdbc:script location=\"classpath:org/springframework/security/core/userdetails/jdbc/users.ddl\"/>\n</jdbc:embedded-database>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun dataSource(): DataSource {\n    return EmbeddedDatabaseBuilder()\n        .setType(H2)\n        .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)\n        .build()\n}\n----\n======\n\nIn a production environment, you want to ensure that you set up a connection to an external database.\n\n[[servlet-authentication-jdbc-bean]]\n== JdbcUserDetailsManager Bean\n\nIn this sample, we use xref:features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode a password value of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`.\nSee the xref:features/authentication/password-storage.adoc#authentication-password-storage[PasswordEncoder] section for more details about how to store passwords.\n\n.JdbcUserDetailsManager\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Bean\nUserDetailsManager users(DataSource dataSource) {\n\tUserDetails user = User.builder()\n\t\t.username(\"user\")\n\t\t.password(\"{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\")\n\t\t.roles(\"USER\")\n\t\t.build();\n\tUserDetails admin = User.builder()\n\t\t.username(\"admin\")\n\t\t.password(\"{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\")\n\t\t.roles(\"USER\", \"ADMIN\")\n\t\t.build();\n\tJdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);\n\tusers.createUser(user);\n\tusers.createUser(admin);\n\treturn users;\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\",attrs=\"-attributes\"]\n----\n<jdbc-user-service>\n\t<user name=\"user\"\n\t\tpassword=\"{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\"\n\t\tauthorities=\"ROLE_USER\" />\n\t<user name=\"admin\"\n\t\tpassword=\"{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\"\n\t\tauthorities=\"ROLE_USER,ROLE_ADMIN\" />\n</jdbc-user-service>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Bean\nfun users(dataSource: DataSource): UserDetailsManager {\n    val user = User.builder()\n            .username(\"user\")\n            .password(\"{bcrypt}$2a$10\\$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\")\n            .roles(\"USER\")\n            .build();\n    val admin = User.builder()\n            .username(\"admin\")\n            .password(\"{bcrypt}$2a$10\\$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW\")\n            .roles(\"USER\", \"ADMIN\")\n            .build();\n    val users = JdbcUserDetailsManager(dataSource)\n    users.createUser(user)\n    users.createUser(admin)\n    return users\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/ldap.adoc",
    "content": "[[servlet-authentication-ldap]]\n= LDAP Authentication\n\nLDAP (Lightweight Directory Access Protocol) is often used by organizations as a central repository for user information and as an authentication service.\nIt can also be used to store the role information for application users.\n\nSpring Security's LDAP-based authentication is used by Spring Security when it is configured to  xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication.\nHowever, despite using a username and password for authentication, it does not use `UserDetailsService`, because, in <<servlet-authentication-ldap-bind,bind authentication>>, the LDAP server does not return the password, so the application cannot perform validation of the password.\n\nThere are many different scenarios for how an LDAP server can be configured, so Spring Security's LDAP provider is fully configurable.\nIt uses separate strategy interfaces for authentication and role retrieval and provides default implementations, which can be configured to handle a wide range of situations.\n\n[[servlet-authentication-ldap-required-dependencies]]\n== Required Dependencies\n\nTo get started, add the `spring-security-ldap` dependency to your project.\nWhen using Spring Boot, add the following dependencies:\n\n.Spring Security LDAP Dependencies\n[tabs]\n======\nMaven::\n+\n[source,xml,role=\"primary\"]\n----\n<dependency>\n    <groupId>org.springframework.boot</groupId>\n    <artifactId>spring-boot-starter-data-ldap</artifactId>\n</dependency>\n\n<dependency>\n    <groupId>org.springframework.security</groupId>\n    <artifactId>spring-security-ldap</artifactId>\n</dependency>\n----\n\nGradle::\n+\n[source,groovy,role=\"secondary\"]\n----\ndependencies {\n    implementation \"org.springframework.boot:spring-boot-starter-data-ldap\"\n    implementation \"org.springframework.security:spring-security-ldap\"\n}\n----\n======\n\n[[servlet-authentication-ldap-prerequisites]]\n== Prerequisites\n\nYou should be familiar with LDAP before trying to use it with Spring Security.\nThe following link provides a good introduction to the concepts involved and a guide to setting up a directory using the free LDAP server, OpenLDAP: https://www.zytrax.com/books/ldap/.\nSome familiarity with the JNDI APIs used to access LDAP from Java can also be useful.\nWe do not use any third-party LDAP libraries (Mozilla, JLDAP, or others) in the LDAP provider, but extensive use is made of Spring LDAP, so some familiarity with that project may be useful if you plan on adding your own customizations.\n\nWhen using LDAP authentication, you should ensure that you properly configure LDAP connection pooling.\nIf you are unfamiliar with how to do so, see the https://docs.oracle.com/javase/jndi/tutorial/ldap/connect/config.html[Java LDAP documentation].\n\n\n// FIXME:\n// ldap server\n//\tembedded (both java and xml)\n//\texternal\n// authentication\n//\tbind\n//\tpassword\n//\troles\n//\tsearch, etc (other APIs)\n\n[[servlet-authentication-ldap-embedded]]\n== Setting up an Embedded LDAP Server\n\nThe first thing you need to do is to ensure that you have an LDAP Server to which to point your configuration.\nFor simplicity, it is often best to start with an embedded LDAP Server.\nSpring Security supports using either:\n\n* <<servlet-authentication-ldap-unboundid>>\n* <<servlet-authentication-ldap-apacheds>>\n\nIn the following samples, we expose `users.ldif` as a classpath resource to initialize the embedded LDAP server with two users, `user` and `admin`, both of which have a password of `password`:\n\n.users.ldif\n[source,ldif]\n----\ndn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: uid=admin,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Rod Johnson\nsn: Johnson\nuid: admin\nuserPassword: password\n\ndn: uid=user,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Dianne Emu\nsn: Emu\nuid: user\nuserPassword: password\n\ndn: cn=user,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: user\nmember: uid=admin,ou=people,dc=springframework,dc=org\nmember: uid=user,ou=people,dc=springframework,dc=org\n\ndn: cn=admin,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: admin\nmember: uid=admin,ou=people,dc=springframework,dc=org\n----\n\n[[servlet-authentication-ldap-unboundid]]\n=== Embedded UnboundID Server\n\nIf you wish to use https://ldap.com/unboundid-ldap-sdk-for-java/[UnboundID], specify the following dependencies:\n\n.UnboundID Dependencies\n[tabs]\n======\nMaven::\n+\n[source,xml,role=\"primary\",subs=\"verbatim,attributes\"]\n----\n<dependency>\n\t<groupId>com.unboundid</groupId>\n\t<artifactId>unboundid-ldapsdk</artifactId>\n\t<version>{unboundid-ldapsdk-version}</version>\n\t<scope>runtime</scope>\n</dependency>\n----\n\nGradle::\n+\n[source,groovy,role=\"secondary\",subs=\"verbatim,attributes\"]\n----\ndependencies {\n\truntimeOnly \"com.unboundid:unboundid-ldapsdk:{unboundid-ldapsdk-version}\"\n}\n----\n======\n\nYou can then configure the Embedded LDAP Server using an `EmbeddedLdapServerContextSourceFactoryBean`.\nThis will instruct Spring Security to start an in-memory LDAP server:\n\n.Embedded LDAP Server Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {\n\treturn EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun contextSourceFactoryBean(): EmbeddedLdapServerContextSourceFactoryBean {\n    return EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer()\n}\n----\n======\n\nAlternatively, you can manually configure the Embedded LDAP Server.\nIf you choose this approach, you will be responsible for managing the lifecycle of the Embedded LDAP Server.\n\n.Explicit Embedded LDAP Server Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nUnboundIdContainer ldapContainer() {\n\treturn new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\"classpath:users.ldif\");\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<b:bean class=\"org.springframework.security.ldap.server.UnboundIdContainer\"\n\tc:defaultPartitionSuffix=\"dc=springframework,dc=org\"\n\tc:ldif=\"classpath:users.ldif\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun ldapContainer(): UnboundIdContainer {\n    return UnboundIdContainer(\"dc=springframework,dc=org\",\"classpath:users.ldif\")\n}\n----\n======\n\n[[servlet-authentication-ldap-apacheds]]\n=== Embedded ApacheDS Server\n\nSpring Security 7 removes support for Apache DS.\nPlease use <<servlet-authentication-ldap-unboundid,UnboundID>> instead.\n\n[[servlet-authentication-ldap-contextsource]]\n== LDAP ContextSource\n\nOnce you have an LDAP Server to which to point your configuration, you need to configure Spring Security to point to an LDAP server that should be used to authenticate users.\nTo do so, create an LDAP `ContextSource` (which is the equivalent of a JDBC `DataSource`).\nIf you have already configured an `EmbeddedLdapServerContextSourceFactoryBean`, Spring Security will create an LDAP `ContextSource` that points to the embedded LDAP server.\n\n.LDAP Context Source with Embedded LDAP Server\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {\n\tEmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean =\n\t\t\tEmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();\n\tcontextSourceFactoryBean.setPort(0);\n\treturn contextSourceFactoryBean;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun contextSourceFactoryBean(): EmbeddedLdapServerContextSourceFactoryBean {\n    val contextSourceFactoryBean = EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer()\n    contextSourceFactoryBean.setPort(0)\n    return contextSourceFactoryBean\n}\n----\n======\n\nAlternatively, you can explicitly configure the LDAP `ContextSource` to connect to the supplied LDAP server:\n\n.LDAP Context Source\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nContextSource contextSource(UnboundIdContainer container) {\n\treturn new DefaultSpringSecurityContextSource(\"ldap://localhost:53389/dc=springframework,dc=org\");\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<ldap-server\n\turl=\"ldap://localhost:53389/dc=springframework,dc=org\" />\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nfun contextSource(container: UnboundIdContainer): ContextSource {\n    return DefaultSpringSecurityContextSource(\"ldap://localhost:53389/dc=springframework,dc=org\")\n}\n----\n======\n\n[[servlet-authentication-ldap-authentication]]\n== Authentication\n\nSpring Security's LDAP support does not use the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] because LDAP bind authentication does not let clients read the password or even a hashed version of the password.\nThis means there is no way for a password to be read and then authenticated by Spring Security.\n\nFor this reason, LDAP support is implemented through the `LdapAuthenticator` interface.\nThe `LdapAuthenticator` interface is also responsible for retrieving any required user attributes.\nThis is because the permissions on the attributes may depend on the type of authentication being used.\nFor example, if binding as the user, it may be necessary to read the attributes with the user's own permissions.\n\nSpring Security supplies two `LdapAuthenticator` implementations:\n\n* <<servlet-authentication-ldap-bind>>\n* <<servlet-authentication-ldap-pwd>>\n\n[[servlet-authentication-ldap-bind]]\n== Using Bind Authentication\n\nhttps://ldap.com/the-ldap-bind-operation/[Bind Authentication] is the most common mechanism for authenticating users with LDAP.\nIn bind authentication, the user's credentials (username and password) are submitted to the LDAP server, which authenticates them.\nThe advantage to using bind authentication is that the user's secrets (the password) do not need to be exposed to clients, which helps to protect them from leaking.\n\nThe following example shows bind authentication configuration:\n\n.Bind Authentication\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Bean\nAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\treturn factory.createAuthenticationManager();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\",attrs=\"-attributes\"]\n----\n<ldap-authentication-provider\n\tuser-dn-pattern=\"uid={0},ou=people\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Bean\nfun authenticationManager(contextSource: BaseLdapPathContextSource): AuthenticationManager {\n    val factory = LdapBindAuthenticationManagerFactory(contextSource)\n    factory.setUserDnPatterns(\"uid={0},ou=people\")\n    return factory.createAuthenticationManager()\n}\n----\n======\n\nThe preceding simple example would obtain the DN for the user by substituting the user login name in the supplied pattern and attempting to bind as that user with the login password.\nThis is OK if all your users are stored under a single node in the directory.\nIf, instead, you wish to configure an LDAP search filter to locate the user, you could use the following:\n\n.Bind Authentication with Search Filter\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Bean\nAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\tfactory.setUserSearchFilter(\"(uid={0})\");\n\tfactory.setUserSearchBase(\"ou=people\");\n\treturn factory.createAuthenticationManager();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\",attrs=\"-attributes\"]\n----\n<ldap-authentication-provider\n\t\tuser-search-filter=\"(uid={0})\"\n\tuser-search-base=\"ou=people\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Bean\nfun authenticationManager(contextSource: BaseLdapPathContextSource): AuthenticationManager {\n    val factory = LdapBindAuthenticationManagerFactory(contextSource)\n    factory.setUserSearchFilter(\"(uid={0})\")\n    factory.setUserSearchBase(\"ou=people\")\n    return factory.createAuthenticationManager()\n}\n----\n======\n\nIf used with the `ContextSource` <<servlet-authentication-ldap-contextsource,definition shown earlier>>, this would perform a search under the DN `ou=people,dc=springframework,dc=org` by using `+(uid={0})+` as a filter.\nAgain, the user login name is substituted for the parameter in the filter name, so it searches for an entry with the `uid` attribute equal to the user name.\nIf a user search base is not supplied, the search is performed from the root.\n\n[[servlet-authentication-ldap-pwd]]\n== Using Password Authentication\n\nPassword comparison is when the password supplied by the user is compared with the one stored in the repository.\nThis can either be done by retrieving the value of the password attribute and checking it locally or by performing an LDAP \"`compare`\" operation, where the supplied password is passed to the server for comparison and the real password value is never retrieved.\nAn LDAP compare cannot be done when the password is properly hashed with a random salt.\n\n.Minimal Password Compare Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\tLdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(\n\t\t\tcontextSource, NoOpPasswordEncoder.getInstance());\n\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\treturn factory.createAuthenticationManager();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\",attrs=\"-attributes\"]\n----\n<ldap-authentication-provider\n\t\tuser-dn-pattern=\"uid={0},ou=people\">\n\t<password-compare />\n</ldap-authentication-provider>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationManager(contextSource: BaseLdapPathContextSource?): AuthenticationManager? {\n    val factory = LdapPasswordComparisonAuthenticationManagerFactory(\n        contextSource, NoOpPasswordEncoder.getInstance()\n    )\n    factory.setUserDnPatterns(\"uid={0},ou=people\")\n    return factory.createAuthenticationManager()\n}\n----\n======\n\nThe following example shows a more advanced configuration with some customizations:\n\n.Password Compare Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {\n\tLdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(\n\t\t\tcontextSource, new BCryptPasswordEncoder());\n\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\tfactory.setPasswordAttribute(\"pwd\");  // <1>\n\treturn factory.createAuthenticationManager();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\",attrs=\"-attributes\"]\n----\n<ldap-authentication-provider\n\t\tuser-dn-pattern=\"uid={0},ou=people\">\n\t<password-compare password-attribute=\"pwd\"> <!--1-->\n\t\t<password-encoder ref=\"passwordEncoder\" /> <!--2-->\n\t</password-compare>\n</ldap-authentication-provider>\n<b:bean id=\"passwordEncoder\"\n\tclass=\"org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder\" />\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationManager(contextSource: BaseLdapPathContextSource): AuthenticationManager {\n    val factory = LdapPasswordComparisonAuthenticationManagerFactory(\n        contextSource, BCryptPasswordEncoder()\n    )\n    factory.setUserDnPatterns(\"uid={0},ou=people\")\n    factory.setPasswordAttribute(\"pwd\") // <1>\n    return factory.createAuthenticationManager()\n}\n----\n======\n\n<1> Specify the password attribute as `pwd`.\n\n== LdapAuthoritiesPopulator\n\nSpring Security's `LdapAuthoritiesPopulator` is used to determine what authorities are returned for the user.\nThe following example shows how configure `LdapAuthoritiesPopulator`:\n\n.LdapAuthoritiesPopulator Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Bean\nLdapAuthoritiesPopulator authorities(BaseLdapPathContextSource contextSource) {\n\tString groupSearchBase = \"\";\n\tDefaultLdapAuthoritiesPopulator authorities =\n\t\tnew DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);\n\tauthorities.setGroupSearchFilter(\"member={0}\");\n\treturn authorities;\n}\n\n@Bean\nAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource, LdapAuthoritiesPopulator authorities) {\n\tLdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);\n\tfactory.setUserDnPatterns(\"uid={0},ou=people\");\n\tfactory.setLdapAuthoritiesPopulator(authorities);\n\treturn factory.createAuthenticationManager();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\",attrs=\"-attributes\"]\n----\n<ldap-authentication-provider\n\tuser-dn-pattern=\"uid={0},ou=people\"\n\tgroup-search-filter=\"member={0}\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Bean\nfun authorities(contextSource: BaseLdapPathContextSource): LdapAuthoritiesPopulator {\n    val groupSearchBase = \"\"\n    val authorities = DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase)\n    authorities.setGroupSearchFilter(\"member={0}\")\n    return authorities\n}\n\n@Bean\nfun authenticationManager(\n    contextSource: BaseLdapPathContextSource,\n    authorities: LdapAuthoritiesPopulator): AuthenticationManager {\n    val factory = LdapBindAuthenticationManagerFactory(contextSource)\n    factory.setUserDnPatterns(\"uid={0},ou=people\")\n    factory.setLdapAuthoritiesPopulator(authorities)\n    return factory.createAuthenticationManager()\n}\n----\n======\n\n== Active Directory\n\nActive Directory supports its own non-standard authentication options, and the normal usage pattern does not fit too cleanly with the standard `LdapAuthenticationProvider`.\nTypically, authentication is performed by using the domain username (in the form of `user@domain`), rather than using an LDAP distinguished name.\nTo make this easier, Spring Security has an authentication provider, which is customized for a typical Active Directory setup.\n\nConfiguring `ActiveDirectoryLdapAuthenticationProvider` is quite straightforward.\nYou need only supply the domain name and an LDAP URL that supplies the address of the server.\n\n[NOTE]\n====\nIt is also possible to obtain the server's IP address by using a DNS lookup.\nThis is not currently supported, but hopefully will be in a future version.\n====\n\nThe following example configures Active Directory:\n\n.Example Active Directory Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nActiveDirectoryLdapAuthenticationProvider authenticationProvider() {\n\treturn new ActiveDirectoryLdapAuthenticationProvider(\"example.com\", \"ldap://company.example.com/\");\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<bean id=\"authenticationProvider\"\n        class=\"org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider\">\n\t<constructor-arg value=\"example.com\" />\n\t<constructor-arg value=\"ldap://company.example.com/\" />\n</bean>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationProvider(): ActiveDirectoryLdapAuthenticationProvider {\n    return ActiveDirectoryLdapAuthenticationProvider(\"example.com\", \"ldap://company.example.com/\")\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/password-encoder.adoc",
    "content": "[[servlet-authentication-password-storage]]\n= PasswordEncoder\n\nSpring Security's servlet support includes storing passwords securely by integrating with xref:features/authentication/password-storage.adoc#authentication-password-storage[`PasswordEncoder`].\nYou can customize the `PasswordEncoder` implementation used by Spring Security by xref:features/authentication/password-storage.adoc#authentication-password-storage-configuration[exposing a `PasswordEncoder` Bean].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/storage.adoc",
    "content": "[[servlet-authentication-unpwd-storage]]\n= Storage Mechanisms\n:page-section-summary-toc: 1\n\nEach of the supported mechanisms for reading a username and password can use any of the supported storage mechanisms:\n\n* Simple Storage with xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[In-Memory Authentication]\n* Relational Databases with xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc[JDBC Authentication]\n* Custom data stores with xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService]\n* LDAP storage with xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap[LDAP Authentication]\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc",
    "content": "[[servlet-authentication-userdetailsservice]]\n= UserDetailsService\n\njavadoc:org.springframework.security.core.userdetails.UserDetailsService[] is used by xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] for retrieving a username, a password, and other attributes for authenticating with a username and password.\nSpring Security provides xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory], xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc[JDBC], and xref:servlet/authentication/passwords/caching.adoc#servlet-authentication-caching-user-details[caching] implementations of `UserDetailsService`.\n\nYou can define custom authentication by exposing a custom `UserDetailsService` as a bean.\nFor example, the following listing customizes authentication, assuming that `CustomUserDetailsService` implements `UserDetailsService`:\n\n[NOTE]\n====\nThis is only used if the `AuthenticationManagerBuilder` has not been populated and no `AuthenticationProviderBean` is defined.\n====\n\n.Custom UserDetailsService Bean\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nCustomUserDetailsService customUserDetailsService() {\n\treturn new CustomUserDetailsService();\n}\n----\n\nXML::\n+\n[source,java,role=\"secondary\"]\n----\n<b:bean class=\"example.CustomUserDetailsService\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun customUserDetailsService() = CustomUserDetailsService()\n----\n======\n\n// FIXME: Add CustomUserDetails example with links to @AuthenticationPrincipal\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/passwords/user-details.adoc",
    "content": "[[servlet-authentication-userdetails]]\n= UserDetails\n\njavadoc:org.springframework.security.core.userdetails.UserDetails[] is returned by the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`].\nThe xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] validates the `UserDetails` and then returns an xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that has a principal that is the `UserDetails` returned by the configured `UserDetailsService`.\n\n== Credentials Management\n\nImplementing the `CredentialsContainer` interface in classes that store user credentials, such as those extending or implementing `UserDetails`, is strongly recommended, especially in applications where user details are not cached.\nThis practice enhances security by ensuring that sensitive data, such as passwords, are not retained in memory longer than necessary.\n\n[TIP]\n====\nIn cases where user details are cached, consider creating a copy of the `UserDetails` that does not include credentials and returning the copy in the response from a custom `AuthenticationProvider` instead of the original object.\nThis can help prevent the cached instance containing credentials from being referenced by the rest of the application once the authentication process is complete.\n====\n\n=== When to Implement `CredentialsContainer`\n\nApplications that do not employ caching mechanisms for `UserDetails` should particularly consider implementing `CredentialsContainer`.\nThis approach helps in mitigating the risk associated with retaining sensitive information in memory, which can be vulnerable to attack vectors such as memory dumps.\n\n[source,java]\n----\npublic class MyUserDetails implements UserDetails, CredentialsContainer {\n\n    private String username;\n\n    private String password;\n\n    // UserDetails implementation...\n\n    @Override\n    public void eraseCredentials() {\n        this.password = null; // Securely dereference the password field\n    }\n\n}\n----\n\n=== Implementation Guidelines\n\n* *Immediate Erasure*: Credentials should be erased immediately after they are no longer needed, typically post-authentication.\n* *Automatic Invocation*: Ensure that `eraseCredentials()` is automatically called by your authentication framework, such as `AuthenticationManager`, once the authentication process is complete.\n* *Consistency*: Apply this practice uniformly across all applications to prevent security lapses that could lead to data breaches.\n\n=== Beyond Basic Interface Implementation\n\nWhile interfaces like `CredentialsContainer` provide a framework for credential management, the practical implementation often depends on specific classes and their interactions.\n\nFor example, the `DaoAuthenticationProvider` class, adhering to the contract of `AuthenticationProvider`, does not perform credential erasure within its own `authenticate` method.\nInstead, it relies on `ProviderManager`—Spring Security's default implementation of `AuthenticationManager`—to handle the erasure of credentials and other sensitive data post-authentication.\nThis separation emphasizes the principle that the `AuthenticationProvider` should not assume the responsibility for credentials management.\n\nIncorporating `CredentialsContainer` into your `UserDetails` implementation aligns with security best practices, reducing potential exposure to data breaches by minimizing the lifespan of sensitive data in memory.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/persistence.adoc",
    "content": "[[persistant]]\n= Persisting Authentication\n:figures: servlet/authentication\n\nThe first time a user requests a protected resource, they are xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[prompted for credentials].\nOne of the most common ways to prompt for credentials is to redirect the user to a xref:servlet/authentication/passwords/form.adoc[log in page].\nA summarized HTTP exchange for an unauthenticated user requesting a protected resource might look like this:\n\n.Unauthenticated User Requests Protected Resource\n====\n[source,http]\n----\nGET / HTTP/1.1\nHost: example.com\nCookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b\n----\n\n[source,http]\n----\nHTTP/1.1 302 Found\nLocation: /login\n----\n====\n\nThe user submits their username and password.\n\n.Username and Password Submitted\n[source,http]\n----\nPOST /login HTTP/1.1\nHost: example.com\nCookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b\n\nusername=user&password=password&_csrf=35942e65-a172-4cd4-a1d4-d16a51147b3e\n----\n\nUpon authenticating the user, the user is associated to a new session id to prevent xref:servlet/authentication/session-management.adoc#ns-session-fixation[session fixation attacks].\n\n.Authenticated User is Associated to New Session\n[source,http]\n----\nHTTP/1.1 302 Found\nLocation: /\nSet-Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8; Path=/; HttpOnly; SameSite=Lax\n----\n\nSubsequent requests include the session cookie which is used to authenticate the user for the remainder of the session.\n\n.Authenticated Session Provided as Credentials\n[source,http]\n----\nGET / HTTP/1.1\nHost: example.com\nCookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8\n----\n\n\n[[securitycontextrepository]]\n== SecurityContextRepository\n\n// FIXME: api documentation\nIn Spring Security the association of the user to future requests is made using  javadoc:org.springframework.security.web.context.SecurityContextRepository[].\nThe default implementation of `SecurityContextRepository` is javadoc:org.springframework.security.web.context.DelegatingSecurityContextRepository[] which delegates to the following:\n\n* <<httpsecuritycontextrepository,`HttpSessionSecurityContextRepository`>>\n* <<requestattributesecuritycontextrepository,`RequestAttributeSecurityContextRepository`>>\n\n[[httpsecuritycontextrepository]]\n=== HttpSessionSecurityContextRepository\n\nThe javadoc:org.springframework.security.web.context.HttpSessionSecurityContextRepository[] associates the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to the `HttpSession`.\nUsers can replace `HttpSessionSecurityContextRepository` with another implementation of `SecurityContextRepository` if they wish to associate the user with subsequent requests in another way or not at all.\n\n[[nullsecuritycontextrepository]]\n=== NullSecurityContextRepository\n\nIf it is not desirable to associate the `SecurityContext` to an `HttpSession` (i.e. when authenticating with OAuth) the javadoc:org.springframework.security.web.context.NullSecurityContextRepository[] is an implementation of `SecurityContextRepository` that does nothing.\n\n[[requestattributesecuritycontextrepository]]\n=== RequestAttributeSecurityContextRepository\n\nThe javadoc:org.springframework.security.web.context.RequestAttributeSecurityContextRepository[] saves the `SecurityContext` as a request attribute to make sure the `SecurityContext` is available for a single request that occurs across dispatch types that may clear out the `SecurityContext`.\n\nFor example, assume that a client makes a request, is authenticated, and then an error occurs.\nDepending on the servlet container implementation, the error means that any `SecurityContext` that was established is cleared out and then the error dispatch is made.\nWhen the error dispatch is made, there is no `SecurityContext` established.\nThis means that the error page cannot use the `SecurityContext` for authorization or displaying the current user unless the `SecurityContext` is persisted somehow.\n\n.Use RequestAttributeSecurityContextRepository\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.securityContext((securityContext) -> securityContext\n\t\t\t.securityContextRepository(new RequestAttributeSecurityContextRepository())\n\t\t);\n\treturn http.build();\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http security-context-repository-ref=\"contextRepository\">\n\t<!-- ... -->\n</http>\n<b:bean name=\"contextRepository\"\n\tclass=\"org.springframework.security.web.context.RequestAttributeSecurityContextRepository\" />\n----\n======\n\n[[delegatingsecuritycontextrepository]]\n=== DelegatingSecurityContextRepository\n\nThe javadoc:org.springframework.security.web.context.DelegatingSecurityContextRepository[] saves the `SecurityContext` to multiple `SecurityContextRepository` delegates and allows retrieval from any of the delegates in a specified order.\n\nThe most useful arrangement for this is configured with the following example, which allows the use of both xref:requestattributesecuritycontextrepository[`RequestAttributeSecurityContextRepository`] and xref:httpsecuritycontextrepository[`HttpSessionSecurityContextRepository`] simultaneously.\n\n.Configure DelegatingSecurityContextRepository\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t// ...\n\t\t.securityContext((securityContext) -> securityContext\n\t\t\t.securityContextRepository(new DelegatingSecurityContextRepository(\n\t\t\t\tnew RequestAttributeSecurityContextRepository(),\n\t\t\t\tnew HttpSessionSecurityContextRepository()\n\t\t\t))\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\thttp {\n\t\t// ...\n\t\tsecurityContext {\n\t\t\tsecurityContextRepository = DelegatingSecurityContextRepository(\n\t\t\t\tRequestAttributeSecurityContextRepository(),\n\t\t\t\tHttpSessionSecurityContextRepository()\n\t\t\t)\n\t\t}\n\t}\n\treturn http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http security-context-repository-ref=\"contextRepository\">\n\t<!-- ... -->\n</http>\n<bean name=\"contextRepository\"\n\tclass=\"org.springframework.security.web.context.DelegatingSecurityContextRepository\">\n\t\t<constructor-arg>\n\t\t\t<bean class=\"org.springframework.security.web.context.RequestAttributeSecurityContextRepository\" />\n\t\t</constructor-arg>\n\t\t<constructor-arg>\n\t\t\t<bean class=\"org.springframework.security.web.context.HttpSessionSecurityContextRepository\" />\n\t\t</constructor-arg>\n</bean>\n----\n======\n\n[NOTE]\n====\nIn Spring Security 6, the example shown above is the default configuration.\n====\n\n[[securitycontextpersistencefilter]]\n== SecurityContextPersistenceFilter\n\nThe javadoc:org.springframework.security.web.context.SecurityContextPersistenceFilter[] is responsible for persisting the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].\n\n[.invert-dark]\nimage::{figures}/securitycontextpersistencefilter.png[]\n\nimage:{icondir}/number_1.png[] Before running the rest of the application, `SecurityContextPersistenceFilter` loads the `SecurityContext` from the `SecurityContextRepository` and sets it on the `SecurityContextHolder`.\n\nimage:{icondir}/number_2.png[] Next, the application is run.\n\nimage:{icondir}/number_3.png[] Finally, if the `SecurityContext` has changed, we save the `SecurityContext` using the `SecurityContextRepository`.\nThis means that when using `SecurityContextPersistenceFilter`, just setting the `SecurityContextHolder` will ensure that the `SecurityContext` is persisted using `SecurityContextRepository`.\n\nIn some cases a response is committed and written to the client before the `SecurityContextPersistenceFilter` method completes.\nFor example, if a redirect is sent to the client the response is immediately written back to the client.\nThis means that establishing an `HttpSession` would not be possible in step 3 because the session id could not be included in the already written response.\nAnother situation that can happen is that if a client authenticates successfully, the response is committed before `SecurityContextPersistenceFilter` completes, and the client makes a second request before the `SecurityContextPersistenceFilter` completes. the wrong authentication could be present in the second request.\n\n\nTo avoid these problems, the `SecurityContextPersistenceFilter` wraps both the `HttpServletRequest` and the `HttpServletResponse` to detect if the `SecurityContext` has changed and if so save the `SecurityContext` just before the response is committed.\n\n[[securitycontextholderfilter]]\n== SecurityContextHolderFilter\n\nThe javadoc:org.springframework.security.web.context.SecurityContextHolderFilter[] is responsible for loading the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].\n\n[.invert-dark]\nimage::{figures}/securitycontextholderfilter.png[]\n\nimage:{icondir}/number_1.png[] Before running the rest of the application, `SecurityContextHolderFilter` loads the `SecurityContext` from the `SecurityContextRepository` and sets it on the `SecurityContextHolder`.\n\nimage:{icondir}/number_2.png[] Next, the application is run.\n\nUnlike, xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[`SecurityContextPersistenceFilter`], `SecurityContextHolderFilter` only loads the `SecurityContext` it does not save the `SecurityContext`.\nThis means that when using `SecurityContextHolderFilter`, it is required that the `SecurityContext` is explicitly saved.\n\n\ninclude::partial$servlet/architecture/security-context-explicit.adoc[]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/preauth.adoc",
    "content": "[[servlet-preauth]]\n= Pre-Authentication Scenarios\nThere are situations where you want to use Spring Security for authorization, but the user has already been reliably authenticated by some external system prior to accessing the application.\nWe refer to these situations as \"`pre-authenticated`\" scenarios.\nExamples include X.509, Siteminder, and authentication by the Java EE container in which the application runs.\nWhen using pre-authentication, Spring Security has to:\n\n* Identify the user making the request.\n* Obtain the authorities for the user.\n\nThe details depend on the external authentication mechanism.\nA user might be identified by their certificate information in the case of X.509, or by an HTTP request header in the case of Siteminder.\nIf relying on container authentication, the user is identified by calling the `getUserPrincipal()` method on the incoming HTTP request.\nIn some cases, the external mechanism may supply role and authority information for the user. However, in other cases, you must obtain the authorities from a separate source, such as a `UserDetailsService`.\n\n== Pre-Authentication Framework Classes\nBecause most pre-authentication mechanisms follow the same pattern, Spring Security has a set of classes that provide an internal framework for implementing pre-authenticated authentication providers.\nThis removes duplication and lets new implementations be added in a structured fashion, without having to write everything from scratch.\nYou need not know about these classes if you want to use something like xref:servlet/authentication/x509.adoc#servlet-x509[X.509 authentication], as it already has a namespace configuration option which is simpler to use and get started with.\nIf you need to use explicit bean configuration or are planning on writing your own implementation, you need an understanding of how the provided implementations work.\nYou can find the classes under the `org.springframework.security.web.authentication.preauth`.\nWe provide only an outline here, so you should consult the Javadoc and source where appropriate.\n\n=== AbstractPreAuthenticatedProcessingFilter\nThis class checks the current contents of the security context and, if it is empty, tries to extract user information from the HTTP request and submit it to the `AuthenticationManager`.\nSubclasses override the following methods to obtain this information.\n\n.Override AbstractPreAuthenticatedProcessingFilter\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprotected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);\n\nprotected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nprotected abstract fun getPreAuthenticatedPrincipal(request: HttpServletRequest): Any?\n\nprotected abstract fun getPreAuthenticatedCredentials(request: HttpServletRequest): Any?\n----\n======\n\n\nAfter calling these, the filter creates a `PreAuthenticatedAuthenticationToken` that contains the returned data and submits it for authentication.\nBy \"`authentication`\" here, we really just mean further processing to perhaps load the user's authorities, but the standard Spring Security authentication architecture is followed.\n\nAs other Spring Security authentication filters, the pre-authentication filter has an `authenticationDetailsSource` property, which, by default, creates a `WebAuthenticationDetails` object to store additional information, such as the session identifier and the originating IP address in the `details` property of the `Authentication` object.\nIn cases where user role information can be obtained from the pre-authentication mechanism, the data is also stored in this property, with the details implementing the `GrantedAuthoritiesContainer` interface.\nThis enables the authentication provider to read the authorities which were externally allocated to the user.\nWe look at a concrete example next.\n\n\n[[j2ee-preauth-details]]\n==== J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource\nIf the filter is configured with an `authenticationDetailsSource`, which is an instance of this class, the authority information is obtained by calling the `isUserInRole(String role)` method for each of a pre-determined set of \"`mappable roles`\".\nThe class gets these from a configured `MappableAttributesRetriever`.\nPossible implementations include hard-coding a list in the application context and reading the role information from the `<security-role>` information in a `web.xml` file.\nThe pre-authentication sample application uses the latter approach.\n\nThere is an additional stage where the roles (or attributes) are mapped to Spring Security `GrantedAuthority` objects by using a configured `Attributes2GrantedAuthoritiesMapper`.\nThe default just adds the usual `ROLE_` prefix to the names, but it gives you full control over the behavior.\n\n\n=== PreAuthenticatedAuthenticationProvider\nThe pre-authenticated provider has little more to do than load the `UserDetails` object for the user.\nIt does this by delegating to an `AuthenticationUserDetailsService`.\nThe latter is similar to the standard `UserDetailsService` but takes an `Authentication` object rather than just user name:\n\n[source,java]\n----\npublic interface AuthenticationUserDetailsService {\n\tUserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;\n}\n----\n\nThis interface may also have other uses, but, with pre-authentication, it allows access to the authorities that were packaged in the `Authentication` object, as we saw in the previous section.\nThe `PreAuthenticatedGrantedAuthoritiesUserDetailsService` class does this.\nAlternatively, it may delegate to a standard `UserDetailsService` through the `UserDetailsByNameServiceWrapper` implementation.\n\n=== Http403ForbiddenEntryPoint\nThe xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is responsible for kick-starting the authentication process for an unauthenticated user (when they try to access a protected resource). However, in the pre-authenticated case, this does not apply.\nYou would only configure the `ExceptionTranslationFilter` with an instance of this class if you do not use pre-authentication in combination with other authentication mechanisms.\nIt is called if the user is rejected by the `AbstractPreAuthenticatedProcessingFilter`, resulting in a null authentication.\nIt always returns a `403`-forbidden response code if called.\n\n\n== Concrete Implementations\nX.509 authentication is covered in its xref:servlet/authentication/x509.adoc#servlet-x509[own chapter].\nHere, we look at some classes which provide support for other pre-authenticated scenarios.\n\n\n=== Request-Header Authentication (Siteminder)\nAn external authentication system may supply information to the application by setting specific headers on the HTTP request.\nA well-known example of this is Siteminder, which passes the username in a header called `SM_USER`.\nThis mechanism is supported by the `RequestHeaderAuthenticationFilter` class, which only extracts the username from the header.\nIt defaults to using a name of `SM_USER` as the header name.\nSee the Javadoc for more details.\n\n[TIP]\n====\nWhen using a system like this, the framework performs no authentication checks at all, and it is _extremely_ important that the external system is configured properly and protects all access to the application.\nIf an attacker is able to forge the headers in their original request without this being detected, they could potentially choose any username they wished.\n====\n\n==== Siteminder Example Configuration\nThe following example shows a typical configuration that uses this filter:\n\n[source,xml]\n----\n<security:http>\n<!-- Additional http configuration omitted -->\n<security:custom-filter position=\"PRE_AUTH_FILTER\" ref=\"siteminderFilter\" />\n</security:http>\n\n<bean id=\"siteminderFilter\" class=\"org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter\">\n<property name=\"principalRequestHeader\" value=\"SM_USER\"/>\n<property name=\"authenticationManager\" ref=\"authenticationManager\" />\n</bean>\n\n<bean id=\"preauthAuthProvider\" class=\"org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider\">\n<property name=\"preAuthenticatedUserDetailsService\">\n\t<bean id=\"userDetailsServiceWrapper\"\n\t\tclass=\"org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper\">\n\t<property name=\"userDetailsService\" ref=\"userDetailsService\"/>\n\t</bean>\n</property>\n</bean>\n\n<security:authentication-manager alias=\"authenticationManager\">\n<security:authentication-provider ref=\"preauthAuthProvider\" />\n</security:authentication-manager>\n----\n\nWe've assumed here that the xref:servlet/configuration/xml-namespace.adoc#ns-config[security namespace] is being used for configuration.\nIt's also assumed that you have added a `UserDetailsService` (called \"userDetailsService\") to your configuration to load the user's roles.\n\n\n=== Java EE Container Authentication\nThe `J2eePreAuthenticatedProcessingFilter` class extracts the username from the `userPrincipal` property of the `HttpServletRequest`.\nUse of this filter would usually be combined with the use of Java EE roles, as described earlier in <<j2ee-preauth-details>>.\n\nThere is a {gh-old-samples-url}/xml/preauth[sample application] that uses this approach in the codebase, so get hold of the code from Github and have a look at the application context file if you are interested.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc",
    "content": "[[servlet-rememberme]]\n= Remember-Me Authentication\n\n[[remember-me-overview]]\nRemember-me or persistent-login authentication refers to web sites being able to remember the identity of a principal between sessions.\nThis is typically accomplished by sending a cookie to the browser, with the cookie being detected during future sessions and causing automated login to take place.\nSpring Security provides the necessary hooks for these operations to take place and has two concrete remember-me implementations.\nOne uses hashing to preserve the security of cookie-based tokens and the other uses a database or other persistent storage mechanism to store the generated tokens.\n\nNote that both implementations require a `UserDetailsService`.\nIf you use an authentication provider that does not use a `UserDetailsService` (for example, the LDAP provider), it does not work unless you also have a `UserDetailsService` bean in your application context.\n\n\n[[remember-me-hash-token]]\n== Simple Hash-Based Token Approach\nThis approach uses hashing to achieve a useful remember-me strategy.\nIn essence, a cookie is sent to the browser upon successful interactive authentication, with the cookie being composed as follows:\n\n[source,txt]\n----\nbase64(username + \":\" + expirationTime + \":\" + algorithmName + \":\"\nalgorithmHex(username + \":\" + expirationTime + \":\" password + \":\" + key))\n\nusername:          As identifiable to the UserDetailsService\npassword:          That matches the one in the retrieved UserDetails\nexpirationTime:    The date and time when the remember-me token expires, expressed in milliseconds\nkey:               A private key to prevent modification of the remember-me token\nalgorithmName:     The algorithm used to generate and to verify the remember-me token signature\n----\n\nThe remember-me token is valid only for the period specified and only if the username, password, and key do not change.\nNotably, this has a potential security issue, in that a captured remember-me token is usable from any user agent until such time as the token expires.\nThis is the same issue as with digest authentication.\nIf a principal is aware that a token has been captured, they can easily change their password and immediately invalidate all remember-me tokens on issue.\nIf more significant security is needed, you should use the approach described in the next section.\nAlternatively, remember-me services should not be used at all.\n\nIf you are familiar with the topics discussed in the chapter on xref:servlet/configuration/xml-namespace.adoc#ns-config[namespace configuration], you can enable remember-me authentication by adding the `<remember-me>` element:\n\n[source,xml]\n----\n<http>\n...\n<remember-me key=\"myAppKey\"/>\n</http>\n----\n\nThe `UserDetailsService` is normally selected automatically.\nIf you have more than one in your application context, you need to specify which one should be used with the `user-service-ref` attribute, where the value is the name of your `UserDetailsService` bean.\n\n[[remember-me-persistent-token]]\n== Persistent Token Approach\nThis approach is based on the article https://web.archive.org/web/20180819014446/http://jaspan.com/improved_persistent_login_cookie_best_practice[Improved Persistent Login Cookie Best Practice] with some minor modifications  footnote:[Essentially, the username is not included in the cookie, to prevent exposing a valid login name unnecessarily.\nThere is a discussion on this in the comments section of this article.].\nTo use this approach with namespace configuration, you would supply a datasource reference:\n\n[source,xml]\n----\n<http>\n...\n<remember-me data-source-ref=\"someDataSource\"/>\n</http>\n----\n\nThe database should contain a `persistent_logins` table, created by using the following SQL (or equivalent):\n\n[source,ddl]\n----\ncreate table persistent_logins (username varchar(64) not null,\n\t\t\t\t\t\t\t\tseries varchar(64) primary key,\n\t\t\t\t\t\t\t\ttoken varchar(64) not null,\n\t\t\t\t\t\t\t\tlast_used timestamp not null)\n----\n\n[[remember-me-impls]]\n== Remember-Me Interfaces and Implementations\nRemember-me is used with `UsernamePasswordAuthenticationFilter` and is implemented through hooks in the `AbstractAuthenticationProcessingFilter` superclass.\nIt is also used within `BasicAuthenticationFilter`.\nThe hooks invoke a concrete `RememberMeServices` at the appropriate times.\nThe following listing shows the interface:\n\n[source,java]\n----\nAuthentication autoLogin(HttpServletRequest request, HttpServletResponse response);\n\nvoid loginFail(HttpServletRequest request, HttpServletResponse response);\n\nvoid loginSuccess(HttpServletRequest request, HttpServletResponse response,\n\tAuthentication successfulAuthentication);\n----\n\nSee the Javadoc for javadoc:org.springframework.security.web.authentication.RememberMeServices[] for a fuller discussion on what the methods do, although note that, at this stage, `AbstractAuthenticationProcessingFilter` calls only the `loginFail()` and `loginSuccess()` methods.\nThe `autoLogin()` method is called by `RememberMeAuthenticationFilter` whenever the `SecurityContextHolder` does not contain an `Authentication`.\nThis interface, therefore, provides the underlying remember-me implementation with sufficient notification of authentication-related events and delegates to the implementation whenever a candidate web request might contain a cookie and wish to be remembered.\nThis design allows any number of remember-me implementation strategies.\n\nWe have seen earlier that Spring Security provides two implementations.\nWe look at each of these in turn.\n\n[[token-based-remember-me-services]]\n=== TokenBasedRememberMeServices\nThis implementation supports the simpler approach described in <<remember-me-hash-token>>.\n`TokenBasedRememberMeServices` generates a `RememberMeAuthenticationToken`, which is processed by `RememberMeAuthenticationProvider`.\nA `key` is shared between this authentication provider and the `TokenBasedRememberMeServices`.\nIn addition, `TokenBasedRememberMeServices` requires a `UserDetailsService`, from which it can retrieve the username and password for signature comparison purposes and generate the `RememberMeAuthenticationToken` to contain the correct `GrantedAuthority` instances.\n`TokenBasedRememberMeServices` also implements Spring Security's `LogoutHandler` interface so that it can be used with `LogoutFilter` to have the cookie cleared automatically.\n\nBy default, this implementation uses the SHA-256 algorithm to encode the token signature.\nTo verify the token signature, the algorithm retrieved from `algorithmName` is parsed and used.\nIf no `algorithmName` is present, the default matching algorithm will be used, which is SHA-256.\nYou can specify different algorithms for signature encoding and for signature matching, this allows users to safely upgrade to a different encoding algorithm while still able to verify old ones if there is no `algorithmName` present.\nTo do that you can specify your customized `TokenBasedRememberMeServices` as a Bean and use it in the configuration.\n\ninclude-code::./CustomAlgorithmRememberMeServicesConfiguration[tag=snippet,indent=0]\n\nThe following beans are required in an application context to enable remember-me services:\n\ninclude-code::./DefaultAlgorithmRememberMeServicesConfiguration[tag=snippet,indent=0]\n\nRemember to add your `RememberMeServices` implementation to your `UsernamePasswordAuthenticationFilter.setRememberMeServices()` property, include the `RememberMeAuthenticationProvider` in your `AuthenticationManager.setProviders()` list, and add `RememberMeAuthenticationFilter` into your `FilterChainProxy` (typically immediately after your `UsernamePasswordAuthenticationFilter`).\n\n\n=== PersistentTokenBasedRememberMeServices\nYou can use this class in the same way as `TokenBasedRememberMeServices`, but it additionally needs to be configured with a `PersistentTokenRepository` to store the tokens.\n\n* `InMemoryTokenRepositoryImpl` which is intended for testing only.\n* `JdbcTokenRepositoryImpl` which stores the tokens in a database.\n\nSee <<remember-me-persistent-token>> for the database schema.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/runas.adoc",
    "content": "[[runas]]\n= Run-As Authentication Replacement\n\n[[runas-overview]]\nThe `AbstractSecurityInterceptor` is able to temporarily replace the `Authentication` object in the `SecurityContext` and `SecurityContextHolder` during the secure object callback phase.\nThis only occurs if the original `Authentication` object was successfully processed by the `AuthenticationManager` and `AccessDecisionManager`.\nThe `RunAsManager` indicates the replacement `Authentication` object, if any, that should be used during the `SecurityInterceptorCallback`.\n\nBy temporarily replacing the `Authentication` object during the secure object callback phase, the secured invocation can call other objects that require different authentication and authorization credentials.\nIt can also perform any internal security checks for specific `GrantedAuthority` objects.\nBecause Spring Security provides a number of helper classes that automatically configure remoting protocols based on the contents of the `SecurityContextHolder`, these run-as replacements are particularly useful when calling remote web services.\n\n[[runas-config]]\n== Configuration\nSpring Security provides a `RunAsManager` interface:\n\n[source,java]\n----\nAuthentication buildRunAs(Authentication authentication, Object object,\n\tList<ConfigAttribute> config);\n\nboolean supports(ConfigAttribute attribute);\n\nboolean supports(Class clazz);\n----\n\n\nThe first method returns the `Authentication` object that should replace the existing `Authentication` object for the duration of the method invocation.\nIf the method returns `null`, it indicates no replacement should be made.\nThe second method is used by the `AbstractSecurityInterceptor` as part of its startup validation of configuration attributes.\nThe `supports(Class)` method is called by a security interceptor implementation to ensure that the configured `RunAsManager` supports the type of secure object that the security interceptor presents.\n\nSpring Security provides one concrete implementation of `RunAsManager`.\nThe `RunAsManagerImpl` class returns a replacement `RunAsUserToken` if any `ConfigAttribute` starts with `RUN_AS_`.\nIf any such `ConfigAttribute` is found, the replacement `RunAsUserToken` contains the same principal, credentials, and granted authorities as the original `Authentication` object, along with a new `SimpleGrantedAuthority` for each `RUN_AS_` `ConfigAttribute`.\nEach new `SimpleGrantedAuthority` is prefixed with `ROLE_`, followed by the `RUN_AS` `ConfigAttribute`.\nFor example, a `RUN_AS_SERVER` results in the replacement `RunAsUserToken` containing a `ROLE_RUN_AS_SERVER` granted authority.\n\nThe replacement `RunAsUserToken` is like any other `Authentication` object.\nIt needs to be authenticated by the `AuthenticationManager`, probably through delegation to a suitable `AuthenticationProvider`.\nThe `RunAsImplAuthenticationProvider` performs such authentication.\nIt accepts as valid any `RunAsUserToken` presented.\n\nTo ensure malicious code does not create a `RunAsUserToken` and present it for guaranteed acceptance by the `RunAsImplAuthenticationProvider`, the hash of a key is stored in all generated tokens.\nThe `RunAsManagerImpl` and `RunAsImplAuthenticationProvider` is created in the bean context with the same key:\n\n[source,xml]\n----\n<bean id=\"runAsManager\"\n\tclass=\"org.springframework.security.access.intercept.RunAsManagerImpl\">\n<property name=\"key\" value=\"my_run_as_password\"/>\n</bean>\n\n<bean id=\"runAsAuthenticationProvider\"\n\tclass=\"org.springframework.security.access.intercept.RunAsImplAuthenticationProvider\">\n<property name=\"key\" value=\"my_run_as_password\"/>\n</bean>\n----\n\nBy using the same key, each `RunAsUserToken` can be validated because it was created by an approved `RunAsManagerImpl`.\nThe `RunAsUserToken` is immutable after creation, for security reasons.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/session-management.adoc",
    "content": "[[session-mgmt]]\n= Authentication Persistence and Session Management\n\nOnce you have got an application that is xref:servlet/authentication/index.adoc[authenticating requests], it is important to consider how that resulting authentication will be persisted and restored on future requests.\n\nThis is done automatically by default.\nIf you have a custom filter or controller that is setting the security context, you will need to use a `SecurityContextRepository` to persist it across requests.\n\nIf you are upgrading from an older version, you may be interested in the `requireExplicitSave` setting that preserves Spring Security 5's default, though note that this is primarily for migration purposes.\n\nIf you like, <<how-it-works-requireexplicitsave,you can read more about what requireExplicitSave is doing>> or <<requireexplicitsave,why it's important>>. Otherwise, in most cases you are done with this section.\n\nBut before you leave, consider if any of these use cases fit your application:\n\n* I want to <<understanding-session-management-components,Understand Session Management's components>>\n* I want to <<ns-concurrent-sessions,restrict the number of times>> a user can be logged in concurrently\n* I want <<store-authentication-manually,to store the authentication directly>> myself instead of Spring Security doing it for me\n* I am storing the authentication manually and I want <<properly-clearing-authentication,to remove it>>\n* I am using <<the-sessionmanagementfilter, `SessionManagementFilter`>> and I need <<moving-away-from-sessionmanagementfilter,guidance on moving away from that>>\n* I want to store the authentication <<customizing-where-authentication-is-stored,in something other than the session>>\n* I am using a <<stateless-authentication, stateless authentication>>, but <<storing-stateless-authentication-in-the-session,I'd still like to store it in the session>>\n* I am using `SessionCreationPolicy.NEVER` but <<never-policy-session-still-created,the application is still creating sessions>>.\n\n\n[[understanding-session-management-components]]\n== Understanding Session Management's Components\n\nThe Session Management support is composed of a few components that work together to provide the functionality.\nThose components are, xref:servlet/authentication/persistence.adoc#securitycontextholderfilter[the `SecurityContextHolderFilter`], xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[the `SecurityContextPersistenceFilter`] and <<the-sessionmanagementfilter,the `SessionManagementFilter`>>.\n\n[NOTE]\n=====\nIn Spring Security 6, the `SecurityContextPersistenceFilter` and `SessionManagementFilter` are not set by default.\nIn addition to that, any application should only have either `SecurityContextHolderFilter` or `SecurityContextPersistenceFilter` set, never both.\n=====\n\n[[the-sessionmanagementfilter]]\n=== The `SessionManagementFilter`\n\nThe `SessionManagementFilter` checks the contents of the `SecurityContextRepository` against the current contents of the `SecurityContextHolder` to determine whether a user has been authenticated during the current request, typically by a non-interactive authentication mechanism, such as pre-authentication or remember-me  footnote:[\nAuthentication by mechanisms which perform a redirect after authenticating (such as form-login) will not be detected by `SessionManagementFilter`, as the filter will not be invoked during the authenticating request.\nSession-management functionality has to be handled separately in these cases.\n].\nIf the repository contains a security context, the filter does nothing.\nIf it doesn't, and the thread-local `SecurityContext` contains a (non-anonymous) `Authentication` object, the filter assumes they have been authenticated by a previous filter in the stack.\nIt will then invoke the configured `SessionAuthenticationStrategy`.\n\nIf the user is not currently authenticated, the filter will check whether an invalid session ID has been requested (because of a timeout, for example) and will invoke the configured `InvalidSessionStrategy`, if one is set.\nThe most common behaviour is just to redirect to a fixed URL and this is encapsulated in the standard implementation `SimpleRedirectInvalidSessionStrategy`.\nThe latter is also used when configuring an invalid session URL through the namespace, <<session-mgmt,as described earlier>>.\n\n[[moving-away-from-sessionmanagementfilter]]\n==== Moving Away From `SessionManagementFilter`\n\nIn Spring Security 5, the default configuration relies on `SessionManagementFilter` to detect if a user just authenticated and invoke the javadoc:org.springframework.security.web.authentication.session.SessionAuthenticationStrategy[SessionAuthenticationStrategy].\nThe problem with this is that it means that in a typical setup, the `HttpSession` must be read for every request.\n\nIn Spring Security 6, the default is that authentication mechanisms themselves must invoke the `SessionAuthenticationStrategy`.\nThis means that there is no need to detect when `Authentication` is done and thus the `HttpSession` does not need to be read for every request.\n\n==== Things To Consider When Moving Away From `SessionManagementFilter`\n\nIn Spring Security 6, the `SessionManagementFilter` is not used by default, therefore, some methods from the `sessionManagement` DSL will not have any effect.\n\n|===\n|Method |Replacement\n\n|`sessionAuthenticationErrorUrl`\n|Configure an javadoc:org.springframework.security.web.authentication.AuthenticationFailureHandler[] in your authentication mechanism\n\n|`sessionAuthenticationFailureHandler`\n|Configure an javadoc:org.springframework.security.web.authentication.AuthenticationFailureHandler[] in your authentication mechanism\n\n|`sessionAuthenticationStrategy`\n|Configure an `SessionAuthenticationStrategy` in your authentication mechanism as <<moving-away-from-sessionmanagementfilter,discussed above>>\n|===\n\nIf you try to use any of these methods, an exception will be thrown.\n\n\n[[customizing-where-authentication-is-stored]]\n== Customizing Where the Authentication Is Stored\n\nBy default, Spring Security stores the security context for you in the HTTP session. However, here are several reasons you may want to customize that:\n\n* You may want to call individual setters on the `HttpSessionSecurityContextRepository` instance\n* You may want to store the security context in a cache or database to enable horizontal scaling\n\nFirst, you need to create an implementation of `SecurityContextRepository` or use an existing implementation like `HttpSessionSecurityContextRepository`, then you can set it in `HttpSecurity`.\n\n[[customizing-the-securitycontextrepository]]\n.Customizing the `SecurityContextRepository`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    SecurityContextRepository repo = new MyCustomSecurityContextRepository();\n    http\n        // ...\n        .securityContext((context) -> context\n            .securityContextRepository(repo)\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    val repo = MyCustomSecurityContextRepository()\n    http {\n        // ...\n        securityContext {\n            securityContextRepository = repo\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http security-context-repository-ref=\"repo\">\n    <!-- ... -->\n</http>\n<bean name=\"repo\" class=\"com.example.MyCustomSecurityContextRepository\" />\n----\n======\n\n[NOTE]\n====\nThe above configuration sets the `SecurityContextRepository` on the `SecurityContextHolderFilter` and **participating** authentication filters, like `UsernamePasswordAuthenticationFilter`.\nTo also set it in stateless filters, please see <<storing-stateless-authentication-in-the-session,how to customize the `SecurityContextRepository` for Stateless Authentication>>.\n====\n\nIf you are using a custom authentication mechanism, you might want to <<store-authentication-manually,store the `Authentication` by yourself>>.\n\n[[store-authentication-manually]]\n=== Storing the `Authentication` manually\n\nIn some cases, for example, you might be authenticating a user manually instead of relying on Spring Security filters.\nYou can use a custom filters or a {spring-framework-reference-url}/web.html#mvc-controller[Spring MVC controller] endpoint to do that.\nIf you want to save the authentication between requests, in the `HttpSession`, for example, you have to do so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprivate SecurityContextRepository securityContextRepository =\n        new HttpSessionSecurityContextRepository(); <1>\n\n@PostMapping(\"/login\")\npublic void login(@RequestBody LoginRequest loginRequest, HttpServletRequest request, HttpServletResponse response) { <2>\n    UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\n        loginRequest.getUsername(), loginRequest.getPassword()); <3>\n    Authentication authentication = authenticationManager.authenticate(token); <4>\n    SecurityContext context = securityContextHolderStrategy.createEmptyContext();\n    context.setAuthentication(authentication); <5>\n    securityContextHolderStrategy.setContext(context);\n    securityContextRepository.saveContext(context, request, response); <6>\n}\n\nclass LoginRequest {\n\n    private String username;\n    private String password;\n\n    // getters and setters\n}\n----\n======\n\n<1> Add the `SecurityContextRepository` to the controller\n<2> Inject the `HttpServletRequest` and `HttpServletResponse` to be able to save the `SecurityContext`\n<3> Create an unauthenticated `UsernamePasswordAuthenticationToken` using the provided credentials\n<4> Call `AuthenticationManager#authenticate` to authenticate the user\n<5> Create a `SecurityContext` and set the `Authentication` in it\n<6> Save the `SecurityContext` in the `SecurityContextRepository`\n\nAnd that's it.\nIf you are not sure what `securityContextHolderStrategy` is in the above example, you can read more about it in the <<use-securitycontextholderstrategy, Using `SecurityContextStrategy` section>>.\n\n[[properly-clearing-authentication]]\n=== Properly Clearing an Authentication\n\nIf you are using Spring Security's xref:servlet/authentication/logout.adoc[Logout Support] then it handles a lot of stuff for you including clearing and saving the context.\nBut, let's say you need to manually log users out of your app. In that case, you'll need to make sure you're xref:servlet/authentication/logout.adoc#creating-custom-logout-endpoint[clearing and saving the context properly].\n\n[[stateless-authentication]]\n=== Configuring Persistence for Stateless Authentication\n\nSometimes there is no need to create and maintain a `HttpSession` for example, to persist the authentication across requests.\nSome authentication mechanisms like xref:servlet/authentication/passwords/basic.adoc[HTTP Basic] are stateless and, therefore, re-authenticates the user on every request.\n\nIf you do not wish to create sessions, you can use `SessionCreationPolicy.STATELESS`, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        // ...\n        .sessionManagement((session) -> session\n            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        // ...\n        sessionManagement {\n            sessionCreationPolicy = SessionCreationPolicy.STATELESS\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http create-session=\"stateless\">\n    <!-- ... -->\n</http>\n----\n======\n\nThe above configuration is <<customizing-where-authentication-is-stored, configuring the `SecurityContextRepository`>> to use a `NullSecurityContextRepository` and is also xref:servlet/architecture.adoc#requestcache-prevent-saved-request[preventing the request from being saved in the session].\n\n\n[[never-policy-session-still-created]]\nIf you are using `SessionCreationPolicy.NEVER`, you might notice that the application is still creating a `HttpSession`.\nIn most cases, this happens because the xref:servlet/architecture.adoc#savedrequests[request is saved in the session] for the authenticated resource to re-request after authentication is successful.\nTo avoid that, please refer to xref:servlet/architecture.adoc#requestcache-prevent-saved-request[how to prevent the request of being saved] section.\n\n\n[[storing-stateless-authentication-in-the-session]]\n==== Storing Stateless Authentication in the Session\n\nIf, for some reason, you are using a stateless authentication mechanism, but you still want to store the authentication in the session you can use the `HttpSessionSecurityContextRepository` instead of the `NullSecurityContextRepository`.\n\nFor the xref:servlet/authentication/passwords/basic.adoc[HTTP Basic], you can add xref:servlet/configuration/java.adoc#post-processing-configured-objects[a `ObjectPostProcessor`] that changes the `SecurityContextRepository` used by the `BasicAuthenticationFilter`:\n\n.Store HTTP Basic authentication in the `HttpSession`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityFilterChain web(HttpSecurity http) throws Exception {\n    http\n        // ...\n        .httpBasic((basic) -> basic\n            .addObjectPostProcessor(new ObjectPostProcessor<BasicAuthenticationFilter>() {\n                @Override\n                public <O extends BasicAuthenticationFilter> O postProcess(O filter) {\n                    filter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());\n                    return filter;\n                }\n            })\n        );\n\n    return http.build();\n}\n----\n======\n\nThe above also applies to others authentication mechanisms, like xref:servlet/oauth2/resource-server/index.adoc[Bearer Token Authentication].\n\n\n[[requireexplicitsave]]\n== Understanding Require Explicit Save\n\nIn Spring Security 5, the default behavior is for the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to automatically be saved to the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] using the <<securitycontextpersistencefilter, `SecurityContextPersistenceFilter`>>.\nSaving must be done just prior to the `HttpServletResponse` being committed and just before `SecurityContextPersistenceFilter`.\nUnfortunately, automatic persistence of the `SecurityContext` can surprise users when it is done prior to the request completing (i.e. just prior to committing the `HttpServletResponse`).\nIt also is complex to keep track of the state to determine if a save is necessary causing unnecessary writes to the `SecurityContextRepository` (i.e. `HttpSession`) at times.\n\nFor these reasons, the `SecurityContextPersistenceFilter` has been deprecated to be replaced with the `SecurityContextHolderFilter`.\nIn Spring Security 6, the default behavior is that xref:servlet/authentication/persistence.adoc#securitycontextholderfilter[the `SecurityContextHolderFilter`] will only read the `SecurityContext` from  `SecurityContextRepository` and populate it in the `SecurityContextHolder`.\nUsers now must explicitly save the `SecurityContext` with the `SecurityContextRepository` if they want the `SecurityContext` to persist between requests.\nThis removes ambiguity and improves performance by only requiring writing to the `SecurityContextRepository` (i.e. `HttpSession`) when it is necessary.\n\n[[how-it-works-requireexplicitsave]]\n=== How it works\n\nIn summary, when `requireExplicitSave` is `true`, Spring Security sets up xref:servlet/authentication/persistence.adoc#securitycontextholderfilter[the `SecurityContextHolderFilter`] instead of xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[the `SecurityContextPersistenceFilter`]\n\n\n[[ns-concurrent-sessions]]\n== Configuring Concurrent Session Control\nIf you wish to place constraints on a single user's ability to log in to your application, Spring Security supports this out of the box with the following simple additions.\nFirst, you need to add the following listener to your configuration to keep Spring Security updated about session lifecycle events:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic HttpSessionEventPublisher httpSessionEventPublisher() {\n    return new HttpSessionEventPublisher();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun httpSessionEventPublisher(): HttpSessionEventPublisher {\n    return HttpSessionEventPublisher()\n}\n----\n\nweb.xml::\n+\n[source,xml,role=\"secondary\"]\n----\n<listener>\n<listener-class>\n    org.springframework.security.web.session.HttpSessionEventPublisher\n</listener-class>\n</listener>\n----\n======\n\nThen add the following lines to your security configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        .sessionManagement((session) -> session\n            .maximumSessions(1)\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = 1\n            }\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n...\n<session-management>\n    <concurrency-control max-sessions=\"1\" />\n</session-management>\n</http>\n----\n======\n\n\nThis will prevent a user from logging in multiple times - a second login will cause the first to be invalidated.\n\nYou can also adjust this based on who the user is.\nFor example, administrators may be able to have more than one session:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n\tAuthorizationManager<?> isAdmin = AuthorityAuthorizationManager.hasRole(\"ADMIN\");\n    http\n        .sessionManagement((session) -> session\n            .maximumSessions((authentication) -> isAdmin.authorize(() -> authentication, null).isGranted() ? -1 : 1)\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    val isAdmin: AuthorizationManager<*> = AuthorityAuthorizationManager.hasRole(\"ADMIN\")\n    http {\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions {\n                    authentication -> if (isAdmin.authorize({ authentication }, null)!!.isGranted) -1 else 1\n                }\n            }\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n...\n<session-management>\n    <concurrency-control max-sessions-ref=\"sessionLimit\" />\n</session-management>\n</http>\n\n<b:bean id=\"sessionLimit\" class=\"my.SessionLimitImplementation\"/>\n----\n======\n\nUsing Spring Boot, you can test the above configurations in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@AutoConfigureMockMvc\npublic class MaximumSessionsTests {\n\n    @Autowired\n    private MockMvc mvc;\n\n    @Test\n    void loginOnSecondLoginThenFirstSessionTerminated() throws Exception {\n        MvcResult mvcResult = this.mvc.perform(formLogin())\n                .andExpect(authenticated())\n                .andReturn();\n\n        MockHttpSession firstLoginSession = (MockHttpSession) mvcResult.getRequest().getSession();\n\n        this.mvc.perform(get(\"/\").session(firstLoginSession))\n                .andExpect(authenticated());\n\n        this.mvc.perform(formLogin()).andExpect(authenticated());\n\n        // first session is terminated by second login\n        this.mvc.perform(get(\"/\").session(firstLoginSession))\n                .andExpect(unauthenticated());\n    }\n\n}\n----\n======\n\nYou can try it using the {gh-samples-url}/servlet/spring-boot/java/session-management/maximum-sessions[Maximum Sessions sample].\n\nIt is also common that you would prefer to prevent a second login, in which case you can use:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        .sessionManagement((session) -> session\n            .maximumSessions(1)\n            .maxSessionsPreventsLogin(true)\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        sessionManagement {\n            sessionConcurrency {\n                maximumSessions = 1\n                maxSessionsPreventsLogin = true\n            }\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n<session-management>\n    <concurrency-control max-sessions=\"1\" error-if-maximum-exceeded=\"true\" />\n</session-management>\n</http>\n----\n======\n\n\nThe second login will then be rejected.\nBy \"rejected\", we mean that the user will be sent to the `authentication-failure-url` if form-based login is being used.\nIf the second authentication takes place through another non-interactive mechanism, such as \"remember-me\", an \"unauthorized\" (401) error will be sent to the client.\nIf instead you want to use an error page, you can add the attribute `session-authentication-error-url` to the `session-management` element.\n\nUsing Spring Boot, you can test the above configuration the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@AutoConfigureMockMvc\npublic class MaximumSessionsPreventLoginTests {\n\n    @Autowired\n    private MockMvc mvc;\n\n    @Test\n    void loginOnSecondLoginThenPreventLogin() throws Exception {\n        MvcResult mvcResult = this.mvc.perform(formLogin())\n                .andExpect(authenticated())\n                .andReturn();\n\n        MockHttpSession firstLoginSession = (MockHttpSession) mvcResult.getRequest().getSession();\n\n        this.mvc.perform(get(\"/\").session(firstLoginSession))\n                .andExpect(authenticated());\n\n        // second login is prevented\n        this.mvc.perform(formLogin()).andExpect(unauthenticated());\n\n        // first session is still valid\n        this.mvc.perform(get(\"/\").session(firstLoginSession))\n                .andExpect(authenticated());\n    }\n\n}\n----\n======\n\nIf you are using a customized authentication filter for form-based login, then you have to configure concurrent session control support explicitly.\nYou can try it using the {gh-samples-url}/servlet/spring-boot/java/session-management/maximum-sessions-prevent-login[Maximum Sessions Prevent Login sample].\n\n[NOTE]\n=====\nIf you are using a custom implementation of `UserDetails`, ensure you override the **equals()** and **hashCode()** methods.\nThe default `SessionRegistry` implementation in Spring Security relies on an in-memory Map that uses these methods to correctly identify and manage user sessions.\nFailing to override them may lead to issues where session tracking and user comparison behave unexpectedly.\n=====\n\n== Detecting Timeouts\n\nSessions expire on their own, and there is nothing that needs to be done to ensure that a security context gets removed.\nThat said, Spring Security can detect when a session has expired and take specific actions that you indicate.\nFor example, you may want to redirect to a specific endpoint when a user makes a request with an already-expired session.\nThis is achieved through the `invalidSessionUrl` in `HttpSecurity`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        .sessionManagement((session) -> session\n            .invalidSessionUrl(\"/invalidSession\")\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        sessionManagement {\n            invalidSessionUrl = \"/invalidSession\"\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n...\n<session-management invalid-session-url=\"/invalidSession\" />\n</http>\n----\n======\n\nNote that if you use this mechanism to detect session timeouts, it may falsely report an error if the user logs out and then logs back in without closing the browser.\nThis is because the session cookie is not cleared when you invalidate the session and will be resubmitted even if the user has logged out.\nIf that is your case, you might want to <<clearing-session-cookie-on-logout,configure logout to clear the session cookie>>.\n\n=== Customizing the Invalid Session Strategy\n\nThe `invalidSessionUrl` is a convenience method for setting the `InvalidSessionStrategy` using the javadoc:org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy[`SimpleRedirectInvalidSessionStrategy` implementation].\nIf you want to customize the behavior, you can implement the javadoc:org.springframework.security.web.session.InvalidSessionStrategy[] interface and configure it using the `invalidSessionStrategy` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        .sessionManagement((session) -> session\n            .invalidSessionStrategy(new MyCustomInvalidSessionStrategy())\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        sessionManagement {\n            invalidSessionStrategy = MyCustomInvalidSessionStrategy()\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n...\n<session-management invalid-session-strategy-ref=\"myCustomInvalidSessionStrategy\" />\n<bean name=\"myCustomInvalidSessionStrategy\" class=\"com.example.MyCustomInvalidSessionStrategy\" />\n</http>\n----\n======\n\n[[clearing-session-cookie-on-logout]]\n== Clearing Session Cookies on Logout\n\nYou can explicitly delete the JSESSIONID cookie on logging out, for example by using the https://w3c.github.io/webappsec-clear-site-data/[`Clear-Site-Data` header] in the logout handler:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        .logout((logout) -> logout\n            .addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(COOKIES)))\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        logout {\n            addLogoutHandler(HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(COOKIES)))\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n<logout success-handler-ref=\"clearSiteDataHandler\" />\n<b:bean id=\"clearSiteDataHandler\" class=\"org.springframework.security.web.authentication.logout.HeaderWriterLogoutHandler\">\n    <b:constructor-arg>\n        <b:bean class=\"org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter\">\n            <b:constructor-arg>\n                <b:list>\n                    <b:value>COOKIES</b:value>\n                </b:list>\n            </b:constructor-arg>\n        </b:bean>\n    </b:constructor-arg>\n</b:bean>\n</http>\n----\n======\n\nThis has the advantage of being container agnostic and will work with any container that supports the `Clear-Site-Data` header.\n\nAs an alternative, you can also use the following syntax in the logout handler:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        .logout((logout) -> logout\n            .deleteCookies(\"JSESSIONID\")\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        logout {\n            deleteCookies(\"JSESSIONID\")\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n  <logout delete-cookies=\"JSESSIONID\" />\n</http>\n----\n======\n\nUnfortunately, this cannot be guaranteed to work with every servlet container, so you need to test it in your environment.\n\n[NOTE]\n=====\nIf you run your application behind a proxy, you may also be able to remove the session cookie by configuring the proxy server.\nFor example, by using Apache HTTPD's `mod_headers`, the following directive deletes the `JSESSIONID` cookie by expiring it in the response to a logout request (assuming the application is deployed under the `/tutorial` path):\n=====\n\n[source,xml]\n----\n<LocationMatch \"/tutorial/logout\">\nHeader always set Set-Cookie \"JSESSIONID=;Path=/tutorial;Expires=Thu, 01 Jan 1970 00:00:00 GMT\"\n</LocationMatch>\n----\n\nMore details on the xref:servlet/exploits/headers.adoc#servlet-headers-clear-site-data[Clear Site Data] and xref:servlet/authentication/logout.adoc[Logout sections].\n\n\n\n[[ns-session-fixation]]\n== Understanding Session Fixation Attack Protection\n\nhttps://en.wikipedia.org/wiki/Session_fixation[Session fixation] attacks are a potential risk where it is possible for a malicious attacker to create a session by accessing a site, then persuade another user to log in with the same session (by sending them a link containing the session identifier as a parameter, for example).\nSpring Security protects against this automatically by creating a new session or otherwise changing the session ID when a user logs in.\n\n=== Configuring Session Fixation Protection\n\nYou can control the strategy for Session Fixation Protection by choosing between three recommended options:\n\n* `changeSessionId` - Do not create a new session.\nInstead, use the session fixation protection provided by the Servlet container (`HttpServletRequest#changeSessionId()`).\nThis option is only available in Servlet 3.1 (Java EE 7) and newer containers.\nSpecifying it in older containers will result in an exception.\nThis is the default in Servlet 3.1 and newer containers.\n\n* `newSession` - Create a new \"clean\" session, without copying the existing session data (Spring Security-related attributes will still be copied).\n\n* `migrateSession` - Create a new session and copy all existing session attributes to the new session.\nThis is the default in Servlet 3.0 or older containers.\n\nYou can configure the session fixation protection by doing:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        .sessionManagement((session) -> session\n            .sessionFixation((sessionFixation) -> sessionFixation\n                .newSession()\n            )\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        sessionManagement {\n            sessionFixation {\n                newSession()\n            }\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n  <session-management session-fixation-protection=\"newSession\" />\n</http>\n----\n======\n\nWhen session fixation protection occurs, it results in a `SessionFixationProtectionEvent` being published in the application context.\nIf you use `changeSessionId`, this protection will __also__ result in any  ``jakarta.servlet.http.HttpSessionIdListener``s being notified, so use caution if your code listens for both events.\n\nYou can also set the session fixation protection to `none` to disable it, but this is not recommended as it leaves your application vulnerable.\n\n\n\n[[use-securitycontextholderstrategy]]\n== Using `SecurityContextHolderStrategy`\n\nConsider the following block of code:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nUsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(\n        loginRequest.getUsername(), loginRequest.getPassword());\nAuthentication authentication = this.authenticationManager.authenticate(token);\n// ...\nSecurityContext context = SecurityContextHolder.createEmptyContext(); <1>\ncontext.setAuthentication(authentication); <2>\nSecurityContextHolder.setContext(context); <3>\n----\n======\n\n1. Creates an empty `SecurityContext` instance by accessing the `SecurityContextHolder` statically.\n2. Sets the `Authentication` object in the `SecurityContext` instance.\n3. Sets the `SecurityContext` instance in the `SecurityContextHolder` statically.\n\nWhile the above code works fine, it can produce some undesired effects: when components access the `SecurityContext` statically through `SecurityContextHolder`, this can create race conditions when there are multiple application contexts that want to specify the `SecurityContextHolderStrategy`.\nThis is because in `SecurityContextHolder` there is one strategy per classloader instead of one per application context.\n\nTo address this, components can wire `SecurityContextHolderStrategy` from the application context.\nBy default, they will still look up the strategy from `SecurityContextHolder`.\n\nThese changes are largely internal, but they present the opportunity for applications to autowire the `SecurityContextHolderStrategy` instead of accessing the `SecurityContext` statically.\nTo do so, you should change the code to the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class SomeClass {\n\n    private final SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();\n\n    public void someMethod() {\n        UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(\n                loginRequest.getUsername(), loginRequest.getPassword());\n        Authentication authentication = this.authenticationManager.authenticate(token);\n        // ...\n        SecurityContext context = this.securityContextHolderStrategy.createEmptyContext(); <1>\n        context.setAuthentication(authentication); <2>\n        this.securityContextHolderStrategy.setContext(context); <3>\n    }\n\n}\n----\n======\n\n1. Creates an empty `SecurityContext` instance using the configured `SecurityContextHolderStrategy`.\n2. Sets the `Authentication` object in the `SecurityContext` instance.\n3. Sets the `SecurityContext` instance in the `SecurityContextHolderStrategy`.\n\n\n[[session-mgmt-force-session-creation]]\n== Forcing Eager Session Creation\n\nAt times, it can be valuable to eagerly create sessions.\nThis can be done by using the javadoc:org.springframework.security.web.session.ForceEagerSessionCreationFilter[] which can be configured using:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n    http\n        .sessionManagement((session) -> session\n            .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        sessionManagement {\n            sessionCreationPolicy = SessionCreationPolicy.ALWAYS\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http create-session=\"ALWAYS\">\n\n</http>\n----\n======\n\n\n\n== What to read next\n\n- Clustered sessions with https://docs.spring.io/spring-session/reference/index.html[Spring Session]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authentication/x509.adoc",
    "content": "[[servlet-x509]]\n= X.509 Authentication\n\n[[x509-overview]]\nThe most common use of X.509 certificate authentication is in verifying the identity of a server when using SSL, most commonly when using HTTPS from a browser.\nThe browser automatically checks that the certificate presented by a server has been issued (digitally signed) by one of a list of trusted certificate authorities that it maintains.\n\nYou can also use SSL with \"`mutual authentication`\". The server then requests a valid certificate from the client as part of the SSL handshake.\nThe server authenticates the client by checking that its certificate is signed by an acceptable authority.\nIf a valid certificate has been provided, it can be obtained through the servlet API in an application.\nFor example, if you use Tomcat, you should read the https://tomcat.apache.org/tomcat-10.1-doc/ssl-howto.html[Tomcat SSL instructions].\nYou should get this working before trying it out with Spring Security.\n\nThe Spring Security X.509 module extracts the certificate by using a filter.\nIt maps the certificate to an application user and loads that user's set of granted authorities for use with the standard Spring Security infrastructure, specifically including at least the `FACTOR_X509` authority when <<servlet-x509-config, using the `HttpSecurity` DSL>>.\n\n[[servlet-x509-config]]\n== Adding X.509 Authentication to Your Web Application\n\nSimilar to xref:reactive/authentication/x509.adoc[Reactive X.509 authentication], the servlet x509 authentication filter allows extracting an authentication token from a certificate provided by a client.\n\nThe following example shows a reactive x509 security configuration:\n\ninclude-code::./DefaultX509Configuration[tag=springSecurity,indent=0]\n\nIn the preceding configuration, when neither `principalExtractor` nor `authenticationManager` is provided, defaults are used.\nThe default principal extractor is `SubjectX500PrincipalExtractor`, which extracts the CN (common name) field from a certificate provided by a client.\nThe default authentication manager is `ReactivePreAuthenticatedAuthenticationManager`, which performs user account validation, checking that a user account with a name extracted by `principalExtractor` exists and that it is not locked, disabled, or expired.\n\nThe following example demonstrates how these defaults can be overridden:\n\ninclude-code::./CustomX509Configuration[tag=springSecurity,indent=0]\n\nIn the previous example, a username is extracted from the `emailAddress` field of a client certificate instead of CN, and account lookup uses a custom `ReactiveAuthenticationManager` instance.\n\nFor an example of configuring Netty and `WebClient` or `curl` command-line tool to use mutual TLS and enable X.509 authentication, see https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/x509.\n\n\n[[x509-ssl-config]]\n== Setting up SSL in Tomcat\nThere are some pre-generated certificates in the {gh-samples-url}/servlet/java-configuration/authentication/x509/server[Spring Security Samples repository].\nYou can use these to enable SSL for testing if you do not want to generate your own.\nThe `server.jks` file contains the server certificate, the private key, and the issuing authority certificate.\nThere are also some client certificate files for the users from the sample applications.\nYou can install these in your browser to enable SSL client authentication.\n\nTo run tomcat with SSL support, drop the `server.jks` file into the tomcat `conf` directory and add the following connector to the `server.xml` file:\n\n[source,xml]\n----\n<Connector port=\"8443\" protocol=\"HTTP/1.1\" SSLEnabled=\"true\" scheme=\"https\" secure=\"true\"\n\t\t\tclientAuth=\"true\" sslProtocol=\"TLS\"\n\t\t\tkeystoreFile=\"${catalina.home}/conf/server.jks\"\n\t\t\tkeystoreType=\"JKS\" keystorePass=\"password\"\n\t\t\ttruststoreFile=\"${catalina.home}/conf/server.jks\"\n\t\t\ttruststoreType=\"JKS\" truststorePass=\"password\"\n/>\n----\n\n`clientAuth` can also be set to `want` if you still want SSL connections to succeed even if the client does not provide a certificate.\nClients that do not present a certificate cannot access any objects secured by Spring Security unless you use a non-X.509 authentication mechanism, such as form authentication.\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authorization/acls.adoc",
    "content": "[[domain-acls]]\n= Domain Object Security (ACLs)\n\nThis section describes how Spring Security provides domain object security with Access Control Lists (ACLs).\n\n[[domain-acls-overview]]\nComplex applications often need to define access permissions beyond a web request or method invocation level.\nInstead, security decisions need to comprise who (`Authentication`), where (`MethodInvocation`), and what (`SomeDomainObject`).\nIn other words, authorization decisions also need to consider the actual domain object instance subject of a method invocation.\n\nImagine you are designing an application for a pet clinic.\nThere are two main groups of users of your Spring-based application: staff of the pet clinic and the pet clinic's customers.\nThe staff should have access to all of the data, while your customers should be able to see only their own customer records.\nTo make it a little more interesting, your customers can let other users see their customer records, such as their \"`puppy preschool`\" mentor or the president of their local \"`Pony Club`\".\nWhen you use Spring Security as the foundation, you have several possible approaches:\n\n* Write your business methods to enforce the security.\nYou could consult a collection within the `Customer` domain object instance to determine which users have access.\nBy using `SecurityContextHolder.getContext().getAuthentication()`, you can access the `Authentication` object.\n* Write an `AuthorizationManager` to enforce the security from the `GrantedAuthority[]` instances stored in the `Authentication` object.\nThis means that your `AuthenticationManager` needs to populate the `Authentication` with custom `GrantedAuthority[]` objects to represent each of the `Customer` domain object instances to which the principal has access.\n* Write an `AuthorizationManager` to enforce the security and open the target `Customer` domain object directly.\nThis would mean your voter needs access to a DAO that lets it retrieve the `Customer` object.\nIt can then access the `Customer` object's collection of approved users and make the appropriate decision.\n\nEach one of these approaches is perfectly legitimate.\nHowever, the first couples your authorization checking to your business code.\nThe main problems with this include the enhanced difficulty of unit testing and the fact that it would be more difficult to reuse the `Customer` authorization logic elsewhere.\nObtaining the `GrantedAuthority[]` instances from the `Authentication` object is also fine but will not scale to large numbers of `Customer` objects.\nIf a user can access 5,000 `Customer` objects (unlikely in this case, but imagine if it were a popular vet for a large Pony Club!) the amount of memory consumed and the time required to construct the `Authentication` object would be undesirable.\nThe final method, opening the `Customer` directly from external code, is probably the best of the three.\nIt achieves separation of concerns and does not misuse memory or CPU cycles, but it is still inefficient in that both the `AuthorizationManager` and the eventual business method itself perform a call to the DAO responsible for retrieving the `Customer` object.\nTwo accesses per method invocation is clearly undesirable.\nIn addition, with every approach listed, you need to write your own access control list (ACL) persistence and business logic from scratch.\n\nFortunately, there is another alternative, which we discuss later.\n\n[[domain-acls-key-concepts]]\n== Key Concepts\nSpring Security's ACL services are shipped in the `spring-security-acl-xxx.jar`.\nYou need to add this JAR to your classpath to use Spring Security's domain object instance security capabilities.\n\n[NOTE]\n====\nIf you need access to the legacy Access API that includes `AclEntryVoter`, please also include `spring-security-access-xxx.jar`.\n====\n\nSpring Security's domain object instance security capabilities center on the concept of an access control list (ACL).\nEvery domain object instance in your system has its own ACL, and the ACL records details of who can and cannot work with that domain object.\nWith this in mind, Spring Security provides three main ACL-related capabilities to your application:\n\n* A way to efficiently retrieve ACL entries for all of your domain objects (and modifying those ACLs)\n* A way to ensure a given principal is permitted to work with your objects before methods are called\n* A way to ensure a given principal is permitted to work with your objects (or something they return) after methods are called\n\nAs indicated by the first bullet point, one of the main capabilities of the Spring Security ACL module is providing a high-performance way of retrieving ACLs.\nThis ACL repository capability is extremely important, because every domain object instance in your system might have several access control entries, and each ACL might inherit from other ACLs in a tree-like structure (this is supported by Spring Security, and it is very commonly used).\nSpring Security's ACL capability has been carefully designed to provide high performance retrieval of ACLs, together with pluggable caching, deadlock-minimizing database updates, independence from ORM frameworks (we use JDBC directly), proper encapsulation, and transparent database updating.\n\nGiven that databases are central to the operation of the ACL module, we need explore the four main tables used by default in the implementation.\nThe tables are presented in order of size in a typical Spring Security ACL deployment, with the table with the most rows listed last:\n\n[[acl_tables]]\n* `ACL_SID` lets us uniquely identify any principal or authority in the system (\"`SID`\" stands for \"`Security IDentity`\").\nThe only columns are the ID, a textual representation of the SID, and a flag to indicate whether the textual representation refers to a principal name or a `GrantedAuthority`.\nThus, there is a single row for each unique principal or `GrantedAuthority`.\nWhen used in the context of receiving a permission, an SID is generally called a \"`recipient`\".\n\n* `ACL_CLASS` lets us uniquely identify any domain object class in the system.\nThe only columns are the ID and the Java class name.\nThus, there is a single row for each unique Class for which we wish to store ACL permissions.\n\n* `ACL_OBJECT_IDENTITY` stores information for each unique domain object instance in the system.\nColumns include the ID, a foreign key to the ACL_CLASS table, a unique identifier so we know the ACL_CLASS instance for which we provide information, the parent, a foreign key to the ACL_SID table to represent the owner of the domain object instance, and whether we allow ACL entries to inherit from any parent ACL.\nWe have a single row for every domain object instance for which we store ACL permissions.\n\n* Finally, `ACL_ENTRY` stores the individual permissions assigned to each recipient.\nColumns include a foreign key to the `ACL_OBJECT_IDENTITY`, the recipient (i.e. a foreign key to ACL_SID), whether we'll be auditing or not, and the integer bit mask that represents the actual permission being granted or denied.\nWe have a single row for every recipient that receives a permission to work with a domain object.\n\n\n\n\nAs mentioned in the last paragraph, the ACL system uses integer bit masking.\nHowever, you need not be aware of the finer points of bit shifting to use the ACL system.\nSuffice it to say that we have 32 bits we can switch on or off.\nEach of these bits represents a permission. By default, the permissions are read (bit 0), write (bit 1), create (bit 2), delete (bit 3), and administer (bit 4).\nYou can implement your own `Permission` instance if you wish to use other permissions, and the remainder of the ACL framework operates without knowledge of your extensions.\n\nYou should understand that the number of domain objects in your system has absolutely no bearing on the fact that we have chosen to use integer bit masking.\nWhile you have 32 bits available for permissions, you could have billions of domain object instances (which means billions of rows in ACL_OBJECT_IDENTITY and, probably, ACL_ENTRY).\nWe make this point because we have found that people sometimes mistakenly that believe they need a bit for each potential domain object, which is not the case.\n\nNow that we have provided a basic overview of what the ACL system does, and what it looks like at a table-structure level, we need to explore the key interfaces:\n\n\n* `Acl`: Every domain object has one and only one `Acl` object, which internally holds the `AccessControlEntry` objects and knows the owner of the `Acl`.\nAn Acl does not refer directly to the domain object, but instead to an `ObjectIdentity`.\nThe `Acl` is stored in the `ACL_OBJECT_IDENTITY` table.\n\n* `AccessControlEntry`: An `Acl` holds multiple `AccessControlEntry` objects, which are often abbreviated as ACEs in the framework.\nEach ACE refers to a specific tuple of `Permission`, `Sid`, and `Acl`.\nAn ACE can also be granting or non-granting and contain audit settings.\nThe ACE is stored in the `ACL_ENTRY` table.\n\n* `Permission`: A permission represents a particular immutable bit mask and offers convenience functions for bit masking and outputting information.\nThe basic permissions presented above (bits 0 through 4) are contained in the `BasePermission` class.\n\n* `Sid`: The ACL module needs to refer to principals and `GrantedAuthority[]` instances.\nA level of indirection is provided by the `Sid` interface. (\"`SID`\" is an abbreviation of \"`Security IDentity`\".)\nCommon classes include `PrincipalSid` (to represent the principal inside an `Authentication` object) and `GrantedAuthoritySid`.\nThe security identity information is stored in the `ACL_SID` table.\n\n* `ObjectIdentity`: Each domain object is represented internally within the ACL module by an `ObjectIdentity`.\nThe default implementation is called `ObjectIdentityImpl`.\n\n* `AclService`: Retrieves the `Acl` applicable for a given `ObjectIdentity`.\nIn the included implementation (`JdbcAclService`), retrieval operations are delegated to a `LookupStrategy`.\nThe `LookupStrategy` provides a highly optimized strategy for retrieving ACL information, using batched retrievals (`BasicLookupStrategy`) and supporting custom implementations that use materialized views, hierarchical queries, and similar performance-centric, non-ANSI SQL capabilities.\n\n* `MutableAclService`: Lets a modified `Acl` be presented for persistence.\nUse of this interface is optional.\n\nNote that our `AclService` and related database classes all use ANSI SQL.\nThis should therefore work with all major databases.\nAt the time of writing, the system had been successfully tested with Hypersonic SQL, PostgreSQL, Microsoft SQL Server, and Oracle.\n\nTwo samples ship with Spring Security that demonstrate the ACL module.\nThe first is the {gh-samples-url}/servlet/xml/java/contacts[Contacts Sample], and the other is the {gh-samples-url}/servlet/xml/java/dms[Document Management System (DMS) Sample].\nWe suggest taking a look at these examples.\n\n[[domain-acls-getting-started]]\n== Getting Started\nTo get starting with Spring Security's ACL capability, you need to store your ACL information somewhere.\nThis necessitates the instantiation of a `DataSource` in Spring.\nThe `DataSource` is then injected into a `JdbcMutableAclService` and a `BasicLookupStrategy` instance.\nThe former provides mutator capabilities, and the latter provides high-performance ACL retrieval capabilities.\nSee one of the {gh-samples-url}[samples] that ship with Spring Security for an example configuration.\nYou also need to populate the database with the <<acl_tables,four ACL-specific tables>> listed in the previous section (see the ACL samples for the appropriate SQL statements).\n\nOnce you have created the required schema and instantiated `JdbcMutableAclService`, you need to ensure your domain model supports interoperability with the Spring Security ACL package.\nHopefully, `ObjectIdentityImpl` proves sufficient, as it provides a large number of ways in which it can be used.\nMost people have domain objects that contain a `public Serializable getId()` method.\nIf the return type is `long` or compatible with `long` (such as an `int`), you may find that you need not give further consideration to `ObjectIdentity` issues.\nMany parts of the ACL module rely on long identifiers.\nIf you do not use `long` (or an `int`, `byte`, and so on), you probably need to reimplement a number of classes.\nWe do not intend to support non-long identifiers in Spring Security's ACL module, as longs are already compatible with all database sequences, are the most common identifier data type, and are of sufficient length to accommodate all common usage scenarios.\n\nThe following fragment of code shows how to create an `Acl` or modify an existing `Acl`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Prepare the information we'd like in our access control entry (ACE)\nObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));\nSid sid = new PrincipalSid(\"Samantha\");\nPermission p = BasePermission.ADMINISTRATION;\n\n// Create or update the relevant ACL\nMutableAcl acl = null;\ntry {\nacl = (MutableAcl) aclService.readAclById(oi);\n} catch (NotFoundException nfe) {\nacl = aclService.createAcl(oi);\n}\n\n// Now grant some permissions via an access control entry (ACE)\nacl.insertAce(acl.getEntries().length, p, sid, true);\naclService.updateAcl(acl);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval oi: ObjectIdentity = ObjectIdentityImpl(Foo::class.java, 44)\nval sid: Sid = PrincipalSid(\"Samantha\")\nval p: Permission = BasePermission.ADMINISTRATION\n\n// Create or update the relevant ACL\nvar acl: MutableAcl? = null\nacl = try {\naclService.readAclById(oi) as MutableAcl\n} catch (nfe: NotFoundException) {\naclService.createAcl(oi)\n}\n\n// Now grant some permissions via an access control entry (ACE)\nacl!!.insertAce(acl.entries.size, p, sid, true)\naclService.updateAcl(acl)\n----\n======\n\nIn the preceding example, we retrieve the ACL associated with the `Foo` domain object with identifier number 44.\nWe then add an ACE so that a principal named \"`Samantha`\" can \"`administer`\" the object.\nThe code fragment is relatively self-explanatory, except for the `insertAce` method.\nThe first argument to the `insertAce` method determine position in the Acl at which the new entry is inserted.\nIn the preceding example, we put the new ACE at the end of the existing ACEs.\nThe final argument is a Boolean indicating whether the ACE is granting or denying.\nMost of the time it grants (`true`). However, if it denies (`false`), the permissions are effectively being blocked.\n\nSpring Security does not provide any special integration to automatically create, update, or delete ACLs as part of your DAO or repository operations.\nInstead, you need to write code similar to that shown in the preceding example for your individual domain objects.\nYou should consider using AOP on your services layer to automatically integrate the ACL information with your services layer operations.\nWe have found this approach to be effective.\n\n== Using the PermissionEvaluator\n\nOnce you have used the techniques described here to store some ACL information in the database, the next step is to actually use the ACL information as part of authorization decision logic.\n\nYou have a number of choices here with the primary one being using `AclPermissionEvaluator` in your `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` annotation expressions.\n\nThis is a sample listing of the components needed to wire an `AclPersmissionEvaluator` into your authorization logic:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableMethodSecurity\n@Configuration\nclass SecurityConfig {\n\t@Bean\n\tstatic MethodSecurityExpressionHandler expressionHandler(AclPermissionEvaluator aclPermissionEvaluator) {\n\t\tfinal DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\t\texpressionHandler.setPermissionEvaluator(aclPermissionEvaluator);\n\t\treturn expressionHandler;\n\t}\n\n\t@Bean\n\tstatic AclPermissionEvaluator aclPermissionEvaluator(AclService aclService) {\n\t\treturn new AclPermissionEvaluator(aclService);\n\t}\n\n\t@Bean\n\tstatic JdbcMutableAclService aclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {\n\t\treturn new JdbcMutableAclService(dataSource, lookupStrategy, aclCache);\n\t}\n\n\t@Bean\n\tstatic LookupStrategy lookupStrategy(DataSource dataSource, AclCache cache,\n\t\t\tAclAuthorizationStrategy aclAuthorizationStrategy, PermissionGrantingStrategy permissionGrantingStrategy) {\n\t\treturn new BasicLookupStrategy(dataSource, cache, aclAuthorizationStrategy, permissionGrantingStrategy);\n\t}\n\n\t@Bean\n\tstatic AclCache aclCache(PermissionGrantingStrategy permissionGrantingStrategy,\n\t\t\tAclAuthorizationStrategy aclAuthorizationStrategy) {\n\t\tCache cache = new ConcurrentMapCache(\"aclCache\");\n\t\treturn new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);\n\t}\n\n\t@Bean\n\tstatic AclAuthorizationStrategy aclAuthorizationStrategy() {\n\t\treturn new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority(\"ADMIN\"));\n\t}\n\n\t@Bean\n\tstatic PermissionGrantingStrategy permissionGrantingStrategy() {\n\t\treturn new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableMethodSecurity\n@Configuration\ninternal object SecurityConfig {\n    @Bean\n    fun expressionHandler(aclPermissionEvaluator: AclPermissionEvaluator?): MethodSecurityExpressionHandler {\n        val expressionHandler = DefaultMethodSecurityExpressionHandler()\n        expressionHandler.setPermissionEvaluator(aclPermissionEvaluator)\n        return expressionHandler\n    }\n\n    @Bean\n    fun aclPermissionEvaluator(aclService: AclService?): AclPermissionEvaluator {\n        return AclPermissionEvaluator(aclService)\n    }\n\n    @Bean\n    fun aclService(dataSource: DataSource?, lookupStrategy: LookupStrategy?, aclCache: AclCache?): JdbcMutableAclService {\n        return JdbcMutableAclService(dataSource, lookupStrategy, aclCache)\n    }\n\n    @Bean\n    fun lookupStrategy(dataSource: DataSource?, cache: AclCache?,\n    aclAuthorizationStrategy: AclAuthorizationStrategy?, permissionGrantingStrategy: PermissionGrantingStrategy?): LookupStrategy {\n        return BasicLookupStrategy(dataSource, cache, aclAuthorizationStrategy, permissionGrantingStrategy)\n    }\n\n    @Bean\n    fun aclCache(permissionGrantingStrategy: PermissionGrantingStrategy?,\n    aclAuthorizationStrategy: AclAuthorizationStrategy?): AclCache {\n        val cache: Cache = ConcurrentMapCache(\"aclCache\")\n        return SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy)\n    }\n\n    @Bean\n    fun aclAuthorizationStrategy(): AclAuthorizationStrategy {\n        return AclAuthorizationStrategyImpl(SimpleGrantedAuthority(\"ADMIN\"))\n    }\n\n    @Bean\n    fun permissionGrantingStrategy(): PermissionGrantingStrategy {\n        return DefaultPermissionGrantingStrategy(ConsoleAuditLogger())\n    }\n}\n----\n======\n\n\nThen using xref:servlet/authorization/method-security.adoc#authorizing-with-annotations[method-based security] you can use `hasPermission` in your annotation expressions like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping\n@PostFilter(\"hasPermission(filterObject, read)\")\nIterable<Message> getAll() {\n\treturn this.messagesRepository.findAll();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping\n@PostFilter(\"hasPermission(filterObject, read)\")\nfun getAll(): Iterable<Message> {\n    return this.messagesRepository.findAll()\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authorization/architecture.adoc",
    "content": "// from the original documentation\n\n[[authz-arch]]\n= Authorization Architecture\n:figures: servlet/authorization\n\nThis section describes the Spring Security architecture that applies to authorization.\n\n[[authz-authorities]]\n== Authorities\nxref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] discusses how all `Authentication` implementations store a list of `GrantedAuthority` objects.\nThese represent the authorities that have been granted to the principal.\nThe `GrantedAuthority` objects are inserted into the `Authentication` object by the `AuthenticationManager` and are later read by `AuthorizationManager` instances when making authorization decisions.\n\nThe `GrantedAuthority` interface has only one method:\n\n[source,java]\n----\n\nString getAuthority();\n\n----\n\nThis method is used by an\n`AuthorizationManager` instance to obtain a precise `String` representation of the `GrantedAuthority`.\nBy returning a representation as a `String`, a `GrantedAuthority` can be easily \"read\" by most `AuthorizationManager` implementations.\nIf a `GrantedAuthority` cannot be precisely represented as a `String`, the `GrantedAuthority` is considered \"complex\" and `getAuthority()` must return `null`.\n\nAn example of a complex `GrantedAuthority` would be an implementation that stores a list of operations and authority thresholds that apply to different customer account numbers.\nRepresenting this complex `GrantedAuthority` as a `String` would be quite difficult. As a result, the `getAuthority()` method should return `null`.\nThis indicates to any `AuthorizationManager` that it needs to support the specific `GrantedAuthority` implementation to understand its contents.\n\nSpring Security includes one concrete `GrantedAuthority` implementation: `SimpleGrantedAuthority`.\nThis implementation lets any user-specified `String` be converted into a `GrantedAuthority`.\nAll `AuthenticationProvider` instances included with the security architecture use `SimpleGrantedAuthority` to populate the `Authentication` object.\n\n[[jc-method-security-custom-granted-authority-defaults]]\nBy default, role-based authorization rules include `ROLE_` as a prefix.\nThis means that if there is an authorization rule that requires a security context to have a role of \"USER\", Spring Security will by default look for a `GrantedAuthority#getAuthority` that returns \"ROLE_USER\".\n\nYou can customize this with `GrantedAuthorityDefaults`.\n`GrantedAuthorityDefaults` exists to allow customizing the prefix to use for role-based authorization rules.\n\nYou can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so:\n\n.Custom MethodSecurityExpressionHandler\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nstatic GrantedAuthorityDefaults grantedAuthorityDefaults() {\n\treturn new GrantedAuthorityDefaults(\"MYPREFIX_\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ncompanion object {\n\t@Bean\n\tfun grantedAuthorityDefaults() : GrantedAuthorityDefaults {\n\t\treturn GrantedAuthorityDefaults(\"MYPREFIX_\");\n\t}\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<bean id=\"grantedAuthorityDefaults\" class=\"org.springframework.security.config.core.GrantedAuthorityDefaults\">\n\t<constructor-arg value=\"MYPREFIX_\"/>\n</bean>\n----\n======\n\n[TIP]\n====\nYou expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes\n====\n\n[[authz-pre-invocation]]\n== Invocation Handling\nSpring Security provides interceptors that control access to secure objects, such as method invocations or web requests.\nA pre-invocation decision on whether the invocation is allowed to proceed is made by `AuthorizationManager` instances.\nAlso post-invocation decisions on whether a given value may be returned is made by `AuthorizationManager` instances.\n\n=== The AuthorizationManager\n`AuthorizationManager` supersedes both <<authz-legacy-note,`AccessDecisionManager` and `AccessDecisionVoter`>>.\n\nApplications that customize an `AccessDecisionManager` or `AccessDecisionVoter` are encouraged to <<authz-voter-adaptation,change to using `AuthorizationManager`>>.\n\n``AuthorizationManager``s are called by Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[request-based], xref:servlet/authorization/method-security.adoc[method-based], and xref:servlet/integrations/websocket.adoc[message-based] authorization components and are responsible for making final access control decisions.\nThe `AuthorizationManager` interface contains two methods:\n\n[source,java]\n----\nAuthorizationResult authorize(Supplier<Authentication> authentication, Object secureObject);\n\ndefault void verify(Supplier<Authentication> authentication, Object secureObject)\n        throws AccessDeniedException {\n    // ...\n}\n----\n\nThe ``AuthorizationManager``'s `authorize` method is passed all the relevant information it needs in order to make an authorization decision.\nIn particular, passing the secure `Object` enables those arguments contained in the actual secure object invocation to be inspected.\nFor example, let's assume the secure object was a `MethodInvocation`.\nIt would be easy to query the `MethodInvocation` for any `Customer` argument, and then implement some sort of security logic in the `AuthorizationManager` to ensure the principal is permitted to operate on that customer.\nImplementations are expected to return a positive `AuthorizationDecision` if access is granted, negative `AuthorizationDecision` if access is denied, and a null `AuthorizationDecision` when abstaining from making a decision.\n\n`verify` calls `authorize` and subsequently throws an `AccessDeniedException` in the case of a negative `AuthorizationDecision`.\n\n[[authz-delegate-authorization-manager]]\n=== Delegate-based AuthorizationManager Implementations\nWhilst users can implement their own `AuthorizationManager` to control all aspects of authorization, Spring Security ships with a delegating `AuthorizationManager` that can collaborate with individual ``AuthorizationManager``s.\n\n`RequestMatcherDelegatingAuthorizationManager` will match the request with the most appropriate delegate `AuthorizationManager`.\nFor method security, you can use `AuthorizationManagerBeforeMethodInterceptor` and `AuthorizationManagerAfterMethodInterceptor`.\n\n<<authz-authorization-manager-implementations>> illustrates the relevant classes.\n\n[[authz-authorization-manager-implementations]]\n.Authorization Manager Implementations\n[.invert-dark]\nimage::{figures}/authorizationhierarchy.png[]\n\nUsing this approach, a composition of `AuthorizationManager` implementations can be polled on an authorization decision.\n\n[[authz-authority-authorization-manager]]\n==== AuthorityAuthorizationManager\nThe most common `AuthorizationManager` provided with Spring Security is `AuthorityAuthorizationManager`.\nIt is configured with a given set of authorities to look for on the current `Authentication`.\nIt will return positive `AuthorizationDecision` should the `Authentication` contain any of the configured authorities.\nIt will return a negative `AuthorizationDecision` otherwise.\n\n[[authz-authenticated-authorization-manager]]\n==== AuthenticatedAuthorizationManager\nAnother manager is the `AuthenticatedAuthorizationManager`.\nIt can be used to differentiate between anonymous, fully-authenticated and remember-me authenticated users.\nMany sites allow certain limited access under remember-me authentication, but require a user to confirm their identity by logging in for full access.\n\n[[authz-conditional-authorization-manager]]\n==== ConditionalAuthorizationManager\njavadoc:org.springframework.security.authorization.ConditionalAuthorizationManager[] delegates to one of two ``AuthorizationManager``s based on a condition evaluated against the current ``Authentication``.\nWhen the condition returns true (and the authentication is non-null), the ``whenTrue`` manager is used; otherwise the ``whenFalse`` manager is used.\nCreate an instance using the builder returned by `ConditionalAuthorizationManager.when(Predicate<Authentication>)`: set `whenTrue` (required) and optionally `whenFalse` (defaults to permit-all).\nThis is useful for scenarios such as requiring multi-factor authentication only when the user has registered a second factor.\n\n.ConditionalAuthorizationManager example\ninclude-code::./ConditionalAuthorizationManagerExample[tag=conditionalAuthorizationManager,indent=0]\n\n[[authz-authorization-manager-factory]]\n=== Creating AuthorizationManager instances\n\nThe javadoc:org.springframework.security.authorization.AuthorizationManagerFactory[] interface (introduced in Spring Security 7.0) is used to create generic ``AuthorizationManager``s in xref:servlet/authorization/authorize-http-requests.adoc[request-based] and xref:servlet/authorization/method-security.adoc[method-based] authorization components.\nThe following is a sketch of the `AuthorizationManagerFactory` interface:\n\n[source,java]\n----\npublic interface AuthorizationManagerFactory<T> {\n\tAuthorizationManager<T> permitAll();\n\tAuthorizationManager<T> denyAll();\n\tAuthorizationManager<T> hasRole(String role);\n\tAuthorizationManager<T> hasAnyRole(String... roles);\n\tAuthorizationManager<T> hasAllRoles(String... roles);\n\tAuthorizationManager<T> hasAuthority(String authority);\n\tAuthorizationManager<T> hasAnyAuthority(String... authorities);\n\tAuthorizationManager<T> hasAllAuthorities(String... authorities);\n\tAuthorizationManager<T> authenticated();\n\tAuthorizationManager<T> fullyAuthenticated();\n\tAuthorizationManager<T> rememberMe();\n\tAuthorizationManager<T> anonymous();\n}\n----\n\nThe default implementation is javadoc:org.springframework.security.authorization.DefaultAuthorizationManagerFactory[], which allows for customizing the `rolePrefix` (defaults to `\"ROLE_\"`), `RoleHierarchy` and `AuthenticationTrustManager` that are provided to the ``AuthorizationManager``s created by the factory.\n\nIn order to customize the default instance used by Spring Security, simply publish a bean as in the following example:\n\ninclude-code::./AuthorizationManagerFactoryConfiguration[tag=config,indent=0]\n\n[TIP]\nIt is also possible to target a specific usage of this factory within Spring Security by providing a concrete parameterized type instead of a generic type.\nSee examples of each in the xref:servlet/authorization/authorize-http-requests.adoc#customizing-authorization-managers[request-based] and xref:servlet/authorization/method-security.adoc#customizing-authorization-managers[method-based] sections of the documentation.\n\nIn addition to simply customizing the default instance of `AuthorizationManagerFactory`, you can provide your own implementation to fully customize the instances created by the factory and provide your own implementations.\n\n[NOTE]\nThe {gh-url}/core/src/main/java/org/springframework/security/authorization/AuthorizationManagerFactory.java[actual interface] provides default implementations for all factory methods, which allows custom implementations to only implement the methods that need to be customized.\n\n[[authz-authorization-managers]]\n==== AuthorizationManagers\nThere are also helpful static factories in javadoc:org.springframework.security.authorization.AuthorizationManagers[] for composing individual ``AuthorizationManager``s into more sophisticated expressions.\n\n[[authz-custom-authorization-manager]]\n==== Custom Authorization Managers\nObviously, you can also implement a custom `AuthorizationManager` and you can put just about any access-control logic you want in it.\nIt might be specific to your application (business-logic related) or it might implement some security administration logic.\nFor example, you can create an implementation that can query Open Policy Agent or your own authorization database.\n\n[TIP]\nYou'll find a https://spring.io/blog/2009/01/03/spring-security-customization-part-2-adjusting-secured-session-in-real-time[blog article] on the Spring web site which describes how to use the legacy `AccessDecisionVoter` to deny access in real-time to users whose accounts have been suspended.\nYou can achieve the same outcome by implementing `AuthorizationManager` instead.\n\n[[authz-voter-adaptation]]\n== Adapting AccessDecisionManager and AccessDecisionVoters\n\nPrevious to `AuthorizationManager`, Spring Security published <<authz-legacy-note,`AccessDecisionManager` and `AccessDecisionVoter`>>.\n\nIn some cases, like migrating an older application, it may be desirable to introduce an `AuthorizationManager` that invokes an `AccessDecisionManager` or `AccessDecisionVoter`.\n\nTo call an existing `AccessDecisionManager`, you can do:\n\n.Adapting an AccessDecisionManager\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class AccessDecisionManagerAuthorizationManagerAdapter implements AuthorizationManager {\n    private final AccessDecisionManager accessDecisionManager;\n    private final SecurityMetadataSource securityMetadataSource;\n\n    @Override\n    public AuthorizationResult authorize(Supplier<Authentication> authentication, Object object) {\n        try {\n            Collection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(object);\n            this.accessDecisionManager.decide(authentication.get(), object, attributes);\n            return new AuthorizationDecision(true);\n        } catch (AccessDeniedException ex) {\n            return new AuthorizationDecision(false);\n        }\n    }\n\n    @Override\n    public void verify(Supplier<Authentication> authentication, Object object) {\n        Collection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(object);\n        this.accessDecisionManager.decide(authentication.get(), object, attributes);\n    }\n}\n----\n======\n\nAnd then wire it into your `SecurityFilterChain`.\n\nOr to only call an `AccessDecisionVoter`, you can do:\n\n.Adapting an AccessDecisionVoter\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class AccessDecisionVoterAuthorizationManagerAdapter implements AuthorizationManager {\n    private final AccessDecisionVoter accessDecisionVoter;\n    private final SecurityMetadataSource securityMetadataSource;\n\n    @Override\n    public AuthorizationResult authorize(Supplier<Authentication> authentication, Object object) {\n        Collection<ConfigAttribute> attributes = this.securityMetadataSource.getAttributes(object);\n        int decision = this.accessDecisionVoter.vote(authentication.get(), object, attributes);\n        switch (decision) {\n        case ACCESS_GRANTED:\n            return new AuthorizationDecision(true);\n        case ACCESS_DENIED:\n            return new AuthorizationDecision(false);\n        }\n        return null;\n    }\n}\n----\n======\n\nAnd then wire it into your `SecurityFilterChain`.\n\n[[authz-hierarchical-roles]]\n== Hierarchical Roles\nIt is a common requirement that a particular role in an application should automatically \"include\" other roles.\nFor example, in an application which has the concept of an \"admin\" and a \"user\" role, you may want an admin to be able to do everything a normal user can.\nTo achieve this, you can either make sure that all admin users are also assigned the \"user\" role.\nAlternatively, you can modify every access constraint which requires the \"user\" role to also include the \"admin\" role.\nThis can get quite complicated if you have a lot of different roles in your application.\n\nThe use of a role-hierarchy allows you to configure which roles (or authorities) should include others.\nThis is supported for filter-based authorization in `HttpSecurity#authorizeHttpRequests` and for method-based authorization through `DefaultMethodSecurityExpressionHandler` for pre-post annotations, `SecuredAuthorizationManager` for `@Secured`, and `Jsr250AuthorizationManager` for JSR-250 annotations.\nYou can configure the behavior for all of them at once in the following way:\n\n.Hierarchical Roles Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nstatic RoleHierarchy roleHierarchy() {\n    return RoleHierarchyImpl.withDefaultRolePrefix()\n        .role(\"ADMIN\").implies(\"STAFF\")\n        .role(\"STAFF\").implies(\"USER\")\n        .role(\"USER\").implies(\"GUEST\")\n        .build();\n}\n\n// and, if using pre-post method security also add\n@Bean\nstatic MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {\n\tDefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n\texpressionHandler.setRoleHierarchy(roleHierarchy);\n\treturn expressionHandler;\n}\n----\n\nXml::\n+\n[source,java,role=\"secondary\"]\n----\n<bean id=\"roleHierarchy\"\n\t\tclass=\"org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl\" factory-method=\"fromHierarchy\">\n\t<constructor-arg>\n\t\t<value>\n\t\t\tROLE_ADMIN > ROLE_STAFF\n\t\t\tROLE_STAFF > ROLE_USER\n\t\t\tROLE_USER > ROLE_GUEST\n\t\t</value>\n\t</constructor-arg>\n</bean>\n\n<!-- and, if using method security also add -->\n<bean id=\"methodSecurityExpressionHandler\"\n        class=\"org.springframework.security.access.expression.method.MethodSecurityExpressionHandler\">\n    <property ref=\"roleHierarchy\"/>\n</bean>\n----\n======\n\nHere we have four roles in a hierarchy `ROLE_ADMIN => ROLE_STAFF => ROLE_USER => ROLE_GUEST`.\nA user who is authenticated with `ROLE_ADMIN`, will behave as if they have all four roles when security constraints are evaluated against any filter- or method-based rules.\n\n[TIP]\nThe `>` symbol can be thought of as meaning \"includes\".\n\nRole hierarchies offer a convenient means of simplifying the access-control configuration data for your application and/or reducing the number of authorities which you need to assign to a user.\nFor more complex requirements you may wish to define a logical mapping between the specific access-rights your application requires and the roles that are assigned to users, translating between the two when loading the user information.\n\n[[authz-legacy-note]]\n== Legacy Authorization Components\n\n[NOTE]\nSpring Security contains some legacy components.\nSince they are not yet removed, documentation is included for historical purposes.\nTheir recommended replacements are above.\n\nWhen accessing legacy authorization components, please also include the `spring-security-access` dependency like so:\n\n[tabs]\n======\nMaven::\n+\n[source,xml,role=\"primary\"]\n----\n<dependency>\n    <groupId>org.springframework.security</groupId>\n    <artifactId>spring-security-access</artifactId>\n</dependency>\n----\n\nGradle::\n+\n[source,groovy,role=\"primary\"]\n----\nimplementation('org.springframework.security:spring-security-access')\n----\n======\n\n[[authz-access-decision-manager]]\n=== The AccessDecisionManager\nThe `AccessDecisionManager` is called by the `AbstractSecurityInterceptor` and is responsible for making final access control decisions.\nThe `AccessDecisionManager` interface contains three methods:\n\n[source,java]\n----\nvoid decide(Authentication authentication, Object secureObject,\n\tCollection<ConfigAttribute> attrs) throws AccessDeniedException;\n\nboolean supports(ConfigAttribute attribute);\n\nboolean supports(Class clazz);\n----\n\nThe `decide` method of the `AccessDecisionManager` is passed all the relevant information it needs to make an authorization decision.\nIn particular, passing the secure `Object` lets those arguments contained in the actual secure object invocation be inspected.\nFor example, assume the secure object is a `MethodInvocation`.\nYou can query the `MethodInvocation` for any `Customer` argument and then implement some sort of security logic in the `AccessDecisionManager` to ensure the principal is permitted to operate on that customer.\nImplementations are expected to throw an `AccessDeniedException` if access is denied.\n\nThe `supports(ConfigAttribute)` method is called by the `AbstractSecurityInterceptor` at startup time to determine if the `AccessDecisionManager` can process the passed `ConfigAttribute`.\nThe `supports(Class)` method is called by a security interceptor implementation to ensure the configured `AccessDecisionManager` supports the type of secure object that the security interceptor presents.\n\n[[authz-voting-based]]\n=== Voting-Based AccessDecisionManager Implementations\nWhile users can implement their own `AccessDecisionManager` to control all aspects of authorization, Spring Security includes several `AccessDecisionManager` implementations that are based on voting.\n<<authz-access-voting>> describes the relevant classes.\n\nThe following image shows the `AccessDecisionManager` interface:\n\n[[authz-access-voting]]\n.Voting Decision Manager\n[.invert-dark]\nimage::{figures}/access-decision-voting.png[]\n\nBy using this approach, a series of `AccessDecisionVoter` implementations are polled on an authorization decision.\nThe `AccessDecisionManager` then decides whether or not to throw an `AccessDeniedException` based on its assessment of the votes.\n\nThe `AccessDecisionVoter` interface has three methods:\n\n[source,java]\n----\nint vote(Authentication authentication, Object object, Collection<ConfigAttribute> attrs);\n\nboolean supports(ConfigAttribute attribute);\n\nboolean supports(Class clazz);\n----\n\nConcrete implementations return an `int`, with possible values being reflected in the `AccessDecisionVoter` static fields named `ACCESS_ABSTAIN`, `ACCESS_DENIED` and `ACCESS_GRANTED`.\nA voting implementation returns `ACCESS_ABSTAIN` if it has no opinion on an authorization decision.\nIf it does have an opinion, it must return either `ACCESS_DENIED` or `ACCESS_GRANTED`.\n\nThere are three concrete `AccessDecisionManager` implementations provided with Spring Security to tally the votes.\nThe `ConsensusBased` implementation grants or denies access based on the consensus of non-abstain votes.\nProperties are provided to control behavior in the event of an equality of votes or if all votes are abstain.\nThe `AffirmativeBased` implementation grants access if one or more `ACCESS_GRANTED` votes were received (in other words, a deny vote will be ignored, provided there was at least one grant vote).\nLike the `ConsensusBased` implementation, there is a parameter that controls the behavior if all voters abstain.\nThe `UnanimousBased` provider expects unanimous `ACCESS_GRANTED` votes in order to grant access, ignoring abstains.\nIt denies access if there is any `ACCESS_DENIED` vote.\nLike the other implementations, there is a parameter that controls the behavior if all voters abstain.\n\nYou can implement a custom `AccessDecisionManager` that tallies votes differently.\nFor example, votes from a particular `AccessDecisionVoter` might receive additional weighting, while a deny vote from a particular voter may have a veto effect.\n\n[[authz-role-voter]]\n==== RoleVoter\nThe most commonly used `AccessDecisionVoter` provided with Spring Security is the `RoleVoter`, which treats configuration attributes as role names and votes to grant access if the user has been assigned that role.\n\nIt votes if any `ConfigAttribute` begins with the `ROLE_` prefix.\nIt votes to grant access if there is a `GrantedAuthority` that returns a `String` representation (from the `getAuthority()` method) exactly equal to one or more `ConfigAttributes` that start with the `ROLE_` prefix.\nIf there is no exact match of any `ConfigAttribute` starting with `ROLE_`, `RoleVoter` votes to deny access.\nIf no `ConfigAttribute` begins with `ROLE_`, the voter abstains.\n\n\n[[authz-authenticated-voter]]\n==== AuthenticatedVoter\nAnother voter which we have implicitly seen is the `AuthenticatedVoter`, which can be used to differentiate between anonymous, fully-authenticated, and remember-me authenticated users.\nMany sites allow certain limited access under remember-me authentication but require a user to confirm their identity by logging in for full access.\n\nWhen we have used the `IS_AUTHENTICATED_ANONYMOUSLY` attribute to grant anonymous access, this attribute was being processed by the `AuthenticatedVoter`.\nFor more information, see\njavadoc:org.springframework.security.access.vote.AuthenticatedVoter[].\n\n\n[[authz-custom-voter]]\n==== Custom Voters\nYou can also implement a custom `AccessDecisionVoter` and put just about any access-control logic you want in it.\nIt might be specific to your application (business-logic related) or it might implement some security administration logic.\nFor example, on the Spring web site, you can find a https://spring.io/blog/2009/01/03/spring-security-customization-part-2-adjusting-secured-session-in-real-time[blog article] that describes how to use a voter to deny access in real-time to users whose accounts have been suspended.\n\n[[authz-after-invocation]]\n.After Invocation Implementation\n[.invert-dark]\nimage::{figures}/after-invocation.png[]\n\nLike many other parts of Spring Security, `AfterInvocationManager` has a single concrete implementation, `AfterInvocationProviderManager`, which polls a list of ``AfterInvocationProvider``s.\nEach `AfterInvocationProvider` is allowed to modify the return object or throw an `AccessDeniedException`.\nIndeed multiple providers can modify the object, as the result of the previous provider is passed to the next in the list.\n\nPlease be aware that if you're using `AfterInvocationManager`, you will still need configuration attributes that allow the ``MethodSecurityInterceptor``'s `AccessDecisionManager` to allow an operation.\nIf you're using the typical Spring Security included `AccessDecisionManager` implementations, having no configuration attributes defined for a particular secure method invocation will cause each `AccessDecisionVoter` to abstain from voting.\nIn turn, if the `AccessDecisionManager` property           \"`allowIfAllAbstainDecisions`\" is `false`, an `AccessDeniedException` will be thrown.\nYou may avoid this potential issue by either (i) setting \"`allowIfAllAbstainDecisions`\" to `true` (although this is generally not recommended) or (ii) simply ensure that there is at least one configuration attribute that an `AccessDecisionVoter` will vote to grant access for.\nThis latter (recommended) approach is usually achieved through a `ROLE_USER` or `ROLE_AUTHENTICATED` configuration attribute.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc",
    "content": "[[servlet-authorization-authorizationfilter]]\n= Authorize HttpServletRequests\n:figures: servlet/authorization\n\nSpring Security allows you to xref:servlet/authorization/index.adoc[model your authorization] at the request level.\nFor example, with Spring Security you can say that all pages under `/admin` require one authority while all other pages simply require authentication.\n\nBy default, Spring Security requires that every request be authenticated.\nThat said, any time you use xref:servlet/configuration/java.adoc#jc-httpsecurity[an `HttpSecurity` instance], it's necessary to declare your authorization rules.\n\n[[activate-request-security]]\nWhenever you have an `HttpSecurity` instance, you should at least do:\n\n.Use authorizeHttpRequests\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .anyRequest().authenticated()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(anyRequest, authenticated)\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n</http>\n----\n======\n\nThis tells Spring Security that any endpoint in your application requires that the security context at a minimum be authenticated in order to allow it.\n\nIn many cases, your authorization rules will be more sophisticated than that, so please consider the following use cases:\n\n* I have an app that uses `authorizeRequests` and I want to <<migrate-authorize-requests,migrate it to `authorizeHttpRequests`>>\n* I want to <<request-authorization-architecture,understand how the `AuthorizationFilter` components work>>\n* I want to <<match-requests, match requests>> based on a pattern; specifically <<match-by-regex,regex>>\n* I want to match request, and I map Spring MVC to <<mvc-not-default-servlet, something other than the default servlet>>\n* I want to <<authorize-requests, authorize requests>>\n* I want to <<match-by-custom, match a request programmatically>>\n* I want to <<authorize-requests, authorize a request programmatically>>\n* I want to <<remote-authorization-manager, delegate request authorization>> to a policy agent\n* I want to <<customizing-authorization-managers,customize how authorization managers are created>>\n\n[[request-authorization-architecture]]\n== Understanding How Request Authorization Components Work\n\n[NOTE]\nThis section builds on xref:servlet/architecture.adoc#servlet-architecture[Servlet Architecture and Implementation] by digging deeper into how xref:servlet/authorization/index.adoc#servlet-authorization[authorization] works at the request level in Servlet-based applications.\n\n.Authorize HttpServletRequest\n[.invert-dark]\nimage::{figures}/authorizationfilter.png[]\n\n* image:{icondir}/number_1.png[] First, the `AuthorizationFilter` constructs a `Supplier` that retrieves an  xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] from the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].\n* image:{icondir}/number_2.png[] Second, it passes the `Supplier<Authentication>` and the `HttpServletRequest` to the xref:servlet/architecture.adoc#authz-authorization-manager[`AuthorizationManager`].\nThe `AuthorizationManager` matches the request to the patterns in `authorizeHttpRequests`, and runs the corresponding rule.\n** image:{icondir}/number_3.png[] If authorization is denied, xref:servlet/authorization/events.adoc[an `AuthorizationDeniedEvent` is published], and an `AccessDeniedException` is thrown.\nIn this case the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] handles the `AccessDeniedException`.\n** image:{icondir}/number_4.png[] If access is granted, xref:servlet/authorization/events.adoc[an `AuthorizationGrantedEvent` is published] and `AuthorizationFilter` continues with the xref:servlet/architecture.adoc#servlet-filters-review[FilterChain] which allows the application to process normally.\n\n=== `AuthorizationFilter` Is Last By Default\n\nThe `AuthorizationFilter` is last in xref:servlet/architecture.adoc#servlet-filterchain-figure[the Spring Security filter chain] by default.\nThis means that Spring Security's xref:servlet/authentication/index.adoc[authentication filters], xref:servlet/exploits/index.adoc[exploit protections], and other filter integrations do not require authorization.\nIf you add filters of your own before the `AuthorizationFilter`, they will also not require authorization; otherwise, they will.\n\nA place where this typically becomes important is when you are adding {spring-framework-reference-url}web.html#spring-web[Spring MVC] endpoints.\nBecause they are executed by the {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`] and this comes after the `AuthorizationFilter`, your endpoints need to be <<authorizing-endpoints,included in `authorizeHttpRequests` to be permitted>>.\n\n=== All Dispatches Are Authorized\n\nThe `AuthorizationFilter` runs not just on every request, but on every dispatch.\nThis means that the `REQUEST` dispatch needs authorization, but also ``FORWARD``s, ``ERROR``s, and ``INCLUDE``s.\n\nFor example, {spring-framework-reference-url}web.html#spring-web[Spring MVC] can `FORWARD` the request to a view resolver that renders a Thymeleaf template, like so:\n\n.Sample Forwarding Spring MVC Controller\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class MyController {\n    @GetMapping(\"/endpoint\")\n    public String endpoint() {\n        return \"endpoint\";\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass MyController {\n    @GetMapping(\"/endpoint\")\n    fun endpoint(): String {\n        return \"endpoint\"\n    }\n}\n----\n======\n\nIn this case, authorization happens twice; once for authorizing `/endpoint` and once for forwarding to Thymeleaf to render the \"endpoint\" template.\n\nFor that reason, you may want to <<match-by-dispatcher-type, permit all `FORWARD` dispatches>>.\n\nAnother example of this principle is {spring-boot-reference-url}reference/web/servlet.html#web.servlet.spring-mvc.error-handling[how Spring Boot handles errors].\nIf the container catches an exception, say like the following:\n\n.Sample Erroring Spring MVC Controller\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class MyController {\n    @GetMapping(\"/endpoint\")\n    public String endpoint() {\n        throw new UnsupportedOperationException(\"unsupported\");\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass MyController {\n    @GetMapping(\"/endpoint\")\n    fun endpoint(): String {\n        throw UnsupportedOperationException(\"unsupported\")\n    }\n}\n----\n======\n\nthen Boot will dispatch it to the `ERROR` dispatch.\n\nIn that case, authorization also happens twice; once for authorizing `/endpoint` and once for dispatching the error.\n\nFor that reason, you may want to <<match-by-dispatcher-type, permit all `ERROR` dispatches>>.\n\n=== `Authentication` Lookup is Deferred\n\nRemember that xref:servlet/authorization/architecture.adoc#_the_authorizationmanager[the `AuthorizationManager` API uses a `Supplier<Authentication>`].\n\nThis matters with `authorizeHttpRequests` when requests are <<authorize-requests,always permitted or always denied>>.\nIn those cases, xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] is not queried, making for a faster request.\n\n[[authorizing-endpoints]]\n== Authorizing an Endpoint\n\nYou can configure Spring Security to have different rules by adding more rules in order of precedence.\n\nIf you want to require that `/endpoint` only be accessible by end users with the `USER` authority, then you can do:\n\n.Authorize an Endpoint\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain web(HttpSecurity http) throws Exception {\n    http\n        .authorizeHttpRequests((authorize) -> authorize\n\t    .requestMatchers(\"/endpoint\").hasAuthority(\"USER\")\n            .anyRequest().authenticated()\n        );\n        // ...\n\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun web(http: HttpSecurity): SecurityFilterChain {\n    http {\n        authorizeHttpRequests {\n            authorize(\"/endpoint\", hasAuthority(\"USER\"))\n            authorize(anyRequest, authenticated)\n        }\n    }\n\n    return http.build()\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-url pattern=\"/endpoint\" access=\"hasAuthority('USER')\"/>\n    <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n</http>\n----\n======\n\nAs you can see, the declaration can be broken up in to pattern/rule pairs.\n\n`AuthorizationFilter` processes these pairs in the order listed, applying only the first match to the request.\nThis means that even though `/**` would also match for `/endpoint` the above rules are not a problem.\nThe way to read the above rules is \"if the request is `/endpoint`, then require the `USER` authority; else, only require authentication\".\n\nSpring Security supports several patterns and several rules; you can also programmatically create your own of each.\n\nOnce authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:\n\n.Test Endpoint Authorization\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@WithMockUser(authorities=\"USER\")\n@Test\nvoid endpointWhenUserAuthorityThenAuthorized() {\n    this.mvc.perform(get(\"/endpoint\"))\n        .andExpect(status().isOk());\n}\n\n@WithMockUser\n@Test\nvoid endpointWhenNotUserAuthorityThenForbidden() {\n    this.mvc.perform(get(\"/endpoint\"))\n        .andExpect(status().isForbidden());\n}\n\n@Test\nvoid anyWhenUnauthenticatedThenUnauthorized() {\n    this.mvc.perform(get(\"/any\"))\n        .andExpect(status().isUnauthorized());\n}\n----\n======\n\n[[match-requests]]\n== Matching Requests\n\nAbove you've already seen <<authorizing-endpoints, two ways to match requests>>.\n\nThe first you saw was the simplest, which is to match any request.\n\nThe second is to match by a URI pattern.\nSpring Security supports two languages for URI pattern-matching: <<match-by-ant,Ant>> (as seen above) and <<match-by-regex,Regular Expressions>>.\n\n[[match-by-ant]]\n=== Matching Using Ant\nAnt is the default language that Spring Security uses to match requests.\n\nYou can use it to match a single endpoint or a directory, and you can even capture placeholders for later use.\nYou can also refine it to match a specific set of HTTP methods.\n\nLet's say that you instead of wanting to match the `/endpoint` endpoint, you want to match all endpoints under the `/resource` directory.\nIn that case, you can do something like the following:\n\n.Match with Ant\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .requestMatchers(\"/resource/**\").hasAuthority(\"USER\")\n        .anyRequest().authenticated()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(\"/resource/**\", hasAuthority(\"USER\"))\n        authorize(anyRequest, authenticated)\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-url pattern=\"/resource/**\" access=\"hasAuthority('USER')\"/>\n    <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n</http>\n----\n======\n\nThe way to read this is \"if the request is `/resource` or some subdirectory, require the `USER` authority; otherwise, only require authentication\"\n\nYou can also extract path values from the request, as seen below:\n\n.Authorize and Extract\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .requestMatchers(\"/resource/{name}\").access(new WebExpressionAuthorizationManager(\"#name == authentication.name\"))\n        .anyRequest().authenticated()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(\"/resource/{name}\", WebExpressionAuthorizationManager(\"#name == authentication.name\"))\n        authorize(anyRequest, authenticated)\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-url pattern=\"/resource/{name}\" access=\"#name == authentication.name\"/>\n    <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n</http>\n----\n======\n\nOnce authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:\n\n.Test Directory Authorization\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@WithMockUser(authorities=\"USER\")\n@Test\nvoid endpointWhenUserAuthorityThenAuthorized() {\n    this.mvc.perform(get(\"/resource/jon\"))\n        .andExpect(status().isOk());\n}\n\n@WithMockUser\n@Test\nvoid endpointWhenNotUserAuthorityThenForbidden() {\n    this.mvc.perform(get(\"/resource/jon\"))\n        .andExpect(status().isForbidden());\n}\n\n@Test\nvoid anyWhenUnauthenticatedThenUnauthorized() {\n    this.mvc.perform(get(\"/any\"))\n        .andExpect(status().isUnauthorized());\n}\n----\n======\n\n[NOTE]\nSpring Security only matches paths.\nIf you want to match query parameters, you will need a custom request matcher.\n\n[[match-by-regex]]\n=== Matching Using Regular Expressions\nSpring Security supports matching requests against a regular expression.\nThis can come in handy if you want to apply more strict matching criteria than `**` on a subdirectory.\n\nFor example, consider a path that contains the username and the rule that all usernames must be alphanumeric.\nYou can use javadoc:org.springframework.security.web.util.matcher.RegexRequestMatcher[] to respect this rule, like so:\n\n.Match with Regex\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .requestMatchers(RegexRequestMatcher.regexMatcher(\"/resource/[A-Za-z0-9]+\")).hasAuthority(\"USER\")\n        .anyRequest().denyAll()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(RegexRequestMatcher.regexMatcher(\"/resource/[A-Za-z0-9]+\"), hasAuthority(\"USER\"))\n        authorize(anyRequest, denyAll)\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-url request-matcher=\"regex\" pattern=\"/resource/[A-Za-z0-9]+\" access=\"hasAuthority('USER')\"/>\n    <intercept-url pattern=\"/**\" access=\"denyAll\"/>\n</http>\n----\n======\n\n[[match-by-httpmethod]]\n=== Matching By Http Method\n\nYou can also match rules by HTTP method.\nOne place where this is handy is when authorizing by permissions granted, like being granted a `read` or `write` privilege.\n\nTo require all ``GET``s to have the `read` permission and all ``POST``s to have the `write` permission, you can do something like this:\n\n.Match by HTTP Method\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .requestMatchers(HttpMethod.GET).hasAuthority(\"read\")\n        .requestMatchers(HttpMethod.POST).hasAuthority(\"write\")\n        .anyRequest().denyAll()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(HttpMethod.GET, hasAuthority(\"read\"))\n        authorize(HttpMethod.POST, hasAuthority(\"write\"))\n        authorize(anyRequest, denyAll)\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-url http-method=\"GET\" pattern=\"/**\" access=\"hasAuthority('read')\"/>\n    <intercept-url http-method=\"POST\" pattern=\"/**\" access=\"hasAuthority('write')\"/>\n    <intercept-url pattern=\"/**\" access=\"denyAll\"/>\n</http>\n----\n======\n\nThese authorization rules should read as: \"if the request is a GET, then require `read` permission; else, if the request is a POST, then require `write` permission; else, deny the request\"\n\n[TIP]\nDenying the request by default is a healthy security practice since it turns the set of rules into an allow list.\n\nOnce authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:\n\n.Test Http Method Authorization\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@WithMockUser(authorities=\"read\")\n@Test\nvoid getWhenReadAuthorityThenAuthorized() {\n    this.mvc.perform(get(\"/any\"))\n        .andExpect(status().isOk());\n}\n\n@WithMockUser\n@Test\nvoid getWhenNoReadAuthorityThenForbidden() {\n    this.mvc.perform(get(\"/any\"))\n        .andExpect(status().isForbidden());\n}\n\n@WithMockUser(authorities=\"write\")\n@Test\nvoid postWhenWriteAuthorityThenAuthorized() {\n    this.mvc.perform(post(\"/any\").with(csrf()))\n        .andExpect(status().isOk());\n}\n\n@WithMockUser(authorities=\"read\")\n@Test\nvoid postWhenNoWriteAuthorityThenForbidden() {\n    this.mvc.perform(post(\"/any\").with(csrf()))\n        .andExpect(status().isForbidden());\n}\n----\n======\n\n[[match-by-dispatcher-type]]\n=== Matching By Dispatcher Type\n\n[NOTE]\nThis feature is not currently supported in XML\n\nAs stated earlier, Spring Security <<_all_dispatches_are_authorized, authorizes all dispatcher types by default>>.\nAnd even though xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[the security context] established on the `REQUEST` dispatch carries over to subsequent dispatches, subtle mismatches can sometimes cause an unexpected `AccessDeniedException`.\n\nTo address that, you can configure Spring Security Java configuration to allow dispatcher types like `FORWARD` and `ERROR`, like so:\n\n.Match by Dispatcher Type\n[tabs]\n======\nJava::\n+\n[source,java,role=\"secondary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()\n        .requestMatchers(\"/endpoint\").permitAll()\n        .anyRequest().denyAll()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(DispatcherTypeRequestMatcher(DispatcherType.FORWARD), permitAll)\n        authorize(DispatcherTypeRequestMatcher(DispatcherType.ERROR), permitAll)\n        authorize(\"/endpoint\", permitAll)\n        authorize(anyRequest, denyAll)\n    }\n}\n----\n======\n\n[[match-by-mvc]]\n=== Matching by Servlet Path\n\nGenerally speaking, you can use `requestMatchers(String)` as demonstrated above.\n\nHowever, if you have authorization rules from multiple servlets, you need to specify those:\n\n.Match by PathPatternRequestMatcher\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.withDefaults;\n\n@Bean\nSecurityFilterChain appEndpoints(HttpSecurity http) {\n\tPathPatternRequestMatcher.Builder mvc = withDefaults().basePath(\"/spring-mvc\");\n\thttp\n        .authorizeHttpRequests((authorize) -> authorize\n            .requestMatchers(mvc.matcher(\"/admin/**\")).hasAuthority(\"admin\")\n            .requestMatchers(mvc.matcher(\"/my/controller/**\")).hasAuthority(\"controller\")\n            .anyRequest().authenticated()\n        );\n\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun appEndpoints(http: HttpSecurity): SecurityFilterChain {\n    http {\n        authorizeHttpRequests {\n            authorize(\"/spring-mvc\", \"/admin/**\", hasAuthority(\"admin\"))\n            authorize(\"/spring-mvc\", \"/my/controller/**\", hasAuthority(\"controller\"))\n            authorize(anyRequest, authenticated)\n        }\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-url servlet-path=\"/spring-mvc\" pattern=\"/admin/**\" access=\"hasAuthority('admin')\"/>\n    <intercept-url servlet-path=\"/spring-mvc\" pattern=\"/my/controller/**\" access=\"hasAuthority('controller')\"/>\n    <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n</http>\n----\n======\n\nThis is because Spring Security requires all URIs to be absolute (minus the context path).\n\n[TIP]\n=====\nThere are several other components that create request matchers for you like {spring-boot-api-url}org/springframework/boot/security/autoconfigure/web/servlet/PathRequest.html[`PathRequest#toStaticResources#atCommonLocations`]\n=====\n\n[[match-by-custom]]\n=== Using a Custom Matcher\n\n[NOTE]\nThis feature is not currently supported in XML\n\nIn Java configuration, you can create your own javadoc:org.springframework.security.web.util.matcher.RequestMatcher[] and supply it to the DSL like so:\n\n.Authorize by Dispatcher Type\n[tabs]\n======\nJava::\n+\n[source,java,role=\"secondary\"]\n----\nRequestMatcher printview = (request) -> request.getParameter(\"print\") != null;\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .requestMatchers(printview).hasAuthority(\"print\")\n        .anyRequest().authenticated()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval printview: RequestMatcher = { (request) -> request.getParameter(\"print\") != null }\nhttp {\n    authorizeHttpRequests {\n        authorize(printview, hasAuthority(\"print\"))\n        authorize(anyRequest, authenticated)\n    }\n}\n----\n======\n\n[TIP]\nBecause javadoc:org.springframework.security.web.util.matcher.RequestMatcher[] is a functional interface, you can supply it as a lambda in the DSL.\nHowever, if you want to extract values from the request, you will need to have a concrete class since that requires overriding a `default` method.\n\nOnce authorized, you can test it using xref:servlet/test/method.adoc#test-method-withmockuser[Security's test support] in the following way:\n\n.Test Custom Authorization\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@WithMockUser(authorities=\"print\")\n@Test\nvoid printWhenPrintAuthorityThenAuthorized() {\n    this.mvc.perform(get(\"/any?print\"))\n        .andExpect(status().isOk());\n}\n\n@WithMockUser\n@Test\nvoid printWhenNoPrintAuthorityThenForbidden() {\n    this.mvc.perform(get(\"/any?print\"))\n        .andExpect(status().isForbidden());\n}\n----\n======\n\n[[authorize-requests]]\n== Authorizing Requests\n\nOnce a request is matched, you can authorize it in several ways <<match-requests, already seen>> like `permitAll`, `denyAll`, and `hasAuthority`.\n\nAs a quick summary, here are the authorization rules built into the DSL:\n\n* `permitAll` - The request requires no authorization and is a public endpoint; note that in this case, xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] is never retrieved from the session\n* `denyAll` - The request is not allowed under any circumstances; note that in this case, the `Authentication` is never retrieved from the session\n* `hasAuthority` - The request requires that the `Authentication` have xref:servlet/authorization/architecture.adoc#authz-authorities[a `GrantedAuthority`] that matches the given value\n* `hasRole` - A shortcut for `hasAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix\n* `hasAnyAuthority` - The request requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values\n* `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix\n* `hasAllRoles` - A shortcut for `hasAllAuthorities` that prefixes `ROLE_` or whatever is configured as the default prefix\n* `hasAllAuthorities` - The request requires that the `Authentication` have a `GrantedAuthority` that matches all of the given values\n* `access` - The request uses this custom `AuthorizationManager` to determine access\n\nHaving now learned the patterns, rules, and how they can be paired together, you should be able to understand what is going on in this more complex example:\n\n.Authorize Requests\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static jakarta.servlet.DispatcherType.*;\n\nimport static org.springframework.security.authorization.AuthorizationManagers.allOf;\nimport static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority;\nimport static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole;\n\n@Bean\nSecurityFilterChain web(HttpSecurity http) throws Exception {\n\thttp\n\t\t// ...\n\t\t.authorizeHttpRequests((authorize) -> authorize                                  // <1>\n            .dispatcherTypeMatchers(FORWARD, ERROR).permitAll() // <2>\n\t\t\t.requestMatchers(\"/static/**\", \"/signup\", \"/about\").permitAll()         // <3>\n\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")                             // <4>\n\t\t\t.requestMatchers(\"/db/**\").hasAllAuthorities(\"db\", \"ROLE_ADMIN\")   // <5>\n\t\t\t.anyRequest().denyAll()                                                // <6>\n\t\t);\n\n\treturn http.build();\n}\n----\n======\n<1> There are multiple authorization rules specified.\nEach rule is considered in the order they were declared.\n<2> Dispatches `FORWARD` and `ERROR` are permitted to allow {spring-framework-reference-url}web.html#spring-web[Spring MVC] to render views and Spring Boot to render errors\n<3> We specified multiple URL patterns that any user can access.\nSpecifically, any user can access a request if the URL starts with \"/static/\", equals \"/signup\", or equals \"/about\".\n<4> Any URL that starts with \"/admin/\" will be restricted to users who have the role \"ROLE_ADMIN\".\nYou will notice that since we are invoking the `hasRole` method we do not need to specify the \"ROLE_\" prefix.\n<5> Any URL that starts with \"/db/\" requires the user to have both been granted the \"db\" permission as well as be a \"ROLE_ADMIN\".\nYou will notice that since we are using the `hasAllAuthorities` expression we must specify the \"ROLE_\" prefix.\n<6> Any URL that has not already been matched on is denied access.\nThis is a good strategy if you do not want to accidentally forget to update your authorization rules.\n\n[[customizing-authorization-managers]]\n== Customizing Authorization Managers\n\nWhen you use the `authorizeHttpRequests` DSL, Spring Security takes care of creating the appropriate `AuthorizationManager` instances for you.\nIn certain cases, you may want to customize what is created in order to have complete control over how authorization decisions are made xref:servlet/authorization/architecture.adoc#authz-delegate-authorization-manager[at the framework level].\n\nIn order to take control of creating instances of `AuthorizationManager` for authorizing HTTP requests, you can create a custom xref:servlet/authorization/architecture.adoc#authz-authorization-manager-factory[`AuthorizationManagerFactory`].\nFor example, let's say you want to create a convention that authenticated users must be authenticated _AND_ have the `USER` role.\nTo do this, you can create a custom implementation for HTTP requests as in the following example:\n\ninclude-code::./CustomHttpRequestsAuthorizationManagerFactory[tag=class,indent=0]\n\nNow, whenever you <<activate-request-security,require authentication>>, Spring Security will automatically invoke your custom factory to create an instance of `AuthorizationManager` that requires authentication _AND_ the `USER` role.\n\n[TIP]\nWe use this as a simple example of creating a custom `AuthorizationManagerFactory`, though it is also possible (and often simpler) to replace a specific `AuthorizationManager` only for a particular request.\nSee <<remote-authorization-manager>> for an example.\n\n[[authorization-expressions]]\n== Expressing Authorization with SpEL\n\nWhile using a concrete `AuthorizationManager` is recommended, there are some cases where an expression is necessary, like with `<intercept-url>` or with JSP Taglibs.\nFor that reason, this section will focus on examples from those domains.\n\nGiven that, let's cover Spring Security's Web Security Authorization SpEL API a bit more in depth.\n\nSpring Security encapsulates all of its authorization fields and methods in a set of root objects.\nThe most generic root object is called `SecurityExpressionRoot` and it forms the basis for `WebSecurityExpressionRoot`.\nSpring Security supplies this root object to `StandardEvaluationContext` when preparing to evaluate an authorization expression.\n\n[[using-authorization-expression-fields-and-methods]]\n=== Using Authorization Expression Fields and Methods\n\nThe first thing this provides is an enhanced set of authorization fields and methods to your SpEL expressions.\nWhat follows is a quick overview of the most common methods:\n\n* `permitAll` - The request requires no authorization to be invoked; note that in this case, xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] is never retrieved from the session\n* `denyAll` - The request is not allowed under any circumstances; note that in this case, the `Authentication` is never retrieved from the session\n* `hasAuthority` - The request requires that the `Authentication` have xref:servlet/authorization/architecture.adoc#authz-authorities[a `GrantedAuthority`] that matches the given value\n* `hasRole` - A shortcut for `hasAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix\n* `hasAnyAuthority` - The request requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values\n* `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix\n* `hasPermission` - A hook into your `PermissionEvaluator` instance for doing object-level authorization\n\nAnd here is a brief look at the most common fields:\n\n* `authentication` - The `Authentication` instance associated with this method invocation\n* `principal` - The `Authentication#getPrincipal` associated with this method invocation\n\nHaving now learned the patterns, rules, and how they can be paired together, you should be able to understand what is going on in this more complex example:\n\n.Authorize Requests Using SpEL\n[tabs]\n======\nXml::\n+\n[source,java,role=\"primary\"]\n----\n<http>\n    <intercept-url pattern=\"/static/**\" access=\"permitAll\"/> <1>\n    <intercept-url pattern=\"/admin/**\" access=\"hasRole('ADMIN')\"/> <2>\n    <intercept-url pattern=\"/db/**\" access=\"hasAuthority('db') and hasRole('ADMIN')\"/> <3>\n    <intercept-url pattern=\"/**\" access=\"denyAll\"/> <4>\n</http>\n----\n======\n<1> We specified a URL pattern that any user can access.\nSpecifically, any user can access a request if the URL starts with \"/static/\".\n<2> Any URL that starts with \"/admin/\" will be restricted to users who have the role \"ROLE_ADMIN\".\nYou will notice that since we are invoking the `hasRole` method we do not need to specify the \"ROLE_\" prefix.\n<3> Any URL that starts with \"/db/\" requires the user to have both been granted the \"db\" permission as well as be a \"ROLE_ADMIN\".\nYou will notice that since we are using the `hasRole` expression we do not need to specify the \"ROLE_\" prefix.\n<4> Any URL that has not already been matched on is denied access.\nThis is a good strategy if you do not want to accidentally forget to update your authorization rules.\n\n[[using_path_parameters]]\n=== Using Path Parameters\n\nAdditionally, Spring Security provides a mechanism for discovering path parameters so they can also be accessed in the SpEL expression as well.\n\nFor example, you can access a path parameter in your SpEL expression in the following way:\n\n.Authorize Request using SpEL path variable\n[tabs]\n======\nXml::\n+\n[source,xml,role=\"primary\"]\n----\n<http>\n    <intercept-url pattern=\"/resource/{name}\" access=\"#name == authentication.name\"/>\n    <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n</http>\n----\n======\n\nThis expression refers to the path variable after `/resource/` and requires that it is equal to `Authentication#getName`.\n\n[[remote-authorization-manager]]\n=== Use an Authorization Database, Policy Agent, or Other Service\nIf you want to configure Spring Security to use a separate service for authorization, you can create your own `AuthorizationManager` and match it to `anyRequest`.\n\nFirst, your `AuthorizationManager` may look something like this:\n\n.Open Policy Agent Authorization Manager\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic final class OpenPolicyAgentAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {\n    @Override\n    public AuthorizationResult authorize(Supplier<Authentication> authentication, RequestAuthorizationContext context) {\n        // make request to Open Policy Agent\n    }\n}\n----\n======\n\nThen, you can wire it into Spring Security in the following way:\n\n.Any Request Goes to Remote Service\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> authz) throws Exception {\n\thttp\n\t\t// ...\n\t\t.authorizeHttpRequests((authorize) -> authorize\n            .anyRequest().access(authz)\n\t\t);\n\n\treturn http.build();\n}\n----\n======\n\n[[favor-permitall]]\n=== Favor `permitAll` over `ignoring`\nWhen you have static resources it can be tempting to configure the filter chain to ignore these values.\nA more secure approach is to permit them using `permitAll` like so:\n\n.Permit Static Resources\n[tabs]\n======\nJava::\n+\n[source,java,role=\"secondary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .requestMatchers(\"/css/**\").permitAll()\n        .anyRequest().authenticated()\n    )\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(\"/css/**\", permitAll)\n        authorize(anyRequest, authenticated)\n    }\n}\n----\n======\n\nIt's more secure because even with static resources it's important to write secure headers, which Spring Security cannot do if the request is ignored.\n\nIn this past, this came with a performance tradeoff since the session was consulted by Spring Security on every request.\nAs of Spring Security 6, however, the session is no longer pinged unless required by the authorization rule.\nBecause the performance impact is now addressed, Spring Security recommends using at least `permitAll` for all requests.\n\n[[migrate-authorize-requests]]\n== Migrating from `authorizeRequests`\n\n[NOTE]\n`AuthorizationFilter` supersedes javadoc:org.springframework.security.web.access.intercept.FilterSecurityInterceptor[].\nTo remain backward compatible, `FilterSecurityInterceptor` remains the default.\nThis section discusses how `AuthorizationFilter` works and how to override the default configuration.\n\nThe javadoc:org.springframework.security.web.access.intercept.AuthorizationFilter[] provides xref:servlet/authorization/index.adoc#servlet-authorization[authorization] for ``HttpServletRequest``s.\nIt is inserted into the xref:servlet/architecture.adoc#servlet-filterchainproxy[FilterChainProxy] as one of the xref:servlet/architecture.adoc#servlet-security-filters[Security Filters].\n\nYou can override the default when you declare a `SecurityFilterChain`.\nInstead of using javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()[authorizeRequests], use `authorizeHttpRequests`, like so:\n\n.Use authorizeHttpRequests\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityFilterChain web(HttpSecurity http) throws AuthenticationException {\n    http\n        .authorizeHttpRequests((authorize) -> authorize\n            .anyRequest().authenticated();\n        )\n        // ...\n\n    return http.build();\n}\n----\n======\n\nThis improves on `authorizeRequests` in a number of ways:\n\n1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters.\nThis simplifies reuse and customization.\n2. Delays `Authentication` lookup.\nInstead of the authentication needing to be looked up for every request, it will only look it up in requests where an authorization decision requires authentication.\n3. Bean-based configuration support.\n\nWhen `authorizeHttpRequests` is used instead of `authorizeRequests`, then javadoc:org.springframework.security.web.access.intercept.AuthorizationFilter[] is used instead of javadoc:org.springframework.security.web.access.intercept.FilterSecurityInterceptor[].\n\n=== Migrating Expressions\n\nWhere possible, it is recommended that you use type-safe authorization managers instead of SpEL.\nFor Java configuration, javadoc:org.springframework.security.web.access.expression.WebExpressionAuthorizationManager[] is available to help migrate legacy SpEL.\n\nTo use `WebExpressionAuthorizationManager`, you can construct one with the expression you are trying to migrate, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n.requestMatchers(\"/test/**\").access(new WebExpressionAuthorizationManager(\"hasRole('ADMIN') && hasRole('USER')\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n.requestMatchers(\"/test/**\").access(WebExpressionAuthorizationManager(\"hasRole('ADMIN') && hasRole('USER')\"))\n----\n======\n\nTo migrate several, you can use `WebExpressionAuthorizationManager#withDefaults`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nWebExpressionAuthorizationManager.Builder authz = WebExpressionAuthorizationManager.withDefaults();\n.requestMatchers(\"/test/**\").access(authz.expression(\"hasRole('ADMIN') && hasRole('USER')\"))\n.requestMatchers(\"/test/**\").access(authz.expression(\"permitAll\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nvar authz = WebExpressionAuthorizationManager.withDefaults()\n.requestMatchers(\"/test/**\").access(authz.expression(\"hasRole('ADMIN') && hasRole('USER')\"))\n.requestMatchers(\"/test/**\").access(authz.expression(\"permitAll\"))\n----\n======\n\nIf you are referring to a bean in your expression like so: `@webSecurity.check(authentication, request)`, it's recommended that you instead call the bean directly, which will look something like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n.requestMatchers(\"/test/**\").access((authentication, context) ->\n    new AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest())))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n.requestMatchers(\"/test/**\").access((authentication, context): AuthorizationManager<RequestAuthorizationContext> ->\n    AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest())))\n----\n======\n\nFor complex instructions that include bean references as well as other expressions, it is recommended that you change those to implement `AuthorizationManager` and refer to them by calling `.access(AuthorizationManager)`.\n\nIf you are not able to do that, you can publish javadoc:org.springframework.security.web.access.expression.WebExpressionAuthorizationManager$Builder[] as a bean:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nWebExpressionAuthorizationManager.Builder authz() {\n\treturn WebExpressionAuthorizationManager.withDefaults();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authz(): WebExpressionAuthorizationManager.Builder {\n\treturn WebExpressionAuthorizationManager.withDefaults()\n}\n----\n======\n\nThen, expressions passed to that builder will be able to refer to beans.\n\n[[security-matchers]]\n== Security Matchers\n\nThe javadoc:org.springframework.security.web.util.matcher.RequestMatcher[] interface is used to determine if a request matches a given rule.\nWe use `securityMatchers` to determine if xref:servlet/configuration/java.adoc#jc-httpsecurity[a given `HttpSecurity`] should be applied to a given request.\nThe same way, we can use `requestMatchers` to determine the authorization rules that we should apply to a given request.\nLook at the following example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.securityMatcher(\"/api/**\")                            <1>\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(\"/api/user/**\").hasRole(\"USER\")   <2>\n\t\t\t\t.requestMatchers(\"/api/admin/**\").hasRole(\"ADMIN\") <3>\n\t\t\t\t.anyRequest().authenticated()                      <4>\n\t\t\t)\n\t\t\t.formLogin(withDefaults());\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nopen class SecurityConfig {\n\n    @Bean\n    open fun web(http: HttpSecurity): SecurityFilterChain {\n        http {\n            securityMatcher(\"/api/**\")                                           <1>\n            authorizeHttpRequests {\n                authorize(\"/api/user/**\", hasRole(\"USER\"))                       <2>\n                authorize(\"/api/admin/**\", hasRole(\"ADMIN\"))                     <3>\n                authorize(anyRequest, authenticated)                             <4>\n            }\n        }\n        return http.build()\n    }\n\n}\n----\n======\n\n<1> Configure `HttpSecurity` to only be applied to URLs that start with `/api/`\n<2> Allow access to URLs that start with `/api/user/` to users with the `USER` role\n<3> Allow access to URLs that start with `/api/admin/` to users with the `ADMIN` role\n<4> Any other request that doesn't match the rules above, will require authentication\n\nThe `securityMatcher(s)` and `requestMatcher(s)` methods will construct ``RequestMatcher``s using a javadoc:org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher$Builder[] bean, if available.\nYou can read more about the Spring MVC integration xref:servlet/integrations/mvc.adoc[here].\n\nIf you want to use a specific `RequestMatcher`, just pass an implementation to the `securityMatcher` and/or `requestMatcher` methods:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.withDefaults; <1>\nimport static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.securityMatcher(antMatcher(\"/api/**\"))                              <2>\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(withDefaults().matcher(\"/api/user/**\")).hasRole(\"USER\")     <3>\n\t\t\t\t.requestMatchers(regexMatcher(\"/api/admin/.*\")).hasRole(\"ADMIN\") <4>\n\t\t\t\t.requestMatchers(new MyCustomRequestMatcher()).hasRole(\"SUPERVISOR\")     <5>\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.formLogin(withDefaults());\n\t\treturn http.build();\n\t}\n}\n\npublic class MyCustomRequestMatcher implements RequestMatcher {\n\n    @Override\n    public boolean matches(HttpServletRequest request) {\n        // ...\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.withDefaults <1>\nimport org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher\n\n@Configuration\n@EnableWebSecurity\nopen class SecurityConfig {\n\n    @Bean\n    open fun web(http: HttpSecurity): SecurityFilterChain {\n        http {\n            securityMatcher(antMatcher(\"/api/**\"))                               <2>\n            authorizeHttpRequests {\n                authorize(withDefaults().matcher(\"/api/user/**\"), hasRole(\"USER\"))           <3>\n                authorize(regexMatcher(\"/api/admin/**\"), hasRole(\"ADMIN\"))       <4>\n                authorize(MyCustomRequestMatcher(), hasRole(\"SUPERVISOR\"))       <5>\n                authorize(anyRequest, authenticated)\n            }\n        }\n        return http.build()\n    }\n\n}\n----\n======\n\n<1> Import the static factory methods from `PathPatternRequestMatcher` and `RegexRequestMatcher` to create `RequestMatcher` instances.\n<2> Configure `HttpSecurity` to only be applied to URLs that start with `/api/`, using `PathPatternRequestMatcher`\n<3> Allow access to URLs that start with `/api/user/` to users with the `USER` role, using `PathPatternRequestMatcher`\n<4> Allow access to URLs that start with `/api/admin/` to users with the `ADMIN` role, using `RegexRequestMatcher`\n<5> Allow access to URLs that match the `MyCustomRequestMatcher` to users with the `SUPERVISOR` role, using a custom `RequestMatcher`\n\n== Further Reading\n\nNow that you have secured your application's requests, consider xref:servlet/authorization/method-security.adoc[securing its methods].\nYou can also read further on xref:servlet/test/index.adoc[testing your application] or on integrating Spring Security with other aspects of your application like xref:servlet/integrations/data.adoc[the data layer] or xref:servlet/integrations/observability.adoc[tracing and metrics].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authorization/events.adoc",
    "content": "[[servlet-events]]\n= Authorization Events\n\nFor each authorization that is denied, an `AuthorizationDeniedEvent` is fired.\nAlso, it's possible to fire an `AuthorizationGrantedEvent` for authorizations that are granted.\n\nTo listen for these events, you must first publish an `AuthorizationEventPublisher`.\n\nSpring Security's `SpringAuthorizationEventPublisher` will probably do fine.\nIt comes publishes authorization events using Spring's `ApplicationEventPublisher`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic AuthorizationEventPublisher authorizationEventPublisher\n        (ApplicationEventPublisher applicationEventPublisher) {\n    return new SpringAuthorizationEventPublisher(applicationEventPublisher);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizationEventPublisher\n        (applicationEventPublisher: ApplicationEventPublisher?): AuthorizationEventPublisher {\n    return SpringAuthorizationEventPublisher(applicationEventPublisher)\n}\n----\n======\n\nThen, you can use Spring's `@EventListener` support:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class AuthenticationEvents {\n\n    @EventListener\n    public void onFailure(AuthorizationDeniedEvent failure) {\n\t\t// ...\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass AuthenticationEvents {\n\n    @EventListener\n    fun onFailure(failure: AuthorizationDeniedEvent?) {\n        // ...\n    }\n}\n----\n======\n\n[[authorization-granted-events]]\n== Authorization Granted Events\n\nBecause ``AuthorizationGrantedEvent``s have the potential to be quite noisy, they are not published by default.\n\nIn fact, publishing these events will likely require some business logic on your part to ensure that your application is not inundated with noisy authorization events.\n\nYou can provide your own predicate that filters success events.\nFor example, the following publisher only publishes authorization grants where `ROLE_ADMIN` was required:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nAuthorizationEventPublisher authorizationEventPublisher() {\n    SpringAuthorizationEventPublisher eventPublisher = new SpringAuthorizationEventPublisher();\n    eventPublisher.setShouldPublishEvent((result) -> {\n        if (!result.isGranted()) {\n            return true;\n        }\n        if (result instanceof AuthorityAuthorizationDecision decision) {\n            Collection<GrantedAuthority> authorities = decision.getAuthorities();\n            return AuthorityUtils.authorityListToSet(authorities).contains(\"ROLE_ADMIN\");\n        }\n        return false;\n    });\n    return eventPublisher;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizationEventPublisher(): AuthorizationEventPublisher {\n    val eventPublisher = SpringAuthorizationEventPublisher()\n    eventPublisher.setShouldPublishEvent { (result) ->\n        if (!result.isGranted()) {\n            return true\n        }\n        if (decision is AuthorityAuthorizationDecision) {\n            val authorities = decision.getAuthorities()\n            return AuthorityUtils.authorityListToSet(authorities).contains(\"ROLE_ADMIN\")\n        }\n        return false\n    }\n    return eventPublisher\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authorization/index.adoc",
    "content": "[[servlet-authorization]]\n= Authorization\n:page-section-summary-toc: 1\n\nHaving established xref:servlet/authentication/index.adoc[how users will authenticate], you also need to configure your application's authorization rules.\n\nThe advanced authorization capabilities within Spring Security represent one of the most compelling reasons for its popularity.\nIrrespective of how you choose to authenticate (whether using a Spring Security-provided mechanism and provider or integrating with a container or other non-Spring Security authentication authority), the authorization services can be used within your application in a consistent and simple way.\n\nYou should consider attaching authorization rules to xref:servlet/authorization/authorize-http-requests.adoc[request URIs] and xref:servlet/authorization/method-security.adoc[methods] to begin.\nIn either case, you can listen and react to xref:servlet/authorization/events.adoc[authorization events] that each authorization check publishes.\nBelow there is also wealth of detail about xref:servlet/authorization/architecture.adoc[how Spring Security authorization works] and how, having established a basic model, it can be fine-tuned.\n\n[NOTE]\n====\nAs of Spring Security 7, the Access API (`AccessDecisionManager`, `AccessDecisionVoter`, etc.) are moved to a legacy module, `spring-security-access`.\nFor new applications, there is no need to include the dependency.\nFor older applications that have not yet migrated to the Authorization API, this module is available to assist your continued migration efforts.\n====\n\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/authorization/method-security.adoc",
    "content": "[[jc-method]]\n= Method Security\n:figures: servlet/authorization\n\nIn addition to xref:servlet/authorization/authorize-http-requests.adoc[modeling authorization at the request level], Spring Security also supports modeling at the method level.\n\n[[activate-method-security]]\nYou can activate it in your application by annotating any `@Configuration` class with `@EnableMethodSecurity` or adding `<method-security>` to any  XML configuration file, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableMethodSecurity\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableMethodSecurity\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security/>\n----\n======\n\nThen, you are immediately able to annotate any Spring-managed class or method with <<use-preauthorize, `@PreAuthorize`>>, <<use-postauthorize,`@PostAuthorize`>>, <<use-prefilter,`@PreFilter`>>, and <<use-postfilter,`@PostFilter`>> to authorize method invocations, including the input parameters and return values.\n\n[NOTE]\n{spring-boot-reference-url}reference/using/build-systems.html#using.build-systems.starters[Spring Boot Starter Security] does not activate method-level authorization by default.\n\nMethod Security supports many other use cases as well including <<use-aspectj, AspectJ support>>, <<use-programmatic-authorization,custom annotations>>, and several configuration points.\nConsider learning about the following use cases:\n\n* <<migration-enableglobalmethodsecurity, Migrating from `@EnableGlobalMethodSecurity`>>\n* Understanding <<method-security-architecture,how method security works>> and reasons to use it\n* Comparing <<request-vs-method,request-level and method-level authorization>>\n* Authorizing methods with <<use-preauthorize,`@PreAuthorize`>> and <<use-postauthorize,`@PostAuthorize`>>\n* Providing <<fallback-values-authorization-denied,fallback values when authorization is denied>>\n* Filtering methods with <<use-prefilter,`@PreFilter`>> and <<use-postfilter,`@PostFilter`>>\n* Authorizing methods with <<use-jsr250,JSR-250 annotations>>\n* Authorizing methods with <<use-aspectj,AspectJ expressions>>\n* Integrating with <<weave-aspectj,AspectJ byte-code weaving>>\n* Coordinating with <<changing-the-order,@Transactional and other AOP-based annotations>>\n* Customizing <<customizing-expression-handling,SpEL expression handling>>\n* Integrating with <<custom-authorization-managers,custom authorization systems>>\n\n[[method-security-architecture]]\n== How Method Security Works\n\nSpring Security's method authorization support is handy for:\n\n* Extracting fine-grained authorization logic; for example, when the method parameters and return values contribute to the authorization decision.\n* Enforcing security at the service layer\n* Stylistically favoring annotation-based over `HttpSecurity`-based configuration\n\nAnd since Method Security is built using {spring-framework-reference-url}core.html#aop-api[Spring AOP], you have access to all its expressive power to override Spring Security's defaults as needed.\n\nAs already mentioned, you begin by adding `@EnableMethodSecurity` to a `@Configuration` class or `<sec:method-security/>` in a Spring XML configuration file.\n\n[[use-method-security]]\n[NOTE]\n====\nThis annotation and XML element supercede `@EnableGlobalMethodSecurity` and `<sec:global-method-security/>`, respectively.\nThey offer the following improvements:\n\n1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters.\nThis simplifies reuse and customization.\n2. Favors direct bean-based configuration, instead of requiring extending `GlobalMethodSecurityConfiguration` to customize beans\n3. Is built using native Spring AOP, removing abstractions and allowing you to use Spring AOP building blocks to customize\n4. Checks for conflicting annotations to ensure an unambiguous security configuration\n5. Complies with JSR-250\n6. Enables `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` by default\n\nIf you are using `@EnableGlobalMethodSecurity` or `<global-method-security/>`, these are now deprecated, and you are encouraged to migrate.\n====\n\nMethod authorization is a combination of before- and after-method authorization.\nConsider a service bean that is annotated in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Service\npublic class MyCustomerService {\n    @PreAuthorize(\"hasAuthority('permission:read')\")\n    @PostAuthorize(\"returnObject.owner == authentication.name\")\n    public Customer readCustomer(String id) { ... }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Service\nopen class MyCustomerService {\n    @PreAuthorize(\"hasAuthority('permission:read')\")\n    @PostAuthorize(\"returnObject.owner == authentication.name\")\n    fun readCustomer(id: String): Customer { ... }\n}\n----\n======\n\nA given invocation to `MyCustomerService#readCustomer` may look something like this when Method Security <<activate-method-security,is activated>>:\n\n[.invert-dark]\nimage::{figures}/methodsecurity.png[]\n\n1. Spring AOP invokes its proxy method for `readCustomer`. Among the proxy's other advisors, it invokes an javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[] that matches <<annotation-method-pointcuts,the `@PreAuthorize` pointcut>>\n2. The interceptor invokes javadoc:org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager[`PreAuthorizeAuthorizationManager#authorize`]\n3. The authorization manager uses a `MethodSecurityExpressionHandler` to parse the annotation's <<authorization-expressions,SpEL expression>> and constructs a corresponding `EvaluationContext` from a `MethodSecurityExpressionRoot` containing xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[a `Supplier<Authentication>`] and `MethodInvocation`.\n4. The interceptor uses this context to evaluate the expression; specifically, it reads xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] from the `Supplier` and checks whether it has `permission:read` in its collection of xref:servlet/authorization/architecture.adoc#authz-authorities[authorities]\n5. If the evaluation passes, then Spring AOP proceeds to invoke the method.\n6. If not, the interceptor publishes an `AuthorizationDeniedEvent` and throws an javadoc:org.springframework.security.access.AccessDeniedException[] which xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[the `ExceptionTranslationFilter`] catches and returns a 403 status code to the response\n7. After the method returns, Spring AOP invokes an javadoc:org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor[] that matches <<annotation-method-pointcuts,the `@PostAuthorize` pointcut>>, operating the same as above, but with javadoc:org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager[]\n8. If the evaluation passes (in this case, the return value belongs to the logged-in user), processing continues normally\n9. If not, the interceptor publishes an `AuthorizationDeniedEvent` and throws an javadoc:org.springframework.security.access.AccessDeniedException[], which xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[the `ExceptionTranslationFilter`] catches and returns a 403 status code to the response\n\n[NOTE]\nIf the method is not being called in the context of an HTTP request, you will likely need to handle the `AccessDeniedException` yourself\n\n[[unanimous-based-authorization-decisions]]\n=== Multiple Annotations Are Computed In Series\n\nAs demonstrated above, if a method invocation involves multiple <<authorizing-with-annotations,Method Security annotations>>, each of those is processed one at a time.\nThis means that they can collectively be thought of as being \"anded\" together.\nIn other words, for an invocation to be authorized, all annotation inspections need to pass authorization.\n\n[[repeated-annotations]]\n=== Repeated Annotations Are Not Supported\n\nThat said, it is not supported to repeat the same annotation on the same method.\nFor example, you cannot place `@PreAuthorize` twice on the same method.\n\nInstead, use SpEL's boolean support or its support for delegating to a separate bean.\n\n[[annotation-method-pointcuts]]\n=== Each Annotation Has Its Own Pointcut\n\nEach annotation has its own pointcut instance that looks for that annotation or its <<meta-annotations,meta-annotation>> counterparts across the entire object hierarchy, starting at <<class-or-interface-annotations,the method and its enclosing class>>.\n\n// FIXME: AuthorizationMethodPointcuts is package private and Javadoc is not published You can see the specifics of this in javadoc:org.springframework.security.authorization.method.AuthorizationMethodPointcuts[].\n\n[[annotation-method-interceptors]]\n=== Each Annotation Has Its Own Method Interceptor\n\nEach annotation has its own dedicated method interceptor.\nThe reason for this is to make things more composable.\nFor example, if needed, you can disable the Spring Security defaults and <<_enabling_certain_annotations,publish only the `@PostAuthorize` method interceptor>>.\n\nThe method interceptors are as follows:\n\n* For <<use-preauthorize,`@PreAuthorize`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[`AuthorizationManagerBeforeMethodInterceptor#preAuthorize`], which in turn uses javadoc:org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager[]\n* For <<use-postauthorize,`@PostAuthorize`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor[`AuthorizationManagerAfterMethodInterceptor#postAuthorize`], which in turn uses javadoc:org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager[]\n* For <<use-prefilter,`@PreFilter`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor[]\n* For <<use-postfilter,`@PostFilter`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor[]\n* For <<use-secured,`@Secured`>>, Spring Security uses javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[`AuthorizationManagerBeforeMethodInterceptor#secured`], which in turn uses javadoc:org.springframework.security.authorization.method.SecuredAuthorizationManager[]\n* For JSR-250 annotations, Spring Security uses javadoc:org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor[`AuthorizationManagerBeforeMethodInterceptor#jsr250`], which in turn uses javadoc:org.springframework.security.authorization.method.Jsr250AuthorizationManager[]\n\nGenerally speaking, you can consider the following listing as representative of what interceptors Spring Security publishes when you add `@EnableMethodSecurity`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nstatic Advisor preAuthorizeMethodInterceptor() {\n    return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();\n}\n\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nstatic Advisor postAuthorizeMethodInterceptor() {\n    return AuthorizationManagerAfterMethodInterceptor.postAuthorize();\n}\n\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nstatic Advisor preFilterMethodInterceptor() {\n    return AuthorizationManagerBeforeMethodInterceptor.preFilter();\n}\n\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nstatic Advisor postFilterMethodInterceptor() {\n    return AuthorizationManagerAfterMethodInterceptor.postFilter();\n}\n----\n======\n\n[[favor-granting-authorities]]\n=== Favor Granting Authorities Over Complicated SpEL Expressions\n\nQuite often it can be tempting to introduce a complicated SpEL expression like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"hasAuthority('permission:read') || hasRole('ADMIN')\")\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"hasAuthority('permission:read') || hasRole('ADMIN')\")\n----\n======\n\n\nHowever, you could instead grant `permission:read` to those with `ROLE_ADMIN`.\nOne way to do this is with a `RoleHierarchy` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nstatic RoleHierarchy roleHierarchy() {\n    return RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > permission:read\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ncompanion object {\n    @Bean\n    fun roleHierarchy(): RoleHierarchy {\n        return RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > permission:read\")\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<bean id=\"roleHierarchy\"\n        class=\"org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl\" factory-method=\"fromHierarchy\">\n    <constructor-arg value=\"ROLE_ADMIN > permission:read\"/>\n</bean>\n----\n======\n\nand then <<customizing-expression-handling,set that in a `MethodSecurityExpressionHandler` instance>>.\nThis then allows you to have a simpler <<use-preauthorize,`@PreAuthorize`>> expression like this one:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"hasAuthority('permission:read')\")\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"hasAuthority('permission:read')\")\n----\n======\n\nOr, where possible, adapt application-specific authorization logic into granted authorities at login time.\n\n[[request-vs-method]]\n== Comparing Request-level vs Method-level Authorization\n\nWhen should you favor method-level authorization over xref:servlet/authorization/authorize-http-requests.adoc[request-level authorization]?\nSome of it comes down to taste; however, consider the following strengths list of each to help you decide.\n\n|===\n|| *request-level* | *method-level*\n| *authorization type* | coarse-grained | fine-grained\n| *configuration location* | declared in a config class | local to method declaration\n| *configuration style* | DSL | Annotations\n| *authorization definitions* | programmatic | SpEL\n|===\n\nThe main tradeoff seems to be where you want your authorization rules to live.\n\n[NOTE]\nIt's important to remember that when you use annotation-based Method Security, then unannotated methods are not secured.\nTo protect against this, declare xref:servlet/authorization/authorize-http-requests.adoc#activate-request-security[a catch-all authorization rule] in your xref:servlet/configuration/java.adoc#jc-httpsecurity[`HttpSecurity`] instance.\n\n[[authorizing-with-annotations]]\n== Authorizing with Annotations\n\nThe primary way Spring Security enables method-level authorization support is through annotations that you can add to methods, classes, and interfaces.\n\n[[use-preauthorize]]\n=== Authorizing Method Invocation with `@PreAuthorize`\n\nWhen <<activate-method-security,Method Security is active>>, you can annotate a method with the javadoc:org.springframework.security.access.prepost.PreAuthorize[format=annotation] annotation like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class BankService {\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tpublic Account readAccount(Long id) {\n        // ... is only invoked if the `Authentication` has the `ROLE_ADMIN` authority\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class BankService {\n\t@PreAuthorize(\"hasRole('ADMIN')\")\n\tfun readAccount(id: Long): Account {\n        // ... is only invoked if the `Authentication` has the `ROLE_ADMIN` authority\n\t}\n}\n----\n======\n\nThis is meant to indicate that the method can only be invoked if the provided expression `hasRole('ADMIN')` passes.\n\nYou can then xref:servlet/test/method.adoc[test the class] to confirm it is enforcing the authorization rule like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nBankService bankService;\n\n@WithMockUser(roles=\"ADMIN\")\n@Test\nvoid readAccountWithAdminRoleThenInvokes() {\n    Account account = this.bankService.readAccount(\"12345678\");\n    // ... assertions\n}\n\n@WithMockUser(roles=\"WRONG\")\n@Test\nvoid readAccountWithWrongRoleThenAccessDenied() {\n    assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(\n        () -> this.bankService.readAccount(\"12345678\"));\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@WithMockUser(roles=\"ADMIN\")\n@Test\nfun readAccountWithAdminRoleThenInvokes() {\n    val account: Account = this.bankService.readAccount(\"12345678\")\n    // ... assertions\n}\n\n@WithMockUser(roles=\"WRONG\")\n@Test\nfun readAccountWithWrongRoleThenAccessDenied() {\n    assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n        this.bankService.readAccount(\"12345678\")\n    }\n}\n----\n======\n\n[TIP]\n`@PreAuthorize` also can be a <<meta-annotations, meta-annotation>>, be defined <<class-or-interface-annotations,at the class or interface level>>, and use <<authorization-expressions, SpEL Authorization Expressions>>.\n\nWhile `@PreAuthorize` is quite helpful for declaring needed authorities, it can also be used to evaluate more complex <<using_method_parameters,expressions that involve the method parameters>>.\n\n[[use-postauthorize]]\n=== Authorization Method Results with `@PostAuthorize`\n\nWhen Method Security is active, you can annotate a method with the javadoc:org.springframework.security.access.prepost.PostAuthorize[format=annotation] annotation like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class BankService {\n\t@PostAuthorize(\"returnObject.owner == authentication.name\")\n\tpublic Account readAccount(Long id) {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class BankService {\n\t@PostAuthorize(\"returnObject.owner == authentication.name\")\n\tfun readAccount(id: Long): Account {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n======\n\nThis is meant to indicate that the method can only return the value if the provided expression `returnObject.owner == authentication.name` passes.\n`returnObject` represents the `Account` object to be returned.\n\nYou can then xref:servlet/test/method.adoc[test the class] to confirm it is enforcing the authorization rule:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nBankService bankService;\n\n@WithMockUser(username=\"owner\")\n@Test\nvoid readAccountWhenOwnedThenReturns() {\n    Account account = this.bankService.readAccount(\"12345678\");\n    // ... assertions\n}\n\n@WithMockUser(username=\"wrong\")\n@Test\nvoid readAccountWhenNotOwnedThenAccessDenied() {\n    assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(\n        () -> this.bankService.readAccount(\"12345678\"));\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@WithMockUser(username=\"owner\")\n@Test\nfun readAccountWhenOwnedThenReturns() {\n    val account: Account = this.bankService.readAccount(\"12345678\")\n    // ... assertions\n}\n\n@WithMockUser(username=\"wrong\")\n@Test\nfun readAccountWhenNotOwnedThenAccessDenied() {\n    assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy {\n        this.bankService.readAccount(\"12345678\")\n    }\n}\n----\n======\n\n[TIP]\n`@PostAuthorize` also can be a <<meta-annotations,meta-annotation>>, be defined <<class-or-interface-annotations,at the class or interface level>>, and use <<authorization-expressions, SpEL Authorization Expressions>>.\n\n`@PostAuthorize` is particularly helpful when defending against https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html[Insecure Direct Object Reference].\nIn fact, it can be defined as a <<meta-annotations,meta-annotation>> like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@PostAuthorize(\"returnObject.owner == authentication.name\")\npublic @interface RequireOwnership {}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Target(ElementType.METHOD, ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@PostAuthorize(\"returnObject.owner == authentication.name\")\nannotation class RequireOwnership\n----\n======\n\nAllowing you to instead annotate the service in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class BankService {\n\t@RequireOwnership\n\tpublic Account readAccount(Long id) {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class BankService {\n\t@RequireOwnership\n\tfun readAccount(id: Long): Account {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n======\n\nThe result is that the above method will only return the `Account` if its `owner` attribute matches the logged-in user's `name`.\nIf not, Spring Security will throw an `AccessDeniedException` and return a 403 status code.\n\n[NOTE]\n=====\nNote that `@PostAuthorize` is not recommended for classes that perform database writes since that typically means that a database change was made before the security invariants were checked.\nA common example of doing this is if you have `@Transactional` and `@PostAuthorize` on the same method.\nInstead, read the value first, using `@PostAuthorize` on the read, and then perform the database write, should that read is authorized.\nIf you must do something like this, you can <<changing-the-order, ensure that `@EnableTransactionManagement` comes before `@EnableMethodSecurity`>>.\n=====\n\n[[use-prefilter]]\n=== Filtering Method Parameters with `@PreFilter`\n\nWhen Method Security is active, you can annotate a method with the javadoc:org.springframework.security.access.prepost.PreFilter[format=annotation] annotation like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class BankService {\n\t@PreFilter(\"filterObject.owner == authentication.name\")\n\tpublic Collection<Account> updateAccounts(Account... accounts) {\n        // ... `accounts` will only contain the accounts owned by the logged-in user\n        return updated;\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class BankService {\n\t@PreFilter(\"filterObject.owner == authentication.name\")\n\tfun updateAccounts(vararg accounts: Account): Collection<Account> {\n        // ... `accounts` will only contain the accounts owned by the logged-in user\n        return updated\n\t}\n}\n----\n======\n\nThis is meant to filter out any values from `accounts` where the expression `filterObject.owner == authentication.name` fails.\n`filterObject` represents each `account` in `accounts` and is used to test each `account`.\n\nYou can then test the class in the following way to confirm it is enforcing the authorization rule:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nBankService bankService;\n\n@WithMockUser(username=\"owner\")\n@Test\nvoid updateAccountsWhenOwnedThenReturns() {\n    Account ownedBy = ...\n    Account notOwnedBy = ...\n    Collection<Account> updated = this.bankService.updateAccounts(ownedBy, notOwnedBy);\n    assertThat(updated).containsOnly(ownedBy);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nlateinit var bankService: BankService\n\n@WithMockUser(username=\"owner\")\n@Test\nfun updateAccountsWhenOwnedThenReturns() {\n    val ownedBy: Account = ...\n    val notOwnedBy: Account = ...\n    val updated: Collection<Account> = bankService.updateAccounts(ownedBy, notOwnedBy)\n    assertThat(updated).containsOnly(ownedBy)\n}\n----\n======\n\n[TIP]\n`@PreFilter` also can be a <<meta-annotations,meta-annotation>>, be defined <<class-or-interface-annotations,at the class or interface level>>, and use <<authorization-expressions, SpEL Authorization Expressions>>.\n\n`@PreFilter` supports arrays, collections, maps, and streams (so long as the stream is still open).\n\nFor example, the above `updateAccounts` declaration will function the same way as the following other four:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreFilter(\"filterObject.owner == authentication.name\")\npublic Collection<Account> updateAccounts(Account[] accounts)\n\n@PreFilter(\"filterObject.owner == authentication.name\")\npublic Collection<Account> updateAccounts(Collection<Account> accounts)\n\n@PreFilter(\"filterObject.value.owner == authentication.name\")\npublic Collection<Account> updateAccounts(Map<String, Account> accounts)\n\n@PreFilter(\"filterObject.owner == authentication.name\")\npublic Collection<Account> updateAccounts(Stream<Account> accounts)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreFilter(\"filterObject.owner == authentication.name\")\nfun updateAccounts(accounts: Array<Account>): Collection<Account>\n\n@PreFilter(\"filterObject.owner == authentication.name\")\nfun updateAccounts(accounts: Collection<Account>): Collection<Account>\n\n@PreFilter(\"filterObject.value.owner == authentication.name\")\nfun updateAccounts(accounts: Map<String, Account>): Collection<Account>\n\n@PreFilter(\"filterObject.owner == authentication.name\")\nfun updateAccounts(accounts: Stream<Account>): Collection<Account>\n----\n======\n\nThe result is that the above method will only have the `Account` instances where their `owner` attribute matches the logged-in user's `name`.\n\n[[use-postfilter]]\n=== Filtering Method Results with `@PostFilter`\n\nWhen Method Security is active, you can annotate a method with the javadoc:org.springframework.security.access.prepost.PostFilter[format=annotation] annotation like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class BankService {\n\t@PostFilter(\"filterObject.owner == authentication.name\")\n\tpublic Collection<Account> readAccounts(String... ids) {\n        // ... the return value will be filtered to only contain the accounts owned by the logged-in user\n        return accounts;\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class BankService {\n\t@PostFilter(\"filterObject.owner == authentication.name\")\n\tfun readAccounts(vararg ids: String): Collection<Account> {\n        // ... the return value will be filtered to only contain the accounts owned by the logged-in user\n        return accounts\n\t}\n}\n----\n======\n\nThis is meant to filter out any values from the return value where the expression `filterObject.owner == authentication.name` fails.\n`filterObject` represents each `account` in `accounts` and is used to test each `account`.\n\nYou can then test the class like so to confirm it is enforcing the authorization rule:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nBankService bankService;\n\n@WithMockUser(username=\"owner\")\n@Test\nvoid readAccountsWhenOwnedThenReturns() {\n    Collection<Account> accounts = this.bankService.updateAccounts(\"owner\", \"not-owner\");\n    assertThat(accounts).hasSize(1);\n    assertThat(accounts.get(0).getOwner()).isEqualTo(\"owner\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nlateinit var bankService: BankService\n\n@WithMockUser(username=\"owner\")\n@Test\nfun readAccountsWhenOwnedThenReturns() {\n    val accounts: Collection<Account> = bankService.updateAccounts(\"owner\", \"not-owner\")\n    assertThat(accounts).hasSize(1)\n    assertThat(accounts[0].owner).isEqualTo(\"owner\")\n}\n----\n======\n\n[TIP]\n`@PostFilter` also can be a <<meta-annotations,meta-annotation>>, be defined <<class-or-interface-annotations,at the class or interface level>>, and use <<authorization-expressions, SpEL Authorization Expressions>>.\n\n`@PostFilter` supports arrays, collections, maps, and streams (so long as the stream is still open).\n\nFor example, the above `readAccounts` declaration will function the same way as the following other four:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PostFilter(\"filterObject.owner == authentication.name\")\npublic Collection<Account> readAccounts(String... ids)\n\n@PostFilter(\"filterObject.owner == authentication.name\")\npublic Account[] readAccounts(String... ids)\n\n@PostFilter(\"filterObject.value.owner == authentication.name\")\npublic Map<String, Account> readAccounts(String... ids)\n\n@PostFilter(\"filterObject.owner == authentication.name\")\npublic Stream<Account> readAccounts(String... ids)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PostFilter(\"filterObject.owner == authentication.name\")\nfun readAccounts(vararg ids: String): Collection<Account>\n\n@PostFilter(\"filterObject.owner == authentication.name\")\nfun readAccounts(vararg ids: String): Array<Account>\n\n@PostFilter(\"filterObject.owner == authentication.name\")\nfun readAccounts(vararg ids: String): Map<String, Account>\n\n@PostFilter(\"filterObject.owner == authentication.name\")\nfun readAccounts(vararg ids: String): Stream<Account>\n----\n======\n\nThe result is that the above method will return the `Account` instances where their `owner` attribute matches the logged-in user's `name`.\n\n[NOTE]\nIn-memory filtering can obviously be expensive, and so be considerate of whether it is better to xref:servlet/integrations/data.adoc[filter the data in the data layer] instead.\n\n[[use-secured]]\n=== Authorizing Method Invocation with `@Secured`\n\njavadoc:org.springframework.security.access.annotation.Secured[format=annotation] is a legacy option for authorizing invocations.\n<<use-preauthorize,`@PreAuthorize`>> supersedes it and is recommended instead.\n\nTo use the `@Secured` annotation, you should first change your Method Security declaration to enable it like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableMethodSecurity(securedEnabled = true)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableMethodSecurity(securedEnabled = true)\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security secured-enabled=\"true\"/>\n----\n======\n\nThis will cause Spring Security to publish <<annotation-method-interceptors,the corresponding method interceptor>> that authorizes methods, classes, and interfaces annotated with `@Secured`.\n\n[[use-jsr250]]\n=== Authorizing Method Invocation with JSR-250 Annotations\n\nIn case you would like to use https://jcp.org/en/jsr/detail?id=250[JSR-250] annotations, Spring Security also supports that.\n<<use-preauthorize,`@PreAuthorize`>> has more expressive power and is thus recommended.\n\nTo use the JSR-250 annotations, you should first change your Method Security declaration to enable them like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableMethodSecurity(jsr250Enabled = true)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableMethodSecurity(jsr250Enabled = true)\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security jsr250-enabled=\"true\"/>\n----\n======\n\nThis will cause Spring Security to publish <<annotation-method-interceptors,the corresponding method interceptor>> that authorizes methods, classes, and interfaces annotated with `@RolesAllowed`, `@PermitAll`, and `@DenyAll`.\n\n\n[[class-or-interface-annotations]]\n=== Declaring Annotations at the Class or Interface Level\n\nIt's also supported to have Method Security annotations at the class and interface level.\n\nIf it is at the class level like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\n@PreAuthorize(\"hasAuthority('ROLE_USER')\")\npublic class MyController {\n    @GetMapping(\"/endpoint\")\n    public String endpoint() { ... }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\n@PreAuthorize(\"hasAuthority('ROLE_USER')\")\nopen class MyController {\n    @GetMapping(\"/endpoint\")\n    fun endpoint(): String { ... }\n}\n----\n======\n\nthen all methods inherit the class-level behavior.\n\nOr, if it's declared like the following at both the class and method level:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\n@PreAuthorize(\"hasAuthority('ROLE_USER')\")\npublic class MyController {\n    @GetMapping(\"/endpoint\")\n    @PreAuthorize(\"hasAuthority('ROLE_ADMIN')\")\n    public String endpoint() { ... }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\n@PreAuthorize(\"hasAuthority('ROLE_USER')\")\nopen class MyController {\n    @GetMapping(\"/endpoint\")\n    @PreAuthorize(\"hasAuthority('ROLE_ADMIN')\")\n    fun endpoint(): String { ... }\n}\n----\n======\n\nthen methods declaring the annotation override the class-level annotation.\n\nThe same is true for interfaces, with the exception that if a class inherits the annotation from two different interfaces, then startup will fail.\nThis is because Spring Security has no way to tell which one you want to use.\n\nIn cases like this, you can resolve the ambiguity by adding the annotation to the concrete method.\n\n[[meta-annotations]]\n=== Using Meta Annotations\n\nMethod Security supports meta annotations.\nThis means that you can take any annotation and improve readability based on your application-specific use cases.\n\nFor example, you can simplify `@PreAuthorize(\"hasRole('ADMIN')\")` to `@IsAdmin` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('ADMIN')\")\npublic @interface IsAdmin {}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Target(ElementType.METHOD, ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('ADMIN')\")\nannotation class IsAdmin\n----\n======\n\nAnd the result is that on your secured methods you can now do the following instead:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class BankService {\n\t@IsAdmin\n\tpublic Account readAccount(Long id) {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class BankService {\n\t@IsAdmin\n\tfun readAccount(id: Long): Account {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n======\n\nThis results in more readable method definitions.\n\n==== Templating Meta-Annotation Expressions\n\nYou can also opt into using meta-annotation templates, which allow for much more powerful annotation definitions.\n\nFirst, publish the following bean:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nstatic AnnotationTemplateExpressionDefaults templateExpressionDefaults() {\n\treturn new AnnotationTemplateExpressionDefaults();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ncompanion object {\n    @Bean\n    fun templateExpressionDefaults(): AnnotationTemplateExpressionDefaults {\n        return AnnotationTemplateExpressionDefaults()\n    }\n}\n----\n======\n\nNow instead of `@IsAdmin`, you can create something more powerful like `@HasRole` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('{value}')\")\npublic @interface HasRole {\n\tString value();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Target(ElementType.METHOD, ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasRole('{value}')\")\nannotation class HasRole(val value: String)\n----\n======\n\nAnd the result is that on your secured methods you can now do the following instead:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class BankService {\n\t@HasRole(\"ADMIN\")\n\tpublic Account readAccount(Long id) {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class BankService {\n\t@HasRole(\"ADMIN\")\n\tfun readAccount(id: Long): Account {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n======\n\nNote that this works with method variables and all annotation types, too, though you will want to be careful to correctly take care of quotation marks so the resulting SpEL expression is correct.\n\nFor example, consider the following `@HasAnyRole` annotation:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasAnyRole({roles})\")\npublic @interface HasAnyRole {\n\tString[] roles();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Target(ElementType.METHOD, ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@PreAuthorize(\"hasAnyRole({roles})\")\nannotation class HasAnyRole(val roles: Array<String>)\n----\n======\n\nIn that case, you'll notice that you should not use the quotation marks in the expression, but instead in the parameter value like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class BankService {\n\t@HasAnyRole(roles = { \"'USER'\", \"'ADMIN'\" })\n\tpublic Account readAccount(Long id) {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class BankService {\n\t@HasAnyRole(roles = arrayOf(\"'USER'\", \"'ADMIN'\"))\n\tfun readAccount(id: Long): Account {\n        // ... is only returned if the `Account` belongs to the logged in user\n\t}\n}\n----\n======\n\nso that, once replaced, the expression becomes `@PreAuthorize(\"hasAnyRole('USER', 'ADMIN')\")`.\n\n[[enable-annotation]]\n=== Enabling Certain Annotations\n\nYou can turn off ``@EnableMethodSecurity``'s pre-configuration and replace it with you own.\nYou may choose to do this if you want to <<custom-authorization-managers,customize the `AuthorizationManager`>> or `Pointcut`.\nOr you may simply want to only enable a specific annotation, like `@PostAuthorize`.\n\nYou can do this in the following way:\n\n.Only @PostAuthorize Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableMethodSecurity(prePostEnabled = false)\nclass MethodSecurityConfig {\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tAdvisor postAuthorize() {\n\t\treturn AuthorizationManagerAfterMethodInterceptor.postAuthorize();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableMethodSecurity(prePostEnabled = false)\nclass MethodSecurityConfig {\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tfun postAuthorize() : Advisor {\n\t\treturn AuthorizationManagerAfterMethodInterceptor.postAuthorize()\n\t}\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security pre-post-enabled=\"false\"/>\n\n<aop:config/>\n\n<bean id=\"postAuthorize\"\n\tclass=\"org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor\"\n\tfactory-method=\"postAuthorize\"/>\n----\n======\n\nThe above snippet achieves this by first disabling Method Security's pre-configurations and then publishing <<annotation-method-interceptors, the `@PostAuthorize` interceptor>> itself.\n\n[[use-intercept-methods]]\n== Authorizing with `<intercept-methods>`\n\nWhile using Spring Security's <<authorizing-with-annotations,annotation-based support>> is preferred for method security, you can also use XML to declare bean authorization rules.\n\nIf you need to declare it in your XML configuration instead, you can use xref:servlet/appendix/namespace/method-security.adoc#nsa-intercept-methods[`<intercept-methods>`] like so:\n\n[tabs]\n======\nXml::\n+\n[source,xml,role=\"primary\"]\n----\n<bean class=\"org.mycompany.MyController\">\n    <intercept-methods>\n        <protect method=\"get*\" access=\"hasAuthority('read')\"/>\n        <protect method=\"*\" access=\"hasAuthority('write')\"/>\n    </intercept-methods>\n</bean>\n----\n======\n\n[NOTE]\nThis only supports matching method by prefix or by name.\nIf your needs are more complex than that, <<authorizing-with-annotations,use annotation support>> instead.\n\n[[use-programmatic-authorization]]\n== Authorizing Methods Programmatically\n\nAs you've already seen, there are several ways that you can specify non-trivial authorization rules using <<authorization-expressions, Method Security SpEL expressions>>.\n\nThere are a number of ways that you can instead allow your logic to be Java-based instead of SpEL-based.\nThis gives use access the entire Java language for increased testability and flow control.\n\n=== Using a Custom Bean in SpEL\n\nThe first way to authorize a method programmatically is a two-step process.\n\nFirst, declare a bean that has a method that takes a `MethodSecurityExpressionOperations` instance like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component(\"authz\")\npublic class AuthorizationLogic {\n    public boolean decide(MethodSecurityExpressionOperations operations) {\n        // ... authorization logic\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component(\"authz\")\nopen class AuthorizationLogic {\n    fun decide(operations: MethodSecurityExpressionOperations): boolean {\n        // ... authorization logic\n    }\n}\n----\n======\n\nThen, reference that bean in your annotations in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class MyController {\n    @PreAuthorize(\"@authz.decide(#root)\")\n    @GetMapping(\"/endpoint\")\n    public String endpoint() {\n        // ...\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nopen class MyController {\n    @PreAuthorize(\"@authz.decide(#root)\")\n    @GetMapping(\"/endpoint\")\n    fun String endpoint() {\n        // ...\n    }\n}\n----\n======\n\nSpring Security will invoke the given method on that bean for each method invocation.\n\nWhat's nice about this is all your authorization logic is in a separate class that can be independently unit tested and verified for correctness.\nIt also has access to the full Java language.\n\n[TIP]\nIn addition to returning a `Boolean`, you can also return `null` to indicate that the code abstains from making a decision.\n\nIf you want to include more information about the nature of the decision, you can instead return a custom `AuthorizationDecision` like this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component(\"authz\")\npublic class AuthorizationLogic {\n    public AuthorizationDecision decide(MethodSecurityExpressionOperations operations) {\n        // ... authorization logic\n        return new MyAuthorizationDecision(false, details);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component(\"authz\")\nopen class AuthorizationLogic {\n    fun decide(operations: MethodSecurityExpressionOperations): AuthorizationDecision {\n        // ... authorization logic\n        return MyAuthorizationDecision(false, details)\n    }\n}\n----\n======\n\nOr throw a custom `AuthorizationDeniedException` instance.\nNote, though, that returning an object is preferred as this doesn't incur the expense of generating a stacktrace.\n\nThen, you can access the custom details when you <<fallback-values-authorization-denied, customize how the authorization result is handled>>.\n\n[TIP]\n====\nFurther, you can return an `AuthorizationManager` itself.\nThis is helpful when unifying custom web authorization rules with method security ones since web security by default requires specifying an `AuthorizationManager` instance.\n====\n\n[[custom-authorization-managers]]\n=== Using a Custom Authorization Manager\n\nThe second way to authorize a method programmatically is to create a custom xref:servlet/authorization/architecture.adoc#_the_authorizationmanager[`AuthorizationManager`].\n\nFirst, declare an authorization manager instance, perhaps like this one:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class MyAuthorizationManager implements AuthorizationManager<MethodInvocation>, AuthorizationManager<MethodInvocationResult> {\n    @Override\n    public AuthorizationResult authorize(Supplier<Authentication> authentication, MethodInvocation invocation) {\n        // ... authorization logic\n    }\n\n    @Override\n    public AuthorizationResult authorize(Supplier<Authentication> authentication, MethodInvocationResult invocation) {\n        // ... authorization logic\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass MyAuthorizationManager : AuthorizationManager<MethodInvocation>, AuthorizationManager<MethodInvocationResult> {\n    override fun authorize(authentication: Supplier<Authentication>, invocation: MethodInvocation): AuthorizationResult {\n        // ... authorization logic\n    }\n\n    override fun authorize(authentication: Supplier<Authentication>, invocation: MethodInvocationResult): AuthorizationResult {\n        // ... authorization logic\n    }\n}\n----\n======\n\nThen, publish the method interceptor with a pointcut that corresponds to when you want that `AuthorizationManager` to run.\nFor example, you could replace how `@PreAuthorize` and `@PostAuthorize` work like so:\n\n.Only @PreAuthorize and @PostAuthorize Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableMethodSecurity(prePostEnabled = false)\nclass MethodSecurityConfig {\n    @Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tAdvisor preAuthorize(MyAuthorizationManager manager) {\n\t\treturn AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tAdvisor postAuthorize(MyAuthorizationManager manager) {\n\t\treturn AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableMethodSecurity(prePostEnabled = false)\nclass MethodSecurityConfig {\n   \t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tfun preAuthorize(manager: MyAuthorizationManager) : Advisor {\n\t\treturn AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager)\n\t}\n\n\t@Bean\n\t@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n\tfun postAuthorize(manager: MyAuthorizationManager) : Advisor {\n\t\treturn AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager)\n\t}\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security pre-post-enabled=\"false\"/>\n\n<aop:config/>\n\n<bean id=\"preAuthorize\"\n\tclass=\"org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor\"\n\tfactory-method=\"preAuthorize\">\n    <constructor-arg ref=\"myAuthorizationManager\"/>\n</bean>\n\n<bean id=\"postAuthorize\"\n\tclass=\"org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor\"\n\tfactory-method=\"postAuthorize\">\n    <constructor-arg ref=\"myAuthorizationManager\"/>\n</bean>\n----\n======\n\n[TIP]\n====\nYou can place your interceptor in between Spring Security method interceptors using the order constants specified in `AuthorizationInterceptorsOrder`.\n====\n\n[[customizing-expression-handling]]\n=== Customizing Expression Handling\n\nOr, third, you can customize how each SpEL expression is handled.\nTo do that, you can expose a custom javadoc:org.springframework.security.access.expression.method.MethodSecurityExpressionHandler[], like so:\n\n.Custom MethodSecurityExpressionHandler\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nstatic MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {\n\tDefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();\n\thandler.setRoleHierarchy(roleHierarchy);\n\treturn handler;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ncompanion object {\n\t@Bean\n\tfun methodSecurityExpressionHandler(roleHierarchy: RoleHierarchy) : MethodSecurityExpressionHandler {\n\t\tval handler = DefaultMethodSecurityExpressionHandler()\n\t\thandler.setRoleHierarchy(roleHierarchy)\n\t\treturn handler\n\t}\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security>\n\t<sec:expression-handler ref=\"myExpressionHandler\"/>\n</sec:method-security>\n\n<bean id=\"myExpressionHandler\"\n\t\tclass=\"org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler\">\n\t<property name=\"roleHierarchy\" ref=\"roleHierarchy\"/>\n</bean>\n----\n======\n\n[TIP]\n====\nWe expose `MethodSecurityExpressionHandler` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes\n====\n\nYou can also <<subclass-defaultmethodsecurityexpressionhandler,subclass `DefaultMessageSecurityExpressionHandler`>> to add your own custom authorization expressions beyond the defaults.\n\n[[pre-post-authorize-aot]]\n=== Working with AOT\n\nSpring Security will scan all beans in the application context for methods that use `@PreAuthorize` or `@PostAuthorize`.\nWhen it finds one, it will resolve any beans used inside the security expression and register the appropriate runtime hints for that bean.\nIf it finds a method that uses `@AuthorizeReturnObject`, it will recursively search inside the method's return type for `@PreAuthorize` and `@PostAuthorize` annotations and register them accordingly.\n\nFor example, consider the following Spring Boot application:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Service\npublic class AccountService { <1>\n\n    @PreAuthorize(\"@authz.decide()\") <2>\n    @AuthorizeReturnObject <3>\n    public Account getAccountById(String accountId) {\n        // ...\n    }\n\n}\n\npublic class Account {\n\n    private final String accountNumber;\n\n    // ...\n\n    @PreAuthorize(\"@accountAuthz.canViewAccountNumber()\") <4>\n    public String getAccountNumber() {\n        return this.accountNumber;\n    }\n\n    @AuthorizeReturnObject <5>\n    public User getUser() {\n        return new User(\"John Doe\");\n    }\n\n}\n\npublic class User {\n\n    private final String fullName;\n\n    // ...\n\n    @PostAuthorize(\"@myOtherAuthz.decide()\") <6>\n    public String getFullName() {\n        return this.fullName;\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Service\nclass AccountService { <1>\n\n    @PreAuthorize(\"@authz.decide()\") <2>\n    @AuthorizeReturnObject <3>\n    fun getAccountById(accountId: String): Account {\n        // ...\n    }\n\n}\n\nclass Account(private val accountNumber: String) {\n\n    @PreAuthorize(\"@accountAuthz.canViewAccountNumber()\") <4>\n    fun getAccountNumber(): String {\n        return this.accountNumber\n    }\n\n    @AuthorizeReturnObject <5>\n    fun getUser(): User {\n        return User(\"John Doe\")\n    }\n\n}\n\nclass User(private val fullName: String) {\n\n    @PostAuthorize(\"@myOtherAuthz.decide()\") <6>\n    fun getFullName(): String {\n        return this.fullName\n    }\n\n}\n----\n======\n\n<1> Spring Security finds the `AccountService` bean\n<2> Finding a method that uses `@PreAuthorize`, it will resolve any bean names used inside the expression, `authz` in that case, and register runtime hints for the bean class\n<3> Finding a method that uses `@AuthorizeReturnObject`, it will look into the method's return type for any `@PreAuthorize` or `@PostAuthorize`\n<4> Then, it finds a `@PreAuthorize` with another bean name: `accountAuthz`; the runtime hints are registered for the bean class as well\n<5> Finding another `@AuthorizeReturnObject` it will look again into the method's return type\n<6> Now, a `@PostAuthorize` is found with yet another bean name used: `myOtherAuthz`; the runtime hints are registered for the bean class as well\n\nThere will be many times when Spring Security cannot determine the actual return type of the method ahead of time since it may be hidden in an erased generic type.\n\nConsider the following service:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Service\npublic class AccountService {\n\n    @AuthorizeReturnObject\n    public List<Account> getAllAccounts() {\n        // ...\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Service\nclass AccountService {\n\n    @AuthorizeReturnObject\n    fun getAllAccounts(): List<Account> {\n        // ...\n    }\n\n}\n----\n======\n\nIn this case, the generic type is erased and so it isn’t apparent to Spring Security ahead-of-time that `Account` needs to be visited in order to check for `@PreAuthorize` and `@PostAuthorize`.\n\nTo address this, you can publish a javadoc:org.springframework.security.aot.hint.PrePostAuthorizeExpressionBeanHintsRegistrar[`PrePostAuthorizeExpressionBeanHintsRegistrar`] like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nstatic SecurityHintsRegistrar registerTheseToo() {\n    return new PrePostAuthorizeExpressionBeanHintsRegistrar(Account.class);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nfun registerTheseToo(): SecurityHintsRegistrar {\n    return PrePostAuthorizeExpressionBeanHintsRegistrar(Account::class.java)\n}\n----\n======\n\n[[use-aspectj]]\n== Authorizing with AspectJ\n\n[[match-by-pointcut]]\n=== Matching Methods with Custom Pointcuts\n\nBeing built on Spring AOP, you can declare patterns that are not related to annotations, similar to xref:servlet/authorization/authorize-http-requests.adoc[request-level authorization].\nThis has the potential advantage of centralizing method-level authorization rules.\n\nFor example, you can use publish your own `Advisor` or use xref:servlet/appendix/namespace/method-security.adoc#nsa-protect-pointcut[`<protect-pointcut>`] to match AOP expressions to authorization rules for your service layer like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole\n\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nstatic Advisor protectServicePointcut() {\n    AspectJExpressionPointcut pattern = new AspectJExpressionPointcut()\n    pattern.setExpression(\"execution(* com.mycompany.*Service.*(..))\")\n    return new AuthorizationManagerBeforeMethodInterceptor(pattern, hasRole(\"USER\"))\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole\n\ncompanion object {\n    @Bean\n    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n    fun protectServicePointcut(): Advisor {\n        val pattern = AspectJExpressionPointcut()\n        pattern.setExpression(\"execution(* com.mycompany.*Service.*(..))\")\n        return new AuthorizationManagerBeforeMethodInterceptor(pattern, hasRole(\"USER\"))\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security>\n    <protect-pointcut expression=\"execution(* com.mycompany.*Service.*(..))\" access=\"hasRole('USER')\"/>\n</sec:method-security>\n----\n======\n\n[[weave-aspectj]]\n=== Integrate with AspectJ Byte-weaving\n\nPerformance can at times be enhanced by using AspectJ to weave Spring Security advice into the byte code of your beans.\n\nAfter setting up AspectJ, you can quite simply state in the `@EnableMethodSecurity` annotation or `<method-security>` element that you are using AspectJ:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableMethodSecurity(mode=AdviceMode.ASPECTJ)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableMethodSecurity(mode=AdviceMode.ASPECTJ)\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security mode=\"aspectj\"/>\n----\n======\n\nAnd the result will be that Spring Security will publish its advisors as AspectJ advice so that they can be woven in accordingly.\n\n[[changing-the-order]]\n== Specifying Order\n\nAs already noted, there is a Spring AOP method interceptor for each annotation, and each of these has a location in the Spring AOP advisor chain.\n\nNamely, the `@PreFilter` method interceptor's order is 100, ``@PreAuthorize``'s is 200, and so on.\n\nYou can use the `offset` parameter on `@EnableMethodSecurity` to move all interceptors en masse to provide their advice earlier or later in a method invocation.\n\n[[authorization-expressions]]\n== Expressing Authorization with SpEL\n\nYou've already seen several examples using SpEL, so now let's cover the API a bit more in depth.\n\nSpring Security encapsulates all of its authorization fields and methods in a set of root objects.\nThe most generic root object is called `SecurityExpressionRoot` and it forms the basis for `MethodSecurityExpressionRoot`.\nSpring Security supplies this root object to `MethodSecurityEvaluationContext` when preparing to evaluate an authorization expression.\n\n[[using-authorization-expression-fields-and-methods]]\n=== Using Authorization Expression Fields and Methods\n\nThe first thing this provides is an enhanced set of authorization fields and methods to your SpEL expressions.\nWhat follows is a quick overview of the most common methods:\n\n* `permitAll` - The method requires no authorization to be invoked; note that in this case, xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[the `Authentication`] is never retrieved from the session\n* `denyAll` - The method is not allowed under any circumstances; note that in this case, the `Authentication` is never retrieved from the session\n* `hasAuthority` - The method requires that the `Authentication` have xref:servlet/authorization/architecture.adoc#authz-authorities[a `GrantedAuthority`] that matches the given value\n* `hasRole` - A shortcut for `hasAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix\n* `hasAnyAuthority` - The method requires that the `Authentication` have a `GrantedAuthority` that matches any of the given values\n* `hasAnyRole` - A shortcut for `hasAnyAuthority` that prefixes `ROLE_` or whatever is configured as the default prefix\n* `hasAllAuthorities` - The method requires that the `Authentication` have ``GrantedAuthority``s that matches all of the given values\n* `hasAllRoles` - A shortcut for `hasAllAuthorities` that prefixes `ROLE_` or whatever is configured as the default prefix\n* `hasPermission` - A hook into your `PermissionEvaluator` instance for doing object-level authorization\n\nAnd here is a brief look at the most common fields:\n\n* `authentication` - The `Authentication` instance associated with this method invocation\n* `principal` - The `Authentication#getPrincipal` associated with this method invocation\n\nHaving now learned the patterns, rules, and how they can be paired together, you should be able to understand what is going on in this more complex example:\n\n.Authorize Requests\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class MyService {\n    @PreAuthorize(\"denyAll\") <1>\n    MyResource myDeprecatedMethod(...);\n\n    @PreAuthorize(\"hasRole('ADMIN')\") <2>\n    MyResource writeResource(...)\n\n    @PreAuthorize(\"hasAuthority('db') and hasRole('ADMIN')\") <3>\n    MyResource deleteResource(...)\n\n    @PreAuthorize(\"principal.claims['aud'] == 'my-audience'\") <4>\n    MyResource readResource(...);\n\n\t@PreAuthorize(\"@authz.check(authentication, #root)\")\n    MyResource shareResource(...);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class MyService {\n    @PreAuthorize(\"denyAll\") <1>\n    fun myDeprecatedMethod(...): MyResource\n\n    @PreAuthorize(\"hasRole('ADMIN')\") <2>\n    fun writeResource(...): MyResource\n\n    @PreAuthorize(\"hasAuthority('db') and hasRole('ADMIN')\") <3>\n    fun deleteResource(...): MyResource\n\n    @PreAuthorize(\"principal.claims['aud'] == 'my-audience'\") <4>\n    fun readResource(...): MyResource\n\n    @PreAuthorize(\"@authz.check(#root)\")\n    fun shareResource(...): MyResource\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<sec:method-security>\n    <protect-pointcut expression=\"execution(* com.mycompany.*Service.myDeprecatedMethod(..))\" access=\"denyAll\"/> <1>\n    <protect-pointcut expression=\"execution(* com.mycompany.*Service.writeResource(..))\" access=\"hasRole('ADMIN')\"/> <2>\n    <protect-pointcut expression=\"execution(* com.mycompany.*Service.deleteResource(..))\" access=\"hasAuthority('db') and hasRole('ADMIN')\"/> <3>\n    <protect-pointcut expression=\"execution(* com.mycompany.*Service.readResource(..))\" access=\"principal.claims['aud'] == 'my-audience'\"/> <4>\n    <protect-pointcut expression=\"execution(* com.mycompany.*Service.shareResource(..))\" access=\"@authz.check(#root)\"/> <5>\n</sec:method-security>\n----\n======\n<1> This method may not be invoked by anyone for any reason\n<2> This method may only be invoked by ``Authentication``s granted the `ROLE_ADMIN` authority\n<3> This method may only be invoked by ``Authentication``s granted the `db` and `ROLE_ADMIN` authorities\n<4> This method may only be invoked by ``Princpal``s with an `aud` claim equal to \"my-audience\"\n<5> This method may only be invoked if the bean ``authz``'s `check` method returns `true`\n\n[TIP]\n====\nYou can use a bean like `authz` above to <<_using_a_custom_bean_in_spel, add programmatic authorization>>.\n====\n\n[[using_method_parameters]]\n=== Using Method Parameters\n\nAdditionally, Spring Security provides a mechanism for discovering method parameters so they can also be accessed in the SpEL expression as well.\n\nFor a complete reference, Spring Security uses `DefaultSecurityParameterNameDiscoverer` to discover the parameter names.\nBy default, the following options are tried for a method.\n\n1. If Spring Security's `@P` annotation is present on a single argument to the method, the value is used.\nThe following example uses the `@P` annotation:\n\n+\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport org.springframework.security.access.method.P;\n\n...\n\n@PreAuthorize(\"hasPermission(#c, 'write')\")\npublic void updateContact(@P(\"c\") Contact contact);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.access.method.P\n\n...\n\n@PreAuthorize(\"hasPermission(#c, 'write')\")\nfun doSomething(@P(\"c\") contact: Contact?)\n----\n======\n+\nThe intention of this expression is to require that the current `Authentication` have `write` permission specifically for this `Contact` instance.\n+\nBehind the scenes, this is implemented by using `AnnotationParameterNameDiscoverer`, which you can customize to support the value attribute of any specified annotation.\n\n2. If xref:servlet/integrations/data.adoc[Spring Data's] `@Param` annotation is present on at least one parameter for the method, the value is used.\nThe following example uses the `@Param` annotation:\n+\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport org.springframework.data.repository.query.Param;\n\n...\n\n@PreAuthorize(\"#n == authentication.name\")\nContact findContactByName(@Param(\"n\") String name);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.data.repository.query.Param\n\n...\n\n@PreAuthorize(\"#n == authentication.name\")\nfun findContactByName(@Param(\"n\") name: String?): Contact?\n----\n======\n+\nThe intention of this expression is to require that `name` be equal to `Authentication#getName` for the invocation to be authorized.\n+\nBehind the scenes, this is implemented by using `AnnotationParameterNameDiscoverer`, which you can customize to support the value attribute of any specified annotation.\n\n3. If you compile your code with the `-parameters` argument, the standard JDK reflection API is used to discover the parameter names.\nThis works on both classes and interfaces.\n\n4. Finally, if you compile your code with debug symbols, the parameter names are discovered by using the debug symbols.\nThis does not work for interfaces, since they do not have debug information about the parameter names.\nFor interfaces, either annotations or the `-parameters` approach must be used.\n\n[[customizing-authorization-managers]]\n== Customizing Authorization Managers\n\nWhen you use SpEL expressions with <<use-preauthorize,`@PreAuthorize`>>, <<use-postauthorize,`@PostAuthorize`>>, <<use-prefilter,`@PreFilter`>> and <<use-postfilter,`@PostFilter`>>, Spring Security takes care of creating the appropriate `AuthorizationManager` instances for you.\nIn certain cases, you may want to customize what is created in order to have complete control over how authorization decisions are made xref:servlet/authorization/architecture.adoc#authz-delegate-authorization-manager[at the framework level].\n\nIn order to take control of creating instances of `AuthorizationManager` for pre- and post-annotations, you can create a custom xref:servlet/authorization/architecture.adoc#authz-authorization-manager-factory[`AuthorizationManagerFactory`].\nFor example, let's say you want to allow users with the `ADMIN` role whenever any other role is required.\nTo do this, you can create a custom implementation for method security as in the following example:\n\ninclude-code::./CustomMethodInvocationAuthorizationManagerFactory[tag=class,indent=0]\n\nNow, whenever you <<use-preauthorize,use the `@PreAuthorize` annotation>> with `hasRole` or `hasAnyRole`, Spring Security will automatically invoke your custom factory to create an instance of `AuthorizationManager` that allows access for the given role(s) _OR_ the `ADMIN` role.\n\n[TIP]\nWe use this as a simple example of creating a custom `AuthorizationManagerFactory`, though the same outcome could be accomplished with <<favor-granting-authorities,a role hierarchy>>.\nUse whichever approach fits best in your situation.\n\n[[authorize-object]]\n== Authorizing Arbitrary Objects\n\nSpring Security also supports wrapping any object that is annotated its method security annotations.\n\nThe simplest way to achieve this is to mark any method that returns the object you wish to authorize with the `@AuthorizeReturnObject` annotation.\n\nFor example, consider the following `User` class:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class User {\n\tprivate String name;\n\tprivate String email;\n\n\tpublic User(String name, String email) {\n\t\tthis.name = name;\n\t\tthis.email = email;\n\t}\n\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n    @PreAuthorize(\"hasAuthority('user:read')\")\n    public String getEmail() {\n\t\treturn this.email;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass User (val name:String, @get:PreAuthorize(\"hasAuthority('user:read')\") val email:String)\n----\n======\n\nGiven an interface like this one:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class UserRepository {\n\t@AuthorizeReturnObject\n    Optional<User> findByName(String name) {\n\t\t// ...\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass UserRepository {\n    @AuthorizeReturnObject\n    fun findByName(name:String?): Optional<User?>? {\n        // ...\n    }\n}\n----\n======\n\nThen any `User` that is returned from `findById` will be secured like other Spring Security-protected components:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nUserRepository users;\n\n@Test\nvoid getEmailWhenProxiedThenAuthorizes() {\n    Optional<User> securedUser = users.findByName(\"name\");\n    assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> securedUser.get().getEmail());\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n\nimport jdk.incubator.vector.VectorOperators.Test\nimport java.nio.file.AccessDeniedException\nimport java.util.*\n\n@Autowired\nvar users:UserRepository? = null\n\n@Test\nfun getEmailWhenProxiedThenAuthorizes() {\n    val securedUser: Optional<User> = users.findByName(\"name\")\n    assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy{securedUser.get().getEmail()}\n}\n----\n======\n\n=== Using `@AuthorizeReturnObject` at the class level\n\n`@AuthorizeReturnObject` can be placed at the class level. Note, though, that this means Spring Security will attempt to proxy any return object, including ``String``, ``Integer`` and other types.\nThis is often not what you want to do.\n\nIf you want to use `@AuthorizeReturnObject` on a class or interface whose methods return value types, like `int`, `String`, `Double` or collections of those types, then you should also publish the appropriate `AuthorizationAdvisorProxyFactory.TargetVisitor` as follows:\n\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;\n\n// ...\n\n@Bean\nstatic TargetVisitor skipValueTypes() {\n    return TargetVisitor.defaultsSkipValueTypes();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor\n\n// ...\n\n@Bean\nopen fun skipValueTypes() = TargetVisitor.defaultsSkipValueTypes()\n----\n======\n\n[TIP]\n====\nYou can set your own `AuthorizationAdvisorProxyFactory.TargetVisitor` to customize the proxying for any set of types\n====\n\n=== Programmatically Proxying\n\nYou can also programmatically proxy a given object.\n\nTo achieve this, you can autowire the provided `AuthorizationProxyFactory` instance, which is based on which method security interceptors you have configured.\nIf you are using `@EnableMethodSecurity`, then this means that it will by default have the interceptors for `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter`.\n\n\nYou can proxy an instance of user in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nAuthorizationProxyFactory proxyFactory;\n\n@Test\nvoid getEmailWhenProxiedThenAuthorizes() {\n    User user = new User(\"name\", \"email\");\n    assertThat(user.getEmail()).isNotNull();\n    User securedUser = proxyFactory.proxy(user);\n    assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(securedUser::getEmail);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nvar proxyFactory:AuthorizationProxyFactory? = null\n\n@Test\nfun getEmailWhenProxiedThenAuthorizes() {\n    val user: User = User(\"name\", \"email\")\n    assertThat(user.getEmail()).isNotNull()\n    val securedUser: User = proxyFactory.proxy(user)\n    assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy(securedUser::getEmail)\n}\n----\n======\n\n=== Manual Construction\n\nYou can also define your own instance if you need something different from the Spring Security default.\n\nFor example, if you define an `AuthorizationProxyFactory` instance like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;\nimport static org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.preAuthorize;\n// ...\n\nAuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();\n// and if needing to skip value types\nproxyFactory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;\nimport org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.preAuthorize\n\n// ...\n\nval proxyFactory: AuthorizationProxyFactory = AuthorizationProxyFactory(preAuthorize())\n// and if needing to skip value types\nproxyFactory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes())\n----\n======\n\nThen you can wrap any instance of `User` as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Test\nvoid getEmailWhenProxiedThenAuthorizes() {\n\tAuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();\n    User user = new User(\"name\", \"email\");\n    assertThat(user.getEmail()).isNotNull();\n    User securedUser = proxyFactory.proxy(user);\n    assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(securedUser::getEmail);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Test\nfun getEmailWhenProxiedThenAuthorizes() {\n    val proxyFactory: AuthorizationProxyFactory = AuthorizationAdvisorProxyFactory.withDefaults()\n    val user: User = User(\"name\", \"email\")\n    assertThat(user.getEmail()).isNotNull()\n    val securedUser: User = proxyFactory.proxy(user)\n    assertThatExceptionOfType(AccessDeniedException::class.java).isThrownBy(securedUser::getEmail)\n}\n----\n======\n\n=== Proxying Collections\n\n`AuthorizationProxyFactory` supports Java collections, streams, arrays, optionals, and iterators by proxying the element type and maps by proxying the value type.\n\nThis means that when proxying a `List` of objects, the following also works:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Test\nvoid getEmailWhenProxiedThenAuthorizes() {\n\tAuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();\n    List<User> users = List.of(ada, albert, marie);\n    List<User> securedUsers = proxyFactory.proxy(users);\n\tsecuredUsers.forEach((securedUser) ->\n        assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(securedUser::getEmail));\n}\n----\n======\n\n=== Proxying Classes\n\nIn limited circumstances, it may be valuable to proxy a `Class` itself, and `AuthorizationProxyFactory` also supports this.\nThis is roughly the equivalent of calling `ProxyFactory#getProxyClass` in Spring Framework's support for creating proxies.\n\nOne place where this is handy is when you need to construct the proxy class ahead-of-time, like with Spring AOT.\n\n=== Support for All Method Security Annotations\n\n`AuthorizationProxyFactory` supports whichever method security annotations are enabled in your application.\nIt is based off of whatever `AuthorizationAdvisor` classes are published as a bean.\n\nSince `@EnableMethodSecurity` publishes `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter`  advisors by default, you will typically need to do nothing to activate the ability.\n\n[NOTE]\n====\nSpEL expressions that use `returnObject` or `filterObject` sit behind the proxy and so have full access to the object.\n====\n\n[#custom_advice]\n=== Custom Advice\n\nIf you have security advice that you also want applied, you can publish your own `AuthorizationAdvisor` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableMethodSecurity\nclass SecurityConfig {\n    @Bean\n    static AuthorizationAdvisor myAuthorizationAdvisor() {\n        return new AuthorizationAdvisor();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableMethodSecurity\ninternal class SecurityConfig {\n    @Bean\n    fun myAuthorizationAdvisor(): AuthorizationAdvisor {\n        return AuthorizationAdvisor()\n    }\n]\n----\n======\n\nAnd Spring Security will add that advisor into the set of advice that `AuthorizationProxyFactory` adds when proxying an object.\n\n=== Working with Jackson\n\nOne powerful use of this feature is to return a secured value from a controller like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class UserController {\n    @Autowired\n    AuthorizationProxyFactory proxyFactory;\n\n    @GetMapping\n    User currentUser(@AuthenticationPrincipal User user) {\n        return this.proxyFactory.proxy(user);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RestController\nclass UserController  {\n    @Autowired\n    var proxyFactory: AuthorizationProxyFactory? = null\n\n    @GetMapping\n    fun currentUser(@AuthenticationPrincipal user:User?): User {\n        return proxyFactory.proxy(user)\n    }\n}\n----\n======\n\nYou will need to <<fallback-values-authorization-denied,add a `MethodAuthorizationDeniedHandler`>> like this one:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class Null implements MethodAuthorizationDeniedHandler {\n    @Override\n    public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {\n        return null;\n    }\n}\n\n// ...\n\n@HandleAuthorizationDenied(handlerClass = Null.class)\npublic class User {\n\t...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass Null : MethodAuthorizationDeniedHandler {\n    override fun handleDeniedInvocation(methodInvocation: MethodInvocation?, authorizationResult: AuthorizationResult?): Any? {\n        return null\n    }\n}\n\n// ...\n\n@HandleAuthorizationDenied(handlerClass = Null.class)\nopen class User {\n\t...\n}\n----\n======\n\nThen, you'll see a different JSON serialization based on the authorization level of the user.\nIf they don't have the `user:read` authority, then they'll see:\n\n[source,json]\n----\n{\n    \"name\" : \"name\",\n    \"email\" : null\n}\n----\n\nAnd if they do have that authority, they'll see:\n\n[source,json]\n----\n{\n    \"name\" : \"name\",\n    \"email\" : \"email\"\n}\n----\n\n[TIP]\n====\nYou can also add the Spring Boot property `spring.jackson.default-property-inclusion=non_null` to exclude the null value from serialization, if you also don't want to reveal the JSON key to an unauthorized user.\n====\n\n[[authorize-return-object-aot]]\n=== Working with AOT\n\nSpring Security will scan all beans in the application context for methods that use `@AuthorizeReturnObject`.\nWhen it finds one, it will create and register the appropriate proxy class ahead of time.\nIt will also recursively search for other nested objects that also use `@AuthorizeReturnObject` and register them accordingly.\n\nFor example, consider the following Spring Boot application:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@SpringBootApplication\npublic class MyApplication {\n\t@RestController\n    public static class MyController { <1>\n\t\t@GetMapping\n        @AuthorizeReturnObject\n        Message getMessage() { <2>\n\t\t\treturn new Message(someUser, \"hello!\");\n        }\n    }\n\n\tpublic static class Message { <3>\n\t\tUser to;\n\t\tString text;\n\n\t\t// ...\n\n        @AuthorizeReturnObject\n        public User getTo() { <4>\n\t\t\treturn this.to;\n        }\n\n\t\t// ...\n\t}\n\n\tpublic static class User { <5>\n\t\t// ...\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(MyApplication.class);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@SpringBootApplication\nopen class MyApplication {\n\t@RestController\n    open class MyController { <1>\n\t\t@GetMapping\n        @AuthorizeReturnObject\n        fun getMessage():Message { <2>\n\t\t\treturn Message(someUser, \"hello!\")\n        }\n    }\n\n\topen class Message { <3>\n\t\tval to: User\n\t\tval test: String\n\n\t\t// ...\n\n        @AuthorizeReturnObject\n        fun getTo(): User { <4>\n\t\t\treturn this.to\n        }\n\n\t\t// ...\n\t}\n\n\topen class User { <5>\n\t\t// ...\n\t}\n\n\tfun main(args: Array<String>) {\n\t\tSpringApplication.run(MyApplication.class)\n\t}\n}\n----\n======\n<1> - First, Spring Security finds the `MyController` bean\n<2> - Finding a method that uses `@AuthorizeReturnObject`, it proxies `Message`, the return value, and registers that proxy class to `RuntimeHints`\n<3> - Then, it traverses `Message` to see if it uses `@AuthorizeReturnObject`\n<4> - Finding a method that uses `@AuthorizeReturnObject`, it proxies `User`, the return value, and registers that proxy class to `RuntimeHints`\n<5> - Finally, it traverses `User` to see if it uses `@AuthorizeReturnObject`; finding nothing, the algorithm completes\n\nThere will be many times when Spring Security cannot determine the proxy class ahead of time since it may be hidden in an erased generic type.\n\nConsider the following change to `MyController`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic static class MyController {\n    @GetMapping\n    @AuthorizeReturnObject\n    List<Message> getMessages() {\n        return List.of(new Message(someUser, \"hello!\"));\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RestController\nstatic class MyController {\n    @AuthorizeReturnObject\n    @GetMapping\n    fun getMessages(): Array<Message> = arrayOf(Message(someUser, \"hello!\"))\n}\n----\n======\n\nIn this case, the generic type is erased and so it isn't apparent to Spring Security ahead-of-time that `Message` will need to be proxied at runtime.\n\nTo address this, you can publish `AuthorizeProxyFactoryHintsRegistrar` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nstatic SecurityHintsRegsitrar registerTheseToo(AuthorizationProxyFactory proxyFactory) {\n\treturn new AuthorizeReturnObjectHintsRegistrar(proxyFactory, Message.class);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\n@Role(BeanDefinition.ROLE_INFRASTRUCTURE)\nfun registerTheseToo(proxyFactory: AuthorizationProxyFactory?): SecurityHintsRegistrar {\n    return AuthorizeReturnObjectHintsRegistrar(proxyFactory, Message::class.java)\n}\n----\n======\n\nSpring Security will register that class and then traverse its type as before.\n\n[[fallback-values-authorization-denied]]\n== Providing Fallback Values When Authorization is Denied\n\nThere are some scenarios where you may not wish to throw an `AuthorizationDeniedException` when a method is invoked without the required permissions.\nInstead, you might wish to return a post-processed result, like a masked result, or a default value in cases where authorization denied happened before invoking the method.\n\nSpring Security provides support for handling authorization denied on method invocation by using the javadoc:org.springframework.security.authorization.method.HandleAuthorizationDenied[format=annotation].\nThe handler works for denied authorizations that happened in the <<authorizing-with-annotations,`@PreAuthorize` and `@PostAuthorize` annotations>> as well as javadoc:org.springframework.security.authorization.AuthorizationDeniedException[] thrown from the method invocation itself.\n\nLet's consider the example from the <<authorize-object,previous section>>, but instead of creating the `AccessDeniedExceptionInterceptor` to transform an `AccessDeniedException` to a `null` return value, we will use the `handlerClass` attribute from `@HandleAuthorizationDenied`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class NullMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler { <1>\n\n    @Override\n    public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {\n        return null;\n    }\n\n}\n\n@Configuration\n@EnableMethodSecurity\npublic class SecurityConfig {\n\n    @Bean <2>\n    public NullMethodAuthorizationDeniedHandler nullMethodAuthorizationDeniedHandler() {\n        return new NullMethodAuthorizationDeniedHandler();\n    }\n\n}\n\npublic class User {\n    // ...\n\n    @PreAuthorize(value = \"hasAuthority('user:read')\")\n    @HandleAuthorizationDenied(handlerClass = NullMethodAuthorizationDeniedHandler.class)\n    public String getEmail() {\n        return this.email;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass NullMethodAuthorizationDeniedHandler : MethodAuthorizationDeniedHandler { <1>\n\n    override fun handleDeniedInvocation(methodInvocation: MethodInvocation, authorizationResult: AuthorizationResult): Any {\n        return null\n    }\n\n}\n\n@Configuration\n@EnableMethodSecurity\nclass SecurityConfig {\n\n    @Bean <2>\n    fun nullMethodAuthorizationDeniedHandler(): NullMethodAuthorizationDeniedHandler {\n        return MaskMethodAuthorizationDeniedHandler()\n    }\n\n}\n\nclass User (val name:String, @PreAuthorize(value = \"hasAuthority('user:read')\") @HandleAuthorizationDenied(handlerClass = NullMethodAuthorizationDeniedHandler::class) val email:String) <3>\n----\n======\n\n<1> Create an implementation of `MethodAuthorizationDeniedHandler` that returns a `null` value\n<2> Register the `NullMethodAuthorizationDeniedHandler` as a bean\n<3> Annotate the method with `@HandleAuthorizationDenied` and pass the `NullMethodAuthorizationDeniedHandler` to the `handlerClass` attribute\n\nAnd then you can verify that a `null` value is returned instead of the `AccessDeniedException`:\n\n[TIP]\n====\nYou can also annotate your class with `@Component` instead of creating a `@Bean` method\n====\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nUserRepository users;\n\n@Test\nvoid getEmailWhenProxiedThenNullEmail() {\n    Optional<User> securedUser = users.findByName(\"name\");\n    assertThat(securedUser.get().getEmail()).isNull();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nvar users:UserRepository? = null\n\n@Test\nfun getEmailWhenProxiedThenNullEmail() {\n    val securedUser: Optional<User> = users.findByName(\"name\")\n    assertThat(securedUser.get().getEmail()).isNull()\n}\n----\n======\n\n=== Using the Denied Result From the Method Invocation\n\nThere are some scenarios where you might want to return a secure result derived from the denied result.\nFor example, if a user is not authorized to see email addresses, you might want to apply some masking on the original email address, i.e. _useremail@example.com_ would become _use\\\\******@example.com_.\n\nFor those scenarios, you can override the `handleDeniedInvocationResult` from the `MethodAuthorizationDeniedHandler`, which has the javadoc:org.springframework.security.authorization.method.MethodInvocationResult[] as an argument.\nLet's continue with the previous example, but instead of returning `null`, we will return a masked value of the email:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class EmailMaskingMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler { <1>\n\n    @Override\n    public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {\n        return \"***\";\n    }\n\n    @Override\n    public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult, AuthorizationResult authorizationResult) {\n        String email = (String) methodInvocationResult.getResult();\n        return email.replaceAll(\"(^[^@]{3}|(?!^)\\\\G)[^@]\", \"$1*\");\n    }\n\n}\n\n@Configuration\n@EnableMethodSecurity\npublic class SecurityConfig {\n\n    @Bean <2>\n    public EmailMaskingMethodAuthorizationDeniedHandler emailMaskingMethodAuthorizationDeniedHandler() {\n        return new EmailMaskingMethodAuthorizationDeniedHandler();\n    }\n\n}\n\npublic class User {\n    // ...\n\n    @PostAuthorize(value = \"hasAuthority('user:read')\")\n    @HandleAuthorizationDenied(handlerClass = EmailMaskingMethodAuthorizationDeniedHandler.class)\n    public String getEmail() {\n        return this.email;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass EmailMaskingMethodAuthorizationDeniedHandler : MethodAuthorizationDeniedHandler {\n\n    override fun handleDeniedInvocation(methodInvocation: MethodInvocation, authorizationResult: AuthorizationResult): Any {\n        return \"***\"\n    }\n\n    override fun handleDeniedInvocationResult(methodInvocationResult: MethodInvocationResult, authorizationResult: AuthorizationResult): Any {\n        val email = methodInvocationResult.result as String\n        return email.replace(\"(^[^@]{3}|(?!^)\\\\G)[^@]\".toRegex(), \"$1*\")\n    }\n\n}\n\n@Configuration\n@EnableMethodSecurity\nclass SecurityConfig {\n\n    @Bean\n    fun emailMaskingMethodAuthorizationDeniedHandler(): EmailMaskingMethodAuthorizationDeniedHandler {\n        return EmailMaskingMethodAuthorizationDeniedHandler()\n    }\n\n}\n\nclass User (val name:String, @PostAuthorize(value = \"hasAuthority('user:read')\") @HandleAuthorizationDenied(handlerClass = EmailMaskingMethodAuthorizationDeniedHandler::class) val email:String) <3>\n----\n======\n\n<1> Create an implementation of `MethodAuthorizationDeniedHandler` that returns a masked value of the unauthorized result value\n<2> Register the `EmailMaskingMethodAuthorizationDeniedHandler` as a bean\n<3> Annotate the method with `@HandleAuthorizationDenied` and pass the `EmailMaskingMethodAuthorizationDeniedHandler` to the `handlerClass` attribute\n\nAnd then you can verify that a masked email is returned instead of an `AccessDeniedException`:\n\n[WARNING]\n====\nSince you have access to the original denied value, make sure that you correctly handle it and do not return it to the caller.\n====\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nUserRepository users;\n\n@Test\nvoid getEmailWhenProxiedThenMaskedEmail() {\n    Optional<User> securedUser = users.findByName(\"name\");\n    // email is useremail@example.com\n    assertThat(securedUser.get().getEmail()).isEqualTo(\"use******@example.com\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nvar users:UserRepository? = null\n\n@Test\nfun getEmailWhenProxiedThenMaskedEmail() {\n    val securedUser: Optional<User> = users.findByName(\"name\")\n    // email is useremail@example.com\n    assertThat(securedUser.get().getEmail()).isEqualTo(\"use******@example.com\")\n}\n----\n======\n\nWhen implementing the `MethodAuthorizationDeniedHandler` you have a few options on what type you can return:\n\n- A `null` value.\n- A non-null value, respecting the method's return type.\n- Throw an exception, usually an instance of `AuthorizationDeniedException`. This is the default behavior.\n- A `Mono` type for reactive applications.\n\nNote that since the handler must be registered as beans in your application context, you can inject dependencies into them if you need a more complex logic.\nIn addition to that, you have available the `MethodInvocation` or the `MethodInvocationResult`, as well as the `AuthorizationResult` for more details related to the authorization decision.\n\n[[deciding-return-based-parameters]]\n=== Deciding What to Return Based on Available Parameters\n\nConsider a scenario where there might be multiple mask values for different methods, it would be not so productive if we had to create a handler for each of those methods, although it is perfectly fine to do that.\nIn such cases, we can use the information passed via parameters to decide what to do.\nFor example, we can create a custom `@Mask` annotation and a handler that detects that annotation to decide what mask value to return:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport org.springframework.core.annotation.AnnotationUtils;\n\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Mask {\n\n    String value();\n\n}\n\npublic class MaskAnnotationDeniedHandler implements MethodAuthorizationDeniedHandler {\n\n    @Override\n    public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {\n        Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);\n        return mask.value();\n    }\n\n}\n\n@Configuration\n@EnableMethodSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public MaskAnnotationDeniedHandler maskAnnotationDeniedHandler() {\n        return new MaskAnnotationDeniedHandler();\n    }\n\n}\n\n@Component\npublic class MyService {\n\n    @PreAuthorize(value = \"hasAuthority('user:read')\")\n    @HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler.class)\n    @Mask(\"***\")\n    public String foo() {\n        return \"foo\";\n    }\n\n    @PreAuthorize(value = \"hasAuthority('user:read')\")\n    @HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler.class)\n    @Mask(\"???\")\n    public String bar() {\n        return \"bar\";\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.core.annotation.AnnotationUtils\n\n@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)\n@Retention(AnnotationRetention.RUNTIME)\nannotation class Mask(val value: String)\n\nclass MaskAnnotationDeniedHandler : MethodAuthorizationDeniedHandler {\n\n    override fun handleDeniedInvocation(methodInvocation: MethodInvocation, authorizationResult: AuthorizationResult): Any {\n        val mask = AnnotationUtils.getAnnotation(methodInvocation.method, Mask::class.java)\n        return mask.value\n    }\n\n}\n\n@Configuration\n@EnableMethodSecurity\nclass SecurityConfig {\n\n    @Bean\n    fun maskAnnotationDeniedHandler(): MaskAnnotationDeniedHandler {\n        return MaskAnnotationDeniedHandler()\n    }\n\n}\n\n@Component\nclass MyService {\n\n    @PreAuthorize(value = \"hasAuthority('user:read')\")\n    @HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler::class)\n    @Mask(\"***\")\n    fun foo(): String {\n        return \"foo\"\n    }\n\n    @PreAuthorize(value = \"hasAuthority('user:read')\")\n    @HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler::class)\n    @Mask(\"???\")\n    fun bar(): String {\n        return \"bar\"\n    }\n\n}\n----\n======\n\nNow the return values when access is denied will be decided based on the `@Mask` annotation:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nMyService myService;\n\n@Test\nvoid fooWhenDeniedThenReturnStars() {\n    String value = this.myService.foo();\n    assertThat(value).isEqualTo(\"***\");\n}\n\n@Test\nvoid barWhenDeniedThenReturnQuestionMarks() {\n    String value = this.myService.bar();\n    assertThat(value).isEqualTo(\"???\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nvar myService: MyService\n\n@Test\nfun fooWhenDeniedThenReturnStars() {\n    val value: String = myService.foo()\n    assertThat(value).isEqualTo(\"***\")\n}\n\n@Test\nfun barWhenDeniedThenReturnQuestionMarks() {\n    val value: String = myService.bar()\n    assertThat(value).isEqualTo(\"???\")\n}\n----\n======\n\n=== Combining with Meta Annotation Support\n\nYou can also combine the `@HandleAuthorizationDenied` with other annotations in order to reduce and simplify the annotations in a method.\nLet's consider the <<deciding-return-based-parameters,example from the previous section>> and merge `@HandleAuthorizationDenied` with `@Mask`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler.class)\npublic @interface Mask {\n\n    String value();\n\n}\n\n@Mask(\"***\")\npublic String myMethod() {\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)\n@Retention(AnnotationRetention.RUNTIME)\n@HandleAuthorizationDenied(handlerClass = MaskAnnotationDeniedHandler::class)\nannotation class Mask(val value: String)\n\n@Mask(\"***\")\nfun myMethod(): String {\n    // ...\n}\n----\n======\n\nNow you do not have to remember to add both annotations when you need a mask behavior in your method.\nMake sure to read the <<meta-annotations,Meta Annotations Support>> section for more details on the usage.\n\n[[migration-enableglobalmethodsecurity]]\n== Migrating from `@EnableGlobalMethodSecurity`\n\nIf you are using `@EnableGlobalMethodSecurity`, you should migrate to `@EnableMethodSecurity`.\n\nIf you cannot migrate at this time, please include the `spring-security-access` module as a dependency like so:\n\n[tabs]\n======\nMaven::\n+\n[source,xml,role=\"primary\"]\n----\n<dependency>\n    <groupId>org.springframework.security</groupId>\n    <artifactId>spring-security-access</artifactId>\n</dependency>\n----\n\nGradle::\n+\n[source,groovy,role=\"primary\"]\n----\nimplementation('org.springframework.security:spring-security-access')\n----\n======\n\n[[servlet-replace-globalmethodsecurity-with-methodsecurity]]\n=== Replace xref:servlet/authorization/method-security.adoc#jc-enable-global-method-security[global method security] with xref:servlet/authorization/method-security.adoc#jc-enable-method-security[method security]\n\njavadoc:org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity[format=annotation] and xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[`<global-method-security>`] are deprecated in favor of javadoc:org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity[`@EnableMethodSecurity`] and xref:servlet/appendix/namespace/method-security.adoc#nsa-method-security[`<method-security>`], respectively.\nThe new annotation and XML element activate Spring's xref:servlet/authorization/method-security.adoc#jc-enable-method-security[pre-post annotations] by default and use `AuthorizationManager` internally.\n\nThis means that the following two listings are functionally equivalent:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableGlobalMethodSecurity(prePostEnabled = true)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableGlobalMethodSecurity(prePostEnabled = true)\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<global-method-security pre-post-enabled=\"true\"/>\n----\n======\n\nand:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableMethodSecurity\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableMethodSecurity\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<method-security/>\n----\n======\n\nFor applications not using the pre-post annotations, make sure to turn it off to avoid activating unwanted behavior.\n\nFor example, a listing like:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableGlobalMethodSecurity(securedEnabled = true)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableGlobalMethodSecurity(securedEnabled = true)\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<global-method-security secured-enabled=\"true\"/>\n----\n======\n\nshould change to:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@EnableMethodSecurity(securedEnabled = true, prePostEnabled = false)\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<method-security secured-enabled=\"true\" pre-post-enabled=\"false\"/>\n----\n======\n\n=== Use a Custom `@Bean` instead of subclassing `DefaultMethodSecurityExpressionHandler`\n\nAs a performance optimization, a new method was introduced to `MethodSecurityExpressionHandler` that takes a `Supplier<Authentication>` instead of an `Authentication`.\n\nThis allows Spring Security to defer the lookup of the `Authentication`, and is taken advantage of automatically when you use `@EnableMethodSecurity` instead of `@EnableGlobalMethodSecurity`.\n\nHowever, let's say that your code extends `DefaultMethodSecurityExpressionHandler` and overrides `createSecurityExpressionRoot(Authentication, MethodInvocation)` to return a custom `SecurityExpressionRoot` instance.\nThis will no longer work because the arrangement that `@EnableMethodSecurity` sets up calls `createEvaluationContext(Supplier<Authentication>, MethodInvocation)` instead.\n\nHappily, such a level of customization is often unnecessary.\nInstead, you can create a custom bean with the authorization methods that you need.\n\nFor example, let's say you are wanting a custom evaluation of `@PostAuthorize(\"hasAuthority('ADMIN')\")`.\nYou can create a custom `@Bean` like this one:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nclass MyAuthorizer {\n\tboolean isAdmin(MethodSecurityExpressionOperations root) {\n\t\tboolean decision = root.hasAuthority(\"ADMIN\");\n\t\t// custom work ...\n        return decision;\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass MyAuthorizer {\n\tfun isAdmin(root: MethodSecurityExpressionOperations): boolean {\n\t\tval decision = root.hasAuthority(\"ADMIN\");\n\t\t// custom work ...\n        return decision;\n\t}\n}\n----\n======\n\nand then refer to it in the annotation like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"@authz.isAdmin(#root)\")\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"@authz.isAdmin(#root)\")\n----\n======\n\n[[subclass-defaultmethodsecurityexpressionhandler]]\n==== I'd still prefer to subclass `DefaultMethodSecurityExpressionHandler`\n\nIf you must continue subclassing `DefaultMethodSecurityExpressionHandler`, you can still do so.\nInstead, override the `createEvaluationContext(Supplier<Authentication>, MethodInvocation)` method like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\nclass MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {\n    @Override\n    public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) {\n\t\tStandardEvaluationContext context = (StandardEvaluationContext) super.createEvaluationContext(authentication, mi);\n        MethodSecurityExpressionOperations delegate = (MethodSecurityExpressionOperations) context.getRootObject().getValue();\n        MySecurityExpressionRoot root = new MySecurityExpressionRoot(delegate);\n        context.setRootObject(root);\n        return context;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass MyExpressionHandler: DefaultMethodSecurityExpressionHandler {\n    override fun createEvaluationContext(authentication: Supplier<Authentication>,\n        val mi: MethodInvocation): EvaluationContext {\n\t\tval context = super.createEvaluationContext(authentication, mi) as StandardEvaluationContext\n        val delegate = context.getRootObject().getValue() as MethodSecurityExpressionOperations\n        val root = MySecurityExpressionRoot(delegate)\n        context.setRootObject(root)\n        return context\n    }\n}\n----\n======\n\n== Further Reading\n\nNow that you have secured your application's methods, please xref:servlet/authorization/authorize-http-requests.adoc[secure its requests] if you haven't already.\nYou can also read further on xref:servlet/test/index.adoc[testing your application] or on integrating Spring Security with other aspects of you application like xref:servlet/integrations/data.adoc[the data layer] or xref:servlet/integrations/observability.adoc[tracing and metrics].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/configuration/java.adoc",
    "content": "\n[[jc]]\n= Java Configuration\n\nGeneral support for {spring-framework-reference-url}core/beans/java.html[Java configuration] was added to Spring Framework in Spring 3.1.\nSpring Security 3.2 introduced Java configuration to let users configure Spring Security without the use of any XML.\n\nIf you are familiar with the xref:servlet/configuration/xml-namespace.adoc#ns-config[Security Namespace Configuration], you should find quite a few similarities between it and Spring Security Java configuration.\n\n[NOTE]\n====\nSpring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration[lots of sample applications] to demonstrate the use of Spring Security Java Configuration.\n====\n\n[[jc-hello-wsca]]\n== Hello Web Security Java Configuration\n\nThe first step is to create our Spring Security Java Configuration.\nThe configuration creates a Servlet Filter known as the `springSecurityFilterChain`, which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, and so on) within your application.\nThe following example shows the most basic example of a Spring Security Java Configuration:\n\n[source,java]\n----\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport org.springframework.context.annotation.*;\nimport org.springframework.security.config.annotation.authentication.builders.*;\nimport org.springframework.security.config.annotation.web.configuration.*;\n\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic UserDetailsService userDetailsService() {\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();\n\t\tmanager.createUser(User.withDefaultPasswordEncoder().username(\"user\").password(\"password\").roles(\"USER\").build());\n\t\treturn manager;\n\t}\n}\n----\n\nThis configuration is not complex or extensive, but it does a lot:\n\n* Require authentication to every URL in your application\n* Generate a login form for you\n* Let the user with a *Username* of `user` and a *Password* of `password` authenticate with form based authentication\n* Let the user logout\n* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention\n* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection\n* Security Header integration:\n** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests\n** https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx[X-Content-Type-Options] integration\n** Cache Control (which you can override later in your application to allow caching of your static resources)\n** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration\n** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking]\n* Integration with the following Servlet API methods:\n** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest#getRemoteUser()`]\n** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest#getUserPrincipal()`]\n** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest#isUserInRole(java.lang.String)`]\n** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[`HttpServletRequest#login(java.lang.String, java.lang.String)`]\n** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[`HttpServletRequest#logout()`]\n\n=== AbstractSecurityWebApplicationInitializer\n\nThe next step is to register the `springSecurityFilterChain` with the WAR file.\nYou can do so in Java configuration with {spring-framework-reference-url}web/webmvc/mvc-servlet/container-config.html[Spring's `WebApplicationInitializer` support] in a Servlet 3.0+ environment.\nNot surprisingly, Spring Security provides a base class (`AbstractSecurityWebApplicationInitializer`) to ensure that the `springSecurityFilterChain` gets registered for you.\nThe way in which we use `AbstractSecurityWebApplicationInitializer` differs depending on if we are already using Spring or if Spring Security is the only Spring component in our application.\n\n* <<abstractsecuritywebapplicationinitializer-without-existing-spring>> - Use these instructions if you are not already using Spring\n* <<abstractsecuritywebapplicationinitializer-with-spring-mvc>> - Use these instructions if you are already using Spring\n\n[[abstractsecuritywebapplicationinitializer-without-existing-spring]]\n=== AbstractSecurityWebApplicationInitializer without Existing Spring\n\nIf you are not using Spring or Spring MVC, you need to pass the `WebSecurityConfig` to the superclass to ensure the configuration is picked up:\n\n[source,java]\n----\nimport org.springframework.security.web.context.*;\n\npublic class SecurityWebApplicationInitializer\n\textends AbstractSecurityWebApplicationInitializer {\n\n\tpublic SecurityWebApplicationInitializer() {\n\t\tsuper(WebSecurityConfig.class);\n\t}\n}\n----\n\nThe `SecurityWebApplicationInitializer`:\n\n* Automatically registers the `springSecurityFilterChain` Filter for every URL in your application.\n* Add a `ContextLoaderListener` that loads the <<jc-hello-wsca,WebSecurityConfig>>.\n\n[[abstractsecuritywebapplicationinitializer-with-spring-mvc]]\n=== AbstractSecurityWebApplicationInitializer with Spring MVC\n\nIf we use Spring elsewhere in our application, we probably already have a `WebApplicationInitializer` that is loading our Spring Configuration.\nIf we use the previous configuration, we would get an error.\nInstead, we should register Spring Security with the existing `ApplicationContext`.\nFor example, if we use Spring MVC, our `SecurityWebApplicationInitializer` could look something like the following:\n\n[source,java]\n----\nimport org.springframework.security.web.context.*;\n\npublic class SecurityWebApplicationInitializer\n\textends AbstractSecurityWebApplicationInitializer {\n\n}\n----\n\nThis only registers the `springSecurityFilterChain` for every URL in your application.\nAfter that, we need to ensure that `WebSecurityConfig` was loaded in our existing `ApplicationInitializer`.\nFor example, if we use Spring MVC it is added in the `getServletConfigClasses()`:\n\n[[message-web-application-inititializer-java]]\n[source,java]\n----\npublic class MvcWebApplicationInitializer extends\n\t\tAbstractAnnotationConfigDispatcherServletInitializer {\n\n\t@Override\n\tprotected Class<?>[] getServletConfigClasses() {\n\t\treturn new Class[] { WebSecurityConfig.class, WebMvcConfig.class };\n\t}\n\n\t// ... other overrides ...\n}\n----\n\nThe reason for this is that Spring Security needs to be able to inspect some Spring MVC configuration in order to appropriately configure xref:servlet/authorization/authorize-http-requests.adoc#authorizing-endpoints[underlying request matchers], so they need to be in the same application context.\nPlacing Spring Security in `getRootConfigClasses` places it into a parent application context that may not be able to find Spring MVC's `PathPatternParser`.\n\n==== Configuring for Multiple Spring MVC Dispatchers\n\nIf desired, any Spring Security configuration that is unrelated to Spring MVC may be placed in a different configuration class like so:\n\n[source,java]\n----\npublic class MvcWebApplicationInitializer extends\n\t\tAbstractAnnotationConfigDispatcherServletInitializer {\n\n\t@Override\n    protected Class<?>[] getRootConfigClasses() {\n\t\treturn new Class[] { NonWebSecurityConfig.class };\n    }\n\n\t@Override\n\tprotected Class<?>[] getServletConfigClasses() {\n\t\treturn new Class[] { WebSecurityConfig.class, WebMvcConfig.class };\n\t}\n\n\t// ... other overrides ...\n}\n----\n\nThis can be helpful if you have multiple instances of `AbstractAnnotationConfigDispatcherServletInitializer` and don't want to duplicate the general security configuration across both of them.\n\n[[jc-httpsecurity]]\n== HttpSecurity\n\nThus far, our <<jc-hello-wsca,`WebSecurityConfig`>> contains only information about how to authenticate our users.\nHow does Spring Security know that we want to require all users to be authenticated?\nHow does Spring Security know we want to support form-based authentication?\nActually, there is a configuration class (called `SecurityFilterChain`) that is being invoked behind the scenes.\nIt is configured with the following default implementation:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t.anyRequest().authenticated()\n\t\t)\n\t\t.formLogin(Customizer.withDefaults())\n\t\t.httpBasic(Customizer.withDefaults());\n\treturn http.build();\n}\n----\n\nThe default configuration (shown in the preceding example):\n\n* Ensures that any request to our application requires the user to be authenticated\n* Lets users authenticate with form-based login\n* Lets users authenticate with HTTP Basic authentication\n\nNote that this configuration parallels the XML namespace configuration:\n\n[source,xml]\n----\n<http>\n\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t<form-login />\n\t<http-basic />\n</http>\n----\n\n=== Multiple HttpSecurity Instances\n\nTo effectively manage security in an application where certain areas need different protection, we can employ multiple filter chains alongside the `securityMatcher` DSL method.\nThis approach allows us to define distinct security configurations tailored to specific parts of the application, enhancing overall application security and control.\n\nWe can configure multiple `HttpSecurity` instances just as we can have multiple `<http>` blocks in XML.\nThe key is to register multiple `SecurityFilterChain` ``@Bean``s.\nThe following example has a different configuration for URLs that begin with `/api/`:\n\n[[multiple-httpsecurity-instances-java]]\n[source,java]\n----\n@Configuration\n@EnableWebSecurity\npublic class MultiHttpSecurityConfig {\n\t@Bean                                                             <1>\n\tpublic UserDetailsService userDetailsService() throws Exception {\n\t\tUserBuilder users = User.withDefaultPasswordEncoder();\n\t\tInMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();\n\t\tmanager.createUser(users.username(\"user\").password(\"password\").roles(\"USER\").build());\n\t\tmanager.createUser(users.username(\"admin\").password(\"password\").roles(\"USER\",\"ADMIN\").build());\n\t\treturn manager;\n\t}\n\n\t@Bean\n\t@Order(1)                                                        <2>\n\tpublic SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.securityMatcher(\"/api/**\")                              <3>\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().hasRole(\"ADMIN\")\n\t\t\t)\n\t\t\t.httpBasic(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n\n\t@Bean                                                            <4>\n\tpublic SecurityFilterChain formLoginFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.formLogin(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n}\n----\n<1> Configure Authentication as usual.\n<2> Create an instance of `SecurityFilterChain` that contains `@Order` to specify which `SecurityFilterChain` should be considered first.\n<3> The `http.securityMatcher()` states that this `HttpSecurity` is applicable only to URLs that begin with `/api/`.\n<4> Create another instance of `SecurityFilterChain`.\nIf the URL does not begin with `/api/`, this configuration is used.\nThis configuration is considered after `apiFilterChain`, since it has an `@Order` value after `1` (no `@Order` defaults to last).\n\n=== Choosing `securityMatcher` or `requestMatchers`\n\nA common question is:\n\n> What is the difference between the `http.securityMatcher()` method and `requestMatchers()` used for request authorization (i.e. inside of `http.authorizeHttpRequests()`)?\n\nTo answer this question, it helps to understand that each `HttpSecurity` instance used to build a `SecurityFilterChain` contains a `RequestMatcher` to match incoming requests.\nIf a request does not match a `SecurityFilterChain` with higher priority (e.g. `@Order(1)`), the request can be tried against a filter chain with lower priority (e.g. no `@Order`).\n\n[NOTE]\n====\nThe matching logic for multiple filter chains is performed by the xref:servlet/architecture.adoc#servlet-filterchainproxy[`FilterChainProxy`].\n====\n\nThe default `RequestMatcher` matches *any request* to ensure Spring Security protects *all requests by default*.\n\n[NOTE]\n====\nSpecifying a `securityMatcher` overrides this default.\n====\n\n[WARNING]\n====\nIf no filter chain matches a particular request, the request is *not protected* by Spring Security.\n====\n\nThe following example demonstrates a single filter chain that only protects requests that begin with `/secured/`:\n\n[[choosing-security-matcher-request-matchers-java]]\n[source,java]\n----\n@Configuration\n@EnableWebSecurity\npublic class PartialSecurityConfig {\n\n\t@Bean\n\tpublic UserDetailsService userDetailsService() throws Exception {\n\t\t// ...\n\t}\n\n\t@Bean\n\tpublic SecurityFilterChain securedFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.securityMatcher(\"/secured/**\")                            <1>\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(\"/secured/user\").hasRole(\"USER\")      <2>\n\t\t\t\t.requestMatchers(\"/secured/admin\").hasRole(\"ADMIN\")    <3>\n\t\t\t\t.anyRequest().authenticated()                          <4>\n\t\t\t)\n\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t.formLogin(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n}\n----\n<1> Requests that begin with `/secured/` will be protected but any other requests are not protected.\n<2> Requests to `/secured/user` require the `ROLE_USER` authority.\n<3> Requests to `/secured/admin` require the `ROLE_ADMIN` authority.\n<4> Any other requests (such as `/secured/other`) simply require an authenticated user.\n\n[TIP]\n====\nIt is _recommended_ to provide a `SecurityFilterChain` that does not specify any `securityMatcher` to ensure the entire application is protected, as demonstrated in the <<multiple-httpsecurity-instances-java,earlier example>>.\n====\n\nNotice that the `requestMatchers` method only applies to individual authorization rules.\nEach request listed there must also match the overall `securityMatcher` for this particular `HttpSecurity` instance used to create the `SecurityFilterChain`.\nUsing `anyRequest()` in this example matches all other requests within this particular `SecurityFilterChain` (which must begin with `/secured/`).\n\n[NOTE]\n====\nSee xref:servlet/authorization/authorize-http-requests.adoc[Authorize HttpServletRequests] for more information on `requestMatchers`.\n====\n\n=== `SecurityFilterChain` Endpoints\n\nSeveral filters in the `SecurityFilterChain` directly provide endpoints, such as the `UsernamePasswordAuthenticationFilter` which is set up by `http.formLogin()` and provides the `POST /login` endpoint.\nIn the <<choosing-security-matcher-request-matchers-java,above example>>, the `/login` endpoint is not matched by `http.securityMatcher(\"/secured/**\")` and therefore that application would not have any `GET /login` or `POST /login` endpoint.\nSuch requests would return `404 Not Found`.\nThis is often surprising to users.\n\nSpecifying `http.securityMatcher()` affects what requests are matched by that `SecurityFilterChain`.\nHowever, it does not automatically affect endpoints provided by the filter chain.\nIn such cases, you may need to customize the URL of any endpoints you would like the filter chain to provide.\n\nThe following example demonstrates a configuration that secures requests that begin with `/secured/` and denies all other requests, while also customizing endpoints provided by the `SecurityFilterChain`:\n\n[[security-filter-chain-endpoints-java]]\n[source,java]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecuredSecurityConfig {\n\n\t@Bean\n\tpublic UserDetailsService userDetailsService() throws Exception {\n\t\t// ...\n\t}\n\n\t@Bean\n\t@Order(1)\n\tpublic SecurityFilterChain securedFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.securityMatcher(\"/secured/**\")                            <1>\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()                          <2>\n\t\t\t)\n\t\t\t.formLogin((formLogin) -> formLogin                          <3>\n\t\t\t\t.loginPage(\"/secured/login\")\n\t\t\t\t.loginProcessingUrl(\"/secured/login\")\n\t\t\t\t.permitAll()\n\t\t\t)\n\t\t\t.logout((logout) -> logout                                   <4>\n\t\t\t\t.logoutUrl(\"/secured/logout\")\n\t\t\t\t.logoutSuccessUrl(\"/secured/login?logout\")\n\t\t\t\t.permitAll()\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().denyAll()                                <5>\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n<1> Requests that begin with `/secured/` will be protected by this filter chain.\n<2> Requests that begin with `/secured/` require an authenticated user.\n<3> Customize form login to prefix URLs with `/secured/`.\n<4> Customize logout to prefix URLs with `/secured/`.\n<5> All other requests will be denied.\n\n[NOTE]\n====\nThis example customizes the login and logout pages, which disables Spring Security's generated pages.\nYou must xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form-custom[provide your own] custom endpoints for `GET /secured/login` and `GET /secured/logout`.\nNote that Spring Security still provides `POST /secured/login` and `POST /secured/logout` endpoints for you.\n====\n\n=== Real World Example\n\nThe following example demonstrates a slightly more real-world configuration putting all of these elements together:\n\n[[real-world-example-java]]\n[source,java]\n----\n@Configuration\n@EnableWebSecurity\npublic class BankingSecurityConfig {\n\n    @Bean                                                              <1>\n    public UserDetailsService userDetailsService() {\n\t\tUserBuilder users = User.withDefaultPasswordEncoder();\n        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();\n        manager.createUser(users.username(\"user1\").password(\"password\").roles(\"USER\", \"VIEW_BALANCE\").build());\n        manager.createUser(users.username(\"user2\").password(\"password\").roles(\"USER\").build());\n        manager.createUser(users.username(\"admin\").password(\"password\").roles(\"ADMIN\").build());\n        return manager;\n    }\n\n    @Bean\n    @Order(1)                                                          <2>\n    public SecurityFilterChain approvalsSecurityFilterChain(HttpSecurity http) throws Exception {\n        String[] approvalsPaths = { \"/accounts/approvals/**\", \"/loans/approvals/**\", \"/credit-cards/approvals/**\" };\n        http\n            .securityMatcher(approvalsPaths)\n            .authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().hasRole(\"ADMIN\")\n            )\n            .httpBasic(Customizer.withDefaults());\n        return http.build();\n    }\n\n    @Bean\n    @Order(2)                                                          <3>\n    public SecurityFilterChain bankingSecurityFilterChain(HttpSecurity http) throws Exception {\n        String[] bankingPaths = { \"/accounts/**\", \"/loans/**\", \"/credit-cards/**\", \"/balances/**\" };\n\t\tString[] viewBalancePaths = { \"/balances/**\" };\n        http\n\t\t\t.securityMatcher(bankingPaths)\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(viewBalancePaths).hasRole(\"VIEW_BALANCE\")\n\t\t\t\t.anyRequest().hasRole(\"USER\")\n            );\n        return http.build();\n    }\n\n    @Bean                                                              <4>\n    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {\n\t\tString[] allowedPaths = { \"/\", \"/user-login\", \"/user-logout\", \"/notices\", \"/contact\", \"/register\" };\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(allowedPaths).permitAll()\n\t\t\t\t.anyRequest().authenticated()\n            )\n\t\t\t.formLogin((formLogin) -> formLogin\n\t\t\t\t.loginPage(\"/user-login\")\n\t\t\t\t.loginProcessingUrl(\"/user-login\")\n\t\t\t)\n\t\t\t.logout((logout) -> logout\n\t\t\t\t.logoutUrl(\"/user-logout\")\n\t\t\t\t.logoutSuccessUrl(\"/?logout\")\n\t\t\t);\n        return http.build();\n    }\n}\n----\n<1> Begin by configuring authentication settings.\n<2> Define a `SecurityFilterChain` instance with `@Order(1)`, which means that this filter chain will have the highest priority.\n    This filter chain applies only to requests that begin with `/accounts/approvals/`, `/loans/approvals/` or `/credit-cards/approvals/`.\n\tRequests to this filter chain require the `ROLE_ADMIN` authority and allow HTTP Basic Authentication.\n<3> Next, create another `SecurityFilterChain` instance with `@Order(2)` which will be considered second.\n    This filter chain applies only to requests that begin with `/accounts/`, `/loans/`, `/credit-cards/`, or `/balances/`.\n\tNotice that because this filter chain is second, any requests that include `/approvals/` will match the previous filter chain and will *not* be matched by this filter chain.\n\tRequests to this filter chain require the `ROLE_USER` authority.\n\tThis filter chain does not define any authentication because the next (default) filter chain contains that configuration.\n<4> Lastly, create an additional `SecurityFilterChain` instance without an `@Order` annotation.\n\tThis configuration will handle requests not covered by the other filter chains and will be processed last (no `@Order` defaults to last).\n\tRequests that match `/`, `/user-login`, `/user-logout`, `/notices`, `/contact` and `/register` allow access without authentication.\n\tAny other requests require the user to be authenticated to access any URL not explicitly allowed or protected by other filter chains.\n\n[[jc-custom-dsls]]\n== Custom DSLs\n\nYou can provide your own custom DSLs in Spring Security:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {\n\tprivate boolean flag;\n\n\t@Override\n\tpublic void init(HttpSecurity http) {\n\t\t// any method that adds another configurer\n\t\t// must be done in the init method\n\t\thttp.csrf(csrf -> csrf.disable());\n\t}\n\n\t@Override\n\tpublic void configure(HttpSecurity http) {\n\t\tApplicationContext context = http.getSharedObject(ApplicationContext.class);\n\n\t\t// here we lookup from the ApplicationContext. You can also just create a new instance.\n\t\tMyFilter myFilter = context.getBean(MyFilter.class);\n\t\tmyFilter.setFlag(flag);\n\t\thttp.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);\n\t}\n\n\tpublic MyCustomDsl flag(boolean value) {\n\t\tthis.flag = value;\n\t\treturn this;\n\t}\n\n\tpublic static MyCustomDsl customDsl() {\n\t\treturn new MyCustomDsl();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass MyCustomDsl : AbstractHttpConfigurer<MyCustomDsl, HttpSecurity>() {\n    var flag: Boolean = false\n\n    override fun init(http: HttpSecurity) {\n        // any method that adds another configurer\n        // must be done in the init method\n        http.csrf().disable()\n    }\n\n    override fun configure(http: HttpSecurity) {\n        val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)\n\n        // here we lookup from the ApplicationContext. You can also just create a new instance.\n        val myFilter: MyFilter = context.getBean(MyFilter::class.java)\n        myFilter.setFlag(flag)\n        http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter::class.java)\n    }\n\n    companion object {\n        @JvmStatic\n        fun customDsl(): MyCustomDsl {\n            return MyCustomDsl()\n        }\n    }\n}\n----\n======\n\n[NOTE]\n====\nThis is actually how methods like `HttpSecurity.authorizeHttpRequests()` are implemented.\n====\n\nYou can then use the custom DSL:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class Config {\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.with(MyCustomDsl.customDsl(), (dsl) -> dsl\n\t\t\t\t.flag(true)\n\t\t\t)\n\t\t\t// ...\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass Config {\n\n    @Bean\n    fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http\n            .with(MyCustomDsl.customDsl()) {\n                flag = true\n            }\n            // ...\n\n        return http.build()\n    }\n}\n----\n======\n\nThe code is invoked in the following order:\n\n* Code in the `Config.filterChain` method is invoked\n* Code in the `MyCustomDsl.init` method is invoked\n* Code in the `MyCustomDsl.configure` method is invoked\n\nIf you want, you can have `HttpSecurity` add `MyCustomDsl` by default by using `SpringFactories`.\nFor example, you can create a resource on the classpath named `META-INF/spring.factories` with the following contents:\n\n.META-INF/spring.factories\n[source]\n----\norg.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl\n----\n\nYou can also explicit disable the default:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n\n@Configuration\n@EnableWebSecurity\npublic class Config {\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.with(MyCustomDsl.customDsl(), (dsl) -> dsl\n\t\t\t\t.disable()\n\t\t\t)\n\t\t\t...;\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass Config {\n\n    @Bean\n    fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http\n            .with(MyCustomDsl.customDsl()) {\n                disable()\n            }\n            // ...\n        return http.build()\n    }\n\n}\n----\n======\n\n[[modular-httpsecurity-configuration]]\n== Modular HttpSecurity Configuration\n\nMany users prefer that their Spring Security configuration lives in a centralized place and will choose to configure it in a single `SecurityFilterChain` instance.\nHowever, there are times that users may want to modularize the configuration.\nThis can be done using:\n\n* xref:#httpsecurity-customizer-bean[Customizer<HttpSecurity> Beans]\n* xref:#top-level-customizer-bean[Top Level HttpSecurity Customizer Beans]\n\nNOTE: If you are using Spring Security's xref:servlet/configuration/kotlin.adoc[], then you can also expose `*Dsl -> Unit` Beans as outlined in xref:./kotlin.adoc#modular-httpsecuritydsl-configuration[Modular HttpSecurityDsl Configuration].\n\n\n[[httpsecurity-customizer-bean]]\n=== Customizer<HttpSecurity> Beans\n\nIf you would like to modularize your security configuration you can place logic in a `Customizer<HttpSecurity>` Bean.\nFor example, the following configuration will ensure all `HttpSecurity` instances are configured to:\n\ninclude-code::./HttpSecurityCustomizerBeanConfiguration[tag=httpSecurityCustomizer,indent=0]\n\n<1> Set the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] to `object-src 'none'`\n<2> xref:servlet/exploits/http.adoc#servlet-http-redirect[Redirect any request to https]\n\n\n[[top-level-customizer-bean]]\n=== Top Level HttpSecurity Customizer Beans\n\nIf you prefer to have further modularization of your security configuration, Spring Security will automatically apply any top level `HttpSecurity` `Customizer` Beans.\n\nA top level `HttpSecurity` `Customizer` type can be summarized as any `Customizer<T>` that matches `public HttpSecurity.*(Customizer<T>)`.\nThis translates to any `Customizer<T>` that is a single argument to a public method on javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity[].\n\nA few examples can help to clarify.\nIf `Customizer<ContentTypeOptionsConfig>` is published as a Bean, it will not be automatically applied because it is an argument to javadoc:org.springframework.security.config.annotation.web.configurers.HeadersConfigurer#contentTypeOptions(org.springframework.security.config.Customizer)[] which is not a method defined on `HttpSecurity`.\nHowever, if `Customizer<HeadersConfigurer<HttpSecurity>>` is published as a Bean, it will be automatically applied because it is an argument to javadoc:org.springframework.security.config.annotation.web.builders.HttpSecurity#headers(org.springframework.security.config.Customizer)[].\n\nFor example, the following configuration will ensure that the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] is set to `object-src 'none'`:\n\ninclude-code::./TopLevelCustomizerBeanConfiguration[tag=headersCustomizer,indent=0]\n\n[[customizer-bean-ordering]]\n=== Customizer Bean Ordering\n\nFirst each xref:#httpsecurity-customizer-bean[Customizer<HttpSecurity> Bean] is applied using {spring-framework-api-url}org/springframework/beans/factory/ObjectProvider.html#orderedStream()[ObjectProvider#orderedStream()].\nThis means that if there are multiple `Customizer<HttpSecurity>` Beans, the {spring-framework-api-url}org/springframework/core/annotation/Order.html[@Order] annotation can be added to the Bean definitions to control the ordering.\n\nNext every xref:#top-level-customizer-bean[Top Level HttpSecurity Customizer Beans] type is looked up and each is applied using `ObjectProvider#orderedStream()`.\nIf there is are two `Customizer<HeadersConfigurer<HttpSecurity>>` beans and two `Customizer<HttpsRedirectConfigurer<HttpSecurity>>` instances, the order that each `Customizer` type is invoked is undefined.\nHowever, the order that each instance of `Customizer<HttpsRedirectConfigurer<HttpSecurity>>` is defined by `ObjectProvider#orderedStream()` and can be controlled using `@Order` on the Bean the definitions.\n\nFinally, the `HttpSecurity` Bean is injected as a Bean.\nAll `Customizer` instances are applied before the `HttpSecurity` Bean is created.\nThis allows overriding the customizations provided by the `Customizer` Beans.\n\nYou can find an example below that illustrates the ordering:\n\ninclude-code::./CustomizerBeanOrderingConfiguration[tag=sample,indent=0]\n\n<1> First all `Customizer<HttpSecurity>` instances are applied.\nThe `adminAuthorization` Bean has the highest `@Order` so it is applied first.\nIf there are no `@Order` annotations on the `Customizer<HttpSecurity>` Beans or the `@Order` annotations had the same value, then the order that the `Customizer<HttpSecurity>` instances are applied is undefined.\n<2> The `userAuthorization` is applied next due to being an instance of `Customizer<HttpSecurity>`\n<3> The order that the `Customizer` types are undefined.\nIn this example, the order of `contentSecurityPolicy`, `contentTypeOptions`, and `httpsRedirect` are undefined.\nIf `@Order(Ordered.HIGHEST_PRECEDENCE)` was added to `contentTypeOptions`, then we would know that `contentTypeOptions` is before `contentSecurityPolicy` (they are the same type), but we do not know if `httpsRedirect` is before or after the `Customizer<HeadersConfigurer<HttpSecurity>>` Beans.\n<4> After all of the `Customizer` Beans are applied, the `HttpSecurity` is passed in as a Bean.\n\n\n[[post-processing-configured-objects]]\n== Post Processing Configured Objects\n\nSpring Security's Java configuration does not expose every property of every object that it configures.\nThis simplifies the configuration for a majority of users.\nAfter all, if every property were exposed, users could use standard bean configuration.\n\nWhile there are good reasons to not directly expose every property, users may still need more advanced configuration options.\nTo address this issue, Spring Security introduces the concept of an `ObjectPostProcessor`, which can be used to modify or replace many of the `Object` instances created by the Java Configuration.\nFor example, to configure the `filterSecurityPublishAuthorizationSuccess` property on `FilterSecurityInterceptor`, you can use the following:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t.anyRequest().authenticated()\n\t\t\t.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {\n\t\t\t\tpublic <O extends FilterSecurityInterceptor> O postProcess(\n\t\t\t\t\t\tO fsi) {\n\t\t\t\t\tfsi.setPublishAuthorizationSuccess(true);\n\t\t\t\t\treturn fsi;\n\t\t\t\t}\n\t\t\t})\n\t\t);\n\treturn http.build();\n}\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/configuration/kotlin.adoc",
    "content": "\n[[kotlin-config]]\n= Kotlin Configuration\n\nSpring Security Kotlin configuration has been available since Spring Security 5.3.\nIt lets users configure Spring Security by using a native Kotlin DSL.\n\n[NOTE]\n====\nSpring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/kotlin/hello-security[a sample application] to demonstrate the use of Spring Security Kotlin Configuration.\n====\n\n[[kotlin-config-httpsecurity]]\n== HttpSecurity\n\nHow does Spring Security know that we want to require all users to be authenticated?\nHow does Spring Security know we want to support form-based authentication?\nThere is a configuration class (called `SecurityFilterChain`) that is being invoked behind the scenes.\nIt is configured with the following default implementation:\n\n[source,kotlin]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        authorizeHttpRequests {\n            authorize(anyRequest, authenticated)\n        }\n        formLogin { }\n        httpBasic { }\n    }\n    return http.build()\n}\n----\n\n[NOTE]\nMake sure to import the `org.springframework.security.config.annotation.web.invoke` function to enable the Kotlin DSL in your class, as the IDE will not always auto-import the method, causing compilation issues.\n\nThe default configuration (shown in the preceding example):\n\n* Ensures that any request to our application requires the user to be authenticated\n* Lets users authenticate with form-based login\n* Lets users authenticate with HTTP Basic authentication\n\nNote that this configuration parallels the XML namespace configuration:\n\n[source,xml]\n----\n<http>\n\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t<form-login />\n\t<http-basic />\n</http>\n----\n\n=== Multiple HttpSecurity Instances\n\nTo effectively manage security in an application where certain areas need different protection, we can employ multiple filter chains alongside the `securityMatcher` DSL method.\nThis approach allows us to define distinct security configurations tailored to specific parts of the application, enhancing overall application security and control.\n\nWe can configure multiple `HttpSecurity` instances just as we can have multiple `<http>` blocks in XML.\nThe key is to register multiple `SecurityFilterChain` ``@Bean``s.\nThe following example has a different configuration for URLs that begin with `/api/`:\n\n[[multiple-httpsecurity-instances-kotlin]]\n[source,kotlin]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass MultiHttpSecurityConfig {\n    @Bean                                                            <1>\n    open fun userDetailsService(): UserDetailsService {\n        val users = User.withDefaultPasswordEncoder()\n        val manager = InMemoryUserDetailsManager()\n        manager.createUser(users.username(\"user\").password(\"password\").roles(\"USER\").build())\n        manager.createUser(users.username(\"admin\").password(\"password\").roles(\"USER\",\"ADMIN\").build())\n        return manager\n    }\n\n    @Bean\n    @Order(1)                                                        <2>\n    open fun apiFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            securityMatcher(\"/api/**\")                               <3>\n            authorizeHttpRequests {\n                authorize(anyRequest, hasRole(\"ADMIN\"))\n            }\n            httpBasic { }\n        }\n        return http.build()\n    }\n\n    @Bean                                                            <4>\n    open fun formLoginFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            formLogin { }\n        }\n        return http.build()\n    }\n}\n----\n<1> Configure Authentication as usual.\n<2> Create an instance of `SecurityFilterChain` that contains `@Order` to specify which `SecurityFilterChain` should be considered first.\n<3> The `http.securityMatcher()` states that this `HttpSecurity` is applicable only to URLs that begin with `/api/`.\n<4> Create another instance of `SecurityFilterChain`.\nIf the URL does not begin with `/api/`, this configuration is used.\nThis configuration is considered after `apiFilterChain`, since it has an `@Order` value after `1` (no `@Order` defaults to last).\n\n=== Choosing `securityMatcher` or `requestMatchers`\n\nA common question is:\n\n> What is the difference between the `http.securityMatcher()` method and `requestMatchers()` used for request authorization (i.e. inside of `http.authorizeHttpRequests()`)?\n\nTo answer this question, it helps to understand that each `HttpSecurity` instance used to build a `SecurityFilterChain` contains a `RequestMatcher` to match incoming requests.\nIf a request does not match a `SecurityFilterChain` with higher priority (e.g. `@Order(1)`), the request can be tried against a filter chain with lower priority (e.g. no `@Order`).\n\n[NOTE]\n====\nThe matching logic for multiple filter chains is performed by the xref:servlet/architecture.adoc#servlet-filterchainproxy[`FilterChainProxy`].\n====\n\nThe default `RequestMatcher` matches *any request* to ensure Spring Security protects *all requests by default*.\n\n[NOTE]\n====\nSpecifying a `securityMatcher` overrides this default.\n====\n\n[WARNING]\n====\nIf no filter chain matches a particular request, the request is *not protected* by Spring Security.\n====\n\nThe following example demonstrates a single filter chain that only protects requests that begin with `/secured/`:\n\n[[choosing-security-matcher-request-matchers-kotlin]]\n[source,kotlin]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass PartialSecurityConfig {\n\t@Bean\n\topen fun userDetailsService(): UserDetailsService {\n\t\t// ...\n\t}\n\n\t@Bean\n\topen fun securedFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tsecurityMatcher(\"/secured/**\")                             <1>\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(\"/secured/user\", hasRole(\"USER\"))            <2>\n\t\t\t\tauthorize(\"/secured/admin\", hasRole(\"ADMIN\"))          <3>\n\t\t\t\tauthorize(anyRequest, authenticated)                   <4>\n\t\t\t}\n\t\t\thttpBasic { }\n\t\t\tformLogin { }\n\t\t}\n\t\treturn http.build()\n\t}\n}\n----\n<1> Requests that begin with `/secured/` will be protected but any other requests are not protected.\n<2> Requests to `/secured/user` require the `ROLE_USER` authority.\n<3> Requests to `/secured/admin` require the `ROLE_ADMIN` authority.\n<4> Any other requests (such as `/secured/other`) simply require an authenticated user.\n\n[TIP]\n====\nIt is _recommended_ to provide a `SecurityFilterChain` that does not specify any `securityMatcher` to ensure the entire application is protected, as demonstrated in the <<multiple-httpsecurity-instances-kotlin,earlier example>>.\n====\n\nNotice that the `requestMatchers` method only applies to individual authorization rules.\nEach request listed there must also match the overall `securityMatcher` for this particular `HttpSecurity` instance used to create the `SecurityFilterChain`.\nUsing `anyRequest()` in this example matches all other requests within this particular `SecurityFilterChain` (which must begin with `/secured/`).\n\n[NOTE]\n====\nSee xref:servlet/authorization/authorize-http-requests.adoc[Authorize HttpServletRequests] for more information on `requestMatchers`.\n====\n\n=== `SecurityFilterChain` Endpoints\n\nSeveral filters in the `SecurityFilterChain` directly provide endpoints, such as the `UsernamePasswordAuthenticationFilter` which is set up by `http.formLogin()` and provides the `POST /login` endpoint.\nIn the <<choosing-security-matcher-request-matchers-kotlin,above example>>, the `/login` endpoint is not matched by `http.securityMatcher(\"/secured/**\")` and therefore that application would not have any `GET /login` or `POST /login` endpoint.\nSuch requests would return `404 Not Found`.\nThis is often surprising to users.\n\nSpecifying `http.securityMatcher()` affects what requests are matched by that `SecurityFilterChain`.\nHowever, it does not automatically affect endpoints provided by the filter chain.\nIn such cases, you may need to customize the URL of any endpoints you would like the filter chain to provide.\n\nThe following example demonstrates a configuration that secures requests that begin with `/secured/` and denies all other requests, while also customizing endpoints provided by the `SecurityFilterChain`:\n\n[[security-filter-chain-endpoints-kotlin]]\n[source,kotlin]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecuredSecurityConfig {\n\t@Bean\n\topen fun userDetailsService(): UserDetailsService {\n\t\t// ...\n\t}\n\n\t@Bean\n\t@Order(1)\n\topen fun securedFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tsecurityMatcher(\"/secured/**\")                             <1>\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, authenticated)                   <2>\n\t\t\t}\n\t\t\tformLogin {                                                <3>\n                loginPage = \"/secured/login\"\n                loginProcessingUrl = \"/secured/login\"\n                permitAll = true\n\t\t\t}\n\t\t\tlogout {                                                   <4>\n                logoutUrl = \"/secured/logout\"\n                logoutSuccessUrl = \"/secured/login?logout\"\n                permitAll = true\n\t\t\t}\n\t\t}\n\t\treturn http.build()\n\t}\n\n\t@Bean\n    open fun defaultFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, denyAll)                         <5>\n            }\n        }\n        return http.build()\n    }\n}\n----\n<1> Requests that begin with `/secured/` will be protected by this filter chain.\n<2> Requests that begin with `/secured/` require an authenticated user.\n<3> Customize form login to prefix URLs with `/secured/`.\n<4> Customize logout to prefix URLs with `/secured/`.\n<5> All other requests will be denied.\n\n[NOTE]\n====\nThis example customizes the login and logout pages, which disables Spring Security's generated pages.\nYou must xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form-custom[provide your own] custom endpoints for `GET /secured/login` and `GET /secured/logout`.\nNote that Spring Security still provides `POST /secured/login` and `POST /secured/logout` endpoints for you.\n====\n\n=== Real World Example\n\nThe following example demonstrates a slightly more real-world configuration putting all of these elements together:\n\n[[real-world-example-kotlin]]\n[source,kotlin]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass BankingSecurityConfig {\n    @Bean                                                              <1>\n    open fun userDetailsService(): UserDetailsService {\n        val users = User.withDefaultPasswordEncoder()\n        val manager = InMemoryUserDetailsManager()\n        manager.createUser(users.username(\"user1\").password(\"password\").roles(\"USER\", \"VIEW_BALANCE\").build())\n        manager.createUser(users.username(\"user2\").password(\"password\").roles(\"USER\").build())\n        manager.createUser(users.username(\"admin\").password(\"password\").roles(\"ADMIN\").build())\n        return manager\n    }\n\n    @Bean\n    @Order(1)                                                          <2>\n    open fun approvalsSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        val approvalsPaths = arrayOf(\"/accounts/approvals/**\", \"/loans/approvals/**\", \"/credit-cards/approvals/**\")\n        http {\n            securityMatcher(*approvalsPaths)\n            authorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, hasRole(\"ADMIN\"))\n            }\n            httpBasic { }\n        }\n        return http.build()\n    }\n\n    @Bean\n    @Order(2)                                                          <3>\n\topen fun bankingSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        val bankingPaths = arrayOf(\"/accounts/**\", \"/loans/**\", \"/credit-cards/**\", \"/balances/**\")\n\t\tval viewBalancePaths = arrayOf(\"/balances/**\")\n        http {\n            securityMatcher(*bankingPaths)\n            authorizeHttpRequests {\n                authorize(viewBalancePaths, hasRole(\"VIEW_BALANCE\"))\n\t\t\t\tauthorize(anyRequest, hasRole(\"USER\"))\n            }\n        }\n        return http.build()\n    }\n\n    @Bean                                                              <4>\n\topen fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        val allowedPaths = arrayOf(\"/\", \"/user-login\", \"/user-logout\", \"/notices\", \"/contact\", \"/register\")\n        http {\n            authorizeHttpRequests {\n                authorize(allowedPaths, permitAll)\n\t\t\t\tauthorize(anyRequest, authenticated)\n            }\n\t\t\tformLogin {\n                loginPage = \"/user-login\"\n                loginProcessingUrl = \"/user-login\"\n\t\t\t}\n\t\t\tlogout {\n                logoutUrl = \"/user-logout\"\n                logoutSuccessUrl = \"/?logout\"\n\t\t\t}\n        }\n        return http.build()\n    }\n}\n----\n<1> Begin by configuring authentication settings.\n<2> Define a `SecurityFilterChain` instance with `@Order(1)`, which means that this filter chain will have the highest priority.\n    This filter chain applies only to requests that begin with `/accounts/approvals/`, `/loans/approvals/` or `/credit-cards/approvals/`.\n\tRequests to this filter chain require the `ROLE_ADMIN` authority and allow HTTP Basic Authentication.\n<3> Next, create another `SecurityFilterChain` instance with `@Order(2)` which will be considered second.\n    This filter chain applies only to requests that begin with `/accounts/`, `/loans/`, `/credit-cards/`, or `/balances/`.\n\tNotice that because this filter chain is second, any requests that include `/approvals/` will match the previous filter chain and will *not* be matched by this filter chain.\n\tRequests to this filter chain require the `ROLE_USER` authority.\n\tThis filter chain does not define any authentication because the next (default) filter chain contains that configuration.\n<4> Lastly, create an additional `SecurityFilterChain` instance without an `@Order` annotation.\n\tThis configuration will handle requests not covered by the other filter chains and will be processed last (no `@Order` defaults to last).\n\tRequests that match `/`, `/user-login`, `/user-logout`, `/notices`, `/contact` and `/register` allow access without authentication.\n\tAny other requests require the user to be authenticated to access any URL not explicitly allowed or protected by other filter chains.\n\n\n[[modular-httpsecuritydsl-configuration]]\n== Modular HttpSecurityDsl Configuration\n\nMany users prefer that their Spring Security configuration lives in a centralized place and will choose to configure it in a single `SecurityFilterChain` instance.\nHowever, there are times that users may want to modularize the configuration.\nThis can be done using:\n\n* xref:#httpsecuritydsl-bean[HttpSecurityDsl.() -> Unit Beans]\n* xref:#top-level-dsl-bean[Top Level Security Dsl Beans]\n\nNOTE: Since the Spring Security Kotlin Dsl (`HttpSecurityDsl`) uses `HttpSecurity`, all of the Java xref:./kotlin.adoc#modular-bean-configuration[Modular Bean Customization] is applied before xref:#modular-httpsecuritydsl-configuration[Modular HttpSecurity Configuration].\n\n[[httpsecuritydsl-bean]]\n=== HttpSecurityDsl.() -> Unit Beans\n\nIf you would like to modularize your security configuration you can place logic in a `HttpSecurityDsl.() -> Unit` Bean.\nFor example, the following configuration will ensure all `HttpSecurityDsl` instances are configured to:\n\ninclude-code::./HttpSecurityDslBeanConfiguration[tag=httpSecurityDslBean,indent=0]\n\n<1> Set the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] to `object-src 'none'`\n<2> xref:servlet/exploits/http.adoc#servlet-http-redirect[Redirect any request to https]\n\n\n[[top-level-dsl-bean]]\n=== Top Level Security Dsl Beans\n\nIf you prefer to have further modularization of your security configuration, Spring Security will automatically apply any top level Security Dsl Beans.\n\nA top level Security Dsl can be summarized as any class Dsl class that matches `public HttpSecurityDsl.*(<Dsl>)`.\nThis translates to any Security Dsl that is a single argument to a public method on `HttpSecurityDsl`.\n\nA few examples can help to clarify.\nIf `ContentTypeOptionsDsl.() -> Unit` is published as a Bean, it will not be be automatically applied because it is an argument to `HeadersDsl#contentTypeOptions(ContentTypeOptionsDsl.() -> Unit)` and is not an argument to a method defined on `HttpSecurityDsl`.\nHowever, if `HeadersDsl.() -> Unit` is published as a Bean, it will be automatically applied because it is an argument to `HttpSecurityDsl.headers(HeadersDsl.() -> Unit)`.\n\nFor example, the following configuration ensure all `HttpSecurityDsl` instances are configured to:\n\ninclude-code::./TopLevelDslBeanConfiguration[tag=headersSecurity,indent=0]\n\n<1> Set the xref:servlet/exploits/headers.adoc#servlet-headers-csp[Content Security Policy] to `object-src 'none'`\n\n[[dsl-bean-ordering]]\n=== Dsl Bean Ordering\n\nFirst, all xref:servlet/configuration/java.adoc#modular-httpsecurity-configuration[Modular HttpSecurity Configuration] is applied since the Kotlin Dsl uses an `HttpSecurity` Bean.\n\nSecond, each xref:#httpsecuritydsl-bean[HttpSecurityDsl.() -> Unit Beans] is applied using {spring-framework-api-url}org/springframework/beans/factory/ObjectProvider.html#orderedStream()[ObjectProvider#orderedStream()].\nThis means that if there are multiple `HttpSecurity.() -> Unit` Beans, the {spring-framework-api-url}org/springframework/core/annotation/Order.html[@Order] annotation can be added to the Bean definitions to control the ordering.\n\nNext, every xref:#top-level-dsl-bean[Top Level Security Dsl Beans] type is looked up and each is is applied using `ObjectProvider#orderedStream()`.\nIf there is are different types of top level security Beans (e.g. `HeadersDsl.() -> Unit` and `HttpsRedirectDsl.() -> Unit`), then the order that each Dsl type is invoked is undefined.\nHowever, the order that each instance of of the same top level security Bean type is defined by `ObjectProvider#orderedStream()` and can be controlled using `@Order` on the Bean the definitions.\n\nFinally, the `HttpSecurityDsl` Bean is injected as a Bean.\nAll `*Dsl.() -> Unit` Beans are applied before the `HttpSecurityDsl` Bean is created.\nThis allows overriding the customizations provided by the `*Dsl.() -> Unit` Beans.\n\nYou can find an example below that illustrates the ordering:\n\ninclude-code::./DslBeanOrderingConfiguration[tag=sample,indent=0]\n\n<1> All xref:servlet/configuration/java.adoc#modular-httpsecurity-configuration[Modular HttpSecurity Configuration] is applied since the Kotlin Dsl uses an `HttpSecurity` Bean.\n<2> All `HttpSecurity.() -> Unit` instances are applied.\nThe `adminAuthorization` Bean has the highest `@Order` so it is applied first.\nIf there are no `@Order` annotations on the `HttpSecurity.() -> Unit` Beans or the `@Order` annotations had the same value, then the order that the `HttpSecurity.() -> Unit` instances are applied is undefined.\n<3> The `userAuthorization` is applied next due to being an instance of `HttpSecurity.() -> Unit`\n<4> The order that the `*Dsl.() -> Unit` types are undefined.\nIn this example, the order of `contentSecurityPolicy`, `contentTypeOptions`, and `httpsRedirect` are undefined.\nIf `@Order(Ordered.HIGHEST_PRECEDENCE)` was added to `contentTypeOptions`, then we would know that `contentTypeOptions` is before `contentSecurityPolicy` (they are the same type), but we do not know if `httpsRedirect` is before or after the `HeadersDsl.() -> Unit` Beans.\n<5> After all of the `*Dsl.() -> Unit` Beans are applied, the `HttpSecurityDsl` is passed in as a Bean.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/configuration/xml-namespace.adoc",
    "content": "\n[[ns-config]]\n= Security Namespace Configuration\n\n\nNamespace configuration has been available since version 2.0 of the Spring Framework.\nIt lets you supplement the traditional Spring beans application context syntax with elements from additional XML schema.\nYou can find more information in the Spring {spring-framework-reference-url}index.html[Reference Documentation].\nYou can use a namespace element to more concisely configure an individual bean or, more powerfully, to define an alternative configuration syntax that more closely matches the problem domain and hides the underlying complexity from the user.\nA simple element can conceal the fact that multiple beans and processing steps are being added to the application context.\nFor example, adding the following element from the `security` namespace to an application context starts up an embedded LDAP server for testing use within the application:\n\n[source,xml]\n----\n<security:ldap-server />\n----\n\nThis is much simpler than wiring up the equivalent UnboundID Server beans.\nThe most common alternative configuration requirements are supported by attributes on the `ldap-server` element, and the user is isolated from worrying about which beans they need to create and what the bean property names are.\nYou can find out more about the use of the `ldap-server` element in the chapter on xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap[LDAP Authentication].\nA good XML editor while editing the application context file should provide information on the attributes and elements that are available.\nWe recommend that you try the https://spring.io/tools/sts[Spring Tool Suite], as it has special features for working with standard Spring namespaces.\n\nTo start using the `security` namespace in your application context, add the `spring-security-config` jar to your classpath.\nThen, all you need to do is add the schema declaration to your application context file:\n\n[source,xml]\n----\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\nxmlns:security=\"http://www.springframework.org/schema/security\"\nxmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\nxsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\t\thttps://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\thttp://www.springframework.org/schema/security\n\t\thttps://www.springframework.org/schema/security/spring-security.xsd\">\n\t...\n</beans>\n----\n\nIn many of the examples you can see (and in the sample applications), we often use `security` (rather than `beans`) as the default namespace, which means we can omit the prefix on all the security namespace elements, making the content easier to read.\nYou may also want to do this if you have your application context divided up into separate files and have most of your security configuration in one of them.\nYour security application context file would then start like this:\n\n[source,xml]\n----\n<beans:beans xmlns=\"http://www.springframework.org/schema/security\"\nxmlns:beans=\"http://www.springframework.org/schema/beans\"\nxmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\nxsi:schemaLocation=\"http://www.springframework.org/schema/beans\n\t\thttps://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\thttp://www.springframework.org/schema/security\n\t\thttps://www.springframework.org/schema/security/spring-security.xsd\">\n\t...\n</beans:beans>\n----\n\nWe assume this syntax is being used from now on in this chapter.\n\n\n== Design of the Namespace\nThe namespace is designed to capture the most common uses of the framework and provide a simplified and concise syntax for enabling them within an application.\nThe design is based around the large-scale dependencies within the framework and can be divided up into the following areas:\n\n* _Web/HTTP Security_ is the most complex part.\nIt sets up the filters and related service beans used to apply the framework authentication mechanisms, to secure URLs, render login and error pages, and much more.\n\n* _Business Object (Method) Security_ defines options for securing the service layer.\n\n* _AuthenticationManager_ handles authentication requests from other parts of the framework.\n\n* _AccessDecisionManager_ provides access decisions for web and method security.\nA default one is registered, but you can choose to use a custom one, declared with normal Spring bean syntax.\n\n* _AuthenticationProvider_ instances provide mechanisms against which the authentication manager authenticates users.\nThe namespace provides supports for several standard options and a means of adding custom beans declared with a traditional syntax.\n\n* _UserDetailsService_ is closely related to authentication providers but is often also required by other beans.\n\nWe see how to configure these in the following sections.\n\n[[ns-getting-started]]\n== Getting Started with Security Namespace Configuration\nThis section looks at how you can build up a namespace configuration to use some of the main features of the framework.\nWe assume that you initially want to get up and running as quickly as possible and add authentication support and access control to an existing web application, with a few test logins.\nThen we look at how to change over to authenticating against a database or other security repository.\nIn later sections, we introduce more advanced namespace configuration options.\n\n[[ns-web-xml]]\n=== web.xml Configuration\nThe first thing you need to do is add the following filter declaration to your `web.xml` file:\n\n[source,xml]\n----\n<filter>\n<filter-name>springSecurityFilterChain</filter-name>\n<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\n</filter>\n\n<filter-mapping>\n<filter-name>springSecurityFilterChain</filter-name>\n<url-pattern>/*</url-pattern>\n</filter-mapping>\n----\n\n`DelegatingFilterProxy` is a Spring Framework class that delegates to a filter implementation that is defined as a Spring bean in your application context.\nIn this case, the bean is named `springSecurityFilterChain`, which is an internal infrastructure bean created by the namespace to handle web security.\nIn this case, the bean is named \"springSecurityFilterChain\", which is an internal infrastructure bean created by the namespace to handle web security.\nNote that you should not use this bean name yourself.\nOnce you have added this bean to your `web.xml`, you are ready to start editing your application context file.\nWeb security services are configured by the `<http>` element.\n\n[[ns-minimal]]\n=== A Minimal <http> Configuration\nTo enable web security, you need the following configuration:\n\n[source,xml]\n----\n<http>\n<intercept-url pattern=\"/**\" access=\"hasRole('USER')\" />\n<form-login />\n<logout />\n</http>\n----\n\nThat listing says that we want:\n\n* All URLs within our application to be secured, requiring the role `ROLE_USER` to access them\n* To log in to the application using a form with username and password\n* A logout URL registered which will allow us to log out of the application\n\nThe `<http>` element is the parent for all web-related namespace functionality.\nThe `<intercept-url>` element defines a `pattern`, which is matched against the URLs of incoming requests using Ant path syntax. See the section on xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`] for more details on how matches are actually performed.\nYou can also use regular-expression matching as an alternative (see the namespace appendix for more details).\nThe `access` attribute defines the access requirements for requests that match the given pattern.\nWith the default configuration, this is typically a comma-separated list of roles, one of which a user must have to be allowed to make the request.\nThe `ROLE_` prefix is a marker that indicates that a simple comparison with the user's authorities should be made.\nIn other words, a normal role-based check should be used.\nAccess-control in Spring Security is not limited to the use of simple roles (hence the use of the prefix to differentiate between different types of security attributes).\nWe see later how the interpretation can vary. The interpretation of the comma-separated values in the `access` attribute depends on the which implementation of the <<ns-access-manager,`AccessDecisionManager`>> is used.\nSince Spring Security 3.0, you can also populate the attribute with an  xref:servlet/authorization/authorize-http-requests.adoc#authorization-expressions[EL expression].\n\n\n[NOTE]\n====\nYou can use multiple `<intercept-url>` elements to define different access requirements for different sets of URLs, but they are evaluated in the order listed and the first match is used.\nSo you must put the most specific matches at the top.\nYou can also add a `method` attribute to limit the match to a particular HTTP method (`GET`, `POST`, `PUT`, and so on).\n====\n\nTo add users, you can define a set of test data directly in the namespace:\n\n[source,xml,attrs=\"-attributes\"]\n----\n<authentication-manager>\n<authentication-provider>\n\t<user-service>\n\t<!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that\n\tNoOpPasswordEncoder should be used. This is not safe for production, but makes reading\n\tin samples easier. Normally passwords should be hashed using BCrypt -->\n\t<user name=\"jimi\" password=\"{noop}jimispassword\" authorities=\"ROLE_USER, ROLE_ADMIN\" />\n\t<user name=\"bob\" password=\"{noop}bobspassword\" authorities=\"ROLE_USER\" />\n\t</user-service>\n</authentication-provider>\n</authentication-manager>\n----\n\nThe preceding listing shows an example of a secure way to store the same passwords.\nThe password is prefixed with `+{bcrypt}+` to instruct `DelegatingPasswordEncoder`, which supports any configured `PasswordEncoder` for matching, that the passwords are hashed using BCrypt:\n\n[source,xml,attrs=\"-attributes\"]\n----\n<authentication-manager>\n<authentication-provider>\n\t<user-service>\n\t<user name=\"jimi\" password=\"{bcrypt}$2a$10$ddEWZUl8aU0GdZPPpy7wbu82dvEw/pBpbRvDQRqA41y6mK1CoH00m\"\n\t\t\tauthorities=\"ROLE_USER, ROLE_ADMIN\" />\n\t<user name=\"bob\" password=\"{bcrypt}$2a$10$/elFpMBnAYYig6KRR5bvOOYeZr1ie1hSogJryg9qDlhza4oCw1Qka\"\n\t\t\tauthorities=\"ROLE_USER\" />\n\t<user name=\"jimi\" password=\"{noop}jimispassword\" authorities=\"ROLE_USER, ROLE_ADMIN\" />\n\t<user name=\"bob\" password=\"{noop}bobspassword\" authorities=\"ROLE_USER\" />\n\t</user-service>\n</authentication-provider>\n</authentication-manager>\n----\n\n\n[subs=\"quotes\"]\n****\nThe `<http>` element is responsible for creating a `FilterChainProxy` and the filter beans that it uses.\nPreviously common problems, such as incorrect filter ordering, are no longer an issue, as the filter positions are predefined.\n\nThe `<authentication-provider>` element creates a `DaoAuthenticationProvider` bean, and the `<user-service>` element creates an `InMemoryDaoImpl`.\nAll `authentication-provider` elements must be children of the `<authentication-manager>` element, which creates a `ProviderManager` and registers the authentication providers with it.\nYou can find more detailed information on the beans that are created in the xref:servlet/appendix/namespace/index.adoc#appendix-namespace[namespace appendix].\nYou should cross-check this appendix if you want to start understanding what the important classes in the framework are and how they are used, particularly if you want to customize things later.\n****\n\nThe preceding configuration defines two users, their passwords, and their roles within the application (which are used for access control).\nYou can also possible load user information from a standard properties file by setting the `properties` attribute on the `user-service` element.\nSee the section on xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] for more details on the file format.\nUsing the `<authentication-provider>` element means that the user information is used by the authentication manager to process authentication requests.\nYou can have multiple `<authentication-provider>` elements to define different authentication sources. Each is consulted in turn.\n\nAt this point, you should be able to start up your application, and you should be required to log in to proceed.\nTry it out, or try experimenting with the \"`tutorial`\" sample application that comes with the project.\n\n[[ns-form-target]]\n==== Setting a Default Post-Login Destination\nIf a form login is not prompted by an attempt to access a protected resource, the `default-target-url` option comes into play.\nThis is the URL to which the user is taken after successfully logging in. it defaults to `/`.\nYou can also configure things so that the user _always_ ends up at this page (regardless of whether the login was \"`on-demand`\" or they explicitly chose to log in) by setting the `always-use-default-target` attribute to `true`.\nThis is useful if your application always requires that the user starts at a \"`home`\" page, for example:\n\n[source,xml]\n----\n<http pattern=\"/login.htm*\" security=\"none\"/>\n<http use-expressions=\"false\">\n<intercept-url pattern='/**' access='ROLE_USER' />\n<form-login login-page='/login.htm' default-target-url='/home.htm'\n\t\talways-use-default-target='true' />\n</http>\n----\n\nFor even more control over the destination, you can use the `authentication-success-handler-ref` attribute as an alternative to `default-target-url`.\nThe referenced bean should be an instance of `AuthenticationSuccessHandler`.\n\n[[ns-web-advanced]]\n== Advanced Web Features\n\nThis section covers various features that go beyond the basics.\n\n[[ns-custom-filters]]\n=== Adding in Your Own Filters\nIf you have used Spring Security before, you know that the framework maintains a chain of filters that it uses to apply its services.\nYou may want to add your own filters to the stack at particular locations or use a Spring Security filter for which there is not currently a namespace configuration option (CAS, for example).\n// FIXME: Is it still true that there is no CAS filter?\nAlternatively, you might want to use a customized version of a standard namespace filter, such as the `UsernamePasswordAuthenticationFilter` (which is created by the `<form-login>` element) to take advantage of some of the extra configuration options that are available when you use the bean explicitly.\nHow can you do this with namespace configuration, since the filter chain is not directly exposed?\n\nThe order of the filters is always strictly enforced when you use the namespace.\nWhen the application context is being created, the filter beans are sorted by the namespace handling code, and the standard Spring Security filters each have an alias in the namespace and a well-known position.\n\n[NOTE]\n====\nIn previous versions, the sorting took place after the filter instances had been created, during post-processing of the application context.\nIn version 3.0+ the sorting is now done at the bean metadata level, before the classes have been instantiated.\nThis has implications for how you add your own filters to the stack as the entire filter list must be known during the parsing of the `<http>` element, so the syntax has changed slightly in 3.0.\n====\n\nThe filters, aliases, and namespace elements and attributes that create the filters are shown in the following table, in the order in which they occur in the filter chain:\n\n[[filter-stack]]\n.Standard Filter Aliases and Ordering\n|===\n| Alias | Filter Class | Namespace Element or Attribute\n\n| DISABLE_ENCODE_URL_FILTER\n| `DisableEncodeUrlFilter`\n| `http@disable-url-rewriting`\n\n| FORCE_EAGER_SESSION_FILTER\n| `ForceEagerSessionCreationFilter`\n| `http@create-session=\"ALWAYS\"`\n\n|  CHANNEL_FILTER\n| `ChannelProcessingFilter`\n| `http/intercept-url@requires-channel`\n\n|  SECURITY_CONTEXT_FILTER\n| `SecurityContextPersistenceFilter`\n| `http`\n\n|  CONCURRENT_SESSION_FILTER\n| `ConcurrentSessionFilter`\n| `session-management/concurrency-control`\n\n| HEADERS_FILTER\n| `HeaderWriterFilter`\n| `http/headers`\n\n| CSRF_FILTER\n| `CsrfFilter`\n| `http/csrf`\n\n|  LOGOUT_FILTER\n| `LogoutFilter`\n| `http/logout`\n\n|  X509_FILTER\n| `X509AuthenticationFilter`\n| `http/x509`\n\n|  PRE_AUTH_FILTER\n| `AbstractPreAuthenticatedProcessingFilter` Subclasses\n| N/A\n\n|  CAS_FILTER\n| `CasAuthenticationFilter`\n| N/A\n\n|  FORM_LOGIN_FILTER\n| `UsernamePasswordAuthenticationFilter`\n| `http/form-login`\n\n|  BASIC_AUTH_FILTER\n| `BasicAuthenticationFilter`\n| `http/http-basic`\n\n|  SERVLET_API_SUPPORT_FILTER\n| `SecurityContextHolderAwareRequestFilter`\n| `http/@servlet-api-provision`\n\n| JAAS_API_SUPPORT_FILTER\n| `JaasApiIntegrationFilter`\n| `http/@jaas-api-provision`\n\n|  REMEMBER_ME_FILTER\n| `RememberMeAuthenticationFilter`\n| `http/remember-me`\n\n|  ANONYMOUS_FILTER\n| `AnonymousAuthenticationFilter`\n| `http/anonymous`\n\n|  SESSION_MANAGEMENT_FILTER\n| `SessionManagementFilter`\n| `session-management`\n\n| EXCEPTION_TRANSLATION_FILTER\n| `ExceptionTranslationFilter`\n| `http`\n\n|  FILTER_SECURITY_INTERCEPTOR\n| `FilterSecurityInterceptor`\n| `http`\n\n|  SWITCH_USER_FILTER\n| `SwitchUserFilter`\n| N/A\n|===\n\nYou can add your own filter to the stack by using the `custom-filter` element and one of these names to specify the position at which your filter should appear:\n\n[source,xml]\n----\n<http>\n<custom-filter position=\"FORM_LOGIN_FILTER\" ref=\"myFilter\" />\n</http>\n\n<beans:bean id=\"myFilter\" class=\"com.mycompany.MySpecialAuthenticationFilter\"/>\n----\n\nYou can also use the `after` or `before` attributes if you want your filter to be inserted before or after another filter in the stack.\nYou can use `FIRST` and `LAST` with the `position` attribute to indicate that you want your filter to appear before or after the entire stack, respectively.\n\n.Avoiding filter position conflicts\n[TIP]\n====\nIf you insert a custom filter that may occupy the same position as one of the standard filters created by the namespace, you should not include the namespace versions by mistake.\nRemove any elements that create filters whose functionality you want to replace.\n\nNote that you cannot replace filters that are created by the use of the `<http>` element itself: `SecurityContextPersistenceFilter`, `ExceptionTranslationFilter`, or `FilterSecurityInterceptor`.\nBy default, an `AnonymousAuthenticationFilter` is added and unless you have xref:servlet/authentication/session-management.adoc#ns-session-fixation[session-fixation protection] disabled, a `SessionManagementFilter` is also added to the filter chain.\n====\n\nIf you replace a namespace filter that requires an authentication entry point (that is, where the authentication process is triggered by an unauthenticated user's attempt to access to a secured resource), you need to add a custom entry-point bean too.\n\n[[ns-method-security]]\n== Method Security\nSince version 2.0, Spring Security has substantial support for adding security to your service layer methods.\nIt provides support for JSR-250 annotation security as well as the framework's original `@Secured` annotation.\nSince version 3.0, you can also make use of xref:servlet/authorization/method-security.adoc#authorizing-with-annotations[expression-based annotations].\nYou can apply security to a single bean (by using the `intercept-methods` element to decorate the bean declaration), or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts.\n\n[[ns-access-manager]]\n== The Default AccessDecisionManager\nThis section assumes that you have some knowledge of the underlying architecture for access-control within Spring Security.\nIf you do not, you can skip it and come back to it later, as this section is relevant only for people who need to do some customization to use more than simple role-based security.\n\nWhen you use a namespace configuration, a default instance of `AccessDecisionManager` is automatically registered for you and is used to make access decisions for method invocations and web URL access, based on the access attributes you specify in your `intercept-url` and `protect-pointcut` declarations (and in annotations, if you use annotations to secure methods).\n\nThe default strategy is to use an `AffirmativeBased` `AccessDecisionManager` with a `RoleVoter` and an `AuthenticatedVoter`.\nYou can find out more about these in the chapter on xref:servlet/authorization/architecture.adoc#authz-arch[authorization].\n\n\n[[ns-custom-access-mgr]]\n=== Customizing the AccessDecisionManager\nIf you need to use a more complicated access control strategy, you can set an alternative for both method and web security.\n\nFor method security, you do so by setting the `access-decision-manager-ref` attribute on `global-method-security` to the `id` of the appropriate `AccessDecisionManager` bean in the application context:\n\n[source,xml]\n----\n<global-method-security access-decision-manager-ref=\"myAccessDecisionManagerBean\">\n...\n</global-method-security>\n----\n\nThe syntax for web security is the same, but the attribute is on the `http` element:\n\n[source,xml]\n----\n<http access-decision-manager-ref=\"myAccessDecisionManagerBean\">\n...\n</http>\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/exploits/csrf.adoc",
    "content": "[[servlet-csrf]]\n= Cross Site Request Forgery (CSRF)\n:figures: servlet/exploits\n\nIn an application where end users can xref:servlet/authentication/index.adoc[log in], it is important to consider how to protect against xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)].\n\nSpring Security protects against CSRF attacks by default for xref:features/exploits/csrf.adoc#csrf-protection-read-only[unsafe HTTP methods], such as a POST request, so no additional code is necessary.\nYou can specify the default configuration explicitly using the following:\n\n[[csrf-configuration]]\n.Configure CSRF Protection\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf { }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf/>\n</http>\n----\n======\n\nTo learn more about CSRF protection for your application, consider the following use cases:\n\n* I want to <<csrf-components,understand CSRF protection's components>>\n* I need to <<migrating-to-spring-security-6,migrate an application from Spring Security 5 to 6>>\n* I want to <<csrf-token-repository-cookie,store the `CsrfToken` in a cookie>> instead of <<csrf-token-repository-httpsession,the session>>\n* I want to <<csrf-token-repository-custom,store the `CsrfToken` in a custom location>>\n* I want to <<deferred-csrf-token-opt-out,opt-out of deferred tokens>>\n* I want to <<csrf-token-request-handler-opt-out-of-breach,opt-out of BREACH protection>>\n* I need guidance integrating <<csrf-integration-form,Thymeleaf, JSPs or another view technology>> with the backend\n* I need guidance integrating <<csrf-integration-javascript,Angular or another JavaScript framework>> with the backend\n* I need guidance integrating <<csrf-integration-mobile,a mobile application or another client>> with the backend\n* I need guidance on <<csrf-access-denied-handler,handling errors>>\n* I want to <<csrf-testing,test CSRF protection>>\n* I need guidance on <<disable-csrf,disabling CSRF protection>>\n\n[[csrf-components]]\n== Understanding CSRF Protection's Components\n\nCSRF protection is provided by several components that are composed within the javadoc:org.springframework.security.web.csrf.CsrfFilter[]:\n\n.`CsrfFilter` Components\n[.invert-dark]\nimage::{figures}/csrf.png[]\n\nCSRF protection is divided into two parts:\n\n1. Make the javadoc:org.springframework.security.web.csrf.CsrfToken[] available to the application by delegating to the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>.\n2. Determine if the request requires CSRF protection, load and validate the token, and <<csrf-access-denied-handler,handle `AccessDeniedException`>>.\n\n.`CsrfFilter` Processing\n[.invert-dark]\nimage::{figures}/csrf-processing.png[]\n\n* image:{icondir}/number_1.png[] First, the javadoc:org.springframework.security.web.csrf.DeferredCsrfToken[] is loaded, which holds a reference to the <<csrf-token-repository,`CsrfTokenRepository`>> so that the persisted `CsrfToken` can be loaded later (in image:{icondir}/number_4.png[]).\n* image:{icondir}/number_2.png[] Second, a `Supplier<CsrfToken>` (created from `DeferredCsrfToken`) is given to the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>, which is responsible for populating a request attribute to make the `CsrfToken` available to the rest of the application.\n* image:{icondir}/number_3.png[] Next, the main CSRF protection processing begins and checks if the current request requires CSRF protection. If not required, the filter chain is continued and processing ends.\n* image:{icondir}/number_4.png[] If CSRF protection is required, the persisted `CsrfToken` is finally loaded from the `DeferredCsrfToken`.\n* image:{icondir}/number_5.png[] Continuing, the actual CSRF token provided by the client (if any) is resolved using the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>.\n* image:{icondir}/number_6.png[] The actual CSRF token is compared against the persisted `CsrfToken`. If valid, the filter chain is continued and processing ends.\n* image:{icondir}/number_7.png[] If the actual CSRF token is invalid (or missing), an `AccessDeniedException` is passed to the <<csrf-access-denied-handler,`AccessDeniedHandler`>> and processing ends.\n\n[[migrating-to-spring-security-6]]\n== Migrating to Spring Security 6\n\nWhen migrating from Spring Security 5 to 6, there are a few changes that may impact your application.\nThe following is an overview of the aspects of CSRF protection that have changed in Spring Security 6:\n\n* Loading of the `CsrfToken` is now <<deferred-csrf-token,deferred by default>> to improve performance by no longer requiring the session to be loaded on every request.\n* The `CsrfToken` now includes <<csrf-token-request-handler-breach,randomness on every request by default>> to protect the CSRF token from a https://en.wikipedia.org/wiki/BREACH[BREACH] attack.\n\n[TIP]\n====\nThe changes in Spring Security 6 require additional configuration for single-page applications, and as such you may find the <<csrf-integration-javascript-spa>> section particularly useful.\n====\n\nSee the {site-url}/5.8/migration/servlet/exploits.html[Exploit Protection] section of the {site-url}/5.8/migration/index.html[Migration] chapter for more information on migrating a Spring Security 5 application.\n\n[[csrf-token-repository]]\n== Persisting the `CsrfToken`\n\nThe `CsrfToken` is persisted using a `CsrfTokenRepository`.\n\nBy default, the <<csrf-token-repository-httpsession,`HttpSessionCsrfTokenRepository`>> is used for storing tokens in a session.\nSpring Security also provides the <<csrf-token-repository-cookie,`CookieCsrfTokenRepository`>> for storing tokens in a cookie.\nYou can also specify <<csrf-token-repository-custom,your own implementation>> to store tokens wherever you like.\n\n[[csrf-token-repository-httpsession]]\n=== Using the `HttpSessionCsrfTokenRepository`\n\nBy default, Spring Security stores the expected CSRF token in the `HttpSession` by using javadoc:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository[], so no additional code is necessary.\n\nThe `HttpSessionCsrfTokenRepository` reads the token from a session (whether in-memory, cache, or database). If you need to access the session attribute directly, please first configure the session attribute name using `HttpSessionCsrfTokenRepository#setSessionAttributeName`.\n\nYou can specify the default configuration explicitly using the following configuration:\n\n[[csrf-token-repository-httpsession-configuration]]\n.Configure `HttpSessionCsrfTokenRepository`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t.csrfTokenRepository(new HttpSessionCsrfTokenRepository())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                csrfTokenRepository = HttpSessionCsrfTokenRepository()\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf token-repository-ref=\"tokenRepository\"/>\n</http>\n<b:bean id=\"tokenRepository\"\n\tclass=\"org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository\"/>\n----\n======\n\n[[csrf-token-repository-cookie]]\n=== Using the `CookieCsrfTokenRepository`\n\nYou can persist the `CsrfToken` in a cookie to <<csrf-integration-javascript,support a JavaScript-based application>> using the javadoc:org.springframework.security.web.csrf.CookieCsrfTokenRepository[].\n\nThe `CookieCsrfTokenRepository` writes to a cookie named `XSRF-TOKEN` and reads it from an HTTP request header named `X-XSRF-TOKEN` or the request parameter `_csrf` by default.\nThese defaults come from Angular and its predecessor https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS].\n\n[TIP]\n====\nSee the https://angular.dev/best-practices/security#httpclient-xsrf-csrf-security[HttpClient XSRF/CSRF security] and the https://angular.dev/api/common/http/withXsrfConfiguration[withXsrfConfiguration] for more recent information on this topic.\n====\n\nYou can configure the `CookieCsrfTokenRepository` using the following configuration:\n\n[[csrf-token-repository-cookie-configuration]]\n.Configure `CookieCsrfTokenRepository`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf token-repository-ref=\"tokenRepository\"/>\n</http>\n<b:bean id=\"tokenRepository\"\n\tclass=\"org.springframework.security.web.csrf.CookieCsrfTokenRepository\"\n\tp:cookieHttpOnly=\"false\"/>\n----\n======\n\n[NOTE]\n====\nThe example explicitly sets `HttpOnly` to `false`.\nThis is necessary to let JavaScript frameworks (such as Angular) read it.\nIf you do not need the ability to read the cookie with JavaScript directly, we _recommend_ omitting `HttpOnly` (by using `new CookieCsrfTokenRepository()` instead) to improve security.\n====\n\n[[csrf-token-repository-custom]]\n=== Customizing the `CsrfTokenRepository`\n\nThere can be cases where you want to implement a custom javadoc:org.springframework.security.web.csrf.CsrfTokenRepository[].\n\nOnce you've implemented the `CsrfTokenRepository` interface, you can configure Spring Security to use it with the following configuration:\n\n[[csrf-token-repository-custom-configuration]]\n.Configure Custom `CsrfTokenRepository`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t.csrfTokenRepository(new CustomCsrfTokenRepository())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                csrfTokenRepository = CustomCsrfTokenRepository()\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf token-repository-ref=\"tokenRepository\"/>\n</http>\n<b:bean id=\"tokenRepository\"\n\tclass=\"example.CustomCsrfTokenRepository\"/>\n----\n======\n\n[[csrf-token-request-handler]]\n== Handling the `CsrfToken`\n\nThe `CsrfToken` is made available to an application using a `CsrfTokenRequestHandler`.\nThis component is also responsible for resolving the `CsrfToken` from HTTP headers or request parameters.\n\nBy default, the <<csrf-token-request-handler-breach,`XorCsrfTokenRequestAttributeHandler`>> is used for providing https://en.wikipedia.org/wiki/BREACH[BREACH] protection of the `CsrfToken`.\nSpring Security also provides the <<csrf-token-request-handler-plain,`CsrfTokenRequestAttributeHandler`>> for opting out of BREACH protection.\nYou can also specify <<csrf-token-request-handler-custom,your own implementation>> to customize the strategy for handling and resolving tokens.\n\n[[csrf-token-request-handler-breach]]\n=== Using the `XorCsrfTokenRequestAttributeHandler` (BREACH)\n\nThe `XorCsrfTokenRequestAttributeHandler` makes the `CsrfToken` available as an `HttpServletRequest` attribute called `_csrf`, and additionally provides protection for https://en.wikipedia.org/wiki/BREACH[BREACH].\n\n[NOTE]\n====\nThe `CsrfToken` is also made available as a request attribute using the name `CsrfToken.class.getName()`.\nThis name is not configurable, but the name `_csrf` can be changed using `XorCsrfTokenRequestAttributeHandler#setCsrfRequestAttributeName`.\n====\n\nThis implementation also resolves the token value from the request as either a request header (one of <<csrf-token-repository-httpsession,`X-CSRF-TOKEN`>> or <<csrf-token-repository-cookie,`X-XSRF-TOKEN`>> by default) or a request parameter (`_csrf` by default).\n\n[NOTE]\n====\nBREACH protection is provided by encoding randomness into the CSRF token value to ensure the returned `CsrfToken` changes on every request.\nWhen the token is later resolved as a header value or request parameter, it is decoded to obtain the raw token which is then compared to the <<csrf-token-repository,persisted `CsrfToken`>>.\n====\n\nSpring Security protects the CSRF token from a BREACH attack by default, so no additional code is necessary.\nYou can specify the default configuration explicitly using the following configuration:\n\n[[csrf-token-request-handler-breach-configuration]]\n.Configure BREACH protection\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t.csrfTokenRequestHandler(new XorCsrfTokenRequestAttributeHandler())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                csrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf request-handler-ref=\"requestHandler\"/>\n</http>\n<b:bean id=\"requestHandler\"\n\tclass=\"org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler\"/>\n----\n======\n\n[[csrf-token-request-handler-plain]]\n=== Using the `CsrfTokenRequestAttributeHandler`\n\nThe `CsrfTokenRequestAttributeHandler` makes the `CsrfToken` available as an `HttpServletRequest` attribute called `_csrf`.\n\n[NOTE]\n====\nThe `CsrfToken` is also made available as a request attribute using the name `CsrfToken.class.getName()`.\nThis name is not configurable, but the name `_csrf` can be changed using `CsrfTokenRequestAttributeHandler#setCsrfRequestAttributeName`.\n====\n\nThis implementation also resolves the token value from the request as either a request header (one of <<csrf-token-repository-httpsession,`X-CSRF-TOKEN`>> or <<csrf-token-repository-cookie,`X-XSRF-TOKEN`>> by default) or a request parameter (`_csrf` by default).\n\n[[csrf-token-request-handler-opt-out-of-breach]]\nThe primary use of `CsrfTokenRequestAttributeHandler` is to opt-out of BREACH protection of the `CsrfToken`, which can be configured using the following configuration:\n\n.Opt-out of BREACH protection\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                csrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf request-handler-ref=\"requestHandler\"/>\n</http>\n<b:bean id=\"requestHandler\"\n\tclass=\"org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler\"/>\n----\n======\n\n[[csrf-token-request-handler-custom]]\n=== Customizing the `CsrfTokenRequestHandler`\n\nYou can implement the `CsrfTokenRequestHandler` interface to customize the strategy for handling and resolving tokens.\n\n[TIP]\n====\nThe `CsrfTokenRequestHandler` interface is a `@FunctionalInterface` that can be implemented using a lambda expression to customize request handling.\nYou will need to implement the full interface to customize how tokens are resolved from the request.\nSee <<csrf-integration-javascript-spa-configuration>> for an example that uses delegation to implement a custom strategy for handling and resolving tokens.\n====\n\nOnce you've implemented the `CsrfTokenRequestHandler` interface, you can configure Spring Security to use it with the following configuration:\n\n[[csrf-token-request-handler-custom-configuration]]\n.Configure Custom `CsrfTokenRequestHandler`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t.csrfTokenRequestHandler(new CustomCsrfTokenRequestHandler())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                csrfTokenRequestHandler = CustomCsrfTokenRequestHandler()\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf request-handler-ref=\"requestHandler\"/>\n</http>\n<b:bean id=\"requestHandler\"\n\tclass=\"example.CustomCsrfTokenRequestHandler\"/>\n----\n======\n\n[[deferred-csrf-token]]\n== Deferred Loading of the `CsrfToken`\n\nBy default, Spring Security defers loading of the `CsrfToken` until it is needed.\n\n[NOTE]\n====\nThe `CsrfToken` is needed whenever a request is made with an xref:features/exploits/csrf.adoc#csrf-protection-read-only[unsafe HTTP method], such as a POST.\nAdditionally, it is needed by any request that renders the token to the response, such as a web page with a `<form>` tag that includes a hidden `<input>` for the CSRF token.\n====\n\nBecause Spring Security also stores the `CsrfToken` in the `HttpSession` by default, deferred CSRF tokens can improve performance by not requiring the session to be loaded on every request.\n\n[[deferred-csrf-token-opt-out]]\nIn the event that you want to opt-out of deferred tokens and cause the `CsrfToken` to be loaded on every request, you can do so with the following configuration:\n\n[[deferred-csrf-token-opt-out-configuration]]\n.Opt-out of Deferred CSRF Tokens\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\tXorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();\n\t\t// set the name of the attribute the CsrfToken will be populated on\n\t\trequestHandler.setCsrfRequestAttributeName(null);\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t.csrfTokenRequestHandler(requestHandler)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        val requestHandler = XorCsrfTokenRequestAttributeHandler()\n        // set the name of the attribute the CsrfToken will be populated on\n        requestHandler.setCsrfRequestAttributeName(null)\n        http {\n            // ...\n            csrf {\n                csrfTokenRequestHandler = requestHandler\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf request-handler-ref=\"requestHandler\"/>\n</http>\n<b:bean id=\"requestHandler\"\n\tclass=\"org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler\">\n\t<b:property name=\"csrfRequestAttributeName\">\n\t\t<b:null/>\n\t</b:property>\n</b:bean>\n----\n======\n\n[NOTE]\n====\nBy setting the `csrfRequestAttributeName` to `null`, the `CsrfToken` must first be loaded to determine what attribute name to use.\nThis causes the `CsrfToken` to be loaded on every request.\n====\n\n\n[[csrf-integration]]\n== Integrating with CSRF Protection\n\nFor the xref:features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request.\nThis must be included in a part of the request (a form parameter, an HTTP header, or other part) that is not automatically included in the HTTP request by the browser.\n\nThe following sections describe the various ways a frontend or client application can integrate with a CSRF-protected backend application:\n\n* <<csrf-integration-form>>\n* <<csrf-integration-javascript>>\n* <<csrf-integration-mobile>>\n\n[[csrf-integration-form]]\n=== HTML Forms\n\nTo submit an HTML form, the CSRF token must be included in the form as a hidden input.\nFor example, the rendered HTML might look like:\n\n.CSRF Token in HTML Form\n[source,html]\n----\n<input type=\"hidden\"\n\tname=\"_csrf\"\n\tvalue=\"4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\"/>\n----\n\nThe following view technologies automatically include the actual CSRF token in a form that has an unsafe HTTP method, such as a POST:\n\n* {spring-framework-reference-url}web/webmvc-view/mvc-jsp.html#mvc-view-jsp-formtaglib[Spring’s form tag library]\n* https://www.thymeleaf.org/doc/tutorials/3.1/thymeleafspring.html#integration-with-requestdatavalueprocessor[Thymeleaf]\n* Any other view technology that integrates with {spring-framework-api-url}org/springframework/web/servlet/support/RequestDataValueProcessor.html[`RequestDataValueProcessor`] (via javadoc:org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor[])\n* You can also include the token yourself via the xref:servlet/integrations/jsp-taglibs.adoc#taglibs-csrfinput[csrfInput] tag\n\nIf these options are not available, you can take advantage of the fact that the `CsrfToken` is exposed as an <<csrf-token-request-handler,`HttpServletRequest` attribute named `_csrf`>>.\nThe following example does this with a JSP:\n\n.CSRF Token in HTML Form with Request Attribute\n[source,xml]\n----\n<c:url var=\"logoutUrl\" value=\"/logout\"/>\n<form action=\"${logoutUrl}\"\n\tmethod=\"post\">\n<input type=\"submit\"\n\tvalue=\"Log out\" />\n<input type=\"hidden\"\n\tname=\"${_csrf.parameterName}\"\n\tvalue=\"${_csrf.token}\"/>\n</form>\n----\n\n[[csrf-integration-javascript]]\n=== JavaScript Applications\n\nJavaScript applications typically use JSON instead of HTML.\nIf you use JSON, you can submit the CSRF token within an HTTP request header instead of a request parameter.\n\nIn order to obtain the CSRF token, you can configure Spring Security to store the expected CSRF token <<csrf-token-repository-cookie,in a cookie>>.\nBy storing the expected token in a cookie, JavaScript frameworks such as https://angular.io/api/common/http/HttpClientXsrfModule[Angular] can automatically include the actual CSRF token as an HTTP request header.\n\n[TIP]\n====\nThere are special considerations for BREACH protection and deferred tokens when integrating a single-page application (SPA) with Spring Security's CSRF protection.\nA full configuration example is provided in the <<csrf-integration-javascript-spa,next section>>.\n====\n\nYou can read about different types of JavaScript applications in the following sections:\n\n* <<csrf-integration-javascript-spa>>\n* <<csrf-integration-javascript-mpa>>\n* <<csrf-integration-javascript-other>>\n\n[[csrf-integration-javascript-spa]]\n==== Single-Page Applications\n\nThere are special considerations for integrating a single-page application (SPA) with Spring Security's CSRF protection.\n\nRecall that Spring Security provides <<csrf-token-request-handler-breach,BREACH protection of the `CsrfToken`>> by default.\nWhen storing the expected CSRF token <<csrf-token-repository-cookie,in a cookie>>, JavaScript applications will only have access to the plain token value and _will not_ have access to the encoded value.\nA <<csrf-token-request-handler-custom,customized request handler>> for resolving the actual token value will need to be provided.\n\nIn addition, the cookie storing the CSRF token will be cleared upon authentication success and logout success.\nSpring Security defers loading a new CSRF token by default, and additional work is required to return a fresh cookie.\n\n[NOTE]\n====\nRefreshing the token after authentication success and logout success is required because the javadoc:org.springframework.security.web.csrf.CsrfAuthenticationStrategy[] and javadoc:org.springframework.security.web.csrf.CsrfLogoutHandler[] will clear the previous token.\nThe client application will not be able to perform an unsafe HTTP request, such as a POST, without obtaining a fresh token.\n====\n\nIn order to easily integrate a single-page application with Spring Security, the following configuration can be used:\n\n[[csrf-integration-javascript-spa-configuration]]\n.Configure CSRF for Single-Page Application\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf.spa());\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                spa()\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf>\n        <spa />\n    </csrf>\n</http>\n----\n======\n\n[[csrf-integration-javascript-mpa]]\n==== Multi-Page Applications\n\nFor multi-page applications where JavaScript is loaded on each page, an alternative to exposing the CSRF token <<csrf-token-repository-cookie,in a cookie>> is to include the CSRF token within your `meta` tags.\nThe HTML might look something like this:\n\n.CSRF Token in HTML Meta Tag\n[source,html]\n----\n<html>\n<head>\n\t<meta name=\"_csrf\" content=\"4bfd1575-3ad1-4d21-96c7-4ef2d9f86721\"/>\n\t<meta name=\"_csrf_header\" content=\"X-CSRF-TOKEN\"/>\n\t<!-- ... -->\n</head>\n<!-- ... -->\n</html>\n----\n\nIn order to include the CSRF token in the request, you can take advantage of the fact that the `CsrfToken` is exposed as an <<csrf-token-request-handler,`HttpServletRequest` attribute named `_csrf`>>.\nThe following example does this with a JSP:\n\n.CSRF Token in HTML Meta Tag with Request Attribute\n[source,html]\n----\n<html>\n<head>\n\t<meta name=\"_csrf\" content=\"${_csrf.token}\"/>\n\t<!-- default header name is X-CSRF-TOKEN -->\n\t<meta name=\"_csrf_header\" content=\"${_csrf.headerName}\"/>\n\t<!-- ... -->\n</head>\n<!-- ... -->\n</html>\n----\n\nOnce the meta tags contain the CSRF token, the JavaScript code can read the meta tags and include the CSRF token as a header.\nIf you use jQuery, you can do this with the following code:\n\n.Include CSRF Token in AJAX Request\n[source,javascript]\n----\n$(function () {\n\tvar token = $(\"meta[name='_csrf']\").attr(\"content\");\n\tvar header = $(\"meta[name='_csrf_header']\").attr(\"content\");\n\t$(document).ajaxSend(function(e, xhr, options) {\n\t\txhr.setRequestHeader(header, token);\n\t});\n});\n----\n\n[[csrf-integration-javascript-other]]\n==== Other JavaScript Applications\n\nAnother option for JavaScript applications is to include the CSRF token in an HTTP response header.\n\nOne way to achieve this is through the use of a `@ControllerAdvice` with the xref:servlet/integrations/mvc.adoc#mvc-csrf-resolver[`CsrfTokenArgumentResolver`].\nThe following is an example of `@ControllerAdvice` that applies to all controller endpoints in the application:\n\n[[controller-advice]]\n.CSRF Token in HTTP Response Header\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@ControllerAdvice\npublic class CsrfControllerAdvice {\n\n\t@ModelAttribute\n\tpublic void getCsrfToken(HttpServletResponse response, CsrfToken csrfToken) {\n\t\tresponse.setHeader(csrfToken.getHeaderName(), csrfToken.getToken());\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@ControllerAdvice\nclass CsrfControllerAdvice {\n\n\t@ModelAttribute\n\tfun getCsrfToken(response: HttpServletResponse, csrfToken: CsrfToken) {\n\t\tresponse.setHeader(csrfToken.headerName, csrfToken.token)\n\t}\n\n}\n----\n======\n\n[NOTE]\n====\nBecause this `@ControllerAdvice` applies to all endpoints in the application, it will cause the CSRF token to be loaded on every request, which can negate the benefits of <<deferred-csrf-token,deferred tokens>> when using the <<csrf-token-repository-httpsession,`HttpSessionCsrfTokenRepository`>>.\nHowever, this is not usually an issue when using the <<csrf-token-repository-cookie,`CookieCsrfTokenRepository`>>.\n====\n\n[NOTE]\n====\nIt is important to remember that controller endpoints and controller advice are called _after_ the Spring Security filter chain.\nThis means that this `@ControllerAdvice` will only be applied if the request passes through the filter chain to your application.\nSee the configuration for <<csrf-integration-javascript-spa-configuration,single-page applications>> for an example of adding a filter to the filter chain for earlier access to the `HttpServletResponse`.\n====\n\nThe CSRF token will now be available in a response header (<<csrf-token-repository-httpsession,`X-CSRF-TOKEN`>> or <<csrf-token-repository-cookie,`X-XSRF-TOKEN`>> by default) for any custom endpoints the controller advice applies to.\nAny request to the backend can be used to obtain the token from the response, and a subsequent request can include the token in a request header with the same name.\n\n[[csrf-integration-mobile]]\n=== Mobile Applications\n\nLike <<csrf-integration-javascript,JavaScript applications>>, mobile applications typically use JSON instead of HTML.\nA backend application that _does not_ serve browser traffic may choose to <<disable-csrf,disable CSRF>>.\nIn that case, no additional work is required.\n\nHowever, a backend application that also serves browser traffic and therefore _still requires_ CSRF protection may continue to store the `CsrfToken` <<csrf-token-repository-httpsession,in the session>> instead of <<csrf-token-repository-cookie,in a cookie>>.\n\nIn this case, a typical pattern for integrating with the backend is to expose a `/csrf` endpoint to allow the frontend (mobile or browser client) to request a CSRF token on demand.\nThe benefit of using this pattern is that the CSRF token <<deferred-csrf-token,can continue to be deferred>> and only needs to be loaded from the session when a request requires CSRF protection.\nThe use of a custom endpoint also means the client application can request that a new token be generated on demand (if necessary) by issuing an explicit request.\n\n[TIP]\n====\nThis pattern can be used for any type of application that requires CSRF protection, not just mobile applications.\nWhile this approach isn't typically required in those cases, it is another option for integrating with a CSRF-protected backend.\n====\n\nThe following is an example of the `/csrf` endpoint that makes use of the xref:servlet/integrations/mvc.adoc#mvc-csrf-resolver[`CsrfTokenArgumentResolver`]:\n\n[[csrf-endpoint]]\n.The `/csrf` endpoint\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class CsrfController {\n\n    @GetMapping(\"/csrf\")\n    public CsrfToken csrf(CsrfToken csrfToken) {\n        return csrfToken;\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RestController\nclass CsrfController {\n\n    @GetMapping(\"/csrf\")\n    fun csrf(csrfToken: CsrfToken): CsrfToken {\n        return csrfToken\n    }\n\n}\n----\n======\n\n[NOTE]\n====\nYou may consider adding `.requestMatchers(\"/csrf\").permitAll()` if the endpoint above is required prior to authenticating with the server.\n====\n\nThis endpoint should be called to obtain a CSRF token when the application is launched or initialized (e.g. at load time), and also after authentication success and logout success.\n\n[NOTE]\n====\nRefreshing the token after authentication success and logout success is required because the javadoc:org.springframework.security.web.csrf.CsrfAuthenticationStrategy[] and javadoc:org.springframework.security.web.csrf.CsrfLogoutHandler[] will clear the previous token.\nThe client application will not be able to perform an unsafe HTTP request, such as a POST, without obtaining a fresh token.\n====\n\nOnce you've obtained the CSRF token, you will need to include it as an HTTP request header (one of <<csrf-token-repository-httpsession,`X-CSRF-TOKEN`>> or <<csrf-token-repository-cookie,`X-XSRF-TOKEN`>> by default) yourself.\n\n[[csrf-access-denied-handler]]\n== Handle `AccessDeniedException`\n\nTo handle an `AccessDeniedException` such as `InvalidCsrfTokenException`, you can configure Spring Security to handle these exceptions in any way you like.\nFor example, you can configure a custom access denied page using the following configuration:\n\n[[csrf-access-denied-handler-configuration]]\n.Configure `AccessDeniedHandler`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.exceptionHandling((exceptionHandling) -> exceptionHandling\n\t\t\t\t.accessDeniedPage(\"/access-denied\")\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            exceptionHandling {\n                accessDeniedPage = \"/access-denied\"\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<access-denied-handler error-page=\"/access-denied\"/>\n</http>\n----\n======\n\n[[csrf-testing]]\n== CSRF Testing\n\nYou can use Spring Security's xref:servlet/test/mockmvc/setup.adoc[testing support] and xref:servlet/test/mockmvc/csrf.adoc[`CsrfRequestPostProcessor`] to test CSRF protection, like this:\n\n[[csrf-testing-example]]\n.Test CSRF Protection\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SecurityConfig.class)\n@WebAppConfiguration\npublic class CsrfTests {\n\n\tprivate MockMvc mockMvc;\n\n\t@BeforeEach\n\tpublic void setUp(WebApplicationContext applicationContext) {\n\t\tthis.mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void loginWhenValidCsrfTokenThenSuccess() throws Exception {\n\t\tthis.mockMvc.perform(post(\"/login\").with(csrf())\n\t\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(header().string(HttpHeaders.LOCATION, \"/\"));\n\t}\n\n\t@Test\n\tpublic void loginWhenInvalidCsrfTokenThenForbidden() throws Exception {\n\t\tthis.mockMvc.perform(post(\"/login\").with(csrf().useInvalidToken())\n\t\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\"))\n\t\t\t.andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void loginWhenMissingCsrfTokenThenForbidden() throws Exception {\n\t\tthis.mockMvc.perform(post(\"/login\")\n\t\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\"))\n\t\t\t.andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void logoutWhenValidCsrfTokenThenSuccess() throws Exception {\n\t\tthis.mockMvc.perform(post(\"/logout\").with(csrf())\n\t\t\t\t.accept(MediaType.TEXT_HTML))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(header().string(HttpHeaders.LOCATION, \"/login?logout\"));\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*\nimport org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.*\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration(classes = [SecurityConfig::class])\n@WebAppConfiguration\nclass CsrfTests {\n\tprivate lateinit var mockMvc: MockMvc\n\n\t@BeforeEach\n\tfun setUp(applicationContext: WebApplicationContext) {\n\t\tmockMvc = MockMvcBuilders.webAppContextSetup(applicationContext)\n\t\t\t.apply<DefaultMockMvcBuilder>(springSecurity())\n\t\t\t.build()\n\t}\n\n\t@Test\n\tfun loginWhenValidCsrfTokenThenSuccess() {\n\t\tmockMvc.perform(post(\"/login\").with(csrf())\n\t\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\"))\n\t\t\t.andExpect(status().is3xxRedirection)\n\t\t\t.andExpect(header().string(HttpHeaders.LOCATION, \"/\"))\n\t}\n\n\t@Test\n\tfun loginWhenInvalidCsrfTokenThenForbidden() {\n\t\tmockMvc.perform(post(\"/login\").with(csrf().useInvalidToken())\n\t\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\"))\n\t\t\t.andExpect(status().isForbidden)\n\t}\n\n\t@Test\n\tfun loginWhenMissingCsrfTokenThenForbidden() {\n\t\tmockMvc.perform(post(\"/login\")\n\t\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t\t.param(\"username\", \"user\")\n\t\t\t\t.param(\"password\", \"password\"))\n\t\t\t.andExpect(status().isForbidden)\n\t}\n\n\t@Test\n\t@WithMockUser\n\t@Throws(Exception::class)\n\tfun logoutWhenValidCsrfTokenThenSuccess() {\n\t\tmockMvc.perform(post(\"/logout\").with(csrf())\n\t\t\t\t.accept(MediaType.TEXT_HTML))\n\t\t\t.andExpect(status().is3xxRedirection)\n\t\t\t.andExpect(header().string(HttpHeaders.LOCATION, \"/login?logout\"))\n\t}\n}\n----\n======\n\n[[disable-csrf]]\n== Disable CSRF Protection\n\nBy default, CSRF protection is enabled, which affects <<csrf-integration,integrating with the backend>> and <<csrf-testing,testing>> your application.\nBefore disabling CSRF protection, consider whether it xref:features/exploits/csrf.adoc#csrf-when[makes sense for your application].\n\nYou can also consider whether only certain endpoints do not require CSRF protection and configure an ignoring rule, as in the following example:\n\n[[disable-csrf-ignoring-configuration]]\n.Ignoring Requests\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n        http\n            // ...\n            .csrf((csrf) -> csrf\n                .ignoringRequestMatchers(\"/api/*\")\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                ignoringRequestMatchers(\"/api/*\")\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf request-matcher-ref=\"csrfMatcher\"/>\n</http>\n<b:bean id=\"csrfMatcher\"\n    class=\"org.springframework.security.web.util.matcher.AndRequestMatcher\">\n    <b:constructor-arg value=\"#{T(org.springframework.security.web.csrf.CsrfFilter).DEFAULT_CSRF_MATCHER}\"/>\n    <b:constructor-arg>\n        <b:bean class=\"org.springframework.security.web.util.matcher.NegatedRequestMatcher\">\n            <b:bean class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\">\n                <b:constructor-arg value=\"/api/*\"/>\n            </b:bean>\n        </b:bean>\n    </b:constructor-arg>\n</b:bean>\n----\n======\n\nIf you need to disable CSRF protection, you can do so using the following configuration:\n\n[[disable-csrf-configuration]]\n.Disable CSRF\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.csrf((csrf) -> csrf.disable());\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            csrf {\n                disable()\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\t<csrf disabled=\"true\"/>\n</http>\n----\n======\n\n[[csrf-considerations]]\n== CSRF Considerations\n\nThere are a few special considerations when implementing protection against CSRF attacks.\nThis section discusses those considerations as they pertain to servlet environments.\nSee xref:features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion.\n\n[[csrf-considerations-login]]\n=== Logging In\n\nIt is important to xref:features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for log in] requests to protect against forging log in attempts.\nSpring Security's servlet support does this out of the box.\n\n[[csrf-considerations-logout]]\n=== Logging Out\n\nIt is important to xref:features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging logout attempts.\nIf CSRF protection is enabled (the default), Spring Security's `LogoutFilter` will only process HTTP POST requests.\nThis ensures that logging out requires a CSRF token and that a malicious user cannot forcibly log your users out.\n\nThe easiest approach is to use a form to log the user out.\nIf you really want a link, you can use JavaScript to have the link perform a POST (maybe on a hidden form).\nFor browsers with JavaScript that is disabled, you can optionally have the link take the user to a log out confirmation page that performs the POST.\n\nIf you really want to use HTTP GET with logout, you can do so.\nHowever, remember that this is generally not recommended.\nFor example, the following logs out when the `/logout` URL is requested with any HTTP method:\n\n.Log Out with Any HTTP Method\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.logout((logout) -> logout\n\t\t\t\t.logoutRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher(\"/logout\"))\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            logout {\n                logoutRequestMatcher = PathPatternRequestMatcher.withDefaults().matcher(\"/logout\")\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nSee the xref:servlet/authentication/logout.adoc[Logout] chapter for more information.\n\n[[considerations-csrf-timeouts]]\n=== CSRF and Session Timeouts\n\nBy default, Spring Security stores the CSRF token in the `HttpSession` using the <<csrf-token-repository-httpsession,`HttpSessionCsrfTokenRepository`>>.\nThis can lead to a situation where the session expires, leaving no CSRF token to validate against.\n\nWe have already discussed xref:features/exploits/csrf.adoc#csrf-considerations-timeouts[general solutions] to session timeouts.\nThis section discusses the specifics of CSRF timeouts as it pertains to the servlet support.\n\nYou can change the storage of the CSRF token to be in a cookie.\nFor details, see the <<csrf-token-repository-cookie>> section.\n\nIf a token does expire, you might want to customize how it is handled by specifying a <<csrf-access-denied-handler,custom `AccessDeniedHandler`>>.\nThe custom `AccessDeniedHandler` can process the `InvalidCsrfTokenException` any way you like.\n\n[[csrf-considerations-multipart]]\n=== Multipart (file upload)\n\nWe have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem.\nWhen JavaScript is available, we _recommend_ <<csrf-integration-javascript-other,including the CSRF token in an HTTP request header>> to side-step the issue.\n\nIf JavaScript is not available, the following sections discuss options for placing the CSRF token in the <<csrf-considerations-multipart-body,body>> and <<csrf-considerations-multipart-url,url>> within a servlet application.\n\n[NOTE]\n====\nYou can find more information about using multipart forms with Spring in the {spring-framework-reference-url}web/webmvc/mvc-servlet/multipart.html[Multipart Resolver] section of the Spring reference and the {spring-framework-api-url}org/springframework/web/multipart/support/MultipartFilter.html[`MultipartFilter` javadoc].\n====\n\n[[csrf-considerations-multipart-body]]\n==== Place CSRF Token in the Body\n\nWe have xref:features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the tradeoffs of placing the CSRF token in the body.\nIn this section, we discuss how to configure Spring Security to read the CSRF from the body.\n\nTo read the CSRF token from the body, the `MultipartFilter` is specified before the Spring Security filter.\nSpecifying the `MultipartFilter` before the Spring Security filter means that there is no authorization for invoking the `MultipartFilter`, which means anyone can place temporary files on your server.\nHowever, only authorized users can submit a file that is processed by your application.\nIn general, this is the recommended approach because the temporary file upload should have a negligible impact on most servers.\n\n.Configure `MultipartFilter`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {\n\n\t@Override\n\tprotected void beforeSpringSecurityFilterChain(ServletContext servletContext) {\n\t\tinsertFilters(servletContext, new MultipartFilter());\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass SecurityApplicationInitializer : AbstractSecurityWebApplicationInitializer() {\n    override fun beforeSpringSecurityFilterChain(servletContext: ServletContext?) {\n        insertFilters(servletContext, MultipartFilter())\n    }\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<filter>\n\t<filter-name>MultipartFilter</filter-name>\n\t<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>\n</filter>\n<filter>\n\t<filter-name>springSecurityFilterChain</filter-name>\n\t<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\n</filter>\n<filter-mapping>\n\t<filter-name>MultipartFilter</filter-name>\n\t<url-pattern>/*</url-pattern>\n</filter-mapping>\n<filter-mapping>\n\t<filter-name>springSecurityFilterChain</filter-name>\n\t<url-pattern>/*</url-pattern>\n</filter-mapping>\n----\n======\n\n[NOTE]\n====\nTo ensure that `MultipartFilter` is specified before the Spring Security filter with XML configuration, you can ensure the `<filter-mapping>` element of the `MultipartFilter` is placed before the `springSecurityFilterChain` within the `web.xml` file.\n====\n\n[[csrf-considerations-multipart-url]]\n==== Include a CSRF Token in a URL\n\nIf letting unauthorized users upload temporary files is not acceptable, an alternative is to place the `MultipartFilter` after the Spring Security filter and include the CSRF as a query parameter in the action attribute of the form.\nSince the `CsrfToken` is exposed as an <<csrf-token-request-handler,`HttpServletRequest` attribute named `_csrf`>>, we can use that to create an `action` with the CSRF token in it.\nThe following example does this with a JSP:\n\n.CSRF Token in Action\n[source,html]\n----\n<form method=\"post\"\n\taction=\"./upload?${_csrf.parameterName}=${_csrf.token}\"\n\tenctype=\"multipart/form-data\">\n----\n\n[[csrf-considerations-override-method]]\n=== HiddenHttpMethodFilter\n\nWe have xref:features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the trade-offs of placing the CSRF token in the body.\n\nIn Spring's Servlet support, overriding the HTTP method is done by using {spring-framework-api-url}org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[`HiddenHttpMethodFilter`].\nYou can find more information in the {spring-framework-reference-url}web/webmvc-view/mvc-jsp.html#mvc-rest-method-conversion[HTTP Method Conversion] section of the reference documentation.\n\n[[csrf-further-reading]]\n== Further Reading\n\nNow that you have reviewed CSRF protection, consider learning more about xref:servlet/exploits/index.adoc[exploit protection] including xref:servlet/exploits/headers.adoc[secure headers] and the xref:servlet/exploits/firewall.adoc[HTTP firewall] or move on to learning how to xref:servlet/test/index.adoc[test] your application.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/exploits/firewall.adoc",
    "content": "[[servlet-httpfirewall]]\n= HttpFirewall\nSpring Security has several areas where patterns you have defined are tested against incoming requests to decide how the request should be handled.\nThis occurs when the `FilterChainProxy` decides which filter chain a request should be passed through and when the `FilterSecurityInterceptor` decides which security constraints apply to a request.\nIt is important to understand what the mechanism is and what URL value is used when testing against the patterns that you define.\n\nThe servlet specification defines several properties for the `HttpServletRequest` that are accessible via getter methods and that we might want to match against.\nThese are the `contextPath`, `servletPath`, `pathInfo`, and `queryString`.\nSpring Security is only interested in securing paths within the application, so the `contextPath` is ignored.\nUnfortunately, the servlet spec does not define exactly what the values of `servletPath` and `pathInfo` contain for a particular request URI.\nFor example, each path segment of a URL may contain parameters, as defined in https://www.ietf.org/rfc/rfc2396.txt[RFC 2396]\n(You have probably seen this when a browser does not support cookies and the `jsessionid` parameter is appended to the URL after a semicolon.\nHowever, the RFC allows the presence of these parameters in any path segment of the URL.)\nThe Specification does not clearly state whether these should be included in the `servletPath` and `pathInfo` values and the behavior varies between different servlet containers.\nThere is a danger that, when an application is deployed in a container that does not strip path parameters from these values, an attacker could add them to the requested URL to cause a pattern match to succeed or fail unexpectedly.\n(The original values will be returned once the request leaves the `FilterChainProxy`, so will still be available to the application.)\nOther variations in the incoming URL are also possible.\nFor example, it could contain path-traversal sequences (such as `/../`) or multiple forward slashes (`//`) that could also cause pattern-matches to fail.\nSome containers normalize these out before performing the servlet mapping, but others do not.\nTo protect against issues like these, `FilterChainProxy` uses an `HttpFirewall` strategy to check and wrap the request.\nBy default, un-normalized requests are automatically rejected, and path parameters and duplicate slashes are removed for matching purposes.\n(So, for example, an original request path of `/secure;hack=1/somefile.html;hack=2` is returned as `/secure/somefile.html`.)\nIt is, therefore, essential that a `FilterChainProxy` is used to manage the security filter chain.\nNote that the `servletPath` and `pathInfo` values are decoded by the container, so your application should not have any valid paths that contain semi-colons, as these parts are removed for matching purposes.\n\nAs mentioned earlier, the default strategy is to use Ant-style paths for matching, and this is likely to be the best choice for most users.\nThe strategy is implemented in the class `PathPatternRequestMatcher`, which uses Spring's `PathPattern` to perform a case-insensitive match of the pattern against the concatenated `servletPath` and `pathInfo`, ignoring the `queryString`.\n\nIf you need a more powerful matching strategy, you can use regular expressions.\nThe strategy implementation is then `RegexRequestMatcher`.\nSee the javadoc:org.springframework.security.web.util.matcher.RegexRequestMatcher[] Javadoc for more information.\n\nIn practice, we recommend that you use method security at your service layer, to control access to your application, rather than rely entirely on the use of security constraints defined at the web-application level.\nURLs change, and it is difficult to take into account all the possible URLs that an application might support and how requests might be manipulated.\nYou should restrict yourself to using a few simple Ant paths that are simple to understand.\nAlways try to use a \"`deny-by-default`\" approach, where you have a catch-all wildcard (`/**` or `**`) defined last to deny access.\n\nSecurity defined at the service layer is much more robust and harder to bypass, so you should always take advantage of Spring Security's method security options.\n\nThe `HttpFirewall` also prevents https://www.owasp.org/index.php/HTTP_Response_Splitting[HTTP Response Splitting] by rejecting new line characters in the HTTP Response headers.\n\nBy default, the `StrictHttpFirewall` implementation is used.\nThis implementation rejects requests that appear to be malicious.\nIf it is too strict for your needs, you can customize what types of requests are rejected.\nHowever, it is important that you do so knowing that this can open your application up to attacks.\nFor example, if you wish to use Spring MVC's matrix variables, you could use the following configuration:\n\n.Allow Matrix Variables\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic StrictHttpFirewall httpFirewall() {\n    StrictHttpFirewall firewall = new StrictHttpFirewall();\n    firewall.setAllowSemicolon(true);\n    return firewall;\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<b:bean id=\"httpFirewall\"\n    class=\"org.springframework.security.web.firewall.StrictHttpFirewall\"\n    p:allowSemicolon=\"true\"/>\n\n<http-firewall ref=\"httpFirewall\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun httpFirewall(): StrictHttpFirewall {\n    val firewall = StrictHttpFirewall()\n    firewall.setAllowSemicolon(true)\n    return firewall\n}\n----\n======\n\nTo protect against https://www.owasp.org/index.php/Cross_Site_Tracing[Cross Site Tracing (XST)] and https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)[HTTP Verb Tampering], the `StrictHttpFirewall` provides an allowed list of valid HTTP methods that are allowed.\nThe default valid methods are `DELETE`, `GET`, `HEAD`, `OPTIONS`, `PATCH`, `POST`, and `PUT`.\nIf your application needs to modify the valid methods, you can configure a custom `StrictHttpFirewall` bean.\nThe following example allows only HTTP `GET` and `POST` methods:\n\n\n.Allow Only GET & POST\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic StrictHttpFirewall httpFirewall() {\n    StrictHttpFirewall firewall = new StrictHttpFirewall();\n    firewall.setAllowedHttpMethods(Arrays.asList(\"GET\", \"POST\"));\n    return firewall;\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<b:bean id=\"httpFirewall\"\n      class=\"org.springframework.security.web.firewall.StrictHttpFirewall\"\n      p:allowedHttpMethods=\"GET,POST\"/>\n\n<http-firewall ref=\"httpFirewall\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun httpFirewall(): StrictHttpFirewall {\n    val firewall = StrictHttpFirewall()\n    firewall.setAllowedHttpMethods(listOf(\"GET\", \"POST\"))\n    return firewall\n}\n----\n======\n\n[TIP]\n====\nIf you use `new MockHttpServletRequest()`, it currently creates an HTTP method as an empty String (`\"\"`).\nThis is an invalid HTTP method and is rejected by Spring Security.\nYou can resolve this by replacing it with `new MockHttpServletRequest(\"GET\", \"\")`.\nSee https://jira.spring.io/browse/SPR-16851[SPR_16851] for an issue that requests improving this.\n====\n\nIf you must allow any HTTP method (not recommended), you can use `StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)`.\nDoing so entirely disables validation of the HTTP method.\n\n\n[[servlet-httpfirewall-headers-parameters]]\n`StrictHttpFirewall` also checks header names and values and parameter names.\nIt requires that each character have a defined code point and not be a control character.\n\nThis requirement can be relaxed or adjusted as necessary by using the following methods:\n\n* `StrictHttpFirewall#setAllowedHeaderNames(Predicate)`\n* `StrictHttpFirewall#setAllowedHeaderValues(Predicate)`\n* `StrictHttpFirewall#setAllowedParameterNames(Predicate)`\n\n[NOTE]\n====\nParameter values can be also controlled with `setAllowedParameterValues(Predicate)`.\n====\n\nFor example, to switch off this check, you can wire your `StrictHttpFirewall` with `Predicate` instances that always return `true`:\n\n.Allow Any Header Name, Header Value, and Parameter Name\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic StrictHttpFirewall httpFirewall() {\n    StrictHttpFirewall firewall = new StrictHttpFirewall();\n    firewall.setAllowedHeaderNames((header) -> true);\n    firewall.setAllowedHeaderValues((header) -> true);\n    firewall.setAllowedParameterNames((parameter) -> true);\n    return firewall;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun httpFirewall(): StrictHttpFirewall {\n    val firewall = StrictHttpFirewall()\n    firewall.setAllowedHeaderNames { true }\n    firewall.setAllowedHeaderValues { true }\n    firewall.setAllowedParameterNames { true }\n    return firewall\n}\n----\n======\n\nAlternatively, there might be a specific value that you need to allow.\n\nFor example, iPhone Xʀ uses a `User-Agent` that includes a character that is not in the ISO-8859-1 charset.\nDue to this fact, some application servers parse this value into two separate characters, the latter being an undefined character.\n\nYou can address this with the `setAllowedHeaderValues` method:\n\n.Allow Certain User Agents\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic StrictHttpFirewall httpFirewall() {\n    StrictHttpFirewall firewall = new StrictHttpFirewall();\n    Pattern allowed = Pattern.compile(\"[\\\\p{IsAssigned}&&[^\\\\p{IsControl}]]*\");\n    Pattern userAgent = ...;\n    firewall.setAllowedHeaderValues((header) -> allowed.matcher(header).matches() || userAgent.matcher(header).matches());\n    return firewall;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun httpFirewall(): StrictHttpFirewall {\n    val firewall = StrictHttpFirewall()\n    val allowed = Pattern.compile(\"[\\\\p{IsAssigned}&&[^\\\\p{IsControl}]]*\")\n    val userAgent = Pattern.compile(...)\n    firewall.setAllowedHeaderValues { allowed.matcher(it).matches() || userAgent.matcher(it).matches() }\n    return firewall\n}\n----\n======\n\nIn the case of header values, you may instead consider parsing them as UTF-8 at verification time:\n\n.Parse Headers As UTF-8\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nfirewall.setAllowedHeaderValues((header) -> {\n    String parsed = new String(header.getBytes(ISO_8859_1), UTF_8);\n    return allowed.matcher(parsed).matches();\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nfirewall.setAllowedHeaderValues {\n    val parsed = String(header.getBytes(ISO_8859_1), UTF_8)\n    return allowed.matcher(parsed).matches()\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/exploits/headers.adoc",
    "content": "[[servlet-headers]]\n= Security HTTP Response Headers\n\nYou can use xref:features/exploits/headers.adoc#headers[Security HTTP Response Headers] to increase the security of web applications.\nThis section is dedicated to servlet-based support for Security HTTP Response Headers.\n\n[[servlet-headers-default]]\n== Default Security Headers\n\nSpring Security provides a xref:features/exploits/headers.adoc#headers-default[default set of Security HTTP Response Headers] to provide secure defaults.\nWhile each of these headers are considered best practice, it should be noted that not all clients use the headers, so additional testing is encouraged.\n\nYou can customize specific headers.\nFor example, assume that you want the defaults but you wish to specify `SAMEORIGIN` for <<servlet-headers-frame-options,X-Frame-Options>>.\n\nYou can do so with the following configuration:\n\n.Customize Default Security Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.frameOptions((frameOptions) -> frameOptions\n\t\t\t\t\t.sameOrigin()\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<frame-options policy=\"SAMEORIGIN\" />\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                frameOptions {\n                    sameOrigin = true\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nIf you do not want the defaults to be added and want explicit control over what should be used, you can disable the defaults.\nThe next code listing shows how to do so.\n\nIf you use Spring Security's configuration, the following adds only xref:features/exploits/headers.adoc#headers-cache-control[Cache Control]:\n\n.Customize Cache Control Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t// do not use any default headers unless explicitly listed\n\t\t\t\t.defaultsDisabled()\n\t\t\t\t.cacheControl(withDefaults())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers defaults-disabled=\"true\">\n\t\t<cache-control/>\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                // do not use any default headers unless explicitly listed\n                defaultsDisabled = true\n                cacheControl {\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nIf necessary, you can disable all of the HTTP Security response headers with the following configuration:\n\n.Disable All HTTP Security Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers.disable());\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers disabled=\"true\" />\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                disable()\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-cache-control]]\n== Cache Control\n\nSpring Security includes xref:features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default.\n\nHowever, if you actually want to cache specific responses, your application can selectively invoke https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#setHeader(java.lang.String,java.lang.String)[`HttpServletResponse.setHeader(String,String)`] to override the header set by Spring Security.\nYou can use this to ensure that content (such as CSS, JavaScript, and images) is properly cached.\n\nWhen you use Spring Web MVC, this is typically done within your configuration.\nYou can find details on how to do this in the {spring-framework-reference-url}web/webmvc/mvc-config/static-resources.html[Static Resources] portion of the Spring Reference documentation\n\nIf necessary, you can also disable Spring Security's cache control HTTP response headers.\n\n.Cache Control Disabled\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.cacheControl((cache) -> cache.disable())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<cache-control disabled=\"true\"/>\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n       http {\n            headers {\n                cacheControl {\n                    disable()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-content-type-options]]\n== Content Type Options\n\nSpring Security includes xref:features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers by default.\nHowever, you can disable it:\n\n.Content Type Options Disabled\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.contentTypeOptions((contentTypeOptions) -> contentTypeOptions.disable())\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<content-type-options disabled=\"true\"/>\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n       http {\n            headers {\n                contentTypeOptions {\n                    disable()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-hsts]]\n== HTTP Strict Transport Security (HSTS)\n\nBy default, Spring Security provides the xref:features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header.\nHowever, you can explicitly customize the results.\nThe following example explicitly provides HSTS:\n\n.Strict Transport Security\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.httpStrictTransportSecurity((hsts) -> hsts\n\t\t\t\t\t.includeSubDomains(true)\n\t\t\t\t\t.preload(true)\n\t\t\t\t\t.maxAgeInSeconds(31536000)\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<hsts\n\t\t\tinclude-subdomains=\"true\"\n\t\t\tmax-age-seconds=\"31536000\"\n\t\t\tpreload=\"true\" />\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            headers {\n                httpStrictTransportSecurity {\n                    includeSubDomains = true\n                    preload = true\n                    maxAgeInSeconds = 31536000\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-hpkp]]\n== HTTP Public Key Pinning (HPKP)\nSpring Security provides servlet support for xref:features/exploits/headers.adoc#headers-hpkp[HTTP Public Key Pinning], but it is xref:features/exploits/headers.adoc#headers-hpkp-deprecated[no longer recommended].\n\nYou can enable HPKP headers with the following configuration:\n\n.HTTP Public Key Pinning\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.httpPublicKeyPinning((hpkp) -> hpkp\n\t\t\t\t\t.includeSubDomains(true)\n\t\t\t\t\t.reportUri(\"https://example.net/pkp-report\")\n\t\t\t\t\t.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\")\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<hpkp\n\t\t\tinclude-subdomains=\"true\"\n\t\t\treport-uri=\"https://example.net/pkp-report\">\n\t\t\t<pins>\n\t\t\t\t<pin algorithm=\"sha256\">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>\n\t\t\t\t<pin algorithm=\"sha256\">E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=</pin>\n\t\t\t</pins>\n\t\t</hpkp>\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            headers {\n                httpPublicKeyPinning {\n                    includeSubDomains = true\n                    reportUri = \"https://example.net/pkp-report\"\n                    pins = mapOf(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\" to \"sha256\",\n                            \"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\" to \"sha256\")\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-frame-options]]\n== X-Frame-Options\n\nBy default, Spring Security instructs browsers to block reflected XSS attacks by using the xref:features/exploits/headers.adoc#headers-frame-options[X-Frame-Options].\n\nFor example, the following configuration specifies that Spring Security should no longer instruct browsers to block the content:\n\n.X-Frame-Options: SAMEORIGIN\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.frameOptions((frameOptions) -> frameOptions\n\t\t\t\t\t.sameOrigin()\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<frame-options\n\t\tpolicy=\"SAMEORIGIN\" />\n\t</headers>\n</http>\n----\n\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            headers {\n                frameOptions {\n                    sameOrigin = true\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-xss-protection]]\n== X-XSS-Protection\n\nBy default, Spring Security instructs browsers to disable the XSS Auditor by using <<headers-xss-protection,X-XSS-Protection header>.\nHowever, you can change this default.\nFor example, the following configuration specifies that Spring Security instruct compatible browsers to enable filtering,\nand block the content:\n\n.X-XSS-Protection Customization\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.xssProtection((xss) -> xss\n\t\t\t\t\t.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK)\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<xss-protection headerValue=\"1; mode=block\"/>\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        // ...\n        http {\n            headers {\n                xssProtection {\n                    headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-csp]]\n== Content Security Policy (CSP)\n\nSpring Security does not add xref:features/exploits/headers.adoc#headers-csp[Content Security Policy] by default, because a reasonable default is impossible to know without knowing the context of the application.\nThe web application author must declare the security policy (or policies) to enforce or monitor for the protected resources.\n\nConsider the following security policy:\n\n.Content Security Policy Example\n[source,http]\n----\nContent-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\n----\n\nGiven the preceding security policy, you can enable the CSP header:\n\n.Content Security Policy\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t\t.policyDirectives(\"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\")\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<content-security-policy\n\t\t\tpolicy-directives=\"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\" />\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                contentSecurityPolicy {\n                    policyDirectives = \"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\"\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nTo enable the CSP `report-only` header, provide the following configuration:\n\n.Content Security Policy Report Only\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t\t.policyDirectives(\"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\")\n\t\t\t\t\t.reportOnly()\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<content-security-policy\n\t\t\tpolicy-directives=\"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\"\n\t\t\treport-only=\"true\" />\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                contentSecurityPolicy {\n                    policyDirectives = \"script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/\"\n                    reportOnly = true\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-referrer]]\n== Referrer Policy\n\nSpring Security does not add xref:features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default.\nYou can enable the Referrer Policy header by using the configuration:\n\n.Referrer Policy\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.referrerPolicy((referrer) -> referrer\n\t\t\t\t\t.policy(ReferrerPolicy.SAME_ORIGIN)\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<referrer-policy policy=\"same-origin\" />\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                referrerPolicy {\n                    policy = ReferrerPolicy.SAME_ORIGIN\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-feature]]\n== Feature Policy\n\nSpring Security does not add xref:features/exploits/headers.adoc#headers-feature[Feature Policy] headers by default.\nConsider the following `Feature-Policy` header:\n\n.Feature-Policy Example\n[source]\n----\nFeature-Policy: geolocation 'self'\n----\n\nYou can enable the preceding feature policy header by using the following configuration:\n\n.Feature-Policy\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.featurePolicy(\"geolocation 'self'\")\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<feature-policy policy-directives=\"geolocation 'self'\" />\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                featurePolicy(\"geolocation 'self'\")\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-permissions]]\n== Permissions Policy\n\nSpring Security does not add xref:features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers by default.\nConsider the following `Permissions-Policy` header:\n\n.Permissions-Policy Example\n[source]\n----\nPermissions-Policy: geolocation=(self)\n----\n\nYou can enable the preceding permissions policy header using the following configuration:\n\n.Permissions-Policy\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.permissionsPolicy((permissions) -> permissions\n\t\t\t\t\t.policy(\"geolocation=(self)\")\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<permissions-policy policy=\"geolocation=(self)\" />\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                permissionPolicy {\n                    policy = \"geolocation=(self)\"\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-clear-site-data]]\n== Clear Site Data\n\nSpring Security does not add xref:features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers by default.\nConsider the following Clear-Site-Data header:\n\n.Clear-Site-Data Example\n----\nClear-Site-Data: \"cache\", \"cookies\"\n----\n\nYou can send the preceding header on log out with the following configuration:\n\n.Clear-Site-Data\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.logout((logout) -> logout\n                .addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(CACHE, COOKIES)))\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            logout {\n                addLogoutHandler(HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(CACHE, COOKIES)))\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-custom]]\n== Custom Headers\nSpring Security has mechanisms to make it convenient to add the more common security headers to your application.\nHowever, it also provides hooks to enable adding custom headers.\n\n[[servlet-headers-static]]\n=== Static Headers\nThere may be times when you wish to inject custom security headers that are not supported out of the box into your application.\nConsider the following custom security header:\n\n[source]\n----\nX-Custom-Security-Header: header-value\n----\n\nGiven the preceding header, you could add the headers to the response by using the following configuration:\n\n.StaticHeadersWriter\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.addHeaderWriter(new StaticHeadersWriter(\"X-Custom-Security-Header\",\"header-value\"))\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<header name=\"X-Custom-Security-Header\" value=\"header-value\"/>\n\t</headers>\n</http>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                addHeaderWriter(StaticHeadersWriter(\"X-Custom-Security-Header\",\"header-value\"))\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-headers-writer]]\n=== Headers Writer\nWhen the namespace or Java configuration does not support the headers you want, you can create a custom `HeadersWriter` instance or even provide a custom implementation of the `HeadersWriter`.\n\nThe next example use a custom instance of `XFrameOptionsHeaderWriter`.\nIf you wanted to explicitly configure <<servlet-headers-frame-options>>, you could do so with the following configuration:\n\n.Headers Writer\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\",subs=\"+attributes\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<header ref=\"frameOptionsWriter\"/>\n\t</headers>\n</http>\n<!-- Requires the c-namespace.\nSee {spring-framework-reference-url}core/beans/dependencies/factory-properties-detailed.html#beans-c-namespace\n-->\n<beans:bean id=\"frameOptionsWriter\"\n\tclass=\"org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\"\n\tc:frameOptionsMode=\"SAMEORIGIN\"/>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                addHeaderWriter(XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[headers-delegatingrequestmatcherheaderwriter]]\n=== DelegatingRequestMatcherHeaderWriter\n\nAt times, you may want to write a header only for certain requests.\nFor example, perhaps you want to protect only your login page from being framed.\nYou could use the `DelegatingRequestMatcherHeaderWriter` to do so.\n\nThe following configuration example uses `DelegatingRequestMatcherHeaderWriter`:\n\n.DelegatingRequestMatcherHeaderWriter Java Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\tRequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher(\"/login\");\n\t\tDelegatingRequestMatcherHeaderWriter headerWriter =\n\t\t\tnew DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter());\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.frameOptions((frameOptions) -> frameOptions.disable())\n\t\t\t\t.addHeaderWriter(headerWriter)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<!-- ... -->\n\n\t<headers>\n\t\t<frame-options disabled=\"true\"/>\n\t\t<header ref=\"headerWriter\"/>\n\t</headers>\n</http>\n\n<beans:bean id=\"headerWriter\"\n\tclass=\"org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter\">\n\t<beans:constructor-arg>\n\t\t<bean class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\"\n\t\t\tc:pattern=\"/login\"/>\n\t</beans:constructor-arg>\n\t<beans:constructor-arg>\n\t\t<beans:bean\n\t\t\tclass=\"org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter\"/>\n\t</beans:constructor-arg>\n</beans:bean>\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        val matcher: RequestMatcher = PathPatternRequestMatcher.withDefaults().matcher(\"/login\")\n        val headerWriter = DelegatingRequestMatcherHeaderWriter(matcher, XFrameOptionsHeaderWriter())\n       http {\n            headers {\n                frameOptions {\n                    disable()\n                }\n                addHeaderWriter(headerWriter)\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/exploits/http.adoc",
    "content": "[[servlet-http]]\n= HTTP\n\nAll HTTP-based communication should be protected xref:features/exploits/http.adoc#http[using TLS].\n\nThis section discusses the details of servlet-specific features that assist with HTTPS usage.\n\n[[servlet-http-redirect]]\n== Redirect to HTTPS\n\nIf a client makes a request using HTTP rather than HTTPS, you can configure Spring Security to redirect to HTTPS.\n\nFor example, the following Java or Kotlin configuration redirects any HTTP requests to HTTPS:\n\n.Redirect to HTTPS\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.redirectToHttps(withDefaults());\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            redirectToHttps { }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nThe following XML configuration redirects all HTTP requests to HTTPS\n\n.Redirect to HTTPS with XML Configuration\n[source,xml]\n----\n<http>\n\t<intercept-url pattern=\"/**\" access=\"ROLE_USER\" requires-channel=\"https\"/>\n...\n</http>\n----\n\n\n[[servlet-hsts]]\n== Strict Transport Security\n\nSpring Security provides support for xref:servlet/exploits/headers.adoc#servlet-headers-hsts[Strict Transport Security] and enables it by default.\n\n[[servlet-http-proxy-server]]\n== Proxy Server Configuration\n\nSpring Security xref:features/exploits/http.adoc#http-proxy-server[integrates with proxy servers].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/exploits/index.adoc",
    "content": "[[servlet-exploits]]\n= Protection Against Exploits\n:page-section-summary-toc: 1\n\nThis section discusses Servlet specific support for xref:features/exploits/index.adoc#exploits[Spring Security's protection against common exploits].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/getting-started.adoc",
    "content": "[[servlet-hello]]\n= Hello Spring Security\n\nThis section covers the minimum setup for how to use Spring Security with {spring-boot-reference-url}[Spring Boot] and then points you to next steps after that.\n\n[NOTE]\n====\nThe completed starter application can be found {gh-samples-url}/servlet/spring-boot/java/hello-security[in our samples repository].\nFor your convenience, you can download a minimal Spring Boot + Spring Security application https://start.spring.io/starter.zip?type=maven-project&language=java&packaging=jar&jvmVersion=1.8&groupId=example&artifactId=hello-security&name=hello-security&description=Hello%20Security&packageName=example.hello-security&dependencies=web,security[prepared by Spring Initializr].\n====\n\n[[servlet-hello-dependencies]]\n== Updating Dependencies\n\nYou first need to add Spring Security to your application's classpath; two ways to do this are to xref:getting-spring-security.adoc#getting-maven-boot[use Maven] or xref:getting-spring-security.adoc#getting-gradle-boot[Gradle].\n\n[[servlet-hello-starting]]\n== Starting Hello Spring Security Boot\n\nWith Spring Security <<servlet-hello-dependencies,on the classpath>>, you can now {spring-boot-reference-url}reference/using/running-your-application.html[run the Spring Boot application].\nThe following snippet shows some of the output that indicates that Spring Security is enabled in your application:\n\n.Running Spring Boot Application\n[tabs]\n======\nMaven::\n+\n[source,bash,role=\"primary\"]\n----\n$ ./mvnw spring-boot:run\n...\nINFO 23689 --- [  restartedMain] .s.s.UserDetailsServiceAutoConfiguration :\n\nUsing generated security password: 8e557245-73e2-4286-969a-ff57fe326336\n\n...\n----\n\nGradle::\n+\n[source,bash,role=\"secondary\"]\n----\n$ ./gradlew :bootRun\n...\nINFO 23689 --- [  restartedMain] .s.s.UserDetailsServiceAutoConfiguration :\n\nUsing generated security password: 8e557245-73e2-4286-969a-ff57fe326336\n\n...\n----\n\nJar::\n+\n[source,bash,role=\"secondary\"]\n----\n$ java -jar target/myapplication-0.0.1.jar\n...\nINFO 23689 --- [  restartedMain] .s.s.UserDetailsServiceAutoConfiguration :\n\nUsing generated security password: 8e557245-73e2-4286-969a-ff57fe326336\n\n...\n----\n======\n\nNow that you have it running, you might try hitting an endpoint to see what happens.\nIf you hit an endpoint without credentials like so:\n\n.Querying a Secured Boot Application\n[source,bash]\n----\n$ curl -i http://localhost:8080/some/path\nHTTP/1.1 401\n...\n----\n\nthen Spring Security denies access with a `401 Unauthorized`.\n\n[TIP]\nIf you provide the same URL in a browser, it will redirect to a default login page.\n\nAnd if you hit an endpoint with credentials (found in the console output) as follows:\n\n.Querying with Credentials\n[source,bash]\n----\n$ curl -i -u user:8e557245-73e2-4286-969a-ff57fe326336 http://localhost:8080/some/path\nHTTP/1.1 404\n...\n----\n\nthen Spring Boot will service the request, returning a `404 Not Found` in this case since `/some/path` doesn't exist.\n\nFrom here, you can:\n\n* Better understand <<servlet-hello-auto-configuration,what Spring Boot enables in Spring Security by default>>\n* Read about <<security-use-cases,common use cases>> that Spring Security helps with\n* Start configuring xref:servlet/authentication/index.adoc[authentication]\n\n[[servlet-hello-auto-configuration]]\n== Runtime Expectations\n\nThe default arrangement of Spring Boot and Spring Security affords the following behaviors at runtime:\n\n* Requires an authenticated user xref:servlet/authorization/authorize-http-requests.adoc[for any endpoint] (including Boot's `/error` endpoint)\n* xref:servlet/authentication/passwords/user-details-service.adoc[Registers a default user] with a generated password at startup (the password is logged to the console; in the preceding example, the password is `8e557245-73e2-4286-969a-ff57fe326336`)\n* Protects xref:servlet/authentication/passwords/password-encoder.adoc[password storage with BCrypt] as well as others\n* Provides form-based xref:servlet/authentication/passwords/form.adoc[login] and xref:servlet/authentication/logout.adoc[logout] flows\n* Authenticates xref:servlet/authentication/passwords/form.adoc[form-based login] as well as xref:servlet/authentication/passwords/basic.adoc[HTTP Basic]\n* Provides content negotiation; for web requests, redirects to the login page; for service requests, returns a `401 Unauthorized`\n* xref:servlet/exploits/csrf.adoc[Mitigates CSRF] attacks\n* xref:servlet/authentication/session-management.adoc#ns-session-fixation[Mitigates Session Fixation] attacks\n* Writes xref:servlet/exploits/headers.adoc#servlet-headers-hsts[Strict-Transport-Security] to https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[ensure HTTPS]\n* Writes xref:servlet/exploits/headers.adoc#servlet-headers-content-type-options[X-Content-Type-Options] to mitigate https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options[sniffing attacks]\n* Writes xref:servlet/exploits/headers.adoc#servlet-headers-cache-control[Cache Control headers] that protect authenticated resources\n* Writes xref:servlet/exploits/headers.adoc#servlet-headers-frame-options[X-Frame-Options] to mitigate https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-frame-options[Clickjacking]\n* Integrates with xref:servlet/integrations/servlet-api.adoc[``HttpServletRequest``'s authentication methods]\n* Publishes xref:servlet/authentication/events.adoc[authentication success and failure events]\n\nIt can be helpful to understand how Spring Boot is coordinating with Spring Security to achieve this.\nTaking a look at {spring-boot-api-url}org/springframework/boot/security/autoconfigure/SecurityAutoConfiguration.html[Boot's security auto configuration], it does the following (simplified for illustration):\n\n.Spring Boot Security Auto Configuration\n[source,java]\n----\n@EnableWebSecurity <1>\n@Configuration\npublic class DefaultSecurityConfig {\n    @Bean\n    @ConditionalOnMissingBean(UserDetailsService.class)\n    InMemoryUserDetailsManager inMemoryUserDetailsManager() { <2>\n        String generatedPassword = // ...;\n        return new InMemoryUserDetailsManager(User.withUsername(\"user\")\n                .password(generatedPassword).roles(\"USER\").build());\n    }\n\n    @Bean\n    @ConditionalOnMissingBean(AuthenticationEventPublisher.class)\n    DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher(ApplicationEventPublisher delegate) { <3>\n        return new DefaultAuthenticationEventPublisher(delegate);\n    }\n}\n----\n1. Adds the `@EnableWebSecurity` annotation. (Among other things, this publishes xref:servlet/architecture.adoc#servlet-securityfilterchain[Spring Security's default `Filter` chain] as a `@Bean`)\n2. Publishes a xref:servlet/authentication/passwords/user-details-service.adoc[`UserDetailsService`] `@Bean` with a username of `user` and a randomly generated password that is logged to the console\n3. Publishes an xref:servlet/authentication/events.adoc[`AuthenticationEventPublisher`] `@Bean` for publishing authentication events\n\n[NOTE]\nSpring Boot adds any `Filter` published as a `@Bean` to the application's filter chain.\nThis means that using `@EnableWebSecurity` in conjunction with Spring Boot automatically registers Spring Security's filter chain for every request.\n\n[[security-use-cases]]\n== Security Use Cases\n\nThere are a number of places that you may want to go from here.\nTo figure out what's next for you and your application, consider these common use cases that Spring Security is built to address:\n\n* I am building a REST API, and I need to xref:servlet/oauth2/resource-server/jwt.adoc[authenticate a JWT] or xref:servlet/oauth2/resource-server/opaque-token.adoc[other bearer token]\n* I am building a Web Application, API Gateway, or BFF and\n** I need to xref:servlet/oauth2/login/core.adoc[login using OAuth 2.0 or OIDC]\n** I need to xref:servlet/saml2/login/index.adoc[login using SAML 2.0]\n** I need to xref:servlet/authentication/cas.adoc[login using CAS]\n* I need to manage\n** Users in xref:servlet/authentication/passwords/ldap.adoc[LDAP] or xref:servlet/authentication/passwords/ldap.adoc#_active_directory[Active Directory], with xref:servlet/integrations/data.adoc[Spring Data], or with xref:servlet/authentication/passwords/jdbc.adoc[JDBC]\n** xref:servlet/authentication/passwords/storage.adoc[Passwords]\n\nIn case none of those match what you are looking for, consider thinking about your application in the following order:\n\n1. *Protocol*: First, consider the protocol your application will use to communicate.\nFor servlet-based applications, Spring Security supports HTTP as well as xref:servlet/integrations/websocket.adoc[Websockets].\n2. *Authentication*: Next, consider how users will xref:servlet/authentication/index.adoc[authenticate] and if that authentication will be stateful or stateless\n3. *Authorization*: Then, consider how you will determine xref:servlet/authorization/index.adoc[what a user is authorized to do]\n4. *Defense*: Finally, xref:servlet/exploits/csrf.adoc#csrf-considerations[integrate with Spring Security's default protections] and consider xref:servlet/exploits/headers.adoc[which additional protections you need]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/index.adoc",
    "content": "[[servlet-applications]]\n= Servlet Applications\n:page-section-summary-toc: 1\n\nSpring Security integrates with the Servlet Container by using a standard Servlet `Filter`. This means it works with any application that runs in a Servlet Container. More concretely, you do not need to use Spring in your Servlet-based application to take advantage of Spring Security.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc",
    "content": "[[concurrency]]\n= Concurrency Support\n\nIn most environments, Security is stored on a per-`Thread` basis.\nThis means that when work is done on a new `Thread`, the `SecurityContext` is lost.\nSpring Security provides some infrastructure to help make this much easier to manage.\nSpring Security provides low-level abstractions for working with Spring Security in multi-threaded environments.\nIn fact, this is what Spring Security builds on to integrate with xref:servlet/integrations/servlet-api.adoc#servletapi-start-runnable[`AsyncContext.start(Runnable)`] and xref:servlet/integrations/mvc.adoc#mvc-async[Spring MVC Async Integration].\n\n== DelegatingSecurityContextRunnable\n\nOne of the most fundamental building blocks within Spring Security's concurrency support is the `DelegatingSecurityContextRunnable`.\nIt wraps a delegate `Runnable` to initialize the `SecurityContextHolder` with a specified `SecurityContext` for the delegate.\nIt then invokes the delegate `Runnable`, ensuring to clear the `SecurityContextHolder` afterwards.\nThe `DelegatingSecurityContextRunnable` looks something like this:\n\n[source,java]\n----\npublic void run() {\ntry {\n\tSecurityContextHolder.setContext(securityContext);\n\tdelegate.run();\n} finally {\n\tSecurityContextHolder.clearContext();\n}\n}\n----\n\nWhile very simple, it makes it seamless to transfer the `SecurityContext` from one `Thread` to another.\nThis is important since, in most cases, the `SecurityContextHolder` acts on a per-`Thread` basis.\nFor example, you might have used Spring Security's xref:servlet/appendix/namespace/method-security.adoc#nsa-global-method-security[`<global-method-security>`] support to secure one of your services.\nYou can now transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service.\nThe following example show how you might do so:\n\n[source,java]\n----\nRunnable originalRunnable = new Runnable() {\npublic void run() {\n\t// invoke secured service\n}\n};\n\nSecurityContext context = SecurityContextHolder.getContext();\nDelegatingSecurityContextRunnable wrappedRunnable =\n\tnew DelegatingSecurityContextRunnable(originalRunnable, context);\n\nnew Thread(wrappedRunnable).start();\n----\n\nThe preceding code:\n\n* Creates a `Runnable` that invokes our secured service.\nNote that it is not aware of Spring Security.\n* Obtains the `SecurityContext` that we wish to use from the `SecurityContextHolder` and initializes the `DelegatingSecurityContextRunnable`.\n* Uses the `DelegatingSecurityContextRunnable` to create a `Thread`.\n* Starts the `Thread` we created.\n\nSince it is common to create a `DelegatingSecurityContextRunnable` with the `SecurityContext` from the `SecurityContextHolder`, there is a shortcut constructor for it.\nThe following code has the same effect as the preceding code:\n\n\n[source,java]\n----\nRunnable originalRunnable = new Runnable() {\npublic void run() {\n\t// invoke secured service\n}\n};\n\nDelegatingSecurityContextRunnable wrappedRunnable =\n\tnew DelegatingSecurityContextRunnable(originalRunnable);\n\nnew Thread(wrappedRunnable).start();\n----\n\nThe code we have is simple to use, but it still requires knowledge that we are using Spring Security.\nIn the next section we will take a look at how we can utilize `DelegatingSecurityContextExecutor` to hide the fact that we are using Spring Security.\n\n== DelegatingSecurityContextExecutor\n\nIn the previous section, we found that it was easy to use the `DelegatingSecurityContextRunnable`, but it was not ideal since we had to be aware of Spring Security to use it.\nNow we look at how `DelegatingSecurityContextExecutor` can shield our code from any knowledge that we are using Spring Security.\n\nThe design of `DelegatingSecurityContextExecutor` is similar to that of `DelegatingSecurityContextRunnable`, except that it accepts a delegate `Executor` instead of a delegate `Runnable`.\nThe following example shows how to use it:\n\n[source,java]\n----\nSecurityContext context = SecurityContextHolder.createEmptyContext();\nAuthentication authentication =\n\tUsernamePasswordAuthenticationToken.authenticated(\"user\",\"doesnotmatter\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\ncontext.setAuthentication(authentication);\n\nSimpleAsyncTaskExecutor delegateExecutor =\n\tnew SimpleAsyncTaskExecutor();\nDelegatingSecurityContextExecutor executor =\n\tnew DelegatingSecurityContextExecutor(delegateExecutor, context);\n\nRunnable originalRunnable = new Runnable() {\npublic void run() {\n\t// invoke secured service\n}\n};\n\nexecutor.execute(originalRunnable);\n----\n\nThis code:\n\nNote that, in this example, we create the `SecurityContext` by hand.\nHowever, it does not matter where or how we get the `SecurityContext` (for example, we could obtain it from the `SecurityContextHolder`).\n* Creates a `delegateExecutor` that is in charge of executing submitted `Runnable` objects.\n* Finally, we create a `DelegatingSecurityContextExecutor`, which is in charge of wrapping any `Runnable` that is passed into the `execute` method with a `DelegatingSecurityContextRunnable`.\nIt then passes the wrapped `Runnable` to the `delegateExecutor`.\nIn this case, the same `SecurityContext` is used for every `Runnable` submitted to our `DelegatingSecurityContextExecutor`.\nThis is nice if we run background tasks that need to be run by a user with elevated privileges.\n* At this point, you may ask yourself, \"`How does this shield my code of any knowledge of Spring Security?`\" Instead of creating the `SecurityContext` and the `DelegatingSecurityContextExecutor` in our own code, we can inject an already initialized instance of `DelegatingSecurityContextExecutor`.\n\nConsider the following example:\n\n[source,java]\n----\n@Autowired\nprivate Executor executor; // becomes an instance of our DelegatingSecurityContextExecutor\n\npublic void submitRunnable() {\nRunnable originalRunnable = new Runnable() {\n\tpublic void run() {\n\t// invoke secured service\n\t}\n};\nexecutor.execute(originalRunnable);\n}\n----\n\nNow our code is unaware that the `SecurityContext` is being propagated to the `Thread`, the `originalRunnable` is run, and the `SecurityContextHolder` is cleared out.\nIn this example, the same user is being used to run each thread.\nWhat if we wanted to use the user from `SecurityContextHolder` (that is, the currently logged in-user) at the time we invoked `executor.execute(Runnable)` to process `originalRunnable`?\nYou can do so by removing the `SecurityContext` argument from our `DelegatingSecurityContextExecutor` constructor:\n\n[source,java]\n----\nSimpleAsyncTaskExecutor delegateExecutor = new SimpleAsyncTaskExecutor();\nDelegatingSecurityContextExecutor executor =\n\tnew DelegatingSecurityContextExecutor(delegateExecutor);\n----\n\nNow, any time `executor.execute(Runnable)` is run, the `SecurityContext` is first obtained by the `SecurityContextHolder` and then that `SecurityContext` is used to create our `DelegatingSecurityContextRunnable`.\nThis means that we are running our `Runnable` with the same user that was used to invoke the `executor.execute(Runnable)` code.\n\n== Spring Security Concurrency Classes\n\nSee the {security-api-url}index.html[Javadoc] for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.\nThey are self-explanatory once you understand the previous code.\n\n* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextCallable[]\n* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextExecutor[]\n* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextExecutorService[]\n* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextRunnable[]\n* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService[]\n* javadoc:org.springframework.security.scheduling.DelegatingSecurityContextSchedulingTaskExecutor[]\n* javadoc:org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor[]\n* javadoc:org.springframework.security.task.DelegatingSecurityContextTaskExecutor[]\n* javadoc:org.springframework.security.scheduling.DelegatingSecurityContextTaskScheduler[]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/cors.adoc",
    "content": "[[cors]]\n= CORS\n\nSpring Framework provides {spring-framework-reference-url}web/webmvc-cors.html[first class support for CORS].\nCORS must be processed before Spring Security, because the pre-flight request does not contain any cookies (that is, the `JSESSIONID`).\nIf the request does not contain any cookies and Spring Security is first, the request determines that the user is not authenticated (since there are no cookies in the request) and rejects it.\n\nThe easiest way to ensure that CORS is handled first is to use the `CorsFilter`.\nUsers can integrate the `CorsFilter` with Spring Security by providing a `CorsConfigurationSource`. Note that Spring Security will automatically configure CORS only if a `UrlBasedCorsConfigurationSource` instance is present.\nFor example, the following will integrate CORS support within Spring Security:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nUrlBasedCorsConfigurationSource corsConfigurationSource() {\n    CorsConfiguration configuration = new CorsConfiguration();\n    configuration.setAllowedOrigins(Arrays.asList(\"https://example.com\"));\n    configuration.setAllowedMethods(Arrays.asList(\"GET\",\"POST\"));\n    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n    source.registerCorsConfiguration(\"/**\", configuration);\n    return source;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun corsConfigurationSource(): UrlBasedCorsConfigurationSource {\n    val configuration = CorsConfiguration()\n    configuration.allowedOrigins = listOf(\"https://example.com\")\n    configuration.allowedMethods = listOf(\"GET\", \"POST\")\n    val source = UrlBasedCorsConfigurationSource()\n    source.registerCorsConfiguration(\"/**\", configuration)\n    return source\n}\n----\n======\n\nThe following listing does the same thing in XML:\n\n[source,xml]\n----\n<http>\n\t<cors configuration-source-ref=\"corsSource\"/>\n\t...\n</http>\n<b:bean id=\"corsSource\" class=\"org.springframework.web.cors.UrlBasedCorsConfigurationSource\">\n\t...\n</b:bean>\n----\n\nIf you use Spring MVC's CORS support, you can omit specifying the `CorsConfigurationSource` and Spring Security uses the CORS configuration provided to Spring MVC:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// if Spring MVC is on classpath and no CorsConfigurationSource is provided,\n\t\t\t// Spring Security will use CORS configuration provided to Spring MVC\n\t\t\t.cors(withDefaults())\n\t\t\t...\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nopen class WebSecurityConfig {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // if Spring MVC is on classpath and no CorsConfigurationSource is provided,\n            // Spring Security will use CORS configuration provided to Spring MVC\n            cors { }\n            // ...\n        }\n        return http.build()\n    }\n}\n----\n======\n\nThe following listing does the same thing in XML:\n\n[source,xml]\n----\n<http>\n\t<!-- Default to Spring MVC's CORS configuration -->\n\t<cors />\n\t...\n</http>\n----\n\nIf you have more than one `CorsConfigurationSource` bean, Spring Security won't automatically configure CORS support for you, that is because it cannot decide which one to use.\nIf you want to specify different `CorsConfigurationSource` for each `SecurityFilterChain`, you can pass it directly into the `.cors()` DSL.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\t@Order(0)\n\tpublic SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.securityMatcher(\"/api/**\")\n\t\t\t.cors((cors) -> cors\n\t\t\t\t.configurationSource(apiConfigurationSource())\n\t\t\t)\n\t\t\t...\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\t@Order(1)\n\tpublic SecurityFilterChain myOtherFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.cors((cors) -> cors\n\t\t\t\t.configurationSource(myWebsiteConfigurationSource())\n\t\t\t)\n\t\t\t...\n\t\treturn http.build();\n\t}\n\n\tUrlBasedCorsConfigurationSource apiConfigurationSource() {\n\t\tCorsConfiguration configuration = new CorsConfiguration();\n\t\tconfiguration.setAllowedOrigins(Arrays.asList(\"https://api.example.com\"));\n\t\tconfiguration.setAllowedMethods(Arrays.asList(\"GET\",\"POST\"));\n\t\tUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n\t\tsource.registerCorsConfiguration(\"/**\", configuration);\n\t\treturn source;\n\t}\n\n\tUrlBasedCorsConfigurationSource myWebsiteConfigurationSource() {\n\t\tCorsConfiguration configuration = new CorsConfiguration();\n\t\tconfiguration.setAllowedOrigins(Arrays.asList(\"https://example.com\"));\n\t\tconfiguration.setAllowedMethods(Arrays.asList(\"GET\",\"POST\"));\n\t\tUrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n\t\tsource.registerCorsConfiguration(\"/**\", configuration);\n\t\treturn source;\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun corsConfigurationSource(): UrlBasedCorsConfigurationSource {\n    val configuration = CorsConfiguration()\n    configuration.allowedOrigins = listOf(\"https://example.com\")\n    configuration.allowedMethods = listOf(\"GET\", \"POST\")\n    val source = UrlBasedCorsConfigurationSource()\n    source.registerCorsConfiguration(\"/**\", configuration)\n    return source\n}\n----\n======\n\n[WARNING]\n====\nCORS is a browser-based security feature.\nBy disabling CORS in Spring Security with `.cors(CorsConfigurer::disable)`, you are not removing CORS protection from your browser.\nInstead, you are removing CORS support from Spring Security, and users will not be able to interact with your Spring backend from a cross-origin browser application.\nTo fix CORS errors in your application, you must enable CORS support, and provide an appropriate configuration source.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/data.adoc",
    "content": "[[data]]\n= Spring Data Integration\n\nSpring Security provides Spring Data integration that allows referring to the current user within your queries.\nIt is not only useful but necessary to include the user in the queries to support paged results since filtering the results afterwards would not scale.\n\n[[data-configuration]]\n== Spring Data & Spring Security Configuration\n\nTo use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityEvaluationContextExtension securityEvaluationContextExtension() {\n\treturn new SecurityEvaluationContextExtension();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun securityEvaluationContextExtension(): SecurityEvaluationContextExtension {\n\treturn SecurityEvaluationContextExtension()\n}\n----\n======\n\nIn XML Configuration, this would look like:\n\n[source,xml]\n----\n<bean class=\"org.springframework.security.data.repository.query.SecurityEvaluationContextExtension\"/>\n----\n\n[[data-query]]\n== Security Expressions within @Query\n\nNow you can use Spring Security within your queries:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic interface MessageRepository extends PagingAndSortingRepository<Message,Long> {\n\t@Query(\"select m from Message m where m.to.id = ?#{ principal?.id }\")\n\tPage<Message> findInbox(Pageable pageable);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ninterface MessageRepository : PagingAndSortingRepository<Message,Long> {\n\t@Query(\"select m from Message m where m.to.id = ?#{ principal?.id }\")\n\tfun findInbox(pageable: Pageable): Page<Message>\n}\n----\n======\n\nThis checks to see if the `Authentication.getPrincipal().getId()` is equal to the recipient of the `Message`.\nNote that this example assumes you have customized the principal to be an Object that has an id property.\nBy exposing the `SecurityEvaluationContextExtension` bean, all of the xref:servlet/authorization/method-security.adoc#authorization-expressions[Common Security Expressions] are available within the Query.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/index.adoc",
    "content": "= Integrations\n:page-section-summary-toc: 1\n\nSpring Security integrates with numerous frameworks and APIs.\nThis section describes various integrations that Spring Security has with other technologies:\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc",
    "content": "[[taglibs]]\n= JSP Tag Libraries\nSpring Security has its own taglib, which provides basic support for accessing security information and applying security constraints in JSPs.\n\n\n== Declaring the Taglib\nTo use any of the tags, you must have the security taglib declared in your JSP:\n\n[source,xml]\n----\n<%@ taglib prefix=\"sec\" uri=\"http://www.springframework.org/security/tags\" %>\n----\n\n[[taglibs-authorize]]\n== The authorize Tag\nThis tag is used to determine whether its contents should be evaluated or not.\nIn Spring Security 3.0, it can be used in two ways.\n\n[NOTE]\n====\nThe legacy options from Spring Security 2.0 are also supported, but discouraged.\n====\n\nThe first approach uses a xref:servlet/authorization/authorize-http-requests.adoc#authorization-expressions[web-security expression], which is specified in the `access` attribute of the tag.\nThe expression evaluation is delegated to the `SecurityExpressionHandler<FilterInvocation>` defined in the application context (you should have web expressions enabled in your `<http>` namespace configuration to make sure this service is available).\nSo, for example, you might have:\n\n[source,xml]\n----\n<sec:authorize access=\"hasRole('supervisor')\">\n\nThis content will only be visible to users who have the \"supervisor\" authority in their list of <tt>GrantedAuthority</tt>s.\n\n</sec:authorize>\n----\n\nWhen used in conjunction with Spring Security's `PermissionEvaluator`, the tag can also be used to check permissions:\n\n[source,xml]\n----\n<sec:authorize access=\"hasPermission(#domain,'read') or hasPermission(#domain,'write')\">\n\nThis content will only be visible to users who have read or write permission to the Object found as a request attribute named \"domain\".\n\n</sec:authorize>\n----\n\nA common requirement is to show only a particular link, assuming the user is actually allowed to click it.\nHow can we determine in advance whether something is allowed? This tag can also operate in an alternative mode that lets you define a particular URL as an attribute.\nIf the user is allowed to invoke that URL, the tag body is evaluated. Otherwise, it is skipped.\nSo you might have something like:\n\n[source,xml]\n----\n<sec:authorize url=\"/admin\">\n\nThis content will only be visible to users who are authorized to send requests to the \"/admin\" URL.\n\n</sec:authorize>\n----\n\nTo use this tag, you must also have an instance of `WebInvocationPrivilegeEvaluator` in your application context.\nIf you are using the namespace, one is automatically registered.\nThis is an instance of `DefaultWebInvocationPrivilegeEvaluator`, which creates a dummy web request for the supplied URL and invokes the security interceptor to see whether the request would succeed or fail.\nThis lets you delegate to the access-control setup you defined by using `intercept-url` declarations within the `<http>` namespace configuration and saves having to duplicate the information (such as the required roles) within your JSPs.\n\nIf you have xref:servlet/authorization/authorize-http-requests.adoc#match-by-httpmethod[method-based authorization rules], you should combine this approach with the `method` attribute (supplying the HTTP method, such as `POST`) to activate the intended method-based rule.\nFor example, if you have a rule `.requestMatchers(POST, \"/admin\").hasRole(\"ADMIN\")`, then you should do `<sec:authorize method=\"POST\" url=\"/admin\">` to match.\n\nYou can store the Boolean result of evaluating the tag (whether it grants or denies access) in a page context scope variable by setting the `var` attribute to the variable name, avoiding the need for duplicating and re-evaluating the condition at other points in the page.\n\n\n=== Disabling Tag Authorization for Testing\nHiding a link in a page for unauthorized users does not prevent them from accessing the URL.\nThey could just type it into their browser directly, for example.\nAs part of your testing process, you may want to reveal the hidden areas, to check that links really are secured at the back end.\nIf you set the `spring.security.disableUISecurity` system property to `true`, the `authorize` tag still runs but does not hide its contents.\nBy default, it also surrounds the content with `<span class=\"securityHiddenUI\">...</span>` tags.\nThis lets you to display \"`hidden`\" content with a particular CSS style, such as a different background color.\nTry running the \"`tutorial`\" sample application, for example, with this property enabled.\n\nYou can also set the `spring.security.securedUIPrefix` and `spring.security.securedUISuffix` properties if you want to change surrounding text from the default `span` tags (or use empty strings to remove it completely).\n\n\n== The authentication Tag\nThis tag allows access to the current `Authentication` object stored in the security context.\nIt renders a property of the object directly in the JSP.\nSo, for example, if the `principal` property of the `Authentication` is an instance of Spring Security's `UserDetails` object, then using `<sec:authentication property=\"principal.username\" />` renders the name of the current user.\n\nOf course, it is not necessary to use JSP tags for this kind of thing, and some people prefer to keep as little logic as possible in the view.\nYou can access the `Authentication` object in your MVC controller (by calling `SecurityContextHolder.getContext().getAuthentication()`) and add the data directly to your model for rendering by the view.\n\n\n== The accesscontrollist Tag\nThis tag is only valid when used with Spring Security's ACL module.\nIt checks a comma-separated list of required permissions for a specified domain object.\nIf the current user has all of those permissions, the tag body is evaluated.\nIf they do not, it is skipped.\n\n[CAUTION]\n====\nIn general, this tag should be considered deprecated.\nInstead, use the <<taglibs-authorize>>.\n====\n\nThe following listing shows an example:\n\n[source,xml]\n----\n<sec:accesscontrollist hasPermission=\"1,2\" domainObject=\"${someObject}\">\n\n<!-- This will be shown if the user has all of the permissions represented by the values \"1\" or \"2\" on the given object. -->\n\n</sec:accesscontrollist>\n----\n\nThe permissions are passed to the `PermissionFactory` defined in the application context, converting them to ACL `Permission` instances, so they may be any format that is supported by the factory. They do not have to be integers. They could be strings such as `READ` or `WRITE`.\nIf no `PermissionFactory` is found, an instance of `DefaultPermissionFactory` is used.\nThe `AclService` from the application context is used to load the `Acl` instance for the supplied object.\nThe `Acl` is invoked with the required permissions to check if all of them are granted.\n\nThis tag also supports the `var` attribute, in the same way as the `authorize` tag.\n\n[[taglibs-csrfinput]]\n== The csrfInput Tag\nIf CSRF protection is enabled, this tag inserts a hidden form field with the correct name and value for the CSRF protection token.\nIf CSRF protection is not enabled, this tag outputs nothing.\n\nNormally, Spring Security automatically inserts a CSRF form field for any `<form:form>` tags you use, but if for some reason you cannot use `<form:form>`, `csrfInput` is a handy replacement.\n\nYou should place this tag within an HTML `<form></form>` block, where you would normally place other input fields.\nDo NOT place this tag within a Spring `<form:form></form:form>` block.\nSpring Security handles Spring forms automatically.\nThe following listing shows an example:\n\n[source,xml]\n----\n\t<form method=\"post\" action=\"/do/something\">\n\t\t<sec:csrfInput />\n\t\tName:<br />\n\t\t<input type=\"text\" name=\"name\" />\n\t\t...\n\t</form>\n----\n\n[[taglibs-csrfmeta]]\n== The csrfMetaTags Tag\nIf CSRF protection is enabled, this tag inserts meta tags that contain the CSRF protection token form field and header names and CSRF protection token value.\nThese meta tags are useful for employing CSRF protection within JavaScript in your applications.\n\nYou should place `csrfMetaTags` within an HTML `<head></head>` block, where you would normally place other meta tags.\nOnce you use this tag, you can access the form field name, header name, and token value by using JavaScript.\nJQuery is used in this example to make the task easier.\nThe following listing shows an example:\n\n[source,xml]\n----\n<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<title>CSRF Protected JavaScript Page</title>\n\t\t<meta name=\"description\" content=\"This is the description for this page\" />\n\t\t<sec:csrfMetaTags />\n\t\t<script type=\"text/javascript\" language=\"javascript\">\n\n\t\t\tvar csrfParameter = $(\"meta[name='_csrf_parameter']\").attr(\"content\");\n\t\t\tvar csrfHeader = $(\"meta[name='_csrf_header']\").attr(\"content\");\n\t\t\tvar csrfToken = $(\"meta[name='_csrf']\").attr(\"content\");\n\n\t\t\t// using XMLHttpRequest directly to send an x-www-form-urlencoded request\n\t\t\tvar ajax = new XMLHttpRequest();\n\t\t\tajax.open(\"POST\", \"https://www.example.org/do/something\", true);\n\t\t\tajax.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded data\");\n\t\t\tajax.send(csrfParameter + \"=\" + csrfToken + \"&name=John&...\");\n\n\t\t\t// using XMLHttpRequest directly to send a non-x-www-form-urlencoded request\n\t\t\tvar ajax = new XMLHttpRequest();\n\t\t\tajax.open(\"POST\", \"https://www.example.org/do/something\", true);\n\t\t\tajax.setRequestHeader(csrfHeader, csrfToken);\n\t\t\tajax.send(\"...\");\n\n\t\t\t// using JQuery to send an x-www-form-urlencoded request\n\t\t\tvar data = {};\n\t\t\tdata[csrfParameter] = csrfToken;\n\t\t\tdata[\"name\"] = \"John\";\n\t\t\t...\n\t\t\t$.ajax({\n\t\t\t\turl: \"https://www.example.org/do/something\",\n\t\t\t\ttype: \"POST\",\n\t\t\t\tdata: data,\n\t\t\t\t...\n\t\t\t});\n\n\t\t\t// using JQuery to send a non-x-www-form-urlencoded request\n\t\t\tvar headers = {};\n\t\t\theaders[csrfHeader] = csrfToken;\n\t\t\t$.ajax({\n\t\t\t\turl: \"https://www.example.org/do/something\",\n\t\t\t\ttype: \"POST\",\n\t\t\t\theaders: headers,\n\t\t\t\t...\n\t\t\t});\n\n\t\t<script>\n\t</head>\n\t<body>\n\t\t...\n\t</body>\n</html>\n----\n\nIf CSRF protection is not enabled, `csrfMetaTags` outputs nothing.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/localization.adoc",
    "content": "[[localization]]\n= Localization\nSpring Security supports localization of exception messages that end users are likely to see.\nIf your application is designed for English-speaking users, you need not do anything as, by default, all Security messages are in English.\nIf you need to support other locales, this section contains everything you need to know.\n\nAll exception messages, including messages related to authentication failures and access being denied (authorization failures), can be localized.\nExceptions and logging messages that are focused on developers or system deployers (including incorrect attributes, interface contract violations, using incorrect constructors, startup time validation, debug-level logging) are not localized and instead are hard-coded in English within Spring Security's code.\n\nIn the `spring-security-core-xx.jar`, you find an `org.springframework.security` package that, in turn, contains a `messages.properties` file as well as localized versions for some common languages.\nYour `ApplicationContext` should refer to this, as Spring Security classes implement Spring's `MessageSourceAware` interface and expect the message resolver to be dependency injected at application context startup time.\nUsually, all you need to do is register a bean inside your application context to refer to the messages.\nThe following listing shows an example:\n\n[source,xml]\n----\n<bean id=\"messageSource\"\n\tclass=\"org.springframework.context.support.ReloadableResourceBundleMessageSource\">\n<property name=\"basename\" value=\"classpath:org/springframework/security/messages\"/>\n</bean>\n----\n\nThe `messages.properties` is named in accordance with standard resource bundles and represents the default language supported by Spring Security messages.\nThis default file is in English.\n\nTo customize the `messages.properties` file or support other languages, you should copy the file, rename it accordingly, and register it inside the preceding bean definition.\nThere are not a large number of message keys inside this file, so localization should not be considered a major initiative.\nIf you do perform localization of this file, consider sharing your work with the community by logging a JIRA task and attaching your appropriately-named localized version of `messages.properties`.\n\nSpring Security relies on Spring's localization support in order to actually look up the appropriate message.\nFor this to work, you have to make sure that the locale from the incoming request is stored in Spring's `org.springframework.context.i18n.LocaleContextHolder`.\nSpring MVC's `DispatcherServlet` does this for your application automatically. However, since Spring Security's filters are invoked before this, the `LocaleContextHolder` needs to be set up to contain the correct `Locale` before the filters are called.\nYou can either do this in a filter yourself (which must come before the Spring Security filters in `web.xml`) or you can use Spring's `RequestContextFilter`.\nSee the Spring Framework documentation for further details on using localization with Spring.\n\nThe `contacts` sample application is set up to use localized messages.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/mvc.adoc",
    "content": "[[mvc]]\n= Spring MVC Integration\n\nSpring Security provides a number of optional integrations with Spring MVC.\nThis section covers the integration in further detail.\n\n[[mvc-enablewebmvcsecurity]]\n[[mvc-enablewebsecurity]]\n== @EnableWebSecurity\n\nTo enable Spring Security integration with Spring MVC, add the `@EnableWebSecurity` annotation to your configuration.\n\n[NOTE]\n====\nSpring Security provides the configuration by using Spring MVC's {spring-framework-reference-url}web/webmvc/mvc-config/customize.html[`WebMvcConfigurer`].\nThis means that, if you use more advanced options, such as integrating with `WebMvcConfigurationSupport` directly, you need to manually provide the Spring Security configuration.\n====\n\n[[mvc-requestmatcher]]\n== PathPatternRequestMatcher\n\nSpring Security provides deep integration with how Spring MVC matches on URLs with `PathPatternRequestMatcher`.\nThis is helpful to ensure that your Security rules match the logic used to handle your requests.\n\n`PathPatternRequestMatcher` must use the same `PathPatternParser` as Spring MVC.\nIf you are not customizing the `PathPatternParser`, then you can do:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nPathPatternRequestMatcherBuilderFactoryBean usePathPattern() {\n\treturn new PathPatternRequestMatcherBuilderFactoryBean();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun usePathPattern(): PathPatternRequestMatcherBuilderFactoryBean {\n    return PathPatternRequestMatcherBuilderFactoryBean()\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<b:bean class=\"org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean\"/>\n----\n======\n\nand Spring Security will find the appropriate Spring MVC configuration for you.\n\nIf you *are* customizing Spring MVC's `PathPatternParser` instance, you will need to <<security-mvc-same-application-context, configure Spring Security and Spring MVC in the same `ApplicationContext`>>.\n\n[NOTE]\n====\nWe always recommend that you provide authorization rules by matching on the `HttpServletRequest` and method security.\n\nProviding authorization rules by matching on `HttpServletRequest` is good, because it happens very early in the code path and helps reduce the https://en.wikipedia.org/wiki/Attack_surface[attack surface].\nMethod security ensures that, if someone has bypassed the web authorization rules, your application is still secured.\nThis is known as https://en.wikipedia.org/wiki/Defense_in_depth_(computing)[Defense in Depth]\n====\n\nNow that Spring MVC is integrated with Spring Security, you are ready to write some xref:servlet/authorization/authorize-http-requests.adoc[authorization rules] that will use `PathPatternRequestMatcher`.\n\n[[mvc-authentication-principal]]\n== @AuthenticationPrincipal\n\nSpring Security provides `AuthenticationPrincipalArgumentResolver`, which can automatically resolve the current `Authentication.getPrincipal()` for Spring MVC arguments.\nBy using `@EnableWebSecurity`, you automatically have this added to your Spring MVC configuration.\nIf you use XML-based configuration, you must add this yourself:\n\n[source,xml]\n----\n<mvc:annotation-driven>\n\t\t<mvc:argument-resolvers>\n\t\t\t\t<bean class=\"org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver\" />\n\t\t</mvc:argument-resolvers>\n</mvc:annotation-driven>\n----\n\nOnce you have properly configured `AuthenticationPrincipalArgumentResolver`, you can entirely decouple from Spring Security in your Spring MVC layer.\n\nConsider a situation where a custom `UserDetailsService` returns an `Object` that implements `UserDetails` and your own `CustomUser` `Object`. The `CustomUser` of the currently authenticated user could be accessed by using the following code:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RequestMapping(\"/messages/inbox\")\npublic ModelAndView findMessagesForUser() {\n\tAuthentication authentication =\n\tSecurityContextHolder.getContext().getAuthentication();\n\tCustomUser custom = (CustomUser) authentication == null ? null : authentication.getPrincipal();\n\n\t// .. find messages for this user and return them ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RequestMapping(\"/messages/inbox\")\nopen fun findMessagesForUser(): ModelAndView {\n    val authentication: Authentication = SecurityContextHolder.getContext().authentication\n    val custom: CustomUser? = if (authentication as CustomUser == null) null else authentication.principal\n\n    // .. find messages for this user and return them ...\n}\n----\n======\n\nAs of Spring Security 3.2, we can resolve the argument more directly by adding an annotation:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\n\n// ...\n\n@RequestMapping(\"/messages/inbox\")\npublic ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {\n\n\t// .. find messages for this user and return them ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RequestMapping(\"/messages/inbox\")\nopen fun findMessagesForUser(@AuthenticationPrincipal customUser: CustomUser?): ModelAndView {\n\n    // .. find messages for this user and return them ...\n}\n----\n======\n\nSometimes, you may need to transform the principal in some way.\nFor example, if `CustomUser` needed to be final, it could not be extended.\nIn this situation, the `UserDetailsService` might return an `Object` that implements `UserDetails` and provides a method named `getCustomUser` to access `CustomUser`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class CustomUserUserDetails extends User {\n\t\t// ...\n\t\tpublic CustomUser getCustomUser() {\n\t\t\t\treturn customUser;\n\t\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass CustomUserUserDetails(\n    username: String?,\n    password: String?,\n    authorities: MutableCollection<out GrantedAuthority>?\n) : User(username, password, authorities) {\n    // ...\n    val customUser: CustomUser? = null\n}\n----\n======\n\nWe could then access the `CustomUser` by using a {spring-framework-reference-url}core/expressions.html[SpEL expression] that uses `Authentication.getPrincipal()` as the root object:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\n\n// ...\n\n@RequestMapping(\"/messages/inbox\")\npublic ModelAndView findMessagesForUser(@AuthenticationPrincipal(expression = \"customUser\") CustomUser customUser) {\n\n\t// .. find messages for this user and return them ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.core.annotation.AuthenticationPrincipal\n\n// ...\n\n@RequestMapping(\"/messages/inbox\")\nopen fun findMessagesForUser(@AuthenticationPrincipal(expression = \"customUser\") customUser: CustomUser?): ModelAndView {\n\n    // .. find messages for this user and return them ...\n}\n----\n======\n\nWe can also refer to beans in our SpEL expressions.\nFor example, we could use the following if we were using JPA to manage our users and if we wanted to modify and save a property on the current user:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\n\n// ...\n\n@PutMapping(\"/users/self\")\npublic ModelAndView updateName(@AuthenticationPrincipal(expression = \"@jpaEntityManager.merge(#this)\") CustomUser attachedCustomUser,\n\t\t@RequestParam String firstName) {\n\n\t// change the firstName on an attached instance which will be persisted to the database\n\tattachedCustomUser.setFirstName(firstName);\n\n\t// ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.core.annotation.AuthenticationPrincipal\n\n// ...\n\n@PutMapping(\"/users/self\")\nopen fun updateName(\n    @AuthenticationPrincipal(expression = \"@jpaEntityManager.merge(#this)\") attachedCustomUser: CustomUser,\n    @RequestParam firstName: String?\n): ModelAndView {\n\n    // change the firstName on an attached instance which will be persisted to the database\n    attachedCustomUser.setFirstName(firstName)\n\n    // ...\n}\n----\n======\n\nWe can further remove our dependency on Spring Security by making `@AuthenticationPrincipal` a meta-annotation on our own annotation.\nThe next example demonstrates how we could do so on an annotation named `@CurrentUser`.\n\n[NOTE]\n====\nTo remove the dependency on Spring Security, it is the consuming application that would create `@CurrentUser`.\nThis step is not strictly required but assists in isolating your dependency to Spring Security to a more central location.\n====\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Target({ElementType.PARAMETER, ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@AuthenticationPrincipal\npublic @interface CurrentUser {}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)\n@Retention(AnnotationRetention.RUNTIME)\n@MustBeDocumented\n@AuthenticationPrincipal\nannotation class CurrentUser\n----\n======\n\nWe have isolated our dependency on Spring Security to a single file.\nNow that `@CurrentUser` has been specified, we can use it to signal to resolve our `CustomUser` of the currently authenticated user:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RequestMapping(\"/messages/inbox\")\npublic ModelAndView findMessagesForUser(@CurrentUser CustomUser customUser) {\n\n\t// .. find messages for this user and return them ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RequestMapping(\"/messages/inbox\")\nopen fun findMessagesForUser(@CurrentUser customUser: CustomUser?): ModelAndView {\n\n    // .. find messages for this user and return them ...\n}\n----\n======\n\nOnce it is a meta-annotation, parameterization is also available to you.\n\nFor example, consider when you have a JWT as your principal and you want to say which claim to retrieve.\nAs a meta-annotation, you might do:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Target({ElementType.PARAMETER, ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@AuthenticationPrincipal(expression = \"claims['sub']\")\npublic @interface CurrentUser {}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)\n@Retention(AnnotationRetention.RUNTIME)\n@MustBeDocumented\n@AuthenticationPrincipal(expression = \"claims['sub']\")\nannotation class CurrentUser\n----\n======\n\nwhich is already quite powerful.\nBut, it is also limited to retrieving the `sub` claim.\n\nTo make this more flexible, first publish the `AnnotationTemplateExpressionDefaults` bean like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic AnnotationTemplateExpressionDefaults templateDefaults() {\n\treturn new AnnotationTemplateExpressionDefaults();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun templateDefaults(): AnnotationTemplateExpressionDefaults {\n\treturn AnnotationTemplateExpressionDefaults()\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<b:bean name=\"annotationExpressionTemplateDefaults\" class=\"org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults\"/>\n----\n======\n\nand then you can supply a parameter to `@CurrentUser` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Target({ElementType.PARAMETER, ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@AuthenticationPrincipal(expression = \"claims['{claim}']\")\npublic @interface CurrentUser {\n\tString claim() default 'sub';\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)\n@Retention(AnnotationRetention.RUNTIME)\n@MustBeDocumented\n@AuthenticationPrincipal(expression = \"claims['{claim}']\")\nannotation class CurrentUser(val claim: String = \"sub\")\n----\n======\n\nThis will allow you more flexibility across your set of applications in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RequestMapping(\"/messages/inbox\")\npublic ModelAndView findMessagesForUser(@CurrentUser(\"user_id\") String userId) {\n\n\t// .. find messages for this user and return them ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RequestMapping(\"/messages/inbox\")\nopen fun findMessagesForUser(@CurrentUser(\"user_id\") userId: String?): ModelAndView {\n\n    // .. find messages for this user and return them ...\n}\n----\n======\n\n[[mvc-current-security-context]]\n== @CurrentSecurityContext\n\nSpring Security provides `CurrentSecurityContextArgumentResolver`, which can automatically resolve the current `SecurityContext` for Spring MVC arguments.\nBy using `@EnableWebSecurity`, you automatically have this added to your Spring MVC configuration.\nIf you use XML-based configuration, you must add this yourself:\n\n[source,xml]\n----\n<mvc:annotation-driven>\n\t\t<mvc:argument-resolvers>\n\t\t\t\t<bean class=\"org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver\" />\n\t\t</mvc:argument-resolvers>\n</mvc:annotation-driven>\n----\n\nOnce `CurrentSecurityContextArgumentResolver` is configured, you can access the `SecurityContext` directly:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/me\")\npublic String me(@CurrentSecurityContext SecurityContext context) {\n\treturn context.getAuthentication().getName();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/me\")\nfun me(@CurrentSecurityContext context: SecurityContext): String {\n    return context.authentication.name\n}\n----\n======\n\nYou can also use a SpEL expression that is rooted at the `SecurityContext`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/me\")\npublic String me(@CurrentSecurityContext(expression = \"authentication\") Authentication authentication) {\n\treturn authentication.getName();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/me\")\nfun me(@CurrentSecurityContext(expression = \"authentication\") authentication: Authentication): String {\n    return authentication.name\n}\n----\n======\n\n[[mvc-async]]\n== Spring MVC Async Integration\n\nSpring Web MVC 3.2+ has excellent support for {spring-framework-reference-url}web/webmvc/mvc-ann-async.html[Asynchronous Request Processing].\nWith no additional configuration, Spring Security automatically sets up the `SecurityContext` to the `Thread` that invokes a `Callable` returned by your controllers.\nFor example, the following method automatically has its `Callable` invoked with the `SecurityContext` that was available when the `Callable` was created:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RequestMapping(method=RequestMethod.POST)\npublic Callable<String> processUpload(final MultipartFile file) {\n\nreturn new Callable<String>() {\n\tpublic Object call() throws Exception {\n\t// ...\n\treturn \"someView\";\n\t}\n};\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RequestMapping(method = [RequestMethod.POST])\nopen fun processUpload(file: MultipartFile?): Callable<String> {\n    return Callable {\n        // ...\n        \"someView\"\n    }\n}\n----\n======\n\n\n.Associating SecurityContext to Callable's\n[NOTE]\n====\nMore technically speaking, Spring Security integrates with `WebAsyncManager`.\nThe `SecurityContext` that is used to process the `Callable` is the `SecurityContext` that exists on the `SecurityContextHolder` when `startCallableProcessing` is invoked.\n====\n\nThere is no automatic integration with a `DeferredResult` that is returned by controllers.\nThis is because `DeferredResult` is processed by the users and, thus, there is no way of automatically integrating with it.\nHowever, you can still use xref:features/integrations/concurrency.adoc#concurrency[Concurrency Support] to provide transparent integration with Spring Security.\n\n[[mvc-csrf]]\n== Spring MVC and CSRF Integration\n\nSpring Security integrates with Spring MVC to add CSRF protection.\n\n=== Automatic Token Inclusion\n\nSpring Security automatically xref:servlet/exploits/csrf.adoc#csrf-integration-form[include the CSRF Token] within forms that use the {spring-framework-reference-url}web/webmvc-view/mvc-jsp.html#mvc-view-jsp-formtaglib-formtag[Spring MVC form tag].\nConsider the following JSP:\n\n[source,xml]\n----\n<jsp:root xmlns:jsp=\"http://java.sun.com/JSP/Page\"\n\txmlns:c=\"http://java.sun.com/jsp/jstl/core\"\n\txmlns:form=\"http://www.springframework.org/tags/form\" version=\"2.0\">\n\t<jsp:directive.page language=\"java\" contentType=\"text/html\" />\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n\t<!-- ... -->\n\n\t<c:url var=\"logoutUrl\" value=\"/logout\"/>\n\t<form:form action=\"${logoutUrl}\"\n\t\tmethod=\"post\">\n\t<input type=\"submit\"\n\t\tvalue=\"Log out\" />\n\t<input type=\"hidden\"\n\t\tname=\"${_csrf.parameterName}\"\n\t\tvalue=\"${_csrf.token}\"/>\n\t</form:form>\n\n\t<!-- ... -->\n</html>\n</jsp:root>\n----\n\nThe preceding example output HTMLs that is similar to the following:\n\n[source,xml]\n----\n<!-- ... -->\n\n<form action=\"/context/logout\" method=\"post\">\n<input type=\"submit\" value=\"Log out\"/>\n<input type=\"hidden\" name=\"_csrf\" value=\"f81d4fae-7dec-11d0-a765-00a0c91e6bf6\"/>\n</form>\n\n<!-- ... -->\n----\n\n[[mvc-csrf-resolver]]\n=== Resolving the CsrfToken\n\nSpring Security provides `CsrfTokenArgumentResolver`, which can automatically resolve the current `CsrfToken` for Spring MVC arguments.\nBy using xref:servlet/configuration/java.adoc#jc-hello-wsca[@EnableWebSecurity], you automatically have this added to your Spring MVC configuration.\nIf you use XML-based configuration, you must add this yourself.\n\nOnce `CsrfTokenArgumentResolver` is properly configured, you can expose the `CsrfToken` to your static HTML based application:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class CsrfController {\n\n\t@RequestMapping(\"/csrf\")\n\tpublic CsrfToken csrf(CsrfToken token) {\n\t\treturn token;\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RestController\nclass CsrfController {\n    @RequestMapping(\"/csrf\")\n    fun csrf(token: CsrfToken): CsrfToken {\n        return token\n    }\n}\n----\n======\n\nIt is important to keep the `CsrfToken` a secret from other domains.\nThis means that, if you use https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS[Cross Origin Sharing (CORS)], you should *NOT* expose the `CsrfToken` to any external domains.\n\n[[security-mvc-same-application-context]]\n== Configuring Spring MVC and Spring Security in the Same Application Context\n\nIf you are using Boot, Spring MVC and Spring Security are in the same application context by default.\n\nOtherwise, for Java Config, including both `@EnableWebMvc` and `@EnableWebSecurity` will construct Spring Security and Spring MVC components in the same context.\n\nOf, if you are using ``ServletListener``s you can do:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class SecurityInitializer extends\n    AbstractAnnotationConfigDispatcherServletInitializer {\n\n  @Override\n  protected Class<?>[] getRootConfigClasses() {\n    return null;\n  }\n\n  @Override\n  protected Class<?>[] getServletConfigClasses() {\n    return new Class[] { RootConfiguration.class,\n        WebMvcConfiguration.class };\n  }\n\n  @Override\n  protected String[] getServletMappings() {\n    return new String[] { \"/\" };\n  }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass SecurityInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {\n    override fun getRootConfigClasses(): Array<Class<*>>? {\n        return null\n    }\n\n    override fun getServletConfigClasses(): Array<Class<*>> {\n        return arrayOf(\n            RootConfiguration::class.java,\n            WebMvcConfiguration::class.java\n        )\n    }\n\n    override fun getServletMappings(): Array<String> {\n        return arrayOf(\"/\")\n    }\n}\n----\n======\n\nAnd finally for a `web.xml` file, you configure the `DispatcherServlet` like so:\n\n[source,xml]\n----\n<listener>\n  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n</listener>\n\n<!-- All Spring Configuration (both MVC and Security) are in /WEB-INF/spring/ -->\n<context-param>\n  <param-name>contextConfigLocation</param-name>\n  <param-value>/WEB-INF/spring/*.xml</param-value>\n</context-param>\n\n<servlet>\n  <servlet-name>spring</servlet-name>\n  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n  <!-- Load from the ContextLoaderListener -->\n  <init-param>\n    <param-name>contextConfigLocation</param-name>\n    <param-value></param-value>\n  </init-param>\n</servlet>\n\n<servlet-mapping>\n  <servlet-name>spring</servlet-name>\n  <url-pattern>/</url-pattern>\n</servlet-mapping>\n----\n\nThe following `WebSecurityConfiguration` in placed in the  `ApplicationContext` of the `DispatcherServlet`.\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/observability.adoc",
    "content": "[[observability]]\n= Observability\n\nSpring Security integrates with Spring Observability out-of-the-box for tracing; though it's also quite simple to configure for gathering metrics.\n\n[[observability-tracing]]\n== Tracing\n\nWhen an `ObservationRegistry` bean is present, Spring Security creates traces for:\n\n* the filter chain\n* the `AuthenticationManager`, and\n* the `AuthorizationManager`\n\n[[observability-tracing-boot]]\n=== Boot Integration\n\nFor example, consider a simple Boot application:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@SpringBootApplication\npublic class MyApplication {\n\t@Bean\n\tpublic UserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tObservationRegistryCustomizer<ObservationRegistry> addTextHandler() {\n\t\treturn (registry) -> registry.observationConfig().observationHandler(new ObservationTextPublisher());\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ListenerSamplesApplication.class, args);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@SpringBootApplication\nclass MyApplication {\n\t@Bean\n\tfun userDetailsService(): UserDetailsService {\n\t\tInMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tfun addTextHandler(): ObservationRegistryCustomizer<ObservationRegistry> {\n\t\treturn registry: ObservationRegistry -> registry.observationConfig()\n\t\t\t\t.observationHandler(ObservationTextPublisher());\n\t}\n\n\tfun main(args: Array<String>) {\n\t\trunApplication<MyApplication>(*args)\n\t}\n}\n----\n======\n\nAnd a corresponding request:\n\n[source,bash]\n----\n?> http -a user:password :8080\n----\n\nWill produce the following output (indentation added for clarity):\n\n[source,bash]\n----\nSTART - name='http.server.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@687e16d1', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.001779024, duration(nanos)=1779024.0, startTimeNanos=91695917264958}']\n\tSTART - name='spring.security.http.chains', contextualName='spring.security.http.chains.before', error='null', lowCardinalityKeyValues=[chain.position='0', chain.size='17', filter.section='before'], highCardinalityKeyValues=[request.line='GET /'], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@79f554a5', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=7.42147E-4, duration(nanos)=742147.0, startTimeNanos=91695947182029}']\n\t... skipped for brevity ...\n\tSTOP - name='spring.security.http.chains', contextualName='spring.security.http.chains.before', error='null', lowCardinalityKeyValues=[chain.position='0', chain.size='17', filter.section='before'], highCardinalityKeyValues=[request.line='GET /'], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@79f554a5', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.014771848, duration(nanos)=1.4771848E7, startTimeNanos=91695947182029}']\n\t\tSTART - name='spring.security.authentications', contextualName='null', error='null', lowCardinalityKeyValues=[authentication.failure.type='Optional', authentication.method='ProviderManager', authentication.request.type='UsernamePasswordAuthenticationToken'], highCardinalityKeyValues=[], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@4d4b2b56', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=7.09759E-4, duration(nanos)=709759.0, startTimeNanos=91696094477504}']\n\t\t... skipped for brevity ...\n\t\tSTOP - name='spring.security.authentications', contextualName='null', error='null', lowCardinalityKeyValues=[authentication.failure.type='Optional', authentication.method='ProviderManager', authentication.request.type='UsernamePasswordAuthenticationToken', authentication.result.type='UsernamePasswordAuthenticationToken'], highCardinalityKeyValues=[], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@4d4b2b56', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.895141386, duration(nanos)=8.95141386E8, startTimeNanos=91696094477504}']\n\t\tSTART - name='spring.security.authorizations', contextualName='null', error='null', lowCardinalityKeyValues=[object.type='Servlet3SecurityContextHolderAwareRequestWrapper'], highCardinalityKeyValues=[], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@6d834cc7', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=3.0965E-4, duration(nanos)=309650.0, startTimeNanos=91697034893983}']\n\t\t... skipped for brevity ...\n\t\tSTOP - name='spring.security.authorizations', contextualName='null', error='null', lowCardinalityKeyValues=[authorization.decision='true', object.type='Servlet3SecurityContextHolderAwareRequestWrapper'], highCardinalityKeyValues=[authentication.authorities='[app]', authorization.decision.details='AuthorizationDecision [granted=true]'], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@6d834cc7', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.02084809, duration(nanos)=2.084809E7, startTimeNanos=91697034893983}']\n\t\tSTART - name='spring.security.http.secured.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@649c5ec3', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=2.67878E-4, duration(nanos)=267878.0, startTimeNanos=91697059819304}']\n\t\t... skipped for brevity ...\n\t\tSTOP - name='spring.security.http.secured.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@649c5ec3', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.090753322, duration(nanos)=9.0753322E7, startTimeNanos=91697059819304}']\n\tSTART - name='spring.security.http.chains', contextualName='spring.security.http.chains.after', error='null', lowCardinalityKeyValues=[chain.position='0', chain.size='17', filter.section='after'], highCardinalityKeyValues=[request.line='GET /'], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@47af8207', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=5.31832E-4, duration(nanos)=531832.0, startTimeNanos=91697152857268}']\n\t... skipped for brevity ...\n\tSTOP - name='spring.security.http.chains', contextualName='spring.security.http.chains.after', error='null', lowCardinalityKeyValues=[chain.position='17', chain.size='17', current.filter.name='DisableEncodeUrlFilter', filter.section='after'], highCardinalityKeyValues=[request.line='GET /'], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@47af8207', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=0.007689382, duration(nanos)=7689382.0, startTimeNanos=91697152857268}']\nSTOP - name='http.server.requests', contextualName='null', error='null', lowCardinalityKeyValues=[], highCardinalityKeyValues=[request.line='GET /'], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@687e16d1', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=1.245858319, duration(nanos)=1.245858319E9, startTimeNanos=91695917264958}']\n----\n\n[[observability-tracing-manual-configuration]]\n=== Manual Configuration\n\nFor a non-Spring Boot application, or to override the existing Boot configuration, you can publish your own `ObservationRegistry` and Spring Security will still pick it up.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@SpringBootApplication\npublic class MyApplication {\n\t@Bean\n\tpublic UserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tObservationRegistry observationRegistry() {\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(new ObservationTextPublisher());\n\t\treturn registry;\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(ListenerSamplesApplication.class, args);\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@SpringBootApplication\nclass MyApplication {\n\t@Bean\n\tfun userDetailsService(): UserDetailsService {\n\t\tInMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tfun observationRegistry(): ObservationRegistry {\n\t\tObservationRegistry registry = ObservationRegistry.create()\n\t\tregistry.observationConfig().observationHandler(ObservationTextPublisher())\n\t\treturn registry\n\t}\n\n\tfun main(args: Array<String>) {\n\t\trunApplication<MyApplication>(*args)\n\t}\n}\n----\n\nXml::\n+\n[source,kotlin,role=\"secondary\"]\n----\n<sec:http auto-config=\"true\" observation-registry-ref=\"ref\">\n\t<sec:intercept-url pattern=\"/**\" access=\"authenticated\"/>\n</sec:http>\n\n<!-- define and configure ObservationRegistry bean -->\n----\n======\n\n[[observability-tracing-disable]]\n==== Disabling Observability\n\nIf you don't want any Spring Security observations, in a Spring Boot application you can publish a `ObservationRegistry.NOOP` `@Bean`.\nHowever, this may turn off observations for more than just Spring Security.\n\nInstead, you can publish a `SecurityObservationSettings` like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityObservationSettings noSpringSecurityObservations() {\n\treturn SecurityObservationSettings.noObservations();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun noSpringSecurityObservations(): SecurityObservationSettings {\n\treturn SecurityObservationSettings.noObservations()\n}\n----\n======\n\nand then Spring Security will not wrap any filter chains, authentications, or authorizations in their `ObservationXXX` counterparts.\n\n[TIP]\nThere is no facility for disabling observations with XML support.\nInstead, simply do not set the `observation-registry-ref` attribute.\n\nYou can also disable security for only a subset of Security's observations.\nFor example, the `SecurityObservationSettings` bean excludes the filter chain observations by default.\nSo, you can also do:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityObservationSettings defaultSpringSecurityObservations() {\n\treturn SecurityObservationSettings.withDefaults().build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun defaultSpringSecurityObservations(): SecurityObservationSettings {\n\treturn SecurityObservationSettings.withDefaults().build()\n}\n----\n======\n\nOr you can turn on and off observations individually, based on the defaults:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityObservationSettings allSpringSecurityObservations() {\n\treturn SecurityObservationSettings.withDefaults()\n            .shouldObserveFilterChains(true).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun allSpringSecurityObservations(): SecurityObservationSettings {\n    return SecurityObservationSettings.builder()\n            .shouldObserveFilterChains(true).build()\n}\n----\n======\n\n[NOTE]\n=====\nFor backward compatibility, the all Spring Security observations are made unless a `SecurityObservationSettings` is published.\n=====\n\n[[observability-tracing-listing]]\n=== Trace Listing\n\nSpring Security tracks the following spans on each request:\n\n1. `spring.security.http.requests` - a span that wraps the entire filter chain, including the request\n2. `spring.security.http.chains.before` - a span that wraps the receiving part of the security filters\n3. `spring.security.http.chains.after` - a span that wraps the returning part of the security filters\n4. `spring.security.http.secured.requests` - a span that wraps the now-secured application request\n5. `spring.security.http.unsecured.requests` - a span that wraps requests that Spring Security does not secure\n6. `spring.security.authentications` - a span that wraps authentication attempts\n7. `spring.security.authorizations` - a span that wraps authorization attempts\n\n[TIP]\n`spring.security.http.chains.before` + `spring.security.http.secured.requests` + `spring.security.http.chains.after` = `spring.security.http.requests` +\n`spring.security.http.chains.before` + `spring.security.http.chains.after` = Spring Security's part of the request\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc",
    "content": "[[servletapi]]\n= Servlet API integration\nThis section describes how Spring Security is integrated with the Servlet API.\n\n\n[[servletapi-25]]\n== Servlet 2.5+ Integration\n\nThis section describes how Spring Security integrates with the Servlet 2.5 specification.\n\n\n[[servletapi-remote-user]]\n=== HttpServletRequest.getRemoteUser()\nhttps://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest.getRemoteUser()`] returns the result of `SecurityContextHolder.getContext().getAuthentication().getName()`, which is typically the current username.This can be useful if you want to display the current username in your application.\nAdditionally, you can check this for null to determine whether a user has authenticated or is anonymous.\nKnowing whether the user is authenticated or not can be useful for determining if certain UI elements should be shown or not (for example, a logout link that should be displayed only if the user is authenticated).\n\n\n[[servletapi-user-principal]]\n=== HttpServletRequest.getUserPrincipal()\nhttps://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest.getUserPrincipal()`] returns the result of `SecurityContextHolder.getContext().getAuthentication()`.\nThis means that it is an `Authentication`, which is typically an instance of `UsernamePasswordAuthenticationToken` when using username- and password-based authentication.\nThis can be useful if you need additional information about your user.\nFor example, you might have created a custom `UserDetailsService` that returns a custom `UserDetails` containing a first and last name for your user.\nYou could obtain this information with the following:\n\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nAuthentication auth = httpServletRequest.getUserPrincipal();\n// assume integrated custom UserDetails called MyCustomUserDetails\n// by default, typically instance of UserDetails\nMyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal();\nString firstName = userDetails.getFirstName();\nString lastName = userDetails.getLastName();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval auth: Authentication = httpServletRequest.getUserPrincipal()\n// assume integrated custom UserDetails called MyCustomUserDetails\n// by default, typically instance of UserDetails\nval userDetails: MyCustomUserDetails = auth.principal as MyCustomUserDetails\nval firstName: String = userDetails.firstName\nval lastName: String = userDetails.lastName\n----\n======\n\n[NOTE]\n====\nIt should be noted that it is typically bad practice to perform so much logic throughout your application.\nInstead, one should centralize it to reduce any coupling of Spring Security and the Servlet API's.\n====\n\n[[servletapi-user-in-role]]\n=== HttpServletRequest.isUserInRole(String)\nhttps://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest.isUserInRole(String)`] determines if `SecurityContextHolder.getContext().getAuthentication().getAuthorities()` contains a `GrantedAuthority` with the role passed into `isUserInRole(String)`.\nTypically, users should not pass the `ROLE_` prefix to this method, since it is added automatically.\nFor example, if you want to determine if the current user has the authority \"ROLE_ADMIN\", you could use the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nboolean isAdmin = httpServletRequest.isUserInRole(\"ADMIN\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval isAdmin: Boolean = httpServletRequest.isUserInRole(\"ADMIN\")\n----\n======\n\nThis might be useful to determine if certain UI components should be displayed.\nFor example, you might display admin links only if the current user is an admin.\n\n[[servletapi-3]]\n== Servlet 3+ Integration\nThe following section describes the Servlet 3 methods with which Spring Security integrates.\n\n\n[[servletapi-authenticate]]\n=== HttpServletRequest.authenticate(HttpServletResponse)\nYou can use the https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#authenticate%28javax.servlet.http.HttpServletResponse%29[`HttpServletRequest.authenticate(HttpServletResponse)`] method to ensure that a user is authenticated.\nIf they are not authenticated, the configured `AuthenticationEntryPoint` is used to request the user to authenticate (redirect to the login page).\n\n\n[[servletapi-login]]\n=== HttpServletRequest.login(String,String)\nYou can use the https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login%28java.lang.String,%20java.lang.String%29[`HttpServletRequest.login(String,String)`] method to authenticate the user with the current `AuthenticationManager`.\nFor example, the following would attempt to authenticate with a username of `user` and a password of `password`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\ntry {\nhttpServletRequest.login(\"user\",\"password\");\n} catch(ServletException ex) {\n// fail to authenticate\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ntry {\n    httpServletRequest.login(\"user\", \"password\")\n} catch (ex: ServletException) {\n    // fail to authenticate\n}\n----\n======\n\n[NOTE]\n====\nYou need not catch the `ServletException` if you want Spring Security to process the failed authentication attempt.\n====\n\n[[servletapi-logout]]\n=== HttpServletRequest.logout()\nYou can use the https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout%28%29[`HttpServletRequest.logout()`] method to log out the current user.\n\nTypically, this means that the `SecurityContextHolder` is cleared out, the `HttpSession` is invalidated, any \"`Remember Me`\" authentication is cleaned up, and so on.\nHowever, the configured `LogoutHandler` implementations vary, depending on your Spring Security configuration.\nNote that, after `HttpServletRequest.logout()` has been invoked, you are still in charge of writing out a response.\nTypically, this would involve a redirect to the welcome page.\n\n[[servletapi-start-runnable]]\n=== AsyncContext.start(Runnable)\nThe https://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html#start%28java.lang.Runnable%29[`AsyncContext.start(Runnable)`] method ensures your credentials are propagated to the new `Thread`.\nBy using Spring Security's concurrency support, Spring Security overrides `AsyncContext.start(Runnable)` to ensure that the current `SecurityContext` is used when processing the Runnable.\nThe following example outputs the current user's Authentication:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nfinal AsyncContext async = httpServletRequest.startAsync();\nasync.start(new Runnable() {\n\tpublic void run() {\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\ttry {\n\t\t\tfinal HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse();\n\t\t\tasyncResponse.setStatus(HttpServletResponse.SC_OK);\n\t\t\tasyncResponse.getWriter().write(String.valueOf(authentication));\n\t\t\tasync.complete();\n\t\t} catch(Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval async: AsyncContext = httpServletRequest.startAsync()\nasync.start {\n    val authentication: Authentication = SecurityContextHolder.getContext().authentication\n    try {\n        val asyncResponse = async.response as HttpServletResponse\n        asyncResponse.status = HttpServletResponse.SC_OK\n        asyncResponse.writer.write(String.valueOf(authentication))\n        async.complete()\n    } catch (ex: Exception) {\n        throw RuntimeException(ex)\n    }\n}\n----\n======\n\n[[servletapi-async]]\n=== Async Servlet Support\nIf you use Java-based configuration, you are ready to go.\nIf you use XML configuration, a few updates are necessary.\nThe first step is to ensure that you have updated your `web.xml` file to use at least the 3.0 schema:\n\n[source,xml]\n----\n<web-app xmlns=\"http://java.sun.com/xml/ns/javaee\"\nxmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\nxsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\"\nversion=\"3.0\">\n\n</web-app>\n----\n\nNext, you need to ensure that your `springSecurityFilterChain` is set up for processing asynchronous requests:\n\n[source,xml]\n----\n<filter>\n<filter-name>springSecurityFilterChain</filter-name>\n<filter-class>\n\torg.springframework.web.filter.DelegatingFilterProxy\n</filter-class>\n<async-supported>true</async-supported>\n</filter>\n<filter-mapping>\n<filter-name>springSecurityFilterChain</filter-name>\n<url-pattern>/*</url-pattern>\n<dispatcher>REQUEST</dispatcher>\n<dispatcher>ASYNC</dispatcher>\n</filter-mapping>\n----\n\nNow Spring Security ensures that your `SecurityContext` is propagated on asynchronous requests, too.\n\nSo how does it work? If you are not really interested, feel free to skip the remainder of this section\nMost of this is built into the Servlet specification, but there is a little bit of tweaking that Spring Security does to ensure things work properly with asynchronous requests.\nPrior to Spring Security 3.2, the `SecurityContext` from the `SecurityContextHolder` was automatically saved as soon as the `HttpServletResponse` was committed.\nThis can cause issues in an asynchronous environment.\nConsider the following example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttpServletRequest.startAsync();\nnew Thread(\"AsyncThread\") {\n\t@Override\n\tpublic void run() {\n\t\ttry {\n\t\t\t// Do work\n\t\t\tTimeUnit.SECONDS.sleep(1);\n\n\t\t\t// Write to and commit the httpServletResponse\n\t\t\thttpServletResponse.getOutputStream().flush();\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t}\n}.start();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttpServletRequest.startAsync()\nobject : Thread(\"AsyncThread\") {\n    override fun run() {\n        try {\n            // Do work\n            TimeUnit.SECONDS.sleep(1)\n\n            // Write to and commit the httpServletResponse\n            httpServletResponse.outputStream.flush()\n        } catch (ex: java.lang.Exception) {\n            ex.printStackTrace()\n        }\n    }\n}.start()\n----\n======\n\nThe issue is that this `Thread` is not known to Spring Security, so the `SecurityContext` is not propagated to it.\nThis means that, when we commit the `HttpServletResponse`, there is no `SecurityContext`.\nWhen Spring Security automatically saved the `SecurityContext` on committing the `HttpServletResponse`, it would lose a logged in user.\n\nSince version 3.2, Spring Security is smart enough to no longer automatically save the `SecurityContext` on committing the `HttpServletResponse` as soon as `HttpServletRequest.startAsync()` is invoked.\n\n[[servletapi-31]]\n== Servlet 3.1+ Integration\nThe following section describes the Servlet 3.1 methods that Spring Security integrates with.\n\n[[servletapi-change-session-id]]\n=== HttpServletRequest#changeSessionId()\nhttps://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#changeSessionId()[HttpServletRequest.changeSessionId()] is the default method for protecting against xref:servlet/authentication/session-management.adoc#ns-session-fixation[Session Fixation] attacks in Servlet 3.1 and higher.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/integrations/websocket.adoc",
    "content": "[[websocket]]\n= WebSocket Security\n\nSpring Security 4 added support for securing {spring-framework-reference-url}web/websocket.html[Spring's WebSocket support].\nThis section describes how to use Spring Security's WebSocket support.\n\n.Direct JSR-356 Support\n****\nSpring Security does not provide direct JSR-356 support, because doing so would provide little value.\nAdditionally, JSR-356 does not provide a way to intercept messages, so security would be invasive.\n\nSpring Framework provides support for using STOMP — a simple, messaging protocol originally created for use in scripting languages with frames inspired by HTTP. STOMP is widely supported and well suited for use over WebSocket and over the web. For more details, see {spring-framework-reference-url}web/websocket/stomp.html[here].\n****\n\n[[websocket-authentication]]\n== WebSocket Authentication\n\nWebSockets reuse the same authentication information that is found in the HTTP request when the WebSocket connection was made.\nThis means that the `Principal` on the `HttpServletRequest` will be handed off to WebSockets.\nIf you are using Spring Security, the `Principal` on the `HttpServletRequest` is overridden automatically.\n\nMore concretely, to ensure a user has authenticated to your WebSocket application, all that is necessary is to ensure that you setup Spring Security to authenticate your HTTP based web application.\n\n[[websocket-authorization]]\n== WebSocket Authorization\n\nSpring Security 4.0 has introduced authorization support for WebSockets through the Spring Messaging abstraction.\n\nIn Spring Security 5.8, this support has been refreshed to use the `AuthorizationManager` API.\n\nTo configure authorization using Java Configuration, simply include the `@EnableWebSocketSecurity` annotation and publish an `AuthorizationManager<Message<?>>` bean or in xref:servlet/appendix/namespace/websocket.adoc#nsa-websocket-security[XML] use the `use-authorization-manager` attribute.\nOne way to do this is by using the `AuthorizationManagerMessageMatcherRegistry` to specify endpoint patterns like so:\n\ninclude-code::./WebSocketSecurityConfig[tag=snippet,indent=0]\n\n<1> Any inbound CONNECT message requires a valid CSRF token to enforce the <<websocket-sameorigin,Same Origin Policy>>.\n<2> The `SecurityContextHolder` is populated with the user within the `simpUser` header attribute for any inbound request.\n<3> Our messages require the proper authorization. Specifically, any inbound message that starts with `/user/` will require `ROLE_USER`. You can find additional details on authorization in <<websocket-authorization>>\n\n[[custom-authorization]]\n=== Custom Authorization\n\nWhen using `AuthorizationManager`, customization is quite simple.\nFor example, you can publish an `AuthorizationManager` that requires that all messages have a role of `\"USER\"` using `AuthorityAuthorizationManager`, as seen below:\n\ninclude-code::./WebSocketSecurityConfig[tag=snippet,indent=0]\n\nThere are several ways to further match messages, as can be seen in a more advanced example below:\n\ninclude-code::./AdvancedWebSocketSecurityConfig[tag=snippet,indent=0]\n\nThis will ensure that:\n\n<1> Any message without a destination (i.e. anything other than Message type of `MESSAGE` or `SUBSCRIBE`) will require the user to be authenticated\n<2> Anyone can subscribe to `/user/queue/errors`\n<3> Any message that has a destination starting with `\"/app/\"` will be required the user to have the role `ROLE_USER`\n<4> Any message that starts with `\"/user/\"` or `\"/topic/friends/\"` that is of type SUBSCRIBE will require `ROLE_USER`\n<5> Any other message of type `MESSAGE` or `SUBSCRIBE` is rejected. Due to 6 we do not need this step, but it illustrates how one can match on specific message types.\n<6> Any other Message is rejected. This is a good idea to ensure that you do not miss any messages.\n\n[[migrating-spel-expressions]]\n=== Migrating SpEL Expressions\n\nIf you are migrating from an older version of Spring Security, your destination matchers may include SpEL expressions.\nIt's recommended that these be changed to using concrete implementations of `AuthorizationManager` since this is independently testable.\n\nHowever, to ease migration, you can use\n`org.springframework.security.messaging.access.expression.MessageExpressionAuthorizationManager`.\n\nAnd specify an instance for each matcher that you cannot yet migrate:\n\ninclude-code::./WebSocketSecurityConfig[tag=snippet,indent=0]\n\n[[websocket-authorization-notes]]\n=== WebSocket Authorization Notes\n\nTo properly secure your application, you need to understand Spring's WebSocket support.\n\n[[websocket-authorization-notes-messagetypes]]\n==== WebSocket Authorization on Message Types\n\nYou need to understand the distinction between `SUBSCRIBE` and `MESSAGE` types of messages and how they work within Spring.\n\nConsider a chat application:\n\n* The system can send a notification `MESSAGE` to all users through a destination of `/topic/system/notifications`.\n* Clients can receive notifications by `SUBSCRIBE` to the `/topic/system/notifications`.\n\nWhile we want clients to be able to `SUBSCRIBE` to `/topic/system/notifications`, we do not want to enable them to send a `MESSAGE` to that destination.\nIf we allowed sending a `MESSAGE` to `/topic/system/notifications`, clients could send a message directly to that endpoint and impersonate the system.\n\nIn general, it is common for applications to deny any `MESSAGE` sent to a destination that starts with the {spring-framework-reference-url}web/websocket/stomp.html[broker prefix] (`/topic/` or `/queue/`).\n\n[[websocket-authorization-notes-destinations]]\n==== WebSocket Authorization on Destinations\n\nYou should also understand how destinations are transformed.\n\nConsider a chat application:\n\n* Users can send messages to a specific user by sending a message to the `/app/chat` destination.\n* The application sees the message, ensures that the `from` attribute is specified as the current user (we cannot trust the client).\n* The application then sends the message to the recipient by using `SimpMessageSendingOperations.convertAndSendToUser(\"toUser\", \"/queue/messages\", message)`.\n* The message gets turned into the destination of `/queue/user/messages-<sessionid>`.\n\nWith this chat application, we want to let our client to listen `/user/queue`, which is transformed into `/queue/user/messages-<sessionid>`.\nHowever, we do not want the client to be able to listen to `/queue/*`, because that would let the client see messages for every user.\n\nIn general, it is common for applications to deny any `SUBSCRIBE` sent to a message that starts with the {spring-framework-reference-url}web/websocket/stomp.html[broker prefix] (`/topic/` or `/queue/`).\nWe may provide exceptions to account for things like\n//FIXME: Like what?\n\n[[websocket-authorization-notes-outbound]]\n=== Outbound Messages\n\nThe Spring Framework reference documentation contains a section titled {spring-framework-reference-url}web/websocket/stomp/message-flow.html[\"`Flow of Messages`\"] that describes how messages flow through the system.\nNote that Spring Security secures only the `clientInboundChannel`.\nSpring Security does not attempt to secure the `clientOutboundChannel`.\n\nThe most important reason for this is performance.\nFor every message that goes in, typically many more go out.\nInstead of securing the outbound messages, we encourage securing the subscription to the endpoints.\n\n[[websocket-sameorigin]]\n== Enforcing Same Origin Policy\n\nNote that the browser does not enforce the https://en.wikipedia.org/wiki/Same-origin_policy[Same Origin Policy] for WebSocket connections.\nThis is an extremely important consideration.\n\n[[websocket-sameorigin-why]]\n=== Why Same Origin?\n\nConsider the following scenario.\nA user visits `bank.com` and authenticates to their account.\nThe same user opens another tab in their browser and visits `evil.com`.\nThe Same Origin Policy ensures that `evil.com` cannot read data from or write data to `bank.com`.\n\nWith WebSockets, the Same Origin Policy does not apply.\nIn fact, unless `bank.com` explicitly forbids it, `evil.com` can read and write data on behalf of the user.\nThis means that anything the user can do over the webSocket (such as transferring money), `evil.com` can do on that user's behalf.\n\nSince SockJS tries to emulate WebSockets, it also bypasses the Same Origin Policy.\nThis means that developers need to explicitly protect their applications from external domains when they use SockJS.\n\n[[websocket-sameorigin-spring]]\n=== Spring WebSocket Allowed Origin\n\nFortunately, since Spring 4.1.5 Spring's WebSocket and SockJS support restricts access to the {spring-framework-reference-url}web/websocket/server.html#websocket-server-allowed-origins[current domain].\nSpring Security adds an additional layer of protection to provide https://en.wikipedia.org/wiki/Defence_in_depth_(non-military)#Information_security[defense in depth].\n\n[[websocket-sameorigin-csrf]]\n=== Adding CSRF to Stomp Headers\n\nBy default, Spring Security requires the xref:features/exploits/csrf.adoc#csrf[CSRF token]  in any `CONNECT` message type.\nThis ensures that only a site that has access to the CSRF token can connect.\nSince only the *same origin* can access the CSRF token, external domains are not allowed to make a connection.\n\nTypically we need to include the CSRF token in an HTTP header or an HTTP parameter.\nHowever, SockJS does not allow for these options.\nInstead, we must include the token in the Stomp headers.\n\nApplications can xref:servlet/exploits/csrf.adoc#csrf-integration[obtain a CSRF token] by accessing the request attribute named `_csrf`.\nFor example, the following allows accessing the `CsrfToken` in a JSP:\n\n[source,javascript]\n----\nvar headerName = \"${_csrf.headerName}\";\nvar token = \"${_csrf.token}\";\n----\n\nIf you use static HTML, you can expose the `CsrfToken` on a REST endpoint.\nFor example, the following would expose the `CsrfToken` on the `/csrf` URL:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class CsrfController {\n\n    @RequestMapping(\"/csrf\")\n    public CsrfToken csrf(CsrfToken token) {\n        return token;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@RestController\nclass CsrfController {\n\n    @RequestMapping(\"/csrf\")\n    fun csrf(token: CsrfToken): CsrfToken {\n        return token\n    }\n}\n----\n======\n\nThe JavaScript can make a REST call to the endpoint and use the response to populate the `headerName` and the token.\n\nWe can now include the token in our Stomp client:\n\n[source,javascript]\n----\n...\nvar headers = {};\nheaders[headerName] = token;\nstompClient.connect(headers, function(frame) {\n  ...\n\n})\n----\n\n[[websocket-sameorigin-disable]]\n=== Disable CSRF within WebSockets\nNOTE: At this point, CSRF is not configurable when using `@EnableWebSocketSecurity`, though this will likely be added in a future release.\n\nTo disable CSRF, instead of using `@EnableWebSocketSecurity`, you can use XML support or add the Spring Security components yourself, like so:\n\ninclude-code::./WebSocketSecurityConfig[tag=snippet,indent=0]\n\n[[websocket-expression-handler]]\n=== Custom Expression Handler\n\nAt times, there may be value in customizing how the `access` expressions are handled defined in your `intercept-message` XML elements.\nTo do this, you can create a class of type `SecurityExpressionHandler<MessageAuthorizationContext<?>>` and refer to it in your XML definition like so:\n\n[source,xml]\n----\n<websocket-message-broker use-authorization-manager=\"true\">\n    <expression-handler ref=\"myRef\"/>\n    ...\n</websocket-message-broker>\n\n<b:bean ref=\"myRef\" class=\"org.springframework.security.messaging.access.expression.MessageAuthorizationContextSecurityExpressionHandler\"/>\n----\n\nIf you are migrating from a legacy usage of `websocket-message-broker` that implements a `SecurityExpressionHandler<Message<?>>`, you can:\n 1. Additionally implement the `createEvaluationContext(Supplier, Message)` method and then\n 2. Wrap that value in a `MessageAuthorizationContextSecurityExpressionHandler` like so:\n\n[source,xml]\n----\n<websocket-message-broker use-authorization-manager=\"true\">\n    <expression-handler ref=\"myRef\"/>\n    ...\n</websocket-message-broker>\n\n<b:bean ref=\"myRef\" class=\"org.springframework.security.messaging.access.expression.MessageAuthorizationContextSecurityExpressionHandler\">\n    <b:constructor-arg>\n        <b:bean class=\"org.example.MyLegacyExpressionHandler\"/>\n    </b:constructor-arg>\n</b:bean>\n----\n\n[[websocket-sockjs]]\n== Working with SockJS\n\n{spring-framework-reference-url}web/websocket/fallback.html[SockJS] provides fallback transports to support older browsers.\nWhen using the fallback options, we need to relax a few security constraints to allow SockJS to work with Spring Security.\n\n[[websocket-sockjs-sameorigin]]\n=== SockJS & frame-options\n\nSockJS may use a https://github.com/sockjs/sockjs-client/tree/v0.3.4[transport that leverages an iframe].\nBy default, Spring Security xref:features/exploits/headers.adoc#headers-frame-options[denies] the site from being framed to prevent clickjacking attacks.\nTo allow SockJS frame-based transports to work, we need to configure Spring Security to let the same origin frame the content.\n\nYou can customize `X-Frame-Options` with the xref:servlet/appendix/namespace/http.adoc#nsa-frame-options[frame-options] element.\nFor example, the following instructs Spring Security to use `X-Frame-Options: SAMEORIGIN`, which allows iframes within the same domain:\n\n[source,xml]\n----\n<http>\n    <!-- ... -->\n\n    <headers>\n        <frame-options\n          policy=\"SAMEORIGIN\" />\n    </headers>\n</http>\n----\n\nSimilarly, you can customize frame options to use the same origin within Java Configuration by using the following:\n\ninclude-code::./WebSecurityConfig[tag=snippet,indent=0]\n\n[[websocket-sockjs-csrf]]\n=== SockJS & Relaxing CSRF\n\nSockJS uses a POST on the CONNECT messages for any HTTP-based transport.\nTypically, we need to include the CSRF token in an HTTP header or an HTTP parameter.\nHowever, SockJS does not allow for these options.\nInstead, we must include the token in the Stomp headers as described in <<websocket-sameorigin-csrf>>.\n\nIt also means that we need to relax our CSRF protection with the web layer.\nSpecifically, we want to disable CSRF protection for our connect URLs.\nWe do NOT want to disable CSRF protection for every URL.\nOtherwise, our site is vulnerable to CSRF attacks.\n\nWe can easily achieve this by providing a CSRF `RequestMatcher`.\nOur Java configuration makes this easy.\nFor example, if our stomp endpoint is `/chat`, we can disable CSRF protection only for URLs that start with `/chat/` by using the following configuration:\n\ninclude-code::./WebSecurityConfig[tag=snippet,indent=0]\n\nIf we use XML-based configuration, we can use the xref:servlet/appendix/namespace/http.adoc#nsa-csrf-request-matcher-ref[csrf@request-matcher-ref].\n\ninclude-code::./WebSocketSecurityConfig[tag=snippet,indent=0]\n\n[[legacy-websocket-configuration]]\n== Legacy WebSocket Configuration\n\n`AbstractSecurityWebSocketMessageBrokerConfigurer` and `MessageSecurityMetadataSourceRegistry` are removed as of Spring Security 7.\nPlease see {site-url}/5.8/migration/servlet/authorization.html#_use_authorizationmanager_for_message_security[the 5.8 migration guide] for guidance.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/authorization-server/configuration-model.adoc",
    "content": "[[oauth2AuthorizationServer-configuration-model]]\n= Configuration Model\n\n[[oauth2AuthorizationServer-default-configuration]]\n== Default configuration\n\n`OAuth2AuthorizationServerConfiguration` is a `@Configuration` that provides the minimal default configuration for an OAuth2 authorization server.\n\n`OAuth2AuthorizationServerConfiguration` uses xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-customizing-the-configuration[`OAuth2AuthorizationServerConfigurer`] to apply the default configuration and registers a `SecurityFilterChain` `@Bean` composed of all the infrastructure components supporting an OAuth2 authorization server.\n\nThe OAuth2 authorization server `SecurityFilterChain` `@Bean` is configured with the following default protocol endpoints:\n\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-endpoint[OAuth2 Authorization endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-endpoint[OAuth2 Token endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-introspection-endpoint[OAuth2 Token Introspection endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-revocation-endpoint[OAuth2 Token Revocation endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-server-metadata-endpoint[OAuth2 Authorization Server Metadata endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-jwk-set-endpoint[JWK Set endpoint]\n\n[NOTE]\nThe JWK Set endpoint is configured *only* if a `JWKSource<SecurityContext>` `@Bean` is registered.\n\n[NOTE]\n====\nThe following protocol endpoints are disabled by default:\n\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-authorization-endpoint[OAuth2 Device Authorization Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-verification-endpoint[OAuth2 Device Verification Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-client-registration-endpoint[OAuth2 Client Registration endpoint]\n====\n\nThe following example shows how to use `OAuth2AuthorizationServerConfiguration` to apply the minimal default configuration:\n\n[source,java]\n----\n@Configuration\n@Import(OAuth2AuthorizationServerConfiguration.class)\npublic class AuthorizationServerConfig {\n\n\t@Bean\n\tpublic RegisteredClientRepository registeredClientRepository() {\n\t\tList<RegisteredClient> registrations = ...\n\t\treturn new InMemoryRegisteredClientRepository(registrations);\n\t}\n\n\t@Bean\n\tpublic JWKSource<SecurityContext> jwkSource() {\n\t\tRSAKey rsaKey = ...\n\t\tJWKSet jwkSet = new JWKSet(rsaKey);\n\t\treturn (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);\n\t}\n\n}\n----\n\n[IMPORTANT]\nThe https://datatracker.ietf.org/doc/html/rfc6749#section-4.1[authorization_code grant] requires the resource owner to be authenticated. Therefore, a user authentication mechanism *must* be configured in addition to the default OAuth2 security configuration.\n\nhttps://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect 1.0] is disabled in the default configuration. The following example shows how to enable OpenID Connect 1.0 by initializing the `OidcConfigurer`:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.oidc(Customizer.withDefaults())\t// Initialize `OidcConfigurer`\n\t\t);\n\treturn http.build();\n}\n----\n\nIn addition to the default protocol endpoints, the OAuth2 authorization server `SecurityFilterChain` `@Bean` is configured with the following OpenID Connect 1.0 protocol endpoints:\n\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-provider-configuration-endpoint[OpenID Connect 1.0 Provider Configuration endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-logout-endpoint[OpenID Connect 1.0 Logout endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-user-info-endpoint[OpenID Connect 1.0 UserInfo endpoint]\n\n[NOTE]\nThe xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration endpoint] is disabled by default.\n\n[TIP]\n`OAuth2AuthorizationServerConfiguration.jwtDecoder(JWKSource<SecurityContext>)` is a convenience (`static`) utility method that can be used to register a `JwtDecoder` `@Bean`, which is *REQUIRED* for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-user-info-endpoint[OpenID Connect 1.0 UserInfo endpoint] and the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration endpoint].\n\nThe following example shows how to register a `JwtDecoder` `@Bean`:\n\n[source,java]\n----\n@Bean\npublic JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n}\n----\n\nThe main intent of `OAuth2AuthorizationServerConfiguration` is to provide a convenient method to apply the minimal default configuration for an OAuth2 authorization server. However, in most cases, customizing the configuration will be required.\n\n[[oauth2AuthorizationServer-customizing-the-configuration]]\n== Customizing the configuration\n\n`OAuth2AuthorizationServerConfigurer` provides the ability to fully customize the security configuration for an OAuth2 authorization server.\nIt lets you specify the core components to use - for example, xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client-repository[`RegisteredClientRepository`],  xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-authorization-service[`OAuth2AuthorizationService`], xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-generator[`OAuth2TokenGenerator`], and others.\nFurthermore, it lets you customize the request processing logic for the protocol endpoints – for example, xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-endpoint[authorization endpoint], xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-authorization-endpoint[device authorization endpoint], xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-verification-endpoint[device verification endpoint], xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-endpoint[token endpoint], xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-introspection-endpoint[token introspection endpoint], and others.\n\n`OAuth2AuthorizationServerConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.registeredClientRepository(registeredClientRepository)\t<1>\n\t\t\t\t.authorizationService(authorizationService)\t<2>\n\t\t\t\t.authorizationConsentService(authorizationConsentService)\t<3>\n\t\t\t\t.authorizationServerSettings(authorizationServerSettings)\t<4>\n\t\t\t\t.tokenGenerator(tokenGenerator)\t<5>\n\t\t\t\t.clientAuthentication(clientAuthentication -> { })\t<6>\n\t\t\t\t.authorizationEndpoint(authorizationEndpoint -> { })\t<7>\n\t\t\t\t.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint -> { })  <8>\n\t\t\t\t.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { })\t<9>\n\t\t\t\t.deviceVerificationEndpoint(deviceVerificationEndpoint -> { })\t<10>\n\t\t\t\t.tokenEndpoint(tokenEndpoint -> { })\t<11>\n\t\t\t\t.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint -> { })\t<12>\n\t\t\t\t.tokenRevocationEndpoint(tokenRevocationEndpoint -> { })\t<13>\n\t\t\t\t.clientRegistrationEndpoint(clientRegistrationEndpoint -> { })  <14>\n\t\t\t\t.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint -> { })\t<15>\n\t\t\t\t.oidc(oidc -> oidc\n\t\t\t\t\t.providerConfigurationEndpoint(providerConfigurationEndpoint -> { })\t<16>\n\t\t\t\t\t.logoutEndpoint(logoutEndpoint -> { })\t<17>\n\t\t\t\t\t.userInfoEndpoint(userInfoEndpoint -> { })\t<18>\n\t\t\t\t\t.clientRegistrationEndpoint(clientRegistrationEndpoint -> { })\t<19>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `registeredClientRepository()`: The xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client-repository[`RegisteredClientRepository`] (*REQUIRED*) for managing new and existing clients.\n<2> `authorizationService()`: The xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-authorization-service[`OAuth2AuthorizationService`] for managing new and existing authorizations.\n<3> `authorizationConsentService()`: The xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-authorization-consent-service[`OAuth2AuthorizationConsentService`] for managing new and existing authorization consents.\n<4> `authorizationServerSettings()`: The xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-configuring-authorization-server-settings[`AuthorizationServerSettings`] (*REQUIRED*) for customizing configuration settings for the OAuth2 authorization server.\n<5> `tokenGenerator()`: The xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-generator[`OAuth2TokenGenerator`] for generating tokens supported by the OAuth2 authorization server.\n<6> `clientAuthentication()`: The configurer for xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-configuring-client-authentication[OAuth2 Client Authentication].\n<7> `authorizationEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-endpoint[OAuth2 Authorization endpoint].\n<8> `pushedAuthorizationRequestEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-pushed-authorization-request-endpoint[OAuth2 Pushed Authorization Request endpoint].\n<9> `deviceAuthorizationEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-authorization-endpoint[OAuth2 Device Authorization endpoint].\n<10> `deviceVerificationEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-verification-endpoint[OAuth2 Device Verification endpoint].\n<11> `tokenEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-endpoint[OAuth2 Token endpoint].\n<12> `tokenIntrospectionEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-introspection-endpoint[OAuth2 Token Introspection endpoint].\n<13> `tokenRevocationEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-revocation-endpoint[OAuth2 Token Revocation endpoint].\n<14> `clientRegistrationEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-client-registration-endpoint[OAuth2 Client Registration endpoint].\n<15> `authorizationServerMetadataEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-server-metadata-endpoint[OAuth2 Authorization Server Metadata endpoint].\n<16> `providerConfigurationEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-provider-configuration-endpoint[OpenID Connect 1.0 Provider Configuration endpoint].\n<17> `logoutEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-logout-endpoint[OpenID Connect 1.0 Logout endpoint].\n<18> `userInfoEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-user-info-endpoint[OpenID Connect 1.0 UserInfo endpoint].\n<19> `clientRegistrationEndpoint()`: The configurer for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration endpoint].\n\n[[oauth2AuthorizationServer-configuring-authorization-server-settings]]\n== Configuring Authorization Server Settings\n\n`AuthorizationServerSettings` contains the configuration settings for the OAuth2 authorization server.\nIt specifies the `URI` for the protocol endpoints as well as the https://datatracker.ietf.org/doc/html/rfc8414#section-2[issuer identifier].\nThe default `URI` for the protocol endpoints are as follows:\n\n[source,java]\n----\npublic final class AuthorizationServerSettings extends AbstractSettings {\n\n\t...\n\n\tpublic static Builder builder() {\n\t\treturn new Builder()\n\t\t\t.authorizationEndpoint(\"/oauth2/authorize\")\n\t\t\t.pushedAuthorizationRequestEndpoint(\"/oauth2/par\")\n\t\t\t.deviceAuthorizationEndpoint(\"/oauth2/device_authorization\")\n\t\t\t.deviceVerificationEndpoint(\"/oauth2/device_verification\")\n\t\t\t.tokenEndpoint(\"/oauth2/token\")\n\t\t\t.tokenIntrospectionEndpoint(\"/oauth2/introspect\")\n\t\t\t.tokenRevocationEndpoint(\"/oauth2/revoke\")\n\t\t\t.clientRegistrationEndpoint(\"/oauth2/register\")\n\t\t\t.jwkSetEndpoint(\"/oauth2/jwks\")\n\t\t\t.oidcLogoutEndpoint(\"/connect/logout\")\n\t\t\t.oidcUserInfoEndpoint(\"/userinfo\")\n\t\t\t.oidcClientRegistrationEndpoint(\"/connect/register\");\n\t}\n\n\t...\n\n}\n----\n\n[NOTE]\n`AuthorizationServerSettings` is a *REQUIRED* component.\n\n[TIP]\nxref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-default-configuration[`@Import(OAuth2AuthorizationServerConfiguration.class)`] automatically registers an `AuthorizationServerSettings` `@Bean`, if not already provided.\n\nThe following example shows how to customize the configuration settings and register an `AuthorizationServerSettings` `@Bean`:\n\n[source,java]\n----\n@Bean\npublic AuthorizationServerSettings authorizationServerSettings() {\n\treturn AuthorizationServerSettings.builder()\n\t\t.issuer(\"https://example.com\")\n\t\t.authorizationEndpoint(\"/oauth2/v1/authorize\")\n\t\t.pushedAuthorizationRequestEndpoint(\"/oauth2/v1/par\")\n\t\t.deviceAuthorizationEndpoint(\"/oauth2/v1/device_authorization\")\n\t\t.deviceVerificationEndpoint(\"/oauth2/v1/device_verification\")\n\t\t.tokenEndpoint(\"/oauth2/v1/token\")\n\t\t.tokenIntrospectionEndpoint(\"/oauth2/v1/introspect\")\n\t\t.tokenRevocationEndpoint(\"/oauth2/v1/revoke\")\n\t\t.clientRegistrationEndpoint(\"/oauth2/v1/register\")\n\t\t.jwkSetEndpoint(\"/oauth2/v1/jwks\")\n\t\t.oidcLogoutEndpoint(\"/connect/v1/logout\")\n\t\t.oidcUserInfoEndpoint(\"/connect/v1/userinfo\")\n\t\t.oidcClientRegistrationEndpoint(\"/connect/v1/register\")\n\t\t.build();\n}\n----\n\nThe `AuthorizationServerContext` is a context object that holds information of the Authorization Server runtime environment.\nIt provides access to the `AuthorizationServerSettings` and the \"`current`\" issuer identifier.\n\n[NOTE]\nIf the issuer identifier is not configured in `AuthorizationServerSettings.builder().issuer(String)`, it is resolved from the current request.\n\n[NOTE]\nThe `AuthorizationServerContext` is accessible through the `AuthorizationServerContextHolder`, which associates it with the current request thread by using a `ThreadLocal`.\n\n[[oauth2AuthorizationServer-configuring-client-authentication]]\n== Configuring Client Authentication\n\n`OAuth2ClientAuthenticationConfigurer` provides the ability to customize https://datatracker.ietf.org/doc/html/rfc6749#section-2.3[OAuth2 client authentication].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for client authentication requests.\n\n`OAuth2ClientAuthenticationConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.clientAuthentication(clientAuthentication ->\n\t\t\t\t\tclientAuthentication\n\t\t\t\t\t\t.authenticationConverter(authenticationConverter)\t<1>\n\t\t\t\t\t\t.authenticationConverters(authenticationConvertersConsumer)\t<2>\n\t\t\t\t\t\t.authenticationProvider(authenticationProvider)\t<3>\n\t\t\t\t\t\t.authenticationProviders(authenticationProvidersConsumer)\t<4>\n\t\t\t\t\t\t.authenticationSuccessHandler(authenticationSuccessHandler)\t<5>\n\t\t\t\t\t\t.errorResponseHandler(errorResponseHandler)\t<6>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `authenticationConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract client credentials from `HttpServletRequest` to an instance of `OAuth2ClientAuthenticationToken`.\n<2> `authenticationConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2ClientAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `authenticationSuccessHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling a successful client authentication and associating the `OAuth2ClientAuthenticationToken` to the `SecurityContext`.\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling a failed client authentication and returning the https://datatracker.ietf.org/doc/html/rfc6749#section-5.2[`OAuth2Error` response].\n\n`OAuth2ClientAuthenticationConfigurer` configures the `OAuth2ClientAuthenticationFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2ClientAuthenticationFilter` is the `Filter` that processes client authentication requests.\n\nBy default, client authentication is required for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-endpoint[OAuth2 Token endpoint], the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-introspection-endpoint[OAuth2 Token Introspection endpoint], and the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-revocation-endpoint[OAuth2 Token Revocation endpoint].\nThe supported client authentication methods are `client_secret_basic`, `client_secret_post`, `private_key_jwt`, `client_secret_jwt`, `tls_client_auth`, `self_signed_tls_client_auth`, and `none` (public clients).\n\n`OAuth2ClientAuthenticationFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- A `DelegatingAuthenticationConverter` composed of  `JwtClientAssertionAuthenticationConverter`, `X509ClientCertificateAuthenticationConverter`, `ClientSecretBasicAuthenticationConverter`, `ClientSecretPostAuthenticationConverter`, and `PublicClientAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `JwtClientAssertionAuthenticationProvider`, `X509ClientCertificateAuthenticationProvider`, `ClientSecretAuthenticationProvider`, and `PublicClientAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that associates the \"`authenticated`\" `OAuth2ClientAuthenticationToken` (current `Authentication`) to the `SecurityContext`.\n* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthenticationException` to return the OAuth2 error response.\n\n[[oauth2AuthorizationServer-customizing-jwt-client-assertion-validation]]\n=== Customizing Jwt Client Assertion Validation\n\n`JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY` is the default factory that provides an `OAuth2TokenValidator<Jwt>` for the specified `RegisteredClient` and is used for validating the `iss`, `sub`, `aud`, `exp` and `nbf` claims of the `Jwt` client assertion.\n\n`JwtClientAssertionDecoderFactory` provides the ability to override the default `Jwt` client assertion validation by supplying a custom factory of type `Function<RegisteredClient, OAuth2TokenValidator<Jwt>>` to `setJwtValidatorFactory()`.\n\n[NOTE]\n`JwtClientAssertionDecoderFactory` is the default `JwtDecoderFactory` used by `JwtClientAssertionAuthenticationProvider` that provides a `JwtDecoder` for the specified `RegisteredClient` and is used for authenticating a `Jwt` Bearer Token during OAuth2 client authentication.\n\nA common use case for customizing `JwtClientAssertionDecoderFactory` is to validate additional claims in the `Jwt` client assertion.\n\nThe following example shows how to configure `JwtClientAssertionAuthenticationProvider` with a customized `JwtClientAssertionDecoderFactory` that validates an additional claim in the `Jwt` client assertion:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.clientAuthentication(clientAuthentication ->\n\t\t\t\t\tclientAuthentication\n\t\t\t\t\t\t.authenticationProviders(configureJwtClientAssertionValidator())\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n\nprivate Consumer<List<AuthenticationProvider>> configureJwtClientAssertionValidator() {\n\treturn (authenticationProviders) ->\n\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\tif (authenticationProvider instanceof JwtClientAssertionAuthenticationProvider) {\n\t\t\t\t// Customize JwtClientAssertionDecoderFactory\n\t\t\t\tJwtClientAssertionDecoderFactory jwtDecoderFactory = new JwtClientAssertionDecoderFactory();\n\t\t\t\tFunction<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = (registeredClient) ->\n\t\t\t\t\tnew DelegatingOAuth2TokenValidator<>(\n\t\t\t\t\t\t// Use default validators\n\t\t\t\t\t\tJwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY.apply(registeredClient),\n\t\t\t\t\t\t// Add custom validator\n\t\t\t\t\t\tnew JwtClaimValidator<>(\"claim\", \"value\"::equals));\n\t\t\t\tjwtDecoderFactory.setJwtValidatorFactory(jwtValidatorFactory);\n\n\t\t\t\t((JwtClientAssertionAuthenticationProvider) authenticationProvider)\n\t\t\t\t\t.setJwtDecoderFactory(jwtDecoderFactory);\n\t\t\t}\n\t\t});\n}\n----\n\n[[oauth2AuthorizationServer-customizing-mutual-tls-client-authentication]]\n=== Customizing Mutual-TLS Client Authentication\n\n`X509ClientCertificateAuthenticationProvider` is used for authenticating the client `X509Certificate` chain received when `ClientAuthenticationMethod.TLS_CLIENT_AUTH` or `ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH` method is used during OAuth2 client authentication.\nIt is also composed with a _\"Certificate Verifier\"_, which is used to verify the contents of the client `X509Certificate` after the TLS handshake has successfully completed.\n\n[[oauth2AuthorizationServer-customizing-mutual-tls-client-authentication-pki-mutual-tls-method]]\n==== PKI Mutual-TLS Method\n\nFor the PKI Mutual-TLS (`ClientAuthenticationMethod.TLS_CLIENT_AUTH`) method, the default implementation of the certificate verifier verifies the subject distinguished name of the client `X509Certificate` against the setting `RegisteredClient.getClientSettings.getX509CertificateSubjectDN()`.\n\nIf you need to verify another attribute of the client `X509Certificate`, for example, a Subject Alternative Name (SAN) entry, the following example shows how to configure `X509ClientCertificateAuthenticationProvider` with a custom implementation of a certificate verifier:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.clientAuthentication(clientAuthentication ->\n\t\t\t\t\tclientAuthentication\n\t\t\t\t\t\t.authenticationProviders(configureX509ClientCertificateVerifier())\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n\nprivate Consumer<List<AuthenticationProvider>> configureX509ClientCertificateVerifier() {\n\treturn (authenticationProviders) ->\n\t\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\t\tif (authenticationProvider instanceof X509ClientCertificateAuthenticationProvider) {\n\t\t\t\t\tConsumer<OAuth2ClientAuthenticationContext> certificateVerifier = (clientAuthenticationContext) -> {\n\t\t\t\t\t\tOAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();\n\t\t\t\t\t\tRegisteredClient registeredClient = clientAuthenticationContext.getRegisteredClient();\n\t\t\t\t\t\tX509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();\n\t\t\t\t\t\tX509Certificate clientCertificate = clientCertificateChain[0];\n\n\t\t\t\t\t\t// TODO Verify Subject Alternative Name (SAN) entry\n\n\t\t\t\t\t};\n\n\t\t\t\t\t((X509ClientCertificateAuthenticationProvider) authenticationProvider)\n\t\t\t\t\t\t\t.setCertificateVerifier(certificateVerifier);\n\t\t\t\t}\n\t\t\t});\n}\n----\n\n[[oauth2AuthorizationServer-customizing-mutual-tls-client-authentication-self-signed-certificate-mutual-tls-method]]\n==== Self-Signed Certificate Mutual-TLS Method\n\nFor the Self-Signed Certificate Mutual-TLS (`ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH`) method, the default implementation of the certificate verifier will retrieve the client's JSON Web Key Set using the setting `RegisteredClient.getClientSettings.getJwkSetUrl()` and expect to find a match against the client `X509Certificate` received during the TLS handshake.\n\n[NOTE]\nThe `RegisteredClient.getClientSettings.getJwkSetUrl()` setting is used to retrieve the client's certificates via a JSON Web Key (JWK) Set.\nA certificate is represented with the `x5c` parameter of an individual JWK within the set.\n\n[[oauth2AuthorizationServer-customizing-mutual-tls-client-authentication-client-certificate-bound-access-tokens]]\n==== Client Certificate-Bound Access Tokens\n\nWhen Mutual-TLS client authentication is used at the token endpoint, the authorization server is able to bind the issued access token to the client's `X509Certificate`.\nThe binding is accomplished by computing the SHA-256 thumbprint of the client's `X509Certificate` and associating the thumbprint with the access token.\nFor example, a JWT access token would include a `x5t#S256` claim, containing the `X509Certificate` thumbprint, within the top-level `cnf` (confirmation method) claim.\n\nBinding the access token to the client's `X509Certificate` provides the ability to implement a proof-of-possession mechanism during protected resource access.\nFor example, the protected resource would obtain the client's `X509Certificate` used during Mutual-TLS authentication and then verify that the certificate thumbprint matches the `x5t#S256` claim associated with the access token.\n\nThe following example shows how to enable certificate-bound access tokens for a client:\n\n[source,java]\n----\nRegisteredClient mtlsClient = RegisteredClient.withId(UUID.randomUUID().toString())\n\t\t.clientId(\"mtls-client\")\n\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)\n\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t.scope(\"scope-a\")\n\t\t.clientSettings(\n\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t.x509CertificateSubjectDN(\"CN=mtls-client,OU=Spring Samples,O=Spring,C=US\")\n\t\t\t\t\t\t.build()\n\t\t)\n\t\t.tokenSettings(\n\t\t\t\tTokenSettings.builder()\n\t\t\t\t\t\t.x509CertificateBoundAccessTokens(true)\n\t\t\t\t\t\t.build()\n\t\t)\n\t\t.build();\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/authorization-server/core-model-components.adoc",
    "content": "[[oauth2AuthorizationServer-core-model-components]]\n= Core Model / Components\n\n[[oauth2AuthorizationServer-registered-client]]\n== RegisteredClient\n\nA `RegisteredClient` is a representation of a client that is https://datatracker.ietf.org/doc/html/rfc6749#section-2[registered] with the authorization server.\nA client must be registered with the authorization server before it can initiate an authorization grant flow, such as `authorization_code` or `client_credentials`.\n\nDuring client registration, the client is assigned a unique https://datatracker.ietf.org/doc/html/rfc6749#section-2.2[client identifier], (optionally) a client secret (depending on https://datatracker.ietf.org/doc/html/rfc6749#section-2.1[client type]), and metadata associated with its unique client identifier.\nThe client's metadata can range from human-facing display strings (such as client name) to items specific to a protocol flow (such as the list of valid redirect URIs).\n\n[TIP]\nThe corresponding client registration model in Spring Security's OAuth2 Client support is xref:servlet/oauth2/client/core.adoc#oauth2Client-client-registration[ClientRegistration].\n\nThe primary purpose of a client is to request access to protected resources.\nThe client first requests an access token by authenticating with the authorization server and presenting the authorization grant.\nThe authorization server authenticates the client and authorization grant, and, if they are valid, issues an access token.\nThe client can now request the protected resource from the resource server by presenting the access token.\n\nThe following example shows how to configure a `RegisteredClient` that is allowed to perform the https://datatracker.ietf.org/doc/html/rfc6749#section-4.1[authorization_code grant] flow to request an access token:\n\n[source,java]\n----\nRegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())\n\t.clientId(\"client-a\")\n\t.clientSecret(\"{noop}secret\")   <1>\n\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t.redirectUri(\"http://127.0.0.1:8080/authorized\")\n\t.scope(\"scope-a\")\n\t.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())\n\t.build();\n----\n<1> `\\{noop\\}` represents the `PasswordEncoder` id for Spring Security's xref:features/authentication/password-storage.adoc#authentication-password-storage-dpe[NoOpPasswordEncoder].\n\nThe corresponding configuration in Spring Security's xref:servlet/oauth2/client/index.adoc[OAuth2 Client support] is:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          client-a:\n            provider: spring\n            client-id: client-a\n            client-secret: secret\n            authorization-grant-type: authorization_code\n            redirect-uri: \"http://127.0.0.1:8080/authorized\"\n            scope: scope-a\n        provider:\n          spring:\n            issuer-uri: http://localhost:9000\n----\n\nA `RegisteredClient` has metadata (attributes) associated with its unique Client Identifier and is defined as follows:\n\n[source,java]\n----\npublic class RegisteredClient implements Serializable {\n\tprivate String id;  <1>\n\tprivate String clientId;    <2>\n\tprivate Instant clientIdIssuedAt;   <3>\n\tprivate String clientSecret;    <4>\n\tprivate Instant clientSecretExpiresAt;  <5>\n\tprivate String clientName;  <6>\n\tprivate Set<ClientAuthenticationMethod> clientAuthenticationMethods;    <7>\n\tprivate Set<AuthorizationGrantType> authorizationGrantTypes;    <8>\n\tprivate Set<String> redirectUris;   <9>\n\tprivate Set<String> postLogoutRedirectUris; <10>\n\tprivate Set<String> scopes; <11>\n\tprivate ClientSettings clientSettings;  <12>\n\tprivate TokenSettings tokenSettings;    <13>\n\n\t...\n\n}\n----\n<1> `id`: The ID that uniquely identifies the `RegisteredClient`.\n<2> `clientId`: The client identifier.\n<3> `clientIdIssuedAt`: The time at which the client identifier was issued.\n<4> `clientSecret`: The client's secret. The value should be encoded using Spring Security's xref:features/authentication/password-storage.adoc#authentication-password-storage-dpe[PasswordEncoder].\n<5> `clientSecretExpiresAt`: The time at which the client secret expires.\n<6> `clientName`: A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the client name in the consent page.\n<7> `clientAuthenticationMethods`: The authentication method(s) that the client may use. The supported values are `client_secret_basic`, `client_secret_post`, https://datatracker.ietf.org/doc/html/rfc7523[`private_key_jwt`], `client_secret_jwt`, and `none` https://datatracker.ietf.org/doc/html/rfc7636[(public clients)].\n<8> `authorizationGrantTypes`: The https://datatracker.ietf.org/doc/html/rfc6749#section-1.3[authorization grant type(s)] that the client can use. The supported values are `authorization_code`, `client_credentials`, `refresh_token`, `urn:ietf:params:oauth:grant-type:device_code`, and `urn:ietf:params:oauth:grant-type:token-exchange`.\n<9> `redirectUris`: The registered https://datatracker.ietf.org/doc/html/rfc6749#section-3.1.2[redirect URI(s)] that the client may use in redirect-based flows – for example, `authorization_code` grant.\n<10> `postLogoutRedirectUris`: The post logout redirect URI(s) that the client may use for logout.\n<11> `scopes`: The scope(s) that the client is allowed to request.\n<12> `clientSettings`: The custom settings for the client – for example, require https://datatracker.ietf.org/doc/html/rfc7636[PKCE], require authorization consent, and others.\n<13> `tokenSettings`: The custom settings for the OAuth2 tokens issued to the client – for example, access/refresh token time-to-live, reuse refresh tokens, and others.\n\n[[oauth2AuthorizationServer-client-settings]]\n== ClientSettings\n\n`ClientSettings` contains the configuration settings associated to a `RegisteredClient`.\n\n`ClientSettings` provides the following accessors:\n\n[source,java]\n----\npublic final class ClientSettings extends AbstractSettings {\n\n    public boolean isRequireProofKey() ...  <1>\n\n    public boolean isRequireAuthorizationConsent() ...  <2>\n\n    public String getJwkSetUrl() ...    <3>\n\n    public JwsAlgorithm getTokenEndpointAuthenticationSigningAlgorithm() ...    <4>\n\n    public String getX509CertificateSubjectDN() ... <5>\n\n\t...\n\n}\n----\n<1> `isRequireProofKey()`: If `true`, the client is required to provide a proof key challenge and verifier when performing the Authorization Code Grant flow (PKCE). The default is `true`.\n<2> `isRequireAuthorizationConsent()`: If `true`, authorization consent is required when the client requests access. The default is `false`.\n<3> `getJwkSetUrl()`: The `URL` for the client's JSON Web Key Set. Used for `private_key_jwt`, `self_signed_tls_client_auth` and `client_secret_jwt` client authentication methods.\n<4> `getTokenEndpointAuthenticationSigningAlgorithm()`: The `JwsAlgorithm` that must be used for signing the JWT used to authenticate the client at the Token Endpoint for `private_key_jwt` and `client_secret_jwt` authentication methods.\n<5> `getX509CertificateSubjectDN()`: The expected subject distinguished name associated to the client `X509Certificate` received during client authentication when using the `tls_client_auth` method.\n\n[NOTE]\nhttps://datatracker.ietf.org/doc/html/rfc7636[Proof Key for Code Exchange (PKCE)] is enabled by default for all clients using the Authorization Code grant. To disable PKCE, set `requireProofKey` to `false`.\n\n[[oauth2AuthorizationServer-registered-client-repository]]\n== RegisteredClientRepository\n\nThe `RegisteredClientRepository` is the central component where new clients can be registered and existing clients can be queried.\nIt is used by other components when following a specific protocol flow, such as client authentication, authorization grant processing, token introspection, dynamic client registration, and others.\n\nThe provided implementations of `RegisteredClientRepository` are `InMemoryRegisteredClientRepository` and `JdbcRegisteredClientRepository`.\nThe `InMemoryRegisteredClientRepository` implementation stores `RegisteredClient` instances in-memory and is recommended *ONLY* to be used during development and testing.\n`JdbcRegisteredClientRepository` is a JDBC implementation that persists `RegisteredClient` instances by using `JdbcOperations`.\n\n[NOTE]\nThe `RegisteredClientRepository` is a *REQUIRED* component.\n\nThe following example shows how to register a `RegisteredClientRepository` `@Bean`:\n\n[source,java]\n----\n@Bean\npublic RegisteredClientRepository registeredClientRepository() {\n\tList<RegisteredClient> registrations = ...\n\treturn new InMemoryRegisteredClientRepository(registrations);\n}\n----\n\nAlternatively, you can configure the `RegisteredClientRepository` through the xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-customizing-the-configuration[`OAuth2AuthorizationServerConfigurer`]:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.registeredClientRepository(registeredClientRepository)\n\t\t)\n\t    ...\n\n\treturn http.build();\n}\n----\n\n[NOTE]\nThe `OAuth2AuthorizationServerConfigurer` is useful when applying multiple configuration options simultaneously.\n\n[[oauth2AuthorizationServer-oauth2-authorization]]\n== OAuth2Authorization\n\nAn `OAuth2Authorization` is a representation of an OAuth2 authorization, which holds state related to the authorization granted to a xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client[client], by the resource owner or itself in the case of the `client_credentials` authorization grant type.\n\n[TIP]\nThe corresponding authorization model in Spring Security's OAuth2 Client support is xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-client[OAuth2AuthorizedClient].\n\nAfter the successful completion of an authorization grant flow, an `OAuth2Authorization` is created and associates an {security-api-url}/org/springframework/security/oauth2/core/OAuth2AccessToken.html[`OAuth2AccessToken`], an (optional) {security-api-url}/org/springframework/security/oauth2/core/OAuth2RefreshToken.html[`OAuth2RefreshToken`], and additional state specific to the executed authorization grant type.\n\nThe {security-api-url}/org/springframework/security/oauth2/core/OAuth2Token.html[`OAuth2Token`] instances associated with an `OAuth2Authorization` vary, depending on the authorization grant type.\n\nFor the OAuth2 https://datatracker.ietf.org/doc/html/rfc6749#section-4.1[authorization_code grant], an `OAuth2AuthorizationCode`, an `OAuth2AccessToken`, and an (optional) `OAuth2RefreshToken` are associated.\n\nFor the OpenID Connect 1.0 https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[authorization_code grant], an `OAuth2AuthorizationCode`, an {security-api-url}/org/springframework/security/oauth2/core/oidc/OidcIdToken.html[`OidcIdToken`], an `OAuth2AccessToken`, and an (optional) `OAuth2RefreshToken` are associated.\n\nFor the OAuth2 https://datatracker.ietf.org/doc/html/rfc6749#section-4.4[client_credentials grant], only an `OAuth2AccessToken` is associated.\n\n`OAuth2Authorization` and its attributes are defined as follows:\n\n[source,java]\n----\npublic class OAuth2Authorization implements Serializable {\n\tprivate String id;  <1>\n\tprivate String registeredClientId;  <2>\n\tprivate String principalName;   <3>\n\tprivate AuthorizationGrantType authorizationGrantType;  <4>\n\tprivate Set<String> authorizedScopes;   <5>\n\tprivate Map<Class<? extends OAuth2Token>, Token<?>> tokens; <6>\n\tprivate Map<String, Object> attributes; <7>\n\n\t...\n\n}\n----\n<1> `id`: The ID that uniquely identifies the `OAuth2Authorization`.\n<2> `registeredClientId`: The ID that uniquely identifies the xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client[RegisteredClient].\n<3> `principalName`: The principal name of the resource owner (or client).\n<4> `authorizationGrantType`: The `AuthorizationGrantType` used.\n<5> `authorizedScopes`: The `Set` of scope(s) authorized for the client.\n<6> `tokens`: The `OAuth2Token` instances (and associated metadata) specific to the executed authorization grant type.\n<7> `attributes`: The additional attributes specific to the executed authorization grant type – for example, the authenticated `Principal`, `OAuth2AuthorizationRequest`, and others.\n\n`OAuth2Authorization` and its associated `OAuth2Token` instances have a set lifespan.\nA newly issued `OAuth2Token` is active and becomes inactive when it either expires or is invalidated (revoked).\nThe `OAuth2Authorization` is (implicitly) inactive when all associated `OAuth2Token` instances are inactive.\nEach `OAuth2Token` is held in an `OAuth2Authorization.Token`, which provides accessors for `isExpired()`, `isInvalidated()`, and `isActive()`.\n\n`OAuth2Authorization.Token` also provides `getClaims()`, which returns the claims (if any) associated with the `OAuth2Token`.\n\n[[oauth2AuthorizationServer-oauth2-authorization-service]]\n== OAuth2AuthorizationService\n\nThe `OAuth2AuthorizationService` is the central component where new authorizations are stored and existing authorizations are queried.\nIt is used by other components when following a specific protocol flow – for example, client authentication, authorization grant processing, token introspection, token revocation, dynamic client registration, and others.\n\nThe provided implementations of `OAuth2AuthorizationService` are `InMemoryOAuth2AuthorizationService` and `JdbcOAuth2AuthorizationService`.\nThe `InMemoryOAuth2AuthorizationService` implementation stores `OAuth2Authorization` instances in-memory and is recommended *ONLY* to be used during development and testing.\n`JdbcOAuth2AuthorizationService` is a JDBC implementation that persists `OAuth2Authorization` instances by using `JdbcOperations`.\n\n[NOTE]\nThe `OAuth2AuthorizationService` is an *OPTIONAL* component and defaults to `InMemoryOAuth2AuthorizationService`.\n\nThe following example shows how to register an `OAuth2AuthorizationService` `@Bean`:\n\n[source,java]\n----\n@Bean\npublic OAuth2AuthorizationService authorizationService() {\n\treturn new InMemoryOAuth2AuthorizationService();\n}\n----\n\nAlternatively, you can configure the `OAuth2AuthorizationService` through the xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-customizing-the-configuration[`OAuth2AuthorizationServerConfigurer`]:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.authorizationService(authorizationService)\n\t\t)\n\t    ...\n\n\treturn http.build();\n}\n----\n\n[NOTE]\nThe `OAuth2AuthorizationServerConfigurer` is useful when applying multiple configuration options simultaneously.\n\n[[oauth2AuthorizationServer-oauth2-authorization-consent]]\n== OAuth2AuthorizationConsent\n\nAn `OAuth2AuthorizationConsent` is a representation of an authorization \"consent\" (decision) from an https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1[OAuth2 authorization request flow] – for example, the `authorization_code` grant, which holds the authorities granted to a xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client[client] by the resource owner.\n\nWhen authorizing access to a client, the resource owner may grant only a subset of the authorities requested by the client.\nThe typical use case is the `authorization_code` grant flow, in which the client requests scope(s) and the resource owner grants (or denies) access to the requested scope(s).\n\nAfter the completion of an OAuth2 authorization request flow, an `OAuth2AuthorizationConsent` is created (or updated) and associates the granted authorities with the client and resource owner.\n\n`OAuth2AuthorizationConsent` and its attributes are defined as follows:\n\n[source,java]\n----\npublic final class OAuth2AuthorizationConsent implements Serializable {\n\tprivate final String registeredClientId;    <1>\n\tprivate final String principalName; <2>\n\tprivate final Set<GrantedAuthority> authorities;    <3>\n\n\t...\n\n}\n----\n<1> `registeredClientId`: The ID that uniquely identifies the xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client[RegisteredClient].\n<2> `principalName`: The principal name of the resource owner.\n<3> `authorities`: The authorities granted to the client by the resource owner. An authority can represent a scope, a claim, a permission, a role, and others.\n\n[[oauth2AuthorizationServer-oauth2-authorization-consent-service]]\n== OAuth2AuthorizationConsentService\n\nThe `OAuth2AuthorizationConsentService` is the central component where new authorization consents are stored and existing authorization consents are queried.\nIt is primarily used by components that implement an OAuth2 authorization request flow – for example, the `authorization_code` grant.\n\nThe provided implementations of `OAuth2AuthorizationConsentService` are `InMemoryOAuth2AuthorizationConsentService` and `JdbcOAuth2AuthorizationConsentService`.\nThe `InMemoryOAuth2AuthorizationConsentService` implementation stores `OAuth2AuthorizationConsent` instances in-memory and is recommended *ONLY* for development and testing.\n`JdbcOAuth2AuthorizationConsentService` is a JDBC implementation that persists `OAuth2AuthorizationConsent` instances by using `JdbcOperations`.\n\n[NOTE]\nThe `OAuth2AuthorizationConsentService` is an *OPTIONAL* component and defaults to `InMemoryOAuth2AuthorizationConsentService`.\n\nThe following example shows how to register an `OAuth2AuthorizationConsentService` `@Bean`:\n\n[source,java]\n----\n@Bean\npublic OAuth2AuthorizationConsentService authorizationConsentService() {\n\treturn new InMemoryOAuth2AuthorizationConsentService();\n}\n----\n\nAlternatively, you can configure the `OAuth2AuthorizationConsentService` through the xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-customizing-the-configuration[`OAuth2AuthorizationServerConfigurer`]:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.authorizationConsentService(authorizationConsentService)\n\t\t)\n\t    ...\n\n\treturn http.build();\n}\n----\n\n[NOTE]\nThe `OAuth2AuthorizationServerConfigurer` is useful when applying multiple configuration options simultaneously.\n\n[[oauth2AuthorizationServer-oauth2-token-context]]\n== OAuth2TokenContext\n\nAn `OAuth2TokenContext` is a context object that holds information associated with an `OAuth2Token` and is used by an xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-generator[OAuth2TokenGenerator] and xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-customizer[OAuth2TokenCustomizer].\n\n`OAuth2TokenContext` provides the following accessors:\n\n[source,java]\n----\npublic interface OAuth2TokenContext extends Context {\n\n\tdefault RegisteredClient getRegisteredClient() ...  <1>\n\n\tdefault <T extends Authentication> T getPrincipal() ... <2>\n\n\tdefault AuthorizationServerContext getAuthorizationServerContext() ...    <3>\n\n\t@Nullable\n\tdefault OAuth2Authorization getAuthorization() ...  <4>\n\n\tdefault Set<String> getAuthorizedScopes() ...   <5>\n\n\tdefault OAuth2TokenType getTokenType() ...  <6>\n\n\tdefault AuthorizationGrantType getAuthorizationGrantType() ...  <7>\n\n\tdefault <T extends Authentication> T getAuthorizationGrant() ...    <8>\n\n\t...\n\n}\n----\n<1> `getRegisteredClient()`: The xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client[RegisteredClient] associated with the authorization grant.\n<2> `getPrincipal()`: The `Authentication` instance of the resource owner (or client).\n<3> `getAuthorizationServerContext()`: The xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-configuring-authorization-server-settings[`AuthorizationServerContext`] object that holds information of the Authorization Server runtime environment.\n<4> `getAuthorization()`: The xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-authorization[OAuth2Authorization] associated with the authorization grant.\n<5> `getAuthorizedScopes()`: The scope(s) authorized for the client.\n<6> `getTokenType()`: The `OAuth2TokenType` to generate. The supported values are `code`, `access_token`, `refresh_token`, and `id_token`.\n<7> `getAuthorizationGrantType()`: The `AuthorizationGrantType` associated with the authorization grant.\n<8> `getAuthorizationGrant()`: The `Authentication` instance used by the `AuthenticationProvider` that processes the authorization grant.\n\n[[oauth2AuthorizationServer-oauth2-token-generator]]\n== OAuth2TokenGenerator\n\nAn `OAuth2TokenGenerator` is responsible for generating an `OAuth2Token` from the information contained in the provided xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-context[OAuth2TokenContext].\n\nThe `OAuth2Token` generated primarily depends on the type of `OAuth2TokenType` specified in the `OAuth2TokenContext`.\n\nFor example, when the `value` for `OAuth2TokenType` is:\n\n* `code`, then `OAuth2AuthorizationCode` is generated.\n* `access_token`, then `OAuth2AccessToken` is generated.\n* `refresh_token`, then `OAuth2RefreshToken` is generated.\n* `id_token`, then `OidcIdToken` is generated.\n\nFurthermore, the format of the generated `OAuth2AccessToken` varies, depending on the `TokenSettings.getAccessTokenFormat()` configured for the xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client[RegisteredClient].\nIf the format is `OAuth2TokenFormat.SELF_CONTAINED` (the default), then a `Jwt` is generated.\nIf the format is `OAuth2TokenFormat.REFERENCE`, then an \"opaque\" token is generated.\n\nFinally, if the generated `OAuth2Token` has a set of claims and implements `ClaimAccessor`, the claims are made accessible from xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-authorization[OAuth2Authorization.Token.getClaims()].\n\nThe `OAuth2TokenGenerator` is primarily used by components that implement authorization grant processing – for example, `authorization_code`, `client_credentials`, and `refresh_token`.\n\nThe provided implementations are `OAuth2AccessTokenGenerator`, `OAuth2RefreshTokenGenerator`, and `JwtGenerator`.\nThe `OAuth2AccessTokenGenerator` generates an \"opaque\" (`OAuth2TokenFormat.REFERENCE`) access token, and the `JwtGenerator` generates a `Jwt` (`OAuth2TokenFormat.SELF_CONTAINED`).\n\n[NOTE]\nThe `OAuth2TokenGenerator` is an *OPTIONAL* component and defaults to a `DelegatingOAuth2TokenGenerator` composed of an `OAuth2AccessTokenGenerator` and `OAuth2RefreshTokenGenerator`.\n\n[NOTE]\nIf a `JwtEncoder` `@Bean` or `JWKSource<SecurityContext>` `@Bean` is registered, then a `JwtGenerator` is additionally composed in the `DelegatingOAuth2TokenGenerator`.\n\nThe `OAuth2TokenGenerator` provides great flexibility, as it can support any custom token format for `access_token` and `refresh_token`.\n\nThe following example shows how to register an `OAuth2TokenGenerator` `@Bean`:\n\n[source,java]\n----\n@Bean\npublic OAuth2TokenGenerator<?> tokenGenerator() {\n\tJwtEncoder jwtEncoder = ...\n\tJwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);\n\tOAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();\n\tOAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();\n\treturn new DelegatingOAuth2TokenGenerator(\n\t\t\tjwtGenerator, accessTokenGenerator, refreshTokenGenerator);\n}\n----\n\nAlternatively, you can configure the `OAuth2TokenGenerator` through the xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-customizing-the-configuration[`OAuth2AuthorizationServerConfigurer`]:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.tokenGenerator(tokenGenerator)\n\t\t)\n\t    ...\n\n\treturn http.build();\n}\n----\n\n[NOTE]\nThe `OAuth2AuthorizationServerConfigurer` is useful when applying multiple configuration options simultaneously.\n\n[[oauth2AuthorizationServer-oauth2-token-customizer]]\n== OAuth2TokenCustomizer\n\nAn `OAuth2TokenCustomizer` provides the ability to customize the attributes of an `OAuth2Token`, which are accessible in the provided xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-context[OAuth2TokenContext].\nIt is used by an xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-generator[OAuth2TokenGenerator] to let it customize the attributes of the `OAuth2Token` before it is generated.\n\nAn `OAuth2TokenCustomizer<OAuth2TokenClaimsContext>` declared with a generic type of `OAuth2TokenClaimsContext` (`implements OAuth2TokenContext`) provides the ability to customize the claims of an \"opaque\" `OAuth2AccessToken`.\n`OAuth2TokenClaimsContext.getClaims()` provides access to the `OAuth2TokenClaimsSet.Builder`, allowing the ability to add, replace, and remove claims.\n\nThe following example shows how to implement an `OAuth2TokenCustomizer<OAuth2TokenClaimsContext>` and configure it with an `OAuth2AccessTokenGenerator`:\n\n[source,java]\n----\n@Bean\npublic OAuth2TokenGenerator<?> tokenGenerator() {\n\tJwtEncoder jwtEncoder = ...\n\tJwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);\n\tOAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();\n\taccessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer());\n\tOAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();\n\treturn new DelegatingOAuth2TokenGenerator(\n\t\t\tjwtGenerator, accessTokenGenerator, refreshTokenGenerator);\n}\n\n@Bean\npublic OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer() {\n\treturn context -> {\n\t\tOAuth2TokenClaimsSet.Builder claims = context.getClaims();\n\t\t// Customize claims\n\n\t};\n}\n----\n\n[NOTE]\nIf the `OAuth2TokenGenerator` is not provided as a `@Bean` or is not configured through the `OAuth2AuthorizationServerConfigurer`, an `OAuth2TokenCustomizer<OAuth2TokenClaimsContext>` `@Bean` will automatically be configured with an `OAuth2AccessTokenGenerator`.\n\nAn `OAuth2TokenCustomizer<JwtEncodingContext>` declared with a generic type of `JwtEncodingContext` (`implements OAuth2TokenContext`) provides the ability to customize the headers and claims of a `Jwt`.\n`JwtEncodingContext.getJwsHeader()` provides access to the `JwsHeader.Builder`, allowing the ability to add, replace, and remove headers.\n`JwtEncodingContext.getClaims()` provides access to the `JwtClaimsSet.Builder`, allowing the ability to add, replace, and remove claims.\n\nThe following example shows how to implement an `OAuth2TokenCustomizer<JwtEncodingContext>` and configure it with a `JwtGenerator`:\n\n[source,java]\n----\n@Bean\npublic OAuth2TokenGenerator<?> tokenGenerator() {\n\tJwtEncoder jwtEncoder = ...\n\tJwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);\n\tjwtGenerator.setJwtCustomizer(jwtCustomizer());\n\tOAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();\n\tOAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();\n\treturn new DelegatingOAuth2TokenGenerator(\n\t\t\tjwtGenerator, accessTokenGenerator, refreshTokenGenerator);\n}\n\n@Bean\npublic OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {\n\treturn context -> {\n\t\tJwsHeader.Builder headers = context.getJwsHeader();\n\t\tJwtClaimsSet.Builder claims = context.getClaims();\n\t\tif (context.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) {\n\t\t\t// Customize headers/claims for access_token\n\n\t\t} else if (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) {\n\t\t\t// Customize headers/claims for id_token\n\n\t\t}\n\t};\n}\n----\n\n[NOTE]\nIf the `OAuth2TokenGenerator` is not provided as a `@Bean` or is not configured through the `OAuth2AuthorizationServerConfigurer`, an `OAuth2TokenCustomizer<JwtEncodingContext>` `@Bean` will automatically be configured with a `JwtGenerator`.\n\n[[oauth2AuthorizationServer-session-registry]]\n== SessionRegistry\n\nIf OpenID Connect 1.0 is enabled, a `SessionRegistry` instance is used to track authenticated sessions.\nThe `SessionRegistry` is used by the default implementation of `SessionAuthenticationStrategy` associated to the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-endpoint[OAuth2 Authorization Endpoint] for registering new authenticated sessions.\n\n[NOTE]\nIf a `SessionRegistry` `@Bean` is not registered, the default implementation `SessionRegistryImpl` will be used.\n\n[IMPORTANT]\nIf a `SessionRegistry` `@Bean` is registered and is an instance of `SessionRegistryImpl`, a `HttpSessionEventPublisher` `@Bean` *SHOULD* also be registered as it's responsible for notifying `SessionRegistryImpl` of session lifecycle events, for example, `SessionDestroyedEvent`, to provide the ability to remove the `SessionInformation` instance.\n\nWhen a logout is requested by an End-User, the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-logout-endpoint[OpenID Connect 1.0 Logout Endpoint] uses the `SessionRegistry` to lookup the `SessionInformation` associated to the authenticated End-User to perform the logout.\n\nIf Spring Security's xref:servlet/authentication/session-management.adoc#ns-concurrent-sessions[Concurrent Session Control] feature is being used, it is *RECOMMENDED* to register a `SessionRegistry` `@Bean` to ensure it's shared between Spring Security's Concurrent Session Control and Spring Security Authorization Server's Logout feature.\n\nThe following example shows how to register a `SessionRegistry` `@Bean` and `HttpSessionEventPublisher` `@Bean` (required by `SessionRegistryImpl`):\n\n[source,java]\n----\n@Bean\npublic SessionRegistry sessionRegistry() {\n\treturn new SessionRegistryImpl();\n}\n\n@Bean\npublic HttpSessionEventPublisher httpSessionEventPublisher() {\n\treturn new HttpSessionEventPublisher();\n}\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/authorization-server/getting-started.adoc",
    "content": "\n[[oauth2AuthorizationServer-getting-started]]\n= Getting Started\n\nIf you are just getting started with Spring Security Authorization Server, the following sections walk you through creating your first application.\n\n[[oauth2AuthorizationServer-system-requirements]]\n== System Requirements\n\nSpring Security Authorization Server requires a Java 17 or higher Runtime Environment.\n\n[[oauth2AuthorizationServer-installing-spring-security-authorization-server]]\n== Installing Spring Security Authorization Server\n\nThe easiest way to begin using Spring Security Authorization Server is by creating a https://spring.io/projects/spring-boot[Spring Boot]-based application.\nYou can use https://start.spring.io[start.spring.io] to generate a basic project or use the https://github.com/spring-projects/spring-authorization-server/tree/main/samples/default-authorizationserver[default authorization server sample] as a guide.\nThen add Spring Boot's starter for Spring Security Authorization Server as a dependency:\n\n[tabs]\n======\nMaven::\n+\n[[oauth2AuthorizationServer-spring-boot-maven-dependency]]\n[source,xml,role=\"primary\",subs=\"attributes,verbatim\"]\n----\n<dependency>\n    <groupId>org.springframework.boot</groupId>\n    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>\n</dependency>\n----\n\nGradle::\n+\n[[oauth2AuthorizationServer-spring-boot-gradle-dependency]]\n[source,gradle,role=\"secondary\",subs=\"attributes,verbatim\"]\n----\nimplementation \"org.springframework.boot:spring-boot-starter-oauth2-authorization-server\"\n----\n======\n\nTIP: See {spring-boot-reference-url}installing.html[Installing Spring Boot] for more information on using Spring Boot with Maven or Gradle.\n\nAlternatively, you can add Spring Security Authorization Server without Spring Boot using the following example:\n\n[tabs]\n======\nMaven::\n+\n[[oauth2AuthorizationServer-maven-dependency]]\n[source,xml,role=\"primary\",subs=\"attributes,verbatim\"]\n----\n<dependency>\n    <groupId>org.springframework.security</groupId>\n    <artifactId>spring-security-oauth2-authorization-server</artifactId>\n    <version>{spring-security-version}</version>\n</dependency>\n----\n\nGradle::\n+\n[[oauth2AuthorizationServer-gradle-dependency]]\n[source,gradle,role=\"secondary\",subs=\"attributes,verbatim\"]\n----\nimplementation \"org.springframework.security:spring-security-oauth2-authorization-server:{spring-security-version}\"\n----\n======\n\n[[oauth2AuthorizationServer-developing-your-first-application]]\n== Developing Your First Application\n\nTo get started, you need the minimum required components defined as a `@Bean`. When using the `spring-boot-starter-oauth2-authorization-server` dependency, define the following properties and Spring Boot will provide the necessary `@Bean` definitions for you:\n\n[[oauth2AuthorizationServer-application-yml]]\n.application.yml\n[source,yaml]\n----\nserver:\n  port: 9000\n\nlogging:\n  level:\n    org.springframework.security: trace\n\nspring:\n  security:\n    user:\n      name: user\n      password: password\n    oauth2:\n      authorizationserver:\n        client:\n          oidc-client:\n            registration:\n              client-id: \"oidc-client\"\n              client-secret: \"{noop}secret\"\n              client-authentication-methods:\n                - \"client_secret_basic\"\n              authorization-grant-types:\n                - \"authorization_code\"\n                - \"refresh_token\"\n              redirect-uris:\n                - \"http://127.0.0.1:8080/login/oauth2/code/oidc-client\"\n              post-logout-redirect-uris:\n                - \"http://127.0.0.1:8080/\"\n              scopes:\n                - \"openid\"\n                - \"profile\"\n            require-authorization-consent: true\n----\n\nIf you want to customize the default `HttpSecurity` configuration, you may override Spring Boot's auto-configuration with the following example:\n\n[[oauth2AuthorizationServer-minimal-sample-gettingstarted]]\n.SecurityConfig.java\n[source,java]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\tauthorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\t\tauthorizationServer\n\t\t\t\t\t.oidc(Customizer.withDefaults())\t// Enable OpenID Connect 1.0\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n}\n----\n\nTIP: Beyond the Getting Started experience, most users will want to customize the default configuration. The xref:servlet/oauth2/authorization-server/getting-started.adoc#oauth2AuthorizationServer-defining-required-components[next section] demonstrates providing all of the necessary beans yourself.\n\n[[oauth2AuthorizationServer-defining-required-components]]\n== Defining Required Components\n\nIf you want to customize the default configuration (regardless of whether you're using Spring Boot), you can define the minimum required components as a `@Bean` in a Spring `@Configuration`.\n\nThese components can be defined as follows:\n\n[[oauth2AuthorizationServer-sample-gettingstarted]]\n.SecurityConfig.java\n[source,java]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean // <1>\n\t@Order(1)\n\tpublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)\n\t\t\tthrows Exception {\n\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.oauth2AuthorizationServer((authorizationServer) -> {\n\t\t\t\thttp.securityMatcher(authorizationServer.getEndpointsMatcher());\n\t\t\t\tauthorizationServer\n\t\t\t\t\t.oidc(Customizer.withDefaults());\t// Enable OpenID Connect 1.0\n\t\t\t})\n\t\t\t.authorizeHttpRequests((authorize) ->\n\t\t\t\tauthorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t// Redirect to the login page when not authenticated from the\n\t\t\t// authorization endpoint\n\t\t\t.exceptionHandling((exceptions) -> exceptions\n\t\t\t\t.defaultAuthenticationEntryPointFor(\n\t\t\t\t\tnew LoginUrlAuthenticationEntryPoint(\"/login\"),\n\t\t\t\t\tnew MediaTypeRequestMatcher(MediaType.TEXT_HTML)\n\t\t\t\t)\n\t\t\t);\n\t\t// @formatter:on\n\n\t\treturn http.build();\n\t}\n\n\t@Bean // <2>\n\t@Order(2)\n\tpublic SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)\n\t\t\tthrows Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t// Form login handles the redirect to the login page from the\n\t\t\t// authorization server filter chain\n\t\t\t.formLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\n\t\treturn http.build();\n\t}\n\n\t@Bean // <3>\n\tpublic UserDetailsService userDetailsService() {\n\t\t// @formatter:off\n\t\tUserDetails userDetails = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\treturn new InMemoryUserDetailsManager(userDetails);\n\t}\n\n\t@Bean // <4>\n\tpublic RegisteredClientRepository registeredClientRepository() {\n\t\t// @formatter:off\n\t\tRegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())\n\t\t\t\t.clientId(\"oidc-client\")\n\t\t\t\t.clientSecret(\"{noop}secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t\t.redirectUri(\"http://127.0.0.1:8080/login/oauth2/code/oidc-client\")\n\t\t\t\t.postLogoutRedirectUri(\"http://127.0.0.1:8080/\")\n\t\t\t\t.scope(OidcScopes.OPENID)\n\t\t\t\t.scope(OidcScopes.PROFILE)\n\t\t\t\t.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\treturn new InMemoryRegisteredClientRepository(oidcClient);\n\t}\n\n\t@Bean // <5>\n\tpublic JWKSource<SecurityContext> jwkSource() {\n\t\tKeyPair keyPair = generateRsaKey();\n\t\tRSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();\n\t\tRSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();\n\t\t// @formatter:off\n\t\tRSAKey rsaKey = new RSAKey.Builder(publicKey)\n\t\t\t\t.privateKey(privateKey)\n\t\t\t\t.keyID(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJWKSet jwkSet = new JWKSet(rsaKey);\n\t\treturn new ImmutableJWKSet<>(jwkSet);\n\t}\n\n\tprivate static KeyPair generateRsaKey() { // <6>\n\t\tKeyPair keyPair;\n\t\ttry {\n\t\t\tKeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"RSA\");\n\t\t\tkeyPairGenerator.initialize(2048);\n\t\t\tkeyPair = keyPairGenerator.generateKeyPair();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t\treturn keyPair;\n\t}\n\n\t@Bean // <7>\n\tpublic JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {\n\t\treturn OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);\n\t}\n\n\t@Bean // <8>\n\tpublic AuthorizationServerSettings authorizationServerSettings() {\n\t\treturn AuthorizationServerSettings.builder().build();\n\t}\n\n}\n----\n\nThis is a minimal configuration for getting started quickly. To understand what each component is used for, see the following descriptions:\n\n<1> A Spring Security filter chain for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc[Protocol Endpoints].\n<2> A Spring Security filter chain for xref:servlet/authentication/index.adoc#servlet-authentication[authentication].\n<3> An instance of {security-api-url}/org/springframework/security/core/userdetails/UserDetailsService.html[`UserDetailsService`] for retrieving users to authenticate.\n<4> An instance of xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client-repository[`RegisteredClientRepository`] for managing clients.\n<5> An instance of `com.nimbusds.jose.jwk.source.JWKSource` for signing access tokens.\n<6> An instance of `java.security.KeyPair` with keys generated on startup used to create the `JWKSource` above.\n<7> An instance of {security-api-url}/org/springframework/security/oauth2/jwt/JwtDecoder.html[`JwtDecoder`] for decoding signed access tokens.\n<8> An instance of xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-configuring-authorization-server-settings[`AuthorizationServerSettings`] to configure Spring Security Authorization Server.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/authorization-server/index.adoc",
    "content": "[[oauth2AuthorizationServer]]\n= OAuth 2.1 Authorization Server\n:page-section-summary-toc: 1\n\nThe OAuth 2.1 Authorization Server features provide support for the Authorization Server role as defined in the https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-1.1[OAuth 2.1 Authorization Framework].\n\nThe Authorization Server features provide implementations of the https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07[OAuth 2.1] and https://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect 1.0] specifications and other related specifications.\nIt provides a secure, light-weight, and customizable foundation for building OpenID Connect 1.0 Identity Providers and OAuth 2.1 Authorization Server products.\n\n[[oauth2AuthorizationServer-use-cases]]\n== Use Cases\n\nThe following list provides some use cases for using Spring Security Authorization Server compared to using an open source or commercial OAuth2 or OpenID Connect 1.0 Provider product.\n\n* Provides full control of configuration and customization when advanced customization scenarios are required.\n* Preference for a light-weight authorization server compared to a commercial product that includes all the \"bells and whistles\".\n* Potential savings in software licensing and/or hosting costs.\n* Quick startup and ease of use during development using the familiar Spring programming model.\n\n[[oauth2AuthorizationServer-feature-list]]\n== Feature List\n\nSpring Security Authorization Server supports the following features:\n\n[cols=\"2a,4a,6a\"]\n|===\n|Category |Feature |Related specifications\n\n|xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-endpoint[Authorization Grant]\n|\n* Authorization Code\n** xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-endpoint[User Consent]\n* Client Credentials\n* Refresh Token\n* Device Code\n** xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-verification-endpoint[User Consent]\n* Token Exchange\n|\n* The OAuth 2.1 Authorization Framework (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07[draft])\n** https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-4.1[Authorization Code Grant]\n** https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-4.2[Client Credentials Grant]\n** https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-4.3[Refresh Token Grant]\n* OpenID Connect Core 1.0 (https://openid.net/specs/openid-connect-core-1_0.html[spec])\n** https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[Authorization Code Flow]\n* OAuth 2.0 Device Authorization Grant\n(https://tools.ietf.org/html/rfc8628[spec])\n** https://tools.ietf.org/html/rfc8628#section-3[Device Flow]\n* OAuth 2.0 Token Exchange (https://datatracker.ietf.org/doc/html/rfc8693[spec])\n** https://datatracker.ietf.org/doc/html/rfc8693#section-2[Token Exchange Flow]\n\n|xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-generator[Token Formats]\n|\n* Self-contained (JWT)\n* Reference (Opaque)\n|\n* JSON Web Token (JWT) (https://tools.ietf.org/html/rfc7519[RFC 7519])\n* JSON Web Signature (JWS) (https://tools.ietf.org/html/rfc7515[RFC 7515])\n\n|Token Types\n|\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-endpoint-dpop-bound-access-tokens[DPoP-bound Access Tokens]\n|\n* OAuth 2.0 Demonstrating Proof of Possession (DPoP) (https://datatracker.ietf.org/doc/html/rfc9449[RFC 9449])\n\n|xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-configuring-client-authentication[Client Authentication]\n|\n* `client_secret_basic`\n* `client_secret_post`\n* `client_secret_jwt`\n* `private_key_jwt`\n* `tls_client_auth`\n* `self_signed_tls_client_auth`\n* `none` (public clients)\n|\n* The OAuth 2.1 Authorization Framework (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-2.4[Client Authentication])\n* JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication (https://tools.ietf.org/html/rfc7523[RFC 7523])\n* OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens (https://datatracker.ietf.org/doc/html/rfc8705[RFC 8705])\n* Proof Key for Code Exchange by OAuth Public Clients (PKCE) (https://tools.ietf.org/html/rfc7636[RFC 7636])\n\n|xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc[Protocol Endpoints]\n|\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-endpoint[OAuth2 Authorization Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-pushed-authorization-request-endpoint[OAuth2 Pushed Authorization Request Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-authorization-endpoint[OAuth2 Device Authorization Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-device-verification-endpoint[OAuth2 Device Verification Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-endpoint[OAuth2 Token Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-introspection-endpoint[OAuth2 Token Introspection Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-token-revocation-endpoint[OAuth2 Token Revocation Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-client-registration-endpoint[OAuth2 Client Registration Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oauth2-authorization-server-metadata-endpoint[OAuth2 Authorization Server Metadata Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-jwk-set-endpoint[JWK Set Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-provider-configuration-endpoint[OpenID Connect 1.0 Provider Configuration Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-logout-endpoint[OpenID Connect 1.0 Logout Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-user-info-endpoint[OpenID Connect 1.0 UserInfo Endpoint]\n* xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc#oauth2AuthorizationServer-oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration Endpoint]\n|\n* The OAuth 2.1 Authorization Framework (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07[draft])\n** https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-3.1[Authorization Endpoint]\n** https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-3.2[Token Endpoint]\n* OAuth 2.0 Pushed Authorization Requests (https://datatracker.ietf.org/doc/html/rfc9126[RFC 9126])\n** https://datatracker.ietf.org/doc/html/rfc9126#section-2[Pushed Authorization Request Endpoint]\n* OAuth 2.0 Device Authorization Grant (https://tools.ietf.org/html/rfc8628[RFC 8628])\n** https://tools.ietf.org/html/rfc8628#section-3.1[Device Authorization Endpoint]\n** https://tools.ietf.org/html/rfc8628#section-3.3[Device Verification Endpoint]\n* OAuth 2.0 Token Introspection (https://tools.ietf.org/html/rfc7662[RFC 7662])\n* OAuth 2.0 Token Revocation (https://tools.ietf.org/html/rfc7009[RFC 7009])\n* OAuth 2.0 Dynamic Client Registration Protocol (https://datatracker.ietf.org/doc/html/rfc7591[RFC 7591])\n* OAuth 2.0 Authorization Server Metadata (https://tools.ietf.org/html/rfc8414[RFC 8414])\n* JSON Web Key (JWK) (https://tools.ietf.org/html/rfc7517[RFC 7517])\n* OpenID Connect Discovery 1.0 (https://openid.net/specs/openid-connect-discovery-1_0.html[spec])\n** https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Provider Configuration Endpoint]\n* OpenID Connect RP-Initiated Logout 1.0 (https://openid.net/specs/openid-connect-rpinitiated-1_0.html[spec])\n** https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout[Logout Endpoint]\n* OpenID Connect Core 1.0 (https://openid.net/specs/openid-connect-core-1_0.html[spec])\n** https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint]\n* OpenID Connect Dynamic Client Registration 1.0 (https://openid.net/specs/openid-connect-registration-1_0.html[spec])\n** https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration[Client Registration Endpoint]\n** https://openid.net/specs/openid-connect-registration-1_0.html#ClientConfigurationEndpoint[Client Configuration Endpoint]\n|===\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/authorization-server/protocol-endpoints.adoc",
    "content": "[[oauth2AuthorizationServer-protocol-endpoints]]\n= Protocol Endpoints\n\n[[oauth2AuthorizationServer-oauth2-authorization-endpoint]]\n== OAuth2 Authorization Endpoint\n\n`OAuth2AuthorizationEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc6749#section-3.1[OAuth2 Authorization endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1[OAuth2 authorization requests].\n\n`OAuth2AuthorizationEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.authorizationEndpoint(authorizationEndpoint ->\n\t\t\t\t\tauthorizationEndpoint\n        \t\t\t\t.authorizationRequestConverter(authorizationRequestConverter)   <1>\n                        .authorizationRequestConverters(authorizationRequestConvertersConsumer) <2>\n                        .authenticationProvider(authenticationProvider) <3>\n                        .authenticationProviders(authenticationProvidersConsumer)   <4>\n                        .authorizationResponseHandler(authorizationResponseHandler) <5>\n                        .errorResponseHandler(errorResponseHandler) <6>\n                        .consentPage(\"/oauth2/v1/authorize\")    <7>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `authorizationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1[OAuth2 authorization request] (or consent) from `HttpServletRequest` to an instance of `OAuth2AuthorizationCodeRequestAuthenticationToken` or `OAuth2AuthorizationConsentAuthenticationToken`.\n<2> `authorizationRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2AuthorizationCodeRequestAuthenticationToken` or `OAuth2AuthorizationConsentAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `authorizationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OAuth2AuthorizationCodeRequestAuthenticationToken` and returning the https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2[OAuth2AuthorizationResponse].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthorizationCodeRequestAuthenticationException` and returning the https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1[OAuth2Error response].\n<7> `consentPage()`: The `URI` of the custom consent page to redirect resource owners to if consent is required during the authorization request flow.\n\n`OAuth2AuthorizationEndpointConfigurer` configures the `OAuth2AuthorizationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2AuthorizationEndpointFilter` is the `Filter` that processes OAuth2 authorization requests (and consents).\n\n`OAuth2AuthorizationEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- A `DelegatingAuthenticationConverter` composed of `OAuth2AuthorizationCodeRequestAuthenticationConverter` and `OAuth2AuthorizationConsentAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2AuthorizationCodeRequestAuthenticationProvider` and `OAuth2AuthorizationConsentAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an \"`authenticated`\" `OAuth2AuthorizationCodeRequestAuthenticationToken` and returns the `OAuth2AuthorizationResponse`.\n* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthorizationCodeRequestAuthenticationException` and returns the `OAuth2Error` response.\n\n[[oauth2AuthorizationServer-oauth2-authorization-endpoint-customizing-authorization-request-validation]]\n=== Customizing Authorization Request Validation\n\n`OAuth2AuthorizationCodeRequestAuthenticationValidator` is the default validator used for validating specific OAuth2 authorization request parameters used in the Authorization Code Grant.\nThe default implementation validates the `redirect_uri` and `scope` parameters.\nIf validation fails, an `OAuth2AuthorizationCodeRequestAuthenticationException` is thrown.\n\n`OAuth2AuthorizationCodeRequestAuthenticationProvider` provides the ability to override the default authorization request validation by supplying a custom authentication validator of type `Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext>` to `setAuthenticationValidator()`.\n\n[TIP]\n`OAuth2AuthorizationCodeRequestAuthenticationContext` holds the `OAuth2AuthorizationCodeRequestAuthenticationToken`, which contains the OAuth2 authorization request parameters.\n\n[IMPORTANT]\nIf validation fails, the authentication validator *MUST* throw `OAuth2AuthorizationCodeRequestAuthenticationException`.\n\nA common use case during the development life cycle phase is to allow for `localhost` in the `redirect_uri` parameter.\n\nThe following example shows how to configure `OAuth2AuthorizationCodeRequestAuthenticationProvider` with a custom authentication validator that allows for `localhost` in the `redirect_uri` parameter:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.authorizationEndpoint(authorizationEndpoint ->\n\t\t\t\t\tauthorizationEndpoint\n                        .authenticationProviders(configureAuthenticationValidator())\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n\nprivate Consumer<List<AuthenticationProvider>> configureAuthenticationValidator() {\n\treturn (authenticationProviders) ->\n\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\tif (authenticationProvider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider) {\n\t\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator =\n\t\t\t\t\t// Override default redirect_uri validator\n\t\t\t\t\tnew CustomRedirectUriValidator()\n\t\t\t\t\t\t// Reuse default scope validator\n\t\t\t\t\t\t.andThen(OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_SCOPE_VALIDATOR);\n\n\t\t\t\t((OAuth2AuthorizationCodeRequestAuthenticationProvider) authenticationProvider)\n\t\t\t\t\t.setAuthenticationValidator(authenticationValidator);\n\t\t\t}\n\t\t});\n}\n\nstatic class CustomRedirectUriValidator implements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {\n\n\t@Override\n\tpublic void accept(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =\n\t\t\tauthenticationContext.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\t\tString requestedRedirectUri = authorizationCodeRequestAuthentication.getRedirectUri();\n\n\t\t// Use exact string matching when comparing client redirect URIs against pre-registered URIs\n\t\tif (!registeredClient.getRedirectUris().contains(requestedRedirectUri)) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);\n\t\t}\n\t}\n}\n----\n\n[[oauth2AuthorizationServer-oauth2-pushed-authorization-request-endpoint]]\n== OAuth2 Pushed Authorization Request Endpoint\n\n`OAuth2PushedAuthorizationRequestEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc9126#section-2[OAuth2 Pushed Authorization Request endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://datatracker.ietf.org/doc/html/rfc9126#section-2.1[OAuth2 Pushed Authorization requests].\n\n`OAuth2PushedAuthorizationRequestEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint ->\n\t\t\t\t\tpushedAuthorizationRequestEndpoint\n        \t\t\t\t.pushedAuthorizationRequestConverter(pushedAuthorizationRequestConverter)   <1>\n                        .pushedAuthorizationRequestConverters(pushedAuthorizationRequestConvertersConsumer) <2>\n                        .authenticationProvider(authenticationProvider) <3>\n                        .authenticationProviders(authenticationProvidersConsumer)   <4>\n                        .pushedAuthorizationResponseHandler(pushedAuthorizationResponseHandler) <5>\n                        .errorResponseHandler(errorResponseHandler) <6>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `pushedAuthorizationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://datatracker.ietf.org/doc/html/rfc9126#section-2.1[OAuth2 pushed authorization request] from `HttpServletRequest` to an instance of `OAuth2PushedAuthorizationRequestAuthenticationToken`.\n<2> `pushedAuthorizationRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2PushedAuthorizationRequestAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `pushedAuthorizationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OAuth2PushedAuthorizationRequestAuthenticationToken` and returning the https://datatracker.ietf.org/doc/html/rfc9126#section-2.2[OAuth2 pushed authorization response].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://datatracker.ietf.org/doc/html/rfc9126#section-2.3[OAuth2Error response].\n\n`OAuth2PushedAuthorizationRequestEndpointConfigurer` configures the `OAuth2PushedAuthorizationRequestEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2PushedAuthorizationRequestEndpointFilter` is the `Filter` that processes OAuth2 pushed authorization requests.\n\n`OAuth2PushedAuthorizationRequestEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- A `DelegatingAuthenticationConverter` composed of `OAuth2AuthorizationCodeRequestAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2PushedAuthorizationRequestAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an \"`authenticated`\" `OAuth2PushedAuthorizationRequestAuthenticationToken` and returns the OAuth2 pushed authorization response.\n* `*AuthenticationFailureHandler*` -- An `OAuth2ErrorAuthenticationFailureHandler`.\n\n[[oauth2AuthorizationServer-oauth2-pushed-authorization-request-endpoint-customizing-pushed-authorization-request-validation]]\n=== Customizing Pushed Authorization Request Validation\n\n`OAuth2AuthorizationCodeRequestAuthenticationValidator` is the default validator used for validating specific OAuth2 pushed authorization request parameters used in the Authorization Code Grant.\nThe default implementation validates the `redirect_uri` and `scope` parameters.\nIf validation fails, an `OAuth2AuthorizationCodeRequestAuthenticationException` is thrown.\n\n`OAuth2PushedAuthorizationRequestAuthenticationProvider` provides the ability to override the default pushed authorization request validation by supplying a custom authentication validator of type `Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext>` to `setAuthenticationValidator()`.\n\n[TIP]\n`OAuth2AuthorizationCodeRequestAuthenticationContext` holds the `OAuth2AuthorizationCodeRequestAuthenticationToken`, which contains the OAuth2 pushed authorization request parameters.\n\n[IMPORTANT]\nIf validation fails, the authentication validator *MUST* throw `OAuth2AuthorizationCodeRequestAuthenticationException`.\n\nA common use case during the development life cycle phase is to allow for `localhost` in the `redirect_uri` parameter.\n\nThe following example shows how to configure `OAuth2PushedAuthorizationRequestAuthenticationProvider` with a custom authentication validator that allows for `localhost` in the `redirect_uri` parameter:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint ->\n\t\t\t\t\tpushedAuthorizationRequestEndpoint\n                        .authenticationProviders(configureAuthenticationValidator())\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n\nprivate Consumer<List<AuthenticationProvider>> configureAuthenticationValidator() {\n\treturn (authenticationProviders) ->\n\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\tif (authenticationProvider instanceof OAuth2PushedAuthorizationRequestAuthenticationProvider) {\n\t\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator =\n\t\t\t\t\t// Override default redirect_uri validator\n\t\t\t\t\tnew CustomRedirectUriValidator()\n\t\t\t\t\t\t// Reuse default scope validator\n\t\t\t\t\t\t.andThen(OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_SCOPE_VALIDATOR);\n\n\t\t\t\t((OAuth2PushedAuthorizationRequestAuthenticationProvider) authenticationProvider)\n\t\t\t\t\t.setAuthenticationValidator(authenticationValidator);\n\t\t\t}\n\t\t});\n}\n\nstatic class CustomRedirectUriValidator implements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {\n\n\t@Override\n\tpublic void accept(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =\n\t\t\tauthenticationContext.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\t\tString requestedRedirectUri = authorizationCodeRequestAuthentication.getRedirectUri();\n\n\t\t// Use exact string matching when comparing client redirect URIs against pre-registered URIs\n\t\tif (!registeredClient.getRedirectUris().contains(requestedRedirectUri)) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);\n\t\t}\n\t}\n}\n----\n\n[[oauth2AuthorizationServer-oauth2-device-authorization-endpoint]]\n== OAuth2 Device Authorization Endpoint\n\n`OAuth2DeviceAuthorizationEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc8628#section-3.1[OAuth2 Device Authorization endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for OAuth2 device authorization requests.\n\n`OAuth2DeviceAuthorizationEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint ->\n                    deviceAuthorizationEndpoint\n                        .deviceAuthorizationRequestConverter(deviceAuthorizationRequestConverter)   <1>\n                        .deviceAuthorizationRequestConverters(deviceAuthorizationRequestConvertersConsumer) <2>\n                        .authenticationProvider(authenticationProvider) <3>\n                        .authenticationProviders(authenticationProvidersConsumer)   <4>\n                        .deviceAuthorizationResponseHandler(deviceAuthorizationResponseHandler) <5>\n                        .errorResponseHandler(errorResponseHandler) <6>\n                        .verificationUri(\"/oauth2/v1/device_verification\")  <7>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `deviceAuthorizationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://datatracker.ietf.org/doc/html/rfc8628#section-3.1[OAuth2 device authorization request] from `HttpServletRequest` to an instance of `OAuth2DeviceAuthorizationRequestAuthenticationToken`.\n<2> `deviceAuthorizationRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2DeviceAuthorizationRequestAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `deviceAuthorizationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OAuth2DeviceAuthorizationRequestAuthenticationToken` and returning the https://datatracker.ietf.org/doc/html/rfc8628#section-3.2[OAuth2DeviceAuthorizationResponse].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://datatracker.ietf.org/doc/html/rfc6749#section-5.2[OAuth2Error response].\n<7> `verificationUri()`: The `URI` of the custom end-user verification page to direct resource owners to on a secondary device.\n\n[NOTE]\nThe OAuth2 Device Authorization endpoint is disabled by default.\n\n`OAuth2DeviceAuthorizationEndpointConfigurer` configures the `OAuth2DeviceAuthorizationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2DeviceAuthorizationEndpointFilter` is the `Filter` that processes OAuth2 device authorization requests.\n\n`OAuth2DeviceAuthorizationEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- An `OAuth2DeviceAuthorizationRequestAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2DeviceAuthorizationRequestAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an \"`authenticated`\" `OAuth2DeviceAuthorizationRequestAuthenticationToken` and returns the `OAuth2DeviceAuthorizationResponse`.\n* `*AuthenticationFailureHandler*` -- An `OAuth2ErrorAuthenticationFailureHandler`.\n\n[[oauth2AuthorizationServer-oauth2-device-verification-endpoint]]\n== OAuth2 Device Verification Endpoint\n\n`OAuth2DeviceVerificationEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc8628#section-3.3[OAuth2 Device Verification endpoint] (or \"User Interaction\").\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for OAuth2 device verification requests.\n\n`OAuth2DeviceVerificationEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.deviceVerificationEndpoint(deviceVerificationEndpoint ->\n                    deviceVerificationEndpoint\n                        .deviceVerificationRequestConverter(deviceVerificationRequestConverter) <1>\n                        .deviceVerificationRequestConverters(deviceVerificationRequestConvertersConsumer)   <2>\n                        .authenticationProvider(authenticationProvider) <3>\n                        .authenticationProviders(authenticationProvidersConsumer)   <4>\n                        .deviceVerificationResponseHandler(deviceVerificationResponseHandler)   <5>\n                        .errorResponseHandler(errorResponseHandler) <6>\n                        .consentPage(\"/oauth2/v1/consent\")  <7>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `deviceVerificationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://datatracker.ietf.org/doc/html/rfc8628#section-3.3[OAuth2 device verification request] (or consent) from `HttpServletRequest` to an instance of `OAuth2DeviceVerificationAuthenticationToken` or `OAuth2DeviceAuthorizationConsentAuthenticationToken`.\n<2> `deviceVerificationRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2DeviceVerificationAuthenticationToken` or `OAuth2DeviceAuthorizationConsentAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `deviceVerificationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OAuth2DeviceVerificationAuthenticationToken` and directing the resource owner to return to their device.\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the error response.\n<7> `consentPage()`: The `URI` of the custom consent page to redirect resource owners to if consent is required during the device verification request flow.\n\n[NOTE]\nThe OAuth2 Device Verification endpoint is disabled by default.\n\n`OAuth2DeviceVerificationEndpointConfigurer` configures the `OAuth2DeviceVerificationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2DeviceVerificationEndpointFilter` is the `Filter` that processes OAuth2 device verification requests (and consents).\n\n`OAuth2DeviceVerificationEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- A `DelegatingAuthenticationConverter` composed of `OAuth2DeviceVerificationAuthenticationConverter` and `OAuth2DeviceAuthorizationConsentAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2DeviceVerificationAuthenticationProvider` and `OAuth2DeviceAuthorizationConsentAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- A `SimpleUrlAuthenticationSuccessHandler` that handles an \"`authenticated`\" `OAuth2DeviceVerificationAuthenticationToken` and redirects the user to a success page (`/?success`).\n* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthenticationException` and returns the `OAuth2Error` response.\n\n[[oauth2AuthorizationServer-oauth2-token-endpoint]]\n== OAuth2 Token Endpoint\n\n`OAuth2TokenEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc6749#section-3.2[OAuth2 Token endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3[OAuth2 access token requests].\n\n`OAuth2TokenEndpointConfigurer` provides the following configuration options:\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.tokenEndpoint(tokenEndpoint ->\n                    tokenEndpoint\n                        .accessTokenRequestConverter(accessTokenRequestConverter)   <1>\n                        .accessTokenRequestConverters(accessTokenRequestConvertersConsumer) <2>\n                        .authenticationProvider(authenticationProvider) <3>\n                        .authenticationProviders(authenticationProvidersConsumer)   <4>\n                        .accessTokenResponseHandler(accessTokenResponseHandler) <5>\n                        .errorResponseHandler(errorResponseHandler) <6>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `accessTokenRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3[OAuth2 access token request] from `HttpServletRequest` to an instance of `OAuth2AuthorizationGrantAuthenticationToken`.\n<2> `accessTokenRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2AuthorizationGrantAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `accessTokenResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an `OAuth2AccessTokenAuthenticationToken` and returning the https://datatracker.ietf.org/doc/html/rfc6749#section-5.1[`OAuth2AccessTokenResponse`].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://datatracker.ietf.org/doc/html/rfc6749#section-5.2[OAuth2Error response].\n\n`OAuth2TokenEndpointConfigurer` configures the `OAuth2TokenEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2TokenEndpointFilter` is the `Filter` that processes OAuth2 access token requests.\n\nThe supported https://datatracker.ietf.org/doc/html/rfc6749#section-1.3[authorization grant types] are `authorization_code`, `refresh_token`, `client_credentials`, `urn:ietf:params:oauth:grant-type:device_code`, and `urn:ietf:params:oauth:grant-type:token-exchange`.\n\n`OAuth2TokenEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- A `DelegatingAuthenticationConverter` composed of `OAuth2AuthorizationCodeAuthenticationConverter`, `OAuth2RefreshTokenAuthenticationConverter`, `OAuth2ClientCredentialsAuthenticationConverter`, `OAuth2DeviceCodeAuthenticationConverter`, and `OAuth2TokenExchangeAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2AuthorizationCodeAuthenticationProvider`, `OAuth2RefreshTokenAuthenticationProvider`, `OAuth2ClientCredentialsAuthenticationProvider`, `OAuth2DeviceCodeAuthenticationProvider`, and `OAuth2TokenExchangeAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An `OAuth2AccessTokenResponseAuthenticationSuccessHandler`.\n* `*AuthenticationFailureHandler*` -- An `OAuth2ErrorAuthenticationFailureHandler`.\n\n[[oauth2AuthorizationServer-oauth2-token-endpoint-customizing-client-credentials-grant-request-validation]]\n=== Customizing Client Credentials Grant Request Validation\n\n`OAuth2ClientCredentialsAuthenticationValidator` is the default validator used for validating specific OAuth2 Client Credentials Grant request parameters.\nThe default implementation validates the `scope` parameter.\nIf validation fails, an `OAuth2AuthenticationException` is thrown.\n\n`OAuth2ClientCredentialsAuthenticationProvider` provides the ability to override the default request validation by supplying a custom authentication validator of type `Consumer<OAuth2ClientCredentialsAuthenticationContext>` to `setAuthenticationValidator()`.\n\n[TIP]\n`OAuth2ClientCredentialsAuthenticationContext` holds the `OAuth2ClientCredentialsAuthenticationToken`, which contains the OAuth2 Client Credentials Grant request parameters.\n\n[IMPORTANT]\nIf validation fails, the authentication validator *MUST* throw `OAuth2AuthenticationException`.\n\nThe following example shows how to configure `OAuth2ClientCredentialsAuthenticationProvider` with a custom authentication validator that overrides the default `scope` validation:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.tokenEndpoint(tokenEndpoint ->\n                    tokenEndpoint\n                        .authenticationProviders(configureAuthenticationValidator())\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n\nprivate Consumer<List<AuthenticationProvider>> configureAuthenticationValidator() {\n\treturn (authenticationProviders) ->\n\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\tif (authenticationProvider instanceof OAuth2ClientCredentialsAuthenticationProvider) {\n\t\t\t\tConsumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator =\n\t\t\t\t\tnew CustomScopeValidator();\n\n\t\t\t\t// Override default scope validation\n\t\t\t\t((OAuth2ClientCredentialsAuthenticationProvider) authenticationProvider)\n\t\t\t\t\t.setAuthenticationValidator(authenticationValidator);\n\t\t\t}\n\t\t});\n}\n\nstatic class CustomScopeValidator implements Consumer<OAuth2ClientCredentialsAuthenticationContext> {\n\n\t@Override\n\tpublic void accept(OAuth2ClientCredentialsAuthenticationContext authenticationContext) {\n\t\tOAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =\n\t\t\tauthenticationContext.getAuthentication();\n\n\t\tSet<String> requestedScopes = clientCredentialsAuthentication.getScopes();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\t\tSet<String> allowedScopes = registeredClient.getScopes();\n\n        // TODO Implement scope validation\n\n\t}\n}\n----\n\n[[oauth2AuthorizationServer-oauth2-token-endpoint-dpop-bound-access-tokens]]\n=== DPoP-bound Access Tokens\n\nhttps://datatracker.ietf.org/doc/html/rfc9449[RFC 9449 OAuth 2.0 Demonstrating Proof of Possession (DPoP)] is an application-level mechanism for sender-constraining an access token.\n\nThe primary goal of DPoP is to prevent unauthorized or illegitimate clients from using leaked or stolen access tokens, by binding an access token to a public key upon issuance by the authorization server and requiring that the client proves possession of the corresponding private key when using the access token at the resource server.\n\nAccess tokens that are sender-constrained via DPoP stand in contrast to the typical bearer token, which can be used by any client in possession of the access token.\n\nDPoP introduces the concept of a https://datatracker.ietf.org/doc/html/rfc9449#name-dpop-proof-jwts[DPoP Proof], which is a JWT created by the client and sent as a header in an HTTP request.\nA client uses a DPoP proof to prove the possession of a private key corresponding to a certain public key.\n\nWhen the client initiates an access token request, it attaches a DPoP proof to the request in an HTTP header.\nThe authorization server binds (sender-constrains) the access token to the public key associated in the DPoP proof.\n\nWhen the client initiates a protected resource request, it again attaches a DPoP proof to the request in an HTTP header.\n\nThe resource server obtains information about the public key bound to the access token, either directly in the access token (JWT) or via the <<oauth2AuthorizationServer-oauth2-token-introspection-endpoint,OAuth2 Token Introspection endpoint>>.\nThe resource server then verifies that the public key bound to the access token matches the public key in the DPoP proof.\nIt also verifies that the access token hash in the DPoP proof matches the access token in the request.\n\n[[oauth2AuthorizationServer-oauth2-token-endpoint-dpop-access-token-request]]\n==== DPoP Access Token Request\n\nTo request an access token that is bound to a public key using DPoP, the client MUST provide a valid DPoP proof in the `DPoP` header when making an access token request to the OAuth2 Token endpoint.\nThis is applicable for all access token requests regardless of authorization grant type (e.g. `authorization_code`, `refresh_token`, `client_credentials`, etc).\n\nThe following HTTP request shows an `authorization_code` access token request with a DPoP proof in the `DPoP` header:\n\n[source,shell]\n----\nPOST /oauth2/token HTTP/1.1\nHost: server.example.com\nContent-Type: application/x-www-form-urlencoded\nDPoP: eyJraWQiOiJyc2EtandrLWtpZCIsInR5cCI6ImRwb3Arand0IiwiYWxnIjoiUlMyNTYiLCJqd2siOnsia3R5IjoiUlNBIiwiZSI6IkFRQUIiLCJraWQiOiJyc2EtandrLWtpZCIsIm4iOiIzRmxxSnI1VFJza0lRSWdkRTNEZDdEOWxib1dkY1RVVDhhLWZKUjdNQXZRbTdYWE5vWWttM3Y3TVFMMU5ZdER2TDJsOENBbmMwV2RTVElOVTZJUnZjNUtxbzJRNGNzTlg5U0hPbUVmem9ST2pRcWFoRWN2ZTFqQlhsdW9DWGRZdVlweDRfMXRmUmdHNmlpNFVoeGg2aUk4cU5NSlFYLWZMZnFoYmZZZnhCUVZSUHl3QmtBYklQNHgxRUFzYkM2RlNObWtoQ3hpTU5xRWd4YUlwWThDMmtKZEpfWklWLVdXNG5vRGR6cEtxSGN3bUI4RnNydW1sVllfRE5WdlVTRElpcGlxOVBiUDRIOTlUWE4xbzc0Nm9SYU5hMDdycTFob0NnTVNTeS04NVNhZ0NveGxteUUtRC1vZjlTc01ZOE9sOXQwcmR6cG9iQnVoeUpfbzVkZnZqS3cifX0.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20vb2F1dGgyL3Rva2VuIiwiaWF0IjoxNzQ2ODA2MzA1LCJqdGkiOiI0YjIzNDBkMi1hOTFmLTQwYTUtYmFhOS1kZDRlNWRlYWM4NjcifQ.wq8gJ_G6vpiEinfaY3WhereqCCLoeJOG8tnWBBAzRWx9F1KU5yAAWq-ZVCk_k07-h6DIqz2wgv6y9dVbNpRYwNwDUeik9qLRsC60M8YW7EFVyI3n_NpujLwzZeub_nDYMVnyn4ii0NaZrYHtoGXOlswQfS_-ET-jpC0XWm5nBZsCdUEXjOYtwaACC6Js-pyNwKmSLp5SKIk11jZUR5xIIopaQy521y9qJHhGRwzj8DQGsP7wMZ98UFL0E--1c-hh4rTy8PMeWCqRHdwjj_ry_eTe0DJFcxxYQdeL7-0_0CIO4Ayx5WHEpcUOIzBRoN32RsNpDZc-5slDNj9ku004DA\n\ngrant_type=authorization_code\\\n&client_id=s6BhdRkqt\\\n&code=SplxlOBeZQQYbYS6WxSbIA\\\n&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb\\\n&code_verifier=bEaL42izcC-o-xBk0K2vuJ6U-y1p9r_wW2dFWIWgjz-\n----\n\nThe following shows a representation of the DPoP Proof JWT header and claims:\n\n[source,json]\n----\n{\n  \"typ\": \"dpop+jwt\",\n  \"alg\": \"RS256\",\n  \"jwk\": {\n    \"kty\": \"RSA\",\n    \"e\": \"AQAB\",\n    \"n\": \"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\"\n  }\n}\n----\n\n[source,json]\n----\n{\n  \"htm\": \"POST\",\n  \"htu\": \"https://server.example.com/oauth2/token\",\n  \"iat\": 1746806305,\n  \"jti\": \"4b2340d2-a91f-40a5-baa9-dd4e5deac867\"\n}\n----\n\nThe following code shows an example of how to generate the DPoP Proof JWT:\n\n[source,java]\n----\nRSAKey rsaKey = ...\nJWKSource<SecurityContext> jwkSource = (jwkSelector, securityContext) -> jwkSelector\n\t\t.select(new JWKSet(rsaKey));\nNimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);\n\nJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t.type(\"dpop+jwt\")\n\t\t.jwk(rsaKey.toPublicJWK().toJSONObject())\n\t\t.build();\nJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t.issuedAt(Instant.now())\n\t\t.claim(\"htm\", \"POST\")\n\t\t.claim(\"htu\", \"https://server.example.com/oauth2/token\")\n\t\t.id(UUID.randomUUID().toString())\n\t\t.build();\n\nJwt dPoPProof = jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n----\n\nAfter the authorization server successfully validates the DPoP proof, the public key from the DPoP proof will be bound (sender-constrained) to the issued access token.\n\nThe following access token response shows the `token_type` parameter as `DPoP` to signal to the client that the access token was bound to its DPoP proof public key:\n\n[source,shell]\n----\nHTTP/1.1 200 OK\nContent-Type: application/json\nCache-Control: no-store\n\n{\n \"access_token\": \"Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU\",\n \"token_type\": \"DPoP\",\n \"expires_in\": 2677\n}\n----\n\n[[oauth2AuthorizationServer-oauth2-token-endpoint-dpop-public-key-confirmation]]\n==== Public Key Confirmation\n\nResource servers MUST be able to identify whether an access token is DPoP-bound and verify the binding to the public key of the DPoP proof.\nThe binding is accomplished by associating the public key with the access token in a way that can be accessed by the resource server, such as embedding the public key hash in the access token directly (JWT) or through token introspection.\n\nWhen an access token is represented as a JWT, the public key hash is contained in the `jkt` claim under the confirmation method (`cnf`) claim.\n\nThe following example shows the claims of a JWT access token containing a `cnf` claim with a `jkt` claim, which is the JWK SHA-256 Thumbprint of the DPoP proof public key:\n\n[source,json]\n----\n{\n  \"sub\":\"user@example.com\",\n  \"iss\":\"https://server.example.com\",\n  \"nbf\":1562262611,\n  \"exp\":1562266216,\n  \"cnf\":\n  {\n    \"jkt\":\"CQMknzRoZ5YUi7vS58jck1q8TmZT8wiIiXrCN1Ny4VU\"\n  }\n}\n----\n\n[[oauth2AuthorizationServer-oauth2-token-introspection-endpoint]]\n== OAuth2 Token Introspection Endpoint\n\n`OAuth2TokenIntrospectionEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc7662#section-2[OAuth2 Token Introspection endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://datatracker.ietf.org/doc/html/rfc7662#section-2.1[OAuth2 introspection requests].\n\n`OAuth2TokenIntrospectionEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint ->\n                    tokenIntrospectionEndpoint\n                        .introspectionRequestConverter(introspectionRequestConverter)   <1>\n                        .introspectionRequestConverters(introspectionRequestConvertersConsumer) <2>\n                        .authenticationProvider(authenticationProvider) <3>\n                        .authenticationProviders(authenticationProvidersConsumer)   <4>\n                        .introspectionResponseHandler(introspectionResponseHandler) <5>\n                        .errorResponseHandler(errorResponseHandler) <6>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `introspectionRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://datatracker.ietf.org/doc/html/rfc7662#section-2.1[OAuth2 introspection request] from `HttpServletRequest` to an instance of `OAuth2TokenIntrospectionAuthenticationToken`.\n<2> `introspectionRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2TokenIntrospectionAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `introspectionResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OAuth2TokenIntrospectionAuthenticationToken` and returning the https://datatracker.ietf.org/doc/html/rfc7662#section-2.2[OAuth2TokenIntrospection response].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://datatracker.ietf.org/doc/html/rfc7662#section-2.3[OAuth2Error response].\n\n`OAuth2TokenIntrospectionEndpointConfigurer` configures the `OAuth2TokenIntrospectionEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2TokenIntrospectionEndpointFilter` is the `Filter` that processes OAuth2 introspection requests.\n\n`OAuth2TokenIntrospectionEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- An `OAuth2TokenIntrospectionAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2TokenIntrospectionAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an \"`authenticated`\" `OAuth2TokenIntrospectionAuthenticationToken` and returns the `OAuth2TokenIntrospection` response.\n* `*AuthenticationFailureHandler*` -- An `OAuth2ErrorAuthenticationFailureHandler`.\n\n[[oauth2AuthorizationServer-oauth2-token-revocation-endpoint]]\n== OAuth2 Token Revocation Endpoint\n\n`OAuth2TokenRevocationEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc7009#section-2[OAuth2 Token Revocation endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://datatracker.ietf.org/doc/html/rfc7009#section-2.1[OAuth2 revocation requests].\n\n`OAuth2TokenRevocationEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.tokenRevocationEndpoint(tokenRevocationEndpoint ->\n                    tokenRevocationEndpoint\n                        .revocationRequestConverter(revocationRequestConverter) <1>\n                        .revocationRequestConverters(revocationRequestConvertersConsumer)   <2>\n                        .authenticationProvider(authenticationProvider) <3>\n                        .authenticationProviders(authenticationProvidersConsumer)   <4>\n                        .revocationResponseHandler(revocationResponseHandler)   <5>\n                        .errorResponseHandler(errorResponseHandler) <6>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `revocationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://datatracker.ietf.org/doc/html/rfc7009#section-2.1[OAuth2 revocation request] from `HttpServletRequest` to an instance of `OAuth2TokenRevocationAuthenticationToken`.\n<2> `revocationRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2TokenRevocationAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `revocationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OAuth2TokenRevocationAuthenticationToken` and returning the https://datatracker.ietf.org/doc/html/rfc7009#section-2.2[OAuth2 revocation response].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://datatracker.ietf.org/doc/html/rfc7009#section-2.2.1[OAuth2Error response].\n\n`OAuth2TokenRevocationEndpointConfigurer` configures the `OAuth2TokenRevocationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2TokenRevocationEndpointFilter` is the `Filter` that processes OAuth2 revocation requests.\n\n`OAuth2TokenRevocationEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- An `OAuth2TokenRevocationAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2TokenRevocationAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an \"`authenticated`\" `OAuth2TokenRevocationAuthenticationToken` and returns the OAuth2 revocation response.\n* `*AuthenticationFailureHandler*` -- An `OAuth2ErrorAuthenticationFailureHandler`.\n\n[[oauth2AuthorizationServer-oauth2-client-registration-endpoint]]\n== OAuth2 Client Registration Endpoint\n\n`OAuth2ClientRegistrationEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc7591#section-3[OAuth2 Client Registration endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://datatracker.ietf.org/doc/html/rfc7591#section-3.1[Client Registration requests].\n\n`OAuth2ClientRegistrationEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.clientRegistrationEndpoint(clientRegistrationEndpoint ->\n                    clientRegistrationEndpoint\n                        .clientRegistrationRequestConverter(clientRegistrationRequestConverter) <1>\n                        .clientRegistrationRequestConverters(clientRegistrationRequestConvertersConsumer)   <2>\n                        .authenticationProvider(authenticationProvider) <3>\n                        .authenticationProviders(authenticationProvidersConsumer)   <4>\n                        .clientRegistrationResponseHandler(clientRegistrationResponseHandler)   <5>\n                        .errorResponseHandler(errorResponseHandler) <6>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `clientRegistrationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract a https://datatracker.ietf.org/doc/html/rfc7591#section-3.1[Client Registration request] from `HttpServletRequest` to an instance of `OAuth2ClientRegistrationAuthenticationToken`.\n<2> `clientRegistrationRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OAuth2ClientRegistrationAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `clientRegistrationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OAuth2ClientRegistrationAuthenticationToken` and returning the https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1[Client Registration response].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2[Client Registration Error response].\n\n[NOTE]\nThe OAuth2 Client Registration endpoint is disabled by default.\n\n`OAuth2ClientRegistrationEndpointConfigurer` configures the `OAuth2ClientRegistrationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2ClientRegistrationEndpointFilter` is the `Filter` that processes https://datatracker.ietf.org/doc/html/rfc7591#section-3.1[Client Registration requests] and returns the https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1[OAuth2ClientRegistration response].\n\n`OAuth2ClientRegistrationEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- An `OAuth2ClientRegistrationAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2ClientRegistrationAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an \"`authenticated`\" `OAuth2ClientRegistrationAuthenticationToken` and returns the `OAuth2ClientRegistration` response.\n* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthenticationException` and returns the `OAuth2Error` response.\n\nThe OAuth2 Client Registration endpoint is an https://datatracker.ietf.org/doc/html/rfc7591#section-3[OAuth2 protected resource], which *REQUIRES* an access token to be sent as a bearer token in the Client Registration request.\n\n[NOTE]\nOAuth2 resource server support is autoconfigured, however, a `JwtDecoder` `@Bean` is *REQUIRED* for the OAuth2 Client Registration endpoint.\n\n[IMPORTANT]\nThe access token in a Client Registration request *REQUIRES* the OAuth2 scope `client.create`.\n\n[TIP]\nTo allow open client registration (no access token in request), configure `OAuth2ClientRegistrationAuthenticationProvider.setOpenRegistrationAllowed(true)`.\n\n[[oauth2AuthorizationServer-oauth2-authorization-server-metadata-endpoint]]\n== OAuth2 Authorization Server Metadata Endpoint\n\n`OAuth2AuthorizationServerMetadataEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc8414#section-3[OAuth2 Authorization Server Metadata endpoint].\nIt defines an extension point that lets you customize the https://datatracker.ietf.org/doc/html/rfc8414#section-3.2[OAuth2 Authorization Server Metadata response].\n\n`OAuth2AuthorizationServerMetadataEndpointConfigurer` provides the following configuration option:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n\t\t\t\t.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint ->\n                    authorizationServerMetadataEndpoint\n                        .authorizationServerMetadataCustomizer(authorizationServerMetadataCustomizer)   <1>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `authorizationServerMetadataCustomizer()`: The `Consumer` providing access to the `OAuth2AuthorizationServerMetadata.Builder` allowing the ability to customize the claims of the Authorization Server's configuration.\n\n`OAuth2AuthorizationServerMetadataEndpointConfigurer` configures the `OAuth2AuthorizationServerMetadataEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OAuth2AuthorizationServerMetadataEndpointFilter` is the `Filter` that returns the https://datatracker.ietf.org/doc/html/rfc8414#section-3.2[OAuth2AuthorizationServerMetadata response].\n\n[[oauth2AuthorizationServer-jwk-set-endpoint]]\n== JWK Set Endpoint\n\n`OAuth2AuthorizationServerConfigurer` provides support for the https://datatracker.ietf.org/doc/html/rfc7517[JWK Set endpoint].\n\n`OAuth2AuthorizationServerConfigurer` configures the `NimbusJwkSetEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`NimbusJwkSetEndpointFilter` is the `Filter` that returns the https://datatracker.ietf.org/doc/html/rfc7517#section-5[JWK Set].\n\n[NOTE]\nThe JWK Set endpoint is configured *only* if a `JWKSource<SecurityContext>` `@Bean` is registered.\n\n[[oauth2AuthorizationServer-oidc-provider-configuration-endpoint]]\n== OpenID Connect 1.0 Provider Configuration Endpoint\n\n`OidcProviderConfigurationEndpointConfigurer` provides the ability to customize the https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[OpenID Connect 1.0 Provider Configuration endpoint].\nIt defines an extension point that lets you customize the https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse[OpenID Provider Configuration response].\n\n`OidcProviderConfigurationEndpointConfigurer` provides the following configuration option:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n                .oidc(oidc ->\n                    oidc\n                        .providerConfigurationEndpoint(providerConfigurationEndpoint ->\n                            providerConfigurationEndpoint\n                                .providerConfigurationCustomizer(providerConfigurationCustomizer)   <1>\n                        )\n                )\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `providerConfigurationCustomizer()`: The `Consumer` providing access to the `OidcProviderConfiguration.Builder` allowing the ability to customize the claims of the OpenID Provider's configuration.\n\n`OidcProviderConfigurationEndpointConfigurer` configures the `OidcProviderConfigurationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OidcProviderConfigurationEndpointFilter` is the `Filter` that returns the https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse[OidcProviderConfiguration response].\n\n[[oauth2AuthorizationServer-oidc-logout-endpoint]]\n== OpenID Connect 1.0 Logout Endpoint\n\n`OidcLogoutEndpointConfigurer` provides the ability to customize the https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout[OpenID Connect 1.0 Logout endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for RP-Initiated Logout requests.\n\n`OidcLogoutEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n                .oidc(oidc ->\n                    oidc\n                        .logoutEndpoint(logoutEndpoint ->\n                            logoutEndpoint\n                                .logoutRequestConverter(logoutRequestConverter) <1>\n                                .logoutRequestConverters(logoutRequestConvertersConsumer)   <2>\n                                .authenticationProvider(authenticationProvider) <3>\n                                .authenticationProviders(authenticationProvidersConsumer)   <4>\n                                .logoutResponseHandler(logoutResponseHandler)   <5>\n                                .errorResponseHandler(errorResponseHandler) <6>\n                        )\n                )\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `logoutRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract a https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout[Logout request] from `HttpServletRequest` to an instance of `OidcLogoutAuthenticationToken`.\n<2> `logoutRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OidcLogoutAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `logoutResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OidcLogoutAuthenticationToken` and performing the logout.\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the error response.\n\n`OidcLogoutEndpointConfigurer` configures the `OidcLogoutEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OidcLogoutEndpointFilter` is the `Filter` that processes https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout[RP-Initiated Logout requests] and performs the logout of the End-User.\n\n`OidcLogoutEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- An `OidcLogoutAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OidcLogoutAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An `OidcLogoutAuthenticationSuccessHandler`.\n* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthenticationException` and returns the `OAuth2Error` response.\n\n[NOTE]\n`OidcLogoutAuthenticationProvider` uses a xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-session-registry[`SessionRegistry`] to look up the `SessionInformation` instance associated to the End-User requesting to be logged out.\n\n[TIP]\n`OidcClientInitiatedLogoutSuccessHandler` is the corresponding configuration in Spring Security’s OAuth2 Client support for configuring xref:servlet/oauth2/login/advanced.adoc#oauth2login-advanced-oidc-logout[OpenID Connect 1.0 RP-Initiated Logout].\n\n[[oauth2AuthorizationServer-oidc-logout-endpoint-customizing-logout-request-validation]]\n=== Customizing Logout Request Validation\n\n`OidcLogoutAuthenticationValidator` is the default validator used for validating specific OpenID Connect RP-Initiated Logout Request parameters.\nThe default implementation validates the `post_logout_redirect_uri` parameter.\nIf validation fails, an `OAuth2AuthenticationException` is thrown.\n\n`OidcLogoutAuthenticationProvider` provides the ability to override the default logout request validation by supplying a custom authentication validator of type `Consumer<OidcLogoutAuthenticationContext>` to `setAuthenticationValidator()`.\n\n[TIP]\n`OidcLogoutAuthenticationContext` holds the `OidcLogoutAuthenticationToken`, which contains the logout request parameters.\n\n[IMPORTANT]\nIf validation fails, the authentication validator *MUST* throw `OAuth2AuthenticationException`.\n\nThe following example shows how to configure `OidcLogoutAuthenticationProvider` with a custom authentication validator:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n                .oidc(oidc ->\n                    oidc\n                        .logoutEndpoint(logoutEndpoint ->\n                            logoutEndpoint\n                                .authenticationProviders(configureAuthenticationValidator())\n                        )\n                )\n\t\t);\n\n\treturn http.build();\n}\n\nprivate Consumer<List<AuthenticationProvider>> configureAuthenticationValidator() {\n\treturn (authenticationProviders) ->\n\t\t\tauthenticationProviders.forEach((authenticationProvider) -> {\n\t\t\t\tif (authenticationProvider instanceof OidcLogoutAuthenticationProvider oidcLogoutAuthenticationProvider) {\n\t\t\t\t\tConsumer<OidcLogoutAuthenticationContext> authenticationValidator = new CustomPostLogoutRedirectUriValidator();\n\t\t\t\t\toidcLogoutAuthenticationProvider.setAuthenticationValidator(authenticationValidator);\n\t\t\t\t}\n\t\t\t});\n}\n\nstatic class CustomPostLogoutRedirectUriValidator implements Consumer<OidcLogoutAuthenticationContext> {\n\n\t@Override\n\tpublic void accept(OidcLogoutAuthenticationContext authenticationContext) {\n\t\tOidcLogoutAuthenticationToken oidcLogoutAuthentication =\n\t\t\t\tauthenticationContext.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\n\t\t// TODO\n\n\t}\n}\n----\n\n[[oauth2AuthorizationServer-oidc-user-info-endpoint]]\n== OpenID Connect 1.0 UserInfo Endpoint\n\n`OidcUserInfoEndpointConfigurer` provides the ability to customize the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[OpenID Connect 1.0 UserInfo endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest[UserInfo requests].\n\n`OidcUserInfoEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n                .oidc(oidc ->\n                    oidc\n                        .userInfoEndpoint(userInfoEndpoint ->\n                            userInfoEndpoint\n                                .userInfoRequestConverter(userInfoRequestConverter) <1>\n                                .userInfoRequestConverters(userInfoRequestConvertersConsumer)   <2>\n                                .authenticationProvider(authenticationProvider) <3>\n                                .authenticationProviders(authenticationProvidersConsumer)   <4>\n                                .userInfoResponseHandler(userInfoResponseHandler)   <5>\n                                .errorResponseHandler(errorResponseHandler) <6>\n                                .userInfoMapper(userInfoMapper) <7>\n                        )\n                )\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `userInfoRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest[UserInfo request] from `HttpServletRequest` to an instance of `OidcUserInfoAuthenticationToken`.\n<2> `userInfoRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OidcUserInfoAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `userInfoResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OidcUserInfoAuthenticationToken` and returning the https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse[UserInfo response].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError[UserInfo Error response].\n<7> `userInfoMapper()`: The `Function` used to extract claims from `OidcUserInfoAuthenticationContext` to an instance of `OidcUserInfo`.\n\n`OidcUserInfoEndpointConfigurer` configures the `OidcUserInfoEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OidcUserInfoEndpointFilter` is the `Filter` that processes https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest[UserInfo requests] and returns the https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse[OidcUserInfo response].\n\n`OidcUserInfoEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- An internal implementation that obtains the `Authentication` from the `SecurityContext` and creates an `OidcUserInfoAuthenticationToken` with the principal.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OidcUserInfoAuthenticationProvider`, which is associated with an internal implementation of `userInfoMapper` that extracts https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims[standard claims] from the https://openid.net/specs/openid-connect-core-1_0.html#IDToken[ID Token] based on the https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims[scopes requested] during authorization.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an \"`authenticated`\" `OidcUserInfoAuthenticationToken` and returns the `OidcUserInfo` response.\n* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthenticationException` and returns the `OAuth2Error` response.\n\n[TIP]\nYou can customize the ID Token by providing an xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-oauth2-token-customizer[`OAuth2TokenCustomizer<JwtEncodingContext>`] `@Bean`.\n\nThe OpenID Connect 1.0 UserInfo endpoint is an OAuth2 protected resource, which *REQUIRES* an access token to be sent as a bearer token in the https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest[UserInfo request].\n\n[NOTE]\nOAuth2 resource server support is autoconfigured, however, a `JwtDecoder` `@Bean` is *REQUIRED* for the OpenID Connect 1.0 UserInfo endpoint.\n\n[[oauth2AuthorizationServer-oidc-client-registration-endpoint]]\n== OpenID Connect 1.0 Client Registration Endpoint\n\n`OidcClientRegistrationEndpointConfigurer` provides the ability to customize the https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration[OpenID Connect 1.0 Client Registration endpoint].\nIt defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration requests] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadRequest[Client Read requests].\n\n`OidcClientRegistrationEndpointConfigurer` provides the following configuration options:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2AuthorizationServer((authorizationServer) ->\n\t\t\tauthorizationServer\n                .oidc(oidc ->\n                    oidc\n                        .clientRegistrationEndpoint(clientRegistrationEndpoint ->\n                            clientRegistrationEndpoint\n                                .clientRegistrationRequestConverter(clientRegistrationRequestConverter) <1>\n                                .clientRegistrationRequestConverters(clientRegistrationRequestConvertersConsumers)  <2>\n                                .authenticationProvider(authenticationProvider) <3>\n                                .authenticationProviders(authenticationProvidersConsumer)   <4>\n                                .clientRegistrationResponseHandler(clientRegistrationResponseHandler)   <5>\n                                .errorResponseHandler(errorResponseHandler) <6>\n                        )\n                )\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `clientRegistrationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract a https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration request] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadRequest[Client Read request] from `HttpServletRequest` to an instance of `OidcClientRegistrationAuthenticationToken`.\n<2> `clientRegistrationRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`.\n<3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OidcClientRegistrationAuthenticationToken`.\n<4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`.\n<5> `clientRegistrationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an \"`authenticated`\" `OidcClientRegistrationAuthenticationToken` and returning the https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[Client Registration response] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadResponse[Client Read response].\n<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError[Client Registration Error response] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadError[Client Read Error response].\n\n[NOTE]\nThe OpenID Connect 1.0 Client Registration endpoint is disabled by default.\n\n`OidcClientRegistrationEndpointConfigurer` configures the `OidcClientRegistrationEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.\n`OidcClientRegistrationEndpointFilter` is the `Filter` that processes https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration requests] and returns the https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[OidcClientRegistration response].\n\n[TIP]\n`OidcClientRegistrationEndpointFilter` also processes https://openid.net/specs/openid-connect-registration-1_0.html#ReadRequest[Client Read requests] and returns the https://openid.net/specs/openid-connect-registration-1_0.html#ReadResponse[OidcClientRegistration response].\n\n`OidcClientRegistrationEndpointFilter` is configured with the following defaults:\n\n* `*AuthenticationConverter*` -- An `OidcClientRegistrationAuthenticationConverter`.\n* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OidcClientRegistrationAuthenticationProvider` and `OidcClientConfigurationAuthenticationProvider`.\n* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an \"`authenticated`\" `OidcClientRegistrationAuthenticationToken` and returns the `OidcClientRegistration` response.\n* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthenticationException` and returns the `OAuth2Error` response.\n\nThe OpenID Connect 1.0 Client Registration endpoint is an https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration[OAuth2 protected resource], which *REQUIRES* an access token to be sent as a bearer token in the Client Registration (or Client Read) request.\n\n[NOTE]\nOAuth2 resource server support is autoconfigured, however, a `JwtDecoder` `@Bean` is *REQUIRED* for the OpenID Connect 1.0 Client Registration endpoint.\n\n[IMPORTANT]\nThe access token in a Client Registration request *REQUIRES* the OAuth2 scope `client.create`.\n\n[IMPORTANT]\nThe access token in a Client Read request *REQUIRES* the OAuth2 scope `client.read`.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/client/authorization-grants.adoc",
    "content": "[[oauth2-client-authorization-grants]]\n= [[oauth2Client-auth-grant-support]]Authorization Grant Support\n\nThis section describes Spring Security's support for authorization grants.\n\n[[oauth2-client-authorization-code]]\n== [[oauth2Client-auth-code-grant]]Authorization Code\n\n[NOTE]\n====\nSee the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.1[Authorization Code] grant.\n====\n\n[[oauth2-client-authorization-code-authorization]]\n=== Obtaining Authorization\n\n[NOTE]\n====\nSee the https://tools.ietf.org/html/rfc6749#section-4.1.1[Authorization Request/Response] protocol flow for the Authorization Code grant.\n====\n\n[[oauth2-client-authorization-code-authorization-request]]\n=== Initiating the Authorization Request\n\nThe `OAuth2AuthorizationRequestRedirectFilter` uses an `OAuth2AuthorizationRequestResolver` to resolve an `OAuth2AuthorizationRequest` and initiate the Authorization Code grant flow by redirecting the end-user's user-agent to the Authorization Server's Authorization Endpoint.\n\nThe primary role of the `OAuth2AuthorizationRequestResolver` is to resolve an `OAuth2AuthorizationRequest` from the provided web request.\nThe default implementation `DefaultOAuth2AuthorizationRequestResolver` matches on the (default) path `+/oauth2/authorization/{registrationId}+`, extracting the `registrationId`, and using it to build the `OAuth2AuthorizationRequest` for the associated `ClientRegistration`.\n\nConsider the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml,attrs=\"-attributes\"]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            authorization-grant-type: authorization_code\n            redirect-uri: \"{baseUrl}/authorized/okta\"\n            scope: read, write\n        provider:\n          okta:\n            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize\n            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token\n----\n\nGiven the preceding properties, a request with the base path `/oauth2/authorization/okta` initiates the Authorization Request redirect by the `OAuth2AuthorizationRequestRedirectFilter` and ultimately starts the Authorization Code grant flow.\n\n[NOTE]\n====\nThe `AuthorizationCodeOAuth2AuthorizedClientProvider` is an implementation of `OAuth2AuthorizedClientProvider` for the Authorization Code grant,\nwhich also initiates the Authorization Request redirect by the `OAuth2AuthorizationRequestRedirectFilter`.\n====\n\nIf the OAuth 2.0 Client is a https://tools.ietf.org/html/rfc6749#section-2.1[Public Client], configure the OAuth 2.0 Client registration as follows:\n\n[source,yaml,attrs=\"-attributes\"]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-authentication-method: none\n            authorization-grant-type: authorization_code\n            redirect-uri: \"{baseUrl}/authorized/okta\"\n            # ...\n----\n\nPublic Clients are supported by using https://tools.ietf.org/html/rfc7636[Proof Key for Code Exchange] (PKCE).\nIf the client is running in an untrusted environment (such as a native application or web browser-based application) and is therefore incapable of maintaining the confidentiality of its credentials, PKCE is automatically used when the following conditions are true:\n\n. `client-secret` is omitted (or empty)\n. `client-authentication-method` is set to `none` (`ClientAuthenticationMethod.NONE`)\n\nor\n\n. When `ClientRegistration.clientSettings.requireProofKey` is `true` (in this case `ClientRegistration.authorizationGrantType` must be `authorization_code`)\n\n\n[TIP]\n====\nIf the OAuth 2.0 Provider doesn't support PKCE for https://tools.ietf.org/html/rfc6749#section-2.1[Confidential Clients], you need to disable it by setting `ClientRegistration.clientSettings.requireProofKey` to `false`.\n====\n\n[[oauth2-client-authorization-code-redirect-uri]]\n[[oauth2Client-auth-code-redirect-uri]]The `DefaultOAuth2AuthorizationRequestResolver` also supports `URI` template variables for the `redirect-uri` by using `UriComponentsBuilder`.\n\nThe following configuration uses all the supported `URI` template variables:\n\n[source,yaml,attrs=\"-attributes\"]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            # ...\n            redirect-uri: \"{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}\"\n            # ...\n----\n\n[NOTE]\n====\n`+{baseUrl}+` resolves to `+{baseScheme}://{baseHost}{basePort}{basePath}+`\n====\n\nConfiguring the `redirect-uri` with `URI` template variables is especially useful when the OAuth 2.0 Client is running behind a xref:features/exploits/http.adoc#http-proxy-server[Proxy Server].\nDoing so ensures that the `X-Forwarded-*` headers are used when expanding the `redirect-uri`.\n\n[[oauth2-client-authorization-code-authorization-request-resolver]]\n=== Customizing the Authorization Request\n\nOne of the primary use cases an `OAuth2AuthorizationRequestResolver` can realize is the ability to customize the Authorization Request with additional parameters above the standard parameters defined in the OAuth 2.0 Authorization Framework.\n\nFor example, OpenID Connect defines additional OAuth 2.0 request parameters for the https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest[Authorization Code Flow] extending from the standard parameters defined in the https://tools.ietf.org/html/rfc6749#section-4.1.1[OAuth 2.0 Authorization Framework].\nOne of those extended parameters is the `prompt` parameter.\n\n[NOTE]\n====\nThe `prompt` parameter is optional. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for re-authentication and consent. The defined values are: `none`, `login`, `consent`, and `select_account`.\n====\n\nThe following example shows how to configure the `DefaultOAuth2AuthorizationRequestResolver` with a `Consumer<OAuth2AuthorizationRequest.Builder>` that customizes the Authorization Request for `oauth2Login()`, by including the request parameter `prompt=consent`.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t.authorizationEndpoint((authorization) -> authorization\n\t\t\t\t\t.authorizationRequestResolver(\n\t\t\t\t\t\tauthorizationRequestResolver(this.clientRegistrationRepository)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate OAuth2AuthorizationRequestResolver authorizationRequestResolver(\n\t\t\tClientRegistrationRepository clientRegistrationRepository) {\n\n\t\tDefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver =\n\t\t\t\tnew DefaultOAuth2AuthorizationRequestResolver(\n\t\t\t\t\t\tclientRegistrationRepository, \"/oauth2/authorization\");\n\t\tauthorizationRequestResolver.setAuthorizationRequestCustomizer(\n\t\t\t\tauthorizationRequestCustomizer());\n\n\t\treturn  authorizationRequestResolver;\n\t}\n\n\tprivate Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {\n\t\treturn customizer -> customizer\n\t\t\t\t\t.additionalParameters((params) -> params.put(\"prompt\", \"consent\"));\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n    @Autowired\n    private lateinit var customClientRegistrationRepository: ClientRegistrationRepository\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2Login {\n                authorizationEndpoint {\n                    authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)\n                }\n            }\n        }\n        return http.build()\n    }\n\n    private fun authorizationRequestResolver(\n            clientRegistrationRepository: ClientRegistrationRepository?): OAuth2AuthorizationRequestResolver {\n        val authorizationRequestResolver = DefaultOAuth2AuthorizationRequestResolver(\n                clientRegistrationRepository, \"/oauth2/authorization\")\n        authorizationRequestResolver.setAuthorizationRequestCustomizer(\n                authorizationRequestCustomizer())\n        return authorizationRequestResolver\n    }\n\n    private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {\n        return Consumer { customizer ->\n            customizer\n                    .additionalParameters { params -> params[\"prompt\"] = \"consent\" }\n        }\n    }\n}\n----\n======\n\nFor the simple use case where the additional request parameter is always the same for a specific provider, you can add it directly in the `authorization-uri` property.\n\nFor example, if the value for the request parameter `prompt` is always `consent` for the provider `okta`, you can configure it as follows:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        provider:\n          okta:\n            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent\n----\n\nThe preceding example shows the common use case of adding a custom parameter on top of the standard parameters.\nAlternatively, if your requirements are more advanced, you can take full control in building the Authorization Request URI by overriding the `OAuth2AuthorizationRequest.authorizationRequestUri` property.\n\n[TIP]\n====\n`OAuth2AuthorizationRequest.Builder.build()` constructs the `OAuth2AuthorizationRequest.authorizationRequestUri`, which represents the Authorization Request URI including all query parameters using the `application/x-www-form-urlencoded` format.\n====\n\nThe following example shows a variation of `authorizationRequestCustomizer()` from the preceding example and instead overrides the `OAuth2AuthorizationRequest.authorizationRequestUri` property:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprivate Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {\n\treturn customizer -> customizer\n\t\t\t\t.authorizationRequestUri((uriBuilder) -> uriBuilder\n\t\t\t\t\t.queryParam(\"prompt\", \"consent\").build());\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nprivate fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {\n    return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->\n        customizer\n                .authorizationRequestUri { uriBuilder: UriBuilder ->\n                    uriBuilder\n                            .queryParam(\"prompt\", \"consent\").build()\n                }\n    }\n}\n----\n======\n\n[[oauth2-client-authorization-code-authorization-request-repository]]\n=== Storing the Authorization Request\n\nThe `AuthorizationRequestRepository` is responsible for the persistence of the `OAuth2AuthorizationRequest` from the time the Authorization Request is initiated to the time the Authorization Response is received (the callback).\n\n[TIP]\n====\nThe `OAuth2AuthorizationRequest` is used to correlate and validate the Authorization Response.\n====\n\nThe default implementation of `AuthorizationRequestRepository` is `HttpSessionOAuth2AuthorizationRequestRepository`, which stores the `OAuth2AuthorizationRequest` in the `HttpSession`.\n\nIf you have a custom implementation of `AuthorizationRequestRepository`, you can configure it as follows:\n\n.AuthorizationRequestRepository Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2ClientSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Client((oauth2) -> oauth2\n\t\t\t\t.authorizationCodeGrant((codeGrant) -> codeGrant\n\t\t\t\t\t.authorizationRequestRepository(this.authorizationRequestRepository())\n\t\t\t\t\t// ...\n\t\t\t\t)\n\t\t\t)\n            .oauth2Login((oauth2) -> oauth2\n                .authorizationEndpoint((endpoint) -> endpoint\n                    .authorizationRequestRepository(this.authorizationRequestRepository())\n                    // ...\n                )\n            );\n\t\t\treturn http.build();\n\t}\n\n    @Bean\n    public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {\n        return new CustomOAuth2AuthorizationRequestRepository();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2ClientSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Client {\n                authorizationCodeGrant {\n                    authorizationRequestRepository = authorizationRequestRepository()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<oauth2-client>\n\t\t<authorization-code-grant authorization-request-repository-ref=\"authorizationRequestRepository\"/>\n\t</oauth2-client>\n</http>\n----\n======\n\n[[oauth2-client-authorization-code-access-token]]\n=== Requesting an Access Token\n\n[NOTE]\n====\nSee the https://tools.ietf.org/html/rfc6749#section-4.1.3[Access Token Request/Response] protocol flow for the Authorization Code grant.\n====\n\nThe default implementation of `OAuth2AccessTokenResponseClient` for the Authorization Code grant is `RestClientAuthorizationCodeTokenResponseClient`, which uses a `RestClient` instance to exchange an authorization code for an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: authorization-code\n:grant-type: Authorization Code\n:class-name: RestClientAuthorizationCodeTokenResponseClient\n:grant-request: OAuth2AuthorizationCodeGrantRequest\n:leveloffset: +1\ninclude::partial$servlet/oauth2/client/rest-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-authorization-code-access-token-response-client-dsl]]\n=== Customize using the DSL\n\nWhether you customize `{class-name}` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you can configure it using the DSL (as an alternative to <<oauth2-client-authorization-code-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via DSL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2ClientSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Client((oauth2) -> oauth2\n\t\t\t\t.authorizationCodeGrant((codeGrant) -> codeGrant\n\t\t\t\t\t.accessTokenResponseClient(this.accessTokenResponseClient())\n\t\t\t\t\t// ...\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2ClientSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Client {\n                authorizationCodeGrant {\n                    accessTokenResponseClient = accessTokenResponseClient()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<oauth2-client>\n\t\t<authorization-code-grant access-token-response-client-ref=\"accessTokenResponseClient\"/>\n\t</oauth2-client>\n</http>\n----\n======\n\n[[oauth2-client-refresh-token]]\n== [[oauth2Client-refresh-token-grant]]Refresh Token\n\n[NOTE]\n====\nSee the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.5[Refresh Token].\n====\n\n[[oauth2-client-refresh-token-access-token]]\n=== Refreshing an Access Token\n\n[NOTE]\n====\nSee the https://tools.ietf.org/html/rfc6749#section-6[Access Token Request/Response] protocol flow for the Refresh Token grant.\n====\n\nThe default implementation of `OAuth2AccessTokenResponseClient` for the Refresh Token grant is `RestClientRefreshTokenTokenResponseClient`, which uses a `RestClient` instance to obtain an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: refresh-token\n:grant-type: Refresh Token\n:class-name: RestClientRefreshTokenTokenResponseClient\n:grant-request: OAuth2RefreshTokenGrantRequest\n:leveloffset: +1\ninclude::partial$servlet/oauth2/client/rest-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-refresh-token-authorized-client-provider-builder]]\n=== Customize using the Builder\n\nWhether you customize `RestClientRefreshTokenTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you can configure it using the `OAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-refresh-token-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via Builder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Customize\nOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...\n\nOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken((configurer) -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))\n\t\t\t\t.build();\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Customize\nval refreshTokenTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...\n\nval authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n        .authorizationCode()\n        .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }\n        .build()\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n----\n======\n\n[NOTE]\n====\n`OAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenOAuth2AuthorizedClientProvider`,\nwhich is an implementation of an `OAuth2AuthorizedClientProvider` for the Refresh Token grant.\n====\n\nThe `OAuth2RefreshToken` can optionally be returned in the Access Token Response for the `authorization_code` grant type.\nIf the `OAuth2AuthorizedClient.getRefreshToken()` is available and the `OAuth2AuthorizedClient.getAccessToken()` is expired, it is automatically refreshed by the `RefreshTokenOAuth2AuthorizedClientProvider`.\n\n[[oauth2-client-client-credentials]]\n== [[oauth2Client-client-creds-grant]]Client Credentials\n\n[NOTE]\n====\nPlease refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.4[Client Credentials] grant.\n====\n\n[[oauth2-client-client-credentials-access-token]]\n=== Requesting an Access Token\n\n[NOTE]\n====\nSee the https://tools.ietf.org/html/rfc6749#section-4.4.2[Access Token Request/Response] protocol flow for the Client Credentials grant.\n====\n\nThe default implementation of `OAuth2AccessTokenResponseClient` for the Client Credentials grant is `RestClientClientCredentialsTokenResponseClient`, which uses a `RestClient` instance to obtain an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: client-credentials\n:grant-type: Client Credentials\n:class-name: RestClientClientCredentialsTokenResponseClient\n:grant-request: OAuth2ClientCredentialsGrantRequest\n:leveloffset: +1\ninclude::partial$servlet/oauth2/client/rest-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-client-credentials-authorized-client-provider-builder]]\n=== Customize using the Builder\n\nWhether you customize `RestClientClientCredentialsTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you can configure it using the `OAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-client-credentials-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via Builder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Customize\nOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...\n\nOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.clientCredentials((configurer) -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))\n\t\t\t\t.build();\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Customize\nval clientCredentialsTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...\n\nval authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n        .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }\n        .build()\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n----\n======\n\n[NOTE]\n====\n`OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsOAuth2AuthorizedClientProvider`,\nwhich is an implementation of an `OAuth2AuthorizedClientProvider` for the Client Credentials grant.\n====\n\n[[oauth2-client-client-credentials-authorized-client-manager]]\n=== Using the Access Token\n\nConsider the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            authorization-grant-type: client_credentials\n            scope: read, write\n        provider:\n          okta:\n            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token\n----\n\nFurther consider the following `OAuth2AuthorizedClientManager` `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.clientCredentials()\n\t\t\t\t\t.build();\n\n\tDefaultOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ClientRegistrationRepository,\n        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {\n    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n            .clientCredentials()\n            .build()\n    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n\nGiven the preceding properties and bean, you can obtain the `OAuth2AccessToken` as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class OAuth2ClientController {\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@GetMapping(\"/\")\n\tpublic String index(Authentication authentication,\n\t\t\t\t\t\tHttpServletRequest servletRequest,\n\t\t\t\t\t\tHttpServletResponse servletResponse) {\n\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n\t\t\t\t.principal(authentication)\n\t\t\t\t.attributes(attrs -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), servletRequest);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), servletResponse);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\n\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\n\t\t// ...\n\n\t\treturn \"index\";\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass OAuth2ClientController {\n\n    @Autowired\n    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager\n\n    @GetMapping(\"/\")\n    fun index(authentication: Authentication?,\n              servletRequest: HttpServletRequest,\n              servletResponse: HttpServletResponse): String {\n        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n                .principal(authentication)\n                .attributes(Consumer { attrs: MutableMap<String, Any> ->\n                    attrs[HttpServletRequest::class.java.name] = servletRequest\n                    attrs[HttpServletResponse::class.java.name] = servletResponse\n                })\n                .build()\n        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)\n        val accessToken: OAuth2AccessToken = authorizedClient.accessToken\n\n        // ...\n\n        return \"index\"\n    }\n}\n----\n======\n\n[NOTE]\n====\n`HttpServletRequest` and `HttpServletResponse` are both OPTIONAL attributes.\nIf not provided, they default to `ServletRequestAttributes` by using `RequestContextHolder.getRequestAttributes()`.\n====\n\n[[oauth2-client-jwt-bearer]]\n== JWT Bearer\n\n[NOTE]\n====\nPlease refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the https://datatracker.ietf.org/doc/html/rfc7523[JWT Bearer] grant.\n====\n\n[[oauth2-client-jwt-bearer-access-token]]\n=== Requesting an Access Token\n\n[NOTE]\n====\nPlease refer to the https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[Access Token Request/Response] protocol flow for the JWT Bearer grant.\n====\n\nThe default implementation of `OAuth2AccessTokenResponseClient` for the JWT Bearer grant is `RestClientJwtBearerTokenResponseClient`, which uses a `RestClient` instance to obtain an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: jwt-bearer\n:grant-type: JWT Bearer\n:class-name: RestClientJwtBearerTokenResponseClient\n:grant-request: JwtBearerGrantRequest\n:leveloffset: +1\ninclude::partial$servlet/oauth2/client/rest-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-jwt-bearer-authorized-client-provider-builder]]\n=== Customize using the Builder\n\nWhether you customize `RestClientJwtBearerTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you can configure it using the `OAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-jwt-bearer-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via Builder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Customize\nOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...\n\nJwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();\njwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);\n\nOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t\t.build();\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Customize\nval jwtBearerTokenResponseClient: OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...\n\nval jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()\njwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient)\n\nval authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n        .provider(jwtBearerAuthorizedClientProvider)\n        .build()\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n----\n======\n\n[[oauth2-client-jwt-bearer-authorized-client-manager]]\n=== Using the Access Token\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer\n            scope: read\n        provider:\n          okta:\n            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token\n----\n\n...and the `OAuth2AuthorizedClientManager` `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tJwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =\n\t\t\tnew JwtBearerOAuth2AuthorizedClientProvider();\n\n\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t\t\t.build();\n\n\tDefaultOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ClientRegistrationRepository,\n        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {\n    val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()\n    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n            .provider(jwtBearerAuthorizedClientProvider)\n            .build()\n    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n\nYou may obtain the `OAuth2AccessToken` as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class OAuth2ResourceServerController {\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@GetMapping(\"/resource\")\n\tpublic String resource(JwtAuthenticationToken jwtAuthentication) {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n\t\t\t\t.principal(jwtAuthentication)\n\t\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\n\t\t// ...\n\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass OAuth2ResourceServerController {\n\n    @Autowired\n    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager\n\n    @GetMapping(\"/resource\")\n    fun resource(jwtAuthentication: JwtAuthenticationToken?): String {\n        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n                .principal(jwtAuthentication)\n                .build()\n        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)\n        val accessToken: OAuth2AccessToken = authorizedClient.accessToken\n\n        // ...\n\n    }\n}\n----\n======\n\n[NOTE]\n====\n`JwtBearerOAuth2AuthorizedClientProvider` resolves the `Jwt` assertion via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.\n====\n\n[TIP]\n====\nIf you need to resolve the `Jwt` assertion from a different source, you can provide `JwtBearerOAuth2AuthorizedClientProvider.setJwtAssertionResolver()` with a custom `Function<OAuth2AuthorizationContext, Jwt>`.\n====\n\n[[oauth2-client-token-exchange]]\n== [[oauth2Client-token-exchange-grant]]Token Exchange\n\n[NOTE]\n====\nPlease refer to OAuth 2.0 Token Exchange for further details on the https://datatracker.ietf.org/doc/html/rfc8693[Token Exchange] grant.\n====\n\n[[oauth2-client-token-exchange-access-token]]\n=== Requesting an Access Token\n\n[NOTE]\n====\nPlease refer to the https://datatracker.ietf.org/doc/html/rfc8693#section-2[Token Exchange Request and Response] protocol flow for the Token Exchange grant.\n====\n\nThe default implementation of `OAuth2AccessTokenResponseClient` for the Token Exchange grant is `RestClientTokenExchangeTokenResponseClient`, which uses a `RestClient` instance to obtain an access token at the Authorization Server’s Token Endpoint.\n\n:section-id: token-exchange\n:grant-type: Token Exchange\n:class-name: RestClientTokenExchangeTokenResponseClient\n:grant-request: TokenExchangeGrantRequest\n:leveloffset: +1\ninclude::partial$servlet/oauth2/client/rest-client-access-token-response-client.adoc[]\n\n:leveloffset: -1\n\n[[oauth2-client-token-exchange-authorized-client-provider-builder]]\n=== Customize using the Builder\n\nWhether you customize `RestClientTokenExchangeTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you can configure it using the `OAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-token-exchange-access-token-response-client-bean,publishing a bean>>) as follows:\n\n.Access Token Response Configuration via Builder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n// Customize\nOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient = ...\n\nTokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();\ntokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient);\n\nOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.provider(tokenExchangeAuthorizedClientProvider)\n\t\t\t\t.build();\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n// Customize\nval tokenExchangeTokenResponseClient: OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> = ...\n\nval tokenExchangeAuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()\ntokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient)\n\nval authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n        .provider(tokenExchangeAuthorizedClientProvider)\n        .build()\n\n// ...\n\nauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n----\n======\n\n[[oauth2-client-token-exchange-authorized-client-manager]]\n=== [[token-exchange-grant-access-token]]Using the Access Token\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange\n            scope: read\n        provider:\n          okta:\n            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token\n----\n\n...and the `OAuth2AuthorizedClientManager` `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tTokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =\n\t\t\tnew TokenExchangeOAuth2AuthorizedClientProvider();\n\n\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.provider(tokenExchangeAuthorizedClientProvider)\n\t\t\t\t\t.build();\n\n\tDefaultOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ClientRegistrationRepository,\n        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {\n    val tokenExchangeAuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()\n    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n            .provider(tokenExchangeAuthorizedClientProvider)\n            .build()\n    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n\nYou may obtain the `OAuth2AccessToken` as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class OAuth2ResourceServerController {\n\n\t@Autowired\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@GetMapping(\"/resource\")\n\tpublic String resource(JwtAuthenticationToken jwtAuthentication) {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n\t\t\t\t.principal(jwtAuthentication)\n\t\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\n\t\t// ...\n\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass OAuth2ResourceServerController {\n\n    @Autowired\n    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager\n\n    @GetMapping(\"/resource\")\n    fun resource(jwtAuthentication: JwtAuthenticationToken?): String {\n        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(\"okta\")\n                .principal(jwtAuthentication)\n                .build()\n        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)\n        val accessToken: OAuth2AccessToken = authorizedClient.accessToken\n\n        // ...\n\n    }\n}\n----\n======\n\n[NOTE]\n====\n`TokenExchangeOAuth2AuthorizedClientProvider` resolves the subject token (as an `OAuth2Token`) via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.\nAn actor token is not resolved by default.\n====\n\n[TIP]\n====\nIf you need to resolve the subject token from a different source, you can provide `TokenExchangeOAuth2AuthorizedClientProvider.setSubjectTokenResolver()` with a custom `Function<OAuth2AuthorizationContext, OAuth2Token>`.\n====\n\n[TIP]\n====\nIf you need to resolve an actor token, you can provide `TokenExchangeOAuth2AuthorizedClientProvider.setActorTokenResolver()` with a custom `Function<OAuth2AuthorizationContext, OAuth2Token>`.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/client/authorized-clients.adoc",
    "content": "[[oauth2-client-additional-features]]\n= [[oauth2Client-additional-features]]Authorized Client Features\n\nThis section covers additional features provided by Spring Security for OAuth2 client.\n\n[[oauth2-client-registered-authorized-client]]\n== [[oauth2Client-registered-authorized-client]]Resolving an Authorized Client\n\nThe `@RegisteredOAuth2AuthorizedClient` annotation provides the ability to resolve a method parameter to an argument value of type `OAuth2AuthorizedClient`.\nThis is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` by using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`.\nThe following example shows how to use `@RegisteredOAuth2AuthorizedClient`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class OAuth2ClientController {\n\n\t@GetMapping(\"/\")\n\tpublic String index(@RegisteredOAuth2AuthorizedClient(\"okta\") OAuth2AuthorizedClient authorizedClient) {\n\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\n\t\t...\n\n\t\treturn \"index\";\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass OAuth2ClientController {\n    @GetMapping(\"/\")\n    fun index(@RegisteredOAuth2AuthorizedClient(\"okta\") authorizedClient: OAuth2AuthorizedClient): String {\n        val accessToken = authorizedClient.accessToken\n\n        ...\n\n        return \"index\"\n    }\n}\n----\n======\n\nThe `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] and, therefore, inherits its capabilities.\n\n[[oauth2-client-rest-client]]\n== RestClient Integration\n\nSupport for `RestClient` is provided by `OAuth2ClientHttpRequestInterceptor`.\nThis interceptor provides the ability to make protected resources requests by placing a `Bearer` token in the `Authorization` header of an outbound request.\nThe interceptor directly uses an `OAuth2AuthorizedClientManager` and therefore inherits the following capabilities:\n\n* Performs an OAuth 2.0 Access Token request to obtain `OAuth2AccessToken` if the client has not yet been authorized\n** `authorization_code`: Triggers the Authorization Request redirect to initiate the flow\n** `client_credentials`: The access token is obtained directly from the Token Endpoint\n** Additional grant types are supported by xref:servlet/oauth2/index.adoc#oauth2-client-enable-extension-grant-type[enabling extension grant types]\n* If an existing `OAuth2AccessToken` is expired, it is refreshed (or renewed)\n\nThe following example uses the default `OAuth2AuthorizedClientManager` to configure a `RestClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:\n\n.Configure `RestClient` with `ClientHttpRequestInterceptor`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class RestClientConfig {\n\n\t@Bean\n\tpublic RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tOAuth2ClientHttpRequestInterceptor requestInterceptor =\n\t\t\t\tnew OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n\n\t\treturn RestClient.builder()\n\t\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass RestClientConfig {\n\n\t@Bean\n\tfun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {\n\t\tval requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)\n\n\t\treturn RestClient.builder()\n\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\n[[oauth2-client-rest-client-registration-id]]\n=== Providing the `clientRegistrationId`\n\n`OAuth2ClientHttpRequestInterceptor` uses a `ClientRegistrationIdResolver` to determine which client is used to obtain an access token.\nBy default, `RequestAttributeClientRegistrationIdResolver` is used to resolve the `clientRegistrationId` from `HttpRequest#attributes()`.\n\nThe following example demonstrates providing a `clientRegistrationId` via attributes:\n\n.Provide `clientRegistrationId` via attributes\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId;\n\n@Controller\npublic class ResourceController {\n\n\tprivate final RestClient restClient;\n\n\tpublic ResourceController(RestClient restClient) {\n\t\tthis.restClient = restClient;\n\t}\n\n\t@GetMapping(\"/\")\n\tpublic String index() {\n\t\tString resourceUri = \"...\";\n\n\t\tString body = this.restClient.get()\n\t\t\t\t.uri(resourceUri)\n\t\t\t\t.attributes(clientRegistrationId(\"okta\"))   // <1>\n\t\t\t\t.retrieve()\n\t\t\t\t.body(String.class);\n\n\t\t// ...\n\n\t\treturn \"index\";\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId\nimport org.springframework.web.client.body\n\n@Controller\nclass ResourceController(private restClient: RestClient) {\n\n\t@GetMapping(\"/\")\n\tfun index(): String {\n\t\tval resourceUri = \"...\"\n\n\t\tval body: String = restClient.get()\n\t\t\t\t.uri(resourceUri)\n\t\t\t\t.attributes(clientRegistrationId(\"okta\"))   // <1>\n\t\t\t\t.retrieve()\n\t\t\t\t.body<String>()\n\n\t\t// ...\n\n\t\treturn \"index\"\n\t}\n\n}\n----\n======\n<1> `clientRegistrationId()` is a `static` method in `RequestAttributeClientRegistrationIdResolver`.\n\nAlternatively, a custom `ClientRegistrationIdResolver` can be provided.\nThe following example configures a custom implementation that resolves the `clientRegistrationId` from the current user.\n\n.Configure `ClientHttpRequestInterceptor` with custom `ClientRegistrationIdResolver`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class RestClientConfig {\n\n\t@Bean\n\tpublic RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tOAuth2ClientHttpRequestInterceptor requestInterceptor =\n\t\t\t\tnew OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n\t\trequestInterceptor.setClientRegistrationIdResolver(clientRegistrationIdResolver());\n\n\t\treturn RestClient.builder()\n\t\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t\t.build();\n\t}\n\n\tprivate static ClientRegistrationIdResolver clientRegistrationIdResolver() {\n\t\treturn (request) -> {\n\t\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t\treturn (authentication instanceof OAuth2AuthenticationToken principal)\n\t\t\t\t\t? principal.getAuthorizedClientRegistrationId() : null;\n\t\t};\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass RestClientConfig {\n\n\t@Bean\n\tfun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {\n\t\tval requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)\n\t\trequestInterceptor.setClientRegistrationIdResolver(clientRegistrationIdResolver())\n\n\t\treturn RestClient.builder()\n\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t.build()\n\t}\n\n\tfun clientRegistrationIdResolver(): ClientRegistrationIdResolver {\n\t\treturn ClientRegistrationIdResolver { request ->\n\t\t\tval authentication = SecurityContextHolder.getContext().getAuthentication()\n\t\t\treturn if (authentication instanceof OAuth2AuthenticationToken) {\n\t\t\t\tauthentication.getAuthorizedClientRegistrationId()\n\t\t\t} else {\n                null\n\t\t\t}\n\t\t}\n\t}\n\n}\n----\n=====\n\n[[oauth2-client-rest-client-principal]]\n=== Providing the `principal`\n\n`OAuth2ClientHttpRequestInterceptor` uses a `PrincipalResolver` to determine which principal name is associated with the access token, which allows an application to choose how to scope the `OAuth2AuthorizedClient` that is stored.\nBy default, `SecurityContextHolderPrincipalResolver` is used to resolve the current `principal` from the `SecurityContextHolder`.\n\nAlternatively, the `principal` can be resolved from `HttpRequest#attributes()` by configuring `RequestAttributePrincipalResolver`, as the following example shows:\n\n.Configure `ClientHttpRequestInterceptor` with `RequestAttributePrincipalResolver`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class RestClientConfig {\n\n\t@Bean\n\tpublic RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tOAuth2ClientHttpRequestInterceptor requestInterceptor =\n\t\t\t\tnew OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n\t\trequestInterceptor.setPrincipalResolver(new RequestAttributePrincipalResolver());\n\n\t\treturn RestClient.builder()\n\t\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass RestClientConfig {\n\n\t@Bean\n\tfun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {\n\t\tval requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)\n\t\trequestInterceptor.setPrincipalResolver(RequestAttributePrincipalResolver())\n\n\t\treturn RestClient.builder()\n\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\nThe following example demonstrates providing a `principal` name via attributes that scopes the `OAuth2AuthorizedClient` to the application instead of the current user:\n\n.Provide `principal` name via attributes\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId;\nimport static org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal;\n\n@Controller\npublic class ResourceController {\n\n\tprivate final RestClient restClient;\n\n\tpublic ResourceController(RestClient restClient) {\n\t\tthis.restClient = restClient;\n\t}\n\n\t@GetMapping(\"/\")\n\tpublic String index() {\n\t\tString resourceUri = \"...\";\n\n\t\tString body = this.restClient.get()\n\t\t\t\t.uri(resourceUri)\n\t\t\t\t.attributes(clientRegistrationId(\"okta\"))\n\t\t\t\t.attributes(principal(\"my-application\"))   // <1>\n\t\t\t\t.retrieve()\n\t\t\t\t.body(String.class);\n\n\t\t// ...\n\n\t\treturn \"index\";\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId\nimport org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal\nimport org.springframework.web.client.body\n\n@Controller\nclass ResourceController(private restClient: RestClient) {\n\n    @GetMapping(\"/\")\n\tfun index(): String {\n\t\tval resourceUri = \"...\"\n\n\t\tval body: String = restClient.get()\n\t\t\t\t.uri(resourceUri)\n\t\t\t\t.attributes(clientRegistrationId(\"okta\"))\n\t\t\t\t.attributes(principal(\"my-application\"))   // <1>\n\t\t\t\t.retrieve()\n\t\t\t\t.body<String>()\n\n\t\t// ...\n\n\t\treturn \"index\"\n\t}\n\n}\n----\n======\n<1> `principal()` is a `static` method in `RequestAttributePrincipalResolver`.\n\n[[oauth2-client-rest-client-authorization-failure-handler]]\n=== Handling Failure\n\nIf an access token is invalid for any reason (e.g. expired token), it can be beneficial to handle the failure by removing the access token so that it cannot be used again.\nYou can set up the interceptor to do this automatically by providing an `OAuth2AuthorizationFailureHandler` to remove the access token.\n\nThe following example uses an `OAuth2AuthorizedClientRepository` to set up an `OAuth2AuthorizationFailureHandler` that removes an invalid `OAuth2AuthorizedClient` *within* the context of an `HttpServletRequest`:\n\n.Configure `OAuth2AuthorizationFailureHandler` using `OAuth2AuthorizedClientRepository`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class RestClientConfig {\n\n\t@Bean\n\tpublic RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager,\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\t\tOAuth2ClientHttpRequestInterceptor requestInterceptor =\n\t\t\t\tnew OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n\n\t\tOAuth2AuthorizationFailureHandler authorizationFailureHandler =\n\t\t\tOAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(authorizedClientRepository);\n\t\trequestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler);\n\n\t\treturn RestClient.builder()\n\t\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass RestClientConfig {\n\n\t@Bean\n\tfun restClient(authorizedClientManager: OAuth2AuthorizedClientManager,\n\t\t\tauthorizedClientRepository: OAuth2AuthorizedClientRepository): RestClient {\n\n\t\tval requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)\n\n\t\tval authorizationFailureHandler = OAuth2ClientHttpRequestInterceptor\n\t\t\t.authorizationFailureHandler(authorizedClientRepository)\n\t\trequestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler)\n\n\t\treturn RestClient.builder()\n\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\nAlternatively, an `OAuth2AuthorizedClientService` can be used to remove an invalid `OAuth2AuthorizedClient` *outside* the context of an `HttpServletRequest`, as the following example shows:\n\n.Configure `OAuth2AuthorizationFailureHandler` using `OAuth2AuthorizedClientService`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class RestClientConfig {\n\n\t@Bean\n\tpublic RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager,\n\t\t\tOAuth2AuthorizedClientService authorizedClientService) {\n\n\t\tOAuth2ClientHttpRequestInterceptor requestInterceptor =\n\t\t\t\tnew OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n\n\t\tOAuth2AuthorizationFailureHandler authorizationFailureHandler =\n\t\t\tOAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(authorizedClientService);\n\t\trequestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler);\n\n\t\treturn RestClient.builder()\n\t\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass RestClientConfig {\n\n\t@Bean\n\tfun restClient(authorizedClientManager: OAuth2AuthorizedClientManager,\n\t\t\tauthorizedClientService: OAuth2AuthorizedClientService): RestClient {\n\n\t\tval requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)\n\n\t\tval authorizationFailureHandler = OAuth2ClientHttpRequestInterceptor\n\t\t\t.authorizationFailureHandler(authorizedClientService)\n\t\trequestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler)\n\n\t\treturn RestClient.builder()\n\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\n[[oauth2-client-rest-client-interface]]\n=== HTTP Service Clients\n\nSpring Security's OAuth support integrates with xref:features/integrations/rest/http-service-client.adoc[].\n\n[[oauth2-client-web-client]]\n== [[oauth2Client-webclient-servlet]]WebClient Integration for Servlet Environments\n\nThe OAuth 2.0 Client support integrates with `WebClient` by using an `ExchangeFilterFunction`.\n\nThe `ServletOAuth2AuthorizedClientExchangeFilterFunction` provides a mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.\nIt directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] and, therefore, inherits the following capabilities:\n\n* An `OAuth2AccessToken` is requested if the client has not yet been authorized.\n** `authorization_code`: Triggers the Authorization Request redirect to initiate the flow.\n** `client_credentials`: The access token is obtained directly from the Token Endpoint.\n* If the `OAuth2AccessToken` is expired, it is refreshed (or renewed) if an `OAuth2AuthorizedClientProvider` is available to perform the authorization\n\nThe following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nWebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\tServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =\n\t\t\tnew ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\treturn WebClient.builder()\n\t\t\t.apply(oauth2Client.oauth2Configuration())\n\t\t\t.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {\n    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n    return WebClient.builder()\n            .apply(oauth2Client.oauth2Configuration())\n            .build()\n}\n----\n======\n\n[[oauth2-client-web-client-authorized-client]]\n=== Providing the Authorized Client\n\nThe `ServletOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).\n\nThe following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/\")\npublic String index(@RegisteredOAuth2AuthorizedClient(\"okta\") OAuth2AuthorizedClient authorizedClient) {\n\tString resourceUri = ...\n\n\tString body = webClient\n\t\t\t.get()\n\t\t\t.uri(resourceUri)\n\t\t\t.attributes(oauth2AuthorizedClient(authorizedClient))   <1>\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t.block();\n\n\t...\n\n\treturn \"index\";\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/\")\nfun index(@RegisteredOAuth2AuthorizedClient(\"okta\") authorizedClient: OAuth2AuthorizedClient): String {\n    val resourceUri: String = ...\n    val body: String = webClient\n            .get()\n            .uri(resourceUri)\n            .attributes(oauth2AuthorizedClient(authorizedClient)) <1>\n            .retrieve()\n            .bodyToMono()\n            .block()\n\n    ...\n\n    return \"index\"\n}\n----\n======\n<1> `oauth2AuthorizedClient()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.\n\nThe following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/\")\npublic String index() {\n\tString resourceUri = ...\n\n\tString body = webClient\n\t\t\t.get()\n\t\t\t.uri(resourceUri)\n\t\t\t.attributes(clientRegistrationId(\"okta\"))   <1>\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t.block();\n\n\t...\n\n\treturn \"index\";\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/\")\nfun index(): String {\n    val resourceUri: String = ...\n\n    val body: String = webClient\n            .get()\n            .uri(resourceUri)\n            .attributes(clientRegistrationId(\"okta\"))  <1>\n            .retrieve()\n            .bodyToMono()\n            .block()\n\n    ...\n\n    return \"index\"\n}\n----\n======\n<1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.\n\nThe following code shows how to set an `Authentication` as a request attribute:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/\")\npublic String index() {\n\tString resourceUri = ...\n\n\tAuthentication anonymousAuthentication = new AnonymousAuthenticationToken(\n\t\t\t\"anonymous\", \"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\tString body = webClient\n\t\t\t.get()\n\t\t\t.uri(resourceUri)\n\t\t\t.attributes(authentication(anonymousAuthentication))   <1>\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t.block();\n\n\t...\n\n\treturn \"index\";\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/\")\nfun index(): String {\n    val resourceUri: String = ...\n\n    val anonymousAuthentication: Authentication = AnonymousAuthenticationToken(\n            \"anonymous\", \"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"))\n    val body: String = webClient\n            .get()\n            .uri(resourceUri)\n            .attributes(authentication(anonymousAuthentication))  <1>\n            .retrieve()\n            .bodyToMono()\n            .block()\n\n    ...\n\n    return \"index\"\n}\n----\n======\n<1> `authentication()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.\n\n[WARNING]\n====\nIt is recommended to be cautious with this feature since all HTTP requests will receive an access token bound to the provided principal.\n====\n\n[[oauth2-client-web-client-default-authorized-client]]\n=== Defaulting the Authorized Client\n\nIf neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServletOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use, depending on its configuration.\n\nIf `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated by using `HttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.\n\nThe following code shows the specific configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nWebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\tServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =\n\t\t\tnew ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\toauth2Client.setDefaultOAuth2AuthorizedClient(true);\n\treturn WebClient.builder()\n\t\t\t.apply(oauth2Client.oauth2Configuration())\n\t\t\t.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {\n    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n    oauth2Client.setDefaultOAuth2AuthorizedClient(true)\n    return WebClient.builder()\n            .apply(oauth2Client.oauth2Configuration())\n            .build()\n}\n----\n======\n\n[WARNING]\n====\nBe cautious with this feature, since all HTTP requests receive the access token.\n====\n\nAlternatively, if `setDefaultClientRegistrationId(\"okta\")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.\n\nThe following code shows the specific configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nWebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\tServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =\n\t\t\tnew ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\toauth2Client.setDefaultClientRegistrationId(\"okta\");\n\treturn WebClient.builder()\n\t\t\t.apply(oauth2Client.oauth2Configuration())\n\t\t\t.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {\n    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n    oauth2Client.setDefaultClientRegistrationId(\"okta\")\n    return WebClient.builder()\n            .apply(oauth2Client.oauth2Configuration())\n            .build()\n}\n----\n======\n\n[WARNING]\n====\nBe cautious with this feature, since all HTTP requests receive the access token.\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/client/client-authentication.adoc",
    "content": "[[oauth2-client-authentication]]\n= [[oauth2Client-client-auth-support]]Client Authentication Support\n\n[[oauth2-client-authentication-client-credentials]]\n== [[oauth2Client-client-credentials-auth]]Client Credentials\n\n[[oauth2-client-authentication-client-credentials-client-secret-basic]]\n=== Authenticate using `client_secret_basic`\n\nClient Authentication with HTTP Basic is supported out of the box and no customization is necessary to enable it.\nThe default implementation is provided by `DefaultOAuth2TokenRequestHeadersConverter`.\n\nGiven the following Spring Boot properties for an OAuth 2.0 client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: client-id\n            client-secret: client-secret\n            client-authentication-method: client_secret_basic\n            authorization-grant-type: authorization_code\n            ...\n----\n\nThe following example shows how to configure `RestClientAuthorizationCodeTokenResponseClient` to disable URL encoding of the client credentials:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nDefaultOAuth2TokenRequestHeadersConverter<OAuth2AuthorizationCodeGrantRequest> headersConverter =\n\t\tnew DefaultOAuth2TokenRequestHeadersConverter<>();\nheadersConverter.setEncodeClientCredentials(false);\n\nRestClientAuthorizationCodeTokenResponseClient tokenResponseClient =\n\t\tnew RestClientAuthorizationCodeTokenResponseClient();\ntokenResponseClient.setHeadersConverter(headersConverter);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval headersConverter = DefaultOAuth2TokenRequestHeadersConverter<OAuth2AuthorizationCodeGrantRequest>()\nheadersConverter.setEncodeClientCredentials(false)\n\nval tokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()\ntokenResponseClient.setHeadersConverter(headersConverter)\n----\n======\n\n[[oauth2-client-authentication-client-credentials-client-secret-post]]\n=== Authenticate using `client_secret_post`\n\nClient Authentication with client credentials included in the request-body is supported out of the box and no customization is necessary to enable it.\n\nThe following Spring Boot properties for an OAuth 2.0 client registration demonstrate the configuration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: client-id\n            client-secret: client-secret\n            client-authentication-method: client_secret_post\n            authorization-grant-type: authorization_code\n            ...\n----\n\n[[oauth2-client-authentication-jwt-bearer]]\n== [[oauth2Client-jwt-bearer-auth]]JWT Bearer\n\n[NOTE]\n====\nPlease refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on https://datatracker.ietf.org/doc/html/rfc7523#section-2.2[JWT Bearer] Client Authentication.\n====\n\nThe default implementation for JWT Bearer Client Authentication is `NimbusJwtClientAuthenticationParametersConverter`,\nwhich is a `Converter` that customizes the Token Request parameters by adding\na signed JSON Web Token (JWS) in the `client_assertion` parameter.\n\nThe `java.security.PrivateKey` or `javax.crypto.SecretKey` used for signing the JWS\nis supplied by the `com.nimbusds.jose.jwk.JWK` resolver associated with `NimbusJwtClientAuthenticationParametersConverter`.\n\n[[oauth2-client-authentication-jwt-bearer-private-key-jwt]]\n=== Authenticate using `private_key_jwt`\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-authentication-method: private_key_jwt\n            authorization-grant-type: authorization_code\n            ...\n----\n\nThe following example shows how to configure `RestClientAuthorizationCodeTokenResponseClient`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nFunction<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {\n\tif (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {\n\t\t// Assuming RSA key type\n\t\tRSAPublicKey publicKey = ...\n\t\tRSAPrivateKey privateKey = ...\n\t\treturn new RSAKey.Builder(publicKey)\n\t\t\t\t.privateKey(privateKey)\n\t\t\t\t.keyID(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t}\n\treturn null;\n};\n\nRestClientAuthorizationCodeTokenResponseClient tokenResponseClient =\n\t\tnew RestClientAuthorizationCodeTokenResponseClient();\ntokenResponseClient.addParametersConverter(\n\t\tnew NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwkResolver: Function<ClientRegistration, JWK> =\n    Function<ClientRegistration, JWK> { clientRegistration ->\n        if (clientRegistration.clientAuthenticationMethod.equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {\n            // Assuming RSA key type\n            var publicKey: RSAPublicKey\n            var privateKey: RSAPrivateKey\n            RSAKey.Builder(publicKey) = //...\n                .privateKey(privateKey) = //...\n                .keyID(UUID.randomUUID().toString())\n                .build()\n        }\n        null\n    }\n\nval tokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()\ntokenResponseClient.addParametersConverter(\n    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)\n)\n----\n======\n\n[[oauth2-client-authentication-jwt-bearer-client-secret-jwt]]\n=== Authenticate using `client_secret_jwt`\n\nGiven the following Spring Boot properties for an OAuth 2.0 Client registration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            client-authentication-method: client_secret_jwt\n            authorization-grant-type: client_credentials\n            ...\n----\n\nThe following example shows how to configure `RestClientClientCredentialsTokenResponseClient`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nFunction<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {\n\tif (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {\n\t\tSecretKeySpec secretKey = new SecretKeySpec(\n\t\t\t\tclientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\"HmacSHA256\");\n\t\treturn new OctetSequenceKey.Builder(secretKey)\n\t\t\t\t.keyID(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t}\n\treturn null;\n};\n\nRestClientClientCredentialsTokenResponseClient tokenResponseClient =\n\t\tnew RestClientClientCredentialsTokenResponseClient();\ntokenResponseClient.addParametersConverter(\n\t\tnew NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwkResolver = Function<ClientRegistration, JWK?> { clientRegistration: ClientRegistration ->\n    if (clientRegistration.clientAuthenticationMethod == ClientAuthenticationMethod.CLIENT_SECRET_JWT) {\n        val secretKey = SecretKeySpec(\n            clientRegistration.clientSecret.toByteArray(StandardCharsets.UTF_8),\n            \"HmacSHA256\"\n        )\n        OctetSequenceKey.Builder(secretKey)\n            .keyID(UUID.randomUUID().toString())\n            .build()\n    }\n    null\n}\n\nval tokenResponseClient = RestClientClientCredentialsTokenResponseClient()\ntokenResponseClient.addParametersConverter(\n    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)\n)\n----\n======\n\n[[oauth2-client-authentication-jwt-bearer-assertion]]\n=== Customizing the JWT assertion\n\nThe JWT produced by `NimbusJwtClientAuthenticationParametersConverter` contains the `iss`, `sub`, `aud`, `jti`, `iat` and `exp` claims by default. You can customize the headers and/or claims by providing a `Consumer<NimbusJwtClientAuthenticationParametersConverter.JwtClientAuthenticationContext<T>>` to `setJwtClientAssertionCustomizer()`. The following example shows how to customize claims of the JWT:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nFunction<ClientRegistration, JWK> jwkResolver = ...\n\nNimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> converter =\n\t\tnew NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver);\nconverter.setJwtClientAssertionCustomizer((context) -> {\n\tcontext.getHeaders().header(\"custom-header\", \"header-value\");\n\tcontext.getClaims().claim(\"custom-claim\", \"claim-value\");\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwkResolver = ...\n\nval converter: NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> =\n    NimbusJwtClientAuthenticationParametersConverter(jwkResolver)\nconverter.setJwtClientAssertionCustomizer { context ->\n    context.headers.header(\"custom-header\", \"header-value\")\n    context.claims.claim(\"custom-claim\", \"claim-value\")\n}\n----\n======\n\n[[oauth2-client-authentication-public]]\n== [[oauth2Client-public-auth]]Public Authentication\n\nPublic Client Authentication is supported out of the box and no customization is necessary to enable it.\n\nThe following Spring Boot properties for an OAuth 2.0 client registration demonstrate the configuration:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: client-id\n            client-authentication-method: none\n            authorization-grant-type: authorization_code\n            ...\n----\n\n[NOTE]\n====\nPublic Clients are supported using https://tools.ietf.org/html/rfc7636[Proof Key for Code Exchange] (PKCE).\nPKCE will automatically be used when `client-authentication-method` is set to \"none\" (`ClientAuthenticationMethod.NONE`).\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/client/core.adoc",
    "content": "[[oauth2Client-core-interface-class]]\n= Core Interfaces and Classes\n\nThis section describes the OAuth2 core interfaces and classes that Spring Security offers.\n\n[[oauth2Client-client-registration]]\n== ClientRegistration\n\n`ClientRegistration` is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.\n\nA `ClientRegistration` object holds information, such as client id, client secret, authorization grant type, redirect URI, scope(s), authorization URI, token URI, and other details.\n\n`ClientRegistration` and its properties are defined as follows:\n\n[source,java]\n----\npublic final class ClientRegistration {\n\tprivate String registrationId;\t<1>\n\tprivate String clientId;\t<2>\n\tprivate String clientSecret;\t<3>\n\tprivate ClientAuthenticationMethod clientAuthenticationMethod;\t<4>\n\tprivate AuthorizationGrantType authorizationGrantType;\t<5>\n\tprivate String redirectUri;\t<6>\n\tprivate Set<String> scopes;\t<7>\n\tprivate ProviderDetails providerDetails;\n\tprivate String clientName;\t<8>\n\n\tpublic class ProviderDetails {\n\t\tprivate String authorizationUri;\t<9>\n\t\tprivate String tokenUri;\t<10>\n\t\tprivate UserInfoEndpoint userInfoEndpoint;\n\t\tprivate String jwkSetUri;\t<11>\n\t\tprivate String issuerUri;\t<12>\n        private Map<String, Object> configurationMetadata;  <13>\n\n\t\tpublic class UserInfoEndpoint {\n\t\t\tprivate String uri;\t<14>\n            private AuthenticationMethod authenticationMethod;  <15>\n\t\t\tprivate String userNameAttributeName;\t<16>\n\n\t\t}\n\t}\n\n\tpublic static final class ClientSettings {\n\t\tprivate boolean requireProofKey; // <17>\n\t}\n}\n----\n<1> `registrationId`: The ID that uniquely identifies the `ClientRegistration`.\n<2> `clientId`: The client identifier.\n<3> `clientSecret`: The client secret.\n<4> `clientAuthenticationMethod`: The method used to authenticate the Client with the Provider.\nThe supported values are *client_secret_basic*, *client_secret_post*, *private_key_jwt*, *client_secret_jwt* and *none* https://tools.ietf.org/html/rfc6749#section-2.1[(public clients)].\n<5> `authorizationGrantType`: The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types.\n The supported values are `authorization_code`, `client_credentials`, as well as, extension grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`.\n<6> `redirectUri`: The client's registered redirect URI that the _Authorization Server_ redirects the end-user's user-agent\n to after the end-user has authenticated and authorized access to the client.\n<7> `scopes`: The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.\n<8> `clientName`: A descriptive name used for the client.\nThe name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.\n<9> `authorizationUri`: The Authorization Endpoint URI for the Authorization Server.\n<10> `tokenUri`: The Token Endpoint URI for the Authorization Server.\n<11> `jwkSetUri`: The URI used to retrieve the https://tools.ietf.org/html/rfc7517[JSON Web Key (JWK)] Set from the Authorization Server,\nwhich contains the cryptographic key(s) used to verify the https://tools.ietf.org/html/rfc7515[JSON Web Signature (JWS)] of the ID Token and (optionally) the UserInfo Response.\n<12> `issuerUri`: Returns the issuer identifier URI for the OpenID Connect 1.0 provider or the OAuth 2.0 Authorization Server.\n<13> `configurationMetadata`: The https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[OpenID Provider Configuration Information].\nThis information is available only if the Spring Boot property `spring.security.oauth2.client.provider.[providerId].issuerUri` is configured.\n<14> `(userInfoEndpoint)uri`: The UserInfo Endpoint URI used to access the claims and attributes of the authenticated end-user.\n<15> `(userInfoEndpoint)authenticationMethod`: The authentication method used when sending the access token to the UserInfo Endpoint.\nThe supported values are *header*, *form*, and *query*.\n<16> `userNameAttributeName`: The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.\n<17> [[oauth2Client-client-registration-requireProofKey]]`requireProofKey`: If `true` or if `clientAuthenticationMethod` is `none`, then PKCE will be enabled. Defaults to `true` for `authorization_code` grant type and `false` for other grant types.\n\nYou can initially configure a `ClientRegistration` by using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint].\n\n`ClientRegistrations` provides convenience methods for configuring a `ClientRegistration` in this way, as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nClientRegistration clientRegistration =\n    ClientRegistrations.fromIssuerLocation(\"https://idp.example.com/issuer\").build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval clientRegistration = ClientRegistrations.fromIssuerLocation(\"https://idp.example.com/issuer\").build()\n----\n======\n\nThe preceding code queries, in series, `https://idp.example.com/issuer/.well-known/openid-configuration`, `https://idp.example.com/.well-known/openid-configuration/issuer`, and `https://idp.example.com/.well-known/oauth-authorization-server/issuer`, stopping at the first to return a 200 response.\n\nAs an alternative, you can use `ClientRegistrations.fromOidcIssuerLocation()` to query only the OpenID Connect Provider's Configuration endpoint.\n\n[[oauth2Client-client-registration-repo]]\n== ClientRegistrationRepository\n\nThe `ClientRegistrationRepository` serves as a repository for OAuth 2.0 / OpenID Connect 1.0 `ClientRegistration`(s).\n\n[NOTE]\n====\nClient registration information is ultimately stored and owned by the associated Authorization Server.\nThis repository provides the ability to retrieve a subset of the primary client registration information, which is stored with the Authorization Server.\n====\n\nSpring Boot auto-configuration binds each of the properties under `spring.security.oauth2.client.registration._[registrationId]_` to an instance of `ClientRegistration` and then composes each of the `ClientRegistration` instance(s) within a `ClientRegistrationRepository`.\n\n[NOTE]\n====\nThe default implementation of `ClientRegistrationRepository` is `InMemoryClientRegistrationRepository`.\n====\n\nThe auto-configuration also registers the `ClientRegistrationRepository` as a `@Bean` in the `ApplicationContext` so that it is available for dependency injection, if needed by the application.\n\nThe following listing shows an example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class OAuth2ClientController {\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@GetMapping(\"/\")\n\tpublic String index() {\n\t\tClientRegistration oktaRegistration =\n\t\t\tthis.clientRegistrationRepository.findByRegistrationId(\"okta\");\n\n\t\t...\n\n\t\treturn \"index\";\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass OAuth2ClientController {\n\n    @Autowired\n    private lateinit var clientRegistrationRepository: ClientRegistrationRepository\n\n    @GetMapping(\"/\")\n    fun index(): String {\n        val oktaRegistration =\n                this.clientRegistrationRepository.findByRegistrationId(\"okta\")\n\n        //...\n\n        return \"index\";\n    }\n}\n----\n======\n\n[[oauth2Client-authorized-client]]\n== OAuth2AuthorizedClient\n\n`OAuth2AuthorizedClient` is a representation of an Authorized Client.\nA client is considered to be authorized when the end-user (the Resource Owner) has granted authorization to the client to access its protected resources.\n\n`OAuth2AuthorizedClient` serves the purpose of associating an `OAuth2AccessToken` (and optional `OAuth2RefreshToken`) to a `ClientRegistration` (client) and resource owner, who is the `Principal` end-user that granted the authorization.\n\n\n[[oauth2Client-authorized-repo-service]]\n== OAuth2AuthorizedClientRepository and OAuth2AuthorizedClientService\n\n`OAuth2AuthorizedClientRepository` is responsible for persisting `OAuth2AuthorizedClient`(s) between web requests, whereas the primary role of `OAuth2AuthorizedClientService` is to manage `OAuth2AuthorizedClient`(s) at the application-level.\n\nFrom a developer perspective, the `OAuth2AuthorizedClientRepository` or `OAuth2AuthorizedClientService` provides the ability to look up an `OAuth2AccessToken` associated with a client so that it can be used to initiate a protected resource request.\n\nThe following listing shows an example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class OAuth2ClientController {\n\n    @Autowired\n    private OAuth2AuthorizedClientService authorizedClientService;\n\n    @GetMapping(\"/\")\n    public String index(Authentication authentication) {\n        OAuth2AuthorizedClient authorizedClient =\n            this.authorizedClientService.loadAuthorizedClient(\"okta\", authentication.getName());\n\n        OAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\n        ...\n\n        return \"index\";\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass OAuth2ClientController {\n\n    @Autowired\n    private lateinit var authorizedClientService: OAuth2AuthorizedClientService\n\n    @GetMapping(\"/\")\n    fun index(authentication: Authentication): String {\n        val authorizedClient: OAuth2AuthorizedClient =\n            this.authorizedClientService.loadAuthorizedClient(\"okta\", authentication.getName());\n        val accessToken = authorizedClient.accessToken\n\n        ...\n\n        return \"index\";\n    }\n}\n----\n======\n\n[NOTE]\n====\nSpring Boot auto-configuration registers an `OAuth2AuthorizedClientRepository` or an `OAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext`.\nHowever, the application can override and register a custom `OAuth2AuthorizedClientRepository` or `OAuth2AuthorizedClientService` `@Bean`.\n====\n\nThe default implementation of `OAuth2AuthorizedClientService` is `InMemoryOAuth2AuthorizedClientService`, which stores `OAuth2AuthorizedClient` objects in-memory.\n\nAlternatively, you can configure the JDBC implementation `JdbcOAuth2AuthorizedClientService` to persist `OAuth2AuthorizedClient` instances in a database.\n\n[NOTE]\n====\n`JdbcOAuth2AuthorizedClientService` depends on the table definition described in xref:servlet/appendix/database-schema.adoc#dbschema-oauth2-client[ OAuth 2.0 Client Schema].\n====\n\n\n[[oauth2Client-authorized-manager-provider]]\n== OAuth2AuthorizedClientManager and OAuth2AuthorizedClientProvider\n\nThe `OAuth2AuthorizedClientManager` is responsible for the overall management of `OAuth2AuthorizedClient`(s).\n\nThe primary responsibilities include:\n\n* Authorizing (or re-authorizing) an OAuth 2.0 Client, by using an `OAuth2AuthorizedClientProvider`.\n* Delegating the persistence of an `OAuth2AuthorizedClient`, typically by using an `OAuth2AuthorizedClientService` or `OAuth2AuthorizedClientRepository`.\n* Delegating to an `OAuth2AuthorizationSuccessHandler` when an OAuth 2.0 Client has been successfully authorized (or re-authorized).\n* Delegating to an `OAuth2AuthorizationFailureHandler` when an OAuth 2.0 Client fails to authorize (or re-authorize).\n\nAn `OAuth2AuthorizedClientProvider` implements a strategy for authorizing (or re-authorizing) an OAuth 2.0 Client.\nImplementations typically implement an authorization grant type, such as `authorization_code`, `client_credentials`, and others.\n\nThe default implementation of `OAuth2AuthorizedClientManager` is `DefaultOAuth2AuthorizedClientManager`, which is associated with an `OAuth2AuthorizedClientProvider` that may support multiple authorization grant types using a delegation-based composite.\nYou can use `OAuth2AuthorizedClientProviderBuilder` to configure and build the delegation-based composite.\n\nThe following code shows an example of how to configure and build an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token` and `client_credentials` authorization grant types:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.authorizationCode()\n\t\t\t\t\t.refreshToken()\n\t\t\t\t\t.clientCredentials()\n\t\t\t\t\t.build();\n\n\tDefaultOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ClientRegistrationRepository,\n        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {\n    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n            .authorizationCode()\n            .refreshToken()\n            .clientCredentials()\n            .build()\n    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n\nWhen an authorization attempt succeeds, the `DefaultOAuth2AuthorizedClientManager` delegates to the `OAuth2AuthorizationSuccessHandler`, which (by default) saves the `OAuth2AuthorizedClient` through the `OAuth2AuthorizedClientRepository`.\nIn the case of a re-authorization failure (for example, a refresh token is no longer valid), the previously saved `OAuth2AuthorizedClient` is removed from the `OAuth2AuthorizedClientRepository` through the `RemoveAuthorizedClientOAuth2AuthorizationFailureHandler`.\nYou can customize the default behavior through `setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler)` and `setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)`.\n\nThe `DefaultOAuth2AuthorizedClientManager` is also associated with a `contextAttributesMapper` of type `Function<OAuth2AuthorizeRequest, Map<String, Object>>`, which is responsible for mapping attribute(s) from the `OAuth2AuthorizeRequest` to a `Map` of attributes to be associated to the `OAuth2AuthorizationContext`.\nThis can be useful when you need to supply an `OAuth2AuthorizedClientProvider` with required (supported) attribute(s).\n\nThe following code shows an example of the `contextAttributesMapper`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.authorizationCode()\n\t\t\t\t\t.refreshToken()\n\t\t\t\t\t.build();\n\n\tDefaultOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t// Assuming the attributes are supplied as `HttpServletRequest` parameters,\n\t// map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`\n\tauthorizedClientManager.setContextAttributesMapper(contextAttributesMapper());\n\n\treturn authorizedClientManager;\n}\n\nprivate Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper() {\n\treturn authorizeRequest -> {\n\t\tMap<String, Object> contextAttributes = Collections.emptyMap();\n\t\tHttpServletRequest servletRequest = authorizeRequest.getAttribute(HttpServletRequest.class.getName());\n\t\tString param1 = servletRequest.getParameter(\"param1\");\n\t\tString param2 = servletRequest.getParameter(\"param2\");\n\t\tif (StringUtils.hasText(param1) && StringUtils.hasText(param2)) {\n\t\t\tcontextAttributes = new HashMap<>();\n\t\t\tcontextAttributes.put(\"param1\", param1);\n\t\t\tcontextAttributes.put(\"param2\", param2);\n\t\t}\n\t\treturn contextAttributes;\n\t};\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ClientRegistrationRepository,\n        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {\n    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n            .authorizationCode()\n            .refreshToken()\n            .build()\n    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n\n\t// Assuming the attributes are supplied as `HttpServletRequest` parameters,\n\t// map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`\n    authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())\n    return authorizedClientManager\n}\n\nprivate fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableMap<String, Any>> {\n    return Function { authorizeRequest ->\n        var contextAttributes: MutableMap<String, Any> = mutableMapOf()\n        val servletRequest: HttpServletRequest = authorizeRequest.getAttribute(HttpServletRequest::class.java.name)\n        val param1: String = servletRequest.getParameter(\"param1\")\n        val param2: String = servletRequest.getParameter(\"param2\")\n        if (StringUtils.hasText(param1) && StringUtils.hasText(param2)) {\n            contextAttributes = hashMapOf()\n            contextAttributes[\"param1\"] = param1\n            contextAttributes[\"param2\"] = param2\n        }\n        contextAttributes\n    }\n}\n----\n======\n\nThe `DefaultOAuth2AuthorizedClientManager` is designed to be used _within_ the context of a `HttpServletRequest`.\nWhen operating _outside_ of a `HttpServletRequest` context, use `AuthorizedClientServiceOAuth2AuthorizedClientManager` instead.\n\nA service application is a common use case for when to use an `AuthorizedClientServiceOAuth2AuthorizedClientManager`.\nService applications often run in the background, without any user interaction, and typically run under a system-level account instead of a user account.\nAn OAuth 2.0 Client configured with the `client_credentials` grant type can be considered a type of service application.\n\nThe following code shows an example of how to configure an `AuthorizedClientServiceOAuth2AuthorizedClientManager` that provides support for the `client_credentials` grant type:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\tOAuth2AuthorizedClientService authorizedClientService) {\n\n\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.clientCredentials()\n\t\t\t\t\t.build();\n\n\tAuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew AuthorizedClientServiceOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientService);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ClientRegistrationRepository,\n        authorizedClientService: OAuth2AuthorizedClientService): OAuth2AuthorizedClientManager {\n    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n            .clientCredentials()\n            .build()\n    val authorizedClientManager = AuthorizedClientServiceOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientService)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/client/index.adoc",
    "content": "[[oauth2-client]]\n= OAuth 2.0 Client\n:page-section-summary-toc: 1\n\nThe OAuth 2.0 Client features provide support for the Client role as defined in the https://tools.ietf.org/html/rfc6749#section-1.1[OAuth 2.0 Authorization Framework].\n\nAt a high-level, the core features available are:\n\n.Authorization Grant support\n* xref:servlet/oauth2/client/authorization-grants.adoc#oauth2-client-authorization-code[Authorization Code]\n* xref:servlet/oauth2/client/authorization-grants.adoc#oauth2-client-refresh-token[Refresh Token]\n* xref:servlet/oauth2/client/authorization-grants.adoc#oauth2-client-client-credentials[Client Credentials]\n* xref:servlet/oauth2/client/authorization-grants.adoc#oauth2-client-jwt-bearer[JWT Bearer]\n* xref:servlet/oauth2/client/authorization-grants.adoc#oauth2-client-token-exchange[Token Exchange]\n\n.Client Authentication support\n* xref:servlet/oauth2/client/client-authentication.adoc#oauth2-client-authentication-jwt-bearer[JWT Bearer]\n\n.HTTP Client support (for requesting protected resources)\n* xref:servlet/oauth2/client/authorized-clients.adoc#oauth2-client-rest-client[`RestClient` integration]\n* xref:servlet/oauth2/client/authorized-clients.adoc#oauth2-client-web-client[`WebClient` integration for Servlet Environments]\n\nThe `HttpSecurity.oauth2Client()` DSL provides a number of configuration options for customizing the core components used by OAuth 2.0 Client.\nIn addition, `HttpSecurity.oauth2Client().authorizationCodeGrant()` enables the customization of the Authorization Code grant.\n\nThe following code shows the complete configuration options provided by the `HttpSecurity.oauth2Client()` DSL:\n\n.OAuth2 Client Configuration Options\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2ClientSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Client((oauth2) -> oauth2\n\t\t\t\t.clientRegistrationRepository(this.clientRegistrationRepository())\n\t\t\t\t.authorizedClientRepository(this.authorizedClientRepository())\n\t\t\t\t.authorizedClientService(this.authorizedClientService())\n\t\t\t\t.authorizationCodeGrant((codeGrant) -> codeGrant\n\t\t\t\t\t.authorizationRequestRepository(this.authorizationRequestRepository())\n\t\t\t\t\t.authorizationRequestResolver(this.authorizationRequestResolver())\n\t\t\t\t\t.accessTokenResponseClient(this.accessTokenResponseClient())\n\t\t\t\t)\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2ClientSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Client {\n                clientRegistrationRepository = clientRegistrationRepository()\n                authorizedClientRepository = authorizedClientRepository()\n                authorizedClientService = authorizedClientService()\n                authorizationCodeGrant {\n                    authorizationRequestRepository = authorizationRequestRepository()\n                    authorizationRequestResolver = authorizationRequestResolver()\n                    accessTokenResponseClient = accessTokenResponseClient()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nIn addition to the `HttpSecurity.oauth2Client()` DSL, XML configuration is also supported.\n\nThe following code shows the complete configuration options available in the xref:servlet/appendix/namespace/http.adoc#nsa-oauth2-client[ security namespace]:\n\n.OAuth2 Client XML Configuration Options\n[source,xml]\n----\n<http>\n\t<oauth2-client client-registration-repository-ref=\"clientRegistrationRepository\"\n\t\t\t\t   authorized-client-repository-ref=\"authorizedClientRepository\"\n\t\t\t\t   authorized-client-service-ref=\"authorizedClientService\">\n\t\t<authorization-code-grant\n\t\t\t\tauthorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\tauthorization-request-resolver-ref=\"authorizationRequestResolver\"\n\t\t\t\taccess-token-response-client-ref=\"accessTokenResponseClient\"/>\n\t</oauth2-client>\n</http>\n----\n\nThe `OAuth2AuthorizedClientManager` is responsible for managing the authorization (or re-authorization) of an OAuth 2.0 Client, in collaboration with one or more `OAuth2AuthorizedClientProvider`(s).\n\nThe following code shows an example of how to register an `OAuth2AuthorizedClientManager` `@Bean` and associate it with an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token` and `client_credentials` authorization grant types:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t\t.authorizationCode()\n\t\t\t\t\t.refreshToken()\n\t\t\t\t\t.clientCredentials()\n\t\t\t\t\t.build();\n\n\tDefaultOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultOAuth2AuthorizedClientManager(\n\t\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\treturn authorizedClientManager;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authorizedClientManager(\n        clientRegistrationRepository: ClientRegistrationRepository,\n        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {\n    val authorizedClientProvider: OAuth2AuthorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n            .authorizationCode()\n            .refreshToken()\n            .clientCredentials()\n            .build()\n    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(\n            clientRegistrationRepository, authorizedClientRepository)\n    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n    return authorizedClientManager\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/index.adoc",
    "content": "= OAuth2\n\nSpring Security provides comprehensive OAuth 2.0 support.\nThis section discusses how to integrate OAuth 2.0 into your servlet based application.\n\n[[oauth2-overview]]\n== Overview\n\nSpring Security's OAuth 2.0 support consists of three primary feature sets:\n\n* <<oauth2-resource-server>>\n* <<oauth2-client>>\n* xref:servlet/oauth2/authorization-server/index.adoc[OAuth2 Authorization Server]\n\n[NOTE]\n====\n<<oauth2-client-log-users-in,OAuth2 Login>> is a very powerful OAuth2 Client feature that deserves its own section in the reference documentation.\nHowever, it does not exist as a standalone feature and requires OAuth2 Client in order to function.\n====\n\nThese feature sets cover the _resource server_, _client_ and _authorization server_ roles defined in the https://tools.ietf.org/html/rfc6749#section-1.1[OAuth 2.0 Authorization Framework].\n\nThe _resource server_ and _client_ roles in OAuth2 are typically represented by one or more server-side applications.\nAdditionally, the _authorization server_ role can be represented by one or more third parties (as is the case when centralizing identity management and/or authentication within an organization) *-or-* it can be represented by an application (as is the case with the _authorization server_ feature).\n\nFor example, a typical OAuth2-based microservices architecture might consist of a single user-facing client application, several backend resource servers providing REST APIs and a third party authorization server for managing users and authentication concerns.\nIt is also common to have a single application representing only one of these roles with the need to integrate with one or more third parties that are providing the other roles.\n\nSpring Security handles these scenarios and more.\nThe following sections cover the roles provided by Spring Security and contain examples for common scenarios.\n\n[[oauth2-resource-server]]\n== OAuth2 Resource Server\n\n[NOTE]\n====\nThis section contains a summary of OAuth2 Resource Server features with examples.\nSee xref:servlet/oauth2/resource-server/index.adoc[OAuth 2.0 Resource Server] for complete reference documentation.\n====\n\nTo get started, add the `spring-security-oauth2-resource-server` dependency to your project.\nWhen using Spring Boot, add the following starter:\n\n.OAuth2 Resource Server with Spring Boot\n[tabs]\n======\nGradle::\n+\n[source,gradle,role=\"primary\"]\n----\nimplementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'\n----\n\nMaven::\n+\n[source,maven,role=\"secondary\"]\n----\n<dependency>\n\t<groupId>org.springframework.boot</groupId>\n\t<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>\n</dependency>\n----\n======\n\n[TIP]\n====\nSee xref:getting-spring-security.adoc[] for additional options when not using Spring Boot.\n====\n\nConsider the following use cases for OAuth2 Resource Server:\n\n* I want to <<oauth2-resource-server-access-token,protect access to the API using OAuth2>> (authorization server provides JWT or opaque access token)\n* I want to <<oauth2-resource-server-custom-jwt,protect access to the API using a JWT>> (custom token)\n\n[[oauth2-resource-server-access-token]]\n=== Protect Access with an OAuth2 Access Token\n\nIt is very common to protect access to an API using OAuth2 access tokens.\nIn most cases, Spring Security requires only minimal configuration to secure an application with OAuth2.\n\nThere are two types of `Bearer` tokens supported by Spring Security which each use a different component for validation:\n\n* <<oauth2-resource-server-access-token-jwt,JWT support>> uses a `JwtDecoder` bean to validate signatures and decode tokens\n* <<oauth2-resource-server-access-token-opaque,Opaque token support>> uses an `OpaqueTokenIntrospector` bean to introspect tokens\n\n[[oauth2-resource-server-access-token-jwt]]\n==== JWT Support\n\nThe following example configures a `JwtDecoder` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          issuer-uri: https://my-auth-server.com\n----\n\nWhen using Spring Boot, this is all that is required.\nThe default arrangement provided by Spring Boot is equivalent to the following:\n\n.Configure Resource Server with JWTs\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t.jwt(Customizer.withDefaults())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic JwtDecoder jwtDecoder() {\n\t\treturn JwtDecoders.fromIssuerLocation(\"https://my-auth-server.com\");\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, authenticated)\n\t\t\t}\n\t\t\toauth2ResourceServer {\n\t\t\t\tjwt { }\n\t\t\t}\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun jwtDecoder(): JwtDecoder {\n\t\treturn JwtDecoders.fromIssuerLocation(\"https://my-auth-server.com\")\n\t}\n\n}\n----\n=====\n\n[[oauth2-resource-server-access-token-opaque]]\n==== Opaque Token Support\n\nThe following example configures an `OpaqueTokenIntrospector` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        opaquetoken:\n          introspection-uri: https://my-auth-server.com/oauth2/introspect\n          client-id: my-client-id\n          client-secret: my-client-secret\n----\n\nWhen using Spring Boot, this is all that is required.\nThe default arrangement provided by Spring Boot is equivalent to the following:\n\n.Configure Resource Server with Opaque Tokens\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t.opaqueToken(Customizer.withDefaults())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic OpaqueTokenIntrospector opaqueTokenIntrospector() {\n\t\treturn new SpringOpaqueTokenIntrospector(\n\t\t\t\"https://my-auth-server.com/oauth2/introspect\", \"my-client-id\", \"my-client-secret\");\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, authenticated)\n\t\t\t}\n\t\t\toauth2ResourceServer {\n\t\t\t\topaqueToken { }\n\t\t\t}\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun opaqueTokenIntrospector(): OpaqueTokenIntrospector {\n\t\treturn SpringOpaqueTokenIntrospector(\n\t\t\t\"https://my-auth-server.com/oauth2/introspect\", \"my-client-id\", \"my-client-secret\"\n\t\t)\n\t}\n\n}\n----\n=====\n\n[[oauth2-resource-server-custom-jwt]]\n=== Protect Access with a custom JWT\n\nIt is a fairly common goal to protect access to an API using JWTs, particularly when the frontend is developed as a single-page application.\nThe OAuth2 Resource Server support in Spring Security can be used for any type of `Bearer` token, including a custom JWT.\n\nAll that is required to protect an API using JWTs is a `JwtDecoder` bean, which is used to validate signatures and decode tokens.\nSpring Security will automatically use the provided bean to configure protection within the `SecurityFilterChain`.\n\nThe following example configures a `JwtDecoder` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          public-key-location: classpath:my-public-key.pub\n----\n\n[NOTE]\n====\nYou can provide the public key as a classpath resource (called `my-public-key.pub` in this example).\n====\n\nWhen using Spring Boot, this is all that is required.\nThe default arrangement provided by Spring Boot is equivalent to the following:\n\n.Configure Resource Server with Custom JWTs\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2ResourceServer((oauth2) -> oauth2\n\t\t\t\t.jwt(Customizer.withDefaults())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic JwtDecoder jwtDecoder() {\n\t\treturn NimbusJwtDecoder.withPublicKey(publicKey()).build();\n\t}\n\n\tprivate RSAPublicKey publicKey() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, authenticated)\n\t\t\t}\n\t\t\toauth2ResourceServer {\n\t\t\t\tjwt { }\n\t\t\t}\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun jwtDecoder(): JwtDecoder {\n\t\treturn NimbusJwtDecoder.withPublicKey(publicKey()).build()\n\t}\n\n\tprivate fun publicKey(): RSAPublicKey {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\n[NOTE]\n====\nSpring Security does not provide an endpoint for minting tokens.\nHowever, Spring Security does provide the `JwtEncoder` interface along with one implementation, which is `NimbusJwtEncoder`.\n====\n\n[[oauth2-client]]\n== OAuth2 Client\n\n[NOTE]\n====\nThis section contains a summary of OAuth2 Client features with examples.\nSee xref:servlet/oauth2/client/index.adoc[OAuth 2.0 Client] and xref:servlet/oauth2/login/index.adoc[OAuth 2.0 Login] for complete reference documentation.\n====\n\nTo get started, add the `spring-security-oauth2-client` dependency to your project.\nWhen using Spring Boot, add the following starter:\n\n.OAuth2 Client with Spring Boot\n[tabs]\n======\nGradle::\n+\n[source,gradle,role=\"primary\"]\n----\nimplementation 'org.springframework.boot:spring-boot-starter-oauth2-client'\n----\n\nMaven::\n+\n[source,maven,role=\"secondary\"]\n----\n<dependency>\n\t<groupId>org.springframework.boot</groupId>\n\t<artifactId>spring-boot-starter-oauth2-client</artifactId>\n</dependency>\n----\n======\n\n[TIP]\n====\nSee xref:getting-spring-security.adoc[] for additional options when not using Spring Boot.\n====\n\nConsider the following use cases for OAuth2 Client:\n\n* I want to <<oauth2-client-log-users-in,log users in using OAuth 2.0 or OpenID Connect 1.0>>\n* I want to <<oauth2-client-access-protected-resources,use `RestClient` to obtain an access token for users>> in order to access a third-party API\n* I want to <<oauth2-client-access-protected-resources-webclient,use `WebClient` to obtain an access token for users>> in order to access a third-party API\n* I want to <<oauth2-client-access-protected-resources-current-user,do both>> (log users in _and_ access a third-party API)\n* I want to <<oauth2-client-client-credentials,use the `client_credentials` grant type>> to obtain a single token per application\n* I want to <<oauth2-client-enable-extension-grant-type,enable an extension grant type>>\n* I want to <<oauth2-client-customize-existing-grant-type,customize an existing grant type>>\n* I want to <<oauth2-client-customize-request-parameters,customize token request parameters>>\n* I want to <<oauth2-client-customize-rest-client,customize the `RestClient` used by OAuth2 Client components>>\n\n[[oauth2-client-log-users-in]]\n=== Log Users In with OAuth2\n\nIt is very common to require users to log in via OAuth2.\nhttps://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect 1.0] provides a special token called the `id_token` which is designed to provide an OAuth2 Client with the ability to perform user identity verification and log users in.\nIn certain cases, OAuth2 can be used directly to log users in (as is the case with popular social login providers that do not implement OpenID Connect such as GitHub and Facebook).\n\nThe following example configures the application to act as an OAuth2 Client capable of logging users in with OAuth2 or OpenID Connect:\n\n.Configure OAuth2 Login\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.oauth2Login(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\t// ...\n\t\t\toauth2Login { }\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n}\n----\n=====\n\nIn addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean.\nThe following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          my-oidc-client:\n            provider: my-oidc-provider\n            client-id: my-client-id\n            client-secret: my-client-secret\n            authorization-grant-type: authorization_code\n            scope: openid,profile\n        provider:\n          my-oidc-provider:\n            issuer-uri: https://my-oidc-provider.com\n----\n\nWith the above configuration, the application now supports two additional endpoints:\n\n1. The login endpoint (e.g. `/oauth2/authorization/my-oidc-client`) is used to initiate login and perform a redirect to the third party authorization server.\n2. The redirection endpoint (e.g. `/login/oauth2/code/my-oidc-client`) is used by the authorization server to redirect back to the client application, and will contain a `code` parameter used to obtain an `id_token` and/or `access_token` via the access token request.\n\n[NOTE]\n====\nThe presence of the `openid` scope in the above configuration indicates that OpenID Connect 1.0 should be used.\nThis instructs Spring Security to use OIDC-specific components (such as `OidcUserService`) during request processing.\nWithout this scope, Spring Security will use OAuth2-specific components (such as `DefaultOAuth2UserService`) instead.\n====\n\n[[oauth2-client-access-protected-resources]]\n=== Access Protected Resources\n\nMaking requests to a third party API that is protected by OAuth2 is a core use case of OAuth2 Client.\nThis is accomplished by authorizing a client (represented by the `OAuth2AuthorizedClient` class in Spring Security) and accessing protected resources by placing a `Bearer` token in the `Authorization` header of an outbound request.\n\nThe following example configures the application to act as an OAuth2 Client capable of requesting protected resources from a third party API:\n\n.Configure OAuth2 Client\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.oauth2Client(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\t// ...\n\t\t\toauth2Client { }\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n}\n----\n=====\n\n[NOTE]\n====\nThe above example does not provide a way to log users in.\nYou can use any other login mechanism (such as `formLogin()`).\nSee the <<oauth2-client-access-protected-resources-current-user,next section>> for an example combining `oauth2Client()` with `oauth2Login()`.\n====\n\nIn addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean.\nThe following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          my-oauth2-client:\n            provider: my-auth-server\n            client-id: my-client-id\n            client-secret: my-client-secret\n            authorization-grant-type: authorization_code\n            scope: message.read,message.write\n        provider:\n          my-auth-server:\n            issuer-uri: https://my-auth-server.com\n----\n\nIn addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly.\nSpring Security provides implementations of `OAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources.\n\n[TIP]\n====\nSpring Security registers a default `OAuth2AuthorizedClientManager` bean for you when one does not exist.\n====\n\nThe easiest way to use an `OAuth2AuthorizedClientManager` is via a `ClientHttpRequestInterceptor` that intercepts requests through a `RestClient`, which is already available when `spring-web` is on the classpath.\n\nThe following example uses the default `OAuth2AuthorizedClientManager` to configure a `RestClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:\n\n.Configure `RestClient` with `ClientHttpRequestInterceptor`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class RestClientConfig {\n\n\t@Bean\n\tpublic RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tOAuth2ClientHttpRequestInterceptor requestInterceptor =\n\t\t\t\tnew OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n\t\treturn RestClient.builder()\n\t\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass RestClientConfig {\n\n\t@Bean\n\tfun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {\n\t\tval requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)\n\t\treturn RestClient.builder()\n\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\nThis configured `RestClient` can be used as in the following example:\n\n[[oauth2-client-accessing-protected-resources-example]]\n.Use `RestClient` to Access Protected Resources\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId;\n\n@RestController\npublic class MessagesController {\n\n\tprivate final RestClient restClient;\n\n\tpublic MessagesController(RestClient restClient) {\n\t\tthis.restClient = restClient;\n\t}\n\n\t@GetMapping(\"/messages\")\n\tpublic ResponseEntity<List<Message>> messages() {\n\t\tMessage[] messages = this.restClient.get()\n\t\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t\t.attributes(clientRegistrationId(\"my-oauth2-client\"))\n\t\t\t\t.retrieve()\n\t\t\t\t.body(Message[].class);\n\t\treturn ResponseEntity.ok(Arrays.asList(messages));\n\t}\n\n\tpublic record Message(String message) {\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId\nimport org.springframework.web.client.body\n\n@RestController\nclass MessagesController(private val restClient: RestClient) {\n\n\t@GetMapping(\"/messages\")\n\tfun messages(): ResponseEntity<List<Message>> {\n\t\tval messages = restClient.get()\n\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t.attributes(clientRegistrationId(\"my-oauth2-client\"))\n\t\t\t.retrieve()\n\t\t\t.body<Array<Message>>()!!\n\t\t\t.toList()\n\t\treturn ResponseEntity.ok(messages)\n\t}\n\n\tdata class Message(val message: String)\n\n}\n----\n=====\n\n[[oauth2-client-access-protected-resources-webclient]]\n=== Access Protected Resources with `WebClient`\n\nMaking requests to a third party API that is protected by OAuth2 is a core use case of OAuth2 Client.\nThis is accomplished by authorizing a client (represented by the `OAuth2AuthorizedClient` class in Spring Security) and accessing protected resources by placing a `Bearer` token in the `Authorization` header of an outbound request.\n\nThe following example configures the application to act as an OAuth2 Client capable of requesting protected resources from a third party API:\n\n.Configure OAuth2 Client\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.oauth2Client(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\t// ...\n\t\t\toauth2Client { }\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n}\n----\n=====\n\n[NOTE]\n====\nThe above example does not provide a way to log users in.\nYou can use any other login mechanism (such as `formLogin()`).\nSee the <<oauth2-client-access-protected-resources-current-user,previous section>> for an example combining `oauth2Client()` with `oauth2Login()`.\n====\n\nIn addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean.\nThe following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          my-oauth2-client:\n            provider: my-auth-server\n            client-id: my-client-id\n            client-secret: my-client-secret\n            authorization-grant-type: authorization_code\n            scope: message.read,message.write\n        provider:\n          my-auth-server:\n            issuer-uri: https://my-auth-server.com\n----\n\nIn addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly.\nSpring Security provides implementations of `OAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources.\n\n[TIP]\n====\nSpring Security registers a default `OAuth2AuthorizedClientManager` bean for you when one does not exist.\n====\n\n<<oauth2-client-access-protected-resources,Instead of configuring a `RestClient`>>, another way to use an `OAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`.\nTo use `WebClient`, you will need to add the `spring-webflux` dependency along with a reactive client implementation:\n\n.Add Spring WebFlux Dependency\n[tabs]\n======\nGradle::\n+\n[source,gradle,role=\"primary\"]\n----\nimplementation 'org.springframework:spring-webflux'\nimplementation 'io.projectreactor.netty:reactor-netty'\n----\n\nMaven::\n+\n[source,maven,role=\"secondary\"]\n----\n<dependency>\n\t<groupId>org.springframework</groupId>\n\t<artifactId>spring-webflux</artifactId>\n</dependency>\n<dependency>\n\t<groupId>io.projectreactor.netty</groupId>\n\t<artifactId>reactor-netty</artifactId>\n</dependency>\n----\n======\n\nThe following example uses the default `OAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:\n\n.Configure `WebClient` with `ExchangeFilterFunction`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class WebClientConfig {\n\n\t@Bean\n\tpublic WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tServletOAuth2AuthorizedClientExchangeFilterFunction filter =\n\t\t\t\tnew ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\t\treturn WebClient.builder()\n\t\t\t\t.apply(filter.oauth2Configuration())\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass WebClientConfig {\n\n\t@Bean\n\tfun webClient(authorizedClientManager: OAuth2AuthorizedClientManager): WebClient {\n\t\tval filter = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)\n\t\treturn WebClient.builder()\n\t\t\t.apply(filter.oauth2Configuration())\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\nThis configured `WebClient` can be used as in the following example:\n\n.Use `WebClient` to Access Protected Resources\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;\n\n@RestController\npublic class MessagesController {\n\n\tprivate final WebClient webClient;\n\n\tpublic MessagesController(WebClient webClient) {\n\t\tthis.webClient = webClient;\n\t}\n\n\t@GetMapping(\"/messages\")\n\tpublic ResponseEntity<List<Message>> messages() {\n\t\treturn this.webClient.get()\n\t\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t\t.attributes(clientRegistrationId(\"my-oauth2-client\"))\n\t\t\t\t.retrieve()\n\t\t\t\t.toEntityList(Message.class)\n\t\t\t\t.block();\n\t}\n\n\tpublic record Message(String message) {\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId\n\n@RestController\nclass MessagesController(private val webClient: WebClient) {\n\n\t@GetMapping(\"/messages\")\n\tfun messages(): ResponseEntity<List<Message>> {\n\t\treturn webClient.get()\n\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t.attributes(clientRegistrationId(\"my-oauth2-client\"))\n\t\t\t.retrieve()\n\t\t\t.toEntityList<Message>()\n\t\t\t.block()!!\n\t}\n\n\tdata class Message(val message: String)\n\n}\n----\n=====\n\n[[oauth2-client-access-protected-resources-current-user]]\n=== Access Protected Resources for the Current User\n\nWhen a user is logged in via OAuth2 or OpenID Connect, the authorization server may provide an access token that can be used directly to access protected resources.\nThis is convenient because it only requires a single `ClientRegistration` to be configured for both use cases simultaneously.\n\n[NOTE]\n====\nThis section combines <<oauth2-client-log-users-in>> and <<oauth2-client-access-protected-resources>> into a single configuration.\nOther advanced scenarios exist, such as configuring one `ClientRegistration` for login and another for accessing protected resources.\nAll such scenarios would use the same basic configuration.\n====\n\nThe following example configures the application to act as an OAuth2 Client capable of logging the user in _and_ requesting protected resources from a third party API:\n\n.Configure OAuth2 Login and OAuth2 Client\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t.oauth2Client(Customizer.withDefaults());\n\t\treturn http.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\t// ...\n\t\t\toauth2Login { }\n\t\t\toauth2Client { }\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n}\n----\n=====\n\nIn addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean.\nThe following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          my-combined-client:\n            provider: my-auth-server\n            client-id: my-client-id\n            client-secret: my-client-secret\n            authorization-grant-type: authorization_code\n            scope: openid,profile,message.read,message.write\n        provider:\n          my-auth-server:\n            issuer-uri: https://my-auth-server.com\n----\n\n[NOTE]\n====\nThe main difference between the previous examples (<<oauth2-client-log-users-in>>,  <<oauth2-client-access-protected-resources>>) and this one is what is configured via the `scope` property, which combines the standard scopes `openid` and `profile` with the custom scopes `message.read` and `message.write`.\n====\n\nIn addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly.\nSpring Security provides implementations of `OAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources.\n\n[TIP]\n====\nSpring Security registers a default `OAuth2AuthorizedClientManager` bean for you when one does not exist.\n====\n\nThe easiest way to use an `OAuth2AuthorizedClientManager` is via a `ClientHttpRequestInterceptor` that intercepts requests through a `RestClient`, which is already available when `spring-web` is on the classpath.\n\nThe following example uses the default `OAuth2AuthorizedClientManager` to configure a `RestClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:\n\n.Configure `RestClient` with `ClientHttpRequestInterceptor`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class RestClientConfig {\n\n\t@Bean\n\tpublic RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tOAuth2ClientHttpRequestInterceptor requestInterceptor =\n\t\t\t\tnew OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n\t\trequestInterceptor.setClientRegistrationIdResolver(clientRegistrationIdResolver());\n\n\t\treturn RestClient.builder()\n\t\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t\t.build();\n\t}\n\n\tprivate static ClientRegistrationIdResolver clientRegistrationIdResolver() {\n\t\treturn (request) -> {\n\t\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t\treturn (authentication instanceof OAuth2AuthenticationToken principal)\n\t\t\t\t? principal.getAuthorizedClientRegistrationId()\n\t\t\t\t: null;\n\t\t};\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass RestClientConfig {\n\n\t@Bean\n\tfun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {\n\t\tval requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)\n\t\trequestInterceptor.setClientRegistrationIdResolver(clientRegistrationIdResolver())\n\n\t\treturn RestClient.builder()\n\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t.build()\n\t}\n\n\tprivate fun clientRegistrationIdResolver(): OAuth2ClientHttpRequestInterceptor.ClientRegistrationIdResolver {\n\t\treturn OAuth2ClientHttpRequestInterceptor.ClientRegistrationIdResolver { request ->\n\t\t\tval authentication = SecurityContextHolder.getContext().authentication\n\t\t\tif (authentication is OAuth2AuthenticationToken) {\n\t\t\t\tauthentication.authorizedClientRegistrationId\n\t\t\t} else {\n\t\t\t\tnull\n\t\t\t}\n\t\t}\n\t}\n\n}\n----\n=====\n\nThis configured `RestClient` can be used as in the following example:\n\n[[oauth2-client-accessing-protected-resources-current-user-example]]\n.Use `RestClient` to Access Protected Resources (Current User)\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@RestController\npublic class MessagesController {\n\n\tprivate final RestClient restClient;\n\n\tpublic MessagesController(RestClient restClient) {\n\t\tthis.restClient = restClient;\n\t}\n\n\t@GetMapping(\"/messages\")\n\tpublic ResponseEntity<List<Message>> messages() {\n\t\tMessage[] messages = this.restClient.get()\n\t\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t\t.retrieve()\n\t\t\t\t.body(Message[].class);\n\t\treturn ResponseEntity.ok(Arrays.asList(messages));\n\t}\n\n\tpublic record Message(String message) {\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.web.client.body\n\n@RestController\nclass MessagesController(private val restClient: RestClient) {\n\n\t@GetMapping(\"/messages\")\n\tfun messages(): ResponseEntity<List<Message>> {\n\t\tval messages = restClient.get()\n\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t.retrieve()\n\t\t\t.body<Array<Message>>()!!\n\t\t\t.toList()\n\t\treturn ResponseEntity.ok(messages)\n\t}\n\n\tdata class Message(val message: String)\n\n}\n----\n=====\n\n[NOTE]\n====\nUnlike the <<oauth2-client-accessing-protected-resources-example,previous example>>, notice that we do not need to tell Spring Security about the `clientRegistrationId` we'd like to use.\nThis is because it can be derived from the currently logged in user.\n====\n\n[[oauth2-client-client-credentials]]\n=== Use the Client Credentials Grant\n\n[NOTE]\n====\nThis section focuses on additional considerations for the client credentials grant type.\nSee <<oauth2-client-access-protected-resources>> for general setup and usage with all grant types.\n====\n\nThe https://tools.ietf.org/html/rfc6749#section-1.3.4[client credentials grant] allows a client to obtain an `access_token` on behalf of itself.\nThe client credentials grant is a simple flow that does not involve a resource owner (i.e. a user).\n\n[WARNING]\n====\nIt is important to note that typical use of the client credentials grant implies that any request (or user) can potentially obtain an access token and make protected resources requests to a resource server.\nExercise caution when designing applications to ensure that users cannot make unauthorized requests since every request will be able to obtain an access token.\n====\n\nWhen obtaining access tokens within a web application where users can log in, the default behavior of Spring Security is to obtain an access token per user.\n\n[NOTE]\n====\nBy default, access tokens are scoped to the principal name of the current user which means every user will receive a unique access token.\n====\n\nClients using the client credentials grant typically require access tokens to be scoped to the application instead of to individual users so there is only one access token per application.\nIn order to scope access tokens to the application, you will need to set a strategy for resolving a custom principal name.\nThe following example does this by configuring a `RestClient` with the `RequestAttributePrincipalResolver`:\n\n.Configure `RestClient` for `client_credentials`\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class RestClientConfig {\n\n\t@Bean\n\tpublic RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tOAuth2ClientHttpRequestInterceptor requestInterceptor =\n\t\t\t\tnew OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n\t\trequestInterceptor.setPrincipalResolver(new RequestAttributePrincipalResolver());\n\t\treturn RestClient.builder()\n\t\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t\t.build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass RestClientConfig {\n\n\t@Bean\n\tfun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {\n\t\tval requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)\n\t\trequestInterceptor.setPrincipalResolver(RequestAttributePrincipalResolver())\n\t\treturn RestClient.builder()\n\t\t\t.requestInterceptor(requestInterceptor)\n\t\t\t.build()\n\t}\n\n}\n----\n=====\n\nWith the above configuration in place, a principal name can be specified for each request.\nThe following example demonstrates how to scope access tokens to the application by specifying a principal name:\n\n.Scope Access Tokens to the Application\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId;\nimport static org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal;\n\n@RestController\npublic class MessagesController {\n\n\tprivate final RestClient restClient;\n\n\tpublic MessagesController(RestClient restClient) {\n\t\tthis.restClient = restClient;\n\t}\n\n\t@GetMapping(\"/messages\")\n\tpublic ResponseEntity<List<Message>> messages() {\n\t\tMessage[] messages = this.restClient.get()\n\t\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t\t.attributes(clientRegistrationId(\"my-oauth2-client\"))\n\t\t\t\t.attributes(principal(\"my-application\"))\n\t\t\t\t.retrieve()\n\t\t\t\t.body(Message[].class);\n\t\treturn ResponseEntity.ok(Arrays.asList(messages));\n\t}\n\n\tpublic record Message(String message) {\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId\nimport org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal\nimport org.springframework.web.client.body\n\n@RestController\nclass MessagesController(private val restClient: RestClient) {\n\n\t@GetMapping(\"/messages\")\n\tfun messages(): ResponseEntity<List<Message>> {\n\t\tval messages = restClient.get()\n\t\t\t.uri(\"http://localhost:8090/messages\")\n\t\t\t.attributes(clientRegistrationId(\"my-oauth2-client\"))\n\t\t\t.attributes(principal(\"my-application\"))\n\t\t\t.retrieve()\n\t\t\t.body<Array<Message>>()!!\n\t\t\t.toList()\n\t\treturn ResponseEntity.ok(messages)\n\t}\n\n\tdata class Message(val message: String)\n\n}\n----\n=====\n\n[NOTE]\n====\nWhen specifying a principal name via attributes as in the above example, there will only be a single access token and it will be used for all requests.\n====\n\n[[oauth2-client-enable-extension-grant-type]]\n=== Enable an Extension Grant Type\n\nA common use case involves enabling and/or configuring an extension grant type.\nFor example, Spring Security provides support for the `jwt-bearer` and `token-exchange` grant types, but does not enable them by default because they are not part of the core OAuth 2.0 specification.\n\nWith Spring Security 6.2 and later, we can simply publish a bean for one or more `OAuth2AuthorizedClientProvider` and they will be picked up automatically.\nThe following example simply enables the `jwt-bearer` grant type:\n\n.Enable `jwt-bearer` Grant Type\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic OAuth2AuthorizedClientProvider jwtBearer() {\n\t\treturn new JwtBearerOAuth2AuthorizedClientProvider();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun jwtBearer(): OAuth2AuthorizedClientProvider {\n\t\treturn JwtBearerOAuth2AuthorizedClientProvider()\n\t}\n\n}\n----\n=====\n\nA default `OAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided.\n\n[TIP]\n====\nAny custom `OAuth2AuthorizedClientProvider` bean will also be picked up and applied to the provided `OAuth2AuthorizedClientManager` after the default grant types.\n====\n\nIn order to achieve the above configuration prior to Spring Security 6.2, we had to publish this bean ourselves and ensure we re-enabled default grant types as well.\nTo understand what is being configured behind the scenes, here's what the configuration might have looked like:\n\n.Enable `jwt-bearer` Grant Type (prior to 6.2)\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken()\n\t\t\t\t.clientCredentials()\n\t\t\t\t.provider(new JwtBearerOAuth2AuthorizedClientProvider())\n\t\t\t\t.build();\n\n\t\tDefaultOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultOAuth2AuthorizedClientManager(\n\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t\treturn authorizedClientManager;\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun authorizedClientManager(\n\t\tclientRegistrationRepository: ClientRegistrationRepository,\n\t\tauthorizedClientRepository: OAuth2AuthorizedClientRepository\n\t): OAuth2AuthorizedClientManager {\n\t\tval authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken()\n\t\t\t.clientCredentials()\n\t\t\t.provider(JwtBearerOAuth2AuthorizedClientProvider())\n\t\t\t.build()\n\n\t\tval authorizedClientManager = DefaultOAuth2AuthorizedClientManager(\n\t\t\tclientRegistrationRepository, authorizedClientRepository\n\t\t)\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n\n\t\treturn authorizedClientManager\n\t}\n\n}\n----\n=====\n\n[[oauth2-client-customize-existing-grant-type]]\n=== Customize an Existing Grant Type\n\nThe ability to <<oauth2-client-enable-extension-grant-type,enable extension grant types>> by publishing a bean also provides the opportunity for customizing an existing grant type without the need to re-define the defaults.\nFor example, if we want to customize the clock skew of the `OAuth2AuthorizedClientProvider` for the `client_credentials` grant, we can simply publish a bean like so:\n\n.Customize Client Credentials Grant Type\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic OAuth2AuthorizedClientProvider clientCredentials() {\n\t\tClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\t\tnew ClientCredentialsOAuth2AuthorizedClientProvider();\n\t\tauthorizedClientProvider.setClockSkew(Duration.ofMinutes(5));\n\n\t\treturn authorizedClientProvider;\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun clientCredentials(): OAuth2AuthorizedClientProvider {\n\t\tval authorizedClientProvider = ClientCredentialsOAuth2AuthorizedClientProvider()\n\t\tauthorizedClientProvider.setClockSkew(Duration.ofMinutes(5))\n\t\treturn authorizedClientProvider\n\t}\n\n}\n----\n=====\n\n[[oauth2-client-customize-request-parameters]]\n=== Customize Token Request Parameters\n\nThe need to customize request parameters when obtaining an access token is fairly common.\nFor example, let's say we want to add a custom `audience` parameter to the token request because the provider requires this parameter for the `authorization_code` grant.\n\nWith Spring Security 6.2 and later, we can simply publish a bean of type `OAuth2AccessTokenResponseClient` with the generic type `OAuth2AuthorizationCodeGrantRequest` and it will be used by Spring Security to configure OAuth2 Client components.\n\nThe following example customizes token request parameters for the `authorization_code` grant without the DSL:\n\n.Customize Token Request Parameters for Authorization Code Grant\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {\n\t\tRestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew RestClientAuthorizationCodeTokenResponseClient();\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\tprivate static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {\n\t\treturn (grantRequest) -> {\n\t\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\t\tparameters.set(\"audience\", \"xyz_value\");\n\n\t\t\treturn parameters;\n\t\t};\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {\n\t\tval accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\tprivate fun parametersConverter(): Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> {\n\t\treturn Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> { grantRequest ->\n\t\t\tLinkedMultiValueMap<String, String>().also { parameters ->\n\t\t\t\tparameters[\"audience\"] = \"xyz_value\"\n\t\t\t}\n\t\t}\n\t}\n\n}\n----\n=====\n\n[TIP]\n====\nNotice that we don't need to customize the `SecurityFilterChain` bean in this case, and can stick with the defaults.\nIf using Spring Boot with no additional customizations, we can actually omit the `SecurityFilterChain` bean entirely.\n====\n\nPrior to Spring Security 6.2, we had to ensure that this customization was applied for both OAuth2 Login (if we are using this feature) and OAuth2 Client components using the Spring Security DSL.\nTo understand what is being configured behind the scenes, here's what the configuration might have looked like:\n\n.Customize Token Request Parameters for Authorization Code Grant (prior to 6.2)\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\tRestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew RestClientAuthorizationCodeTokenResponseClient();\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter());\n\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login((oauth2Login) -> oauth2Login\n\t\t\t\t.tokenEndpoint((tokenEndpoint) -> tokenEndpoint\n\t\t\t\t\t.accessTokenResponseClient(accessTokenResponseClient)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.oauth2Client((oauth2Client) -> oauth2Client\n\t\t\t\t.authorizationCodeGrant((authorizationCode) -> authorizationCode\n\t\t\t\t\t.accessTokenResponseClient(accessTokenResponseClient)\n\t\t\t\t)\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n\n\tprivate static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\tval tokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()\n\t\ttokenResponseClient.addParametersConverter(parametersConverter())\n\n\t\thttp {\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, authenticated)\n\t\t\t}\n\t\t\toauth2Login {\n\t\t\t\ttokenEndpoint {\n\t\t\t\t\taccessTokenResponseClient = tokenResponseClient\n\t\t\t\t}\n\t\t\t}\n\t\t\toauth2Client {\n\t\t\t\tauthorizationCodeGrant {\n\t\t\t\t\taccessTokenResponseClient = tokenResponseClient\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n\tprivate fun parametersConverter(): Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\nFor other grant types we can publish additional `OAuth2AccessTokenResponseClient` beans to override the defaults.\nFor example, to customize token requests for the `client_credentials` grant we can publish the following bean:\n\n.Customize Token Request Parameters for Client Credentials Grant\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {\n\t\tRestClientClientCredentialsTokenResponseClient accessTokenResponseClient =\n\t\t\t\tnew RestClientClientCredentialsTokenResponseClient();\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\tprivate static Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {\n\t\tval accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()\n\t\taccessTokenResponseClient.addParametersConverter(parametersConverter())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\tprivate fun parametersConverter(): Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\nSpring Security automatically resolves the following generic types of `OAuth2AccessTokenResponseClient` beans:\n\n* `OAuth2AuthorizationCodeGrantRequest` (see `RestClientAuthorizationCodeTokenResponseClient`)\n* `OAuth2RefreshTokenGrantRequest` (see `RestClientRefreshTokenTokenResponseClient`)\n* `OAuth2ClientCredentialsGrantRequest` (see `RestClientClientCredentialsTokenResponseClient`)\n* `JwtBearerGrantRequest` (see `RestClientJwtBearerTokenResponseClient`)\n* `TokenExchangeGrantRequest` (see `RestClientTokenExchangeTokenResponseClient`)\n\n[TIP]\n====\nPublishing a bean of type `OAuth2AccessTokenResponseClient<JwtBearerGrantRequest>` will automatically enable the `jwt-bearer` grant type without the need to <<oauth2-client-enable-extension-grant-type,configure it separately>>.\n====\n\n[TIP]\n====\nPublishing a bean of type `OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest>` will automatically enable the `token-exchange` grant type without the need to <<oauth2-client-enable-extension-grant-type,configure it separately>>.\n====\n\n[[oauth2-client-customize-rest-client]]\n=== Customize the `RestClient` used by OAuth2 Client Components\n\nAnother common use case is the need to customize the `RestClient` used when obtaining an access token.\nWe might need to do this to customize processing of the response (via a custom `HttpMessageConverter`) or to apply proxy settings for a corporate network (via a customized `ClientHttpRequestFactory`).\n\nWith Spring Security 6.2 and later, we can simply publish beans of type `OAuth2AccessTokenResponseClient` and Spring Security will configure and publish an `OAuth2AuthorizedClientManager` bean for us.\n\nThe following example customizes the `RestClient` for all of the supported grant types:\n\n.Customize `RestClient` for OAuth2 Client\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {\n\t\tRestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew RestClientAuthorizationCodeTokenResponseClient();\n\t\taccessTokenResponseClient.setRestClient(restClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {\n\t\tRestClientRefreshTokenTokenResponseClient accessTokenResponseClient =\n\t\t\tnew RestClientRefreshTokenTokenResponseClient();\n\t\taccessTokenResponseClient.setRestClient(restClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {\n\t\tRestClientClientCredentialsTokenResponseClient accessTokenResponseClient =\n\t\t\tnew RestClientClientCredentialsTokenResponseClient();\n\t\taccessTokenResponseClient.setRestClient(restClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {\n\t\tRestClientJwtBearerTokenResponseClient accessTokenResponseClient =\n\t\t\tnew RestClientJwtBearerTokenResponseClient();\n\t\taccessTokenResponseClient.setRestClient(restClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {\n\t\tRestClientTokenExchangeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew RestClientTokenExchangeTokenResponseClient();\n\t\taccessTokenResponseClient.setRestClient(restClient());\n\n\t\treturn accessTokenResponseClient;\n\t}\n\n\t@Bean\n\tpublic RestClient restClient() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig {\n\n\t@Bean\n\tfun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {\n\t\tval accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()\n\t\taccessTokenResponseClient.setRestClient(restClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun refreshTokenAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> {\n\t\tval accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()\n\t\taccessTokenResponseClient.setRestClient(restClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {\n\t\tval accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()\n\t\taccessTokenResponseClient.setRestClient(restClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun jwtBearerAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> {\n\t\tval accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()\n\t\taccessTokenResponseClient.setRestClient(restClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun tokenExchangeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> {\n\t\tval accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()\n\t\taccessTokenResponseClient.setRestClient(restClient())\n\n\t\treturn accessTokenResponseClient\n\t}\n\n\t@Bean\n\tfun restClient(): RestClient {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\nA default `OAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided.\n\n[TIP]\n====\nNotice that we don't need to customize the `SecurityFilterChain` bean in this case, and can stick with the defaults.\nIf using Spring Boot with no additional customizations, we can actually omit the `SecurityFilterChain` bean entirely.\n====\n\nPrior to Spring Security 6.2, we had to ensure this customization was applied to both OAuth2 Login (if we are using this feature) and OAuth2 Client components.\nWe had to use both the Spring Security DSL (for the `authorization_code` grant) and publish a bean of type `OAuth2AuthorizedClientManager` for other grant types.\nTo understand what is being configured behind the scenes, here's what the configuration might have looked like:\n\n.Customize `RestClient` for OAuth2 Client (prior to 6.2)\n[tabs]\n=====\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\tRestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =\n\t\t\tnew RestClientAuthorizationCodeTokenResponseClient();\n\t\taccessTokenResponseClient.setRestClient(restClient());\n\n\t\thttp\n\t\t\t// ...\n\t\t\t.oauth2Login((oauth2Login) -> oauth2Login\n\t\t\t\t.tokenEndpoint((tokenEndpoint) -> tokenEndpoint\n\t\t\t\t\t.accessTokenResponseClient(accessTokenResponseClient)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.oauth2Client((oauth2Client) -> oauth2Client\n\t\t\t\t.authorizationCodeGrant((authorizationCode) -> authorizationCode\n\t\t\t\t\t.accessTokenResponseClient(accessTokenResponseClient)\n\t\t\t\t)\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic OAuth2AuthorizedClientManager authorizedClientManager(\n\t\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\n\t\tRestClientRefreshTokenTokenResponseClient refreshTokenAccessTokenResponseClient =\n\t\t\tnew RestClientRefreshTokenTokenResponseClient();\n\t\trefreshTokenAccessTokenResponseClient.setRestClient(restClient());\n\n\t\tRestClientClientCredentialsTokenResponseClient clientCredentialsAccessTokenResponseClient =\n\t\t\tnew RestClientClientCredentialsTokenResponseClient();\n\t\tclientCredentialsAccessTokenResponseClient.setRestClient(restClient());\n\n\t\tRestClientJwtBearerTokenResponseClient jwtBearerAccessTokenResponseClient =\n\t\t\tnew RestClientJwtBearerTokenResponseClient();\n\t\tjwtBearerAccessTokenResponseClient.setRestClient(restClient());\n\n\t\tJwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =\n\t\t\tnew JwtBearerOAuth2AuthorizedClientProvider();\n\t\tjwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient);\n\n\t\tRestClientTokenExchangeTokenResponseClient tokenExchangeAccessTokenResponseClient =\n\t\t\tnew RestClientTokenExchangeTokenResponseClient();\n\t\ttokenExchangeAccessTokenResponseClient.setRestClient(restClient());\n\n\t\tTokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =\n\t\t\tnew TokenExchangeOAuth2AuthorizedClientProvider();\n\t\ttokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient);\n\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider =\n\t\t\tOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken((refreshToken) -> refreshToken\n\t\t\t\t\t.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)\n\t\t\t\t)\n\t\t\t\t.clientCredentials((clientCredentials) -> clientCredentials\n\t\t\t\t\t.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)\n\t\t\t\t)\n\t\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t\t.provider(tokenExchangeAuthorizedClientProvider)\n\t\t\t\t.build();\n\n\t\tDefaultOAuth2AuthorizedClientManager authorizedClientManager =\n\t\t\tnew DefaultOAuth2AuthorizedClientManager(\n\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\n\t\treturn authorizedClientManager;\n\t}\n\n\t@Bean\n\tpublic RestClient restClient() {\n\t\t// ...\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.config.annotation.web.invoke\n\n@Configuration\n@EnableWebSecurity\nclass SecurityConfig {\n\n\t@Bean\n\tfun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n\t\tval tokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()\n\t\ttokenResponseClient.setRestClient(restClient())\n\n\t\thttp {\n\t\t\t// ...\n\t\t\toauth2Login {\n\t\t\t\ttokenEndpoint {\n\t\t\t\t\taccessTokenResponseClient = tokenResponseClient\n\t\t\t\t}\n\t\t\t}\n\t\t\toauth2Client {\n\t\t\t\tauthorizationCodeGrant {\n\t\t\t\t\taccessTokenResponseClient = tokenResponseClient\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn http.build()\n\t}\n\n\t@Bean\n\tfun authorizedClientManager(\n\t\tclientRegistrationRepository: ClientRegistrationRepository?,\n\t\tauthorizedClientRepository: OAuth2AuthorizedClientRepository?\n\t): OAuth2AuthorizedClientManager {\n\t\tval refreshTokenAccessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()\n\t\trefreshTokenAccessTokenResponseClient.setRestClient(restClient())\n\n\t\tval clientCredentialsAccessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()\n\t\tclientCredentialsAccessTokenResponseClient.setRestClient(restClient())\n\n\t\tval jwtBearerAccessTokenResponseClient = RestClientJwtBearerTokenResponseClient()\n\t\tjwtBearerAccessTokenResponseClient.setRestClient(restClient())\n\n\t\tval jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()\n\t\tjwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient)\n\n\t\tval tokenExchangeAccessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()\n\t\ttokenExchangeAccessTokenResponseClient.setRestClient(restClient())\n\n\t\tval tokenExchangeAuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()\n\t\ttokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient)\n\n\t\tval authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken { refreshToken ->\n\t\t\t\trefreshToken.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)\n\t\t\t}\n\t\t\t.clientCredentials { clientCredentials ->\n\t\t\t\tclientCredentials.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)\n\t\t\t}\n\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t.provider(tokenExchangeAuthorizedClientProvider)\n\t\t\t.build()\n\n\t\tval authorizedClientManager = DefaultOAuth2AuthorizedClientManager(\n\t\t\tclientRegistrationRepository, authorizedClientRepository\n\t\t)\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)\n\n\t\treturn authorizedClientManager\n\t}\n\n\t@Bean\n\tfun restClient(): RestClient {\n\t\t// ...\n\t}\n\n}\n----\n=====\n\n\n[[further-reading]]\n== Further Reading\n\nThe preceding sections introduced Spring Security's support for OAuth2 with examples for common scenarios.\nYou can read more about OAuth2 Client, Resource Server and Authorization Server in the following sections of the reference documentation:\n\n* xref:servlet/oauth2/login/index.adoc[]\n* xref:servlet/oauth2/client/index.adoc[]\n* xref:servlet/oauth2/resource-server/index.adoc[]\n* xref:servlet/oauth2/authorization-server/index.adoc[]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/login/advanced.adoc",
    "content": "[[oauth2login-advanced]]\n= Advanced Configuration\n\n`HttpSecurity.oauth2Login()` provides a number of configuration options for customizing OAuth 2.0 Login.\nThe main configuration options are grouped into their protocol endpoint counterparts.\n\nFor example, `oauth2Login().authorizationEndpoint()` allows configuring the _Authorization Endpoint_, whereas `oauth2Login().tokenEndpoint()` allows configuring the _Token Endpoint_.\n\nThe following code shows an example:\n\n.Advanced OAuth2 Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t    .authorizationEndpoint((authorization) -> authorization\n\t\t\t            ...\n\t\t\t    )\n\t\t\t    .redirectionEndpoint((redirection) -> redirection\n\t\t\t            ...\n\t\t\t    )\n\t\t\t    .tokenEndpoint((token) -> token\n\t\t\t            ...\n\t\t\t    )\n\t\t\t    .userInfoEndpoint((userInfo) -> userInfo\n\t\t\t            ...\n\t\t\t    )\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login {\n                authorizationEndpoint {\n                    ...\n                }\n                redirectionEndpoint {\n                    ...\n                }\n                tokenEndpoint {\n                    ...\n                }\n                userInfoEndpoint {\n                    ...\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nThe main goal of the `oauth2Login()` DSL was to closely align with the naming, as defined in the specifications.\n\nThe OAuth 2.0 Authorization Framework defines the https://tools.ietf.org/html/rfc6749#section-3[Protocol Endpoints] as follows:\n\nThe authorization process uses two authorization server endpoints (HTTP resources):\n\n* Authorization Endpoint: Used by the client to obtain authorization from the resource owner through user-agent redirection.\n* Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication.\n\nThe authorization process also uses one client endpoint:\n\n* Redirection Endpoint: Used by the authorization server to return responses that contain authorization credentials to the client through the resource owner user-agent.\n\nThe OpenID Connect Core 1.0 specification defines the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] as follows:\n\nThe UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user.\nTo obtain the requested claims about the end-user, the client makes a request to the UserInfo Endpoint by using an access token obtained through OpenID Connect Authentication.\nThese claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims.\n\nThe following code shows the complete configuration options available for the `oauth2Login()` DSL:\n\n.OAuth2 Login Configuration Options\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t    .clientRegistrationRepository(this.clientRegistrationRepository())\n\t\t\t    .authorizedClientRepository(this.authorizedClientRepository())\n\t\t\t    .authorizedClientService(this.authorizedClientService())\n\t\t\t    .loginPage(\"/login\")\n\t\t\t    .authorizationEndpoint((authorization) -> authorization\n\t\t\t        .baseUri(this.authorizationRequestBaseUri())\n\t\t\t        .authorizationRequestRepository(this.authorizationRequestRepository())\n\t\t\t        .authorizationRequestResolver(this.authorizationRequestResolver())\n\t\t\t    )\n\t\t\t    .redirectionEndpoint((redirection) -> redirection\n\t\t\t        .baseUri(this.authorizationResponseBaseUri())\n\t\t\t    )\n\t\t\t    .tokenEndpoint((token) -> token\n\t\t\t        .accessTokenResponseClient(this.accessTokenResponseClient())\n\t\t\t    )\n\t\t\t    .userInfoEndpoint((userInfo) -> userInfo\n\t\t\t        .userAuthoritiesMapper(this.userAuthoritiesMapper())\n\t\t\t        .userService(this.oauth2UserService())\n\t\t\t        .oidcUserService(this.oidcUserService())\n\t\t\t    )\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login {\n                clientRegistrationRepository = clientRegistrationRepository()\n                authorizedClientRepository = authorizedClientRepository()\n                authorizedClientService = authorizedClientService()\n                loginPage = \"/login\"\n                authorizationEndpoint {\n                    baseUri = authorizationRequestBaseUri()\n                    authorizationRequestRepository = authorizationRequestRepository()\n                    authorizationRequestResolver = authorizationRequestResolver()\n                }\n                redirectionEndpoint {\n                    baseUri = authorizationResponseBaseUri()\n                }\n                tokenEndpoint {\n                    accessTokenResponseClient = accessTokenResponseClient()\n                }\n                userInfoEndpoint {\n                    userAuthoritiesMapper = userAuthoritiesMapper()\n                    userService = oauth2UserService()\n                    oidcUserService = oidcUserService()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nIn addition to the `oauth2Login()` DSL, XML configuration is also supported.\n\nThe following code shows the complete configuration options available in the xref:servlet/appendix/namespace/http.adoc#nsa-oauth2-login[ security namespace]:\n\n.OAuth2 Login XML Configuration Options\n[source,xml]\n----\n<http>\n\t<oauth2-login client-registration-repository-ref=\"clientRegistrationRepository\"\n\t\t\t\t  authorized-client-repository-ref=\"authorizedClientRepository\"\n\t\t\t\t  authorized-client-service-ref=\"authorizedClientService\"\n\t\t\t\t  authorization-request-repository-ref=\"authorizationRequestRepository\"\n\t\t\t\t  authorization-request-resolver-ref=\"authorizationRequestResolver\"\n\t\t\t\t  access-token-response-client-ref=\"accessTokenResponseClient\"\n\t\t\t\t  user-authorities-mapper-ref=\"userAuthoritiesMapper\"\n\t\t\t\t  user-service-ref=\"oauth2UserService\"\n\t\t\t\t  oidc-user-service-ref=\"oidcUserService\"\n\t\t\t\t  login-processing-url=\"/login/oauth2/code/*\"\n\t\t\t\t  login-page=\"/login\"\n\t\t\t\t  authentication-success-handler-ref=\"authenticationSuccessHandler\"\n\t\t\t\t  authentication-failure-handler-ref=\"authenticationFailureHandler\"\n\t\t\t\t  jwt-decoder-factory-ref=\"jwtDecoderFactory\"/>\n</http>\n----\n\nThe following sections go into more detail on each of the configuration options available:\n\n* <<oauth2login-advanced-login-page>>\n* <<oauth2login-advanced-redirection-endpoint>>\n* <<oauth2login-advanced-userinfo-endpoint>>\n* <<oauth2login-advanced-idtoken-verify>>\n* <<oauth2login-advanced-oidc-logout>>\n\n\n[[oauth2login-advanced-login-page]]\n== OAuth 2.0 Login Page\n\nBy default, the OAuth 2.0 Login Page is auto-generated by the `DefaultLoginPageGeneratingFilter`.\nThe default login page shows each configured OAuth Client with its `ClientRegistration.clientName` as a link, which is capable of initiating the Authorization Request (or OAuth 2.0 Login).\n\n[NOTE]\n====\nFor `DefaultLoginPageGeneratingFilter` to show links for configured OAuth Clients, the registered `ClientRegistrationRepository` needs to also implement `Iterable<ClientRegistration>`.\nSee `InMemoryClientRegistrationRepository` for reference.\n====\n\nThe link's destination for each OAuth Client defaults to the following:\n\n`+OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/{registrationId}\"+`\n\nThe following line shows an example:\n\n[source,html]\n----\n<a href=\"/oauth2/authorization/google\">Google</a>\n----\n\nTo override the default login page, configure `oauth2Login().loginPage()` and (optionally) `oauth2Login().authorizationEndpoint().baseUri()`.\n\nThe following listing shows an example:\n\n.OAuth2 Login Page Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t    .loginPage(\"/login/oauth2\")\n\t\t\t    ...\n\t\t\t    .authorizationEndpoint((authorization) -> authorization\n\t\t\t        .baseUri(\"/login/oauth2/authorization\")\n\t\t\t        ...\n\t\t\t    )\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login {\n                loginPage = \"/login/oauth2\"\n                authorizationEndpoint {\n                    baseUri = \"/login/oauth2/authorization\"\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<oauth2-login login-page=\"/login/oauth2\"\n\t\t\t\t  ...\n    />\n</http>\n----\n======\n\n[IMPORTANT]\n====\nYou need to provide a `@Controller` with a `@RequestMapping(\"/login/oauth2\")` that is capable of rendering the custom login page.\n====\n\n[TIP]\n=====\nAs noted earlier, configuring `oauth2Login().authorizationEndpoint().baseUri()` is optional.\nHowever, if you choose to customize it, ensure the link to each OAuth Client matches the `authorizationEndpoint().baseUri()`.\n\nThe following line shows an example:\n[source,html]\n----\n<a href=\"/login/oauth2/authorization/google\">Google</a>\n----\n=====\n\n\n[[oauth2login-advanced-redirection-endpoint]]\n== Redirection Endpoint\n\nThe Redirection Endpoint is used by the Authorization Server for returning the Authorization Response (which contains the authorization credentials) to the client through the Resource Owner user-agent.\n\n[TIP]\n====\nOAuth 2.0 Login leverages the Authorization Code Grant.\nTherefore, the authorization credential is the authorization code.\n====\n\nThe default Authorization Response `baseUri` (redirection endpoint) is `*/login/oauth2/code/**`, which is defined in `OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI`.\n\nIf you would like to customize the Authorization Response `baseUri`, configure it as follows:\n\n.Redirection Endpoint Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n    @Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t    .redirectionEndpoint((redirection) -> redirection\n\t\t\t        .baseUri(\"/login/oauth2/callback/*\")\n\t\t\t        ...\n\t\t\t    )\n\t\t\t);\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login {\n                redirectionEndpoint {\n                    baseUri = \"/login/oauth2/callback/*\"\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<oauth2-login login-processing-url=\"/login/oauth2/callback/*\"\n\t\t\t\t  ...\n    />\n</http>\n----\n======\n\n[IMPORTANT]\n=====\nYou also need to ensure the `ClientRegistration.redirectUri` matches the custom Authorization Response `baseUri`.\n\nThe following listing shows an example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"-attributes\"]\n----\nreturn CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t.clientId(\"google-client-id\")\n\t.clientSecret(\"google-client-secret\")\n\t.redirectUri(\"{baseUrl}/login/oauth2/callback/{registrationId}\")\n\t.build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"-attributes\"]\n----\nreturn CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n    .clientId(\"google-client-id\")\n    .clientSecret(\"google-client-secret\")\n    .redirectUri(\"{baseUrl}/login/oauth2/callback/{registrationId}\")\n    .build()\n----\n======\n=====\n\n\n[[oauth2login-advanced-userinfo-endpoint]]\n== UserInfo Endpoint\n\nThe UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections:\n\n* <<oauth2login-advanced-map-authorities>>\n* <<oauth2login-advanced-oauth2-user-service>>\n* <<oauth2login-advanced-oidc-user-service>>\n\n\n[[oauth2login-advanced-map-authorities]]\n=== Mapping User Authorities\n\nAfter the user successfully authenticates with the OAuth 2.0 Provider, the `OAuth2User.getAuthorities()` (or `OidcUser.getAuthorities()`) contains a list of granted authorities populated from `OAuth2UserRequest.getAccessToken().getScopes()` and prefixed with `SCOPE_`.\nThese granted authorities can be mapped to a new set of `GrantedAuthority` instances, which are supplied to `OAuth2AuthenticationToken` when completing the authentication.\n\n[TIP]\n`OAuth2AuthenticationToken.getAuthorities()` is used for authorizing requests, such as in `hasRole('USER')` or `hasRole('ADMIN')`.\n\nThere are a couple of options to choose from when mapping user authorities:\n\n* <<oauth2login-advanced-map-authorities-grantedauthoritiesmapper>>\n* <<oauth2login-advanced-map-authorities-oauth2userservice>>\n\n\n[[oauth2login-advanced-map-authorities-grantedauthoritiesmapper]]\n==== Using a GrantedAuthoritiesMapper\n\nThe `GrantedAuthoritiesMapper` is given a list of granted authorities which contains a special authority of type `OAuth2UserAuthority` and the authority string `OAUTH2_USER` (or `OidcUserAuthority` and the authority string `OIDC_USER`).\n\nProvide an implementation of `GrantedAuthoritiesMapper` and configure it, as follows:\n\n.Granted Authorities Mapper Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n    @Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t    .userInfoEndpoint((userInfo) -> userInfo\n\t\t\t        .userAuthoritiesMapper(this.userAuthoritiesMapper())\n\t\t\t        ...\n\t\t\t    )\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate GrantedAuthoritiesMapper userAuthoritiesMapper() {\n\t\treturn (authorities) -> {\n\t\t\tSet<GrantedAuthority> mappedAuthorities = new HashSet<>();\n\n\t\t\tauthorities.forEach(authority -> {\n\t\t\t\tif (OidcUserAuthority.class.isInstance(authority)) {\n\t\t\t\t\tOidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;\n\n\t\t\t\t\tOidcIdToken idToken = oidcUserAuthority.getIdToken();\n\t\t\t\t\tOidcUserInfo userInfo = oidcUserAuthority.getUserInfo();\n\n\t\t\t\t\t// Map the claims found in idToken and/or userInfo\n\t\t\t\t\t// to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t\t} else if (OAuth2UserAuthority.class.isInstance(authority)) {\n\t\t\t\t\tOAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;\n\n\t\t\t\t\tMap<String, Object> userAttributes = oauth2UserAuthority.getAttributes();\n\n\t\t\t\t\t// Map the attributes found in userAttributes\n\t\t\t\t\t// to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn mappedAuthorities;\n\t\t};\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login {\n                userInfoEndpoint {\n                    userAuthoritiesMapper = userAuthoritiesMapper()\n                }\n            }\n        }\n        return http.build()\n    }\n\n    private fun userAuthoritiesMapper(): GrantedAuthoritiesMapper = GrantedAuthoritiesMapper { authorities: Collection<GrantedAuthority> ->\n        val mappedAuthorities = emptySet<GrantedAuthority>()\n\n        authorities.forEach { authority ->\n            if (authority is OidcUserAuthority) {\n                val idToken = authority.idToken\n                val userInfo = authority.userInfo\n                // Map the claims found in idToken and/or userInfo\n                // to one or more GrantedAuthority's and add it to mappedAuthorities\n            } else if (authority is OAuth2UserAuthority) {\n                val userAttributes = authority.attributes\n                // Map the attributes found in userAttributes\n                // to one or more GrantedAuthority's and add it to mappedAuthorities\n            }\n        }\n\n        mappedAuthorities\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<oauth2-login user-authorities-mapper-ref=\"userAuthoritiesMapper\"\n\t\t\t\t  ...\n    />\n</http>\n----\n======\n\nAlternatively, you can register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as follows:\n\n.Granted Authorities Mapper Bean Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t    .oauth2Login(withDefaults());\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic GrantedAuthoritiesMapper userAuthoritiesMapper() {\n\t\t...\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login { }\n        }\n        return http.build()\n    }\n\n    @Bean\n    fun userAuthoritiesMapper(): GrantedAuthoritiesMapper {\n        ...\n    }\n}\n----\n======\n\n[TIP]\n====\nOnce authentication completes, it also contains the `FACTOR_AUTHORIZATION_CODE` granted authority.\n====\n\n[[oauth2login-advanced-map-authorities-oauth2userservice]]\n==== Delegation-based Strategy with OAuth2UserService\n\nThis strategy is advanced compared to using a `GrantedAuthoritiesMapper`. However, it is also more flexible, as it gives you access to the `OAuth2UserRequest` and `OAuth2User` (when using an OAuth 2.0 UserService) or `OidcUserRequest` and `OidcUser` (when using an OpenID Connect 1.0 UserService).\n\nThe `OAuth2UserRequest` (and `OidcUserRequest`) provides you access to the associated `OAuth2AccessToken`, which is very useful in cases where the _delegator_ needs to fetch authority information from a protected resource before it can map the custom authorities for the user.\n\nThe following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:\n\n.OAuth2UserService Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t    .userInfoEndpoint((userInfo) -> userInfo\n\t\t\t        .oidcUserService(this.oidcUserService())\n\t\t\t        ...\n\t\t\t    )\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {\n\t\tfinal OidcUserService delegate = new OidcUserService();\n\n\t\treturn (userRequest) -> {\n\t\t\t// Delegate to the default implementation for loading a user\n\t\t\tOidcUser oidcUser = delegate.loadUser(userRequest);\n\n\t\t\tOAuth2AccessToken accessToken = userRequest.getAccessToken();\n\t\t\tSet<GrantedAuthority> mappedAuthorities = new HashSet<>();\n\n\t\t\t// TODO\n\t\t\t// 1) Fetch the authority information from the protected resource using accessToken\n\t\t\t// 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities\n\n\t\t\t// 3) Create a copy of oidcUser but use the mappedAuthorities instead\n\t\t\tProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();\n\t\t\tString userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();\n\t\t\tif (StringUtils.hasText(userNameAttributeName)) {\n\t\t\t\toidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo(), userNameAttributeName);\n\t\t\t} else {\n\t\t\t\toidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());\n\t\t\t}\n\n\t\t\treturn oidcUser;\n\t\t};\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig  {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login {\n                userInfoEndpoint {\n                    oidcUserService = oidcUserService()\n                }\n            }\n        }\n        return http.build()\n    }\n\n    @Bean\n    fun oidcUserService(): OAuth2UserService<OidcUserRequest, OidcUser> {\n        val delegate = OidcUserService()\n\n        return OAuth2UserService { userRequest ->\n            // Delegate to the default implementation for loading a user\n            val oidcUser = delegate.loadUser(userRequest)\n\n            val accessToken = userRequest.accessToken\n            val mappedAuthorities = HashSet<GrantedAuthority>()\n\n            // TODO\n            // 1) Fetch the authority information from the protected resource using accessToken\n            // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities\n            // 3) Create a copy of oidcUser but use the mappedAuthorities instead\n            val providerDetails = userRequest.getClientRegistration().getProviderDetails()\n            val userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName()\n            if (StringUtils.hasText(userNameAttributeName)) {\n                DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo, userNameAttributeName)\n            } else {\n                DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo)\n            }\n        }\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n\t<oauth2-login oidc-user-service-ref=\"oidcUserService\"\n\t\t\t\t  ...\n    />\n</http>\n----\n======\n\n\n[[oauth2login-advanced-oauth2-user-service]]\n=== OAuth 2.0 UserService\n\n`DefaultOAuth2UserService` is an implementation of an `OAuth2UserService` that supports standard OAuth 2.0 Provider's.\n\n[NOTE]\n====\n`OAuth2UserService` obtains the user attributes of the end-user (the resource owner) from the UserInfo Endpoint (by using the access token granted to the client during the authorization flow) and returns an `AuthenticatedPrincipal` in the form of an `OAuth2User`.\n====\n\n`DefaultOAuth2UserService` uses a `RestOperations` instance when requesting the user attributes at the UserInfo Endpoint.\n\nIf you need to customize the pre-processing of the UserInfo Request, you can provide `DefaultOAuth2UserService.setRequestEntityConverter()` with a custom `Converter<OAuth2UserRequest, RequestEntity<?>>`.\nThe default implementation `OAuth2UserRequestEntityConverter` builds a `RequestEntity` representation of a UserInfo Request that sets the `OAuth2AccessToken` in the `Authorization` header by default.\n\nOn the other end, if you need to customize the post-handling of the UserInfo Response, you need to provide `DefaultOAuth2UserService.setRestOperations()` with a custom configured `RestOperations`.\nThe default `RestOperations` is configured as follows:\n\n[source,java]\n----\nRestTemplate restTemplate = new RestTemplate();\nrestTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());\n----\n\n`OAuth2ErrorResponseErrorHandler` is a `ResponseErrorHandler` that can handle an OAuth 2.0 Error (400 Bad Request).\nIt uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error parameters to an `OAuth2Error`.\n\nWhether you customize `DefaultOAuth2UserService` or provide your own implementation of `OAuth2UserService`, you need to configure it as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t    .userInfoEndpoint((userInfo) -> userInfo\n\t\t\t        .userService(this.oauth2UserService())\n\t\t\t        ...\n\t\t\t    )\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {\n\t\t...\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login {\n                userInfoEndpoint {\n                    userService = oauth2UserService()\n                    // ...\n                }\n            }\n        }\n        return http.build()\n    }\n\n    private fun oauth2UserService(): OAuth2UserService<OAuth2UserRequest, OAuth2User> {\n        // ...\n    }\n}\n----\n======\n\n\n[[oauth2login-advanced-oidc-user-service]]\n=== OpenID Connect 1.0 UserService\n\n`OidcUserService` is an implementation of an `OAuth2UserService` that supports OpenID Connect 1.0 Provider's.\n\nThe `OidcUserService` leverages the `DefaultOAuth2UserService` when requesting the user attributes at the UserInfo Endpoint.\n\nIf you need to customize the pre-processing of the UserInfo Request or the post-handling of the UserInfo Response, you need to provide `OidcUserService.setOauth2UserService()` with a custom configured `DefaultOAuth2UserService`.\n\nWhether you customize `OidcUserService` or provide your own implementation of `OAuth2UserService` for OpenID Connect 1.0 Provider's, you need to configure it as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.oauth2Login((oauth2) -> oauth2\n\t\t\t\t.userInfoEndpoint((userInfo) -> userInfo\n\t\t\t\t    .oidcUserService(this.oidcUserService())\n\t\t\t\t    ...\n\t\t\t    )\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {\n\t\t...\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            oauth2Login {\n                userInfoEndpoint {\n                    oidcUserService = oidcUserService()\n                    // ...\n                }\n            }\n        }\n        return http.build()\n    }\n\n    private fun oidcUserService(): OAuth2UserService<OidcUserRequest, OidcUser> {\n        // ...\n    }\n}\n----\n======\n\n\n[[oauth2login-advanced-idtoken-verify]]\n== ID Token Signature Verification\n\nOpenID Connect 1.0 Authentication introduces the https://openid.net/specs/openid-connect-core-1_0.html#IDToken[ID Token], which is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when used by a Client.\n\nThe ID Token is represented as a https://tools.ietf.org/html/rfc7519[JSON Web Token] (JWT) and MUST be signed by using https://tools.ietf.org/html/rfc7515[JSON Web Signature] (JWS).\n\nThe `OidcIdTokenDecoderFactory` provides a `JwtDecoder` used for `OidcIdToken` signature verification. The default algorithm is `RS256` but may be different when assigned during client registration.\nFor these cases, you can configure a resolver to return the expected JWS algorithm assigned for a specific client.\n\nThe JWS algorithm resolver is a `Function` that accepts a `ClientRegistration` and returns the expected `JwsAlgorithm` for the client, such as `SignatureAlgorithm.RS256` or `MacAlgorithm.HS256`\n\nThe following code shows how to configure the `OidcIdTokenDecoderFactory` `@Bean` to default to `MacAlgorithm.HS256` for all `ClientRegistration` instances:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {\n\tOidcIdTokenDecoderFactory idTokenDecoderFactory = new OidcIdTokenDecoderFactory();\n\tidTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> clientRegistration.HS256);\n\treturn idTokenDecoderFactory;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun idTokenDecoderFactory(): JwtDecoderFactory<ClientRegistration?> {\n    val idTokenDecoderFactory = OidcIdTokenDecoderFactory()\n    idTokenDecoderFactory.setJwsAlgorithmResolver { MacAlgorithm.HS256 }\n    return idTokenDecoderFactory\n}\n----\n======\n\n[NOTE]\n====\nFor MAC-based algorithms (such as `HS256`, `HS384`, or `HS512`), the `client-secret` that corresponds to the `client-id` is used as the symmetric key for signature verification.\n====\n\n[TIP]\n====\nIf more than one `ClientRegistration` is configured for OpenID Connect 1.0 Authentication, the JWS algorithm resolver may evaluate the provided `ClientRegistration` to determine which algorithm to return.\n====\n\n[[oauth2login-advanced-oidc-logout]]\nThen, you can proceed to configure xref:servlet/oauth2/login/logout.adoc[logout]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/login/core.adoc",
    "content": "= Core Configuration\n\n[[oauth2login-sample-boot]]\n== Spring Boot Sample\n\nSpring Boot brings full auto-configuration capabilities for OAuth 2.0 Login.\n\nThis section shows how to configure the {gh-samples-url}/servlet/spring-boot/java/oauth2/login[*OAuth 2.0 Login sample*] by using _Google_ as the _Authentication Provider_ and covers the following topics:\n\n* <<oauth2login-sample-initial-setup>>\n* <<oauth2login-sample-redirect-uri>>\n* <<oauth2login-sample-application-config>>\n* <<oauth2login-sample-boot-application>>\n\n\n[[oauth2login-sample-initial-setup]]\n=== Initial Setup\n\nTo use Google's OAuth 2.0 authentication system for login, you must set up a project in the Google API Console to obtain OAuth 2.0 credentials.\n\n[NOTE]\n====\nhttps://developers.google.com/identity/protocols/OpenIDConnect[Google's OAuth 2.0 implementation] for authentication conforms to the  https://openid.net/connect/[OpenID Connect 1.0] specification and is https://openid.net/certification/[OpenID certified].\n====\n\nFollow the instructions on the https://developers.google.com/identity/protocols/OpenIDConnect[OpenID Connect] page, starting in the \"`Setting up OAuth 2.0`\" section.\n\nAfter completing the \"`Obtain OAuth 2.0 credentials`\" instructions, you should have new OAuth Client with credentials consisting of a Client ID and a Client Secret.\n\n\n[[oauth2login-sample-redirect-uri]]\n=== Setting the Redirect URI\n\nThe redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Google and have granted access to the OAuth Client (<<oauth2login-sample-initial-setup,created in the previous step>>) on the Consent page.\n\nIn the \"`Set a redirect URI`\" subsection, ensure that the *Authorized redirect URIs* field is set to `http://localhost:8080/login/oauth2/code/google`.\n\n[TIP]\n====\nThe default redirect URI template is `+{baseUrl}/login/oauth2/code/{registrationId}+`.\nThe `registrationId` is a unique identifier for the xref:servlet/oauth2/client/index.adoc#oauth2Client-client-registration[`ClientRegistration`].\n====\n\n[IMPORTANT]\n====\nIf the OAuth Client runs behind a proxy server, you should check the xref:features/exploits/http.adoc#http-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured.\nAlso, see the supported xref:servlet/oauth2/client/authorization-grants.adoc#oauth2Client-auth-code-redirect-uri[ `URI` template variables] for `redirect-uri`.\n====\n\n\n[[oauth2login-sample-application-config]]\n=== Configure application.yml\n\nNow that you have a new OAuth Client with Google, you need to configure the application to use the OAuth Client for the _authentication flow_.\nTo do so:\n\n. Go to `application.yml` and set the following configuration:\n+\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\t<1>\n          google:\t<2>\n            client-id: google-client-id\n            client-secret: google-client-secret\n----\n+\n.OAuth Client properties\n<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties.\n<2> Following the base property prefix is the ID for the xref:servlet/oauth2/client/index.adoc#oauth2Client-client-registration[`ClientRegistration`], such as Google.\n\n. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier.\n\n\n[[oauth2login-sample-boot-application]]\n=== Boot up the Application\n\nLaunch the Spring Boot sample and go to `http://localhost:8080`.\nYou are then redirected to the default _auto-generated_ login page, which displays a link for Google.\n\nClick on the Google link, and you are then redirected to Google for authentication.\n\nAfter authenticating with your Google account credentials, you see the Consent screen.\nThe Consent screen asks you to either allow or deny access to the OAuth Client you created earlier.\nClick *Allow* to authorize the OAuth Client to access your email address and basic profile information.\n\nAt this point, the OAuth Client retrieves your email address and basic profile information from the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session.\n\n\n[[oauth2login-boot-property-mappings]]\n== Spring Boot Property Mappings\n\nThe following table outlines the mapping of the Spring Boot OAuth Client properties to the xref:servlet/oauth2/client/index.adoc#oauth2Client-client-registration[ClientRegistration] properties.\n\n|===\n|Spring Boot |ClientRegistration\n\n|`spring.security.oauth2.client.registration._[registrationId]_`\n|`registrationId`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.client-id`\n|`clientId`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.client-secret`\n|`clientSecret`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.client-authentication-method`\n|`clientAuthenticationMethod`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.authorization-grant-type`\n|`authorizationGrantType`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.redirect-uri`\n|`redirectUri`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.scope`\n|`scopes`\n\n|`spring.security.oauth2.client.registration._[registrationId]_.client-name`\n|`clientName`\n\n|`spring.security.oauth2.client.provider._[providerId]_.authorization-uri`\n|`providerDetails.authorizationUri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.token-uri`\n|`providerDetails.tokenUri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.jwk-set-uri`\n|`providerDetails.jwkSetUri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.issuer-uri`\n|`providerDetails.issuerUri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.user-info-uri`\n|`providerDetails.userInfoEndpoint.uri`\n\n|`spring.security.oauth2.client.provider._[providerId]_.user-info-authentication-method`\n|`providerDetails.userInfoEndpoint.authenticationMethod`\n\n|`spring.security.oauth2.client.provider._[providerId]_.user-name-attribute`\n|`providerDetails.userInfoEndpoint.userNameAttributeName`\n|===\n\n[TIP]\n====\nYou can initially configure a `ClientRegistration` by using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint], by specifying the `spring.security.oauth2.client.provider._[providerId]_.issuer-uri` property.\n====\n\n\n[[oauth2login-common-oauth2-provider]]\n== CommonOAuth2Provider\n\n`CommonOAuth2Provider` pre-defines a set of default client properties for a number of well known providers: Google, GitHub, Facebook, X, and Okta.\n\nFor example, the `authorization-uri`, `token-uri`, and `user-info-uri` do not change often for a provider.\nTherefore, it makes sense to provide default values, to reduce the required configuration.\n\nAs demonstrated previously, when we <<oauth2login-sample-application-config,configured a Google client>>, only the `client-id` and `client-secret` properties are required.\n\nThe following listing shows an example:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          google:\n            client-id: google-client-id\n            client-secret: google-client-secret\n----\n\n[TIP]\nThe auto-defaulting of client properties works seamlessly here because the `registrationId` (`google`) matches the `GOOGLE` `enum` (case-insensitive) in `CommonOAuth2Provider`.\n\nFor cases where you may want to specify a different `registrationId`, such as `google-login`, you can still leverage auto-defaulting of client properties by configuring the `provider` property.\n\nThe following listing shows an example:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          google-login:\t<1>\n            provider: google\t<2>\n            client-id: google-client-id\n            client-secret: google-client-secret\n----\n<1> The `registrationId` is set to `google-login`.\n<2> The `provider` property is set to `google`, which will leverage the auto-defaulting of client properties set in `CommonOAuth2Provider.GOOGLE.getBuilder()`.\n\n[[oauth2login-custom-provider-properties]]\n== Configuring Custom Provider Properties\n\nThere are some OAuth 2.0 Providers that support multi-tenancy, which results in different protocol endpoints for each tenant (or sub-domain).\n\nFor example, an OAuth Client registered with Okta is assigned to a specific sub-domain and have their own protocol endpoints.\n\nFor these cases, Spring Boot provides the following base property for configuring custom provider properties: `spring.security.oauth2.client.provider._[providerId]_`.\n\nThe following listing shows an example:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n        provider:\n          okta:\t<1>\n            authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize\n            token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token\n            user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo\n            user-name-attribute: sub\n            jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys\n----\n<1> The base property (`spring.security.oauth2.client.provider.okta`) allows for custom configuration of protocol endpoint locations.\n\n[[oauth2login-override-boot-autoconfig]]\n== Overriding Spring Boot Auto-configuration\n\nThe Spring Boot auto-configuration class for OAuth Client support is `OAuth2ClientAutoConfiguration`.\n\nIt performs the following tasks:\n\n* Registers a `ClientRegistrationRepository` `@Bean` composed of `ClientRegistration`(s) from the configured OAuth Client properties.\n* Registers a `SecurityFilterChain` `@Bean` and enables OAuth 2.0 Login through `httpSecurity.oauth2Login()`.\n\nIf you need to override the auto-configuration based on your specific requirements, you may do so in the following ways:\n\n* <<oauth2login-register-clientregistrationrepository-bean>>\n* <<oauth2login-provide-securityfilterchain-bean>>\n* <<oauth2login-completely-override-autoconfiguration>>\n\n[[oauth2login-register-clientregistrationrepository-bean]]\n=== Register a ClientRegistrationRepository @Bean\n\nThe following example shows how to register a `ClientRegistrationRepository` `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Configuration\npublic class OAuth2LoginConfig {\n\n\t@Bean\n\tpublic ClientRegistrationRepository clientRegistrationRepository() {\n\t\treturn new InMemoryClientRegistrationRepository(this.googleClientRegistration());\n\t}\n\n\tprivate ClientRegistration googleClientRegistration() {\n\t\treturn ClientRegistration.withRegistrationId(\"google\")\n\t\t\t.clientId(\"google-client-id\")\n\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n\t\t\t.scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n\t\t\t.authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n\t\t\t.tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n\t\t\t.userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n\t\t\t.userNameAttributeName(IdTokenClaimNames.SUB)\n\t\t\t.jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n\t\t\t.clientName(\"Google\")\n\t\t\t.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Configuration\nclass OAuth2LoginConfig {\n    @Bean\n    fun clientRegistrationRepository(): ClientRegistrationRepository {\n        return InMemoryClientRegistrationRepository(googleClientRegistration())\n    }\n\n    private fun googleClientRegistration(): ClientRegistration {\n        return ClientRegistration.withRegistrationId(\"google\")\n                .clientId(\"google-client-id\")\n                .clientSecret(\"google-client-secret\")\n                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n                .redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n                .scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n                .authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n                .tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n                .userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n                .userNameAttributeName(IdTokenClaimNames.SUB)\n                .jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n                .clientName(\"Google\")\n                .build()\n    }\n}\n----\n======\n\n\n[[oauth2login-provide-securityfilterchain-bean]]\n=== Register a SecurityFilterChain @Bean\n\nThe following example shows how to register a `SecurityFilterChain` `@Bean` with `@EnableWebSecurity` and enable OAuth 2.0 login through `httpSecurity.oauth2Login()`:\n\n.OAuth2 Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults());\n\t\treturn http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2Login { }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n\n[[oauth2login-completely-override-autoconfiguration]]\n=== Completely Override the Auto-configuration\n\nThe following example shows how to completely override the auto-configuration by registering a `ClientRegistrationRepository` `@Bean` and a `SecurityFilterChain` `@Bean`.\n\n.Overriding the auto-configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",attrs=\"-attributes\"]\n----\n@Configuration\npublic class OAuth2LoginConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults());\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ClientRegistrationRepository clientRegistrationRepository() {\n\t\treturn new InMemoryClientRegistrationRepository(this.googleClientRegistration());\n\t}\n\n\tprivate ClientRegistration googleClientRegistration() {\n\t\treturn ClientRegistration.withRegistrationId(\"google\")\n\t\t\t.clientId(\"google-client-id\")\n\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n\t\t\t.scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n\t\t\t.authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n\t\t\t.tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n\t\t\t.userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n\t\t\t.userNameAttributeName(IdTokenClaimNames.SUB)\n\t\t\t.jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n\t\t\t.clientName(\"Google\")\n\t\t\t.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",attrs=\"-attributes\"]\n----\n@Configuration\nclass OAuth2LoginConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2Login { }\n        }\n        return http.build()\n    }\n\n    @Bean\n    fun clientRegistrationRepository(): ClientRegistrationRepository {\n        return InMemoryClientRegistrationRepository(googleClientRegistration())\n    }\n\n    private fun googleClientRegistration(): ClientRegistration {\n        return ClientRegistration.withRegistrationId(\"google\")\n                .clientId(\"google-client-id\")\n                .clientSecret(\"google-client-secret\")\n                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n                .redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n                .scope(\"openid\", \"profile\", \"email\", \"address\", \"phone\")\n                .authorizationUri(\"https://accounts.google.com/o/oauth2/v2/auth\")\n                .tokenUri(\"https://www.googleapis.com/oauth2/v4/token\")\n                .userInfoUri(\"https://www.googleapis.com/oauth2/v3/userinfo\")\n                .userNameAttributeName(IdTokenClaimNames.SUB)\n                .jwkSetUri(\"https://www.googleapis.com/oauth2/v3/certs\")\n                .clientName(\"Google\")\n                .build()\n    }\n}\n----\n======\n\n\n[[oauth2login-javaconfig-wo-boot]]\n== Java Configuration without Spring Boot\n\nIf you are not able to use Spring Boot and would like to configure one of the pre-defined providers in `CommonOAuth2Provider` (for example, Google), apply the following configuration:\n\n.OAuth2 Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults());\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic ClientRegistrationRepository clientRegistrationRepository() {\n\t\treturn new InMemoryClientRegistrationRepository(this.googleClientRegistration());\n\t}\n\n\t@Bean\n\tpublic OAuth2AuthorizedClientService authorizedClientService(\n\t\t\tClientRegistrationRepository clientRegistrationRepository) {\n\t\treturn new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);\n\t}\n\n\t@Bean\n\tpublic OAuth2AuthorizedClientRepository authorizedClientRepository(\n\t\t\tOAuth2AuthorizedClientService authorizedClientService) {\n\t\treturn new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);\n\t}\n\n\tprivate ClientRegistration googleClientRegistration() {\n\t\treturn CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n\t\t\t.clientId(\"google-client-id\")\n\t\t\t.clientSecret(\"google-client-secret\")\n\t\t\t.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nopen class OAuth2LoginConfig {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2Login { }\n        }\n        return http.build()\n    }\n\n    @Bean\n    open fun clientRegistrationRepository(): ClientRegistrationRepository {\n        return InMemoryClientRegistrationRepository(googleClientRegistration())\n    }\n\n    @Bean\n    open fun authorizedClientService(\n        clientRegistrationRepository: ClientRegistrationRepository?\n    ): OAuth2AuthorizedClientService {\n        return InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository)\n    }\n\n    @Bean\n    open fun authorizedClientRepository(\n        authorizedClientService: OAuth2AuthorizedClientService?\n    ): OAuth2AuthorizedClientRepository {\n        return AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService)\n    }\n\n    private fun googleClientRegistration(): ClientRegistration {\n        return CommonOAuth2Provider.GOOGLE.getBuilder(\"google\")\n            .clientId(\"google-client-id\")\n            .clientSecret(\"google-client-secret\")\n            .build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http auto-config=\"true\">\n\t<intercept-url pattern=\"/**\" access=\"authenticated\"/>\n\t<oauth2-login authorized-client-repository-ref=\"authorizedClientRepository\"/>\n</http>\n\n<client-registrations>\n\t<client-registration registration-id=\"google\"\n\t\t\t\t\t\t client-id=\"google-client-id\"\n\t\t\t\t\t\t client-secret=\"google-client-secret\"\n\t\t\t\t\t\t provider-id=\"google\"/>\n</client-registrations>\n\n<b:bean id=\"authorizedClientService\"\n\t\tclass=\"org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService\"\n\t\tautowire=\"constructor\"/>\n\n<b:bean id=\"authorizedClientRepository\"\n\t\tclass=\"org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository\">\n\t<b:constructor-arg ref=\"authorizedClientService\"/>\n</b:bean>\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/login/index.adoc",
    "content": "[[oauth2login]]\n= OAuth 2.0 Login\n:page-section-summary-toc: 1\n\nThe OAuth 2.0 Login feature lets an application have users log in to the application by using their existing account at an OAuth 2.0 Provider (such as GitHub) or OpenID Connect 1.0 Provider (such as Google).\nOAuth 2.0 Login implements two use cases: \"`Login with Google`\" or \"`Login with GitHub`\".\n\n[NOTE]\n====\nOAuth 2.0 Login is implemented by using the *Authorization Code Grant*, as specified in the https://tools.ietf.org/html/rfc6749#section-4.1[OAuth 2.0 Authorization Framework] and https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[OpenID Connect Core 1.0].\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/login/logout.adoc",
    "content": "= OIDC Logout\n\nOnce an end user is able to login to your application, it's important to consider how they will log out.\n\nGenerally speaking, there are three use cases for you to consider:\n\n1. I want to perform only a local logout\n2. I want to log out both my application and the OIDC Provider, initiated by my application\n3. I want to log out both my application and the OIDC Provider, initiated by the OIDC Provider\n\n[[configure-local-logout]]\n== Local Logout\n\nTo perform a local logout, no special OIDC configuration is needed.\nSpring Security automatically stands up a local logout endpoint, which you can xref:servlet/authentication/logout.adoc[configure through the `logout()` DSL].\n\n[[configure-client-initiated-oidc-logout]]\n== OpenID Connect 1.0 Client-Initiated Logout\n\nOpenID Connect Session Management 1.0 allows the ability to log out the end user at the Provider by using the Client.\nOne of the strategies available is https://openid.net/specs/openid-connect-rpinitiated-1_0.html[RP-Initiated Logout].\n\nIf the OpenID Provider supports both Session Management and https://openid.net/specs/openid-connect-discovery-1_0.html[Discovery], the client can obtain the `end_session_endpoint` `URL` from the OpenID Provider's https://openid.net/specs/openid-connect-session-1_0.html#OPMetadata[Discovery Metadata].\nYou can do so by configuring the `ClientRegistration` with the `issuer-uri`, as follows:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      client:\n        registration:\n          okta:\n            client-id: okta-client-id\n            client-secret: okta-client-secret\n            ...\n        provider:\n          okta:\n            issuer-uri: https://dev-1234.oktapreview.com\n----\n\nAlso, you should configure `OidcClientInitiatedLogoutSuccessHandler`, which implements RP-Initiated Logout, as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class OAuth2LoginSecurityConfig {\n\n\t@Autowired\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.oauth2Login(withDefaults())\n\t\t\t.logout((logout) -> logout\n\t\t\t\t.logoutSuccessHandler(oidcLogoutSuccessHandler())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\tprivate LogoutSuccessHandler oidcLogoutSuccessHandler() {\n\t\tOidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =\n\t\t\t\tnew OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository);\n\n\t\t// Sets the location that the End-User's User Agent will be redirected to\n\t\t// after the logout has been performed at the Provider\n\t\toidcLogoutSuccessHandler.setPostLogoutRedirectUri(\"{baseUrl}\");\n\n\t\treturn oidcLogoutSuccessHandler;\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass OAuth2LoginSecurityConfig {\n    @Autowired\n    private lateinit var clientRegistrationRepository: ClientRegistrationRepository\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2Login { }\n            logout {\n                logoutSuccessHandler = oidcLogoutSuccessHandler()\n            }\n        }\n        return http.build()\n    }\n\n    private fun oidcLogoutSuccessHandler(): LogoutSuccessHandler {\n        val oidcLogoutSuccessHandler = OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository)\n\n        // Sets the location that the End-User's User Agent will be redirected to\n        // after the logout has been performed at the Provider\n        oidcLogoutSuccessHandler.setPostLogoutRedirectUri(\"{baseUrl}\")\n        return oidcLogoutSuccessHandler\n    }\n}\n----\n======\n\n[NOTE]\n====\n`OidcClientInitiatedLogoutSuccessHandler` supports the `+{baseUrl}+` placeholder.\nIf used, the application's base URL, such as `https://app.example.org`, replaces it at request time.\n====\n\n[NOTE]\n====\nBy default, `OidcClientInitiatedLogoutSuccessHandler` redirects to the logout URL using a standard HTTP redirect with the `GET` method.\nTo perform the logout using a `POST` request, set the redirect strategy to `FormPostRedirectStrategy`, for example with `OidcClientInitiatedLogoutSuccessHandler.setRedirectStrategy(new FormPostRedirectStrategy())`.\n====\n\n[[configure-provider-initiated-oidc-logout]]\n== OpenID Connect 1.0 Back-Channel Logout\n\nOpenID Connect Session Management 1.0 allows the ability to log out the end user at the Client by having the Provider make an API call to the Client.\nThis is referred to as https://openid.net/specs/openid-connect-backchannel-1_0.html[OIDC Back-Channel Logout].\n\nTo enable this, you can stand up the Back-Channel Logout endpoint in the DSL like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nOidcBackChannelLogoutHandler oidcLogoutHandler() {\n\treturn new OidcBackChannelLogoutHandler();\n}\n\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n    http\n        .authorizeHttpRequests((authorize) -> authorize\n            .anyRequest().authenticated()\n        )\n        .oauth2Login(withDefaults())\n        .oidcLogout((logout) -> logout\n            .backChannel(Customizer.withDefaults())\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun oidcLogoutHandler(): OidcBackChannelLogoutHandler {\n    return OidcBackChannelLogoutHandler()\n}\n\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        authorizeHttpRequests {\n            authorize(anyRequest, authenticated)\n        }\n        oauth2Login { }\n        oidcLogout {\n            backChannel { }\n        }\n    }\n    return http.build()\n}\n----\n======\n\nThen, you need a way listen to events published by Spring Security to remove old `OidcSessionInformation` entries, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic HttpSessionEventPublisher sessionEventPublisher() {\n    return new HttpSessionEventPublisher();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun sessionEventPublisher(): HttpSessionEventPublisher {\n    return HttpSessionEventPublisher()\n}\n----\n======\n\nThis will make so that if `HttpSession#invalidate` is called, then the session is also removed from memory.\n\nAnd that's it!\n\nThis will stand up the endpoint `+/logout/connect/back-channel/{registrationId}+` which the OIDC Provider can request to invalidate a given session of an end user in your application.\n\n[NOTE]\n`oidcLogout` requires that `oauth2Login` also be configured.\n\n[NOTE]\n`oidcLogout` requires that the session cookie be called `JSESSIONID` in order to correctly log out each session through a backchannel.\n\n=== Back-Channel Logout Architecture\n\nConsider a `ClientRegistration` whose identifier is `registrationId`.\n\nThe overall flow for a Back-Channel logout is like this:\n\n1. At login time, Spring Security correlates the ID Token, CSRF Token, and Provider Session ID (if any) to your application's session id in its `OidcSessionRegistry` implementation.\n2. Then at logout time, your OIDC Provider makes an API call to `/logout/connect/back-channel/registrationId` including a Logout Token that indicates either the `sub` (the End User) or the `sid` (the Provider Session ID) to logout.\n3. Spring Security validates the token's signature and claims.\n4. If the token contains a `sid` claim, then only the Client's session that correlates to that provider session is terminated.\n5. Otherwise, if the token contains a `sub` claim, then all that Client's sessions for that End User are terminated.\n\n[NOTE]\nRemember that Spring Security's OIDC support is multi-tenant.\nThis means that it will only terminate sessions whose Client matches the `aud` claim in the Logout Token.\n\nOne notable part of this architecture's implementation is that it propagates the incoming back-channel request internally for each corresponding session.\nInitially, this may seem unnecessary.\nHowever, recall that the Servlet API does not give direct access to the `HttpSession` store.\nBy making an internal logout call, the corresponding session can now be invalidated.\n\nAdditionally, forging a logout call internally allows for each set of ``LogoutHandler``s to be run against that session and corresponding `SecurityContext`.\n\n=== Customizing the Session Logout Endpoint\n\nWith `OidcBackChannelLogoutHandler` published, the session logout endpoint is `+{baseUrl}+/logout/connect/back-channel/+{registrationId}+`.\n\nIf `OidcBackChannelLogoutHandler` is not wired, then the URL is `+{baseUrl}+/logout/connect/back-channel/+{registrationId}+`, which is not recommended since it requires passing a CSRF token, which can be challenging depending on the kind of repository your application uses.\n\nIn the event that you need to customize the endpoint, you can provide the URL as follows:\n\n\n[tabs]\n======\nJava::\n+\n[source=java,role=\"primary\"]\n----\nhttp\n    // ...\n    .oidcLogout((oidc) -> oidc\n        .backChannel((backChannel) -> backChannel\n            .logoutUri(\"http://localhost:9000/logout/connect/back-channel/+{registrationId}+\")\n        )\n    );\n----\n\nKotlin::\n+\n[source=kotlin,role=\"secondary\"]\n----\nhttp {\n    oidcLogout {\n        backChannel {\n            logoutUri = \"http://localhost:9000/logout/connect/back-channel/+{registrationId}+\"\n        }\n    }\n}\n----\n======\n\n=== Customizing the Session Logout Cookie Name\n\nBy default, the session logout endpoint uses the `JSESSIONID` cookie to correlate the session to the corresponding `OidcSessionInformation`.\n\nHowever, the default cookie name in Spring Session is `SESSION`.\n\nYou can configure Spring Session's cookie name in the DSL like so:\n\n[tabs]\n======\nJava::\n+\n[source=java,role=\"primary\"]\n----\n@Bean\nOidcBackChannelLogoutHandler oidcLogoutHandler(OidcSessionRegistry oidcSessionRegistry) {\n    OidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(oidcSessionRegistry);\n    logoutHandler.setSessionCookieName(\"SESSION\");\n    return logoutHandler;\n}\n----\n\nKotlin::\n+\n[source=kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun oidcLogoutHandler(val sessionRegistry: OidcSessionRegistry): OidcBackChannelLogoutHandler {\n    val logoutHandler = OidcBackChannelLogoutHandler(sessionRegistry)\n    logoutHandler.setSessionCookieName(\"SESSION\")\n    return logoutHandler\n}\n----\n======\n\n[[oidc-backchannel-logout-session-registry]]\n=== Customizing the OIDC Provider Session Registry\n\nBy default, Spring Security stores in-memory all links between the OIDC Provider session and the Client session.\n\nThere are a number of circumstances, like a clustered application, where it would be nice to store this instead in a separate location, like a database.\n\nYou can achieve this by configuring a custom `OidcSessionRegistry`, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic final class MySpringDataOidcSessionRegistry implements OidcSessionRegistry {\n    private final OidcProviderSessionRepository sessions;\n\n    // ...\n\n    @Override\n    public void saveSessionInformation(OidcSessionInformation info) {\n        this.sessions.save(info);\n    }\n\n    @Override\n    public OidcSessionInformation removeSessionInformation(String clientSessionId) {\n       return this.sessions.removeByClientSessionId(clientSessionId);\n    }\n\n    @Override\n    public Iterable<OidcSessionInformation> removeSessionInformation(OidcLogoutToken token) {\n        return token.getSessionId() != null ?\n            this.sessions.removeBySessionIdAndIssuerAndAudience(...) :\n            this.sessions.removeBySubjectAndIssuerAndAudience(...);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass MySpringDataOidcSessionRegistry: OidcSessionRegistry {\n    val sessions: OidcProviderSessionRepository\n\n    // ...\n\n    @Override\n    fun saveSessionInformation(info: OidcSessionInformation) {\n        this.sessions.save(info)\n    }\n\n    @Override\n    fun removeSessionInformation(clientSessionId: String): OidcSessionInformation {\n       return this.sessions.removeByClientSessionId(clientSessionId);\n    }\n\n    @Override\n    fun removeSessionInformation(token: OidcLogoutToken): Iterable<OidcSessionInformation> {\n        return token.getSessionId() != null ?\n            this.sessions.removeBySessionIdAndIssuerAndAudience(...) :\n            this.sessions.removeBySubjectAndIssuerAndAudience(...);\n    }\n}\n----\n======\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/resource-server/bearer-tokens.adoc",
    "content": "= OAuth 2.0 Bearer Tokens\n\n[[oauth2resourceserver-bearertoken-resolver]]\n== Bearer Token Resolution\n\nBy default, Resource Server looks for a bearer token in the `Authorization` header.\nThis, however, can be customized in a handful of ways.\n\n=== Reading the Bearer Token from a Custom Header\n\nFor example, you may have a need to read the bearer token from a custom header.\nTo achieve this, you can expose a `DefaultBearerTokenResolver` as a bean, or wire an instance into the DSL, as you can see in the following example:\n\n.Custom Bearer Token Header\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nBearerTokenResolver bearerTokenResolver() {\n    DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();\n    bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION);\n    return bearerTokenResolver;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun bearerTokenResolver(): BearerTokenResolver {\n    val bearerTokenResolver = DefaultBearerTokenResolver()\n    bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION)\n    return bearerTokenResolver\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <oauth2-resource-server bearer-token-resolver-ref=\"bearerTokenResolver\"/>\n</http>\n\n<bean id=\"bearerTokenResolver\"\n        class=\"org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver\">\n    <property name=\"bearerTokenHeaderName\" value=\"Proxy-Authorization\"/>\n</bean>\n----\n======\n\nOr, in circumstances where a provider is using both a custom header and value, you can use `HeaderBearerTokenResolver` instead.\n\n=== Reading the Bearer Token from a Form Parameter\n\nOr, you may wish to read the token from a form parameter, which you can do by configuring the `DefaultBearerTokenResolver`, as you can see below:\n\n.Form Parameter Bearer Token\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nDefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();\nresolver.setAllowFormEncodedBodyParameter(true);\nhttp\n    .oauth2ResourceServer((oauth2) -> oauth2\n        .bearerTokenResolver(resolver)\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval resolver = DefaultBearerTokenResolver()\nresolver.setAllowFormEncodedBodyParameter(true)\nhttp {\n    oauth2ResourceServer {\n        bearerTokenResolver = resolver\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <oauth2-resource-server bearer-token-resolver-ref=\"bearerTokenResolver\"/>\n</http>\n\n<bean id=\"bearerTokenResolver\"\n        class=\"org.springframework.security.oauth2.server.resource.web.HeaderBearerTokenResolver\">\n    <property name=\"allowFormEncodedBodyParameter\" value=\"true\"/>\n</bean>\n----\n======\n\n== Bearer Token Propagation\n\nNow that your resource server has validated the token, it might be handy to pass it to downstream services.\nThis is quite simple with javadoc:org.springframework.security.oauth2.server.resource.web.reactive.function.client.ServletBearerExchangeFilterFunction[], which you can see in the following example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic WebClient rest() {\n    return WebClient.builder()\n            .filter(new ServletBearerExchangeFilterFunction())\n            .build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun rest(): WebClient {\n    return WebClient.builder()\n            .filter(ServletBearerExchangeFilterFunction())\n            .build()\n}\n----\n======\n\nWhen the above `WebClient` is used to perform requests, Spring Security will look up the current `Authentication` and extract any javadoc:org.springframework.security.oauth2.core.AbstractOAuth2Token[] credential.\nThen, it will propagate that token in the `Authorization` header.\n\nFor example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nthis.rest.get()\n        .uri(\"https://other-service.example.com/endpoint\")\n        .retrieve()\n        .bodyToMono(String.class)\n        .block()\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nthis.rest.get()\n        .uri(\"https://other-service.example.com/endpoint\")\n        .retrieve()\n        .bodyToMono<String>()\n        .block()\n----\n======\n\nWill invoke the `https://other-service.example.com/endpoint`, adding the bearer token `Authorization` header for you.\n\nIn places where you need to override this behavior, it's a simple matter of supplying the header yourself, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nthis.rest.get()\n        .uri(\"https://other-service.example.com/endpoint\")\n        .headers((headers) -> headers.setBearerAuth(overridingToken))\n        .retrieve()\n        .bodyToMono(String.class)\n        .block()\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nthis.rest.get()\n        .uri(\"https://other-service.example.com/endpoint\")\n        .headers{  headers -> headers.setBearerAuth(overridingToken)}\n        .retrieve()\n        .bodyToMono<String>()\n        .block()\n----\n======\n\nIn this case, the filter will fall back and simply forward the request onto the rest of the web filter chain.\n\n[NOTE]\nUnlike the javadoc:org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction[OAuth 2.0 Client filter function], this filter function makes no attempt to renew the token, should it be expired.\nTo obtain this level of support, please use the OAuth 2.0 Client filter.\n\n=== `RestTemplate` support\n\nThere is no `RestTemplate` equivalent for `ServletBearerExchangeFilterFunction` at the moment, but you can propagate the request's bearer token quite simply with your own interceptor:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nRestTemplate rest() {\n\tRestTemplate rest = new RestTemplate();\n\trest.getInterceptors().add((request, body, execution) -> {\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\treturn execution.execute(request, body);\n\t\t}\n\n\t\tif (!(authentication.getCredentials() instanceof AbstractOAuth2Token)) {\n\t\t\treturn execution.execute(request, body);\n\t\t}\n\n\t\tAbstractOAuth2Token token = (AbstractOAuth2Token) authentication.getCredentials();\n\t    request.getHeaders().setBearerAuth(token.getTokenValue());\n\t    return execution.execute(request, body);\n\t});\n\treturn rest;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun rest(): RestTemplate {\n    val rest = RestTemplate()\n    rest.interceptors.add(ClientHttpRequestInterceptor { request, body, execution ->\n        val authentication: Authentication? = SecurityContextHolder.getContext().authentication\n        if (authentication == null) {\n            return execution.execute(request, body)\n        }\n\n        if (authentication.credentials !is AbstractOAuth2Token) {\n            return execution.execute(request, body)\n        }\n\n        request.headers.setBearerAuth(authentication.credentials.tokenValue)\n        execution.execute(request, body)\n    })\n    return rest\n}\n----\n======\n\n\n[NOTE]\nUnlike the javadoc:org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager[OAuth 2.0 Authorized Client Manager], this filter interceptor makes no attempt to renew the token, should it be expired.\nTo obtain this level of support, please create an interceptor using the xref:servlet/oauth2/client/index.adoc#oauth2client[OAuth 2.0 Authorized Client Manager].\n\n[[oauth2resourceserver-bearertoken-failure]]\n== Bearer Token Failure\n\nA bearer token may be invalid for a number of reasons. For example, the token may no longer be active.\n\nIn these circumstances, Resource Server throws an `InvalidBearerTokenException`.\nLike other exceptions, this results in an OAuth 2.0 Bearer Token error response:\n\n[source,http request]\n----\nHTTP/1.1 401 Unauthorized\nWWW-Authenticate: Bearer error_code=\"invalid_token\", error_description=\"Unsupported algorithm of none\", error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"\n----\n\nAdditionally, it is published as an `AuthenticationFailureBadCredentialsEvent`, which you can xref:servlet/authentication/events.adoc#servlet-events[listen for in your application] like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class FailureEvents {\n\t@EventListener\n    public void onFailure(AuthenticationFailureBadCredentialsEvent badCredentials) {\n\t\tif (badCredentials.getAuthentication() instanceof BearerTokenAuthenticationToken) {\n\t\t    // ... handle\n        }\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass FailureEvents {\n    @EventListener\n    fun onFailure(badCredentials: AuthenticationFailureBadCredentialsEvent) {\n        if (badCredentials.authentication is BearerTokenAuthenticationToken) {\n            // ... handle\n        }\n    }\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/resource-server/dpop-tokens.adoc",
    "content": "[[oauth2-dpop-bound-access-tokens]]\n= OAuth 2.0 DPoP-bound Access Tokens\n\nhttps://datatracker.ietf.org/doc/html/rfc9449[RFC 9449 OAuth 2.0 Demonstrating Proof of Possession (DPoP)] is an application-level mechanism for sender-constraining an access token.\n\nThe primary goal of DPoP is to prevent unauthorized or illegitimate clients from using leaked or stolen access tokens, by binding an access token to a public key upon issuance by the authorization server and requiring that the client proves possession of the corresponding private key when using the access token at the resource server.\n\nAccess tokens that are sender-constrained via DPoP stand in contrast to the typical bearer token, which can be used by any client in possession of the access token.\n\nDPoP introduces the concept of a https://datatracker.ietf.org/doc/html/rfc9449#name-dpop-proof-jwts[DPoP Proof], which is a JWT created by the client and sent as a header in an HTTP request.\nA client uses a DPoP proof to prove the possession of a private key corresponding to a certain public key.\n\nWhen the client initiates an <<dpop-access-token-request,access token request>>, it attaches a DPoP proof to the request in an HTTP header.\nThe authorization server binds (sender-constrains) the access token to the public key associated in the DPoP proof.\n\nWhen the client initiates a <<dpop-protected-resource-request,protected resource request>>, it again attaches a DPoP proof to the request in an HTTP header.\n\nThe resource server obtains information about the public key bound to the access token, either directly in the access token (JWT) or via the token introspection endpoint.\nThe resource server then verifies that the public key bound to the access token matches the public key in the DPoP proof.\nIt also verifies that the access token hash in the DPoP proof matches the access token in the request.\n\n[[dpop-access-token-request]]\n== DPoP Access Token Request\n\nTo request an access token that is bound to a public key using DPoP, the client MUST provide a valid DPoP proof in the `DPoP` header when making an access token request to the authorization server token endpoint.\nThis is applicable for all access token requests regardless of authorization grant type (e.g. `authorization_code`, `refresh_token`, `client_credentials`, etc).\n\nThe following HTTP request shows an `authorization_code` access token request with a DPoP proof in the `DPoP` header:\n\n[source,shell]\n----\nPOST /oauth2/token HTTP/1.1\nHost: server.example.com\nContent-Type: application/x-www-form-urlencoded\nDPoP: eyJraWQiOiJyc2EtandrLWtpZCIsInR5cCI6ImRwb3Arand0IiwiYWxnIjoiUlMyNTYiLCJqd2siOnsia3R5IjoiUlNBIiwiZSI6IkFRQUIiLCJraWQiOiJyc2EtandrLWtpZCIsIm4iOiIzRmxxSnI1VFJza0lRSWdkRTNEZDdEOWxib1dkY1RVVDhhLWZKUjdNQXZRbTdYWE5vWWttM3Y3TVFMMU5ZdER2TDJsOENBbmMwV2RTVElOVTZJUnZjNUtxbzJRNGNzTlg5U0hPbUVmem9ST2pRcWFoRWN2ZTFqQlhsdW9DWGRZdVlweDRfMXRmUmdHNmlpNFVoeGg2aUk4cU5NSlFYLWZMZnFoYmZZZnhCUVZSUHl3QmtBYklQNHgxRUFzYkM2RlNObWtoQ3hpTU5xRWd4YUlwWThDMmtKZEpfWklWLVdXNG5vRGR6cEtxSGN3bUI4RnNydW1sVllfRE5WdlVTRElpcGlxOVBiUDRIOTlUWE4xbzc0Nm9SYU5hMDdycTFob0NnTVNTeS04NVNhZ0NveGxteUUtRC1vZjlTc01ZOE9sOXQwcmR6cG9iQnVoeUpfbzVkZnZqS3cifX0.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20vb2F1dGgyL3Rva2VuIiwiaWF0IjoxNzQ2ODA2MzA1LCJqdGkiOiI0YjIzNDBkMi1hOTFmLTQwYTUtYmFhOS1kZDRlNWRlYWM4NjcifQ.wq8gJ_G6vpiEinfaY3WhereqCCLoeJOG8tnWBBAzRWx9F1KU5yAAWq-ZVCk_k07-h6DIqz2wgv6y9dVbNpRYwNwDUeik9qLRsC60M8YW7EFVyI3n_NpujLwzZeub_nDYMVnyn4ii0NaZrYHtoGXOlswQfS_-ET-jpC0XWm5nBZsCdUEXjOYtwaACC6Js-pyNwKmSLp5SKIk11jZUR5xIIopaQy521y9qJHhGRwzj8DQGsP7wMZ98UFL0E--1c-hh4rTy8PMeWCqRHdwjj_ry_eTe0DJFcxxYQdeL7-0_0CIO4Ayx5WHEpcUOIzBRoN32RsNpDZc-5slDNj9ku004DA\n\ngrant_type=authorization_code\\\n&client_id=s6BhdRkqt\\\n&code=SplxlOBeZQQYbYS6WxSbIA\\\n&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb\\\n&code_verifier=bEaL42izcC-o-xBk0K2vuJ6U-y1p9r_wW2dFWIWgjz-\n----\n\nThe following shows a representation of the DPoP Proof JWT header and claims:\n\n[source,json]\n----\n{\n  \"typ\": \"dpop+jwt\",\n  \"alg\": \"RS256\",\n  \"jwk\": {\n    \"kty\": \"RSA\",\n    \"e\": \"AQAB\",\n    \"n\": \"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\"\n  }\n}\n----\n\n[source,json]\n----\n{\n  \"htm\": \"POST\",\n  \"htu\": \"https://server.example.com/oauth2/token\",\n  \"iat\": 1746806305,\n  \"jti\": \"4b2340d2-a91f-40a5-baa9-dd4e5deac867\"\n}\n----\n\nThe following code shows an example of how to generate the DPoP Proof JWT:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nRSAKey rsaKey = ...\nJWKSource<SecurityContext> jwkSource = (jwkSelector, securityContext) -> jwkSelector\n\t\t.select(new JWKSet(rsaKey));\nNimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);\n\nJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t.type(\"dpop+jwt\")\n\t\t.jwk(rsaKey.toPublicJWK().toJSONObject())\n\t\t.build();\nJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t.issuedAt(Instant.now())\n\t\t.claim(\"htm\", \"POST\")\n\t\t.claim(\"htu\", \"https://server.example.com/oauth2/token\")\n\t\t.id(UUID.randomUUID().toString())\n\t\t.build();\n\nJwt dPoPProof = jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n----\n======\n\nAfter the authorization server successfully validates the DPoP proof, the public key from the DPoP proof will be bound (sender-constrained) to the issued access token.\n\nThe following access token response shows the `token_type` parameter as `DPoP` to signal to the client that the access token was bound to its DPoP proof public key:\n\n[source,shell]\n----\nHTTP/1.1 200 OK\nContent-Type: application/json\nCache-Control: no-store\n\n{\n \"access_token\": \"Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU\",\n \"token_type\": \"DPoP\",\n \"expires_in\": 2677\n}\n----\n\n[[dpop-public-key-confirmation]]\n== Public Key Confirmation\n\nResource servers MUST be able to identify whether an access token is DPoP-bound and verify the binding to the public key of the DPoP proof.\nThe binding is accomplished by associating the public key with the access token in a way that can be accessed by the resource server, such as embedding the public key hash in the access token directly (JWT) or through token introspection.\n\nWhen an access token is represented as a JWT, the public key hash is contained in the `jkt` claim under the confirmation method (`cnf`) claim.\n\nThe following example shows the claims of a JWT access token containing a `cnf` claim with a `jkt` claim, which is the JWK SHA-256 Thumbprint of the DPoP proof public key:\n\n[source,json]\n----\n{\n  \"sub\":\"user@example.com\",\n  \"iss\":\"https://server.example.com\",\n  \"nbf\":1562262611,\n  \"exp\":1562266216,\n  \"cnf\":\n  {\n    \"jkt\":\"CQMknzRoZ5YUi7vS58jck1q8TmZT8wiIiXrCN1Ny4VU\"\n  }\n}\n----\n\n[[dpop-protected-resource-request]]\n== DPoP Protected Resource Request\n\nRequests to DPoP-protected resources MUST include both a DPoP proof and the DPoP-bound access token.\nThe DPoP proof MUST include the `ath` claim with a valid hash of the access token.\nThe resource server will calculate the hash of the received access token and verify that it is the same as the `ath` claim in the DPoP proof.\n\nA DPoP-bound access token is sent using the `Authorization` request header with an authentication scheme of `DPoP`.\n\nThe following HTTP request shows a protected resource request with a DPoP-bound access token in the `Authorization` header and the DPoP proof in the `DPoP` header:\n\n[source,shell]\n----\nGET /resource HTTP/1.1\nHost: resource.example.com\nAuthorization: DPoP Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU\nDPoP: eyJraWQiOiJyc2EtandrLWtpZCIsInR5cCI6ImRwb3Arand0IiwiYWxnIjoiUlMyNTYiLCJqd2siOnsia3R5IjoiUlNBIiwiZSI6IkFRQUIiLCJraWQiOiJyc2EtandrLWtpZCIsIm4iOiIzRmxxSnI1VFJza0lRSWdkRTNEZDdEOWxib1dkY1RVVDhhLWZKUjdNQXZRbTdYWE5vWWttM3Y3TVFMMU5ZdER2TDJsOENBbmMwV2RTVElOVTZJUnZjNUtxbzJRNGNzTlg5U0hPbUVmem9ST2pRcWFoRWN2ZTFqQlhsdW9DWGRZdVlweDRfMXRmUmdHNmlpNFVoeGg2aUk4cU5NSlFYLWZMZnFoYmZZZnhCUVZSUHl3QmtBYklQNHgxRUFzYkM2RlNObWtoQ3hpTU5xRWd4YUlwWThDMmtKZEpfWklWLVdXNG5vRGR6cEtxSGN3bUI4RnNydW1sVllfRE5WdlVTRElpcGlxOVBiUDRIOTlUWE4xbzc0Nm9SYU5hMDdycTFob0NnTVNTeS04NVNhZ0NveGxteUUtRC1vZjlTc01ZOE9sOXQwcmR6cG9iQnVoeUpfbzVkZnZqS3cifX0.eyJodG0iOiJHRVQiLCJodHUiOiJodHRwczovL3Jlc291cmNlLmV4YW1wbGUuY29tL3Jlc291cmNlIiwiYXRoIjoiZlVIeU8ycjJaM0RaNTNFc05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyIsImlhdCI6MTc0NjgwNzEzOCwianRpIjoiM2MyZWU5YmItMDNhYy00MGNmLWI4MTItMDBiZmJhMzQxY2VlIn0.oS6NwjURR6wZemh1ZBNiBjycGeXwnkguLtgiKdCjQSEhFQpEJm04bBa0tdfZgWT17Z2mBgddnNQSkROzUGfssg8rBBldZXOAiduF-whtEGZA-pXXWJilXrwH3Glb6hIOMZOVmIH8fmYCDmqn-sE_DmDIsv57Il2-jdZbgeDcrxADO-6E5gsuNf1jvy7qqHq7INrKX6jRuydti_Re35lecvaAWfTyD7s7tQ_-3x_xLxxPwf_eA6z8OWbc58O2PYoUeO2JKLiOIg6UVZOZzxLEWV42WIKjha_kkoykvsf98W2y8pWOEr65u0VPsn5esw2X3I1eFL_A-XkxstZHRaGXJg\n----\n\nThe following shows a representation of the DPoP Proof JWT header and claims with the `ath` claim:\n\n[source,json]\n----\n{\n  \"typ\": \"dpop+jwt\",\n  \"alg\": \"RS256\",\n  \"jwk\": {\n    \"kty\": \"RSA\",\n    \"e\": \"AQAB\",\n    \"n\": \"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\"\n  }\n}\n----\n\n[source,json]\n----\n{\n  \"htm\": \"GET\",\n  \"htu\": \"https://resource.example.com/resource\",\n  \"ath\": \"fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo\",\n  \"iat\": 1746807138,\n  \"jti\": \"3c2ee9bb-03ac-40cf-b812-00bfba341cee\"\n}\n----\n\nThe following code shows an example of how to generate the DPoP Proof JWT:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nRSAKey rsaKey = ...\nJWKSource<SecurityContext> jwkSource = (jwkSelector, securityContext) -> jwkSelector\n\t\t.select(new JWKSet(rsaKey));\nNimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);\n\nString accessToken = ...\n\nJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t.type(\"dpop+jwt\")\n\t\t.jwk(rsaKey.toPublicJWK().toJSONObject())\n\t\t.build();\nJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t.issuedAt(Instant.now())\n\t\t.claim(\"htm\", \"GET\")\n\t\t.claim(\"htu\", \"https://resource.example.com/resource\")\n\t\t.claim(\"ath\", sha256(accessToken))\n\t\t.id(UUID.randomUUID().toString())\n\t\t.build();\n\nJwt dPoPProof = jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/resource-server/index.adoc",
    "content": "[[oauth2resourceserver]]\n= OAuth 2.0 Resource Server\n:figures: servlet/oauth2\n\nSpring Security supports protecting endpoints by using two forms of OAuth 2.0 https://tools.ietf.org/html/rfc6750.html[Bearer Tokens]:\n\n* https://tools.ietf.org/html/rfc7519[JWT]\n* Opaque Tokens\n\nThis is handy in circumstances where an application has delegated its authority management to an https://tools.ietf.org/html/rfc6749[authorization server] (for example, Okta or Ping Identity).\nThis authorization server can be consulted by resource servers to authorize requests.\n\nThis section details how Spring Security provides support for OAuth 2.0 https://tools.ietf.org/html/rfc6750.html[Bearer Tokens].\n\n[NOTE]\n====\nWorking samples for both {gh-samples-url}/servlet/spring-boot/java/oauth2/resource-server/jwe[JWTs] and {gh-samples-url}/servlet/spring-boot/java/oauth2/resource-server/opaque[Opaque Tokens] are available in the {gh-samples-url}[Spring Security Samples repository].\n====\n\nNow we can consider how Bearer Token Authentication works within Spring Security.\nFirst, we see that, as with xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[Basic Authentication], the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authenticate] header is sent back to an unauthenticated client:\n\n.Sending WWW-Authenticate Header\n[.invert-dark]\nimage::{figures}/bearerauthenticationentrypoint.png[]\n\nThe figure above builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.\n\nimage:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the `/private` resource for which the user is not authorized.\n\nimage:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is _Denied_ by throwing an `AccessDeniedException`.\n\nimage:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates _Start Authentication_.\nThe configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of javadoc:org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint[], which sends a `WWW-Authenticate` header.\nThe `RequestCache` is typically a `NullRequestCache` that does not save the request, since the client is capable of replaying the requests it originally requested.\n\nWhen a client receives the `WWW-Authenticate: Bearer` header, it knows it should retry with a bearer token.\nThe following image shows the flow for the bearer token being processed:\n\n[[oauth2resourceserver-authentication-bearertokenauthenticationfilter]]\n.Authenticating Bearer Token\n[.invert-dark]\nimage::{figures}/bearertokenauthenticationfilter.png[]\n\nThe figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.\n\nimage:{icondir}/number_1.png[] When the user submits their bearer token, the `BearerTokenAuthenticationFilter` creates a `BearerTokenAuthenticationToken` which is a type of xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] by extracting the token from the `HttpServletRequest`.\n\nimage:{icondir}/number_2.png[] Next, the `HttpServletRequest` is passed to the `AuthenticationManagerResolver`, which selects the `AuthenticationManager`. The `BearerTokenAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated.\nThe details of what `AuthenticationManager` looks like depends on whether you're configured for xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-minimalconfiguration[JWT] or xref:servlet/oauth2/resource-server/opaque-token.adoc#oauth2resourceserver-opaque-minimalconfiguration[opaque token].\n\nimage:{icondir}/number_3.png[] If authentication fails, then __Failure__\n\n* The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.\n* The `AuthenticationEntryPoint` is invoked to trigger the WWW-Authenticate header to be sent again.\n\nimage:{icondir}/number_4.png[] If authentication is successful, then __Success__.\n\n* Any already-authenticated `Authentication` in the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] is loaded and its\nauthorities are added to the returned xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`].\n* The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].\n* The `BearerTokenAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc",
    "content": "= OAuth 2.0 Resource Server JWT\n:figures: servlet/oauth2\n\n[[oauth2resourceserver-jwt-minimaldependencies]]\n== Minimal Dependencies for JWT\n\nMost Resource Server support is collected into `spring-security-oauth2-resource-server`.\nHowever, the support for decoding and verifying JWTs is in `spring-security-oauth2-jose`, meaning that both are necessary in order to have a working resource server that supports JWT-encoded Bearer Tokens.\n\n[[oauth2resourceserver-jwt-minimalconfiguration]]\n== Minimal Configuration for JWTs\n\nWhen using https://spring.io/projects/spring-boot[Spring Boot], configuring an application as a resource server consists of two basic steps.\nFirst, include the needed dependencies and second, indicate the location of the authorization server.\n\n=== Specifying the Authorization Server\n\nIn a Spring Boot application, to specify which authorization server to use, simply do:\n\n[source,yml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          issuer-uri: https://idp.example.com/issuer\n----\n\nWhere `https://idp.example.com/issuer` is the value contained in the `iss` claim for JWT tokens that the authorization server will issue.\nResource Server will use this property to further self-configure, discover the authorization server's public keys, and subsequently validate incoming JWTs.\n\n[NOTE]\nTo use the `issuer-uri` property, it must also be true that one of `https://idp.example.com/issuer/.well-known/openid-configuration`, `https://idp.example.com/.well-known/openid-configuration/issuer`, or `https://idp.example.com/.well-known/oauth-authorization-server/issuer` is a supported endpoint for the authorization server.\nThis endpoint is referred to as a https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Provider Configuration] endpoint or a https://tools.ietf.org/html/rfc8414#section-3[Authorization Server Metadata] endpoint.\n\nAnd that's it!\n\n=== Startup Expectations\n\nWhen this property and these dependencies are used, Resource Server will automatically configure itself to validate JWT-encoded Bearer Tokens.\n\nIt achieves this through a deterministic discovery process it launches at the first request containing a JWT:\n\n1. Query the Provider Configuration or Authorization Server Metadata endpoint for the `jwks_url` property\n2. Query the `jwks_url` endpoint for supported algorithms\n3. Configure the validation strategy to query `jwks_url` for valid public keys of the algorithms found\n4. Configure the validation strategy to validate each JWTs `iss` claim against `https://idp.example.com`.\n\nOne benefit of deferring this process is that Resource Server startup is not coupled to the authorization server's availability.\n\n[NOTE]\n====\nThis deferral is managed by javadoc:org.springframework.security.oauth2.jwt.SupplierJwtDecoder[`SupplierJwtDecoder`].\nConsider wrapping any <<oauth2resourceserver-jwt-decoder,`JwtDecoder` `@Bean`>> you declare in order to preserve this behavior.\n====\n\n=== Runtime Expectations\n\nOnce the application is started up, Resource Server will attempt to process any request containing an `Authorization: Bearer` header:\n\n[source,html]\n----\nGET / HTTP/1.1\nAuthorization: Bearer some-token-value # Resource Server will process this\n----\n\nSo long as this scheme is indicated, Resource Server will attempt to process the request according to the Bearer Token specification.\n\nGiven a well-formed JWT, Resource Server will:\n\n1. Validate its signature against a public key obtained from the `jwks_url` endpoint during startup or on first request, depending on configuration, and matched against the JWT\n2. Validate the JWT's `exp` and `nbf` timestamps and the JWT's `iss` claim, and\n3. Map each scope to an authority with the prefix `SCOPE_`.\n\n[NOTE]\nAs the authorization server makes available new keys, Spring Security will automatically rotate the keys used to validate JWTs.\n\nThe resulting `Authentication#getPrincipal`, by default, is a Spring Security `Jwt` object, and `Authentication#getName` maps to the JWT's `sub` property, if one is present.\n\nFrom here, consider jumping to:\n\n* <<oauth2resourceserver-jwt-architecture,How JWT Authentication Works>>\n* <<oauth2resourceserver-jwt-jwkseturi,How to Configure without tying Resource Server startup to an authorization server's availability>>\n* <<oauth2resourceserver-jwt-sansboot,How to Configure without Spring Boot>>\n\n[[oauth2resourceserver-jwt-architecture]]\n== How JWT Authentication Works\n\nNext, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7519[JWT] Authentication in servlet-based applications, like the one we just saw.\n\njavadoc:org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider[] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <<oauth2resourceserver-jwt-decoder,`JwtDecoder`>> and <<oauth2resourceserver-jwt-authorization-extraction,`JwtAuthenticationConverter`>> to authenticate a JWT.\n\nLet's take a look at how `JwtAuthenticationProvider` works within Spring Security.\nThe figure explains details of how the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from xref:servlet/oauth2/resource-server/index.adoc#oauth2resourceserver-authentication-bearertokenauthenticationfilter[Reading the Bearer Token] works.\n\n.`JwtAuthenticationProvider` Usage\n[.invert-dark]\nimage::{figures}/jwtauthenticationprovider.png[]\n\nimage:{icondir}/number_1.png[] The authentication `Filter` from xref:servlet/oauth2/resource-server/index.adoc#oauth2resourceserver-authentication-bearertokenauthenticationfilter[Reading the Bearer Token] passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`ProviderManager`].\n\nimage:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `JwtAuthenticationProvider`.\n\n[[oauth2resourceserver-jwt-architecture-jwtdecoder]]\nimage:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, and validates the `Jwt` using a <<oauth2resourceserver-jwt-decoder,`JwtDecoder`>>.\n\n[[oauth2resourceserver-jwt-architecture-jwtauthenticationconverter]]\nimage:{icondir}/number_4.png[] `JwtAuthenticationProvider` then uses the <<oauth2resourceserver-jwt-authorization-extraction,`JwtAuthenticationConverter`>> to convert the `Jwt` into a `Collection` of granted authorities.\n\nimage:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `JwtAuthenticationToken` and has a principal that is the `Jwt` returned by the configured `JwtDecoder` and a set of authorities that contains at least `FACTOR_BEARER`.\nUltimately, the returned `JwtAuthenticationToken` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.\n\n[[oauth2resourceserver-jwt-jwkseturi]]\n== Specifying the Authorization Server JWK Set Uri Directly\n\nIf the authorization server doesn't support any configuration endpoints, or if Resource Server must be able to initialize independently from the authorization server, then the `jwk-set-uri` can be supplied as well:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          issuer-uri: https://idp.example.com\n          jwk-set-uri: https://idp.example.com/.well-known/jwks.json\n----\n\n[NOTE]\nThe JWK Set uri is not standardized, but can typically be found in the authorization server's documentation\n\nConsequently, Resource Server will not ping the authorization server at startup.\nWe still specify the `issuer-uri` so that Resource Server still validates the `iss` claim on incoming JWTs.\n\n[NOTE]\nThis property can also be supplied directly on the <<oauth2resourceserver-jwt-jwkseturi-dsl,DSL>>.\n\n== Supplying Audiences\n\nAs already seen, the <<_specifying_the_authorization_server, `issuer-uri` property validates the `iss` claim>>; this is who sent the JWT.\n\nBoot also has the `audiences` property for validating the `aud` claim; this is who the JWT was sent to.\n\nA resource server's audience can be indicated like so:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          issuer-uri: https://idp.example.com\n          audiences: https://my-resource-server.example.com\n----\n\n[NOTE]\nYou can also add <<oauth2resourceserver-jwt-validation-custom, the `aud` validation programmatically>>, if needed.\n\nThe result will be that if the JWT's `iss` claim is not `https://idp.example.com`, and its `aud` claim does not contain `https://my-resource-server.example.com` in its list, then validation will fail.\n\n[[oauth2resourceserver-jwt-sansboot]]\n== Overriding or Replacing Boot Auto Configuration\n\nThere are two ``@Bean``s that Spring Boot generates on Resource Server's behalf.\n\nThe first is a `SecurityFilterChain` that configures the app as a resource server. When including `spring-security-oauth2-jose`, this `SecurityFilterChain` looks like:\n\n.Default JWT Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n    http\n        .authorizeHttpRequests((authorize) -> authorize\n            .anyRequest().authenticated()\n        )\n        .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        authorizeHttpRequests {\n            authorize(anyRequest, authenticated)\n        }\n        oauth2ResourceServer {\n            jwt { }\n        }\n    }\n    return http.build()\n}\n----\n======\n\nIf the application doesn't expose a `SecurityFilterChain` bean, then Spring Boot will expose the above default one.\n\nReplacing this is as simple as exposing the bean within the application:\n\n.Custom JWT Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;\n\n@Configuration\n@EnableWebSecurity\npublic class MyCustomSecurityConfiguration {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .requestMatchers(\"/messages/**\").access(hasScope(\"message:read\"))\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .jwt((jwt) -> jwt\n                    .jwtAuthenticationConverter(myConverter())\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope\n\n@Configuration\n@EnableWebSecurity\nclass MyCustomSecurityConfiguration {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(\"/messages/**\", hasScope(\"message:read\"))\n                authorize(anyRequest, authenticated)\n            }\n            oauth2ResourceServer {\n                jwt {\n                    jwtAuthenticationConverter = myConverter()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nThe above requires the scope of `message:read` for any URL that starts with `/messages/`.\n\nMethods on the `oauth2ResourceServer` DSL will also override or replace auto configuration.\n\n[[oauth2resourceserver-jwt-decoder]]\nFor example, the second `@Bean` Spring Boot creates is a `JwtDecoder`, which <<oauth2resourceserver-jwt-architecture-jwtdecoder,decodes `String` tokens into validated instances of `Jwt`>>:\n\n.JWT Decoder\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoder jwtDecoder() {\n    return JwtDecoders.fromIssuerLocation(issuerUri);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    return JwtDecoders.fromIssuerLocation(issuerUri)\n}\n----\n======\n\n[NOTE]\nCalling javadoc:org.springframework.security.oauth2.jwt.JwtDecoders#fromIssuerLocation-java.lang.String-[JwtDecoders#fromIssuerLocation] is what invokes the Provider Configuration or Authorization Server Metadata endpoint in order to derive the JWK Set Uri.\n\nIf the application doesn't expose a `JwtDecoder` bean, then Spring Boot will expose the above default one.\n\nAnd its configuration can be overridden using `jwkSetUri()` or replaced using `decoder()`.\n\nOr, if you're not using Spring Boot at all, then both of these components - the filter chain and a `JwtDecoder` can be specified in XML.\n\nThe filter chain is specified like so:\n\n.Default JWT Configuration\n[tabs]\n======\nXml::\n+\n[source,xml,role=\"primary\"]\n----\n<http>\n    <intercept-uri pattern=\"/**\" access=\"authenticated\"/>\n    <oauth2-resource-server>\n        <jwt decoder-ref=\"jwtDecoder\"/>\n    </oauth2-resource-server>\n</http>\n----\n======\n\nAnd the `JwtDecoder` like so:\n\n.JWT Decoder\n[tabs]\n======\nXml::\n+\n[source,xml,role=\"primary\"]\n----\n<bean id=\"jwtDecoder\"\n        class=\"org.springframework.security.oauth2.jwt.JwtDecoders\"\n        factory-method=\"fromIssuerLocation\">\n    <constructor-arg value=\"${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}\"/>\n</bean>\n----\n======\n\n[[oauth2resourceserver-jwt-jwkseturi-dsl]]\n=== Using `jwkSetUri()`\n\nAn authorization server's JWK Set Uri can be configured <<oauth2resourceserver-jwt-jwkseturi,as a configuration property>> or it can be supplied in the DSL:\n\n.JWK Set Uri Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class DirectlyConfiguredJwkSetUri {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .jwt((jwt) -> jwt\n                    .jwkSetUri(\"https://idp.example.com/.well-known/jwks.json\")\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass DirectlyConfiguredJwkSetUri {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2ResourceServer {\n                jwt {\n                    jwkSetUri = \"https://idp.example.com/.well-known/jwks.json\"\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-uri pattern=\"/**\" access=\"authenticated\"/>\n    <oauth2-resource-server>\n        <jwt jwk-set-uri=\"https://idp.example.com/.well-known/jwks.json\"/>\n    </oauth2-resource-server>\n</http>\n----\n======\n\nUsing `jwkSetUri()` takes precedence over any configuration property.\n\n[[oauth2resourceserver-jwt-decoder-dsl]]\n=== Using `decoder()`\n\nMore powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>>:\n\n.JWT Decoder Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class DirectlyConfiguredJwtDecoder {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .jwt((jwt) -> jwt\n                    .decoder(myCustomDecoder())\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass DirectlyConfiguredJwtDecoder {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2ResourceServer {\n                jwt {\n                    jwtDecoder = myCustomDecoder()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-uri pattern=\"/**\" access=\"authenticated\"/>\n    <oauth2-resource-server>\n        <jwt decoder-ref=\"myCustomDecoder\"/>\n    </oauth2-resource-server>\n</http>\n----\n======\n\nThis is handy when deeper configuration, like <<oauth2resourceserver-jwt-validation,validation>>, <<oauth2resourceserver-jwt-claimsetmapping,mapping>>, or <<oauth2resourceserver-jwt-timeouts,request timeouts>>, is necessary.\n\n[[oauth2resourceserver-jwt-decoder-bean]]\n=== Exposing a `JwtDecoder` `@Bean`\n\nOr, exposing a <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> `@Bean` has the same effect as `decoder()`.\nYou can construct one with a `jwkSetUri` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoder jwtDecoder() {\n    return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build()\n}\n----\n======\n\nor you can use the issuer and have `NimbusJwtDecoder` look up the `jwkSetUri` when `build()` is invoked, like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoder jwtDecoder() {\n    return NimbusJwtDecoder.withIssuerLocation(issuer).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    return NimbusJwtDecoder.withIssuerLocation(issuer).build()\n}\n----\n======\n\nOr, if the defaults work for you, you can also use `JwtDecoders`, which does the above in addition to configuring the decoder's validator:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoders jwtDecoder() {\n    return JwtDecoders.fromIssuerLocation(issuer);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoders {\n    return JwtDecoders.fromIssuerLocation(issuer)\n}\n----\n======\n\n[[oauth2resourceserver-jwt-decoder-algorithm]]\n== Configuring Trusted Algorithms\n\nBy default, `NimbusJwtDecoder`, and hence Resource Server, will only trust and verify tokens using `RS256`.\n\nYou can customize this via <<oauth2resourceserver-jwt-boot-algorithm,Spring Boot>>, <<oauth2resourceserver-jwt-decoder-builder,the NimbusJwtDecoder builder>>, or from the <<oauth2resourceserver-jwt-decoder-jwk-response,JWK Set response>>.\n\n[[oauth2resourceserver-jwt-boot-algorithm]]\n=== Via Spring Boot\n\nThe simplest way to set the algorithm is as a property:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          jws-algorithms: RS512\n          jwk-set-uri: https://idp.example.org/.well-known/jwks.json\n----\n\n[[oauth2resourceserver-jwt-decoder-builder]]\n=== Using a Builder\n\nFor greater power, though, we can use a builder that ships with `NimbusJwtDecoder`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n    return NimbusJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithm(RS512).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    return NimbusJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithm(RS512).build()\n}\n----\n======\n\nCalling `jwsAlgorithm` more than once will configure `NimbusJwtDecoder` to trust more than one algorithm, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n    return NimbusJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithm(RS512).jwsAlgorithm(ES512).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    return NimbusJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithm(RS512).jwsAlgorithm(ES512).build()\n}\n----\n======\n\nOr, you can call `jwsAlgorithms`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n    return NimbusJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithms(algorithms -> {\n                    algorithms.add(RS512);\n                    algorithms.add(ES512);\n            }).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    return NimbusJwtDecoder.withIssuerLocation(this.issuer)\n            .jwsAlgorithms {\n                it.add(RS512)\n                it.add(ES512)\n            }.build()\n}\n----\n======\n\n[[oauth2resourceserver-jwt-decoder-jwk-response]]\n=== From JWK Set response\n\nSince Spring Security's JWT support is based off of Nimbus, you can use all it's great features as well.\n\nFor example, Nimbus has a `JWSKeySelector` implementation that will select the set of algorithms based on the JWK Set URI response.\nYou can use it to generate a `NimbusJwtDecoder` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoder jwtDecoder() {\n    // makes a request to the JWK Set endpoint\n    JWSKeySelector<SecurityContext> jwsKeySelector =\n            JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(this.jwkSetUrl);\n\n    DefaultJWTProcessor<SecurityContext> jwtProcessor =\n            new DefaultJWTProcessor<>();\n    jwtProcessor.setJWSKeySelector(jwsKeySelector);\n\n    return new NimbusJwtDecoder(jwtProcessor);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    // makes a request to the JWK Set endpoint\n    val jwsKeySelector: JWSKeySelector<SecurityContext> = JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL<SecurityContext>(this.jwkSetUrl)\n    val jwtProcessor: DefaultJWTProcessor<SecurityContext> = DefaultJWTProcessor()\n    jwtProcessor.jwsKeySelector = jwsKeySelector\n    return NimbusJwtDecoder(jwtProcessor)\n}\n----\n======\n\n[[oauth2resourceserver-jwt-decoder-public-key]]\n== Trusting a Single Asymmetric Key\n\nSimpler than backing a Resource Server with a JWK Set endpoint is to hard-code an RSA public key.\nThe public key can be provided via <<oauth2resourceserver-jwt-decoder-public-key-boot,Spring Boot>> or by <<oauth2resourceserver-jwt-decoder-public-key-builder,Using a Builder>>.\n\n[[oauth2resourceserver-jwt-decoder-public-key-boot]]\n=== Via Spring Boot\n\nSpecifying a key via Spring Boot is quite simple.\nThe key's location can be specified like so:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        jwt:\n          public-key-location: classpath:my-key.pub\n----\n\nOr, to allow for a more sophisticated lookup, you can post-process the `RsaKeyConversionServicePostProcessor`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nBeanFactoryPostProcessor conversionServiceCustomizer() {\n    return beanFactory ->\n        beanFactory.getBean(RsaKeyConversionServicePostProcessor.class)\n                .setResourceLoader(new CustomResourceLoader());\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun conversionServiceCustomizer(): BeanFactoryPostProcessor {\n    return BeanFactoryPostProcessor { beanFactory ->\n        beanFactory.getBean<RsaKeyConversionServicePostProcessor>()\n                .setResourceLoader(CustomResourceLoader())\n    }\n}\n----\n======\n\nSpecify your key's location:\n\n[source,yaml]\n----\nkey.location: hfds://my-key.pub\n----\n\nAnd then autowire the value:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Value(\"${key.location}\")\nRSAPublicKey key;\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Value(\"\\${key.location}\")\nval key: RSAPublicKey? = null\n----\n======\n\n[[oauth2resourceserver-jwt-decoder-public-key-builder]]\n=== Using a Builder\n\nTo wire an `RSAPublicKey` directly, you can simply use the appropriate `NimbusJwtDecoder` builder, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoder jwtDecoder() {\n    return NimbusJwtDecoder.withPublicKey(this.key).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    return NimbusJwtDecoder.withPublicKey(this.key).build()\n}\n----\n======\n\n[[oauth2resourceserver-jwt-decoder-secret-key]]\n== Trusting a Single Symmetric Key\n\nUsing a single symmetric key is also simple.\nYou can simply load in your `SecretKey` and use the appropriate `NimbusJwtDecoder` builder, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoder jwtDecoder() {\n    return NimbusJwtDecoder.withSecretKey(this.key).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    return NimbusJwtDecoder.withSecretKey(key).build()\n}\n----\n======\n\n[[oauth2resourceserver-jwt-authorization]]\n== Configuring Authorization\n\nA JWT that is issued from an OAuth 2.0 Authorization Server will typically either have a `scope` or `scp` attribute, indicating the scopes (or authorities) it's been granted, for example:\n\n`{ ..., \"scope\" : \"messages contacts\"}`\n\nWhen this is the case, Resource Server will attempt to coerce these scopes into a list of granted authorities, prefixing each scope with the string \"SCOPE_\".\n\nThis means that to protect an endpoint or method with a scope derived from a JWT, the corresponding expressions should include this prefix:\n\n.Authorization Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;\n\n@Configuration\n@EnableWebSecurity\npublic class DirectlyConfiguredJwkSetUri {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .requestMatchers(\"/contacts/**\").access(hasScope(\"contacts\"))\n                .requestMatchers(\"/messages/**\").access(hasScope(\"messages\"))\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .jwt(Customizer.withDefaults())\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;\n\n@Configuration\n@EnableWebSecurity\nclass DirectlyConfiguredJwkSetUri {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(\"/contacts/**\", hasScope(\"contacts\"))\n                authorize(\"/messages/**\", hasScope(\"messages\"))\n                authorize(anyRequest, authenticated)\n            }\n            oauth2ResourceServer {\n                jwt { }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-uri pattern=\"/contacts/**\" access=\"hasAuthority('SCOPE_contacts')\"/>\n    <intercept-uri pattern=\"/messages/**\" access=\"hasAuthority('SCOPE_messages')\"/>\n    <oauth2-resource-server>\n        <jwt jwk-set-uri=\"https://idp.example.org/.well-known/jwks.json\"/>\n    </oauth2-resource-server>\n</http>\n----\n======\n\nOr similarly with method security:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"hasAuthority('SCOPE_messages')\")\npublic List<Message> getMessages(...) {}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"hasAuthority('SCOPE_messages')\")\nfun getMessages(): List<Message> { }\n----\n======\n\n[[method-security-has-scope]]\n=== Using `hasScope` in Method Security\n\nBecause method security expressions can evaluation `AuthorizationManager` instances, you can also use the `hasScope` API by publishing a `DefaultOAuth2AuthorizationManagerFactory` `@Bean`:\n\ninclude-code::./MethodSecurityHasScopeConfiguration[tag=declare-factory,indent=0]\n\nand then doing:\n\ninclude-code::./MessageService[tag=protected-method,indent=0]\n\nIf you are using xref:servlet/authentication/mfa.adoc[Spring Security's MFA feature], then you can supply its `AuthorizationManagerFactory` instance to ensure that your authentication factors are automatically checked as well by including it in your `DefaultOAuth2AuthorizationManagerFactory` constructor as follows:\n\ninclude-code::./MethodSecurityHasScopeMfaConfiguration[tag=declare-factory,indent=0]\n\n[[oauth2resourceserver-jwt-authorization-extraction]]\n=== Extracting Authorities Manually\n\nHowever, there are a number of circumstances where this default is insufficient.\nFor example, some authorization servers don't use the `scope` attribute, but instead have their own custom attribute.\nOr, at other times, the resource server may need to adapt the attribute or a composition of attributes into internalized authorities.\n\nTo this end, Spring Security ships with `JwtAuthenticationConverter`, which is responsible for <<oauth2resourceserver-jwt-architecture-jwtauthenticationconverter,converting a `Jwt` into an `Authentication`>>.\nBy default, Spring Security will wire the `JwtAuthenticationProvider` with a default instance of `JwtAuthenticationConverter`.\n\nAs part of configuring a `JwtAuthenticationConverter`, you can supply a subsidiary converter to go from `Jwt` to a `Collection` of granted authorities.\n\n[[jwt-granted-authorities-custom-claim-name]]\n==== Using a Custom Claim Name\n\nLet's say that your authorization server communicates authorities in a custom claim called `authorities`.\n\nIn that case, you can configure the claim that <<oauth2resourceserver-jwt-architecture-jwtauthenticationconverter,`JwtAuthenticationConverter`>> should inspect, like so:\n\n.Authorities Claim Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtAuthenticationConverter jwtAuthenticationConverter() {\n    JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n    grantedAuthoritiesConverter.setAuthoritiesClaimName(\"authorities\");\n\n    JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();\n    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);\n    return jwtAuthenticationConverter;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtAuthenticationConverter(): JwtAuthenticationConverter {\n    val grantedAuthoritiesConverter = JwtGrantedAuthoritiesConverter()\n    grantedAuthoritiesConverter.setAuthoritiesClaimName(\"authorities\")\n\n    val jwtAuthenticationConverter = JwtAuthenticationConverter()\n    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter)\n    return jwtAuthenticationConverter\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-uri pattern=\"/contacts/**\" access=\"hasAuthority('SCOPE_contacts')\"/>\n    <intercept-uri pattern=\"/messages/**\" access=\"hasAuthority('SCOPE_messages')\"/>\n    <oauth2-resource-server>\n        <jwt jwk-set-uri=\"https://idp.example.org/.well-known/jwks.json\"\n                jwt-authentication-converter-ref=\"jwtAuthenticationConverter\"/>\n    </oauth2-resource-server>\n</http>\n\n<bean id=\"jwtAuthenticationConverter\"\n        class=\"org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter\">\n    <property name=\"jwtGrantedAuthoritiesConverter\" ref=\"jwtGrantedAuthoritiesConverter\"/>\n</bean>\n\n<bean id=\"jwtGrantedAuthoritiesConverter\"\n        class=\"org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter\">\n    <property name=\"authoritiesClaimName\" value=\"authorities\"/>\n</bean>\n----\n======\n\n[[jwt-granted-authorities-custom-scope-prefix]]\n==== Using a Custom Scope Prefix\n\nYou can also configure the authority prefix to be different as well.\nInstead of prefixing each authority with `SCOPE_`, you can change it to `ROLE_` like so:\n\n.Authorities Prefix Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtAuthenticationConverter jwtAuthenticationConverter() {\n    JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n    grantedAuthoritiesConverter.setAuthorityPrefix(\"ROLE_\");\n\n    JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();\n    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);\n    return jwtAuthenticationConverter;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtAuthenticationConverter(): JwtAuthenticationConverter {\n    val grantedAuthoritiesConverter = JwtGrantedAuthoritiesConverter()\n    grantedAuthoritiesConverter.setAuthorityPrefix(\"ROLE_\")\n\n    val jwtAuthenticationConverter = JwtAuthenticationConverter()\n    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter)\n    return jwtAuthenticationConverter\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-uri pattern=\"/contacts/**\" access=\"hasAuthority('SCOPE_contacts')\"/>\n    <intercept-uri pattern=\"/messages/**\" access=\"hasAuthority('SCOPE_messages')\"/>\n    <oauth2-resource-server>\n        <jwt jwk-set-uri=\"https://idp.example.org/.well-known/jwks.json\"\n                jwt-authentication-converter-ref=\"jwtAuthenticationConverter\"/>\n    </oauth2-resource-server>\n</http>\n\n<bean id=\"jwtAuthenticationConverter\"\n        class=\"org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter\">\n    <property name=\"jwtGrantedAuthoritiesConverter\" ref=\"jwtGrantedAuthoritiesConverter\"/>\n</bean>\n\n<bean id=\"jwtGrantedAuthoritiesConverter\"\n        class=\"org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter\">\n    <property name=\"authorityPrefix\" value=\"ROLE_\"/>\n</bean>\n----\n======\n\nOr, you can remove the prefix altogether by calling `JwtGrantedAuthoritiesConverter#setAuthorityPrefix(\"\")`.\n\nFor more flexibility, the DSL supports entirely replacing the converter with any class that implements `Converter<Jwt, AbstractAuthenticationToken>`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nstatic class CustomAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {\n    public AbstractAuthenticationToken convert(Jwt jwt) {\n        return new CustomAuthenticationToken(jwt);\n    }\n}\n\n// ...\n\n@Configuration\n@EnableWebSecurity\npublic class CustomAuthenticationConverterConfig {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .jwt((jwt) -> jwt\n                    .jwtAuthenticationConverter(new CustomAuthenticationConverter())\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ninternal class CustomAuthenticationConverter : Converter<Jwt, AbstractAuthenticationToken> {\n    override fun convert(jwt: Jwt): AbstractAuthenticationToken {\n        return CustomAuthenticationToken(jwt)\n    }\n}\n\n// ...\n\n@Configuration\n@EnableWebSecurity\nclass CustomAuthenticationConverterConfig {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n       http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n           oauth2ResourceServer {\n               jwt {\n                   jwtAuthenticationConverter = CustomAuthenticationConverter()\n               }\n           }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[jwt-granted-authorities-spel-expression]]\n==== Using a SpEL Expression\n\nIn circumstances where the location of scopes is nested or complex in some other way, you can use `ExpressionJwtGrantedAuthoritiesConverter` with a SpEL expression to extract the scopes.\n\nFor example, if your JWT has a claim called `nested` and, inside of that, it has a claim called `scopes`, you can do:\n\ninclude-code::./ExpressionJwtGrantedAuthoritiesConverterTests[tag=spel-expression,indent=0]\n\nThe SpEL expression result should be a `Collection`.\n\n[[oauth2resourceserver-jwt-validation]]\n== Configuring Validation\n\nUsing <<oauth2resourceserver-jwt-minimalconfiguration,minimal Spring Boot configuration>>, indicating the authorization server's issuer uri, Resource Server will default to verifying the `iss` claim as well as the `exp` and `nbf` timestamp claims.\n\nIn circumstances where validation needs to be customized, Resource Server ships with two standard validators and also accepts custom `OAuth2TokenValidator` instances.\n\n[[oauth2resourceserver-jwt-validation-clockskew]]\n=== Customizing Timestamp Validation\n\nJWT's typically have a window of validity, with the start of the window indicated in the `nbf` claim and the end indicated in the `exp` claim.\n\nHowever, every server can experience clock drift, which can cause tokens to appear expired to one server, but not to another.\nThis can cause some implementation heartburn as the number of collaborating servers increases in a distributed system.\n\nResource Server uses `JwtTimestampValidator` to verify a token's validity window, and it can be configured with a `clockSkew` to alleviate the above problem:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n     NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)\n             JwtDecoders.fromIssuerLocation(issuerUri);\n\n     OAuth2TokenValidator<Jwt> withClockSkew = new DelegatingOAuth2TokenValidator<>(\n            new JwtTimestampValidator(Duration.ofSeconds(60)),\n            new JwtIssuerValidator(issuerUri));\n\n     jwtDecoder.setJwtValidator(withClockSkew);\n\n     return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder: NimbusJwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri) as NimbusJwtDecoder\n\n    val withClockSkew: OAuth2TokenValidator<Jwt> = DelegatingOAuth2TokenValidator(\n            JwtTimestampValidator(Duration.ofSeconds(60)),\n            JwtIssuerValidator(issuerUri))\n\n    jwtDecoder.setJwtValidator(withClockSkew)\n\n    return jwtDecoder\n}\n----\n======\n\n[NOTE]\nBy default, Resource Server configures a clock skew of 60 seconds.\n\n[[oauth2resourceserver-jwt-validation-rfc9068]]\n=== Configuring RFC 9068 Validation\n\nIf you need to require tokens that meet https://datatracker.ietf.org/doc/rfc9068/[RFC 9068], you can configure validation in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n    NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuerUri)\n            .validateTypes(false).build();\n    jwtDecoder.setJwtValidator(JwtValidators.createAtJwtValidator()\n            .audience(\"https://audience.example.org\")\n            .clientId(\"client-identifier\")\n            .issuer(\"https://issuer.example.org\").build());\n     return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuerUri)\n            .validateTypes(false).build()\n    jwtDecoder.setJwtValidator(JwtValidators.createAtJwtValidator()\n            .audience(\"https://audience.example.org\")\n            .clientId(\"client-identifier\")\n            .issuer(\"https://issuer.example.org\").build())\n    return jwtDecoder\n}\n----\n======\n\n[[oauth2resourceserver-jwt-validation-custom]]\n=== Configuring a Custom Validator\n\nAdding a check for <<_supplying_audiences, the `aud` claim>> is simple with the `OAuth2TokenValidator` API:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOAuth2TokenValidator<Jwt> audienceValidator() {\n    return new JwtClaimValidator<List<String>>(AUD, aud -> aud.contains(\"messaging\"));\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nfun audienceValidator(): OAuth2TokenValidator<Jwt?> {\n    return JwtClaimValidator<List<String>>(AUD) { aud -> aud.contains(\"messaging\") }\n}\n----\n======\n\nOr, for more control you can implement your own `OAuth2TokenValidator`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nstatic class AudienceValidator implements OAuth2TokenValidator<Jwt> {\n    OAuth2Error error = new OAuth2Error(\"custom_code\", \"Custom error message\", null);\n\n    @Override\n    public OAuth2TokenValidatorResult validate(Jwt jwt) {\n        if (jwt.getAudience().contains(\"messaging\")) {\n            return OAuth2TokenValidatorResult.success();\n        } else {\n            return OAuth2TokenValidatorResult.failure(error);\n        }\n    }\n}\n\n// ...\n\nOAuth2TokenValidator<Jwt> audienceValidator() {\n    return new AudienceValidator();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ninternal class AudienceValidator : OAuth2TokenValidator<Jwt> {\n    var error: OAuth2Error = OAuth2Error(\"custom_code\", \"Custom error message\", null)\n\n    override fun validate(jwt: Jwt): OAuth2TokenValidatorResult {\n        return if (jwt.audience.contains(\"messaging\")) {\n            OAuth2TokenValidatorResult.success()\n        } else {\n            OAuth2TokenValidatorResult.failure(error)\n        }\n    }\n}\n\n// ...\n\nfun audienceValidator(): OAuth2TokenValidator<Jwt> {\n    return AudienceValidator()\n}\n----\n======\n\nThen, to add into a resource server, it's a matter of specifying the <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> instance:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n    NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)\n        JwtDecoders.fromIssuerLocation(issuerUri);\n\n    OAuth2TokenValidator<Jwt> audienceValidator = audienceValidator();\n    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);\n    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);\n\n    jwtDecoder.setJwtValidator(withAudience);\n\n    return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder: NimbusJwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri) as NimbusJwtDecoder\n\n    val audienceValidator = audienceValidator()\n    val withIssuer: OAuth2TokenValidator<Jwt> = JwtValidators.createDefaultWithIssuer(issuerUri)\n    val withAudience: OAuth2TokenValidator<Jwt> = DelegatingOAuth2TokenValidator(withIssuer, audienceValidator)\n\n    jwtDecoder.setJwtValidator(withAudience)\n\n    return jwtDecoder\n}\n----\n======\n\n[TIP]\nAs stated earlier, you can instead <<_supplying_audiences, configure `aud` validation in Boot>>.\n\n[[oauth2resourceserver-jwt-claimsetmapping]]\n== Configuring Claim Set Mapping\n\nSpring Security uses the https://bitbucket.org/connect2id/nimbus-jose-jwt/wiki/Home[Nimbus] library for parsing JWTs and validating their signatures.\nConsequently, Spring Security is subject to Nimbus's interpretation of each field value and how to coerce each into a Java type.\n\nFor example, because Nimbus remains Java 7 compatible, it doesn't use `Instant` to represent timestamp fields.\n\nAnd it's entirely possible to use a different library or for JWT processing, which may make its own coercion decisions that need adjustment.\n\nOr, quite simply, a resource server may want to add or remove claims from a JWT for domain-specific reasons.\n\nFor these purposes, Resource Server supports mapping the JWT claim set with `MappedJwtClaimSetConverter`.\n\n[[oauth2resourceserver-jwt-claimsetmapping-singleclaim]]\n=== Customizing the Conversion of a Single Claim\n\nBy default, `MappedJwtClaimSetConverter` will attempt to coerce claims into the following types:\n\n|============\n| Claim | Java Type\n| `aud` | `Collection<String>`\n| `exp` | `Instant`\n| `iat` | `Instant`\n| `iss` | `String`\n| `jti` | `String`\n| `nbf` | `Instant`\n| `sub` | `String`\n|============\n\nAn individual claim's conversion strategy can be configured using `MappedJwtClaimSetConverter.withDefaults`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n    NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();\n\n    MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter\n            .withDefaults(Collections.singletonMap(\"sub\", this::lookupUserIdBySub));\n    jwtDecoder.setClaimSetConverter(converter);\n\n    return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build()\n\n    val converter = MappedJwtClaimSetConverter\n            .withDefaults(mapOf(\"sub\" to this::lookupUserIdBySub))\n    jwtDecoder.setClaimSetConverter(converter)\n\n    return jwtDecoder\n}\n----\n======\nThis will keep all the defaults, except it will override the default claim converter for `sub`.\n\n[[oauth2resourceserver-jwt-claimsetmapping-add]]\n=== Adding a Claim\n\n`MappedJwtClaimSetConverter` can also be used to add a custom claim, for example, to adapt to an existing system:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMappedJwtClaimSetConverter.withDefaults(Collections.singletonMap(\"custom\", custom -> \"value\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nMappedJwtClaimSetConverter.withDefaults(mapOf(\"custom\" to Converter<Any, String> { \"value\" }))\n----\n======\n\n[[oauth2resourceserver-jwt-claimsetmapping-remove]]\n=== Removing a Claim\n\nAnd removing a claim is also simple, using the same API:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMappedJwtClaimSetConverter.withDefaults(Collections.singletonMap(\"legacyclaim\", legacy -> null));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nMappedJwtClaimSetConverter.withDefaults(mapOf(\"legacyclaim\" to Converter<Any, Any> { null }))\n----\n======\n\n[[oauth2resourceserver-jwt-claimsetmapping-rename]]\n=== Renaming a Claim\n\nIn more sophisticated scenarios, like consulting multiple claims at once or renaming a claim, Resource Server accepts any class that implements `Converter<Map<String, Object>, Map<String,Object>>`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class UsernameSubClaimAdapter implements Converter<Map<String, Object>, Map<String, Object>> {\n    private final MappedJwtClaimSetConverter delegate =\n            MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\n    public Map<String, Object> convert(Map<String, Object> claims) {\n        Map<String, Object> convertedClaims = this.delegate.convert(claims);\n\n        String username = (String) convertedClaims.get(\"user_name\");\n        convertedClaims.put(\"sub\", username);\n\n        return convertedClaims;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass UsernameSubClaimAdapter : Converter<Map<String, Any?>, Map<String, Any?>> {\n    private val delegate = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap())\n    override fun convert(claims: Map<String, Any?>): Map<String, Any?> {\n        val convertedClaims = delegate.convert(claims)\n        val username = convertedClaims[\"user_name\"] as String\n        convertedClaims[\"sub\"] = username\n        return convertedClaims\n    }\n}\n----\n======\n\nAnd then, the instance can be supplied like normal:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder() {\n    NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();\n    jwtDecoder.setClaimSetConverter(new UsernameSubClaimAdapter());\n    return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(): JwtDecoder {\n    val jwtDecoder: NimbusJwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build()\n    jwtDecoder.setClaimSetConverter(UsernameSubClaimAdapter())\n    return jwtDecoder\n}\n----\n======\n\n[[oauth2resourceserver-jwt-timeouts]]\n== Configuring Timeouts\n\nBy default, Resource Server uses connection and socket timeouts of 30 seconds each for coordinating with the authorization server.\n\nThis may be too short in some scenarios.\nFurther, it doesn't take into account more sophisticated patterns like back-off and discovery.\n\nTo adjust the way in which Resource Server connects to the authorization server, `NimbusJwtDecoder` accepts an instance of `RestOperations`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoder jwtDecoder(RestTemplateBuilder builder) {\n    RestOperations rest = builder\n            .setConnectTimeout(Duration.ofSeconds(60))\n            .setReadTimeout(Duration.ofSeconds(60))\n            .build();\n\n    NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(rest).build();\n    return jwtDecoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(builder: RestTemplateBuilder): JwtDecoder {\n    val rest: RestOperations = builder\n            .setConnectTimeout(Duration.ofSeconds(60))\n            .setReadTimeout(Duration.ofSeconds(60))\n            .build()\n    return NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(rest).build()\n}\n----\n======\n\nAlso by default, Resource Server caches in-memory the authorization server's JWK set for 5 minutes, which you may want to adjust.\nFurther, it doesn't take into account more sophisticated caching patterns like eviction or using a shared cache.\n\nTo adjust the way in which Resource Server caches the JWK set, `NimbusJwtDecoder` accepts an instance of `Cache`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic JwtDecoder jwtDecoder(CacheManager cacheManager) {\n    return NimbusJwtDecoder.withIssuerLocation(issuer)\n            .cache(cacheManager.getCache(\"jwks\"))\n            .build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(cacheManager: CacheManager): JwtDecoder {\n    return NimbusJwtDecoder.withIssuerLocation(issuer)\n            .cache(cacheManager.getCache(\"jwks\"))\n            .build()\n}\n----\n======\n\nWhen given a `Cache`, Resource Server will use the JWK Set Uri as the key and the JWK Set JSON as the value.\n\nNOTE: Spring isn't a cache provider, so you'll need to make sure to include the appropriate dependencies, like `spring-boot-starter-cache` and your favorite caching provider.\n\nNOTE: Whether it's socket or cache timeouts, you may instead want to work with Nimbus directly.\nTo do so, remember that `NimbusJwtDecoder` ships with a constructor that takes Nimbus's `JWTProcessor`.\n\n[[custom-principal]]\n== Customizing the Principal\n\nSometimes it can be helpful to look up a pre-existing user based on the JWTs subject or other identifier.\n\n[[custom-user-details-service]]\n=== Wiring a UserDetailsService\n\nFor example, if your application has a `UserDetailsService` and you want the corresponding user in the resulting `Authentication`, you can create a class to adapt your `UserDetails` into an `OAuth2AuthenticatedPrincipal`:\n\ninclude-code::./UserDetailsJwtPrincipalConverter[tag=custom-converter,indent=0]\n\nAnd then apply your principal converter to a `JwtAuthenticationConverter` `@Bean` like so:\n\ninclude-code::./UserDetailsJwtPrincipalConverterConfiguration[tag=configure-converter,indent=0]\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/resource-server/multitenancy.adoc",
    "content": "= OAuth 2.0 Resource Server Multi-tenancy\n\n[[oauth2reourceserver-opaqueandjwt]]\n== Supporting both JWT and Opaque Token\n\nIn some cases, you may have a need to access both kinds of tokens.\nFor example, you may support more than one tenant where one tenant issues JWTs and the other issues opaque tokens.\n\nIf this decision must be made at request-time, then you can use an `AuthenticationManagerResolver` to achieve it, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nAuthenticationManagerResolver<HttpServletRequest> tokenAuthenticationManagerResolver\n        (JwtDecoder jwtDecoder, OpaqueTokenIntrospector opaqueTokenIntrospector) {\n    AuthenticationManager jwt = new ProviderManager(new JwtAuthenticationProvider(jwtDecoder));\n    AuthenticationManager opaqueToken = new ProviderManager(\n            new OpaqueTokenAuthenticationProvider(opaqueTokenIntrospector));\n    return (request) -> useJwt(request) ? jwt : opaqueToken;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun tokenAuthenticationManagerResolver\n        (jwtDecoder: JwtDecoder, opaqueTokenIntrospector: OpaqueTokenIntrospector):\n        AuthenticationManagerResolver<HttpServletRequest> {\n    val jwt = ProviderManager(JwtAuthenticationProvider(jwtDecoder))\n    val opaqueToken = ProviderManager(OpaqueTokenAuthenticationProvider(opaqueTokenIntrospector));\n\n    return AuthenticationManagerResolver { request ->\n        if (useJwt(request)) {\n            jwt\n        } else {\n            opaqueToken\n        }\n    }\n}\n----\n======\n\nNOTE: The implementation of `useJwt(HttpServletRequest)` will likely depend on custom request material like the path.\n\nAnd then specify this `AuthenticationManagerResolver` in the DSL:\n\n.Authentication Manager Resolver\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .anyRequest().authenticated()\n    )\n    .oauth2ResourceServer((oauth2) -> oauth2\n        .authenticationManagerResolver(this.tokenAuthenticationManagerResolver)\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    authorizeHttpRequests {\n        authorize(anyRequest, authenticated)\n    }\n    oauth2ResourceServer {\n        authenticationManagerResolver = tokenAuthenticationManagerResolver()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <oauth2-resource-server authentication-manager-resolver-ref=\"tokenAuthenticationManagerResolver\"/>\n</http>\n----\n======\n\n[[oauth2resourceserver-multitenancy]]\n== Multi-tenancy\n\nA resource server is considered multi-tenant when there are multiple strategies for verifying a bearer token, keyed by some tenant identifier.\n\nFor example, your resource server may accept bearer tokens from two different authorization servers.\nOr, your authorization server may represent a multiplicity of issuers.\n\nIn each case, there are two things that need to be done and trade-offs associated with how you choose to do them:\n\n1. Resolve the tenant\n2. Propagate the tenant\n\n=== Resolving the Tenant By Claim\n\nOne way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, this can be done with the `JwtIssuerAuthenticationManagerResolver`, like so:\n\n.Multi-tenancy Tenant by JWT Claim\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n    .fromTrustedIssuers(\"https://idp.example.org/issuerOne\", \"https://idp.example.org/issuerTwo\");\n\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .anyRequest().authenticated()\n    )\n    .oauth2ResourceServer((oauth2) -> oauth2\n        .authenticationManagerResolver(authenticationManagerResolver)\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval customAuthenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n    .fromTrustedIssuers(\"https://idp.example.org/issuerOne\", \"https://idp.example.org/issuerTwo\")\nhttp {\n    authorizeHttpRequests {\n        authorize(anyRequest, authenticated)\n    }\n    oauth2ResourceServer {\n        authenticationManagerResolver = customAuthenticationManagerResolver\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <oauth2-resource-server authentication-manager-resolver-ref=\"authenticationManagerResolver\"/>\n</http>\n\n<bean id=\"authenticationManagerResolver\"\n        class=\"org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver\">\n    <constructor-arg>\n        <list>\n            <value>https://idp.example.org/issuerOne</value>\n            <value>https://idp.example.org/issuerTwo</value>\n        </list>\n    </constructor-arg>\n</bean>\n----\n======\n\nThis is nice because the issuer endpoints are loaded lazily.\nIn fact, the corresponding `JwtAuthenticationProvider` is instantiated only when the first request with the corresponding issuer is sent.\nThis allows for an application startup that is independent from those authorization servers being up and available.\n\n==== Dynamic Tenants\n\nOf course, you may not want to restart the application each time a new tenant is added.\nIn this case, you can configure the `JwtIssuerAuthenticationManagerResolver` with a repository of `AuthenticationManager` instances, which you can edit at runtime, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprivate void addManager(Map<String, AuthenticationManager> authenticationManagers, String issuer) {\n\tJwtAuthenticationProvider authenticationProvider = new JwtAuthenticationProvider\n\t        (JwtDecoders.fromIssuerLocation(issuer));\n\tauthenticationManagers.put(issuer, authenticationProvider::authenticate);\n}\n\n// ...\n\nJwtIssuerAuthenticationManagerResolver authenticationManagerResolver =\n        new JwtIssuerAuthenticationManagerResolver(authenticationManagers::get);\n\nhttp\n    .authorizeHttpRequests((authorize) -> authorize\n        .anyRequest().authenticated()\n    )\n    .oauth2ResourceServer((oauth2) -> oauth2\n        .authenticationManagerResolver(authenticationManagerResolver)\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nprivate fun addManager(authenticationManagers: MutableMap<String, AuthenticationManager>, issuer: String) {\n    val authenticationProvider = JwtAuthenticationProvider(JwtDecoders.fromIssuerLocation(issuer))\n    authenticationManagers[issuer] = AuthenticationManager {\n        authentication: Authentication? -> authenticationProvider.authenticate(authentication)\n    }\n}\n\n// ...\n\nval customAuthenticationManagerResolver: JwtIssuerAuthenticationManagerResolver =\n    JwtIssuerAuthenticationManagerResolver(authenticationManagers::get)\nhttp {\n    authorizeHttpRequests {\n        authorize(anyRequest, authenticated)\n    }\n    oauth2ResourceServer {\n        authenticationManagerResolver = customAuthenticationManagerResolver\n    }\n}\n----\n======\n\nIn this case, you construct `JwtIssuerAuthenticationManagerResolver` with a strategy for obtaining the `AuthenticationManager` given the issuer.\nThis approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime.\n\nNOTE: It would be unsafe to simply take any issuer and construct an `AuthenticationManager` from it.\nThe issuer should be one that the code can verify from a trusted source like a list of allowed issuers.\n\n==== Parsing the Claim Only Once\n\nYou may have observed that this strategy, while simple, comes with the trade-off that the JWT is parsed once by the `AuthenticationManagerResolver` and then again by the xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-architecture-jwtdecoder[`JwtDecoder`] later on in the request.\n\nThis extra parsing can be alleviated by configuring the xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-architecture-jwtdecoder[`JwtDecoder`] directly with a `JWTClaimsSetAwareJWSKeySelector` from Nimbus:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class TenantJWSKeySelector\n    implements JWTClaimsSetAwareJWSKeySelector<SecurityContext> {\n\n\tprivate final TenantRepository tenants; <1>\n\tprivate final Map<String, JWSKeySelector<SecurityContext>> selectors = new ConcurrentHashMap<>(); <2>\n\n\tpublic TenantJWSKeySelector(TenantRepository tenants) {\n\t\tthis.tenants = tenants;\n\t}\n\n\t@Override\n\tpublic List<? extends Key> selectKeys(JWSHeader jwsHeader, JWTClaimsSet jwtClaimsSet, SecurityContext securityContext)\n\t\t\tthrows KeySourceException {\n\t\treturn this.selectors.computeIfAbsent(toTenant(jwtClaimsSet), this::fromTenant)\n\t\t\t\t.selectJWSKeys(jwsHeader, securityContext);\n\t}\n\n\tprivate String toTenant(JWTClaimsSet claimSet) {\n\t\treturn (String) claimSet.getClaim(\"iss\");\n\t}\n\n\tprivate JWSKeySelector<SecurityContext> fromTenant(String tenant) {\n\t\treturn Optional.ofNullable(this.tenants.findById(tenant)) <3>\n\t\t        .map((t) -> t.getAttrbute(\"jwks_uri\"))\n\t\t\t\t.map(this::fromUri)\n\t\t\t\t.orElseThrow(() -> new IllegalArgumentException(\"unknown tenant\"));\n\t}\n\n\tprivate JWSKeySelector<SecurityContext> fromUri(String uri) {\n\t\ttry {\n\t\t\treturn JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(new URL(uri)); <4>\n\t\t} catch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass TenantJWSKeySelector(tenants: TenantRepository) : JWTClaimsSetAwareJWSKeySelector<SecurityContext> {\n    private val tenants: TenantRepository <1>\n    private val selectors: MutableMap<String, JWSKeySelector<SecurityContext>> = ConcurrentHashMap() <2>\n\n    init {\n        this.tenants = tenants\n    }\n\n    fun selectKeys(jwsHeader: JWSHeader?, jwtClaimsSet: JWTClaimsSet, securityContext: SecurityContext): List<Key?> {\n        return selectors.computeIfAbsent(toTenant(jwtClaimsSet)) { tenant: String -> fromTenant(tenant) }\n                .selectJWSKeys(jwsHeader, securityContext)\n    }\n\n    private fun toTenant(claimSet: JWTClaimsSet): String {\n        return claimSet.getClaim(\"iss\") as String\n    }\n\n    private fun fromTenant(tenant: String): JWSKeySelector<SecurityContext> {\n        return Optional.ofNullable(this.tenants.findById(tenant)) <3>\n                .map { t -> t.getAttrbute(\"jwks_uri\") }\n                .map { uri: String -> fromUri(uri) }\n                .orElseThrow { IllegalArgumentException(\"unknown tenant\") }\n    }\n\n    private fun fromUri(uri: String): JWSKeySelector<SecurityContext?> {\n        return try {\n            JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(URL(uri)) <4>\n        } catch (ex: Exception) {\n            throw IllegalArgumentException(ex)\n        }\n    }\n}\n----\n======\n<1> A hypothetical source for tenant information\n<2> A cache for ``JWSKeySelector``s, keyed by tenant identifier\n<3> Looking up the tenant is more secure than simply calculating the JWK Set endpoint on the fly - the lookup acts as a list of allowed tenants\n<4> Create a `JWSKeySelector` via the types of keys that come back from the JWK Set endpoint - the lazy lookup here means that you don't need to configure all tenants at startup\n\nThe above key selector is a composition of many key selectors.\nIt chooses which key selector to use based on the `iss` claim in the JWT.\n\nNOTE: To use this approach, make sure that the authorization server is configured to include the claim set as part of the token's signature.\nWithout this, you have no guarantee that the issuer hasn't been altered by a bad actor.\n\nNext, we can construct a `JWTProcessor`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJWTProcessor jwtProcessor(JWTClaimsSetAwareJWSKeySelector keySelector) {\n\tConfigurableJWTProcessor<SecurityContext> jwtProcessor =\n            new DefaultJWTProcessor();\n\tjwtProcessor.setJWTClaimSetJWSKeySelector(keySelector);\n\treturn jwtProcessor;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtProcessor(keySelector: JWTClaimsSetAwareJWSKeySelector<SecurityContext>): JWTProcessor<SecurityContext> {\n    val jwtProcessor = DefaultJWTProcessor<SecurityContext>()\n    jwtProcessor.jwtClaimsSetAwareJWSKeySelector = keySelector\n    return jwtProcessor\n}\n----\n======\n\nAs you are already seeing, the trade-off for moving tenant-awareness down to this level is more configuration.\nWe have just a bit more.\n\nNext, we still want to make sure you are validating the issuer.\nBut, since the issuer may be different per JWT, then you'll need a tenant-aware validator, too:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class TenantJwtIssuerValidator implements OAuth2TokenValidator<Jwt> {\n    private final TenantRepository tenants;\n\n    private final OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, \"The iss claim is not valid\",\n            \"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\n    public TenantJwtIssuerValidator(TenantRepository tenants) {\n        this.tenants = tenants;\n    }\n\n    @Override\n    public OAuth2TokenValidatorResult validate(Jwt token) {\n        if(this.tenants.findById(token.getIssuer()) != null) {\n            return OAuth2TokenValidatorResult.success();\n        }\n        return OAuth2TokenValidatorResult.failure(this.error);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass TenantJwtIssuerValidator(private val tenants: TenantRepository) : OAuth2TokenValidator<Jwt> {\n    private val error: OAuth2Error = OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, \"The iss claim is not valid\",\n            \"https://tools.ietf.org/html/rfc6750#section-3.1\")\n\n    override fun validate(token: Jwt): OAuth2TokenValidatorResult {\n        return if (tenants.findById(token.issuer) != null)\n            OAuth2TokenValidatorResult.success() else OAuth2TokenValidatorResult.failure(error)\n    }\n}\n----\n======\nNow that we have a tenant-aware processor and a tenant-aware validator, we can proceed with creating our xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-architecture-jwtdecoder[`JwtDecoder`]:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nJwtDecoder jwtDecoder(JWTProcessor jwtProcessor, OAuth2TokenValidator<Jwt> jwtValidator) {\n\tNimbusJwtDecoder decoder = new NimbusJwtDecoder(jwtProcessor);\n\tOAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>\n\t\t\t(JwtValidators.createDefault(), jwtValidator);\n\tdecoder.setJwtValidator(validator);\n\treturn decoder;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun jwtDecoder(jwtProcessor: JWTProcessor<SecurityContext>?, jwtValidator: OAuth2TokenValidator<Jwt>?): JwtDecoder {\n    val decoder = NimbusJwtDecoder(jwtProcessor)\n    val validator: OAuth2TokenValidator<Jwt> = DelegatingOAuth2TokenValidator(JwtValidators.createDefault(), jwtValidator)\n    decoder.setJwtValidator(validator)\n    return decoder\n}\n----\n======\n\nWe've finished talking about resolving the tenant.\n\nIf you've chosen to resolve the tenant by something other than a JWT claim, then you'll need to make sure you address your downstream resource servers in the same way.\nFor example, if you are resolving it by subdomain, you may need to address the downstream resource server using the same subdomain.\n\nHowever, if you resolve it by a claim in the bearer token, read on to learn about xref:servlet/oauth2/resource-server/bearer-tokens.adoc#oauth2resourceserver-bearertoken-resolver[Spring Security's support for bearer token propagation].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc",
    "content": "= OAuth 2.0 Resource Server Opaque Token\n:figures: servlet/oauth2\n\n[[oauth2resourceserver-opaque-minimaldependencies]]\n== Minimal Dependencies for Introspection\nAs described in xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-minimaldependencies[Minimal Dependencies for JWT] most of Resource Server support is collected in `spring-security-oauth2-resource-server`.\nHowever unless a custom <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>> is provided, the Resource Server will fallback to `SpringOpaqueTokenIntrospector`.\nThis means that only `spring-security-oauth2-resource-server` is necessary in order to have a working minimal Resource Server that supports opaque Bearer Tokens.\n\n[[oauth2resourceserver-opaque-minimalconfiguration]]\n== Minimal Configuration for Introspection\n\nTypically, an opaque token can be verified via an https://tools.ietf.org/html/rfc7662[OAuth 2.0 Introspection Endpoint], hosted by the authorization server.\nThis can be handy when revocation is a requirement.\n\nWhen using https://spring.io/projects/spring-boot[Spring Boot], configuring an application as a resource server that uses introspection consists of two basic steps.\nFirst, include the needed dependencies and second, indicate the introspection endpoint details.\n\n[[oauth2resourceserver-opaque-introspectionuri]]\n=== Specifying the Authorization Server\n\nTo specify where the introspection endpoint is, simply do:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        opaquetoken:\n          introspection-uri: https://idp.example.com/introspect\n          client-id: client\n          client-secret: secret\n----\n\nWhere `https://idp.example.com/introspect` is the introspection endpoint hosted by your authorization server and `client-id` and `client-secret` are the credentials needed to hit that endpoint.\n\nResource Server will use these properties to further self-configure and subsequently validate incoming JWTs.\n\n[NOTE]\nWhen using introspection, the authorization server's word is the law.\nIf the authorization server responses that the token is valid, then it is.\n\nAnd that's it!\n\n=== Startup Expectations\n\nWhen this property and these dependencies are used, Resource Server will automatically configure itself to validate Opaque Bearer Tokens.\n\nThis startup process is quite a bit simpler than for JWTs since no endpoints need to be discovered and no additional validation rules get added.\n\n=== Runtime Expectations\n\nOnce the application is started up, Resource Server will attempt to process any request containing an `Authorization: Bearer` header:\n\n[source,http]\n----\nGET / HTTP/1.1\nAuthorization: Bearer some-token-value # Resource Server will process this\n----\n\nSo long as this scheme is indicated, Resource Server will attempt to process the request according to the Bearer Token specification.\n\nGiven an Opaque Token, Resource Server will\n\n1. Query the provided introspection endpoint using the provided credentials and the token\n2. Inspect the response for an `{ 'active' : true }` attribute\n3. Map each scope to an authority with the prefix `SCOPE_`\n\nThe resulting `Authentication#getPrincipal`, by default, is a Spring Security javadoc:org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal[] object, and `Authentication#getName` maps to the token's `sub` property, if one is present.\n\nFrom here, you may want to jump to:\n\n* <<oauth2resourceserver-opaque-architecture>>\n* <<oauth2resourceserver-opaque-attributes,Looking Up Attributes Post-Authentication>>\n* <<oauth2resourceserver-opaque-authorization-extraction,Extracting Authorities Manually>>\n* <<oauth2resourceserver-opaque-jwt-introspector,Using Introspection with JWTs>>\n\n[[oauth2resourceserver-opaque-architecture]]\n== How Opaque Token Authentication Works\n\nNext, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7662[opaque token] Authentication in servlet-based applications, like the one we just saw.\n\njavadoc:org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider[] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>> to authenticate an opaque token.\n\nLet's take a look at how `OpaqueTokenAuthenticationProvider` works within Spring Security.\nThe figure explains details of how the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from xref:servlet/oauth2/resource-server/index.adoc#oauth2resourceserver-authentication-bearertokenauthenticationfilter[Reading the Bearer Token] works.\n\n.`OpaqueTokenAuthenticationProvider` Usage\n[.invert-dark]\nimage::{figures}/opaquetokenauthenticationprovider.png[]\n\nimage:{icondir}/number_1.png[] The authentication `Filter` from xref:servlet/oauth2/resource-server/index.adoc#oauth2resourceserver-authentication-bearertokenauthenticationfilter[Reading the Bearer Token] passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`ProviderManager`].\n\nimage:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `OpaqueTokenAuthenticationProvider`.\n\n[[oauth2resourceserver-opaque-architecture-introspector]]\nimage:{icondir}/number_3.png[] `OpaqueTokenAuthenticationProvider` introspects the opaque token and adds granted authorities using an <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>>.\nWhen authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>> and a set of authorities that contains at least `FACTOR_BEARER`.\nUltimately, the returned `BearerTokenAuthentication` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.\n\n[[oauth2resourceserver-opaque-attributes]]\n== Looking Up Attributes Post-Authentication\n\nOnce a token is authenticated, an instance of `BearerTokenAuthentication` is set in the `SecurityContext`.\n\nThis means that it's available in `@Controller` methods when using `@EnableWebMvc` in your configuration:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/foo\")\npublic String foo(BearerTokenAuthentication authentication) {\n    return authentication.getTokenAttributes().get(\"sub\") + \" is the subject\";\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/foo\")\nfun foo(authentication: BearerTokenAuthentication): String {\n    return authentication.tokenAttributes[\"sub\"].toString() + \" is the subject\"\n}\n----\n======\n\nSince `BearerTokenAuthentication` holds an `OAuth2AuthenticatedPrincipal`, that also means that it's available to controller methods, too:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/foo\")\npublic String foo(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal) {\n    return principal.getAttribute(\"sub\") + \" is the subject\";\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/foo\")\nfun foo(@AuthenticationPrincipal principal: OAuth2AuthenticatedPrincipal): String {\n    return principal.getAttribute<Any>(\"sub\").toString() + \" is the subject\"\n}\n----\n======\n\n=== Looking Up Attributes Via SpEL\n\nOf course, this also means that attributes can be accessed via SpEL.\n\nFor example, if using `@EnableGlobalMethodSecurity` so that you can use `@PreAuthorize` annotations, you can do:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"principal?.attributes['sub'] == 'foo'\")\npublic String forFoosEyesOnly() {\n    return \"foo\";\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"principal?.attributes['sub'] == 'foo'\")\nfun forFoosEyesOnly(): String {\n    return \"foo\"\n}\n----\n======\n\n[[oauth2resourceserver-opaque-sansboot]]\n== Overriding or Replacing Boot Auto Configuration\n\nThere are two ``@Bean``s that Spring Boot generates on Resource Server's behalf.\n\nThe first is a `SecurityFilterChain` that configures the app as a resource server.\nWhen use Opaque Token, this `SecurityFilterChain` looks like:\n\n.Default Opaque Token Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n    http\n        .authorizeHttpRequests((authorize) -> authorize\n            .anyRequest().authenticated()\n        )\n        .oauth2ResourceServer((oauth2) -> oauth2\n            .opaqueToken(Customizer.withDefaults())\n        );\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        authorizeHttpRequests {\n            authorize(anyRequest, authenticated)\n        }\n        oauth2ResourceServer {\n            opaqueToken { }\n        }\n    }\n    return http.build()\n}\n----\n======\n\nIf the application doesn't expose a `SecurityFilterChain` bean, then Spring Boot will expose the above default one.\n\nReplacing this is as simple as exposing the bean within the application:\n\n.Custom Opaque Token Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;\n\n@Configuration\n@EnableWebSecurity\npublic class MyCustomSecurityConfiguration {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .requestMatchers(\"/messages/**\").access(hasScope(\"message:read\"))\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .opaqueToken((opaqueToken) -> opaqueToken\n                    .introspector(myIntrospector())\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;\n\n@Configuration\n@EnableWebSecurity\nclass MyCustomSecurityConfiguration {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(\"/messages/**\", hasScope(\"SCOPE_message:read\"))\n                authorize(anyRequest, authenticated)\n            }\n            oauth2ResourceServer {\n                opaqueToken {\n                    introspector = myIntrospector()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nThe above requires the scope of `message:read` for any URL that starts with `/messages/`.\n\nMethods on the `oauth2ResourceServer` DSL will also override or replace auto configuration.\n\n[[oauth2resourceserver-opaque-introspector]]\nFor example, the second `@Bean` Spring Boot creates is an `OpaqueTokenIntrospector`, <<oauth2resourceserver-opaque-architecture-introspector,which decodes `String` tokens into validated instances of `OAuth2AuthenticatedPrincipal`>>:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OpaqueTokenIntrospector introspector() {\n    return SpringOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n            .clientId(clientId).clientSecret(clientSecret).build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): OpaqueTokenIntrospector {\n    return SpringOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n            .clientId(clientId).clientSecret(clientSecret).build()\n}\n----\n======\n\nIf the application doesn't expose an <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> bean, then Spring Boot will expose the above default one.\n\nAnd its configuration can be overridden using `introspectionUri()` and `introspectionClientCredentials()` or replaced using `introspector()`.\n\nIf the application doesn't expose an `OpaqueTokenAuthenticationConverter` bean, then spring-security will build `BearerTokenAuthentication`.\n\nOr, if you're not using Spring Boot at all, then all of these components - the filter chain, an <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> and an `OpaqueTokenAuthenticationConverter` can be specified in XML.\n\nThe filter chain is specified like so:\n\n.Default Opaque Token Configuration\n[tabs]\n======\nXml::\n+\n[source,xml,role=\"primary\"]\n----\n<http>\n    <intercept-uri pattern=\"/**\" access=\"authenticated\"/>\n    <oauth2-resource-server>\n        <opaque-token introspector-ref=\"opaqueTokenIntrospector\"\n                authentication-converter-ref=\"opaqueTokenAuthenticationConverter\"/>\n    </oauth2-resource-server>\n</http>\n----\n======\n\nAnd the <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> like so:\n\n.Opaque Token Introspector\n[tabs]\n======\nXml::\n+\n[source,xml,role=\"primary\"]\n----\n<bean id=\"opaqueTokenIntrospector\"\n        class=\"org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector\">\n    <constructor-arg value=\"${spring.security.oauth2.resourceserver.opaquetoken.introspection_uri}\"/>\n    <constructor-arg value=\"${spring.security.oauth2.resourceserver.opaquetoken.client_id}\"/>\n    <constructor-arg value=\"${spring.security.oauth2.resourceserver.opaquetoken.client_secret}\"/>\n</bean>\n----\n======\n\nAnd the `OpaqueTokenAuthenticationConverter` like so:\n\n.Opaque Token Authentication Converter\n[tabs]\n======\nXml::\n+\n[source,xml,role=\"primary\"]\n----\n<bean id=\"opaqueTokenAuthenticationConverter\"\n        class=\"com.example.CustomOpaqueTokenAuthenticationConverter\"/>\n----\n======\n\n[[oauth2resourceserver-opaque-introspectionuri-dsl]]\n=== Using `introspectionUri()`\n\nAn authorization server's Introspection Uri can be configured <<oauth2resourceserver-opaque-introspectionuri,as a configuration property>> or it can be supplied in the DSL:\n\n.Introspection URI Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class DirectlyConfiguredIntrospectionUri {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .opaqueToken((opaqueToken) -> opaqueToken\n                    .introspectionUri(\"https://idp.example.com/introspect\")\n                    .introspectionClientCredentials(\"client\", \"secret\")\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass DirectlyConfiguredIntrospectionUri {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2ResourceServer {\n                opaqueToken {\n                    introspectionUri = \"https://idp.example.com/introspect\"\n                    introspectionClientCredentials(\"client\", \"secret\")\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<bean id=\"opaqueTokenIntrospector\"\n        class=\"org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector\">\n    <constructor-arg value=\"https://idp.example.com/introspect\"/>\n    <constructor-arg value=\"client\"/>\n    <constructor-arg value=\"secret\"/>\n</bean>\n----\n======\n\nUsing `introspectionUri()` takes precedence over any configuration property.\n\n[[oauth2resourceserver-opaque-introspector-dsl]]\n=== Using `introspector()`\n\nMore powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>>:\n\n.Introspector Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class DirectlyConfiguredIntrospector {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .opaqueToken((opaqueToken) -> opaqueToken\n                    .introspector(myCustomIntrospector())\n                )\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass DirectlyConfiguredIntrospector {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            oauth2ResourceServer {\n                opaqueToken {\n                    introspector = myCustomIntrospector()\n                }\n            }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-uri pattern=\"/**\" access=\"authenticated\"/>\n    <oauth2-resource-server>\n        <opaque-token introspector-ref=\"myCustomIntrospector\"/>\n    </oauth2-resource-server>\n</http>\n----\n======\n\nThis is handy when deeper configuration, like <<oauth2resourceserver-opaque-authorization-extraction,authority mapping>>, <<oauth2resourceserver-opaque-jwt-introspector,JWT revocation>>, or <<oauth2resourceserver-opaque-timeouts,request timeouts>>, is necessary.\n\n[[oauth2resourceserver-opaque-introspector-bean]]\n=== Exposing a `OpaqueTokenIntrospector` `@Bean`\n\nOr, exposing a <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> `@Bean` has the same effect as `introspector()`:\n\n[source,java]\n----\n@Bean\npublic OpaqueTokenIntrospector introspector() {\n    return SpringOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n            .clientId(clientId).clientSecret(clientSecret).build();\n}\n----\n\n[[oauth2resourceserver-opaque-authorization]]\n== Configuring Authorization\n\nAn OAuth 2.0 Introspection endpoint will typically return a `scope` attribute, indicating the scopes (or authorities) it's been granted, for example:\n\n`{ ..., \"scope\" : \"messages contacts\"}`\n\nWhen this is the case, Resource Server will attempt to coerce these scopes into a list of granted authorities, prefixing each scope with the string \"SCOPE_\".\n\nThis means that to protect an endpoint or method with a scope derived from an Opaque Token, the corresponding expressions should include this prefix:\n\n.Authorization Opaque Token Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;\n\n@Configuration\n@EnableWebSecurity\npublic class MappedAuthorities {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorizeRequests) -> authorizeRequests\n                .requestMatchers(\"/contacts/**\").access(hasScope(\"contacts\"))\n                .requestMatchers(\"/messages/**\").access(hasScope(\"messages\"))\n                .anyRequest().authenticated()\n            )\n            .oauth2ResourceServer((oauth2) -> oauth2\n                .opaqueToken(Customizer.withDefaults())\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope\n\n@Configuration\n@EnableWebSecurity\nclass MappedAuthorities {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n       http {\n            authorizeHttpRequests {\n                authorize(\"/contacts/**\", hasScope(\"contacts\"))\n                authorize(\"/messages/**\", hasScope(\"messages\"))\n                authorize(anyRequest, authenticated)\n            }\n           oauth2ResourceServer {\n               opaqueToken { }\n           }\n        }\n        return http.build()\n    }\n}\n----\n\nXml::\n+\n[source,xml,role=\"secondary\"]\n----\n<http>\n    <intercept-uri pattern=\"/contacts/**\" access=\"hasAuthority('SCOPE_contacts')\"/>\n    <intercept-uri pattern=\"/messages/**\" access=\"hasAuthority('SCOPE_messages')\"/>\n    <oauth2-resource-server>\n        <opaque-token introspector-ref=\"opaqueTokenIntrospector\"/>\n    </oauth2-resource-server>\n</http>\n----\n======\n\nOr similarly with method security:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@PreAuthorize(\"hasAuthority('SCOPE_messages')\")\npublic List<Message> getMessages(...) {}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@PreAuthorize(\"hasAuthority('SCOPE_messages')\")\nfun getMessages(): List<Message?> {}\n----\n======\n\n[[method-security-has-scope]]\n=== Using `hasScope` in Method Security\n\nBecause method security expressions can evaluation `AuthorizationManager` instances, you can also use the `hasScope` API by publishing a `DefaultOAuth2AuthorizationManagerFactory` `@Bean`:\n\ninclude-code::./MethodSecurityHasScopeConfiguration[tag=declare-factory,indent=0]\n\nand then doing:\n\ninclude-code::./MessageService[tag=protected-method,indent=0]\n\nIf you are using xref:servlet/authentication/mfa.adoc[Spring Security's MFA feature], then you can supply its `AuthorizationManagerFactory` instance to ensure that your authentication factors are automatically checked as well by including it in your `DefaultOAuth2AuthorizationManagerFactory` constructor as follows:\n\ninclude-code::./MethodSecurityHasScopeMfaConfiguration[tag=declare-factory,indent=0]\n\n[[oauth2resourceserver-opaque-authorization-extraction]]\n=== Extracting Authorities Manually\n\nBy default, Opaque Token support will extract the scope claim from an introspection response and parse it into individual `GrantedAuthority` instances.\n\nFor example, if the introspection response were:\n\n[source,json]\n----\n{\n    \"active\" : true,\n    \"scope\" : \"message:read message:write\"\n}\n----\n\nThen Resource Server would generate an `Authentication` with two authorities, one for `message:read` and the other for `message:write`.\n\nThis can, of course, be customized using a custom <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> that takes a look at the attribute set and converts in its own way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector {\n    private OpaqueTokenIntrospector delegate = SpringOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build();\n\n    public OAuth2AuthenticatedPrincipal introspect(String token) {\n        OAuth2AuthenticatedPrincipal principal = this.delegate.introspect(token);\n        return new DefaultOAuth2AuthenticatedPrincipal(\n                principal.getName(), principal.getAttributes(), extractAuthorities(principal));\n    }\n\n    private Collection<GrantedAuthority> extractAuthorities(OAuth2AuthenticatedPrincipal principal) {\n        List<String> scopes = principal.getAttribute(OAuth2IntrospectionClaimNames.SCOPE);\n        return scopes.stream()\n                .map(SimpleGrantedAuthority::new)\n                .collect(Collectors.toList());\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass CustomAuthoritiesOpaqueTokenIntrospector : OpaqueTokenIntrospector {\n    private val delegate: OpaqueTokenIntrospector = SpringOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build()\n    override fun introspect(token: String): OAuth2AuthenticatedPrincipal {\n        val principal: OAuth2AuthenticatedPrincipal = delegate.introspect(token)\n        return DefaultOAuth2AuthenticatedPrincipal(\n                principal.name, principal.attributes, extractAuthorities(principal))\n    }\n\n    private fun extractAuthorities(principal: OAuth2AuthenticatedPrincipal): Collection<GrantedAuthority> {\n        val scopes: List<String> = principal.getAttribute(OAuth2IntrospectionClaimNames.SCOPE)\n        return scopes\n                .map { SimpleGrantedAuthority(it) }\n    }\n}\n----\n======\n\nThereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OpaqueTokenIntrospector introspector() {\n    return new CustomAuthoritiesOpaqueTokenIntrospector();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): OpaqueTokenIntrospector {\n    return CustomAuthoritiesOpaqueTokenIntrospector()\n}\n----\n======\n\n[[oauth2resourceserver-opaque-timeouts]]\n== Configuring Timeouts\n\nBy default, Resource Server uses connection and socket timeouts of 30 seconds each for coordinating with the authorization server.\n\nThis may be too short in some scenarios.\nFurther, it doesn't take into account more sophisticated patterns like back-off and discovery.\n\n[[opaque-token-timeouts-rest-client]]\n=== Using `RestClientOpaqueTokenIntrospector`\n\nYou can use `RestClientOpaqueTokenIntrospector`, which uses `RestClient` to communicate with the introspection endpoint.\n\n[TIP]\n====\nWhen using Spring Boot, you can inject `OAuth2ResourceServerProperties` to obtain the introspection URI and client credentials.\n====\n\n.Minimal configuration using the builder\ninclude-code::./RestClientOpaqueTokenIntrospectorConfiguration[tag=restclient-simple,indent=0]\n\nTo customize timeouts, build a `RestClient` with a custom `RequestFactory` and pass it to the introspector:\n\n.Custom timeouts\ninclude-code::./RestClientOpaqueTokenIntrospectorConfiguration[tag=restclient-timeouts,indent=0]\n\n[TIP]\n====\nIf you prefer to use `RestTemplate`, you can use `SpringOpaqueTokenIntrospector` instead, which accepts an instance of `RestOperations`.\n====\n\n[[oauth2resourceserver-opaque-jwt-introspector]]\n== Using Introspection with JWTs\n\nA common question is whether or not introspection is compatible with JWTs.\nSpring Security's Opaque Token support has been designed to not care about the format of the token -- it will gladly pass any token to the introspection endpoint provided.\n\nSo, let's say that you've got a requirement that requires you to check with the authorization server on each request, in case the JWT has been revoked.\n\nEven though you are using the JWT format for the token, your validation method is introspection, meaning you'd want to do:\n\n[source,yaml]\n----\nspring:\n  security:\n    oauth2:\n      resourceserver:\n        opaquetoken:\n          introspection-uri: https://idp.example.org/introspection\n          client-id: client\n          client-secret: secret\n----\n\nIn this case, the resulting `Authentication` would be `BearerTokenAuthentication`.\nAny attributes in the corresponding `OAuth2AuthenticatedPrincipal` would be whatever was returned by the introspection endpoint.\n\nBut, let's say that, oddly enough, the introspection endpoint only returns whether or not the token is active.\nNow what?\n\nIn this case, you can create a custom <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>> that still hits the endpoint, but then updates the returned principal to have the JWTs claims as the attributes:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class JwtOpaqueTokenIntrospector implements OpaqueTokenIntrospector {\n    private OpaqueTokenIntrospector delegate = SpringOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build();\n    private JwtDecoder jwtDecoder = new NimbusJwtDecoder(new ParseOnlyJWTProcessor());\n\n    public OAuth2AuthenticatedPrincipal introspect(String token) {\n        OAuth2AuthenticatedPrincipal principal = this.delegate.introspect(token);\n        try {\n            Jwt jwt = this.jwtDecoder.decode(token);\n            return new DefaultOAuth2AuthenticatedPrincipal(jwt.getClaims(), NO_AUTHORITIES);\n        } catch (JwtException ex) {\n            throw new OAuth2IntrospectionException(ex);\n        }\n    }\n\n    private static class ParseOnlyJWTProcessor extends DefaultJWTProcessor<SecurityContext> {\n    \tJWTClaimsSet process(SignedJWT jwt, SecurityContext context)\n                throws JOSEException {\n            return jwt.getJWTClaimsSet();\n        }\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass JwtOpaqueTokenIntrospector : OpaqueTokenIntrospector {\n    private val delegate: OpaqueTokenIntrospector = SpringOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build()\n    private val jwtDecoder: JwtDecoder = NimbusJwtDecoder(ParseOnlyJWTProcessor())\n    override fun introspect(token: String): OAuth2AuthenticatedPrincipal {\n        val principal = delegate.introspect(token)\n        return try {\n            val jwt: Jwt = jwtDecoder.decode(token)\n            DefaultOAuth2AuthenticatedPrincipal(jwt.claims, NO_AUTHORITIES)\n        } catch (ex: JwtException) {\n            throw OAuth2IntrospectionException(ex.message)\n        }\n    }\n\n    private class ParseOnlyJWTProcessor : DefaultJWTProcessor<SecurityContext>() {\n        override fun process(jwt: SignedJWT, context: SecurityContext): JWTClaimsSet {\n            return jwt.jwtClaimsSet\n        }\n    }\n}\n----\n======\n\nThereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic OpaqueTokenIntrospector introspector() {\n    return new JwtOpaqueTokenIntrospector();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): OpaqueTokenIntrospector {\n    return JwtOpaqueTokenIntrospector()\n}\n----\n======\n\n[[oauth2resourceserver-opaque-userinfo]]\n== Calling a `/userinfo` Endpoint\n\nGenerally speaking, a Resource Server doesn't care about the underlying user, but instead about the authorities that have been granted.\n\nThat said, at times it can be valuable to tie the authorization statement back to a user.\n\nIf an application is also using `spring-security-oauth2-client`, having set up the appropriate `ClientRegistrationRepository`, then this is quite simple with a custom <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>>.\nThis implementation below does three things:\n\n* Delegates to the introspection endpoint, to affirm the token's validity\n* Looks up the appropriate client registration associated with the `/userinfo` endpoint\n* Invokes and returns the response from the `/userinfo` endpoint\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {\n    private final OpaqueTokenIntrospector delegate = SpringOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build();\n    private final OAuth2UserService oauth2UserService = new DefaultOAuth2UserService();\n\n    private final ClientRegistrationRepository repository;\n\n    // ... constructor\n\n    @Override\n    public OAuth2AuthenticatedPrincipal introspect(String token) {\n        OAuth2AuthenticatedPrincipal authorized = this.delegate.introspect(token);\n        Instant issuedAt = authorized.getAttribute(ISSUED_AT);\n        Instant expiresAt = authorized.getAttribute(EXPIRES_AT);\n        ClientRegistration clientRegistration = this.repository.findByRegistrationId(\"registration-id\");\n        OAuth2AccessToken token = new OAuth2AccessToken(BEARER, token, issuedAt, expiresAt);\n        OAuth2UserRequest oauth2UserRequest = new OAuth2UserRequest(clientRegistration, token);\n        return this.oauth2UserService.loadUser(oauth2UserRequest);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass UserInfoOpaqueTokenIntrospector : OpaqueTokenIntrospector {\n    private val delegate: OpaqueTokenIntrospector = SpringOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build()\n    private val oauth2UserService = DefaultOAuth2UserService()\n    private val repository: ClientRegistrationRepository? = null\n\n    // ... constructor\n\n    override fun introspect(token: String): OAuth2AuthenticatedPrincipal {\n        val authorized = delegate.introspect(token)\n        val issuedAt: Instant? = authorized.getAttribute(ISSUED_AT)\n        val expiresAt: Instant? = authorized.getAttribute(EXPIRES_AT)\n        val clientRegistration: ClientRegistration = repository!!.findByRegistrationId(\"registration-id\")\n        val accessToken = OAuth2AccessToken(BEARER, token, issuedAt, expiresAt)\n        val oauth2UserRequest = OAuth2UserRequest(clientRegistration, accessToken)\n        return oauth2UserService.loadUser(oauth2UserRequest)\n    }\n}\n----\n======\n\nIf you aren't using `spring-security-oauth2-client`, it's still quite simple.\nYou will simply need to invoke the `/userinfo` with your own instance of `WebClient`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {\n    private final OpaqueTokenIntrospector delegate = SpringOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build();\n    private final WebClient rest = WebClient.create();\n\n    @Override\n    public OAuth2AuthenticatedPrincipal introspect(String token) {\n        OAuth2AuthenticatedPrincipal authorized = this.delegate.introspect(token);\n        return makeUserInfoRequest(authorized);\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nclass UserInfoOpaqueTokenIntrospector : OpaqueTokenIntrospector {\n    private val delegate: OpaqueTokenIntrospector = SpringOpaqueTokenIntrospector\n            .withIntrospectionUri(\"https://idp.example.org/introspect\")\n            .clientId(\"client\").clientSecret(\"secret\").build()\n    private val rest: WebClient = WebClient.create()\n\n    override fun introspect(token: String): OAuth2AuthenticatedPrincipal {\n        val authorized = delegate.introspect(token)\n        return makeUserInfoRequest(authorized)\n    }\n}\n----\n======\n\nEither way, having created your <<oauth2resourceserver-opaque-architecture-introspector,`OpaqueTokenIntrospector`>>, you should publish it as a `@Bean` to override the defaults:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nOpaqueTokenIntrospector introspector() {\n    return new UserInfoOpaqueTokenIntrospector(...);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun introspector(): OpaqueTokenIntrospector {\n    return UserInfoOpaqueTokenIntrospector(...)\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/oauth2/resource-server/protected-resource-metadata.adoc",
    "content": "[[oauth2resourceserver-protected-resource-metadata]]\n= OAuth 2.0 Protected Resource Metadata\n\n`OAuth2ResourceServerConfigurer.ProtectedResourceMetadataConfigurer` provides the ability to customize the https://www.rfc-editor.org/rfc/rfc9728.html#section-3[OAuth 2.0 Protected Resource Metadata endpoint].\nIt defines an extension point that lets you customize the https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2[OAuth 2.0 Protected Resource Metadata response].\n\n`OAuth2ResourceServerConfigurer.ProtectedResourceMetadataConfigurer` provides the following configuration option:\n\n[source,java]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\thttp\n\t\t.oauth2ResourceServer((resourceServer) ->\n\t\t\tresourceServer\n\t\t\t\t.protectedResourceMetadata(protectedResourceMetadata ->\n                    protectedResourceMetadata\n                        .protectedResourceMetadataCustomizer(protectedResourceMetadataCustomizer)   <1>\n\t\t\t\t)\n\t\t);\n\n\treturn http.build();\n}\n----\n<1> `protectedResourceMetadataCustomizer()`: The `Consumer` providing access to the `OAuth2ProtectedResourceMetadata.Builder` allowing the ability to customize the claims of the Resource Server's configuration.\n\n`OAuth2ResourceServerConfigurer.ProtectedResourceMetadataConfigurer` configures the `OAuth2ProtectedResourceMetadataFilter` and registers it with the Resource Server `SecurityFilterChain` `@Bean`.\n`OAuth2ProtectedResourceMetadataFilter` is the `Filter` that returns the https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2[OAuth2ProtectedResourceMetadata response].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/saml2/index.adoc",
    "content": "[[servlet-saml2]]\n= SAML2\n:page-section-summary-toc: 1\n\nSpring Security provides comprehensive SAML 2 support.\nThis section discusses how to integrate SAML 2 into your servlet based application.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/saml2/login/authentication-requests.adoc",
    "content": "[[servlet-saml2login-sp-initiated-factory]]\n= Producing ``<saml2:AuthnRequest>``s\n\nAs stated earlier, Spring Security's SAML 2.0 support produces a `<saml2:AuthnRequest>` to commence authentication with the asserting party.\n\nSpring Security achieves this in part by registering the `Saml2WebSsoAuthenticationRequestFilter` in the filter chain.\nThis filter by default responds to the endpoints `+/saml2/authenticate/{registrationId}+` and `+/saml2/authenticate?registrationId={registrationId}+`.\n\nFor example, if you were deployed to `https://rp.example.com` and you gave your registration an ID of `okta`, you could navigate to:\n\n`https://rp.example.org/saml2/authenticate/okta`\n\nand the result would be a redirect that included a `SAMLRequest` parameter containing the signed, deflated, and encoded `<saml2:AuthnRequest>`.\n\n[[configuring-authentication-request-uri]]\n== Configuring the `<saml2:AuthnRequest>` Endpoint\n\nTo configure the endpoint differently from the default, you can set the value in `saml2Login`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityFilterChain filterChain(HttpSecurity http) {\n\thttp\n        .saml2Login((saml2) -> saml2\n            .authenticationRequestUriQuery(\"/custom/auth/sso?peerEntityID={registrationId}\")\n        );\n\treturn new CustomSaml2AuthenticationRequestRepository();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        saml2Login {\n            authenticationRequestUriQuery = \"/custom/auth/sso?peerEntityID={registrationId}\"\n        }\n    }\n    return CustomSaml2AuthenticationRequestRepository()\n}\n----\n======\n\n[[servlet-saml2login-store-authn-request]]\n== Changing How the `<saml2:AuthnRequest>` Gets Stored\n\n`Saml2WebSsoAuthenticationRequestFilter` uses an `Saml2AuthenticationRequestRepository` to persist an `AbstractSaml2AuthenticationRequest` instance before xref:servlet/saml2/login/authentication-requests.adoc#servlet-saml2login-sp-initiated-factory[sending the `<saml2:AuthnRequest>`] to the asserting party.\n\nAdditionally, `Saml2WebSsoAuthenticationFilter` and `Saml2AuthenticationTokenConverter` use an `Saml2AuthenticationRequestRepository` to load any `AbstractSaml2AuthenticationRequest` as part of xref:servlet/saml2/login/authentication.adoc#servlet-saml2login-authenticate-responses[authenticating the `<saml2:Response>`].\n\nBy default, Spring Security uses an `HttpSessionSaml2AuthenticationRequestRepository`, which stores the `AbstractSaml2AuthenticationRequest` in the `HttpSession`.\n\nIf you have a custom implementation of `Saml2AuthenticationRequestRepository`, you may configure it by exposing it as a `@Bean` as shown in the following example:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {\n\treturn new CustomSaml2AuthenticationRequestRepository();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {\n    return CustomSaml2AuthenticationRequestRepository()\n}\n----\n======\n\n=== Caching the `<saml2:AuthnRequest>` by the Relay State\n\nIf you don't want to use the session to store the `<saml2:AuthnRequest>`, you can also store it in a distributed cache.\nThis can be helpful if you are trying to use `SameSite=Strict` and are losing the authentication request in the redirect from the Identity Provider.\n\n[NOTE]\n=====\nIt's important to remember that there are security benefits to storing it in the session.\nOne such benefit is the natural login fixation defense it provides.\nFor example, if an application looks the authentication request up from the session, then even if an attacker provides their own SAML response to a victim, the login will fail.\n\nOn the other hand, if we trust the InResponseTo or RelayState to retrieve the authentication request, then there's no way to know if the SAML response was requested by that handshake.\n=====\n\nTo help with this, Spring Security has `CacheSaml2AuthenticationRequestRepository`, which you can publish as a bean for the filter chain to pick up:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSaml2AuthenticationRequestRepository<?> authenticationRequestRepository() {\n\treturn new CacheSaml2AuthenticationRequestRepository();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<*> {\n    return CacheSaml2AuthenticationRequestRepository()\n}\n----\n======\n\n\n[[servlet-saml2login-sp-initiated-factory-signing]]\n== Changing How the `<saml2:AuthnRequest>` Gets Sent\n\nBy default, Spring Security signs each `<saml2:AuthnRequest>` and send it as a GET to the asserting party.\n\nMany asserting parties don't require a signed `<saml2:AuthnRequest>`.\nThis can be configured automatically via `RelyingPartyRegistrations`, or you can supply it manually, like so:\n\n\n.Not Requiring Signed AuthnRequests\n[tabs]\n======\nBoot::\n+\n[source,yaml,role=\"primary\"]\n----\nspring:\n  security:\n    saml2:\n      relyingparty:\n        registration:\n          okta:\n            assertingparty:\n              entity-id: ...\n              singlesignon.sign-request: false\n----\n\nJava::\n+\n[source,java,role=\"secondary\"]\n----\nRelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId(\"okta\")\n        // ...\n        .assertingPartyMetadata((party) -> party\n            // ...\n            .wantAuthnRequestsSigned(false)\n        )\n        .build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nvar relyingPartyRegistration: RelyingPartyRegistration =\n    RelyingPartyRegistration.withRegistrationId(\"okta\")\n        // ...\n        .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party\n                // ...\n                .wantAuthnRequestsSigned(false)\n        }\n        .build()\n----\n======\n\nOtherwise, you will need to specify a private key to `RelyingPartyRegistration#signingX509Credentials` so that Spring Security can sign the `<saml2:AuthnRequest>` before sending.\n\n[[servlet-saml2login-sp-initiated-factory-algorithm]]\nBy default, Spring Security will sign the `<saml2:AuthnRequest>` using `rsa-sha256`, though some asserting parties will require a different algorithm, as indicated in their metadata.\n\nYou can configure the algorithm based on the asserting party's xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[metadata using `RelyingPartyRegistrations`].\n\nOr, you can provide it manually:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nString metadataLocation = \"classpath:asserting-party-metadata.xml\";\nRelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)\n        // ...\n        .assertingPartyMetadata((party) -> party\n            // ...\n            .signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512))\n        )\n        .build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nvar metadataLocation = \"classpath:asserting-party-metadata.xml\"\nvar relyingPartyRegistration: RelyingPartyRegistration =\n    RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)\n        // ...\n        .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party\n                // ...\n                .signingAlgorithms { sign: MutableList<String?> ->\n                    sign.add(\n                        SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512\n                    )\n                }\n        }\n        .build()\n----\n======\n\nNOTE: The snippet above uses the OpenSAML `SignatureConstants` class to supply the algorithm name.\nBut, that's just for convenience.\nSince the datatype is `String`, you can supply the name of the algorithm directly.\n\n[[servlet-saml2login-sp-initiated-factory-binding]]\nSome asserting parties require that the `<saml2:AuthnRequest>` be POSTed.\nThis can be configured automatically via `RelyingPartyRegistrations`, or you can supply it manually, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nRelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId(\"okta\")\n        // ...\n        .assertingPartyMetadata((party) -> party\n            // ...\n            .singleSignOnServiceBinding(Saml2MessageBinding.POST)\n        )\n        .build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nvar relyingPartyRegistration: RelyingPartyRegistration? =\n    RelyingPartyRegistration.withRegistrationId(\"okta\")\n        // ...\n        .assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party\n            // ...\n            .singleSignOnServiceBinding(Saml2MessageBinding.POST)\n        }\n        .build()\n----\n======\n\n[[servlet-saml2login-sp-initiated-factory-custom-authnrequest]]\n== Customizing OpenSAML's `AuthnRequest` Instance\n\nThere are a number of reasons that you may want to adjust an `AuthnRequest`.\nFor example, you may want `ForceAuthN` to be set to `true`, which Spring Security sets to `false` by default.\n\nYou can customize elements of OpenSAML's `AuthnRequest` by publishing an `OpenSaml5AuthenticationRequestResolver` as a `@Bean`, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSaml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) {\n    RelyingPartyRegistrationResolver registrationResolver =\n            new DefaultRelyingPartyRegistrationResolver(registrations);\n    OpenSaml5AuthenticationRequestResolver authenticationRequestResolver =\n            new OpenSaml5AuthenticationRequestResolver(registrationResolver);\n    authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context\n            .getAuthnRequest().setForceAuthn(true));\n    return authenticationRequestResolver;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationRequestResolver(registrations : RelyingPartyRegistrationRepository) : Saml2AuthenticationRequestResolver {\n    val registrationResolver : RelyingPartyRegistrationResolver =\n            new DefaultRelyingPartyRegistrationResolver(registrations)\n    val authenticationRequestResolver : OpenSaml5AuthenticationRequestResolver =\n            new OpenSaml5AuthenticationRequestResolver(registrationResolver)\n    authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context\n            .getAuthnRequest().setForceAuthn(true))\n    return authenticationRequestResolver\n}\n----\n======\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/saml2/login/authentication.adoc",
    "content": "[[servlet-saml2login-authenticate-responses]]\n= Authenticating ``<saml2:Response>``s\n\nTo verify SAML 2.0 Responses, Spring Security uses xref:servlet/saml2/login/overview.adoc#servlet-saml2login-authentication-saml2authenticationtokenconverter[`Saml2AuthenticationTokenConverter`] to populate the `Authentication` request and xref:servlet/saml2/login/overview.adoc#servlet-saml2login-architecture[`OpenSaml5AuthenticationProvider`] to authenticate it.\n\nYou can configure this in a number of ways including:\n\n1. Changing the way the `RelyingPartyRegistration` is Looked Up\n2. Setting a clock skew to timestamp validation\n3. Mapping the response to a list of `GrantedAuthority` instances\n4. Customizing the strategy for validating assertions\n5. Customizing the strategy for decrypting response and assertion elements\n\nTo configure these, you'll use the `saml2Login#authenticationManager` method in the DSL.\n\n[[saml2-response-processing-endpoint]]\n== Changing the SAML Response Processing Endpoint\n\nThe default endpoint is `+/login/saml2/sso/{registrationId}+`.\nYou can change this in the DSL and in the associated metadata like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityFilterChain securityFilters(HttpSecurity http) throws Exception {\n\thttp\n        // ...\n        .saml2Login((saml2) -> saml2.loginProcessingUrl(\"/saml2/login/sso\"))\n        // ...\n\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun securityFilters(val http: HttpSecurity): SecurityFilterChain {\n\thttp {\n        // ...\n        .saml2Login {\n            loginProcessingUrl = \"/saml2/login/sso\"\n        }\n        // ...\n    }\n\n    return http.build()\n}\n----\n======\n\nand:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nrelyingPartyRegistrationBuilder.assertionConsumerServiceLocation(\"/saml/SSO\")\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nrelyingPartyRegistrationBuilder.assertionConsumerServiceLocation(\"/saml/SSO\")\n----\n======\n\n[[relyingpartyregistrationresolver-apply]]\n== Changing `RelyingPartyRegistration` lookup\n\nBy default, this converter will match against any associated `<saml2:AuthnRequest>` or any `registrationId` it finds in the URL.\nOr, if it cannot find one in either of those cases, then it attempts to look it up by the `<saml2:Response#Issuer>` element.\n\nThere are a number of circumstances where you might need something more sophisticated, like if you are supporting `ARTIFACT` binding.\nIn those cases, you can customize lookup through a custom `AuthenticationConverter`, which you can customize like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSecurityFilterChain securityFilters(HttpSecurity http, AuthenticationConverter authenticationConverter) throws Exception {\n\thttp\n        // ...\n        .saml2Login((saml2) -> saml2.authenticationConverter(authenticationConverter))\n        // ...\n\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun securityFilters(val http: HttpSecurity, val converter: AuthenticationConverter): SecurityFilterChain {\n\thttp {\n        // ...\n        .saml2Login {\n            authenticationConverter = converter\n        }\n        // ...\n    }\n\n    return http.build()\n}\n----\n======\n\n[[servlet-saml2login-opensamlauthenticationprovider-clockskew]]\n== Setting a Clock Skew\n\nIt's not uncommon for the asserting and relying parties to have system clocks that aren't perfectly synchronized.\nFor that reason, you can configure `OpenSaml5AuthenticationProvider.AssertionValidator` as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        OpenSaml5AuthenticationProvider authenticationProvider = new OpenSaml5AuthenticationProvider();\n        AssertionValidator assertionValidator = AssertionValidator.builder()\n                .clockSkew(Duration.ofMinutes(10)).build();\n\t\tauthenticationProvider.setAssertionValidator(assertionValidator);\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated()\n            )\n            .saml2Login((saml2) -> saml2\n                .authenticationManager(new ProviderManager(authenticationProvider))\n            );\n        return http.build();\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n\n\n@Configuration @EnableWebSecurity\nclass SecurityConfig {\n    @Bean\n    @Throws(Exception::class)\n    fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        val authenticationProvider = OpenSaml5AuthenticationProvider()\n        val assertionValidator = AssertionValidator.builder().clockSkew(Duration.ofMinutes(10)).build()\n        authenticationProvider.setAssertionValidator(assertionValidator)\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            saml2Login {\n                authenticationManager = ProviderManager(authenticationProvider)\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n== Converting an `Assertion` into an `Authentication`\n\n`OpenSamlXAuthenticationProvider#setResponseAuthenticationConverter` provides a way for you to change how it converts your assertion into an `Authentication` instance.\n\nYou can set a custom converter in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n    @Autowired\n    Converter<ResponseToken, Saml2Authentication> authenticationConverter;\n\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        OpenSaml5AuthenticationProvider authenticationProvider = new OpenSaml5AuthenticationProvider();\n        authenticationProvider.setResponseAuthenticationConverter(this.authenticationConverter);\n\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated())\n            .saml2Login((saml2) -> saml2\n                .authenticationManager(new ProviderManager(authenticationProvider))\n            );\n        return http.build();\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nopen class SecurityConfig {\n    @Autowired\n    var authenticationConverter: Converter<ResponseToken, Saml2Authentication>? = null\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        val authenticationProvider = OpenSaml5AuthenticationProvider()\n        authenticationProvider.setResponseAuthenticationConverter(this.authenticationConverter)\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            saml2Login {\n                authenticationManager = ProviderManager(authenticationProvider)\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nThe ensuing examples all build off of this common construct to show you different ways this converter comes in handy.\n\n[[servlet-saml2login-opensamlauthenticationprovider-userdetailsservice]]\n== Coordinating with a `UserDetailsService`\n\nOr, perhaps you would like to include user details from a legacy `UserDetailsService`.\nIn that case, the response authentication converter can come in handy, as can be seen below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\nclass MyUserDetailsResponseAuthenticationConverter implements Converter<ResponseToken, Saml2Authentication> {\n\tprivate final ResponseAuthenticationConverter delegate = new ResponseAuthenticationConverter();\n\tprivate final UserDetailsService userDetailsService;\n\n\tMyUserDetailsResponseAuthenticationConverter(UserDetailsService userDetailsService) {\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t@Override\n    public Saml2Authentication convert(ResponseToken responseToken) {\n\t    Saml2Authentication authentication = this.delegate.convert(responseToken); <1>\n        String username = authentication.getName();\n\t\tUserDetails user  = this.userDetailsService.loadUserByUsername(username); <2>\n\t\tString saml2Response = authentication.getSaml2Response();\n\t\tSaml2ResponseAssertionAccessor assertion = new OpenSamlResponseAssertionAccessor(\n\t\t\t\tsaml2Response, CollectionUtils.getFirst(responseToken.getResponse().getAssertions()));\n\t\tCollection<GrantedAuthority> authorities = user.getAuthorities();\n\t\treturn new Saml2AssertionAuthentication(user, assertion, authorities); <3>\n    }\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class MyUserDetailsResponseAuthenticationConverter(private val userDetailsService: UserDetailsService) : Converter<ResponseToken, Saml2Authentication> {\n\n    private val delegate = ResponseAuthenticationConverter()\n\n    override fun convert(responseToken: ResponseToken): Saml2Authentication {\n\t    val authentication = this.delegate.convert(responseToken) <1>\n        val username = authentication.name\n\t\tval userDetails = this.userDetailsService.loadUserByUsername(username) <2>\n\t\tval saml2Response = authentication.saml2Response\n\t\tval assertion = OpenSamlResponseAssertionAccessor(\n\t\t\t\tsaml2Response, responseToken.response.assertions.firstOrNull())\n\t\tval authorities = principal.getAuthorities()\n\t\treturn Saml2AssertionAuthentication(userDetails, assertion, userDetails.authorities) <3>\n    }\n\n}\n----\n======\n<1> First, call the default converter, which extracts attributes and authorities from the response\n<2> Second, call the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] using the relevant information\n<3> Third, return an authentication that includes the user details\n\n[TIP]\n====\nIf your `UserDetailsService` returns a value that also implements `AuthenticatedPrincipal`, then you don't need a custom authentication implementation.\n====\n\n[NOTE]\nIt's not required to call ``OpenSaml5AuthenticationProvider``'s default authentication converter.\nIt returns a `Saml2AuthenticatedPrincipal` containing the attributes it extracted from ``AttributeStatement``s as well as the single `ROLE_USER` authority.\n\n=== Configuring the Principal Name\n\nSometimes, the principal name is not in the `<saml2:NameID>` element.\nIn that case, you can configure the `ResponseAuthenticationConverter` with a custom strategy like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nResponseAuthenticationConverter authenticationConverter() {\n\tResponseAuthenticationConverter authenticationConverter = new ResponseAuthenticationConverter();\n\tauthenticationConverter.setPrincipalNameConverter((assertion) -> {\n\t\t// ... work with OpenSAML's Assertion object to extract the principal\n\t});\n\treturn authenticationConverter;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationConverter(): ResponseAuthenticationConverter {\n    val authenticationConverter: ResponseAuthenticationConverter = ResponseAuthenticationConverter()\n    authenticationConverter.setPrincipalNameConverter { assertion ->\n\t\t// ... work with OpenSAML's Assertion object to extract the principal\n    }\n    return authenticationConverter\n}\n----\n======\n\n=== Configuring a Principal's Granted Authorities\n\nSpring Security automatically grants `ROLE_USER` when using `OpenSamlXAuhenticationProvider`.\nWith `OpenSaml5AuthenticationProvider`, you can configure a different set of granted authorities like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nResponseAuthenticationConverter authenticationConverter() {\n\tResponseAuthenticationConverter authenticationConverter = new ResponseAuthenticationConverter();\n\tauthenticationConverter.setPrincipalNameConverter((assertion) -> {\n\t\t// ... grant the needed authorities based on attributes in the assertion\n\t});\n\treturn authenticationConverter;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun authenticationConverter(): ResponseAuthenticationConverter {\n    val authenticationConverter = ResponseAuthenticationConverter()\n    authenticationConverter.setPrincipalNameConverter{ assertion ->\n\t\t// ... grant the needed authorities based on attributes in the assertion\n    }\n    return authenticationConverter\n}\n----\n======\n\n[[servlet-saml2login-opensamlauthenticationprovider-additionalvalidation]]\n== Performing Additional Response Validation\n\n`OpenSaml5AuthenticationProvider` validates the `Issuer` and `Destination` values right after decrypting the `Response`.\nYou can customize the validation by extending the default validator concatenating with your own response validator, or you can replace it entirely with yours.\n\nFor example, you can throw a custom exception with any additional information available in the `Response` object, like so:\n[source,java]\n----\nOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\nResponseValidator responseValidator = ResponseValidator.withDefaults(myCustomValidator);\nprovider.setResponseValidator(responseValidator);\n----\n\nYou can also customize which validation steps Spring Security should do.\nFor example, if you want to skip `Response#InResponseTo` validation, you can call ``ResponseValidator``'s constructor, excluding `InResponseToValidator` from the list:\n\n[source,java]\n----\nOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\nResponseValidator responseValidator = new ResponseValidator(new DestinationValidator(), new IssuerValidator());\nprovider.setResponseValidator(responseValidator);\n----\n\n[TIP]\n====\nOpenSAML performs `Asssertion#InResponseTo` validation in its `BearerSubjectConfirmationValidator` class, which is configurable using <<_performing_additional_assertion_validation, setAssertionValidator>>.\n====\n\n== Performing Additional Assertion Validation\n`OpenSaml5AuthenticationProvider` performs minimal validation on SAML 2.0 Assertions.\nAfter verifying the signature, it will:\n\n1. Validate `<AudienceRestriction>` and `<DelegationRestriction>` conditions\n2. Validate ``<SubjectConfirmation>``s, expect for any IP address information\n\nTo perform additional validation, you can configure your own assertion validator that delegates to ``OpenSaml5AuthenticationProvider``'s default and then performs its own.\n\n[[servlet-saml2login-opensamlauthenticationprovider-onetimeuse]]\nFor example, you can use OpenSAML's `OneTimeUseConditionValidator` to also validate a `<OneTimeUse>` condition, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\nOneTimeUseConditionValidator validator = ...;\nAssertionValidator assertionValidator = AssertionValidator.builder()\n        .conditionValidators((c) -> c.add(validator)).build();\nprovider.setAssertionValidator(assertionValidator);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval provider = OpenSaml5AuthenticationProvider()\nval validator: OneTimeUseConditionValidator = ...;\nval assertionValidator = AssertionValidator.builder()\n        .conditionValidators { add(validator) }.build()\nprovider.setAssertionValidator(assertionValidator)\n----\n======\n\nYou can use this same builder to remove validators that you don't want to use like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\nAssertionValidator assertionValidator = AssertionValidator.builder()\n        .conditionValidators((c) -> c.removeIf(AudienceRestrictionValidator.class::isInstance)).build();\nprovider.setAssertionValidator(assertionValidator);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval provider = new OpenSaml5AuthenticationProvider()\nval assertionValidator = AssertionValidator.builder()\n        .conditionValidators {\n\t\t\tc: List<ConditionValidator> -> c.removeIf { it is AudienceRestrictionValidator }\n        }.build()\nprovider.setAssertionValidator(assertionValidator)\n----\n======\n\n[[servlet-saml2login-opensamlauthenticationprovider-decryption]]\n== Customizing Decryption\n\nSpring Security decrypts `<saml2:EncryptedAssertion>`, `<saml2:EncryptedAttribute>`, and `<saml2:EncryptedID>` elements automatically by using the decryption xref:servlet/saml2/login/overview.adoc#servlet-saml2login-rpr-credentials[`Saml2X509Credential` instances] registered in the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`].\n\n`OpenSaml5AuthenticationProvider` exposes xref:servlet/saml2/login/overview.adoc#servlet-saml2login-architecture[two decryption strategies].\nThe response decrypter is for decrypting encrypted elements of the `<saml2:Response>`, like `<saml2:EncryptedAssertion>`.\nThe assertion decrypter is for decrypting encrypted elements of the `<saml2:Assertion>`, like `<saml2:EncryptedAttribute>` and `<saml2:EncryptedID>`.\n\nYou can replace ``OpenSaml5AuthenticationProvider``'s default decryption strategy with your own.\nFor example, if you have a separate service that decrypts the assertions in a `<saml2:Response>`, you can use it instead like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMyDecryptionService decryptionService = ...;\nOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\nprovider.setResponseElementsDecrypter((responseToken) -> decryptionService.decrypt(responseToken.getResponse()));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval decryptionService: MyDecryptionService = ...\nval provider = OpenSaml5AuthenticationProvider()\nprovider.setResponseElementsDecrypter { responseToken -> decryptionService.decrypt(responseToken.response) }\n----\n======\n\nIf you are also decrypting individual elements in a `<saml2:Assertion>`, you can customize the assertion decrypter, too:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprovider.setAssertionElementsDecrypter((assertionToken) -> decryptionService.decrypt(assertionToken.getAssertion()));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nprovider.setAssertionElementsDecrypter { assertionToken -> decryptionService.decrypt(assertionToken.assertion) }\n----\n======\n\nNOTE: There are two separate decrypters since assertions can be signed separately from responses.\nTrying to decrypt a signed assertion's elements before signature verification may invalidate the signature.\nIf your asserting party signs the response only, then it's safe to decrypt all elements using only the response decrypter.\n\n[[servlet-saml2login-authenticationmanager-custom]]\n== Using a Custom Authentication Manager\n\n[[servlet-saml2login-opensamlauthenticationprovider-authenticationmanager]]\nOf course, the `authenticationManager` DSL method can be also used to perform a completely custom SAML 2.0 authentication.\nThis authentication manager should expect a `Saml2AuthenticationToken` object containing the SAML 2.0 Response XML data.\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        AuthenticationManager authenticationManager = new MySaml2AuthenticationManager(...);\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated()\n            )\n            .saml2Login((saml2) -> saml2\n                .authenticationManager(authenticationManager)\n            )\n        ;\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nopen class SecurityConfig {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        val customAuthenticationManager: AuthenticationManager = MySaml2AuthenticationManager(...)\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            saml2Login {\n                authenticationManager = customAuthenticationManager\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[[servlet-saml2login-authenticatedprincipal]]\n== Using `Saml2AuthenticatedPrincipal`\n\nWith the relying party correctly configured for a given asserting party, it's ready to accept assertions.\nOnce the relying party validates an assertion, the result is a `Saml2Authentication` with a `Saml2AuthenticatedPrincipal`.\n\nThis means that you can access the principal in your controller like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Controller\npublic class MainController {\n\t@GetMapping(\"/\")\n\tpublic String index(@AuthenticationPrincipal Saml2AuthenticatedPrincipal principal, Model model) {\n\t\tString email = principal.getFirstAttribute(\"email\");\n\t\tmodel.setAttribute(\"email\", email);\n\t\treturn \"index\";\n\t}\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Controller\nclass MainController {\n    @GetMapping(\"/\")\n    fun index(@AuthenticationPrincipal principal: Saml2AuthenticatedPrincipal, model: Model): String {\n        val email = principal.getFirstAttribute<String>(\"email\")\n        model.setAttribute(\"email\", email)\n        return \"index\"\n    }\n}\n----\n======\n\n[TIP]\nBecause the SAML 2.0 specification allows for each attribute to have multiple values, you can either call `getAttribute` to get the list of attributes or `getFirstAttribute` to get the first in the list.\n`getFirstAttribute` is quite handy when you know that there is only one value.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/saml2/login/index.adoc",
    "content": "[[servlet-saml2login]]\n= SAML 2.0 Login\n:page-section-summary-toc: 1\n\nThe SAML 2.0 Login feature provides an application with the ability to act as a SAML 2.0 relying party, having users https://wiki.shibboleth.net/confluence/display/CONCEPT/FlowsAndConfig[log in] to the application by using their existing account at a SAML 2.0 Asserting Party (Okta, ADFS, and others).\n\n[NOTE]\n====\nSAML 2.0 Login is implemented by using the *Web Browser SSO Profile*, as specified in\nhttps://www.oasis-open.org/committees/download.php/35389/sstc-saml-profiles-errata-2.0-wd-06-diff.pdf#page=15[SAML 2 Profiles].\n====\n\n[[servlet-saml2login-spring-security-history]]\nSince 2009, support for relying parties has existed as an https://github.com/spring-projects/spring-security-saml/tree/1e013b07a7772defd6a26fcfae187c9bf661ee8f#spring-saml[extension project].\nIn 2019, the process began to port that into https://github.com/spring-projects/spring-security[Spring Security] proper.\nThis process is similar to the one started in 2017 for xref:servlet/oauth2/index.adoc[Spring Security's OAuth 2.0 support].\n\n[NOTE]\n====\nA working sample for {gh-samples-url}/servlet/spring-boot/java/saml2/login[SAML 2.0 Login] is available in the {gh-samples-url}[Spring Security Samples repository].\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc",
    "content": "= SAML 2.0 Login Overview\n:figures: servlet/saml2\n:icondir: icons\n\nWe start by examining how SAML 2.0 Relying Party Authentication works within Spring Security.\nFirst, we see that, like <<oauth2login, OAuth 2.0 Login>>, Spring Security takes the user to a third party for performing authentication.\nIt does this through a series of redirects:\n\n.Redirecting to Asserting Party Authentication\n[.invert-dark]\nimage::{figures}/saml2webssoauthenticationrequestfilter.png[]\n\n[NOTE]\n====\nThe figure above builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] and xref:servlet/authentication/architecture.adoc#servlet-authentication-abstractprocessingfilter[`AbstractAuthenticationProcessingFilter`] diagrams:\n====\n\nimage:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the `/private` resource, for which it is not authorized.\n\nimage:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-http-requests.adoc[`AuthorizationFilter`] indicates that the unauthenticated request is _Denied_ by throwing an `AccessDeniedException`.\n\nimage:{icondir}/number_3.png[] Since the user lacks authorization, the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates _Start Authentication_.\nThe configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of javadoc:org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint[], which redirects to <<servlet-saml2login-sp-initiated-factory,the `<saml2:AuthnRequest>` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`.\nAlternatively, if you have <<servlet-saml2login-relyingpartyregistrationrepository,configured more than one asserting party>>, it first redirects to a picker page.\n\nimage:{icondir}/number_4.png[] Next, the `Saml2WebSsoAuthenticationRequestFilter` creates, signs, serializes, and encodes a `<saml2:AuthnRequest>` using its configured <<servlet-saml2login-sp-initiated-factory,`Saml2AuthenticationRequestFactory`>>.\n\nimage:{icondir}/number_5.png[] Then the browser takes this `<saml2:AuthnRequest>` and presents it to the asserting party.\nThe asserting party tries to authentication the user.\nIf successful, it returns a `<saml2:Response>` back to the browser.\n\nimage:{icondir}/number_6.png[] The browser then POSTs the `<saml2:Response>` to the assertion consumer service endpoint.\n\nThe following image shows how Spring Security authenticates a `<saml2:Response>`.\n\n[[servlet-saml2login-authentication-saml2webssoauthenticationfilter]]\n.Authenticating a `<saml2:Response>`\n[.invert-dark]\nimage::{figures}/saml2webssoauthenticationfilter.png[]\n\n[NOTE]\n====\nThe figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.\n====\n\n[[servlet-saml2login-authentication-saml2authenticationtokenconverter]]\nimage:{icondir}/number_1.png[] When the browser submits a `<saml2:Response>` to the application, it xref:servlet/saml2/login/authentication.adoc#servlet-saml2login-authenticate-responses[delegates to `Saml2WebSsoAuthenticationFilter`].\nThis filter calls its configured `AuthenticationConverter` to create a `Saml2AuthenticationToken` by extracting the response from the `HttpServletRequest`.\nThis converter additionally resolves the <<servlet-saml2login-relyingpartyregistration, `RelyingPartyRegistration`>> and supplies it to `Saml2AuthenticationToken`.\n\nimage:{icondir}/number_2.png[] Next, the filter passes the token to its configured xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`AuthenticationManager`].\nBy default, it uses the <<servlet-saml2login-architecture,`OpenSaml5AuthenticationProvider`>>.\n\nimage:{icondir}/number_3.png[] If authentication fails, then _Failure_.\n\n* The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] is cleared out.\n* The xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is invoked to restart the authentication process.\n\nimage:{icondir}/number_4.png[] If authentication is successful, then _Success_.\n\n* The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`].\n* The `Saml2WebSsoAuthenticationFilter` invokes `FilterChain#doFilter(request,response)` to continue with the rest of the application logic.\n\n[[servlet-saml2login-minimaldependencies]]\n== Minimal Dependencies\n\nSAML 2.0 service provider support resides in `spring-security-saml2-service-provider`.\nIt builds off of the OpenSAML library, and, for that reason, you must also include the Shibboleth Maven repository in your build configuration.\nCheck https://shibboleth.atlassian.net/wiki/spaces/DEV/pages/1123844333/Use+of+Maven+Central#Publishing-to-Maven-Central[this link] for more details about why a separate repository is needed.\n\n[tabs]\n======\nMaven::\n+\n[source,xml,role=\"primary\"]\n----\n<repositories>\n    <!-- ... -->\n    <repository>\n        <id>shibboleth-releases</id>\n        <name>Shibboleth Releases Repository</name>\n        <url>https://build.shibboleth.net/maven/releases/</url>\n        <snapshots>\n            <enabled>false</enabled>\n        </snapshots>\n    </repository>\n</repositories>\n<dependency>\n    <groupId>org.springframework.security</groupId>\n    <artifactId>spring-security-saml2-service-provider</artifactId>\n</dependency>\n----\n\nGradle::\n+\n[source,groovy,role=\"secondary\"]\n----\nrepositories {\n    // ...\n    maven { url \"https://build.shibboleth.net/nexus/content/repositories/releases/\" }\n}\ndependencies {\n    // ...\n    implementation 'org.springframework.security:spring-security-saml2-service-provider'\n}\n----\n======\n\n[[servlet-saml2login-minimalconfiguration]]\n== Minimal Configuration\n\nWhen using https://spring.io/projects/spring-boot[Spring Boot], configuring an application as a service provider consists of two basic steps:\n. Include the needed dependencies.\n. Indicate the necessary asserting party metadata.\n\n[NOTE]\nAlso, this configuration presupposes that you have already xref:servlet/saml2/metadata.adoc#servlet-saml2login-metadata[registered the relying party with your asserting party].\n\n[[saml2-specifying-identity-provider-metadata]]\n=== Specifying Identity Provider Metadata\n\nIn a Spring Boot application, to specify an identity provider's metadata, create configuration similar to the following:\n\n[source,yml]\n----\nspring:\n  security:\n    saml2:\n      relyingparty:\n        registration:\n          adfs:\n            assertingparty:\n              entity-id: https://idp.example.com/issuer\n              verification.credentials:\n                - certificate-location: \"classpath:idp.crt\"\n              singlesignon.url: https://idp.example.com/issuer/sso\n              singlesignon.sign-request: false\n----\n\nwhere:\n\n* `https://idp.example.com/issuer` is the value contained in the `Issuer` attribute of the SAML responses that the identity provider issues.\n* `classpath:idp.crt` is the location on the classpath for the identity provider's certificate for verifying SAML responses.\n* `https://idp.example.com/issuer/sso` is the endpoint where the identity provider is expecting `AuthnRequest` instances.\n* `adfs` is <<servlet-saml2login-relyingpartyregistrationid, an arbitrary identifier you choose>>\n\nAnd that's it!\n\n[NOTE]\n====\nIdentity Provider and Asserting Party are synonymous, as are Service Provider and Relying Party.\nThese are frequently abbreviated as AP and RP, respectively.\n====\n\n=== Runtime Expectations\n\nAs configured <<saml2-specifying-identity-provider-metadata,earlier>>, the application processes any `+POST /login/saml2/sso/{registrationId}+` request containing a `SAMLResponse` parameter:\n\n[source,http]\n----\nPOST /login/saml2/sso/adfs HTTP/1.1\n\nSAMLResponse=PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZ...\n----\n\nThere are two ways to induce your asserting party to generate a `SAMLResponse`:\n\n* You can navigate to your asserting party.\nIt likely has some kind of link or button for each registered relying party that you can click to send the `SAMLResponse`.\n* You can navigate to a protected page in your application -- for example, `http://localhost:8080`.\nYour application then redirects to the configured asserting party, which then sends the `SAMLResponse`.\n\nFrom here, consider jumping to:\n\n* <<servlet-saml2login-architecture,How SAML 2.0 Login Integrates with OpenSAML>>\n* xref:servlet/saml2/login/authentication.adoc#servlet-saml2login-authenticatedprincipal[How to Use the `Saml2AuthenticatedPrincipal`]\n* <<servlet-saml2login-sansboot,How to Override or Replace Spring Boot's Auto Configuration>>\n\n[[servlet-saml2login-architecture]]\n== How SAML 2.0 Login Integrates with OpenSAML\n\nSpring Security's SAML 2.0 support has a couple of design goals:\n\n* Rely on a library for SAML 2.0 operations and domain objects.\nTo achieve this, Spring Security uses OpenSAML.\n* Ensure that this library is not required when using Spring Security's SAML support.\nTo achieve this, any interfaces or classes where Spring Security uses OpenSAML in the contract remain encapsulated.\nThis makes it possible for you to switch out OpenSAML for some other library or an unsupported version of OpenSAML.\n\nAs a natural outcome of these two goals, Spring Security's SAML API is quite small relative to other modules.\nInstead, such classes as `OpenSamlXAuthenticationRequestFactory` and `OpenSamlXAuthenticationProvider` expose `Converter` implementations that customize various steps in the authentication process.\n\nFor example, once your application receives a `SAMLResponse` and delegates to `Saml2WebSsoAuthenticationFilter`, the filter delegates to `OpenSamlXAuthenticationProvider`:\n\n.Authenticating an OpenSAML `Response`\nimage:{figures}/opensamlauthenticationprovider.png[]\n\nThis figure builds off of the <<servlet-saml2login-authentication-saml2webssoauthenticationfilter,`Saml2WebSsoAuthenticationFilter` diagram>>.\n\nimage:{icondir}/number_1.png[] The `Saml2WebSsoAuthenticationFilter` formulates the `Saml2AuthenticationToken` and invokes the xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`AuthenticationManager`].\n\nimage:{icondir}/number_2.png[] The xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`AuthenticationManager`] invokes the OpenSAML authentication provider.\n\nimage:{icondir}/number_3.png[] The authentication provider deserializes the response into an OpenSAML `Response` and checks its signature.\nIf the signature is invalid, authentication fails.\n\nimage:{icondir}/number_4.png[] Then the provider xref:servlet/saml2/login/authentication.adoc#servlet-saml2login-opensamlauthenticationprovider-decryption[decrypts any `EncryptedAssertion` elements].\nIf any decryptions fail, authentication fails.\n\nimage:{icondir}/number_5.png[] Next, the provider validates the response's `Issuer` and `Destination` values.\nIf they do not match what's in the `RelyingPartyRegistration`, authentication fails.\n\nimage:{icondir}/number_6.png[] After that, the provider verifies the signature of each `Assertion`.\nIf any signature is invalid, authentication fails.\nAlso, if neither the response nor the assertions have signatures, authentication fails.\nEither the response or all the assertions must have signatures.\n\nimage:{icondir}/number_7.png[] Then, the provider xref:servlet/saml2/login/authentication.adoc#servlet-saml2login-opensamlauthenticationprovider-decryption[decrypts any `EncryptedID` or `EncryptedAttribute` elements].\nIf any decryptions fail, authentication fails.\n\nimage:{icondir}/number_8.png[] Next, the provider validates each assertion's `ExpiresAt` and `NotBefore` timestamps, the `<Subject>` and any `<AudienceRestriction>` conditions.\nIf any validations fail, authentication fails.\n\nimage:{icondir}/number_9.png[] Following that, the provider takes the first assertion's `AttributeStatement` and maps it to a `Map<String, List<Object>>`.\nIt also grants the `FACTOR_SAML_RESPONSE` and `ROLE_USER` granted authorities.\n\nimage:{icondir}/number_10.png[] And finally, it takes the `NameID` from the first assertion, the `Map` of attributes, and the `GrantedAuthority` and constructs a `Saml2AuthenticatedPrincipal`.\nThen, it places that principal and the authorities into a `Saml2Authentication`.\n\nThe resulting `Authentication#getPrincipal` is a Spring Security `Saml2AuthenticatedPrincipal` object, and `Authentication#getName` maps to the first assertion's `NameID` element.\n`Saml2AuthenticatedPrincipal#getRelyingPartyRegistrationId` holds the <<servlet-saml2login-relyingpartyregistrationid,identifier to the associated `RelyingPartyRegistration`>>.\n\n[[servlet-saml2login-opensaml-customization]]\n=== Customizing OpenSAML Configuration\n\nAny class that uses both Spring Security and OpenSAML should statically initialize `OpenSamlInitializationService` at the beginning of the class:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nstatic {\n\tOpenSamlInitializationService.initialize();\n}\n----\n\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ncompanion object {\n    init {\n        OpenSamlInitializationService.initialize()\n    }\n}\n----\n======\n\nThis replaces OpenSAML's `InitializationService#initialize`.\n\nOccasionally, it can be valuable to customize how OpenSAML builds, marshalls, and unmarshalls SAML objects.\nIn these circumstances, you may instead want to call `OpenSamlInitializationService#requireInitialize(Consumer)` that gives you access to OpenSAML's `XMLObjectProviderFactory`.\n\nFor example, when sending an unsigned AuthNRequest, you may want to force reauthentication.\nIn that case, you can register your own `AuthnRequestMarshaller`, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nstatic {\n    OpenSamlInitializationService.requireInitialize(factory -> {\n        AuthnRequestMarshaller marshaller = new AuthnRequestMarshaller() {\n            @Override\n            public Element marshall(XMLObject object, Element element) throws MarshallingException {\n                configureAuthnRequest((AuthnRequest) object);\n                return super.marshall(object, element);\n            }\n\n            public Element marshall(XMLObject object, Document document) throws MarshallingException {\n                configureAuthnRequest((AuthnRequest) object);\n                return super.marshall(object, document);\n            }\n\n            private void configureAuthnRequest(AuthnRequest authnRequest) {\n                authnRequest.setForceAuthn(true);\n            }\n        }\n\n        factory.getMarshallerFactory().registerMarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME, marshaller);\n    });\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\ncompanion object {\n    init {\n        OpenSamlInitializationService.requireInitialize {\n            val marshaller = object : AuthnRequestMarshaller() {\n                override fun marshall(xmlObject: XMLObject, element: Element): Element {\n                    configureAuthnRequest(xmlObject as AuthnRequest)\n                    return super.marshall(xmlObject, element)\n                }\n\n                override fun marshall(xmlObject: XMLObject, document: Document): Element {\n                    configureAuthnRequest(xmlObject as AuthnRequest)\n                    return super.marshall(xmlObject, document)\n                }\n\n                private fun configureAuthnRequest(authnRequest: AuthnRequest) {\n                    authnRequest.isForceAuthn = true\n                }\n            }\n            it.marshallerFactory.registerMarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME, marshaller)\n        }\n    }\n}\n----\n======\n\nThe `requireInitialize` method may be called only once per application instance.\n\n[[servlet-saml2login-sansboot]]\n== Overriding or Replacing Boot Auto Configuration\n\nSpring Boot generates two `@Bean` objects for a relying party.\n\nThe first is a `SecurityFilterChain` that configures the application as a relying party.\nWhen including `spring-security-saml2-service-provider`, the `SecurityFilterChain` looks like:\n\n.Default SAML 2.0 Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n    http\n        .authorizeHttpRequests((authorize) -> authorize\n            .anyRequest().authenticated()\n        )\n        .saml2Login(withDefaults());\n    return http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n    http {\n        authorizeHttpRequests {\n            authorize(anyRequest, authenticated)\n        }\n        saml2Login { }\n    }\n    return http.build()\n}\n----\n======\n\nIf the application does not expose a `SecurityFilterChain` bean, Spring Boot exposes the preceding default one.\n\nYou can replace this by exposing the bean within the application:\n\n.Custom SAML 2.0 Login Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class MyCustomSecurityConfiguration {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .requestMatchers(\"/messages/**\").hasAuthority(\"ROLE_USER\")\n                .anyRequest().authenticated()\n            )\n            .saml2Login(withDefaults());\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass MyCustomSecurityConfiguration {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(\"/messages/**\", hasAuthority(\"ROLE_USER\"))\n                authorize(anyRequest, authenticated)\n            }\n            saml2Login {\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\nThe preceding example requires the role of `USER` for any URL that starts with `/messages/`.\n\n[[servlet-saml2login-relyingpartyregistrationrepository]]\nThe second `@Bean` Spring Boot creates is a javadoc:org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository[], which represents the asserting party and relying party metadata.\nThis includes such things as the location of the SSO endpoint the relying party should use when requesting authentication from the asserting party.\n\nYou can override the default by publishing your own `RelyingPartyRegistrationRepository` bean.\nFor example, you can look up the asserting party's configuration by hitting its metadata endpoint:\n\n.Relying Party Registration Repository\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Value(\"${metadata.location}\")\nString assertingPartyMetadataLocation;\n\n@Bean\npublic RelyingPartyRegistrationRepository relyingPartyRegistrations() {\n    RelyingPartyRegistration registration = RelyingPartyRegistrations\n            .fromMetadataLocation(assertingPartyMetadataLocation)\n            .registrationId(\"example\")\n            .build();\n    return new InMemoryRelyingPartyRegistrationRepository(registration);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Value(\"\\${metadata.location}\")\nvar assertingPartyMetadataLocation: String? = null\n\n@Bean\nopen fun relyingPartyRegistrations(): RelyingPartyRegistrationRepository? {\n    val registration = RelyingPartyRegistrations\n        .fromMetadataLocation(assertingPartyMetadataLocation)\n        .registrationId(\"example\")\n        .build()\n    return InMemoryRelyingPartyRegistrationRepository(registration)\n}\n----\n======\n\n[[servlet-saml2login-relyingpartyregistrationid]]\n[NOTE]\nThe `registrationId` is an arbitrary value that you choose for differentiating between registrations.\n\nAlternatively, you can provide each detail manually:\n\n.Relying Party Registration Repository Manual Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Value(\"${verification.key}\")\nFile verificationKey;\n\n@Bean\npublic RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {\n    X509Certificate certificate = X509Support.decodeCertificate(this.verificationKey);\n    Saml2X509Credential credential = Saml2X509Credential.verification(certificate);\n    RelyingPartyRegistration registration = RelyingPartyRegistration\n            .withRegistrationId(\"example\")\n            .assertingPartyMetadata((party) -> party\n                .entityId(\"https://idp.example.com/issuer\")\n                .singleSignOnServiceLocation(\"https://idp.example.com/SSO.saml2\")\n                .wantAuthnRequestsSigned(false)\n                .verificationX509Credentials((c) -> c.add(credential))\n            )\n            .build();\n    return new InMemoryRelyingPartyRegistrationRepository(registration);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Value(\"\\${verification.key}\")\nvar verificationKey: File? = null\n\n@Bean\nopen fun relyingPartyRegistrations(): RelyingPartyRegistrationRepository {\n    val certificate: X509Certificate? = X509Support.decodeCertificate(verificationKey!!)\n    val credential: Saml2X509Credential = Saml2X509Credential.verification(certificate)\n    val registration = RelyingPartyRegistration\n        .withRegistrationId(\"example\")\n        .assertingPartyMetadata { party: AssertingPartyMetadata.Builder ->\n            party\n                .entityId(\"https://idp.example.com/issuer\")\n                .singleSignOnServiceLocation(\"https://idp.example.com/SSO.saml2\")\n                .wantAuthnRequestsSigned(false)\n                .verificationX509Credentials { c: MutableCollection<Saml2X509Credential?> ->\n                    c.add(\n                        credential\n                    )\n                }\n        }\n        .build()\n    return InMemoryRelyingPartyRegistrationRepository(registration)\n}\n----\n======\n\n[NOTE]\n====\n`X509Support` is an OpenSAML class, used in the preceding snippet for brevity.\n====\n\n\n[[servlet-saml2login-relyingpartyregistrationrepository-dsl]]\nAlternatively, you can directly wire up the repository by using the DSL, which also overrides the auto-configured `SecurityFilterChain`:\n\n.Custom Relying Party Registration DSL\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class MyCustomSecurityConfiguration {\n    @Bean\n    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .requestMatchers(\"/messages/**\").hasAuthority(\"ROLE_USER\")\n                .anyRequest().authenticated()\n            )\n            .saml2Login((saml2) -> saml2\n                .relyingPartyRegistrationRepository(relyingPartyRegistrations())\n            );\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass MyCustomSecurityConfiguration {\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(\"/messages/**\", hasAuthority(\"ROLE_USER\"))\n                authorize(anyRequest, authenticated)\n            }\n            saml2Login {\n                relyingPartyRegistrationRepository = relyingPartyRegistrations()\n            }\n        }\n        return http.build()\n    }\n}\n----\n======\n\n[NOTE]\n====\nA relying party can be multi-tenant by registering more than one relying party in the `RelyingPartyRegistrationRepository`.\n====\n\n[[servlet-saml2login-relyingpartyregistrationrepository-caching]]\nIf you want your metadata to be refreshable on a periodic basis, you can wrap your repository in `CachingRelyingPartyRegistrationRepository` like so:\n\n.Caching Relying Party Registration Repository\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\n@EnableWebSecurity\npublic class MyCustomSecurityConfiguration {\n    @Bean\n    public RelyingPartyRegistrationRepository registrations(CacheManager cacheManager) {\n\t\tSupplier<IterableRelyingPartyRegistrationRepository> delegate = () ->\n            new InMemoryRelyingPartyRegistrationRepository(RelyingPartyRegistrations\n                .fromMetadataLocation(\"https://idp.example.org/ap/metadata\")\n                .registrationId(\"ap\").build());\n\t\tCachingRelyingPartyRegistrationRepository registrations =\n            new CachingRelyingPartyRegistrationRepository(delegate);\n\t\tregistrations.setCache(cacheManager.getCache(\"my-cache-name\"));\n        return registrations;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\n@EnableWebSecurity\nclass MyCustomSecurityConfiguration  {\n    @Bean\n    fun registrations(cacheManager: CacheManager): RelyingPartyRegistrationRepository {\n        val delegate = Supplier<IterableRelyingPartyRegistrationRepository> {\n             InMemoryRelyingPartyRegistrationRepository(RelyingPartyRegistrations\n                .fromMetadataLocation(\"https://idp.example.org/ap/metadata\")\n                .registrationId(\"ap\").build())\n        }\n        val registrations = CachingRelyingPartyRegistrationRepository(delegate)\n        registrations.setCache(cacheManager.getCache(\"my-cache-name\"))\n        return registrations\n    }\n}\n----\n======\n\nIn this way, the set of ``RelyingPartyRegistration``s will refresh based on {spring-framework-reference-url}integration/cache/store-configuration.html[the cache's eviction schedule].\n\n[[servlet-saml2login-relyingpartyregistration]]\n== RelyingPartyRegistration\nA javadoc:org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration[]\ninstance represents a link between an relying party and an asserting party's metadata.\n\nIn a `RelyingPartyRegistration`, you can provide relying party metadata like its `Issuer` value, where it expects SAML Responses to be sent to, and any credentials that it owns for the purposes of signing or decrypting payloads.\n\nAlso, you can provide asserting party metadata like its `Issuer` value, where it expects AuthnRequests to be sent to, and any public credentials that it owns for the purposes of the relying party verifying or encrypting payloads.\n\nThe following `RelyingPartyRegistration` is the minimum required for most setups:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nRelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations\n        .fromMetadataLocation(\"https://ap.example.org/metadata\")\n        .registrationId(\"my-id\")\n        .build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval relyingPartyRegistration = RelyingPartyRegistrations\n    .fromMetadataLocation(\"https://ap.example.org/metadata\")\n    .registrationId(\"my-id\")\n    .build()\n----\n======\n\nNote that you can also create a `RelyingPartyRegistration` from an arbitrary `InputStream` source.\nOne such example is when the metadata is stored in a database:\n\n[source,java]\n----\nString xml = fromDatabase();\ntry (InputStream source = new ByteArrayInputStream(xml.getBytes())) {\n    RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations\n            .fromMetadata(source)\n            .registrationId(\"my-id\")\n            .build();\n}\n----\n\nA more sophisticated setup is also possible:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nRelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId(\"my-id\")\n        .entityId(\"{baseUrl}/{registrationId}\")\n        .decryptionX509Credentials((c) -> c.add(relyingPartyDecryptingCredential()))\n        .assertionConsumerServiceLocation(\"/my-login-endpoint/{registrationId}\")\n        .assertingPartyMetadata((party) -> party\n                .entityId(\"https://ap.example.org\")\n                .verificationX509Credentials((c) -> c.add(assertingPartyVerifyingCredential()))\n                .singleSignOnServiceLocation(\"https://ap.example.org/SSO.saml2\")\n        )\n        .build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval relyingPartyRegistration =\n    RelyingPartyRegistration.withRegistrationId(\"my-id\")\n        .entityId(\"{baseUrl}/{registrationId}\")\n        .decryptionX509Credentials { c: MutableCollection<Saml2X509Credential?> ->\n            c.add(relyingPartyDecryptingCredential())\n        }\n        .assertionConsumerServiceLocation(\"/my-login-endpoint/{registrationId}\")\n        .assertingPartyMetadata { party -> party\n                .entityId(\"https://ap.example.org\")\n                .verificationX509Credentials { c -> c.add(assertingPartyVerifyingCredential()) }\n                .singleSignOnServiceLocation(\"https://ap.example.org/SSO.saml2\")\n        }\n        .build()\n----\n======\n\n[TIP]\n====\nThe top-level metadata methods are details about the relying party.\nThe methods inside `AssertingPartyMetadata` are details about the asserting party.\n====\n\n[NOTE]\n====\nThe location where a relying party is expecting SAML Responses is the Assertion Consumer Service Location.\n====\n\nThe default for the relying party's `entityId` is `+{baseUrl}/saml2/service-provider-metadata/{registrationId}+`.\nThis is this value needed when configuring the asserting party to know about your relying party.\n\nThe default for the `assertionConsumerServiceLocation` is `+/login/saml2/sso/{registrationId}+`.\nBy default, it is mapped to <<servlet-saml2login-authentication-saml2webssoauthenticationfilter,`Saml2WebSsoAuthenticationFilter`>> in the filter chain.\n\n[[servlet-saml2login-rpr-uripatterns]]\n=== URI Patterns\n\nYou probably noticed the `+{baseUrl}+` and `+{registrationId}+` placeholders in the preceding examples.\n\nThese are useful for generating URIs. As a result, the relying party's `entityId` and `assertionConsumerServiceLocation` support the following placeholders:\n\n* `baseUrl` - the scheme, host, and port of a deployed application\n* `registrationId` - the registration id for this relying party\n* `baseScheme` - the scheme of a deployed application\n* `baseHost` - the host of a deployed application\n* `basePort` - the port of a deployed application\n\nFor example, the `assertionConsumerServiceLocation` defined earlier was:\n\n`+/my-login-endpoint/{registrationId}+`\n\nIn a deployed application, it translates to:\n\n`+/my-login-endpoint/adfs+`\n\nThe `entityId` shown earlier was defined as:\n\n`+{baseUrl}/{registrationId}+`\n\nIn a deployed application, that translates to:\n\n`+https://rp.example.com/adfs+`\n\nThe prevailing URI patterns are as follows:\n\n* `+/saml2/authenticate/{registrationId}+` - The endpoint that xref:servlet/saml2/login/authentication-requests.adoc[generates a `<saml2:AuthnRequest>`] based on the configurations for that `RelyingPartyRegistration` and sends it to the asserting party\n* `+/login/saml2/sso/+` - The endpoint that xref:servlet/saml2/login/authentication.adoc[authenticates an asserting party's `<saml2:Response>`]; the `RelyingPartyRegistration` is looked up from previously authenticated state or the response's issuer if needed; also supports `+/login/saml2/sso/{registrationId}+`\n* `+/logout/saml2/sso+` - The endpoint that xref:servlet/saml2/logout.adoc[processes `<saml2:LogoutRequest>` and `<saml2:LogoutResponse>` payloads]; the `RelyingPartyRegistration` is looked up from previously authenticated state or the request's issuer if needed; also supports `+/logout/saml2/slo/{registrationId}+`\n* `+/saml2/metadata+` - The xref:servlet/saml2/metadata.adoc[relying party metadata] for the set of ``RelyingPartyRegistration``s; also supports `+/saml2/metadata/{registrationId}+` or `+/saml2/service-provider-metadata/{registrationId}+` for a specific `RelyingPartyRegistration`\n\nSince the `registrationId` is the primary identifier for a `RelyingPartyRegistration`, it is needed in the URL for unauthenticated scenarios.\nIf you wish to remove the `registrationId` from the URL for any reason, you can <<servlet-saml2login-rpr-relyingpartyregistrationresolver,specify a `RelyingPartyRegistrationResolver`>> to tell Spring Security how to look up the `registrationId`.\n\n[[servlet-saml2login-rpr-credentials]]\n=== Credentials\n\nIn the example shown <<servlet-saml2login-relyingpartyregistration,earlier>>, you also likely noticed the credential that was used.\n\nOftentimes, a relying party uses the same key to sign payloads as well as decrypt them.\nAlternatively, it can use the same key to verify payloads as well as encrypt them.\n\nBecause of this, Spring Security ships with `Saml2X509Credential`, a SAML-specific credential that simplifies configuring the same key for different use cases.\n\nAt a minimum, you need to have a certificate from the asserting party so that the asserting party's signed responses can be verified.\n\nTo construct a `Saml2X509Credential` that you can use to verify assertions from the asserting party, you can load the file and use\nthe `CertificateFactory`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nResource resource = new ClassPathResource(\"ap.crt\");\ntry (InputStream is = resource.getInputStream()) {\n    X509Certificate certificate = (X509Certificate)\n            CertificateFactory.getInstance(\"X.509\").generateCertificate(is);\n    return Saml2X509Credential.verification(certificate);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval resource = ClassPathResource(\"ap.crt\")\nresource.inputStream.use {\n    return Saml2X509Credential.verification(\n        CertificateFactory.getInstance(\"X.509\").generateCertificate(it) as X509Certificate?\n    )\n}\n----\n======\n\nSuppose that the asserting party is going to also encrypt the assertion.\nIn that case, the relying party needs a private key to decrypt the encrypted value.\n\nIn that case, you need an `RSAPrivateKey` as well as its corresponding `X509Certificate`.\nYou can load the first by using Spring Security's `RsaKeyConverters` utility class and the second as you did before:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nX509Certificate certificate = relyingPartyDecryptionCertificate();\nResource resource = new ClassPathResource(\"rp.crt\");\ntry (InputStream is = resource.getInputStream()) {\n    RSAPrivateKey rsa = RsaKeyConverters.pkcs8().convert(is);\n    return Saml2X509Credential.decryption(rsa, certificate);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval certificate: X509Certificate = relyingPartyDecryptionCertificate()\nval resource = ClassPathResource(\"rp.crt\")\nresource.inputStream.use {\n    val rsa: RSAPrivateKey = RsaKeyConverters.pkcs8().convert(it)\n    return Saml2X509Credential.decryption(rsa, certificate)\n}\n----\n======\n\n[TIP]\n====\nWhen you specify the locations of these files as the appropriate Spring Boot properties, Spring Boot performs these conversions for you.\n====\n\n[[servlet-saml2login-rpr-duplicated]]\n=== Duplicated Relying Party Configurations\n\nWhen an application uses multiple asserting parties, some configuration is duplicated between `RelyingPartyRegistration` instances:\n\n* The relying party's `entityId`\n* Its `assertionConsumerServiceLocation`\n* Its credentials -- for example, its signing or decryption credentials\n\nThis setup may let credentials be more easily rotated for some identity providers versus others.\n\nThe duplication can be alleviated in a few different ways.\n\nFirst, in YAML this can be alleviated with references:\n\n[source,yaml]\n----\nspring:\n  security:\n    saml2:\n      relyingparty:\n        registration:\n          okta:\n            signing.credentials: &relying-party-credentials\n              - private-key-location: classpath:rp.key\n                certificate-location: classpath:rp.crt\n            assertingparty:\n              entity-id: ...\n          azure:\n            signing.credentials: *relying-party-credentials\n            assertingparty:\n              entity-id: ...\n----\n\nSecond, in a database, you need not replicate the model of `RelyingPartyRegistration`.\n\nThird, in Java, you can create a custom configuration method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nprivate RelyingPartyRegistration.Builder\n        addRelyingPartyDetails(RelyingPartyRegistration.Builder builder) {\n\n    Saml2X509Credential signingCredential = ...\n    builder.signingX509Credentials((c) -> c.addAll(signingCredential));\n    // ... other relying party configurations\n}\n\n@Bean\npublic RelyingPartyRegistrationRepository relyingPartyRegistrations() {\n    RelyingPartyRegistration okta = addRelyingPartyDetails(\n            RelyingPartyRegistrations\n                .fromMetadataLocation(oktaMetadataUrl)\n                .registrationId(\"okta\")).build();\n\n    RelyingPartyRegistration azure = addRelyingPartyDetails(\n            RelyingPartyRegistrations\n                .fromMetadataLocation(oktaMetadataUrl)\n                .registrationId(\"azure\")).build();\n\n    return new InMemoryRelyingPartyRegistrationRepository(okta, azure);\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nprivate fun addRelyingPartyDetails(builder: RelyingPartyRegistration.Builder): RelyingPartyRegistration.Builder {\n    val signingCredential: Saml2X509Credential = ...\n    builder.signingX509Credentials { c: MutableCollection<Saml2X509Credential?> ->\n        c.add(\n            signingCredential\n        )\n    }\n    // ... other relying party configurations\n}\n\n@Bean\nopen fun relyingPartyRegistrations(): RelyingPartyRegistrationRepository? {\n    val okta = addRelyingPartyDetails(\n        RelyingPartyRegistrations\n            .fromMetadataLocation(oktaMetadataUrl)\n            .registrationId(\"okta\")\n    ).build()\n    val azure = addRelyingPartyDetails(\n        RelyingPartyRegistrations\n            .fromMetadataLocation(oktaMetadataUrl)\n            .registrationId(\"azure\")\n    ).build()\n    return InMemoryRelyingPartyRegistrationRepository(okta, azure)\n}\n----\n======\n\n[[servlet-saml2login-rpr-relyingpartyregistrationresolver]]\n=== Resolving the `RelyingPartyRegistration` from the Request\n\nAs seen so far, Spring Security resolves the `RelyingPartyRegistration` by looking for the registration id in the URI path.\n\nDepending on the use case, a number of other strategies are also employed to derive one.\nFor example:\n\n* For processing ``<saml2:Response>``s, the `RelyingPartyRegistration` is looked up from the associated `<saml2:AuthRequest>` or from the `<saml2:Response#Issuer>` element\n* For processing ``<saml2:LogoutRequest>``s, the `RelyingPartyRegistration` is looked up from the currently logged in user or from the `<saml2:LogoutRequest#Issuer>` element\n* For publishing metadata, the ``RelyingPartyRegistration``s are looked up from any repository that also implements `Iterable<RelyingPartyRegistration>`\n\nWhen this needs adjustment, you can turn to the specific components for each of these endpoints targeted at customizing this:\n\n* For SAML Responses, customize the `AuthenticationConverter`\n* For Logout Requests, customize the `Saml2LogoutRequestValidatorParametersResolver`\n* For Metadata, customize the `Saml2MetadataResponseResolver`\n\n[[federating-saml2-login]]\n=== Federating Login\n\nOne common arrangement with SAML 2.0 is an identity provider that has multiple asserting parties.\nIn this case, the identity provider's metadata endpoint returns multiple `<md:IDPSSODescriptor>` elements.\n\nThese multiple asserting parties can be accessed in a single call to `RelyingPartyRegistrations` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nCollection<RelyingPartyRegistration> registrations = RelyingPartyRegistrations\n        .collectionFromMetadataLocation(\"https://example.org/saml2/idp/metadata.xml\")\n        .stream().map((builder) -> builder\n            .registrationId(UUID.randomUUID().toString())\n            .entityId(\"https://example.org/saml2/sp\")\n            .build()\n        )\n        .collect(Collectors.toList());\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nvar registrations: Collection<RelyingPartyRegistration> = RelyingPartyRegistrations\n        .collectionFromMetadataLocation(\"https://example.org/saml2/idp/metadata.xml\")\n        .stream().map { builder : RelyingPartyRegistration.Builder -> builder\n            .registrationId(UUID.randomUUID().toString())\n            .entityId(\"https://example.org/saml2/sp\")\n            .assertionConsumerServiceLocation(\"{baseUrl}/login/saml2/sso\")\n            .build()\n        }\n        .collect(Collectors.toList())\n----\n======\n\nNote that because the registration id is set to a random value, this will change certain SAML 2.0 endpoints to be unpredictable.\nThere are several ways to address this; let's focus on a way that suits the specific use case of federation.\n\nIn many federation cases, all the asserting parties share service provider configuration.\nGiven that Spring Security will by default include the `registrationId` in the service provider metadata, another step is to change corresponding URIs to exclude the `registrationId`, which you can see has already been done in the above sample where the `entityId` and `assertionConsumerServiceLocation` are configured with a static endpoint.\n\nYou can see a completed example of this in {gh-samples-url}/servlet/spring-boot/java/saml2/saml-extension-federation[our `saml-extension-federation` sample].\n\n[[using-spring-security-saml-extension-uris]]\n=== Using Spring Security SAML Extension URIs\n\nIn the event that you are migrating from the Spring Security SAML Extension, there may be some benefit to configuring your application to use the SAML Extension URI defaults.\n\nFor more information on this, please see {gh-samples-url}/servlet/spring-boot/java/saml2/saml-extension-urls[our `saml-extension-urls` sample] and {gh-samples-url}/servlet/spring-boot/java/saml2/saml-extension-federation[our `saml-extension-federation` sample].\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/saml2/logout.adoc",
    "content": "[[servlet-saml2login-logout]]\n= Performing Single Logout\n\nAmong its xref:servlet/authentication/logout.adoc[other logout mechanisms], Spring Security ships with support for RP- and AP-initiated SAML 2.0 Single Logout.\n\nBriefly, there are two use cases Spring Security supports:\n\n* **RP-Initiated** - Your application has an endpoint that, when POSTed to, will logout the user and send a `saml2:LogoutRequest` to the asserting party.\nThereafter, the asserting party will send back a `saml2:LogoutResponse` and allow your application to respond\n* **AP-Initiated** - Your application has an endpoint that will receive a `saml2:LogoutRequest` from the asserting party.\nYour application will complete its logout at that point and then send a `saml2:LogoutResponse` to the asserting party.\n\n[NOTE]\nIn the **AP-Initiated** scenario, any local redirection that your application would do post-logout is rendered moot.\nOnce your application sends a `saml2:LogoutResponse`, it no longer has control of the browser.\n\n== Minimal Configuration for Single Logout\n\nTo use Spring Security's SAML 2.0 Single Logout feature, you will need the following things:\n\n* First, the asserting party must support SAML 2.0 Single Logout\n* Second, the asserting party should be configured to sign and POST `saml2:LogoutRequest` s and `saml2:LogoutResponse` s your application's `/logout/saml2/slo` endpoint\n* Third, your application must have a PKCS#8 private key and X.509 certificate for signing `saml2:LogoutRequest` s and `saml2:LogoutResponse` s\n\nYou can achieve this in Spring Boot in the following way:\n\n[source,yaml]\n----\nspring:\n  security:\n    saml2:\n      relyingparty:\n        registration:\n          metadata:\n            signing.credentials: <3>\n              - private-key-location: classpath:credentials/rp-private.key\n                certificate-location: classpath:credentials/rp-certificate.crt\n            singlelogout.url: \"{baseUrl}/logout/saml2/slo\" <2>\n            assertingparty:\n              metadata-uri: https://ap.example.com/metadata <1>\n\n----\n<1> - The metadata URI of the IDP, which will indicate to your application its support of SLO\n<2> - The SLO endpoint in your application\n<3> - The signing credentials to sign ``<saml2:LogoutRequest>``s and ``<saml2:LogoutResponse>``s\n\n[NOTE]\n----\nAn asserting party supports Single Logout if their metadata includes the `<SingleLogoutService>` element in their metadata.\n----\n\nAnd that's it!\n\nSpring Security's logout support offers a number of configuration points.\nConsider the following use cases:\n\n* Understand how the above <<_startup_expectations, minimal configuration works>>\n* Get a picture of <<architecture, the overall architecture>>\n* Allow users to <<separating-local-saml2-logout, logout out of the app only>>\n* Customize <<_configuring_logout_endpoints, logout endpoints>>\n* Storing `<saml2:LogoutRequests>` somewhere <<_customizing_storage, other than the session>>\n\n=== Startup Expectations\n\nWhen these properties are used, in addition to login, SAML 2.0 Service Provider will automatically configure itself facilitate logout by way of ``<saml2:LogoutRequest>``s and ``<saml2:LogoutResponse>``s using either RP- or AP-initiated logout.\n\nIt achieves this through a deterministic startup process:\n\n1. Query the Identity Server Metadata endpoint for the `<SingleLogoutService>` element\n2. Scan the metadata and cache any public signature verification keys\n3. Prepare the appropriate endpoints\n\nA consequence of this process is that the identity server must be up and receiving requests in order for Service Provider to successfully start up.\n\n[NOTE]\nIf the identity server is down when Service Provider queries it (given appropriate timeouts), then startup will fail.\n\n=== Runtime Expectations\n\nGiven the above configuration any logged-in user can send a `POST /logout` to your application to perform RP-initiated SLO.\nYour application will then do the following:\n\n1. Logout the user and invalidate the session\n2. Produce a `<saml2:LogoutRequest>` and POST it to the associated asserting party's SLO endpoint\n3. Then, if the asserting party responds with a `<saml2:LogoutResponse>`, the application with verify it and redirect to the configured success endpoint\n\nAlso, your application can participate in an AP-initiated logout when the asserting party sends a `<saml2:LogoutRequest>` to `/logout/saml2/slo`.\nWhen this happens, your application will do the following:\n\n1. Verify the `<saml2:LogoutRequest>`\n2. Logout the user and invalidate the session\n3. Produce a `<saml2:LogoutResponse>` and POST it back to the asserting party's SLO endpoint\n\n== Minimal Configuration Sans Boot\n\nInstead of Boot properties, you can also achieve the same outcome by publishing the beans directly like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Configuration\npublic class SecurityConfig {\n    @Value(\"${private.key}\") RSAPrivateKey key;\n    @Value(\"${public.certificate}\") X509Certificate certificate;\n\n    @Bean\n    RelyingPartyRegistrationRepository registrations() {\n        Saml2X509Credential credential = Saml2X509Credential.signing(key, certificate);\n        RelyingPartyRegistration registration = RelyingPartyRegistrations\n                .fromMetadataLocation(\"https://ap.example.org/metadata\") <1>\n                .registrationId(\"metadata\")\n                .singleLogoutServiceLocation(\"{baseUrl}/logout/saml2/slo\") <2>\n                .signingX509Credentials((signing) -> signing.add(credential)) <3>\n                .build();\n        return new InMemoryRelyingPartyRegistrationRepository(registration);\n    }\n\n    @Bean\n    SecurityFilterChain web(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests((authorize) -> authorize\n                .anyRequest().authenticated()\n            )\n            .saml2Login(withDefaults())\n            .saml2Logout(withDefaults()); <4>\n\n        return http.build();\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Configuration\nclass SecurityConfig(@Value(\"${private.key}\") val key: RSAPrivateKey,\n        @Value(\"${public.certificate}\") val certificate: X509Certificate) {\n\n    @Bean\n    fun registrations(): RelyingPartyRegistrationRepository {\n        val credential = Saml2X509Credential.signing(key, certificate)\n        val registration = RelyingPartyRegistrations\n                .fromMetadataLocation(\"https://ap.example.org/metadata\") <1>\n                .registrationId(\"metadata\")\n                .singleLogoutServiceLocation(\"{baseUrl}/logout/saml2/slo\") <2>\n                .signingX509Credentials({ signing: List<Saml2X509Credential> -> signing.add(credential) }) <3>\n                .build()\n        return InMemoryRelyingPartyRegistrationRepository(registration)\n    }\n\n    @Bean\n    fun web(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                anyRequest = authenticated\n            }\n            saml2Login {\n\n            }\n            saml2Logout { <4>\n\n            }\n        }\n\n        return http.build()\n    }\n}\n----\n======\n<1> - The metadata URI of the IDP, which will indicate to your application its support of SLO\n<2> - The SLO endpoint in your application\n<3> - The signing credentials to sign ``<saml2:LogoutRequest>``s and ``<saml2:LogoutResponse>``s, which you can also add to xref:servlet/saml2/login/overview.adoc#servlet-saml2login-rpr-duplicated[multiple relying parties]\n<4> - Second, indicate that your application wants to use SAML SLO to logout the end user\n\n[NOTE]\nAdding `saml2Logout` adds the capability for logout to your service provider as a whole.\nBecause it is an optional capability, you need to enable it for each individual `RelyingPartyRegistration`.\nYou do this by setting the `RelyingPartyRegistration.Builder#singleLogoutServiceLocation` property as seen above.\n\n[[architecture]]\n== How Saml 2.0 Logout Works\n\nNext, let's see the architectural components that Spring Security uses to support https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf#page=37[SAML 2.0 Logout] in servlet-based applications, like the one we just saw.\n\nFor RP-initiated logout:\n\nimage:{icondir}/number_1.png[] Spring Security executes its xref:servlet/authentication/logout.adoc#logout-architecture[logout flow], calling its ``LogoutHandler``s to invalidate the session and perform other cleanup.\nIt then invokes the javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2RelyingPartyInitiatedLogoutSuccessHandler[].\n\nimage:{icondir}/number_2.png[] The logout success handler uses an instance of\njavadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver[] to create, sign, and serialize a `<saml2:LogoutRequest>`.\nIt uses the keys and configuration from the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] that is associated with the current `Saml2AuthenticatedPrincipal`.\nThen, it redirect-POSTs the `<saml2:LogoutRequest>` to the asserting party SLO endpoint\n\nThe browser hands control over to the asserting party.\nIf the asserting party redirects back (which it may not), then the application proceeds to step image:{icondir}/number_3.png[].\n\nimage:{icondir}/number_3.png[] The javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter[] deserializes, verifies, and processes the `<saml2:LogoutResponse>` with its javadoc:org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator[].\n\nimage:{icondir}/number_4.png[] If valid, then it completes the local logout flow by redirecting to `/login?logout`, or whatever has been configured.\nIf invalid, then it responds with a 400.\n\nFor AP-initiated logout:\n\nimage:{icondir}/number_1.png[] The javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter[] deserializes, verifies, and processes the `<saml2:LogoutRequest>` with its javadoc:org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator[].\n\nimage:{icondir}/number_2.png[] If valid, then the filter calls the configured ``LogoutHandler``s, invalidating the session and performing other cleanup.\n\nimage:{icondir}/number_3.png[] It uses a javadoc:org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver[] to create, sign and serialize a `<saml2:LogoutResponse>`.\nIt uses the keys and configuration from the xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistration[`RelyingPartyRegistration`] derived from the endpoint or from the contents of the `<saml2:LogoutRequest>`.\nThen, it redirect-POSTs the `<saml2:LogoutResponse>` to the asserting party SLO endpoint.\n\nThe browser hands control over to the asserting party.\n\nimage:{icondir}/number_4.png[] If invalid, then it https://github.com/spring-projects/spring-security/pull/14676[responds with a 400].\n\n== Configuring Logout Endpoints\n\nThere are three behaviors that can be triggered by different endpoints:\n\n* RP-initiated logout, which allows an authenticated user to `POST` and trigger the logout process by sending the asserting party a `<saml2:LogoutRequest>`\n* AP-initiated logout, which allows an asserting party to send a `<saml2:LogoutRequest>` to the application\n* AP logout response, which allows an asserting party to send a `<saml2:LogoutResponse>` in response to the RP-initiated `<saml2:LogoutRequest>`\n\nThe first is triggered by performing normal `POST /logout` when the principal is of type `Saml2AuthenticatedPrincipal`.\n\nThe second is triggered by POSTing to the `/logout/saml2/slo` endpoint with a `SAMLRequest` signed by the asserting party.\n\nThe third is triggered by POSTing to the `/logout/saml2/slo` endpoint with a `SAMLResponse` signed by the asserting party.\n\nBecause the user is already logged in or the original Logout Request is known, the `registrationId` is already known.\nFor this reason, `+{registrationId}+` is not part of these URLs by default.\n\nThis URL is customizable in the DSL.\n\nFor example, if you are migrating your existing relying party over to Spring Security, your asserting party may already be pointing to `GET /SLOService.saml2`.\nTo reduce changes in configuration for the asserting party, you can configure the filter in the DSL like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .saml2Logout((saml2) -> saml2\n        .logoutRequest((request) -> request.logoutUrl(\"/SLOService.saml2\"))\n        .logoutResponse((response) -> response.logoutUrl(\"/SLOService.saml2\"))\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    saml2Logout {\n        logoutRequest {\n            logoutUrl = \"/SLOService.saml2\"\n        }\n        logoutResponse {\n            logoutUrl = \"/SLOService.saml2\"\n        }\n    }\n}\n----\n======\n\nYou should also configure these endpoints in your `RelyingPartyRegistration`.\n\nAlso, you can customize the endpoint for triggering logout locally like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .saml2Logout((saml2) -> saml2.logoutUrl(\"/saml2/logout\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    saml2Logout {\n        logoutUrl = \"/saml2/logout\"\n    }\n}\n----\n======\n\n[[separating-local-saml2-logout]]\n=== Separating Local Logout from SAML 2.0 Logout\n\nIn some cases, you may want to expose one logout endpoint for local logout and another for RP-initiated SLO.\nLike is the case with other logout mechanisms, you can register more than one, so long as they each have a different endpoint.\n\nSo, for example, you can wire the DSL like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .logout((logout) -> logout.logoutUrl(\"/logout\"))\n    .saml2Logout((saml2) -> saml2.logoutUrl(\"/saml2/logout\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    logout {\n        logoutUrl = \"/logout\"\n    }\n    saml2Logout {\n        logoutUrl = \"/saml2/logout\"\n    }\n}\n----\n======\n\nand now if a client sends a `POST /logout`, the session will be cleared, but there won't be a `<saml2:LogoutRequest>` sent to the asserting party.\nBut, if the client sends a `POST /saml2/logout`, then the application will initiate SAML 2.0 SLO as normal.\n\n== Customizing `<saml2:LogoutRequest>` Resolution\n\nIt's common to need to set other values in the `<saml2:LogoutRequest>` than the defaults that Spring Security provides.\n\nBy default, Spring Security will issue a `<saml2:LogoutRequest>` and supply:\n\n* The `DestinationValidator` attribute - from `RelyingPartyRegistration#getAssertingPartyMetadata#getSingleLogoutServiceLocation`\n* The `ID` attribute - a GUID\n* The `<Issuer>` element - from `RelyingPartyRegistration#getEntityId`\n* The `<NameID>` element - from `Authentication#getName`\n\nTo add other values, you can use delegation, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSaml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationRepository registrations) {\n\tOpenSaml5LogoutRequestResolver logoutRequestResolver =\n\t\t\tnew OpenSaml5LogoutRequestResolver(registrations);\n\tlogoutRequestResolver.setParametersConsumer((parameters) -> {\n\t\tString name = ((Saml2AuthenticatedPrincipal) parameters.getAuthentication().getPrincipal()).getFirstAttribute(\"CustomAttribute\");\n\t\tString format = \"urn:oasis:names:tc:SAML:2.0:nameid-format:transient\";\n\t\tLogoutRequest logoutRequest = parameters.getLogoutRequest();\n\t\tNameID nameId = logoutRequest.getNameID();\n\t\tnameId.setValue(name);\n\t\tnameId.setFormat(format);\n\t});\n\treturn logoutRequestResolver;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun logoutRequestResolver(registrations:RelyingPartyRegistrationRepository?): Saml2LogoutRequestResolver {\n    val logoutRequestResolver = OpenSaml5LogoutRequestResolver(registrations)\n    logoutRequestResolver.setParametersConsumer { parameters: LogoutRequestParameters ->\n        val name: String = (parameters.getAuthentication().getPrincipal() as Saml2AuthenticatedPrincipal).getFirstAttribute(\"CustomAttribute\")\n        val format = \"urn:oasis:names:tc:SAML:2.0:nameid-format:transient\"\n        val logoutRequest: LogoutRequest = parameters.getLogoutRequest()\n        val nameId: NameID = logoutRequest.getNameID()\n        nameId.setValue(name)\n        nameId.setFormat(format)\n    }\n    return logoutRequestResolver\n}\n----\n======\n\nThen, you can supply your custom `Saml2LogoutRequestResolver` in the DSL as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .saml2Logout((saml2) -> saml2\n        .logoutRequest((request) -> request\n            .logoutRequestResolver(this.logoutRequestResolver)\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    saml2Logout {\n        logoutRequest {\n            logoutRequestResolver = this.logoutRequestResolver\n        }\n    }\n}\n----\n======\n\n== Customizing `<saml2:LogoutResponse>` Resolution\n\nIt's common to need to set other values in the `<saml2:LogoutResponse>` than the defaults that Spring Security provides.\n\nBy default, Spring Security will issue a `<saml2:LogoutResponse>` and supply:\n\n* The `DestinationValidator` attribute - from `RelyingPartyRegistration#getAssertingPartyMetadata#getSingleLogoutServiceResponseLocation`\n* The `ID` attribute - a GUID\n* The `<Issuer>` element - from `RelyingPartyRegistration#getEntityId`\n* The `<Status>` element - `SUCCESS`\n\nTo add other values, you can use delegation, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\npublic Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) {\n\tOpenSaml5LogoutResponseResolver resolver = \n\t\t\tnew OpenSaml5LogoutResponseResolver(registrations);\n\tresolver.setParametersConsumer((parameters) -> {\n\t\tif (checkOtherPrevailingConditions(parameters.getRequest())) {\n\t\t\tparameters.getLogoutResponse().getStatus().getStatusCode().setCode(StatusCode.PARTIAL_LOGOUT);\n\t\t}\n\t});\n\treturn resolver;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?): Saml2LogoutResponseResolver {\n    val resolver = OpenSaml5LogoutResponseResolver(registrations)\n    resolver.setParametersConsumer { parameters ->\n        if (checkOtherPrevailingConditions(parameters.request)) {\n            parameters.logoutResponse.status.statusCode.code = StatusCode.PARTIAL_LOGOUT\n        }\n    }\n    return resolver\n}\n----\n======\n\nThen, you can supply your custom `Saml2LogoutResponseResolver` in the DSL as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .saml2Logout((saml2) -> saml2\n        .logoutResponse((request) -> request\n            .logoutResponseResolver(this.logoutResponseResolver)\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    saml2Logout {\n        logoutResponse  {\n            logoutResponseResolver = logoutResponseResolver\n        }\n    }\n}\n----\n======\n\n== Customizing `<saml2:LogoutRequest>` Authentication\n\nTo customize validation, you can implement your own `Saml2LogoutRequestValidator`.\nAt this point, the validation is minimal, so you may be able to first delegate to the default `Saml2LogoutRequestValidator` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class MyOpenSamlLogoutRequestValidator implements Saml2LogoutRequestValidator {\n\tprivate final Saml2LogoutRequestValidator delegate = new OpenSaml5LogoutRequestValidator();\n\n\t@Override\n    public Saml2LogoutValidatorResult validate(Saml2LogoutRequestValidatorParameters parameters) {\n\t\t // verify signature, issuer, destination, and principal name\n\t\tSaml2LogoutValidatorResult result = delegate.validate(authentication);\n\n        if(result.hasErrors()){\n            return result;\n        }\n\t\t\n        // perform custom validation\n\n        return result;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class MyOpenSamlLogoutRequestValidator : Saml2LogoutRequestValidator {\n\tprivate val delegate = OpenSaml5LogoutRequestValidator()\n\n\t@Override\n    fun validate(parameters: Saml2LogoutRequestValidatorParameters): Saml2LogoutValidatorResult {\n\t\t // verify signature, issuer, destination, and principal name\n\t\tval result = delegate.validate(authentication)\n\n\t\tif (result.hasErrors()) {\n            return result\n        }\n\n        // perform custom validation\n\n        return result\n    }\n}\n----\n======\n\nThen, you can supply your custom `Saml2LogoutRequestValidator` in the DSL as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .saml2Logout((saml2) -> saml2\n        .logoutRequest((request) -> request\n            .logoutRequestValidator(myOpenSamlLogoutRequestValidator)\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    saml2Logout {\n        logoutRequest {\n            logoutRequestValidator = myOpenSamlLogoutRequestValidator\n        }\n    }\n}\n----\n======\n\n== Customizing `<saml2:LogoutResponse>` Authentication\n\nTo customize validation, you can implement your own `Saml2LogoutResponseValidator`.\nAt this point, the validation is minimal, so you may be able to first delegate to the default `Saml2LogoutResponseValidator` like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class MyOpenSamlLogoutResponseValidator implements Saml2LogoutResponseValidator {\n\tprivate final Saml2LogoutResponseValidator delegate = new OpenSaml5LogoutResponseValidator();\n\n\t@Override\n    public Saml2LogoutValidatorResult validate(Saml2LogoutResponseValidatorParameters parameters) {\n\t\t// verify signature, issuer, destination, and status\n\t\tSaml2LogoutValidatorResult result = delegate.validate(parameters);\n\n        if (result.hasErrors()) {\n            return result;\n        }\n\n        // perform custom validation\n\n        return result;\n    }\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nopen class MyOpenSamlLogoutResponseValidator : Saml2LogoutResponseValidator {\n\tprivate val delegate = OpenSaml5LogoutResponseValidator()\n\n    override fun validate(parameters: Saml2LogoutResponseValidatorParameters): Saml2LogoutValidatorResult {\n\t\t// verify signature, issuer, destination, and status\n\t\tval result = delegate.validate(authentication)\n\n        if (result.hasErrors()) {\n            return result\n        }\n\t\t\n        // perform custom validation\n\n        return result\n    }\n}\n----\n======\n\nThen, you can supply your custom `Saml2LogoutResponseValidator` in the DSL as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .saml2Logout((saml2) -> saml2\n        .logoutResponse((response) -> response\n            .logoutResponseValidator(myOpenSamlLogoutResponseValidator)\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    saml2Logout {\n        logoutResponse {\n            logoutResponseValidator = myOpenSamlLogoutResponseValidator\n        }\n    }\n}\n----\n======\n\n== Customizing `<saml2:LogoutRequest>` storage\n\nWhen your application sends a `<saml2:LogoutRequest>`, the value is stored in the session so that the `RelayState` parameter and the `InResponseTo` attribute in the `<saml2:LogoutResponse>` can be verified.\n\nIf you want to store logout requests in some place other than the session, you can supply your custom implementation in the DSL, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    .saml2Logout((saml2) -> saml2\n        .logoutRequest((request) -> request\n            .logoutRequestRepository(myCustomLogoutRequestRepository)\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    saml2Logout {\n        logoutRequest {\n            logoutRequestRepository = myCustomLogoutRequestRepository\n        }\n    }\n}\n----\n======\n\n[[jc-logout-references]]\n== Further Logout-Related References\n\n- xref:servlet/test/mockmvc/logout.adoc#test-logout[Testing Logout]\n- xref:servlet/integrations/servlet-api.adoc#servletapi-logout[HttpServletRequest.logout()]\n- xref:servlet/exploits/csrf.adoc#csrf-considerations-logout[Logging Out] in section CSRF Caveats\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/saml2/metadata.adoc",
    "content": "[[servlet-saml2login-metadata]]\n= Saml 2.0 Metadata\n\nSpring Security can <<parsing-asserting-party-metadata,parse asserting party metadata>> to produce an `AssertingPartyMetadata` instance as well as <<publishing-relying-party-metadata,publish relying party metadata>> from a `RelyingPartyRegistration` instance.\n\n[[parsing-asserting-party-metadata]]\n== Parsing `<saml2:IDPSSODescriptor>` metadata\n\nYou can parse an asserting party's metadata xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[using `RelyingPartyRegistrations`].\n\nWhen using the OpenSAML vendor support, the resulting `AssertingPartyMetadata` will be of type `OpenSamlAssertingPartyDetails`.\nThis means you'll be able to do get the underlying OpenSAML XMLObject by doing the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOpenSamlAssertingPartyDetails details = (OpenSamlAssertingPartyDetails)\n        registration.getAssertingPartyMetadata();\nEntityDescriptor openSamlEntityDescriptor = details.getEntityDescriptor();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval details: OpenSamlAssertingPartyDetails =\n        registration.getAssertingPartyMetadata() as OpenSamlAssertingPartyDetails\nval openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor()\n----\n======\n\n[[using-assertingpartymetadatarepository]]\n=== Using `AssertingPartyMetadataRepository`\n\nYou can also be more targeted than `RelyingPartyRegistrations` by using `AssertingPartyMetadataRepository`, an interface that allows for only retrieving the asserting party metadata.\n\nThis allows three valuable features:\n\n* Implementations can refresh asserting party metadata in an expiry-aware fashion\n* Implementations of `RelyingPartyRegistrationRepository` can more easily articulate a relationship between a relying party and its one or many corresponding asserting parties\n* Implementations can verify metadata signatures\n\nFor example, `OpenSaml5AssertingPartyMetadataRepository` uses OpenSAML's `MetadataResolver`, and API whose implementations regularly refresh the underlying metadata in an expiry-aware fashion.\n\nThis means that you can now create a refreshable `RelyingPartyRegistrationRepository` in just a few lines of code:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Component\npublic class RefreshableRelyingPartyRegistrationRepository\n        implements IterableRelyingPartyRegistrationRepository {\n\n\tprivate final AssertingPartyMetadataRepository metadata =\n            OpenSaml5AssertingPartyMetadataRepository\n                .fromTrustedMetadataLocation(\"https://idp.example.org/metadata\").build();\n\n\t@Override\n    public RelyingPartyRegistration findByRegistrationId(String registrationId) {\n\t\tAssertingPartyMetadata metadata = this.metadata.findByEntityId(registrationId);\n        if (metadata == null) {\n            return null;\n        }\n\t\treturn applyRelyingParty(metadata);\n    }\n\n\t@Override\n    public Iterator<RelyingPartyRegistration> iterator() {\n\t\treturn StreamSupport.stream(this.metadata.spliterator(), false)\n            .map(this::applyRelyingParty).iterator();\n    }\n\n\tprivate RelyingPartyRegistration applyRelyingParty(AssertingPartyMetadata metadata) {\n\t\treturn RelyingPartyRegistration.withAssertingPartyMetadata(metadata)\n            // apply any relying party configuration\n            .build();\n\t}\n\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Component\nclass RefreshableRelyingPartyRegistrationRepository : IterableRelyingPartyRegistrationRepository {\n\n    private val metadata: AssertingPartyMetadataRepository =\n        OpenSaml5AssertingPartyMetadataRepository.fromTrustedMetadataLocation(\n            \"https://idp.example.org/metadata\").build()\n\n    fun findByRegistrationId(registrationId:String?): RelyingPartyRegistration {\n        val metadata = this.metadata.findByEntityId(registrationId)\n        if (metadata == null) {\n            return null\n        }\n        return applyRelyingParty(metadata)\n    }\n\n    fun iterator(): Iterator<RelyingPartyRegistration> {\n        return StreamSupport.stream(this.metadata.spliterator(), false)\n            .map(this::applyRelyingParty).iterator()\n    }\n\n    private fun applyRelyingParty(metadata: AssertingPartyMetadata): RelyingPartyRegistration {\n        val details: AssertingPartyMetadata = metadata as AssertingPartyMetadata\n        return RelyingPartyRegistration.withAssertingPartyMetadata(details)\n            // apply any relying party configuration\n            .build()\n    }\n }\n----\n======\n\n[TIP]\n`OpenSaml5AssertingPartyMetadataRepository` also ships with a constructor so you can provide a custom `MetadataResolver`. Since the underlying `MetadataResolver` is doing the expiring and refreshing, if you use the constructor directly, you will only get these features by providing an implementation that does so.\n\n=== Verifying Metadata Signatures\n\nYou can also verify metadata signatures using `OpenSaml5AssertingPartyMetadataRepository` by providing the appropriate set of ``Saml2X509Credential``s as follows:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOpenSaml5AssertingPartyMetadataRepository.withMetadataLocation(\"https://idp.example.org/metadata\")\n    .verificationCredentials((c) -> c.add(myVerificationCredential))\n    .build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nOpenSaml5AssertingPartyMetadataRepository.withMetadataLocation(\"https://idp.example.org/metadata\")\n    .verificationCredentials({ c : Collection<Saml2X509Credential> ->\n        c.add(myVerificationCredential) })\n    .build()\n----\n======\n\n[NOTE]\nIf no credentials are provided, the component will not perform signature validation.\n\n[[publishing-relying-party-metadata]]\n== Producing `<saml2:SPSSODescriptor>` Metadata\n\nYou can publish a metadata endpoint using the `saml2Metadata` DSL method, as you'll see below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nhttp\n    // ...\n    .saml2Login(withDefaults())\n    .saml2Metadata(withDefaults());\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nhttp {\n    //...\n    saml2Login { }\n    saml2Metadata { }\n}\n----\n======\n\nYou can use this metadata endpoint to register your relying party with your asserting party.\nThis is often as simple as finding the correct form field to supply the metadata endpoint.\n\nBy default, the metadata endpoint is `+/saml2/metadata+`, though it also responds to `+/saml2/metadata/{registrationId}+` and `+/saml2/service-provider-metadata/{registrationId}+`.\n\nYou can change this by calling the `metadataUrl` method in the DSL:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n.saml2Metadata((saml2) -> saml2.metadataUrl(\"/saml/metadata\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nsaml2Metadata {\n\tmetadataUrl = \"/saml/metadata\"\n}\n----\n======\n\n== Changing the Way a `RelyingPartyRegistration` Is Looked Up\n\nIf you have a different strategy for identifying which `RelyingPartyRegistration` to use, you can configure your own `Saml2MetadataResponseResolver` like the one below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nSaml2MetadataResponseResolver metadataResponseResolver(RelyingPartyRegistrationRepository registrations) {\n\tRequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(\n\t\t\t(id) -> registrations.findByRegistrationId(\"relying-party\"));\n\tmetadata.setMetadataFilename(\"metadata.xml\");\n\treturn metadata;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nfun metadataResponseResolver(val registrations: RelyingPartyRegistrationRepository): Saml2MetadataResponseResolver {\n    val metadata = new RequestMatcherMetadataResponseResolver(\n\t\t\tid: String -> registrations.findByRegistrationId(\"relying-party\"))\n\tmetadata.setMetadataFilename(\"metadata.xml\")\n\treturn metadata\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/saml2/saml-extension-migration.adoc",
    "content": "= SAML 2.0 Extension Migration\n\nThis document contains guidance for moving SAML 2.0 Service Providers from Spring Security SAML Extensions 1.x to Spring Security Since Spring Security doesn’t provide Identity Provider support, migrating a Spring Security SAML Extensions Identity Provider is out of scope for this document.\n\nBecause the two approaches are as different as they are, this document will tend to cover patterns more than precise search-and-replace steps.\n\n[[saml2-login-logout]]\n== Login & Logout\n\n=== Changes In Approach\n\nhttps://github.com/spring-projects/spring-security[Spring Security] takes a slightly different approach from https://github.com/spring-projects/spring-security-saml[Spring Security SAML Extensions] in a few notable ways.\n\n==== Simplified Enablement\n\nSpring Security SAML Extensions support for Service Providers is provided by a series of filters enabled by adding each filter manually in the correct order to various Spring Security filter chains.\n\nSpring Security’s SAML 2.0 Service Provider support is enabled via the Spring Security DSL methods:\nxref:servlet/saml2/login/index.adoc[`saml2Login`],\nxref:servlet/saml2/logout.adoc[`saml2Logout`], and\nxref:servlet/saml2/metadata.adoc[`saml2Metadata`]. It selects the correct filters to add and puts them in the appropriate places in the filter chain.\n\n==== Stronger Encapsulation\n\nLike Spring Security SAML Extensions, Spring Security bases it’s SAML support on OpenSAML. The Extensions project exposes OpenSAML over public interfaces, blurring the lines between the two projects, effectively requiring OpenSAML, and making upgrades to later versions of OpenSAML more complicated.\n\nSpring Security provides stronger encapsulation. No public interfaces expose OpenSAML components and any class that exposes OpenSAML in its public API is named with an `OpenSaml` prefix for additional clarity.\n\n==== Out-of-the-box Multitenancy\n\nSpring Security SAML Extensions offered some lightweight support for declaring more than one Identity Provider and accessing it at login time using the `idp` request parameter. This was limiting as far as changing things at runtime was concerned and also doesn’t allow for a many-to-many relationship between relying and asserting parties.\n\nSpring Security builds SAML 2.0 multitenancy into its default URLs and basic components in the form of a `RelyingPartyRegistration`. This component acts as a link between a Relying Party’s metadata and an Asserting Party’s metadata, and all pairs are available for lookup in a `RelyingPartyRegistrationRepository`. Each URL represents a unique registration pair to be retrieved.\n\nWhether it’s AuthnRequests, Responses, LogoutRequests, LogoutResponses, or EntityDescriptors, each filter is based off of `RelyingPartyRegistrationRepository` and so is fundamentally multi-tenant.\n\n=== Examples Matrix\n\nBoth Spring Security and Spring Security SAML Extensions have examples for how to configure the Service Provider:\n\n[options=\"header\"]\n|===\n| Use case | Spring Security | Spring Security SAML Extension\n\n| Login & Logout | https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/java/saml2/login[Sample] |\nhttps://github.com/jzheaux/spring-security-saml-migrate/tree/main/login-logout[Sample]\n| Login using SAML Extension URLs | https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/java/saml2/custom-urls[Sample] | -\n| Metadata support | https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/java/saml2/refreshable-metadata[Sample] | -\n|===\n\nYou can also see a showcase example in https://github.com/spring-projects/spring-security-saml/tree/main/sample[Spring Security SAML Extension]'s GitHub project.\n\n\n[NOTE]\n====\nSpring Security does not support HTTP-Redirect binding for SAML 2.0 Responses.\nAccording to the SAML specification, the HTTP-Redirect binding is not permitted for SAML Responses due to URL length and signature limitations. Attempting to use this binding may result in unexpected errors.\nUse HTTP-POST binding instead when configuring your identity provider.\n====\n\n[[saml2-unported]]\n== Unported Features\n\nThere are some features that are not yet ported over and there are as yet no plans to do so:\n\n* HTTP-Redirect binding for SAML 2.0 Responses\n* Artifact binding support\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/index.adoc",
    "content": "[[test]]\n= Testing\n:page-section-summary-toc: 1\n\nThis section describes the testing support provided by Spring Security.\n\nTo use the Spring Security test support, you must include `spring-security-test-{spring-security-version}.jar` as a dependency of your project.\n\nAt a high level Spring Security's test support provides integration for:\n\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/method.adoc",
    "content": "[[test-method]]\n= Testing Method Security\n\nThis section demonstrates how to use Spring Security's Test support to test method-based security.\nWe first introduce a `MessageService` that requires the user to be authenticated to be able to access it:\n\ninclude-code::./HelloMessageService[tag=authenticated,indent=0]\n\nThe result of `getMessage` is a `String` that says \"`Hello`\" to the current Spring Security `Authentication`.\nThe following listing shows example output:\n\n[source,text]\n----\nHello org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ca25360: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER\n----\n\n[[test-method-setup]]\n== Security Test Setup\n\nBefore we can use the Spring Security test support, we must perform some setup:\n\ninclude-code::./WithMockUserTests[tag=setup,indent=0]\n\n<1> `@ExtendWith` instructs the spring-test module that it should create an `ApplicationContext`. For additional information, refer to the {spring-framework-reference-url}testing.html#testcontext-junit-jupiter-extension[Spring reference].\n<2> `@ContextConfiguration` instructs the spring-test the configuration to use to create the `ApplicationContext`. Since no configuration is specified, the default configuration locations will be tried. This is no different than using the existing Spring Test support. For additional information, refer to the {spring-framework-reference-url}testing.html#spring-testing-annotation-contextconfiguration[Spring Reference].\n\n[NOTE]\n====\nSpring Security hooks into Spring Test support through the `WithSecurityContextTestExecutionListener`, which ensures that our tests are run with the correct user.\nIt does this by populating the `SecurityContextHolder` prior to running our tests.\nIf you use reactive method security, you also need `ReactorContextTestExecutionListener`, which populates `ReactiveSecurityContextHolder`.\nAfter the test is done, it clears out the `SecurityContextHolder`.\nIf you need only Spring Security related support, you can replace `@ContextConfiguration` with `@SecurityTestExecutionListeners`.\n====\n\nRemember, we added the `@PreAuthorize` annotation to our `HelloMessageService`, so it requires an authenticated user to invoke it.\nIf we run the tests, we expect the following test will pass:\n\ninclude-code::./WithMockUserSampleTests[tag=snippet,indent=0]\n\n[[test-method-withmockuser]]\n== @WithMockUser\n\nThe question is \"How could we most easily run the test as a specific user?\"\nThe answer is to use `@WithMockUser`.\nThe following test will be run as a user with the username \"user\", the password \"password\", and the roles \"ROLE_USER\".\n\ninclude-code::./WithMockUserTests[tag=mock-user,indent=0]\n\nSpecifically the following is true:\n\n* The user with a username of `user` does not have to exist, since we mock the user object.\n* The `Authentication` that is populated in the `SecurityContext` is of type `UsernamePasswordAuthenticationToken`.\n* The principal on the `Authentication` is Spring Security's `User` object.\n* The `User` has a username of `user`.\n* The `User` has a password of `password`.\n* A single `GrantedAuthority` named `ROLE_USER` is used.\n\nThe preceding example is handy, because it lets us use a lot of defaults.\nWhat if we wanted to run the test with a different username?\nThe following test would run with a username of `customUser` (again, the user does not need to actually exist):\n\ninclude-code::./WithMockUserTests[tag=custom-user,indent=0]\n\nWe can also easily customize the roles.\nFor example, the following test is invoked with a username of `admin` and roles of `ROLE_USER` and `ROLE_ADMIN`.\n\ninclude-code::./WithMockUserTests[tag=custom-roles,indent=0]\n\nIf we do not want the value to automatically be prefixed with `ROLE_` we can use the `authorities` attribute.\nFor example, the following test is invoked with a username of `admin` and the `USER` and `ADMIN` authorities.\n\ninclude-code::./WithMockUserTests[tag=custom-authorities,indent=0]\n\nIt can be a bit tedious to place the annotation on every test method.\nInstead, we can place the annotation at the class level. Then every test uses the specified user.\nThe following example runs every test with a user whose username is `admin`, whose password is `password`, and who has the `ROLE_USER` and `ROLE_ADMIN` roles:\n\ninclude-code::./WithMockUserClassTests[tag=snippet,indent=0]\n\nIf you use JUnit 5's `@Nested` test support, you can also place the annotation on the enclosing class to apply to all nested classes.\nThe following example runs every test with a user whose username is `admin`, whose password is `password`, and who has the `ROLE_USER` and `ROLE_ADMIN` roles for both test methods.\n\ninclude-code::./WithMockUserNestedTests[tag=snippet,indent=0]\n\nBy default, the `SecurityContext` is set during the `TestExecutionListener.beforeTestMethod` event.\nThis is the equivalent of happening before JUnit's `@Before`.\nYou can change this to happen during the `TestExecutionListener.beforeTestExecution` event, which is after JUnit's `@Before` but before the test method is invoked:\n\n[source,java]\n----\n@WithMockUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n----\n\n\n[[test-method-withanonymoususer]]\n== @WithAnonymousUser\n\nUsing `@WithAnonymousUser` allows running as an anonymous user.\nThis is especially convenient when you wish to run most of your tests with a specific user but want to run a few tests as an anonymous user.\nThe following example runs `withMockUser1` and `withMockUser2` by using <<test-method-withmockuser,@WithMockUser>> and `anonymous` as an anonymous user:\n\ninclude-code::./WithUserClassLevelAuthenticationTests[tag=snippet,indent=0]\n\nBy default, the `SecurityContext` is set during the `TestExecutionListener.beforeTestMethod` event.\nThis is the equivalent of happening before JUnit's `@Before`.\nYou can change this to happen during the `TestExecutionListener.beforeTestExecution` event, which is after JUnit's `@Before` but before the test method is invoked:\n\n[source,java]\n----\n@WithAnonymousUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n----\n\n\n[[test-method-withuserdetails]]\n== @WithUserDetails\n\nWhile `@WithMockUser` is a convenient way to get started, it may not work in all instances.\nFor example, some applications expect the `Authentication` principal to be of a specific type.\nThis is done so that the application can refer to the principal as the custom type and reduce coupling on Spring Security.\n\nThe custom principal is often returned by a custom `UserDetailsService` that returns an object that implements both `UserDetails` and the custom type.\nFor situations like this, it is useful to create the test user by using a custom `UserDetailsService`.\nThat is exactly what `@WithUserDetails` does.\n\nAssuming we have a `UserDetailsService` exposed as a bean, the following test is invoked with an `Authentication` of type `UsernamePasswordAuthenticationToken` and a principal that is returned from the `UserDetailsService` with the username of `user`:\n\ninclude-code::./WithUserDetailsTests[tag=user-details,indent=0]\n\nWe can also customize the username used to lookup the user from our `UserDetailsService`.\nFor example, this test can be run with a principal that is returned from the `UserDetailsService` with the username of `customUsername`:\n\ninclude-code::./WithUserDetailsTests[tag=user-details-custom-username,indent=0]\n\nWe can also provide an explicit bean name to look up the `UserDetailsService`.\nThe following test looks up the username of `customUsername` by using the `UserDetailsService` with a bean name of `myUserDetailsService`:\n\ninclude-code::./WithCustomUserDetailsTests[tag=custom-user-details-service,indent=0]\n\nAs we did with `@WithMockUser`, we can also place our annotation at the class level so that every test uses the same user.\nHowever, unlike `@WithMockUser`, `@WithUserDetails` requires the user to exist.\n\nBy default, the `SecurityContext` is set during the `TestExecutionListener.beforeTestMethod` event.\nThis is the equivalent of happening before JUnit's `@Before`.\nYou can change this to happen during the `TestExecutionListener.beforeTestExecution` event, which is after JUnit's `@Before` but before the test method is invoked:\n\n[source,java]\n----\n@WithUserDetails(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n----\n\n[[test-method-withsecuritycontext]]\n== @WithSecurityContext\n\nWe have seen that `@WithMockUser` is an excellent choice if we do not use a custom `Authentication` principal.\nNext, we discovered that `@WithUserDetails` lets us use a custom `UserDetailsService` to create our `Authentication` principal but requires the user to exist.\nWe now see an option that allows the most flexibility.\n\nWe can create our own annotation that uses the `@WithSecurityContext` to create any `SecurityContext` we want.\nFor example, we might create an annotation named `@WithMockCustomUser`:\n\ninclude-code::./WithMockCustomUser[tag=snippet,indent=0]\n\nYou can see that `@WithMockCustomUser` is annotated with the `@WithSecurityContext` annotation.\nThis is what signals to Spring Security test support that we intend to create a `SecurityContext` for the test.\nThe `@WithSecurityContext` annotation requires that we specify a `SecurityContextFactory` to create a new `SecurityContext`, given our `@WithMockCustomUser` annotation.\nThe following listing shows our `WithMockCustomUserSecurityContextFactory` implementation:\n\ninclude-code::./WithMockCustomUserSecurityContextFactory[tag=snippet,indent=0]\n\nWe can now annotate a test class or a test method with our new annotation and Spring Security's `WithSecurityContextTestExecutionListener` to ensure that our `SecurityContext` is populated appropriately.\n\nWhen creating your own `WithSecurityContextFactory` implementations, it is nice to know that they can be annotated with standard Spring annotations.\nFor example, the `WithUserDetailsSecurityContextFactory` uses the `@Autowired` annotation to acquire the `UserDetailsService`:\n\ninclude-code::./WithUserDetailsSecurityContextFactory[tag=snippet,indent=0]\n\nBy default, the `SecurityContext` is set during the `TestExecutionListener.beforeTestMethod` event.\nThis is the equivalent of happening before JUnit's `@Before`.\nYou can change this to happen during the `TestExecutionListener.beforeTestExecution` event, which is after JUnit's `@Before` but before the test method is invoked:\n\n[source,java]\n----\n@WithSecurityContext(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n----\n\n[NOTE]\n====\n`@WithMockUser`, `@WithUserDetails`, and `@WithSecurityContext` populate the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] for the test thread.\nThis cannot apply to full HTTP requests a test makes to a running server since those requests are handled by a different thread.\nFor end-to-end HTTP tests, xref:servlet/authentication/index.adoc[authenticate] the request itself (for example, with HTTP Basic or a bearer token).\n====\n\n\n[[test-method-meta-annotations]]\n== Test Meta Annotations\n\nIf you reuse the same user within your tests often, it is not ideal to have to repeatedly specify the attributes.\nFor example, if you have many tests related to an administrative user with a username of `admin` and roles of `ROLE_USER` and `ROLE_ADMIN`, you have to write:\n\ninclude-code::./WithMockUserTests[tag=snippet,indent=0]\n\nRather than repeating this everywhere, we can use a meta annotation.\nFor example, we could create a meta annotation named `WithMockAdmin`:\n\ninclude-code::./WithMockAdmin[tag=snippet,indent=0]\n\nNow we can use `@WithMockAdmin` in the same way as the more verbose `@WithMockUser`.\n\nMeta annotations work with any of the testing annotations described above.\nFor example, this means we could create a meta annotation for `@WithUserDetails(\"admin\")` as well.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/authentication.adoc",
    "content": "[[test-mockmvc-securitycontextholder]]\n= Running a Test as a User in Spring MVC Test\n\nIt is often desirable to run tests as a specific user.\nThere are two simple ways to populate the user:\n\n* <<test-mockmvc-securitycontextholder-rpp,Running as a User in Spring MVC Test with RequestPostProcessor>>\n* <<test-mockmvc-withmockuser,Running as a User in Spring MVC Test with Annotations>>\n\n[[test-mockmvc-securitycontextholder-rpp]]\n== Running as a User in Spring MVC Test with RequestPostProcessor\n\nYou have a number of options to associate a user to the current `HttpServletRequest`.\nThe following example runs as a user (which does not need to exist) whose username is `user`, whose password is `password`, and whose role is `ROLE_USER`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(get(\"/\").with(user(\"user\")))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/\") {\n    with(user(\"user\"))\n}\n----\n======\n\n[NOTE]\n====\nThe support works by associating the user to the `HttpServletRequest`.\nTo associate the request to the `SecurityContextHolder`, you need to ensure that the `SecurityContextPersistenceFilter` is associated with the `MockMvc` instance.\nYou can do so in a number of ways:\n\n* Invoking xref:servlet/test/mockmvc/setup.adoc#test-mockmvc-setup[`apply(springSecurity())`]\n* Adding Spring Security's `FilterChainProxy` to `MockMvc`\n* Manually adding `SecurityContextPersistenceFilter` to the `MockMvc` instance may make sense when using `MockMvcBuilders.standaloneSetup`\n====\n\n\n\nYou can easily make customizations.\nFor example, the following will run as a user (which does not need to exist) with the username \"admin\", the password \"pass\", and the roles \"ROLE_USER\" and \"ROLE_ADMIN\".\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(get(\"/admin\").with(user(\"admin\").password(\"pass\").roles(\"USER\",\"ADMIN\")))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/admin\") {\n    with(user(\"admin\").password(\"pass\").roles(\"USER\",\"ADMIN\"))\n}\n----\n======\n\nIf you have a custom `UserDetails` that you would like to use, you can easily specify that as well.\nFor example, the following will use the specified `UserDetails` (which does not need to exist) to run with a `UsernamePasswordAuthenticationToken` that has a principal of the specified `UserDetails`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(get(\"/\").with(user(userDetails)))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/\") {\n    with(user(userDetails))\n}\n----\n======\n\nYou can run as anonymous user using the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(get(\"/\").with(anonymous()))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/\") {\n    with(anonymous())\n}\n----\n======\n\nThis is especially useful if you are running with a default user and wish to process a few requests as an anonymous user.\n\nIf you want a custom `Authentication` (which does not need to exist) you can do so using the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(get(\"/\").with(authentication(authentication)))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/\") {\n    with(authentication(authentication))\n}\n----\n======\n\nYou can even customize the `SecurityContext` using the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(get(\"/\").with(securityContext(securityContext)))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/\") {\n    with(securityContext(securityContext))\n}\n----\n======\n\nWe can also ensure to run as a specific user for every request by using ``MockMvcBuilders``'s default request.\nFor example, the following will run as a user (which does not need to exist) with the username \"admin\", the password \"password\", and the role \"ROLE_ADMIN\":\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc = MockMvcBuilders\n\t\t.webAppContextSetup(context)\n\t\t.defaultRequest(get(\"/\").with(user(\"user\").roles(\"ADMIN\")))\n\t\t.apply(springSecurity())\n\t\t.build();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc = MockMvcBuilders\n    .webAppContextSetup(context)\n    .defaultRequest<DefaultMockMvcBuilder>(get(\"/\").with(user(\"user\").roles(\"ADMIN\")))\n    .apply<DefaultMockMvcBuilder>(springSecurity())\n    .build()\n----\n======\n\nIf you find you are using the same user in many of your tests, it is recommended to move the user to a method.\nFor example, you can specify the following in your own class named `CustomSecurityMockMvcRequestPostProcessors`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic static RequestPostProcessor rob() {\n\treturn user(\"rob\").roles(\"ADMIN\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nfun rob(): RequestPostProcessor {\n    return user(\"rob\").roles(\"ADMIN\")\n}\n----\n======\n\nNow you can perform a static import on `CustomSecurityMockMvcRequestPostProcessors` and use that within your tests:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static sample.CustomSecurityMockMvcRequestPostProcessors.*;\n\n...\n\nmvc\n\t.perform(get(\"/\").with(rob()))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport sample.CustomSecurityMockMvcRequestPostProcessors.*\n\n//...\n\nmvc.get(\"/\") {\n    with(rob())\n}\n----\n======\n\n[[test-mockmvc-withmockuser]]\n== Running as a User in Spring MVC Test with Annotations\n\nAs an alternative to using a `RequestPostProcessor` to create your user, you can use annotations described in xref:servlet/test/method.adoc[Testing Method Security].\nFor example, the following will run the test with the user with username \"user\", password \"password\", and role \"ROLE_USER\":\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Test\n@WithMockUser\npublic void requestProtectedUrlWithUser() throws Exception {\nmvc\n\t\t.perform(get(\"/\"))\n\t\t...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Test\n@WithMockUser\nfun requestProtectedUrlWithUser() {\n    mvc\n        .get(\"/\")\n        // ...\n}\n----\n======\n\nAlternatively, the following will run the test with the user with username \"user\", password \"password\", and role \"ROLE_ADMIN\":\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Test\n@WithMockUser(roles=\"ADMIN\")\npublic void requestProtectedUrlWithUser() throws Exception {\nmvc\n\t\t.perform(get(\"/\"))\n\t\t...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Test\n@WithMockUser(roles = [\"ADMIN\"])\nfun requestProtectedUrlWithUser() {\n    mvc\n        .get(\"/\")\n        // ...\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/csrf.adoc",
    "content": "[[test-mockmvc-csrf]]\n= Testing with CSRF Protection\n\nWhen testing any non-safe HTTP methods and using Spring Security's CSRF protection, you must include a valid CSRF Token in the request.\nTo specify a valid CSRF token as a request parameter use the CSRF xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(post(\"/\").with(csrf()))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.post(\"/\") {\n    with(csrf())\n}\n----\n======\n\nIf you like, you can include CSRF token in the header instead:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(post(\"/\").with(csrf().asHeader()))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.post(\"/\") {\n    with(csrf().asHeader())\n}\n----\n======\n\nYou can also test providing an invalid CSRF token by using the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(post(\"/\").with(csrf().useInvalidToken()))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.post(\"/\") {\n    with(csrf().useInvalidToken())\n}\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/form-login.adoc",
    "content": "= Testing Form Based Authentication\n\nYou can easily create a request to test a form based authentication using Spring Security's testing support.\nFor example, the following `formLogin` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] will submit a POST to \"/login\" with the username \"user\", the password \"password\", and a valid CSRF token:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin())\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n\t.perform(formLogin())\n----\n======\n\nIt is easy to customize the request.\nFor example, the following will submit a POST to \"/auth\" with the username \"admin\", the password \"pass\", and a valid CSRF token:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin(\"/auth\").user(\"admin\").password(\"pass\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(formLogin(\"/auth\").user(\"admin\").password(\"pass\"))\n----\n======\n\nWe can also customize the parameters names that the username and password are included on.\nFor example, this is the above request modified to include the username on the HTTP parameter \"u\" and the password on the HTTP parameter \"p\".\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin(\"/auth\").user(\"u\",\"admin\").password(\"p\",\"pass\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(formLogin(\"/auth\").user(\"u\",\"admin\").password(\"p\",\"pass\"))\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/http-basic.adoc",
    "content": "= Testing HTTP Basic Authentication\n\nWhile it has always been possible to authenticate with HTTP Basic, it was a bit tedious to remember the header name, format, and encode the values.\nNow this can be done using Spring Security's `httpBasic` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`].\nFor example, the snippet below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(get(\"/\").with(httpBasic(\"user\",\"password\")))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/\") {\n    with(httpBasic(\"user\",\"password\"))\n}\n----\n======\n\nwill attempt to use HTTP Basic to authenticate a user with the username \"user\" and the password \"password\" by ensuring the following header is populated on the HTTP Request:\n\n[source,text]\n----\nAuthorization: Basic dXNlcjpwYXNzd29yZA==\n----\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/index.adoc",
    "content": "[[test-mockmvc]]\n= Spring MVC Test Integration\n:page-section-summary-toc: 1\n\nSpring Security provides comprehensive integration with {spring-framework-reference-url}testing/mockmvc.html[Spring MVC Test]\n\nTo begin using Spring Security's MockMvc integration, you'll first need to\nxref:servlet/test/mockmvc/setup.adoc#test-mockmvc-setup[configure MockMvc],\nand then you'll be able to use Security's\nxref:servlet/test/mockmvc/request-post-processors.adoc[RequestPostProcessors]\nand other MockMvc test support.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/logout.adoc",
    "content": "[[test-logout]]\n= Testing Logout\n\nWhile fairly trivial using standard Spring MVC Test, you can use Spring Security's testing support to make testing log out easier.\nFor example, the following `logout` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] will submit a POST to \"/logout\" with a valid CSRF token:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(logout())\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(logout())\n----\n======\n\nYou can also customize the URL to post to.\nFor example, the snippet below will submit a POST to \"/signout\" with a valid CSRF token:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(logout(\"/signout\"))\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n\t.perform(logout(\"/signout\"))\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/oauth2.adoc",
    "content": "[[testing-oauth2]]\n= Testing OAuth 2.0\n\nWhen it comes to OAuth 2.0, the same principles covered earlier still apply: Ultimately, it depends on what your method under test is expecting to be in the `SecurityContextHolder`.\n\nFor example, for a controller that looks like this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(Principal user) {\n    return user.getName();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(user: Principal): String {\n    return user.name\n}\n----\n======\n\nThere's nothing OAuth2-specific about it, so you will likely be able to simply xref:servlet/test/method.adoc#test-method-withmockuser[use `@WithMockUser`] and be fine.\n\nBut, in cases where your controllers are bound to some aspect of Spring Security's OAuth 2.0 support, like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(@AuthenticationPrincipal OidcUser user) {\n    return user.getIdToken().getSubject();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@AuthenticationPrincipal user: OidcUser): String {\n    return user.idToken.subject\n}\n----\n======\n\nthen Spring Security's test support can come in handy.\n\n[[testing-oidc-login]]\n== Testing OIDC Login\n\nTesting the method above with Spring MVC Test would require simulating some kind of grant flow with an authorization server.\nCertainly this would be a daunting task, which is why Spring Security ships with support for removing this boilerplate.\n\nFor example, we can tell Spring Security to include a default `OidcUser` using the `oidcLogin` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`], like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\").with(oidcLogin()));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(oidcLogin())\n}\n----\n======\n\nWhat this will do is configure the associated `MockHttpServletRequest` with an `OidcUser` that includes a simple `OidcIdToken`, `OidcUserInfo`, and `Collection` of granted authorities.\n\nSpecifically, it will include an `OidcIdToken` with a `sub` claim set to `user`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(user.getIdToken().getClaim(\"sub\")).isEqualTo(\"user\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.idToken.getClaim<String>(\"sub\")).isEqualTo(\"user\")\n----\n======\n\nan `OidcUserInfo` with no claims set:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(user.getUserInfo().getClaims()).isEmpty();\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.userInfo.claims).isEmpty()\n----\n======\n\nand a `Collection` of authorities with just one authority, `SCOPE_read`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(user.getAuthorities()).hasSize(1);\nassertThat(user.getAuthorities()).containsExactly(new SimpleGrantedAuthority(\"SCOPE_read\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.authorities).hasSize(1)\nassertThat(user.authorities).containsExactly(SimpleGrantedAuthority(\"SCOPE_read\"))\n----\n======\n\nSpring Security does the necessary work to make sure that the `OidcUser` instance is available for xref:servlet/integrations/mvc.adoc#mvc-authentication-principal[the `@AuthenticationPrincipal` annotation].\n\nFurther, it also links that `OidcUser` to a simple instance of `OAuth2AuthorizedClient` that it deposits into an mock `OAuth2AuthorizedClientRepository`.\nThis can be handy if your tests <<testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>..\n\n[[testing-oidc-login-authorities]]\n== Configuring Authorities\n\nIn many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.\n\nIn this case, you can supply what granted authorities you need using the `authorities()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(oidcLogin()\n            .authorities(new SimpleGrantedAuthority(\"SCOPE_message:read\"))\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(oidcLogin()\n        .authorities(SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n}\n----\n======\n\n[[testing-oidc-login-claims]]\n== Configuring Claims\n\nAnd while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.\n\nLet's say, for example, that you've got a `user_id` claim that indicates the user's id in your system.\nYou might access it like so in a controller:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(@AuthenticationPrincipal OidcUser oidcUser) {\n    String userId = oidcUser.getIdToken().getClaim(\"user_id\");\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@AuthenticationPrincipal oidcUser: OidcUser): String {\n    val userId = oidcUser.idToken.getClaim<String>(\"user_id\")\n    // ...\n}\n----\n======\n\nIn that case, you'd want to specify that claim with the `idToken()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(oidcLogin()\n                .idToken((token) -> token.claim(\"user_id\", \"1234\"))\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(oidcLogin()\n        .idToken {\n            it.claim(\"user_id\", \"1234\")\n        }\n    )\n}\n----\n======\n\nsince `OidcUser` collects its claims from `OidcIdToken`.\n\n[[testing-oidc-login-user]]\n== Additional Configurations\n\nThere are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:\n\n* `userInfo(OidcUserInfo.Builder)` - For configuring the `OidcUserInfo` instance\n* `clientRegistration(ClientRegistration)` - For configuring the associated `OAuth2AuthorizedClient` with a given `ClientRegistration`\n* `oidcUser(OidcUser)` - For configuring the complete `OidcUser` instance\n\nThat last one is handy if you:\n1. Have your own implementation of `OidcUser`, or\n2. Need to change the name attribute\n\nFor example, let's say that your authorization server sends the principal name in the `user_name` claim instead of the `sub` claim.\nIn that case, you can configure an `OidcUser` by hand:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOidcUser oidcUser = new DefaultOidcUser(\n        AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"),\n        OidcIdToken.withTokenValue(\"id-token\").claim(\"user_name\", \"foo_user\").build(),\n        \"user_name\");\n\nmvc\n    .perform(get(\"/endpoint\")\n        .with(oidcLogin().oidcUser(oidcUser))\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval oidcUser: OidcUser = DefaultOidcUser(\n    AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"),\n    OidcIdToken.withTokenValue(\"id-token\").claim(\"user_name\", \"foo_user\").build(),\n    \"user_name\"\n)\n\nmvc.get(\"/endpoint\") {\n    with(oidcLogin().oidcUser(oidcUser))\n}\n----\n======\n\n[[testing-oauth2-login]]\n== Testing OAuth 2.0 Login\n\nAs with <<testing-oidc-login,testing OIDC login>>, testing OAuth 2.0 Login presents a similar challenge of mocking a grant flow.\nAnd because of that, Spring Security also has test support for non-OIDC use cases.\n\nLet's say that we've got a controller that gets the logged-in user as an `OAuth2User`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(@AuthenticationPrincipal OAuth2User oauth2User) {\n    return oauth2User.getAttribute(\"sub\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@AuthenticationPrincipal oauth2User: OAuth2User): String? {\n    return oauth2User.getAttribute(\"sub\")\n}\n----\n======\n\nIn that case, we can tell Spring Security to include a default `OAuth2User` using the `oauth2Login` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`], like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\").with(oauth2Login()));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(oauth2Login())\n}\n----\n======\n\nWhat this will do is configure the associated `MockHttpServletRequest` with an `OAuth2User` that includes a simple `Map` of attributes and `Collection` of granted authorities.\n\nSpecifically, it will include a `Map` with a key/value pair of `sub`/`user`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat((String) user.getAttribute(\"sub\")).isEqualTo(\"user\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.getAttribute<String>(\"sub\")).isEqualTo(\"user\")\n----\n======\n\nand a `Collection` of authorities with just one authority, `SCOPE_read`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(user.getAuthorities()).hasSize(1);\nassertThat(user.getAuthorities()).containsExactly(new SimpleGrantedAuthority(\"SCOPE_read\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(user.authorities).hasSize(1)\nassertThat(user.authorities).containsExactly(SimpleGrantedAuthority(\"SCOPE_read\"))\n----\n======\n\nSpring Security does the necessary work to make sure that the `OAuth2User` instance is available for xref:servlet/integrations/mvc.adoc#mvc-authentication-principal[the `@AuthenticationPrincipal` annotation].\n\nFurther, it also links that `OAuth2User` to a simple instance of `OAuth2AuthorizedClient` that it deposits in a mock `OAuth2AuthorizedClientRepository`.\nThis can be handy if your tests <<testing-oauth2-client,use the `@RegisteredOAuth2AuthorizedClient` annotation>>.\n\n[[testing-oauth2-login-authorities]]\n== Configuring Authorities\n\nIn many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.\n\nIn this case, you can supply what granted authorities you need using the `authorities()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(oauth2Login()\n            .authorities(new SimpleGrantedAuthority(\"SCOPE_message:read\"))\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(oauth2Login()\n        .authorities(SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n}\n----\n======\n\n[[testing-oauth2-login-claims]]\n== Configuring Claims\n\nAnd while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0.\n\nLet's say, for example, that you've got a `user_id` attribute that indicates the user's id in your system.\nYou might access it like so in a controller:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(@AuthenticationPrincipal OAuth2User oauth2User) {\n    String userId = oauth2User.getAttribute(\"user_id\");\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@AuthenticationPrincipal oauth2User: OAuth2User): String {\n    val userId = oauth2User.getAttribute<String>(\"user_id\")\n    // ...\n}\n----\n======\n\nIn that case, you'd want to specify that attribute with the `attributes()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(oauth2Login()\n                .attributes((attrs) -> attrs.put(\"user_id\", \"1234\"))\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(oauth2Login()\n        .attributes { attrs -> attrs[\"user_id\"] = \"1234\" }\n    )\n}\n----\n======\n\n[[testing-oauth2-login-user]]\n== Additional Configurations\n\nThere are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:\n\n* `clientRegistration(ClientRegistration)` - For configuring the associated `OAuth2AuthorizedClient` with a given `ClientRegistration`\n* `oauth2User(OAuth2User)` - For configuring the complete `OAuth2User` instance\n\nThat last one is handy if you:\n1. Have your own implementation of `OAuth2User`, or\n2. Need to change the name attribute\n\nFor example, let's say that your authorization server sends the principal name in the `user_name` claim instead of the `sub` claim.\nIn that case, you can configure an `OAuth2User` by hand:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOAuth2User oauth2User = new DefaultOAuth2User(\n        AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"),\n        Collections.singletonMap(\"user_name\", \"foo_user\"),\n        \"user_name\");\n\nmvc\n    .perform(get(\"/endpoint\")\n        .with(oauth2Login().oauth2User(oauth2User))\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval oauth2User: OAuth2User = DefaultOAuth2User(\n    AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"),\n    mapOf(Pair(\"user_name\", \"foo_user\")),\n    \"user_name\"\n)\n\nmvc.get(\"/endpoint\") {\n    with(oauth2Login().oauth2User(oauth2User))\n}\n----\n======\n\n[[testing-oauth2-client]]\n== Testing OAuth 2.0 Clients\n\nIndependent of how your user authenticates, you may have other tokens and client registrations that are in play for the request you are testing.\nFor example, your controller may be relying on the client credentials grant to get a token that isn't associated with the user at all:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(@RegisteredOAuth2AuthorizedClient(\"my-app\") OAuth2AuthorizedClient authorizedClient) {\n    return this.webClient.get()\n        .attributes(oauth2AuthorizedClient(authorizedClient))\n        .retrieve()\n        .bodyToMono(String.class)\n        .block();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@RegisteredOAuth2AuthorizedClient(\"my-app\") authorizedClient: OAuth2AuthorizedClient?): String? {\n    return this.webClient.get()\n        .attributes(oauth2AuthorizedClient(authorizedClient))\n        .retrieve()\n        .bodyToMono(String::class.java)\n        .block()\n}\n----\n======\n\nSimulating this handshake with the authorization server could be cumbersome.\nInstead, you can use the `oauth2Client` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] to add a `OAuth2AuthorizedClient` into a mock `OAuth2AuthorizedClientRepository`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\").with(oauth2Client(\"my-app\")));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(\n        oauth2Client(\"my-app\")\n    )\n}\n----\n======\n\nWhat this will do is create an `OAuth2AuthorizedClient` that has a simple `ClientRegistration`, `OAuth2AccessToken`, and resource owner name.\n\nSpecifically, it will include a `ClientRegistration` with a client id of \"test-client\" and client secret of \"test-secret\":\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(authorizedClient.getClientRegistration().getClientId()).isEqualTo(\"test-client\");\nassertThat(authorizedClient.getClientRegistration().getClientSecret()).isEqualTo(\"test-secret\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(authorizedClient.clientRegistration.clientId).isEqualTo(\"test-client\")\nassertThat(authorizedClient.clientRegistration.clientSecret).isEqualTo(\"test-secret\")\n----\n======\n\na resource owner name of \"user\":\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(authorizedClient.getPrincipalName()).isEqualTo(\"user\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(authorizedClient.principalName).isEqualTo(\"user\")\n----\n======\n\nand an `OAuth2AccessToken` with just one scope, `read`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(authorizedClient.getAccessToken().getScopes()).hasSize(1);\nassertThat(authorizedClient.getAccessToken().getScopes()).containsExactly(\"read\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(authorizedClient.accessToken.scopes).hasSize(1)\nassertThat(authorizedClient.accessToken.scopes).containsExactly(\"read\")\n----\n======\n\nThe client can then be retrieved as normal using `@RegisteredOAuth2AuthorizedClient` in a controller method.\n\n[[testing-oauth2-client-scopes]]\n== Configuring Scopes\n\nIn many circumstances, the OAuth 2.0 access token comes with a set of scopes.\nIf your controller inspects these, say like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(@RegisteredOAuth2AuthorizedClient(\"my-app\") OAuth2AuthorizedClient authorizedClient) {\n    Set<String> scopes = authorizedClient.getAccessToken().getScopes();\n    if (scopes.contains(\"message:read\")) {\n        return this.webClient.get()\n            .attributes(oauth2AuthorizedClient(authorizedClient))\n            .retrieve()\n            .bodyToMono(String.class)\n            .block();\n    }\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(@RegisteredOAuth2AuthorizedClient(\"my-app\") authorizedClient: OAuth2AuthorizedClient): String? {\n    val scopes = authorizedClient.accessToken.scopes\n    if (scopes.contains(\"message:read\")) {\n        return webClient.get()\n            .attributes(oauth2AuthorizedClient(authorizedClient))\n            .retrieve()\n            .bodyToMono(String::class.java)\n            .block()\n    }\n    // ...\n}\n----\n======\n\nthen you can configure the scope using the `accessToken()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(oauth2Client(\"my-app\")\n            .accessToken(new OAuth2AccessToken(BEARER, \"token\", null, null, Collections.singleton(\"message:read\"))))\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(oauth2Client(\"my-app\")\n            .accessToken(OAuth2AccessToken(BEARER, \"token\", null, null, Collections.singleton(\"message:read\")))\n    )\n}\n----\n======\n\n[[testing-oauth2-client-registration]]\n== Additional Configurations\n\nThere are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects:\n\n* `principalName(String)` - For configuring the resource owner name\n* `clientRegistration(Consumer<ClientRegistration.Builder>)` - For configuring the associated `ClientRegistration`\n* `clientRegistration(ClientRegistration)` - For configuring the complete `ClientRegistration`\n\nThat last one is handy if you want to use a real `ClientRegistration`\n\nFor example, let's say that you are wanting to use one of your app's `ClientRegistration` definitions, as specified in your `application.yml`.\n\nIn that case, your test can autowire the `ClientRegistrationRepository` and look up the one your test needs:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Autowired\nClientRegistrationRepository clientRegistrationRepository;\n\n// ...\n\nmvc\n    .perform(get(\"/endpoint\")\n        .with(oauth2Client()\n            .clientRegistration(this.clientRegistrationRepository.findByRegistrationId(\"facebook\"))));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Autowired\nlateinit var clientRegistrationRepository: ClientRegistrationRepository\n\n// ...\n\nmvc.get(\"/endpoint\") {\n    with(oauth2Client(\"my-app\")\n        .clientRegistration(clientRegistrationRepository.findByRegistrationId(\"facebook\"))\n    )\n}\n----\n======\n\n[[testing-jwt]]\n== Testing JWT Authentication\n\nIn order to make an authorized request on a resource server, you need a bearer token.\n\nIf your resource server is configured for JWTs, then this would mean that the bearer token needs to be signed and then encoded according to the JWT specification.\nAll of this can be quite daunting, especially when this isn't the focus of your test.\n\nFortunately, there are a number of simple ways that you can overcome this difficulty and allow your tests to focus on authorization and not on representing bearer tokens.\nWe'll look at two of them now:\n\n== `jwt() RequestPostProcessor`\n\nThe first way is via the `jwt` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`].\nThe simplest of these would look something like this:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\").with(jwt()));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(jwt())\n}\n----\n======\n\nWhat this will do is create a mock `Jwt`, passing it correctly through any authentication APIs so that it's available for your authorization mechanisms to verify.\n\nBy default, the `JWT` that it creates has the following characteristics:\n\n[source,json]\n----\n{\n  \"headers\" : { \"alg\" : \"none\" },\n  \"claims\" : {\n    \"sub\" : \"user\",\n    \"scope\" : \"read\"\n  }\n}\n----\n\nAnd the resulting `Jwt`, were it tested, would pass in the following way:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(jwt.getTokenValue()).isEqualTo(\"token\");\nassertThat(jwt.getHeaders().get(\"alg\")).isEqualTo(\"none\");\nassertThat(jwt.getSubject()).isEqualTo(\"sub\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(jwt.tokenValue).isEqualTo(\"token\")\nassertThat(jwt.headers[\"alg\"]).isEqualTo(\"none\")\nassertThat(jwt.subject).isEqualTo(\"sub\")\n----\n======\n\nThese values can, of course be configured.\n\nAny headers or claims can be configured with their corresponding methods:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(jwt().jwt((jwt) -> jwt.header(\"kid\", \"one\").claim(\"iss\", \"https://idp.example.org\"))));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(\n        jwt().jwt { jwt -> jwt.header(\"kid\", \"one\").claim(\"iss\", \"https://idp.example.org\") }\n    )\n}\n----\n======\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(jwt().jwt((jwt) -> jwt.claims((claims) -> claims.remove(\"scope\")))));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(\n        jwt().jwt { jwt -> jwt.claims { claims -> claims.remove(\"scope\") } }\n    )\n}\n----\n======\n\nThe `scope` and `scp` claims are processed the same way here as they are in a normal bearer token request.\nHowever, this can be overridden simply by providing the list of `GrantedAuthority` instances that you need for your test:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(jwt().authorities(new SimpleGrantedAuthority(\"SCOPE_messages\"))));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(\n        jwt().authorities(SimpleGrantedAuthority(\"SCOPE_messages\"))\n    )\n}\n----\n======\n\nOr, if you have a custom `Jwt` to `Collection<GrantedAuthority>` converter, you can also use that to derive the authorities:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(jwt().authorities(new MyConverter())));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(\n        jwt().authorities(MyConverter())\n    )\n}\n----\n======\n\nYou can also specify a complete `Jwt`, for which javadoc:org.springframework.security.oauth2.jwt.Jwt$Builder[] comes quite handy:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nJwt jwt = Jwt.withTokenValue(\"token\")\n    .header(\"alg\", \"none\")\n    .claim(\"sub\", \"user\")\n    .claim(\"scope\", \"read\")\n    .build();\n\nmvc\n    .perform(get(\"/endpoint\")\n        .with(jwt().jwt(jwt)));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwt: Jwt = Jwt.withTokenValue(\"token\")\n    .header(\"alg\", \"none\")\n    .claim(\"sub\", \"user\")\n    .claim(\"scope\", \"read\")\n    .build()\n\nmvc.get(\"/endpoint\") {\n    with(\n        jwt().jwt(jwt)\n    )\n}\n----\n======\n\n== `authentication()` `RequestPostProcessor`\n\nThe second way is by using the `authentication()` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`].\nEssentially, you can instantiate your own `JwtAuthenticationToken` and provide it in your test, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nJwt jwt = Jwt.withTokenValue(\"token\")\n    .header(\"alg\", \"none\")\n    .claim(\"sub\", \"user\")\n    .build();\nCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"SCOPE_read\");\nJwtAuthenticationToken token = new JwtAuthenticationToken(jwt, authorities);\n\nmvc\n    .perform(get(\"/endpoint\")\n        .with(authentication(token)));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval jwt = Jwt.withTokenValue(\"token\")\n    .header(\"alg\", \"none\")\n    .claim(\"sub\", \"user\")\n    .build()\nval authorities: Collection<GrantedAuthority> = AuthorityUtils.createAuthorityList(\"SCOPE_read\")\nval token = JwtAuthenticationToken(jwt, authorities)\n\nmvc.get(\"/endpoint\") {\n    with(\n        authentication(token)\n    )\n}\n----\n======\n\nNote that as an alternative to these, you can also mock the `JwtDecoder` bean itself with a `@MockBean` annotation.\n\n[[testing-opaque-token]]\n== Testing Opaque Token Authentication\n\nSimilar to <<testing-jwt,JWTs>>, opaque tokens require an authorization server in order to verify their validity, which can make testing more difficult.\nTo help with that, Spring Security has test support for opaque tokens.\n\nLet's say that we've got a controller that retrieves the authentication as a `BearerTokenAuthentication`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(BearerTokenAuthentication authentication) {\n    return (String) authentication.getTokenAttributes().get(\"sub\");\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(authentication: BearerTokenAuthentication): String {\n    return authentication.tokenAttributes[\"sub\"] as String\n}\n----\n======\n\nIn that case, we can tell Spring Security to include a default `BearerTokenAuthentication` using the `opaqueToken` xref:servlet/test/mockmvc/request-post-processors.adoc[`RequestPostProcessor`] method, like so:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\").with(opaqueToken()));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(opaqueToken())\n}\n----\n======\n\nWhat this will do is configure the associated `MockHttpServletRequest` with a `BearerTokenAuthentication` that includes a simple `OAuth2AuthenticatedPrincipal`, `Map` of attributes, and `Collection` of granted authorities.\n\nSpecifically, it will include a `Map` with a key/value pair of `sub`/`user`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat((String) token.getTokenAttributes().get(\"sub\")).isEqualTo(\"user\");\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(token.tokenAttributes[\"sub\"] as String).isEqualTo(\"user\")\n----\n======\n\nand a `Collection` of authorities with just one authority, `SCOPE_read`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nassertThat(token.getAuthorities()).hasSize(1);\nassertThat(token.getAuthorities()).containsExactly(new SimpleGrantedAuthority(\"SCOPE_read\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nassertThat(token.authorities).hasSize(1)\nassertThat(token.authorities).containsExactly(SimpleGrantedAuthority(\"SCOPE_read\"))\n----\n======\n\nSpring Security does the necessary work to make sure that the `BearerTokenAuthentication` instance is available for your controller methods.\n\n[[testing-opaque-token-authorities]]\n== Configuring Authorities\n\nIn many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request.\n\nIn this case, you can supply what granted authorities you need using the `authorities()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(opaqueToken()\n            .authorities(new SimpleGrantedAuthority(\"SCOPE_message:read\"))\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(opaqueToken()\n        .authorities(SimpleGrantedAuthority(\"SCOPE_message:read\"))\n    )\n}\n----\n======\n\n[[testing-opaque-token-attributes]]\n== Configuring Claims\n\nAnd while granted authorities are quite common across all of Spring Security, we also have attributes in the case of OAuth 2.0.\n\nLet's say, for example, that you've got a `user_id` attribute that indicates the user's id in your system.\nYou might access it like so in a controller:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@GetMapping(\"/endpoint\")\npublic String foo(BearerTokenAuthentication authentication) {\n    String userId = (String) authentication.getTokenAttributes().get(\"user_id\");\n    // ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@GetMapping(\"/endpoint\")\nfun foo(authentication: BearerTokenAuthentication): String {\n    val userId = authentication.tokenAttributes[\"user_id\"] as String\n    // ...\n}\n----\n======\n\nIn that case, you'd want to specify that attribute with the `attributes()` method:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n    .perform(get(\"/endpoint\")\n        .with(opaqueToken()\n                .attributes((attrs) -> attrs.put(\"user_id\", \"1234\"))\n        )\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc.get(\"/endpoint\") {\n    with(opaqueToken()\n        .attributes { attrs -> attrs[\"user_id\"] = \"1234\" }\n    )\n}\n----\n======\n\n[[testing-opaque-token-principal]]\n== Additional Configurations\n\nThere are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects.\n\nOne such is `principal(OAuth2AuthenticatedPrincipal)`, which you can use to configure the complete `OAuth2AuthenticatedPrincipal` instance that underlies the `BearerTokenAuthentication`\n\nIt's handy if you:\n1. Have your own implementation of `OAuth2AuthenticatedPrincipal`, or\n2. Want to specify a different principal name\n\nFor example, let's say that your authorization server sends the principal name in the `user_name` attribute instead of the `sub` attribute.\nIn that case, you can configure an `OAuth2AuthenticatedPrincipal` by hand:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nMap<String, Object> attributes = Collections.singletonMap(\"user_name\", \"foo_user\");\nOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(\n        (String) attributes.get(\"user_name\"),\n        attributes,\n        AuthorityUtils.createAuthorityList(\"SCOPE_message:read\"));\n\nmvc\n    .perform(get(\"/endpoint\")\n        .with(opaqueToken().principal(principal))\n    );\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval attributes: Map<String, Any> = Collections.singletonMap(\"user_name\", \"foo_user\")\nval principal: OAuth2AuthenticatedPrincipal = DefaultOAuth2AuthenticatedPrincipal(\n    attributes[\"user_name\"] as String?,\n    attributes,\n    AuthorityUtils.createAuthorityList(\"SCOPE_message:read\")\n)\n\nmvc.get(\"/endpoint\") {\n    with(opaqueToken().principal(principal))\n}\n----\n======\n\nNote that as an alternative to using `opaqueToken()` test support, you can also mock the `OpaqueTokenIntrospector` bean itself with a `@MockBean` annotation.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/request-builders.adoc",
    "content": "= SecurityMockMvcRequestBuilders\n\nSpring MVC Test also provides a `RequestBuilder` interface that can be used to create the `MockHttpServletRequest` used in your test.\nSpring Security provides a few `RequestBuilder` implementations that can be used to make testing easier.\nIn order to use Spring Security's `RequestBuilder` implementations ensure the following static import is used:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/request-post-processors.adoc",
    "content": "[[test-mockmvc-smmrpp]]\n= SecurityMockMvcRequestPostProcessors\n:page-section-summary-toc: 1\nSpring MVC Test provides a convenient interface (`RequestPostProcessor`) that you can use to modify a request.\nSpring Security provides a number of `RequestPostProcessor` implementations that make testing easier.\nTo use Spring Security's `RequestPostProcessor` implementations, use the following static import:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/result-handlers.adoc",
    "content": "= SecurityMockMvcResultHandlers\n\nSpring Security provides a few ``ResultHandler``s implementations.\nIn order to use Spring Security's ``ResultHandler``s implementations ensure the following static import is used:\n\n[source,java]\n----\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHandlers.*;\n----\n\n== Exporting the SecurityContext\n\nOften times we want to query a repository to see if some `MockMvc` request actually persisted in the database.\nIn some cases our repository query uses the xref:features/integrations/data.adoc[Spring Data Integration] to filter the results based on current user's username or any other property.\nLet's see an example:\n\nA repository interface:\n[source,java]\n----\nprivate interface MessageRepository extends JpaRepository<Message, Long> {\n\t@Query(\"SELECT m.content FROM Message m WHERE m.sentBy = ?#{ principal?.name }\")\n\tList<String> findAllUserMessages();\n}\n----\n\nOur test scenario:\n\n[source,java]\n----\nmvc\n\t.perform(post(\"/message\")\n\t\t.content(\"New Message\")\n\t\t.contentType(MediaType.TEXT_PLAIN)\n\t)\n\t.andExpect(status().isOk());\n\nList<String> userMessages = messageRepository.findAllUserMessages();\nassertThat(userMessages).hasSize(1);\n----\n\nThis test won't pass because after our request finishes, the `SecurityContextHolder` will be cleared out by the filter chain.\nWe can then export the `TestSecurityContextHolder` to our `SecurityContextHolder` and use it as we want:\n\n[source,java]\n----\nmvc\n\t.perform(post(\"/message\")\n\t\t.content(\"New Message\")\n\t\t.contentType(MediaType.TEXT_PLAIN)\n\t)\n\t.andDo(exportTestSecurityContext())\n\t.andExpect(status().isOk());\n\nList<String> userMessages = messageRepository.findAllUserMessages();\nassertThat(userMessages).hasSize(1);\n----\n\n[NOTE]\n====\nRemember to clear the `SecurityContextHolder` between your tests, or it may leak amongst them\n====\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/result-matchers.adoc",
    "content": "= SecurityMockMvcResultMatchers\n\nAt times it is desirable to make various security related assertions about a request.\nTo accommodate this need, Spring Security Test support implements Spring MVC Test's `ResultMatcher` interface.\nIn order to use Spring Security's `ResultMatcher` implementations ensure the following static import is used:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*\n\n----\n======\n\n== Unauthenticated Assertion\n\nAt times it may be valuable to assert that there is no authenticated user associated with the result of a `MockMvc` invocation.\nFor example, you might want to test submitting an invalid username and password and verify that no user is authenticated.\nYou can easily do this with Spring Security's testing support using something like the following:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin().password(\"invalid\"))\n\t.andExpect(unauthenticated());\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(formLogin().password(\"invalid\"))\n    .andExpect { unauthenticated() }\n----\n======\n\n== Authenticated Assertion\n\nIt is often times that we must assert that an authenticated user exists.\nFor example, we may want to verify that we authenticated successfully.\nWe could verify that a form based login was successful with the following snippet of code:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin())\n\t.andExpect(authenticated());\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(formLogin())\n    .andExpect { authenticated() }\n----\n======\n\nIf we wanted to assert the roles of the user, we could refine our previous code as shown below:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin().user(\"admin\"))\n\t.andExpect(authenticated().withRoles(\"USER\",\"ADMIN\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(formLogin())\n    .andExpect { authenticated().withRoles(\"USER\",\"ADMIN\") }\n----\n======\n\nAlternatively, we could verify the username:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin().user(\"admin\"))\n\t.andExpect(authenticated().withUsername(\"admin\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(formLogin().user(\"admin\"))\n    .andExpect { authenticated().withUsername(\"admin\") }\n----\n======\n\nWe can also combine the assertions:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin().user(\"admin\"))\n\t.andExpect(authenticated().withUsername(\"admin\").withRoles(\"USER\", \"ADMIN\"));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(formLogin().user(\"admin\"))\n    .andExpect { authenticated().withUsername(\"admin\").withRoles(\"USER\", \"ADMIN\") }\n----\n======\n\nWe can also make arbitrary assertions on the authentication\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nmvc\n\t.perform(formLogin())\n\t.andExpect(authenticated().withAuthentication(auth ->\n\t\tassertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken.class)));\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nmvc\n    .perform(formLogin())\n    .andExpect {\n        authenticated().withAuthentication { auth ->\n            assertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken::class.java) }\n        }\n    }\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/pages/servlet/test/mockmvc/setup.adoc",
    "content": "[[test-mockmvc-setup]]\n= Setting Up MockMvc and Spring Security\n\n[NOTE]\n====\nSpring Security's testing support requires spring-test-4.1.3.RELEASE or greater.\n====\n\nTo use Spring Security with Spring MVC Test, add the Spring Security `FilterChainProxy` as a `Filter`.\nYou also need to add Spring Security's `TestSecurityContextHolderPostProcessor` to support xref:servlet/test/mockmvc/authentication.adoc#test-mockmvc-withmockuser[Running as a User in Spring MVC Test with Annotations].\nTo do so, use Spring Security's `SecurityMockMvcConfigurers.springSecurity()`:\n\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SecurityConfig.class)\n@WebAppConfiguration\npublic class CsrfShowcaseTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tmvc = MockMvcBuilders\n\t\t\t\t.webAppContextSetup(context)\n\t\t\t\t.apply(springSecurity()) // <1>\n\t\t\t\t.build();\n\t}\n\t// ...\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration(classes = [SecurityConfig::class])\n@WebAppConfiguration\nclass CsrfShowcaseTests {\n\n    @Autowired\n    private lateinit var context: WebApplicationContext\n\n    private lateinit var mvc: MockMvc\n\n    @BeforeEach\n    fun setup() {\n        mvc = MockMvcBuilders\n            .webAppContextSetup(context)\n            .apply<DefaultMockMvcBuilder>(springSecurity()) // <1>\n            .build()\n    }\n    // ...\n}\n----\n======\n<1> `SecurityMockMvcConfigurers.springSecurity()` will perform all of the initial setup we need to integrate Spring Security with Spring MVC Test\n\nNow that you have configured MockMvc with `springSecurity()`, you can use Security's xref:servlet/test/mockmvc/request-post-processors.adoc[RequestPostProcessors] and other MockMvc test support.\n"
  },
  {
    "path": "docs/modules/ROOT/pages/whats-new.adoc",
    "content": "[[new]]\n= What's New in Spring Security 7.1\n\n== Web\n\n* https://github.com/spring-projects/spring-security/pull/18634[gh-18634] - Added javadoc:org.springframework.security.web.util.matcher.InetAddressMatcher[]\n* https://github.com/spring-projects/spring-security/issues/18755[gh-18755] - Include `charset` in `WWW-Authenticate` header\n* Added xref:servlet/authorization/architecture.adoc#authz-conditional-authorization-manager[ConditionalAuthorizationManager]\n* Added `when` and `withWhen` conditions to `AuthorizationManagerFactories.multiFactor()` for xref:servlet/authentication/mfa.adoc#programmatic-mfa[Programmatic MFA]\n* Added `MultiFactorCondition.WEBAUTHN_REGISTERED` to `@EnableMultiFactorAuthentication(when = ...)` for xref:servlet/authentication/mfa.adoc#mfa-when-webauthn-registered[conditionally requiring MFA for WebAuthn Users]\n\n== OAuth 2.0\n\n* https://github.com/spring-projects/spring-security/issues/18745[gh-18745] - Add RestClientOpaqueTokenIntrospector\n"
  },
  {
    "path": "docs/modules/ROOT/partials/reactive/oauth2/client/web-client-access-token-response-client.adoc",
    "content": "To customize `{class-name}`, simply provide a bean as in the following example and it will be picked up by the default `ReactiveOAuth2AuthorizedClientManager` automatically:\n\n[#oauth2-client-{section-id}-access-token-response-client-bean]\n.Access Token Response Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n@Bean\npublic ReactiveOAuth2AccessTokenResponseClient<{grant-request}> accessTokenResponseClient() {\n\t{class-name} accessTokenResponseClient =\n\t\tnew {class-name}();\n\t// ...\n\treturn accessTokenResponseClient;\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\n@Bean\nfun accessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<{grant-type}> {\n\tval accessTokenResponseClient = {class-name}()\n\t// ...\n\treturn accessTokenResponseClient\n}\n----\n======\n\n`{class-name}` is very flexible and provides several options for customizing the OAuth 2.0 Access Token request and response for the {grant-type} grant.\nChoose from the following use cases to learn more:\n\n* I want to <<oauth2-client-{section-id}-access-token-request-headers,customize headers of the Access Token request>>\n* I want to <<oauth2-client-{section-id}-access-token-request-parameters,customize parameters of the Access Token request>>\n* I want to <<oauth2-client-{section-id}-access-token-response-parameters,customize parameters of the Access Token response>>\n* I want to <<oauth2-client-{section-id}-access-token-response-web-client,customize the instance of `WebClient` that is used>>\n\n[#oauth2-client-{section-id}-access-token-request]\n== Customizing the Access Token Request\n\n`{class-name}` provides hooks for customizing HTTP headers and request parameters of the Token Request.\n\n[#oauth2-client-{section-id}-access-token-request-headers]\n=== Customizing Request Headers\n\nThere are two options for customizing HTTP headers:\n\n* Add additional headers by calling `addHeadersConverter()`\n* Fully customize headers by calling `setHeadersConverter()`\n\nYou can include additional headers without affecting the default headers added to every request using `addHeadersConverter()`.\nThe following example adds a `User-Agent` header to the request when the `registrationId` is `spring`:\n\n.Include Additional HTTP Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.addHeadersConverter(grantRequest -> {\n\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\tHttpHeaders headers = new HttpHeaders();\n\tif (clientRegistration.getRegistrationId().equals(\"spring\")) {\n\t\theaders.set(HttpHeaders.USER_AGENT, \"my-user-agent\");\n\t}\n\treturn headers;\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.addHeadersConverter { grantRequest ->\n\tval clientRegistration = grantRequest.getClientRegistration()\n\tval headers = HttpHeaders()\n\tif (clientRegistration.getRegistrationId() == \"spring\") {\n        headers[HttpHeaders.USER_AGENT] = \"my-user-agent\"\n\t}\n\theaders\n}\n----\n======\n\nYou can fully customize headers by re-using `DefaultOAuth2TokenRequestHeadersConverter` or providing a custom implementation using `setHeadersConverter()`.\nThe following example re-uses `DefaultOAuth2TokenRequestHeadersConverter` and disables `encodeClientCredentials` so that HTTP Basic credentials are no longer encoded with `application/x-www-form-urlencoded`:\n\n.Customize HTTP Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\nDefaultOAuth2TokenRequestHeadersConverter headersConverter =\n\tnew DefaultOAuth2TokenRequestHeadersConverter();\nheadersConverter.setEncodeClientCredentials(false);\n\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.setHeadersConverter(headersConverter);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval headersConverter = DefaultOAuth2TokenRequestHeadersConverter()\nheadersConverter.setEncodeClientCredentials(false)\n\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.setHeadersConverter(headersConverter)\n----\n======\n\n[#oauth2-client-{section-id}-access-token-request-parameters]\n=== Customizing Request Parameters\n\nThere are three options for customizing request parameters:\n\n* Add additional parameters by calling `addParametersConverter()`\n* Override parameters by calling `setParametersConverter()`\n* Fully customize parameters by calling `setParametersCustomizer()`\n\n[NOTE]\n====\nUsing `setParametersConverter()` does not fully customize parameters because it would require the user to provide all default parameters themselves.\nDefault parameters are always provided, but can be fully customized or omitted by calling `setParametersCustomizer()`.\n====\n\nYou can include additional parameters without affecting the default parameters added to every request using `addParametersConverter()`.\nThe following example adds an `audience` parameter to the request when the `registrationId` is `keycloak`:\n\n.Include Additional Request Parameters\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.addParametersConverter(grantRequest -> {\n\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();\n\tif (clientRegistration.getRegistrationId().equals(\"keycloak\")) {\n\t\tparameters.set(OAuth2ParameterNames.AUDIENCE, \"my-audience\");\n\t}\n\treturn parameters;\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.addParametersConverter { grantRequest ->\n\tval clientRegistration = grantRequest.getClientRegistration()\n\tval parameters = LinkedMultiValueMap<String, String>()\n\tif (clientRegistration.getRegistrationId() == \"keycloak\") {\n        parameters[OAuth2ParameterNames.AUDIENCE] = \"my-audience\"\n\t}\n\tparameters\n}\n----\n======\n\nYou can override default parameters using `setParametersConverter()`.\nThe following example overrides the `client_id` parameter when the `registrationId` is `okta`:\n\n.Override Request Parameters\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.setParametersConverter(grantRequest -> {\n\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\tLinkedMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\tif (clientRegistration.getRegistrationId().equals(\"okta\")) {\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, \"my-client\");\n\t}\n\treturn parameters;\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.setParametersConverter { grantRequest ->\n    val clientRegistration = grantRequest.getClientRegistration()\n\tval parameters = LinkedMultiValueMap<String, String>()\n\tif (clientRegistration.getRegistrationId() == \"okta\") {\n        parameters[OAuth2ParameterNames.CLIENT_ID] = \"my-client\"\n\t}\n\tparameters\n}\n----\n======\n\nYou can fully customize parameters (including omitting default parameters) using `setParametersCustomizer()`.\nThe following example omits the `client_id` parameter when the `client_assertion` parameter is present in the request:\n\n.Omit Request Parameters\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.setParametersCustomizer(parameters -> {\n\tif (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {\n\t\tparameters.remove(OAuth2ParameterNames.CLIENT_ID);\n\t}\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.setParametersCustomizer { parameters ->\n\tif (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {\n\t\tparameters.remove(OAuth2ParameterNames.CLIENT_ID)\n\t}\n}\n----\n======\n\n[#oauth2-client-{section-id}-access-token-response]\n== Customizing the Access Token Response\n\n`{class-name}` provides hooks for customizing the OAuth 2.0 Access Token Response.\n\n[#oauth2-client-{section-id}-access-token-response-parameters]\n=== Customizing Response Parameters\n\nYou can customize the conversion of Token Response parameters to an `OAuth2AccessTokenResponse` by calling `setBodyExtractor()`.\nThe default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.\n\nThe following example provides a starting point for customizing the conversion of Token Response parameters to an `OAuth2AccessTokenResponse`:\n\n.Customize Body Extractor\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\n\nBodyExtractor<Mono<Map<String, Object>>, ReactiveHttpInputMessage> bodyExtractor =\n\tBodyExtractors.toMono(new ParameterizedTypeReference<>() {});\naccessTokenResponseClient.setBodyExtractor((inputMessage, context) ->\n\tbodyExtractor.extract(inputMessage, context)\n\t\t.map((parameters) -> parameters.withToken(\"custom-token\")\n\t\t\t// ...\n\t\t\t.build()\n\t\t)\n);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval accessTokenResponseClient = {class-name}()\n\nval bodyExtractor = BodyExtractors.toMono(object : ParameterizedTypeReference<Map<String, Any>>() {})\naccessTokenResponseClient.setBodyExtractor { inputMessage, context ->\n\tbodyExtractor.extract(inputMessage, context).map { parameters ->\n\t\tOAuth2AccessTokenResponse.withToken(\"custom-token\")\n\t\t\t// ...\n\t\t\t.build()\n\t}\n}\n----\n======\n\n[CAUTION]\n====\nWhen providing a custom `BodyExtractor`, you are responsible for detecting and converting an OAuth 2.0 Error Response to a `Mono.error()` with `OAuth2Error` based on parameters of the response.\n====\n\n[#oauth2-client-{section-id}-access-token-response-web-client]\n=== Customizing the `WebClient`\n\nAlternatively, if your requirements are more advanced, you can take full control of the request and/or response by providing a pre-configured `WebClient` to `setWebClient()` as the following example shows:\n\n.Customize `WebClient`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\nWebClient webClient = WebClient.builder()\n\t// ...\n\t.build();\n\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.setWebClient(webClient);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval webClient = WebClient.builder()\n\t// ...\n\t.build()\n\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.setWebClient(webClient)\n----\n======\n"
  },
  {
    "path": "docs/modules/ROOT/partials/servlet/architecture/request-cache-continue.adoc",
    "content": ".`RequestCache` Only Checks for Saved Requests if `continue` Parameter Present\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\n@Bean\nDefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\tHttpSessionRequestCache requestCache = new HttpSessionRequestCache();\n\trequestCache.setMatchingRequestParameterName(\"continue\");\n\thttp\n\t\t// ...\n\t\t.requestCache((cache) -> cache\n\t\t\t.requestCache(requestCache)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n    val httpRequestCache = HttpSessionRequestCache()\n    httpRequestCache.setMatchingRequestParameterName(\"continue\")\n    http {\n        requestCache {\n            requestCache = httpRequestCache\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http auto-config=\"true\">\n\t<!-- ... -->\n\t<request-cache ref=\"requestCache\"/>\n</http>\n\n<b:bean id=\"requestCache\" class=\"org.springframework.security.web.savedrequest.HttpSessionRequestCache\"\n\tp:matchingRequestParameterName=\"continue\"/>\n----\n======"
  },
  {
    "path": "docs/modules/ROOT/partials/servlet/architecture/security-context-explicit.adoc",
    "content": ".Explicit Saving of SecurityContext\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\npublic SecurityFilterChain filterChain(HttpSecurity http) {\n\thttp\n\t\t// ...\n\t\t.securityContext((securityContext) -> securityContext\n\t\t\t.requireExplicitSave(true)\n\t\t);\n\treturn http.build();\n}\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\n@Bean\nopen fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n    http {\n        securityContext {\n            requireExplicitSave = true\n        }\n    }\n    return http.build()\n}\n----\n\nXML::\n+\n[source,xml,role=\"secondary\"]\n----\n<http security-context-explicit-save=\"true\">\n\t<!-- ... -->\n</http>\n----\n======\n\n\nUpon using the configuration, it is important that any code that sets the `SecurityContextHolder` with a `SecurityContext` also saves the `SecurityContext` to the `SecurityContextRepository` if it should be persisted between requests.\n\nFor example, the following code:\n\n.Setting `SecurityContextHolder` with `SecurityContextPersistenceFilter`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nSecurityContextHolder.setContext(securityContext);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nSecurityContextHolder.setContext(securityContext)\n----\n======\n\nshould be replaced with\n\n.Setting `SecurityContextHolder` with `SecurityContextHolderFilter`\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nSecurityContextHolder.setContext(securityContext);\nsecurityContextRepository.saveContext(securityContext, httpServletRequest, httpServletResponse);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nSecurityContextHolder.setContext(securityContext)\nsecurityContextRepository.saveContext(securityContext, httpServletRequest, httpServletResponse)\n----\n======"
  },
  {
    "path": "docs/modules/ROOT/partials/servlet/oauth2/client/rest-client-access-token-response-client.adoc",
    "content": "`{class-name}` is very flexible and provides several options for customizing the OAuth 2.0 Access Token request and response for the {grant-type} grant.\nChoose from the following use cases to learn more:\n\n* I want to <<oauth2-client-{section-id}-access-token-request-headers,customize headers of the Access Token request>>\n* I want to <<oauth2-client-{section-id}-access-token-request-parameters,customize parameters of the Access Token request>>\n* I want to <<oauth2-client-{section-id}-access-token-response-rest-client,customize the instance of `RestClient` that is used>>\n* I want to <<oauth2-client-{section-id}-access-token-response-parameters,customize parameters of the Access Token response>>\n* I want to <<oauth2-client-{section-id}-access-token-response-errors,customize error handling of the Access Token response>>\n\n[#oauth2-client-{section-id}-access-token-request]\n== Customizing the Access Token Request\n\n`{class-name}` provides hooks for customizing HTTP headers and request parameters of the OAuth 2.0 Access Token Request.\n\n[#oauth2-client-{section-id}-access-token-request-headers]\n=== Customizing Request Headers\n\nThere are two options for customizing HTTP headers:\n\n* Add additional headers by calling `addHeadersConverter()`\n* Fully customize headers by calling `setHeadersConverter()`\n\nYou can include additional headers without affecting the default headers added to every request using `addHeadersConverter()`.\nThe following example adds a `User-Agent` header to the request when the `registrationId` is `spring`:\n\n.Include Additional HTTP Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.addHeadersConverter(grantRequest -> {\n\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\tHttpHeaders headers = new HttpHeaders();\n\tif (clientRegistration.getRegistrationId().equals(\"spring\")) {\n\t\theaders.set(HttpHeaders.USER_AGENT, \"my-user-agent\");\n\t}\n\treturn headers;\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.addHeadersConverter { grantRequest ->\n\tval clientRegistration = grantRequest.getClientRegistration()\n\tval headers = HttpHeaders()\n\tif (clientRegistration.getRegistrationId() == \"spring\") {\n        headers[HttpHeaders.USER_AGENT] = \"my-user-agent\"\n\t}\n\theaders\n}\n----\n======\n\nYou can fully customize headers by re-using `DefaultOAuth2TokenRequestHeadersConverter` or providing a custom implementation using `setHeadersConverter()`.\nThe following example re-uses `DefaultOAuth2TokenRequestHeadersConverter` and disables `encodeClientCredentials` so that HTTP Basic credentials are no longer encoded with `application/x-www-form-urlencoded`:\n\n.Customize HTTP Headers\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\nDefaultOAuth2TokenRequestHeadersConverter headersConverter =\n\tnew DefaultOAuth2TokenRequestHeadersConverter();\nheadersConverter.setEncodeClientCredentials(false);\n\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.setHeadersConverter(headersConverter);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval headersConverter = DefaultOAuth2TokenRequestHeadersConverter()\nheadersConverter.setEncodeClientCredentials(false)\n\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.setHeadersConverter(headersConverter)\n----\n======\n\n[#oauth2-client-{section-id}-access-token-request-parameters]\n=== Customizing Request Parameters\n\nThere are three options for customizing request parameters:\n\n* Add additional parameters by calling `addParametersConverter()`\n* Override parameters by calling `setParametersConverter()`\n* Fully customize parameters by calling `setParametersCustomizer()`\n\n[NOTE]\n====\nUsing `setParametersConverter()` does not fully customize parameters because it would require the user to provide all default parameters themselves.\nDefault parameters are always provided, but can be fully customized or omitted by calling `setParametersCustomizer()`.\n====\n\nYou can include additional parameters without affecting the default parameters added to every request using `addParametersConverter()`.\nThe following example adds an `audience` parameter to the request when the `registrationId` is `keycloak`:\n\n.Include Additional Request Parameters\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.addParametersConverter(grantRequest -> {\n\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();\n\tif (clientRegistration.getRegistrationId().equals(\"keycloak\")) {\n\t\tparameters.set(OAuth2ParameterNames.AUDIENCE, \"my-audience\");\n\t}\n\treturn parameters;\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.addParametersConverter { grantRequest ->\n\tval clientRegistration = grantRequest.getClientRegistration()\n\tval parameters = LinkedMultiValueMap<String, String>()\n\tif (clientRegistration.getRegistrationId() == \"keycloak\") {\n        parameters[OAuth2ParameterNames.AUDIENCE] = \"my-audience\"\n\t}\n\tparameters\n}\n----\n======\n\nYou can override default parameters using `setParametersConverter()`.\nThe following example overrides the `client_id` parameter when the `registrationId` is `okta`:\n\n.Override Request Parameters\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.setParametersConverter(grantRequest -> {\n\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\tLinkedMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\tif (clientRegistration.getRegistrationId().equals(\"okta\")) {\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, \"my-client\");\n\t}\n\treturn parameters;\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval parametersConverter = DefaultOAuth2TokenRequestParametersConverter<{grant-request}>()\nparametersConverter.setParametersCustomizer { parameters ->\n\tif (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {\n\t\tparameters.remove(OAuth2ParameterNames.CLIENT_ID)\n\t}\n}\n\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.setParametersConverter { grantRequest ->\n    val clientRegistration = grantRequest.getClientRegistration()\n\tval parameters = LinkedMultiValueMap<String, String>()\n\tif (clientRegistration.getRegistrationId() == \"okta\") {\n        parameters[OAuth2ParameterNames.CLIENT_ID] = \"my-client\"\n\t}\n\tparameters\n}\n----\n======\n\nYou can fully customize parameters (including omitting default parameters) using `setParametersCustomizer()`.\nThe following example omits the `client_id` parameter when the `client_assertion` parameter is present in the request:\n\n.Omit Request Parameters\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.setParametersCustomizer(parameters -> {\n\tif (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {\n\t\tparameters.remove(OAuth2ParameterNames.CLIENT_ID);\n\t}\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.setParametersCustomizer { parameters ->\n\tif (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {\n\t\tparameters.remove(OAuth2ParameterNames.CLIENT_ID)\n\t}\n}\n----\n======\n\n[#oauth2-client-{section-id}-access-token-response]\n== Customizing the Access Token Response\n\n`{class-name}` provides hooks for customizing response parameters and error handling of the OAuth 2.0 Access Token Response.\n\n[#oauth2-client-{section-id}-access-token-response-rest-client]\n=== Customizing the `RestClient`\n\nYou can customize the Token Response by providing a pre-configured `RestClient` to `setRestClient()`.\nThe default `RestClient` is configured as follows:\n\n.Default `RestClient` Configuration\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\",subs=\"+attributes\"]\n----\nRestClient restClient = RestClient.builder()\n\t.messageConverters(messageConverters -> {\n\t\tmessageConverters.clear();\n\t\tmessageConverters.add(new FormHttpMessageConverter());\n\t\tmessageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());\n\t})\n\t.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())\n\t.build();\n\n{class-name} accessTokenResponseClient =\n\tnew {class-name}();\naccessTokenResponseClient.setRestClient(restClient);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\",subs=\"+attributes\"]\n----\nval restClient = RestClient.builder()\n\t.messageConverters { messageConverters ->\n\t\tmessageConverters.clear()\n\t\tmessageConverters.add(FormHttpMessageConverter())\n\t\tmessageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())\n\t}\n\t.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())\n\t.build()\n\nval accessTokenResponseClient = {class-name}()\naccessTokenResponseClient.setRestClient(restClient)\n----\n======\n\n`OAuth2AccessTokenResponseHttpMessageConverter` is an `HttpMessageConverter` for an OAuth 2.0 Access Token Response.\nYou can customize the conversion of Token Response parameters to an `OAuth2AccessTokenResponse` by calling `setAccessTokenResponseConverter()`.\nThe default implementation is `DefaultMapOAuth2AccessTokenResponseConverter`.\n\n`OAuth2ErrorResponseErrorHandler` is a `ResponseErrorHandler` that can handle an OAuth 2.0 Error, such as `400 Bad Request`.\nIt uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error parameters to an `OAuth2Error`.\nYou can customize the conversion of Token Response parameters to an `OAuth2Error` by calling `setErrorConverter()`.\n\n[TIP]\n====\nSpring MVC `FormHttpMessageConverter` is required, as it is used when sending the OAuth 2.0 Access Token Request.\n====\n\n[#oauth2-client-{section-id}-access-token-response-parameters]\n=== Customizing Response Parameters\n\nThe following example provides a starting point for customizing the conversion of Token Response parameters to an `OAuth2AccessTokenResponse`:\n\n.Customize Access Token Response Converter\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =\n\tnew OAuth2AccessTokenResponseHttpMessageConverter();\naccessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {\n\t// ...\n\treturn OAuth2AccessTokenResponse.withToken(\"custom-token\")\n\t\t// ...\n\t\t.build();\n});\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()\naccessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->\n\t// ...\n\treturn OAuth2AccessTokenResponse.withToken(\"custom-token\")\n\t\t// ...\n\t\t.build()\n}\n----\n======\n\n[#oauth2-client-{section-id}-access-token-response-errors]\n=== Customizing Error Handling\n\nThe following example provides a starting point for customizing the conversion of Error parameters to an `OAuth2Error`:\n\n.Customize Access Token Error Handler\n[tabs]\n======\nJava::\n+\n[source,java,role=\"primary\"]\n----\nOAuth2ErrorHttpMessageConverter errorConverter =\n\tnew OAuth2ErrorHttpMessageConverter();\nerrorConverter.setErrorConverter(parameters -> {\n\t// ...\n\treturn new OAuth2Error(\"custom-error\", \"custom description\", \"custom-uri\");\n});\n\nOAuth2ErrorResponseErrorHandler errorHandler =\n\tnew OAuth2ErrorResponseErrorHandler();\nerrorHandler.setErrorConverter(errorConverter);\n----\n\nKotlin::\n+\n[source,kotlin,role=\"secondary\"]\n----\nval errorConverter = OAuth2ErrorHttpMessageConverter()\nerrorConverter.setErrorConverter { parameters ->\n\t// ...\n\treturn OAuth2Error(\"custom-error\", \"custom description\", \"custom-uri\")\n}\n\nval errorHandler = OAuth2ErrorResponseErrorHandler()\nerrorHandler.setErrorConverter(errorConverter)\n----\n======\n"
  },
  {
    "path": "docs/package.json",
    "content": "{\n  \"dependencies\": {\n    \"antora\": \"3.2.0-alpha.11\",\n    \"@antora/atlas-extension\": \"1.0.0-alpha.5\",\n    \"@antora/collector-extension\": \"1.0.3\",\n    \"@asciidoctor/tabs\": \"1.0.0-beta.6\",\n    \"@springio/antora-extensions\": \"1.14.9\",\n    \"@springio/asciidoctor-extensions\": \"1.0.0-alpha.18\"\n  }\n}\n"
  },
  {
    "path": "docs/spring-security-docs.gradle",
    "content": "plugins {\n\tid 'org.antora' version '1.0.0'\n\tid 'io.spring.antora.generate-antora-yml' version '0.0.1'\n\tid 'io.spring.convention.repository'\n\tid 'security-kotlin'\n\tid 'java-toolchain'\n\tid 'test-compile-target-jdk25'\n\tid 'compile-warnings-error'\n\tid 'javadoc-warnings-error'\n}\n\napply plugin: 'io.spring.convention.docs'\napply plugin: 'java'\n\nantora {\n\toptions = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]\n\tenvironment = [\n\t\t'BUILD_REFNAME': 'HEAD',\n\t\t'BUILD_VERSION': project.version,\n\t]\n}\n\ntasks.register(\"syncAntoraAttachments\", Sync) {\n\tgroup = 'Documentation'\n\tdescription = 'Syncs the Antora attachments'\n\tfrom project.provider( { project.tasks.api.outputs } )\n\tinto project.layout.buildDirectory.dir('generated-antora-resources/modules/ROOT/assets/attachments/api/java')\n}\n\ntasks.named(\"generateAntoraYml\") {\n\tasciidocAttributes = project.provider( { generateAttributes() } )\n\tasciidocAttributes.putAll(providers.provider( { resolvedVersions(project.configurations.testRuntimeClasspath) }))\n}\n\ntasks.register(\"generateAntoraResources\") {\n\tdependsOn 'generateAntoraYml', 'syncAntoraAttachments'\n}\n\ndependencies {\n\ttestImplementation platform(project(':spring-security-dependencies'))\n\ttestImplementation project(':spring-security-config')\n\ttestImplementation project(path : ':spring-security-config', configuration : 'tests')\n\ttestImplementation project(':spring-security-test')\n\ttestImplementation project(':spring-security-oauth2-client')\n\ttestImplementation project(':spring-security-oauth2-resource-server')\n\ttestImplementation project(':spring-security-messaging')\n\ttestImplementation project(':spring-security-webauthn')\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\ttestImplementation libs.com.password4j.password4j\n\ttestImplementation 'com.unboundid:unboundid-ldapsdk'\n\ttestImplementation libs.webauthn4j.core\n\ttestImplementation 'org.jetbrains.kotlin:kotlin-reflect'\n\ttestImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'\n\ttestImplementation 'org.springframework:spring-core'\n\ttestImplementation 'org.springframework:spring-test'\n\ttestImplementation 'org.springframework:spring-websocket'\n\n\ttestImplementation 'org.springframework:spring-webmvc'\n\ttestImplementation 'jakarta.servlet:jakarta.servlet-api'\n\ttestImplementation 'io.mockk:mockk'\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\ndef generateAttributes() {\n\tdef springFrameworkVersion = libs.org.springframework.spring.framework.bom.get().versionConstraint.displayName\n\tdef springBootVersion = project.property(\"springBootVersion\")\n\tdef samplesBranch = project.property(\"samplesBranch\")\n\n\tdef docsTag = snapshotBuild ? 'current' : project.version\n\tdef ghTag = snapshotBuild ? 'main' : project.version\n\tdef ghUrl = \"https://github.com/spring-projects/spring-security/tree/$ghTag\"\n\tdef ghOldSamplesUrl = 'https://github.com/spring-projects/spring-security/tree/5.4.x/samples'\n\tdef ghSamplesUrl = \"https://github.com/spring-projects/spring-security-samples/tree/$samplesBranch\"\n\tdef securityDocsUrl = \"https://docs.spring.io/spring-security/site/docs/$docsTag\"\n\tdef securityApiUrl = \"$securityDocsUrl/api/\"\n\tdef securityReferenceUrl = \"$securityDocsUrl/reference/html5/\"\n\tdef springFrameworkApiUrl = \"https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/javadoc-api/\"\n\tdef springFrameworkReferenceUrl = \"https://docs.spring.io/spring-framework/reference/$springFrameworkVersion/\"\n\tdef springBootReferenceUrl = \"https://docs.spring.io/spring-boot/$springBootVersion/\"\n\tdef springBootApiUrl = \"https://docs.spring.io/spring-boot/$springBootVersion/api/java/\"\n\n\treturn\t['gh-old-samples-url': ghOldSamplesUrl.toString(),\n\t\t'gh-samples-url': ghSamplesUrl.toString(),\n\t\t'gh-url': ghUrl.toString(),\n\t\t'security-api-url': securityApiUrl.toString(),\n\t\t'security-reference-url': securityReferenceUrl.toString(),\n\t\t'spring-framework-api-url': springFrameworkApiUrl.toString(),\n\t\t'spring-framework-reference-url': springFrameworkReferenceUrl.toString(),\n\t\t'spring-boot-api-url': springBootApiUrl.toString(),\n\t\t'spring-boot-reference-url': springBootReferenceUrl.toString(),\n\t\t'spring-security-version': project.version]\n\t\t+ resolvedVersions(project.configurations.testRuntimeClasspath)\n}\n\ndef resolvedVersions(Configuration configuration) {\n\treturn configuration.resolvedConfiguration\n\t\t\t\t.resolvedArtifacts\n\t\t\t\t.collectEntries { [(it.name + '-version'): it.moduleVersion.id.version] }\n}\n\ntest {\n\tuseJUnitPlatform()\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationcompromisedpasswordcheck/CompromisedPasswordCheckerUsage.java",
    "content": "package org.springframework.security.docs.features.authentication.authenticationcompromisedpasswordcheck;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.security.authentication.password.CompromisedPasswordChecker;\nimport org.springframework.security.authentication.password.CompromisedPasswordException;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker;\n\npublic class CompromisedPasswordCheckerUsage {\n\t// tag::configuration[]\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.authorizeHttpRequests(authorize -> authorize\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.formLogin((login) -> login\n\t\t\t\t.failureHandler(new CompromisedPasswordAuthenticationFailureHandler())\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tpublic CompromisedPasswordChecker compromisedPasswordChecker() {\n\t\treturn new HaveIBeenPwnedRestApiPasswordChecker();\n\t}\n\n\tstatic class CompromisedPasswordAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n\t\tprivate final SimpleUrlAuthenticationFailureHandler defaultFailureHandler = new SimpleUrlAuthenticationFailureHandler(\n\t\t\t\t\"/login?error\");\n\n\t\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\t\t@Override\n\t\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthenticationException exception) throws IOException, ServletException {\n\t\t\tif (exception instanceof CompromisedPasswordException) {\n\t\t\t\tthis.redirectStrategy.sendRedirect(request, response, \"/reset-password\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.defaultFailureHandler.onAuthenticationFailure(request, response, exception);\n\t\t}\n\n\t}\n\t// end::configuration[]\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstorageargon2/Argon2PasswordEncoderUsage.java",
    "content": "package org.springframework.security.docs.features.authentication.authenticationpasswordstorageargon2;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.springframework.security.crypto.argon2.Argon2PasswordEncoder;\n\npublic class Argon2PasswordEncoderUsage {\n\tpublic void testArgon2PasswordEncoder() {\n\t\t// tag::argon2PasswordEncoder[]\n\t\t// Create an encoder with all the defaults\n\t\tArgon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertTrue(encoder.matches(\"myPassword\", result));\n\t\t// end::argon2PasswordEncoder[]\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragebcrypt/BCryptPasswordEncoderUsage.java",
    "content": "package org.springframework.security.docs.features.authentication.authenticationpasswordstoragebcrypt;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\n\npublic class BCryptPasswordEncoderUsage {\n\tpublic void testBCryptPasswordEncoder() {\n\t\t// tag::bcryptPasswordEncoder[]\n\t\t// Create an encoder with strength 16\n\t\tBCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertTrue(encoder.matches(\"myPassword\", result));\n\t\t// end::bcryptPasswordEncoder[]\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragedepgettingstarted/WithDefaultPasswordEncoderUsage.java",
    "content": "package org.springframework.security.docs.features.authentication.authenticationpasswordstoragedepgettingstarted;\n\nimport java.util.List;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport static org.springframework.security.core.userdetails.User.UserBuilder;\n\npublic class WithDefaultPasswordEncoderUsage {\n\tpublic UserDetails createSingleUser() {\n\t\t// tag::createSingleUser[]\n\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"user\")\n\t\t\t\t.build();\n\t\tSystem.out.println(user.getPassword());\n\t\t// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n\t\t// end::createSingleUser[]\n\t\treturn user;\n\t}\n\n\tpublic List<UserDetails> createMultipleUsers() {\n\t\t// tag::createMultipleUsers[]\n\t\tUserBuilder users = User.withDefaultPasswordEncoder();\n\t\tUserDetails user = users\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\tUserDetails admin = users\n\t\t\t\t.username(\"admin\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\",\"ADMIN\")\n\t\t\t\t.build();\n\t\t// end::createMultipleUsers[]\n\t\treturn List.of(user, admin);\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragedpe/DelegatingPasswordEncoderUsage.java",
    "content": "package org.springframework.security.docs.features.authentication.authenticationpasswordstoragedpe;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.springframework.security.crypto.argon2.Argon2PasswordEncoder;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.DelegatingPasswordEncoder;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;\nimport org.springframework.security.crypto.password.StandardPasswordEncoder;\nimport org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;\n\npublic class DelegatingPasswordEncoderUsage {\n\tPasswordEncoder defaultDelegatingPasswordEncoder() {\n\t\t// tag::createDefaultPasswordEncoder[]\n\t\tPasswordEncoder passwordEncoder =\n\t\t\t\tPasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t\t// end::createDefaultPasswordEncoder[]\n\t\treturn passwordEncoder;\n\t}\n\n\tPasswordEncoder customDelegatingPasswordEncoder() {\n\t\t// tag::createCustomPasswordEncoder[]\n\t\tString idForEncode = \"bcrypt\";\n\t\tMap encoders = new HashMap<>();\n\t\tencoders.put(idForEncode, new BCryptPasswordEncoder());\n\t\tencoders.put(\"noop\", NoOpPasswordEncoder.getInstance());\n\t\tencoders.put(\"pbkdf2\", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());\n\t\tencoders.put(\"pbkdf2@SpringSecurity_v5_8\", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());\n\t\tencoders.put(\"scrypt\", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());\n\t\tencoders.put(\"scrypt@SpringSecurity_v5_8\", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());\n\t\tencoders.put(\"argon2\", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());\n\t\tencoders.put(\"argon2@SpringSecurity_v5_8\", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());\n\t\tencoders.put(\"sha256\", new StandardPasswordEncoder());\n\n\t\tPasswordEncoder passwordEncoder =\n\t\t\tnew DelegatingPasswordEncoder(idForEncode, encoders);\n\t\t// end::createCustomPasswordEncoder[]\n\t\treturn passwordEncoder;\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragepbkdf2/Pbkdf2PasswordEncoderUsage.java",
    "content": "package org.springframework.security.docs.features.authentication.authenticationpasswordstoragepbkdf2;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;\n\npublic class Pbkdf2PasswordEncoderUsage {\n\tvoid testPbkdf2PasswordEncoder() {\n\t\t// tag::pbkdf2PasswordEncoder[]\n\t\t// Create an encoder with all the defaults\n\t\tPbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertTrue(encoder.matches(\"myPassword\", result));\n\t\t// end::pbkdf2PasswordEncoder[]\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/authenticationpasswordstoragescrypt/SCryptPasswordEncoderUsage.java",
    "content": "package org.springframework.security.docs.features.authentication.authenticationpasswordstoragescrypt;\n\nimport static org.junit.Assert.assertTrue;\n\nimport org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;\n\npublic class SCryptPasswordEncoderUsage {\n\tvoid testSCryptPasswordEncoder() {\n\t\t// tag::sCryptPasswordEncoder[]\n\t\t// Create an encoder with all the defaults\n\t\tSCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertTrue(encoder.matches(\"myPassword\", result));\n\t\t// end::sCryptPasswordEncoder[]\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/password4jargon2/Argon2UsageTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.authentication.password4jargon2;\n\nimport com.password4j.Argon2Function;\nimport com.password4j.types.Argon2;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.crypto.password4j.Argon2Password4jPasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class Argon2UsageTests {\n\n\t@Test\n\tvoid defaultParams() {\n\t\t// tag::default-params[]\n\t\tPasswordEncoder encoder = new Argon2Password4jPasswordEncoder();\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::default-params[]\n\t}\n\n\t@Test\n\tvoid customParameters() {\n\t\t// tag::custom-params[]\n\t\tArgon2Function argon2Fn = Argon2Function.getInstance(65536, 3, 4, 32,\n\t\t\t\tArgon2.ID);\n\t\tPasswordEncoder encoder = new Argon2Password4jPasswordEncoder(argon2Fn);\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::custom-params[]\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/password4jballooning/BallooningHashingUsageTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.authentication.password4jballooning;\n\nimport com.password4j.BalloonHashingFunction;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.crypto.password4j.BalloonHashingPassword4jPasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class BallooningHashingUsageTests {\n\n\t@Test\n\tvoid defaultParams() {\n\t\t// tag::default-params[]\n\t\tPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder();\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::default-params[]\n\t}\n\n\t@Test\n\tvoid customParameters() {\n\t\t// tag::custom-params[]\n\t\tBalloonHashingFunction ballooningHashingFn =\n\t\t\tBalloonHashingFunction.getInstance(\"SHA-256\", 1024, 3, 4, 3);\n\t\tPasswordEncoder encoder = new BalloonHashingPassword4jPasswordEncoder(ballooningHashingFn);\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::custom-params[]\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/password4jbcrypt/BcryptUsageTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.authentication.password4jbcrypt;\n\nimport com.password4j.BcryptFunction;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.crypto.password4j.BcryptPassword4jPasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class BcryptUsageTests {\n\n\t@Test\n\tvoid defaultParams() {\n\t\t// tag::default-params[]\n\t\tPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder();\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::default-params[]\n\t}\n\n\t@Test\n\tvoid customParameters() {\n\t\t// tag::custom-params[]\n\t\tBcryptFunction bcryptFn = BcryptFunction.getInstance(12);\n\t\tPasswordEncoder encoder = new BcryptPassword4jPasswordEncoder(bcryptFn);\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::custom-params[]\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/password4jpbkdf2/Pbkdf2UsageTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.authentication.password4jpbkdf2;\n\nimport com.password4j.PBKDF2Function;\nimport com.password4j.types.Hmac;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.crypto.password4j.Pbkdf2Password4jPasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class Pbkdf2UsageTests {\n\n\t@Test\n\tvoid defaultParams() {\n\t\t// tag::default-params[]\n\t\tPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder();\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::default-params[]\n\t}\n\n\t@Test\n\tvoid customParameters() {\n\t\t// tag::custom-params[]\n\t\tPBKDF2Function pbkdf2Fn = PBKDF2Function.getInstance(Hmac.SHA256, 100000, 256);\n\t\tPasswordEncoder encoder = new Pbkdf2Password4jPasswordEncoder(pbkdf2Fn);\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::custom-params[]\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/authentication/password4jscrypt/ScryptUsageTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.authentication.password4jscrypt;\n\nimport com.password4j.ScryptFunction;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.crypto.password4j.ScryptPassword4jPasswordEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class ScryptUsageTests {\n\n\t@Test\n\tvoid defaultParams() {\n\t\t// tag::default-params[]\n\t\tPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder();\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::default-params[]\n\t}\n\n\t@Test\n\tvoid customParameters() {\n\t\t// tag::custom-params[]\n\t\tScryptFunction scryptFn = ScryptFunction.getInstance(32768, 8, 1, 32);\n\t\tPasswordEncoder encoder = new ScryptPassword4jPasswordEncoder(scryptFn);\n\t\tString result = encoder.encode(\"myPassword\");\n\t\tassertThat(encoder.matches(\"myPassword\", result)).isTrue();\n\t\t// end::custom-params[]\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/integrations/rest/clientregistrationid/User.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.integrations.rest.clientregistrationid;\n\n/**\n * A user.\n * @param login\n * @param id\n * @param name\n * @author Rob Winch\n * @see UserService\n */\npublic record User(String login, int id, String name) {\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/integrations/rest/clientregistrationid/UserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.integrations.rest.clientregistrationid;\n\nimport org.springframework.security.oauth2.client.annotation.ClientRegistrationId;\nimport org.springframework.web.service.annotation.GetExchange;\nimport org.springframework.web.service.annotation.HttpExchange;\n\n/**\n * Demonstrates a service for {@link ClientRegistrationId} and HTTP Service clients.\n * @author Rob Winch\n */\n@HttpExchange\npublic interface UserService {\n\n\t// tag::getAuthenticatedUser[]\n\t@GetExchange(\"/user\")\n\t@ClientRegistrationId(\"github\")\n\tUser getAuthenticatedUser();\n\t// end::getAuthenticatedUser[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/integrations/rest/configurationrestclient/RestClientHttpInterfaceIntegrationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.integrations.rest.configurationrestclient;\n\nimport okhttp3.mockwebserver.MockWebServer;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.docs.features.integrations.rest.clientregistrationid.UserService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer;\nimport org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer;\nimport org.springframework.web.service.registry.ImportHttpServices;\n\nimport static org.mockito.Mockito.mock;\n\n/**\n * Documentation for {@link OAuth2RestClientHttpServiceGroupConfigurer}.\n * @author Rob Winch\n */\n@Configuration(proxyBeanMethods = false)\n@ImportHttpServices(types = UserService.class)\npublic class RestClientHttpInterfaceIntegrationConfiguration {\n\n\t// tag::config[]\n\t@Bean\n\tOAuth2RestClientHttpServiceGroupConfigurer securityConfigurer(\n\t\t\tOAuth2AuthorizedClientManager manager) {\n\t\treturn OAuth2RestClientHttpServiceGroupConfigurer.from(manager);\n\t}\n\t// end::config[]\n\n\t@Bean\n\tOAuth2AuthorizedClientManager authorizedClientManager() {\n\t\treturn mock(OAuth2AuthorizedClientManager.class);\n\t}\n\n\t@Bean\n\tRestClientHttpServiceGroupConfigurer groupConfigurer(MockWebServer server) {\n\t\treturn groups -> {\n\n\t\t\tgroups\n\t\t\t\t.forEachClient((group, builder) -> builder\n\t\t\t\t.baseUrl(server.url(\"\").toString())\n\t\t\t\t.defaultHeader(\"Accept\", \"application/vnd.github.v3+json\"));\n\t\t};\n\t}\n\n\t@Bean\n\tMockWebServer mockServer() {\n\t\treturn new MockWebServer();\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/integrations/rest/configurationrestclient/RestClientHttpInterfaceIntegrationConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.integrations.rest.configurationrestclient;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.docs.features.integrations.rest.clientregistrationid.UserService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * Tests RestClient configuration for HTTP Interface clients.\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = RestClientHttpInterfaceIntegrationConfiguration.class)\nclass RestClientHttpInterfaceIntegrationConfigurationTests {\n\n\t@Test\n\tvoid getAuthenticatedUser(@Autowired MockWebServer webServer, @Autowired OAuth2AuthorizedClientManager authorizedClients, @Autowired UserService users)\n\t\t\tthrows InterruptedException {\n\t\tClientRegistration registration = CommonOAuth2Provider.GITHUB.getBuilder(\"github\").clientId(\"github\").build();\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(5));\n\t\tOAuth2AccessToken token = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"1234\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient result = new OAuth2AuthorizedClient(registration, \"rob\", token);\n\t\tgiven(authorizedClients.authorize(any())).willReturn(result);\n\n\t\twebServer.enqueue(new MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(\n\t\t\t\t\"\"\"\n\t\t\t\t{\"login\": \"rob_winch\", \"id\": 1234, \"name\": \"Rob Winch\" }\n\t\t\t\t\"\"\"));\n\n\t\tusers.getAuthenticatedUser();\n\n\t\tassertThat(webServer.takeRequest().getHeader(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer \" + token.getTokenValue());\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/integrations/rest/configurationwebclient/ServerRestClientHttpInterfaceIntegrationConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.integrations.rest.configurationwebclient;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider;\nimport org.springframework.security.docs.features.integrations.rest.clientregistrationid.UserService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * Demonstrates configuring RestClient with interface based proxy clients.\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = ServerWebClientHttpInterfaceIntegrationConfiguration.class)\nclass ServerRestClientHttpInterfaceIntegrationConfigurationTests {\n\n\t@Test\n\tvoid getAuthenticatedUser(@Autowired MockWebServer webServer, @Autowired ReactiveOAuth2AuthorizedClientManager authorizedClients, @Autowired UserService users)\n\t\t\tthrows InterruptedException {\n\t\tClientRegistration registration = CommonOAuth2Provider.GITHUB.getBuilder(\"github\").clientId(\"github\").build();\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(5));\n\t\tOAuth2AccessToken token = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"1234\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient result = new OAuth2AuthorizedClient(registration, \"rob\", token);\n\t\tgiven(authorizedClients.authorize(any())).willReturn(Mono.just(result));\n\n\t\twebServer.enqueue(new MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(\n\t\t\t\t\"\"\"\n\t\t\t\t{\"login\": \"rob_winch\", \"id\": 1234, \"name\": \"Rob Winch\" }\n\t\t\t\t\"\"\"));\n\n\t\tusers.getAuthenticatedUser();\n\n\t\tassertThat(webServer.takeRequest().getHeader(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer \" + token.getTokenValue());\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/integrations/rest/configurationwebclient/ServerWebClientHttpInterfaceIntegrationConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.integrations.rest.configurationwebclient;\n\nimport okhttp3.mockwebserver.MockWebServer;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.docs.features.integrations.rest.clientregistrationid.UserService;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer;\nimport org.springframework.security.oauth2.client.web.reactive.function.client.support.OAuth2WebClientHttpServiceGroupConfigurer;\nimport org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer;\nimport org.springframework.web.service.registry.HttpServiceGroup;\nimport org.springframework.web.service.registry.ImportHttpServices;\n\nimport static org.mockito.Mockito.mock;\n\n/**\n * Documentation for {@link OAuth2RestClientHttpServiceGroupConfigurer}.\n * @author Rob Winch\n */\n@Configuration(proxyBeanMethods = false)\n@ImportHttpServices(types = UserService.class, clientType = HttpServiceGroup.ClientType.WEB_CLIENT)\npublic class ServerWebClientHttpInterfaceIntegrationConfiguration {\n\n\t// tag::config[]\n\t@Bean\n\tOAuth2WebClientHttpServiceGroupConfigurer securityConfigurer(\n\t\t\tReactiveOAuth2AuthorizedClientManager manager) {\n\t\treturn OAuth2WebClientHttpServiceGroupConfigurer.from(manager);\n\t}\n\t// end::config[]\n\n\t@Bean\n\tReactiveOAuth2AuthorizedClientManager authorizedClientManager() {\n\t\treturn mock(ReactiveOAuth2AuthorizedClientManager.class);\n\t}\n\n\t@Bean\n\tWebClientHttpServiceGroupConfigurer groupConfigurer(MockWebServer server) {\n\t\treturn groups -> {\n\t\t\tString baseUrl = server.url(\"\").toString();\n\t\t\tgroups\n\t\t\t\t.forEachClient((group, builder) -> builder\n\t\t\t\t.baseUrl(baseUrl)\n\t\t\t\t.defaultHeader(\"Accept\", \"application/vnd.github.v3+json\"));\n\t\t};\n\t}\n\n\t@Bean\n\tMockWebServer mockServer() {\n\t\treturn new MockWebServer();\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/Hovercard.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.integrations.rest.type;\n\n/**\n * Used to ensure {@link UserService} compiles, but not show in the documentation.\n *\n * @author Rob Winch\n */\npublic record Hovercard() {\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/UserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.features.integrations.rest.type;\n\nimport org.springframework.security.docs.features.integrations.rest.clientregistrationid.User;\nimport org.springframework.security.oauth2.client.annotation.ClientRegistrationId;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.service.annotation.GetExchange;\nimport org.springframework.web.service.annotation.HttpExchange;\n\n/**\n * Demonstrates a service for {@link ClientRegistrationId} at the type level.\n * @author Rob Winch\n */\n// tag::type[]\n@HttpExchange\n@ClientRegistrationId(\"github\")\npublic interface UserService {\n\n\t@GetExchange(\"/user\")\n\tUser getAuthenticatedUser();\n\n\t@GetExchange(\"/users/{username}/hovercard\")\n\tHovercard getHovercard(@PathVariable String username);\n\n}\n// end::type[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/authentication/reactivex509/CustomX509Configuration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.authentication.reactivex509;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.authentication.ReactivePreAuthenticatedAuthenticationManager;\nimport org.springframework.web.reactive.config.EnableWebFlux;\n\n/**\n * Demonstrates custom configuration for x509 reactive configuration.\n *\n * @author Rob Winch\n */\n@Configuration(proxyBeanMethods = false)\n@EnableWebFluxSecurity\n@EnableWebFlux\npublic class CustomX509Configuration {\n\n\t// tag::springSecurity[]\n\t@Bean\n\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\tSubjectX500PrincipalExtractor principalExtractor = new SubjectX500PrincipalExtractor();\n\t\tprincipalExtractor.setExtractPrincipalNameFromEmail(true);\n\n\t\t// @formatter:off\n\t\tUserDetails user = User\n\t\t\t.withUsername(\"luke@monkeymachine\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tReactiveUserDetailsService users = new MapReactiveUserDetailsService(user);\n\t\tReactiveAuthenticationManager authenticationManager = new ReactivePreAuthenticatedAuthenticationManager(users);\n\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.x509((x509) -> x509\n\t\t\t\t.principalExtractor(principalExtractor)\n\t\t\t\t.authenticationManager(authenticationManager)\n\t\t\t)\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t);\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::springSecurity[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/authentication/reactivex509/DefaultX509Configuration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.authentication.reactivex509;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.web.reactive.config.EnableWebFlux;\n\n/**\n * Demonstrates custom configuration for x509 reactive configuration.\n *\n * @author Rob Winch\n */\n@Configuration(proxyBeanMethods = false)\n@EnableWebFluxSecurity\n@EnableWebFlux\npublic class DefaultX509Configuration {\n\n\t// tag::springSecurity[]\n\t@Bean\n\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.x509(Customizer.withDefaults())\n\t\t\t.authorizeExchange((authorize) -> authorize\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t);\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::springSecurity[]\n\n\t@Bean\n\tReactiveUserDetailsService userDetailsService() {\n\t\t// @formatter:off\n\t\tUserDetails user = User\n\t\t\t\t.withUsername(\"rod\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\treturn new MapReactiveUserDetailsService(user);\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/authentication/reactivex509/X509ConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.authentication.reactivex509;\n\nimport java.io.InputStream;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.authentication.preauth.x509.X509TestUtils;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.server.WebFilter;\n\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;\nimport static org.springframework.test.web.reactive.server.UserWebTestClientConfigurer.x509;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class X509ConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\tWebTestClient client;\n\n\t@Autowired\n\tvoid setSpringSecurityFilterChain(WebFilter springSecurityFilterChain) {\n\t\tthis.client = WebTestClient.bindToController(WebTestClientBuilder.Http200RestController.class)\n\t\t\t.webFilter(springSecurityFilterChain)\n\t\t\t.apply(springSecurity())\n\t\t\t.configureClient()\n\t\t\t.build();\n\t}\n\n\t@Test\n\tvoid x509WhenDefaultX509Configuration() throws Exception {\n\t\tthis.spring.register(DefaultX509Configuration.class).autowire();\n\t\tX509Certificate certificate = loadCert(\"rod.cer\");\n\t\t// @formatter:off\n\t\tthis.client\n\t\t\t.mutateWith(x509(certificate))\n\t\t\t.get()\n\t\t\t.uri(\"/\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid x509WhenCustomX509Configuration() throws Exception {\n\t\tthis.spring.register(CustomX509Configuration.class).autowire();\n\t\tX509Certificate certificate = X509TestUtils.buildTestCertificate();\n\t\t// @formatter:off\n\t\tthis.client\n\t\t\t\t.mutateWith(x509(certificate))\n\t\t\t\t.get()\n\t\t\t\t.uri(\"/\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n\tprivate <T extends Certificate> T loadCert(String location) {\n\t\ttry (InputStream is = new ClassPathResource(location).getInputStream()) {\n\t\t\tCertificateFactory certFactory = CertificateFactory.getInstance(\"X.509\");\n\t\t\treturn (T) certFactory.generateCertificate(is);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/configuration/customizerbeanordering/CustomizerBeanOrderingConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.configuration.customizerbeanordering;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.web.reactive.config.EnableWebFlux;\n\n/**\n *\n */\n@EnableWebFlux\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\nclass CustomizerBeanOrderingConfiguration {\n\n\t// tag::sample[]\n\t@Bean // <4>\n\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeExchange((exchange) -> exchange\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t);\n\t\treturn http.build();\n\t\t// @formatter:on\n\t}\n\n\t@Bean\n\t@Order(Ordered.LOWEST_PRECEDENCE) // <2>\n\tCustomizer<ServerHttpSecurity> userAuthorization() {\n\t\t// @formatter:off\n\t\treturn (http) -> http\n\t\t\t.authorizeExchange((exchange) -> exchange\n\t\t\t\t.pathMatchers(\"/users/**\").hasRole(\"USER\")\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Bean\n\t@Order(Ordered.HIGHEST_PRECEDENCE) // <1>\n\tCustomizer<ServerHttpSecurity> adminAuthorization() {\n\t\t// @formatter:off\n\t\treturn (http) -> http\n\t\t\t.authorizeExchange((exchange) -> exchange\n\t\t\t\t.pathMatchers(\"/admins/**\").hasRole(\"ADMIN\")\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t// <3>\n\n\t@Bean\n\tCustomizer<ServerHttpSecurity.HeaderSpec> contentSecurityPolicy() {\n\t\t// @formatter:off\n\t\treturn (headers) -> headers\n\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t.policyDirectives(\"object-src 'none'\")\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Bean\n\tCustomizer<ServerHttpSecurity.HeaderSpec> contentTypeOptions() {\n\t\t// @formatter:off\n\t\treturn (headers) -> headers\n\t\t\t.contentTypeOptions(Customizer.withDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Bean\n\tCustomizer<ServerHttpSecurity.HttpsRedirectSpec> httpsRedirect() {\n\t\t// @formatter:off\n\t\treturn Customizer.withDefaults();\n\t\t// @formatter:on\n\t}\n\t// end::sample[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/configuration/customizerbeanordering/CustomizerBeanOrderingTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.configuration.customizerbeanordering;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class CustomizerBeanOrderingTests {\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate WebTestClient webTest;\n\n\t@Test\n\tvoid authorizationOrdered() throws Exception {\n\t\tthis.spring.register(\n\t\t\t\tCustomizerBeanOrderingConfiguration.class).autowire();\n\t\t// @formatter:off\n\t\tthis.webTest.mutateWith(mockUser(\"admin\").roles(\"ADMIN\"))\n\t\t\t.get()\n\t\t\t.uri(\"https://localhost/admins/1\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().isOk();\n\t\tthis.webTest.mutateWith(mockUser(\"user\").roles(\"USER\"))\n\t\t\t.get()\n\t\t\t.uri(\"https://localhost/admins/1\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().isForbidden();\n\t\tthis.webTest.mutateWith(mockUser(\"user\").roles(\"USER\"))\n\t\t\t.get()\n\t\t\t.uri(\"https://localhost/users/1\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().isOk();\n\t\tthis.webTest.mutateWith(mockUser(\"user\").roles(\"OTHER\"))\n\t\t\t.get()\n\t\t\t.uri(\"https://localhost/users/1\")\n\t\t\t.exchange()\n\t\t\t.expectStatus().isForbidden();\n\t\tthis.webTest.mutateWith(mockUser(\"authenticated\").roles(\"OTHER\"))\n\t\t\t\t.get()\n\t\t\t\t.uri(\"https://localhost/other\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/configuration/serverhttpsecuritycustomizerbean/ServerHttpSecurityCustomizerBeanConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.configuration.serverhttpsecuritycustomizerbean;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\n\n/**\n *\n */\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\nclass ServerHttpSecurityCustomizerBeanConfiguration {\n\n\t@Bean\n\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeExchange((exchange) -> exchange\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t);\n\t\treturn http.build();\n\t\t// @formatter:on\n\t}\n\n\t// tag::httpSecurityCustomizer[]\n\t@Bean\n\tCustomizer<ServerHttpSecurity> httpSecurityCustomizer() {\n\t\t// @formatter:off\n\t\treturn (http) -> http\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t\t// <1>\n\t\t\t\t\t.policyDirectives(\"object-src 'none'\")\n\t\t\t\t)\n\t\t\t)\n\t\t\t// <2>\n\t\t\t.redirectToHttps(Customizer.withDefaults());\n\t\t// @formatter:on\n\t}\n\t// end::httpSecurityCustomizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/configuration/serverhttpsecuritycustomizerbean/ServerHttpSecurityCustomizerBeanTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.configuration.serverhttpsecuritycustomizerbean;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class ServerHttpSecurityCustomizerBeanTests {\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate WebTestClient webTest;\n\n\t@Test\n\tvoid httpSecurityCustomizer() throws Exception {\n\t\tthis.spring.register(\n\t\t\t\tServerHttpSecurityCustomizerBeanConfiguration.class).autowire();\n\t\t// @formatter:off\n\t\tthis.webTest\n\t\t\t.get()\n\t\t\t.uri(\"http://localhost/\")\n\t\t\t.exchange()\n\t\t\t.expectHeader().location(\"https://localhost/\")\n\t\t\t.expectHeader()\n\t\t\t\t.value(\"Content-Security-Policy\", csp ->\n\t\t\t\t\tassertThat(csp).isEqualTo(\"object-src 'none'\")\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/configuration/toplevelcustomizerbean/TopLevelCustomizerBeanConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.configuration.toplevelcustomizerbean;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\n\n/**\n *\n */\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\npublic class TopLevelCustomizerBeanConfiguration {\n\n\t@Bean\n\tSecurityWebFilterChain springSecurity(ServerHttpSecurity http) {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeExchange((exchange) -> exchange\n\t\t\t\t.anyExchange().authenticated()\n\t\t\t);\n\t\treturn http.build();\n\t\t// @formatter:on\n\t}\n\n\t// tag::headersCustomizer[]\n\t@Bean\n\tCustomizer<ServerHttpSecurity.HeaderSpec> headersSecurity() {\n\t\t// @formatter:off\n\t\treturn (headers) -> headers\n\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t// <1>\n\t\t\t\t.policyDirectives(\"object-src 'none'\")\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\t// end::headersCustomizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/reactive/configuration/toplevelcustomizerbean/TopLevelCustomizerBeanTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.reactive.configuration.toplevelcustomizerbean;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.test.web.servlet.ResultMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class TopLevelCustomizerBeanTests {\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate WebTestClient webTest;\n\n\n\t@Test\n\tvoid headersCustomizer() throws Exception {\n\t\tthis.spring.register(TopLevelCustomizerBeanConfiguration.class).autowire();\n\t\t// @formatter:off\n\t\tthis.webTest\n\t\t\t.get()\n\t\t\t.uri(\"http://localhost/\")\n\t\t\t.exchange()\n\t\t\t.expectHeader()\n\t\t\t\t.value(\"Content-Security-Policy\", csp ->\n\t\t\t\t\t\tassertThat(csp).isEqualTo(\"object-src 'none'\")\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate static @NotNull ResultMatcher cspIsObjectSrcNone() {\n\t\treturn header().string(\"Content-Security-Policy\", \"object-src 'none'\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/addingcustomfilter/CustomFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.addingcustomfilter;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = {\n\t\tCustomFilterTests.UserDetailsConfig.class,\n\t\tCustomFilterTests.ApiController.class,\n\t\tSecurityConfig.class })\n@WebAppConfiguration\npublic class CustomFilterTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t\t.defaultRequest(get(\"/api\").with(user(\"user\")))\n\t\t\t\t.apply(springSecurity())\n\t\t\t\t.build();\n\t}\n\n\t@Test\n\tvoid tenantFilterWhenHeaderMissingThenAccessDenied() {\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t\t.isThrownBy(() -> this.mvc.perform(get(\"/api\")).andReturn());\n\t}\n\n\t@Test\n\tvoid tenantFilterWhenHeaderPresentThenContinuesFilterChain() throws Exception {\n\t\tthis.mvc.perform(get(\"/api\").header(\"X-Tenant-Id\", \"some-tenant-id\"))\n\t\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Configuration\n\tstatic class UserDetailsConfig {\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.roles(\"USER\")\n\t\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\t}\n\n\t@RestController\n\tstatic class ApiController {\n\n\t\t@GetMapping(\"/api\")\n\t\tString api() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/addingcustomfilter/SecurityConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.addingcustomfilter;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter;\nimport org.springframework.test.context.ContextConfiguration;\n\n@Configuration\n@ContextConfiguration(classes = { SecurityConfig.class })\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t// tag::snippet[]\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.addFilterAfter(new TenantFilter(), AnonymousAuthenticationFilter.class); // <1>\n\t\treturn http.build();\n\t}\n\t// end::snippet[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/addingcustomfilter/TenantFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.addingcustomfilter;\n\n// tag::snippet[]\nimport java.io.IOException;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.access.AccessDeniedException;\n\npublic class TenantFilter implements Filter {\n\n\t@Override\n\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n\t\tHttpServletRequest request = (HttpServletRequest) servletRequest;\n\t\tHttpServletResponse response = (HttpServletResponse) servletResponse;\n\n\t\tString tenantId = request.getHeader(\"X-Tenant-Id\"); // <1>\n\t\tboolean hasAccess = isUserAllowed(tenantId); // <2>\n\t\tif (hasAccess) {\n\t\t\tfilterChain.doFilter(request, response); // <3>\n\t\t\treturn;\n\t\t}\n\t\tthrow new AccessDeniedException(\"Access denied\"); // <4>\n\t}\n\n\tprivate boolean isUserAllowed(String tenantId) {\n\t\treturn \"some-tenant-id\".equals(tenantId);\n\t}\n\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/authorizationmanagerfactory/AuthorizationManagerFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.authorizationmanagerfactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class AuthorizationManagerFactoryTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY })\n\tvoid getWhenAuthenticatedWithPasswordAndOttThenPermits() throws Exception {\n\t\tthis.spring.register(UseAuthorizationManagerFactoryConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\tvoid getWhenAuthenticatedWithPasswordThenRedirectsToOtt() throws Exception {\n\t\tthis.spring.register(UseAuthorizationManagerFactoryConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.OTT_AUTHORITY)\n\tvoid getWhenAuthenticatedWithOttThenRedirectsToPassword() throws Exception {\n\t\tthis.spring.register(UseAuthorizationManagerFactoryConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getWhenAuthenticatedThenRedirectsToPassword() throws Exception {\n\t\tthis.spring.register(UseAuthorizationManagerFactoryConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getWhenUnauthenticatedThenRedirectsToBoth() throws Exception {\n\t\tthis.spring.register(UseAuthorizationManagerFactoryConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/authorizationmanagerfactory/UseAuthorizationManagerFactoryConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.authorizationmanagerfactory;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass UseAuthorizationManagerFactoryConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t// tag::authorizationManagerFactoryBean[]\n\t@Bean\n\tAuthorizationManagerFactory<Object> authz() {\n\t\treturn AuthorizationManagerFactories.multiFactor()\n\t\t\t.requireFactors(\n\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY\n\t\t\t)\n\t\t\t.build();\n\t}\n\t// end::authorizationManagerFactoryBean[]\n\n\t// tag::customizer[]\n\t@Bean\n\tCustomizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer() {\n\t\treturn (builder) -> builder.when((auth) -> \"admin\".equals(auth.getName()));\n\t}\n\t// end::customizer[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/emfa/EnableMultiFactorAuthenticationConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.emfa;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\n// tag::enable-mfa[]\n@EnableMultiFactorAuthentication(authorities = {\n\tFactorGrantedAuthority.PASSWORD_AUTHORITY,\n\tFactorGrantedAuthority.OTT_AUTHORITY })\n// end::enable-mfa[]\npublic class EnableMultiFactorAuthenticationConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t// <1>\n\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t// <2>\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t// <3>\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/emfa/EnableMultiFactorAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.emfa;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class EnableMultiFactorAuthenticationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY, \"ROLE_USER\" })\n\tvoid getWhenAuthenticatedWithPasswordAndOttThenPermits() throws Exception {\n\t\tthis.spring.register(EnableMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\tvoid getWhenAuthenticatedWithPasswordThenRedirectsToOtt() throws Exception {\n\t\tthis.spring.register(EnableMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.OTT_AUTHORITY)\n\tvoid getWhenAuthenticatedWithOttThenRedirectsToPassword() throws Exception {\n\t\tthis.spring.register(EnableMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getWhenAuthenticatedThenRedirectsToPassword() throws Exception {\n\t\tthis.spring.register(EnableMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getWhenUnauthenticatedThenRedirectsToBoth() throws Exception {\n\t\tthis.spring.register(EnableMultiFactorAuthenticationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/hasallauthorities/ListAuthoritiesConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.hasallauthorities;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass ListAuthoritiesConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t// <1>\n\t\t\t\t.anyRequest().hasAllAuthorities(\n\t\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY\n\t\t\t\t)\n\t\t\t)\n\t\t\t// <2>\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t@Bean\n\tUserDetailsService users() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/hasallauthorities/MultiFactorAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.hasallauthorities;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class MultiFactorAuthenticationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY })\n\tvoid getWhenAuthenticatedWithPasswordAndOttThenPermits() throws Exception {\n\t\tthis.spring.register(ListAuthoritiesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\tvoid getWhenAuthenticatedWithPasswordThenRedirectsToOtt() throws Exception {\n\t\tthis.spring.register(ListAuthoritiesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.OTT_AUTHORITY)\n\tvoid getWhenAuthenticatedWithOttThenRedirectsToPassword() throws Exception {\n\t\tthis.spring.register(ListAuthoritiesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getWhenAuthenticatedThenRedirectsToPassword() throws Exception {\n\t\tthis.spring.register(ListAuthoritiesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getWhenUnauthenticatedThenRedirectsToBoth() throws Exception {\n\t\tthis.spring.register(ListAuthoritiesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/hasallauthorities/MultipleAuthorizationRulesConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.hasallauthorities;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class MultipleAuthorizationRulesConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t// <1>\n\t\t\t\t.requestMatchers(\"/admin/**\").hasAllAuthorities(\n\t\t\t\t\t\"ROLE_ADMIN\",\n\t\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY\n\t\t\t\t)\n\t\t\t\t// <2>\n\t\t\t\t.anyRequest().hasAllAuthorities(\n\t\t\t\t\t\"ROLE_USER\",\n\t\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY\n\t\t\t\t)\n\t\t\t)\n\t\t\t// <3>\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/hasallauthorities/MultipleAuthorizationRulesConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.hasallauthorities;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class MultipleAuthorizationRulesConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY, \"ROLE_USER\" })\n\tvoid getWhenAuthenticatedWithPasswordAndOttThenPermits() throws Exception {\n\t\tthis.spring.register(MultipleAuthorizationRulesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\tvoid getWhenAuthenticatedWithPasswordThenRedirectsToOtt() throws Exception {\n\t\tthis.spring.register(MultipleAuthorizationRulesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.OTT_AUTHORITY)\n\tvoid getWhenAuthenticatedWithOttThenRedirectsToPassword() throws Exception {\n\t\tthis.spring.register(MultipleAuthorizationRulesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getWhenAuthenticatedThenRedirectsToPassword() throws Exception {\n\t\tthis.spring.register(MultipleAuthorizationRulesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getWhenUnauthenticatedThenRedirectsToBoth() throws Exception {\n\t\tthis.spring.register(MultipleAuthorizationRulesConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/mfawhencustomconditions/CustomizerAuthorizationManagerFactoryConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.mfawhencustomconditions;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.config.Customizer;\n\n@Configuration(proxyBeanMethods = false)\nclass CustomizerAuthorizationManagerFactoryConfiguration {\n\n\t// tag::customizer[]\n\t@Bean\n\tCustomizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer() {\n\t\treturn (builder) -> builder.when((auth) -> \"admin\".equals(auth.getName()));\n\t}\n\t// end::customizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/mfawhenwebauthnregistered/WebAuthnConditionConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.mfawhenwebauthnregistered;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication;\nimport org.springframework.security.config.annotation.authorization.MultiFactorCondition;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository;\nimport org.springframework.security.web.webauthn.management.MapUserCredentialRepository;\nimport org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\n// tag::enable-mfa-webauthn[]\n@EnableMultiFactorAuthentication(\n\tauthorities = {\n\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\tFactorGrantedAuthority.WEBAUTHN_AUTHORITY\n\t},\n\twhen = MultiFactorCondition.WEBAUTHN_REGISTERED\n)\npublic class WebAuthnConditionConfiguration {\n\n\t@Bean\n\tpublic PublicKeyCredentialUserEntityRepository userEntityRepository() {\n\t\treturn new MapPublicKeyCredentialUserEntityRepository();\n\t}\n\n\t@Bean\n\tpublic UserCredentialRepository userCredentialRepository() {\n\t\treturn new MapUserCredentialRepository();\n\t}\n\n}\n// end::enable-mfa-webauthn[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.obtainingmoreauthorization;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.stereotype.Component;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass MissingAuthorityConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http, ScopeRetrievingAuthenticationEntryPoint oauth2) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(\"/profile/**\").hasAuthority(\"SCOPE_profile:read\")\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.x509(Customizer.withDefaults())\n\t\t\t.oauth2Login(Customizer.withDefaults())\n\t\t\t.exceptionHandling((exceptions) -> exceptions\n\t\t\t\t.defaultDeniedHandlerForMissingAuthority(oauth2, \"SCOPE_profile:read\")\n\t\t\t);\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t// tag::authorizationManagerFactoryBean[]\n\t@Bean\n\tAuthorizationManagerFactory<Object> authz() {\n\t\treturn AuthorizationManagerFactories.multiFactor()\n\t\t\t\t.requireFactors(FactorGrantedAuthority.X509_AUTHORITY, FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY)\n\t\t\t\t.build();\n\t}\n\t// end::authorizationManagerFactoryBean[]\n\n\t// tag::authenticationEntryPoint[]\n\t@Component\n\tclass ScopeRetrievingAuthenticationEntryPoint implements AuthenticationEntryPoint {\n\t\t@Override\n\t\tpublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tresponse.sendRedirect(\"https://authz.example.org/authorize?scope=profile:read\");\n\t\t}\n\t}\n\t// end::authenticationEntryPoint[]\n\n\t@Bean\n\tClientRegistrationRepository clients() {\n\t\treturn new InMemoryClientRegistrationRepository(TestClientRegistrations.clientRegistration().build());\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/obtainingmoreauthorization/ObtainingMoreAuthorizationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.obtainingmoreauthorization;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class ObtainingMoreAuthorizationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser\n\tvoid profileWhenScopeConfigurationThenDenies() throws Exception {\n\t\tthis.spring.register(ScopeConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/profile\"))\n\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.X509_AUTHORITY, FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY })\n\tvoid profileWhenMissingAuthorityConfigurationThenRedirectsToAuthorizationServer() throws Exception {\n\t\tthis.spring.register(MissingAuthorityConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/profile\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"https://authz.example.org/authorize?scope=profile:read\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { \"SCOPE_profile:read\" })\n\tvoid profileWhenMissingX509WithOttThenForbidden() throws Exception {\n\t\tthis.spring.register(MissingAuthorityConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/profile\"))\n\t\t\t.andExpect(status().isForbidden());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.X509_AUTHORITY, FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY, \"SCOPE_profile:read\" })\n\tvoid profileWhenAuthenticatedAndHasScopeThenPermits() throws Exception {\n\t\tthis.spring.register(MissingAuthorityConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/profile\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/obtainingmoreauthorization/ScopeConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.obtainingmoreauthorization;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.web.SecurityFilterChain;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class ScopeConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(\"/profile/**\").hasAuthority(\"SCOPE_profile:read\")\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.x509(Customizer.withDefaults())\n\t\t\t.oauth2Login(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t@Bean\n\tClientRegistrationRepository clients() {\n\t\treturn new InMemoryClientRegistrationRepository(TestClientRegistrations.clientRegistration().build());\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/programmaticmfa/AdminMfaAuthorizationManagerConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.programmaticmfa;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass AdminMfaAuthorizationManagerConfiguration {\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t// <1>\n\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t// <2>\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t// tag::authorizationManagerFactory[]\n\t@Bean\n\tAuthorizationManagerFactory<Object> authorizationManagerFactory() {\n\t\t// <3>\n\t\treturn AuthorizationManagerFactories.multiFactor()\n\t\t\t// <1>\n\t\t\t.requireFactors(FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t// <2>\n\t\t\t.when((auth) -> \"admin\".equals(auth.getName()))\n\t\t\t.build();\n\t}\n\t// end::authorizationManagerFactory[]\n\n\t@Bean\n\tpublic UserDetailsService users() {\n\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/programmaticmfa/AdminMfaAuthorizationManagerConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.programmaticmfa;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({SpringExtension.class, SpringTestContextExtension.class})\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class AdminMfaAuthorizationManagerConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser(username = \"admin\")\n\tvoid getWhenAdminThenRedirectsToOtt() throws Exception {\n\t\tthis.spring.register(AdminMfaAuthorizationManagerConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.type=password&factor.reason=missing&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getWhenNotAdminThenAllows() throws Exception {\n\t\tthis.spring.register(AdminMfaAuthorizationManagerConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"admin\", authorities = { FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY })\n\tvoid getWhenAdminAndHasFactorThenAllows() throws Exception {\n\t\tthis.spring.register(AdminMfaAuthorizationManagerConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"admin\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/raammfa/RequiredAuthoritiesAuthorizationManagerConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.raammfa;\n\nimport java.util.List;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.authorization.MapRequiredAuthoritiesRepository;\nimport org.springframework.security.authorization.RequiredAuthoritiesAuthorizationManager;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass RequiredAuthoritiesAuthorizationManagerConfiguration {\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\") // <1>\n\t\t\t\t.anyRequest().authenticated() // <2>\n\t\t\t)\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t// tag::authorizationManager[]\n\t@Bean\n\tRequiredAuthoritiesAuthorizationManager<Object> adminAuthorization() {\n\t\t// <1>\n\t\tMapRequiredAuthoritiesRepository authorities = new MapRequiredAuthoritiesRepository();\n\t\tauthorities.saveRequiredAuthorities(\"admin\", List.of(\n\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\tFactorGrantedAuthority.OTT_AUTHORITY)\n\t\t);\n\t\t// <2>\n\t\treturn new RequiredAuthoritiesAuthorizationManager<>(authorities);\n\t}\n\t// end::authorizationManager[]\n\n\t// tag::authorizationManagerFactory[]\n\t@Bean\n\tAuthorizationManagerFactory<Object> authorizationManagerFactory(\n\t\t\tRequiredAuthoritiesAuthorizationManager admins) {\n\t\tDefaultAuthorizationManagerFactory<Object> defaults = new DefaultAuthorizationManagerFactory<>();\n\t\t// <1>\n\t\tdefaults.setAdditionalAuthorization(admins);\n\t\t// <2>\n\t\treturn defaults;\n\t}\n\t// end::authorizationManagerFactory[]\n\n\t@Bean\n\tpublic UserDetailsService users() {\n\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/raammfa/RequiredAuthoritiesAuthorizationManagerConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.programmaticmfa;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({SpringExtension.class, SpringTestContextExtension.class})\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class RequiredAuthoritiesAuthorizationManagerConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser(username = \"admin\")\n\tvoid getWhenAdminThenRedirectsToOtt() throws Exception {\n\t\tthis.spring.register(AdminMfaAuthorizationManagerConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.type=password&factor.reason=missing&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid getWhenNotAdminThenAllows() throws Exception {\n\t\tthis.spring.register(AdminMfaAuthorizationManagerConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"admin\", authorities = { FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY })\n\tvoid getWhenAdminAndHasFactorThenAllows() throws Exception {\n\t\tthis.spring.register(AdminMfaAuthorizationManagerConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"admin\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/reauthentication/ReauthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.reauthentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class ReauthenticationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser\n\tvoid formLoginWhenSimpleConfigurationThenPermits() throws Exception {\n\t\tthis.spring.register(SimpleConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid formLoginWhenRequireOttConfigurationThenRedirectsToOtt() throws Exception {\n\t\tthis.spring.register(RequireOttConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/profile\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = FactorGrantedAuthority.OTT_AUTHORITY)\n\tvoid ottWhenRequireOttConfigurationThenAllows() throws Exception {\n\t\tthis.spring.register(RequireOttConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/profile\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/reauthentication/RequireOttConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.reauthentication;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class RequireOttConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t.requestMatchers(\"/profile/**\").hasAuthority(FactorGrantedAuthority.OTT_AUTHORITY) // <1>\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/reauthentication/SimpleConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.reauthentication;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class SimpleConfiguration {\n\t// tag::httpSecurity[]\n\t@Bean\n\tpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/selectivemfa/SelectiveMfaConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.selectivemfa;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n// tag::enable-mfa[]\n@EnableMultiFactorAuthentication(authorities = {})\n// end::enable-mfa[]\n@Configuration(proxyBeanMethods = false)\nclass SelectiveMfaConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\t// <1>\n\t\tvar mfa = AuthorizationManagerFactories.multiFactor()\n\t\t\t.requireFactors(\n\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY,\n\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY\n\t\t\t)\n\t\t\t.build();\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t// <2>\n\t\t\t\t.requestMatchers(\"/admin/**\").access(mfa.hasRole(\"ADMIN\"))\n\t\t\t\t// <3>\n\t\t\t\t.requestMatchers(\"/user/settings/**\").access(mfa.authenticated())\n\t\t\t\t// <4>\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t// <5>\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.oneTimeTokenLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n\n\t@Bean\n\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler() {\n\t\treturn new RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/selectivemfa/SelectiveMfaConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.selectivemfa;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class SelectiveMfaConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, \"ROLE_ADMIN\" })\n\tvoid adminWhenMissingOttThenRequired() throws Exception {\n\t\tthis.spring.register(SelectiveMfaConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/admin/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrlPattern(\"/login?*\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY, \"ROLE_ADMIN\" })\n\tvoid adminWhenMfaThenAllowed() throws Exception {\n\t\tthis.spring.register(SelectiveMfaConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/admin/\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, \"ROLE_ADMIN\" })\n\tvoid userSettingsRequiresMfa() throws Exception {\n\t\tthis.spring.register(SelectiveMfaConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/admin/\"))\n\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t.andExpect(redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, \"ROLE_USER\" })\n\tvoid userSettingsWhenMissingOttThenRequired() throws Exception {\n\t\tthis.spring.register(SelectiveMfaConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user/settings/\"))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrlPattern(\"/login?*\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"USER\")\n\tvoid rootDoesNotRequireMfa() throws Exception {\n\t\tthis.spring.register(SelectiveMfaConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\"))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/servletauthenticationauthentication/CopyAuthoritiesTests.java",
    "content": "package org.springframework.security.docs.servlet.authentication.servletauthenticationauthentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthentication;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\npublic class CopyAuthoritiesTests {\n\t@Test\n\tvoid toBuilderWhenApplyThenCopies() {\n\t\tUsernamePasswordAuthenticationToken previous = new UsernamePasswordAuthenticationToken(\"alice\", \"pass\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\tFactorGrantedAuthority.PASSWORD_AUTHORITY));\n\t\tSecurityContextHolder.getContext().setAuthentication(previous);\n\t\tAuthentication latest = new OneTimeTokenAuthentication(\"bob\",\n\t\t\t\tAuthorityUtils.createAuthorityList(FactorGrantedAuthority.OTT_AUTHORITY));\n\t\tAuthenticationManager authenticationManager = mock(AuthenticationManager.class);\n\t\tgiven(authenticationManager.authenticate(any())).willReturn(latest);\n\t\tAuthentication authenticationRequest = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\t// tag::springSecurity[]\n\t\tAuthentication lastestResult = authenticationManager.authenticate(authenticationRequest);\n\t\tAuthentication previousResult = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (previousResult != null && previousResult.isAuthenticated()) {\n\t\t\tlastestResult = lastestResult.toBuilder()\n\t\t\t\t\t.authorities((a) -> a.addAll(previous.getAuthorities()))\n\t\t\t\t\t.build();\n\t\t}\n\t\t// end::springSecurity[]\n\t\tSecurityAssertions.assertThat(lastestResult).hasAuthorities(\n\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY);\n\t\tSecurityContextHolder.clearContext();\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/servletx509config/CustomX509Configuration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.servletx509config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\n/**\n * Demonstrates custom configuration for x509 reactive configuration.\n *\n * @author Rob Winch\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class CustomX509Configuration {\n\n\t// tag::springSecurity[]\n\t@Bean\n\tDefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\tSubjectX500PrincipalExtractor principalExtractor = new SubjectX500PrincipalExtractor();\n\t\tprincipalExtractor.setExtractPrincipalNameFromEmail(true);\n\n\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.x509((x509) -> x509\n\t\t\t\t.x509PrincipalExtractor(principalExtractor)\n\t\t\t)\n\t\t\t.authorizeHttpRequests((exchanges) -> exchanges\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t);\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::springSecurity[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\t// @formatter:off\n\t\tUserDetails user = User\n\t\t\t\t.withUsername(\"luke@monkeymachine\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\treturn new InMemoryUserDetailsManager(user);\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/servletx509config/DefaultX509Configuration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.servletx509config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\n/**\n * Demonstrates custom configuration for x509 reactive configuration.\n *\n * @author Rob Winch\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class DefaultX509Configuration {\n\n\t// tag::springSecurity[]\n\t@Bean\n\tDefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.x509(Customizer.withDefaults())\n\t\t\t.authorizeHttpRequests((exchanges) -> exchanges\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t);\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::springSecurity[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\t// @formatter:off\n\t\tUserDetails user = User\n\t\t\t.withUsername(\"rod\")\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\t// @formatter:on\n\n\t\treturn new InMemoryUserDetailsManager(user);\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/servletx509config/X509ConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.servletx509config;\n\nimport java.security.cert.X509Certificate;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.authentication.preauth.x509.X509TestUtils;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class X509ConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\tvoid x509WhenDefaultX509Configuration() throws Exception {\n\t\tthis.spring.register(DefaultX509Configuration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\").with(x509(\"rod.cer\")))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid x509WhenDefaultX509ConfigurationXml() throws Exception {\n\t\tthis.spring.testConfigLocations(\"DefaultX509Configuration.xml\").autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\").with(x509(\"rod.cer\")))\n\t\t\t.andExpect(authenticated().withUsername(\"rod\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid x509WhenCustomX509Configuration() throws Exception {\n\t\tthis.spring.register(CustomX509Configuration.class, Http200Controller.class).autowire();\n\t\tX509Certificate certificate = X509TestUtils.buildTestCertificate();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/\").with(x509(certificate)))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(authenticated().withUsername(\"luke@monkeymachine\"));\n\t\t// @formatter:on\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/tokenbasedremembermeservices/CustomAlgorithmRememberMeServicesConfiguration.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.tokenbasedremembermeservices;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.RememberMeTokenAlgorithm;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\n/**\n * Demonstrates custom algorithm for remember me configuration.\n *\n * @author Ngoc Nhan\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class CustomAlgorithmRememberMeServicesConfiguration {\n\n\t// tag::snippet[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.rememberMe((remember) -> remember\n\t\t\t\t\t\t.rememberMeServices(rememberMeServices)\n\t\t\t\t);\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\n\t@Bean\n\tRememberMeServices rememberMeServices(UserDetailsService userDetailsService) {\n\t\tRememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;\n\t\tTokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(\"myKey\", userDetailsService,\n\t\t\t\tencodingAlgorithm);\n\t\trememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);\n\t\treturn rememberMe;\n\t}\n\t// end::snippet[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/tokenbasedremembermeservices/DefaultAlgorithmRememberMeServicesConfiguration.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.tokenbasedremembermeservices;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.RememberMeAuthenticationProvider;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\n/**\n * Demonstrates default algorithm for remember me configuration.\n *\n * @author Ngoc Nhan\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class DefaultAlgorithmRememberMeServicesConfiguration {\n\n\t// tag::snippet[]\n\t@Bean\n\tRememberMeServices rememberMeServices(UserDetailsService userDetailsService) {\n\t\treturn new TokenBasedRememberMeServices(\"myKey\", userDetailsService);\n\t}\n\n\t@Bean\n\tRememberMeAuthenticationFilter rememberMeFilter(AuthenticationManager authenticationManager,\n\t\t\tTokenBasedRememberMeServices rememberMeServices) {\n\t\treturn new RememberMeAuthenticationFilter(authenticationManager, rememberMeServices);\n\t}\n\n\t@Bean\n\tRememberMeAuthenticationProvider rememberMeAuthenticationProvider() {\n\t\treturn new RememberMeAuthenticationProvider(\"myKey\");\n\t}\n\t// end::snippet[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/validduration/ValidDurationConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.authentication.validduration;\n\nimport java.time.Duration;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass ValidDurationConfiguration {\n\n\t// tag::httpSecurity[]\n\t@Bean\n\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\t// <1>\n\t\tvar passwordIn30m = AuthorizationManagerFactories.multiFactor()\n\t\t\t.requireFactor( (factor) -> factor\n\t\t\t\t.passwordAuthority()\n\t\t\t\t.validDuration(Duration.ofMinutes(30))\n\t\t\t)\n\t\t\t.build();\n\t\t// <2>\n\t\tvar passwordInHour = AuthorizationManagerFactories.multiFactor()\n\t\t\t.requireFactor( (factor) -> factor\n\t\t\t\t.passwordAuthority()\n\t\t\t\t.validDuration(Duration.ofHours(1))\n\t\t\t)\n\t\t\t.build();\n\t\thttp\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t// <3>\n\t\t\t\t.requestMatchers(\"/admin/**\").access(passwordIn30m.hasRole(\"ADMIN\"))\n\t\t\t\t// <4>\n\t\t\t\t.requestMatchers(\"/user/settings/**\").access(passwordInHour.authenticated())\n\t\t\t\t// <5>\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t)\n\t\t\t// <6>\n\t\t\t.formLogin(Customizer.withDefaults());\n\t\t// @formatter:on\n\t\treturn http.build();\n\t}\n\t// end::httpSecurity[]\n\n\t@Bean\n\tUserDetailsService userDetailsService() {\n\t\treturn new InMemoryUserDetailsManager(\n\t\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t\t.authorities(\"app\")\n\t\t\t\t\t\t.build()\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authentication/validduration/ValidDurationConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authentication.validduration;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.docs.servlet.authentication.servletx509config.CustomX509Configuration;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests {@link CustomX509Configuration}.\n *\n * @author Rob Winch\n */\n@ExtendWith({ SpringExtension.class, SpringTestContextExtension.class })\n@TestExecutionListeners(WithSecurityContextTestExecutionListener.class)\npublic class ValidDurationConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\tvoid adminWhenExpiredThenRequired() throws Exception {\n\t\tthis.spring.register(\n\t\t\t\tValidDurationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/admin/\").with(admin(Duration.ofMinutes(31))))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrlPattern(\"/login?*\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid adminWhenNotExpiredThenOk() throws Exception {\n\t\tthis.spring.register(\n\t\t\t\tValidDurationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/admin/\").with(admin(Duration.ofMinutes(29))))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid settingsWhenExpiredThenRequired() throws Exception {\n\t\tthis.spring.register(\n\t\t\t\tValidDurationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user/settings\").with(user(Duration.ofMinutes(61))))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andExpect(redirectedUrlPattern(\"/login?*\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid settingsWhenNotExpiredThenOk() throws Exception {\n\t\tthis.spring.register(\n\t\t\t\tValidDurationConfiguration.class, Http200Controller.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user/settings\").with(user(Duration.ofMinutes(59))))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\tprivate static RequestPostProcessor admin(Duration sinceAuthn) {\n\t\treturn authn(\"admin\", sinceAuthn);\n\t}\n\n\tprivate static RequestPostProcessor user(Duration sinceAuthn) {\n\t\treturn authn(\"user\", sinceAuthn);\n\t}\n\n\tprivate static RequestPostProcessor authn(String username, Duration sinceAuthn) {\n\t\tInstant issuedAt = Instant.now().minus(sinceAuthn);\n\t\tFactorGrantedAuthority factor = FactorGrantedAuthority\n\t\t\t\t.withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.build();\n\t\tString role = username.toUpperCase();\n\t\tTestingAuthenticationToken authn = new TestingAuthenticationToken(username, \"\",\n\t\t\t\tfactor, new SimpleGrantedAuthority(\"ROLE_\" + role));\n\t\treturn authentication(authn);\n\t}\n\n\t@RestController\n\tstatic class Http200Controller {\n\t\t@GetMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authorization/authzauthorizationmanagerfactory/AuthorizationManagerFactoryConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authorization.authzauthorizationmanagerfactory;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\n\n/**\n * Documentation for {@link AuthorizationManagerFactory}.\n *\n * @author Steve Riesenberg\n */\n@Configuration(proxyBeanMethods = false)\npublic class AuthorizationManagerFactoryConfiguration {\n\n\t// tag::config[]\n\t@Bean\n\t<T> AuthorizationManagerFactory<T> authorizationManagerFactory() {\n\t\tDefaultAuthorizationManagerFactory<T> authorizationManagerFactory =\n\t\t\t\tnew DefaultAuthorizationManagerFactory<>();\n\t\tauthorizationManagerFactory.setTrustResolver(getAuthenticationTrustResolver());\n\t\tauthorizationManagerFactory.setRoleHierarchy(getRoleHierarchy());\n\t\tauthorizationManagerFactory.setRolePrefix(\"role_\");\n\n\t\treturn authorizationManagerFactory;\n\t}\n\t// end::config[]\n\n\tprivate static AuthenticationTrustResolverImpl getAuthenticationTrustResolver() {\n\t\tAuthenticationTrustResolverImpl authenticationTrustResolver =\n\t\t\t\tnew AuthenticationTrustResolverImpl();\n\t\tauthenticationTrustResolver.setAnonymousClass(Anonymous.class);\n\t\tauthenticationTrustResolver.setRememberMeClass(RememberMe.class);\n\n\t\treturn authenticationTrustResolver;\n\t}\n\n\tprivate static RoleHierarchyImpl getRoleHierarchy() {\n\t\treturn RoleHierarchyImpl.fromHierarchy(\"role_admin > role_user\");\n\t}\n\n\tstatic class Anonymous extends TestingAuthenticationToken {\n\n\t\tAnonymous(String principal) {\n\t\t\tsuper(principal, \"\", \"role_anonymous\");\n\t\t}\n\n\t}\n\n\tstatic class RememberMe extends TestingAuthenticationToken {\n\n\t\tRememberMe(String principal) {\n\t\t\tsuper(principal, \"\", \"role_rememberMe\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authorization/authzauthorizationmanagerfactory/AuthorizationManagerFactoryConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authorization.authzauthorizationmanagerfactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.docs.servlet.authorization.authzauthorizationmanagerfactory.AuthorizationManagerFactoryConfiguration.Anonymous;\nimport org.springframework.security.docs.servlet.authorization.authzauthorizationmanagerfactory.AuthorizationManagerFactoryConfiguration.RememberMe;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link AuthorizationManagerFactoryConfiguration}.\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class AuthorizationManagerFactoryConfigurationTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\tvoid getAnonymousWhenCustomAnonymousClassThenOk() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new Anonymous(\"anonymous\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/anonymous\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getAnonymousWhenAuthenticatedThenForbidden() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"role_user\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/anonymous\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getRememberMeWhenCustomRememberMeClassThenOk() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new RememberMe(\"rememberMe\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/rememberMe\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getRememberMeWhenAuthenticatedThenForbidden() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"role_user\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/rememberMe\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getUserWhenCustomUserRoleThenOk() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"role_user\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getUserWhenCustomAdminRoleThenOk() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"admin\", \"\", \"role_admin\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getPreAuthorizeWhenCustomUserRoleThenOk() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"role_user\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/preAuthorize\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getPreAuthorizeWhenCustomAdminRoleThenOk() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"admin\", \"\", \"role_admin\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/preAuthorize\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getPreAuthorizeWhenOtherRoleThenForbidden() throws Exception {\n\t\tthis.spring.register(AuthorizationManagerFactoryConfiguration.class, SecurityConfiguration.class,\n\t\t\t\tTestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"other\", \"\", \"role_other\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/preAuthorize\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\t@EnableMethodSecurity\n\t@Configuration\n\tstatic class SecurityConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.requestMatchers(\"/anonymous\").anonymous()\n\t\t\t\t\t.requestMatchers(\"/rememberMe\").rememberMe()\n\t\t\t\t\t.requestMatchers(\"/user\").hasRole(\"user\")\n\t\t\t\t\t.requestMatchers(\"/preAuthorize\").permitAll()\n\t\t\t\t\t.anyRequest().denyAll()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class TestController {\n\n\t\t@GetMapping({ \"/anonymous\", \"/rememberMe\", \"/user\" })\n\t\t@ResponseStatus(HttpStatus.OK)\n\t\tvoid httpRequest() {\n\t\t}\n\n\t\t@GetMapping(\"/preAuthorize\")\n\t\t@ResponseStatus(HttpStatus.OK)\n\t\t@PreAuthorize(\"hasRole('user')\")\n\t\tvoid preAuthorize() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authorization/authzconditionalauthorizationmanager/ConditionalAuthorizationManagerExample.java",
    "content": "package org.springframework.security.docs.servlet.authorization.authzconditionalauthorizationmanager;\n\nimport java.util.function.Predicate;\n\nimport org.springframework.security.authorization.AllRequiredFactorsAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.ConditionalAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\n\npublic class ConditionalAuthorizationManagerExample {\n\n\tpublic void configure(MfaRepository mfaRepository) {\n\t\t// tag::conditionalAuthorizationManager[]\n\t\tPredicate<Authentication> whenUserHasMfa = (auth) -> mfaRepository.hasRegisteredMfa(auth.getName());\n\t\tAuthorizationManager<RequestAuthorizationContext> mfaRequired = AllRequiredFactorsAuthorizationManager\n\t\t\t.<RequestAuthorizationContext>builder()\n\t\t\t.requireFactor((f) -> f.passwordAuthority())\n\t\t\t.requireFactor((f) -> f.webauthnAuthority())\n\t\t\t.build();\n\t\tAuthorizationManager<RequestAuthorizationContext> manager = ConditionalAuthorizationManager.<RequestAuthorizationContext>when(whenUserHasMfa)\n\t\t\t.whenTrue(mfaRequired)\n\t\t\t.build();\n\t\t// end::conditionalAuthorizationManager[]\n\t}\n\n\tinterface MfaRepository {\n\n\t\tboolean hasRegisteredMfa(String username);\n\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authorization/customizingauthorizationmanagers/CustomHttpRequestsAuthorizationManagerFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authorization.customizingauthorizationmanagers;\n\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.AuthorizationManagers;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.stereotype.Component;\n\n/**\n * Documentation for {@link AuthorizationManagerFactory}.\n *\n * @author Steve Riesenberg\n */\n// tag::class[]\n@Component\npublic class CustomHttpRequestsAuthorizationManagerFactory\n\t\timplements AuthorizationManagerFactory<RequestAuthorizationContext> {\n\n\tprivate final AuthorizationManagerFactory<RequestAuthorizationContext> delegate =\n\t\t\tnew DefaultAuthorizationManagerFactory<>();\n\n\t@Override\n\tpublic AuthorizationManager<RequestAuthorizationContext> authenticated() {\n\t\treturn AuthorizationManagers.allOf(\n\t\t\tthis.delegate.authenticated(),\n\t\t\tthis.delegate.hasRole(\"USER\")\n\t\t);\n\t}\n\n}\n// end::class[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authorization/customizingauthorizationmanagers/CustomHttpRequestsAuthorizationManagerFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authorization.customizingauthorizationmanagers;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link CustomHttpRequestsAuthorizationManagerFactory}.\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class CustomHttpRequestsAuthorizationManagerFactoryTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\tvoid getHelloWhenAnonymousThenForbidden() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/hello\").with(anonymous()))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(unauthenticated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getHelloWhenAuthenticatedWithNoRolesThenForbidden() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", Collections.emptyList());\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/hello\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getHelloWhenAuthenticatedWithUserRoleThenOk() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"ROLE_USER\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/hello\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getHelloWhenAuthenticatedWithOtherRoleThenForbidden() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"ROLE_OTHER\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/hello\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class SecurityConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tCustomHttpRequestsAuthorizationManagerFactory customHttpRequestsAuthorizationManagerFactory() {\n\t\t\treturn new CustomHttpRequestsAuthorizationManagerFactory();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class TestController {\n\n\t\t@GetMapping(\"/**\")\n\t\t@ResponseStatus(HttpStatus.OK)\n\t\tvoid ok() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authorization/customizingauthorizationmanagers/CustomMethodInvocationAuthorizationManagerFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authorization.customizingauthorizationmanagers;\n\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.AuthorizationManagers;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.stereotype.Component;\n\n/**\n * Documentation for {@link AuthorizationManagerFactory}.\n *\n * @author Steve Riesenberg\n */\n// tag::class[]\n@Component\npublic class CustomMethodInvocationAuthorizationManagerFactory\n\t\timplements AuthorizationManagerFactory<MethodInvocation> {\n\n\tprivate final AuthorizationManagerFactory<MethodInvocation> delegate =\n\t\t\tnew DefaultAuthorizationManagerFactory<>();\n\n\t@Override\n\tpublic AuthorizationManager<MethodInvocation> hasRole(String role) {\n\t\treturn AuthorizationManagers.anyOf(\n\t\t\tthis.delegate.hasRole(role),\n\t\t\tthis.delegate.hasRole(\"ADMIN\")\n\t\t);\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<MethodInvocation> hasAnyRole(String... roles) {\n\t\treturn AuthorizationManagers.anyOf(\n\t\t\tthis.delegate.hasAnyRole(roles),\n\t\t\tthis.delegate.hasRole(\"ADMIN\")\n\t\t);\n\t}\n\n}\n// end::class[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/authorization/customizingauthorizationmanagers/CustomMethodInvocationAuthorizationManagerFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.authorization.customizingauthorizationmanagers;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link CustomMethodInvocationAuthorizationManagerFactory}.\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class CustomMethodInvocationAuthorizationManagerFactoryTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mockMvc;\n\n\t@Test\n\tvoid getUserWhenAnonymousThenForbidden() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user\").with(anonymous()))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(unauthenticated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getUserWhenAuthenticatedWithNoRolesThenForbidden() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", Collections.emptyList());\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getUserWhenAuthenticatedWithUserRoleThenOk() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"ROLE_USER\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getUserWhenAuthenticatedWithAdminRoleThenOk() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"admin\", \"\", \"ROLE_ADMIN\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getUserWhenAuthenticatedWithOtherRoleThenForbidden() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"ROLE_OTHER\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getRolesWhenAuthenticatedWithRole1RoleThenOk() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"ROLE_ROLE1\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/roles\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getRolesWhenAuthenticatedWithAdminRoleThenOk() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"admin\", \"\", \"ROLE_ADMIN\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/roles\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid getRolesWhenAuthenticatedWithOtherRoleThenForbidden() throws Exception {\n\t\tthis.spring.register(SecurityConfiguration.class, TestController.class).autowire();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"\", \"ROLE_OTHER\");\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(get(\"/roles\").with(authentication(authentication)))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t\t// @formatter:on\n\t}\n\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\t@EnableMethodSecurity\n\t@Configuration\n\tstatic class SecurityConfiguration {\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tCustomMethodInvocationAuthorizationManagerFactory customMethodInvocationAuthorizationManagerFactory() {\n\t\t\treturn new CustomMethodInvocationAuthorizationManagerFactory();\n\t\t}\n\n\t}\n\n\t@RestController\n\tstatic class TestController {\n\n\t\t@GetMapping(\"/user\")\n\t\t@ResponseStatus(HttpStatus.OK)\n\t\t@PreAuthorize(\"hasRole('USER')\")\n\t\tvoid user() {\n\t\t}\n\n\t\t@GetMapping(\"/roles\")\n\t\t@ResponseStatus(HttpStatus.OK)\n\t\t@PreAuthorize(\"hasAnyRole('ROLE1', 'ROLE2')\")\n\t\tvoid roles() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/configuration/customizerbeanordering/CustomizerBeanOrderingConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.configuration.customizerbeanordering;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ThrowingCustomizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;\nimport org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\n/**\n *\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass CustomizerBeanOrderingConfiguration {\n\n\t// tag::sample[]\n\t@Bean // <4>\n\tSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t);\n\t\treturn http.build();\n\t\t// @formatter:on\n\t}\n\n\t@Bean\n\t@Order(Ordered.LOWEST_PRECEDENCE) // <2>\n\tThrowingCustomizer<HttpSecurity> userAuthorization() {\n\t\t// @formatter:off\n\t\treturn (http) -> http\n\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t.requestMatchers(\"/users/**\").hasRole(\"USER\")\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Bean\n\t@Order(Ordered.HIGHEST_PRECEDENCE) // <1>\n\tThrowingCustomizer<HttpSecurity> adminAuthorization() {\n\t\t// @formatter:off\n\t\treturn (http) -> http\n\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t.requestMatchers(\"/admins/**\").hasRole(\"ADMIN\")\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t// <3>\n\n\t@Bean\n\tCustomizer<HeadersConfigurer<HttpSecurity>> contentSecurityPolicy() {\n\t\t// @formatter:off\n\t\treturn (headers) -> headers\n\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t.policyDirectives(\"object-src 'none'\")\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Bean\n\tCustomizer<HeadersConfigurer<HttpSecurity>> contentTypeOptions() {\n\t\t// @formatter:off\n\t\treturn (headers) -> headers\n\t\t\t.contentTypeOptions(Customizer.withDefaults());\n\t\t// @formatter:on\n\t}\n\n\t@Bean\n\tCustomizer<HttpsRedirectConfigurer<HttpSecurity>> httpsRedirect() {\n\t\t// @formatter:off\n\t\treturn Customizer.withDefaults();\n\t\t// @formatter:on\n\t}\n\t// end::sample[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/configuration/customizerbeanordering/CustomizerBeanOrderingTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.configuration.customizerbeanordering;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class CustomizerBeanOrderingTests {\n\tpublic final SpringTestContext spring = new SpringTestContext(this).mockMvcAfterSpringSecurityOk();\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tvoid authorizationOrdered() throws Exception {\n\t\tthis.spring.register(\n\t\t\t\tCustomizerBeanOrderingConfiguration.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc\n\t\t\t.perform(get(\"https://localhost/admins/1\").with(user(\"admin\").roles(\"ADMIN\")))\n\t\t\t.andExpect(status().isOk());\n\t\tthis.mockMvc\n\t\t\t\t.perform(get(\"https://localhost/admins/1\").with(user(\"user\").roles(\"USER\")))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mockMvc\n\t\t\t\t.perform(get(\"https://localhost/users/1\").with(user(\"user\").roles(\"USER\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\tthis.mockMvc\n\t\t\t\t.perform(get(\"https://localhost/users/1\").with(user(\"user\").roles(\"OTHER\")))\n\t\t\t\t.andExpect(status().isForbidden());\n\t\tthis.mockMvc\n\t\t\t\t.perform(get(\"https://localhost/other\").with(user(\"authenticated\").roles(\"OTHER\")))\n\t\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/configuration/httpsecuritycustomizerbean/HttpSecurityCustomizerBeanConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.configuration.httpsecuritycustomizerbean;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ThrowingCustomizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n *\n */\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass HttpSecurityCustomizerBeanConfiguration {\n\n\t@Bean\n\tSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t);\n\t\treturn http.build();\n\t\t// @formatter:on\n\t}\n\n\t// tag::httpSecurityCustomizer[]\n\t@Bean\n\tThrowingCustomizer<HttpSecurity> httpSecurityCustomizer() {\n\t\t// @formatter:off\n\t\treturn (http) -> http\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t\t// <1>\n\t\t\t\t\t.policyDirectives(\"object-src 'none'\")\n\t\t\t\t)\n\t\t\t)\n\t\t\t// <2>\n\t\t\t.redirectToHttps(Customizer.withDefaults());\n\t\t// @formatter:on\n\t}\n\t// end::httpSecurityCustomizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/configuration/httpsecuritycustomizerbean/HttpSecurityCustomizerBeanTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.configuration.httpsecuritycustomizerbean;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ThrowingCustomizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class HttpSecurityCustomizerBeanTests {\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\t@Test\n\tvoid httpSecurityCustomizer() throws Exception {\n\t\tthis.spring.register(HttpSecurityCustomizerBeanConfiguration.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc\n\t\t\t.perform(get(\"/\"))\n\t\t\t.andExpect(redirectsToHttps());\n\t\t// headers are not sent back as a part of the redirect to https, so a separate request is necessary\n\t\tthis.mockMvc.perform(get(\"https://localhost/\"))\n\t\t\t.andExpect(cspIsObjectSrcNone());\n\t\t// @formatter:on\n\t}\n\n\tprivate static @NotNull ResultMatcher redirectsToHttps() {\n\t\treturn mvcResult -> assertThat(\n\t\t\tmvcResult.getResponse().getRedirectedUrl()).startsWith(\"https://\");\n\t}\n\n\tprivate static @NotNull ResultMatcher cspIsObjectSrcNone() {\n\t\treturn header().string(\"Content-Security-Policy\", \"object-src 'none'\");\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/configuration/toplevelcustomizerbean/TopLevelCustomizerBeanConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.configuration.toplevelcustomizerbean;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;\nimport org.springframework.security.web.SecurityFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n *\n */\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\npublic class TopLevelCustomizerBeanConfiguration {\n\n\t@Bean\n\tSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t// @formatter:off\n\t\thttp\n\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t.anyRequest().authenticated()\n\t\t\t);\n\t\treturn http.build();\n\t\t// @formatter:on\n\t}\n\n\t// tag::headersCustomizer[]\n\t@Bean\n\tCustomizer<HeadersConfigurer<HttpSecurity>> headersSecurity() {\n\t\t// @formatter:off\n\t\treturn (headers) -> headers\n\t\t\t.contentSecurityPolicy((csp) -> csp\n\t\t\t\t// <1>\n\t\t\t\t.policyDirectives(\"object-src 'none'\")\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\t// end::headersCustomizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/configuration/toplevelcustomizerbean/TopLevelCustomizerBeanTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.configuration.toplevelcustomizerbean;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.ThrowingCustomizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class TopLevelCustomizerBeanTests {\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tprivate MockMvc mockMvc;\n\n\n\t@Test\n\tvoid headersCustomizer() throws Exception {\n\t\tthis.spring.register(TopLevelCustomizerBeanConfiguration.class).autowire();\n\t\t// @formatter:off\n\t\tthis.mockMvc\n\t\t\t.perform(get(\"/\"))\n\t\t\t.andExpect(cspIsObjectSrcNone());\n\t\t// @formatter:on\n\t}\n\n\tprivate static @NotNull ResultMatcher cspIsObjectSrcNone() {\n\t\treturn header().string(\"Content-Security-Policy\", \"object-src 'none'\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/customizingfilter/CustomizingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.customizingfilter;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter;\nimport org.springframework.test.web.servlet.MockMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\n\n/**\n * Tests for customizing security filters.\n *\n */\n@ExtendWith(SpringTestContextExtension.class)\npublic class CustomizingFilterTests {\n\n\tpublic final SpringTestContext spring = new SpringTestContext(this);\n\n\t@Autowired\n\tMockMvc mvc;\n\n\t@Autowired\n\tFilterChainProxy filterChainProxy;\n\n\t@Test\n\tpublic void filterChainWhenBasicDefaultThenBasicAuthenticationFilterPresent() {\n\t\tthis.spring.register(SecurityConfigBasicDefault.class).autowire();\n\t\tList<Filter> filters = this.filterChainProxy.getFilters(\"/\");\n\t\tassertThat(filters).extracting(\"class\").contains(BasicAuthenticationFilter.class);\n\t}\n\n\t@Test\n\tpublic void filterChainWhenCustomFilterThenCustomFilterPresent() {\n\t\tthis.spring.register(SecurityConfigCustom.class).autowire();\n\t\tList<Filter> filters = this.filterChainProxy.getFilters(\"/\");\n\t\tassertThat(filters).extracting(\"class\").contains(SecurityConfigCustom.MyBasicAuthenticationFilter.class);\n\t\tassertThat(filters).extracting(\"class\").doesNotContain(BasicAuthenticationFilter.class);\n\t}\n\n\t@Test\n\tpublic void requestWhenDisableThenNoWwwAuthenticateHeader() throws Exception {\n\t\tthis.spring.register(SecurityConfigDisable.class).autowire();\n\t\tthis.mvc.perform(get(\"/\")).andExpect(header().doesNotExist(HttpHeaders.WWW_AUTHENTICATE));\n\t}\n\n\t@Test\n\tpublic void filterChainWhenIncorrectThenBothFiltersPresent() {\n\t\tthis.spring.register(SecurityConfigIncorrect.class).autowire();\n\t\tList<Filter> filters = this.filterChainProxy.getFilters(\"/\");\n\t\tassertThat(filters).extracting(\"class\").contains(BasicAuthenticationFilter.class);\n\t\tassertThat(filters).extracting(\"class\").contains(SecurityConfigIncorrect.MyBasicAuthenticationFilter.class);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityConfigBasicDefault {\n\n\t\t// tag::basic-default[]\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t.httpBasic(Customizer.withDefaults());\n\t\t\t// ...\n\n\t\t\treturn http.build();\n\t\t}\n\t\t// end::basic-default[]\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityConfigCustom {\n\n\t\t// tag::custom-filter[]\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tMyBasicAuthenticationFilter basic = new MyBasicAuthenticationFilter();\n\t\t\t// ... configure\n\n\t\t\thttp\n\t\t\t\t// ...\n\t\t\t\t.addFilterAt(basic, BasicAuthenticationFilter.class);\n\n\t\t\treturn http.build();\n\t\t}\n\t\t// end::custom-filter[]\n\n\t\tstatic class MyBasicAuthenticationFilter implements Filter {\n\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\t\tthrows IOException, ServletException {\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityConfigDisable {\n\n\t\t// tag::disable[]\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp\n\t\t\t\t.httpBasic((basic) -> basic.disable());\n\t\t\t\t// ...\n\n\t\t\treturn http.build();\n\t\t}\n\t\t// end::disable[]\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityConfigIncorrect {\n\n\t\t// tag::incorrect[]\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\tMyBasicAuthenticationFilter basic = new MyBasicAuthenticationFilter();\n\t\t\t// ... configure\n\n\t\t\thttp\n\t\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t\t// ... on no! BasicAuthenticationFilter is added twice!\n\t\t\t\t.addFilterAt(basic, BasicAuthenticationFilter.class);\n\n\t\t\treturn http.build();\n\t\t}\n\t\t// end::incorrect[]\n\n\t\tstatic class MyBasicAuthenticationFilter implements Filter {\n\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\t\tthrows IOException, ServletException {\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class UserDetailsConfig {\n\n\t\t@Bean\n\t\tInMemoryUserDetailsManager userDetailsManager() {\n\t\t\treturn new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/integrations/customauthorization/AdvancedWebSocketSecurityConfig.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.integrations.customauthorization;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\n\nimport static org.springframework.messaging.simp.SimpMessageType.MESSAGE;\nimport static org.springframework.messaging.simp.SimpMessageType.SUBSCRIBE;\n\n// tag::snippet[]\n@Configuration\npublic class AdvancedWebSocketSecurityConfig {\n\n\t@Bean\n\tpublic AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\tmessages\n\t\t\t.nullDestMatcher().authenticated() // <1>\n\t\t\t.simpSubscribeDestMatchers(\"/user/queue/errors\").permitAll() // <2>\n\t\t\t.simpDestMatchers(\"/app/**\").hasRole(\"USER\") // <3>\n\t\t\t.simpSubscribeDestMatchers(\"/user/**\", \"/topic/friends/*\").hasRole(\"USER\") // <4>\n\t\t\t.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll() // <5>\n\t\t\t.anyMessage().denyAll(); // <6>\n\n\t\treturn messages.build();\n\t}\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/integrations/customauthorization/WebSocketSecurityConfig.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.integrations.customauthorization;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.authorization.AuthorityAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\n\n// tag::snippet[]\n@Configuration\n@EnableWebSocketSecurity\npublic class WebSocketSecurityConfig {\n\n\t@Bean\n\tAuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\treturn AuthorityAuthorizationManager.hasRole(\"USER\");\n\t}\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/integrations/migratingspelexpressions/WebSocketSecurityConfig.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.integrations.migratingspelexpressions;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.messaging.access.expression.MessageExpressionAuthorizationManager;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\n\n// tag::snippet[]\n@Configuration\npublic class WebSocketSecurityConfig {\n\n\t@Bean\n\tpublic AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\tmessages\n\t\t\t// ...\n\t\t\t.simpSubscribeDestMatchers(\"/topic/friends/{friend}\")\n\t\t\t.access(new MessageExpressionAuthorizationManager(\"#friend == 'john'\"));\n\t\t// ...\n\n\t\treturn messages.build();\n\t}\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/integrations/websocketauthorization/WebSocketSecurityConfig.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.integrations.websocketauthorization;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity;\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;\n\n// tag::snippet[]\n@Configuration\n@EnableWebSocketSecurity // <1> <2>\npublic class WebSocketSecurityConfig {\n\n\t@Bean\n\tAuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {\n\t\tmessages.simpDestMatchers(\"/user/**\").hasRole(\"USER\"); // <3>\n\t\treturn messages.build();\n\t}\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/integrations/websocketsameorigindisable/WebSocketSecurityConfig.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.integrations.websocketsameorigindisable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;\nimport org.springframework.messaging.simp.config.ChannelRegistration;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.SpringAuthorizationEventPublisher;\nimport org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;\nimport org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;\nimport org.springframework.security.messaging.context.SecurityContextChannelInterceptor;\nimport org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;\n\nimport java.util.List;\n\n// tag::snippet[]\n@Configuration\npublic class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {\n\n\tprivate final ApplicationContext applicationContext;\n\n\tprivate final AuthorizationManager<Message<?>> authorizationManager;\n\n\tpublic WebSocketSecurityConfig(ApplicationContext applicationContext, AuthorizationManager<Message<?>> authorizationManager) {\n\t\tthis.applicationContext = applicationContext;\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t@Override\n\tpublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {\n\t\targumentResolvers.add(new AuthenticationPrincipalArgumentResolver());\n\t}\n\n\t@Override\n\tpublic void configureClientInboundChannel(ChannelRegistration registration) {\n\t\tAuthorizationChannelInterceptor authz = new AuthorizationChannelInterceptor(authorizationManager);\n\t\tAuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(applicationContext);\n\t\tauthz.setAuthorizationEventPublisher(publisher);\n\t\tregistration.interceptors(new SecurityContextChannelInterceptor(), authz);\n\t}\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/integrations/websocketsockjscsrf/WebSecurityConfig.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.integrations.websocketsockjscsrf;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;\nimport org.springframework.security.web.SecurityFilterChain;\n\n// tag::snippet[]\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t// ignore our stomp endpoints since they are protected using Stomp headers\n\t\t\t\t.ignoringRequestMatchers(\"/chat/**\")\n\t\t\t)\n\t\t\t.headers((headers) -> headers\n\t\t\t\t// allow same origin to frame our site to support iframe SockJS\n\t\t\t\t.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)\n\t\t\t)\n\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t/* @chomp:line // ... */.anyRequest().permitAll()\n\t\t\t)\n\t\t/* @chomp:line // ... */;\n\t\treturn http.build();\n\t}\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/integrations/websocketsockjssameorigin/WebSecurityConfig.java",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.integrations.websocketsockjssameorigin;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;\nimport org.springframework.security.web.SecurityFilterChain;\n\n// tag::snippet[]\n@Configuration\n@EnableWebSecurity\npublic class WebSecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t// ...\n\t\t\t.headers((headers) -> headers\n\t\t\t\t.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)\n\t\t\t);\n\t\treturn http.build();\n\t}\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/customuserdetailsservice/UserDetailsJwtPrincipalConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.oauth2.resourceserver.customuserdetailsservice;\n\nimport java.util.Map;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.stereotype.Component;\n\n// tag::custom-converter[]\n@Component\npublic final class UserDetailsJwtPrincipalConverter implements Converter<Jwt, OAuth2AuthenticatedPrincipal> {\n\n\tprivate final UserDetailsService users;\n\n\tpublic UserDetailsJwtPrincipalConverter(UserDetailsService users) {\n\t\tthis.users = users;\n\t}\n\n\t@Override\n\tpublic OAuth2AuthenticatedPrincipal convert(Jwt jwt) {\n\t\tUserDetails user = this.users.loadUserByUsername(jwt.getSubject());\n\t\treturn new JwtUser(jwt, user);\n\t}\n\n\tprivate static final class JwtUser extends User implements OAuth2AuthenticatedPrincipal {\n\n\t\tprivate final Jwt jwt;\n\n\t\tprivate JwtUser(Jwt jwt, UserDetails user) {\n\t\t\tsuper(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),\n\t\t\t\t\tuser.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());\n\t\t\tthis.jwt = jwt;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn this.jwt.getSubject();\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, Object> getAttributes() {\n\t\t\treturn this.jwt.getClaims();\n\t\t}\n\n\t}\n\n}\n// end::custom-converter[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/customuserdetailsservice/UserDetailsJwtPrincipalConverterConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.oauth2.resourceserver.customuserdetailsservice;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;\n\n@Configuration\nclass UserDetailsJwtPrincipalConverterConfiguration {\n\n\t// tag::configure-converter[]\n\t@Bean\n\tJwtAuthenticationConverter authenticationConverter(UserDetailsJwtPrincipalConverter principalConverter) {\n\t\tJwtAuthenticationConverter converter = new JwtAuthenticationConverter();\n\t\tconverter.setJwtPrincipalConverter(principalConverter);\n\t\treturn converter;\n\t}\n\t// end::configure-converter[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/customuserdetailsservice/UserDetailsJwtPrincipalConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.oauth2.resourceserver.customuserdetailsservice;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass UserDetailsJwtPrincipalConverterTests {\n\n\t@Test\n\tvoid convertWhenUserFoundThenPrincipalIsUserDetails() {\n\t\tUserDetailsService users = (username) -> User.withDefaultPasswordEncoder()\n\t\t\t.username(username)\n\t\t\t.password(\"password\")\n\t\t\t.roles(\"USER\")\n\t\t\t.build();\n\t\tUserDetailsJwtPrincipalConverter principalConverter = new UserDetailsJwtPrincipalConverter(users);\n\t\tJwtAuthenticationConverter converter = new JwtAuthenticationConverter();\n\t\tconverter.setJwtPrincipalConverter(principalConverter);\n\t\tJwt jwt = TestJwts.jwt().subject(\"user\").build();\n\t\tOAuth2AuthenticatedPrincipal principal = (OAuth2AuthenticatedPrincipal) converter.convert(jwt).getPrincipal();\n\t\tassertThat(principal.getName()).isEqualTo(\"user\");\n\t\tassertThat(principal.getAttributes()).containsKey(\"sub\");\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/jwtgrantedauthoritiesspelexpression/ExpressionJwtGrantedAuthoritiesConverterTests.java",
    "content": "package org.springframework.security.docs.servlet.oauth2.resourceserver.jwtgrantedauthoritiesspelexpression;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.authentication.ExpressionJwtGrantedAuthoritiesConverter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass ExpressionJwtGrantedAuthoritiesConverterTests {\n\n\t@Test\n\tpublic void convertWhenTokenHasCustomClaimNameExpressionThenCustomClaimNameAttributeIsTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"nested\", Collections.singletonMap(\"scopes\", Arrays.asList(\"read\", \"write\")))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\t// tag::spel-expression[]\n\t\tSpelExpressionParser parser = new SpelExpressionParser();\n\t\tExpression expression = parser.parseExpression(\"[nested][scopes]\");\n\t\tExpressionJwtGrantedAuthoritiesConverter converter = new ExpressionJwtGrantedAuthoritiesConverter(expression);\n\t\tCollection<GrantedAuthority> authorities = converter.convert(jwt);\n\t\t// end::spel-expression[]\n\t\tassertThat(authorities).extracting(GrantedAuthority::getAuthority)\n\t\t\t\t.containsExactly(\"SCOPE_read\", \"SCOPE_write\");\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/methodsecurityhasscope/MessageService.java",
    "content": "package org.springframework.security.docs.servlet.oauth2.resourceserver.methodsecurityhasscope;\n\n\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.stereotype.Service;\n\n@Service\nclass MessageService {\n\n\t// tag::protected-method[]\n\t@PreAuthorize(\"@oauth2.hasScope('message:read')\")\n\tString readMessage() {\n\t\treturn \"message\";\n\t}\n\t// end::protected-method[]\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/methodsecurityhasscope/MethodSecurityHasScopeConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.oauth2.resourceserver.methodsecurityhasscope;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.oauth2.core.authorization.DefaultOAuth2AuthorizationManagerFactory;\nimport org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagerFactory;\n\n@Configuration\n@EnableMethodSecurity\nclass MethodSecurityHasScopeConfiguration {\n\t// tag::declare-factory[]\n\t@Bean\n\tOAuth2AuthorizationManagerFactory<?> oauth2() {\n\t\treturn new DefaultOAuth2AuthorizationManagerFactory<>();\n\t}\n\t// end::declare-factory[]\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/methodsecurityhasscope/MethodSecurityHasScopeConfigurationTests.java",
    "content": "package org.springframework.security.docs.servlet.oauth2.resourceserver.methodsecurityhasscope;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.config.test.SpringTestContext;\nimport org.springframework.security.config.test.SpringTestContextExtension;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n@ExtendWith(SpringTestContextExtension.class)\n@ExtendWith(SpringExtension.class)\n@SecurityTestExecutionListeners\npublic class MethodSecurityHasScopeConfigurationTests {\n\tpublic final SpringTestContext spring = new SpringTestContext(this).mockMvcAfterSpringSecurityOk();\n\n\t@Autowired\n\tprivate MessageService messages;\n\n\t@Test\n\t@WithMockUser(authorities = \"SCOPE_message:read\")\n\tvoid readMessageWhenMessageReadThenAllowed() {\n\t\tthis.spring.register(MethodSecurityHasScopeConfiguration.class, MessageService.class).autowire();\n\t\tthis.messages.readMessage();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid readMessageWhenNoScopeThenDenied() {\n\t\tthis.spring.register(MethodSecurityHasScopeConfiguration.class, MessageService.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.messages::readMessage);\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { \"SCOPE_message:read\", \"FACTOR_BEARER\", \"FACTOR_X509\" })\n\tvoid mfaReadMessageWhenMessageReadAndFactorsThenAllowed() {\n\t\tthis.spring.register(MethodSecurityHasScopeMfaConfiguration.class, MessageService.class).autowire();\n\t\tthis.messages.readMessage();\n\t}\n\n\t@Test\n\t@WithMockUser(authorities = { \"SCOPE_message:read\" })\n\tvoid mfaReadMessageWhenMessageReadThenDenied() {\n\t\tthis.spring.register(MethodSecurityHasScopeMfaConfiguration.class, MessageService.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.messages::readMessage);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid mfaReadMessageWhenNoScopeThenDenied() {\n\t\tthis.spring.register(MethodSecurityHasScopeMfaConfiguration.class, MessageService.class).autowire();\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.messages::readMessage);\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/methodsecurityhasscope/MethodSecurityHasScopeMfaConfiguration.java",
    "content": "package org.springframework.security.docs.servlet.oauth2.resourceserver.methodsecurityhasscope;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.oauth2.core.authorization.DefaultOAuth2AuthorizationManagerFactory;\nimport org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagerFactory;\n\n@Configuration\n@EnableMethodSecurity\n@EnableMultiFactorAuthentication(authorities = { \"FACTOR_BEARER\", \"FACTOR_X509\" })\nclass MethodSecurityHasScopeMfaConfiguration {\n\t// tag::declare-factory[]\n\t@Bean\n\tOAuth2AuthorizationManagerFactory<?> oauth2(AuthorizationManagerFactory<?> authz) {\n\t\treturn new DefaultOAuth2AuthorizationManagerFactory<>(authz);\n\t}\n\t// end::declare-factory[]\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/opaquetokentimeoutsrestclient/RestClientOpaqueTokenIntrospectorConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.oauth2.resourceserver.opaquetokentimeoutsrestclient;\n\nimport java.time.Duration;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.client.SimpleClientHttpRequestFactory;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.introspection.RestClientOpaqueTokenIntrospector;\nimport org.springframework.web.client.RestClient;\n\n@Configuration\npublic class RestClientOpaqueTokenIntrospectorConfiguration {\n\n\t// tag::restclient-simple[]\n\t@Bean\n\tpublic OpaqueTokenIntrospector introspector(String introspectionUri, String clientId, String clientSecret) {\n\t\treturn RestClientOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n\t\t\t\t.clientId(clientId)\n\t\t\t\t.clientSecret(clientSecret)\n\t\t\t\t.build();\n\t}\n\t// end::restclient-simple[]\n\n\t// tag::restclient-timeouts[]\n\t@Bean\n\tpublic OpaqueTokenIntrospector introspectorWithTimeouts(String introspectionUri, String clientId,\n\t\t\tString clientSecret) {\n\t\tSimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();\n\t\trequestFactory.setConnectTimeout(Duration.ofSeconds(60));\n\t\trequestFactory.setReadTimeout(Duration.ofSeconds(60));\n\t\tRestClient restClient = RestClient.builder()\n\t\t\t\t.requestFactory(requestFactory)\n\t\t\t\t.defaultHeaders((headers) -> headers.setBasicAuth(clientId, clientSecret))\n\t\t\t\t.build();\n\t\treturn new RestClientOpaqueTokenIntrospector(introspectionUri, restClient);\n\t}\n\t// end::restclient-timeouts[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/oauth2/resourceserver/opaquetokentimeoutsrestclient/RestClientOpaqueTokenIntrospectorConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.oauth2.resourceserver.opaquetokentimeoutsrestclient;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.Optional;\n\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.client.SimpleClientHttpRequestFactory;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;\nimport org.springframework.security.oauth2.server.resource.introspection.RestClientOpaqueTokenIntrospector;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link RestClientOpaqueTokenIntrospectorConfiguration} sample snippets.\n */\nclass RestClientOpaqueTokenIntrospectorConfigurationTests {\n\n\tprivate static final String CLIENT_ID = \"client\";\n\n\tprivate static final String CLIENT_SECRET = \"secret\";\n\n\tprivate static final String ACTIVE_RESPONSE = \"\"\"\n\t\t\t{\n\t\t\t  \"active\": true,\n\t\t\t  \"sub\": \"Z5O3upPC88QrAjx00dis\",\n\t\t\t  \"scope\": \"read write\",\n\t\t\t  \"exp\": 1419356238,\n\t\t\t  \"iat\": 1419350238\n\t\t\t}\n\t\t\t\"\"\";\n\n\t@Test\n\tvoid introspectorWhenBuilderThenIntrospectsSuccessfully() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectionUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspector = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectionUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal principal = introspector.introspect(\"token\");\n\t\t\tassertThat(principal.getAttributes()).isNotNull()\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\")\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238));\n\t\t\tassertThat((List<String>) principal.getAttribute(OAuth2TokenIntrospectionClaimNames.SCOPE))\n\t\t\t\t.isEqualTo(Arrays.asList(\"read\", \"write\"));\n\t\t}\n\t}\n\n\t@Test\n\tvoid introspectorWithTimeoutsWhenCustomRestClientThenIntrospectsSuccessfully() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectionUri = server.url(\"/introspect\").toString();\n\t\t\tSimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();\n\t\t\trequestFactory.setConnectTimeout(Duration.ofSeconds(60));\n\t\t\trequestFactory.setReadTimeout(Duration.ofSeconds(60));\n\t\t\tRestClient restClient = RestClient.builder()\n\t\t\t\t.requestFactory(requestFactory)\n\t\t\t\t.defaultHeaders((headers) -> headers.setBasicAuth(CLIENT_ID, CLIENT_SECRET))\n\t\t\t\t.build();\n\t\t\tOpaqueTokenIntrospector introspector = new RestClientOpaqueTokenIntrospector(introspectionUri, restClient);\n\t\t\tOAuth2AuthenticatedPrincipal principal = introspector.introspect(\"token\");\n\t\t\tassertThat(principal.getAttributes()).isNotNull()\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\");\n\t\t}\n\t}\n\n\tprivate static Dispatcher requiresAuth(String username, String password, String response) {\n\t\treturn new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\tString authorization = request.getHeader(HttpHeaders.AUTHORIZATION);\n\t\t\t\treturn Optional.ofNullable(authorization)\n\t\t\t\t\t.filter((a) -> isAuthorized(authorization, username, password))\n\t\t\t\t\t.map((a) -> new MockResponse().setBody(response)\n\t\t\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))\n\t\t\t\t\t.orElse(new MockResponse().setResponseCode(401));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static boolean isAuthorized(String authorization, String username, String password) {\n\t\tString decoded = new String(Base64.getDecoder().decode(authorization.substring(6)));\n\t\tString[] values = decoded.split(\":\", 2);\n\t\treturn values.length == 2 && username.equals(values[0]) && password.equals(values[1]);\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/requestcachepreventsavedrequest/SecurityConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.requestcachepreventsavedrequest;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.savedrequest.NullRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\n\npublic class SecurityConfig {\n\n\t// tag::snippet[]\n\t@Bean\n\tSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\tRequestCache nullRequestCache = new NullRequestCache();\n\t\thttp\n\t\t\t\t// ...\n\t\t\t\t.requestCache((cache) -> cache\n\t\t\t\t\t\t.requestCache(nullRequestCache)\n\t\t\t\t);\n\t\treturn http.build();\n\t}\n\t// end::snippet[]\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/servletdelegatingfilterproxy/SampleDelegatingFilterProxy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.servletdelegatingfilterproxy;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\n\nimport org.springframework.web.context.support.StaticWebApplicationContext;\nimport org.springframework.web.filter.GenericFilterBean;\n\nimport java.io.IOException;\n\n/**\n * A very simple implementation of a DelegatingFilterProxy.\n */\npublic class SampleDelegatingFilterProxy extends GenericFilterBean {\n\n\tprivate StaticWebApplicationContext wac;\n\tprivate final String someBeanName;\n\n\tpublic SampleDelegatingFilterProxy(String someBeanName, StaticWebApplicationContext webApplicationContext) {\n\t\tthis.wac = webApplicationContext;\n\t\tthis.someBeanName = someBeanName;\n\t}\n\n\t// tag::dofilter[]\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n\t\tFilter delegate = getFilterBean(someBeanName); // <1>\n\t\tdelegate.doFilter(request, response, chain); // <2>\n\t}\n\t// end::dofilter[]\n\n\tprivate Filter getFilterBean(String someBeanName) {\n\t\treturn this.wac.getBean(someBeanName, Filter.class);\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/servletdelegatingfilterproxy/SampleDelegatingFilterProxyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.servletdelegatingfilterproxy;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.FilterConfig;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.servlet.MockServletContext;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.StaticWebApplicationContext;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class SampleDelegatingFilterProxyTests {\n\n\t@Test\n\tvoid testFilter() throws ServletException, IOException {\n\t\tServletContext sc = new MockServletContext();\n\t\tStaticWebApplicationContext wac = new StaticWebApplicationContext();\n\t\twac.registerSingleton(\"targetFilter\", MockFilter.class);\n\t\twac.setServletContext(sc);\n\t\twac.refresh();\n\t\tsc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);\n\n\t\tMockFilter targetFilter = (MockFilter) wac.getBean(\"targetFilter\");\n\t\tMockFilterConfig proxyConfig = new MockFilterConfig(sc);\n\t\tproxyConfig.addInitParameter(\"targetBeanName\", \"targetFilter\");\n\t\tSampleDelegatingFilterProxy filterProxy = new SampleDelegatingFilterProxy(\"targetFilter\", wac);\n\t\tfilterProxy.init(proxyConfig);\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilterProxy.doFilter(request, response, null);\n\n\t\tassertThat(targetFilter.filterConfig).isNull();\n\t\tassertThat(request.getAttribute(\"called\")).isEqualTo(Boolean.TRUE);\n\n\t\tfilterProxy.destroy();\n\t\tassertThat(targetFilter.filterConfig).isNull();\n\t}\n\n\tprivate static class MockFilter implements Filter {\n\n\t\tprivate FilterConfig filterConfig;\n\n\t\t@Override\n\t\tpublic void init(FilterConfig filterConfig) throws ServletException {\n\t\t\tthis.filterConfig = filterConfig;\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response,\n\t\t\t\tFilterChain chain) throws java.io.IOException, ServletException {\n\t\t\trequest.setAttribute(\"called\", Boolean.TRUE);\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() {\n\t\t\tthis.filterConfig = null;\n\t\t}\n\t}\n\n\tprivate static class MockFilterConfig  implements FilterConfig {\n\t\tprivate final ServletContext servletContext;\n\n\t\tprivate final String filterName;\n\n\t\tprivate final Map<String, String> initParameters = new LinkedHashMap<>();\n\n\t\tpublic MockFilterConfig(ServletContext servletContext) {\n\t\t\tthis.servletContext = servletContext;\n\t\t\tthis.filterName = \"\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String getFilterName() {\n\t\t\treturn this.filterName;\n\t\t}\n\n\t\t@Override\n\t\tpublic ServletContext getServletContext() {\n\t\t\treturn this.servletContext;\n\t\t}\n\n\t\tpublic void addInitParameter(String name, String value) {\n\t\t\tAssert.notNull(name, \"Parameter name must not be null\");\n\t\t\tthis.initParameters.put(name, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getInitParameter(String name) {\n\t\t\tAssert.notNull(name, \"Parameter name must not be null\");\n\t\t\treturn this.initParameters.get(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic Enumeration<String> getInitParameterNames() {\n\t\t\treturn Collections.enumeration(this.initParameters.keySet());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/servletfiltersreview/FilterChainUsage.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.servletfiltersreview;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\n\n/**\n * Demos FilterChain Usage.\n * @author Rob Winch\n */\npublic class FilterChainUsage implements Filter {\n\n\t// tag::dofilter[]\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response,\n\t\t\tFilterChain chain) throws IOException, ServletException {\n\t\t// do something before the rest of the application\n\t\tchain.doFilter(request, response); // invoke the rest of the application\n\t\t// do something after the rest of the application\n\t}\n\t// end::dofilter[]\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/servletsecurityfilters/SampleSecurityConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.servletsecurityfilters;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = { SampleSecurityConfigTests.UserDetailsConfig.class, SecurityConfig.class })\n@WebAppConfiguration\npublic class SampleSecurityConfigTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t\t.defaultRequest(get(\"/api\").with(user(\"user\")))\n\t\t\t\t.defaultRequest(post(\"/api\").with(csrf()))\n\t\t\t\t.apply(springSecurity())\n\t\t\t\t.build();\n\t}\n\n\t@Test\n\tvoid testGet() throws Exception {\n\t\tthis.mvc.perform(get(\"/api\")\n\t\t\t\t\t\t.with(httpBasic(\"user\", \"password\")))\n\t\t\t\t// Security check was successful\n\t\t\t\t.andExpect(status().isNotFound())\n\t\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tvoid testUnauthenticated() throws Exception {\n\t\tthis.mvc.perform(get(\"/api\"))\n\t\t\t\t// Security check was successful\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t}\n\n\t@Test\n\tvoid testCsrf() throws Exception {\n\t\tthis.mvc.perform(post(\"/api\")\n\t\t\t\t.with(csrf())\n\t\t\t\t.with(httpBasic(\"user\", \"password\"))\n\t\t).andExpect(status().isNotFound());\n\t}\n\n\t@Configuration\n\tstatic class UserDetailsConfig {\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/servletsecurityfilters/SecurityConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.servletsecurityfilters;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.Customizer;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\n\n// tag::snippet[]\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n\t@Bean\n\tpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\thttp\n\t\t\t.csrf(Customizer.withDefaults())\n\t\t\t.httpBasic(Customizer.withDefaults())\n\t\t\t.formLogin(Customizer.withDefaults())\n\t\t\t.authorizeHttpRequests(authorize -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t);\n\n\t\treturn http.build();\n\t}\n\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethod/HelloMessageService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethod;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.config.core.MessageService;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * A message service for demonstrating test support for method-based security.\n */\n// tag::authenticated[]\npublic class HelloMessageService implements MessageService {\n\n\t@Override\n\t@PreAuthorize(\"isAuthenticated()\")\n\tpublic String getMessage() {\n\t\tAuthentication authentication = SecurityContextHolder.getContext()\n\t\t\t\t.getAuthentication();\n\t\treturn \"Hello \" + authentication;\n\t}\n\n\t@Override\n\t@PreAuthorize(\"isAuthenticated()\")\n\tpublic String getJsrMessage() {\n\t\tAuthentication authentication = SecurityContextHolder.getContext()\n\t\t\t\t.getAuthentication();\n\t\treturn \"Hello JSR \" + authentication;\n\t}\n}\n// end::authenticated[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethod/HelloServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethod;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.core.MessageService;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\nclass HelloServiceTests {\n\n\t@Autowired\n\tMessageService messageService;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tUsernamePasswordAuthenticationToken user = UsernamePasswordAuthenticationToken.authenticated(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(user);\n\t}\n\n\t@Test\n\tvoid helloServiceTest() {\n\t\tassertThat(messageService.getMessage())\n\t\t\t\t.contains(\"user\")\n\t\t\t\t.contains(\"ROLE_USER\");\n\t}\n\n\t@EnableMethodSecurity(prePostEnabled = true, jsr250Enabled = true)\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tMessageService messageService() {\n\t\t\treturn new HelloMessageService();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodmetaannotations/WithMockAdmin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodmetaannotations;\n\nimport org.springframework.security.test.context.support.WithMockUser;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n// tag::snippet[]\n@Retention(RetentionPolicy.RUNTIME)\n@WithMockUser(value=\"rob\",roles={\"USER\",\"ADMIN\"})\npublic @interface WithMockAdmin { }\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodmetaannotations/WithMockAdminTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodmetaannotations;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.core.MessageService;\nimport org.springframework.security.docs.servlet.test.testmethod.HelloMessageService;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class WithMockAdminTests {\n\n\t@Autowired\n\tMessageService messageService;\n\n\t@Test\n\t@WithMockAdmin\n\tvoid getMessageWithMockUserAdminRoles() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message)\n\t\t\t\t.contains(\"rob\")\n\t\t\t\t.contains(\"ROLE_ADMIN\")\n\t\t\t\t.contains(\"ROLE_USER\");\n\t}\n\n\t@EnableMethodSecurity\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tMessageService messageService() {\n\t\t\treturn new HelloMessageService();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodmetaannotations/WithMockUserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodmetaannotations;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.core.MessageService;\nimport org.springframework.security.docs.servlet.test.testmethod.HelloMessageService;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\npublic class WithMockUserTests {\n\n\t@Autowired\n\tMessageService messageService;\n\n\t@Test\n\t// tag::snippet[]\n\t@WithMockUser(username = \"admin\", roles = {\"USER\", \"ADMIN\"})\n\t// end::snippet[]\n\tvoid getMessageWithMockUserAdminRoles() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message)\n\t\t\t\t.contains(\"admin\")\n\t\t\t\t.contains(\"ROLE_ADMIN\")\n\t\t\t\t.contains(\"ROLE_USER\");\n\t}\n\n\t@EnableMethodSecurity\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tMessageService messageService() {\n\t\t\treturn new HelloMessageService();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodsetup/WithMockUserSampleTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodsetup;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.docs.servlet.test.testmethod.HelloMessageService;\nimport org.springframework.security.config.core.MessageService;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\nclass WithMockUserSampleTests {\n\n\t@Autowired\n\tMessageService messageService;\n\n\t// tag::snippet[]\n\t@Test\n\tvoid getMessageUnauthenticated() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t\t.isThrownBy(() -> messageService.getMessage());\n\t}\n\t// end::snippet[]\n\n\t@EnableMethodSecurity\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tMessageService messageService() {\n\t\t\treturn new HelloMessageService();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodsetup/WithMockUserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodsetup;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n// tag::setup[]\n@ExtendWith(SpringExtension.class) // <1>\n@ContextConfiguration // <2>\nclass WithMockUserTests {\n\t// ...\n}\n// end::setup[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithanonymoususer/WithUserClassLevelAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithanonymoususer;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.security.test.context.support.WithAnonymousUser;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n// tag::snippet[]\n@ExtendWith(SpringExtension.class)\n@WithMockUser\nclass WithUserClassLevelAuthenticationTests {\n\n\t@Test\n\tvoid withMockUser1() {\n\t}\n\n\t@Test\n\tvoid withMockUser2() {\n\t}\n\n\t@Test\n\t@WithAnonymousUser\n\tvoid anonymous() throws Exception {\n\t\t// override default to run as anonymous user\n\t}\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithmockuser/WithMockUserClassTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithmockuser;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n// tag::snippet[]\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WithMockUser(username = \"admin\", roles = {\"USER\", \"ADMIN\"})\nclass WithMockUserClassTests {\n\t// ...\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithmockuser/WithMockUserNestedTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithmockuser;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n// tag::snippet[]\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WithMockUser(username = \"admin\", roles = {\"USER\", \"ADMIN\"})\nclass WithMockUserNestedTests {\n\n\t@Nested\n\tclass TestSuite1 {\n\t\t// ... all test methods use admin user\n\t}\n\n\t@Nested\n\tclass TestSuite2 {\n\t\t// ... all test methods use admin user\n\t}\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithmockuser/WithMockUserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithmockuser;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.core.MessageService;\nimport org.springframework.security.docs.servlet.test.testmethod.HelloMessageService;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\nclass WithMockUserTests {\n\n\t@Autowired\n\tMessageService messageService;\n\n\t// tag::mock-user[]\n\t@Test\n\t@WithMockUser\n\tvoid getMessageWithMockUser() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message).contains(\"user\");\n\t}\n\t// end::mock-user[]\n\n\t// tag::custom-user[]\n\t@Test\n\t@WithMockUser(\"customUser\")\n\tvoid getMessageWithMockUserCustomUsername() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message).contains(\"customUser\");\n\t}\n\t// end::custom-user[]\n\n\t// tag::custom-roles[]\n\t@Test\n\t@WithMockUser(username = \"admin\", roles = {\"USER\", \"ADMIN\"})\n\tvoid getMessageWithMockUserCustomRoles() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message)\n\t\t\t\t.contains(\"admin\")\n\t\t\t\t.contains(\"ROLE_ADMIN\")\n\t\t\t\t.contains(\"ROLE_USER\");\n\t}\n\t// end::custom-roles[]\n\n\t// tag::custom-authorities[]\n\t@Test\n\t@WithMockUser(username = \"admin\", authorities = {\"ADMIN\", \"USER\"})\n\tpublic void getMessageWithMockUserCustomAuthorities() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message)\n\t\t\t\t.contains(\"admin\")\n\t\t\t\t.contains(\"ADMIN\")\n\t\t\t\t.contains(\"USER\")\n\t\t\t\t.doesNotContain(\"ROLE_\");\n\t}\n\t// end::custom-authorities[]\n\n\t@EnableMethodSecurity\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tMessageService messageService() {\n\t\t\treturn new HelloMessageService();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithsecuritycontext/CustomUserDetails.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithsecuritycontext;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport java.util.Collection;\n\npublic class CustomUserDetails implements UserDetails {\n\n\tprivate final String name;\n\n\tprivate final String username;\n\n\tprivate final Collection<? extends GrantedAuthority> authorities;\n\n\tpublic CustomUserDetails(String name, String username) {\n\t\tthis.name = name;\n\t\tthis.username = username;\n\t\tthis.authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t}\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic String getPassword() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getUsername() {\n\t\treturn this.username;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CustomUserDetails{\" + \"username='\" + this.username + '\\'' + '}';\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithsecuritycontext/WithMockCustomUser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithsecuritycontext;\n\nimport org.springframework.security.test.context.support.WithSecurityContext;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n// tag::snippet[]\n@Retention(RetentionPolicy.RUNTIME)\n@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)\npublic @interface WithMockCustomUser {\n\n\tString username() default \"rob\";\n\n\tString name() default \"Rob Winch\";\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithsecuritycontext/WithMockCustomUserSecurityContextFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithsecuritycontext;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.support.WithSecurityContextFactory;\n\n// tag::snippet[]\npublic class WithMockCustomUserSecurityContextFactory\n\t\timplements WithSecurityContextFactory<WithMockCustomUser> {\n\n\t@Override\n\tpublic SecurityContext createSecurityContext(WithMockCustomUser customUser) {\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tCustomUserDetails principal = new CustomUserDetails(customUser.name(), customUser.username());\n\t\tAuthentication auth = UsernamePasswordAuthenticationToken.authenticated(principal, \"password\",\n\t\t\t\tprincipal.getAuthorities());\n\t\tcontext.setAuthentication(auth);\n\t\treturn context;\n\t}\n\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithsecuritycontext/WithUserDetailsSecurityContextFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithsecuritycontext;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.test.context.support.WithSecurityContextFactory;\nimport org.springframework.security.test.context.support.WithUserDetails;\nimport org.springframework.util.Assert;\n\n// tag::snippet[]\nfinal class WithUserDetailsSecurityContextFactory\n\t\timplements WithSecurityContextFactory<WithUserDetails> {\n\n\tprivate final UserDetailsService userDetailsService;\n\n\t@Autowired\n\tpublic WithUserDetailsSecurityContextFactory(UserDetailsService userDetailsService) {\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\tpublic SecurityContext createSecurityContext(WithUserDetails withUser) {\n\t\tString username = withUser.value();\n\t\tAssert.hasLength(username, \"value() must be non-empty String\");\n\t\tUserDetails principal = userDetailsService.loadUserByUsername(username);\n\t\tAuthentication authentication = UsernamePasswordAuthenticationToken.authenticated(principal, principal.getPassword(), principal.getAuthorities());\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\treturn context;\n\t}\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithuserdetails/WithCustomUserDetailsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithuserdetails;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.core.MessageService;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.docs.servlet.test.testmethod.HelloMessageService;\nimport org.springframework.security.docs.servlet.test.testmethodwithsecuritycontext.CustomUserDetails;\nimport org.springframework.security.test.context.support.WithUserDetails;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\nclass WithCustomUserDetailsTests {\n\n\t@Autowired\n\tMessageService messageService;\n\n\t// tag::custom-user-details-service[]\n\t@Test\n\t@WithUserDetails(value=\"customUsername\", userDetailsServiceBeanName=\"myUserDetailsService\")\n\tvoid getMessageWithUserDetailsServiceBeanName() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message).contains(\"customUsername\");\n\t\tObject principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();\n\t\tassertThat(principal).isInstanceOf(CustomUserDetails.class);\n\t}\n\t// end::custom-user-details-service[]\n\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tUserDetailsService myUserDetailsService() {\n\t\t\treturn new CustomUserDetailsService();\n\t\t}\n\n\t\t@Bean\n\t\tMessageService messageService() {\n\t\t\treturn new HelloMessageService();\n\t\t}\n\t}\n\n\tstatic class CustomUserDetailsService implements UserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {\n\t\t\treturn new CustomUserDetails(\"name\", username);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/java/org/springframework/security/docs/servlet/test/testmethodwithuserdetails/WithUserDetailsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.docs.servlet.test.testmethodwithuserdetails;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.core.MessageService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.docs.servlet.test.testmethod.HelloMessageService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.context.support.WithUserDetails;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\nclass WithUserDetailsTests {\n\n\t@Autowired\n\tMessageService messageService;\n\n\t// tag::user-details[]\n\t@Test\n\t@WithUserDetails\n\tvoid getMessageWithUserDetails() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message).contains(\"user\");\n\t}\n\t// end::user-details[]\n\n\t// tag::user-details-custom-username[]\n\t@Test\n\t@WithUserDetails(\"customUsername\")\n\tvoid getMessageWithUserDetailsCustomUsername() {\n\t\tString message = messageService.getMessage();\n\t\tassertThat(message).contains(\"customUsername\");\n\t}\n\t// end::user-details-custom-username[]\n\n\t@EnableWebSecurity\n\t@Configuration\n\tstatic class Config {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user1 = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"user\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.build();\n\t\t\tUserDetails customUser = User.withDefaultPasswordEncoder()\n\t\t\t\t\t.username(\"customUsername\")\n\t\t\t\t\t.password(\"password\")\n\t\t\t\t\t.build();\n\t\t\treturn new InMemoryUserDetailsManager(user1, customUser);\n\t\t}\n\n\t\t@Bean\n\t\tMessageService messageService() {\n\t\t\treturn new HelloMessageService();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationcompromisedpasswordcheck/CompromisedPasswordCheckerUsage.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.authenticationcompromisedpasswordcheck\n\nimport jakarta.servlet.http.HttpServletRequest\nimport jakarta.servlet.http.HttpServletResponse\nimport org.springframework.context.annotation.Bean\nimport org.springframework.security.authentication.password.CompromisedPasswordChecker\nimport org.springframework.security.authentication.password.CompromisedPasswordException\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.AuthenticationException\nimport org.springframework.security.web.DefaultRedirectStrategy\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler\nimport org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker\n\n\nopen class CompromisedPasswordCheckerUsage {\n    // tag::configuration[]\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            formLogin {\n                authenticationFailureHandler = CompromisedPasswordAuthenticationFailureHandler()\n            }\n        }\n        return http.build()\n    }\n\n    @Bean\n    open fun compromisedPasswordChecker(): CompromisedPasswordChecker {\n        return HaveIBeenPwnedRestApiPasswordChecker()\n    }\n\n    class CompromisedPasswordAuthenticationFailureHandler : AuthenticationFailureHandler {\n        private val defaultFailureHandler = SimpleUrlAuthenticationFailureHandler(\"/login?error\")\n        private val redirectStrategy = DefaultRedirectStrategy()\n\n        override fun onAuthenticationFailure(\n            request: HttpServletRequest,\n            response: HttpServletResponse,\n            exception: AuthenticationException\n        ) {\n            if (exception is CompromisedPasswordException) {\n                redirectStrategy.sendRedirect(request, response, \"/reset-password\")\n                return\n            }\n            defaultFailureHandler.onAuthenticationFailure(request, response, exception)\n        }\n    }\n    // end::configuration[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstorageargon2/Argon2PasswordEncoderUsage.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstorageargon2\n\nimport org.junit.Assert.assertTrue\nimport org.springframework.security.crypto.argon2.Argon2PasswordEncoder\n\nclass Argon2PasswordEncoderUsage {\n    fun testArgon2PasswordEncoder() {\n        // tag::argon2PasswordEncoder[]\n        // Create an encoder with all the defaults\n        val encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()\n        val result: String? = encoder.encode(\"myPassword\")\n        assertTrue(encoder.matches(\"myPassword\", result))\n        // end::argon2PasswordEncoder[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragebcrypt/BCryptPasswordEncoderUsage.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragebcrypt\n\nimport org.junit.Assert.assertTrue\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder\n\nclass BCryptPasswordEncoderUsage {\n    fun testBCryptPasswordEncoder() {\n        // tag::bcryptPasswordEncoder[]\n        // Create an encoder with strength 16\n        val encoder = BCryptPasswordEncoder(16)\n        val result: String? = encoder.encode(\"myPassword\")\n        assertTrue(encoder.matches(\"myPassword\", result))\n        // end::bcryptPasswordEncoder[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragedepgettingstarted/WithDefaultPasswordEncoderUsage.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragedepgettingstarted\n\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetails\n\nclass WithDefaultPasswordEncoderUsage {\n\n    @Suppress(\"DEPRECATION\")\n    fun createSingleUser(): UserDetails {\n        // tag::createSingleUser[]\n        val user = User.withDefaultPasswordEncoder()\n            .username(\"user\")\n            .password(\"password\")\n            .roles(\"user\")\n            .build()\n        println(user.password)\n        // {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG\n        // end::createSingleUser[]\n        return user\n    }\n\n    @Suppress(\"DEPRECATION\")\n    fun createMultipleUsers(): List<UserDetails> {\n        // tag::createMultipleUsers[]\n        val users = User.withDefaultPasswordEncoder()\n        val user = users\n            .username(\"user\")\n            .password(\"password\")\n            .roles(\"USER\")\n            .build()\n        val admin = users\n            .username(\"admin\")\n            .password(\"password\")\n            .roles(\"USER\", \"ADMIN\")\n            .build()\n        // end::createMultipleUsers[]\n        return listOf(user, admin)\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragedpe/DelegatingPasswordEncoderUsage.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragedpe\n\nimport org.springframework.security.crypto.argon2.Argon2PasswordEncoder\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories\nimport org.springframework.security.crypto.password.DelegatingPasswordEncoder\nimport org.springframework.security.crypto.password.PasswordEncoder\nimport org.springframework.security.crypto.password.Pbkdf2PasswordEncoder\nimport org.springframework.security.crypto.scrypt.SCryptPasswordEncoder\n\nclass DelegatingPasswordEncoderUsage {\n    fun defaultDelegatingPasswordEncoder(): PasswordEncoder {\n        // tag::createDefaultPasswordEncoder[]\n        val passwordEncoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()\n        // end::createDefaultPasswordEncoder[]\n        return passwordEncoder\n    }\n\n    @Suppress(\"DEPRECATION\")\n    fun customDelegatingPasswordEncoder(): PasswordEncoder {\n        // tag::createCustomPasswordEncoder[]\n        val idForEncode = \"bcrypt\"\n        val encoders: MutableMap<String, PasswordEncoder> = mutableMapOf()\n        encoders[idForEncode] = BCryptPasswordEncoder()\n        encoders[\"noop\"] = org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()\n        encoders[\"pbkdf2\"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()\n        encoders[\"pbkdf2@SpringSecurity_v5_8\"] = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()\n        encoders[\"scrypt\"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()\n        encoders[\"scrypt@SpringSecurity_v5_8\"] = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()\n        encoders[\"argon2\"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()\n        encoders[\"argon2@SpringSecurity_v5_8\"] = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()\n        encoders[\"sha256\"] = org.springframework.security.crypto.password.StandardPasswordEncoder()\n\n        val passwordEncoder: PasswordEncoder = DelegatingPasswordEncoder(idForEncode, encoders)\n        // end::createCustomPasswordEncoder[]\n        return passwordEncoder\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragepbkdf2/Pbkdf2PasswordEncoderUsage.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragepbkdf2\n\nimport org.junit.Assert.assertTrue\nimport org.springframework.security.crypto.password.Pbkdf2PasswordEncoder\n\nclass Pbkdf2PasswordEncoderUsage {\n    fun testPbkdf2PasswordEncoder() {\n        // tag::pbkdf2PasswordEncoder[]\n        // Create an encoder with all the defaults\n        val encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()\n        val result: String? = encoder.encode(\"myPassword\")\n        assertTrue(encoder.matches(\"myPassword\", result))\n        // end::pbkdf2PasswordEncoder[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/authenticationpasswordstoragescrypt/SCryptPasswordEncoderUsage.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.authenticationpasswordstoragescrypt\n\nimport org.junit.Assert.assertTrue\nimport org.springframework.security.crypto.scrypt.SCryptPasswordEncoder\n\nclass SCryptPasswordEncoderUsage {\n    fun testSCryptPasswordEncoder() {\n        // tag::sCryptPasswordEncoder[]\n        // Create an encoder with all the defaults\n        val encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()\n        val result: String? = encoder.encode(\"myPassword\")\n        assertTrue(encoder.matches(\"myPassword\", result))\n        // end::sCryptPasswordEncoder[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jargon2/Argon2UsageTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.features.authentication.password4jargon2\n\nimport com.password4j.Argon2Function\nimport com.password4j.types.Argon2\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.springframework.security.crypto.password.PasswordEncoder\nimport org.springframework.security.crypto.password4j.Argon2Password4jPasswordEncoder\n\n/**\n * @author Rob Winch\n */\nclass Argon2UsageTests {\n\n    @Test\n    fun defaultParams() {\n        // tag::default-params[]\n        val encoder: PasswordEncoder = Argon2Password4jPasswordEncoder()\n        val result = encoder.encode(\"myPassword\")\n        assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::default-params[]\n    }\n\n    @Test\n    fun customParameters() {\n        // tag::custom-params[]\n        val argon2Fn = Argon2Function.getInstance(\n            65536, 3, 4, 32,\n            Argon2.ID\n        )\n        val encoder: PasswordEncoder = Argon2Password4jPasswordEncoder(argon2Fn)\n        val result = encoder.encode(\"myPassword\")\n        assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::custom-params[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jballooning/BallooningHashingUsageTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.features.authentication.password4jballooning\n\nimport com.password4j.BalloonHashingFunction\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.springframework.security.crypto.password.PasswordEncoder\nimport org.springframework.security.crypto.password4j.BalloonHashingPassword4jPasswordEncoder\n\n/**\n * @author Rob Winch\n */\nclass BallooningHashingUsageTests {\n    @Test\n    fun defaultParams() {\n        // tag::default-params[]\n        val encoder: PasswordEncoder = BalloonHashingPassword4jPasswordEncoder()\n        val result = encoder.encode(\"myPassword\")\n        Assertions.assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::default-params[]\n    }\n\n    @Test\n    fun customParameters() {\n        // tag::custom-params[]\n        val ballooningHashingFn =\n            BalloonHashingFunction.getInstance(\"SHA-256\", 1024, 3, 4, 3)\n        val encoder: PasswordEncoder = BalloonHashingPassword4jPasswordEncoder(ballooningHashingFn)\n        val result = encoder.encode(\"myPassword\")\n        Assertions.assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::custom-params[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jbcrypt/BcryptUsageTests.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.password4jbcrypt\n\nimport com.password4j.BcryptFunction\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.springframework.security.crypto.password.PasswordEncoder\nimport org.springframework.security.crypto.password4j.BcryptPassword4jPasswordEncoder\n\n/**\n * @author Rob Winch\n */\nclass BcryptUsageTests {\n    @Test\n    fun defaultParams() {\n        // tag::default-params[]\n        val encoder: PasswordEncoder = BcryptPassword4jPasswordEncoder()\n        val result = encoder.encode(\"myPassword\")\n        Assertions.assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::default-params[]\n    }\n\n    @Test\n    fun customParameters() {\n        // tag::custom-params[]\n        val bcryptFunction = BcryptFunction.getInstance(12)\n        val encoder: PasswordEncoder = BcryptPassword4jPasswordEncoder(bcryptFunction)\n        val result = encoder.encode(\"myPassword\")\n        Assertions.assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::custom-params[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jpbkdf2/Pbkdf2UsageTests.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.password4jpbkdf2\n\nimport com.password4j.PBKDF2Function\nimport com.password4j.types.Hmac\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.springframework.security.crypto.password.PasswordEncoder\nimport org.springframework.security.crypto.password4j.Pbkdf2Password4jPasswordEncoder\n\n/**\n * @author Rob Winch\n */\nclass Pbkdf2UsageTests {\n    @Test\n    fun defaultParams() {\n        // tag::default-params[]\n        val encoder: PasswordEncoder = Pbkdf2Password4jPasswordEncoder()\n        val result = encoder.encode(\"myPassword\")\n        Assertions.assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::default-params[]\n    }\n\n    @Test\n    fun customParameters() {\n        // tag::custom-params[]\n        val pbkdf2Fn = PBKDF2Function.getInstance(Hmac.SHA256, 100000, 256)\n        val encoder: PasswordEncoder = Pbkdf2Password4jPasswordEncoder(pbkdf2Fn)\n        val result = encoder.encode(\"myPassword\")\n        Assertions.assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::custom-params[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/authentication/password4jscrypt/ScryptUsageTests.kt",
    "content": "package org.springframework.security.kt.docs.features.authentication.password4jscrypt\n\nimport com.password4j.ScryptFunction\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.springframework.security.crypto.password.PasswordEncoder\nimport org.springframework.security.crypto.password4j.ScryptPassword4jPasswordEncoder\n\n/**\n * @author Rob Winch\n */\nclass ScryptUsageTests {\n    @Test\n    fun defaultParams() {\n        // tag::default-params[]\n        val encoder: PasswordEncoder = ScryptPassword4jPasswordEncoder()\n        val result = encoder.encode(\"myPassword\")\n        Assertions.assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::default-params[]\n    }\n\n    @Test\n    fun customParameters() {\n        // tag::custom-params[]\n        val scryptFn = ScryptFunction.getInstance(32768, 8, 1, 32)\n        val encoder: PasswordEncoder = ScryptPassword4jPasswordEncoder(scryptFn)\n        val result = encoder.encode(\"myPassword\")\n        Assertions.assertThat(encoder.matches(\"myPassword\", result)).isTrue()\n        // end::custom-params[]\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/clientregistrationid/User.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid\n\n\n/**\n * A user.\n * @param login\n * @param id\n * @param name\n * @author Rob Winch\n * @see UserService\n */\n@JvmRecord\ndata class User(val login: String, val id: Int, val name: String)\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/clientregistrationid/UserService.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid\n\nimport org.springframework.security.oauth2.client.annotation.ClientRegistrationId\nimport org.springframework.web.service.annotation.GetExchange\nimport org.springframework.web.service.annotation.HttpExchange\n\n/**\n * Demonstrates a service for {@link ClientRegistrationId} and HTTP Service Clients.\n * @author Rob Winch\n */\n@HttpExchange\ninterface UserService {\n\n    // tag::getAuthenticatedUser[]\n    @GetExchange(\"/user\")\n    @ClientRegistrationId(\"github\")\n    fun getAuthenticatedUser() : User\n    // end::getAuthenticatedUser[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/configurationrestclient/RestClientHttpInterfaceIntegrationConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.features.integrations.rest.configurationrestclient\n\nimport okhttp3.mockwebserver.MockWebServer\nimport org.mockito.Mockito\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.UserService\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager\nimport org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer\nimport org.springframework.web.client.RestClient\nimport org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer\nimport org.springframework.web.service.registry.HttpServiceGroup\nimport org.springframework.web.service.registry.HttpServiceGroupConfigurer\nimport org.springframework.web.service.registry.HttpServiceGroupConfigurer.ClientCallback\nimport org.springframework.web.service.registry.ImportHttpServices\n\n/**\n * Documentation for [OAuth2RestClientHttpServiceGroupConfigurer].\n * @author Rob Winch\n */\n@Configuration(proxyBeanMethods = false)\n@ImportHttpServices(types = [UserService::class])\nclass RestClientHttpInterfaceIntegrationConfiguration {\n    // tag::config[]\n    @Bean\n    fun securityConfigurer(manager: OAuth2AuthorizedClientManager): OAuth2RestClientHttpServiceGroupConfigurer {\n        return OAuth2RestClientHttpServiceGroupConfigurer.from(manager)\n    }\n    // end::config[]\n\n    @Bean\n    fun authorizedClientManager(): OAuth2AuthorizedClientManager? {\n        return Mockito.mock<OAuth2AuthorizedClientManager?>(OAuth2AuthorizedClientManager::class.java)\n    }\n\n    @Bean\n    fun groupConfigurer(server: MockWebServer): RestClientHttpServiceGroupConfigurer {\n        return RestClientHttpServiceGroupConfigurer { groups: HttpServiceGroupConfigurer.Groups<RestClient.Builder> ->\n            groups.forEachClient(ClientCallback { group: HttpServiceGroup, builder: RestClient.Builder ->\n                    builder\n                        .baseUrl(server.url(\"\").toString())\n                })\n        }\n    }\n\n    @Bean\n    fun mockServer(): MockWebServer {\n        return MockWebServer()\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/configurationrestclient/RestClientHttpInterfaceIntegrationConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.features.integrations.rest.configurationrestclient\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.http.HttpHeaders\nimport org.springframework.http.MediaType\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.UserService\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager\nimport org.springframework.security.oauth2.core.OAuth2AccessToken\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport java.time.Duration\nimport java.time.Instant\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration(classes = [RestClientHttpInterfaceIntegrationConfiguration::class])\ninternal class RestClientHttpInterfaceIntegrationConfigurationTests {\n    @Test\n    fun getAuthenticatedUser(\n        @Autowired webServer: MockWebServer,\n        @Autowired authorizedClients: OAuth2AuthorizedClientManager,\n        @Autowired users: UserService\n    ) {\n        val registration = CommonOAuth2Provider.GITHUB.getBuilder(\"github\").clientId(\"github\").build()\n\n        val issuedAt = Instant.now()\n        val expiresAt = issuedAt.plus(Duration.ofMinutes(5))\n        val token = OAuth2AccessToken(\n            OAuth2AccessToken.TokenType.BEARER, \"1234\",\n            issuedAt, expiresAt\n        )\n        val result = OAuth2AuthorizedClient(registration, \"rob\", token)\n        mockkObject(authorizedClients)\n        every {\n            authorizedClients.authorize(any())\n        } returns result\n\n        webServer.enqueue(\n            MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(\n                \"\"\"\n                {\"login\": \"rob_winch\", \"id\": 1234, \"name\": \"Rob Winch\" }\n                \"\"\".trimIndent()\n            )\n        )\n\n        users.getAuthenticatedUser()\n\n        Assertions.assertThat(webServer.takeRequest().getHeader(HttpHeaders.AUTHORIZATION))\n            .isEqualTo(\"Bearer \" + token.getTokenValue())\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/configurationwebclient/ServerRestClientHttpInterfaceIntegrationConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.features.integrations.rest.configurationwebclient\n\nimport io.mockk.every\nimport io.mockk.mockkObject\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.http.HttpHeaders\nimport org.springframework.http.MediaType\nimport org.springframework.security.config.oauth2.client.CommonOAuth2Provider\nimport org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.UserService\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager\nimport org.springframework.security.oauth2.core.OAuth2AccessToken\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport reactor.core.publisher.Mono\nimport java.time.Duration\nimport java.time.Instant\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration(classes = [ServerWebClientHttpInterfaceIntegrationConfiguration::class])\ninternal class ServerRestClientHttpInterfaceIntegrationConfigurationTests {\n    @Test\n    @Throws(InterruptedException::class)\n    fun getAuthenticatedUser(\n        @Autowired webServer: MockWebServer,\n        @Autowired authorizedClients: ReactiveOAuth2AuthorizedClientManager,\n        @Autowired users: UserService\n    ) {\n        val registration = CommonOAuth2Provider.GITHUB.getBuilder(\"github\").clientId(\"github\").build()\n\n        val issuedAt = Instant.now()\n        val expiresAt = issuedAt.plus(Duration.ofMinutes(5))\n        val token = OAuth2AccessToken(\n            OAuth2AccessToken.TokenType.BEARER, \"1234\",\n            issuedAt, expiresAt\n        )\n        val result = OAuth2AuthorizedClient(registration, \"rob\", token)\n        mockkObject(authorizedClients)\n        every {\n            authorizedClients.authorize(any())\n        } returns Mono.just(result)\n\n        webServer.enqueue(\n            MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(\n                \"\"\"\n                {\"login\": \"rob_winch\", \"id\": 1234, \"name\": \"Rob Winch\" }\n                \n                \"\"\".trimIndent()\n            )\n        )\n\n        users.getAuthenticatedUser()\n\n        Assertions.assertThat(webServer.takeRequest().getHeader(HttpHeaders.AUTHORIZATION))\n            .isEqualTo(\"Bearer \" + token.getTokenValue())\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/configurationwebclient/ServerWebClientHttpInterfaceIntegrationConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.features.integrations.rest.configurationwebclient\n\nimport okhttp3.mockwebserver.MockWebServer\nimport org.mockito.Mockito\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.UserService\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager\nimport org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer\nimport org.springframework.security.oauth2.client.web.reactive.function.client.support.OAuth2WebClientHttpServiceGroupConfigurer\nimport org.springframework.web.reactive.function.client.WebClient\nimport org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer\nimport org.springframework.web.service.registry.HttpServiceGroup\nimport org.springframework.web.service.registry.HttpServiceGroupConfigurer\nimport org.springframework.web.service.registry.HttpServiceGroupConfigurer.ClientCallback\nimport org.springframework.web.service.registry.ImportHttpServices\n\n/**\n * Documentation for [OAuth2RestClientHttpServiceGroupConfigurer].\n * @author Rob Winch\n */\n@Configuration(proxyBeanMethods = false)\n@ImportHttpServices(types = [UserService::class], clientType = HttpServiceGroup.ClientType.WEB_CLIENT)\nclass ServerWebClientHttpInterfaceIntegrationConfiguration {\n    // tag::config[]\n    @Bean\n    fun securityConfigurer(\n        manager: ReactiveOAuth2AuthorizedClientManager?\n    ): OAuth2WebClientHttpServiceGroupConfigurer {\n        return OAuth2WebClientHttpServiceGroupConfigurer.from(requireNotNull(manager))\n    }\n\n    // end::config[]\n    @Bean\n    fun authorizedClientManager(): ReactiveOAuth2AuthorizedClientManager? {\n        return Mockito.mock<ReactiveOAuth2AuthorizedClientManager?>(ReactiveOAuth2AuthorizedClientManager::class.java)\n    }\n\n    @Bean\n    fun groupConfigurer(server: MockWebServer): WebClientHttpServiceGroupConfigurer {\n        return WebClientHttpServiceGroupConfigurer { groups: HttpServiceGroupConfigurer.Groups<WebClient.Builder> ->\n            val baseUrl = server.url(\"\").toString()\n            groups!!\n                .forEachClient(ClientCallback { group: HttpServiceGroup, builder: WebClient.Builder ->\n                    builder\n                        .baseUrl(baseUrl)\n                        .defaultHeader(\"Accept\", \"application/vnd.github.v3+json\")\n                })\n        }\n    }\n\n    @Bean\n    fun mockServer(): MockWebServer {\n        return MockWebServer()\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/Hovercard.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.features.integrations.rest.type\n\n/**\n * Used to ensure [UserService] compiles, but not show in the documentation.\n *\n * @author Rob Winch\n */\nclass Hovercard\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/UserService.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain clients copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.features.integrations.rest.type\n\nimport org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.User\nimport org.springframework.security.oauth2.client.annotation.ClientRegistrationId\nimport org.springframework.web.bind.annotation.PathVariable\nimport org.springframework.web.service.annotation.GetExchange\nimport org.springframework.web.service.annotation.HttpExchange\n\n/**\n * Demonstrates a service for [ClientRegistrationId] at the type level.\n * @author Rob Winch\n */\n// tag::type[]\n@HttpExchange\n@ClientRegistrationId(\"github\")\ninterface UserService {\n    @GetExchange(\"/user\")\n    fun getAuthenticatedUser(): User\n\n    @GetExchange(\"/users/{username}/hovercard\")\n    fun getHovercard(@PathVariable username: String): Hovercard\n}\n// end::type[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/authentication/reactivex509/CustomX509Configuration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.reactive.authentication.reactivex509\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authentication.ReactiveAuthenticationManager\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.invoke\nimport org.springframework.security.config.web.server.ServerHttpSecurity\nimport org.springframework.security.config.web.server.ServerHttpSecurity.http\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.authentication.ReactivePreAuthenticatedAuthenticationManager\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Demonstrates custom configuration for x509 reactive configuration.\n *\n * @author Rob Winch\n */\n@EnableWebFlux\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\nclass CustomX509Configuration {\n\n    // tag::springSecurity[]\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        val extractor = SubjectX500PrincipalExtractor()\n        extractor.setExtractPrincipalNameFromEmail(true)\n\n        // @formatter:off\n        val user = User\n            .withUsername(\"luke@monkeymachine\")\n            .password(\"password\")\n            .roles(\"USER\")\n            .build()\n        // @formatter:on\n\n        val users: ReactiveUserDetailsService = MapReactiveUserDetailsService(user)\n        val authentication: ReactiveAuthenticationManager = ReactivePreAuthenticatedAuthenticationManager(users)\n\n        return http {\n            x509 {\n                principalExtractor = extractor\n                authenticationManager = authentication\n            }\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n        }\n    }\n    // end::springSecurity[]\n\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/authentication/reactivex509/DefaultX509Configuration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.reactive.authentication.reactivex509\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.ServerHttpSecurity\nimport org.springframework.security.config.web.server.invoke\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.web.reactive.config.EnableWebFlux\n\n/**\n * Demonstrates custom configuration for x509 reactive configuration.\n *\n * @author Rob Winch\n */\n@EnableWebFlux\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\nclass DefaultX509Configuration {\n\n    // tag::springSecurity[]\n    @Bean\n    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {\n        return http {\n            x509 { }\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n        }\n    }\n    // end::springSecurity[]\n\n    @Bean\n    fun userDetailsService(): MapReactiveUserDetailsService {\n        // @formatter:off\n        val user = User\n            .withUsername(\"rod\")\n            .password(\"password\")\n            .roles(\"USER\")\n            .build()\n        // @formatter:on\n\n        return MapReactiveUserDetailsService(user)\n    }\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/authentication/reactivex509/X509ConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.reactive.authentication.reactivex509\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.core.io.ClassPathResource\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder.Http200RestController\nimport org.springframework.security.web.authentication.preauth.x509.X509TestUtils\nimport org.springframework.test.web.reactive.server.UserWebTestClientConfigurer.x509\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.web.server.WebFilter\nimport java.security.cert.Certificate\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass X509ConfigurationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    var client: WebTestClient? = null\n\n    @Autowired\n    fun setSpringSecurityFilterChain(springSecurityFilterChain: WebFilter) {\n        this.client = WebTestClient\n            .bindToController(Http200RestController::class.java)\n            .webFilter<WebTestClient.ControllerSpec>(springSecurityFilterChain)\n            .apply<WebTestClient.ControllerSpec>(SecurityMockServerConfigurers.springSecurity())\n            .configureClient()\n            .build()\n    }\n\n    @Test\n    fun x509WhenDefaultX509Configuration() {\n        this.spring.register(DefaultX509Configuration::class.java).autowire()\n        val certificate = loadCert<X509Certificate>(\"rod.cer\")\n        // @formatter:off\n        this.client!!.mutateWith(x509(certificate))\n            .get()\n            .uri(\"/\")\n            .exchange()\n            .expectStatus().isOk()\n        // @formatter:on\n    }\n\n    @Test\n    fun x509WhenCustomX509Configuration() {\n        this.spring.register(CustomX509Configuration::class.java).autowire()\n        val certificate = X509TestUtils.buildTestCertificate()\n        // @formatter:off\n        this.client!!.mutateWith(x509(certificate))\n            .get()\n            .uri(\"/\")\n            .exchange()\n            .expectStatus().isOk()\n        // @formatter:on\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    private fun <T : Certificate?> loadCert(location: String): T {\n        try {\n            ClassPathResource(location).inputStream.use { `is` ->\n                val certFactory = CertificateFactory.getInstance(\"X.509\")\n                return certFactory.generateCertificate(`is`) as T\n            }\n        } catch (ex: Exception) {\n            throw IllegalArgumentException(ex)\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/customizerbeanordering/CustomizerBeanOrderingConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.reactive.configuration.customizerbeanordering\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.Ordered\nimport org.springframework.core.annotation.Order\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.ThrowingCustomizer\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.ServerHttpSecurity\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.anyExchange\n\n/**\n *\n */\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class CustomizerBeanOrderingConfiguration {\n    // tag::sample[]\n    @Bean // <4>\n    fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n        // @formatter:off\n        http\n            .authorizeExchange({ exchanges -> exchanges\n                .anyExchange().authenticated()\n            })\n        return http.build()\n        // @formatter:on\n    }\n\n    @Bean\n    @Order(Ordered.LOWEST_PRECEDENCE)  // <2>\n    fun userAuthorization(): Customizer<ServerHttpSecurity> {\n        // @formatter:off\n        return Customizer { http -> http\n            .authorizeExchange { exchanges -> exchanges\n                .pathMatchers(\"/users/**\").hasRole(\"USER\")\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    @Order(Ordered.HIGHEST_PRECEDENCE) // <1>\n    fun adminAuthorization(): Customizer<ServerHttpSecurity> {\n        // @formatter:off\n        return ThrowingCustomizer { http -> http\n            .authorizeExchange { exchanges -> exchanges\n                .pathMatchers(\"/admins/**\").hasRole(\"ADMIN\")\n            }\n        }\n        // @formatter:on\n    }\n\n    // <3>\n\n    @Bean\n    fun contentSecurityPolicy(): Customizer<ServerHttpSecurity.HeaderSpec> {\n        // @formatter:off\n        return Customizer { headers -> headers\n            .contentSecurityPolicy { csp -> csp\n                .policyDirectives(\"object-src 'none'\")\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    fun contentTypeOptions(): Customizer<ServerHttpSecurity.HeaderSpec> {\n        // @formatter:off\n        return Customizer { headers -> headers\n            .contentTypeOptions(Customizer.withDefaults())\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    fun httpsRedirect(): Customizer<ServerHttpSecurity.HttpsRedirectSpec> {\n        // @formatter:off\n        return Customizer.withDefaults()\n        // @formatter:on\n    }\n    // end::sample[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/customizerbeanordering/CustomizerBeanOrderingTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.reactive.configuration.customizerbeanordering\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass CustomizerBeanOrderingTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var webTest: WebTestClient\n\n    @Test\n    fun authorizationOrdered() {\n        this.spring.register(CustomizerBeanOrderingConfiguration::class.java).autowire()\n        // @formatter:off\n        this.webTest.mutateWith(mockUser(\"admin\").roles(\"ADMIN\"))\n            .get()\n            .uri(\"https://localhost/admins/1\")\n            .exchange()\n            .expectStatus().isOk\n        this.webTest.mutateWith(mockUser(\"user\").roles(\"USER\"))\n            .get()\n            .uri(\"https://localhost/admins/1\")\n            .exchange()\n            .expectStatus().isForbidden\n        this.webTest.mutateWith(mockUser(\"user\").roles(\"USER\"))\n            .get()\n            .uri(\"https://localhost/users/1\")\n            .exchange()\n            .expectStatus().isOk\n        this.webTest.mutateWith(mockUser(\"user\").roles(\"OTHER\"))\n            .get()\n            .uri(\"https://localhost/users/1\")\n            .exchange()\n             .expectStatus().isForbidden\n        this.webTest.mutateWith(mockUser(\"other\").roles(\"OTHER\"))\n            .get()\n            .uri(\"https://localhost/other\")\n            .exchange()\n            .expectStatus().isOk\n        // @formatter:on\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/dslbeanordering/DslBeanOrderingConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.reactive.configuration.dslbeanordering\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.Ordered\nimport org.springframework.core.annotation.Order\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.*\nimport org.springframework.security.web.server.SecurityWebFilterChain\n\n/**\n *\n */\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class DslBeanOrderingConfiguration {\n    // tag::sample[]\n    // All of the Java Modular Configuration is applied first <1>\n\n    @Bean // <5>\n    fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n        // @formatter:off\n        return http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    @Order(Ordered.LOWEST_PRECEDENCE)  // <3>\n    fun userAuthorization(): ServerHttpSecurityDsl.() -> Unit {\n        // @formatter:off\n        return {\n            authorizeExchange {\n                authorize(\"/users/**\", hasRole(\"USER\"))\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    @Order(Ordered.HIGHEST_PRECEDENCE) // <2>\n    fun adminAuthorization(): ServerHttpSecurityDsl.() -> Unit {\n        // @formatter:off\n        return {\n            authorizeExchange {\n                authorize(\"/admins/**\", hasRole(\"ADMIN\"))\n            }\n        }\n        // @formatter:on\n    }\n\n    // <4>\n\n    @Bean\n    fun contentSecurityPolicy(): ServerHeadersDsl.() -> Unit {\n        // @formatter:off\n        return {\n            contentSecurityPolicy {\n                policyDirectives = \"object-src 'none'\"\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    fun contentTypeOptions(): ServerHeadersDsl.() -> Unit {\n        // @formatter:off\n        return {\n            contentTypeOptions { }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    fun httpsRedirect(): ServerHttpsRedirectDsl.() -> Unit {\n        // @formatter:off\n        return { }\n        // @formatter:on\n    }\n    // end::sample[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/dslbeanordering/DslBeanOrderingTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.reactive.configuration.dslbeanordering\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.kt.docs.servlet.configuration.customizerbeanordering.CustomizerBeanOrderingConfiguration\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass DslBeanOrderingTests {\n    @JvmField\n    val spring = SpringTestContext(this).mockMvcAfterSpringSecurityOk()\n\n    @Autowired\n    lateinit var webTest: WebTestClient\n\n    @Test\n    fun dslOrdered() {\n        this.spring.register(org.springframework.security.kt.docs.reactive.configuration.customizerbeanordering.CustomizerBeanOrderingConfiguration::class.java).autowire()\n        // @formatter:off\n        this.webTest.mutateWith(mockUser(\"admin\").roles(\"ADMIN\"))\n            .get()\n            .uri(\"https://localhost/admins/1\")\n            .exchange()\n            .expectStatus().isOk\n        this.webTest.mutateWith(mockUser(\"user\").roles(\"USER\"))\n            .get()\n            .uri(\"https://localhost/admins/1\")\n            .exchange()\n            .expectStatus().isForbidden\n        this.webTest.mutateWith(mockUser(\"user\").roles(\"USER\"))\n            .get()\n            .uri(\"https://localhost/users/1\")\n            .exchange()\n            .expectStatus().isOk\n        this.webTest.mutateWith(mockUser(\"user\").roles(\"OTHER\"))\n            .get()\n            .uri(\"https://localhost/users/1\")\n            .exchange()\n            .expectStatus().isForbidden\n        this.webTest.mutateWith(mockUser(\"other\").roles(\"OTHER\"))\n            .get()\n            .uri(\"https://localhost/other\")\n            .exchange()\n            .expectStatus().isOk\n        // @formatter:on\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/serverhttpsecuritycustomizerbean/ServerHttpSecurityCustomizerBeanConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.reactive.configuration.serverhttpsecuritycustomizerbean\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.ServerHttpSecurity\nimport org.springframework.security.web.server.SecurityWebFilterChain\n\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\nclass ServerHttpSecurityCustomizerBeanConfiguration {\n\n    @Bean\n    fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n        // @formatter:off\n        http\n            .authorizeExchange({ exchanges -> exchanges\n                .anyExchange().authenticated()\n            })\n        return http.build()\n        // @formatter:on\n    }\n\n\n    // tag::httpSecurityCustomizer[]\n    @Bean\n    fun httpSecurityCustomizer(): Customizer<ServerHttpSecurity> {\n        // @formatter:off\n        return Customizer { http -> http\n            .headers { headers -> headers\n                .contentSecurityPolicy { csp -> csp\n                    // <1>\n                    .policyDirectives(\"object-src 'none'\")\n                }\n            }\n            // <2>\n            .redirectToHttps(Customizer.withDefaults())\n        }\n        // @formatter:on\n    }\n    // end::httpSecurityCustomizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/serverhttpsecuritycustomizerbean/ServerHttpSecurityCustomizerBeanTests.kt",
    "content": "package org.springframework.security.kt.docs.reactive.configuration.serverhttpsecuritycustomizerbean\n\nimport org.assertj.core.api.Assertions\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport java.util.function.Consumer\n\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerHttpSecurityCustomizerBeanTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var webTest: WebTestClient\n\n    @Test\n    fun `serverhttpsecurity customizer config`() {\n        this.spring.register(ServerHttpSecurityCustomizerBeanConfiguration::class.java).autowire()\n        // @formatter:off\n        this.webTest\n            .get()\n            .uri(\"http://localhost/\")\n            .exchange()\n            .expectHeader().location(\"https://localhost/\")\n            .expectHeader()\n            .value(\"Content-Security-Policy\", Consumer { csp ->\n                assertThat(csp).isEqualTo(\"object-src 'none'\")\n            })\n            // @formatter:on\n    }\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/serverhttpsecuritydslbean/ServerHttpSecurityDslBeanConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.reactive.configuration.serverhttpsecuritydslbean\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.ServerHttpSecurity\nimport org.springframework.security.config.web.server.ServerHttpSecurityDsl\nimport org.springframework.security.config.web.server.invoke\nimport org.springframework.security.web.server.SecurityWebFilterChain\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\nclass ServerHttpSecurityDslBeanConfiguration {\n\n    @Bean\n    fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n        return http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n        }\n    }\n\n    // tag::httpSecurityDslBean[]\n    @Bean\n    fun httpSecurityDslBean(): ServerHttpSecurityDsl.() -> Unit {\n        return {\n            headers {\n                contentSecurityPolicy {\n                    // <1>\n                    policyDirectives = \"object-src 'none'\"\n                }\n            }\n            // <2>\n            redirectToHttps { }\n        }\n    }\n    // end::httpSecurityDslBean[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/serverhttpsecuritydslbean/ServerHttpSecurityDslBeanTests.kt",
    "content": "package org.springframework.security.kt.docs.reactive.configuration.serverhttpsecuritydslbean\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport java.util.function.Consumer\n\n\n@ExtendWith(SpringTestContextExtension::class)\nclass ServerHttpSecurityDslBeanTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var webTest: WebTestClient\n\n    @Test\n    fun `ServerHttpSecurityDslBean`() {\n        this.spring.register(ServerHttpSecurityDslBeanConfiguration::class.java).autowire()\n\n        // @formatter:off\n        this.webTest\n            .get()\n            .uri(\"http://localhost/\")\n            .exchange()\n            .expectHeader().location(\"https://localhost/\")\n            .expectHeader().value(\"Content-Security-Policy\", Consumer { csp ->\n                assertThat(csp).isEqualTo(\"object-src 'none'\")\n            })\n        // @formatter:on\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/toplevelcustomizerbean/TopLevelCustomizerBeanConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.reactive.configuration.toplevelcustomizerbean\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.ServerHttpSecurity\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.server.SecurityWebFilterChain\n\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\nclass TopLevelCustomizerBeanConfiguration {\n\n    @Bean\n    fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n        // @formatter:off\n        http\n            .authorizeExchange({ exchanges -> exchanges\n                .anyExchange().authenticated()\n            })\n        return http.build()\n        // @formatter:on\n    }\n\n    // tag::headersCustomizer[]\n    @Bean\n    fun headersSecurity(): Customizer<ServerHttpSecurity.HeaderSpec> {\n        // @formatter:off\n        return Customizer { headers -> headers\n            .contentSecurityPolicy { csp -> csp\n                // <1>\n                .policyDirectives(\"object-src 'none'\")\n            }\n        }\n        // @formatter:on\n    }\n    // end::headersCustomizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/toplevelcustomizerbean/TopLevelCustomizerBeanTests.kt",
    "content": "package org.springframework.security.kt.docs.reactive.configuration.toplevelcustomizerbean\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport java.util.function.Consumer\n\n@ExtendWith(SpringTestContextExtension::class)\nclass TopLevelCustomizerBeanTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var webTest: WebTestClient\n\n    @Test\n    fun `top level dsl bean`() {\n        this.spring.register(TopLevelCustomizerBeanConfiguration::class.java).autowire()\n\n        // @formatter:off\n        this.webTest\n            .get()\n            .uri(\"http://localhost/\")\n            .exchange()\n            .expectHeader().value(\"Content-Security-Policy\", Consumer { csp ->\n                assertThat(csp).isEqualTo(\"object-src 'none'\")\n            })\n        // @formatter:on\n    }\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/topleveldslbean/TopLevelDslBeanConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.reactive.configuration.topleveldslbean\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity\nimport org.springframework.security.config.web.server.ServerHeadersDsl\nimport org.springframework.security.config.web.server.ServerHttpSecurity\nimport org.springframework.security.config.web.server.invoke\nimport org.springframework.security.web.server.SecurityWebFilterChain\n\n\n@EnableWebFluxSecurity\n@Configuration(proxyBeanMethods = false)\nclass TopLevelDslBeanConfiguration {\n\n    @Bean\n    fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {\n        return http {\n            authorizeExchange {\n                authorize(anyExchange, authenticated)\n            }\n        }\n    }\n\n    // tag::headersSecurity[]\n    @Bean\n    fun headersSecurity(): ServerHeadersDsl.() -> Unit {\n        return {\n            contentSecurityPolicy {\n                // <1>\n                policyDirectives = \"object-src 'none'\"\n            }\n        }\n    }\n    // end::headersSecurity[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/configuration/topleveldslbean/TopLevelDslBeanTests.kt",
    "content": "package org.springframework.security.kt.docs.reactive.configuration.topleveldslbean\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.test.web.reactive.server.WebTestClient\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport java.util.function.Consumer\n\n\n@ExtendWith(SpringTestContextExtension::class)\nclass TopLevelDslBeanTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var webTest: WebTestClient\n\n    @Test\n    fun `HttpSecurityDslBean`() {\n        this.spring.register(TopLevelDslBeanConfiguration::class.java).autowire()\n\n        // @formatter:off\n        this.webTest\n            .get()\n            .uri(\"http://localhost/\")\n            .exchange()\n            .expectHeader().value(\"Content-Security-Policy\", Consumer { csp ->\n                assertThat(csp).isEqualTo(\"object-src 'none'\")\n            })\n        // @formatter:on\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/addingcustomfilter/CustomFilterTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.addingcustomfilter\n\nimport org.assertj.core.api.Assertions.assertThatExceptionOfType\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetails\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.context.web.WebAppConfiguration\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders\nimport org.springframework.web.context.WebApplicationContext\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user\nimport org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated\nimport org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration(\n\tclasses = [\n\t\tCustomFilterTests.UserDetailsConfig::class,\n\t\tCustomFilterTests.ApiController::class,\n\t\tSecurityConfig::class\n\t]\n)\n@WebAppConfiguration\nclass CustomFilterTests {\n\n\t@Autowired\n\tprivate lateinit var context: WebApplicationContext\n\n\tprivate lateinit var mvc: MockMvc\n\n\t@BeforeEach\n\tfun setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.apply<DefaultMockMvcBuilder>(springSecurity())\n\t\t\t.build();\n\t}\n\n\t@Test\n\tfun tenantFilterWhenHeaderMissingThenAccessDenied() {\n\t\tassertThatExceptionOfType(Exception::class.java)\n\t\t\t.isThrownBy { this.mvc.perform(get(\"/api\").with(user(\"user\"))).andReturn() }\n\t}\n\n\t@Test\n\tfun tenantFilterWhenHeaderPresentThenContinuesFilterChain() {\n\t\tthis.mvc.perform(get(\"/api\")\n\t\t\t\t.with(user(\"user\"))\n\t\t\t\t.header(\"X-Tenant-Id\", \"some-tenant-id\"))\n\t\t\t.andExpect(status().isOk)\n\t\t\t.andExpect(authenticated().withUsername(\"user\"))\n\t}\n\n\t@Configuration\n\topen class UserDetailsConfig {\n\t\t@Bean\n\t\topen fun userDetailsService(): UserDetailsService {\n\t\t\t@Suppress(\"DEPRECATION\")\n\t\t\tval user: UserDetails = User.withDefaultPasswordEncoder()\n\t\t\t\t.username(\"user\")\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build()\n\t\t\treturn InMemoryUserDetailsManager(user)\n\t\t}\n\t}\n\n\t@RestController\n\tclass ApiController {\n\n\t\t@GetMapping(\"/api\")\n\t\tfun api(): String {\n\t\t\treturn \"ok\"\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/addingcustomfilter/SecurityConfig.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.addingcustomfilter\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.AnonymousAuthenticationFilter\n\n@Configuration\n@EnableWebSecurity\nopen class SecurityConfig {\n\n\t// tag::snippet[]\n\t@Bean\n\topen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp\n\t\t\t// ...\n\t\t\t.addFilterAfter(TenantFilter(), AnonymousAuthenticationFilter::class.java) // <1>\n\t\treturn http.build()\n\t}\n\t// end::snippet[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/addingcustomfilter/TenantFilter.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.addingcustomfilter\n\nimport jakarta.servlet.Filter\nimport jakarta.servlet.FilterChain\nimport jakarta.servlet.ServletException\nimport jakarta.servlet.ServletRequest\nimport jakarta.servlet.ServletResponse\nimport jakarta.servlet.http.HttpServletRequest\nimport jakarta.servlet.http.HttpServletResponse\nimport org.springframework.security.access.AccessDeniedException\nimport java.io.IOException\n\n// tag::snippet[]\nclass TenantFilter : Filter {\n\n\t@Throws(IOException::class, ServletException::class)\n\toverride fun doFilter(servletRequest: ServletRequest, servletResponse: ServletResponse, filterChain: FilterChain) {\n\t\tval request = servletRequest as HttpServletRequest\n\t\tval response = servletResponse as HttpServletResponse\n\n\t\tval tenantId = request.getHeader(\"X-Tenant-Id\") // <1>\n\t\tval hasAccess = isUserAllowed(tenantId) // <2>\n\t\tif (hasAccess) {\n\t\t\tfilterChain.doFilter(request, response) // <3>\n\t\t\treturn\n\t\t}\n\t\tthrow AccessDeniedException(\"Access denied\") // <4>\n\t}\n\n\tprivate fun isUserAllowed(tenantId: String?): Boolean {\n\t\treturn \"some-tenant-id\" == tenantId\n\t}\n\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/authorizationmanagerfactory/AuthorizationManagerFactoryTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.authorizationmanagerfactory\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass AuthorizationManagerFactoryTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithPasswordAndOttThenPermits() {\n        this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java)\n            .autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithPasswordThenRedirectsToOtt() {\n        this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java)\n            .autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.OTT_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithOttThenRedirectsToPassword() {\n        this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java)\n            .autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedThenRedirectsToPassword() {\n        this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java)\n            .autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getWhenUnauthenticatedThenRedirectsToBoth() {\n        this.spring.register(UseAuthorizationManagerFactoryConfiguration::class.java, Http200Controller::class.java)\n            .autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login\"))\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/authorizationmanagerfactory/UseAuthorizationManagerFactoryConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.authorizationmanagerfactory\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.AuthorizationManagerFactories\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class UseAuthorizationManagerFactoryConfiguration {\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(\"/admin/**\", hasRole(\"ADMIN\"))\n                authorize(anyRequest, authenticated)\n            }\n            formLogin { }\n            oneTimeTokenLogin { }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    // tag::authorizationManagerFactoryBean[]\n    @Bean\n    fun authz(): AuthorizationManagerFactory<Any> {\n        return AuthorizationManagerFactories.multiFactor<Any>()\n            .requireFactors(\n                FactorGrantedAuthority.PASSWORD_AUTHORITY,\n                FactorGrantedAuthority.OTT_AUTHORITY\n            )\n            .build()\n    }\n    // end::authorizationManagerFactoryBean[]\n\n    // tag::customizer[]\n    @Bean\n    fun additionalRequiredFactorsCustomizer(): Customizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Any>> {\n        return Customizer { builder -> builder.`when` { auth -> \"admin\" == auth.name } }\n    }\n    // end::customizer[]\n\n    @Suppress(\"DEPRECATION\")\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        return InMemoryUserDetailsManager(\n            User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"app\")\n                .build()\n        )\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/emfa/EnableMultiFactorAuthenticationConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.emfa\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.AuthorizationManagerFactories\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\n\n// tag::enable-mfa[]\n@EnableMultiFactorAuthentication( authorities = [\n    FactorGrantedAuthority.PASSWORD_AUTHORITY,\n    FactorGrantedAuthority.OTT_AUTHORITY])\n// end::enable-mfa[]\ninternal class EnableMultiFactorAuthenticationConfiguration {\n\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                // <1>\n                authorize(\"/admin/**\", hasRole(\"ADMIN\"))\n                // <2>\n                authorize(anyRequest, authenticated)\n            }\n            // <3>\n            formLogin { }\n            oneTimeTokenLogin {  }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    @Suppress(\"DEPRECATION\")\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        return InMemoryUserDetailsManager(\n            User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"app\")\n                .build()\n        )\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/emfa/EnableMultiFactorAuthenticationConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.emfa\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass EnableMultiFactorAuthenticationConfigurationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY, \"ROLE_ADMIN\"])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithPasswordAndOttThenPermits() {\n        this.spring.register(EnableMultiFactorAuthenticationConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithPasswordThenRedirectsToOtt() {\n        this.spring.register(EnableMultiFactorAuthenticationConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.OTT_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithOttThenRedirectsToPassword() {\n        this.spring.register(EnableMultiFactorAuthenticationConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedThenRedirectsToPassword() {\n        this.spring.register(EnableMultiFactorAuthenticationConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getWhenUnauthenticatedThenRedirectsToBoth() {\n        this.spring.register(EnableMultiFactorAuthenticationConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login\"))\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/hasallauthorities/ListAuthoritiesConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.hasallauthorities\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class ListAuthoritiesConfiguration {\n\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                // <1>\n                authorize(anyRequest, hasAllAuthorities(\n                    FactorGrantedAuthority.PASSWORD_AUTHORITY,\n                    FactorGrantedAuthority.OTT_AUTHORITY\n                ))\n            }\n            // <2>\n            formLogin { }\n            oneTimeTokenLogin {  }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    @Suppress(\"DEPRECATION\")\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        return InMemoryUserDetailsManager(\n            User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"app\")\n                .build()\n        )\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/hasallauthorities/MultiFactorAuthenticationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.hasallauthorities\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass MultiFactorAuthenticationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithPasswordAndOttThenPermits() {\n        this.spring.register(ListAuthoritiesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithPasswordThenRedirectsToOtt() {\n        this.spring.register(ListAuthoritiesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.OTT_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithOttThenRedirectsToPassword() {\n        this.spring.register(ListAuthoritiesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedThenRedirectsToPassword() {\n        this.spring.register(ListAuthoritiesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getWhenUnauthenticatedThenRedirectsToBoth() {\n        this.spring.register(ListAuthoritiesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login\"))\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/hasallauthorities/MultipleAuthorizationRulesConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.hasallauthorities\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class MultipleAuthorizationRulesConfiguration {\n\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                // <1>\n                authorize(\"/admin/**\", hasAllAuthorities(\n                    \"ROLE_ADMIN\",\n                    FactorGrantedAuthority.PASSWORD_AUTHORITY,\n                    FactorGrantedAuthority.OTT_AUTHORITY\n                ))\n                // <2>\n                authorize(anyRequest, hasAllAuthorities(\n                    \"ROLE_USER\",\n                    FactorGrantedAuthority.PASSWORD_AUTHORITY,\n                    FactorGrantedAuthority.OTT_AUTHORITY\n                ))\n            }\n            // <3>\n            formLogin { }\n            oneTimeTokenLogin {  }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    @Suppress(\"DEPRECATION\")\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        return InMemoryUserDetailsManager(\n            User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"app\")\n                .build()\n        )\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/hasallauthorities/MultipleAuthorizationRulesConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.hasallauthorities\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass MultipleAuthorizationRulesConfigurationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY, \"ROLE_USER\"])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithPasswordAndOttThenPermits() {\n        this.spring.register(MultipleAuthorizationRulesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithPasswordThenRedirectsToOtt() {\n        this.spring.register(MultipleAuthorizationRulesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.OTT_AUTHORITY])\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedWithOttThenRedirectsToPassword() {\n        this.spring.register(MultipleAuthorizationRulesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=password&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser\n    @Throws(Exception::class)\n    fun getWhenAuthenticatedThenRedirectsToPassword() {\n        this.spring.register(MultipleAuthorizationRulesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=password&factor.type=ott&factor.reason=missing&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getWhenUnauthenticatedThenRedirectsToBoth() {\n        this.spring.register(MultipleAuthorizationRulesConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login\"))\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/mfawhencustomconditions/CustomizerAuthorizationManagerFactoryConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.mfawhencustomconditions\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.AuthorizationManagerFactories\nimport org.springframework.security.config.Customizer\n\n@Configuration(proxyBeanMethods = false)\ninternal class CustomizerAuthorizationManagerFactoryConfiguration {\n\n    // tag::customizer[]\n    @Bean\n    fun additionalRequiredFactorsCustomizer(): Customizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Any>> {\n        return Customizer { builder -> builder.`when` { auth -> \"admin\" == auth.name } }\n    }\n    // end::customizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/mfawhenwebauthnregistered/WebAuthnConditionConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.mfawhenwebauthnregistered\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication\nimport org.springframework.security.config.annotation.authorization.MultiFactorCondition\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository\nimport org.springframework.security.web.webauthn.management.MapUserCredentialRepository\nimport org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\n// tag::enable-mfa-webauthn[]\n@EnableMultiFactorAuthentication(\n    authorities = [\n        FactorGrantedAuthority.PASSWORD_AUTHORITY,\n        FactorGrantedAuthority.WEBAUTHN_AUTHORITY\n    ],\n    `when` = [MultiFactorCondition.WEBAUTHN_REGISTERED]\n)\ninternal class WebAuthnConditionConfiguration {\n\n    @Bean\n    fun userEntityRepository(): PublicKeyCredentialUserEntityRepository {\n        return MapPublicKeyCredentialUserEntityRepository()\n    }\n\n    @Bean\n    fun userCredentialRepository(): UserCredentialRepository {\n        return MapUserCredentialRepository()\n    }\n\n}\n// end::enable-mfa-webauthn[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/obtainingmoreauthorization/MissingAuthorityConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.obtainingmoreauthorization\n\nimport jakarta.servlet.http.HttpServletRequest\nimport jakarta.servlet.http.HttpServletResponse\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.AuthorizationManagerFactories\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.AuthenticationException\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations\nimport org.springframework.security.web.AuthenticationEntryPoint\nimport org.springframework.security.web.DefaultSecurityFilterChain\nimport org.springframework.stereotype.Component\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class MissingAuthorityConfiguration {\n\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity, oauth2: ScopeRetrievingAuthenticationEntryPoint): DefaultSecurityFilterChain? {\n        http {\n            authorizeHttpRequests {\n                authorize(\"/profile/**\", hasAuthority(\"SCOPE_profile:read\"))\n                authorize(anyRequest, authenticated)\n            }\n            x509 { }\n            oauth2Login { }\n        }\n\n        http.exceptionHandling { e: ExceptionHandlingConfigurer<HttpSecurity> -> e\n            .defaultDeniedHandlerForMissingAuthority(oauth2, \"SCOPE_profile:read\")\n        }\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    // tag::authenticationEntryPoint[]\n    @Component\n    internal class ScopeRetrievingAuthenticationEntryPoint : AuthenticationEntryPoint {\n        override fun commence(request: HttpServletRequest, response: HttpServletResponse, authException: AuthenticationException) {\n            response.sendRedirect(\"https://authz.example.org/authorize?scope=profile:read\")\n        }\n    }\n    // end::authenticationEntryPoint[]\n\n    // tag::authorizationManagerFactoryBean[]\n    @Bean\n    fun authz(): AuthorizationManagerFactory<Any> {\n        return AuthorizationManagerFactories.multiFactor<Any>()\n                .requireFactors(\n                    FactorGrantedAuthority.X509_AUTHORITY,\n                    FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY\n                )\n                .build()\n    }\n    // end::authorizationManagerFactoryBean[]\n\n    @Bean\n    fun clients(): ClientRegistrationRepository {\n        return InMemoryClientRegistrationRepository(TestClientRegistrations.clientRegistration().build())\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/obtainingmoreauthorization/ObtainingMoreAuthorizationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.obtainingmoreauthorization\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.docs.servlet.authentication.obtainingmoreauthorization.ScopeConfiguration\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass ObtainingMoreAuthorizationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @WithMockUser\n    @Throws(Exception::class)\n    fun profileWhenScopeConfigurationThenDenies() {\n        this.spring.register(ScopeConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/profile\"))\n        .andExpect(MockMvcResultMatchers.status().isForbidden())\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.X509_AUTHORITY, FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY])\n    @Throws(Exception::class)\n    fun profileWhenMissingAuthorityConfigurationThenRedirectsToAuthorizationServer() {\n        this.spring.register(MissingAuthorityConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/profile\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"https://authz.example.org/authorize?scope=profile:read\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"SCOPE_profile:read\"])\n    @Throws(Exception::class)\n    fun profileWhenMissingX509WithOttThenForbidden() {\n        this.spring.register(MissingAuthorityConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/profile\"))\n        .andExpect(MockMvcResultMatchers.status().isForbidden())\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.X509_AUTHORITY, FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY, \"SCOPE_profile:read\"])\n    @Throws(\n        Exception::class\n    )\n    fun profileWhenAuthenticatedAndHasScopeThenPermits() {\n        this.spring.register(MissingAuthorityConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/profile\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/obtainingmoreauthorization/ScopeConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.obtainingmoreauthorization\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations\nimport org.springframework.security.web.SecurityFilterChain\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass ScopeConfiguration {\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(\"/profile/**\", hasAuthority(\"SCOPE_profile:read\"))\n                authorize(anyRequest, authenticated)\n            }\n            x509 { }\n            oauth2Login { }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    @Bean\n    fun clients(): ClientRegistrationRepository {\n        return InMemoryClientRegistrationRepository(TestClientRegistrations.clientRegistration().build())\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/programmaticmfa/AdminMfaAuthorizationManagerConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.programmaticmfa\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.*\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.userdetails.PasswordEncodedUser\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class AdminMfaAuthorizationManagerConfiguration {\n\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                // <1>\n                authorize(\"/admin/**\", hasRole(\"ADMIN\"))\n                // <2>\n                authorize(anyRequest, authenticated)\n            }\n            formLogin { }\n            oneTimeTokenLogin { }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    // tag::authorizationManagerFactory[]\n    @Bean\n    fun authorizationManagerFactory(): AuthorizationManagerFactory<Any> {\n        // <3>\n        return AuthorizationManagerFactories.multiFactor<Any>()\n            // <1>\n            .requireFactors(FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY)\n            // <2>\n            .`when` { auth -> \"admin\" == auth.name }\n            .build()\n    }\n    // end::authorizationManagerFactory[]\n\n    @Bean\n    fun users(): UserDetailsService {\n        return InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin())\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/programmaticmfa/AdminMfaAuthorizationManagerConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.programmaticmfa\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass AdminMfaAuthorizationManagerConfigurationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @Throws(Exception::class)\n    @WithMockUser(username = \"admin\")\n    fun getWhenAdminThenRedirectsToOtt() {\n        this.spring.register(AdminMfaAuthorizationManagerConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(get(\"/\"))\n            .andExpect(status().is3xxRedirection())\n            .andExpect(redirectedUrl(\"/login?factor.type=ott&factor.type=password&factor.reason=missing&factor.reason=missing\"))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    @WithMockUser\n    fun getWhenNotAdminThenAllows() {\n        this.spring.register(AdminMfaAuthorizationManagerConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(get(\"/\"))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withUsername(\"user\"))\n    \t// @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    @WithMockUser(username = \"admin\", authorities = [FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY])\n    fun getWhenAdminAndHasFactorThenAllows() {\n        this.spring.register(AdminMfaAuthorizationManagerConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(get(\"/\"))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withUsername(\"admin\"))\n    \t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/raammfa/RequiredAuthoritiesAuthorizationManagerConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.raammfa\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory\nimport org.springframework.security.authorization.MapRequiredAuthoritiesRepository\nimport org.springframework.security.authorization.RequiredAuthoritiesAuthorizationManager\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.userdetails.PasswordEncodedUser\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class RequiredAuthoritiesAuthorizationManagerConfiguration {\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(\"/admin/**\", hasRole(\"ADMIN\")) // <1>\n                authorize(anyRequest, authenticated) // <2>\n            }\n            formLogin { }\n            oneTimeTokenLogin { }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    // tag::authorizationManager[]\n    @Bean\n    fun adminAuthorization(): RequiredAuthoritiesAuthorizationManager<Any> {\n        // <1>\n        val authorities = MapRequiredAuthoritiesRepository()\n        authorities.saveRequiredAuthorities(\"admin\", listOf(\n            FactorGrantedAuthority.PASSWORD_AUTHORITY,\n            FactorGrantedAuthority.OTT_AUTHORITY)\n        )\n        // <2>\n        return RequiredAuthoritiesAuthorizationManager(authorities)\n    }\n    // end::authorizationManager[]\n\n\n    // tag::authorizationManagerFactory[]\n    @Bean\n    fun authorizationManagerFactory(admins: RequiredAuthoritiesAuthorizationManager<Any>): AuthorizationManagerFactory<Any> {\n        val defaults = DefaultAuthorizationManagerFactory<Any>()\n        // <1>\n        defaults.setAdditionalAuthorization(admins)\n        // <2>\n        return defaults\n    }\n    // end::authorizationManagerFactory[]\n\n    @Bean\n    fun users(): UserDetailsService {\n        return InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin())\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/raammfa/RequiredAuthoritiesAuthorizationManagerConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.raammfa\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass RequiredAuthoritiesAuthorizationManagerConfigurationTests {\n\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @WithMockUser(username = \"admin\")\n    @Throws(Exception::class)\n    fun getWhenAdminThenRedirectsToOtt() {\n        this.spring.register(RequiredAuthoritiesAuthorizationManagerConfiguration::class.java, Http200Controller::class.java)\n            .autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser\n    @Throws(Exception::class)\n    fun getWhenNotAdminThenAllows() {\n        this.spring.register(RequiredAuthoritiesAuthorizationManagerConfiguration::class.java, Http200Controller::class.java)\n            .autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(\n        username = \"admin\",\n        authorities = [FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY]\n    )\n    @Throws(\n        Exception::class\n    )\n    fun getWhenAdminAndHasFactorThenAllows() {\n        this.spring.register(RequiredAuthoritiesAuthorizationManagerConfiguration::class.java, Http200Controller::class.java)\n            .autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"admin\"))\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/reauthentication/ReauthenticationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.reauthentication\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.docs.servlet.authentication.reauthentication.RequireOttConfiguration\nimport org.springframework.security.docs.servlet.authentication.reauthentication.SimpleConfiguration\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass ReauthenticationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @WithMockUser\n    @Throws(Exception::class)\n    fun formLoginWhenSimpleConfigurationThenPermits() {\n        this.spring.register(SimpleConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser\n    @Throws(Exception::class)\n    fun formLoginWhenRequireOttConfigurationThenRedirectsToOtt() {\n        this.spring.register(RequireOttConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/profile\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.OTT_AUTHORITY])\n    @Throws(Exception::class)\n    fun ottWhenRequireOttConfigurationThenAllows() {\n        this.spring.register(RequireOttConfiguration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/profile\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/reauthentication/RequireOttConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.reauthentication\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass RequireOttConfiguration {\n\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(\"/profile/**\", hasAuthority(FactorGrantedAuthority.OTT_AUTHORITY)) // <1>\n                authorize(anyRequest, authenticated)\n            }\n            formLogin { }\n            oneTimeTokenLogin { }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    @Suppress(\"DEPRECATION\")\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        return InMemoryUserDetailsManager(\n            User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"app\")\n                .build()\n        )\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/reauthentication/SimpleConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.reauthentication\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass SimpleConfiguration {\n    // tag::httpSecurity[]\n    @Bean\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            formLogin { }\n            oneTimeTokenLogin { }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::httpSecurity[]\n\n    @Suppress(\"DEPRECATION\")\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        return InMemoryUserDetailsManager(\n            User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"app\")\n                .build()\n        )\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/selectivemfa/SelectiveMfaConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.selectivemfa\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.AuthorizationManagerFactories\nimport org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\n\n// tag::enable-mfa[]\n@EnableMultiFactorAuthentication(authorities = [])\n// end::enable-mfa[]\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class SelectiveMfaConfiguration {\n    // tag::httpSecurity[]\n    @Bean\n    @Throws(Exception::class)\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        // <1>\n        val mfa = AuthorizationManagerFactories.multiFactor<Any>()\n            .requireFactors(\n                FactorGrantedAuthority.PASSWORD_AUTHORITY,\n                FactorGrantedAuthority.OTT_AUTHORITY\n            )\n            .build()\n        http {\n            authorizeHttpRequests {\n                // <2>\n                authorize(\"/admin/**\", mfa.hasRole(\"ADMIN\"))\n                // <3>\n                authorize(\"/user/settings/**\", mfa.authenticated())\n                // <4>\n                authorize(anyRequest, authenticated)\n            }\n            // <5>\n            formLogin { }\n            oneTimeTokenLogin {  }\n        }\n        // @formatter:on\n        return http.build()\n    }\n\n    // end::httpSecurity[]\n    @Suppress(\"DEPRECATION\")\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        return InMemoryUserDetailsManager(\n            User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"app\")\n                .build()\n        )\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/selectivemfa/SelectiveMfaConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.selectivemfa\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass SelectiveMfaConfigurationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY, \"ROLE_ADMIN\"])\n    @Throws(Exception::class)\n    fun adminWhenMissingOttThenRequired() {\n        this.spring.register(\n            SelectiveMfaConfiguration::class.java, Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/admin/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrlPattern(\"/login?*\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.OTT_AUTHORITY, \"ROLE_ADMIN\"])\n    @Throws(\n        Exception::class\n    )\n    fun adminWhenMfaThenAllowed() {\n        this.spring.register(\n            SelectiveMfaConfiguration::class.java, Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/admin/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n        .andExpect(SecurityMockMvcResultMatchers.authenticated().withUsername(\"user\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY, \"ROLE_ADMIN\"])\n    @Throws(Exception::class)\n    fun userSettingsRequiresMfa() {\n        this.spring.register(\n            SelectiveMfaConfiguration::class.java, Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/admin/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrl(\"/login?factor.type=ott&factor.reason=missing\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(authorities = [FactorGrantedAuthority.PASSWORD_AUTHORITY, \"ROLE_USER\"])\n    @Throws(Exception::class)\n    fun userSettingsWhenMissingOttThenRequired() {\n        this.spring.register(\n            SelectiveMfaConfiguration::class.java, Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/user/settings/\"))\n        .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n        .andExpect(MockMvcResultMatchers.redirectedUrlPattern(\"/login?*\"))\n    \t\t// @formatter:on\n    }\n\n    @Test\n    @WithMockUser(roles = [\"USER\"])\n    @Throws(Exception::class)\n    fun rootDoesNotRequireMfa() {\n        this.spring.register(\n            SelectiveMfaConfiguration::class.java, Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/\"))\n        .andExpect(MockMvcResultMatchers.status().isOk())\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/servletauthenticationauthentication/CopyAuthoritiesTests.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.servletauthenticationauthentication\n\nimport org.junit.jupiter.api.Test\nimport org.mockito.ArgumentMatchers\nimport org.mockito.BDDMockito\nimport org.mockito.Mockito\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.authentication.SecurityAssertions\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthentication\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.authority.AuthorityUtils\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.context.SecurityContextHolder\n\nclass CopyAuthoritiesTests {\n    @Test\n    fun toBuilderWhenApplyThenCopies() {\n        val previous: Authentication = UsernamePasswordAuthenticationToken(\"alice\", \"pass\",\n            AuthorityUtils.createAuthorityList(FactorGrantedAuthority.PASSWORD_AUTHORITY))\n        SecurityContextHolder.getContext().authentication = previous\n        var latest: Authentication = OneTimeTokenAuthentication(\"bob\",\n            AuthorityUtils.createAuthorityList(FactorGrantedAuthority.OTT_AUTHORITY))\n        val authenticationManager: AuthenticationManager = Mockito.mock(AuthenticationManager::class.java)\n        BDDMockito.given(authenticationManager.authenticate(ArgumentMatchers.any())).willReturn(latest)\n        val authenticationRequest: Authentication = TestingAuthenticationToken(\"user\", \"pass\")\n        // tag::springSecurity[]\n        var latestResult: Authentication = authenticationManager.authenticate(authenticationRequest)\n        val previousResult = SecurityContextHolder.getContext().authentication;\n        if (previousResult?.isAuthenticated == true) {\n            latestResult = latestResult.toBuilder().authorities { a ->\n                a.addAll(previousResult.authorities)\n            }.build()\n        }\n        // end::springSecurity[]\n        SecurityAssertions.assertThat(latestResult).hasAuthorities(\n            FactorGrantedAuthority.PASSWORD_AUTHORITY,\n            FactorGrantedAuthority.OTT_AUTHORITY\n        )\n        SecurityContextHolder.clearContext()\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/servletx509config/CustomX509Configuration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.servlet509config\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.DefaultSecurityFilterChain\nimport org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Demonstrates custom configuration for x509 reactive configuration.\n *\n * @author Rob Winch\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass CustomX509Configuration {\n    // tag::springSecurity[]\n    @Bean\n    fun springSecurity(http: HttpSecurity): DefaultSecurityFilterChain? {\n        val principalExtractor = SubjectX500PrincipalExtractor()\n        principalExtractor.setExtractPrincipalNameFromEmail(true)\n\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            x509 {\n                x509PrincipalExtractor = principalExtractor\n            }\n        }\n        return http.build()\n    }\n    // end::springSecurity[]\n\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        // @formatter:off\n        val user = User\n            .withUsername(\"luke@monkeymachine\")\n            .password(\"password\")\n            .roles(\"USER\")\n            .build()\n        // @formatter:on\n        return InMemoryUserDetailsManager(user)\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/servletx509config/DefaultX509Configuration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.servlet509config\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.DefaultSecurityFilterChain\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Demonstrates custom configuration for x509 reactive configuration.\n *\n * @author Rob Winch\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass DefaultX509Configuration {\n    // tag::springSecurity[]\n    @Bean\n    fun springSecurity(http: HttpSecurity): DefaultSecurityFilterChain? {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n            x509 { }\n        }\n        // @formatter:on\n        return http.build()\n    }\n    // end::springSecurity[]\n\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        // @formatter:off\n        val user = User\n            .withUsername(\"rod\")\n            .password(\"password\")\n            .roles(\"USER\")\n            .build()\n        // @formatter:on\n        return InMemoryUserDetailsManager(user)\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/servletx509config/X509ConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.servlet509config\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated\nimport org.springframework.security.web.authentication.preauth.x509.X509TestUtils\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass X509ConfigurationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @Throws(Exception::class)\n    fun x509WhenDefaultX509Configuration() {\n        this.spring.register(DefaultX509Configuration::class.java, Http200Controller::class.java).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(get(\"/\").with(SecurityMockMvcRequestPostProcessors.x509(\"rod.cer\")))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withUsername(\"rod\"))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun x509WhenCustomX509Configuration() {\n        this.spring.register(CustomX509Configuration::class.java, Http200Controller::class.java).autowire()\n        val certificate = X509TestUtils.buildTestCertificate()\n        // @formatter:off\n        this.mockMvc!!.perform(get(\"/\").with(SecurityMockMvcRequestPostProcessors.x509(certificate)))\n        .andExpect(status().isOk())\n        .andExpect(authenticated().withUsername(\"luke@monkeymachine\"))\n    \t\t// @formatter:on\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/tokenbasedremembermeservices/CustomAlgorithmRememberMeServicesConfiguration.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.authentication.tokenbasedremembermeservices\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.RememberMeServices\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.RememberMeTokenAlgorithm\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Demonstrates custom algorithm for remember me configuration.\n *\n * @author Ngoc Nhan\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass CustomAlgorithmRememberMeServicesConfiguration {\n\n    // tag::snippet[]\n    @Bean\n    @Throws(Exception::class)\n    fun securityFilterChain(http: HttpSecurity, rememberMeServices: RememberMeServices): SecurityFilterChain {\n        // @formatter:off\n        http\n            .authorizeHttpRequests{ it.anyRequest().authenticated() }\n            .rememberMe { it.rememberMeServices(rememberMeServices) }\n        // @formatter:on\n        return http.build()\n    }\n\n    @Bean\n    fun rememberMeServices(userDetailsService: UserDetailsService): RememberMeServices {\n        val encodingAlgorithm = RememberMeTokenAlgorithm.SHA256\n        val rememberMe = TokenBasedRememberMeServices(\"myKey\", userDetailsService, encodingAlgorithm)\n        rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5)\n        return rememberMe\n    }\n    // end::snippet[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/tokenbasedremembermeservices/DefaultAlgorithmRememberMeServicesConfiguration.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.authentication.tokenbasedremembermeservices\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authentication.AuthenticationManager\nimport org.springframework.security.authentication.RememberMeAuthenticationProvider\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.web.authentication.RememberMeServices\nimport org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Demonstrates default algorithm for remember me configuration.\n *\n * @author Ngoc Nhan\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass DefaultAlgorithmRememberMeServicesConfiguration {\n\n    // tag::snippet[]\n    @Bean\n    fun rememberMeServices(userDetailsService: UserDetailsService): RememberMeServices {\n        return TokenBasedRememberMeServices(\"myKey\", userDetailsService)\n    }\n\n    @Bean\n    fun rememberMeFilter(authenticationManager: AuthenticationManager, rememberMeServices: TokenBasedRememberMeServices): RememberMeAuthenticationFilter {\n        return RememberMeAuthenticationFilter(authenticationManager, rememberMeServices)\n    }\n\n    @Bean\n    fun rememberMeAuthenticationProvider(): RememberMeAuthenticationProvider {\n        return RememberMeAuthenticationProvider(\"myKey\")\n    }\n    // end::snippet[]\n\n}"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/validduration/ValidDurationConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authentication.validduration\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.AuthorizationManagerFactories\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler\nimport org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler\nimport java.time.Duration\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class ValidDurationConfiguration {\n    // tag::httpSecurity[]\n    @Bean\n    @Throws(Exception::class)\n    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain? {\n        // @formatter:off\n        // <1>\n        val passwordIn30m = AuthorizationManagerFactories.multiFactor<Any>()\n            .requireFactor( { factor -> factor\n                .passwordAuthority()\n                .validDuration(Duration.ofMinutes(30))\n            })\n            .build()\n        // <2>\n        val passwordInHour = AuthorizationManagerFactories.multiFactor<Any>()\n            .requireFactor( { factor -> factor\n                .passwordAuthority()\n                .validDuration(Duration.ofHours(1))\n            })\n            .build()\n        http {\n            authorizeHttpRequests {\n                // <3>\n                authorize(\"/admin/**\", passwordIn30m.hasRole(\"ADMIN\"))\n                // <4>\n                authorize(\"/user/settings/**\", passwordInHour.authenticated())\n                // <5>\n                authorize(anyRequest, authenticated)\n            }\n            // <6>\n            formLogin { }\n        }\n        // @formatter:on\n        return http.build()\n    }\n\n    // end::httpSecurity[]\n    @Suppress(\"DEPRECATION\")\n    @Bean\n    fun userDetailsService(): UserDetailsService {\n        return InMemoryUserDetailsManager(\n            User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .authorities(\"app\")\n                .build()\n        )\n    }\n\n    @Bean\n    fun tokenGenerationSuccessHandler(): OneTimeTokenGenerationSuccessHandler {\n        return RedirectOneTimeTokenGenerationSuccessHandler(\"/ott/sent\")\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authentication/validduration/ValidDurationConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authentication.validduration\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.core.authority.FactorGrantedAuthority\nimport org.springframework.security.core.authority.SimpleGrantedAuthority\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors\nimport org.springframework.test.context.TestExecutionListeners\nimport org.springframework.test.context.junit.jupiter.SpringExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.request.RequestPostProcessor\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.RestController\nimport java.time.Duration\nimport java.time.Instant\nimport java.util.*\n\n/**\n * Tests [CustomX509Configuration].\n *\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension::class, SpringTestContextExtension::class)\n@TestExecutionListeners(WithSecurityContextTestExecutionListener::class)\nclass ValidDurationConfigurationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this)\n\n    @Autowired\n    var mockMvc: MockMvc? = null\n\n    @Test\n    @Throws(Exception::class)\n    fun adminWhenExpiredThenRequired() {\n        this.spring.register(\n            ValidDurationConfiguration::class.java, Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/admin/\").with(admin(Duration.ofMinutes(31))))\n            .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n            .andExpect(MockMvcResultMatchers.redirectedUrlPattern(\"/login?*\"))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun adminWhenNotExpiredThenOk() {\n        this.spring.register(\n            ValidDurationConfiguration::class.java, Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/admin/\").with(admin(Duration.ofMinutes(29))))\n            .andExpect(MockMvcResultMatchers.status().isOk())\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun settingsWhenExpiredThenRequired() {\n        this.spring.register(\n            ValidDurationConfiguration::class.java, Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/user/settings\").with(user(Duration.ofMinutes(61))))\n            .andExpect(MockMvcResultMatchers.status().is3xxRedirection())\n            .andExpect(MockMvcResultMatchers.redirectedUrlPattern(\"/login?*\"))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun settingsWhenNotExpiredThenOk() {\n        this.spring.register(\n            ValidDurationConfiguration::class.java, ValidDurationConfigurationTests.Http200Controller::class.java\n        ).autowire()\n        // @formatter:off\n        this.mockMvc!!.perform(MockMvcRequestBuilders.get(\"/user/settings\").with(user(Duration.ofMinutes(59))))\n            .andExpect(MockMvcResultMatchers.status().isOk())\n        // @formatter:on\n    }\n\n    private fun admin(sinceAuthn: Duration): RequestPostProcessor {\n        return authn(\"admin\", sinceAuthn)\n    }\n\n    private fun user(sinceAuthn: Duration): RequestPostProcessor {\n        return authn(\"user\", sinceAuthn)\n    }\n\n    private fun authn(username: String, sinceAuthn: Duration): RequestPostProcessor {\n        val issuedAt = Instant.now().minus(sinceAuthn)\n        val factor = FactorGrantedAuthority\n            .withAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY)\n            .issuedAt(issuedAt)\n            .build()\n        val role = username.uppercase(Locale.getDefault())\n        val authn = TestingAuthenticationToken(\n            username, \"\",\n            factor, SimpleGrantedAuthority(\"ROLE_\" + role)\n        )\n        return SecurityMockMvcRequestPostProcessors.authentication(authn)\n    }\n\n    @RestController\n    internal class Http200Controller {\n        @GetMapping(\"/**\")\n        fun ok(): String {\n            return \"ok\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authorization/authzauthorizationmanagerfactory/AuthorizationManagerFactoryConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authorization.authzauthorizationmanagerfactory\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory\n\n/**\n * Documentation for [org.springframework.security.authorization.AuthorizationManagerFactory].\n *\n * @author Steve Riesenberg\n */\n@Configuration(proxyBeanMethods = false)\nclass AuthorizationManagerFactoryConfiguration {\n    // tag::config[]\n    @Bean\n    fun <T> authorizationManagerFactory(): AuthorizationManagerFactory<T> {\n        val authorizationManagerFactory = DefaultAuthorizationManagerFactory<T>()\n        authorizationManagerFactory.setTrustResolver(getAuthenticationTrustResolver())\n        authorizationManagerFactory.setRoleHierarchy(getRoleHierarchy())\n        authorizationManagerFactory.setRolePrefix(\"role_\")\n\n        return authorizationManagerFactory\n    }\n    // end::config[]\n\n    private fun getAuthenticationTrustResolver(): AuthenticationTrustResolverImpl {\n        val authenticationTrustResolver = AuthenticationTrustResolverImpl()\n        authenticationTrustResolver.setAnonymousClass(Anonymous::class.java)\n        authenticationTrustResolver.setRememberMeClass(RememberMe::class.java)\n\n        return authenticationTrustResolver\n    }\n\n    private fun getRoleHierarchy(): RoleHierarchyImpl {\n        return RoleHierarchyImpl.fromHierarchy(\"role_admin > role_user\")\n    }\n\n    internal class Anonymous(principal: String) :\n        TestingAuthenticationToken(principal, \"\", \"role_anonymous\")\n\n    internal class RememberMe(principal: String) :\n        TestingAuthenticationToken(principal, \"\", \"role_rememberMe\")\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authorization/authzauthorizationmanagerfactory/AuthorizationManagerFactoryConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authorization.authzauthorizationmanagerfactory\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.access.prepost.PreAuthorize\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.kt.docs.servlet.authorization.authzauthorizationmanagerfactory.AuthorizationManagerFactoryConfiguration.Anonymous\nimport org.springframework.security.kt.docs.servlet.authorization.authzauthorizationmanagerfactory.AuthorizationManagerFactoryConfiguration.RememberMe\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.ResponseStatus\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [AuthorizationManagerFactoryConfiguration].\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass AuthorizationManagerFactoryConfigurationTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    @Throws(Exception::class)\n    fun getAnonymousWhenCustomAnonymousClassThenOk() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val authentication = Anonymous(\"anonymous\")\n        // @formatter:off\n        mockMvc.perform(get(\"/anonymous\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getAnonymousWhenAuthenticatedThenForbidden() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"role_user\")\n        // @formatter:off\n        mockMvc.perform(get(\"/anonymous\").with(authentication(authentication)))\n            .andExpect(status().isForbidden())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getRememberMeWhenCustomRememberMeClassThenOk() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val authentication = RememberMe(\"rememberMe\")\n        // @formatter:off\n        mockMvc.perform(get(\"/rememberMe\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getRememberMeWhenAuthenticatedThenForbidden() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val user = TestingAuthenticationToken(\"user\", \"\", \"role_user\")\n        // @formatter:off\n        mockMvc.perform(get(\"/rememberMe\").with(authentication(user)))\n            .andExpect(status().isForbidden())\n            .andExpect(authenticated().withAuthentication(user))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getUserWhenCustomUserRoleThenOk() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"role_user\")\n        // @formatter:off\n        mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getUserWhenCustomAdminRoleThenOk() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val admin = TestingAuthenticationToken(\"admin\", \"\", \"role_admin\")\n        // @formatter:off\n        mockMvc.perform(get(\"/user\").with(authentication(admin)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(admin))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getPreAuthorizeWhenCustomUserRoleThenOk() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"role_user\")\n        // @formatter:off\n        mockMvc.perform(get(\"/preAuthorize\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getPreAuthorizeWhenCustomAdminRoleThenOk() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val authentication = TestingAuthenticationToken(\"admin\", \"\", \"role_admin\")\n        // @formatter:off\n        mockMvc.perform(get(\"/preAuthorize\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getPreAuthorizeWhenOtherRoleThenForbidden() {\n        this.spring.register(AuthorizationManagerFactoryConfiguration::class.java, SecurityConfiguration::class.java)\n            .autowire()\n        val authentication = TestingAuthenticationToken(\"other\", \"\", \"role_other\")\n        // @formatter:off\n        mockMvc.perform(get(\"/preAuthorize\").with(authentication(authentication)))\n            .andExpect(status().isForbidden())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @EnableWebMvc\n    @EnableWebSecurity\n    @EnableMethodSecurity\n    @Configuration\n    internal open class SecurityConfiguration {\n        @Bean\n        @Throws(Exception::class)\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            // @formatter:off\n            http.authorizeHttpRequests { authorize ->\n                authorize\n                    .requestMatchers(\"/anonymous\").anonymous()\n                    .requestMatchers(\"/rememberMe\").rememberMe()\n                    .requestMatchers(\"/user\").hasRole(\"user\")\n                    .requestMatchers(\"/preAuthorize\").permitAll()\n                    .anyRequest().denyAll()\n            }\n            // @formatter:on\n            return http.build()\n        }\n\n        @Bean\n        open fun testController(testService: TestService): TestController {\n            return TestController(testService())\n        }\n\n        @Bean\n        open fun testService(): TestService {\n            return TestServiceImpl()\n        }\n    }\n\n    @RestController\n    internal open class TestController(private val testService: TestService) {\n        @GetMapping(value = [\"/anonymous\", \"/rememberMe\", \"/user\"])\n        @ResponseStatus(HttpStatus.OK)\n        fun httpRequest() {\n        }\n\n        @GetMapping(\"/preAuthorize\")\n        @ResponseStatus(HttpStatus.OK)\n        fun preAuthorize() {\n            testService.preAuthorize()\n        }\n    }\n\n    internal interface TestService {\n        @PreAuthorize(\"hasRole('user')\")\n        fun preAuthorize()\n    }\n\n    internal open class TestServiceImpl : TestService {\n        override fun preAuthorize() {\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authorization/authzconditionalauthorizationmanager/ConditionalAuthorizationManagerExample.kt",
    "content": "package org.springframework.security.kt.docs.servlet.authorization.authzconditionalauthorizationmanager;\n\nimport org.springframework.security.authorization.AllRequiredFactorsAuthorizationManager\nimport org.springframework.security.authorization.ConditionalAuthorizationManager\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext\nimport java.util.function.Predicate\n\nclass ConditionalAuthorizationManagerExample {\n    fun configure(mfaRepository: MfaRepository) {\n        // tag::conditionalAuthorizationManager[]\n        val whenUserHasMfa = Predicate { auth: Authentication -> mfaRepository.hasRegisteredMfa(auth.name) }\n        val mfaRequired = AllRequiredFactorsAuthorizationManager.builder<RequestAuthorizationContext>()\n            .requireFactor { f -> f.passwordAuthority() }\n            .requireFactor { f -> f.webauthnAuthority() }\n            .build()\n        val manager = ConditionalAuthorizationManager.`when`<RequestAuthorizationContext>(whenUserHasMfa)\n            .whenTrue(mfaRequired)\n            .build()\n        // end::conditionalAuthorizationManager[]\n    }\n\n    interface MfaRepository {\n        fun hasRegisteredMfa(username: String?): Boolean\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authorization/customizingauthorizationmanagers/CustomHttpRequestsAuthorizationManagerFactory.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authorization.customizingauthorizationmanagers\n\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.authorization.AuthorizationManagers\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext\nimport org.springframework.stereotype.Component\n\n/**\n * Documentation for {@link AuthorizationManagerFactory}.\n *\n * @author Steve Riesenberg\n */\n// tag::class[]\n@Component\nclass CustomHttpRequestsAuthorizationManagerFactory : AuthorizationManagerFactory<RequestAuthorizationContext> {\n    private val delegate = DefaultAuthorizationManagerFactory<RequestAuthorizationContext>()\n\n    override fun authenticated(): AuthorizationManager<RequestAuthorizationContext> {\n        return AuthorizationManagers.allOf(\n            delegate.authenticated(),\n            delegate.hasRole(\"USER\")\n        )\n    }\n}\n// end::class[]\n\n\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authorization/customizingauthorizationmanagers/CustomHttpRequestsAuthorizationManagerFactoryTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authorization.customizingauthorizationmanagers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.ResponseStatus\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [CustomHttpRequestsAuthorizationManagerFactory].\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass CustomHttpRequestsAuthorizationManagerFactoryTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    @Throws(Exception::class)\n    fun getHelloWhenAnonymousThenForbidden() {\n        spring.register(SecurityConfiguration::class.java, TestController::class.java).autowire()\n        // @formatter:off\n        mockMvc.perform(get(\"/hello\").with(anonymous()))\n            .andExpect(status().isForbidden())\n            .andExpect(SecurityMockMvcResultMatchers.unauthenticated())\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getHelloWhenAuthenticatedWithUserRoleThenOk() {\n        spring.register(SecurityConfiguration::class.java, TestController::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"ROLE_USER\")\n        // @formatter:off\n        mockMvc.perform(get(\"/hello\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getHelloWhenAuthenticatedWithOtherRoleThenForbidden() {\n        spring.register(SecurityConfiguration::class.java, TestController::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"ROLE_OTHER\")\n        // @formatter:off\n        mockMvc.perform(get(\"/hello\").with(authentication(authentication)))\n            .andExpect(status().isForbidden())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getHelloWhenAuthenticatedWithNoRolesThenForbidden() {\n        spring.register(SecurityConfiguration::class.java, TestController::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", listOf())\n        // @formatter:off\n        mockMvc.perform(get(\"/hello\").with(authentication(authentication)))\n            .andExpect(status().isForbidden())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @EnableWebMvc\n    @EnableWebSecurity\n    @Configuration\n    internal open class SecurityConfiguration {\n        @Bean\n        @Throws(Exception::class)\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            // @formatter:off\n            http\n                .authorizeHttpRequests { authorize ->\n                    authorize.anyRequest().authenticated()\n                }\n            // @formatter:on\n            return http.build()\n        }\n\n        @Bean\n        open fun customHttpRequestsAuthorizationManagerFactory(): CustomHttpRequestsAuthorizationManagerFactory {\n            return CustomHttpRequestsAuthorizationManagerFactory()\n        }\n    }\n\n    @RestController\n    internal class TestController {\n        @GetMapping(\"/**\")\n        @ResponseStatus(HttpStatus.OK)\n        fun ok() {\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authorization/customizingauthorizationmanagers/CustomMethodInvocationAuthorizationManagerFactory.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authorization.customizingauthorizationmanagers\n\nimport org.aopalliance.intercept.MethodInvocation\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.authorization.AuthorizationManagers\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory\nimport org.springframework.stereotype.Component\n\n/**\n * Documentation for [AuthorizationManagerFactory].\n *\n * @author Steve Riesenberg\n */\n// tag::class[]\n@Component\nclass CustomMethodInvocationAuthorizationManagerFactory : AuthorizationManagerFactory<MethodInvocation> {\n    private val delegate = DefaultAuthorizationManagerFactory<MethodInvocation>()\n\n    override fun hasRole(role: String): AuthorizationManager<MethodInvocation> {\n        return AuthorizationManagers.anyOf(\n            delegate.hasRole(role),\n            delegate.hasRole(\"ADMIN\")\n        )\n    }\n\n    override fun hasAnyRole(vararg roles: String): AuthorizationManager<MethodInvocation> {\n        return AuthorizationManagers.anyOf(\n            delegate.hasAnyRole(*roles),\n            delegate.hasRole(\"ADMIN\")\n        )\n    }\n}\n// end::class[]\n\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/authorization/customizingauthorizationmanagers/CustomMethodInvocationAuthorizationManagerFactoryTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.authorization.customizingauthorizationmanagers\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.HttpStatus\nimport org.springframework.security.access.prepost.PreAuthorize\nimport org.springframework.security.authentication.TestingAuthenticationToken\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated\nimport org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers.status\nimport org.springframework.web.bind.annotation.GetMapping\nimport org.springframework.web.bind.annotation.ResponseStatus\nimport org.springframework.web.bind.annotation.RestController\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n * Tests for [CustomMethodInvocationAuthorizationManagerFactory].\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass CustomMethodInvocationAuthorizationManagerFactoryTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    @Throws(Exception::class)\n    fun getUserWhenAnonymousThenForbidden() {\n        spring.register(SecurityConfiguration::class.java).autowire()\n        // @formatter:off\n        mockMvc.perform(get(\"/user\").with(anonymous()))\n            .andExpect(status().isForbidden())\n            .andExpect(unauthenticated())\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getUserWhenAuthenticatedWithNoRolesThenForbidden() {\n        spring.register(SecurityConfiguration::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", listOf())\n        // @formatter:off\n        mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n            .andExpect(status().isForbidden())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getUserWhenAuthenticatedWithUserRoleThenOk() {\n        spring.register(SecurityConfiguration::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"ROLE_USER\")\n        // @formatter:off\n        mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getUserWhenAuthenticatedWithAdminRoleThenOk() {\n        spring.register(SecurityConfiguration::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"ROLE_ADMIN\")\n        // @formatter:off\n        mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getUserWhenAuthenticatedWithOtherRoleThenForbidden() {\n        spring.register(SecurityConfiguration::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"ROLE_OTHER\")\n        // @formatter:off\n        mockMvc.perform(get(\"/user\").with(authentication(authentication)))\n            .andExpect(status().isForbidden())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getRolesWhenAuthenticatedWithRole1RoleThenOk() {\n        spring.register(SecurityConfiguration::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"ROLE_ROLE1\")\n        // @formatter:off\n        mockMvc.perform(get(\"/roles\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getRolesWhenAuthenticatedWithAdminRoleThenOk() {\n        spring.register(SecurityConfiguration::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"ROLE_ADMIN\")\n        // @formatter:off\n        mockMvc.perform(get(\"/roles\").with(authentication(authentication)))\n            .andExpect(status().isOk())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @Test\n    @Throws(Exception::class)\n    fun getRolesWhenAuthenticatedWithOtherRoleThenForbidden() {\n        spring.register(SecurityConfiguration::class.java).autowire()\n        val authentication = TestingAuthenticationToken(\"user\", \"\", \"ROLE_OTHER\")\n        // @formatter:off\n        mockMvc.perform(get(\"/roles\").with(authentication(authentication)))\n            .andExpect(status().isForbidden())\n            .andExpect(authenticated().withAuthentication(authentication))\n        // @formatter:on\n    }\n\n    @EnableWebMvc\n    @EnableWebSecurity\n    @EnableMethodSecurity\n    @Configuration\n    internal open class SecurityConfiguration {\n        @Bean\n        @Throws(Exception::class)\n        open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {\n            // @formatter:off\n            http\n                .authorizeHttpRequests { authorize ->\n                    authorize.anyRequest().authenticated()\n                }\n            // @formatter:on\n            return http.build()\n        }\n\n        @Bean\n        open fun customMethodInvocationAuthorizationManagerFactory(): CustomMethodInvocationAuthorizationManagerFactory {\n            return CustomMethodInvocationAuthorizationManagerFactory()\n        }\n\n        @Bean\n        open fun testController(testService: TestService): TestController {\n            return TestController(testService())\n        }\n\n        @Bean\n        open fun testService(): TestService {\n            return TestServiceImpl()\n        }\n    }\n\n    @RestController\n    internal open class TestController(private val testService: TestService) {\n        @GetMapping(\"/user\")\n        @ResponseStatus(HttpStatus.OK)\n        fun user() {\n            testService.user()\n        }\n\n        @GetMapping(\"/roles\")\n        @ResponseStatus(HttpStatus.OK)\n        fun roles() {\n            testService.roles()\n        }\n    }\n\n    internal interface TestService {\n        @PreAuthorize(\"hasRole('USER')\")\n        fun user()\n\n        @PreAuthorize(\"hasAnyRole('ROLE1', 'ROLE2')\")\n        fun roles()\n    }\n\n    internal open class TestServiceImpl : TestService {\n        override fun user() {\n        }\n\n        override fun roles() {\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/customizerbeanordering/CustomizerBeanOrderingConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.configuration.customizerbeanordering\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.Ordered\nimport org.springframework.core.annotation.Order\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.ThrowingCustomizer\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n *\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class CustomizerBeanOrderingConfiguration {\n    // tag::sample[]\n    @Bean // <4>\n    fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n        // @formatter:off\n        http\n            .authorizeHttpRequests({ requests -> requests\n                .anyRequest().authenticated()\n            })\n        return http.build()\n        // @formatter:on\n    }\n\n    @Bean\n    @Order(Ordered.LOWEST_PRECEDENCE)  // <2>\n    fun userAuthorization(): ThrowingCustomizer<HttpSecurity> {\n        // @formatter:off\n        return ThrowingCustomizer { http -> http\n            .authorizeHttpRequests { requests -> requests\n                .requestMatchers(\"/users/**\").hasRole(\"USER\")\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    @Order(Ordered.HIGHEST_PRECEDENCE) // <1>\n    fun adminAuthorization(): ThrowingCustomizer<HttpSecurity> {\n        // @formatter:off\n        return ThrowingCustomizer { http -> http\n            .authorizeHttpRequests { requests -> requests\n                .requestMatchers(\"/admins/**\").hasRole(\"ADMIN\")\n            }\n        }\n        // @formatter:on\n    }\n\n    // <3>\n\n    @Bean\n    fun contentSecurityPolicy(): Customizer<HeadersConfigurer<HttpSecurity>> {\n        // @formatter:off\n        return Customizer { headers -> headers\n            .contentSecurityPolicy { csp -> csp\n                .policyDirectives(\"object-src 'none'\")\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    fun contentTypeOptions(): Customizer<HeadersConfigurer<HttpSecurity>> {\n        // @formatter:off\n        return Customizer { headers -> headers\n            .contentTypeOptions(Customizer.withDefaults())\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    fun httpsRedirect(): Customizer<HttpsRedirectConfigurer<HttpSecurity>> {\n        // @formatter:off\n        return Customizer.withDefaults<HttpsRedirectConfigurer<HttpSecurity>>()\n        // @formatter:on\n    }\n    // end::sample[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/customizerbeanordering/CustomizerBeanOrderingTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.configuration.customizerbeanordering\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass CustomizerBeanOrderingTests {\n    @JvmField\n    val spring = SpringTestContext(this).mockMvcAfterSpringSecurityOk()\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun authorizationOrdered() {\n        this.spring.register(CustomizerBeanOrderingConfiguration::class.java).autowire()\n        // @formatter:off\n        this.mockMvc.get(\"https://localhost/admins/1\") {\n            with(user(\"admin\").roles(\"ADMIN\"))\n        }.andExpect {\n            status { isOk() }\n        }\n        this.mockMvc.get(\"https://localhost/admins/1\") {\n            with(user(\"user\").roles(\"USER\"))\n        }.andExpect {\n            status { isForbidden() }\n        }\n        this.mockMvc.get(\"https://localhost/users/1\") {\n            with(user(\"user\").roles(\"USER\"))\n        }.andExpect {\n            status { isOk() }\n        }\n        this.mockMvc.get(\"https://localhost/users/1\") {\n            with(user(\"noUserRole\").roles(\"OTHER\"))\n        }.andExpect {\n            status { isForbidden() }\n        }\n        this.mockMvc.get(\"https://localhost/other\") {\n            with(user(\"authenticated\").roles(\"OTHER\"))\n        }.andExpect {\n            status { isOk() }\n        }\n        // @formatter:on\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/dslbeanordering/DslBeanOrderingConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.configuration.dslbeanordering\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.core.Ordered\nimport org.springframework.core.annotation.Order\nimport org.springframework.security.config.annotation.web.HeadersDsl\nimport org.springframework.security.config.annotation.web.HttpSecurityDsl\nimport org.springframework.security.config.annotation.web.HttpsRedirectDsl\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n/**\n *\n */\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\ninternal class DslBeanOrderingConfiguration {\n    // tag::sample[]\n    // All of the Java Modular Configuration is applied first <1>\n\n    @Bean // <5>\n    fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n        }\n        return http.build()\n        // @formatter:on\n    }\n\n    @Bean\n    @Order(Ordered.LOWEST_PRECEDENCE)  // <3>\n    fun userAuthorization(): HttpSecurityDsl.() -> Unit {\n        // @formatter:off\n        return {\n            authorizeHttpRequests {\n                authorize(\"/users/**\", hasRole(\"USER\"))\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    @Order(Ordered.HIGHEST_PRECEDENCE) // <2>\n    fun adminAuthorization(): HttpSecurityDsl.() -> Unit {\n        // @formatter:off\n        return {\n            authorizeHttpRequests {\n                authorize(\"/admins/**\", hasRole(\"ADMIN\"))\n            }\n        }\n        // @formatter:on\n    }\n\n    // <4>\n\n    @Bean\n    fun contentSecurityPolicy(): HeadersDsl.() -> Unit {\n        // @formatter:off\n        return {\n            contentSecurityPolicy {\n                policyDirectives = \"object-src 'none'\"\n            }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    fun contentTypeOptions(): HeadersDsl.() -> Unit {\n        // @formatter:off\n        return {\n            contentTypeOptions { }\n        }\n        // @formatter:on\n    }\n\n    @Bean\n    fun httpsRedirect(): HttpsRedirectDsl.() -> Unit {\n        // @formatter:off\n        return { }\n        // @formatter:on\n    }\n    // end::sample[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/dslbeanordering/DslBeanOrderingTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.springframework.security.kt.docs.servlet.configuration.dslbeanordering\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.kt.docs.servlet.configuration.customizerbeanordering.CustomizerBeanOrderingConfiguration\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n/**\n *\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass DslBeanOrderingTests {\n    @JvmField\n    val spring = SpringTestContext(this).mockMvcAfterSpringSecurityOk()\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun dslOrdered() {\n        this.spring.register(DslBeanOrderingConfiguration::class.java).autowire()\n        // @formatter:off\n        this.mockMvc.get(\"https://localhost/admins/1\") {\n            with(user(\"admin\").roles(\"ADMIN\"))\n        }.andExpect {\n            status { isOk() }\n        }\n        this.mockMvc.get(\"https://localhost/admins/1\") {\n            with(user(\"user\").roles(\"USER\"))\n        }.andExpect {\n            status { isForbidden() }\n        }\n        this.mockMvc.get(\"https://localhost/users/1\") {\n            with(user(\"user\").roles(\"USER\"))\n        }.andExpect {\n            status { isOk() }\n        }\n        this.mockMvc.get(\"https://localhost/users/1\") {\n            with(user(\"noUserRole\").roles(\"OTHER\"))\n        }.andExpect {\n            status { isForbidden() }\n        }\n        this.mockMvc.get(\"https://localhost/other\") {\n            with(user(\"authenticated\").roles(\"OTHER\"))\n        }.andExpect {\n            status { isOk() }\n        }\n        // @formatter:on\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/httpsecuritycustomizerbean/HttpSecurityCustomizerBeanConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.configuration.httpsecuritycustomizerbean\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.ThrowingCustomizer\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass HttpSecurityCustomizerBeanConfiguration {\n\n    @Bean\n    fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n        }\n        return http.build()\n    }\n\n\n    // tag::httpSecurityCustomizer[]\n    @Bean\n    fun httpSecurityCustomizer(): ThrowingCustomizer<HttpSecurity> {\n        // @formatter:off\n        return ThrowingCustomizer { http -> http\n            .headers { headers -> headers\n                .contentSecurityPolicy { csp -> csp\n                    // <1>\n                    .policyDirectives(\"object-src 'none'\")\n                }\n            }\n            // <2>\n            .redirectToHttps(Customizer.withDefaults())\n        }\n        // @formatter:on\n    }\n    // end::httpSecurityCustomizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/httpsecuritycustomizerbean/HttpSecurityCustomizerBeanTests.kt",
    "content": "package org.springframework.security.kt.docs.servlet.configuration.httpsecuritycustomizerbean\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n@ExtendWith(SpringTestContextExtension::class)\nclass HttpSecurityCustomizerBeanTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `httpsecurity customizer config`() {\n        this.spring.register(HttpSecurityCustomizerBeanConfiguration::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n            .andExpect {\n                redirectedUrl(\"https://localhost/\")\n            }\n        this.mockMvc.get(\"https://localhost/\")\n            .andExpect {\n                header {\n                    string(\"Content-Security-Policy\", \"object-src 'none'\")\n                }\n            }\n    }\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/httpsecuritydslbean/HttpSecurityDslBeanConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.configuration.httpsecuritydslbean\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.HttpSecurityDsl\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass HttpSecurityDslBeanConfiguration {\n\n    @Bean\n    fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n        }\n        return http.build()\n    }\n\n    // tag::httpSecurityDslBean[]\n    @Bean\n    fun httpSecurityDslBean(): HttpSecurityDsl.() -> Unit {\n        return {\n            headers {\n                contentSecurityPolicy {\n                    // <1>\n                    policyDirectives = \"object-src 'none'\"\n                }\n            }\n            // <2>\n            redirectToHttps { }\n        }\n    }\n    // end::httpSecurityDslBean[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/httpsecuritydslbean/HttpSecurityDslBeanTests.kt",
    "content": "package org.springframework.security.kt.docs.servlet.configuration.httpsecuritydslbean\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n\n@ExtendWith(SpringTestContextExtension::class)\nclass HttpSecurityDslBeanTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `HttpSecurityDslBean`() {\n        this.spring.register(HttpSecurityDslBeanConfiguration::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n            .andExpect {\n                redirectedUrl(\"https://localhost/\")\n            }\n\n        this.mockMvc.get(\"https://localhost/\")\n            .andExpect {\n                header {\n                    string(\"Content-Security-Policy\", \"object-src 'none'\")\n                }\n            }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/toplevelcustomizerbean/TopLevelCustomizerBeanConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.configuration.toplevelcustomizerbean\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.Customizer\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.configurers.HeadersConfigurer\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\n\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass TopLevelCustomizerBeanConfiguration {\n\n    @Bean\n    fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n        // @formatter:off\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n        }\n        return http.build()\n        // @formatter:on\n    }\n\n    // tag::headersCustomizer[]\n    @Bean\n    fun headersSecurity(): Customizer<HeadersConfigurer<HttpSecurity>> {\n        // @formatter:off\n        return Customizer { headers -> headers\n            .contentSecurityPolicy { csp -> csp\n                // <1>\n                .policyDirectives(\"object-src 'none'\")\n            }\n        }\n        // @formatter:on\n    }\n    // end::headersCustomizer[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/toplevelcustomizerbean/TopLevelCustomizerBeanTests.kt",
    "content": "package org.springframework.security.kt.docs.servlet.configuration.toplevelcustomizerbean\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n@ExtendWith(SpringTestContextExtension::class)\nclass TopLevelCustomizerBeanTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `top level dsl bean`() {\n        this.spring.register(TopLevelCustomizerBeanConfiguration::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n            .andExpect {\n                header {\n                    string(\"Content-Security-Policy\", \"object-src 'none'\")\n                }\n            }\n    }\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/topleveldslbean/TopLevelDslBeanConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.configuration.topleveldslbean\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.HeadersDsl\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc\n\n\n@EnableWebMvc\n@EnableWebSecurity\n@Configuration(proxyBeanMethods = false)\nclass TopLevelDslBeanConfiguration {\n\n    @Bean\n    fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n        http {\n            authorizeHttpRequests {\n                authorize(anyRequest, authenticated)\n            }\n        }\n        return http.build()\n    }\n\n    // tag::headersSecurity[]\n    @Bean\n    fun headersSecurity(): HeadersDsl.() -> Unit {\n        return {\n            contentSecurityPolicy {\n                // <1>\n                policyDirectives = \"object-src 'none'\"\n            }\n        }\n    }\n    // end::headersSecurity[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/configuration/topleveldslbean/TopLevelDslBeanTests.kt",
    "content": "package org.springframework.security.kt.docs.servlet.configuration.topleveldslbean\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.test.web.servlet.MockMvc\nimport org.springframework.test.web.servlet.get\n\n\n@ExtendWith(SpringTestContextExtension::class)\nclass TopLevelDslBeanTests {\n    @JvmField\n    val spring = SpringTestContext(this)\n\n    @Autowired\n    lateinit var mockMvc: MockMvc\n\n    @Test\n    fun `HttpSecurityDslBean`() {\n        this.spring.register(TopLevelDslBeanConfiguration::class.java).autowire()\n\n        this.mockMvc.get(\"/\")\n            .andExpect {\n                header {\n                    string(\"Content-Security-Policy\", \"object-src 'none'\")\n                }\n            }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/customizingfilter/CustomizingFilterTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.customizingfilter\n\nimport jakarta.servlet.Filter\nimport jakarta.servlet.FilterChain\nimport jakarta.servlet.ServletRequest\nimport jakarta.servlet.ServletResponse\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.web.FilterChainProxy\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.authentication.www.BasicAuthenticationFilter\nimport org.springframework.test.web.servlet.MockMvc\n\n/**\n * Tests for customizing security filters.\n *\n */\n@ExtendWith(SpringTestContextExtension::class)\nclass CustomizingFilterTests {\n\n\t@JvmField\n\tval spring = SpringTestContext(this)\n\n\t@Autowired\n\tlateinit var mvc: MockMvc\n\n\t@Autowired\n\tlateinit var filterChainProxy: FilterChainProxy\n\n\t@Test\n\tfun `filter chain when basic default then BasicAuthenticationFilter present`() {\n\t\tspring.register(SecurityConfigBasicDefault::class.java).autowire()\n\t\tval filters = filterChainProxy.getFilters(\"/\")\n\t\tassertThat(filters).extracting(\"class\").contains(BasicAuthenticationFilter::class.java)\n\t}\n\n\t@Test\n\tfun `filter chain when custom filter then custom filter present`() {\n\t\tspring.register(SecurityConfigCustom::class.java).autowire()\n\t\tval filters = filterChainProxy.getFilters(\"/\")\n\t\tassertThat(filters).extracting(\"class\").contains(SecurityConfigCustom.MyBasicAuthenticationFilter::class.java)\n\t\tassertThat(filters).extracting(\"class\").doesNotContain(BasicAuthenticationFilter::class.java)\n\t}\n\n\t@Test\n\tfun `filter chain when incorrect then both filters present`() {\n\t\tspring.register(SecurityConfigIncorrect::class.java).autowire()\n\t\tval filters = filterChainProxy.getFilters(\"/\")\n\t\tassertThat(filters).extracting(\"class\").contains(BasicAuthenticationFilter::class.java)\n\t\tassertThat(filters).extracting(\"class\").contains(SecurityConfigIncorrect.MyBasicAuthenticationFilter::class.java)\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\topen class SecurityConfigBasicDefault {\n\n\t\t// tag::basic-default[]\n\t\t@Bean\n\t\topen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\t\t\thttp {\n\t\t\t\thttpBasic { }\n\t\t\t\t// ...\n\t\t\t}\n\t\t\treturn http.build()\n\t\t}\n\t\t// end::basic-default[]\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\topen class SecurityConfigCustom {\n\n\t\t// tag::custom-filter[]\n\t\t@Bean\n\t\topen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\t\t\tval basic = MyBasicAuthenticationFilter()\n\t\t\t// ... configure\n\n\t\t\thttp\n\t\t\t\t// ...\n\t\t\t\t.addFilterAt(basic, BasicAuthenticationFilter::class.java)\n\n\t\t\treturn http.build()\n\t\t}\n\t\t// end::custom-filter[]\n\n\t\tclass MyBasicAuthenticationFilter : Filter {\n\t\t\toverride fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {\n\t\t\t\tchain.doFilter(request, response)\n\t\t\t}\n\t\t}\n\n\t}\n\n    @Configuration @EnableWebSecurity\n    open class SecurityConfigDisable {\n\n        // tag::disable[]\n        @Bean\n        open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n            http {\n                httpBasic {\n                    disable()\n                }\n                // ...\n            }\n            return http.build()\n        }\n        // end::disable[]\n    }\n\n\t@Configuration\n\t@EnableWebSecurity\n\topen class SecurityConfigIncorrect {\n\n\t\t// tag::incorrect[]\n\t\t@Bean\n\t\topen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\t\t\tval basic = MyBasicAuthenticationFilter()\n\t\t\t// ... configure\n\n\t\t\thttp {\n\t\t\t\thttpBasic { }\n\t\t\t}\n\n\t\t\t// ... on no! BasicAuthenticationFilter is added twice!\n\t\t\thttp.addFilterAt(basic, BasicAuthenticationFilter::class.java)\n\n\t\t\treturn http.build()\n\t\t}\n\t\t// end::incorrect[]\n\n\t\tclass MyBasicAuthenticationFilter : Filter {\n\t\t\toverride fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {\n\t\t\t\tchain.doFilter(request, response)\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/integrations/customauthorization/AdvancedWebSocketSecurityConfig.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.integrations.customauthorization\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.messaging.Message\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager\n\nimport org.springframework.messaging.simp.SimpMessageType.MESSAGE\nimport org.springframework.messaging.simp.SimpMessageType.SUBSCRIBE\n\n// tag::snippet[]\n@Configuration\nopen class AdvancedWebSocketSecurityConfig {\n\n    @Bean\n    open fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {\n        messages\n            .nullDestMatcher().authenticated() // <1>\n            .simpSubscribeDestMatchers(\"/user/queue/errors\").permitAll() // <2>\n            .simpDestMatchers(\"/app/**\").hasRole(\"USER\") // <3>\n            .simpSubscribeDestMatchers(\"/user/**\", \"/topic/friends/*\").hasRole(\"USER\") // <4>\n            .simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll() // <5>\n            .anyMessage().denyAll() // <6>\n\n        return messages.build()\n    }\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/integrations/customauthorization/WebSocketSecurityConfig.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.integrations.customauthorization\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.messaging.Message\nimport org.springframework.security.authorization.AuthorityAuthorizationManager\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager\n\n// tag::snippet[]\n@Configuration\n@EnableWebSocketSecurity\nopen class WebSocketSecurityConfig {\n\n    @Bean\n    open fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {\n        return AuthorityAuthorizationManager.hasRole(\"USER\")\n    }\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/integrations/migratingspelexpressions/WebSocketSecurityConfig.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.integrations.migratingspelexpressions\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.messaging.Message\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.messaging.access.expression.MessageExpressionAuthorizationManager\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager\n\n// tag::snippet[]\n@Configuration\nopen class WebSocketSecurityConfig {\n\n    @Bean\n    open fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {\n        messages\n            // ...\n            .simpSubscribeDestMatchers(\"/topic/friends/{friend}\")\n            .access(MessageExpressionAuthorizationManager(\"#friend == 'john'\"))\n        // ...\n\n        return messages.build()\n    }\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/integrations/websocketauthorization/WebSocketSecurityConfig.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.integrations.websocketauthorization\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.messaging.Message\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.config.annotation.web.socket.EnableWebSocketSecurity\nimport org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager\n\n// tag::snippet[]\n@Configuration\n@EnableWebSocketSecurity // <1> <2>\nopen class WebSocketSecurityConfig {\n\n    @Bean\n    open fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {\n        messages.simpDestMatchers(\"/user/**\").hasRole(\"USER\") // <3>\n        return messages.build()\n    }\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/integrations/websocketsameorigindisable/WebSocketSecurityConfig.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.integrations.websocketsameorigindisable\n\nimport org.springframework.context.ApplicationContext\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.messaging.Message\nimport org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver\nimport org.springframework.messaging.simp.config.ChannelRegistration\nimport org.springframework.security.authorization.AuthorizationEventPublisher\nimport org.springframework.security.authorization.AuthorizationManager\nimport org.springframework.security.authorization.SpringAuthorizationEventPublisher\nimport org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor\nimport org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver\nimport org.springframework.security.messaging.context.SecurityContextChannelInterceptor\nimport org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer\n\n// tag::snippet[]\n@Configuration\nopen class WebSocketSecurityConfig(val applicationContext: ApplicationContext, val authorizationManager: AuthorizationManager<Message<*>>): WebSocketMessageBrokerConfigurer {\n\n    @Override\n    override fun addArgumentResolvers(argumentResolvers: MutableList<HandlerMethodArgumentResolver>) {\n        argumentResolvers.add(AuthenticationPrincipalArgumentResolver())\n    }\n\n    @Override\n    override fun configureClientInboundChannel(registration: ChannelRegistration) {\n        val authz = AuthorizationChannelInterceptor(authorizationManager)\n        val publisher: AuthorizationEventPublisher = SpringAuthorizationEventPublisher(applicationContext)\n        authz.setAuthorizationEventPublisher(publisher)\n        registration.interceptors(SecurityContextChannelInterceptor(), authz)\n    }\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/integrations/websocketsockjscsrf/WebSecurityConfig.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.integrations.websocketsockjscsrf\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\n\n// tag::snippet[]\n@Configuration\n@EnableWebSecurity\nopen class WebSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            csrf {\n                // ignore our stomp endpoints since they are protected using Stomp headers\n                ignoringRequestMatchers(\"/chat/**\")\n            }\n            headers {\n                frameOptions {\n                    // allow same origin to frame our site to support iframe SockJS\n                    sameOrigin = true\n                }\n            }\n            authorizeHttpRequests {\n                // ...\n            }\n            // ...\n        }\n        return http.build()\n    }\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/integrations/websocketsockjssameorigin/WebSecurityConfig.kt",
    "content": "/*\n * Copyright 2026-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.integrations.websocketsockjssameorigin\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\n\n// tag::snippet[]\n@Configuration\n@EnableWebSecurity\nopen class WebSecurityConfig {\n\n    @Bean\n    open fun filterChain(http: HttpSecurity): SecurityFilterChain {\n        http {\n            // ...\n            headers {\n                frameOptions {\n                    sameOrigin = true\n                }\n            }\n        }\n        return http.build()\n    }\n\n}\n// end::snippet[]"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/customuserdetailsservice/UserDetailsJwtPrincipalConverter.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.oauth2.resourceserver.customuserdetailsservice\n\nimport org.springframework.core.convert.converter.Converter\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetails\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal\nimport org.springframework.security.oauth2.jwt.Jwt\nimport org.springframework.stereotype.Component\n\n// tag::custom-converter[]\n@Component\nclass UserDetailsJwtPrincipalConverter(private val users: UserDetailsService) : Converter<Jwt, OAuth2AuthenticatedPrincipal> {\n\n\toverride fun convert(jwt: Jwt): OAuth2AuthenticatedPrincipal {\n\t\tval subject = jwt.subject ?: throw IllegalArgumentException(\"JWT subject is required\")\n\t\tval user = users.loadUserByUsername(subject)\n\t\treturn JwtUser(jwt, user)\n\t}\n\n\tprivate class JwtUser(private val jwt: Jwt, user: UserDetails) :\n\t\tUser(user.username, user.password, user.isEnabled, user.isAccountNonExpired, user.isCredentialsNonExpired, user.isAccountNonLocked, user.authorities),\n\t\tOAuth2AuthenticatedPrincipal {\n\n\t\toverride fun getName(): String = jwt.subject ?: \"\"\n\n\t\toverride fun getAttributes(): Map<String, Any> = jwt.claims\n\n\t}\n\n}\n// end::custom-converter[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/customuserdetailsservice/UserDetailsJwtPrincipalConverterConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.oauth2.resourceserver.customuserdetailsservice\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter\n\n@Configuration\nopen class UserDetailsJwtPrincipalConverterConfiguration {\n\n\t// tag::configure-converter[]\n\t@Bean\n\topen fun authenticationConverter(principalConverter: UserDetailsJwtPrincipalConverter): JwtAuthenticationConverter {\n\t\tval converter = JwtAuthenticationConverter()\n\t\tconverter.setJwtPrincipalConverter(principalConverter)\n\t\treturn converter\n\t}\n\t// end::configure-converter[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/customuserdetailsservice/UserDetailsJwtPrincipalConverterTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.oauth2.resourceserver.customuserdetailsservice\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal\nimport org.springframework.security.oauth2.jwt.TestJwts\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter\n\nclass UserDetailsJwtPrincipalConverterTests {\n\n\t@Test\n\tfun convertWhenUserFoundThenPrincipalIsUserDetails() {\n\t\t@Suppress(\"DEPRECATION\")\n\t\tval users = { username: String ->\n\t\t\tUser.withDefaultPasswordEncoder()\n\t\t\t\t.username(username)\n\t\t\t\t.password(\"password\")\n\t\t\t\t.roles(\"USER\")\n\t\t\t\t.build()\n\t\t}\n\t\tval principalConverter = UserDetailsJwtPrincipalConverter(users)\n\t\tval converter = JwtAuthenticationConverter()\n\t\tconverter.setJwtPrincipalConverter(principalConverter)\n\t\tval jwt = TestJwts.jwt().subject(\"user\").build()\n\t\tval principal = converter.convert(jwt).principal as OAuth2AuthenticatedPrincipal\n\t\tassertThat(principal.name).isEqualTo(\"user\")\n\t\tassertThat(principal.attributes).containsKey(\"sub\")\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/jwtgrantedauthoritiesspelexpression/ExpressionJwtGrantedAuthoritiesConverterTests.kt",
    "content": "package org.springframework.security.kt.docs.servlet.oauth2.resourceserver.jwtgrantedauthoritiesspelexpression\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.assertj.core.api.Assertions.tuple\nimport org.junit.jupiter.api.Test\nimport org.springframework.expression.spel.standard.SpelExpressionParser\nimport org.springframework.security.core.GrantedAuthority\nimport org.springframework.security.oauth2.jwt.TestJwts\nimport org.springframework.security.oauth2.server.resource.authentication.ExpressionJwtGrantedAuthoritiesConverter\n\nclass ExpressionJwtGrantedAuthoritiesConverterTests {\n\t@Test\n\tfun convertWhenTokenHasCustomClaimNameExpressionThenCustomClaimNameAttributeIsTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tval jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"nested\", mapOf(\"scopes\" to listOf(\"read\", \"write\")))\n\t\t\t\t.build()\n\t\t// @formatter:on\n        // tag::spel-expression[]\n        val parser = SpelExpressionParser()\n\t\tval expression = parser.parseExpression(\"[nested][scopes]\")\n\t\tval converter = ExpressionJwtGrantedAuthoritiesConverter(expression)\n\t\tval authorities = converter.convert(jwt)\n        // end::spel-expression[]\n\t\tassertThat(authorities).extracting(GrantedAuthority::getAuthority)\n            .containsExactly(tuple(\"SCOPE_read\"), tuple(\"SCOPE_write\"))\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/methodsecurityhasscope/MessageService.kt",
    "content": "package org.springframework.security.kt.docs.servlet.oauth2.resourceserver.methodsecurityhasscope\n\nimport org.springframework.security.access.prepost.PreAuthorize\nimport org.springframework.stereotype.Service\n\n\n@Service\nopen class MessageService {\n    // tag::protected-method[]\n    @PreAuthorize(\"@oauth2.hasScope('message:read')\")\n    open fun readMessage(): String {\n        return \"message\"\n    }\n    // end::protected-method[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/methodsecurityhasscope/MethodSecurityHasScopeConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.oauth2.resourceserver.methodsecurityhasscope\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.oauth2.core.authorization.DefaultOAuth2AuthorizationManagerFactory\nimport org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagerFactory\n\n@Configuration\n@EnableMethodSecurity\nopen class MethodSecurityHasScopeConfiguration {\n    // tag::declare-factory[]\n    @Bean\n    open fun oauth2(): OAuth2AuthorizationManagerFactory<Any> {\n        return DefaultOAuth2AuthorizationManagerFactory()\n    }\n    // end::declare-factory[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/methodsecurityhasscope/MethodSecurityHasScopeConfigurationTests.kt",
    "content": "package org.springframework.security.kt.docs.servlet.oauth2.resourceserver.methodsecurityhasscope\n\nimport org.assertj.core.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.access.AccessDeniedException\nimport org.springframework.security.config.test.SpringTestContext\nimport org.springframework.security.config.test.SpringTestContextExtension\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringTestContextExtension::class)\n@ExtendWith(SpringExtension::class)\n@SecurityTestExecutionListeners\nclass MethodSecurityHasScopeConfigurationTests {\n    @JvmField\n    val spring: SpringTestContext = SpringTestContext(this).mockMvcAfterSpringSecurityOk()\n\n    @Autowired\n    var messages: MessageService? = null\n\n    @Test\n    @WithMockUser(authorities = [\"SCOPE_message:read\"])\n    fun readMessageWhenMessageReadThenAllowed() {\n        this.spring.register(MethodSecurityHasScopeConfiguration::class.java, MessageService::class.java).autowire()\n        this.messages!!.readMessage()\n    }\n\n    @Test\n    @WithMockUser\n    fun readMessageWhenNoScopeThenDenied() {\n        this.spring.register(MethodSecurityHasScopeConfiguration::class.java, MessageService::class.java).autowire()\n        Assertions.assertThatExceptionOfType<AccessDeniedException?>(AccessDeniedException::class.java)\n            .isThrownBy({ this.messages!!.readMessage() })\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"SCOPE_message:read\", \"FACTOR_BEARER\", \"FACTOR_X509\"])\n    fun mfaReadMessageWhenMessageReadAndFactorsThenAllowed() {\n        this.spring.register(MethodSecurityHasScopeMfaConfiguration::class.java, MessageService::class.java).autowire()\n        this.messages!!.readMessage()\n    }\n\n    @Test\n    @WithMockUser(authorities = [\"SCOPE_message:read\"])\n    fun mfaReadMessageWhenMessageReadThenDenied() {\n        this.spring.register(MethodSecurityHasScopeMfaConfiguration::class.java, MessageService::class.java).autowire()\n        Assertions.assertThatExceptionOfType<AccessDeniedException?>(AccessDeniedException::class.java)\n            .isThrownBy({ this.messages!!.readMessage() })\n    }\n\n    @Test\n    @WithMockUser\n    fun mfaReadMessageWhenNoScopeThenDenied() {\n        this.spring.register(MethodSecurityHasScopeMfaConfiguration::class.java, MessageService::class.java).autowire()\n        Assertions.assertThatExceptionOfType<AccessDeniedException?>(AccessDeniedException::class.java)\n            .isThrownBy({ this.messages!!.readMessage() })\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/methodsecurityhasscope/MethodSecurityHasScopeMfaConfiguration.kt",
    "content": "package org.springframework.security.kt.docs.servlet.oauth2.resourceserver.methodsecurityhasscope\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authorization.AuthorizationManagerFactory\nimport org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.oauth2.core.authorization.DefaultOAuth2AuthorizationManagerFactory\nimport org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagerFactory\n\n@Configuration\n@EnableMethodSecurity\n@EnableMultiFactorAuthentication(authorities = [\"FACTOR_BEARER\", \"FACTOR_X509\"])\nopen class MethodSecurityHasScopeMfaConfiguration {\n    // tag::declare-factory[]\n    @Bean\n    open fun oauth2(authz: AuthorizationManagerFactory<Any>): OAuth2AuthorizationManagerFactory<Any> {\n        return DefaultOAuth2AuthorizationManagerFactory(authz)\n    } // end::declare-factory[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/opaquetokentimeoutsrestclient/RestClientOpaqueTokenIntrospectorConfiguration.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.oauth2.resourceserver.opaquetokentimeoutsrestclient\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.http.client.SimpleClientHttpRequestFactory\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector\nimport org.springframework.security.oauth2.server.resource.introspection.RestClientOpaqueTokenIntrospector\nimport org.springframework.web.client.RestClient\nimport java.time.Duration\n\n@Configuration\nopen class RestClientOpaqueTokenIntrospectorConfiguration {\n\n\t// tag::restclient-simple[]\n\t@Bean\n\topen fun introspector(introspectionUri: String, clientId: String, clientSecret: String): OpaqueTokenIntrospector {\n\t\treturn RestClientOpaqueTokenIntrospector.withIntrospectionUri(introspectionUri)\n\t\t\t.clientId(clientId)\n\t\t\t.clientSecret(clientSecret)\n\t\t\t.build()\n\t}\n\t// end::restclient-simple[]\n\n\t// tag::restclient-timeouts[]\n\t@Bean\n\topen fun introspectorWithTimeouts(\n\t\tintrospectionUri: String,\n\t\tclientId: String,\n\t\tclientSecret: String\n\t): OpaqueTokenIntrospector {\n\t\tval requestFactory = SimpleClientHttpRequestFactory()\n\t\trequestFactory.setConnectTimeout(Duration.ofSeconds(60))\n\t\trequestFactory.setReadTimeout(Duration.ofSeconds(60))\n\t\tval restClient = RestClient.builder()\n\t\t\t.requestFactory(requestFactory)\n\t\t\t.defaultHeaders { headers -> headers.setBasicAuth(clientId, clientSecret) }\n\t\t\t.build()\n\t\treturn RestClientOpaqueTokenIntrospector(introspectionUri, restClient)\n\t}\n\t// end::restclient-timeouts[]\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/oauth2/resourceserver/opaquetokentimeoutsrestclient/RestClientOpaqueTokenIntrospectorConfigurationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.oauth2.resourceserver.opaquetokentimeoutsrestclient\n\nimport okhttp3.mockwebserver.Dispatcher\nimport okhttp3.mockwebserver.MockResponse\nimport okhttp3.mockwebserver.MockWebServer\nimport okhttp3.mockwebserver.RecordedRequest\nimport org.junit.jupiter.api.Test\nimport org.springframework.http.HttpHeaders\nimport org.springframework.http.MediaType\nimport org.springframework.http.client.SimpleClientHttpRequestFactory\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector\nimport org.springframework.security.oauth2.server.resource.introspection.RestClientOpaqueTokenIntrospector\nimport org.springframework.web.client.RestClient\nimport org.assertj.core.api.Assertions.assertThat\nimport java.time.Duration\nimport java.time.Instant\nimport java.util.Base64\n\n/**\n * Tests for [RestClientOpaqueTokenIntrospectorConfiguration] sample snippets.\n */\nclass RestClientOpaqueTokenIntrospectorConfigurationTests {\n\n\tcompanion object {\n\t\tprivate const val CLIENT_ID = \"client\"\n\t\tprivate const val CLIENT_SECRET = \"secret\"\n\t\tprivate const val ACTIVE_RESPONSE = \"\"\"\n\t\t\t{\n\t\t\t  \"active\": true,\n\t\t\t  \"sub\": \"Z5O3upPC88QrAjx00dis\",\n\t\t\t  \"scope\": \"read write\",\n\t\t\t  \"exp\": 1419356238,\n\t\t\t  \"iat\": 1419350238\n\t\t\t}\n\t\t\t\"\"\"\n\t}\n\n\t@Test\n\tfun introspectorWhenBuilderThenIntrospectsSuccessfully() {\n\t\tMockWebServer().use { server ->\n\t\t\tserver.dispatcher = requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE)\n\t\t\tserver.start()\n\t\t\tval introspectionUri = server.url(\"/introspect\").toString()\n\t\t\tval introspector: OpaqueTokenIntrospector = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectionUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build()\n\t\t\tval principal: OAuth2AuthenticatedPrincipal = introspector.introspect(\"token\")\n\t\t\tassertThat(principal.attributes).isNotNull\n\t\t\tassertThat(principal.attributes).containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\tassertThat(principal.attributes).containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\")\n\t\t\tassertThat(principal.attributes).containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))\n\t\t\tassertThat(principal.getAttribute<Any>(OAuth2TokenIntrospectionClaimNames.SCOPE))\n\t\t\t\t.isEqualTo(listOf(\"read\", \"write\"))\n\t\t}\n\t}\n\n\t@Test\n\tfun introspectorWithTimeoutsWhenCustomRestClientThenIntrospectsSuccessfully() {\n\t\tMockWebServer().use { server ->\n\t\t\tserver.dispatcher = requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE)\n\t\t\tserver.start()\n\t\t\tval introspectionUri = server.url(\"/introspect\").toString()\n\t\t\tval requestFactory = SimpleClientHttpRequestFactory()\n\t\t\trequestFactory.setConnectTimeout(Duration.ofSeconds(60))\n\t\t\trequestFactory.setReadTimeout(Duration.ofSeconds(60))\n\t\t\tval restClient = RestClient.builder()\n\t\t\t\t.requestFactory(requestFactory)\n\t\t\t\t.defaultHeaders { headers -> headers.setBasicAuth(CLIENT_ID, CLIENT_SECRET) }\n\t\t\t\t.build()\n\t\t\tval introspector = RestClientOpaqueTokenIntrospector(introspectionUri, restClient)\n\t\t\tval principal: OAuth2AuthenticatedPrincipal = introspector.introspect(\"token\")\n\t\t\tassertThat(principal.attributes).isNotNull\n\t\t\tassertThat(principal.attributes).containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\tassertThat(principal.attributes).containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\")\n\t\t}\n\t}\n\n\tprivate fun requiresAuth(username: String, password: String, response: String): Dispatcher {\n\t\treturn object : Dispatcher() {\n\t\t\toverride fun dispatch(request: RecordedRequest): MockResponse {\n\t\t\t\tval authorization = request.getHeader(HttpHeaders.AUTHORIZATION)\n\t\t\t\treturn if (authorization != null && isAuthorized(authorization, username, password)) {\n\t\t\t\t\tMockResponse()\n\t\t\t\t\t\t.setBody(response)\n\t\t\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t} else {\n\t\t\t\t\tMockResponse().setResponseCode(401)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate fun isAuthorized(authorization: String, username: String, password: String): Boolean {\n\t\tval decoded = String(Base64.getDecoder().decode(authorization.substring(6)))\n\t\tval values = decoded.split(\":\", limit = 2)\n\t\treturn values.size == 2 && username == values[0] && password == values[1]\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/requestcachepreventsavedrequest/SecurityConfig.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.requestcachepreventsavedrequest\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\nimport org.springframework.security.web.savedrequest.NullRequestCache\n\nopen class SecurityConfig {\n\n\t// tag::snippet[]\n\t@Bean\n\topen fun springSecurity(http: HttpSecurity): SecurityFilterChain {\n\t\tval nullRequestCache = NullRequestCache()\n\t\thttp {\n\t\t\trequestCache {\n\t\t\t\trequestCache = nullRequestCache\n\t\t\t}\n\t\t}\n\t\treturn http.build()\n\t}\n\t// end::snippet[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/servletdelegatingfilterproxy/SampleDelegatingFilterProxy.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.servletdelegatingfilterproxy;\n\nimport jakarta.servlet.Filter\nimport jakarta.servlet.FilterChain\nimport jakarta.servlet.ServletException\nimport jakarta.servlet.ServletRequest\nimport jakarta.servlet.ServletResponse\nimport org.springframework.web.context.support.StaticWebApplicationContext;\nimport org.springframework.web.filter.GenericFilterBean;\n\nimport java.io.IOException;\n\n\n/**\n * A very simple implementation of a DelegatingFilterProxy.\n */\nclass SampleDelegatingFilterProxy(\n\tprivate val someBeanName: String,\n\tprivate var wac: StaticWebApplicationContext\n) : GenericFilterBean() {\n\n\t// tag::dofilter[]\n\t@Throws(IOException::class, ServletException::class)\n\toverride fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain?) {\n\t\tval delegate: Filter = getFilterBean(someBeanName) // <1>\n\t\tdelegate.doFilter(request, response, chain) // <2>\n\t}\n\t// end::dofilter[]\n\n\tprivate fun getFilterBean(someBeanName: String): Filter {\n\t\treturn wac.getBean(someBeanName, Filter::class.java)\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/servletdelegatingfilterproxy/SampleDelegatingFilterProxyTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.servletdelegatingfilterproxy\n\nimport jakarta.servlet.Filter\nimport jakarta.servlet.FilterChain\nimport jakarta.servlet.FilterConfig\nimport jakarta.servlet.ServletContext\nimport jakarta.servlet.ServletException\nimport jakarta.servlet.ServletRequest\nimport jakarta.servlet.ServletResponse\nimport org.junit.jupiter.api.Test\nimport org.springframework.mock.web.MockHttpServletRequest\nimport org.springframework.mock.web.MockHttpServletResponse\nimport org.springframework.security.web.servlet.MockServletContext\nimport org.springframework.util.Assert\nimport org.springframework.web.context.WebApplicationContext\nimport org.springframework.web.context.support.StaticWebApplicationContext\nimport java.io.IOException\nimport org.assertj.core.api.Assertions.assertThat\nimport java.util.Enumeration\nimport java.util.Collections\nimport kotlin.collections.LinkedHashMap\n\nclass SampleDelegatingFilterProxyTests {\n\n\t@Test\n\t@Throws(ServletException::class, IOException::class)\n\tfun testFilter() {\n\t\tval sc: ServletContext = MockServletContext()\n\t\tval wac = StaticWebApplicationContext()\n\t\twac.registerSingleton(\"targetFilter\", MockFilter::class.java)\n\t\twac.setServletContext(sc)\n\t\twac.refresh()\n\t\tsc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac)\n\n\t\tval targetFilter = wac.getBean(\"targetFilter\") as MockFilter\n\t\tval proxyConfig = MockFilterConfig(sc)\n\t\tproxyConfig.addInitParameter(\"targetBeanName\", \"targetFilter\")\n\t\tval filterProxy = SampleDelegatingFilterProxy(\"targetFilter\", wac)\n\t\tfilterProxy.init(proxyConfig)\n\n\t\tval request = MockHttpServletRequest()\n\t\tval response = MockHttpServletResponse()\n\t\tfilterProxy.doFilter(request, response, null)\n\n\t\tassertThat(targetFilter.filterConfig).isNull()\n\t\tassertThat(request.getAttribute(\"called\")).isEqualTo(true)\n\n\t\tfilterProxy.destroy()\n\t\tassertThat(targetFilter.filterConfig).isNull()\n\t}\n\n\tprivate class MockFilter : Filter {\n\t\tvar filterConfig: FilterConfig? = null\n\n\t\t@Throws(ServletException::class)\n\t\toverride fun init(filterConfig: FilterConfig) {\n\t\t\tthis.filterConfig = filterConfig\n\t\t}\n\n\t\t@Throws(IOException::class, ServletException::class)\n\t\toverride fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain?) {\n\t\t\trequest.setAttribute(\"called\", true)\n\t\t}\n\n\t\toverride fun destroy() {\n\t\t\tfilterConfig = null\n\t\t}\n\t}\n\n\tprivate class MockFilterConfig(\n\t\t\tprivate val servletContext: ServletContext\n\t) : FilterConfig {\n\t\tprivate val filterName: String = \"\"\n\t\tprivate val initParameters = LinkedHashMap<String, String>()\n\n\t\toverride fun getFilterName(): String = filterName\n\n\t\toverride fun getServletContext(): ServletContext = servletContext\n\n\t\tfun addInitParameter(name: String, value: String) {\n\t\t\tAssert.notNull(name, \"Parameter name must not be null\")\n\t\t\tinitParameters[name] = value\n\t\t}\n\n\t\toverride fun getInitParameter(name: String): String? {\n\t\t\t\tAssert.notNull(name, \"Parameter name must not be null\")\n\t\treturn initParameters[name]\n        }\n\n\t\toverride fun getInitParameterNames(): Enumeration<String> =\n\t\tCollections.enumeration(initParameters.keys)\n\t}\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/servletfiltersreview/FilterChainUsage.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.servletfiltersreview\n\nimport jakarta.servlet.*\nimport java.io.IOException\n\n/**\n * Demos FilterChain Usage.\n * @author Rob Winch\n */\nclass FilterChainUsage : Filter {\n\n    // tag::dofilter[]\n    @Throws(IOException::class, ServletException::class)\n    override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain) {\n        // do something before the rest of the application\n        chain.doFilter(request, response) // invoke the rest of the application\n        // do something after the rest of the application\n    }\n    // end::dofilter[]\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/servletsecurityfilters/SecurityConfig.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.servletsecurityfilters\n\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity\nimport org.springframework.security.config.annotation.web.invoke\nimport org.springframework.security.web.SecurityFilterChain\n\n// tag::snippet[]\n@Configuration\n@EnableWebSecurity\nopen class SecurityConfig {\n\n\t@Bean\n\topen fun filterChain(http: HttpSecurity): SecurityFilterChain {\n\t\thttp {\n\t\t\tcsrf { }\n\t\t\thttpBasic { }\n\t\t\tformLogin { }\n\t\t\tauthorizeHttpRequests {\n\t\t\t\tauthorize(anyRequest, authenticated)\n\t\t\t}\n\t\t}\n\t\treturn http.build()\n\t}\n\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethod/HelloMessageService.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethod\n\nimport org.springframework.security.access.prepost.PreAuthorize\nimport org.springframework.security.config.core.MessageService\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.context.SecurityContextHolder\n\n/**\n * A message service for demonstrating test support for method-based security.\n */\n// tag::authenticated[]\nclass HelloMessageService : MessageService {\n\n\t@PreAuthorize(\"isAuthenticated()\")\n\toverride fun getMessage(): String {\n\t\tval authentication: Authentication? = SecurityContextHolder.getContext().authentication\n\t\treturn \"Hello $authentication\"\n\t}\n\n\t@PreAuthorize(\"isAuthenticated()\")\n\toverride fun getJsrMessage(): String {\n\t\tval authentication = SecurityContextHolder.getContext().authentication\n\t\treturn \"Hello JSR $authentication\"\n\t}\n}\n// end::authenticated[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethod/HelloServiceTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethod\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.core.MessageService\nimport org.springframework.security.core.authority.AuthorityUtils\nimport org.springframework.security.core.context.SecurityContextHolder\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\nclass HelloServiceTests {\n\n    @Autowired\n    lateinit var messageService: MessageService\n\n    @BeforeEach\n    fun setup() {\n        val user = UsernamePasswordAuthenticationToken.authenticated(\n            \"user\",\n            \"password\",\n            AuthorityUtils.createAuthorityList(\"ROLE_USER\")\n        )\n        SecurityContextHolder.getContext().authentication = user\n    }\n\n    @Test\n    fun helloServiceTest() {\n        assertThat(messageService.message)\n            .contains(\"user\")\n            .contains(\"ROLE_USER\")\n    }\n\n    @EnableMethodSecurity(prePostEnabled = true, jsr250Enabled = true)\n    @Configuration\n    open class Config {\n        @Bean\n        open fun messageService(): MessageService {\n            return HelloMessageService()\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodmetaannotations/WithMockAdmin.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodmetaannotations\n\nimport org.springframework.security.test.context.support.WithMockUser\n\n// tag::snippet[]\n@Retention(AnnotationRetention.RUNTIME)\n@WithMockUser(value = \"rob\", roles = [\"USER\", \"ADMIN\"])\nannotation class WithMockAdmin\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodmetaannotations/WithMockAdminTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodmetaannotations\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.core.MessageService\nimport org.springframework.security.kt.docs.servlet.test.testmethod.HelloMessageService\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\nclass WithMockAdminTests {\n\n    @Autowired\n    lateinit var messageService: MessageService\n\n    @Test\n    @WithMockAdmin\n    fun getMessageWithMockUserAdminRoles() {\n        val message = messageService.message\n        assertThat(message)\n            .contains(\"rob\")\n            .contains(\"ROLE_ADMIN\")\n            .contains(\"ROLE_USER\")\n    }\n\n    @EnableMethodSecurity\n    @Configuration\n    open class Config {\n        @Bean\n        open fun messageService(): MessageService {\n            return HelloMessageService()\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodmetaannotations/WithMockUserTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodmetaannotations\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.core.MessageService\nimport org.springframework.security.kt.docs.servlet.test.testmethod.HelloMessageService\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\nclass WithMockUserTests {\n\n    @Autowired\n    lateinit var messageService: MessageService\n\n    @Test\n    // tag::snippet[]\n    @WithMockUser(username = \"admin\", roles = [\"USER\", \"ADMIN\"])\n    // end::snippet[]\n    fun getMessageWithMockUserAdminRoles() {\n        val message = messageService.message\n        assertThat(message)\n            .contains(\"admin\")\n            .contains(\"ROLE_ADMIN\")\n            .contains(\"ROLE_USER\")\n    }\n\n    @EnableMethodSecurity\n    @Configuration\n    open class Config {\n        @Bean\n        open fun messageService(): MessageService {\n            return HelloMessageService()\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodsetup/WithMockUserSampleTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodsetup\n\nimport org.assertj.core.api.Assertions.assertThatExceptionOfType\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.core.MessageService\nimport org.springframework.security.kt.docs.servlet.test.testmethod.HelloMessageService\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\nclass WithMockUserSampleTests {\n\n\t@Autowired\n\tlateinit var messageService: MessageService\n\n\t// tag::snippet[]\n\t@Test\n\tfun getMessageUnauthenticated() {\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException::class.java)\n\t\t\t.isThrownBy { messageService.getMessage() }\n\t}\n\t// end::snippet[]\n\n\t@EnableMethodSecurity\n\t@Configuration\n\topen class Config {\n\t\t@Bean\n\t\topen fun messageService(): MessageService {\n\t\t\treturn HelloMessageService()\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodsetup/WithMockUserTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodsetup\n\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n// tag::setup[]\n@ExtendWith(SpringExtension::class) // <1>\n@ContextConfiguration // <2>\nclass WithMockUserTests {\n}\n// end::setup[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithanonymoususer/WithUserClassLevelAuthenticationTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithanonymoususer\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.security.test.context.support.WithAnonymousUser\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n\n// tag::snippet[]\n@ExtendWith(SpringExtension::class)\n@WithMockUser\nclass WithUserClassLevelAuthenticationTests {\n\n\t@Test\n\tfun withMockUser1() {\n\t}\n\n\t@Test\n\tfun withMockUser2() {\n\t}\n\n\t@Test\n\t@WithAnonymousUser\n\tfun anonymous() {\n\t\t// override default to run as anonymous user\n\t}\n\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithmockuser/WithMockUserClassTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithmockuser\n\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n// tag::snippet[]\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\n@WithMockUser(username = \"admin\", roles = [\"USER\", \"ADMIN\"])\nclass WithMockUserClassTests {\n\t// ...\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithmockuser/WithMockUserNestedTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithmockuser\n\nimport org.junit.jupiter.api.Nested\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n// tag::snippet[]\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\n@WithMockUser(username = \"admin\", roles = [\"USER\", \"ADMIN\"])\nclass WithMockUserNestedTests {\n\n\t@Nested\n\tinner class TestSuite1 {\n\t\t// ... all test methods use admin user\n\t}\n\n\t@Nested\n\tinner class TestSuite2 {\n\t\t// ... all test methods use admin user\n\t}\n\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithmockuser/WithMockUserTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithmockuser\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.core.MessageService\nimport org.springframework.security.kt.docs.servlet.test.testmethod.HelloMessageService\nimport org.springframework.security.test.context.support.WithMockUser\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\nclass WithMockUserTests {\n\n    @Autowired\n    lateinit var messageService: MessageService\n\n    // tag::mock-user[]\n    @Test\n    @WithMockUser\n    fun getMessageWithMockUser() {\n        val message = messageService.message\n        assertThat(message).contains(\"user\")\n    }\n    // end::mock-user[]\n\n    // tag::custom-user[]\n    @Test\n    @WithMockUser(\"customUser\")\n    fun getMessageWithMockUserCustomUsername() {\n        val message = messageService.message\n        assertThat(message).contains(\"customUser\")\n    }\n    // end::custom-user[]\n\n    // tag::custom-roles[]\n    @Test\n    @WithMockUser(username = \"admin\", roles = [\"USER\", \"ADMIN\"])\n    fun getMessageWithMockUserCustomRoles() {\n        val message = messageService.message\n        assertThat(message)\n            .contains(\"admin\")\n            .contains(\"ROLE_ADMIN\")\n            .contains(\"ROLE_USER\")\n    }\n    // end::custom-roles[]\n\n    // tag::custom-authorities[]\n    @Test\n    @WithMockUser(username = \"admin\", authorities = [\"ADMIN\", \"USER\"])\n    fun getMessageWithMockUserCustomAuthorities() {\n        val message = messageService.message\n        assertThat(message)\n            .contains(\"admin\")\n            .contains(\"ADMIN\")\n            .contains(\"USER\")\n            .doesNotContain(\"ROLE_\")\n    }\n    // end::custom-authorities[]\n\n    @EnableMethodSecurity\n    @Configuration\n    open class Config {\n        @Bean\n        open fun messageService(): MessageService {\n            return HelloMessageService()\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithsecuritycontext/CustomUserDetails.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithsecuritycontext\n\nimport org.springframework.security.core.GrantedAuthority\nimport org.springframework.security.core.authority.AuthorityUtils\nimport org.springframework.security.core.userdetails.UserDetails\n\nclass CustomUserDetails(\n    name: String,\n    username: String,\n    authorities: MutableCollection<GrantedAuthority> = AuthorityUtils.createAuthorityList(\"ROLE_USER\")) : UserDetails {\n\n    override fun getAuthorities(): MutableCollection<out GrantedAuthority> {\n        return authorities\n    }\n\n    override fun getPassword(): String {\n        TODO(\"Not yet implemented\")\n    }\n\n    override fun getUsername(): String {\n        return username\n    }\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithsecuritycontext/WithMockCustomUser.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithsecuritycontext\n\nimport org.springframework.security.test.context.support.WithSecurityContext\n\n// tag::snippet[]\n@Retention(AnnotationRetention.RUNTIME)\n@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory::class)\nannotation class WithMockCustomUser(val username: String = \"rob\", val name: String = \"Rob Winch\")\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithsecuritycontext/WithMockCustomUserSecurityContextFactory.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithsecuritycontext;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.context.SecurityContext\nimport org.springframework.security.core.context.SecurityContextHolder\nimport org.springframework.security.test.context.support.WithSecurityContextFactory\n\n// tag::snippet[]\nclass WithMockCustomUserSecurityContextFactory : WithSecurityContextFactory<WithMockCustomUser> {\n\toverride fun createSecurityContext(customUser: WithMockCustomUser): SecurityContext {\n\t\tval context = SecurityContextHolder.createEmptyContext()\n\t\tval principal = CustomUserDetails(customUser.name, customUser.username)\n\t\tval auth: Authentication =\n\t\t\t\tUsernamePasswordAuthenticationToken(principal, \"password\", principal.authorities)\n\t\tcontext.authentication = auth\n\t\treturn context\n\t}\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithsecuritycontext/WithUserDetailsSecurityContextFactory.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithsecuritycontext\n\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken\nimport org.springframework.security.core.Authentication\nimport org.springframework.security.core.context.SecurityContext\nimport org.springframework.security.core.context.SecurityContextHolder\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.test.context.support.WithSecurityContextFactory\nimport org.springframework.security.test.context.support.WithUserDetails\nimport org.springframework.util.Assert\n\n// tag::snippet[]\nclass WithUserDetailsSecurityContextFactory @Autowired constructor(private val userDetailsService: UserDetailsService) :\n    WithSecurityContextFactory<WithUserDetails> {\n\n    override fun createSecurityContext(withUser: WithUserDetails): SecurityContext {\n        val username: String = withUser.value\n        Assert.hasLength(username, \"value() must be non-empty String\")\n        val principal = userDetailsService.loadUserByUsername(username)\n        val authentication: Authentication =\n            UsernamePasswordAuthenticationToken(principal, principal.password, principal.authorities)\n        val context = SecurityContextHolder.createEmptyContext()\n        context.authentication = authentication\n        return context\n    }\n\n}\n// end::snippet[]\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithuserdetails/WithCustomUserDetailsTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithuserdetails\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.core.MessageService\nimport org.springframework.security.core.context.SecurityContextHolder\nimport org.springframework.security.core.userdetails.UserDetails\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.core.userdetails.UsernameNotFoundException\nimport org.springframework.security.docs.servlet.test.testmethodwithsecuritycontext.CustomUserDetails\nimport org.springframework.security.kt.docs.servlet.test.testmethod.HelloMessageService\nimport org.springframework.security.test.context.support.WithUserDetails\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\nclass WithCustomUserDetailsTests {\n\n    @Autowired\n    lateinit var messageService: MessageService\n\n    // tag::custom-user-details-service[]\n    @Test\n    @WithUserDetails(value = \"customUsername\", userDetailsServiceBeanName = \"myUserDetailsService\")\n    fun getMessageWithUserDetailsServiceBeanName() {\n        val message: String = messageService.getMessage()\n        assertThat(message).contains(\"customUsername\");\n        val principal = SecurityContextHolder.getContext().authentication!!.principal\n        assertThat(principal).isInstanceOf(CustomUserDetails::class.java)\n    }\n    // end::custom-user-details-service[]\n\n    @EnableMethodSecurity\n    @Configuration\n    open class Config {\n\n        @Bean\n        open fun myUserDetailsService(): UserDetailsService {\n            return CustomUserDetailsService()\n        }\n\n        @Bean\n        open fun messageService(): MessageService {\n            return HelloMessageService()\n        }\n    }\n\n    open class CustomUserDetailsService : UserDetailsService {\n\n        @Throws(UsernameNotFoundException::class)\n        override fun loadUserByUsername(username: String): UserDetails {\n            return CustomUserDetails(\"name\", username)\n        }\n    }\n\n}\n"
  },
  {
    "path": "docs/src/test/kotlin/org/springframework/security/kt/docs/servlet/test/testmethodwithuserdetails/WithUserDetailsTests.kt",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kt.docs.servlet.test.testmethodwithuserdetails\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity\nimport org.springframework.security.config.core.MessageService\nimport org.springframework.security.core.userdetails.User\nimport org.springframework.security.core.userdetails.UserDetailsService\nimport org.springframework.security.kt.docs.servlet.test.testmethod.HelloMessageService\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager\nimport org.springframework.security.test.context.support.WithUserDetails\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration\nclass WithUserDetailsTests {\n\n    @Autowired\n    lateinit var messageService: MessageService\n\n    // tag::user-details[]\n    @Test\n    @WithUserDetails\n    fun getMessageWithUserDetails() {\n        val message: String = messageService.message\n        assertThat(message).contains(\"user\")\n    }\n    // end::user-details[]\n\n    // tag::user-details-custom-username[]\n    @Test\n    @WithUserDetails(\"customUsername\")\n    fun getMessageWithUserDetailsCustomUsername() {\n        val message: String = messageService.message\n        assertThat(message).contains(\"customUsername\")\n    }\n    // end::user-details-custom-username[]\n\n    @EnableMethodSecurity\n    @Configuration\n    open class Config {\n\n        @Suppress(\"DEPRECATION\")\n        @Bean\n        open fun userDetailsService(): UserDetailsService {\n            val user1 = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"password\")\n                .build()\n            val customUser = User.withDefaultPasswordEncoder()\n                .username(\"customUsername\")\n                .password(\"password\")\n                .build()\n            return InMemoryUserDetailsManager(user1, customUser)\n        }\n\n        @Bean\n        open fun messageService(): MessageService {\n            return HelloMessageService()\n        }\n    }\n}\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/authentication/servletx509config/CustomX509Configuration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:p=\"http://www.springframework.org/schema/p\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <!-- tag::springSecurity[] -->\n    <http>\n        <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n        <x509 principal-extractor-ref=\"principalExtractor\"/>\n    </http>\n    <b:bean id=\"principalExtractor\"\n        class=\"org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor\"\n        p:extractPrincipalNameFromEmail=\"true\"/>\n    <!-- end::springSecurity[] -->\n\n    <user-service id=\"us\">\n        <user name=\"luke@monkeymachine\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n    </user-service>\n\n\n\n    <b:import resource=\"MiscHttpConfigTests-controllers.xml\"/>\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/authentication/servletx509config/DefaultX509Configuration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:p=\"http://www.springframework.org/schema/p\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/security\n\t\t\thttps://www.springframework.org/schema/security/spring-security.xsd\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <!-- tag::springSecurity[] -->\n    <http>\n        <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n        <x509 />\n    </http>\n    <!-- end::springSecurity[] -->\n\n    <user-service>\n        <user name=\"rod\" password=\"{noop}password\" authorities=\"ROLE_USER\"/>\n    </user-service>\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/authentication/tokenbasedremembermeservices/CustomAlgorithmRememberMeServicesConfiguration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2026-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n            http://www.springframework.org/schema/security\n            https://www.springframework.org/schema/security/spring-security.xsd\n            http://www.springframework.org/schema/beans\n            https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <b:bean id=\"userDetailsService\"\n            class=\"org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl\"/>\n\n    <!-- tag::snippet[] -->\n    <http>\n        <intercept-url pattern=\"/**\" access=\"authenticated\"/>\n        <remember-me services-ref=\"rememberMeServices\"/>\n    </http>\n\n    <b:bean id=\"rememberMeServices\"\n            class=\"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices\">\n        <b:constructor-arg value=\"myKey\"/>\n        <b:constructor-arg ref=\"userDetailsService\"/>\n        <b:constructor-arg value=\"SHA256\"\n                           type=\"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices$RememberMeTokenAlgorithm\"/>\n        <b:property name=\"matchingAlgorithm\" value=\"MD5\"/>\n    </b:bean>\n    <!-- end::snippet[] -->\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/authentication/tokenbasedremembermeservices/DefaultAlgorithmRememberMeServicesConfiguration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2026-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n            http://www.springframework.org/schema/security\n            https://www.springframework.org/schema/security/spring-security.xsd\n            http://www.springframework.org/schema/beans\n            https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <b:bean id=\"userDetailsService\"\n            class=\"org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl\"/>\n\n    <!-- tag::snippet[] -->\n    <b:bean id=\"rememberMeServices\"\n                class=\"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices\">\n        <b:constructor-arg value=\"myKey\"/>\n        <b:constructor-arg ref=\"userDetailsService\"/>\n    </b:bean>\n\n    <b:bean id=\"rememberMeFilter\"\n                class=\"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter\">\n        <b:constructor-arg ref=\"authenticationManager\"/>\n        <b:constructor-arg ref=\"rememberMeServices\"/>\n    </b:bean>\n\n    <b:bean id=\"rememberMeAuthenticationProvider\"\n                class=\"org.springframework.security.authentication.RememberMeAuthenticationProvider\">\n        <b:constructor-arg value=\"myKey\"/>\n    </b:bean>\n    <!-- end::snippet[] -->\n\n    <authentication-manager alias=\"authenticationManager\">\n        <authentication-provider ref=\"rememberMeAuthenticationProvider\"/>\n    </authentication-manager>\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/authorization/authzauthorizationmanagerfactory/AuthorizationManagerFactoryConfiguration.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xsi:schemaLocation=\"\n\t\t\thttp://www.springframework.org/schema/beans\n\t\t\thttps://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<!-- tag::config[] -->\n\t<b:bean id=\"authorizationManagerFactory\" class=\"org.springframework.security.authorization.DefaultAuthorizationManagerFactory\">\n\t\t<b:property name=\"trustResolver\" ref=\"authenticationTrustResolver\"/>\n\t\t<b:property name=\"roleHierarchy\" ref=\"roleHierarchy\"/>\n\t\t<b:property name=\"rolePrefix\" value=\"role_\"/>\n\t</b:bean>\n\t<!-- end::config[] -->\n\n\t<b:bean id=\"authenticationTrustResolver\" class=\"org.springframework.security.authentication.AuthenticationTrustResolverImpl\">\n\t\t<b:property name=\"anonymousClass\" value=\"org.springframework.security.authentication.TestingAuthenticationToken\"/>\n\t</b:bean>\n\n\t<b:bean id=\"roleHierarchy\" class=\"org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl\" factory-method=\"fromHierarchy\">\n\t\t<b:constructor-arg name=\"hierarchy\" value=\"role_admin > role_user\"/>\n\t</b:bean>\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/integrations/customauthorization/AdvancedWebSocketSecurityConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2026-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n            http://www.springframework.org/schema/security\n            https://www.springframework.org/schema/security/spring-security.xsd\n            http://www.springframework.org/schema/beans\n            https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <!-- tag::snippet[] -->\n    <websocket-message-broker use-authorization-manager=\"true\">\n        <!--1-->\n        <intercept-message type=\"CONNECT\" access=\"permitAll\" />\n        <intercept-message type=\"UNSUBSCRIBE\" access=\"permitAll\" />\n        <intercept-message type=\"DISCONNECT\" access=\"permitAll\" />\n\n        <intercept-message pattern=\"/user/queue/errors\" type=\"SUBSCRIBE\" access=\"permitAll\" /> <!--2-->\n        <intercept-message pattern=\"/app/**\" access=\"hasRole('USER')\" /> <!--3-->\n\n        <!--4-->\n        <intercept-message pattern=\"/user/**\" type=\"SUBSCRIBE\" access=\"hasRole('USER')\" />\n        <intercept-message pattern=\"/topic/friends/*\" type=\"SUBSCRIBE\" access=\"hasRole('USER')\" />\n\n        <!--5-->\n        <intercept-message type=\"MESSAGE\" access=\"denyAll\" />\n        <intercept-message type=\"SUBSCRIBE\" access=\"denyAll\" />\n\n        <intercept-message pattern=\"/**\" access=\"denyAll\" /> <!--6-->\n    </websocket-message-broker>\n    <!-- end::snippet[] -->\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/integrations/customauthorization/WebSocketSecurityConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2026-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n            http://www.springframework.org/schema/security\n            https://www.springframework.org/schema/security/spring-security.xsd\n            http://www.springframework.org/schema/beans\n            https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <!-- tag::snippet[] -->\n    <b:bean id=\"myAuthorizationManager\"\n          class=\"org.springframework.security.authorization.AuthorityAuthorizationManager\"\n          factory-method=\"hasRole\">\n        <b:constructor-arg type=\"java.lang.String\" value=\"USER\"/>\n    </b:bean>\n    <websocket-message-broker authorization-manager-ref=\"myAuthorizationManager\"/>\n    <!-- end::snippet[] -->\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/integrations/websocketauthorization/WebSocketSecurityConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2026-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n            http://www.springframework.org/schema/security\n            https://www.springframework.org/schema/security/spring-security.xsd\n            http://www.springframework.org/schema/beans\n            https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <!-- tag::snippet[] -->\n    <websocket-message-broker use-authorization-manager=\"true\"> <!--1--> <!--2-->\n        <intercept-message pattern=\"/user/**\" access=\"hasRole('USER')\"/> <!--3-->\n    </websocket-message-broker>\n    <!-- end::snippet[] -->\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/integrations/websocketsameorigindisable/WebSocketSecurityConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2026-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n            http://www.springframework.org/schema/security\n            https://www.springframework.org/schema/security/spring-security.xsd\n            http://www.springframework.org/schema/beans\n            https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <!-- tag::snippet[] -->\n    <websocket-message-broker use-authorization-manager=\"true\" same-origin-disabled=\"true\">\n        <intercept-message pattern=\"/**\" access=\"authenticated\"/>\n    </websocket-message-broker>\n    <!-- end::snippet[] -->\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/integrations/websocketsockjscsrf/WebSocketSecurityConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2026-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~       https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xmlns=\"http://www.springframework.org/schema/security\"\n         xsi:schemaLocation=\"\n            http://www.springframework.org/schema/security\n            https://www.springframework.org/schema/security/spring-security.xsd\n            http://www.springframework.org/schema/beans\n            https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n    <!-- tag::snippet[] -->\n    <http> <!-- ... -->\n        <csrf request-matcher-ref=\"csrfMatcher\"/>\n\n        <headers>\n            <frame-options policy=\"SAMEORIGIN\"/>\n        </headers>\n\n        <!-- ... -->\n    </http>\n\n    <b:bean id=\"csrfMatcher\"\n            class=\"org.springframework.security.web.util.matcher.AndRequestMatcher\">\n        <b:constructor-arg>\n            <b:array>\n                <b:bean class=\"org.springframework.security.web.csrf.CsrfFilter$DefaultRequiresCsrfMatcher\"/>\n                <b:bean class=\"org.springframework.security.web.util.matcher.NegatedRequestMatcher\">\n                    <b:constructor-arg>\n                        <b:bean class=\"org.springframework.security.config.http.PathPatternRequestMatcherFactoryBean\">\n                            <b:constructor-arg value=\"/chat/**\"/>\n                        </b:bean>\n                    </b:constructor-arg>\n                </b:bean>\n            </b:array>\n        </b:constructor-arg>\n    </b:bean>\n    <!-- end::snippet[] -->\n\n</b:beans>\n"
  },
  {
    "path": "docs/src/test/resources/org/springframework/security/docs/servlet/requestcachepreventsavedrequest/SecurityConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<b:beans xmlns:b=\"http://www.springframework.org/schema/beans\"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t xmlns=\"http://www.springframework.org/schema/security\"\n\t\t xmlns:p=\"http://www.springframework.org/schema/p\"\n\t\t xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\">\n\n\t<!-- tag::snippet[] -->\n\t<http auto-config=\"true\">\n\t\t<!-- ... -->\n\t\t<request-cache ref=\"nullRequestCache\"/>\n\t</http>\n\n\t<b:bean id=\"nullRequestCache\" class=\"org.springframework.security.web.savedrequest.NullRequestCache\"/>\n\t<!-- end::snippet[] -->\n\n</b:beans>\n"
  },
  {
    "path": "etc/checkstyle/checkstyle-suppressions.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE suppressions PUBLIC\n\t\t\"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN\"\n\t\t\"https://checkstyle.org/dtds/suppressions_1_2.dtd\">\n<suppressions>\n\t<suppress files=\".*\" checks=\"JavadocMethod\" />\n\t<suppress files=\".*\" checks=\"JavadocStyle\" />\n\t<suppress files=\".*\" checks=\"JavadocTagContinuationIndentation\" />\n\t<suppress files=\".*\" checks=\"JavadocType\" />\n\t<suppress files=\".*\" checks=\"JavadocVariable\" />\n\t<suppress files=\".*\" checks=\"NonEmptyAtclauseDescription\" />\n\t<suppress files=\".*\" checks=\"SpringJavadoc\" />\n\n\t<!-- Ignore third-party code -->\n\t<suppress files=\"BCrypt\\.java|BCryptTests\\.java\" checks=\".*\"/>\n\t<suppress files=\"org[\\\\/]springframework[\\\\/]security[\\\\/]core[\\\\/]ComparableVersion\\.java\" checks=\".*\"/>\n\n\t<!-- Suppress sun.misc.Unsafe in this class (we should eventually remove its usage but it is unrelated to nullability imports) -->\n\t<suppress files=\"StaticFinalReflectionUtils\\.java\" checks=\"IllegalImport\" id=\"bannedNullabilityImports\"/>\n\n\t<!-- Method Visibility that we can't reduce -->\n\t<suppress files=\"AbstractAclVoterTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"AnnotationParameterNameDiscovererTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"AnnotationSecurityAspectTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"AuthenticationPrincipalArgumentResolverTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"ELRequestMatcherContext\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"EnableWebFluxSecurityTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"ExpressionBasedPreInvocationAdviceTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"Jsr250MethodSecurityMetadataSourceTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"JdbcOAuth2AuthorizationService\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"MapBasedMethodSecurityMetadataSourceTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"OAuth2ResourceServerBeanDefinitionParserTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"ObjectIdentityImplTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"ObjectIdentityRetrievalStrategyImplTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"ProtectPointcutPostProcessor\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"ReactiveMethodSecurityConfigurationTests\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"WebSocketMessageBrokerConfigTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"WebSecurityConfigurationTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"WithSecurityContextTestExecutionListenerTests\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"JoseHeader\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"AbstractOAuth2AuthorizationCodeRequestAuthenticationToken\\.java\" checks=\"SpringMethodVisibility\"/>\n\t<suppress files=\"DefaultLoginPageGeneratingFilterTests\\.java\" checks=\"SpringLeadingWhitespace\"/>\n\t<suppress files=\"AuthenticationException\\.java\" checks=\"MutableException\"/>\n\t<suppress files=\"FilterInvocationExpressionRoot\\.java\" checks=\"SpringMethodVisibility\"/>\n\n\t<!-- Lambdas that we can't replace with a method reference because a closure is required -->\n\t<suppress files=\"BearerTokenAuthenticationFilter\\.java\" checks=\"SpringLambda\"/>\n\n\t<!-- CSS content -->\n\t<suppress files=\"CssUtils\\.java\" checks=\"SpringLeadingWhitespace\"/>\n\n\t<!-- Ignore String.toUpperCase() and String.toLowerCase() checks in tests -->\n\t<suppress files=\"[\\\\/]src[\\\\/]test[\\\\/]\" checks=\"RegexpSinglelineJava\" id=\"toLowerCaseWithoutLocale\"/>\n\t<suppress files=\"[\\\\/]src[\\\\/]test[\\\\/]\" checks=\"RegexpSinglelineJava\" id=\"toUpperCaseWithoutLocale\"/>\n\n\t<!-- Suppress @NullMarked check for all files that are NOT package-info.java -->\n\t<suppress files=\".*(?&lt;!package-info)\\.java$\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\n\t<!-- Suppress package-info.java and @NullMarked checks for test sources -->\n\t<suppress files=\"[\\\\/]src[\\\\/]test[\\\\/]\" checks=\"JavadocPackage\"/>\n\t<suppress files=\"[\\\\/]src[\\\\/]test[\\\\/].*package-info\\.java$\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\n\t<!-- Suppress package-info.java and @NullMarked checks for integration test sources -->\n\t<suppress files=\"[\\\\/]src[\\\\/]integration-test[\\\\/]\" checks=\"JavadocPackage\"/>\n\t<suppress files=\"[\\\\/]src[\\\\/]integration-test[\\\\/].*package-info\\.java$\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\n\t<!-- Suppress package-info.java and @NullMarked checks for test fixture sources -->\n\t<suppress files=\"[\\\\/]src[\\\\/]testFixtures[\\\\/]\" checks=\"JavadocPackage\"/>\n\t<suppress files=\"[\\\\/]src[\\\\/]testFixtures[\\\\/].*package-info\\.java$\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\n\t<!-- Suppress nullability checks for modules that don't have JSpecify nullability applied yet -->\n\t<suppress files=\"access[\\\\/]\" checks=\"IllegalImport\" id=\"bannedNullabilityImports\"/>\n\t<suppress files=\"access[\\\\/]\" checks=\"JavadocPackage\"/>\n\t<suppress files=\"access[\\\\/].*package-info\\.java\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\t<suppress files=\"aspects[\\\\/]\" checks=\"IllegalImport\" id=\"bannedNullabilityImports\"/>\n\t<suppress files=\"aspects[\\\\/]\" checks=\"JavadocPackage\"/>\n\t<suppress files=\"aspects[\\\\/].*package-info\\.java\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\t<suppress files=\"config[\\\\/]\" checks=\"IllegalImport\" id=\"bannedNullabilityImports\"/>\n\t<suppress files=\"config[\\\\/]\" checks=\"JavadocPackage\"/>\n\t<suppress files=\"config[\\\\/].*package-info\\.java\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\t<suppress files=\"itest[\\\\/]\" checks=\"IllegalImport\" id=\"bannedNullabilityImports\"/>\n\t<suppress files=\"itest[\\\\/]\" checks=\"JavadocPackage\"/>\n\t<suppress files=\"itest[\\\\/].*package-info\\.java\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\t<suppress files=\"oauth2[\\\\/]oauth2-authorization-server[\\\\/]\" checks=\"IllegalImport\" id=\"bannedNullabilityImports\"/>\n\t<suppress files=\"oauth2[\\\\/]oauth2-authorization-server[\\\\/]\" checks=\"JavadocPackage\"/>\n\t<suppress files=\"oauth2[\\\\/]oauth2-authorization-server[\\\\/].*package-info\\.java\" checks=\"RegexpMultiline\" id=\"requireNullMarkedInPackageInfo\"/>\n\t<suppress files=\"saml2[\\\\/]saml2-service-provider[\\\\/]\" checks=\"JavadocPackage\"/>\n</suppressions>\n"
  },
  {
    "path": "etc/checkstyle/checkstyle.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE module PUBLIC\n\t\t\"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"\n\t\t\"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n<module name=\"com.puppycrawl.tools.checkstyle.Checker\">\n\t<module name=\"SuppressionFilter\">\n\t\t<property name=\"file\"\n\t\t\tvalue=\"${config_loc}/checkstyle-suppressions.xml\" />\n\t</module>\n\t<module name=\"com.puppycrawl.tools.checkstyle.checks.header.RegexpHeaderCheck\">\n\t\t<property name=\"headerFile\" value=\"${config_loc}/header.txt\" />\n\t\t<property name=\"fileExtensions\" value=\"java\" />\n\t</module>\n\t<module name=\"com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocPackageCheck\" /><!-- Require package-info.java -->\n\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpMultilineCheck\">\n\t\t<property name=\"id\" value=\"requireNullMarkedInPackageInfo\"/>\n\t\t<property name=\"format\" value=\"@NullMarked\\s+package\"/>\n\t\t<property name=\"minimum\" value=\"1\"/>\n\t\t<property name=\"maximum\" value=\"1\"/>\n\t\t<property name=\"message\" value=\"package-info.java must include @NullMarked annotation before the package declaration\"/>\n\t\t<property name=\"fileExtensions\" value=\"java\"/>\n\t</module>\n\t<module name=\"io.spring.javaformat.checkstyle.SpringChecks\">\n\t\t<property name=\"excludes\" value=\"io.spring.javaformat.checkstyle.check.SpringHeaderCheck\" />\n\t\t<property name=\"excludes\" value=\"com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocPackageCheck\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.*\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHandlers.*\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.*\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.springframework.security.web.csrf.CsrfTokenAssert.*\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.springframework.security.web.servlet.TestMockHttpServletRequests.*\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.*\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.springframework.security.web.util.matcher.RegexRequestMatcher.*\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.springframework.core.annotation.MergedAnnotations.SearchStrategy.*\" />\n\t\t<property name=\"avoidStaticImportExcludes\" value=\"org.assertj.core.api.InstanceOfAssertFactories.*\"/>\n\t</module>\n\t<module name=\"com.puppycrawl.tools.checkstyle.TreeWalker\">\n \t\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n \t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"format\" value=\"org\\.assertj\\.core\\.api\\.Assertions\\.(catchThrowable|catchThrowableOfType|assertThatThrownBy|assertThatCode)\" />\n\t\t\t<property name=\"message\" value=\"Please use assertThatExceptionOfType.\" />\n\t\t\t<property name=\"ignoreComments\" value=\"true\" />\n\t\t</module>\n\t\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t\t<property name=\"id\" value=\"toLowerCaseWithoutLocale\"/>\n\t\t\t<property name=\"format\" value=\"\\.toLowerCase\\(\\)\"/>\n\t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"message\"\n\t\t\t\t\t  value=\"String.toLowerCase() should be String.toLowerCase(Locale.ROOT) or String.toLowerCase(Locale.ENGLISH)\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t\t<property name=\"id\" value=\"toUpperCaseWithoutLocale\"/>\n\t\t\t<property name=\"format\" value=\"\\.toUpperCase\\(\\)\"/>\n\t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"message\"\n\t\t\t\t\t  value=\"String.toUpperCase() should be String.toUpperCase(Locale.ROOT) or String.toUpperCase(Locale.ENGLISH)\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t<module name=\"com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck\">\n\t\t<property name=\"id\" value=\"bannedImports\"/>\n\t\t<property name=\"regexp\" value=\"true\"/>\n\t\t<property name=\"illegalPkgs\" value=\"org.jetbrains.annotations, reactor.util.annotation, javax.annotation\"/>\n\t</module>\n\t<module name=\"com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck\">\n\t\t<property name=\"id\" value=\"bannedNullabilityImports\"/>\n\t\t<property name=\"regexp\" value=\"true\"/>\n\t\t<!-- Rejects all NonNull, Nonnull, and Nullable types that are NOT in the org.jspecify.annotations package. -->\n\t\t<property name=\"illegalClasses\" value=\"^(?!org\\.jspecify\\.annotations).*(Non[Nn]ull|Nullable)$\"/>\n\t</module>\n\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t<property name=\"id\" value=\"nullableAfterModifiers\"/>\n\t\t<property name=\"format\" value=\"^\\s*(public|protected|private|static|final|synchronized|native|strictfp|abstract)\\s+@Nullable\\s+(public|protected|private|static|final|synchronized|native|strictfp|abstract)\\s\"/>\n\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t<property name=\"message\" value=\"@Nullable must appear immediately before the type, after all other modifiers (e.g., 'private final @Nullable Object' not 'private @Nullable final Object')\"/>\n\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t</module>\n\t</module>\n</module>\n"
  },
  {
    "path": "etc/checkstyle/header.txt",
    "content": "^\\Q/*\\E$\n^\\Q * Copyright \\E(\\d{4}-present the original author or authors.|\\d{4}(, \\d{4})* Acegi Technology Pty Limited)$\n^\\Q *\\E$\n^\\Q * Licensed under the Apache License, Version 2.0 (the \"License\");\\E$\n^\\Q * you may not use this file except in compliance with the License.\\E$\n^\\Q * You may obtain a copy of the License at\\E$\n^\\Q *\\E$\n^\\Q *      https://www.apache.org/licenses/LICENSE-2.0\\E$\n^\\Q *\\E$\n^\\Q * Unless required by applicable law or agreed to in writing, software\\E$\n^\\Q * distributed under the License is distributed on an \"AS IS\" BASIS,\\E$\n^\\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\E$\n^\\Q * See the License for the specific language governing permissions and\\E$\n^\\Q * limitations under the License.\\E$\n^\\Q */\\E$\n^$\n"
  },
  {
    "path": "etc/eclipse/eclipse-code-formatter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<profiles version=\"12\">\n<profile kind=\"CodeFormatterProfile\" name=\"Spring Session Java Conventions\" version=\"12\">\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"90\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_lambda_body\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"enabled\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_resources_in_try\" value=\"80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.source\" value=\"1.8\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.8\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_binary_operator\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.8\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"tab\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"90\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n</profile>\n</profiles>\n"
  },
  {
    "path": "etc/eclipse/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.codeComplete.argumentPrefixes=\norg.eclipse.jdt.core.codeComplete.argumentSuffixes=\norg.eclipse.jdt.core.codeComplete.fieldPrefixes=\norg.eclipse.jdt.core.codeComplete.fieldSuffixes=\norg.eclipse.jdt.core.codeComplete.localPrefixes=\norg.eclipse.jdt.core.codeComplete.localSuffixes=\norg.eclipse.jdt.core.codeComplete.staticFieldPrefixes=\norg.eclipse.jdt.core.codeComplete.staticFieldSuffixes=\norg.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=\norg.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=\norg.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\norg.eclipse.jdt.core.compiler.codegen.methodParameters=generate\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\norg.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve\norg.eclipse.jdt.core.compiler.compliance=1.6\norg.eclipse.jdt.core.compiler.debug.lineNumber=generate\norg.eclipse.jdt.core.compiler.debug.localVariable=generate\norg.eclipse.jdt.core.compiler.debug.sourceFile=generate\norg.eclipse.jdt.core.compiler.doc.comment.support=enabled\norg.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning\norg.eclipse.jdt.core.compiler.problem.assertIdentifier=error\norg.eclipse.jdt.core.compiler.problem.autoboxing=ignore\norg.eclipse.jdt.core.compiler.problem.comparingIdentical=warning\norg.eclipse.jdt.core.compiler.problem.deadCode=warning\norg.eclipse.jdt.core.compiler.problem.deprecation=warning\norg.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled\norg.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled\norg.eclipse.jdt.core.compiler.problem.discouragedReference=warning\norg.eclipse.jdt.core.compiler.problem.emptyStatement=ignore\norg.eclipse.jdt.core.compiler.problem.enumIdentifier=error\norg.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore\norg.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled\norg.eclipse.jdt.core.compiler.problem.fieldHiding=ignore\norg.eclipse.jdt.core.compiler.problem.finalParameterBound=warning\norg.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning\norg.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled\norg.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning\norg.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore\norg.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore\norg.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning\norg.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled\norg.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled\norg.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled\norg.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=default\norg.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore\norg.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning\norg.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore\norg.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore\norg.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore\norg.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled\norg.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public\norg.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags\norg.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning\norg.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled\norg.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled\norg.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=default\norg.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore\norg.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled\norg.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore\norg.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore\norg.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning\norg.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning\norg.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore\norg.eclipse.jdt.core.compiler.problem.nullReference=ignore\norg.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning\norg.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore\norg.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore\norg.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore\norg.eclipse.jdt.core.compiler.problem.rawTypeReference=warning\norg.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore\norg.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore\norg.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore\norg.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore\norg.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore\norg.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled\norg.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning\norg.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled\norg.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled\norg.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore\norg.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning\norg.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled\norg.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning\norg.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore\norg.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning\norg.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore\norg.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning\norg.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore\norg.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore\norg.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled\norg.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled\norg.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled\norg.eclipse.jdt.core.compiler.problem.unusedImport=warning\norg.eclipse.jdt.core.compiler.problem.unusedLabel=warning\norg.eclipse.jdt.core.compiler.problem.unusedLocal=warning\norg.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore\norg.eclipse.jdt.core.compiler.problem.unusedParameter=ignore\norg.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled\norg.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled\norg.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled\norg.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning\norg.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning\norg.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning\norg.eclipse.jdt.core.compiler.processAnnotations=disabled\norg.eclipse.jdt.core.compiler.source=1.6\norg.eclipse.jdt.core.formatter.align_type_members_on_columns=false\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_assignment=0\norg.eclipse.jdt.core.formatter.alignment_for_binary_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_compact_if=16\norg.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80\norg.eclipse.jdt.core.formatter.alignment_for_enum_constants=0\norg.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16\norg.eclipse.jdt.core.formatter.alignment_for_method_declaration=0\norg.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16\norg.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80\norg.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16\norg.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16\norg.eclipse.jdt.core.formatter.blank_lines_after_imports=1\norg.eclipse.jdt.core.formatter.blank_lines_after_package=1\norg.eclipse.jdt.core.formatter.blank_lines_before_field=0\norg.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0\norg.eclipse.jdt.core.formatter.blank_lines_before_imports=1\norg.eclipse.jdt.core.formatter.blank_lines_before_member_type=1\norg.eclipse.jdt.core.formatter.blank_lines_before_method=1\norg.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1\norg.eclipse.jdt.core.formatter.blank_lines_before_package=0\norg.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1\norg.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1\norg.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false\norg.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false\norg.eclipse.jdt.core.formatter.comment.format_block_comments=true\norg.eclipse.jdt.core.formatter.comment.format_header=false\norg.eclipse.jdt.core.formatter.comment.format_html=true\norg.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true\norg.eclipse.jdt.core.formatter.comment.format_line_comments=true\norg.eclipse.jdt.core.formatter.comment.format_source_code=false\norg.eclipse.jdt.core.formatter.comment.indent_parameter_description=true\norg.eclipse.jdt.core.formatter.comment.indent_root_tags=false\norg.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert\norg.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert\norg.eclipse.jdt.core.formatter.comment.line_length=90\norg.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true\norg.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true\norg.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false\norg.eclipse.jdt.core.formatter.compact_else_if=true\norg.eclipse.jdt.core.formatter.continuation_indentation=2\norg.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2\norg.eclipse.jdt.core.formatter.disabling_tag=@formatter\\:off\norg.eclipse.jdt.core.formatter.enabling_tag=@formatter\\:on\norg.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false\norg.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true\norg.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true\norg.eclipse.jdt.core.formatter.indent_empty_lines=false\norg.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true\norg.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true\norg.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true\norg.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false\norg.eclipse.jdt.core.formatter.indentation.size=4\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert\norg.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert\norg.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert\norg.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert\norg.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert\norg.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert\norg.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert\norg.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.join_lines_in_comments=true\norg.eclipse.jdt.core.formatter.join_wrapped_lines=true\norg.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false\norg.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false\norg.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false\norg.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false\norg.eclipse.jdt.core.formatter.lineSplit=90\norg.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false\norg.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false\norg.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0\norg.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1\norg.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true\norg.eclipse.jdt.core.formatter.tabulation.char=tab\norg.eclipse.jdt.core.formatter.tabulation.size=4\norg.eclipse.jdt.core.formatter.use_on_off_tags=true\norg.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false\norg.eclipse.jdt.core.formatter.wrap_before_binary_operator=true\norg.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true\norg.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true\norg.eclipse.jdt.core.javaFormatter=org.springframework.ide.eclipse.jdt.formatter.javaformatter\n"
  },
  {
    "path": "etc/eclipse/org.eclipse.jdt.ui.prefs",
    "content": "cleanup.add_default_serial_version_id=true\ncleanup.add_generated_serial_version_id=false\ncleanup.add_missing_annotations=true\ncleanup.add_missing_deprecated_annotations=true\ncleanup.add_missing_methods=false\ncleanup.add_missing_nls_tags=false\ncleanup.add_missing_override_annotations=true\ncleanup.add_missing_override_annotations_interface_methods=true\ncleanup.add_serial_version_id=false\ncleanup.always_use_blocks=true\ncleanup.always_use_parentheses_in_expressions=false\ncleanup.always_use_this_for_non_static_field_access=true\ncleanup.always_use_this_for_non_static_method_access=false\ncleanup.convert_functional_interfaces=false\ncleanup.convert_to_enhanced_for_loop=false\ncleanup.correct_indentation=false\ncleanup.format_source_code=true\ncleanup.format_source_code_changes_only=false\ncleanup.insert_inferred_type_arguments=false\ncleanup.make_local_variable_final=false\ncleanup.make_parameters_final=false\ncleanup.make_private_fields_final=false\ncleanup.make_type_abstract_if_missing_method=false\ncleanup.make_variable_declarations_final=false\ncleanup.never_use_blocks=false\ncleanup.never_use_parentheses_in_expressions=true\ncleanup.organize_imports=true\ncleanup.qualify_static_field_accesses_with_declaring_class=false\ncleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true\ncleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true\ncleanup.qualify_static_member_accesses_with_declaring_class=true\ncleanup.qualify_static_method_accesses_with_declaring_class=false\ncleanup.remove_private_constructors=true\ncleanup.remove_redundant_type_arguments=true\ncleanup.remove_trailing_whitespaces=true\ncleanup.remove_trailing_whitespaces_all=true\ncleanup.remove_trailing_whitespaces_ignore_empty=false\ncleanup.remove_unnecessary_casts=true\ncleanup.remove_unnecessary_nls_tags=false\ncleanup.remove_unused_imports=true\ncleanup.remove_unused_local_variables=false\ncleanup.remove_unused_private_fields=true\ncleanup.remove_unused_private_members=false\ncleanup.remove_unused_private_methods=true\ncleanup.remove_unused_private_types=true\ncleanup.sort_members=false\ncleanup.sort_members_all=false\ncleanup.use_anonymous_class_creation=false\ncleanup.use_blocks=true\ncleanup.use_blocks_only_for_return_and_throw=false\ncleanup.use_lambda=true\ncleanup.use_parentheses_in_expressions=false\ncleanup.use_this_for_non_static_field_access=false\ncleanup.use_this_for_non_static_field_access_only_if_necessary=false\ncleanup.use_this_for_non_static_method_access=false\ncleanup.use_this_for_non_static_method_access_only_if_necessary=true\ncleanup.use_type_arguments=false\ncleanup_profile=_Spring Boot Cleanup Conventions\ncleanup_settings_version=2\neclipse.preferences.version=1\neditor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true\nformatter_profile=_Spring Boot Java Conventions\nformatter_settings_version=12\norg.eclipse.jdt.ui.exception.name=e\norg.eclipse.jdt.ui.gettersetter.use.is=true\norg.eclipse.jdt.ui.ignorelowercasenames=true\norg.eclipse.jdt.ui.importorder=java;javax;;org.springframework;\\#;\norg.eclipse.jdt.ui.javadoc=true\norg.eclipse.jdt.ui.keywordthis=false\norg.eclipse.jdt.ui.ondemandthreshold=9999\norg.eclipse.jdt.ui.overrideannotation=true\norg.eclipse.jdt.ui.staticondemandthreshold=9999\norg.eclipse.jdt.ui.text.custom_code_templates=<?xml version\\=\"1.0\" encoding\\=\"UTF-8\" standalone\\=\"no\"?><templates><template autoinsert\\=\"true\" context\\=\"gettercomment_context\" deleted\\=\"false\" description\\=\"Comment for getter method\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.gettercomment\" name\\=\"gettercomment\">/**\\n * @return the ${bare_field_name}\\n */</template><template autoinsert\\=\"true\" context\\=\"settercomment_context\" deleted\\=\"false\" description\\=\"Comment for setter method\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.settercomment\" name\\=\"settercomment\">/**\\n * @param ${param} the ${bare_field_name} to set\\n */</template><template autoinsert\\=\"true\" context\\=\"constructorcomment_context\" deleted\\=\"false\" description\\=\"Comment for created constructors\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.constructorcomment\" name\\=\"constructorcomment\">/**\\n * ${tags}\\n */</template><template autoinsert\\=\"false\" context\\=\"filecomment_context\" deleted\\=\"false\" description\\=\"Comment for created Java files\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.filecomment\" name\\=\"filecomment\">/*\\n * Copyright 2004-present the original author or authors.\\n *\\n * Licensed under the Apache License, Version 2.0 (the \"License\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n *      https\\://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \"AS IS\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */</template><template autoinsert\\=\"false\" context\\=\"typecomment_context\" deleted\\=\"false\" description\\=\"Comment for created types\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.typecomment\" name\\=\"typecomment\">/**\\n * @author ${user}\\n */</template><template autoinsert\\=\"true\" context\\=\"fieldcomment_context\" deleted\\=\"false\" description\\=\"Comment for fields\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.fieldcomment\" name\\=\"fieldcomment\">/**\\n * \\n */</template><template autoinsert\\=\"true\" context\\=\"methodcomment_context\" deleted\\=\"false\" description\\=\"Comment for non-overriding methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.methodcomment\" name\\=\"methodcomment\">/**\\n * ${tags}\\n */</template><template autoinsert\\=\"true\" context\\=\"overridecomment_context\" deleted\\=\"false\" description\\=\"Comment for overriding methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.overridecomment\" name\\=\"overridecomment\">/* (non-Javadoc)\\n * ${see_to_overridden}\\n */</template><template autoinsert\\=\"true\" context\\=\"delegatecomment_context\" deleted\\=\"false\" description\\=\"Comment for delegate methods\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.delegatecomment\" name\\=\"delegatecomment\">/**\\n * ${tags}\\n * ${see_to_target}\\n */</template><template autoinsert\\=\"false\" context\\=\"newtype_context\" deleted\\=\"false\" description\\=\"Newly created files\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.newtype\" name\\=\"newtype\">${filecomment}\\n\\n${package_declaration}\\n${typecomment}\\n${type_declaration}</template><template autoinsert\\=\"true\" context\\=\"classbody_context\" deleted\\=\"false\" description\\=\"Code in new class type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.classbody\" name\\=\"classbody\">\\n</template><template autoinsert\\=\"true\" context\\=\"interfacebody_context\" deleted\\=\"false\" description\\=\"Code in new interface type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.interfacebody\" name\\=\"interfacebody\">\\n</template><template autoinsert\\=\"true\" context\\=\"enumbody_context\" deleted\\=\"false\" description\\=\"Code in new enum type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.enumbody\" name\\=\"enumbody\">\\n</template><template autoinsert\\=\"true\" context\\=\"annotationbody_context\" deleted\\=\"false\" description\\=\"Code in new annotation type bodies\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.annotationbody\" name\\=\"annotationbody\">\\n</template><template autoinsert\\=\"false\" context\\=\"catchblock_context\" deleted\\=\"false\" description\\=\"Code in new catch blocks\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.catchblock\" name\\=\"catchblock\">// ${todo} Auto-generated catch block\\nthrow new UnsupportedOperationException(\"Auto-generated method stub\", ${exception_var});</template><template autoinsert\\=\"false\" context\\=\"methodbody_context\" deleted\\=\"false\" description\\=\"Code in created method stubs\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.methodbody\" name\\=\"methodbody\">// ${todo} Auto-generated method stub\\nthrow new UnsupportedOperationException(\"Auto-generated method stub\");</template><template autoinsert\\=\"true\" context\\=\"constructorbody_context\" deleted\\=\"false\" description\\=\"Code in created constructor stubs\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.constructorbody\" name\\=\"constructorbody\">${body_statement}\\n// ${todo} Auto-generated constructor stub</template><template autoinsert\\=\"true\" context\\=\"getterbody_context\" deleted\\=\"false\" description\\=\"Code in created getters\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.getterbody\" name\\=\"getterbody\">return ${field};</template><template autoinsert\\=\"true\" context\\=\"setterbody_context\" deleted\\=\"false\" description\\=\"Code in created setters\" enabled\\=\"true\" id\\=\"org.eclipse.jdt.ui.text.codetemplates.setterbody\" name\\=\"setterbody\">${field} \\= ${param};</template></templates>\nsp_cleanup.add_default_serial_version_id=true\nsp_cleanup.add_generated_serial_version_id=false\nsp_cleanup.add_missing_annotations=true\nsp_cleanup.add_missing_deprecated_annotations=true\nsp_cleanup.add_missing_methods=false\nsp_cleanup.add_missing_nls_tags=false\nsp_cleanup.add_missing_override_annotations=true\nsp_cleanup.add_missing_override_annotations_interface_methods=true\nsp_cleanup.add_serial_version_id=false\nsp_cleanup.always_use_blocks=true\nsp_cleanup.always_use_parentheses_in_expressions=true\nsp_cleanup.always_use_this_for_non_static_field_access=true\nsp_cleanup.always_use_this_for_non_static_method_access=false\nsp_cleanup.convert_to_enhanced_for_loop=false\nsp_cleanup.correct_indentation=false\nsp_cleanup.format_source_code=true\nsp_cleanup.format_source_code_changes_only=false\nsp_cleanup.make_local_variable_final=false\nsp_cleanup.make_parameters_final=false\nsp_cleanup.make_private_fields_final=false\nsp_cleanup.make_type_abstract_if_missing_method=false\nsp_cleanup.make_variable_declarations_final=false\nsp_cleanup.never_use_blocks=false\nsp_cleanup.never_use_parentheses_in_expressions=false\nsp_cleanup.on_save_use_additional_actions=true\nsp_cleanup.organize_imports=true\nsp_cleanup.qualify_static_field_accesses_with_declaring_class=false\nsp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true\nsp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true\nsp_cleanup.qualify_static_member_accesses_with_declaring_class=true\nsp_cleanup.qualify_static_method_accesses_with_declaring_class=false\nsp_cleanup.remove_private_constructors=true\nsp_cleanup.remove_trailing_whitespaces=true\nsp_cleanup.remove_trailing_whitespaces_all=true\nsp_cleanup.remove_trailing_whitespaces_ignore_empty=false\nsp_cleanup.remove_unnecessary_casts=true\nsp_cleanup.remove_unnecessary_nls_tags=false\nsp_cleanup.remove_unused_imports=true\nsp_cleanup.remove_unused_local_variables=false\nsp_cleanup.remove_unused_private_fields=true\nsp_cleanup.remove_unused_private_members=false\nsp_cleanup.remove_unused_private_methods=true\nsp_cleanup.remove_unused_private_types=true\nsp_cleanup.sort_members=false\nsp_cleanup.sort_members_all=false\nsp_cleanup.use_blocks=true\nsp_cleanup.use_blocks_only_for_return_and_throw=false\nsp_cleanup.use_parentheses_in_expressions=false\nsp_cleanup.use_this_for_non_static_field_access=true\nsp_cleanup.use_this_for_non_static_field_access_only_if_necessary=false\nsp_cleanup.use_this_for_non_static_method_access=false\nsp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true\n"
  },
  {
    "path": "etc/nohttp/allowlist.lines",
    "content": "^http://[^/]*nabble.com.*\n^http://blog.opensecurityresearch.com/.*\n^http://iharder.sourceforge.net/current/java/base64/\n^http://jaspan.com.*\n^http://lists.webappsec.org/.*\n^http://webblaze.cs.berkeley.edu/.*\n^http://www.w3.org/2000/09/xmldsig.*\n^http://www.w3.org/2001/10/xml-exc-c14n\n^http://www.w3.org/2001/04/xmldsig-more\n^http://www.w3.org/2001/04/xmlenc\n^http://www.springframework.org/schema/security/.*\n^http://openoffice.org/.*\n^http://www.w3.org/2003/g/data-view\n^http://schemas.openid.net/event/backchannel-logout\n^http://host.docker.internal:8090/back-channel/logout\n^http://host.docker.internal:8090/logout\n"
  },
  {
    "path": "etc/nohttp/checkstyle.xml",
    "content": "<!DOCTYPE module PUBLIC \"-//Puppy Crawl//DTD Check Configuration 1.3//EN\"\n    \"https://www.puppycrawl.com/dtds/configuration_1_3.dtd\">\n<module name=\"Checker\">\n  <property name=\"charset\" value=\"UTF-8\"/>\n  <property name=\"fileExtensions\" value=\"\"/>\n  <module name=\"io.spring.nohttp.checkstyle.check.NoHttpCheck\">\n    <!--  Workaround for https://github.com/spring-io/nohttp/issues/55  -->\n    <property name=\"allowlistFileName\" value=\"${config_loc}/allowlist.lines\" default=\"\"/>\n  </module>\n  <module name=\"SuppressionFilter\">\n    <property name=\"file\" value=\"${config_loc}/suppressions.xml\" default=\"\"/>\n    <property name=\"optional\" value=\"true\"/>\n  </module>\n  <module name=\"SuppressWithPlainTextCommentFilter\"/>\n  <module name=\"BeforeExecutionExclusionFileFilter\">\n    <property name=\"fileNamePattern\" value=\".*[\\\\/]node(_modules|js)[\\\\/].*$\"/>\n  </module>\n</module>\n"
  },
  {
    "path": "etc/s101/config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<headless version=\"1.0\">\n\t<operations>\n\t\t<operation type=\"publish\">\n\t\t\t<argument name=\"overwrite\" value=\"true\"/>\n\t\t\t<argument name=\"diagrams\" value=\"true\"/>\n\t\t</operation>\n\t\t<operation type=\"check-key-measures\">\n\t\t\t<argument name=\"baseline\" value=\"baseline\"/>\n\t\t\t<argument name=\"useProjectFileSpec\" value=\"true\"/>\n\t\t\t<argument name=\"useProjectFileDiagrams\" value=\"true\"/>\n\t\t\t<argument name=\"fail-on-architecture-violations\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-fat-package\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-fat-class\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-fat-method\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-feedback-dependencies\" value=\"true\"/>\n\t\t\t<argument name=\"fail-on-spec-violation-dependencies\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-total-problem-dependencies\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-spec-item-violations\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-biggest-class-tangle\" value=\"true\"/>\n\t\t\t<argument name=\"fail-on-tangled-package\" value=\"true\"/>\n\t\t\t<argument name=\"fail-on-architecture-violations\" value=\"false\"/>\n\t\t\t<argument name=\"fail-on-total-problem-dependencies\" value=\"true\"/>\n\t\t\t<argument name=\"identifier-on-violation\" value=\"S101 key measure violation\"/>\n\t\t</operation>\n\t</operations>\n\t<arguments>\n\t\t<argument name=\"local-project\" value=\"const(THIS_FILE)/project.java.hsp\"/>\n\t\t<argument name=\"repository\" value=\"const(THIS_FILE)/repository\"/>\n\t\t<argument name=\"project\" value=\"snapshots\"/>\n\t</arguments>\n</headless>\n"
  },
  {
    "path": "etc/s101/project.java.hsp",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<local-project language=\"java\" version=\"6.1.19139\" xml-version=\"3\" flavor=\"j2se\">\n  <property name=\"show-as-module\" value=\"false\" />\n  <property name=\"publish-architecture-artifacts\" value=\"true\" />\n  <property name=\"force-classpath\" value=\"false\" />\n  <property name=\"project-type\" value=\"classpath\" />\n  <property name=\"hide-externals\" value=\"true\" />\n  <property name=\"parse-archive-in-archive\" value=\"false\" />\n  <property name=\"include-injected-dependency\" value=\"false\" />\n  <property name=\"relative-to\" value=\"const(THIS_FILE)/../..\" />\n  <property name=\"action-set-mod\" value=\"1\" />\n  <property name=\"detail-mode\" value=\"true\" />\n  <property name=\"hide-deprecated\" value=\"true\" />\n  <property name=\"resolve-name-clashes\" value=\"true\" />\n  <property name=\"project-excluded\" />\n  <property name=\"show-needs-to-compile\" value=\"false\" />\n  <classpath>\n    <classpathentry kind=\"lib\" path=\"acl/build/classes/java/main\" module=\"spring-security-acl\" />\n    <classpathentry kind=\"lib\" path=\"aspects/build/classes/aspectj/main\" module=\"spring-security-aspects\" />\n    <classpathentry kind=\"lib\" path=\"cas/build/classes/java/main\" module=\"spring-security-cas\" />\n    <classpathentry kind=\"lib\" path=\"config/build/classes/java/main\" module=\"spring-security-config\" />\n    <classpathentry kind=\"lib\" path=\"config/build/classes/kotlin/main\" module=\"spring-security-config\" />\n    <classpathentry kind=\"lib\" path=\"core/build/classes/java/main\" module=\"spring-security-core\" />\n    <classpathentry kind=\"lib\" path=\"crypto/build/classes/java/main\" module=\"spring-security-crypto\" />\n    <classpathentry kind=\"lib\" path=\"data/build/classes/java/main\" module=\"spring-security-data\" />\n    <classpathentry kind=\"lib\" path=\"ldap/build/classes/java/main\" module=\"spring-security-ldap\" />\n    <classpathentry kind=\"lib\" path=\"messaging/build/classes/java/main\" module=\"spring-security-messaging\" />\n    <classpathentry kind=\"lib\" path=\"oauth2/oauth2-client/build/classes/java/main\" module=\"spring-security-oauth2-client\" />\n    <classpathentry kind=\"lib\" path=\"oauth2/oauth2-core/build/classes/java/main\" module=\"spring-security-oauth2-core\" />\n    <classpathentry kind=\"lib\" path=\"oauth2/oauth2-jose/build/classes/java/main\" module=\"spring-security-oauth2-jose\" />\n    <classpathentry kind=\"lib\" path=\"oauth2/oauth2-resource-server/build/classes/java/main\" module=\"spring-security-oauth2-resource-server\" />\n    <classpathentry kind=\"lib\" path=\"rsocket/build/classes/java/main\" module=\"spring-security-rsocket\" />\n    <classpathentry kind=\"lib\" path=\"saml2/saml2-service-provider/build/classes/java/main\" module=\"spring-security-saml2-service-provider\" />\n    <classpathentry kind=\"lib\" path=\"taglibs/build/classes/java/main\" module=\"spring-security-taglibs\" />\n    <classpathentry kind=\"lib\" path=\"test/build/classes/java/main\" module=\"spring-security-test\" />\n    <classpathentry kind=\"lib\" path=\"web/build/classes/java/main\" module=\"spring-security-web\" />\n  </classpath>\n  <pom-root-files />\n  <modules-in-scope />\n  <restructuring>\n    <set version=\"3\" name=\"Action list 1\" hiview=\"Codemap\" active=\"true\" todo=\"false\" list=\"0\" />\n  </restructuring>\n  <grid-set sep=\".\" version=\"6.1.19139\" />\n</local-project>\n"
  },
  {
    "path": "etc/s101/repository/repository.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<structure101-repository language=\"java\" version=\"19139\">\n  <xs-configuration>\n    <entry metric=\"Tangled\" scope=\"design\" threshold=\"0\" color=\"153,53,0\" />\n    <entry metric=\"Fat\" scope=\"design\" threshold=\"120\" color=\"255,153,0\" />\n    <entry metric=\"Fat\" scope=\"leaf package\" threshold=\"120\" color=\"0,153,153\" />\n    <entry metric=\"Fat\" scope=\"class\" threshold=\"120\" color=\"255,153,153\" />\n    <entry metric=\"Fat\" scope=\"method\" threshold=\"15\" color=\"51,255,51\" />\n  </xs-configuration>\n  <!--Note: All date strings are stored in short US format e.g. 2/1/06 for 1st Feb 2006-->\n  <project name=\"snapshots\" dir=\"snapshots\" baselineSnapshot=\"default\" version=\"19139\">\n    <snapshot label=\"baseline\" location=\"baseline\" timestamp=\"11/19/21, 11:32 AM\" version=\"19161\" detail=\"true\" good=\"true\" size=\"1500\" />\n  </project>\n</structure101-repository>\n"
  },
  {
    "path": "etc/s101/repository/snapshots/baseline/actions.hsx",
    "content": "x]N;\n\u00021\u0010\u00050Lbcq\u0011\u0013\u0001B2j \u001fIfw\u0012,\"t-L8nw\b]!?\boq2\\UQ\u00024_rmb\u0013% &0\"<C\u000f&<\u0017ɾ\u0010]}\bR|!\u0014,\u0019B}0,\u0017A7+"
  },
  {
    "path": "etc/s101/repository/snapshots/baseline/key-measures.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<KeyMeasureData formatVersion=\"2\" specViolationDependencies=\"0\" specViolationContainers=\"0\" specItemViolations=\"0\" specDependencyViolatingItems=\"0\" biggestUnspecifiedNumber=\"0\" specCoverage=\"0.0\" feedbackDependencies=\"0\" totalProblemDependencies=\"0\" tangledDesign=\"0\" tanglePercentage=\"0.0\" biggestClassTangle=\"3\" fatPackage=\"1\" fatClass=\"5\" fatMethod=\"11\" fatPercentage=\"16.0\" numDiagrams=\"0\" numDiagramDependencyViolations=\"0\" numDiagramDependencyViolatingItems=\"0\" numUnassociatedItems=\"0\" numDiagramViolationContainers=\"0\" moduleAPIViolations=\"0\" moduleRequiresViolations=\"0\" deprecationViolations=\"0\" moduleViolatingItems=\"0\" moduleCoverage=\"0.0\">\n    <enabled-optional-measures/>\n    <specOverlays/>\n    <archOverlays/>\n    <tangledDesign/>\n    <fatPackage>\n        <node id=\"19282\" measure=\"174\" impact=\"20634\" threshold=\"120.0\"/>\n    </fatPackage>\n    <fatClass>\n        <node id=\"595\" measure=\"123\" impact=\"1219\" threshold=\"120.0\"/>\n        <node id=\"70\" measure=\"127\" impact=\"1157\" threshold=\"120.0\"/>\n        <node id=\"392\" measure=\"128\" impact=\"1191\" threshold=\"120.0\"/>\n        <node id=\"88\" measure=\"210\" impact=\"2248\" threshold=\"120.0\"/>\n        <node id=\"79\" measure=\"224\" impact=\"4599\" threshold=\"120.0\"/>\n    </fatClass>\n    <fatMethod>\n        <node id=\"6341\" measure=\"16\" impact=\"137\" threshold=\"15.0\"/>\n        <node id=\"13404\" measure=\"16\" impact=\"109\" threshold=\"15.0\"/>\n        <node id=\"6415\" measure=\"16\" impact=\"241\" threshold=\"15.0\"/>\n        <node id=\"14097\" measure=\"16\" impact=\"288\" threshold=\"15.0\"/>\n        <node id=\"14292\" measure=\"16\" impact=\"199\" threshold=\"15.0\"/>\n        <node id=\"12707\" measure=\"19\" impact=\"154\" threshold=\"15.0\"/>\n        <node id=\"17377\" measure=\"19\" impact=\"283\" threshold=\"15.0\"/>\n        <node id=\"5668\" measure=\"23\" impact=\"387\" threshold=\"15.0\"/>\n        <node id=\"8028\" measure=\"25\" impact=\"371\" threshold=\"15.0\"/>\n        <node id=\"9256\" measure=\"26\" impact=\"292\" threshold=\"15.0\"/>\n        <node id=\"13997\" measure=\"26\" impact=\"361\" threshold=\"15.0\"/>\n    </fatMethod>\n    <specViolationContainers/>\n    <specItemViolations/>\n    <biggestClassTangle>\n        <node id=\"2075\" measure=\"353\"/>\n        <node id=\"2621\" measure=\"357\"/>\n        <node id=\"1680\" measure=\"718\"/>\n    </biggestClassTangle>\n    <archViolationContainers/>\n    <unassociatedItems/>\n    <moduleViolations/>\n    <nodeMap>\n        <entry>\n            <key>17377</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.web.server.ServerHeadersDsl$get$1.invoke(org.springframework.security.config.web.server.ServerHttpSecurity$HeaderSpec):void\" type=\"method\" localSize=\"283\" size=\"283\" displayName=\"spring-security-config.org.springframework.security.config.web.server.ServerHeadersDsl$get$1.invoke(org.springframework.security.config.web.server.ServerHttpSecurity$HeaderSpec):void\" navigationName=\"spring-security-config.org.springframework.security.config.web.server.ServerHeadersDsl$get$1.invoke(org.springframework.security.config.web.server.ServerHttpSecurity$HeaderSpec):void\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>12707</key>\n            <value realName=\"spring-security-web.org.springframework.security.web.util.TextEscapeUtils.escapeEntities(java.lang.String):java.lang.String\" type=\"method\" localSize=\"154\" size=\"154\" displayName=\"spring-security-web.org.springframework.security.web.util.TextEscapeUtils.escapeEntities(java.lang.String):java.lang.String\" navigationName=\"spring-security-web.org.springframework.security.web.util.TextEscapeUtils.escapeEntities(java.lang.String):java.lang.String\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>5668</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.annotation.web.OAuth2LoginDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer):void\" type=\"method\" localSize=\"387\" size=\"387\" displayName=\"spring-security-config.org.springframework.security.config.annotation.web.OAuth2LoginDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer):void\" navigationName=\"spring-security-config.org.springframework.security.config.annotation.web.OAuth2LoginDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer):void\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>6341</key>\n            <value realName=\"spring-security-core.org.springframework.security.authentication.ProviderManager.authenticate(org.springframework.security.core.Authentication):org.springframework.security.core.Authentication\" type=\"method\" localSize=\"137\" size=\"137\" displayName=\"spring-security-core.org.springframework.security.authentication.ProviderManager.authenticate(org.springframework.security.core.Authentication):org.springframework.security.core.Authentication\" navigationName=\"spring-security-core.org.springframework.security.authentication.ProviderManager.authenticate(org.springframework.security.core.Authentication):org.springframework.security.core.Authentication\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>70</key>\n            <value realName=\"spring-security-oauth2-client.org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction\" type=\"class\" localSize=\"1\" size=\"1157\" displayName=\"spring-security-oauth2-client.org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction\" navigationName=\"spring-security-oauth2-client.org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>392</key>\n            <value realName=\"spring-security-core.org.springframework.security.provisioning.JdbcUserDetailsManager\" type=\"class\" localSize=\"1\" size=\"1191\" displayName=\"spring-security-core.org.springframework.security.provisioning.JdbcUserDetailsManager\" navigationName=\"spring-security-core.org.springframework.security.provisioning.JdbcUserDetailsManager\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>9256</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.web.server.ServerHttpSecurity.build():org.springframework.security.web.server.SecurityWebFilterChain\" type=\"method\" localSize=\"292\" size=\"292\" displayName=\"spring-security-config.org.springframework.security.config.web.server.ServerHttpSecurity.build():org.springframework.security.web.server.SecurityWebFilterChain\" navigationName=\"spring-security-config.org.springframework.security.config.web.server.ServerHttpSecurity.build():org.springframework.security.web.server.SecurityWebFilterChain\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>13997</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.http.HttpConfigurationBuilder.createSessionManagementFilters():void\" type=\"method\" localSize=\"361\" size=\"361\" displayName=\"spring-security-config.org.springframework.security.config.http.HttpConfigurationBuilder.createSessionManagementFilters():void\" navigationName=\"spring-security-config.org.springframework.security.config.http.HttpConfigurationBuilder.createSessionManagementFilters():void\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>6415</key>\n            <value realName=\"spring-security-saml2-service-provider.org.springframework.security.saml2.provider.service.registration.OpenSamlAssertingPartyMetadataConverter.convert(java.io.InputStream):org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration$Builder\" type=\"method\" localSize=\"241\" size=\"241\" displayName=\"spring-security-saml2-service-provider.org.springframework.security.saml2.provider.service.registration.OpenSamlAssertingPartyMetadataConverter.convert(java.io.InputStream):org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration$Builder\" navigationName=\"spring-security-saml2-service-provider.org.springframework.security.saml2.provider.service.registration.OpenSamlAssertingPartyMetadataConverter.convert(java.io.InputStream):org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration$Builder\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>79</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.web.server.ServerHttpSecurity\" type=\"class\" localSize=\"1\" size=\"4599\" displayName=\"spring-security-config.org.springframework.security.config.web.server.ServerHttpSecurity\" navigationName=\"spring-security-config.org.springframework.security.config.web.server.ServerHttpSecurity\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>1680</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer\" type=\"class\" localSize=\"1\" size=\"718\" displayName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer\" navigationName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>14097</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.annotation.web.LogoutDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.LogoutConfigurer):void\" type=\"method\" localSize=\"288\" size=\"288\" displayName=\"spring-security-config.org.springframework.security.config.annotation.web.LogoutDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.LogoutConfigurer):void\" navigationName=\"spring-security-config.org.springframework.security.config.annotation.web.LogoutDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.LogoutConfigurer):void\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>19282</key>\n            <value realName=\"spring-security-oauth2-client.org.springframework.security.oauth2.client\" type=\"package\" localSize=\"0\" size=\"20634\" displayName=\"spring-security-oauth2-client.org.springframework.security.oauth2.client\" navigationName=\"spring-security-oauth2-client.org.springframework.security.oauth2.client\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>595</key>\n            <value realName=\"spring-security-saml2-service-provider.org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider\" type=\"class\" localSize=\"1\" size=\"1219\" displayName=\"spring-security-saml2-service-provider.org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider\" navigationName=\"spring-security-saml2-service-provider.org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>14292</key>\n            <value realName=\"spring-security-crypto.org.springframework.security.crypto.bcrypt.BCrypt.hashpw(byte[], java.lang.String):java.lang.String\" type=\"method\" localSize=\"199\" size=\"199\" displayName=\"spring-security-crypto.org.springframework.security.crypto.bcrypt.BCrypt.hashpw(byte[], java.lang.String):java.lang.String\" navigationName=\"spring-security-crypto.org.springframework.security.crypto.bcrypt.BCrypt.hashpw(byte[], java.lang.String):java.lang.String\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>88</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.http.AuthenticationConfigBuilder\" type=\"class\" localSize=\"1\" size=\"2248\" displayName=\"spring-security-config.org.springframework.security.config.http.AuthenticationConfigBuilder\" navigationName=\"spring-security-config.org.springframework.security.config.http.AuthenticationConfigBuilder\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>2075</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.LogoutConfigurer\" type=\"class\" localSize=\"1\" size=\"353\" displayName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.LogoutConfigurer\" navigationName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.LogoutConfigurer\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>13404</key>\n            <value realName=\"spring-security-oauth2-core.org.springframework.security.oauth2.core.oidc.DefaultAddressStandardClaim.equals(java.lang.Object):boolean\" type=\"method\" localSize=\"109\" size=\"109\" displayName=\"spring-security-oauth2-core.org.springframework.security.oauth2.core.oidc.DefaultAddressStandardClaim.equals(java.lang.Object):boolean\" navigationName=\"spring-security-oauth2-core.org.springframework.security.oauth2.core.oidc.DefaultAddressStandardClaim.equals(java.lang.Object):boolean\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>8028</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.annotation.web.HeadersDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.HeadersConfigurer):void\" type=\"method\" localSize=\"371\" size=\"371\" displayName=\"spring-security-config.org.springframework.security.config.annotation.web.HeadersDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.HeadersConfigurer):void\" navigationName=\"spring-security-config.org.springframework.security.config.annotation.web.HeadersDsl$get$1.invoke(org.springframework.security.config.annotation.web.configurers.HeadersConfigurer):void\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n        <entry>\n            <key>2621</key>\n            <value realName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.CsrfConfigurer\" type=\"class\" localSize=\"1\" size=\"357\" displayName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.CsrfConfigurer\" navigationName=\"spring-security-config.org.springframework.security.config.annotation.web.configurers.CsrfConfigurer\" isModule=\"false\" isModuleInterface=\"false\" isDeprecated=\"false\"/>\n        </entry>\n    </nodeMap>\n</KeyMeasureData>\n"
  },
  {
    "path": "etc/s101/repository/snapshots/baseline/settings.hsx",
    "content": "xݘێ0\u0010x\u0007+i6\bԲBHq\u0007\u000f]'<N\u0019;\u001eb'\bXdx\u0014ۃAU/\u0012\u0006uWK\u0004ki\u000e[X%;v/<a\u00143\u001d\u001f\u0012Q|\r9\u0011\u0012ز1\u0001c\u0015R?\u001cJV\u0001E媥\rW`ً6\u0005\reiJbrSBa[\u00034r\u000bc\fk!ޑ1Dz񲫔\u0002R8X05=\u001bt\u000fV*B\u001c-T@\u0003=\u00050uOz4d }/\u00042l\f\u0015Fg\u00051B\u001aiٺJdbA[SϺ*\u0018\u000b\\*l\r\u001c\u0018(8#6\u0007\u0015|0HwA¯\u0001\u0004RBWYc\u0004\u001cٓ*Qr07R{%`lVqY';jDp_)\u0012&$6\u001fɢ1qB47L4袩\n~M\u0014\u001c2\u0005Q&\u001eC\u001bA}a^{<O*Y5\"\u0003sc \u0014\u0012sl}a^\u0016#i+҉It\u0012\nth\n\u0010\"L\u0019A3e4-~K$3^؃W깬\u000e9sqiO̱v&Pފ`<\u001b*N,OeQ\u0019\u0017e<\u0006*mg\u001d\u0019d:\tP\u0017O0\\0ӠFB^.i\u0012{9N\u0010폭ՓS?|\\[\u001a͸O ᨋG\u0002q\f+\fE$ϰ\u000eB av\u001c.\u001b]FknAEB.\u0001\u000ba~\u0016u/C\u0001ú\u0013goqoәh\u0019Kx^%\rE͵9Z豍uUrwb~2\u001b^3\u0007?ݎk"
  },
  {
    "path": "etc/s101/repository/snapshots/baseline/violations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<architecture num-diagrams=\"0\">\n  <violations total=\"0\" total-weighted=\"0\" population=\"0\" population-weighted=\"0\" generated=\"statically\" />\n</architecture>\n"
  },
  {
    "path": "git/hooks/forward-merge",
    "content": "#!/usr/bin/ruby\nrequire 'json'\nrequire 'net/http'\nrequire 'yaml'\nrequire 'logger'\n\n$log = Logger.new(STDOUT)\n$log.level = Logger::WARN\n\nclass ForwardMerge\n  attr_reader :issue, :milestone, :message, :line\n  def initialize(issue, milestone, message, line)\n    @issue = issue\n    @milestone = milestone\n    @message = message\n    @line = line\n  end\nend\n\ndef find_forward_merges(message_file)\n  $log.debug \"Searching for forward merge\"\n  rev=`git rev-parse -q --verify MERGE_HEAD`.strip\n  $log.debug \"Found #{rev} from git rev-parse\"\n  return nil unless rev\n  message = File.read(message_file)\n  forward_merges = []\n  message.each_line do |line|\n    $log.debug \"Checking #{line} for message\"\n    match = /^(?:Fixes|Closes) gh-(\\d+) in (\\d\\.\\d\\.[\\dx](?:[\\.\\-](?:M|RC)\\d)?)$/i.match(line)\n    if match then\n      issue = match[1]\n      milestone = match[2]\n      $log.debug \"Matched reference to issue #{issue} in milestone #{milestone}\"\n      forward_merges << ForwardMerge.new(issue, milestone, message, line)\n    end\n  end\n  $log.debug \"No match in merge message\" unless forward_merges\n  return forward_merges\nend\n\ndef get_issue(username, password, repository, number)\n  $log.debug \"Getting issue #{number} from GitHub repository #{repository}\"\n  uri = URI(\"https://api.github.com/repos/#{repository}/issues/#{number}\")\n  http = Net::HTTP.new(uri.host, uri.port)\n  http.use_ssl=true\n  request = Net::HTTP::Get.new(uri.path)\n  request.basic_auth(username, password)\n  response = http.request(request)\n  $log.debug \"Get HTTP response #{response.code}\"\n  return JSON.parse(response.body) unless response.code != '200'\n  puts \"Failed to retrieve issue #{number}: #{response.message}\"\n  exit 1\nend\n\ndef find_milestone(username, password, repository, title)\n  $log.debug \"Finding milestone #{title} from GitHub repository #{repository}\"\n  uri = URI(\"https://api.github.com/repos/#{repository}/milestones\")\n  http = Net::HTTP.new(uri.host, uri.port)\n  http.use_ssl=true\n  request = Net::HTTP::Get.new(uri.path)\n  request.basic_auth(username, password)\n  response = http.request(request)\n  milestones = JSON.parse(response.body)\n  if title.end_with?(\".x\")\n    prefix = title.delete_suffix('.x')\n    $log.debug \"Finding nearest milestone from candidates starting with #{prefix}\"\n    titles = milestones.map { |milestone| milestone['title'] }\n    titles = titles.select{ |title| title.start_with?(prefix) unless title.end_with?('.x')}\n    titles = titles.sort_by { |v| Gem::Version.new(v) }\n    $log.debug \"Considering candidates #{titles}\"\n    if(titles.empty?)\n      puts \"Cannot find nearest milestone for prefix #{title}\"\n      exit 1\n    end\n    title = titles.first\n    $log.debug \"Found nearest milestone #{title}\"\n  end\n  milestones.each do |milestone|\n    $log.debug \"Considering #{milestone['title']}\"\n    return milestone['number'] if milestone['title'] == title\n  end\n  puts \"Milestone #{title} not found in #{repository}\"\n  exit 1\nend\n\ndef create_issue(username, password, repository, original, title, labels, milestone, milestone_name, dry_run)\n  $log.debug \"Finding forward-merge issue in GitHub repository #{repository} for '#{title}'\"\n  uri = URI(\"https://api.github.com/repos/#{repository}/issues\")\n  http = Net::HTTP.new(uri.host, uri.port)\n  http.use_ssl=true\n  request = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')\n  request.basic_auth(username, password)\n  request.body = {\n    title: title,\n    labels: labels,\n    milestone: milestone.to_i,\n    body: \"Forward port of issue ##{original} to #{milestone_name}.\"\n  }.to_json\n  if dry_run then\n    puts \"Dry run\"\n    puts \"POSTing to #{uri} with body #{request.body}\"\n    return \"dry-run\"\n  end\n  response = JSON.parse(http.request(request).body)\n  $log.debug \"Created new issue #{response['number']}\"\n  return response['number']\nend\n\n$log.debug \"Running forward-merge hook script\"\nmessage_file=ARGV[0]\n\nforward_merges = find_forward_merges(message_file)\nexit 0 unless forward_merges\n\n$log.debug \"Loading config from ~/.spring-boot/forward_merge.yml\"\nconfig = YAML.load_file(File.join(Dir.home, '.spring-boot', 'forward-merge.yml'))\nusername = config['github']['credentials']['username']\npassword = config['github']['credentials']['password']\ndry_run = config['dry_run']\nrepository = 'spring-projects/spring-security'\n\nforward_merges.each do |forward_merge|\n  existing_issue = get_issue(username, password, repository, forward_merge.issue)\n  title = existing_issue['title']\n  labels = existing_issue['labels'].map { |label| label['name'] }\n  labels << \"status: forward-port\"\n  $log.debug \"Processing issue '#{title}'\"\n\n  milestone = find_milestone(username, password, repository, forward_merge.milestone)\n  new_issue_number = create_issue(username, password, repository, forward_merge.issue, title, labels, milestone, forward_merge.milestone, dry_run)\n\n  puts \"Created gh-#{new_issue_number} for forward port of gh-#{forward_merge.issue} into #{forward_merge.milestone}\"\n  rewritten_message = forward_merge.message.sub(forward_merge.line, \"Closes gh-#{new_issue_number}\\n\")\n  File.write(message_file, rewritten_message)\nend\n"
  },
  {
    "path": "git/hooks/pre-push",
    "content": "#!/bin/bash\n# .git/hooks/pre-push\n\necho \"Verifying if the target branch matches the version in gradle.properties\"\n\n# Get the current branch name\nbranch_name=$(git symbolic-ref --short HEAD)\n\n# Verify if branch name ends with '.x'\nif [[ ! \"$branch_name\" =~ \\.x$ ]]; then\n  echo \"Branch name '$branch_name' does not end with '.x', skipping verification.\"\n  exit 0\nfi\n\n# Extract version from gradle.properties\nversion=$(cat gradle.properties | grep 'version=' | awk -F'=' '{print $2}')\n\n# Extract the version prefix from the version\nversion_prefix=$(echo $version | cut -d'-' -f1 | sed 's/\\.[0-9]*$//')\n\n# Check if branch starts with the version prefix\nif [[ \"$branch_name\" != \"$version_prefix\"* ]]; then\n  echo \"Branch name '$branch_name' does not match the version prefix '$version_prefix' in gradle.properties. Make sure you are pushing to the right branch.\"\n  exit 1\nfi\n\nexit 0\n"
  },
  {
    "path": "git/hooks/prepare-forward-merge",
    "content": "#!/usr/bin/ruby\nrequire 'json'\nrequire 'net/http'\nrequire 'yaml'\nrequire 'logger'\n\n$main_branch = \"7.1.x\"\n\n$log = Logger.new(STDOUT)\n$log.level = Logger::WARN\n\ndef get_fixed_issues()\n  $log.debug \"Searching for for forward merge\"\n  rev=`git rev-parse -q --verify MERGE_HEAD`.strip\n  $log.debug \"Found #{rev} from git rev-parse\"\n  return nil unless rev\n  fixed = []\n  message = `git log -1 --pretty=%B #{rev}`\n  message.each_line do |line|\n    $log.debug \"Checking #{line} for message\"\n    fixed << line.strip if /^(?:Fixes|Closes) gh-(\\d+)/i.match(line)\n  end\n  $log.debug \"Found fixed issues #{fixed}\"\n  return fixed;\nend\n\ndef rewrite_message(message_file, fixed)\n  current_branch = `git rev-parse --abbrev-ref HEAD`.strip\n  if current_branch == \"main\"\n    current_branch = $main_branch\n  end\n  rewritten_message = \"\"\n  message = File.read(message_file)\n  message.each_line do |line|\n    match = /^Merge.*branch\\ '(.*)'(?:\\ into\\ (.*))?$/.match(line)\n    if match\n      from_branch = match[1]\n      if from_branch.include? \"/\"\n        from_branch = from_branch.partition(\"/\").last\n      end\n      to_brach = match[2]\n      $log.debug \"Rewriting merge message\"\n      line = \"Merge branch '#{from_branch}'\" + (to_brach ? \" into #{to_brach}\\n\" : \"\\n\")\n    end\n    if fixed and line.start_with?(\"#\")\n      $log.debug \"Adding fixed\"\n      rewritten_message << \"\\n\"\n      fixed.each do |fixes|\n        rewritten_message << \"#{fixes} in #{current_branch}\\n\"\n      end\n      fixed = nil\n    end\n    rewritten_message << line\n  end\n  return rewritten_message\nend\n\n$log.debug \"Running prepare-forward-merge hook script\"\n\nmessage_file=ARGV[0]\nmessage_type=ARGV[1]\n\nif message_type != \"merge\"\n  $log.debug \"Not a merge commit\"\n  exit 0;\nend\n\n$log.debug \"Searching for for forward merge\"\nfixed = get_fixed_issues()\nrewritten_message = rewrite_message(message_file, fixed)\nFile.write(message_file, rewritten_message)\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\ncom-squareup-okhttp3 = \"3.14.9\"\nio-rsocket = \"1.1.5\"\nio-spring-javaformat = \"0.0.47\"\nio-spring-nohttp = \"0.0.11\"\njakarta-websocket = \"2.2.0\"\norg-apache-maven-resolver = \"1.9.27\"\norg-aspectj = \"1.9.25.1\"\norg-bouncycastle = \"1.83\"\norg-eclipse-jetty = \"11.0.26\"\norg-jetbrains-kotlin = \"2.3.20\"\norg-jetbrains-kotlinx = \"1.10.2\"\norg-mockito = \"5.23.0\"\norg-opensaml5 = \"5.2.1\"\norg-springframework = \"7.0.6\"\ncom-password4j = \"1.8.4\"\n\n[libraries]\nch-qos-logback-logback-classic = \"ch.qos.logback:logback-classic:1.5.32\"\ncom-fasterxml-jackson-jackson-bom = \"com.fasterxml.jackson:jackson-bom:2.21.1\"\ncom-google-inject-guice = \"com.google.inject:guice:3.0\"\ncom-netflix-nebula-nebula-project-plugin = \"com.netflix.nebula:nebula-project-plugin:8.2.0\"\ncom-nimbusds-nimbus-jose-jwt = \"com.nimbusds:nimbus-jose-jwt:10.6\"\ncom-nimbusds-oauth2-oidc-sdk = \"com.nimbusds:oauth2-oidc-sdk:11.34\"\ncom-squareup-okhttp3-mockwebserver = { module = \"com.squareup.okhttp3:mockwebserver\", version.ref = \"com-squareup-okhttp3\" }\ncom-squareup-okhttp3-okhttp = { module = \"com.squareup.okhttp3:okhttp\", version.ref = \"com-squareup-okhttp3\" }\ncom-unboundid-unboundid-ldapsdk = \"com.unboundid:unboundid-ldapsdk:7.0.4\"\ncom-jayway-jsonpath-json-path = \"com.jayway.jsonpath:json-path:2.10.0\"\ncommons-collections = \"commons-collections:commons-collections:3.2.2\"\nio-micrometer-context-propagation = \"io.micrometer:context-propagation:1.2.1\"\nio-micrometer-micrometer-observation = \"io.micrometer:micrometer-observation:1.16.4\"\nio-mockk = \"io.mockk:mockk:1.14.9\"\nio-projectreactor-reactor-bom = \"io.projectreactor:reactor-bom:2025.0.4\"\nio-rsocket-rsocket-bom = { module = \"io.rsocket:rsocket-bom\", version.ref = \"io-rsocket\" }\nio-spring-javaformat-spring-javaformat-checkstyle = { module = \"io.spring.javaformat:spring-javaformat-checkstyle\", version.ref = \"io-spring-javaformat\" }\nio-spring-javaformat-spring-javaformat-gradle-plugin = { module = \"io.spring.javaformat:spring-javaformat-gradle-plugin\", version.ref = \"io-spring-javaformat\" }\nio-spring-nohttp-nohttp-checkstyle = { module = \"io.spring.nohttp:nohttp-checkstyle\", version.ref = \"io-spring-nohttp\" }\nio-spring-nohttp-nohttp-gradle = { module = \"io.spring.nohttp:nohttp-gradle\", version.ref = \"io-spring-nohttp\" }\nio-spring-security-release-plugin = \"io.spring.gradle:spring-security-release-plugin:1.0.15\"\njakarta-annotation-jakarta-annotation-api = \"jakarta.annotation:jakarta.annotation-api:3.0.0\"\njakarta-inject-jakarta-inject-api = \"jakarta.inject:jakarta.inject-api:2.0.1\"\njakarta-persistence-jakarta-persistence-api = \"jakarta.persistence:jakarta.persistence-api:3.2.0\"\njakarta-servlet-jakarta-servlet-api = \"jakarta.servlet:jakarta.servlet-api:6.1.0\"\njakarta-servlet-jsp-jakarta-servlet-jsp-api = \"jakarta.servlet.jsp:jakarta.servlet.jsp-api:4.0.0\"\njakarta-servlet-jsp-jstl-jakarta-servlet-jsp-jstl-api = \"jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.2\"\njakarta-websocket-jakarta-websocket-api = { module = \"jakarta.websocket:jakarta.websocket-api\", version.ref = \"jakarta-websocket\" }\njakarta-websocket-jakarta-websocket-client-api = { module = \"jakarta.websocket:jakarta.websocket-client-api\", version.ref = \"jakarta-websocket\" }\njakarta-xml-bind-jakarta-xml-bind-api = \"jakarta.xml.bind:jakarta.xml.bind-api:4.0.5\"\nldapsdk = \"ldapsdk:ldapsdk:4.1\"\nnet-sourceforge-htmlunit = \"net.sourceforge.htmlunit:htmlunit:2.70.0\"\norg-htmlunit-htmlunit = \"org.htmlunit:htmlunit:4.21.0\"\norg-apache-httpcomponents-httpclient = \"org.apache.httpcomponents.client5:httpclient5:5.6\"\norg-apache-kerby-simplekdc='org.apache.kerby:kerb-simplekdc:2.1.1'\norg-apache-maven-maven-resolver-provider = \"org.apache.maven:maven-resolver-provider:3.9.14\"\norg-apache-maven-resolver-maven-resolver-connector-basic = { module = \"org.apache.maven.resolver:maven-resolver-connector-basic\", version.ref = \"org-apache-maven-resolver\" }\norg-apache-maven-resolver-maven-resolver-impl = { module = \"org.apache.maven.resolver:maven-resolver-impl\", version.ref = \"org-apache-maven-resolver\" }\norg-apache-maven-resolver-maven-resolver-transport-http = { module = \"org.apache.maven.resolver:maven-resolver-transport-http\", version.ref = \"org-apache-maven-resolver\" }\norg-apereo-cas-client-cas-client-core = \"org.apereo.cas.client:cas-client-core:4.0.4\"\nio-freefair-gradle-aspectj-plugin = \"io.freefair.gradle:aspectj-plugin:8.14.4\"\norg-aspectj-aspectjrt = { module = \"org.aspectj:aspectjrt\", version.ref = \"org-aspectj\" }\norg-aspectj-aspectjweaver = { module = \"org.aspectj:aspectjweaver\", version.ref = \"org-aspectj\" }\norg-assertj-assertj-core = \"org.assertj:assertj-core:3.27.7\"\norg-bouncycastle-bcpkix-jdk15on = { module = \"org.bouncycastle:bcpkix-jdk18on\", version.ref = \"org-bouncycastle\" }\norg-bouncycastle-bcprov-jdk15on = { module = \"org.bouncycastle:bcprov-jdk18on\", version.ref = \"org-bouncycastle\" }\norg-eclipse-jetty-jetty-server = { module = \"org.eclipse.jetty:jetty-server\", version.ref = \"org-eclipse-jetty\" }\norg-eclipse-jetty-jetty-servlet = { module = \"org.eclipse.jetty:jetty-servlet\", version.ref = \"org-eclipse-jetty\" }\norg-hamcrest = \"org.hamcrest:hamcrest:2.2\"\norg-hibernate-orm-hibernate-core = \"org.hibernate.orm:hibernate-core:7.3.0.Final\"\norg-hsqldb = \"org.hsqldb:hsqldb:2.7.4\"\norg-jetbrains-kotlin-kotlin-bom = { module = \"org.jetbrains.kotlin:kotlin-bom\", version.ref = \"org-jetbrains-kotlin\" }\norg-jetbrains-kotlin-kotlin-gradle-plugin = { module = \"org.jetbrains.kotlin:kotlin-gradle-plugin\", version.ref = \"org-jetbrains-kotlin\" }\norg-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-bom\", version.ref = \"org-jetbrains-kotlinx\" }\norg-junit-junit-bom = \"org.junit:junit-bom:6.0.3\"\norg-mockito-mockito-bom = { module = \"org.mockito:mockito-bom\", version.ref = \"org-mockito\" }\norg-opensaml-opensaml5-saml-api = { module = \"org.opensaml:opensaml-saml-api\", version.ref = \"org-opensaml5\" }\norg-opensaml-opensaml5-saml-impl = { module = \"org.opensaml:opensaml-saml-impl\", version.ref = \"org-opensaml5\" }\norg-python-jython = { module = \"org.python:jython\", version = \"2.5.3\" }\norg-seleniumhq-selenium-htmlunit-driver = \"org.seleniumhq.selenium:htmlunit3-driver:4.41.0\"\norg-seleniumhq-selenium-selenium-java = \"org.seleniumhq.selenium:selenium-java:4.41.0\"\norg-seleniumhq-selenium-selenium-support = \"org.seleniumhq.selenium:selenium-support:3.141.59\"\norg-skyscreamer-jsonassert = \"org.skyscreamer:jsonassert:1.5.3\"\norg-slf4j-log4j-over-slf4j = \"org.slf4j:log4j-over-slf4j:1.7.36\"\norg-slf4j-slf4j-api = \"org.slf4j:slf4j-api:2.0.17\"\norg-springframework-data-spring-data-bom = \"org.springframework.data:spring-data-bom:2025.1.4\"\norg-springframework-ldap-spring-ldap-core = \"org.springframework.ldap:spring-ldap-core:4.0.2\"\norg-springframework-spring-framework-bom = { module = \"org.springframework:spring-framework-bom\", version.ref = \"org-springframework\" }\norg-synchronoss-cloud-nio-multipart-parser = \"org.synchronoss.cloud:nio-multipart-parser:1.1.0\"\ntools-jackson-jackson-bom = \"tools.jackson:jackson-bom:3.1.0\"\n\ncom-google-code-gson-gson = \"com.google.code.gson:gson:2.13.2\"\ncom-thaiopensource-trag = \"com.thaiopensource:trang:20091111\"\nnet-sourceforge-saxon-saxon = \"net.sourceforge.saxon:saxon:9.1.0.8\"\norg-yaml-snakeyaml = \"org.yaml:snakeyaml:1.33\"\norg-apache-commons-commons-io = \"org.apache.commons:commons-io:1.3.2\"\nio-github-gradle-nexus-publish-plugin = \"io.github.gradle-nexus:publish-plugin:2.0.0\"\norg-gretty-gretty = \"org.gretty:gretty:4.1.10\"\ncom-github-ben-manes-gradle-versions-plugin = \"com.github.ben-manes:gradle-versions-plugin:0.52.0\"\ncom-github-spullara-mustache-java-compiler = \"com.github.spullara.mustache.java:compiler:0.9.14\"\norg-hidetake-gradle-ssh-plugin = \"org.hidetake:gradle-ssh-plugin:2.10.1\"\norg-jfrog-buildinfo-build-info-extractor-gradle = \"org.jfrog.buildinfo:build-info-extractor-gradle:6.0.4\"\norg-sonarsource-scanner-gradle-sonarqube-gradle-plugin = \"org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8.0.1969\"\norg-instancio-instancio-junit = \"org.instancio:instancio-junit:3.7.1\"\n\nspring-nullability = 'io.spring.nullability:io.spring.nullability.gradle.plugin:0.0.12'\nwebauthn4j-core = 'com.webauthn4j:webauthn4j-core:0.31.1.RELEASE'\ncom-password4j-password4j = { module = \"com.password4j:password4j\", version.ref = \"com-password4j\" }\n\n[plugins]\n\norg-gradle-wrapper-upgrade = \"org.gradle.wrapper-upgrade:0.12\"\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.4.1-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "#\n# Copyright 2004-present the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nspringBootVersion=4.1.0-SNAPSHOT\nversion=7.1.0-SNAPSHOT\nsamplesBranch=main\norg.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError\norg.gradle.parallel=true\norg.gradle.caching=true\nkotlin.stdlib.default.dependency=false\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "itest/context/spring-security-itest-context.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-test'\napply plugin: 'java-toolchain'\napply plugin: 'test-compile-target-jdk25'\n\ndependencies {\n\timplementation platform(project(\":spring-security-dependencies\"))\n\timplementation project(':spring-security-core')\n\timplementation project(':spring-security-access')\n\timplementation 'org.python:jython'\n\timplementation 'org.springframework:spring-aop'\n\timplementation 'org.springframework:spring-beans'\n\timplementation 'org.springframework:spring-context'\n\timplementation 'org.springframework:spring-tx'\n\n\ttestImplementation project(path: ':spring-security-web')\n\ttestImplementation project(path: ':spring-security-web', configuration: 'tests')\n\ttestImplementation 'jakarta.servlet:jakarta.servlet-api'\n\ttestImplementation 'org.springframework:spring-web'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly project(':spring-security-config')\n\ttestRuntimeOnly 'org.aspectj:aspectjweaver'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\nSystem.setProperty('python.cachedir.skip', 'true')\n"
  },
  {
    "path": "itest/context/src/integration-test/java/org/springframework/security/integration/HttpNamespaceWithMultipleInterceptorsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ContextConfiguration(locations = { \"/http-extra-fsi-app-context.xml\" })\n@ExtendWith(SpringExtension.class)\npublic class HttpNamespaceWithMultipleInterceptorsTests {\n\n\t@Autowired\n\tprivate FilterChainProxy fcp;\n\n\t@Test\n\tpublic void requestThatIsMatchedByDefaultInterceptorIsAllowed() throws Exception {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"/somefile.html\").build();\n\t\trequest.setSession(createAuthenticatedSession(\"ROLE_0\", \"ROLE_1\", \"ROLE_2\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.fcp.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getStatus()).isEqualTo(200);\n\t}\n\n\t@Test\n\tpublic void securedUrlAccessIsRejectedWithoutRequiredRole() throws Exception {\n\t\tMockHttpServletRequest request = TestMockHttpServletRequests.get(\"/secure/somefile.html\").build();\n\t\trequest.setSession(createAuthenticatedSession(\"ROLE_0\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.fcp.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getStatus()).isEqualTo(403);\n\t}\n\n\tpublic HttpSession createAuthenticatedSession(String... roles) {\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"bob\", \"bobspassword\", roles));\n\t\tsession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,\n\t\t\t\tSecurityContextHolder.getContext());\n\t\tSecurityContextHolder.clearContext();\n\t\treturn session;\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/integration-test/java/org/springframework/security/integration/HttpPathParameterStrippingTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ContextConfiguration(locations = { \"/http-path-param-stripping-app-context.xml\" })\n@ExtendWith(SpringExtension.class)\npublic class HttpPathParameterStrippingTests {\n\n\t@Autowired\n\tprivate FilterChainProxy fcp;\n\n\t@Test\n\tpublic void securedFilterChainCannotBeBypassedByAddingPathParameters() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setPathInfo(\"/secured;x=y/admin.html\");\n\t\trequest.setSession(createAuthenticatedSession(\"ROLE_USER\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.fcp.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t}\n\n\t@Test\n\tpublic void adminFilePatternCannotBeBypassedByAddingPathParameters() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setServletPath(\"/secured/admin.html;x=user.html\");\n\t\trequest.setSession(createAuthenticatedSession(\"ROLE_USER\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.fcp.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t}\n\n\t@Test\n\tpublic void adminFilePatternCannotBeBypassedByAddingPathParametersWithPathInfo() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setServletPath(\"/secured\");\n\t\trequest.setPathInfo(\"/admin.html;x=user.html\");\n\t\trequest.setSession(createAuthenticatedSession(\"ROLE_USER\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.fcp.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t}\n\n\tpublic HttpSession createAuthenticatedSession(String... roles) {\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"bob\", \"bobspassword\", roles));\n\t\tsession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,\n\t\t\t\tSecurityContextHolder.getContext());\n\t\tSecurityContextHolder.clearContext();\n\t\treturn session;\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/integration-test/java/org/springframework/security/integration/MultiAnnotationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.integration.multiannotation.MultiAnnotationService;\nimport org.springframework.security.integration.multiannotation.PreAuthorizeService;\nimport org.springframework.security.integration.multiannotation.SecuredService;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n */\n@ContextConfiguration(locations = { \"/multi-sec-annotation-app-context.xml\" })\n@ExtendWith(SpringExtension.class)\npublic class MultiAnnotationTests {\n\n\tprivate final TestingAuthenticationToken joe_a = new TestingAuthenticationToken(\"joe\", \"pass\", \"ROLE_A\");\n\n\tprivate final TestingAuthenticationToken joe_b = new TestingAuthenticationToken(\"joe\", \"pass\", \"ROLE_B\");\n\n\t@Autowired\n\tMultiAnnotationService service;\n\n\t@Autowired\n\tPreAuthorizeService preService;\n\n\t@Autowired\n\tSecuredService secService;\n\n\t@AfterEach\n\t@BeforeEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void preAuthorizeDeniedIsDenied() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.joe_a);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.service::preAuthorizeDenyAllMethod);\n\t}\n\n\t@Test\n\tpublic void preAuthorizeRoleAIsDeniedIfRoleMissing() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.joe_b);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.service::preAuthorizeHasRoleAMethod);\n\t}\n\n\t@Test\n\tpublic void preAuthorizeRoleAIsAllowedIfRolePresent() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.joe_a);\n\t\tthis.service.preAuthorizeHasRoleAMethod();\n\t}\n\n\t@Test\n\tpublic void securedAnonymousIsAllowed() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.joe_a);\n\t\tthis.service.securedAnonymousMethod();\n\t}\n\n\t@Test\n\tpublic void securedRoleAIsDeniedIfRoleMissing() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.joe_b);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.service::securedRoleAMethod);\n\t}\n\n\t@Test\n\tpublic void securedRoleAIsAllowedIfRolePresent() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.joe_a);\n\t\tthis.service.securedRoleAMethod();\n\t}\n\n\t@Test\n\tpublic void preAuthorizedOnlyServiceDeniesIfRoleMissing() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.joe_b);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.preService::preAuthorizedMethod);\n\t}\n\n\t@Test\n\tpublic void securedOnlyRoleAServiceDeniesIfRoleMissing() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.joe_b);\n\t\tassertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.secService::securedMethod);\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/integration-test/java/org/springframework/security/integration/SEC933ApplicationContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ContextConfiguration(locations = { \"/sec-933-app-context.xml\" })\n@ExtendWith(SpringExtension.class)\npublic class SEC933ApplicationContextTests {\n\n\t@Autowired\n\tprivate UserDetailsService userDetailsService;\n\n\t@Test\n\tpublic void testSimpleApplicationContextBootstrap() {\n\t\tassertThat(this.userDetailsService).isNotNull();\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/integration-test/java/org/springframework/security/integration/StubUserRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\npublic class StubUserRepository implements UserRepository {\n\n\t@Override\n\tpublic void doSomething() {\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/integration-test/java/org/springframework/security/integration/python/PythonInterpreterBasedSecurityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.python;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n@ContextConfiguration(locations = { \"/python-method-access-app-context.xml\" })\n@ExtendWith(SpringExtension.class)\npublic class PythonInterpreterBasedSecurityTests {\n\n\t@Autowired\n\tprivate TestService service;\n\n\t@Test\n\tpublic void serviceMethod() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.unauthenticated(\"bob\", \"bobspassword\"));\n\n\t\t// for (int i=0; i < 1000; i++) {\n\t\tthis.service.someMethod();\n\t\t// }\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/integration-test/java/org/springframework/security/performance/FilterChainPerformanceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.performance;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.util.StopWatch;\n\n/**\n * @author Luke Taylor\n * @since 2.0\n */\n@ContextConfiguration(locations = { \"/filter-chain-performance-app-context.xml\" })\n@ExtendWith(SpringExtension.class)\npublic class FilterChainPerformanceTests {\n\n\t// Adjust as required\n\tprivate static final int N_INVOCATIONS = 1; // 1000\n\n\tprivate static final int N_AUTHORITIES = 2; // 200\n\n\tprivate static StopWatch sw = new StopWatch(\"Filter Chain Performance Tests\");\n\n\tprivate final UsernamePasswordAuthenticationToken user = UsernamePasswordAuthenticationToken.authenticated(\"bob\",\n\t\t\t\"bobspassword\", createRoles(N_AUTHORITIES));\n\n\tprivate HttpSession session;\n\n\t@Autowired\n\t@Qualifier(\"fcpMinimalStack\")\n\tprivate FilterChainProxy minimalStack;\n\n\t@Autowired\n\t@Qualifier(\"fcpFullStack\")\n\tprivate FilterChainProxy fullStack;\n\n\t@BeforeEach\n\tpublic void createAuthenticatedSession() {\n\t\tthis.session = new MockHttpSession();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.user);\n\t\tthis.session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,\n\t\t\t\tSecurityContextHolder.getContext());\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterAll\n\tpublic static void dumpStopWatch() {\n\t\tSystem.out.println(sw.prettyPrint());\n\t}\n\n\tprivate MockHttpServletRequest createRequest(String url) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(this.session);\n\t\trequest.setServletPath(url);\n\t\trequest.setMethod(\"GET\");\n\t\treturn request;\n\t}\n\n\tprivate void runWithStack(FilterChainProxy stack) throws Exception {\n\t\tfor (int i = 0; i < N_INVOCATIONS; i++) {\n\t\t\tMockHttpServletRequest request = createRequest(\"/somefile.html\");\n\t\t\tstack.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\t\tthis.session = request.getSession();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void minimalStackInvocation() throws Exception {\n\t\tsw.start(\"Run with Minimal Filter Stack\");\n\t\trunWithStack(this.minimalStack);\n\t\tsw.stop();\n\t}\n\n\t@Test\n\tpublic void fullStackInvocation() throws Exception {\n\t\tsw.start(\"Run with Full Filter Stack\");\n\t\trunWithStack(this.fullStack);\n\t\tsw.stop();\n\t}\n\n\t/**\n\t * Creates data from 1 to N_AUTHORITIES in steps of 10, performing N_INVOCATIONS for\n\t * each\n\t */\n\t@Test\n\tpublic void provideDataOnScalingWithNumberOfAuthoritiesUserHas() throws Exception {\n\t\tStopWatch sw = new StopWatch(\"Scaling with nAuthorities\");\n\t\tfor (int user = 0; user < N_AUTHORITIES / 10; user++) {\n\t\t\tint nAuthorities = (user != 0) ? user * 10 : 1;\n\t\t\tSecurityContextHolder.getContext()\n\t\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.authenticated(\"bob\", \"bobspassword\",\n\t\t\t\t\t\tcreateRoles(nAuthorities)));\n\t\t\tthis.session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,\n\t\t\t\t\tSecurityContextHolder.getContext());\n\t\t\tSecurityContextHolder.clearContext();\n\t\t\tsw.start(nAuthorities + \" authorities\");\n\t\t\trunWithStack(this.minimalStack);\n\t\t\tSystem.out.println(sw.shortSummary());\n\t\t\tsw.stop();\n\t\t}\n\t\tSystem.out.println(sw.prettyPrint());\n\t}\n\n\tprivate List<GrantedAuthority> createRoles(int howMany) {\n\t\t// This is always the worst case scenario - the required role is ROLE_1, but they\n\t\t// are created in reverse order\n\t\tGrantedAuthority[] roles = new GrantedAuthority[howMany];\n\n\t\tfor (int i = howMany - 1; i >= 0; i--) {\n\t\t\troles[i] = new SimpleGrantedAuthority(\"ROLE_\" + i);\n\t\t}\n\n\t\treturn Arrays.asList(roles);\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/integration-test/java/org/springframework/security/performance/ProtectPointcutPerformanceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.performance;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.util.StopWatch;\n\nimport static org.assertj.core.api.Assertions.fail;\n\n/**\n * @author Luke Taylor\n */\n@ContextConfiguration(locations = { \"/protect-pointcut-performance-app-context.xml\" })\n@ExtendWith(SpringExtension.class)\npublic class ProtectPointcutPerformanceTests implements ApplicationContextAware {\n\n\tApplicationContext ctx;\n\n\t@BeforeEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t// Method for use with profiler\n\t@Test\n\tpublic void usingPrototypeDoesNotParsePointcutOnEachCall() {\n\t\tStopWatch sw = new StopWatch();\n\t\tsw.start();\n\t\tfor (int i = 0; i < 1000; i++) {\n\t\t\ttry {\n\t\t\t\tSessionRegistry reg = (SessionRegistry) this.ctx.getBean(\"sessionRegistryPrototype\");\n\t\t\t\treg.getAllPrincipals();\n\t\t\t\tfail(\"Expected AuthenticationCredentialsNotFoundException\");\n\t\t\t}\n\t\t\tcatch (AuthenticationCredentialsNotFoundException expected) {\n\t\t\t}\n\t\t}\n\t\tsw.stop();\n\t\t// assertThat(sw.getTotalTimeMillis() < 1000).isTrue();\n\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.ctx = applicationContext;\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/filter-chain-performance-app-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  -\n  -->\n\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:sec=\"http://www.springframework.org/schema/security\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<bean id=\"fcpMinimalStack\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<sec:filter-chain-map>\n\t\t\t<sec:filter-chain pattern=\"/**\" filters=\"scpf,preAuthFilter,etf,fsi\"/>\n\t\t</sec:filter-chain-map>\n\t</bean>\n\n\t<bean id=\"fcpFullStack\" class=\"org.springframework.security.web.FilterChainProxy\">\n\t\t<sec:filter-chain-map>\n\t\t\t<sec:filter-chain pattern=\"/**\" filters=\"scpf,preAuthFilter,apf,basicPf,logoutFilter,scharf,etf,fsi\"/>\n\t\t</sec:filter-chain-map>\n\t</bean>\n\n\t<bean id=\"authenticationManager\" class=\"org.springframework.security.authentication.ProviderManager\">\n\t\t<constructor-arg>\n\t\t\t<list>\n\t\t\t\t<bean class=\"org.springframework.security.authentication.dao.DaoAuthenticationProvider\">\n\t\t\t\t\t<constructor-arg name=\"userDetailsService\" ref=\"userService\" />\n\t\t\t\t</bean>\n\t\t\t</list>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<sec:user-service id=\"userService\">\n\t\t<sec:user name=\"bob\" password=\"bobspassword\" authorities=\"ROLE_0,ROLE_1\"/>\n\t</sec:user-service>\n\n\t<bean id=\"scpf\" class=\"org.springframework.security.web.context.SecurityContextPersistenceFilter\"/>\n\n\t<bean id=\"apf\" class=\"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter\">\n\t\t<property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n\t</bean>\n\n\t<bean id=\"basicPf\" class=\"org.springframework.security.web.authentication.www.BasicAuthenticationFilter\">\n\t\t<constructor-arg ref=\"authenticationManager\"/>\n\t</bean>\n\n\t<bean id=\"preAuthFilter\" class=\"org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter\">\n\t\t<property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n\t\t<property name=\"exceptionIfHeaderMissing\" value=\"false\" />\n\t</bean>\n\n\t<bean id=\"scharf\" class=\"org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter\" />\n\n\t<bean id=\"preAuthenticatedProcessingFilterEntryPoint\"\n\t\t\tclass=\"org.springframework.security.web.authentication.Http403ForbiddenEntryPoint\"/>\n\n\t<bean id=\"logoutFilter\" class=\"org.springframework.security.web.authentication.logout.LogoutFilter\">\n\t\t<constructor-arg value=\"/\"/>\n\t\t<constructor-arg>\n\t\t\t<list>\n\t\t\t\t<bean class=\"org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler\"/>\n\t\t\t</list>\n\t\t</constructor-arg>\n\t</bean>\n\n\t<bean id=\"etf\" class=\"org.springframework.security.web.access.ExceptionTranslationFilter\">\n\t\t<constructor-arg ref=\"preAuthenticatedProcessingFilterEntryPoint\"/>\n\t</bean>\n\n\t<bean id=\"preAuthenticatedAuthenticationProvider\" class=\"org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider\">\n\t\t<property name=\"preAuthenticatedUserDetailsService\" ref=\"preAuthenticatedUserDetailsService\"/>\n\t</bean>\n\n\t<bean id=\"preAuthenticatedUserDetailsService\" class=\"org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper\">\n\t\t<property name=\"userDetailsService\" ref=\"userService\"/>\n\t</bean>\n\n\t<bean id=\"accessDecisionManager\" class=\"org.springframework.security.access.vote.AffirmativeBased\">\n\t\t<constructor-arg>\n\t\t\t<list>\n\t\t\t\t<bean class=\"org.springframework.security.access.vote.RoleVoter\"/>\n\t\t\t</list>\n\t\t</constructor-arg>\n\t\t<property name=\"allowIfAllAbstainDecisions\" value=\"false\"/>\n\t</bean>\n\n\t<bean id=\"fsi\" class=\"org.springframework.security.web.access.intercept.FilterSecurityInterceptor\">\n\t\t<property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n\t\t<property name=\"accessDecisionManager\" ref=\"accessDecisionManager\"/>\n\t\t<property name=\"securityMetadataSource\">\n\t\t\t<sec:filter-security-metadata-source use-expressions=\"false\">\n\t\t\t\t<sec:intercept-url pattern=\"/secure/extreme/**\" access=\"ROLE_2\"/>\n\t\t\t\t<sec:intercept-url pattern=\"/secure/**\" access=\"ROLE_1\"/>\n\t\t\t\t<sec:intercept-url pattern=\"/**\" access=\"ROLE_0\"/>\n\t\t\t</sec:filter-security-metadata-source>\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"roleVoter\" class=\"org.springframework.security.access.vote.RoleVoter\"/>\n\n</beans>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/http-extra-fsi-app-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  -\n  -->\n\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:sec=\"http://www.springframework.org/schema/security\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<sec:http>\n\t\t<!-- Slip in a bean property name EL test -->\n\t\t<sec:intercept-url pattern=\"/**\" access=\"@fsi.getAccessDecisionManager() eq @accessDecisionManager\" />\n\t\t<sec:form-login />\n\t\t<sec:custom-filter ref=\"fsi\" after=\"FILTER_SECURITY_INTERCEPTOR \" />\n\t\t<sec:csrf disabled=\"true\"/>\n\t</sec:http>\n\n\t<bean id=\"fsi\" class=\"org.springframework.security.web.access.intercept.FilterSecurityInterceptor\">\n\t\t<property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n\t\t<property name=\"accessDecisionManager\" ref=\"accessDecisionManager\"/>\n\t\t<property name=\"securityMetadataSource\">\n\t\t\t<sec:filter-security-metadata-source use-expressions=\"false\">\n\t\t\t\t<sec:intercept-url pattern=\"/secure/extreme/**\" access=\"ROLE_2\"/>\n\t\t\t\t<sec:intercept-url pattern=\"/secure/**\" access=\"ROLE_1\"/>\n\t\t\t</sec:filter-security-metadata-source>\n\t\t</property>\n\t\t<property name=\"observeOncePerRequest\" value=\"false\" />\n\t</bean>\n\n\t<bean id=\"accessDecisionManager\" class=\"org.springframework.security.access.vote.AffirmativeBased\">\n\t\t<constructor-arg>\n\t\t\t<list>\n\t\t\t\t<bean class=\"org.springframework.security.access.vote.RoleVoter\"/>\n\t\t\t</list>\n\t\t</constructor-arg>\n\t\t<property name=\"allowIfAllAbstainDecisions\" value=\"false\"/>\n\t</bean>\n\n\t<sec:authentication-manager alias=\"authenticationManager\">\n\t\t<sec:authentication-provider>\n\t\t\t<sec:user-service id=\"userService\">\n\t\t\t\t<sec:user name=\"notused\" password=\"notused\" authorities=\"ROLE_0,ROLE_1\"/>\n\t\t\t</sec:user-service>\n\t\t</sec:authentication-provider>\n\t</sec:authentication-manager>\n\n</beans>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/http-path-param-stripping-app-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  -\n  -->\n\n<b:beans xmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<http pattern=\"/secured/**\" use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/secured/*user.html\" access=\"ROLE_USER\" />\n\t\t<intercept-url pattern=\"/secured/admin.html\" access=\"ROLE_ADMIN\" />\n\t\t<intercept-url pattern=\"/secured/user/**\" access=\"ROLE_USER\" />\n\t\t<intercept-url pattern=\"/secured/admin/*\" access=\"ROLE_ADMIN\" />\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_NO_ACCESS\" />\n\t\t<form-login />\n\t</http>\n\n\t<http pattern=\"/**\" security=\"none\" />\n\n\t<authentication-manager alias=\"authenticationManager\">\n\t\t<authentication-provider>\n\t\t\t<user-service id=\"userService\">\n\t\t\t\t<user name=\"notused\" password=\"notused\" authorities=\"ROLE_0,ROLE_1\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n</b:beans>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/multi-sec-annotation-app-context.xml",
    "content": "<b:beans xmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\n\txmlns:security=\"http://www.springframework.org/schema/security\"\n\txsi:schemaLocation=\"\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\thttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd\n\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<global-method-security pre-post-annotations=\"enabled\" secured-annotations=\"enabled\" />\n\n\t<b:bean class=\"org.springframework.security.integration.multiannotation.MultiAnnotationServiceImpl\"/>\n\t<b:bean class=\"org.springframework.security.integration.multiannotation.PreAuthorizeServiceImpl\"/>\n\t<b:bean class=\"org.springframework.security.integration.multiannotation.SecuredServiceImpl\"/>\n\n\t<authentication-manager>\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"bob\" password=\"bobspassword\" authorities=\"ROLE_A,ROLE_B\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n</b:beans>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/protect-pointcut-performance-app-context.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:sec=\"http://www.springframework.org/schema/security\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<sec:global-method-security>\n\t\t<sec:protect-pointcut expression=\"execution(* org.springframework.security.core.session.SessionRegistry.refreshLastRequest(..))\" access=\"ROLE_ADMIN\" />\n\t\t<sec:protect-pointcut expression=\"execution(* org.springframework.security.core.session.SessionRegistry.registerNewSession(..))\" access=\"ROLE_ADMIN\" />\n\t\t<sec:protect-pointcut expression=\"execution(* org.springframework.security.core.session.SessionRegistry.removeSessionInformation(..))\" access=\"ROLE_ADMIN\" />\n\t\t<sec:protect-pointcut expression=\"execution(* org.springframework.security.core.session.SessionRegistry.get*(..))\" access=\"ROLE_ADMIN\" />\n\t</sec:global-method-security>\n\n\t<bean id=\"sessionRegistry\" class=\"org.springframework.security.core.session.SessionRegistryImpl\" />\n\n\t<bean id=\"sessionRegistryPrototype\" class=\"org.springframework.security.core.session.SessionRegistryImpl\" scope=\"prototype\"/>\n\n\t<sec:authentication-manager alias=\"authenticationManager\">\n\t\t<sec:authentication-provider>\n\t\t\t<sec:user-service id=\"userService\">\n\t\t\t\t<sec:user name=\"notused\" password=\"notused\" authorities=\"ROLE_0,ROLE_1\"/>\n\t\t\t</sec:user-service>\n\t\t</sec:authentication-provider>\n\t</sec:authentication-manager>\n\n</beans>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/python-method-access-app-context.xml",
    "content": "<b:beans xmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:b=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\n\txmlns:security=\"http://www.springframework.org/schema/security\"\n\txsi:schemaLocation=\"\n\t\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\thttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd\n\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<global-method-security pre-post-annotations=\"enabled\">\n\t\t<pre-post-annotation-handling>\n\t\t\t<invocation-attribute-factory ref=\"attributeFactory\"/>\n\t\t\t<pre-invocation-advice ref=\"preAdvice\"/>\n\t\t\t<post-invocation-advice ref=\"postAdvice\"/>\n\t\t</pre-post-annotation-handling>\n\t</global-method-security>\n\n\t<b:bean id=\"attributeFactory\" class=\"org.springframework.security.integration.python.PythonInterpreterPrePostInvocationAttributeFactory\"/>\n\t<b:bean id=\"preAdvice\" class=\"org.springframework.security.integration.python.PythonInterpreterPreInvocationAdvice\"/>\n\t<b:bean id=\"postAdvice\" class=\"org.springframework.security.integration.python.PythonInterpreterPostInvocationAdvice\"/>\n\n\t<b:bean id=\"service\" class=\"org.springframework.security.integration.python.TestServiceImpl\"/>\n\n\t<authentication-manager>\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t\t<user name=\"bob\" password=\"{noop}bobspassword\" authorities=\"ROLE_A,ROLE_B\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n</b:beans>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/sec-933-app-context.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\n\txmlns:security=\"http://www.springframework.org/schema/security\"\n\txsi:schemaLocation=\"\n\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\thttp://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-3.0.xsd\n\thttp://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd\n\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\n\t<bean id=\"userRepository\" class=\"org.springframework.security.integration.StubUserRepository\"/>\n\n\t<security:authentication-manager>\n\t\t<security:authentication-provider\n\t\tuser-service-ref=\"userDetailsService\" />\n\t</security:authentication-manager>\n\n\t<bean id=\"userDetailsService\" class=\"org.springframework.security.integration.UserDetailsServiceImpl\">\n\t\t<property name=\"userRepository\" ref=\"userRepository\"/>\n\t</bean>\n\n\t<security:global-method-security>\n\t\t<security:protect-pointcut\n\t\t\texpression=\"execution(* org.springframework.security.integration.*Repository+.*(..))\"\n\t\t\taccess=\"ROLE_LOGGEDIN\" />\n\t</security:global-method-security>\n\n\t<aop:aspectj-autoproxy/>\n\n</beans>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/sec-936-app-context.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns:util=\"http://www.springframework.org/schema/util\"\n\txmlns:security=\"http://www.springframework.org/schema/security\"\n\txsi:schemaLocation=\"\n\thttp://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\thttp://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-3.0.xsd\n\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<security:authentication-manager alias=\"authenticationManager\">\n\t\t<security:authentication-provider>\n\t\t\t<security:user-service>\n\t\t\t\t<security:user name=\"bob\" password=\"{noop}bobspassword\" authorities=\"ROLE_A,ROLE_B\"/>\n\t\t\t</security:user-service>\n\t\t</security:authentication-provider>\n\t</security:authentication-manager>\n\n\t<bean id=\"accessDecisionManager\" class=\"org.springframework.security.access.vote.AffirmativeBased\">\n\t\t<constructor-arg>\n\t\t\t<util:list>\n\t\t\t\t<bean class=\"org.springframework.security.access.vote.RoleVoter\" />\n\t\t\t\t<bean class=\"org.springframework.security.access.vote.AuthenticatedVoter\" />\n\t\t\t</util:list>\n\t\t</constructor-arg>\n\t\t<property name=\"allowIfAllAbstainDecisions\" value=\"false\"/>\n\t</bean>\n\n\t<bean id=\"securityInterceptor\" class=\"org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor\">\n\t\t<property name=\"validateConfigAttributes\" value=\"true\"/>\n\t\t<property name=\"rejectPublicInvocations\" value=\"true\"/>\n\t\t<property name=\"authenticationManager\" ref=\"authenticationManager\"/>\n\t\t<property name=\"accessDecisionManager\" ref=\"accessDecisionManager\"/>\n\t\t<property name=\"securityMetadataSource\">\n\t\t\t<security:method-security-metadata-source>\n\t\t\t\t<security:protect method=\"org.springframework.security.core.session.SessionRegistry.get*\" access=\"ROLE_C\" />\n\t\t\t</security:method-security-metadata-source>\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"httpRemoteService\" class=\"org.springframework.aop.framework.ProxyFactoryBean\">\n\t\t<property name=\"proxyInterfaces\" value=\"org.springframework.security.core.session.SessionRegistry\"/>\n\t\t<property name=\"interceptorNames\">\n\t\t\t<list>\n\t\t\t\t<value>securityInterceptor</value>\n\t\t\t\t<value>httpInvokerClientInterceptor</value>\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"httpInvokerClientInterceptor\" class=\"org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor\">\n\t\t<property name=\"serviceUrl\" value=\"https://somehost/someUrl\"/>\n\t</bean>\n\n</beans>\n"
  },
  {
    "path": "itest/context/src/integration-test/resources/someMethod.py",
    "content": "print authentication.name;\n\nfor authority in authentication.authorities:\n    print authority\n\nprint \"Granting access\"\n\nallow = 1\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/UserDetailsServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.transaction.annotation.Transactional;\n\npublic class UserDetailsServiceImpl implements UserDetailsService {\n\n\t@SuppressWarnings({ \"unused\", \"FieldCanBeLocal\" })\n\tprivate UserRepository userRepository;\n\n\t@Override\n\t@Transactional(readOnly = true)\n\tpublic UserDetails loadUserByUsername(String username) {\n\t\treturn null;\n\t}\n\n\tpublic void setUserRepository(UserRepository userRepository) {\n\t\tthis.userRepository = userRepository;\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/UserRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\npublic interface UserRepository {\n\n\tvoid doSomething();\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/multiannotation/MultiAnnotationService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.multiannotation;\n\nimport org.springframework.security.access.annotation.Secured;\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * Allows testing mixing of different annotation types\n *\n * @author Luke Taylor\n */\npublic interface MultiAnnotationService {\n\n\t@PreAuthorize(\"denyAll\")\n\tvoid preAuthorizeDenyAllMethod();\n\n\t@PreAuthorize(\"hasRole('ROLE_A')\")\n\tvoid preAuthorizeHasRoleAMethod();\n\n\t@Secured(\"IS_AUTHENTICATED_ANONYMOUSLY\")\n\tvoid securedAnonymousMethod();\n\n\t@Secured(\"ROLE_A\")\n\tvoid securedRoleAMethod();\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/multiannotation/MultiAnnotationServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.multiannotation;\n\npublic class MultiAnnotationServiceImpl implements MultiAnnotationService {\n\n\t@Override\n\tpublic void preAuthorizeDenyAllMethod() {\n\t}\n\n\t@Override\n\tpublic void preAuthorizeHasRoleAMethod() {\n\t}\n\n\t@Override\n\tpublic void securedAnonymousMethod() {\n\t}\n\n\t@Override\n\tpublic void securedRoleAMethod() {\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/multiannotation/PreAuthorizeService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.multiannotation;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\n/**\n * @author Luke Taylor\n */\npublic interface PreAuthorizeService {\n\n\t@PreAuthorize(\"hasRole('ROLE_A')\")\n\tvoid preAuthorizedMethod();\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/multiannotation/PreAuthorizeServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.multiannotation;\n\n/**\n * @author Luke Taylor\n */\npublic class PreAuthorizeServiceImpl implements PreAuthorizeService {\n\n\t@Override\n\tpublic void preAuthorizedMethod() {\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/multiannotation/SecuredService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.multiannotation;\n\nimport org.springframework.security.access.annotation.Secured;\n\n/**\n * @author Luke Taylor\n */\npublic interface SecuredService {\n\n\t@Secured(\"ROLE_A\")\n\tvoid securedMethod();\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/multiannotation/SecuredServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.multiannotation;\n\n/**\n * @author Luke Taylor\n */\npublic class SecuredServiceImpl implements SecuredService {\n\n\t@Override\n\tpublic void securedMethod() {\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/python/PythonInterpreterPostInvocationAdvice.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.python;\n\nimport org.aopalliance.intercept.MethodInvocation;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.access.prepost.PostInvocationAttribute;\nimport org.springframework.security.access.prepost.PostInvocationAuthorizationAdvice;\nimport org.springframework.security.core.Authentication;\n\npublic class PythonInterpreterPostInvocationAdvice implements PostInvocationAuthorizationAdvice {\n\n\t@Override\n\tpublic Object after(Authentication authentication, MethodInvocation mi, PostInvocationAttribute pia,\n\t\t\tObject returnedObject) throws AccessDeniedException {\n\t\treturn returnedObject;\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/python/PythonInterpreterPreInvocationAdvice.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.python;\n\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.python.core.Py;\nimport org.python.core.PyObject;\nimport org.python.util.PythonInterpreter;\n\nimport org.springframework.core.ParameterNameDiscoverer;\nimport org.springframework.core.StandardReflectionParameterNameDiscoverer;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.security.access.prepost.PreInvocationAttribute;\nimport org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.ClassUtils;\n\npublic class PythonInterpreterPreInvocationAdvice implements PreInvocationAuthorizationAdvice {\n\n\tprivate final ParameterNameDiscoverer parameterNameDiscoverer = new StandardReflectionParameterNameDiscoverer();\n\n\t@Override\n\tpublic boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute preAttr) {\n\t\tPythonInterpreterPreInvocationAttribute pythonAttr = (PythonInterpreterPreInvocationAttribute) preAttr;\n\t\tString script = pythonAttr.getScript();\n\n\t\tPythonInterpreter python = new PythonInterpreter();\n\t\tpython.set(\"authentication\", authentication);\n\t\tpython.set(\"args\", createArgumentMap(mi));\n\t\tpython.set(\"method\", mi.getMethod().getName());\n\t\tResource scriptResource = new PathMatchingResourcePatternResolver().getResource(script);\n\n\t\ttry {\n\t\t\tpython.execfile(scriptResource.getInputStream());\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Couldn't run python script, \" + script, ex);\n\t\t}\n\n\t\tPyObject allowed = python.get(\"allow\");\n\n\t\tif (allowed == null) {\n\t\t\tthrow new IllegalStateException(\"Python script did not set the permit flag\");\n\t\t}\n\n\t\treturn Py.tojava(allowed, Boolean.class);\n\t}\n\n\tprivate Map<String, Object> createArgumentMap(MethodInvocation mi) {\n\t\tObject[] args = mi.getArguments();\n\t\tObject targetObject = mi.getThis();\n\t\tMethod method = ClassUtils.getMostSpecificMethod(mi.getMethod(), targetObject.getClass());\n\t\tString[] paramNames = this.parameterNameDiscoverer.getParameterNames(method);\n\n\t\tMap<String, Object> argMap = new HashMap<>();\n\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\targMap.put(paramNames[i], args[i]);\n\t\t}\n\n\t\treturn argMap;\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/python/PythonInterpreterPreInvocationAttribute.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.python;\n\nimport org.springframework.security.access.prepost.PreInvocationAttribute;\n\npublic class PythonInterpreterPreInvocationAttribute implements PreInvocationAttribute {\n\n\tprivate final String script;\n\n\tPythonInterpreterPreInvocationAttribute(String script) {\n\t\tthis.script = script;\n\t}\n\n\t@Override\n\tpublic String getAttribute() {\n\t\treturn null;\n\t}\n\n\tpublic String getScript() {\n\t\treturn this.script;\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/python/PythonInterpreterPrePostInvocationAttributeFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.python;\n\nimport org.python.util.PythonInterpreter;\n\nimport org.springframework.security.access.prepost.PostInvocationAttribute;\nimport org.springframework.security.access.prepost.PreInvocationAttribute;\nimport org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;\n\npublic class PythonInterpreterPrePostInvocationAttributeFactory implements PrePostInvocationAttributeFactory {\n\n\tpublic PythonInterpreterPrePostInvocationAttributeFactory() {\n\t\tPythonInterpreter.initialize(System.getProperties(), null, new String[] {});\n\t}\n\n\t@Override\n\tpublic PreInvocationAttribute createPreInvocationAttribute(String preFilterAttribute, String filterObject,\n\t\t\tString preAuthorizeAttribute) {\n\t\treturn new PythonInterpreterPreInvocationAttribute(preAuthorizeAttribute);\n\t}\n\n\t@Override\n\tpublic PostInvocationAttribute createPostInvocationAttribute(String postFilterAttribute,\n\t\t\tString postAuthorizeAttribute) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/python/TestService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.python;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\n\npublic interface TestService {\n\n\t@PreAuthorize(\"someMethod.py\")\n\tvoid someMethod();\n\n}\n"
  },
  {
    "path": "itest/context/src/main/java/org/springframework/security/integration/python/TestServiceImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration.python;\n\npublic class TestServiceImpl implements TestService {\n\n\t@Override\n\tpublic void someMethod() {\n\t\tSystem.out.print(\"Invoked someMethod()\");\n\t}\n\n}\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-mode-unboundid/spring-security-itest-ldap-embedded-mode-unboundid.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-test'\n\ndependencies {\n\timplementation platform(project(\":spring-security-dependencies\"))\n    implementation project(':spring-security-core')\n    implementation 'org.springframework:spring-beans'\n    implementation 'org.springframework:spring-context'\n    implementation 'org.springframework:spring-core'\n    implementation 'org.springframework:spring-tx'\n    implementation project(':spring-security-config')\n    implementation project(':spring-security-ldap')\n\n    testImplementation \"com.unboundid:unboundid-ldapsdk\"\n    \ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-mode-unboundid/src/integration-test/java/org/springframework/security/LdapServerBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Eddú Meléndez\n */\npublic class LdapServerBeanDefinitionParserTests {\n\n\tprivate ClassPathXmlApplicationContext context;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.context = new ClassPathXmlApplicationContext(\"applicationContext-security.xml\");\n\t}\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t\tthis.context = null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void apacheDirectoryServerIsStartedByDefault() {\n\t\tString[] beanNames = this.context.getBeanNamesForType(UnboundIdContainer.class);\n\t\tassertThat(beanNames).hasSize(1);\n\t\tassertThat(beanNames[0]).isEqualTo(BeanIds.EMBEDDED_UNBOUNDID);\n\t}\n\n}\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-mode-unboundid/src/integration-test/resources/applicationContext-security.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:s=\"http://www.springframework.org/schema/security\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<s:ldap-server mode=\"unboundid\" ldif=\"classpath:users.ldif\" port=\"0\"/>\n\n</beans>\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-mode-unboundid/src/integration-test/resources/users.ldif",
    "content": "dn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: uid=rod,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Rod Johnson\nsn: Johnson\nuid: rod\nuserPassword: koala\n\ndn: uid=dianne,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Dianne Emu\nsn: Emu\nuid: dianne\nuserPassword: emu\n\ndn: uid=scott,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Scott\nsn: Wombat\nuid: scott\nuserPassword: wombat\n\ndn: uid=bcrypt,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: BCrypt user\nsn: BCrypt\nuid: bcrypt\nuserPassword: $2a$10$FBAKClV1zBIOOC9XMXf3AO8RoGXYVYsfvUdoLxGkd/BnXEn4tqT3u\n\ndn: cn=user,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: user\nmember: uid=rod,ou=people,dc=springframework,dc=org\nmember: uid=dianne,ou=people,dc=springframework,dc=org\nmember: uid=scott,ou=people,dc=springframework,dc=org\n\ndn: cn=teller,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: teller\nmember: uid=rod,ou=people,dc=springframework,dc=org\nmember: uid=dianne,ou=people,dc=springframework,dc=org\n\ndn: cn=supervisor,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: supervisor\nmember: uid=rod,ou=people,dc=springframework,dc=org\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-none/spring-security-itest-ldap-embedded-none.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-test'\n\ndependencies {\n\timplementation platform(project(\":spring-security-dependencies\"))\n    implementation project(':spring-security-core')\n    implementation 'org.springframework:spring-beans'\n    implementation 'org.springframework:spring-context'\n    implementation 'org.springframework:spring-core'\n    implementation 'org.springframework:spring-tx'\n    implementation project(':spring-security-config')\n    implementation project(':spring-security-ldap')\n    \n    \ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-none/src/integration-test/java/org/springframework/security/LdapServerBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.BeanDefinitionStoreException;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Eddú Meléndez\n */\npublic class LdapServerBeanDefinitionParserTests {\n\n\tprivate ClassPathXmlApplicationContext context;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t\tthis.context = null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void apacheDirectoryServerIsStartedByDefault() {\n\t\tassertThatExceptionOfType(BeanDefinitionStoreException.class)\n\t\t\t.isThrownBy(() -> this.context = new ClassPathXmlApplicationContext(\"applicationContext-security.xml\"))\n\t\t\t.havingRootCause()\n\t\t\t.withMessageContaining(\"Embedded LDAP server is not provided\");\n\t}\n\n}\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-none/src/integration-test/resources/applicationContext-security.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:s=\"http://www.springframework.org/schema/security\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<s:ldap-server ldif=\"classpath:users.ldif\" port=\"0\"/>\n\n</beans>\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-none/src/integration-test/resources/users.ldif",
    "content": "dn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: uid=rod,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Rod Johnson\nsn: Johnson\nuid: rod\nuserPassword: koala\n\ndn: uid=dianne,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Dianne Emu\nsn: Emu\nuid: dianne\nuserPassword: emu\n\ndn: uid=scott,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Scott\nsn: Wombat\nuid: scott\nuserPassword: wombat\n\ndn: cn=user,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: user\nmember: uid=rod,ou=people,dc=springframework,dc=org\nmember: uid=dianne,ou=people,dc=springframework,dc=org\nmember: uid=scott,ou=people,dc=springframework,dc=org\n\ndn: cn=teller,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: teller\nmember: uid=rod,ou=people,dc=springframework,dc=org\nmember: uid=dianne,ou=people,dc=springframework,dc=org\n\ndn: cn=supervisor,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: supervisor\nmember: uid=rod,ou=people,dc=springframework,dc=org\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-unboundid-default/spring-security-itest-ldap-embedded-unboundid-default.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-test'\n\ndependencies {\n\timplementation platform(project(\":spring-security-dependencies\"))\n    implementation project(':spring-security-core')\n    implementation 'org.springframework:spring-beans'\n    implementation 'org.springframework:spring-context'\n    implementation 'org.springframework:spring-core'\n    implementation 'org.springframework:spring-tx'\n    implementation project(':spring-security-config')\n    implementation project(':spring-security-ldap')\n\n    testImplementation \"com.unboundid:unboundid-ldapsdk\"\n    \ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-unboundid-default/src/integration-test/java/org/springframework/security/LdapServerBeanDefinitionParserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Eddú Meléndez\n */\npublic class LdapServerBeanDefinitionParserTests {\n\n\tprivate ClassPathXmlApplicationContext context;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.context = new ClassPathXmlApplicationContext(\"applicationContext-security.xml\");\n\t}\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t\tthis.context = null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void apacheDirectoryServerIsStartedByDefault() {\n\t\tString[] beanNames = this.context.getBeanNamesForType(UnboundIdContainer.class);\n\t\tassertThat(beanNames).hasSize(1);\n\t\tassertThat(beanNames[0]).isEqualTo(BeanIds.EMBEDDED_UNBOUNDID);\n\t}\n\n}\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-unboundid-default/src/integration-test/resources/applicationContext-security.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:s=\"http://www.springframework.org/schema/security\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<s:ldap-server ldif=\"classpath:users.ldif\" port=\"0\"/>\n\n</beans>\n"
  },
  {
    "path": "itest/ldap/embedded-ldap-unboundid-default/src/integration-test/resources/users.ldif",
    "content": "dn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: uid=rod,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Rod Johnson\nsn: Johnson\nuid: rod\nuserPassword: koala\n\ndn: uid=dianne,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Dianne Emu\nsn: Emu\nuid: dianne\nuserPassword: emu\n\ndn: uid=scott,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Scott\nsn: Wombat\nuid: scott\nuserPassword: wombat\n\ndn: cn=user,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: user\nmember: uid=rod,ou=people,dc=springframework,dc=org\nmember: uid=dianne,ou=people,dc=springframework,dc=org\nmember: uid=scott,ou=people,dc=springframework,dc=org\n\ndn: cn=teller,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: teller\nmember: uid=rod,ou=people,dc=springframework,dc=org\nmember: uid=dianne,ou=people,dc=springframework,dc=org\n\ndn: cn=supervisor,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: supervisor\nmember: uid=rod,ou=people,dc=springframework,dc=org\n"
  },
  {
    "path": "itest/misc/src/integration-test/java/org/springframework/security/concurrent/SessionRegistryImplMTTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.concurrent;\n\nimport junit.framework.TestCase;\n\nimport java.util.Set;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Random;\n\n/**\n * Tests concurrency access to SessionRegistryImpl.\n *\n * @author Luke Taylor\n */\npublic class SessionRegistryImplMTTests extends TestCase {\n\tprivate static final Random rnd = new Random();\n\tprivate static boolean errorOccurred;\n\n\tprotected void setUp() throws Exception {\n\t\terrorOccurred = false;\n\t}\n\n\t/**\n\t * Reproduces the NPE mentioned in SEC-484 where a sessionId is removed from\n\t * the set of sessions before it is removed from the list of sessions for a principal.\n\t * getAllSessions(principal, false) then finds the sessionId in the principal's session list\n\t * but reads null for the SessionInformation with the same Id.\n\t * Note that this is not guaranteed to produce the error but is a good testing point. Increasing the number\n\t * of sessions makes a failure more likely, but slows the test considerably.\n\t * Inserting temporary sleep statements in SessionRegistryClassImpl will also help.\n\t */\n\tpublic void testConcurrencyOfReadAndRemoveIsSafe() {\n\t\tObject principal = \"Joe Principal\";\n\t\tSessionRegistryImpl sessionregistry = new SessionRegistryImpl();\n\t\tSet sessions = Collections.synchronizedSet(new HashSet());\n\t\t// Register some sessions\n\t\tfor (int i = 0; i < 50; i++) {\n\t\t\tString sessionId = Integer.toString(i);\n\t\t\tsessions.add(sessionId);\n\t\t\tsessionregistry.registerNewSession(sessionId, principal);\n\t\t}\n\n\t\t// Pile of readers to hammer the getAllSessions method.\n\t\tfor (int i=0; i < 10; i++) {\n\t\t\tThread reader = new Thread(new SessionRegistryReader(principal, sessionregistry));\n\t\t\treader.start();\n\t\t}\n\n\t\tThread remover = new Thread(new SessionRemover(\"remover\", sessionregistry, sessions));\n\n\t\tremover.start();\n\n\t\twhile(remover.isAlive()) {\n\t\t\tpause(250);\n\t\t}\n\n\t\tassertThat(errorOccurred).as(\"Thread errors detected; review log output for details\").isFalse();\n\t}\n\n\tpublic void testConcurrentRemovalIsSafe() {\n\t\tObject principal = \"Some principal object\";\n\t\tSessionRegistryImpl sessionregistry = new SessionRegistryImpl();\n\t\t// The session list (effectivelly the containers sessions).\n\t\tSet sessions = Collections.synchronizedSet(new HashSet());\n\t\tThread registerer = new Thread(new SessionRegisterer(principal, sessionregistry, 100, sessions));\n\n\t\tregisterer.start();\n\n\t\tint nRemovers = 4;\n\n\t\tSessionRemover[] removers = new SessionRemover[nRemovers];\n\t\tThread[] removerThreads = new Thread[nRemovers];\n\n\t\tfor (int i = 0; i < removers.length; i++) {\n\t\t\tremovers[i] = new SessionRemover(\"remover\" + i, sessionregistry, sessions);\n\t\t\tremoverThreads[i] = new Thread(removers[i], \"remover\" + i);\n\t\t\tremoverThreads[i].start();\n\t\t}\n\n\t\twhile (stillRunning(removerThreads)) {\n\t\t\tpause(500);\n\t\t}\n\t}\n\n\tprivate boolean stillRunning(Thread[] threads) {\n\t\tfor (int i = 0; i < threads.length; i++) {\n\t\t\tif (threads[i].isAlive()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate static class SessionRegisterer implements Runnable {\n\t\tprivate SessionRegistry sessionregistry;\n\t\tprivate int nIterations;\n\t\tprivate Set sessionList;\n\t\tprivate Object principal;\n\n\t\tpublic SessionRegisterer(Object principal, SessionRegistry sessionregistry, int nIterations, Set sessionList) {\n\t\t\tthis.sessionregistry = sessionregistry;\n\t\t\tthis.nIterations = nIterations;\n\t\t\tthis.sessionList = sessionList;\n\t\t\tthis.principal = principal;\n\t\t}\n\n\t\tpublic void run() {\n\t\t\tfor (int i=0; i < nIterations && !errorOccurred; i++) {\n\t\t\t\tString sessionId = Integer.toString(i);\n\t\t\t\tsessionList.add(sessionId);\n\t\t\t\ttry {\n\t\t\t\t\tsessionregistry.registerNewSession(sessionId,principal);\n\t\t\t\t\tpause(20);\n\t\t\t\t\tThread.yield();\n\t\t\t\t} catch(Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\terrorOccurred = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class SessionRegistryReader implements Runnable {\n\t\tprivate SessionRegistry sessionRegistry;\n\t\tprivate Object principal;\n\n\t\tpublic SessionRegistryReader(Object principal, SessionRegistry sessionregistry) {\n\t\t\tthis.sessionRegistry = sessionregistry;\n\t\t\tthis.principal = principal;\n\t\t}\n\n\t\tpublic void run() {\n\t\t\twhile (!errorOccurred) {\n\t\t\t\ttry {\n\t\t\t\t\tsessionRegistry.getAllSessions(principal, false);\n\t\t\t\t\tsessionRegistry.getAllPrincipals();\n\t\t\t\t\tsessionRegistry.getAllSessions(principal, true);\n\t\t\t\t\tThread.yield();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\terrorOccurred = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class SessionRemover implements Runnable {\n\t\tprivate SessionRegistry sessionregistry;\n\t\tprivate Set sessionList;\n\t\tprivate String name;\n\n\t\tpublic SessionRemover(String name, SessionRegistry sessionregistry, Set sessionList) {\n\t\t\tthis.name = name;\n\t\t\tthis.sessionregistry = sessionregistry;\n\t\t\tthis.sessionList = sessionList;\n\t\t}\n\n\t\tpublic void run() {\n\t\t\tboolean finished = false;\n\n\t\t\twhile (!finished && !errorOccurred) {\n\t\t\t\tif (sessionList.isEmpty()) {\n\t\t\t\t\tfinished = true;\n\t\t\t\t\t// List of sessions appears to be empty but give it a chance to fill up again\n\t\t\t\t\tSystem.out.println(name + \": Session list empty. Waiting.\");\n\t\t\t\t\tpause(500);\n\t\t\t\t}\n\n\t\t\t\tObject[] sessions = sessionList.toArray();\n\n\t\t\t\tif (sessions.length > 0) {\n\t\t\t\t\tfinished = false;\n\t\t\t\t\tString sessionId = (String) sessions[0];\n//                    System.out.println(name + \": removing \" + sessionId);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsessionregistry.removeSessionInformation(sessionId);\n\n\t\t\t\t\t\tpause(rnd.nextInt(100));\n\n\t\t\t\t\t\tsessionList.remove(sessionId);\n\t\t\t\t\t\tThread.yield();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\terrorOccurred = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void pause(int length) {\n\t\ttry {\n\t\t\tThread.sleep(length);\n\t\t} catch (InterruptedException ignore) {}\n\t}\n}\n"
  },
  {
    "path": "itest/misc/src/integration-test/java/org/springframework/security/context/SecurityContextHolderMTTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.context;\n\nimport java.util.Random;\n\nimport junit.framework.ComparisonFailure;\nimport junit.framework.TestCase;\n\n\n\nimport org.springframework.security.providers.UsernamePasswordAuthenticationToken;\n\n/**\n * Multi-threaded tests for SecurityContextHolder\n *\n * @author Ben Alex\n * @Author Luke Taylor\n */\npublic class SecurityContextHolderMTTests extends TestCase{\n\tprivate int errors = 0;\n\n\tprivate static final int NUM_OPS = 25;\n\tprivate static final int NUM_THREADS = 25;\n\n\tpublic final void setUp() throws Exception {\n\t\tSecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);\n\t}\n\n\tpublic void testSynchronizationCustomStrategyLoading() {\n\t\tSecurityContextHolder.setStrategyName(InheritableThreadLocalSecurityContextHolderStrategy.class.getName());\n\t\tassertThat(new SecurityContextHolder().toString().isTrue()\n\t\t\t\t\t\t\t\t\t\t\t.lastIndexOf(\"SecurityContextHolder[strategy='org.springframework.security.context.InheritableThreadLocalSecurityContextHolderStrategy'\") != -1);\n\t\tloadStartAndWaitForThreads(true, \"Main_\", NUM_THREADS, false, true);\n\t\tassertThat(errors).as(\"Thread errors detected; review log output for details\").isZero();\n\t}\n\n\tpublic void testSynchronizationGlobal() throws Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tSecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_GLOBAL);\n\t\tloadStartAndWaitForThreads(true, \"Main_\", NUM_THREADS, true, false);\n\t\tassertThat(errors).as(\"Thread errors detected; review log output for details\").isZero();\n\t}\n\n\tpublic void testSynchronizationInheritableThreadLocal()\n\t\tthrows Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tSecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);\n\t\tloadStartAndWaitForThreads(true, \"Main_\", NUM_THREADS, false, true);\n\t\tassertThat(errors).as(\"Thread errors detected; review log output for details\").isZero();\n\t}\n\n\tpublic void testSynchronizationThreadLocal() throws Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tSecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL);\n\t\tloadStartAndWaitForThreads(true, \"Main_\", NUM_THREADS, false, false);\n\t\tassertThat(errors).as(\"Thread errors detected; review log output for details\").isZero();\n\t}\n\n\tprivate void startAndRun(Thread[] threads) {\n\t\t// Start them up\n\t\tfor (int i = 0; i < threads.length; i++) {\n\t\t\tthreads[i].start();\n\t\t}\n\n\t\t// Wait for them to finish\n\t\twhile (stillRunning(threads)) {\n\t\t\ttry {\n\t\t\t\tThread.sleep(250);\n\t\t\t} catch (InterruptedException ignore) {}\n\t\t}\n\t}\n\n\tprivate boolean stillRunning(Thread[] threads) {\n\t\tfor (int i = 0; i < threads.length; i++) {\n\t\t\tif (threads[i].isAlive()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate void loadStartAndWaitForThreads(boolean topLevelThread, String prefix, int createThreads,\n\t\t\tboolean expectAllThreadsToUseIdenticalAuthentication, boolean expectChildrenToShareAuthenticationWithParent) {\n\t\tThread[] threads = new Thread[createThreads];\n\t\terrors = 0;\n\n\t\tif (topLevelThread) {\n\t\t\t// PARENT (TOP-LEVEL) THREAD CREATION\n\t\t\tif (expectChildrenToShareAuthenticationWithParent) {\n\t\t\t\t// An InheritableThreadLocal\n\t\t\t\tfor (int i = 0; i < threads.length; i++) {\n\t\t\t\t\tif ((i % 2) == 0) {\n\t\t\t\t\t\t// Don't inject auth into current thread; neither current thread or child will have authentication\n\t\t\t\t\t\tthreads[i] = makeThread(prefix + \"Unauth_Parent_\" + i, true, false, false, true, null);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Inject auth into current thread, but not child; current thread will have auth, child will also have auth\n\t\t\t\t\t\tthreads[i] = makeThread(prefix + \"Auth_Parent_\" + i, true, true, false, true,\n\t\t\t\t\t\t\t\tprefix + \"Auth_Parent_\" + i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (expectAllThreadsToUseIdenticalAuthentication) {\n\t\t\t\t// A global\n\t\t\t\tSecurityContextHolder.getContext()\n\t\t\t\t\t\t\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.unauthenticated(\"GLOBAL_USERNAME\",\n\t\t\t\t\t\t\"pass\"));\n\n\t\t\t\tfor (int i = 0; i < threads.length; i++) {\n\t\t\t\t\tif ((i % 2) == 0) {\n\t\t\t\t\t\t// Don't inject auth into current thread;both current thread and child will have same authentication\n\t\t\t\t\t\tthreads[i] = makeThread(prefix + \"Unauth_Parent_\" + i, true, false, true, true,\n\t\t\t\t\t\t\t\t\"GLOBAL_USERNAME\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Inject auth into current thread; current thread will have auth, child will also have auth\n\t\t\t\t\t\tthreads[i] = makeThread(prefix + \"Auth_Parent_\" + i, true, true, true, true, \"GLOBAL_USERNAME\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// A standard ThreadLocal\n\t\t\t\tfor (int i = 0; i < threads.length; i++) {\n\t\t\t\t\tif ((i % 2) == 0) {\n\t\t\t\t\t\t// Don't inject auth into current thread; neither current thread or child will have authentication\n\t\t\t\t\t\tthreads[i] = makeThread(prefix + \"Unauth_Parent_\" + i, true, false, false, false, null);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Inject auth into current thread, but not child; current thread will have auth, child will not have auth\n\t\t\t\t\t\tthreads[i] = makeThread(prefix + \"Auth_Parent_\" + i, true, true, false, false,\n\t\t\t\t\t\t\t\tprefix + \"Auth_Parent_\" + i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// CHILD THREAD CREATION\n\t\t\tif (expectChildrenToShareAuthenticationWithParent || expectAllThreadsToUseIdenticalAuthentication) {\n\t\t\t\t// The children being created are all expected to have security (ie an InheritableThreadLocal/global AND auth was injected into parent)\n\t\t\t\tfor (int i = 0; i < threads.length; i++) {\n\t\t\t\t\tString expectedUsername = prefix;\n\n\t\t\t\t\tif (expectAllThreadsToUseIdenticalAuthentication) {\n\t\t\t\t\t\texpectedUsername = \"GLOBAL_USERNAME\";\n\t\t\t\t\t}\n\n\t\t\t\t\t// Don't inject auth into current thread; the current thread will obtain auth from its parent\n\t\t\t\t\t// NB: As topLevelThread = true, no further child threads will be created\n\t\t\t\t\tthreads[i] = makeThread(prefix + \"->child->Inherited_Auth_Child_\" + i, false, false,\n\t\t\t\t\t\t\texpectAllThreadsToUseIdenticalAuthentication, false, expectedUsername);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// The children being created are NOT expected to have security (ie not an InheritableThreadLocal OR auth was not injected into parent)\n\t\t\t\tfor (int i = 0; i < threads.length; i++) {\n\t\t\t\t\t// Don't inject auth into current thread; neither current thread or child will have authentication\n\t\t\t\t\t// NB: As topLevelThread = true, no further child threads will be created\n\t\t\t\t\tthreads[i] = makeThread(prefix + \"->child->Unauth_Child_\" + i, false, false, false, false, null);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Start and execute the threads\n\t\tstartAndRun(threads);\n\t}\n\n\tprivate Thread makeThread(final String threadIdentifier, final boolean topLevelThread,\n\t\tfinal boolean injectAuthIntoCurrentThread, final boolean expectAllThreadsToUseIdenticalAuthentication,\n\t\tfinal boolean expectChildrenToShareAuthenticationWithParent, final String expectedUsername) {\n\t\tfinal Random rnd = new Random();\n\n\t\tThread t = new Thread(new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\t\tif (injectAuthIntoCurrentThread) {\n\t\t\t\t\t\t// Set authentication in this thread\n\t\t\t\t\t\tSecurityContextHolder.getContext().setAuthentication(UsernamePasswordAuthenticationToken.authenticated(\n\t\t\t\t\t\t\t\texpectedUsername, \"pass\"));\n\n\t\t\t\t\t\t//System.out.println(threadIdentifier + \" - set to \" + SecurityContextHolder.getContext().getAuthentication());\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//System.out.println(threadIdentifier + \" - not set (currently \" + SecurityContextHolder.getContext().getAuthentication() + \")\");\n\t\t\t\t\t}\n\n\t\t\t\t\t// Do some operations in current thread, checking authentication is as expected in the current thread (ie another thread doesn't change it)\n\t\t\t\t\tfor (int i = 0; i < NUM_OPS; i++) {\n\t\t\t\t\t\tString currentUsername = (SecurityContextHolder.getContext().getAuthentication() == null)\n\t\t\t\t\t\t\t? null : SecurityContextHolder.getContext().getAuthentication().getName();\n\n\t\t\t\t\t\tif ((i % 7) == 0) {\n\t\t\t\t\t\t\tSystem.out.println(threadIdentifier + \" at \" + i + \" username \" + currentUsername);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tassertEquals(\"Failed on iteration \" + i + \"; Authentication was '\"\n\t\t\t\t\t\t\t\t+ currentUsername + \"' but principal was expected to contain username '\"\n\t\t\t\t\t\t\t\t+ expectedUsername + \"'\", expectedUsername, currentUsername);\n\t\t\t\t\t\t} catch (ComparisonFailure err) {\n\t\t\t\t\t\t\terrors++;\n\t\t\t\t\t\t\tthrow err;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tThread.sleep(rnd.nextInt(250));\n\t\t\t\t\t\t} catch (InterruptedException ignore) {}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Load some children threads, checking the authentication is as expected in the children (ie another thread doesn't change it)\n\t\t\t\t\tif (topLevelThread) {\n\t\t\t\t\t\t// Make four children, but we don't want the children to have any more children (so anti-nature, huh?)\n\t\t\t\t\t\tif (injectAuthIntoCurrentThread && expectChildrenToShareAuthenticationWithParent) {\n\t\t\t\t\t\t\tloadStartAndWaitForThreads(false, threadIdentifier, 4,\n\t\t\t\t\t\t\t\texpectAllThreadsToUseIdenticalAuthentication, true);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tloadStartAndWaitForThreads(false, threadIdentifier, 4,\n\t\t\t\t\t\t\t\texpectAllThreadsToUseIdenticalAuthentication, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, threadIdentifier);\n\n\t\treturn t;\n\t}\n}\n"
  },
  {
    "path": "itest/web/spring-security-itest-web.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-test'\n\ndependencies {\n\timplementation platform(project(\":spring-security-dependencies\"))\n\timplementation project(':spring-security-access')\n\timplementation 'org.springframework:spring-context'\n\timplementation 'org.springframework:spring-web'\n\n\tcompileOnly 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(':spring-security-core')\n\ttestImplementation project(':spring-security-test')\n\ttestImplementation project(':spring-security-web')\n\ttestImplementation 'org.hamcrest:hamcrest'\n\ttestImplementation 'org.springframework:spring-beans'\n\ttestImplementation 'org.springframework:spring-test'\n\ttestImplementation 'org.springframework:spring-webmvc'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestRuntimeOnly project(':spring-security-config')\n\ttestRuntimeOnly project(':spring-security-ldap')\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\nintegrationTest {\n\toptions {\n\t\tjvmArgs = ['-ea', '-Xms128m', '-Xmx500m']\n\t}\n\tmaxParallelForks = 1\n}\n"
  },
  {
    "path": "itest/web/src/integration-test/java/org/springframework/security/integration/AbstractWebServerIntegrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\nimport org.junit.jupiter.api.AfterEach;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.support.XmlWebApplicationContext;\n\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\n\n/**\n * Base class which allows the application to be started with a particular Spring\n * application context. Subclasses override the <tt>getContextConfigLocations</tt> method\n * to return a list of context file names which is passed to the\n * <tt>ContextLoaderListener</tt> when starting up the webapp.\n *\n * @author Luke Taylor\n */\npublic abstract class AbstractWebServerIntegrationTests {\n\n\tprotected ConfigurableApplicationContext context;\n\n\t@AfterEach\n\tpublic void close() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\tprotected final MockMvc createMockMvc(String... configLocations) {\n\t\tif (this.context != null) {\n\t\t\tthrow new IllegalStateException(\"context is already loaded\");\n\t\t}\n\n\t\tXmlWebApplicationContext context = new XmlWebApplicationContext();\n\t\tcontext.setConfigLocations(configLocations);\n\t\tcontext.setServletContext(new MockServletContext());\n\t\tcontext.refresh();\n\t\tthis.context = context;\n\n\t\t// @formatter:off\n\t\treturn MockMvcBuilders.webAppContextSetup(context)\n\t\t\t\t.apply(springSecurity())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "itest/web/src/integration-test/java/org/springframework/security/integration/BasicAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\npublic class BasicAuthenticationTests extends AbstractWebServerIntegrationTests {\n\n\t@Test\n\tpublic void httpBasicWhenAuthenticationRequiredAndNotAuthenticatedThen401() throws Exception {\n\t\tMockMvc mockMvc = createMockMvc(\"classpath:/spring/http-security-basic.xml\",\n\t\t\t\t\"classpath:/spring/in-memory-provider.xml\", \"classpath:/spring/testapp-servlet.xml\");\n\t\t// @formatter:off\n\t\tmockMvc.perform(get(\"/secure/index\"))\n\t\t\t\t.andExpect(status().isUnauthorized());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void httpBasicWhenProvidedThen200() throws Exception {\n\t\tMockMvc mockMvc = createMockMvc(\"classpath:/spring/http-security-basic.xml\",\n\t\t\t\t\"classpath:/spring/in-memory-provider.xml\", \"classpath:/spring/testapp-servlet.xml\");\n\t\t// @formatter:off\n\t\tMockHttpServletRequestBuilder request = get(\"/secure/index\")\n\t\t\t\t.with(httpBasic(\"johnc\", \"johncspassword\"));\n\t\t// @formatter:on\n\t\tmockMvc.perform(request).andExpect(status().isOk());\n\t}\n\n}\n"
  },
  {
    "path": "itest/web/src/integration-test/java/org/springframework/security/integration/ConcurrentSessionManagementTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.integration;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.session.SessionDestroyedEvent;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Luke Taylor\n */\npublic class ConcurrentSessionManagementTests extends AbstractWebServerIntegrationTests {\n\n\t@Test\n\tpublic void maxConcurrentLoginsValueIsRespected() throws Exception {\n\t\tfinal MockHttpSession session1 = new MockHttpSession();\n\t\tfinal MockHttpSession session2 = new MockHttpSession();\n\n\t\tMockMvc mockMvc = createMockMvc(\"classpath:/spring/http-security-concurrency.xml\",\n\t\t\t\t\"classpath:/spring/in-memory-provider.xml\", \"classpath:/spring/testapp-servlet.xml\");\n\n\t\t// @formatter:off\n\t\tmockMvc.perform(get(\"/secure/index\").session(session1))\n\t\t\t\t.andExpect(status().is3xxRedirection());\n\t\t// @formatter:on\n\n\t\tMockHttpServletRequestBuilder login1 = login().session(session1);\n\t\tmockMvc.perform(login1).andExpect(authenticated().withUsername(\"jimi\"));\n\n\t\tMockHttpServletRequestBuilder login2 = login().session(session2);\n\t\t// @formatter:off\n\t\tmockMvc.perform(login2)\n\t\t\t\t.andExpect(redirectedUrl(\"/login.jsp?login_error=true\"));\n\t\t// @formatter:on\n\t\tException exception = (Exception) session2.getAttribute(\"SPRING_SECURITY_LAST_EXCEPTION\");\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getMessage()).contains(\"Maximum sessions of 1 for this principal exceeded\");\n\n\t\t// Now logout to kill first session\n\t\t// @formatter:off\n\t\tmockMvc.perform(post(\"/logout\").with(csrf()))\n\t\t\t\t.andExpect(status().is3xxRedirection())\n\t\t\t\t.andDo((result) -> this.context.publishEvent(new SessionDestroyedEvent(session1) {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic List<SecurityContext> getSecurityContexts() {\n\t\t\t\t\t\treturn Collections.emptyList();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic String getId() {\n\t\t\t\t\t\treturn session1.getId();\n\t\t\t\t\t}\n\t\t\t\t}));\n\t\t// @formatter:on\n\n\t\t// Try second session again\n\t\tlogin2 = login().session(session2);\n\t\t// @formatter:off\n\t\tmockMvc.perform(login2)\n\t\t\t\t.andExpect(authenticated().withUsername(\"jimi\"));\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tmockMvc.perform(get(\"/secure/index\").session(session2))\n\t\t\t\t.andExpect(content().string(containsString(\"A Secure Page\")));\n\t\t// @formatter:on\n\t}\n\n\tprivate MockHttpServletRequestBuilder login() {\n\t\t// @formatter:off\n\t\treturn post(\"/login\")\n\t\t\t\t.param(\"username\", \"jimi\")\n\t\t\t\t.param(\"password\", \"jimispassword\")\n\t\t\t\t.with(csrf());\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "itest/web/src/integration-test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "itest/web/src/integration-test/resources/spring/http-security-basic.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<beans:beans xmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<http use-expressions=\"false\" use-authorization-manager=\"false\">\n\t\t<intercept-url pattern=\"/**\" access=\"ROLE_DEVELOPER,ROLE_USER\" />\n\t\t<http-basic />\n\t</http>\n\n</beans:beans>\n"
  },
  {
    "path": "itest/web/src/integration-test/resources/spring/http-security-concurrency.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<beans:beans xmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<debug />\n\n\t<!--\n\t   Http App Context to test form login, remember-me and concurrent session control.\n\t   Needs to be supplemented with authentication provider(s)\n\t-->\n\t<http pattern=\"/login.jsp\" security=\"none\" />\n\n\t<http>\n\t\t<intercept-url pattern=\"/secure/**\" access=\"hasAnyRole('ROLE_DEVELOPER','ROLE_USER')\" />\n\t\t<intercept-url pattern=\"/**\" access=\"hasAnyRole('ROLE_DEVELOPER','ROLE_USER')\" />\n\n\t\t<form-login login-page=\"/login.jsp\" authentication-failure-url=\"/login.jsp?login_error=true\"/>\n\t\t<http-basic/>\n\n\t\t<!-- Default logout configuration -->\n\t\t<logout logout-url=\"/logout\"/>\n\n\t\t<session-management>\n\t\t\t<concurrency-control max-sessions=\"1\" error-if-maximum-exceeded=\"true\" />\n\t\t</session-management>\n\n\t\t<csrf disabled=\"true\"/>\n\t</http>\n\n\t<beans:bean class=\"org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean\"/>\n\n</beans:beans>\n"
  },
  {
    "path": "itest/web/src/integration-test/resources/spring/http-security.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<beans:beans xmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<!--\n\t   Http App Context to test form login, remember-me and concurrent session control.\n\t   Needs to be supplemented with authentication provider(s)\n\t-->\n\t<http pattern=\"/login.jsp\" security=\"none\" />\n\n\t<http>\n\t\t<intercept-url pattern=\"/secure/**\" access=\"hasAnyRole('ROLE_DEVELOPER','ROLE_USER')\" />\n\t\t<intercept-url pattern=\"/**\" access=\"hasAnyRole('ROLE_DEVELOPER','ROLE_USER')\" />\n\n\t\t<form-login login-page=\"/login.jsp\" authentication-failure-url=\"/login.jsp?login_error=true\"/>\n\t\t<http-basic/>\n\n\t\t<!-- Default logout configuration -->\n\t\t<logout logout-url=\"/logout\"/>\n\n\t\t<session-management>\n\t\t\t<concurrency-control max-sessions=\"1\" />\n\t\t</session-management>\n\n\t\t<remember-me key=\"doesntmatter\" token-repository-ref=\"tokenRepo\"/>\n\t</http>\n\n\t<beans:bean name=\"tokenRepo\" class=\"org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl\"/>\n\n</beans:beans>\n"
  },
  {
    "path": "itest/web/src/integration-test/resources/spring/in-memory-provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<beans:beans xmlns=\"http://www.springframework.org/schema/security\"\n\txmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\t\t\thttp://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n\t<authentication-manager alias=\"authenticationManager\">\n\t\t<authentication-provider>\n\t\t\t<user-service>\n\t\t\t  <user name=\"miles\" password=\"{noop}milespassword\" authorities=\"ROLE_USER,ROLE_JAZZ,ROLE_TRUMPETER\"/>\n\t\t\t  <user name=\"johnc\" password=\"{noop}johncspassword\" authorities=\"ROLE_USER,ROLE_JAZZ,ROLE_SAXOPHONIST\"/>\n\t\t\t  <user name=\"jimi\" password=\"{noop}jimispassword\" authorities=\"ROLE_USER,ROLE_ROCK,ROLE_GUITARIST\"/>\n\t\t\t  <user name=\"bessie\" password=\"{noop}bessiespassword\" authorities=\"ROLE_USER,ROLE_JAZZ,ROLE_SINGER\"/>\n\t\t\t  <user name=\"theescapist&lt;&gt;&amp;.\" password=\"{noop}theescapistspassword\" authorities=\"ROLE_USER\"/>\n\t\t\t</user-service>\n\t\t</authentication-provider>\n\t</authentication-manager>\n\n</beans:beans>\n"
  },
  {
    "path": "itest/web/src/integration-test/resources/spring/testapp-servlet.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\txmlns:p=\"http://www.springframework.org/schema/p\" xmlns:context=\"http://www.springframework.org/schema/context\"\n\t\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n\t\t\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n\t<context:component-scan base-package=\"org.springframework.security.itest.web\"/>\n\t<context:annotation-config />\n</beans>\n"
  },
  {
    "path": "itest/web/src/main/java/org/springframework/security/itest/web/TestController.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.itest.web;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n\t@RequestMapping(value = \"/secure/file?with?special?chars.htm\", method = RequestMethod.GET)\n\tpublic String sec1255TestUrl() {\n\t\treturn \"I'm file?with?special?chars.htm\";\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String home() {\n\t\treturn \"home\";\n\t}\n\n\t@RequestMapping(\"/secure/index\")\n\t@ResponseBody\n\tpublic String secure() {\n\t\treturn \"A Secure Page\";\n\t}\n\n}\n"
  },
  {
    "path": "itest/web/src/main/resources/test-server.ldif",
    "content": "\n# Users\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: ou=musicians,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: uid=ben,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Ben Alex\nsn: Alex\nuid: ben\nuserPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=\n\ndn: uid=bob,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Bob Hamilton\nsn: Hamilton\nuid: bob\nuserPassword: bobspassword\n\ndn: uid=miles,ou=musicians,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Miles Davis\nsn: Davis\nuid: miles\nuserPassword: milespassword\n\ndn: uid=johnc,ou=musicians,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: John Coltrane\nsn: Coltrane\nuid: johnc\nuserPassword: johncspassword\n\ndn: uid=jimi,ou=musicians,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Jimi Hendrix\nsn: Hendrix\nuid: jimi\nuserPassword: {SSHA}S6jnyvykw4K5eF35OXvAkQsf3y2fPrRQ\n\n\n# Groups\n\ndn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: cn=developers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfUniqueNames\ncn: developers\nou: developer\nuniqueMember: uid=ben,ou=people,dc=springframework,dc=org\nuniqueMember: uid=bob,ou=people,dc=springframework,dc=org\n\ndn: cn=managers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfUniqueNames\ncn: managers\nou: manager\nuniqueMember: uid=ben,ou=people,dc=springframework,dc=org\n\ndn: ou=genres,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: genres\n\ndn: cn=rock,ou=genres,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfUniqueNames\ncn: rock\nou: rock\nuniqueMember: uid=jimi,ou=musicians,dc=springframework,dc=org\n\ndn: cn=jazz,ou=genres,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfUniqueNames\ncn: jazz\nou: jazz\nuniqueMember: uid=miles,ou=musicians,dc=springframework,dc=org\n\n\n"
  },
  {
    "path": "javascript/.gitignore",
    "content": "node_modules/\ndist/\n"
  },
  {
    "path": "javascript/.prettierrc",
    "content": "{\n    \"printWidth\": 120,\n    \"endOfLine\": \"auto\"\n}\n"
  },
  {
    "path": "javascript/eslint.config.js",
    "content": "import globals from \"globals\";\nimport eslintConfigPrettier from \"eslint-plugin-prettier/recommended\";\n\nexport default [\n  {\n    ignores: [\"build/**/*\"],\n  },\n  {\n    files: [\"lib/**/*.js\"],\n    languageOptions: {\n      sourceType: \"module\",\n      globals: {\n        ...globals.browser,\n        gobalThis: \"readonly\",\n      },\n    },\n  },\n  {\n    files: [\"test/**/*.js\"],\n    languageOptions: {\n      globals: {\n        ...globals.browser,\n        ...globals.mocha,\n        ...globals.chai,\n        ...globals.nodeBuiltin,\n      },\n    },\n  },\n  eslintConfigPrettier,\n];\n"
  },
  {
    "path": "javascript/lib/abort-controller.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nconst holder = {\n  controller: new AbortController(),\n};\n\n/**\n * Returns a new AbortSignal to be used in the options for the registration and authentication ceremonies.\n * Aborts the existing AbortController if it exists, cancelling any existing ceremony.\n *\n * The authentication ceremony, when triggered with conditional mediation, shows a non-modal\n * interaction. If the user does not interact with the non-modal dialog, the existing ceremony MUST\n * be cancelled before initiating a new one, hence the need for a singleton AbortController.\n *\n * @returns {AbortSignal} a new, non-aborted AbortSignal\n */\nfunction newSignal() {\n  if (!!holder.controller) {\n    holder.controller.abort(\"Initiating new WebAuthN ceremony, cancelling current ceremony\");\n  }\n  holder.controller = new AbortController();\n  return holder.controller.signal;\n}\n\nexport default {\n  newSignal,\n};\n"
  },
  {
    "path": "javascript/lib/base64url.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nexport default {\n  encode: function (buffer) {\n    const base64 = window.btoa(String.fromCharCode(...new Uint8Array(buffer)));\n    return base64.replace(/=/g, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\");\n  },\n  decode: function (base64url) {\n    const base64 = base64url.replace(/-/g, \"+\").replace(/_/g, \"/\");\n    const binStr = window.atob(base64);\n    const bin = new Uint8Array(binStr.length);\n    for (let i = 0; i < binStr.length; i++) {\n      bin[i] = binStr.charCodeAt(i);\n    }\n    return bin.buffer;\n  },\n};\n"
  },
  {
    "path": "javascript/lib/http.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nasync function post(url, headers, body) {\n  const options = {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application/json\",\n      ...headers,\n    },\n  };\n  if (body) {\n    options.body = JSON.stringify(body);\n  }\n  return fetch(url, options);\n}\n\nexport default { post };\n"
  },
  {
    "path": "javascript/lib/index.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport { setupLogin } from \"./webauthn-login.js\";\nimport { setupRegistration } from \"./webauthn-registration.js\";\n\n// Make \"setup\" available in the window domain, so it can be run with \"setupLogin()\"\nwindow.setupLogin = setupLogin;\nwindow.setupRegistration = setupRegistration;\n"
  },
  {
    "path": "javascript/lib/webauthn-core.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport base64url from \"./base64url.js\";\nimport http from \"./http.js\";\nimport abortController from \"./abort-controller.js\";\n\nasync function isConditionalMediationAvailable() {\n  return !!(\n    window.PublicKeyCredential &&\n    window.PublicKeyCredential.isConditionalMediationAvailable &&\n    (await window.PublicKeyCredential.isConditionalMediationAvailable())\n  );\n}\n\nasync function authenticate(headers, contextPath, useConditionalMediation) {\n  let options;\n  try {\n    const optionsResponse = await http.post(`${contextPath}/webauthn/authenticate/options`, headers);\n    if (!optionsResponse.ok) {\n      throw new Error(`HTTP ${optionsResponse.status}`);\n    }\n    options = await optionsResponse.json();\n  } catch (err) {\n    throw new Error(`Authentication failed. Could not fetch authentication options: ${err.message}`, { cause: err });\n  }\n\n  // FIXME: Use https://www.w3.org/TR/webauthn-3/#sctn-parseRequestOptionsFromJSON\n  const decodedAllowCredentials = !options.allowCredentials\n    ? []\n    : options.allowCredentials.map((cred) => ({\n        ...cred,\n        id: base64url.decode(cred.id),\n      }));\n\n  const decodedOptions = {\n    ...options,\n    allowCredentials: decodedAllowCredentials,\n    challenge: base64url.decode(options.challenge),\n  };\n\n  // Invoke the WebAuthn get() method.\n  const credentialOptions = {\n    publicKey: decodedOptions,\n    signal: abortController.newSignal(),\n  };\n  if (useConditionalMediation) {\n    // Request a conditional UI\n    credentialOptions.mediation = \"conditional\";\n  }\n\n  let cred;\n  try {\n    cred = await navigator.credentials.get(credentialOptions);\n  } catch (err) {\n    throw new Error(`Authentication failed. Call to navigator.credentials.get failed: ${err.message}`, { cause: err });\n  }\n\n  const { response, type: credType } = cred;\n  let userHandle;\n  if (response.userHandle) {\n    userHandle = base64url.encode(response.userHandle);\n  }\n  const body = {\n    id: cred.id,\n    rawId: base64url.encode(cred.rawId),\n    response: {\n      authenticatorData: base64url.encode(response.authenticatorData),\n      clientDataJSON: base64url.encode(response.clientDataJSON),\n      signature: base64url.encode(response.signature),\n      userHandle,\n    },\n    credType,\n    clientExtensionResults: cred.getClientExtensionResults(),\n    authenticatorAttachment: cred.authenticatorAttachment,\n  };\n\n  let authenticationResponse;\n  try {\n    const authenticationCallResponse = await http.post(`${contextPath}/login/webauthn`, headers, body);\n    if (!authenticationCallResponse.ok) {\n      throw new Error(`HTTP ${authenticationCallResponse.status}`);\n    }\n    authenticationResponse = await authenticationCallResponse.json();\n    //   if (authenticationResponse && authenticationResponse.authenticated) {\n  } catch (err) {\n    throw new Error(`Authentication failed. Could not process the authentication request: ${err.message}`, {\n      cause: err,\n    });\n  }\n\n  if (!(authenticationResponse && authenticationResponse.authenticated && authenticationResponse.redirectUrl)) {\n    throw new Error(\n      `Authentication failed. Expected {\"authenticated\": true, \"redirectUrl\": \"...\"}, server responded with: ${JSON.stringify(authenticationResponse)}`,\n    );\n  }\n\n  return authenticationResponse.redirectUrl;\n}\n\nasync function register(headers, contextPath, label) {\n  if (!label) {\n    throw new Error(\"Error: Passkey Label is required\");\n  }\n\n  let options;\n  try {\n    const optionsResponse = await http.post(`${contextPath}/webauthn/register/options`, headers);\n    if (!optionsResponse.ok) {\n      throw new Error(`Server responded with HTTP ${optionsResponse.status}`);\n    }\n    options = await optionsResponse.json();\n  } catch (e) {\n    throw new Error(`Registration failed. Could not fetch registration options: ${e.message}`, { cause: e });\n  }\n\n  // FIXME: Use https://www.w3.org/TR/webauthn-3/#sctn-parseCreationOptionsFromJSON\n  const decodedExcludeCredentials = !options.excludeCredentials\n    ? []\n    : options.excludeCredentials.map((cred) => ({\n        ...cred,\n        id: base64url.decode(cred.id),\n      }));\n\n  const decodedOptions = {\n    ...options,\n    user: {\n      ...options.user,\n      id: base64url.decode(options.user.id),\n    },\n    challenge: base64url.decode(options.challenge),\n    excludeCredentials: decodedExcludeCredentials,\n  };\n\n  let credentialsContainer;\n  try {\n    credentialsContainer = await navigator.credentials.create({\n      publicKey: decodedOptions,\n      signal: abortController.newSignal(),\n    });\n  } catch (e) {\n    throw new Error(`Registration failed. Call to navigator.credentials.create failed: ${e.message}`, { cause: e });\n  }\n\n  // FIXME: Let response be credential.response. If response is not an instance of AuthenticatorAttestationResponse, abort the ceremony with a user-visible error. https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n  const { response } = credentialsContainer;\n  const credential = {\n    id: credentialsContainer.id,\n    rawId: base64url.encode(credentialsContainer.rawId),\n    response: {\n      attestationObject: base64url.encode(response.attestationObject),\n      clientDataJSON: base64url.encode(response.clientDataJSON),\n      transports: response.getTransports ? response.getTransports() : [],\n    },\n    type: credentialsContainer.type,\n    clientExtensionResults: credentialsContainer.getClientExtensionResults(),\n    authenticatorAttachment: credentialsContainer.authenticatorAttachment,\n  };\n\n  const registrationRequest = {\n    publicKey: {\n      credential: credential,\n      label: label,\n    },\n  };\n\n  let verificationJSON;\n  try {\n    const verificationResp = await http.post(`${contextPath}/webauthn/register`, headers, registrationRequest);\n    if (!verificationResp.ok) {\n      throw new Error(`HTTP ${verificationResp.status}`);\n    }\n    verificationJSON = await verificationResp.json();\n  } catch (e) {\n    throw new Error(`Registration failed. Could not process the registration request: ${e.message}`, { cause: e });\n  }\n\n  if (!(verificationJSON && verificationJSON.success)) {\n    throw new Error(`Registration failed. Server responded with: ${JSON.stringify(verificationJSON)}`);\n  }\n}\n\nexport default {\n  authenticate,\n  register,\n  isConditionalMediationAvailable,\n};\n"
  },
  {
    "path": "javascript/lib/webauthn-login.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport webauthn from \"./webauthn-core.js\";\n\nasync function authenticateOrError(headers, contextPath, useConditionalMediation) {\n  try {\n    const redirectUrl = await webauthn.authenticate(headers, contextPath, useConditionalMediation);\n    window.location.href = redirectUrl;\n  } catch (err) {\n    console.error(err);\n    window.location.href = `${contextPath}/login?error`;\n  }\n}\n\nasync function conditionalMediation(headers, contextPath) {\n  const available = await webauthn.isConditionalMediationAvailable();\n  if (available) {\n    await authenticateOrError(headers, contextPath, true);\n  }\n  return available;\n}\n\nexport async function setupLogin(headers, contextPath, signinButton) {\n  signinButton.addEventListener(\"click\", async () => {\n    await authenticateOrError(headers, contextPath, false);\n  });\n\n  // FIXME: conditional mediation triggers browser crashes\n  // See: https://github.com/rwinch/spring-security-webauthn/issues/73\n  // await conditionalMediation(headers, contextPath);\n}\n"
  },
  {
    "path": "javascript/lib/webauthn-registration.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport webauthn from \"./webauthn-core.js\";\n\nfunction setVisibility(element, value) {\n  if (!element) {\n    return;\n  }\n  element.style.display = value ? \"block\" : \"none\";\n}\n\nfunction setError(ui, msg) {\n  resetPopups(ui);\n  const error = ui.getError();\n  if (!error) {\n    return;\n  }\n  error.textContent = msg;\n  setVisibility(error, true);\n}\n\nfunction setSuccess(ui) {\n  resetPopups(ui);\n  const success = ui.getSuccess();\n  if (!success) {\n    return;\n  }\n  setVisibility(success, true);\n}\n\nfunction resetPopups(ui) {\n  const success = ui.getSuccess();\n  const error = ui.getError();\n  setVisibility(success, false);\n  setVisibility(error, false);\n}\n\nasync function submitDeleteForm(contextPath, form, headers) {\n  const options = {\n    method: \"DELETE\",\n    headers: {\n      \"Content-Type\": \"application/json\",\n      ...headers,\n    },\n  };\n  await fetch(form.action, options);\n}\n\n/**\n *\n * @param headers headers added to the credentials creation POST request, typically CSRF\n * @param contextPath the contextPath from which the app is served\n * @param ui contains getRegisterButton(), getSuccess(), getError(), getLabelInput(), getDeleteForms()\n * @returns {Promise<void>}\n */\nexport async function setupRegistration(headers, contextPath, ui) {\n  resetPopups(ui);\n\n  if (!window.PublicKeyCredential) {\n    setError(ui, \"WebAuthn is not supported\");\n    return;\n  }\n\n  const queryString = new URLSearchParams(window.location.search);\n  if (queryString.has(\"success\")) {\n    setSuccess(ui);\n  }\n\n  ui.getRegisterButton().addEventListener(\"click\", async () => {\n    resetPopups(ui);\n    const label = ui.getLabelInput().value;\n    try {\n      await webauthn.register(headers, contextPath, label);\n      window.location.href = `${contextPath}/webauthn/register?success`;\n    } catch (err) {\n      setError(ui, err.message);\n      console.error(err);\n    }\n  });\n\n  ui.getDeleteForms().forEach((form) =>\n    form.addEventListener(\"submit\", async function (e) {\n      e.preventDefault();\n      try {\n        await submitDeleteForm(contextPath, form, headers);\n        window.location.href = `${contextPath}/webauthn/register?success`;\n      } catch (err) {\n        setError(ui, err.message);\n      }\n    }),\n  );\n}\n"
  },
  {
    "path": "javascript/package.json",
    "content": "{\n  \"name\": \"@springprojects/spring-security-webauthn\",\n  \"version\": \"1.0.0-alpha.9\",\n  \"description\": \"WebAuthN JS library for Spring Security\",\n  \"license\": \"ASL-2.0\",\n  \"author\": \"????\",\n  \"contributors\": [\n    \"Rob Winch <rwinch@users.noreply.github.com>\",\n    \"Daniel Garnier-Moiroux <git@garnier.wf>\"\n  ],\n  \"repository\": \"github:spring-projects/spring-security\",\n  \"bugs\": {\n    \"url\": \"https://github.com/spring-projects/spring-security/issues\"\n  },\n  \"engines\": {\n    \"node\": \">=20.0.0\"\n  },\n  \"scripts\": {\n    \"test\": \"mocha\",\n    \"check\": \"npm test && npm run lint\",\n    \"test:watch\": \"mocha --watch --parallel\",\n    \"assemble\": \"esbuild lib/index.js --bundle --outfile=build/dist/spring-security-webauthn.js\",\n    \"build\": \"npm run check && npm run assemble\",\n    \"lint\": \"eslint\",\n    \"format\": \"npm run lint -- --fix\"\n  },\n  \"main\": \"lib/index.js\",\n  \"files\": [\n    \"lib\"\n  ],\n  \"keywords\": [\n    \"Spring Security\",\n    \"WebAuthn\",\n    \"passkeys\"\n  ],\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.6.0\",\n    \"@types/sinon\": \"^17.0.3\",\n    \"chai\": \"~4.3\",\n    \"esbuild\": \"^0.25.0\",\n    \"eslint\": \"^9.6.0\",\n    \"eslint-config-prettier\": \"^9.1.0\",\n    \"eslint-plugin-prettier\": \"^5.1.3\",\n    \"globals\": \"^15.8.0\",\n    \"mocha\": \"~10.8\",\n    \"prettier\": \"^3.3.2\",\n    \"prettier-eslint\": \"~15.0\",\n    \"sinon\": \"^18.0.0\"\n  },\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "javascript/spring-security-javascript.gradle",
    "content": "\n/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nplugins {\n    id 'base'\n    id 'com.github.node-gradle.node' version '7.1.0'\n    id 'compile-warnings-error'\n}\n\nnode {\n    download = true\n    version = '20.17.0'\n}\n\ntasks.named('check') {\n    dependsOn 'npm_run_check'\n}\n\ntasks.register('dist', Zip) {\n    dependsOn 'npm_run_assemble'\n    from 'build/dist/spring-security.js'\n    into 'org/springframework/security'\n}\n\nconfigurations {\n    javascript {\n        canBeConsumed = true\n        canBeResolved = false\n    }\n}\n\nartifacts {\n    javascript(project.layout.buildDirectory.dir('dist')) {\n        builtBy(npm_run_assemble)\n    }\n}\n"
  },
  {
    "path": "javascript/test/abort-controller.test.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport \"./bootstrap.js\";\nimport abortController from \"../lib/abort-controller.js\";\nimport { expect } from \"chai\";\n\ndescribe(\"abort-controller\", () => {\n  describe(\"newSignal\", () => {\n    it(\"returns an AbortSignal\", () => {\n      const signal = abortController.newSignal();\n\n      expect(signal).to.be.instanceof(AbortSignal);\n      expect(signal.aborted).to.be.false;\n    });\n\n    it(\"returns a new signal every time\", () => {\n      const initialSignal = abortController.newSignal();\n\n      const newSignal = abortController.newSignal();\n\n      expect(initialSignal).to.not.equal(newSignal);\n    });\n\n    it(\"aborts the existing signal\", () => {\n      const signal = abortController.newSignal();\n\n      abortController.newSignal();\n\n      expect(signal.aborted).to.be.true;\n      expect(signal.reason).to.equal(\"Initiating new WebAuthN ceremony, cancelling current ceremony\");\n    });\n  });\n});\n"
  },
  {
    "path": "javascript/test/base64.test.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport { expect } from \"chai\";\nimport base64url from \"../lib/base64url.js\";\n\ndescribe(\"base64url\", () => {\n  before(() => {\n    // Emulate the atob / btoa base64 encoding/decoding from the browser\n    global.window = {\n      btoa: (str) => Buffer.from(str, \"binary\").toString(\"base64\"),\n      atob: (b64) => Buffer.from(b64, \"base64\").toString(\"binary\"),\n    };\n  });\n\n  after(() => {\n    // Reset window object\n    global.window = {};\n  });\n\n  it(\"decodes\", () => {\n    // \"Zm9vYmFy\" is \"foobar\" in base 64, i.e. f:102 o:111 o:111 b:98 a:97 r:114\n    const decoded = base64url.decode(\"Zm9vYmFy\");\n\n    expect(new Uint8Array(decoded)).to.be.deep.equal(new Uint8Array([102, 111, 111, 98, 97, 114]));\n  });\n\n  it(\"decodes special characters\", () => {\n    // Wrap the decode function for easy testing\n    const decode = (str) => {\n      const decoded = new Uint8Array(base64url.decode(str));\n      return Array.from(decoded);\n    };\n\n    // \"Pz8/\" is \"???\" in base64, i.e. ?:63 three times\n    expect(decode(\"Pz8/\")).to.be.deep.equal(decode(\"Pz8_\"));\n    expect(decode(\"Pz8_\")).to.be.deep.equal([63, 63, 63]);\n    // \"Pj4+\" is \">>>\" in base64, ie >:62 three times\n    expect(decode(\"Pj4+\")).to.be.deep.equal(decode(\"Pj4-\"));\n    expect(decode(\"Pj4-\")).to.be.deep.equal([62, 62, 62]);\n  });\n\n  it(\"encodes\", () => {\n    const encoded = base64url.encode(Buffer.from(\"foobar\"));\n\n    expect(encoded).to.be.equal(\"Zm9vYmFy\");\n  });\n\n  it(\"encodes special +/ characters\", () => {\n    const encode = (str) => base64url.encode(Buffer.from(str));\n\n    expect(encode(\"???\")).to.be.equal(\"Pz8_\");\n    expect(encode(\">>>\")).to.be.equal(\"Pj4-\");\n  });\n\n  it(\"is stable\", () => {\n    const base = \"tyRDnKxdj7uWOT5jrchXu54lo6nf3bWOUvMQnGOXk7g\";\n\n    expect(base64url.encode(base64url.decode(base))).to.be.equal(base);\n  });\n});\n"
  },
  {
    "path": "javascript/test/bootstrap.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport chai from \"chai\";\n\n// Show full diffs when there is an equality difference an assertion.\n// By default, chai truncates at 40 characters, making it difficult to\n// compare e.g. error messages\nchai.config.truncateThreshold = 0;\n"
  },
  {
    "path": "javascript/test/http.test.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport http from \"../lib/http.js\";\nimport { expect } from \"chai\";\nimport { fake, assert } from \"sinon\";\n\ndescribe(\"http\", () => {\n  beforeEach(() => {\n    global.fetch = fake.resolves({ ok: true });\n  });\n\n  afterEach(() => {\n    delete global.fetch;\n  });\n\n  describe(\"post\", () => {\n    it(\"calls fetch with headers\", async () => {\n      const url = \"https://example.com/some/path\";\n      const headers = { \"x-custom\": \"some-value\" };\n\n      const resp = await http.post(url, headers);\n\n      expect(resp.ok).to.be.true;\n      assert.calledOnceWithExactly(global.fetch, url, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          ...headers,\n        },\n      });\n    });\n\n    it(\"sends the body as a JSON string\", async () => {\n      const body = { foo: \"bar\", baz: 42 };\n      const url = \"https://example.com/some/path\";\n\n      const resp = await http.post(url, {}, body);\n\n      expect(resp.ok).to.be.true;\n      assert.calledOnceWithExactly(global.fetch, url, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n        },\n        body: `{\"foo\":\"bar\",\"baz\":42}`,\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "javascript/test/webauthn-core.test.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport \"./bootstrap.js\";\nimport { expect } from \"chai\";\nimport { assert, fake, match, stub } from \"sinon\";\nimport http from \"../lib/http.js\";\nimport webauthn from \"../lib/webauthn-core.js\";\nimport base64url from \"../lib/base64url.js\";\n\ndescribe(\"webauthn-core\", () => {\n  beforeEach(() => {\n    global.window = {\n      btoa: (str) => Buffer.from(str, \"binary\").toString(\"base64\"),\n      atob: (b64) => Buffer.from(b64, \"base64\").toString(\"binary\"),\n    };\n  });\n\n  afterEach(() => {\n    delete global.window;\n  });\n\n  describe(\"isConditionalMediationAvailable\", () => {\n    afterEach(() => {\n      delete global.window.PublicKeyCredential;\n    });\n\n    it(\"is available\", async () => {\n      global.window = {\n        PublicKeyCredential: {\n          isConditionalMediationAvailable: fake.resolves(true),\n        },\n      };\n\n      const result = await webauthn.isConditionalMediationAvailable();\n\n      expect(result).to.be.true;\n    });\n\n    describe(\"is not available\", async () => {\n      it(\"PublicKeyCredential does not exist\", async () => {\n        global.window = {};\n        const result = await webauthn.isConditionalMediationAvailable();\n        expect(result).to.be.false;\n      });\n      it(\"PublicKeyCredential.isConditionalMediationAvailable undefined\", async () => {\n        global.window = {\n          PublicKeyCredential: {},\n        };\n        const result = await webauthn.isConditionalMediationAvailable();\n        expect(result).to.be.false;\n      });\n      it(\"PublicKeyCredential.isConditionalMediationAvailable false\", async () => {\n        global.window = {\n          PublicKeyCredential: {\n            isConditionalMediationAvailable: fake.resolves(false),\n          },\n        };\n        const result = await webauthn.isConditionalMediationAvailable();\n        expect(result).to.be.false;\n      });\n    });\n  });\n\n  describe(\"authenticate\", () => {\n    let httpPostStub;\n    const contextPath = \"/some/path\";\n\n    const credentialsGetOptions = {\n      challenge: \"nRbOrtNKTfJ1JaxfUDKs8j3B-JFqyGQw8DO4u6eV3JA\",\n      timeout: 300000,\n      rpId: \"localhost\",\n      allowCredentials: [\n        {\n          id: \"nOsjw8eaaqSwVdTBBYE1FqfGdHs\",\n          type: \"public-key\",\n          transports: [],\n        },\n      ],\n      userVerification: \"preferred\",\n      extensions: {},\n    };\n\n    // This is kind of a self-fulfilling prophecy type of test: we produce array buffers by calling\n    // base64url.decode ; they will then be re-encoded to the same string in the production code.\n    // The ArrayBuffer API is not super friendly.\n    beforeEach(() => {\n      httpPostStub = stub(http, \"post\");\n      httpPostStub.withArgs(contextPath + \"/webauthn/authenticate/options\", match.any).resolves({\n        ok: true,\n        status: 200,\n        json: fake.resolves(credentialsGetOptions),\n      });\n      httpPostStub.withArgs(`${contextPath}/login/webauthn`, match.any, match.any).resolves({\n        ok: true,\n        status: 200,\n        json: fake.resolves({\n          authenticated: true,\n          redirectUrl: \"/success\",\n        }),\n      });\n\n      const validAuthenticatorResponse = {\n        id: \"UgghgP5QKozwsSUK1twCj8mpgZs\",\n        rawId: base64url.decode(\"UgghgP5QKozwsSUK1twCj8mpgZs\"),\n        response: {\n          authenticatorData: base64url.decode(\"y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA\"),\n          clientDataJSON: base64url.decode(\n            \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiUTdlR0NkNUw2cG9fa01meWNIQnBWRlR5dmd3RklCV0QxZWg5OUktRFhnWSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyJ9\",\n          ),\n          signature: base64url.decode(\n            \"MEUCIGT9PAWfU3lMicOXFMpHGcl033dY-sNSJvehlXvvoivyAiEA_D_yOsChERlXX2rFcK6Qx5BaAbx5qdU2hgYDVN6W770\",\n          ),\n          userHandle: base64url.decode(\"tyRDnKxdj7uWOT5jrchXu54lo6nf3bWOUvMQnGOXk7g\"),\n        },\n        getClientExtensionResults: () => ({}),\n        authenticatorAttachment: \"platform\",\n        type: \"public-key\",\n      };\n      global.navigator = {\n        credentials: {\n          get: fake.resolves(validAuthenticatorResponse),\n        },\n      };\n    });\n\n    afterEach(() => {\n      http.post.restore();\n      delete global.navigator;\n    });\n\n    it(\"succeeds\", async () => {\n      const redirectUrl = await webauthn.authenticate({ \"x-custom\": \"some-value\" }, contextPath, false);\n\n      expect(redirectUrl).to.equal(\"/success\");\n      assert.calledWith(\n        httpPostStub.lastCall,\n        `${contextPath}/login/webauthn`,\n        { \"x-custom\": \"some-value\" },\n        {\n          id: \"UgghgP5QKozwsSUK1twCj8mpgZs\",\n          rawId: \"UgghgP5QKozwsSUK1twCj8mpgZs\",\n          credType: \"public-key\",\n          response: {\n            authenticatorData: \"y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA\",\n            clientDataJSON:\n              \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiUTdlR0NkNUw2cG9fa01meWNIQnBWRlR5dmd3RklCV0QxZWg5OUktRFhnWSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyJ9\",\n            signature:\n              \"MEUCIGT9PAWfU3lMicOXFMpHGcl033dY-sNSJvehlXvvoivyAiEA_D_yOsChERlXX2rFcK6Qx5BaAbx5qdU2hgYDVN6W770\",\n            userHandle: \"tyRDnKxdj7uWOT5jrchXu54lo6nf3bWOUvMQnGOXk7g\",\n          },\n          clientExtensionResults: {},\n          authenticatorAttachment: \"platform\",\n        },\n      );\n    });\n\n    it(\"calls the authenticator with the correct options\", async () => {\n      await webauthn.authenticate({}, contextPath, false);\n\n      assert.calledOnceWithMatch(global.navigator.credentials.get, {\n        publicKey: {\n          challenge: base64url.decode(\"nRbOrtNKTfJ1JaxfUDKs8j3B-JFqyGQw8DO4u6eV3JA\"),\n          timeout: 300000,\n          rpId: \"localhost\",\n          allowCredentials: [\n            {\n              id: base64url.decode(\"nOsjw8eaaqSwVdTBBYE1FqfGdHs\"),\n              type: \"public-key\",\n              transports: [],\n            },\n          ],\n          userVerification: \"preferred\",\n          extensions: {},\n        },\n        signal: match.any,\n      });\n    });\n\n    describe(\"authentication failures\", () => {\n      it(\"when authentication options call\", async () => {\n        httpPostStub\n          .withArgs(`${contextPath}/webauthn/authenticate/options`, match.any)\n          .rejects(new Error(\"Connection refused\"));\n\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Authentication failed. Could not fetch authentication options: Connection refused\",\n          );\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it(\"when authentication options call returns does not return HTTP 200 OK\", async () => {\n        httpPostStub.withArgs(`${contextPath}/webauthn/authenticate/options`, match.any).resolves({\n          ok: false,\n          status: 400,\n        });\n\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\"Authentication failed. Could not fetch authentication options: HTTP 400\");\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it(\"when authentication options are not valid json\", async () => {\n        httpPostStub.withArgs(`${contextPath}/webauthn/authenticate/options`, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.rejects(new Error(\"Not valid JSON\")),\n        });\n\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\"Authentication failed. Could not fetch authentication options: Not valid JSON\");\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it(\"when navigator.credentials.get fails\", async () => {\n        global.navigator.credentials.get = fake.rejects(new Error(\"Operation was aborted\"));\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Authentication failed. Call to navigator.credentials.get failed: Operation was aborted\",\n          );\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it(\"when authentication call fails\", async () => {\n        httpPostStub\n          .withArgs(`${contextPath}/login/webauthn`, match.any, match.any)\n          .rejects(new Error(\"Connection refused\"));\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Authentication failed. Could not process the authentication request: Connection refused\",\n          );\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it(\"when authentication call does not return HTTP 200 OK\", async () => {\n        httpPostStub.withArgs(`${contextPath}/login/webauthn`, match.any, match.any).resolves({\n          ok: false,\n          status: 400,\n        });\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\"Authentication failed. Could not process the authentication request: HTTP 400\");\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it(\"when authentication call does not return JSON\", async () => {\n        httpPostStub.withArgs(`${contextPath}/login/webauthn`, match.any, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.rejects(new Error(\"Not valid JSON\")),\n        });\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Authentication failed. Could not process the authentication request: Not valid JSON\",\n          );\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it(\"when authentication call returns null\", async () => {\n        httpPostStub.withArgs(`${contextPath}/login/webauthn`, match.any, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.resolves(null),\n        });\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            'Authentication failed. Expected {\"authenticated\": true, \"redirectUrl\": \"...\"}, server responded with: null',\n          );\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it('when authentication call returns {\"authenticated\":false}', async () => {\n        httpPostStub.withArgs(`${contextPath}/login/webauthn`, match.any, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.resolves({\n            authenticated: false,\n          }),\n        });\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            'Authentication failed. Expected {\"authenticated\": true, \"redirectUrl\": \"...\"}, server responded with: {\"authenticated\":false}',\n          );\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n\n      it(\"when authentication call returns no redirectUrl\", async () => {\n        httpPostStub.withArgs(`${contextPath}/login/webauthn`, match.any, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.resolves({\n            authenticated: true,\n          }),\n        });\n        try {\n          await webauthn.authenticate({}, contextPath, false);\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            'Authentication failed. Expected {\"authenticated\": true, \"redirectUrl\": \"...\"}, server responded with: {\"authenticated\":true}',\n          );\n          return;\n        }\n        expect.fail(\"authenticate should throw\");\n      });\n    });\n  });\n\n  describe(\"register\", () => {\n    let httpPostStub;\n    const contextPath = \"/some/path\";\n\n    beforeEach(() => {\n      const credentialsCreateOptions = {\n        rp: {\n          name: \"Spring Security Relying Party\",\n          id: \"example.localhost\",\n        },\n        user: {\n          name: \"user\",\n          id: \"eatPy60xmXG_58JrIiIBa5wq8Y76c7MD6mnY5vW8yP8\",\n          displayName: \"user\",\n        },\n        challenge: \"s0hBOfkSaVLXdsbyD8jii6t2IjUd-eiTP1Cmeuo1qUo\",\n        pubKeyCredParams: [\n          {\n            type: \"public-key\",\n            alg: -8,\n          },\n          {\n            type: \"public-key\",\n            alg: -7,\n          },\n          {\n            type: \"public-key\",\n            alg: -257,\n          },\n        ],\n        timeout: 300000,\n        excludeCredentials: [\n          {\n            id: \"nOsjw8eaaqSwVdTBBYE1FqfGdHs\",\n            type: \"public-key\",\n            transports: [],\n          },\n        ],\n        authenticatorSelection: {\n          residentKey: \"required\",\n          userVerification: \"preferred\",\n        },\n        attestation: \"direct\",\n        extensions: { credProps: true },\n      };\n      const validAuthenticatorResponse = {\n        authenticatorAttachment: \"platform\",\n        id: \"9wAuex_025BgEQrs7fOypo5SGBA\",\n        rawId: base64url.decode(\"9wAuex_025BgEQrs7fOypo5SGBA\"),\n        response: {\n          attestationObject: base64url.decode(\n            \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViYy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAAPv8MAcVTk7MjAtuAgVX170AFPcALnsf9NuQYBEK7O3zsqaOUhgQpQECAyYgASFYIMB9pM2BeSeEG83fAKFVSLKIfvDBBVoyGgMoiGxE-6WgIlggazAojM5sduQy2M7rz1do55nVaNLGXh8k4xBHz-Oy91E\",\n          ),\n          getAuthenticatorData: () =>\n            base64url.decode(\n              \"y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAAPv8MAcVTk7MjAtuAgVX170AFPcALnsf9NuQYBEK7O3zsqaOUhgQpQECAyYgASFYIMB9pM2BeSeEG83fAKFVSLKIfvDBBVoyGgMoiGxE-6WgIlggazAojM5sduQy2M7rz1do55nVaNLGXh8k4xBHz-Oy91E\",\n            ),\n          clientDataJSON: base64url.decode(\n            \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiUVdwd3lUcXJpYVlqbVdnOWFvZ0FxUlRKNVFYMFBGV2JWR2xNeGNsVjZhcyIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyJ9\",\n          ),\n          getPublicKey: () =>\n            base64url.decode(\n              \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwH2kzYF5J4Qbzd8AoVVIsoh-8MEFWjIaAyiIbET7paBrMCiMzmx25DLYzuvPV2jnmdVo0sZeHyTjEEfP47L3UQ\",\n            ),\n          getPublicKeyAlgorithm: () => -7,\n          getTransports: () => [\"internal\"],\n        },\n        type: \"public-key\",\n        getClientExtensionResults: () => ({}),\n      };\n      global.navigator = {\n        credentials: {\n          create: fake.resolves(validAuthenticatorResponse),\n        },\n      };\n      httpPostStub = stub(http, \"post\");\n      httpPostStub.withArgs(contextPath + \"/webauthn/register/options\", match.any).resolves({\n        ok: true,\n        status: 200,\n        json: fake.resolves(credentialsCreateOptions),\n      });\n      httpPostStub.withArgs(`${contextPath}/webauthn/register`, match.any, match.any).resolves({\n        ok: true,\n        json: fake.resolves({\n          success: true,\n        }),\n      });\n    });\n\n    afterEach(() => {\n      httpPostStub.restore();\n      delete global.navigator;\n    });\n\n    it(\"succeeds\", async () => {\n      const contextPath = \"/some/path\";\n      const headers = { _csrf: \"csrf-value\" };\n\n      await webauthn.register(headers, contextPath, \"my passkey\");\n      assert.calledWithExactly(\n        httpPostStub.lastCall,\n        `${contextPath}/webauthn/register`,\n        headers,\n        match({\n          publicKey: {\n            credential: {\n              id: \"9wAuex_025BgEQrs7fOypo5SGBA\",\n              rawId: \"9wAuex_025BgEQrs7fOypo5SGBA\",\n              response: {\n                attestationObject:\n                  \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViYy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAAPv8MAcVTk7MjAtuAgVX170AFPcALnsf9NuQYBEK7O3zsqaOUhgQpQECAyYgASFYIMB9pM2BeSeEG83fAKFVSLKIfvDBBVoyGgMoiGxE-6WgIlggazAojM5sduQy2M7rz1do55nVaNLGXh8k4xBHz-Oy91E\",\n                clientDataJSON:\n                  \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiUVdwd3lUcXJpYVlqbVdnOWFvZ0FxUlRKNVFYMFBGV2JWR2xNeGNsVjZhcyIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyJ9\",\n                transports: [\"internal\"],\n              },\n              type: \"public-key\",\n              clientExtensionResults: {},\n              authenticatorAttachment: \"platform\",\n            },\n            label: \"my passkey\",\n          },\n        }),\n      );\n    });\n\n    it(\"calls the authenticator with the correct options\", async () => {\n      await webauthn.register({}, contextPath, \"my passkey\");\n\n      assert.calledOnceWithExactly(\n        global.navigator.credentials.create,\n        match({\n          publicKey: {\n            rp: {\n              name: \"Spring Security Relying Party\",\n              id: \"example.localhost\",\n            },\n            user: {\n              name: \"user\",\n              id: base64url.decode(\"eatPy60xmXG_58JrIiIBa5wq8Y76c7MD6mnY5vW8yP8\"),\n              displayName: \"user\",\n            },\n            challenge: base64url.decode(\"s0hBOfkSaVLXdsbyD8jii6t2IjUd-eiTP1Cmeuo1qUo\"),\n            pubKeyCredParams: [\n              {\n                type: \"public-key\",\n                alg: -8,\n              },\n              {\n                type: \"public-key\",\n                alg: -7,\n              },\n              {\n                type: \"public-key\",\n                alg: -257,\n              },\n            ],\n            timeout: 300000,\n            excludeCredentials: [\n              {\n                id: base64url.decode(\"nOsjw8eaaqSwVdTBBYE1FqfGdHs\"),\n                type: \"public-key\",\n                transports: [],\n              },\n            ],\n            authenticatorSelection: {\n              residentKey: \"required\",\n              userVerification: \"preferred\",\n            },\n            attestation: \"direct\",\n            extensions: { credProps: true },\n          },\n          signal: match.any,\n        }),\n      );\n    });\n\n    describe(\"registration failures\", () => {\n      it(\"when label is missing\", async () => {\n        try {\n          await webauthn.register({}, \"/\", \"\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\"Error: Passkey Label is required\");\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it(\"when cannot get the registration options\", async () => {\n        httpPostStub.withArgs(match.any, match.any).rejects(new Error(\"Server threw an error\"));\n        try {\n          await webauthn.register({}, \"/\", \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Registration failed. Could not fetch registration options: Server threw an error\",\n          );\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it(\"when registration options call does not return HTTP 200 OK\", async () => {\n        httpPostStub.withArgs(match.any, match.any).resolves({\n          ok: false,\n          status: 400,\n        });\n        try {\n          await webauthn.register({}, \"/\", \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Registration failed. Could not fetch registration options: Server responded with HTTP 400\",\n          );\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it(\"when registration options are not valid JSON\", async () => {\n        httpPostStub.withArgs(match.any, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.rejects(new Error(\"Not a JSON response\")),\n        });\n        try {\n          await webauthn.register({}, \"/\", \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Registration failed. Could not fetch registration options: Not a JSON response\",\n          );\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it(\"when navigator.credentials.create fails\", async () => {\n        global.navigator = {\n          credentials: {\n            create: fake.rejects(new Error(\"authenticator threw an error\")),\n          },\n        };\n        try {\n          await webauthn.register({}, contextPath, \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Registration failed. Call to navigator.credentials.create failed: authenticator threw an error\",\n          );\n          expect(err.cause).to.deep.equal(new Error(\"authenticator threw an error\"));\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it(\"when registration call fails\", async () => {\n        httpPostStub\n          .withArgs(`${contextPath}/webauthn/register`, match.any, match.any)\n          .rejects(new Error(\"Connection refused\"));\n        try {\n          await webauthn.register({}, contextPath, \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Registration failed. Could not process the registration request: Connection refused\",\n          );\n          expect(err.cause).to.deep.equal(new Error(\"Connection refused\"));\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it(\"when registration call does not return HTTP 200 OK\", async () => {\n        httpPostStub.withArgs(`${contextPath}/webauthn/register`, match.any, match.any).resolves({\n          ok: false,\n          status: 400,\n        });\n        try {\n          await webauthn.register({}, contextPath, \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\"Registration failed. Could not process the registration request: HTTP 400\");\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it(\"when registration call does not return JSON\", async () => {\n        httpPostStub.withArgs(`${contextPath}/webauthn/register`, match.any, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.rejects(new Error(\"Not valid JSON\")),\n        });\n        try {\n          await webauthn.register({}, contextPath, \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\n            \"Registration failed. Could not process the registration request: Not valid JSON\",\n          );\n          expect(err.cause).to.deep.equal(new Error(\"Not valid JSON\"));\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it(\"when registration call returns null\", async () => {\n        httpPostStub.withArgs(`${contextPath}/webauthn/register`, match.any, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.resolves(null),\n        });\n        try {\n          await webauthn.register({}, contextPath, \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal(\"Registration failed. Server responded with: null\");\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n\n      it('when registration call returns {\"success\":false}', async () => {\n        httpPostStub.withArgs(`${contextPath}/webauthn/register`, match.any, match.any).resolves({\n          ok: true,\n          status: 200,\n          json: fake.resolves({ success: false }),\n        });\n        try {\n          await webauthn.register({}, contextPath, \"my passkey\");\n        } catch (err) {\n          expect(err).to.be.an(\"error\");\n          expect(err.message).to.equal('Registration failed. Server responded with: {\"success\":false}');\n          return;\n        }\n        expect.fail(\"register should throw\");\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "javascript/test/webauthn-login.test.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport \"./bootstrap.js\";\nimport { expect } from \"chai\";\nimport { setupLogin } from \"../lib/webauthn-login.js\";\nimport webauthn from \"../lib/webauthn-core.js\";\nimport { assert, fake, match, stub } from \"sinon\";\n\ndescribe(\"webauthn-login\", () => {\n  describe(\"bootstrap\", () => {\n    let authenticateStub;\n    let isConditionalMediationAvailableStub;\n    let signinButton;\n\n    beforeEach(() => {\n      isConditionalMediationAvailableStub = stub(webauthn, \"isConditionalMediationAvailable\").resolves(false);\n      authenticateStub = stub(webauthn, \"authenticate\").resolves(\"/success\");\n      signinButton = {\n        addEventListener: fake(),\n      };\n\n      global.console = {\n        error: stub(),\n      };\n      global.window = {\n        location: {\n          href: {},\n        },\n      };\n    });\n\n    afterEach(() => {\n      authenticateStub.restore();\n      isConditionalMediationAvailableStub.restore();\n    });\n\n    it(\"sets up a click event listener on the signin button\", async () => {\n      await setupLogin({}, \"/some/path\", signinButton);\n\n      assert.calledOnceWithMatch(signinButton.addEventListener, \"click\", match.typeOf(\"function\"));\n    });\n\n    // FIXME: conditional mediation triggers browser crashes\n    // See: https://github.com/rwinch/spring-security-webauthn/issues/73\n    xit(\"uses conditional mediation when available\", async () => {\n      isConditionalMediationAvailableStub.resolves(true);\n\n      const headers = { \"x-header\": \"value\" };\n      const contextPath = \"/some/path\";\n\n      await setupLogin(headers, contextPath, signinButton);\n\n      assert.calledOnceWithExactly(authenticateStub, headers, contextPath, true);\n      expect(global.window.location.href).to.equal(\"/success\");\n    });\n\n    it(\"does not call authenticate when conditional mediation is not available\", async () => {\n      await setupLogin({}, \"/\", signinButton);\n\n      assert.notCalled(authenticateStub);\n    });\n\n    it(\"calls authenticate when the signin button is clicked\", async () => {\n      const headers = { \"x-header\": \"value\" };\n      const contextPath = \"/some/path\";\n\n      await setupLogin(headers, contextPath, signinButton);\n\n      // Call the event listener\n      await signinButton.addEventListener.firstCall.lastArg();\n\n      assert.calledOnceWithExactly(authenticateStub, headers, contextPath, false);\n      expect(global.window.location.href).to.equal(\"/success\");\n    });\n\n    it(\"handles authentication errors\", async () => {\n      authenticateStub.rejects(new Error(\"Authentication failed\"));\n      await setupLogin({}, \"/some/path\", signinButton);\n\n      // Call the event listener\n      await signinButton.addEventListener.firstCall.lastArg();\n\n      expect(global.window.location.href).to.equal(`/some/path/login?error`);\n      assert.calledOnceWithMatch(\n        global.console.error,\n        match.instanceOf(Error).and(match.has(\"message\", \"Authentication failed\")),\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "javascript/test/webauthn-registration.test.js",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\"use strict\";\n\nimport \"./bootstrap.js\";\nimport { expect, util, Assertion } from \"chai\";\nimport { setupRegistration } from \"../lib/webauthn-registration.js\";\nimport webauthn from \"../lib/webauthn-core.js\";\nimport { assert, fake, match, stub } from \"sinon\";\n\ndescribe(\"webauthn-registration\", () => {\n  before(() => {\n    Assertion.addProperty(\"visible\", function () {\n      const obj = util.flag(this, \"object\");\n      new Assertion(obj).to.have.nested.property(\"style.display\", \"block\");\n    });\n    Assertion.addProperty(\"hidden\", function () {\n      const obj = util.flag(this, \"object\");\n      new Assertion(obj).to.have.nested.property(\"style.display\", \"none\");\n    });\n  });\n\n  describe(\"bootstrap\", () => {\n    let registerStub;\n    let registerButton;\n    let labelField;\n    let errorPopup;\n    let successPopup;\n    let deleteForms;\n    let ui;\n\n    beforeEach(() => {\n      registerStub = stub(webauthn, \"register\").resolves(undefined);\n      errorPopup = {\n        style: {\n          display: undefined,\n        },\n        textContent: undefined,\n      };\n      successPopup = {\n        style: {\n          display: undefined,\n        },\n        textContent: undefined,\n      };\n      registerButton = {\n        addEventListener: fake(),\n      };\n      labelField = {\n        value: undefined,\n      };\n      deleteForms = [];\n      ui = {\n        getSuccess: function () {\n          return successPopup;\n        },\n        getError: function () {\n          return errorPopup;\n        },\n        getRegisterButton: function () {\n          return registerButton;\n        },\n        getLabelInput: function () {\n          return labelField;\n        },\n        getDeleteForms: function () {\n          return deleteForms;\n        },\n      };\n      global.window = {\n        location: {\n          href: {},\n        },\n      };\n      global.console = {\n        error: stub(),\n      };\n    });\n\n    afterEach(() => {\n      registerStub.restore();\n      delete global.window;\n    });\n\n    describe(\"when webauthn is not supported\", () => {\n      beforeEach(() => {\n        delete global.window.PublicKeyCredential;\n      });\n\n      it(\"does not set up a click event listener\", async () => {\n        await setupRegistration({}, \"/\", ui);\n\n        assert.notCalled(registerButton.addEventListener);\n      });\n\n      it(\"shows an error popup\", async () => {\n        await setupRegistration({}, \"/\", ui);\n\n        expect(errorPopup).to.be.visible;\n        expect(errorPopup.textContent).to.equal(\"WebAuthn is not supported\");\n        expect(successPopup).to.be.hidden;\n      });\n    });\n\n    describe(\"when webauthn is supported\", () => {\n      beforeEach(() => {\n        global.window.PublicKeyCredential = fake();\n      });\n\n      it(\"hides the popups\", async () => {\n        await setupRegistration({}, \"/\", ui);\n\n        expect(successPopup).to.be.hidden;\n        expect(errorPopup).to.be.hidden;\n      });\n\n      it(\"sets up a click event listener on the register button\", async () => {\n        await setupRegistration({}, \"/some/path\", ui);\n\n        assert.calledOnceWithMatch(registerButton.addEventListener, \"click\", match.typeOf(\"function\"));\n      });\n\n      describe(`when the query string contains \"success\"`, () => {\n        beforeEach(() => {\n          global.window.location.search = \"?success&continue=true\";\n        });\n\n        it(\"shows the success popup\", async () => {\n          await setupRegistration({}, \"/\", ui);\n\n          expect(successPopup).to.be.visible;\n          expect(errorPopup).to.be.hidden;\n        });\n      });\n\n      describe(\"when the register button is clicked\", () => {\n        const headers = { \"x-header\": \"value\" };\n        const contextPath = \"/some/path\";\n\n        beforeEach(async () => {\n          await setupRegistration(headers, contextPath, ui);\n        });\n\n        it(\"hides all the popups\", async () => {\n          successPopup.textContent = \"dummy-content\";\n          successPopup.style.display = \"block\";\n          errorPopup.textContent = \"dummy-content\";\n          errorPopup.style.display = \"block\";\n\n          await registerButton.addEventListener.firstCall.lastArg();\n\n          expect(successPopup).to.be.hidden;\n          expect(errorPopup).to.be.hidden;\n        });\n\n        it(\"calls register\", async () => {\n          labelField.value = \"passkey name\";\n\n          await registerButton.addEventListener.firstCall.lastArg();\n\n          assert.calledOnceWithExactly(registerStub, headers, contextPath, labelField.value);\n        });\n\n        it(\"navigates to success page\", async () => {\n          labelField.value = \"passkey name\";\n\n          await registerButton.addEventListener.firstCall.lastArg();\n\n          expect(global.window.location.href).to.equal(`${contextPath}/webauthn/register?success`);\n        });\n\n        it(\"handles errors\", async () => {\n          registerStub.rejects(new Error(\"The registration failed\"));\n\n          await registerButton.addEventListener.firstCall.lastArg();\n\n          expect(errorPopup.textContent).to.equal(\"The registration failed\");\n          expect(errorPopup).to.be.visible;\n          expect(successPopup).to.be.hidden;\n          assert.calledOnceWithMatch(\n            global.console.error,\n            match.instanceOf(Error).and(match.has(\"message\", \"The registration failed\")),\n          );\n        });\n      });\n\n      describe(\"delete\", () => {\n        beforeEach(() => {\n          global.fetch = fake.resolves({ ok: true });\n        });\n\n        afterEach(() => {\n          delete global.fetch;\n        });\n\n        it(\"no errors when no forms\", async () => {\n          await setupRegistration({}, \"/some/path\", ui);\n        });\n\n        it(\"sets up forms for fetch\", async () => {\n          const deleteFormOne = {\n            addEventListener: fake(),\n          };\n          const deleteFormTwo = {\n            addEventListener: fake(),\n          };\n          deleteForms = [deleteFormOne, deleteFormTwo];\n\n          await setupRegistration({}, \"\", ui);\n\n          assert.calledOnceWithMatch(deleteFormOne.addEventListener, \"submit\", match.typeOf(\"function\"));\n          assert.calledOnceWithMatch(deleteFormTwo.addEventListener, \"submit\", match.typeOf(\"function\"));\n        });\n\n        describe(\"when the delete button is clicked\", () => {\n          it(\"calls POST to the form action\", async () => {\n            const contextPath = \"/some/path\";\n            const deleteForm = {\n              addEventListener: fake(),\n              action: `${contextPath}/webauthn/1234`,\n            };\n            deleteForms = [deleteForm];\n            const headers = {\n              \"X-CSRF-TOKEN\": \"token\",\n            };\n\n            await setupRegistration(headers, contextPath, ui);\n\n            const clickEvent = {\n              preventDefault: fake(),\n            };\n            await deleteForm.addEventListener.firstCall.lastArg(clickEvent);\n            assert.calledOnce(clickEvent.preventDefault);\n            assert.calledOnceWithExactly(global.fetch, `/some/path/webauthn/1234`, {\n              method: \"DELETE\",\n              headers: {\n                \"Content-Type\": \"application/json\",\n                ...headers,\n              },\n            });\n            expect(global.window.location.href).to.equal(`/some/path/webauthn/register?success`);\n          });\n        });\n\n        it(\"handles errors\", async () => {\n          global.fetch = fake.rejects(\"Server threw an error\");\n          global.window.location.href = \"/initial/location\";\n          const deleteForm = {\n            addEventListener: fake(),\n          };\n          deleteForms = [deleteForm];\n\n          await setupRegistration({}, \"\", ui);\n          const clickEvent = { preventDefault: fake() };\n          await deleteForm.addEventListener.firstCall.lastArg(clickEvent);\n\n          expect(errorPopup).to.be.visible;\n          expect(errorPopup.textContent).to.equal(\"Server threw an error\");\n          // URL does not change\n          expect(global.window.location.href).to.equal(\"/initial/location\");\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "kerberos/kerberos-client/spring-security-kerberos-client.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'io.spring.convention.spring-module'\n\tid 'javadoc-warnings-error'\n\tid 'compile-warnings-error'\n}\n\ndescription = 'Spring Security Kerberos Client'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\timplementation project(':spring-security-kerberos-core')\n\timplementation project(':spring-security-kerberos-web')\n\tapi('org.springframework:spring-web')\n\tapi libs.org.apache.httpcomponents.httpclient\n\toptional project(':spring-security-ldap')\n\ttestImplementation project(':spring-security-kerberos-test')\n\ttestImplementation 'org.springframework:spring-test'\n\ttestImplementation project(':spring-security-config')\n\ttestImplementation 'org.junit.jupiter:junit-jupiter'\n\ttestImplementation 'org.mockito:mockito-junit-jupiter'\n\ttestImplementation libs.org.assertj.assertj.core\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\n"
  },
  {
    "path": "kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/KerberosRestTemplate.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.client;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.security.Principal;\nimport java.security.PrivilegedAction;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.PasswordCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.auth.kerberos.KerberosPrincipal;\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\nimport javax.security.auth.login.LoginException;\n\nimport org.apache.hc.client5.http.SystemDefaultDnsResolver;\nimport org.apache.hc.client5.http.auth.AuthSchemeFactory;\nimport org.apache.hc.client5.http.auth.AuthScope;\nimport org.apache.hc.client5.http.auth.Credentials;\nimport org.apache.hc.client5.http.auth.KerberosConfig;\nimport org.apache.hc.client5.http.auth.StandardAuthScheme;\nimport org.apache.hc.client5.http.classic.HttpClient;\nimport org.apache.hc.client5.http.config.RequestConfig;\nimport org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;\nimport org.apache.hc.client5.http.impl.auth.SPNegoSchemeFactory;\nimport org.apache.hc.client5.http.impl.classic.CloseableHttpClient;\nimport org.apache.hc.client5.http.impl.classic.HttpClientBuilder;\nimport org.apache.hc.core5.http.config.Lookup;\nimport org.apache.hc.core5.http.config.RegistryBuilder;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.client.HttpComponentsClientHttpRequestFactory;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RequestCallback;\nimport org.springframework.web.client.ResponseExtractor;\nimport org.springframework.web.client.RestClientException;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * {@code RestTemplate} that is able to make kerberos SPNEGO authenticated REST requests.\n * Under a hood this {@code KerberosRestTemplate} is using {@link HttpClient} to support\n * Kerberos.\n *\n * <p>\n * Generally this template can be configured in few different ways.\n * <ul>\n * <li>Leave keyTabLocation and userPrincipal empty if you want to use cached ticket</li>\n * <li>Use keyTabLocation and userPrincipal if you want to use keytab file</li>\n * <li>Use userPrincipal and password if you want to use user/password</li>\n * <li>Use loginOptions if you want to customise Krb5LoginModule options</li>\n * <li>Use a customised httpClient</li>\n * </ul>\n *\n * @author Janne Valkealahti\n *\n */\npublic class KerberosRestTemplate extends RestTemplate {\n\n\tprivate static final Credentials credentials = new NullCredentials();\n\n\tprivate final @Nullable String keyTabLocation;\n\n\tprivate final @Nullable String userPrincipal;\n\n\tprivate final @Nullable String password;\n\n\tprivate final @Nullable Map<String, Object> loginOptions;\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t */\n\tpublic KerberosRestTemplate() {\n\t\tthis(null, null, null, null, buildHttpClient());\n\t}\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t * @param httpClient the http client\n\t */\n\tpublic KerberosRestTemplate(HttpClient httpClient) {\n\t\tthis(null, null, null, null, httpClient);\n\t}\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t * @param keyTabLocation the key tab location\n\t * @param userPrincipal the user principal\n\t */\n\tpublic KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal) {\n\t\tthis(keyTabLocation, userPrincipal, buildHttpClient());\n\t}\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t * @param keyTabLocation the key tab location\n\t * @param userPrincipal the user principal\n\t * @param httpClient the http client\n\t */\n\tpublic KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,\n\t\t\tHttpClient httpClient) {\n\t\tthis(keyTabLocation, userPrincipal, null, null, httpClient);\n\t}\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t * @param loginOptions the login options\n\t */\n\tpublic KerberosRestTemplate(@Nullable Map<String, Object> loginOptions) {\n\t\tthis(null, null, null, loginOptions, buildHttpClient());\n\t}\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t * @param loginOptions the login options\n\t * @param httpClient the http client\n\t */\n\tpublic KerberosRestTemplate(@Nullable Map<String, Object> loginOptions, HttpClient httpClient) {\n\t\tthis(null, null, null, loginOptions, httpClient);\n\t}\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t * @param keyTabLocation the key tab location\n\t * @param userPrincipal the user principal\n\t * @param loginOptions the login options\n\t */\n\tpublic KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,\n\t\t\t@Nullable Map<String, Object> loginOptions) {\n\t\tthis(keyTabLocation, userPrincipal, null, loginOptions, buildHttpClient());\n\t}\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t * @param keyTabLocation the key tab location\n\t * @param userPrincipal the user principal\n\t * @param password the password\n\t * @param loginOptions the login options\n\t */\n\tpublic KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,\n\t\t\t@Nullable String password, @Nullable Map<String, Object> loginOptions) {\n\t\tthis(keyTabLocation, userPrincipal, password, loginOptions, buildHttpClient());\n\t}\n\n\t/**\n\t * Instantiates a new kerberos rest template.\n\t * @param keyTabLocation the key tab location\n\t * @param userPrincipal the user principal\n\t * @param password the password\n\t * @param loginOptions the login options\n\t * @param httpClient the http client\n\t */\n\tprivate KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,\n\t\t\t@Nullable String password, @Nullable Map<String, Object> loginOptions, HttpClient httpClient) {\n\t\tsuper(new HttpComponentsClientHttpRequestFactory(httpClient));\n\t\tthis.keyTabLocation = keyTabLocation;\n\t\tthis.userPrincipal = userPrincipal;\n\t\tthis.password = password;\n\t\tthis.loginOptions = loginOptions;\n\t}\n\n\t/**\n\t * Builds the default instance of {@link HttpClient} having kerberos support.\n\t * @return the http client with SPNEGO auth scheme\n\t */\n\tprivate static HttpClient buildHttpClient() {\n\t\tHttpClientBuilder builder = HttpClientBuilder.create();\n\n\t\tLookup<AuthSchemeFactory> authSchemeRegistry = RegistryBuilder.<AuthSchemeFactory>create()\n\t\t\t.register(StandardAuthScheme.SPNEGO,\n\t\t\t\t\tnew SPNegoSchemeFactory(KerberosConfig.custom()\n\t\t\t\t\t\t.setStripPort(KerberosConfig.Option.ENABLE)\n\t\t\t\t\t\t.setUseCanonicalHostname(KerberosConfig.Option.DISABLE)\n\t\t\t\t\t\t.build(), SystemDefaultDnsResolver.INSTANCE))\n\t\t\t.build();\n\n\t\tbuilder.setDefaultAuthSchemeRegistry(authSchemeRegistry);\n\t\tRequestConfig negotiate = RequestConfig.copy(RequestConfig.DEFAULT)\n\t\t\t.setTargetPreferredAuthSchemes(Set.of(StandardAuthScheme.SPNEGO, StandardAuthScheme.KERBEROS))\n\t\t\t.build();\n\t\tbuilder.setDefaultRequestConfig(negotiate);\n\t\tBasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();\n\t\tcredentialsProvider.setCredentials(new AuthScope(null, -1), credentials);\n\t\tbuilder.setDefaultCredentialsProvider(credentialsProvider);\n\t\tCloseableHttpClient httpClient = builder.build();\n\t\treturn httpClient;\n\t}\n\n\t/**\n\t * Setup the {@link LoginContext} with credentials and options for authentication\n\t * against kerberos.\n\t * @return the login context\n\t */\n\tprivate LoginContext buildLoginContext() throws LoginException {\n\t\tClientLoginConfig loginConfig = new ClientLoginConfig(this.keyTabLocation, this.userPrincipal, this.password,\n\t\t\t\tthis.loginOptions);\n\t\tSet<Principal> princ = new HashSet<Principal>(1);\n\t\tif (this.userPrincipal != null) {\n\t\t\tprinc.add(new KerberosPrincipal(this.userPrincipal));\n\t\t}\n\t\tSubject sub = new Subject(false, princ, new HashSet<Object>(), new HashSet<Object>());\n\t\tCallbackHandler callbackHandler = new CallbackHandlerImpl(this.userPrincipal, this.password);\n\t\tLoginContext lc = new LoginContext(\"\", sub, callbackHandler, loginConfig);\n\t\treturn lc;\n\t}\n\n\t@Override\n\tprotected final <T> T doExecute(final URI url, final @Nullable String uriTemplate,\n\t\t\tfinal @Nullable HttpMethod method, final @Nullable RequestCallback requestCallback,\n\t\t\tfinal @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {\n\n\t\ttry {\n\t\t\tLoginContext lc = buildLoginContext();\n\t\t\tlc.login();\n\t\t\tSubject serviceSubject = lc.getSubject();\n\t\t\treturn Subject.doAs(serviceSubject, new PrivilegedAction<T>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic T run() {\n\t\t\t\t\treturn KerberosRestTemplate.this.doExecuteSubject(url, uriTemplate, method, requestCallback,\n\t\t\t\t\t\t\tresponseExtractor);\n\t\t\t\t}\n\t\t\t});\n\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RestClientException(\"Error running rest call\", ex);\n\t\t}\n\t}\n\n\tprivate <T> T doExecuteSubject(URI url, @Nullable String uriTemplate, @Nullable HttpMethod method,\n\t\t\t@Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor)\n\t\t\tthrows RestClientException {\n\t\tT result = super.doExecute(url, uriTemplate, method, requestCallback, responseExtractor);\n\t\tif (result == null) {\n\t\t\tthrow new RestClientException(\"doExecute returned null\");\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static final class ClientLoginConfig extends Configuration {\n\n\t\tprivate final @Nullable String keyTabLocation;\n\n\t\tprivate final @Nullable String userPrincipal;\n\n\t\tprivate final @Nullable String password;\n\n\t\tprivate final @Nullable Map<String, Object> loginOptions;\n\n\t\tprivate ClientLoginConfig(@Nullable String keyTabLocation, @Nullable String userPrincipal,\n\t\t\t\t@Nullable String password, @Nullable Map<String, Object> loginOptions) {\n\t\t\tsuper();\n\t\t\tthis.keyTabLocation = keyTabLocation;\n\t\t\tthis.userPrincipal = userPrincipal;\n\t\t\tthis.password = password;\n\t\t\tthis.loginOptions = loginOptions;\n\t\t}\n\n\t\t@Override\n\t\tpublic AppConfigurationEntry[] getAppConfigurationEntry(String name) {\n\n\t\t\tMap<String, Object> options = new HashMap<String, Object>();\n\n\t\t\t// if we don't have keytab or principal only option is to rely on\n\t\t\t// credentials cache.\n\t\t\tif (!StringUtils.hasText(this.keyTabLocation) || !StringUtils.hasText(this.userPrincipal)) {\n\t\t\t\t// cache\n\t\t\t\toptions.put(\"useTicketCache\", \"true\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// keytab\n\t\t\t\toptions.put(\"useKeyTab\", \"true\");\n\t\t\t\toptions.put(\"keyTab\", this.keyTabLocation);\n\t\t\t\toptions.put(\"principal\", this.userPrincipal);\n\t\t\t\toptions.put(\"storeKey\", \"true\");\n\t\t\t}\n\n\t\t\toptions.put(\"doNotPrompt\", Boolean.toString(this.password == null));\n\t\t\toptions.put(\"isInitiator\", \"true\");\n\n\t\t\tif (this.loginOptions != null) {\n\t\t\t\toptions.putAll(this.loginOptions);\n\t\t\t}\n\n\t\t\treturn new AppConfigurationEntry[] {\n\t\t\t\t\tnew AppConfigurationEntry(\"com.sun.security.auth.module.Krb5LoginModule\",\n\t\t\t\t\t\t\tAppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) };\n\t\t}\n\n\t}\n\n\tprivate static class NullCredentials implements Credentials {\n\n\t\t@Override\n\t\tpublic @Nullable Principal getUserPrincipal() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic char @Nullable [] getPassword() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tprivate static final class CallbackHandlerImpl implements CallbackHandler {\n\n\t\tprivate final @Nullable String userPrincipal;\n\n\t\tprivate final @Nullable String password;\n\n\t\tprivate CallbackHandlerImpl(@Nullable String userPrincipal, @Nullable String password) {\n\t\t\tsuper();\n\t\t\tthis.userPrincipal = userPrincipal;\n\t\t\tthis.password = password;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\n\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\tif (callback instanceof NameCallback) {\n\t\t\t\t\tNameCallback nc = (NameCallback) callback;\n\t\t\t\t\tnc.setName(this.userPrincipal);\n\t\t\t\t}\n\t\t\t\telse if (callback instanceof PasswordCallback) {\n\t\t\t\t\tPasswordCallback pc = (PasswordCallback) callback;\n\t\t\t\t\tif (this.password != null) {\n\t\t\t\t\t\tpc.setPassword(this.password.toCharArray());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new UnsupportedCallbackException(callback, \"Unknown Callback\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/config/SunJaasKrb5LoginConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.client.config;\n\nimport java.util.HashMap;\n\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.Configuration;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of {@link Configuration} which uses Sun's JAAS Krb5LoginModule.\n *\n * @author Nelson Rodrigues\n * @author Janne Valkealahti\n *\n */\npublic class SunJaasKrb5LoginConfig extends Configuration implements InitializingBean {\n\n\tprivate static final Log LOG = LogFactory.getLog(SunJaasKrb5LoginConfig.class);\n\n\tprivate @Nullable String servicePrincipal;\n\n\tprivate @Nullable Resource keyTabLocation;\n\n\tprivate Boolean useTicketCache = false;\n\n\tprivate Boolean isInitiator = false;\n\n\tprivate Boolean debug = false;\n\n\tprivate @Nullable String keyTabLocationAsString;\n\n\tpublic void setServicePrincipal(String servicePrincipal) {\n\t\tthis.servicePrincipal = servicePrincipal;\n\t}\n\n\tpublic void setKeyTabLocation(Resource keyTabLocation) {\n\t\tthis.keyTabLocation = keyTabLocation;\n\t}\n\n\tpublic void setUseTicketCache(Boolean useTicketCache) {\n\t\tthis.useTicketCache = useTicketCache;\n\t}\n\n\tpublic void setIsInitiator(Boolean isInitiator) {\n\t\tthis.isInitiator = isInitiator;\n\t}\n\n\tpublic void setDebug(Boolean debug) {\n\t\tthis.debug = debug;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tAssert.hasText(this.servicePrincipal, \"servicePrincipal must be specified\");\n\n\t\tif (this.keyTabLocation != null && this.keyTabLocation instanceof ClassPathResource) {\n\t\t\tLOG.warn(\n\t\t\t\t\t\"Your keytab is in the classpath. This file needs special protection and shouldn't be in the classpath. JAAS may also not be able to load this file from classpath.\");\n\t\t}\n\n\t\tif (!this.useTicketCache) {\n\t\t\tAssert.notNull(this.keyTabLocation, \"keyTabLocation must be specified when useTicketCache is false\");\n\t\t}\n\n\t\tif (this.keyTabLocation != null) {\n\t\t\tthis.keyTabLocationAsString = this.keyTabLocation.getURL().toExternalForm();\n\t\t\tif (this.keyTabLocationAsString.startsWith(\"file:\")) {\n\t\t\t\tthis.keyTabLocationAsString = this.keyTabLocationAsString.substring(5);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic AppConfigurationEntry[] getAppConfigurationEntry(String name) {\n\t\tHashMap<String, String> options = new HashMap<>();\n\n\t\toptions.put(\"principal\", this.servicePrincipal);\n\n\t\tif (this.keyTabLocation != null) {\n\t\t\toptions.put(\"useKeyTab\", \"true\");\n\t\t\toptions.put(\"keyTab\", this.keyTabLocationAsString);\n\t\t\toptions.put(\"storeKey\", \"true\");\n\t\t}\n\n\t\toptions.put(\"doNotPrompt\", \"true\");\n\n\t\tif (this.useTicketCache) {\n\t\t\toptions.put(\"useTicketCache\", \"true\");\n\t\t\toptions.put(\"renewTGT\", \"true\");\n\t\t}\n\n\t\toptions.put(\"isInitiator\", this.isInitiator.toString());\n\t\toptions.put(\"debug\", this.debug.toString());\n\n\t\treturn new AppConfigurationEntry[] { new AppConfigurationEntry(\"com.sun.security.auth.module.Krb5LoginModule\",\n\t\t\t\tAppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), };\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/config/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.kerberos.client.config;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/ldap/KerberosLdapContextSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.client.ldap;\n\nimport java.security.PrivilegedAction;\nimport java.util.Hashtable;\nimport java.util.List;\n\nimport javax.naming.AuthenticationException;\nimport javax.naming.Context;\nimport javax.naming.NamingException;\nimport javax.naming.directory.DirContext;\nimport javax.security.auth.Subject;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\nimport javax.security.auth.login.LoginException;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.ldap.core.support.LdapContextSource;\nimport org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of an {@link LdapContextSource} that authenticates with the ldap server\n * using Kerberos.\n *\n * Example usage:\n *\n * <pre>\n *  &lt;bean id=&quot;authorizationContextSource&quot; class=&quot;org.springframework.security.kerberos.ldap.KerberosLdapContextSource&quot;&gt;\n *      &lt;constructor-arg value=&quot;${authentication.ldap.ldapUrl}&quot; /&gt;\n *      &lt;property name=&quot;referral&quot; value=&quot;ignore&quot; /&gt;\n *\n *       &lt;property name=&quot;loginConfig&quot;&gt;\n *           &lt;bean class=&quot;org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig&quot;&gt;\n *               &lt;property name=&quot;servicePrincipal&quot; value=&quot;${authentication.ldap.servicePrincipal}&quot; /&gt;\n *               &lt;property name=&quot;useTicketCache&quot; value=&quot;true&quot; /&gt;\n *               &lt;property name=&quot;isInitiator&quot; value=&quot;true&quot; /&gt;\n *               &lt;property name=&quot;debug&quot; value=&quot;false&quot; /&gt;\n *           &lt;/bean&gt;\n *       &lt;/property&gt;\n *   &lt;/bean&gt;\n *\n *   &lt;sec:ldap-user-service id=&quot;ldapUserService&quot; server-ref=&quot;authorizationContextSource&quot; user-search-filter=&quot;(| (userPrincipalName={0}) (sAMAccountName={0}))&quot;\n *       group-search-filter=&quot;(member={0})&quot; group-role-attribute=&quot;cn&quot; role-prefix=&quot;none&quot; /&gt;\n * </pre>\n *\n * @author Nelson Rodrigues\n * @see SunJaasKrb5LoginConfig\n */\npublic class KerberosLdapContextSource extends DefaultSpringSecurityContextSource implements InitializingBean {\n\n\tprivate @Nullable Configuration loginConfig;\n\n\t/**\n\t * Instantiates a new kerberos ldap context source.\n\t * @param url the url\n\t */\n\tpublic KerberosLdapContextSource(String url) {\n\t\tsuper(url);\n\t}\n\n\t/**\n\t * Instantiates a new kerberos ldap context source.\n\t * @param urls the urls\n\t * @param baseDn the base dn\n\t */\n\tpublic KerberosLdapContextSource(List<String> urls, String baseDn) {\n\t\tsuper(urls, baseDn);\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() /* throws Exception */ {\n\t\t// org.springframework.ldap.core.support.AbstractContextSource in 4.x\n\t\t// doesn't throw Exception for its InitializingBean method, so\n\t\t// we had to remove it from here also. Addition to that\n\t\t// we need to catch super call and re-throw.\n\t\ttry {\n\t\t\tsuper.afterPropertiesSet();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t\tAssert.notNull(this.loginConfig, \"loginConfig must be specified\");\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tprotected DirContext getDirContextInstance(final @SuppressWarnings(\"rawtypes\") Hashtable environment)\n\t\t\tthrows NamingException {\n\t\tenvironment.put(Context.SECURITY_AUTHENTICATION, \"GSSAPI\");\n\n\t\tSubject serviceSubject = login();\n\n\t\tfinal NamingException[] suppressedException = new NamingException[] { null };\n\t\tDirContext dirContext = Subject.doAs(serviceSubject, new PrivilegedAction<@Nullable DirContext>() {\n\n\t\t\t@Override\n\t\t\tpublic @Nullable DirContext run() {\n\t\t\t\ttry {\n\t\t\t\t\treturn KerberosLdapContextSource.super.getDirContextInstance(environment);\n\t\t\t\t}\n\t\t\t\tcatch (NamingException ex) {\n\t\t\t\t\tsuppressedException[0] = ex;\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif (suppressedException[0] != null) {\n\t\t\tthrow suppressedException[0];\n\t\t}\n\t\tif (dirContext == null) {\n\t\t\tthrow new NamingException(\"Failed to obtain DirContext\");\n\t\t}\n\n\t\treturn dirContext;\n\t}\n\n\t/**\n\t * The login configuration to get the serviceSubject from LoginContext\n\t * @param loginConfig the login config\n\t */\n\tpublic void setLoginConfig(Configuration loginConfig) {\n\t\tthis.loginConfig = loginConfig;\n\t}\n\n\tprivate Subject login() throws AuthenticationException {\n\t\ttry {\n\t\t\tLoginContext lc = new LoginContext(KerberosLdapContextSource.class.getSimpleName(), null, null,\n\t\t\t\t\tthis.loginConfig);\n\n\t\t\tlc.login();\n\n\t\t\treturn lc.getSubject();\n\t\t}\n\t\tcatch (LoginException ex) {\n\t\t\tAuthenticationException ae = new AuthenticationException(ex.getMessage());\n\t\t\tae.initCause(ex);\n\t\t\tthrow ae;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/ldap/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.kerberos.client.ldap;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.kerberos.client;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "kerberos/kerberos-client/src/test/java/org/springframework/security/kerberos/client/KerberosRestTemplateTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.client;\n\nimport java.io.File;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\n\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport okio.Buffer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.kerberos.test.KerberosSecurityTestcase;\nimport org.springframework.security.kerberos.test.MiniKdc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass KerberosRestTemplateTests extends KerberosSecurityTestcase {\n\n\tprivate final MockWebServer server = new MockWebServer();\n\n\tprivate static final String helloWorld = \"Hello World\";\n\n\tprivate static final MediaType textContentType = new MediaType(\"text\", \"plain\",\n\t\t\tCollections.singletonMap(\"charset\", \"UTF-8\"));\n\n\tprivate int port;\n\n\tprivate String baseUrl;\n\n\tprivate KerberosRestTemplate restTemplate;\n\n\tprivate String clientPrincipal;\n\n\tprivate File clientKeytab;\n\n\t@BeforeEach\n\tvoid setUp() throws Exception {\n\t\tthis.server.setDispatcher(new TestDispatcher());\n\t\tthis.server.start();\n\t\tthis.port = this.server.getPort();\n\t\tthis.baseUrl = \"http://localhost:\" + this.port;\n\n\t\tMiniKdc kdc = getKdc();\n\t\tFile workDir = getWorkDir();\n\n\t\tthis.clientPrincipal = \"client/localhost\";\n\t\tthis.clientKeytab = new File(workDir, \"client.keytab\");\n\t\tkdc.createPrincipal(this.clientKeytab, this.clientPrincipal);\n\n\t\tString serverPrincipal = \"HTTP/localhost\";\n\t\tFile serverKeytab = new File(workDir, \"server.keytab\");\n\t\tkdc.createPrincipal(serverKeytab, serverPrincipal);\n\t}\n\n\t@AfterEach\n\tvoid tearDown() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tvoid sendsNegotiateHeader() {\n\t\tsetUpClient();\n\t\tString s = this.restTemplate.getForObject(this.baseUrl + \"/get\", String.class);\n\t\tassertThat(s).isEqualTo(helloWorld);\n\t}\n\n\tprivate void setUpClient() {\n\t\tthis.restTemplate = new KerberosRestTemplate(this.clientKeytab.getAbsolutePath(), this.clientPrincipal);\n\t}\n\n\tprivate MockResponse getRequest(RecordedRequest request, byte[] body, String contentType) {\n\t\tif (request.getMethod().equals(\"OPTIONS\")) {\n\t\t\treturn new MockResponse().setResponseCode(200).setHeader(\"Allow\", \"GET, OPTIONS, HEAD, TRACE\");\n\t\t}\n\t\tBuffer buf = new Buffer();\n\t\tbuf.write(body);\n\t\tMockResponse response = new MockResponse().setHeader(HttpHeaders.CONTENT_LENGTH, body.length)\n\t\t\t.setBody(buf)\n\t\t\t.setResponseCode(200);\n\t\tif (contentType != null) {\n\t\t\tresponse = response.setHeader(HttpHeaders.CONTENT_TYPE, contentType);\n\t\t}\n\t\treturn response;\n\t}\n\n\tprotected class TestDispatcher extends Dispatcher {\n\n\t\t@Override\n\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\ttry {\n\t\t\t\tbyte[] helloWorldBytes = helloWorld.getBytes(StandardCharsets.UTF_8);\n\n\t\t\t\tif (request.getPath().equals(\"/get\")) {\n\t\t\t\t\tString header = request.getHeader(HttpHeaders.AUTHORIZATION);\n\t\t\t\t\tif (header == null) {\n\t\t\t\t\t\treturn new MockResponse().setResponseCode(401)\n\t\t\t\t\t\t\t.addHeader(HttpHeaders.WWW_AUTHENTICATE, \"Negotiate\");\n\t\t\t\t\t}\n\t\t\t\t\telse if (header.startsWith(\"Negotiate \")) {\n\t\t\t\t\t\treturn getRequest(request, helloWorldBytes, textContentType.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn new MockResponse().setResponseCode(404);\n\t\t\t}\n\t\t\tcatch (Throwable ex) {\n\t\t\t\treturn new MockResponse().setResponseCode(500).setBody(ex.toString());\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-client/src/test/resources/log4j.properties",
    "content": "log4j.rootCategory=INFO, stdout\n\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2} - %m%n\n\nlog4j.category.org.springframework.boot=INFO\nxlog4j.category.org.apache.http.wire=TRACE\nxlog4j.category.org.apache.http.headers=TRACE\n\n"
  },
  {
    "path": "kerberos/kerberos-client/src/test/resources/minikdc-krb5.conf",
    "content": "#\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n[libdefaults]\n    default_realm = {0}\n    udp_preference_limit = 1\n    forwardable = true\n\n[realms]\n    {0} = '{'\n        kdc = {1}:{2}\n    '}'"
  },
  {
    "path": "kerberos/kerberos-client/src/test/resources/minikdc.ldiff",
    "content": "#\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\ndn: ou=users,dc=${0},dc=${1}\nobjectClass: organizationalUnit\nobjectClass: top\nou: users\n\ndn: uid=krbtgt,ou=users,dc=${0},dc=${1}\nobjectClass: top\nobjectClass: person\nobjectClass: inetOrgPerson\nobjectClass: krb5principal\nobjectClass: krb5kdcentry\ncn: KDC Service\nsn: Service\nuid: krbtgt\nuserPassword: secret\nkrb5PrincipalName: krbtgt/${2}.${3}@${2}.${3}\nkrb5KeyVersionNumber: 0\n\ndn: uid=ldap,ou=users,dc=${0},dc=${1}\nobjectClass: top\nobjectClass: person\nobjectClass: inetOrgPerson\nobjectClass: krb5principal\nobjectClass: krb5kdcentry\ncn: LDAP\nsn: Service\nuid: ldap\nuserPassword: secret\nkrb5PrincipalName: ldap/${4}@${2}.${3}\nkrb5KeyVersionNumber: 0\n\ndn: uid=user1,ou=users,dc=${0},dc=${1}\nobjectClass: top\nobjectClass: person\nobjectClass: inetOrgPerson\nobjectClass: krb5principal\nobjectClass: krb5kdcentry\ncn: user1\nsn: Service\nuid: user1\nuserPassword: secret\nkrb5PrincipalName: user1@${2}.${3}\nkrb5KeyVersionNumber: 0\n\ndn: uid=webtier,ou=users,dc=${0},dc=${1}\nobjectClass: top\nobjectClass: person\nobjectClass: inetOrgPerson\nobjectClass: krb5principal\nobjectClass: krb5kdcentry\ncn: webtier\nsn: Service\nuid: webtier\nuserPassword: secret\nkrb5PrincipalName: HTTP/webtier@${2}.${3}\nkrb5KeyVersionNumber: 0\n\ndn: uid=servicetier,ou=users,dc=${0},dc=${1}\nobjectClass: top\nobjectClass: person\nobjectClass: inetOrgPerson\nobjectClass: krb5principal\nobjectClass: krb5kdcentry\ncn: servicetier\nsn: Service\nuid: servicetier\nuserPassword: secret\nkrb5PrincipalName: HTTP/servicetier@${2}.${3}\nkrb5KeyVersionNumber: 0\n"
  },
  {
    "path": "kerberos/kerberos-core/spring-security-kerberos-core.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'io.spring.convention.spring-module'\n\tid 'javadoc-warnings-error'\n\tid 'compile-warnings-error'\n}\n\ndescription = 'Spring Security Kerberos Core'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi(project(':spring-security-core'))\n\ttestImplementation 'org.junit.jupiter:junit-jupiter'\n\ttestImplementation 'org.mockito:mockito-junit-jupiter'\n\ttestImplementation libs.org.assertj.assertj.core\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/aot/hint/KerberosRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.aot.hint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\n\n/**\n * {@link RuntimeHintsRegistrar} for Kerberos authentication classes.\n *\n * @author Josh Long\n */\nclass KerberosRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\t// Krb5LoginModule is referenced as a plain string in LoginConfig and loaded by\n\t\t// JAAS via Class.forName at runtime, so it needs an explicit reflection hint.\n\t\thints.reflection()\n\t\t\t.registerType(TypeReference.of(\"com.sun.security.auth.module.Krb5LoginModule\"),\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * AOT and native image hint support for Kerberos authentication.\n */\n@NullMarked\npackage org.springframework.security.kerberos.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/JaasSubjectHolder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.security.auth.Subject;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;\n\n/**\n * <p>\n * Holds the Subject of the currently authenticated user, since this Jaas object also has\n * the credentials, and permits creating new credentials against other Kerberos services.\n * </p>\n *\n * @author Bogdan Mustiata\n * @see SunJaasKerberosClient\n * @see org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider\n */\npublic class JaasSubjectHolder implements Serializable {\n\n\tprivate static final long serialVersionUID = 8174713761131577405L;\n\n\tprivate Subject jaasSubject;\n\n\tprivate @Nullable String username;\n\n\tprivate Map<String, byte[]> savedTokens = new HashMap<String, byte[]>();\n\n\tpublic JaasSubjectHolder(Subject jaasSubject) {\n\t\tthis.jaasSubject = jaasSubject;\n\t}\n\n\tpublic JaasSubjectHolder(Subject jaasSubject, String username) {\n\t\tthis.jaasSubject = jaasSubject;\n\t\tthis.username = username;\n\t}\n\n\tpublic @Nullable String getUsername() {\n\t\treturn this.username;\n\t}\n\n\tpublic Subject getJaasSubject() {\n\t\treturn this.jaasSubject;\n\t}\n\n\tpublic void addToken(String targetService, byte[] outToken) {\n\t\tthis.savedTokens.put(targetService, outToken);\n\t}\n\n\tpublic byte @Nullable [] getToken(String principalName) {\n\t\treturn this.savedTokens.get(principalName);\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\npublic interface KerberosAuthentication {\n\n\tJaasSubjectHolder getJaasSubjectHolder();\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\n/**\n * {@link AuthenticationProvider} for kerberos.\n *\n * @author Mike Wiesner\n * @author Bogdan Mustiata\n * @since 1.0\n */\npublic class KerberosAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate @Nullable KerberosClient kerberosClient;\n\n\tprivate @Nullable UserDetailsService userDetailsService;\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tUsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;\n\t\tif (this.kerberosClient == null) {\n\t\t\tthrow new IllegalStateException(\"kerberosClient must be set\");\n\t\t}\n\t\tif (this.userDetailsService == null) {\n\t\t\tthrow new IllegalStateException(\"userDetailsService must be set\");\n\t\t}\n\t\tObject credentials = auth.getCredentials();\n\t\tif (credentials == null) {\n\t\t\tthrow new IllegalArgumentException(\"credentials cannot be null\");\n\t\t}\n\t\tJaasSubjectHolder subjectHolder = this.kerberosClient.login(auth.getName(), credentials.toString());\n\t\tString username = subjectHolder.getUsername();\n\t\tif (username == null) {\n\t\t\tthrow new IllegalStateException(\"username cannot be null\");\n\t\t}\n\t\tUserDetails userDetails = this.userDetailsService.loadUserByUsername(username);\n\t\tKerberosUsernamePasswordAuthenticationToken output = new KerberosUsernamePasswordAuthenticationToken(\n\t\t\t\tuserDetails, credentials, userDetails.getAuthorities(), subjectHolder);\n\t\toutput.setDetails(authentication.getDetails());\n\t\treturn output;\n\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<? extends Object> authentication) {\n\t\treturn (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));\n\t}\n\n\t/**\n\t * Sets the kerberos client.\n\t * @param kerberosClient the new kerberos client\n\t */\n\tpublic void setKerberosClient(KerberosClient kerberosClient) {\n\t\tthis.kerberosClient = kerberosClient;\n\t}\n\n\t/**\n\t * Sets the user details service.\n\t * @param detailsService the new user details service\n\t */\n\tpublic void setUserDetailsService(UserDetailsService detailsService) {\n\t\tthis.userDetailsService = detailsService;\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\n/**\n * @author Mike Wiesner\n * @author Bogdan Mustiata\n * @since 1.0\n * @version $Id$\n */\npublic interface KerberosClient {\n\n\tJaasSubjectHolder login(String username, String password);\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosMultiTier.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport java.security.PrivilegedAction;\n\nimport javax.security.auth.Subject;\n\nimport org.ietf.jgss.GSSContext;\nimport org.ietf.jgss.GSSCredential;\nimport org.ietf.jgss.GSSException;\nimport org.ietf.jgss.GSSManager;\nimport org.ietf.jgss.GSSName;\nimport org.ietf.jgss.Oid;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\n\n/**\n * <p>\n * Allows creating tickets against other service principals storing the tickets in the\n * KerberosAuthentication's JaasSubjectHolder.\n * </p>\n *\n * @author Bogdan Mustiata\n */\npublic final class KerberosMultiTier {\n\n\tpublic static final String KERBEROS_OID_STRING = \"1.2.840.113554.1.2.2\";\n\n\tpublic static final Oid KERBEROS_OID = createOid(KERBEROS_OID_STRING);\n\n\t/**\n\t * Create a new ticket for the\n\t * @param authentication\n\t * @param username\n\t * @param lifetimeInSeconds\n\t * @param targetService\n\t * @return\n\t */\n\tpublic static Authentication authenticateService(Authentication authentication, final String username,\n\t\t\tfinal int lifetimeInSeconds, final String targetService) {\n\n\t\tKerberosAuthentication kerberosAuthentication = (KerberosAuthentication) authentication;\n\t\tfinal JaasSubjectHolder jaasSubjectHolder = kerberosAuthentication.getJaasSubjectHolder();\n\t\tSubject subject = jaasSubjectHolder.getJaasSubject();\n\n\t\tSubject.doAs(subject, new PrivilegedAction<@Nullable Object>() {\n\t\t\t@Override\n\t\t\tpublic @Nullable Object run() {\n\t\t\t\trunAuthentication(jaasSubjectHolder, username, lifetimeInSeconds, targetService);\n\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\n\t\treturn authentication;\n\t}\n\n\tpublic static byte @Nullable [] getTokenForService(Authentication authentication, String principalName) {\n\t\tKerberosAuthentication kerberosAuthentication = (KerberosAuthentication) authentication;\n\t\tfinal JaasSubjectHolder jaasSubjectHolder = kerberosAuthentication.getJaasSubjectHolder();\n\n\t\treturn jaasSubjectHolder.getToken(principalName);\n\t}\n\n\tprivate static void runAuthentication(JaasSubjectHolder jaasContext, String username, int lifetimeInSeconds,\n\t\t\tString targetService) {\n\t\ttry {\n\t\t\tGSSManager manager = GSSManager.getInstance();\n\t\t\tGSSName clientName = manager.createName(username, GSSName.NT_USER_NAME);\n\n\t\t\tGSSCredential clientCredential = manager.createCredential(clientName, lifetimeInSeconds, KERBEROS_OID,\n\t\t\t\t\tGSSCredential.INITIATE_ONLY);\n\n\t\t\tGSSName serverName = manager.createName(targetService, GSSName.NT_USER_NAME);\n\n\t\t\tGSSContext securityContext = manager.createContext(serverName, KERBEROS_OID, clientCredential,\n\t\t\t\t\tGSSContext.DEFAULT_LIFETIME);\n\n\t\t\tsecurityContext.requestCredDeleg(true);\n\t\t\tsecurityContext.requestInteg(false);\n\t\t\tsecurityContext.requestAnonymity(false);\n\t\t\tsecurityContext.requestMutualAuth(false);\n\t\t\tsecurityContext.requestReplayDet(false);\n\t\t\tsecurityContext.requestSequenceDet(false);\n\n\t\t\tboolean established = false;\n\n\t\t\tbyte[] outToken = new byte[0];\n\n\t\t\twhile (!established) {\n\t\t\t\tbyte[] inToken = new byte[0];\n\t\t\t\toutToken = securityContext.initSecContext(inToken, 0, inToken.length);\n\n\t\t\t\testablished = securityContext.isEstablished();\n\t\t\t}\n\n\t\t\tjaasContext.addToken(targetService, outToken);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new BadCredentialsException(\"Kerberos authentication failed\", ex);\n\t\t}\n\t}\n\n\tprivate static Oid createOid(String oid) {\n\t\ttry {\n\t\t\treturn new Oid(oid);\n\t\t}\n\t\tcatch (GSSException ex) {\n\t\t\tthrow new IllegalStateException(\"Unable to instantiate Oid: \", ex);\n\t\t}\n\t}\n\n\tprivate KerberosMultiTier() {\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosServiceAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Authentication Provider which validates Kerberos Service Tickets or SPNEGO Tokens\n * (which includes Kerberos Service Tickets).\n * </p>\n *\n * <p>\n * It needs a <code>KerberosTicketValidator</code>, which contains the code to validate\n * the ticket, as this code is different between SUN and IBM JRE.<br>\n * It also needs an <code>UserDetailsService</code> to load the user properties and the\n * <code>GrantedAuthorities</code>, as we only get back the username from Kerberos\n * </p>\n *\n * You can see an example configuration in\n * <code>SpnegoAuthenticationProcessingFilter</code>.\n *\n * @author Mike Wiesner\n * @author Jeremy Stone\n * @since 1.0\n * @see KerberosTicketValidator\n * @see UserDetailsService\n */\npublic class KerberosServiceAuthenticationProvider implements AuthenticationProvider, InitializingBean {\n\n\tprivate static final Log LOG = LogFactory.getLog(KerberosServiceAuthenticationProvider.class);\n\n\tprivate @Nullable KerberosTicketValidator ticketValidator;\n\n\tprivate @Nullable UserDetailsService userDetailsService;\n\n\tprivate UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tKerberosServiceRequestToken auth = (KerberosServiceRequestToken) authentication;\n\t\tbyte[] token = auth.getToken();\n\t\tLOG.debug(\"Try to validate Kerberos Token\");\n\t\tif (this.ticketValidator == null) {\n\t\t\tthrow new IllegalStateException(\"ticketValidator must be set\");\n\t\t}\n\t\tif (this.userDetailsService == null) {\n\t\t\tthrow new IllegalStateException(\"userDetailsService must be set\");\n\t\t}\n\t\tKerberosTicketValidation ticketValidation = this.ticketValidator.validateTicket(token);\n\t\tLOG.debug(\"Successfully validated \" + ticketValidation.username());\n\t\tUserDetails userDetails = this.userDetailsService.loadUserByUsername(ticketValidation.username());\n\t\tthis.userDetailsChecker.check(userDetails);\n\t\tadditionalAuthenticationChecks(userDetails, auth);\n\t\tKerberosServiceRequestToken responseAuth = new KerberosServiceRequestToken(userDetails, ticketValidation,\n\t\t\t\tuserDetails.getAuthorities(), token);\n\t\tresponseAuth.setDetails(authentication.getDetails());\n\t\treturn responseAuth;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<? extends Object> auth) {\n\t\treturn KerberosServiceRequestToken.class.isAssignableFrom(auth);\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tAssert.notNull(this.ticketValidator, \"ticketValidator must be specified\");\n\t\tAssert.notNull(this.userDetailsService, \"userDetailsService must be specified\");\n\t}\n\n\t/**\n\t * The <code>UserDetailsService</code> to use, for loading the user properties and the\n\t * <code>GrantedAuthorities</code>.\n\t * @param userDetailsService the new user details service\n\t */\n\tpublic void setUserDetailsService(UserDetailsService userDetailsService) {\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t/**\n\t * The <code>KerberosTicketValidator</code> to use, for validating the Kerberos/SPNEGO\n\t * tickets.\n\t * @param ticketValidator the new ticket validator\n\t */\n\tpublic void setTicketValidator(KerberosTicketValidator ticketValidator) {\n\t\tthis.ticketValidator = ticketValidator;\n\t}\n\n\t/**\n\t * Allows subclasses to perform any additional checks of a returned\n\t * <code>UserDetails</code> for a given authentication request.\n\t * @param userDetails as retrieved from the {@link UserDetailsService}\n\t * @param authentication validated {@link KerberosServiceRequestToken}\n\t * @throws AuthenticationException AuthenticationException if the credentials could\n\t * not be validated (generally a <code>BadCredentialsException</code>, an\n\t * <code>AuthenticationServiceException</code>)\n\t */\n\tprotected void additionalAuthenticationChecks(UserDetails userDetails, KerberosServiceRequestToken authentication)\n\t\t\tthrows AuthenticationException {\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosServiceRequestToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport java.security.PrivilegedActionException;\nimport java.security.PrivilegedExceptionAction;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collection;\n\nimport javax.security.auth.Subject;\n\nimport org.ietf.jgss.GSSContext;\nimport org.ietf.jgss.MessageProp;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * <p>\n * Holds the Kerberos/SPNEGO token for requesting a kerberized service and is also the\n * output of <code>KerberosServiceAuthenticationProvider</code>.\n * </p>\n * <p>\n * Will mostly be created in <code>SpnegoAuthenticationProcessingFilter</code> and\n * authenticated in <code>KerberosServiceAuthenticationProvider</code>.\n * </p>\n *\n * This token cannot be re-authenticated, as you will get a Kerberos Reply error.\n *\n * @author Mike Wiesner\n * @author Jeremy Stone\n * @author Bogdan Mustiata\n * @since 1.0\n * @see KerberosServiceAuthenticationProvider\n */\npublic class KerberosServiceRequestToken extends AbstractAuthenticationToken implements KerberosAuthentication {\n\n\tprivate static final long serialVersionUID = 395488921064775014L;\n\n\tprivate final byte[] token;\n\n\tprivate final @Nullable Object principal;\n\n\tprivate final transient @Nullable KerberosTicketValidation ticketValidation;\n\n\tprivate @Nullable JaasSubjectHolder jaasSubjectHolder;\n\n\t/**\n\t * Creates an authenticated token, normally used as an output of an authentication\n\t * provider.\n\t * @param principal the user principal (mostly of instance <code>UserDetails</code>)\n\t * @param ticketValidation result of ticket validation\n\t * @param authorities the authorities which are granted to the user\n\t * @param token the Kerberos/SPNEGO token\n\t * @see UserDetails\n\t */\n\tpublic KerberosServiceRequestToken(Object principal, KerberosTicketValidation ticketValidation,\n\t\t\tCollection<? extends GrantedAuthority> authorities, byte[] token) {\n\t\tsuper(authorities);\n\t\tthis.token = token;\n\t\tthis.principal = principal;\n\t\tthis.ticketValidation = ticketValidation;\n\t\tthis.jaasSubjectHolder = new JaasSubjectHolder(ticketValidation.subject(), ticketValidation.username());\n\t\tsuper.setAuthenticated(true);\n\t}\n\n\t/**\n\t * Creates an unauthenticated instance which should then be authenticated by\n\t * <code>KerberosServiceAuthenticationProvider</code>.\n\t * @param token Kerberos/SPNEGO token\n\t * @see KerberosServiceAuthenticationProvider\n\t */\n\tpublic KerberosServiceRequestToken(byte[] token) {\n\t\tsuper(AuthorityUtils.NO_AUTHORITIES);\n\t\tthis.token = token;\n\t\tthis.ticketValidation = null;\n\t\tthis.principal = null;\n\t}\n\n\t/**\n\t * equals() is based only on the Kerberos token\n\t */\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!super.equals(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tKerberosServiceRequestToken other = (KerberosServiceRequestToken) obj;\n\t\tif (!Arrays.equals(this.token, other.token)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Calculates hashcode based on the Kerberos token\n\t */\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = super.hashCode();\n\t\tresult = prime * result + Arrays.hashCode(this.token);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic @Nullable Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t/**\n\t * Returns the Kerberos token\n\t * @return the token data\n\t */\n\tpublic byte[] getToken() {\n\t\treturn this.token;\n\t}\n\n\t/**\n\t * Gets the ticket validation\n\t * @return the ticket validation (which will be null if the token is unauthenticated)\n\t */\n\tpublic @Nullable KerberosTicketValidation getTicketValidation() {\n\t\treturn this.ticketValidation;\n\t}\n\n\t/**\n\t * Determines whether an authenticated token has a response token\n\t * @return whether a response token is available\n\t */\n\tpublic boolean hasResponseToken() {\n\t\treturn this.ticketValidation != null && this.ticketValidation.responseToken() != null;\n\t}\n\n\t/**\n\t * Gets the (Base64) encoded response token assuming one is available.\n\t * @return encoded response token\n\t */\n\tpublic String getEncodedResponseToken() {\n\t\tif (!hasResponseToken()) {\n\t\t\tthrow new IllegalStateException(\"Unauthenticated or no response token\");\n\t\t}\n\t\tif (this.ticketValidation == null) {\n\t\t\tthrow new IllegalStateException(\"Ticket validation is not available\");\n\t\t}\n\t\treturn Base64.getEncoder().encodeToString(this.ticketValidation.responseToken());\n\t}\n\n\t/**\n\t * Unwraps an encrypted message using the gss context\n\t * @param data the data\n\t * @param offset data offset\n\t * @param length data length\n\t * @return the decrypted message\n\t * @throws PrivilegedActionException if jaas throws and error\n\t */\n\tpublic byte[] decrypt(final byte[] data, final int offset, final int length) throws PrivilegedActionException {\n\t\tKerberosTicketValidation validation = getTicketValidation();\n\t\tif (validation == null) {\n\t\t\tthrow new IllegalStateException(\"Cannot decrypt without ticket validation\");\n\t\t}\n\t\treturn Subject.doAs(validation.subject(), new PrivilegedExceptionAction<byte[]>() {\n\t\t\tpublic byte[] run() throws Exception {\n\t\t\t\tfinal GSSContext context = validation.getGssContext();\n\t\t\t\tif (context == null) {\n\t\t\t\t\tthrow new IllegalStateException(\"GSSContext is not available\");\n\t\t\t\t}\n\t\t\t\treturn context.unwrap(data, offset, length, new MessageProp(true));\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Unwraps an encrypted message using the gss context\n\t * @param data the data\n\t * @return the decrypted message\n\t * @throws PrivilegedActionException if jaas throws and error\n\t */\n\tpublic byte[] decrypt(final byte[] data) throws PrivilegedActionException {\n\t\treturn decrypt(data, 0, data.length);\n\t}\n\n\t/**\n\t * Wraps an message using the gss context\n\t * @param data the data\n\t * @param offset data offset\n\t * @param length data length\n\t * @return the encrypted message\n\t * @throws PrivilegedActionException if jaas throws and error\n\t */\n\tpublic byte[] encrypt(final byte[] data, final int offset, final int length) throws PrivilegedActionException {\n\t\tKerberosTicketValidation validation = getTicketValidation();\n\t\tif (validation == null) {\n\t\t\tthrow new IllegalStateException(\"Cannot encrypt without ticket validation\");\n\t\t}\n\t\treturn Subject.doAs(validation.subject(), new PrivilegedExceptionAction<byte[]>() {\n\t\t\tpublic byte[] run() throws Exception {\n\t\t\t\tfinal GSSContext context = validation.getGssContext();\n\t\t\t\tif (context == null) {\n\t\t\t\t\tthrow new IllegalStateException(\"GSSContext is not available\");\n\t\t\t\t}\n\t\t\t\treturn context.wrap(data, offset, length, new MessageProp(true));\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Wraps an message using the gss context\n\t * @param data the data\n\t * @return the encrypted message\n\t * @throws PrivilegedActionException if jaas throws and error\n\t */\n\tpublic byte[] encrypt(final byte[] data) throws PrivilegedActionException {\n\t\treturn encrypt(data, 0, data.length);\n\t}\n\n\t@Override\n\tpublic JaasSubjectHolder getJaasSubjectHolder() {\n\t\tif (this.jaasSubjectHolder == null) {\n\t\t\tthrow new IllegalStateException(\"JaasSubjectHolder is not available for unauthenticated token\");\n\t\t}\n\t\treturn this.jaasSubjectHolder;\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosTicketValidation.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport java.util.HashSet;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.kerberos.KerberosPrincipal;\n\nimport org.ietf.jgss.GSSContext;\nimport org.ietf.jgss.GSSCredential;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Result of ticket validation\n */\npublic final class KerberosTicketValidation {\n\n\tprivate final String username;\n\n\tprivate final Subject subject;\n\n\tprivate final byte[] responseToken;\n\n\tprivate final @Nullable GSSContext gssContext;\n\n\tprivate final @Nullable GSSCredential delegationCredential;\n\n\tpublic KerberosTicketValidation(String username, String servicePrincipal, byte[] responseToken,\n\t\t\t@Nullable GSSContext gssContext) {\n\t\tthis(username, servicePrincipal, responseToken, gssContext, null);\n\t}\n\n\tpublic KerberosTicketValidation(String username, String servicePrincipal, byte[] responseToken,\n\t\t\t@Nullable GSSContext gssContext, @Nullable GSSCredential delegationCredential) {\n\t\tfinal HashSet<KerberosPrincipal> princs = new HashSet<KerberosPrincipal>();\n\t\tprincs.add(new KerberosPrincipal(servicePrincipal));\n\n\t\tthis.username = username;\n\t\tthis.subject = new Subject(false, princs, new HashSet<Object>(), new HashSet<Object>());\n\t\tthis.responseToken = responseToken;\n\t\tthis.gssContext = gssContext;\n\t\tthis.delegationCredential = delegationCredential;\n\t}\n\n\tpublic KerberosTicketValidation(String username, Subject subject, byte[] responseToken,\n\t\t\t@Nullable GSSContext gssContext) {\n\t\tthis(username, subject, responseToken, gssContext, null);\n\t}\n\n\tpublic KerberosTicketValidation(String username, Subject subject, byte[] responseToken,\n\t\t\t@Nullable GSSContext gssContext, @Nullable GSSCredential delegationCredential) {\n\t\tthis.username = username;\n\t\tthis.subject = subject;\n\t\tthis.responseToken = responseToken;\n\t\tthis.gssContext = gssContext;\n\t\tthis.delegationCredential = delegationCredential;\n\t}\n\n\tpublic String username() {\n\t\treturn this.username;\n\t}\n\n\tpublic byte[] responseToken() {\n\t\treturn this.responseToken;\n\t}\n\n\tpublic @Nullable GSSContext getGssContext() {\n\t\treturn this.gssContext;\n\t}\n\n\tpublic Subject subject() {\n\t\treturn this.subject;\n\t}\n\n\tpublic @Nullable GSSCredential getDelegationCredential() {\n\t\treturn this.delegationCredential;\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosTicketValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport org.springframework.security.authentication.BadCredentialsException;\n\n/**\n * Implementations of this interface are used in\n * {@link KerberosServiceAuthenticationProvider} to validate a Kerberos/SPNEGO Ticket.\n *\n * @author Mike Wiesner\n * @author Jeremy Stone\n * @since 1.0\n * @see KerberosServiceAuthenticationProvider\n */\npublic interface KerberosTicketValidator {\n\n\t/**\n\t * Validates a Kerberos/SPNEGO ticket.\n\t * @param token Kerberos/SPNEGO ticket\n\t * @return authenticated kerberos principal\n\t * @throws BadCredentialsException if the ticket is not valid\n\t */\n\tKerberosTicketValidation validateTicket(byte[] token) throws BadCredentialsException;\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/KerberosUsernamePasswordAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport java.util.Collection;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * <p>\n * Holds the Username/Password as well as the JAAS Subject allowing multi-tier\n * authentications using Kerberos.\n * </p>\n *\n * <p>\n * The JAAS Subject has in its private credentials the Kerberos tickets for generating new\n * tickets against other service principals using\n * <code>KerberosMultiTier.authenticateService()</code>\n * </p>\n *\n * @author Bogdan Mustiata\n * @see KerberosAuthenticationProvider\n * @see KerberosMultiTier\n */\npublic class KerberosUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken\n\t\timplements KerberosAuthentication {\n\n\tprivate static final long serialVersionUID = 6327699460703504153L;\n\n\tprivate final JaasSubjectHolder jaasSubjectHolder;\n\n\t/**\n\t * <p>\n\t * Creates an authentication token that holds the username and password, and the\n\t * Subject that the user will need to create new authentication tokens against other\n\t * services.\n\t * </p>\n\t * @param principal\n\t * @param credentials\n\t * @param authorities\n\t * @param subjectHolder\n\t */\n\tpublic KerberosUsernamePasswordAuthenticationToken(Object principal, Object credentials,\n\t\t\tCollection<? extends GrantedAuthority> authorities, JaasSubjectHolder subjectHolder) {\n\t\tsuper(principal, credentials, authorities);\n\t\tthis.jaasSubjectHolder = subjectHolder;\n\t}\n\n\t@Override\n\tpublic JaasSubjectHolder getJaasSubjectHolder() {\n\t\treturn this.jaasSubjectHolder;\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.kerberos.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/sun/GlobalSunJaasKerberosConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication.sun;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\n\n/**\n * Config for global jaas.\n *\n * @author Mike Wiesner\n * @since 1.0\n */\npublic class GlobalSunJaasKerberosConfig implements BeanPostProcessor, InitializingBean {\n\n\tprivate boolean debug = false;\n\n\tprivate @Nullable String krbConfLocation;\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tif (this.debug) {\n\t\t\tSystem.setProperty(\"sun.security.krb5.debug\", \"true\");\n\t\t}\n\t\tif (this.krbConfLocation != null) {\n\t\t\tSystem.setProperty(\"java.security.krb5.conf\", this.krbConfLocation);\n\t\t}\n\n\t}\n\n\t/**\n\t * Enable debug logs from the Sun Kerberos Implementation. Default is false.\n\t * @param debug true if debug should be enabled\n\t */\n\tpublic void setDebug(boolean debug) {\n\t\tthis.debug = debug;\n\t}\n\n\t/**\n\t * Kerberos config file location can be specified here.\n\t * @param krbConfLocation the path to krb config file\n\t */\n\tpublic void setKrbConfLocation(String krbConfLocation) {\n\t\tthis.krbConfLocation = krbConfLocation;\n\t}\n\n\t// The following methods are not used here. This Bean implements only\n\t// BeanPostProcessor to ensure that it\n\t// is created before any other bean is created, because the system properties needed\n\t// to be set very early\n\t// in the startup-phase, but after the BeanFactoryPostProcessing.\n\n\t@Override\n\tpublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n\t\treturn bean;\n\t}\n\n\t@Override\n\tpublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n\t\treturn bean;\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/sun/JaasUtil.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication.sun;\n\nimport java.security.Principal;\nimport java.util.HashSet;\n\nimport javax.security.auth.Subject;\n\n/**\n * JAAS utility functions.\n *\n * @author Bogdan Mustiata\n */\npublic final class JaasUtil {\n\n\t/**\n\t * Copy the principal and the credentials into a new Subject.\n\t * @param subject\n\t * @return\n\t */\n\tpublic static Subject copySubject(Subject subject) {\n\t\tSubject subjectCopy = new Subject(false, new HashSet<Principal>(subject.getPrincipals()),\n\t\t\t\tnew HashSet<Object>(subject.getPublicCredentials()),\n\t\t\t\tnew HashSet<Object>(subject.getPrivateCredentials()));\n\n\t\treturn subjectCopy;\n\t}\n\n\tprivate JaasUtil() {\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/sun/SunJaasKerberosClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication.sun;\n\nimport java.io.IOException;\nimport java.util.HashMap;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.PasswordCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\nimport javax.security.auth.login.LoginException;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.kerberos.authentication.JaasSubjectHolder;\nimport org.springframework.security.kerberos.authentication.KerberosClient;\n\n/**\n * Implementation of {@link KerberosClient} which uses the SUN JAAS login module, which is\n * included in the SUN JRE, it will not work with an IBM JRE. The whole configuration is\n * done in this class, no additional JAAS configuration is needed.\n *\n * @author Mike Wiesner\n * @author Bogdan Mustiata\n * @since 1.0\n */\npublic class SunJaasKerberosClient implements KerberosClient {\n\n\tprivate boolean debug = false;\n\n\tprivate boolean multiTier = false;\n\n\tprivate static final Log LOG = LogFactory.getLog(SunJaasKerberosClient.class);\n\n\t@Override\n\tpublic JaasSubjectHolder login(String username, String password) {\n\t\tLOG.debug(\"Trying to authenticate \" + username + \" with Kerberos\");\n\t\tJaasSubjectHolder result;\n\n\t\ttry {\n\t\t\tLoginContext loginContext = new LoginContext(\"\", null,\n\t\t\t\t\tnew KerberosClientCallbackHandler(username, password), new LoginConfig(this.debug));\n\t\t\tloginContext.login();\n\n\t\t\tSubject jaasSubject = loginContext.getSubject();\n\n\t\t\tif (LOG.isDebugEnabled()) {\n\t\t\t\tLOG.debug(\"Kerberos authenticated user: \" + jaasSubject);\n\t\t\t}\n\n\t\t\tString validatedUsername = jaasSubject.getPrincipals().iterator().next().toString();\n\t\t\tSubject subjectCopy = JaasUtil.copySubject(jaasSubject);\n\t\t\tresult = new JaasSubjectHolder(subjectCopy, validatedUsername);\n\n\t\t\tif (!this.multiTier) {\n\t\t\t\tloginContext.logout();\n\t\t\t}\n\t\t}\n\t\tcatch (LoginException ex) {\n\t\t\tthrow new BadCredentialsException(\"Kerberos authentication failed\", ex);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic void setDebug(boolean debug) {\n\t\tthis.debug = debug;\n\t}\n\n\tpublic void setMultiTier(boolean multiTier) {\n\t\tthis.multiTier = multiTier;\n\t}\n\n\tprivate static final class LoginConfig extends Configuration {\n\n\t\tprivate boolean debug;\n\n\t\tprivate LoginConfig(boolean debug) {\n\t\t\tsuper();\n\t\t\tthis.debug = debug;\n\t\t}\n\n\t\t@Override\n\t\tpublic AppConfigurationEntry[] getAppConfigurationEntry(String name) {\n\t\t\tHashMap<String, String> options = new HashMap<String, String>();\n\t\t\toptions.put(\"storeKey\", \"true\");\n\t\t\tif (this.debug) {\n\t\t\t\toptions.put(\"debug\", \"true\");\n\t\t\t}\n\n\t\t\treturn new AppConfigurationEntry[] {\n\t\t\t\t\tnew AppConfigurationEntry(\"com.sun.security.auth.module.Krb5LoginModule\",\n\t\t\t\t\t\t\tAppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), };\n\t\t}\n\n\t}\n\n\tstatic final class KerberosClientCallbackHandler implements CallbackHandler {\n\n\t\tprivate String username;\n\n\t\tprivate String password;\n\n\t\tprivate KerberosClientCallbackHandler(String username, String password) {\n\t\t\tthis.username = username;\n\t\t\tthis.password = password;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\tif (callback instanceof NameCallback) {\n\t\t\t\t\tNameCallback ncb = (NameCallback) callback;\n\t\t\t\t\tncb.setName(this.username);\n\t\t\t\t}\n\t\t\t\telse if (callback instanceof PasswordCallback) {\n\t\t\t\t\tPasswordCallback pwcb = (PasswordCallback) callback;\n\t\t\t\t\tpwcb.setPassword(this.password.toCharArray());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new UnsupportedCallbackException(callback,\n\t\t\t\t\t\t\t\"We got a \" + callback.getClass().getCanonicalName()\n\t\t\t\t\t\t\t\t\t+ \", but only NameCallback and PasswordCallback is supported\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/sun/SunJaasKerberosTicketValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication.sun;\n\nimport java.security.Principal;\nimport java.security.PrivilegedActionException;\nimport java.security.PrivilegedExceptionAction;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.kerberos.KerberosPrincipal;\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\n\nimport com.sun.security.jgss.GSSUtil;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.ietf.jgss.GSSContext;\nimport org.ietf.jgss.GSSCredential;\nimport org.ietf.jgss.GSSManager;\nimport org.ietf.jgss.GSSName;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.kerberos.authentication.JaasSubjectHolder;\nimport org.springframework.security.kerberos.authentication.KerberosTicketValidation;\nimport org.springframework.security.kerberos.authentication.KerberosTicketValidator;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of {@link KerberosTicketValidator} which uses the SUN JAAS login module,\n * which is included in the SUN JRE, it will not work with an IBM JRE. The whole\n * configuration is done in this class, no additional JAAS configuration is needed.\n *\n * @author Mike Wiesner\n * @author Jeremy Stone\n * @author Bogdan Mustiata\n * @since 1.0\n */\npublic class SunJaasKerberosTicketValidator implements KerberosTicketValidator, InitializingBean {\n\n\tprivate @Nullable String servicePrincipal;\n\n\tprivate @Nullable String realmName;\n\n\tprivate @Nullable Resource keyTabLocation;\n\n\tprivate @Nullable Subject serviceSubject;\n\n\tprivate boolean holdOnToGSSContext;\n\n\tprivate boolean debug = false;\n\n\tprivate boolean multiTier = false;\n\n\tprivate boolean refreshKrb5Config = false;\n\n\tprivate static final Log LOG = LogFactory.getLog(SunJaasKerberosTicketValidator.class);\n\n\t@Override\n\tpublic KerberosTicketValidation validateTicket(byte[] token) {\n\t\ttry {\n\t\t\tif (this.serviceSubject == null) {\n\t\t\t\tthrow new IllegalStateException(\"serviceSubject must be initialized\");\n\t\t\t}\n\t\t\tif (!this.multiTier) {\n\t\t\t\treturn Subject.doAs(this.serviceSubject, new KerberosValidateAction(token));\n\t\t\t}\n\n\t\t\tSubject subjectCopy = JaasUtil.copySubject(this.serviceSubject);\n\t\t\tJaasSubjectHolder subjectHolder = new JaasSubjectHolder(subjectCopy);\n\n\t\t\treturn Subject.doAs(subjectHolder.getJaasSubject(), new KerberosMultitierValidateAction(token));\n\n\t\t}\n\t\tcatch (IllegalStateException | PrivilegedActionException ex) {\n\t\t\tthrow new BadCredentialsException(\"Kerberos validation not successful\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tAssert.notNull(this.servicePrincipal, \"servicePrincipal must be specified\");\n\t\tAssert.notNull(this.keyTabLocation, \"keyTab must be specified\");\n\t\tif (this.servicePrincipal == null || this.keyTabLocation == null) {\n\t\t\tthrow new IllegalStateException(\"servicePrincipal and keyTabLocation must be set\");\n\t\t}\n\t\tif (this.keyTabLocation instanceof ClassPathResource) {\n\t\t\tthis.LOG.warn(\n\t\t\t\t\t\"Your keytab is in the classpath. This file needs special protection and shouldn't be in the classpath. JAAS may also not be able to load this file from classpath.\");\n\t\t}\n\t\tString keyTabLocationAsString = this.keyTabLocation.getURL().toExternalForm();\n\t\t// We need to remove the file prefix (if there is one), as it is not supported in\n\t\t// Java 7 anymore.\n\t\t// As Java 6 accepts it with and without the prefix, we don't need to check for\n\t\t// Java 7\n\t\tif (keyTabLocationAsString.startsWith(\"file:\")) {\n\t\t\tkeyTabLocationAsString = keyTabLocationAsString.substring(5);\n\t\t}\n\t\tLoginConfig loginConfig = new LoginConfig(keyTabLocationAsString, this.servicePrincipal, this.realmName,\n\t\t\t\tthis.multiTier, this.debug, this.refreshKrb5Config);\n\t\tSet<Principal> princ = new HashSet<Principal>(1);\n\t\tprinc.add(new KerberosPrincipal(this.servicePrincipal));\n\t\tSubject sub = new Subject(false, princ, new HashSet<Object>(), new HashSet<Object>());\n\t\tLoginContext lc = new LoginContext(\"\", sub, null, loginConfig);\n\t\tlc.login();\n\t\tthis.serviceSubject = lc.getSubject();\n\t}\n\n\t/**\n\t * The service principal of the application. For web apps this is\n\t * <code>HTTP/full-qualified-domain-name@DOMAIN</code>. The keytab must contain the\n\t * key for this principal.\n\t * @param servicePrincipal service principal to use\n\t * @see #setKeyTabLocation(Resource)\n\t */\n\tpublic void setServicePrincipal(String servicePrincipal) {\n\t\tthis.servicePrincipal = servicePrincipal;\n\t}\n\n\t/**\n\t * The realm name of the application. For web apps this is <code>DOMAIN</code>\n\t * @param realmName\n\t */\n\tpublic void setRealmName(String realmName) {\n\t\tthis.realmName = realmName;\n\t}\n\n\t/**\n\t * @param multiTier\n\t */\n\tpublic void setMultiTier(boolean multiTier) {\n\t\tthis.multiTier = multiTier;\n\t}\n\n\t/**\n\t * <p>\n\t * The location of the keytab. You can use the normal Spring Resource prefixes like\n\t * <code>file:</code> or <code>classpath:</code>, but as the file is later on read by\n\t * JAAS, we cannot guarantee that <code>classpath</code> works in every environment,\n\t * esp. not in Java EE application servers. You should use <code>file:</code> there.\n\t *\n\t * This file also needs special protection, which is another reason to not include it\n\t * in the classpath but rather use <code>file:/etc/http.keytab</code> for example.\n\t * @param keyTabLocation The location where the keytab resides\n\t */\n\tpublic void setKeyTabLocation(Resource keyTabLocation) {\n\t\tthis.keyTabLocation = keyTabLocation;\n\t}\n\n\t/**\n\t * Enables the debug mode of the JAAS Kerberos login module.\n\t * @param debug default is false\n\t */\n\tpublic void setDebug(boolean debug) {\n\t\tthis.debug = debug;\n\t}\n\n\t/**\n\t * Determines whether to hold on to the {@link GSSContext GSS security context} or\n\t * otherwise {@link GSSContext#dispose() dispose} of it immediately (the default\n\t * behaviour).\n\t * <p>\n\t * Holding on to the GSS context allows decrypt and encrypt operations for subsequent\n\t * interactions with the principal.\n\t * @param holdOnToGSSContext true if should hold on to context\n\t */\n\tpublic void setHoldOnToGSSContext(boolean holdOnToGSSContext) {\n\t\tthis.holdOnToGSSContext = holdOnToGSSContext;\n\t}\n\n\t/**\n\t * Enables configuration to be refreshed before the login method is called.\n\t * @param refreshKrb5Config Set this to true, if you want the configuration to be\n\t * refreshed before the login method is called.\n\t */\n\tpublic void setRefreshKrb5Config(boolean refreshKrb5Config) {\n\t\tthis.refreshKrb5Config = refreshKrb5Config;\n\t}\n\n\t/**\n\t * This class is needed, because the validation must run with previously generated\n\t * JAAS subject which belongs to the service principal and was loaded out of the\n\t * keytab during startup.\n\t */\n\tprivate final class KerberosMultitierValidateAction implements PrivilegedExceptionAction<KerberosTicketValidation> {\n\n\t\tbyte[] kerberosTicket;\n\n\t\tprivate KerberosMultitierValidateAction(byte[] kerberosTicket) {\n\t\t\tthis.kerberosTicket = kerberosTicket;\n\t\t}\n\n\t\t@Override\n\t\tpublic KerberosTicketValidation run() throws Exception {\n\t\t\tbyte[] responseToken = new byte[0];\n\t\t\tGSSManager manager = GSSManager.getInstance();\n\n\t\t\tGSSContext context = manager.createContext((GSSCredential) null);\n\n\t\t\twhile (!context.isEstablished()) {\n\t\t\t\tcontext.acceptSecContext(this.kerberosTicket, 0, this.kerberosTicket.length);\n\t\t\t}\n\n\t\t\tSubject subject = GSSUtil.createSubject(context.getSrcName(), context.getDelegCred());\n\n\t\t\tKerberosTicketValidation result = new KerberosTicketValidation(context.getSrcName().toString(), subject,\n\t\t\t\t\tresponseToken, context);\n\n\t\t\tif (!SunJaasKerberosTicketValidator.this.holdOnToGSSContext) {\n\t\t\t\tcontext.dispose();\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n\t/**\n\t * This class is needed, because the validation must run with previously generated\n\t * JAAS subject which belongs to the service principal and was loaded out of the\n\t * keytab during startup.\n\t */\n\tprivate final class KerberosValidateAction implements PrivilegedExceptionAction<KerberosTicketValidation> {\n\n\t\tbyte[] kerberosTicket;\n\n\t\tprivate KerberosValidateAction(byte[] kerberosTicket) {\n\t\t\tthis.kerberosTicket = kerberosTicket;\n\t\t}\n\n\t\t@Override\n\t\tpublic KerberosTicketValidation run() throws Exception {\n\t\t\tbyte[] responseToken = new byte[0];\n\t\t\tGSSName gssName = null;\n\t\t\tGSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);\n\t\t\twhile (!context.isEstablished()) {\n\t\t\t\tresponseToken = context.acceptSecContext(this.kerberosTicket, 0, this.kerberosTicket.length);\n\t\t\t\tgssName = context.getSrcName();\n\t\t\t\tif (gssName == null) {\n\t\t\t\t\tthrow new BadCredentialsException(\"GSSContext name of the context initiator is null\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tGSSCredential delegationCredential = null;\n\t\t\tif (context.getCredDelegState()) {\n\t\t\t\tdelegationCredential = context.getDelegCred();\n\t\t\t}\n\n\t\t\tif (!SunJaasKerberosTicketValidator.this.holdOnToGSSContext) {\n\t\t\t\tcontext.dispose();\n\t\t\t}\n\t\t\tif (gssName == null) {\n\t\t\t\tthrow new BadCredentialsException(\"GSSContext name of the context initiator is null\");\n\t\t\t}\n\t\t\tString servicePrincipal = SunJaasKerberosTicketValidator.this.servicePrincipal;\n\t\t\tif (servicePrincipal == null) {\n\t\t\t\tthrow new IllegalStateException(\"servicePrincipal must be set\");\n\t\t\t}\n\t\t\treturn new KerberosTicketValidation(gssName.toString(), servicePrincipal, responseToken, context,\n\t\t\t\t\tdelegationCredential);\n\t\t}\n\n\t}\n\n\t/**\n\t * Normally you need a JAAS config file in order to use the JAAS Kerberos Login\n\t * Module, with this class it is not needed and you can have different configurations\n\t * in one JVM.\n\t */\n\tprivate static final class LoginConfig extends Configuration {\n\n\t\tprivate String keyTabLocation;\n\n\t\tprivate String servicePrincipalName;\n\n\t\tprivate @Nullable String realmName;\n\n\t\tprivate boolean multiTier;\n\n\t\tprivate boolean debug;\n\n\t\tprivate boolean refreshKrb5Config;\n\n\t\tprivate LoginConfig(String keyTabLocation, String servicePrincipalName, @Nullable String realmName,\n\t\t\t\tboolean multiTier, boolean debug, boolean refreshKrb5Config) {\n\t\t\tthis.keyTabLocation = keyTabLocation;\n\t\t\tthis.servicePrincipalName = servicePrincipalName;\n\t\t\tthis.realmName = realmName;\n\t\t\tthis.multiTier = multiTier;\n\t\t\tthis.debug = debug;\n\t\t\tthis.refreshKrb5Config = refreshKrb5Config;\n\t\t}\n\n\t\t@Override\n\t\tpublic AppConfigurationEntry[] getAppConfigurationEntry(String name) {\n\t\t\tHashMap<String, String> options = new HashMap<String, String>();\n\t\t\toptions.put(\"useKeyTab\", \"true\");\n\t\t\toptions.put(\"keyTab\", this.keyTabLocation);\n\t\t\toptions.put(\"principal\", this.servicePrincipalName);\n\t\t\toptions.put(\"storeKey\", \"true\");\n\t\t\toptions.put(\"doNotPrompt\", \"true\");\n\t\t\tif (this.debug) {\n\t\t\t\toptions.put(\"debug\", \"true\");\n\t\t\t}\n\n\t\t\tif (this.realmName != null) {\n\t\t\t\toptions.put(\"realm\", this.realmName);\n\t\t\t}\n\n\t\t\tif (this.refreshKrb5Config) {\n\t\t\t\toptions.put(\"refreshKrb5Config\", \"true\");\n\t\t\t}\n\n\t\t\tif (!this.multiTier) {\n\t\t\t\toptions.put(\"isInitiator\", \"false\");\n\t\t\t}\n\n\t\t\treturn new AppConfigurationEntry[] {\n\t\t\t\t\tnew AppConfigurationEntry(\"com.sun.security.auth.module.Krb5LoginModule\",\n\t\t\t\t\t\t\tAppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), };\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/java/org/springframework/security/kerberos/authentication/sun/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.kerberos.authentication.sun;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "kerberos/kerberos-core/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.kerberos.aot.hint.KerberosRuntimeHints\n"
  },
  {
    "path": "kerberos/kerberos-core/src/test/java/org/springframework/security/kerberos/authentication/KerberosAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Test class for {@link KerberosAuthenticationProvider}\n *\n * @author Mike Wiesner\n * @since 1.0\n */\npublic class KerberosAuthenticationProviderTests {\n\n\tprivate KerberosAuthenticationProvider provider;\n\n\tprivate KerberosClient kerberosClient;\n\n\tprivate UserDetailsService userDetailsService;\n\n\tprivate static final String TEST_USER = \"Testuser@SPRINGSOURCE.ORG\";\n\n\tprivate static final String TEST_PASSWORD = \"password\";\n\n\tprivate static final UsernamePasswordAuthenticationToken INPUT_TOKEN = new UsernamePasswordAuthenticationToken(\n\t\t\tTEST_USER, TEST_PASSWORD);\n\n\tprivate static final List<GrantedAuthority> AUTHORITY_LIST = AuthorityUtils.createAuthorityList(\"ROLE_ADMIN\");\n\n\tprivate static final UserDetails USER_DETAILS = new User(TEST_USER, \"empty\", true, true, true, true,\n\t\t\tAUTHORITY_LIST);\n\n\tprivate static final JaasSubjectHolder JAAS_SUBJECT_HOLDER = new JaasSubjectHolder(null, TEST_USER);\n\n\t@BeforeEach\n\tpublic void before() {\n\t\t// mocking\n\t\tthis.kerberosClient = mock(KerberosClient.class);\n\t\tthis.userDetailsService = mock(UserDetailsService.class);\n\t\tthis.provider = new KerberosAuthenticationProvider();\n\t\tthis.provider.setKerberosClient(this.kerberosClient);\n\t\tthis.provider.setUserDetailsService(this.userDetailsService);\n\t}\n\n\t@Test\n\tpublic void testLoginOk() throws Exception {\n\t\tgiven(this.userDetailsService.loadUserByUsername(TEST_USER)).willReturn(USER_DETAILS);\n\t\tgiven(this.kerberosClient.login(TEST_USER, TEST_PASSWORD)).willReturn(JAAS_SUBJECT_HOLDER);\n\n\t\tAuthentication authenticate = this.provider.authenticate(INPUT_TOKEN);\n\n\t\tverify(this.kerberosClient).login(TEST_USER, TEST_PASSWORD);\n\n\t\tassertThat(authenticate).isNotNull();\n\t\tassertThat(authenticate.getName()).isEqualTo(TEST_USER);\n\t\tassertThat(authenticate.getPrincipal()).isEqualTo(USER_DETAILS);\n\t\tassertThat(authenticate.getCredentials()).isEqualTo(TEST_PASSWORD);\n\t\tassertThat(authenticate.getAuthorities()).isEqualTo(AUTHORITY_LIST);\n\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/test/java/org/springframework/security/kerberos/authentication/KerberosServiceAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Test class for {@link KerberosServiceAuthenticationProvider}\n *\n * @author Mike Wiesner\n * @author Jeremy Stone\n * @since 1.0\n */\npublic class KerberosServiceAuthenticationProviderTests {\n\n\tprivate KerberosServiceAuthenticationProvider provider;\n\n\tprivate KerberosTicketValidator ticketValidator;\n\n\tprivate UserDetailsService userDetailsService;\n\n\t// data\n\tprivate static final byte[] TEST_TOKEN = \"TestToken\".getBytes();\n\n\tprivate static final byte[] RESPONSE_TOKEN = \"ResponseToken\".getBytes();\n\n\tprivate static final String TEST_USER = \"Testuser@SPRINGSOURCE.ORG\";\n\n\tprivate static final KerberosTicketValidation TICKET_VALIDATION = new KerberosTicketValidation(TEST_USER,\n\t\t\t\"XXX@test.com\", RESPONSE_TOKEN, null);\n\n\tprivate static final List<GrantedAuthority> AUTHORITY_LIST = AuthorityUtils.createAuthorityList(\"ROLE_ADMIN\");\n\n\tprivate static final UserDetails USER_DETAILS = new User(TEST_USER, \"empty\", true, true, true, true,\n\t\t\tAUTHORITY_LIST);\n\n\tprivate static final KerberosServiceRequestToken INPUT_TOKEN = new KerberosServiceRequestToken(TEST_TOKEN);\n\n\t@BeforeEach\n\tpublic void before() {\n\t\tSystem.setProperty(\"java.security.krb5.conf\", \"test.com\");\n\t\tSystem.setProperty(\"java.security.krb5.kdc\", \"kdc.test.com\");\n\t\t// mocking\n\t\tthis.ticketValidator = mock(KerberosTicketValidator.class);\n\t\tthis.userDetailsService = mock(UserDetailsService.class);\n\t\tthis.provider = new KerberosServiceAuthenticationProvider();\n\t\tthis.provider.setTicketValidator(this.ticketValidator);\n\t\tthis.provider.setUserDetailsService(this.userDetailsService);\n\t}\n\n\t@AfterEach\n\tpublic void after() {\n\t\tSystem.clearProperty(\"java.security.krb5.conf\");\n\t\tSystem.clearProperty(\"java.security.krb5.kdc\");\n\t}\n\n\t@Test\n\tpublic void testEverythingWorks() throws Exception {\n\t\tAuthentication output = callProviderAndReturnUser(USER_DETAILS, INPUT_TOKEN);\n\t\tassertThat(output).isNotNull();\n\t\tassertThat(output.getName()).isEqualTo(TEST_USER);\n\t\tassertThat(output.getAuthorities()).isEqualTo(AUTHORITY_LIST);\n\t\tassertThat(output.getPrincipal()).isEqualTo(USER_DETAILS);\n\t}\n\n\t@Test\n\tpublic void testAuthenticationDetailsPropagation() throws Exception {\n\t\tKerberosServiceRequestToken requestToken = new KerberosServiceRequestToken(TEST_TOKEN);\n\t\trequestToken.setDetails(\"TestDetails\");\n\t\tAuthentication output = callProviderAndReturnUser(USER_DETAILS, requestToken);\n\t\tassertThat(output).isNotNull();\n\t\tassertThat(output.getDetails()).isEqualTo(requestToken.getDetails());\n\t}\n\n\t@Test\n\tpublic void testUserIsDisabled() throws Exception {\n\t\tassertThatExceptionOfType(DisabledException.class).isThrownBy(() -> {\n\t\t\tUser disabledUser = new User(TEST_USER, \"empty\", false, true, true, true, AUTHORITY_LIST);\n\t\t\tcallProviderAndReturnUser(disabledUser, INPUT_TOKEN);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void testUserAccountIsExpired() throws Exception {\n\t\tassertThatExceptionOfType(AccountExpiredException.class).isThrownBy(() -> {\n\t\t\tUser expiredUser = new User(TEST_USER, \"empty\", true, false, true, true, AUTHORITY_LIST);\n\t\t\tcallProviderAndReturnUser(expiredUser, INPUT_TOKEN);\n\t\t}).isInstanceOf(AccountExpiredException.class);\n\t}\n\n\t@Test\n\tpublic void testUserCredentialsExpired() throws Exception {\n\t\tassertThatExceptionOfType(CredentialsExpiredException.class).isThrownBy(() -> {\n\t\t\tUser credExpiredUser = new User(TEST_USER, \"empty\", true, true, false, true, AUTHORITY_LIST);\n\t\t\tcallProviderAndReturnUser(credExpiredUser, INPUT_TOKEN);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void testUserAccountLockedCredentialsExpired() throws Exception {\n\t\tassertThatExceptionOfType(LockedException.class).isThrownBy(() -> {\n\t\t\tUser lockedUser = new User(TEST_USER, \"empty\", true, true, true, false, AUTHORITY_LIST);\n\t\t\tcallProviderAndReturnUser(lockedUser, INPUT_TOKEN);\n\t\t});\n\t}\n\n\t@Test\n\tpublic void testUsernameNotFound() throws Exception {\n\t\t// stubbing\n\t\tgiven(this.ticketValidator.validateTicket(TEST_TOKEN)).willReturn(TICKET_VALIDATION);\n\t\tgiven(this.userDetailsService.loadUserByUsername(TEST_USER)).willThrow(new UsernameNotFoundException(\"\"));\n\n\t\t// testing\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(INPUT_TOKEN));\n\t}\n\n\t@Test\n\tpublic void testTicketValidationWrong() throws Exception {\n\t\t// stubbing\n\t\tgiven(this.ticketValidator.validateTicket(TEST_TOKEN)).willThrow(new BadCredentialsException(\"\"));\n\n\t\t// testing\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(INPUT_TOKEN));\n\t}\n\n\tprivate Authentication callProviderAndReturnUser(UserDetails userDetails, Authentication inputToken) {\n\t\t// stubbing\n\t\tgiven(this.ticketValidator.validateTicket(TEST_TOKEN)).willReturn(TICKET_VALIDATION);\n\t\tgiven(this.userDetailsService.loadUserByUsername(TEST_USER)).willReturn(userDetails);\n\n\t\t// testing\n\t\treturn this.provider.authenticate(inputToken);\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/test/java/org/springframework/security/kerberos/authentication/KerberosTicketValidationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication;\n\nimport javax.security.auth.Subject;\n\nimport org.ietf.jgss.GSSContext;\nimport org.ietf.jgss.GSSCredential;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\npublic class KerberosTicketValidationTests {\n\n\tprivate String username = \"username\";\n\n\tprivate Subject subject = new Subject();\n\n\tprivate byte[] responseToken = \"token\".getBytes();\n\n\tprivate GSSContext gssContext = mock(GSSContext.class);\n\n\tprivate GSSCredential delegationCredential = mock(GSSCredential.class);\n\n\t@Test\n\tpublic void createResultOfTicketValidationWithSubject() {\n\n\t\tKerberosTicketValidation ticketValidation = new KerberosTicketValidation(this.username, this.subject,\n\t\t\t\tthis.responseToken, this.gssContext);\n\n\t\tassertThat(ticketValidation.username()).isEqualTo(this.username);\n\t\tassertThat(ticketValidation.responseToken()).isEqualTo(this.responseToken);\n\t\tassertThat(ticketValidation.getGssContext()).isEqualTo(this.gssContext);\n\n\t\tassertThat(ticketValidation.getDelegationCredential()).withFailMessage(\"With no credential delegation\")\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void createResultOfTicketValidationWithSubjectAndDelegation() {\n\n\t\tKerberosTicketValidation ticketValidation = new KerberosTicketValidation(this.username, this.subject,\n\t\t\t\tthis.responseToken, this.gssContext, this.delegationCredential);\n\n\t\tassertThat(ticketValidation.username()).isEqualTo(this.username);\n\t\tassertThat(ticketValidation.responseToken()).isEqualTo(this.responseToken);\n\t\tassertThat(ticketValidation.getGssContext()).isEqualTo(this.gssContext);\n\n\t\tassertThat(ticketValidation.getDelegationCredential()).withFailMessage(\"With credential delegation\")\n\t\t\t.isEqualTo(this.delegationCredential);\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-core/src/test/java/org/springframework/security/kerberos/authentication/sun/SunJaasKerberosTicketValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.authentication.sun;\n\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.BadCredentialsException;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\npublic class SunJaasKerberosTicketValidatorTests {\n\n\t// copy of token taken from a test where windows host\n\t// is trying to authenticate with spnego. nothing sensitive here\n\tprivate static String header = \"YIIGXAYGKwYBBQUCoIIGUDCCBkygMDAuBgkqhkiC9xIBAgIGCSqGSIb3EgEC\"\n\t\t\t+ \"AgYKKwYBBAGCNwICHgYKKwYBBAGCNwICCqKCBhYEggYSYIIGDgYJKoZIhvcS\"\n\t\t\t+ \"AQICAQBuggX9MIIF+aADAgEFoQMCAQ6iBwMFACAAAACjggSFYYIEgTCCBH2g\"\n\t\t\t+ \"AwIBBaENGwtFWEFNUExFLk9SR6IiMCCgAwIBAqEZMBcbBEhUVFAbD25lby5l\"\n\t\t\t+ \"eGFtcGxlLm9yZ6OCBEEwggQ9oAMCARehAwIBA6KCBC8EggQrD8vaEz0V5W5n\"\n\t\t\t+ \"PZINBBxp1yCVZOn4kpHzfNtqj9F3L/6MzrTo9bP2l0UhxCQIKo+ixUMJgQAs\"\n\t\t\t+ \"Xd82tF4JEsSt90pyv8f751pH3UeqCOhssTcXhJpTKQmYlAro+t3klpT6/c/r\"\n\t\t\t+ \"4KX+wqM++19IjWE2CJpyloo/5Wi9Kwk83bjO6UfCTreqkd+eIPM16rf8p/wH\"\n\t\t\t+ \"KYj+ssla4y+IvwvZvAW8TXuth8opiqeLvt5H0GWkwuJhrZu6cHlSWZAMtRQg\"\n\t\t\t+ \"TSZCS/0LCiZVCyNNCpvvXbyp8p5T6ImKPfMO5l8VJKgdrmCOlAQYFwTpG0MD\"\n\t\t\t+ \"1e9LUvk/Fh7OoeglJAygTRgbvIGDAuexw7o6MHbj+XhXvEtC6kUEwHuG5C/1\"\n\t\t\t+ \"5Q327FRLfMeL8YcdU6YZ06wNmUmDPGqy+WHlEaFM7G38u/oKKS4cKIZKi8PL\"\n\t\t\t+ \"hpVPvjU+uIOJVuIP882IxCW7rcqaRCleYCp7YAQbjussrCS0DSRKPEy60bv0\"\n\t\t\t+ \"MIkh71lCY5/KwQloEDMqav12+1wtWTnmLAkfglGjgb1Q7fb79h58nnTBJAwI\"\n\t\t\t+ \"e6Bv72XYdgcU1orDQVlylAk9trxDP42yOGuG5IozJTIn+9zPOvM5CGgTCzZv\"\n\t\t\t+ \"4wInGa1Stuz11WwaIenwGbpCXWSP4uoe9TLpKVzJUmLd8dpZ0YjpuFNBGnHz\"\n\t\t\t+ \"1LG0Q9aUni7nl7seKVc2AnuBqS+mlS+/In0LaEW4k0GctgMqfVyP2mmb7ur+\"\n\t\t\t+ \"wl4YjAVRFhPMSSy4AYftRYoIUGad97VcZx107pD0v/gE1Eu4iqTomqJBOaWJ\"\n\t\t\t+ \"gqnjmf6A8P9IHbeVx/zbnKYp8nC+M57jpFcy9GKVh3DIXkbSBHQ+feamGBJn\"\n\t\t\t+ \"AxTpeix/DN5u91azJaB9RlfIvQYGLGaxupCXpjVfhTSJHvoA6sOUObgK3/hQ\"\n\t\t\t+ \"7Gj81FR+C8AfrHzOPPD2S14pkL7n2WC6jOTHrghxm7/iXcreDHos/1OuPFk0\"\n\t\t\t+ \"9wbrCWgF9tHAuXQJW/zxjYg9CUboJ51+ZposfmABTKoUKeFY4zgVyuEwE2YO\"\n\t\t\t+ \"hn7OLsfbXalmF5IPAlNibAIIFVos1u+14oFOYivIXEEgpvZMhvFOuGaqrHHR\"\n\t\t\t+ \"xRBQ/z8nogMVGyCukFH/tg5N8IX9X+VQ1U43rf4IYaCJ0no5skmStf7fmcUJ\"\n\t\t\t+ \"+3KXhKfP4TKrSIDdo313GW/6rIM2wo4RPdjQ1LlX+EAb8X73W0OZLumtvhm9\"\n\t\t\t+ \"1jL2pWFL/mTGEGkPd7Od29h7JYcvwdDCjkIzIlrbzFJyyTU3ATaMyrvDZKys\"\n\t\t\t+ \"ZSJ2m3v7Y0E/Cw+/T8SG3HeSjJ2e/dsjJRpv+6RxXzdNWKKCUN3UFEH0QfAk\"\n\t\t\t+ \"6s8avEF767U87Df7BBCuecxIJAUL+kBBsYuDCw8FP0AOxOIjh9EX/EopeJpi\"\n\t\t\t+ \"e1ekNGvUK+mhj3WgjCExEe60y4FoENKkggFZMIIBVaADAgEXooIBTASCAUgR\"\n\t\t\t+ \"/FTo9JsQB4yInDswmvHiOyJYGdA9jv72rjvJfdHejaU6L8QHj0DPMdGWxAXI\"\n\t\t\t+ \"aqLrANjOOSGb9HEdt9QUd/zvi8fBEEZgWIX0nUUrvN9wsKEB1jxmlAx87mf7\"\n\t\t\t+ \"2Kyo9z7mdlFBG49mq/jjFFLtiVJxHfea4B4VGRUodNRLWUY7H05ruJZQbeUF\"\n\t\t\t+ \"UgYMsiMC59oi82OR3re8gpypecrtD0g88CwCrReDpoLb7VGVCc4z00ld7ugz\"\n\t\t\t+ \"EbGsZvh0SLMKnxAAm1nYlqQTu/VKC8zi9N0c7ikJegGwBKOgbebPm+ckKDra\"\n\t\t\t+ \"fbVsm0pcmnXv5WvwjJPFjJWsL+7NzUfsedJxgHTCzdztZyNxu6iQf8cpAabp\"\n\t\t\t+ \"PB1vJdIMjc8benP9/+EUhX1LkwvV/rOO3ocwjtdLY1rcmNXSbhnf8jDcVjOe\" + \"eL2PHBfvkne/FgxC\";\n\n\t// @Rule\n\t// public ExpectedException thrown = ExpectedException.none();\n\n\t// @Test\n\t// public void testJdkMsKrb5OIDRegressionTweak() throws Exception {\n\t// thrown.expect(BadCredentialsException.class);\n\t// thrown.expectMessage(not(containsString(\"GSSContext name of the context initiator\n\t// is null\")));\n\t// thrown.expectMessage(containsString(\"Kerberos validation not successful\"));\n\t// SunJaasKerberosTicketValidator validator = new SunJaasKerberosTicketValidator();\n\t// byte[] kerberosTicket = Base64.decode(header.getBytes());\n\t// validator.validateTicket(kerberosTicket);\n\t// }\n\n\t@Test\n\tpublic void testJdkMsKrb5OIDRegressionTweak() {\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> {\n\t\t\tSunJaasKerberosTicketValidator validator = new SunJaasKerberosTicketValidator();\n\t\t\tbyte[] kerberosTicket = Base64.getDecoder().decode(header.getBytes());\n\t\t\tvalidator.validateTicket(kerberosTicket);\n\t\t}).withMessage(\"Kerberos validation not successful\");\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-test/spring-security-kerberos-test.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.spring-module'\n\tid 'javadoc-warnings-error'\n\tid 'compile-warnings-error'\n\tid 'security-nullability'\n}\n\ndescription = 'Spring Security Kerberos Test'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi libs.org.apache.kerby.simplekdc\n\tapi 'org.junit.jupiter:junit-jupiter'\n\ttestImplementation 'org.springframework:spring-test'\n\ttestImplementation 'org.mockito:mockito-junit-jupiter'\n\ttestImplementation libs.org.assertj.assertj.core\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "kerberos/kerberos-test/src/main/java/org/springframework/security/kerberos/test/KerberosSecurityTestcase.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.test;\n\nimport java.io.File;\nimport java.util.Properties;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\n\n/**\n * KerberosSecurityTestcase provides a base class for using MiniKdc with other testcases.\n * KerberosSecurityTestcase starts the MiniKdc (@BeforeEach) before running tests, and\n * stop the MiniKdc (@AfterEach) after the testcases, using default settings (working dir\n * and kdc configurations).\n * <p>\n * Users can directly inherit this class and implement their own test functions using the\n * default settings, or override functions getTestDir() and createMiniKdcConf() to provide\n * new settings.\n *\n */\npublic class KerberosSecurityTestcase {\n\n\tprivate @Nullable MiniKdc kdc;\n\n\tprivate @Nullable File workDir;\n\n\tprivate @Nullable Properties conf;\n\n\t@BeforeEach\n\tpublic void startMiniKdc() throws Exception {\n\t\tcreateTestDir();\n\t\tcreateMiniKdcConf();\n\n\t\tif (this.conf == null) {\n\t\t\tthrow new IllegalStateException(\"conf must be initialized\");\n\t\t}\n\t\tif (this.workDir == null) {\n\t\t\tthrow new IllegalStateException(\"workDir must be initialized\");\n\t\t}\n\t\tthis.kdc = new MiniKdc(this.conf, this.workDir);\n\t\tthis.kdc.start();\n\t}\n\n\t/**\n\t * Create a working directory, it should be the build directory. Under this directory\n\t * an ApacheDS working directory will be created, this directory will be deleted when\n\t * the MiniKdc stops.\n\t */\n\tpublic void createTestDir() {\n\t\tthis.workDir = new File(System.getProperty(\"test.dir\", \"target\"));\n\t}\n\n\t/**\n\t * Create a Kdc configuration\n\t */\n\tpublic void createMiniKdcConf() {\n\t\tthis.conf = MiniKdc.createConf();\n\t}\n\n\t@AfterEach\n\tpublic void stopMiniKdc() {\n\t\tif (this.kdc != null) {\n\t\t\tthis.kdc.stop();\n\t\t}\n\t}\n\n\tpublic @Nullable MiniKdc getKdc() {\n\t\treturn this.kdc;\n\t}\n\n\tpublic @Nullable File getWorkDir() {\n\t\treturn this.workDir;\n\t}\n\n\tpublic @Nullable Properties getConf() {\n\t\treturn this.conf;\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-test/src/main/java/org/springframework/security/kerberos/test/MiniKdc.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.test;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport org.apache.kerby.kerberos.kerb.KrbException;\nimport org.apache.kerby.kerberos.kerb.server.KdcConfigKey;\nimport org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;\nimport org.apache.kerby.util.IOUtil;\nimport org.apache.kerby.util.NetworkUtil;\nimport org.jspecify.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Mini KDC based on Apache Directory Server that can be embedded in testcases or used\n * from command line as a standalone KDC.\n * <p>\n * <b>From within testcases:</b>\n * <p>\n * MiniKdc sets one System property when started and un-set when stopped:\n * <ul>\n * <li>sun.security.krb5.debug: set to the debug value provided in the configuration</li>\n * </ul>\n * Because of this, multiple MiniKdc instances cannot be started in parallel. For example,\n * running testcases in parallel that start a KDC each. To accomplish this a single\n * MiniKdc should be used for all testcases running in parallel.\n * <p>\n * MiniKdc default configuration values are:\n * <ul>\n * <li>org.name=EXAMPLE (used to create the REALM)</li>\n * <li>org.domain=COM (used to create the REALM)</li>\n * <li>kdc.bind.address=localhost</li>\n * <li>kdc.port=0 (ephemeral port)</li>\n * <li>instance=DefaultKrbServer</li>\n * <li>max.ticket.lifetime=86400000 (1 day)</li>\n * <li>max.renewable.lifetime=604800000 (7 days)</li>\n * <li>transport=TCP</li>\n * <li>debug=false</li>\n * </ul>\n * The generated krb5.conf forces TCP connections.\n *\n * @author Original Hadoop MiniKdc Authors\n * @author Janne Valkealahti\n * @author Bogdan Mustiata\n */\npublic class MiniKdc {\n\n\tpublic static final String JAVA_SECURITY_KRB5_CONF = \"java.security.krb5.conf\";\n\n\tpublic static final String SUN_SECURITY_KRB5_DEBUG = \"sun.security.krb5.debug\";\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tif (args.length < 4) {\n\t\t\tSystem.out.println(\"Arguments: <WORKDIR> <MINIKDCPROPERTIES> \" + \"<KEYTABFILE> [<PRINCIPALS>]+\");\n\t\t\tSystem.exit(1);\n\t\t}\n\t\tFile workDir = new File(args[0]);\n\t\tif (!workDir.exists()) {\n\t\t\tthrow new RuntimeException(\"Specified work directory does not exists: \" + workDir.getAbsolutePath());\n\t\t}\n\t\tProperties conf = createConf();\n\t\tFile file = new File(args[1]);\n\t\tif (!file.exists()) {\n\t\t\tthrow new RuntimeException(\"Specified configuration does not exists: \" + file.getAbsolutePath());\n\t\t}\n\t\tProperties userConf = new Properties();\n\t\tInputStreamReader r = null;\n\t\ttry {\n\t\t\tr = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);\n\t\t\tuserConf.load(r);\n\t\t}\n\t\tfinally {\n\t\t\tif (r != null) {\n\t\t\t\tr.close();\n\t\t\t}\n\t\t}\n\t\tfor (Map.Entry<?, ?> entry : userConf.entrySet()) {\n\t\t\tconf.put(entry.getKey(), entry.getValue());\n\t\t}\n\t\tfinal MiniKdc miniKdc = new MiniKdc(conf, workDir);\n\t\tminiKdc.start();\n\t\tFile krb5conf = new File(workDir, \"krb5.conf\");\n\t\tif (miniKdc.getKrb5conf().renameTo(krb5conf)) {\n\t\t\tFile keytabFile = new File(args[2]).getAbsoluteFile();\n\t\t\tString[] principals = new String[args.length - 3];\n\t\t\tSystem.arraycopy(args, 3, principals, 0, args.length - 3);\n\t\t\tminiKdc.createPrincipal(keytabFile, principals);\n\t\t\tSystem.out.println();\n\t\t\tSystem.out.println(\"Standalone MiniKdc Running\");\n\t\t\tSystem.out.println(\"---------------------------------------------------\");\n\t\t\tSystem.out.println(\"  Realm           : \" + miniKdc.getRealm());\n\t\t\tSystem.out.println(\"  Running at      : \" + miniKdc.getHost() + \":\" + miniKdc.getPort());\n\t\t\tSystem.out.println(\"  krb5conf        : \" + krb5conf);\n\t\t\tSystem.out.println();\n\t\t\tSystem.out.println(\"  created keytab  : \" + keytabFile);\n\t\t\tSystem.out.println(\"  with principals : \" + Arrays.asList(principals));\n\t\t\tSystem.out.println();\n\t\t\tSystem.out.println(\" Do <CTRL-C> or kill <PID> to stop it\");\n\t\t\tSystem.out.println(\"---------------------------------------------------\");\n\t\t\tSystem.out.println();\n\t\t\tRuntime.getRuntime().addShutdownHook(new Thread() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tminiKdc.stop();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\tthrow new RuntimeException(\"Cannot rename KDC's krb5conf to \" + krb5conf.getAbsolutePath());\n\t\t}\n\t}\n\n\tprivate static final Logger LOG = LoggerFactory.getLogger(MiniKdc.class);\n\n\tpublic static final String ORG_NAME = \"org.name\";\n\n\tpublic static final String ORG_DOMAIN = \"org.domain\";\n\n\tpublic static final String KDC_BIND_ADDRESS = \"kdc.bind.address\";\n\n\tpublic static final String KDC_PORT = \"kdc.port\";\n\n\tpublic static final String INSTANCE = \"instance\";\n\n\tpublic static final String MAX_TICKET_LIFETIME = \"max.ticket.lifetime\";\n\n\tpublic static final String MIN_TICKET_LIFETIME = \"min.ticket.lifetime\";\n\n\tpublic static final String MAX_RENEWABLE_LIFETIME = \"max.renewable.lifetime\";\n\n\tpublic static final String TRANSPORT = \"transport\";\n\n\tpublic static final String DEBUG = \"debug\";\n\n\tprivate static final Set<String> PROPERTIES = new HashSet<String>();\n\n\tprivate static final Properties DEFAULT_CONFIG = new Properties();\n\n\tstatic {\n\t\tPROPERTIES.add(ORG_NAME);\n\t\tPROPERTIES.add(ORG_DOMAIN);\n\t\tPROPERTIES.add(KDC_BIND_ADDRESS);\n\t\tPROPERTIES.add(KDC_BIND_ADDRESS);\n\t\tPROPERTIES.add(KDC_PORT);\n\t\tPROPERTIES.add(INSTANCE);\n\t\tPROPERTIES.add(TRANSPORT);\n\t\tPROPERTIES.add(MAX_TICKET_LIFETIME);\n\t\tPROPERTIES.add(MAX_RENEWABLE_LIFETIME);\n\n\t\tDEFAULT_CONFIG.setProperty(KDC_BIND_ADDRESS, \"localhost\");\n\t\tDEFAULT_CONFIG.setProperty(KDC_PORT, \"0\");\n\t\tDEFAULT_CONFIG.setProperty(INSTANCE, \"DefaultKrbServer\");\n\t\tDEFAULT_CONFIG.setProperty(ORG_NAME, \"EXAMPLE\");\n\t\tDEFAULT_CONFIG.setProperty(ORG_DOMAIN, \"COM\");\n\t\tDEFAULT_CONFIG.setProperty(TRANSPORT, \"TCP\");\n\t\tDEFAULT_CONFIG.setProperty(MAX_TICKET_LIFETIME, \"86400000\");\n\t\tDEFAULT_CONFIG.setProperty(MAX_RENEWABLE_LIFETIME, \"604800000\");\n\t\tDEFAULT_CONFIG.setProperty(DEBUG, \"false\");\n\t}\n\n\t/**\n\t * Convenience method that returns MiniKdc default configuration.\n\t * <p>\n\t * The returned configuration is a copy, it can be customized before using it to\n\t * create a MiniKdc.\n\t * @return a MiniKdc default configuration.\n\t */\n\tpublic static Properties createConf() {\n\t\treturn (Properties) DEFAULT_CONFIG.clone();\n\t}\n\n\tprivate Properties conf;\n\n\tprivate @Nullable SimpleKdcServer simpleKdc;\n\n\tprivate int port;\n\n\tprivate String realm;\n\n\tprivate File workDir;\n\n\tprivate @Nullable File krb5conf;\n\n\tprivate @Nullable String transport;\n\n\tprivate boolean krb5Debug;\n\n\tpublic void setTransport(String transport) {\n\t\tthis.transport = transport;\n\t}\n\n\t/**\n\t * Creates a MiniKdc.\n\t * @param conf MiniKdc configuration.\n\t * @param workDir working directory, it should be the build directory. Under this\n\t * directory an ApacheDS working directory will be created, this directory will be\n\t * deleted when the MiniKdc stops.\n\t * @throws Exception thrown if the MiniKdc could not be created.\n\t */\n\tpublic MiniKdc(Properties conf, File workDir) throws Exception {\n\t\tif (!conf.keySet().containsAll(PROPERTIES)) {\n\t\t\tSet<String> missingProperties = new HashSet<String>(PROPERTIES);\n\t\t\tmissingProperties.removeAll(conf.keySet());\n\t\t\tthrow new IllegalArgumentException(\"Missing configuration properties: \" + missingProperties);\n\t\t}\n\t\tthis.workDir = new File(workDir, Long.toString(System.currentTimeMillis()));\n\t\tif (!this.workDir.exists() && !this.workDir.mkdirs()) {\n\t\t\tthrow new RuntimeException(\"Cannot create directory \" + this.workDir);\n\t\t}\n\t\tLOG.info(\"Configuration:\");\n\t\tLOG.info(\"---------------------------------------------------------------\");\n\t\tfor (Map.Entry<?, ?> entry : conf.entrySet()) {\n\t\t\tLOG.info(\"  {}: {}\", entry.getKey(), entry.getValue());\n\t\t}\n\t\tLOG.info(\"---------------------------------------------------------------\");\n\t\tthis.conf = conf;\n\t\tthis.port = Integer.parseInt(conf.getProperty(KDC_PORT));\n\t\tString orgName = conf.getProperty(ORG_NAME);\n\t\tString orgDomain = conf.getProperty(ORG_DOMAIN);\n\t\tthis.realm = orgName.toUpperCase(Locale.ENGLISH) + \".\" + orgDomain.toUpperCase(Locale.ENGLISH);\n\t}\n\n\t/**\n\t * Returns the port of the MiniKdc.\n\t * @return the port of the MiniKdc.\n\t */\n\tpublic int getPort() {\n\t\treturn this.port;\n\t}\n\n\t/**\n\t * Returns the host of the MiniKdc.\n\t * @return the host of the MiniKdc.\n\t */\n\tpublic String getHost() {\n\t\treturn this.conf.getProperty(KDC_BIND_ADDRESS);\n\t}\n\n\t/**\n\t * Returns the realm of the MiniKdc.\n\t * @return the realm of the MiniKdc.\n\t */\n\tpublic String getRealm() {\n\t\treturn this.realm;\n\t}\n\n\tpublic File getKrb5conf() {\n\t\tthis.krb5conf = new File(System.getProperty(JAVA_SECURITY_KRB5_CONF));\n\t\treturn this.krb5conf;\n\t}\n\n\t/**\n\t * Starts the MiniKdc.\n\t * @throws Exception thrown if the MiniKdc could not be started.\n\t */\n\tpublic synchronized void start() throws Exception {\n\t\tif (this.simpleKdc != null) {\n\t\t\tthrow new RuntimeException(\"Already started\");\n\t\t}\n\t\tthis.simpleKdc = new SimpleKdcServer();\n\t\tprepareKdcServer();\n\t\tthis.simpleKdc.init();\n\t\tresetDefaultRealm();\n\t\tthis.simpleKdc.start();\n\t\tLOG.info(\"MiniKdc started.\");\n\t}\n\n\tprivate void resetDefaultRealm() throws IOException {\n\t\tInputStream templateResource = new FileInputStream(getKrb5conf().getAbsolutePath());\n\t\tString content = IOUtil.readInput(templateResource);\n\t\tcontent = content.replaceAll(\"default_realm = .*\\n\", \"default_realm = \" + getRealm() + \"\\n\");\n\t\tIOUtil.writeFile(content, getKrb5conf());\n\t}\n\n\tprivate void prepareKdcServer() throws Exception {\n\t\t// transport\n\t\tif (this.simpleKdc == null) {\n\t\t\tthrow new IllegalStateException(\"simpleKdc must be initialized\");\n\t\t}\n\t\tthis.simpleKdc.setWorkDir(this.workDir);\n\t\tthis.simpleKdc.setKdcHost(getHost());\n\t\tthis.simpleKdc.setKdcRealm(this.realm);\n\t\tif (this.transport == null) {\n\t\t\tthis.transport = this.conf.getProperty(TRANSPORT);\n\t\t}\n\t\tif (this.port == 0) {\n\t\t\tthis.port = NetworkUtil.getServerPort();\n\t\t}\n\t\tif (this.transport != null) {\n\t\t\tif (this.transport.trim().equals(\"TCP\")) {\n\t\t\t\tthis.simpleKdc.setKdcTcpPort(this.port);\n\t\t\t\tthis.simpleKdc.setAllowUdp(false);\n\t\t\t}\n\t\t\telse if (this.transport.trim().equals(\"UDP\")) {\n\t\t\t\tthis.simpleKdc.setKdcUdpPort(this.port);\n\t\t\t\tthis.simpleKdc.setAllowTcp(false);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new IllegalArgumentException(\"Invalid transport: \" + this.transport);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\"Need to set transport!\");\n\t\t}\n\t\tthis.simpleKdc.getKdcConfig().setString(KdcConfigKey.KDC_SERVICE_NAME, this.conf.getProperty(INSTANCE));\n\t\tif (this.conf.getProperty(DEBUG) != null) {\n\t\t\tthis.krb5Debug = getAndSet(SUN_SECURITY_KRB5_DEBUG, this.conf.getProperty(DEBUG));\n\t\t}\n\t\tif (this.conf.getProperty(MIN_TICKET_LIFETIME) != null) {\n\t\t\tthis.simpleKdc.getKdcConfig()\n\t\t\t\t.setLong(KdcConfigKey.MINIMUM_TICKET_LIFETIME,\n\t\t\t\t\t\tLong.parseLong(this.conf.getProperty(MIN_TICKET_LIFETIME)));\n\t\t}\n\t\tif (this.conf.getProperty(MAX_TICKET_LIFETIME) != null) {\n\t\t\tthis.simpleKdc.getKdcConfig()\n\t\t\t\t.setLong(KdcConfigKey.MAXIMUM_TICKET_LIFETIME,\n\t\t\t\t\t\tLong.parseLong(this.conf.getProperty(MiniKdc.MAX_TICKET_LIFETIME)));\n\t\t}\n\t}\n\n\t/**\n\t * Stops the MiniKdc\n\t */\n\tpublic synchronized void stop() {\n\t\tif (this.simpleKdc != null) {\n\t\t\ttry {\n\t\t\t\tthis.simpleKdc.stop();\n\t\t\t}\n\t\t\tcatch (KrbException ex) {\n\t\t\t\tex.printStackTrace();\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tif (this.conf.getProperty(DEBUG) != null) {\n\t\t\t\t\tSystem.setProperty(SUN_SECURITY_KRB5_DEBUG, Boolean.toString(this.krb5Debug));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdelete(this.workDir);\n\t\ttry {\n\t\t\t// Will be fixed in next Kerby version.\n\t\t\tThread.sleep(1000);\n\t\t}\n\t\tcatch (InterruptedException ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\tLOG.info(\"MiniKdc stopped.\");\n\t}\n\n\tprivate void delete(File f) {\n\t\tif (f.isFile()) {\n\t\t\tif (!f.delete()) {\n\t\t\t\tLOG.warn(\"WARNING: cannot delete file \" + f.getAbsolutePath());\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tFile[] fileList = f.listFiles();\n\t\t\tif (fileList != null) {\n\t\t\t\tfor (File c : fileList) {\n\t\t\t\t\tdelete(c);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!f.delete()) {\n\t\t\t\tLOG.warn(\"WARNING: cannot delete directory \" + f.getAbsolutePath());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Creates a principal in the KDC with the specified user and password.\n\t * @param principal principal name, do not include the domain.\n\t * @param password password.\n\t * @throws Exception thrown if the principal could not be created.\n\t */\n\tpublic synchronized void createPrincipal(String principal, String password) throws Exception {\n\t\tif (this.simpleKdc == null) {\n\t\t\tthrow new IllegalStateException(\"MiniKdc must be started before creating principals\");\n\t\t}\n\t\tthis.simpleKdc.createPrincipal(principal, password);\n\t}\n\n\t/**\n\t * Creates multiple principals in the KDC and adds them to a keytab file.\n\t * @param keytabFile keytab file to add the created principals.\n\t * @param principals principals to add to the KDC, do not include the domain.\n\t * @throws Exception thrown if the principals or the keytab file could not be created.\n\t */\n\tpublic synchronized void createPrincipal(File keytabFile, String... principals) throws Exception {\n\t\tif (this.simpleKdc == null) {\n\t\t\tthrow new IllegalStateException(\"MiniKdc must be started before creating principals\");\n\t\t}\n\t\tthis.simpleKdc.createPrincipals(principals);\n\t\tif (keytabFile.exists() && !keytabFile.delete()) {\n\t\t\tLOG.error(\"Failed to delete keytab file: \" + keytabFile);\n\t\t}\n\t\tfor (String principal : principals) {\n\t\t\tthis.simpleKdc.getKadmin().exportKeytab(keytabFile, principal);\n\t\t}\n\t}\n\n\t/**\n\t * Set the System property; return the old value for caching.\n\t * @param sysprop property\n\t * @param debug true or false\n\t * @return the previous value\n\t */\n\tprivate boolean getAndSet(String sysprop, String debug) {\n\t\tboolean old = Boolean.getBoolean(sysprop);\n\t\tSystem.setProperty(sysprop, debug);\n\t\treturn old;\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-test/src/main/java/org/springframework/security/kerberos/test/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.kerberos.test;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "kerberos/kerberos-test/src/test/java/org/springframework/security/kerberos/test/TestMiniKdc.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.test;\n\nimport java.io.File;\nimport java.security.Principal;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.kerberos.KerberosPrincipal;\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\n\nimport org.apache.kerby.kerberos.kerb.keytab.Keytab;\nimport org.apache.kerby.kerberos.kerb.type.base.PrincipalName;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TestMiniKdc extends KerberosSecurityTestcase {\n\n\tprivate static final boolean IBM_JAVA = shouldUseIbmPackages();\n\n\t// duplicated to avoid cycles in the build\n\tprivate static boolean shouldUseIbmPackages() {\n\t\tfinal List<String> ibmTechnologyEditionSecurityModules = Arrays.asList(\n\t\t\t\t\"com.ibm.security.auth.module.JAASLoginModule\", \"com.ibm.security.auth.module.Win64LoginModule\",\n\t\t\t\t\"com.ibm.security.auth.module.NTLoginModule\", \"com.ibm.security.auth.module.AIX64LoginModule\",\n\t\t\t\t\"com.ibm.security.auth.module.LinuxLoginModule\", \"com.ibm.security.auth.module.Krb5LoginModule\");\n\n\t\tif (System.getProperty(\"java.vendor\").contains(\"IBM\")) {\n\t\t\treturn ibmTechnologyEditionSecurityModules.stream().anyMatch((module) -> isSystemClassAvailable(module));\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Test\n\tpublic void testKerberosLogin() throws Exception {\n\t\tMiniKdc kdc = getKdc();\n\t\tFile workDir = getWorkDir();\n\t\tLoginContext loginContext = null;\n\t\ttry {\n\t\t\tString principal = \"foo\";\n\t\t\tFile keytab = new File(workDir, \"foo.keytab\");\n\t\t\tkdc.createPrincipal(keytab, principal);\n\n\t\t\tSet<Principal> principals = new HashSet<Principal>();\n\t\t\tprincipals.add(new KerberosPrincipal(principal));\n\n\t\t\t// client login\n\t\t\tSubject subject = new Subject(false, principals, new HashSet<Object>(), new HashSet<Object>());\n\t\t\tloginContext = new LoginContext(\"\", subject, null,\n\t\t\t\t\tKerberosConfiguration.createClientConfig(principal, keytab));\n\t\t\tloginContext.login();\n\t\t\tsubject = loginContext.getSubject();\n\t\t\tassertThat(subject.getPrincipals().size()).isEqualTo(1);\n\t\t\tassertThat(subject.getPrincipals().iterator().next().getClass()).isEqualTo(KerberosPrincipal.class);\n\t\t\tassertThat(subject.getPrincipals().iterator().next().getName()).isEqualTo(principal + \"@\" + kdc.getRealm());\n\t\t\tloginContext.logout();\n\n\t\t\t// server login\n\t\t\tsubject = new Subject(false, principals, new HashSet<Object>(), new HashSet<Object>());\n\t\t\tloginContext = new LoginContext(\"\", subject, null,\n\t\t\t\t\tKerberosConfiguration.createServerConfig(principal, keytab));\n\t\t\tloginContext.login();\n\t\t\tsubject = loginContext.getSubject();\n\t\t\tassertThat(subject.getPrincipals().size()).isEqualTo(1);\n\t\t\tassertThat(subject.getPrincipals().iterator().next().getClass()).isEqualTo(KerberosPrincipal.class);\n\t\t\tassertThat(subject.getPrincipals().iterator().next().getName()).isEqualTo(principal + \"@\" + kdc.getRealm());\n\t\t\tloginContext.logout();\n\n\t\t}\n\t\tfinally {\n\t\t\tif (loginContext != null && loginContext.getSubject() != null\n\t\t\t\t\t&& !loginContext.getSubject().getPrivateCredentials().isEmpty()) {\n\t\t\t\tloginContext.logout();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean isSystemClassAvailable(String className) {\n\t\ttry {\n\t\t\tClass.forName(className);\n\t\t\treturn true;\n\t\t}\n\t\tcatch (Exception ignored) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testMiniKdcStart() {\n\t\tMiniKdc kdc = getKdc();\n\t\tassertThat(kdc.getPort()).isNotEqualTo(0);\n\t}\n\n\t@Test\n\tpublic void testKeytabGen() throws Exception {\n\t\tMiniKdc kdc = getKdc();\n\t\tFile workDir = getWorkDir();\n\n\t\tkdc.createPrincipal(new File(workDir, \"keytab\"), \"foo/bar\", \"bar/foo\");\n\t\tList<PrincipalName> principalNameList = Keytab.loadKeytab(new File(workDir, \"keytab\")).getPrincipals();\n\n\t\tSet<String> principals = new HashSet<String>();\n\t\tfor (PrincipalName principalName : principalNameList) {\n\t\t\tprincipals.add(principalName.getName());\n\t\t}\n\n\t\tassertThat(principals).containsExactlyInAnyOrder(\"foo/bar@\" + kdc.getRealm(), \"bar/foo@\" + kdc.getRealm());\n\n\t}\n\n\tprivate static final class KerberosConfiguration extends Configuration {\n\n\t\tprivate String principal;\n\n\t\tprivate String keytab;\n\n\t\tprivate boolean isInitiator;\n\n\t\tprivate KerberosConfiguration(String principal, File keytab, boolean client) {\n\t\t\tthis.principal = principal;\n\t\t\tthis.keytab = keytab.getAbsolutePath();\n\t\t\tthis.isInitiator = client;\n\t\t}\n\n\t\tprivate static Configuration createClientConfig(String principal, File keytab) {\n\t\t\treturn new KerberosConfiguration(principal, keytab, true);\n\t\t}\n\n\t\tprivate static Configuration createServerConfig(String principal, File keytab) {\n\t\t\treturn new KerberosConfiguration(principal, keytab, false);\n\t\t}\n\n\t\tprivate static String getKrb5LoginModuleName() {\n\t\t\treturn System.getProperty(\"java.vendor\").contains(\"IBM\") ? \"com.ibm.security.auth.module.Krb5LoginModule\"\n\t\t\t\t\t: \"com.sun.security.auth.module.Krb5LoginModule\";\n\t\t}\n\n\t\t@Override\n\t\tpublic AppConfigurationEntry[] getAppConfigurationEntry(String name) {\n\t\t\tMap<String, String> options = new HashMap<String, String>();\n\t\t\toptions.put(\"principal\", this.principal);\n\t\t\toptions.put(\"refreshKrb5Config\", \"true\");\n\t\t\tif (IBM_JAVA) {\n\t\t\t\toptions.put(\"useKeytab\", this.keytab);\n\t\t\t\toptions.put(\"credsType\", \"both\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\toptions.put(\"keyTab\", this.keytab);\n\t\t\t\toptions.put(\"useKeyTab\", \"true\");\n\t\t\t\toptions.put(\"storeKey\", \"true\");\n\t\t\t\toptions.put(\"doNotPrompt\", \"true\");\n\t\t\t\toptions.put(\"useTicketCache\", \"true\");\n\t\t\t\toptions.put(\"renewTGT\", \"true\");\n\t\t\t\toptions.put(\"isInitiator\", Boolean.toString(this.isInitiator));\n\t\t\t}\n\t\t\tString ticketCache = System.getenv(\"KRB5CCNAME\");\n\t\t\tif (ticketCache != null) {\n\t\t\t\toptions.put(\"ticketCache\", ticketCache);\n\t\t\t}\n\t\t\toptions.put(\"debug\", \"true\");\n\n\t\t\treturn new AppConfigurationEntry[] { new AppConfigurationEntry(getKrb5LoginModuleName(),\n\t\t\t\t\tAppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) };\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-test/src/test/resources/log4j.properties",
    "content": "log4j.rootCategory=INFO, stdout\n\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2} - %m%n\n\nlog4j.category.org.springframework.boot=INFO\nxlog4j.category.org.apache.http.wire=TRACE\nxlog4j.category.org.apache.http.headers=TRACE\n\n"
  },
  {
    "path": "kerberos/kerberos-test/src/test/resources/minikdc-krb5.conf",
    "content": "#\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n[libdefaults]\n    default_realm = {0}\n    udp_preference_limit = 1\n\n[realms]\n    {0} = '{'\n        kdc = {1}:{2}\n    '}'"
  },
  {
    "path": "kerberos/kerberos-test/src/test/resources/minikdc.ldiff",
    "content": "#\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\ndn: ou=users,dc=${0},dc=${1}\nobjectClass: organizationalUnit\nobjectClass: top\nou: users\n\ndn: uid=krbtgt,ou=users,dc=${0},dc=${1}\nobjectClass: top\nobjectClass: person\nobjectClass: inetOrgPerson\nobjectClass: krb5principal\nobjectClass: krb5kdcentry\ncn: KDC Service\nsn: Service\nuid: krbtgt\nuserPassword: secret\nkrb5PrincipalName: krbtgt/${2}.${3}@${2}.${3}\nkrb5KeyVersionNumber: 0\n\ndn: uid=ldap,ou=users,dc=${0},dc=${1}\nobjectClass: top\nobjectClass: person\nobjectClass: inetOrgPerson\nobjectClass: krb5principal\nobjectClass: krb5kdcentry\ncn: LDAP\nsn: Service\nuid: ldap\nuserPassword: secret\nkrb5PrincipalName: ldap/${4}@${2}.${3}\nkrb5KeyVersionNumber: 0"
  },
  {
    "path": "kerberos/kerberos-web/spring-security-kerberos-web.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'io.spring.convention.spring-module'\n\tid 'javadoc-warnings-error'\n\tid 'compile-warnings-error'\n}\n\ndescription = 'Spring Security Kerberos Web'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\timplementation project(':spring-security-kerberos-core')\n\tapi(project(':spring-security-web'))\n\tapi(libs.jakarta.servlet.jakarta.servlet.api)\n\ttestImplementation 'org.springframework:spring-test'\n\ttestImplementation project(':spring-security-config')\n\ttestImplementation 'org.junit.jupiter:junit-jupiter'\n\ttestImplementation 'org.mockito:mockito-junit-jupiter'\n\ttestImplementation libs.org.assertj.assertj.core\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "kerberos/kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/ResponseHeaderSettingKerberosAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.kerberos.authentication.KerberosServiceRequestToken;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\n\n/**\n * Adds a WWW-Authenticate (or other) header to the response following successful\n * authentication.\n *\n * @author Jeremy Stone\n */\npublic class ResponseHeaderSettingKerberosAuthenticationSuccessHandler implements AuthenticationSuccessHandler {\n\n\tprivate static final String NEGOTIATE_PREFIX = \"Negotiate \";\n\n\tprivate static final String WWW_AUTHENTICATE = \"WWW-Authenticate\";\n\n\tprivate String headerName = WWW_AUTHENTICATE;\n\n\tprivate String headerPrefix = NEGOTIATE_PREFIX;\n\n\t/**\n\t * Sets the name of the header to set. By default this is 'WWW-Authenticate'.\n\t * @param headerName the www authenticate header name\n\t */\n\tpublic void setHeaderName(String headerName) {\n\t\tthis.headerName = headerName;\n\t}\n\n\t/**\n\t * Sets the value of the prefix for the encoded response token value. By default this\n\t * is 'Negotiate '.\n\t * @param headerPrefix the negotiate prefix\n\t */\n\tpublic void setHeaderPrefix(String headerPrefix) {\n\t\tthis.headerPrefix = headerPrefix;\n\t}\n\n\t@Override\n\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException, ServletException {\n\t\tKerberosServiceRequestToken auth = (KerberosServiceRequestToken) authentication;\n\t\tif (auth.hasResponseToken()) {\n\t\t\tresponse.addHeader(this.headerName, this.headerPrefix + auth.getEncodedResponseToken());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/SpnegoAuthenticationProcessingFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.web.authentication;\n\nimport java.io.IOException;\nimport java.util.Base64;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;\nimport org.springframework.security.kerberos.authentication.KerberosServiceRequestToken;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Parses the SPNEGO authentication Header, which was generated by the browser and creates\n * a {@link KerberosServiceRequestToken} out of it. It will then call the\n * {@link AuthenticationManager}.\n *\n * <p>\n * A typical Spring Security configuration might look like this:\n * </p>\n *\n * <pre>\n * &lt;beans xmlns=&quot;https://www.springframework.org/schema/beans&quot;\n * xmlns:xsi=&quot;https://www.w3.org/2001/XMLSchema-instance&quot; xmlns:sec=&quot;https://www.springframework.org/schema/security&quot;\n * xsi:schemaLocation=&quot;https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n * \thttps://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-3.0.xsd&quot;&gt;\n *\n * &lt;sec:http entry-point-ref=&quot;spnegoEntryPoint&quot;&gt;\n * \t&lt;sec:intercept-url pattern=&quot;/secure/**&quot; access=&quot;IS_AUTHENTICATED_FULLY&quot; /&gt;\n * \t&lt;sec:custom-filter ref=&quot;spnegoAuthenticationProcessingFilter&quot; position=&quot;BASIC_AUTH_FILTER&quot; /&gt;\n * &lt;/sec:http&gt;\n *\n * &lt;bean id=&quot;spnegoEntryPoint&quot; class=&quot;org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint&quot; /&gt;\n *\n * &lt;bean id=&quot;spnegoAuthenticationProcessingFilter&quot;\n * \tclass=&quot;org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter&quot;&gt;\n * \t&lt;property name=&quot;authenticationManager&quot; ref=&quot;authenticationManager&quot; /&gt;\n * &lt;/bean&gt;\n *\n * &lt;sec:authentication-manager alias=&quot;authenticationManager&quot;&gt;\n * \t&lt;sec:authentication-provider ref=&quot;kerberosServiceAuthenticationProvider&quot; /&gt;\n * &lt;/sec:authentication-manager&gt;\n *\n * &lt;bean id=&quot;kerberosServiceAuthenticationProvider&quot;\n * \tclass=&quot;org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider&quot;&gt;\n * \t&lt;property name=&quot;ticketValidator&quot;&gt;\n * \t\t&lt;bean class=&quot;org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator&quot;&gt;\n * \t\t\t&lt;property name=&quot;servicePrincipal&quot; value=&quot;HTTP/web.springsource.com&quot; /&gt;\n * \t\t\t&lt;property name=&quot;keyTabLocation&quot; value=&quot;classpath:http-java.keytab&quot; /&gt;\n * \t\t&lt;/bean&gt;\n * \t&lt;/property&gt;\n * \t&lt;property name=&quot;userDetailsService&quot; ref=&quot;inMemoryUserDetailsService&quot; /&gt;\n * &lt;/bean&gt;\n *\n * &lt;bean id=&quot;inMemoryUserDetailsService&quot;\n * \tclass=&quot;org.springframework.security.core.userdetails.memory.InMemoryDaoImpl&quot;&gt;\n * \t&lt;property name=&quot;userProperties&quot;&gt;\n * \t\t&lt;value&gt;\n * \t\t\tmike@SECPOD.DE=notUsed,ROLE_ADMIN\n * \t\t&lt;/value&gt;\n * \t&lt;/property&gt;\n * &lt;/bean&gt;\n * &lt;/beans&gt;\n * </pre>\n *\n * <p>\n * If you get a \"GSSException: Channel binding mismatch (Mechanism level:ChannelBinding\n * not provided!) have a look at this\n * <a href=\"https://bugs.sun.com/view_bug.do?bug_id=6851973\">bug</a>.\n * </p>\n * <p>\n * A workaround until this is fixed in the JVM is to change\n * </p>\n * HKEY_LOCAL_MACHINE\\System \\CurrentControlSet\\Control\\LSA\\SuppressExtendedProtection to\n * 0x02\n *\n * @author Mike Wiesner\n * @author Jeremy Stone\n * @author Denis Angilella\n * @since 1.0\n * @see KerberosServiceAuthenticationProvider\n * @see SpnegoEntryPoint\n */\npublic class SpnegoAuthenticationProcessingFilter extends OncePerRequestFilter {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate @Nullable AuthenticationManager authenticationManager;\n\n\tprivate @Nullable AuthenticationSuccessHandler successHandler;\n\n\tprivate @Nullable AuthenticationFailureHandler failureHandler;\n\n\tprivate SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();\n\n\tprivate boolean skipIfAlreadyAuthenticated = true;\n\n\tprivate boolean stopFilterChainOnSuccessfulAuthentication = false;\n\n\t/**\n\t * Authentication header prefix sent by IE/Windows when the domain controller fails to\n\t * issue a Kerberos ticket for the URL.\n\t *\n\t * \"TlRMTVNTUA\" is the base64 encoding of \"NTLMSSP\". This will be followed by the\n\t * actual token.\n\t **/\n\tprivate static final String NTLMSSP_PREFIX = \"Negotiate TlRMTVNTUA\";\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (this.skipIfAlreadyAuthenticated) {\n\t\t\tAuthentication existingAuth = SecurityContextHolder.getContext().getAuthentication();\n\n\t\t\tif (existingAuth != null && existingAuth.isAuthenticated()\n\t\t\t\t\t&& !(existingAuth instanceof AnonymousAuthenticationToken)) {\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tString header = request.getHeader(\"Authorization\");\n\n\t\tif (header != null && ((header.startsWith(\"Negotiate \") && !header.startsWith(NTLMSSP_PREFIX))\n\t\t\t\t|| header.startsWith(\"Kerberos \"))) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(\"Received Negotiate Header for request \" + request.getRequestURL() + \": \" + header);\n\t\t\t}\n\t\t\tbyte[] base64Token = header.substring(header.indexOf(\" \") + 1).getBytes(\"UTF-8\");\n\t\t\tbyte[] kerberosTicket = Base64.getDecoder().decode(base64Token);\n\t\t\tKerberosServiceRequestToken authenticationRequest = new KerberosServiceRequestToken(kerberosTicket);\n\t\t\tauthenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\tAuthentication authentication;\n\t\t\ttry {\n\t\t\t\tif (this.authenticationManager == null) {\n\t\t\t\t\tthrow new IllegalStateException(\"authenticationManager must be set\");\n\t\t\t\t}\n\t\t\t\tauthentication = this.authenticationManager.authenticate(authenticationRequest);\n\t\t\t}\n\t\t\tcatch (AuthenticationException ex) {\n\t\t\t\t// That shouldn't happen, as it is most likely a wrong\n\t\t\t\t// configuration on the server side\n\t\t\t\tthis.logger.warn(\"Negotiate Header was invalid: \" + header, ex);\n\t\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\t\tif (this.failureHandler != null) {\n\t\t\t\t\tthis.failureHandler.onAuthenticationFailure(request, response, ex);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tresponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n\t\t\t\t\tresponse.flushBuffer();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.sessionStrategy.onAuthentication(authentication, request, response);\n\n\t\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\tcontext.setAuthentication(authentication);\n\t\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\t\tif (this.successHandler != null) {\n\t\t\t\tthis.successHandler.onAuthenticationSuccess(request, response, authentication);\n\t\t\t}\n\t\t\tif (this.stopFilterChainOnSuccessfulAuthentication) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tchain.doFilter(request, response);\n\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() throws ServletException {\n\t\tsuper.afterPropertiesSet();\n\t\tAssert.notNull(this.authenticationManager, \"authenticationManager must be specified\");\n\t}\n\n\t/**\n\t * The authentication manager for validating the ticket.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic void setAuthenticationManager(AuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t/**\n\t * <p>\n\t * This handler is called after a successful authentication. One can add additional\n\t * authentication behavior by setting this.\n\t * </p>\n\t * <p>\n\t * Default is null, which means nothing additional happens\n\t * </p>\n\t * @param successHandler the authentication success handler\n\t */\n\tpublic void setSuccessHandler(AuthenticationSuccessHandler successHandler) {\n\t\tthis.successHandler = successHandler;\n\t}\n\n\t/**\n\t * <p>\n\t * This handler is called after a failure authentication. In most cases you only get\n\t * Kerberos/SPNEGO failures with a wrong server or network configurations and not\n\t * during runtime. If the client encounters an error, he will just stop the\n\t * communication with server and therefore this handler will not be called in this\n\t * case.\n\t * </p>\n\t * <p>\n\t * Default is null, which means that the Filter returns the HTTP 500 code\n\t * </p>\n\t * @param failureHandler the authentication failure handler\n\t */\n\tpublic void setFailureHandler(AuthenticationFailureHandler failureHandler) {\n\t\tthis.failureHandler = failureHandler;\n\t}\n\n\t/**\n\t * Should Kerberos authentication be skipped if a user is already authenticated for\n\t * this request (e.g. in the HTTP session).\n\t * @param skipIfAlreadyAuthenticated default is true\n\t */\n\tpublic void setSkipIfAlreadyAuthenticated(boolean skipIfAlreadyAuthenticated) {\n\t\tthis.skipIfAlreadyAuthenticated = skipIfAlreadyAuthenticated;\n\t}\n\n\t/**\n\t * The session handling strategy which will be invoked immediately after an\n\t * authentication request is successfully processed by the\n\t * <tt>AuthenticationManager</tt>. Used, for example, to handle changing of the\n\t * session identifier to prevent session fixation attacks.\n\t * @param sessionStrategy the implementation to use. If not set a null implementation\n\t * is used.\n\t */\n\tpublic void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {\n\t\tthis.sessionStrategy = sessionStrategy;\n\t}\n\n\t/**\n\t * Sets the authentication details source.\n\t * @param authenticationDetailsSource the authentication details source\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"AuthenticationDetailsSource required\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * If set to {@code false} (the default) and authentication is successful, the request\n\t * will be processed by the next filter in the chain. If {@code true} and\n\t * authentication is successful, the filter chain will stop here.\n\t * @param shouldStop set to {@code true} to prevent the next filter in the chain from\n\t * processing the request after a successful authentication.\n\t * @since 1.0.2\n\t */\n\tpublic void setStopFilterChainOnSuccessfulAuthentication(boolean shouldStop) {\n\t\tthis.stopFilterChainOnSuccessfulAuthentication = shouldStop;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * authentication success. The default action is not to save the\n\t * {@link SecurityContext}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t * @param securityContextHolderStrategy the {@link SecurityContextHolderStrategy} to\n\t * use. Cannot be null.\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/SpnegoEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.RequestDispatcher;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Sends back a request for a Negotiate Authentication to the browser.\n *\n * <p>\n * With optional configured <code>forwardUrl</code> it is possible to use form login as\n * fallback authentication.\n * </p>\n *\n * <p>\n * This approach enables security configuration to use SPNEGO in combination with login\n * form as fallback for clients that do not support this kind of authentication. Set\n * Response Code 401 - unauthorized and forward to login page. A useful scenario might be\n * an environment where windows domain is present but it is required to access the\n * application also from non domain client devices. One could use a combination with form\n * based LDAP login.\n * </p>\n *\n * <p>\n * See <code>spnego-with-form-login.xml</code> in spring-security-kerberos-sample for\n * details\n * </p>\n *\n * @author Mike Wiesner\n * @author Andre Schaefer, Namics AG\n * @since 1.0\n * @see SpnegoAuthenticationProcessingFilter\n */\npublic class SpnegoEntryPoint implements AuthenticationEntryPoint {\n\n\tprivate static final Log LOG = LogFactory.getLog(SpnegoEntryPoint.class);\n\n\tprivate final @Nullable String forwardUrl;\n\n\tprivate final @Nullable HttpMethod forwardMethod;\n\n\tprivate final boolean forward;\n\n\t/**\n\t * Instantiates a new spnego entry point. Using this constructor the EntryPoint will\n\t * Sends back a request for a Negotiate Authentication to the browser without\n\t * providing a fallback mechanism for login, Use constructor with forwardUrl to\n\t * provide form based login.\n\t */\n\tpublic SpnegoEntryPoint() {\n\t\tthis(null);\n\t}\n\n\t/**\n\t * Instantiates a new spnego entry point. This constructor enables security\n\t * configuration to use SPNEGO in combination with a fallback page (login form, custom\n\t * 401 page ...). The forward method will be the same as the original request.\n\t * @param forwardUrl URL where the login page can be found. Should be relative to the\n\t * web-app context path (include a leading {@code /}) and can't be absolute URL.\n\t */\n\tpublic SpnegoEntryPoint(@Nullable String forwardUrl) {\n\t\tthis(forwardUrl, null);\n\t}\n\n\t/**\n\t * Instantiates a new spnego entry point. This constructor enables security\n\t * configuration to use SPNEGO in combination with a fallback page (login form, custom\n\t * 401 page ...). The forward URL will be accessed via provided HTTP method.\n\t * @param forwardUrl URL where the login page can be found. Should be relative to the\n\t * web-app context path (include a leading {@code /}) and can't be absolute URL.\n\t * @param forwardMethod HTTP method to use when accessing the forward URL\n\t */\n\tpublic SpnegoEntryPoint(@Nullable String forwardUrl, @Nullable HttpMethod forwardMethod) {\n\t\tif (StringUtils.hasText(forwardUrl)) {\n\t\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), \"Forward url specified must be a valid forward URL\");\n\t\t\tAssert.isTrue(!UrlUtils.isAbsoluteUrl(forwardUrl), \"Forward url specified must not be absolute\");\n\n\t\t\tthis.forwardUrl = forwardUrl;\n\t\t\tthis.forwardMethod = forwardMethod;\n\t\t\tthis.forward = true;\n\t\t}\n\t\telse {\n\t\t\tthis.forwardUrl = null;\n\t\t\tthis.forwardMethod = null;\n\t\t\tthis.forward = false;\n\t\t}\n\t}\n\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex)\n\t\t\tthrows IOException, ServletException {\n\t\tif (LOG.isDebugEnabled()) {\n\t\t\tLOG.debug(\"Add header WWW-Authenticate:Negotiate to \" + request.getRequestURL() + \", forward: \"\n\t\t\t\t\t+ (this.forward ? this.forwardUrl : \"no\"));\n\t\t}\n\t\tresponse.addHeader(\"WWW-Authenticate\", \"Negotiate\");\n\t\tresponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n\n\t\tif (this.forward) {\n\t\t\tRequestDispatcher dispatcher = request.getRequestDispatcher(this.forwardUrl);\n\t\t\tHttpMethod method = this.forwardMethod;\n\t\t\tHttpServletRequest fwdRequest = (method != null) ? new HttpServletRequestWrapper(request) {\n\t\t\t\t@Override\n\t\t\t\tpublic String getMethod() {\n\t\t\t\t\treturn method.name();\n\t\t\t\t}\n\t\t\t} : request;\n\t\t\tdispatcher.forward(fwdRequest, response);\n\t\t}\n\t\telse {\n\t\t\tresponse.flushBuffer();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@NullMarked\npackage org.springframework.security.kerberos.web.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/java/org/springframework/security/kerberos/docs/AuthProviderConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.docs;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;\nimport org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;\n\n//tag::snippetA[]\n@Configuration\npublic class AuthProviderConfig {\n\n\t@Bean\n\tpublic KerberosAuthenticationProvider kerberosAuthenticationProvider() {\n\t\tKerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();\n\t\tSunJaasKerberosClient client = new SunJaasKerberosClient();\n\t\tclient.setDebug(true);\n\t\tprovider.setKerberosClient(client);\n\t\tprovider.setUserDetailsService(dummyUserDetailsService());\n\t\treturn provider;\n\t}\n\n\t@Bean\n\tpublic DummyUserDetailsService dummyUserDetailsService() {\n\t\treturn new DummyUserDetailsService();\n\t}\n\n}\n// end::snippetA[]\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/java/org/springframework/security/kerberos/docs/AuthProviderConfigTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.docs;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = { \"AuthProviderConfig.xml\" })\npublic class AuthProviderConfigTests {\n\n\t@Test\n\tpublic void configLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/java/org/springframework/security/kerberos/docs/DummyUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.docs;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\n\n//tag::snippetA[]\npublic class DummyUserDetailsService implements UserDetailsService {\n\n\t@Override\n\tpublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n\t\treturn new User(username, \"notUsed\", true, true, true, true, AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t}\n\n}\n// end::snippetA[]\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/java/org/springframework/security/kerberos/docs/SpnegoConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.docs;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;\nimport org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;\nimport org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;\nimport org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;\nimport org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;\nimport org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;\n\n//tag::snippetA[]\n@Configuration\npublic class SpnegoConfig {\n\n\t@Bean\n\tpublic KerberosAuthenticationProvider kerberosAuthenticationProvider() {\n\t\tKerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();\n\t\tSunJaasKerberosClient client = new SunJaasKerberosClient();\n\t\tclient.setDebug(true);\n\t\tprovider.setKerberosClient(client);\n\t\tprovider.setUserDetailsService(dummyUserDetailsService());\n\t\treturn provider;\n\t}\n\n\t@Bean\n\tpublic SpnegoEntryPoint spnegoEntryPoint() {\n\t\treturn new SpnegoEntryPoint(\"/login\");\n\t}\n\n\t@Bean\n\tpublic SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(\n\t\t\tAuthenticationManager authenticationManager) {\n\t\tSpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();\n\t\tfilter.setAuthenticationManager(authenticationManager);\n\t\treturn filter;\n\t}\n\n\t@Bean\n\tpublic KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {\n\t\tKerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();\n\t\tprovider.setTicketValidator(sunJaasKerberosTicketValidator());\n\t\tprovider.setUserDetailsService(dummyUserDetailsService());\n\t\treturn provider;\n\t}\n\n\t@Bean\n\tpublic SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {\n\t\tSunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();\n\t\tticketValidator.setServicePrincipal(\"HTTP/servicehost.example.org@EXAMPLE.ORG\");\n\t\tticketValidator.setKeyTabLocation(new FileSystemResource(\"/tmp/service.keytab\"));\n\t\tticketValidator.setDebug(true);\n\t\treturn ticketValidator;\n\t}\n\n\t@Bean\n\tpublic DummyUserDetailsService dummyUserDetailsService() {\n\t\treturn new DummyUserDetailsService();\n\t}\n\n}\n// end::snippetA[]\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/java/org/springframework/security/kerberos/web/SpnegoAuthenticationProcessingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.kerberos.authentication.KerberosServiceRequestToken;\nimport org.springframework.security.kerberos.authentication.KerberosTicketValidation;\nimport org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Test class for {@link SpnegoAuthenticationProcessingFilter}\n *\n * @author Mike Wiesner\n * @author Jeremy Stone\n * @since 1.0\n */\npublic class SpnegoAuthenticationProcessingFilterTests {\n\n\tprivate SpnegoAuthenticationProcessingFilter filter;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate HttpServletRequest request;\n\n\tprivate HttpServletResponse response;\n\n\tprivate FilterChain chain;\n\n\tprivate AuthenticationSuccessHandler successHandler;\n\n\tprivate AuthenticationFailureHandler failureHandler;\n\n\tprivate WebAuthenticationDetailsSource detailsSource;\n\n\t// data\n\tprivate static final byte[] TEST_TOKEN = \"TestToken\".getBytes();\n\n\tprivate static final String TEST_TOKEN_BASE64 = \"VGVzdFRva2Vu\";\n\n\tprivate static KerberosTicketValidation UNUSED_TICKET_VALIDATION = mock(KerberosTicketValidation.class);\n\n\tprivate static final Authentication AUTHENTICATION = new KerberosServiceRequestToken(\"test\",\n\t\t\tUNUSED_TICKET_VALIDATION, AuthorityUtils.createAuthorityList(\"ROLE_ADMIN\"), TEST_TOKEN);\n\n\tprivate static final String HEADER = \"Authorization\";\n\n\tprivate static final String TOKEN_PREFIX_NEG = \"Negotiate \";\n\n\tprivate static final String TOKEN_PREFIX_KERB = \"Kerberos \";\n\n\tprivate static final String TOKEN_NTLM = \"Negotiate TlRMTVNTUAABAAAAl4II4gAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw==\";\n\n\tprivate static final BadCredentialsException BCE = new BadCredentialsException(\"\");\n\n\t@BeforeEach\n\tpublic void before() throws Exception {\n\t\t// mocking\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.detailsSource = new WebAuthenticationDetailsSource();\n\t\tthis.filter = new SpnegoAuthenticationProcessingFilter();\n\t\tthis.filter.setAuthenticationManager(this.authenticationManager);\n\t\tthis.request = mock(HttpServletRequest.class);\n\t\tthis.response = mock(HttpServletResponse.class);\n\t\tthis.chain = mock(FilterChain.class);\n\t\tthis.filter.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void testEverythingWorks() throws Exception {\n\t\teverythingWorks(TOKEN_PREFIX_NEG);\n\t}\n\n\t@Test\n\tpublic void testEverythingWorks_Kerberos() throws Exception {\n\t\teverythingWorks(TOKEN_PREFIX_KERB);\n\t}\n\n\t@Test\n\tpublic void testEverythingWorksWithHandlers() throws Exception {\n\t\teverythingWorksWithHandlers(TOKEN_PREFIX_NEG);\n\t}\n\n\t@Test\n\tpublic void testEverythingWorksWithHandlers_Kerberos() throws Exception {\n\t\teverythingWorksWithHandlers(TOKEN_PREFIX_KERB);\n\t}\n\n\tprivate void everythingWorksWithHandlers(String tokenPrefix) throws Exception {\n\t\tcreateHandler();\n\t\teverythingWorks(tokenPrefix);\n\t\teverythingWorksVerifyHandlers();\n\t}\n\n\tprivate void everythingWorksVerifyHandlers() throws Exception {\n\t\tverify(this.successHandler).onAuthenticationSuccess(this.request, this.response, AUTHENTICATION);\n\t\tverify(this.failureHandler, never()).onAuthenticationFailure(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(AuthenticationException.class));\n\t}\n\n\tprivate void everythingWorks(String tokenPrefix) throws IOException, ServletException {\n\t\t// stubbing\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tthis.filter.setSecurityContextRepository(securityContextRepository);\n\t\teverythingWorksStub(tokenPrefix);\n\n\t\t// testing\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.chain).doFilter(this.request, this.response);\n\t\tverify(securityContextRepository).saveContext(SecurityContextHolder.getContext(), this.request, this.response);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isEqualTo(AUTHENTICATION);\n\t}\n\n\t@Test\n\tpublic void testNoHeader() throws Exception {\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\t// If the header is not present, the filter is not allowed to call\n\t\t// authenticate()\n\t\tverify(this.authenticationManager, never()).authenticate(any(Authentication.class));\n\t\t// chain should go on\n\t\tverify(this.chain).doFilter(this.request, this.response);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isEqualTo(null);\n\t}\n\n\t@Test\n\tpublic void testNTLMSSPHeader() throws Exception {\n\t\tgiven(this.request.getHeader(HEADER)).willReturn(TOKEN_NTLM);\n\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\t// If the header is not present, the filter is not allowed to call\n\t\t// authenticate()\n\t\tverify(this.authenticationManager, never()).authenticate(any(Authentication.class));\n\t\t// chain should go on\n\t\tverify(this.chain).doFilter(this.request, this.response);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isEqualTo(null);\n\t}\n\n\t@Test\n\tpublic void testAuthenticationFails() throws Exception {\n\t\tauthenticationFails();\n\t\tverify(this.response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n\t}\n\n\t@Test\n\tpublic void testAuthenticationFailsWithHandlers() throws Exception {\n\t\tcreateHandler();\n\t\tauthenticationFails();\n\t\tverify(this.failureHandler).onAuthenticationFailure(this.request, this.response, BCE);\n\t\tverify(this.successHandler, never()).onAuthenticationSuccess(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(Authentication.class));\n\t\tverify(this.response, never()).setStatus(anyInt());\n\t}\n\n\t@Test\n\tpublic void testAlreadyAuthenticated() throws Exception {\n\t\ttry {\n\t\t\tAuthentication existingAuth = new UsernamePasswordAuthenticationToken(\"mike\", \"mike\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_TEST\"));\n\t\t\tSecurityContextHolder.getContext().setAuthentication(existingAuth);\n\t\t\tgiven(this.request.getHeader(HEADER)).willReturn(TOKEN_PREFIX_NEG + TEST_TOKEN_BASE64);\n\t\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\t\tverify(this.authenticationManager, never()).authenticate(any(Authentication.class));\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testAlreadyAuthenticatedWithNotAuthenticatedToken() throws Exception {\n\t\ttry {\n\t\t\t// this token is not authenticated yet!\n\t\t\tAuthentication existingAuth = new UsernamePasswordAuthenticationToken(\"mike\", \"mike\");\n\t\t\tSecurityContextHolder.getContext().setAuthentication(existingAuth);\n\t\t\teverythingWorks(TOKEN_PREFIX_NEG);\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testAlreadyAuthenticatedWithAnonymousToken() throws Exception {\n\t\ttry {\n\t\t\tAuthentication existingAuth = new AnonymousAuthenticationToken(\"test\", \"mike\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_TEST\"));\n\t\t\tSecurityContextHolder.getContext().setAuthentication(existingAuth);\n\t\t\teverythingWorks(TOKEN_PREFIX_NEG);\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testAlreadyAuthenticatedNotActive() throws Exception {\n\t\ttry {\n\t\t\tAuthentication existingAuth = new UsernamePasswordAuthenticationToken(\"mike\", \"mike\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_TEST\"));\n\t\t\tSecurityContextHolder.getContext().setAuthentication(existingAuth);\n\t\t\tthis.filter.setSkipIfAlreadyAuthenticated(false);\n\t\t\teverythingWorks(TOKEN_PREFIX_NEG);\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testEverythingWorksWithHandlers_stopFilterChain() throws Exception {\n\t\tthis.filter.setStopFilterChainOnSuccessfulAuthentication(true);\n\n\t\tcreateHandler();\n\t\teverythingWorksStub(TOKEN_PREFIX_NEG);\n\n\t\t// testing\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.chain, never()).doFilter(this.request, this.response);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isEqualTo(AUTHENTICATION);\n\t\teverythingWorksVerifyHandlers();\n\t}\n\n\tprivate void everythingWorksStub(String tokenPrefix) throws IOException, ServletException {\n\t\tgiven(this.request.getHeader(HEADER)).willReturn(tokenPrefix + TEST_TOKEN_BASE64);\n\t\tKerberosServiceRequestToken requestToken = new KerberosServiceRequestToken(TEST_TOKEN);\n\t\trequestToken.setDetails(this.detailsSource.buildDetails(this.request));\n\t\tgiven(this.authenticationManager.authenticate(requestToken)).willReturn(AUTHENTICATION);\n\t}\n\n\tprivate void authenticationFails() throws IOException, ServletException {\n\t\t// stubbing\n\t\tgiven(this.request.getHeader(HEADER)).willReturn(TOKEN_PREFIX_NEG + TEST_TOKEN_BASE64);\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willThrow(BCE);\n\n\t\t// testing\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\t// chain should stop here and it should send back a 500\n\t\t// future version should call some error handler\n\t\tverify(this.chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t}\n\n\tprivate void createHandler() {\n\t\tthis.successHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.failureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setSuccessHandler(this.successHandler);\n\t\tthis.filter.setFailureHandler(this.failureHandler);\n\t}\n\n\t@AfterEach\n\tpublic void after() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/java/org/springframework/security/kerberos/web/SpnegoEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.kerberos.web;\n\nimport jakarta.servlet.RequestDispatcher;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;\nimport org.springframework.web.bind.annotation.RequestMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Test class for {@link SpnegoEntryPoint}\n *\n * @author Mike Wiesner\n * @author Janne Valkealahti\n * @author Andre Schaefer, Namics AG\n * @since 1.0\n */\npublic class SpnegoEntryPointTests {\n\n\tprivate SpnegoEntryPoint entryPoint = new SpnegoEntryPoint();\n\n\t@Test\n\tpublic void testEntryPointOk() throws Exception {\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\n\t\tthis.entryPoint.commence(request, response, null);\n\n\t\tverify(response).addHeader(\"WWW-Authenticate\", \"Negotiate\");\n\t\tverify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void testEntryPointOkWithDispatcher() throws Exception {\n\t\tSpnegoEntryPoint entryPoint = new SpnegoEntryPoint();\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tRequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n\t\tgiven(request.getRequestDispatcher(anyString())).willReturn(requestDispatcher);\n\t\tentryPoint.commence(request, response, null);\n\t\tverify(response).addHeader(\"WWW-Authenticate\", \"Negotiate\");\n\t\tverify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void testEntryPointForwardOk() throws Exception {\n\t\tString forwardUrl = \"/login\";\n\t\tSpnegoEntryPoint entryPoint = new SpnegoEntryPoint(forwardUrl);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tRequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n\t\tgiven(request.getRequestDispatcher(anyString())).willReturn(requestDispatcher);\n\t\tentryPoint.commence(request, response, null);\n\t\tverify(response).addHeader(\"WWW-Authenticate\", \"Negotiate\");\n\t\tverify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tverify(request).getRequestDispatcher(forwardUrl);\n\t\tverify(requestDispatcher).forward(request, response);\n\t}\n\n\t@Test\n\tpublic void testForwardUsesDefaultHttpMethod() throws Exception {\n\t\tArgumentCaptor<HttpServletRequest> servletRequestCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);\n\t\tString forwardUrl = \"/login\";\n\t\tSpnegoEntryPoint entryPoint = new SpnegoEntryPoint(forwardUrl);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tgiven(request.getMethod()).willReturn(RequestMethod.POST.name());\n\t\tRequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n\t\tgiven(request.getRequestDispatcher(anyString())).willReturn(requestDispatcher);\n\t\tentryPoint.commence(request, response, null);\n\t\tverify(requestDispatcher).forward(servletRequestCaptor.capture(), eq(response));\n\t\tassertThat(servletRequestCaptor.getValue().getMethod()).isEqualTo(HttpMethod.POST.name());\n\t}\n\n\t@Test\n\tpublic void testForwardUsesCustomHttpMethod() throws Exception {\n\t\tArgumentCaptor<HttpServletRequest> servletRequestCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);\n\t\tString forwardUrl = \"/login\";\n\t\tSpnegoEntryPoint entryPoint = new SpnegoEntryPoint(forwardUrl, HttpMethod.DELETE);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tRequestDispatcher requestDispatcher = mock(RequestDispatcher.class);\n\t\tgiven(request.getRequestDispatcher(anyString())).willReturn(requestDispatcher);\n\t\tentryPoint.commence(request, response, null);\n\t\tverify(requestDispatcher).forward(servletRequestCaptor.capture(), eq(response));\n\t\tassertThat(servletRequestCaptor.getValue().getMethod()).isEqualTo(HttpMethod.DELETE.name());\n\t}\n\n\t@Test\n\tpublic void testEntryPointForwardAbsolute() throws Exception {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SpnegoEntryPoint(\"http://test/login\"));\n\t}\n\n}\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/resources/org/springframework/security/kerberos/docs/AuthProviderConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- tag::snippetA[] -->\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xmlns:sec=\"http://www.springframework.org/schema/security\"\n  xmlns:context=\"http://www.springframework.org/schema/context\"\n  xsi:schemaLocation=\"\n    http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-3.2.xsd\n    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.2.xsd\n    http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\">\n\n  <sec:http entry-point-ref=\"spnegoEntryPoint\" use-expressions=\"true\">\n    <sec:intercept-url pattern=\"/\" access=\"permitAll\" />\n    <sec:intercept-url pattern=\"/home\" access=\"permitAll\" />\n    <sec:intercept-url pattern=\"/**\" access=\"authenticated\"/>\n  </sec:http>\n\n  <sec:authentication-manager alias=\"authenticationManager\">\n    <sec:authentication-provider ref=\"kerberosAuthenticationProvider\"/>\n  </sec:authentication-manager>\n\n  <bean id=\"kerberosAuthenticationProvider\"\n    class=\"org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider\">\n    <property name=\"kerberosClient\">\n      <bean class=\"org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient\">\n        <property name=\"debug\" value=\"true\"/>\n      </bean>\n    </property>\n    <property name=\"userDetailsService\" ref=\"dummyUserDetailsService\"/>\n  </bean>\n\n  <bean\n    class=\"org.springframework.security.kerberos.authentication.sun.GlobalSunJaasKerberosConfig\">\n    <property name=\"debug\" value=\"true\" />\n    <property name=\"krbConfLocation\" value=\"/path/to/krb5.ini\"/>\n  </bean>\n\n  <bean id=\"dummyUserDetailsService\"\n    class=\"org.springframework.security.kerberos.docs.DummyUserDetailsService\" />\n\n  <bean id=\"spnegoEntryPoint\"\n    class=\"org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint\" >\n    <constructor-arg value=\"/login\" />\n  </bean>\n\n</beans>\n<!-- end::snippetA[] -->\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/resources/org/springframework/security/kerberos/docs/SpnegoConfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- tag::snippetA[] -->\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xmlns:sec=\"http://www.springframework.org/schema/security\"\n  xmlns:context=\"http://www.springframework.org/schema/context\"\n  xsi:schemaLocation=\"http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd\n    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.1.xsd\n    http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.1.xsd\">\n\n  <sec:http entry-point-ref=\"spnegoEntryPoint\" use-expressions=\"true\" >\n    <sec:intercept-url pattern=\"/\" access=\"permitAll\" />\n    <sec:intercept-url pattern=\"/home\" access=\"permitAll\" />\n    <sec:intercept-url pattern=\"/login\" access=\"permitAll\" />\n    <sec:intercept-url pattern=\"/**\" access=\"authenticated\"/>\n    <sec:form-login login-page=\"/login\" />\n    <sec:custom-filter ref=\"spnegoAuthenticationProcessingFilter\"\n      before=\"BASIC_AUTH_FILTER\" />\n  </sec:http>\n\n  <sec:authentication-manager alias=\"authenticationManager\">\n    <sec:authentication-provider ref=\"kerberosAuthenticationProvider\" />\n    <sec:authentication-provider ref=\"kerberosServiceAuthenticationProvider\" />\n  </sec:authentication-manager>\n\n  <bean id=\"kerberosAuthenticationProvider\"\n    class=\"org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider\">\n    <property name=\"userDetailsService\" ref=\"dummyUserDetailsService\"/>\n    <property name=\"kerberosClient\">\n      <bean class=\"org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient\">\n        <property name=\"debug\" value=\"true\"/>\n      </bean>\n    </property>\n  </bean>\n\n  <bean id=\"spnegoEntryPoint\"\n    class=\"org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint\" >\n    <constructor-arg value=\"/login\" />\n  </bean>\n\n  <bean id=\"spnegoAuthenticationProcessingFilter\"\n    class=\"org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter\">\n    <property name=\"authenticationManager\" ref=\"authenticationManager\" />\n  </bean>\n\n  <bean id=\"kerberosServiceAuthenticationProvider\"\n    class=\"org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider\">\n    <property name=\"ticketValidator\">\n      <bean\n        class=\"org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator\">\n        <property name=\"servicePrincipal\" value=\"${app.service-principal}\" />\n        <property name=\"keyTabLocation\" value=\"${app.keytab-location}\" />\n        <property name=\"debug\" value=\"true\" />\n      </bean>\n    </property>\n    <property name=\"userDetailsService\" ref=\"dummyUserDetailsService\" />\n  </bean>\n\n  <bean id=\"dummyUserDetailsService\"\n    class=\"org.springframework.security.kerberos.docs.DummyUserDetailsService\" />\n\n</beans>\n<!-- end::snippetA[] -->\n"
  },
  {
    "path": "kerberos/kerberos-web/src/test/resources/org/springframework/security/kerberos/docs/appproperties.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txmlns:util=\"http://www.springframework.org/schema/util\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\n\t\thttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.1.xsd\n\t\thttp://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-4.1.xsd\">\n\n\t<context:property-placeholder location=\"app.properties\"/>\n\n</beans>\n"
  },
  {
    "path": "ldap/openldaptest.ldif",
    "content": "dn: dc=springsource,dc=com\nobjectClass: dcObject\nobjectClass: domain\ndc: springsource\n\ndn: ou=users,dc=springsource,dc=com\nobjectClass: organizationalUnit\nobjectClass: top\nou: users\n\ndn: ou=\\\"quoted people\\\",dc=springsource,dc=com\nobjectclass: top\nobjectclass: organizationalUnit\nou: \"quoted people\"\n\ndn: cn=quoteguy,ou=\\\"quoted people\\\",dc=springsource,dc=com\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: quoteguy\nsn: Quote\nuid: quoteguy\nuserPassword: quoteguyspassword\n\ndn: uid=luke,ou=users,dc=springsource,dc=com\nobjectClass: person\nobjectClass: organizationalPerson\nobjectClass: inetOrgPerson\nobjectClass: top\ncn: Luke\nuid: luke\ngivenName: Luke\no: SpringSource\nsn: Taylor\nuserPassword: password\n\ndn: ou=policies,dc=springsource,dc=com\nobjectClass: organizationalUnit\nobjectClass: top\nou: policies\n\ndn: cn=default,ou=policies,dc=springsource,dc=com\nobjectClass: device\nobjectClass: top\nobjectClass: pwdPolicy\ncn: default\npwdAttribute: userPassword\npwdCheckQuality: 1\npwdExpireWarning: 600000\npwdFailureCountInterval: 0\npwdGraceAuthNLimit: 100\npwdInHistory: 50\npwdLockout: FALSE\npwdLockoutDuration: 0\npwdMaxAge: 5184000\npwdMaxFailure: 3\npwdMinAge: 0\npwdMinLength: 8\npwdMustChange: FALSE\n\ndn: cn=lockoutafter1,ou=policies,dc=springsource,dc=com\nobjectClass: device\nobjectClass: top\nobjectClass: pwdPolicy\ncn: lockoutafter1\npwdAttribute: userPassword\npwdCheckQuality: 1\npwdFailureCountInterval: 0\npwdGraceAuthNLimit: 2\npwdInHistory: 3\npwdLockout: TRUE\npwdLockoutDuration: 10\npwdMaxFailure: 1\npwdMinAge: 0\npwdMinLength: 6\npwdMustChange: TRUE\n\ndn: cn=expirein10,ou=policies,dc=springsource,dc=com\nobjectClass: device\nobjectClass: top\nobjectClass: pwdPolicy\ncn: expirein10\npwdAttribute: userPassword\npwdExpireWarning: 9999\npwdGraceAuthNLimit: 5\npwdMaxAge: 10000\npwdInHistory: 3\npwdLockout: FALSE\npwdMinLength: 6\npwdMustChange: TRUE\n\n\ndn: uid=expireme,ou=users,dc=springsource,dc=com\nobjectClass: person\nobjectClass: organizationalPerson\nobjectClass: inetOrgPerson\nobjectClass: top\nuid: expireme\ncn: Expired\ngivenName: Expired\no: SpringSource\nsn: User\nuserPassword: password\npwdPolicySubentry: cn=expirein10,ou=policies,dc=springsource,dc=com\n\ndn: uid=lockme,ou=users,dc=springsource,dc=com\nobjectClass: person\nobjectClass: organizationalPerson\nobjectClass: inetOrgPerson\nobjectClass: top\nuid: lockme\ncn: Expired\ngivenName: Expired\no: SpringSource\nsn: User\nuserPassword: password\npwdPolicySubentry: cn=lockoutafter1,ou=policies,dc=springsource,dc=com"
  },
  {
    "path": "ldap/run_slapd.sh",
    "content": "#! /bin/sh\n\nrm -Rf build/openldap\nmkdir -p build/openldap\n/usr/libexec/slapd -h ldap://localhost:22389 -d -1 -f slapd.conf &\nsleep 3\nldapadd -h localhost -p 22389 -D cn=admin,dc=springsource,dc=com -w password -x -f openldaptest.ldif\n"
  },
  {
    "path": "ldap/slapd.conf",
    "content": "include     /etc/openldap/schema/core.schema\ninclude     /etc/openldap/schema/cosine.schema\ninclude     /etc/openldap/schema/inetorgperson.schema\ninclude     /etc/openldap/schema/ppolicy.schema\n\n\npidfile\t\t./build/slapd.pid\nargsfile\t./build/slapd.args\n\n# Load dynamic backend modules:\nmodulepath\t/usr/lib/openldap/modules\n# moduleload\tback_ldap.la\n# moduleload\tback_meta.la\n# moduleload\tback_monitor.la\n# moduleload\tback_perl.la\n\n#allow bind_anon\nallow bind_v2 bind_anon_dn\n#require authc\n\naccess to dn.base=\"\"\n    by * read\n\ndatabase        bdb\nsuffix          \"dc=springsource,dc=com\"\ncheckpoint      1024    5\ncachesize       10000\nrootdn          \"cn=admin,dc=springsource,dc=com\"\n\nrootpw          password\n\ndirectory       ./build/openldap\n\nindex   uid     eq\nindex   cn      eq\nindex objectClass eq\n\naccess to attrs=userpassword\n  by self       =wx\n  by anonymous  =x\n  by *          none\n  \naccess to dn.subtree=\"ou=users,dc=springsource,dc=com\"\n  by self     write\n  by *        read\n\n\n#overlay ppolicy\n#ppolicy_default \"cn=default,ou=policies,dc=springsource,dc=com\"\n#ppolicy_use_lockout\n#ppolicy_hash_cleartext\n\n"
  },
  {
    "path": "ldap/spring-security-ldap.gradle",
    "content": "apply plugin: 'io.spring.convention.spring-module'\napply plugin: 'javadoc-warnings-error'\napply plugin: 'compile-warnings-error'\napply plugin: 'security-nullability'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-tx'\n\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\toptional 'ldapsdk:ldapsdk'\n\toptional \"com.unboundid:unboundid-ldapsdk\"\n\toptional 'tools.jackson.core:jackson-databind'\n\tapi ('org.springframework.ldap:spring-ldap-core') {\n\t\texclude(group: 'commons-logging', module: 'commons-logging')\n\t\texclude(group: 'org.springframework', module: 'spring-beans')\n\t\texclude(group: 'org.springframework', module: 'spring-core')\n\t\texclude(group: 'org.springframework', module: 'spring-tx')\n\t\texclude(group: 'org.springframework.data', module: 'spring-data-commons')\n\t}\n\n\ttestImplementation project(path : ':spring-security-core', configuration : 'tests')\n\ttestImplementation project(\":spring-security-test\")\n\ttestImplementation 'org.slf4j:slf4j-api'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation 'org.skyscreamer:jsonassert'\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\nintegrationTest {\n//\t  exclude('**/OpenLDAPIntegrationTestSuite.class')\n\tmaxParallelForks = 1\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport java.util.ArrayList;\nimport java.util.Hashtable;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ldap.AuthenticationException;\nimport org.springframework.ldap.core.support.AbstractContextSource;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = UnboundIdContainerConfig.class)\npublic class DefaultSpringSecurityContextSourceTests {\n\n\t@Autowired\n\tprivate DefaultSpringSecurityContextSource contextSource;\n\n\t@Test\n\tpublic void instantiationSucceedsWithExpectedProperties() {\n\t\tDefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(\n\t\t\t\t\"ldap://blah:789/dc=springframework,dc=org\");\n\t\tassertThat(ctxSrc.isAnonymousReadOnly()).isFalse();\n\t\tassertThat(ctxSrc.isPooled()).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsSpacesInUrl() {\n\t\tDefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(\n\t\t\t\t\"ldap://myhost:10389/dc=spring%20framework,dc=org\");\n\t\tassertThat(contextSource.getBaseLdapPathAsString()).isEqualTo(\"dc=spring framework,dc=org\");\n\t}\n\n\t// gh-9742\n\t@Test\n\tpublic void constructorWhenUrlEncodedSpacesWithPlusCharacterThenBaseDnIsProperlyDecoded() {\n\t\tDefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(\n\t\t\t\t\"ldap://blah:123/dc=spring+framework,dc=org ldap://blah:456/dc=spring+framework,dc=org\");\n\t\tassertThat(contextSource.getBaseLdapPathAsString()).isEqualTo(\"dc=spring framework,dc=org\");\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tpublic void poolingFlagIsSetWhenAuthenticationDnMatchesManagerUserDn() {\n\t\tEnvExposingDefaultSpringSecurityContextSource ctxSrc = new EnvExposingDefaultSpringSecurityContextSource(\n\t\t\t\t\"ldap://blah:789/dc=springframework,dc=org\");\n\t\tctxSrc.setUserDn(\"manager\");\n\t\tctxSrc.setPassword(\"password\");\n\t\tctxSrc.afterPropertiesSet();\n\t\tassertThat(ctxSrc.getAuthenticatedEnvForTest(\"manager\", \"password\"))\n\t\t\t.containsKey(AbstractContextSource.SUN_LDAP_POOLING_FLAG);\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tpublic void poolingFlagIsNotSetWhenAuthenticationDnIsNotManagerUserDn() {\n\t\tEnvExposingDefaultSpringSecurityContextSource ctxSrc = new EnvExposingDefaultSpringSecurityContextSource(\n\t\t\t\t\"ldap://blah:789/dc=springframework,dc=org\");\n\t\tctxSrc.setUserDn(\"manager\");\n\t\tctxSrc.setPassword(\"password\");\n\t\tctxSrc.afterPropertiesSet();\n\t\tassertThat(ctxSrc.getAuthenticatedEnvForTest(\"user\", \"password\"))\n\t\t\t.doesNotContainKey(AbstractContextSource.SUN_LDAP_POOLING_FLAG);\n\t}\n\n\t// SEC-1145. Confirms that there is no issue here with pooling.\n\t@Test\n\tpublic void cantBindWithWrongPasswordImmediatelyAfterSuccessfulBind() throws Exception {\n\t\tthis.contextSource.getContext(\"uid=Bob,ou=people,dc=springframework,dc=org\", \"bobspassword\").close();\n\t\t// com.sun.jndi.ldap.LdapPoolManager.showStats(System.out);\n\t\t// com.sun.jndi.ldap.LdapPoolManager.showStats(System.out);\n\t\t// Now get it gain, with wrong password. Should fail.\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(\n\t\t\t\t() -> this.contextSource.getContext(\"uid=Bob,ou=people,dc=springframework,dc=org\", \"wrongpassword\")\n\t\t\t\t\t.close());\n\t}\n\n\t@Test\n\tpublic void serverUrlWithSpacesIsSupported() {\n\t\tDefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(\n\t\t\t\tthis.contextSource.getUrls()[0] + \"ou=space%20cadets,dc=springframework,dc=org\");\n\t\tassertThat(contextSource.getBaseLdapPathAsString()).isEqualTo(\"ou=space cadets,dc=springframework,dc=org\");\n\t\tcontextSource.afterPropertiesSet();\n\t\tcontextSource.getContext(\"uid=space cadet,ou=space cadets,dc=springframework,dc=org\", \"spacecadetspassword\");\n\t}\n\n\t@Test\n\tpublic void instantiationFailsWithEmptyServerList() {\n\t\tList<String> serverUrls = new ArrayList<>();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tDefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls,\n\t\t\t\t\t\"dc=springframework,dc=org\");\n\t\t\tctxSrc.afterPropertiesSet();\n\t\t});\n\t}\n\n\t@Test\n\tpublic void instantiationSucceedsWithProperServerList() {\n\t\tList<String> serverUrls = new ArrayList<>();\n\t\tserverUrls.add(\"ldap://foo:789\");\n\t\tserverUrls.add(\"ldap://bar:389\");\n\t\tserverUrls.add(\"ldaps://blah:636\");\n\t\tDefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls,\n\t\t\t\t\"dc=springframework,dc=org\");\n\n\t\tassertThat(ctxSrc.isAnonymousReadOnly()).isFalse();\n\t\tassertThat(ctxSrc.isPooled()).isTrue();\n\t}\n\n\t// SEC-2308\n\t@Test\n\tpublic void instantiationSucceedsWithEmptyBaseDn() {\n\t\tString baseDn = \"\";\n\t\tList<String> serverUrls = new ArrayList<>();\n\t\tserverUrls.add(\"ldap://foo:789\");\n\t\tserverUrls.add(\"ldap://bar:389\");\n\t\tserverUrls.add(\"ldaps://blah:636\");\n\t\tDefaultSpringSecurityContextSource ctxSrc = new DefaultSpringSecurityContextSource(serverUrls, baseDn);\n\n\t\tassertThat(ctxSrc.isAnonymousReadOnly()).isFalse();\n\t\tassertThat(ctxSrc.isPooled()).isTrue();\n\t}\n\n\t// gh-9742\n\t@Test\n\tpublic void constructorWhenServerListWithSpacesInBaseDnThenSuccess() {\n\t\tList<String> serverUrls = new ArrayList<>();\n\t\tserverUrls.add(\"ldap://ad1.example.org:789\");\n\t\tserverUrls.add(\"ldap://ad2.example.org:389\");\n\t\tserverUrls.add(\"ldaps://ad3.example.org:636\");\n\t\tDefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(serverUrls,\n\t\t\t\t\"dc=spring framework,dc=org\");\n\t\tassertThat(contextSource.getBaseLdapPathAsString()).isEqualTo(\"dc=spring framework,dc=org\");\n\t}\n\n\t@Test\n\tpublic void instantiationFailsWithIncorrectServerUrl() {\n\t\tList<String> serverUrls = new ArrayList<>();\n\t\t// a simple trailing slash should be ok\n\t\tserverUrls.add(\"ldaps://blah:636/\");\n\t\t// this url should be rejected because the root DN goes into a separate parameter\n\t\tserverUrls.add(\"ldap://bar:389/dc=foobar,dc=org\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultSpringSecurityContextSource(serverUrls, \"dc=springframework,dc=org\"));\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tstatic class EnvExposingDefaultSpringSecurityContextSource extends DefaultSpringSecurityContextSource {\n\n\t\tEnvExposingDefaultSpringSecurityContextSource(String providerUrl) {\n\t\t\tsuper(providerUrl);\n\t\t}\n\n\t\tHashtable getAuthenticatedEnvForTest(String userDn, String password) {\n\t\t\treturn getAuthenticatedEnv(userDn, password);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateITests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.naming.Context;\nimport javax.naming.NamingException;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.SearchControls;\nimport javax.naming.directory.SearchResult;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ldap.UncategorizedLdapException;\nimport org.springframework.ldap.core.ContextExecutor;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = UnboundIdContainerConfig.class)\npublic class SpringSecurityLdapTemplateITests {\n\n\t@Autowired\n\tprivate DefaultSpringSecurityContextSource contextSource;\n\n\tprivate SpringSecurityLdapTemplate template;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.template = new SpringSecurityLdapTemplate(this.contextSource);\n\t}\n\n\t@Test\n\tpublic void compareOfCorrectValueSucceeds() {\n\t\tassertThat(this.template.compare(\"uid=bob,ou=people\", \"uid\", \"bob\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void compareOfCorrectByteValueSucceeds() {\n\t\tassertThat(this.template.compare(\"uid=bob,ou=people\", \"userPassword\", Utf8.encode(\"bobspassword\"))).isTrue();\n\t}\n\n\t@Test\n\tpublic void compareOfWrongByteValueFails() {\n\t\tassertThat(this.template.compare(\"uid=bob,ou=people\", \"userPassword\", Utf8.encode(\"wrongvalue\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void compareOfWrongValueFails() {\n\t\tassertThat(this.template.compare(\"uid=bob,ou=people\", \"uid\", \"wrongvalue\")).isFalse();\n\t}\n\n\t// @Test\n\t// public void testNameExistsForInValidNameFails() {\n\t// assertThat(template.nameExists(\"ou=doesntexist,dc=springframework,dc=org\")).isFalse();\n\t// }\n\t//\n\t// @Test\n\t// public void testNameExistsForValidNameSucceeds() {\n\t// assertThat(template.nameExists(\"ou=groups,dc=springframework,dc=org\")).isTrue();\n\t// }\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void namingExceptionIsTranslatedCorrectly() {\n\t\tassertThatExceptionOfType(UncategorizedLdapException.class)\n\t\t\t.isThrownBy(() -> this.template.executeReadOnly((ContextExecutor) (dirContext) -> {\n\t\t\t\tthrow new NamingException();\n\t\t\t}));\n\t}\n\n\t@Test\n\tpublic void roleSearchReturnsCorrectNumberOfRoles() {\n\t\tString param = \"uid=ben,ou=people,dc=springframework,dc=org\";\n\n\t\tSet<String> values = this.template.searchForSingleAttributeValues(\"ou=groups\", \"(member={0})\",\n\t\t\t\tnew String[] { param }, \"ou\");\n\n\t\tassertThat(values).as(\"Expected 3 results from search\").hasSize(3);\n\t\tassertThat(values).contains(\"developer\");\n\t\tassertThat(values).contains(\"manager\");\n\t\tassertThat(values).contains(\"submanager\");\n\t}\n\n\t@Test\n\tpublic void testMultiAttributeRetrievalWithNullAttributeNames() {\n\t\tSet<Map<String, List<String>>> values = this.template.searchForMultipleAttributeValues(\"ou=people\", \"(uid={0})\",\n\t\t\t\tnew String[] { \"bob\" }, null);\n\t\tassertThat(values).hasSize(1);\n\t\tMap<String, List<String>> record = values.iterator().next();\n\t\tassertAttributeValue(record, \"uid\", \"bob\");\n\t\tassertAttributeValue(record, \"objectClass\", \"top\", \"person\", \"organizationalPerson\", \"inetOrgPerson\");\n\t\tassertAttributeValue(record, \"cn\", \"Bob Hamilton\");\n\t\tassertAttributeValue(record, \"sn\", \"Hamilton\");\n\t\tassertThat(record.containsKey(\"userPassword\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void testMultiAttributeRetrievalWithZeroLengthAttributeNames() {\n\t\tSet<Map<String, List<String>>> values = this.template.searchForMultipleAttributeValues(\"ou=people\", \"(uid={0})\",\n\t\t\t\tnew String[] { \"bob\" }, new String[0]);\n\t\tassertThat(values).hasSize(1);\n\t\tMap<String, List<String>> record = values.iterator().next();\n\t\tassertAttributeValue(record, \"uid\", \"bob\");\n\t\tassertAttributeValue(record, \"objectClass\", \"top\", \"person\", \"organizationalPerson\", \"inetOrgPerson\");\n\t\tassertAttributeValue(record, \"cn\", \"Bob Hamilton\");\n\t\tassertAttributeValue(record, \"sn\", \"Hamilton\");\n\t\tassertThat(record.containsKey(\"userPassword\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void testMultiAttributeRetrievalWithSpecifiedAttributeNames() {\n\t\tSet<Map<String, List<String>>> values = this.template.searchForMultipleAttributeValues(\"ou=people\", \"(uid={0})\",\n\t\t\t\tnew String[] { \"bob\" }, new String[] { \"uid\", \"cn\", \"sn\" });\n\t\tassertThat(values).hasSize(1);\n\t\tMap<String, List<String>> record = values.iterator().next();\n\t\tassertAttributeValue(record, \"uid\", \"bob\");\n\t\tassertAttributeValue(record, \"cn\", \"Bob Hamilton\");\n\t\tassertAttributeValue(record, \"sn\", \"Hamilton\");\n\t\tassertThat(record.containsKey(\"userPassword\")).isFalse();\n\t\tassertThat(record.containsKey(\"objectClass\")).isFalse();\n\t}\n\n\tprotected void assertAttributeValue(Map<String, List<String>> record, String attributeName, String... values) {\n\t\tassertThat(record).containsKey(attributeName);\n\t\tassertThat(record.get(attributeName)).hasSize(values.length);\n\t\tfor (int i = 0; i < values.length; i++) {\n\t\t\tassertThat(record.get(attributeName).get(i)).isEqualTo(values[i]);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testRoleSearchForMissingAttributeFailsGracefully() {\n\t\tString param = \"uid=ben,ou=people,dc=springframework,dc=org\";\n\n\t\tSet<String> values = this.template.searchForSingleAttributeValues(\"ou=groups\", \"(member={0})\",\n\t\t\t\tnew String[] { param }, \"mail\");\n\n\t\tassertThat(values).isEmpty();\n\t}\n\n\t@Test\n\tpublic void roleSearchWithEscapedCharacterSucceeds() {\n\t\tString param = \"cn=mouse\\\\, jerry,ou=people,dc=springframework,dc=org\";\n\n\t\tSet<String> values = this.template.searchForSingleAttributeValues(\"ou=groups\", \"(member={0})\",\n\t\t\t\tnew String[] { param }, \"cn\");\n\n\t\tassertThat(values).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void nonSpringLdapSearchCodeTestMethod() throws Exception {\n\t\tjava.util.Hashtable<String, String> env = new java.util.Hashtable<>();\n\t\tenv.put(Context.INITIAL_CONTEXT_FACTORY, \"com.sun.jndi.ldap.LdapCtxFactory\");\n\t\tenv.put(Context.PROVIDER_URL, this.contextSource.getUrls()[0]);\n\t\tenv.put(Context.SECURITY_PRINCIPAL, \"\");\n\t\tenv.put(Context.SECURITY_CREDENTIALS, \"\");\n\n\t\tDirContext ctx = new javax.naming.directory.InitialDirContext(env);\n\t\tSearchControls controls = new SearchControls();\n\t\tcontrols.setSearchScope(SearchControls.SUBTREE_SCOPE);\n\t\tcontrols.setReturningObjFlag(true);\n\t\tcontrols.setReturningAttributes(null);\n\t\tString param = \"cn=mouse\\\\, jerry,ou=people,dc=springframework,dc=org\";\n\n\t\tjavax.naming.NamingEnumeration<SearchResult> results = ctx.search(\"ou=groups,dc=springframework,dc=org\",\n\t\t\t\t\"(member={0})\", new String[] { param }, controls);\n\n\t\tassertThat(results.hasMore()).as(\"Expected a result\").isTrue();\n\t}\n\n\t@Test\n\tpublic void searchForSingleEntryWithEscapedCharsInDnSucceeds() {\n\t\tString param = \"mouse, jerry\";\n\n\t\tthis.template.searchForSingleEntry(\"ou=people\", \"(cn={0})\", new String[] { param });\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/UnboundIdContainerConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\n\n/**\n * @author Eddú Meléndez\n */\n@Configuration\npublic class UnboundIdContainerConfig implements DisposableBean {\n\n\tprivate UnboundIdContainer container;\n\n\t@Bean\n\tUnboundIdContainer ldapContainer() {\n\t\tthis.container = new UnboundIdContainer(\"dc=springframework,dc=org\", \"classpath:test-server.ldif\");\n\t\tthis.container.setPort(0);\n\t\treturn this.container;\n\t}\n\n\t@Bean\n\tContextSource contextSource(UnboundIdContainer ldapContainer) {\n\t\treturn new DefaultSpringSecurityContextSource(\n\t\t\t\t\"ldap://127.0.0.1:\" + ldapContainer.getPort() + \"/dc=springframework,dc=org\");\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tthis.container.stop();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/authentication/BindAuthenticatorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport javax.naming.Name;\nimport javax.naming.ldap.LdapContext;\n\nimport org.assertj.core.api.ThrowableAssert.ThrowingCallable;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ldap.AuthenticationException;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.ldap.support.LdapUtils;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.UnboundIdContainerConfig;\nimport org.springframework.security.ldap.search.FilterBasedLdapUserSearch;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\n\n/**\n * Tests for {@link BindAuthenticator}.\n *\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = UnboundIdContainerConfig.class)\npublic class BindAuthenticatorTests {\n\n\t@Autowired\n\tprivate DefaultSpringSecurityContextSource contextSource;\n\n\tprivate BindAuthenticator authenticator;\n\n\tprivate Authentication bob;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticator = new BindAuthenticator(this.contextSource);\n\t\tthis.authenticator.setMessageSource(new SpringSecurityMessageSource());\n\t\tthis.bob = UsernamePasswordAuthenticationToken.unauthenticated(\"bob\", \"bobspassword\");\n\n\t}\n\n\t@Test\n\tpublic void emptyPasswordIsRejected() {\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(\n\t\t\t\t() -> this.authenticator.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"jen\", \"\")));\n\t}\n\n\t@Test\n\tpublic void testAuthenticationWithCorrectPasswordSucceeds() {\n\t\tthis.authenticator.setUserDnPatterns(new String[] { \"uid={0},ou=people\", \"cn={0},ou=people\" });\n\n\t\tDirContextOperations user = this.authenticator.authenticate(this.bob);\n\t\tassertThat(user.getStringAttribute(\"uid\")).isEqualTo(\"bob\");\n\t\tthis.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"mouse, jerry\", \"jerryspassword\"));\n\t}\n\n\t@Test\n\tpublic void testAuthenticationWithInvalidUserNameFails() {\n\t\tthis.authenticator.setUserDnPatterns(new String[] { \"uid={0},ou=people\" });\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"nonexistentsuser\", \"password\")));\n\t}\n\n\t@Test\n\tpublic void testAuthenticationWithUserSearch() throws Exception {\n\t\t// DirContextAdapter ctx = new DirContextAdapter(new\n\t\t// DistinguishedName(\"uid=bob,ou=people\"));\n\t\tthis.authenticator.setUserSearch(new FilterBasedLdapUserSearch(\"ou=people\", \"(uid={0})\", this.contextSource));\n\t\tthis.authenticator.afterPropertiesSet();\n\t\tDirContextOperations result = this.authenticator.authenticate(this.bob);\n\t\t// ensure we are getting the same attributes back\n\t\tassertThat(result.getStringAttribute(\"cn\")).isEqualTo(\"Bob Hamilton\");\n\t\t// SEC-1444\n\t\tthis.authenticator.setUserSearch(new FilterBasedLdapUserSearch(\"ou=people\", \"(cn={0})\", this.contextSource));\n\t\tthis.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"mouse, jerry\", \"jerryspassword\"));\n\t\tthis.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"slash/guy\", \"slashguyspassword\"));\n\t\t// SEC-1661\n\t\tthis.authenticator\n\t\t\t.setUserSearch(new FilterBasedLdapUserSearch(\"ou=\\\\\\\"quoted people\\\\\\\"\", \"(cn={0})\", this.contextSource));\n\t\tthis.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"quote\\\"guy\", \"quoteguyspassword\"));\n\t\tthis.authenticator.setUserSearch(new FilterBasedLdapUserSearch(\"\", \"(cn={0})\", this.contextSource));\n\t\tthis.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"quote\\\"guy\", \"quoteguyspassword\"));\n\t}\n\n\t/*\n\t * @Test public void messingWithEscapedChars() throws Exception {\n\t * Hashtable<String,String> env = new Hashtable<>();\n\t * env.put(Context.INITIAL_CONTEXT_FACTORY, \"com.sun.jndi.ldap.LdapCtxFactory\");\n\t * env.put(Context.PROVIDER_URL, \"ldap://127.0.0.1:22389/dc=springsource,dc=com\");\n\t * env.put(Context.SECURITY_AUTHENTICATION, \"simple\");\n\t * env.put(Context.SECURITY_PRINCIPAL, \"cn=admin,dc=springsource,dc=com\");\n\t * env.put(Context.SECURITY_CREDENTIALS, \"password\");\n\t *\n\t * InitialDirContext idc = new InitialDirContext(env); SearchControls searchControls =\n\t * new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);\n\t * DistinguishedName baseDn = new DistinguishedName(\"ou=\\\\\\\"quoted people\\\\\\\"\");\n\t * NamingEnumeration<SearchResult> matches = idc.search(baseDn, \"(cn=*)\", new Object[]\n\t * {\"quoteguy\"}, searchControls);\n\t *\n\t * while(matches.hasMore()) { SearchResult match = matches.next(); DistinguishedName\n\t * dn = new DistinguishedName(match.getName()); System.out.println(\"**** Match: \" +\n\t * match.getName() + \" ***** \" + dn);\n\t *\n\t * } }\n\t */\n\t@Test\n\tpublic void testAuthenticationWithWrongPasswordFails() {\n\t\tthis.authenticator.setUserDnPatterns(new String[] { \"uid={0},ou=people\" });\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"bob\", \"wrongpassword\")));\n\t}\n\n\t@Test\n\tpublic void testUserDnPatternReturnsCorrectDn() {\n\t\tthis.authenticator.setUserDnPatterns(new String[] { \"cn={0},ou=people\" });\n\t\tassertThat(this.authenticator.getUserDns(\"Joe\").get(0)).isEqualTo(\"cn=Joe,ou=people\");\n\t}\n\n\t@Test\n\tpublic void setAlsoHandleJavaxNamingBindExceptionsWhenTrueThenHandles() throws Exception {\n\t\tBaseLdapPathContextSource contextSource = spy(this.contextSource);\n\t\tBindAuthenticator authenticator = new BindAuthenticator(contextSource);\n\t\tauthenticator.setUserDnPatterns(new String[] { \"uid={0},ou=people\" });\n\t\tLdapContext dirContext = mock(LdapContext.class);\n\t\tgiven(dirContext.getAttributes(any(Name.class), any()))\n\t\t\t.willThrow(new javax.naming.AuthenticationException(\"exception\"));\n\t\tName fullDn = LdapUtils.prepend(LdapUtils.newLdapName(\"uid=bob,ou=people\"), contextSource.getBaseLdapName());\n\t\tgiven(contextSource.getContext(fullDn.toString(), (String) this.bob.getCredentials())).willReturn(dirContext);\n\t\tauthenticator.setAlsoHandleJavaxNamingBindExceptions(true);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(authenticateBob(authenticator));\n\t\tauthenticator.setAlsoHandleJavaxNamingBindExceptions(false);\n\t\tassertThatExceptionOfType(AuthenticationException.class).isThrownBy(authenticateBob(authenticator))\n\t\t\t.withCauseInstanceOf(javax.naming.AuthenticationException.class);\n\t}\n\n\tprivate ThrowingCallable authenticateBob(BindAuthenticator authenticator) {\n\t\treturn () -> authenticator.authenticate(this.bob);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticatorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DistinguishedName;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.password.LdapShaPasswordEncoder;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.UnboundIdContainerConfig;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PasswordComparisonAuthenticator}.\n *\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = UnboundIdContainerConfig.class)\n@SuppressWarnings(\"deprecation\")\npublic class PasswordComparisonAuthenticatorTests {\n\n\t@Autowired\n\tprivate DefaultSpringSecurityContextSource contextSource;\n\n\tprivate PasswordComparisonAuthenticator authenticator;\n\n\tprivate Authentication bob;\n\n\tprivate Authentication ben;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticator = new PasswordComparisonAuthenticator(this.contextSource);\n\t\tthis.authenticator.setPasswordEncoder(NoOpPasswordEncoder.getInstance());\n\t\tthis.authenticator.setUserDnPatterns(new String[] { \"uid={0},ou=people\" });\n\t\tthis.bob = UsernamePasswordAuthenticationToken.unauthenticated(\"bob\", \"bobspassword\");\n\t\tthis.ben = UsernamePasswordAuthenticationToken.unauthenticated(\"ben\", \"benspassword\");\n\t}\n\n\t@Test\n\tpublic void testAllAttributesAreRetrievedByDefault() {\n\t\tDirContextAdapter user = (DirContextAdapter) this.authenticator.authenticate(this.bob);\n\t\t// System.out.println(user.getAttributes().toString());\n\t\tassertThat(user.getAttributes().size()).withFailMessage(\"User should have 5 attributes\").isEqualTo(5);\n\t}\n\n\t@Test\n\tpublic void testFailedSearchGivesUserNotFoundException() throws Exception {\n\t\tthis.authenticator = new PasswordComparisonAuthenticator(this.contextSource);\n\t\tassertThat(this.authenticator.getUserDns(\"Bob\")).withFailMessage(\"User DN matches shouldn't be available\")\n\t\t\t.isEmpty();\n\t\tthis.authenticator.setUserSearch(new MockUserSearch(null));\n\t\tthis.authenticator.afterPropertiesSet();\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> this.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"Joe\", \"pass\")));\n\t}\n\n\t@Test\n\tpublic void testLdapPasswordCompareFailsWithWrongPassword() {\n\t\t// Don't retrieve the password\n\t\tthis.authenticator.setUserAttributes(new String[] { \"uid\", \"cn\", \"sn\" });\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"bob\", \"wrongpass\")));\n\t}\n\n\t@Test\n\tpublic void testMultipleDnPatternsWorkOk() {\n\t\tthis.authenticator.setUserDnPatterns(new String[] { \"uid={0},ou=nonexistent\", \"uid={0},ou=people\" });\n\t\tthis.authenticator.authenticate(this.bob);\n\t}\n\n\t@Test\n\tpublic void testOnlySpecifiedAttributesAreRetrieved() {\n\t\tthis.authenticator.setUserAttributes(new String[] { \"uid\", \"userPassword\" });\n\n\t\tDirContextAdapter user = (DirContextAdapter) this.authenticator.authenticate(this.bob);\n\t\tassertThat(user.getAttributes().size()).withFailMessage(\"Should have retrieved 2 attribute (uid)\").isEqualTo(2);\n\t}\n\n\t@Test\n\tpublic void testLdapCompareSucceedsWithCorrectPassword() {\n\t\t// Don't retrieve the password\n\t\tthis.authenticator.setUserAttributes(new String[] { \"uid\" });\n\t\tthis.authenticator.authenticate(this.bob);\n\t}\n\n\t@Test\n\tpublic void testLdapCompareSucceedsWithShaEncodedPassword() {\n\t\t// Don't retrieve the password\n\t\tthis.authenticator.setUserAttributes(new String[] { \"uid\" });\n\t\tthis.authenticator.setPasswordEncoder(new LdapShaPasswordEncoder(KeyGenerators.shared(0)));\n\t\tthis.authenticator.setUsePasswordAttrCompare(false);\n\t\tthis.authenticator.authenticate(this.ben);\n\t}\n\n\t@Test\n\tpublic void testPasswordEncoderCantBeNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authenticator.setPasswordEncoder(null));\n\t}\n\n\t@Test\n\tpublic void testUseOfDifferentPasswordAttributeSucceeds() {\n\t\tthis.authenticator.setPasswordAttributeName(\"uid\");\n\t\tthis.authenticator.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"bob\", \"bob\"));\n\t}\n\n\t@Test\n\tpublic void testLdapCompareWithDifferentPasswordAttributeSucceeds() {\n\t\tthis.authenticator.setUserAttributes(new String[] { \"uid\" });\n\t\tthis.authenticator.setPasswordAttributeName(\"cn\");\n\t\tthis.authenticator.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"ben\", \"Ben Alex\"));\n\t}\n\n\t@Test\n\tpublic void testWithUserSearch() {\n\t\tthis.authenticator = new PasswordComparisonAuthenticator(this.contextSource);\n\t\tthis.authenticator.setPasswordEncoder(NoOpPasswordEncoder.getInstance());\n\t\tassertThat(this.authenticator.getUserDns(\"Bob\")).withFailMessage(\"User DN matches shouldn't be available\")\n\t\t\t.isEmpty();\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(\"uid=Bob,ou=people\"));\n\t\tctx.setAttributeValue(\"userPassword\", \"bobspassword\");\n\n\t\tthis.authenticator.setUserSearch(new MockUserSearch(ctx));\n\t\tthis.authenticator\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"shouldntbeused\", \"bobspassword\"));\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.search;\n\nimport javax.naming.ldap.LdapName;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.UnboundIdContainerConfig;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for FilterBasedLdapUserSearch.\n *\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = UnboundIdContainerConfig.class)\npublic class FilterBasedLdapUserSearchTests {\n\n\t@Autowired\n\tprivate DefaultSpringSecurityContextSource contextSource;\n\n\t@Test\n\tpublic void basicSearchSucceeds() throws Exception {\n\t\tFilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(\"ou=people\", \"(uid={0})\", this.contextSource);\n\t\tlocator.setSearchSubtree(false);\n\t\tlocator.setSearchTimeLimit(0);\n\t\tlocator.setDerefLinkFlag(false);\n\n\t\tDirContextOperations bob = locator.searchForUser(\"bob\");\n\t\tassertThat(bob.getStringAttribute(\"uid\")).isEqualTo(\"bob\");\n\n\t\tassertThat(bob.getDn()).isEqualTo(new LdapName(\"uid=bob,ou=people\"));\n\t}\n\n\t@Test\n\tpublic void searchForNameWithCommaSucceeds() throws Exception {\n\t\tFilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(\"ou=people\", \"(uid={0})\", this.contextSource);\n\t\tlocator.setSearchSubtree(false);\n\n\t\tDirContextOperations jerry = locator.searchForUser(\"jerry\");\n\t\tassertThat(jerry.getStringAttribute(\"uid\")).isEqualTo(\"jerry\");\n\n\t\tassertThat(jerry.getDn()).isEqualTo(new LdapName(\"cn=mouse\\\\, jerry,ou=people\"));\n\t}\n\n\t// Try some funny business with filters.\n\t@Test\n\tpublic void extraFilterPartToExcludeBob() {\n\t\tFilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(\"ou=people\",\n\t\t\t\t\"(&(cn=*)(!(|(uid={0})(uid=rod)(uid=jerry)(uid=slashguy)(uid=javadude)(uid=groovydude)(uid=closuredude)(uid=scaladude))))\",\n\t\t\t\tthis.contextSource);\n\n\t\t// Search for bob, get back ben...\n\t\tDirContextOperations ben = locator.searchForUser(\"bob\");\n\t\tassertThat(ben.getStringAttribute(\"cn\")).isEqualTo(\"Ben Alex\");\n\t}\n\n\t@Test\n\tpublic void searchFailsOnMultipleMatches() {\n\t\tFilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(\"ou=people\", \"(cn=*)\", this.contextSource);\n\t\tassertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)\n\t\t\t.isThrownBy(() -> locator.searchForUser(\"Ignored\"));\n\t}\n\n\t@Test\n\tpublic void searchForInvalidUserFails() {\n\t\tFilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(\"ou=people\", \"(uid={0})\", this.contextSource);\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> locator.searchForUser(\"Joe\"));\n\t}\n\n\t@Test\n\tpublic void subTreeSearchSucceeds() throws Exception {\n\t\t// Don't set the searchBase, so search from the root.\n\t\tFilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(\"\", \"(cn={0})\", this.contextSource);\n\t\tlocator.setSearchSubtree(true);\n\n\t\tDirContextOperations ben = locator.searchForUser(\"Ben Alex\");\n\t\tassertThat(ben.getStringAttribute(\"uid\")).isEqualTo(\"ben\");\n\n\t\tassertThat(ben.getDn()).isEqualTo(new LdapName(\"uid=ben,ou=people\"));\n\t}\n\n\t@Test\n\tpublic void searchWithDifferentSearchBaseIsSuccessful() {\n\t\tFilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(\"ou=otherpeople\", \"(cn={0})\",\n\t\t\t\tthis.contextSource);\n\t\tDirContextOperations joe = locator.searchForUser(\"Joe Smeth\");\n\t\tassertThat(joe.getStringAttribute(\"cn\")).isEqualTo(\"Joe Smeth\");\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchWithSpacesTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.search;\n\nimport javax.naming.ldap.LdapName;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Additional tests for {@link FilterBasedLdapUserSearch} with spaces in the base dn.\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = FilterBasedLdapUserSearchWithSpacesTests.UnboundIdContainerWithSpacesConfig.class)\npublic class FilterBasedLdapUserSearchWithSpacesTests {\n\n\t@Autowired\n\tprivate DefaultSpringSecurityContextSource contextSource;\n\n\t// gh-9742\n\t@Test\n\tpublic void searchForUserWhenSpacesInBaseDnThenSuccess() throws Exception {\n\t\tFilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch(\"ou=space cadets\", \"(uid={0})\",\n\t\t\t\tthis.contextSource);\n\t\tlocator.setSearchSubtree(false);\n\t\tlocator.setSearchTimeLimit(0);\n\t\tlocator.setDerefLinkFlag(false);\n\n\t\tDirContextOperations bob = locator.searchForUser(\"space cadet\");\n\t\tassertThat(bob.getStringAttribute(\"uid\")).isEqualTo(\"space cadet\");\n\t\tassertThat(bob.getDn()).isEqualTo(new LdapName(\"uid=space cadet,ou=space cadets\"));\n\t}\n\n\t@Configuration\n\tstatic class UnboundIdContainerWithSpacesConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container;\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapContainer() {\n\t\t\tthis.container = new UnboundIdContainer(\"dc=spring framework,dc=org\",\n\t\t\t\t\t\"classpath:test-server-with-spaces.ldif\");\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Bean\n\t\tContextSource contextSource(UnboundIdContainer ldapContainer) {\n\t\t\treturn new DefaultSpringSecurityContextSource(\n\t\t\t\t\t\"ldap://127.0.0.1:\" + ldapContainer.getPort() + \"/dc=spring%20framework,dc=org\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerLdifTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.server;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link UnboundIdContainer}, specifically relating to LDIF file detection.\n *\n * @author Eleftheria Stein\n */\npublic class UnboundIdContainerLdifTests {\n\n\tAnnotationConfigApplicationContext appCtx;\n\n\t@AfterEach\n\tpublic void closeAppContext() {\n\t\tif (this.appCtx != null) {\n\t\t\tthis.appCtx.close();\n\t\t\tthis.appCtx = null;\n\t\t}\n\t}\n\n\t@Test\n\tpublic void unboundIdContainerWhenCustomLdifNameThenLdifLoaded() {\n\t\tthis.appCtx = new AnnotationConfigApplicationContext(CustomLdifConfig.class);\n\n\t\tDefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) this.appCtx\n\t\t\t.getBean(ContextSource.class);\n\n\t\tSpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource);\n\t\tassertThat(template.compare(\"uid=bob,ou=people\", \"uid\", \"bob\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void unboundIdContainerWhenWildcardLdifNameThenLdifLoaded() {\n\t\tthis.appCtx = new AnnotationConfigApplicationContext(WildcardLdifConfig.class);\n\n\t\tDefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) this.appCtx\n\t\t\t.getBean(ContextSource.class);\n\n\t\tSpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource);\n\t\tassertThat(template.compare(\"uid=bob,ou=people\", \"uid\", \"bob\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void unboundIdContainerWhenMalformedLdifThenException() {\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t.isThrownBy(() -> this.appCtx = new AnnotationConfigApplicationContext(MalformedLdifConfig.class))\n\t\t\t.withCauseInstanceOf(IllegalStateException.class)\n\t\t\t.withMessageContaining(\"Unable to load LDIF classpath:test-server-malformed.txt\");\n\t}\n\n\t@Test\n\tpublic void unboundIdContainerWhenMissingLdifThenException() {\n\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t.isThrownBy(() -> this.appCtx = new AnnotationConfigApplicationContext(MissingLdifConfig.class))\n\t\t\t.withCauseInstanceOf(IllegalStateException.class)\n\t\t\t.withMessageContaining(\"Unable to load LDIF classpath:does-not-exist.ldif\");\n\t}\n\n\t@Test\n\tpublic void unboundIdContainerWhenWildcardLdifNotFoundThenProceeds() {\n\t\tnew AnnotationConfigApplicationContext(WildcardNoLdifConfig.class);\n\t}\n\n\t@Configuration\n\tstatic class CustomLdifConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container = new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\"classpath:test-server.ldif\");\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapContainer() {\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Bean\n\t\tContextSource contextSource(UnboundIdContainer container) {\n\t\t\treturn new DefaultSpringSecurityContextSource(\n\t\t\t\t\t\"ldap://127.0.0.1:\" + container.getPort() + \"/dc=springframework,dc=org\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class WildcardLdifConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container = new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\"classpath*:test-server.ldif\");\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapContainer() {\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Bean\n\t\tContextSource contextSource(UnboundIdContainer container) {\n\t\t\treturn new DefaultSpringSecurityContextSource(\n\t\t\t\t\t\"ldap://127.0.0.1:\" + container.getPort() + \"/dc=springframework,dc=org\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class MalformedLdifConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container = new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\"classpath:test-server-malformed.txt\");\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapContainer() {\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class MissingLdifConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container = new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\"classpath:does-not-exist.ldif\");\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapContainer() {\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class WildcardNoLdifConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container = new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\"classpath*:*.test.ldif\");\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapContainer() {\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.server;\n\nimport java.io.IOException;\nimport java.net.ServerSocket;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.GenericApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Eddú Meléndez\n */\npublic class UnboundIdContainerTests {\n\n\t@Test\n\tpublic void startLdapServer() throws Exception {\n\t\tUnboundIdContainer server = new UnboundIdContainer(\"dc=springframework,dc=org\", \"classpath:test-server.ldif\");\n\t\tserver.setApplicationContext(new GenericApplicationContext());\n\t\tList<Integer> ports = getDefaultPorts(1);\n\t\tserver.setPort(ports.get(0));\n\n\t\ttry {\n\t\t\tserver.afterPropertiesSet();\n\t\t\tassertThat(server.getPort()).isEqualTo(ports.get(0));\n\t\t}\n\t\tfinally {\n\t\t\tserver.destroy();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void afterPropertiesSetWhenPortIsZeroThenRandomPortIsSelected() throws Exception {\n\t\tUnboundIdContainer server = new UnboundIdContainer(\"dc=springframework,dc=org\", null);\n\t\tserver.setPort(0);\n\n\t\ttry {\n\t\t\tserver.afterPropertiesSet();\n\t\t\tassertThat(server.getPort()).isNotEqualTo(0);\n\t\t}\n\t\tfinally {\n\t\t\tserver.destroy();\n\t\t}\n\t}\n\n\tprivate List<Integer> getDefaultPorts(int count) throws IOException {\n\t\tList<ServerSocket> connections = new ArrayList<>();\n\t\tList<Integer> availablePorts = new ArrayList<>(count);\n\t\ttry {\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tServerSocket socket = new ServerSocket(0);\n\t\t\t\tconnections.add(socket);\n\t\t\t\tavailablePorts.add(socket.getLocalPort());\n\t\t\t}\n\t\t\treturn availablePorts;\n\t\t}\n\t\tfinally {\n\t\t\tfor (ServerSocket conn : connections) {\n\t\t\t\tconn.close();\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DistinguishedName;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Dayan Kodippily\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(\n\t\tclasses = DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.UnboundIdContainerWithUndefinedGroupRoleAttributeConfig.class)\npublic class DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests {\n\n\t@Autowired\n\tprivate DefaultSpringSecurityContextSource contextSource;\n\n\tprivate DefaultLdapAuthoritiesPopulator populator;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.populator = new DefaultLdapAuthoritiesPopulator(this.contextSource, \"ou=groups\");\n\t\tthis.populator.setIgnorePartialResultException(false);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void groupSearchDoesNotAllowNullRoles() {\n\t\tthis.populator.setRolePrefix(\"ROLE_\");\n\t\tthis.populator.setGroupRoleAttribute(\"ou\");\n\t\tthis.populator.setSearchSubtree(true);\n\t\tthis.populator.setSearchSubtree(false);\n\t\tthis.populator.setConvertToUpperCase(true);\n\t\tthis.populator.setGroupSearchFilter(\"(member={0})\");\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(\n\t\t\t\tnew DistinguishedName(\"uid=dayan,ou=people,dc=springframework,dc=org\"));\n\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(this.populator.getGrantedAuthorities(ctx, \"dayan\"));\n\n\t\tassertThat(authorities).as(\"Should have 1 role\").hasSize(2);\n\n\t\tassertThat(authorities).contains(\"ROLE_DEVELOPER\");\n\t\tassertThat(authorities).contains(\"ROLE_\");\n\t}\n\n\t@Configuration\n\tstatic class UnboundIdContainerWithUndefinedGroupRoleAttributeConfig implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container;\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapContainer() {\n\t\t\tthis.container = new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\t\"classpath:test-server-with-undefined-group-role-attributes.ldif\");\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Bean\n\t\tContextSource contextSource(UnboundIdContainer ldapContainer) {\n\t\t\treturn new DefaultSpringSecurityContextSource(\n\t\t\t\t\t\"ldap://127.0.0.1:\" + ldapContainer.getPort() + \"/dc=springframework,dc=org\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.DistinguishedName;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\nimport org.springframework.security.ldap.UnboundIdContainerConfig;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = UnboundIdContainerConfig.class)\n@SuppressWarnings({ \"deprecation\" })\npublic class DefaultLdapAuthoritiesPopulatorTests {\n\n\t@Autowired\n\tprivate ContextSource contextSource;\n\n\tprivate DefaultLdapAuthoritiesPopulator populator;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.populator = new DefaultLdapAuthoritiesPopulator(this.contextSource, \"ou=groups\");\n\t\tthis.populator.setIgnorePartialResultException(false);\n\t}\n\n\t@Test\n\tpublic void defaultRoleIsAssignedWhenSet() {\n\t\tthis.populator.setDefaultRole(\"ROLE_USER\");\n\t\tassertThat(this.populator.getContextSource()).isSameAs(this.contextSource);\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(new DistinguishedName(\"cn=notfound\"));\n\n\t\tCollection<GrantedAuthority> authorities = this.populator.getGrantedAuthorities(ctx, \"notfound\");\n\t\tassertThat(authorities).hasSize(1);\n\t\tassertThat(AuthorityUtils.authorityListToSet(authorities)).contains(\"ROLE_USER\");\n\t}\n\n\t@Test\n\tpublic void nullSearchBaseIsAccepted() {\n\t\tthis.populator = new DefaultLdapAuthoritiesPopulator(this.contextSource, null);\n\t\tthis.populator.setDefaultRole(\"ROLE_USER\");\n\n\t\tCollection<GrantedAuthority> authorities = this.populator\n\t\t\t.getGrantedAuthorities(new DirContextAdapter(new DistinguishedName(\"cn=notused\")), \"notused\");\n\t\tassertThat(authorities).hasSize(1);\n\t\tassertThat(AuthorityUtils.authorityListToSet(authorities)).contains(\"ROLE_USER\");\n\t}\n\n\t@Test\n\tpublic void groupSearchReturnsExpectedRoles() {\n\t\tthis.populator.setRolePrefix(\"ROLE_\");\n\t\tthis.populator.setGroupRoleAttribute(\"ou\");\n\t\tthis.populator.setSearchSubtree(true);\n\t\tthis.populator.setSearchSubtree(false);\n\t\tthis.populator.setConvertToUpperCase(true);\n\t\tthis.populator.setGroupSearchFilter(\"(member={0})\");\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(\n\t\t\t\tnew DistinguishedName(\"uid=ben,ou=people,dc=springframework,dc=org\"));\n\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(this.populator.getGrantedAuthorities(ctx, \"ben\"));\n\n\t\tassertThat(authorities).as(\"Should have 2 roles\").hasSize(2);\n\n\t\tassertThat(authorities).contains(\"ROLE_DEVELOPER\");\n\t\tassertThat(authorities).contains(\"ROLE_MANAGER\");\n\t}\n\n\t@Test\n\tpublic void useOfUsernameParameterReturnsExpectedRoles() {\n\t\tthis.populator.setGroupRoleAttribute(\"ou\");\n\t\tthis.populator.setConvertToUpperCase(true);\n\t\tthis.populator.setGroupSearchFilter(\"(ou={1})\");\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(\n\t\t\t\tnew DistinguishedName(\"uid=ben,ou=people,dc=springframework,dc=org\"));\n\n\t\tSet<String> authorities = AuthorityUtils\n\t\t\t.authorityListToSet(this.populator.getGrantedAuthorities(ctx, \"manager\"));\n\n\t\tassertThat(authorities).as(\"Should have 1 role\").hasSize(1);\n\t\tassertThat(authorities).contains(\"ROLE_MANAGER\");\n\t}\n\n\t@Test\n\tpublic void subGroupRolesAreNotFoundByDefault() {\n\t\tthis.populator.setGroupRoleAttribute(\"ou\");\n\t\tthis.populator.setConvertToUpperCase(true);\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(\n\t\t\t\tnew DistinguishedName(\"uid=ben,ou=people,dc=springframework,dc=org\"));\n\n\t\tSet<String> authorities = AuthorityUtils\n\t\t\t.authorityListToSet(this.populator.getGrantedAuthorities(ctx, \"manager\"));\n\n\t\tassertThat(authorities).as(\"Should have 2 roles\").hasSize(2);\n\t\tassertThat(authorities).contains(\"ROLE_MANAGER\");\n\t\tassertThat(authorities).contains(\"ROLE_DEVELOPER\");\n\t}\n\n\t@Test\n\tpublic void subGroupRolesAreFoundWhenSubtreeSearchIsEnabled() {\n\t\tthis.populator.setGroupRoleAttribute(\"ou\");\n\t\tthis.populator.setConvertToUpperCase(true);\n\t\tthis.populator.setSearchSubtree(true);\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(\n\t\t\t\tnew DistinguishedName(\"uid=ben,ou=people,dc=springframework,dc=org\"));\n\n\t\tSet<String> authorities = AuthorityUtils\n\t\t\t.authorityListToSet(this.populator.getGrantedAuthorities(ctx, \"manager\"));\n\n\t\tassertThat(authorities).as(\"Should have 3 roles\").hasSize(3);\n\t\tassertThat(authorities).contains(\"ROLE_MANAGER\");\n\t\tassertThat(authorities).contains(\"ROLE_SUBMANAGER\");\n\t\tassertThat(authorities).contains(\"ROLE_DEVELOPER\");\n\t}\n\n\t@Test\n\tpublic void extraRolesAreAdded() {\n\t\tthis.populator = new DefaultLdapAuthoritiesPopulator(this.contextSource, null) {\n\t\t\t@Override\n\t\t\tprotected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {\n\t\t\t\treturn new HashSet<>(AuthorityUtils.createAuthorityList(\"ROLE_EXTRA\"));\n\t\t\t}\n\t\t};\n\n\t\tCollection<GrantedAuthority> authorities = this.populator\n\t\t\t.getGrantedAuthorities(new DirContextAdapter(new DistinguishedName(\"cn=notused\")), \"notused\");\n\t\tassertThat(authorities).hasSize(1);\n\t\tassertThat(AuthorityUtils.authorityListToSet(authorities)).contains(\"ROLE_EXTRA\");\n\t}\n\n\t@Test\n\tpublic void userDnWithEscapedCharacterParameterReturnsExpectedRoles() {\n\t\tthis.populator.setGroupRoleAttribute(\"ou\");\n\t\tthis.populator.setConvertToUpperCase(true);\n\t\tthis.populator.setGroupSearchFilter(\"(member={0})\");\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(\n\t\t\t\tnew DistinguishedName(\"cn=mouse\\\\, jerry,ou=people,dc=springframework,dc=org\"));\n\n\t\tSet<String> authorities = AuthorityUtils\n\t\t\t.authorityListToSet(this.populator.getGrantedAuthorities(ctx, \"notused\"));\n\n\t\tassertThat(authorities).as(\"Should have 1 role\").hasSize(1);\n\t\tassertThat(authorities).contains(\"ROLE_MANAGER\");\n\t}\n\n\t@Test\n\tpublic void customAuthoritiesMappingFunction() {\n\t\tthis.populator.setAuthorityMapper((record) -> {\n\t\t\tString dn = record.get(SpringSecurityLdapTemplate.DN_KEY).get(0);\n\t\t\tString role = record.get(this.populator.getGroupRoleAttribute()).get(0);\n\t\t\treturn new LdapAuthority(role, dn);\n\t\t});\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(\n\t\t\t\tnew DistinguishedName(\"cn=mouse\\\\, jerry,ou=people,dc=springframework,dc=org\"));\n\n\t\tCollection<GrantedAuthority> authorities = this.populator.getGrantedAuthorities(ctx, \"notused\");\n\n\t\tassertThat(authorities).allMatch(LdapAuthority.class::isInstance);\n\t}\n\n\t@Test\n\tpublic void customAuthoritiesMappingFunctionThrowsIfNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.populator.setAuthorityMapper(null));\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerModifyPasswordTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\nimport org.springframework.security.ldap.server.UnboundIdContainer;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link LdapUserDetailsManager#changePassword}, specifically relating to the\n * use of the Modify Password Extended Operation.\n *\n * @author Josh Cummings\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = LdapUserDetailsManagerModifyPasswordTests.UnboundIdContainerConfiguration.class)\npublic class LdapUserDetailsManagerModifyPasswordTests {\n\n\tLdapUserDetailsManager userDetailsManager;\n\n\t@Autowired\n\tContextSource contextSource;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.userDetailsManager = new LdapUserDetailsManager(this.contextSource);\n\t\tthis.userDetailsManager.setUsePasswordModifyExtensionOperation(true);\n\t\tthis.userDetailsManager.setUsernameMapper(new DefaultLdapUsernameToDnMapper(\"ou=people\", \"uid\"));\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"bob\", password = \"bobspassword\", authorities = \"ROLE_USER\")\n\tpublic void changePasswordWhenOldPasswordIsIncorrectThenThrowsException() {\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> this.userDetailsManager.changePassword(\"wrongoldpassword\", \"bobsnewpassword\"));\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"bob\", password = \"bobspassword\", authorities = \"ROLE_USER\")\n\tpublic void changePasswordWhenOldPasswordIsCorrectThenPasses() {\n\t\tSpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(this.contextSource);\n\n\t\tthis.userDetailsManager.changePassword(\"bobspassword\",\n\t\t\t\t\"bobsshinynewandformidablylongandnearlyimpossibletorememberthoughdemonstrablyhardtocrackduetoitshighlevelofentropypasswordofjustice\");\n\n\t\tassertThat(template.compare(\"uid=bob,ou=people\", \"userPassword\",\n\t\t\t\t\"bobsshinynewandformidablylongandnearlyimpossibletorememberthoughdemonstrablyhardtocrackduetoitshighlevelofentropypasswordofjustice\"))\n\t\t\t.isTrue();\n\t}\n\n\t@Configuration\n\tstatic class UnboundIdContainerConfiguration implements DisposableBean {\n\n\t\tprivate UnboundIdContainer container = new UnboundIdContainer(\"dc=springframework,dc=org\",\n\t\t\t\t\"classpath:test-server.ldif\");\n\n\t\t@Bean\n\t\tUnboundIdContainer ldapContainer() {\n\t\t\tthis.container.setPort(0);\n\t\t\treturn this.container;\n\t\t}\n\n\t\t@Bean\n\t\tContextSource contextSource(UnboundIdContainer container) {\n\t\t\treturn new DefaultSpringSecurityContextSource(\n\t\t\t\t\t\"ldap://127.0.0.1:\" + container.getPort() + \"/dc=springframework,dc=org\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() throws Exception {\n\t\t\tthis.container.stop();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\nimport org.springframework.security.ldap.UnboundIdContainerConfig;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @author Roman Zabaluev\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = UnboundIdContainerConfig.class)\npublic class LdapUserDetailsManagerTests {\n\n\t@Autowired\n\tprivate ContextSource contextSource;\n\n\tprivate static final List<GrantedAuthority> TEST_AUTHORITIES = AuthorityUtils.createAuthorityList(\"ROLE_CLOWNS\",\n\t\t\t\"ROLE_ACROBATS\");\n\n\tprivate static final String DEFAULT_ROLE_PREFIX = \"ROLE_\";\n\n\tprivate LdapUserDetailsManager mgr;\n\n\tprivate SpringSecurityLdapTemplate template;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.mgr = new LdapUserDetailsManager(this.contextSource);\n\t\tthis.template = new SpringSecurityLdapTemplate(this.contextSource);\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\n\t\tctx.setAttributeValue(\"objectclass\", \"organizationalUnit\");\n\t\tctx.setAttributeValue(\"ou\", \"test people\");\n\t\tthis.template.bind(\"ou=test people\", ctx, null);\n\n\t\tctx.setAttributeValue(\"ou\", \"testgroups\");\n\t\tthis.template.bind(\"ou=testgroups\", ctx, null);\n\n\t\tDirContextAdapter group = new DirContextAdapter();\n\n\t\tgroup.setAttributeValue(\"objectclass\", \"groupOfNames\");\n\t\tgroup.setAttributeValue(\"cn\", \"clowns\");\n\t\tgroup.setAttributeValue(\"member\", \"cn=nobody,ou=test people,dc=springframework,dc=org\");\n\t\tthis.template.bind(\"cn=clowns,ou=testgroups\", group, null);\n\n\t\tgroup.setAttributeValue(\"cn\", \"acrobats\");\n\t\tthis.template.bind(\"cn=acrobats,ou=testgroups\", group, null);\n\n\t\tthis.mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper(\"ou=test people\", \"uid\"));\n\t\tthis.mgr.setGroupSearchBase(\"ou=testgroups\");\n\t\tthis.mgr.setGroupRoleAttributeName(\"cn\");\n\t\tthis.mgr.setGroupMemberAttributeName(\"member\");\n\t\tthis.mgr.setUserDetailsMapper(new PersonContextMapper());\n\t}\n\n\t@AfterEach\n\tpublic void onTearDown() {\n\t\t// Iterator people = template.list(\"ou=testpeople\").iterator();\n\n\t\t// DirContext rootCtx = new DirContextAdapter(new\n\t\t// DistinguishedName(getInitialCtxFactory().getRootDn()));\n\t\t//\n\t\t// while(people.hasNext()) {\n\t\t// template.unbind((String) people.next() + \",ou=testpeople\");\n\t\t// }\n\n\t\tthis.template.unbind(\"ou=test people\", true);\n\t\tthis.template.unbind(\"ou=testgroups\", true);\n\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testLoadUserByUsernameReturnsCorrectData() {\n\t\tthis.mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper(\"ou=people\", \"uid\"));\n\t\tthis.mgr.setGroupSearchBase(\"ou=groups\");\n\t\tLdapUserDetails bob = (LdapUserDetails) this.mgr.loadUserByUsername(\"bob\");\n\t\tassertThat(bob.getUsername()).isEqualTo(\"bob\");\n\t\tassertThat(bob.getDn()).isEqualTo(\"uid=bob,ou=people,dc=springframework,dc=org\");\n\t\tassertThat(bob.getPassword()).isEqualTo(\"bobspassword\");\n\n\t\tassertThat(bob.getAuthorities()).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void testLoadingInvalidUsernameThrowsUsernameNotFoundException() {\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> this.mgr.loadUserByUsername(\"jim\"));\n\t}\n\n\t@Test\n\tpublic void testUserExistsReturnsTrueForValidUser() {\n\t\tthis.mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper(\"ou=people\", \"uid\"));\n\t\tassertThat(this.mgr.userExists(\"bob\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void testUserExistsReturnsFalseForInValidUser() {\n\t\tassertThat(this.mgr.userExists(\"jim\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void testCreateNewUserSucceeds() {\n\t\tInetOrgPerson.Essence p = new InetOrgPerson.Essence();\n\t\tp.setCarLicense(\"XXX\");\n\t\tp.setCn(new String[] { \"Joe Smeth\" });\n\t\tp.setDepartmentNumber(\"5679\");\n\t\tp.setDescription(\"Some description\");\n\t\tp.setDn(\"whocares\");\n\t\tp.setEmployeeNumber(\"E781\");\n\t\tp.setInitials(\"J\");\n\t\tp.setMail(\"joe@smeth.com\");\n\t\tp.setMobile(\"+44776542911\");\n\t\tp.setOu(\"Joes Unit\");\n\t\tp.setO(\"Organization\");\n\t\tp.setRoomNumber(\"500X\");\n\t\tp.setSn(\"Smeth\");\n\t\tp.setUid(\"joe\");\n\n\t\tp.setAuthorities(TEST_AUTHORITIES);\n\n\t\tthis.mgr.createUser(p.createUserDetails());\n\t}\n\n\t@Test\n\tpublic void testDeleteUserSucceeds() {\n\t\tInetOrgPerson.Essence p = new InetOrgPerson.Essence();\n\t\tp.setDn(\"whocares\");\n\t\tp.setCn(new String[] { \"Don Smeth\" });\n\t\tp.setSn(\"Smeth\");\n\t\tp.setUid(\"don\");\n\t\tp.setAuthorities(TEST_AUTHORITIES);\n\n\t\tthis.mgr.createUser(p.createUserDetails());\n\t\tthis.mgr.setUserDetailsMapper(new InetOrgPersonContextMapper());\n\n\t\tInetOrgPerson don = (InetOrgPerson) this.mgr.loadUserByUsername(\"don\");\n\n\t\tassertThat(don.getAuthorities()).hasSize(2);\n\n\t\tthis.mgr.deleteUser(\"don\");\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> this.mgr.loadUserByUsername(\"don\"));\n\n\t\t// Check that no authorities are left\n\t\tassertThat(this.mgr.getUserAuthorities(this.mgr.usernameMapper.buildLdapName(\"don\"), \"don\")).hasSize(0);\n\t}\n\n\t@Test\n\tpublic void testPasswordChangeWithCorrectOldPasswordSucceeds() {\n\t\tInetOrgPerson.Essence p = new InetOrgPerson.Essence();\n\t\tp.setDn(\"whocares\");\n\t\tp.setCn(new String[] { \"John Yossarian\" });\n\t\tp.setSn(\"Yossarian\");\n\t\tp.setUid(\"johnyossarian\");\n\t\tp.setPassword(\"yossarianspassword\");\n\t\tp.setAuthorities(TEST_AUTHORITIES);\n\n\t\tthis.mgr.createUser(p.createUserDetails());\n\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.authenticated(\"johnyossarian\", \"yossarianspassword\",\n\t\t\t\t\tTEST_AUTHORITIES));\n\n\t\tthis.mgr.changePassword(\"yossarianspassword\", \"yossariansnewpassword\");\n\n\t\tassertThat(this.template.compare(\"uid=johnyossarian,ou=test people\", \"userPassword\", \"yossariansnewpassword\"))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void testPasswordChangeUsesCustomSecurityContextHolderStrategy() {\n\t\tInetOrgPerson.Essence p = new InetOrgPerson.Essence();\n\t\tp.setDn(\"whocares\");\n\t\tp.setCn(new String[] { \"John Yossarian\" });\n\t\tp.setSn(\"Yossarian\");\n\t\tp.setUid(\"johnyossarian\");\n\t\tp.setPassword(\"yossarianspassword\");\n\t\tp.setAuthorities(TEST_AUTHORITIES);\n\n\t\tthis.mgr.createUser(p.createUserDetails());\n\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(UsernamePasswordAuthenticationToken\n\t\t\t.authenticated(\"johnyossarian\", \"yossarianspassword\", TEST_AUTHORITIES)));\n\t\tthis.mgr.setSecurityContextHolderStrategy(strategy);\n\n\t\tthis.mgr.changePassword(\"yossarianspassword\", \"yossariansnewpassword\");\n\n\t\tassertThat(this.template.compare(\"uid=johnyossarian,ou=test people\", \"userPassword\", \"yossariansnewpassword\"))\n\t\t\t.isTrue();\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void testPasswordChangeWithWrongOldPasswordFails() {\n\t\tInetOrgPerson.Essence p = new InetOrgPerson.Essence();\n\t\tp.setDn(\"whocares\");\n\t\tp.setCn(new String[] { \"John Yossarian\" });\n\t\tp.setSn(\"Yossarian\");\n\t\tp.setUid(\"johnyossarian\");\n\t\tp.setPassword(\"yossarianspassword\");\n\t\tp.setAuthorities(TEST_AUTHORITIES);\n\t\tthis.mgr.createUser(p.createUserDetails());\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(UsernamePasswordAuthenticationToken.authenticated(\"johnyossarian\", \"yossarianspassword\",\n\t\t\t\t\tTEST_AUTHORITIES));\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> this.mgr.changePassword(\"wrongpassword\", \"yossariansnewpassword\"));\n\t}\n\n\t@Test\n\tpublic void testRoleNamesStartWithDefaultRolePrefix() {\n\t\tthis.mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper(\"ou=people\", \"uid\"));\n\t\tthis.mgr.setGroupSearchBase(\"ou=groups\");\n\t\tLdapUserDetails bob = (LdapUserDetails) this.mgr.loadUserByUsername(\"bob\");\n\n\t\tassertThat(bob.getAuthorities()).isNotEmpty();\n\n\t\tbob.getAuthorities()\n\t\t\t.stream()\n\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t.forEach((authority) -> assertThat(authority).startsWith(DEFAULT_ROLE_PREFIX));\n\t}\n\n\t@Test\n\tpublic void testRoleNamesStartWithCustomRolePrefix() {\n\t\tString customPrefix = \"GROUP_\";\n\t\tthis.mgr.setRolePrefix(customPrefix);\n\n\t\tthis.mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper(\"ou=people\", \"uid\"));\n\t\tthis.mgr.setGroupSearchBase(\"ou=groups\");\n\t\tLdapUserDetails bob = (LdapUserDetails) this.mgr.loadUserByUsername(\"bob\");\n\n\t\tassertThat(bob.getAuthorities()).isNotEmpty();\n\n\t\tbob.getAuthorities()\n\t\t\t.stream()\n\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t.forEach((authority) -> assertThat(authority).startsWith(customPrefix));\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulatorTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashSet;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.ldap.UnboundIdContainerConfig;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Filip Hanik\n * @author Eddú Meléndez\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = UnboundIdContainerConfig.class)\npublic class NestedLdapAuthoritiesPopulatorTests {\n\n\t@Autowired\n\tprivate ContextSource contextSource;\n\n\tprivate NestedLdapAuthoritiesPopulator populator;\n\n\tprivate LdapAuthority javaDevelopers;\n\n\tprivate LdapAuthority groovyDevelopers;\n\n\tprivate LdapAuthority scalaDevelopers;\n\n\tprivate LdapAuthority closureDevelopers;\n\n\tprivate LdapAuthority jDevelopers;\n\n\tprivate LdapAuthority circularJavaDevelopers;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.populator = new NestedLdapAuthoritiesPopulator(this.contextSource, \"ou=jdeveloper\");\n\t\tthis.populator.setGroupSearchFilter(\"(member={0})\");\n\t\tthis.populator.setIgnorePartialResultException(false);\n\t\tthis.populator.setRolePrefix(\"\");\n\t\tthis.populator.setSearchSubtree(true);\n\t\tthis.populator.setConvertToUpperCase(false);\n\t\tthis.jDevelopers = new LdapAuthority(\"j-developers\", \"cn=j-developers,ou=jdeveloper,dc=springframework,dc=org\");\n\t\tthis.javaDevelopers = new LdapAuthority(\"java-developers\",\n\t\t\t\t\"cn=java-developers,ou=jdeveloper,dc=springframework,dc=org\");\n\t\tthis.groovyDevelopers = new LdapAuthority(\"groovy-developers\",\n\t\t\t\t\"cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org\");\n\t\tthis.scalaDevelopers = new LdapAuthority(\"scala-developers\",\n\t\t\t\t\"cn=scala-developers,ou=jdeveloper,dc=springframework,dc=org\");\n\t\tthis.closureDevelopers = new LdapAuthority(\"closure-developers\",\n\t\t\t\t\"cn=closure-developers,ou=jdeveloper,dc=springframework,dc=org\");\n\t\tthis.circularJavaDevelopers = new LdapAuthority(\"circular-java-developers\",\n\t\t\t\t\"cn=circular-java-developers,ou=jdeveloper,dc=springframework,dc=org\");\n\t}\n\n\t@Test\n\tpublic void testScalaDudeJDevelopersAuthorities() {\n\t\tDirContextAdapter ctx = new DirContextAdapter(\"uid=scaladude,ou=people,dc=springframework,dc=org\");\n\t\tCollection<GrantedAuthority> authorities = this.populator.getGrantedAuthorities(ctx, \"scaladude\");\n\t\tassertThat(authorities).hasSize(5);\n\t\tassertThat(authorities).isEqualTo(Arrays.asList(this.javaDevelopers, this.circularJavaDevelopers,\n\t\t\t\tthis.scalaDevelopers, this.groovyDevelopers, this.jDevelopers));\n\t}\n\n\t@Test\n\tpublic void testJavaDudeJDevelopersAuthorities() {\n\t\tDirContextAdapter ctx = new DirContextAdapter(\"uid=javadude,ou=people,dc=springframework,dc=org\");\n\t\tCollection<GrantedAuthority> authorities = this.populator.getGrantedAuthorities(ctx, \"javadude\");\n\t\tassertThat(authorities).hasSize(4);\n\t\tassertThat(authorities).contains(this.javaDevelopers);\n\t}\n\n\t@Test\n\tpublic void testScalaDudeJDevelopersAuthoritiesWithSearchLimit() {\n\t\tthis.populator.setMaxSearchDepth(1);\n\t\tDirContextAdapter ctx = new DirContextAdapter(\"uid=scaladude,ou=people,dc=springframework,dc=org\");\n\t\tCollection<GrantedAuthority> authorities = this.populator.getGrantedAuthorities(ctx, \"scaladude\");\n\t\tassertThat(authorities).hasSize(1);\n\t\tassertThat(authorities).isEqualTo(Arrays.asList(this.scalaDevelopers));\n\t}\n\n\t@Test\n\tpublic void testGroovyDudeJDevelopersAuthorities() {\n\t\tDirContextAdapter ctx = new DirContextAdapter(\"uid=groovydude,ou=people,dc=springframework,dc=org\");\n\t\tCollection<GrantedAuthority> authorities = this.populator.getGrantedAuthorities(ctx, \"groovydude\");\n\t\tassertThat(authorities).hasSize(4);\n\t\tassertThat(authorities).isEqualTo(Arrays.asList(this.javaDevelopers, this.circularJavaDevelopers,\n\t\t\t\tthis.groovyDevelopers, this.jDevelopers));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void testClosureDudeJDevelopersWithMembershipAsAttributeValues() {\n\t\tthis.populator.setAttributeNames(new HashSet(Arrays.asList(\"member\")));\n\n\t\tDirContextAdapter ctx = new DirContextAdapter(\"uid=closuredude,ou=people,dc=springframework,dc=org\");\n\t\tCollection<GrantedAuthority> authorities = this.populator.getGrantedAuthorities(ctx, \"closuredude\");\n\t\tassertThat(authorities).hasSize(5);\n\t\tassertThat(authorities).isEqualTo(Arrays.asList(this.javaDevelopers, this.circularJavaDevelopers,\n\t\t\t\tthis.closureDevelopers, this.groovyDevelopers, this.jDevelopers));\n\n\t\tLdapAuthority[] ldapAuthorities = authorities.toArray(new LdapAuthority[0]);\n\t\tassertThat(ldapAuthorities).hasSize(5);\n\t\t// groovy-developers group\n\t\tassertThat(ldapAuthorities[0].getAttributes()).containsKey(\"member\");\n\t\tassertThat(ldapAuthorities[0].getAttributes().get(\"member\")).isNotNull();\n\t\tassertThat(ldapAuthorities[0].getAttributes().get(\"member\")).hasSize(3);\n\t\tassertThat(ldapAuthorities[0].getFirstAttributeValue(\"member\"))\n\t\t\t.isEqualTo(\"cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org\");\n\n\t\t// java group\n\t\tassertThat(ldapAuthorities[1].getAttributes()).containsKey(\"member\");\n\t\tassertThat(ldapAuthorities[1].getAttributes().get(\"member\")).isNotNull();\n\t\tassertThat(ldapAuthorities[1].getAttributes().get(\"member\")).hasSize(3);\n\t\tassertThat(this.groovyDevelopers.getDn()).isEqualTo(ldapAuthorities[1].getFirstAttributeValue(\"member\"));\n\t\tassertThat(ldapAuthorities[2].getAttributes().get(\"member\"))\n\t\t\t.contains(\"uid=closuredude,ou=people,dc=springframework,dc=org\");\n\n\t\t// test non existent attribute\n\t\tassertThat(ldapAuthorities[2].getFirstAttributeValue(\"test\")).isNull();\n\t\tassertThat(ldapAuthorities[2].getAttributeValues(\"test\")).isNotNull();\n\t\tassertThat(ldapAuthorities[2].getAttributeValues(\"test\")).isEmpty();\n\t\t// test role name\n\t\tassertThat(ldapAuthorities[3].getAuthority()).isEqualTo(this.groovyDevelopers.getAuthority());\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/integration-test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "ldap/src/integration-test/resources/test-server-custom-attribute-types.ldif",
    "content": "version: 1\n\ndn: cn=ssattributes, ou=schema\nobjectclass: metaSchema\nobjectclass: top\ncn: ssattributes\n\ndn: ou=attributetypes, cn=ssattributes, ou=schema\nobjectclass: organizationalUnit\nobjectclass: top\nou: attributetypes\n\ndn: m-oid=1.3.6.1.4.1.18060.0.4.3.9.1, ou=attributetypes, cn=ssattributes, ou=schema\nobjectclass: metaAttributeType\nobjectclass: metaTop\nobjectclass: top\nm-oid: 1.3.6.1.4.1.18060.0.4.3.9.1\nm-name: customAttribute\nm-syntax: 1.3.6.1.4.1.1466.115.121.1.15\n\ndn: ou=objectclasses, cn=ssattributes, ou=schema\nobjectclass: organizationalUnit\nobjectclass: top\nou: objectclasses\n\ndn: m-oid=1.3.6.1.4.1.18060.0.4.3.10.1, ou=objectclasses, cn=ssattributes, ou=schema\nobjectclass: metaObjectClass\nobjectclass: metaTop\nobjectclass: top\nm-oid: 1.3.6.1.4.1.18060.0.4.3.10.1\nm-name: customObject\nm-must: uid\nm-may: customAttribute\n\ndn: ou=ssattributes,dc=springframework,dc=org\nobjectclass: top\nobjectclass: extensibleObject\nobjectclass: organizationalUnit\nou: ssattributes\n\ndn: uid=objectWithCustomAttribute1, ou=ssattributes, dc=springframework, dc=org\nobjectClass: top\nobjectClass: extensibleObject\nobjectClass: customObject\nuid: objectWithCustomAttribute1\ncustomAttribute: I am custom\n"
  },
  {
    "path": "ldap/src/integration-test/resources/test-server-malformed.txt",
    "content": "dn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn ou=subgroups,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: subgroups\n"
  },
  {
    "path": "ldap/src/integration-test/resources/test-server-with-spaces.ldif",
    "content": "dn: ou=space cadets,dc=spring framework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: space cadets\n\ndn: uid=space cadet,ou=space cadets,dc=spring framework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Space Cadet\nsn: Cadet\nuid: space cadet\nuserPassword: spacecadetspassword\n"
  },
  {
    "path": "ldap/src/integration-test/resources/test-server-with-undefined-group-role-attributes.ldif",
    "content": "dn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: uid=dayan,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Dayan K\nsn: Dayan\nuid: dayan\nuserPassword: dayanspassword\n\n\n\ndn: cn=managers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: managers\nou:\nmember: uid=dayan,ou=people,dc=springframework,dc=org\n\ndn: cn=researchers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: researchers\nmember: uid=dayan,ou=people,dc=springframework,dc=org\n\ndn: cn=developers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: developers\nou: developer\nmember: uid=dayan,ou=people,dc=springframework,dc=org\n"
  },
  {
    "path": "ldap/src/integration-test/resources/test-server.ldif",
    "content": "dn: ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: groups\n\ndn: ou=subgroups,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: subgroups\n\ndn: ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: people\n\ndn: ou=space cadets,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: space cadets\n\ndn: ou=\\\"quoted people\\\",dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: \"quoted people\"\n\ndn: ou=otherpeople,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: otherpeople\n\ndn: uid=ben,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Ben Alex\nsn: Alex\nuid: ben\nuserPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=\n\ndn: uid=bob,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Bob Hamilton\nsn: Hamilton\nuid: bob\nuserPassword: bobspassword\n\ndn: uid=joe,ou=otherpeople,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Joe Smeth\nsn: Smeth\nuid: joe\nuserPassword: joespassword\n\ndn: cn=mouse\\, jerry,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Mouse, Jerry\nsn: Mouse\nuid: jerry\nuserPassword: jerryspassword\n\ndn: cn=slash/guy,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: slash/guy\nsn: Slash\nuid: slashguy\nuserPassword: slashguyspassword\n\ndn: cn=quote\\\"guy,ou=\\\"quoted people\\\",dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: quote\\\"guy\nsn: Quote\nuid: quoteguy\nuserPassword: quoteguyspassword\n\ndn: uid=space cadet,ou=space cadets,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Space Cadet\nsn: Cadet\nuid: space cadet\nuserPassword: spacecadetspassword\n\n\n\ndn: cn=developers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: developers\nou: developer\nmember: uid=ben,ou=people,dc=springframework,dc=org\nmember: uid=bob,ou=people,dc=springframework,dc=org\n\ndn: cn=managers,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: managers\nou: manager\nmember: uid=ben,ou=people,dc=springframework,dc=org\nmember: cn=mouse\\, jerry,ou=people,dc=springframework,dc=org\n\ndn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: submanagers\nou: submanager\nmember: uid=ben,ou=people,dc=springframework,dc=org\n\n#Nested groups data\n###################\n\ndn: ou=jdeveloper,dc=springframework,dc=org\nobjectclass: top\nobjectclass: organizationalUnit\nou: jdeveloper\n\n\n# javadude is part of (in a nested search)\n# circular-java-developers, java-developers, j-developers\ndn: uid=javadude,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Java Dude\nsn: Dude\nuid: javadude\nuserPassword: javadudespassword\n\n# groovydude is part of (in a nested search)\n# groovy-developers, java-developers, circular-java-developers, j-developers \ndn: uid=groovydude,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Groovy Dude\nsn: Dude\nuid: groovydude\nuserPassword: groovydudespassword\n\n# closuredude is part of (in a nested search)\n# closure-developers, groovy-developers, java-developers, circular-java-developers, j-developers \ndn: uid=closuredude,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Closure Dude\nsn: Dude\nuid: closuredude\nuserPassword: closuredudespassword\n\n# scaladude is part of (in a nested search)\n# scala-developers, groovy-developers, java-developers, circular-java-developers, j-developers \ndn: uid=scaladude,ou=people,dc=springframework,dc=org\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerson\nobjectclass: inetOrgPerson\ncn: Scala Dude\nsn: Dude\nuid: scaladude\nuserPassword: scaladudespassword\n\ndn: cn=j-developers,ou=jdeveloper,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: j-developers\nou: jdeveloper\nmember: cn=java-developers,ou=jdeveloper,dc=springframework,dc=org\n\ndn: cn=java-developers,ou=jdeveloper,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: java-developers\nou: jdeveloper\nmember: cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org\nmember: cn=scala-developers,ou=jdeveloper,dc=springframework,dc=org\nmember: uid=javadude,ou=people,dc=springframework,dc=org\n\ndn: cn=circular-java-developers,ou=jdeveloper,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: circular-java-developers\nou: jdeveloper\nmember: cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org\nmember: cn=scala-developers,ou=jdeveloper,dc=springframework,dc=org\nmember: uid=javadude,ou=people,dc=springframework,dc=org\n\n\ndn: cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: groovy-developers\nou: jdeveloper\nmember: cn=closure-developers,ou=jdeveloper,dc=springframework,dc=org\nmember: uid=groovydude,ou=people,dc=springframework,dc=org\nmember: cn=circular-java-developers,ou=jdeveloper,dc=springframework,dc=org\n\ndn: cn=closure-developers,ou=jdeveloper,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: closure-developers\nou: jdeveloper\nmember: uid=closuredude,ou=people,dc=springframework,dc=org\n\ndn: cn=scala-developers,ou=jdeveloper,dc=springframework,dc=org\nobjectclass: top\nobjectclass: groupOfNames\ncn: scala-developers\nou: jdeveloper\nmember: uid=scaladude,ou=people,dc=springframework,dc=org\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/DefaultLdapUsernameToDnMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport javax.naming.ldap.LdapName;\n\nimport org.springframework.ldap.support.LdapNameBuilder;\n\n/**\n * This implementation appends a name component to the <tt>userDnBase</tt> context using\n * the <tt>usernameAttributeName</tt> property. So if the <tt>uid</tt> attribute is used\n * to store the username, and the base DN is <tt>cn=users</tt> and we are creating a new\n * user called \"sam\", then the DN will be <tt>uid=sam,cn=users</tt>.\n *\n * @author Luke Taylor\n */\npublic class DefaultLdapUsernameToDnMapper implements LdapUsernameToDnMapper {\n\n\tprivate final String userDnBase;\n\n\tprivate final String usernameAttribute;\n\n\t/**\n\t * @param userDnBase the base name of the DN\n\t * @param usernameAttribute the attribute to append for the username component.\n\t */\n\tpublic DefaultLdapUsernameToDnMapper(String userDnBase, String usernameAttribute) {\n\t\tthis.userDnBase = userDnBase;\n\t\tthis.usernameAttribute = usernameAttribute;\n\t}\n\n\t@Override\n\tpublic LdapName buildLdapName(String username) {\n\t\treturn LdapNameBuilder.newInstance(this.userDnBase).add(this.usernameAttribute, username).build();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Hashtable;\nimport java.util.List;\nimport java.util.StringTokenizer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.ldap.core.support.DirContextAuthenticationStrategy;\nimport org.springframework.ldap.core.support.LdapContextSource;\nimport org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * ContextSource implementation which uses Spring LDAP's <tt>LdapContextSource</tt> as a\n * base class. Used internally by the Spring Security LDAP namespace configuration.\n * <p>\n * From Spring Security 3.0, Spring LDAP 1.3 is used and the <tt>ContextSource</tt>\n * interface provides support for binding with a username and password. As a result,\n * Spring LDAP <tt>ContextSource</tt> implementations such as <tt>LdapContextSource</tt>\n * may be used directly with Spring Security.\n * <p>\n * Spring LDAP 1.3 doesn't have JVM-level LDAP connection pooling enabled by default. This\n * class sets the <tt>pooled</tt> property to true, but customizes the\n * {@link DirContextAuthenticationStrategy} used to disable pooling when the <tt>DN</tt>\n * doesn't match the <tt>userDn</tt> property. This prevents pooling for calls to\n * {@link #getContext(String, String)} to authenticate as specific users.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class DefaultSpringSecurityContextSource extends LdapContextSource {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\t/**\n\t * Create and initialize an instance which will connect to the supplied LDAP URL. If\n\t * you want to use more than one server for fail-over, rather use the\n\t * {@link #DefaultSpringSecurityContextSource(List, String)} constructor.\n\t * @param providerUrl an LDAP URL of the form\n\t * <code>ldap://localhost:389/base_dn</code>\n\t */\n\tpublic DefaultSpringSecurityContextSource(String providerUrl) {\n\t\tAssert.hasLength(providerUrl, \"An LDAP connection URL must be supplied.\");\n\t\tStringTokenizer tokenizer = new StringTokenizer(providerUrl);\n\t\tArrayList<String> urls = new ArrayList<>();\n\t\t// Work out rootDn from the first URL and check that the other URLs (if any) match\n\t\tString rootDn = null;\n\t\twhile (tokenizer.hasMoreTokens()) {\n\t\t\tString url = tokenizer.nextToken();\n\t\t\tString urlRootDn = LdapUtils.parseRootDnFromUrl(url);\n\t\t\turls.add(url.substring(0, url.lastIndexOf(urlRootDn)));\n\t\t\tthis.logger.info(LogMessage.format(\"Configure with URL %s and root DN %s\", url, urlRootDn));\n\t\t\tAssert.isTrue(rootDn == null || rootDn.equals(urlRootDn),\n\t\t\t\t\t\"Root DNs must be the same when using multiple URLs\");\n\t\t\trootDn = (rootDn != null) ? rootDn : urlRootDn;\n\t\t}\n\t\tsetUrls(urls.toArray(new String[0]));\n\t\tsetBase((rootDn != null) ? decodeUrl(rootDn) : null);\n\t\tsetPooled(true);\n\t\tsetAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy() {\n\n\t\t\t@Override\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tpublic void setupEnvironment(Hashtable env, String dn, String password) {\n\t\t\t\tsuper.setupEnvironment(env, dn, password);\n\t\t\t\t// Remove the pooling flag unless authenticating as the 'manager' user.\n\t\t\t\tif (!DefaultSpringSecurityContextSource.this.getUserDn().equals(dn)\n\t\t\t\t\t\t&& env.containsKey(SUN_LDAP_POOLING_FLAG)) {\n\t\t\t\t\tDefaultSpringSecurityContextSource.this.logger.trace(\"Removing pooling flag for user \" + dn);\n\t\t\t\t\tenv.remove(SUN_LDAP_POOLING_FLAG);\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\t}\n\n\t/**\n\t * Create and initialize an instance which will connect of the LDAP Spring Security\n\t * Context Source. It will connect to any of the provided LDAP server URLs.\n\t * @param urls A list of string values which are LDAP server URLs. An example would be\n\t * <code>ldap://ldap.company.com:389</code>. LDAPS URLs (SSL-secured) may be used as\n\t * well, given that Spring Security is able to connect to the server. Note that these\n\t * <b>URLs must not include the base DN</b>!\n\t * @param baseDn The common Base DN for all provided servers, e.g.\n\t *\n\t * <pre>\n\t * dc=company,dc=com\n\t * </pre>\n\t *\n\t * .\n\t */\n\tpublic DefaultSpringSecurityContextSource(List<String> urls, String baseDn) {\n\t\tthis(buildProviderUrl(urls, baseDn));\n\t}\n\n\t/**\n\t * Builds a Spring LDAP-compliant Provider URL string, i.e. a space-separated list of\n\t * LDAP servers with their base DNs. As the base DN must be identical for all servers,\n\t * it needs to be supplied only once.\n\t * @param urls A list of string values which are LDAP server URLs. An example would be\n\t *\n\t * <pre>\n\t * ldap://ldap.company.com:389\n\t * </pre>\n\t *\n\t * . LDAPS URLs may be used as well, given that Spring Security is able to connect to\n\t * the server.\n\t * @param baseDn The common Base DN for all provided servers, e.g.\n\t *\n\t * <pre>\n\t * dc=company,dc=com\n\t * </pre>\n\t *\n\t * .\n\t * @return A Spring Security/Spring LDAP-compliant Provider URL string.\n\t */\n\tprivate static String buildProviderUrl(List<String> urls, String baseDn) {\n\t\tAssert.notNull(baseDn, \"The Base DN for the LDAP server must not be null.\");\n\t\tAssert.notEmpty(urls, \"At least one LDAP server URL must be provided.\");\n\t\tString encodedBaseDn = encodeUrl(baseDn.trim());\n\t\tStringBuilder providerUrl = new StringBuilder();\n\t\tfor (String serverUrl : urls) {\n\t\t\tString trimmedUrl = serverUrl.trim();\n\t\t\tif (trimmedUrl.isEmpty()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tproviderUrl.append(trimmedUrl);\n\t\t\tif (!trimmedUrl.endsWith(\"/\")) {\n\t\t\t\tproviderUrl.append(\"/\");\n\t\t\t}\n\t\t\tproviderUrl.append(encodedBaseDn);\n\t\t\tproviderUrl.append(\" \");\n\t\t}\n\t\treturn providerUrl.toString();\n\n\t}\n\n\tprivate static String encodeUrl(String url) {\n\t\treturn URLEncoder.encode(url, StandardCharsets.UTF_8);\n\t}\n\n\tprivate String decodeUrl(String url) {\n\t\treturn URLDecoder.decode(url, StandardCharsets.UTF_8);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/LdapEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Helper class to encode and decode ldap names and values.\n *\n * <p>\n * NOTE: This is a copy from Spring LDAP so that both Spring LDAP 1.x and 2.x can be\n * supported without reflection.\n * </p>\n *\n * @author Adam Skogman\n * @author Mattias Hellborg Arthursson\n */\nfinal class LdapEncoder {\n\n\tprivate static final @Nullable String[] FILTER_ESCAPE_TABLE = new String['\\\\' + 1];\n\n\tstatic {\n\t\t// fill with char itself\n\t\tfor (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {\n\t\t\tFILTER_ESCAPE_TABLE[c] = String.valueOf(c);\n\t\t}\n\t\t// escapes (RFC2254)\n\t\tFILTER_ESCAPE_TABLE['*'] = \"\\\\2a\";\n\t\tFILTER_ESCAPE_TABLE['('] = \"\\\\28\";\n\t\tFILTER_ESCAPE_TABLE[')'] = \"\\\\29\";\n\t\tFILTER_ESCAPE_TABLE['\\\\'] = \"\\\\5c\";\n\t\tFILTER_ESCAPE_TABLE[0] = \"\\\\00\";\n\t}\n\n\t/**\n\t * All static methods - not to be instantiated.\n\t */\n\tprivate LdapEncoder() {\n\t}\n\n\t/**\n\t * Escape a value for use in a filter.\n\t * @param value the value to escape.\n\t * @return a properly escaped representation of the supplied value.\n\t */\n\tstatic String filterEncode(String value) {\n\t\tStringBuilder encodedValue = new StringBuilder(value.length() * 2);\n\t\tint length = value.length();\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tchar ch = value.charAt(i);\n\t\t\tencodedValue.append((ch < FILTER_ESCAPE_TABLE.length) ? FILTER_ESCAPE_TABLE[ch] : ch);\n\t\t}\n\t\treturn encodedValue.toString();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/LdapUsernameToDnMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport javax.naming.ldap.LdapName;\n\n/**\n * Constructs an Ldap Distinguished Name from a username.\n *\n * @author Luke Taylor\n */\npublic interface LdapUsernameToDnMapper {\n\n\tLdapName buildLdapName(String username);\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/LdapUtils.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\nimport javax.naming.Context;\nimport javax.naming.NamingEnumeration;\nimport javax.naming.NamingException;\nimport javax.naming.ldap.LdapName;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.util.Assert;\n\n/**\n * LDAP Utility methods.\n *\n * @author Luke Taylor\n */\npublic final class LdapUtils {\n\n\tprivate static final Log logger = LogFactory.getLog(LdapUtils.class);\n\n\tprivate LdapUtils() {\n\t}\n\n\tpublic static void closeContext(@Nullable Context ctx) {\n\t\tif (ctx instanceof DirContextAdapter) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (ctx != null) {\n\t\t\t\tctx.close();\n\t\t\t}\n\t\t}\n\t\tcatch (NamingException ex) {\n\t\t\tlogger.debug(\"Failed to close context.\", ex);\n\t\t}\n\t}\n\n\tpublic static void closeEnumeration(@Nullable NamingEnumeration ne) {\n\t\ttry {\n\t\t\tif (ne != null) {\n\t\t\t\tne.close();\n\t\t\t}\n\t\t}\n\t\tcatch (NamingException ex) {\n\t\t\tlogger.debug(\"Failed to close enumeration.\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Obtains the part of a DN relative to a supplied base context.\n\t * <p>\n\t * If the DN is \"cn=bob,ou=people,dc=springframework,dc=org\" and the base context name\n\t * is \"ou=people,dc=springframework,dc=org\" it would return \"cn=bob\".\n\t * </p>\n\t * @param fullDn the DN\n\t * @param baseCtx the context to work out the name relative to.\n\t * @return the\n\t * @throws NamingException any exceptions thrown by the context are propagated.\n\t */\n\tpublic static String getRelativeName(String fullDn, Context baseCtx) throws NamingException {\n\t\tString baseDn = baseCtx.getNameInNamespace();\n\t\tif (baseDn.isEmpty()) {\n\t\t\treturn fullDn;\n\t\t}\n\t\tLdapName base = LdapNameBuilder.newInstance(baseDn).build();\n\t\tLdapName full = LdapNameBuilder.newInstance(fullDn).build();\n\t\tif (base.equals(full)) {\n\t\t\treturn \"\";\n\t\t}\n\t\tAssert.isTrue(full.startsWith(base), \"Full DN does not start with base DN\");\n\t\tfor (int i = 0; i < base.size(); i++) {\n\t\t\tfull.remove(0);\n\t\t}\n\t\treturn full.toString();\n\t}\n\n\t/**\n\t * Gets the full dn of a name by prepending the name of the context it is relative to.\n\t * If the name already contains the base name, it is returned unaltered.\n\t */\n\tpublic static LdapName getFullDn(LdapName dn, Context baseCtx) throws NamingException {\n\t\tLdapName baseDn = LdapNameBuilder.newInstance(baseCtx.getNameInNamespace()).build();\n\t\tif (dn.startsWith(baseDn)) {\n\t\t\treturn dn;\n\t\t}\n\t\tbaseDn.addAll(dn);\n\t\treturn baseDn;\n\t}\n\n\tpublic static String convertPasswordToString(Object passObj) {\n\t\tAssert.notNull(passObj, \"Password object to convert must not be null\");\n\t\tif (passObj instanceof byte[]) {\n\t\t\treturn Utf8.decode((byte[]) passObj);\n\t\t}\n\t\tif (passObj instanceof String) {\n\t\t\treturn (String) passObj;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"Password object was not a String or byte array.\");\n\t}\n\n\t/**\n\t * Works out the root DN for an LDAP URL.\n\t * <p>\n\t * For example, the URL <tt>ldap://monkeymachine:11389/dc=springframework,dc=org</tt>\n\t * has the root DN \"dc=springframework,dc=org\".\n\t * </p>\n\t * @param url the LDAP URL\n\t * @return the root DN\n\t */\n\tpublic static String parseRootDnFromUrl(String url) {\n\t\tAssert.hasLength(url, \"url must have length\");\n\t\tString urlRootDn;\n\t\tif (url.startsWith(\"ldap:\") || url.startsWith(\"ldaps:\")) {\n\t\t\tURI uri = parseLdapUrl(url);\n\t\t\turlRootDn = uri.getRawPath();\n\t\t}\n\t\telse {\n\t\t\t// Assume it's an embedded server\n\t\t\turlRootDn = url;\n\t\t}\n\t\tif (urlRootDn.startsWith(\"/\")) {\n\t\t\turlRootDn = urlRootDn.substring(1);\n\t\t}\n\t\treturn urlRootDn;\n\t}\n\n\t/**\n\t * Parses the supplied LDAP URL.\n\t * @param url the URL (e.g.\n\t * <tt>ldap://monkeymachine:11389/dc=springframework,dc=org</tt>).\n\t * @return the URI object created from the URL\n\t * @throws IllegalArgumentException if the URL is null, empty or the URI syntax is\n\t * invalid.\n\t */\n\n\tprivate static URI parseLdapUrl(String url) {\n\t\tAssert.hasLength(url, \"url must have length\");\n\t\ttry {\n\t\t\treturn new URI(url);\n\t\t}\n\t\tcatch (URISyntaxException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Unable to parse url: \" + url, ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.naming.NamingEnumeration;\nimport javax.naming.NamingException;\nimport javax.naming.PartialResultException;\nimport javax.naming.directory.Attribute;\nimport javax.naming.directory.Attributes;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.SearchControls;\nimport javax.naming.directory.SearchResult;\nimport javax.naming.ldap.LdapName;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.ldap.core.ContextMapper;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.LdapTemplate;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * Extension of Spring LDAP's LdapTemplate class which adds extra functionality required\n * by Spring Security.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @author Filip Hanik\n * @since 2.0\n */\npublic class SpringSecurityLdapTemplate extends LdapTemplate {\n\n\tprivate static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class);\n\n\tpublic static final String[] NO_ATTRS = new String[0];\n\n\t/**\n\t * Every search results where a record is defined by a Map&lt;String,String[]&gt;\n\t * contains at least this key - the DN of the record itself.\n\t */\n\tpublic static final String DN_KEY = \"spring.security.ldap.dn\";\n\n\tprivate static final boolean RETURN_OBJECT = true;\n\n\t/** Default search controls */\n\tprivate SearchControls searchControls = new SearchControls();\n\n\tpublic SpringSecurityLdapTemplate(ContextSource contextSource) {\n\t\tAssert.notNull(contextSource, \"ContextSource cannot be null\");\n\t\tsetContextSource(contextSource);\n\t\tthis.searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);\n\t}\n\n\t/**\n\t * Performs an LDAP compare operation of the value of an attribute for a particular\n\t * directory entry.\n\t * @param dn the entry who's attribute is to be used\n\t * @param attributeName the attribute who's value we want to compare\n\t * @param value the value to be checked against the directory value\n\t * @return true if the supplied value matches that in the directory\n\t */\n\tpublic boolean compare(String dn, String attributeName, Object value) {\n\t\tString comparisonFilter = \"(\" + attributeName + \"={0})\";\n\t\treturn executeReadOnly((ctx) -> {\n\t\t\tSearchControls searchControls = new SearchControls();\n\t\t\tsearchControls.setReturningAttributes(NO_ATTRS);\n\t\t\tsearchControls.setSearchScope(SearchControls.OBJECT_SCOPE);\n\t\t\tObject[] params = new Object[] { value };\n\t\t\tNamingEnumeration<SearchResult> results = ctx.search(dn, comparisonFilter, params, searchControls);\n\t\t\tboolean match = results.hasMore();\n\t\t\tLdapUtils.closeEnumeration(results);\n\t\t\treturn match;\n\t\t});\n\t}\n\n\t/**\n\t * Composes an object from the attributes of the given DN.\n\t * @param dn the directory entry which will be read\n\t * @param attributesToRetrieve the named attributes which will be retrieved from the\n\t * directory entry.\n\t * @return the object created by the mapper\n\t */\n\tpublic DirContextOperations retrieveEntry(final String dn, final String @Nullable [] attributesToRetrieve) {\n\t\treturn executeReadOnly((ctx) -> {\n\t\t\tAttributes attrs = ctx.getAttributes(dn, attributesToRetrieve);\n\t\t\treturn new DirContextAdapter(attrs, LdapNameBuilder.newInstance(dn).build(),\n\t\t\t\t\tLdapNameBuilder.newInstance(ctx.getNameInNamespace()).build());\n\t\t});\n\t}\n\n\t/**\n\t * Performs a search using the supplied filter and returns the union of the values of\n\t * the named attribute found in all entries matched by the search. Note that one\n\t * directory entry may have several values for the attribute. Intended for role\n\t * searches and similar scenarios.\n\t * @param base the DN to search in\n\t * @param filter search filter to use\n\t * @param params the parameters to substitute in the search filter\n\t * @param attributeName the attribute who's values are to be retrieved.\n\t * @return the set of String values for the attribute as a union of the values found\n\t * in all the matching entries.\n\t */\n\tpublic Set<String> searchForSingleAttributeValues(final String base, final String filter, final Object[] params,\n\t\t\tfinal String attributeName) {\n\t\tString[] attributeNames = new String[] { attributeName };\n\t\tSet<Map<String, List<String>>> multipleAttributeValues = searchForMultipleAttributeValues(base, filter, params,\n\t\t\t\tattributeNames);\n\t\tSet<String> result = new HashSet<>();\n\t\tfor (Map<String, List<String>> map : multipleAttributeValues) {\n\t\t\tList<String> values = map.get(attributeName);\n\t\t\tif (values != null) {\n\t\t\t\tresult.addAll(values);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Performs a search using the supplied filter and returns the values of each named\n\t * attribute found in all entries matched by the search. Note that one directory entry\n\t * may have several values for the attribute. Intended for role searches and similar\n\t * scenarios.\n\t * @param base the DN to search in\n\t * @param filter search filter to use\n\t * @param params the parameters to substitute in the search filter\n\t * @param attributeNames the attributes' values that are to be retrieved; a\n\t * {@code null} array means retrieve all attributes\n\t * @return the set of String values for each attribute found in all the matching\n\t * entries. The attribute name is the key for each set of values. In addition each map\n\t * contains the DN as a String with the key predefined key {@link #DN_KEY}.\n\t */\n\tpublic Set<Map<String, List<String>>> searchForMultipleAttributeValues(String base, String filter, Object[] params,\n\t\t\tString @Nullable [] attributeNames) {\n\t\t// Escape the params acording to RFC2254\n\t\tObject[] encodedParams = new String[params.length];\n\t\tfor (int i = 0; i < params.length; i++) {\n\t\t\tencodedParams[i] = LdapEncoder.filterEncode(params[i].toString());\n\t\t}\n\t\tString formattedFilter = MessageFormat.format(filter, encodedParams);\n\t\tlogger.trace(LogMessage.format(\"Using filter: %s\", formattedFilter));\n\t\tHashSet<Map<String, List<String>>> result = new HashSet<>();\n\t\tContextMapper<?> roleMapper = (ctx) -> {\n\t\t\tDirContextAdapter adapter = (DirContextAdapter) ctx;\n\t\t\tMap<String, List<String>> record = new HashMap<>();\n\t\t\tif (ObjectUtils.isEmpty(attributeNames)) {\n\t\t\t\ttry {\n\t\t\t\t\tfor (NamingEnumeration<? extends Attribute> enumeration = adapter.getAttributes()\n\t\t\t\t\t\t.getAll(); enumeration.hasMore();) {\n\t\t\t\t\t\tAttribute attr = enumeration.next();\n\t\t\t\t\t\textractStringAttributeValues(adapter, record, attr.getID());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (NamingException ex) {\n\t\t\t\t\tthrow org.springframework.ldap.support.LdapUtils.convertLdapException(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfor (String attributeName : attributeNames) {\n\t\t\t\t\textractStringAttributeValues(adapter, record, attributeName);\n\t\t\t\t}\n\t\t\t}\n\t\t\trecord.put(DN_KEY, Collections.singletonList(getAdapterDN(adapter)));\n\t\t\tresult.add(record);\n\t\t\treturn void.class;\n\t\t};\n\t\tSearchControls ctls = new SearchControls();\n\t\tctls.setSearchScope(this.searchControls.getSearchScope());\n\t\tctls.setReturningAttributes((attributeNames != null && attributeNames.length > 0) ? attributeNames : null);\n\t\tsearch(base, formattedFilter, ctls, roleMapper);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Returns the DN for the context representing this LDAP record. By default this is\n\t * using {@link javax.naming.Context#getNameInNamespace()} instead of\n\t * {@link org.springframework.ldap.core.DirContextAdapter#getDn()} since the latter\n\t * returns a partial DN if a base has been specified.\n\t * @param adapter - the Context to extract the DN from\n\t * @return - the String representing the full DN\n\t */\n\tprivate String getAdapterDN(DirContextAdapter adapter) {\n\t\t// returns the full DN rather than the sub DN if a base is specified\n\t\treturn adapter.getNameInNamespace();\n\t}\n\n\t/**\n\t * Extracts String values for a specified attribute name and places them in the map\n\t * representing the ldap record If a value is not of type String, it will derive it's\n\t * value from the {@link Object#toString()}\n\t * @param adapter - the adapter that contains the values\n\t * @param record - the map holding the attribute names and values\n\t * @param attributeName - the name for which to fetch the values from\n\t */\n\tprivate void extractStringAttributeValues(DirContextAdapter adapter, Map<String, List<String>> record,\n\t\t\tString attributeName) {\n\t\tObject[] values = adapter.getObjectAttributes(attributeName);\n\t\tif (values == null || values.length == 0) {\n\t\t\tlogger.debug(LogMessage.format(\"Did not find attribute value for %s\", attributeName));\n\t\t\treturn;\n\t\t}\n\t\tList<String> stringValues = new ArrayList<>();\n\t\tfor (Object value : values) {\n\t\t\tif (value != null) {\n\t\t\t\tif (String.class.isAssignableFrom(value.getClass())) {\n\t\t\t\t\tstringValues.add((String) value);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tstringValues.add(value.toString());\n\t\t\t\t\tlogger.debug(LogMessage.format(\"Coerced attribute value for %s of type %s to a String\",\n\t\t\t\t\t\t\tattributeName, value.getClass()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\trecord.put(attributeName, stringValues);\n\t}\n\n\t/**\n\t * Performs a search, with the requirement that the search shall return a single\n\t * directory entry, and uses the supplied mapper to create the object from that entry.\n\t * <p>\n\t * Ignores <tt>PartialResultException</tt> if thrown, for compatibility with Active\n\t * Directory (see {@link LdapTemplate#setIgnorePartialResultException(boolean)}).\n\t * @param base the search base, relative to the base context supplied by the context\n\t * source.\n\t * @param filter the LDAP search filter\n\t * @param params parameters to be substituted in the search.\n\t * @return a DirContextOperations instance created from the matching entry.\n\t * @throws IncorrectResultSizeDataAccessException if no results are found or the\n\t * search returns more than one result.\n\t */\n\tpublic DirContextOperations searchForSingleEntry(String base, String filter, Object[] params) {\n\t\treturn executeReadOnly((ctx) -> searchForSingleEntryInternal(ctx, this.searchControls, base, filter, params));\n\t}\n\n\t/**\n\t * Internal method extracted to avoid code duplication in AD search.\n\t */\n\tpublic static DirContextOperations searchForSingleEntryInternal(DirContext ctx, SearchControls searchControls,\n\t\t\tString base, String filter, Object[] params) throws NamingException {\n\t\tfinal LdapName ctxBaseDn = LdapNameBuilder.newInstance(ctx.getNameInNamespace()).build();\n\t\tfinal LdapName searchBaseDn = LdapNameBuilder.newInstance(base).build();\n\t\tfinal NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn, filter, params,\n\t\t\t\tbuildControls(searchControls));\n\t\tlogger.trace(LogMessage.format(\"Searching for entry under DN '%s', base = '%s', filter = '%s'\", ctxBaseDn,\n\t\t\t\tsearchBaseDn, filter));\n\t\tSet<DirContextOperations> results = new HashSet<>();\n\t\ttry {\n\t\t\twhile (resultsEnum.hasMore()) {\n\t\t\t\tSearchResult searchResult = resultsEnum.next();\n\t\t\t\tDirContextAdapter dca = (DirContextAdapter) searchResult.getObject();\n\t\t\t\tAssert.notNull(dca, \"No object returned by search, DirContext is not correctly configured\");\n\t\t\t\tlogger.debug(LogMessage.format(\"Found DN: %s\", dca.getDn()));\n\t\t\t\tresults.add(dca);\n\t\t\t}\n\t\t}\n\t\tcatch (PartialResultException ex) {\n\t\t\tLdapUtils.closeEnumeration(resultsEnum);\n\t\t\tlogger.trace(\"Ignoring PartialResultException\");\n\t\t}\n\t\tif (results.size() != 1) {\n\t\t\tthrow new IncorrectResultSizeDataAccessException(1, results.size());\n\t\t}\n\t\treturn results.iterator().next();\n\t}\n\n\t/**\n\t * We need to make sure the search controls has the return object flag set to true, in\n\t * order for the search to return DirContextAdapter instances.\n\t * @param originalControls the {@link SearchControls} that might have the return\n\t * object flag set to true\n\t * @return a {@link SearchControls} that does have the return object flag set to true\n\t */\n\tprivate static SearchControls buildControls(SearchControls originalControls) {\n\t\treturn new SearchControls(originalControls.getSearchScope(), originalControls.getCountLimit(),\n\t\t\t\toriginalControls.getTimeLimit(), originalControls.getReturningAttributes(), RETURN_OBJECT,\n\t\t\t\toriginalControls.getDerefLinkFlag());\n\t}\n\n\t/**\n\t * Sets the search controls which will be used for search operations by the template.\n\t * @param searchControls the SearchControls instance which will be cached in the\n\t * template.\n\t */\n\tpublic void setSearchControls(SearchControls searchControls) {\n\t\tthis.searchControls = searchControls;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/aot/hint/LdapSecurityRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.aot.hint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\n\n/**\n * {@link RuntimeHintsRegistrar} for LDAP Security resources and classes\n *\n * @author Marcus Da Coregio\n * @since 6.0\n */\nclass LdapSecurityRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\thints.reflection()\n\t\t\t.registerType(TypeReference.of(\"com.sun.jndi.ldap.LdapCtxFactory\"),\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));\n\t\thints.resources().registerPattern(\"*.ldif\");\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Package for AOT Hints\n */\n@NullMarked\npackage org.springframework.security.ldap.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;\nimport org.springframework.security.ldap.userdetails.UserDetailsContextMapper;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Base class for the standard {@code LdapAuthenticationProvider} and the\n * {@code ActiveDirectoryLdapAuthenticationProvider}.\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic abstract class AbstractLdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.PASSWORD_AUTHORITY;\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate boolean useAuthenticationRequestCredentials = true;\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();\n\n\tprotected UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper();\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tAssert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,\n\t\t\t\t() -> this.messages.getMessage(\"LdapAuthenticationProvider.onlySupports\",\n\t\t\t\t\t\t\"Only UsernamePasswordAuthenticationToken is supported\"));\n\t\tUsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken) authentication;\n\t\tString username = userToken.getName();\n\t\tString password = (String) authentication.getCredentials();\n\t\tif (!StringUtils.hasLength(username)) {\n\t\t\tthrow new BadCredentialsException(\n\t\t\t\t\tthis.messages.getMessage(\"LdapAuthenticationProvider.emptyUsername\", \"Empty Username\"));\n\t\t}\n\t\tif (!StringUtils.hasLength(password)) {\n\t\t\tthrow new BadCredentialsException(\n\t\t\t\t\tthis.messages.getMessage(\"AbstractLdapAuthenticationProvider.emptyPassword\", \"Empty Password\"));\n\t\t}\n\t\tAssert.notNull(password, \"Null password was supplied in authentication token\");\n\t\tDirContextOperations userData = doAuthentication(userToken);\n\t\tUserDetails user = this.userDetailsContextMapper.mapUserFromContext(userData, authentication.getName(),\n\t\t\t\tloadUserAuthorities(userData, authentication.getName(), password));\n\t\treturn createSuccessfulAuthentication(userToken, user);\n\t}\n\n\tprotected abstract DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth);\n\n\tprotected abstract Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData,\n\t\t\tString username, String password);\n\n\t/**\n\t * Creates the final {@code Authentication} object which will be returned from the\n\t * {@code authenticate} method.\n\t * @param authentication the original authentication request token\n\t * @param user the <tt>UserDetails</tt> instance returned by the configured\n\t * <tt>UserDetailsContextMapper</tt>.\n\t * @return the Authentication object for the fully authenticated user.\n\t */\n\tprotected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenticationToken authentication,\n\t\t\tUserDetails user) {\n\t\tObject password = this.useAuthenticationRequestCredentials ? authentication.getCredentials()\n\t\t\t\t: user.getPassword();\n\t\tCollection<GrantedAuthority> authorities = new LinkedHashSet<>(\n\t\t\t\tthis.authoritiesMapper.mapAuthorities(user.getAuthorities()));\n\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\tUsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(user, password,\n\t\t\t\tauthorities);\n\t\tresult.setDetails(authentication.getDetails());\n\t\tthis.logger.debug(\"Authenticated user\");\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Determines whether the supplied password will be used as the credentials in the\n\t * successful authentication token. If set to false, then the password will be\n\t * obtained from the UserDetails object created by the configured\n\t * {@code UserDetailsContextMapper}. Often it will not be possible to read the\n\t * password from the directory, so defaults to true.\n\t * @param useAuthenticationRequestCredentials whether to use the credentials in the\n\t * authentication request\n\t */\n\tpublic void setUseAuthenticationRequestCredentials(boolean useAuthenticationRequestCredentials) {\n\t\tthis.useAuthenticationRequestCredentials = useAuthenticationRequestCredentials;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t/**\n\t * Sets the {@link GrantedAuthoritiesMapper} used for converting the authorities\n\t * loaded from storage to a new set of authorities which will be associated to the\n\t * {@link UsernamePasswordAuthenticationToken}. If not set, defaults to a\n\t * {@link NullAuthoritiesMapper}.\n\t * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the\n\t * user's authorities\n\t */\n\tpublic void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\t/**\n\t * Allows a custom strategy to be used for creating the <tt>UserDetails</tt> which\n\t * will be stored as the principal in the <tt>Authentication</tt> returned by the\n\t * {@link #createSuccessfulAuthentication(org.springframework.security.authentication.UsernamePasswordAuthenticationToken, org.springframework.security.core.userdetails.UserDetails)}\n\t * method.\n\t * @param userDetailsContextMapper the strategy instance. If not set, defaults to a\n\t * simple <tt>LdapUserDetailsMapper</tt>.\n\t */\n\tpublic void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {\n\t\tAssert.notNull(userDetailsContextMapper, \"UserDetailsContextMapper must not be null\");\n\t\tthis.userDetailsContextMapper = userDetailsContextMapper;\n\t}\n\n\t/**\n\t * Provides access to the injected {@code UserDetailsContextMapper} strategy for use\n\t * by subclasses.\n\t */\n\tprotected UserDetailsContextMapper getUserDetailsContextMapper() {\n\t\treturn this.userDetailsContextMapper;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.ldap.search.LdapUserSearch;\nimport org.springframework.util.Assert;\n\n/**\n * Base class for the authenticator implementations.\n *\n * @author Luke Taylor\n */\npublic abstract class AbstractLdapAuthenticator implements LdapAuthenticator, InitializingBean, MessageSourceAware {\n\n\tprivate final Object mutex = new Object();\n\n\tprivate final ContextSource contextSource;\n\n\t/**\n\t * Optional search object which can be used to locate a user when a simple DN match\n\t * isn't sufficient\n\t */\n\tprivate @Nullable LdapUserSearch userSearch;\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\t/**\n\t * The attributes which will be retrieved from the directory. Null means all\n\t * attributes\n\t */\n\tprivate String @Nullable [] userAttributes = null;\n\n\t// private String[] userDnPattern = null;\n\t/** Stores the patterns which are used as potential DN matches */\n\tprivate MessageFormat @Nullable [] userDnFormat = null;\n\n\t/**\n\t * Create an initialized instance with the {@link ContextSource} provided.\n\t * @param contextSource the {@link ContextSource} to use\n\t */\n\tpublic AbstractLdapAuthenticator(ContextSource contextSource) {\n\t\tAssert.notNull(contextSource, \"contextSource must not be null.\");\n\t\tthis.contextSource = contextSource;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.isTrue((this.userDnFormat != null) || (this.userSearch != null),\n\t\t\t\t\"Either an LdapUserSearch or DN pattern (or both) must be supplied.\");\n\t}\n\n\tprotected ContextSource getContextSource() {\n\t\treturn this.contextSource;\n\t}\n\n\tpublic String @Nullable [] getUserAttributes() {\n\t\treturn this.userAttributes;\n\t}\n\n\t/**\n\t * Builds list of possible DNs for the user, worked out from the\n\t * <tt>userDnPatterns</tt> property.\n\t * @param username the user's login name\n\t * @return the list of possible DN matches, empty if <tt>userDnPatterns</tt> wasn't\n\t * set.\n\t */\n\tprotected List<String> getUserDns(String username) {\n\t\tif (this.userDnFormat == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<String> userDns = new ArrayList<>(this.userDnFormat.length);\n\t\tString[] args = new String[] { LdapEncoder.nameEncode(username) };\n\t\tsynchronized (this.mutex) {\n\t\t\tfor (MessageFormat formatter : this.userDnFormat) {\n\t\t\t\tuserDns.add(formatter.format(args));\n\t\t\t}\n\t\t}\n\t\treturn userDns;\n\t}\n\n\tprotected @Nullable LdapUserSearch getUserSearch() {\n\t\treturn this.userSearch;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"Message source must not be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t/**\n\t * Sets the user attributes which will be retrieved from the directory.\n\t * @param userAttributes the set of user attributes to retrieve\n\t */\n\tpublic void setUserAttributes(String[] userAttributes) {\n\t\tAssert.notNull(userAttributes, \"The userAttributes property cannot be set to null\");\n\t\tthis.userAttributes = userAttributes;\n\t}\n\n\t/**\n\t * Sets the pattern which will be used to supply a DN for the user. The pattern should\n\t * be the name relative to the root DN. The pattern argument {0} will contain the\n\t * username. An example would be \"cn={0},ou=people\".\n\t * @param dnPattern the array of patterns which will be tried when converting a\n\t * username to a DN.\n\t */\n\tpublic void setUserDnPatterns(String[] dnPattern) {\n\t\tAssert.notNull(dnPattern, \"The array of DN patterns cannot be set to null\");\n\t\t// this.userDnPattern = dnPattern;\n\t\tthis.userDnFormat = new MessageFormat[dnPattern.length];\n\t\tfor (int i = 0; i < dnPattern.length; i++) {\n\t\t\tthis.userDnFormat[i] = new MessageFormat(dnPattern[i]);\n\t\t}\n\t}\n\n\tpublic void setUserSearch(LdapUserSearch userSearch) {\n\t\tAssert.notNull(userSearch, \"The userSearch cannot be set to null\");\n\t\tthis.userSearch = userSearch;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/BindAuthenticator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport javax.naming.Name;\nimport javax.naming.directory.Attributes;\nimport javax.naming.directory.DirContext;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.ldap.NamingException;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.ldap.support.LdapUtils;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyControl;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyControlExtractor;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An authenticator which binds as a user.\n *\n * @author Luke Taylor\n * @see AbstractLdapAuthenticator\n */\npublic class BindAuthenticator extends AbstractLdapAuthenticator {\n\n\tprivate static final Log logger = LogFactory.getLog(BindAuthenticator.class);\n\n\tprivate boolean alsoHandleJavaxNamingBindExceptions = false;\n\n\t/**\n\t * Create an initialized instance using the {@link BaseLdapPathContextSource}\n\t * provided.\n\t * @param contextSource the BaseLdapPathContextSource instance against which bind\n\t * operations will be performed.\n\t */\n\tpublic BindAuthenticator(BaseLdapPathContextSource contextSource) {\n\t\tsuper(contextSource);\n\t}\n\n\t@Override\n\tpublic DirContextOperations authenticate(Authentication authentication) {\n\t\tDirContextOperations user = null;\n\t\tAssert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,\n\t\t\t\t\"Can only process UsernamePasswordAuthenticationToken objects\");\n\t\tString username = authentication.getName();\n\t\tString password = (String) authentication.getCredentials();\n\t\tif (!StringUtils.hasLength(password)) {\n\t\t\tlogger.debug(LogMessage.format(\"Failed to authenticate since no credentials provided\"));\n\t\t\tthrow new BadCredentialsException(\n\t\t\t\t\tthis.messages.getMessage(\"BindAuthenticator.emptyPassword\", \"Empty Password\"));\n\t\t}\n\t\t// If DN patterns are configured, try authenticating with them directly\n\t\tfor (String dn : getUserDns(username)) {\n\t\t\tuser = bindWithDn(dn, username, password);\n\t\t\tif (user != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (user == null) {\n\t\t\tlogger.debug(LogMessage.of(() -> \"Failed to bind with any user DNs \" + getUserDns(username)));\n\t\t}\n\t\t// Otherwise use the configured search object to find the user and authenticate\n\t\t// with the returned DN.\n\t\tif (user == null && getUserSearch() != null) {\n\t\t\tlogger.trace(\"Searching for user using \" + getUserSearch());\n\t\t\tDirContextOperations userFromSearch = getUserSearch().searchForUser(username);\n\t\t\tuser = bindWithDn(userFromSearch.getDn().toString(), username, password, userFromSearch.getAttributes());\n\t\t\tif (user == null) {\n\t\t\t\tlogger.debug(\"Failed to find user using \" + getUserSearch());\n\t\t\t}\n\t\t}\n\t\tif (user == null) {\n\t\t\tthrow new BadCredentialsException(\n\t\t\t\t\tthis.messages.getMessage(\"BindAuthenticator.badCredentials\", \"Bad credentials\"));\n\t\t}\n\t\treturn user;\n\t}\n\n\tprivate @Nullable DirContextOperations bindWithDn(String userDnStr, String username, String password) {\n\t\treturn bindWithDn(userDnStr, username, password, null);\n\t}\n\n\tprivate @Nullable DirContextOperations bindWithDn(String userDnStr, String username, String password,\n\t\t\t@Nullable Attributes attrs) {\n\t\tBaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();\n\t\tName userDn = LdapUtils.newLdapName(userDnStr);\n\t\tName fullDn = LdapUtils.prepend(userDn, ctxSource.getBaseLdapName());\n\t\tlogger.trace(LogMessage.format(\"Attempting to bind as %s\", fullDn));\n\t\tDirContext ctx = null;\n\t\ttry {\n\t\t\tctx = getContextSource().getContext(fullDn.toString(), password);\n\t\t\t// Check for password policy control\n\t\t\tPasswordPolicyControl ppolicy = PasswordPolicyControlExtractor.extractControl(ctx);\n\t\t\tif (attrs == null || attrs.size() == 0) {\n\t\t\t\tattrs = ctx.getAttributes(userDn, getUserAttributes());\n\t\t\t}\n\t\t\tDirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapName());\n\t\t\tif (ppolicy != null) {\n\t\t\t\tresult.setAttributeValue(ppolicy.getID(), ppolicy);\n\t\t\t}\n\t\t\tlogger.debug(LogMessage.format(\"Bound %s\", fullDn));\n\t\t\treturn result;\n\t\t}\n\t\tcatch (NamingException ex) {\n\t\t\t// This will be thrown if an invalid user name is used and the method may\n\t\t\t// be called multiple times to try different names, so we trap the exception\n\t\t\t// unless a subclass wishes to implement more specialized behaviour.\n\t\t\thandleIfBindException(userDnStr, username, ex);\n\t\t}\n\t\tcatch (javax.naming.NamingException ex) {\n\t\t\tif (!this.alsoHandleJavaxNamingBindExceptions) {\n\t\t\t\tthrow LdapUtils.convertLdapException(ex);\n\t\t\t}\n\t\t\thandleIfBindException(userDnStr, username, LdapUtils.convertLdapException(ex));\n\t\t}\n\t\tfinally {\n\t\t\tLdapUtils.closeContext(ctx);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void handleIfBindException(String dn, String username, org.springframework.ldap.NamingException naming) {\n\t\tif ((naming instanceof org.springframework.ldap.AuthenticationException)\n\t\t\t\t|| (naming instanceof org.springframework.ldap.OperationNotSupportedException)) {\n\t\t\thandleBindException(dn, username, naming);\n\t\t}\n\t\telse {\n\t\t\tthrow naming;\n\t\t}\n\t}\n\n\t/**\n\t * Allows subclasses to inspect the exception thrown by an attempt to bind with a\n\t * particular DN. The default implementation just reports the failure to the debug\n\t * logger.\n\t */\n\tprotected void handleBindException(String userDn, String username, Throwable cause) {\n\t\tlogger.trace(LogMessage.format(\"Failed to bind as %s\", userDn), cause);\n\t}\n\n\t/**\n\t * Set whether javax-based bind exceptions should also be delegated to\n\t * {@code #handleBindException} (only Spring-based bind exceptions are handled by\n\t * default)\n\t *\n\t * <p>\n\t * For passivity reasons, defaults to {@code false}, though may change to {@code true}\n\t * in future releases.\n\t * @param alsoHandleJavaxNamingBindExceptions - whether to delegate javax-based bind\n\t * exceptions to #handleBindException\n\t * @since 6.4\n\t */\n\tpublic void setAlsoHandleJavaxNamingBindExceptions(boolean alsoHandleJavaxNamingBindExceptions) {\n\t\tthis.alsoHandleJavaxNamingBindExceptions = alsoHandleJavaxNamingBindExceptions;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport java.util.Collection;\n\nimport org.springframework.ldap.NamingException;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyException;\nimport org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link org.springframework.security.authentication.AuthenticationProvider}\n * implementation that authenticates against an LDAP server.\n * <p>\n * There are many ways in which an LDAP directory can be configured so this class\n * delegates most of its responsibilities to two separate strategy interfaces,\n * {@link LdapAuthenticator} and {@link LdapAuthoritiesPopulator}.\n *\n * <h3>LdapAuthenticator</h3> This interface is responsible for performing the user\n * authentication and retrieving the user's information from the directory. Example\n * implementations are\n * {@link org.springframework.security.ldap.authentication.BindAuthenticator\n * BindAuthenticator} which authenticates the user by \"binding\" as that user, and\n * {@link org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator\n * PasswordComparisonAuthenticator} which compares the supplied password with the value\n * stored in the directory, using an LDAP \"compare\" operation.\n * <p>\n * The task of retrieving the user attributes is delegated to the authenticator because\n * the permissions on the attributes may depend on the type of authentication being used;\n * for example, if binding as the user, it may be necessary to read them with the user's\n * own permissions (using the same context used for the bind operation).\n *\n * <h3>LdapAuthoritiesPopulator</h3> Once the user has been authenticated, this interface\n * is called to obtain the set of granted authorities for the user. The\n * {@link DefaultLdapAuthoritiesPopulator DefaultLdapAuthoritiesPopulator} can be\n * configured to obtain user role information from the user's attributes and/or to perform\n * a search for \"groups\" that the user is a member of and map these to roles.\n *\n * <p>\n * A custom implementation could obtain the roles from a completely different source, for\n * example from a database.\n *\n * <h3>Configuration</h3>\n *\n * A simple configuration might be as follows:\n *\n * <pre>\n *   &lt;bean id=&quot;contextSource&quot;\n *       class=&quot;org.springframework.security.ldap.DefaultSpringSecurityContextSource&quot;&gt;\n *     &lt;constructor-arg value=&quot;ldap://monkeymachine:389/dc=springframework,dc=org&quot;/&gt;\n *     &lt;property name=&quot;userDn&quot; value=&quot;cn=manager,dc=springframework,dc=org&quot;/&gt;\n *     &lt;property name=&quot;password&quot; value=&quot;password&quot;/&gt;\n *   &lt;/bean&gt;\n *\n *   &lt;bean id=&quot;ldapAuthProvider&quot;\n *       class=&quot;org.springframework.security.ldap.authentication.LdapAuthenticationProvider&quot;&gt;\n *     &lt;constructor-arg&gt;\n *       &lt;bean class=&quot;org.springframework.security.ldap.authentication.BindAuthenticator&quot;&gt;\n *           &lt;constructor-arg ref=&quot;contextSource&quot;/&gt;\n *           &lt;property name=&quot;userDnPatterns&quot;&gt;&lt;list&gt;&lt;value&gt;uid={0},ou=people&lt;/value&gt;&lt;/list&gt;&lt;/property&gt;\n *       &lt;/bean&gt;\n *     &lt;/constructor-arg&gt;\n *     &lt;constructor-arg&gt;\n *       &lt;bean class=&quot;org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator&quot;&gt;\n *           &lt;constructor-arg ref=&quot;contextSource&quot;/&gt;\n *           &lt;constructor-arg value=&quot;ou=groups&quot;/&gt;\n *           &lt;property name=&quot;groupRoleAttribute&quot; value=&quot;ou&quot;/&gt;\n *       &lt;/bean&gt;\n *     &lt;/constructor-arg&gt;\n *   &lt;/bean&gt;\n * </pre>\n *\n * <p>\n * This would set up the provider to access an LDAP server with URL\n * <tt>ldap://monkeymachine:389/dc=springframework,dc=org</tt>. Authentication will be\n * performed by attempting to bind with the DN\n * <tt>uid=&lt;user-login-name&gt;,ou=people,dc=springframework,dc=org</tt>. After\n * successful authentication, roles will be assigned to the user by searching under the DN\n * <tt>ou=groups,dc=springframework,dc=org</tt> with the default filter\n * <tt>(member=&lt;user's-DN&gt;)</tt>. The role name will be taken from the \"ou\"\n * attribute of each match.\n * <p>\n * The authenticate method will reject empty passwords outright. LDAP servers may allow an\n * anonymous bind operation with an empty password, even if a DN is supplied. In practice\n * this means that if the LDAP directory is configured to allow unauthenticated access, it\n * might be possible to authenticate as <i>any</i> user just by supplying an empty\n * password. More information on the misuse of unauthenticated access can be found in\n * <a href=\"https://www.ietf.org/internet-drafts/draft-ietf-ldapbis-authmeth-19.txt\">\n * draft -ietf-ldapbis-authmeth-19.txt</a>.\n *\n * @author Luke Taylor\n * @see BindAuthenticator\n * @see DefaultLdapAuthoritiesPopulator\n */\npublic class LdapAuthenticationProvider extends AbstractLdapAuthenticationProvider {\n\n\tprivate LdapAuthenticator authenticator;\n\n\tprivate LdapAuthoritiesPopulator authoritiesPopulator;\n\n\tprivate boolean hideUserNotFoundExceptions = true;\n\n\t/**\n\t * Create an instance with the supplied authenticator and authorities populator\n\t * implementations.\n\t * @param authenticator the authentication strategy (bind, password comparison, etc)\n\t * to be used by this provider for authenticating users.\n\t * @param authoritiesPopulator the strategy for obtaining the authorities for a given\n\t * user after they've been authenticated.\n\t */\n\tpublic LdapAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {\n\t\tthis.setAuthenticator(authenticator);\n\t\tthis.setAuthoritiesPopulator(authoritiesPopulator);\n\t}\n\n\t/**\n\t * Creates an instance with the supplied authenticator and a null authorities\n\t * populator. In this case, the authorities must be mapped from the user context.\n\t * @param authenticator the authenticator strategy.\n\t */\n\tpublic LdapAuthenticationProvider(LdapAuthenticator authenticator) {\n\t\tthis.setAuthenticator(authenticator);\n\t\tthis.setAuthoritiesPopulator(new NullLdapAuthoritiesPopulator());\n\t}\n\n\tprivate void setAuthenticator(LdapAuthenticator authenticator) {\n\t\tAssert.notNull(authenticator, \"An LdapAuthenticator must be supplied\");\n\t\tthis.authenticator = authenticator;\n\t}\n\n\tprivate LdapAuthenticator getAuthenticator() {\n\t\treturn this.authenticator;\n\t}\n\n\tprivate void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) {\n\t\tAssert.notNull(authoritiesPopulator, \"An LdapAuthoritiesPopulator must be supplied\");\n\t\tthis.authoritiesPopulator = authoritiesPopulator;\n\t}\n\n\tprotected LdapAuthoritiesPopulator getAuthoritiesPopulator() {\n\t\treturn this.authoritiesPopulator;\n\t}\n\n\tpublic void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {\n\t\tthis.hideUserNotFoundExceptions = hideUserNotFoundExceptions;\n\t}\n\n\t@Override\n\tprotected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken authentication) {\n\t\ttry {\n\t\t\treturn getAuthenticator().authenticate(authentication);\n\t\t}\n\t\tcatch (PasswordPolicyException ex) {\n\t\t\t// The only reason a ppolicy exception can occur during a bind is that the\n\t\t\t// account is locked.\n\t\t\tthrow new LockedException(\n\t\t\t\t\tthis.messages.getMessage(ex.getStatus().getErrorCode(), ex.getStatus().getDefaultMessage()));\n\t\t}\n\t\tcatch (UsernameNotFoundException ex) {\n\t\t\tif (this.hideUserNotFoundExceptions) {\n\t\t\t\tthrow new BadCredentialsException(\n\t\t\t\t\t\tthis.messages.getMessage(\"LdapAuthenticationProvider.badCredentials\", \"Bad credentials\"));\n\t\t\t}\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (NamingException ex) {\n\t\t\tthrow new InternalAuthenticationServiceException(ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData, String username,\n\t\t\tString password) {\n\t\treturn getAuthoritiesPopulator().getGrantedAuthorities(userData, username);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.Authentication;\n\n/**\n * The strategy interface for locating and authenticating an Ldap user.\n * <p>\n * The LdapAuthenticationProvider calls this interface to authenticate a user and obtain\n * the information for that user from the directory.\n *\n * @author Luke Taylor\n * @see org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator\n * @see org.springframework.security.ldap.authentication.UserDetailsServiceLdapAuthoritiesPopulator\n */\npublic interface LdapAuthenticator {\n\n\t/**\n\t * Authenticates as a user and obtains additional user information from the directory.\n\t * @param authentication the authentication request\n\t * @return the details of the successfully authenticated user.\n\t */\n\tDirContextOperations authenticate(Authentication authentication);\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/LdapEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport java.util.Locale;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Helper class to encode and decode ldap names and values.\n *\n * <p>\n * NOTE: This is a copy from Spring LDAP so that both Spring LDAP 1.x and 2.x can be\n * supported without reflection.\n * </p>\n *\n * @author Adam Skogman\n * @author Mattias Hellborg Arthursson\n */\nfinal class LdapEncoder {\n\n\tprivate static final @Nullable String[] NAME_ESCAPE_TABLE = new String[96];\n\tstatic {\n\t\t// all below 0x20 (control chars)\n\t\tfor (char c = 0; c < ' '; c++) {\n\t\t\tNAME_ESCAPE_TABLE[c] = \"\\\\\" + toTwoCharHex(c);\n\t\t}\n\t\tNAME_ESCAPE_TABLE['#'] = \"\\\\#\";\n\t\tNAME_ESCAPE_TABLE[','] = \"\\\\,\";\n\t\tNAME_ESCAPE_TABLE[';'] = \"\\\\;\";\n\t\tNAME_ESCAPE_TABLE['='] = \"\\\\=\";\n\t\tNAME_ESCAPE_TABLE['+'] = \"\\\\+\";\n\t\tNAME_ESCAPE_TABLE['<'] = \"\\\\<\";\n\t\tNAME_ESCAPE_TABLE['>'] = \"\\\\>\";\n\t\tNAME_ESCAPE_TABLE['\\\"'] = \"\\\\\\\"\";\n\t\tNAME_ESCAPE_TABLE['\\\\'] = \"\\\\\\\\\";\n\t}\n\n\t/**\n\t * All static methods - not to be instantiated.\n\t */\n\tprivate LdapEncoder() {\n\t}\n\n\tstatic String toTwoCharHex(char c) {\n\t\tString raw = Integer.toHexString(c).toUpperCase(Locale.ENGLISH);\n\t\treturn (raw.length() > 1) ? raw : \"0\" + raw;\n\t}\n\n\t/**\n\t * LDAP Encodes a value for use with a DN. Escapes for LDAP, not JNDI! <br/>\n\t * Escapes:<br/>\n\t * ' ' [space] - \"\\ \" [if first or last] <br/>\n\t * '#' [hash] - \"\\#\" <br/>\n\t * ',' [comma] - \"\\,\" <br/>\n\t * ';' [semicolon] - \"\\;\" <br/>\n\t * '= [equals] - \"\\=\" <br/>\n\t * '+' [plus] - \"\\+\" <br/>\n\t * '&lt;' [less than] - \"\\&lt;\" <br/>\n\t * '&gt;' [greater than] - \"\\&gt;\" <br/>\n\t * '\"' [double quote] - \"\\\"\" <br/>\n\t * '\\' [backslash] - \"\\\\\" <br/>\n\t * @param value the value to escape.\n\t * @return The escaped value.\n\t */\n\tstatic String nameEncode(String value) {\n\t\tStringBuilder encodedValue = new StringBuilder(value.length() * 2);\n\t\tint length = value.length();\n\t\tint last = length - 1;\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tchar c = value.charAt(i);\n\t\t\t// space first or last\n\t\t\tif (c == ' ' && (i == 0 || i == last)) {\n\t\t\t\tencodedValue.append(\"\\\\ \");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// check in table for escapes\n\t\t\tif (c < NAME_ESCAPE_TABLE.length) {\n\t\t\t\tString esc = NAME_ESCAPE_TABLE[c];\n\t\t\t\tif (esc != null) {\n\t\t\t\t\tencodedValue.append(esc);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// default: add the char\n\t\t\tencodedValue.append(c);\n\t\t}\n\t\treturn encodedValue.toString();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/NullLdapAuthoritiesPopulator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport java.util.Collection;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\npublic final class NullLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {\n\n\t@Override\n\tpublic Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userDetails, String username) {\n\t\treturn AuthorityUtils.NO_AUTHORITIES;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.ldap.NameNotFoundException;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.password.LdapShaPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link org.springframework.security.ldap.authentication.LdapAuthenticator\n * LdapAuthenticator} which compares the login password with the value stored in the\n * directory using a remote LDAP \"compare\" operation.\n *\n * <p>\n * If passwords are stored in digest form in the repository, then a suitable\n * {@link PasswordEncoder} implementation must be supplied. By default, passwords are\n * encoded using the {@link LdapShaPasswordEncoder}. Note that compare operations will not\n * work if salted-SHA (SSHA) passwords are used, as it is not possible to know the salt\n * value which is a random byte sequence generated by the directory.\n *\n * @author Luke Taylor\n */\npublic final class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator {\n\n\tprivate static final Log logger = LogFactory.getLog(PasswordComparisonAuthenticator.class);\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\tprivate String passwordAttributeName = \"userPassword\";\n\n\tprivate boolean usePasswordAttrCompare = false;\n\n\t/**\n\t * @deprecated Use\n\t * {@link #PasswordComparisonAuthenticator(BaseLdapPathContextSource, PasswordEncoder)}\n\t * instead\n\t */\n\t@Deprecated(since = \"7.1\")\n\t@SuppressWarnings(\"deprecation\")\n\tpublic PasswordComparisonAuthenticator(BaseLdapPathContextSource contextSource) {\n\t\tthis(contextSource, new LdapShaPasswordEncoder(KeyGenerators.shared(0)));\n\t}\n\n\tpublic PasswordComparisonAuthenticator(BaseLdapPathContextSource contextSource, PasswordEncoder passwordEncoder) {\n\t\tsuper(contextSource);\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder must not be null\");\n\t\tthis.passwordEncoder = passwordEncoder;\n\t}\n\n\t@Override\n\tpublic DirContextOperations authenticate(final Authentication authentication) {\n\t\tAssert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,\n\t\t\t\t\"Can only process UsernamePasswordAuthenticationToken objects\");\n\t\t// locate the user and check the password\n\t\tDirContextOperations user = null;\n\t\tString username = authentication.getName();\n\t\tString password = (String) authentication.getCredentials();\n\t\tSpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(getContextSource());\n\t\tfor (String userDn : getUserDns(username)) {\n\t\t\ttry {\n\t\t\t\tuser = ldapTemplate.retrieveEntry(userDn, getUserAttributes());\n\t\t\t}\n\t\t\tcatch (NameNotFoundException ignore) {\n\t\t\t\tlogger.trace(LogMessage.format(\"Failed to retrieve user with %s\", userDn), ignore);\n\t\t\t}\n\t\t\tif (user != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (user == null) {\n\t\t\tlogger.debug(LogMessage.of(() -> \"Failed to retrieve user with any user DNs \" + getUserDns(username)));\n\t\t}\n\t\tif (user == null && getUserSearch() != null) {\n\t\t\tlogger.trace(\"Searching for user using \" + getUserSearch());\n\t\t\tuser = getUserSearch().searchForUser(username);\n\t\t}\n\t\tif (user == null) {\n\t\t\tthrow UsernameNotFoundException.fromUsername(username);\n\t\t}\n\t\tif (logger.isTraceEnabled()) {\n\t\t\tlogger.trace(LogMessage.format(\"Comparing password attribute '%s' for user '%s'\",\n\t\t\t\t\tthis.passwordAttributeName, user.getDn()));\n\t\t}\n\t\tif (this.usePasswordAttrCompare && isPasswordAttrCompare(user, password)) {\n\t\t\tlogger.debug(LogMessage.format(\"Locally matched password attribute '%s' for user '%s'\",\n\t\t\t\t\tthis.passwordAttributeName, user.getDn()));\n\t\t\treturn user;\n\t\t}\n\t\tAssert.notNull(password, \"LDAP password cannot be null\");\n\t\tif (isLdapPasswordCompare(user, ldapTemplate, password)) {\n\t\t\tlogger.debug(LogMessage.format(\"LDAP-matched password attribute '%s' for user '%s'\",\n\t\t\t\t\tthis.passwordAttributeName, user.getDn()));\n\t\t\treturn user;\n\t\t}\n\t\tthrow new BadCredentialsException(\n\t\t\t\tthis.messages.getMessage(\"PasswordComparisonAuthenticator.badCredentials\", \"Bad credentials\"));\n\t}\n\n\tprivate boolean isPasswordAttrCompare(DirContextOperations user, @Nullable String password) {\n\t\tString passwordAttrValue = getPassword(user);\n\t\treturn this.passwordEncoder.matches(password, passwordAttrValue);\n\t}\n\n\tprivate @Nullable String getPassword(DirContextOperations user) {\n\t\tObject passwordAttrValue = user.getObjectAttribute(this.passwordAttributeName);\n\t\tif (passwordAttrValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (passwordAttrValue instanceof byte[]) {\n\t\t\treturn new String((byte[]) passwordAttrValue);\n\t\t}\n\t\treturn String.valueOf(passwordAttrValue);\n\t}\n\n\tprivate boolean isLdapPasswordCompare(DirContextOperations user, SpringSecurityLdapTemplate ldapTemplate,\n\t\t\tString password) {\n\t\tString encodedPassword = this.passwordEncoder.encode(password);\n\t\tbyte[] passwordBytes = Utf8.encode(encodedPassword);\n\t\treturn ldapTemplate.compare(user.getDn().toString(), this.passwordAttributeName, passwordBytes);\n\t}\n\n\tpublic void setPasswordAttributeName(String passwordAttribute) {\n\t\tAssert.hasLength(passwordAttribute, \"passwordAttributeName must not be empty or null\");\n\t\tthis.passwordAttributeName = passwordAttribute;\n\t}\n\n\tpublic void setUsePasswordAttrCompare(boolean usePasswordAttrCompare) {\n\t\tthis.usePasswordAttrCompare = usePasswordAttrCompare;\n\t}\n\n\tpublic void setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder must not be null.\");\n\t\tthis.passwordEncoder = passwordEncoder;\n\t\tsetUsePasswordAttrCompare(true);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/SpringSecurityAuthenticationSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.ldap.core.AuthenticationSource;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.ldap.userdetails.LdapUserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * An AuthenticationSource to retrieve authentication information stored in Spring\n * Security's {@link SecurityContextHolder}.\n * <p>\n * This is a copy of Spring LDAP's AcegiAuthenticationSource, updated for use with Spring\n * Security 2.0.\n *\n * @author Mattias Arthursson\n * @author Luke Taylor\n * @since 2.0\n */\npublic class SpringSecurityAuthenticationSource implements AuthenticationSource {\n\n\tprivate static final Log log = LogFactory.getLog(SpringSecurityAuthenticationSource.class);\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t/**\n\t * Get the principals of the logged in user, in this case the distinguished name.\n\t * @return the distinguished name of the logged in user.\n\t */\n\t@Override\n\tpublic String getPrincipal() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tlog.debug(\"Returning empty String as Principal since authentication is null\");\n\t\t\treturn \"\";\n\t\t}\n\t\tObject principal = authentication.getPrincipal();\n\t\tif (principal instanceof LdapUserDetails details) {\n\t\t\treturn details.getDn();\n\t\t}\n\t\tif (authentication instanceof AnonymousAuthenticationToken) {\n\t\t\tlog.debug(\"Returning empty String as Principal since authentication is anonymous\");\n\t\t\treturn \"\";\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"The principal property of the authentication object\" + \"needs to be an LdapUserDetails.\");\n\t}\n\n\t/**\n\t * @see org.springframework.ldap.core.AuthenticationSource#getCredentials()\n\t */\n\t@Override\n\tpublic String getCredentials() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tlog.debug(\"Returning empty String as Credentials since authentication is null\");\n\t\t\treturn \"\";\n\t\t}\n\t\tString password = (String) authentication.getCredentials();\n\t\tif (password == null) {\n\t\t\tlog.debug(\"Returning empty String as Credentials since password is null\");\n\t\t\treturn \"\";\n\t\t}\n\t\treturn password;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/UserDetailsServiceLdapAuthoritiesPopulator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport java.util.Collection;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.util.Assert;\n\n/**\n * Simple LdapAuthoritiesPopulator which delegates to a UserDetailsService, using the name\n * which was supplied at login as the username.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class UserDetailsServiceLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {\n\n\tprivate final UserDetailsService userDetailsService;\n\n\tpublic UserDetailsServiceLdapAuthoritiesPopulator(UserDetailsService userService) {\n\t\tAssert.notNull(userService, \"userDetailsService cannot be null\");\n\t\tthis.userDetailsService = userService;\n\t}\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData,\n\t\t\tString username) {\n\t\treturn this.userDetailsService.loadUserByUsername(username).getAuthorities();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryAuthenticationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication.ad;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * <p>\n * Thrown as a translation of an {@link javax.naming.AuthenticationException} when\n * attempting to authenticate against Active Directory using\n * {@link ActiveDirectoryLdapAuthenticationProvider}. Typically this error is wrapped by\n * an {@link AuthenticationException} since it does not provide a user friendly message.\n * When wrapped, the original Exception can be caught and\n * {@link ActiveDirectoryAuthenticationException} can be accessed using\n * {@link AuthenticationException#getCause()} for custom error handling.\n * </p>\n * <p>\n * The {@link #getDataCode()} will return the error code associated with the data portion\n * of the error message. For example, the following error message would return 773 for\n * {@link #getDataCode()}.\n * </p>\n *\n * <pre>\n * javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 775, vece ]\n * </pre>\n *\n * @author Rob Winch\n */\n@SuppressWarnings(\"serial\")\npublic final class ActiveDirectoryAuthenticationException extends AuthenticationException {\n\n\tprivate final String dataCode;\n\n\tActiveDirectoryAuthenticationException(String dataCode, @Nullable String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t\tthis.dataCode = dataCode;\n\t}\n\n\tpublic String getDataCode() {\n\t\treturn this.dataCode;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication.ad;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Hashtable;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.naming.AuthenticationException;\nimport javax.naming.Context;\nimport javax.naming.NamingException;\nimport javax.naming.OperationNotSupportedException;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.SearchControls;\nimport javax.naming.ldap.InitialLdapContext;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.ldap.CommunicationException;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.LdapClient;\nimport org.springframework.ldap.core.support.DefaultDirObjectFactory;\nimport org.springframework.ldap.core.support.SingleContextSource;\nimport org.springframework.ldap.query.LdapQuery;\nimport org.springframework.ldap.query.LdapQueryBuilder;\nimport org.springframework.ldap.query.SearchScope;\nimport org.springframework.ldap.support.LdapUtils;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Specialized LDAP authentication provider which uses Active Directory configuration\n * conventions.\n * <p>\n * It will authenticate using the Active Directory\n * <a href=\"https://msdn.microsoft.com/en-us/library/ms680857%28VS.85%29.aspx\">\n * {@code userPrincipalName}</a> or a custom {@link #setSearchFilter(String) searchFilter}\n * in the form {@code username@domain}. If the username does not already end with the\n * domain name, the {@code userPrincipalName} will be built by appending the configured\n * domain name to the username supplied in the authentication request. If no domain name\n * is configured, it is assumed that the username will always contain the domain name.\n * <p>\n * The user authorities are obtained from the data contained in the {@code memberOf}\n * attribute.\n * <p>\n * <h3>Active Directory Sub-Error Codes</h3>\n * <p>\n * When an authentication fails, resulting in a standard LDAP 49 error code, Active\n * Directory also supplies its own sub-error codes within the error message. These will be\n * used to provide additional log information on why an authentication has failed. Typical\n * examples are\n *\n * <ul>\n * <li>525 - user not found</li>\n * <li>52e - invalid credentials</li>\n * <li>530 - not permitted to logon at this time</li>\n * <li>532 - password expired</li>\n * <li>533 - account disabled</li>\n * <li>701 - account expired</li>\n * <li>773 - user must reset password</li>\n * <li>775 - account locked</li>\n * </ul>\n * <p>\n * If you set the {@link #setConvertSubErrorCodesToExceptions(boolean)\n * convertSubErrorCodesToExceptions} property to {@code true}, the codes will also be used\n * to control the exception raised.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @author Roman Zabaluev\n * @author Andrey Litvitski\n * @since 3.1\n */\npublic final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLdapAuthenticationProvider {\n\n\tprivate static final Pattern SUB_ERROR_CODE = Pattern.compile(\".*data\\\\s([0-9a-f]{3,4}).*\");\n\n\t// Error codes\n\tprivate static final int USERNAME_NOT_FOUND = 0x525;\n\n\tprivate static final int INVALID_PASSWORD = 0x52e;\n\n\tprivate static final int NOT_PERMITTED = 0x530;\n\n\tprivate static final int PASSWORD_EXPIRED = 0x532;\n\n\tprivate static final int ACCOUNT_DISABLED = 0x533;\n\n\tprivate static final int ACCOUNT_EXPIRED = 0x701;\n\n\tprivate static final int PASSWORD_NEEDS_RESET = 0x773;\n\n\tprivate static final int ACCOUNT_LOCKED = 0x775;\n\n\tprivate final @Nullable String domain;\n\n\tprivate final @Nullable String rootDn;\n\n\tprivate final String url;\n\n\tprivate boolean convertSubErrorCodesToExceptions;\n\n\tprivate String searchFilter = \"(&(objectClass=user)(userPrincipalName={0}))\";\n\n\tprivate Map<String, Object> contextEnvironmentProperties = new HashMap<>();\n\n\t// Only used to allow tests to substitute a mock LdapContext\n\tContextFactory contextFactory = new ContextFactory();\n\n\tprivate LdapAuthoritiesPopulator authoritiesPopulator = new DefaultActiveDirectoryAuthoritiesPopulator();\n\n\t/**\n\t * @param domain the domain name (can be null or empty)\n\t * @param url an LDAP url (or multiple space-delimited URLs).\n\t * @param rootDn the root DN (can be null or empty)\n\t * @see <a href=\"https://docs.oracle.com/javase/jndi/tutorial/ldap/misc/url.html\">JNDI\n\t * URL format documentation</a>\n\t */\n\tpublic ActiveDirectoryLdapAuthenticationProvider(String domain, String url, String rootDn) {\n\t\tAssert.isTrue(StringUtils.hasText(url), \"Url cannot be empty\");\n\t\tthis.domain = StringUtils.hasText(domain) ? domain.toLowerCase(Locale.ROOT) : null;\n\t\tthis.url = url;\n\t\tthis.rootDn = StringUtils.hasText(rootDn) ? rootDn.toLowerCase(Locale.ROOT) : null;\n\t}\n\n\t/**\n\t * @param domain the domain name (can be null or empty)\n\t * @param url an LDAP url (or multiple URLs)\n\t */\n\tpublic ActiveDirectoryLdapAuthenticationProvider(String domain, String url) {\n\t\tAssert.isTrue(StringUtils.hasText(url), \"Url cannot be empty\");\n\t\tthis.domain = StringUtils.hasText(domain) ? domain.toLowerCase(Locale.ROOT) : null;\n\t\tthis.url = url;\n\t\tthis.rootDn = (this.domain != null) ? rootDnFromDomain(this.domain) : null;\n\t}\n\n\t@Override\n\tprotected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth) {\n\t\tString username = auth.getName();\n\t\tString password = (String) auth.getCredentials();\n\t\tAssert.notNull(password, \"password cannot be null\");\n\t\tDirContext ctx = null;\n\t\ttry {\n\t\t\tctx = bindAsUser(username, password);\n\t\t\treturn searchForUser(ctx, username);\n\t\t}\n\t\tcatch (CommunicationException ex) {\n\t\t\tthrow badLdapConnection(ex);\n\t\t}\n\t\tcatch (NamingException ex) {\n\t\t\tthis.logger.error(\"Failed to locate directory entry for authenticated user: \" + username, ex);\n\t\t\tthrow badCredentials(ex);\n\t\t}\n\t\tfinally {\n\t\t\tLdapUtils.closeContext(ctx);\n\t\t}\n\t}\n\n\t/**\n\t * Creates the user authority list from the values of the {@code memberOf} attribute\n\t * obtained from the user's Active Directory entry.\n\t */\n\t@Override\n\tprotected Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData, String username,\n\t\t\tString password) {\n\t\treturn this.authoritiesPopulator.getGrantedAuthorities(userData, username);\n\t}\n\n\tprivate DirContext bindAsUser(String username, String password) {\n\t\t// TODO. add DNS lookup based on domain\n\t\tHashtable<String, Object> env = new Hashtable<>();\n\t\tenv.put(Context.SECURITY_AUTHENTICATION, \"simple\");\n\t\tString bindPrincipal = createBindPrincipal(username);\n\t\tenv.put(Context.SECURITY_PRINCIPAL, bindPrincipal);\n\t\tenv.put(Context.PROVIDER_URL, this.url);\n\t\tenv.put(Context.SECURITY_CREDENTIALS, password);\n\t\tenv.put(Context.INITIAL_CONTEXT_FACTORY, \"com.sun.jndi.ldap.LdapCtxFactory\");\n\t\tenv.put(Context.OBJECT_FACTORIES, DefaultDirObjectFactory.class.getName());\n\t\tenv.putAll(this.contextEnvironmentProperties);\n\t\ttry {\n\t\t\treturn this.contextFactory.createContext(env);\n\t\t}\n\t\tcatch (NamingException ex) {\n\t\t\tif ((ex instanceof AuthenticationException) || (ex instanceof OperationNotSupportedException)) {\n\t\t\t\thandleBindException(bindPrincipal, ex);\n\t\t\t\tthrow badCredentials(ex);\n\t\t\t}\n\t\t\tthrow LdapUtils.convertLdapException(ex);\n\t\t}\n\t}\n\n\tprivate void handleBindException(String bindPrincipal, NamingException exception) {\n\t\tthis.logger.debug(LogMessage.format(\"Authentication for %s failed:%s\", bindPrincipal, exception));\n\t\thandleResolveObj(exception);\n\t\tint subErrorCode = parseSubErrorCode(exception.getMessage());\n\t\tif (subErrorCode <= 0) {\n\t\t\tthis.logger.debug(\"Failed to locate AD-specific sub-error code in message\");\n\t\t\treturn;\n\t\t}\n\t\tthis.logger\n\t\t\t.info(LogMessage.of(() -> \"Active Directory authentication failed: \" + subCodeToLogMessage(subErrorCode)));\n\t\tif (this.convertSubErrorCodesToExceptions) {\n\t\t\traiseExceptionForErrorCode(subErrorCode, exception);\n\t\t}\n\t}\n\n\tprivate void handleResolveObj(NamingException exception) {\n\t\tObject resolvedObj = exception.getResolvedObj();\n\t\tboolean serializable = resolvedObj instanceof Serializable;\n\t\tif (resolvedObj != null && !serializable) {\n\t\t\texception.setResolvedObj(null);\n\t\t}\n\t}\n\n\tprivate int parseSubErrorCode(@Nullable String message) {\n\t\tif (message == null) {\n\t\t\treturn -1;\n\t\t}\n\t\tMatcher matcher = SUB_ERROR_CODE.matcher(message);\n\t\tif (matcher.matches()) {\n\t\t\treturn Integer.parseInt(matcher.group(1), 16);\n\t\t}\n\t\treturn -1;\n\t}\n\n\tprivate void raiseExceptionForErrorCode(int code, NamingException exception) {\n\t\tString hexString = Integer.toHexString(code);\n\t\tThrowable cause = new ActiveDirectoryAuthenticationException(hexString, exception.getMessage(), exception);\n\t\tswitch (code) {\n\t\t\tcase PASSWORD_EXPIRED -> throw new CredentialsExpiredException(this.messages\n\t\t\t\t.getMessage(\"LdapAuthenticationProvider.credentialsExpired\", \"User credentials have expired\"), cause);\n\t\t\tcase ACCOUNT_DISABLED -> throw new DisabledException(\n\t\t\t\t\tthis.messages.getMessage(\"LdapAuthenticationProvider.disabled\", \"User is disabled\"), cause);\n\t\t\tcase ACCOUNT_EXPIRED -> throw new AccountExpiredException(\n\t\t\t\t\tthis.messages.getMessage(\"LdapAuthenticationProvider.expired\", \"User account has expired\"), cause);\n\t\t\tcase ACCOUNT_LOCKED -> throw new LockedException(\n\t\t\t\t\tthis.messages.getMessage(\"LdapAuthenticationProvider.locked\", \"User account is locked\"), cause);\n\t\t\tdefault -> throw badCredentials(cause);\n\t\t}\n\t}\n\n\tprivate String subCodeToLogMessage(int code) {\n\t\treturn switch (code) {\n\t\t\tcase USERNAME_NOT_FOUND -> \"User was not found in directory\";\n\t\t\tcase INVALID_PASSWORD -> \"Supplied password was invalid\";\n\t\t\tcase NOT_PERMITTED -> \"User not permitted to logon at this time\";\n\t\t\tcase PASSWORD_EXPIRED -> \"Password has expired\";\n\t\t\tcase ACCOUNT_DISABLED -> \"Account is disabled\";\n\t\t\tcase ACCOUNT_EXPIRED -> \"Account expired\";\n\t\t\tcase PASSWORD_NEEDS_RESET -> \"User must reset password\";\n\t\t\tcase ACCOUNT_LOCKED -> \"Account locked\";\n\t\t\tdefault -> \"Unknown (error code \" + Integer.toHexString(code) + \")\";\n\t\t};\n\t}\n\n\tprivate BadCredentialsException badCredentials() {\n\t\treturn new BadCredentialsException(\n\t\t\t\tthis.messages.getMessage(\"LdapAuthenticationProvider.badCredentials\", \"Bad credentials\"));\n\t}\n\n\tprivate BadCredentialsException badCredentials(Throwable cause) {\n\t\treturn (BadCredentialsException) badCredentials().initCause(cause);\n\t}\n\n\tprivate InternalAuthenticationServiceException badLdapConnection(Throwable cause) {\n\t\treturn new InternalAuthenticationServiceException(this.messages\n\t\t\t.getMessage(\"LdapAuthenticationProvider.badLdapConnection\", \"Connection to LDAP server failed.\"), cause);\n\t}\n\n\tprivate DirContextOperations searchForUser(DirContext context, String username) throws NamingException {\n\t\tSearchControls searchControls = new SearchControls();\n\t\tsearchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);\n\t\tString bindPrincipal = createBindPrincipal(username);\n\t\tString searchRoot = (this.rootDn != null) ? this.rootDn : searchRootFromPrincipal(bindPrincipal);\n\t\tSingleContextSource contextSource = new SingleContextSource(context);\n\t\tLdapClient ldapClient = LdapClient.builder()\n\t\t\t.contextSource(contextSource)\n\t\t\t.defaultSearchControls(() -> searchControls)\n\t\t\t.ignorePartialResultException(true)\n\t\t\t.build();\n\t\ttry {\n\t\t\tLdapQuery query = LdapQueryBuilder.query()\n\t\t\t\t.base(searchRoot)\n\t\t\t\t.searchScope(SearchScope.SUBTREE)\n\t\t\t\t.filter(this.searchFilter, bindPrincipal, username);\n\t\t\tDirContextOperations result = ldapClient.search().query(query).toEntry();\n\t\t\tif (result == null) {\n\t\t\t\tthrow new IncorrectResultSizeDataAccessException(1, 0);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\tcatch (CommunicationException ex) {\n\t\t\tthrow badLdapConnection(ex);\n\t\t}\n\t\tcatch (IncorrectResultSizeDataAccessException ex) {\n\t\t\t// Search should never return multiple results if properly configured -\n\t\t\tif (ex.getActualSize() != 0) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\t// If we found no results, then the username/password did not match\n\t\t\tUsernameNotFoundException userNameNotFoundException = UsernameNotFoundException.fromUsername(username, ex);\n\t\t\tthrow badCredentials(userNameNotFoundException);\n\t\t}\n\t\tcatch (org.springframework.ldap.NamingException ex) {\n\t\t\tif (ex.getCause() instanceof NamingException original) {\n\t\t\t\tthrow original;\n\t\t\t}\n\t\t\tthrow badCredentials(ex);\n\t\t}\n\t}\n\n\tprivate String searchRootFromPrincipal(String bindPrincipal) {\n\t\tint atChar = bindPrincipal.lastIndexOf('@');\n\t\tif (atChar < 0) {\n\t\t\tthis.logger.debug(\"User principal '\" + bindPrincipal\n\t\t\t\t\t+ \"' does not contain the domain, and no domain has been configured\");\n\t\t\tthrow badCredentials();\n\t\t}\n\t\treturn rootDnFromDomain(bindPrincipal.substring(atChar + 1));\n\t}\n\n\tprivate String rootDnFromDomain(String domain) {\n\t\tString[] tokens = StringUtils.tokenizeToStringArray(domain, \".\");\n\t\tStringBuilder root = new StringBuilder();\n\t\tfor (String token : tokens) {\n\t\t\tif (!root.isEmpty()) {\n\t\t\t\troot.append(',');\n\t\t\t}\n\t\t\troot.append(\"dc=\").append(token);\n\t\t}\n\t\treturn root.toString();\n\t}\n\n\tString createBindPrincipal(String username) {\n\t\tif (this.domain == null || username.toLowerCase(Locale.ROOT).endsWith(this.domain)) {\n\t\t\treturn username;\n\t\t}\n\t\treturn username + \"@\" + this.domain;\n\t}\n\n\t/**\n\t * By default, a failed authentication (LDAP error 49) will result in a\n\t * {@code BadCredentialsException}.\n\t * <p>\n\t * If this property is set to {@code true}, the exception message from a failed bind\n\t * attempt will be parsed for the AD-specific error code and a\n\t * {@link CredentialsExpiredException}, {@link DisabledException},\n\t * {@link AccountExpiredException} or {@link LockedException} will be thrown for the\n\t * corresponding codes. All other codes will result in the default\n\t * {@code BadCredentialsException}.\n\t * @param convertSubErrorCodesToExceptions {@code true} to raise an exception based on\n\t * the AD error code.\n\t */\n\tpublic void setConvertSubErrorCodesToExceptions(boolean convertSubErrorCodesToExceptions) {\n\t\tthis.convertSubErrorCodesToExceptions = convertSubErrorCodesToExceptions;\n\t}\n\n\t/**\n\t * The LDAP filter string to search for the user being authenticated. Occurrences of\n\t * {0} are replaced with the {@code username@domain}. Occurrences of {1} are replaced\n\t * with the {@code username} only.\n\t * <p>\n\t * Defaults to: {@code (&(objectClass=user)(userPrincipalName={0}))}\n\t * </p>\n\t * @param searchFilter the filter string\n\t * @since 3.2.6\n\t */\n\tpublic void setSearchFilter(String searchFilter) {\n\t\tAssert.hasText(searchFilter, \"searchFilter must have text\");\n\t\tthis.searchFilter = searchFilter;\n\t}\n\n\t/**\n\t * Allows a custom environment properties to be used to create initial LDAP context.\n\t * @param environment the additional environment parameters to use when creating the\n\t * LDAP Context\n\t */\n\tpublic void setContextEnvironmentProperties(Map<String, Object> environment) {\n\t\tAssert.notEmpty(environment, \"environment must not be empty\");\n\t\tthis.contextEnvironmentProperties = new Hashtable<>(environment);\n\t}\n\n\t/**\n\t * Set the strategy for obtaining the authorities for a given user after they've been\n\t * authenticated. Consider adjusting this if you require a custom authorities mapping\n\t * algorithm different from a default one. The default value is\n\t * DefaultActiveDirectoryAuthoritiesPopulator.\n\t * @param authoritiesPopulator authorities population strategy\n\t * @since 6.3\n\t */\n\tpublic void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) {\n\t\tAssert.notNull(authoritiesPopulator, \"authoritiesPopulator must not be null\");\n\t\tthis.authoritiesPopulator = authoritiesPopulator;\n\t}\n\n\tstatic class ContextFactory {\n\n\t\tDirContext createContext(Hashtable<?, ?> env) throws NamingException {\n\t\t\treturn new InitialLdapContext(env, null);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/ad/DefaultActiveDirectoryAuthoritiesPopulator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication.ad;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport javax.naming.ldap.LdapName;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\n\n/**\n * The default strategy for obtaining user role information from the active directory.\n * Creates the user authority list from the values of the {@code memberOf} attribute\n * obtained from the user's Active Directory entry.\n *\n * @author Luke Taylor\n * @author Roman Zabaluev\n * @since 6.3\n */\npublic final class DefaultActiveDirectoryAuthoritiesPopulator implements LdapAuthoritiesPopulator {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData,\n\t\t\tString username) {\n\t\tString[] groups = userData.getStringAttributes(\"memberOf\");\n\t\tif (groups == null) {\n\t\t\tthis.logger.debug(\"No values for 'memberOf' attribute.\");\n\t\t\treturn AuthorityUtils.NO_AUTHORITIES;\n\t\t}\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(\"'memberOf' attribute values: \" + Arrays.asList(groups));\n\t\t}\n\n\t\tList<GrantedAuthority> authorities = new ArrayList<>(groups.length);\n\n\t\tfor (String group : groups) {\n\t\t\tLdapName name = LdapNameBuilder.newInstance(group).build();\n\t\t\tString authority = name.getRdn(name.size() - 1).getValue().toString();\n\t\t\tauthorities.add(new SimpleGrantedAuthority(authority));\n\t\t}\n\n\t\treturn authorities;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/ad/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Package for ActiveDirectory support\n */\n@NullMarked\npackage org.springframework.security.ldap.authentication.ad;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The LDAP authentication provider package. Interfaces are provided for both\n * authentication and retrieval of user roles from an LDAP server.\n * <p>\n * The main provider class is <tt>LdapAuthenticationProvider</tt>. This is configured with\n * an <tt>LdapAuthenticator</tt> instance and an <tt>LdapAuthoritiesPopulator</tt>. The\n * latter is used to obtain the list of roles for the user.\n */\n@NullMarked\npackage org.springframework.security.ldap.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson/InetOrgPersonMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.ldap.userdetails.InetOrgPerson;\n\n/**\n * This Jackson mixin is used to serialize/deserialize {@link InetOrgPerson}.\n *\n * @author Sebastien Deleuze\n * @since 7.0\n * @see LdapJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class InetOrgPersonMixin {\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson/LdapAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.ldap.userdetails.LdapAuthority;\n\n/**\n * This Jackson mixin is used to serialize/deserialize {@link LdapAuthority}.\n *\n * @author Sebastien Deleuze\n * @since 7.0\n * @see LdapJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class LdapAuthorityMixin {\n\n\t@JsonCreator\n\tLdapAuthorityMixin(@JsonProperty(\"role\") String role, @JsonProperty(\"dn\") String dn,\n\t\t\t@JsonProperty(\"attributes\") Map<String, List<String>> attributes) {\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson/LdapJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.ldap.userdetails.InetOrgPerson;\nimport org.springframework.security.ldap.userdetails.LdapAuthority;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;\nimport org.springframework.security.ldap.userdetails.Person;\n\n/**\n * Jackson module for {@code spring-security-ldap}. This module registers\n * {@link LdapAuthorityMixin}, {@link LdapUserDetailsImplMixin}, {@link PersonMixin},\n * {@link InetOrgPersonMixin}.\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @since 7.0\n * @see SecurityJacksonModules\n */\n@SuppressWarnings(\"serial\")\npublic class LdapJacksonModule extends SecurityJacksonModule {\n\n\tpublic LdapJacksonModule() {\n\t\tsuper(LdapJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(InetOrgPerson.class)\n\t\t\t.allowIfSubType(LdapUserDetailsImpl.class)\n\t\t\t.allowIfSubType(Person.class);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(LdapAuthority.class, LdapAuthorityMixin.class);\n\t\tcontext.setMixIn(LdapUserDetailsImpl.class, LdapUserDetailsImplMixin.class);\n\t\tcontext.setMixIn(Person.class, PersonMixin.class);\n\t\tcontext.setMixIn(InetOrgPerson.class, InetOrgPersonMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson/LdapUserDetailsImplMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;\n\n/**\n * This Jackson mixin is used to serialize/deserialize {@link LdapUserDetailsImpl}.\n *\n * @author Sebastien Deleuze\n * @since 7.0\n * @see LdapJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class LdapUserDetailsImplMixin {\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson/PersonMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.ldap.userdetails.Person;\n\n/**\n * This Jackson mixin is used to serialize/deserialize {@link Person}.\n *\n * @author Sebastien Deleuze\n * @since 7.0\n * @see LdapJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class PersonMixin {\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 3+ serialization support for LDAP.\n */\n@NullMarked\npackage org.springframework.security.ldap.jackson;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.ldap.userdetails.InetOrgPerson;\n\n/**\n * This Jackson mixin is used to serialize/deserialize {@link InetOrgPerson}.\n *\n * @since 5.7\n * @see LdapJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.ldap.jackson.InetOrgPersonMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class InetOrgPersonMixin {\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson2;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.ldap.userdetails.LdapAuthority;\n\n/**\n * This Jackson mixin is used to serialize/deserialize {@link LdapAuthority}.\n *\n * @since 5.7\n * @see LdapJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.ldap.jackson.LdapAuthorityMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class LdapAuthorityMixin {\n\n\t@JsonCreator\n\tLdapAuthorityMixin(@JsonProperty(\"role\") String role, @JsonProperty(\"dn\") String dn,\n\t\t\t@JsonProperty(\"attributes\") Map<String, List<String>> attributes) {\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson2;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.ldap.userdetails.InetOrgPerson;\nimport org.springframework.security.ldap.userdetails.LdapAuthority;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;\nimport org.springframework.security.ldap.userdetails.Person;\n\n/**\n * Jackson module for {@code spring-security-ldap}. This module registers\n * {@link LdapAuthorityMixin}, {@link LdapUserDetailsImplMixin}, {@link PersonMixin},\n * {@link InetOrgPersonMixin}.\n *\n * <p>\n * If not already enabled, default typing will be automatically enabled as type info is\n * required to properly serialize/deserialize objects. In order to use this module just\n * add it to your {@code ObjectMapper} configuration.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new LdapJackson2Module());\n * </pre>\n *\n * <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list of all\n * security modules.</b>\n *\n * @since 5.7\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.ldap.jackson.LdapJacksonModule} based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings({ \"serial\", \"removal\" })\npublic class LdapJackson2Module extends SimpleModule {\n\n\tpublic LdapJackson2Module() {\n\t\tsuper(LdapJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tSecurityJackson2Modules.enableDefaultTyping(context.getOwner());\n\t\tcontext.setMixInAnnotations(LdapAuthority.class, LdapAuthorityMixin.class);\n\t\tcontext.setMixInAnnotations(LdapUserDetailsImpl.class, LdapUserDetailsImplMixin.class);\n\t\tcontext.setMixInAnnotations(Person.class, PersonMixin.class);\n\t\tcontext.setMixInAnnotations(InetOrgPerson.class, InetOrgPersonMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;\n\n/**\n * This Jackson mixin is used to serialize/deserialize {@link LdapUserDetailsImpl}.\n *\n * @since 5.7\n * @see LdapJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.ldap.jackson.LdapUserDetailsImplMixin} based on\n * Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class LdapUserDetailsImplMixin {\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson2/PersonMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.ldap.userdetails.Person;\n\n/**\n * This Jackson mixin is used to serialize/deserialize {@link Person}.\n *\n * @since 5.7\n * @see LdapJackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.ldap.jackson.PersonMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class PersonMixin {\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/jackson2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 2 serialization support for LDAP.\n */\n@NullMarked\npackage org.springframework.security.ldap.jackson2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security's LDAP module.\n */\n@NullMarked\npackage org.springframework.security.ldap;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyAwareContextSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport java.util.Hashtable;\n\nimport javax.naming.Context;\nimport javax.naming.directory.DirContext;\nimport javax.naming.ldap.Control;\nimport javax.naming.ldap.LdapContext;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.ldap.support.LdapUtils;\nimport org.springframework.security.ldap.DefaultSpringSecurityContextSource;\n\n/**\n * Extended version of the <tt>DefaultSpringSecurityContextSource</tt> which adds support\n * for the use of {@link PasswordPolicyControl} to make use of user account data stored in\n * the directory.\n * <p>\n * When binding with specific username (not the <tt>userDn</tt>) property it will connect\n * first as the userDn, then reconnect as the user in order to retrieve any\n * password-policy control sent with the response, even if an exception occurs.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class PasswordPolicyAwareContextSource extends DefaultSpringSecurityContextSource {\n\n\tpublic PasswordPolicyAwareContextSource(String providerUrl) {\n\t\tsuper(providerUrl);\n\t}\n\n\t@Override\n\tpublic DirContext getContext(String principal, String credentials) throws PasswordPolicyException {\n\t\tif (principal.equals(getUserDn())) {\n\t\t\treturn super.getContext(principal, credentials);\n\t\t}\n\t\tthis.logger.trace(LogMessage.format(\"Binding as %s, prior to reconnect as user %s\", getUserDn(), principal));\n\t\t// First bind as manager user before rebinding as the specific principal.\n\t\tLdapContext ctx = (LdapContext) super.getContext(getUserDn(), getPassword());\n\t\tControl[] rctls = { new PasswordPolicyControl(false) };\n\t\ttry {\n\t\t\tctx.addToEnvironment(Context.SECURITY_PRINCIPAL, principal);\n\t\t\tctx.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials);\n\t\t\tctx.reconnect(rctls);\n\t\t}\n\t\tcatch (javax.naming.NamingException ex) {\n\t\t\tPasswordPolicyResponseControl ctrl = PasswordPolicyControlExtractor.extractControl(ctx);\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Failed to bind with %s\", ctrl), ex);\n\t\t\t}\n\t\t\tLdapUtils.closeContext(ctx);\n\t\t\tif (ctrl != null && ctrl.isLocked() && ctrl.getErrorStatus() != null) {\n\t\t\t\tthrow new PasswordPolicyException(ctrl.getErrorStatus());\n\t\t\t}\n\t\t\tthrow LdapUtils.convertLdapException(ex);\n\t\t}\n\t\tthis.logger.debug(LogMessage.of(() -> \"Bound with \" + PasswordPolicyControlExtractor.extractControl(ctx)));\n\t\treturn ctx;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected Hashtable getAuthenticatedEnv(String principal, String credentials) {\n\t\tHashtable<String, Object> env = super.getAuthenticatedEnv(principal, credentials);\n\t\tenv.put(LdapContext.CONTROL_FACTORIES, PasswordPolicyControlFactory.class.getName());\n\t\treturn env;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyControl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport java.io.Serial;\n\nimport javax.naming.ldap.Control;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n *\n * A Password Policy request control.\n * <p>\n * Based on the information in the corresponding <a href=\n * \"https://tools.ietf.org/draft/draft-behera-ldap-password-policy/draft-behera-ldap-password-policy-09.txt\"\n * > internet draft on LDAP password policy</a>\n *\n * @author Stefan Zoerner\n * @author Luke Taylor\n * @see PasswordPolicyResponseControl\n */\npublic class PasswordPolicyControl implements Control {\n\n\t/**\n\t * OID of the Password Policy Control\n\t */\n\tpublic static final String OID = \"1.3.6.1.4.1.42.2.27.8.5.1\";\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2843242715616817932L;\n\n\tprivate final boolean critical;\n\n\t/**\n\t * Creates a non-critical (request) control.\n\t */\n\tpublic PasswordPolicyControl() {\n\t\tthis(Control.NONCRITICAL);\n\t}\n\n\t/**\n\t * Creates a (request) control.\n\t * @param critical indicates whether the control is critical for the client\n\t */\n\tpublic PasswordPolicyControl(boolean critical) {\n\t\tthis.critical = critical;\n\t}\n\n\t/**\n\t * Retrieves the ASN.1 BER encoded value of the LDAP control. The request value for\n\t * this control is always empty.\n\t * @return always null\n\t */\n\t@Override\n\tpublic byte @Nullable [] getEncodedValue() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the OID of the Password Policy Control (\"1.3.6.1.4.1.42.2.27.8.5.1\").\n\t */\n\t@Override\n\tpublic String getID() {\n\t\treturn OID;\n\t}\n\n\t/**\n\t * Returns whether the control is critical for the client.\n\t */\n\t@Override\n\tpublic boolean isCritical() {\n\t\treturn this.critical;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyControlExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport javax.naming.directory.DirContext;\nimport javax.naming.ldap.Control;\nimport javax.naming.ldap.LdapContext;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Obtains the <tt>PasswordPolicyControl</tt> from a context for use by other classes.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic final class PasswordPolicyControlExtractor {\n\n\tprivate static final Log logger = LogFactory.getLog(PasswordPolicyControlExtractor.class);\n\n\tprivate PasswordPolicyControlExtractor() {\n\t}\n\n\tpublic static @Nullable PasswordPolicyResponseControl extractControl(DirContext dirCtx) {\n\t\tLdapContext ctx = (LdapContext) dirCtx;\n\t\tControl[] ctrls = null;\n\t\ttry {\n\t\t\tctrls = ctx.getResponseControls();\n\t\t}\n\t\tcatch (javax.naming.NamingException ex) {\n\t\t\tlogger.trace(\"Failed to obtain response controls\", ex);\n\t\t}\n\t\tfor (int i = 0; ctrls != null && i < ctrls.length; i++) {\n\t\t\tif (ctrls[i] instanceof PasswordPolicyResponseControl) {\n\t\t\t\treturn (PasswordPolicyResponseControl) ctrls[i];\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyControlFactory.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport javax.naming.ldap.Control;\nimport javax.naming.ldap.ControlFactory;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Transforms a control object to a PasswordPolicyResponseControl object, if appropriate.\n *\n * @author Stefan Zoerner\n * @author Luke Taylor\n */\npublic class PasswordPolicyControlFactory extends ControlFactory {\n\n\t/**\n\t * Creates an instance of PasswordPolicyResponseControl if the passed control is a\n\t * response control of this type. Attributes of the result are filled with the correct\n\t * values (e.g. error code).\n\t * @param ctl the control the check\n\t * @return a response control of type PasswordPolicyResponseControl, or null\n\t */\n\t@Override\n\tpublic @Nullable Control getControlInstance(Control ctl) {\n\t\tif (ctl.getID().equals(PasswordPolicyControl.OID)) {\n\t\t\treturn new PasswordPolicyResponseControl(ctl.getEncodedValue());\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyData.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface PasswordPolicyData {\n\n\tint getTimeBeforeExpiration();\n\n\tint getGraceLoginsRemaining();\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyErrorStatus.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\n/**\n * Defines status codes for use with <tt>PasswordPolicyException</tt>, with error codes\n * (for message source lookup) and default messages.\n *\n * <pre>\n *    PasswordPolicyResponseValue ::= SEQUENCE {\n *        warning [0] CHOICE {\n *           timeBeforeExpiration [0] INTEGER (0 .. maxInt),\n *           graceAuthNsRemaining [1] INTEGER (0 .. maxInt)\n *        } OPTIONAL,\n *        error   [1] ENUMERATED {\n *           passwordExpired             (0),     accountLocked               (1),\n *           changeAfterReset            (2),     passwordModNotAllowed       (3),\n *           mustSupplyOldPassword       (4),     insufficientPasswordQuality (5),\n *           passwordTooShort            (6),     passwordTooYoung            (7),\n *           passwordInHistory           (8)\n *        } OPTIONAL\n *    }\n * </pre>\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic enum PasswordPolicyErrorStatus {\n\n\tPASSWORD_EXPIRED(\"ppolicy.expired\", \"Your password has expired\"),\n\n\tACCOUNT_LOCKED(\"ppolicy.locked\", \"Account is locked\"),\n\n\tCHANGE_AFTER_RESET(\"ppolicy.change.after.reset\", \"Your password must be changed after being reset\"),\n\n\tPASSWORD_MOD_NOT_ALLOWED(\"ppolicy.mod.not.allowed\", \"Password cannot be changed\"),\n\n\tMUST_SUPPLY_OLD_PASSWORD(\"ppolicy.must.supply.old.password\", \"The old password must be supplied\"),\n\n\tINSUFFICIENT_PASSWORD_QUALITY(\"ppolicy.insufficient.password.quality\",\n\t\t\t\"The supplied password is of insufficient quality\"),\n\n\tPASSWORD_TOO_SHORT(\"ppolicy.password.too.short\", \"The supplied password is too short\"),\n\n\tPASSWORD_TOO_YOUNG(\"ppolicy.password.too.young\", \"Your password was changed too recently to be changed again\"),\n\n\tPASSWORD_IN_HISTORY(\"ppolicy.password.in.history\", \"The supplied password has already been used\");\n\n\tprivate final String errorCode;\n\n\tprivate final String defaultMessage;\n\n\tPasswordPolicyErrorStatus(String errorCode, String defaultMessage) {\n\t\tthis.errorCode = errorCode;\n\t\tthis.defaultMessage = defaultMessage;\n\t}\n\n\tpublic String getErrorCode() {\n\t\treturn this.errorCode;\n\t}\n\n\tpublic String getDefaultMessage() {\n\t\treturn this.defaultMessage;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport java.io.Serial;\n\n/**\n * Generic exception raised by the ppolicy package.\n * <p>\n * The <tt>status</tt> property should be checked for more detail on the cause of the\n * exception.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class PasswordPolicyException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2586535034047453106L;\n\n\tprivate final PasswordPolicyErrorStatus status;\n\n\tpublic PasswordPolicyException(PasswordPolicyErrorStatus status) {\n\t\tsuper(status.getDefaultMessage());\n\t\tthis.status = status;\n\t}\n\n\tpublic PasswordPolicyErrorStatus getStatus() {\n\t\treturn this.status;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/PasswordPolicyResponseControl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.Serial;\n\nimport netscape.ldap.ber.stream.BERChoice;\nimport netscape.ldap.ber.stream.BERElement;\nimport netscape.ldap.ber.stream.BEREnumerated;\nimport netscape.ldap.ber.stream.BERInteger;\nimport netscape.ldap.ber.stream.BERIntegral;\nimport netscape.ldap.ber.stream.BERSequence;\nimport netscape.ldap.ber.stream.BERTag;\nimport netscape.ldap.ber.stream.BERTagDecoder;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.dao.DataRetrievalFailureException;\n\n/**\n * Represents the response control received when a <tt>PasswordPolicyControl</tt> is used\n * when binding to a directory. Currently tested with the OpenLDAP 2.3.19 implementation\n * of the LDAP Password Policy Draft. It extends the request control with the control\n * specific data. This is accomplished by the properties <tt>timeBeforeExpiration</tt>,\n * <tt>graceLoginsRemaining</tt>.\n * <p>\n *\n * @author Stefan Zoerner\n * @author Luke Taylor\n * @see org.springframework.security.ldap.ppolicy.PasswordPolicyControl\n * @see <a href=\n * \"https://www.ibm.com/developerworks/tivoli/library/t-ldap-controls/\">Stefan Zoerner's\n * IBM developerworks article on LDAP controls.</a>\n */\npublic class PasswordPolicyResponseControl extends PasswordPolicyControl {\n\n\tprivate static final Log logger = LogFactory.getLog(PasswordPolicyResponseControl.class);\n\n\t@Serial\n\tprivate static final long serialVersionUID = -4592657167939234499L;\n\n\tprivate final byte[] encodedValue;\n\n\tprivate @Nullable PasswordPolicyErrorStatus errorStatus;\n\n\tprivate int graceLoginsRemaining = Integer.MAX_VALUE;\n\n\tprivate int timeBeforeExpiration = Integer.MAX_VALUE;\n\n\t/**\n\t * Decodes the Ber encoded control data. The ASN.1 value of the control data is:\n\t *\n\t * <pre>\n\t *    PasswordPolicyResponseValue ::= SEQUENCE {       warning [0] CHOICE {\n\t *           timeBeforeExpiration [0] INTEGER (0 .. maxInt),\n\t *           graceAuthNsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL,       error   [1] ENUMERATED {\n\t *           passwordExpired             (0),          accountLocked               (1),\n\t *           changeAfterReset            (2),          passwordModNotAllowed       (3),\n\t *           mustSupplyOldPassword       (4),          insufficientPasswordQuality (5),\n\t *           passwordTooShort            (6),          passwordTooYoung            (7),\n\t *           passwordInHistory           (8) } OPTIONAL }\n\t * </pre>\n\t *\n\t */\n\tpublic PasswordPolicyResponseControl(byte[] encodedValue) {\n\t\tthis.encodedValue = encodedValue;\n\t\tPPolicyDecoder decoder = new NetscapeDecoder();\n\t\ttry {\n\t\t\tdecoder.decode();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new DataRetrievalFailureException(\"Failed to parse control value\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the unchanged value of the response control. Returns the unchanged value of\n\t * the response control as byte array.\n\t */\n\t@Override\n\tpublic byte[] getEncodedValue() {\n\t\treturn this.encodedValue;\n\t}\n\n\tpublic @Nullable PasswordPolicyErrorStatus getErrorStatus() {\n\t\treturn this.errorStatus;\n\t}\n\n\t/**\n\t * Returns the graceLoginsRemaining.\n\t * @return Returns the graceLoginsRemaining.\n\t */\n\tpublic int getGraceLoginsRemaining() {\n\t\treturn this.graceLoginsRemaining;\n\t}\n\n\t/**\n\t * Returns the timeBeforeExpiration.\n\t * @return Returns the time before expiration in seconds\n\t */\n\tpublic int getTimeBeforeExpiration() {\n\t\treturn this.timeBeforeExpiration;\n\t}\n\n\t/**\n\t * Checks whether an error is present.\n\t * @return true, if an error is present\n\t */\n\tpublic boolean hasError() {\n\t\treturn this.errorStatus != null;\n\t}\n\n\t/**\n\t * Checks whether a warning is present.\n\t * @return true, if a warning is present\n\t */\n\tpublic boolean hasWarning() {\n\t\treturn (this.graceLoginsRemaining != Integer.MAX_VALUE) || (this.timeBeforeExpiration != Integer.MAX_VALUE);\n\t}\n\n\tpublic boolean isExpired() {\n\t\treturn this.errorStatus == PasswordPolicyErrorStatus.PASSWORD_EXPIRED;\n\t}\n\n\tpublic boolean isChangeAfterReset() {\n\t\treturn this.errorStatus == PasswordPolicyErrorStatus.CHANGE_AFTER_RESET;\n\t}\n\n\tpublic boolean isUsingGraceLogins() {\n\t\treturn this.graceLoginsRemaining < Integer.MAX_VALUE;\n\t}\n\n\t/**\n\t * Determines whether an account locked error has been returned.\n\t * @return true if the account is locked.\n\t */\n\tpublic boolean isLocked() {\n\t\treturn this.errorStatus == PasswordPolicyErrorStatus.ACCOUNT_LOCKED;\n\t}\n\n\t/**\n\t * Create a textual representation containing error and warning messages, if any are\n\t * present.\n\t * @return error and warning messages\n\t */\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(getClass().getSimpleName()).append(\" [\");\n\t\tif (hasError() && this.errorStatus != null) {\n\t\t\tsb.append(\"error=\").append(this.errorStatus.getDefaultMessage()).append(\"; \");\n\t\t}\n\t\tif (this.graceLoginsRemaining != Integer.MAX_VALUE) {\n\t\t\tsb.append(\"warning=\").append(this.graceLoginsRemaining).append(\" grace logins remain; \");\n\t\t}\n\t\tif (this.timeBeforeExpiration != Integer.MAX_VALUE) {\n\t\t\tsb.append(\"warning=time before expiration is \").append(this.timeBeforeExpiration).append(\"; \");\n\t\t}\n\t\tif (!hasError() && !hasWarning()) {\n\t\t\tsb.append(\"(no error, no warning)\");\n\t\t}\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n\tprivate interface PPolicyDecoder {\n\n\t\tvoid decode() throws IOException;\n\n\t}\n\n\t/**\n\t * Decoder based on Netscape ldapsdk library\n\t */\n\tprivate class NetscapeDecoder implements PPolicyDecoder {\n\n\t\t@Override\n\t\tpublic void decode() throws IOException {\n\t\t\tint[] bread = { 0 };\n\t\t\tBERSequence seq = (BERSequence) BERElement.getElement(new SpecificTagDecoder(),\n\t\t\t\t\tnew ByteArrayInputStream(PasswordPolicyResponseControl.this.encodedValue), bread);\n\t\t\tint size = seq.size();\n\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\tlogger.debug(LogMessage.format(\"Received PasswordPolicyResponse whose ASN.1 sequence has %d elements\",\n\t\t\t\t\t\tsize));\n\t\t\t}\n\t\t\tfor (int i = 0; i < seq.size(); i++) {\n\t\t\t\tBERTag elt = (BERTag) seq.elementAt(i);\n\t\t\t\tint tag = elt.getTag() & 0x1F;\n\t\t\t\tif (tag == 0) {\n\t\t\t\t\tBERChoice warning = (BERChoice) elt.getValue();\n\t\t\t\t\tBERTag content = (BERTag) warning.getValue();\n\t\t\t\t\tint value = ((BERInteger) content.getValue()).getValue();\n\t\t\t\t\tif ((content.getTag() & 0x1F) == 0) {\n\t\t\t\t\t\tPasswordPolicyResponseControl.this.timeBeforeExpiration = value;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tPasswordPolicyResponseControl.this.graceLoginsRemaining = value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (tag == 1) {\n\t\t\t\t\tBERIntegral error = (BERIntegral) elt.getValue();\n\t\t\t\t\tPasswordPolicyResponseControl.this.errorStatus = PasswordPolicyErrorStatus.values()[error\n\t\t\t\t\t\t.getValue()];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstatic class SpecificTagDecoder extends BERTagDecoder {\n\n\t\t\t/** Allows us to remember which of the two options we're decoding */\n\t\t\tprivate @Nullable Boolean inChoice = null;\n\n\t\t\t@Override\n\t\t\tpublic BERElement getElement(BERTagDecoder decoder, int tag, InputStream stream, int[] bytesRead,\n\t\t\t\t\tboolean[] implicit) throws IOException {\n\t\t\t\ttag &= 0x1F;\n\t\t\t\timplicit[0] = false;\n\t\t\t\tif (tag == 0) {\n\t\t\t\t\t// Either the choice or the time before expiry within it\n\t\t\t\t\tif (this.inChoice == null) {\n\t\t\t\t\t\tsetInChoice(true);\n\t\t\t\t\t\t// Read the choice length from the stream (ignored)\n\t\t\t\t\t\tBERElement.readLengthOctets(stream, bytesRead);\n\t\t\t\t\t\tint[] componentLength = new int[1];\n\t\t\t\t\t\tBERElement choice = new BERChoice(decoder, stream, componentLength);\n\t\t\t\t\t\tbytesRead[0] += componentLength[0];\n\t\t\t\t\t\t// inChoice = null;\n\t\t\t\t\t\treturn choice;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// Must be time before expiry\n\t\t\t\t\t\treturn new BERInteger(stream, bytesRead);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (tag == 1) {\n\t\t\t\t\t// Either the graceLogins or the error enumeration.\n\t\t\t\t\tif (this.inChoice == null) {\n\t\t\t\t\t\t// The enumeration\n\t\t\t\t\t\tsetInChoice(false);\n\t\t\t\t\t\treturn new BEREnumerated(stream, bytesRead);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (this.inChoice) {\n\t\t\t\t\t\t\t// graceLogins\n\t\t\t\t\t\t\treturn new BERInteger(stream, bytesRead);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow new DataRetrievalFailureException(\"Unexpected tag \" + tag);\n\t\t\t}\n\n\t\t\tprivate void setInChoice(boolean inChoice) {\n\t\t\t\tthis.inChoice = inChoice;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/ppolicy/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Implementation of password policy functionality based on the <a href=\n * \"https://tools.ietf.org/draft/draft-behera-ldap-password-policy/draft-behera-ldap-password-policy-09.txt\">\n * Password Policy for LDAP Directories</a>.\n * <p>\n * This code will not work with servers such as Active Directory, which do not implement\n * this standard.\n */\n@NullMarked\npackage org.springframework.security.ldap.ppolicy;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearch.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.search;\n\nimport javax.naming.directory.SearchControls;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\nimport org.springframework.util.Assert;\n\n/**\n * LdapUserSearch implementation which uses an Ldap filter to locate the user.\n *\n * @author Robert Sanders\n * @author Luke Taylor\n * @see SearchControls\n */\npublic class FilterBasedLdapUserSearch implements LdapUserSearch {\n\n\tprivate static final Log logger = LogFactory.getLog(FilterBasedLdapUserSearch.class);\n\n\tprivate final ContextSource contextSource;\n\n\t/**\n\t * The LDAP SearchControls object used for the search. Shared between searches so\n\t * shouldn't be modified once the bean has been configured.\n\t */\n\tprivate final SearchControls searchControls = new SearchControls();\n\n\t/**\n\t * Context name to search in, relative to the base of the configured ContextSource.\n\t */\n\tprivate final String searchBase;\n\n\t/**\n\t * The filter expression used in the user search. This is an LDAP search filter (as\n\t * defined in 'RFC 2254') with optional arguments. See the documentation for the\n\t * <tt>search</tt> methods in {@link javax.naming.directory.DirContext DirContext} for\n\t * more information.\n\t *\n\t * <p>\n\t * In this case, the username is the only parameter.\n\t * </p>\n\t * Possible examples are:\n\t * <ul>\n\t * <li>(uid={0}) - this would search for a username match on the uid attribute.</li>\n\t * </ul>\n\t */\n\tprivate final String searchFilter;\n\n\tpublic FilterBasedLdapUserSearch(String searchBase, String searchFilter, BaseLdapPathContextSource contextSource) {\n\t\tAssert.notNull(contextSource, \"contextSource must not be null\");\n\t\tAssert.notNull(searchFilter, \"searchFilter must not be null.\");\n\t\tAssert.notNull(searchBase, \"searchBase must not be null (an empty string is acceptable).\");\n\t\tthis.searchFilter = searchFilter;\n\t\tthis.contextSource = contextSource;\n\t\tthis.searchBase = searchBase;\n\t\tsetSearchSubtree(true);\n\t\tif (searchBase.isEmpty()) {\n\t\t\tlogger.info(LogMessage.format(\"Searches will be performed from the root %s since SearchBase not set\",\n\t\t\t\t\tcontextSource.getBaseLdapName()));\n\t\t}\n\t}\n\n\t/**\n\t * Return the LdapUserDetails containing the user's information\n\t * @param username the username to search for.\n\t * @return An LdapUserDetails object containing the details of the located user's\n\t * directory entry\n\t * @throws UsernameNotFoundException if no matching entry is found.\n\t */\n\t@Override\n\tpublic DirContextOperations searchForUser(String username) {\n\t\tlogger.trace(LogMessage.of(() -> \"Searching for user '\" + username + \"', with \" + this));\n\t\tSpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(this.contextSource);\n\t\ttemplate.setSearchControls(this.searchControls);\n\t\ttry {\n\t\t\tDirContextOperations operations = template.searchForSingleEntry(this.searchBase, this.searchFilter,\n\t\t\t\t\tnew String[] { username });\n\t\t\tlogger.debug(LogMessage.of(() -> \"Found user '\" + username + \"', with \" + this));\n\t\t\treturn operations;\n\t\t}\n\t\tcatch (IncorrectResultSizeDataAccessException ex) {\n\t\t\tif (ex.getActualSize() == 0) {\n\t\t\t\tthrow UsernameNotFoundException.fromUsername(username);\n\t\t\t}\n\t\t\t// Search should never return multiple results if properly configured\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the corresponding property on the {@link SearchControls} instance used in the\n\t * search.\n\t * @param deref the derefLinkFlag value as defined in SearchControls..\n\t */\n\tpublic void setDerefLinkFlag(boolean deref) {\n\t\tthis.searchControls.setDerefLinkFlag(deref);\n\t}\n\n\t/**\n\t * If true then searches the entire subtree as identified by context, if false (the\n\t * default) then only searches the level identified by the context.\n\t * @param searchSubtree true the underlying search controls should be set to\n\t * SearchControls.SUBTREE_SCOPE rather than SearchControls.ONELEVEL_SCOPE.\n\t */\n\tpublic void setSearchSubtree(boolean searchSubtree) {\n\t\tthis.searchControls\n\t\t\t.setSearchScope(searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);\n\t}\n\n\t/**\n\t * The time to wait before the search fails; the default is zero, meaning forever.\n\t * @param searchTimeLimit the time limit for the search (in milliseconds).\n\t */\n\tpublic void setSearchTimeLimit(int searchTimeLimit) {\n\t\tthis.searchControls.setTimeLimit(searchTimeLimit);\n\t}\n\n\t/**\n\t * Specifies the attributes that will be returned as part of the search.\n\t * <p>\n\t * null indicates that all attributes will be returned. An empty array indicates no\n\t * attributes are returned.\n\t * @param attrs An array of attribute names identifying the attributes that will be\n\t * returned. Can be null.\n\t */\n\tpublic void setReturningAttributes(String[] attrs) {\n\t\tthis.searchControls.setReturningAttributes(attrs);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(getClass().getSimpleName()).append(\" [\");\n\t\tsb.append(\"searchFilter=\").append(this.searchFilter).append(\"; \");\n\t\tsb.append(\"searchBase=\").append(this.searchBase).append(\"; \");\n\t\tsb.append(\"scope=\")\n\t\t\t.append((this.searchControls.getSearchScope() != SearchControls.SUBTREE_SCOPE) ? \"single-level\" : \"subtree\")\n\t\t\t.append(\"; \");\n\t\tsb.append(\"searchTimeLimit=\").append(this.searchControls.getTimeLimit()).append(\"; \");\n\t\tsb.append(\"derefLinkFlag=\").append(this.searchControls.getDerefLinkFlag()).append(\" ]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/search/LdapUserSearch.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.search;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\n\n/**\n * Obtains a user's information from the LDAP directory given a login name.\n * <p>\n * May be optionally used to configure the LDAP authentication implementation when a more\n * sophisticated approach is required than just using a simple username-&gt;DN mapping.\n *\n * @author Luke Taylor\n */\npublic interface LdapUserSearch {\n\n\t/**\n\t * Locates a single user in the directory and returns the LDAP information for that\n\t * user.\n\t * @param username the login name supplied to the authentication service.\n\t * @return a DirContextOperations object containing the user's full DN and requested\n\t * attributes.\n\t * @throws UsernameNotFoundException if no user with the supplied name could be\n\t * located by the search.\n\t */\n\tDirContextOperations searchForUser(String username) throws UsernameNotFoundException;\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/search/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * {@code LdapUserSearch} implementations. These may be used to locate the user in the\n * directory.\n */\n@NullMarked\npackage org.springframework.security.ldap.search;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/server/EmbeddedLdapServerContainer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.server;\n\n/**\n * Provides lifecycle services for an embedded LDAP server.\n *\n * @author Eleftheria Stein\n * @since 5.7\n */\npublic interface EmbeddedLdapServerContainer {\n\n\t/**\n\t * Returns the embedded LDAP server port.\n\t * @return the embedded LDAP server port\n\t */\n\tint getPort();\n\n\t/**\n\t * The embedded LDAP server port to connect to. Supplying 0 as the port indicates that\n\t * a random available port should be selected.\n\t * @param port the port to connect to\n\t */\n\tvoid setPort(int port);\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/server/UnboundIdContainer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.server;\n\nimport java.io.InputStream;\n\nimport com.unboundid.ldap.listener.InMemoryDirectoryServer;\nimport com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;\nimport com.unboundid.ldap.listener.InMemoryListenerConfig;\nimport com.unboundid.ldap.sdk.DN;\nimport com.unboundid.ldap.sdk.Entry;\nimport com.unboundid.ldap.sdk.LDAPException;\nimport com.unboundid.ldap.sdk.RDN;\nimport com.unboundid.ldif.LDIFReader;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.Lifecycle;\nimport org.springframework.core.io.Resource;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Eddú Meléndez\n */\npublic class UnboundIdContainer\n\t\timplements EmbeddedLdapServerContainer, InitializingBean, DisposableBean, Lifecycle, ApplicationContextAware {\n\n\tprivate @Nullable InMemoryDirectoryServer directoryServer;\n\n\tprivate final String defaultPartitionSuffix;\n\n\tprivate int port = 53389;\n\n\tprivate boolean isEphemeral;\n\n\tprivate @Nullable ConfigurableApplicationContext context;\n\n\tprivate boolean running;\n\n\tprivate final @Nullable String ldif;\n\n\tpublic UnboundIdContainer(String defaultPartitionSuffix, @Nullable String ldif) {\n\t\tthis.defaultPartitionSuffix = defaultPartitionSuffix;\n\t\tthis.ldif = ldif;\n\t}\n\n\t@Override\n\tpublic int getPort() {\n\t\treturn this.port;\n\t}\n\n\t@Override\n\tpublic void setPort(int port) {\n\t\tthis.port = port;\n\t\tthis.isEphemeral = port == 0;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tstop();\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tstart();\n\t}\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tthis.context = (ConfigurableApplicationContext) applicationContext;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tif (isRunning()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tInMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(this.defaultPartitionSuffix);\n\t\t\tconfig.addAdditionalBindCredentials(\"uid=admin,ou=system\", \"secret\");\n\t\t\tconfig.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig(\"LDAP\", this.port));\n\t\t\tconfig.setEnforceSingleStructuralObjectClass(false);\n\t\t\tconfig.setEnforceAttributeSyntaxCompliance(true);\n\t\t\tDN dn = new DN(this.defaultPartitionSuffix);\n\t\t\tRDN rdn = dn.getRDN();\n\t\t\tAssert.notNull(rdn, \"defaultPartitionSuffix cannot be the empty DN\");\n\t\t\tEntry entry = new Entry(dn);\n\t\t\tentry.addAttribute(\"objectClass\", \"top\", \"domain\", \"extensibleObject\");\n\t\t\tentry.addAttribute(\"dc\", rdn.getAttributeValues()[0]);\n\t\t\tInMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config);\n\t\t\tdirectoryServer.add(entry);\n\t\t\timportLdif(directoryServer);\n\t\t\tdirectoryServer.startListening();\n\t\t\tthis.port = directoryServer.getListenPort();\n\t\t\tthis.directoryServer = directoryServer;\n\t\t\tthis.running = true;\n\t\t}\n\t\tcatch (LDAPException ex) {\n\t\t\tthrow new RuntimeException(\"Server startup failed\", ex);\n\t\t}\n\t}\n\n\tprivate void importLdif(InMemoryDirectoryServer directoryServer) {\n\t\tif (StringUtils.hasText(this.ldif)) {\n\t\t\tAssert.notNull(this.context, \"context cannot be null if ldif has a value\");\n\t\t\ttry {\n\t\t\t\tResource[] resources = this.context.getResources(this.ldif);\n\t\t\t\tif (resources.length > 0) {\n\t\t\t\t\tif (!resources[0].exists()) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"Unable to find LDIF resource \" + this.ldif);\n\t\t\t\t\t}\n\t\t\t\t\ttry (InputStream inputStream = resources[0].getInputStream()) {\n\t\t\t\t\t\tdirectoryServer.importFromLDIF(false, new LDIFReader(inputStream));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalStateException(\"Unable to load LDIF \" + this.ldif, ex);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tif (this.isEphemeral && this.context != null && !this.context.isClosed()) {\n\t\t\treturn;\n\t\t}\n\t\tif (this.directoryServer != null) {\n\t\t\tthis.directoryServer.shutDown(true);\n\t\t}\n\t\tthis.running = false;\n\t}\n\n\t@Override\n\tpublic boolean isRunning() {\n\t\treturn this.running;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/server/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Embedded UnboundID Server implementation, as used by the configuration namespace.\n */\n@NullMarked\npackage org.springframework.security.ldap.server;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport javax.naming.directory.SearchControls;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.core.LdapTemplate;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * The default strategy for obtaining user role information from the directory.\n * <p>\n * It obtains roles by performing a search for \"groups\" the user is a member of.\n * <p>\n * A typical group search scenario would be where each group/role is specified using the\n * <tt>groupOfNames</tt> (or <tt>groupOfUniqueNames</tt>) LDAP objectClass and the user's\n * DN is listed in the <tt>member</tt> (or <tt>uniqueMember</tt>) attribute to indicate\n * that they should be assigned that role. The following LDIF sample has the groups stored\n * under the DN <tt>ou=groups,dc=springframework,dc=org</tt> and a group called\n * \"developers\" with \"ben\" and \"luke\" as members:\n *\n * <pre>\n * dn: ou=groups,dc=springframework,dc=org\n * objectClass: top\n * objectClass: organizationalUnit\n * ou: groups\n *\n * dn: cn=developers,ou=groups,dc=springframework,dc=org\n * objectClass: groupOfNames\n * objectClass: top\n * cn: developers\n * description: Spring Security Developers\n * member: uid=ben,ou=people,dc=springframework,dc=org\n * member: uid=luke,ou=people,dc=springframework,dc=org\n * ou: developer\n * </pre>\n * <p>\n * The group search is performed within a DN specified by the <tt>groupSearchBase</tt>\n * property, which should be relative to the root DN of its <tt>ContextSource</tt>. If the\n * search base is null, group searching is disabled. The filter used in the search is\n * defined by the <tt>groupSearchFilter</tt> property, with the filter argument {0} being\n * the full DN of the user. You can also optionally use the parameter {1}, which will be\n * substituted with the username. You can also specify which attribute defines the role\n * name by setting the <tt>groupRoleAttribute</tt> property (the default is \"cn\").\n * <p>\n * The configuration below shows how the group search might be performed with the above\n * schema.\n *\n * <pre>\n * &lt;bean id=\"ldapAuthoritiesPopulator\"\n *       class=\"org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator\"&gt;\n *   &lt;constructor-arg ref=\"contextSource\"/&gt;\n *   &lt;constructor-arg value=\"ou=groups\"/&gt;\n *   &lt;property name=\"groupRoleAttribute\" value=\"ou\"/&gt;\n * &lt;!-- the following properties are shown with their default values --&gt;\n *   &lt;property name=\"searchSubtree\" value=\"false\"/&gt;\n *   &lt;property name=\"rolePrefix\" value=\"ROLE_\"/&gt;\n *   &lt;property name=\"convertToUpperCase\" value=\"true\"/&gt;\n * &lt;/bean&gt;\n * </pre>\n *\n * A search for roles for user \"uid=ben,ou=people,dc=springframework,dc=org\" would return\n * the single granted authority \"ROLE_DEVELOPER\".\n * <p>\n * The single-level search is performed by default. Setting the <tt>searchSubTree</tt>\n * property to true will enable a search of the entire subtree under\n * <tt>groupSearchBase</tt>.\n *\n * @author Luke Taylor\n * @author Filip Hanik\n */\npublic class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {\n\n\tprivate static final Log logger = LogFactory.getLog(DefaultLdapAuthoritiesPopulator.class);\n\n\t/**\n\t * A default role which will be assigned to all authenticated users if set\n\t */\n\tprivate @Nullable GrantedAuthority defaultRole;\n\n\t/**\n\t * Template that will be used for searching\n\t */\n\tprivate final SpringSecurityLdapTemplate ldapTemplate;\n\n\t/**\n\t * Controls used to determine whether group searches should be performed over the full\n\t * sub-tree from the base DN. Modified by searchSubTree property\n\t */\n\tprivate final SearchControls searchControls = new SearchControls();\n\n\t/**\n\t * The ID of the attribute which contains the role name for a group\n\t */\n\tprivate String groupRoleAttribute = \"cn\";\n\n\t/**\n\t * The base DN from which the search for group membership should be performed\n\t */\n\tprivate final @Nullable String groupSearchBase;\n\n\t/**\n\t * The pattern to be used for the user search. {0} is the user's DN\n\t */\n\tprivate String groupSearchFilter = \"(member={0})\";\n\n\t/**\n\t * The role prefix that will be prepended to each role name\n\t */\n\tprivate String rolePrefix = \"ROLE_\";\n\n\t/**\n\t * Should we convert the role name to uppercase\n\t */\n\tprivate boolean convertToUpperCase = true;\n\n\t/**\n\t * The mapping function to be used to populate authorities.\n\t */\n\tprivate Function<Map<String, List<String>>, @Nullable GrantedAuthority> authorityMapper;\n\n\t/**\n\t * Constructor for group search scenarios. <tt>userRoleAttributes</tt> may still be\n\t * set as a property.\n\t * @param contextSource supplies the contexts used to search for user roles.\n\t * @param groupSearchBase if this is an empty string the search will be performed from\n\t * the root DN of the context factory. If null, no search will be performed.\n\t */\n\tpublic DefaultLdapAuthoritiesPopulator(ContextSource contextSource, @Nullable String groupSearchBase) {\n\t\tAssert.notNull(contextSource, \"contextSource must not be null\");\n\t\tthis.ldapTemplate = new SpringSecurityLdapTemplate(contextSource);\n\t\tgetLdapTemplate().setSearchControls(getSearchControls());\n\t\tthis.groupSearchBase = groupSearchBase;\n\t\tif (groupSearchBase == null) {\n\t\t\tlogger.info(\"Will not perform group search since groupSearchBase is null.\");\n\t\t}\n\t\telse if (groupSearchBase.isEmpty()) {\n\t\t\tlogger.info(\"Will perform group search from the context source base since groupSearchBase is empty.\");\n\t\t}\n\t\tthis.authorityMapper = (record) -> {\n\t\t\tList<String> roles = record.get(this.groupRoleAttribute);\n\t\t\tif (CollectionUtils.isEmpty(roles)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString role = roles.get(0);\n\t\t\tif (this.convertToUpperCase) {\n\t\t\t\trole = role.toUpperCase(Locale.ROOT);\n\t\t\t}\n\t\t\treturn new SimpleGrantedAuthority(this.rolePrefix + role);\n\t\t};\n\t}\n\n\t/**\n\t * This method should be overridden if required to obtain any additional roles for the\n\t * given user (on top of those obtained from the standard search implemented by this\n\t * class).\n\t * @param user the context representing the user who's roles are required\n\t * @return the extra roles which will be merged with those returned by the group\n\t * search\n\t */\n\n\tprotected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {\n\t\treturn Collections.emptySet();\n\t}\n\n\t/**\n\t * Obtains the authorities for the user who's directory entry is represented by the\n\t * supplied LdapUserDetails object.\n\t * @param user the user who's authorities are required\n\t * @return the set of roles granted to the user.\n\t */\n\t@Override\n\tpublic final Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations user, String username) {\n\t\tString userDn = user.getNameInNamespace();\n\t\tSet<GrantedAuthority> roles = getGroupMembershipRoles(userDn, username);\n\t\tSet<GrantedAuthority> extraRoles = getAdditionalRoles(user, username);\n\t\tif (extraRoles != null) {\n\t\t\troles.addAll(extraRoles);\n\t\t}\n\t\tif (this.defaultRole != null) {\n\t\t\troles.add(this.defaultRole);\n\t\t}\n\t\tList<GrantedAuthority> result = new ArrayList<>(roles.size());\n\t\tresult.addAll(roles);\n\t\tlogger.debug(LogMessage.format(\"Retrieved authorities for user %s\", userDn));\n\t\treturn result;\n\t}\n\n\tpublic Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {\n\t\tString base = getGroupSearchBase();\n\t\tif (base == null) {\n\t\t\treturn new HashSet<>();\n\t\t}\n\t\tSet<GrantedAuthority> authorities = new HashSet<>();\n\t\tlogger.trace(LogMessage.of(() -> \"Searching for roles for user \" + username + \" with DN \" + userDn\n\t\t\t\t+ \" and filter \" + this.groupSearchFilter + \" in search base \" + getGroupSearchBase()));\n\t\tSet<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(base,\n\t\t\t\tthis.groupSearchFilter, new String[] { userDn, username }, new String[] { this.groupRoleAttribute });\n\t\tlogger.debug(LogMessage.of(() -> \"Found roles from search \" + userRoles));\n\t\tfor (Map<String, List<String>> role : userRoles) {\n\t\t\tGrantedAuthority authority = this.authorityMapper.apply(role);\n\t\t\tif (authority != null) {\n\t\t\t\tauthorities.add(authority);\n\t\t\t}\n\t\t}\n\t\treturn authorities;\n\t}\n\n\tprotected ContextSource getContextSource() {\n\t\treturn getLdapTemplate().getContextSource();\n\t}\n\n\tprotected @Nullable String getGroupSearchBase() {\n\t\treturn this.groupSearchBase;\n\t}\n\n\t/**\n\t * Convert the role to uppercase\n\t */\n\tpublic void setConvertToUpperCase(boolean convertToUpperCase) {\n\t\tthis.convertToUpperCase = convertToUpperCase;\n\t}\n\n\t/**\n\t * The default role which will be assigned to all users.\n\t * @param defaultRole the role name, including any desired prefix.\n\t */\n\tpublic void setDefaultRole(String defaultRole) {\n\t\tAssert.notNull(defaultRole, \"The defaultRole property cannot be set to null\");\n\t\tthis.defaultRole = new SimpleGrantedAuthority(defaultRole);\n\t}\n\n\tpublic void setGroupRoleAttribute(String groupRoleAttribute) {\n\t\tAssert.notNull(groupRoleAttribute, \"groupRoleAttribute must not be null\");\n\t\tthis.groupRoleAttribute = groupRoleAttribute;\n\t}\n\n\tpublic void setGroupSearchFilter(String groupSearchFilter) {\n\t\tAssert.notNull(groupSearchFilter, \"groupSearchFilter must not be null\");\n\t\tthis.groupSearchFilter = groupSearchFilter;\n\t}\n\n\t/**\n\t * Sets the prefix which will be prepended to the values loaded from the directory.\n\t * Defaults to \"ROLE_\" for compatibility with <tt>RoleVoter</tt>.\n\t */\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tAssert.notNull(rolePrefix, \"rolePrefix must not be null\");\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n\t/**\n\t * If set to true, a subtree scope search will be performed. If false a single-level\n\t * search is used.\n\t * @param searchSubtree set to true to enable searching of the entire tree below the\n\t * <tt>groupSearchBase</tt>.\n\t */\n\tpublic void setSearchSubtree(boolean searchSubtree) {\n\t\tint searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;\n\t\tthis.searchControls.setSearchScope(searchScope);\n\t}\n\n\t/**\n\t * Sets the corresponding property on the underlying template, avoiding specific\n\t * issues with Active Directory.\n\t *\n\t * @see LdapTemplate#setIgnoreNameNotFoundException(boolean)\n\t */\n\tpublic void setIgnorePartialResultException(boolean ignore) {\n\t\tgetLdapTemplate().setIgnorePartialResultException(ignore);\n\t}\n\n\t/**\n\t * Sets the mapping function which will be used to create instances of\n\t * {@link GrantedAuthority} given the context record.\n\t * @param authorityMapper the mapping function\n\t */\n\tpublic void setAuthorityMapper(Function<Map<String, List<String>>, @Nullable GrantedAuthority> authorityMapper) {\n\t\tAssert.notNull(authorityMapper, \"authorityMapper must not be null\");\n\t\tthis.authorityMapper = authorityMapper;\n\t}\n\n\t/**\n\t * Returns the current LDAP template. Method available so that classes extending this\n\t * can override the template used\n\t * @return the LDAP template\n\t * @see org.springframework.security.ldap.SpringSecurityLdapTemplate\n\t */\n\tprotected SpringSecurityLdapTemplate getLdapTemplate() {\n\t\treturn this.ldapTemplate;\n\t}\n\n\t/**\n\t * Returns the attribute name of the LDAP attribute that will be mapped to the role\n\t * name Method available so that classes extending this can override\n\t * @return the attribute name used for role mapping\n\t * @see #setGroupRoleAttribute(String)\n\t */\n\tprotected final String getGroupRoleAttribute() {\n\t\treturn this.groupRoleAttribute;\n\t}\n\n\t/**\n\t * Returns the search filter configured for this populator Method available so that\n\t * classes extending this can override\n\t * @return the search filter\n\t * @see #setGroupSearchFilter(String)\n\t */\n\tprotected final String getGroupSearchFilter() {\n\t\treturn this.groupSearchFilter;\n\t}\n\n\t/**\n\t * Returns the role prefix used by this populator Method available so that classes\n\t * extending this can override\n\t * @return the role prefix\n\t * @see #setRolePrefix(String)\n\t */\n\tprotected final String getRolePrefix() {\n\t\treturn this.rolePrefix;\n\t}\n\n\t/**\n\t * Returns true if role names are converted to uppercase Method available so that\n\t * classes extending this can override\n\t * @return true if role names are converted to uppercase.\n\t * @see #setConvertToUpperCase(boolean)\n\t */\n\tprotected final boolean isConvertToUpperCase() {\n\t\treturn this.convertToUpperCase;\n\t}\n\n\t/**\n\t * Returns the search controls Method available so that classes extending this can\n\t * override the search controls used\n\t * @return the search controls\n\t */\n\tprivate SearchControls getSearchControls() {\n\t\treturn this.searchControls;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/InetOrgPerson.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.util.Assert;\n\n/**\n * UserDetails implementation whose properties are based on a subset of the LDAP schema\n * for <tt>inetOrgPerson</tt>.\n *\n * <p>\n * The username will be mapped from the <tt>uid</tt> attribute by default.\n *\n * @author Luke Taylor\n */\npublic class InetOrgPerson extends Person {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate @Nullable String carLicense;\n\n\t// Person.cn\n\tprivate @Nullable String destinationIndicator;\n\n\tprivate @Nullable String departmentNumber;\n\n\t// Person.description\n\tprivate @Nullable String displayName;\n\n\tprivate @Nullable String employeeNumber;\n\n\tprivate @Nullable String homePhone;\n\n\tprivate @Nullable String homePostalAddress;\n\n\tprivate @Nullable String initials;\n\n\tprivate @Nullable String mail;\n\n\tprivate @Nullable String mobile;\n\n\tprivate @Nullable String o;\n\n\tprivate @Nullable String ou;\n\n\tprivate @Nullable String postalAddress;\n\n\tprivate @Nullable String postalCode;\n\n\tprivate @Nullable String roomNumber;\n\n\tprivate @Nullable String street;\n\n\t// Person.sn\n\t// Person.telephoneNumber\n\tprivate @Nullable String title;\n\n\tprivate @Nullable String uid;\n\n\tpublic String getUid() {\n\t\treturn Objects.requireNonNull(this.uid, \"uid cannot be null\");\n\t}\n\n\tpublic @Nullable String getMail() {\n\t\treturn this.mail;\n\t}\n\n\tpublic @Nullable String getEmployeeNumber() {\n\t\treturn this.employeeNumber;\n\t}\n\n\tpublic @Nullable String getInitials() {\n\t\treturn this.initials;\n\t}\n\n\tpublic @Nullable String getDestinationIndicator() {\n\t\treturn this.destinationIndicator;\n\t}\n\n\tpublic @Nullable String getO() {\n\t\treturn this.o;\n\t}\n\n\tpublic @Nullable String getOu() {\n\t\treturn this.ou;\n\t}\n\n\tpublic @Nullable String getTitle() {\n\t\treturn this.title;\n\t}\n\n\tpublic @Nullable String getCarLicense() {\n\t\treturn this.carLicense;\n\t}\n\n\tpublic @Nullable String getDepartmentNumber() {\n\t\treturn this.departmentNumber;\n\t}\n\n\tpublic @Nullable String getDisplayName() {\n\t\treturn this.displayName;\n\t}\n\n\tpublic @Nullable String getHomePhone() {\n\t\treturn this.homePhone;\n\t}\n\n\tpublic @Nullable String getRoomNumber() {\n\t\treturn this.roomNumber;\n\t}\n\n\tpublic @Nullable String getHomePostalAddress() {\n\t\treturn this.homePostalAddress;\n\t}\n\n\tpublic @Nullable String getMobile() {\n\t\treturn this.mobile;\n\t}\n\n\tpublic @Nullable String getPostalAddress() {\n\t\treturn this.postalAddress;\n\t}\n\n\tpublic @Nullable String getPostalCode() {\n\t\treturn this.postalCode;\n\t}\n\n\tpublic @Nullable String getStreet() {\n\t\treturn this.street;\n\t}\n\n\t@Override\n\tprotected void populateContext(DirContextAdapter adapter) {\n\t\tsuper.populateContext(adapter);\n\t\tadapter.setAttributeValue(\"carLicense\", this.carLicense);\n\t\tadapter.setAttributeValue(\"departmentNumber\", this.departmentNumber);\n\t\tadapter.setAttributeValue(\"destinationIndicator\", this.destinationIndicator);\n\t\tadapter.setAttributeValue(\"displayName\", this.displayName);\n\t\tadapter.setAttributeValue(\"employeeNumber\", this.employeeNumber);\n\t\tadapter.setAttributeValue(\"homePhone\", this.homePhone);\n\t\tadapter.setAttributeValue(\"homePostalAddress\", this.homePostalAddress);\n\t\tadapter.setAttributeValue(\"initials\", this.initials);\n\t\tadapter.setAttributeValue(\"mail\", this.mail);\n\t\tadapter.setAttributeValue(\"mobile\", this.mobile);\n\t\tadapter.setAttributeValue(\"postalAddress\", this.postalAddress);\n\t\tadapter.setAttributeValue(\"postalCode\", this.postalCode);\n\t\tadapter.setAttributeValue(\"ou\", this.ou);\n\t\tadapter.setAttributeValue(\"o\", this.o);\n\t\tadapter.setAttributeValue(\"roomNumber\", this.roomNumber);\n\t\tadapter.setAttributeValue(\"street\", this.street);\n\t\tadapter.setAttributeValue(\"uid\", this.uid);\n\t\tadapter.setAttributeValues(\"objectclass\",\n\t\t\t\tnew String[] { \"top\", \"person\", \"organizationalPerson\", \"inetOrgPerson\" });\n\t}\n\n\tpublic static class Essence extends Person.Essence {\n\n\t\tprivate @Nullable String username;\n\n\t\tpublic Essence() {\n\t\t}\n\n\t\tpublic Essence(InetOrgPerson copyMe) {\n\t\t\tsuper(copyMe);\n\t\t\tsetCarLicense(copyMe.getCarLicense());\n\t\t\tsetDepartmentNumber(copyMe.getDepartmentNumber());\n\t\t\tsetDestinationIndicator(copyMe.getDestinationIndicator());\n\t\t\tsetDisplayName(copyMe.getDisplayName());\n\t\t\tsetEmployeeNumber(copyMe.getEmployeeNumber());\n\t\t\tsetHomePhone(copyMe.getHomePhone());\n\t\t\tsetHomePostalAddress(copyMe.getHomePostalAddress());\n\t\t\tsetInitials(copyMe.getInitials());\n\t\t\tsetMail(copyMe.getMail());\n\t\t\tsetMobile(copyMe.getMobile());\n\t\t\tsetO(copyMe.getO());\n\t\t\tsetOu(copyMe.getOu());\n\t\t\tsetPostalAddress(copyMe.getPostalAddress());\n\t\t\tsetPostalCode(copyMe.getPostalCode());\n\t\t\tsetRoomNumber(copyMe.getRoomNumber());\n\t\t\tsetStreet(copyMe.getStreet());\n\t\t\tsetTitle(copyMe.getTitle());\n\t\t\tsetUid(copyMe.getUid());\n\t\t}\n\n\t\tpublic Essence(DirContextOperations ctx) {\n\t\t\tsuper(ctx);\n\t\t\tsetCarLicense(ctx.getStringAttribute(\"carLicense\"));\n\t\t\tsetDepartmentNumber(ctx.getStringAttribute(\"departmentNumber\"));\n\t\t\tsetDestinationIndicator(ctx.getStringAttribute(\"destinationIndicator\"));\n\t\t\tsetDisplayName(ctx.getStringAttribute(\"displayName\"));\n\t\t\tsetEmployeeNumber(ctx.getStringAttribute(\"employeeNumber\"));\n\t\t\tsetHomePhone(ctx.getStringAttribute(\"homePhone\"));\n\t\t\tsetHomePostalAddress(ctx.getStringAttribute(\"homePostalAddress\"));\n\t\t\tsetInitials(ctx.getStringAttribute(\"initials\"));\n\t\t\tsetMail(ctx.getStringAttribute(\"mail\"));\n\t\t\tsetMobile(ctx.getStringAttribute(\"mobile\"));\n\t\t\tsetO(ctx.getStringAttribute(\"o\"));\n\t\t\tsetOu(ctx.getStringAttribute(\"ou\"));\n\t\t\tsetPostalAddress(ctx.getStringAttribute(\"postalAddress\"));\n\t\t\tsetPostalCode(ctx.getStringAttribute(\"postalCode\"));\n\t\t\tsetRoomNumber(ctx.getStringAttribute(\"roomNumber\"));\n\t\t\tsetStreet(ctx.getStringAttribute(\"street\"));\n\t\t\tsetTitle(ctx.getStringAttribute(\"title\"));\n\t\t\tString uid = ctx.getStringAttribute(\"uid\");\n\t\t\tAssert.notNull(uid, \"uid cannot be null\");\n\t\t\tsetUid(uid);\n\t\t}\n\n\t\t@Override\n\t\tprotected LdapUserDetailsImpl createTarget() {\n\t\t\treturn new InetOrgPerson();\n\t\t}\n\n\t\tpublic void setMail(@Nullable String email) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).mail = email;\n\t\t}\n\n\t\tpublic void setUid(String uid) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).uid = uid;\n\n\t\t\tif (this.username == null) {\n\t\t\t\tsetUsername(uid);\n\t\t\t}\n\t\t}\n\n\t\tpublic void setUsername(String username) {\n\t\t\tsuper.setUsername(username);\n\t\t\tthis.username = username;\n\t\t}\n\n\t\tpublic void setInitials(@Nullable String initials) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).initials = initials;\n\t\t}\n\n\t\tpublic void setO(@Nullable String organization) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).o = organization;\n\t\t}\n\n\t\tpublic void setOu(@Nullable String ou) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).ou = ou;\n\t\t}\n\n\t\tpublic void setRoomNumber(@Nullable String no) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).roomNumber = no;\n\t\t}\n\n\t\tpublic void setTitle(@Nullable String title) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).title = title;\n\t\t}\n\n\t\tpublic void setCarLicense(@Nullable String carLicense) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).carLicense = carLicense;\n\t\t}\n\n\t\tpublic void setDepartmentNumber(@Nullable String departmentNumber) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).departmentNumber = departmentNumber;\n\t\t}\n\n\t\tpublic void setDisplayName(@Nullable String displayName) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).displayName = displayName;\n\t\t}\n\n\t\tpublic void setEmployeeNumber(@Nullable String no) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).employeeNumber = no;\n\t\t}\n\n\t\tpublic void setDestinationIndicator(@Nullable String destination) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).destinationIndicator = destination;\n\t\t}\n\n\t\tpublic void setHomePhone(@Nullable String homePhone) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).homePhone = homePhone;\n\t\t}\n\n\t\tpublic void setStreet(@Nullable String street) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).street = street;\n\t\t}\n\n\t\tpublic void setPostalCode(@Nullable String postalCode) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).postalCode = postalCode;\n\t\t}\n\n\t\tpublic void setPostalAddress(@Nullable String postalAddress) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).postalAddress = postalAddress;\n\t\t}\n\n\t\tpublic void setMobile(@Nullable String mobile) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).mobile = mobile;\n\t\t}\n\n\t\tpublic void setHomePostalAddress(@Nullable String homePostalAddress) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((InetOrgPerson) this.instance).homePostalAddress = homePostalAddress;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/InetOrgPersonContextMapper.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * @author Luke Taylor\n */\npublic class InetOrgPersonContextMapper implements UserDetailsContextMapper {\n\n\t@Override\n\tpublic UserDetails mapUserFromContext(DirContextOperations ctx, String username,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tInetOrgPerson.Essence p = new InetOrgPerson.Essence(ctx);\n\t\tp.setUsername(username);\n\t\tp.setAuthorities(authorities);\n\t\treturn p.createUserDetails();\n\n\t}\n\n\t@Override\n\tpublic void mapUserToContext(UserDetails user, DirContextAdapter ctx) {\n\t\tAssert.isInstanceOf(InetOrgPerson.class, user, \"UserDetails must be an InetOrgPerson instance\");\n\t\tInetOrgPerson p = (InetOrgPerson) user;\n\t\tp.populateContext(ctx);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapAuthoritiesPopulator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Obtains a list of granted authorities for an Ldap user.\n * <p>\n * Used by the <tt>LdapAuthenticationProvider</tt> once a user has been authenticated to\n * create the final user details object.\n * </p>\n *\n * @author Luke Taylor\n */\npublic interface LdapAuthoritiesPopulator {\n\n\t/**\n\t * Get the list of authorities for the user.\n\t * @param userData the context object which was returned by the LDAP authenticator.\n\t * @return the granted authorities for the given user.\n\t *\n\t */\n\tCollection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapAuthority.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * An authority that contains at least a DN and a role name for an LDAP entry but can also\n * contain other desired attributes to be fetched during an LDAP authority search.\n *\n * @author Filip Hanik\n */\npublic class LdapAuthority implements GrantedAuthority {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 343193700821611354L;\n\n\tprivate final String dn;\n\n\tprivate final String role;\n\n\tprivate final @Nullable Map<String, List<String>> attributes;\n\n\t/**\n\t * Constructs an LdapAuthority that has a role and a DN but no other attributes\n\t * @param role the principal's role\n\t * @param dn the distinguished name\n\t */\n\tpublic LdapAuthority(String role, String dn) {\n\t\tthis(role, dn, null);\n\t}\n\n\t/**\n\t * Constructs an LdapAuthority with the given role, DN and other LDAP attributes\n\t * @param role the principal's role\n\t * @param dn the distinguished name\n\t * @param attributes additional LDAP attributes\n\t */\n\tpublic LdapAuthority(String role, String dn, @Nullable Map<String, List<String>> attributes) {\n\t\tAssert.notNull(role, \"role can not be null\");\n\t\tAssert.notNull(dn, \"dn can not be null\");\n\t\tthis.role = role;\n\t\tthis.dn = dn;\n\t\tthis.attributes = attributes;\n\t}\n\n\t/**\n\t * Returns the LDAP attributes\n\t * @return the LDAP attributes, map can be null\n\t */\n\tpublic @Nullable Map<String, List<String>> getAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\t/**\n\t * Returns the DN for this LDAP authority\n\t * @return the distinguished name\n\t */\n\tpublic String getDn() {\n\t\treturn this.dn;\n\t}\n\n\t/**\n\t * Returns the values for a specific attribute\n\t * @param name the attribute name\n\t * @return a String array, never null but may be zero length\n\t */\n\tpublic List<String> getAttributeValues(String name) {\n\t\tList<String> result = null;\n\t\tif (this.attributes != null) {\n\t\t\tresult = this.attributes.get(name);\n\t\t}\n\t\treturn (result != null) ? result : Collections.emptyList();\n\t}\n\n\t/**\n\t * Returns the first attribute value for a specified attribute\n\t * @param name the attribute name\n\t * @return the first attribute value for a specified attribute, may be null\n\t */\n\tpublic @Nullable String getFirstAttributeValue(String name) {\n\t\tList<String> result = getAttributeValues(name);\n\t\treturn (!result.isEmpty()) ? result.get(0) : null;\n\t}\n\n\t@Override\n\tpublic String getAuthority() {\n\t\treturn this.role;\n\t}\n\n\t/**\n\t * Compares the LdapAuthority based on {@link #getAuthority()} and {@link #getDn()}\n\t * values.\n\t */\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof LdapAuthority other)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!this.dn.equals(other.getDn())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.role.equals(other.getAuthority());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.dn.hashCode();\n\t\tresult = 31 * result + ((this.role != null) ? this.role.hashCode() : 0);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"LdapAuthority{\" + \"dn='\" + this.dn + '\\'' + \", role='\" + this.role + '\\'' + '}';\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetails.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport org.springframework.security.core.CredentialsContainer;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * Captures the information for a user's LDAP entry.\n *\n * @author Luke Taylor\n */\npublic interface LdapUserDetails extends UserDetails, CredentialsContainer {\n\n\t/**\n\t * The DN of the entry for this user's account.\n\t * @return the user's DN\n\t */\n\tString getDn();\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport javax.naming.Name;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyData;\nimport org.springframework.util.Assert;\n\n/**\n * A UserDetails implementation which is used internally by the Ldap services. It also\n * contains the user's distinguished name and a set of attributes that have been retrieved\n * from the Ldap server.\n * <p>\n * An instance may be created as the result of a search, or when user information is\n * retrieved during authentication.\n * <p>\n * An instance of this class will be used by the <tt>LdapAuthenticationProvider</tt> to\n * construct the final user details object that it returns.\n * <p>\n * The {@code equals} and {@code hashcode} methods are implemented using the {@code Dn}\n * property and do not consider additional state, so it is not possible two store two\n * instances with the same DN in the same set, or use them as keys in a map.\n *\n * @author Luke Taylor\n */\npublic class LdapUserDetailsImpl implements LdapUserDetails, PasswordPolicyData {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate @Nullable String dn;\n\n\tprivate @Nullable String password;\n\n\tprivate @Nullable String username;\n\n\tprivate Collection<GrantedAuthority> authorities = AuthorityUtils.NO_AUTHORITIES;\n\n\tprivate boolean accountNonExpired = true;\n\n\tprivate boolean accountNonLocked = true;\n\n\tprivate boolean credentialsNonExpired = true;\n\n\tprivate boolean enabled = true;\n\n\t// PPolicy data\n\tprivate int timeBeforeExpiration = Integer.MAX_VALUE;\n\n\tprivate int graceLoginsRemaining = Integer.MAX_VALUE;\n\n\tprotected LdapUserDetailsImpl() {\n\t}\n\n\t@Override\n\tpublic Collection<GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic String getDn() {\n\t\treturn Objects.requireNonNull(this.dn, \"dn cannot be null\");\n\t}\n\n\t@Override\n\tpublic @Nullable String getPassword() {\n\t\treturn this.password;\n\t}\n\n\t@Override\n\tpublic String getUsername() {\n\t\treturn Objects.requireNonNull(this.username, \"username cannot be null\");\n\t}\n\n\t@Override\n\tpublic boolean isAccountNonExpired() {\n\t\treturn this.accountNonExpired;\n\t}\n\n\t@Override\n\tpublic boolean isAccountNonLocked() {\n\t\treturn this.accountNonLocked;\n\t}\n\n\t@Override\n\tpublic boolean isCredentialsNonExpired() {\n\t\treturn this.credentialsNonExpired;\n\t}\n\n\t@Override\n\tpublic boolean isEnabled() {\n\t\treturn this.enabled;\n\t}\n\n\t@Override\n\tpublic void eraseCredentials() {\n\t\tthis.password = null;\n\t}\n\n\t@Override\n\tpublic int getTimeBeforeExpiration() {\n\t\treturn this.timeBeforeExpiration;\n\t}\n\n\t@Override\n\tpublic int getGraceLoginsRemaining() {\n\t\treturn this.graceLoginsRemaining;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof LdapUserDetailsImpl) {\n\t\t\tAssert.notNull(this.dn, \"dn cannot be null\");\n\t\t\treturn this.dn.equals(((LdapUserDetailsImpl) obj).dn);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tAssert.notNull(this.dn, \"dn cannot be null\");\n\t\treturn this.dn.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(getClass().getSimpleName()).append(\" [\");\n\t\tsb.append(\"Dn=\").append(this.dn).append(\"; \");\n\t\tsb.append(\"Username=\").append(this.username).append(\"; \");\n\t\tsb.append(\"Password=[PROTECTED]; \");\n\t\tsb.append(\"Enabled=\").append(this.enabled).append(\"; \");\n\t\tsb.append(\"AccountNonExpired=\").append(this.accountNonExpired).append(\"; \");\n\t\tsb.append(\"CredentialsNonExpired=\").append(this.credentialsNonExpired).append(\"; \");\n\t\tsb.append(\"AccountNonLocked=\").append(this.accountNonLocked).append(\"; \");\n\t\tsb.append(\"Granted Authorities=\").append(getAuthorities());\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * Variation of essence pattern. Used to create mutable intermediate object\n\t */\n\tpublic static class Essence {\n\n\t\tprotected @Nullable LdapUserDetailsImpl instance = createTarget();\n\n\t\tprivate List<GrantedAuthority> mutableAuthorities = new ArrayList<>();\n\n\t\tpublic Essence() {\n\t\t}\n\n\t\tpublic Essence(DirContextOperations ctx) {\n\t\t\tsetDn(ctx.getDn());\n\t\t}\n\n\t\tpublic Essence(LdapUserDetails copyMe) {\n\t\t\tsetDn(copyMe.getDn());\n\t\t\tsetUsername(copyMe.getUsername());\n\t\t\tsetPassword(copyMe.getPassword());\n\t\t\tsetEnabled(copyMe.isEnabled());\n\t\t\tsetAccountNonExpired(copyMe.isAccountNonExpired());\n\t\t\tsetCredentialsNonExpired(copyMe.isCredentialsNonExpired());\n\t\t\tsetAccountNonLocked(copyMe.isAccountNonLocked());\n\t\t\tsetAuthorities(copyMe.getAuthorities());\n\t\t}\n\n\t\tprotected LdapUserDetailsImpl createTarget() {\n\t\t\treturn new LdapUserDetailsImpl();\n\t\t}\n\n\t\t/**\n\t\t * Adds the authority to the list, unless it is already there, in which case it is\n\t\t * ignored\n\t\t */\n\t\tpublic void addAuthority(GrantedAuthority a) {\n\t\t\tif (!hasAuthority(a)) {\n\t\t\t\tthis.mutableAuthorities.add(a);\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean hasAuthority(GrantedAuthority a) {\n\t\t\tfor (GrantedAuthority authority : this.mutableAuthorities) {\n\t\t\t\tif (authority.equals(a)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic LdapUserDetails createUserDetails() {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tAssert.notNull(this.instance.username, \"username must not be null\");\n\t\t\tAssert.notNull(this.instance.getDn(), \"Distinguished name must not be null\");\n\t\t\tthis.instance.authorities = Collections.unmodifiableList(this.mutableAuthorities);\n\t\t\tLdapUserDetails newInstance = this.instance;\n\t\t\tthis.instance = null;\n\t\t\treturn newInstance;\n\t\t}\n\n\t\tpublic Collection<GrantedAuthority> getGrantedAuthorities() {\n\t\t\treturn this.mutableAuthorities;\n\t\t}\n\n\t\tpublic void setAccountNonExpired(boolean accountNonExpired) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.accountNonExpired = accountNonExpired;\n\t\t}\n\n\t\tpublic void setAccountNonLocked(boolean accountNonLocked) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.accountNonLocked = accountNonLocked;\n\t\t}\n\n\t\tpublic void setAuthorities(Collection<? extends GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.mutableAuthorities = new ArrayList<>();\n\t\t\tthis.mutableAuthorities.addAll(authorities);\n\t\t}\n\n\t\tpublic void setCredentialsNonExpired(boolean credentialsNonExpired) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.credentialsNonExpired = credentialsNonExpired;\n\t\t}\n\n\t\tpublic void setDn(String dn) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.dn = dn;\n\t\t}\n\n\t\tpublic void setDn(Name dn) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.dn = dn.toString();\n\t\t}\n\n\t\tpublic void setEnabled(boolean enabled) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.enabled = enabled;\n\t\t}\n\n\t\tpublic void setPassword(@Nullable String password) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.password = password;\n\t\t}\n\n\t\tpublic void setUsername(String username) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.username = username;\n\t\t}\n\n\t\tpublic void setTimeBeforeExpiration(int timeBeforeExpiration) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.timeBeforeExpiration = timeBeforeExpiration;\n\t\t}\n\n\t\tpublic void setGraceLoginsRemaining(int graceLoginsRemaining) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\tthis.instance.graceLoginsRemaining = graceLoginsRemaining;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.Serial;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Locale;\n\nimport javax.naming.Context;\nimport javax.naming.NameNotFoundException;\nimport javax.naming.NamingEnumeration;\nimport javax.naming.directory.Attribute;\nimport javax.naming.directory.Attributes;\nimport javax.naming.directory.BasicAttribute;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.ModificationItem;\nimport javax.naming.directory.SearchControls;\nimport javax.naming.ldap.ExtendedRequest;\nimport javax.naming.ldap.ExtendedResponse;\nimport javax.naming.ldap.LdapContext;\nimport javax.naming.ldap.LdapName;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.ldap.core.AttributesMapper;\nimport org.springframework.ldap.core.AttributesMapperCallbackHandler;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.LdapTemplate;\nimport org.springframework.ldap.core.SearchExecutor;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;\nimport org.springframework.security.ldap.LdapUsernameToDnMapper;\nimport org.springframework.security.ldap.LdapUtils;\nimport org.springframework.security.provisioning.UserDetailsManager;\nimport org.springframework.util.Assert;\n\n/**\n * An Ldap implementation of UserDetailsManager.\n * <p>\n * It is designed around a standard setup where users and groups/roles are stored under\n * separate contexts, defined by the \"userDnBase\" and \"groupSearchBase\" properties\n * respectively.\n * <p>\n * In this case, LDAP is being used purely to retrieve information and this class can be\n * used in place of any other UserDetailsService for authentication. Authentication isn't\n * performed directly against the directory, unlike with the LDAP authentication provider\n * setup.\n *\n * @author Luke Taylor\n * @author Josh Cummings\n * @since 2.0\n */\npublic class LdapUserDetailsManager implements UserDetailsManager {\n\n\tprivate final Log logger = LogFactory.getLog(LdapUserDetailsManager.class);\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t/**\n\t * The strategy for mapping usernames to LDAP distinguished names. This will be used\n\t * when building DNs for creating new users etc.\n\t */\n\tLdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper(\"cn=users\", \"uid\");\n\n\t/** The DN under which groups are stored */\n\tprivate LdapName groupSearchBase = LdapNameBuilder.newInstance(\"cn=groups\").build();\n\n\t/** Password attribute name */\n\tprivate String passwordAttributeName = \"userPassword\";\n\n\t/** The attribute which corresponds to the role name of a group. */\n\tprivate String groupRoleAttributeName = \"cn\";\n\n\t/** The attribute which contains members of a group */\n\tprivate String groupMemberAttributeName = \"uniquemember\";\n\n\tprivate String rolePrefix = \"ROLE_\";\n\n\t/** The pattern to be used for the user search. {0} is the user's DN */\n\tprivate String groupSearchFilter = \"(uniquemember={0})\";\n\n\t/**\n\t * The strategy used to create a UserDetails object from the LDAP context, username\n\t * and list of authorities. This should be set to match the required UserDetails\n\t * implementation.\n\t */\n\tprivate UserDetailsContextMapper userDetailsMapper = new InetOrgPersonContextMapper();\n\n\tprivate final LdapTemplate template;\n\n\t/** Default context mapper used to create a set of roles from a list of attributes */\n\tprivate AttributesMapper<GrantedAuthority> roleMapper = (attributes) -> {\n\t\tAttribute roleAttr = attributes.get(this.groupRoleAttributeName);\n\t\tNamingEnumeration<?> ne = roleAttr.getAll();\n\t\tObject group = ne.next();\n\t\tString role = group.toString();\n\t\treturn new SimpleGrantedAuthority(this.rolePrefix + role.toUpperCase(Locale.ROOT));\n\t};\n\n\tprivate String @Nullable [] attributesToRetrieve;\n\n\tprivate boolean usePasswordModifyExtensionOperation = false;\n\n\tpublic LdapUserDetailsManager(ContextSource contextSource) {\n\t\tthis.template = new LdapTemplate(contextSource);\n\t}\n\n\t@Override\n\tpublic UserDetails loadUserByUsername(String username) {\n\t\tLdapName dn = this.usernameMapper.buildLdapName(username);\n\t\tList<GrantedAuthority> authorities = getUserAuthorities(dn, username);\n\t\tthis.logger.debug(LogMessage.format(\"Loading user '%s' with DN '%s'\", username, dn));\n\t\tDirContextAdapter userCtx = loadUserAsContext(dn, username);\n\t\treturn this.userDetailsMapper.mapUserFromContext(userCtx, username, authorities);\n\t}\n\n\tprivate DirContextAdapter loadUserAsContext(final LdapName dn, final String username) {\n\t\treturn this.template.executeReadOnly((ctx) -> {\n\t\t\ttry {\n\t\t\t\tAttributes attrs = ctx.getAttributes(dn, this.attributesToRetrieve);\n\t\t\t\treturn new DirContextAdapter(attrs, LdapUtils.getFullDn(dn, ctx));\n\t\t\t}\n\t\t\tcatch (NameNotFoundException ex) {\n\t\t\t\tthrow UsernameNotFoundException.fromUsername(username, ex);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Changes the password for the current user. The username is obtained from the\n\t * security context.\n\t *\n\t * <p>\n\t * There are two supported strategies for modifying the user's password depending on\n\t * the capabilities of the corresponding LDAP server.\n\t *\n\t * <p>\n\t * Configured one way, this method will modify the user's password via the\n\t * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc3062\"> LDAP Password Modify\n\t * Extended Operation </a>.\n\t *\n\t * <p>\n\t * See {@link LdapUserDetailsManager#setUsePasswordModifyExtensionOperation(boolean)}\n\t * for details.\n\t * </p>\n\t *\n\t * <p>\n\t * By default, though, if the old password is supplied, the update will be made by\n\t * rebinding as the user, thus modifying the password using the user's permissions. If\n\t * <code>oldPassword</code> is null, the update will be attempted using a standard\n\t * read/write context supplied by the context source.\n\t * </p>\n\t * @param oldPassword the old password\n\t * @param newPassword the new value of the password.\n\t */\n\t@Override\n\tpublic void changePassword(final @Nullable String oldPassword, final @Nullable String newPassword) {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tAssert.notNull(authentication,\n\t\t\t\t\"No authentication object found in security context. Can't change current user's password!\");\n\t\tString username = authentication.getName();\n\t\tthis.logger.debug(LogMessage.format(\"Changing password for user '%s'\", username));\n\t\tLdapName userDn = this.usernameMapper.buildLdapName(username);\n\t\tif (this.usePasswordModifyExtensionOperation) {\n\t\t\tchangePasswordUsingExtensionOperation(userDn, oldPassword, newPassword);\n\t\t}\n\t\telse {\n\t\t\tchangePasswordUsingAttributeModification(userDn, oldPassword, newPassword);\n\t\t}\n\t}\n\n\t/**\n\t * @param dn the distinguished name of the entry - may be either relative to the base\n\t * context or a complete DN including the name of the context (either is supported).\n\t * @param username the user whose roles are required.\n\t * @return the granted authorities returned by the group search\n\t */\n\tList<GrantedAuthority> getUserAuthorities(final LdapName dn, final String username) {\n\t\tSearchExecutor se = (ctx) -> {\n\t\t\tLdapName fullDn = LdapUtils.getFullDn(dn, ctx);\n\t\t\tSearchControls ctrls = new SearchControls();\n\t\t\tctrls.setReturningAttributes(new String[] { this.groupRoleAttributeName });\n\t\t\treturn ctx.search(this.groupSearchBase, this.groupSearchFilter,\n\t\t\t\t\tnew String[] { fullDn.toString(), username }, ctrls);\n\t\t};\n\t\tAttributesMapperCallbackHandler<GrantedAuthority> roleCollector = new AttributesMapperCallbackHandler<>(\n\t\t\t\tthis.roleMapper);\n\t\tthis.template.search(se, roleCollector);\n\t\treturn roleCollector.getList();\n\t}\n\n\t@Override\n\tpublic void createUser(UserDetails user) {\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tcopyToContext(user, ctx);\n\t\tLdapName dn = this.usernameMapper.buildLdapName(user.getUsername());\n\t\tthis.logger.debug(LogMessage.format(\"Creating new user '%s' with DN '%s'\", user.getUsername(), dn));\n\t\tthis.template.bind(dn, ctx, null);\n\t\t// Check for any existing authorities which might be set for this\n\t\t// DN and remove them\n\t\tList<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername());\n\t\tif (!authorities.isEmpty()) {\n\t\t\tremoveAuthorities(dn, authorities);\n\t\t}\n\t\taddAuthorities(dn, user.getAuthorities());\n\t}\n\n\t@Override\n\tpublic void updateUser(UserDetails user) {\n\t\tLdapName dn = this.usernameMapper.buildLdapName(user.getUsername());\n\t\tthis.logger.debug(LogMessage.format(\"Updating new user '%s' with DN '%s'\", user.getUsername(), dn));\n\t\tList<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername());\n\t\tDirContextAdapter ctx = loadUserAsContext(dn, user.getUsername());\n\t\tctx.setUpdateMode(true);\n\t\tcopyToContext(user, ctx);\n\t\t// Remove the objectclass attribute from the list of mods (if present).\n\t\tList<ModificationItem> mods = new LinkedList<>(Arrays.asList(ctx.getModificationItems()));\n\t\tListIterator<ModificationItem> modIt = mods.listIterator();\n\t\twhile (modIt.hasNext()) {\n\t\t\tModificationItem mod = modIt.next();\n\t\t\tAttribute a = mod.getAttribute();\n\t\t\tif (\"objectclass\".equalsIgnoreCase(a.getID())) {\n\t\t\t\tmodIt.remove();\n\t\t\t}\n\t\t}\n\t\tthis.template.modifyAttributes(dn, mods.toArray(new ModificationItem[0]));\n\t\t// template.rebind(dn, ctx, null);\n\t\t// Remove the old authorities and replace them with the new one\n\t\tremoveAuthorities(dn, authorities);\n\t\taddAuthorities(dn, user.getAuthorities());\n\t}\n\n\t@Override\n\tpublic void deleteUser(String username) {\n\t\tLdapName dn = this.usernameMapper.buildLdapName(username);\n\t\tremoveAuthorities(dn, getUserAuthorities(dn, username));\n\t\tthis.template.unbind(dn);\n\t}\n\n\t@Override\n\tpublic boolean userExists(String username) {\n\t\tLdapName dn = this.usernameMapper.buildLdapName(username);\n\t\ttry {\n\t\t\tObject obj = this.template.lookup(dn);\n\t\t\tif (obj instanceof Context) {\n\t\t\t\tLdapUtils.closeContext((Context) obj);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tcatch (org.springframework.ldap.NameNotFoundException ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Creates a DN from a group name.\n\t * @param group the name of the group\n\t * @return the DN of the corresponding group, including the groupSearchBase\n\t */\n\tprotected LdapName buildGroupDn(String group) {\n\t\treturn LdapNameBuilder.newInstance(this.groupSearchBase)\n\t\t\t.add(this.groupRoleAttributeName, group.toLowerCase(Locale.ROOT))\n\t\t\t.build();\n\t}\n\n\tprotected void copyToContext(UserDetails user, DirContextAdapter ctx) {\n\t\tthis.userDetailsMapper.mapUserToContext(user, ctx);\n\t}\n\n\tprotected void addAuthorities(LdapName userDn, Collection<? extends GrantedAuthority> authorities) {\n\t\tmodifyAuthorities(LdapNameBuilder.newInstance(userDn).build(), authorities, DirContext.ADD_ATTRIBUTE);\n\t}\n\n\tprotected void removeAuthorities(LdapName userDn, Collection<? extends GrantedAuthority> authorities) {\n\t\tmodifyAuthorities(LdapNameBuilder.newInstance(userDn).build(), authorities, DirContext.REMOVE_ATTRIBUTE);\n\t}\n\n\tprivate void modifyAuthorities(final LdapName userDn, final Collection<? extends GrantedAuthority> authorities,\n\t\t\tfinal int modType) {\n\t\tthis.template.executeReadWrite((ctx) -> {\n\t\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\t\tString group = convertAuthorityToGroup(authority);\n\t\t\t\tif (group == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLdapName fullDn = LdapUtils.getFullDn(userDn, ctx);\n\t\t\t\tModificationItem addGroup = new ModificationItem(modType,\n\t\t\t\t\t\tnew BasicAttribute(this.groupMemberAttributeName, fullDn.toString()));\n\t\t\t\tctx.modifyAttributes(buildGroupDn(group), new ModificationItem[] { addGroup });\n\t\t\t}\n\t\t\treturn void.class;\n\t\t});\n\t}\n\n\tprivate @Nullable String convertAuthorityToGroup(GrantedAuthority authority) {\n\t\tString group = authority.getAuthority();\n\t\tif (group == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (group.startsWith(this.rolePrefix)) {\n\t\t\tgroup = group.substring(this.rolePrefix.length());\n\t\t}\n\t\treturn group;\n\t}\n\n\tpublic void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {\n\t\tthis.usernameMapper = usernameMapper;\n\t}\n\n\tpublic void setPasswordAttributeName(String passwordAttributeName) {\n\t\tthis.passwordAttributeName = passwordAttributeName;\n\t}\n\n\tpublic void setGroupSearchBase(String groupSearchBase) {\n\t\tthis.groupSearchBase = LdapNameBuilder.newInstance(groupSearchBase).build();\n\t}\n\n\tpublic void setGroupRoleAttributeName(String groupRoleAttributeName) {\n\t\tthis.groupRoleAttributeName = groupRoleAttributeName;\n\t}\n\n\tpublic void setAttributesToRetrieve(String[] attributesToRetrieve) {\n\t\tAssert.notNull(attributesToRetrieve, \"attributesToRetrieve cannot be null\");\n\t\tthis.attributesToRetrieve = attributesToRetrieve;\n\t}\n\n\tpublic void setUserDetailsMapper(UserDetailsContextMapper userDetailsMapper) {\n\t\tthis.userDetailsMapper = userDetailsMapper;\n\t}\n\n\t/**\n\t * Sets the name of the multi-valued attribute which holds the DNs of users who are\n\t * members of a group.\n\t * <p>\n\t * Usually this will be <tt>uniquemember</tt> (the default value) or <tt>member</tt>.\n\t * </p>\n\t * @param groupMemberAttributeName the name of the attribute used to store group\n\t * members.\n\t */\n\tpublic void setGroupMemberAttributeName(String groupMemberAttributeName) {\n\t\tAssert.hasText(groupMemberAttributeName, \"groupMemberAttributeName should have text\");\n\t\tthis.groupMemberAttributeName = groupMemberAttributeName;\n\t\tthis.groupSearchFilter = \"(\" + groupMemberAttributeName + \"={0})\";\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setRoleMapper(AttributesMapper roleMapper) {\n\t\tthis.roleMapper = roleMapper;\n\t}\n\n\t/**\n\t * Sets the method by which a user's password gets modified.\n\t *\n\t * <p>\n\t * If set to {@code true}, then {@link LdapUserDetailsManager#changePassword} will\n\t * modify the user's password by way of the\n\t * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc3062\">Password Modify\n\t * Extension Operation</a>.\n\t *\n\t * <p>\n\t * If set to {@code false}, then {@link LdapUserDetailsManager#changePassword} will\n\t * modify the user's password by directly modifying attributes on the corresponding\n\t * entry.\n\t *\n\t * <p>\n\t * Before using this setting, ensure that the corresponding LDAP server supports this\n\t * extended operation.\n\t *\n\t * <p>\n\t * By default, {@code usePasswordModifyExtensionOperation} is false.\n\t * @param usePasswordModifyExtensionOperation whether to use the\n\t * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc3062\">Password Modify\n\t * Extension Operation</a> to modify the password\n\t * @since 4.2.9\n\t */\n\tpublic void setUsePasswordModifyExtensionOperation(boolean usePasswordModifyExtensionOperation) {\n\t\tthis.usePasswordModifyExtensionOperation = usePasswordModifyExtensionOperation;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Sets the role prefix used when converting authorities. The default value is \"ROLE_\"\n\t * @param rolePrefix role prefix\n\t * @since 6.3\n\t */\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tAssert.notNull(rolePrefix, \"A rolePrefix must be supplied\");\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n\tprivate void changePasswordUsingAttributeModification(LdapName userDn, @Nullable String oldPassword,\n\t\t\t@Nullable String newPassword) {\n\t\tModificationItem[] passwordChange = new ModificationItem[] { new ModificationItem(DirContext.REPLACE_ATTRIBUTE,\n\t\t\t\tnew BasicAttribute(this.passwordAttributeName, newPassword)) };\n\t\tif (oldPassword == null) {\n\t\t\tthis.template.modifyAttributes(userDn, passwordChange);\n\t\t\treturn;\n\t\t}\n\t\tthis.template.executeReadWrite((dirCtx) -> {\n\t\t\tLdapContext ctx = (LdapContext) dirCtx;\n\t\t\tctx.removeFromEnvironment(\"com.sun.jndi.ldap.connect.pool\");\n\t\t\tctx.addToEnvironment(Context.SECURITY_PRINCIPAL, LdapUtils.getFullDn(userDn, ctx).toString());\n\t\t\tctx.addToEnvironment(Context.SECURITY_CREDENTIALS, oldPassword);\n\t\t\t// TODO: reconnect doesn't appear to actually change the credentials\n\t\t\ttry {\n\t\t\t\tctx.reconnect(null);\n\t\t\t}\n\t\t\tcatch (javax.naming.AuthenticationException ex) {\n\t\t\t\tthrow new BadCredentialsException(\"Authentication for password change failed.\");\n\t\t\t}\n\t\t\tctx.modifyAttributes(userDn, passwordChange);\n\t\t\treturn void.class;\n\t\t});\n\t}\n\n\tprivate void changePasswordUsingExtensionOperation(LdapName userDn, @Nullable String oldPassword,\n\t\t\t@Nullable String newPassword) {\n\t\tthis.template.executeReadWrite((dirCtx) -> {\n\t\t\tLdapContext ctx = (LdapContext) dirCtx;\n\t\t\tString userIdentity = LdapUtils.getFullDn(userDn, ctx).toString();\n\t\t\tPasswordModifyRequest request = new PasswordModifyRequest(userIdentity, oldPassword, newPassword);\n\t\t\ttry {\n\t\t\t\treturn ctx.extendedOperation(request);\n\t\t\t}\n\t\t\tcatch (javax.naming.AuthenticationException ex) {\n\t\t\t\tthrow new BadCredentialsException(\"Authentication for password change failed.\");\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * An implementation of the\n\t * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc3062\"> LDAP Password Modify\n\t * Extended Operation </a> client request.\n\t *\n\t * <p>\n\t * Can be directed at any LDAP server that supports the Password Modify Extended\n\t * Operation.\n\t *\n\t * @author Josh Cummings\n\t * @since 4.2.9\n\t */\n\tprivate static class PasswordModifyRequest implements ExtendedRequest {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 3154223576081503237L;\n\n\t\tprivate static final byte SEQUENCE_TYPE = 48;\n\n\t\tprivate static final String PASSWORD_MODIFY_OID = \"1.3.6.1.4.1.4203.1.11.1\";\n\n\t\tprivate static final byte USER_IDENTITY_OCTET_TYPE = -128;\n\n\t\tprivate static final byte OLD_PASSWORD_OCTET_TYPE = -127;\n\n\t\tprivate static final byte NEW_PASSWORD_OCTET_TYPE = -126;\n\n\t\tprivate final ByteArrayOutputStream value = new ByteArrayOutputStream();\n\n\t\tPasswordModifyRequest(@Nullable String userIdentity, @Nullable String oldPassword,\n\t\t\t\t@Nullable String newPassword) {\n\t\t\tByteArrayOutputStream elements = new ByteArrayOutputStream();\n\t\t\tif (userIdentity != null) {\n\t\t\t\tberEncode(USER_IDENTITY_OCTET_TYPE, userIdentity.getBytes(), elements);\n\t\t\t}\n\t\t\tif (oldPassword != null) {\n\t\t\t\tberEncode(OLD_PASSWORD_OCTET_TYPE, oldPassword.getBytes(), elements);\n\t\t\t}\n\t\t\tif (newPassword != null) {\n\t\t\t\tberEncode(NEW_PASSWORD_OCTET_TYPE, newPassword.getBytes(), elements);\n\t\t\t}\n\t\t\tberEncode(SEQUENCE_TYPE, elements.toByteArray(), this.value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getID() {\n\t\t\treturn PASSWORD_MODIFY_OID;\n\t\t}\n\n\t\t@Override\n\t\tpublic byte[] getEncodedValue() {\n\t\t\treturn this.value.toByteArray();\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable ExtendedResponse createExtendedResponse(String id, byte[] berValue, int offset, int length) {\n\t\t\treturn null;\n\t\t}\n\n\t\t/**\n\t\t * Only minimal support for <a target=\"_blank\" href=\n\t\t * \"https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf\"> BER\n\t\t * encoding </a>; just what is necessary for the Password Modify request.\n\t\t *\n\t\t */\n\t\tprivate void berEncode(byte type, byte[] src, ByteArrayOutputStream dest) {\n\t\t\tint length = src.length;\n\t\t\tdest.write(type);\n\t\t\tif (length < 128) {\n\t\t\t\tdest.write(length);\n\t\t\t}\n\t\t\telse if ((length & 0x0000_00FF) == length) {\n\t\t\t\tdest.write((byte) 0x81);\n\t\t\t\tdest.write((byte) (length & 0xFF));\n\t\t\t}\n\t\t\telse if ((length & 0x0000_FFFF) == length) {\n\t\t\t\tdest.write((byte) 0x82);\n\t\t\t\tdest.write((byte) ((length >> 8) & 0xFF));\n\t\t\t\tdest.write((byte) (length & 0xFF));\n\t\t\t}\n\t\t\telse if ((length & 0x00FF_FFFF) == length) {\n\t\t\t\tdest.write((byte) 0x83);\n\t\t\t\tdest.write((byte) ((length >> 16) & 0xFF));\n\t\t\t\tdest.write((byte) ((length >> 8) & 0xFF));\n\t\t\t\tdest.write((byte) (length & 0xFF));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdest.write((byte) 0x84);\n\t\t\t\tdest.write((byte) ((length >> 24) & 0xFF));\n\t\t\t\tdest.write((byte) ((length >> 16) & 0xFF));\n\t\t\t\tdest.write((byte) ((length >> 8) & 0xFF));\n\t\t\t\tdest.write((byte) (length & 0xFF));\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tdest.write(src);\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tthrow new IllegalArgumentException(\"Failed to BER encode provided value of type: \" + type);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapper.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\nimport java.util.Locale;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyControl;\nimport org.springframework.security.ldap.ppolicy.PasswordPolicyResponseControl;\nimport org.springframework.util.Assert;\n\n/**\n * The context mapper used by the LDAP authentication provider to create an LDAP user\n * object.\n *\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\npublic class LdapUserDetailsMapper implements UserDetailsContextMapper {\n\n\tprivate final Log logger = LogFactory.getLog(LdapUserDetailsMapper.class);\n\n\tprivate String passwordAttributeName = \"userPassword\";\n\n\tprivate String rolePrefix = \"ROLE_\";\n\n\tprivate String @Nullable [] roleAttributes = null;\n\n\tprivate boolean convertToUpperCase = true;\n\n\t@Override\n\tpublic UserDetails mapUserFromContext(DirContextOperations ctx, String username,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tString dn = ctx.getNameInNamespace();\n\t\tthis.logger.debug(LogMessage.format(\"Mapping user details from context with DN %s\", dn));\n\t\tLdapUserDetailsImpl.Essence essence = new LdapUserDetailsImpl.Essence();\n\t\tessence.setDn(dn);\n\t\tObject passwordValue = ctx.getObjectAttribute(this.passwordAttributeName);\n\t\tif (passwordValue != null) {\n\t\t\tessence.setPassword(mapPassword(passwordValue));\n\t\t}\n\t\tessence.setUsername(username);\n\t\t// Map the roles\n\t\tfor (int i = 0; (this.roleAttributes != null) && (i < this.roleAttributes.length); i++) {\n\t\t\tString[] rolesForAttribute = ctx.getStringAttributes(this.roleAttributes[i]);\n\t\t\tif (rolesForAttribute == null) {\n\t\t\t\tthis.logger.debug(\n\t\t\t\t\t\tLogMessage.format(\"Couldn't read role attribute %s for user %s\", this.roleAttributes[i], dn));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (String role : rolesForAttribute) {\n\t\t\t\tGrantedAuthority authority = createAuthority(role);\n\t\t\t\tif (authority != null) {\n\t\t\t\t\tessence.addAuthority(authority);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Add the supplied authorities\n\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\tessence.addAuthority(authority);\n\t\t}\n\t\t// Check for PPolicy data\n\t\tPasswordPolicyResponseControl ppolicy = (PasswordPolicyResponseControl) ctx\n\t\t\t.getObjectAttribute(PasswordPolicyControl.OID);\n\t\tif (ppolicy != null) {\n\t\t\tessence.setTimeBeforeExpiration(ppolicy.getTimeBeforeExpiration());\n\t\t\tessence.setGraceLoginsRemaining(ppolicy.getGraceLoginsRemaining());\n\t\t}\n\t\treturn essence.createUserDetails();\n\t}\n\n\t@Override\n\tpublic void mapUserToContext(UserDetails user, DirContextAdapter ctx) {\n\t\tthrow new UnsupportedOperationException(\"LdapUserDetailsMapper only supports reading from a context. Please\"\n\t\t\t\t+ \" use a subclass if mapUserToContext() is required.\");\n\t}\n\n\t/**\n\t * Extension point to allow customized creation of the user's password from the\n\t * attribute stored in the directory.\n\t * @param passwordValue the value of the password attribute\n\t * @return a String representation of the password.\n\t */\n\tprotected String mapPassword(Object passwordValue) {\n\t\tif (!(passwordValue instanceof String)) {\n\t\t\t// Assume it's binary\n\t\t\tpasswordValue = new String((byte[]) passwordValue);\n\t\t}\n\t\treturn (String) passwordValue;\n\n\t}\n\n\t/**\n\t * Creates a GrantedAuthority from a role attribute. Override to customize authority\n\t * object creation.\n\t * <p>\n\t * The default implementation converts string attributes to roles, making use of the\n\t * <tt>rolePrefix</tt> and <tt>convertToUpperCase</tt> properties. Non-String\n\t * attributes are ignored.\n\t * </p>\n\t * @param role the attribute returned from\n\t * @return the authority to be added to the list of authorities for the user, or null\n\t * if this attribute should be ignored.\n\t */\n\tprotected @Nullable GrantedAuthority createAuthority(Object role) {\n\t\tif (role instanceof String) {\n\t\t\tif (this.convertToUpperCase) {\n\t\t\t\trole = ((String) role).toUpperCase(Locale.ROOT);\n\t\t\t}\n\t\t\treturn new SimpleGrantedAuthority(this.rolePrefix + role);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Determines whether role field values will be converted to upper case when loaded.\n\t * The default is true.\n\t * @param convertToUpperCase true if the roles should be converted to upper case.\n\t */\n\tpublic void setConvertToUpperCase(boolean convertToUpperCase) {\n\t\tthis.convertToUpperCase = convertToUpperCase;\n\t}\n\n\t/**\n\t * The name of the attribute which contains the user's password. Defaults to\n\t * \"userPassword\".\n\t * @param passwordAttributeName the name of the attribute\n\t */\n\tpublic void setPasswordAttributeName(String passwordAttributeName) {\n\t\tthis.passwordAttributeName = passwordAttributeName;\n\t}\n\n\t/**\n\t * The names of any attributes in the user's entry which represent application roles.\n\t * These will be converted to <tt>GrantedAuthority</tt>s and added to the list in the\n\t * returned LdapUserDetails object. The attribute values must be Strings by default.\n\t * @param roleAttributes the names of the role attributes.\n\t */\n\tpublic void setRoleAttributes(String[] roleAttributes) {\n\t\tAssert.notNull(roleAttributes, \"roleAttributes array cannot be null\");\n\t\tthis.roleAttributes = roleAttributes;\n\t}\n\n\t/**\n\t * The prefix that should be applied to the role names\n\t * @param rolePrefix the prefix (defaults to \"ROLE_\").\n\t */\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tthis.rolePrefix = rolePrefix;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/LdapUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.search.LdapUserSearch;\nimport org.springframework.util.Assert;\n\n/**\n * LDAP implementation of UserDetailsService based around an {@link LdapUserSearch} and an\n * {@link LdapAuthoritiesPopulator}. The final <tt>UserDetails</tt> object returned from\n * <tt>loadUserByUsername</tt> is created by the configured\n * <tt>UserDetailsContextMapper</tt>.\n *\n * @author Luke Taylor\n */\npublic class LdapUserDetailsService implements UserDetailsService {\n\n\tprivate final LdapUserSearch userSearch;\n\n\tprivate final LdapAuthoritiesPopulator authoritiesPopulator;\n\n\tprivate UserDetailsContextMapper userDetailsMapper = new LdapUserDetailsMapper();\n\n\tpublic LdapUserDetailsService(LdapUserSearch userSearch) {\n\t\tthis(userSearch, new NullLdapAuthoritiesPopulator());\n\t}\n\n\tpublic LdapUserDetailsService(LdapUserSearch userSearch, LdapAuthoritiesPopulator authoritiesPopulator) {\n\t\tAssert.notNull(userSearch, \"userSearch must not be null\");\n\t\tAssert.notNull(authoritiesPopulator, \"authoritiesPopulator must not be null\");\n\t\tthis.userSearch = userSearch;\n\t\tthis.authoritiesPopulator = authoritiesPopulator;\n\t}\n\n\t@Override\n\tpublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n\t\tDirContextOperations userData = this.userSearch.searchForUser(username);\n\t\treturn this.userDetailsMapper.mapUserFromContext(userData, username,\n\t\t\t\tthis.authoritiesPopulator.getGrantedAuthorities(userData, username));\n\t}\n\n\tpublic void setUserDetailsMapper(UserDetailsContextMapper userDetailsMapper) {\n\t\tAssert.notNull(userDetailsMapper, \"userDetailsMapper must not be null\");\n\t\tthis.userDetailsMapper = userDetailsMapper;\n\t}\n\n\tprivate static final class NullLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {\n\n\t\t@Override\n\t\tpublic Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userDetails, String username) {\n\t\t\treturn AuthorityUtils.NO_AUTHORITIES;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.ldap.core.ContextSource;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\nimport org.springframework.util.StringUtils;\n\n/**\n * A LDAP authority populator that can recursively search static nested groups.\n * <p>\n * An example of nested groups can be\n *\n * <pre>\n *  #Nested groups data\n *\n *  dn: uid=javadude,ou=people,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: person\n *  objectclass: organizationalPerson\n *  objectclass: inetOrgPerson\n *  cn: Java Dude\n *  sn: Dude\n *  uid: javadude\n *  userPassword: javadudespassword\n *\n *  dn: uid=groovydude,ou=people,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: person\n *  objectclass: organizationalPerson\n *  objectclass: inetOrgPerson\n *  cn: Groovy Dude\n *  sn: Dude\n *  uid: groovydude\n *  userPassword: groovydudespassword\n *\n *  dn: uid=closuredude,ou=people,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: person\n *  objectclass: organizationalPerson\n *  objectclass: inetOrgPerson\n *  cn: Closure Dude\n *  sn: Dude\n *  uid: closuredude\n *  userPassword: closuredudespassword\n *\n *  dn: uid=scaladude,ou=people,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: person\n *  objectclass: organizationalPerson\n *  objectclass: inetOrgPerson\n *  cn: Scala Dude\n *  sn: Dude\n *  uid: scaladude\n *  userPassword: scaladudespassword\n *\n *  dn: cn=j-developers,ou=jdeveloper,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: groupOfNames\n *  cn: j-developers\n *  ou: jdeveloper\n *  member: cn=java-developers,ou=groups,dc=springframework,dc=org\n *\n *  dn: cn=java-developers,ou=jdeveloper,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: groupOfNames\n *  cn: java-developers\n *  ou: jdeveloper\n *  member: cn=groovy-developers,ou=groups,dc=springframework,dc=org\n *  member: cn=scala-developers,ou=groups,dc=springframework,dc=org\n *  member: uid=javadude,ou=people,dc=springframework,dc=org\n *\n *  dn: cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: groupOfNames\n *  cn: java-developers\n *  ou: jdeveloper\n *  member: cn=closure-developers,ou=groups,dc=springframework,dc=org\n *  member: uid=groovydude,ou=people,dc=springframework,dc=org\n *\n *  dn: cn=closure-developers,ou=jdeveloper,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: groupOfNames\n *  cn: java-developers\n *  ou: jdeveloper\n *  member: uid=closuredude,ou=people,dc=springframework,dc=org\n *\n *  dn: cn=scala-developers,ou=jdeveloper,dc=springframework,dc=org\n *  objectclass: top\n *  objectclass: groupOfNames\n *  cn: java-developers\n *  ou: jdeveloper\n *  member: uid=scaladude,ou=people,dc=springframework,dc=org *\n * </pre>\n *\n * @author Filip Hanik\n */\n\npublic class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopulator {\n\n\tprivate static final Log logger = LogFactory.getLog(NestedLdapAuthoritiesPopulator.class);\n\n\t/**\n\t * The attribute names to retrieve for each LDAP group\n\t */\n\tprivate Set<String> attributeNames = new HashSet<>();\n\n\t/**\n\t * Maximum search depth - represents the number of recursive searches performed\n\t */\n\tprivate int maxSearchDepth = 10;\n\n\t/**\n\t * Constructor for group search scenarios. <tt>userRoleAttributes</tt> may still be\n\t * set as a property.\n\t * @param contextSource supplies the contexts used to search for user roles.\n\t * @param groupSearchBase if this is an empty string the search will be performed from\n\t * the root DN of the\n\t */\n\tpublic NestedLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {\n\t\tsuper(contextSource, groupSearchBase);\n\t}\n\n\t@Override\n\tpublic Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {\n\t\tString base = getGroupSearchBase();\n\t\tif (base == null) {\n\t\t\treturn new HashSet<>();\n\t\t}\n\t\tSet<GrantedAuthority> authorities = new HashSet<>();\n\t\tperformNestedSearch(base, userDn, username, authorities, getMaxSearchDepth());\n\t\treturn authorities;\n\t}\n\n\t/**\n\t * Performs the nested group search\n\t * @param userDn - the userDN to search for, will become the group DN for subsequent\n\t * searches\n\t * @param username - the username of the user\n\t * @param authorities - the authorities set that will be populated, must not be null\n\t * @param depth - the depth remaining, when 0 recursion will end\n\t */\n\tprivate void performNestedSearch(String base, String userDn, String username, Set<GrantedAuthority> authorities,\n\t\t\tint depth) {\n\t\tif (depth == 0) {\n\t\t\t// back out of recursion\n\t\t\tlogger.debug(LogMessage.of(() -> \"Aborted search since max depth reached,\" + \" for roles for user '\"\n\t\t\t\t\t+ username + \" with DN = \" + userDn + \" and filter \" + getGroupSearchFilter() + \" in search base '\"\n\t\t\t\t\t+ getGroupSearchBase() + \"'\"));\n\t\t\treturn;\n\t\t}\n\t\tlogger.trace(LogMessage.of(() -> \"Searching for roles for user \" + username + \" with DN \" + userDn\n\t\t\t\t+ \" and filter \" + getGroupSearchFilter() + \" in search base \" + getGroupSearchBase()));\n\t\tif (StringUtils.hasText(getGroupRoleAttribute())) {\n\t\t\tgetAttributeNames().add(getGroupRoleAttribute());\n\t\t}\n\t\tSet<Map<String, List<String>>> userRoles = getLdapTemplate().searchForMultipleAttributeValues(base,\n\t\t\t\tgetGroupSearchFilter(), new String[] { userDn, username }, getAttributeNames().toArray(new String[0]));\n\t\tlogger.debug(LogMessage.format(\"Found roles from search %s\", userRoles));\n\t\tfor (Map<String, List<String>> record : userRoles) {\n\t\t\tboolean circular = false;\n\t\t\tString dn = Objects.requireNonNull(record.get(SpringSecurityLdapTemplate.DN_KEY)).get(0);\n\t\t\tList<String> roleValues = record.get(getGroupRoleAttribute());\n\t\t\tSet<String> roles = new HashSet<>();\n\t\t\tif (roleValues != null) {\n\t\t\t\troles.addAll(roleValues);\n\t\t\t}\n\t\t\tfor (String role : roles) {\n\t\t\t\tif (isConvertToUpperCase()) {\n\t\t\t\t\trole = role.toUpperCase(Locale.ROOT);\n\t\t\t\t}\n\t\t\t\trole = getRolePrefix() + role;\n\t\t\t\t// if the group already exist, we will not search for it's parents again.\n\t\t\t\t// this prevents a forever loop for a misconfigured ldap directory\n\t\t\t\tcircular = circular | (!authorities.add(new LdapAuthority(role, dn, record)));\n\t\t\t}\n\t\t\tString roleName = (!roles.isEmpty()) ? roles.iterator().next() : dn;\n\t\t\tif (!circular) {\n\t\t\t\tperformNestedSearch(base, dn, roleName, authorities, (depth - 1));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns the attribute names that this populator has been configured to retrieve\n\t * Value can be null, represents fetch all attributes\n\t * @return the attribute names or null for all\n\t */\n\tprivate Set<String> getAttributeNames() {\n\t\treturn this.attributeNames;\n\t}\n\n\t/**\n\t * Sets the attribute names to retrieve for each ldap groups. Null means retrieve all\n\t * @param attributeNames - the names of the LDAP attributes to retrieve\n\t */\n\tpublic void setAttributeNames(Set<String> attributeNames) {\n\t\tthis.attributeNames = (attributeNames != null) ? attributeNames : new HashSet<>();\n\t}\n\n\t/**\n\t * How far should a nested search go. Depth is calculated in the number of levels we\n\t * search up for parent groups.\n\t * @return the max search depth, default is 10\n\t */\n\tprivate int getMaxSearchDepth() {\n\t\treturn this.maxSearchDepth;\n\t}\n\n\t/**\n\t * How far should a nested search go. Depth is calculated in the number of levels we\n\t * search up for parent groups.\n\t * @param maxSearchDepth the max search depth\n\t */\n\tpublic void setMaxSearchDepth(int maxSearchDepth) {\n\t\tthis.maxSearchDepth = maxSearchDepth;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/Person.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.ldap.LdapUtils;\nimport org.springframework.util.Assert;\n\n/**\n * UserDetails implementation whose properties are based on the LDAP schema for\n * <tt>Person</tt>.\n *\n * @author Luke\n * @since 2.0\n */\npublic class Person extends LdapUserDetailsImpl {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate @Nullable String givenName;\n\n\tprivate @Nullable String sn;\n\n\tprivate @Nullable String description;\n\n\tprivate @Nullable String telephoneNumber;\n\n\tprivate List<String> cn = new ArrayList<>();\n\n\tprotected Person() {\n\t}\n\n\tpublic @Nullable String getGivenName() {\n\t\treturn this.givenName;\n\t}\n\n\tpublic @Nullable String getSn() {\n\t\treturn this.sn;\n\t}\n\n\tpublic String[] getCn() {\n\t\treturn this.cn.toArray(new String[0]);\n\t}\n\n\tpublic @Nullable String getDescription() {\n\t\treturn this.description;\n\t}\n\n\tpublic @Nullable String getTelephoneNumber() {\n\t\treturn this.telephoneNumber;\n\t}\n\n\tprotected void populateContext(DirContextAdapter adapter) {\n\t\tadapter.setAttributeValue(\"givenName\", this.givenName);\n\t\tadapter.setAttributeValue(\"sn\", this.sn);\n\t\tadapter.setAttributeValues(\"cn\", getCn());\n\t\tadapter.setAttributeValue(\"description\", getDescription());\n\t\tadapter.setAttributeValue(\"telephoneNumber\", getTelephoneNumber());\n\t\tif (getPassword() != null) {\n\t\t\tadapter.setAttributeValue(\"userPassword\", getPassword());\n\t\t}\n\t\tadapter.setAttributeValues(\"objectclass\", new String[] { \"top\", \"person\" });\n\t}\n\n\tpublic static class Essence extends LdapUserDetailsImpl.Essence {\n\n\t\tpublic Essence() {\n\t\t}\n\n\t\tpublic Essence(DirContextOperations ctx) {\n\t\t\tsuper(ctx);\n\t\t\tString[] cns = ctx.getStringAttributes(\"cn\");\n\t\t\tcns = (cns != null) ? cns : new String[0];\n\t\t\tsetCn(cns);\n\t\t\tsetGivenName(ctx.getStringAttribute(\"givenName\"));\n\t\t\tsetSn(ctx.getStringAttribute(\"sn\"));\n\t\t\tsetDescription(ctx.getStringAttribute(\"description\"));\n\t\t\tsetTelephoneNumber(ctx.getStringAttribute(\"telephoneNumber\"));\n\t\t\tObject password = ctx.getObjectAttribute(\"userPassword\");\n\t\t\tif (password != null) {\n\t\t\t\tsetPassword(LdapUtils.convertPasswordToString(password));\n\t\t\t}\n\t\t}\n\n\t\tpublic Essence(Person copyMe) {\n\t\t\tsuper(copyMe);\n\t\t\tsetGivenName(copyMe.givenName);\n\t\t\tsetSn(copyMe.sn);\n\t\t\tsetDescription(copyMe.getDescription());\n\t\t\tsetTelephoneNumber(copyMe.getTelephoneNumber());\n\t\t\tsetCn(copyMe.cn.toArray(String[]::new));\n\t\t}\n\n\t\t@Override\n\t\tprotected LdapUserDetailsImpl createTarget() {\n\t\t\treturn new Person();\n\t\t}\n\n\t\tpublic void setGivenName(@Nullable String givenName) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((Person) this.instance).givenName = givenName;\n\t\t}\n\n\t\tpublic void setSn(@Nullable String sn) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((Person) this.instance).sn = sn;\n\t\t}\n\n\t\tpublic void setCn(String[] cn) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((Person) this.instance).cn = Arrays.asList(cn);\n\t\t}\n\n\t\tpublic void addCn(String value) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((Person) this.instance).cn.add(value);\n\t\t}\n\n\t\tpublic void setTelephoneNumber(@Nullable String tel) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((Person) this.instance).telephoneNumber = tel;\n\t\t}\n\n\t\tpublic void setDescription(@Nullable String desc) {\n\t\t\tAssert.notNull(this.instance, \"Essence can only be used to create a single instance\");\n\t\t\t((Person) this.instance).description = desc;\n\t\t}\n\n\t\t@Override\n\t\tpublic LdapUserDetails createUserDetails() {\n\t\t\tPerson p = (Person) super.createUserDetails();\n\t\t\tAssert.notNull(p.cn, \"person.sn cannot be null\");\n\t\t\tAssert.notEmpty(p.cn, \"person.cn cannot be empty\");\n\t\t\t// TODO: Check contents for null entries\n\t\t\treturn p;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/PersonContextMapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * @author Luke Taylor\n */\npublic class PersonContextMapper implements UserDetailsContextMapper {\n\n\t@Override\n\tpublic UserDetails mapUserFromContext(DirContextOperations ctx, String username,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tPerson.Essence p = new Person.Essence(ctx);\n\t\tp.setUsername(username);\n\t\tp.setAuthorities(authorities);\n\t\treturn p.createUserDetails();\n\t}\n\n\t@Override\n\tpublic void mapUserToContext(UserDetails user, DirContextAdapter ctx) {\n\t\tAssert.isInstanceOf(Person.class, user, \"UserDetails must be a Person instance\");\n\t\tPerson p = (Person) user;\n\t\tp.populateContext(ctx);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/UserDetailsContextMapper.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * Operations to map a UserDetails object to and from a Spring LDAP\n * {@code DirContextOperations} implementation. Used by {@code LdapUserDetailsManager}\n * when loading and saving/creating user information, and also by the\n * {@code LdapAuthenticationProvider} to allow customization of the user data loaded\n * during authentication.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic interface UserDetailsContextMapper {\n\n\t/**\n\t * Creates a fully populated UserDetails object for use by the security framework.\n\t * @param ctx the context object which contains the user information.\n\t * @param username the user's supplied login name.\n\t * @param authorities the authorities to add to the {@code UserDetails} instance\n\t * @return the user object.\n\t */\n\tUserDetails mapUserFromContext(DirContextOperations ctx, String username,\n\t\t\tCollection<? extends GrantedAuthority> authorities);\n\n\t/**\n\t * Reverse of the above operation. Populates a context object from the supplied user\n\t * object. Called when saving a user, for example.\n\t */\n\tvoid mapUserToContext(UserDetails user, DirContextAdapter ctx);\n\n}\n"
  },
  {
    "path": "ldap/src/main/java/org/springframework/security/ldap/userdetails/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * LDAP-focused {@code UserDetails} implementations which map from a ubset of the data\n * contained in some of the standard LDAP types (such as {@code InetOrgPerson}).\n */\n@NullMarked\npackage org.springframework.security.ldap.userdetails;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "ldap/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.ldap.aot.hint.LdapSecurityRuntimeHints\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/LdapUtilsTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport javax.naming.NamingException;\nimport javax.naming.directory.DirContext;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link LdapUtils}\n *\n * @author Luke Taylor\n */\npublic class LdapUtilsTests {\n\n\t@Test\n\tpublic void testCloseContextSwallowsNamingException() throws Exception {\n\t\tfinal DirContext dirCtx = mock(DirContext.class);\n\t\twillThrow(new NamingException()).given(dirCtx).close();\n\t\tLdapUtils.closeContext(dirCtx);\n\t}\n\n\t@Test\n\tpublic void testGetRelativeNameReturnsEmptyStringForDnEqualToBaseName() throws Exception {\n\t\tfinal DirContext mockCtx = mock(DirContext.class);\n\t\tgiven(mockCtx.getNameInNamespace()).willReturn(\"dc=springframework,dc=org\");\n\t\tassertThat(LdapUtils.getRelativeName(\"dc=springframework,dc=org\", mockCtx)).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void testGetRelativeNameReturnsFullDnWithEmptyBaseName() throws Exception {\n\t\tfinal DirContext mockCtx = mock(DirContext.class);\n\t\tgiven(mockCtx.getNameInNamespace()).willReturn(\"\");\n\t\tassertThat(LdapUtils.getRelativeName(\"cn=jane,dc=springframework,dc=org\", mockCtx))\n\t\t\t.isEqualTo(\"cn=jane,dc=springframework,dc=org\");\n\t}\n\n\t@Test\n\tpublic void testGetRelativeNameWorksWithArbitrarySpaces() throws Exception {\n\t\tfinal DirContext mockCtx = mock(DirContext.class);\n\t\tgiven(mockCtx.getNameInNamespace()).willReturn(\"dc=springsecurity,dc = org\");\n\t\tassertThat(LdapUtils.getRelativeName(\"cn=jane smith, dc = springsecurity , dc=org\", mockCtx))\n\t\t\t.isEqualTo(\"cn=jane smith\");\n\t}\n\n\t@Test\n\tpublic void testRootDnsAreParsedFromUrlsCorrectly() {\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldap://monkeymachine\")).isEqualTo(\"\");\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldap://monkeymachine:11389\")).isEqualTo(\"\");\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldap://monkeymachine/\")).isEqualTo(\"\");\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldap://monkeymachine.co.uk/\")).isEqualTo(\"\");\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldaps://monkeymachine.co.uk/dc=springframework,dc=org\"))\n\t\t\t.isEqualTo(\"dc=springframework,dc=org\");\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldap:///dc=springframework,dc=org\"))\n\t\t\t.isEqualTo(\"dc=springframework,dc=org\");\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldap://monkeymachine/dc=springframework,dc=org\"))\n\t\t\t.isEqualTo(\"dc=springframework,dc=org\");\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldap://monkeymachine.co.uk/dc=springframework,dc=org/ou=blah\"))\n\t\t\t.isEqualTo(\"dc=springframework,dc=org/ou=blah\");\n\t\tassertThat(LdapUtils.parseRootDnFromUrl(\"ldap://monkeymachine.co.uk:389/dc=springframework,dc=org/ou=blah\"))\n\t\t\t.isEqualTo(\"dc=springframework,dc=org/ou=blah\");\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/SpringSecurityAuthenticationSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.core.AuthenticationSource;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n */\npublic class SpringSecurityAuthenticationSourceTests {\n\n\t@BeforeEach\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void principalAndCredentialsAreEmptyWithNoAuthentication() {\n\t\tAuthenticationSource source = new SpringSecurityAuthenticationSource();\n\t\tassertThat(source.getPrincipal()).isEqualTo(\"\");\n\t\tassertThat(source.getCredentials()).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void credentialsAreEmptyWithNullCredentials() {\n\t\tAuthenticationSource source = new SpringSecurityAuthenticationSource();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(\n\t\t\t\t\tnew AnonymousAuthenticationToken(\"key\", \"anonUser\", AuthorityUtils.createAuthorityList(\"ignored\")));\n\t\tassertThat(source.getCredentials()).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void principalIsEmptyForAnonymousUser() {\n\t\tAuthenticationSource source = new SpringSecurityAuthenticationSource();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(\n\t\t\t\t\tnew AnonymousAuthenticationToken(\"key\", \"anonUser\", AuthorityUtils.createAuthorityList(\"ignored\")));\n\t\tassertThat(source.getPrincipal()).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void getPrincipalRejectsNonLdapUserDetailsObject() {\n\t\tAuthenticationSource source = new SpringSecurityAuthenticationSource();\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(new Object(), \"password\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(source::getPrincipal);\n\t}\n\n\t@Test\n\tpublic void expectedCredentialsAreReturned() {\n\t\tAuthenticationSource source = new SpringSecurityAuthenticationSource();\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(new Object(), \"password\"));\n\t\tassertThat(source.getCredentials()).isEqualTo(\"password\");\n\t}\n\n\t@Test\n\tpublic void expectedPrincipalIsReturned() {\n\t\tLdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence();\n\t\tuser.setUsername(\"joe\");\n\t\tuser.setDn(LdapNameBuilder.newInstance(\"uid=joe,ou=users\").build());\n\t\tAuthenticationSource source = new SpringSecurityAuthenticationSource();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(user.createUserDetails(), null));\n\t\tassertThat(source.getPrincipal()).isEqualTo(\"uid=joe,ou=users\");\n\t}\n\n\t@Test\n\tpublic void getPrincipalWhenCustomSecurityContextHolderStrategyThenExpectedPrincipalIsReturned() {\n\t\tLdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence();\n\t\tuser.setUsername(\"joe\");\n\t\tuser.setDn(LdapNameBuilder.newInstance(\"uid=joe,ou=users\").build());\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext())\n\t\t\t.willReturn(new SecurityContextImpl(new TestingAuthenticationToken(user.createUserDetails(), null)));\n\t\tSpringSecurityAuthenticationSource source = new SpringSecurityAuthenticationSource();\n\t\tsource.setSecurityContextHolderStrategy(strategy);\n\t\tassertThat(source.getPrincipal()).isEqualTo(\"uid=joe,ou=users\");\n\t\tverify(strategy).getContext();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap;\n\nimport javax.naming.Name;\nimport javax.naming.NamingEnumeration;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.SearchControls;\nimport javax.naming.directory.SearchResult;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.ldap.core.DirContextAdapter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n@ExtendWith(MockitoExtension.class)\npublic class SpringSecurityLdapTemplateTests {\n\n\t@Mock\n\tprivate DirContext ctx;\n\n\t@Captor\n\tprivate ArgumentCaptor<SearchControls> searchControls;\n\n\t@Mock\n\tprivate NamingEnumeration<SearchResult> resultsEnum;\n\n\t@Mock\n\tprivate SearchResult searchResult;\n\n\t// SEC-2405\n\t@Test\n\tpublic void searchForSingleEntryInternalAllowsReferrals() throws Exception {\n\t\tString base = \"\";\n\t\tString filter = \"\";\n\t\tString searchResultName = \"ldap://example.com/dc=springframework,dc=org\";\n\t\tObject[] params = new Object[] {};\n\t\tDirContextAdapter searchResultObject = mock(DirContextAdapter.class);\n\t\tgiven(this.ctx.getNameInNamespace()).willReturn(\"dc=springframework,dc=org\");\n\t\tgiven(this.ctx.search(any(Name.class), eq(filter), eq(params), this.searchControls.capture()))\n\t\t\t.willReturn(this.resultsEnum);\n\t\tgiven(this.resultsEnum.hasMore()).willReturn(true, false);\n\t\tgiven(this.resultsEnum.next()).willReturn(this.searchResult);\n\t\tgiven(this.searchResult.getObject()).willReturn(searchResultObject);\n\t\tSpringSecurityLdapTemplate.searchForSingleEntryInternal(this.ctx, mock(SearchControls.class), base, filter,\n\t\t\t\tparams);\n\t\tassertThat(this.searchControls.getValue().getReturningObjFlag()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/aot/hint/LdapSecurityRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.aot.hint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link LdapSecurityRuntimeHints}\n *\n * @author Marcus Da Coregio\n */\nclass LdapSecurityRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@Test\n\tvoid ldifResourcesHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.resource().forResource(\"users.ldif\")).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid ldapCtxFactoryHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(\"com.sun.jndi.ldap.LdapCtxFactory\")))\n\t\t\t.accepts(this.hints);\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/authentication/LdapAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.CommunicationException;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests {@link LdapAuthenticationProvider}.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @author Eddú Meléndez\n */\npublic class LdapAuthenticationProviderTests {\n\n\t@Test\n\tpublic void testSupportsUsernamePasswordAuthenticationToken() {\n\t\tLdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),\n\t\t\t\tnew MockAuthoritiesPopulator());\n\t\tassertThat(ldapProvider.supports(UsernamePasswordAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testDefaultMapperIsSet() {\n\t\tLdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),\n\t\t\t\tnew MockAuthoritiesPopulator());\n\t\tassertThat(ldapProvider.getUserDetailsContextMapper() instanceof LdapUserDetailsMapper).isTrue();\n\t}\n\n\t@Test\n\tpublic void testEmptyOrNullUserNameThrowsException() {\n\t\tLdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),\n\t\t\t\tnew MockAuthoritiesPopulator());\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(\n\t\t\t\t() -> ldapProvider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(null, \"password\")));\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> ldapProvider\n\t\t\t.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"\", \"bobspassword\")));\n\t}\n\n\t@Test\n\tpublic void usernameNotFoundExceptionIsHiddenByDefault() {\n\t\tfinal LdapAuthenticator authenticator = mock(LdapAuthenticator.class);\n\t\tfinal UsernamePasswordAuthenticationToken joe = UsernamePasswordAuthenticationToken.unauthenticated(\"joe\",\n\t\t\t\t\"password\");\n\t\tgiven(authenticator.authenticate(joe)).willThrow(new UsernameNotFoundException(\"nobody\"));\n\t\tLdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> provider.authenticate(joe));\n\t}\n\n\t@Test\n\tpublic void usernameNotFoundExceptionIsNotHiddenIfConfigured() {\n\t\tfinal LdapAuthenticator authenticator = mock(LdapAuthenticator.class);\n\t\tfinal UsernamePasswordAuthenticationToken joe = UsernamePasswordAuthenticationToken.unauthenticated(\"joe\",\n\t\t\t\t\"password\");\n\t\tgiven(authenticator.authenticate(joe)).willThrow(new UsernameNotFoundException(\"nobody\"));\n\t\tLdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);\n\t\tprovider.setHideUserNotFoundExceptions(false);\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(joe));\n\t}\n\n\t@Test\n\tpublic void normalUsage() {\n\t\tMockAuthoritiesPopulator populator = new MockAuthoritiesPopulator();\n\t\tLdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(), populator);\n\t\tLdapUserDetailsMapper userMapper = new LdapUserDetailsMapper();\n\t\tuserMapper.setRoleAttributes(new String[] { \"ou\" });\n\t\tldapProvider.setUserDetailsContextMapper(userMapper);\n\t\tassertThat(ldapProvider.getAuthoritiesPopulator()).isNotNull();\n\t\tUsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(\"ben\",\n\t\t\t\t\"benspassword\");\n\t\tObject authDetails = new Object();\n\t\tauthRequest.setDetails(authDetails);\n\t\tAuthentication authResult = ldapProvider.authenticate(authRequest);\n\t\tassertThat(authResult.getCredentials()).isEqualTo(\"benspassword\");\n\t\tassertThat(authResult.getDetails()).isSameAs(authDetails);\n\t\tUserDetails user = (UserDetails) authResult.getPrincipal();\n\t\tassertThat(user.getAuthorities()).hasSize(2);\n\t\tassertThat(user.getPassword()).isEqualTo(\"{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=\");\n\t\tassertThat(user.getUsername()).isEqualTo(\"ben\");\n\t\tassertThat(populator.getRequestedUsername()).isEqualTo(\"ben\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_FROM_ENTRY\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_FROM_POPULATOR\");\n\t}\n\n\t@Test\n\tpublic void passwordIsSetFromUserDataIfUseAuthenticationRequestCredentialsIsFalse() {\n\t\tLdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(),\n\t\t\t\tnew MockAuthoritiesPopulator());\n\t\tldapProvider.setUseAuthenticationRequestCredentials(false);\n\t\tUsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(\"ben\",\n\t\t\t\t\"benspassword\");\n\t\tAuthentication authResult = ldapProvider.authenticate(authRequest);\n\t\tassertThat(authResult.getCredentials()).isEqualTo(\"{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=\");\n\t}\n\n\t@Test\n\tpublic void useWithNullAuthoritiesPopulatorReturnsCorrectRole() {\n\t\tLdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator());\n\t\tLdapUserDetailsMapper userMapper = new LdapUserDetailsMapper();\n\t\tuserMapper.setRoleAttributes(new String[] { \"ou\" });\n\t\tldapProvider.setUserDetailsContextMapper(userMapper);\n\t\tUsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(\"ben\",\n\t\t\t\t\"benspassword\");\n\t\tUserDetails user = (UserDetails) ldapProvider.authenticate(authRequest).getPrincipal();\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_FROM_ENTRY\");\n\t}\n\n\t@Test\n\tpublic void authenticateWithNamingException() {\n\t\tUsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(\"ben\",\n\t\t\t\t\"benspassword\");\n\t\tLdapAuthenticator mockAuthenticator = mock(LdapAuthenticator.class);\n\t\tCommunicationException expectedCause = new CommunicationException(new javax.naming.CommunicationException());\n\t\tgiven(mockAuthenticator.authenticate(authRequest)).willThrow(expectedCause);\n\t\tLdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(mockAuthenticator);\n\t\tassertThatExceptionOfType(InternalAuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> ldapProvider.authenticate(authRequest))\n\t\t\t.havingCause()\n\t\t\t.isSameAs(expectedCause);\n\t}\n\n\t@Test\n\tvoid authenticateWhenSuccessThenIssuesFactor() {\n\t\tMockAuthenticator authenticator = new MockAuthenticator();\n\t\tMockAuthoritiesPopulator populator = new MockAuthoritiesPopulator();\n\t\tLdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(authenticator, populator);\n\t\tUsernamePasswordAuthenticationToken request = new UsernamePasswordAuthenticationToken(\"ben\", \"benspassword\");\n\t\tAuthentication result = ldapProvider.authenticate(request);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY);\n\t}\n\n\t@NullMarked\n\tclass MockAuthenticator implements LdapAuthenticator {\n\n\t\t@Override\n\t\tpublic DirContextOperations authenticate(Authentication authentication) {\n\t\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\t\tctx.setAttributeValue(\"ou\", \"FROM_ENTRY\");\n\t\t\tString username = authentication.getName();\n\t\t\tString password = (String) authentication.getCredentials();\n\t\t\tif (username.equals(\"ben\") && password.equals(\"benspassword\")) {\n\t\t\t\tctx.setDn(LdapNameBuilder.newInstance(\"cn=jen,ou=people,dc=springframework,dc=org\").build());\n\t\t\t\tctx.setAttributeValue(\"userPassword\", \"{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=\");\n\t\t\t\treturn ctx;\n\t\t\t}\n\t\t\telse if (username.equals(\"jen\") && password.equals(\"\")) {\n\t\t\t\tctx.setDn(LdapNameBuilder.newInstance(\"cn=jen,ou=people,dc=springframework,dc=org\").build());\n\t\t\t\treturn ctx;\n\t\t\t}\n\t\t\tthrow new BadCredentialsException(\"Authentication failed.\");\n\t\t}\n\n\t}\n\n\tclass MockAuthoritiesPopulator implements LdapAuthoritiesPopulator {\n\n\t\tString username;\n\n\t\t@Override\n\t\tpublic Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userCtx, String username) {\n\t\t\tthis.username = username;\n\t\t\treturn AuthorityUtils.createAuthorityList(\"ROLE_FROM_POPULATOR\");\n\t\t}\n\n\t\tString getRequestedUsername() {\n\t\t\treturn this.username;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/authentication/MockUserSearch.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.ldap.search.LdapUserSearch;\n\n/**\n * @author Luke Taylor\n */\n@NullMarked\npublic class MockUserSearch implements LdapUserSearch {\n\n\t@Nullable DirContextOperations user;\n\n\tpublic MockUserSearch() {\n\t}\n\n\tpublic MockUserSearch(DirContextOperations user) {\n\t\tthis.user = user;\n\t}\n\n\t@Override\n\tpublic DirContextOperations searchForUser(String username) {\n\t\tif (this.user == null) {\n\t\t\tthrow UsernameNotFoundException.fromUsername(username);\n\t\t}\n\t\treturn this.user;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticatorMockTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication;\n\nimport javax.naming.NamingEnumeration;\nimport javax.naming.directory.BasicAttribute;\nimport javax.naming.directory.BasicAttributes;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.SearchControls;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.core.support.BaseLdapPathContextSource;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings(\"deprecation\")\npublic class PasswordComparisonAuthenticatorMockTests {\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void ldapCompareOperationIsUsedWhenPasswordIsNotRetrieved() throws Exception {\n\t\tfinal DirContext dirCtx = mock(DirContext.class);\n\t\tfinal BaseLdapPathContextSource source = mock(BaseLdapPathContextSource.class);\n\t\tfinal BasicAttributes attrs = new BasicAttributes();\n\t\tattrs.put(new BasicAttribute(\"uid\", \"bob\"));\n\t\tPasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(source);\n\t\tauthenticator.setUserDnPatterns(new String[] { \"cn={0},ou=people\" });\n\t\t// Get the mock to return an empty attribute set\n\t\tgiven(source.getReadOnlyContext()).willReturn(dirCtx);\n\t\tgiven(dirCtx.getAttributes(eq(\"cn=Bob,ou=people\"), any(String[].class))).willReturn(attrs);\n\t\tgiven(dirCtx.getNameInNamespace()).willReturn(\"dc=springframework,dc=org\");\n\t\t// Setup a single return value (i.e. success)\n\t\tfinal NamingEnumeration searchResults = new BasicAttributes(\"\", null).getAll();\n\t\tgiven(dirCtx.search(eq(\"cn=Bob,ou=people\"), eq(\"(userPassword={0})\"), any(Object[].class),\n\t\t\t\tany(SearchControls.class)))\n\t\t\t.willReturn(searchResults);\n\t\tauthenticator.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"Bob\", \"bobspassword\"));\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.authentication.ad;\n\nimport java.text.MessageFormat;\nimport java.util.Collections;\nimport java.util.Hashtable;\n\nimport javax.naming.AuthenticationException;\nimport javax.naming.CommunicationException;\nimport javax.naming.Name;\nimport javax.naming.NameNotFoundException;\nimport javax.naming.NamingEnumeration;\nimport javax.naming.NamingException;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.SearchControls;\nimport javax.naming.directory.SearchResult;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.ContextFactory;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n * @author Gengwu Zhao\n * @author Andrey Litvitski\n */\npublic class ActiveDirectoryLdapAuthenticationProviderTests {\n\n\tpublic static final String EXISTING_LDAP_PROVIDER = \"ldap://192.168.1.200/\";\n\n\tpublic static final String NON_EXISTING_LDAP_PROVIDER = \"ldap://192.168.1.201/\";\n\n\tActiveDirectoryLdapAuthenticationProvider provider;\n\n\tUsernamePasswordAuthenticationToken joe = UsernamePasswordAuthenticationToken.unauthenticated(\"joe\", \"password\");\n\n\tDirContext ctx;\n\n\t@BeforeEach\n\tpublic void setUp() throws NamingException {\n\t\tthis.provider = new ActiveDirectoryLdapAuthenticationProvider(\"mydomain.eu\", \"ldap://192.168.1.200/\");\n\t\tthis.ctx = mock(DirContext.class);\n\t\tgiven(this.ctx.getNameInNamespace()).willReturn(\"\");\n\t}\n\n\t@Test\n\tpublic void bindPrincipalIsCreatedCorrectly() {\n\t\tassertThat(this.provider.createBindPrincipal(\"joe\")).isEqualTo(\"joe@mydomain.eu\");\n\t\tassertThat(this.provider.createBindPrincipal(\"joe@mydomain.eu\")).isEqualTo(\"joe@mydomain.eu\");\n\t}\n\n\t@Test\n\tpublic void successfulAuthenticationProducesExpectedAuthorities() throws Exception {\n\t\tcheckAuthentication(\"dc=mydomain,dc=eu\", this.provider);\n\t}\n\n\t// SEC-1915\n\t@Test\n\tpublic void customSearchFilterIsUsedForSuccessfulAuthentication() throws Exception {\n\t\tString customSearchFilter = \"(&(objectClass=user)(sAMAccountName={0}))\";\n\t\tString domain = \"mydomain.eu\";\n\t\tString encoded = MessageFormat.format(customSearchFilter, this.joe.getPrincipal() + \"@\" + domain);\n\t\tDirContextAdapter dca = new DirContextAdapter();\n\t\tSearchResult sr = new SearchResult(\"CN=Joe Jannsen,CN=Users\", dca, dca.getAttributes());\n\t\tgiven(this.ctx.search(any(Name.class), eq(encoded), any(SearchControls.class)))\n\t\t\t.willReturn(new MockNamingEnumeration(sr));\n\t\tActiveDirectoryLdapAuthenticationProvider customProvider = new ActiveDirectoryLdapAuthenticationProvider(\n\t\t\t\t\"mydomain.eu\", \"ldap://192.168.1.200/\");\n\t\tcustomProvider.contextFactory = createContextFactoryReturning(this.ctx);\n\t\tcustomProvider.setSearchFilter(customSearchFilter);\n\t\tAuthentication result = customProvider.authenticate(this.joe);\n\t\tassertThat(result.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void defaultSearchFilter() throws Exception {\n\t\tString defaultSearchFilter = \"(&(objectClass=user)(userPrincipalName={0}))\";\n\t\tString domain = \"mydomain.eu\";\n\t\tString encoded = MessageFormat.format(defaultSearchFilter, this.joe.getPrincipal() + \"@\" + domain);\n\t\tDirContextAdapter dca = new DirContextAdapter();\n\t\tSearchResult sr = new SearchResult(\"CN=Joe Jannsen,CN=Users\", dca, dca.getAttributes());\n\t\tgiven(this.ctx.search(any(Name.class), eq(encoded), any(SearchControls.class)))\n\t\t\t.willReturn(new MockNamingEnumeration(sr));\n\t\tActiveDirectoryLdapAuthenticationProvider customProvider = new ActiveDirectoryLdapAuthenticationProvider(\n\t\t\t\t\"mydomain.eu\", \"ldap://192.168.1.200/\");\n\t\tcustomProvider.contextFactory = createContextFactoryReturning(this.ctx);\n\t\tAuthentication result = customProvider.authenticate(this.joe);\n\t\tassertThat(result.isAuthenticated()).isTrue();\n\t\tverify(this.ctx).search(any(Name.class), any(String.class), any(SearchControls.class));\n\t}\n\n\t// SEC-2897,SEC-2224\n\t@Test\n\tpublic void bindPrincipalAndUsernameUsed() throws Exception {\n\t\tfinal String captureValue = \"(&(objectClass=user)(userPrincipalName=joe@mydomain.eu))\";\n\t\tArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);\n\t\tDirContextAdapter dca = new DirContextAdapter();\n\t\tSearchResult sr = new SearchResult(\"CN=Joe Jannsen,CN=Users\", dca, dca.getAttributes());\n\t\tgiven(this.ctx.search(any(Name.class), captor.capture(), any(SearchControls.class)))\n\t\t\t.willReturn(new MockNamingEnumeration(sr));\n\t\tActiveDirectoryLdapAuthenticationProvider customProvider = new ActiveDirectoryLdapAuthenticationProvider(\n\t\t\t\t\"mydomain.eu\", \"ldap://192.168.1.200/\");\n\t\tcustomProvider.contextFactory = createContextFactoryReturning(this.ctx);\n\t\tAuthentication result = customProvider.authenticate(this.joe);\n\t\tassertThat(captor.getValue()).isEqualTo(captureValue);\n\t\tassertThat(result.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void setSearchFilterNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.provider.setSearchFilter(null));\n\t}\n\n\t@Test\n\tpublic void setSearchFilterEmpty() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.provider.setSearchFilter(\" \"));\n\t}\n\n\t@Test\n\tpublic void nullDomainIsSupportedIfAuthenticatingWithFullUserPrincipal() throws Exception {\n\t\tthis.provider = new ActiveDirectoryLdapAuthenticationProvider(null, \"ldap://192.168.1.200/\");\n\t\tDirContextAdapter dca = new DirContextAdapter();\n\t\tSearchResult sr = new SearchResult(\"CN=Joe Jannsen,CN=Users\", dca, dca.getAttributes());\n\t\tgiven(this.ctx.search(eq(LdapNameBuilder.newInstance(\"DC=mydomain,DC=eu\").build()), any(String.class),\n\t\t\t\tany(SearchControls.class)))\n\t\t\t.willReturn(new MockNamingEnumeration(sr));\n\t\tthis.provider.contextFactory = createContextFactoryReturning(this.ctx);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t\tthis.provider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"joe@mydomain.eu\", \"password\"));\n\t}\n\n\t@Test\n\tpublic void failedUserSearchCausesBadCredentials() throws Exception {\n\t\tgiven(this.ctx.search(any(Name.class), any(String.class), any(SearchControls.class)))\n\t\t\t.willThrow(new NameNotFoundException());\n\t\tthis.provider.contextFactory = createContextFactoryReturning(this.ctx);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t// SEC-2017\n\t@Test\n\tpublic void noUserSearchCausesUsernameNotFound() throws Exception {\n\t\tgiven(this.ctx.search(any(Name.class), any(String.class), any(SearchControls.class)))\n\t\t\t.willReturn(new MockNamingEnumeration(null));\n\t\tthis.provider.contextFactory = createContextFactoryReturning(this.ctx);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t// SEC-2500\n\t@Test\n\tpublic void sec2500PreventAnonymousBind() {\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(\n\t\t\t\t() -> this.provider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(\"rwinch\", \"\")));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void duplicateUserSearchCausesError() throws Exception {\n\t\tNamingEnumeration<SearchResult> searchResults = mock(NamingEnumeration.class);\n\t\tgiven(searchResults.hasMore()).willReturn(true, true, false);\n\t\tSearchResult searchResult = mock(SearchResult.class);\n\t\tgiven(searchResult.getObject()).willReturn(new DirContextAdapter(\"ou=1\"), new DirContextAdapter(\"ou=2\"));\n\t\tgiven(searchResults.next()).willReturn(searchResult);\n\t\tgiven(this.ctx.search(any(Name.class), any(String.class), any(SearchControls.class))).willReturn(searchResults);\n\t\tthis.provider.contextFactory = createContextFactoryReturning(this.ctx);\n\t\tassertThatExceptionOfType(IncorrectResultSizeDataAccessException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\tstatic final String msg = \"[LDAP: error code 49 - 80858585: LdapErr: DSID-DECAFF0, comment: AcceptSecurityContext error, data \";\n\n\t@Test\n\tpublic void userNotFoundIsCorrectlyMapped() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + \"525, xxxx]\"));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void incorrectPasswordIsCorrectlyMapped() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + \"52e, xxxx]\"));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void notPermittedIsCorrectlyMapped() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + \"530, xxxx]\"));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void passwordNeedsResetIsCorrectlyMapped() {\n\t\tfinal String dataCode = \"773\";\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(\n\t\t\t\tnew AuthenticationException(msg + dataCode + \", xxxx]\"));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe))\n\t\t\t.withCauseInstanceOf(ActiveDirectoryAuthenticationException.class)\n\t\t\t.satisfies((ex) -> assertThat(((ActiveDirectoryAuthenticationException) ex.getCause()).getDataCode())\n\t\t\t\t.isEqualTo(dataCode));\n\t}\n\n\t@Test\n\tpublic void expiredPasswordIsCorrectlyMapped() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + \"532, xxxx]\"));\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(CredentialsExpiredException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void accountDisabledIsCorrectlyMapped() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + \"533, xxxx]\"));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(DisabledException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void accountExpiredIsCorrectlyMapped() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + \"701, xxxx]\"));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(AccountExpiredException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void accountLockedIsCorrectlyMapped() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + \"775, xxxx]\"));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(LockedException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void unknownErrorCodeIsCorrectlyMapped() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg + \"999, xxxx]\"));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void errorWithNoSubcodeIsHandledCleanly() {\n\t\tthis.provider.contextFactory = createContextFactoryThrowing(new AuthenticationException(msg));\n\t\tthis.provider.setConvertSubErrorCodesToExceptions(true);\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(this.joe));\n\t}\n\n\t@Test\n\tpublic void nonAuthenticationExceptionIsConvertedToSpringLdapException() throws Throwable {\n\t\tassertThatExceptionOfType(InternalAuthenticationServiceException.class).isThrownBy(() -> {\n\t\t\tthis.provider.contextFactory = createContextFactoryThrowing(new CommunicationException(msg));\n\t\t\tthis.provider.authenticate(this.joe);\n\t\t}).withCauseInstanceOf(org.springframework.ldap.CommunicationException.class);\n\t}\n\n\t@Test\n\tpublic void connectionExceptionIsWrappedInInternalException() throws Exception {\n\t\tActiveDirectoryLdapAuthenticationProvider noneReachableProvider = new ActiveDirectoryLdapAuthenticationProvider(\n\t\t\t\t\"mydomain.eu\", NON_EXISTING_LDAP_PROVIDER, \"dc=ad,dc=eu,dc=mydomain\");\n\t\tnoneReachableProvider\n\t\t\t.setContextEnvironmentProperties(Collections.singletonMap(\"com.sun.jndi.ldap.connect.timeout\", \"5\"));\n\t\tassertThatExceptionOfType(\n\t\t\t\torg.springframework.security.authentication.InternalAuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> noneReachableProvider.doAuthentication(this.joe));\n\t}\n\n\t@Test\n\tpublic void rootDnProvidedSeparatelyFromDomainAlsoWorks() throws Exception {\n\t\tActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(\n\t\t\t\t\"mydomain.eu\", EXISTING_LDAP_PROVIDER, \"dc=ad,dc=eu,dc=mydomain\");\n\t\tcheckAuthentication(\"dc=ad,dc=eu,dc=mydomain\", provider);\n\t}\n\n\t@Test\n\tpublic void setContextEnvironmentPropertiesNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.provider.setContextEnvironmentProperties(null));\n\t}\n\n\t@Test\n\tpublic void setContextEnvironmentPropertiesEmpty() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.provider.setContextEnvironmentProperties(new Hashtable<>()));\n\t}\n\n\t@Test\n\tpublic void contextEnvironmentPropertiesUsed() {\n\t\tHashtable<String, Object> env = new Hashtable<>();\n\t\tenv.put(\"java.naming.ldap.factory.socket\", \"unknown.package.NonExistingSocketFactory\");\n\t\tthis.provider.setContextEnvironmentProperties(env);\n\t\tassertThatExceptionOfType(InternalAuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(this.joe))\n\t\t\t.withCauseInstanceOf(org.springframework.ldap.CommunicationException.class)\n\t\t\t.withRootCauseInstanceOf(ClassNotFoundException.class);\n\t}\n\n\tContextFactory createContextFactoryThrowing(final NamingException ex) {\n\t\treturn new ContextFactory() {\n\t\t\t@Override\n\t\t\tDirContext createContext(Hashtable<?, ?> env) throws NamingException {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t};\n\t}\n\n\tContextFactory createContextFactoryReturning(final DirContext ctx) {\n\t\treturn new ContextFactory() {\n\t\t\t@Override\n\t\t\tDirContext createContext(Hashtable<?, ?> env) {\n\t\t\t\treturn ctx;\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate void checkAuthentication(String rootDn, ActiveDirectoryLdapAuthenticationProvider provider)\n\t\t\tthrows NamingException {\n\t\tDirContextAdapter dca = new DirContextAdapter();\n\t\tSearchResult sr = new SearchResult(\"CN=Joe Jannsen,CN=Users\", dca, dca.getAttributes());\n\t\t@SuppressWarnings(\"deprecation\")\n\t\tName searchBaseDn = LdapNameBuilder.newInstance(rootDn).build();\n\t\tgiven(this.ctx.search(eq(searchBaseDn), any(String.class), any(SearchControls.class)))\n\t\t\t.willReturn(new MockNamingEnumeration(sr))\n\t\t\t.willReturn(new MockNamingEnumeration(sr));\n\t\tprovider.contextFactory = createContextFactoryReturning(this.ctx);\n\t\tAuthentication result = provider.authenticate(this.joe);\n\t\tSecurityAssertions.assertThat(result).authorities().doesNotHaveToString(\"Admin\");\n\t\tdca.addAttributeValue(\"memberOf\", \"CN=Admin,CN=Users,DC=mydomain,DC=eu\");\n\t\tresult = provider.authenticate(this.joe);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(\"Admin\");\n\t}\n\n\tstatic class MockNamingEnumeration implements NamingEnumeration<SearchResult> {\n\n\t\tprivate SearchResult sr;\n\n\t\tMockNamingEnumeration(SearchResult sr) {\n\t\t\tthis.sr = sr;\n\t\t}\n\n\t\t@Override\n\t\tpublic SearchResult next() {\n\t\t\tSearchResult result = this.sr;\n\t\t\tthis.sr = null;\n\t\t\treturn result;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasMore() {\n\t\t\treturn this.sr != null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasMoreElements() {\n\t\t\treturn hasMore();\n\t\t}\n\n\t\t@Override\n\t\tpublic SearchResult nextElement() {\n\t\t\treturn next();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/jackson/InetOrgPersonMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.ldap.userdetails.InetOrgPerson;\nimport org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link org.springframework.security.ldap.jackson.InetOrgPersonMixin}.\n */\npublic class InetOrgPersonMixinTests {\n\n\tprivate static final String USER_PASSWORD = \"Password1234\";\n\n\tprivate static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", []]\";\n\n\t// @formatter:off\n\tprivate static final String INET_ORG_PERSON_JSON = \"{\\n\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.ldap.userdetails.InetOrgPerson\\\",\"\n\t\t\t+ \"\\\"dn\\\": \\\"ignored=ignored\\\",\"\n\t\t\t+ \"\\\"uid\\\": \\\"ghengis\\\",\"\n\t\t\t+ \"\\\"username\\\": \\\"ghengis\\\",\"\n\t\t\t+ \"\\\"password\\\": \\\"\" + USER_PASSWORD + \"\\\",\"\n\t\t\t+ \"\\\"carLicense\\\": \\\"HORS1\\\",\"\n\t\t\t+ \"\\\"givenName\\\": \\\"Ghengis\\\",\"\n\t\t\t+ \"\\\"destinationIndicator\\\": \\\"West\\\",\"\n\t\t\t+ \"\\\"displayName\\\": \\\"Ghengis McCann\\\",\"\n\t\t\t+ \"\\\"givenName\\\": \\\"Ghengis\\\",\"\n\t\t\t+ \"\\\"homePhone\\\": \\\"+467575436521\\\",\"\n\t\t\t+ \"\\\"initials\\\": \\\"G\\\",\"\n\t\t\t+ \"\\\"employeeNumber\\\": \\\"00001\\\",\"\n\t\t\t+ \"\\\"homePostalAddress\\\": \\\"Steppes\\\",\"\n\t\t\t+ \"\\\"mail\\\": \\\"ghengis@mongolia\\\",\"\n\t\t\t+ \"\\\"mobile\\\": \\\"always\\\",\"\n\t\t\t+ \"\\\"o\\\": \\\"Hordes\\\",\"\n\t\t\t+ \"\\\"ou\\\": \\\"Horde1\\\",\"\n\t\t\t+ \"\\\"postalAddress\\\": \\\"On the Move\\\",\"\n\t\t\t+ \"\\\"postalCode\\\": \\\"Changes Frequently\\\",\"\n\t\t\t+ \"\\\"roomNumber\\\": \\\"Yurt 1\\\",\"\n\t\t\t+ \"\\\"sn\\\": \\\"Khan\\\",\"\n\t\t\t+ \"\\\"street\\\": \\\"Westward Avenue\\\",\"\n\t\t\t+ \"\\\"telephoneNumber\\\": \\\"+442075436521\\\",\"\n\t\t\t+ \"\\\"departmentNumber\\\": \\\"5679\\\",\"\n\t\t\t+ \"\\\"title\\\": \\\"T\\\",\"\n\t\t\t+ \"\\\"cn\\\": [\\\"java.util.Arrays$ArrayList\\\",[\\\"Ghengis Khan\\\"]],\"\n\t\t\t+ \"\\\"description\\\": \\\"Scary\\\",\"\n\t\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t\t+ \"\\\"enabled\\\": true, \"\n\t\t\t+ \"\\\"authorities\\\": \" + AUTHORITIES_ARRAYLIST_JSON + \",\"\n\t\t\t+ \"\\\"graceLoginsRemaining\\\": \" + Integer.MAX_VALUE + \",\"\n\t\t\t+ \"\\\"timeBeforeExpiration\\\": \" + Integer.MAX_VALUE\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tInetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();\n\t\tInetOrgPerson p = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t\tString json = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(INET_ORG_PERSON_JSON, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull() throws JacksonException, JSONException {\n\t\tInetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();\n\t\tInetOrgPerson p = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t\tp.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(INET_ORG_PERSON_JSON.replaceAll(\"\\\"\" + USER_PASSWORD + \"\\\"\", \"null\"), actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> new JsonMapper().readValue(INET_ORG_PERSON_JSON, InetOrgPerson.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tInetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();\n\t\tInetOrgPerson expectedAuthentication = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t\tInetOrgPerson authentication = this.mapper.readValue(INET_ORG_PERSON_JSON, InetOrgPerson.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getCarLicense()).isEqualTo(expectedAuthentication.getCarLicense());\n\t\tassertThat(authentication.getDepartmentNumber()).isEqualTo(expectedAuthentication.getDepartmentNumber());\n\t\tassertThat(authentication.getDestinationIndicator())\n\t\t\t.isEqualTo(expectedAuthentication.getDestinationIndicator());\n\t\tassertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());\n\t\tassertThat(authentication.getDescription()).isEqualTo(expectedAuthentication.getDescription());\n\t\tassertThat(authentication.getDisplayName()).isEqualTo(expectedAuthentication.getDisplayName());\n\t\tassertThat(authentication.getUid()).isEqualTo(expectedAuthentication.getUid());\n\t\tassertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());\n\t\tassertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());\n\t\tassertThat(authentication.getHomePhone()).isEqualTo(expectedAuthentication.getHomePhone());\n\t\tassertThat(authentication.getEmployeeNumber()).isEqualTo(expectedAuthentication.getEmployeeNumber());\n\t\tassertThat(authentication.getHomePostalAddress()).isEqualTo(expectedAuthentication.getHomePostalAddress());\n\t\tassertThat(authentication.getInitials()).isEqualTo(expectedAuthentication.getInitials());\n\t\tassertThat(authentication.getMail()).isEqualTo(expectedAuthentication.getMail());\n\t\tassertThat(authentication.getMobile()).isEqualTo(expectedAuthentication.getMobile());\n\t\tassertThat(authentication.getO()).isEqualTo(expectedAuthentication.getO());\n\t\tassertThat(authentication.getOu()).isEqualTo(expectedAuthentication.getOu());\n\t\tassertThat(authentication.getPostalAddress()).isEqualTo(expectedAuthentication.getPostalAddress());\n\t\tassertThat(authentication.getPostalCode()).isEqualTo(expectedAuthentication.getPostalCode());\n\t\tassertThat(authentication.getRoomNumber()).isEqualTo(expectedAuthentication.getRoomNumber());\n\t\tassertThat(authentication.getStreet()).isEqualTo(expectedAuthentication.getStreet());\n\t\tassertThat(authentication.getSn()).isEqualTo(expectedAuthentication.getSn());\n\t\tassertThat(authentication.getTitle()).isEqualTo(expectedAuthentication.getTitle());\n\t\tassertThat(authentication.getGivenName()).isEqualTo(expectedAuthentication.getGivenName());\n\t\tassertThat(authentication.getTelephoneNumber()).isEqualTo(expectedAuthentication.getTelephoneNumber());\n\t\tassertThat(authentication.getGraceLoginsRemaining())\n\t\t\t.isEqualTo(expectedAuthentication.getGraceLoginsRemaining());\n\t\tassertThat(authentication.getTimeBeforeExpiration())\n\t\t\t.isEqualTo(expectedAuthentication.getTimeBeforeExpiration());\n\t\tassertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());\n\t\tassertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());\n\t\tassertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());\n\t\tassertThat(authentication.isCredentialsNonExpired())\n\t\t\t.isEqualTo(expectedAuthentication.isCredentialsNonExpired());\n\t}\n\n\tprivate DirContextAdapter createUserContext() {\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tctx.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tctx.setAttributeValue(\"uid\", \"ghengis\");\n\t\tctx.setAttributeValue(\"userPassword\", USER_PASSWORD);\n\t\tctx.setAttributeValue(\"carLicense\", \"HORS1\");\n\t\tctx.setAttributeValue(\"cn\", \"Ghengis Khan\");\n\t\tctx.setAttributeValue(\"description\", \"Scary\");\n\t\tctx.setAttributeValue(\"destinationIndicator\", \"West\");\n\t\tctx.setAttributeValue(\"displayName\", \"Ghengis McCann\");\n\t\tctx.setAttributeValue(\"givenName\", \"Ghengis\");\n\t\tctx.setAttributeValue(\"homePhone\", \"+467575436521\");\n\t\tctx.setAttributeValue(\"initials\", \"G\");\n\t\tctx.setAttributeValue(\"employeeNumber\", \"00001\");\n\t\tctx.setAttributeValue(\"homePostalAddress\", \"Steppes\");\n\t\tctx.setAttributeValue(\"mail\", \"ghengis@mongolia\");\n\t\tctx.setAttributeValue(\"mobile\", \"always\");\n\t\tctx.setAttributeValue(\"o\", \"Hordes\");\n\t\tctx.setAttributeValue(\"ou\", \"Horde1\");\n\t\tctx.setAttributeValue(\"postalAddress\", \"On the Move\");\n\t\tctx.setAttributeValue(\"postalCode\", \"Changes Frequently\");\n\t\tctx.setAttributeValue(\"roomNumber\", \"Yurt 1\");\n\t\tctx.setAttributeValue(\"sn\", \"Khan\");\n\t\tctx.setAttributeValue(\"street\", \"Westward Avenue\");\n\t\tctx.setAttributeValue(\"telephoneNumber\", \"+442075436521\");\n\t\tctx.setAttributeValue(\"departmentNumber\", \"5679\");\n\t\tctx.setAttributeValue(\"title\", \"T\");\n\t\treturn ctx;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/jackson/LdapUserDetailsImplMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link org.springframework.security.ldap.jackson.LdapUserDetailsImplMixin}.\n */\npublic class LdapUserDetailsImplMixinTests {\n\n\tprivate static final String USER_PASSWORD = \"Password1234\";\n\n\tprivate static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", []]\";\n\n\t// @formatter:off\n\tprivate static final String USER_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.ldap.userdetails.LdapUserDetailsImpl\\\", \"\n\t\t\t+ \"\\\"dn\\\": \\\"ignored=ignored\\\",\"\n\t\t\t+ \"\\\"username\\\": \\\"ghengis\\\",\"\n\t\t\t+ \"\\\"password\\\": \\\"\" + USER_PASSWORD + \"\\\",\"\n\t\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t\t+ \"\\\"enabled\\\": true, \"\n\t\t\t+ \"\\\"authorities\\\": \" + AUTHORITIES_ARRAYLIST_JSON + \",\"\n\t\t\t+ \"\\\"graceLoginsRemaining\\\": \" + Integer.MAX_VALUE + \",\"\n\t\t\t+ \"\\\"timeBeforeExpiration\\\": \" + Integer.MAX_VALUE\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tLdapUserDetailsImpl p = (LdapUserDetailsImpl) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t\tString json = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(USER_JSON, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull() throws JacksonException, JSONException {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tLdapUserDetailsImpl p = (LdapUserDetailsImpl) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t\tp.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(USER_JSON.replaceAll(\"\\\"\" + USER_PASSWORD + \"\\\"\", \"null\"), actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> new JsonMapper().readValue(USER_JSON, LdapUserDetailsImpl.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tLdapUserDetailsImpl expectedAuthentication = (LdapUserDetailsImpl) mapper\n\t\t\t.mapUserFromContext(createUserContext(), \"ghengis\", AuthorityUtils.NO_AUTHORITIES);\n\n\t\tLdapUserDetailsImpl authentication = this.mapper.readValue(USER_JSON, LdapUserDetailsImpl.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());\n\t\tassertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());\n\t\tassertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());\n\t\tassertThat(authentication.getGraceLoginsRemaining())\n\t\t\t.isEqualTo(expectedAuthentication.getGraceLoginsRemaining());\n\t\tassertThat(authentication.getTimeBeforeExpiration())\n\t\t\t.isEqualTo(expectedAuthentication.getTimeBeforeExpiration());\n\t\tassertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());\n\t\tassertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());\n\t\tassertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());\n\t\tassertThat(authentication.isCredentialsNonExpired())\n\t\t\t.isEqualTo(expectedAuthentication.isCredentialsNonExpired());\n\t}\n\n\tprivate DirContextAdapter createUserContext() {\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tctx.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tctx.setAttributeValue(\"userPassword\", USER_PASSWORD);\n\t\treturn ctx;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/jackson/PersonMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.ldap.userdetails.Person;\nimport org.springframework.security.ldap.userdetails.PersonContextMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link org.springframework.security.ldap.jackson.PersonMixin}.\n */\n@SuppressWarnings(\"removal\")\npublic class PersonMixinTests {\n\n\tprivate static final String USER_PASSWORD = \"Password1234\";\n\n\tprivate static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", []]\";\n\n\t// @formatter:off\n\tprivate static final String PERSON_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.ldap.userdetails.Person\\\", \"\n\t\t\t+ \"\\\"dn\\\": \\\"ignored=ignored\\\",\"\n\t\t\t+ \"\\\"username\\\": \\\"ghengis\\\",\"\n\t\t\t+ \"\\\"password\\\": \\\"\" + USER_PASSWORD + \"\\\",\"\n\t\t\t+ \"\\\"givenName\\\": \\\"Ghengis\\\",\"\n\t\t\t+ \"\\\"sn\\\": \\\"Khan\\\",\"\n\t\t\t+ \"\\\"cn\\\": [\\\"java.util.Arrays$ArrayList\\\",[\\\"Ghengis Khan\\\"]],\"\n\t\t\t+ \"\\\"description\\\": \\\"Scary\\\",\"\n\t\t\t+ \"\\\"telephoneNumber\\\": \\\"+442075436521\\\",\"\n\t\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t\t+ \"\\\"enabled\\\": true, \"\n\t\t\t+ \"\\\"authorities\\\": \" + AUTHORITIES_ARRAYLIST_JSON + \",\"\n\t\t\t+ \"\\\"graceLoginsRemaining\\\": \" + Integer.MAX_VALUE + \",\"\n\t\t\t+ \"\\\"timeBeforeExpiration\\\": \" + Integer.MAX_VALUE\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tPersonContextMapper mapper = new PersonContextMapper();\n\t\tPerson p = (Person) mapper.mapUserFromContext(createUserContext(), \"ghengis\", AuthorityUtils.NO_AUTHORITIES);\n\n\t\tString json = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(PERSON_JSON, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull() throws JacksonException, JSONException {\n\t\tPersonContextMapper mapper = new PersonContextMapper();\n\t\tPerson p = (Person) mapper.mapUserFromContext(createUserContext(), \"ghengis\", AuthorityUtils.NO_AUTHORITIES);\n\t\tp.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(PERSON_JSON.replaceAll(\"\\\"\" + USER_PASSWORD + \"\\\"\", \"null\"), actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> new JsonMapper().readValue(PERSON_JSON, Person.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tPersonContextMapper mapper = new PersonContextMapper();\n\t\tPerson expectedAuthentication = (Person) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t\tPerson authentication = this.mapper.readValue(PERSON_JSON, Person.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());\n\t\tassertThat(authentication.getDescription()).isEqualTo(expectedAuthentication.getDescription());\n\t\tassertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());\n\t\tassertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());\n\t\tassertThat(authentication.getSn()).isEqualTo(expectedAuthentication.getSn());\n\t\tassertThat(authentication.getGivenName()).isEqualTo(expectedAuthentication.getGivenName());\n\t\tassertThat(authentication.getTelephoneNumber()).isEqualTo(expectedAuthentication.getTelephoneNumber());\n\t\tassertThat(authentication.getGraceLoginsRemaining())\n\t\t\t.isEqualTo(expectedAuthentication.getGraceLoginsRemaining());\n\t\tassertThat(authentication.getTimeBeforeExpiration())\n\t\t\t.isEqualTo(expectedAuthentication.getTimeBeforeExpiration());\n\t\tassertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());\n\t\tassertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());\n\t\tassertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());\n\t\tassertThat(authentication.isCredentialsNonExpired())\n\t\t\t.isEqualTo(expectedAuthentication.isCredentialsNonExpired());\n\t}\n\n\tprivate DirContextAdapter createUserContext() {\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tctx.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tctx.setAttributeValue(\"userPassword\", USER_PASSWORD);\n\t\tctx.setAttributeValue(\"cn\", \"Ghengis Khan\");\n\t\tctx.setAttributeValue(\"description\", \"Scary\");\n\t\tctx.setAttributeValue(\"givenName\", \"Ghengis\");\n\t\tctx.setAttributeValue(\"sn\", \"Khan\");\n\t\tctx.setAttributeValue(\"telephoneNumber\", \"+442075436521\");\n\t\treturn ctx;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.ldap.userdetails.InetOrgPerson;\nimport org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link InetOrgPersonMixin}.\n */\n@SuppressWarnings(\"removal\")\npublic class InetOrgPersonMixinTests {\n\n\tprivate static final String USER_PASSWORD = \"Password1234\";\n\n\tprivate static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", []]\";\n\n\t// @formatter:off\n\tprivate static final String INET_ORG_PERSON_JSON = \"{\\n\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.ldap.userdetails.InetOrgPerson\\\",\"\n\t\t\t+ \"\\\"dn\\\": \\\"ignored=ignored\\\",\"\n\t\t\t+ \"\\\"uid\\\": \\\"ghengis\\\",\"\n\t\t\t+ \"\\\"username\\\": \\\"ghengis\\\",\"\n\t\t\t+ \"\\\"password\\\": \\\"\" + USER_PASSWORD + \"\\\",\"\n\t\t\t+ \"\\\"carLicense\\\": \\\"HORS1\\\",\"\n\t\t\t+ \"\\\"givenName\\\": \\\"Ghengis\\\",\"\n\t\t\t+ \"\\\"destinationIndicator\\\": \\\"West\\\",\"\n\t\t\t+ \"\\\"displayName\\\": \\\"Ghengis McCann\\\",\"\n\t\t\t+ \"\\\"givenName\\\": \\\"Ghengis\\\",\"\n\t\t\t+ \"\\\"homePhone\\\": \\\"+467575436521\\\",\"\n\t\t\t+ \"\\\"initials\\\": \\\"G\\\",\"\n\t\t\t+ \"\\\"employeeNumber\\\": \\\"00001\\\",\"\n\t\t\t+ \"\\\"homePostalAddress\\\": \\\"Steppes\\\",\"\n\t\t\t+ \"\\\"mail\\\": \\\"ghengis@mongolia\\\",\"\n\t\t\t+ \"\\\"mobile\\\": \\\"always\\\",\"\n\t\t\t+ \"\\\"o\\\": \\\"Hordes\\\",\"\n\t\t\t+ \"\\\"ou\\\": \\\"Horde1\\\",\"\n\t\t\t+ \"\\\"postalAddress\\\": \\\"On the Move\\\",\"\n\t\t\t+ \"\\\"postalCode\\\": \\\"Changes Frequently\\\",\"\n\t\t\t+ \"\\\"roomNumber\\\": \\\"Yurt 1\\\",\"\n\t\t\t+ \"\\\"sn\\\": \\\"Khan\\\",\"\n\t\t\t+ \"\\\"street\\\": \\\"Westward Avenue\\\",\"\n\t\t\t+ \"\\\"telephoneNumber\\\": \\\"+442075436521\\\",\"\n\t\t\t+ \"\\\"departmentNumber\\\": \\\"5679\\\",\"\n\t\t\t+ \"\\\"title\\\": \\\"T\\\",\"\n\t\t\t+ \"\\\"cn\\\": [\\\"java.util.Arrays$ArrayList\\\",[\\\"Ghengis Khan\\\"]],\"\n\t\t\t+ \"\\\"description\\\": \\\"Scary\\\",\"\n\t\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t\t+ \"\\\"enabled\\\": true, \"\n\t\t\t+ \"\\\"authorities\\\": \" + AUTHORITIES_ARRAYLIST_JSON + \",\"\n\t\t\t+ \"\\\"graceLoginsRemaining\\\": \" + Integer.MAX_VALUE + \",\"\n\t\t\t+ \"\\\"timeBeforeExpiration\\\": \" + Integer.MAX_VALUE\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tInetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();\n\t\tInetOrgPerson p = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t\tString json = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(INET_ORG_PERSON_JSON, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tInetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();\n\t\tInetOrgPerson p = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t\tp.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(INET_ORG_PERSON_JSON.replaceAll(\"\\\"\" + USER_PASSWORD + \"\\\"\", \"null\"), actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tassertThatExceptionOfType(JsonProcessingException.class)\n\t\t\t.isThrownBy(() -> new ObjectMapper().readValue(INET_ORG_PERSON_JSON, InetOrgPerson.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tInetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();\n\t\tInetOrgPerson expectedAuthentication = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t\tInetOrgPerson authentication = this.mapper.readValue(INET_ORG_PERSON_JSON, InetOrgPerson.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getCarLicense()).isEqualTo(expectedAuthentication.getCarLicense());\n\t\tassertThat(authentication.getDepartmentNumber()).isEqualTo(expectedAuthentication.getDepartmentNumber());\n\t\tassertThat(authentication.getDestinationIndicator())\n\t\t\t.isEqualTo(expectedAuthentication.getDestinationIndicator());\n\t\tassertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());\n\t\tassertThat(authentication.getDescription()).isEqualTo(expectedAuthentication.getDescription());\n\t\tassertThat(authentication.getDisplayName()).isEqualTo(expectedAuthentication.getDisplayName());\n\t\tassertThat(authentication.getUid()).isEqualTo(expectedAuthentication.getUid());\n\t\tassertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());\n\t\tassertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());\n\t\tassertThat(authentication.getHomePhone()).isEqualTo(expectedAuthentication.getHomePhone());\n\t\tassertThat(authentication.getEmployeeNumber()).isEqualTo(expectedAuthentication.getEmployeeNumber());\n\t\tassertThat(authentication.getHomePostalAddress()).isEqualTo(expectedAuthentication.getHomePostalAddress());\n\t\tassertThat(authentication.getInitials()).isEqualTo(expectedAuthentication.getInitials());\n\t\tassertThat(authentication.getMail()).isEqualTo(expectedAuthentication.getMail());\n\t\tassertThat(authentication.getMobile()).isEqualTo(expectedAuthentication.getMobile());\n\t\tassertThat(authentication.getO()).isEqualTo(expectedAuthentication.getO());\n\t\tassertThat(authentication.getOu()).isEqualTo(expectedAuthentication.getOu());\n\t\tassertThat(authentication.getPostalAddress()).isEqualTo(expectedAuthentication.getPostalAddress());\n\t\tassertThat(authentication.getPostalCode()).isEqualTo(expectedAuthentication.getPostalCode());\n\t\tassertThat(authentication.getRoomNumber()).isEqualTo(expectedAuthentication.getRoomNumber());\n\t\tassertThat(authentication.getStreet()).isEqualTo(expectedAuthentication.getStreet());\n\t\tassertThat(authentication.getSn()).isEqualTo(expectedAuthentication.getSn());\n\t\tassertThat(authentication.getTitle()).isEqualTo(expectedAuthentication.getTitle());\n\t\tassertThat(authentication.getGivenName()).isEqualTo(expectedAuthentication.getGivenName());\n\t\tassertThat(authentication.getTelephoneNumber()).isEqualTo(expectedAuthentication.getTelephoneNumber());\n\t\tassertThat(authentication.getGraceLoginsRemaining())\n\t\t\t.isEqualTo(expectedAuthentication.getGraceLoginsRemaining());\n\t\tassertThat(authentication.getTimeBeforeExpiration())\n\t\t\t.isEqualTo(expectedAuthentication.getTimeBeforeExpiration());\n\t\tassertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());\n\t\tassertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());\n\t\tassertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());\n\t\tassertThat(authentication.isCredentialsNonExpired())\n\t\t\t.isEqualTo(expectedAuthentication.isCredentialsNonExpired());\n\t}\n\n\tprivate DirContextAdapter createUserContext() {\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tctx.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tctx.setAttributeValue(\"uid\", \"ghengis\");\n\t\tctx.setAttributeValue(\"userPassword\", USER_PASSWORD);\n\t\tctx.setAttributeValue(\"carLicense\", \"HORS1\");\n\t\tctx.setAttributeValue(\"cn\", \"Ghengis Khan\");\n\t\tctx.setAttributeValue(\"description\", \"Scary\");\n\t\tctx.setAttributeValue(\"destinationIndicator\", \"West\");\n\t\tctx.setAttributeValue(\"displayName\", \"Ghengis McCann\");\n\t\tctx.setAttributeValue(\"givenName\", \"Ghengis\");\n\t\tctx.setAttributeValue(\"homePhone\", \"+467575436521\");\n\t\tctx.setAttributeValue(\"initials\", \"G\");\n\t\tctx.setAttributeValue(\"employeeNumber\", \"00001\");\n\t\tctx.setAttributeValue(\"homePostalAddress\", \"Steppes\");\n\t\tctx.setAttributeValue(\"mail\", \"ghengis@mongolia\");\n\t\tctx.setAttributeValue(\"mobile\", \"always\");\n\t\tctx.setAttributeValue(\"o\", \"Hordes\");\n\t\tctx.setAttributeValue(\"ou\", \"Horde1\");\n\t\tctx.setAttributeValue(\"postalAddress\", \"On the Move\");\n\t\tctx.setAttributeValue(\"postalCode\", \"Changes Frequently\");\n\t\tctx.setAttributeValue(\"roomNumber\", \"Yurt 1\");\n\t\tctx.setAttributeValue(\"sn\", \"Khan\");\n\t\tctx.setAttributeValue(\"street\", \"Westward Avenue\");\n\t\tctx.setAttributeValue(\"telephoneNumber\", \"+442075436521\");\n\t\tctx.setAttributeValue(\"departmentNumber\", \"5679\");\n\t\tctx.setAttributeValue(\"title\", \"T\");\n\t\treturn ctx;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;\nimport org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link LdapUserDetailsImplMixin}.\n */\n@SuppressWarnings(\"removal\")\npublic class LdapUserDetailsImplMixinTests {\n\n\tprivate static final String USER_PASSWORD = \"Password1234\";\n\n\tprivate static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", []]\";\n\n\t// @formatter:off\n\tprivate static final String USER_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.ldap.userdetails.LdapUserDetailsImpl\\\", \"\n\t\t\t+ \"\\\"dn\\\": \\\"ignored=ignored\\\",\"\n\t\t\t+ \"\\\"username\\\": \\\"ghengis\\\",\"\n\t\t\t+ \"\\\"password\\\": \\\"\" + USER_PASSWORD + \"\\\",\"\n\t\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t\t+ \"\\\"enabled\\\": true, \"\n\t\t\t+ \"\\\"authorities\\\": \" + AUTHORITIES_ARRAYLIST_JSON + \",\"\n\t\t\t+ \"\\\"graceLoginsRemaining\\\": \" + Integer.MAX_VALUE + \",\"\n\t\t\t+ \"\\\"timeBeforeExpiration\\\": \" + Integer.MAX_VALUE\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tLdapUserDetailsImpl p = (LdapUserDetailsImpl) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t\tString json = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(USER_JSON, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tLdapUserDetailsImpl p = (LdapUserDetailsImpl) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t\tp.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(USER_JSON.replaceAll(\"\\\"\" + USER_PASSWORD + \"\\\"\", \"null\"), actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tassertThatExceptionOfType(JsonProcessingException.class)\n\t\t\t.isThrownBy(() -> new ObjectMapper().readValue(USER_JSON, LdapUserDetailsImpl.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tLdapUserDetailsImpl expectedAuthentication = (LdapUserDetailsImpl) mapper\n\t\t\t.mapUserFromContext(createUserContext(), \"ghengis\", AuthorityUtils.NO_AUTHORITIES);\n\n\t\tLdapUserDetailsImpl authentication = this.mapper.readValue(USER_JSON, LdapUserDetailsImpl.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());\n\t\tassertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());\n\t\tassertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());\n\t\tassertThat(authentication.getGraceLoginsRemaining())\n\t\t\t.isEqualTo(expectedAuthentication.getGraceLoginsRemaining());\n\t\tassertThat(authentication.getTimeBeforeExpiration())\n\t\t\t.isEqualTo(expectedAuthentication.getTimeBeforeExpiration());\n\t\tassertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());\n\t\tassertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());\n\t\tassertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());\n\t\tassertThat(authentication.isCredentialsNonExpired())\n\t\t\t.isEqualTo(expectedAuthentication.isCredentialsNonExpired());\n\t}\n\n\tprivate DirContextAdapter createUserContext() {\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tctx.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tctx.setAttributeValue(\"userPassword\", USER_PASSWORD);\n\t\treturn ctx;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/jackson2/PersonMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.jackson2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.ldap.userdetails.Person;\nimport org.springframework.security.ldap.userdetails.PersonContextMapper;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link PersonMixin}.\n */\n@SuppressWarnings(\"removal\")\npublic class PersonMixinTests {\n\n\tprivate static final String USER_PASSWORD = \"Password1234\";\n\n\tprivate static final String AUTHORITIES_ARRAYLIST_JSON = \"[\\\"java.util.Collections$UnmodifiableRandomAccessList\\\", []]\";\n\n\t// @formatter:off\n\tprivate static final String PERSON_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.ldap.userdetails.Person\\\", \"\n\t\t\t+ \"\\\"dn\\\": \\\"ignored=ignored\\\",\"\n\t\t\t+ \"\\\"username\\\": \\\"ghengis\\\",\"\n\t\t\t+ \"\\\"password\\\": \\\"\" + USER_PASSWORD + \"\\\",\"\n\t\t\t+ \"\\\"givenName\\\": \\\"Ghengis\\\",\"\n\t\t\t+ \"\\\"sn\\\": \\\"Khan\\\",\"\n\t\t\t+ \"\\\"cn\\\": [\\\"java.util.Arrays$ArrayList\\\",[\\\"Ghengis Khan\\\"]],\"\n\t\t\t+ \"\\\"description\\\": \\\"Scary\\\",\"\n\t\t\t+ \"\\\"telephoneNumber\\\": \\\"+442075436521\\\",\"\n\t\t\t+ \"\\\"accountNonExpired\\\": true, \"\n\t\t\t+ \"\\\"accountNonLocked\\\": true, \"\n\t\t\t+ \"\\\"credentialsNonExpired\\\": true, \"\n\t\t\t+ \"\\\"enabled\\\": true, \"\n\t\t\t+ \"\\\"authorities\\\": \" + AUTHORITIES_ARRAYLIST_JSON + \",\"\n\t\t\t+ \"\\\"graceLoginsRemaining\\\": \" + Integer.MAX_VALUE + \",\"\n\t\t\t+ \"\\\"timeBeforeExpiration\\\": \" + Integer.MAX_VALUE\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tPersonContextMapper mapper = new PersonContextMapper();\n\t\tPerson p = (Person) mapper.mapUserFromContext(createUserContext(), \"ghengis\", AuthorityUtils.NO_AUTHORITIES);\n\n\t\tString json = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(PERSON_JSON, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tPersonContextMapper mapper = new PersonContextMapper();\n\t\tPerson p = (Person) mapper.mapUserFromContext(createUserContext(), \"ghengis\", AuthorityUtils.NO_AUTHORITIES);\n\t\tp.eraseCredentials();\n\t\tString actualJson = this.mapper.writeValueAsString(p);\n\t\tJSONAssert.assertEquals(PERSON_JSON.replaceAll(\"\\\"\" + USER_PASSWORD + \"\\\"\", \"null\"), actualJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tassertThatExceptionOfType(JsonProcessingException.class)\n\t\t\t.isThrownBy(() -> new ObjectMapper().readValue(PERSON_JSON, Person.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tPersonContextMapper mapper = new PersonContextMapper();\n\t\tPerson expectedAuthentication = (Person) mapper.mapUserFromContext(createUserContext(), \"ghengis\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t\tPerson authentication = this.mapper.readValue(PERSON_JSON, Person.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());\n\t\tassertThat(authentication.getDescription()).isEqualTo(expectedAuthentication.getDescription());\n\t\tassertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());\n\t\tassertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());\n\t\tassertThat(authentication.getSn()).isEqualTo(expectedAuthentication.getSn());\n\t\tassertThat(authentication.getGivenName()).isEqualTo(expectedAuthentication.getGivenName());\n\t\tassertThat(authentication.getTelephoneNumber()).isEqualTo(expectedAuthentication.getTelephoneNumber());\n\t\tassertThat(authentication.getGraceLoginsRemaining())\n\t\t\t.isEqualTo(expectedAuthentication.getGraceLoginsRemaining());\n\t\tassertThat(authentication.getTimeBeforeExpiration())\n\t\t\t.isEqualTo(expectedAuthentication.getTimeBeforeExpiration());\n\t\tassertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());\n\t\tassertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());\n\t\tassertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());\n\t\tassertThat(authentication.isCredentialsNonExpired())\n\t\t\t.isEqualTo(expectedAuthentication.isCredentialsNonExpired());\n\t}\n\n\tprivate DirContextAdapter createUserContext() {\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tctx.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tctx.setAttributeValue(\"userPassword\", USER_PASSWORD);\n\t\tctx.setAttributeValue(\"cn\", \"Ghengis Khan\");\n\t\tctx.setAttributeValue(\"description\", \"Scary\");\n\t\tctx.setAttributeValue(\"givenName\", \"Ghengis\");\n\t\tctx.setAttributeValue(\"sn\", \"Khan\");\n\t\tctx.setAttributeValue(\"telephoneNumber\", \"+442075436521\");\n\t\treturn ctx;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/ppolicy/OpenLDAPIntegrationTestSuite.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\n/**\n * Test cases which run against an OpenLDAP server.\n * <p>\n * Run the script in the module root to start the server and import the data before\n * running.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class OpenLDAPIntegrationTestSuite {\n\n\tPasswordPolicyAwareContextSource cs;\n\n\t/*\n\t * @Before public void createContextSource() throws Exception { cs = new\n\t * PasswordPolicyAwareContextSource(\"ldap://localhost:22389/dc=springsource,dc=com\");\n\t * cs.setUserDn(\"cn=admin,dc=springsource,dc=com\"); cs.setPassword(\"password\");\n\t * cs.afterPropertiesSet(); }\n\t *\n\t * @Test public void simpleBindSucceeds() throws Exception { BindAuthenticator\n\t * authenticator = new BindAuthenticator(cs); authenticator.setUserDnPatterns(new\n\t * String[] {\"uid={0},ou=users\"}); LdapAuthenticationProvider provider = new\n\t * LdapAuthenticationProvider(authenticator); provider.authenticate(new\n\t * UsernamePasswordAuthenticationToken(\"luke\",\"password\")); }\n\t *\n\t * @Test(expected=LockedException.class) public void\n\t * repeatedBindWithWrongPasswordLocksAccount() throws Exception { BindAuthenticator\n\t * authenticator = new BindAuthenticator(cs); authenticator.setUserDnPatterns(new\n\t * String[] {\"uid={0},ou=users\"}); LdapAuthenticationProvider provider = new\n\t * LdapAuthenticationProvider(authenticator); for (int count=1; count < 4; count++) {\n\t * try { Authentication a = provider.authenticate(new\n\t * UsernamePasswordAuthenticationToken(\"lockme\",\"wrong\")); LdapUserDetailsImpl ud =\n\t * (LdapUserDetailsImpl) a.getPrincipal(); assertTrue(ud.getTimeBeforeExpiration() <\n\t * Integer.MAX_VALUE && ud.getTimeBeforeExpiration() > 0); } catch\n\t * (BadCredentialsException expected) { } } }\n\t *\n\t * @Test public void passwordExpiryTimeIsDetectedCorrectly() throws Exception {\n\t * BindAuthenticator authenticator = new BindAuthenticator(cs);\n\t * authenticator.setUserDnPatterns(new String[] {\"uid={0},ou=users\"});\n\t * LdapAuthenticationProvider provider = new\n\t * LdapAuthenticationProvider(authenticator); Authentication a =\n\t * provider.authenticate(new\n\t * UsernamePasswordAuthenticationToken(\"expireme\",\"password\")); PasswordPolicyData ud\n\t * = (LdapUserDetailsImpl) a.getPrincipal(); assertTrue(ud.getTimeBeforeExpiration() <\n\t * Integer.MAX_VALUE && ud.getTimeBeforeExpiration() > 0); }\n\t */\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/ppolicy/PasswordPolicyAwareContextSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport java.util.Hashtable;\n\nimport javax.naming.Context;\nimport javax.naming.NamingException;\nimport javax.naming.directory.DirContext;\nimport javax.naming.ldap.Control;\nimport javax.naming.ldap.LdapContext;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.UncategorizedLdapException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\n\n/**\n * @author Luke Taylor\n */\npublic class PasswordPolicyAwareContextSourceTests {\n\n\tprivate PasswordPolicyAwareContextSource ctxSource;\n\n\tprivate final LdapContext ctx = mock(LdapContext.class);\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\treset(this.ctx);\n\t\tthis.ctxSource = new PasswordPolicyAwareContextSource(\"ldap://blah:789/dc=springframework,dc=org\") {\n\t\t\t@Override\n\t\t\tprotected DirContext createContext(Hashtable env) {\n\t\t\t\tif (\"manager\".equals(env.get(Context.SECURITY_PRINCIPAL))) {\n\t\t\t\t\treturn PasswordPolicyAwareContextSourceTests.this.ctx;\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\t\tthis.ctxSource.setUserDn(\"manager\");\n\t\tthis.ctxSource.setPassword(\"password\");\n\t\tthis.ctxSource.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void contextIsReturnedWhenNoControlsAreSetAndReconnectIsSuccessful() {\n\t\tassertThat(this.ctxSource.getContext(\"user\", \"ignored\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void standardExceptionIsPropagatedWhenExceptionRaisedAndNoControlsAreSet() throws Exception {\n\t\twillThrow(new NamingException(\"some LDAP exception\")).given(this.ctx).reconnect(any(Control[].class));\n\t\tassertThatExceptionOfType(UncategorizedLdapException.class)\n\t\t\t.isThrownBy(() -> this.ctxSource.getContext(\"user\", \"ignored\"));\n\t}\n\n\t@Test\n\tpublic void lockedPasswordPolicyControlRaisesPasswordPolicyException() throws Exception {\n\t\tgiven(this.ctx.getResponseControls()).willReturn(new Control[] {\n\t\t\t\tnew PasswordPolicyResponseControl(PasswordPolicyResponseControlTests.OPENLDAP_LOCKED_CTRL) });\n\t\twillThrow(new NamingException(\"locked message\")).given(this.ctx).reconnect(any(Control[].class));\n\t\tassertThatExceptionOfType(PasswordPolicyException.class)\n\t\t\t.isThrownBy(() -> this.ctxSource.getContext(\"user\", \"ignored\"));\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/ppolicy/PasswordPolicyControlFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport javax.naming.ldap.Control;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class PasswordPolicyControlFactoryTests {\n\n\t@Test\n\tpublic void returnsNullForUnrecognisedOID() {\n\t\tPasswordPolicyControlFactory ctrlFactory = new PasswordPolicyControlFactory();\n\t\tControl wrongCtrl = mock(Control.class);\n\t\tgiven(wrongCtrl.getID()).willReturn(\"wrongId\");\n\t\tassertThat(ctrlFactory.getControlInstance(wrongCtrl)).isNull();\n\t}\n\n\t@Test\n\tpublic void returnsControlForCorrectOID() {\n\t\tPasswordPolicyControlFactory ctrlFactory = new PasswordPolicyControlFactory();\n\t\tControl control = mock(Control.class);\n\t\tgiven(control.getID()).willReturn(PasswordPolicyControl.OID);\n\t\tgiven(control.getEncodedValue()).willReturn(PasswordPolicyResponseControlTests.OPENLDAP_LOCKED_CTRL);\n\t\tControl result = ctrlFactory.getControlInstance(control);\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(PasswordPolicyResponseControlTests.OPENLDAP_LOCKED_CTRL).isEqualTo(result.getEncodedValue());\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/ppolicy/PasswordPolicyResponseControlTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.ppolicy;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for <tt>PasswordPolicyResponse</tt>.\n *\n * @author Luke Taylor\n */\npublic class PasswordPolicyResponseControlTests {\n\n\t/**\n\t * Useful method for obtaining data from a server for use in tests\n\t */\n\t// public void testAgainstServer() throws Exception {\n\t// Hashtable env = new Hashtable();\n\t// env.put(Context.INITIAL_CONTEXT_FACTORY, \"com.sun.jndi.ldap.LdapCtxFactory\");\n\t// env.put(Context.PROVIDER_URL, \"ldap://gorille:389/\");\n\t// env.put(Context.SECURITY_AUTHENTICATION, \"simple\");\n\t// env.put(Context.SECURITY_PRINCIPAL, \"cn=manager,dc=security,dc=org\");\n\t// env.put(Context.SECURITY_CREDENTIALS, \"security\");\n\t// env.put(LdapContext.CONTROL_FACTORIES,\n\t// PasswordPolicyControlFactory.class.getName());\n\t//\n\t// InitialLdapContext ctx = new InitialLdapContext(env, null);\n\t//\n\t// Control[] rctls = { new PasswordPolicyControl(false) };\n\t//\n\t// ctx.setRequestControls(rctls);\n\t//\n\t// try {\n\t// ctx.addToEnvironment(Context.SECURITY_PRINCIPAL,\n\t// \"uid=bob,ou=people,dc=security,dc=org\" );\n\t// ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, \"bobspassword\");\n\t// Object o = ctx.lookup(\"\");\n\t//\n\t// System.out.println(o);\n\t//\n\t// } catch(NamingException ne) {\n\t// // Ok.\n\t// System.err.println(ne);\n\t// }\n\t//\n\t// PasswordPolicyResponseControl ctrl = getPPolicyResponseCtl(ctx);\n\t// System.out.println(ctrl);\n\t//\n\t// assertThat(ctrl).isNotNull();\n\t//\n\t// //com.sun.jndi.ldap.LdapPoolManager.showStats(System.out);\n\t// }\n\t// private PasswordPolicyResponseControl getPPolicyResponseCtl(InitialLdapContext ctx)\n\t// throws NamingException {\n\t// Control[] ctrls = ctx.getResponseControls();\n\t//\n\t// for (int i = 0; ctrls != null && i < ctrls.length; i++) {\n\t// if (ctrls[i] instanceof PasswordPolicyResponseControl) {\n\t// return (PasswordPolicyResponseControl) ctrls[i];\n\t// }\n\t// }\n\t//\n\t// return null;\n\t// }\n\t@Test\n\tpublic void openLDAP33SecondsTillPasswordExpiryCtrlIsParsedCorrectly() {\n\t\tbyte[] ctrlBytes = { 0x30, 0x05, (byte) 0xA0, 0x03, (byte) 0xA0, 0x1, 0x21 };\n\t\tPasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);\n\t\tassertThat(ctrl.hasWarning()).isTrue();\n\t\tassertThat(ctrl.getTimeBeforeExpiration()).isEqualTo(33);\n\t}\n\n\t@Test\n\tpublic void openLDAP496GraceLoginsRemainingCtrlIsParsedCorrectly() {\n\t\tbyte[] ctrlBytes = { 0x30, 0x06, (byte) 0xA0, 0x04, (byte) 0xA1, 0x02, 0x01, (byte) 0xF0 };\n\t\tPasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);\n\t\tassertThat(ctrl.hasWarning()).isTrue();\n\t\tassertThat(ctrl.getGraceLoginsRemaining()).isEqualTo(496);\n\t}\n\n\tstatic final byte[] OPENLDAP_5_LOGINS_REMAINING_CTRL = { 0x30, 0x05, (byte) 0xA0, 0x03, (byte) 0xA1, 0x01, 0x05 };\n\n\t@Test\n\tpublic void openLDAP5GraceLoginsRemainingCtrlIsParsedCorrectly() {\n\t\tPasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(OPENLDAP_5_LOGINS_REMAINING_CTRL);\n\t\tassertThat(ctrl.hasWarning()).isTrue();\n\t\tassertThat(ctrl.getGraceLoginsRemaining()).isEqualTo(5);\n\t}\n\n\tstatic final byte[] OPENLDAP_LOCKED_CTRL = { 0x30, 0x03, (byte) 0xA1, 0x01, 0x01 };\n\n\t@Test\n\tpublic void openLDAPAccountLockedCtrlIsParsedCorrectly() {\n\t\tPasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(OPENLDAP_LOCKED_CTRL);\n\t\tassertThat(ctrl.hasError() && ctrl.isLocked()).isTrue();\n\t\tassertThat(ctrl.hasWarning()).isFalse();\n\t}\n\n\t@Test\n\tpublic void openLDAPPasswordExpiredCtrlIsParsedCorrectly() {\n\t\tbyte[] ctrlBytes = { 0x30, 0x03, (byte) 0xA1, 0x01, 0x00 };\n\t\tPasswordPolicyResponseControl ctrl = new PasswordPolicyResponseControl(ctrlBytes);\n\t\tassertThat(ctrl.hasError() && ctrl.isExpired()).isTrue();\n\t\tassertThat(ctrl.hasWarning()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/userdetails/InetOrgPersonTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\npublic class InetOrgPersonTests {\n\n\t@Test\n\tpublic void testUsernameIsMappedFromContextUidIfNotSet() {\n\t\tInetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());\n\t\tInetOrgPerson p = (InetOrgPerson) essence.createUserDetails();\n\t\tassertThat(p.getUsername()).isEqualTo(\"ghengis\");\n\t}\n\n\t@Test\n\tpublic void hashLookupViaEqualObjectRetrievesOriginal() {\n\t\tInetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());\n\t\tInetOrgPerson p = (InetOrgPerson) essence.createUserDetails();\n\t\tessence = new InetOrgPerson.Essence(createUserContext());\n\t\tInetOrgPerson p2 = (InetOrgPerson) essence.createUserDetails();\n\t\tSet<InetOrgPerson> set = new HashSet<>();\n\t\tset.add(p);\n\t\tassertThat(set).contains(p2);\n\t}\n\n\t@Test\n\tpublic void usernameIsDifferentFromContextUidIfSet() {\n\t\tInetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());\n\t\tessence.setUsername(\"joe\");\n\t\tInetOrgPerson p = (InetOrgPerson) essence.createUserDetails();\n\t\tassertThat(p.getUsername()).isEqualTo(\"joe\");\n\t\tassertThat(p.getUid()).isEqualTo(\"ghengis\");\n\t}\n\n\t@Test\n\tpublic void attributesMapCorrectlyFromContext() {\n\t\tInetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());\n\t\tInetOrgPerson p = (InetOrgPerson) essence.createUserDetails();\n\t\tassertThat(p.getCarLicense()).isEqualTo(\"HORS1\");\n\t\tassertThat(p.getMail()).isEqualTo(\"ghengis@mongolia\");\n\t\tassertThat(p.getGivenName()).isEqualTo(\"Ghengis\");\n\t\tassertThat(p.getSn()).isEqualTo(\"Khan\");\n\t\tassertThat(p.getCn()[0]).isEqualTo(\"Ghengis Khan\");\n\t\tassertThat(p.getEmployeeNumber()).isEqualTo(\"00001\");\n\t\tassertThat(p.getTelephoneNumber()).isEqualTo(\"+442075436521\");\n\t\tassertThat(p.getHomePostalAddress()).isEqualTo(\"Steppes\");\n\t\tassertThat(p.getHomePhone()).isEqualTo(\"+467575436521\");\n\t\tassertThat(p.getO()).isEqualTo(\"Hordes\");\n\t\tassertThat(p.getOu()).isEqualTo(\"Horde1\");\n\t\tassertThat(p.getPostalAddress()).isEqualTo(\"On the Move\");\n\t\tassertThat(p.getPostalCode()).isEqualTo(\"Changes Frequently\");\n\t\tassertThat(p.getRoomNumber()).isEqualTo(\"Yurt 1\");\n\t\tassertThat(p.getStreet()).isEqualTo(\"Westward Avenue\");\n\t\tassertThat(p.getDescription()).isEqualTo(\"Scary\");\n\t\tassertThat(p.getDisplayName()).isEqualTo(\"Ghengis McCann\");\n\t\tassertThat(p.getInitials()).isEqualTo(\"G\");\n\t}\n\n\t@Test\n\tpublic void testPasswordIsSetFromContextUserPassword() {\n\t\tInetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());\n\t\tInetOrgPerson p = (InetOrgPerson) essence.createUserDetails();\n\t\tassertThat(p.getPassword()).isEqualTo(\"pillage\");\n\t}\n\n\t@Test\n\tpublic void mappingBackToContextMatchesOriginalData() {\n\t\tDirContextAdapter ctx1 = createUserContext();\n\t\tDirContextAdapter ctx2 = new DirContextAdapter();\n\t\tctx1.setAttributeValues(\"objectclass\",\n\t\t\t\tnew String[] { \"top\", \"person\", \"organizationalPerson\", \"inetOrgPerson\" });\n\t\tctx2.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tInetOrgPerson p = (InetOrgPerson) (new InetOrgPerson.Essence(ctx1)).createUserDetails();\n\t\tp.populateContext(ctx2);\n\t\tassertThat(ctx2).isEqualTo(ctx1);\n\t}\n\n\t@Test\n\tpublic void copyMatchesOriginalData() {\n\t\tDirContextAdapter ctx1 = createUserContext();\n\t\tDirContextAdapter ctx2 = new DirContextAdapter();\n\t\tctx2.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tctx1.setAttributeValues(\"objectclass\",\n\t\t\t\tnew String[] { \"top\", \"person\", \"organizationalPerson\", \"inetOrgPerson\" });\n\t\tInetOrgPerson p = (InetOrgPerson) (new InetOrgPerson.Essence(ctx1)).createUserDetails();\n\t\tInetOrgPerson p2 = (InetOrgPerson) new InetOrgPerson.Essence(p).createUserDetails();\n\t\tp2.populateContext(ctx2);\n\t\tassertThat(ctx2).isEqualTo(ctx1);\n\t}\n\n\tprivate DirContextAdapter createUserContext() {\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tctx.setDn(LdapNameBuilder.newInstance(\"ignored=ignored\").build());\n\t\tctx.setAttributeValue(\"uid\", \"ghengis\");\n\t\tctx.setAttributeValue(\"userPassword\", \"pillage\");\n\t\tctx.setAttributeValue(\"carLicense\", \"HORS1\");\n\t\tctx.setAttributeValue(\"cn\", \"Ghengis Khan\");\n\t\tctx.setAttributeValue(\"description\", \"Scary\");\n\t\tctx.setAttributeValue(\"destinationIndicator\", \"West\");\n\t\tctx.setAttributeValue(\"displayName\", \"Ghengis McCann\");\n\t\tctx.setAttributeValue(\"givenName\", \"Ghengis\");\n\t\tctx.setAttributeValue(\"homePhone\", \"+467575436521\");\n\t\tctx.setAttributeValue(\"initials\", \"G\");\n\t\tctx.setAttributeValue(\"employeeNumber\", \"00001\");\n\t\tctx.setAttributeValue(\"homePostalAddress\", \"Steppes\");\n\t\tctx.setAttributeValue(\"mail\", \"ghengis@mongolia\");\n\t\tctx.setAttributeValue(\"mobile\", \"always\");\n\t\tctx.setAttributeValue(\"o\", \"Hordes\");\n\t\tctx.setAttributeValue(\"ou\", \"Horde1\");\n\t\tctx.setAttributeValue(\"postalAddress\", \"On the Move\");\n\t\tctx.setAttributeValue(\"postalCode\", \"Changes Frequently\");\n\t\tctx.setAttributeValue(\"roomNumber\", \"Yurt 1\");\n\t\tctx.setAttributeValue(\"roomNumber\", \"Yurt 1\");\n\t\tctx.setAttributeValue(\"sn\", \"Khan\");\n\t\tctx.setAttributeValue(\"street\", \"Westward Avenue\");\n\t\tctx.setAttributeValue(\"telephoneNumber\", \"+442075436521\");\n\t\treturn ctx;\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapAuthorityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.ldap.SpringSecurityLdapTemplate;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Filip Hanik\n */\npublic class LdapAuthorityTests {\n\n\tpublic static final String DN = \"cn=filip,ou=Users,dc=test,dc=com\";\n\n\tLdapAuthority authority;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tMap<String, List<String>> attributes = new HashMap<>();\n\t\tattributes.put(SpringSecurityLdapTemplate.DN_KEY, Arrays.asList(DN));\n\t\tattributes.put(\"mail\", Arrays.asList(\"filip@ldap.test.org\", \"filip@ldap.test2.org\"));\n\t\tthis.authority = new LdapAuthority(\"testRole\", DN, attributes);\n\t}\n\n\t@Test\n\tpublic void testGetDn() {\n\t\tassertThat(this.authority.getDn()).isEqualTo(DN);\n\t\tassertThat(this.authority.getAttributeValues(SpringSecurityLdapTemplate.DN_KEY)).isNotNull();\n\t\tassertThat(this.authority.getAttributeValues(SpringSecurityLdapTemplate.DN_KEY)).hasSize(1);\n\t\tassertThat(this.authority.getFirstAttributeValue(SpringSecurityLdapTemplate.DN_KEY)).isEqualTo(DN);\n\t}\n\n\t@Test\n\tpublic void testGetAttributes() {\n\t\tassertThat(this.authority.getAttributes()).isNotNull();\n\t\tassertThat(this.authority.getAttributeValues(\"mail\")).isNotNull();\n\t\tassertThat(this.authority.getAttributeValues(\"mail\")).hasSize(2);\n\t\tassertThat(this.authority.getFirstAttributeValue(\"mail\")).isEqualTo(\"filip@ldap.test.org\");\n\t\tassertThat(this.authority.getAttributeValues(\"mail\").get(0)).isEqualTo(\"filip@ldap.test.org\");\n\t\tassertThat(this.authority.getAttributeValues(\"mail\").get(1)).isEqualTo(\"filip@ldap.test2.org\");\n\t}\n\n\t@Test\n\tpublic void testGetAuthority() {\n\t\tassertThat(this.authority.getAuthority()).isNotNull();\n\t\tassertThat(this.authority.getAuthority()).isEqualTo(\"testRole\");\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsImplTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.CredentialsContainer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link LdapUserDetailsImpl}\n *\n * @author Joe Grandja\n */\npublic class LdapUserDetailsImplTests {\n\n\t@Test\n\tpublic void credentialsAreCleared() {\n\t\tLdapUserDetailsImpl.Essence mutableLdapUserDetails = new LdapUserDetailsImpl.Essence();\n\t\tmutableLdapUserDetails.setDn(\"uid=username1,ou=people,dc=example,dc=com\");\n\t\tmutableLdapUserDetails.setUsername(\"username1\");\n\t\tmutableLdapUserDetails.setPassword(\"password\");\n\t\tLdapUserDetails ldapUserDetails = mutableLdapUserDetails.createUserDetails();\n\t\tassertThat(ldapUserDetails).isInstanceOf(CredentialsContainer.class);\n\t\tldapUserDetails.eraseCredentials();\n\t\tassertThat(ldapUserDetails.getPassword()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsMapperTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport javax.naming.directory.BasicAttribute;\nimport javax.naming.directory.BasicAttributes;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link LdapUserDetailsMapper}.\n *\n * @author Luke Taylor\n * @author Eddú Meléndez\n */\npublic class LdapUserDetailsMapperTests {\n\n\t@Test\n\tpublic void testMultipleRoleAttributeValuesAreMappedToAuthorities() {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tmapper.setConvertToUpperCase(false);\n\t\tmapper.setRolePrefix(\"\");\n\t\tmapper.setRoleAttributes(new String[] { \"userRole\" });\n\t\tDirContextAdapter ctx = new DirContextAdapter();\n\t\tctx.setAttributeValues(\"userRole\", new String[] { \"X\", \"Y\", \"Z\" });\n\t\tctx.setAttributeValue(\"uid\", \"ani\");\n\t\tLdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, \"ani\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t\tassertThat(user.getAuthorities()).hasSize(3);\n\t}\n\n\t/**\n\t * SEC-303. Non-retrieved role attribute causes NullPointerException\n\t */\n\t@Test\n\tpublic void testNonRetrievedRoleAttributeIsIgnored() {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tmapper.setRoleAttributes(new String[] { \"userRole\", \"nonRetrievedAttribute\" });\n\t\tBasicAttributes attrs = new BasicAttributes();\n\t\tattrs.put(new BasicAttribute(\"userRole\", \"x\"));\n\t\tDirContextAdapter ctx = new DirContextAdapter(attrs, LdapNameBuilder.newInstance(\"cn=someName\").build());\n\t\tctx.setAttributeValue(\"uid\", \"ani\");\n\t\tLdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, \"ani\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(AuthorityUtils.authorityListToSet(user.getAuthorities())).contains(\"ROLE_X\");\n\t}\n\n\t@Test\n\tpublic void testPasswordAttributeIsMappedCorrectly() {\n\t\tLdapUserDetailsMapper mapper = new LdapUserDetailsMapper();\n\t\tmapper.setPasswordAttributeName(\"myappsPassword\");\n\t\tBasicAttributes attrs = new BasicAttributes();\n\t\tattrs.put(new BasicAttribute(\"myappsPassword\", \"mypassword\".getBytes()));\n\t\tDirContextAdapter ctx = new DirContextAdapter(attrs, LdapNameBuilder.newInstance(\"cn=someName\").build());\n\t\tctx.setAttributeValue(\"uid\", \"ani\");\n\t\tLdapUserDetails user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, \"ani\",\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t\tassertThat(user.getPassword()).isEqualTo(\"mypassword\");\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\nimport java.util.Set;\n\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.ldap.core.DirContextOperations;\nimport org.springframework.ldap.support.LdapNameBuilder;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.ldap.authentication.MockUserSearch;\nimport org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link LdapUserDetailsService}\n *\n * @author Luke Taylor\n */\npublic class LdapUserDetailsServiceTests {\n\n\t@Test\n\tpublic void rejectsNullSearchObject() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new LdapUserDetailsService(null, new NullLdapAuthoritiesPopulator()));\n\t}\n\n\t@Test\n\tpublic void rejectsNullAuthoritiesPopulator() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new LdapUserDetailsService(new MockUserSearch(), null));\n\t}\n\n\t@Test\n\tpublic void correctAuthoritiesAreReturned() {\n\t\tDirContextAdapter userData = new DirContextAdapter(LdapNameBuilder.newInstance(\"uid=joe\").build());\n\t\tLdapUserDetailsService service = new LdapUserDetailsService(new MockUserSearch(userData),\n\t\t\t\tnew MockAuthoritiesPopulator());\n\t\tservice.setUserDetailsMapper(new LdapUserDetailsMapper());\n\t\tUserDetails user = service.loadUserByUsername(\"doesntmatterwegetjoeanyway\");\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(user.getAuthorities());\n\t\tassertThat(authorities).hasSize(1);\n\t\tassertThat(authorities).contains(\"ROLE_FROM_POPULATOR\");\n\t}\n\n\t@Test\n\tpublic void nullPopulatorConstructorReturnsEmptyAuthoritiesList() {\n\t\tDirContextAdapter userData = new DirContextAdapter(LdapNameBuilder.newInstance(\"uid=joe\").build());\n\t\tLdapUserDetailsService service = new LdapUserDetailsService(new MockUserSearch(userData));\n\t\tUserDetails user = service.loadUserByUsername(\"doesntmatterwegetjoeanyway\");\n\t\tassertThat(user.getAuthorities()).isEmpty();\n\t}\n\n\t@NullUnmarked\n\tclass MockAuthoritiesPopulator implements LdapAuthoritiesPopulator {\n\n\t\t@Override\n\t\tpublic Collection<GrantedAuthority> getGrantedAuthorities(@NonNull DirContextOperations userCtx,\n\t\t\t\tString username) {\n\t\t\treturn AuthorityUtils.createAuthorityList(\"ROLE_FROM_POPULATOR\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/java/org/springframework/security/ldap/userdetails/UserDetailsServiceLdapAuthoritiesPopulatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.ldap.userdetails;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.ldap.core.DirContextAdapter;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.ldap.authentication.UserDetailsServiceLdapAuthoritiesPopulator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class UserDetailsServiceLdapAuthoritiesPopulatorTests {\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void delegationToUserDetailsServiceReturnsCorrectRoles() {\n\t\tUserDetailsService uds = mock(UserDetailsService.class);\n\t\tUserDetails user = mock(UserDetails.class);\n\t\tgiven(uds.loadUserByUsername(\"joe\")).willReturn(user);\n\t\tList authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tgiven(user.getAuthorities()).willReturn(authorities);\n\t\tUserDetailsServiceLdapAuthoritiesPopulator populator = new UserDetailsServiceLdapAuthoritiesPopulator(uds);\n\t\tCollection<? extends GrantedAuthority> auths = populator.getGrantedAuthorities(new DirContextAdapter(), \"joe\");\n\t\tassertThat(auths).hasSize(1);\n\t\tassertThat(AuthorityUtils.authorityListToSet(auths)).contains(\"ROLE_USER\");\n\t}\n\n}\n"
  },
  {
    "path": "ldap/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "messaging/spring-security-messaging.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n\tid 'compile-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-expression'\n\tapi 'org.springframework:spring-messaging'\n\n\toptional project(':spring-security-web')\n\toptional 'org.springframework:spring-websocket'\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(path: ':spring-security-core', configuration: 'tests')\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation \"org.slf4j:slf4j-api\"\n\ttestImplementation \"org.slf4j:log4j-over-slf4j\"\n\ttestImplementation \"ch.qos.logback:logback-classic\"\n\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/expression/DefaultMessageSecurityExpressionHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.expression.AbstractSecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionOperations;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\n\n/**\n * The default implementation of {@link SecurityExpressionHandler} which uses a\n * {@link MessageSecurityExpressionRoot}.\n *\n * @param <T> the type for the body of the Message\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 4.0\n */\npublic class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurityExpressionHandler<Message<T>> {\n\n\t@Override\n\tpublic EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMessage<T> message) {\n\t\tMessageSecurityExpressionRoot<T> root = createSecurityExpressionRoot(authentication, message);\n\t\tStandardEvaluationContext ctx = new StandardEvaluationContext(root);\n\t\tBeanResolver beanResolver = getBeanResolver();\n\t\tif (beanResolver != null) {\n\t\t\t// https://github.com/spring-projects/spring-framework/issues/35371\n\t\t\tctx.setBeanResolver(beanResolver);\n\t\t}\n\t\treturn ctx;\n\t}\n\n\t@Override\n\tprotected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,\n\t\t\tMessage<T> invocation) {\n\t\treturn createSecurityExpressionRoot(() -> authentication, invocation);\n\t}\n\n\tprivate MessageSecurityExpressionRoot<T> createSecurityExpressionRoot(\n\t\t\tSupplier<? extends Authentication> authentication, Message<T> invocation) {\n\t\tMessageSecurityExpressionRoot<T> root = new MessageSecurityExpressionRoot<>(authentication, invocation);\n\t\troot.setAuthorizationManagerFactory(getAuthorizationManagerFactory());\n\t\troot.setPermissionEvaluator(getPermissionEvaluator());\n\t\treturn root;\n\t}\n\n\t/**\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tgetDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/expression/MessageAuthorizationContextSecurityExpressionHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.messaging.access.intercept.MessageAuthorizationContext;\n\n/**\n * An expression handler for {@link MessageAuthorizationContext}.\n *\n * @author Josh Cummings\n * @since 5.8\n */\npublic final class MessageAuthorizationContextSecurityExpressionHandler\n\t\timplements SecurityExpressionHandler<MessageAuthorizationContext<?>> {\n\n\tprivate final SecurityExpressionHandler<Message<?>> delegate;\n\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic MessageAuthorizationContextSecurityExpressionHandler() {\n\t\tthis(new DefaultMessageSecurityExpressionHandler());\n\t}\n\n\tpublic MessageAuthorizationContextSecurityExpressionHandler(\n\t\t\tSecurityExpressionHandler<Message<?>> expressionHandler) {\n\t\tthis.delegate = expressionHandler;\n\t}\n\n\t@Override\n\tpublic ExpressionParser getExpressionParser() {\n\t\treturn this.delegate.getExpressionParser();\n\t}\n\n\t@Override\n\tpublic EvaluationContext createEvaluationContext(@Nullable Authentication authentication,\n\t\t\tMessageAuthorizationContext<?> message) {\n\t\treturn createEvaluationContext(() -> authentication, message);\n\t}\n\n\t@Override\n\tpublic EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMessageAuthorizationContext<?> message) {\n\t\tEvaluationContext context = this.delegate.createEvaluationContext(authentication, message.getMessage());\n\t\tMap<String, String> variables = message.getVariables();\n\t\tif (variables != null) {\n\t\t\tfor (Map.Entry<String, String> entry : variables.entrySet()) {\n\t\t\t\tcontext.setVariable(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\t\treturn context;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/expression/MessageExpressionAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ExpressionAuthorizationDecision;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.messaging.access.intercept.MessageAuthorizationContext;\nimport org.springframework.util.Assert;\n\n/**\n * An expression-based {@link AuthorizationManager} that determines the access by\n * evaluating the provided expression.\n *\n * @since 7.1\n */\npublic final class MessageExpressionAuthorizationManager\n\t\timplements AuthorizationManager<MessageAuthorizationContext<?>> {\n\n\tprivate final SecurityExpressionHandler<MessageAuthorizationContext<?>> expressionHandler;\n\n\tprivate final Expression expression;\n\n\t/**\n\t * Creates an instance.\n\t * @param expressionString the raw expression string to parse\n\t */\n\tpublic MessageExpressionAuthorizationManager(String expressionString) {\n\t\tthis(new MessageAuthorizationContextSecurityExpressionHandler(), expressionString);\n\t}\n\n\tprivate MessageExpressionAuthorizationManager(\n\t\t\tSecurityExpressionHandler<MessageAuthorizationContext<?>> expressionHandler, String expressionString) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tAssert.hasText(expressionString, \"expressionString cannot be empty\");\n\t\tthis.expressionHandler = expressionHandler;\n\t\tthis.expression = expressionHandler.getExpressionParser().parseExpression(expressionString);\n\t}\n\n\t/**\n\t * Use a {@link MessageAuthorizationContextSecurityExpressionHandler} to create\n\t * {@link MessageExpressionAuthorizationManager} instances.\n\t * @return a {@link Builder} for constructing\n\t * {@link MessageExpressionAuthorizationManager} instances\n\t * @since 7.1\n\t */\n\tpublic static Builder withDefaults() {\n\t\treturn new Builder(new MessageAuthorizationContextSecurityExpressionHandler());\n\t}\n\n\t/**\n\t * Use this {@link SecurityExpressionHandler} to create\n\t * {@link MessageExpressionAuthorizationManager} instances.\n\t * @param expressionHandler the expression handler to use\n\t * @return a {@link Builder} for constructing\n\t * {@link MessageExpressionAuthorizationManager} instances\n\t * @since 7.1\n\t */\n\tpublic static Builder withSecurityExpressionHandler(\n\t\t\tSecurityExpressionHandler<MessageAuthorizationContext<?>> expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\treturn new Builder(expressionHandler);\n\t}\n\n\t/**\n\t * Determines the access by evaluating the provided expression.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param context the {@link MessageAuthorizationContext} to check\n\t * @return an {@link ExpressionAuthorizationDecision} based on the evaluated\n\t * expression\n\t */\n\t@Override\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMessageAuthorizationContext<?> context) {\n\t\tEvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, context);\n\t\tboolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx);\n\t\treturn new ExpressionAuthorizationDecision(granted, this.expression);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"MessageExpressionAuthorizationManager[expression='\" + this.expression + \"']\";\n\t}\n\n\t/**\n\t * A {@link Builder} for constructing {@link MessageExpressionAuthorizationManager}\n\t * instances.\n\t *\n\t * <p>\n\t * May be reused to create multiple instances.\n\t *\n\t * @since 7.1\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final SecurityExpressionHandler<MessageAuthorizationContext<?>> expressionHandler;\n\n\t\tprivate Builder(SecurityExpressionHandler<MessageAuthorizationContext<?>> expressionHandler) {\n\t\t\tthis.expressionHandler = expressionHandler;\n\t\t}\n\n\t\t/**\n\t\t * Create a {@link MessageExpressionAuthorizationManager} using this\n\t\t * {@code expression}.\n\t\t * @param expression the expression to evaluate\n\t\t * @return the resulting {@link AuthorizationManager}\n\t\t */\n\t\tpublic MessageExpressionAuthorizationManager expression(String expression) {\n\t\t\treturn new MessageExpressionAuthorizationManager(this.expressionHandler, expression);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/expression/MessageSecurityExpressionRoot.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.core.Authentication;\n\n/**\n * The {@link SecurityExpressionRoot} used for {@link Message} expressions.\n *\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @author Steve Riesenberg\n * @since 4.0\n */\npublic class MessageSecurityExpressionRoot<T> extends SecurityExpressionRoot<Message<T>> {\n\n\tpublic final Message<T> message;\n\n\tpublic MessageSecurityExpressionRoot(Authentication authentication, Message<T> message) {\n\t\tthis(() -> authentication, message);\n\t}\n\n\t/**\n\t * Creates an instance for the given {@link Supplier} of the {@link Authentication}\n\t * and {@link Message}.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to use\n\t * @param message the {@link Message} to use\n\t * @since 5.8\n\t */\n\tpublic MessageSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMessage<T> message) {\n\t\tsuper(authentication, message);\n\t\tthis.message = message;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/expression/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Security expression support for {@link org.springframework.messaging.Message}.\n */\n@NullMarked\npackage org.springframework.security.messaging.access.expression;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport java.util.function.Supplier;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.support.ChannelInterceptor;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * Authorizes {@link Message} resources using the provided {@link AuthorizationManager}\n *\n * @author Josh Cummings\n * @since 5.8\n */\npublic final class AuthorizationChannelInterceptor implements ChannelInterceptor {\n\n\tprivate Supplier<Authentication> authentication = getAuthentication(\n\t\t\tSecurityContextHolder.getContextHolderStrategy());\n\n\tprivate final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate final AuthorizationManager<Message<?>> preSendAuthorizationManager;\n\n\tprivate AuthorizationEventPublisher eventPublisher = new NoopAuthorizationEventPublisher();\n\n\t/**\n\t * Creates a new instance\n\t * @param preSendAuthorizationManager the {@link AuthorizationManager} to use. Cannot\n\t * be null.\n\t *\n\t */\n\tpublic AuthorizationChannelInterceptor(AuthorizationManager<Message<?>> preSendAuthorizationManager) {\n\t\tAssert.notNull(preSendAuthorizationManager, \"preSendAuthorizationManager cannot be null\");\n\t\tthis.preSendAuthorizationManager = preSendAuthorizationManager;\n\t}\n\n\t@Override\n\tpublic Message<?> preSend(Message<?> message, MessageChannel channel) {\n\t\tthis.logger.debug(LogMessage.of(() -> \"Authorizing message send\"));\n\t\tAuthorizationResult result = this.preSendAuthorizationManager.authorize(this.authentication, message);\n\t\tthis.eventPublisher.publishAuthorizationEvent(this.authentication, message, result);\n\t\tif (result == null || !result.isGranted()) { // default deny\n\t\t\tthis.logger.debug(LogMessage.of(() -> \"Failed to authorize message with authorization manager \"\n\t\t\t\t\t+ this.preSendAuthorizationManager + \" and result \" + result));\n\t\t\tthrow new AccessDeniedException(\"Access Denied\");\n\t\t}\n\t\tthis.logger.debug(LogMessage.of(() -> \"Authorized message send\"));\n\t\treturn message;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.authentication = getAuthentication(securityContextHolderStrategy);\n\t}\n\n\t/**\n\t * Use this {@link AuthorizationEventPublisher} to publish the\n\t * {@link AuthorizationManager} result.\n\t * @param eventPublisher\n\t */\n\tpublic void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {\n\t\tAssert.notNull(eventPublisher, \"eventPublisher cannot be null\");\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\tprivate Supplier<Authentication> getAuthentication(SecurityContextHolderStrategy strategy) {\n\t\treturn () -> {\n\t\t\tAuthentication authentication = strategy.getContext().getAuthentication();\n\t\t\tif (authentication == null) {\n\t\t\t\tthrow new AuthenticationCredentialsNotFoundException(\n\t\t\t\t\t\t\"An Authentication object was not found in the SecurityContext\");\n\t\t\t}\n\t\t\treturn authentication;\n\t\t};\n\t}\n\n\tprivate static class NoopAuthorizationEventPublisher implements AuthorizationEventPublisher {\n\n\t\t@Override\n\t\tpublic <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,\n\t\t\t\t@Nullable AuthorizationResult result) {\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageAuthorizationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.messaging.Message;\n\n/**\n * An {@link Message} authorization context.\n *\n * @author Josh Cummings\n * @since 5.8\n */\npublic final class MessageAuthorizationContext<T> {\n\n\tprivate final Message<T> message;\n\n\tprivate final Map<String, String> variables;\n\n\t/**\n\t * Creates an instance.\n\t * @param message the {@link HttpServletRequest} to use\n\t */\n\tpublic MessageAuthorizationContext(Message<T> message) {\n\t\tthis(message, Collections.emptyMap());\n\t}\n\n\t/**\n\t * Creates an instance.\n\t * @param message the {@link HttpServletRequest} to use\n\t * @param variables a map containing key-value pairs representing extracted variable\n\t * names and variable values\n\t */\n\tpublic MessageAuthorizationContext(Message<T> message, Map<String, String> variables) {\n\t\tthis.message = message;\n\t\tthis.variables = variables;\n\t}\n\n\t/**\n\t * Returns the {@link HttpServletRequest}.\n\t * @return the {@link HttpServletRequest} to use\n\t */\n\tpublic Message<T> getMessage() {\n\t\treturn this.message;\n\t}\n\n\t/**\n\t * Returns the extracted variable values where the key is the variable name and the\n\t * value is the variable value.\n\t * @return a map containing key-value pairs representing extracted variable names and\n\t * variable values\n\t */\n\tpublic Map<String, String> getVariables() {\n\t\treturn this.variables;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.SingleResultAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\nimport org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;\nimport org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\npublic final class MessageMatcherDelegatingAuthorizationManager implements AuthorizationManager<Message<?>> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final List<Entry<AuthorizationManager<MessageAuthorizationContext<?>>>> mappings;\n\n\tprivate MessageMatcherDelegatingAuthorizationManager(\n\t\t\tList<Entry<AuthorizationManager<MessageAuthorizationContext<?>>>> mappings) {\n\t\tAssert.notEmpty(mappings, \"mappings cannot be empty\");\n\t\tthis.mappings = mappings;\n\t}\n\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tMessage<?> message) {\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(LogMessage.format(\"Authorizing message\"));\n\t\t}\n\t\tfor (Entry<AuthorizationManager<MessageAuthorizationContext<?>>> mapping : this.mappings) {\n\t\t\tMessageMatcher<?> matcher = mapping.getMessageMatcher();\n\t\t\tMessageAuthorizationContext<?> authorizationContext = authorizationContext(matcher, message);\n\t\t\tif (authorizationContext != null) {\n\t\t\t\tAuthorizationManager<MessageAuthorizationContext<?>> manager = mapping.getEntry();\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(LogMessage.format(\"Checking authorization on message using %s\", manager));\n\t\t\t\t}\n\t\t\t\treturn manager.authorize(authentication, authorizationContext);\n\t\t\t}\n\t\t}\n\t\tthis.logger.trace(\"Abstaining since did not find matching MessageMatcher\");\n\t\treturn null;\n\t}\n\n\tprivate @Nullable MessageAuthorizationContext<?> authorizationContext(MessageMatcher<?> matcher,\n\t\t\tMessage<?> message) {\n\t\tMessageMatcher.MatchResult matchResult = matcher.matcher((Message) message);\n\t\tif (!matchResult.isMatch()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(matchResult.getVariables())) {\n\t\t\treturn new MessageAuthorizationContext<>(message, matchResult.getVariables());\n\t\t}\n\n\t\treturn new MessageAuthorizationContext<>(message);\n\t}\n\n\t/**\n\t * Creates a builder for {@link MessageMatcherDelegatingAuthorizationManager}.\n\t * @return the new {@link Builder} instance\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * A builder for {@link MessageMatcherDelegatingAuthorizationManager}.\n\t */\n\tpublic static final class Builder implements ApplicationContextAware {\n\n\t\tprivate final List<Entry<AuthorizationManager<MessageAuthorizationContext<?>>>> mappings = new ArrayList<>();\n\n\t\tprivate PathPatternMessageMatcher.Builder messageMatcherBuilder = PathPatternMessageMatcher.withDefaults();\n\n\t\tpublic Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Maps any {@link Message} to a security expression.\n\t\t * @return the Expression to associate\n\t\t */\n\t\tpublic Builder.Constraint anyMessage() {\n\t\t\treturn matchers(MessageMatcher.ANY_MESSAGE);\n\t\t}\n\n\t\t/**\n\t\t * Maps any {@link Message} that has a null SimpMessageHeaderAccessor destination\n\t\t * header (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, UNSUBSCRIBE, DISCONNECT,\n\t\t * DISCONNECT_ACK, OTHER)\n\t\t * @return the Expression to associate\n\t\t */\n\t\tpublic Builder.Constraint nullDestMatcher() {\n\t\t\treturn matchers(PathPatternMessageMatcher.NULL_DESTINATION_MATCHER);\n\t\t}\n\n\t\t/**\n\t\t * Maps a {@link List} of {@link SimpMessageTypeMatcher} instances.\n\t\t * @param typesToMatch the {@link SimpMessageType} instance to match on\n\t\t * @return the {@link Builder.Constraint} associated to the matchers.\n\t\t */\n\t\tpublic Builder.Constraint simpTypeMatchers(SimpMessageType... typesToMatch) {\n\t\t\tMessageMatcher<?>[] typeMatchers = new MessageMatcher<?>[typesToMatch.length];\n\t\t\tfor (int i = 0; i < typesToMatch.length; i++) {\n\t\t\t\tSimpMessageType typeToMatch = typesToMatch[i];\n\t\t\t\ttypeMatchers[i] = new SimpMessageTypeMatcher(typeToMatch);\n\t\t\t}\n\t\t\treturn matchers(typeMatchers);\n\t\t}\n\n\t\t/**\n\t\t * Maps a {@link List} of {@link PathPatternMessageMatcher}s instances without\n\t\t * regard to the {@link SimpMessageType}. If no destination is found on the\n\t\t * Message, then the Matcher returns false.\n\t\t * @param patterns the patterns to create {@code MessageMatcher}s from.\n\t\t */\n\t\tpublic Builder.Constraint simpDestMatchers(String... patterns) {\n\t\t\treturn simpDestMatchers(null, patterns);\n\t\t}\n\n\t\t/**\n\t\t * Maps a {@link List} of {@link PathPatternMessageMatcher}s instances that match\n\t\t * on {@code SimpMessageType.MESSAGE}. If no destination is found on the Message,\n\t\t * then the Matcher returns false.\n\t\t * @param patterns the patterns to create {@code MessageMatcher}s from.\n\t\t */\n\t\tpublic Builder.Constraint simpMessageDestMatchers(String... patterns) {\n\t\t\treturn simpDestMatchers(SimpMessageType.MESSAGE, patterns);\n\t\t}\n\n\t\t/**\n\t\t * Maps a {@link List} of {@link PathPatternMessageMatcher}s instances that match\n\t\t * on {@code SimpMessageType.SUBSCRIBE}. If no destination is found on the\n\t\t * Message, then the Matcher returns false.\n\t\t * @param patterns the patterns to create {@code MessageMatcher}s from.\n\t\t */\n\t\tpublic Builder.Constraint simpSubscribeDestMatchers(String... patterns) {\n\t\t\treturn simpDestMatchers(SimpMessageType.SUBSCRIBE, patterns);\n\t\t}\n\n\t\t/**\n\t\t * Maps a {@link List} of {@link PathPatternMessageMatcher} instances. If no\n\t\t * destination is found on the Message, then the Matcher returns false.\n\t\t * @param type the {@link SimpMessageType} to match on. If null, the\n\t\t * {@link SimpMessageType} is not considered for matching.\n\t\t * @param patterns the patterns to create {@code MessageMatcher}s from.\n\t\t * @return the {@link Builder.Constraint} that is associated to the\n\t\t * {@link MessageMatcher}\n\t\t */\n\t\tprivate Builder.Constraint simpDestMatchers(@Nullable SimpMessageType type, String... patterns) {\n\t\t\tList<MessageMatcher<?>> matchers = new ArrayList<>(patterns.length);\n\t\t\tfor (String pattern : patterns) {\n\t\t\t\tMessageMatcher<Object> matcher = this.messageMatcherBuilder.matcher(type, pattern);\n\t\t\t\tmatchers.add(matcher);\n\t\t\t}\n\t\t\treturn new Builder.Constraint(matchers);\n\t\t}\n\n\t\t/**\n\t\t * Maps a {@link List} of {@link MessageMatcher} instances to a security\n\t\t * expression.\n\t\t * @param matchers the {@link MessageMatcher} instances to map.\n\t\t * @return The {@link Builder.Constraint} that is associated to the\n\t\t * {@link MessageMatcher} instances\n\t\t */\n\t\tpublic Builder.Constraint matchers(MessageMatcher<?>... matchers) {\n\t\t\tList<MessageMatcher<?>> builders = new ArrayList<>(matchers.length);\n\t\t\tfor (MessageMatcher<?> matcher : matchers) {\n\t\t\t\tbuilders.add(matcher);\n\t\t\t}\n\t\t\treturn new Builder.Constraint(builders);\n\t\t}\n\n\t\tpublic AuthorizationManager<Message<?>> build() {\n\t\t\treturn new MessageMatcherDelegatingAuthorizationManager(this.mappings);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\t\tthis.messageMatcherBuilder = context.getBeanProvider(PathPatternMessageMatcher.Builder.class)\n\t\t\t\t.getIfUnique(PathPatternMessageMatcher::withDefaults);\n\t\t}\n\n\t\t/**\n\t\t * Represents the security constraint to be applied to the {@link MessageMatcher}\n\t\t * instances.\n\t\t */\n\t\tpublic final class Constraint {\n\n\t\t\tprivate final List<? extends MessageMatcher<?>> messageMatchers;\n\n\t\t\t/**\n\t\t\t * Creates a new instance\n\t\t\t * @param messageMatchers the {@link MessageMatcher} instances to map to this\n\t\t\t * constraint\n\t\t\t */\n\t\t\tprivate Constraint(List<? extends MessageMatcher<?>> messageMatchers) {\n\t\t\t\tAssert.notEmpty(messageMatchers, \"messageMatchers cannot be null or empty\");\n\t\t\t\tthis.messageMatchers = messageMatchers;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Shortcut for specifying {@link Message} instances require a particular\n\t\t\t * role. If you do not want to have \"ROLE_\" automatically inserted see\n\t\t\t * {@link #hasAuthority(String)}.\n\t\t\t * @param role the role to require (i.e. USER, ADMIN, etc). Note, it should\n\t\t\t * not start with \"ROLE_\" as this is automatically inserted.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder hasRole(String role) {\n\t\t\t\treturn access(AuthorityAuthorizationManager.hasRole(role));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Shortcut for specifying {@link Message} instances require any of a number\n\t\t\t * of roles. If you do not want to have \"ROLE_\" automatically inserted see\n\t\t\t * {@link #hasAnyAuthority(String...)}\n\t\t\t * @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should\n\t\t\t * not start with \"ROLE_\" as this is automatically inserted.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder hasAnyRole(String... roles) {\n\t\t\t\treturn access(AuthorityAuthorizationManager.hasAnyRole(roles));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that {@link Message} instances require a particular authority.\n\t\t\t * @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN,\n\t\t\t * etc).\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder hasAuthority(String authority) {\n\t\t\t\treturn access(AuthorityAuthorizationManager.hasAuthority(authority));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that {@link Message} instances requires any of a number\n\t\t\t * authorities.\n\t\t\t * @param authorities the requests require at least one of the authorities\n\t\t\t * (i.e. \"ROLE_USER\",\"ROLE_ADMIN\" would mean either \"ROLE_USER\" or\n\t\t\t * \"ROLE_ADMIN\" is required).\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder hasAnyAuthority(String... authorities) {\n\t\t\t\treturn access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that Messages are allowed by anyone.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder permitAll() {\n\t\t\t\treturn access(SingleResultAuthorizationManager.permitAll());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that Messages are not allowed by anyone.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder denyAll() {\n\t\t\t\treturn access(SingleResultAuthorizationManager.denyAll());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that Messages are allowed by any authenticated user.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder authenticated() {\n\t\t\t\treturn access(AuthenticatedAuthorizationManager.authenticated());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that Messages are allowed by users who have authenticated and were\n\t\t\t * not \"remembered\".\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t * @since 5.8\n\t\t\t */\n\t\t\tpublic Builder fullyAuthenticated() {\n\t\t\t\treturn access(AuthenticatedAuthorizationManager.fullyAuthenticated());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that Messages are allowed by users that have been remembered.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t * @since 5.8\n\t\t\t */\n\t\t\tpublic Builder rememberMe() {\n\t\t\t\treturn access(AuthenticatedAuthorizationManager.rememberMe());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that Messages are allowed by anonymous users.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t * @since 5.8\n\t\t\t */\n\t\t\tpublic Builder anonymous() {\n\t\t\t\treturn access(AuthenticatedAuthorizationManager.anonymous());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Allows specifying that Messages are secured by an arbitrary expression\n\t\t\t * @param authorizationManager the {@link AuthorizationManager} to secure the\n\t\t\t * destinations\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder access(AuthorizationManager<MessageAuthorizationContext<?>> authorizationManager) {\n\t\t\t\tfor (MessageMatcher<?> messageMatcher : this.messageMatchers) {\n\t\t\t\t\tBuilder.this.mappings.add(new Entry<>(messageMatcher, authorizationManager));\n\t\t\t\t}\n\t\t\t\treturn Builder.this;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tprivate static final class Entry<T> {\n\n\t\tprivate final MessageMatcher<?> messageMatcher;\n\n\t\tprivate final T entry;\n\n\t\tEntry(MessageMatcher requestMatcher, T entry) {\n\t\t\tthis.messageMatcher = requestMatcher;\n\t\t\tthis.entry = entry;\n\t\t}\n\n\t\tMessageMatcher<?> getMessageMatcher() {\n\t\t\treturn this.messageMatcher;\n\t\t}\n\n\t\tT getEntry() {\n\t\t\treturn this.entry;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/access/intercept/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Authorization support for {@link org.springframework.messaging.Message}.\n */\n@NullMarked\npackage org.springframework.security.messaging.access.intercept;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.context;\n\nimport java.lang.annotation.Annotation;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Allows resolving the {@link Authentication#getPrincipal()} using the\n * {@link AuthenticationPrincipal} annotation. For example, the following\n * {@link Controller}:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;MessageMapping(\"/im\")\n *     public void im(@AuthenticationPrincipal CustomUser customUser) {\n *         // do something with CustomUser\n *     }\n * }\n * </pre>\n *\n * <p>\n * Will resolve the CustomUser argument using {@link Authentication#getPrincipal()} from\n * the {@link SecurityContextHolder}. If the {@link Authentication} or\n * {@link Authentication#getPrincipal()} is null, it will return null. If the types do not\n * match, null will be returned unless\n * {@link AuthenticationPrincipal#errorOnInvalidType()} is true in which case a\n * {@link ClassCastException} will be thrown.\n *\n * <p>\n * Alternatively, users can create a custom meta annotation as shown below:\n *\n * <pre>\n * &#064;Target({ ElementType.PARAMETER })\n * &#064;Retention(RetentionPolicy.RUNTIME)\n * &#064;AuthenticationPrincipal\n * public @interface CurrentUser {\n * }\n * </pre>\n *\n * <p>\n * The custom annotation can then be used instead. For example:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;MessageMapping(\"/im\")\n *     public void im(@CurrentUser CustomUser customUser) {\n *         // do something with CustomUser\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @author DingHao\n * @since 4.0\n */\npublic final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate ExpressionParser parser = new SpelExpressionParser();\n\n\tprivate final Class<AuthenticationPrincipal> annotationType = AuthenticationPrincipal.class;\n\n\tprivate SecurityAnnotationScanner<AuthenticationPrincipal> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(this.annotationType);\n\n\tprivate boolean useAnnotationTemplate = false;\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn findMethodAnnotation(parameter) != null;\n\t}\n\n\t@Override\n\tpublic @Nullable Object resolveArgument(MethodParameter parameter, Message<?> message) {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\treturn null;\n\t\t}\n\t\tObject principal = authentication.getPrincipal();\n\t\tAuthenticationPrincipal authPrincipal = findMethodAnnotation(parameter);\n\t\tAssert.notNull(authPrincipal, \"AuthenticationPrincipal must not be null. Run supports first\");\n\t\tString expressionToParse = authPrincipal.expression();\n\t\tif (StringUtils.hasLength(expressionToParse)) {\n\t\t\tStandardEvaluationContext context = new StandardEvaluationContext();\n\t\t\tcontext.setRootObject(principal);\n\t\t\tcontext.setVariable(\"this\", principal);\n\t\t\tExpression expression = this.parser.parseExpression(expressionToParse);\n\t\t\tprincipal = expression.getValue(context);\n\t\t}\n\t\tif (principal != null && !ClassUtils.isAssignable(parameter.getParameterType(), principal.getClass())) {\n\t\t\tif (authPrincipal.errorOnInvalidType()) {\n\t\t\t\tthrow new ClassCastException(principal + \" is not assignable to \" + parameter.getParameterType());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn principal;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Configure AuthenticationPrincipal template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param templateDefaults - whether to resolve AuthenticationPrincipal templates\n\t * parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.useAnnotationTemplate = templateDefaults != null;\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(AuthenticationPrincipal.class, templateDefaults);\n\t}\n\n\t/**\n\t * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.\n\t * {@link MethodParameter}\n\t * @param parameter the {@link MethodParameter} to search for an {@link Annotation}\n\t * @return the {@link Annotation} that was found or null.\n\t */\n\tprivate @Nullable AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) {\n\t\tif (this.useAnnotationTemplate) {\n\t\t\treturn this.scanner.scan(parameter.getParameter());\n\t\t}\n\t\tAuthenticationPrincipal annotation = parameter.getParameterAnnotation(this.annotationType);\n\t\tif (annotation != null) {\n\t\t\treturn annotation;\n\t\t}\n\t\tAnnotation[] annotationsToSearch = parameter.getParameterAnnotations();\n\t\tfor (Annotation toSearch : annotationsToSearch) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), this.annotationType);\n\t\t\tif (annotation != null) {\n\t\t\t\treturn MergedAnnotations.from(toSearch).get(this.annotationType).synthesize();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/context/SecurityContextChannelInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.context;\n\nimport java.util.Stack;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.MessageHandler;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.support.ChannelInterceptor;\nimport org.springframework.messaging.support.ExecutorChannelInterceptor;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Creates a {@link ExecutorChannelInterceptor} that will obtain the\n * {@link Authentication} from the specified {@link Message#getHeaders()}.\n * </p>\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic final class SecurityContextChannelInterceptor implements ExecutorChannelInterceptor, ChannelInterceptor {\n\n\tprivate static final ThreadLocal<Stack<SecurityContext>> originalContext = new ThreadLocal<>();\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate SecurityContext empty = this.securityContextHolderStrategy.createEmptyContext();\n\n\tprivate final String authenticationHeaderName;\n\n\tprivate Authentication anonymous = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\t/**\n\t * Creates a new instance using the header of the name\n\t * {@link SimpMessageHeaderAccessor#USER_HEADER}.\n\t */\n\tpublic SecurityContextChannelInterceptor() {\n\t\tthis(SimpMessageHeaderAccessor.USER_HEADER);\n\t}\n\n\t/**\n\t * Creates a new instance that uses the specified header to obtain the\n\t * {@link Authentication}.\n\t * @param authenticationHeaderName the header name to obtain the\n\t * {@link Authentication}. Cannot be null.\n\t */\n\tpublic SecurityContextChannelInterceptor(String authenticationHeaderName) {\n\t\tAssert.notNull(authenticationHeaderName, \"authenticationHeaderName cannot be null\");\n\t\tthis.authenticationHeaderName = authenticationHeaderName;\n\t}\n\n\t/**\n\t * Allows setting the Authentication used for anonymous authentication. Default is:\n\t *\n\t * <pre>\n\t * new AnonymousAuthenticationToken(&quot;key&quot;, &quot;anonymous&quot;,\n\t * \t\tAuthorityUtils.createAuthorityList(&quot;ROLE_ANONYMOUS&quot;));\n\t * </pre>\n\t * @param authentication the Authentication used for anonymous authentication. Cannot\n\t * be null.\n\t */\n\tpublic void setAnonymousAuthentication(Authentication authentication) {\n\t\tAssert.notNull(authentication, \"authentication cannot be null\");\n\t\tthis.anonymous = authentication;\n\t}\n\n\t@Override\n\tpublic Message<?> preSend(Message<?> message, MessageChannel channel) {\n\t\tsetup(message);\n\t\treturn message;\n\t}\n\n\t@Override\n\tpublic void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, @Nullable Exception ex) {\n\t\tcleanup();\n\t}\n\n\t@Override\n\tpublic Message<?> beforeHandle(Message<?> message, MessageChannel channel, MessageHandler handler) {\n\t\tsetup(message);\n\t\treturn message;\n\t}\n\n\t@Override\n\tpublic void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler,\n\t\t\t@Nullable Exception ex) {\n\t\tcleanup();\n\t}\n\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {\n\t\tthis.securityContextHolderStrategy = strategy;\n\t\tthis.empty = this.securityContextHolderStrategy.createEmptyContext();\n\t}\n\n\tprivate void setup(Message<?> message) {\n\t\tSecurityContext currentContext = this.securityContextHolderStrategy.getContext();\n\t\tStack<SecurityContext> contextStack = originalContext.get();\n\t\tif (contextStack == null) {\n\t\t\tcontextStack = new Stack<>();\n\t\t\toriginalContext.set(contextStack);\n\t\t}\n\t\tcontextStack.push(currentContext);\n\t\tObject user = message.getHeaders().get(this.authenticationHeaderName);\n\t\tAuthentication authentication = getAuthentication(user);\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t}\n\n\tprivate Authentication getAuthentication(@Nullable Object user) {\n\t\tif ((user instanceof Authentication)) {\n\t\t\treturn (Authentication) user;\n\t\t}\n\t\treturn this.anonymous;\n\t}\n\n\tprivate void cleanup() {\n\t\tStack<SecurityContext> contextStack = originalContext.get();\n\t\tif (contextStack == null || contextStack.isEmpty()) {\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\toriginalContext.remove();\n\t\t\treturn;\n\t\t}\n\t\tSecurityContext context = contextStack.pop();\n\t\ttry {\n\t\t\tif (SecurityContextChannelInterceptor.this.empty.equals(context)) {\n\t\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\t\toriginalContext.remove();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\t\t}\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/context/SecurityContextPropagationChannelInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.context;\n\nimport java.util.Stack;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.MessageHandler;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.support.ExecutorChannelInterceptor;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link ExecutorChannelInterceptor} that takes an {@link Authentication} from the\n * current {@link SecurityContext} (if any) in the\n * {@link #preSend(Message, MessageChannel)} callback and stores it into an\n * {@link #authenticationHeaderName} message header. Then sets the context from this\n * header in the {@link #beforeHandle(Message, MessageChannel, MessageHandler)} and\n * {@link #postReceive(Message, MessageChannel)} both of which typically happen on a\n * different thread.\n * <p>\n * Note: cannot be used in combination with a {@link SecurityContextChannelInterceptor} on\n * the same channel since both these interceptors modify a security context on a handling\n * and receiving operations.\n *\n * @author Artem Bilan\n * @since 6.2\n * @see SecurityContextChannelInterceptor\n */\npublic final class SecurityContextPropagationChannelInterceptor implements ExecutorChannelInterceptor {\n\n\tprivate static final ThreadLocal<Stack<SecurityContext>> originalContext = new ThreadLocal<>();\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate SecurityContext empty = this.securityContextHolderStrategy.createEmptyContext();\n\n\tprivate final String authenticationHeaderName;\n\n\tprivate Authentication anonymous = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\t/**\n\t * Create a new instance using the header of the name\n\t * {@link SimpMessageHeaderAccessor#USER_HEADER}.\n\t */\n\tpublic SecurityContextPropagationChannelInterceptor() {\n\t\tthis(SimpMessageHeaderAccessor.USER_HEADER);\n\t}\n\n\t/**\n\t * Create a new instance that uses the specified header to populate the\n\t * {@link Authentication}.\n\t * @param authenticationHeaderName the header name to populate the\n\t * {@link Authentication}. Cannot be null.\n\t */\n\tpublic SecurityContextPropagationChannelInterceptor(String authenticationHeaderName) {\n\t\tAssert.notNull(authenticationHeaderName, \"authenticationHeaderName cannot be null\");\n\t\tthis.authenticationHeaderName = authenticationHeaderName;\n\t}\n\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {\n\t\tthis.securityContextHolderStrategy = strategy;\n\t\tthis.empty = this.securityContextHolderStrategy.createEmptyContext();\n\t}\n\n\t/**\n\t * Configure an Authentication used for anonymous authentication. Default is: <pre>\n\t * new AnonymousAuthenticationToken(&quot;key&quot;, &quot;anonymous&quot;,\n\t * \t\tAuthorityUtils.createAuthorityList(&quot;ROLE_ANONYMOUS&quot;));\n\t * </pre>\n\t * @param authentication the Authentication used for anonymous authentication. Cannot\n\t * be null.\n\t */\n\tpublic void setAnonymousAuthentication(Authentication authentication) {\n\t\tAssert.notNull(authentication, \"authentication cannot be null\");\n\t\tthis.anonymous = authentication;\n\t}\n\n\t@Override\n\tpublic Message<?> preSend(Message<?> message, MessageChannel channel) {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tauthentication = this.anonymous;\n\t\t}\n\t\treturn MessageBuilder.fromMessage(message).setHeader(this.authenticationHeaderName, authentication).build();\n\t}\n\n\t@Override\n\tpublic Message<?> beforeHandle(Message<?> message, MessageChannel channel, MessageHandler handler) {\n\t\treturn postReceive(message, channel);\n\t}\n\n\t@Override\n\tpublic Message<?> postReceive(Message<?> message, MessageChannel channel) {\n\t\tsetup(message);\n\t\treturn message;\n\t}\n\n\t@Override\n\tpublic void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler,\n\t\t\t@Nullable Exception ex) {\n\t\tcleanup();\n\t}\n\n\tprivate void setup(Message<?> message) {\n\t\tAuthentication authentication = message.getHeaders().get(this.authenticationHeaderName, Authentication.class);\n\t\tSecurityContext currentContext = this.securityContextHolderStrategy.getContext();\n\t\tStack<SecurityContext> contextStack = originalContext.get();\n\t\tif (contextStack == null) {\n\t\t\tcontextStack = new Stack<>();\n\t\t\toriginalContext.set(contextStack);\n\t\t}\n\t\tcontextStack.push(currentContext);\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t}\n\n\tprivate void cleanup() {\n\t\tStack<SecurityContext> contextStack = originalContext.get();\n\t\tif (contextStack == null || contextStack.isEmpty()) {\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\toriginalContext.remove();\n\t\t\treturn;\n\t\t}\n\t\tSecurityContext context = contextStack.pop();\n\t\ttry {\n\t\t\tif (this.empty.equals(context)) {\n\t\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\t\toriginalContext.remove();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\t\t}\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/context/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for establishing the\n * {@link org.springframework.security.core.context.SecurityContext} within messaging.\n */\n@NullMarked\npackage org.springframework.security.messaging.context;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.handler.invocation.reactive;\n\nimport java.lang.annotation.Annotation;\n\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.handler.invocation.reactive.HandlerMethodArgumentResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Allows resolving the {@link Authentication#getPrincipal()} using the\n * {@link AuthenticationPrincipal} annotation. For example, the following\n * {@link Controller}:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;MessageMapping(\"/im\")\n *     public void im(@AuthenticationPrincipal CustomUser customUser) {\n *         // do something with CustomUser\n *     }\n * }\n * </pre>\n *\n * <p>\n * Will resolve the CustomUser argument using {@link Authentication#getPrincipal()} from\n * the {@link ReactiveSecurityContextHolder}. If the {@link Authentication} or\n * {@link Authentication#getPrincipal()} is null, it will return null. If the types do not\n * match, null will be returned unless\n * {@link AuthenticationPrincipal#errorOnInvalidType()} is true in which case a\n * {@link ClassCastException} will be thrown.\n *\n * <p>\n * Alternatively, users can create a custom meta annotation as shown below:\n *\n * <pre>\n * &#064;Target({ ElementType.PARAMETER })\n * &#064;Retention(RetentionPolicy.RUNTIME)\n * &#064;AuthenticationPrincipal\n * public @interface CurrentUser {\n * }\n * </pre>\n *\n * <p>\n * The custom annotation can then be used instead. For example:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;MessageMapping(\"/im\")\n *     public void im(@CurrentUser CustomUser customUser) {\n *         // do something with CustomUser\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @author DingHao\n * @since 5.2\n */\npublic class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {\n\n\tprivate ExpressionParser parser = new SpelExpressionParser();\n\n\tprivate final Class<AuthenticationPrincipal> annotationType = AuthenticationPrincipal.class;\n\n\tprivate SecurityAnnotationScanner<AuthenticationPrincipal> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(this.annotationType);\n\n\tprivate boolean useAnnotationTemplate = false;\n\n\tprivate @Nullable BeanResolver beanResolver;\n\n\tprivate ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance();\n\n\t/**\n\t * Sets the {@link BeanResolver} to be used on the expressions\n\t * @param beanResolver the {@link BeanResolver} to use\n\t */\n\tpublic void setBeanResolver(BeanResolver beanResolver) {\n\t\tthis.beanResolver = beanResolver;\n\t}\n\n\t/**\n\t * Sets the {@link ReactiveAdapterRegistry} to be used.\n\t * @param adapterRegistry the {@link ReactiveAdapterRegistry} to use. Cannot be null.\n\t * Default is {@link ReactiveAdapterRegistry#getSharedInstance()}\n\t */\n\tpublic void setAdapterRegistry(ReactiveAdapterRegistry adapterRegistry) {\n\t\tAssert.notNull(adapterRegistry, \"adapterRegistry cannot be null\");\n\t\tthis.adapterRegistry = adapterRegistry;\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn findMethodAnnotation(parameter) != null;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<Object> resolveArgument(MethodParameter parameter, Message<?> message) {\n\t\tReactiveAdapter adapter = this.adapterRegistry.getAdapter(parameter.getParameterType());\n\t\t// @formatter:off\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.mapNotNull(SecurityContext::getAuthentication)\n\t\t\t\t.flatMap((a) -> {\n\t\t\t\t\tObject p = resolvePrincipal(parameter, a.getPrincipal());\n\t\t\t\t\tMono<Object> principal = Mono.justOrEmpty(p);\n\t\t\t\t\treturn (adapter != null) ? Mono.just(adapter.fromPublisher(principal)) : principal;\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate @Nullable Object resolvePrincipal(MethodParameter parameter, @Nullable Object principal) {\n\t\tAuthenticationPrincipal authPrincipal = findMethodAnnotation(parameter);\n\t\tif (authPrincipal == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString expressionToParse = authPrincipal.expression();\n\t\tif (StringUtils.hasLength(expressionToParse)) {\n\t\t\tStandardEvaluationContext context = new StandardEvaluationContext();\n\t\t\tcontext.setRootObject(principal);\n\t\t\tcontext.setVariable(\"this\", principal);\n\t\t\tcontext.setBeanResolver(this.beanResolver);\n\t\t\tExpression expression = this.parser.parseExpression(expressionToParse);\n\t\t\tprincipal = expression.getValue(context);\n\t\t}\n\t\tif (isInvalidType(parameter, principal)) {\n\t\t\tif (authPrincipal.errorOnInvalidType()) {\n\t\t\t\tthrow new ClassCastException(principal + \" is not assignable to \" + parameter.getParameterType());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn principal;\n\t}\n\n\tprivate boolean isInvalidType(MethodParameter parameter, @Nullable Object principal) {\n\t\tif (principal == null) {\n\t\t\treturn false;\n\t\t}\n\t\tClass<?> typeToCheck = parameter.getParameterType();\n\t\tboolean isParameterPublisher = Publisher.class.isAssignableFrom(parameter.getParameterType());\n\t\tif (isParameterPublisher) {\n\t\t\tResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);\n\t\t\tClass<?> genericType = resolvableType.resolveGeneric(0);\n\t\t\tif (genericType == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttypeToCheck = genericType;\n\t\t}\n\t\treturn !ClassUtils.isAssignable(typeToCheck, principal.getClass());\n\t}\n\n\t/**\n\t * Configure AuthenticationPrincipal template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param templateDefaults - whether to resolve AuthenticationPrincipal templates\n\t * parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.useAnnotationTemplate = templateDefaults != null;\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(AuthenticationPrincipal.class, templateDefaults);\n\t}\n\n\t/**\n\t * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.\n\t * {@link MethodParameter}\n\t * @param parameter the {@link MethodParameter} to search for an {@link Annotation}\n\t * @return the {@link Annotation} that was found or null.\n\t */\n\tprivate @Nullable AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) {\n\t\tif (this.useAnnotationTemplate) {\n\t\t\treturn this.scanner.scan(parameter.getParameter());\n\t\t}\n\t\tAuthenticationPrincipal annotation = parameter.getParameterAnnotation(this.annotationType);\n\t\tif (annotation != null) {\n\t\t\treturn annotation;\n\t\t}\n\t\tAnnotation[] annotationsToSearch = parameter.getParameterAnnotations();\n\t\tfor (Annotation toSearch : annotationsToSearch) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), this.annotationType);\n\t\t\tif (annotation != null) {\n\t\t\t\treturn MergedAnnotations.from(toSearch).get(this.annotationType).synthesize();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/CurrentSecurityContextArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.handler.invocation.reactive;\n\nimport java.lang.annotation.Annotation;\n\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.handler.invocation.reactive.HandlerMethodArgumentResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Allows resolving the {@link Authentication#getPrincipal()} using the\n * {@link CurrentSecurityContext} annotation. For example, the following\n * {@link Controller}:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;MessageMapping(\"/im\")\n *     public void im(@CurrentSecurityContext SecurityContext context) {\n *         // do something with context\n *     }\n * }\n * </pre>\n *\n * <p>\n * Will resolve the SecurityContext argument using the\n * {@link ReactiveSecurityContextHolder}. If the {@link SecurityContext} is empty, it will\n * return null. If the types do not match, null will be returned unless\n * {@link CurrentSecurityContext#errorOnInvalidType()} is true in which case a\n * {@link ClassCastException} will be thrown.\n *\n * <p>\n * Alternatively, users can create a custom meta annotation as shown below:\n *\n * <pre>\n * &#064;Target({ ElementType.PARAMETER })\n * &#064;Retention(RetentionPolicy.RUNTIME)\n * &#064;CurrentSecurityContext(expression = \"authentication?.principal\")\n * public @interface CurrentUser {\n * }\n * </pre>\n *\n * <p>\n * The custom annotation can then be used instead. For example:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;MessageMapping(\"/im\")\n *     public void im(@CurrentUser CustomUser customUser) {\n *         // do something with CustomUser\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @author DingHao\n * @since 5.2\n */\npublic class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {\n\n\tprivate ExpressionParser parser = new SpelExpressionParser();\n\n\tprivate final Class<CurrentSecurityContext> annotationType = CurrentSecurityContext.class;\n\n\tprivate SecurityAnnotationScanner<CurrentSecurityContext> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(this.annotationType);\n\n\tprivate boolean useAnnotationTemplate = false;\n\n\tprivate @Nullable BeanResolver beanResolver;\n\n\tprivate ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance();\n\n\t/**\n\t * Sets the {@link BeanResolver} to be used on the expressions\n\t * @param beanResolver the {@link BeanResolver} to use\n\t */\n\tpublic void setBeanResolver(BeanResolver beanResolver) {\n\t\tthis.beanResolver = beanResolver;\n\t}\n\n\t/**\n\t * Sets the {@link ReactiveAdapterRegistry} to be used.\n\t * @param adapterRegistry the {@link ReactiveAdapterRegistry} to use. Cannot be null.\n\t * Default is {@link ReactiveAdapterRegistry#getSharedInstance()}\n\t */\n\tpublic void setAdapterRegistry(ReactiveAdapterRegistry adapterRegistry) {\n\t\tAssert.notNull(adapterRegistry, \"adapterRegistry cannot be null\");\n\t\tthis.adapterRegistry = adapterRegistry;\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn isMonoSecurityContext(parameter) || findMethodAnnotation(parameter) != null;\n\t}\n\n\tprivate boolean isMonoSecurityContext(MethodParameter parameter) {\n\t\tboolean isParameterPublisher = Publisher.class.isAssignableFrom(parameter.getParameterType());\n\t\tif (isParameterPublisher) {\n\t\t\tResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);\n\t\t\tClass<?> genericType = resolvableType.resolveGeneric(0);\n\t\t\tif (genericType == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn SecurityContext.class.isAssignableFrom(genericType);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Mono<Object> resolveArgument(MethodParameter parameter, Message<?> message) {\n\t\tReactiveAdapter adapter = this.adapterRegistry.getAdapter(parameter.getParameterType());\n\t\t// @formatter:off\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.flatMap((securityContext) -> {\n\t\t\t\t\tObject sc = resolveSecurityContext(parameter, securityContext);\n\t\t\t\t\tMono<Object> result = Mono.justOrEmpty(sc);\n\t\t\t\t\treturn (adapter != null) ? Mono.just(adapter.fromPublisher(result)) : result;\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate @Nullable Object resolveSecurityContext(MethodParameter parameter, Object securityContext) {\n\t\tCurrentSecurityContext contextAnno = findMethodAnnotation(parameter);\n\t\tif (contextAnno != null) {\n\t\t\treturn resolveSecurityContextFromAnnotation(contextAnno, parameter, securityContext);\n\t\t}\n\t\treturn securityContext;\n\t}\n\n\tprivate @Nullable Object resolveSecurityContextFromAnnotation(CurrentSecurityContext contextAnno,\n\t\t\tMethodParameter parameter, Object securityContext) {\n\t\tString expressionToParse = contextAnno.expression();\n\t\tif (StringUtils.hasLength(expressionToParse)) {\n\t\t\tStandardEvaluationContext context = new StandardEvaluationContext();\n\t\t\tcontext.setRootObject(securityContext);\n\t\t\tcontext.setVariable(\"this\", securityContext);\n\t\t\tif (this.beanResolver != null) {\n\t\t\t\t// https://github.com/spring-projects/spring-framework/issues/35371\n\t\t\t\tcontext.setBeanResolver(this.beanResolver);\n\t\t\t}\n\t\t\tExpression expression = this.parser.parseExpression(expressionToParse);\n\t\t\tsecurityContext = expression.getValue(context);\n\t\t}\n\t\tif (isInvalidType(parameter, securityContext)) {\n\t\t\tif (contextAnno.errorOnInvalidType()) {\n\t\t\t\tthrow new ClassCastException(securityContext + \" is not assignable to \" + parameter.getParameterType());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn securityContext;\n\t}\n\n\tprivate boolean isInvalidType(MethodParameter parameter, @Nullable Object value) {\n\t\tif (value == null) {\n\t\t\treturn false;\n\t\t}\n\t\tClass<?> typeToCheck = parameter.getParameterType();\n\t\tboolean isParameterPublisher = Publisher.class.isAssignableFrom(parameter.getParameterType());\n\t\tif (isParameterPublisher) {\n\t\t\tResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);\n\t\t\tClass<?> genericType = resolvableType.resolveGeneric(0);\n\t\t\tif (genericType == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttypeToCheck = genericType;\n\t\t}\n\t\treturn !typeToCheck.isAssignableFrom(value.getClass());\n\t}\n\n\t/**\n\t * Configure CurrentSecurityContext template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param templateDefaults - whether to resolve CurrentSecurityContext templates\n\t * parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.useAnnotationTemplate = templateDefaults != null;\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(CurrentSecurityContext.class, templateDefaults);\n\t}\n\n\t/**\n\t * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.\n\t * @param parameter the {@link MethodParameter} to search for an {@link Annotation}\n\t * @return the {@link Annotation} that was found or null.\n\t */\n\tprivate @Nullable CurrentSecurityContext findMethodAnnotation(MethodParameter parameter) {\n\t\tif (this.useAnnotationTemplate) {\n\t\t\treturn this.scanner.scan(parameter.getParameter());\n\t\t}\n\t\tCurrentSecurityContext annotation = parameter.getParameterAnnotation(this.annotationType);\n\t\tif (annotation != null) {\n\t\t\treturn annotation;\n\t\t}\n\t\tAnnotation[] annotationsToSearch = parameter.getParameterAnnotations();\n\t\tfor (Annotation toSearch : annotationsToSearch) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), this.annotationType);\n\t\t\tif (annotation != null) {\n\t\t\t\treturn MergedAnnotations.from(toSearch).get(this.annotationType).synthesize();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive support for resolving security related arguments.\n */\n@NullMarked\npackage org.springframework.security.messaging.handler.invocation.reactive;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/util/matcher/AbstractMessageMatcherComposite.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.util.Assert;\n\n/**\n * Abstract {@link MessageMatcher} containing multiple {@link MessageMatcher}\n *\n * @since 4.0\n */\npublic abstract class AbstractMessageMatcherComposite<T> implements MessageMatcher<T> {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\t/**\n\t * @deprecated since 5.4 in favor of {@link #logger}\n\t */\n\t@Deprecated\n\tprotected final Log LOGGER = this.logger;\n\n\tprivate final List<MessageMatcher<T>> messageMatchers;\n\n\t/**\n\t * Creates a new instance\n\t * @param messageMatchers the {@link MessageMatcher} instances to try\n\t */\n\tAbstractMessageMatcherComposite(List<MessageMatcher<T>> messageMatchers) {\n\t\tAssert.notEmpty(messageMatchers, \"messageMatchers must contain a value\");\n\t\tAssert.isTrue(!messageMatchers.contains(null), \"messageMatchers cannot contain null values\");\n\t\tthis.messageMatchers = messageMatchers;\n\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param messageMatchers the {@link MessageMatcher} instances to try\n\t */\n\t@SafeVarargs\n\tAbstractMessageMatcherComposite(MessageMatcher<T>... messageMatchers) {\n\t\tthis(Arrays.asList(messageMatchers));\n\t}\n\n\tpublic List<MessageMatcher<T>> getMessageMatchers() {\n\t\treturn this.messageMatchers;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \"[messageMatchers=\" + this.messageMatchers + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/util/matcher/AndMessageMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport java.util.List;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.messaging.Message;\n\n/**\n * {@link MessageMatcher} that will return true if all of the passed in\n * {@link MessageMatcher} instances match.\n *\n * @since 4.0\n */\npublic final class AndMessageMatcher<T> extends AbstractMessageMatcherComposite<T> {\n\n\t/**\n\t * Creates a new instance\n\t * @param messageMatchers the {@link MessageMatcher} instances to try\n\t */\n\tpublic AndMessageMatcher(List<MessageMatcher<T>> messageMatchers) {\n\t\tsuper(messageMatchers);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param messageMatchers the {@link MessageMatcher} instances to try\n\t */\n\t@SafeVarargs\n\tpublic AndMessageMatcher(MessageMatcher<T>... messageMatchers) {\n\t\tsuper(messageMatchers);\n\n\t}\n\n\t@Override\n\tpublic boolean matches(Message<? extends T> message) {\n\t\tfor (MessageMatcher<T> matcher : getMessageMatchers()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Trying to match using %s\", matcher));\n\t\t\tif (!matcher.matches(message)) {\n\t\t\t\tthis.logger.debug(\"Did not match\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(\"All messageMatchers returned true\");\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/util/matcher/MessageMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.springframework.messaging.Message;\n\n/**\n * API for determining if a {@link Message} should be matched on.\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic interface MessageMatcher<T> {\n\n\t/**\n\t * Matches every {@link Message}\n\t */\n\tMessageMatcher<Object> ANY_MESSAGE = new MessageMatcher<>() {\n\n\t\t@Override\n\t\tpublic boolean matches(Message<?> message) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"ANY_MESSAGE\";\n\t\t}\n\n\t};\n\n\t/**\n\t * Returns true if the {@link Message} matches, else false\n\t * @param message the {@link Message} to match on\n\t * @return true if the {@link Message} matches, else false\n\t */\n\tboolean matches(Message<? extends T> message);\n\n\t/**\n\t * Returns a {@link MatchResult} for this {@code MessageMatcher}. The default\n\t * implementation returns {@link Collections#emptyMap()} when\n\t * {@link MatchResult#getVariables()} is invoked.\n\t * @return the {@code MatchResult} from comparing this {@code MessageMatcher} against\n\t * the {@code Message}\n\t * @since 6.5\n\t */\n\tdefault MatchResult matcher(Message<? extends T> message) {\n\t\tboolean match = matches(message);\n\t\treturn new MatchResult(match, Collections.emptyMap());\n\t}\n\n\t/**\n\t * The result of matching against a {@code Message} contains the status, true or\n\t * false, of the match and if present, any variables extracted from the match\n\t *\n\t * @since 6.5\n\t */\n\tclass MatchResult {\n\n\t\tprivate final boolean match;\n\n\t\tprivate final Map<String, String> variables;\n\n\t\tMatchResult(boolean match, Map<String, String> variables) {\n\t\t\tthis.match = match;\n\t\t\tthis.variables = variables;\n\t\t}\n\n\t\t/**\n\t\t * Return whether the comparison against the {@code Message} produced a successful\n\t\t * match\n\t\t */\n\t\tpublic boolean isMatch() {\n\t\t\treturn this.match;\n\t\t}\n\n\t\t/**\n\t\t * Returns the extracted variable values where the key is the variable name and\n\t\t * the value is the variable value\n\t\t * @return a map containing key-value pairs representing extracted variable names\n\t\t * and variable values\n\t\t */\n\t\tpublic Map<String, String> getVariables() {\n\t\t\treturn this.variables;\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is a match with no variables\n\t\t */\n\t\tpublic static MatchResult match() {\n\t\t\treturn new MatchResult(true, Collections.emptyMap());\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is a match with the specified\n\t\t * variables\n\t\t */\n\t\tpublic static MatchResult match(Map<String, String> variables) {\n\t\t\treturn new MatchResult(true, variables);\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is not a match.\n\t\t * @return a {@code MatchResult} with match set to false\n\t\t */\n\t\tpublic static MatchResult notMatch() {\n\t\t\treturn new MatchResult(false, Collections.emptyMap());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/util/matcher/OrMessageMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport java.util.List;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.messaging.Message;\n\n/**\n * {@link MessageMatcher} that will return true if any of the passed in\n * {@link MessageMatcher} instances match.\n *\n * @since 4.0\n */\npublic final class OrMessageMatcher<T> extends AbstractMessageMatcherComposite<T> {\n\n\t/**\n\t * Creates a new instance\n\t * @param messageMatchers the {@link MessageMatcher} instances to try\n\t */\n\tpublic OrMessageMatcher(List<MessageMatcher<T>> messageMatchers) {\n\t\tsuper(messageMatchers);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param messageMatchers the {@link MessageMatcher} instances to try\n\t */\n\t@SafeVarargs\n\tpublic OrMessageMatcher(MessageMatcher<T>... messageMatchers) {\n\t\tsuper(messageMatchers);\n\n\t}\n\n\t@Override\n\tpublic boolean matches(Message<? extends T> message) {\n\t\tfor (MessageMatcher<T> matcher : getMessageMatchers()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Trying to match using %s\", matcher));\n\t\t\tif (matcher.matches(message)) {\n\t\t\t\tthis.logger.debug(\"matched\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(\"No matches found\");\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/util/matcher/PathPatternMessageMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport java.util.Collections;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.server.PathContainer;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.security.messaging.access.intercept.MessageAuthorizationContext;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.pattern.PathPattern;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\n/**\n * Match {@link Message}s based on the message destination pattern using a\n * {@link PathPattern}. There is also support for optionally matching on a specified\n * {@link SimpMessageType}.\n *\n * @author Pat McCusker\n * @since 6.5\n */\npublic final class PathPatternMessageMatcher implements MessageMatcher<Object> {\n\n\tpublic static final MessageMatcher<Object> NULL_DESTINATION_MATCHER = (message) -> getDestination(message) == null;\n\n\tprivate final PathPattern pattern;\n\n\tprivate final PathContainer.Options options;\n\n\t/**\n\t * The {@link MessageMatcher} that determines if the type matches. If the type was\n\t * null, this matcher will match every Message.\n\t */\n\tprivate MessageMatcher<Object> messageTypeMatcher = ANY_MESSAGE;\n\n\tprivate PathPatternMessageMatcher(PathPattern pattern, PathContainer.Options options) {\n\t\tthis.options = options;\n\t\tthis.pattern = pattern;\n\t}\n\n\t/**\n\t * Initialize this builder with the {@link PathPatternParser#defaultInstance} that is\n\t * configured with the\n\t * {@link org.springframework.http.server.PathContainer.Options#HTTP_PATH} separator\n\t */\n\tpublic static Builder withDefaults() {\n\t\treturn new Builder(PathPatternParser.defaultInstance);\n\t}\n\n\t/**\n\t * Initialize this builder with the provided {@link PathPatternParser}\n\t */\n\tpublic static Builder withPathPatternParser(PathPatternParser parser) {\n\t\treturn new Builder(parser);\n\t}\n\n\tvoid setMessageTypeMatcher(MessageMatcher<Object> messageTypeMatcher) {\n\t\tthis.messageTypeMatcher = messageTypeMatcher;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic boolean matches(Message<?> message) {\n\t\treturn matcher(message).isMatch();\n\t}\n\n\t/**\n\t * Extract the path variables from the {@link Message} destination if the path is a\n\t * match, otherwise the {@link MatchResult#getVariables()} returns a\n\t * {@link Collections#emptyMap()}\n\t * @param message the message whose path variables to extract.\n\t * @return a {@code MatchResult} of the path variables and values.\n\t */\n\t@Override\n\tpublic MatchResult matcher(Message<?> message) {\n\t\tif (!this.messageTypeMatcher.matches(message)) {\n\t\t\treturn MatchResult.notMatch();\n\t\t}\n\n\t\tString destination = getDestination(message);\n\t\tif (destination == null) {\n\t\t\treturn MatchResult.notMatch();\n\t\t}\n\n\t\tPathContainer destinationPathContainer = PathContainer.parsePath(destination, this.options);\n\t\tPathPattern.PathMatchInfo pathMatchInfo = this.pattern.matchAndExtract(destinationPathContainer);\n\n\t\treturn (pathMatchInfo != null) ? MatchResult.match(pathMatchInfo.getUriVariables()) : MatchResult.notMatch();\n\t}\n\n\tprivate static @Nullable String getDestination(Message<?> message) {\n\t\treturn SimpMessageHeaderAccessor.getDestination(message.getHeaders());\n\t}\n\n\t/**\n\t * A builder for specifying various elements of a message for the purpose of creating\n\t * a {@link PathPatternMessageMatcher}.\n\t */\n\tpublic static class Builder {\n\n\t\tprivate final PathPatternParser parser;\n\n\t\tBuilder(PathPatternParser parser) {\n\t\t\tthis.parser = parser;\n\t\t}\n\n\t\t/**\n\t\t * Match messages having this destination pattern.\n\t\t *\n\t\t * <p>\n\t\t * Path patterns always start with a slash and may contain placeholders. They can\n\t\t * also be followed by {@code /**} to signify all URIs under a given path.\n\t\t *\n\t\t * <p>\n\t\t * The following are valid patterns and their meaning\n\t\t * <ul>\n\t\t * <li>{@code /path} - match exactly and only `/path`</li>\n\t\t * <li>{@code /path/**} - match `/path` and any of its descendants</li>\n\t\t * <li>{@code /path/{value}/**} - match `/path/subdirectory` and any of its\n\t\t * descendants, capturing the value of the subdirectory in\n\t\t * {@link MessageAuthorizationContext#getVariables()}</li>\n\t\t * </ul>\n\t\t *\n\t\t * <p>\n\t\t * A more comprehensive list can be found at {@link PathPattern}.\n\t\t *\n\t\t * <p>\n\t\t * A dot-based message pattern is also supported when configuring a\n\t\t * {@link PathPatternParser} using\n\t\t * {@link PathPatternMessageMatcher#withPathPatternParser}\n\t\t * @param pattern the destination pattern to match\n\t\t * @return the {@link PathPatternMessageMatcher.Builder} for more configuration\n\t\t */\n\t\tpublic PathPatternMessageMatcher matcher(String pattern) {\n\t\t\treturn matcher(null, pattern);\n\t\t}\n\n\t\t/**\n\t\t * Match messages having this type and destination pattern.\n\t\t *\n\t\t * <p>\n\t\t * When the message {@code type} is null, then the matcher does not consider the\n\t\t * message type\n\t\t *\n\t\t * <p>\n\t\t * Path patterns always start with a slash and may contain placeholders. They can\n\t\t * also be followed by {@code /**} to signify all URIs under a given path.\n\t\t *\n\t\t * <p>\n\t\t * The following are valid patterns and their meaning\n\t\t * <ul>\n\t\t * <li>{@code /path} - match exactly and only `/path`</li>\n\t\t * <li>{@code /path/**} - match `/path` and any of its descendants</li>\n\t\t * <li>{@code /path/{value}/**} - match `/path/subdirectory` and any of its\n\t\t * descendants, capturing the value of the subdirectory in\n\t\t * {@link MessageAuthorizationContext#getVariables()}</li>\n\t\t * </ul>\n\t\t *\n\t\t * <p>\n\t\t * A more comprehensive list can be found at {@link PathPattern}.\n\t\t *\n\t\t * <p>\n\t\t * A dot-based message pattern is also supported when configuring a\n\t\t * {@link PathPatternParser} using\n\t\t * {@link PathPatternMessageMatcher#withPathPatternParser}\n\t\t * @param type the message type to match\n\t\t * @param pattern the destination pattern to match\n\t\t * @return the {@link PathPatternMessageMatcher.Builder} for more configuration\n\t\t */\n\t\tpublic PathPatternMessageMatcher matcher(@Nullable SimpMessageType type, String pattern) {\n\t\t\tAssert.notNull(pattern, \"pattern must not be null\");\n\t\t\tPathPattern pathPattern = this.parser.parse(pattern);\n\t\t\tPathPatternMessageMatcher matcher = new PathPatternMessageMatcher(pathPattern,\n\t\t\t\t\tthis.parser.getPathOptions());\n\t\t\tif (type != null) {\n\t\t\t\tmatcher.setMessageTypeMatcher(new SimpMessageTypeMatcher(type));\n\t\t\t}\n\t\t\treturn matcher;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/util/matcher/SimpMessageTypeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageHeaders;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * A {@link MessageMatcher} that matches if the provided {@link Message} has a type that\n * is the same as the {@link SimpMessageType} that was specified in the constructor.\n *\n * @author Rob Winch\n * @since 4.0\n *\n */\npublic class SimpMessageTypeMatcher implements MessageMatcher<Object> {\n\n\tprivate final SimpMessageType typeToMatch;\n\n\t/**\n\t * Creates a new instance\n\t * @param typeToMatch the {@link SimpMessageType} that will result in a match. Cannot\n\t * be null.\n\t */\n\tpublic SimpMessageTypeMatcher(SimpMessageType typeToMatch) {\n\t\tAssert.notNull(typeToMatch, \"typeToMatch cannot be null\");\n\t\tthis.typeToMatch = typeToMatch;\n\t}\n\n\t@Override\n\tpublic boolean matches(Message<?> message) {\n\t\tMessageHeaders headers = message.getHeaders();\n\t\tSimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(headers);\n\t\treturn this.typeToMatch == messageType;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object other) {\n\t\tif (this == other) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(other instanceof SimpMessageTypeMatcher otherMatcher)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn ObjectUtils.nullSafeEquals(this.typeToMatch, otherMatcher.typeToMatch);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\t// Using nullSafeHashCode for proper array hashCode handling\n\t\treturn ObjectUtils.nullSafeHashCode(this.typeToMatch);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SimpMessageTypeMatcher [typeToMatch=\" + this.typeToMatch + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/util/matcher/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for matching messages.\n */\n@NullMarked\npackage org.springframework.security.messaging.util.matcher;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/web/csrf/CsrfChannelInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.web.csrf;\n\nimport java.util.Map;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.support.ChannelInterceptor;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\nimport org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.InvalidCsrfTokenException;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\n\n/**\n * {@link ChannelInterceptor} that validates that a valid CSRF is included in the header\n * of any {@link SimpMessageType#CONNECT} message. The expected {@link CsrfToken} is\n * populated by CsrfTokenHandshakeInterceptor.\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic final class CsrfChannelInterceptor implements ChannelInterceptor {\n\n\tprivate final MessageMatcher<Object> matcher = new SimpMessageTypeMatcher(SimpMessageType.CONNECT);\n\n\t@Override\n\tpublic Message<?> preSend(Message<?> message, MessageChannel channel) {\n\t\tif (!this.matcher.matches(message)) {\n\t\t\treturn message;\n\t\t}\n\t\tMap<String, Object> sessionAttributes = SimpMessageHeaderAccessor.getSessionAttributes(message.getHeaders());\n\t\tCsrfToken expectedToken = (sessionAttributes != null)\n\t\t\t\t? (CsrfToken) sessionAttributes.get(CsrfToken.class.getName()) : null;\n\t\tif (expectedToken == null) {\n\t\t\tthrow new MissingCsrfTokenException(null);\n\t\t}\n\t\tString actualTokenValue = SimpMessageHeaderAccessor.wrap(message)\n\t\t\t.getFirstNativeHeader(expectedToken.getHeaderName());\n\t\tboolean csrfCheckPassed = expectedToken.getToken().equals(actualTokenValue);\n\t\tif (!csrfCheckPassed) {\n\t\t\tthrow new InvalidCsrfTokenException(expectedToken, actualTokenValue);\n\t\t}\n\t\treturn message;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/web/csrf/XorCsrfChannelInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.web.csrf;\n\nimport java.security.MessageDigest;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.support.ChannelInterceptor;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.messaging.util.matcher.MessageMatcher;\nimport org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.InvalidCsrfTokenException;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\n\n/**\n * {@link ChannelInterceptor} that validates a CSRF token masked by the\n * {@link org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler} in\n * the header of any {@link SimpMessageType#CONNECT} message.\n *\n * @author Steve Riesenberg\n * @since 5.8\n */\npublic final class XorCsrfChannelInterceptor implements ChannelInterceptor {\n\n\tprivate final MessageMatcher<Object> matcher = new SimpMessageTypeMatcher(SimpMessageType.CONNECT);\n\n\t@Override\n\tpublic Message<?> preSend(Message<?> message, MessageChannel channel) {\n\t\tif (!this.matcher.matches(message)) {\n\t\t\treturn message;\n\t\t}\n\t\tMap<String, Object> sessionAttributes = SimpMessageHeaderAccessor.getSessionAttributes(message.getHeaders());\n\t\tCsrfToken expectedToken = (sessionAttributes != null)\n\t\t\t\t? (CsrfToken) sessionAttributes.get(CsrfToken.class.getName()) : null;\n\t\tif (expectedToken == null) {\n\t\t\tthrow new MissingCsrfTokenException(null);\n\t\t}\n\t\tString actualToken = SimpMessageHeaderAccessor.wrap(message)\n\t\t\t.getFirstNativeHeader(expectedToken.getHeaderName());\n\t\tString actualTokenValue = XorCsrfTokenUtils.getTokenValue(actualToken, expectedToken.getToken());\n\t\tboolean csrfCheckPassed = equalsConstantTime(expectedToken.getToken(), actualTokenValue);\n\t\tif (!csrfCheckPassed) {\n\t\t\tthrow new InvalidCsrfTokenException(expectedToken, actualToken);\n\t\t}\n\t\treturn message;\n\t}\n\n\t/**\n\t * Constant time comparison to prevent against timing attacks.\n\t * @param expected\n\t * @param actual\n\t * @return\n\t */\n\tprivate static boolean equalsConstantTime(String expected, @Nullable String actual) {\n\t\tif (expected == actual) {\n\t\t\treturn true;\n\t\t}\n\t\tif (expected == null || actual == null) {\n\t\t\treturn false;\n\t\t}\n\t\t// Encode after ensure that the string is not null\n\t\tbyte[] expectedBytes = Utf8.encode(expected);\n\t\tbyte[] actualBytes = Utf8.encode(actual);\n\t\treturn MessageDigest.isEqual(expectedBytes, actualBytes);\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/web/csrf/XorCsrfTokenUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.web.csrf;\n\nimport java.util.Base64;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.util.Assert;\n\n/**\n * Copied from\n * {@link org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler}.\n *\n * @see <a href=\n * \"https://github.com/spring-projects/spring-security/issues/12378\">gh-12378</a>\n */\nfinal class XorCsrfTokenUtils {\n\n\tprivate XorCsrfTokenUtils() {\n\t}\n\n\tstatic @Nullable String getTokenValue(@Nullable String actualToken, String token) {\n\t\tbyte[] actualBytes;\n\t\ttry {\n\t\t\tactualBytes = Base64.getUrlDecoder().decode(actualToken);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn null;\n\t\t}\n\n\t\tbyte[] tokenBytes = Utf8.encode(token);\n\t\tint tokenSize = tokenBytes.length;\n\t\tif (actualBytes.length != tokenSize * 2) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// extract token and random bytes\n\t\tbyte[] xoredCsrf = new byte[tokenSize];\n\t\tbyte[] randomBytes = new byte[tokenSize];\n\n\t\tSystem.arraycopy(actualBytes, 0, randomBytes, 0, tokenSize);\n\t\tSystem.arraycopy(actualBytes, tokenSize, xoredCsrf, 0, tokenSize);\n\n\t\tbyte[] csrfBytes = xorCsrf(randomBytes, xoredCsrf);\n\t\treturn Utf8.decode(csrfBytes);\n\t}\n\n\tprivate static byte[] xorCsrf(byte[] randomBytes, byte[] csrfBytes) {\n\t\tAssert.isTrue(randomBytes.length == csrfBytes.length, \"arrays must be equal length\");\n\t\tint len = csrfBytes.length;\n\t\tbyte[] xoredCsrf = new byte[len];\n\t\tSystem.arraycopy(csrfBytes, 0, xoredCsrf, 0, len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\txoredCsrf[i] ^= randomBytes[i];\n\t\t}\n\t\treturn xoredCsrf;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/web/csrf/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support CSRF protection in messages.\n */\n@NullMarked\npackage org.springframework.security.messaging.web.csrf;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/web/socket/server/CsrfTokenHandshakeInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.web.socket.server;\n\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.server.HandshakeInterceptor;\n\n/**\n * Loads a CsrfToken from the HttpServletRequest and HttpServletResponse to populate the\n * WebSocket attributes. This is used as the expected CsrfToken when validating connection\n * requests to ensure only the same origin connects.\n *\n * @author Rob Winch\n * @author Steve Riesenberg\n * @since 4.0\n */\npublic final class CsrfTokenHandshakeInterceptor implements HandshakeInterceptor {\n\n\t@Override\n\tpublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,\n\t\t\tMap<String, Object> attributes) {\n\t\tHttpServletRequest httpRequest = ((ServletServerHttpRequest) request).getServletRequest();\n\t\tDeferredCsrfToken deferredCsrfToken = (DeferredCsrfToken) httpRequest\n\t\t\t.getAttribute(DeferredCsrfToken.class.getName());\n\t\tif (deferredCsrfToken == null) {\n\t\t\treturn true;\n\t\t}\n\t\tCsrfToken csrfToken = deferredCsrfToken.get();\n\t\t// Ensure the values of the CsrfToken are copied into a new token so the old token\n\t\t// is available for garbage collection.\n\t\t// This is required because the original token could hold a reference to the\n\t\t// HttpServletRequest/Response of the handshake request.\n\t\tCsrfToken resolvedCsrfToken = new DefaultCsrfToken(csrfToken.getHeaderName(), csrfToken.getParameterName(),\n\t\t\t\tcsrfToken.getToken());\n\t\tattributes.put(CsrfToken.class.getName(), resolvedCsrfToken);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,\n\t\t\t@Nullable Exception exception) {\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/main/java/org/springframework/security/messaging/web/socket/server/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive Security CSRF protection.\n */\n@NullMarked\npackage org.springframework.security.messaging.web.socket.server;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/access/expression/DefaultMessageSecurityExpressionHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport java.util.function.Supplier;\n\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Answers;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.TypedValue;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.GenericMessage;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n@ExtendWith(MockitoExtension.class)\npublic class DefaultMessageSecurityExpressionHandlerTests {\n\n\t@Mock(answer = Answers.CALLS_REAL_METHODS)\n\tAuthenticationTrustResolver trustResolver;\n\n\t@Mock\n\tPermissionEvaluator permissionEvaluator;\n\n\tDefaultMessageSecurityExpressionHandler<Object> handler;\n\n\tMessage<Object> message;\n\n\tAuthentication authentication;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.handler = new DefaultMessageSecurityExpressionHandler<>();\n\t\tthis.message = new GenericMessage<>(\"\");\n\t\tthis.authentication = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t}\n\n\t// SEC-2705\n\t@Test\n\tpublic void trustResolverPopulated() {\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.message);\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"authenticated\");\n\t\tassertThat(ExpressionUtils.evaluateAsBoolean(expression, context)).isFalse();\n\t}\n\n\t@Test\n\tpublic void trustResolverNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setTrustResolver(null));\n\t}\n\n\t@Test\n\tpublic void trustResolverCustom() {\n\t\tthis.handler.setTrustResolver(this.trustResolver);\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.message);\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"authenticated\");\n\t\tgiven(this.trustResolver.isAnonymous(this.authentication)).willReturn(false);\n\t\tassertThat(ExpressionUtils.evaluateAsBoolean(expression, context)).isTrue();\n\t}\n\n\t@Test\n\tpublic void roleHierarchy() {\n\t\tthis.authentication = new TestingAuthenticationToken(\"admin\", \"pass\", \"ROLE_ADMIN\");\n\t\tRoleHierarchyImpl roleHierarchy = RoleHierarchyImpl.fromHierarchy(\"ROLE_ADMIN > ROLE_USER\");\n\t\tthis.handler.setRoleHierarchy(roleHierarchy);\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.message);\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"hasRole('ROLE_USER')\");\n\t\tassertThat(ExpressionUtils.evaluateAsBoolean(expression, context)).isTrue();\n\t}\n\n\t@Test\n\tpublic void permissionEvaluator() {\n\t\tthis.handler.setPermissionEvaluator(this.permissionEvaluator);\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.message);\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"hasPermission(message, 'read')\");\n\t\tgiven(this.permissionEvaluator.hasPermission(this.authentication, this.message, \"read\")).willReturn(true);\n\t\tassertThat(ExpressionUtils.evaluateAsBoolean(expression, context)).isTrue();\n\t}\n\n\t@Test\n\tpublic void createEvaluationContextSupplierAuthentication() {\n\t\tSupplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);\n\t\tgiven(mockAuthenticationSupplier.get()).willReturn(this.authentication);\n\t\tEvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier, this.message);\n\t\tverifyNoInteractions(mockAuthenticationSupplier);\n\t\tassertThat(context.getRootObject()).extracting(TypedValue::getValue)\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(MessageSecurityExpressionRoot.class))\n\t\t\t.extracting(SecurityExpressionRoot::getAuthentication)\n\t\t\t.isEqualTo(this.authentication);\n\t\tverify(mockAuthenticationSupplier).get();\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/access/expression/MessageExpressionAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.expression;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.messaging.support.GenericMessage;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.messaging.access.intercept.MessageAuthorizationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link MessageExpressionAuthorizationManager}.\n */\nclass MessageExpressionAuthorizationManagerTests {\n\n\t@Test\n\tvoid instantiateWhenExpressionStringNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MessageExpressionAuthorizationManager(null))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionStringEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MessageExpressionAuthorizationManager(\"\"))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionStringBlankThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MessageExpressionAuthorizationManager(\" \"))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid withSecurityExpressionHandlerWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> MessageExpressionAuthorizationManager.withSecurityExpressionHandler(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionHandlerNotSetThenDefaultUsed() {\n\t\tMessageExpressionAuthorizationManager manager = new MessageExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tassertThat(manager).extracting(\"expressionHandler\")\n\t\t\t.isInstanceOf(MessageAuthorizationContextSecurityExpressionHandler.class);\n\t}\n\n\t@Test\n\tvoid withSecurityExpressionHandlerWhenNotNullThenVerifyExpressionHandler() {\n\t\tString expressionString = \"hasRole('ADMIN')\";\n\t\tSecurityExpressionHandler<MessageAuthorizationContext<?>> expressionHandler = mock(\n\t\t\t\tSecurityExpressionHandler.class);\n\t\tExpressionParser expressionParser = mock(ExpressionParser.class);\n\t\tExpression expression = mock(Expression.class);\n\t\tgiven(expressionHandler.getExpressionParser()).willReturn(expressionParser);\n\t\tgiven(expressionParser.parseExpression(expressionString)).willReturn(expression);\n\t\tMessageExpressionAuthorizationManager manager = MessageExpressionAuthorizationManager\n\t\t\t.withSecurityExpressionHandler(expressionHandler)\n\t\t\t.expression(expressionString);\n\t\tassertThat(manager).extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t\tassertThat(manager).extracting(\"expression\").isEqualTo(expression);\n\t\tverify(expressionParser).parseExpression(expressionString);\n\t}\n\n\t@Test\n\tvoid authorizeWhenExpressionHasRoleAdminAndRoleAdminThenGrantedDecision() {\n\t\tMessageExpressionAuthorizationManager manager = new MessageExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"admin\", \"password\", \"ROLE_ADMIN\");\n\t\tAuthorizationResult result = manager.authorize(() -> authentication,\n\t\t\t\tnew MessageAuthorizationContext<>(new GenericMessage<>(\"message\")));\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenExpressionHasRoleAdminAndRoleUserThenDeniedDecision() {\n\t\tMessageExpressionAuthorizationManager manager = new MessageExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tAuthorizationResult result = manager.authorize(() -> authentication,\n\t\t\t\tnew MessageAuthorizationContext<>(new GenericMessage<>(\"message\")));\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.isGranted()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link AuthorizationChannelInterceptor}\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthorizationChannelInterceptorTests {\n\n\t@Mock\n\tMessage<Object> message;\n\n\t@Mock\n\tMessageChannel channel;\n\n\t@Mock\n\tAuthorizationManager<Message<?>> authorizationManager;\n\n\t@Mock\n\tAuthorizationEventPublisher eventPublisher;\n\n\tAuthentication originalAuth;\n\n\tAuthorizationChannelInterceptor interceptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.interceptor = new AuthorizationChannelInterceptor(this.authorizationManager);\n\t\tthis.originalAuth = new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(this.originalAuth);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationManagerNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AuthorizationChannelInterceptor(null));\n\t}\n\n\t@Test\n\tpublic void preSendWhenAllowThenSameMessage() {\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(true));\n\t\tassertThat(this.interceptor.preSend(this.message, this.channel)).isSameAs(this.message);\n\t}\n\n\t@Test\n\tpublic void preSendWhenDenyThenException() {\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(false));\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.preSend(this.message, this.channel));\n\t}\n\n\t@Test\n\tpublic void setEventPublisherWhenNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.setAuthorizationEventPublisher(null));\n\t}\n\n\t@Test\n\tpublic void preSendWhenAuthorizationEventPublisherThenPublishes() {\n\t\tthis.interceptor.setAuthorizationEventPublisher(this.eventPublisher);\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(true));\n\t\tthis.interceptor.preSend(this.message, this.channel);\n\t\tverify(this.eventPublisher).publishAuthorizationEvent(any(), any(), any());\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.access.intercept;\n\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageHeaders;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.support.GenericMessage;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link MessageMatcherDelegatingAuthorizationManager}\n */\n@ExtendWith(MockitoExtension.class)\npublic final class MessageMatcherDelegatingAuthorizationManagerTests {\n\n\t@Mock\n\tprivate ApplicationContext context;\n\n\t@Mock\n\tprivate ObjectProvider<PathPatternMessageMatcher.Builder> provider;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tMockito.when(this.context.getBeanProvider(PathPatternMessageMatcher.Builder.class)).thenReturn(this.provider);\n\t\tMockito.when(this.provider.getIfUnique(any())).thenReturn(PathPatternMessageMatcher.withDefaults());\n\t}\n\n\t@Test\n\tvoid checkWhenPermitAllThenPermits() {\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().anyMessage().permitAll().build();\n\t\tMessage<?> message = new GenericMessage<>(new Object());\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkWhenAnyMessageHasRoleThenRequires() {\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().anyMessage().hasRole(\"USER\").build();\n\t\tMessage<?> message = new GenericMessage<>(new Object());\n\t\tAuthentication user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tassertThat(authorizationManager.authorize(() -> user, message).isGranted()).isTrue();\n\t\tAuthentication admin = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_ADMIN\");\n\t\tassertThat(authorizationManager.authorize(() -> admin, message).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkWhenSimpDestinationMatchesThenUses() {\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().simpDestMatchers(\"destination\")\n\t\t\t.permitAll()\n\t\t\t.anyMessage()\n\t\t\t.denyAll()\n\t\t\t.build();\n\t\tMessageHeaders headers = new MessageHeaders(\n\t\t\t\tMap.of(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"destination\"));\n\t\tMessage<?> message = new GenericMessage<>(new Object(), headers);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkWhenNullDestinationHeaderMatchesThenUses() {\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().nullDestMatcher()\n\t\t\t.permitAll()\n\t\t\t.anyMessage()\n\t\t\t.denyAll()\n\t\t\t.build();\n\t\tMessage<?> message = new GenericMessage<>(new Object());\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isTrue();\n\t\tMessageHeaders headers = new MessageHeaders(\n\t\t\t\tMap.of(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"destination\"));\n\t\tmessage = new GenericMessage<>(new Object(), headers);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkWhenSimpTypeMatchesThenUses() {\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().simpTypeMatchers(SimpMessageType.CONNECT)\n\t\t\t.permitAll()\n\t\t\t.anyMessage()\n\t\t\t.denyAll()\n\t\t\t.build();\n\t\tMessageHeaders headers = new MessageHeaders(\n\t\t\t\tMap.of(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.CONNECT));\n\t\tMessage<?> message = new GenericMessage<>(new Object(), headers);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isTrue();\n\t}\n\n\t// gh-12540\n\t@Test\n\tvoid checkWhenSimpDestinationMatchesThenVariablesExtracted() {\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().simpDestMatchers(\"destination/{id}\")\n\t\t\t.access(variable(\"id\").isEqualTo(\"3\"))\n\t\t\t.anyMessage()\n\t\t\t.denyAll()\n\t\t\t.build();\n\t\tMessageHeaders headers = new MessageHeaders(\n\t\t\t\tMap.of(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"destination/3\"));\n\t\tMessage<?> message = new GenericMessage<>(new Object(), headers);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkWhenMessageTypeAndPathPatternMatches() {\n\t\tMockito.when(this.provider.getIfUnique(any())).thenReturn(PathPatternMessageMatcher.withDefaults());\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().simpMessageDestMatchers(\"/destination\")\n\t\t\t.permitAll()\n\t\t\t.simpSubscribeDestMatchers(\"/destination\")\n\t\t\t.denyAll()\n\t\t\t.anyMessage()\n\t\t\t.denyAll()\n\t\t\t.build();\n\t\tMessageHeaders headers = new MessageHeaders(Map.of(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER,\n\t\t\t\tSimpMessageType.MESSAGE, SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination\"));\n\t\tMessage<?> message = new GenericMessage<>(new Object(), headers);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isTrue();\n\t\tMessageHeaders headers2 = new MessageHeaders(Map.of(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER,\n\t\t\t\tSimpMessageType.SUBSCRIBE, SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination\"));\n\t\tMessage<?> message2 = new GenericMessage<>(new Object(), headers2);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message2).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkWhenMessageTypeAndPathPatternMatchesCaseInsensitive() {\n\t\tPathPatternParser pathPatternParser = new PathPatternParser();\n\t\tpathPatternParser.setCaseSensitive(false);\n\t\tPathPatternMessageMatcher.Builder messageMatcherBuilder = PathPatternMessageMatcher\n\t\t\t.withPathPatternParser(pathPatternParser);\n\t\tMockito.when(this.provider.getIfUnique(any())).thenReturn(messageMatcherBuilder);\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().simpMessageDestMatchers(\"/desTinaTion\")\n\t\t\t.permitAll()\n\t\t\t.simpSubscribeDestMatchers(\"/desTinaTion\")\n\t\t\t.denyAll()\n\t\t\t.anyMessage()\n\t\t\t.denyAll()\n\t\t\t.build();\n\t\tMessageHeaders headers = new MessageHeaders(Map.of(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER,\n\t\t\t\tSimpMessageType.MESSAGE, SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination\"));\n\t\tMessage<?> message = new GenericMessage<>(new Object(), headers);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isTrue();\n\t\tMessageHeaders headers2 = new MessageHeaders(Map.of(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER,\n\t\t\t\tSimpMessageType.SUBSCRIBE, SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination\"));\n\t\tMessage<?> message2 = new GenericMessage<>(new Object(), headers2);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message2).isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkPatternMismatch() {\n\t\tAuthorizationManager<Message<?>> authorizationManager = builder().simpDestMatchers(\"/destination/*\")\n\t\t\t.permitAll()\n\t\t\t.anyMessage()\n\t\t\t.denyAll()\n\t\t\t.build();\n\t\tMessageHeaders headers = new MessageHeaders(\n\t\t\t\tMap.of(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination/sub/asdf\"));\n\t\tMessage<?> message = new GenericMessage<>(new Object(), headers);\n\t\tassertThat(authorizationManager.authorize(mock(Supplier.class), message).isGranted()).isFalse();\n\t}\n\n\tprivate MessageMatcherDelegatingAuthorizationManager.Builder builder() {\n\t\tMessageMatcherDelegatingAuthorizationManager.Builder builder = MessageMatcherDelegatingAuthorizationManager\n\t\t\t.builder();\n\t\tbuilder.setApplicationContext(this.context);\n\t\treturn builder;\n\t}\n\n\tprivate Builder variable(String name) {\n\t\treturn new Builder(name);\n\n\t}\n\n\tprivate static final class Builder {\n\n\t\tprivate final String name;\n\n\t\tprivate Builder(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tAuthorizationManager<MessageAuthorizationContext<?>> isEqualTo(String value) {\n\t\t\treturn (authentication, object) -> {\n\t\t\t\tString extracted = object.getVariables().get(this.name);\n\t\t\t\treturn new AuthorizationDecision(value.equals(extracted));\n\t\t\t};\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.context;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.core.annotation.AnnotatedMethod;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n *\n */\npublic class AuthenticationPrincipalArgumentResolverTests {\n\n\tprivate Object expectedPrincipal;\n\n\tprivate AuthenticationPrincipalArgumentResolver resolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.resolver = new AuthenticationPrincipalArgumentResolver();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void supportsParameterNoAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserNoAnnotation())).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsParameterAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserAnnotationObject())).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterCustomAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserCustomAnnotation())).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullAuthentication() throws Exception {\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullPrincipal() throws Exception {\n\t\tsetAuthenticationPrincipal(null);\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentString() throws Exception {\n\t\tsetAuthenticationPrincipal(\"john\");\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null)).isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentPrincipalStringOnObject() throws Exception {\n\t\tsetAuthenticationPrincipal(\"john\");\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationObject(), null)).isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentUserDetails() throws Exception {\n\t\tsetAuthenticationPrincipal(new User(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\")));\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationUserDetails(), null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomUserPrincipal() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationCustomUserPrincipal(), null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomAnnotation() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserCustomAnnotation(), null)).isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSpel() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.expectedPrincipal = principal.property;\n\t\tassertThat(this.resolver.resolveArgument(showUserSpel(), null)).isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSpelCopy() throws Exception {\n\t\tCopyUserPrincipal principal = new CopyUserPrincipal(\"property\");\n\t\tsetAuthenticationPrincipal(principal);\n\t\tObject resolveArgument = this.resolver.resolveArgument(showUserSpelCopy(), null);\n\t\tassertThat(resolveArgument).isEqualTo(principal);\n\t\tassertThat(resolveArgument).isNotSameAs(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSpelPrimitive() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.expectedPrincipal = principal.id;\n\t\tassertThat(this.resolver.resolveArgument(showUserSpelPrimitive(), null)).isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentErrorOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThatExceptionOfType(ClassCastException.class)\n\t\t\t.isThrownBy(() -> this.resolver.resolveArgument(showUserAnnotationErrorOnInvalidType(), null));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomserErrorOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThatExceptionOfType(ClassCastException.class)\n\t\t\t.isThrownBy(() -> this.resolver.resolveArgument(showUserAnnotationCurrentUserErrorOnInvalidType(), null));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentObject() throws Exception {\n\t\tsetAuthenticationPrincipal(new Object());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationObject(), null)).isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotation() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.expectedPrincipal = principal.id;\n\t\tassertThat(this.resolver.resolveArgument(showUserCustomMetaAnnotation(), null)).isEqualTo(principal.id);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotationTpl() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());\n\t\tthis.expectedPrincipal = principal.id;\n\t\tassertThat(this.resolver.resolveArgument(showUserCustomMetaAnnotationTpl(), null)).isEqualTo(principal.id);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAliasForOnInterfaceThenInherits() {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tassertThat(this.resolver.resolveArgument(showUserNoConcreteAnnotation(), null)).isEqualTo(principal.property);\n\t}\n\n\tprivate MethodParameter showUserNoAnnotation() {\n\t\treturn getMethodParameter(\"showUserNoAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showUserNoConcreteAnnotation() {\n\t\treturn getMethodParameter(\"showUserNoConcreteAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationString() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationErrorOnInvalidType() {\n\t\treturn getMethodParameter(\"showUserAnnotationErrorOnInvalidType\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationCurrentUserErrorOnInvalidType() {\n\t\treturn getMethodParameter(\"showUserAnnotationCurrentUserErrorOnInvalidType\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationUserDetails() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", UserDetails.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationCustomUserPrincipal() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", CustomUserPrincipal.class);\n\t}\n\n\tprivate MethodParameter showUserCustomAnnotation() {\n\t\treturn getMethodParameter(\"showUserCustomAnnotation\", CustomUserPrincipal.class);\n\t}\n\n\tprivate MethodParameter showUserCustomMetaAnnotation() {\n\t\treturn getMethodParameter(\"showUserCustomMetaAnnotation\", int.class);\n\t}\n\n\tprivate MethodParameter showUserCustomMetaAnnotationTpl() {\n\t\treturn getMethodParameter(\"showUserCustomMetaAnnotationTpl\", int.class);\n\t}\n\n\tprivate MethodParameter showUserSpel() {\n\t\treturn getMethodParameter(\"showUserSpel\", String.class);\n\t}\n\n\tprivate MethodParameter showUserSpelCopy() {\n\t\treturn getMethodParameter(\"showUserSpelCopy\", CopyUserPrincipal.class);\n\t}\n\n\tprivate MethodParameter showUserSpelPrimitive() {\n\t\treturn getMethodParameter(\"showUserSpelPrimitive\", int.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationObject() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", Object.class);\n\t}\n\n\tprivate MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {\n\t\tMethod method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);\n\t\treturn new AnnotatedMethod(method).getMethodParameters()[0];\n\t}\n\n\tprivate void setAuthenticationPrincipal(Object principal) {\n\t\tthis.expectedPrincipal = principal;\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(this.expectedPrincipal, \"password\", \"ROLE_USER\"));\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\tstatic @interface CurrentUser {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal(errorOnInvalidType = true)\n\tstatic @interface CurrentUserErrorOnInvalidType {\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\tpublic @interface CurrentUser2 {\n\n\t\t@AliasFor(annotation = AuthenticationPrincipal.class)\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal(expression = \"principal.{property}\")\n\tpublic @interface CurrentUser3 {\n\n\t\tString property() default \"\";\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\t@interface Property {\n\n\t\t@AliasFor(attribute = \"expression\", annotation = AuthenticationPrincipal.class)\n\t\tString value() default \"id\";\n\n\t}\n\n\tprivate interface TestInterface {\n\n\t\tvoid showUserNoConcreteAnnotation(@Property(\"property\") String property);\n\n\t}\n\n\tpublic static class TestController implements TestInterface {\n\n\t\tpublic void showUserNoAnnotation(String user) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void showUserNoConcreteAnnotation(String user) {\n\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotationErrorOnInvalidType(\n\t\t\t\t@AuthenticationPrincipal(errorOnInvalidType = true) String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotationCurrentUserErrorOnInvalidType(@CurrentUserErrorOnInvalidType String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal UserDetails user) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal CustomUserPrincipal user) {\n\t\t}\n\n\t\tpublic void showUserCustomAnnotation(@CurrentUser CustomUserPrincipal user) {\n\t\t}\n\n\t\tpublic void showUserCustomMetaAnnotation(@CurrentUser2(expression = \"principal.id\") int userId) {\n\t\t}\n\n\t\tpublic void showUserCustomMetaAnnotationTpl(@CurrentUser3(property = \"id\") int userId) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal Object user) {\n\t\t}\n\n\t\tpublic void showUserSpel(@AuthenticationPrincipal(expression = \"property\") String user) {\n\t\t}\n\n\t\tpublic void showUserSpelCopy(@AuthenticationPrincipal(\n\t\t\t\texpression = \"new org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)\") CopyUserPrincipal user) {\n\t\t}\n\n\t\tpublic void showUserSpelPrimitive(@AuthenticationPrincipal(expression = \"id\") int id) {\n\t\t}\n\n\t}\n\n\tstatic class CustomUserPrincipal {\n\n\t\tpublic final String property = \"property\";\n\n\t\tpublic final int id = 1;\n\n\t\tpublic Object getPrincipal() {\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic static class CopyUserPrincipal {\n\n\t\tpublic final String property;\n\n\t\tpublic CopyUserPrincipal(String property) {\n\t\t\tthis.property = property;\n\t\t}\n\n\t\tpublic CopyUserPrincipal(CopyUserPrincipal toCopy) {\n\t\t\tthis.property = toCopy.property;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (this == obj) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (obj == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (getClass() != obj.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tCopyUserPrincipal other = (CopyUserPrincipal) obj;\n\t\t\tif (this.property == null) {\n\t\t\t\tif (other.property != null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!this.property.equals(other.property)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tfinal int prime = 31;\n\t\t\tint result = 1;\n\t\t\tresult = prime * result + ((this.property == null) ? 0 : this.property.hashCode());\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/context/SecurityContextChannelInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.context;\n\nimport java.security.Principal;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.MessageHandler;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityContextChannelInterceptorTests {\n\n\t@Mock\n\tMessageChannel channel;\n\n\t@Mock\n\tMessageHandler handler;\n\n\t@Mock\n\tPrincipal principal;\n\n\tMessageBuilder<String> messageBuilder;\n\n\tAuthentication authentication;\n\n\tSecurityContextChannelInterceptor interceptor;\n\n\tAnonymousAuthenticationToken expectedAnonymous;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authentication = new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\");\n\t\tthis.messageBuilder = MessageBuilder.withPayload(\"payload\");\n\t\tthis.expectedAnonymous = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tthis.interceptor = new SecurityContextChannelInterceptor();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorNullHeader() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SecurityContextChannelInterceptor(null));\n\t}\n\n\t@Test\n\tpublic void preSendCustomHeader() {\n\t\tString headerName = \"header\";\n\t\tthis.interceptor = new SecurityContextChannelInterceptor(headerName);\n\t\tthis.messageBuilder.setHeader(headerName, this.authentication);\n\t\tthis.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t@Test\n\tpublic void preSendUserSet() {\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t@Test\n\tpublic void preSendWhenCustomSecurityContextHolderStrategyThenUserSet() {\n\t\tSecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());\n\t\tstrategy.setContext(new SecurityContextImpl(this.authentication));\n\t\tthis.interceptor.setSecurityContextHolderStrategy(strategy);\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tverify(strategy).getContext();\n\t\tassertThat(strategy.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t@Test\n\tpublic void setAnonymousAuthenticationNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.interceptor.setAnonymousAuthentication(null));\n\t}\n\n\t@Test\n\tpublic void preSendUsesCustomAnonymous() {\n\t\tthis.expectedAnonymous = new AnonymousAuthenticationToken(\"customKey\", \"customAnonymous\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_CUSTOM\"));\n\t\tthis.interceptor.setAnonymousAuthentication(this.expectedAnonymous);\n\t\tthis.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertAnonymous();\n\t}\n\n\t// SEC-2845\n\t@Test\n\tpublic void preSendUserNotAuthentication() {\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.principal);\n\t\tthis.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertAnonymous();\n\t}\n\n\t// SEC-2845\n\t@Test\n\tpublic void preSendUserNotSet() {\n\t\tthis.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertAnonymous();\n\t}\n\n\t// SEC-2845\n\t@Test\n\tpublic void preSendUserNotSetCustomAnonymous() {\n\t\tthis.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertAnonymous();\n\t}\n\n\t@Test\n\tpublic void afterSendCompletion() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tthis.interceptor.afterSendCompletion(this.messageBuilder.build(), this.channel, true, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void afterSendCompletionNullAuthentication() {\n\t\tthis.interceptor.afterSendCompletion(this.messageBuilder.build(), this.channel, true, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void afterSendCompletionWhenCustomSecurityContextHolderStrategyThenNullAuthentication() {\n\t\tSecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());\n\t\tstrategy.setContext(new SecurityContextImpl(this.authentication));\n\t\tthis.interceptor.setSecurityContextHolderStrategy(strategy);\n\t\tthis.interceptor.afterSendCompletion(this.messageBuilder.build(), this.channel, true, null);\n\t\tverify(strategy).clearContext();\n\t\tassertThat(strategy.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void beforeHandleUserSet() {\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t@Test\n\tpublic void beforeHandleWhenCustomSecurityContextHolderStrategyThenUserSet() {\n\t\tSecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());\n\t\tstrategy.setContext(new SecurityContextImpl(this.authentication));\n\t\tthis.interceptor.setSecurityContextHolderStrategy(strategy);\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tverify(strategy).getContext();\n\t\tassertThat(strategy.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t// SEC-2845\n\t@Test\n\tpublic void beforeHandleUserNotAuthentication() {\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.principal);\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertAnonymous();\n\t}\n\n\t// SEC-2845\n\t@Test\n\tpublic void beforeHandleUserNotSet() {\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertAnonymous();\n\t}\n\n\t@Test\n\tpublic void afterMessageHandledUserNotSet() {\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void afterMessageHandled() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void afterMessageHandledWhenCustomSecurityContextHolderStrategyThenUses() {\n\t\tSecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());\n\t\tstrategy.setContext(new SecurityContextImpl(this.authentication));\n\t\tthis.interceptor.setSecurityContextHolderStrategy(strategy);\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tverify(strategy).clearContext();\n\t}\n\n\t// SEC-2829\n\t@Test\n\tpublic void restoresOriginalContext() {\n\t\tTestingAuthenticationToken original = new TestingAuthenticationToken(\"original\", \"original\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(original);\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(original);\n\t}\n\n\t/**\n\t * If a user sends a websocket when processing another websocket\n\t *\n\t */\n\t@Test\n\tpublic void restoresOriginalContextNestedThreeDeep() {\n\t\tAnonymousAuthenticationToken anonymous = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tTestingAuthenticationToken origional = new TestingAuthenticationToken(\"original\", \"origional\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(origional);\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t\t// start send websocket\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, null);\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(anonymous.getName());\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t\t// end send websocket\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(origional);\n\t}\n\n\tprivate void assertAnonymous() {\n\t\tAuthentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(currentAuthentication).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tAnonymousAuthenticationToken anonymous = (AnonymousAuthenticationToken) currentAuthentication;\n\t\tassertThat(anonymous.getName()).isEqualTo(this.expectedAnonymous.getName());\n\t\tassertThat(anonymous.getAuthorities()).containsOnlyElementsOf(this.expectedAnonymous.getAuthorities());\n\t\tassertThat(anonymous.getKeyHash()).isEqualTo(this.expectedAnonymous.getKeyHash());\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/context/SecurityContextPropagationChannelInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.context;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.MessageHandler;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityContextPropagationChannelInterceptorTests {\n\n\t@Mock\n\tMessageChannel channel;\n\n\t@Mock\n\tMessageHandler handler;\n\n\tMessageBuilder<String> messageBuilder;\n\n\tAuthentication authentication;\n\n\tSecurityContextPropagationChannelInterceptor interceptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authentication = new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\");\n\t\tthis.messageBuilder = MessageBuilder.withPayload(\"payload\");\n\t\tthis.interceptor = new SecurityContextPropagationChannelInterceptor();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void preSendDefaultHeader() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tMessage<?> message = this.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertThat(message.getHeaders()).containsEntry(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t}\n\n\t@Test\n\tpublic void preSendCustomHeader() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tString headerName = \"header\";\n\t\tthis.interceptor = new SecurityContextPropagationChannelInterceptor(headerName);\n\t\tMessage<?> message = this.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertThat(message.getHeaders()).containsEntry(headerName, this.authentication);\n\t}\n\n\t@Test\n\tpublic void preSendWhenCustomSecurityContextHolderStrategyThenUserSet() {\n\t\tSecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());\n\t\tstrategy.setContext(new SecurityContextImpl(this.authentication));\n\t\tthis.interceptor.setSecurityContextHolderStrategy(strategy);\n\t\tMessage<?> message = this.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tthis.interceptor.beforeHandle(message, this.channel, this.handler);\n\t\tverify(strategy, times(2)).getContext();\n\t\tassertThat(strategy.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t@Test\n\tpublic void preSendUserNoContext() {\n\t\tMessage<?> message = this.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertThat(message.getHeaders()).containsKey(SimpMessageHeaderAccessor.USER_HEADER);\n\t\tassertThat(message.getHeaders().get(SimpMessageHeaderAccessor.USER_HEADER))\n\t\t\t.isInstanceOf(AnonymousAuthenticationToken.class);\n\t}\n\n\t@Test\n\tpublic void beforeHandleUserSet() {\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t@Test\n\tpublic void postReceiveUserSet() {\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.postReceive(this.messageBuilder.build(), this.channel);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t@Test\n\tpublic void authenticationIsPropagatedFromPreSendToPostReceive() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tMessage<?> message = this.interceptor.preSend(this.messageBuilder.build(), this.channel);\n\t\tassertThat(message.getHeaders().get(SimpMessageHeaderAccessor.USER_HEADER)).isSameAs(this.authentication);\n\t\tthis.interceptor.postReceive(message, this.channel);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t}\n\n\t@Test\n\tpublic void beforeHandleUserNotSet() {\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void afterMessageHandledUserNotSet() {\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void afterMessageHandled() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void restoresOriginalContext() {\n\t\tTestingAuthenticationToken original = new TestingAuthenticationToken(\"original\", \"original\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(original);\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication);\n\t\tthis.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication);\n\t\tthis.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(original);\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/handler/invocation/ResolvableMethod.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.handler.invocation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.aop.framework.ProxyFactory;\nimport org.springframework.aop.target.EmptyTargetSource;\nimport org.springframework.cglib.core.SpringNamingPolicy;\nimport org.springframework.cglib.proxy.Callback;\nimport org.springframework.cglib.proxy.Enhancer;\nimport org.springframework.cglib.proxy.Factory;\nimport org.springframework.cglib.proxy.MethodProxy;\nimport org.springframework.core.MethodIntrospector;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.SynthesizingMethodParameter;\nimport org.springframework.objenesis.ObjenesisException;\nimport org.springframework.objenesis.SpringObjenesis;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.util.ReflectionUtils;\n\n/**\n * NOTE: This class is a replica of the same class in spring-web so it can be used for\n * tests in spring-messaging.\n *\n * <p>\n * Convenience class to resolve method parameters from hints.\n *\n * <h1>Background</h1>\n *\n * <p>\n * When testing annotated methods we create test classes such as \"TestController\" with a\n * diverse range of method signatures representing supported annotations and argument\n * types. It becomes challenging to use naming strategies to keep track of methods and\n * arguments especially in combination with variables for reflection metadata.\n *\n * <p>\n * The idea with {@link ResolvableMethod} is NOT to rely on naming techniques but to use\n * hints to zero in on method parameters. Such hints can be strongly typed and explicit\n * about what is being tested.\n *\n * <h2>1. Declared Return Type</h2>\n *\n * When testing return types it's likely to have many methods with a unique return type,\n * possibly with or without an annotation.\n *\n * <pre>\n * import static org.springframework.web.method.ResolvableMethod.on;\n * import static org.springframework.web.method.MvcAnnotationPredicates.requestMapping;\n *\n * // Return type\n * on(TestController.class).resolveReturnType(Foo.class);\n * on(TestController.class).resolveReturnType(List.class, Foo.class);\n * on(TestController.class).resolveReturnType(Mono.class, responseEntity(Foo.class));\n *\n * // Annotation + return type\n * on(TestController.class).annotPresent(RequestMapping.class).resolveReturnType(Bar.class);\n *\n * // Annotation not present\n * on(TestController.class).annotNotPresent(RequestMapping.class).resolveReturnType();\n *\n * // Annotation with attributes\n * on(TestController.class).annot(requestMapping(\"/foo\").params(\"p\")).resolveReturnType();\n * </pre>\n *\n * <h2>2. Method Arguments</h2>\n *\n * When testing method arguments it's more likely to have one or a small number of methods\n * with a wide array of argument types and parameter annotations.\n *\n * <pre>\n * import static org.springframework.web.method.MvcAnnotationPredicates.requestParam;\n *\n * ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named(\"handle\").build();\n *\n * testMethod.arg(Foo.class);\n * testMethod.annotPresent(RequestParam.class).arg(Integer.class);\n * testMethod.annotNotPresent(RequestParam.class)).arg(Integer.class);\n * testMethod.annot(requestParam().name(\"c\").notRequired()).arg(Integer.class);\n * </pre>\n *\n * <h3>3. Mock Handler Method Invocation</h3>\n *\n * Locate a method by invoking it through a proxy of the target handler:\n *\n * <pre>\n * ResolvableMethod.on(TestController.class).mockCall((o) -> o.handle(null)).method();\n * </pre>\n *\n * @author Rossen Stoyanchev\n * @since 5.2\n */\npublic final class ResolvableMethod {\n\n\tprivate static final Log logger = LogFactory.getLog(ResolvableMethod.class);\n\n\tprivate static final SpringObjenesis objenesis = new SpringObjenesis();\n\n\t// Matches ValueConstants.DEFAULT_NONE (spring-web and spring-messaging)\n\tprivate static final String DEFAULT_VALUE_NONE = \"\\n\\t\\t\\n\\t\\t\\n\\uE000\\uE001\\uE002\\n\\t\\t\\t\\t\\n\";\n\n\tprivate final Method method;\n\n\tprivate ResolvableMethod(Method method) {\n\t\tAssert.notNull(method, \"'method' is required\");\n\t\tthis.method = method;\n\t}\n\n\t/**\n\t * Return the resolved method.\n\t */\n\tpublic Method method() {\n\t\treturn this.method;\n\t}\n\n\t/**\n\t * Return the declared return type of the resolved method.\n\t */\n\tpublic MethodParameter returnType() {\n\t\treturn new SynthesizingMethodParameter(this.method, -1);\n\t}\n\n\t/**\n\t * Find a unique argument matching the given type.\n\t * @param type the expected type\n\t * @param generics optional array of generic types\n\t */\n\tpublic MethodParameter arg(Class<?> type, Class<?>... generics) {\n\t\treturn new ArgResolver().arg(type, generics);\n\t}\n\n\t/**\n\t * Find a unique argument matching the given type.\n\t * @param type the expected type\n\t * @param generic at least one generic type\n\t * @param generics optional array of generic types\n\t */\n\tpublic MethodParameter arg(Class<?> type, ResolvableType generic, ResolvableType... generics) {\n\t\treturn new ArgResolver().arg(type, generic, generics);\n\t}\n\n\t/**\n\t * Find a unique argument matching the given type.\n\t * @param type the expected type\n\t */\n\tpublic MethodParameter arg(ResolvableType type) {\n\t\treturn new ArgResolver().arg(type);\n\t}\n\n\t/**\n\t * Filter on method arguments with annotation. See\n\t * {@link org.springframework.web.method.MvcAnnotationPredicates}.\n\t */\n\t@SafeVarargs\n\tpublic final ArgResolver annot(Predicate<MethodParameter>... filter) {\n\t\treturn new ArgResolver(filter);\n\t}\n\n\t@SafeVarargs\n\tpublic final ArgResolver annotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\treturn new ArgResolver().annotPresent(annotationTypes);\n\t}\n\n\t/**\n\t * Filter on method arguments that don't have the given annotation type(s).\n\t * @param annotationTypes the annotation types\n\t */\n\t@SafeVarargs\n\tpublic final ArgResolver annotNotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\treturn new ArgResolver().annotNotPresent(annotationTypes);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ResolvableMethod=\" + formatMethod();\n\t}\n\n\tprivate String formatMethod() {\n\t\treturn (method().getName() + Arrays.stream(this.method.getParameters())\n\t\t\t.map(this::formatParameter)\n\t\t\t.collect(Collectors.joining(\",\\n\\t\", \"(\\n\\t\", \"\\n)\")));\n\t}\n\n\tprivate String formatParameter(Parameter param) {\n\t\tAnnotation[] anns = param.getAnnotations();\n\t\treturn (anns.length > 0)\n\t\t\t\t? Arrays.stream(anns).map(this::formatAnnotation).collect(Collectors.joining(\",\", \"[\", \"]\")) + \" \"\n\t\t\t\t\t\t+ param\n\t\t\t\t: param.toString();\n\t}\n\n\tprivate String formatAnnotation(Annotation annotation) {\n\t\tMap<String, Object> map = AnnotationUtils.getAnnotationAttributes(annotation);\n\t\tmap.forEach((key, value) -> {\n\t\t\tif (value.equals(DEFAULT_VALUE_NONE)) {\n\t\t\t\tmap.put(key, \"NONE\");\n\t\t\t}\n\t\t});\n\t\treturn annotation.annotationType().getName() + map;\n\t}\n\n\tprivate static ResolvableType toResolvableType(Class<?> type, Class<?>... generics) {\n\t\treturn (ObjectUtils.isEmpty(generics) ? ResolvableType.forClass(type)\n\t\t\t\t: ResolvableType.forClassWithGenerics(type, generics));\n\t}\n\n\tprivate static ResolvableType toResolvableType(Class<?> type, ResolvableType generic, ResolvableType... generics) {\n\t\tResolvableType[] genericTypes = new ResolvableType[generics.length + 1];\n\t\tgenericTypes[0] = generic;\n\t\tSystem.arraycopy(generics, 0, genericTypes, 1, generics.length);\n\t\treturn ResolvableType.forClassWithGenerics(type, genericTypes);\n\t}\n\n\t/**\n\t * Create a {@code ResolvableMethod} builder for the given handler class.\n\t */\n\tpublic static <T> Builder<T> on(Class<T> objectClass) {\n\t\treturn new Builder<>(objectClass);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> T initProxy(Class<?> type, MethodInvocationInterceptor interceptor) {\n\t\tAssert.notNull(type, \"'type' must not be null\");\n\t\tif (type.isInterface()) {\n\t\t\tProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE);\n\t\t\tfactory.addInterface(type);\n\t\t\tfactory.addInterface(Supplier.class);\n\t\t\tfactory.addAdvice(interceptor);\n\t\t\treturn (T) factory.getProxy();\n\t\t}\n\t\telse {\n\t\t\tEnhancer enhancer = new Enhancer();\n\t\t\tenhancer.setSuperclass(type);\n\t\t\tenhancer.setInterfaces(new Class<?>[] { Supplier.class });\n\t\t\tenhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);\n\t\t\tenhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);\n\t\t\tClass<?> proxyClass = enhancer.createClass();\n\t\t\tObject proxy = null;\n\t\t\tif (objenesis.isWorthTrying()) {\n\t\t\t\ttry {\n\t\t\t\t\tproxy = objenesis.newInstance(proxyClass, enhancer.getUseCache());\n\t\t\t\t}\n\t\t\t\tcatch (ObjenesisException ex) {\n\t\t\t\t\tlogger.debug(\"Objenesis failed, falling back to default constructor\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (proxy == null) {\n\t\t\t\ttry {\n\t\t\t\t\tproxy = ReflectionUtils.accessibleConstructor(proxyClass).newInstance();\n\t\t\t\t}\n\t\t\t\tcatch (Throwable ex) {\n\t\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\t\"Unable to instantiate proxy \" + \"via both Objenesis and default constructor fails as well\",\n\t\t\t\t\t\t\tex);\n\t\t\t\t}\n\t\t\t}\n\t\t\t((Factory) proxy).setCallbacks(new Callback[] { interceptor });\n\t\t\treturn (T) proxy;\n\t\t}\n\t}\n\n\t/**\n\t * Builder for {@code ResolvableMethod}.\n\t */\n\tpublic static final class Builder<T> {\n\n\t\tprivate final Class<?> objectClass;\n\n\t\tprivate final List<Predicate<Method>> filters = new ArrayList<>(4);\n\n\t\tprivate Builder(Class<?> objectClass) {\n\t\t\tAssert.notNull(objectClass, \"Class must not be null\");\n\t\t\tthis.objectClass = objectClass;\n\t\t}\n\n\t\tprivate void addFilter(String message, Predicate<Method> filter) {\n\t\t\tthis.filters.add(new LabeledPredicate<>(message, filter));\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods with the given name.\n\t\t */\n\t\tpublic Builder<T> named(String methodName) {\n\t\t\taddFilter(\"methodName=\" + methodName, (method) -> method.getName().equals(methodName));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods with the given parameter types.\n\t\t */\n\t\tpublic Builder<T> argTypes(Class<?>... argTypes) {\n\t\t\taddFilter(\"argTypes=\" + Arrays.toString(argTypes), (method) -> ObjectUtils.isEmpty(argTypes)\n\t\t\t\t\t? method.getParameterCount() == 0 : Arrays.equals(method.getParameterTypes(), argTypes));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on annotated methods. See\n\t\t * {@link org.springframework.web.method.MvcAnnotationPredicates}.\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final Builder<T> annot(Predicate<Method>... filters) {\n\t\t\tthis.filters.addAll(Arrays.asList(filters));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods annotated with the given annotation type.\n\t\t * @see #annot(Predicate[]) See\n\t\t * {@link org.springframework.web.method.MvcAnnotationPredicates}.\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final Builder<T> annotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\t\tString message = \"annotationPresent=\" + Arrays.toString(annotationTypes);\n\t\t\taddFilter(message, (candidate) -> Arrays.stream(annotationTypes)\n\t\t\t\t.allMatch((annotType) -> AnnotatedElementUtils.findMergedAnnotation(candidate, annotType) != null));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods not annotated with the given annotation type.\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final Builder<T> annotNotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\t\tString message = \"annotationNotPresent=\" + Arrays.toString(annotationTypes);\n\t\t\taddFilter(message, (candidate) -> {\n\t\t\t\tif (annotationTypes.length != 0) {\n\t\t\t\t\treturn Arrays.stream(annotationTypes)\n\t\t\t\t\t\t.noneMatch((\n\t\t\t\t\t\t\t\tannotType) -> AnnotatedElementUtils.findMergedAnnotation(candidate, annotType) != null);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn candidate.getAnnotations().length == 0;\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods returning the given type.\n\t\t * @param returnType the return type\n\t\t * @param generics optional array of generic types\n\t\t */\n\t\tpublic Builder<T> returning(Class<?> returnType, Class<?>... generics) {\n\t\t\treturn returning(toResolvableType(returnType, generics));\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods returning the given type with generics.\n\t\t * @param returnType the return type\n\t\t * @param generic at least one generic type\n\t\t * @param generics optional extra generic types\n\t\t */\n\t\tpublic Builder<T> returning(Class<?> returnType, ResolvableType generic, ResolvableType... generics) {\n\t\t\treturn returning(toResolvableType(returnType, generic, generics));\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods returning the given type.\n\t\t * @param returnType the return type\n\t\t */\n\t\tpublic Builder<T> returning(ResolvableType returnType) {\n\t\t\tString expected = returnType.toString();\n\t\t\tString message = \"returnType=\" + expected;\n\t\t\taddFilter(message, (m) -> expected.equals(ResolvableType.forMethodReturnType(m).toString()));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build a {@code ResolvableMethod} from the provided filters which must resolve\n\t\t * to a unique, single method.\n\t\t * <p>\n\t\t * See additional resolveXxx shortcut methods going directly to {@link Method} or\n\t\t * return type parameter.\n\t\t * @throws IllegalStateException for no match or multiple matches\n\t\t */\n\t\tpublic ResolvableMethod method() {\n\t\t\tSet<Method> methods = MethodIntrospector.selectMethods(this.objectClass, this::isMatch);\n\t\t\tAssert.state(!methods.isEmpty(), () -> \"No matching method: \" + this);\n\t\t\tAssert.state(methods.size() == 1, () -> \"Multiple matching methods: \" + this + formatMethods(methods));\n\t\t\treturn new ResolvableMethod(methods.iterator().next());\n\t\t}\n\n\t\tprivate boolean isMatch(Method method) {\n\t\t\treturn this.filters.stream().allMatch((p) -> p.test(method));\n\t\t}\n\n\t\tprivate String formatMethods(Set<Method> methods) {\n\t\t\treturn \"\\nMatched:\\n\" + methods.stream()\n\t\t\t\t.map(Method::toGenericString)\n\t\t\t\t.collect(Collectors.joining(\",\\n\\t\", \"[\\n\\t\", \"\\n]\"));\n\t\t}\n\n\t\tpublic ResolvableMethod mockCall(Consumer<T> invoker) {\n\t\t\tMethodInvocationInterceptor interceptor = new MethodInvocationInterceptor();\n\t\t\tT proxy = initProxy(this.objectClass, interceptor);\n\t\t\tinvoker.accept(proxy);\n\t\t\tMethod method = interceptor.getInvokedMethod();\n\t\t\treturn new ResolvableMethod(method);\n\t\t}\n\n\t\t// Build & resolve shortcuts...\n\t\t/**\n\t\t * Resolve and return the {@code Method} equivalent to:\n\t\t * <p>\n\t\t * {@code build().method()}\n\t\t */\n\t\tpublic Method resolveMethod() {\n\t\t\treturn method().method();\n\t\t}\n\n\t\t/**\n\t\t * Resolve and return the {@code Method} equivalent to:\n\t\t * <p>\n\t\t * {@code named(methodName).build().method()}\n\t\t */\n\t\tpublic Method resolveMethod(String methodName) {\n\t\t\treturn named(methodName).method().method();\n\t\t}\n\n\t\t/**\n\t\t * Resolve and return the declared return type equivalent to:\n\t\t * <p>\n\t\t * {@code build().returnType()}\n\t\t */\n\t\tpublic MethodParameter resolveReturnType() {\n\t\t\treturn method().returnType();\n\t\t}\n\n\t\t/**\n\t\t * Shortcut to the unique return type equivalent to:\n\t\t * <p>\n\t\t * {@code returning(returnType).build().returnType()}\n\t\t * @param returnType the return type\n\t\t * @param generics optional array of generic types\n\t\t */\n\t\tpublic MethodParameter resolveReturnType(Class<?> returnType, Class<?>... generics) {\n\t\t\treturn returning(returnType, generics).method().returnType();\n\t\t}\n\n\t\t/**\n\t\t * Shortcut to the unique return type equivalent to:\n\t\t * <p>\n\t\t * {@code returning(returnType).build().returnType()}\n\t\t * @param returnType the return type\n\t\t * @param generic at least one generic type\n\t\t * @param generics optional extra generic types\n\t\t */\n\t\tpublic MethodParameter resolveReturnType(Class<?> returnType, ResolvableType generic,\n\t\t\t\tResolvableType... generics) {\n\t\t\treturn returning(returnType, generic, generics).method().returnType();\n\t\t}\n\n\t\tpublic MethodParameter resolveReturnType(ResolvableType returnType) {\n\t\t\treturn returning(returnType).method().returnType();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"ResolvableMethod.Builder[\\n\" + \"\\tobjectClass = \" + this.objectClass.getName() + \",\\n\"\n\t\t\t\t\t+ \"\\tfilters = \" + formatFilters() + \"\\n]\";\n\t\t}\n\n\t\tprivate String formatFilters() {\n\t\t\treturn this.filters.stream()\n\t\t\t\t.map(Object::toString)\n\t\t\t\t.collect(Collectors.joining(\",\\n\\t\\t\", \"[\\n\\t\\t\", \"\\n\\t]\"));\n\t\t}\n\n\t}\n\n\t/**\n\t * Predicate with a descriptive label.\n\t */\n\tprivate static final class LabeledPredicate<T> implements Predicate<T> {\n\n\t\tprivate final String label;\n\n\t\tprivate final Predicate<T> delegate;\n\n\t\tprivate LabeledPredicate(String label, Predicate<T> delegate) {\n\t\t\tthis.label = label;\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean test(T method) {\n\t\t\treturn this.delegate.test(method);\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate<T> and(Predicate<? super T> other) {\n\t\t\treturn this.delegate.and(other);\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate<T> negate() {\n\t\t\treturn this.delegate.negate();\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate<T> or(Predicate<? super T> other) {\n\t\t\treturn this.delegate.or(other);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn this.label;\n\t\t}\n\n\t}\n\n\t/**\n\t * Resolver for method arguments.\n\t */\n\tpublic final class ArgResolver {\n\n\t\tprivate final List<Predicate<MethodParameter>> filters = new ArrayList<>(4);\n\n\t\t@SafeVarargs\n\t\tprivate ArgResolver(Predicate<MethodParameter>... filter) {\n\t\t\tthis.filters.addAll(Arrays.asList(filter));\n\t\t}\n\n\t\t/**\n\t\t * Filter on method arguments with annotations. See\n\t\t * {@link org.springframework.web.method.MvcAnnotationPredicates}.\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final ArgResolver annot(Predicate<MethodParameter>... filters) {\n\t\t\tthis.filters.addAll(Arrays.asList(filters));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on method arguments that have the given annotations.\n\t\t * @param annotationTypes the annotation types\n\t\t * @see #annot(Predicate[]) See\n\t\t * {@link org.springframework.web.method.MvcAnnotationPredicates}.\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final ArgResolver annotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\t\tthis.filters.add((param) -> Arrays.stream(annotationTypes).allMatch(param::hasParameterAnnotation));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on method arguments that don't have the given annotations.\n\t\t * @param annotationTypes the annotation types\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final ArgResolver annotNotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\t\tthis.filters.add((param) -> (annotationTypes.length > 0)\n\t\t\t\t\t? Arrays.stream(annotationTypes).noneMatch(param::hasParameterAnnotation)\n\t\t\t\t\t: param.getParameterAnnotations().length == 0);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Resolve the argument also matching to the given type.\n\t\t * @param type the expected type\n\t\t */\n\t\tpublic MethodParameter arg(Class<?> type, Class<?>... generics) {\n\t\t\treturn arg(toResolvableType(type, generics));\n\t\t}\n\n\t\t/**\n\t\t * Resolve the argument also matching to the given type.\n\t\t * @param type the expected type\n\t\t */\n\t\tpublic MethodParameter arg(Class<?> type, ResolvableType generic, ResolvableType... generics) {\n\t\t\treturn arg(toResolvableType(type, generic, generics));\n\t\t}\n\n\t\t/**\n\t\t * Resolve the argument also matching to the given type.\n\t\t * @param type the expected type\n\t\t */\n\t\tpublic MethodParameter arg(ResolvableType type) {\n\t\t\tthis.filters.add((p) -> type.toString().equals(ResolvableType.forMethodParameter(p).toString()));\n\t\t\treturn arg();\n\t\t}\n\n\t\t/**\n\t\t * Resolve the argument.\n\t\t */\n\t\tpublic MethodParameter arg() {\n\t\t\tList<MethodParameter> matches = applyFilters();\n\t\t\tAssert.state(!matches.isEmpty(), () -> \"No matching arg in method\\n\" + formatMethod());\n\t\t\tAssert.state(matches.size() == 1,\n\t\t\t\t\t() -> \"Multiple matching args in method\\n\" + formatMethod() + \"\\nMatches:\\n\\t\" + matches);\n\t\t\treturn matches.get(0);\n\t\t}\n\n\t\tprivate List<MethodParameter> applyFilters() {\n\t\t\tList<MethodParameter> matches = new ArrayList<>();\n\t\t\tfor (int i = 0; i < ResolvableMethod.this.method.getParameterCount(); i++) {\n\t\t\t\tMethodParameter param = new SynthesizingMethodParameter(ResolvableMethod.this.method, i);\n\t\t\t\tif (this.filters.stream().allMatch((p) -> p.test(param))) {\n\t\t\t\t\tmatches.add(param);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn matches;\n\t\t}\n\n\t}\n\n\tprivate static class MethodInvocationInterceptor\n\t\t\timplements org.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor {\n\n\t\tprivate Method invokedMethod;\n\n\t\tMethod getInvokedMethod() {\n\t\t\treturn this.invokedMethod;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) {\n\t\t\tif (ReflectionUtils.isObjectMethod(method)) {\n\t\t\t\treturn ReflectionUtils.invokeMethod(method, object, args);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.invokedMethod = method;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Object invoke(org.aopalliance.intercept.MethodInvocation inv) throws Throwable {\n\t\t\treturn intercept(inv.getThis(), inv.getMethod(), inv.getArguments(), null);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.handler.invocation.reactive;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.core.annotation.AnnotatedMethod;\nimport org.springframework.core.annotation.SynthesizingMethodParameter;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.messaging.handler.invocation.ResolvableMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class AuthenticationPrincipalArgumentResolverTests {\n\n\tprivate AuthenticationPrincipalArgumentResolver resolver = new AuthenticationPrincipalArgumentResolver();\n\n\t@Test\n\tpublic void supportsParameterWhenAuthenticationPrincipalThenTrue() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"authenticationPrincipalOnMonoUserDetails\"))).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAuthenticationPrincipalAndEmptyContextThenNull() {\n\t\tObject result = this.resolver.resolveArgument(arg0(\"authenticationPrincipalOnMonoUserDetails\"), null).block();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAuthenticationPrincipalThenFound() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\t// @formatter:off\n\t\tMono<UserDetails> result = (Mono<UserDetails>) this.resolver\n\t\t\t\t.resolveArgument(arg0(\"authenticationPrincipalOnMonoUserDetails\"), null)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(result.block()).isEqualTo(authentication.getPrincipal());\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void authenticationPrincipalOnMonoUserDetails(@AuthenticationPrincipal Mono<UserDetails> user) {\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenCurrentUserThenTrue() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"currentUserOnMonoUserDetails\"))).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenMonoAndAuthenticationPrincipalThenFound() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\t// @formatter:off\n\t\tMono<UserDetails> result = (Mono<UserDetails>) this.resolver\n\t\t\t\t.resolveArgument(arg0(\"currentUserOnMonoUserDetails\"), null)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(result.block()).isEqualTo(authentication.getPrincipal());\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void currentUserOnMonoUserDetails(@CurrentUser Mono<UserDetails> user) {\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenExpressionThenFound() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\t// @formatter:off\n\t\tMono<String> result = (Mono<String>) this.resolver\n\t\t\t\t.resolveArgument(arg0(\"authenticationPrincipalExpression\"), null)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(result.block()).isEqualTo(authentication.getName());\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void authenticationPrincipalExpression(\n\t\t\t@AuthenticationPrincipal(expression = \"username\") Mono<String> username) {\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenExpressionPrimitiveThenFound() {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\t// @formatter:off\n\t\tMono<Object> result = this.resolver\n\t\t\t\t.resolveArgument(arg0(\"authenticationPrincipalExpressionPrimitive\"), null)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(new TestingAuthenticationToken(principal, \"password\", \"ROLE_USER\")));\n\t\t// @formatter:on\n\t\tassertThat(result.block()).isEqualTo(principal.id);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void authenticationPrincipalExpressionPrimitive(@AuthenticationPrincipal(expression = \"id\") int username) {\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenNotAnnotatedThenFalse() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"monoUserDetails\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAliasForOnInterfaceThenInherits() {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tAuthentication authentication = new TestingAuthenticationToken(principal, \"password\", \"ROLE_USER\");\n\t\tResolvableMethod method = ResolvableMethod.on(TestController.class)\n\t\t\t.named(\"showUserNoConcreteAnnotation\")\n\t\t\t.method();\n\t\tMethodParameter parameter = new AnnotatedMethod(method.method()).getMethodParameters()[0];\n\t\tMono<Object> result = this.resolver.resolveArgument(parameter, null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));\n\t\tassertThat(result.block()).isEqualTo(principal.property);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void monoUserDetails(Mono<UserDetails> user) {\n\t}\n\n\tprivate MethodParameter arg0(String methodName) {\n\t\tResolvableMethod method = ResolvableMethod.on(getClass()).named(methodName).method();\n\t\treturn new SynthesizingMethodParameter(method.method(), 0);\n\t}\n\n\t@AuthenticationPrincipal\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface CurrentUser {\n\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotation() {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tMono<Object> result = this.resolver.resolveArgument(arg0(\"showUserCustomMetaAnnotation\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder\n\t\t\t\t.withAuthentication(new TestingAuthenticationToken(principal, \"password\", \"ROLE_USER\")));\n\t\tassertThat(result.block()).isEqualTo(principal.id);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotationTpl() {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tthis.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());\n\t\tMono<Object> result = this.resolver.resolveArgument(arg0(\"showUserCustomMetaAnnotationTpl\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder\n\t\t\t\t.withAuthentication(new TestingAuthenticationToken(principal, \"password\", \"ROLE_USER\")));\n\t\tassertThat(result.block()).isEqualTo(principal.id);\n\t}\n\n\tpublic void showUserCustomMetaAnnotation(@CurrentUser2(expression = \"principal.id\") int userId) {\n\t}\n\n\tpublic void showUserCustomMetaAnnotationTpl(@CurrentUser3(property = \"id\") int userId) {\n\t}\n\n\tstatic class CustomUserPrincipal {\n\n\t\tpublic final int id = 1;\n\n\t\tpublic final String property = \"property\";\n\n\t\tpublic Object getPrincipal() {\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\tpublic @interface CurrentUser2 {\n\n\t\t@AliasFor(annotation = AuthenticationPrincipal.class)\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal(expression = \"principal.{property}\")\n\tpublic @interface CurrentUser3 {\n\n\t\tString property() default \"\";\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\t@interface Property {\n\n\t\t@AliasFor(attribute = \"expression\", annotation = AuthenticationPrincipal.class)\n\t\tString value() default \"id\";\n\n\t}\n\n\tprivate interface TestInterface {\n\n\t\tvoid showUserNoConcreteAnnotation(@Property(\"property\") String property);\n\n\t}\n\n\tprivate static class TestController implements TestInterface {\n\n\t\t@Override\n\t\tpublic void showUserNoConcreteAnnotation(String user) {\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/CurrentSecurityContextArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.handler.invocation.reactive;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.core.annotation.SynthesizingMethodParameter;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.messaging.handler.invocation.ResolvableMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class CurrentSecurityContextArgumentResolverTests {\n\n\tprivate CurrentSecurityContextArgumentResolver resolver = new CurrentSecurityContextArgumentResolver();\n\n\t@Test\n\tpublic void supportsParameterWhenAuthenticationPrincipalThenTrue() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"currentSecurityContextOnMonoSecurityContext\"))).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenMonoSecurityContextNoAnnotationThenTrue() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"currentSecurityContextOnMonoSecurityContextNoAnnotation\")))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenMonoCustomSecurityContextNoAnnotationThenTrue() {\n\t\tassertThat(\n\t\t\t\tthis.resolver.supportsParameter(arg0(\"currentCustomSecurityContextOnMonoSecurityContextNoAnnotation\")))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenNoSecurityContextNoAnnotationThenFalse() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"currentSecurityContextOnMonoStringNoAnnotation\"))).isFalse();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAuthenticationPrincipalAndEmptyContextThenNull() {\n\t\tObject result = this.resolver.resolveArgument(arg0(\"currentSecurityContextOnMonoSecurityContext\"), null)\n\t\t\t.block();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAuthenticationPrincipalThenFound() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tMono<SecurityContext> result = (Mono<SecurityContext>) this.resolver\n\t\t\t.resolveArgument(arg0(\"currentSecurityContextOnMonoSecurityContext\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t.block();\n\t\tassertThat(result.block().getAuthentication()).isEqualTo(authentication);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void currentSecurityContextOnMonoSecurityContext(@CurrentSecurityContext Mono<SecurityContext> context) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void currentSecurityContextOnMonoSecurityContextNoAnnotation(Mono<SecurityContext> context) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void currentCustomSecurityContextOnMonoSecurityContextNoAnnotation(Mono<CustomSecurityContext> context) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void currentSecurityContextOnMonoStringNoAnnotation(Mono<String> context) {\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenCurrentUserThenTrue() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"currentUserOnMonoUserDetails\"))).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenMonoAndAuthenticationPrincipalThenFound() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tMono<UserDetails> result = (Mono<UserDetails>) this.resolver\n\t\t\t.resolveArgument(arg0(\"currentUserOnMonoUserDetails\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t.block();\n\t\tassertThat(result.block()).isEqualTo(authentication.getPrincipal());\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void currentUserOnMonoUserDetails(@CurrentUser Mono<UserDetails> user) {\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenExpressionThenFound() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tMono<String> result = (Mono<String>) this.resolver\n\t\t\t.resolveArgument(arg0(\"authenticationPrincipalExpression\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t.block();\n\t\tassertThat(result.block()).isEqualTo(authentication.getName());\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void authenticationPrincipalExpression(\n\t\t\t@CurrentSecurityContext(expression = \"authentication?.principal?.username\") Mono<String> username) {\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenNotAnnotatedThenFalse() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"monoUserDetails\"))).isFalse();\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void monoUserDetails(Mono<UserDetails> user) {\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenSecurityContextNotAnnotatedThenTrue() {\n\t\tassertThat(this.resolver.supportsParameter(arg0(\"monoSecurityContext\"))).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenMonoSecurityContextNoAnnotationThenFound() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tMono<SecurityContext> result = (Mono<SecurityContext>) this.resolver\n\t\t\t.resolveArgument(arg0(\"monoSecurityContext\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t.block();\n\t\tassertThat(result.block().getAuthentication().getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void monoSecurityContext(Mono<SecurityContext> securityContext) {\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenMonoCustomSecurityContextNoAnnotationThenFound() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tCustomSecurityContext securityContext = new CustomSecurityContext();\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tMono<CustomSecurityContext> result = (Mono<CustomSecurityContext>) this.resolver\n\t\t\t.resolveArgument(arg0(\"monoCustomSecurityContext\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))\n\t\t\t.block();\n\t\tassertThat(result.block().getAuthentication().getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotation() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tCustomSecurityContext securityContext = new CustomSecurityContext();\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tMono<UserDetails> result = (Mono<UserDetails>) this.resolver\n\t\t\t.resolveArgument(arg0(\"showUserCustomMetaAnnotation\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))\n\t\t\t.block();\n\t\tassertThat(result.block()).isEqualTo(authentication.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotationTpl() {\n\t\tthis.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tCustomSecurityContext securityContext = new CustomSecurityContext();\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tMono<UserDetails> result = (Mono<UserDetails>) this.resolver\n\t\t\t.resolveArgument(arg0(\"showUserCustomMetaAnnotationTpl\"), null)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))\n\t\t\t.block();\n\t\tassertThat(result.block()).isEqualTo(authentication.getPrincipal());\n\t}\n\n\tprivate void showUserCustomMetaAnnotation(\n\t\t\t@AliasedCurrentSecurityContext(expression = \"authentication.principal\") Mono<UserDetails> user) {\n\t}\n\n\tprivate void showUserCustomMetaAnnotationTpl(\n\t\t\t@CurrentAuthenticationProperty(property = \"principal\") Mono<UserDetails> user) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void monoCustomSecurityContext(Mono<CustomSecurityContext> securityContext) {\n\t}\n\n\tprivate MethodParameter arg0(String methodName) {\n\t\tResolvableMethod method = ResolvableMethod.on(getClass()).named(methodName).method();\n\t\treturn new SynthesizingMethodParameter(method.method(), 0);\n\t}\n\n\t@CurrentSecurityContext(expression = \"authentication?.principal\")\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface CurrentUser {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext\n\t@interface AliasedCurrentSecurityContext {\n\n\t\t@AliasFor(annotation = CurrentSecurityContext.class)\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(expression = \"authentication.{property}\")\n\t@interface CurrentAuthenticationProperty {\n\n\t\tString property() default \"\";\n\n\t}\n\n\tstatic class CustomSecurityContext implements SecurityContext {\n\n\t\tprivate Authentication authentication;\n\n\t\t@Override\n\t\tpublic Authentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAuthentication(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/util/matcher/AndMessageMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.Message;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNullPointerException;\nimport static org.mockito.BDDMockito.given;\n\n@ExtendWith(MockitoExtension.class)\npublic class AndMessageMatcherTests {\n\n\t@Mock\n\tprivate MessageMatcher<Object> delegate;\n\n\t@Mock\n\tprivate MessageMatcher<Object> delegate2;\n\n\t@Mock\n\tprivate Message<Object> message;\n\n\tprivate MessageMatcher<Object> matcher;\n\n\t@Test\n\tpublic void constructorNullArray() {\n\t\tassertThatNullPointerException().isThrownBy(() -> new AndMessageMatcher<>((MessageMatcher<Object>[]) null));\n\t}\n\n\t@Test\n\tpublic void constructorArrayContainsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AndMessageMatcher<>((MessageMatcher<Object>) null));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void constructorEmptyArray() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AndMessageMatcher<>(new MessageMatcher[0]));\n\t}\n\n\t@Test\n\tpublic void constructorNullList() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AndMessageMatcher<>((List<MessageMatcher<Object>>) null));\n\t}\n\n\t@Test\n\tpublic void constructorListContainsNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AndMessageMatcher<>(Arrays.asList((MessageMatcher<Object>) null)));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyList() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AndMessageMatcher<>(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void matchesSingleTrue() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(true);\n\t\tthis.matcher = new AndMessageMatcher<>(this.delegate);\n\t\tassertThat(this.matcher.matches(this.message)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesMultiTrue() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(true);\n\t\tgiven(this.delegate2.matches(this.message)).willReturn(true);\n\t\tthis.matcher = new AndMessageMatcher<>(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.message)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesSingleFalse() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(false);\n\t\tthis.matcher = new AndMessageMatcher<>(this.delegate);\n\t\tassertThat(this.matcher.matches(this.message)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMultiBothFalse() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(false);\n\t\tthis.matcher = new AndMessageMatcher<>(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.message)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMultiSingleFalse() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(true);\n\t\tgiven(this.delegate2.matches(this.message)).willReturn(false);\n\t\tthis.matcher = new AndMessageMatcher<>(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.message)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/util/matcher/OrMessageMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.Message;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNullPointerException;\nimport static org.mockito.BDDMockito.given;\n\n@ExtendWith(MockitoExtension.class)\npublic class OrMessageMatcherTests {\n\n\t@Mock\n\tprivate MessageMatcher<Object> delegate;\n\n\t@Mock\n\tprivate MessageMatcher<Object> delegate2;\n\n\t@Mock\n\tprivate Message<Object> message;\n\n\tprivate MessageMatcher<Object> matcher;\n\n\t@Test\n\tpublic void constructorNullArray() {\n\t\tassertThatNullPointerException().isThrownBy(() -> new OrMessageMatcher<>((MessageMatcher<Object>[]) null));\n\t}\n\n\t@Test\n\tpublic void constructorArrayContainsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OrMessageMatcher<>((MessageMatcher<Object>) null));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void constructorEmptyArray() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OrMessageMatcher<>(new MessageMatcher[0]));\n\t}\n\n\t@Test\n\tpublic void constructorNullList() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OrMessageMatcher<>((List<MessageMatcher<Object>>) null));\n\t}\n\n\t@Test\n\tpublic void constructorListContainsNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OrMessageMatcher<>(Arrays.asList((MessageMatcher<Object>) null)));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyList() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OrMessageMatcher<>(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void matchesSingleTrue() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(true);\n\t\tthis.matcher = new OrMessageMatcher<>(this.delegate);\n\t\tassertThat(this.matcher.matches(this.message)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesMultiTrue() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(true);\n\t\tthis.matcher = new OrMessageMatcher<>(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.message)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesSingleFalse() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(false);\n\t\tthis.matcher = new OrMessageMatcher<>(this.delegate);\n\t\tassertThat(this.matcher.matches(this.message)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMultiBothFalse() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(false);\n\t\tgiven(this.delegate2.matches(this.message)).willReturn(false);\n\t\tthis.matcher = new OrMessageMatcher<>(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.message)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMultiSingleFalse() {\n\t\tgiven(this.delegate.matches(this.message)).willReturn(true);\n\t\tthis.matcher = new OrMessageMatcher<>(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.message)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/util/matcher/PathPatternMessageMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.server.PathContainer;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\npublic class PathPatternMessageMatcherTests {\n\n\tMessageBuilder<String> messageBuilder;\n\n\tPathPatternMessageMatcher matcher;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.messageBuilder = MessageBuilder.withPayload(\"M\");\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(\"/**\");\n\t}\n\n\t@Test\n\tvoid constructorPatternNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> PathPatternMessageMatcher.withDefaults().matcher(null));\n\t}\n\n\t@Test\n\tvoid matchesDoesNotMatchNullDestination() {\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesTrueWithSpecificDestinationPattern() {\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(\"/destination/1\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination/1\");\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesFalseWithDifferentDestination() {\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(\"/nomatch\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination/1\");\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesTrueWithDotSeparator() {\n\t\tthis.matcher = PathPatternMessageMatcher.withPathPatternParser(dotSeparatedPathParser())\n\t\t\t.matcher(\"destination.1\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"destination.1\");\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesFalseWithDotSeparatorAndAdditionalWildcardPathSegment() {\n\t\tthis.matcher = PathPatternMessageMatcher.withPathPatternParser(dotSeparatedPathParser())\n\t\t\t.matcher(\"/destination/a.*\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination/a.b\");\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isTrue();\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination/a.b.c\");\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesFalseWithDifferentMessageType() {\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(SimpMessageType.MESSAGE, \"/match\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.DISCONNECT);\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/match\");\n\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesTrueMessageType() {\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(SimpMessageType.MESSAGE, \"/match\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/match\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE);\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesTrueSubscribeType() {\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(SimpMessageType.SUBSCRIBE, \"/match\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/match\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.SUBSCRIBE);\n\t\tassertThat(this.matcher.matches(this.messageBuilder.build())).isTrue();\n\t}\n\n\t@Test\n\tvoid extractPathVariablesFromDestination() {\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(\"/topics/{topic}/**\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/topics/someTopic/sub1\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE);\n\n\t\tMessageMatcher.MatchResult matchResult = this.matcher.matcher(this.messageBuilder.build());\n\t\tassertThat(matchResult.isMatch()).isTrue();\n\t\tassertThat(matchResult.getVariables()).containsEntry(\"topic\", \"someTopic\");\n\t}\n\n\t@Test\n\tvoid extractPathVariablesFromMessageDestinationPath() {\n\t\tthis.matcher = PathPatternMessageMatcher.withPathPatternParser(dotSeparatedPathParser())\n\t\t\t.matcher(\"destination.{destinationNum}\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"destination.1\");\n\t\tMessageMatcher.MatchResult matchResult = this.matcher.matcher(this.messageBuilder.build());\n\t\tassertThat(matchResult.getVariables()).containsEntry(\"destinationNum\", \"1\");\n\t}\n\n\t@Test\n\tvoid extractPathVariables_isEmptyWithNullDestination() {\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(\"/topics/{topic}/**\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE);\n\n\t\tMessageMatcher.MatchResult matchResult = this.matcher.matcher(this.messageBuilder.build());\n\t\tassertThat(matchResult.isMatch()).isFalse();\n\t\tassertThat(matchResult.getVariables()).isEmpty();\n\t}\n\n\t@Test\n\tvoid getUriVariablesIsEmpty_onExtractPathVariables_whenNoMatch() {\n\t\tthis.matcher = PathPatternMessageMatcher.withDefaults().matcher(\"/nomatch\");\n\t\tthis.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, \"/destination/1\");\n\t\tMessageMatcher.MatchResult matchResult = this.matcher.matcher(this.messageBuilder.build());\n\t\tassertThat(matchResult.isMatch()).isFalse();\n\t\tassertThat(matchResult.getVariables()).isEmpty();\n\t}\n\n\tprivate static PathPatternParser dotSeparatedPathParser() {\n\t\tPathPatternParser parser = new PathPatternParser();\n\t\tparser.setPathOptions(PathContainer.Options.MESSAGE_ROUTE);\n\t\treturn parser;\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/util/matcher/SimpMessageTypeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.util.matcher;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.support.MessageBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\npublic class SimpMessageTypeMatcherTests {\n\n\tprivate SimpMessageTypeMatcher matcher;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.matcher = new SimpMessageTypeMatcher(SimpMessageType.MESSAGE);\n\t}\n\n\t@Test\n\tpublic void constructorNullType() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SimpMessageTypeMatcher(null));\n\t}\n\n\t@Test\n\tpublic void matchesMessageMessageTrue() {\n\t\t// @formatter:off\n\t\tMessage<String> message = MessageBuilder.withPayload(\"Hi\")\n\t\t\t\t.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.matcher.matches(message)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesMessageConnectFalse() {\n\t\t// @formatter:off\n\t\tMessage<String> message = MessageBuilder.withPayload(\"Hi\")\n\t\t\t\t.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.CONNECT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.matcher.matches(message)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMessageNullFalse() {\n\t\tMessage<String> message = MessageBuilder.withPayload(\"Hi\").build();\n\t\tassertThat(this.matcher.matches(message)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/web/csrf/CsrfChannelInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.web.csrf;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.InvalidCsrfTokenException;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n@ExtendWith(MockitoExtension.class)\npublic class CsrfChannelInterceptorTests {\n\n\t@Mock\n\tMessageChannel channel;\n\n\tSimpMessageHeaderAccessor messageHeaders;\n\n\tCsrfToken token;\n\n\tCsrfChannelInterceptor interceptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.token = new DefaultCsrfToken(\"header\", \"param\", \"token\");\n\t\tthis.interceptor = new CsrfChannelInterceptor();\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), this.token.getToken());\n\t\tthis.messageHeaders.setSessionAttributes(new HashMap<>());\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t}\n\n\t@Test\n\tpublic void preSendValidToken() {\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendIgnoresConnectAck() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT_ACK);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendIgnoresDisconnect() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.DISCONNECT);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendIgnoresDisconnectAck() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.DISCONNECT_ACK);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendIgnoresHeartbeat() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.HEARTBEAT);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendIgnoresMessage() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendIgnoresOther() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.OTHER);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendIgnoresSubscribe() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.SUBSCRIBE);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendIgnoresUnsubscribe() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.UNSUBSCRIBE);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendNoToken() {\n\t\tthis.messageHeaders.removeNativeHeader(this.token.getHeaderName());\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), this.channel));\n\t}\n\n\t@Test\n\tpublic void preSendInvalidToken() {\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), this.token.getToken() + \"invalid\");\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), this.channel));\n\t}\n\n\t@Test\n\tpublic void preSendMissingToken() {\n\t\tthis.messageHeaders.getSessionAttributes().clear();\n\t\tassertThatExceptionOfType(MissingCsrfTokenException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), this.channel));\n\t}\n\n\t@Test\n\tpublic void preSendMissingTokenNullSessionAttributes() {\n\t\tthis.messageHeaders.setSessionAttributes(null);\n\t\tassertThatExceptionOfType(MissingCsrfTokenException.class)\n\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), this.channel));\n\t}\n\n\tprivate Message<String> message() {\n\t\tMap<String, Object> headersToCopy = this.messageHeaders.toMap();\n\t\treturn MessageBuilder.withPayload(\"hi\").copyHeaders(headersToCopy).build();\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/web/csrf/XorCsrfChannelInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.web.csrf;\n\nimport java.util.Base64;\nimport java.util.HashMap;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.messaging.simp.SimpMessageHeaderAccessor;\nimport org.springframework.messaging.simp.SimpMessageType;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.InvalidCsrfTokenException;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link XorCsrfChannelInterceptor}.\n *\n * @author Steve Riesenberg\n */\npublic class XorCsrfChannelInterceptorTests {\n\n\tprivate static final String XOR_CSRF_TOKEN_VALUE = \"wpe7zB62-NCpcA==\";\n\n\tprivate static final String INVALID_XOR_CSRF_TOKEN_VALUE = \"KneoaygbRZtfHQ==\";\n\n\tprivate CsrfToken token;\n\n\tprivate SimpMessageHeaderAccessor messageHeaders;\n\n\tprivate MessageChannel channel;\n\n\tprivate XorCsrfChannelInterceptor interceptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.token = new DefaultCsrfToken(\"header\", \"param\", \"token\");\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);\n\t\tthis.messageHeaders.setSessionAttributes(new HashMap<>());\n\t\tthis.channel = mock(MessageChannel.class);\n\t\tthis.interceptor = new XorCsrfChannelInterceptor();\n\t}\n\n\t@Test\n\tpublic void preSendWhenConnectWithValidTokenThenSuccess() {\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendWhenConnectWithInvalidTokenThenThrowsInvalidCsrfTokenException() {\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), INVALID_XOR_CSRF_TOKEN_VALUE);\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void preSendWhenConnectWithNoTokenThenThrowsInvalidCsrfTokenException() {\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void preSendWhenConnectWithMissingTokenThenThrowsMissingCsrfTokenException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(MissingCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void preSendWhenConnectWithNullSessionAttributesThenThrowsMissingCsrfTokenException() {\n\t\tthis.messageHeaders.setSessionAttributes(null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(MissingCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void preSendWhenAckThenIgnores() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT_ACK);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendWhenDisconnectThenIgnores() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.DISCONNECT);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendWhenHeartbeatThenIgnores() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.HEARTBEAT);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendWhenMessageThenIgnores() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendWhenOtherThenIgnores() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.OTHER);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t@Test\n\tpublic void preSendWhenUnsubscribeThenIgnores() {\n\t\tthis.messageHeaders = SimpMessageHeaderAccessor.create(SimpMessageType.UNSUBSCRIBE);\n\t\tthis.interceptor.preSend(message(), this.channel);\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void preSendWhenCsrfBytesIsShorterThanRandomBytesThenThrowsInvalidCsrfTokenException() {\n\t\t/*\n\t\t * Token format: 3 random pad bytes + 2 padded bytes.\n\t\t */\n\t\tbyte[] actualBytes = { 1, 1, 1, 96, 99 };\n\t\tString actualToken = Base64.getEncoder().encodeToString(actualBytes);\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), actualToken);\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void preSendWhenCsrfBytesIsLongerThanRandomBytesThenThrowsInvalidCsrfTokenException() {\n\t\t/*\n\t\t * Token format: 3 random pad bytes + 4 padded bytes.\n\t\t */\n\t\tbyte[] actualBytes = { 1, 1, 1, 96, 99, 98, 97 };\n\t\tString actualToken = Base64.getEncoder().encodeToString(actualBytes);\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), actualToken);\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void preSendWhenTokenBytesIsShorterThanActualBytesThenThrowsInvalidCsrfTokenException() {\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"header\", \"param\", \"a\");\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), csrfToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void preSendWhenTokenBytesIsLongerThanActualBytesThenThrowsInvalidCsrfTokenException() {\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"header\", \"param\", \"abcde\");\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), csrfToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void preSendWhenActualBytesIsEmptyThenThrowsInvalidCsrfTokenException() {\n\t\tthis.messageHeaders.setNativeHeader(this.token.getHeaderName(), \"\");\n\t\tthis.messageHeaders.getSessionAttributes().put(CsrfToken.class.getName(), this.token);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(InvalidCsrfTokenException.class)\n\t\t\t\t.isThrownBy(() -> this.interceptor.preSend(message(), mock(MessageChannel.class)));\n\t\t// @formatter:on\n\t}\n\n\tprivate Message<String> message() {\n\t\treturn MessageBuilder.withPayload(\"message\").copyHeaders(this.messageHeaders.toMap()).build();\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/java/org/springframework/security/messaging/web/socket/server/CsrfTokenHandshakeInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.messaging.web.socket.server;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\nimport org.springframework.web.socket.WebSocketHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class CsrfTokenHandshakeInterceptorTests {\n\n\t@Mock\n\tWebSocketHandler wsHandler;\n\n\t@Mock\n\tServerHttpResponse response;\n\n\tMap<String, Object> attributes;\n\n\tServerHttpRequest request;\n\n\tMockHttpServletRequest httpRequest;\n\n\tCsrfTokenHandshakeInterceptor interceptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.httpRequest = new MockHttpServletRequest();\n\t\tthis.attributes = new HashMap<>();\n\t\tthis.request = new ServletServerHttpRequest(this.httpRequest);\n\t\tthis.interceptor = new CsrfTokenHandshakeInterceptor();\n\t}\n\n\t@Test\n\tpublic void beforeHandshakeNoAttribute() throws Exception {\n\t\tthis.interceptor.beforeHandshake(this.request, this.response, this.wsHandler, this.attributes);\n\t\tassertThat(this.attributes).isEmpty();\n\t}\n\n\t@Test\n\tpublic void beforeHandshake() throws Exception {\n\t\tCsrfToken token = new DefaultCsrfToken(\"header\", \"param\", \"token\");\n\t\tthis.httpRequest.setAttribute(DeferredCsrfToken.class.getName(), new TestDeferredCsrfToken(token));\n\t\tthis.interceptor.beforeHandshake(this.request, this.response, this.wsHandler, this.attributes);\n\t\tassertThat(this.attributes).containsOnlyKeys(CsrfToken.class.getName());\n\t\tCsrfToken csrfToken = (CsrfToken) this.attributes.get(CsrfToken.class.getName());\n\t\tassertThat(csrfToken.getHeaderName()).isEqualTo(token.getHeaderName());\n\t\tassertThat(csrfToken.getParameterName()).isEqualTo(token.getParameterName());\n\t\tassertThat(csrfToken.getToken()).isEqualTo(token.getToken());\n\t\t// Ensure the values of the CsrfToken are copied into a new token so the old token\n\t\t// is available for garbage collection.\n\t\t// This is required because the original token could hold a reference to the\n\t\t// HttpServletRequest/Response of the handshake request.\n\t\tassertThat(csrfToken).isNotSameAs(token);\n\t}\n\n\tprivate static final class TestDeferredCsrfToken implements DeferredCsrfToken {\n\n\t\tprivate final CsrfToken csrfToken;\n\n\t\tprivate TestDeferredCsrfToken(CsrfToken csrfToken) {\n\t\t\tthis.csrfToken = csrfToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic CsrfToken get() {\n\t\t\treturn this.csrfToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGenerated() {\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "messaging/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "notice.txt",
    "content": "   ======================================================================\n   == NOTICE file corresponding to section 4(d) of the Apache License, ==\n   == Version 2.0, in this case for the Spring Security distribution.  ==\n   ======================================================================\n\n   The end-user documentation included with a redistribution, if any,\n   must include the following acknowledgement:\n\n     \"This product includes software developed by Spring Security\n      Project (https://www.springframework.org/security).\"\n\n   Alternately, this acknowledgement may appear in the software itself,\n   if and wherever such third-party acknowledgements normally appear.\n\n   The names \"Spring\", \"Spring Security\", \"Spring Security System\",\n   \"SpringSource\", \"Acegi\", \"Acegi Security\", \"Acegi Security System\",\n   \"Acegi\" or any derivatives thereof may not be used to endorse or\n   promote products derived from this software without prior written\n   permission. For written permission, please contact \n   ben.alex@springsource.com.\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle",
    "content": "plugins {\n\tid 'compile-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\napply plugin: 'javadoc-warnings-error'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\n\tapi project(\":spring-security-web\")\n\tapi project(\":spring-security-oauth2-core\")\n\tapi project(\":spring-security-oauth2-jose\")\n\tapi project(\":spring-security-oauth2-resource-server\")\n\tapi (\"org.springframework:spring-core\") {\n\t\texclude group: \"commons-logging\", module: \"commons-logging\"\n\t}\n\tapi \"com.nimbusds:nimbus-jose-jwt\"\n\tapi 'tools.jackson.core:jackson-databind'\n\n\toptional \"com.fasterxml.jackson.core:jackson-databind\"\n\toptional \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\"\n\toptional \"org.springframework:spring-jdbc\"\n\n\ttestImplementation project(\":spring-security-test\")\n\ttestImplementation project(path : ':spring-security-oauth2-jose', configuration : 'tests')\n\ttestImplementation \"org.springframework:spring-webmvc\"\n\ttestImplementation \"org.bouncycastle:bcpkix-jdk18on\"\n\ttestImplementation \"org.bouncycastle:bcprov-jdk18on\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter\"\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"com.jayway.jsonpath:json-path\"\n\ttestImplementation \"com.squareup.okhttp3:mockwebserver\"\n\n\ttestRuntimeOnly \"org.hsqldb:hsqldb\"\n\ttestRuntimeOnly \"org.junit.platform:junit-platform-launcher\"\n\n\tprovided \"jakarta.servlet:jakarta.servlet-api\"\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\nimport org.springframework.util.Assert;\n\n/**\n * A base representation of OAuth 2.0 Authorization Server metadata, returned by an\n * endpoint defined in OAuth 2.0 Authorization Server Metadata and OpenID Connect\n * Discovery 1.0. The metadata endpoint returns a set of claims an Authorization Server\n * describes about its configuration.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerMetadataClaimAccessor\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8414#section-3.2\">3.2.\n * Authorization Server Metadata Response</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">4.2.\n * OpenID Provider Configuration Response</a>\n * @see <a target=\"_blank\" href=\"https://www.rfc-editor.org/rfc/rfc8628.html#section-4\">4.\n * Device Authorization Grant Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8705#section-3.3\">3.3 Mutual-TLS Client\n * Certificate-Bound Access Tokens Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9449#section-5.1\">5.1 OAuth 2.0 Demonstrating\n * Proof of Possession (DPoP) Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9126#name-authorization-server-metada\">5.\n * OAuth 2.0 Pushed Authorization Requests Metadata</a>\n */\npublic abstract class AbstractOAuth2AuthorizationServerMetadata\n\t\timplements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -8817963285912690443L;\n\n\tprivate final Map<String, Object> claims;\n\n\tprotected AbstractOAuth2AuthorizationServerMetadata(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\tthis.claims = Collections.unmodifiableMap(new LinkedHashMap<>(claims));\n\t}\n\n\t/**\n\t * Returns the metadata as claims.\n\t * @return a {@code Map} of the metadata as claims\n\t */\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * A builder for subclasses of {@link AbstractOAuth2AuthorizationServerMetadata}.\n\t *\n\t * @param <T> the type of object\n\t * @param <B> the type of the builder\n\t */\n\tprotected abstract static class AbstractBuilder<T extends AbstractOAuth2AuthorizationServerMetadata, B extends AbstractBuilder<T, B>> {\n\n\t\tprivate final Map<String, Object> claims = new LinkedHashMap<>();\n\n\t\tprotected AbstractBuilder() {\n\t\t}\n\n\t\tprotected Map<String, Object> getClaims() {\n\t\t\treturn this.claims;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected final B getThis() {\n\t\t\t// avoid unchecked casts in subclasses by using \"getThis()\" instead of \"(B)\n\t\t\t// this\"\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code issuer} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.\n\t\t * @param issuer the {@code URL} of the Authorization Server's Issuer Identifier\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B issuer(String issuer) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, issuer);\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code authorization_endpoint} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.\n\t\t * @param authorizationEndpoint the {@code URL} of the OAuth 2.0 Authorization\n\t\t * Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B authorizationEndpoint(String authorizationEndpoint) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, authorizationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code pushed_authorization_request_endpoint} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param pushedAuthorizationRequestEndpoint the {@code URL} of the OAuth 2.0\n\t\t * Pushed Authorization Request Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B pushedAuthorizationRequestEndpoint(String pushedAuthorizationRequestEndpoint) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,\n\t\t\t\t\tpushedAuthorizationRequestEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code device_authorization_endpoint} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param deviceAuthorizationEndpoint the {@code URL} of the OAuth 2.0 Device\n\t\t * Authorization Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B deviceAuthorizationEndpoint(String deviceAuthorizationEndpoint) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT,\n\t\t\t\t\tdeviceAuthorizationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code token_endpoint} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.\n\t\t * @param tokenEndpoint the {@code URL} of the OAuth 2.0 Token Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenEndpoint(String tokenEndpoint) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, tokenEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Add this client authentication method to the collection of\n\t\t * {@code token_endpoint_auth_methods_supported} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param authenticationMethod the client authentication method supported by the\n\t\t * OAuth 2.0 Token Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenEndpointAuthenticationMethod(String authenticationMethod) {\n\t\t\taddClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tauthenticationMethod);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the client authentication method(s) allowing the ability\n\t\t * to add, replace, or remove.\n\t\t * @param authenticationMethodsConsumer a {@code Consumer} of the client\n\t\t * authentication method(s) supported by the OAuth 2.0 Token Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenEndpointAuthenticationMethods(Consumer<List<String>> authenticationMethodsConsumer) {\n\t\t\tacceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tauthenticationMethodsConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code jwks_uri} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param jwkSetUrl the {@code URL} of the JSON Web Key Set\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B jwkSetUrl(String jwkSetUrl) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, jwkSetUrl);\n\t\t}\n\n\t\t/**\n\t\t * Add this OAuth 2.0 {@code scope} to the collection of {@code scopes_supported}\n\t\t * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata},\n\t\t * RECOMMENDED.\n\t\t * @param scope the OAuth 2.0 {@code scope} value supported\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B scope(String scope) {\n\t\t\taddClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, scope);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the OAuth 2.0 {@code scope} values supported allowing the\n\t\t * ability to add, replace, or remove.\n\t\t * @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values\n\t\t * supported\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B scopes(Consumer<List<String>> scopesConsumer) {\n\t\t\tacceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, scopesConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Add this OAuth 2.0 {@code response_type} to the collection of\n\t\t * {@code response_types_supported} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.\n\t\t * @param responseType the OAuth 2.0 {@code response_type} value supported\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B responseType(String responseType) {\n\t\t\taddClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, responseType);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the OAuth 2.0 {@code response_type} values supported\n\t\t * allowing the ability to add, replace, or remove.\n\t\t * @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0\n\t\t * {@code response_type} values supported\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B responseTypes(Consumer<List<String>> responseTypesConsumer) {\n\t\t\tacceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED,\n\t\t\t\t\tresponseTypesConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Add this OAuth 2.0 {@code grant_type} to the collection of\n\t\t * {@code grant_types_supported} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param grantType the OAuth 2.0 {@code grant_type} value supported\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B grantType(String grantType) {\n\t\t\taddClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, grantType);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the OAuth 2.0 {@code grant_type} values supported\n\t\t * allowing the ability to add, replace, or remove.\n\t\t * @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0\n\t\t * {@code grant_type} values supported\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B grantTypes(Consumer<List<String>> grantTypesConsumer) {\n\t\t\tacceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, grantTypesConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code revocation_endpoint} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param tokenRevocationEndpoint the {@code URL} of the OAuth 2.0 Token\n\t\t * Revocation Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenRevocationEndpoint(String tokenRevocationEndpoint) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT, tokenRevocationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Add this client authentication method to the collection of\n\t\t * {@code revocation_endpoint_auth_methods_supported} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param authenticationMethod the client authentication method supported by the\n\t\t * OAuth 2.0 Token Revocation Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenRevocationEndpointAuthenticationMethod(String authenticationMethod) {\n\t\t\taddClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tauthenticationMethod);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the client authentication method(s) allowing the ability\n\t\t * to add, replace, or remove.\n\t\t * @param authenticationMethodsConsumer a {@code Consumer} of the client\n\t\t * authentication method(s) supported by the OAuth 2.0 Token Revocation Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenRevocationEndpointAuthenticationMethods(Consumer<List<String>> authenticationMethodsConsumer) {\n\t\t\tacceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tauthenticationMethodsConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code introspection_endpoint} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param tokenIntrospectionEndpoint the {@code URL} of the OAuth 2.0 Token\n\t\t * Introspection Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenIntrospectionEndpoint(String tokenIntrospectionEndpoint) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT,\n\t\t\t\t\ttokenIntrospectionEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Add this client authentication method to the collection of\n\t\t * {@code introspection_endpoint_auth_methods_supported} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param authenticationMethod the client authentication method supported by the\n\t\t * OAuth 2.0 Token Introspection Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenIntrospectionEndpointAuthenticationMethod(String authenticationMethod) {\n\t\t\taddClaimToClaimList(\n\t\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tauthenticationMethod);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the client authentication method(s) allowing the ability\n\t\t * to add, replace, or remove.\n\t\t * @param authenticationMethodsConsumer a {@code Consumer} of the client\n\t\t * authentication method(s) supported by the OAuth 2.0 Token Introspection\n\t\t * Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenIntrospectionEndpointAuthenticationMethods(Consumer<List<String>> authenticationMethodsConsumer) {\n\t\t\tacceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tauthenticationMethodsConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code registration_endpoint} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param clientRegistrationEndpoint the {@code URL} of the OAuth 2.0 Dynamic\n\t\t * Client Registration Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B clientRegistrationEndpoint(String clientRegistrationEndpoint) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT, clientRegistrationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Add this Proof Key for Code Exchange (PKCE) {@code code_challenge_method} to\n\t\t * the collection of {@code code_challenge_methods_supported} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param codeChallengeMethod the {@code code_challenge_method} value supported\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B codeChallengeMethod(String codeChallengeMethod) {\n\t\t\taddClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED,\n\t\t\t\t\tcodeChallengeMethod);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the Proof Key for Code Exchange (PKCE)\n\t\t * {@code code_challenge_method} values supported allowing the ability to add,\n\t\t * replace, or remove.\n\t\t * @param codeChallengeMethodsConsumer a {@code Consumer} of the\n\t\t * {@code code_challenge_method} values supported\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B codeChallengeMethods(Consumer<List<String>> codeChallengeMethodsConsumer) {\n\t\t\tacceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED,\n\t\t\t\t\tcodeChallengeMethodsConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code tls_client_certificate_bound_access_tokens} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param tlsClientCertificateBoundAccessTokens {@code true} to indicate support\n\t\t * for mutual-TLS client certificate-bound access tokens\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tlsClientCertificateBoundAccessTokens(boolean tlsClientCertificateBoundAccessTokens) {\n\t\t\treturn claim(OAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS,\n\t\t\t\t\ttlsClientCertificateBoundAccessTokens);\n\t\t}\n\n\t\t/**\n\t\t * Add a {@link JwsAlgorithms JSON Web Signature (JWS) algorithm} to the\n\t\t * collection of {@code dpop_signing_alg_values_supported} in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.\n\t\t * @param dPoPSigningAlgorithm the {@link JwsAlgorithms JSON Web Signature (JWS)\n\t\t * algorithm} supported for DPoP Proof JWTs\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B dPoPSigningAlgorithm(String dPoPSigningAlgorithm) {\n\t\t\taddClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED,\n\t\t\t\t\tdPoPSigningAlgorithm);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the {@link JwsAlgorithms JSON Web Signature (JWS)\n\t\t * algorithms} supported for DPoP Proof JWTs allowing the ability to add, replace,\n\t\t * or remove.\n\t\t * @param dPoPSigningAlgorithmsConsumer a {@code Consumer} of the\n\t\t * {@link JwsAlgorithms JSON Web Signature (JWS) algorithms} supported for DPoP\n\t\t * Proof JWTs\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B dPoPSigningAlgorithms(Consumer<List<String>> dPoPSigningAlgorithmsConsumer) {\n\t\t\tacceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED,\n\t\t\t\t\tdPoPSigningAlgorithmsConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Use this claim in the resulting\n\t\t * {@link AbstractOAuth2AuthorizationServerMetadata}.\n\t\t * @param name the claim name\n\t\t * @param value the claim value\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B claim(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #claim(String, Object)} declared so far with\n\t\t * the possibility to add, replace, or remove.\n\t\t * @param claimsConsumer a {@code Consumer} of the claims\n\t\t * @return the {@link AbstractBuilder} for further configurations\n\t\t */\n\t\tpublic B claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Creates the {@link AbstractOAuth2AuthorizationServerMetadata}.\n\t\t * @return the {@link AbstractOAuth2AuthorizationServerMetadata}\n\t\t */\n\t\tpublic abstract T build();\n\n\t\tprotected void validate() {\n\t\t\tAssert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER),\n\t\t\t\t\t\"issuer cannot be null\");\n\t\t\tvalidateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER),\n\t\t\t\t\t\"issuer must be a valid URL\");\n\t\t\tAssert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT),\n\t\t\t\t\t\"authorizationEndpoint cannot be null\");\n\t\t\tvalidateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT),\n\t\t\t\t\t\"authorizationEndpoint must be a valid URL\");\n\t\t\tif (getClaims()\n\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT) != null) {\n\t\t\t\tvalidateURL(\n\t\t\t\t\t\tgetClaims()\n\t\t\t\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT),\n\t\t\t\t\t\t\"pushedAuthorizationRequestEndpoint must be a valid URL\");\n\t\t\t}\n\t\t\tif (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT) != null) {\n\t\t\t\tvalidateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT),\n\t\t\t\t\t\t\"deviceAuthorizationEndpoint must be a valid URL\");\n\t\t\t}\n\t\t\tAssert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT),\n\t\t\t\t\t\"tokenEndpoint cannot be null\");\n\t\t\tvalidateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT),\n\t\t\t\t\t\"tokenEndpoint must be a valid URL\");\n\t\t\tif (getClaims()\n\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tgetClaims()\n\t\t\t\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"tokenEndpointAuthenticationMethods must be of type List\");\n\t\t\t\tAssert.notEmpty(\n\t\t\t\t\t\t(List<?>) getClaims()\n\t\t\t\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"tokenEndpointAuthenticationMethods cannot be empty\");\n\t\t\t}\n\t\t\tif (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI) != null) {\n\t\t\t\tvalidateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI),\n\t\t\t\t\t\t\"jwksUri must be a valid URL\");\n\t\t\t}\n\t\t\tif (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tgetClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED),\n\t\t\t\t\t\t\"scopes must be of type List\");\n\t\t\t\tAssert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED),\n\t\t\t\t\t\t\"scopes cannot be empty\");\n\t\t\t}\n\t\t\tAssert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED),\n\t\t\t\t\t\"responseTypes cannot be null\");\n\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\tgetClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED),\n\t\t\t\t\t\"responseTypes must be of type List\");\n\t\t\tAssert.notEmpty(\n\t\t\t\t\t(List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED),\n\t\t\t\t\t\"responseTypes cannot be empty\");\n\t\t\tif (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tgetClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED),\n\t\t\t\t\t\t\"grantTypes must be of type List\");\n\t\t\t\tAssert.notEmpty(\n\t\t\t\t\t\t(List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED),\n\t\t\t\t\t\t\"grantTypes cannot be empty\");\n\t\t\t}\n\t\t\tif (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT) != null) {\n\t\t\t\tvalidateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT),\n\t\t\t\t\t\t\"tokenRevocationEndpoint must be a valid URL\");\n\t\t\t}\n\t\t\tif (getClaims()\n\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tgetClaims().get(\n\t\t\t\t\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"tokenRevocationEndpointAuthenticationMethods must be of type List\");\n\t\t\t\tAssert.notEmpty(\n\t\t\t\t\t\t(List<?>) getClaims().get(\n\t\t\t\t\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"tokenRevocationEndpointAuthenticationMethods cannot be empty\");\n\t\t\t}\n\t\t\tif (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT) != null) {\n\t\t\t\tvalidateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT),\n\t\t\t\t\t\t\"tokenIntrospectionEndpoint must be a valid URL\");\n\t\t\t}\n\t\t\tif (getClaims().get(\n\t\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class, getClaims()\n\t\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"tokenIntrospectionEndpointAuthenticationMethods must be of type List\");\n\t\t\t\tAssert.notEmpty((List<?>) getClaims()\n\t\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"tokenIntrospectionEndpointAuthenticationMethods cannot be empty\");\n\t\t\t}\n\t\t\tif (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT) != null) {\n\t\t\t\tvalidateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT),\n\t\t\t\t\t\t\"clientRegistrationEndpoint must be a valid URL\");\n\t\t\t}\n\t\t\tif (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tgetClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"codeChallengeMethods must be of type List\");\n\t\t\t\tAssert.notEmpty(\n\t\t\t\t\t\t(List<?>) getClaims()\n\t\t\t\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"codeChallengeMethods cannot be empty\");\n\t\t\t}\n\t\t\tif (getClaims()\n\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tgetClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED),\n\t\t\t\t\t\t\"dPoPSigningAlgorithms must be of type List\");\n\t\t\t\tAssert.notEmpty(\n\t\t\t\t\t\t(List<?>) getClaims()\n\t\t\t\t\t\t\t.get(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED),\n\t\t\t\t\t\t\"dPoPSigningAlgorithms cannot be empty\");\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void addClaimToClaimList(String name, String value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tgetClaims().computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\t((List<String>) getClaims().get(name)).add(value);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void acceptClaimValues(String name, Consumer<List<String>> valuesConsumer) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(valuesConsumer, \"valuesConsumer cannot be null\");\n\t\t\tgetClaims().computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\tList<String> values = (List<String>) getClaims().get(name);\n\t\t\tvaluesConsumer.accept(values);\n\t\t}\n\n\t\tprotected static void validateURL(Object url, String errorMessage) {\n\t\t\tif (URL.class.isAssignableFrom(url.getClass())) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnew URI(url.toString()).toURL();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(errorMessage, ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2ClientRegistration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.net.URI;\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.util.Assert;\n\n/**\n * A base representation of an OAuth 2.0 Client Registration Request and Response, which\n * is sent to and returned from the Client Registration Endpoint, and contains a set of\n * claims about the Client's Registration information. The claims are defined by the OAuth\n * 2.0 Dynamic Client Registration Protocol specification.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2ClientMetadataClaimAccessor\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc7591#section-3.1\">3.1. Client Registration\n * Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1\">3.2.1. Client\n * Registration Response</a>\n */\npublic abstract class AbstractOAuth2ClientRegistration implements OAuth2ClientMetadataClaimAccessor, Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 8042785346181558593L;\n\n\tprivate final Map<String, Object> claims;\n\n\tprotected AbstractOAuth2ClientRegistration(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\tthis.claims = Collections.unmodifiableMap(new LinkedHashMap<>(claims));\n\t}\n\n\t/**\n\t * Returns the metadata as claims.\n\t * @return a {@code Map} of the metadata as claims\n\t */\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * A builder for subclasses of {@link AbstractOAuth2ClientRegistration}.\n\t *\n\t * @param <T> the type of object\n\t * @param <B> the type of the builder\n\t */\n\tprotected abstract static class AbstractBuilder<T extends AbstractOAuth2ClientRegistration, B extends AbstractBuilder<T, B>> {\n\n\t\tprivate final Map<String, Object> claims = new LinkedHashMap<>();\n\n\t\tprotected AbstractBuilder() {\n\t\t}\n\n\t\tprotected Map<String, Object> getClaims() {\n\t\t\treturn this.claims;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected final B getThis() {\n\t\t\t// avoid unchecked casts in subclasses by using \"getThis()\" instead of \"(B)\n\t\t\t// this\"\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the Client Identifier, REQUIRED.\n\t\t * @param clientId the Client Identifier\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B clientId(String clientId) {\n\t\t\treturn claim(OAuth2ClientMetadataClaimNames.CLIENT_ID, clientId);\n\t\t}\n\n\t\t/**\n\t\t * Sets the time at which the Client Identifier was issued, OPTIONAL.\n\t\t * @param clientIdIssuedAt the time at which the Client Identifier was issued\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B clientIdIssuedAt(Instant clientIdIssuedAt) {\n\t\t\treturn claim(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, clientIdIssuedAt);\n\t\t}\n\n\t\t/**\n\t\t * Sets the Client Secret, OPTIONAL.\n\t\t * @param clientSecret the Client Secret\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B clientSecret(String clientSecret) {\n\t\t\treturn claim(OAuth2ClientMetadataClaimNames.CLIENT_SECRET, clientSecret);\n\t\t}\n\n\t\t/**\n\t\t * Sets the time at which the {@code client_secret} will expire or {@code null} if\n\t\t * it will not expire, REQUIRED if {@code client_secret} was issued.\n\t\t * @param clientSecretExpiresAt the time at which the {@code client_secret} will\n\t\t * expire or {@code null} if it will not expire\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B clientSecretExpiresAt(Instant clientSecretExpiresAt) {\n\t\t\treturn claim(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, clientSecretExpiresAt);\n\t\t}\n\n\t\t/**\n\t\t * Sets the name of the Client to be presented to the End-User, OPTIONAL.\n\t\t * @param clientName the name of the Client to be presented to the End-User\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B clientName(String clientName) {\n\t\t\treturn claim(OAuth2ClientMetadataClaimNames.CLIENT_NAME, clientName);\n\t\t}\n\n\t\t/**\n\t\t * Add the redirection {@code URI} used by the Client, REQUIRED for redirect-based\n\t\t * flows.\n\t\t * @param redirectUri the redirection {@code URI} used by the Client\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B redirectUri(String redirectUri) {\n\t\t\taddClaimToClaimList(OAuth2ClientMetadataClaimNames.REDIRECT_URIS, redirectUri);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the redirection {@code URI} values used by the Client,\n\t\t * allowing the ability to add, replace, or remove, REQUIRED for redirect-based\n\t\t * flows.\n\t\t * @param redirectUrisConsumer a {@code Consumer} of the redirection {@code URI}\n\t\t * values used by the Client\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B redirectUris(Consumer<List<String>> redirectUrisConsumer) {\n\t\t\tacceptClaimValues(OAuth2ClientMetadataClaimNames.REDIRECT_URIS, redirectUrisConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the authentication method used by the Client for the Token Endpoint,\n\t\t * OPTIONAL.\n\t\t * @param tokenEndpointAuthenticationMethod the authentication method used by the\n\t\t * Client for the Token Endpoint\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenEndpointAuthenticationMethod(String tokenEndpointAuthenticationMethod) {\n\t\t\treturn claim(OAuth2ClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD, tokenEndpointAuthenticationMethod);\n\t\t}\n\n\t\t/**\n\t\t * Add the OAuth 2.0 {@code grant_type} that the Client will restrict itself to\n\t\t * using, OPTIONAL.\n\t\t * @param grantType the OAuth 2.0 {@code grant_type} that the Client will restrict\n\t\t * itself to using\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B grantType(String grantType) {\n\t\t\taddClaimToClaimList(OAuth2ClientMetadataClaimNames.GRANT_TYPES, grantType);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the OAuth 2.0 {@code grant_type} values that the Client\n\t\t * will restrict itself to using, allowing the ability to add, replace, or remove,\n\t\t * OPTIONAL.\n\t\t * @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0\n\t\t * {@code grant_type} values that the Client will restrict itself to using\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B grantTypes(Consumer<List<String>> grantTypesConsumer) {\n\t\t\tacceptClaimValues(OAuth2ClientMetadataClaimNames.GRANT_TYPES, grantTypesConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Add the OAuth 2.0 {@code response_type} that the Client will restrict itself to\n\t\t * using, OPTIONAL.\n\t\t * @param responseType the OAuth 2.0 {@code response_type} that the Client will\n\t\t * restrict itself to using\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B responseType(String responseType) {\n\t\t\taddClaimToClaimList(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES, responseType);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the OAuth 2.0 {@code response_type} values that the\n\t\t * Client will restrict itself to using, allowing the ability to add, replace, or\n\t\t * remove, OPTIONAL.\n\t\t * @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0\n\t\t * {@code response_type} values that the Client will restrict itself to using\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B responseTypes(Consumer<List<String>> responseTypesConsumer) {\n\t\t\tacceptClaimValues(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES, responseTypesConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Add the OAuth 2.0 {@code scope} that the Client will restrict itself to using,\n\t\t * OPTIONAL.\n\t\t * @param scope the OAuth 2.0 {@code scope} that the Client will restrict itself\n\t\t * to using\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B scope(String scope) {\n\t\t\taddClaimToClaimList(OAuth2ClientMetadataClaimNames.SCOPE, scope);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the OAuth 2.0 {@code scope} values that the Client will\n\t\t * restrict itself to using, allowing the ability to add, replace, or remove,\n\t\t * OPTIONAL.\n\t\t * @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values\n\t\t * that the Client will restrict itself to using\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B scopes(Consumer<List<String>> scopesConsumer) {\n\t\t\tacceptClaimValues(OAuth2ClientMetadataClaimNames.SCOPE, scopesConsumer);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code URL} for the Client's JSON Web Key Set, OPTIONAL.\n\t\t * @param jwkSetUrl the {@code URL} for the Client's JSON Web Key Set\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B jwkSetUrl(String jwkSetUrl) {\n\t\t\treturn claim(OAuth2ClientMetadataClaimNames.JWKS_URI, jwkSetUrl);\n\t\t}\n\n\t\t/**\n\t\t * Sets the claim.\n\t\t * @param name the claim name\n\t\t * @param value the claim value\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B claim(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #claim(String, Object)} declared so far\n\t\t * allowing the ability to add, replace, or remove.\n\t\t * @param claimsConsumer a {@code Consumer} of the claims\n\t\t * @return the {@link AbstractBuilder} for further configurations\n\t\t */\n\t\tpublic B claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Validate the claims and build the {@link AbstractOAuth2ClientRegistration}.\n\t\t * @return the {@link AbstractOAuth2ClientRegistration}\n\t\t */\n\t\tpublic abstract T build();\n\n\t\tprotected void validate() {\n\t\t\tif (this.claims.get(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT) != null\n\t\t\t\t\t|| this.claims.get(OAuth2ClientMetadataClaimNames.CLIENT_SECRET) != null) {\n\t\t\t\tAssert.notNull(this.claims.get(OAuth2ClientMetadataClaimNames.CLIENT_ID), \"client_id cannot be null\");\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT) != null) {\n\t\t\t\tAssert.isInstanceOf(Instant.class, this.claims.get(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT),\n\t\t\t\t\t\t\"client_id_issued_at must be of type Instant\");\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT) != null) {\n\t\t\t\tAssert.notNull(this.claims.get(OAuth2ClientMetadataClaimNames.CLIENT_SECRET),\n\t\t\t\t\t\t\"client_secret cannot be null\");\n\t\t\t\tAssert.isInstanceOf(Instant.class,\n\t\t\t\t\t\tthis.claims.get(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT),\n\t\t\t\t\t\t\"client_secret_expires_at must be of type Instant\");\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ClientMetadataClaimNames.REDIRECT_URIS) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class, this.claims.get(OAuth2ClientMetadataClaimNames.REDIRECT_URIS),\n\t\t\t\t\t\t\"redirect_uris must be of type List\");\n\t\t\t\tAssert.notEmpty((List<?>) this.claims.get(OAuth2ClientMetadataClaimNames.REDIRECT_URIS),\n\t\t\t\t\t\t\"redirect_uris cannot be empty\");\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ClientMetadataClaimNames.GRANT_TYPES) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class, this.claims.get(OAuth2ClientMetadataClaimNames.GRANT_TYPES),\n\t\t\t\t\t\t\"grant_types must be of type List\");\n\t\t\t\tAssert.notEmpty((List<?>) this.claims.get(OAuth2ClientMetadataClaimNames.GRANT_TYPES),\n\t\t\t\t\t\t\"grant_types cannot be empty\");\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class, this.claims.get(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES),\n\t\t\t\t\t\t\"response_types must be of type List\");\n\t\t\t\tAssert.notEmpty((List<?>) this.claims.get(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES),\n\t\t\t\t\t\t\"response_types cannot be empty\");\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ClientMetadataClaimNames.SCOPE) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class, this.claims.get(OAuth2ClientMetadataClaimNames.SCOPE),\n\t\t\t\t\t\t\"scope must be of type List\");\n\t\t\t\tAssert.notEmpty((List<?>) this.claims.get(OAuth2ClientMetadataClaimNames.SCOPE),\n\t\t\t\t\t\t\"scope cannot be empty\");\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ClientMetadataClaimNames.JWKS_URI) != null) {\n\t\t\t\tvalidateURL(this.claims.get(OAuth2ClientMetadataClaimNames.JWKS_URI), \"jwksUri must be a valid URL\");\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void addClaimToClaimList(String name, String value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\t((List<String>) this.claims.get(name)).add(value);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void acceptClaimValues(String name, Consumer<List<String>> valuesConsumer) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(valuesConsumer, \"valuesConsumer cannot be null\");\n\t\t\tthis.claims.computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\tList<String> values = (List<String>) this.claims.get(name);\n\t\t\tvaluesConsumer.accept(values);\n\t\t}\n\n\t\tprivate static void validateURL(Object url, String errorMessage) {\n\t\t\tif (URL.class.isAssignableFrom(url.getClass())) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnew URI(url.toString()).toURL();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(errorMessage, ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthorizationConsentService} that stores\n * {@link OAuth2AuthorizationConsent}'s in-memory.\n *\n * <p>\n * <b>NOTE:</b> This implementation should ONLY be used during development/testing.\n *\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OAuth2AuthorizationConsentService\n */\npublic final class InMemoryOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {\n\n\tprivate final Map<Integer, OAuth2AuthorizationConsent> authorizationConsents = new ConcurrentHashMap<>();\n\n\t/**\n\t * Constructs an {@code InMemoryOAuth2AuthorizationConsentService}.\n\t */\n\tpublic InMemoryOAuth2AuthorizationConsentService() {\n\t\tthis(Collections.emptyList());\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided\n\t * parameters.\n\t * @param authorizationConsents the authorization consent(s)\n\t */\n\tpublic InMemoryOAuth2AuthorizationConsentService(OAuth2AuthorizationConsent... authorizationConsents) {\n\t\tthis(Arrays.asList(authorizationConsents));\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided\n\t * parameters.\n\t * @param authorizationConsents the authorization consent(s)\n\t */\n\tpublic InMemoryOAuth2AuthorizationConsentService(List<OAuth2AuthorizationConsent> authorizationConsents) {\n\t\tAssert.notNull(authorizationConsents, \"authorizationConsents cannot be null\");\n\t\tauthorizationConsents.forEach((authorizationConsent) -> {\n\t\t\tAssert.notNull(authorizationConsent, \"authorizationConsent cannot be null\");\n\t\t\tint id = getId(authorizationConsent);\n\t\t\tAssert.isTrue(!this.authorizationConsents.containsKey(id),\n\t\t\t\t\t\"The authorizationConsent must be unique. Found duplicate, with registered client id: [\"\n\t\t\t\t\t\t\t+ authorizationConsent.getRegisteredClientId() + \"] and principal name: [\"\n\t\t\t\t\t\t\t+ authorizationConsent.getPrincipalName() + \"]\");\n\t\t\tthis.authorizationConsents.put(id, authorizationConsent);\n\t\t});\n\t}\n\n\t@Override\n\tpublic void save(OAuth2AuthorizationConsent authorizationConsent) {\n\t\tAssert.notNull(authorizationConsent, \"authorizationConsent cannot be null\");\n\t\tint id = getId(authorizationConsent);\n\t\tthis.authorizationConsents.put(id, authorizationConsent);\n\t}\n\n\t@Override\n\tpublic void remove(OAuth2AuthorizationConsent authorizationConsent) {\n\t\tAssert.notNull(authorizationConsent, \"authorizationConsent cannot be null\");\n\t\tint id = getId(authorizationConsent);\n\t\tthis.authorizationConsents.remove(id, authorizationConsent);\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {\n\t\tAssert.hasText(registeredClientId, \"registeredClientId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tint id = getId(registeredClientId, principalName);\n\t\treturn this.authorizationConsents.get(id);\n\t}\n\n\tprivate static int getId(String registeredClientId, String principalName) {\n\t\treturn Objects.hash(registeredClientId, principalName);\n\t}\n\n\tprivate static int getId(OAuth2AuthorizationConsent authorizationConsent) {\n\t\treturn getId(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthorizationService} that stores {@link OAuth2Authorization}'s\n * in-memory.\n *\n * <p>\n * <b>NOTE:</b> This implementation should ONLY be used during development/testing.\n *\n * @author Krisztian Toth\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationService\n */\npublic final class InMemoryOAuth2AuthorizationService implements OAuth2AuthorizationService {\n\n\tprivate int maxInitializedAuthorizations = 100;\n\n\t/*\n\t * Stores \"initialized\" (uncompleted) authorizations, where an access token has not\n\t * yet been granted. This state occurs with the authorization_code grant flow during\n\t * the user consent step OR when the code is returned in the authorization response\n\t * but the access token request is not yet initiated.\n\t */\n\tprivate Map<String, OAuth2Authorization> initializedAuthorizations = Collections\n\t\t.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));\n\n\t/*\n\t * Stores \"completed\" authorizations, where an access token has been granted.\n\t */\n\tprivate final Map<String, OAuth2Authorization> authorizations = new ConcurrentHashMap<>();\n\n\t/*\n\t * Constructor used for testing only.\n\t */\n\tInMemoryOAuth2AuthorizationService(int maxInitializedAuthorizations) {\n\t\tthis.maxInitializedAuthorizations = maxInitializedAuthorizations;\n\t\tthis.initializedAuthorizations = Collections\n\t\t\t.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryOAuth2AuthorizationService}.\n\t */\n\tpublic InMemoryOAuth2AuthorizationService() {\n\t\tthis(Collections.emptyList());\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided\n\t * parameters.\n\t * @param authorizations the authorization(s)\n\t */\n\tpublic InMemoryOAuth2AuthorizationService(OAuth2Authorization... authorizations) {\n\t\tthis(Arrays.asList(authorizations));\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided\n\t * parameters.\n\t * @param authorizations the authorization(s)\n\t */\n\tpublic InMemoryOAuth2AuthorizationService(List<OAuth2Authorization> authorizations) {\n\t\tAssert.notNull(authorizations, \"authorizations cannot be null\");\n\t\tauthorizations.forEach((authorization) -> {\n\t\t\tAssert.notNull(authorization, \"authorization cannot be null\");\n\t\t\tAssert.isTrue(!this.authorizations.containsKey(authorization.getId()),\n\t\t\t\t\t\"The authorization must be unique. Found duplicate identifier: \" + authorization.getId());\n\t\t\tthis.authorizations.put(authorization.getId(), authorization);\n\t\t});\n\t}\n\n\t@Override\n\tpublic void save(OAuth2Authorization authorization) {\n\t\tAssert.notNull(authorization, \"authorization cannot be null\");\n\t\tif (isComplete(authorization)) {\n\t\t\tthis.authorizations.put(authorization.getId(), authorization);\n\t\t}\n\t\telse {\n\t\t\tthis.initializedAuthorizations.put(authorization.getId(), authorization);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void remove(OAuth2Authorization authorization) {\n\t\tAssert.notNull(authorization, \"authorization cannot be null\");\n\t\tif (isComplete(authorization)) {\n\t\t\tthis.authorizations.remove(authorization.getId(), authorization);\n\t\t}\n\t\telse {\n\t\t\tthis.initializedAuthorizations.remove(authorization.getId(), authorization);\n\t\t}\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2Authorization findById(String id) {\n\t\tAssert.hasText(id, \"id cannot be empty\");\n\t\tOAuth2Authorization authorization = this.authorizations.get(id);\n\t\treturn (authorization != null) ? authorization : this.initializedAuthorizations.get(id);\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {\n\t\tAssert.hasText(token, \"token cannot be empty\");\n\t\tfor (OAuth2Authorization authorization : this.authorizations.values()) {\n\t\t\tif (hasToken(authorization, token, tokenType)) {\n\t\t\t\treturn authorization;\n\t\t\t}\n\t\t}\n\t\tfor (OAuth2Authorization authorization : this.initializedAuthorizations.values()) {\n\t\t\tif (hasToken(authorization, token, tokenType)) {\n\t\t\t\treturn authorization;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static boolean isComplete(OAuth2Authorization authorization) {\n\t\treturn authorization.getAccessToken() != null;\n\t}\n\n\tprivate static boolean hasToken(OAuth2Authorization authorization, String token,\n\t\t\t@Nullable OAuth2TokenType tokenType) {\n\t\t// @formatter:off\n\t\tif (tokenType == null) {\n\t\t\treturn matchesState(authorization, token) ||\n\t\t\t\t\tmatchesAuthorizationCode(authorization, token) ||\n\t\t\t\t\tmatchesAccessToken(authorization, token) ||\n\t\t\t\t\tmatchesIdToken(authorization, token) ||\n\t\t\t\t\tmatchesRefreshToken(authorization, token) ||\n\t\t\t\t\tmatchesDeviceCode(authorization, token) ||\n\t\t\t\t\tmatchesUserCode(authorization, token);\n\t\t}\n\t\telse if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {\n\t\t\treturn matchesState(authorization, token);\n\t\t}\n\t\telse if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {\n\t\t\treturn matchesAuthorizationCode(authorization, token);\n\t\t}\n\t\telse if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {\n\t\t\treturn matchesAccessToken(authorization, token);\n\t\t}\n\t\telse if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {\n\t\t\treturn matchesIdToken(authorization, token);\n\t\t}\n\t\telse if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {\n\t\t\treturn matchesRefreshToken(authorization, token);\n\t\t}\n\t\telse if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) {\n\t\t\treturn matchesDeviceCode(authorization, token);\n\t\t}\n\t\telse if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) {\n\t\t\treturn matchesUserCode(authorization, token);\n\t\t}\n\t\t// @formatter:on\n\t\treturn false;\n\t}\n\n\tprivate static boolean matchesState(OAuth2Authorization authorization, String token) {\n\t\treturn token.equals(authorization.getAttribute(OAuth2ParameterNames.STATE));\n\t}\n\n\tprivate static boolean matchesAuthorizationCode(OAuth2Authorization authorization, String token) {\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\treturn authorizationCode != null && authorizationCode.getToken().getTokenValue().equals(token);\n\t}\n\n\tprivate static boolean matchesAccessToken(OAuth2Authorization authorization, String token) {\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getToken(OAuth2AccessToken.class);\n\t\treturn accessToken != null && accessToken.getToken().getTokenValue().equals(token);\n\t}\n\n\tprivate static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) {\n\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getToken(OAuth2RefreshToken.class);\n\t\treturn refreshToken != null && refreshToken.getToken().getTokenValue().equals(token);\n\t}\n\n\tprivate static boolean matchesIdToken(OAuth2Authorization authorization, String token) {\n\t\tOAuth2Authorization.Token<OidcIdToken> idToken = authorization.getToken(OidcIdToken.class);\n\t\treturn idToken != null && idToken.getToken().getTokenValue().equals(token);\n\t}\n\n\tprivate static boolean matchesDeviceCode(OAuth2Authorization authorization, String token) {\n\t\tOAuth2Authorization.Token<OAuth2DeviceCode> deviceCode = authorization.getToken(OAuth2DeviceCode.class);\n\t\treturn deviceCode != null && deviceCode.getToken().getTokenValue().equals(token);\n\t}\n\n\tprivate static boolean matchesUserCode(OAuth2Authorization authorization, String token) {\n\t\tOAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);\n\t\treturn userCode != null && userCode.getToken().getTokenValue().equals(token);\n\t}\n\n\tprivate static final class MaxSizeHashMap<K, V> extends LinkedHashMap<K, V> {\n\n\t\tprivate final int maxSize;\n\n\t\tprivate MaxSizeHashMap(int maxSize) {\n\t\t\tthis.maxSize = maxSize;\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {\n\t\t\treturn size() > this.maxSize;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.context.annotation.ImportRuntimeHints;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A JDBC implementation of an {@link OAuth2AuthorizationConsentService} that uses a\n * {@link JdbcOperations} for {@link OAuth2AuthorizationConsent} persistence.\n *\n * <p>\n * <b>IMPORTANT:</b> This {@code OAuth2AuthorizationConsentService} depends on the table\n * definition described in\n * \"classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql\"\n * and therefore MUST be defined in the database schema.\n *\n * <p>\n * <b>NOTE:</b> This {@code OAuth2AuthorizationConsentService} is a simplified JDBC\n * implementation that MAY be used in a production environment. However, it does have\n * limitations as it likely won't perform well in an environment requiring high\n * throughput. The expectation is that the consuming application will provide their own\n * implementation of {@code OAuth2AuthorizationConsentService} that meets the performance\n * requirements for its deployment environment.\n *\n * @author Ovidiu Popa\n * @author Josh Long\n * @since 7.0\n * @see OAuth2AuthorizationConsentService\n * @see OAuth2AuthorizationConsent\n * @see JdbcOperations\n * @see RowMapper\n */\n@ImportRuntimeHints(JdbcOAuth2AuthorizationConsentService.JdbcOAuth2AuthorizationConsentServiceRuntimeHintsRegistrar.class)\npublic class JdbcOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {\n\n\t// @formatter:off\n\tprivate static final String COLUMN_NAMES = \"registered_client_id, \"\n\t\t\t+ \"principal_name, \"\n\t\t\t+ \"authorities\";\n\t// @formatter:on\n\n\tprivate static final String TABLE_NAME = \"oauth2_authorization_consent\";\n\n\tprivate static final String PK_FILTER = \"registered_client_id = ? AND principal_name = ?\";\n\n\t// @formatter:off\n\tprivate static final String LOAD_AUTHORIZATION_CONSENT_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + PK_FILTER;\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String SAVE_AUTHORIZATION_CONSENT_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?)\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String UPDATE_AUTHORIZATION_CONSENT_SQL = \"UPDATE \" + TABLE_NAME\n\t\t\t+ \" SET authorities = ?\"\n\t\t\t+ \" WHERE \" + PK_FILTER;\n\t// @formatter:on\n\n\tprivate static final String REMOVE_AUTHORIZATION_CONSENT_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + PK_FILTER;\n\n\tprivate final JdbcOperations jdbcOperations;\n\n\tprivate RowMapper<OAuth2AuthorizationConsent> authorizationConsentRowMapper;\n\n\tprivate Function<OAuth2AuthorizationConsent, List<SqlParameterValue>> authorizationConsentParametersMapper;\n\n\t/**\n\t * Constructs a {@code JdbcOAuth2AuthorizationConsentService} using the provided\n\t * parameters.\n\t * @param jdbcOperations the JDBC operations\n\t * @param registeredClientRepository the registered client repository\n\t */\n\tpublic JdbcOAuth2AuthorizationConsentService(JdbcOperations jdbcOperations,\n\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\tAssert.notNull(jdbcOperations, \"jdbcOperations cannot be null\");\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t\tthis.authorizationConsentRowMapper = new OAuth2AuthorizationConsentRowMapper(registeredClientRepository);\n\t\tthis.authorizationConsentParametersMapper = new OAuth2AuthorizationConsentParametersMapper();\n\t}\n\n\t@Override\n\tpublic void save(OAuth2AuthorizationConsent authorizationConsent) {\n\t\tAssert.notNull(authorizationConsent, \"authorizationConsent cannot be null\");\n\t\tOAuth2AuthorizationConsent existingAuthorizationConsent = findById(authorizationConsent.getRegisteredClientId(),\n\t\t\t\tauthorizationConsent.getPrincipalName());\n\t\tif (existingAuthorizationConsent == null) {\n\t\t\tinsertAuthorizationConsent(authorizationConsent);\n\t\t}\n\t\telse {\n\t\t\tupdateAuthorizationConsent(authorizationConsent);\n\t\t}\n\t}\n\n\tprivate void updateAuthorizationConsent(OAuth2AuthorizationConsent authorizationConsent) {\n\t\tList<SqlParameterValue> parameters = this.authorizationConsentParametersMapper.apply(authorizationConsent);\n\t\tSqlParameterValue registeredClientId = parameters.remove(0);\n\t\tSqlParameterValue principalName = parameters.remove(0);\n\t\tparameters.add(registeredClientId);\n\t\tparameters.add(principalName);\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\tthis.jdbcOperations.update(UPDATE_AUTHORIZATION_CONSENT_SQL, pss);\n\t}\n\n\tprivate void insertAuthorizationConsent(OAuth2AuthorizationConsent authorizationConsent) {\n\t\tList<SqlParameterValue> parameters = this.authorizationConsentParametersMapper.apply(authorizationConsent);\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\tthis.jdbcOperations.update(SAVE_AUTHORIZATION_CONSENT_SQL, pss);\n\t}\n\n\t@Override\n\tpublic void remove(OAuth2AuthorizationConsent authorizationConsent) {\n\t\tAssert.notNull(authorizationConsent, \"authorizationConsent cannot be null\");\n\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, authorizationConsent.getRegisteredClientId()),\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName()) };\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\tthis.jdbcOperations.update(REMOVE_AUTHORIZATION_CONSENT_SQL, pss);\n\t}\n\n\t@Override\n\t@Nullable\n\tpublic OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {\n\t\tAssert.hasText(registeredClientId, \"registeredClientId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, registeredClientId),\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, principalName) };\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\tList<OAuth2AuthorizationConsent> result = this.jdbcOperations.query(LOAD_AUTHORIZATION_CONSENT_SQL, pss,\n\t\t\t\tthis.authorizationConsentRowMapper);\n\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t}\n\n\t/**\n\t * Sets the {@link RowMapper} used for mapping the current row in\n\t * {@code java.sql.ResultSet} to {@link OAuth2AuthorizationConsent}. The default is\n\t * {@link OAuth2AuthorizationConsentRowMapper}.\n\t * @param authorizationConsentRowMapper the {@link RowMapper} used for mapping the\n\t * current row in {@code ResultSet} to {@link OAuth2AuthorizationConsent}\n\t */\n\tpublic final void setAuthorizationConsentRowMapper(\n\t\t\tRowMapper<OAuth2AuthorizationConsent> authorizationConsentRowMapper) {\n\t\tAssert.notNull(authorizationConsentRowMapper, \"authorizationConsentRowMapper cannot be null\");\n\t\tthis.authorizationConsentRowMapper = authorizationConsentRowMapper;\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping {@link OAuth2AuthorizationConsent} to a\n\t * {@code List} of {@link SqlParameterValue}. The default is\n\t * {@link OAuth2AuthorizationConsentParametersMapper}.\n\t * @param authorizationConsentParametersMapper the {@code Function} used for mapping\n\t * {@link OAuth2AuthorizationConsent} to a {@code List} of {@link SqlParameterValue}\n\t */\n\tpublic final void setAuthorizationConsentParametersMapper(\n\t\t\tFunction<OAuth2AuthorizationConsent, List<SqlParameterValue>> authorizationConsentParametersMapper) {\n\t\tAssert.notNull(authorizationConsentParametersMapper, \"authorizationConsentParametersMapper cannot be null\");\n\t\tthis.authorizationConsentParametersMapper = authorizationConsentParametersMapper;\n\t}\n\n\tprotected final JdbcOperations getJdbcOperations() {\n\t\treturn this.jdbcOperations;\n\t}\n\n\tprotected final RowMapper<OAuth2AuthorizationConsent> getAuthorizationConsentRowMapper() {\n\t\treturn this.authorizationConsentRowMapper;\n\t}\n\n\tprotected final Function<OAuth2AuthorizationConsent, List<SqlParameterValue>> getAuthorizationConsentParametersMapper() {\n\t\treturn this.authorizationConsentParametersMapper;\n\t}\n\n\t/**\n\t * The default {@link RowMapper} that maps the current row in {@code ResultSet} to\n\t * {@link OAuth2AuthorizationConsent}.\n\t */\n\tpublic static class OAuth2AuthorizationConsentRowMapper implements RowMapper<OAuth2AuthorizationConsent> {\n\n\t\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\t\tpublic OAuth2AuthorizationConsentRowMapper(RegisteredClientRepository registeredClientRepository) {\n\t\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2AuthorizationConsent mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\tString registeredClientId = rs.getString(\"registered_client_id\");\n\t\t\tRegisteredClient registeredClient = this.registeredClientRepository.findById(registeredClientId);\n\t\t\tif (registeredClient == null) {\n\t\t\t\tthrow new DataRetrievalFailureException(\"The RegisteredClient with id '\" + registeredClientId\n\t\t\t\t\t\t+ \"' was not found in the RegisteredClientRepository.\");\n\t\t\t}\n\n\t\t\tString principalName = rs.getString(\"principal_name\");\n\n\t\t\tOAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId,\n\t\t\t\t\tprincipalName);\n\t\t\tString authorizationConsentAuthorities = rs.getString(\"authorities\");\n\t\t\tif (authorizationConsentAuthorities != null) {\n\t\t\t\tfor (String authority : StringUtils.commaDelimitedListToSet(authorizationConsentAuthorities)) {\n\t\t\t\t\tbuilder.authority(new SimpleGrantedAuthority(authority));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn builder.build();\n\t\t}\n\n\t\tprotected final RegisteredClientRepository getRegisteredClientRepository() {\n\t\t\treturn this.registeredClientRepository;\n\t\t}\n\n\t}\n\n\t/**\n\t * The default {@code Function} that maps {@link OAuth2AuthorizationConsent} to a\n\t * {@code List} of {@link SqlParameterValue}.\n\t */\n\tpublic static class OAuth2AuthorizationConsentParametersMapper\n\t\t\timplements Function<OAuth2AuthorizationConsent, List<SqlParameterValue>> {\n\n\t\t@Override\n\t\tpublic List<SqlParameterValue> apply(OAuth2AuthorizationConsent authorizationConsent) {\n\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorizationConsent.getRegisteredClientId()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName()));\n\n\t\t\tSet<String> authorities = new HashSet<>();\n\t\t\tfor (GrantedAuthority authority : authorizationConsent.getAuthorities()) {\n\t\t\t\tauthorities.add(authority.getAuthority());\n\t\t\t}\n\t\t\tparameters\n\t\t\t\t.add(new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToDelimitedString(authorities, \",\")));\n\t\t\treturn parameters;\n\t\t}\n\n\t}\n\n\tstatic class JdbcOAuth2AuthorizationConsentServiceRuntimeHintsRegistrar implements RuntimeHintsRegistrar {\n\n\t\t@Override\n\t\tpublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {\n\t\t\thints.resources()\n\t\t\t\t.registerResource(new ClassPathResource(\n\t\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql\"));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.nio.charset.StandardCharsets;\nimport java.sql.DatabaseMetaData;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.Module;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport tools.jackson.databind.JacksonModule;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.context.annotation.ImportRuntimeHints;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.ConnectionCallback;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.jdbc.support.lob.DefaultLobHandler;\nimport org.springframework.jdbc.support.lob.LobCreator;\nimport org.springframework.jdbc.support.lob.LobHandler;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A JDBC implementation of an {@link OAuth2AuthorizationService} that uses a\n * {@link JdbcOperations} for {@link OAuth2Authorization} persistence.\n *\n * <p>\n * <b>IMPORTANT:</b> This {@code OAuth2AuthorizationService} depends on the table\n * definition described in\n * \"classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\"\n * and therefore MUST be defined in the database schema.\n *\n * <p>\n * <b>NOTE:</b> This {@code OAuth2AuthorizationService} is a simplified JDBC\n * implementation that MAY be used in a production environment. However, it does have\n * limitations as it likely won't perform well in an environment requiring high\n * throughput. The expectation is that the consuming application will provide their own\n * implementation of {@code OAuth2AuthorizationService} that meets the performance\n * requirements for its deployment environment.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @author Josh Long\n * @since 7.0\n * @see OAuth2AuthorizationService\n * @see OAuth2Authorization\n * @see JdbcOperations\n * @see RowMapper\n */\n@ImportRuntimeHints(JdbcOAuth2AuthorizationService.JdbcOAuth2AuthorizationServiceRuntimeHintsRegistrar.class)\npublic class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationService {\n\n\tprivate static final String REFRESH_TOKEN_VALUE = \"refresh_token_value\";\n\n\tprivate static final String AUTHORIZATION_CODE_VALUE = \"authorization_code_value\";\n\n\tprivate static final String ACCESS_TOKEN_VALUE = \"access_token_value\";\n\n\tprivate static final String OIDC_ID_TOKEN_VALUE = \"oidc_id_token_value\";\n\n\tprivate static final String USER_CODE_VALUE = \"user_code_value\";\n\n\tprivate static final String DEVICE_CODE_VALUE = \"device_code_value\";\n\n\tprivate static final String AUTHORIZATION_CODE_METADATA = \"authorization_code_metadata\";\n\n\tprivate static final String ACCESS_TOKEN_METADATA = \"access_token_metadata\";\n\n\tprivate static final String OIDC_ID_TOKEN_METADATA = \"oidc_id_token_metadata\";\n\n\tprivate static final String REFRESH_TOKEN_METADATA = \"refresh_token_metadata\";\n\n\tprivate static final String USER_CODE_METADATA = \"user_code_metadata\";\n\n\tprivate static final String DEVICE_CODE_METADATA = \"device_code_metadata\";\n\n\t// @formatter:off\n\tprivate static final String COLUMN_NAMES = \"id, \"\n\t\t\t+ \"registered_client_id, \"\n\t\t\t+ \"principal_name, \"\n\t\t\t+ \"authorization_grant_type, \"\n\t\t\t+ \"authorized_scopes, \"\n\t\t\t+ \"attributes, \"\n\t\t\t+ \"state, \"\n\t\t\t+ \"authorization_code_value, \"\n\t\t\t+ \"authorization_code_issued_at, \"\n\t\t\t+ \"authorization_code_expires_at,\"\n\t\t\t+ \"authorization_code_metadata,\"\n\t\t\t+ \"access_token_value,\"\n\t\t\t+ \"access_token_issued_at,\"\n\t\t\t+ \"access_token_expires_at,\"\n\t\t\t+ \"access_token_metadata,\"\n\t\t\t+ \"access_token_type,\"\n\t\t\t+ \"access_token_scopes,\"\n\t\t\t+ \"oidc_id_token_value,\"\n\t\t\t+ \"oidc_id_token_issued_at,\"\n\t\t\t+ \"oidc_id_token_expires_at,\"\n\t\t\t+ \"oidc_id_token_metadata,\"\n\t\t\t+ \"refresh_token_value,\"\n\t\t\t+ \"refresh_token_issued_at,\"\n\t\t\t+ \"refresh_token_expires_at,\"\n\t\t\t+ \"refresh_token_metadata,\"\n\t\t\t+ \"user_code_value,\"\n\t\t\t+ \"user_code_issued_at,\"\n\t\t\t+ \"user_code_expires_at,\"\n\t\t\t+ \"user_code_metadata,\"\n\t\t\t+ \"device_code_value,\"\n\t\t\t+ \"device_code_issued_at,\"\n\t\t\t+ \"device_code_expires_at,\"\n\t\t\t+ \"device_code_metadata\";\n\t// @formatter:on\n\n\tprivate static final String TABLE_NAME = \"oauth2_authorization\";\n\n\tprivate static final String PK_FILTER = \"id = ?\";\n\n\tprivate static final String UNKNOWN_TOKEN_TYPE_FILTER = \"state = ? OR authorization_code_value = ? OR \"\n\t\t\t+ \"access_token_value = ? OR oidc_id_token_value = ? OR refresh_token_value = ? OR user_code_value = ? OR \"\n\t\t\t+ \"device_code_value = ?\";\n\n\tprivate static final String STATE_FILTER = \"state = ?\";\n\n\tprivate static final String AUTHORIZATION_CODE_FILTER = \"authorization_code_value = ?\";\n\n\tprivate static final String ACCESS_TOKEN_FILTER = \"access_token_value = ?\";\n\n\tprivate static final String ID_TOKEN_FILTER = \"oidc_id_token_value = ?\";\n\n\tprivate static final String REFRESH_TOKEN_FILTER = \"refresh_token_value = ?\";\n\n\tprivate static final String USER_CODE_FILTER = \"user_code_value = ?\";\n\n\tprivate static final String DEVICE_CODE_FILTER = \"device_code_value = ?\";\n\n\t// @formatter:off\n\tprivate static final String LOAD_AUTHORIZATION_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String SAVE_AUTHORIZATION_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String UPDATE_AUTHORIZATION_SQL = \"UPDATE \" + TABLE_NAME\n\t\t\t+ \" SET registered_client_id = ?, principal_name = ?, authorization_grant_type = ?, authorized_scopes = ?, attributes = ?, state = ?,\"\n\t\t\t+ \" authorization_code_value = ?, authorization_code_issued_at = ?, authorization_code_expires_at = ?, authorization_code_metadata = ?,\"\n\t\t\t+ \" access_token_value = ?, access_token_issued_at = ?, access_token_expires_at = ?, access_token_metadata = ?, access_token_type = ?, access_token_scopes = ?,\"\n\t\t\t+ \" oidc_id_token_value = ?, oidc_id_token_issued_at = ?, oidc_id_token_expires_at = ?, oidc_id_token_metadata = ?,\"\n\t\t\t+ \" refresh_token_value = ?, refresh_token_issued_at = ?, refresh_token_expires_at = ?, refresh_token_metadata = ?,\"\n\t\t\t+ \" user_code_value = ?, user_code_issued_at = ?, user_code_expires_at = ?, user_code_metadata = ?,\"\n\t\t\t+ \" device_code_value = ?, device_code_issued_at = ?, device_code_expires_at = ?, device_code_metadata = ?\"\n\t\t\t+ \" WHERE \" + PK_FILTER;\n\t// @formatter:on\n\n\tprivate static final String REMOVE_AUTHORIZATION_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + PK_FILTER;\n\n\tprivate static Map<String, ColumnMetadata> columnMetadataMap;\n\n\tprivate final JdbcOperations jdbcOperations;\n\n\tprivate final LobHandler lobHandler;\n\n\tprivate RowMapper<OAuth2Authorization> authorizationRowMapper;\n\n\tprivate Function<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper;\n\n\t/**\n\t * Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters.\n\t * @param jdbcOperations the JDBC operations\n\t * @param registeredClientRepository the registered client repository\n\t */\n\tpublic JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations,\n\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\tthis(jdbcOperations, registeredClientRepository, new DefaultLobHandler());\n\t}\n\n\t/**\n\t * Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters.\n\t * @param jdbcOperations the JDBC operations\n\t * @param registeredClientRepository the registered client repository\n\t * @param lobHandler the handler for large binary fields and large text fields\n\t */\n\tpublic JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations,\n\t\t\tRegisteredClientRepository registeredClientRepository, LobHandler lobHandler) {\n\t\tAssert.notNull(jdbcOperations, \"jdbcOperations cannot be null\");\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(lobHandler, \"lobHandler cannot be null\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t\tthis.lobHandler = lobHandler;\n\t\tJsonMapperOAuth2AuthorizationRowMapper authorizationRowMapper = new JsonMapperOAuth2AuthorizationRowMapper(\n\t\t\t\tregisteredClientRepository);\n\t\tauthorizationRowMapper.setLobHandler(lobHandler);\n\t\tthis.authorizationRowMapper = authorizationRowMapper;\n\t\tthis.authorizationParametersMapper = new JsonMapperOAuth2AuthorizationParametersMapper();\n\t\tinitColumnMetadata(jdbcOperations);\n\t}\n\n\t@Override\n\tpublic void save(OAuth2Authorization authorization) {\n\t\tAssert.notNull(authorization, \"authorization cannot be null\");\n\t\tOAuth2Authorization existingAuthorization = findById(authorization.getId());\n\t\tif (existingAuthorization == null) {\n\t\t\tinsertAuthorization(authorization);\n\t\t}\n\t\telse {\n\t\t\tupdateAuthorization(authorization);\n\t\t}\n\t}\n\n\tprivate void updateAuthorization(OAuth2Authorization authorization) {\n\t\tList<SqlParameterValue> parameters = this.authorizationParametersMapper.apply(authorization);\n\t\tSqlParameterValue id = parameters.remove(0);\n\t\tparameters.add(id);\n\t\ttry (LobCreator lobCreator = this.lobHandler.getLobCreator()) {\n\t\t\tPreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,\n\t\t\t\t\tparameters.toArray());\n\t\t\tthis.jdbcOperations.update(UPDATE_AUTHORIZATION_SQL, pss);\n\t\t}\n\t}\n\n\tprivate void insertAuthorization(OAuth2Authorization authorization) {\n\t\tList<SqlParameterValue> parameters = this.authorizationParametersMapper.apply(authorization);\n\t\ttry (LobCreator lobCreator = this.lobHandler.getLobCreator()) {\n\t\t\tPreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,\n\t\t\t\t\tparameters.toArray());\n\t\t\tthis.jdbcOperations.update(SAVE_AUTHORIZATION_SQL, pss);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void remove(OAuth2Authorization authorization) {\n\t\tAssert.notNull(authorization, \"authorization cannot be null\");\n\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, authorization.getId()) };\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\tthis.jdbcOperations.update(REMOVE_AUTHORIZATION_SQL, pss);\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2Authorization findById(String id) {\n\t\tAssert.hasText(id, \"id cannot be empty\");\n\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, id));\n\t\treturn findBy(PK_FILTER, parameters);\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {\n\t\tAssert.hasText(token, \"token cannot be empty\");\n\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\tif (tokenType == null) {\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, token));\n\t\t\tparameters.add(mapToSqlParameter(AUTHORIZATION_CODE_VALUE, token));\n\t\t\tparameters.add(mapToSqlParameter(ACCESS_TOKEN_VALUE, token));\n\t\t\tparameters.add(mapToSqlParameter(OIDC_ID_TOKEN_VALUE, token));\n\t\t\tparameters.add(mapToSqlParameter(REFRESH_TOKEN_VALUE, token));\n\t\t\tparameters.add(mapToSqlParameter(USER_CODE_VALUE, token));\n\t\t\tparameters.add(mapToSqlParameter(DEVICE_CODE_VALUE, token));\n\t\t\treturn findBy(UNKNOWN_TOKEN_TYPE_FILTER, parameters);\n\t\t}\n\t\telse if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, token));\n\t\t\treturn findBy(STATE_FILTER, parameters);\n\t\t}\n\t\telse if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {\n\t\t\tparameters.add(mapToSqlParameter(AUTHORIZATION_CODE_VALUE, token));\n\t\t\treturn findBy(AUTHORIZATION_CODE_FILTER, parameters);\n\t\t}\n\t\telse if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {\n\t\t\tparameters.add(mapToSqlParameter(ACCESS_TOKEN_VALUE, token));\n\t\t\treturn findBy(ACCESS_TOKEN_FILTER, parameters);\n\t\t}\n\t\telse if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {\n\t\t\tparameters.add(mapToSqlParameter(OIDC_ID_TOKEN_VALUE, token));\n\t\t\treturn findBy(ID_TOKEN_FILTER, parameters);\n\t\t}\n\t\telse if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {\n\t\t\tparameters.add(mapToSqlParameter(REFRESH_TOKEN_VALUE, token));\n\t\t\treturn findBy(REFRESH_TOKEN_FILTER, parameters);\n\t\t}\n\t\telse if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) {\n\t\t\tparameters.add(mapToSqlParameter(USER_CODE_VALUE, token));\n\t\t\treturn findBy(USER_CODE_FILTER, parameters);\n\t\t}\n\t\telse if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) {\n\t\t\tparameters.add(mapToSqlParameter(DEVICE_CODE_VALUE, token));\n\t\t\treturn findBy(DEVICE_CODE_FILTER, parameters);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate OAuth2Authorization findBy(String filter, List<SqlParameterValue> parameters) {\n\t\ttry (LobCreator lobCreator = getLobHandler().getLobCreator()) {\n\t\t\tPreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,\n\t\t\t\t\tparameters.toArray());\n\t\t\tList<OAuth2Authorization> result = getJdbcOperations().query(LOAD_AUTHORIZATION_SQL + filter, pss,\n\t\t\t\t\tgetAuthorizationRowMapper());\n\t\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link RowMapper} used for mapping the current row in\n\t * {@code java.sql.ResultSet} to {@link OAuth2Authorization}. The default is\n\t * {@link JsonMapperOAuth2AuthorizationRowMapper}.\n\t * @param authorizationRowMapper the {@link RowMapper} used for mapping the current\n\t * row in {@code ResultSet} to {@link OAuth2Authorization}\n\t */\n\tpublic final void setAuthorizationRowMapper(RowMapper<OAuth2Authorization> authorizationRowMapper) {\n\t\tAssert.notNull(authorizationRowMapper, \"authorizationRowMapper cannot be null\");\n\t\tthis.authorizationRowMapper = authorizationRowMapper;\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping {@link OAuth2Authorization} to a\n\t * {@code List} of {@link SqlParameterValue}. The default is\n\t * {@link JsonMapperOAuth2AuthorizationParametersMapper}.\n\t * @param authorizationParametersMapper the {@code Function} used for mapping\n\t * {@link OAuth2Authorization} to a {@code List} of {@link SqlParameterValue}\n\t */\n\tpublic final void setAuthorizationParametersMapper(\n\t\t\tFunction<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper) {\n\t\tAssert.notNull(authorizationParametersMapper, \"authorizationParametersMapper cannot be null\");\n\t\tthis.authorizationParametersMapper = authorizationParametersMapper;\n\t}\n\n\tprotected final JdbcOperations getJdbcOperations() {\n\t\treturn this.jdbcOperations;\n\t}\n\n\tprotected final LobHandler getLobHandler() {\n\t\treturn this.lobHandler;\n\t}\n\n\tprotected final RowMapper<OAuth2Authorization> getAuthorizationRowMapper() {\n\t\treturn this.authorizationRowMapper;\n\t}\n\n\tprotected final Function<OAuth2Authorization, List<SqlParameterValue>> getAuthorizationParametersMapper() {\n\t\treturn this.authorizationParametersMapper;\n\t}\n\n\tprivate static void initColumnMetadata(JdbcOperations jdbcOperations) {\n\t\tcolumnMetadataMap = new HashMap<>();\n\t\tColumnMetadata columnMetadata;\n\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, \"attributes\", Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, AUTHORIZATION_CODE_VALUE, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, AUTHORIZATION_CODE_METADATA, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, ACCESS_TOKEN_VALUE, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, ACCESS_TOKEN_METADATA, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, OIDC_ID_TOKEN_VALUE, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, OIDC_ID_TOKEN_METADATA, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, REFRESH_TOKEN_VALUE, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, REFRESH_TOKEN_METADATA, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, USER_CODE_VALUE, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, USER_CODE_METADATA, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, DEVICE_CODE_VALUE, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t\tcolumnMetadata = getColumnMetadata(jdbcOperations, DEVICE_CODE_METADATA, Types.BLOB);\n\t\tcolumnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);\n\t}\n\n\tprivate static ColumnMetadata getColumnMetadata(JdbcOperations jdbcOperations, String columnName,\n\t\t\tint defaultDataType) {\n\t\tInteger dataType = jdbcOperations.execute((ConnectionCallback<Integer>) (conn) -> {\n\t\t\tDatabaseMetaData databaseMetaData = conn.getMetaData();\n\t\t\tResultSet rs = databaseMetaData.getColumns(null, null, TABLE_NAME, columnName);\n\t\t\tif (rs.next()) {\n\t\t\t\treturn rs.getInt(\"DATA_TYPE\");\n\t\t\t}\n\t\t\t// NOTE: (Applies to HSQL)\n\t\t\t// When a database object is created with one of the CREATE statements or\n\t\t\t// renamed with the ALTER statement,\n\t\t\t// if the name is enclosed in double quotes, the exact name is used as the\n\t\t\t// case-normal form.\n\t\t\t// But if it is not enclosed in double quotes,\n\t\t\t// the name is converted to uppercase and this uppercase version is stored in\n\t\t\t// the database as the case-normal form.\n\t\t\trs = databaseMetaData.getColumns(null, null, TABLE_NAME.toUpperCase(Locale.ENGLISH),\n\t\t\t\t\tcolumnName.toUpperCase(Locale.ENGLISH));\n\t\t\tif (rs.next()) {\n\t\t\t\treturn rs.getInt(\"DATA_TYPE\");\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\treturn new ColumnMetadata(columnName, (dataType != null) ? dataType : defaultDataType);\n\t}\n\n\tprivate static SqlParameterValue mapToSqlParameter(String columnName, String value) {\n\t\tColumnMetadata columnMetadata = columnMetadataMap.get(columnName);\n\t\treturn (Types.BLOB == columnMetadata.getDataType() && StringUtils.hasText(value))\n\t\t\t\t? new SqlParameterValue(Types.BLOB, value.getBytes(StandardCharsets.UTF_8))\n\t\t\t\t: new SqlParameterValue(columnMetadata.getDataType(), value);\n\t}\n\n\t/**\n\t * The default {@link RowMapper} that maps the current row in\n\t * {@code java.sql.ResultSet} to {@link OAuth2Authorization} using Jackson 3's\n\t * {@link JsonMapper}.\n\t *\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tpublic static class JsonMapperOAuth2AuthorizationRowMapper extends AbstractOAuth2AuthorizationRowMapper {\n\n\t\tprivate final JsonMapper jsonMapper;\n\n\t\tpublic JsonMapperOAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {\n\t\t\tthis(registeredClientRepository, Jackson3.createJsonMapper());\n\t\t}\n\n\t\tpublic JsonMapperOAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository,\n\t\t\t\tJsonMapper jsonMapper) {\n\t\t\tsuper(registeredClientRepository);\n\t\t\tAssert.notNull(jsonMapper, \"jsonMapper cannot be null\");\n\t\t\tthis.jsonMapper = jsonMapper;\n\t\t}\n\n\t\t@Override\n\t\tMap<String, Object> readValue(String data) {\n\t\t\tfinal ParameterizedTypeReference<Map<String, Object>> typeReference = new ParameterizedTypeReference<>() {\n\t\t\t};\n\t\t\ttools.jackson.databind.JavaType javaType = this.jsonMapper.getTypeFactory()\n\t\t\t\t.constructType(typeReference.getType());\n\t\t\treturn this.jsonMapper.readValue(data, javaType);\n\t\t}\n\n\t}\n\n\t/**\n\t * A {@link RowMapper} that maps the current row in {@code java.sql.ResultSet} to\n\t * {@link OAuth2Authorization} using Jackson 2's {@link ObjectMapper}.\n\t *\n\t * @deprecated Use {@link JsonMapperOAuth2AuthorizationRowMapper} to switch to Jackson\n\t * 3.\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\t@SuppressWarnings(\"removal\")\n\tpublic static class OAuth2AuthorizationRowMapper extends AbstractOAuth2AuthorizationRowMapper {\n\n\t\tprivate ObjectMapper objectMapper = Jackson2.createObjectMapper();\n\n\t\tpublic OAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {\n\t\t\tsuper(registeredClientRepository);\n\t\t}\n\n\t\tpublic final void setObjectMapper(ObjectMapper objectMapper) {\n\t\t\tAssert.notNull(objectMapper, \"objectMapper cannot be null\");\n\t\t\tthis.objectMapper = objectMapper;\n\t\t}\n\n\t\tprotected ObjectMapper getObjectMapper() {\n\t\t\treturn this.objectMapper;\n\t\t}\n\n\t\t@Override\n\t\tMap<String, Object> readValue(String data) throws JsonProcessingException {\n\t\t\tfinal ParameterizedTypeReference<Map<String, Object>> typeReference = new ParameterizedTypeReference<>() {\n\t\t\t};\n\t\t\tcom.fasterxml.jackson.databind.JavaType javaType = this.objectMapper.getTypeFactory()\n\t\t\t\t.constructType(typeReference.getType());\n\t\t\treturn this.objectMapper.readValue(data, javaType);\n\t\t}\n\n\t}\n\n\t/**\n\t * The base {@link RowMapper} that maps the current row in {@code java.sql.ResultSet}\n\t * to {@link OAuth2Authorization}. This is extracted to a distinct class so that\n\t * {@link OAuth2AuthorizationRowMapper} can be deprecated in favor of\n\t * {@link JsonMapperOAuth2AuthorizationRowMapper}.\n\t */\n\tprivate abstract static class AbstractOAuth2AuthorizationRowMapper implements RowMapper<OAuth2Authorization> {\n\n\t\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\t\tprivate LobHandler lobHandler = new DefaultLobHandler();\n\n\t\tprivate AbstractOAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {\n\t\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic OAuth2Authorization mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\tString registeredClientId = rs.getString(\"registered_client_id\");\n\t\t\tRegisteredClient registeredClient = this.registeredClientRepository.findById(registeredClientId);\n\t\t\tif (registeredClient == null) {\n\t\t\t\tthrow new DataRetrievalFailureException(\"The RegisteredClient with id '\" + registeredClientId\n\t\t\t\t\t\t+ \"' was not found in the RegisteredClientRepository.\");\n\t\t\t}\n\n\t\t\tOAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient);\n\t\t\tString id = rs.getString(\"id\");\n\t\t\tString principalName = rs.getString(\"principal_name\");\n\t\t\tString authorizationGrantType = rs.getString(\"authorization_grant_type\");\n\t\t\tSet<String> authorizedScopes = Collections.emptySet();\n\t\t\tString authorizedScopesString = rs.getString(\"authorized_scopes\");\n\t\t\tif (authorizedScopesString != null) {\n\t\t\t\tauthorizedScopes = StringUtils.commaDelimitedListToSet(authorizedScopesString);\n\t\t\t}\n\t\t\tMap<String, Object> attributes = parseMap(getLobValue(rs, \"attributes\"));\n\n\t\t\tbuilder.id(id)\n\t\t\t\t.principalName(principalName)\n\t\t\t\t.authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))\n\t\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t\t.attributes((attrs) -> attrs.putAll(attributes));\n\n\t\t\tString state = rs.getString(\"state\");\n\t\t\tif (StringUtils.hasText(state)) {\n\t\t\t\tbuilder.attribute(OAuth2ParameterNames.STATE, state);\n\t\t\t}\n\n\t\t\tInstant tokenIssuedAt;\n\t\t\tInstant tokenExpiresAt;\n\t\t\tString authorizationCodeValue = getLobValue(rs, AUTHORIZATION_CODE_VALUE);\n\n\t\t\tif (StringUtils.hasText(authorizationCodeValue)) {\n\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"authorization_code_issued_at\").toInstant();\n\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"authorization_code_expires_at\").toInstant();\n\t\t\t\tMap<String, Object> authorizationCodeMetadata = parseMap(getLobValue(rs, AUTHORIZATION_CODE_METADATA));\n\n\t\t\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(authorizationCodeValue,\n\t\t\t\t\t\ttokenIssuedAt, tokenExpiresAt);\n\t\t\t\tbuilder.token(authorizationCode, (metadata) -> metadata.putAll(authorizationCodeMetadata));\n\t\t\t}\n\n\t\t\tString accessTokenValue = getLobValue(rs, ACCESS_TOKEN_VALUE);\n\t\t\tif (StringUtils.hasText(accessTokenValue)) {\n\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"access_token_issued_at\").toInstant();\n\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"access_token_expires_at\").toInstant();\n\t\t\t\tMap<String, Object> accessTokenMetadata = parseMap(getLobValue(rs, ACCESS_TOKEN_METADATA));\n\t\t\t\tOAuth2AccessToken.TokenType tokenType = null;\n\t\t\t\tif (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString(\"access_token_type\"))) {\n\t\t\t\t\ttokenType = OAuth2AccessToken.TokenType.BEARER;\n\t\t\t\t}\n\t\t\t\telse if (OAuth2AccessToken.TokenType.DPOP.getValue()\n\t\t\t\t\t.equalsIgnoreCase(rs.getString(\"access_token_type\"))) {\n\t\t\t\t\ttokenType = OAuth2AccessToken.TokenType.DPOP;\n\t\t\t\t}\n\n\t\t\t\tSet<String> scopes = Collections.emptySet();\n\t\t\t\tString accessTokenScopes = rs.getString(\"access_token_scopes\");\n\t\t\t\tif (accessTokenScopes != null) {\n\t\t\t\t\tscopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);\n\t\t\t\t}\n\t\t\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, accessTokenValue, tokenIssuedAt,\n\t\t\t\t\t\ttokenExpiresAt, scopes);\n\t\t\t\tbuilder.token(accessToken, (metadata) -> metadata.putAll(accessTokenMetadata));\n\t\t\t}\n\n\t\t\tString oidcIdTokenValue = getLobValue(rs, OIDC_ID_TOKEN_VALUE);\n\t\t\tif (StringUtils.hasText(oidcIdTokenValue)) {\n\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"oidc_id_token_issued_at\").toInstant();\n\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"oidc_id_token_expires_at\").toInstant();\n\t\t\t\tMap<String, Object> oidcTokenMetadata = parseMap(getLobValue(rs, OIDC_ID_TOKEN_METADATA));\n\n\t\t\t\tOidcIdToken oidcToken = new OidcIdToken(oidcIdTokenValue, tokenIssuedAt, tokenExpiresAt,\n\t\t\t\t\t\t(Map<String, Object>) oidcTokenMetadata.get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME));\n\t\t\t\tbuilder.token(oidcToken, (metadata) -> metadata.putAll(oidcTokenMetadata));\n\t\t\t}\n\n\t\t\tString refreshTokenValue = getLobValue(rs, REFRESH_TOKEN_VALUE);\n\t\t\tif (StringUtils.hasText(refreshTokenValue)) {\n\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"refresh_token_issued_at\").toInstant();\n\t\t\t\ttokenExpiresAt = null;\n\t\t\t\tTimestamp refreshTokenExpiresAt = rs.getTimestamp(\"refresh_token_expires_at\");\n\t\t\t\tif (refreshTokenExpiresAt != null) {\n\t\t\t\t\ttokenExpiresAt = refreshTokenExpiresAt.toInstant();\n\t\t\t\t}\n\t\t\t\tMap<String, Object> refreshTokenMetadata = parseMap(getLobValue(rs, REFRESH_TOKEN_METADATA));\n\n\t\t\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(refreshTokenValue, tokenIssuedAt,\n\t\t\t\t\t\ttokenExpiresAt);\n\t\t\t\tbuilder.token(refreshToken, (metadata) -> metadata.putAll(refreshTokenMetadata));\n\t\t\t}\n\n\t\t\tString userCodeValue = getLobValue(rs, USER_CODE_VALUE);\n\t\t\tif (StringUtils.hasText(userCodeValue)) {\n\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"user_code_issued_at\").toInstant();\n\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"user_code_expires_at\").toInstant();\n\t\t\t\tMap<String, Object> userCodeMetadata = parseMap(getLobValue(rs, USER_CODE_METADATA));\n\n\t\t\t\tOAuth2UserCode userCode = new OAuth2UserCode(userCodeValue, tokenIssuedAt, tokenExpiresAt);\n\t\t\t\tbuilder.token(userCode, (metadata) -> metadata.putAll(userCodeMetadata));\n\t\t\t}\n\n\t\t\tString deviceCodeValue = getLobValue(rs, DEVICE_CODE_VALUE);\n\t\t\tif (StringUtils.hasText(deviceCodeValue)) {\n\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"device_code_issued_at\").toInstant();\n\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"device_code_expires_at\").toInstant();\n\t\t\t\tMap<String, Object> deviceCodeMetadata = parseMap(getLobValue(rs, DEVICE_CODE_METADATA));\n\n\t\t\t\tOAuth2DeviceCode deviceCode = new OAuth2DeviceCode(deviceCodeValue, tokenIssuedAt, tokenExpiresAt);\n\t\t\t\tbuilder.token(deviceCode, (metadata) -> metadata.putAll(deviceCodeMetadata));\n\t\t\t}\n\n\t\t\treturn builder.build();\n\t\t}\n\n\t\tprivate String getLobValue(ResultSet rs, String columnName) throws SQLException {\n\t\t\tString columnValue = null;\n\t\t\tColumnMetadata columnMetadata = columnMetadataMap.get(columnName);\n\t\t\tif (Types.BLOB == columnMetadata.getDataType()) {\n\t\t\t\tbyte[] columnValueBytes = this.lobHandler.getBlobAsBytes(rs, columnName);\n\t\t\t\tif (columnValueBytes != null) {\n\t\t\t\t\tcolumnValue = new String(columnValueBytes, StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (Types.CLOB == columnMetadata.getDataType()) {\n\t\t\t\tcolumnValue = this.lobHandler.getClobAsString(rs, columnName);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcolumnValue = rs.getString(columnName);\n\t\t\t}\n\t\t\treturn columnValue;\n\t\t}\n\n\t\tpublic final void setLobHandler(LobHandler lobHandler) {\n\t\t\tAssert.notNull(lobHandler, \"lobHandler cannot be null\");\n\t\t\tthis.lobHandler = lobHandler;\n\t\t}\n\n\t\tprotected final RegisteredClientRepository getRegisteredClientRepository() {\n\t\t\treturn this.registeredClientRepository;\n\t\t}\n\n\t\tprotected final LobHandler getLobHandler() {\n\t\t\treturn this.lobHandler;\n\t\t}\n\n\t\tprivate Map<String, Object> parseMap(String data) {\n\t\t\ttry {\n\t\t\t\treturn readValue(data);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\n\t\tabstract Map<String, Object> readValue(String data) throws Exception;\n\n\t}\n\n\t/**\n\t * The default {@code Function} that maps {@link OAuth2Authorization} to a\n\t * {@code List} of {@link SqlParameterValue} using an instance of Jackson 3's\n\t * {@link JsonMapper}.\n\t */\n\tpublic static class JsonMapperOAuth2AuthorizationParametersMapper\n\t\t\textends AbstractOAuth2AuthorizationParametersMapper {\n\n\t\tprivate final JsonMapper jsonMapper;\n\n\t\tpublic JsonMapperOAuth2AuthorizationParametersMapper() {\n\t\t\tthis(Jackson3.createJsonMapper());\n\t\t}\n\n\t\tpublic JsonMapperOAuth2AuthorizationParametersMapper(JsonMapper jsonMapper) {\n\t\t\tAssert.notNull(jsonMapper, \"jsonMapper cannot be null\");\n\t\t\tthis.jsonMapper = jsonMapper;\n\t\t}\n\n\t\t@Override\n\t\tString writeValueAsString(Map<String, Object> data) throws Exception {\n\t\t\treturn this.jsonMapper.writeValueAsString(data);\n\t\t}\n\n\t}\n\n\t/**\n\t * A {@code Function} that maps {@link OAuth2Authorization} to a {@code List} of\n\t * {@link SqlParameterValue} using an instance of Jackson 2's {@link ObjectMapper}.\n\t *\n\t * @deprecated Use {@link JsonMapperOAuth2AuthorizationParametersMapper} to switch to\n\t * Jackson 3.\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\t@SuppressWarnings(\"removal\")\n\tpublic static class OAuth2AuthorizationParametersMapper extends AbstractOAuth2AuthorizationParametersMapper {\n\n\t\tprivate ObjectMapper objectMapper = Jackson2.createObjectMapper();\n\n\t\t@Override\n\t\tString writeValueAsString(Map<String, Object> data) throws JsonProcessingException {\n\t\t\treturn this.objectMapper.writeValueAsString(data);\n\t\t}\n\n\t\tprotected final ObjectMapper getObjectMapper() {\n\t\t\treturn this.objectMapper;\n\t\t}\n\n\t\tpublic final void setObjectMapper(ObjectMapper objectMapper) {\n\t\t\tAssert.notNull(objectMapper, \"objectMapper cannot be null\");\n\t\t\tthis.objectMapper = objectMapper;\n\t\t}\n\n\t}\n\n\t/**\n\t * The base {@code Function} that maps {@link OAuth2Authorization} to a {@code List}\n\t * of {@link SqlParameterValue}.\n\t */\n\tprivate abstract static class AbstractOAuth2AuthorizationParametersMapper\n\t\t\timplements Function<OAuth2Authorization, List<SqlParameterValue>> {\n\n\t\tprivate AbstractOAuth2AuthorizationParametersMapper() {\n\t\t}\n\n\t\t@Override\n\t\tpublic List<SqlParameterValue> apply(OAuth2Authorization authorization) {\n\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getId()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getRegisteredClientId()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getPrincipalName()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getAuthorizationGrantType().getValue()));\n\n\t\t\tString authorizedScopes = null;\n\t\t\tif (!CollectionUtils.isEmpty(authorization.getAuthorizedScopes())) {\n\t\t\t\tauthorizedScopes = StringUtils.collectionToDelimitedString(authorization.getAuthorizedScopes(), \",\");\n\t\t\t}\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorizedScopes));\n\n\t\t\tString attributes = writeMap(authorization.getAttributes());\n\t\t\tparameters.add(mapToSqlParameter(\"attributes\", attributes));\n\n\t\t\tString state = null;\n\t\t\tString authorizationState = authorization.getAttribute(OAuth2ParameterNames.STATE);\n\t\t\tif (StringUtils.hasText(authorizationState)) {\n\t\t\t\tstate = authorizationState;\n\t\t\t}\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, state));\n\n\t\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization\n\t\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\t\tList<SqlParameterValue> authorizationCodeSqlParameters = toSqlParameterList(AUTHORIZATION_CODE_VALUE,\n\t\t\t\t\tAUTHORIZATION_CODE_METADATA, authorizationCode);\n\t\t\tparameters.addAll(authorizationCodeSqlParameters);\n\n\t\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getToken(OAuth2AccessToken.class);\n\t\t\tList<SqlParameterValue> accessTokenSqlParameters = toSqlParameterList(ACCESS_TOKEN_VALUE,\n\t\t\t\t\tACCESS_TOKEN_METADATA, accessToken);\n\t\t\tparameters.addAll(accessTokenSqlParameters);\n\t\t\tString accessTokenType = null;\n\t\t\tString accessTokenScopes = null;\n\t\t\tif (accessToken != null) {\n\t\t\t\taccessTokenType = accessToken.getToken().getTokenType().getValue();\n\t\t\t\tif (!CollectionUtils.isEmpty(accessToken.getToken().getScopes())) {\n\t\t\t\t\taccessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(),\n\t\t\t\t\t\t\t\",\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenType));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenScopes));\n\n\t\t\tOAuth2Authorization.Token<OidcIdToken> oidcIdToken = authorization.getToken(OidcIdToken.class);\n\t\t\tList<SqlParameterValue> oidcIdTokenSqlParameters = toSqlParameterList(OIDC_ID_TOKEN_VALUE,\n\t\t\t\t\tOIDC_ID_TOKEN_METADATA, oidcIdToken);\n\t\t\tparameters.addAll(oidcIdTokenSqlParameters);\n\n\t\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getRefreshToken();\n\t\t\tList<SqlParameterValue> refreshTokenSqlParameters = toSqlParameterList(REFRESH_TOKEN_VALUE,\n\t\t\t\t\tREFRESH_TOKEN_METADATA, refreshToken);\n\t\t\tparameters.addAll(refreshTokenSqlParameters);\n\n\t\t\tOAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);\n\t\t\tList<SqlParameterValue> userCodeSqlParameters = toSqlParameterList(USER_CODE_VALUE, USER_CODE_METADATA,\n\t\t\t\t\tuserCode);\n\t\t\tparameters.addAll(userCodeSqlParameters);\n\n\t\t\tOAuth2Authorization.Token<OAuth2DeviceCode> deviceCode = authorization.getToken(OAuth2DeviceCode.class);\n\t\t\tList<SqlParameterValue> deviceCodeSqlParameters = toSqlParameterList(DEVICE_CODE_VALUE,\n\t\t\t\t\tDEVICE_CODE_METADATA, deviceCode);\n\t\t\tparameters.addAll(deviceCodeSqlParameters);\n\n\t\t\treturn parameters;\n\t\t}\n\n\t\tprivate <T extends OAuth2Token> List<SqlParameterValue> toSqlParameterList(String tokenColumnName,\n\t\t\t\tString tokenMetadataColumnName, OAuth2Authorization.Token<T> token) {\n\n\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\t\tString tokenValue = null;\n\t\t\tTimestamp tokenIssuedAt = null;\n\t\t\tTimestamp tokenExpiresAt = null;\n\t\t\tString metadata = null;\n\t\t\tif (token != null) {\n\t\t\t\ttokenValue = token.getToken().getTokenValue();\n\t\t\t\tif (token.getToken().getIssuedAt() != null) {\n\t\t\t\t\ttokenIssuedAt = Timestamp.from(token.getToken().getIssuedAt());\n\t\t\t\t}\n\t\t\t\tif (token.getToken().getExpiresAt() != null) {\n\t\t\t\t\ttokenExpiresAt = Timestamp.from(token.getToken().getExpiresAt());\n\t\t\t\t}\n\t\t\t\tmetadata = writeMap(token.getMetadata());\n\t\t\t}\n\n\t\t\tparameters.add(mapToSqlParameter(tokenColumnName, tokenValue));\n\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP, tokenIssuedAt));\n\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP, tokenExpiresAt));\n\t\t\tparameters.add(mapToSqlParameter(tokenMetadataColumnName, metadata));\n\t\t\treturn parameters;\n\t\t}\n\n\t\tprivate String writeMap(Map<String, Object> data) {\n\t\t\ttry {\n\t\t\t\treturn writeValueAsString(data);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\n\t\tabstract String writeValueAsString(Map<String, Object> data) throws Exception;\n\n\t}\n\n\t/**\n\t * Nested class to protect from getting {@link NoClassDefFoundError} when Jackson 2 is\n\t * not on the classpath.\n\t *\n\t * @deprecated This is used to allow transition to Jackson 3. Use {@link Jackson3}\n\t * instead.\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tprivate static final class Jackson2 {\n\n\t\t@SuppressWarnings(\"removal\")\n\t\tprivate static ObjectMapper createObjectMapper() {\n\t\t\tObjectMapper objectMapper = new ObjectMapper();\n\t\t\tClassLoader classLoader = Jackson2.class.getClassLoader();\n\t\t\tList<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);\n\t\t\tobjectMapper.registerModules(securityModules);\n\t\t\tobjectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());\n\t\t\treturn objectMapper;\n\t\t}\n\n\t}\n\n\t/**\n\t * Nested class used to get a common default instance of {@link JsonMapper}. It is in\n\t * a nested class to protect from getting {@link NoClassDefFoundError} when Jackson 3\n\t * is not on the classpath.\n\t */\n\tprivate static final class Jackson3 {\n\n\t\tprivate static JsonMapper createJsonMapper() {\n\t\t\tList<JacksonModule> modules = SecurityJacksonModules.getModules(Jackson3.class.getClassLoader());\n\t\t\treturn JsonMapper.builder().addModules(modules).build();\n\t\t}\n\n\t}\n\n\tprivate static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {\n\n\t\tprivate final LobCreator lobCreator;\n\n\t\tprivate LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) {\n\t\t\tsuper(args);\n\t\t\tthis.lobCreator = lobCreator;\n\t\t}\n\n\t\t@Override\n\t\tprotected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {\n\t\t\tif (argValue instanceof SqlParameterValue paramValue) {\n\t\t\t\tif (paramValue.getSqlType() == Types.BLOB) {\n\t\t\t\t\tif (paramValue.getValue() != null) {\n\t\t\t\t\t\tAssert.isInstanceOf(byte[].class, paramValue.getValue(),\n\t\t\t\t\t\t\t\t\"Value of blob parameter must be byte[]\");\n\t\t\t\t\t}\n\t\t\t\t\tbyte[] valueBytes = (byte[]) paramValue.getValue();\n\t\t\t\t\tthis.lobCreator.setBlobAsBytes(ps, parameterPosition, valueBytes);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (paramValue.getSqlType() == Types.CLOB) {\n\t\t\t\t\tif (paramValue.getValue() != null) {\n\t\t\t\t\t\tAssert.isInstanceOf(String.class, paramValue.getValue(),\n\t\t\t\t\t\t\t\t\"Value of clob parameter must be String\");\n\t\t\t\t\t}\n\t\t\t\t\tString valueString = (String) paramValue.getValue();\n\t\t\t\t\tthis.lobCreator.setClobAsString(ps, parameterPosition, valueString);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsuper.doSetValue(ps, parameterPosition, argValue);\n\t\t}\n\n\t}\n\n\tprivate static final class ColumnMetadata {\n\n\t\tprivate final String columnName;\n\n\t\tprivate final int dataType;\n\n\t\tprivate ColumnMetadata(String columnName, int dataType) {\n\t\t\tthis.columnName = columnName;\n\t\t\tthis.dataType = dataType;\n\t\t}\n\n\t\tprivate String getColumnName() {\n\t\t\treturn this.columnName;\n\t\t}\n\n\t\tprivate int getDataType() {\n\t\t\treturn this.dataType;\n\t\t}\n\n\t}\n\n\tstatic class JdbcOAuth2AuthorizationServiceRuntimeHintsRegistrar implements RuntimeHintsRegistrar {\n\n\t\t@Override\n\t\tpublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {\n\t\t\thints.resources()\n\t\t\t\t.registerResource(new ClassPathResource(\n\t\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\"));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A representation of an OAuth 2.0 Authorization, which holds state related to the\n * authorization granted to a {@link #getRegisteredClientId() client}, by the\n * {@link #getPrincipalName() resource owner} or itself in the case of the\n * {@code client_credentials} grant type.\n *\n * @author Joe Grandja\n * @author Krisztian Toth\n * @since 7.0\n * @see RegisteredClient\n * @see AuthorizationGrantType\n * @see OAuth2Token\n * @see OAuth2AccessToken\n * @see OAuth2RefreshToken\n */\npublic class OAuth2Authorization implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 880363144799377926L;\n\n\tprivate String id;\n\n\tprivate String registeredClientId;\n\n\tprivate String principalName;\n\n\tprivate AuthorizationGrantType authorizationGrantType;\n\n\tprivate Set<String> authorizedScopes;\n\n\tprivate Map<Class<? extends OAuth2Token>, Token<?>> tokens;\n\n\tprivate Map<String, Object> attributes;\n\n\tprotected OAuth2Authorization() {\n\t}\n\n\t/**\n\t * Returns the identifier for the authorization.\n\t * @return the identifier for the authorization\n\t */\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * Returns the identifier for the {@link RegisteredClient#getId() registered client}.\n\t * @return the {@link RegisteredClient#getId()}\n\t */\n\tpublic String getRegisteredClientId() {\n\t\treturn this.registeredClientId;\n\t}\n\n\t/**\n\t * Returns the {@code Principal} name of the resource owner (or client).\n\t * @return the {@code Principal} name of the resource owner (or client)\n\t */\n\tpublic String getPrincipalName() {\n\t\treturn this.principalName;\n\t}\n\n\t/**\n\t * Returns the {@link AuthorizationGrantType authorization grant type} used for the\n\t * authorization.\n\t * @return the {@link AuthorizationGrantType} used for the authorization\n\t */\n\tpublic AuthorizationGrantType getAuthorizationGrantType() {\n\t\treturn this.authorizationGrantType;\n\t}\n\n\t/**\n\t * Returns the authorized scope(s).\n\t * @return the {@code Set} of authorized scope(s)\n\t */\n\tpublic Set<String> getAuthorizedScopes() {\n\t\treturn this.authorizedScopes;\n\t}\n\n\t/**\n\t * Returns the {@link Token} of type {@link OAuth2AccessToken}.\n\t * @return the {@link Token} of type {@link OAuth2AccessToken}\n\t */\n\tpublic Token<OAuth2AccessToken> getAccessToken() {\n\t\treturn getToken(OAuth2AccessToken.class);\n\t}\n\n\t/**\n\t * Returns the {@link Token} of type {@link OAuth2RefreshToken}.\n\t * @return the {@link Token} of type {@link OAuth2RefreshToken}, or {@code null} if\n\t * not available\n\t */\n\t@Nullable\n\tpublic Token<OAuth2RefreshToken> getRefreshToken() {\n\t\treturn getToken(OAuth2RefreshToken.class);\n\t}\n\n\t/**\n\t * Returns the {@link Token} of type {@code tokenType}.\n\t * @param tokenType the token type\n\t * @param <T> the type of the token\n\t * @return the {@link Token}, or {@code null} if not available\n\t */\n\t@Nullable\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends OAuth2Token> Token<T> getToken(Class<T> tokenType) {\n\t\tAssert.notNull(tokenType, \"tokenType cannot be null\");\n\t\tToken<?> token = this.tokens.get(tokenType);\n\t\treturn (token != null) ? (Token<T>) token : null;\n\t}\n\n\t/**\n\t * Returns the {@link Token} matching the {@code tokenValue}.\n\t * @param tokenValue the token value\n\t * @param <T> the type of the token\n\t * @return the {@link Token}, or {@code null} if not available\n\t */\n\t@Nullable\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends OAuth2Token> Token<T> getToken(String tokenValue) {\n\t\tAssert.hasText(tokenValue, \"tokenValue cannot be empty\");\n\t\tfor (Token<?> token : this.tokens.values()) {\n\t\t\tif (token.getToken().getTokenValue().equals(tokenValue)) {\n\t\t\t\treturn (Token<T>) token;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the attribute(s) associated to the authorization.\n\t * @return a {@code Map} of the attribute(s)\n\t */\n\tpublic Map<String, Object> getAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\t/**\n\t * Returns the value of an attribute associated to the authorization.\n\t * @param name the name of the attribute\n\t * @param <T> the type of the attribute\n\t * @return the value of an attribute associated to the authorization, or {@code null}\n\t * if not available\n\t */\n\t@Nullable\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T getAttribute(String name) {\n\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\treturn (T) this.attributes.get(name);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2Authorization that = (OAuth2Authorization) obj;\n\t\treturn Objects.equals(this.id, that.id) && Objects.equals(this.registeredClientId, that.registeredClientId)\n\t\t\t\t&& Objects.equals(this.principalName, that.principalName)\n\t\t\t\t&& Objects.equals(this.authorizationGrantType, that.authorizationGrantType)\n\t\t\t\t&& Objects.equals(this.authorizedScopes, that.authorizedScopes)\n\t\t\t\t&& Objects.equals(this.tokens, that.tokens) && Objects.equals(this.attributes, that.attributes);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.id, this.registeredClientId, this.principalName, this.authorizationGrantType,\n\t\t\t\tthis.authorizedScopes, this.tokens, this.attributes);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided\n\t * {@link RegisteredClient#getId()}.\n\t * @param registeredClient the {@link RegisteredClient}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withRegisteredClient(RegisteredClient registeredClient) {\n\t\tAssert.notNull(registeredClient, \"registeredClient cannot be null\");\n\t\treturn new Builder(registeredClient.getId());\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the values from the provided\n\t * {@code OAuth2Authorization}.\n\t * @param authorization the {@code OAuth2Authorization} used for initializing the\n\t * {@link Builder}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder from(OAuth2Authorization authorization) {\n\t\tAssert.notNull(authorization, \"authorization cannot be null\");\n\t\treturn new Builder(authorization.getRegisteredClientId()).id(authorization.getId())\n\t\t\t.principalName(authorization.getPrincipalName())\n\t\t\t.authorizationGrantType(authorization.getAuthorizationGrantType())\n\t\t\t.authorizedScopes(authorization.getAuthorizedScopes())\n\t\t\t.tokens(authorization.tokens)\n\t\t\t.attributes((attrs) -> attrs.putAll(authorization.getAttributes()));\n\t}\n\n\t/**\n\t * A holder of an OAuth 2.0 Token and it's associated metadata.\n\t *\n\t * @param <T> the type of the {@link OAuth2Token}\n\t * @author Joe Grandja\n\t */\n\tpublic static class Token<T extends OAuth2Token> implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = -5931125502413497522L;\n\n\t\tprotected static final String TOKEN_METADATA_NAMESPACE = \"metadata.token.\";\n\n\t\t/**\n\t\t * The name of the metadata that indicates if the token has been invalidated.\n\t\t */\n\t\tpublic static final String INVALIDATED_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat(\"invalidated\");\n\n\t\t/**\n\t\t * The name of the metadata used for the claims of the token.\n\t\t */\n\t\tpublic static final String CLAIMS_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat(\"claims\");\n\n\t\tprivate final T token;\n\n\t\tprivate final Map<String, Object> metadata;\n\n\t\tprotected Token(T token) {\n\t\t\tthis(token, defaultMetadata());\n\t\t}\n\n\t\tprotected Token(T token, Map<String, Object> metadata) {\n\t\t\tthis.token = token;\n\t\t\tthis.metadata = Collections.unmodifiableMap(metadata);\n\t\t}\n\n\t\t/**\n\t\t * Returns the token of type {@link OAuth2Token}.\n\t\t * @return the token of type {@link OAuth2Token}\n\t\t */\n\t\tpublic T getToken() {\n\t\t\treturn this.token;\n\t\t}\n\n\t\t/**\n\t\t * Returns {@code true} if the token has been invalidated (e.g. revoked). The\n\t\t * default is {@code false}.\n\t\t * @return {@code true} if the token has been invalidated, {@code false} otherwise\n\t\t */\n\t\tpublic boolean isInvalidated() {\n\t\t\treturn Boolean.TRUE.equals(getMetadata(INVALIDATED_METADATA_NAME));\n\t\t}\n\n\t\t/**\n\t\t * Returns {@code true} if the token has expired.\n\t\t * @return {@code true} if the token has expired, {@code false} otherwise\n\t\t */\n\t\tpublic boolean isExpired() {\n\t\t\treturn getToken().getExpiresAt() != null && Instant.now().isAfter(getToken().getExpiresAt());\n\t\t}\n\n\t\t/**\n\t\t * Returns {@code true} if the token is before the time it can be used.\n\t\t * @return {@code true} if the token is before the time it can be used,\n\t\t * {@code false} otherwise\n\t\t */\n\t\tpublic boolean isBeforeUse() {\n\t\t\tInstant notBefore = null;\n\t\t\tif (!CollectionUtils.isEmpty(getClaims())) {\n\t\t\t\tnotBefore = (Instant) getClaims().get(\"nbf\");\n\t\t\t}\n\t\t\treturn notBefore != null && Instant.now().isBefore(notBefore);\n\t\t}\n\n\t\t/**\n\t\t * Returns {@code true} if the token is currently active.\n\t\t * @return {@code true} if the token is currently active, {@code false} otherwise\n\t\t */\n\t\tpublic boolean isActive() {\n\t\t\treturn !isInvalidated() && !isExpired() && !isBeforeUse();\n\t\t}\n\n\t\t/**\n\t\t * Returns the claims associated to the token.\n\t\t * @return a {@code Map} of the claims, or {@code null} if not available\n\t\t */\n\t\t@Nullable\n\t\tpublic Map<String, Object> getClaims() {\n\t\t\treturn getMetadata(CLAIMS_METADATA_NAME);\n\t\t}\n\n\t\t/**\n\t\t * Returns the value of the metadata associated to the token.\n\t\t * @param name the name of the metadata\n\t\t * @param <V> the value type of the metadata\n\t\t * @return the value of the metadata, or {@code null} if not available\n\t\t */\n\t\t@Nullable\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic <V> V getMetadata(String name) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\treturn (V) this.metadata.get(name);\n\t\t}\n\n\t\t/**\n\t\t * Returns the metadata associated to the token.\n\t\t * @return a {@code Map} of the metadata\n\t\t */\n\t\tpublic Map<String, Object> getMetadata() {\n\t\t\treturn this.metadata;\n\t\t}\n\n\t\tprotected static Map<String, Object> defaultMetadata() {\n\t\t\tMap<String, Object> metadata = new HashMap<>();\n\t\t\tmetadata.put(INVALIDATED_METADATA_NAME, false);\n\t\t\treturn metadata;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (this == obj) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tToken<?> that = (Token<?>) obj;\n\t\t\treturn Objects.equals(this.token, that.token) && Objects.equals(this.metadata, that.metadata);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(this.token, this.metadata);\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2Authorization}.\n\t */\n\tpublic static class Builder {\n\n\t\tprivate String id;\n\n\t\tprivate final String registeredClientId;\n\n\t\tprivate String principalName;\n\n\t\tprivate AuthorizationGrantType authorizationGrantType;\n\n\t\tprivate Set<String> authorizedScopes;\n\n\t\tprivate Map<Class<? extends OAuth2Token>, Token<?>> tokens = new HashMap<>();\n\n\t\tprivate final Map<String, Object> attributes = new HashMap<>();\n\n\t\tprotected Builder(String registeredClientId) {\n\t\t\tthis.registeredClientId = registeredClientId;\n\t\t}\n\n\t\t/**\n\t\t * Sets the identifier for the authorization.\n\t\t * @param id the identifier for the authorization\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder id(String id) {\n\t\t\tthis.id = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code Principal} name of the resource owner (or client).\n\t\t * @param principalName the {@code Principal} name of the resource owner (or\n\t\t * client)\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder principalName(String principalName) {\n\t\t\tthis.principalName = principalName;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link AuthorizationGrantType authorization grant type} used for the\n\t\t * authorization.\n\t\t * @param authorizationGrantType the {@link AuthorizationGrantType}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder authorizationGrantType(AuthorizationGrantType authorizationGrantType) {\n\t\t\tthis.authorizationGrantType = authorizationGrantType;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the authorized scope(s).\n\t\t * @param authorizedScopes the {@code Set} of authorized scope(s)\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder authorizedScopes(Set<String> authorizedScopes) {\n\t\t\tthis.authorizedScopes = authorizedScopes;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AccessToken access token}.\n\t\t * @param accessToken the {@link OAuth2AccessToken}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder accessToken(OAuth2AccessToken accessToken) {\n\t\t\treturn token(accessToken);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2RefreshToken refresh token}.\n\t\t * @param refreshToken the {@link OAuth2RefreshToken}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder refreshToken(OAuth2RefreshToken refreshToken) {\n\t\t\treturn token(refreshToken);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2Token token}.\n\t\t * @param token the token\n\t\t * @param <T> the type of the token\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic <T extends OAuth2Token> Builder token(T token) {\n\t\t\treturn token(token, (metadata) -> {\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2Token token} and associated metadata.\n\t\t * @param token the token\n\t\t * @param metadataConsumer a {@code Consumer} of the metadata {@code Map}\n\t\t * @param <T> the type of the token\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic <T extends OAuth2Token> Builder token(T token, Consumer<Map<String, Object>> metadataConsumer) {\n\t\t\tAssert.notNull(token, \"token cannot be null\");\n\t\t\tMap<String, Object> metadata = Token.defaultMetadata();\n\t\t\tToken<?> existingToken = this.tokens.get(token.getClass());\n\t\t\tif (existingToken != null) {\n\t\t\t\tmetadata.putAll(existingToken.getMetadata());\n\t\t\t}\n\t\t\tmetadataConsumer.accept(metadata);\n\t\t\tClass<? extends OAuth2Token> tokenClass = token.getClass();\n\t\t\tthis.tokens.put(tokenClass, new Token<>(token, metadata));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Invalidates the {@link OAuth2Token token}.\n\t\t * @param token the token\n\t\t * @param <T> the type of the token\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic <T extends OAuth2Token> Builder invalidate(T token) {\n\t\t\tAssert.notNull(token, \"token cannot be null\");\n\t\t\tif (this.tokens.get(token.getClass()) == null) {\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\ttoken(token, (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true));\n\t\t\tif (OAuth2RefreshToken.class.isAssignableFrom(token.getClass())) {\n\t\t\t\tToken<?> accessToken = this.tokens.get(OAuth2AccessToken.class);\n\t\t\t\ttoken(accessToken.getToken(),\n\t\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true));\n\n\t\t\t\tToken<?> authorizationCode = this.tokens.get(OAuth2AuthorizationCode.class);\n\t\t\t\tif (authorizationCode != null && !authorizationCode.isInvalidated()) {\n\t\t\t\t\ttoken(authorizationCode.getToken(),\n\t\t\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tprotected final Builder tokens(Map<Class<? extends OAuth2Token>, Token<?>> tokens) {\n\t\t\tthis.tokens = new HashMap<>(tokens);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds an attribute associated to the authorization.\n\t\t * @param name the name of the attribute\n\t\t * @param value the value of the attribute\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder attribute(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.attributes.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the attributes {@code Map} allowing the ability to add,\n\t\t * replace, or remove.\n\t\t * @param attributesConsumer a {@link Consumer} of the attributes {@code Map}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder attributes(Consumer<Map<String, Object>> attributesConsumer) {\n\t\t\tattributesConsumer.accept(this.attributes);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2Authorization}.\n\t\t * @return the {@link OAuth2Authorization}\n\t\t */\n\t\tpublic OAuth2Authorization build() {\n\t\t\tAssert.hasText(this.principalName, \"principalName cannot be empty\");\n\t\t\tAssert.notNull(this.authorizationGrantType, \"authorizationGrantType cannot be null\");\n\n\t\t\tOAuth2Authorization authorization = new OAuth2Authorization();\n\t\t\tif (!StringUtils.hasText(this.id)) {\n\t\t\t\tthis.id = UUID.randomUUID().toString();\n\t\t\t}\n\t\t\tauthorization.id = this.id;\n\t\t\tauthorization.registeredClientId = this.registeredClientId;\n\t\t\tauthorization.principalName = this.principalName;\n\t\t\tauthorization.authorizationGrantType = this.authorizationGrantType;\n\t\t\tauthorization.authorizedScopes = Collections.unmodifiableSet(!CollectionUtils.isEmpty(this.authorizedScopes)\n\t\t\t\t\t? new HashSet<>(this.authorizedScopes) : new HashSet<>());\n\t\t\tauthorization.tokens = Collections.unmodifiableMap(this.tokens);\n\t\t\tauthorization.attributes = Collections.unmodifiableMap(this.attributes);\n\t\t\treturn authorization;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationCode.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.time.Instant;\n\nimport org.springframework.security.oauth2.core.AbstractOAuth2Token;\n\n/**\n * An implementation of an {@link AbstractOAuth2Token} representing an OAuth 2.0\n * Authorization Code Grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractOAuth2Token\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant</a>\n */\npublic class OAuth2AuthorizationCode extends AbstractOAuth2Token {\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCode} using the provided parameters.\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the token was issued\n\t * @param expiresAt the time at which the token expires\n\t */\n\tpublic OAuth2AuthorizationCode(String tokenValue, Instant issuedAt, Instant expiresAt) {\n\t\tsuper(tokenValue, issuedAt, expiresAt);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.NonNull;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A representation of an OAuth 2.0 \"consent\" to an Authorization request, which holds\n * state related to the set of {@link #getAuthorities() authorities} granted to a\n * {@link #getRegisteredClientId() client} by the {@link #getPrincipalName() resource\n * owner}.\n * <p>\n * When authorizing access for a given client, the resource owner may only grant a subset\n * of the authorities the client requested. The typical use-case is the\n * {@code authorization_code} flow, in which the client requests a set of {@code scope}s.\n * The resource owner then selects which scopes they grant to the client.\n *\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n */\npublic final class OAuth2AuthorizationConsent implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1950648027021276018L;\n\n\tprivate static final String AUTHORITIES_SCOPE_PREFIX = \"SCOPE_\";\n\n\tprivate final String registeredClientId;\n\n\tprivate final String principalName;\n\n\tprivate final Set<GrantedAuthority> authorities;\n\n\tprivate OAuth2AuthorizationConsent(String registeredClientId, String principalName,\n\t\t\tSet<GrantedAuthority> authorities) {\n\t\tthis.registeredClientId = registeredClientId;\n\t\tthis.principalName = principalName;\n\t\tthis.authorities = Collections.unmodifiableSet(authorities);\n\t}\n\n\t/**\n\t * Returns the identifier for the {@link RegisteredClient#getId() registered client}.\n\t * @return the {@link RegisteredClient#getId()}\n\t */\n\tpublic String getRegisteredClientId() {\n\t\treturn this.registeredClientId;\n\t}\n\n\t/**\n\t * Returns the {@code Principal} name of the resource owner (or client).\n\t * @return the {@code Principal} name of the resource owner (or client)\n\t */\n\tpublic String getPrincipalName() {\n\t\treturn this.principalName;\n\t}\n\n\t/**\n\t * Returns the {@link GrantedAuthority authorities} granted to the client by the\n\t * principal.\n\t * @return the {@link GrantedAuthority authorities} granted to the client by the\n\t * principal.\n\t */\n\tpublic Set<GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t/**\n\t * Convenience method for obtaining the {@code scope}s granted to the client by the\n\t * principal, extracted from the {@link #getAuthorities() authorities}.\n\t * @return the {@code scope}s granted to the client by the principal.\n\t */\n\tpublic Set<String> getScopes() {\n\t\tSet<String> authorities = new HashSet<>();\n\t\tfor (GrantedAuthority authority : getAuthorities()) {\n\t\t\tif (authority.getAuthority().startsWith(AUTHORITIES_SCOPE_PREFIX)) {\n\t\t\t\tauthorities.add(authority.getAuthority().substring(AUTHORITIES_SCOPE_PREFIX.length()));\n\t\t\t}\n\t\t}\n\t\treturn authorities;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2AuthorizationConsent that = (OAuth2AuthorizationConsent) obj;\n\t\treturn Objects.equals(this.registeredClientId, that.registeredClientId)\n\t\t\t\t&& Objects.equals(this.principalName, that.principalName)\n\t\t\t\t&& Objects.equals(this.authorities, that.authorities);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.registeredClientId, this.principalName, this.authorities);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the values from the provided\n\t * {@code OAuth2AuthorizationConsent}.\n\t * @param authorizationConsent the {@code OAuth2AuthorizationConsent} used for\n\t * initializing the {@link Builder}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder from(OAuth2AuthorizationConsent authorizationConsent) {\n\t\tAssert.notNull(authorizationConsent, \"authorizationConsent cannot be null\");\n\t\treturn new Builder(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName(),\n\t\t\t\tauthorizationConsent.getAuthorities());\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the given\n\t * {@link RegisteredClient#getClientId() registeredClientId} and {@code Principal}\n\t * name.\n\t * @param registeredClientId the {@link RegisteredClient#getId()}\n\t * @param principalName the {@code Principal} name\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withId(@NonNull String registeredClientId, @NonNull String principalName) {\n\t\tAssert.hasText(registeredClientId, \"registeredClientId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\treturn new Builder(registeredClientId, principalName);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AuthorizationConsent}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final String registeredClientId;\n\n\t\tprivate final String principalName;\n\n\t\tprivate final Set<GrantedAuthority> authorities = new HashSet<>();\n\n\t\tprivate Builder(String registeredClientId, String principalName) {\n\t\t\tthis(registeredClientId, principalName, Collections.emptySet());\n\t\t}\n\n\t\tprivate Builder(String registeredClientId, String principalName, Set<GrantedAuthority> authorities) {\n\t\t\tthis.registeredClientId = registeredClientId;\n\t\t\tthis.principalName = principalName;\n\t\t\tif (!CollectionUtils.isEmpty(authorities)) {\n\t\t\t\tthis.authorities.addAll(authorities);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Adds a scope to the collection of {@code authorities} in the resulting\n\t\t * {@link OAuth2AuthorizationConsent}, wrapping it in a\n\t\t * {@link SimpleGrantedAuthority}, prefixed by {@code SCOPE_}. For example, a\n\t\t * {@code message.write} scope would be stored as {@code SCOPE_message.write}.\n\t\t * @param scope the scope\n\t\t * @return the {@code Builder} for further configuration\n\t\t */\n\t\tpublic Builder scope(String scope) {\n\t\t\tauthority(new SimpleGrantedAuthority(AUTHORITIES_SCOPE_PREFIX + scope));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds a {@link GrantedAuthority} to the collection of {@code authorities} in the\n\t\t * resulting {@link OAuth2AuthorizationConsent}.\n\t\t * @param authority the {@link GrantedAuthority}\n\t\t * @return the {@code Builder} for further configuration\n\t\t */\n\t\tpublic Builder authority(GrantedAuthority authority) {\n\t\t\tthis.authorities.add(authority);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the {@code authorities}, allowing the ability to add,\n\t\t * replace or remove.\n\t\t * @param authoritiesConsumer a {@code Consumer} of the {@code authorities}\n\t\t * @return the {@code Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorities(Consumer<Set<GrantedAuthority>> authoritiesConsumer) {\n\t\t\tauthoritiesConsumer.accept(this.authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Validate the authorities and build the {@link OAuth2AuthorizationConsent}.\n\t\t * There must be at least one {@link GrantedAuthority}.\n\t\t * @return the {@link OAuth2AuthorizationConsent}\n\t\t */\n\t\tpublic OAuth2AuthorizationConsent build() {\n\t\t\tAssert.notEmpty(this.authorities, \"authorities cannot be empty\");\n\t\t\treturn new OAuth2AuthorizationConsent(this.registeredClientId, this.principalName, this.authorities);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.security.Principal;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\n\n/**\n * Implementations of this interface are responsible for the management of\n * {@link OAuth2AuthorizationConsent OAuth 2.0 Authorization Consent(s)}.\n *\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OAuth2AuthorizationConsent\n */\npublic interface OAuth2AuthorizationConsentService {\n\n\t/**\n\t * Saves the {@link OAuth2AuthorizationConsent}.\n\t * @param authorizationConsent the {@link OAuth2AuthorizationConsent}\n\t */\n\tvoid save(OAuth2AuthorizationConsent authorizationConsent);\n\n\t/**\n\t * Removes the {@link OAuth2AuthorizationConsent}.\n\t * @param authorizationConsent the {@link OAuth2AuthorizationConsent}\n\t */\n\tvoid remove(OAuth2AuthorizationConsent authorizationConsent);\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationConsent} identified by the provided\n\t * {@code registeredClientId} and {@code principalName}, or {@code null} if not found.\n\t * @param registeredClientId the identifier for the {@link RegisteredClient}\n\t * @param principalName the name of the {@link Principal}\n\t * @return the {@link OAuth2AuthorizationConsent} if found, otherwise {@code null}\n\t */\n\t@Nullable\n\tOAuth2AuthorizationConsent findById(String registeredClientId, String principalName);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadata.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.io.Serial;\nimport java.util.Map;\n\nimport org.springframework.util.Assert;\n\n/**\n * A representation of an OAuth 2.0 Authorization Server Metadata response, which is\n * returned from an OAuth 2.0 Authorization Server's Metadata Endpoint, and contains a set\n * of claims about the Authorization Server's configuration. The claims are defined by the\n * OAuth 2.0 Authorization Server Metadata specification (RFC 8414).\n *\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see AbstractOAuth2AuthorizationServerMetadata\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8414#section-3.2\">3.2.\n * Authorization Server Metadata Response</a>\n */\npublic final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2AuthorizationServerMetadata {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 3993358339217009284L;\n\n\tprivate OAuth2AuthorizationServerMetadata(Map<String, Object> claims) {\n\t\tsuper(claims);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with empty claims.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided claims.\n\t * @param claims the claims to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withClaims(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\treturn new Builder().claims((c) -> c.putAll(claims));\n\t}\n\n\t/**\n\t * Helps configure an {@link OAuth2AuthorizationServerMetadata}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OAuth2AuthorizationServerMetadata, Builder> {\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Validate the claims and build the {@link OAuth2AuthorizationServerMetadata}.\n\t\t * <p>\n\t\t * The following claims are REQUIRED: {@code issuer},\n\t\t * {@code authorization_endpoint}, {@code token_endpoint} and\n\t\t * {@code response_types_supported}.\n\t\t * @return the {@link OAuth2AuthorizationServerMetadata}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2AuthorizationServerMetadata build() {\n\t\t\tvalidate();\n\t\t\treturn new OAuth2AuthorizationServerMetadata(getClaims());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.net.URL;\nimport java.util.List;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\n\n/**\n * A {@link ClaimAccessor} for the \"claims\" an Authorization Server describes about its\n * configuration, used in OAuth 2.0 Authorization Server Metadata and OpenID Connect\n * Discovery 1.0.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see ClaimAccessor\n * @see OAuth2AuthorizationServerMetadataClaimNames\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8414#section-2\">2.\n * Authorization Server Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata\">3. OpenID\n * Provider Metadata</a>\n * @see <a target=\"_blank\" href=\"https://www.rfc-editor.org/rfc/rfc8628.html#section-4\">4.\n * Device Authorization Grant Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8705#section-3.3\">3.3 Mutual-TLS Client\n * Certificate-Bound Access Tokens Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9449#section-5.1\">5.1 OAuth 2.0 Demonstrating\n * Proof of Possession (DPoP) Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9126#name-authorization-server-metada\">5.\n * OAuth 2.0 Pushed Authorization Requests Metadata</a>\n */\npublic interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor {\n\n\t/**\n\t * Returns the {@code URL} the Authorization Server asserts as its Issuer Identifier\n\t * {@code (issuer)}.\n\t * @return the {@code URL} the Authorization Server asserts as its Issuer Identifier\n\t */\n\tdefault URL getIssuer() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.ISSUER);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OAuth 2.0 Authorization Endpoint\n\t * {@code (authorization_endpoint)}.\n\t * @return the {@code URL} of the OAuth 2.0 Authorization Endpoint\n\t */\n\tdefault URL getAuthorizationEndpoint() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OAuth 2.0 Pushed Authorization Request Endpoint\n\t * {@code (pushed_authorization_request_endpoint)}.\n\t * @return the {@code URL} of the OAuth 2.0 Pushed Authorization Request Endpoint\n\t */\n\tdefault URL getPushedAuthorizationRequestEndpoint() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OAuth 2.0 Device Authorization Endpoint\n\t * {@code (device_authorization_endpoint)}.\n\t * @return the {@code URL} of the OAuth 2.0 Device Authorization Endpoint\n\t */\n\tdefault URL getDeviceAuthorizationEndpoint() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OAuth 2.0 Token Endpoint {@code (token_endpoint)}.\n\t * @return the {@code URL} of the OAuth 2.0 Token Endpoint\n\t */\n\tdefault URL getTokenEndpoint() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the client authentication methods supported by the OAuth 2.0 Token Endpoint\n\t * {@code (token_endpoint_auth_methods_supported)}.\n\t * @return the client authentication methods supported by the OAuth 2.0 Token Endpoint\n\t */\n\tdefault List<String> getTokenEndpointAuthenticationMethods() {\n\t\treturn getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the JSON Web Key Set {@code (jwks_uri)}.\n\t * @return the {@code URL} of the JSON Web Key Set\n\t */\n\tdefault URL getJwkSetUrl() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 {@code scope} values supported {@code (scopes_supported)}.\n\t * @return the OAuth 2.0 {@code scope} values supported\n\t */\n\tdefault List<String> getScopes() {\n\t\treturn getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 {@code response_type} values supported\n\t * {@code (response_types_supported)}.\n\t * @return the OAuth 2.0 {@code response_type} values supported\n\t */\n\tdefault List<String> getResponseTypes() {\n\t\treturn getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 {@code grant_type} values supported\n\t * {@code (grant_types_supported)}.\n\t * @return the OAuth 2.0 {@code grant_type} values supported\n\t */\n\tdefault List<String> getGrantTypes() {\n\t\treturn getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OAuth 2.0 Token Revocation Endpoint\n\t * {@code (revocation_endpoint)}.\n\t * @return the {@code URL} of the OAuth 2.0 Token Revocation Endpoint\n\t */\n\tdefault URL getTokenRevocationEndpoint() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the client authentication methods supported by the OAuth 2.0 Token\n\t * Revocation Endpoint {@code (revocation_endpoint_auth_methods_supported)}.\n\t * @return the client authentication methods supported by the OAuth 2.0 Token\n\t * Revocation Endpoint\n\t */\n\tdefault List<String> getTokenRevocationEndpointAuthenticationMethods() {\n\t\treturn getClaimAsStringList(\n\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OAuth 2.0 Token Introspection Endpoint\n\t * {@code (introspection_endpoint)}.\n\t * @return the {@code URL} of the OAuth 2.0 Token Introspection Endpoint\n\t */\n\tdefault URL getTokenIntrospectionEndpoint() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the client authentication methods supported by the OAuth 2.0 Token\n\t * Introspection Endpoint {@code (introspection_endpoint_auth_methods_supported)}.\n\t * @return the client authentication methods supported by the OAuth 2.0 Token\n\t * Introspection Endpoint\n\t */\n\tdefault List<String> getTokenIntrospectionEndpointAuthenticationMethods() {\n\t\treturn getClaimAsStringList(\n\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint\n\t * {@code (registration_endpoint)}.\n\t * @return the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint\n\t */\n\tdefault URL getClientRegistrationEndpoint() {\n\t\treturn getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values\n\t * supported {@code (code_challenge_methods_supported)}.\n\t * @return the {@code code_challenge_method} values supported\n\t */\n\tdefault List<String> getCodeChallengeMethods() {\n\t\treturn getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns {@code true} to indicate support for mutual-TLS client certificate-bound\n\t * access tokens {@code (tls_client_certificate_bound_access_tokens)}.\n\t * @return {@code true} to indicate support for mutual-TLS client certificate-bound\n\t * access tokens, {@code false} otherwise\n\t */\n\tdefault boolean isTlsClientCertificateBoundAccessTokens() {\n\t\treturn Boolean.TRUE.equals(getClaimAsBoolean(\n\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS));\n\t}\n\n\t/**\n\t * Returns the {@link JwsAlgorithms JSON Web Signature (JWS) algorithms} supported for\n\t * DPoP Proof JWTs {@code (dpop_signing_alg_values_supported)}.\n\t * @return the {@link JwsAlgorithms JSON Web Signature (JWS) algorithms} supported for\n\t * DPoP Proof JWTs\n\t */\n\tdefault List<String> getDPoPSigningAlgorithms() {\n\t\treturn getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\n\n/**\n * The names of the \"claims\" an Authorization Server describes about its configuration,\n * used in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8414#section-2\">2.\n * Authorization Server Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata\">3. OpenID\n * Provider Metadata</a>\n * @see <a target=\"_blank\" href=\"https://www.rfc-editor.org/rfc/rfc8628.html#section-4\">4.\n * Device Authorization Grant Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8705#section-3.3\">3.3 Mutual-TLS Client\n * Certificate-Bound Access Tokens Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9449#section-5.1\">5.1 OAuth 2.0 Demonstrating\n * Proof of Possession (DPoP) Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9126#name-authorization-server-metada\">5.\n * OAuth 2.0 Pushed Authorization Requests Metadata</a>\n */\npublic class OAuth2AuthorizationServerMetadataClaimNames {\n\n\t/**\n\t * {@code issuer} - the {@code URL} the Authorization Server asserts as its Issuer\n\t * Identifier\n\t */\n\tpublic static final String ISSUER = \"issuer\";\n\n\t/**\n\t * {@code authorization_endpoint} - the {@code URL} of the OAuth 2.0 Authorization\n\t * Endpoint\n\t */\n\tpublic static final String AUTHORIZATION_ENDPOINT = \"authorization_endpoint\";\n\n\t/**\n\t * {@code pushed_authorization_request_endpoint} - the {@code URL} of the OAuth 2.0\n\t * Pushed Authorization Request Endpoint\n\t */\n\tpublic static final String PUSHED_AUTHORIZATION_REQUEST_ENDPOINT = \"pushed_authorization_request_endpoint\";\n\n\t/**\n\t * {@code device_authorization_endpoint} - the {@code URL} of the OAuth 2.0 Device\n\t * Authorization Endpoint\n\t */\n\tpublic static final String DEVICE_AUTHORIZATION_ENDPOINT = \"device_authorization_endpoint\";\n\n\t/**\n\t * {@code token_endpoint} - the {@code URL} of the OAuth 2.0 Token Endpoint\n\t */\n\tpublic static final String TOKEN_ENDPOINT = \"token_endpoint\";\n\n\t/**\n\t * {@code token_endpoint_auth_methods_supported} - the client authentication methods\n\t * supported by the OAuth 2.0 Token Endpoint\n\t */\n\tpublic static final String TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED = \"token_endpoint_auth_methods_supported\";\n\n\t/**\n\t * {@code jwks_uri} - the {@code URL} of the JSON Web Key Set\n\t */\n\tpublic static final String JWKS_URI = \"jwks_uri\";\n\n\t/**\n\t * {@code scopes_supported} - the OAuth 2.0 {@code scope} values supported\n\t */\n\tpublic static final String SCOPES_SUPPORTED = \"scopes_supported\";\n\n\t/**\n\t * {@code response_types_supported} - the OAuth 2.0 {@code response_type} values\n\t * supported\n\t */\n\tpublic static final String RESPONSE_TYPES_SUPPORTED = \"response_types_supported\";\n\n\t/**\n\t * {@code grant_types_supported} - the OAuth 2.0 {@code grant_type} values supported\n\t */\n\tpublic static final String GRANT_TYPES_SUPPORTED = \"grant_types_supported\";\n\n\t/**\n\t * {@code revocation_endpoint} - the {@code URL} of the OAuth 2.0 Token Revocation\n\t * Endpoint\n\t */\n\tpublic static final String REVOCATION_ENDPOINT = \"revocation_endpoint\";\n\n\t/**\n\t * {@code revocation_endpoint_auth_methods_supported} - the client authentication\n\t * methods supported by the OAuth 2.0 Token Revocation Endpoint\n\t */\n\tpublic static final String REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED = \"revocation_endpoint_auth_methods_supported\";\n\n\t/**\n\t * {@code introspection_endpoint} - the {@code URL} of the OAuth 2.0 Token\n\t * Introspection Endpoint\n\t */\n\tpublic static final String INTROSPECTION_ENDPOINT = \"introspection_endpoint\";\n\n\t/**\n\t * {@code introspection_endpoint_auth_methods_supported} - the client authentication\n\t * methods supported by the OAuth 2.0 Token Introspection Endpoint\n\t */\n\tpublic static final String INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED = \"introspection_endpoint_auth_methods_supported\";\n\n\t/**\n\t * {@code registration_endpoint} - the {@code URL} of the OAuth 2.0 Dynamic Client\n\t * Registration Endpoint\n\t */\n\tpublic static final String REGISTRATION_ENDPOINT = \"registration_endpoint\";\n\n\t/**\n\t * {@code code_challenge_methods_supported} - the Proof Key for Code Exchange (PKCE)\n\t * {@code code_challenge_method} values supported\n\t */\n\tpublic static final String CODE_CHALLENGE_METHODS_SUPPORTED = \"code_challenge_methods_supported\";\n\n\t/**\n\t * {@code tls_client_certificate_bound_access_tokens} - {@code true} to indicate\n\t * support for mutual-TLS client certificate-bound access tokens\n\t */\n\tpublic static final String TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS = \"tls_client_certificate_bound_access_tokens\";\n\n\t/**\n\t * {@code dpop_signing_alg_values_supported} - the {@link JwsAlgorithms JSON Web\n\t * Signature (JWS) algorithms} supported for DPoP Proof JWTs\n\t */\n\tpublic static final String DPOP_SIGNING_ALG_VALUES_SUPPORTED = \"dpop_signing_alg_values_supported\";\n\n\tprotected OAuth2AuthorizationServerMetadataClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport org.springframework.lang.Nullable;\n\n/**\n * Implementations of this interface are responsible for the management of\n * {@link OAuth2Authorization OAuth 2.0 Authorization(s)}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2Authorization\n * @see OAuth2TokenType\n */\npublic interface OAuth2AuthorizationService {\n\n\t/**\n\t * Saves the {@link OAuth2Authorization}.\n\t * @param authorization the {@link OAuth2Authorization}\n\t */\n\tvoid save(OAuth2Authorization authorization);\n\n\t/**\n\t * Removes the {@link OAuth2Authorization}.\n\t * @param authorization the {@link OAuth2Authorization}\n\t */\n\tvoid remove(OAuth2Authorization authorization);\n\n\t/**\n\t * Returns the {@link OAuth2Authorization} identified by the provided {@code id}, or\n\t * {@code null} if not found.\n\t * @param id the authorization identifier\n\t * @return the {@link OAuth2Authorization} if found, otherwise {@code null}\n\t */\n\t@Nullable\n\tOAuth2Authorization findById(String id);\n\n\t/**\n\t * Returns the {@link OAuth2Authorization} containing the provided {@code token}, or\n\t * {@code null} if not found.\n\t * @param token the token credential\n\t * @param tokenType the {@link OAuth2TokenType token type}\n\t * @return the {@link OAuth2Authorization} if found, otherwise {@code null}\n\t */\n\t@Nullable\n\tOAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2ClientMetadataClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.List;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\n\n/**\n * A {@link ClaimAccessor} for the claims that are contained in the OAuth 2.0 Client\n * Registration Request and Response.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see ClaimAccessor\n * @see OAuth2ClientMetadataClaimNames\n * @see OAuth2ClientRegistration\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc7591#section-2\">2. Client Metadata</a>\n */\npublic interface OAuth2ClientMetadataClaimAccessor extends ClaimAccessor {\n\n\t/**\n\t * Returns the Client Identifier {@code (client_id)}.\n\t * @return the Client Identifier\n\t */\n\tdefault String getClientId() {\n\t\treturn getClaimAsString(OAuth2ClientMetadataClaimNames.CLIENT_ID);\n\t}\n\n\t/**\n\t * Returns the time at which the Client Identifier was issued\n\t * {@code (client_id_issued_at)}.\n\t * @return the time at which the Client Identifier was issued\n\t */\n\tdefault Instant getClientIdIssuedAt() {\n\t\treturn getClaimAsInstant(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT);\n\t}\n\n\t/**\n\t * Returns the Client Secret {@code (client_secret)}.\n\t * @return the Client Secret\n\t */\n\tdefault String getClientSecret() {\n\t\treturn getClaimAsString(OAuth2ClientMetadataClaimNames.CLIENT_SECRET);\n\t}\n\n\t/**\n\t * Returns the time at which the {@code client_secret} will expire\n\t * {@code (client_secret_expires_at)}.\n\t * @return the time at which the {@code client_secret} will expire\n\t */\n\tdefault Instant getClientSecretExpiresAt() {\n\t\treturn getClaimAsInstant(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT);\n\t}\n\n\t/**\n\t * Returns the name of the Client to be presented to the End-User\n\t * {@code (client_name)}.\n\t * @return the name of the Client to be presented to the End-User\n\t */\n\tdefault String getClientName() {\n\t\treturn getClaimAsString(OAuth2ClientMetadataClaimNames.CLIENT_NAME);\n\t}\n\n\t/**\n\t * Returns the redirection {@code URI} values used by the Client\n\t * {@code (redirect_uris)}.\n\t * @return the redirection {@code URI} values used by the Client\n\t */\n\tdefault List<String> getRedirectUris() {\n\t\treturn getClaimAsStringList(OAuth2ClientMetadataClaimNames.REDIRECT_URIS);\n\t}\n\n\t/**\n\t * Returns the authentication method used by the Client for the Token Endpoint\n\t * {@code (token_endpoint_auth_method)}.\n\t * @return the authentication method used by the Client for the Token Endpoint\n\t */\n\tdefault String getTokenEndpointAuthenticationMethod() {\n\t\treturn getClaimAsString(OAuth2ClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 {@code grant_type} values that the Client will restrict\n\t * itself to using {@code (grant_types)}.\n\t * @return the OAuth 2.0 {@code grant_type} values that the Client will restrict\n\t * itself to using\n\t */\n\tdefault List<String> getGrantTypes() {\n\t\treturn getClaimAsStringList(OAuth2ClientMetadataClaimNames.GRANT_TYPES);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 {@code response_type} values that the Client will restrict\n\t * itself to using {@code (response_types)}.\n\t * @return the OAuth 2.0 {@code response_type} values that the Client will restrict\n\t * itself to using\n\t */\n\tdefault List<String> getResponseTypes() {\n\t\treturn getClaimAsStringList(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 {@code scope} values that the Client will restrict itself to\n\t * using {@code (scope)}.\n\t * @return the OAuth 2.0 {@code scope} values that the Client will restrict itself to\n\t * using\n\t */\n\tdefault List<String> getScopes() {\n\t\treturn getClaimAsStringList(OAuth2ClientMetadataClaimNames.SCOPE);\n\t}\n\n\t/**\n\t * Returns the {@code URL} for the Client's JSON Web Key Set {@code (jwks_uri)}.\n\t * @return the {@code URL} for the Client's JSON Web Key Set {@code (jwks_uri)}\n\t */\n\tdefault URL getJwkSetUrl() {\n\t\treturn getClaimAsURL(OAuth2ClientMetadataClaimNames.JWKS_URI);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2ClientMetadataClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\n/**\n * The names of the claims defined by OAuth 2.0 Dynamic Client Registration Protocol that\n * are contained in the OAuth 2.0 Client Registration Request and Response.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc7591#section-2\">2. Client Metadata</a>\n */\npublic class OAuth2ClientMetadataClaimNames {\n\n\t/**\n\t * {@code client_id} - the Client Identifier\n\t */\n\tpublic static final String CLIENT_ID = \"client_id\";\n\n\t/**\n\t * {@code client_id_issued_at} - the time at which the Client Identifier was issued\n\t */\n\tpublic static final String CLIENT_ID_ISSUED_AT = \"client_id_issued_at\";\n\n\t/**\n\t * {@code client_secret} - the Client Secret\n\t */\n\tpublic static final String CLIENT_SECRET = \"client_secret\";\n\n\t/**\n\t * {@code client_secret_expires_at} - the time at which the {@code client_secret} will\n\t * expire or 0 if it will not expire\n\t */\n\tpublic static final String CLIENT_SECRET_EXPIRES_AT = \"client_secret_expires_at\";\n\n\t/**\n\t * {@code client_name} - the name of the Client to be presented to the End-User\n\t */\n\tpublic static final String CLIENT_NAME = \"client_name\";\n\n\t/**\n\t * {@code redirect_uris} - the redirection {@code URI} values used by the Client\n\t */\n\tpublic static final String REDIRECT_URIS = \"redirect_uris\";\n\n\t/**\n\t * {@code token_endpoint_auth_method} - the authentication method used by the Client\n\t * for the Token Endpoint\n\t */\n\tpublic static final String TOKEN_ENDPOINT_AUTH_METHOD = \"token_endpoint_auth_method\";\n\n\t/**\n\t * {@code grant_types} - the OAuth 2.0 {@code grant_type} values that the Client will\n\t * restrict itself to using\n\t */\n\tpublic static final String GRANT_TYPES = \"grant_types\";\n\n\t/**\n\t * {@code response_types} - the OAuth 2.0 {@code response_type} values that the Client\n\t * will restrict itself to using\n\t */\n\tpublic static final String RESPONSE_TYPES = \"response_types\";\n\n\t/**\n\t * {@code scope} - a space-separated list of OAuth 2.0 {@code scope} values that the\n\t * Client will restrict itself to using\n\t */\n\tpublic static final String SCOPE = \"scope\";\n\n\t/**\n\t * {@code jwks_uri} - the {@code URL} for the Client's JSON Web Key Set\n\t */\n\tpublic static final String JWKS_URI = \"jwks_uri\";\n\n\tprotected OAuth2ClientMetadataClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2ClientRegistration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.io.Serial;\nimport java.util.Map;\n\nimport org.springframework.util.Assert;\n\n/**\n * A representation of an OAuth 2.0 Client Registration Request and Response, which is\n * sent to and returned from the Client Registration Endpoint, and contains a set of\n * claims about the Client's Registration information. The claims are defined by the OAuth\n * 2.0 Dynamic Client Registration Protocol specification.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractOAuth2ClientRegistration\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc7591#section-3.1\">3.1. Client Registration\n * Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1\">3.2.1. Client\n * Registration Response</a>\n */\npublic final class OAuth2ClientRegistration extends AbstractOAuth2ClientRegistration {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 283805553286847831L;\n\n\tprivate OAuth2ClientRegistration(Map<String, Object> claims) {\n\t\tsuper(claims);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with empty claims.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided claims.\n\t * @param claims the claims to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withClaims(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\treturn new Builder().claims((c) -> c.putAll(claims));\n\t}\n\n\t/**\n\t * Helps configure an {@link OAuth2ClientRegistration}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OAuth2ClientRegistration, Builder> {\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Validate the claims and build the {@link OAuth2ClientRegistration}.\n\t\t * @return the {@link OAuth2ClientRegistration}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2ClientRegistration build() {\n\t\t\tvalidate();\n\t\t\treturn new OAuth2ClientRegistration(getClaims());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenIntrospection.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.net.URI;\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.util.Assert;\n\n/**\n * A representation of the claims returned in an OAuth 2.0 Token Introspection Response.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenIntrospectionClaimAccessor\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7662#section-2.2\">Section\n * 2.2 Introspection Response</a>\n */\npublic final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionClaimAccessor, Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -8846164058150912395L;\n\n\tprivate final Map<String, Object> claims;\n\n\tprivate OAuth2TokenIntrospection(Map<String, Object> claims) {\n\t\tthis.claims = Collections.unmodifiableMap(new LinkedHashMap<>(claims));\n\t}\n\n\t/**\n\t * Returns the claims in the Token Introspection Response.\n\t * @return a {@code Map} of the claims\n\t */\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} initialized with the {@link #isActive() active}\n\t * claim to {@code false}.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn builder(false);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} initialized with the provided {@link #isActive()\n\t * active} claim.\n\t * @param active {@code true} if the token is currently active, {@code false}\n\t * otherwise\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder(boolean active) {\n\t\treturn new Builder(active);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} initialized with the provided claims.\n\t * @param claims the claims to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withClaims(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\treturn builder().claims((c) -> c.putAll(claims));\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2TokenIntrospection}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final Map<String, Object> claims = new LinkedHashMap<>();\n\n\t\tprivate Builder(boolean active) {\n\t\t\tactive(active);\n\t\t}\n\n\t\t/**\n\t\t * Sets the indicator of whether or not the presented token is currently active,\n\t\t * REQUIRED.\n\t\t * @param active {@code true} if the token is currently active, {@code false}\n\t\t * otherwise\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder active(boolean active) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.ACTIVE, active);\n\t\t}\n\n\t\t/**\n\t\t * Add the scope associated with this token, OPTIONAL.\n\t\t * @param scope the scope associated with this token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder scope(String scope) {\n\t\t\taddClaimToClaimList(OAuth2TokenIntrospectionClaimNames.SCOPE, scope);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the scope(s) associated with this token, allowing the\n\t\t * ability to add, replace, or remove, OPTIONAL.\n\t\t * @param scopesConsumer a {@code Consumer} of the scope(s) associated with this\n\t\t * token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder scopes(Consumer<List<String>> scopesConsumer) {\n\t\t\tacceptClaimValues(OAuth2TokenIntrospectionClaimNames.SCOPE, scopesConsumer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the client identifier for the OAuth 2.0 client that requested this token,\n\t\t * OPTIONAL.\n\t\t * @param clientId the client identifier for the OAuth 2.0 client that requested\n\t\t * this token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder clientId(String clientId) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, clientId);\n\t\t}\n\n\t\t/**\n\t\t * Sets the human-readable identifier for the resource owner who authorized this\n\t\t * token, OPTIONAL.\n\t\t * @param username the human-readable identifier for the resource owner who\n\t\t * authorized this token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder username(String username) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.USERNAME, username);\n\t\t}\n\n\t\t/**\n\t\t * Sets the token type (e.g. bearer), OPTIONAL.\n\t\t * @param tokenType the token type\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder tokenType(String tokenType) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE, tokenType);\n\t\t}\n\n\t\t/**\n\t\t * Sets the time indicating when this token will expire, OPTIONAL.\n\t\t * @param expiresAt the time indicating when this token will expire\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder expiresAt(Instant expiresAt) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.EXP, expiresAt);\n\t\t}\n\n\t\t/**\n\t\t * Sets the time indicating when this token was originally issued, OPTIONAL.\n\t\t * @param issuedAt the time indicating when this token was originally issued\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder issuedAt(Instant issuedAt) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.IAT, issuedAt);\n\t\t}\n\n\t\t/**\n\t\t * Sets the time indicating when this token is not to be used before, OPTIONAL.\n\t\t * @param notBefore the time indicating when this token is not to be used before\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder notBefore(Instant notBefore) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.NBF, notBefore);\n\t\t}\n\n\t\t/**\n\t\t * Sets the subject of the token, usually a machine-readable identifier of the\n\t\t * resource owner who authorized this token, OPTIONAL.\n\t\t * @param subject the subject of the token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder subject(String subject) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.SUB, subject);\n\t\t}\n\n\t\t/**\n\t\t * Add the identifier representing the intended audience for this token, OPTIONAL.\n\t\t * @param audience the identifier representing the intended audience for this\n\t\t * token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder audience(String audience) {\n\t\t\taddClaimToClaimList(OAuth2TokenIntrospectionClaimNames.AUD, audience);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the intended audience(s) for this token, allowing the\n\t\t * ability to add, replace, or remove, OPTIONAL.\n\t\t * @param audiencesConsumer a {@code Consumer} of the intended audience(s) for\n\t\t * this token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder audiences(Consumer<List<String>> audiencesConsumer) {\n\t\t\tacceptClaimValues(OAuth2TokenIntrospectionClaimNames.AUD, audiencesConsumer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the issuer of this token, OPTIONAL.\n\t\t * @param issuer the issuer of this token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder issuer(String issuer) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.ISS, issuer);\n\t\t}\n\n\t\t/**\n\t\t * Sets the identifier for the token, OPTIONAL.\n\t\t * @param jti the identifier for the token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder id(String jti) {\n\t\t\treturn claim(OAuth2TokenIntrospectionClaimNames.JTI, jti);\n\t\t}\n\n\t\t/**\n\t\t * Sets the claim.\n\t\t * @param name the claim name\n\t\t * @param value the claim value\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder claim(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #claim(String, Object)} declared so far with\n\t\t * the possibility to add, replace, or remove.\n\t\t * @param claimsConsumer a {@code Consumer} of the claims\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Validate the claims and build the {@link OAuth2TokenIntrospection}.\n\t\t * <p>\n\t\t * The following claims are REQUIRED: {@code active}\n\t\t * @return the {@link OAuth2TokenIntrospection}\n\t\t */\n\t\tpublic OAuth2TokenIntrospection build() {\n\t\t\tvalidate();\n\t\t\treturn new OAuth2TokenIntrospection(this.claims);\n\t\t}\n\n\t\tprivate void validate() {\n\t\t\tAssert.notNull(this.claims.get(OAuth2TokenIntrospectionClaimNames.ACTIVE), \"active cannot be null\");\n\t\t\tAssert.isInstanceOf(Boolean.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.ACTIVE),\n\t\t\t\t\t\"active must be of type boolean\");\n\t\t\tif (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.SCOPE)) {\n\t\t\t\tAssert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.SCOPE),\n\t\t\t\t\t\t\"scope must be of type List\");\n\t\t\t}\n\t\t\tif (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.EXP)) {\n\t\t\t\tAssert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.EXP),\n\t\t\t\t\t\t\"exp must be of type Instant\");\n\t\t\t}\n\t\t\tif (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.IAT)) {\n\t\t\t\tAssert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.IAT),\n\t\t\t\t\t\t\"iat must be of type Instant\");\n\t\t\t}\n\t\t\tif (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.NBF)) {\n\t\t\t\tAssert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.NBF),\n\t\t\t\t\t\t\"nbf must be of type Instant\");\n\t\t\t}\n\t\t\tif (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.AUD)) {\n\t\t\t\tAssert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.AUD),\n\t\t\t\t\t\t\"aud must be of type List\");\n\t\t\t}\n\t\t\tif (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.ISS)) {\n\t\t\t\tvalidateURL(this.claims.get(OAuth2TokenIntrospectionClaimNames.ISS), \"iss must be a valid URL\");\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void addClaimToClaimList(String name, String value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\t((List<String>) this.claims.get(name)).add(value);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void acceptClaimValues(String name, Consumer<List<String>> valuesConsumer) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(valuesConsumer, \"valuesConsumer cannot be null\");\n\t\t\tthis.claims.computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\tList<String> values = (List<String>) this.claims.get(name);\n\t\t\tvaluesConsumer.accept(values);\n\t\t}\n\n\t\tprivate static void validateURL(Object url, String errorMessage) {\n\t\t\tif (URL.class.isAssignableFrom(url.getClass())) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnew URI(url.toString()).toURL();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(errorMessage, ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenType.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Standard token types defined in the OAuth Token Type Hints Registry.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7009#section-4.1.2\">4.1.2\n * OAuth Token Type Hints Registry</a>\n */\npublic final class OAuth2TokenType implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -9015673781220922768L;\n\n\t/**\n\t * {@code access_token} token type.\n\t */\n\tpublic static final OAuth2TokenType ACCESS_TOKEN = new OAuth2TokenType(\"access_token\");\n\n\t/**\n\t * {@code refresh_token} token type.\n\t */\n\tpublic static final OAuth2TokenType REFRESH_TOKEN = new OAuth2TokenType(\"refresh_token\");\n\n\tprivate final String value;\n\n\t/**\n\t * Constructs an {@code OAuth2TokenType} using the provided value.\n\t * @param value the value of the token type\n\t */\n\tpublic OAuth2TokenType(String value) {\n\t\tAssert.hasText(value, \"value cannot be empty\");\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Returns the value of the token type.\n\t * @return the value of the token type\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2TokenType that = (OAuth2TokenType) obj;\n\t\treturn getValue().equals(that.getValue());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn getValue().hashCode();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/aot/hint/OAuth2AuthorizationServerBeanRegistrationAotProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.aot.hint;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\n\nimport org.springframework.aot.generate.GenerationContext;\nimport org.springframework.aot.hint.BindingReflectionHintsRegistrar;\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.beans.factory.aot.BeanRegistrationAotContribution;\nimport org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;\nimport org.springframework.beans.factory.aot.BeanRegistrationCode;\nimport org.springframework.beans.factory.support.RegisteredBean;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.jackson.CoreJacksonModule;\nimport org.springframework.security.jackson2.CoreJackson2Module;\nimport org.springframework.security.oauth2.core.AbstractOAuth2Token;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule;\nimport org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.jackson.WebServletJacksonModule;\nimport org.springframework.security.web.jackson2.WebServletJackson2Module;\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\nimport org.springframework.util.ClassUtils;\n\n/**\n * {@link BeanRegistrationAotProcessor} that detects specific registered beans and\n * contributes the required {@link RuntimeHints}. Statically registered via\n * META-INF/spring/aot.factories.\n *\n * @author Joe Grandja\n * @author Josh Long\n * @author William Koch\n * @since 7.0\n */\nclass OAuth2AuthorizationServerBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {\n\n\tprivate static final boolean jackson2Present;\n\n\tprivate static final boolean jackson3Present;\n\n\tstatic {\n\t\tClassLoader classLoader = ClassUtils.getDefaultClassLoader();\n\t\tjackson2Present = ClassUtils.isPresent(\"com.fasterxml.jackson.databind.ObjectMapper\", classLoader)\n\t\t\t\t&& ClassUtils.isPresent(\"com.fasterxml.jackson.core.JsonGenerator\", classLoader);\n\t\tjackson3Present = ClassUtils.isPresent(\"tools.jackson.databind.json.JsonMapper\", classLoader);\n\t}\n\n\tprivate boolean jacksonContributed;\n\n\t@Override\n\tpublic BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {\n\t\tboolean isJdbcBasedOAuth2AuthorizationService = JdbcOAuth2AuthorizationService.class\n\t\t\t.isAssignableFrom(registeredBean.getBeanClass());\n\n\t\tboolean isJdbcBasedRegisteredClientRepository = JdbcRegisteredClientRepository.class\n\t\t\t.isAssignableFrom(registeredBean.getBeanClass());\n\n\t\t// @formatter:off\n\t\tif ((isJdbcBasedOAuth2AuthorizationService || isJdbcBasedRegisteredClientRepository)\n\t\t\t\t&& !this.jacksonContributed) {\n\t\t\tJacksonConfigurationBeanRegistrationAotContribution jacksonContribution =\n\t\t\t\t\tnew JacksonConfigurationBeanRegistrationAotContribution();\n\t\t\tthis.jacksonContributed = true;\n\t\t\treturn jacksonContribution;\n\t\t}\n\t\t// @formatter:on\n\t\treturn null;\n\t}\n\n\tprivate static class JacksonConfigurationBeanRegistrationAotContribution\n\t\t\timplements BeanRegistrationAotContribution {\n\n\t\tprivate final BindingReflectionHintsRegistrar reflectionHintsRegistrar = new BindingReflectionHintsRegistrar();\n\n\t\t@Override\n\t\tpublic void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {\n\t\t\tregisterHints(generationContext.getRuntimeHints());\n\t\t}\n\n\t\tprivate void registerHints(RuntimeHints hints) {\n\t\t\t// Collections -> UnmodifiableSet, UnmodifiableList, UnmodifiableMap,\n\t\t\t// UnmodifiableRandomAccessList, etc.\n\t\t\thints.reflection().registerType(Collections.class);\n\n\t\t\t// HashSet\n\t\t\thints.reflection()\n\t\t\t\t.registerType(HashSet.class, MemberCategory.ACCESS_DECLARED_FIELDS,\n\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS);\n\n\t\t\thints.reflection()\n\t\t\t\t.registerTypes(Arrays.asList(TypeReference.of(AbstractAuthenticationToken.class),\n\t\t\t\t\t\tTypeReference.of(DefaultSavedRequest.Builder.class),\n\t\t\t\t\t\tTypeReference.of(WebAuthenticationDetails.class),\n\t\t\t\t\t\tTypeReference.of(UsernamePasswordAuthenticationToken.class), TypeReference.of(User.class),\n\t\t\t\t\t\tTypeReference.of(DefaultOidcUser.class), TypeReference.of(DefaultOAuth2User.class),\n\t\t\t\t\t\tTypeReference.of(OidcUserAuthority.class), TypeReference.of(OAuth2UserAuthority.class),\n\t\t\t\t\t\tTypeReference.of(SimpleGrantedAuthority.class), TypeReference.of(OidcIdToken.class),\n\t\t\t\t\t\tTypeReference.of(AbstractOAuth2Token.class), TypeReference.of(OidcUserInfo.class),\n\t\t\t\t\t\tTypeReference.of(OAuth2TokenExchangeActor.class),\n\t\t\t\t\t\tTypeReference.of(OAuth2AuthorizationRequest.class),\n\t\t\t\t\t\tTypeReference.of(OAuth2TokenExchangeCompositeAuthenticationToken.class),\n\t\t\t\t\t\tTypeReference.of(AuthorizationGrantType.class),\n\t\t\t\t\t\tTypeReference.of(OAuth2AuthorizationResponseType.class),\n\t\t\t\t\t\tTypeReference.of(OAuth2TokenFormat.class)),\n\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.ACCESS_DECLARED_FIELDS,\n\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));\n\n\t\t\t// Jackson Modules\n\t\t\tif (jackson2Present) {\n\t\t\t\tregisterJackson2Modules(hints);\n\t\t\t}\n\t\t\tif (jackson3Present) {\n\t\t\t\thints.reflection()\n\t\t\t\t\t.registerTypes(\n\t\t\t\t\t\t\tArrays.asList(TypeReference.of(CoreJacksonModule.class),\n\t\t\t\t\t\t\t\t\tTypeReference.of(WebServletJacksonModule.class),\n\t\t\t\t\t\t\t\t\tTypeReference.of(OAuth2AuthorizationServerJacksonModule.class)),\n\t\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.ACCESS_DECLARED_FIELDS,\n\t\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS));\n\t\t\t}\n\n\t\t\t// Jackson Mixins\n\t\t\tif (jackson2Present) {\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson2.UnmodifiableSetMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson2.UnmodifiableListMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson2.UnmodifiableMapMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson2.UnmodifiableMapMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.server.authorization.jackson2.HashSetMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.web.jackson2.DefaultSavedRequestMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.web.jackson2.WebAuthenticationDetailsMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson2.UsernamePasswordAuthenticationTokenMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson2.UserMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson2.SimpleGrantedAuthorityMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenExchangeActorMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationRequestMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenExchangeCompositeAuthenticationTokenMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson2.OAuth2TokenFormatMixin\"));\n\t\t\t}\n\t\t\tif (jackson3Present) {\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.web.jackson.DefaultSavedRequestMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.web.jackson.WebAuthenticationDetailsMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson.UsernamePasswordAuthenticationTokenMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson.UserMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\tloadClass(\"org.springframework.security.jackson.SimpleGrantedAuthorityMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenExchangeActorMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationRequestMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenExchangeCompositeAuthenticationTokenMixin\"));\n\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\"org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenFormatMixin\"));\n\t\t\t}\n\n\t\t\t// Check if OAuth2 Client is on classpath\n\t\t\tif (ClassUtils.isPresent(\"org.springframework.security.oauth2.client.registration.ClientRegistration\",\n\t\t\t\t\tClassUtils.getDefaultClassLoader())) {\n\n\t\t\t\thints.reflection()\n\t\t\t\t\t.registerType(TypeReference\n\t\t\t\t\t\t.of(\"org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken\"),\n\t\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.ACCESS_DECLARED_FIELDS,\n\t\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS));\n\n\t\t\t\t// Jackson Module\n\t\t\t\tif (jackson2Present) {\n\t\t\t\t\thints.reflection()\n\t\t\t\t\t\t.registerType(TypeReference\n\t\t\t\t\t\t\t.of(\"org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module\"),\n\t\t\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.ACCESS_DECLARED_FIELDS,\n\t\t\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS));\n\t\t\t\t}\n\t\t\t\tif (jackson3Present) {\n\t\t\t\t\thints.reflection()\n\t\t\t\t\t\t.registerType(\n\t\t\t\t\t\t\t\tTypeReference\n\t\t\t\t\t\t\t\t\t.of(\"org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule\"),\n\t\t\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.ACCESS_DECLARED_FIELDS,\n\t\t\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS));\n\t\t\t\t}\n\n\t\t\t\t// Jackson Mixins\n\t\t\t\tif (jackson2Present) {\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\t\"org.springframework.security.oauth2.client.jackson2.OAuth2AuthenticationTokenMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson2.DefaultOidcUserMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson2.DefaultOAuth2UserMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson2.OidcUserAuthorityMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson2.OAuth2UserAuthorityMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson2.OidcIdTokenMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson2.OidcUserInfoMixin\"));\n\t\t\t\t}\n\t\t\t\tif (jackson3Present) {\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(), loadClass(\n\t\t\t\t\t\t\t\"org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationTokenMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson.DefaultOidcUserMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson.DefaultOAuth2UserMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson.OidcUserAuthorityMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson.OAuth2UserAuthorityMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson.OidcIdTokenMixin\"));\n\t\t\t\t\tthis.reflectionHintsRegistrar.registerReflectionHints(hints.reflection(),\n\t\t\t\t\t\t\tloadClass(\"org.springframework.security.oauth2.client.jackson.OidcUserInfoMixin\"));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"removal\")\n\t\tprivate void registerJackson2Modules(RuntimeHints hints) {\n\t\t\thints.reflection()\n\t\t\t\t.registerTypes(\n\t\t\t\t\t\tArrays.asList(TypeReference.of(CoreJackson2Module.class),\n\t\t\t\t\t\t\t\tTypeReference.of(WebServletJackson2Module.class),\n\t\t\t\t\t\t\t\tTypeReference.of(OAuth2AuthorizationServerJackson2Module.class)),\n\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.ACCESS_DECLARED_FIELDS,\n\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));\n\t\t}\n\n\t\tprivate static Class<?> loadClass(String className) {\n\t\t\ttry {\n\t\t\t\treturn Class.forName(className);\n\t\t\t}\n\t\t\tcatch (ClassNotFoundException ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/aot/hint/OAuth2AuthorizationServerRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.aot.hint;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;\n\n/**\n * {@link RuntimeHintsRegistrar} that contributes the required {@link RuntimeHints} for\n * OAuth 2.1 Authorization Server. Statically registered via\n * META-INF/spring/aot.factories.\n *\n * @author Joe Grandja\n * @since 7.0\n */\nclass OAuth2AuthorizationServerRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {\n\t\thints.reflection()\n\t\t\t.registerType(OAuth2AuthorizationCodeRequestAuthenticationProvider.class,\n\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS);\n\t\thints.reflection()\n\t\t\t.registerType(OAuth2AuthorizationEndpointFilter.class, MemberCategory.INVOKE_DECLARED_METHODS);\n\t\thints.reflection()\n\t\t\t.registerType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter$OAuth2AuthorizationCodeRequestValidatingFilter\"),\n\t\t\t\t\tMemberCategory.INVOKE_DECLARED_CONSTRUCTORS);\n\t\thints.reflection()\n\t\t\t.registerType(OAuth2AuthorizationCodeRequestAuthenticationToken.class,\n\t\t\t\t\tMemberCategory.ACCESS_DECLARED_FIELDS);\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/AbstractOAuth2AuthorizationCodeRequestAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} base implementation for the OAuth 2.0 Authorization Request\n * used in the Authorization Code Grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationCodeRequestAuthenticationToken\n * @see OAuth2PushedAuthorizationRequestAuthenticationToken\n */\nabstract class AbstractOAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5813797478091794517L;\n\n\tprivate final String authorizationUri;\n\n\tprivate final String clientId;\n\n\tprivate final Authentication principal;\n\n\tprivate final String redirectUri;\n\n\tprivate final String state;\n\n\tprivate final Set<String> scopes;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\tprotected AbstractOAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId,\n\t\t\tAuthentication principal, @Nullable String redirectUri, @Nullable String state,\n\t\t\t@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(authorizationUri, \"authorizationUri cannot be empty\");\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tthis.authorizationUri = authorizationUri;\n\t\tthis.clientId = clientId;\n\t\tthis.principal = principal;\n\t\tthis.redirectUri = redirectUri;\n\t\tthis.state = state;\n\t\tthis.scopes = Collections.unmodifiableSet((scopes != null) ? new HashSet<>(scopes) : Collections.emptySet());\n\t\tthis.additionalParameters = Collections.unmodifiableMap(\n\t\t\t\t(additionalParameters != null) ? new HashMap<>(additionalParameters) : Collections.emptyMap());\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the authorization URI.\n\t * @return the authorization URI\n\t */\n\tpublic String getAuthorizationUri() {\n\t\treturn this.authorizationUri;\n\t}\n\n\t/**\n\t * Returns the client identifier.\n\t * @return the client identifier\n\t */\n\tpublic String getClientId() {\n\t\treturn this.clientId;\n\t}\n\n\t/**\n\t * Returns the redirect uri.\n\t * @return the redirect uri\n\t */\n\t@Nullable\n\tpublic String getRedirectUri() {\n\t\treturn this.redirectUri;\n\t}\n\n\t/**\n\t * Returns the state.\n\t * @return the state\n\t */\n\t@Nullable\n\tpublic String getState() {\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Returns the requested (or authorized) scope(s).\n\t * @return the requested (or authorized) scope(s), or an empty {@code Set} if not\n\t * available\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n\t/**\n\t * Returns the additional parameters.\n\t * @return the additional parameters, or an empty {@code Map} if not available\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client\n * Authentication, which authenticates the {@link OAuth2ParameterNames#CLIENT_SECRET\n * client_secret} parameter.\n *\n * @author Patryk Kostrzewa\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationProvider\n * @see OAuth2ClientAuthenticationToken\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see PasswordEncoder\n */\npublic final class ClientSecretAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final CodeVerifierAuthenticator codeVerifierAuthenticator;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\t/**\n\t * Constructs a {@code ClientSecretAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t */\n\tpublic ClientSecretAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.codeVerifierAuthenticator = new CodeVerifierAuthenticator(authorizationService);\n\t\tthis.passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t}\n\n\t/**\n\t * Sets the {@link PasswordEncoder} used to validate the\n\t * {@link RegisteredClient#getClientSecret() client secret}. If not set, the client\n\t * secret will be compared using\n\t * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}.\n\t * @param passwordEncoder the {@link PasswordEncoder} used to validate the client\n\t * secret\n\t */\n\tpublic void setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder cannot be null\");\n\t\tthis.passwordEncoder = passwordEncoder;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;\n\n\t\t// @formatter:off\n\t\tif (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC.equals(clientAuthentication.getClientAuthenticationMethod()) &&\n\t\t\t\t!ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:on\n\n\t\tString clientId = clientAuthentication.getPrincipal().toString();\n\t\tRegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);\n\t\tif (registeredClient == null) {\n\t\t\tthrowInvalidClient(OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tif (!registeredClient.getClientAuthenticationMethods()\n\t\t\t.contains(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\tthrowInvalidClient(\"authentication_method\");\n\t\t}\n\n\t\tif (clientAuthentication.getCredentials() == null) {\n\t\t\tthrowInvalidClient(\"credentials\");\n\t\t}\n\n\t\tString clientSecret = clientAuthentication.getCredentials().toString();\n\t\tif (!this.passwordEncoder.matches(clientSecret, registeredClient.getClientSecret())) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: client_secret does not match\" + \" for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrowInvalidClient(OAuth2ParameterNames.CLIENT_SECRET);\n\t\t}\n\n\t\tif (registeredClient.getClientSecretExpiresAt() != null\n\t\t\t\t&& Instant.now().isAfter(registeredClient.getClientSecretExpiresAt())) {\n\t\t\tthrowInvalidClient(\"client_secret_expires_at\");\n\t\t}\n\n\t\tif (this.passwordEncoder.upgradeEncoding(registeredClient.getClientSecret())) {\n\t\t\tregisteredClient = RegisteredClient.from(registeredClient)\n\t\t\t\t.clientSecret(this.passwordEncoder.encode(clientSecret))\n\t\t\t\t.build();\n\t\t\tthis.registeredClientRepository.save(registeredClient);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated client authentication parameters\");\n\t\t}\n\n\t\t// Validate the \"code_verifier\" parameter for the confidential client, if\n\t\t// available\n\t\tthis.codeVerifierAuthenticator.authenticateIfAvailable(clientAuthentication, registeredClient);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated client secret\");\n\t\t}\n\n\t\treturn new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tclientAuthentication.getClientAuthenticationMethod(), clientAuthentication.getCredentials());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tprivate static void throwInvalidClient(String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\"Client authentication failed: \" + parameterName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/CodeVerifierAuthenticator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\nimport java.util.Map;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An authenticator used for OAuth 2.0 Client Authentication, which authenticates the\n * {@link PkceParameterNames#CODE_VERIFIER code_verifier} parameter.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2ClientAuthenticationToken\n * @see OAuth2AuthorizationService\n */\nfinal class CodeVerifierAuthenticator {\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tCodeVerifierAuthenticator(OAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t}\n\n\tvoid authenticateRequired(OAuth2ClientAuthenticationToken clientAuthentication, RegisteredClient registeredClient) {\n\t\tif (!authenticate(clientAuthentication, registeredClient)) {\n\t\t\tthrowInvalidGrant(PkceParameterNames.CODE_VERIFIER);\n\t\t}\n\t}\n\n\tvoid authenticateIfAvailable(OAuth2ClientAuthenticationToken clientAuthentication,\n\t\t\tRegisteredClient registeredClient) {\n\t\tauthenticate(clientAuthentication, registeredClient);\n\t}\n\n\tprivate boolean authenticate(OAuth2ClientAuthenticationToken clientAuthentication,\n\t\t\tRegisteredClient registeredClient) {\n\n\t\tMap<String, Object> parameters = clientAuthentication.getAdditionalParameters();\n\t\tif (!authorizationCodeGrant(parameters)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken((String) parameters.get(OAuth2ParameterNames.CODE), AUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tif (authorization == null) {\n\t\t\tthrowInvalidGrant(OAuth2ParameterNames.CODE);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with authorization code\");\n\t\t}\n\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\n\t\tString codeChallenge = (String) authorizationRequest.getAdditionalParameters()\n\t\t\t.get(PkceParameterNames.CODE_CHALLENGE);\n\t\tString codeVerifier = (String) parameters.get(PkceParameterNames.CODE_VERIFIER);\n\t\tif (!StringUtils.hasText(codeChallenge)) {\n\t\t\tif (registeredClient.getClientSettings().isRequireProofKey() || StringUtils.hasText(codeVerifier)) {\n\t\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\t\"Invalid request: code_challenge is required\" + \" for registered client '%s'\",\n\t\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t\t}\n\t\t\t\tthrowInvalidGrant(PkceParameterNames.CODE_CHALLENGE);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(\"Did not authenticate code verifier since requireProofKey=false\");\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated code verifier parameters\");\n\t\t}\n\n\t\tString codeChallengeMethod = (String) authorizationRequest.getAdditionalParameters()\n\t\t\t.get(PkceParameterNames.CODE_CHALLENGE_METHOD);\n\t\tif (!codeVerifierValid(codeVerifier, codeChallenge, codeChallengeMethod)) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: code_verifier is missing or invalid\" + \" for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrowInvalidGrant(PkceParameterNames.CODE_VERIFIER);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated code verifier\");\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate static boolean authorizationCodeGrant(Map<String, Object> parameters) {\n\t\tif (!AuthorizationGrantType.AUTHORIZATION_CODE.getValue()\n\t\t\t.equals(parameters.get(OAuth2ParameterNames.GRANT_TYPE))) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!StringUtils.hasText((String) parameters.get(OAuth2ParameterNames.CODE))) {\n\t\t\tthrowInvalidGrant(OAuth2ParameterNames.CODE);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean codeVerifierValid(String codeVerifier, String codeChallenge, String codeChallengeMethod) {\n\t\tif (!StringUtils.hasText(codeVerifier)) {\n\t\t\treturn false;\n\t\t}\n\t\telse if (\"S256\".equals(codeChallengeMethod)) {\n\t\t\ttry {\n\t\t\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\t\t\tbyte[] digest = md.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII));\n\t\t\t\tString encodedVerifier = Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t\t\t\treturn encodedVerifier.equals(codeChallenge);\n\t\t\t}\n\t\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\t\t// It is unlikely that SHA-256 is not available on the server. If it is\n\t\t\t\t// not available,\n\t\t\t\t// there will likely be bigger issues as well. We default to SERVER_ERROR.\n\t\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static void throwInvalidGrant(String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT,\n\t\t\t\t\"Client authentication failed: \" + parameterName, null);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/DPoPProofVerifier.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.jwt.DPoPProofContext;\nimport org.springframework.security.oauth2.jwt.DPoPProofJwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.util.StringUtils;\n\n/**\n * A verifier for DPoP Proof {@link Jwt}'s.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see DPoPProofJwtDecoderFactory\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc9449\">RFC 9449\n * OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>\n */\nfinal class DPoPProofVerifier {\n\n\tprivate static final JwtDecoderFactory<DPoPProofContext> dPoPProofVerifierFactory = new DPoPProofJwtDecoderFactory();\n\n\tprivate DPoPProofVerifier() {\n\t}\n\n\tstatic Jwt verifyIfAvailable(OAuth2AuthorizationGrantAuthenticationToken authorizationGrantAuthentication) {\n\t\tString dPoPProof = (String) authorizationGrantAuthentication.getAdditionalParameters().get(\"dpop_proof\");\n\t\tif (!StringUtils.hasText(dPoPProof)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString method = (String) authorizationGrantAuthentication.getAdditionalParameters().get(\"dpop_method\");\n\t\tString targetUri = (String) authorizationGrantAuthentication.getAdditionalParameters().get(\"dpop_target_uri\");\n\n\t\tJwt dPoPProofJwt;\n\t\ttry {\n\t\t\t// @formatter:off\n\t\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof)\n\t\t\t\t\t.method(method)\n\t\t\t\t\t.targetUri(targetUri)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tJwtDecoder dPoPProofVerifier = dPoPProofVerifierFactory.createDecoder(dPoPProofContext);\n\t\t\tdPoPProofJwt = dPoPProofVerifier.decode(dPoPProof);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF), ex);\n\t\t}\n\n\t\treturn dPoPProofJwt;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client\n * Authentication, which authenticates the {@link Jwt}\n * {@link OAuth2ParameterNames#CLIENT_ASSERTION client_assertion} parameter.\n *\n * @author Rafal Lewczuk\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationProvider\n * @see OAuth2ClientAuthenticationToken\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see JwtClientAssertionDecoderFactory\n */\npublic final class JwtClientAssertionAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1\";\n\n\tprivate static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = new ClientAuthenticationMethod(\n\t\t\t\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\");\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final CodeVerifierAuthenticator codeVerifierAuthenticator;\n\n\tprivate JwtDecoderFactory<RegisteredClient> jwtDecoderFactory;\n\n\t/**\n\t * Constructs a {@code JwtClientAssertionAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t */\n\tpublic JwtClientAssertionAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.codeVerifierAuthenticator = new CodeVerifierAuthenticator(authorizationService);\n\t\tthis.jwtDecoderFactory = new JwtClientAssertionDecoderFactory();\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;\n\n\t\tif (!JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD.equals(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString clientId = clientAuthentication.getPrincipal().toString();\n\t\tRegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);\n\t\tif (registeredClient == null) {\n\t\t\tthrowInvalidClient(OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tif (!registeredClient.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.PRIVATE_KEY_JWT) &&\n\t\t\t\t!registeredClient.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {\n\t\t\tthrowInvalidClient(\"authentication_method\");\n\t\t}\n\t\t// @formatter:on\n\n\t\tif (clientAuthentication.getCredentials() == null) {\n\t\t\tthrowInvalidClient(\"credentials\");\n\t\t}\n\n\t\tJwt jwtAssertion = null;\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(registeredClient);\n\t\ttry {\n\t\t\tjwtAssertion = jwtDecoder.decode(clientAuthentication.getCredentials().toString());\n\t\t}\n\t\tcatch (JwtException ex) {\n\t\t\tthrowInvalidClient(OAuth2ParameterNames.CLIENT_ASSERTION, ex);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated client authentication parameters\");\n\t\t}\n\n\t\t// Validate the \"code_verifier\" parameter for the confidential client, if\n\t\t// available\n\t\tthis.codeVerifierAuthenticator.authenticateIfAvailable(clientAuthentication, registeredClient);\n\n\t\t// @formatter:off\n\t\tClientAuthenticationMethod clientAuthenticationMethod =\n\t\t\t\t(registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm() instanceof SignatureAlgorithm) ?\n\t\t\t\t\t\tClientAuthenticationMethod.PRIVATE_KEY_JWT :\n\t\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_JWT;\n\t\t// @formatter:on\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated client assertion\");\n\t\t}\n\n\t\treturn new OAuth2ClientAuthenticationToken(registeredClient, clientAuthenticationMethod, jwtAssertion);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the\n\t * specified {@link RegisteredClient} and is used for authenticating a {@link Jwt}\n\t * Bearer Token during OAuth 2.0 Client Authentication. The default factory is\n\t * {@link JwtClientAssertionDecoderFactory}.\n\t * @param jwtDecoderFactory the {@link JwtDecoderFactory} that provides a\n\t * {@link JwtDecoder} for the specified {@link RegisteredClient}\n\t */\n\tpublic void setJwtDecoderFactory(JwtDecoderFactory<RegisteredClient> jwtDecoderFactory) {\n\t\tAssert.notNull(jwtDecoderFactory, \"jwtDecoderFactory cannot be null\");\n\t\tthis.jwtDecoderFactory = jwtDecoderFactory;\n\t}\n\n\tprivate static void throwInvalidClient(String parameterName) {\n\t\tthrowInvalidClient(parameterName, null);\n\t}\n\n\tprivate static void throwInvalidClient(String parameterName, Throwable cause) {\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\"Client authentication failed: \" + parameterName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error, error.toString(), cause);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.springframework.http.client.SimpleClientHttpRequestFactory;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.jwt.JwtClaimValidator;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.JwtTimestampValidator;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder} for the\n * specified {@link RegisteredClient} and is used for authenticating a {@link Jwt} Bearer\n * Token during OAuth 2.0 Client Authentication.\n *\n * @author Rafal Lewczuk\n * @author Joe Grandja\n * @since 7.0\n * @see JwtDecoderFactory\n * @see RegisteredClient\n * @see OAuth2TokenValidator\n * @see JwtClientAssertionAuthenticationProvider\n * @see ClientAuthenticationMethod#PRIVATE_KEY_JWT\n * @see ClientAuthenticationMethod#CLIENT_SECRET_JWT\n */\npublic final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory<RegisteredClient> {\n\n\t/**\n\t * The default {@code OAuth2TokenValidator<Jwt>} factory that validates the\n\t * {@link JwtClaimNames#ISS iss}, {@link JwtClaimNames#SUB sub},\n\t * {@link JwtClaimNames#AUD aud}, {@link JwtClaimNames#EXP exp} and\n\t * {@link JwtClaimNames#NBF nbf} claims of the {@link Jwt} for the specified\n\t * {@link RegisteredClient}.\n\t */\n\tpublic static final Function<RegisteredClient, OAuth2TokenValidator<Jwt>> DEFAULT_JWT_VALIDATOR_FACTORY = defaultJwtValidatorFactory();\n\n\tprivate static final String JWT_CLIENT_AUTHENTICATION_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc7523#section-3\";\n\n\tprivate static final Map<JwsAlgorithm, String> JCA_ALGORITHM_MAPPINGS;\n\n\tstatic {\n\t\tMap<JwsAlgorithm, String> mappings = new HashMap<>();\n\t\tmappings.put(MacAlgorithm.HS256, \"HmacSHA256\");\n\t\tmappings.put(MacAlgorithm.HS384, \"HmacSHA384\");\n\t\tmappings.put(MacAlgorithm.HS512, \"HmacSHA512\");\n\t\tJCA_ALGORITHM_MAPPINGS = Collections.unmodifiableMap(mappings);\n\t}\n\n\tprivate static final RestTemplate restTemplate = new RestTemplate();\n\n\tstatic {\n\t\tSimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();\n\t\trequestFactory.setConnectTimeout(15_000);\n\t\trequestFactory.setReadTimeout(15_000);\n\t\trestTemplate.setRequestFactory(requestFactory);\n\t}\n\n\tprivate final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();\n\n\tprivate Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = DEFAULT_JWT_VALIDATOR_FACTORY;\n\n\t@Override\n\tpublic JwtDecoder createDecoder(RegisteredClient registeredClient) {\n\t\tAssert.notNull(registeredClient, \"registeredClient cannot be null\");\n\t\treturn this.jwtDecoders.computeIfAbsent(registeredClient.getId(), (key) -> {\n\t\t\tNimbusJwtDecoder jwtDecoder = buildDecoder(registeredClient);\n\t\t\tjwtDecoder.setJwtValidator(this.jwtValidatorFactory.apply(registeredClient));\n\t\t\treturn jwtDecoder;\n\t\t});\n\t}\n\n\t/**\n\t * Sets the factory that provides an {@link OAuth2TokenValidator} for the specified\n\t * {@link RegisteredClient} and is used by the {@link JwtDecoder}. The default\n\t * {@code OAuth2TokenValidator<Jwt>} factory is\n\t * {@link #DEFAULT_JWT_VALIDATOR_FACTORY}.\n\t * @param jwtValidatorFactory the factory that provides an\n\t * {@link OAuth2TokenValidator} for the specified {@link RegisteredClient}\n\t */\n\tpublic void setJwtValidatorFactory(Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {\n\t\tAssert.notNull(jwtValidatorFactory, \"jwtValidatorFactory cannot be null\");\n\t\tthis.jwtValidatorFactory = jwtValidatorFactory;\n\t}\n\n\tprivate static NimbusJwtDecoder buildDecoder(RegisteredClient registeredClient) {\n\t\tJwsAlgorithm jwsAlgorithm = registeredClient.getClientSettings()\n\t\t\t.getTokenEndpointAuthenticationSigningAlgorithm();\n\t\tif (jwsAlgorithm instanceof SignatureAlgorithm) {\n\t\t\tString jwkSetUrl = registeredClient.getClientSettings().getJwkSetUrl();\n\t\t\tif (!StringUtils.hasText(jwkSetUrl)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\t\t\"Failed to find a Signature Verifier for Client: '\" + registeredClient.getId()\n\t\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured the JWK Set URL.\",\n\t\t\t\t\t\tJWT_CLIENT_AUTHENTICATION_ERROR_URI);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error);\n\t\t\t}\n\t\t\treturn NimbusJwtDecoder.withJwkSetUri(jwkSetUrl)\n\t\t\t\t.jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm)\n\t\t\t\t.restOperations(restTemplate)\n\t\t\t\t.build();\n\t\t}\n\t\tif (jwsAlgorithm instanceof MacAlgorithm) {\n\t\t\tString clientSecret = registeredClient.getClientSecret();\n\t\t\tif (!StringUtils.hasText(clientSecret)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\t\t\"Failed to find a Signature Verifier for Client: '\" + registeredClient.getId()\n\t\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured the client secret.\",\n\t\t\t\t\t\tJWT_CLIENT_AUTHENTICATION_ERROR_URI);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error);\n\t\t\t}\n\t\t\tSecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\tJCA_ALGORITHM_MAPPINGS.get(jwsAlgorithm));\n\t\t\treturn NimbusJwtDecoder.withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();\n\t\t}\n\t\tOAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\"Failed to find a Signature Verifier for Client: '\" + registeredClient.getId()\n\t\t\t\t\t\t+ \"'. Check to ensure you have configured a valid JWS Algorithm: '\" + jwsAlgorithm + \"'.\",\n\t\t\t\tJWT_CLIENT_AUTHENTICATION_ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(oauth2Error);\n\t}\n\n\tprivate static Function<RegisteredClient, OAuth2TokenValidator<Jwt>> defaultJwtValidatorFactory() {\n\t\treturn (registeredClient) -> {\n\t\t\tString clientId = registeredClient.getClientId();\n\t\t\treturn new DelegatingOAuth2TokenValidator<>(new JwtClaimValidator<>(JwtClaimNames.ISS, clientId::equals),\n\t\t\t\t\tnew JwtClaimValidator<>(JwtClaimNames.SUB, clientId::equals),\n\t\t\t\t\tnew JwtClaimValidator<>(JwtClaimNames.AUD, containsAudience()),\n\t\t\t\t\tnew JwtClaimValidator<>(JwtClaimNames.EXP, Objects::nonNull), new JwtTimestampValidator());\n\t\t};\n\t}\n\n\tprivate static Predicate<List<String>> containsAudience() {\n\t\treturn (audienceClaim) -> {\n\t\t\tif (CollectionUtils.isEmpty(audienceClaim)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tList<String> audienceList = getAudience();\n\t\t\tfor (String audience : audienceClaim) {\n\t\t\t\tif (audienceList.contains(audience)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\t}\n\n\tprivate static List<String> getAudience() {\n\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\tif (!StringUtils.hasText(authorizationServerContext.getIssuer())) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t.getAuthorizationServerSettings();\n\t\tList<String> audience = new ArrayList<>();\n\t\taudience.add(authorizationServerContext.getIssuer());\n\t\taudience.add(asUrl(authorizationServerContext.getIssuer(), authorizationServerSettings.getTokenEndpoint()));\n\t\taudience.add(asUrl(authorizationServerContext.getIssuer(),\n\t\t\t\tauthorizationServerSettings.getTokenIntrospectionEndpoint()));\n\t\taudience.add(asUrl(authorizationServerContext.getIssuer(),\n\t\t\t\tauthorizationServerSettings.getTokenRevocationEndpoint()));\n\t\taudience.add(asUrl(authorizationServerContext.getIssuer(),\n\t\t\t\tauthorizationServerSettings.getPushedAuthorizationRequestEndpoint()));\n\t\treturn audience;\n\t}\n\n\tprivate static String asUrl(String issuer, String endpoint) {\n\t\treturn UriComponentsBuilder.fromUriString(issuer).path(endpoint).build().toUriString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AccessTokenResponseAuthenticationSuccessHandler;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthenticationContext} that holds an\n * {@link OAuth2AccessTokenAuthenticationToken} and additional information and is used\n * when customizing the {@link OAuth2AccessTokenResponse}.\n *\n * @author Dmitriy Dubson\n * @since 7.0\n * @see OAuth2AuthenticationContext\n * @see OAuth2AccessTokenAuthenticationToken\n * @see OAuth2AccessTokenResponse\n * @see OAuth2AccessTokenResponseAuthenticationSuccessHandler#setAccessTokenResponseCustomizer(Consumer)\n */\npublic final class OAuth2AccessTokenAuthenticationContext implements OAuth2AuthenticationContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OAuth2AccessTokenAuthenticationContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessTokenResponse.Builder access token response\n\t * builder}.\n\t * @return the {@link OAuth2AccessTokenResponse.Builder}\n\t */\n\tpublic OAuth2AccessTokenResponse.Builder getAccessTokenResponse() {\n\t\treturn get(OAuth2AccessTokenResponse.Builder.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided\n\t * {@link OAuth2AccessTokenAuthenticationToken}.\n\t * @param authentication the {@link OAuth2AccessTokenAuthenticationToken}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OAuth2AccessTokenAuthenticationToken authentication) {\n\t\treturn new Builder(authentication);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AccessTokenAuthenticationContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OAuth2AccessTokenAuthenticationContext, Builder> {\n\n\t\tprivate Builder(OAuth2AccessTokenAuthenticationToken authentication) {\n\t\t\tsuper(authentication);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AccessTokenResponse.Builder access token response\n\t\t * builder}.\n\t\t * @param accessTokenResponse the {@link OAuth2AccessTokenResponse.Builder}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder accessTokenResponse(OAuth2AccessTokenResponse.Builder accessTokenResponse) {\n\t\t\treturn put(OAuth2AccessTokenResponse.Builder.class, accessTokenResponse);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AccessTokenAuthenticationContext}.\n\t\t * @return the {@link OAuth2AccessTokenAuthenticationContext}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2AccessTokenAuthenticationContext build() {\n\t\t\tAssert.notNull(get(OAuth2AccessTokenResponse.Builder.class), \"accessTokenResponse cannot be null\");\n\t\t\treturn new OAuth2AccessTokenAuthenticationContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used when issuing an OAuth 2.0 Access Token\n * and (optional) Refresh Token.\n *\n * @author Joe Grandja\n * @author Madhu Bhat\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see RegisteredClient\n * @see OAuth2AccessToken\n * @see OAuth2RefreshToken\n * @see OAuth2ClientAuthenticationToken\n */\npublic class OAuth2AccessTokenAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2773767853287774441L;\n\n\tprivate final RegisteredClient registeredClient;\n\n\tprivate final Authentication clientPrincipal;\n\n\tprivate final OAuth2AccessToken accessToken;\n\n\tprivate final OAuth2RefreshToken refreshToken;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\t/**\n\t * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided\n\t * parameters.\n\t * @param registeredClient the registered client\n\t * @param clientPrincipal the authenticated client principal\n\t * @param accessToken the access token\n\t */\n\tpublic OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal,\n\t\t\tOAuth2AccessToken accessToken) {\n\t\tthis(registeredClient, clientPrincipal, accessToken, null);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided\n\t * parameters.\n\t * @param registeredClient the registered client\n\t * @param clientPrincipal the authenticated client principal\n\t * @param accessToken the access token\n\t * @param refreshToken the refresh token\n\t */\n\tpublic OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal,\n\t\t\tOAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {\n\t\tthis(registeredClient, clientPrincipal, accessToken, refreshToken, Collections.emptyMap());\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided\n\t * parameters.\n\t * @param registeredClient the registered client\n\t * @param clientPrincipal the authenticated client principal\n\t * @param accessToken the access token\n\t * @param refreshToken the refresh token\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal,\n\t\t\tOAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken,\n\t\t\tMap<String, Object> additionalParameters) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(registeredClient, \"registeredClient cannot be null\");\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tAssert.notNull(additionalParameters, \"additionalParameters cannot be null\");\n\t\tthis.registeredClient = registeredClient;\n\t\tthis.clientPrincipal = clientPrincipal;\n\t\tthis.accessToken = accessToken;\n\t\tthis.refreshToken = refreshToken;\n\t\tthis.additionalParameters = additionalParameters;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.clientPrincipal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the {@link RegisteredClient registered client}.\n\t * @return the {@link RegisteredClient}\n\t */\n\tpublic RegisteredClient getRegisteredClient() {\n\t\treturn this.registeredClient;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessToken access token}.\n\t * @return the {@link OAuth2AccessToken}\n\t */\n\tpublic OAuth2AccessToken getAccessToken() {\n\t\treturn this.accessToken;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2RefreshToken refresh token}.\n\t * @return the {@link OAuth2RefreshToken} or {@code null} if not available\n\t */\n\t@Nullable\n\tpublic OAuth2RefreshToken getRefreshToken() {\n\t\treturn this.refreshToken;\n\t}\n\n\t/**\n\t * Returns the additional parameters.\n\t * @return a {@code Map} of the additional parameters, may be empty\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.authorization.context.Context;\nimport org.springframework.util.Assert;\n\n/**\n * A context that holds an {@link Authentication} and (optionally) additional information\n * and is used in an {@link AuthenticationProvider}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see Context\n */\npublic interface OAuth2AuthenticationContext extends Context {\n\n\t/**\n\t * Returns the {@link Authentication} associated to the context.\n\t * @param <T> the type of the {@code Authentication}\n\t * @return the {@link Authentication}\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tdefault <T extends Authentication> T getAuthentication() {\n\t\treturn (T) get(Authentication.class);\n\t}\n\n\t/**\n\t * A builder for subclasses of {@link OAuth2AuthenticationContext}.\n\t *\n\t * @param <T> the type of the authentication context\n\t * @param <B> the type of the builder\n\t */\n\tabstract class AbstractBuilder<T extends OAuth2AuthenticationContext, B extends AbstractBuilder<T, B>> {\n\n\t\tprivate final Map<Object, Object> context = new HashMap<>();\n\n\t\tprotected AbstractBuilder(Authentication authentication) {\n\t\t\tAssert.notNull(authentication, \"authentication cannot be null\");\n\t\t\tput(Authentication.class, authentication);\n\t\t}\n\n\t\t/**\n\t\t * Associates an attribute.\n\t\t * @param key the key for the attribute\n\t\t * @param value the value of the attribute\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B put(Object key, Object value) {\n\t\t\tAssert.notNull(key, \"key cannot be null\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tgetContext().put(key, value);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the attributes {@code Map} allowing the ability to add,\n\t\t * replace, or remove.\n\t\t * @param contextConsumer a {@link Consumer} of the attributes {@code Map}\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B context(Consumer<Map<Object, Object>> contextConsumer) {\n\t\t\tcontextConsumer.accept(getContext());\n\t\t\treturn getThis();\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected <V> V get(Object key) {\n\t\t\treturn (V) getContext().get(key);\n\t\t}\n\n\t\tprotected Map<Object, Object> getContext() {\n\t\t\treturn this.context;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected final B getThis() {\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AuthenticationContext}.\n\t\t * @return the {@link OAuth2AuthenticationContext}\n\t\t */\n\t\tpublic abstract T build();\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationProviderUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Map;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Utility methods for the OAuth 2.0 {@link AuthenticationProvider}'s.\n *\n * @author Joe Grandja\n * @since 7.0\n */\nfinal class OAuth2AuthenticationProviderUtils {\n\n\tprivate OAuth2AuthenticationProviderUtils() {\n\t}\n\n\tstatic OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = null;\n\t\tif (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {\n\t\t\tclientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();\n\t\t}\n\t\tif (clientPrincipal != null && clientPrincipal.isAuthenticated()) {\n\t\t\treturn clientPrincipal;\n\t\t}\n\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\tstatic <T extends OAuth2Token> OAuth2AccessToken accessToken(OAuth2Authorization.Builder builder, T token,\n\t\t\tOAuth2TokenContext accessTokenContext) {\n\n\t\tOAuth2AccessToken.TokenType tokenType = OAuth2AccessToken.TokenType.BEARER;\n\t\tif (token instanceof ClaimAccessor claimAccessor) {\n\t\t\tMap<String, Object> cnfClaims = claimAccessor.getClaimAsMap(\"cnf\");\n\t\t\tif (!CollectionUtils.isEmpty(cnfClaims) && cnfClaims.containsKey(\"jkt\")) {\n\t\t\t\ttokenType = OAuth2AccessToken.TokenType.DPOP;\n\t\t\t}\n\t\t}\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, token.getTokenValue(), token.getIssuedAt(),\n\t\t\t\ttoken.getExpiresAt(), accessTokenContext.getAuthorizedScopes());\n\t\tOAuth2TokenFormat accessTokenFormat = accessTokenContext.getRegisteredClient()\n\t\t\t.getTokenSettings()\n\t\t\t.getAccessTokenFormat();\n\t\tbuilder.token(accessToken, (metadata) -> {\n\t\t\tif (token instanceof ClaimAccessor claimAccessor) {\n\t\t\t\tmetadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, claimAccessor.getClaims());\n\t\t\t}\n\t\t\tmetadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, false);\n\t\t\tmetadata.put(OAuth2TokenFormat.class.getName(), accessTokenFormat.getValue());\n\t\t});\n\n\t\treturn accessToken;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.Principal;\nimport java.util.ArrayList;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Code\n * Grant.\n *\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OAuth2AuthorizationCodeAuthenticationToken\n * @see OAuth2AccessTokenAuthenticationToken\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider\n * @see OAuth2AuthorizationService\n * @see OAuth2TokenGenerator\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1\">Section 4.1 Authorization\n * Code Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3\">Section 4.1.3 Access\n * Token Request</a>\n */\npublic final class OAuth2AuthorizationCodeAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;\n\n\tprivate SessionRegistry sessionRegistry;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param authorizationService the authorization service\n\t * @param tokenGenerator the token generator\n\t */\n\tpublic OAuth2AuthorizationCodeAuthenticationProvider(OAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(tokenGenerator, \"tokenGenerator cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.tokenGenerator = tokenGenerator;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(authorizationCodeAuthentication);\n\t\tRegisteredClient registeredClient = clientPrincipal.getRegisteredClient();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(authorizationCodeAuthentication.getCode(), AUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tif (authorization == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with authorization code\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\n\t\tif (!registeredClient.getClientId().equals(authorizationRequest.getClientId())) {\n\t\t\tif (!authorizationCode.isInvalidated()) {\n\t\t\t\t// Invalidate the authorization code given that a different client is\n\t\t\t\t// attempting to use it\n\t\t\t\tauthorization = OAuth2Authorization.from(authorization)\n\t\t\t\t\t.invalidate(authorizationCode.getToken())\n\t\t\t\t\t.build();\n\t\t\t\tthis.authorizationService.save(authorization);\n\t\t\t\tif (this.logger.isWarnEnabled()) {\n\t\t\t\t\tthis.logger.warn(LogMessage.format(\"Invalidated authorization code used by registered client '%s'\",\n\t\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (StringUtils.hasText(authorizationRequest.getRedirectUri())\n\t\t\t\t&& !authorizationRequest.getRedirectUri().equals(authorizationCodeAuthentication.getRedirectUri())) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: redirect_uri does not match\" + \" for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (!authorizationCode.isActive()) {\n\t\t\tif (authorizationCode.isInvalidated()) {\n\t\t\t\tOAuth2Authorization.Token<? extends OAuth2Token> token = (authorization.getRefreshToken() != null)\n\t\t\t\t\t\t? authorization.getRefreshToken() : authorization.getAccessToken();\n\t\t\t\tif (token != null) {\n\t\t\t\t\t// Invalidate the access (and refresh) token as the client is\n\t\t\t\t\t// attempting to use the authorization code more than once\n\t\t\t\t\tauthorization = OAuth2Authorization.from(authorization).invalidate(token.getToken()).build();\n\t\t\t\t\tthis.authorizationService.save(authorization);\n\t\t\t\t\tif (this.logger.isWarnEnabled()) {\n\t\t\t\t\t\tthis.logger.warn(LogMessage.format(\n\t\t\t\t\t\t\t\t\"Invalidated authorization token(s) previously issued to registered client '%s'\",\n\t\t\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\t// Verify the DPoP Proof (if available)\n\t\tJwt dPoPProof = DPoPProofVerifier.verifyIfAvailable(authorizationCodeAuthentication);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated token request parameters\");\n\t\t}\n\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(principal)\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizedScopes(authorization.getAuthorizedScopes())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.authorizationGrant(authorizationCodeAuthentication);\n\t\t// @formatter:on\n\t\tif (dPoPProof != null) {\n\t\t\ttokenContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProof);\n\t\t}\n\n\t\tOAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization);\n\n\t\t// ----- Access token -----\n\t\tOAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();\n\t\tOAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);\n\t\tif (generatedAccessToken == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the access token.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated access token\");\n\t\t}\n\n\t\tOAuth2AccessToken accessToken = OAuth2AuthenticationProviderUtils.accessToken(authorizationBuilder,\n\t\t\t\tgeneratedAccessToken, tokenContext);\n\n\t\t// ----- Refresh token -----\n\t\tOAuth2RefreshToken refreshToken = null;\n\t\t// Do not issue refresh token to public client\n\t\tif (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {\n\t\t\ttokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();\n\t\t\tOAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);\n\t\t\tif (generatedRefreshToken != null) {\n\t\t\t\tif (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {\n\t\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\t\"The token generator failed to generate a valid refresh token.\", ERROR_URI);\n\t\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t\t}\n\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(\"Generated refresh token\");\n\t\t\t\t}\n\n\t\t\t\trefreshToken = (OAuth2RefreshToken) generatedRefreshToken;\n\t\t\t\tauthorizationBuilder.refreshToken(refreshToken);\n\t\t\t}\n\t\t}\n\n\t\t// ----- ID token -----\n\t\tOidcIdToken idToken;\n\t\tif (authorizationRequest.getScopes().contains(OidcScopes.OPENID)) {\n\t\t\tSessionInformation sessionInformation = getSessionInformation(principal);\n\t\t\tif (sessionInformation != null) {\n\t\t\t\ttry {\n\t\t\t\t\t// Compute (and use) hash for Session ID\n\t\t\t\t\tsessionInformation = new SessionInformation(sessionInformation.getPrincipal(),\n\t\t\t\t\t\t\tcreateHash(sessionInformation.getSessionId()), sessionInformation.getLastRequest());\n\t\t\t\t}\n\t\t\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\t\"Failed to compute hash for Session ID.\", ERROR_URI);\n\t\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t\t}\n\t\t\t\ttokenContextBuilder.put(SessionInformation.class, sessionInformation);\n\t\t\t}\n\t\t\t// @formatter:off\n\t\t\ttokenContext = tokenContextBuilder\n\t\t\t\t\t.tokenType(ID_TOKEN_TOKEN_TYPE)\n\t\t\t\t\t.authorization(authorizationBuilder.build())\t// ID token customizer may need access to the access token and/or refresh token\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tOAuth2Token generatedIdToken = this.tokenGenerator.generate(tokenContext);\n\t\t\tif (!(generatedIdToken instanceof Jwt)) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\"The token generator failed to generate the ID token.\", ERROR_URI);\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Generated id token\");\n\t\t\t}\n\n\t\t\tidToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(),\n\t\t\t\t\tgeneratedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims());\n\t\t\tauthorizationBuilder.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));\n\t\t}\n\t\telse {\n\t\t\tidToken = null;\n\t\t}\n\n\t\t// Invalidate the authorization code as it can only be used once\n\t\tauthorizationBuilder.invalidate(authorizationCode.getToken());\n\n\t\tauthorization = authorizationBuilder.build();\n\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = Collections.emptyMap();\n\t\tif (idToken != null) {\n\t\t\tadditionalParameters = new HashMap<>();\n\t\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, idToken.getTokenValue());\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated token request\");\n\t\t}\n\n\t\treturn new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken,\n\t\t\t\tadditionalParameters);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link SessionRegistry} used to track OpenID Connect sessions.\n\t * @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect\n\t * sessions\n\t */\n\tpublic void setSessionRegistry(SessionRegistry sessionRegistry) {\n\t\tAssert.notNull(sessionRegistry, \"sessionRegistry cannot be null\");\n\t\tthis.sessionRegistry = sessionRegistry;\n\t}\n\n\tprivate SessionInformation getSessionInformation(Authentication principal) {\n\t\tSessionInformation sessionInformation = null;\n\t\tif (this.sessionRegistry != null) {\n\t\t\tList<SessionInformation> sessions = this.sessionRegistry.getAllSessions(principal.getPrincipal(), false);\n\t\t\tif (!CollectionUtils.isEmpty(sessions)) {\n\t\t\t\tsessionInformation = sessions.get(0);\n\t\t\t\tif (sessions.size() > 1) {\n\t\t\t\t\t// Get the most recent session\n\t\t\t\t\tsessions = new ArrayList<>(sessions);\n\t\t\t\t\tsessions.sort(Comparator.comparing(SessionInformation::getLastRequest));\n\t\t\t\t\tsessionInformation = sessions.get(sessions.size() - 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sessionInformation;\n\t}\n\n\tprivate static String createHash(String value) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for the OAuth 2.0 Authorization Code\n * Grant.\n *\n * @author Joe Grandja\n * @author Madhu Bhat\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OAuth2AuthorizationGrantAuthenticationToken\n * @see OAuth2AuthorizationCodeAuthenticationProvider\n */\npublic class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {\n\n\tprivate final String code;\n\n\tprivate final String redirectUri;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeAuthenticationToken} using the provided\n\t * parameters.\n\t * @param code the authorization code\n\t * @param clientPrincipal the authenticated client principal\n\t * @param redirectUri the redirect uri\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2AuthorizationCodeAuthenticationToken(String code, Authentication clientPrincipal,\n\t\t\t@Nullable String redirectUri, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(AuthorizationGrantType.AUTHORIZATION_CODE, clientPrincipal, additionalParameters);\n\t\tAssert.hasText(code, \"code cannot be empty\");\n\t\tthis.code = code;\n\t\tthis.redirectUri = redirectUri;\n\t}\n\n\t/**\n\t * Returns the authorization code.\n\t * @return the authorization code\n\t */\n\tpublic String getCode() {\n\t\treturn this.code;\n\t}\n\n\t/**\n\t * Returns the redirect uri.\n\t * @return the redirect uri\n\t */\n\t@Nullable\n\tpublic String getRedirectUri() {\n\t\treturn this.redirectUri;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.util.Base64;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\n\n/**\n * An {@link OAuth2TokenGenerator} that generates an {@link OAuth2AuthorizationCode}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenGenerator\n * @see OAuth2AuthorizationCode\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider\n * @see OAuth2AuthorizationConsentAuthenticationProvider\n */\nfinal class OAuth2AuthorizationCodeGenerator implements OAuth2TokenGenerator<OAuth2AuthorizationCode> {\n\n\tprivate final StringKeyGenerator authorizationCodeGenerator = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2AuthorizationCode generate(OAuth2TokenContext context) {\n\t\tif (context.getTokenType() == null || !OAuth2ParameterNames.CODE.equals(context.getTokenType().getValue())) {\n\t\t\treturn null;\n\t\t}\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt\n\t\t\t.plus(context.getRegisteredClient().getTokenSettings().getAuthorizationCodeTimeToLive());\n\t\treturn new OAuth2AuthorizationCode(this.authorizationCodeGenerator.generateKey(), issuedAt, expiresAt);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthenticationContext} that holds an\n * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and additional information\n * and is used when validating the OAuth 2.0 Authorization Request parameters, as well as,\n * determining if authorization consent is required.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthenticationContext\n * @see OAuth2AuthorizationCodeRequestAuthenticationToken\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer)\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthorizationConsentRequired(Predicate)\n */\npublic final class OAuth2AuthorizationCodeRequestAuthenticationContext implements OAuth2AuthenticationContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OAuth2AuthorizationCodeRequestAuthenticationContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link RegisteredClient registered client}.\n\t * @return the {@link RegisteredClient}\n\t */\n\tpublic RegisteredClient getRegisteredClient() {\n\t\treturn get(RegisteredClient.class);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest authorization request}.\n\t * @return the {@link OAuth2AuthorizationRequest}\n\t */\n\t@Nullable\n\tpublic OAuth2AuthorizationRequest getAuthorizationRequest() {\n\t\treturn get(OAuth2AuthorizationRequest.class);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationConsent authorization consent}.\n\t * @return the {@link OAuth2AuthorizationConsent}\n\t */\n\t@Nullable\n\tpublic OAuth2AuthorizationConsent getAuthorizationConsent() {\n\t\treturn get(OAuth2AuthorizationConsent.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.\n\t * @param authentication the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OAuth2AuthorizationCodeRequestAuthenticationToken authentication) {\n\t\treturn new Builder(authentication);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AuthorizationCodeRequestAuthenticationContext}.\n\t */\n\tpublic static final class Builder\n\t\t\textends AbstractBuilder<OAuth2AuthorizationCodeRequestAuthenticationContext, Builder> {\n\n\t\tprivate Builder(OAuth2AuthorizationCodeRequestAuthenticationToken authentication) {\n\t\t\tsuper(authentication);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link RegisteredClient registered client}.\n\t\t * @param registeredClient the {@link RegisteredClient}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder registeredClient(RegisteredClient registeredClient) {\n\t\t\treturn put(RegisteredClient.class, registeredClient);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AuthorizationRequest authorization request}.\n\t\t * @param authorizationRequest the {@link OAuth2AuthorizationRequest}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationRequest(OAuth2AuthorizationRequest authorizationRequest) {\n\t\t\treturn put(OAuth2AuthorizationRequest.class, authorizationRequest);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AuthorizationConsent authorization consent}.\n\t\t * @param authorizationConsent the {@link OAuth2AuthorizationConsent}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationConsent(OAuth2AuthorizationConsent authorizationConsent) {\n\t\t\treturn put(OAuth2AuthorizationConsent.class, authorizationConsent);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AuthorizationCodeRequestAuthenticationContext}.\n\t\t * @return the {@link OAuth2AuthorizationCodeRequestAuthenticationContext}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2AuthorizationCodeRequestAuthenticationContext build() {\n\t\t\tAssert.notNull(get(RegisteredClient.class), \"registeredClient cannot be null\");\n\t\t\treturn new OAuth2AuthorizationCodeRequestAuthenticationContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\n/**\n * This exception is thrown by\n * {@link OAuth2AuthorizationCodeRequestAuthenticationProvider} when an attempt to\n * authenticate the OAuth 2.0 Authorization Request (or Consent) fails.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationCodeRequestAuthenticationToken\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider\n */\npublic class OAuth2AuthorizationCodeRequestAuthenticationException extends OAuth2AuthenticationException {\n\n\tprivate final OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using\n\t * the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param authorizationCodeRequestAuthentication the {@link Authentication} instance\n\t * of the OAuth 2.0 Authorization Request (or Consent)\n\t */\n\tpublic OAuth2AuthorizationCodeRequestAuthenticationException(OAuth2Error error,\n\t\t\t@Nullable OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) {\n\t\tsuper(error);\n\t\tthis.authorizationCodeRequestAuthentication = authorizationCodeRequestAuthentication;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using\n\t * the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param cause the root cause\n\t * @param authorizationCodeRequestAuthentication the {@link Authentication} instance\n\t * of the OAuth 2.0 Authorization Request (or Consent)\n\t */\n\tpublic OAuth2AuthorizationCodeRequestAuthenticationException(OAuth2Error error, Throwable cause,\n\t\t\t@Nullable OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) {\n\t\tsuper(error, cause);\n\t\tthis.authorizationCodeRequestAuthentication = authorizationCodeRequestAuthentication;\n\t}\n\n\t/**\n\t * Returns the {@link Authentication} instance of the OAuth 2.0 Authorization Request\n\t * (or Consent), or {@code null} if not available.\n\t * @return the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}\n\t */\n\t@Nullable\n\tpublic OAuth2AuthorizationCodeRequestAuthenticationToken getAuthorizationCodeRequestAuthentication() {\n\t\treturn this.authorizationCodeRequestAuthentication;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization\n * Request used in the Authorization Code Grant.\n *\n * @author Joe Grandja\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2AuthorizationCodeRequestAuthenticationToken\n * @see OAuth2AuthorizationCodeRequestAuthenticationValidator\n * @see OAuth2AuthorizationCodeAuthenticationProvider\n * @see OAuth2AuthorizationConsentAuthenticationProvider\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see OAuth2AuthorizationConsentService\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1\">Section 4.1.1\n * Authorization Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\">Section 3.1.2.1\n * Authentication Request</a>\n */\npublic final class OAuth2AuthorizationCodeRequestAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1\";\n\n\tprivate static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);\n\n\tprivate static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder());\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2AuthorizationConsentService authorizationConsentService;\n\n\tprivate OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator = new OAuth2AuthorizationCodeGenerator();\n\n\tprivate Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator = new OAuth2AuthorizationCodeRequestAuthenticationValidator();\n\n\tprivate Predicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired = OAuth2AuthorizationCodeRequestAuthenticationProvider::isAuthorizationConsentRequired;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationProvider} using\n\t * the provided parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t * @param authorizationConsentService the authorization consent service\n\t */\n\tpublic OAuth2AuthorizationCodeRequestAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2AuthorizationConsentService authorizationConsentService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(authorizationConsentService, \"authorizationConsentService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.authorizationConsentService = authorizationConsentService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication;\n\n\t\tOAuth2Authorization pushedAuthorization = null;\n\t\tString requestUri = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()\n\t\t\t.get(OAuth2ParameterNames.REQUEST_URI);\n\t\tif (StringUtils.hasText(requestUri)) {\n\t\t\tOAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = null;\n\t\t\ttry {\n\t\t\t\tpushedAuthorizationRequestUri = OAuth2PushedAuthorizationRequestUri.parse(requestUri);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REQUEST_URI,\n\t\t\t\t\t\tauthorizationCodeRequestAuthentication, null);\n\t\t\t}\n\n\t\t\tpushedAuthorization = this.authorizationService.findByToken(pushedAuthorizationRequestUri.getState(),\n\t\t\t\t\tSTATE_TOKEN_TYPE);\n\t\t\tif (pushedAuthorization == null) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REQUEST_URI,\n\t\t\t\t\t\tauthorizationCodeRequestAuthentication, null);\n\t\t\t}\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Retrieved authorization with pushed authorization request\");\n\t\t\t}\n\n\t\t\tOAuth2AuthorizationRequest authorizationRequest = pushedAuthorization\n\t\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\n\t\t\tif (!authorizationCodeRequestAuthentication.getClientId().equals(authorizationRequest.getClientId())) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\t\t\tauthorizationCodeRequestAuthentication, null);\n\t\t\t}\n\n\t\t\tif (Instant.now().isAfter(pushedAuthorizationRequestUri.getExpiresAt())) {\n\t\t\t\t// Remove (effectively invalidating) the pushed authorization request\n\t\t\t\tthis.authorizationService.remove(pushedAuthorization);\n\t\t\t\tif (this.logger.isWarnEnabled()) {\n\t\t\t\t\tthis.logger\n\t\t\t\t\t\t.warn(LogMessage.format(\"Removed expired pushed authorization request for client id '%s'\",\n\t\t\t\t\t\t\t\tauthorizationRequest.getClientId()));\n\t\t\t\t}\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REQUEST_URI,\n\t\t\t\t\t\tauthorizationCodeRequestAuthentication, null);\n\t\t\t}\n\n\t\t\tauthorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\t\tauthorizationCodeRequestAuthentication.getAuthorizationUri(), authorizationRequest.getClientId(),\n\t\t\t\t\t(Authentication) authorizationCodeRequestAuthentication.getPrincipal(),\n\t\t\t\t\tauthorizationRequest.getRedirectUri(), authorizationRequest.getState(),\n\t\t\t\t\tauthorizationRequest.getScopes(), authorizationRequest.getAdditionalParameters());\n\t\t}\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(authorizationCodeRequestAuthentication.getClientId());\n\t\tif (registeredClient == null) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\t\tauthorizationCodeRequestAuthentication, null);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationContext.Builder authenticationContextBuilder = OAuth2AuthorizationCodeRequestAuthenticationContext\n\t\t\t.with(authorizationCodeRequestAuthentication)\n\t\t\t.registeredClient(registeredClient);\n\n\t\tif (!authorizationCodeRequestAuthentication.isValidated()) {\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext = authenticationContextBuilder\n\t\t\t\t.build();\n\n\t\t\t// grant_type\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_AUTHORIZATION_GRANT_TYPE_VALIDATOR\n\t\t\t\t.accept(authenticationContext);\n\n\t\t\t// redirect_uri and scope\n\t\t\tthis.authenticationValidator.accept(authenticationContext);\n\n\t\t\t// code_challenge (REQUIRED for public clients) - RFC 7636 (PKCE)\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_CODE_CHALLENGE_VALIDATOR\n\t\t\t\t.accept(authenticationContext);\n\n\t\t\t// prompt (OPTIONAL for OpenID Connect 1.0 Authentication Request)\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_PROMPT_VALIDATOR\n\t\t\t\t.accept(authenticationContext);\n\n\t\t\tauthorizationCodeRequestAuthentication.setValidated(true);\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Validated authorization code request parameters\");\n\t\t\t}\n\t\t}\n\n\t\t// ---------------\n\t\t// The request is valid - ensure the resource owner is authenticated\n\t\t// ---------------\n\n\t\tAuthentication principal = (Authentication) authorizationCodeRequestAuthentication.getPrincipal();\n\n\t\tSet<String> promptValues = Collections.emptySet();\n\t\tif (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {\n\t\t\tString prompt = (String) authorizationCodeRequestAuthentication.getAdditionalParameters().get(\"prompt\");\n\t\t\tif (StringUtils.hasText(prompt)) {\n\t\t\t\tpromptValues = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(prompt, \" \")));\n\t\t\t}\n\t\t}\n\n\t\tif (!isPrincipalAuthenticated(principal)) {\n\t\t\tif (promptValues.contains(OidcPrompt.NONE)) {\n\t\t\t\tthrowError(\"login_required\", \"prompt\", authorizationCodeRequestAuthentication, registeredClient);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, \"principal\", authorizationCodeRequestAuthentication,\n\t\t\t\t\t\tregisteredClient);\n\t\t\t}\n\t\t}\n\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(authorizationCodeRequestAuthentication.getAuthorizationUri())\n\t\t\t.clientId(registeredClient.getClientId())\n\t\t\t.redirectUri(authorizationCodeRequestAuthentication.getRedirectUri())\n\t\t\t.scopes(authorizationCodeRequestAuthentication.getScopes())\n\t\t\t.state(authorizationCodeRequestAuthentication.getState())\n\t\t\t.additionalParameters(authorizationCodeRequestAuthentication.getAdditionalParameters())\n\t\t\t.build();\n\t\tauthenticationContextBuilder.authorizationRequest(authorizationRequest);\n\n\t\tOAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService\n\t\t\t.findById(registeredClient.getId(), principal.getName());\n\t\tif (currentAuthorizationConsent != null) {\n\t\t\tauthenticationContextBuilder.authorizationConsent(currentAuthorizationConsent);\n\t\t}\n\n\t\tif (this.authorizationConsentRequired.test(authenticationContextBuilder.build())) {\n\t\t\tif (promptValues.contains(OidcPrompt.NONE)) {\n\t\t\t\t// Return an error instead of displaying the consent page\n\t\t\t\tthrowError(\"consent_required\", \"prompt\", authorizationCodeRequestAuthentication, registeredClient);\n\t\t\t}\n\n\t\t\tString state = DEFAULT_STATE_GENERATOR.generateKey();\n\t\t\tOAuth2Authorization authorization = authorizationBuilder(registeredClient, principal, authorizationRequest)\n\t\t\t\t.attribute(OAuth2ParameterNames.STATE, state)\n\t\t\t\t.build();\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Generated authorization consent state\");\n\t\t\t}\n\n\t\t\tthis.authorizationService.save(authorization);\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t\t}\n\n\t\t\tif (pushedAuthorization != null) {\n\t\t\t\t// Enforce one-time use by removing the pushed authorization request\n\t\t\t\tthis.authorizationService.remove(pushedAuthorization);\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(\"Removed authorization with pushed authorization request\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSet<String> currentAuthorizedScopes = (currentAuthorizationConsent != null)\n\t\t\t\t\t? currentAuthorizationConsent.getScopes() : null;\n\n\t\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\t\tif (pushedAuthorization != null) {\n\t\t\t\tadditionalParameters.put(OAuth2ParameterNames.SCOPE, authorizationRequest.getScopes());\n\t\t\t}\n\n\t\t\treturn new OAuth2AuthorizationConsentAuthenticationToken(authorizationRequest.getAuthorizationUri(),\n\t\t\t\t\tregisteredClient.getClientId(), principal, state, currentAuthorizedScopes, additionalParameters);\n\t\t}\n\n\t\tOAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext(authorizationCodeRequestAuthentication,\n\t\t\t\tregisteredClient, null, authorizationRequest.getScopes());\n\t\tOAuth2AuthorizationCode authorizationCode = this.authorizationCodeGenerator.generate(tokenContext);\n\t\tif (authorizationCode == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the authorization code.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated authorization code\");\n\t\t}\n\n\t\tOAuth2Authorization authorization = authorizationBuilder(registeredClient, principal, authorizationRequest)\n\t\t\t.authorizedScopes(authorizationRequest.getScopes())\n\t\t\t.token(authorizationCode)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t}\n\n\t\tif (pushedAuthorization != null) {\n\t\t\t// Enforce one-time use by removing the pushed authorization request\n\t\t\tthis.authorizationService.remove(pushedAuthorization);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Removed authorization with pushed authorization request\");\n\t\t\t}\n\t\t}\n\n\t\tString redirectUri = authorizationRequest.getRedirectUri();\n\t\tif (!StringUtils.hasText(redirectUri)) {\n\t\t\tredirectUri = registeredClient.getRedirectUris().iterator().next();\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated authorization code request\");\n\t\t}\n\n\t\treturn new OAuth2AuthorizationCodeRequestAuthenticationToken(authorizationRequest.getAuthorizationUri(),\n\t\t\t\tregisteredClient.getClientId(), principal, authorizationCode, redirectUri,\n\t\t\t\tauthorizationRequest.getState(), authorizationRequest.getScopes());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2AuthorizationCodeRequestAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2TokenGenerator} that generates the\n\t * {@link OAuth2AuthorizationCode}.\n\t * @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates\n\t * the {@link OAuth2AuthorizationCode}\n\t */\n\tpublic void setAuthorizationCodeGenerator(\n\t\t\tOAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator) {\n\t\tAssert.notNull(authorizationCodeGenerator, \"authorizationCodeGenerator cannot be null\");\n\t\tthis.authorizationCodeGenerator = authorizationCodeGenerator;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for\n\t * validating specific OAuth 2.0 Authorization Request parameters associated in the\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. The default\n\t * authentication validator is\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationValidator}.\n\t *\n\t * <p>\n\t * <b>NOTE:</b> The authentication validator MUST throw\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationException} if validation fails.\n\t * @param authenticationValidator the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for\n\t * validating specific OAuth 2.0 Authorization Request parameters\n\t */\n\tpublic void setAuthenticationValidator(\n\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {\n\t\tAssert.notNull(authenticationValidator, \"authenticationValidator cannot be null\");\n\t\tthis.authenticationValidator = authenticationValidator;\n\t}\n\n\t/**\n\t * Sets the {@code Predicate} used to determine if authorization consent is required.\n\t *\n\t * <p>\n\t * The {@link OAuth2AuthorizationCodeRequestAuthenticationContext} gives the predicate\n\t * access to the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}, as well\n\t * as, the following context attributes:\n\t * <ul>\n\t * <li>The {@link RegisteredClient} associated with the authorization request.</li>\n\t * <li>The {@link OAuth2AuthorizationRequest} containing the authorization request\n\t * parameters.</li>\n\t * <li>The {@link OAuth2AuthorizationConsent} previously granted to the\n\t * {@link RegisteredClient}, or {@code null} if not available.</li>\n\t * </ul>\n\t * @param authorizationConsentRequired the {@code Predicate} used to determine if\n\t * authorization consent is required\n\t */\n\tpublic void setAuthorizationConsentRequired(\n\t\t\tPredicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired) {\n\t\tAssert.notNull(authorizationConsentRequired, \"authorizationConsentRequired cannot be null\");\n\t\tthis.authorizationConsentRequired = authorizationConsentRequired;\n\t}\n\n\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> getAuthenticationValidatorComposite() {\n\t\treturn OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_AUTHORIZATION_GRANT_TYPE_VALIDATOR\n\t\t\t.andThen(this.authenticationValidator)\n\t\t\t.andThen(OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_CODE_CHALLENGE_VALIDATOR)\n\t\t\t.andThen(OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_PROMPT_VALIDATOR);\n\t}\n\n\tprivate static boolean isAuthorizationConsentRequired(\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tif (!authenticationContext.getRegisteredClient().getClientSettings().isRequireAuthorizationConsent()) {\n\t\t\treturn false;\n\t\t}\n\t\t// 'openid' scope does not require consent\n\t\tif (authenticationContext.getAuthorizationRequest().getScopes().contains(OidcScopes.OPENID)\n\t\t\t\t&& authenticationContext.getAuthorizationRequest().getScopes().size() == 1) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (authenticationContext.getAuthorizationConsent() != null && authenticationContext.getAuthorizationConsent()\n\t\t\t.getScopes()\n\t\t\t.containsAll(authenticationContext.getAuthorizationRequest().getScopes())) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate static OAuth2Authorization.Builder authorizationBuilder(RegisteredClient registeredClient,\n\t\t\tAuthentication principal, OAuth2AuthorizationRequest authorizationRequest) {\n\t\treturn OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.attribute(Principal.class.getName(), principal)\n\t\t\t.attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest);\n\t}\n\n\tprivate static OAuth2TokenContext createAuthorizationCodeTokenContext(\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tRegisteredClient registeredClient, OAuth2Authorization authorization, Set<String> authorizedScopes) {\n\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal((Authentication) authorizationCodeRequestAuthentication.getPrincipal())\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.tokenType(new OAuth2TokenType(OAuth2ParameterNames.CODE))\n\t\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.authorizationGrant(authorizationCodeRequestAuthentication);\n\t\t// @formatter:on\n\n\t\tif (authorization != null) {\n\t\t\ttokenContextBuilder.authorization(authorization);\n\t\t}\n\n\t\treturn tokenContextBuilder.build();\n\t}\n\n\tprivate static boolean isPrincipalAuthenticated(Authentication principal) {\n\t\treturn principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass())\n\t\t\t\t&& principal.isAuthenticated();\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tRegisteredClient registeredClient) {\n\t\tthrowError(errorCode, parameterName, ERROR_URI, authorizationCodeRequestAuthentication, registeredClient, null);\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName, String errorUri,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tRegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, errorUri);\n\t\tthrowError(error, parameterName, authorizationCodeRequestAuthentication, registeredClient,\n\t\t\t\tauthorizationRequest);\n\t}\n\n\tprivate static void throwError(OAuth2Error error, String parameterName,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tRegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) {\n\n\t\tString redirectUri = resolveRedirectUri(authorizationCodeRequestAuthentication, authorizationRequest,\n\t\t\t\tregisteredClient);\n\t\tif (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST)\n\t\t\t\t&& (parameterName.equals(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t\t\t|| parameterName.equals(OAuth2ParameterNames.STATE))) {\n\t\t\tredirectUri = null; // Prevent redirects\n\t\t}\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tauthorizationCodeRequestAuthentication.getAuthorizationUri(),\n\t\t\t\tauthorizationCodeRequestAuthentication.getClientId(),\n\t\t\t\t(Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri,\n\t\t\t\tauthorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(),\n\t\t\t\tauthorizationCodeRequestAuthentication.getAdditionalParameters());\n\n\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error,\n\t\t\t\tauthorizationCodeRequestAuthenticationResult);\n\t}\n\n\tprivate static String resolveRedirectUri(\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tOAuth2AuthorizationRequest authorizationRequest, RegisteredClient registeredClient) {\n\n\t\tif (authorizationCodeRequestAuthentication != null\n\t\t\t\t&& StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())) {\n\t\t\treturn authorizationCodeRequestAuthentication.getRedirectUri();\n\t\t}\n\t\tif (authorizationRequest != null && StringUtils.hasText(authorizationRequest.getRedirectUri())) {\n\t\t\treturn authorizationRequest.getRedirectUri();\n\t\t}\n\t\tif (registeredClient != null) {\n\t\t\treturn registeredClient.getRedirectUris().iterator().next();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation for the OAuth 2.0 Authorization Request used\n * in the Authorization Code Grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider\n * @see OAuth2AuthorizationConsentAuthenticationProvider\n */\npublic class OAuth2AuthorizationCodeRequestAuthenticationToken\n\t\textends AbstractOAuth2AuthorizationCodeRequestAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1946164725241393094L;\n\n\tprivate final OAuth2AuthorizationCode authorizationCode;\n\n\tprivate boolean validated;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the\n\t * provided parameters.\n\t * @param authorizationUri the authorization URI\n\t * @param clientId the client identifier\n\t * @param principal the {@code Principal} (Resource Owner)\n\t * @param redirectUri the redirect uri\n\t * @param state the state\n\t * @param scopes the requested scope(s)\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId,\n\t\t\tAuthentication principal, @Nullable String redirectUri, @Nullable String state,\n\t\t\t@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(authorizationUri, clientId, principal, redirectUri, state, scopes, additionalParameters);\n\t\tthis.authorizationCode = null;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the\n\t * provided parameters.\n\t * @param authorizationUri the authorization URI\n\t * @param clientId the client identifier\n\t * @param principal the {@code Principal} (Resource Owner)\n\t * @param authorizationCode the {@link OAuth2AuthorizationCode}\n\t * @param redirectUri the redirect uri\n\t * @param state the state\n\t * @param scopes the authorized scope(s)\n\t */\n\tpublic OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId,\n\t\t\tAuthentication principal, OAuth2AuthorizationCode authorizationCode, @Nullable String redirectUri,\n\t\t\t@Nullable String state, @Nullable Set<String> scopes) {\n\t\tsuper(authorizationUri, clientId, principal, redirectUri, state, scopes, null);\n\t\tAssert.notNull(authorizationCode, \"authorizationCode cannot be null\");\n\t\tthis.authorizationCode = authorizationCode;\n\t\tsetAuthenticated(true);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationCode}.\n\t * @return the {@link OAuth2AuthorizationCode}\n\t */\n\t@Nullable\n\tpublic OAuth2AuthorizationCode getAuthorizationCode() {\n\t\treturn this.authorizationCode;\n\t}\n\n\tfinal boolean isValidated() {\n\t\treturn this.validated;\n\t}\n\n\tfinal void setValidated(boolean validated) {\n\t\tthis.validated = validated;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Consumer} providing access to the\n * {@link OAuth2AuthorizationCodeRequestAuthenticationContext} containing an\n * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and is the default\n * {@link OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer)\n * authentication validator} used for validating specific OAuth 2.0 Authorization Request\n * parameters used in the Authorization Code Grant.\n *\n * <p>\n * The default implementation first validates\n * {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()} and then\n * {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}. If validation\n * fails, an {@link OAuth2AuthorizationCodeRequestAuthenticationException} is thrown.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationCodeRequestAuthenticationContext\n * @see OAuth2AuthorizationCodeRequestAuthenticationToken\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer)\n * @see OAuth2PushedAuthorizationRequestAuthenticationProvider#setAuthenticationValidator(Consumer)\n */\npublic final class OAuth2AuthorizationCodeRequestAuthenticationValidator\n\t\timplements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1\";\n\n\tprivate static final String PKCE_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1\";\n\n\tprivate static final Log LOGGER = LogFactory.getLog(OAuth2AuthorizationCodeRequestAuthenticationValidator.class);\n\n\tstatic final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_AUTHORIZATION_GRANT_TYPE_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validateAuthorizationGrantType;\n\n\tstatic final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_CODE_CHALLENGE_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validateCodeChallenge;\n\n\tstatic final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_PROMPT_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validatePrompt;\n\n\t/**\n\t * The default validator for\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()}.\n\t */\n\tpublic static final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_REDIRECT_URI_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validateRedirectUri;\n\n\t/**\n\t * The default validator for\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}.\n\t */\n\tpublic static final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_SCOPE_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validateScope;\n\n\tprivate final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator = DEFAULT_REDIRECT_URI_VALIDATOR\n\t\t.andThen(DEFAULT_SCOPE_VALIDATOR);\n\n\t@Override\n\tpublic void accept(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tthis.authenticationValidator.accept(authenticationContext);\n\t}\n\n\tprivate static void validateAuthorizationGrantType(\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext\n\t\t\t.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\t\tif (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: requested grant_type is not allowed for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrowError(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\t\tauthorizationCodeRequestAuthentication, registeredClient);\n\t\t}\n\t}\n\n\tprivate static void validateRedirectUri(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext\n\t\t\t.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\n\t\tString requestedRedirectUri = authorizationCodeRequestAuthentication.getRedirectUri();\n\n\t\tif (StringUtils.hasText(requestedRedirectUri)) {\n\t\t\t// ***** redirect_uri is available in authorization request\n\n\t\t\tUriComponents requestedRedirect = null;\n\t\t\ttry {\n\t\t\t\trequestedRedirect = UriComponentsBuilder.fromUriString(requestedRedirectUri).build();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t}\n\t\t\tif (requestedRedirect == null || requestedRedirect.getFragment() != null) {\n\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\tLOGGER.debug(LogMessage.format(\"Invalid request: redirect_uri is missing or contains a fragment\"\n\t\t\t\t\t\t\t+ \" for registered client '%s'\", registeredClient.getId()));\n\t\t\t\t}\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,\n\t\t\t\t\t\tauthorizationCodeRequestAuthentication, registeredClient);\n\t\t\t}\n\n\t\t\tif (!isLoopbackAddress(requestedRedirect.getHost())) {\n\t\t\t\t// As per\n\t\t\t\t// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-22#section-4.1.3\n\t\t\t\t// When comparing client redirect URIs against pre-registered URIs,\n\t\t\t\t// authorization servers MUST utilize exact string matching.\n\t\t\t\tif (!registeredClient.getRedirectUris().contains(requestedRedirectUri)) {\n\t\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthentication, registeredClient);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// As per\n\t\t\t\t// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-08#section-8.4.2\n\t\t\t\t// The authorization server MUST allow any port to be specified at the\n\t\t\t\t// time of the request for loopback IP redirect URIs, to accommodate\n\t\t\t\t// clients that obtain an available ephemeral port from the operating\n\t\t\t\t// system at the time of the request.\n\t\t\t\tboolean validRedirectUri = false;\n\t\t\t\tfor (String registeredRedirectUri : registeredClient.getRedirectUris()) {\n\t\t\t\t\tUriComponentsBuilder registeredRedirect = UriComponentsBuilder.fromUriString(registeredRedirectUri);\n\t\t\t\t\tregisteredRedirect.port(requestedRedirect.getPort());\n\t\t\t\t\tif (registeredRedirect.build().toString().equals(requestedRedirect.toString())) {\n\t\t\t\t\t\tvalidRedirectUri = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!validRedirectUri) {\n\t\t\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\t\t\tLOGGER.debug(LogMessage.format(\n\t\t\t\t\t\t\t\t\"Invalid request: redirect_uri does not match for registered client '%s'\",\n\t\t\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t\t\t}\n\t\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthentication, registeredClient);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\telse {\n\t\t\t// ***** redirect_uri is NOT available in authorization request\n\n\t\t\tif (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)\n\t\t\t\t\t|| registeredClient.getRedirectUris().size() != 1) {\n\t\t\t\t// redirect_uri is REQUIRED for OpenID Connect\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,\n\t\t\t\t\t\tauthorizationCodeRequestAuthentication, registeredClient);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void validateScope(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext\n\t\t\t.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\n\t\tSet<String> requestedScopes = authorizationCodeRequestAuthentication.getScopes();\n\t\tSet<String> allowedScopes = registeredClient.getScopes();\n\t\tif (!requestedScopes.isEmpty() && !allowedScopes.containsAll(requestedScopes)) {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\n\t\t\t\t\t\tLogMessage.format(\"Invalid request: requested scope is not allowed for registered client '%s'\",\n\t\t\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tauthorizationCodeRequestAuthentication, registeredClient);\n\t\t}\n\t}\n\n\tprivate static void validateCodeChallenge(\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext\n\t\t\t.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\n\t\t// code_challenge (REQUIRED for public clients) - RFC 7636 (PKCE)\n\t\tString codeChallenge = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()\n\t\t\t.get(PkceParameterNames.CODE_CHALLENGE);\n\t\tif (StringUtils.hasText(codeChallenge)) {\n\t\t\tString codeChallengeMethod = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()\n\t\t\t\t.get(PkceParameterNames.CODE_CHALLENGE_METHOD);\n\t\t\tif (!StringUtils.hasText(codeChallengeMethod) || !\"S256\".equals(codeChallengeMethod)) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, PKCE_ERROR_URI,\n\t\t\t\t\t\tauthorizationCodeRequestAuthentication, registeredClient);\n\t\t\t}\n\t\t}\n\t\telse if (registeredClient.getClientSettings().isRequireProofKey()) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE, PKCE_ERROR_URI,\n\t\t\t\t\tauthorizationCodeRequestAuthentication, registeredClient);\n\t\t}\n\t}\n\n\tprivate static void validatePrompt(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext\n\t\t\t.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\n\t\t// prompt (OPTIONAL for OpenID Connect 1.0 Authentication Request)\n\t\tif (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {\n\t\t\tString prompt = (String) authorizationCodeRequestAuthentication.getAdditionalParameters().get(\"prompt\");\n\t\t\tif (StringUtils.hasText(prompt)) {\n\t\t\t\tSet<String> promptValues = new HashSet<>(\n\t\t\t\t\t\tArrays.asList(StringUtils.delimitedListToStringArray(prompt, \" \")));\n\t\t\t\tif (promptValues.contains(OidcPrompt.NONE)) {\n\t\t\t\t\tif (promptValues.contains(OidcPrompt.LOGIN) || promptValues.contains(OidcPrompt.CONSENT)\n\t\t\t\t\t\t\t|| promptValues.contains(OidcPrompt.SELECT_ACCOUNT)) {\n\t\t\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, \"prompt\", authorizationCodeRequestAuthentication,\n\t\t\t\t\t\t\t\tregisteredClient);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean isLoopbackAddress(String host) {\n\t\tif (!StringUtils.hasText(host)) {\n\t\t\treturn false;\n\t\t}\n\t\t// IPv6 loopback address should either be \"0:0:0:0:0:0:0:1\" or \"::1\"\n\t\tif (\"[0:0:0:0:0:0:0:1]\".equals(host) || \"[::1]\".equals(host)) {\n\t\t\treturn true;\n\t\t}\n\t\t// IPv4 loopback address ranges from 127.0.0.1 to 127.255.255.255\n\t\tString[] ipv4Octets = host.split(\"\\\\.\");\n\t\tif (ipv4Octets.length != 4) {\n\t\t\treturn false;\n\t\t}\n\t\ttry {\n\t\t\tint[] address = new int[ipv4Octets.length];\n\t\t\tfor (int i = 0; i < ipv4Octets.length; i++) {\n\t\t\t\taddress[i] = Integer.parseInt(ipv4Octets[i]);\n\t\t\t}\n\t\t\treturn address[0] == 127 && address[1] >= 0 && address[1] <= 255 && address[2] >= 0 && address[2] <= 255\n\t\t\t\t\t&& address[3] >= 1 && address[3] <= 255;\n\t\t}\n\t\tcatch (NumberFormatException ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tRegisteredClient registeredClient) {\n\t\tthrowError(errorCode, parameterName, ERROR_URI, authorizationCodeRequestAuthentication, registeredClient);\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName, String errorUri,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tRegisteredClient registeredClient) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, errorUri);\n\t\tthrowError(error, parameterName, authorizationCodeRequestAuthentication, registeredClient);\n\t}\n\n\tprivate static void throwError(OAuth2Error error, String parameterName,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tRegisteredClient registeredClient) {\n\n\t\tString redirectUri = StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())\n\t\t\t\t? authorizationCodeRequestAuthentication.getRedirectUri()\n\t\t\t\t: registeredClient.getRedirectUris().iterator().next();\n\t\tif (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST)\n\t\t\t\t&& parameterName.equals(OAuth2ParameterNames.REDIRECT_URI)) {\n\t\t\tredirectUri = null; // Prevent redirects\n\t\t}\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tauthorizationCodeRequestAuthentication.getAuthorizationUri(),\n\t\t\t\tauthorizationCodeRequestAuthentication.getClientId(),\n\t\t\t\t(Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri,\n\t\t\t\tauthorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(),\n\t\t\t\tauthorizationCodeRequestAuthentication.getAdditionalParameters());\n\t\tauthorizationCodeRequestAuthenticationResult.setAuthenticated(true);\n\n\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error,\n\t\t\t\tauthorizationCodeRequestAuthenticationResult);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthenticationContext} that holds an\n * {@link OAuth2AuthorizationConsent.Builder} and additional information and is used when\n * customizing the building of the {@link OAuth2AuthorizationConsent}.\n *\n * @author Steve Riesenberg\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthenticationContext\n * @see OAuth2AuthorizationConsent\n * @see OAuth2AuthorizationConsentAuthenticationProvider#setAuthorizationConsentCustomizer(Consumer)\n */\npublic final class OAuth2AuthorizationConsentAuthenticationContext implements OAuth2AuthenticationContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OAuth2AuthorizationConsentAuthenticationContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationConsent.Builder authorization consent\n\t * builder}.\n\t * @return the {@link OAuth2AuthorizationConsent.Builder}\n\t */\n\tpublic OAuth2AuthorizationConsent.Builder getAuthorizationConsent() {\n\t\treturn get(OAuth2AuthorizationConsent.Builder.class);\n\t}\n\n\t/**\n\t * Returns the {@link RegisteredClient registered client}.\n\t * @return the {@link RegisteredClient}\n\t */\n\tpublic RegisteredClient getRegisteredClient() {\n\t\treturn get(RegisteredClient.class);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Authorization authorization}.\n\t * @return the {@link OAuth2Authorization}\n\t */\n\tpublic OAuth2Authorization getAuthorization() {\n\t\treturn get(OAuth2Authorization.class);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest authorization request}.\n\t * @return the {@link OAuth2AuthorizationRequest}\n\t */\n\tpublic OAuth2AuthorizationRequest getAuthorizationRequest() {\n\t\treturn get(OAuth2AuthorizationRequest.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided\n\t * {@link OAuth2AuthorizationConsentAuthenticationToken}.\n\t * @param authentication the {@link OAuth2AuthorizationConsentAuthenticationToken}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OAuth2AuthorizationConsentAuthenticationToken authentication) {\n\t\treturn new Builder(authentication);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AuthorizationConsentAuthenticationContext}.\n\t */\n\tpublic static final class Builder\n\t\t\textends AbstractBuilder<OAuth2AuthorizationConsentAuthenticationContext, Builder> {\n\n\t\tprivate Builder(OAuth2AuthorizationConsentAuthenticationToken authentication) {\n\t\t\tsuper(authentication);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AuthorizationConsent.Builder authorization consent\n\t\t * builder}.\n\t\t * @param authorizationConsent the {@link OAuth2AuthorizationConsent.Builder}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationConsent(OAuth2AuthorizationConsent.Builder authorizationConsent) {\n\t\t\treturn put(OAuth2AuthorizationConsent.Builder.class, authorizationConsent);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link RegisteredClient registered client}.\n\t\t * @param registeredClient the {@link RegisteredClient}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder registeredClient(RegisteredClient registeredClient) {\n\t\t\treturn put(RegisteredClient.class, registeredClient);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2Authorization authorization}.\n\t\t * @param authorization the {@link OAuth2Authorization}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorization(OAuth2Authorization authorization) {\n\t\t\treturn put(OAuth2Authorization.class, authorization);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AuthorizationRequest authorization request}.\n\t\t * @param authorizationRequest the {@link OAuth2AuthorizationRequest}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationRequest(OAuth2AuthorizationRequest authorizationRequest) {\n\t\t\treturn put(OAuth2AuthorizationRequest.class, authorizationRequest);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AuthorizationConsentAuthenticationContext}.\n\t\t * @return the {@link OAuth2AuthorizationConsentAuthenticationContext}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2AuthorizationConsentAuthenticationContext build() {\n\t\t\tAssert.notNull(get(OAuth2AuthorizationConsent.Builder.class), \"authorizationConsentBuilder cannot be null\");\n\t\t\tAssert.notNull(get(RegisteredClient.class), \"registeredClient cannot be null\");\n\t\t\tOAuth2Authorization authorization = get(OAuth2Authorization.class);\n\t\t\tAssert.notNull(authorization, \"authorization cannot be null\");\n\t\t\tif (authorization.getAuthorizationGrantType().equals(AuthorizationGrantType.AUTHORIZATION_CODE)) {\n\t\t\t\tAssert.notNull(get(OAuth2AuthorizationRequest.class), \"authorizationRequest cannot be null\");\n\t\t\t}\n\t\t\treturn new OAuth2AuthorizationConsentAuthenticationContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization\n * Consent used in the Authorization Code Grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationConsentAuthenticationToken\n * @see OAuth2AuthorizationConsent\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see OAuth2AuthorizationConsentService\n */\npublic final class OAuth2AuthorizationConsentAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1\";\n\n\tprivate static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2AuthorizationConsentService authorizationConsentService;\n\n\tprivate OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator = new OAuth2AuthorizationCodeGenerator();\n\n\tprivate Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationConsentAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t * @param authorizationConsentService the authorization consent service\n\t */\n\tpublic OAuth2AuthorizationConsentAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2AuthorizationConsentService authorizationConsentService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(authorizationConsentService, \"authorizationConsentService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.authorizationConsentService = authorizationConsentService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (authentication instanceof OAuth2DeviceAuthorizationConsentAuthenticationToken) {\n\t\t\t// This is NOT an OAuth 2.0 Authorization Consent for the Authorization Code\n\t\t\t// Grant,\n\t\t\t// return null and let OAuth2DeviceAuthorizationConsentAuthenticationProvider\n\t\t\t// handle it instead\n\t\t\treturn null;\n\t\t}\n\n\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = (OAuth2AuthorizationConsentAuthenticationToken) authentication;\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(authorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE);\n\t\tif (authorization == null) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, authorizationConsentAuthentication,\n\t\t\t\t\tnull, null);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with authorization consent state\");\n\t\t}\n\n\t\t// The 'in-flight' authorization must be associated to the current principal\n\t\tAuthentication principal = (Authentication) authorizationConsentAuthentication.getPrincipal();\n\t\tif (!isPrincipalAuthenticated(principal) || !principal.getName().equals(authorization.getPrincipalName())) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, authorizationConsentAuthentication,\n\t\t\t\t\tnull, null);\n\t\t}\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(authorizationConsentAuthentication.getClientId());\n\t\tif (registeredClient == null || !registeredClient.getId().equals(authorization.getRegisteredClientId())) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\t\tauthorizationConsentAuthentication, registeredClient, null);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tSet<String> requestedScopes = authorizationRequest.getScopes();\n\t\tSet<String> authorizedScopes = new HashSet<>(authorizationConsentAuthentication.getScopes());\n\t\tif (!requestedScopes.containsAll(authorizedScopes)) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authorizationConsentAuthentication,\n\t\t\t\t\tregisteredClient, authorizationRequest);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated authorization consent request parameters\");\n\t\t}\n\n\t\tOAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService\n\t\t\t.findById(authorization.getRegisteredClientId(), authorization.getPrincipalName());\n\t\tSet<String> currentAuthorizedScopes = (currentAuthorizationConsent != null)\n\t\t\t\t? currentAuthorizationConsent.getScopes() : Collections.emptySet();\n\n\t\tif (!currentAuthorizedScopes.isEmpty()) {\n\t\t\tfor (String requestedScope : requestedScopes) {\n\t\t\t\tif (currentAuthorizedScopes.contains(requestedScope)) {\n\t\t\t\t\tauthorizedScopes.add(requestedScope);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!authorizedScopes.isEmpty() && requestedScopes.contains(OidcScopes.OPENID)) {\n\t\t\t// 'openid' scope is auto-approved as it does not require consent\n\t\t\tauthorizedScopes.add(OidcScopes.OPENID);\n\t\t}\n\n\t\tOAuth2AuthorizationConsent.Builder authorizationConsentBuilder;\n\t\tif (currentAuthorizationConsent != null) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Retrieved existing authorization consent\");\n\t\t\t}\n\t\t\tauthorizationConsentBuilder = OAuth2AuthorizationConsent.from(currentAuthorizationConsent);\n\t\t}\n\t\telse {\n\t\t\tauthorizationConsentBuilder = OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(),\n\t\t\t\t\tauthorization.getPrincipalName());\n\t\t}\n\t\tauthorizedScopes.forEach(authorizationConsentBuilder::scope);\n\n\t\tif (this.authorizationConsentCustomizer != null) {\n\t\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationConsentAuthenticationContext authorizationConsentAuthenticationContext =\n\t\t\t\t\tOAuth2AuthorizationConsentAuthenticationContext.with(authorizationConsentAuthentication)\n\t\t\t\t\t\t\t.authorizationConsent(authorizationConsentBuilder)\n\t\t\t\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t\t\t\t.authorization(authorization)\n\t\t\t\t\t\t\t.authorizationRequest(authorizationRequest)\n\t\t\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tthis.authorizationConsentCustomizer.accept(authorizationConsentAuthenticationContext);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Customized authorization consent\");\n\t\t\t}\n\t\t}\n\n\t\tSet<GrantedAuthority> authorities = new HashSet<>();\n\t\tauthorizationConsentBuilder.authorities(authorities::addAll);\n\n\t\tif (authorities.isEmpty()) {\n\t\t\t// Authorization consent denied (or revoked)\n\t\t\tif (currentAuthorizationConsent != null) {\n\t\t\t\tthis.authorizationConsentService.remove(currentAuthorizationConsent);\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(\"Revoked authorization consent\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.authorizationService.remove(authorization);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Removed authorization\");\n\t\t\t}\n\t\t\tthrowError(OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\t\tauthorizationConsentAuthentication, registeredClient, authorizationRequest);\n\t\t}\n\n\t\tOAuth2AuthorizationConsent authorizationConsent = authorizationConsentBuilder.build();\n\t\tif (!authorizationConsent.equals(currentAuthorizationConsent)) {\n\t\t\tthis.authorizationConsentService.save(authorizationConsent);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Saved authorization consent\");\n\t\t\t}\n\t\t}\n\n\t\tOAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext(authorizationConsentAuthentication,\n\t\t\t\tregisteredClient, authorization, authorizedScopes);\n\t\tOAuth2AuthorizationCode authorizationCode = this.authorizationCodeGenerator.generate(tokenContext);\n\t\tif (authorizationCode == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the authorization code.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated authorization code\");\n\t\t}\n\n\t\tOAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization)\n\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t.token(authorizationCode)\n\t\t\t.attributes((attrs) -> attrs.remove(OAuth2ParameterNames.STATE))\n\t\t\t.build();\n\t\tthis.authorizationService.save(updatedAuthorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t}\n\n\t\tString redirectUri = authorizationRequest.getRedirectUri();\n\t\tif (!StringUtils.hasText(redirectUri)) {\n\t\t\tredirectUri = registeredClient.getRedirectUris().iterator().next();\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated authorization consent request\");\n\t\t}\n\n\t\treturn new OAuth2AuthorizationCodeRequestAuthenticationToken(authorizationRequest.getAuthorizationUri(),\n\t\t\t\tregisteredClient.getClientId(), principal, authorizationCode, redirectUri,\n\t\t\t\tauthorizationRequest.getState(), authorizedScopes);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2AuthorizationConsentAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2TokenGenerator} that generates the\n\t * {@link OAuth2AuthorizationCode}.\n\t * @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates\n\t * the {@link OAuth2AuthorizationCode}\n\t */\n\tpublic void setAuthorizationCodeGenerator(\n\t\t\tOAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator) {\n\t\tAssert.notNull(authorizationCodeGenerator, \"authorizationCodeGenerator cannot be null\");\n\t\tthis.authorizationCodeGenerator = authorizationCodeGenerator;\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an\n\t * {@link OAuth2AuthorizationConsent.Builder} and additional context information.\n\t *\n\t * <p>\n\t * The following context attributes are available:\n\t * <ul>\n\t * <li>The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization\n\t * consent prior to\n\t * {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.</li>\n\t * <li>The {@link Authentication} of type\n\t * {@link OAuth2AuthorizationConsentAuthenticationToken}.</li>\n\t * <li>The {@link RegisteredClient} associated with the authorization request.</li>\n\t * <li>The {@link OAuth2Authorization} associated with the state token presented in\n\t * the authorization consent request.</li>\n\t * <li>The {@link OAuth2AuthorizationRequest} associated with the authorization\n\t * consent request.</li>\n\t * </ul>\n\t * @param authorizationConsentCustomizer the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an\n\t * {@link OAuth2AuthorizationConsent.Builder}\n\t */\n\tpublic void setAuthorizationConsentCustomizer(\n\t\t\tConsumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer) {\n\t\tAssert.notNull(authorizationConsentCustomizer, \"authorizationConsentCustomizer cannot be null\");\n\t\tthis.authorizationConsentCustomizer = authorizationConsentCustomizer;\n\t}\n\n\tprivate static OAuth2TokenContext createAuthorizationCodeTokenContext(\n\t\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication,\n\t\t\tRegisteredClient registeredClient, OAuth2Authorization authorization, Set<String> authorizedScopes) {\n\n\t\t// @formatter:off\n\t\treturn DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal((Authentication) authorizationConsentAuthentication.getPrincipal())\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.tokenType(new OAuth2TokenType(OAuth2ParameterNames.CODE))\n\t\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.authorizationGrant(authorizationConsentAuthentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static boolean isPrincipalAuthenticated(Authentication principal) {\n\t\treturn principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass())\n\t\t\t\t&& principal.isAuthenticated();\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName,\n\t\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication,\n\t\t\tRegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, ERROR_URI);\n\t\tthrowError(error, parameterName, authorizationConsentAuthentication, registeredClient, authorizationRequest);\n\t}\n\n\tprivate static void throwError(OAuth2Error error, String parameterName,\n\t\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication,\n\t\t\tRegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) {\n\n\t\tString redirectUri = resolveRedirectUri(authorizationRequest, registeredClient);\n\t\tif (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST)\n\t\t\t\t&& (parameterName.equals(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t\t\t|| parameterName.equals(OAuth2ParameterNames.STATE))) {\n\t\t\tredirectUri = null; // Prevent redirects\n\t\t}\n\n\t\tString state = (authorizationRequest != null) ? authorizationRequest.getState()\n\t\t\t\t: authorizationConsentAuthentication.getState();\n\t\tSet<String> requestedScopes = (authorizationRequest != null) ? authorizationRequest.getScopes()\n\t\t\t\t: authorizationConsentAuthentication.getScopes();\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tauthorizationConsentAuthentication.getAuthorizationUri(),\n\t\t\t\tauthorizationConsentAuthentication.getClientId(),\n\t\t\t\t(Authentication) authorizationConsentAuthentication.getPrincipal(), redirectUri, state, requestedScopes,\n\t\t\t\tnull);\n\n\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error,\n\t\t\t\tauthorizationCodeRequestAuthenticationResult);\n\t}\n\n\tprivate static String resolveRedirectUri(OAuth2AuthorizationRequest authorizationRequest,\n\t\t\tRegisteredClient registeredClient) {\n\t\tif (authorizationRequest != null && StringUtils.hasText(authorizationRequest.getRedirectUri())) {\n\t\t\treturn authorizationRequest.getRedirectUri();\n\t\t}\n\t\tif (registeredClient != null) {\n\t\t\treturn registeredClient.getRedirectUris().iterator().next();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation for the OAuth 2.0 Authorization Consent used\n * in the Authorization Code Grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationConsentAuthenticationProvider\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider\n */\npublic class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2111287271882598208L;\n\n\tprivate final String authorizationUri;\n\n\tprivate final String clientId;\n\n\tprivate final Authentication principal;\n\n\tprivate final String state;\n\n\tprivate final Set<String> scopes;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationConsentAuthenticationToken} using the\n\t * provided parameters.\n\t * @param authorizationUri the authorization URI\n\t * @param clientId the client identifier\n\t * @param principal the {@code Principal} (Resource Owner)\n\t * @param state the state\n\t * @param scopes the requested (or authorized) scope(s)\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2AuthorizationConsentAuthenticationToken(String authorizationUri, String clientId,\n\t\t\tAuthentication principal, String state, @Nullable Set<String> scopes,\n\t\t\t@Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(authorizationUri, \"authorizationUri cannot be empty\");\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.hasText(state, \"state cannot be empty\");\n\t\tthis.authorizationUri = authorizationUri;\n\t\tthis.clientId = clientId;\n\t\tthis.principal = principal;\n\t\tthis.state = state;\n\t\tthis.scopes = Collections.unmodifiableSet((scopes != null) ? new HashSet<>(scopes) : Collections.emptySet());\n\t\tthis.additionalParameters = Collections.unmodifiableMap(\n\t\t\t\t(additionalParameters != null) ? new HashMap<>(additionalParameters) : Collections.emptyMap());\n\t\tsetAuthenticated(true);\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the authorization URI.\n\t * @return the authorization URI\n\t */\n\tpublic String getAuthorizationUri() {\n\t\treturn this.authorizationUri;\n\t}\n\n\t/**\n\t * Returns the client identifier.\n\t * @return the client identifier\n\t */\n\tpublic String getClientId() {\n\t\treturn this.clientId;\n\t}\n\n\t/**\n\t * Returns the state.\n\t * @return the state\n\t */\n\tpublic String getState() {\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Returns the requested (or authorized) scope(s).\n\t * @return the requested (or authorized) scope(s), or an empty {@code Set} if not\n\t * available\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n\t/**\n\t * Returns the additional parameters.\n\t * @return the additional parameters, or an empty {@code Map} if not available\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\n\n/**\n * Base implementation of an {@link Authentication} representing an OAuth 2.0\n * Authorization Grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see AuthorizationGrantType\n * @see OAuth2ClientAuthenticationToken\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.3\">Section\n * 1.3 Authorization Grant</a>\n */\npublic class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1715946281123199051L;\n\n\tprivate final AuthorizationGrantType authorizationGrantType;\n\n\tprivate final Authentication clientPrincipal;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\t/**\n\t * Sub-class constructor.\n\t * @param authorizationGrantType the authorization grant type\n\t * @param clientPrincipal the authenticated client principal\n\t * @param additionalParameters the additional parameters\n\t */\n\tprotected OAuth2AuthorizationGrantAuthenticationToken(AuthorizationGrantType authorizationGrantType,\n\t\t\tAuthentication clientPrincipal, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(authorizationGrantType, \"authorizationGrantType cannot be null\");\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\t\tthis.authorizationGrantType = authorizationGrantType;\n\t\tthis.clientPrincipal = clientPrincipal;\n\t\tthis.additionalParameters = Collections.unmodifiableMap(\n\t\t\t\t(additionalParameters != null) ? new HashMap<>(additionalParameters) : Collections.emptyMap());\n\t}\n\n\t/**\n\t * Returns the authorization grant type.\n\t * @return the authorization grant type\n\t */\n\tpublic AuthorizationGrantType getGrantType() {\n\t\treturn this.authorizationGrantType;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.clientPrincipal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the additional parameters.\n\t * @return the additional parameters\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthenticationContext} that holds an\n * {@link OAuth2ClientAuthenticationToken} and additional information and is used when\n * validating an OAuth 2.0 Client Authentication.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthenticationContext\n * @see OAuth2ClientAuthenticationToken\n * @see X509ClientCertificateAuthenticationProvider#setCertificateVerifier(Consumer)\n */\npublic final class OAuth2ClientAuthenticationContext implements OAuth2AuthenticationContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OAuth2ClientAuthenticationContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link RegisteredClient registered client}.\n\t * @return the {@link RegisteredClient}\n\t */\n\tpublic RegisteredClient getRegisteredClient() {\n\t\treturn get(RegisteredClient.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided\n\t * {@link OAuth2ClientAuthenticationToken}.\n\t * @param authentication the {@link OAuth2ClientAuthenticationToken}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OAuth2ClientAuthenticationToken authentication) {\n\t\treturn new Builder(authentication);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2ClientAuthenticationContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OAuth2ClientAuthenticationContext, Builder> {\n\n\t\tprivate Builder(OAuth2ClientAuthenticationToken authentication) {\n\t\t\tsuper(authentication);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link RegisteredClient registered client}.\n\t\t * @param registeredClient the {@link RegisteredClient}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder registeredClient(RegisteredClient registeredClient) {\n\t\t\treturn put(RegisteredClient.class, registeredClient);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2ClientAuthenticationContext}.\n\t\t * @return the {@link OAuth2ClientAuthenticationContext}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2ClientAuthenticationContext build() {\n\t\t\tAssert.notNull(get(RegisteredClient.class), \"registeredClient cannot be null\");\n\t\t\treturn new OAuth2ClientAuthenticationContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.Transient;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for OAuth 2.0 Client Authentication.\n *\n * @author Joe Grandja\n * @author Patryk Kostrzewa\n * @author Anoop Garlapati\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see RegisteredClient\n * @see JwtClientAssertionAuthenticationProvider\n * @see ClientSecretAuthenticationProvider\n * @see PublicClientAuthenticationProvider\n */\n@Transient\npublic class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7150784632941221304L;\n\n\tprivate final String clientId;\n\n\tprivate final RegisteredClient registeredClient;\n\n\tprivate final ClientAuthenticationMethod clientAuthenticationMethod;\n\n\tprivate final Object credentials;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\t/**\n\t * Constructs an {@code OAuth2ClientAuthenticationToken} using the provided\n\t * parameters.\n\t * @param clientId the client identifier\n\t * @param clientAuthenticationMethod the authentication method used by the client\n\t * @param credentials the client credentials\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2ClientAuthenticationToken(String clientId, ClientAuthenticationMethod clientAuthenticationMethod,\n\t\t\t@Nullable Object credentials, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\tAssert.notNull(clientAuthenticationMethod, \"clientAuthenticationMethod cannot be null\");\n\t\tthis.clientId = clientId;\n\t\tthis.registeredClient = null;\n\t\tthis.clientAuthenticationMethod = clientAuthenticationMethod;\n\t\tthis.credentials = credentials;\n\t\tthis.additionalParameters = Collections\n\t\t\t.unmodifiableMap((additionalParameters != null) ? additionalParameters : Collections.emptyMap());\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2ClientAuthenticationToken} using the provided\n\t * parameters.\n\t * @param registeredClient the authenticated registered client\n\t * @param clientAuthenticationMethod the authentication method used by the client\n\t * @param credentials the client credentials\n\t */\n\tpublic OAuth2ClientAuthenticationToken(RegisteredClient registeredClient,\n\t\t\tClientAuthenticationMethod clientAuthenticationMethod, @Nullable Object credentials) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(registeredClient, \"registeredClient cannot be null\");\n\t\tAssert.notNull(clientAuthenticationMethod, \"clientAuthenticationMethod cannot be null\");\n\t\tthis.clientId = registeredClient.getClientId();\n\t\tthis.registeredClient = registeredClient;\n\t\tthis.clientAuthenticationMethod = clientAuthenticationMethod;\n\t\tthis.credentials = credentials;\n\t\tthis.additionalParameters = Collections.emptyMap();\n\t\tsetAuthenticated(true);\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.clientId;\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\t/**\n\t * Returns the authenticated {@link RegisteredClient registered client}, or\n\t * {@code null} if not authenticated.\n\t * @return the authenticated {@link RegisteredClient}, or {@code null} if not\n\t * authenticated\n\t */\n\t@Nullable\n\tpublic RegisteredClient getRegisteredClient() {\n\t\treturn this.registeredClient;\n\t}\n\n\t/**\n\t * Returns the {@link ClientAuthenticationMethod authentication method} used by the\n\t * client.\n\t * @return the {@link ClientAuthenticationMethod} used by the client\n\t */\n\tpublic ClientAuthenticationMethod getClientAuthenticationMethod() {\n\t\treturn this.clientAuthenticationMethod;\n\t}\n\n\t/**\n\t * Returns the additional parameters.\n\t * @return the additional parameters\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthenticationContext} that holds an\n * {@link OAuth2ClientCredentialsAuthenticationToken} and additional information and is\n * used when validating the OAuth 2.0 Client Credentials Grant Request.\n *\n * @author Adam Pilling\n * @since 7.0\n * @see OAuth2AuthenticationContext\n * @see OAuth2ClientCredentialsAuthenticationToken\n * @see OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer)\n */\npublic final class OAuth2ClientCredentialsAuthenticationContext implements OAuth2AuthenticationContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OAuth2ClientCredentialsAuthenticationContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link RegisteredClient registered client}.\n\t * @return the {@link RegisteredClient}\n\t */\n\tpublic RegisteredClient getRegisteredClient() {\n\t\treturn get(RegisteredClient.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided\n\t * {@link OAuth2ClientCredentialsAuthenticationToken}.\n\t * @param authentication the {@link OAuth2ClientCredentialsAuthenticationToken}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OAuth2ClientCredentialsAuthenticationToken authentication) {\n\t\treturn new Builder(authentication);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2ClientCredentialsAuthenticationContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OAuth2ClientCredentialsAuthenticationContext, Builder> {\n\n\t\tprivate Builder(OAuth2ClientCredentialsAuthenticationToken authentication) {\n\t\t\tsuper(authentication);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link RegisteredClient registered client}.\n\t\t * @param registeredClient the {@link RegisteredClient}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder registeredClient(RegisteredClient registeredClient) {\n\t\t\treturn put(RegisteredClient.class, registeredClient);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2ClientCredentialsAuthenticationContext}.\n\t\t * @return the {@link OAuth2ClientCredentialsAuthenticationContext}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2ClientCredentialsAuthenticationContext build() {\n\t\t\tAssert.notNull(get(RegisteredClient.class), \"registeredClient cannot be null\");\n\t\t\treturn new OAuth2ClientCredentialsAuthenticationContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Client Credentials\n * Grant.\n *\n * @author Alexey Nesterov\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2ClientCredentialsAuthenticationToken\n * @see OAuth2AccessTokenAuthenticationToken\n * @see OAuth2AuthorizationService\n * @see OAuth2TokenGenerator\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.4\">Section 4.4 Client\n * Credentials Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2\">Section 4.4.2 Access\n * Token Request</a>\n */\npublic final class OAuth2ClientCredentialsAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;\n\n\tprivate Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator = new OAuth2ClientCredentialsAuthenticationValidator();\n\n\t/**\n\t * Constructs an {@code OAuth2ClientCredentialsAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param authorizationService the authorization service\n\t * @param tokenGenerator the token generator\n\t */\n\tpublic OAuth2ClientCredentialsAuthenticationProvider(OAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(tokenGenerator, \"tokenGenerator cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.tokenGenerator = tokenGenerator;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = (OAuth2ClientCredentialsAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(clientCredentialsAuthentication);\n\t\tRegisteredClient registeredClient = clientPrincipal.getRegisteredClient();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tif (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.CLIENT_CREDENTIALS)) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: requested grant_type is not allowed\" + \" for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);\n\t\t}\n\n\t\tOAuth2ClientCredentialsAuthenticationContext authenticationContext = OAuth2ClientCredentialsAuthenticationContext\n\t\t\t.with(clientCredentialsAuthentication)\n\t\t\t.registeredClient(registeredClient)\n\t\t\t.build();\n\t\tthis.authenticationValidator.accept(authenticationContext);\n\n\t\tSet<String> authorizedScopes = new LinkedHashSet<>(clientCredentialsAuthentication.getScopes());\n\n\t\t// Verify the DPoP Proof (if available)\n\t\tJwt dPoPProof = DPoPProofVerifier.verifyIfAvailable(clientCredentialsAuthentication);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated token request parameters\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(clientPrincipal)\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.authorizationGrant(clientCredentialsAuthentication);\n\t\t// @formatter:on\n\t\tif (dPoPProof != null) {\n\t\t\ttokenContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProof);\n\t\t}\n\t\tOAuth2TokenContext tokenContext = tokenContextBuilder.build();\n\n\t\tOAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);\n\t\tif (generatedAccessToken == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the access token.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated access token\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tOAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(clientPrincipal.getName())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.authorizedScopes(authorizedScopes);\n\t\t// @formatter:on\n\n\t\tOAuth2AccessToken accessToken = OAuth2AuthenticationProviderUtils.accessToken(authorizationBuilder,\n\t\t\t\tgeneratedAccessToken, tokenContext);\n\n\t\tOAuth2Authorization authorization = authorizationBuilder.build();\n\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t\t// This log is kept separate for consistency with other providers\n\t\t\tthis.logger.trace(\"Authenticated token request\");\n\t\t}\n\n\t\treturn new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2ClientCredentialsAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2ClientCredentialsAuthenticationContext} and is responsible for\n\t * validating specific OAuth 2.0 Client Credentials Grant Request parameters\n\t * associated in the {@link OAuth2ClientCredentialsAuthenticationToken}. The default\n\t * authentication validator is {@link OAuth2ClientCredentialsAuthenticationValidator}.\n\t *\n\t * <p>\n\t * <b>NOTE:</b> The authentication validator MUST throw\n\t * {@link OAuth2AuthenticationException} if validation fails.\n\t * @param authenticationValidator the {@code Consumer} providing access to the\n\t * {@link OAuth2ClientCredentialsAuthenticationContext} and is responsible for\n\t * validating specific OAuth 2.0 Client Credentials Grant Request parameters\n\t */\n\tpublic void setAuthenticationValidator(\n\t\t\tConsumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator) {\n\t\tAssert.notNull(authenticationValidator, \"authenticationValidator cannot be null\");\n\t\tthis.authenticationValidator = authenticationValidator;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\n\n/**\n * An {@link Authentication} implementation used for the OAuth 2.0 Client Credentials\n * Grant.\n *\n * @author Alexey Nesterov\n * @since 7.0\n * @see OAuth2AuthorizationGrantAuthenticationToken\n * @see OAuth2ClientCredentialsAuthenticationProvider\n */\npublic class OAuth2ClientCredentialsAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {\n\n\tprivate final Set<String> scopes;\n\n\t/**\n\t * Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided\n\t * parameters.\n\t * @param clientPrincipal the authenticated client principal\n\t * @param scopes the requested scope(s)\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal, @Nullable Set<String> scopes,\n\t\t\t@Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(AuthorizationGrantType.CLIENT_CREDENTIALS, clientPrincipal, additionalParameters);\n\t\tthis.scopes = Collections.unmodifiableSet((scopes != null) ? new HashSet<>(scopes) : Collections.emptySet());\n\t}\n\n\t/**\n\t * Returns the requested scope(s).\n\t * @return the requested scope(s), or an empty {@code Set} if not available\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\n\n/**\n * A {@code Consumer} providing access to the\n * {@link OAuth2ClientCredentialsAuthenticationContext} containing an\n * {@link OAuth2ClientCredentialsAuthenticationToken} and is the default\n * {@link OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer)\n * authentication validator} used for validating specific OAuth 2.0 Client Credentials\n * Grant Request parameters.\n *\n * <p>\n * The default implementation validates\n * {@link OAuth2ClientCredentialsAuthenticationToken#getScopes()}. If validation fails, an\n * {@link OAuth2AuthenticationException} is thrown.\n *\n * @author Adam Pilling\n * @since 7.0\n * @see OAuth2ClientCredentialsAuthenticationContext\n * @see OAuth2ClientCredentialsAuthenticationToken\n * @see OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer)\n */\npublic final class OAuth2ClientCredentialsAuthenticationValidator\n\t\timplements Consumer<OAuth2ClientCredentialsAuthenticationContext> {\n\n\tprivate static final Log LOGGER = LogFactory.getLog(OAuth2ClientCredentialsAuthenticationValidator.class);\n\n\t/**\n\t * The default validator for\n\t * {@link OAuth2ClientCredentialsAuthenticationToken#getScopes()}.\n\t */\n\tpublic static final Consumer<OAuth2ClientCredentialsAuthenticationContext> DEFAULT_SCOPE_VALIDATOR = OAuth2ClientCredentialsAuthenticationValidator::validateScope;\n\n\tprivate final Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator = DEFAULT_SCOPE_VALIDATOR;\n\n\t@Override\n\tpublic void accept(OAuth2ClientCredentialsAuthenticationContext authenticationContext) {\n\t\tthis.authenticationValidator.accept(authenticationContext);\n\t}\n\n\tprivate static void validateScope(OAuth2ClientCredentialsAuthenticationContext authenticationContext) {\n\t\tOAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = authenticationContext\n\t\t\t.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\n\t\tSet<String> requestedScopes = clientCredentialsAuthentication.getScopes();\n\t\tSet<String> allowedScopes = registeredClient.getScopes();\n\t\tif (!requestedScopes.isEmpty() && !allowedScopes.containsAll(requestedScopes)) {\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: requested scope is not allowed\" + \" for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientRegistrationAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientMetadataClaimNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.converter.OAuth2ClientRegistrationRegisteredClientConverter;\nimport org.springframework.security.oauth2.server.authorization.converter.RegisteredClientOAuth2ClientRegistrationConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Dynamic Client\n * Registration Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see OAuth2ClientRegistrationAuthenticationToken\n * @see PasswordEncoder\n * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7591#section-3\">3. Client\n * Registration Endpoint</a>\n */\npublic final class OAuth2ClientRegistrationAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2\";\n\n\tprivate static final String DEFAULT_CLIENT_REGISTRATION_AUTHORIZED_SCOPE = \"client.create\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate Converter<RegisteredClient, OAuth2ClientRegistration> clientRegistrationConverter;\n\n\tprivate Converter<OAuth2ClientRegistration, RegisteredClient> registeredClientConverter;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\tprivate boolean openRegistrationAllowed;\n\n\t/**\n\t * Constructs an {@code OAuth2ClientRegistrationAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t */\n\tpublic OAuth2ClientRegistrationAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.clientRegistrationConverter = new RegisteredClientOAuth2ClientRegistrationConverter();\n\t\tthis.registeredClientConverter = new OAuth2ClientRegistrationRegisteredClientConverter();\n\t\tthis.passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2ClientRegistrationAuthenticationToken clientRegistrationAuthentication = (OAuth2ClientRegistrationAuthenticationToken) authentication;\n\n\t\t// Check if \"initial\" access token is not provided\n\t\tAbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;\n\t\tif (clientRegistrationAuthentication.getPrincipal() != null && AbstractOAuth2TokenAuthenticationToken.class\n\t\t\t.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) {\n\t\t\taccessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication\n\t\t\t\t.getPrincipal();\n\t\t}\n\t\tif (accessTokenAuthentication == null) {\n\t\t\tif (this.openRegistrationAllowed) {\n\t\t\t\treturn registerClient(clientRegistrationAuthentication, null);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t}\n\t\t}\n\n\t\t// Validate the \"initial\" access token\n\t\tif (!accessTokenAuthentication.isAuthenticated()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tString accessTokenValue = accessTokenAuthentication.getToken().getTokenValue();\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue,\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tif (authorization == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with initial access token\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();\n\t\tif (!authorizedAccessToken.isActive()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\t\tcheckScope(authorizedAccessToken, Collections.singleton(DEFAULT_CLIENT_REGISTRATION_AUTHORIZED_SCOPE));\n\n\t\treturn registerClient(clientRegistrationAuthentication, authorization);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2ClientRegistrationAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting an {@link OAuth2ClientRegistration}\n\t * to a {@link RegisteredClient}.\n\t * @param registeredClientConverter the {@link Converter} used for converting an\n\t * {@link OAuth2ClientRegistration} to a {@link RegisteredClient}\n\t */\n\tpublic void setRegisteredClientConverter(\n\t\t\tConverter<OAuth2ClientRegistration, RegisteredClient> registeredClientConverter) {\n\t\tAssert.notNull(registeredClientConverter, \"registeredClientConverter cannot be null\");\n\t\tthis.registeredClientConverter = registeredClientConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting a {@link RegisteredClient} to an\n\t * {@link OAuth2ClientRegistration}.\n\t * @param clientRegistrationConverter the {@link Converter} used for converting a\n\t * {@link RegisteredClient} to an {@link OAuth2ClientRegistration}\n\t */\n\tpublic void setClientRegistrationConverter(\n\t\t\tConverter<RegisteredClient, OAuth2ClientRegistration> clientRegistrationConverter) {\n\t\tAssert.notNull(clientRegistrationConverter, \"clientRegistrationConverter cannot be null\");\n\t\tthis.clientRegistrationConverter = clientRegistrationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link PasswordEncoder} used to encode the\n\t * {@link RegisteredClient#getClientSecret() client secret}. If not set, the client\n\t * secret will be encoded using\n\t * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}.\n\t * @param passwordEncoder the {@link PasswordEncoder} used to encode the client secret\n\t */\n\tpublic void setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder cannot be null\");\n\t\tthis.passwordEncoder = passwordEncoder;\n\t}\n\n\t/**\n\t * Set to {@code true} if open client registration (with no initial access token) is\n\t * allowed. The default is {@code false}.\n\t * @param openRegistrationAllowed {@code true} if open client registration is allowed,\n\t * {@code false} otherwise\n\t */\n\tpublic void setOpenRegistrationAllowed(boolean openRegistrationAllowed) {\n\t\tthis.openRegistrationAllowed = openRegistrationAllowed;\n\t}\n\n\tprivate OAuth2ClientRegistrationAuthenticationToken registerClient(\n\t\t\tOAuth2ClientRegistrationAuthenticationToken clientRegistrationAuthentication,\n\t\t\tOAuth2Authorization authorization) {\n\n\t\tif (!isValidRedirectUris(clientRegistrationAuthentication.getClientRegistration().getRedirectUris())) {\n\t\t\tthrowInvalidClientRegistration(OAuth2ErrorCodes.INVALID_REDIRECT_URI,\n\t\t\t\t\tOAuth2ClientMetadataClaimNames.REDIRECT_URIS);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated client registration request parameters\");\n\t\t}\n\n\t\tRegisteredClient registeredClient = this.registeredClientConverter\n\t\t\t.convert(clientRegistrationAuthentication.getClientRegistration());\n\n\t\tif (StringUtils.hasText(registeredClient.getClientSecret())) {\n\t\t\t// Encode the client secret\n\t\t\tRegisteredClient updatedRegisteredClient = RegisteredClient.from(registeredClient)\n\t\t\t\t.clientSecret(this.passwordEncoder.encode(registeredClient.getClientSecret()))\n\t\t\t\t.build();\n\t\t\tthis.registeredClientRepository.save(updatedRegisteredClient);\n\t\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()\n\t\t\t\t.equals(clientRegistrationAuthentication.getClientRegistration()\n\t\t\t\t\t.getTokenEndpointAuthenticationMethod())) {\n\t\t\t\t// Return the hashed client_secret\n\t\t\t\tregisteredClient = updatedRegisteredClient;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthis.registeredClientRepository.save(registeredClient);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved registered client\");\n\t\t}\n\n\t\tif (authorization != null) {\n\t\t\t// Invalidate the \"initial\" access token as it can only be used once\n\t\t\tOAuth2Authorization.Builder builder = OAuth2Authorization.from(authorization)\n\t\t\t\t.invalidate(authorization.getAccessToken().getToken());\n\t\t\tif (authorization.getRefreshToken() != null) {\n\t\t\t\tbuilder.invalidate(authorization.getRefreshToken().getToken());\n\t\t\t}\n\t\t\tauthorization = builder.build();\n\t\t\tthis.authorizationService.save(authorization);\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Saved authorization with invalidated initial access token\");\n\t\t\t}\n\t\t}\n\n\t\tOAuth2ClientRegistration clientRegistration = this.clientRegistrationConverter.convert(registeredClient);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated client registration request\");\n\t\t}\n\n\t\tOAuth2ClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\t(Authentication) clientRegistrationAuthentication.getPrincipal(), clientRegistration);\n\t\tclientRegistrationAuthenticationResult.setDetails(clientRegistrationAuthentication.getDetails());\n\t\treturn clientRegistrationAuthenticationResult;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static void checkScope(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken,\n\t\t\tSet<String> requiredScope) {\n\t\tCollection<String> authorizedScope = Collections.emptySet();\n\t\tif (authorizedAccessToken.getClaims().containsKey(OAuth2ParameterNames.SCOPE)) {\n\t\t\tauthorizedScope = (Collection<String>) authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE);\n\t\t}\n\t\tif (!authorizedScope.containsAll(requiredScope)) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t}\n\t\telse if (authorizedScope.size() != requiredScope.size()) {\n\t\t\t// Restrict the access token to only contain the required scope\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\t}\n\n\tprivate static boolean isValidRedirectUris(List<String> redirectUris) {\n\t\tif (CollectionUtils.isEmpty(redirectUris)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tfor (String redirectUri : redirectUris) {\n\t\t\ttry {\n\t\t\t\tURI validRedirectUri = new URI(redirectUri);\n\t\t\t\tif (validRedirectUri.getFragment() != null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (URISyntaxException ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate static void throwInvalidClientRegistration(String errorCode, String fieldName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"Invalid Client Registration: \" + fieldName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientRegistrationAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for the OAuth 2.0 Dynamic Client\n * Registration Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OAuth2ClientRegistration\n * @see OAuth2ClientRegistrationAuthenticationProvider\n */\npublic class OAuth2ClientRegistrationAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 7135429161909989115L;\n\n\t@Nullable\n\tprivate final Authentication principal;\n\n\tprivate final OAuth2ClientRegistration clientRegistration;\n\n\t/**\n\t * Constructs an {@code OAuth2ClientRegistrationAuthenticationToken} using the\n\t * provided parameters.\n\t * @param principal the authenticated principal\n\t * @param clientRegistration the client registration\n\t */\n\tpublic OAuth2ClientRegistrationAuthenticationToken(@Nullable Authentication principal,\n\t\t\tOAuth2ClientRegistration clientRegistration) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tthis.principal = principal;\n\t\tthis.clientRegistration = clientRegistration;\n\t\tif (principal != null) {\n\t\t\tsetAuthenticated(principal.isAuthenticated());\n\t\t}\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the client registration.\n\t * @return the client registration\n\t */\n\tpublic OAuth2ClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation for the Device Authorization Consent\n * used in the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2DeviceAuthorizationConsentAuthenticationToken\n * @see OAuth2AuthorizationConsent\n * @see OAuth2DeviceAuthorizationRequestAuthenticationProvider\n * @see OAuth2DeviceVerificationAuthenticationProvider\n * @see OAuth2DeviceCodeAuthenticationProvider\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see OAuth2AuthorizationConsentService\n */\npublic final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\tstatic final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2AuthorizationConsentService authorizationConsentService;\n\n\tprivate Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationProvider} using\n\t * the provided parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t * @param authorizationConsentService the authorization consent service\n\t */\n\tpublic OAuth2DeviceAuthorizationConsentAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2AuthorizationConsentService authorizationConsentService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(authorizationConsentService, \"authorizationConsentService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.authorizationConsentService = authorizationConsentService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken deviceAuthorizationConsentAuthentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) authentication;\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(deviceAuthorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE);\n\t\tif (authorization == null) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with device authorization consent state\");\n\t\t}\n\n\t\t// The authorization must be associated to the current principal\n\t\tAuthentication principal = (Authentication) deviceAuthorizationConsentAuthentication.getPrincipal();\n\t\tif (!isPrincipalAuthenticated(principal) || !principal.getName().equals(authorization.getPrincipalName())) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);\n\t\t}\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(deviceAuthorizationConsentAuthentication.getClientId());\n\t\tif (registeredClient == null || !registeredClient.getId().equals(authorization.getRegisteredClientId())) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tSet<String> requestedScopes = authorization.getAttribute(OAuth2ParameterNames.SCOPE);\n\t\tSet<String> authorizedScopes = new HashSet<>(deviceAuthorizationConsentAuthentication.getScopes());\n\t\tif (!requestedScopes.containsAll(authorizedScopes)) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated device authorization consent request parameters\");\n\t\t}\n\n\t\tOAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService\n\t\t\t.findById(authorization.getRegisteredClientId(), principal.getName());\n\t\tSet<String> currentAuthorizedScopes = (currentAuthorizationConsent != null)\n\t\t\t\t? currentAuthorizationConsent.getScopes() : Collections.emptySet();\n\n\t\tif (!currentAuthorizedScopes.isEmpty()) {\n\t\t\tfor (String requestedScope : requestedScopes) {\n\t\t\t\tif (currentAuthorizedScopes.contains(requestedScope)) {\n\t\t\t\t\tauthorizedScopes.add(requestedScope);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tOAuth2AuthorizationConsent.Builder authorizationConsentBuilder;\n\t\tif (currentAuthorizationConsent != null) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Retrieved existing authorization consent\");\n\t\t\t}\n\t\t\tauthorizationConsentBuilder = OAuth2AuthorizationConsent.from(currentAuthorizationConsent);\n\t\t}\n\t\telse {\n\t\t\tauthorizationConsentBuilder = OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(),\n\t\t\t\t\tprincipal.getName());\n\t\t}\n\t\tauthorizedScopes.forEach(authorizationConsentBuilder::scope);\n\n\t\tif (this.authorizationConsentCustomizer != null) {\n\t\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationConsentAuthenticationContext authorizationConsentAuthenticationContext =\n\t\t\t\t\tOAuth2AuthorizationConsentAuthenticationContext.with(deviceAuthorizationConsentAuthentication)\n\t\t\t\t\t\t\t.authorizationConsent(authorizationConsentBuilder)\n\t\t\t\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t\t\t\t.authorization(authorization)\n\t\t\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tthis.authorizationConsentCustomizer.accept(authorizationConsentAuthenticationContext);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Customized authorization consent\");\n\t\t\t}\n\t\t}\n\n\t\tSet<GrantedAuthority> authorities = new HashSet<>();\n\t\tauthorizationConsentBuilder.authorities(authorities::addAll);\n\n\t\tOAuth2Authorization.Token<OAuth2DeviceCode> deviceCodeToken = authorization.getToken(OAuth2DeviceCode.class);\n\t\tOAuth2Authorization.Token<OAuth2UserCode> userCodeToken = authorization.getToken(OAuth2UserCode.class);\n\n\t\tif (authorities.isEmpty()) {\n\t\t\t// Authorization consent denied (or revoked)\n\t\t\tif (currentAuthorizationConsent != null) {\n\t\t\t\tthis.authorizationConsentService.remove(currentAuthorizationConsent);\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(\"Revoked authorization consent\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tauthorization = OAuth2Authorization.from(authorization)\n\t\t\t\t.invalidate(deviceCodeToken.getToken())\n\t\t\t\t.invalidate(userCodeToken.getToken())\n\t\t\t\t.attributes((attrs) -> attrs.remove(OAuth2ParameterNames.STATE))\n\t\t\t\t.build();\n\t\t\tthis.authorizationService.save(authorization);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Invalidated device code and user code because authorization consent was denied\");\n\t\t\t}\n\t\t\tthrowError(OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tOAuth2AuthorizationConsent authorizationConsent = authorizationConsentBuilder.build();\n\t\tif (!authorizationConsent.equals(currentAuthorizationConsent)) {\n\t\t\tthis.authorizationConsentService.save(authorizationConsent);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Saved authorization consent\");\n\t\t\t}\n\t\t}\n\n\t\tauthorization = OAuth2Authorization.from(authorization)\n\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t.invalidate(userCodeToken.getToken())\n\t\t\t.attributes((attrs) -> attrs.remove(OAuth2ParameterNames.STATE))\n\t\t\t.attributes((attrs) -> attrs.remove(OAuth2ParameterNames.SCOPE))\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization with authorized scopes\");\n\t\t\t// This log is kept separate for consistency with other providers\n\t\t\tthis.logger.trace(\"Authenticated device authorization consent request\");\n\t\t}\n\n\t\treturn new OAuth2DeviceVerificationAuthenticationToken(principal,\n\t\t\t\tdeviceAuthorizationConsentAuthentication.getUserCode(), registeredClient.getClientId());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2DeviceAuthorizationConsentAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an\n\t * {@link OAuth2AuthorizationConsent.Builder} and additional context information.\n\t *\n\t * <p>\n\t * The following context attributes are available:\n\t * <ul>\n\t * <li>The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization\n\t * consent prior to\n\t * {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.</li>\n\t * <li>The {@link Authentication} of type\n\t * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}.</li>\n\t * <li>The {@link RegisteredClient} associated with the device authorization\n\t * request.</li>\n\t * <li>The {@link OAuth2Authorization} associated with the state token presented in\n\t * the device authorization consent request.</li>\n\t * </ul>\n\t * @param authorizationConsentCustomizer the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an\n\t * {@link OAuth2AuthorizationConsent.Builder}\n\t */\n\tpublic void setAuthorizationConsentCustomizer(\n\t\t\tConsumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer) {\n\t\tAssert.notNull(authorizationConsentCustomizer, \"authorizationConsentCustomizer cannot be null\");\n\t\tthis.authorizationConsentCustomizer = authorizationConsentCustomizer;\n\t}\n\n\tprivate static boolean isPrincipalAuthenticated(Authentication principal) {\n\t\treturn principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass())\n\t\t\t\t&& principal.isAuthenticated();\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation for the Device Authorization Consent used in\n * the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider\n */\npublic class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2AuthorizationConsentAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 3789252233721827596L;\n\n\tprivate final String userCode;\n\n\tprivate final Set<String> requestedScopes;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the\n\t * provided parameters.\n\t * @param authorizationUri the authorization URI\n\t * @param clientId the client identifier\n\t * @param principal the {@code Principal} (Resource Owner)\n\t * @param userCode the user code associated with the device authorization response\n\t * @param state the state\n\t * @param authorizedScopes the authorized scope(s)\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2DeviceAuthorizationConsentAuthenticationToken(String authorizationUri, String clientId,\n\t\t\tAuthentication principal, String userCode, String state, @Nullable Set<String> authorizedScopes,\n\t\t\t@Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(authorizationUri, clientId, principal, state, authorizedScopes, additionalParameters);\n\t\tAssert.hasText(userCode, \"userCode cannot be empty\");\n\t\tthis.userCode = userCode;\n\t\tthis.requestedScopes = null;\n\t\tsetAuthenticated(false);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the\n\t * provided parameters.\n\t * @param authorizationUri the authorization URI\n\t * @param clientId the client identifier\n\t * @param principal the {@code Principal} (Resource Owner)\n\t * @param userCode the user code associated with the device authorization response\n\t * @param state the state\n\t * @param requestedScopes the requested scope(s)\n\t * @param authorizedScopes the authorized scope(s)\n\t */\n\tpublic OAuth2DeviceAuthorizationConsentAuthenticationToken(String authorizationUri, String clientId,\n\t\t\tAuthentication principal, String userCode, String state, @Nullable Set<String> requestedScopes,\n\t\t\t@Nullable Set<String> authorizedScopes) {\n\t\tsuper(authorizationUri, clientId, principal, state, authorizedScopes, null);\n\t\tAssert.hasText(userCode, \"userCode cannot be empty\");\n\t\tthis.userCode = userCode;\n\t\tthis.requestedScopes = Collections\n\t\t\t.unmodifiableSet((requestedScopes != null) ? new HashSet<>(requestedScopes) : Collections.emptySet());\n\t\tsetAuthenticated(true);\n\t}\n\n\t/**\n\t * Returns the user code.\n\t * @return the user code\n\t */\n\tpublic String getUserCode() {\n\t\treturn this.userCode;\n\t}\n\n\t/**\n\t * Returns the requested scopes.\n\t * @return the requested scopes\n\t */\n\tpublic Set<String> getRequestedScopes() {\n\t\treturn this.requestedScopes;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.BytesKeyGenerator;\nimport org.springframework.security.crypto.keygen.KeyGenerators;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for the Device Authorization Request\n * used in the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2DeviceAuthorizationRequestAuthenticationToken\n * @see OAuth2DeviceVerificationAuthenticationProvider\n * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider\n * @see OAuth2DeviceCodeAuthenticationProvider\n * @see OAuth2AuthorizationService\n * @see OAuth2TokenGenerator\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc8628\">OAuth 2.0\n * Device Authorization Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.1\">Section 3.1 Device\n * Authorization Request</a>\n */\npublic final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\tstatic final OAuth2TokenType DEVICE_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.DEVICE_CODE);\n\tstatic final OAuth2TokenType USER_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.USER_CODE);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2TokenGenerator<OAuth2DeviceCode> deviceCodeGenerator = new OAuth2DeviceCodeGenerator();\n\n\tprivate OAuth2TokenGenerator<OAuth2UserCode> userCodeGenerator = new OAuth2UserCodeGenerator();\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationProvider} using\n\t * the provided parameters.\n\t * @param authorizationService the authorization service\n\t */\n\tpublic OAuth2DeviceAuthorizationRequestAuthenticationProvider(OAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(deviceAuthorizationRequestAuthentication);\n\t\tRegisteredClient registeredClient = clientPrincipal.getRegisteredClient();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tif (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.DEVICE_CODE)) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: requested grant_type is not allowed\" + \" for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrowError(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tSet<String> requestedScopes = deviceAuthorizationRequestAuthentication.getScopes();\n\t\tif (!CollectionUtils.isEmpty(requestedScopes)) {\n\t\t\tfor (String requestedScope : requestedScopes) {\n\t\t\t\tif (!registeredClient.getScopes().contains(requestedScope)) {\n\t\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (requestedScopes.contains(OidcScopes.OPENID)) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE);\n\t\t\t}\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated device authorization request parameters\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(clientPrincipal)\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.authorizationGrant(deviceAuthorizationRequestAuthentication);\n\t\t// @formatter:on\n\n\t\t// Generate a high-entropy string to use as the device code\n\t\tOAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(DEVICE_CODE_TOKEN_TYPE).build();\n\t\tOAuth2DeviceCode deviceCode = this.deviceCodeGenerator.generate(tokenContext);\n\t\tif (deviceCode == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the device code.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated device code\");\n\t\t}\n\n\t\t// Generate a low-entropy string to use as the user code\n\t\ttokenContext = tokenContextBuilder.tokenType(USER_CODE_TOKEN_TYPE).build();\n\t\tOAuth2UserCode userCode = this.userCodeGenerator.generate(tokenContext);\n\t\tif (userCode == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the user code.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated user code\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(clientPrincipal.getName())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(deviceCode)\n\t\t\t\t.token(userCode)\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, new HashSet<>(requestedScopes))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated device authorization request\");\n\t\t}\n\n\t\treturn new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, requestedScopes, deviceCode,\n\t\t\t\tuserCode);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2DeviceAuthorizationRequestAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2DeviceCode}.\n\t * @param deviceCodeGenerator the {@link OAuth2TokenGenerator} that generates the\n\t * {@link OAuth2DeviceCode}\n\t */\n\tpublic void setDeviceCodeGenerator(OAuth2TokenGenerator<OAuth2DeviceCode> deviceCodeGenerator) {\n\t\tAssert.notNull(deviceCodeGenerator, \"deviceCodeGenerator cannot be null\");\n\t\tthis.deviceCodeGenerator = deviceCodeGenerator;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2UserCode}.\n\t * @param userCodeGenerator the {@link OAuth2TokenGenerator} that generates the\n\t * {@link OAuth2UserCode}\n\t */\n\tpublic void setUserCodeGenerator(OAuth2TokenGenerator<OAuth2UserCode> userCodeGenerator) {\n\t\tAssert.notNull(userCodeGenerator, \"userCodeGenerator cannot be null\");\n\t\tthis.userCodeGenerator = userCodeGenerator;\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n\tprivate static final class OAuth2DeviceCodeGenerator implements OAuth2TokenGenerator<OAuth2DeviceCode> {\n\n\t\tprivate final StringKeyGenerator deviceCodeGenerator = new Base64StringKeyGenerator(\n\t\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\t\t@Nullable\n\t\t@Override\n\t\tpublic OAuth2DeviceCode generate(OAuth2TokenContext context) {\n\t\t\tif (context.getTokenType() == null\n\t\t\t\t\t|| !OAuth2ParameterNames.DEVICE_CODE.equals(context.getTokenType().getValue())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tInstant issuedAt = Instant.now();\n\t\t\tInstant expiresAt = issuedAt\n\t\t\t\t.plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive());\n\t\t\treturn new OAuth2DeviceCode(this.deviceCodeGenerator.generateKey(), issuedAt, expiresAt);\n\t\t}\n\n\t}\n\n\tprivate static final class UserCodeStringKeyGenerator implements StringKeyGenerator {\n\n\t\t// @formatter:off\n\t\tprivate static final char[] VALID_CHARS = {\n\t\t\t\t'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',\n\t\t\t\t'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Z'\n\t\t};\n\t\t// @formatter:on\n\n\t\tprivate final BytesKeyGenerator keyGenerator = KeyGenerators.secureRandom(8);\n\n\t\t@Override\n\t\tpublic String generateKey() {\n\t\t\tbyte[] bytes = this.keyGenerator.generateKey();\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tfor (byte b : bytes) {\n\t\t\t\tint offset = Math.abs(b % 20);\n\t\t\t\tsb.append(VALID_CHARS[offset]);\n\t\t\t}\n\t\t\tsb.insert(4, '-');\n\t\t\treturn sb.toString();\n\t\t}\n\n\t}\n\n\tprivate static final class OAuth2UserCodeGenerator implements OAuth2TokenGenerator<OAuth2UserCode> {\n\n\t\tprivate final StringKeyGenerator userCodeGenerator = new UserCodeStringKeyGenerator();\n\n\t\t@Nullable\n\t\t@Override\n\t\tpublic OAuth2UserCode generate(OAuth2TokenContext context) {\n\t\t\tif (context.getTokenType() == null\n\t\t\t\t\t|| !OAuth2ParameterNames.USER_CODE.equals(context.getTokenType().getValue())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tInstant issuedAt = Instant.now();\n\t\t\tInstant expiresAt = issuedAt\n\t\t\t\t.plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive());\n\t\t\treturn new OAuth2UserCode(this.userCodeGenerator.generateKey(), issuedAt, expiresAt);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation for the Device Authorization Request used in\n * the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OAuth2ClientAuthenticationToken\n * @see OAuth2DeviceAuthorizationRequestAuthenticationProvider\n */\npublic class OAuth2DeviceAuthorizationRequestAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -561059025431630645L;\n\n\tprivate final Authentication clientPrincipal;\n\n\tprivate final String authorizationUri;\n\n\tprivate final Set<String> scopes;\n\n\tprivate final OAuth2DeviceCode deviceCode;\n\n\tprivate final OAuth2UserCode userCode;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the\n\t * provided parameters.\n\t * @param clientPrincipal the authenticated client principal\n\t * @param authorizationUri the authorization {@code URI}\n\t * @param scopes the requested scope(s)\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2DeviceAuthorizationRequestAuthenticationToken(Authentication clientPrincipal, String authorizationUri,\n\t\t\t@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\t\tAssert.hasText(authorizationUri, \"authorizationUri cannot be empty\");\n\t\tthis.clientPrincipal = clientPrincipal;\n\t\tthis.authorizationUri = authorizationUri;\n\t\tthis.scopes = Collections.unmodifiableSet((scopes != null) ? new HashSet<>(scopes) : Collections.emptySet());\n\t\tthis.additionalParameters = Collections.unmodifiableMap(\n\t\t\t\t(additionalParameters != null) ? new HashMap<>(additionalParameters) : Collections.emptyMap());\n\t\tthis.deviceCode = null;\n\t\tthis.userCode = null;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the\n\t * provided parameters.\n\t * @param clientPrincipal the authenticated client principal\n\t * @param scopes the requested scope(s)\n\t * @param deviceCode the {@link OAuth2DeviceCode}\n\t * @param userCode the {@link OAuth2UserCode}\n\t */\n\tpublic OAuth2DeviceAuthorizationRequestAuthenticationToken(Authentication clientPrincipal,\n\t\t\t@Nullable Set<String> scopes, OAuth2DeviceCode deviceCode, OAuth2UserCode userCode) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\t\tAssert.notNull(deviceCode, \"deviceCode cannot be null\");\n\t\tAssert.notNull(userCode, \"userCode cannot be null\");\n\t\tthis.clientPrincipal = clientPrincipal;\n\t\tthis.scopes = Collections.unmodifiableSet((scopes != null) ? new HashSet<>(scopes) : Collections.emptySet());\n\t\tthis.deviceCode = deviceCode;\n\t\tthis.userCode = userCode;\n\t\tthis.authorizationUri = null;\n\t\tthis.additionalParameters = Collections.emptyMap();\n\t\tsetAuthenticated(true);\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.clientPrincipal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the authorization {@code URI}.\n\t * @return the authorization {@code URI}\n\t */\n\tpublic String getAuthorizationUri() {\n\t\treturn this.authorizationUri;\n\t}\n\n\t/**\n\t * Returns the requested scope(s).\n\t * @return the requested scope(s)\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n\t/**\n\t * Returns the device code.\n\t * @return the device code\n\t */\n\tpublic OAuth2DeviceCode getDeviceCode() {\n\t\treturn this.deviceCode;\n\t}\n\n\t/**\n\t * Returns the user code.\n\t * @return the user code\n\t */\n\tpublic OAuth2UserCode getUserCode() {\n\t\treturn this.userCode;\n\t}\n\n\t/**\n\t * Returns the additional parameters.\n\t * @return the additional parameters\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation for the Device Access Token Request\n * used in the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2DeviceCodeAuthenticationToken\n * @see OAuth2AccessTokenAuthenticationToken\n * @see OAuth2DeviceAuthorizationRequestAuthenticationProvider\n * @see OAuth2DeviceVerificationAuthenticationProvider\n * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider\n * @see OAuth2AuthorizationService\n * @see OAuth2TokenGenerator\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc8628\">OAuth 2.0\n * Device Authorization Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.4\">Section 3.4 Device Access\n * Token Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.5\">Section 3.5 Device Access\n * Token Response</a>\n */\npublic final class OAuth2DeviceCodeAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String DEFAULT_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate static final String DEVICE_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.5\";\n\tstatic final OAuth2TokenType DEVICE_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.DEVICE_CODE);\n\tstatic final String EXPIRED_TOKEN = \"expired_token\";\n\tstatic final String AUTHORIZATION_PENDING = \"authorization_pending\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceCodeAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param authorizationService the authorization service\n\t * @param tokenGenerator the token generator\n\t */\n\tpublic OAuth2DeviceCodeAuthenticationProvider(OAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(tokenGenerator, \"tokenGenerator cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.tokenGenerator = tokenGenerator;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2DeviceCodeAuthenticationToken deviceCodeAuthentication = (OAuth2DeviceCodeAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(deviceCodeAuthentication);\n\t\tRegisteredClient registeredClient = clientPrincipal.getRegisteredClient();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(deviceCodeAuthentication.getDeviceCode(), DEVICE_CODE_TOKEN_TYPE);\n\t\tif (authorization == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with device code\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);\n\t\tOAuth2Authorization.Token<OAuth2DeviceCode> deviceCode = authorization.getToken(OAuth2DeviceCode.class);\n\n\t\tif (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {\n\t\t\tif (!deviceCode.isInvalidated()) {\n\t\t\t\t// Invalidate the device code given that a different client is attempting\n\t\t\t\t// to use it\n\t\t\t\tauthorization = OAuth2Authorization.from(authorization).invalidate(deviceCode.getToken()).build();\n\t\t\t\tthis.authorizationService.save(authorization);\n\t\t\t\tif (this.logger.isWarnEnabled()) {\n\t\t\t\t\tthis.logger.warn(LogMessage.format(\"Invalidated device code used by registered client '%s'\",\n\t\t\t\t\t\t\tauthorization.getRegisteredClientId()));\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\t// In https://www.rfc-editor.org/rfc/rfc8628.html#section-3.5,\n\t\t// the following error codes are defined:\n\n\t\t// expired_token\n\t\t// The \"device_code\" has expired, and the device authorization\n\t\t// session has concluded. The client MAY commence a new device\n\t\t// authorization request but SHOULD wait for user interaction before\n\t\t// restarting to avoid unnecessary polling.\n\t\tif (deviceCode.isExpired()) {\n\t\t\tif (!deviceCode.isInvalidated()) {\n\t\t\t\t// Invalidate the device code\n\t\t\t\tauthorization = OAuth2Authorization.from(authorization).invalidate(deviceCode.getToken()).build();\n\t\t\t\tthis.authorizationService.save(authorization);\n\t\t\t\tif (this.logger.isWarnEnabled()) {\n\t\t\t\t\tthis.logger.warn(LogMessage.format(\"Invalidated device code used by registered client '%s'\",\n\t\t\t\t\t\t\tauthorization.getRegisteredClientId()));\n\t\t\t\t}\n\t\t\t}\n\t\t\tOAuth2Error error = new OAuth2Error(EXPIRED_TOKEN, null, DEVICE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\t// authorization_pending\n\t\t// The authorization request is still pending as the end user hasn't\n\t\t// yet completed the user-interaction steps (Section 3.3). The\n\t\t// client SHOULD repeat the access token request to the token\n\t\t// endpoint (a process known as polling). Before each new request,\n\t\t// the client MUST wait at least the number of seconds specified by\n\t\t// the \"interval\" parameter of the device authorization response (see\n\t\t// Section 3.2), or 5 seconds if none was provided, and respect any\n\t\t// increase in the polling interval required by the \"slow_down\"\n\t\t// error.\n\t\tif (!userCode.isInvalidated()) {\n\t\t\tOAuth2Error error = new OAuth2Error(AUTHORIZATION_PENDING, null, DEVICE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\t// slow_down\n\t\t// A variant of \"authorization_pending\", the authorization request is\n\t\t// still pending and polling should continue, but the interval MUST\n\t\t// be increased by 5 seconds for this and all subsequent requests.\n\t\t// NOTE: This error is not handled in the framework.\n\n\t\t// access_denied\n\t\t// The authorization request was denied.\n\t\tif (deviceCode.isInvalidated()) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED, null, DEVICE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\t// Verify the DPoP Proof (if available)\n\t\tJwt dPoPProof = DPoPProofVerifier.verifyIfAvailable(deviceCodeAuthentication);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated device token request parameters\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(authorization.getAttribute(Principal.class.getName()))\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizedScopes(authorization.getAuthorizedScopes())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.authorizationGrant(deviceCodeAuthentication);\n\t\t// @formatter:on\n\t\tif (dPoPProof != null) {\n\t\t\ttokenContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProof);\n\t\t}\n\n\t\t// @formatter:off\n\t\tOAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization)\n\t\t\t\t// Invalidate the device code as it can only be used (successfully) once\n\t\t\t\t.invalidate(deviceCode.getToken());\n\t\t// @formatter:on\n\n\t\t// ----- Access token -----\n\t\tOAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();\n\t\tOAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);\n\t\tif (generatedAccessToken == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the access token.\", DEFAULT_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated access token\");\n\t\t}\n\n\t\tOAuth2AccessToken accessToken = OAuth2AuthenticationProviderUtils.accessToken(authorizationBuilder,\n\t\t\t\tgeneratedAccessToken, tokenContext);\n\n\t\t// ----- Refresh token -----\n\t\tOAuth2RefreshToken refreshToken = null;\n\t\tif (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {\n\t\t\ttokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();\n\t\t\tOAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);\n\t\t\tif (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\"The token generator failed to generate the refresh token.\", DEFAULT_ERROR_URI);\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Generated refresh token\");\n\t\t\t}\n\n\t\t\trefreshToken = (OAuth2RefreshToken) generatedRefreshToken;\n\t\t\tauthorizationBuilder.refreshToken(refreshToken);\n\t\t}\n\n\t\tauthorization = authorizationBuilder.build();\n\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated device token request\");\n\t\t}\n\n\t\treturn new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2DeviceCodeAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation for the Device Access Token Request used in\n * the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2AuthorizationGrantAuthenticationToken\n * @see OAuth2DeviceCodeAuthenticationProvider\n */\npublic class OAuth2DeviceCodeAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {\n\n\tprivate final String deviceCode;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceCodeAuthenticationToken} using the provided\n\t * parameters.\n\t * @param deviceCode the device code\n\t * @param clientPrincipal the authenticated client principal\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2DeviceCodeAuthenticationToken(String deviceCode, Authentication clientPrincipal,\n\t\t\t@Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(AuthorizationGrantType.DEVICE_CODE, clientPrincipal, additionalParameters);\n\t\tAssert.hasText(deviceCode, \"deviceCode cannot be empty\");\n\t\tthis.deviceCode = deviceCode;\n\t}\n\n\t/**\n\t * Returns the device code.\n\t * @return the device code\n\t */\n\tpublic String getDeviceCode() {\n\t\treturn this.deviceCode;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthenticationContext} that holds an\n * {@link OAuth2DeviceVerificationAuthenticationToken} and additional information and is\n * used when determining if authorization consent is required.\n *\n * @author Dinesh Gupta\n * @since 7.0\n * @see OAuth2AuthenticationContext\n * @see OAuth2DeviceVerificationAuthenticationToken\n * @see OAuth2DeviceVerificationAuthenticationProvider#setAuthorizationConsentRequired(java.util.function.Predicate)\n */\npublic final class OAuth2DeviceVerificationAuthenticationContext implements OAuth2AuthenticationContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OAuth2DeviceVerificationAuthenticationContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link RegisteredClient registered client}.\n\t * @return the {@link RegisteredClient}\n\t */\n\tpublic RegisteredClient getRegisteredClient() {\n\t\treturn get(RegisteredClient.class);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Authorization authorization}.\n\t * @return the {@link OAuth2Authorization}\n\t */\n\tpublic OAuth2Authorization getAuthorization() {\n\t\treturn get(OAuth2Authorization.class);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationConsent authorization consent}.\n\t * @return the {@link OAuth2AuthorizationConsent}, or {@code null} if not available\n\t */\n\t@Nullable\n\tpublic OAuth2AuthorizationConsent getAuthorizationConsent() {\n\t\treturn get(OAuth2AuthorizationConsent.class);\n\t}\n\n\t/**\n\t * Returns the requested scopes.\n\t * @return the requested scopes\n\t */\n\tpublic Set<String> getRequestedScopes() {\n\t\tSet<String> requestedScopes = getAuthorization().getAttribute(OAuth2ParameterNames.SCOPE);\n\t\treturn (requestedScopes != null) ? requestedScopes : Collections.emptySet();\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided\n\t * {@link OAuth2DeviceVerificationAuthenticationToken}.\n\t * @param authentication the {@link OAuth2DeviceVerificationAuthenticationToken}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OAuth2DeviceVerificationAuthenticationToken authentication) {\n\t\treturn new Builder(authentication);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2DeviceVerificationAuthenticationContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OAuth2DeviceVerificationAuthenticationContext, Builder> {\n\n\t\tprivate Builder(OAuth2DeviceVerificationAuthenticationToken authentication) {\n\t\t\tsuper(authentication);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link RegisteredClient registered client}.\n\t\t * @param registeredClient the {@link RegisteredClient}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder registeredClient(RegisteredClient registeredClient) {\n\t\t\treturn put(RegisteredClient.class, registeredClient);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2Authorization authorization}.\n\t\t * @param authorization the {@link OAuth2Authorization}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorization(OAuth2Authorization authorization) {\n\t\t\treturn put(OAuth2Authorization.class, authorization);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AuthorizationConsent authorization consent}.\n\t\t * @param authorizationConsent the {@link OAuth2AuthorizationConsent}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationConsent(OAuth2AuthorizationConsent authorizationConsent) {\n\t\t\treturn put(OAuth2AuthorizationConsent.class, authorizationConsent);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2DeviceVerificationAuthenticationContext}.\n\t\t * @return the {@link OAuth2DeviceVerificationAuthenticationContext}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2DeviceVerificationAuthenticationContext build() {\n\t\t\tAssert.notNull(get(RegisteredClient.class), \"registeredClient cannot be null\");\n\t\t\tAssert.notNull(get(OAuth2Authorization.class), \"authorization cannot be null\");\n\t\t\treturn new OAuth2DeviceVerificationAuthenticationContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.util.Base64;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation for the Device Verification Request\n * (submission of the user code) used in the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2DeviceVerificationAuthenticationToken\n * @see OAuth2DeviceAuthorizationRequestAuthenticationProvider\n * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider\n * @see OAuth2DeviceCodeAuthenticationProvider\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see OAuth2AuthorizationConsentService\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc8628\">OAuth 2.0\n * Device Authorization Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.3\">Section 3.3 User\n * Interaction</a>\n */\npublic final class OAuth2DeviceVerificationAuthenticationProvider implements AuthenticationProvider {\n\n\tstatic final OAuth2TokenType USER_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.USER_CODE);\n\n\tprivate static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder());\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2AuthorizationConsentService authorizationConsentService;\n\n\tprivate Predicate<OAuth2DeviceVerificationAuthenticationContext> authorizationConsentRequired = OAuth2DeviceVerificationAuthenticationProvider::isAuthorizationConsentRequired;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceVerificationAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t * @param authorizationConsentService the authorization consent service\n\t */\n\tpublic OAuth2DeviceVerificationAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2AuthorizationConsentService authorizationConsentService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(authorizationConsentService, \"authorizationConsentService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.authorizationConsentService = authorizationConsentService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication = (OAuth2DeviceVerificationAuthenticationToken) authentication;\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(deviceVerificationAuthentication.getUserCode(), USER_CODE_TOKEN_TYPE);\n\t\tif (authorization == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with user code\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);\n\t\tif (!userCode.isActive()) {\n\t\t\tif (!userCode.isInvalidated()) {\n\t\t\t\tauthorization = OAuth2Authorization.from(authorization).invalidate(userCode.getToken()).build();\n\t\t\t\tthis.authorizationService.save(authorization);\n\t\t\t\tif (this.logger.isWarnEnabled()) {\n\t\t\t\t\tthis.logger.warn(LogMessage.format(\"Invalidated user code used by registered client '%s'\",\n\t\t\t\t\t\t\tauthorization.getRegisteredClientId()));\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tAuthentication principal = (Authentication) deviceVerificationAuthentication.getPrincipal();\n\t\tif (!isPrincipalAuthenticated(principal)) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Did not authenticate device verification request since principal not authenticated\");\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findById(authorization.getRegisteredClientId());\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tSet<String> requestedScopes = authorization.getAttribute(OAuth2ParameterNames.SCOPE);\n\n\t\tOAuth2DeviceVerificationAuthenticationContext.Builder authenticationContextBuilder = OAuth2DeviceVerificationAuthenticationContext\n\t\t\t.with(deviceVerificationAuthentication)\n\t\t\t.registeredClient(registeredClient)\n\t\t\t.authorization(authorization);\n\n\t\tOAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService\n\t\t\t.findById(registeredClient.getId(), principal.getName());\n\t\tif (currentAuthorizationConsent != null) {\n\t\t\tauthenticationContextBuilder.authorizationConsent(currentAuthorizationConsent);\n\t\t}\n\n\t\tif (this.authorizationConsentRequired.test(authenticationContextBuilder.build())) {\n\t\t\tString state = DEFAULT_STATE_GENERATOR.generateKey();\n\t\t\tauthorization = OAuth2Authorization.from(authorization)\n\t\t\t\t.principalName(principal.getName())\n\t\t\t\t.attribute(Principal.class.getName(), principal)\n\t\t\t\t.attribute(OAuth2ParameterNames.STATE, state)\n\t\t\t\t.build();\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Generated device authorization consent state\");\n\t\t\t}\n\n\t\t\tthis.authorizationService.save(authorization);\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t\t}\n\n\t\t\tSet<String> currentAuthorizedScopes = (currentAuthorizationConsent != null)\n\t\t\t\t\t? currentAuthorizationConsent.getScopes() : null;\n\n\t\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerContextHolder.getContext()\n\t\t\t\t.getAuthorizationServerSettings();\n\t\t\tString deviceVerificationUri = authorizationServerSettings.getDeviceVerificationEndpoint();\n\n\t\t\treturn new OAuth2DeviceAuthorizationConsentAuthenticationToken(deviceVerificationUri,\n\t\t\t\t\tregisteredClient.getClientId(), principal, deviceVerificationAuthentication.getUserCode(), state,\n\t\t\t\t\trequestedScopes, currentAuthorizedScopes);\n\t\t}\n\n\t\t// @formatter:off\n\t\tauthorization = OAuth2Authorization.from(authorization)\n\t\t\t\t.principalName(principal.getName())\n\t\t\t\t.authorizedScopes(requestedScopes)\n\t\t\t\t.invalidate(userCode.getToken())\n\t\t\t\t.attribute(Principal.class.getName(), principal)\n\t\t\t\t.attributes((attributes) -> attributes.remove(OAuth2ParameterNames.SCOPE))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization with authorized scopes\");\n\t\t\t// This log is kept separate for consistency with other providers\n\t\t\tthis.logger.trace(\"Authenticated device verification request\");\n\t\t}\n\n\t\treturn new OAuth2DeviceVerificationAuthenticationToken(principal,\n\t\t\t\tdeviceVerificationAuthentication.getUserCode(), registeredClient.getClientId());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2DeviceVerificationAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@code Predicate} used to determine if authorization consent is required.\n\t *\n\t * <p>\n\t * The {@link OAuth2DeviceVerificationAuthenticationContext} gives the predicate\n\t * access to the {@link OAuth2DeviceVerificationAuthenticationToken}, as well as, the\n\t * following context attributes:\n\t * <ul>\n\t * <li>The {@link RegisteredClient} associated with the device authorization\n\t * request.</li>\n\t * <li>The {@link OAuth2Authorization} containing the device authorization request\n\t * parameters.</li>\n\t * <li>The {@link OAuth2AuthorizationConsent} previously granted to the\n\t * {@link RegisteredClient}, or {@code null} if not available.</li>\n\t * </ul>\n\t * </p>\n\t * @param authorizationConsentRequired the {@code Predicate} used to determine if\n\t * authorization consent is required\n\t */\n\tpublic void setAuthorizationConsentRequired(\n\t\t\tPredicate<OAuth2DeviceVerificationAuthenticationContext> authorizationConsentRequired) {\n\t\tAssert.notNull(authorizationConsentRequired, \"authorizationConsentRequired cannot be null\");\n\t\tthis.authorizationConsentRequired = authorizationConsentRequired;\n\t}\n\n\tprivate static boolean isAuthorizationConsentRequired(\n\t\t\tOAuth2DeviceVerificationAuthenticationContext authenticationContext) {\n\n\t\tif (authenticationContext.getAuthorizationConsent() != null && authenticationContext.getAuthorizationConsent()\n\t\t\t.getScopes()\n\t\t\t.containsAll(authenticationContext.getRequestedScopes())) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate static boolean isPrincipalAuthenticated(Authentication principal) {\n\t\treturn principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass())\n\t\t\t\t&& principal.isAuthenticated();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation for the Device Verification Request\n * (submission of the user code) used in the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OAuth2DeviceVerificationAuthenticationProvider\n */\npublic class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2164261941629756913L;\n\n\tprivate final Authentication principal;\n\n\tprivate final String userCode;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\tprivate final String clientId;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the\n\t * provided parameters.\n\t * @param principal the {@code Principal} (Resource Owner)\n\t * @param userCode the user code associated with the device authorization response\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2DeviceVerificationAuthenticationToken(Authentication principal, String userCode,\n\t\t\t@Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.hasText(userCode, \"userCode cannot be empty\");\n\t\tthis.principal = principal;\n\t\tthis.userCode = userCode;\n\t\tthis.additionalParameters = Collections.unmodifiableMap(\n\t\t\t\t(additionalParameters != null) ? new HashMap<>(additionalParameters) : Collections.emptyMap());\n\t\tthis.clientId = null;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the\n\t * provided parameters.\n\t * @param principal the {@code Principal} (Resource Owner)\n\t * @param userCode the user code associated with the device authorization response\n\t * @param clientId the client identifier\n\t */\n\tpublic OAuth2DeviceVerificationAuthenticationToken(Authentication principal, String userCode, String clientId) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.hasText(userCode, \"userCode cannot be empty\");\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\tthis.principal = principal;\n\t\tthis.userCode = userCode;\n\t\tthis.clientId = clientId;\n\t\tthis.additionalParameters = Collections.emptyMap();\n\t\tsetAuthenticated(true);\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the user code.\n\t * @return the user code\n\t */\n\tpublic String getUserCode() {\n\t\treturn this.userCode;\n\t}\n\n\t/**\n\t * Returns the additional parameters.\n\t * @return the additional parameters, or an empty {@code Map} if not available\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n\t/**\n\t * Returns the client identifier.\n\t * @return the client identifier\n\t */\n\tpublic String getClientId() {\n\t\treturn this.clientId;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2PushedAuthorizationRequestAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Pushed Authorization\n * Request used in the Authorization Code Grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2PushedAuthorizationRequestAuthenticationToken\n * @see OAuth2AuthorizationCodeRequestAuthenticationToken\n * @see OAuth2AuthorizationCodeRequestAuthenticationValidator\n * @see OAuth2AuthorizationService\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9126#section-2.1\">Section 2.1 Pushed\n * Authorization Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9126#section-2.2\">Section 2.2 Pushed\n * Authorization Response</a>\n */\npublic final class OAuth2PushedAuthorizationRequestAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator = new OAuth2AuthorizationCodeRequestAuthenticationValidator();\n\n\t/**\n\t * Constructs an {@code OAuth2PushedAuthorizationRequestAuthenticationProvider} using\n\t * the provided parameters.\n\t * @param authorizationService the authorization service\n\t */\n\tpublic OAuth2PushedAuthorizationRequestAuthenticationProvider(OAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken pushedAuthorizationRequestAuthentication = (OAuth2PushedAuthorizationRequestAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(pushedAuthorizationRequestAuthentication);\n\t\tRegisteredClient registeredClient = clientPrincipal.getRegisteredClient();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext = OAuth2AuthorizationCodeRequestAuthenticationContext\n\t\t\t.with(toAuthorizationCodeRequestAuthentication(pushedAuthorizationRequestAuthentication))\n\t\t\t.registeredClient(registeredClient)\n\t\t\t.build();\n\n\t\t// grant_type\n\t\tOAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_AUTHORIZATION_GRANT_TYPE_VALIDATOR\n\t\t\t.accept(authenticationContext);\n\n\t\t// redirect_uri and scope\n\t\tthis.authenticationValidator.accept(authenticationContext);\n\n\t\t// code_challenge (REQUIRED for public clients) - RFC 7636 (PKCE)\n\t\tOAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_CODE_CHALLENGE_VALIDATOR\n\t\t\t.accept(authenticationContext);\n\n\t\t// prompt (OPTIONAL for OpenID Connect 1.0 Authentication Request)\n\t\tOAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_PROMPT_VALIDATOR.accept(authenticationContext);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated pushed authorization request parameters\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(pushedAuthorizationRequestAuthentication.getAuthorizationUri())\n\t\t\t.clientId(registeredClient.getClientId())\n\t\t\t.redirectUri(pushedAuthorizationRequestAuthentication.getRedirectUri())\n\t\t\t.scopes(pushedAuthorizationRequestAuthentication.getScopes())\n\t\t\t.state(pushedAuthorizationRequestAuthentication.getState())\n\t\t\t.additionalParameters(pushedAuthorizationRequestAuthentication.getAdditionalParameters())\n\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = OAuth2PushedAuthorizationRequestUri\n\t\t\t.create();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated pushed authorization request uri\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(clientPrincipal.getName())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest)\n\t\t\t\t.attribute(OAuth2ParameterNames.STATE, pushedAuthorizationRequestUri.getState())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated pushed authorization request\");\n\t\t}\n\n\t\treturn new OAuth2PushedAuthorizationRequestAuthenticationToken(authorizationRequest.getAuthorizationUri(),\n\t\t\t\tauthorizationRequest.getClientId(), clientPrincipal, pushedAuthorizationRequestUri.getRequestUri(),\n\t\t\t\tpushedAuthorizationRequestUri.getExpiresAt(), authorizationRequest.getRedirectUri(),\n\t\t\t\tauthorizationRequest.getState(), authorizationRequest.getScopes());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2PushedAuthorizationRequestAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for\n\t * validating specific OAuth 2.0 Pushed Authorization Request parameters associated in\n\t * the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. The default\n\t * authentication validator is\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationValidator}.\n\t *\n\t * <p>\n\t * <b>NOTE:</b> The authentication validator MUST throw\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationException} if validation fails.\n\t * @param authenticationValidator the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for\n\t * validating specific OAuth 2.0 Pushed Authorization Request parameters\n\t */\n\tpublic void setAuthenticationValidator(\n\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {\n\t\tAssert.notNull(authenticationValidator, \"authenticationValidator cannot be null\");\n\t\tthis.authenticationValidator = authenticationValidator;\n\t}\n\n\tprivate static OAuth2AuthorizationCodeRequestAuthenticationToken toAuthorizationCodeRequestAuthentication(\n\t\t\tOAuth2PushedAuthorizationRequestAuthenticationToken pushedAuthorizationCodeRequestAuthentication) {\n\t\treturn new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tpushedAuthorizationCodeRequestAuthentication.getAuthorizationUri(),\n\t\t\t\tpushedAuthorizationCodeRequestAuthentication.getClientId(),\n\t\t\t\t(Authentication) pushedAuthorizationCodeRequestAuthentication.getPrincipal(),\n\t\t\t\tpushedAuthorizationCodeRequestAuthentication.getRedirectUri(),\n\t\t\t\tpushedAuthorizationCodeRequestAuthentication.getState(),\n\t\t\t\tpushedAuthorizationCodeRequestAuthentication.getScopes(),\n\t\t\t\tpushedAuthorizationCodeRequestAuthentication.getAdditionalParameters());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2PushedAuthorizationRequestAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.time.Instant;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation for the OAuth 2.0 Pushed Authorization Request\n * used in the Authorization Code Grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2PushedAuthorizationRequestAuthenticationProvider\n */\npublic class OAuth2PushedAuthorizationRequestAuthenticationToken\n\t\textends AbstractOAuth2AuthorizationCodeRequestAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 7330534287786569644L;\n\n\tprivate final String requestUri;\n\n\tprivate final Instant requestUriExpiresAt;\n\n\t/**\n\t * Constructs an {@code OAuth2PushedAuthorizationRequestAuthenticationToken} using the\n\t * provided parameters.\n\t * @param authorizationUri the authorization URI\n\t * @param clientId the client identifier\n\t * @param principal the authenticated client principal\n\t * @param redirectUri the redirect uri\n\t * @param state the state\n\t * @param scopes the requested scope(s)\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2PushedAuthorizationRequestAuthenticationToken(String authorizationUri, String clientId,\n\t\t\tAuthentication principal, @Nullable String redirectUri, @Nullable String state,\n\t\t\t@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(authorizationUri, clientId, principal, redirectUri, state, scopes, additionalParameters);\n\t\tthis.requestUri = null;\n\t\tthis.requestUriExpiresAt = null;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2PushedAuthorizationRequestAuthenticationToken} using the\n\t * provided parameters.\n\t * @param authorizationUri the authorization URI\n\t * @param clientId the client identifier\n\t * @param principal the authenticated client principal\n\t * @param requestUri the {@code request_uri} corresponding to the authorization\n\t * request posted\n\t * @param requestUriExpiresAt the expiration time on or after which the\n\t * {@code request_uri} MUST NOT be accepted\n\t * @param redirectUri the redirect uri\n\t * @param state the state\n\t * @param scopes the authorized scope(s)\n\t */\n\tpublic OAuth2PushedAuthorizationRequestAuthenticationToken(String authorizationUri, String clientId,\n\t\t\tAuthentication principal, String requestUri, Instant requestUriExpiresAt, @Nullable String redirectUri,\n\t\t\t@Nullable String state, @Nullable Set<String> scopes) {\n\t\tsuper(authorizationUri, clientId, principal, redirectUri, state, scopes, null);\n\t\tAssert.hasText(requestUri, \"requestUri cannot be empty\");\n\t\tAssert.notNull(requestUriExpiresAt, \"requestUriExpiresAt cannot be null\");\n\t\tthis.requestUri = requestUri;\n\t\tthis.requestUriExpiresAt = requestUriExpiresAt;\n\t\tsetAuthenticated(true);\n\t}\n\n\t/**\n\t * Returns the {@code request_uri} corresponding to the authorization request posted.\n\t * @return the {@code request_uri} corresponding to the authorization request posted\n\t */\n\t@Nullable\n\tpublic String getRequestUri() {\n\t\treturn this.requestUri;\n\t}\n\n\t/**\n\t * Returns the expiration time on or after which the {@code request_uri} MUST NOT be\n\t * accepted.\n\t * @return the expiration time on or after which the {@code request_uri} MUST NOT be\n\t * accepted\n\t */\n\t@Nullable\n\tpublic Instant getRequestUriExpiresAt() {\n\t\treturn this.requestUriExpiresAt;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2PushedAuthorizationRequestUri.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.util.Base64;\n\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\n\n/**\n * A representation of a {@code request_uri} used in OAuth 2.0 Pushed Authorization\n * Requests.\n *\n * @author Joe Grandja\n * @since 7.0\n */\nfinal class OAuth2PushedAuthorizationRequestUri {\n\n\tprivate static final String REQUEST_URI_PREFIX = \"urn:ietf:params:oauth:request_uri:\";\n\n\tprivate static final String REQUEST_URI_DELIMITER = \"___\";\n\n\tprivate static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder());\n\n\tprivate String requestUri;\n\n\tprivate String state;\n\n\tprivate Instant expiresAt;\n\n\tstatic OAuth2PushedAuthorizationRequestUri create() {\n\t\treturn create(Instant.now().plusSeconds(300));\n\t}\n\n\tstatic OAuth2PushedAuthorizationRequestUri create(Instant expiresAt) {\n\t\tString state = DEFAULT_STATE_GENERATOR.generateKey();\n\t\tOAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = new OAuth2PushedAuthorizationRequestUri();\n\t\tpushedAuthorizationRequestUri.requestUri = REQUEST_URI_PREFIX + state + REQUEST_URI_DELIMITER\n\t\t\t\t+ expiresAt.toEpochMilli();\n\t\tpushedAuthorizationRequestUri.state = state + REQUEST_URI_DELIMITER + expiresAt.toEpochMilli();\n\t\tpushedAuthorizationRequestUri.expiresAt = expiresAt;\n\t\treturn pushedAuthorizationRequestUri;\n\t}\n\n\tstatic OAuth2PushedAuthorizationRequestUri parse(String requestUri) {\n\t\tint stateStartIndex = REQUEST_URI_PREFIX.length();\n\t\tint expiresAtStartIndex = requestUri.indexOf(REQUEST_URI_DELIMITER) + REQUEST_URI_DELIMITER.length();\n\t\tOAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = new OAuth2PushedAuthorizationRequestUri();\n\t\tpushedAuthorizationRequestUri.requestUri = requestUri;\n\t\tpushedAuthorizationRequestUri.state = requestUri.substring(stateStartIndex);\n\t\tpushedAuthorizationRequestUri.expiresAt = Instant\n\t\t\t.ofEpochMilli(Long.parseLong(requestUri.substring(expiresAtStartIndex)));\n\t\treturn pushedAuthorizationRequestUri;\n\t}\n\n\tString getRequestUri() {\n\t\treturn this.requestUri;\n\t}\n\n\tString getState() {\n\t\treturn this.state;\n\t}\n\n\tInstant getExpiresAt() {\n\t\treturn this.expiresAt;\n\t}\n\n\tprivate OAuth2PushedAuthorizationRequestUri() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Refresh Token Grant.\n *\n * @author Alexey Nesterov\n * @author Joe Grandja\n * @author Anoop Garlapati\n * @since 7.0\n * @see OAuth2RefreshTokenAuthenticationToken\n * @see OAuth2AccessTokenAuthenticationToken\n * @see OAuth2AuthorizationService\n * @see OAuth2TokenGenerator\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-1.5\">Section 1.5 Refresh Token\n * Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-6\">Section 6 Refreshing an\n * Access Token</a>\n */\npublic final class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;\n\n\t/**\n\t * Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param authorizationService the authorization service\n\t * @param tokenGenerator the token generator\n\t */\n\tpublic OAuth2RefreshTokenAuthenticationProvider(OAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(tokenGenerator, \"tokenGenerator cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.tokenGenerator = tokenGenerator;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2RefreshTokenAuthenticationToken refreshTokenAuthentication = (OAuth2RefreshTokenAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(refreshTokenAuthentication);\n\t\tRegisteredClient registeredClient = clientPrincipal.getRegisteredClient();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(refreshTokenAuthentication.getRefreshToken(), OAuth2TokenType.REFRESH_TOKEN);\n\t\tif (authorization == null) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(\"Invalid request: refresh_token is invalid\");\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with refresh token\");\n\t\t}\n\n\t\tif (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: requested grant_type is not allowed\" + \" for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getRefreshToken();\n\t\tif (!refreshToken.isActive()) {\n\t\t\t// As per https://tools.ietf.org/html/rfc6749#section-5.2\n\t\t\t// invalid_grant: The provided authorization grant (e.g., authorization code,\n\t\t\t// resource owner credentials) or refresh token is invalid, expired, revoked\n\t\t\t// [...].\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"Invalid request: refresh_token is not active\" + \" for registered client '%s'\",\n\t\t\t\t\t\tregisteredClient.getId()));\n\t\t\t}\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\t// As per https://tools.ietf.org/html/rfc6749#section-6\n\t\t// The requested scope MUST NOT include any scope not originally granted by the\n\t\t// resource owner,\n\t\t// and if omitted is treated as equal to the scope originally granted by the\n\t\t// resource owner.\n\t\tSet<String> scopes = refreshTokenAuthentication.getScopes();\n\t\tSet<String> authorizedScopes = authorization.getAuthorizedScopes();\n\t\tif (!authorizedScopes.containsAll(scopes)) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);\n\t\t}\n\n\t\t// Verify the DPoP Proof (if available)\n\t\tJwt dPoPProof = DPoPProofVerifier.verifyIfAvailable(refreshTokenAuthentication);\n\n\t\tif (dPoPProof != null\n\t\t\t\t&& clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {\n\t\t\t// For public clients, verify the DPoP Proof public key is same as (current)\n\t\t\t// access token public key binding\n\t\t\tMap<String, Object> accessTokenClaims = authorization.getAccessToken().getClaims();\n\t\t\tverifyDPoPProofPublicKey(dPoPProof, () -> accessTokenClaims);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated token request parameters\");\n\t\t}\n\n\t\tif (scopes.isEmpty()) {\n\t\t\tscopes = authorizedScopes;\n\t\t}\n\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(authorization.getAttribute(Principal.class.getName()))\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizedScopes(scopes)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t\t.authorizationGrant(refreshTokenAuthentication);\n\t\t// @formatter:on\n\t\tif (dPoPProof != null) {\n\t\t\ttokenContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProof);\n\t\t}\n\n\t\tOAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization);\n\n\t\t// ----- Access token -----\n\t\tOAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();\n\t\tOAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);\n\t\tif (generatedAccessToken == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the access token.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated access token\");\n\t\t}\n\n\t\tOAuth2AccessToken accessToken = OAuth2AuthenticationProviderUtils.accessToken(authorizationBuilder,\n\t\t\t\tgeneratedAccessToken, tokenContext);\n\n\t\t// ----- Refresh token -----\n\t\tOAuth2RefreshToken currentRefreshToken = refreshToken.getToken();\n\t\tif (!registeredClient.getTokenSettings().isReuseRefreshTokens()) {\n\t\t\t// @formatter:off\n\t\t\ttokenContext = tokenContextBuilder\n\t\t\t\t\t.tokenType(OAuth2TokenType.REFRESH_TOKEN)\n\t\t\t\t\t.authorization(authorizationBuilder.build())\t// Refresh token generator/customizer may need access to the access token\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tOAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);\n\t\t\tif (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\"The token generator failed to generate the refresh token.\", ERROR_URI);\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Generated refresh token\");\n\t\t\t}\n\n\t\t\tcurrentRefreshToken = (OAuth2RefreshToken) generatedRefreshToken;\n\t\t\tauthorizationBuilder.refreshToken(currentRefreshToken);\n\t\t}\n\n\t\t// ----- ID token -----\n\t\tOidcIdToken idToken;\n\t\tif (authorizedScopes.contains(OidcScopes.OPENID)) {\n\t\t\t// @formatter:off\n\t\t\ttokenContext = tokenContextBuilder\n\t\t\t\t\t.tokenType(ID_TOKEN_TOKEN_TYPE)\n\t\t\t\t\t.authorization(authorizationBuilder.build())\t// ID token customizer may need access to the access token and/or refresh token\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tOAuth2Token generatedIdToken = this.tokenGenerator.generate(tokenContext);\n\t\t\tif (!(generatedIdToken instanceof Jwt)) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\"The token generator failed to generate the ID token.\", ERROR_URI);\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Generated id token\");\n\t\t\t}\n\n\t\t\tidToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(),\n\t\t\t\t\tgeneratedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims());\n\t\t\tauthorizationBuilder.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));\n\t\t}\n\t\telse {\n\t\t\tidToken = null;\n\t\t}\n\n\t\tauthorization = authorizationBuilder.build();\n\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = Collections.emptyMap();\n\t\tif (idToken != null) {\n\t\t\tadditionalParameters = new HashMap<>();\n\t\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, idToken.getTokenValue());\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated token request\");\n\t\t}\n\n\t\treturn new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,\n\t\t\t\tcurrentRefreshToken, additionalParameters);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2RefreshTokenAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tprivate static void verifyDPoPProofPublicKey(Jwt dPoPProof, ClaimAccessor accessTokenClaims) {\n\t\tJWK jwk = null;\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, Object> jwkJson = (Map<String, Object>) dPoPProof.getHeaders().get(\"jwk\");\n\t\ttry {\n\t\t\tjwk = JWK.parse(jwkJson);\n\t\t}\n\t\tcatch (Exception ignored) {\n\t\t}\n\t\tif (jwk == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF,\n\t\t\t\t\t\"jwk header is missing or invalid.\", null);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tString jwkThumbprint;\n\t\ttry {\n\t\t\tjwkThumbprint = jwk.computeThumbprint().toString();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF,\n\t\t\t\t\t\"Failed to compute SHA-256 Thumbprint for jwk.\", null);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tString jwkThumbprintClaim = null;\n\t\tMap<String, Object> confirmationMethodClaim = accessTokenClaims.getClaimAsMap(\"cnf\");\n\t\tif (!CollectionUtils.isEmpty(confirmationMethodClaim) && confirmationMethodClaim.containsKey(\"jkt\")) {\n\t\t\tjwkThumbprintClaim = (String) confirmationMethodClaim.get(\"jkt\");\n\t\t}\n\t\tif (jwkThumbprintClaim == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF, \"jkt claim is missing.\", null);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (!jwkThumbprint.equals(jwkThumbprintClaim)) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF, \"jwk header is invalid.\", null);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for the OAuth 2.0 Refresh Token Grant.\n *\n * @author Alexey Nesterov\n * @since 7.0\n * @see OAuth2AuthorizationGrantAuthenticationToken\n * @see OAuth2RefreshTokenAuthenticationProvider\n */\npublic class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {\n\n\tprivate final String refreshToken;\n\n\tprivate final Set<String> scopes;\n\n\t/**\n\t * Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided\n\t * parameters.\n\t * @param refreshToken the refresh token\n\t * @param clientPrincipal the authenticated client principal\n\t * @param scopes the requested scope(s)\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal,\n\t\t\t@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(AuthorizationGrantType.REFRESH_TOKEN, clientPrincipal, additionalParameters);\n\t\tAssert.hasText(refreshToken, \"refreshToken cannot be empty\");\n\t\tthis.refreshToken = refreshToken;\n\t\tthis.scopes = Collections.unmodifiableSet((scopes != null) ? new HashSet<>(scopes) : Collections.emptySet());\n\t}\n\n\t/**\n\t * Returns the refresh token.\n\t * @return the refresh token\n\t */\n\tpublic String getRefreshToken() {\n\t\treturn this.refreshToken;\n\t}\n\n\t/**\n\t * Returns the requested scope(s).\n\t * @return the requested scope(s), or an empty {@code Set} if not available\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeActor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ClaimAccessor} used for the OAuth 2.0 Token Exchange Grant to represent an\n * actor in a {@link OAuth2TokenExchangeCompositeAuthenticationToken} (e.g. the\n * \"delegation\" use case).\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2TokenExchangeCompositeAuthenticationToken\n */\npublic final class OAuth2TokenExchangeActor implements ClaimAccessor {\n\n\tprivate final Map<String, Object> claims;\n\n\tpublic OAuth2TokenExchangeActor(Map<String, Object> claims) {\n\t\tAssert.notNull(claims, \"claims cannot be null\");\n\t\tthis.claims = Collections.unmodifiableMap(claims);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\tpublic String getIssuer() {\n\t\treturn getClaimAsString(OAuth2TokenClaimNames.ISS);\n\t}\n\n\tpublic String getSubject() {\n\t\treturn getClaimAsString(OAuth2TokenClaimNames.SUB);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (!(obj instanceof OAuth2TokenExchangeActor other)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(this.claims, other.claims);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.claims);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Token Exchange\n * Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2TokenExchangeAuthenticationToken\n * @see OAuth2AccessTokenAuthenticationToken\n * @see OAuth2AuthorizationService\n * @see OAuth2TokenGenerator\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8693#section-1\">Section 1 Introduction</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8693#section-2.1\">Section 2.1 Request</a>\n */\npublic final class OAuth2TokenExchangeAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String MAY_ACT = \"may_act\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;\n\n\t/**\n\t * Constructs an {@code OAuth2TokenExchangeAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param authorizationService the authorization service\n\t * @param tokenGenerator the token generator\n\t */\n\tpublic OAuth2TokenExchangeAuthenticationProvider(OAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(tokenGenerator, \"tokenGenerator cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.tokenGenerator = tokenGenerator;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = (OAuth2TokenExchangeAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(tokenExchangeAuthentication);\n\t\tRegisteredClient registeredClient = clientPrincipal.getRegisteredClient();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tif (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.TOKEN_EXCHANGE)) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);\n\t\t}\n\n\t\tif (JWT_TOKEN_TYPE_VALUE.equals(tokenExchangeAuthentication.getRequestedTokenType())\n\t\t\t\t&& !OAuth2TokenFormat.SELF_CONTAINED\n\t\t\t\t\t.equals(registeredClient.getTokenSettings().getAccessTokenFormat())) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tOAuth2Authorization subjectAuthorization = this.authorizationService\n\t\t\t.findByToken(tokenExchangeAuthentication.getSubjectToken(), OAuth2TokenType.ACCESS_TOKEN);\n\t\tif (subjectAuthorization == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with subject token\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2Token> subjectToken = subjectAuthorization\n\t\t\t.getToken(tokenExchangeAuthentication.getSubjectToken());\n\t\tif (!subjectToken.isActive()) {\n\t\t\t// As per https://tools.ietf.org/html/rfc6749#section-5.2\n\t\t\t// invalid_grant: The provided authorization grant (e.g., authorization code,\n\t\t\t// resource owner credentials) or refresh token is invalid, expired, revoked\n\t\t\t// [...].\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tif (!isValidTokenType(tokenExchangeAuthentication.getSubjectTokenType(), subjectToken)) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tif (subjectAuthorization.getAttribute(Principal.class.getName()) == null) {\n\t\t\t// As per https://datatracker.ietf.org/doc/html/rfc8693#section-1.1,\n\t\t\t// we require a principal to be available via the subject_token for\n\t\t\t// impersonation or delegation use cases.\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\t// As per https://datatracker.ietf.org/doc/html/rfc8693#section-4.4,\n\t\t// The may_act claim makes a statement that one party is authorized to\n\t\t// become the actor and act on behalf of another party.\n\t\tMap<String, Object> authorizedActorClaims = null;\n\t\tif (subjectToken.getClaims() != null && subjectToken.getClaims().containsKey(MAY_ACT)\n\t\t\t\t&& subjectToken.getClaims().get(MAY_ACT) instanceof Map<?, ?> mayAct) {\n\t\t\tauthorizedActorClaims = (Map<String, Object>) mayAct;\n\t\t}\n\n\t\tOAuth2Authorization actorAuthorization = null;\n\t\tif (StringUtils.hasText(tokenExchangeAuthentication.getActorToken())) {\n\t\t\tactorAuthorization = this.authorizationService.findByToken(tokenExchangeAuthentication.getActorToken(),\n\t\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\t\tif (actorAuthorization == null) {\n\t\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t}\n\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Retrieved authorization with actor token\");\n\t\t\t}\n\n\t\t\tOAuth2Authorization.Token<OAuth2Token> actorToken = actorAuthorization\n\t\t\t\t.getToken(tokenExchangeAuthentication.getActorToken());\n\t\t\tif (!actorToken.isActive()) {\n\t\t\t\t// As per https://tools.ietf.org/html/rfc6749#section-5.2\n\t\t\t\t// invalid_grant: The provided authorization grant (e.g., authorization\n\t\t\t\t// code,\n\t\t\t\t// resource owner credentials) or refresh token is invalid, expired,\n\t\t\t\t// revoked [...].\n\t\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t}\n\n\t\t\tif (!isValidTokenType(tokenExchangeAuthentication.getActorTokenType(), actorToken)) {\n\t\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\t}\n\n\t\t\tif (authorizedActorClaims != null) {\n\t\t\t\tvalidateClaims(authorizedActorClaims, actorToken.getClaims(), OAuth2TokenClaimNames.ISS,\n\t\t\t\t\t\tOAuth2TokenClaimNames.SUB);\n\t\t\t}\n\t\t}\n\t\telse if (authorizedActorClaims != null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tSet<String> authorizedScopes = Collections.emptySet();\n\t\tif (!CollectionUtils.isEmpty(tokenExchangeAuthentication.getScopes())) {\n\t\t\tauthorizedScopes = validateRequestedScopes(registeredClient, tokenExchangeAuthentication.getScopes());\n\t\t}\n\t\telse if (!CollectionUtils.isEmpty(subjectAuthorization.getAuthorizedScopes())) {\n\t\t\tauthorizedScopes = validateRequestedScopes(registeredClient, subjectAuthorization.getAuthorizedScopes());\n\t\t}\n\n\t\t// Verify the DPoP Proof (if available)\n\t\tJwt dPoPProof = DPoPProofVerifier.verifyIfAvailable(tokenExchangeAuthentication);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated token request parameters\");\n\t\t}\n\n\t\tAuthentication principal = getPrincipal(subjectAuthorization, actorAuthorization);\n\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.authorization(subjectAuthorization)\n\t\t\t\t.principal(principal)\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t\t.authorizationGrant(tokenExchangeAuthentication);\n\t\t// @formatter:on\n\t\tif (dPoPProof != null) {\n\t\t\ttokenContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProof);\n\t\t}\n\n\t\t// ----- Access token -----\n\t\tOAuth2TokenContext tokenContext = tokenContextBuilder.build();\n\t\tOAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);\n\t\tif (generatedAccessToken == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the access token.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated access token\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tOAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(subjectAuthorization.getPrincipalName())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t\t.attribute(Principal.class.getName(), principal);\n\t\t// @formatter:on\n\n\t\tOAuth2AccessToken accessToken = OAuth2AuthenticationProviderUtils.accessToken(authorizationBuilder,\n\t\t\t\tgeneratedAccessToken, tokenContext);\n\n\t\tOAuth2Authorization authorization = authorizationBuilder.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization\");\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(OAuth2ParameterNames.ISSUED_TOKEN_TYPE,\n\t\t\t\ttokenExchangeAuthentication.getRequestedTokenType());\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated token request\");\n\t\t}\n\n\t\treturn new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, null,\n\t\t\t\tadditionalParameters);\n\t}\n\n\tprivate static boolean isValidTokenType(String tokenType, OAuth2Authorization.Token<OAuth2Token> token) {\n\t\tString tokenFormat = token.getMetadata(OAuth2TokenFormat.class.getName());\n\t\treturn ACCESS_TOKEN_TYPE_VALUE.equals(tokenType) || JWT_TOKEN_TYPE_VALUE.equals(tokenType)\n\t\t\t\t&& OAuth2TokenFormat.SELF_CONTAINED.getValue().equals(tokenFormat);\n\t}\n\n\tprivate static Set<String> validateRequestedScopes(RegisteredClient registeredClient, Set<String> requestedScopes) {\n\t\tfor (String requestedScope : requestedScopes) {\n\t\t\tif (!registeredClient.getScopes().contains(requestedScope)) {\n\t\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);\n\t\t\t}\n\t\t}\n\n\t\treturn new LinkedHashSet<>(requestedScopes);\n\t}\n\n\tprivate static void validateClaims(Map<String, Object> expectedClaims, Map<String, Object> actualClaims,\n\t\t\tString... claimNames) {\n\t\tif (actualClaims == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t}\n\n\t\tfor (String claimName : claimNames) {\n\t\t\tif (!Objects.equals(expectedClaims.get(claimName), actualClaims.get(claimName))) {\n\t\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static Authentication getPrincipal(OAuth2Authorization subjectAuthorization,\n\t\t\tOAuth2Authorization actorAuthorization) {\n\t\tAuthentication subjectPrincipal = subjectAuthorization.getAttribute(Principal.class.getName());\n\t\tif (actorAuthorization == null) {\n\t\t\tif (subjectPrincipal instanceof OAuth2TokenExchangeCompositeAuthenticationToken compositeAuthenticationToken) {\n\t\t\t\treturn compositeAuthenticationToken.getSubject();\n\t\t\t}\n\t\t\treturn subjectPrincipal;\n\t\t}\n\n\t\t// Capture claims for current actor's access token\n\t\tOAuth2TokenExchangeActor currentActor = new OAuth2TokenExchangeActor(\n\t\t\t\tactorAuthorization.getAccessToken().getClaims());\n\t\tList<OAuth2TokenExchangeActor> actorPrincipals = new LinkedList<>();\n\t\tactorPrincipals.add(currentActor);\n\n\t\t// Add chain of delegation for previous actor(s) if any\n\t\tif (subjectPrincipal instanceof OAuth2TokenExchangeCompositeAuthenticationToken compositeAuthenticationToken) {\n\t\t\tsubjectPrincipal = compositeAuthenticationToken.getSubject();\n\t\t\tactorPrincipals.addAll(compositeAuthenticationToken.getActors());\n\t\t}\n\n\t\treturn new OAuth2TokenExchangeCompositeAuthenticationToken(subjectPrincipal, actorPrincipals);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2TokenExchangeAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for the OAuth 2.0 Token Exchange Grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2AuthorizationGrantAuthenticationToken\n * @see OAuth2TokenExchangeAuthenticationProvider\n */\npublic class OAuth2TokenExchangeAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {\n\n\tprivate final String requestedTokenType;\n\n\tprivate final String subjectToken;\n\n\tprivate final String subjectTokenType;\n\n\tprivate final String actorToken;\n\n\tprivate final String actorTokenType;\n\n\tprivate final Set<String> resources;\n\n\tprivate final Set<String> audiences;\n\n\tprivate final Set<String> scopes;\n\n\t/**\n\t * Constructs an {@code OAuth2TokenExchangeAuthenticationToken} using the provided\n\t * parameters.\n\t * @param requestedTokenType the requested token type\n\t * @param subjectToken the subject token\n\t * @param subjectTokenType the subject token type\n\t * @param clientPrincipal the authenticated client principal\n\t * @param actorToken the actor token\n\t * @param actorTokenType the actor token type\n\t * @param resources the requested resource URI(s)\n\t * @param audiences the requested audience value(s)\n\t * @param scopes the requested scope(s)\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2TokenExchangeAuthenticationToken(String requestedTokenType, String subjectToken,\n\t\t\tString subjectTokenType, Authentication clientPrincipal, @Nullable String actorToken,\n\t\t\t@Nullable String actorTokenType, @Nullable Set<String> resources, @Nullable Set<String> audiences,\n\t\t\t@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(AuthorizationGrantType.TOKEN_EXCHANGE, clientPrincipal, additionalParameters);\n\t\tAssert.hasText(requestedTokenType, \"requestedTokenType cannot be empty\");\n\t\tAssert.hasText(subjectToken, \"subjectToken cannot be empty\");\n\t\tAssert.hasText(subjectTokenType, \"subjectTokenType cannot be empty\");\n\t\tthis.requestedTokenType = requestedTokenType;\n\t\tthis.subjectToken = subjectToken;\n\t\tthis.subjectTokenType = subjectTokenType;\n\t\tthis.actorToken = actorToken;\n\t\tthis.actorTokenType = actorTokenType;\n\t\tthis.resources = Collections\n\t\t\t.unmodifiableSet((resources != null) ? new LinkedHashSet<>(resources) : Collections.emptySet());\n\t\tthis.audiences = Collections\n\t\t\t.unmodifiableSet((audiences != null) ? new LinkedHashSet<>(audiences) : Collections.emptySet());\n\t\tthis.scopes = Collections.unmodifiableSet((scopes != null) ? new HashSet<>(scopes) : Collections.emptySet());\n\t}\n\n\t/**\n\t * Returns the requested token type.\n\t * @return the requested token type\n\t */\n\tpublic String getRequestedTokenType() {\n\t\treturn this.requestedTokenType;\n\t}\n\n\t/**\n\t * Returns the subject token.\n\t * @return the subject token\n\t */\n\tpublic String getSubjectToken() {\n\t\treturn this.subjectToken;\n\t}\n\n\t/**\n\t * Returns the subject token type.\n\t * @return the subject token type\n\t */\n\tpublic String getSubjectTokenType() {\n\t\treturn this.subjectTokenType;\n\t}\n\n\t/**\n\t * Returns the actor token.\n\t * @return the actor token\n\t */\n\tpublic String getActorToken() {\n\t\treturn this.actorToken;\n\t}\n\n\t/**\n\t * Returns the actor token type.\n\t * @return the actor token type\n\t */\n\tpublic String getActorTokenType() {\n\t\treturn this.actorTokenType;\n\t}\n\n\t/**\n\t * Returns the requested resource URI(s).\n\t * @return the requested resource URI(s), or an empty {@code Set} if not available\n\t */\n\tpublic Set<String> getResources() {\n\t\treturn this.resources;\n\t}\n\n\t/**\n\t * Returns the requested audience value(s).\n\t * @return the requested audience value(s), or an empty {@code Set} if not available\n\t */\n\tpublic Set<String> getAudiences() {\n\t\treturn this.audiences;\n\t}\n\n\t/**\n\t * Returns the requested scope(s).\n\t * @return the requested scope(s), or an empty {@code Set} if not available\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeCompositeAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for the OAuth 2.0 Token Exchange Grant to\n * represent the principal in a composite token (e.g. the \"delegation\" use case).\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2TokenExchangeAuthenticationToken\n */\npublic class OAuth2TokenExchangeCompositeAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate final Authentication subject;\n\n\tprivate final List<OAuth2TokenExchangeActor> actors;\n\n\tpublic OAuth2TokenExchangeCompositeAuthenticationToken(Authentication subject,\n\t\t\tList<OAuth2TokenExchangeActor> actors) {\n\t\tsuper((subject != null) ? subject.getAuthorities() : null);\n\t\tAssert.notNull(subject, \"subject cannot be null\");\n\t\tAssert.notNull(actors, \"actors cannot be null\");\n\t\tthis.subject = subject;\n\t\tthis.actors = Collections.unmodifiableList(new ArrayList<>(actors));\n\t\tsetDetails(subject.getDetails());\n\t\tsetAuthenticated(subject.isAuthenticated());\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.subject.getPrincipal();\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn null;\n\t}\n\n\tpublic Authentication getSubject() {\n\t\treturn this.subject;\n\t}\n\n\tpublic List<OAuth2TokenExchangeActor> getActors() {\n\t\treturn this.actors;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (!(obj instanceof OAuth2TokenExchangeCompositeAuthenticationToken other)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn super.equals(obj) && Objects.equals(this.subject, other.subject)\n\t\t\t\t&& Objects.equals(this.actors, other.actors);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(super.hashCode(), this.subject, this.actors);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for OAuth 2.0 Token Introspection.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenIntrospectionAuthenticationToken\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7662#section-2.1\">Section\n * 2.1 Introspection Request</a>\n */\npublic final class OAuth2TokenIntrospectionAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);\n\n\tprivate static final TypeDescriptor LIST_STRING_TYPE_DESCRIPTOR = TypeDescriptor.collection(List.class,\n\t\t\tTypeDescriptor.valueOf(String.class));\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\t/**\n\t * Constructs an {@code OAuth2TokenIntrospectionAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t */\n\tpublic OAuth2TokenIntrospectionAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = (OAuth2TokenIntrospectionAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(tokenIntrospectionAuthentication);\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(tokenIntrospectionAuthentication.getToken(), null);\n\t\tif (authorization == null) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Did not authenticate token introspection request since token was not found\");\n\t\t\t}\n\t\t\t// Return the authentication request when token not found\n\t\t\treturn tokenIntrospectionAuthentication;\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with token\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2Token> authorizedToken = authorization\n\t\t\t.getToken(tokenIntrospectionAuthentication.getToken());\n\t\tif (!authorizedToken.isActive()) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Did not introspect token since not active\");\n\t\t\t}\n\t\t\treturn new OAuth2TokenIntrospectionAuthenticationToken(tokenIntrospectionAuthentication.getToken(),\n\t\t\t\t\tclientPrincipal, OAuth2TokenIntrospection.builder().build());\n\t\t}\n\n\t\tRegisteredClient authorizedClient = this.registeredClientRepository\n\t\t\t.findById(authorization.getRegisteredClientId());\n\t\tOAuth2TokenIntrospection tokenClaims = withActiveTokenClaims(authorizedToken, authorizedClient);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated token introspection request\");\n\t\t}\n\n\t\treturn new OAuth2TokenIntrospectionAuthenticationToken(authorizedToken.getToken().getTokenValue(),\n\t\t\t\tclientPrincipal, tokenClaims);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2TokenIntrospectionAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tprivate static OAuth2TokenIntrospection withActiveTokenClaims(\n\t\t\tOAuth2Authorization.Token<OAuth2Token> authorizedToken, RegisteredClient authorizedClient) {\n\n\t\tOAuth2TokenIntrospection.Builder tokenClaims;\n\t\tif (!CollectionUtils.isEmpty(authorizedToken.getClaims())) {\n\t\t\tMap<String, Object> claims = convertClaimsIfNecessary(authorizedToken.getClaims());\n\t\t\ttokenClaims = OAuth2TokenIntrospection.withClaims(claims).active(true);\n\t\t}\n\t\telse {\n\t\t\ttokenClaims = OAuth2TokenIntrospection.builder(true);\n\t\t}\n\n\t\ttokenClaims.clientId(authorizedClient.getClientId());\n\n\t\t// TODO Set \"username\"\n\n\t\tOAuth2Token token = authorizedToken.getToken();\n\t\tif (token.getIssuedAt() != null) {\n\t\t\ttokenClaims.issuedAt(token.getIssuedAt());\n\t\t}\n\t\tif (token.getExpiresAt() != null) {\n\t\t\ttokenClaims.expiresAt(token.getExpiresAt());\n\t\t}\n\n\t\tif (OAuth2AccessToken.class.isAssignableFrom(token.getClass())) {\n\t\t\tOAuth2AccessToken accessToken = (OAuth2AccessToken) token;\n\t\t\ttokenClaims.tokenType(accessToken.getTokenType().getValue());\n\t\t}\n\n\t\treturn tokenClaims.build();\n\t}\n\n\tprivate static Map<String, Object> convertClaimsIfNecessary(Map<String, Object> claims) {\n\t\tMap<String, Object> convertedClaims = new HashMap<>(claims);\n\n\t\tObject value = claims.get(OAuth2TokenIntrospectionClaimNames.ISS);\n\t\tif (value != null && !(value instanceof URL)) {\n\t\t\tURL convertedValue = ClaimConversionService.getSharedInstance().convert(value, URL.class);\n\t\t\tif (convertedValue != null) {\n\t\t\t\tconvertedClaims.put(OAuth2TokenIntrospectionClaimNames.ISS, convertedValue);\n\t\t\t}\n\t\t}\n\n\t\tvalue = claims.get(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\tif (value != null && !(value instanceof List)) {\n\t\t\tObject convertedValue = ClaimConversionService.getSharedInstance()\n\t\t\t\t.convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR);\n\t\t\tif (convertedValue != null) {\n\t\t\t\tconvertedClaims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, convertedValue);\n\t\t\t}\n\t\t}\n\n\t\tvalue = claims.get(OAuth2TokenIntrospectionClaimNames.AUD);\n\t\tif (value != null && !(value instanceof List)) {\n\t\t\tObject convertedValue = ClaimConversionService.getSharedInstance()\n\t\t\t\t.convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR);\n\t\t\tif (convertedValue != null) {\n\t\t\t\tconvertedClaims.put(OAuth2TokenIntrospectionClaimNames.AUD, convertedValue);\n\t\t\t}\n\t\t}\n\n\t\treturn convertedClaims;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for OAuth 2.0 Token Introspection.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OAuth2TokenIntrospection\n * @see OAuth2TokenIntrospectionAuthenticationProvider\n */\npublic class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 9003173975452760956L;\n\n\tprivate final String token;\n\n\tprivate final Authentication clientPrincipal;\n\n\tprivate final String tokenTypeHint;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\tprivate final OAuth2TokenIntrospection tokenClaims;\n\n\t/**\n\t * Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the\n\t * provided parameters.\n\t * @param token the token\n\t * @param clientPrincipal the authenticated client principal\n\t * @param tokenTypeHint the token type hint\n\t * @param additionalParameters the additional parameters\n\t */\n\tpublic OAuth2TokenIntrospectionAuthenticationToken(String token, Authentication clientPrincipal,\n\t\t\t@Nullable String tokenTypeHint, @Nullable Map<String, Object> additionalParameters) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(token, \"token cannot be empty\");\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\t\tthis.token = token;\n\t\tthis.clientPrincipal = clientPrincipal;\n\t\tthis.tokenTypeHint = tokenTypeHint;\n\t\tthis.additionalParameters = Collections.unmodifiableMap(\n\t\t\t\t(additionalParameters != null) ? new HashMap<>(additionalParameters) : Collections.emptyMap());\n\t\tthis.tokenClaims = OAuth2TokenIntrospection.builder().build();\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the\n\t * provided parameters.\n\t * @param token the token\n\t * @param clientPrincipal the authenticated client principal\n\t * @param tokenClaims the token claims\n\t */\n\tpublic OAuth2TokenIntrospectionAuthenticationToken(String token, Authentication clientPrincipal,\n\t\t\tOAuth2TokenIntrospection tokenClaims) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(token, \"token cannot be empty\");\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\t\tAssert.notNull(tokenClaims, \"tokenClaims cannot be null\");\n\t\tthis.token = token;\n\t\tthis.clientPrincipal = clientPrincipal;\n\t\tthis.tokenTypeHint = null;\n\t\tthis.additionalParameters = Collections.emptyMap();\n\t\tthis.tokenClaims = tokenClaims;\n\t\t// Indicates that the request was authenticated, even though the token might not\n\t\t// be active\n\t\tsetAuthenticated(true);\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.clientPrincipal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the token.\n\t * @return the token\n\t */\n\tpublic String getToken() {\n\t\treturn this.token;\n\t}\n\n\t/**\n\t * Returns the token type hint.\n\t * @return the token type hint\n\t */\n\t@Nullable\n\tpublic String getTokenTypeHint() {\n\t\treturn this.tokenTypeHint;\n\t}\n\n\t/**\n\t * Returns the additional parameters.\n\t * @return the additional parameters\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n\t/**\n\t * Returns the token claims.\n\t * @return the {@link OAuth2TokenIntrospection}\n\t */\n\tpublic OAuth2TokenIntrospection getTokenClaims() {\n\t\treturn this.tokenClaims;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation for OAuth 2.0 Token Revocation.\n *\n * @author Vivek Babu\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenRevocationAuthenticationToken\n * @see OAuth2AuthorizationService\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7009#section-2.1\">Section\n * 2.1 Revocation Request</a>\n */\npublic final class OAuth2TokenRevocationAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\t/**\n\t * Constructs an {@code OAuth2TokenRevocationAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param authorizationService the authorization service\n\t */\n\tpublic OAuth2TokenRevocationAuthenticationProvider(OAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = (OAuth2TokenRevocationAuthenticationToken) authentication;\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils\n\t\t\t.getAuthenticatedClientElseThrowInvalidClient(tokenRevocationAuthentication);\n\t\tRegisteredClient registeredClient = clientPrincipal.getRegisteredClient();\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(tokenRevocationAuthentication.getToken(), null);\n\t\tif (authorization == null) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Did not authenticate token revocation request since token was not found\");\n\t\t\t}\n\t\t\t// Return the authentication request when token not found\n\t\t\treturn tokenRevocationAuthentication;\n\t\t}\n\n\t\tif (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2Token> token = authorization.getToken(tokenRevocationAuthentication.getToken());\n\t\tauthorization = OAuth2Authorization.from(authorization).invalidate(token.getToken()).build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization with revoked token\");\n\t\t\t// This log is kept separate for consistency with other providers\n\t\t\tthis.logger.trace(\"Authenticated token revocation request\");\n\t\t}\n\n\t\treturn new OAuth2TokenRevocationAuthenticationToken(token.getToken(), clientPrincipal);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2TokenRevocationAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for OAuth 2.0 Token Revocation.\n *\n * @author Vivek Babu\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OAuth2TokenRevocationAuthenticationProvider\n */\npublic class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -880609099230203249L;\n\n\tprivate final String token;\n\n\tprivate final Authentication clientPrincipal;\n\n\tprivate final String tokenTypeHint;\n\n\t/**\n\t * Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided\n\t * parameters.\n\t * @param token the token\n\t * @param clientPrincipal the authenticated client principal\n\t * @param tokenTypeHint the token type hint\n\t */\n\tpublic OAuth2TokenRevocationAuthenticationToken(String token, Authentication clientPrincipal,\n\t\t\t@Nullable String tokenTypeHint) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(token, \"token cannot be empty\");\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\t\tthis.token = token;\n\t\tthis.clientPrincipal = clientPrincipal;\n\t\tthis.tokenTypeHint = tokenTypeHint;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided\n\t * parameters.\n\t * @param revokedToken the revoked token\n\t * @param clientPrincipal the authenticated client principal\n\t */\n\tpublic OAuth2TokenRevocationAuthenticationToken(OAuth2Token revokedToken, Authentication clientPrincipal) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(revokedToken, \"revokedToken cannot be null\");\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\t\tthis.token = revokedToken.getTokenValue();\n\t\tthis.clientPrincipal = clientPrincipal;\n\t\tthis.tokenTypeHint = null;\n\t\tsetAuthenticated(true); // Indicates that the token was authenticated and revoked\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.clientPrincipal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the token.\n\t * @return the token\n\t */\n\tpublic String getToken() {\n\t\treturn this.token;\n\t}\n\n\t/**\n\t * Returns the token type hint.\n\t * @return the token type hint\n\t */\n\t@Nullable\n\tpublic String getTokenTypeHint() {\n\t\treturn this.tokenTypeHint;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OidcPrompt.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\n/**\n * The values defined for the \"prompt\" parameter for the OpenID Connect 1.0 Authentication\n * Request.\n *\n * @author Joe Grandja\n * @since 7.0\n */\nfinal class OidcPrompt {\n\n\tstatic final String NONE = \"none\";\n\n\tstatic final String LOGIN = \"login\";\n\n\tstatic final String CONSENT = \"consent\";\n\n\tstatic final String SELECT_ACCOUNT = \"select_account\";\n\n\tprivate OidcPrompt() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Public Client\n * Authentication, which authenticates the {@link PkceParameterNames#CODE_VERIFIER\n * code_verifier} parameter.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationProvider\n * @see OAuth2ClientAuthenticationToken\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n */\npublic final class PublicClientAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final CodeVerifierAuthenticator codeVerifierAuthenticator;\n\n\t/**\n\t * Constructs a {@code PublicClientAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t */\n\tpublic PublicClientAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.codeVerifierAuthenticator = new CodeVerifierAuthenticator(authorizationService);\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;\n\n\t\tif (!ClientAuthenticationMethod.NONE.equals(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString clientId = clientAuthentication.getPrincipal().toString();\n\t\tRegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);\n\t\tif (registeredClient == null) {\n\t\t\tthrowInvalidClient(OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tif (!registeredClient.getClientAuthenticationMethods()\n\t\t\t.contains(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\tthrowInvalidClient(\"authentication_method\");\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated client authentication parameters\");\n\t\t}\n\n\t\t// Validate the \"code_verifier\" parameter for the public client\n\t\tthis.codeVerifierAuthenticator.authenticateRequired(clientAuthentication, registeredClient);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated public client\");\n\t\t}\n\n\t\treturn new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tclientAuthentication.getClientAuthenticationMethod(), null);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tprivate static void throwInvalidClient(String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\"Client authentication failed: \" + parameterName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/X509ClientCertificateAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.cert.X509Certificate;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client\n * Authentication, which authenticates the client {@code X509Certificate} received when\n * the {@code tls_client_auth} or {@code self_signed_tls_client_auth} authentication\n * method is used.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationProvider\n * @see OAuth2ClientAuthenticationToken\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n */\npublic final class X509ClientCertificateAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final CodeVerifierAuthenticator codeVerifierAuthenticator;\n\n\tprivate final Consumer<OAuth2ClientAuthenticationContext> selfSignedCertificateVerifier = new X509SelfSignedCertificateVerifier();\n\n\tprivate Consumer<OAuth2ClientAuthenticationContext> certificateVerifier = this::verifyX509Certificate;\n\n\t/**\n\t * Constructs a {@code X509ClientCertificateAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t */\n\tpublic X509ClientCertificateAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.codeVerifierAuthenticator = new CodeVerifierAuthenticator(authorizationService);\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;\n\n\t\tif (!ClientAuthenticationMethod.TLS_CLIENT_AUTH.equals(clientAuthentication.getClientAuthenticationMethod())\n\t\t\t\t&& !ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH\n\t\t\t\t\t.equals(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString clientId = clientAuthentication.getPrincipal().toString();\n\t\tRegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);\n\t\tif (registeredClient == null) {\n\t\t\tthrowInvalidClient(OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tif (!registeredClient.getClientAuthenticationMethods()\n\t\t\t.contains(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\tthrowInvalidClient(\"authentication_method\");\n\t\t}\n\n\t\tif (!(clientAuthentication.getCredentials() instanceof X509Certificate[])) {\n\t\t\tthrowInvalidClient(\"credentials\");\n\t\t}\n\n\t\tOAuth2ClientAuthenticationContext authenticationContext = OAuth2ClientAuthenticationContext\n\t\t\t.with(clientAuthentication)\n\t\t\t.registeredClient(registeredClient)\n\t\t\t.build();\n\t\tthis.certificateVerifier.accept(authenticationContext);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated client authentication parameters\");\n\t\t}\n\n\t\t// Validate the \"code_verifier\" parameter for the confidential client, if\n\t\t// available\n\t\tthis.codeVerifierAuthenticator.authenticateIfAvailable(clientAuthentication, registeredClient);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated client X509Certificate\");\n\t\t}\n\n\t\treturn new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tclientAuthentication.getClientAuthenticationMethod(), clientAuthentication.getCredentials());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2ClientAuthenticationContext} and is responsible for verifying the\n\t * client {@code X509Certificate} associated in the\n\t * {@link OAuth2ClientAuthenticationToken}. The default implementation for the\n\t * {@code tls_client_auth} authentication method verifies the\n\t * {@link ClientSettings#getX509CertificateSubjectDN() expected subject distinguished\n\t * name}.\n\t *\n\t * <p>\n\t * <b>NOTE:</b> If verification fails, an {@link OAuth2AuthenticationException} MUST\n\t * be thrown.\n\t * @param certificateVerifier the {@code Consumer} providing access to the\n\t * {@link OAuth2ClientAuthenticationContext} and is responsible for verifying the\n\t * client {@code X509Certificate}\n\t */\n\tpublic void setCertificateVerifier(Consumer<OAuth2ClientAuthenticationContext> certificateVerifier) {\n\t\tAssert.notNull(certificateVerifier, \"certificateVerifier cannot be null\");\n\t\tthis.certificateVerifier = certificateVerifier;\n\t}\n\n\tprivate void verifyX509Certificate(OAuth2ClientAuthenticationContext clientAuthenticationContext) {\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();\n\t\tif (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH\n\t\t\t.equals(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\tthis.selfSignedCertificateVerifier.accept(clientAuthenticationContext);\n\t\t}\n\t\telse {\n\t\t\tverifyX509CertificateSubjectDN(clientAuthenticationContext);\n\t\t}\n\t}\n\n\tprivate void verifyX509CertificateSubjectDN(OAuth2ClientAuthenticationContext clientAuthenticationContext) {\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();\n\t\tRegisteredClient registeredClient = clientAuthenticationContext.getRegisteredClient();\n\t\tX509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();\n\t\tX509Certificate clientCertificate = clientCertificateChain[0];\n\t\tString expectedSubjectDN = registeredClient.getClientSettings().getX509CertificateSubjectDN();\n\t\tif (!StringUtils.hasText(expectedSubjectDN)\n\t\t\t\t|| !clientCertificate.getSubjectX500Principal().getName().equals(expectedSubjectDN)) {\n\t\t\tthrowInvalidClient(\"x509_certificate_subject_dn\");\n\t\t}\n\t}\n\n\tprivate static void throwInvalidClient(String parameterName) {\n\t\tthrowInvalidClient(parameterName, null);\n\t}\n\n\tprivate static void throwInvalidClient(String parameterName, Throwable cause) {\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\"Client authentication failed: \" + parameterName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error, error.toString(), cause);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/X509SelfSignedCertificateVerifier.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.security.PublicKey;\nimport java.security.cert.X509Certificate;\nimport java.text.ParseException;\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport javax.security.auth.x500.X500Principal;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSet;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.client.SimpleClientHttpRequestFactory;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestOperations;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * The default {@code X509Certificate} verifier for the\n * {@code self_signed_tls_client_auth} authentication method.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see X509ClientCertificateAuthenticationProvider#setCertificateVerifier(Consumer)\n */\nfinal class X509SelfSignedCertificateVerifier implements Consumer<OAuth2ClientAuthenticationContext> {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1\";\n\n\tprivate static final JWKMatcher HAS_X509_CERT_CHAIN_MATCHER = new JWKMatcher.Builder().hasX509CertChain(true)\n\t\t.build();\n\n\tprivate final Function<RegisteredClient, JWKSet> jwkSetSupplier = new JwkSetSupplier();\n\n\t@Override\n\tpublic void accept(OAuth2ClientAuthenticationContext clientAuthenticationContext) {\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();\n\t\tRegisteredClient registeredClient = clientAuthenticationContext.getRegisteredClient();\n\t\tX509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();\n\t\tX509Certificate clientCertificate = clientCertificateChain[0];\n\n\t\tX500Principal issuer = clientCertificate.getIssuerX500Principal();\n\t\tX500Principal subject = clientCertificate.getSubjectX500Principal();\n\t\tif (issuer == null || !issuer.equals(subject)) {\n\t\t\tthrowInvalidClient(\"x509_certificate_issuer\");\n\t\t}\n\n\t\tJWKSet jwkSet = this.jwkSetSupplier.apply(registeredClient);\n\n\t\tboolean publicKeyMatches = false;\n\t\tfor (JWK jwk : jwkSet.filter(HAS_X509_CERT_CHAIN_MATCHER).getKeys()) {\n\t\t\tX509Certificate x509Certificate = jwk.getParsedX509CertChain().get(0);\n\t\t\tPublicKey publicKey = x509Certificate.getPublicKey();\n\t\t\tif (Arrays.equals(clientCertificate.getPublicKey().getEncoded(), publicKey.getEncoded())) {\n\t\t\t\tpublicKeyMatches = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!publicKeyMatches) {\n\t\t\tthrowInvalidClient(\"x509_certificate\");\n\t\t}\n\t}\n\n\tprivate static void throwInvalidClient(String parameterName) {\n\t\tthrowInvalidClient(parameterName, null);\n\t}\n\n\tprivate static void throwInvalidClient(String parameterName, Throwable cause) {\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\t\"Client authentication failed: \" + parameterName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error, error.toString(), cause);\n\t}\n\n\tprivate static final class JwkSetSupplier implements Function<RegisteredClient, JWKSet> {\n\n\t\tprivate static final MediaType APPLICATION_JWK_SET_JSON = new MediaType(\"application\", \"jwk-set+json\");\n\n\t\tprivate final RestOperations restOperations;\n\n\t\tprivate final Map<String, Supplier<JWKSet>> jwkSets = new ConcurrentHashMap<>();\n\n\t\tprivate JwkSetSupplier() {\n\t\t\tSimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();\n\t\t\trequestFactory.setConnectTimeout(15_000);\n\t\t\trequestFactory.setReadTimeout(15_000);\n\t\t\tthis.restOperations = new RestTemplate(requestFactory);\n\t\t}\n\n\t\t@Override\n\t\tpublic JWKSet apply(RegisteredClient registeredClient) {\n\t\t\tSupplier<JWKSet> jwkSetSupplier = this.jwkSets.computeIfAbsent(registeredClient.getId(), (key) -> {\n\t\t\t\tif (!StringUtils.hasText(registeredClient.getClientSettings().getJwkSetUrl())) {\n\t\t\t\t\tthrowInvalidClient(\"client_jwk_set_url\");\n\t\t\t\t}\n\t\t\t\treturn new JwkSetHolder(registeredClient.getClientSettings().getJwkSetUrl());\n\t\t\t});\n\t\t\treturn jwkSetSupplier.get();\n\t\t}\n\n\t\tprivate JWKSet retrieve(String jwkSetUrl) {\n\t\t\tURI jwkSetUri = null;\n\t\t\ttry {\n\t\t\t\tjwkSetUri = new URI(jwkSetUrl);\n\t\t\t}\n\t\t\tcatch (URISyntaxException ex) {\n\t\t\t\tthrowInvalidClient(\"jwk_set_uri\", ex);\n\t\t\t}\n\n\t\t\tHttpHeaders headers = new HttpHeaders();\n\t\t\theaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON));\n\t\t\tRequestEntity<Void> request = new RequestEntity<>(headers, HttpMethod.GET, jwkSetUri);\n\t\t\tResponseEntity<String> response = null;\n\t\t\ttry {\n\t\t\t\tresponse = this.restOperations.exchange(request, String.class);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrowInvalidClient(\"jwk_set_response_error\", ex);\n\t\t\t}\n\t\t\tif (response.getStatusCode().value() != 200) {\n\t\t\t\tthrowInvalidClient(\"jwk_set_response_status\");\n\t\t\t}\n\n\t\t\tJWKSet jwkSet = null;\n\t\t\ttry {\n\t\t\t\tjwkSet = JWKSet.parse(response.getBody());\n\t\t\t}\n\t\t\tcatch (ParseException ex) {\n\t\t\t\tthrowInvalidClient(\"jwk_set_response_body\", ex);\n\t\t\t}\n\n\t\t\treturn jwkSet;\n\t\t}\n\n\t\tprivate final class JwkSetHolder implements Supplier<JWKSet> {\n\n\t\t\tprivate final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();\n\n\t\t\tprivate final Clock clock = Clock.systemUTC();\n\n\t\t\tprivate final String jwkSetUrl;\n\n\t\t\tprivate JWKSet jwkSet;\n\n\t\t\tprivate Instant lastUpdatedAt;\n\n\t\t\tprivate JwkSetHolder(String jwkSetUrl) {\n\t\t\t\tthis.jwkSetUrl = jwkSetUrl;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic JWKSet get() {\n\t\t\t\tthis.rwLock.readLock().lock();\n\t\t\t\tif (shouldRefresh()) {\n\t\t\t\t\tthis.rwLock.readLock().unlock();\n\t\t\t\t\tthis.rwLock.writeLock().lock();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (shouldRefresh()) {\n\t\t\t\t\t\t\tthis.jwkSet = retrieve(this.jwkSetUrl);\n\t\t\t\t\t\t\tthis.lastUpdatedAt = Instant.now();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.rwLock.readLock().lock();\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\tthis.rwLock.writeLock().unlock();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\treturn this.jwkSet;\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tthis.rwLock.readLock().unlock();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate boolean shouldRefresh() {\n\t\t\t\t// Refresh every 5 minutes\n\t\t\t\treturn (this.jwkSet == null\n\t\t\t\t\t\t|| this.clock.instant().isAfter(this.lastUpdatedAt.plus(5, ChronoUnit.MINUTES)));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.client;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link RegisteredClientRepository} that stores {@link RegisteredClient}(s) in-memory.\n *\n * <p>\n * <b>NOTE:</b> This implementation is recommended ONLY to be used during\n * development/testing.\n *\n * @author Anoop Garlapati\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @since 7.0\n * @see RegisteredClientRepository\n * @see RegisteredClient\n */\npublic final class InMemoryRegisteredClientRepository implements RegisteredClientRepository {\n\n\tprivate final Map<String, RegisteredClient> idRegistrationMap;\n\n\tprivate final Map<String, RegisteredClient> clientIdRegistrationMap;\n\n\t/**\n\t * Constructs an {@code InMemoryRegisteredClientRepository} using the provided\n\t * parameters.\n\t * @param registrations the client registration(s)\n\t */\n\tpublic InMemoryRegisteredClientRepository(RegisteredClient... registrations) {\n\t\tthis(Arrays.asList(registrations));\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryRegisteredClientRepository} using the provided\n\t * parameters.\n\t * @param registrations the client registration(s)\n\t */\n\tpublic InMemoryRegisteredClientRepository(List<RegisteredClient> registrations) {\n\t\tAssert.notEmpty(registrations, \"registrations cannot be empty\");\n\t\tConcurrentHashMap<String, RegisteredClient> idRegistrationMapResult = new ConcurrentHashMap<>();\n\t\tConcurrentHashMap<String, RegisteredClient> clientIdRegistrationMapResult = new ConcurrentHashMap<>();\n\t\tfor (RegisteredClient registration : registrations) {\n\t\t\tAssert.notNull(registration, \"registration cannot be null\");\n\t\t\tassertUniqueIdentifiers(registration, idRegistrationMapResult);\n\t\t\tidRegistrationMapResult.put(registration.getId(), registration);\n\t\t\tclientIdRegistrationMapResult.put(registration.getClientId(), registration);\n\t\t}\n\t\tthis.idRegistrationMap = idRegistrationMapResult;\n\t\tthis.clientIdRegistrationMap = clientIdRegistrationMapResult;\n\t}\n\n\t@Override\n\tpublic void save(RegisteredClient registeredClient) {\n\t\tAssert.notNull(registeredClient, \"registeredClient cannot be null\");\n\t\tif (!this.idRegistrationMap.containsKey(registeredClient.getId())) {\n\t\t\tassertUniqueIdentifiers(registeredClient, this.idRegistrationMap);\n\t\t}\n\t\tthis.idRegistrationMap.put(registeredClient.getId(), registeredClient);\n\t\tthis.clientIdRegistrationMap.put(registeredClient.getClientId(), registeredClient);\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic RegisteredClient findById(String id) {\n\t\tAssert.hasText(id, \"id cannot be empty\");\n\t\treturn this.idRegistrationMap.get(id);\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic RegisteredClient findByClientId(String clientId) {\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\treturn this.clientIdRegistrationMap.get(clientId);\n\t}\n\n\tprivate void assertUniqueIdentifiers(RegisteredClient registeredClient,\n\t\t\tMap<String, RegisteredClient> registrations) {\n\t\tregistrations.values().forEach((registration) -> {\n\t\t\tif (registeredClient.getId().equals(registration.getId())) {\n\t\t\t\tthrow new IllegalArgumentException(\"Registered client must be unique. \" + \"Found duplicate identifier: \"\n\t\t\t\t\t\t+ registeredClient.getId());\n\t\t\t}\n\t\t\tif (registeredClient.getClientId().equals(registration.getClientId())) {\n\t\t\t\tthrow new IllegalArgumentException(\"Registered client must be unique. \"\n\t\t\t\t\t\t+ \"Found duplicate client identifier: \" + registeredClient.getClientId());\n\t\t\t}\n\t\t\tif (StringUtils.hasText(registeredClient.getClientSecret())\n\t\t\t\t\t&& registeredClient.getClientSecret().equals(registration.getClientSecret())) {\n\t\t\t\tthrow new IllegalArgumentException(\"Registered client must be unique. \"\n\t\t\t\t\t\t+ \"Found duplicate client secret for identifier: \" + registeredClient.getId());\n\t\t\t}\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.client;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.Module;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport tools.jackson.databind.JacksonModule;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.context.annotation.ImportRuntimeHints;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ConfigurationSettingNames;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A JDBC implementation of a {@link RegisteredClientRepository} that uses a\n * {@link JdbcOperations} for {@link RegisteredClient} persistence.\n *\n * <p>\n * <b>IMPORTANT:</b> This {@code RegisteredClientRepository} depends on the table\n * definition described in\n * \"classpath:org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\"\n * and therefore MUST be defined in the database schema.\n *\n * <p>\n * <b>NOTE:</b> This {@code RegisteredClientRepository} is a simplified JDBC\n * implementation that MAY be used in a production environment. However, it does have\n * limitations as it likely won't perform well in an environment requiring high\n * throughput. The expectation is that the consuming application will provide their own\n * implementation of {@code RegisteredClientRepository} that meets the performance\n * requirements for its deployment environment.\n *\n * @author Rafal Lewczuk\n * @author Joe Grandja\n * @author Ovidiu Popa\n * @author Josh Long\n * @since 7.0\n * @see RegisteredClientRepository\n * @see RegisteredClient\n * @see JdbcOperations\n * @see RowMapper\n */\n@ImportRuntimeHints(JdbcRegisteredClientRepository.JdbcRegisteredClientRepositoryRuntimeHintsRegistrar.class)\npublic class JdbcRegisteredClientRepository implements RegisteredClientRepository {\n\n\t// @formatter:off\n\tprivate static final String COLUMN_NAMES = \"id, \"\n\t\t\t+ \"client_id, \"\n\t\t\t+ \"client_id_issued_at, \"\n\t\t\t+ \"client_secret, \"\n\t\t\t+ \"client_secret_expires_at, \"\n\t\t\t+ \"client_name, \"\n\t\t\t+ \"client_authentication_methods, \"\n\t\t\t+ \"authorization_grant_types, \"\n\t\t\t+ \"redirect_uris, \"\n\t\t\t+ \"post_logout_redirect_uris, \"\n\t\t\t+ \"scopes, \"\n\t\t\t+ \"client_settings,\"\n\t\t\t+ \"token_settings\";\n\t// @formatter:on\n\n\tprivate static final String TABLE_NAME = \"oauth2_registered_client\";\n\n\tprivate static final String PK_FILTER = \"id = ?\";\n\n\tprivate static final String LOAD_REGISTERED_CLIENT_SQL = \"SELECT \" + COLUMN_NAMES + \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \";\n\n\t// @formatter:off\n\tprivate static final String INSERT_REGISTERED_CLIENT_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t+ \"(\" + COLUMN_NAMES + \") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String UPDATE_REGISTERED_CLIENT_SQL = \"UPDATE \" + TABLE_NAME\n\t\t\t+ \" SET client_secret = ?, client_secret_expires_at = ?, client_name = ?, client_authentication_methods = ?,\"\n\t\t\t+ \" authorization_grant_types = ?, redirect_uris = ?, post_logout_redirect_uris = ?, scopes = ?,\"\n\t\t\t+ \" client_settings = ?, token_settings = ?\"\n\t\t\t+ \" WHERE \" + PK_FILTER;\n\t// @formatter:on\n\n\tprivate static final String COUNT_REGISTERED_CLIENT_SQL = \"SELECT COUNT(*) FROM \" + TABLE_NAME + \" WHERE \";\n\n\tprivate final JdbcOperations jdbcOperations;\n\n\tprivate RowMapper<RegisteredClient> registeredClientRowMapper;\n\n\tprivate Function<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper;\n\n\t/**\n\t * Constructs a {@code JdbcRegisteredClientRepository} using the provided parameters.\n\t * @param jdbcOperations the JDBC operations\n\t */\n\tpublic JdbcRegisteredClientRepository(JdbcOperations jdbcOperations) {\n\t\tAssert.notNull(jdbcOperations, \"jdbcOperations cannot be null\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t\tthis.registeredClientRowMapper = new JsonMapperRegisteredClientRowMapper();\n\t\tthis.registeredClientParametersMapper = new JsonMapperRegisteredClientParametersMapper();\n\t}\n\n\t@Override\n\tpublic void save(RegisteredClient registeredClient) {\n\t\tAssert.notNull(registeredClient, \"registeredClient cannot be null\");\n\t\tRegisteredClient existingRegisteredClient = findBy(PK_FILTER, registeredClient.getId());\n\t\tif (existingRegisteredClient != null) {\n\t\t\tupdateRegisteredClient(registeredClient);\n\t\t}\n\t\telse {\n\t\t\tinsertRegisteredClient(registeredClient);\n\t\t}\n\t}\n\n\tprivate void updateRegisteredClient(RegisteredClient registeredClient) {\n\t\tList<SqlParameterValue> parameters = new ArrayList<>(\n\t\t\t\tthis.registeredClientParametersMapper.apply(registeredClient));\n\t\tSqlParameterValue id = parameters.remove(0);\n\t\tparameters.remove(0); // remove client_id\n\t\tparameters.remove(0); // remove client_id_issued_at\n\t\tparameters.add(id);\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\tthis.jdbcOperations.update(UPDATE_REGISTERED_CLIENT_SQL, pss);\n\t}\n\n\tprivate void insertRegisteredClient(RegisteredClient registeredClient) {\n\t\tassertUniqueIdentifiers(registeredClient);\n\t\tList<SqlParameterValue> parameters = this.registeredClientParametersMapper.apply(registeredClient);\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\tthis.jdbcOperations.update(INSERT_REGISTERED_CLIENT_SQL, pss);\n\t}\n\n\tprivate void assertUniqueIdentifiers(RegisteredClient registeredClient) {\n\t\tInteger count = this.jdbcOperations.queryForObject(COUNT_REGISTERED_CLIENT_SQL + \"client_id = ?\", Integer.class,\n\t\t\t\tregisteredClient.getClientId());\n\t\tif (count != null && count > 0) {\n\t\t\tthrow new IllegalArgumentException(\"Registered client must be unique. \"\n\t\t\t\t\t+ \"Found duplicate client identifier: \" + registeredClient.getClientId());\n\t\t}\n\t\tif (StringUtils.hasText(registeredClient.getClientSecret())) {\n\t\t\tcount = this.jdbcOperations.queryForObject(COUNT_REGISTERED_CLIENT_SQL + \"client_secret = ?\", Integer.class,\n\t\t\t\t\tregisteredClient.getClientSecret());\n\t\t\tif (count != null && count > 0) {\n\t\t\t\tthrow new IllegalArgumentException(\"Registered client must be unique. \"\n\t\t\t\t\t\t+ \"Found duplicate client secret for identifier: \" + registeredClient.getId());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic RegisteredClient findById(String id) {\n\t\tAssert.hasText(id, \"id cannot be empty\");\n\t\treturn findBy(\"id = ?\", id);\n\t}\n\n\t@Override\n\tpublic RegisteredClient findByClientId(String clientId) {\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\treturn findBy(\"client_id = ?\", clientId);\n\t}\n\n\tprivate RegisteredClient findBy(String filter, Object... args) {\n\t\tList<RegisteredClient> result = this.jdbcOperations.query(LOAD_REGISTERED_CLIENT_SQL + filter,\n\t\t\t\tthis.registeredClientRowMapper, args);\n\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t}\n\n\t/**\n\t * Sets the {@link RowMapper} used for mapping the current row in\n\t * {@code java.sql.ResultSet} to {@link RegisteredClient}. The default is\n\t * {@link JsonMapperRegisteredClientRowMapper}.\n\t * @param registeredClientRowMapper the {@link RowMapper} used for mapping the current\n\t * row in {@code ResultSet} to {@link RegisteredClient}\n\t */\n\tpublic final void setRegisteredClientRowMapper(RowMapper<RegisteredClient> registeredClientRowMapper) {\n\t\tAssert.notNull(registeredClientRowMapper, \"registeredClientRowMapper cannot be null\");\n\t\tthis.registeredClientRowMapper = registeredClientRowMapper;\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping {@link RegisteredClient} to a\n\t * {@code List} of {@link SqlParameterValue}. The default is\n\t * {@link JsonMapperRegisteredClientParametersMapper}.\n\t * @param registeredClientParametersMapper the {@code Function} used for mapping\n\t * {@link RegisteredClient} to a {@code List} of {@link SqlParameterValue}\n\t */\n\tpublic final void setRegisteredClientParametersMapper(\n\t\t\tFunction<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper) {\n\t\tAssert.notNull(registeredClientParametersMapper, \"registeredClientParametersMapper cannot be null\");\n\t\tthis.registeredClientParametersMapper = registeredClientParametersMapper;\n\t}\n\n\tprotected final JdbcOperations getJdbcOperations() {\n\t\treturn this.jdbcOperations;\n\t}\n\n\tprotected final RowMapper<RegisteredClient> getRegisteredClientRowMapper() {\n\t\treturn this.registeredClientRowMapper;\n\t}\n\n\tprotected final Function<RegisteredClient, List<SqlParameterValue>> getRegisteredClientParametersMapper() {\n\t\treturn this.registeredClientParametersMapper;\n\t}\n\n\t/**\n\t * The default {@link RowMapper} that maps the current row in\n\t * {@code java.sql.ResultSet} to {@link RegisteredClient} using Jackson 3's\n\t * {@link JsonMapper}.\n\t *\n\t * @author Joe Grandja\n\t * @since 7.0\n\t */\n\tpublic static class JsonMapperRegisteredClientRowMapper extends AbstractRegisteredClientRowMapper {\n\n\t\tprivate final JsonMapper jsonMapper;\n\n\t\tpublic JsonMapperRegisteredClientRowMapper() {\n\t\t\tthis(Jackson3.createJsonMapper());\n\t\t}\n\n\t\tpublic JsonMapperRegisteredClientRowMapper(JsonMapper jsonMapper) {\n\t\t\tAssert.notNull(jsonMapper, \"jsonMapper cannot be null\");\n\t\t\tthis.jsonMapper = jsonMapper;\n\t\t}\n\n\t\t@Override\n\t\tMap<String, Object> readValue(String data) {\n\t\t\tfinal ParameterizedTypeReference<Map<String, Object>> typeReference = new ParameterizedTypeReference<>() {\n\t\t\t};\n\t\t\ttools.jackson.databind.JavaType javaType = this.jsonMapper.getTypeFactory()\n\t\t\t\t.constructType(typeReference.getType());\n\t\t\treturn this.jsonMapper.readValue(data, javaType);\n\t\t}\n\n\t}\n\n\t/**\n\t * A {@link RowMapper} that maps the current row in {@code java.sql.ResultSet} to\n\t * {@link RegisteredClient} using Jackson 2's {@link ObjectMapper}.\n\t *\n\t * @deprecated Use {@link JsonMapperRegisteredClientRowMapper} to switch to Jackson 3.\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\t@SuppressWarnings(\"removal\")\n\tpublic static class RegisteredClientRowMapper extends AbstractRegisteredClientRowMapper {\n\n\t\tprivate ObjectMapper objectMapper = Jackson2.createObjectMapper();\n\n\t\tpublic final void setObjectMapper(ObjectMapper objectMapper) {\n\t\t\tAssert.notNull(objectMapper, \"objectMapper cannot be null\");\n\t\t\tthis.objectMapper = objectMapper;\n\t\t}\n\n\t\tprotected final ObjectMapper getObjectMapper() {\n\t\t\treturn this.objectMapper;\n\t\t}\n\n\t\t@Override\n\t\tMap<String, Object> readValue(String data) throws JsonProcessingException {\n\t\t\tfinal ParameterizedTypeReference<Map<String, Object>> typeReference = new ParameterizedTypeReference<>() {\n\t\t\t};\n\t\t\tcom.fasterxml.jackson.databind.JavaType javaType = this.objectMapper.getTypeFactory()\n\t\t\t\t.constructType(typeReference.getType());\n\t\t\treturn this.objectMapper.readValue(data, javaType);\n\t\t}\n\n\t}\n\n\t/**\n\t * The base {@link RowMapper} that maps the current row in {@code java.sql.ResultSet}\n\t * to {@link RegisteredClient}. This is extracted to a distinct class so that\n\t * {@link RegisteredClientRowMapper} can be deprecated in favor of\n\t * {@link JsonMapperRegisteredClientRowMapper}.\n\t */\n\tprivate abstract static class AbstractRegisteredClientRowMapper implements RowMapper<RegisteredClient> {\n\n\t\tprivate AbstractRegisteredClientRowMapper() {\n\t\t}\n\n\t\t@Override\n\t\tpublic RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\tTimestamp clientIdIssuedAt = rs.getTimestamp(\"client_id_issued_at\");\n\t\t\tTimestamp clientSecretExpiresAt = rs.getTimestamp(\"client_secret_expires_at\");\n\t\t\tSet<String> clientAuthenticationMethods = StringUtils\n\t\t\t\t.commaDelimitedListToSet(rs.getString(\"client_authentication_methods\"));\n\t\t\tSet<String> authorizationGrantTypes = StringUtils\n\t\t\t\t.commaDelimitedListToSet(rs.getString(\"authorization_grant_types\"));\n\t\t\tSet<String> redirectUris = StringUtils.commaDelimitedListToSet(rs.getString(\"redirect_uris\"));\n\t\t\tSet<String> postLogoutRedirectUris = StringUtils\n\t\t\t\t.commaDelimitedListToSet(rs.getString(\"post_logout_redirect_uris\"));\n\t\t\tSet<String> clientScopes = StringUtils.commaDelimitedListToSet(rs.getString(\"scopes\"));\n\n\t\t\t// @formatter:off\n\t\t\tRegisteredClient.Builder builder = RegisteredClient.withId(rs.getString(\"id\"))\n\t\t\t\t\t.clientId(rs.getString(\"client_id\"))\n\t\t\t\t\t.clientIdIssuedAt((clientIdIssuedAt != null) ? clientIdIssuedAt.toInstant() : null)\n\t\t\t\t\t.clientSecret(rs.getString(\"client_secret\"))\n\t\t\t\t\t.clientSecretExpiresAt((clientSecretExpiresAt != null) ? clientSecretExpiresAt.toInstant() : null)\n\t\t\t\t\t.clientName(rs.getString(\"client_name\"))\n\t\t\t\t\t.clientAuthenticationMethods((authenticationMethods) ->\n\t\t\t\t\t\t\tclientAuthenticationMethods.forEach((authenticationMethod) ->\n\t\t\t\t\t\t\t\t\tauthenticationMethods.add(resolveClientAuthenticationMethod(authenticationMethod))))\n\t\t\t\t\t.authorizationGrantTypes((grantTypes) ->\n\t\t\t\t\t\t\tauthorizationGrantTypes.forEach((grantType) ->\n\t\t\t\t\t\t\t\t\tgrantTypes.add(resolveAuthorizationGrantType(grantType))))\n\t\t\t\t\t.redirectUris((uris) -> uris.addAll(redirectUris))\n\t\t\t\t\t.postLogoutRedirectUris((uris) -> uris.addAll(postLogoutRedirectUris))\n\t\t\t\t\t.scopes((scopes) -> scopes.addAll(clientScopes));\n\t\t\t// @formatter:on\n\n\t\t\tMap<String, Object> clientSettingsMap = parseMap(rs.getString(\"client_settings\"));\n\t\t\tbuilder.clientSettings(ClientSettings.withSettings(clientSettingsMap).build());\n\n\t\t\tMap<String, Object> tokenSettingsMap = parseMap(rs.getString(\"token_settings\"));\n\t\t\tTokenSettings.Builder tokenSettingsBuilder = TokenSettings.withSettings(tokenSettingsMap);\n\t\t\tif (!tokenSettingsMap.containsKey(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT)) {\n\t\t\t\ttokenSettingsBuilder.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED);\n\t\t\t}\n\t\t\tbuilder.tokenSettings(tokenSettingsBuilder.build());\n\n\t\t\treturn builder.build();\n\t\t}\n\n\t\tprivate Map<String, Object> parseMap(String data) {\n\t\t\ttry {\n\t\t\t\treturn readValue(data);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\n\t\tabstract Map<String, Object> readValue(String data) throws Exception;\n\n\t\tprivate static AuthorizationGrantType resolveAuthorizationGrantType(String authorizationGrantType) {\n\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) {\n\t\t\t\treturn AuthorizationGrantType.AUTHORIZATION_CODE;\n\t\t\t}\n\t\t\telse if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) {\n\t\t\t\treturn AuthorizationGrantType.CLIENT_CREDENTIALS;\n\t\t\t}\n\t\t\telse if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) {\n\t\t\t\treturn AuthorizationGrantType.REFRESH_TOKEN;\n\t\t\t}\n\t\t\t// Custom authorization grant type\n\t\t\treturn new AuthorizationGrantType(authorizationGrantType);\n\t\t}\n\n\t\tprivate static ClientAuthenticationMethod resolveClientAuthenticationMethod(String clientAuthenticationMethod) {\n\t\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue().equals(clientAuthenticationMethod)) {\n\t\t\t\treturn ClientAuthenticationMethod.CLIENT_SECRET_BASIC;\n\t\t\t}\n\t\t\telse if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) {\n\t\t\t\treturn ClientAuthenticationMethod.CLIENT_SECRET_POST;\n\t\t\t}\n\t\t\telse if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) {\n\t\t\t\treturn ClientAuthenticationMethod.NONE;\n\t\t\t}\n\t\t\t// Custom client authentication method\n\t\t\treturn new ClientAuthenticationMethod(clientAuthenticationMethod);\n\t\t}\n\n\t}\n\n\t/**\n\t * The default {@code Function} that maps {@link RegisteredClient} to a {@code List}\n\t * of {@link SqlParameterValue} using an instance of Jackson 3's {@link JsonMapper}.\n\t */\n\tpublic static class JsonMapperRegisteredClientParametersMapper extends AbstractRegisteredClientParametersMapper {\n\n\t\tprivate final JsonMapper jsonMapper;\n\n\t\tpublic JsonMapperRegisteredClientParametersMapper() {\n\t\t\tthis(Jackson3.createJsonMapper());\n\t\t}\n\n\t\tpublic JsonMapperRegisteredClientParametersMapper(JsonMapper jsonMapper) {\n\t\t\tAssert.notNull(jsonMapper, \"jsonMapper cannot be null\");\n\t\t\tthis.jsonMapper = jsonMapper;\n\t\t}\n\n\t\t@Override\n\t\tString writeValueAsString(Map<String, Object> data) throws Exception {\n\t\t\treturn this.jsonMapper.writeValueAsString(data);\n\t\t}\n\n\t}\n\n\t/**\n\t * A {@code Function} that maps {@link RegisteredClient} to a {@code List} of\n\t * {@link SqlParameterValue} using an instance of Jackson 2's {@link ObjectMapper}.\n\t *\n\t * @deprecated Use {@link JsonMapperRegisteredClientParametersMapper} to switch to\n\t * Jackson 3.\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\t@SuppressWarnings(\"removal\")\n\tpublic static class RegisteredClientParametersMapper extends AbstractRegisteredClientParametersMapper {\n\n\t\tprivate ObjectMapper objectMapper = Jackson2.createObjectMapper();\n\n\t\tpublic final void setObjectMapper(ObjectMapper objectMapper) {\n\t\t\tAssert.notNull(objectMapper, \"objectMapper cannot be null\");\n\t\t\tthis.objectMapper = objectMapper;\n\t\t}\n\n\t\tprotected final ObjectMapper getObjectMapper() {\n\t\t\treturn this.objectMapper;\n\t\t}\n\n\t\t@Override\n\t\tString writeValueAsString(Map<String, Object> data) throws JsonProcessingException {\n\t\t\treturn this.objectMapper.writeValueAsString(data);\n\t\t}\n\n\t}\n\n\t/**\n\t * The base {@code Function} that maps {@link RegisteredClient} to a {@code List} of\n\t * {@link SqlParameterValue}.\n\t */\n\tprivate abstract static class AbstractRegisteredClientParametersMapper\n\t\t\timplements Function<RegisteredClient, List<SqlParameterValue>> {\n\n\t\tprivate AbstractRegisteredClientParametersMapper() {\n\t\t}\n\n\t\t@Override\n\t\tpublic List<SqlParameterValue> apply(RegisteredClient registeredClient) {\n\t\t\tTimestamp clientIdIssuedAt = (registeredClient.getClientIdIssuedAt() != null)\n\t\t\t\t\t? Timestamp.from(registeredClient.getClientIdIssuedAt()) : Timestamp.from(Instant.now());\n\n\t\t\tTimestamp clientSecretExpiresAt = (registeredClient.getClientSecretExpiresAt() != null)\n\t\t\t\t\t? Timestamp.from(registeredClient.getClientSecretExpiresAt()) : null;\n\n\t\t\tList<String> clientAuthenticationMethods = new ArrayList<>(\n\t\t\t\t\tregisteredClient.getClientAuthenticationMethods().size());\n\t\t\tregisteredClient.getClientAuthenticationMethods()\n\t\t\t\t.forEach((clientAuthenticationMethod) -> clientAuthenticationMethods\n\t\t\t\t\t.add(clientAuthenticationMethod.getValue()));\n\n\t\t\tList<String> authorizationGrantTypes = new ArrayList<>(\n\t\t\t\t\tregisteredClient.getAuthorizationGrantTypes().size());\n\t\t\tregisteredClient.getAuthorizationGrantTypes()\n\t\t\t\t.forEach((authorizationGrantType) -> authorizationGrantTypes.add(authorizationGrantType.getValue()));\n\n\t\t\treturn Arrays.asList(new SqlParameterValue(Types.VARCHAR, registeredClient.getId()),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, registeredClient.getClientId()),\n\t\t\t\t\tnew SqlParameterValue(Types.TIMESTAMP, clientIdIssuedAt),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, registeredClient.getClientSecret()),\n\t\t\t\t\tnew SqlParameterValue(Types.TIMESTAMP, clientSecretExpiresAt),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, registeredClient.getClientName()),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR,\n\t\t\t\t\t\t\tStringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods)),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR,\n\t\t\t\t\t\t\tStringUtils.collectionToCommaDelimitedString(authorizationGrantTypes)),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR,\n\t\t\t\t\t\t\tStringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR,\n\t\t\t\t\t\t\tStringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris())),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR,\n\t\t\t\t\t\t\tStringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getClientSettings().getSettings())),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getTokenSettings().getSettings())));\n\t\t}\n\n\t\tprivate String writeMap(Map<String, Object> data) {\n\t\t\ttry {\n\t\t\t\treturn writeValueAsString(data);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\n\t\tabstract String writeValueAsString(Map<String, Object> data) throws Exception;\n\n\t}\n\n\t/**\n\t * Nested class to protect from getting {@link NoClassDefFoundError} when Jackson 2 is\n\t * not on the classpath.\n\t *\n\t * @deprecated This is used to allow transition to Jackson 3. Use {@link Jackson3}\n\t * instead.\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tprivate static final class Jackson2 {\n\n\t\t@SuppressWarnings(\"removal\")\n\t\tprivate static ObjectMapper createObjectMapper() {\n\t\t\tObjectMapper objectMapper = new ObjectMapper();\n\t\t\tClassLoader classLoader = Jackson2.class.getClassLoader();\n\t\t\tList<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);\n\t\t\tobjectMapper.registerModules(securityModules);\n\t\t\tobjectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());\n\t\t\treturn objectMapper;\n\t\t}\n\n\t}\n\n\t/**\n\t * Nested class used to get a common default instance of {@link JsonMapper}. It is in\n\t * a nested class to protect from getting {@link NoClassDefFoundError} when Jackson 3\n\t * is not on the classpath.\n\t */\n\tprivate static final class Jackson3 {\n\n\t\tprivate static JsonMapper createJsonMapper() {\n\t\t\tList<JacksonModule> modules = SecurityJacksonModules.getModules(Jackson3.class.getClassLoader());\n\t\t\treturn JsonMapper.builder().addModules(modules).build();\n\t\t}\n\n\t}\n\n\tstatic class JdbcRegisteredClientRepositoryRuntimeHintsRegistrar implements RuntimeHintsRegistrar {\n\n\t\t@Override\n\t\tpublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {\n\t\t\thints.resources()\n\t\t\t\t.registerResource(new ClassPathResource(\n\t\t\t\t\t\t\"org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\"));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.client;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A representation of a client registration with an OAuth 2.0 Authorization Server.\n *\n * @author Joe Grandja\n * @author Anoop Garlapati\n * @since 7.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-2\">Section 2\n * Client Registration</a>\n */\npublic class RegisteredClient implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -717282636175335081L;\n\n\tprivate String id;\n\n\tprivate String clientId;\n\n\tprivate Instant clientIdIssuedAt;\n\n\tprivate String clientSecret;\n\n\tprivate Instant clientSecretExpiresAt;\n\n\tprivate String clientName;\n\n\tprivate Set<ClientAuthenticationMethod> clientAuthenticationMethods;\n\n\tprivate Set<AuthorizationGrantType> authorizationGrantTypes;\n\n\tprivate Set<String> redirectUris;\n\n\tprivate Set<String> postLogoutRedirectUris;\n\n\tprivate Set<String> scopes;\n\n\tprivate ClientSettings clientSettings;\n\n\tprivate TokenSettings tokenSettings;\n\n\tprotected RegisteredClient() {\n\t}\n\n\t/**\n\t * Returns the identifier for the registration.\n\t * @return the identifier for the registration\n\t */\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * Returns the client identifier.\n\t * @return the client identifier\n\t */\n\tpublic String getClientId() {\n\t\treturn this.clientId;\n\t}\n\n\t/**\n\t * Returns the time at which the client identifier was issued.\n\t * @return the time at which the client identifier was issued\n\t */\n\t@Nullable\n\tpublic Instant getClientIdIssuedAt() {\n\t\treturn this.clientIdIssuedAt;\n\t}\n\n\t/**\n\t * Returns the client secret or {@code null} if not available.\n\t * @return the client secret or {@code null} if not available\n\t */\n\t@Nullable\n\tpublic String getClientSecret() {\n\t\treturn this.clientSecret;\n\t}\n\n\t/**\n\t * Returns the time at which the client secret expires or {@code null} if it does not\n\t * expire.\n\t * @return the time at which the client secret expires or {@code null} if it does not\n\t * expire\n\t */\n\t@Nullable\n\tpublic Instant getClientSecretExpiresAt() {\n\t\treturn this.clientSecretExpiresAt;\n\t}\n\n\t/**\n\t * Returns the client name.\n\t * @return the client name\n\t */\n\tpublic String getClientName() {\n\t\treturn this.clientName;\n\t}\n\n\t/**\n\t * Returns the {@link ClientAuthenticationMethod authentication method(s)} that the\n\t * client may use.\n\t * @return the {@code Set} of {@link ClientAuthenticationMethod authentication\n\t * method(s)}\n\t */\n\tpublic Set<ClientAuthenticationMethod> getClientAuthenticationMethods() {\n\t\treturn this.clientAuthenticationMethods;\n\t}\n\n\t/**\n\t * Returns the {@link AuthorizationGrantType authorization grant type(s)} that the\n\t * client may use.\n\t * @return the {@code Set} of {@link AuthorizationGrantType authorization grant\n\t * type(s)}\n\t */\n\tpublic Set<AuthorizationGrantType> getAuthorizationGrantTypes() {\n\t\treturn this.authorizationGrantTypes;\n\t}\n\n\t/**\n\t * Returns the redirect URI(s) that the client may use in redirect-based flows.\n\t * @return the {@code Set} of redirect URI(s)\n\t */\n\tpublic Set<String> getRedirectUris() {\n\t\treturn this.redirectUris;\n\t}\n\n\t/**\n\t * Returns the post logout redirect URI(s) that the client may use for logout. The\n\t * {@code post_logout_redirect_uri} parameter is used by the client when requesting\n\t * that the End-User's User Agent be redirected to after a logout has been performed.\n\t * @return the {@code Set} of post logout redirect URI(s)\n\t */\n\tpublic Set<String> getPostLogoutRedirectUris() {\n\t\treturn this.postLogoutRedirectUris;\n\t}\n\n\t/**\n\t * Returns the scope(s) that the client may use.\n\t * @return the {@code Set} of scope(s)\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n\t/**\n\t * Returns the {@link ClientSettings client configuration settings}.\n\t * @return the {@link ClientSettings}\n\t */\n\tpublic ClientSettings getClientSettings() {\n\t\treturn this.clientSettings;\n\t}\n\n\t/**\n\t * Returns the {@link TokenSettings token configuration settings}.\n\t * @return the {@link TokenSettings}\n\t */\n\tpublic TokenSettings getTokenSettings() {\n\t\treturn this.tokenSettings;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tRegisteredClient that = (RegisteredClient) obj;\n\t\treturn Objects.equals(this.id, that.id) && Objects.equals(this.clientId, that.clientId)\n\t\t\t\t&& Objects.equals(this.clientIdIssuedAt, that.clientIdIssuedAt)\n\t\t\t\t&& Objects.equals(this.clientSecret, that.clientSecret)\n\t\t\t\t&& Objects.equals(this.clientSecretExpiresAt, that.clientSecretExpiresAt)\n\t\t\t\t&& Objects.equals(this.clientName, that.clientName)\n\t\t\t\t&& Objects.equals(this.clientAuthenticationMethods, that.clientAuthenticationMethods)\n\t\t\t\t&& Objects.equals(this.authorizationGrantTypes, that.authorizationGrantTypes)\n\t\t\t\t&& Objects.equals(this.redirectUris, that.redirectUris)\n\t\t\t\t&& Objects.equals(this.postLogoutRedirectUris, that.postLogoutRedirectUris)\n\t\t\t\t&& Objects.equals(this.scopes, that.scopes) && Objects.equals(this.clientSettings, that.clientSettings)\n\t\t\t\t&& Objects.equals(this.tokenSettings, that.tokenSettings);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.id, this.clientId, this.clientIdIssuedAt, this.clientSecret,\n\t\t\t\tthis.clientSecretExpiresAt, this.clientName, this.clientAuthenticationMethods,\n\t\t\t\tthis.authorizationGrantTypes, this.redirectUris, this.postLogoutRedirectUris, this.scopes,\n\t\t\t\tthis.clientSettings, this.tokenSettings);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RegisteredClient {\" + \"id='\" + this.id + '\\'' + \", clientId='\" + this.clientId + '\\'' + \", clientName='\"\n\t\t\t\t+ this.clientName + '\\'' + \", clientAuthenticationMethods=\" + this.clientAuthenticationMethods\n\t\t\t\t+ \", authorizationGrantTypes=\" + this.authorizationGrantTypes + \", redirectUris=\" + this.redirectUris\n\t\t\t\t+ \", postLogoutRedirectUris=\" + this.postLogoutRedirectUris + \", scopes=\" + this.scopes\n\t\t\t\t+ \", clientSettings=\" + this.clientSettings + \", tokenSettings=\" + this.tokenSettings + '}';\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided registration\n\t * identifier.\n\t * @param id the identifier for the registration\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withId(String id) {\n\t\tAssert.hasText(id, \"id cannot be empty\");\n\t\treturn new Builder(id);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the values from the provided\n\t * {@link RegisteredClient}.\n\t * @param registeredClient the {@link RegisteredClient} used for initializing the\n\t * {@link Builder}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder from(RegisteredClient registeredClient) {\n\t\tAssert.notNull(registeredClient, \"registeredClient cannot be null\");\n\t\treturn new Builder(registeredClient);\n\t}\n\n\t/**\n\t * A builder for {@link RegisteredClient}.\n\t */\n\tpublic static class Builder {\n\n\t\tprivate String id;\n\n\t\tprivate String clientId;\n\n\t\tprivate Instant clientIdIssuedAt;\n\n\t\tprivate String clientSecret;\n\n\t\tprivate Instant clientSecretExpiresAt;\n\n\t\tprivate String clientName;\n\n\t\tprivate final Set<ClientAuthenticationMethod> clientAuthenticationMethods = new HashSet<>();\n\n\t\tprivate final Set<AuthorizationGrantType> authorizationGrantTypes = new HashSet<>();\n\n\t\tprivate final Set<String> redirectUris = new HashSet<>();\n\n\t\tprivate final Set<String> postLogoutRedirectUris = new HashSet<>();\n\n\t\tprivate final Set<String> scopes = new HashSet<>();\n\n\t\tprivate ClientSettings clientSettings;\n\n\t\tprivate TokenSettings tokenSettings;\n\n\t\tprotected Builder(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tprotected Builder(RegisteredClient registeredClient) {\n\t\t\tthis.id = registeredClient.getId();\n\t\t\tthis.clientId = registeredClient.getClientId();\n\t\t\tthis.clientIdIssuedAt = registeredClient.getClientIdIssuedAt();\n\t\t\tthis.clientSecret = registeredClient.getClientSecret();\n\t\t\tthis.clientSecretExpiresAt = registeredClient.getClientSecretExpiresAt();\n\t\t\tthis.clientName = registeredClient.getClientName();\n\t\t\tif (!CollectionUtils.isEmpty(registeredClient.getClientAuthenticationMethods())) {\n\t\t\t\tthis.clientAuthenticationMethods.addAll(registeredClient.getClientAuthenticationMethods());\n\t\t\t}\n\t\t\tif (!CollectionUtils.isEmpty(registeredClient.getAuthorizationGrantTypes())) {\n\t\t\t\tthis.authorizationGrantTypes.addAll(registeredClient.getAuthorizationGrantTypes());\n\t\t\t}\n\t\t\tif (!CollectionUtils.isEmpty(registeredClient.getRedirectUris())) {\n\t\t\t\tthis.redirectUris.addAll(registeredClient.getRedirectUris());\n\t\t\t}\n\t\t\tif (!CollectionUtils.isEmpty(registeredClient.getPostLogoutRedirectUris())) {\n\t\t\t\tthis.postLogoutRedirectUris.addAll(registeredClient.getPostLogoutRedirectUris());\n\t\t\t}\n\t\t\tif (!CollectionUtils.isEmpty(registeredClient.getScopes())) {\n\t\t\t\tthis.scopes.addAll(registeredClient.getScopes());\n\t\t\t}\n\t\t\tthis.clientSettings = ClientSettings.withSettings(registeredClient.getClientSettings().getSettings())\n\t\t\t\t.build();\n\t\t\tthis.tokenSettings = TokenSettings.withSettings(registeredClient.getTokenSettings().getSettings()).build();\n\t\t}\n\n\t\t/**\n\t\t * Sets the identifier for the registration.\n\t\t * @param id the identifier for the registration\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder id(String id) {\n\t\t\tthis.id = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the client identifier.\n\t\t * @param clientId the client identifier\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientId(String clientId) {\n\t\t\tthis.clientId = clientId;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the time at which the client identifier was issued.\n\t\t * @param clientIdIssuedAt the time at which the client identifier was issued\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientIdIssuedAt(Instant clientIdIssuedAt) {\n\t\t\tthis.clientIdIssuedAt = clientIdIssuedAt;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the client secret.\n\t\t * @param clientSecret the client secret\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientSecret(String clientSecret) {\n\t\t\tthis.clientSecret = clientSecret;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the time at which the client secret expires or {@code null} if it does not\n\t\t * expire.\n\t\t * @param clientSecretExpiresAt the time at which the client secret expires or\n\t\t * {@code null} if it does not expire\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientSecretExpiresAt(Instant clientSecretExpiresAt) {\n\t\t\tthis.clientSecretExpiresAt = clientSecretExpiresAt;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the client name.\n\t\t * @param clientName the client name\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientName(String clientName) {\n\t\t\tthis.clientName = clientName;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds an {@link ClientAuthenticationMethod authentication method} the client may\n\t\t * use when authenticating with the authorization server.\n\t\t * @param clientAuthenticationMethod the authentication method\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientAuthenticationMethod(ClientAuthenticationMethod clientAuthenticationMethod) {\n\t\t\tthis.clientAuthenticationMethods.add(clientAuthenticationMethod);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the {@link ClientAuthenticationMethod authentication\n\t\t * method(s)} allowing the ability to add, replace, or remove.\n\t\t * @param clientAuthenticationMethodsConsumer a {@code Consumer} of the\n\t\t * authentication method(s)\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientAuthenticationMethods(\n\t\t\t\tConsumer<Set<ClientAuthenticationMethod>> clientAuthenticationMethodsConsumer) {\n\t\t\tclientAuthenticationMethodsConsumer.accept(this.clientAuthenticationMethods);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds an {@link AuthorizationGrantType authorization grant type} the client may\n\t\t * use.\n\t\t * @param authorizationGrantType the authorization grant type\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder authorizationGrantType(AuthorizationGrantType authorizationGrantType) {\n\t\t\tthis.authorizationGrantTypes.add(authorizationGrantType);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the {@link AuthorizationGrantType authorization grant\n\t\t * type(s)} allowing the ability to add, replace, or remove.\n\t\t * @param authorizationGrantTypesConsumer a {@code Consumer} of the authorization\n\t\t * grant type(s)\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder authorizationGrantTypes(Consumer<Set<AuthorizationGrantType>> authorizationGrantTypesConsumer) {\n\t\t\tauthorizationGrantTypesConsumer.accept(this.authorizationGrantTypes);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds a redirect URI the client may use in a redirect-based flow.\n\t\t * @param redirectUri the redirect URI\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder redirectUri(String redirectUri) {\n\t\t\tthis.redirectUris.add(redirectUri);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the redirect URI(s) allowing the ability to add, replace,\n\t\t * or remove.\n\t\t * @param redirectUrisConsumer a {@link Consumer} of the redirect URI(s)\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder redirectUris(Consumer<Set<String>> redirectUrisConsumer) {\n\t\t\tredirectUrisConsumer.accept(this.redirectUris);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds a post logout redirect URI the client may use for logout. The\n\t\t * {@code post_logout_redirect_uri} parameter is used by the client when\n\t\t * requesting that the End-User's User Agent be redirected to after a logout has\n\t\t * been performed.\n\t\t * @param postLogoutRedirectUri the post logout redirect URI\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder postLogoutRedirectUri(String postLogoutRedirectUri) {\n\t\t\tthis.postLogoutRedirectUris.add(postLogoutRedirectUri);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the post logout redirect URI(s) allowing the ability to\n\t\t * add, replace, or remove.\n\t\t * @param postLogoutRedirectUrisConsumer a {@link Consumer} of the post logout\n\t\t * redirect URI(s)\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder postLogoutRedirectUris(Consumer<Set<String>> postLogoutRedirectUrisConsumer) {\n\t\t\tpostLogoutRedirectUrisConsumer.accept(this.postLogoutRedirectUris);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds a scope the client may use.\n\t\t * @param scope the scope\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder scope(String scope) {\n\t\t\tthis.scopes.add(scope);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the scope(s) allowing the ability to add, replace, or\n\t\t * remove.\n\t\t * @param scopesConsumer a {@link Consumer} of the scope(s)\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder scopes(Consumer<Set<String>> scopesConsumer) {\n\t\t\tscopesConsumer.accept(this.scopes);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link ClientSettings client configuration settings}.\n\t\t * @param clientSettings the client configuration settings\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientSettings(ClientSettings clientSettings) {\n\t\t\tthis.clientSettings = clientSettings;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link TokenSettings token configuration settings}.\n\t\t * @param tokenSettings the token configuration settings\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder tokenSettings(TokenSettings tokenSettings) {\n\t\t\tthis.tokenSettings = tokenSettings;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link RegisteredClient}.\n\t\t * @return a {@link RegisteredClient}\n\t\t */\n\t\tpublic RegisteredClient build() {\n\t\t\tAssert.hasText(this.clientId, \"clientId cannot be empty\");\n\t\t\tAssert.notEmpty(this.authorizationGrantTypes, \"authorizationGrantTypes cannot be empty\");\n\t\t\tif (this.authorizationGrantTypes.contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {\n\t\t\t\tAssert.notEmpty(this.redirectUris, \"redirectUris cannot be empty\");\n\t\t\t}\n\t\t\tif (!StringUtils.hasText(this.clientName)) {\n\t\t\t\tthis.clientName = this.id;\n\t\t\t}\n\t\t\tif (CollectionUtils.isEmpty(this.clientAuthenticationMethods)) {\n\t\t\t\tthis.clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\t\t}\n\t\t\tif (this.clientSettings == null) {\n\t\t\t\tClientSettings.Builder builder = ClientSettings.builder();\n\t\t\t\tif (isPublicClientType()) {\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\tbuilder\n\t\t\t\t\t\t\t.requireProofKey(true)\n\t\t\t\t\t\t\t.requireAuthorizationConsent(true);\n\t\t\t\t\t// @formatter:on\n\t\t\t\t}\n\t\t\t\tthis.clientSettings = builder.build();\n\t\t\t}\n\t\t\tif (this.tokenSettings == null) {\n\t\t\t\tthis.tokenSettings = TokenSettings.builder().build();\n\t\t\t}\n\t\t\tvalidateScopes();\n\t\t\tvalidateRedirectUris();\n\t\t\tvalidatePostLogoutRedirectUris();\n\t\t\treturn create();\n\t\t}\n\n\t\tprivate boolean isPublicClientType() {\n\t\t\treturn this.authorizationGrantTypes.contains(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t&& this.clientAuthenticationMethods.size() == 1\n\t\t\t\t\t&& this.clientAuthenticationMethods.contains(ClientAuthenticationMethod.NONE);\n\t\t}\n\n\t\tprivate RegisteredClient create() {\n\t\t\tRegisteredClient registeredClient = new RegisteredClient();\n\n\t\t\tregisteredClient.id = this.id;\n\t\t\tregisteredClient.clientId = this.clientId;\n\t\t\tregisteredClient.clientIdIssuedAt = this.clientIdIssuedAt;\n\t\t\tregisteredClient.clientSecret = this.clientSecret;\n\t\t\tregisteredClient.clientSecretExpiresAt = this.clientSecretExpiresAt;\n\t\t\tregisteredClient.clientName = this.clientName;\n\t\t\tregisteredClient.clientAuthenticationMethods = Collections\n\t\t\t\t.unmodifiableSet(new HashSet<>(this.clientAuthenticationMethods));\n\t\t\tregisteredClient.authorizationGrantTypes = Collections\n\t\t\t\t.unmodifiableSet(new HashSet<>(this.authorizationGrantTypes));\n\t\t\tregisteredClient.redirectUris = Collections.unmodifiableSet(new HashSet<>(this.redirectUris));\n\t\t\tregisteredClient.postLogoutRedirectUris = Collections\n\t\t\t\t.unmodifiableSet(new HashSet<>(this.postLogoutRedirectUris));\n\t\t\tregisteredClient.scopes = Collections.unmodifiableSet(new HashSet<>(this.scopes));\n\t\t\tregisteredClient.clientSettings = this.clientSettings;\n\t\t\tregisteredClient.tokenSettings = this.tokenSettings;\n\n\t\t\treturn registeredClient;\n\t\t}\n\n\t\tprivate void validateScopes() {\n\t\t\tif (CollectionUtils.isEmpty(this.scopes)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (String scope : this.scopes) {\n\t\t\t\tAssert.isTrue(validateScope(scope), \"scope \\\"\" + scope + \"\\\" contains invalid characters\");\n\t\t\t}\n\t\t}\n\n\t\tprivate static boolean validateScope(String scope) {\n\t\t\treturn scope == null || scope.chars()\n\t\t\t\t.allMatch((c) -> withinTheRangeOf(c, 0x21, 0x21) || withinTheRangeOf(c, 0x23, 0x5B)\n\t\t\t\t\t\t|| withinTheRangeOf(c, 0x5D, 0x7E));\n\t\t}\n\n\t\tprivate static boolean withinTheRangeOf(int c, int min, int max) {\n\t\t\treturn c >= min && c <= max;\n\t\t}\n\n\t\tprivate void validateRedirectUris() {\n\t\t\tif (CollectionUtils.isEmpty(this.redirectUris)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (String redirectUri : this.redirectUris) {\n\t\t\t\tAssert.isTrue(validateRedirectUri(redirectUri),\n\t\t\t\t\t\t\"redirect_uri \\\"\" + redirectUri + \"\\\" is not a valid redirect URI or contains fragment\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void validatePostLogoutRedirectUris() {\n\t\t\tif (CollectionUtils.isEmpty(this.postLogoutRedirectUris)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (String postLogoutRedirectUri : this.postLogoutRedirectUris) {\n\t\t\t\tAssert.isTrue(validateRedirectUri(postLogoutRedirectUri), \"post_logout_redirect_uri \\\"\"\n\t\t\t\t\t\t+ postLogoutRedirectUri + \"\\\" is not a valid post logout redirect URI or contains fragment\");\n\t\t\t}\n\t\t}\n\n\t\tprivate static boolean validateRedirectUri(String redirectUri) {\n\t\t\ttry {\n\t\t\t\tURI validRedirectUri = new URI(redirectUri);\n\t\t\t\treturn validRedirectUri.getFragment() == null;\n\t\t\t}\n\t\t\tcatch (URISyntaxException ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.client;\n\nimport org.springframework.lang.Nullable;\n\n/**\n * A repository for OAuth 2.0 {@link RegisteredClient}(s).\n *\n * @author Joe Grandja\n * @author Anoop Garlapati\n * @author Ovidiu Popa\n * @since 7.0\n * @see RegisteredClient\n */\npublic interface RegisteredClientRepository {\n\n\t/**\n\t * Saves the registered client.\n\t *\n\t * <p>\n\t * IMPORTANT: Sensitive information should be encoded externally from the\n\t * implementation, e.g. {@link RegisteredClient#getClientSecret()}\n\t * @param registeredClient the {@link RegisteredClient}\n\t */\n\tvoid save(RegisteredClient registeredClient);\n\n\t/**\n\t * Returns the registered client identified by the provided {@code id}, or\n\t * {@code null} if not found.\n\t * @param id the registration identifier\n\t * @return the {@link RegisteredClient} if found, otherwise {@code null}\n\t */\n\t@Nullable\n\tRegisteredClient findById(String id);\n\n\t/**\n\t * Returns the registered client identified by the provided {@code clientId}, or\n\t * {@code null} if not found.\n\t * @param clientId the client identifier\n\t * @return the {@link RegisteredClient} if found, otherwise {@code null}\n\t */\n\t@Nullable\n\tRegisteredClient findByClientId(String clientId);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.context;\n\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\n\n/**\n * A context that holds information of the Authorization Server runtime environment.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthorizationServerSettings\n * @see AuthorizationServerContextHolder\n */\npublic interface AuthorizationServerContext {\n\n\t/**\n\t * Returns {@link AuthorizationServerSettings#getIssuer()} if available, otherwise,\n\t * resolves the issuer identifier from the <i>\"current\"</i> request.\n\t *\n\t * <p>\n\t * The issuer identifier may contain a path component to support\n\t * {@link AuthorizationServerSettings#isMultipleIssuersAllowed() multiple issuers per\n\t * host} in a multi-tenant hosting configuration.\n\t *\n\t * <p>\n\t * For example:\n\t * <ul>\n\t * <li>{@code https://example.com/issuer1/oauth2/token} &mdash; resolves the issuer to\n\t * {@code https://example.com/issuer1}</li>\n\t * <li>{@code https://example.com/issuer2/oauth2/token} &mdash; resolves the issuer to\n\t * {@code https://example.com/issuer2}</li>\n\t * <li>{@code https://example.com/authz/issuer1/oauth2/token} &mdash; resolves the\n\t * issuer to {@code https://example.com/authz/issuer1}</li>\n\t * <li>{@code https://example.com/authz/issuer2/oauth2/token} &mdash; resolves the\n\t * issuer to {@code https://example.com/authz/issuer2}</li>\n\t * </ul>\n\t * @return {@link AuthorizationServerSettings#getIssuer()} if available, otherwise,\n\t * resolves the issuer identifier from the <i>\"current\"</i> request\n\t */\n\tString getIssuer();\n\n\t/**\n\t * Returns the {@link AuthorizationServerSettings}.\n\t * @return the {@link AuthorizationServerSettings}\n\t */\n\tAuthorizationServerSettings getAuthorizationServerSettings();\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContextHolder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.context;\n\n/**\n * A holder of the {@link AuthorizationServerContext} that associates it with the current\n * thread using a {@code ThreadLocal}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthorizationServerContext\n */\npublic final class AuthorizationServerContextHolder {\n\n\tprivate static final ThreadLocal<AuthorizationServerContext> holder = new ThreadLocal<>();\n\n\tprivate AuthorizationServerContextHolder() {\n\t}\n\n\t/**\n\t * Returns the {@link AuthorizationServerContext} bound to the current thread.\n\t * @return the {@link AuthorizationServerContext}\n\t */\n\tpublic static AuthorizationServerContext getContext() {\n\t\treturn holder.get();\n\t}\n\n\t/**\n\t * Bind the given {@link AuthorizationServerContext} to the current thread.\n\t * @param authorizationServerContext the {@link AuthorizationServerContext}\n\t */\n\tpublic static void setContext(AuthorizationServerContext authorizationServerContext) {\n\t\tif (authorizationServerContext == null) {\n\t\t\tresetContext();\n\t\t}\n\t\telse {\n\t\t\tholder.set(authorizationServerContext);\n\t\t}\n\t}\n\n\t/**\n\t * Reset the {@link AuthorizationServerContext} bound to the current thread.\n\t */\n\tpublic static void resetContext() {\n\t\tholder.remove();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/Context.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.context;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.util.Assert;\n\n/**\n * A facility for holding information associated to a specific context.\n *\n * @author Joe Grandja\n * @since 7.0\n */\npublic interface Context {\n\n\t/**\n\t * Returns the value of the attribute associated to the key.\n\t * @param key the key for the attribute\n\t * @param <V> the type of the value for the attribute\n\t * @return the value of the attribute associated to the key, or {@code null} if not\n\t * available\n\t */\n\t@Nullable\n\t<V> V get(Object key);\n\n\t/**\n\t * Returns the value of the attribute associated to the key.\n\t * @param key the key for the attribute\n\t * @param <V> the type of the value for the attribute\n\t * @return the value of the attribute associated to the key, or {@code null} if not\n\t * available or not of the specified type\n\t */\n\t@Nullable\n\tdefault <V> V get(Class<V> key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\tV value = get((Object) key);\n\t\treturn key.isInstance(value) ? value : null;\n\t}\n\n\t/**\n\t * Returns {@code true} if an attribute associated to the key exists, {@code false}\n\t * otherwise.\n\t * @param key the key for the attribute\n\t * @return {@code true} if an attribute associated to the key exists, {@code false}\n\t * otherwise\n\t */\n\tboolean hasKey(Object key);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/converter/OAuth2ClientRegistrationRegisteredClientConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.converter;\n\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A {@link Converter} that converts the provided {@link OAuth2ClientRegistration} to a\n * {@link RegisteredClient}.\n *\n * @author Joe Grandja\n * @since 7.0\n */\npublic final class OAuth2ClientRegistrationRegisteredClientConverter\n\t\timplements Converter<OAuth2ClientRegistration, RegisteredClient> {\n\n\tprivate static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 32);\n\n\tprivate static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 48);\n\n\tprivate Consumer<TokenSettings.Builder> tokenSettingsCustomizer = (tokenSettings) -> {\n\t};\n\n\t@Override\n\tpublic RegisteredClient convert(OAuth2ClientRegistration clientRegistration) {\n\t\t// @formatter:off\n\t\tRegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())\n\t\t\t\t.clientId(CLIENT_ID_GENERATOR.generateKey())\n\t\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t\t.clientName(clientRegistration.getClientName());\n\n\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {\n\t\t\tbuilder\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());\n\t\t}\n\t\telse if (ClientAuthenticationMethod.NONE.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {\n\t\t\tbuilder.clientAuthenticationMethod(ClientAuthenticationMethod.NONE);\n\t\t}\n\t\telse {\n\t\t\tbuilder\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getRedirectUris())) {\n\t\t\tbuilder.redirectUris((redirectUris) ->\n\t\t\t\t\tredirectUris.addAll(clientRegistration.getRedirectUris()));\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getGrantTypes())) {\n\t\t\tbuilder.authorizationGrantTypes((authorizationGrantTypes) ->\n\t\t\t\t\tclientRegistration.getGrantTypes().forEach((grantType) ->\n\t\t\t\t\t\t\tauthorizationGrantTypes.add(new AuthorizationGrantType(grantType))));\n\t\t}\n\t\telse {\n\t\t\tbuilder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getResponseTypes()) &&\n\t\t\t\tclientRegistration.getResponseTypes().contains(OAuth2AuthorizationResponseType.CODE.getValue())) {\n\t\t\tbuilder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tbuilder.scopes((scopes) ->\n\t\t\t\t\tscopes.addAll(clientRegistration.getScopes()));\n\t\t}\n\n\t\tClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()\n\t\t\t\t.requireProofKey(true)\n\t\t\t\t.requireAuthorizationConsent(true);\n\t\tif (clientRegistration.getJwkSetUrl() != null) {\n\t\t\tclientSettingsBuilder.jwkSetUrl(clientRegistration.getJwkSetUrl().toString());\n\t\t}\n\n\t\tbuilder\n\t\t\t\t.clientSettings(clientSettingsBuilder.build());\n\n\t\tTokenSettings.Builder tokenSettingsBuilder = TokenSettings.builder();\n\t\tthis.tokenSettingsCustomizer.accept(tokenSettingsBuilder);\n\n\t\tbuilder\n\t\t\t\t.tokenSettings(tokenSettingsBuilder.build());\n\n\t\treturn builder.build();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the {@link TokenSettings.Builder}\n\t * allowing the ability to customize the token configuration settings.\n\t * @param tokenSettingsCustomizer the {@code Consumer} providing access to the\n\t * {@link TokenSettings.Builder}\n\t * @since 7.1\n\t */\n\tpublic void setTokenSettingsCustomizer(Consumer<TokenSettings.Builder> tokenSettingsCustomizer) {\n\t\tAssert.notNull(tokenSettingsCustomizer, \"tokenSettingsCustomizer cannot be null\");\n\t\tthis.tokenSettingsCustomizer = tokenSettingsCustomizer;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/converter/RegisteredClientOAuth2ClientRegistrationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.converter;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A {@link Converter} that converts the provided {@link RegisteredClient} to an\n * {@link OAuth2ClientRegistration}.\n *\n * @author Joe Grandja\n * @since 7.0\n */\npublic final class RegisteredClientOAuth2ClientRegistrationConverter\n\t\timplements Converter<RegisteredClient, OAuth2ClientRegistration> {\n\n\t@Override\n\tpublic OAuth2ClientRegistration convert(RegisteredClient registeredClient) {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientId(registeredClient.getClientId())\n\t\t\t\t.clientIdIssuedAt(registeredClient.getClientIdIssuedAt())\n\t\t\t\t.clientName(registeredClient.getClientName());\n\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(registeredClient.getClientAuthenticationMethods().iterator().next().getValue());\n\n\t\tif (registeredClient.getClientSecret() != null) {\n\t\t\tbuilder.clientSecret(registeredClient.getClientSecret());\n\t\t}\n\n\t\tif (registeredClient.getClientSecretExpiresAt() != null) {\n\t\t\tbuilder.clientSecretExpiresAt(registeredClient.getClientSecretExpiresAt());\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(registeredClient.getRedirectUris())) {\n\t\t\tbuilder.redirectUris((redirectUris) ->\n\t\t\t\t\tredirectUris.addAll(registeredClient.getRedirectUris()));\n\t\t}\n\n\t\tbuilder.grantTypes((grantTypes) ->\n\t\t\t\tregisteredClient.getAuthorizationGrantTypes().forEach((authorizationGrantType) ->\n\t\t\t\t\t\tgrantTypes.add(authorizationGrantType.getValue())));\n\n\t\tif (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {\n\t\t\tbuilder.responseType(OAuth2AuthorizationResponseType.CODE.getValue());\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(registeredClient.getScopes())) {\n\t\t\tbuilder.scopes((scopes) ->\n\t\t\t\t\tscopes.addAll(registeredClient.getScopes()));\n\t\t}\n\n\t\tClientSettings clientSettings = registeredClient.getClientSettings();\n\n\t\tif (clientSettings.getJwkSetUrl() != null) {\n\t\t\tbuilder.jwkSetUrl(clientSettings.getJwkSetUrl());\n\t\t}\n\n\t\treturn builder.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/GenericHttpMessageConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.http.converter;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\n\n/**\n * {@link GenericHttpMessageConverter} implementation that delegates to a\n * {@link SmartHttpMessageConverter}.\n *\n * @param <T> the converted object type\n * @author Sebastien Deleuze\n * @since 7.0\n */\nfinal class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {\n\n\tprivate final SmartHttpMessageConverter<T> smartConverter;\n\n\tGenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {\n\t\tthis.smartConverter = smartConverter;\n\t}\n\n\t@Override\n\tpublic boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forType(type), mediaType);\n\t}\n\n\t@Override\n\tpublic T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\treturn this.smartConverter.getSupportedMediaTypes();\n\t}\n\n\t@Override\n\tpublic T read(Class<? extends T> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(clazz, inputMessage);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, contentType, outputMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/HttpMessageConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.http.converter;\n\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.GsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JsonbHttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Utility methods for {@link HttpMessageConverter}'s.\n *\n * @author Joe Grandja\n * @author l uamas\n * @since 7.0\n */\nfinal class HttpMessageConverters {\n\n\tprivate static final boolean jacksonPresent;\n\n\tprivate static final boolean jackson2Present;\n\n\tprivate static final boolean gsonPresent;\n\n\tprivate static final boolean jsonbPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = HttpMessageConverters.class.getClassLoader();\n\t\tjacksonPresent = ClassUtils.isPresent(\"tools.jackson.databind.json.JsonMapper\", classLoader);\n\t\tjackson2Present = ClassUtils.isPresent(\"com.fasterxml.jackson.databind.ObjectMapper\", classLoader)\n\t\t\t\t&& ClassUtils.isPresent(\"com.fasterxml.jackson.core.JsonGenerator\", classLoader);\n\t\tgsonPresent = ClassUtils.isPresent(\"com.google.gson.Gson\", classLoader);\n\t\tjsonbPresent = ClassUtils.isPresent(\"jakarta.json.bind.Jsonb\", classLoader);\n\t}\n\n\tprivate HttpMessageConverters() {\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tstatic GenericHttpMessageConverter<Object> getJsonMessageConverter() {\n\t\tif (jacksonPresent) {\n\t\t\treturn new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());\n\t\t}\n\t\tif (jackson2Present) {\n\t\t\treturn new MappingJackson2HttpMessageConverter();\n\t\t}\n\t\tif (gsonPresent) {\n\t\t\treturn new GsonHttpMessageConverter();\n\t\t}\n\t\tif (jsonbPresent) {\n\t\t\treturn new JsonbHttpMessageConverter();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.http.converter;\n\nimport java.net.URL;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OAuth2AuthorizationServerMetadata OAuth\n * 2.0 Authorization Server Metadata Response}.\n *\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see AbstractHttpMessageConverter\n * @see OAuth2AuthorizationServerMetadata\n */\npublic class OAuth2AuthorizationServerMetadataHttpMessageConverter\n\t\textends AbstractHttpMessageConverter<OAuth2AuthorizationServerMetadata> {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\tprivate Converter<Map<String, Object>, OAuth2AuthorizationServerMetadata> authorizationServerMetadataConverter = new OAuth2AuthorizationServerMetadataConverter();\n\n\tprivate Converter<OAuth2AuthorizationServerMetadata, Map<String, Object>> authorizationServerMetadataParametersConverter = OAuth2AuthorizationServerMetadata::getClaims;\n\n\tpublic OAuth2AuthorizationServerMetadataHttpMessageConverter() {\n\t\tsuper(MediaType.APPLICATION_JSON, new MediaType(\"application\", \"*+json\"));\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OAuth2AuthorizationServerMetadata.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OAuth2AuthorizationServerMetadata readInternal(Class<? extends OAuth2AuthorizationServerMetadata> clazz,\n\t\t\tHttpInputMessage inputMessage) throws HttpMessageNotReadableException {\n\t\ttry {\n\t\t\tMap<String, Object> authorizationServerMetadataParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.authorizationServerMetadataConverter.convert(authorizationServerMetadataParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the OAuth 2.0 Authorization Server Metadata: \" + ex.getMessage(), ex,\n\t\t\t\t\tinputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OAuth2AuthorizationServerMetadata authorizationServerMetadata,\n\t\t\tHttpOutputMessage outputMessage) throws HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, Object> authorizationServerMetadataResponseParameters = this.authorizationServerMetadataParametersConverter\n\t\t\t\t.convert(authorizationServerMetadata);\n\t\t\tthis.jsonMessageConverter.write(authorizationServerMetadataResponseParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the OAuth 2.0 Authorization Server Metadata: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the OAuth 2.0 Authorization Server\n\t * Metadata parameters to an {@link OAuth2AuthorizationServerMetadata}.\n\t * @param authorizationServerMetadataConverter the {@link Converter} used for\n\t * converting to an {@link OAuth2AuthorizationServerMetadata}.\n\t */\n\tpublic final void setAuthorizationServerMetadataConverter(\n\t\t\tConverter<Map<String, Object>, OAuth2AuthorizationServerMetadata> authorizationServerMetadataConverter) {\n\t\tAssert.notNull(authorizationServerMetadataConverter, \"authorizationServerMetadataConverter cannot be null\");\n\t\tthis.authorizationServerMetadataConverter = authorizationServerMetadataConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the\n\t * {@link OAuth2AuthorizationServerMetadata} to a {@code Map} representation of the\n\t * OAuth 2.0 Authorization Server Metadata.\n\t * @param authorizationServerMetadataParametersConverter the {@link Converter} used\n\t * for converting to a {@code Map} representation of the OAuth 2.0 Authorization\n\t * Server Metadata.\n\t */\n\tpublic final void setAuthorizationServerMetadataParametersConverter(\n\t\t\tConverter<OAuth2AuthorizationServerMetadata, Map<String, Object>> authorizationServerMetadataParametersConverter) {\n\t\tAssert.notNull(authorizationServerMetadataParametersConverter,\n\t\t\t\t\"authorizationServerMetadataParametersConverter cannot be null\");\n\t\tthis.authorizationServerMetadataParametersConverter = authorizationServerMetadataParametersConverter;\n\t}\n\n\tprivate static final class OAuth2AuthorizationServerMetadataConverter\n\t\t\timplements Converter<Map<String, Object>, OAuth2AuthorizationServerMetadata> {\n\n\t\tprivate static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService\n\t\t\t.getSharedInstance();\n\n\t\tprivate static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);\n\n\t\tprivate static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);\n\n\t\tprivate static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class);\n\n\t\tprivate final ClaimTypeConverter claimTypeConverter;\n\n\t\tprivate OAuth2AuthorizationServerMetadataConverter() {\n\t\t\tConverter<Object, ?> collectionStringConverter = getConverter(\n\t\t\t\t\tTypeDescriptor.collection(Collection.class, STRING_TYPE_DESCRIPTOR));\n\t\t\tConverter<Object, ?> urlConverter = getConverter(URL_TYPE_DESCRIPTOR);\n\n\t\t\tMap<String, Converter<Object, ?>> claimConverters = new HashMap<>();\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, urlConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, urlConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,\n\t\t\t\t\turlConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT,\n\t\t\t\t\turlConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, urlConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, urlConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT, urlConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, urlConverter);\n\t\t\tclaimConverters.put(\n\t\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tthis.claimTypeConverter = new ClaimTypeConverter(claimConverters);\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2AuthorizationServerMetadata convert(Map<String, Object> source) {\n\t\t\tMap<String, Object> parsedClaims = this.claimTypeConverter.convert(source);\n\t\t\treturn OAuth2AuthorizationServerMetadata.withClaims(parsedClaims).build();\n\t\t}\n\n\t\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\t\treturn (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2ClientRegistrationHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.http.converter;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientMetadataClaimNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OAuth2ClientRegistration OAuth 2.0 Dynamic\n * Client Registration Request and Response}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractHttpMessageConverter\n * @see OAuth2ClientRegistration\n */\npublic class OAuth2ClientRegistrationHttpMessageConverter\n\t\textends AbstractHttpMessageConverter<OAuth2ClientRegistration> {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\tprivate Converter<Map<String, Object>, OAuth2ClientRegistration> clientRegistrationConverter = new MapOAuth2ClientRegistrationConverter();\n\n\tprivate Converter<OAuth2ClientRegistration, Map<String, Object>> clientRegistrationParametersConverter = new OAuth2ClientRegistrationMapConverter();\n\n\tpublic OAuth2ClientRegistrationHttpMessageConverter() {\n\t\tsuper(MediaType.APPLICATION_JSON, new MediaType(\"application\", \"*+json\"));\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OAuth2ClientRegistration.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OAuth2ClientRegistration readInternal(Class<? extends OAuth2ClientRegistration> clazz,\n\t\t\tHttpInputMessage inputMessage) throws HttpMessageNotReadableException {\n\t\ttry {\n\t\t\tMap<String, Object> clientRegistrationParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.clientRegistrationConverter.convert(clientRegistrationParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the OAuth 2.0 Client Registration: \" + ex.getMessage(), ex,\n\t\t\t\t\tinputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OAuth2ClientRegistration clientRegistration, HttpOutputMessage outputMessage)\n\t\t\tthrows HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, Object> clientRegistrationParameters = this.clientRegistrationParametersConverter\n\t\t\t\t.convert(clientRegistration);\n\t\t\tthis.jsonMessageConverter.write(clientRegistrationParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the OAuth 2.0 Client Registration: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the OAuth 2.0 Client Registration\n\t * parameters to an {@link OAuth2ClientRegistration}.\n\t * @param clientRegistrationConverter the {@link Converter} used for converting to an\n\t * {@link OAuth2ClientRegistration}\n\t */\n\tpublic final void setClientRegistrationConverter(\n\t\t\tConverter<Map<String, Object>, OAuth2ClientRegistration> clientRegistrationConverter) {\n\t\tAssert.notNull(clientRegistrationConverter, \"clientRegistrationConverter cannot be null\");\n\t\tthis.clientRegistrationConverter = clientRegistrationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the {@link OAuth2ClientRegistration}\n\t * to a {@code Map} representation of the OAuth 2.0 Client Registration parameters.\n\t * @param clientRegistrationParametersConverter the {@link Converter} used for\n\t * converting to a {@code Map} representation of the OAuth 2.0 Client Registration\n\t * parameters\n\t */\n\tpublic final void setClientRegistrationParametersConverter(\n\t\t\tConverter<OAuth2ClientRegistration, Map<String, Object>> clientRegistrationParametersConverter) {\n\t\tAssert.notNull(clientRegistrationParametersConverter, \"clientRegistrationParametersConverter cannot be null\");\n\t\tthis.clientRegistrationParametersConverter = clientRegistrationParametersConverter;\n\t}\n\n\tprivate static final class MapOAuth2ClientRegistrationConverter\n\t\t\timplements Converter<Map<String, Object>, OAuth2ClientRegistration> {\n\n\t\tprivate static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService\n\t\t\t.getSharedInstance();\n\n\t\tprivate static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);\n\n\t\tprivate static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);\n\n\t\tprivate static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class);\n\n\t\tprivate static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class);\n\n\t\tprivate static final Converter<Object, ?> INSTANT_CONVERTER = getConverter(INSTANT_TYPE_DESCRIPTOR);\n\n\t\tprivate final ClaimTypeConverter claimTypeConverter;\n\n\t\tprivate MapOAuth2ClientRegistrationConverter() {\n\t\t\tConverter<Object, ?> stringConverter = getConverter(STRING_TYPE_DESCRIPTOR);\n\t\t\tConverter<Object, ?> collectionStringConverter = getConverter(\n\t\t\t\t\tTypeDescriptor.collection(Collection.class, STRING_TYPE_DESCRIPTOR));\n\t\t\tConverter<Object, ?> urlConverter = getConverter(URL_TYPE_DESCRIPTOR);\n\n\t\t\tMap<String, Converter<Object, ?>> claimConverters = new HashMap<>();\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.CLIENT_ID, stringConverter);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, INSTANT_CONVERTER);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.CLIENT_SECRET, stringConverter);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT,\n\t\t\t\t\tMapOAuth2ClientRegistrationConverter::convertClientSecretExpiresAt);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.CLIENT_NAME, stringConverter);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.REDIRECT_URIS, collectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD, stringConverter);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.GRANT_TYPES, collectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES, collectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.SCOPE,\n\t\t\t\t\tMapOAuth2ClientRegistrationConverter::convertScope);\n\t\t\tclaimConverters.put(OAuth2ClientMetadataClaimNames.JWKS_URI, urlConverter);\n\t\t\tthis.claimTypeConverter = new ClaimTypeConverter(claimConverters);\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2ClientRegistration convert(Map<String, Object> source) {\n\t\t\tMap<String, Object> parsedClaims = this.claimTypeConverter.convert(source);\n\t\t\tObject clientSecretExpiresAt = parsedClaims.get(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT);\n\t\t\tif (clientSecretExpiresAt instanceof Number && clientSecretExpiresAt.equals(0)) {\n\t\t\t\tparsedClaims.remove(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT);\n\t\t\t}\n\t\t\treturn OAuth2ClientRegistration.withClaims(parsedClaims).build();\n\t\t}\n\n\t\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\t\treturn (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor);\n\t\t}\n\n\t\tprivate static Instant convertClientSecretExpiresAt(Object clientSecretExpiresAt) {\n\t\t\tif (clientSecretExpiresAt != null && String.valueOf(clientSecretExpiresAt).equals(\"0\")) {\n\t\t\t\t// 0 indicates that client_secret_expires_at does not expire\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn (Instant) INSTANT_CONVERTER.convert(clientSecretExpiresAt);\n\t\t}\n\n\t\tprivate static List<String> convertScope(Object scope) {\n\t\t\tif (scope == null) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\treturn Arrays.asList(StringUtils.delimitedListToStringArray(scope.toString(), \" \"));\n\t\t}\n\n\t}\n\n\tprivate static final class OAuth2ClientRegistrationMapConverter\n\t\t\timplements Converter<OAuth2ClientRegistration, Map<String, Object>> {\n\n\t\t@Override\n\t\tpublic Map<String, Object> convert(OAuth2ClientRegistration source) {\n\t\t\tMap<String, Object> responseClaims = new LinkedHashMap<>(source.getClaims());\n\t\t\tif (source.getClientIdIssuedAt() != null) {\n\t\t\t\tresponseClaims.put(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT,\n\t\t\t\t\t\tsource.getClientIdIssuedAt().getEpochSecond());\n\t\t\t}\n\t\t\tif (source.getClientSecret() != null) {\n\t\t\t\tlong clientSecretExpiresAt = 0;\n\t\t\t\tif (source.getClientSecretExpiresAt() != null) {\n\t\t\t\t\tclientSecretExpiresAt = source.getClientSecretExpiresAt().getEpochSecond();\n\t\t\t\t}\n\t\t\t\tresponseClaims.put(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, clientSecretExpiresAt);\n\t\t\t}\n\t\t\tif (!CollectionUtils.isEmpty(source.getScopes())) {\n\t\t\t\tresponseClaims.put(OAuth2ClientMetadataClaimNames.SCOPE,\n\t\t\t\t\t\tStringUtils.collectionToDelimitedString(source.getScopes(), \" \"));\n\t\t\t}\n\t\t\treturn responseClaims;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.http.converter;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OAuth2TokenIntrospection OAuth 2.0 Token\n * Introspection Response}.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractHttpMessageConverter\n * @see OAuth2TokenIntrospection\n */\npublic class OAuth2TokenIntrospectionHttpMessageConverter\n\t\textends AbstractHttpMessageConverter<OAuth2TokenIntrospection> {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\tprivate Converter<Map<String, Object>, OAuth2TokenIntrospection> tokenIntrospectionConverter = new MapOAuth2TokenIntrospectionConverter();\n\n\tprivate Converter<OAuth2TokenIntrospection, Map<String, Object>> tokenIntrospectionParametersConverter = new OAuth2TokenIntrospectionMapConverter();\n\n\tpublic OAuth2TokenIntrospectionHttpMessageConverter() {\n\t\tsuper(MediaType.APPLICATION_JSON, new MediaType(\"application\", \"*+json\"));\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OAuth2TokenIntrospection.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OAuth2TokenIntrospection readInternal(Class<? extends OAuth2TokenIntrospection> clazz,\n\t\t\tHttpInputMessage inputMessage) throws HttpMessageNotReadableException {\n\t\ttry {\n\t\t\tMap<String, Object> tokenIntrospectionParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.tokenIntrospectionConverter.convert(tokenIntrospectionParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the Token Introspection Response: \" + ex.getMessage(), ex, inputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OAuth2TokenIntrospection tokenIntrospection, HttpOutputMessage outputMessage)\n\t\t\tthrows HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, Object> tokenIntrospectionResponseParameters = this.tokenIntrospectionParametersConverter\n\t\t\t\t.convert(tokenIntrospection);\n\t\t\tthis.jsonMessageConverter.write(tokenIntrospectionResponseParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the Token Introspection Response: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the Token Introspection Response\n\t * parameters to an {@link OAuth2TokenIntrospection}.\n\t * @param tokenIntrospectionConverter the {@link Converter} used for converting to an\n\t * {@link OAuth2TokenIntrospection}\n\t */\n\tpublic final void setTokenIntrospectionConverter(\n\t\t\tConverter<Map<String, Object>, OAuth2TokenIntrospection> tokenIntrospectionConverter) {\n\t\tAssert.notNull(tokenIntrospectionConverter, \"tokenIntrospectionConverter cannot be null\");\n\t\tthis.tokenIntrospectionConverter = tokenIntrospectionConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting an {@link OAuth2TokenIntrospection}\n\t * to a {@code Map} representation of the Token Introspection Response parameters.\n\t * @param tokenIntrospectionParametersConverter the {@link Converter} used for\n\t * converting to a {@code Map} representation of the Token Introspection Response\n\t * parameters\n\t */\n\tpublic final void setTokenIntrospectionParametersConverter(\n\t\t\tConverter<OAuth2TokenIntrospection, Map<String, Object>> tokenIntrospectionParametersConverter) {\n\t\tAssert.notNull(tokenIntrospectionParametersConverter, \"tokenIntrospectionParametersConverter cannot be null\");\n\t\tthis.tokenIntrospectionParametersConverter = tokenIntrospectionParametersConverter;\n\t}\n\n\tprivate static final class MapOAuth2TokenIntrospectionConverter\n\t\t\timplements Converter<Map<String, Object>, OAuth2TokenIntrospection> {\n\n\t\tprivate static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService\n\t\t\t.getSharedInstance();\n\n\t\tprivate static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);\n\n\t\tprivate static final TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class);\n\n\t\tprivate static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);\n\n\t\tprivate static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class);\n\n\t\tprivate static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class);\n\n\t\tprivate final ClaimTypeConverter claimTypeConverter;\n\n\t\tprivate MapOAuth2TokenIntrospectionConverter() {\n\t\t\tConverter<Object, ?> booleanConverter = getConverter(BOOLEAN_TYPE_DESCRIPTOR);\n\t\t\tConverter<Object, ?> stringConverter = getConverter(STRING_TYPE_DESCRIPTOR);\n\t\t\tConverter<Object, ?> instantConverter = getConverter(INSTANT_TYPE_DESCRIPTOR);\n\t\t\tConverter<Object, ?> collectionStringConverter = getConverter(\n\t\t\t\t\tTypeDescriptor.collection(Collection.class, STRING_TYPE_DESCRIPTOR));\n\t\t\tConverter<Object, ?> urlConverter = getConverter(URL_TYPE_DESCRIPTOR);\n\n\t\t\tMap<String, Converter<Object, ?>> claimConverters = new HashMap<>();\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, booleanConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.SCOPE,\n\t\t\t\t\tMapOAuth2TokenIntrospectionConverter::convertScope);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, stringConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.USERNAME, stringConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE, stringConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.EXP, instantConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.IAT, instantConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.NBF, instantConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.SUB, stringConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.AUD, collectionStringConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.ISS, urlConverter);\n\t\t\tclaimConverters.put(OAuth2TokenIntrospectionClaimNames.JTI, stringConverter);\n\t\t\tthis.claimTypeConverter = new ClaimTypeConverter(claimConverters);\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2TokenIntrospection convert(Map<String, Object> source) {\n\t\t\tMap<String, Object> parsedClaims = this.claimTypeConverter.convert(source);\n\t\t\treturn OAuth2TokenIntrospection.withClaims(parsedClaims).build();\n\t\t}\n\n\t\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\t\treturn (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor);\n\t\t}\n\n\t\tprivate static List<String> convertScope(Object scope) {\n\t\t\tif (scope == null) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\treturn Arrays.asList(StringUtils.delimitedListToStringArray(scope.toString(), \" \"));\n\t\t}\n\n\t}\n\n\tprivate static final class OAuth2TokenIntrospectionMapConverter\n\t\t\timplements Converter<OAuth2TokenIntrospection, Map<String, Object>> {\n\n\t\t@Override\n\t\tpublic Map<String, Object> convert(OAuth2TokenIntrospection source) {\n\t\t\tMap<String, Object> responseClaims = new LinkedHashMap<>(source.getClaims());\n\t\t\tif (!CollectionUtils.isEmpty(source.getScopes())) {\n\t\t\t\tresponseClaims.put(OAuth2TokenIntrospectionClaimNames.SCOPE,\n\t\t\t\t\t\tStringUtils.collectionToDelimitedString(source.getScopes(), \" \"));\n\t\t\t}\n\t\t\tif (source.getExpiresAt() != null) {\n\t\t\t\tresponseClaims.put(OAuth2TokenIntrospectionClaimNames.EXP, source.getExpiresAt().getEpochSecond());\n\t\t\t}\n\t\t\tif (source.getIssuedAt() != null) {\n\t\t\t\tresponseClaims.put(OAuth2TokenIntrospectionClaimNames.IAT, source.getIssuedAt().getEpochSecond());\n\t\t\t}\n\t\t\tif (source.getNotBefore() != null) {\n\t\t\t\tresponseClaims.put(OAuth2TokenIntrospectionClaimNames.NBF, source.getNotBefore().getEpochSecond());\n\t\t\t}\n\t\t\treturn responseClaims;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/JsonNodeUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\n\n/**\n * Utility class for {@code JsonNode}.\n *\n * @author Joe Grandja\n * @since 7.0\n */\nabstract class JsonNodeUtils {\n\n\tstatic final TypeReference<Set<String>> STRING_SET = new TypeReference<>() {\n\t};\n\n\tstatic final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {\n\t};\n\n\tstatic String findStringValue(JsonNode jsonNode, String fieldName) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isString()) ? value.stringValue() : null;\n\t}\n\n\tstatic <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,\n\t\t\tDeserializationContext context) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isContainer())\n\t\t\t\t? context.readTreeAsValue(value, context.getTypeFactory().constructType(valueTypeReference)) : null;\n\t}\n\n\tstatic JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isObject()) ? value : null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/JwsAlgorithmMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\n/**\n * This mixin class is used to serialize/deserialize {@link SignatureAlgorithm}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see SignatureAlgorithm\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class JwsAlgorithmMixin {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2AuthorizationRequestDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.ValueDeserializer;\nimport tools.jackson.databind.exc.InvalidFormatException;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;\n\n/**\n * A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestMixin\n */\nfinal class OAuth2AuthorizationRequestDeserializer extends ValueDeserializer<OAuth2AuthorizationRequest> {\n\n\t@Override\n\tpublic OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context) {\n\t\tJsonNode root = context.readTree(parser);\n\t\treturn deserialize(parser, context, root);\n\t}\n\n\tprivate OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context, JsonNode root) {\n\t\tAuthorizationGrantType authorizationGrantType = convertAuthorizationGrantType(\n\t\t\t\tJsonNodeUtils.findObjectNode(root, \"authorizationGrantType\"));\n\t\tBuilder builder = getBuilder(parser, authorizationGrantType);\n\t\tbuilder.authorizationUri(JsonNodeUtils.findStringValue(root, \"authorizationUri\"));\n\t\tbuilder.clientId(JsonNodeUtils.findStringValue(root, \"clientId\"));\n\t\tbuilder.redirectUri(JsonNodeUtils.findStringValue(root, \"redirectUri\"));\n\t\tbuilder.scopes(JsonNodeUtils.findValue(root, \"scopes\", JsonNodeUtils.STRING_SET, context));\n\t\tbuilder.state(JsonNodeUtils.findStringValue(root, \"state\"));\n\t\tbuilder.additionalParameters(\n\t\t\t\tJsonNodeUtils.findValue(root, \"additionalParameters\", JsonNodeUtils.STRING_OBJECT_MAP, context));\n\t\tbuilder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, \"authorizationRequestUri\"));\n\t\tbuilder.attributes(JsonNodeUtils.findValue(root, \"attributes\", JsonNodeUtils.STRING_OBJECT_MAP, context));\n\t\treturn builder.build();\n\t}\n\n\tprivate Builder getBuilder(JsonParser parser, AuthorizationGrantType authorizationGrantType) {\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {\n\t\t\treturn OAuth2AuthorizationRequest.authorizationCode();\n\t\t}\n\t\tthrow new InvalidFormatException(parser, \"Invalid authorizationGrantType\", authorizationGrantType,\n\t\t\t\tAuthorizationGrantType.class);\n\t}\n\n\tprivate static AuthorizationGrantType convertAuthorizationGrantType(JsonNode jsonNode) {\n\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {\n\t\t\treturn AuthorizationGrantType.AUTHORIZATION_CODE;\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2AuthorizationRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.\n * It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestDeserializer\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2AuthorizationRequestMixin {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2AuthorizationServerJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport java.net.URL;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\n\n/**\n * Jackson {@code Module} for {@code spring-security-oauth2-authorization-server}, that\n * registers the following mix-in annotations:\n *\n * <ul>\n * <li>{@link OAuth2TokenExchangeActorMixin}</li>\n * <li>{@link OAuth2AuthorizationRequestMixin}</li>\n * <li>{@link OAuth2TokenExchangeCompositeAuthenticationTokenMixin}</li>\n * <li>{@link JwsAlgorithmMixin}</li>\n * <li>{@link OAuth2TokenFormatMixin}</li>\n * </ul>\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @author Steve Riesenberg\n * @since 7.0\n */\n@SuppressWarnings(\"serial\")\npublic class OAuth2AuthorizationServerJacksonModule extends SecurityJacksonModule {\n\n\tpublic OAuth2AuthorizationServerJacksonModule() {\n\t\tsuper(OAuth2AuthorizationServerJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(OAuth2TokenFormat.class)\n\t\t\t.allowIfSubType(OAuth2TokenExchangeActor.class)\n\t\t\t.allowIfSubType(OAuth2TokenExchangeCompositeAuthenticationToken.class)\n\t\t\t.allowIfSubType(SignatureAlgorithm.class)\n\t\t\t.allowIfSubType(MacAlgorithm.class)\n\t\t\t.allowIfSubType(OAuth2AuthorizationRequest.class)\n\t\t\t.allowIfSubType(URL.class);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(OAuth2TokenExchangeActor.class, OAuth2TokenExchangeActorMixin.class);\n\t\tcontext.setMixIn(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);\n\t\tcontext.setMixIn(OAuth2TokenExchangeCompositeAuthenticationToken.class,\n\t\t\t\tOAuth2TokenExchangeCompositeAuthenticationTokenMixin.class);\n\t\tcontext.setMixIn(SignatureAlgorithm.class, JwsAlgorithmMixin.class);\n\t\tcontext.setMixIn(MacAlgorithm.class, JwsAlgorithmMixin.class);\n\t\tcontext.setMixIn(OAuth2TokenFormat.class, OAuth2TokenFormatMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2TokenExchangeActorMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2TokenExchangeActor}.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2TokenExchangeActor\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2TokenExchangeActorMixin {\n\n\t@JsonCreator\n\tOAuth2TokenExchangeActorMixin(@JsonProperty(\"claims\") Map<String, Object> claims) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2TokenExchangeCompositeAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\n\n/**\n * This mixin class is used to serialize/deserialize\n * {@link OAuth2TokenExchangeCompositeAuthenticationToken}.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2TokenExchangeCompositeAuthenticationToken\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2TokenExchangeCompositeAuthenticationTokenMixin {\n\n\t@JsonCreator\n\tOAuth2TokenExchangeCompositeAuthenticationTokenMixin(@JsonProperty(\"subject\") Authentication subject,\n\t\t\t@JsonProperty(\"actors\") List<Authentication> actors) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2TokenFormatMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2TokenFormat}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenFormat\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2TokenFormatMixin {\n\n\t@JsonCreator\n\tOAuth2TokenFormatMixin(@JsonProperty(\"value\") String value) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/DurationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.time.Duration;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonGetter;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * This mixin class is used to serialize/deserialize {@link Duration}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see Duration\n * @deprecated as of 7.0\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tcreatorVisibility = JsonAutoDetect.Visibility.NONE)\n@Deprecated(forRemoval = true)\nabstract class DurationMixin {\n\n\t@JsonCreator\n\tstatic void ofSeconds(@JsonProperty(\"seconds\") long seconds, @JsonProperty(\"nano\") long nanoAdjustment) {\n\t}\n\n\t@JsonGetter(\"seconds\")\n\tabstract long getSeconds();\n\n\t@JsonGetter(\"nano\")\n\tabstract int getNano();\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/HashSetMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * This mixin class is used to serialize/deserialize {@link HashSet}.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see HashSet\n * @deprecated as of 7.0\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@Deprecated(forRemoval = true)\nabstract class HashSetMixin {\n\n\t@JsonCreator\n\tHashSetMixin(Set<?> set) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JsonNodeUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n/**\n * Utility class for {@code JsonNode}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.server.authorization.jackson.JsonNodeUtils}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\nabstract class JsonNodeUtils {\n\n\tstatic final TypeReference<Set<String>> STRING_SET = new TypeReference<>() {\n\t};\n\n\tstatic final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {\n\t};\n\n\tstatic String findStringValue(JsonNode jsonNode, String fieldName) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isTextual()) ? value.asText() : null;\n\t}\n\n\tstatic <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,\n\t\t\tObjectMapper mapper) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isContainerNode()) ? mapper.convertValue(value, valueTypeReference) : null;\n\t}\n\n\tstatic JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isObject()) ? value : null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JwsAlgorithmMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\n/**\n * This mixin class is used to serialize/deserialize {@link SignatureAlgorithm}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see SignatureAlgorithm\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.server.authorization.jackson.JwsAlgorithmMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class JwsAlgorithmMixin {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;\n\n/**\n * A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestMixin\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationRequestDeserializer}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nfinal class OAuth2AuthorizationRequestDeserializer extends JsonDeserializer<OAuth2AuthorizationRequest> {\n\n\t@Override\n\tpublic OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context)\n\t\t\tthrows IOException {\n\t\tObjectMapper mapper = (ObjectMapper) parser.getCodec();\n\t\tJsonNode root = mapper.readTree(parser);\n\t\treturn deserialize(parser, mapper, root);\n\t}\n\n\tprivate OAuth2AuthorizationRequest deserialize(JsonParser parser, ObjectMapper mapper, JsonNode root)\n\t\t\tthrows JsonParseException {\n\t\tAuthorizationGrantType authorizationGrantType = convertAuthorizationGrantType(\n\t\t\t\tJsonNodeUtils.findObjectNode(root, \"authorizationGrantType\"));\n\t\tBuilder builder = getBuilder(parser, authorizationGrantType);\n\t\tbuilder.authorizationUri(JsonNodeUtils.findStringValue(root, \"authorizationUri\"));\n\t\tbuilder.clientId(JsonNodeUtils.findStringValue(root, \"clientId\"));\n\t\tbuilder.redirectUri(JsonNodeUtils.findStringValue(root, \"redirectUri\"));\n\t\tbuilder.scopes(JsonNodeUtils.findValue(root, \"scopes\", JsonNodeUtils.STRING_SET, mapper));\n\t\tbuilder.state(JsonNodeUtils.findStringValue(root, \"state\"));\n\t\tbuilder.additionalParameters(\n\t\t\t\tJsonNodeUtils.findValue(root, \"additionalParameters\", JsonNodeUtils.STRING_OBJECT_MAP, mapper));\n\t\tbuilder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, \"authorizationRequestUri\"));\n\t\tbuilder.attributes(JsonNodeUtils.findValue(root, \"attributes\", JsonNodeUtils.STRING_OBJECT_MAP, mapper));\n\t\treturn builder.build();\n\t}\n\n\tprivate Builder getBuilder(JsonParser parser, AuthorizationGrantType authorizationGrantType)\n\t\t\tthrows JsonParseException {\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {\n\t\t\treturn OAuth2AuthorizationRequest.authorizationCode();\n\t\t}\n\t\tthrow new JsonParseException(parser, \"Invalid authorizationGrantType\");\n\t}\n\n\tprivate static AuthorizationGrantType convertAuthorizationGrantType(JsonNode jsonNode) {\n\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {\n\t\t\treturn AuthorizationGrantType.AUTHORIZATION_CODE;\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.\n * It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestDeserializer\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationRequestMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2AuthorizationRequestMixin {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationServerJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\n\n/**\n * Jackson {@code Module} for {@code spring-security-oauth2-authorization-server}, that\n * registers the following mix-in annotations:\n *\n * <ul>\n * <li>{@link UnmodifiableMapMixin}</li>\n * <li>{@link HashSetMixin}</li>\n * <li>{@link OAuth2AuthorizationRequestMixin}</li>\n * <li>{@link OAuth2TokenExchangeCompositeAuthenticationTokenMixin}</li>\n * <li>{@link DurationMixin}</li>\n * <li>{@link JwsAlgorithmMixin}</li>\n * <li>{@link OAuth2TokenFormatMixin}</li>\n * <li>{@link StringArrayMixin}</li>\n * </ul>\n *\n * If not already enabled, default typing will be automatically enabled as type info is\n * required to properly serialize/deserialize objects. In order to use this module just\n * add it to your {@code ObjectMapper} configuration.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new OAuth2AuthorizationServerJackson2Module());\n * </pre>\n *\n * <b>NOTE:</b> Use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get a list\n * of all security modules.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see SecurityJackson2Modules\n * @see UnmodifiableMapMixin\n * @see HashSetMixin\n * @see OAuth2AuthorizationRequestMixin\n * @see DurationMixin\n * @see JwsAlgorithmMixin\n * @see OAuth2TokenFormatMixin\n * @see StringArrayMixin\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule}\n * based on Jackson 3\n */\n@SuppressWarnings({ \"serial\", \"removal\" })\n@Deprecated(forRemoval = true)\npublic class OAuth2AuthorizationServerJackson2Module extends SimpleModule {\n\n\tpublic OAuth2AuthorizationServerJackson2Module() {\n\t\tsuper(OAuth2AuthorizationServerJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tSecurityJackson2Modules.enableDefaultTyping(context.getOwner());\n\t\tcontext.setMixInAnnotations(Collections.unmodifiableMap(Collections.emptyMap()).getClass(),\n\t\t\t\tUnmodifiableMapMixin.class);\n\t\tcontext.setMixInAnnotations(HashSet.class, HashSetMixin.class);\n\t\tcontext.setMixInAnnotations(LinkedHashSet.class, HashSetMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2TokenExchangeActor.class, OAuth2TokenExchangeActorMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2TokenExchangeCompositeAuthenticationToken.class,\n\t\t\t\tOAuth2TokenExchangeCompositeAuthenticationTokenMixin.class);\n\t\tcontext.setMixInAnnotations(Duration.class, DurationMixin.class);\n\t\tcontext.setMixInAnnotations(SignatureAlgorithm.class, JwsAlgorithmMixin.class);\n\t\tcontext.setMixInAnnotations(MacAlgorithm.class, JwsAlgorithmMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2TokenFormat.class, OAuth2TokenFormatMixin.class);\n\t\tcontext.setMixInAnnotations(String[].class, StringArrayMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2TokenExchangeActorMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2TokenExchangeActor}.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2TokenExchangeActor\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenExchangeActorMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2TokenExchangeActorMixin {\n\n\t@JsonCreator\n\tOAuth2TokenExchangeActorMixin(@JsonProperty(\"claims\") Map<String, Object> claims) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2TokenExchangeCompositeAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\n\n/**\n * This mixin class is used to serialize/deserialize\n * {@link OAuth2TokenExchangeCompositeAuthenticationToken}.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2TokenExchangeCompositeAuthenticationToken\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenExchangeCompositeAuthenticationTokenMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2TokenExchangeCompositeAuthenticationTokenMixin {\n\n\t@JsonCreator\n\tOAuth2TokenExchangeCompositeAuthenticationTokenMixin(@JsonProperty(\"subject\") Authentication subject,\n\t\t\t@JsonProperty(\"actors\") List<Authentication> actors) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2TokenFormatMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2TokenFormat}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenFormat\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.server.authorization.jackson.OAuth2TokenFormatMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2TokenFormatMixin {\n\n\t@JsonCreator\n\tOAuth2TokenFormatMixin(@JsonProperty(\"value\") String value) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/StringArrayMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * This mixin class is used to serialize/deserialize {@link String} array.\n *\n * @author Nikola Jovanovic\n * @since 7.0\n * @see String\n * @deprecated as of 7.0\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\nabstract class StringArrayMixin {\n\n\t@JsonCreator\n\tStringArrayMixin(String[] array) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n/**\n * A {@code JsonDeserializer} for {@link Collections#unmodifiableMap(Map)}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see Collections#unmodifiableMap(Map)\n * @see UnmodifiableMapMixin\n * @deprecated as of 7.0\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nfinal class UnmodifiableMapDeserializer extends JsonDeserializer<Map<?, ?>> {\n\n\t@Override\n\tpublic Map<?, ?> deserialize(JsonParser parser, DeserializationContext context) throws IOException {\n\t\tObjectMapper mapper = (ObjectMapper) parser.getCodec();\n\t\tJsonNode mapNode = mapper.readTree(parser);\n\t\tMap<String, Object> result = new LinkedHashMap<>();\n\t\tif (mapNode != null && mapNode.isObject()) {\n\t\t\tIterable<Map.Entry<String, JsonNode>> fields = mapNode::fields;\n\t\t\tfor (Map.Entry<String, JsonNode> field : fields) {\n\t\t\t\tresult.put(field.getKey(), mapper.readValue(field.getValue().traverse(mapper), Object.class));\n\t\t\t}\n\t\t}\n\t\treturn Collections.unmodifiableMap(result);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * This mixin class is used to serialize/deserialize\n * {@link Collections#unmodifiableMap(Map)}. It also registers a custom deserializer\n * {@link UnmodifiableMapDeserializer}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see Collections#unmodifiableMap(Map)\n * @see UnmodifiableMapDeserializer\n * @deprecated as of 7.0\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = UnmodifiableMapDeserializer.class)\nabstract class UnmodifiableMapMixin {\n\n\t@JsonCreator\n\tUnmodifiableMapMixin(Map<?, ?> map) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientMetadataClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc;\n\nimport java.net.URL;\nimport java.util.List;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientMetadataClaimAccessor;\n\n/**\n * A {@link ClaimAccessor} for the \"claims\" that are contained in the OpenID Client\n * Registration Request and Response.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2ClientMetadataClaimAccessor\n * @see OidcClientMetadataClaimNames\n * @see OidcClientRegistration\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata\">2.\n * Client Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata\">3.1.\n * Client Registration Metadata</a>\n */\npublic interface OidcClientMetadataClaimAccessor extends OAuth2ClientMetadataClaimAccessor {\n\n\t/**\n\t * Returns the post logout redirection {@code URI} values used by the Client\n\t * {@code (post_logout_redirect_uris)}. The {@code post_logout_redirect_uri} parameter\n\t * is used by the client when requesting that the End-User's User Agent be redirected\n\t * to after a logout has been performed.\n\t * @return the post logout redirection {@code URI} values used by the Client\n\t */\n\tdefault List<String> getPostLogoutRedirectUris() {\n\t\treturn getClaimAsStringList(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS);\n\t}\n\n\t/**\n\t * Returns the {@link JwsAlgorithm JWS} algorithm that must be used for signing the\n\t * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the\n\t * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and\n\t * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt}\n\t * authentication methods {@code (token_endpoint_auth_signing_alg)}.\n\t * @return the {@link JwsAlgorithm JWS} algorithm that must be used for signing the\n\t * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint\n\t */\n\tdefault String getTokenEndpointAuthenticationSigningAlgorithm() {\n\t\treturn getClaimAsString(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_SIGNING_ALG);\n\t}\n\n\t/**\n\t * Returns the {@link SignatureAlgorithm JWS} algorithm required for signing the\n\t * {@link OidcIdToken ID Token} issued to the Client\n\t * {@code (id_token_signed_response_alg)}.\n\t * @return the {@link SignatureAlgorithm JWS} algorithm required for signing the\n\t * {@link OidcIdToken ID Token} issued to the Client\n\t */\n\tdefault String getIdTokenSignedResponseAlgorithm() {\n\t\treturn getClaimAsString(OidcClientMetadataClaimNames.ID_TOKEN_SIGNED_RESPONSE_ALG);\n\t}\n\n\t/**\n\t * Returns the Registration Access Token that can be used at the Client Configuration\n\t * Endpoint.\n\t * @return the Registration Access Token that can be used at the Client Configuration\n\t * Endpoint\n\t */\n\tdefault String getRegistrationAccessToken() {\n\t\treturn getClaimAsString(OidcClientMetadataClaimNames.REGISTRATION_ACCESS_TOKEN);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the Client Configuration Endpoint where the Registration\n\t * Access Token can be used.\n\t * @return the {@code URL} of the Client Configuration Endpoint where the Registration\n\t * Access Token can be used\n\t */\n\tdefault URL getRegistrationClientUrl() {\n\t\treturn getClaimAsURL(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientMetadataClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientMetadataClaimNames;\n\n/**\n * The names of the \"claims\" defined by OpenID Connect Dynamic Client Registration 1.0\n * that are contained in the OpenID Client Registration Request and Response.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2ClientMetadataClaimNames\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata\">2.\n * Client Metadata</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata\">3.1.\n * Client Registration Metadata</a>\n */\npublic final class OidcClientMetadataClaimNames extends OAuth2ClientMetadataClaimNames {\n\n\t/**\n\t * {@code post_logout_redirect_uris} - the post logout redirection {@code URI} values\n\t * used by the Client. The {@code post_logout_redirect_uri} parameter is used by the\n\t * client when requesting that the End-User's User Agent be redirected to after a\n\t * logout has been performed.\n\t */\n\tpublic static final String POST_LOGOUT_REDIRECT_URIS = \"post_logout_redirect_uris\";\n\n\t/**\n\t * {@code token_endpoint_auth_signing_alg} - the {@link JwsAlgorithm JWS} algorithm\n\t * that must be used for signing the {@link Jwt JWT} used to authenticate the Client\n\t * at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT\n\t * private_key_jwt} and {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT\n\t * client_secret_jwt} authentication methods\n\t */\n\tpublic static final String TOKEN_ENDPOINT_AUTH_SIGNING_ALG = \"token_endpoint_auth_signing_alg\";\n\n\t/**\n\t * {@code id_token_signed_response_alg} - the {@link JwsAlgorithm JWS} algorithm\n\t * required for signing the {@link OidcIdToken ID Token} issued to the Client\n\t */\n\tpublic static final String ID_TOKEN_SIGNED_RESPONSE_ALG = \"id_token_signed_response_alg\";\n\n\t/**\n\t * {@code registration_access_token} - the Registration Access Token that can be used\n\t * at the Client Configuration Endpoint\n\t */\n\tpublic static final String REGISTRATION_ACCESS_TOKEN = \"registration_access_token\";\n\n\t/**\n\t * {@code registration_client_uri} - the {@code URL} of the Client Configuration\n\t * Endpoint where the Registration Access Token can be used\n\t */\n\tpublic static final String REGISTRATION_CLIENT_URI = \"registration_client_uri\";\n\n\tprivate OidcClientMetadataClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc;\n\nimport java.io.Serial;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.AbstractOAuth2ClientRegistration;\nimport org.springframework.util.Assert;\n\n/**\n * A representation of an OpenID Client Registration Request and Response, which is sent\n * to and returned from the Client Registration Endpoint, and contains a set of claims\n * about the Client's Registration information. The claims are defined by the OpenID\n * Connect Dynamic Client Registration 1.0 specification.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractOAuth2ClientRegistration\n * @see OidcClientMetadataClaimAccessor\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest\">3.1.\n * Client Registration Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse\">3.2.\n * Client Registration Response</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata\">3.1.\n * Client Registration Metadata</a>\n */\npublic final class OidcClientRegistration extends AbstractOAuth2ClientRegistration\n\t\timplements OidcClientMetadataClaimAccessor {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -8485448209864668396L;\n\n\tprivate OidcClientRegistration(Map<String, Object> claims) {\n\t\tsuper(claims);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with empty claims.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided claims.\n\t * @param claims the claims to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withClaims(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\treturn new Builder().claims((c) -> c.putAll(claims));\n\t}\n\n\t/**\n\t * Helps configure an {@link OidcClientRegistration}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OidcClientRegistration, Builder> {\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Add the post logout redirection {@code URI} used by the Client, OPTIONAL. The\n\t\t * {@code post_logout_redirect_uri} parameter is used by the client when\n\t\t * requesting that the End-User's User Agent be redirected to after a logout has\n\t\t * been performed.\n\t\t * @param postLogoutRedirectUri the post logout redirection {@code URI} used by\n\t\t * the Client\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder postLogoutRedirectUri(String postLogoutRedirectUri) {\n\t\t\taddClaimToClaimList(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, postLogoutRedirectUri);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the post logout redirection {@code URI} values used by\n\t\t * the Client, allowing the ability to add, replace, or remove, OPTIONAL.\n\t\t * @param postLogoutRedirectUrisConsumer a {@code Consumer} of the post logout\n\t\t * redirection {@code URI} values used by the Client\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder postLogoutRedirectUris(Consumer<List<String>> postLogoutRedirectUrisConsumer) {\n\t\t\tacceptClaimValues(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, postLogoutRedirectUrisConsumer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link JwsAlgorithm JWS} algorithm that must be used for signing the\n\t\t * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the\n\t\t * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and\n\t\t * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt}\n\t\t * authentication methods, OPTIONAL.\n\t\t * @param authenticationSigningAlgorithm the {@link JwsAlgorithm JWS} algorithm\n\t\t * that must be used for signing the {@link Jwt JWT} used to authenticate the\n\t\t * Client at the Token Endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder tokenEndpointAuthenticationSigningAlgorithm(String authenticationSigningAlgorithm) {\n\t\t\treturn claim(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_SIGNING_ALG, authenticationSigningAlgorithm);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link SignatureAlgorithm JWS} algorithm required for signing the\n\t\t * {@link OidcIdToken ID Token} issued to the Client, OPTIONAL.\n\t\t * @param idTokenSignedResponseAlgorithm the {@link SignatureAlgorithm JWS}\n\t\t * algorithm required for signing the {@link OidcIdToken ID Token} issued to the\n\t\t * Client\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder idTokenSignedResponseAlgorithm(String idTokenSignedResponseAlgorithm) {\n\t\t\treturn claim(OidcClientMetadataClaimNames.ID_TOKEN_SIGNED_RESPONSE_ALG, idTokenSignedResponseAlgorithm);\n\t\t}\n\n\t\t/**\n\t\t * Sets the Registration Access Token that can be used at the Client Configuration\n\t\t * Endpoint, OPTIONAL.\n\t\t * @param registrationAccessToken the Registration Access Token that can be used\n\t\t * at the Client Configuration Endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder registrationAccessToken(String registrationAccessToken) {\n\t\t\treturn claim(OidcClientMetadataClaimNames.REGISTRATION_ACCESS_TOKEN, registrationAccessToken);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code URL} of the Client Configuration Endpoint where the\n\t\t * Registration Access Token can be used, OPTIONAL.\n\t\t * @param registrationClientUrl the {@code URL} of the Client Configuration\n\t\t * Endpoint where the Registration Access Token can be used\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder registrationClientUrl(String registrationClientUrl) {\n\t\t\treturn claim(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI, registrationClientUrl);\n\t\t}\n\n\t\t/**\n\t\t * Validate the claims and build the {@link OidcClientRegistration}.\n\t\t * <p>\n\t\t * The following claims are REQUIRED: {@code client_id}, {@code redirect_uris}.\n\t\t * @return the {@link OidcClientRegistration}\n\t\t */\n\t\t@Override\n\t\tpublic OidcClientRegistration build() {\n\t\t\tvalidate();\n\t\t\treturn new OidcClientRegistration(getClaims());\n\t\t}\n\n\t\t@Override\n\t\tprotected void validate() {\n\t\t\tsuper.validate();\n\t\t\tAssert.notNull(getClaims().get(OidcClientMetadataClaimNames.REDIRECT_URIS), \"redirect_uris cannot be null\");\n\t\t\tAssert.isInstanceOf(List.class, getClaims().get(OidcClientMetadataClaimNames.REDIRECT_URIS),\n\t\t\t\t\t\"redirect_uris must be of type List\");\n\t\t\tAssert.notEmpty((List<?>) getClaims().get(OidcClientMetadataClaimNames.REDIRECT_URIS),\n\t\t\t\t\t\"redirect_uris cannot be empty\");\n\t\t\tif (getClaims().get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class, getClaims().get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS),\n\t\t\t\t\t\t\"post_logout_redirect_uris must be of type List\");\n\t\t\t\tAssert.notEmpty((List<?>) getClaims().get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS),\n\t\t\t\t\t\t\"post_logout_redirect_uris cannot be empty\");\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void addClaimToClaimList(String name, String value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tgetClaims().computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\t((List<String>) getClaims().get(name)).add(value);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void acceptClaimValues(String name, Consumer<List<String>> valuesConsumer) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(valuesConsumer, \"valuesConsumer cannot be null\");\n\t\t\tgetClaims().computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\tList<String> values = (List<String>) getClaims().get(name);\n\t\t\tvaluesConsumer.accept(values);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfiguration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc;\n\nimport java.io.Serial;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.AbstractOAuth2AuthorizationServerMetadata;\nimport org.springframework.util.Assert;\n\n/**\n * A representation of an OpenID Provider Configuration Response, which is returned from\n * an Issuer's Discovery Endpoint, and contains a set of claims about the OpenID\n * Provider's configuration. The claims are defined by the OpenID Connect Discovery 1.0\n * specification.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractOAuth2AuthorizationServerMetadata\n * @see OidcProviderMetadataClaimAccessor\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">4.2.\n * OpenID Provider Configuration Response</a>\n */\npublic final class OidcProviderConfiguration extends AbstractOAuth2AuthorizationServerMetadata\n\t\timplements OidcProviderMetadataClaimAccessor {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2130128410911549024L;\n\n\tprivate OidcProviderConfiguration(Map<String, Object> claims) {\n\t\tsuper(claims);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with empty claims.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided claims.\n\t * @param claims the claims to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withClaims(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\treturn new Builder().claims((c) -> c.putAll(claims));\n\t}\n\n\t/**\n\t * Helps configure an {@link OidcProviderConfiguration}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OidcProviderConfiguration, Builder> {\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Add this Subject Type to the collection of {@code subject_types_supported} in\n\t\t * the resulting {@link OidcProviderConfiguration}, REQUIRED.\n\t\t * @param subjectType the Subject Type that the OpenID Provider supports\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder subjectType(String subjectType) {\n\t\t\taddClaimToClaimList(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, subjectType);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the Subject Types(s) allowing the ability to add,\n\t\t * replace, or remove.\n\t\t * @param subjectTypesConsumer a {@code Consumer} of the Subject Types(s)\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder subjectTypes(Consumer<List<String>> subjectTypesConsumer) {\n\t\t\tacceptClaimValues(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, subjectTypesConsumer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add this {@link JwsAlgorithm JWS} signing algorithm to the collection of\n\t\t * {@code id_token_signing_alg_values_supported} in the resulting\n\t\t * {@link OidcProviderConfiguration}, REQUIRED.\n\t\t * @param signingAlgorithm the {@link JwsAlgorithm JWS} signing algorithm\n\t\t * supported for the {@link OidcIdToken ID Token}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder idTokenSigningAlgorithm(String signingAlgorithm) {\n\t\t\taddClaimToClaimList(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, signingAlgorithm);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the {@link JwsAlgorithm JWS} signing algorithms for the\n\t\t * {@link OidcIdToken ID Token} allowing the ability to add, replace, or remove.\n\t\t * @param signingAlgorithmsConsumer a {@code Consumer} of the {@link JwsAlgorithm\n\t\t * JWS} signing algorithms for the {@link OidcIdToken ID Token}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder idTokenSigningAlgorithms(Consumer<List<String>> signingAlgorithmsConsumer) {\n\t\t\tacceptClaimValues(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED,\n\t\t\t\t\tsigningAlgorithmsConsumer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code userinfo_endpoint} in the resulting\n\t\t * {@link OidcProviderConfiguration}, OPTIONAL.\n\t\t * @param userInfoEndpoint the {@code URL} of the OpenID Connect 1.0 UserInfo\n\t\t * Endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder userInfoEndpoint(String userInfoEndpoint) {\n\t\t\treturn claim(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, userInfoEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code end_session_endpoint} in the resulting\n\t\t * {@link OidcProviderConfiguration}, OPTIONAL.\n\t\t * @param endSessionEndpoint the {@code URL} of the OpenID Connect 1.0 End Session\n\t\t * Endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder endSessionEndpoint(String endSessionEndpoint) {\n\t\t\treturn claim(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT, endSessionEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Validate the claims and build the {@link OidcProviderConfiguration}.\n\t\t * <p>\n\t\t * The following claims are REQUIRED: {@code issuer},\n\t\t * {@code authorization_endpoint}, {@code token_endpoint}, {@code jwks_uri},\n\t\t * {@code response_types_supported}, {@code subject_types_supported} and\n\t\t * {@code id_token_signing_alg_values_supported}.\n\t\t * @return the {@link OidcProviderConfiguration}\n\t\t */\n\t\t@Override\n\t\tpublic OidcProviderConfiguration build() {\n\t\t\tvalidate();\n\t\t\treturn new OidcProviderConfiguration(getClaims());\n\t\t}\n\n\t\t@Override\n\t\tprotected void validate() {\n\t\t\tsuper.validate();\n\t\t\tAssert.notNull(getClaims().get(OidcProviderMetadataClaimNames.JWKS_URI), \"jwksUri cannot be null\");\n\t\t\tAssert.notNull(getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED),\n\t\t\t\t\t\"subjectTypes cannot be null\");\n\t\t\tAssert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED),\n\t\t\t\t\t\"subjectTypes must be of type List\");\n\t\t\tAssert.notEmpty((List<?>) getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED),\n\t\t\t\t\t\"subjectTypes cannot be empty\");\n\t\t\tAssert.notNull(getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED),\n\t\t\t\t\t\"idTokenSigningAlgorithms cannot be null\");\n\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\tgetClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED),\n\t\t\t\t\t\"idTokenSigningAlgorithms must be of type List\");\n\t\t\tAssert.notEmpty(\n\t\t\t\t\t(List<?>) getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED),\n\t\t\t\t\t\"idTokenSigningAlgorithms cannot be empty\");\n\t\t\tif (getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT) != null) {\n\t\t\t\tvalidateURL(getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT),\n\t\t\t\t\t\t\"userInfoEndpoint must be a valid URL\");\n\t\t\t}\n\t\t\tif (getClaims().get(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT) != null) {\n\t\t\t\tvalidateURL(getClaims().get(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT),\n\t\t\t\t\t\t\"endSessionEndpoint must be a valid URL\");\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void addClaimToClaimList(String name, String value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tgetClaims().computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\t((List<String>) getClaims().get(name)).add(value);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void acceptClaimValues(String name, Consumer<List<String>> valuesConsumer) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(valuesConsumer, \"valuesConsumer cannot be null\");\n\t\t\tgetClaims().computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\tList<String> values = (List<String>) getClaims().get(name);\n\t\t\tvaluesConsumer.accept(values);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc;\n\nimport java.net.URL;\nimport java.util.List;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimAccessor;\n\n/**\n * A {@link ClaimAccessor} for the \"claims\" that can be returned in the OpenID Provider\n * Configuration Response.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see ClaimAccessor\n * @see OAuth2AuthorizationServerMetadataClaimAccessor\n * @see OidcProviderMetadataClaimNames\n * @see OidcProviderConfiguration\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata\">3. OpenID\n * Provider Metadata</a>\n */\npublic interface OidcProviderMetadataClaimAccessor extends OAuth2AuthorizationServerMetadataClaimAccessor {\n\n\t/**\n\t * Returns the Subject Identifier types supported {@code (subject_types_supported)}.\n\t * @return the Subject Identifier types supported\n\t */\n\tdefault List<String> getSubjectTypes() {\n\t\treturn getClaimAsStringList(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns the {@link JwsAlgorithm JWS} signing algorithms supported for the\n\t * {@link OidcIdToken ID Token} to encode the claims in a {@link Jwt}\n\t * {@code (id_token_signing_alg_values_supported)}.\n\t * @return the {@link JwsAlgorithm JWS} signing algorithms supported for the\n\t * {@link OidcIdToken ID Token}\n\t */\n\tdefault List<String> getIdTokenSigningAlgorithms() {\n\t\treturn getClaimAsStringList(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint\n\t * {@code (userinfo_endpoint)}.\n\t * @return the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint\n\t */\n\tdefault URL getUserInfoEndpoint() {\n\t\treturn getClaimAsURL(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the {@code URL} of the OpenID Connect 1.0 End Session Endpoint\n\t * {@code (end_session_endpoint)}.\n\t * @return the {@code URL} of the OpenID Connect 1.0 End Session Endpoint\n\t */\n\tdefault URL getEndSessionEndpoint() {\n\t\treturn getClaimAsURL(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc;\n\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames;\n\n/**\n * The names of the \"claims\" defined by OpenID Connect Discovery 1.0 that can be returned\n * in the OpenID Provider Configuration Response.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerMetadataClaimNames\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata\">3. OpenID\n * Provider Metadata</a>\n */\npublic final class OidcProviderMetadataClaimNames extends OAuth2AuthorizationServerMetadataClaimNames {\n\n\t/**\n\t * {@code subject_types_supported} - the Subject Identifier types supported\n\t */\n\tpublic static final String SUBJECT_TYPES_SUPPORTED = \"subject_types_supported\";\n\n\t/**\n\t * {@code id_token_signing_alg_values_supported} - the {@link JwsAlgorithm JWS}\n\t * signing algorithms supported for the {@link OidcIdToken ID Token}\n\t */\n\tpublic static final String ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = \"id_token_signing_alg_values_supported\";\n\n\t/**\n\t * {@code userinfo_endpoint} - the {@code URL} of the OpenID Connect 1.0 UserInfo\n\t * Endpoint\n\t */\n\tpublic static final String USER_INFO_ENDPOINT = \"userinfo_endpoint\";\n\n\t/**\n\t * {@code end_session_endpoint} - the {@code URL} of the OpenID Connect 1.0 End\n\t * Session Endpoint\n\t */\n\tpublic static final String END_SESSION_ENDPOINT = \"end_session_endpoint\";\n\n\tprivate OidcProviderMetadataClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcAuthenticationProviderUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\n\n/**\n * Utility methods for the OpenID Connect 1.0 {@link AuthenticationProvider}'s.\n *\n * @author Joe Grandja\n * @since 7.0\n */\nfinal class OidcAuthenticationProviderUtils {\n\n\tprivate OidcAuthenticationProviderUtils() {\n\t}\n\n\tstatic <T extends OAuth2Token> OAuth2AccessToken accessToken(OAuth2Authorization.Builder builder, T token,\n\t\t\tOAuth2TokenContext accessTokenContext) {\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token.getTokenValue(),\n\t\t\t\ttoken.getIssuedAt(), token.getExpiresAt(), accessTokenContext.getAuthorizedScopes());\n\t\tOAuth2TokenFormat accessTokenFormat = accessTokenContext.getRegisteredClient()\n\t\t\t.getTokenSettings()\n\t\t\t.getAccessTokenFormat();\n\t\tbuilder.token(accessToken, (metadata) -> {\n\t\t\tif (token instanceof ClaimAccessor claimAccessor) {\n\t\t\t\tmetadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, claimAccessor.getClaims());\n\t\t\t}\n\t\t\tmetadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, false);\n\t\t\tmetadata.put(OAuth2TokenFormat.class.getName(), accessTokenFormat.getValue());\n\t\t});\n\n\t\treturn accessToken;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.oidc.converter.RegisteredClientOidcClientRegistrationConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client\n * Configuration Endpoint.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @author Rafal Lewczuk\n * @author Dmitriy Dubson\n * @since 7.0\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see OidcClientRegistrationAuthenticationToken\n * @see OidcClientRegistrationAuthenticationProvider\n * @see <a href=\n * \"https://openid.net/specs/openid-connect-registration-1_0.html#ClientConfigurationEndpoint\">4.\n * Client Configuration Endpoint</a>\n */\npublic final class OidcClientConfigurationAuthenticationProvider implements AuthenticationProvider {\n\n\tstatic final String DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE = \"client.read\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;\n\n\t/**\n\t * Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t */\n\tpublic OidcClientConfigurationAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.clientRegistrationConverter = new RegisteredClientOidcClientRegistrationConverter();\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting a {@link RegisteredClient} to an\n\t * {@link OidcClientRegistration}.\n\t * @param clientRegistrationConverter the {@link Converter} used for converting a\n\t * {@link RegisteredClient} to an {@link OidcClientRegistration}\n\t */\n\tpublic void setClientRegistrationConverter(\n\t\t\tConverter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {\n\t\tAssert.notNull(clientRegistrationConverter, \"clientRegistrationConverter cannot be null\");\n\t\tthis.clientRegistrationConverter = clientRegistrationConverter;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = (OidcClientRegistrationAuthenticationToken) authentication;\n\n\t\tif (!StringUtils.hasText(clientRegistrationAuthentication.getClientId())) {\n\t\t\t// This is not a Client Configuration Request.\n\t\t\t// Return null to allow OidcClientRegistrationAuthenticationProvider to handle\n\t\t\t// it.\n\t\t\treturn null;\n\t\t}\n\n\t\t// Validate the \"registration\" access token\n\t\tAbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;\n\t\tif (AbstractOAuth2TokenAuthenticationToken.class\n\t\t\t.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) {\n\t\t\taccessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication\n\t\t\t\t.getPrincipal();\n\t\t}\n\t\tif (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tString accessTokenValue = accessTokenAuthentication.getToken().getTokenValue();\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue,\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tif (authorization == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with access token\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();\n\t\tif (!authorizedAccessToken.isActive()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\t\tcheckScope(authorizedAccessToken, Collections.singleton(DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE));\n\n\t\treturn findRegistration(clientRegistrationAuthentication, authorization);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OidcClientRegistrationAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tprivate OidcClientRegistrationAuthenticationToken findRegistration(\n\t\t\tOidcClientRegistrationAuthenticationToken clientRegistrationAuthentication,\n\t\t\tOAuth2Authorization authorization) {\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findByClientId(clientRegistrationAuthentication.getClientId());\n\t\tif (registeredClient == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t}\n\n\t\tif (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated client configuration request parameters\");\n\t\t}\n\n\t\tOidcClientRegistration clientRegistration = this.clientRegistrationConverter.convert(registeredClient);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated client configuration request\");\n\t\t}\n\n\t\treturn new OidcClientRegistrationAuthenticationToken(\n\t\t\t\t(Authentication) clientRegistrationAuthentication.getPrincipal(), clientRegistration);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static void checkScope(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken,\n\t\t\tSet<String> requiredScope) {\n\t\tCollection<String> authorizedScope = Collections.emptySet();\n\t\tif (authorizedAccessToken.getClaims().containsKey(OAuth2ParameterNames.SCOPE)) {\n\t\t\tauthorizedScope = (Collection<String>) authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE);\n\t\t}\n\t\tif (!authorizedScope.containsAll(requiredScope)) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t}\n\t\telse if (authorizedScope.size() != requiredScope.size()) {\n\t\t\t// Restrict the access token to only contain the required scope\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.crypto.factory.PasswordEncoderFactories;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientMetadataClaimNames;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.oidc.converter.OidcClientRegistrationRegisteredClientConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.converter.RegisteredClientOidcClientRegistrationConverter;\nimport org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client\n * Registration Endpoint.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @author Rafal Lewczuk\n * @author Dmitriy Dubson\n * @since 7.0\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see OAuth2TokenGenerator\n * @see OidcClientRegistrationAuthenticationToken\n * @see OidcClientConfigurationAuthenticationProvider\n * @see PasswordEncoder\n * @see <a href=\n * \"https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration\">3.\n * Client Registration Endpoint</a>\n */\npublic final class OidcClientRegistrationAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String ERROR_URI = \"https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError\";\n\n\tprivate static final String DEFAULT_CLIENT_REGISTRATION_AUTHORIZED_SCOPE = \"client.create\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;\n\n\tprivate Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;\n\n\tprivate Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\t/**\n\t * Constructs an {@code OidcClientRegistrationAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t * @param tokenGenerator the token generator\n\t */\n\tpublic OidcClientRegistrationAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService,\n\t\t\tOAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(tokenGenerator, \"tokenGenerator cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.tokenGenerator = tokenGenerator;\n\t\tthis.clientRegistrationConverter = new RegisteredClientOidcClientRegistrationConverter();\n\t\tthis.registeredClientConverter = new OidcClientRegistrationRegisteredClientConverter();\n\t\tthis.passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = (OidcClientRegistrationAuthenticationToken) authentication;\n\n\t\tif (clientRegistrationAuthentication.getClientRegistration() == null) {\n\t\t\t// This is not a Client Registration Request.\n\t\t\t// Return null to allow OidcClientConfigurationAuthenticationProvider to\n\t\t\t// handle it.\n\t\t\treturn null;\n\t\t}\n\n\t\t// Validate the \"initial\" access token\n\t\tAbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;\n\t\tif (AbstractOAuth2TokenAuthenticationToken.class\n\t\t\t.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) {\n\t\t\taccessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication\n\t\t\t\t.getPrincipal();\n\t\t}\n\t\tif (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tString accessTokenValue = accessTokenAuthentication.getToken().getTokenValue();\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue,\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tif (authorization == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with initial access token\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();\n\t\tif (!authorizedAccessToken.isActive()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\t\tcheckScope(authorizedAccessToken, Collections.singleton(DEFAULT_CLIENT_REGISTRATION_AUTHORIZED_SCOPE));\n\n\t\treturn registerClient(clientRegistrationAuthentication, authorization);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OidcClientRegistrationAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting an {@link OidcClientRegistration} to\n\t * a {@link RegisteredClient}.\n\t * @param registeredClientConverter the {@link Converter} used for converting an\n\t * {@link OidcClientRegistration} to a {@link RegisteredClient}\n\t */\n\tpublic void setRegisteredClientConverter(\n\t\t\tConverter<OidcClientRegistration, RegisteredClient> registeredClientConverter) {\n\t\tAssert.notNull(registeredClientConverter, \"registeredClientConverter cannot be null\");\n\t\tthis.registeredClientConverter = registeredClientConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting a {@link RegisteredClient} to an\n\t * {@link OidcClientRegistration}.\n\t * @param clientRegistrationConverter the {@link Converter} used for converting a\n\t * {@link RegisteredClient} to an {@link OidcClientRegistration}\n\t */\n\tpublic void setClientRegistrationConverter(\n\t\t\tConverter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter) {\n\t\tAssert.notNull(clientRegistrationConverter, \"clientRegistrationConverter cannot be null\");\n\t\tthis.clientRegistrationConverter = clientRegistrationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link PasswordEncoder} used to encode the\n\t * {@link RegisteredClient#getClientSecret() client secret}. If not set, the client\n\t * secret will be encoded using\n\t * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}.\n\t * @param passwordEncoder the {@link PasswordEncoder} used to encode the client secret\n\t */\n\tpublic void setPasswordEncoder(PasswordEncoder passwordEncoder) {\n\t\tAssert.notNull(passwordEncoder, \"passwordEncoder cannot be null\");\n\t\tthis.passwordEncoder = passwordEncoder;\n\t}\n\n\tprivate OidcClientRegistrationAuthenticationToken registerClient(\n\t\t\tOidcClientRegistrationAuthenticationToken clientRegistrationAuthentication,\n\t\t\tOAuth2Authorization authorization) {\n\n\t\tif (!isValidRedirectUris(clientRegistrationAuthentication.getClientRegistration().getRedirectUris())) {\n\t\t\tthrowInvalidClientRegistration(OAuth2ErrorCodes.INVALID_REDIRECT_URI,\n\t\t\t\t\tOidcClientMetadataClaimNames.REDIRECT_URIS);\n\t\t}\n\n\t\tif (!isValidRedirectUris(\n\t\t\t\tclientRegistrationAuthentication.getClientRegistration().getPostLogoutRedirectUris())) {\n\t\t\tthrowInvalidClientRegistration(\"invalid_client_metadata\",\n\t\t\t\t\tOidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS);\n\t\t}\n\n\t\tif (!isValidTokenEndpointAuthenticationMethod(clientRegistrationAuthentication.getClientRegistration())) {\n\t\t\tthrowInvalidClientRegistration(\"invalid_client_metadata\",\n\t\t\t\t\tOidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated client registration request parameters\");\n\t\t}\n\n\t\tRegisteredClient registeredClient = this.registeredClientConverter\n\t\t\t.convert(clientRegistrationAuthentication.getClientRegistration());\n\n\t\tif (StringUtils.hasText(registeredClient.getClientSecret())) {\n\t\t\t// Encode the client secret\n\t\t\tRegisteredClient updatedRegisteredClient = RegisteredClient.from(registeredClient)\n\t\t\t\t.clientSecret(this.passwordEncoder.encode(registeredClient.getClientSecret()))\n\t\t\t\t.build();\n\t\t\tthis.registeredClientRepository.save(updatedRegisteredClient);\n\t\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()\n\t\t\t\t.equals(clientRegistrationAuthentication.getClientRegistration()\n\t\t\t\t\t.getTokenEndpointAuthenticationMethod())) {\n\t\t\t\t// gh-1344 Return the hashed client_secret\n\t\t\t\tregisteredClient = updatedRegisteredClient;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthis.registeredClientRepository.save(registeredClient);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved registered client\");\n\t\t}\n\n\t\tOAuth2Authorization registeredClientAuthorization = registerAccessToken(registeredClient);\n\n\t\t// Invalidate the \"initial\" access token as it can only be used once\n\t\tOAuth2Authorization.Builder builder = OAuth2Authorization.from(authorization)\n\t\t\t.invalidate(authorization.getAccessToken().getToken());\n\t\tif (authorization.getRefreshToken() != null) {\n\t\t\tbuilder.invalidate(authorization.getRefreshToken().getToken());\n\t\t}\n\t\tauthorization = builder.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization with invalidated initial access token\");\n\t\t}\n\n\t\tMap<String, Object> clientRegistrationClaims = this.clientRegistrationConverter.convert(registeredClient)\n\t\t\t.getClaims();\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.withClaims(clientRegistrationClaims)\n\t\t\t.registrationAccessToken(registeredClientAuthorization.getAccessToken().getToken().getTokenValue())\n\t\t\t.build();\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated client registration request\");\n\t\t}\n\n\t\treturn new OidcClientRegistrationAuthenticationToken(\n\t\t\t\t(Authentication) clientRegistrationAuthentication.getPrincipal(), clientRegistration);\n\t}\n\n\tprivate OAuth2Authorization registerAccessToken(RegisteredClient registeredClient) {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tregisteredClient.getClientAuthenticationMethods().iterator().next(),\n\t\t\t\tregisteredClient.getClientSecret());\n\n\t\tSet<String> authorizedScopes = new HashSet<>();\n\t\tauthorizedScopes\n\t\t\t.add(OidcClientConfigurationAuthenticationProvider.DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE);\n\t\tauthorizedScopes = Collections.unmodifiableSet(authorizedScopes);\n\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(clientPrincipal)\n\t\t\t\t.authorizationServerContext(AuthorizationServerContextHolder.getContext())\n\t\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2Token registrationAccessToken = this.tokenGenerator.generate(tokenContext);\n\t\tif (registrationAccessToken == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"The token generator failed to generate the registration access token.\", ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Generated registration access token\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tOAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t\t.principalName(registeredClient.getClientId())\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.authorizedScopes(authorizedScopes);\n\t\t// @formatter:on\n\n\t\tOidcAuthenticationProviderUtils.accessToken(authorizationBuilder, registrationAccessToken, tokenContext);\n\n\t\tOAuth2Authorization authorization = authorizationBuilder.build();\n\n\t\tthis.authorizationService.save(authorization);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Saved authorization with registration access token\");\n\t\t}\n\n\t\treturn authorization;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static void checkScope(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken,\n\t\t\tSet<String> requiredScope) {\n\t\tCollection<String> authorizedScope = Collections.emptySet();\n\t\tif (authorizedAccessToken.getClaims().containsKey(OAuth2ParameterNames.SCOPE)) {\n\t\t\tauthorizedScope = (Collection<String>) authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE);\n\t\t}\n\t\tif (!authorizedScope.containsAll(requiredScope)) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t}\n\t\telse if (authorizedScope.size() != requiredScope.size()) {\n\t\t\t// Restrict the access token to only contain the required scope\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\t}\n\n\tprivate static boolean isValidRedirectUris(List<String> redirectUris) {\n\t\tif (CollectionUtils.isEmpty(redirectUris)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tfor (String redirectUri : redirectUris) {\n\t\t\ttry {\n\t\t\t\tURI validRedirectUri = new URI(redirectUri);\n\t\t\t\tif (validRedirectUri.getFragment() != null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (URISyntaxException ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate static boolean isValidTokenEndpointAuthenticationMethod(OidcClientRegistration clientRegistration) {\n\t\tString authenticationMethod = clientRegistration.getTokenEndpointAuthenticationMethod();\n\t\tString authenticationSigningAlgorithm = clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm();\n\n\t\tif (!ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(authenticationMethod)\n\t\t\t\t&& !ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(authenticationMethod)) {\n\t\t\treturn !StringUtils.hasText(authenticationSigningAlgorithm);\n\t\t}\n\n\t\tif (\"none\".equals(authenticationSigningAlgorithm)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(authenticationMethod)) {\n\t\t\treturn clientRegistration.getJwkSetUrl() != null && (!StringUtils.hasText(authenticationSigningAlgorithm)\n\t\t\t\t\t|| SignatureAlgorithm.from(authenticationSigningAlgorithm) != null);\n\t\t}\n\t\telse {\n\t\t\t// client_secret_jwt\n\t\t\treturn !StringUtils.hasText(authenticationSigningAlgorithm)\n\t\t\t\t\t|| MacAlgorithm.from(authenticationSigningAlgorithm) != null;\n\t\t}\n\t}\n\n\tprivate static void throwInvalidClientRegistration(String errorCode, String fieldName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"Invalid Client Registration: \" + fieldName, ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for OpenID Connect 1.0 Dynamic Client\n * Registration (and Configuration) Endpoint.\n *\n * @author Joe Grandja\n * @author Ovidiu Popa\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OidcClientRegistration\n * @see OidcClientRegistrationAuthenticationProvider\n * @see OidcClientConfigurationAuthenticationProvider\n */\npublic class OidcClientRegistrationAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 5392324479052435784L;\n\n\tprivate final Authentication principal;\n\n\tprivate final OidcClientRegistration clientRegistration;\n\n\tprivate final String clientId;\n\n\t/**\n\t * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided\n\t * parameters.\n\t * @param principal the authenticated principal\n\t * @param clientRegistration the client registration\n\t */\n\tpublic OidcClientRegistrationAuthenticationToken(Authentication principal,\n\t\t\tOidcClientRegistration clientRegistration) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tthis.principal = principal;\n\t\tthis.clientRegistration = clientRegistration;\n\t\tthis.clientId = null;\n\t\tsetAuthenticated(principal.isAuthenticated());\n\t}\n\n\t/**\n\t * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided\n\t * parameters.\n\t * @param principal the authenticated principal\n\t * @param clientId the client identifier\n\t */\n\tpublic OidcClientRegistrationAuthenticationToken(Authentication principal, String clientId) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\tthis.principal = principal;\n\t\tthis.clientRegistration = null;\n\t\tthis.clientId = clientId;\n\t\tsetAuthenticated(principal.isAuthenticated());\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the client registration.\n\t * @return the client registration\n\t */\n\tpublic OidcClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n\t/**\n\t * Returns the client identifier.\n\t * @return the client identifier\n\t */\n\t@Nullable\n\tpublic String getClientId() {\n\t\treturn this.clientId;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthenticationContext} that holds an\n * {@link OidcLogoutAuthenticationToken} and additional information and is used when\n * validating the OpenID Connect RP-Initiated Logout Request parameters.\n *\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OAuth2AuthenticationContext\n * @see OidcLogoutAuthenticationToken\n * @see OidcLogoutAuthenticationProvider#setAuthenticationValidator(Consumer)\n */\npublic final class OidcLogoutAuthenticationContext implements OAuth2AuthenticationContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OidcLogoutAuthenticationContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link RegisteredClient registered client}.\n\t * @return the {@link RegisteredClient}\n\t */\n\tpublic RegisteredClient getRegisteredClient() {\n\t\treturn get(RegisteredClient.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided\n\t * {@link OidcLogoutAuthenticationToken}.\n\t * @param authentication the {@link OidcLogoutAuthenticationToken}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OidcLogoutAuthenticationToken authentication) {\n\t\treturn new Builder(authentication);\n\t}\n\n\t/**\n\t * A builder for {@link OidcLogoutAuthenticationContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OidcLogoutAuthenticationContext, Builder> {\n\n\t\tprivate Builder(OidcLogoutAuthenticationToken authentication) {\n\t\t\tsuper(authentication);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link RegisteredClient registered client}.\n\t\t * @param registeredClient the {@link RegisteredClient}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder registeredClient(RegisteredClient registeredClient) {\n\t\t\treturn put(RegisteredClient.class, registeredClient);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OidcLogoutAuthenticationContext}.\n\t\t * @return the {@link OidcLogoutAuthenticationContext}\n\t\t */\n\t\t@Override\n\t\tpublic OidcLogoutAuthenticationContext build() {\n\t\t\tAssert.notNull(get(RegisteredClient.class), \"registeredClient cannot be null\");\n\t\t\treturn new OidcLogoutAuthenticationContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.Principal;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 RP-Initiated\n * Logout Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see RegisteredClientRepository\n * @see OAuth2AuthorizationService\n * @see SessionRegistry\n * @see <a href=\"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout\">2.\n * RP-Initiated Logout</a>\n */\npublic final class OidcLogoutAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate final SessionRegistry sessionRegistry;\n\n\tprivate Consumer<OidcLogoutAuthenticationContext> authenticationValidator = new OidcLogoutAuthenticationValidator();\n\n\t/**\n\t * Constructs an {@code OidcLogoutAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param registeredClientRepository the repository of registered clients\n\t * @param authorizationService the authorization service\n\t * @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect\n\t * sessions\n\t */\n\tpublic OidcLogoutAuthenticationProvider(RegisteredClientRepository registeredClientRepository,\n\t\t\tOAuth2AuthorizationService authorizationService, SessionRegistry sessionRegistry) {\n\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tAssert.notNull(sessionRegistry, \"sessionRegistry cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.authorizationService = authorizationService;\n\t\tthis.sessionRegistry = sessionRegistry;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOidcLogoutAuthenticationToken oidcLogoutAuthentication = (OidcLogoutAuthenticationToken) authentication;\n\n\t\tOAuth2Authorization authorization = this.authorizationService\n\t\t\t.findByToken(oidcLogoutAuthentication.getIdTokenHint(), ID_TOKEN_TOKEN_TYPE);\n\t\tif (authorization == null) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_TOKEN, \"id_token_hint\");\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with ID Token\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OidcIdToken> authorizedIdToken = authorization.getToken(OidcIdToken.class);\n\t\tif (authorizedIdToken.isInvalidated() || authorizedIdToken.isBeforeUse()) {\n\t\t\t// Expired ID Token should be accepted\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_TOKEN, \"id_token_hint\");\n\t\t}\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t.findById(authorization.getRegisteredClientId());\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\tOidcIdToken idToken = authorizedIdToken.getToken();\n\n\t\t// Validate client identity\n\t\tList<String> audClaim = idToken.getAudience();\n\t\tif (CollectionUtils.isEmpty(audClaim) || !audClaim.contains(registeredClient.getClientId())) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_TOKEN, IdTokenClaimNames.AUD);\n\t\t}\n\t\tif (StringUtils.hasText(oidcLogoutAuthentication.getClientId())\n\t\t\t\t&& !oidcLogoutAuthentication.getClientId().equals(registeredClient.getClientId())) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tOidcLogoutAuthenticationContext context = OidcLogoutAuthenticationContext.with(oidcLogoutAuthentication)\n\t\t\t.registeredClient(registeredClient)\n\t\t\t.build();\n\t\tthis.authenticationValidator.accept(context);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated logout request parameters\");\n\t\t}\n\n\t\t// Validate user identity\n\t\tif (oidcLogoutAuthentication.isPrincipalAuthenticated()) {\n\t\t\tAuthentication currentUserPrincipal = (Authentication) oidcLogoutAuthentication.getPrincipal();\n\t\t\tAuthentication authorizedUserPrincipal = authorization.getAttribute(Principal.class.getName());\n\t\t\tif (!StringUtils.hasText(idToken.getSubject())\n\t\t\t\t\t|| !currentUserPrincipal.getName().equals(authorizedUserPrincipal.getName())) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_TOKEN, IdTokenClaimNames.SUB);\n\t\t\t}\n\n\t\t\t// Check for active session\n\t\t\tif (StringUtils.hasText(oidcLogoutAuthentication.getSessionId())) {\n\t\t\t\tSessionInformation sessionInformation = findSessionInformation(currentUserPrincipal,\n\t\t\t\t\t\toidcLogoutAuthentication.getSessionId());\n\t\t\t\tif (sessionInformation != null) {\n\t\t\t\t\tString sessionIdHash;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsessionIdHash = createHash(sessionInformation.getSessionId());\n\t\t\t\t\t}\n\t\t\t\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\t\t\t\"Failed to compute hash for Session ID.\", null);\n\t\t\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t\t\t}\n\n\t\t\t\t\tString sidClaim = idToken.getClaim(\"sid\");\n\t\t\t\t\tif (!StringUtils.hasText(sidClaim) || !sidClaim.equals(sessionIdHash)) {\n\t\t\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_TOKEN, \"sid\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated logout request\");\n\t\t}\n\n\t\treturn new OidcLogoutAuthenticationToken(idToken, (Authentication) oidcLogoutAuthentication.getPrincipal(),\n\t\t\t\toidcLogoutAuthentication.getSessionId(), oidcLogoutAuthentication.getClientId(),\n\t\t\t\toidcLogoutAuthentication.getPostLogoutRedirectUri(), oidcLogoutAuthentication.getState());\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OidcLogoutAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OidcLogoutAuthenticationContext} and is responsible for validating specific\n\t * OpenID Connect RP-Initiated Logout Request parameters associated in the\n\t * {@link OidcLogoutAuthenticationToken}. The default authentication validator is\n\t * {@link OidcLogoutAuthenticationValidator}.\n\t *\n\t * <p>\n\t * <b>NOTE:</b> The authentication validator MUST throw\n\t * {@link OAuth2AuthenticationException} if validation fails.\n\t * @param authenticationValidator the {@code Consumer} providing access to the\n\t * {@link OidcLogoutAuthenticationContext} and is responsible for validating specific\n\t * OpenID Connect RP-Initiated Logout Request parameters\n\t */\n\tpublic void setAuthenticationValidator(Consumer<OidcLogoutAuthenticationContext> authenticationValidator) {\n\t\tAssert.notNull(authenticationValidator, \"authenticationValidator cannot be null\");\n\t\tthis.authenticationValidator = authenticationValidator;\n\t}\n\n\tprivate SessionInformation findSessionInformation(Authentication principal, String sessionId) {\n\t\tList<SessionInformation> sessions = this.sessionRegistry.getAllSessions(principal.getPrincipal(), true);\n\t\tSessionInformation sessionInformation = null;\n\t\tif (!CollectionUtils.isEmpty(sessions)) {\n\t\t\tfor (SessionInformation session : sessions) {\n\t\t\t\tif (session.getSessionId().equals(sessionId)) {\n\t\t\t\t\tsessionInformation = session;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sessionInformation;\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OpenID Connect 1.0 Logout Request Parameter: \" + parameterName,\n\t\t\t\t\"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling\");\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n\tprivate static String createHash(String value) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for OpenID Connect 1.0 RP-Initiated\n * Logout Endpoint.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OidcLogoutAuthenticationProvider\n */\npublic class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 4001993612314913888L;\n\n\tprivate final String idTokenHint;\n\n\tprivate final OidcIdToken idToken;\n\n\tprivate final Authentication principal;\n\n\tprivate final String sessionId;\n\n\tprivate final String clientId;\n\n\tprivate final String postLogoutRedirectUri;\n\n\tprivate final String state;\n\n\t/**\n\t * Constructs an {@code OidcLogoutAuthenticationToken} using the provided parameters.\n\t * @param idTokenHint the ID Token previously issued by the Provider to the Client and\n\t * used as a hint about the End-User's current authenticated session with the Client\n\t * @param principal the authenticated principal representing the End-User\n\t * @param sessionId the End-User's current authenticated session identifier with the\n\t * Provider\n\t * @param clientId the client identifier the ID Token was issued to\n\t * @param postLogoutRedirectUri the URI which the Client is requesting that the\n\t * End-User's User Agent be redirected to after a logout has been performed\n\t * @param state the opaque value used by the Client to maintain state between the\n\t * logout request and the callback to the {@code postLogoutRedirectUri}\n\t */\n\tpublic OidcLogoutAuthenticationToken(String idTokenHint, Authentication principal, @Nullable String sessionId,\n\t\t\t@Nullable String clientId, @Nullable String postLogoutRedirectUri, @Nullable String state) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(idTokenHint, \"idTokenHint cannot be empty\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tthis.idTokenHint = idTokenHint;\n\t\tthis.idToken = null;\n\t\tthis.principal = principal;\n\t\tthis.sessionId = sessionId;\n\t\tthis.clientId = clientId;\n\t\tthis.postLogoutRedirectUri = postLogoutRedirectUri;\n\t\tthis.state = state;\n\t\tsetAuthenticated(false);\n\t}\n\n\t/**\n\t * Constructs an {@code OidcLogoutAuthenticationToken} using the provided parameters.\n\t * @param idToken the ID Token previously issued by the Provider to the Client\n\t * @param principal the authenticated principal representing the End-User\n\t * @param sessionId the End-User's current authenticated session identifier with the\n\t * Provider\n\t * @param clientId the client identifier the ID Token was issued to\n\t * @param postLogoutRedirectUri the URI which the Client is requesting that the\n\t * End-User's User Agent be redirected to after a logout has been performed\n\t * @param state the opaque value used by the Client to maintain state between the\n\t * logout request and the callback to the {@code postLogoutRedirectUri}\n\t */\n\tpublic OidcLogoutAuthenticationToken(OidcIdToken idToken, Authentication principal, @Nullable String sessionId,\n\t\t\t@Nullable String clientId, @Nullable String postLogoutRedirectUri, @Nullable String state) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(idToken, \"idToken cannot be null\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tthis.idTokenHint = idToken.getTokenValue();\n\t\tthis.idToken = idToken;\n\t\tthis.principal = principal;\n\t\tthis.sessionId = sessionId;\n\t\tthis.clientId = clientId;\n\t\tthis.postLogoutRedirectUri = postLogoutRedirectUri;\n\t\tthis.state = state;\n\t\tsetAuthenticated(true);\n\t}\n\n\t/**\n\t * Returns the authenticated principal representing the End-User.\n\t * @return the authenticated principal representing the End-User\n\t */\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t/**\n\t * Returns {@code true} if {@link #getPrincipal()} is authenticated, {@code false}\n\t * otherwise.\n\t * @return {@code true} if {@link #getPrincipal()} is authenticated, {@code false}\n\t * otherwise\n\t */\n\tpublic boolean isPrincipalAuthenticated() {\n\t\treturn !AnonymousAuthenticationToken.class.isAssignableFrom(this.principal.getClass())\n\t\t\t\t&& this.principal.isAuthenticated();\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the ID Token previously issued by the Provider to the Client and used as a\n\t * hint about the End-User's current authenticated session with the Client.\n\t * @return the ID Token previously issued by the Provider to the Client\n\t */\n\tpublic String getIdTokenHint() {\n\t\treturn this.idTokenHint;\n\t}\n\n\t/**\n\t * Returns the ID Token previously issued by the Provider to the Client.\n\t * @return the ID Token previously issued by the Provider to the Client\n\t */\n\t@Nullable\n\tpublic OidcIdToken getIdToken() {\n\t\treturn this.idToken;\n\t}\n\n\t/**\n\t * Returns the End-User's current authenticated session identifier with the Provider.\n\t * @return the End-User's current authenticated session identifier with the Provider\n\t */\n\t@Nullable\n\tpublic String getSessionId() {\n\t\treturn this.sessionId;\n\t}\n\n\t/**\n\t * Returns the client identifier the ID Token was issued to.\n\t * @return the client identifier\n\t */\n\t@Nullable\n\tpublic String getClientId() {\n\t\treturn this.clientId;\n\t}\n\n\t/**\n\t * Returns the URI which the Client is requesting that the End-User's User Agent be\n\t * redirected to after a logout has been performed.\n\t * @return the URI which the Client is requesting that the End-User's User Agent be\n\t * redirected to after a logout has been performed\n\t */\n\t@Nullable\n\tpublic String getPostLogoutRedirectUri() {\n\t\treturn this.postLogoutRedirectUri;\n\t}\n\n\t/**\n\t * Returns the opaque value used by the Client to maintain state between the logout\n\t * request and the callback to the {@link #getPostLogoutRedirectUri()}.\n\t * @return the opaque value used by the Client to maintain state between the logout\n\t * request and the callback to the {@link #getPostLogoutRedirectUri()}\n\t */\n\t@Nullable\n\tpublic String getState() {\n\t\treturn this.state;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.util.function.Consumer;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@code Consumer} providing access to the {@link OidcLogoutAuthenticationContext}\n * containing an {@link OidcLogoutAuthenticationToken} and is the default\n * {@link OidcLogoutAuthenticationProvider#setAuthenticationValidator(Consumer)\n * authentication validator} used for validating specific OpenID Connect RP-Initiated\n * Logout Request parameters.\n *\n * <p>\n * The default implementation validates\n * {@link OidcLogoutAuthenticationToken#getPostLogoutRedirectUri()}. If validation fails,\n * an {@link OAuth2AuthenticationException} is thrown.\n *\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OidcLogoutAuthenticationContext\n * @see OidcLogoutAuthenticationToken\n * @see OidcLogoutAuthenticationProvider#setAuthenticationValidator(Consumer)\n */\npublic final class OidcLogoutAuthenticationValidator implements Consumer<OidcLogoutAuthenticationContext> {\n\n\t/**\n\t * The default validator for\n\t * {@link OidcLogoutAuthenticationToken#getPostLogoutRedirectUri()}.\n\t */\n\tpublic static final Consumer<OidcLogoutAuthenticationContext> DEFAULT_POST_LOGOUT_REDIRECT_URI_VALIDATOR = OidcLogoutAuthenticationValidator::validatePostLogoutRedirectUri;\n\n\tprivate final Consumer<OidcLogoutAuthenticationContext> authenticationValidator = DEFAULT_POST_LOGOUT_REDIRECT_URI_VALIDATOR;\n\n\t@Override\n\tpublic void accept(OidcLogoutAuthenticationContext authenticationContext) {\n\t\tthis.authenticationValidator.accept(authenticationContext);\n\t}\n\n\tprivate static void validatePostLogoutRedirectUri(OidcLogoutAuthenticationContext authenticationContext) {\n\t\tOidcLogoutAuthenticationToken oidcLogoutAuthentication = authenticationContext.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\t\tif (StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri())\n\t\t\t\t&& !registeredClient.getPostLogoutRedirectUris()\n\t\t\t\t\t.contains(oidcLogoutAuthentication.getPostLogoutRedirectUri())) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\"OpenID Connect 1.0 Logout Request Parameter: post_logout_redirect_uri\",\n\t\t\t\t\t\"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling\");\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationContext;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthenticationContext} that holds an\n * {@link OidcUserInfoAuthenticationToken} and additional information and is used when\n * mapping claims to an instance of {@link OidcUserInfo}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthenticationContext\n * @see OidcUserInfo\n * @see OidcUserInfoAuthenticationProvider#setUserInfoMapper(Function)\n */\npublic final class OidcUserInfoAuthenticationContext implements OAuth2AuthenticationContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OidcUserInfoAuthenticationContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessToken OAuth 2.0 Access Token}.\n\t * @return the {@link OAuth2AccessToken}\n\t */\n\tpublic OAuth2AccessToken getAccessToken() {\n\t\treturn get(OAuth2AccessToken.class);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Authorization authorization}.\n\t * @return the {@link OAuth2Authorization}\n\t */\n\tpublic OAuth2Authorization getAuthorization() {\n\t\treturn get(OAuth2Authorization.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided\n\t * {@link OidcUserInfoAuthenticationToken}.\n\t * @param authentication the {@link OidcUserInfoAuthenticationToken}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OidcUserInfoAuthenticationToken authentication) {\n\t\treturn new Builder(authentication);\n\t}\n\n\t/**\n\t * A builder for {@link OidcUserInfoAuthenticationContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OidcUserInfoAuthenticationContext, Builder> {\n\n\t\tprivate Builder(OidcUserInfoAuthenticationToken authentication) {\n\t\t\tsuper(authentication);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AccessToken OAuth 2.0 Access Token}.\n\t\t * @param accessToken the {@link OAuth2AccessToken}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder accessToken(OAuth2AccessToken accessToken) {\n\t\t\treturn put(OAuth2AccessToken.class, accessToken);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2Authorization authorization}.\n\t\t * @param authorization the {@link OAuth2Authorization}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorization(OAuth2Authorization authorization) {\n\t\t\treturn put(OAuth2Authorization.class, authorization);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OidcUserInfoAuthenticationContext}.\n\t\t * @return the {@link OidcUserInfoAuthenticationContext}\n\t\t */\n\t\t@Override\n\t\tpublic OidcUserInfoAuthenticationContext build() {\n\t\t\tAssert.notNull(get(OAuth2AccessToken.class), \"accessToken cannot be null\");\n\t\t\tAssert.notNull(get(OAuth2Authorization.class), \"authorization cannot be null\");\n\t\t\treturn new OidcUserInfoAuthenticationContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 UserInfo\n * Endpoint.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2AuthorizationService\n * @see <a href=\"https://openid.net/specs/openid-connect-core-1_0.html#UserInfo\">5.3.\n * UserInfo Endpoint</a>\n */\npublic final class OidcUserInfoAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OAuth2AuthorizationService authorizationService;\n\n\tprivate Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = new DefaultOidcUserInfoMapper();\n\n\t/**\n\t * Constructs an {@code OidcUserInfoAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param authorizationService the authorization service\n\t */\n\tpublic OidcUserInfoAuthenticationProvider(OAuth2AuthorizationService authorizationService) {\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.authorizationService = authorizationService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOidcUserInfoAuthenticationToken userInfoAuthentication = (OidcUserInfoAuthenticationToken) authentication;\n\n\t\tAbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;\n\t\tif (AbstractOAuth2TokenAuthenticationToken.class\n\t\t\t.isAssignableFrom(userInfoAuthentication.getPrincipal().getClass())) {\n\t\t\taccessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) userInfoAuthentication\n\t\t\t\t.getPrincipal();\n\t\t}\n\t\tif (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tString accessTokenValue = accessTokenAuthentication.getToken().getTokenValue();\n\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue,\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tif (authorization == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved authorization with access token\");\n\t\t}\n\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();\n\t\tif (!authorizedAccessToken.isActive()) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tif (!authorizedAccessToken.getToken().getScopes().contains(OidcScopes.OPENID)) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t}\n\n\t\tOAuth2Authorization.Token<OidcIdToken> idToken = authorization.getToken(OidcIdToken.class);\n\t\tif (idToken == null) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Validated user info request\");\n\t\t}\n\n\t\tOidcUserInfoAuthenticationContext authenticationContext = OidcUserInfoAuthenticationContext\n\t\t\t.with(userInfoAuthentication)\n\t\t\t.accessToken(authorizedAccessToken.getToken())\n\t\t\t.authorization(authorization)\n\t\t\t.build();\n\t\tOidcUserInfo userInfo = this.userInfoMapper.apply(authenticationContext);\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Authenticated user info request\");\n\t\t}\n\n\t\treturn new OidcUserInfoAuthenticationToken(accessTokenAuthentication, userInfo);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OidcUserInfoAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link Function} used to extract claims from\n\t * {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}\n\t * for the UserInfo response.\n\t *\n\t * <p>\n\t * The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the\n\t * {@link OidcUserInfoAuthenticationToken}, as well as, the following context\n\t * attributes:\n\t * <ul>\n\t * <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the\n\t * bearer token used to make the request.</li>\n\t * <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the\n\t * {@link OidcIdToken} and {@link OAuth2AccessToken} associated with the bearer token\n\t * used to make the request.</li>\n\t * </ul>\n\t * @param userInfoMapper the {@link Function} used to extract claims from\n\t * {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}\n\t */\n\tpublic void setUserInfoMapper(Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {\n\t\tAssert.notNull(userInfoMapper, \"userInfoMapper cannot be null\");\n\t\tthis.userInfoMapper = userInfoMapper;\n\t}\n\n\tprivate static final class DefaultOidcUserInfoMapper\n\t\t\timplements Function<OidcUserInfoAuthenticationContext, OidcUserInfo> {\n\n\t\t// @formatter:off\n\t\tprivate static final List<String> EMAIL_CLAIMS = Arrays.asList(\n\t\t\t\tStandardClaimNames.EMAIL,\n\t\t\t\tStandardClaimNames.EMAIL_VERIFIED\n\t\t);\n\t\tprivate static final List<String> PHONE_CLAIMS = Arrays.asList(\n\t\t\t\tStandardClaimNames.PHONE_NUMBER,\n\t\t\t\tStandardClaimNames.PHONE_NUMBER_VERIFIED\n\t\t);\n\t\tprivate static final List<String> PROFILE_CLAIMS = Arrays.asList(\n\t\t\t\tStandardClaimNames.NAME,\n\t\t\t\tStandardClaimNames.FAMILY_NAME,\n\t\t\t\tStandardClaimNames.GIVEN_NAME,\n\t\t\t\tStandardClaimNames.MIDDLE_NAME,\n\t\t\t\tStandardClaimNames.NICKNAME,\n\t\t\t\tStandardClaimNames.PREFERRED_USERNAME,\n\t\t\t\tStandardClaimNames.PROFILE,\n\t\t\t\tStandardClaimNames.PICTURE,\n\t\t\t\tStandardClaimNames.WEBSITE,\n\t\t\t\tStandardClaimNames.GENDER,\n\t\t\t\tStandardClaimNames.BIRTHDATE,\n\t\t\t\tStandardClaimNames.ZONEINFO,\n\t\t\t\tStandardClaimNames.LOCALE,\n\t\t\t\tStandardClaimNames.UPDATED_AT\n\t\t);\n\t\t// @formatter:on\n\n\t\t@Override\n\t\tpublic OidcUserInfo apply(OidcUserInfoAuthenticationContext authenticationContext) {\n\t\t\tOAuth2Authorization authorization = authenticationContext.getAuthorization();\n\t\t\tOidcIdToken idToken = authorization.getToken(OidcIdToken.class).getToken();\n\t\t\tOAuth2AccessToken accessToken = authenticationContext.getAccessToken();\n\t\t\tMap<String, Object> scopeRequestedClaims = getClaimsRequestedByScope(idToken.getClaims(),\n\t\t\t\t\taccessToken.getScopes());\n\n\t\t\treturn new OidcUserInfo(scopeRequestedClaims);\n\t\t}\n\n\t\tprivate static Map<String, Object> getClaimsRequestedByScope(Map<String, Object> claims,\n\t\t\t\tSet<String> requestedScopes) {\n\t\t\tSet<String> scopeRequestedClaimNames = new HashSet<>(32);\n\t\t\tscopeRequestedClaimNames.add(StandardClaimNames.SUB);\n\n\t\t\tif (requestedScopes.contains(OidcScopes.ADDRESS)) {\n\t\t\t\tscopeRequestedClaimNames.add(StandardClaimNames.ADDRESS);\n\t\t\t}\n\t\t\tif (requestedScopes.contains(OidcScopes.EMAIL)) {\n\t\t\t\tscopeRequestedClaimNames.addAll(EMAIL_CLAIMS);\n\t\t\t}\n\t\t\tif (requestedScopes.contains(OidcScopes.PHONE)) {\n\t\t\t\tscopeRequestedClaimNames.addAll(PHONE_CLAIMS);\n\t\t\t}\n\t\t\tif (requestedScopes.contains(OidcScopes.PROFILE)) {\n\t\t\t\tscopeRequestedClaimNames.addAll(PROFILE_CLAIMS);\n\t\t\t}\n\n\t\t\tMap<String, Object> requestedClaims = new HashMap<>(claims);\n\t\t\trequestedClaims.keySet().removeIf((claimName) -> !scopeRequestedClaimNames.contains(claimName));\n\n\t\t\treturn requestedClaims;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} implementation used for OpenID Connect 1.0 UserInfo Endpoint.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AbstractAuthenticationToken\n * @see OidcUserInfo\n * @see OidcUserInfoAuthenticationProvider\n */\npublic class OidcUserInfoAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3463488286180103730L;\n\n\tprivate final Authentication principal;\n\n\tprivate final OidcUserInfo userInfo;\n\n\t/**\n\t * Constructs an {@code OidcUserInfoAuthenticationToken} using the provided\n\t * parameters.\n\t * @param principal the principal\n\t */\n\tpublic OidcUserInfoAuthenticationToken(Authentication principal) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tthis.principal = principal;\n\t\tthis.userInfo = null;\n\t\tsetAuthenticated(false);\n\t}\n\n\t/**\n\t * Constructs an {@code OidcUserInfoAuthenticationToken} using the provided\n\t * parameters.\n\t * @param principal the authenticated principal\n\t * @param userInfo the UserInfo claims\n\t */\n\tpublic OidcUserInfoAuthenticationToken(Authentication principal, OidcUserInfo userInfo) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.notNull(userInfo, \"userInfo cannot be null\");\n\t\tthis.principal = principal;\n\t\tthis.userInfo = userInfo;\n\t\tsetAuthenticated(true);\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the UserInfo claims.\n\t * @return the UserInfo claims\n\t */\n\tpublic OidcUserInfo getUserInfo() {\n\t\treturn this.userInfo;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/OidcClientRegistrationRegisteredClientConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.converter;\n\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.UUID;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A {@link Converter} that converts the provided {@link OidcClientRegistration} to a\n * {@link RegisteredClient}.\n *\n * @author Joe Grandja\n * @author Dmitriy Dubson\n * @since 7.0\n */\npublic final class OidcClientRegistrationRegisteredClientConverter\n\t\timplements Converter<OidcClientRegistration, RegisteredClient> {\n\n\tprivate static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 32);\n\n\tprivate static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 48);\n\n\t@Override\n\tpublic RegisteredClient convert(OidcClientRegistration clientRegistration) {\n\t\t// @formatter:off\n\t\tRegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())\n\t\t\t\t.clientId(CLIENT_ID_GENERATOR.generateKey())\n\t\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t\t.clientName(clientRegistration.getClientName());\n\n\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {\n\t\t\tbuilder\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());\n\t\t}\n\t\telse if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {\n\t\t\tbuilder\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());\n\t\t}\n\t\telse if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {\n\t\t\tbuilder.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT);\n\t\t}\n\t\telse {\n\t\t\tbuilder\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET_GENERATOR.generateKey());\n\t\t}\n\n\t\tbuilder.redirectUris((redirectUris) ->\n\t\t\t\tredirectUris.addAll(clientRegistration.getRedirectUris()));\n\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getPostLogoutRedirectUris())) {\n\t\t\tbuilder.postLogoutRedirectUris((postLogoutRedirectUris) ->\n\t\t\t\t\tpostLogoutRedirectUris.addAll(clientRegistration.getPostLogoutRedirectUris()));\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getGrantTypes())) {\n\t\t\tbuilder.authorizationGrantTypes((authorizationGrantTypes) ->\n\t\t\t\t\tclientRegistration.getGrantTypes().forEach((grantType) ->\n\t\t\t\t\t\t\tauthorizationGrantTypes.add(new AuthorizationGrantType(grantType))));\n\t\t}\n\t\telse {\n\t\t\tbuilder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\t}\n\t\tif (CollectionUtils.isEmpty(clientRegistration.getResponseTypes()) ||\n\t\t\t\tclientRegistration.getResponseTypes().contains(OAuth2AuthorizationResponseType.CODE.getValue())) {\n\t\t\tbuilder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tbuilder.scopes((scopes) ->\n\t\t\t\t\tscopes.addAll(clientRegistration.getScopes()));\n\t\t}\n\n\t\tClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()\n\t\t\t\t.requireProofKey(true)\n\t\t\t\t.requireAuthorizationConsent(true);\n\n\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {\n\t\t\tMacAlgorithm macAlgorithm = MacAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());\n\t\t\tif (macAlgorithm == null) {\n\t\t\t\tmacAlgorithm = MacAlgorithm.HS256;\n\t\t\t}\n\t\t\tclientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(macAlgorithm);\n\t\t}\n\t\telse if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {\n\t\t\tSignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());\n\t\t\tif (signatureAlgorithm == null) {\n\t\t\t\tsignatureAlgorithm = SignatureAlgorithm.RS256;\n\t\t\t}\n\t\t\tclientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(signatureAlgorithm);\n\t\t\tclientSettingsBuilder.jwkSetUrl(clientRegistration.getJwkSetUrl().toString());\n\t\t}\n\n\t\tbuilder\n\t\t\t\t.clientSettings(clientSettingsBuilder.build())\n\t\t\t\t.tokenSettings(TokenSettings.builder()\n\t\t\t\t\t\t.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)\n\t\t\t\t\t\t.build());\n\n\t\treturn builder.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/RegisteredClientOidcClientRegistrationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.converter;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@link Converter} that converts the provided {@link RegisteredClient} to an\n * {@link OidcClientRegistration}.\n *\n * @author Joe Grandja\n * @since 7.0\n */\npublic final class RegisteredClientOidcClientRegistrationConverter\n\t\timplements Converter<RegisteredClient, OidcClientRegistration> {\n\n\t@Override\n\tpublic OidcClientRegistration convert(RegisteredClient registeredClient) {\n\t\t// @formatter:off\n\t\tOidcClientRegistration.Builder builder = OidcClientRegistration.builder()\n\t\t\t\t.clientId(registeredClient.getClientId())\n\t\t\t\t.clientIdIssuedAt(registeredClient.getClientIdIssuedAt())\n\t\t\t\t.clientName(registeredClient.getClientName());\n\n\t\tif (registeredClient.getClientSecret() != null) {\n\t\t\tbuilder.clientSecret(registeredClient.getClientSecret());\n\t\t}\n\n\t\tif (registeredClient.getClientSecretExpiresAt() != null) {\n\t\t\tbuilder.clientSecretExpiresAt(registeredClient.getClientSecretExpiresAt());\n\t\t}\n\n\t\tbuilder.redirectUris((redirectUris) ->\n\t\t\t\tredirectUris.addAll(registeredClient.getRedirectUris()));\n\n\t\tif (!CollectionUtils.isEmpty(registeredClient.getPostLogoutRedirectUris())) {\n\t\t\tbuilder.postLogoutRedirectUris((postLogoutRedirectUris) ->\n\t\t\t\t\tpostLogoutRedirectUris.addAll(registeredClient.getPostLogoutRedirectUris()));\n\t\t}\n\n\t\tbuilder.grantTypes((grantTypes) ->\n\t\t\t\tregisteredClient.getAuthorizationGrantTypes().forEach((authorizationGrantType) ->\n\t\t\t\t\t\tgrantTypes.add(authorizationGrantType.getValue())));\n\n\t\tif (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {\n\t\t\tbuilder.responseType(OAuth2AuthorizationResponseType.CODE.getValue());\n\t\t}\n\n\t\tif (!CollectionUtils.isEmpty(registeredClient.getScopes())) {\n\t\t\tbuilder.scopes((scopes) ->\n\t\t\t\t\tscopes.addAll(registeredClient.getScopes()));\n\t\t}\n\n\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\tString registrationClientUri = UriComponentsBuilder.fromUriString(authorizationServerContext.getIssuer())\n\t\t\t\t.path(authorizationServerContext.getAuthorizationServerSettings().getOidcClientRegistrationEndpoint())\n\t\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t\t.toUriString();\n\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(registeredClient.getClientAuthenticationMethods().iterator().next().getValue())\n\t\t\t\t.idTokenSignedResponseAlgorithm(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName())\n\t\t\t\t.registrationClientUrl(registrationClientUri);\n\n\t\tClientSettings clientSettings = registeredClient.getClientSettings();\n\n\t\tif (clientSettings.getJwkSetUrl() != null) {\n\t\t\tbuilder.jwkSetUrl(clientSettings.getJwkSetUrl());\n\t\t}\n\n\t\tif (clientSettings.getTokenEndpointAuthenticationSigningAlgorithm() != null) {\n\t\t\tbuilder.tokenEndpointAuthenticationSigningAlgorithm(clientSettings.getTokenEndpointAuthenticationSigningAlgorithm().getName());\n\t\t}\n\n\t\treturn builder.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/GenericHttpMessageConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.http.converter;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\n\n/**\n * {@link GenericHttpMessageConverter} implementation that delegates to a\n * {@link SmartHttpMessageConverter}.\n *\n * @param <T> the converted object type\n * @author Sebastien Deleuze\n * @since 7.0\n */\nfinal class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {\n\n\tprivate final SmartHttpMessageConverter<T> smartConverter;\n\n\tGenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {\n\t\tthis.smartConverter = smartConverter;\n\t}\n\n\t@Override\n\tpublic boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forType(type), mediaType);\n\t}\n\n\t@Override\n\tpublic T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\treturn this.smartConverter.getSupportedMediaTypes();\n\t}\n\n\t@Override\n\tpublic T read(Class<? extends T> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(clazz, inputMessage);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, contentType, outputMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/HttpMessageConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.http.converter;\n\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.GsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JsonbHttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Utility methods for {@link HttpMessageConverter}'s.\n *\n * @author Joe Grandja\n * @author luamas\n * @since 7.0\n */\nfinal class HttpMessageConverters {\n\n\tprivate static final boolean jacksonPresent;\n\n\tprivate static final boolean jackson2Present;\n\n\tprivate static final boolean gsonPresent;\n\n\tprivate static final boolean jsonbPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = HttpMessageConverters.class.getClassLoader();\n\t\tjacksonPresent = ClassUtils.isPresent(\"tools.jackson.databind.json.JsonMapper\", classLoader);\n\t\tjackson2Present = ClassUtils.isPresent(\"com.fasterxml.jackson.databind.ObjectMapper\", classLoader)\n\t\t\t\t&& ClassUtils.isPresent(\"com.fasterxml.jackson.core.JsonGenerator\", classLoader);\n\t\tgsonPresent = ClassUtils.isPresent(\"com.google.gson.Gson\", classLoader);\n\t\tjsonbPresent = ClassUtils.isPresent(\"jakarta.json.bind.Jsonb\", classLoader);\n\t}\n\n\tprivate HttpMessageConverters() {\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tstatic GenericHttpMessageConverter<Object> getJsonMessageConverter() {\n\t\tif (jacksonPresent) {\n\t\t\treturn new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());\n\t\t}\n\t\tif (jackson2Present) {\n\t\t\treturn new MappingJackson2HttpMessageConverter();\n\t\t}\n\t\tif (gsonPresent) {\n\t\t\treturn new GsonHttpMessageConverter();\n\t\t}\n\t\tif (jsonbPresent) {\n\t\t\treturn new JsonbHttpMessageConverter();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.http.converter;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientMetadataClaimNames;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OidcClientRegistration OpenID Client\n * Registration Request and Response}.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractHttpMessageConverter\n * @see OidcClientRegistration\n */\npublic class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMessageConverter<OidcClientRegistration> {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\tprivate Converter<Map<String, Object>, OidcClientRegistration> clientRegistrationConverter = new MapOidcClientRegistrationConverter();\n\n\tprivate Converter<OidcClientRegistration, Map<String, Object>> clientRegistrationParametersConverter = new OidcClientRegistrationMapConverter();\n\n\tpublic OidcClientRegistrationHttpMessageConverter() {\n\t\tsuper(MediaType.APPLICATION_JSON, new MediaType(\"application\", \"*+json\"));\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OidcClientRegistration.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OidcClientRegistration readInternal(Class<? extends OidcClientRegistration> clazz,\n\t\t\tHttpInputMessage inputMessage) throws HttpMessageNotReadableException {\n\t\ttry {\n\t\t\tMap<String, Object> clientRegistrationParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.clientRegistrationConverter.convert(clientRegistrationParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the OpenID Client Registration: \" + ex.getMessage(), ex, inputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OidcClientRegistration clientRegistration, HttpOutputMessage outputMessage)\n\t\t\tthrows HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, Object> clientRegistrationParameters = this.clientRegistrationParametersConverter\n\t\t\t\t.convert(clientRegistration);\n\t\t\tthis.jsonMessageConverter.write(clientRegistrationParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the OpenID Client Registration: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the OpenID Client Registration\n\t * parameters to an {@link OidcClientRegistration}.\n\t * @param clientRegistrationConverter the {@link Converter} used for converting to an\n\t * {@link OidcClientRegistration}\n\t */\n\tpublic final void setClientRegistrationConverter(\n\t\t\tConverter<Map<String, Object>, OidcClientRegistration> clientRegistrationConverter) {\n\t\tAssert.notNull(clientRegistrationConverter, \"clientRegistrationConverter cannot be null\");\n\t\tthis.clientRegistrationConverter = clientRegistrationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the {@link OidcClientRegistration}\n\t * to a {@code Map} representation of the OpenID Client Registration parameters.\n\t * @param clientRegistrationParametersConverter the {@link Converter} used for\n\t * converting to a {@code Map} representation of the OpenID Client Registration\n\t * parameters\n\t */\n\tpublic final void setClientRegistrationParametersConverter(\n\t\t\tConverter<OidcClientRegistration, Map<String, Object>> clientRegistrationParametersConverter) {\n\t\tAssert.notNull(clientRegistrationParametersConverter, \"clientRegistrationParametersConverter cannot be null\");\n\t\tthis.clientRegistrationParametersConverter = clientRegistrationParametersConverter;\n\t}\n\n\tprivate static final class MapOidcClientRegistrationConverter\n\t\t\timplements Converter<Map<String, Object>, OidcClientRegistration> {\n\n\t\tprivate static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService\n\t\t\t.getSharedInstance();\n\n\t\tprivate static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);\n\n\t\tprivate static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);\n\n\t\tprivate static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class);\n\n\t\tprivate static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class);\n\n\t\tprivate static final Converter<Object, ?> INSTANT_CONVERTER = getConverter(INSTANT_TYPE_DESCRIPTOR);\n\n\t\tprivate final ClaimTypeConverter claimTypeConverter;\n\n\t\tprivate MapOidcClientRegistrationConverter() {\n\t\t\tConverter<Object, ?> stringConverter = getConverter(STRING_TYPE_DESCRIPTOR);\n\t\t\tConverter<Object, ?> collectionStringConverter = getConverter(\n\t\t\t\t\tTypeDescriptor.collection(Collection.class, STRING_TYPE_DESCRIPTOR));\n\t\t\tConverter<Object, ?> urlConverter = getConverter(URL_TYPE_DESCRIPTOR);\n\n\t\t\tMap<String, Converter<Object, ?>> claimConverters = new HashMap<>();\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.CLIENT_ID, stringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, INSTANT_CONVERTER);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.CLIENT_SECRET, stringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT,\n\t\t\t\t\tMapOidcClientRegistrationConverter::convertClientSecretExpiresAt);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.CLIENT_NAME, stringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.REDIRECT_URIS, collectionStringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, collectionStringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD, stringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_SIGNING_ALG, stringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.GRANT_TYPES, collectionStringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.RESPONSE_TYPES, collectionStringConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.SCOPE, MapOidcClientRegistrationConverter::convertScope);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.JWKS_URI, urlConverter);\n\t\t\tclaimConverters.put(OidcClientMetadataClaimNames.ID_TOKEN_SIGNED_RESPONSE_ALG, stringConverter);\n\t\t\tthis.claimTypeConverter = new ClaimTypeConverter(claimConverters);\n\t\t}\n\n\t\t@Override\n\t\tpublic OidcClientRegistration convert(Map<String, Object> source) {\n\t\t\tMap<String, Object> parsedClaims = this.claimTypeConverter.convert(source);\n\t\t\tObject clientSecretExpiresAt = parsedClaims.get(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT);\n\t\t\tif (clientSecretExpiresAt instanceof Number && clientSecretExpiresAt.equals(0)) {\n\t\t\t\tparsedClaims.remove(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT);\n\t\t\t}\n\t\t\treturn OidcClientRegistration.withClaims(parsedClaims).build();\n\t\t}\n\n\t\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\t\treturn (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor);\n\t\t}\n\n\t\tprivate static Instant convertClientSecretExpiresAt(Object clientSecretExpiresAt) {\n\t\t\tif (clientSecretExpiresAt != null && String.valueOf(clientSecretExpiresAt).equals(\"0\")) {\n\t\t\t\t// 0 indicates that client_secret_expires_at does not expire\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn (Instant) INSTANT_CONVERTER.convert(clientSecretExpiresAt);\n\t\t}\n\n\t\tprivate static List<String> convertScope(Object scope) {\n\t\t\tif (scope == null) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\treturn Arrays.asList(StringUtils.delimitedListToStringArray(scope.toString(), \" \"));\n\t\t}\n\n\t}\n\n\tprivate static final class OidcClientRegistrationMapConverter\n\t\t\timplements Converter<OidcClientRegistration, Map<String, Object>> {\n\n\t\t@Override\n\t\tpublic Map<String, Object> convert(OidcClientRegistration source) {\n\t\t\tMap<String, Object> responseClaims = new LinkedHashMap<>(source.getClaims());\n\t\t\tif (source.getClientIdIssuedAt() != null) {\n\t\t\t\tresponseClaims.put(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT,\n\t\t\t\t\t\tsource.getClientIdIssuedAt().getEpochSecond());\n\t\t\t}\n\t\t\tif (source.getClientSecret() != null) {\n\t\t\t\tlong clientSecretExpiresAt = 0;\n\t\t\t\tif (source.getClientSecretExpiresAt() != null) {\n\t\t\t\t\tclientSecretExpiresAt = source.getClientSecretExpiresAt().getEpochSecond();\n\t\t\t\t}\n\t\t\t\tresponseClaims.put(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, clientSecretExpiresAt);\n\t\t\t}\n\t\t\tif (!CollectionUtils.isEmpty(source.getScopes())) {\n\t\t\t\tresponseClaims.put(OidcClientMetadataClaimNames.SCOPE,\n\t\t\t\t\t\tStringUtils.collectionToDelimitedString(source.getScopes(), \" \"));\n\t\t\t}\n\t\t\treturn responseClaims;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.http.converter;\n\nimport java.net.URL;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcProviderMetadataClaimNames;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OidcProviderConfiguration OpenID Provider\n * Configuration Response}.\n *\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see AbstractHttpMessageConverter\n * @see OidcProviderConfiguration\n */\npublic class OidcProviderConfigurationHttpMessageConverter\n\t\textends AbstractHttpMessageConverter<OidcProviderConfiguration> {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\tprivate Converter<Map<String, Object>, OidcProviderConfiguration> providerConfigurationConverter = new OidcProviderConfigurationConverter();\n\n\tprivate Converter<OidcProviderConfiguration, Map<String, Object>> providerConfigurationParametersConverter = OidcProviderConfiguration::getClaims;\n\n\tpublic OidcProviderConfigurationHttpMessageConverter() {\n\t\tsuper(MediaType.APPLICATION_JSON, new MediaType(\"application\", \"*+json\"));\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OidcProviderConfiguration.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OidcProviderConfiguration readInternal(Class<? extends OidcProviderConfiguration> clazz,\n\t\t\tHttpInputMessage inputMessage) throws HttpMessageNotReadableException {\n\t\ttry {\n\t\t\tMap<String, Object> providerConfigurationParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.providerConfigurationConverter.convert(providerConfigurationParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the OpenID Provider Configuration: \" + ex.getMessage(), ex,\n\t\t\t\t\tinputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OidcProviderConfiguration providerConfiguration, HttpOutputMessage outputMessage)\n\t\t\tthrows HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, Object> providerConfigurationResponseParameters = this.providerConfigurationParametersConverter\n\t\t\t\t.convert(providerConfiguration);\n\t\t\tthis.jsonMessageConverter.write(providerConfigurationResponseParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the OpenID Provider Configuration: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the OpenID Provider Configuration\n\t * parameters to an {@link OidcProviderConfiguration}.\n\t * @param providerConfigurationConverter the {@link Converter} used for converting to\n\t * an {@link OidcProviderConfiguration}\n\t */\n\tpublic final void setProviderConfigurationConverter(\n\t\t\tConverter<Map<String, Object>, OidcProviderConfiguration> providerConfigurationConverter) {\n\t\tAssert.notNull(providerConfigurationConverter, \"providerConfigurationConverter cannot be null\");\n\t\tthis.providerConfigurationConverter = providerConfigurationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the\n\t * {@link OidcProviderConfiguration} to a {@code Map} representation of the OpenID\n\t * Provider Configuration.\n\t * @param providerConfigurationParametersConverter the {@link Converter} used for\n\t * converting to a {@code Map} representation of the OpenID Provider Configuration\n\t */\n\tpublic final void setProviderConfigurationParametersConverter(\n\t\t\tConverter<OidcProviderConfiguration, Map<String, Object>> providerConfigurationParametersConverter) {\n\t\tAssert.notNull(providerConfigurationParametersConverter,\n\t\t\t\t\"providerConfigurationParametersConverter cannot be null\");\n\t\tthis.providerConfigurationParametersConverter = providerConfigurationParametersConverter;\n\t}\n\n\tprivate static final class OidcProviderConfigurationConverter\n\t\t\timplements Converter<Map<String, Object>, OidcProviderConfiguration> {\n\n\t\tprivate static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService\n\t\t\t.getSharedInstance();\n\n\t\tprivate static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);\n\n\t\tprivate static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);\n\n\t\tprivate static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class);\n\n\t\tprivate final ClaimTypeConverter claimTypeConverter;\n\n\t\tprivate OidcProviderConfigurationConverter() {\n\t\t\tConverter<Object, ?> collectionStringConverter = getConverter(\n\t\t\t\t\tTypeDescriptor.collection(Collection.class, STRING_TYPE_DESCRIPTOR));\n\t\t\tConverter<Object, ?> urlConverter = getConverter(URL_TYPE_DESCRIPTOR);\n\n\t\t\tMap<String, Converter<Object, ?>> claimConverters = new HashMap<>();\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.ISSUER, urlConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, urlConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, urlConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.JWKS_URI, urlConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, urlConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, collectionStringConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, collectionStringConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, collectionStringConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED,\n\t\t\t\t\tcollectionStringConverter);\n\t\t\tclaimConverters.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, collectionStringConverter);\n\t\t\tthis.claimTypeConverter = new ClaimTypeConverter(claimConverters);\n\t\t}\n\n\t\t@Override\n\t\tpublic OidcProviderConfiguration convert(Map<String, Object> source) {\n\t\t\tMap<String, Object> parsedClaims = this.claimTypeConverter.convert(source);\n\t\t\treturn OidcProviderConfiguration.withClaims(parsedClaims).build();\n\t\t}\n\n\t\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\t\treturn (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.http.converter;\n\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OidcUserInfo OpenID Connect UserInfo\n * Response}.\n *\n * @author Ido Salomon\n * @author Steve Riesenberg\n * @since 7.0\n * @see AbstractHttpMessageConverter\n * @see OidcUserInfo\n */\npublic class OidcUserInfoHttpMessageConverter extends AbstractHttpMessageConverter<OidcUserInfo> {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\tprivate Converter<Map<String, Object>, OidcUserInfo> userInfoConverter = new MapOidcUserInfoConverter();\n\n\tprivate Converter<OidcUserInfo, Map<String, Object>> userInfoParametersConverter = OidcUserInfo::getClaims;\n\n\tpublic OidcUserInfoHttpMessageConverter() {\n\t\tsuper(MediaType.APPLICATION_JSON, new MediaType(\"application\", \"*+json\"));\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OidcUserInfo.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OidcUserInfo readInternal(Class<? extends OidcUserInfo> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows HttpMessageNotReadableException {\n\t\ttry {\n\t\t\tMap<String, Object> userInfoParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.userInfoConverter.convert(userInfoParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the UserInfo response: \" + ex.getMessage(), ex, inputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OidcUserInfo oidcUserInfo, HttpOutputMessage outputMessage)\n\t\t\tthrows HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, Object> userInfoResponseParameters = this.userInfoParametersConverter.convert(oidcUserInfo);\n\t\t\tthis.jsonMessageConverter.write(userInfoResponseParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the UserInfo response: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the UserInfo parameters to an\n\t * {@link OidcUserInfo}.\n\t * @param userInfoConverter the {@link Converter} used for converting to an\n\t * {@link OidcUserInfo}\n\t */\n\tpublic final void setUserInfoConverter(Converter<Map<String, Object>, OidcUserInfo> userInfoConverter) {\n\t\tAssert.notNull(userInfoConverter, \"userInfoConverter cannot be null\");\n\t\tthis.userInfoConverter = userInfoConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the {@link OidcUserInfo} to a\n\t * {@code Map} representation of the UserInfo.\n\t * @param userInfoParametersConverter the {@link Converter} used for converting to a\n\t * {@code Map} representation of the UserInfo\n\t */\n\tpublic final void setUserInfoParametersConverter(\n\t\t\tConverter<OidcUserInfo, Map<String, Object>> userInfoParametersConverter) {\n\t\tAssert.notNull(userInfoParametersConverter, \"userInfoParametersConverter cannot be null\");\n\t\tthis.userInfoParametersConverter = userInfoParametersConverter;\n\t}\n\n\tprivate static final class MapOidcUserInfoConverter implements Converter<Map<String, Object>, OidcUserInfo> {\n\n\t\tprivate static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService\n\t\t\t.getSharedInstance();\n\n\t\tprivate static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);\n\n\t\tprivate static final TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class);\n\n\t\tprivate static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);\n\n\t\tprivate static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class);\n\n\t\tprivate static final TypeDescriptor STRING_OBJECT_MAP_DESCRIPTOR = TypeDescriptor.map(Map.class,\n\t\t\t\tSTRING_TYPE_DESCRIPTOR, OBJECT_TYPE_DESCRIPTOR);\n\n\t\tprivate final ClaimTypeConverter claimTypeConverter;\n\n\t\tprivate MapOidcUserInfoConverter() {\n\t\t\tConverter<Object, ?> booleanConverter = getConverter(BOOLEAN_TYPE_DESCRIPTOR);\n\t\t\tConverter<Object, ?> stringConverter = getConverter(STRING_TYPE_DESCRIPTOR);\n\t\t\tConverter<Object, ?> instantConverter = getConverter(INSTANT_TYPE_DESCRIPTOR);\n\t\t\tConverter<Object, ?> mapConverter = getConverter(STRING_OBJECT_MAP_DESCRIPTOR);\n\n\t\t\tMap<String, Converter<Object, ?>> claimConverters = new HashMap<>();\n\t\t\tclaimConverters.put(StandardClaimNames.SUB, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.NAME, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.GIVEN_NAME, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.FAMILY_NAME, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.MIDDLE_NAME, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.NICKNAME, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.PREFERRED_USERNAME, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.PROFILE, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.PICTURE, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.WEBSITE, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.EMAIL, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.GENDER, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.BIRTHDATE, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.ZONEINFO, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.LOCALE, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.PHONE_NUMBER, stringConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.ADDRESS, mapConverter);\n\t\t\tclaimConverters.put(StandardClaimNames.UPDATED_AT, instantConverter);\n\n\t\t\tthis.claimTypeConverter = new ClaimTypeConverter(claimConverters);\n\t\t}\n\n\t\t@Override\n\t\tpublic OidcUserInfo convert(Map<String, Object> source) {\n\t\t\tMap<String, Object> parsedClaims = this.claimTypeConverter.convert(source);\n\t\t\treturn new OidcUserInfo(parsedClaims);\n\t\t}\n\n\t\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\t\treturn (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientConfigurationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcClientRegistrationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.authentication.OidcClientRegistrationAuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} that processes OpenID Connect 1.0 Dynamic Client Registration (and\n * Client Read) Requests.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OidcClientRegistration\n * @see OidcClientRegistrationAuthenticationConverter\n * @see OidcClientRegistrationAuthenticationProvider\n * @see OidcClientConfigurationAuthenticationProvider\n * @see <a href=\n * \"https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration\">3.\n * Client Registration Endpoint</a>\n * @see <a href=\n * \"https://openid.net/specs/openid-connect-registration-1_0.html#ClientConfigurationEndpoint\">4.\n * Client Configuration Endpoint</a>\n */\npublic final class OidcClientRegistrationEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for OpenID Client Registration requests.\n\t */\n\tprivate static final String DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI = \"/connect/register\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher clientRegistrationEndpointMatcher;\n\n\tprivate final HttpMessageConverter<OidcClientRegistration> clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter();\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tprivate AuthenticationConverter authenticationConverter = new OidcClientRegistrationAuthenticationConverter();\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::sendClientRegistrationResponse;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;\n\n\t/**\n\t * Constructs an {@code OidcClientRegistrationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OidcClientRegistrationEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OidcClientRegistrationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param clientRegistrationEndpointUri the endpoint {@code URI} for OpenID Client\n\t * Registration requests\n\t */\n\tpublic OidcClientRegistrationEndpointFilter(AuthenticationManager authenticationManager,\n\t\t\tString clientRegistrationEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(clientRegistrationEndpointUri, \"clientRegistrationEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.clientRegistrationEndpointMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, clientRegistrationEndpointUri),\n\t\t\t\tcreateClientConfigurationMatcher(clientRegistrationEndpointUri));\n\t}\n\n\tprivate static RequestMatcher createClientConfigurationMatcher(String clientRegistrationEndpointUri) {\n\t\tRequestMatcher clientConfigurationGetMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, clientRegistrationEndpointUri);\n\n\t\tRequestMatcher clientIdMatcher = (request) -> {\n\t\t\tString clientId = request.getParameter(OAuth2ParameterNames.CLIENT_ID);\n\t\t\treturn StringUtils.hasText(clientId);\n\t\t};\n\n\t\treturn new AndRequestMatcher(clientConfigurationGetMatcher, clientIdMatcher);\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.clientRegistrationEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication clientRegistrationAuthentication = this.authenticationConverter.convert(request);\n\n\t\t\tAuthentication clientRegistrationAuthenticationResult = this.authenticationManager\n\t\t\t\t.authenticate(clientRegistrationAuthentication);\n\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response,\n\t\t\t\t\tclientRegistrationAuthenticationResult);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Client registration request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\"OpenID Connect 1.0 Client Registration Error: \" + ex.getMessage(),\n\t\t\t\t\t\"https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError\");\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(error.getDescription(), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response,\n\t\t\t\t\tnew OAuth2AuthenticationException(error));\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Client\n\t * Registration Request from {@link HttpServletRequest} to an instance of\n\t * {@link OidcClientRegistrationAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param authenticationConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract a Client Registration Request from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OidcClientRegistrationAuthenticationToken} and returning the\n\t * {@link OidcClientRegistration Client Registration Response}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OidcClientRegistrationAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate void sendClientRegistrationResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\t\tOidcClientRegistration clientRegistration = ((OidcClientRegistrationAuthenticationToken) authentication)\n\t\t\t.getClientRegistration();\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\tif (HttpMethod.POST.name().equals(request.getMethod())) {\n\t\t\thttpResponse.setStatusCode(HttpStatus.CREATED);\n\t\t}\n\t\telse {\n\t\t\thttpResponse.setStatusCode(HttpStatus.OK);\n\t\t}\n\t\tthis.clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpResponse);\n\t}\n\n\tprivate void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authenticationException) throws IOException {\n\t\tOAuth2Error error = ((OAuth2AuthenticationException) authenticationException).getError();\n\t\tHttpStatus httpStatus = HttpStatus.BAD_REQUEST;\n\t\tif (OAuth2ErrorCodes.INVALID_TOKEN.equals(error.getErrorCode())) {\n\t\t\thttpStatus = HttpStatus.UNAUTHORIZED;\n\t\t}\n\t\telse if (OAuth2ErrorCodes.INSUFFICIENT_SCOPE.equals(error.getErrorCode())) {\n\t\t\thttpStatus = HttpStatus.FORBIDDEN;\n\t\t}\n\t\telse if (OAuth2ErrorCodes.INVALID_CLIENT.equals(error.getErrorCode())) {\n\t\t\thttpStatus = HttpStatus.UNAUTHORIZED;\n\t\t}\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\thttpResponse.setStatusCode(httpStatus);\n\t\tthis.errorHttpResponseConverter.write(error, null, httpResponse);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.authentication.OidcLogoutAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.authentication.OidcLogoutAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} that processes OpenID Connect 1.0 RP-Initiated Logout Requests.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OidcLogoutAuthenticationConverter\n * @see OidcLogoutAuthenticationSuccessHandler\n * @see OidcLogoutAuthenticationProvider\n * @see <a href=\"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout\">2.\n * RP-Initiated Logout</a>\n */\npublic final class OidcLogoutEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for OpenID Connect 1.0 RP-Initiated Logout\n\t * Requests.\n\t */\n\tprivate static final String DEFAULT_OIDC_LOGOUT_ENDPOINT_URI = \"/connect/logout\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher logoutEndpointMatcher;\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = new OidcLogoutAuthenticationSuccessHandler();\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;\n\n\t/**\n\t * Constructs an {@code OidcLogoutEndpointFilter} using the provided parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OidcLogoutEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_OIDC_LOGOUT_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OidcLogoutEndpointFilter} using the provided parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param logoutEndpointUri the endpoint {@code URI} for OpenID Connect 1.0\n\t * RP-Initiated Logout Requests\n\t */\n\tpublic OidcLogoutEndpointFilter(AuthenticationManager authenticationManager, String logoutEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(logoutEndpointUri, \"logoutEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.logoutEndpointMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, logoutEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, logoutEndpointUri));\n\t\tthis.authenticationConverter = new OidcLogoutAuthenticationConverter();\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.logoutEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication oidcLogoutAuthentication = this.authenticationConverter.convert(request);\n\n\t\t\tAuthentication oidcLogoutAuthenticationResult = this.authenticationManager\n\t\t\t\t.authenticate(oidcLogoutAuthentication);\n\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response,\n\t\t\t\t\toidcLogoutAuthenticationResult);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Logout request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\"OpenID Connect 1.0 RP-Initiated Logout Error: \" + ex.getMessage(),\n\t\t\t\t\t\"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling\");\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(error, ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response,\n\t\t\t\t\tnew OAuth2AuthenticationException(error));\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Logout\n\t * Request from {@link HttpServletRequest} to an instance of\n\t * {@link OidcLogoutAuthenticationToken} used for authenticating the request.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract a Logout Request from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OidcLogoutAuthenticationToken} and performing the logout.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OidcLogoutAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException {\n\n\t\tOAuth2Error error = ((OAuth2AuthenticationException) exception).getError();\n\t\tresponse.sendError(HttpStatus.BAD_REQUEST.value(), error.toString());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;\nimport org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcProviderConfigurationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Filter} that processes OpenID Provider Configuration Requests.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see OidcProviderConfiguration\n * @see AuthorizationServerSettings\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">4.1.\n * OpenID Provider Configuration Request</a>\n */\npublic final class OidcProviderConfigurationEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for OpenID Provider Configuration requests.\n\t */\n\tprivate static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = \"/.well-known/openid-configuration\";\n\n\tprivate final RequestMatcher requestMatcher = createRequestMatcher();\n\n\tprivate final OidcProviderConfigurationHttpMessageConverter providerConfigurationHttpMessageConverter = new OidcProviderConfigurationHttpMessageConverter();\n\n\tprivate Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = (providerConfiguration) -> {\n\t};\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OidcProviderConfiguration.Builder} allowing the ability to customize the\n\t * claims of the OpenID Provider's configuration.\n\t * @param providerConfigurationCustomizer the {@code Consumer} providing access to the\n\t * {@link OidcProviderConfiguration.Builder}\n\t */\n\tpublic void setProviderConfigurationCustomizer(\n\t\t\tConsumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer) {\n\t\tAssert.notNull(providerConfigurationCustomizer, \"providerConfigurationCustomizer cannot be null\");\n\t\tthis.providerConfigurationCustomizer = providerConfigurationCustomizer;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\tString issuer = authorizationServerContext.getIssuer();\n\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t.getAuthorizationServerSettings();\n\n\t\tOidcProviderConfiguration.Builder providerConfiguration = OidcProviderConfiguration.builder()\n\t\t\t.issuer(issuer)\n\t\t\t.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))\n\t\t\t.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))\n\t\t\t.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())\n\t\t\t.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))\n\t\t\t.userInfoEndpoint(asUrl(issuer, authorizationServerSettings.getOidcUserInfoEndpoint()))\n\t\t\t.endSessionEndpoint(asUrl(issuer, authorizationServerSettings.getOidcLogoutEndpoint()))\n\t\t\t.responseType(OAuth2AuthorizationResponseType.CODE.getValue())\n\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())\n\t\t\t.grantType(AuthorizationGrantType.TOKEN_EXCHANGE.getValue())\n\t\t\t.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))\n\t\t\t.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())\n\t\t\t.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))\n\t\t\t.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())\n\t\t\t.codeChallengeMethod(\"S256\")\n\t\t\t.tlsClientCertificateBoundAccessTokens(true)\n\t\t\t.dPoPSigningAlgorithms(dPoPSigningAlgorithms())\n\t\t\t.subjectType(\"public\")\n\t\t\t.idTokenSigningAlgorithm(SignatureAlgorithm.RS256.getName())\n\t\t\t.scope(OidcScopes.OPENID);\n\n\t\tthis.providerConfigurationCustomizer.accept(providerConfiguration);\n\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\tthis.providerConfigurationHttpMessageConverter.write(providerConfiguration.build(), MediaType.APPLICATION_JSON,\n\t\t\t\thttpResponse);\n\t}\n\n\tprivate static RequestMatcher createRequestMatcher() {\n\t\tfinal RequestMatcher defaultRequestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI);\n\t\tfinal RequestMatcher multipleIssuersRequestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, \"/**\" + DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI);\n\t\treturn (request) -> AuthorizationServerContextHolder.getContext()\n\t\t\t.getAuthorizationServerSettings()\n\t\t\t.isMultipleIssuersAllowed() ? multipleIssuersRequestMatcher.matches(request)\n\t\t\t\t\t: defaultRequestMatcher.matches(request);\n\t}\n\n\tprivate static Consumer<List<String>> clientAuthenticationMethods() {\n\t\treturn (authenticationMethods) -> {\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.TLS_CLIENT_AUTH.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH.getValue());\n\t\t};\n\t}\n\n\tprivate static Consumer<List<String>> dPoPSigningAlgorithms() {\n\t\treturn (algs) -> {\n\t\t\talgs.add(JwsAlgorithms.RS256);\n\t\t\talgs.add(JwsAlgorithms.RS384);\n\t\t\talgs.add(JwsAlgorithms.RS512);\n\t\t\talgs.add(JwsAlgorithms.PS256);\n\t\t\talgs.add(JwsAlgorithms.PS384);\n\t\t\talgs.add(JwsAlgorithms.PS512);\n\t\t\talgs.add(JwsAlgorithms.ES256);\n\t\t\talgs.add(JwsAlgorithms.ES384);\n\t\t\talgs.add(JwsAlgorithms.ES512);\n\t\t};\n\t}\n\n\tprivate static String asUrl(String issuer, String endpoint) {\n\t\treturn UriComponentsBuilder.fromUriString(issuer).path(endpoint).build().toUriString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcUserInfoHttpMessageConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} that processes OpenID Connect 1.0 UserInfo Requests.\n *\n * @author Ido Salomon\n * @author Steve Riesenberg\n * @author Daniel Garnier-Moiroux\n * @since 7.0\n * @see OidcUserInfo\n * @see OidcUserInfoAuthenticationProvider\n * @see <a href=\"https://openid.net/specs/openid-connect-core-1_0.html#UserInfo\">5.3.\n * UserInfo Endpoint</a>\n */\npublic final class OidcUserInfoEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for OpenID Connect 1.0 UserInfo Requests.\n\t */\n\tprivate static final String DEFAULT_OIDC_USER_INFO_ENDPOINT_URI = \"/userinfo\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher userInfoEndpointMatcher;\n\n\tprivate final HttpMessageConverter<OidcUserInfo> userInfoHttpMessageConverter = new OidcUserInfoHttpMessageConverter();\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tprivate AuthenticationConverter authenticationConverter = this::createAuthentication;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::sendUserInfoResponse;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;\n\n\t/**\n\t * Constructs an {@code OidcUserInfoEndpointFilter} using the provided parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OidcUserInfoEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_OIDC_USER_INFO_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OidcUserInfoEndpointFilter} using the provided parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param userInfoEndpointUri the endpoint {@code URI} for OpenID Connect 1.0 UserInfo\n\t * Requests\n\t */\n\tpublic OidcUserInfoEndpointFilter(AuthenticationManager authenticationManager, String userInfoEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(userInfoEndpointUri, \"userInfoEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.userInfoEndpointMatcher = new OrRequestMatcher(\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, userInfoEndpointUri),\n\t\t\t\tPathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, userInfoEndpointUri));\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.userInfoEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication userInfoAuthentication = this.authenticationConverter.convert(request);\n\n\t\t\tAuthentication userInfoAuthenticationResult = this.authenticationManager\n\t\t\t\t.authenticate(userInfoAuthentication);\n\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, userInfoAuthenticationResult);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"User info request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\"OpenID Connect 1.0 UserInfo Error: \" + ex.getMessage(),\n\t\t\t\t\t\"https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError\");\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(error.getDescription(), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response,\n\t\t\t\t\tnew OAuth2AuthenticationException(error));\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract an\n\t * UserInfo Request from {@link HttpServletRequest} to an instance of\n\t * {@link OidcUserInfoAuthenticationToken} used for authenticating the request.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract an UserInfo Request from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OidcUserInfoAuthenticationToken} and returning the {@link OidcUserInfo\n\t * UserInfo Response}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OidcUserInfoAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate Authentication createAuthentication(HttpServletRequest request) {\n\t\tAuthentication principal = SecurityContextHolder.getContext().getAuthentication();\n\t\treturn new OidcUserInfoAuthenticationToken(principal);\n\t}\n\n\tprivate void sendUserInfoResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\t\tOidcUserInfoAuthenticationToken userInfoAuthenticationToken = (OidcUserInfoAuthenticationToken) authentication;\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\tthis.userInfoHttpMessageConverter.write(userInfoAuthenticationToken.getUserInfo(), null, httpResponse);\n\t}\n\n\tprivate void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authenticationException) throws IOException {\n\t\tOAuth2Error error = ((OAuth2AuthenticationException) authenticationException).getError();\n\t\tHttpStatus httpStatus = HttpStatus.BAD_REQUEST;\n\t\tif (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_TOKEN)) {\n\t\t\thttpStatus = HttpStatus.UNAUTHORIZED;\n\t\t}\n\t\telse if (error.getErrorCode().equals(OAuth2ErrorCodes.INSUFFICIENT_SCOPE)) {\n\t\t\thttpStatus = HttpStatus.FORBIDDEN;\n\t\t}\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\thttpResponse.setStatusCode(httpStatus);\n\t\tthis.errorHttpResponseConverter.write(error, null, httpResponse);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OAuth2EndpointUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web.authentication;\n\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Utility methods for the OAuth 2.0 Protocol Endpoints.\n *\n * @author Joe Grandja\n * @author Greg Li\n * @since 7.0\n */\nfinal class OAuth2EndpointUtils {\n\n\tprivate OAuth2EndpointUtils() {\n\t}\n\n\tstatic MultiValueMap<String, String> getFormParameters(HttpServletRequest request) {\n\t\tMap<String, String[]> parameterMap = request.getParameterMap();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameterMap.forEach((key, values) -> {\n\t\t\tString queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : \"\";\n\t\t\t// If not query parameter then it's a form parameter\n\t\t\tif (!queryString.contains(key) && values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\tparameters.add(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn parameters;\n\t}\n\n\tstatic MultiValueMap<String, String> getQueryParameters(HttpServletRequest request) {\n\t\tMap<String, String[]> parameterMap = request.getParameterMap();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameterMap.forEach((key, values) -> {\n\t\t\tString queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : \"\";\n\t\t\tif (queryString.contains(key) && values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\tparameters.add(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcClientRegistrationAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcClientRegistrationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.OidcClientRegistrationEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an OpenID Connect 1.0 Dynamic Client Registration (or Client Read)\n * Request from {@link HttpServletRequest} and then converts to an\n * {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OidcClientRegistrationAuthenticationToken\n * @see OidcClientRegistrationEndpointFilter\n */\npublic final class OidcClientRegistrationAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate final HttpMessageConverter<OidcClientRegistration> clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter();\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tAuthentication principal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\tif (\"POST\".equals(request.getMethod())) {\n\t\t\tOidcClientRegistration clientRegistration;\n\t\t\ttry {\n\t\t\t\tclientRegistration = this.clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class,\n\t\t\t\t\t\tnew ServletServerHttpRequest(request));\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\t\"OpenID Client Registration Error: \" + ex.getMessage(),\n\t\t\t\t\t\t\"https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError\");\n\t\t\t\tthrow new OAuth2AuthenticationException(error, ex);\n\t\t\t}\n\t\t\treturn new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);\n\t\t}\n\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getQueryParameters(request);\n\n\t\t// client_id (REQUIRED)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\treturn new OidcClientRegistrationAuthenticationToken(principal, clientId);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcLogoutAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.OidcLogoutEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an OpenID Connect 1.0 RP-Initiated Logout Request from\n * {@link HttpServletRequest} and then converts to an\n * {@link OidcLogoutAuthenticationToken} used for authenticating the request.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OidcLogoutAuthenticationToken\n * @see OidcLogoutEndpointFilter\n */\npublic final class OidcLogoutAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(\"anonymous\",\n\t\t\t\"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> parameters = \"GET\".equals(request.getMethod())\n\t\t\t\t? OAuth2EndpointUtils.getQueryParameters(request) : OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// id_token_hint (REQUIRED) // RECOMMENDED as per spec\n\t\tString idTokenHint = parameters.getFirst(\"id_token_hint\");\n\t\tif (!StringUtils.hasText(idTokenHint) || parameters.get(\"id_token_hint\").size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, \"id_token_hint\");\n\t\t}\n\n\t\tAuthentication principal = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (principal == null) {\n\t\t\tprincipal = ANONYMOUS_AUTHENTICATION;\n\t\t}\n\n\t\tString sessionId = null;\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session != null) {\n\t\t\tsessionId = session.getId();\n\t\t}\n\n\t\t// client_id (OPTIONAL)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (StringUtils.hasText(clientId) && parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\t// post_logout_redirect_uri (OPTIONAL)\n\t\tString postLogoutRedirectUri = parameters.getFirst(\"post_logout_redirect_uri\");\n\t\tif (StringUtils.hasText(postLogoutRedirectUri) && parameters.get(\"post_logout_redirect_uri\").size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, \"post_logout_redirect_uri\");\n\t\t}\n\n\t\t// state (OPTIONAL)\n\t\tString state = parameters.getFirst(OAuth2ParameterNames.STATE);\n\t\tif (StringUtils.hasText(state) && parameters.get(OAuth2ParameterNames.STATE).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);\n\t\t}\n\n\t\treturn new OidcLogoutAuthenticationToken(idTokenHint, principal, sessionId, clientId, postLogoutRedirectUri,\n\t\t\t\tstate);\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OpenID Connect 1.0 Logout Request Parameter: \" + parameterName,\n\t\t\t\t\"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling\");\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcLogoutAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web.authentication;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.web.OidcLogoutEndpointFilter;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * An implementation of an {@link AuthenticationSuccessHandler} used for handling an\n * {@link OidcLogoutAuthenticationToken} and performing the OpenID Connect 1.0\n * RP-Initiated Logout.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OidcLogoutEndpointFilter#setAuthenticationSuccessHandler(AuthenticationSuccessHandler)\n * @see LogoutHandler\n */\npublic final class OidcLogoutAuthenticationSuccessHandler implements AuthenticationSuccessHandler {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate final SecurityContextLogoutHandler securityContextLogoutHandler = new SecurityContextLogoutHandler();\n\n\tprivate LogoutHandler logoutHandler = this::performLogout;\n\n\t@Override\n\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException, ServletException {\n\n\t\tif (!(authentication instanceof OidcLogoutAuthenticationToken)) {\n\t\t\tif (this.logger.isErrorEnabled()) {\n\t\t\t\tthis.logger.error(Authentication.class.getSimpleName() + \" must be of type \"\n\t\t\t\t\t\t+ OidcLogoutAuthenticationToken.class.getName() + \" but was \"\n\t\t\t\t\t\t+ authentication.getClass().getName());\n\t\t\t}\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"Unable to process the OpenID Connect 1.0 RP-Initiated Logout response.\", null);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tthis.logoutHandler.logout(request, response, authentication);\n\n\t\tsendLogoutRedirect(request, response, authentication);\n\t}\n\n\t/**\n\t * Sets the {@link LogoutHandler} used for performing logout.\n\t * @param logoutHandler the {@link LogoutHandler} used for performing logout\n\t */\n\tpublic void setLogoutHandler(LogoutHandler logoutHandler) {\n\t\tAssert.notNull(logoutHandler, \"logoutHandler cannot be null\");\n\t\tthis.logoutHandler = logoutHandler;\n\t}\n\n\tprivate void performLogout(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) {\n\t\tOidcLogoutAuthenticationToken oidcLogoutAuthentication = (OidcLogoutAuthenticationToken) authentication;\n\n\t\t// Check for active user session\n\t\tif (oidcLogoutAuthentication.isPrincipalAuthenticated()) {\n\t\t\tthis.securityContextLogoutHandler.logout(request, response,\n\t\t\t\t\t(Authentication) oidcLogoutAuthentication.getPrincipal());\n\t\t}\n\t}\n\n\tprivate void sendLogoutRedirect(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\t\tOidcLogoutAuthenticationToken oidcLogoutAuthentication = (OidcLogoutAuthenticationToken) authentication;\n\n\t\tString redirectUri = \"/\";\n\t\tif (oidcLogoutAuthentication.isAuthenticated()\n\t\t\t\t&& StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri())) {\n\t\t\t// Use the `post_logout_redirect_uri` parameter\n\t\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder\n\t\t\t\t.fromUriString(oidcLogoutAuthentication.getPostLogoutRedirectUri());\n\t\t\tif (StringUtils.hasText(oidcLogoutAuthentication.getState())) {\n\t\t\t\turiBuilder.queryParam(OAuth2ParameterNames.STATE,\n\t\t\t\t\t\tUriUtils.encode(oidcLogoutAuthentication.getState(), StandardCharsets.UTF_8));\n\t\t\t}\n\t\t\t// build(true) -> Components are explicitly encoded\n\t\t\tredirectUri = uriBuilder.build(true).toUriString();\n\t\t}\n\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUri);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AbstractSettings.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport org.springframework.util.Assert;\n\n/**\n * Base implementation for configuration settings.\n *\n * @author Joe Grandja\n * @since 7.0\n */\npublic abstract class AbstractSettings implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 7434920549178503670L;\n\n\tprivate final Map<String, Object> settings;\n\n\tprotected AbstractSettings(Map<String, Object> settings) {\n\t\tAssert.notEmpty(settings, \"settings cannot be empty\");\n\t\tthis.settings = Collections.unmodifiableMap(new HashMap<>(settings));\n\t}\n\n\t/**\n\t * Returns a configuration setting.\n\t * @param name the name of the setting\n\t * @param <T> the type of the setting\n\t * @return the value of the setting, or {@code null} if not available\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T getSetting(String name) {\n\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\treturn (T) getSettings().get(name);\n\t}\n\n\t/**\n\t * Returns a {@code Map} of the configuration settings.\n\t * @return a {@code Map} of the configuration settings\n\t */\n\tpublic Map<String, Object> getSettings() {\n\t\treturn this.settings;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tAbstractSettings that = (AbstractSettings) obj;\n\t\treturn this.settings.equals(that.settings);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.settings);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AbstractSettings {\" + \"settings=\" + this.settings + '}';\n\t}\n\n\t/**\n\t * A builder for subclasses of {@link AbstractSettings}.\n\t *\n\t * @param <T> the type of object\n\t * @param <B> the type of the builder\n\t */\n\tprotected abstract static class AbstractBuilder<T extends AbstractSettings, B extends AbstractBuilder<T, B>> {\n\n\t\tprivate final Map<String, Object> settings = new HashMap<>();\n\n\t\tprotected AbstractBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets a configuration setting.\n\t\t * @param name the name of the setting\n\t\t * @param value the value of the setting\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B setting(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tgetSettings().put(name, value);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the configuration settings {@code Map} allowing the\n\t\t * ability to add, replace, or remove.\n\t\t * @param settingsConsumer a {@link Consumer} of the configuration settings\n\t\t * {@code Map}\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B settings(Consumer<Map<String, Object>> settingsConsumer) {\n\t\t\tsettingsConsumer.accept(getSettings());\n\t\t\treturn getThis();\n\t\t}\n\n\t\tpublic abstract T build();\n\n\t\tprotected final Map<String, Object> getSettings() {\n\t\t\treturn this.settings;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected final B getThis() {\n\t\t\treturn (B) this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport java.io.Serial;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.util.Assert;\n\n/**\n * A facility for authorization server configuration settings.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractSettings\n * @see ConfigurationSettingNames.AuthorizationServer\n */\npublic final class AuthorizationServerSettings extends AbstractSettings {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2719834789442554660L;\n\n\tprivate AuthorizationServerSettings(Map<String, Object> settings) {\n\t\tsuper(settings);\n\t}\n\n\t/**\n\t * Returns the URL of the Authorization Server's Issuer Identifier.\n\t * @return the URL of the Authorization Server's Issuer Identifier\n\t */\n\tpublic String getIssuer() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.ISSUER);\n\t}\n\n\t/**\n\t * Returns {@code true} if multiple issuers are allowed per host. The default is\n\t * {@code false}. Using path components in the URL of the issuer identifier enables\n\t * supporting multiple issuers per host in a multi-tenant hosting configuration.\n\t *\n\t * <p>\n\t * For example:\n\t * <ul>\n\t * <li>{@code https://example.com/issuer1}</li>\n\t * <li>{@code https://example.com/authz/issuer2}</li>\n\t * </ul>\n\t * @return {@code true} if multiple issuers are allowed per host, {@code false}\n\t * otherwise\n\t * @see AuthorizationServerContext#getIssuer()\n\t */\n\tpublic boolean isMultipleIssuersAllowed() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.MULTIPLE_ISSUERS_ALLOWED);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 Authorization endpoint. The default is\n\t * {@code /oauth2/authorize}.\n\t * @return the Authorization endpoint\n\t */\n\tpublic String getAuthorizationEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.AUTHORIZATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 Pushed Authorization Request endpoint. The default is\n\t * {@code /oauth2/par}.\n\t * @return the Pushed Authorization Request endpoint\n\t */\n\tpublic String getPushedAuthorizationRequestEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 Device Authorization endpoint. The default is\n\t * {@code /oauth2/device_authorization}.\n\t * @return the Device Authorization endpoint\n\t */\n\tpublic String getDeviceAuthorizationEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.DEVICE_AUTHORIZATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 Device Verification endpoint. The default is\n\t * {@code /oauth2/device_verification}.\n\t * @return the Device Verification endpoint\n\t */\n\tpublic String getDeviceVerificationEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.DEVICE_VERIFICATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 Token endpoint. The default is {@code /oauth2/token}.\n\t * @return the Token endpoint\n\t */\n\tpublic String getTokenEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.TOKEN_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the JWK Set endpoint. The default is {@code /oauth2/jwks}.\n\t * @return the JWK Set endpoint\n\t */\n\tpublic String getJwkSetEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.JWK_SET_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 Token Revocation endpoint. The default is\n\t * {@code /oauth2/revoke}.\n\t * @return the Token Revocation endpoint\n\t */\n\tpublic String getTokenRevocationEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.TOKEN_REVOCATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 Token Introspection endpoint. The default is\n\t * {@code /oauth2/introspect}.\n\t * @return the Token Introspection endpoint\n\t */\n\tpublic String getTokenIntrospectionEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.TOKEN_INTROSPECTION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OAuth 2.0 Dynamic Client Registration endpoint. The default is\n\t * {@code /oauth2/register}.\n\t * @return the OAuth 2.0 Dynamic Client Registration endpoint\n\t */\n\tpublic String getClientRegistrationEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.CLIENT_REGISTRATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OpenID Connect 1.0 Client Registration endpoint. The default is\n\t * {@code /connect/register}.\n\t * @return the OpenID Connect 1.0 Client Registration endpoint\n\t */\n\tpublic String getOidcClientRegistrationEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.OIDC_CLIENT_REGISTRATION_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OpenID Connect 1.0 UserInfo endpoint. The default is {@code /userinfo}.\n\t * @return the OpenID Connect 1.0 UserInfo endpoint\n\t */\n\tpublic String getOidcUserInfoEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.OIDC_USER_INFO_ENDPOINT);\n\t}\n\n\t/**\n\t * Returns the OpenID Connect 1.0 Logout endpoint. The default is\n\t * {@code /connect/logout}.\n\t * @return the OpenID Connect 1.0 Logout endpoint\n\t */\n\tpublic String getOidcLogoutEndpoint() {\n\t\treturn getSetting(ConfigurationSettingNames.AuthorizationServer.OIDC_LOGOUT_ENDPOINT);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the default settings.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder().multipleIssuersAllowed(false)\n\t\t\t.authorizationEndpoint(\"/oauth2/authorize\")\n\t\t\t.pushedAuthorizationRequestEndpoint(\"/oauth2/par\")\n\t\t\t.deviceAuthorizationEndpoint(\"/oauth2/device_authorization\")\n\t\t\t.deviceVerificationEndpoint(\"/oauth2/device_verification\")\n\t\t\t.tokenEndpoint(\"/oauth2/token\")\n\t\t\t.jwkSetEndpoint(\"/oauth2/jwks\")\n\t\t\t.tokenRevocationEndpoint(\"/oauth2/revoke\")\n\t\t\t.tokenIntrospectionEndpoint(\"/oauth2/introspect\")\n\t\t\t.clientRegistrationEndpoint(\"/oauth2/register\")\n\t\t\t.oidcClientRegistrationEndpoint(\"/connect/register\")\n\t\t\t.oidcUserInfoEndpoint(\"/userinfo\")\n\t\t\t.oidcLogoutEndpoint(\"/connect/logout\");\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided settings.\n\t * @param settings the settings to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withSettings(Map<String, Object> settings) {\n\t\tAssert.notEmpty(settings, \"settings cannot be empty\");\n\t\treturn new Builder().settings((s) -> s.putAll(settings));\n\t}\n\n\t/**\n\t * A builder for {@link AuthorizationServerSettings}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<AuthorizationServerSettings, Builder> {\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the URL the Authorization Server uses as its Issuer Identifier.\n\t\t * @param issuer the URL the Authorization Server uses as its Issuer Identifier.\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder issuer(String issuer) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.ISSUER, issuer);\n\t\t}\n\n\t\t/**\n\t\t * Set to {@code true} if multiple issuers are allowed per host. Using path\n\t\t * components in the URL of the issuer identifier enables supporting multiple\n\t\t * issuers per host in a multi-tenant hosting configuration.\n\t\t *\n\t\t * <p>\n\t\t * For example:\n\t\t * <ul>\n\t\t * <li>{@code https://example.com/issuer1}</li>\n\t\t * <li>{@code https://example.com/authz/issuer2}</li>\n\t\t * </ul>\n\t\t *\n\t\t * <p>\n\t\t * <b>NOTE:</b> Explicitly configuring the issuer identifier via\n\t\t * {@link #issuer(String)} forces to a single-tenant configuration. Avoid\n\t\t * configuring the issuer identifier when using a multi-tenant hosting\n\t\t * configuration, allowing the issuer identifier to be resolved from the\n\t\t * <i>\"current\"</i> request.\n\t\t * @param multipleIssuersAllowed {@code true} if multiple issuers are allowed per\n\t\t * host, {@code false} otherwise\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @see AuthorizationServerContext#getIssuer()\n\t\t */\n\t\tpublic Builder multipleIssuersAllowed(boolean multipleIssuersAllowed) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.MULTIPLE_ISSUERS_ALLOWED,\n\t\t\t\t\tmultipleIssuersAllowed);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 Authorization endpoint.\n\t\t * @param authorizationEndpoint the Authorization endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationEndpoint(String authorizationEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.AUTHORIZATION_ENDPOINT, authorizationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 Pushed Authorization Request endpoint.\n\t\t * @param pushedAuthorizationRequestEndpoint the Pushed Authorization Request\n\t\t * endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder pushedAuthorizationRequestEndpoint(String pushedAuthorizationRequestEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,\n\t\t\t\t\tpushedAuthorizationRequestEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 Device Authorization endpoint.\n\t\t * @param deviceAuthorizationEndpoint the Device Authorization endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder deviceAuthorizationEndpoint(String deviceAuthorizationEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.DEVICE_AUTHORIZATION_ENDPOINT,\n\t\t\t\t\tdeviceAuthorizationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 Device Verification endpoint.\n\t\t * @param deviceVerificationEndpoint the Device Verification endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder deviceVerificationEndpoint(String deviceVerificationEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.DEVICE_VERIFICATION_ENDPOINT,\n\t\t\t\t\tdeviceVerificationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 Token endpoint.\n\t\t * @param tokenEndpoint the Token endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder tokenEndpoint(String tokenEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.TOKEN_ENDPOINT, tokenEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the JWK Set endpoint.\n\t\t * @param jwkSetEndpoint the JWK Set endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder jwkSetEndpoint(String jwkSetEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.JWK_SET_ENDPOINT, jwkSetEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 Token Revocation endpoint.\n\t\t * @param tokenRevocationEndpoint the Token Revocation endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder tokenRevocationEndpoint(String tokenRevocationEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.TOKEN_REVOCATION_ENDPOINT,\n\t\t\t\t\ttokenRevocationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 Token Introspection endpoint.\n\t\t * @param tokenIntrospectionEndpoint the Token Introspection endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder tokenIntrospectionEndpoint(String tokenIntrospectionEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.TOKEN_INTROSPECTION_ENDPOINT,\n\t\t\t\t\ttokenIntrospectionEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OAuth 2.0 Dynamic Client Registration endpoint.\n\t\t * @param clientRegistrationEndpoint the OAuth 2.0 Dynamic Client Registration\n\t\t * endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder clientRegistrationEndpoint(String clientRegistrationEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.CLIENT_REGISTRATION_ENDPOINT,\n\t\t\t\t\tclientRegistrationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OpenID Connect 1.0 Client Registration endpoint.\n\t\t * @param oidcClientRegistrationEndpoint the OpenID Connect 1.0 Client\n\t\t * Registration endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder oidcClientRegistrationEndpoint(String oidcClientRegistrationEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.OIDC_CLIENT_REGISTRATION_ENDPOINT,\n\t\t\t\t\toidcClientRegistrationEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OpenID Connect 1.0 UserInfo endpoint.\n\t\t * @param oidcUserInfoEndpoint the OpenID Connect 1.0 UserInfo endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder oidcUserInfoEndpoint(String oidcUserInfoEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.OIDC_USER_INFO_ENDPOINT, oidcUserInfoEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the OpenID Connect 1.0 Logout endpoint.\n\t\t * @param oidcLogoutEndpoint the OpenID Connect 1.0 Logout endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder oidcLogoutEndpoint(String oidcLogoutEndpoint) {\n\t\t\treturn setting(ConfigurationSettingNames.AuthorizationServer.OIDC_LOGOUT_ENDPOINT, oidcLogoutEndpoint);\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link AuthorizationServerSettings}.\n\t\t * @return the {@link AuthorizationServerSettings}\n\t\t */\n\t\t@Override\n\t\tpublic AuthorizationServerSettings build() {\n\t\t\tAuthorizationServerSettings authorizationServerSettings = new AuthorizationServerSettings(getSettings());\n\t\t\tif (authorizationServerSettings.getIssuer() != null\n\t\t\t\t\t&& authorizationServerSettings.isMultipleIssuersAllowed()) {\n\t\t\t\tthrow new IllegalArgumentException(\"The issuer identifier (\" + authorizationServerSettings.getIssuer()\n\t\t\t\t\t\t+ \") cannot be set when isMultipleIssuersAllowed() is true.\");\n\t\t\t}\n\t\t\treturn authorizationServerSettings;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettings.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport java.io.Serial;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * A facility for client configuration settings.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractSettings\n * @see ConfigurationSettingNames.Client\n */\npublic final class ClientSettings extends AbstractSettings {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 9015034829752473931L;\n\n\tprivate ClientSettings(Map<String, Object> settings) {\n\t\tsuper(settings);\n\t}\n\n\t/**\n\t * Returns {@code true} if the client is required to provide a proof key challenge and\n\t * verifier when performing the Authorization Code Grant flow. The default is\n\t * {@code true}.\n\t * @return {@code true} if the client is required to provide a proof key challenge and\n\t * verifier, {@code false} otherwise\n\t */\n\tpublic boolean isRequireProofKey() {\n\t\treturn getSetting(ConfigurationSettingNames.Client.REQUIRE_PROOF_KEY);\n\t}\n\n\t/**\n\t * Returns {@code true} if authorization consent is required when the client requests\n\t * access. The default is {@code false}.\n\t * @return {@code true} if authorization consent is required when the client requests\n\t * access, {@code false} otherwise\n\t */\n\tpublic boolean isRequireAuthorizationConsent() {\n\t\treturn getSetting(ConfigurationSettingNames.Client.REQUIRE_AUTHORIZATION_CONSENT);\n\t}\n\n\t/**\n\t * Returns the {@code URL} for the Client's JSON Web Key Set.\n\t * @return the {@code URL} for the Client's JSON Web Key Set\n\t */\n\tpublic String getJwkSetUrl() {\n\t\treturn getSetting(ConfigurationSettingNames.Client.JWK_SET_URL);\n\t}\n\n\t/**\n\t * Returns the {@link JwsAlgorithm JWS} algorithm that must be used for signing the\n\t * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the\n\t * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and\n\t * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt}\n\t * authentication methods.\n\t * @return the {@link JwsAlgorithm JWS} algorithm that must be used for signing the\n\t * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint\n\t */\n\tpublic JwsAlgorithm getTokenEndpointAuthenticationSigningAlgorithm() {\n\t\treturn getSetting(ConfigurationSettingNames.Client.TOKEN_ENDPOINT_AUTHENTICATION_SIGNING_ALGORITHM);\n\t}\n\n\t/**\n\t * Returns the expected subject distinguished name associated to the client\n\t * {@code X509Certificate} received during client authentication when using the\n\t * {@code tls_client_auth} method.\n\t * @return the expected subject distinguished name associated to the client\n\t * {@code X509Certificate} received during client authentication\n\t */\n\tpublic String getX509CertificateSubjectDN() {\n\t\treturn getSetting(ConfigurationSettingNames.Client.X509_CERTIFICATE_SUBJECT_DN);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the default settings.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder().requireProofKey(true).requireAuthorizationConsent(false);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided settings.\n\t * @param settings the settings to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withSettings(Map<String, Object> settings) {\n\t\tAssert.notEmpty(settings, \"settings cannot be empty\");\n\t\treturn new Builder().settings((s) -> s.putAll(settings));\n\t}\n\n\t/**\n\t * A builder for {@link ClientSettings}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<ClientSettings, Builder> {\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Set to {@code true} if the client is required to provide a proof key challenge\n\t\t * and verifier when performing the Authorization Code Grant flow.\n\t\t * @param requireProofKey {@code true} if the client is required to provide a\n\t\t * proof key challenge and verifier, {@code false} otherwise\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder requireProofKey(boolean requireProofKey) {\n\t\t\treturn setting(ConfigurationSettingNames.Client.REQUIRE_PROOF_KEY, requireProofKey);\n\t\t}\n\n\t\t/**\n\t\t * Set to {@code true} if authorization consent is required when the client\n\t\t * requests access. This applies to {@code authorization_code} flow.\n\t\t * @param requireAuthorizationConsent {@code true} if authorization consent is\n\t\t * required when the client requests access, {@code false} otherwise\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder requireAuthorizationConsent(boolean requireAuthorizationConsent) {\n\t\t\treturn setting(ConfigurationSettingNames.Client.REQUIRE_AUTHORIZATION_CONSENT, requireAuthorizationConsent);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code URL} for the Client's JSON Web Key Set.\n\t\t * @param jwkSetUrl the {@code URL} for the Client's JSON Web Key Set\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder jwkSetUrl(String jwkSetUrl) {\n\t\t\treturn setting(ConfigurationSettingNames.Client.JWK_SET_URL, jwkSetUrl);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link JwsAlgorithm JWS} algorithm that must be used for signing the\n\t\t * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the\n\t\t * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and\n\t\t * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt}\n\t\t * authentication methods.\n\t\t * @param authenticationSigningAlgorithm the {@link JwsAlgorithm JWS} algorithm\n\t\t * that must be used for signing the {@link Jwt JWT} used to authenticate the\n\t\t * Client at the Token Endpoint\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder tokenEndpointAuthenticationSigningAlgorithm(JwsAlgorithm authenticationSigningAlgorithm) {\n\t\t\treturn setting(ConfigurationSettingNames.Client.TOKEN_ENDPOINT_AUTHENTICATION_SIGNING_ALGORITHM,\n\t\t\t\t\tauthenticationSigningAlgorithm);\n\t\t}\n\n\t\t/**\n\t\t * Sets the expected subject distinguished name associated to the client\n\t\t * {@code X509Certificate} received during client authentication when using the\n\t\t * {@code tls_client_auth} method.\n\t\t * @param x509CertificateSubjectDN the expected subject distinguished name\n\t\t * associated to the client {@code X509Certificate} received during client\n\t\t * authentication * @return the {@link Builder} for further configuration\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder x509CertificateSubjectDN(String x509CertificateSubjectDN) {\n\t\t\treturn setting(ConfigurationSettingNames.Client.X509_CERTIFICATE_SUBJECT_DN, x509CertificateSubjectDN);\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link ClientSettings}.\n\t\t * @return the {@link ClientSettings}\n\t\t */\n\t\t@Override\n\t\tpublic ClientSettings build() {\n\t\t\treturn new ClientSettings(getSettings());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ConfigurationSettingNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\n\n/**\n * The names for all the configuration settings.\n *\n * @author Joe Grandja\n * @since 7.0\n */\npublic final class ConfigurationSettingNames {\n\n\tprivate static final String SETTINGS_NAMESPACE = \"settings.\";\n\n\tprivate ConfigurationSettingNames() {\n\t}\n\n\t/**\n\t * The names for client configuration settings.\n\t */\n\tpublic static final class Client {\n\n\t\tprivate static final String CLIENT_SETTINGS_NAMESPACE = SETTINGS_NAMESPACE.concat(\"client.\");\n\n\t\t/**\n\t\t * Set to {@code true} if the client is required to provide a proof key challenge\n\t\t * and verifier when performing the Authorization Code Grant flow.\n\t\t */\n\t\tpublic static final String REQUIRE_PROOF_KEY = CLIENT_SETTINGS_NAMESPACE.concat(\"require-proof-key\");\n\n\t\t/**\n\t\t * Set to {@code true} if authorization consent is required when the client\n\t\t * requests access. This applies to {@code authorization_code} flow.\n\t\t */\n\t\tpublic static final String REQUIRE_AUTHORIZATION_CONSENT = CLIENT_SETTINGS_NAMESPACE\n\t\t\t.concat(\"require-authorization-consent\");\n\n\t\t/**\n\t\t * Set the {@code URL} for the Client's JSON Web Key Set.\n\t\t */\n\t\tpublic static final String JWK_SET_URL = CLIENT_SETTINGS_NAMESPACE.concat(\"jwk-set-url\");\n\n\t\t/**\n\t\t * Set the {@link JwsAlgorithm JWS} algorithm that must be used for signing the\n\t\t * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the\n\t\t * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and\n\t\t * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt}\n\t\t * authentication methods.\n\t\t */\n\t\tpublic static final String TOKEN_ENDPOINT_AUTHENTICATION_SIGNING_ALGORITHM = CLIENT_SETTINGS_NAMESPACE\n\t\t\t.concat(\"token-endpoint-authentication-signing-algorithm\");\n\n\t\t/**\n\t\t * Set the expected subject distinguished name associated to the client\n\t\t * {@code X509Certificate} received during client authentication when using the\n\t\t * {@code tls_client_auth} method.\n\t\t */\n\t\tpublic static final String X509_CERTIFICATE_SUBJECT_DN = CLIENT_SETTINGS_NAMESPACE\n\t\t\t.concat(\"x509-certificate-subject-dn\");\n\n\t\tprivate Client() {\n\t\t}\n\n\t}\n\n\t/**\n\t * The names for authorization server configuration settings.\n\t */\n\tpublic static final class AuthorizationServer {\n\n\t\tprivate static final String AUTHORIZATION_SERVER_SETTINGS_NAMESPACE = SETTINGS_NAMESPACE\n\t\t\t.concat(\"authorization-server.\");\n\n\t\t/**\n\t\t * Set the URL the Authorization Server uses as its Issuer Identifier.\n\t\t */\n\t\tpublic static final String ISSUER = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat(\"issuer\");\n\n\t\t/**\n\t\t * Set to {@code true} if multiple issuers are allowed per host.\n\t\t */\n\t\tpublic static final String MULTIPLE_ISSUERS_ALLOWED = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"multiple-issuers-allowed\");\n\n\t\t/**\n\t\t * Set the OAuth 2.0 Authorization endpoint.\n\t\t */\n\t\tpublic static final String AUTHORIZATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"authorization-endpoint\");\n\n\t\t/**\n\t\t * Set the OAuth 2.0 Pushed Authorization Request endpoint.\n\t\t */\n\t\tpublic static final String PUSHED_AUTHORIZATION_REQUEST_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"pushed-authorization-request-endpoint\");\n\n\t\t/**\n\t\t * Set the OAuth 2.0 Device Authorization endpoint.\n\t\t */\n\t\tpublic static final String DEVICE_AUTHORIZATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"device-authorization-endpoint\");\n\n\t\t/**\n\t\t * Set the OAuth 2.0 Device Verification endpoint.\n\t\t */\n\t\tpublic static final String DEVICE_VERIFICATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"device-verification-endpoint\");\n\n\t\t/**\n\t\t * Set the OAuth 2.0 Token endpoint.\n\t\t */\n\t\tpublic static final String TOKEN_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat(\"token-endpoint\");\n\n\t\t/**\n\t\t * Set the JWK Set endpoint.\n\t\t */\n\t\tpublic static final String JWK_SET_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"jwk-set-endpoint\");\n\n\t\t/**\n\t\t * Set the OAuth 2.0 Token Revocation endpoint.\n\t\t */\n\t\tpublic static final String TOKEN_REVOCATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"token-revocation-endpoint\");\n\n\t\t/**\n\t\t * Set the OAuth 2.0 Token Introspection endpoint.\n\t\t */\n\t\tpublic static final String TOKEN_INTROSPECTION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"token-introspection-endpoint\");\n\n\t\t/**\n\t\t * Set the OAuth 2.0 Dynamic Client Registration endpoint.\n\t\t */\n\t\tpublic static final String CLIENT_REGISTRATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"client-registration-endpoint\");\n\n\t\t/**\n\t\t * Set the OpenID Connect 1.0 Client Registration endpoint.\n\t\t */\n\t\tpublic static final String OIDC_CLIENT_REGISTRATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"oidc-client-registration-endpoint\");\n\n\t\t/**\n\t\t * Set the OpenID Connect 1.0 UserInfo endpoint.\n\t\t */\n\t\tpublic static final String OIDC_USER_INFO_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"oidc-user-info-endpoint\");\n\n\t\t/**\n\t\t * Set the OpenID Connect 1.0 Logout endpoint.\n\t\t */\n\t\tpublic static final String OIDC_LOGOUT_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE\n\t\t\t.concat(\"oidc-logout-endpoint\");\n\n\t\tprivate AuthorizationServer() {\n\t\t}\n\n\t}\n\n\t/**\n\t * The names for token configuration settings.\n\t */\n\tpublic static final class Token {\n\n\t\tprivate static final String TOKEN_SETTINGS_NAMESPACE = SETTINGS_NAMESPACE.concat(\"token.\");\n\n\t\t/**\n\t\t * Set the time-to-live for an authorization code.\n\t\t */\n\t\tpublic static final String AUTHORIZATION_CODE_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE\n\t\t\t.concat(\"authorization-code-time-to-live\");\n\n\t\t/**\n\t\t * Set the time-to-live for an access token.\n\t\t */\n\t\tpublic static final String ACCESS_TOKEN_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE\n\t\t\t.concat(\"access-token-time-to-live\");\n\n\t\t/**\n\t\t * Set the {@link OAuth2TokenFormat token format} for an access token.\n\t\t */\n\t\tpublic static final String ACCESS_TOKEN_FORMAT = TOKEN_SETTINGS_NAMESPACE.concat(\"access-token-format\");\n\n\t\t/**\n\t\t * Set the time-to-live for a device code.\n\t\t */\n\t\tpublic static final String DEVICE_CODE_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE\n\t\t\t.concat(\"device-code-time-to-live\");\n\n\t\t/**\n\t\t * Set to {@code true} if refresh tokens are reused when returning the access\n\t\t * token response, or {@code false} if a new refresh token is issued.\n\t\t */\n\t\tpublic static final String REUSE_REFRESH_TOKENS = TOKEN_SETTINGS_NAMESPACE.concat(\"reuse-refresh-tokens\");\n\n\t\t/**\n\t\t * Set the time-to-live for a refresh token.\n\t\t */\n\t\tpublic static final String REFRESH_TOKEN_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE\n\t\t\t.concat(\"refresh-token-time-to-live\");\n\n\t\t/**\n\t\t * Set the {@link SignatureAlgorithm JWS} algorithm for signing the\n\t\t * {@link OidcIdToken ID Token}.\n\t\t */\n\t\tpublic static final String ID_TOKEN_SIGNATURE_ALGORITHM = TOKEN_SETTINGS_NAMESPACE\n\t\t\t.concat(\"id-token-signature-algorithm\");\n\n\t\t/**\n\t\t * Set to {@code true} if access tokens must be bound to the client\n\t\t * {@code X509Certificate} received during client authentication when using the\n\t\t * {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.\n\t\t */\n\t\tpublic static final String X509_CERTIFICATE_BOUND_ACCESS_TOKENS = TOKEN_SETTINGS_NAMESPACE\n\t\t\t.concat(\"x509-certificate-bound-access-tokens\");\n\n\t\tprivate Token() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/OAuth2TokenFormat.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Standard data formats for OAuth 2.0 Tokens.\n *\n * @author Joe Grandja\n * @since 7.0\n */\npublic final class OAuth2TokenFormat implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3808658977410337294L;\n\n\t/**\n\t * Self-contained tokens use a protected, time-limited data structure that contains\n\t * token metadata and claims of the user and/or client. JSON Web Token (JWT) is a\n\t * widely used format.\n\t */\n\tpublic static final OAuth2TokenFormat SELF_CONTAINED = new OAuth2TokenFormat(\"self-contained\");\n\n\t/**\n\t * Reference (opaque) tokens are unique identifiers that serve as a reference to the\n\t * token metadata and claims of the user and/or client, stored at the provider.\n\t */\n\tpublic static final OAuth2TokenFormat REFERENCE = new OAuth2TokenFormat(\"reference\");\n\n\tprivate final String value;\n\n\t/**\n\t * Constructs an {@code OAuth2TokenFormat} using the provided value.\n\t * @param value the value of the token format\n\t */\n\tpublic OAuth2TokenFormat(String value) {\n\t\tAssert.hasText(value, \"value cannot be empty\");\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Returns the value of the token format.\n\t * @return the value of the token format\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2TokenFormat that = (OAuth2TokenFormat) obj;\n\t\treturn getValue().equals(that.getValue());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn getValue().hashCode();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport java.io.Serial;\nimport java.time.Duration;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.util.Assert;\n\n/**\n * A facility for token configuration settings.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AbstractSettings\n * @see ConfigurationSettingNames.Token\n */\npublic final class TokenSettings extends AbstractSettings {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2551292126445781141L;\n\n\tprivate TokenSettings(Map<String, Object> settings) {\n\t\tsuper(settings);\n\t}\n\n\t/**\n\t * Returns the time-to-live for an authorization code. The default is 5 minutes.\n\t * @return the time-to-live for an authorization code\n\t */\n\tpublic Duration getAuthorizationCodeTimeToLive() {\n\t\treturn getSetting(ConfigurationSettingNames.Token.AUTHORIZATION_CODE_TIME_TO_LIVE);\n\t}\n\n\t/**\n\t * Returns the time-to-live for an access token. The default is 5 minutes.\n\t * @return the time-to-live for an access token\n\t */\n\tpublic Duration getAccessTokenTimeToLive() {\n\t\treturn getSetting(ConfigurationSettingNames.Token.ACCESS_TOKEN_TIME_TO_LIVE);\n\t}\n\n\t/**\n\t * Returns the token format for an access token. The default is\n\t * {@link OAuth2TokenFormat#SELF_CONTAINED}.\n\t * @return the token format for an access token\n\t */\n\tpublic OAuth2TokenFormat getAccessTokenFormat() {\n\t\treturn getSetting(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT);\n\t}\n\n\t/**\n\t * Returns the time-to-live for a device code. The default is 5 minutes.\n\t * @return the time-to-live for a device code\n\t */\n\tpublic Duration getDeviceCodeTimeToLive() {\n\t\treturn getSetting(ConfigurationSettingNames.Token.DEVICE_CODE_TIME_TO_LIVE);\n\t}\n\n\t/**\n\t * Returns {@code true} if refresh tokens are reused when returning the access token\n\t * response, or {@code false} if a new refresh token is issued. The default is\n\t * {@code true}.\n\t * @return {@code true} if refresh tokens are reused when returning the access token\n\t * response, {@code false} otherwise\n\t */\n\tpublic boolean isReuseRefreshTokens() {\n\t\treturn getSetting(ConfigurationSettingNames.Token.REUSE_REFRESH_TOKENS);\n\t}\n\n\t/**\n\t * Returns the time-to-live for a refresh token. The default is 60 minutes.\n\t * @return the time-to-live for a refresh token\n\t */\n\tpublic Duration getRefreshTokenTimeToLive() {\n\t\treturn getSetting(ConfigurationSettingNames.Token.REFRESH_TOKEN_TIME_TO_LIVE);\n\t}\n\n\t/**\n\t * Returns the {@link SignatureAlgorithm JWS} algorithm for signing the\n\t * {@link OidcIdToken ID Token}. The default is {@link SignatureAlgorithm#RS256\n\t * RS256}.\n\t * @return the {@link SignatureAlgorithm JWS} algorithm for signing the\n\t * {@link OidcIdToken ID Token}\n\t */\n\tpublic SignatureAlgorithm getIdTokenSignatureAlgorithm() {\n\t\treturn getSetting(ConfigurationSettingNames.Token.ID_TOKEN_SIGNATURE_ALGORITHM);\n\t}\n\n\t/**\n\t * Returns {@code true} if access tokens must be bound to the client\n\t * {@code X509Certificate} received during client authentication when using the\n\t * {@code tls_client_auth} or {@code self_signed_tls_client_auth} method. The default\n\t * is {@code false}.\n\t * @return {@code true} if access tokens must be bound to the client\n\t * {@code X509Certificate}, {@code false} otherwise\n\t */\n\tpublic boolean isX509CertificateBoundAccessTokens() {\n\t\treturn getSetting(ConfigurationSettingNames.Token.X509_CERTIFICATE_BOUND_ACCESS_TOKENS);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the default settings.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder().authorizationCodeTimeToLive(Duration.ofMinutes(5))\n\t\t\t.accessTokenTimeToLive(Duration.ofMinutes(5))\n\t\t\t.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)\n\t\t\t.deviceCodeTimeToLive(Duration.ofMinutes(5))\n\t\t\t.reuseRefreshTokens(true)\n\t\t\t.refreshTokenTimeToLive(Duration.ofMinutes(60))\n\t\t\t.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)\n\t\t\t.x509CertificateBoundAccessTokens(false);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided settings.\n\t * @param settings the settings to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withSettings(Map<String, Object> settings) {\n\t\tAssert.notEmpty(settings, \"settings cannot be empty\");\n\t\treturn new Builder().settings((s) -> s.putAll(settings));\n\t}\n\n\t/**\n\t * A builder for {@link TokenSettings}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<TokenSettings, Builder> {\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Set the time-to-live for an authorization code. Must be greater than\n\t\t * {@code Duration.ZERO}. A maximum authorization code lifetime of 10 minutes is\n\t\t * RECOMMENDED.\n\t\t * @param authorizationCodeTimeToLive the time-to-live for an authorization code\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationCodeTimeToLive(Duration authorizationCodeTimeToLive) {\n\t\t\tAssert.notNull(authorizationCodeTimeToLive, \"authorizationCodeTimeToLive cannot be null\");\n\t\t\tAssert.isTrue(authorizationCodeTimeToLive.getSeconds() > 0,\n\t\t\t\t\t\"authorizationCodeTimeToLive must be greater than Duration.ZERO\");\n\t\t\treturn setting(ConfigurationSettingNames.Token.AUTHORIZATION_CODE_TIME_TO_LIVE,\n\t\t\t\t\tauthorizationCodeTimeToLive);\n\t\t}\n\n\t\t/**\n\t\t * Set the time-to-live for an access token. Must be greater than\n\t\t * {@code Duration.ZERO}.\n\t\t * @param accessTokenTimeToLive the time-to-live for an access token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder accessTokenTimeToLive(Duration accessTokenTimeToLive) {\n\t\t\tAssert.notNull(accessTokenTimeToLive, \"accessTokenTimeToLive cannot be null\");\n\t\t\tAssert.isTrue(accessTokenTimeToLive.getSeconds() > 0,\n\t\t\t\t\t\"accessTokenTimeToLive must be greater than Duration.ZERO\");\n\t\t\treturn setting(ConfigurationSettingNames.Token.ACCESS_TOKEN_TIME_TO_LIVE, accessTokenTimeToLive);\n\t\t}\n\n\t\t/**\n\t\t * Set the token format for an access token.\n\t\t * @param accessTokenFormat the token format for an access token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder accessTokenFormat(OAuth2TokenFormat accessTokenFormat) {\n\t\t\tAssert.notNull(accessTokenFormat, \"accessTokenFormat cannot be null\");\n\t\t\treturn setting(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT, accessTokenFormat);\n\t\t}\n\n\t\t/**\n\t\t * Set the time-to-live for a device code. Must be greater than\n\t\t * {@code Duration.ZERO}.\n\t\t * @param deviceCodeTimeToLive the time-to-live for a device code\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder deviceCodeTimeToLive(Duration deviceCodeTimeToLive) {\n\t\t\tAssert.notNull(deviceCodeTimeToLive, \"deviceCodeTimeToLive cannot be null\");\n\t\t\tAssert.isTrue(deviceCodeTimeToLive.getSeconds() > 0,\n\t\t\t\t\t\"deviceCodeTimeToLive must be greater than Duration.ZERO\");\n\t\t\treturn setting(ConfigurationSettingNames.Token.DEVICE_CODE_TIME_TO_LIVE, deviceCodeTimeToLive);\n\t\t}\n\n\t\t/**\n\t\t * Set to {@code true} if refresh tokens are reused when returning the access\n\t\t * token response, or {@code false} if a new refresh token is issued.\n\t\t * @param reuseRefreshTokens {@code true} to reuse refresh tokens, {@code false}\n\t\t * to issue new refresh tokens\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder reuseRefreshTokens(boolean reuseRefreshTokens) {\n\t\t\treturn setting(ConfigurationSettingNames.Token.REUSE_REFRESH_TOKENS, reuseRefreshTokens);\n\t\t}\n\n\t\t/**\n\t\t * Set the time-to-live for a refresh token. Must be greater than\n\t\t * {@code Duration.ZERO}.\n\t\t * @param refreshTokenTimeToLive the time-to-live for a refresh token\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder refreshTokenTimeToLive(Duration refreshTokenTimeToLive) {\n\t\t\tAssert.notNull(refreshTokenTimeToLive, \"refreshTokenTimeToLive cannot be null\");\n\t\t\tAssert.isTrue(refreshTokenTimeToLive.getSeconds() > 0,\n\t\t\t\t\t\"refreshTokenTimeToLive must be greater than Duration.ZERO\");\n\t\t\treturn setting(ConfigurationSettingNames.Token.REFRESH_TOKEN_TIME_TO_LIVE, refreshTokenTimeToLive);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link SignatureAlgorithm JWS} algorithm for signing the\n\t\t * {@link OidcIdToken ID Token}.\n\t\t * @param idTokenSignatureAlgorithm the {@link SignatureAlgorithm JWS} algorithm\n\t\t * for signing the {@link OidcIdToken ID Token}\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder idTokenSignatureAlgorithm(SignatureAlgorithm idTokenSignatureAlgorithm) {\n\t\t\tAssert.notNull(idTokenSignatureAlgorithm, \"idTokenSignatureAlgorithm cannot be null\");\n\t\t\treturn setting(ConfigurationSettingNames.Token.ID_TOKEN_SIGNATURE_ALGORITHM, idTokenSignatureAlgorithm);\n\t\t}\n\n\t\t/**\n\t\t * Set to {@code true} if access tokens must be bound to the client\n\t\t * {@code X509Certificate} received during client authentication when using the\n\t\t * {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.\n\t\t * @param x509CertificateBoundAccessTokens {@code true} if access tokens must be\n\t\t * bound to the client {@code X509Certificate}, {@code false} otherwise\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder x509CertificateBoundAccessTokens(boolean x509CertificateBoundAccessTokens) {\n\t\t\treturn setting(ConfigurationSettingNames.Token.X509_CERTIFICATE_BOUND_ACCESS_TOKENS,\n\t\t\t\t\tx509CertificateBoundAccessTokens);\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link TokenSettings}.\n\t\t * @return the {@link TokenSettings}\n\t\t */\n\t\t@Override\n\t\tpublic TokenSettings build() {\n\t\t\treturn new TokenSettings(getSettings());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DefaultOAuth2TokenContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.util.Assert;\n\n/**\n * Default implementation of {@link OAuth2TokenContext}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenContext\n */\npublic final class DefaultOAuth2TokenContext implements OAuth2TokenContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate DefaultOAuth2TokenContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * A builder for {@link DefaultOAuth2TokenContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<DefaultOAuth2TokenContext, Builder> {\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link DefaultOAuth2TokenContext}.\n\t\t * @return the {@link DefaultOAuth2TokenContext}\n\t\t */\n\t\t@Override\n\t\tpublic DefaultOAuth2TokenContext build() {\n\t\t\treturn new DefaultOAuth2TokenContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2TokenGenerator} that simply delegates to it's internal {@code List} of\n * {@link OAuth2TokenGenerator}(s).\n * <p>\n * Each {@link OAuth2TokenGenerator} is given a chance to\n * {@link OAuth2TokenGenerator#generate(OAuth2TokenContext)} with the first\n * {@code non-null} {@link OAuth2Token} being returned.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenGenerator\n * @see JwtGenerator\n * @see OAuth2RefreshTokenGenerator\n */\npublic final class DelegatingOAuth2TokenGenerator implements OAuth2TokenGenerator<OAuth2Token> {\n\n\tprivate final List<OAuth2TokenGenerator<OAuth2Token>> tokenGenerators;\n\n\t/**\n\t * Constructs a {@code DelegatingOAuth2TokenGenerator} using the provided parameters.\n\t * @param tokenGenerators an array of {@link OAuth2TokenGenerator}(s)\n\t */\n\t@SafeVarargs\n\tpublic DelegatingOAuth2TokenGenerator(OAuth2TokenGenerator<? extends OAuth2Token>... tokenGenerators) {\n\t\tAssert.notEmpty(tokenGenerators, \"tokenGenerators cannot be empty\");\n\t\tAssert.noNullElements(tokenGenerators, \"tokenGenerator cannot be null\");\n\t\tthis.tokenGenerators = Collections.unmodifiableList(asList(tokenGenerators));\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2Token generate(OAuth2TokenContext context) {\n\t\tfor (OAuth2TokenGenerator<OAuth2Token> tokenGenerator : this.tokenGenerators) {\n\t\t\tOAuth2Token token = tokenGenerator.generate(context);\n\t\t\tif (token != null) {\n\t\t\t\treturn token;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static List<OAuth2TokenGenerator<OAuth2Token>> asList(\n\t\t\tOAuth2TokenGenerator<? extends OAuth2Token>... tokenGenerators) {\n\n\t\tList<OAuth2TokenGenerator<OAuth2Token>> tokenGeneratorList = new ArrayList<>();\n\t\tfor (OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator : tokenGenerators) {\n\t\t\ttokenGeneratorList.add((OAuth2TokenGenerator<OAuth2Token>) tokenGenerator);\n\t\t}\n\t\treturn tokenGeneratorList;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2TokenContext} implementation used when encoding a {@link Jwt}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenContext\n * @see JwsHeader.Builder\n * @see JwtClaimsSet.Builder\n * @see JwtEncoder#encode(JwtEncoderParameters)\n */\npublic final class JwtEncodingContext implements OAuth2TokenContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate JwtEncodingContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link JwsHeader.Builder JWS headers} allowing the ability to add,\n\t * replace, or remove.\n\t * @return the {@link JwsHeader.Builder}\n\t */\n\tpublic JwsHeader.Builder getJwsHeader() {\n\t\treturn get(JwsHeader.Builder.class);\n\t}\n\n\t/**\n\t * Returns the {@link JwtClaimsSet.Builder claims} allowing the ability to add,\n\t * replace, or remove.\n\t * @return the {@link JwtClaimsSet.Builder}\n\t */\n\tpublic JwtClaimsSet.Builder getClaims() {\n\t\treturn get(JwtClaimsSet.Builder.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided JWS headers and claims.\n\t * @param jwsHeaderBuilder the JWS headers to initialize the builder\n\t * @param claimsBuilder the claims to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(JwsHeader.Builder jwsHeaderBuilder, JwtClaimsSet.Builder claimsBuilder) {\n\t\treturn new Builder(jwsHeaderBuilder, claimsBuilder);\n\t}\n\n\t/**\n\t * A builder for {@link JwtEncodingContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<JwtEncodingContext, Builder> {\n\n\t\tprivate Builder(JwsHeader.Builder jwsHeaderBuilder, JwtClaimsSet.Builder claimsBuilder) {\n\t\t\tAssert.notNull(jwsHeaderBuilder, \"jwsHeaderBuilder cannot be null\");\n\t\t\tAssert.notNull(claimsBuilder, \"claimsBuilder cannot be null\");\n\t\t\tput(JwsHeader.Builder.class, jwsHeaderBuilder);\n\t\t\tput(JwtClaimsSet.Builder.class, claimsBuilder);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link JwtEncodingContext}.\n\t\t * @return the {@link JwtEncodingContext}\n\t\t */\n\t\t@Override\n\t\tpublic JwtEncodingContext build() {\n\t\t\treturn new JwtEncodingContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.UUID;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link OAuth2TokenGenerator} that generates a {@link Jwt} used for an\n * {@link OAuth2AccessToken} or {@link OidcIdToken}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenGenerator\n * @see Jwt\n * @see JwtEncoder\n * @see OAuth2TokenCustomizer\n * @see JwtEncodingContext\n * @see OAuth2AccessToken\n * @see OidcIdToken\n */\npublic final class JwtGenerator implements OAuth2TokenGenerator<Jwt> {\n\n\tprivate final JwtEncoder jwtEncoder;\n\n\tprivate OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer;\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Constructs a {@code JwtGenerator} using the provided parameters.\n\t * @param jwtEncoder the jwt encoder\n\t */\n\tpublic JwtGenerator(JwtEncoder jwtEncoder) {\n\t\tAssert.notNull(jwtEncoder, \"jwtEncoder cannot be null\");\n\t\tthis.jwtEncoder = jwtEncoder;\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic Jwt generate(OAuth2TokenContext context) {\n\t\t// @formatter:off\n\t\tif (context.getTokenType() == null ||\n\t\t\t\t(!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) &&\n\t\t\t\t\t\t!OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue()))) {\n\t\t\treturn null;\n\t\t}\n\t\tif (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) &&\n\t\t\t\t!OAuth2TokenFormat.SELF_CONTAINED.equals(context.getRegisteredClient().getTokenSettings().getAccessTokenFormat())) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:on\n\n\t\tString issuer = null;\n\t\tif (context.getAuthorizationServerContext() != null) {\n\t\t\tissuer = context.getAuthorizationServerContext().getIssuer();\n\t\t}\n\t\tRegisteredClient registeredClient = context.getRegisteredClient();\n\n\t\tInstant issuedAt = this.clock.instant();\n\t\tInstant expiresAt;\n\t\tJwsAlgorithm jwsAlgorithm = SignatureAlgorithm.RS256;\n\t\tif (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {\n\t\t\t// TODO Allow configuration for ID Token time-to-live\n\t\t\texpiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);\n\t\t\tif (registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm() != null) {\n\t\t\t\tjwsAlgorithm = registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm();\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\texpiresAt = issuedAt.plus(registeredClient.getTokenSettings().getAccessTokenTimeToLive());\n\t\t}\n\n\t\t// @formatter:off\n\t\tJwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder();\n\t\tif (StringUtils.hasText(issuer)) {\n\t\t\tclaimsBuilder.issuer(issuer);\n\t\t}\n\t\tclaimsBuilder\n\t\t\t\t.subject(context.getPrincipal().getName())\n\t\t\t\t.audience(Collections.singletonList(registeredClient.getClientId()))\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.id(UUID.randomUUID().toString());\n\t\tif (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {\n\t\t\tclaimsBuilder.notBefore(issuedAt);\n\t\t\tif (!CollectionUtils.isEmpty(context.getAuthorizedScopes())) {\n\t\t\t\tclaimsBuilder.claim(OAuth2ParameterNames.SCOPE, context.getAuthorizedScopes());\n\t\t\t}\n\t\t}\n\t\telse if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {\n\t\t\tclaimsBuilder.claim(IdTokenClaimNames.AZP, registeredClient.getClientId());\n\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType())) {\n\t\t\t\tOAuth2AuthorizationRequest authorizationRequest = context.getAuthorization().getAttribute(\n\t\t\t\t\t\tOAuth2AuthorizationRequest.class.getName());\n\t\t\t\tString nonce = (String) authorizationRequest.getAdditionalParameters().get(OidcParameterNames.NONCE);\n\t\t\t\tif (StringUtils.hasText(nonce)) {\n\t\t\t\t\tclaimsBuilder.claim(IdTokenClaimNames.NONCE, nonce);\n\t\t\t\t}\n\t\t\t\tSessionInformation sessionInformation = context.get(SessionInformation.class);\n\t\t\t\tif (sessionInformation != null) {\n\t\t\t\t\tclaimsBuilder.claim(\"sid\", sessionInformation.getSessionId());\n\t\t\t\t\tclaimsBuilder.claim(IdTokenClaimNames.AUTH_TIME, sessionInformation.getLastRequest());\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (AuthorizationGrantType.REFRESH_TOKEN.equals(context.getAuthorizationGrantType())) {\n\t\t\t\tOidcIdToken currentIdToken = context.getAuthorization().getToken(OidcIdToken.class).getToken();\n\t\t\t\tif (currentIdToken.hasClaim(\"sid\")) {\n\t\t\t\t\tclaimsBuilder.claim(\"sid\", currentIdToken.getClaim(\"sid\"));\n\t\t\t\t}\n\t\t\t\tif (currentIdToken.hasClaim(IdTokenClaimNames.AUTH_TIME)) {\n\t\t\t\t\tclaimsBuilder.claim(IdTokenClaimNames.AUTH_TIME, currentIdToken.<Date>getClaim(IdTokenClaimNames.AUTH_TIME));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// @formatter:on\n\n\t\tJwsHeader.Builder jwsHeaderBuilder = JwsHeader.with(jwsAlgorithm);\n\n\t\tif (this.jwtCustomizer != null) {\n\t\t\t// @formatter:off\n\t\t\tJwtEncodingContext.Builder jwtContextBuilder = JwtEncodingContext.with(jwsHeaderBuilder, claimsBuilder)\n\t\t\t\t\t.registeredClient(context.getRegisteredClient())\n\t\t\t\t\t.principal(context.getPrincipal())\n\t\t\t\t\t.authorizationServerContext(context.getAuthorizationServerContext())\n\t\t\t\t\t.authorizedScopes(context.getAuthorizedScopes())\n\t\t\t\t\t.tokenType(context.getTokenType())\n\t\t\t\t\t.authorizationGrantType(context.getAuthorizationGrantType());\n\t\t\tif (context.getAuthorization() != null) {\n\t\t\t\tjwtContextBuilder.authorization(context.getAuthorization());\n\t\t\t}\n\t\t\tif (context.getAuthorizationGrant() != null) {\n\t\t\t\tjwtContextBuilder.authorizationGrant(context.getAuthorizationGrant());\n\t\t\t}\n\t\t\tif (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {\n\t\t\t\tSessionInformation sessionInformation = context.get(SessionInformation.class);\n\t\t\t\tif (sessionInformation != null) {\n\t\t\t\t\tjwtContextBuilder.put(SessionInformation.class, sessionInformation);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {\n\t\t\t\tJwt dPoPProofJwt = context.get(OAuth2TokenContext.DPOP_PROOF_KEY);\n\t\t\t\tif (dPoPProofJwt != null) {\n\t\t\t\t\tjwtContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProofJwt);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// @formatter:on\n\n\t\t\tJwtEncodingContext jwtContext = jwtContextBuilder.build();\n\t\t\tthis.jwtCustomizer.customize(jwtContext);\n\t\t}\n\n\t\tJwsHeader jwsHeader = jwsHeaderBuilder.build();\n\t\tJwtClaimsSet claims = claimsBuilder.build();\n\n\t\tJwt jwt = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\treturn jwt;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2TokenCustomizer} that customizes the\n\t * {@link JwtEncodingContext#getJwsHeader() JWS headers} and/or\n\t * {@link JwtEncodingContext#getClaims() claims} for the generated {@link Jwt}.\n\t * @param jwtCustomizer the {@link OAuth2TokenCustomizer} that customizes the headers\n\t * and/or claims for the generated {@code Jwt}\n\t */\n\tpublic void setJwtCustomizer(OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer) {\n\t\tAssert.notNull(jwtCustomizer, \"jwtCustomizer cannot be null\");\n\t\tthis.jwtCustomizer = jwtCustomizer;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used when obtaining the current instant via\n\t * {@link Clock#instant()}.\n\t * @param clock the {@link Clock} used when obtaining the current instant via\n\t * {@link Clock#instant()}\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link OAuth2TokenGenerator} that generates a {@link OAuth2TokenFormat#REFERENCE\n * \"reference\"} (opaque) {@link OAuth2AccessToken}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenGenerator\n * @see OAuth2AccessToken\n * @see OAuth2TokenCustomizer\n * @see OAuth2TokenClaimsContext\n * @see OAuth2TokenClaimsSet\n */\npublic final class OAuth2AccessTokenGenerator implements OAuth2TokenGenerator<OAuth2AccessToken> {\n\n\tprivate final StringKeyGenerator accessTokenGenerator = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\tprivate OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer;\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2AccessToken generate(OAuth2TokenContext context) {\n\t\t// @formatter:off\n\t\tif (!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) ||\n\t\t\t\t!OAuth2TokenFormat.REFERENCE.equals(context.getRegisteredClient().getTokenSettings().getAccessTokenFormat())) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:on\n\n\t\tString issuer = null;\n\t\tif (context.getAuthorizationServerContext() != null) {\n\t\t\tissuer = context.getAuthorizationServerContext().getIssuer();\n\t\t}\n\t\tRegisteredClient registeredClient = context.getRegisteredClient();\n\n\t\tInstant issuedAt = this.clock.instant();\n\t\tInstant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getAccessTokenTimeToLive());\n\n\t\t// @formatter:off\n\t\tOAuth2TokenClaimsSet.Builder claimsBuilder = OAuth2TokenClaimsSet.builder();\n\t\tif (StringUtils.hasText(issuer)) {\n\t\t\tclaimsBuilder.issuer(issuer);\n\t\t}\n\t\tclaimsBuilder\n\t\t\t\t.subject(context.getPrincipal().getName())\n\t\t\t\t.audience(Collections.singletonList(registeredClient.getClientId()))\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.notBefore(issuedAt)\n\t\t\t\t.id(UUID.randomUUID().toString());\n\t\tif (!CollectionUtils.isEmpty(context.getAuthorizedScopes())) {\n\t\t\tclaimsBuilder.claim(OAuth2ParameterNames.SCOPE, context.getAuthorizedScopes());\n\t\t}\n\t\t// @formatter:on\n\n\t\tif (this.accessTokenCustomizer != null) {\n\t\t\t// @formatter:off\n\t\t\tOAuth2TokenClaimsContext.Builder accessTokenContextBuilder = OAuth2TokenClaimsContext.with(claimsBuilder)\n\t\t\t\t\t.registeredClient(context.getRegisteredClient())\n\t\t\t\t\t.principal(context.getPrincipal())\n\t\t\t\t\t.authorizationServerContext(context.getAuthorizationServerContext())\n\t\t\t\t\t.authorizedScopes(context.getAuthorizedScopes())\n\t\t\t\t\t.tokenType(context.getTokenType())\n\t\t\t\t\t.authorizationGrantType(context.getAuthorizationGrantType());\n\t\t\tif (context.getAuthorization() != null) {\n\t\t\t\taccessTokenContextBuilder.authorization(context.getAuthorization());\n\t\t\t}\n\t\t\tif (context.getAuthorizationGrant() != null) {\n\t\t\t\taccessTokenContextBuilder.authorizationGrant(context.getAuthorizationGrant());\n\t\t\t}\n\t\t\tif (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {\n\t\t\t\tJwt dPoPProofJwt = context.get(OAuth2TokenContext.DPOP_PROOF_KEY);\n\t\t\t\tif (dPoPProofJwt != null) {\n\t\t\t\t\taccessTokenContextBuilder.put(OAuth2TokenContext.DPOP_PROOF_KEY, dPoPProofJwt);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// @formatter:on\n\n\t\t\tOAuth2TokenClaimsContext accessTokenContext = accessTokenContextBuilder.build();\n\t\t\tthis.accessTokenCustomizer.customize(accessTokenContext);\n\t\t}\n\n\t\tOAuth2TokenClaimsSet accessTokenClaimsSet = claimsBuilder.build();\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessTokenClaims(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tthis.accessTokenGenerator.generateKey(), accessTokenClaimsSet.getIssuedAt(),\n\t\t\t\taccessTokenClaimsSet.getExpiresAt(), context.getAuthorizedScopes(), accessTokenClaimsSet.getClaims());\n\n\t\treturn accessToken;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2TokenCustomizer} that customizes the\n\t * {@link OAuth2TokenClaimsContext#getClaims() claims} for the\n\t * {@link OAuth2AccessToken}.\n\t * @param accessTokenCustomizer the {@link OAuth2TokenCustomizer} that customizes the\n\t * claims for the {@code OAuth2AccessToken}\n\t */\n\tpublic void setAccessTokenCustomizer(OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer) {\n\t\tAssert.notNull(accessTokenCustomizer, \"accessTokenCustomizer cannot be null\");\n\t\tthis.accessTokenCustomizer = accessTokenCustomizer;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used when obtaining the current instant via\n\t * {@link Clock#instant()}.\n\t * @param clock the {@link Clock} used when obtaining the current instant via\n\t * {@link Clock#instant()}\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n\tprivate static final class OAuth2AccessTokenClaims extends OAuth2AccessToken implements ClaimAccessor {\n\n\t\tprivate final Map<String, Object> claims;\n\n\t\tprivate OAuth2AccessTokenClaims(TokenType tokenType, String tokenValue, Instant issuedAt, Instant expiresAt,\n\t\t\t\tSet<String> scopes, Map<String, Object> claims) {\n\t\t\tsuper(tokenType, tokenValue, issuedAt, expiresAt, scopes);\n\t\t\tthis.claims = claims;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, Object> getClaims() {\n\t\t\treturn this.claims;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.Base64;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2TokenGenerator} that generates an {@link OAuth2RefreshToken}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenGenerator\n * @see OAuth2RefreshToken\n */\npublic final class OAuth2RefreshTokenGenerator implements OAuth2TokenGenerator<OAuth2RefreshToken> {\n\n\tprivate final StringKeyGenerator refreshTokenGenerator = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2RefreshToken generate(OAuth2TokenContext context) {\n\t\tif (!OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())) {\n\t\t\treturn null;\n\t\t}\n\t\tif (isPublicClientForAuthorizationCodeGrant(context)) {\n\t\t\t// Do not issue refresh token to public client\n\t\t\treturn null;\n\t\t}\n\n\t\tInstant issuedAt = this.clock.instant();\n\t\tInstant expiresAt = issuedAt.plus(context.getRegisteredClient().getTokenSettings().getRefreshTokenTimeToLive());\n\t\treturn new OAuth2RefreshToken(this.refreshTokenGenerator.generateKey(), issuedAt, expiresAt);\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used when obtaining the current instant via\n\t * {@link Clock#instant()}.\n\t * @param clock the {@link Clock} used when obtaining the current instant via\n\t * {@link Clock#instant()}\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n\tprivate static boolean isPublicClientForAuthorizationCodeGrant(OAuth2TokenContext context) {\n\t\t// @formatter:off\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType()) &&\n\t\t\t\t(context.getAuthorizationGrant().getPrincipal() instanceof OAuth2ClientAuthenticationToken clientPrincipal)) {\n\t\t\treturn clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE);\n\t\t}\n\t\t// @formatter:on\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.List;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\n\n/**\n * A {@link ClaimAccessor} for the \"claims\" that may be contained in an\n * {@link OAuth2TokenClaimsSet}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see ClaimAccessor\n * @see OAuth2TokenClaimNames\n * @see OAuth2TokenClaimsSet\n */\npublic interface OAuth2TokenClaimAccessor extends ClaimAccessor {\n\n\t/**\n\t * Returns the Issuer {@code (iss)} claim which identifies the principal that issued\n\t * the OAuth 2.0 Token.\n\t * @return the Issuer identifier\n\t */\n\tdefault URL getIssuer() {\n\t\treturn getClaimAsURL(OAuth2TokenClaimNames.ISS);\n\t}\n\n\t/**\n\t * Returns the Subject {@code (sub)} claim which identifies the principal that is the\n\t * subject of the OAuth 2.0 Token.\n\t * @return the Subject identifier\n\t */\n\tdefault String getSubject() {\n\t\treturn getClaimAsString(OAuth2TokenClaimNames.SUB);\n\t}\n\n\t/**\n\t * Returns the Audience {@code (aud)} claim which identifies the recipient(s) that the\n\t * OAuth 2.0 Token is intended for.\n\t * @return the Audience(s) that this OAuth 2.0 Token is intended for\n\t */\n\tdefault List<String> getAudience() {\n\t\treturn getClaimAsStringList(OAuth2TokenClaimNames.AUD);\n\t}\n\n\t/**\n\t * Returns the Expiration time {@code (exp)} claim which identifies the expiration\n\t * time on or after which the OAuth 2.0 Token MUST NOT be accepted for processing.\n\t * @return the Expiration time on or after which the OAuth 2.0 Token MUST NOT be\n\t * accepted for processing\n\t */\n\tdefault Instant getExpiresAt() {\n\t\treturn getClaimAsInstant(OAuth2TokenClaimNames.EXP);\n\t}\n\n\t/**\n\t * Returns the Not Before {@code (nbf)} claim which identifies the time before which\n\t * the OAuth 2.0 Token MUST NOT be accepted for processing.\n\t * @return the Not Before time before which the OAuth 2.0 Token MUST NOT be accepted\n\t * for processing\n\t */\n\tdefault Instant getNotBefore() {\n\t\treturn getClaimAsInstant(OAuth2TokenClaimNames.NBF);\n\t}\n\n\t/**\n\t * Returns the Issued at {@code (iat)} claim which identifies the time at which the\n\t * OAuth 2.0 Token was issued.\n\t * @return the Issued at claim which identifies the time at which the OAuth 2.0 Token\n\t * was issued\n\t */\n\tdefault Instant getIssuedAt() {\n\t\treturn getClaimAsInstant(OAuth2TokenClaimNames.IAT);\n\t}\n\n\t/**\n\t * Returns the ID {@code (jti)} claim which provides a unique identifier for the OAuth\n\t * 2.0 Token.\n\t * @return the ID claim which provides a unique identifier for the OAuth 2.0 Token\n\t */\n\tdefault String getId() {\n\t\treturn getClaimAsString(OAuth2TokenClaimNames.JTI);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport org.springframework.security.oauth2.core.OAuth2Token;\n\n/**\n * The names of the \"claims\" that may be contained in an {@link OAuth2TokenClaimsSet} and\n * are associated to an {@link OAuth2Token}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenClaimAccessor\n * @see OAuth2TokenClaimsSet\n * @see OAuth2Token\n */\npublic final class OAuth2TokenClaimNames {\n\n\t/**\n\t * {@code iss} - the Issuer claim identifies the principal that issued the OAuth 2.0\n\t * Token\n\t */\n\tpublic static final String ISS = \"iss\";\n\n\t/**\n\t * {@code sub} - the Subject claim identifies the principal that is the subject of the\n\t * OAuth 2.0 Token\n\t */\n\tpublic static final String SUB = \"sub\";\n\n\t/**\n\t * {@code aud} - the Audience claim identifies the recipient(s) that the OAuth 2.0\n\t * Token is intended for\n\t */\n\tpublic static final String AUD = \"aud\";\n\n\t/**\n\t * {@code exp} - the Expiration time claim identifies the expiration time on or after\n\t * which the OAuth 2.0 Token MUST NOT be accepted for processing\n\t */\n\tpublic static final String EXP = \"exp\";\n\n\t/**\n\t * {@code nbf} - the Not Before claim identifies the time before which the OAuth 2.0\n\t * Token MUST NOT be accepted for processing\n\t */\n\tpublic static final String NBF = \"nbf\";\n\n\t/**\n\t * {@code iat} - The Issued at claim identifies the time at which the OAuth 2.0 Token\n\t * was issued\n\t */\n\tpublic static final String IAT = \"iat\";\n\n\t/**\n\t * {@code jti} - The ID claim provides a unique identifier for the OAuth 2.0 Token\n\t */\n\tpublic static final String JTI = \"jti\";\n\n\tprivate OAuth2TokenClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2TokenContext} implementation that provides access to the\n * {@link #getClaims() claims} of an OAuth 2.0 Token, allowing the ability to customize.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenContext\n * @see OAuth2TokenClaimsSet.Builder\n */\npublic final class OAuth2TokenClaimsContext implements OAuth2TokenContext {\n\n\tprivate final Map<Object, Object> context;\n\n\tprivate OAuth2TokenClaimsContext(Map<Object, Object> context) {\n\t\tthis.context = Collections.unmodifiableMap(new HashMap<>(context));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Nullable\n\t@Override\n\tpublic <V> V get(Object key) {\n\t\treturn hasKey(key) ? (V) this.context.get(key) : null;\n\t}\n\n\t@Override\n\tpublic boolean hasKey(Object key) {\n\t\tAssert.notNull(key, \"key cannot be null\");\n\t\treturn this.context.containsKey(key);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2TokenClaimsSet.Builder claims} allowing the ability to\n\t * add, replace, or remove.\n\t * @return the {@link OAuth2TokenClaimsSet.Builder}\n\t */\n\tpublic OAuth2TokenClaimsSet.Builder getClaims() {\n\t\treturn get(OAuth2TokenClaimsSet.Builder.class);\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with the provided claims.\n\t * @param claimsBuilder the claims to initialize the builder\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OAuth2TokenClaimsSet.Builder claimsBuilder) {\n\t\treturn new Builder(claimsBuilder);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2TokenClaimsContext}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<OAuth2TokenClaimsContext, Builder> {\n\n\t\tprivate Builder(OAuth2TokenClaimsSet.Builder claimsBuilder) {\n\t\t\tAssert.notNull(claimsBuilder, \"claimsBuilder cannot be null\");\n\t\t\tput(OAuth2TokenClaimsSet.Builder.class, claimsBuilder);\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2TokenClaimsContext}.\n\t\t * @return the {@link OAuth2TokenClaimsContext}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2TokenClaimsContext build() {\n\t\t\treturn new OAuth2TokenClaimsContext(getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSet.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.util.Assert;\n\n/**\n * A representation of a set of claims that are associated to an {@link OAuth2Token}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenClaimAccessor\n * @see OAuth2TokenClaimNames\n * @see OAuth2Token\n */\npublic final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor {\n\n\tprivate final Map<String, Object> claims;\n\n\tprivate OAuth2TokenClaimsSet(Map<String, Object> claims) {\n\t\tthis.claims = Collections.unmodifiableMap(new HashMap<>(claims));\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2TokenClaimsSet}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final Map<String, Object> claims = new HashMap<>();\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the issuer {@code (iss)} claim, which identifies the principal that issued\n\t\t * the OAuth 2.0 Token.\n\t\t * @param issuer the issuer identifier\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder issuer(String issuer) {\n\t\t\treturn claim(OAuth2TokenClaimNames.ISS, issuer);\n\t\t}\n\n\t\t/**\n\t\t * Sets the subject {@code (sub)} claim, which identifies the principal that is\n\t\t * the subject of the OAuth 2.0 Token.\n\t\t * @param subject the subject identifier\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder subject(String subject) {\n\t\t\treturn claim(OAuth2TokenClaimNames.SUB, subject);\n\t\t}\n\n\t\t/**\n\t\t * Sets the audience {@code (aud)} claim, which identifies the recipient(s) that\n\t\t * the OAuth 2.0 Token is intended for.\n\t\t * @param audience the audience that this OAuth 2.0 Token is intended for\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder audience(List<String> audience) {\n\t\t\treturn claim(OAuth2TokenClaimNames.AUD, audience);\n\t\t}\n\n\t\t/**\n\t\t * Sets the expiration time {@code (exp)} claim, which identifies the time on or\n\t\t * after which the OAuth 2.0 Token MUST NOT be accepted for processing.\n\t\t * @param expiresAt the time on or after which the OAuth 2.0 Token MUST NOT be\n\t\t * accepted for processing\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder expiresAt(Instant expiresAt) {\n\t\t\treturn claim(OAuth2TokenClaimNames.EXP, expiresAt);\n\t\t}\n\n\t\t/**\n\t\t * Sets the not before {@code (nbf)} claim, which identifies the time before which\n\t\t * the OAuth 2.0 Token MUST NOT be accepted for processing.\n\t\t * @param notBefore the time before which the OAuth 2.0 Token MUST NOT be accepted\n\t\t * for processing\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder notBefore(Instant notBefore) {\n\t\t\treturn claim(OAuth2TokenClaimNames.NBF, notBefore);\n\t\t}\n\n\t\t/**\n\t\t * Sets the issued at {@code (iat)} claim, which identifies the time at which the\n\t\t * OAuth 2.0 Token was issued.\n\t\t * @param issuedAt the time at which the OAuth 2.0 Token was issued\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder issuedAt(Instant issuedAt) {\n\t\t\treturn claim(OAuth2TokenClaimNames.IAT, issuedAt);\n\t\t}\n\n\t\t/**\n\t\t * Sets the ID {@code (jti)} claim, which provides a unique identifier for the\n\t\t * OAuth 2.0 Token.\n\t\t * @param jti the unique identifier for the OAuth 2.0 Token\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder id(String jti) {\n\t\t\treturn claim(OAuth2TokenClaimNames.JTI, jti);\n\t\t}\n\n\t\t/**\n\t\t * Sets the claim.\n\t\t * @param name the claim name\n\t\t * @param value the claim value\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder claim(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} to be provided access to the claims allowing the ability to\n\t\t * add, replace, or remove.\n\t\t * @param claimsConsumer a {@code Consumer} of the claims\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2TokenClaimsSet}.\n\t\t * @return a {@link OAuth2TokenClaimsSet}\n\t\t */\n\t\tpublic OAuth2TokenClaimsSet build() {\n\t\t\tAssert.notEmpty(this.claims, \"claims cannot be empty\");\n\n\t\t\t// The value of the 'iss' claim is a String or URL (StringOrURI).\n\t\t\t// Attempt to convert to URL.\n\t\t\tObject issuer = this.claims.get(OAuth2TokenClaimNames.ISS);\n\t\t\tif (issuer != null) {\n\t\t\t\tURL convertedValue = ClaimConversionService.getSharedInstance().convert(issuer, URL.class);\n\t\t\t\tif (convertedValue != null) {\n\t\t\t\t\tthis.claims.put(OAuth2TokenClaimNames.ISS, convertedValue);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn new OAuth2TokenClaimsSet(this.claims);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.Context;\nimport org.springframework.util.Assert;\n\n/**\n * A context that holds information (to be) associated to an OAuth 2.0 Token and is used\n * by an {@link OAuth2TokenGenerator} and {@link OAuth2TokenCustomizer}.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see Context\n * @see OAuth2TokenGenerator\n * @see OAuth2TokenCustomizer\n */\npublic interface OAuth2TokenContext extends Context {\n\n\t/**\n\t * The key used for the DPoP Proof {@link Jwt} (if available).\n\t */\n\tString DPOP_PROOF_KEY = Jwt.class.getName().concat(\".DPOP_PROOF\");\n\n\t/**\n\t * Returns the {@link RegisteredClient registered client}.\n\t * @return the {@link RegisteredClient}\n\t */\n\tdefault RegisteredClient getRegisteredClient() {\n\t\treturn get(RegisteredClient.class);\n\t}\n\n\t/**\n\t * Returns the {@link Authentication} representing the {@code Principal} resource\n\t * owner (or client).\n\t * @param <T> the type of the {@code Authentication}\n\t * @return the {@link Authentication} representing the {@code Principal} resource\n\t * owner (or client)\n\t */\n\tdefault <T extends Authentication> T getPrincipal() {\n\t\treturn get(AbstractBuilder.PRINCIPAL_AUTHENTICATION_KEY);\n\t}\n\n\t/**\n\t * Returns the {@link AuthorizationServerContext authorization server context}.\n\t * @return the {@link AuthorizationServerContext}\n\t */\n\tdefault AuthorizationServerContext getAuthorizationServerContext() {\n\t\treturn get(AuthorizationServerContext.class);\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Authorization authorization}.\n\t * @return the {@link OAuth2Authorization}, or {@code null} if not available\n\t */\n\t@Nullable\n\tdefault OAuth2Authorization getAuthorization() {\n\t\treturn get(OAuth2Authorization.class);\n\t}\n\n\t/**\n\t * Returns the authorized scope(s).\n\t * @return the authorized scope(s)\n\t */\n\tdefault Set<String> getAuthorizedScopes() {\n\t\treturn hasKey(AbstractBuilder.AUTHORIZED_SCOPE_KEY) ? get(AbstractBuilder.AUTHORIZED_SCOPE_KEY)\n\t\t\t\t: Collections.emptySet();\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2TokenType token type}.\n\t * @return the {@link OAuth2TokenType}\n\t */\n\tdefault OAuth2TokenType getTokenType() {\n\t\treturn get(OAuth2TokenType.class);\n\t}\n\n\t/**\n\t * Returns the {@link AuthorizationGrantType authorization grant type}.\n\t * @return the {@link AuthorizationGrantType}\n\t */\n\tdefault AuthorizationGrantType getAuthorizationGrantType() {\n\t\treturn get(AuthorizationGrantType.class);\n\t}\n\n\t/**\n\t * Returns the {@link Authentication} representing the authorization grant.\n\t * @param <T> the type of the {@code Authentication}\n\t * @return the {@link Authentication} representing the authorization grant\n\t */\n\tdefault <T extends Authentication> T getAuthorizationGrant() {\n\t\treturn get(AbstractBuilder.AUTHORIZATION_GRANT_AUTHENTICATION_KEY);\n\t}\n\n\t/**\n\t * Base builder for implementations of {@link OAuth2TokenContext}.\n\t *\n\t * @param <T> the type of the context\n\t * @param <B> the type of the builder\n\t */\n\tabstract class AbstractBuilder<T extends OAuth2TokenContext, B extends AbstractBuilder<T, B>> {\n\n\t\tprivate static final String PRINCIPAL_AUTHENTICATION_KEY = Authentication.class.getName().concat(\".PRINCIPAL\");\n\n\t\tprivate static final String AUTHORIZED_SCOPE_KEY = OAuth2Authorization.class.getName()\n\t\t\t.concat(\".AUTHORIZED_SCOPE\");\n\n\t\tprivate static final String AUTHORIZATION_GRANT_AUTHENTICATION_KEY = Authentication.class.getName()\n\t\t\t.concat(\".AUTHORIZATION_GRANT\");\n\n\t\tprivate final Map<Object, Object> context = new HashMap<>();\n\n\t\t/**\n\t\t * Sets the {@link RegisteredClient registered client}.\n\t\t * @param registeredClient the {@link RegisteredClient}\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B registeredClient(RegisteredClient registeredClient) {\n\t\t\treturn put(RegisteredClient.class, registeredClient);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link Authentication} representing the {@code Principal} resource\n\t\t * owner (or client).\n\t\t * @param principal the {@link Authentication} representing the {@code Principal}\n\t\t * resource owner (or client)\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B principal(Authentication principal) {\n\t\t\treturn put(PRINCIPAL_AUTHENTICATION_KEY, principal);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link AuthorizationServerContext authorization server context}.\n\t\t * @param authorizationServerContext the {@link AuthorizationServerContext}\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B authorizationServerContext(AuthorizationServerContext authorizationServerContext) {\n\t\t\treturn put(AuthorizationServerContext.class, authorizationServerContext);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2Authorization authorization}.\n\t\t * @param authorization the {@link OAuth2Authorization}\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B authorization(OAuth2Authorization authorization) {\n\t\t\treturn put(OAuth2Authorization.class, authorization);\n\t\t}\n\n\t\t/**\n\t\t * Sets the authorized scope(s).\n\t\t * @param authorizedScopes the authorized scope(s)\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B authorizedScopes(Set<String> authorizedScopes) {\n\t\t\treturn put(AUTHORIZED_SCOPE_KEY, authorizedScopes);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2TokenType token type}.\n\t\t * @param tokenType the {@link OAuth2TokenType}\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B tokenType(OAuth2TokenType tokenType) {\n\t\t\treturn put(OAuth2TokenType.class, tokenType);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link AuthorizationGrantType authorization grant type}.\n\t\t * @param authorizationGrantType the {@link AuthorizationGrantType}\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B authorizationGrantType(AuthorizationGrantType authorizationGrantType) {\n\t\t\treturn put(AuthorizationGrantType.class, authorizationGrantType);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link Authentication} representing the authorization grant.\n\t\t * @param authorizationGrant the {@link Authentication} representing the\n\t\t * authorization grant\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B authorizationGrant(Authentication authorizationGrant) {\n\t\t\treturn put(AUTHORIZATION_GRANT_AUTHENTICATION_KEY, authorizationGrant);\n\t\t}\n\n\t\t/**\n\t\t * Associates an attribute.\n\t\t * @param key the key for the attribute\n\t\t * @param value the value of the attribute\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B put(Object key, Object value) {\n\t\t\tAssert.notNull(key, \"key cannot be null\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.context.put(key, value);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the attributes {@code Map} allowing the ability to add,\n\t\t * replace, or remove.\n\t\t * @param contextConsumer a {@link Consumer} of the attributes {@code Map}\n\t\t * @return the {@link AbstractBuilder} for further configuration\n\t\t */\n\t\tpublic B context(Consumer<Map<Object, Object>> contextConsumer) {\n\t\t\tcontextConsumer.accept(this.context);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected <V> V get(Object key) {\n\t\t\treturn (V) this.context.get(key);\n\t\t}\n\n\t\tprotected Map<Object, Object> getContext() {\n\t\t\treturn this.context;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected final B getThis() {\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2TokenContext}.\n\t\t * @return the {@link OAuth2TokenContext}\n\t\t */\n\t\tpublic abstract T build();\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenCustomizer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\n/**\n * Implementations of this interface are responsible for customizing the OAuth 2.0 Token\n * attributes contained within the {@link OAuth2TokenContext}.\n *\n * @param <T> the type of the context containing the OAuth 2.0 Token attributes\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2TokenContext\n */\n@FunctionalInterface\npublic interface OAuth2TokenCustomizer<T extends OAuth2TokenContext> {\n\n\t/**\n\t * Customize the OAuth 2.0 Token attributes.\n\t * @param context the context containing the OAuth 2.0 Token attributes\n\t */\n\tvoid customize(T context);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenGenerator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\n\n/**\n * Implementations of this interface are responsible for generating an {@link OAuth2Token}\n * using the attributes contained in the {@link OAuth2TokenContext}.\n *\n * @param <T> the type of the OAuth 2.0 Token\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2Token\n * @see OAuth2TokenContext\n * @see OAuth2TokenClaimsSet\n * @see ClaimAccessor\n */\n@FunctionalInterface\npublic interface OAuth2TokenGenerator<T extends OAuth2Token> {\n\n\t/**\n\t * Generate an OAuth 2.0 Token using the attributes contained in the\n\t * {@link OAuth2TokenContext}, or return {@code null} if the\n\t * {@link OAuth2TokenContext#getTokenType()} is not supported.\n\t *\n\t * <p>\n\t * If the returned {@link OAuth2Token} has a set of claims, it should implement\n\t * {@link ClaimAccessor} in order for it to be stored with the\n\t * {@link OAuth2Authorization}.\n\t * @param context the context containing the OAuth 2.0 Token attributes\n\t * @return an {@link OAuth2Token} or {@code null} if the\n\t * {@link OAuth2TokenContext#getTokenType()} is not supported\n\t */\n\t@Nullable\n\tT generate(OAuth2TokenContext context);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/DefaultConsentPage.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\n\n/**\n * For internal use only.\n *\n * @author Joe Grandja\n */\nfinal class DefaultConsentPage {\n\n\tprivate static final MediaType TEXT_HTML_UTF8 = new MediaType(\"text\", \"html\", StandardCharsets.UTF_8);\n\n\tprivate DefaultConsentPage() {\n\t}\n\n\tstatic void displayConsent(HttpServletRequest request, HttpServletResponse response, String clientId,\n\t\t\tAuthentication principal, Set<String> requestedScopes, Set<String> authorizedScopes, String state,\n\t\t\tMap<String, String> additionalParameters) throws IOException {\n\n\t\tString consentPage = generateConsentPage(request, clientId, principal, requestedScopes, authorizedScopes, state,\n\t\t\t\tadditionalParameters);\n\t\tresponse.setContentType(TEXT_HTML_UTF8.toString());\n\t\tresponse.setContentLength(consentPage.getBytes(StandardCharsets.UTF_8).length);\n\t\tresponse.getWriter().write(consentPage);\n\t}\n\n\tprivate static String generateConsentPage(HttpServletRequest request, String clientId, Authentication principal,\n\t\t\tSet<String> requestedScopes, Set<String> authorizedScopes, String state,\n\t\t\tMap<String, String> additionalParameters) {\n\t\tSet<String> scopesToAuthorize = new HashSet<>();\n\t\tSet<String> scopesPreviouslyAuthorized = new HashSet<>();\n\t\tfor (String scope : requestedScopes) {\n\t\t\tif (authorizedScopes.contains(scope)) {\n\t\t\t\tscopesPreviouslyAuthorized.add(scope);\n\t\t\t}\n\t\t\telse if (!scope.equals(OidcScopes.OPENID)) {\n\t\t\t\t// openid scope does not require consent\n\t\t\t\tscopesToAuthorize.add(scope);\n\t\t\t}\n\t\t}\n\n\t\t// https://datatracker.ietf.org/doc/html/rfc8628#section-3.3.1\n\t\t// The server SHOULD display\n\t\t// the \"user_code\" to the user and ask them to verify that it matches\n\t\t// the \"user_code\" being displayed on the device to confirm they are\n\t\t// authorizing the correct device.\n\t\tString userCode = additionalParameters.get(OAuth2ParameterNames.USER_CODE);\n\n\t\t// @formatter:off\n\t\tStringBuilder builder = new StringBuilder();\n\t\tbuilder.append(\"<!DOCTYPE html>\");\n\t\tbuilder.append(\"<html lang=\\\"en\\\">\");\n\t\tbuilder.append(\"<head>\");\n\t\tbuilder.append(\"    <meta charset=\\\"utf-8\\\">\");\n\t\tbuilder.append(\"    <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1, shrink-to-fit=no\\\">\");\n\t\tbuilder.append(\"    <link rel=\\\"stylesheet\\\" href=\\\"https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css\\\" integrity=\\\"sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z\\\" crossorigin=\\\"anonymous\\\">\");\n\t\tbuilder.append(\"    <title>Consent required</title>\");\n\t\tbuilder.append(\"\t<script>\");\n\t\tbuilder.append(\"\t\tfunction cancelConsent() {\");\n\t\tbuilder.append(\"\t\t\tdocument.consent_form.reset();\");\n\t\tbuilder.append(\"\t\t\tdocument.consent_form.submit();\");\n\t\tbuilder.append(\"\t\t}\");\n\t\tbuilder.append(\"\t</script>\");\n\t\tbuilder.append(\"</head>\");\n\t\tbuilder.append(\"<body>\");\n\t\tbuilder.append(\"<div class=\\\"container\\\">\");\n\t\tbuilder.append(\"    <div class=\\\"py-5\\\">\");\n\t\tbuilder.append(\"        <h1 class=\\\"text-center\\\">Consent required</h1>\");\n\t\tbuilder.append(\"    </div>\");\n\t\tbuilder.append(\"    <div class=\\\"row\\\">\");\n\t\tbuilder.append(\"        <div class=\\\"col text-center\\\">\");\n\t\tbuilder.append(\"            <p><span class=\\\"font-weight-bold text-primary\\\">\" + clientId + \"</span> wants to access your account <span class=\\\"font-weight-bold\\\">\" + principal.getName() + \"</span></p>\");\n\t\tbuilder.append(\"        </div>\");\n\t\tbuilder.append(\"    </div>\");\n\t\tif (userCode != null) {\n\t\t\tbuilder.append(\"    <div class=\\\"row\\\">\");\n\t\t\tbuilder.append(\"        <div class=\\\"col text-center\\\">\");\n\t\t\tbuilder.append(\"            <p class=\\\"alert alert-warning\\\">You have provided the code <span class=\\\"font-weight-bold\\\">\" + userCode + \"</span>. Verify that this code matches what is shown on your device.</p>\");\n\t\t\tbuilder.append(\"        </div>\");\n\t\t\tbuilder.append(\"    </div>\");\n\t\t}\n\t\tbuilder.append(\"    <div class=\\\"row pb-3\\\">\");\n\t\tbuilder.append(\"        <div class=\\\"col text-center\\\">\");\n\t\tbuilder.append(\"            <p>The following permissions are requested by the above app.<br/>Please review these and consent if you approve.</p>\");\n\t\tbuilder.append(\"        </div>\");\n\t\tbuilder.append(\"    </div>\");\n\t\tbuilder.append(\"    <div class=\\\"row\\\">\");\n\t\tbuilder.append(\"        <div class=\\\"col text-center\\\">\");\n\t\tbuilder.append(\"            <form name=\\\"consent_form\\\" method=\\\"post\\\" action=\\\"\" + request.getRequestURI() + \"\\\">\");\n\t\tbuilder.append(\"                <input type=\\\"hidden\\\" name=\\\"client_id\\\" value=\\\"\" + clientId + \"\\\">\");\n\t\tbuilder.append(\"                <input type=\\\"hidden\\\" name=\\\"state\\\" value=\\\"\" + state + \"\\\">\");\n\t\tif (userCode != null) {\n\t\t\tbuilder.append(\"                <input type=\\\"hidden\\\" name=\\\"user_code\\\" value=\\\"\" + userCode + \"\\\">\");\n\t\t}\n\n\t\tfor (String scope : scopesToAuthorize) {\n\t\t\tbuilder.append(\"                <div class=\\\"form-group form-check py-1\\\">\");\n\t\t\tbuilder.append(\"                    <input class=\\\"form-check-input\\\" type=\\\"checkbox\\\" name=\\\"scope\\\" value=\\\"\" + scope + \"\\\" id=\\\"\" + scope + \"\\\">\");\n\t\t\tbuilder.append(\"                    <label class=\\\"form-check-label\\\" for=\\\"\" + scope + \"\\\">\" + scope + \"</label>\");\n\t\t\tbuilder.append(\"                </div>\");\n\t\t}\n\n\t\tif (!scopesPreviouslyAuthorized.isEmpty()) {\n\t\t\tbuilder.append(\"                <p>You have already granted the following permissions to the above app:</p>\");\n\t\t\tfor (String scope : scopesPreviouslyAuthorized) {\n\t\t\t\tbuilder.append(\"                <div class=\\\"form-group form-check py-1\\\">\");\n\t\t\t\tbuilder.append(\"                    <input class=\\\"form-check-input\\\" type=\\\"checkbox\\\" name=\\\"scope\\\" id=\\\"\" + scope + \"\\\" checked disabled>\");\n\t\t\t\tbuilder.append(\"                    <label class=\\\"form-check-label\\\" for=\\\"\" + scope + \"\\\">\" + scope + \"</label>\");\n\t\t\t\tbuilder.append(\"                </div>\");\n\t\t\t}\n\t\t}\n\n\t\tbuilder.append(\"                <div class=\\\"form-group pt-3\\\">\");\n\t\tbuilder.append(\"                    <button class=\\\"btn btn-primary btn-lg\\\" type=\\\"submit\\\" id=\\\"submit-consent\\\">Submit Consent</button>\");\n\t\tbuilder.append(\"                </div>\");\n\t\tbuilder.append(\"                <div class=\\\"form-group\\\">\");\n\t\tbuilder.append(\"                    <button class=\\\"btn btn-link regular\\\" type=\\\"button\\\" onclick=\\\"cancelConsent();\\\" id=\\\"cancel-consent\\\">Cancel</button>\");\n\t\tbuilder.append(\"                </div>\");\n\t\tbuilder.append(\"            </form>\");\n\t\tbuilder.append(\"        </div>\");\n\t\tbuilder.append(\"    </div>\");\n\t\tbuilder.append(\"    <div class=\\\"row pt-4\\\">\");\n\t\tbuilder.append(\"        <div class=\\\"col text-center\\\">\");\n\t\tbuilder.append(\"            <p><small>Your consent to provide access is required.<br/>If you do not approve, click Cancel, in which case no information will be shared with the app.</small></p>\");\n\t\tbuilder.append(\"        </div>\");\n\t\tbuilder.append(\"    </div>\");\n\t\tbuilder.append(\"</div>\");\n\t\tbuilder.append(\"</body>\");\n\t\tbuilder.append(\"</html>\");\n\t\t// @formatter:on\n\n\t\treturn builder.toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/GenericHttpMessageConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\n\n/**\n * {@link GenericHttpMessageConverter} implementation that delegates to a\n * {@link SmartHttpMessageConverter}.\n *\n * @param <T> the converted object type\n * @author Sebastien Deleuze\n * @since 7.0\n */\nfinal class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {\n\n\tprivate final SmartHttpMessageConverter<T> smartConverter;\n\n\tGenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {\n\t\tthis.smartConverter = smartConverter;\n\t}\n\n\t@Override\n\tpublic boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forType(type), mediaType);\n\t}\n\n\t@Override\n\tpublic T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\treturn this.smartConverter.getSupportedMediaTypes();\n\t}\n\n\t@Override\n\tpublic T read(Class<? extends T> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(clazz, inputMessage);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, contentType, outputMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/HttpMessageConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.GsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JsonbHttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Utility methods for {@link HttpMessageConverter}'s.\n *\n * @author Joe Grandja\n * @since 7.0\n */\nfinal class HttpMessageConverters {\n\n\tprivate static final boolean jacksonPresent;\n\n\tprivate static final boolean jackson2Present;\n\n\tprivate static final boolean gsonPresent;\n\n\tprivate static final boolean jsonbPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = HttpMessageConverters.class.getClassLoader();\n\t\tjacksonPresent = ClassUtils.isPresent(\"tools.jackson.databind.json.JsonMapper\", classLoader);\n\t\tjackson2Present = ClassUtils.isPresent(\"com.fasterxml.jackson.databind.ObjectMapper\", classLoader)\n\t\t\t\t&& ClassUtils.isPresent(\"com.fasterxml.jackson.core.JsonGenerator\", classLoader);\n\t\tgsonPresent = ClassUtils.isPresent(\"com.google.gson.Gson\", classLoader);\n\t\tjsonbPresent = ClassUtils.isPresent(\"jakarta.json.bind.Jsonb\", classLoader);\n\t}\n\n\tprivate HttpMessageConverters() {\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tstatic GenericHttpMessageConverter<Object> getJsonMessageConverter() {\n\t\tif (jacksonPresent) {\n\t\t\treturn new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());\n\t\t}\n\t\tif (jackson2Present) {\n\t\t\treturn new MappingJackson2HttpMessageConverter();\n\t\t}\n\t\tif (gsonPresent) {\n\t\t\treturn new GsonHttpMessageConverter();\n\t\t}\n\t\tif (jsonbPresent) {\n\t\t\treturn new JsonbHttpMessageConverter();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.io.Writer;\n\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} that processes JWK Set requests.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see com.nimbusds.jose.jwk.source.JWKSource\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7517\">JSON Web Key\n * (JWK)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7517#section-5\">Section 5\n * JWK Set Format</a>\n */\npublic final class NimbusJwkSetEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for JWK Set requests.\n\t */\n\tprivate static final String DEFAULT_JWK_SET_ENDPOINT_URI = \"/oauth2/jwks\";\n\n\tprivate final JWKSource<SecurityContext> jwkSource;\n\n\tprivate final JWKSelector jwkSelector;\n\n\tprivate final RequestMatcher requestMatcher;\n\n\t/**\n\t * Constructs a {@code NimbusJwkSetEndpointFilter} using the provided parameters.\n\t * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource}\n\t */\n\tpublic NimbusJwkSetEndpointFilter(JWKSource<SecurityContext> jwkSource) {\n\t\tthis(jwkSource, DEFAULT_JWK_SET_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs a {@code NimbusJwkSetEndpointFilter} using the provided parameters.\n\t * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource}\n\t * @param jwkSetEndpointUri the endpoint {@code URI} for JWK Set requests\n\t */\n\tpublic NimbusJwkSetEndpointFilter(JWKSource<SecurityContext> jwkSource, String jwkSetEndpointUri) {\n\t\tAssert.notNull(jwkSource, \"jwkSource cannot be null\");\n\t\tAssert.hasText(jwkSetEndpointUri, \"jwkSetEndpointUri cannot be empty\");\n\t\tthis.jwkSource = jwkSource;\n\t\tthis.jwkSelector = new JWKSelector(new JWKMatcher.Builder().build());\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, jwkSetEndpointUri);\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tJWKSet jwkSet;\n\t\ttry {\n\t\t\tjwkSet = new JWKSet(this.jwkSource.get(this.jwkSelector, null));\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(\"Failed to select the JWK(s) -> \" + ex.getMessage(), ex);\n\t\t}\n\n\t\tresponse.setContentType(MediaType.APPLICATION_JSON_VALUE);\n\t\ttry (Writer writer = response.getWriter()) {\n\t\t\twriter.write(jwkSet.toString()); // toString() excludes private keys\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationConsentAuthenticationConverter;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.RedirectUrlBuilder;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.NegatedRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Authorization Code Grant, which handles the\n * processing of the OAuth 2.0 Authorization Request and Consent.\n *\n * @author Joe Grandja\n * @author Paurav Munshi\n * @author Daniel Garnier-Moiroux\n * @author Anoop Garlapati\n * @author Dmitriy Dubson\n * @since 7.0\n * @see AuthenticationManager\n * @see OAuth2AuthorizationCodeRequestAuthenticationProvider\n * @see OAuth2AuthorizationConsentAuthenticationProvider\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1\">Section 4.1 Authorization\n * Code Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1\">Section 4.1.1\n * Authorization Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2\">Section 4.1.2\n * Authorization Response</a>\n */\npublic final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for authorization requests.\n\t */\n\tprivate static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = \"/oauth2/authorize\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher authorizationEndpointMatcher;\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::sendAuthorizationResponse;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;\n\n\tprivate SessionAuthenticationStrategy sessionAuthenticationStrategy = (authentication, request, response) -> {\n\t};\n\n\tprivate String consentPage;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2AuthorizationEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_AUTHORIZATION_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param authorizationEndpointUri the endpoint {@code URI} for authorization requests\n\t */\n\tpublic OAuth2AuthorizationEndpointFilter(AuthenticationManager authenticationManager,\n\t\t\tString authorizationEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(authorizationEndpointUri, \"authorizationEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.authorizationEndpointMatcher = createDefaultRequestMatcher(authorizationEndpointUri);\n\t\t// @formatter:off\n\t\tthis.authenticationConverter = new DelegatingAuthenticationConverter(\n\t\t\t\tArrays.asList(\n\t\t\t\t\t\tnew OAuth2AuthorizationCodeRequestAuthenticationConverter(),\n\t\t\t\t\t\tnew OAuth2AuthorizationConsentAuthenticationConverter()));\n\t\t// @formatter:on\n\t}\n\n\tprivate static RequestMatcher createDefaultRequestMatcher(String authorizationEndpointUri) {\n\t\tRequestMatcher authorizationRequestGetMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, authorizationEndpointUri);\n\t\tRequestMatcher authorizationRequestPostMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, authorizationEndpointUri);\n\t\tRequestMatcher authorizationConsentMatcher = createAuthorizationConsentMatcher(authorizationEndpointUri);\n\t\tRequestMatcher authorizationRequestMatcher = new OrRequestMatcher(authorizationRequestGetMatcher,\n\t\t\t\tnew AndRequestMatcher(authorizationRequestPostMatcher,\n\t\t\t\t\t\tnew NegatedRequestMatcher(authorizationConsentMatcher)));\n\t\treturn new OrRequestMatcher(authorizationRequestMatcher, authorizationConsentMatcher);\n\t}\n\n\tprivate static RequestMatcher createAuthorizationConsentMatcher(String authorizationEndpointUri) {\n\t\tfinal RequestMatcher authorizationConsentPostMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, authorizationEndpointUri);\n\t\treturn (request) -> authorizationConsentPostMatcher.matches(request)\n\t\t\t\t&& request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) == null\n\t\t\t\t&& request.getParameter(OAuth2ParameterNames.REQUEST_URI) == null\n\t\t\t\t&& request.getParameter(OAuth2ParameterNames.REDIRECT_URI) == null\n\t\t\t\t&& request.getParameter(PkceParameterNames.CODE_CHALLENGE) == null\n\t\t\t\t&& request.getParameter(PkceParameterNames.CODE_CHALLENGE_METHOD) == null;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.authorizationEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\t// Get the pre-validated authorization code request (if available),\n\t\t\t// which was set by OAuth2AuthorizationCodeRequestValidatingFilter\n\t\t\tAuthentication authentication = (Authentication) request\n\t\t\t\t.getAttribute(OAuth2AuthorizationCodeRequestAuthenticationToken.class.getName());\n\t\t\tif (authentication == null) {\n\t\t\t\tauthentication = this.authenticationConverter.convert(request);\n\t\t\t\tif (authentication instanceof AbstractAuthenticationToken authenticationToken) {\n\t\t\t\t\tauthenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\t\t}\n\t\t\t}\n\t\t\tAuthentication authenticationResult = this.authenticationManager.authenticate(authentication);\n\n\t\t\tif (authenticationResult instanceof OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationToken) {\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(\"Authorization consent is required\");\n\t\t\t\t}\n\t\t\t\tsendAuthorizationConsent(request, response,\n\t\t\t\t\t\t(OAuth2AuthorizationCodeRequestAuthenticationToken) authentication,\n\t\t\t\t\t\tauthorizationConsentAuthenticationToken);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.sessionAuthenticationStrategy.onAuthentication(authenticationResult, request, response);\n\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult);\n\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Authorization request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationDetailsSource} used for building an authentication\n\t * details instance from {@link HttpServletRequest}.\n\t * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for\n\t * building an authentication details instance from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"authenticationDetailsSource cannot be null\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract an\n\t * Authorization Request (or Consent) from {@link HttpServletRequest} to an instance\n\t * of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or\n\t * {@link OAuth2AuthorizationConsentAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract an Authorization Request (or Consent) from\n\t * {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and returning the\n\t * {@link OAuth2AuthorizationResponse Authorization Response}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationException} and returning the\n\t * {@link OAuth2Error Error Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\t/**\n\t * Sets the {@link SessionAuthenticationStrategy} used for handling an\n\t * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} before calling the\n\t * {@link AuthenticationSuccessHandler}. If OpenID Connect is enabled, the default\n\t * implementation tracks OpenID Connect sessions using a {@link SessionRegistry}.\n\t * @param sessionAuthenticationStrategy the {@link SessionAuthenticationStrategy} used\n\t * for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}\n\t */\n\tpublic void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthenticationStrategy) {\n\t\tAssert.notNull(sessionAuthenticationStrategy, \"sessionAuthenticationStrategy cannot be null\");\n\t\tthis.sessionAuthenticationStrategy = sessionAuthenticationStrategy;\n\t}\n\n\t/**\n\t * Specify the URI to redirect Resource Owners to if consent is required. A default\n\t * consent page will be generated when this attribute is not specified.\n\t * @param consentPage the URI of the custom consent page to redirect to if consent is\n\t * required (e.g. \"/oauth2/consent\")\n\t */\n\tpublic void setConsentPage(String consentPage) {\n\t\tthis.consentPage = consentPage;\n\t}\n\n\tprivate void sendAuthorizationConsent(HttpServletRequest request, HttpServletResponse response,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,\n\t\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication) throws IOException {\n\n\t\tString clientId = authorizationConsentAuthentication.getClientId();\n\t\tAuthentication principal = (Authentication) authorizationConsentAuthentication.getPrincipal();\n\t\tSet<String> authorizedScopes = authorizationConsentAuthentication.getScopes();\n\t\tString state = authorizationConsentAuthentication.getState();\n\n\t\tSet<String> requestedScopes;\n\t\tString requestUri = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()\n\t\t\t.get(OAuth2ParameterNames.REQUEST_URI);\n\t\tif (StringUtils.hasText(requestUri)) {\n\t\t\trequestedScopes = (Set<String>) authorizationConsentAuthentication.getAdditionalParameters()\n\t\t\t\t.get(OAuth2ParameterNames.SCOPE);\n\t\t}\n\t\telse {\n\t\t\trequestedScopes = authorizationCodeRequestAuthentication.getScopes();\n\t\t}\n\n\t\tif (hasConsentUri()) {\n\t\t\tString redirectUri = UriComponentsBuilder.fromUriString(resolveConsentUri(request))\n\t\t\t\t.queryParam(OAuth2ParameterNames.SCOPE, String.join(\" \", requestedScopes))\n\t\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, clientId)\n\t\t\t\t.queryParam(OAuth2ParameterNames.STATE, state)\n\t\t\t\t.toUriString();\n\t\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUri);\n\t\t}\n\t\telse {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Displaying generated consent screen\");\n\t\t\t}\n\t\t\tDefaultConsentPage.displayConsent(request, response, clientId, principal, requestedScopes, authorizedScopes,\n\t\t\t\t\tstate, Collections.emptyMap());\n\t\t}\n\t}\n\n\tprivate boolean hasConsentUri() {\n\t\treturn StringUtils.hasText(this.consentPage);\n\t}\n\n\tprivate String resolveConsentUri(HttpServletRequest request) {\n\t\tif (UrlUtils.isAbsoluteUrl(this.consentPage)) {\n\t\t\treturn this.consentPage;\n\t\t}\n\t\tRedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();\n\t\turlBuilder.setScheme(request.getScheme());\n\t\turlBuilder.setServerName(request.getServerName());\n\t\turlBuilder.setPort(request.getServerPort());\n\t\turlBuilder.setContextPath(request.getContextPath());\n\t\turlBuilder.setPathInfo(this.consentPage);\n\t\treturn urlBuilder.getUrl();\n\t}\n\n\tprivate void sendAuthorizationResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication;\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder\n\t\t\t.fromUriString(authorizationCodeRequestAuthentication.getRedirectUri())\n\t\t\t.queryParam(OAuth2ParameterNames.CODE,\n\t\t\t\t\tauthorizationCodeRequestAuthentication.getAuthorizationCode().getTokenValue());\n\t\tif (StringUtils.hasText(authorizationCodeRequestAuthentication.getState())) {\n\t\t\turiBuilder.queryParam(OAuth2ParameterNames.STATE,\n\t\t\t\t\tUriUtils.encode(authorizationCodeRequestAuthentication.getState(), StandardCharsets.UTF_8));\n\t\t}\n\t\t// build(true) -> Components are explicitly encoded\n\t\tString redirectUri = uriBuilder.build(true).toUriString();\n\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUri);\n\t}\n\n\tprivate void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException {\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationException authorizationCodeRequestAuthenticationException = (OAuth2AuthorizationCodeRequestAuthenticationException) exception;\n\t\tOAuth2Error error = authorizationCodeRequestAuthenticationException.getError();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authorizationCodeRequestAuthenticationException\n\t\t\t.getAuthorizationCodeRequestAuthentication();\n\n\t\tif (authorizationCodeRequestAuthentication == null\n\t\t\t\t|| !StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())) {\n\t\t\tresponse.sendError(HttpStatus.BAD_REQUEST.value(), error.toString());\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Redirecting to client with error\");\n\t\t}\n\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder\n\t\t\t.fromUriString(authorizationCodeRequestAuthentication.getRedirectUri())\n\t\t\t.queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode());\n\t\tif (StringUtils.hasText(error.getDescription())) {\n\t\t\turiBuilder.queryParam(OAuth2ParameterNames.ERROR_DESCRIPTION,\n\t\t\t\t\tUriUtils.encode(error.getDescription(), StandardCharsets.UTF_8));\n\t\t}\n\t\tif (StringUtils.hasText(error.getUri())) {\n\t\t\turiBuilder.queryParam(OAuth2ParameterNames.ERROR_URI,\n\t\t\t\t\tUriUtils.encode(error.getUri(), StandardCharsets.UTF_8));\n\t\t}\n\t\tif (StringUtils.hasText(authorizationCodeRequestAuthentication.getState())) {\n\t\t\turiBuilder.queryParam(OAuth2ParameterNames.STATE,\n\t\t\t\t\tUriUtils.encode(authorizationCodeRequestAuthentication.getState(), StandardCharsets.UTF_8));\n\t\t}\n\t\t// build(true) -> Components are explicitly encoded\n\t\tString redirectUri = uriBuilder.build(true).toUriString();\n\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUri);\n\t}\n\n\tFilter createAuthorizationCodeRequestValidatingFilter(RegisteredClientRepository registeredClientRepository,\n\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {\n\t\treturn new OAuth2AuthorizationCodeRequestValidatingFilter(registeredClientRepository, authenticationValidator);\n\t}\n\n\t/**\n\t * A {@code Filter} that is applied before {@code OAuth2AuthorizationEndpointFilter}\n\t * and handles the pre-validation of an OAuth 2.0 Authorization Code Request.\n\t */\n\tprivate final class OAuth2AuthorizationCodeRequestValidatingFilter extends OncePerRequestFilter {\n\n\t\tprivate final RegisteredClientRepository registeredClientRepository;\n\n\t\tprivate final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator;\n\n\t\tprivate final Field setValidatedField;\n\n\t\tprivate OAuth2AuthorizationCodeRequestValidatingFilter(RegisteredClientRepository registeredClientRepository,\n\t\t\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {\n\t\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\t\tAssert.notNull(authenticationValidator, \"authenticationValidator cannot be null\");\n\t\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\t\tthis.authenticationValidator = authenticationValidator;\n\t\t\tthis.setValidatedField = ReflectionUtils.findField(OAuth2AuthorizationCodeRequestAuthenticationToken.class,\n\t\t\t\t\t\"validated\");\n\t\t\tReflectionUtils.makeAccessible(this.setValidatedField);\n\t\t}\n\n\t\t@Override\n\t\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tFilterChain filterChain) throws ServletException, IOException {\n\n\t\t\tif (!OAuth2AuthorizationEndpointFilter.this.authorizationEndpointMatcher.matches(request)) {\n\t\t\t\tfilterChain.doFilter(request, response);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tAuthentication authentication = OAuth2AuthorizationEndpointFilter.this.authenticationConverter\n\t\t\t\t\t.convert(request);\n\t\t\t\tif (!(authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication)) {\n\t\t\t\t\tfilterChain.doFilter(request, response);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tString requestUri = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()\n\t\t\t\t\t.get(OAuth2ParameterNames.REQUEST_URI);\n\t\t\t\tif (StringUtils.hasText(requestUri)) {\n\t\t\t\t\tfilterChain.doFilter(request, response);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tauthorizationCodeRequestAuthentication.setDetails(\n\t\t\t\t\t\tOAuth2AuthorizationEndpointFilter.this.authenticationDetailsSource.buildDetails(request));\n\n\t\t\t\tRegisteredClient registeredClient = this.registeredClientRepository\n\t\t\t\t\t.findByClientId(authorizationCodeRequestAuthentication.getClientId());\n\t\t\t\tif (registeredClient == null) {\n\t\t\t\t\tString redirectUri = null; // Prevent redirect\n\t\t\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthentication.getAuthorizationUri(),\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthentication.getClientId(),\n\t\t\t\t\t\t\t(Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri,\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthentication.getState(),\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthentication.getScopes(),\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthentication.getAdditionalParameters());\n\n\t\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\t\t\"OAuth 2.0 Parameter: \" + OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1\");\n\t\t\t\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error,\n\t\t\t\t\t\t\tauthorizationCodeRequestAuthenticationResult);\n\t\t\t\t}\n\n\t\t\t\tOAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext = OAuth2AuthorizationCodeRequestAuthenticationContext\n\t\t\t\t\t.with(authorizationCodeRequestAuthentication)\n\t\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t\t.build();\n\n\t\t\t\tthis.authenticationValidator.accept(authenticationContext);\n\n\t\t\t\tReflectionUtils.setField(this.setValidatedField, authorizationCodeRequestAuthentication, true);\n\n\t\t\t\t// Set the validated authorization code request as a request\n\t\t\t\t// attribute\n\t\t\t\t// to be used upstream by OAuth2AuthorizationEndpointFilter\n\t\t\t\trequest.setAttribute(OAuth2AuthorizationCodeRequestAuthenticationToken.class.getName(),\n\t\t\t\t\t\tauthorizationCodeRequestAuthentication);\n\n\t\t\t\tfilterChain.doFilter(request, response);\n\t\t\t}\n\t\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(LogMessage.format(\"Authorization request failed: %s\", ex.getError()), ex);\n\t\t\t\t}\n\t\t\t\tOAuth2AuthorizationEndpointFilter.this.authenticationFailureHandler.onAuthenticationFailure(request,\n\t\t\t\t\t\tresponse, ex);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\trequest.removeAttribute(OAuth2AuthorizationCodeRequestAuthenticationToken.class.getName());\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.http.converter.OAuth2AuthorizationServerMetadataHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Filter} that processes OAuth 2.0 Authorization Server Metadata Requests.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationServerMetadata\n * @see AuthorizationServerSettings\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8414#section-3\">3.\n * Obtaining Authorization Server Metadata</a>\n */\npublic final class OAuth2AuthorizationServerMetadataEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for OAuth 2.0 Authorization Server Metadata\n\t * requests.\n\t */\n\tprivate static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = \"/.well-known/oauth-authorization-server\";\n\n\tprivate final RequestMatcher requestMatcher = createRequestMatcher();\n\n\tprivate final OAuth2AuthorizationServerMetadataHttpMessageConverter authorizationServerMetadataHttpMessageConverter = new OAuth2AuthorizationServerMetadataHttpMessageConverter();\n\n\tprivate Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer = (\n\t\t\tauthorizationServerMetadata) -> {\n\t};\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2AuthorizationServerMetadata.Builder} allowing the ability to customize\n\t * the claims of the Authorization Server's configuration.\n\t * @param authorizationServerMetadataCustomizer the {@code Consumer} providing access\n\t * to the {@link OAuth2AuthorizationServerMetadata.Builder}\n\t */\n\tpublic void setAuthorizationServerMetadataCustomizer(\n\t\t\tConsumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer) {\n\t\tAssert.notNull(authorizationServerMetadataCustomizer, \"authorizationServerMetadataCustomizer cannot be null\");\n\t\tthis.authorizationServerMetadataCustomizer = authorizationServerMetadataCustomizer;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\tString issuer = authorizationServerContext.getIssuer();\n\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t.getAuthorizationServerSettings();\n\n\t\tOAuth2AuthorizationServerMetadata.Builder authorizationServerMetadata = OAuth2AuthorizationServerMetadata\n\t\t\t.builder()\n\t\t\t.issuer(issuer)\n\t\t\t.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))\n\t\t\t.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))\n\t\t\t.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())\n\t\t\t.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))\n\t\t\t.responseType(OAuth2AuthorizationResponseType.CODE.getValue())\n\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())\n\t\t\t.grantType(AuthorizationGrantType.TOKEN_EXCHANGE.getValue())\n\t\t\t.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))\n\t\t\t.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())\n\t\t\t.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))\n\t\t\t.tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods())\n\t\t\t.codeChallengeMethod(\"S256\")\n\t\t\t.tlsClientCertificateBoundAccessTokens(true)\n\t\t\t.dPoPSigningAlgorithms(dPoPSigningAlgorithms());\n\n\t\tthis.authorizationServerMetadataCustomizer.accept(authorizationServerMetadata);\n\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\tthis.authorizationServerMetadataHttpMessageConverter.write(authorizationServerMetadata.build(),\n\t\t\t\tMediaType.APPLICATION_JSON, httpResponse);\n\t}\n\n\tprivate static RequestMatcher createRequestMatcher() {\n\t\tfinal RequestMatcher defaultRequestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI);\n\t\tfinal RequestMatcher multipleIssuersRequestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI + \"/**\");\n\t\treturn (request) -> AuthorizationServerContextHolder.getContext()\n\t\t\t.getAuthorizationServerSettings()\n\t\t\t.isMultipleIssuersAllowed() ? multipleIssuersRequestMatcher.matches(request)\n\t\t\t\t\t: defaultRequestMatcher.matches(request);\n\t}\n\n\tprivate static Consumer<List<String>> clientAuthenticationMethods() {\n\t\treturn (authenticationMethods) -> {\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.TLS_CLIENT_AUTH.getValue());\n\t\t\tauthenticationMethods.add(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH.getValue());\n\t\t};\n\t}\n\n\tprivate static Consumer<List<String>> dPoPSigningAlgorithms() {\n\t\treturn (algs) -> {\n\t\t\talgs.add(JwsAlgorithms.RS256);\n\t\t\talgs.add(JwsAlgorithms.RS384);\n\t\t\talgs.add(JwsAlgorithms.RS512);\n\t\t\talgs.add(JwsAlgorithms.PS256);\n\t\t\talgs.add(JwsAlgorithms.PS384);\n\t\t\talgs.add(JwsAlgorithms.PS512);\n\t\t\talgs.add(JwsAlgorithms.ES256);\n\t\t\talgs.add(JwsAlgorithms.ES384);\n\t\t\talgs.add(JwsAlgorithms.ES512);\n\t\t};\n\t}\n\n\tprivate static String asUrl(String issuer, String endpoint) {\n\t\treturn UriComponentsBuilder.fromUriString(issuer).path(endpoint).toUriString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.util.Arrays;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.authentication.ClientSecretAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.JwtClientAssertionAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.PublicClientAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.X509ClientCertificateAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretBasicAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretPostAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.JwtClientAssertionAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.PublicClientAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.X509ClientCertificateAuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} that processes an authentication request for an OAuth 2.0 Client.\n *\n * @author Joe Grandja\n * @author Patryk Kostrzewa\n * @since 7.0\n * @see AuthenticationManager\n * @see JwtClientAssertionAuthenticationConverter\n * @see JwtClientAssertionAuthenticationProvider\n * @see X509ClientCertificateAuthenticationConverter\n * @see X509ClientCertificateAuthenticationProvider\n * @see ClientSecretBasicAuthenticationConverter\n * @see ClientSecretPostAuthenticationConverter\n * @see ClientSecretAuthenticationProvider\n * @see PublicClientAuthenticationConverter\n * @see PublicClientAuthenticationProvider\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-2.3\">Section 2.3 Client\n * Authentication</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1\">Section 3.2.1 Token\n * Endpoint Client Authentication</a>\n */\npublic final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter {\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher requestMatcher;\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tprivate final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::onAuthenticationSuccess;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = this::onAuthenticationFailure;\n\n\t/**\n\t * Constructs an {@code OAuth2ClientAuthenticationFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the {@link AuthenticationManager} used for\n\t * authenticating the client\n\t * @param requestMatcher the {@link RequestMatcher} used for matching against the\n\t * {@code HttpServletRequest}\n\t */\n\tpublic OAuth2ClientAuthenticationFilter(AuthenticationManager authenticationManager,\n\t\t\tRequestMatcher requestMatcher) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.requestMatcher = requestMatcher;\n\t\t// @formatter:off\n\t\tthis.authenticationConverter = new DelegatingAuthenticationConverter(\n\t\t\t\tArrays.asList(\n\t\t\t\t\t\tnew JwtClientAssertionAuthenticationConverter(),\n\t\t\t\t\t\tnew ClientSecretBasicAuthenticationConverter(),\n\t\t\t\t\t\tnew ClientSecretPostAuthenticationConverter(),\n\t\t\t\t\t\tnew PublicClientAuthenticationConverter(),\n\t\t\t\t\t\tnew X509ClientCertificateAuthenticationConverter()));\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication authenticationRequest = this.authenticationConverter.convert(request);\n\t\t\tif (authenticationRequest instanceof AbstractAuthenticationToken authenticationToken) {\n\t\t\t\tauthenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\t}\n\t\t\tif (authenticationRequest != null) {\n\t\t\t\tvalidateClientIdentifier(authenticationRequest);\n\t\t\t\tAuthentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);\n\t\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult);\n\t\t\t}\n\t\t\tfilterChain.doFilter(request, response);\n\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Client authentication failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract client\n\t * credentials from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2ClientAuthenticationToken} used for authenticating the client.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract client credentials from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling a successful client\n\t * authentication and associating the {@link OAuth2ClientAuthenticationToken} to the\n\t * {@link SecurityContext}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling a successful client authentication\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling a failed client\n\t * authentication and returning the {@link OAuth2Error Error Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling a failed client authentication\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) {\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t}\n\n\tprivate void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException {\n\n\t\tSecurityContextHolder.clearContext();\n\n\t\t// TODO\n\t\t// The authorization server MAY return an HTTP 401 (Unauthorized) status code\n\t\t// to indicate which HTTP authentication schemes are supported.\n\t\t// If the client attempted to authenticate via the \"Authorization\" request header\n\t\t// field,\n\t\t// the authorization server MUST respond with an HTTP 401 (Unauthorized) status\n\t\t// code and\n\t\t// include the \"WWW-Authenticate\" response header field\n\t\t// matching the authentication scheme used by the client.\n\n\t\tOAuth2Error error = ((OAuth2AuthenticationException) exception).getError();\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\tif (OAuth2ErrorCodes.INVALID_CLIENT.equals(error.getErrorCode())) {\n\t\t\thttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);\n\t\t}\n\t\telse {\n\t\t\thttpResponse.setStatusCode(HttpStatus.BAD_REQUEST);\n\t\t}\n\t\t// We don't want to reveal too much information to the caller so just return the\n\t\t// error code\n\t\tOAuth2Error errorResponse = new OAuth2Error(error.getErrorCode());\n\t\tthis.errorHttpResponseConverter.write(errorResponse, null, httpResponse);\n\t}\n\n\tprivate static void validateClientIdentifier(Authentication authentication) {\n\t\tif (!(authentication instanceof OAuth2ClientAuthenticationToken)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// As per spec, in Appendix A.1.\n\t\t// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#appendix-A.1\n\t\t// The syntax for client_id is *VSCHAR (%x20-7E):\n\t\t// -> Hex 20 -> ASCII 32 -> space\n\t\t// -> Hex 7E -> ASCII 126 -> tilde\n\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;\n\t\tString clientId = (String) clientAuthentication.getPrincipal();\n\t\tfor (int i = 0; i < clientId.length(); i++) {\n\t\t\tchar charAt = clientId.charAt(i);\n\t\t\tif (!(charAt >= 32 && charAt <= 126)) {\n\t\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientRegistrationEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.http.converter.OAuth2ClientRegistrationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientRegistrationAuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} that processes OAuth 2.0 Dynamic Client Registration Requests.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2ClientRegistration\n * @see OAuth2ClientRegistrationAuthenticationConverter\n * @see OAuth2ClientRegistrationAuthenticationProvider\n * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7591#section-3\">3. Client\n * Registration Endpoint</a>\n */\npublic final class OAuth2ClientRegistrationEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for OAuth 2.0 Client Registration requests.\n\t */\n\tprivate static final String DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI = \"/oauth2/register\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher clientRegistrationEndpointMatcher;\n\n\tprivate final HttpMessageConverter<OAuth2ClientRegistration> clientRegistrationHttpMessageConverter = new OAuth2ClientRegistrationHttpMessageConverter();\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tprivate AuthenticationConverter authenticationConverter = new OAuth2ClientRegistrationAuthenticationConverter();\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::sendClientRegistrationResponse;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;\n\n\t/**\n\t * Constructs an {@code OAuth2ClientRegistrationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2ClientRegistrationEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2ClientRegistrationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param clientRegistrationEndpointUri the endpoint {@code URI} for OAuth 2.0 Client\n\t * Registration requests\n\t */\n\tpublic OAuth2ClientRegistrationEndpointFilter(AuthenticationManager authenticationManager,\n\t\t\tString clientRegistrationEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(clientRegistrationEndpointUri, \"clientRegistrationEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.clientRegistrationEndpointMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, clientRegistrationEndpointUri);\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.clientRegistrationEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication clientRegistrationAuthentication = this.authenticationConverter.convert(request);\n\n\t\t\tAuthentication clientRegistrationAuthenticationResult = this.authenticationManager\n\t\t\t\t.authenticate(clientRegistrationAuthentication);\n\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response,\n\t\t\t\t\tclientRegistrationAuthenticationResult);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Client registration request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\"OAuth 2.0 Client Registration Error: \" + ex.getMessage(),\n\t\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2\");\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(error.getDescription(), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response,\n\t\t\t\t\tnew OAuth2AuthenticationException(error));\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Client\n\t * Registration Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2ClientRegistrationAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param authenticationConverter an {@link AuthenticationConverter} used when\n\t * attempting to extract a Client Registration Request from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2ClientRegistrationAuthenticationToken} and returning the\n\t * {@link OAuth2ClientRegistration Client Registration Response}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2ClientRegistrationAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate void sendClientRegistrationResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\t\tOAuth2ClientRegistration clientRegistration = ((OAuth2ClientRegistrationAuthenticationToken) authentication)\n\t\t\t.getClientRegistration();\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\thttpResponse.setStatusCode(HttpStatus.CREATED);\n\t\tthis.clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpResponse);\n\t}\n\n\tprivate void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authenticationException) throws IOException {\n\t\tOAuth2Error error = ((OAuth2AuthenticationException) authenticationException).getError();\n\t\tHttpStatus httpStatus = HttpStatus.BAD_REQUEST;\n\t\tif (OAuth2ErrorCodes.INVALID_TOKEN.equals(error.getErrorCode())) {\n\t\t\thttpStatus = HttpStatus.UNAUTHORIZED;\n\t\t}\n\t\telse if (OAuth2ErrorCodes.INSUFFICIENT_SCOPE.equals(error.getErrorCode())) {\n\t\t\thttpStatus = HttpStatus.FORBIDDEN;\n\t\t}\n\t\telse if (OAuth2ErrorCodes.INVALID_CLIENT.equals(error.getErrorCode())) {\n\t\t\thttpStatus = HttpStatus.UNAUTHORIZED;\n\t\t}\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\thttpResponse.setStatusCode(httpStatus);\n\t\tthis.errorHttpResponseConverter.write(error, null, httpResponse);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2DeviceAuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2DeviceAuthorizationResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceAuthorizationRequestAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ErrorAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.RedirectUrlBuilder;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Device Authorization endpoint, which handles the\n * processing of the OAuth 2.0 Device Authorization Request.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AuthenticationManager\n * @see OAuth2DeviceAuthorizationRequestAuthenticationConverter\n * @see OAuth2DeviceAuthorizationRequestAuthenticationProvider\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc8628\">OAuth 2.0\n * Device Authorization Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.1\">Section 3.1 Device\n * Authorization Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.2\">Section 3.2 Device\n * Authorization Response</a>\n */\npublic final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerRequestFilter {\n\n\tprivate static final String DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI = \"/oauth2/device_authorization\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher deviceAuthorizationEndpointMatcher;\n\n\tprivate final HttpMessageConverter<OAuth2DeviceAuthorizationResponse> deviceAuthorizationHttpResponseConverter = new OAuth2DeviceAuthorizationResponseHttpMessageConverter();\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::sendDeviceAuthorizationResponse;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler();\n\n\tprivate String verificationUri = OAuth2DeviceVerificationEndpointFilter.DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceAuthorizationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceAuthorizationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param deviceAuthorizationEndpointUri the endpoint {@code URI} for device\n\t * authorization requests\n\t */\n\tpublic OAuth2DeviceAuthorizationEndpointFilter(AuthenticationManager authenticationManager,\n\t\t\tString deviceAuthorizationEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(deviceAuthorizationEndpointUri, \"deviceAuthorizationEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.deviceAuthorizationEndpointMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, deviceAuthorizationEndpointUri);\n\t\tthis.authenticationConverter = new OAuth2DeviceAuthorizationRequestAuthenticationConverter();\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.deviceAuthorizationEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication deviceAuthorizationRequestAuthentication = this.authenticationConverter.convert(request);\n\t\t\tif (deviceAuthorizationRequestAuthentication instanceof AbstractAuthenticationToken authenticationToken) {\n\t\t\t\tauthenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\t}\n\n\t\t\tAuthentication deviceAuthorizationRequestAuthenticationResult = this.authenticationManager\n\t\t\t\t.authenticate(deviceAuthorizationRequestAuthentication);\n\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response,\n\t\t\t\t\tdeviceAuthorizationRequestAuthenticationResult);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Device authorization request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationDetailsSource} used for building an authentication\n\t * details instance from {@link HttpServletRequest}.\n\t * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for\n\t * building an authentication details instance from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"authenticationDetailsSource cannot be null\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Device\n\t * Authorization Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating\n\t * the request.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract a Device Authorization Request from\n\t * {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} and returning the\n\t * {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\t/**\n\t * Sets the end-user verification {@code URI} on the authorization server.\n\t * @param verificationUri the end-user verification {@code URI} on the authorization\n\t * server\n\t * @see <a target=\"_blank\" href=\n\t * \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.2\">Section 3.2 Device\n\t * Authorization Response</a>\n\t */\n\tpublic void setVerificationUri(String verificationUri) {\n\t\tAssert.hasText(verificationUri, \"verificationUri cannot be empty\");\n\t\tthis.verificationUri = verificationUri;\n\t}\n\n\tprivate void sendDeviceAuthorizationResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication;\n\n\t\tOAuth2DeviceCode deviceCode = deviceAuthorizationRequestAuthentication.getDeviceCode();\n\t\tOAuth2UserCode userCode = deviceAuthorizationRequestAuthentication.getUserCode();\n\n\t\t// Generate the fully-qualified verification URI\n\t\tUriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(resolveVerificationUri(request));\n\t\tString verificationUri = uriComponentsBuilder.build().toUriString();\n\t\t// @formatter:off\n\t\tString verificationUriComplete = uriComponentsBuilder\n\t\t\t\t.queryParam(OAuth2ParameterNames.USER_CODE, userCode.getTokenValue())\n\t\t\t\t.build().toUriString();\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse =\n\t\t\t\tOAuth2DeviceAuthorizationResponse.with(deviceCode, userCode)\n\t\t\t\t\t\t.verificationUri(verificationUri)\n\t\t\t\t\t\t.verificationUriComplete(verificationUriComplete)\n\t\t\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\tthis.deviceAuthorizationHttpResponseConverter.write(deviceAuthorizationResponse, null, httpResponse);\n\t}\n\n\tprivate String resolveVerificationUri(HttpServletRequest request) {\n\t\tif (UrlUtils.isAbsoluteUrl(this.verificationUri)) {\n\t\t\treturn this.verificationUri;\n\t\t}\n\t\tRedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();\n\t\turlBuilder.setScheme(request.getScheme());\n\t\turlBuilder.setServerName(request.getServerName());\n\t\turlBuilder.setPort(request.getServerPort());\n\t\turlBuilder.setContextPath(request.getContextPath());\n\t\turlBuilder.setPathInfo(this.verificationUri);\n\t\treturn urlBuilder.getUrl();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceAuthorizationConsentAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceVerificationAuthenticationConverter;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.RedirectUrlBuilder;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Device Authorization Grant, which handles the\n * processing of the Device Verification Request (submission of the user code) and the\n * Device Authorization Consent.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AuthenticationManager\n * @see OAuth2DeviceVerificationAuthenticationConverter\n * @see OAuth2DeviceVerificationAuthenticationProvider\n * @see OAuth2DeviceAuthorizationConsentAuthenticationConverter\n * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc8628\">OAuth 2.0\n * Device Authorization Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.3\">Section 3.3 User\n * Interaction</a>\n */\npublic final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequestFilter {\n\n\tstatic final String DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI = \"/oauth2/device_verification\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher deviceVerificationEndpointMatcher;\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = new SimpleUrlAuthenticationSuccessHandler(\n\t\t\t\"/?success\");\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;\n\n\tprivate String consentPage;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceVerificationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2DeviceVerificationEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceVerificationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param deviceVerificationEndpointUri the endpoint {@code URI} for device\n\t * verification requests\n\t */\n\tpublic OAuth2DeviceVerificationEndpointFilter(AuthenticationManager authenticationManager,\n\t\t\tString deviceVerificationEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(deviceVerificationEndpointUri, \"deviceVerificationEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.deviceVerificationEndpointMatcher = createDefaultRequestMatcher(deviceVerificationEndpointUri);\n\t\t// @formatter:off\n\t\tthis.authenticationConverter = new DelegatingAuthenticationConverter(\n\t\t\t\tArrays.asList(\n\t\t\t\t\t\tnew OAuth2DeviceVerificationAuthenticationConverter(),\n\t\t\t\t\t\tnew OAuth2DeviceAuthorizationConsentAuthenticationConverter()));\n\t\t// @formatter:on\n\t}\n\n\tprivate RequestMatcher createDefaultRequestMatcher(String deviceVerificationEndpointUri) {\n\t\tRequestMatcher verificationRequestGetMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.GET, deviceVerificationEndpointUri);\n\t\tRequestMatcher verificationRequestPostMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, deviceVerificationEndpointUri);\n\t\tRequestMatcher userCodeParameterMatcher = (\n\t\t\t\trequest) -> request.getParameter(OAuth2ParameterNames.USER_CODE) != null;\n\n\t\treturn new AndRequestMatcher(\n\t\t\t\tnew OrRequestMatcher(verificationRequestGetMatcher, verificationRequestPostMatcher),\n\t\t\t\tuserCodeParameterMatcher);\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.deviceVerificationEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication authentication = this.authenticationConverter.convert(request);\n\t\t\tif (authentication instanceof AbstractAuthenticationToken authenticationToken) {\n\t\t\t\tauthenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\t}\n\n\t\t\tAuthentication authenticationResult = this.authenticationManager.authenticate(authentication);\n\t\t\tif (authenticationResult instanceof OAuth2DeviceAuthorizationConsentAuthenticationToken) {\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(\"Device authorization consent is required\");\n\t\t\t\t}\n\t\t\t\tsendAuthorizationConsent(request, response, authenticationResult);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Device verification request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationDetailsSource} used for building an authentication\n\t * details instance from {@link HttpServletRequest}.\n\t * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for\n\t * building an authentication details instance from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"authenticationDetailsSource cannot be null\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Device\n\t * Verification Request (or Device Authorization Consent) from\n\t * {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2DeviceVerificationAuthenticationToken} or\n\t * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating\n\t * the request.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract a Device Verification Request (or Device Authorization\n\t * Consent) from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2DeviceVerificationAuthenticationToken} and returning the response.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2DeviceVerificationAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\t/**\n\t * Specify the URI to redirect Resource Owners to if consent is required. A default\n\t * consent page will be generated when this attribute is not specified.\n\t * @param consentPage the URI of the custom consent page to redirect to if consent is\n\t * required (e.g. \"/oauth2/consent\")\n\t */\n\tpublic void setConsentPage(String consentPage) {\n\t\tthis.consentPage = consentPage;\n\t}\n\n\tprivate void sendAuthorizationConsent(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken authorizationConsentAuthentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) authentication;\n\n\t\tString clientId = authorizationConsentAuthentication.getClientId();\n\t\tAuthentication principal = (Authentication) authorizationConsentAuthentication.getPrincipal();\n\t\tSet<String> requestedScopes = authorizationConsentAuthentication.getRequestedScopes();\n\t\tSet<String> authorizedScopes = authorizationConsentAuthentication.getScopes();\n\t\tString state = authorizationConsentAuthentication.getState();\n\t\tString userCode = authorizationConsentAuthentication.getUserCode();\n\n\t\tif (hasConsentUri()) {\n\t\t\tString redirectUri = UriComponentsBuilder.fromUriString(resolveConsentUri(request))\n\t\t\t\t.queryParam(OAuth2ParameterNames.SCOPE, String.join(\" \", requestedScopes))\n\t\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, clientId)\n\t\t\t\t.queryParam(OAuth2ParameterNames.STATE, state)\n\t\t\t\t.queryParam(OAuth2ParameterNames.USER_CODE, userCode)\n\t\t\t\t.toUriString();\n\t\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUri);\n\t\t}\n\t\telse {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Displaying generated consent screen\");\n\t\t\t}\n\t\t\tMap<String, String> additionalParameters = new HashMap<>();\n\t\t\tadditionalParameters.put(OAuth2ParameterNames.USER_CODE, userCode);\n\t\t\tDefaultConsentPage.displayConsent(request, response, clientId, principal, requestedScopes, authorizedScopes,\n\t\t\t\t\tstate, additionalParameters);\n\t\t}\n\t}\n\n\tprivate boolean hasConsentUri() {\n\t\treturn StringUtils.hasText(this.consentPage);\n\t}\n\n\tprivate String resolveConsentUri(HttpServletRequest request) {\n\t\tif (UrlUtils.isAbsoluteUrl(this.consentPage)) {\n\t\t\treturn this.consentPage;\n\t\t}\n\t\tRedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();\n\t\turlBuilder.setScheme(request.getScheme());\n\t\turlBuilder.setServerName(request.getServerName());\n\t\turlBuilder.setPort(request.getServerPort());\n\t\turlBuilder.setContextPath(request.getContextPath());\n\t\turlBuilder.setPathInfo(this.consentPage);\n\t\treturn urlBuilder.getUrl();\n\t}\n\n\tprivate void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authenticationException) throws IOException {\n\n\t\tOAuth2Error error = ((OAuth2AuthenticationException) authenticationException).getError();\n\t\tresponse.sendError(HttpStatus.BAD_REQUEST.value(), error.toString());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2PushedAuthorizationRequestEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2PushedAuthorizationRequestAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2PushedAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ErrorAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Pushed Authorization Request endpoint, which handles\n * the processing of the OAuth 2.0 Pushed Authorization Request.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationManager\n * @see OAuth2PushedAuthorizationRequestAuthenticationProvider\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9126#name-pushed-authorization-reques\">Section\n * 2. Pushed Authorization Request Endpoint</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9126#section-2.1\">Section 2.1 Pushed\n * Authorization Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc9126#section-2.2\">Section 2.2 Pushed\n * Authorization Response</a>\n */\npublic final class OAuth2PushedAuthorizationRequestEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for pushed authorization requests.\n\t */\n\tprivate static final String DEFAULT_PUSHED_AUTHORIZATION_REQUEST_ENDPOINT_URI = \"/oauth2/par\";\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate static final GenericHttpMessageConverter<Object> JSON_MESSAGE_CONVERTER = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher pushedAuthorizationRequestEndpointMatcher;\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::sendPushedAuthorizationResponse;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler();\n\n\t/**\n\t * Constructs an {@code OAuth2PushedAuthorizationRequestEndpointFilter} using the\n\t * provided parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2PushedAuthorizationRequestEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_PUSHED_AUTHORIZATION_REQUEST_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2PushedAuthorizationRequestEndpointFilter} using the\n\t * provided parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param pushedAuthorizationRequestEndpointUri the endpoint {@code URI} for pushed\n\t * authorization requests\n\t */\n\tpublic OAuth2PushedAuthorizationRequestEndpointFilter(AuthenticationManager authenticationManager,\n\t\t\tString pushedAuthorizationRequestEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(pushedAuthorizationRequestEndpointUri, \"pushedAuthorizationRequestEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.pushedAuthorizationRequestEndpointMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, pushedAuthorizationRequestEndpointUri);\n\t\tthis.authenticationConverter = new OAuth2AuthorizationCodeRequestAuthenticationConverter();\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.pushedAuthorizationRequestEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication pushedAuthorizationRequestAuthentication = this.authenticationConverter.convert(request);\n\t\t\tif (pushedAuthorizationRequestAuthentication instanceof AbstractAuthenticationToken) {\n\t\t\t\t((AbstractAuthenticationToken) pushedAuthorizationRequestAuthentication)\n\t\t\t\t\t.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\t}\n\t\t\tAuthentication pushedAuthorizationRequestAuthenticationResult = this.authenticationManager\n\t\t\t\t.authenticate(pushedAuthorizationRequestAuthentication);\n\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response,\n\t\t\t\t\tpushedAuthorizationRequestAuthenticationResult);\n\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Pushed authorization request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationDetailsSource} used for building an authentication\n\t * details instance from {@link HttpServletRequest}.\n\t * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for\n\t * building an authentication details instance from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"authenticationDetailsSource cannot be null\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Pushed\n\t * Authorization Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2PushedAuthorizationRequestAuthenticationToken} used for authenticating\n\t * the request.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract a Pushed Authorization Request from\n\t * {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2PushedAuthorizationRequestAuthenticationToken} and returning the\n\t * Pushed Authorization Response.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2PushedAuthorizationRequestAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate void sendPushedAuthorizationResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken pushedAuthorizationRequestAuthentication = (OAuth2PushedAuthorizationRequestAuthenticationToken) authentication;\n\n\t\tMap<String, Object> pushedAuthorizationResponse = new LinkedHashMap<>();\n\t\tpushedAuthorizationResponse.put(OAuth2ParameterNames.REQUEST_URI,\n\t\t\t\tpushedAuthorizationRequestAuthentication.getRequestUri());\n\t\tlong expiresIn = ChronoUnit.SECONDS.between(Instant.now(),\n\t\t\t\tpushedAuthorizationRequestAuthentication.getRequestUriExpiresAt());\n\t\tpushedAuthorizationResponse.put(OAuth2ParameterNames.EXPIRES_IN, expiresIn);\n\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\thttpResponse.setStatusCode(HttpStatus.CREATED);\n\n\t\tJSON_MESSAGE_CONVERTER.write(pushedAuthorizationResponse, STRING_OBJECT_MAP.getType(),\n\t\t\t\tMediaType.APPLICATION_JSON, httpResponse);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.util.Arrays;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AccessTokenResponseAuthenticationSuccessHandler;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceCodeAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ErrorAuthenticationFailureHandler;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenExchangeAuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationConverter;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Token endpoint, which handles the processing of an\n * OAuth 2.0 Authorization Grant.\n *\n * <p>\n * It converts the OAuth 2.0 Authorization Grant request to an {@link Authentication},\n * which is then authenticated by the {@link AuthenticationManager}. If the authentication\n * succeeds, the {@link AuthenticationManager} returns an\n * {@link OAuth2AccessTokenAuthenticationToken}, which is returned in the OAuth 2.0 Access\n * Token response. In case of any error, an {@link OAuth2Error} is returned in the OAuth\n * 2.0 Error response.\n *\n * <p>\n * By default, this {@code Filter} responds to authorization grant requests at the\n * {@code URI} {@code /oauth2/token} and {@code HttpMethod} {@code POST}.\n *\n * <p>\n * The default endpoint {@code URI} {@code /oauth2/token} may be overridden via the\n * constructor {@link #OAuth2TokenEndpointFilter(AuthenticationManager, String)}.\n *\n * @author Joe Grandja\n * @author Madhu Bhat\n * @author Daniel Garnier-Moiroux\n * @author Dmitriy Dubson\n * @since 7.0\n * @see AuthenticationManager\n * @see OAuth2AuthorizationCodeAuthenticationProvider\n * @see OAuth2RefreshTokenAuthenticationProvider\n * @see OAuth2ClientCredentialsAuthenticationProvider\n * @see OAuth2DeviceCodeAuthenticationProvider\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-3.2\">Section\n * 3.2 Token Endpoint</a>\n */\npublic final class OAuth2TokenEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for access token requests.\n\t */\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static final String DEFAULT_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher tokenEndpointMatcher;\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = new OAuth2AccessTokenResponseAuthenticationSuccessHandler();\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler();\n\n\t/**\n\t * Constructs an {@code OAuth2TokenEndpointFilter} using the provided parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2TokenEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_TOKEN_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2TokenEndpointFilter} using the provided parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param tokenEndpointUri the endpoint {@code URI} for access token requests\n\t */\n\tpublic OAuth2TokenEndpointFilter(AuthenticationManager authenticationManager, String tokenEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(tokenEndpointUri, \"tokenEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.tokenEndpointMatcher = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, tokenEndpointUri);\n\t\t// @formatter:off\n\t\tthis.authenticationConverter = new DelegatingAuthenticationConverter(\n\t\t\t\tArrays.asList(\n\t\t\t\t\t\tnew OAuth2AuthorizationCodeAuthenticationConverter(),\n\t\t\t\t\t\tnew OAuth2RefreshTokenAuthenticationConverter(),\n\t\t\t\t\t\tnew OAuth2ClientCredentialsAuthenticationConverter(),\n\t\t\t\t\t\tnew OAuth2DeviceCodeAuthenticationConverter(),\n\t\t\t\t\t\tnew OAuth2TokenExchangeAuthenticationConverter())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.tokenEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tString[] grantTypes = request.getParameterValues(OAuth2ParameterNames.GRANT_TYPE);\n\t\t\tif (grantTypes == null || grantTypes.length != 1) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.GRANT_TYPE);\n\t\t\t}\n\n\t\t\tAuthentication authorizationGrantAuthentication = this.authenticationConverter.convert(request);\n\t\t\tif (authorizationGrantAuthentication == null) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE, OAuth2ParameterNames.GRANT_TYPE);\n\t\t\t}\n\t\t\tif (authorizationGrantAuthentication instanceof AbstractAuthenticationToken authenticationToken) {\n\t\t\t\tauthenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\t}\n\n\t\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationManager\n\t\t\t\t.authenticate(authorizationGrantAuthentication);\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, accessTokenAuthentication);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Token request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationDetailsSource} used for building an authentication\n\t * details instance from {@link HttpServletRequest}.\n\t * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for\n\t * building an authentication details instance from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"authenticationDetailsSource cannot be null\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract an Access\n\t * Token Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the\n\t * authorization grant.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract an Access Token Request from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2AccessTokenAuthenticationToken} and returning the\n\t * {@link OAuth2AccessTokenResponse Access Token Response}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2AccessTokenAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, DEFAULT_ERROR_URI);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.http.converter.OAuth2TokenIntrospectionHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ErrorAuthenticationFailureHandler;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenIntrospectionAuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Token Introspection endpoint.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n * @author Gaurav Tiwari\n * @since 7.0\n * @see OAuth2TokenIntrospectionAuthenticationProvider\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7662#section-2\">Section 2\n * Introspection Endpoint</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7662#section-2.1\">Section\n * 2.1 Introspection Request</a>\n */\npublic final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for token introspection requests.\n\t */\n\tprivate static final String DEFAULT_TOKEN_INTROSPECTION_ENDPOINT_URI = \"/oauth2/introspect\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher tokenIntrospectionEndpointMatcher;\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate final HttpMessageConverter<OAuth2TokenIntrospection> tokenIntrospectionHttpResponseConverter = new OAuth2TokenIntrospectionHttpMessageConverter();\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::sendIntrospectionResponse;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler();\n\n\t/**\n\t * Constructs an {@code OAuth2TokenIntrospectionEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2TokenIntrospectionEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_TOKEN_INTROSPECTION_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2TokenIntrospectionEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param tokenIntrospectionEndpointUri the endpoint {@code URI} for token\n\t * introspection requests\n\t */\n\tpublic OAuth2TokenIntrospectionEndpointFilter(AuthenticationManager authenticationManager,\n\t\t\tString tokenIntrospectionEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(tokenIntrospectionEndpointUri, \"tokenIntrospectionEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.tokenIntrospectionEndpointMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, tokenIntrospectionEndpointUri);\n\t\tthis.authenticationConverter = new OAuth2TokenIntrospectionAuthenticationConverter();\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.tokenIntrospectionEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication tokenIntrospectionAuthentication = this.authenticationConverter.convert(request);\n\t\t\tAuthentication tokenIntrospectionAuthenticationResult = this.authenticationManager\n\t\t\t\t.authenticate(tokenIntrospectionAuthentication);\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response,\n\t\t\t\t\ttokenIntrospectionAuthenticationResult);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Token introspection request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract an\n\t * Introspection Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract an Introspection Request from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2TokenIntrospectionAuthenticationToken}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Resonse}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate void sendIntrospectionResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException {\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = (OAuth2TokenIntrospectionAuthenticationToken) authentication;\n\t\tOAuth2TokenIntrospection tokenClaims = tokenIntrospectionAuthentication.getTokenClaims();\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\tthis.tokenIntrospectionHttpResponseConverter.write(tokenClaims, null, httpResponse);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ErrorAuthenticationFailureHandler;\nimport org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2TokenRevocationAuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Token Revocation endpoint.\n *\n * @author Vivek Babu\n * @author Joe Grandja\n * @author Arfat Chaus\n * @since 7.0\n * @see OAuth2TokenRevocationAuthenticationProvider\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7009#section-2\">Section 2\n * Token Revocation</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7009#section-2.1\">Section\n * 2.1 Revocation Request</a>\n */\npublic final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default endpoint {@code URI} for token revocation requests.\n\t */\n\tprivate static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = \"/oauth2/revoke\";\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate final RequestMatcher tokenRevocationEndpointMatcher;\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler authenticationSuccessHandler = this::sendRevocationSuccessResponse;\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler();\n\n\t/**\n\t * Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2TokenRevocationEndpointFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided\n\t * parameters.\n\t * @param authenticationManager the authentication manager\n\t * @param tokenRevocationEndpointUri the endpoint {@code URI} for token revocation\n\t * requests\n\t */\n\tpublic OAuth2TokenRevocationEndpointFilter(AuthenticationManager authenticationManager,\n\t\t\tString tokenRevocationEndpointUri) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.hasText(tokenRevocationEndpointUri, \"tokenRevocationEndpointUri cannot be empty\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.tokenRevocationEndpointMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, tokenRevocationEndpointUri);\n\t\tthis.authenticationConverter = new OAuth2TokenRevocationAuthenticationConverter();\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.tokenRevocationEndpointMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthentication tokenRevocationAuthentication = this.authenticationConverter.convert(request);\n\t\t\tif (tokenRevocationAuthentication instanceof AbstractAuthenticationToken authenticationToken) {\n\t\t\t\tauthenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\t}\n\n\t\t\tAuthentication tokenRevocationAuthenticationResult = this.authenticationManager\n\t\t\t\t.authenticate(tokenRevocationAuthentication);\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response,\n\t\t\t\t\ttokenRevocationAuthenticationResult);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException ex) {\n\t\t\tSecurityContextHolder.clearContext();\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Token revocation request failed: %s\", ex.getError()), ex);\n\t\t\t}\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationDetailsSource} used for building an authentication\n\t * details instance from {@link HttpServletRequest}.\n\t * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for\n\t * building an authentication details instance from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"authenticationDetailsSource cannot be null\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationConverter} used when attempting to extract a Revoke\n\t * Token Request from {@link HttpServletRequest} to an instance of\n\t * {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the\n\t * request.\n\t * @param authenticationConverter the {@link AuthenticationConverter} used when\n\t * attempting to extract a Revoke Token Request from {@link HttpServletRequest}\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationSuccessHandler} used for handling an\n\t * {@link OAuth2TokenRevocationAuthenticationToken}.\n\t * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used\n\t * for handling an {@link OAuth2TokenRevocationAuthenticationToken}\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used for handling an\n\t * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error\n\t * Response}.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * for handling an {@link OAuth2AuthenticationException}\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\tprivate void sendRevocationSuccessResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) {\n\t\tresponse.setStatus(HttpStatus.OK.value());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract HTTP Basic credentials from {@link HttpServletRequest} and then\n * converts to an {@link OAuth2ClientAuthenticationToken} used for authenticating the\n * client.\n *\n * @author Patryk Kostrzewa\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2ClientAuthenticationToken\n * @see OAuth2ClientAuthenticationFilter\n */\npublic final class ClientSecretBasicAuthenticationConverter implements AuthenticationConverter {\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tString header = request.getHeader(HttpHeaders.AUTHORIZATION);\n\t\tif (header == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString[] parts = header.split(\"\\\\s\");\n\t\tif (!parts[0].equalsIgnoreCase(\"Basic\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (parts.length != 2) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tbyte[] decodedCredentials;\n\t\ttry {\n\t\t\tdecodedCredentials = Base64.getDecoder().decode(parts[1].getBytes(StandardCharsets.UTF_8));\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\tthrow new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST), ex);\n\t\t}\n\n\t\tString credentialsString = new String(decodedCredentials, StandardCharsets.UTF_8);\n\t\tString[] credentials = credentialsString.split(\":\", 2);\n\t\tif (credentials.length != 2 || !StringUtils.hasText(credentials[0]) || !StringUtils.hasText(credentials[1])) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tString clientID;\n\t\tString clientSecret;\n\t\ttry {\n\t\t\tclientID = URLDecoder.decode(credentials[0], StandardCharsets.UTF_8.name());\n\t\t\tclientSecret = URLDecoder.decode(credentials[1], StandardCharsets.UTF_8.name());\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST), ex);\n\t\t}\n\n\t\treturn new OAuth2ClientAuthenticationToken(clientID, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tclientSecret, OAuth2EndpointUtils.getParametersIfMatchesAuthorizationCodeGrantRequest(request));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract client credentials from POST parameters of\n * {@link HttpServletRequest} and then converts to an\n * {@link OAuth2ClientAuthenticationToken} used for authenticating the client.\n *\n * @author Anoop Garlapati\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2ClientAuthenticationToken\n * @see OAuth2ClientAuthenticationFilter\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-2.3.1\">Section 2.3.1 Client Password</a>\n */\npublic final class ClientSecretPostAuthenticationConverter implements AuthenticationConverter {\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// client_id (REQUIRED)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (!StringUtils.hasText(clientId)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\t// client_secret (REQUIRED)\n\t\tString clientSecret = parameters.getFirst(OAuth2ParameterNames.CLIENT_SECRET);\n\t\tif (!StringUtils.hasText(clientSecret)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (parameters.get(OAuth2ParameterNames.CLIENT_SECRET).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = OAuth2EndpointUtils\n\t\t\t.getParametersIfMatchesAuthorizationCodeGrantRequest(request, OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_SECRET);\n\n\t\treturn new OAuth2ClientAuthenticationToken(clientId, ClientAuthenticationMethod.CLIENT_SECRET_POST,\n\t\t\t\tclientSecret, additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract a JWT client assertion credential from {@link HttpServletRequest}\n * and then converts to an {@link OAuth2ClientAuthenticationToken} used for authenticating\n * the client.\n *\n * @author Rafal Lewczuk\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2ClientAuthenticationToken\n * @see OAuth2ClientAuthenticationFilter\n */\npublic final class JwtClientAssertionAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = new ClientAuthenticationMethod(\n\t\t\t\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\");\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\tif (parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE) == null\n\t\t\t\t|| parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION) == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// client_assertion_type (REQUIRED)\n\t\tString clientAssertionType = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE);\n\t\tif (parameters.get(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\t\tif (!JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD.getValue().equals(clientAssertionType)) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// client_assertion (REQUIRED)\n\t\tString jwtAssertion = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION);\n\t\tif (parameters.get(OAuth2ParameterNames.CLIENT_ASSERTION).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\t// client_id (OPTIONAL as per specification but REQUIRED by this implementation)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = OAuth2EndpointUtils\n\t\t\t.getParametersIfMatchesAuthorizationCodeGrantRequest(request, OAuth2ParameterNames.CLIENT_ASSERTION_TYPE,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ASSERTION, OAuth2ParameterNames.CLIENT_ID);\n\n\t\treturn new OAuth2ClientAuthenticationToken(clientId, JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, jwtAssertion,\n\t\t\t\tadditionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AccessTokenResponseAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.io.IOException;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * An implementation of an {@link AuthenticationSuccessHandler} used for handling an\n * {@link OAuth2AccessTokenAuthenticationToken} and returning the\n * {@link OAuth2AccessTokenResponse Access Token Response}.\n *\n * @author Dmitriy Dubson\n * @since 7.0\n * @see AuthenticationSuccessHandler\n * @see OAuth2AccessTokenResponseHttpMessageConverter\n */\npublic final class OAuth2AccessTokenResponseAuthenticationSuccessHandler implements AuthenticationSuccessHandler {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tprivate Consumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer;\n\n\t@Override\n\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException, ServletException {\n\t\tif (!(authentication instanceof OAuth2AccessTokenAuthenticationToken accessTokenAuthentication)) {\n\t\t\tif (this.logger.isErrorEnabled()) {\n\t\t\t\tthis.logger.error(Authentication.class.getSimpleName() + \" must be of type \"\n\t\t\t\t\t\t+ OAuth2AccessTokenAuthenticationToken.class.getName() + \" but was \"\n\t\t\t\t\t\t+ authentication.getClass().getName());\n\t\t\t}\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,\n\t\t\t\t\t\"Unable to process the access token response.\", null);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tOAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();\n\t\tOAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();\n\t\tMap<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();\n\n\t\tOAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())\n\t\t\t.tokenType(accessToken.getTokenType())\n\t\t\t.scopes(accessToken.getScopes());\n\t\tif (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {\n\t\t\tbuilder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));\n\t\t}\n\t\tif (refreshToken != null) {\n\t\t\tbuilder.refreshToken(refreshToken.getTokenValue());\n\t\t}\n\t\tif (!CollectionUtils.isEmpty(additionalParameters)) {\n\t\t\tbuilder.additionalParameters(additionalParameters);\n\t\t}\n\n\t\tif (this.accessTokenResponseCustomizer != null) {\n\t\t\t// @formatter:off\n\t\t\tOAuth2AccessTokenAuthenticationContext accessTokenAuthenticationContext =\n\t\t\t\t\tOAuth2AccessTokenAuthenticationContext.with(accessTokenAuthentication)\n\t\t\t\t\t\t.accessTokenResponse(builder)\n\t\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tthis.accessTokenResponseCustomizer.accept(accessTokenAuthenticationContext);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Customized access token response\");\n\t\t\t}\n\t\t}\n\n\t\tOAuth2AccessTokenResponse accessTokenResponse = builder.build();\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\tthis.accessTokenResponseConverter.write(accessTokenResponse, null, httpResponse);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2AccessTokenAuthenticationContext} containing an\n\t * {@link OAuth2AccessTokenResponse.Builder} and additional context information.\n\t * @param accessTokenResponseCustomizer the {@code Consumer} providing access to the\n\t * {@link OAuth2AccessTokenAuthenticationContext} containing an\n\t * {@link OAuth2AccessTokenResponse.Builder}\n\t */\n\tpublic void setAccessTokenResponseCustomizer(\n\t\t\tConsumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer) {\n\t\tAssert.notNull(accessTokenResponseCustomizer, \"accessTokenResponseCustomizer cannot be null\");\n\t\tthis.accessTokenResponseCustomizer = accessTokenResponseCustomizer;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the\n * OAuth 2.0 Authorization Code Grant and then converts it to an\n * {@link OAuth2AuthorizationCodeAuthenticationToken} used for authenticating the\n * authorization grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2AuthorizationCodeAuthenticationToken\n * @see OAuth2TokenEndpointFilter\n */\npublic final class OAuth2AuthorizationCodeAuthenticationConverter implements AuthenticationConverter {\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// grant_type (REQUIRED)\n\t\tString grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE);\n\t\tif (!AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(grantType)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\t// code (REQUIRED)\n\t\tString code = parameters.getFirst(OAuth2ParameterNames.CODE);\n\t\tif (!StringUtils.hasText(code) || parameters.get(OAuth2ParameterNames.CODE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CODE,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\n\t\t// redirect_uri (REQUIRED)\n\t\t// Required only if the \"redirect_uri\" parameter was included in the authorization\n\t\t// request\n\t\tString redirectUri = parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tif (StringUtils.hasText(redirectUri) && parameters.get(OAuth2ParameterNames.REDIRECT_URI).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.CODE) && !key.equals(OAuth2ParameterNames.REDIRECT_URI)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\t// Validate DPoP Proof HTTP Header (if available)\n\t\tOAuth2EndpointUtils.validateAndAddDPoPParametersIfAvailable(request, additionalParameters);\n\n\t\treturn new OAuth2AuthorizationCodeAuthenticationToken(code, clientPrincipal, redirectUri, additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeRequestAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2PushedAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2PushedAuthorizationRequestEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an Authorization Request from {@link HttpServletRequest} for the\n * OAuth 2.0 Authorization Code Grant and then converts it to an\n * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} OR\n * {@link OAuth2PushedAuthorizationRequestAuthenticationToken} used for authenticating the\n * request.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2AuthorizationCodeRequestAuthenticationToken\n * @see OAuth2PushedAuthorizationRequestAuthenticationToken\n * @see OAuth2AuthorizationEndpointFilter\n * @see OAuth2PushedAuthorizationRequestEndpointFilter\n */\npublic final class OAuth2AuthorizationCodeRequestAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final String DEFAULT_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1\";\n\n\tprivate static final String PKCE_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1\";\n\n\tprivate static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(\"anonymous\",\n\t\t\t\"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tprivate final RequestMatcher requestMatcher = createDefaultRequestMatcher();\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMultiValueMap<String, String> parameters = \"GET\".equals(request.getMethod())\n\t\t\t\t? OAuth2EndpointUtils.getQueryParameters(request) : OAuth2EndpointUtils.getFormParameters(request);\n\n\t\tboolean pushedAuthorizationRequest = isPushedAuthorizationRequest(request);\n\n\t\t// request_uri (OPTIONAL) - provided if an authorization request was previously\n\t\t// pushed (RFC 9126 OAuth 2.0 Pushed Authorization Requests)\n\t\tString requestUri = parameters.getFirst(OAuth2ParameterNames.REQUEST_URI);\n\t\tif (StringUtils.hasText(requestUri)) {\n\t\t\tif (pushedAuthorizationRequest) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REQUEST_URI);\n\t\t\t}\n\t\t\telse if (parameters.get(OAuth2ParameterNames.REQUEST_URI).size() != 1) {\n\t\t\t\t// Authorization Request\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REQUEST_URI);\n\t\t\t}\n\t\t}\n\n\t\tif (!StringUtils.hasText(requestUri)) {\n\t\t\t// response_type (REQUIRED)\n\t\t\tString responseType = parameters.getFirst(OAuth2ParameterNames.RESPONSE_TYPE);\n\t\t\tif (!StringUtils.hasText(responseType) || parameters.get(OAuth2ParameterNames.RESPONSE_TYPE).size() != 1) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.RESPONSE_TYPE);\n\t\t\t}\n\t\t\telse if (!responseType.equals(OAuth2AuthorizationResponseType.CODE.getValue())) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.UNSUPPORTED_RESPONSE_TYPE, OAuth2ParameterNames.RESPONSE_TYPE);\n\t\t\t}\n\t\t}\n\n\t\tString authorizationUri = request.getRequestURL().toString();\n\n\t\t// client_id (REQUIRED)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tAuthentication principal = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (principal == null) {\n\t\t\tprincipal = ANONYMOUS_AUTHENTICATION;\n\t\t}\n\n\t\t// redirect_uri (OPTIONAL)\n\t\tString redirectUri = parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI);\n\t\tif (StringUtils.hasText(redirectUri) && parameters.get(OAuth2ParameterNames.REDIRECT_URI).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI);\n\t\t}\n\n\t\t// scope (OPTIONAL)\n\t\tSet<String> scopes = null;\n\t\tString scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);\n\t\tif (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE);\n\t\t}\n\t\tif (StringUtils.hasText(scope)) {\n\t\t\tscopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, \" \")));\n\t\t}\n\n\t\t// state (RECOMMENDED)\n\t\tString state = parameters.getFirst(OAuth2ParameterNames.STATE);\n\t\tif (StringUtils.hasText(state) && parameters.get(OAuth2ParameterNames.STATE).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);\n\t\t}\n\n\t\t// code_challenge (REQUIRED for public clients) - RFC 7636 (PKCE)\n\t\tString codeChallenge = parameters.getFirst(PkceParameterNames.CODE_CHALLENGE);\n\t\tif (StringUtils.hasText(codeChallenge) && parameters.get(PkceParameterNames.CODE_CHALLENGE).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE, PKCE_ERROR_URI);\n\t\t}\n\n\t\t// code_challenge_method (OPTIONAL for public clients) - RFC 7636 (PKCE)\n\t\tString codeChallengeMethod = parameters.getFirst(PkceParameterNames.CODE_CHALLENGE_METHOD);\n\t\tif (StringUtils.hasText(codeChallengeMethod)\n\t\t\t\t&& parameters.get(PkceParameterNames.CODE_CHALLENGE_METHOD).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, PKCE_ERROR_URI);\n\t\t}\n\n\t\t// prompt (OPTIONAL for OpenID Connect 1.0 Authentication Request)\n\t\tif (!CollectionUtils.isEmpty(scopes) && scopes.contains(OidcScopes.OPENID)) {\n\t\t\tString prompt = parameters.getFirst(\"prompt\");\n\t\t\tif (StringUtils.hasText(prompt) && parameters.get(\"prompt\").size() != 1) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, \"prompt\");\n\t\t\t}\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.RESPONSE_TYPE) && !key.equals(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.REDIRECT_URI) && !key.equals(OAuth2ParameterNames.SCOPE)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.STATE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\tif (pushedAuthorizationRequest) {\n\t\t\treturn new OAuth2PushedAuthorizationRequestAuthenticationToken(authorizationUri, clientId, principal,\n\t\t\t\t\tredirectUri, state, scopes, additionalParameters);\n\t\t}\n\t\telse {\n\t\t\treturn new OAuth2AuthorizationCodeRequestAuthenticationToken(authorizationUri, clientId, principal,\n\t\t\t\t\tredirectUri, state, scopes, additionalParameters);\n\t\t}\n\t}\n\n\tprivate boolean isPushedAuthorizationRequest(HttpServletRequest request) {\n\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\tAuthorizationServerSettings authorizationServerSettings = authorizationServerContext\n\t\t\t.getAuthorizationServerSettings();\n\t\treturn request.getRequestURL()\n\t\t\t.toString()\n\t\t\t.toLowerCase(Locale.ROOT)\n\t\t\t.endsWith(authorizationServerSettings.getPushedAuthorizationRequestEndpoint().toLowerCase(Locale.ROOT));\n\t}\n\n\tprivate static RequestMatcher createDefaultRequestMatcher() {\n\t\tfinal RequestMatcher authorizationConsentMatcher = OAuth2AuthorizationConsentAuthenticationConverter\n\t\t\t.createDefaultRequestMatcher();\n\t\treturn (request) -> \"GET\".equals(request.getMethod())\n\t\t\t\t|| (\"POST\".equals(request.getMethod()) && !authorizationConsentMatcher.matches(request));\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tthrowError(errorCode, parameterName, DEFAULT_ERROR_URI);\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName, String errorUri) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, errorUri);\n\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationConsentAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an Authorization Consent from {@link HttpServletRequest} for the\n * OAuth 2.0 Authorization Code Grant and then converts it to an\n * {@link OAuth2AuthorizationConsentAuthenticationToken} used for authenticating the\n * request.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2AuthorizationConsentAuthenticationToken\n * @see OAuth2AuthorizationEndpointFilter\n */\npublic final class OAuth2AuthorizationConsentAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final String DEFAULT_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1\";\n\n\tprivate static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(\"anonymous\",\n\t\t\t\"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tprivate final RequestMatcher requestMatcher = createDefaultRequestMatcher();\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\tString authorizationUri = request.getRequestURL().toString();\n\n\t\t// client_id (REQUIRED)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tAuthentication principal = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (principal == null) {\n\t\t\tprincipal = ANONYMOUS_AUTHENTICATION;\n\t\t}\n\n\t\t// state (REQUIRED)\n\t\tString state = parameters.getFirst(OAuth2ParameterNames.STATE);\n\t\tif (!StringUtils.hasText(state) || parameters.get(OAuth2ParameterNames.STATE).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);\n\t\t}\n\n\t\t// scope (OPTIONAL)\n\t\tSet<String> scopes = null;\n\t\tif (parameters.containsKey(OAuth2ParameterNames.SCOPE)) {\n\t\t\tscopes = new HashSet<>(parameters.get(OAuth2ParameterNames.SCOPE));\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.CLIENT_ID) && !key.equals(OAuth2ParameterNames.STATE)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.SCOPE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\treturn new OAuth2AuthorizationConsentAuthenticationToken(authorizationUri, clientId, principal, state, scopes,\n\t\t\t\tadditionalParameters);\n\t}\n\n\tstatic RequestMatcher createDefaultRequestMatcher() {\n\t\treturn (request) -> \"POST\".equals(request.getMethod())\n\t\t\t\t&& request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) == null\n\t\t\t\t&& request.getParameter(OAuth2ParameterNames.REQUEST_URI) == null\n\t\t\t\t&& request.getParameter(OAuth2ParameterNames.REDIRECT_URI) == null\n\t\t\t\t&& request.getParameter(PkceParameterNames.CODE_CHALLENGE) == null\n\t\t\t\t&& request.getParameter(PkceParameterNames.CODE_CHALLENGE_METHOD) == null;\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, DEFAULT_ERROR_URI);\n\t\tthrow new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the\n * OAuth 2.0 Client Credentials Grant and then converts it to an\n * {@link OAuth2ClientCredentialsAuthenticationToken} used for authenticating the\n * authorization grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2ClientCredentialsAuthenticationToken\n * @see OAuth2TokenEndpointFilter\n */\npublic final class OAuth2ClientCredentialsAuthenticationConverter implements AuthenticationConverter {\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// grant_type (REQUIRED)\n\t\tString grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE);\n\t\tif (!AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(grantType)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\t// scope (OPTIONAL)\n\t\tString scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);\n\t\tif (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\t\tSet<String> requestedScopes = null;\n\t\tif (StringUtils.hasText(scope)) {\n\t\t\trequestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, \" \")));\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.SCOPE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\t// Validate DPoP Proof HTTP Header (if available)\n\t\tOAuth2EndpointUtils.validateAndAddDPoPParametersIfAvailable(request, additionalParameters);\n\n\t\treturn new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, requestedScopes, additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientRegistrationAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.http.converter.OAuth2ClientRegistrationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2ClientRegistrationEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\n\n/**\n * Attempts to extract an OAuth 2.0 Dynamic Client Registration Request from\n * {@link HttpServletRequest} and then converts to an\n * {@link OAuth2ClientRegistrationAuthenticationToken} used for authenticating the\n * request.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2ClientRegistrationAuthenticationToken\n * @see OAuth2ClientRegistrationEndpointFilter\n */\npublic final class OAuth2ClientRegistrationAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate final HttpMessageConverter<OAuth2ClientRegistration> clientRegistrationHttpMessageConverter = new OAuth2ClientRegistrationHttpMessageConverter();\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tAuthentication principal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\tOAuth2ClientRegistration clientRegistration;\n\t\ttry {\n\t\t\tclientRegistration = this.clientRegistrationHttpMessageConverter.read(OAuth2ClientRegistration.class,\n\t\t\t\t\tnew ServletServerHttpRequest(request));\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\"OAuth 2.0 Client Registration Error: \" + ex.getMessage(),\n\t\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2\");\n\t\t\tthrow new OAuth2AuthenticationException(error, ex);\n\t\t}\n\n\t\treturn new OAuth2ClientRegistrationAuthenticationToken(principal, clientRegistration);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2DeviceVerificationEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract a Device Authorization Consent from {@link HttpServletRequest} for\n * the OAuth 2.0 Device Authorization Grant and then converts it to an\n * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating the\n * request.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2DeviceAuthorizationConsentAuthenticationToken\n * @see OAuth2DeviceVerificationEndpointFilter\n */\npublic final class OAuth2DeviceAuthorizationConsentAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(\"anonymous\",\n\t\t\t\"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tif (!\"POST\".equals(request.getMethod()) || request.getParameter(OAuth2ParameterNames.STATE) == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\tString authorizationUri = request.getRequestURL().toString();\n\n\t\t// client_id (REQUIRED)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, ERROR_URI);\n\t\t}\n\n\t\tAuthentication principal = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (principal == null) {\n\t\t\tprincipal = ANONYMOUS_AUTHENTICATION;\n\t\t}\n\n\t\t// user_code (REQUIRED)\n\t\tString userCode = parameters.getFirst(OAuth2ParameterNames.USER_CODE);\n\t\tif (!OAuth2EndpointUtils.validateUserCode(userCode)\n\t\t\t\t|| parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.USER_CODE, ERROR_URI);\n\t\t}\n\n\t\t// state (REQUIRED)\n\t\tString state = parameters.getFirst(OAuth2ParameterNames.STATE);\n\t\tif (!StringUtils.hasText(state) || parameters.get(OAuth2ParameterNames.STATE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, ERROR_URI);\n\t\t}\n\n\t\t// scope (OPTIONAL)\n\t\tSet<String> scopes = null;\n\t\tif (parameters.containsKey(OAuth2ParameterNames.SCOPE)) {\n\t\t\tscopes = new HashSet<>(parameters.get(OAuth2ParameterNames.SCOPE));\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.CLIENT_ID) && !key.equals(OAuth2ParameterNames.USER_CODE)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.STATE) && !key.equals(OAuth2ParameterNames.SCOPE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\treturn new OAuth2DeviceAuthorizationConsentAuthenticationToken(authorizationUri, clientId, principal,\n\t\t\t\tOAuth2EndpointUtils.normalizeUserCode(userCode), state, scopes, additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2DeviceAuthorizationEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract a Device Authorization Request from {@link HttpServletRequest} for\n * the OAuth 2.0 Device Authorization Grant and then converts it to an\n * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating the\n * request.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2DeviceAuthorizationRequestAuthenticationToken\n * @see OAuth2DeviceAuthorizationEndpointFilter\n */\npublic final class OAuth2DeviceAuthorizationRequestAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc8628#section-3.1\";\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\tString authorizationUri = request.getRequestURL().toString();\n\n\t\t// scope (OPTIONAL)\n\t\tString scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);\n\t\tif (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE, ERROR_URI);\n\t\t}\n\t\tSet<String> requestedScopes = null;\n\t\tif (StringUtils.hasText(scope)) {\n\t\t\trequestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, \" \")));\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.CLIENT_ID) && !key.equals(OAuth2ParameterNames.SCOPE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\treturn new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, authorizationUri,\n\t\t\t\trequestedScopes, additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract a Device Access Token Request from {@link HttpServletRequest} for\n * the OAuth 2.0 Device Authorization Grant and then converts it to an\n * {@link OAuth2DeviceCodeAuthenticationToken} used for authenticating the authorization\n * grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2DeviceCodeAuthenticationToken\n * @see OAuth2TokenEndpointFilter\n */\npublic final class OAuth2DeviceCodeAuthenticationConverter implements AuthenticationConverter {\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// grant_type (REQUIRED)\n\t\tString grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE);\n\t\tif (!AuthorizationGrantType.DEVICE_CODE.getValue().equals(grantType)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\t// device_code (REQUIRED)\n\t\tString deviceCode = parameters.getFirst(OAuth2ParameterNames.DEVICE_CODE);\n\t\tif (!StringUtils.hasText(deviceCode) || parameters.get(OAuth2ParameterNames.DEVICE_CODE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.DEVICE_CODE,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.DEVICE_CODE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\t// Validate DPoP Proof HTTP Header (if available)\n\t\tOAuth2EndpointUtils.validateAndAddDPoPParametersIfAvailable(request, additionalParameters);\n\n\t\treturn new OAuth2DeviceCodeAuthenticationToken(deviceCode, clientPrincipal, additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2DeviceVerificationEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\n\n/**\n * Attempts to extract a user code from {@link HttpServletRequest} for the OAuth 2.0\n * Device Authorization Grant and then converts it to an\n * {@link OAuth2DeviceVerificationAuthenticationToken} used for authenticating the\n * request.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2DeviceVerificationAuthenticationToken\n * @see OAuth2DeviceVerificationEndpointFilter\n */\npublic final class OAuth2DeviceVerificationAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final String ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(\"anonymous\",\n\t\t\t\"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tif (!(\"GET\".equals(request.getMethod()) || \"POST\".equals(request.getMethod()))) {\n\t\t\treturn null;\n\t\t}\n\t\tif (request.getParameter(OAuth2ParameterNames.STATE) != null\n\t\t\t\t|| request.getParameter(OAuth2ParameterNames.USER_CODE) == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMultiValueMap<String, String> parameters = \"GET\".equals(request.getMethod())\n\t\t\t\t? OAuth2EndpointUtils.getQueryParameters(request) : OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// user_code (REQUIRED)\n\t\tString userCode = parameters.getFirst(OAuth2ParameterNames.USER_CODE);\n\t\tif (!OAuth2EndpointUtils.validateUserCode(userCode)\n\t\t\t\t|| parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.USER_CODE, ERROR_URI);\n\t\t}\n\n\t\tAuthentication principal = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (principal == null) {\n\t\t\tprincipal = ANONYMOUS_AUTHENTICATION;\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.USER_CODE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\treturn new OAuth2DeviceVerificationAuthenticationToken(principal,\n\t\t\t\tOAuth2EndpointUtils.normalizeUserCode(userCode), additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2EndpointUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Utility methods for the OAuth 2.0 Protocol Endpoints.\n *\n * @author Joe Grandja\n * @author Greg Li\n * @since 7.0\n */\nfinal class OAuth2EndpointUtils {\n\n\tstatic final String ACCESS_TOKEN_REQUEST_ERROR_URI = \"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2\";\n\n\tprivate OAuth2EndpointUtils() {\n\t}\n\n\tstatic MultiValueMap<String, String> getFormParameters(HttpServletRequest request) {\n\t\tMap<String, String[]> parameterMap = request.getParameterMap();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameterMap.forEach((key, values) -> {\n\t\t\tString queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : \"\";\n\t\t\t// If not query parameter then it's a form parameter\n\t\t\tif (!queryString.contains(key) && values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\tparameters.add(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn parameters;\n\t}\n\n\tstatic MultiValueMap<String, String> getQueryParameters(HttpServletRequest request) {\n\t\tMap<String, String[]> parameterMap = request.getParameterMap();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameterMap.forEach((key, values) -> {\n\t\t\tString queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : \"\";\n\t\t\tif (queryString.contains(key) && values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\tparameters.add(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn parameters;\n\t}\n\n\tstatic Map<String, Object> getParametersIfMatchesAuthorizationCodeGrantRequest(HttpServletRequest request,\n\t\t\tString... exclusions) {\n\t\tif (!matchesAuthorizationCodeGrantRequest(request)) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMultiValueMap<String, String> multiValueParameters = \"GET\".equals(request.getMethod())\n\t\t\t\t? getQueryParameters(request) : getFormParameters(request);\n\t\tfor (String exclusion : exclusions) {\n\t\t\tmultiValueParameters.remove(exclusion);\n\t\t}\n\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tmultiValueParameters.forEach(\n\t\t\t\t(key, value) -> parameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])));\n\n\t\treturn parameters;\n\t}\n\n\tstatic boolean matchesAuthorizationCodeGrantRequest(HttpServletRequest request) {\n\t\treturn AuthorizationGrantType.AUTHORIZATION_CODE.getValue()\n\t\t\t.equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))\n\t\t\t\t&& request.getParameter(OAuth2ParameterNames.CODE) != null;\n\t}\n\n\tstatic boolean matchesPkceTokenRequest(HttpServletRequest request) {\n\t\treturn matchesAuthorizationCodeGrantRequest(request)\n\t\t\t\t&& request.getParameter(PkceParameterNames.CODE_VERIFIER) != null;\n\t}\n\n\tstatic void validateAndAddDPoPParametersIfAvailable(HttpServletRequest request,\n\t\t\tMap<String, Object> additionalParameters) {\n\t\tfinal String dPoPProofHeaderName = OAuth2AccessToken.TokenType.DPOP.getValue();\n\t\tString dPoPProof = request.getHeader(dPoPProofHeaderName);\n\t\tif (StringUtils.hasText(dPoPProof)) {\n\t\t\tif (Collections.list(request.getHeaders(dPoPProofHeaderName)).size() != 1) {\n\t\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, dPoPProofHeaderName, ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tadditionalParameters.put(\"dpop_proof\", dPoPProof);\n\t\t\t\tadditionalParameters.put(\"dpop_method\", request.getMethod());\n\t\t\t\tadditionalParameters.put(\"dpop_target_uri\", request.getRequestURL().toString());\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic void throwError(String errorCode, String parameterName, String errorUri) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Parameter: \" + parameterName, errorUri);\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n\tstatic String normalizeUserCode(String userCode) {\n\t\tAssert.hasText(userCode, \"userCode cannot be empty\");\n\t\tStringBuilder sb = new StringBuilder(userCode.toUpperCase(Locale.ENGLISH).replaceAll(\"[^A-Z\\\\d]+\", \"\"));\n\t\tAssert.isTrue(sb.length() == 8, \"userCode must be exactly 8 alpha/numeric characters\");\n\t\tsb.insert(4, '-');\n\t\treturn sb.toString();\n\t}\n\n\tstatic boolean validateUserCode(String userCode) {\n\t\treturn (userCode != null && userCode.toUpperCase(Locale.ENGLISH).replaceAll(\"[^A-Z\\\\d]+\", \"\").length() == 8);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ErrorAuthenticationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AuthenticationFailureHandler} used for handling an\n * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error OAuth 2.0\n * Error Response}.\n *\n * @author Dmitriy Dubson\n * @since 7.0\n * @see AuthenticationFailureHandler\n * @see OAuth2ErrorHttpMessageConverter\n */\npublic final class OAuth2ErrorAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate HttpMessageConverter<OAuth2Error> errorResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\t@Override\n\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authenticationException) throws IOException, ServletException {\n\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\thttpResponse.setStatusCode(HttpStatus.BAD_REQUEST);\n\n\t\tif (authenticationException instanceof OAuth2AuthenticationException oauth2AuthenticationException) {\n\t\t\tOAuth2Error error = oauth2AuthenticationException.getError();\n\t\t\tthis.errorResponseConverter.write(error, null, httpResponse);\n\t\t}\n\t\telse {\n\t\t\tif (this.logger.isWarnEnabled()) {\n\t\t\t\tthis.logger.warn(AuthenticationException.class.getSimpleName() + \" must be of type \"\n\t\t\t\t\t\t+ OAuth2AuthenticationException.class.getName() + \" but was \"\n\t\t\t\t\t\t+ authenticationException.getClass().getName());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link HttpMessageConverter} used for converting an {@link OAuth2Error} to\n\t * an HTTP response.\n\t * @param errorResponseConverter the {@link HttpMessageConverter} used for converting\n\t * an {@link OAuth2Error} to an HTTP response\n\t */\n\tpublic void setErrorResponseConverter(HttpMessageConverter<OAuth2Error> errorResponseConverter) {\n\t\tAssert.notNull(errorResponseConverter, \"errorResponseConverter cannot be null\");\n\t\tthis.errorResponseConverter = errorResponseConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the\n * OAuth 2.0 Refresh Token Grant and then converts it to an\n * {@link OAuth2RefreshTokenAuthenticationToken} used for authenticating the authorization\n * grant.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2RefreshTokenAuthenticationToken\n * @see OAuth2TokenEndpointFilter\n */\npublic final class OAuth2RefreshTokenAuthenticationConverter implements AuthenticationConverter {\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// grant_type (REQUIRED)\n\t\tString grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE);\n\t\tif (!AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(grantType)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\t// refresh_token (REQUIRED)\n\t\tString refreshToken = parameters.getFirst(OAuth2ParameterNames.REFRESH_TOKEN);\n\t\tif (!StringUtils.hasText(refreshToken) || parameters.get(OAuth2ParameterNames.REFRESH_TOKEN).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REFRESH_TOKEN,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\n\t\t// scope (OPTIONAL)\n\t\tString scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);\n\t\tif (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\t\tSet<String> requestedScopes = null;\n\t\tif (StringUtils.hasText(scope)) {\n\t\t\trequestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, \" \")));\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.REFRESH_TOKEN)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.SCOPE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\t// Validate DPoP Proof HTTP Header (if available)\n\t\tOAuth2EndpointUtils.validateAndAddDPoPParametersIfAvailable(request, additionalParameters);\n\n\t\treturn new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal, requestedScopes,\n\t\t\t\tadditionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenExchangeAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the\n * OAuth 2.0 Token Exchange Grant and then converts it to an\n * {@link OAuth2TokenExchangeAuthenticationToken} used for authenticating the\n * authorization grant.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2TokenExchangeAuthenticationToken\n * @see OAuth2TokenEndpointFilter\n */\npublic final class OAuth2TokenExchangeAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate static final String TOKEN_TYPE_IDENTIFIERS_URI = \"https://datatracker.ietf.org/doc/html/rfc8693#section-3\";\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate static final Set<String> SUPPORTED_TOKEN_TYPES = Set.of(ACCESS_TOKEN_TYPE_VALUE, JWT_TOKEN_TYPE_VALUE);\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// grant_type (REQUIRED)\n\t\tString grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE);\n\t\tif (!AuthorizationGrantType.TOKEN_EXCHANGE.getValue().equals(grantType)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\t// resource (OPTIONAL)\n\t\tList<String> resources = parameters.getOrDefault(OAuth2ParameterNames.RESOURCE, Collections.emptyList());\n\t\tif (!CollectionUtils.isEmpty(resources)) {\n\t\t\tfor (String resource : resources) {\n\t\t\t\tif (!isValidUri(resource)) {\n\t\t\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.RESOURCE,\n\t\t\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// audience (OPTIONAL)\n\t\tList<String> audiences = parameters.getOrDefault(OAuth2ParameterNames.AUDIENCE, Collections.emptyList());\n\n\t\t// scope (OPTIONAL)\n\t\tString scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);\n\t\tif (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\n\t\tSet<String> requestedScopes = null;\n\t\tif (StringUtils.hasText(scope)) {\n\t\t\trequestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, \" \")));\n\t\t}\n\n\t\t// requested_token_type (OPTIONAL)\n\t\tString requestedTokenType = parameters.getFirst(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE);\n\t\tif (StringUtils.hasText(requestedTokenType)) {\n\t\t\tif (parameters.get(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE).size() != 1) {\n\t\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\tOAuth2ParameterNames.REQUESTED_TOKEN_TYPE, OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t\t}\n\n\t\t\tvalidateTokenType(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, requestedTokenType);\n\t\t}\n\t\telse {\n\t\t\trequestedTokenType = ACCESS_TOKEN_TYPE_VALUE;\n\t\t}\n\n\t\t// subject_token (REQUIRED)\n\t\tString subjectToken = parameters.getFirst(OAuth2ParameterNames.SUBJECT_TOKEN);\n\t\tif (!StringUtils.hasText(subjectToken) || parameters.get(OAuth2ParameterNames.SUBJECT_TOKEN).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SUBJECT_TOKEN,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\n\t\t// subject_token_type (REQUIRED)\n\t\tString subjectTokenType = parameters.getFirst(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE);\n\t\tif (!StringUtils.hasText(subjectTokenType)\n\t\t\t\t|| parameters.get(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SUBJECT_TOKEN_TYPE,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\t\telse {\n\t\t\tvalidateTokenType(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, subjectTokenType);\n\t\t}\n\n\t\t// actor_token (OPTIONAL, REQUIRED if actor_token_type is provided)\n\t\tString actorToken = parameters.getFirst(OAuth2ParameterNames.ACTOR_TOKEN);\n\t\tif (StringUtils.hasText(actorToken) && parameters.get(OAuth2ParameterNames.ACTOR_TOKEN).size() != 1) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.ACTOR_TOKEN,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\n\t\t// actor_token_type (OPTIONAL, REQUIRED if actor_token is provided)\n\t\tString actorTokenType = parameters.getFirst(OAuth2ParameterNames.ACTOR_TOKEN_TYPE);\n\t\tif (StringUtils.hasText(actorTokenType)) {\n\t\t\tif (parameters.get(OAuth2ParameterNames.ACTOR_TOKEN_TYPE).size() != 1) {\n\t\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.ACTOR_TOKEN_TYPE,\n\t\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t\t}\n\n\t\t\tvalidateTokenType(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, actorTokenType);\n\t\t}\n\n\t\tif (!StringUtils.hasText(actorToken) && StringUtils.hasText(actorTokenType)) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.ACTOR_TOKEN,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\t\telse if (StringUtils.hasText(actorToken) && !StringUtils.hasText(actorTokenType)) {\n\t\t\tOAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.ACTOR_TOKEN_TYPE,\n\t\t\t\t\tOAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.RESOURCE)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.AUDIENCE)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.SUBJECT_TOKEN)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.ACTOR_TOKEN)\n\t\t\t\t\t&& !key.equals(OAuth2ParameterNames.ACTOR_TOKEN_TYPE) && !key.equals(OAuth2ParameterNames.SCOPE)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\t// Validate DPoP Proof HTTP Header (if available)\n\t\tOAuth2EndpointUtils.validateAndAddDPoPParametersIfAvailable(request, additionalParameters);\n\n\t\treturn new OAuth2TokenExchangeAuthenticationToken(requestedTokenType, subjectToken, subjectTokenType,\n\t\t\t\tclientPrincipal, actorToken, actorTokenType, new LinkedHashSet<>(resources),\n\t\t\t\tnew LinkedHashSet<>(audiences), requestedScopes, additionalParameters);\n\t}\n\n\tprivate static void validateTokenType(String parameterName, String tokenTypeValue) {\n\t\tif (!SUPPORTED_TOKEN_TYPES.contains(tokenTypeValue)) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.UNSUPPORTED_TOKEN_TYPE,\n\t\t\t\t\tString.format(\"OAuth 2.0 Token Exchange parameter: %s\", parameterName), TOKEN_TYPE_IDENTIFIERS_URI);\n\t\t\t// @formatter:off\n\t\t\tString message = String.format(\n\t\t\t\t\t\"OAuth 2.0 Token Exchange parameter: %s - \" +\n\t\t\t\t\t\"The provided value is not supported by this authorization server. \" +\n\t\t\t\t\t\"Supported values are %s and %s.\",\n\t\t\t\t\tparameterName, ACCESS_TOKEN_TYPE_VALUE, JWT_TOKEN_TYPE_VALUE);\n\t\t\t// @formatter:on\n\t\t\tthrow new OAuth2AuthenticationException(error, message);\n\t\t}\n\t}\n\n\tprivate static boolean isValidUri(String uri) {\n\t\ttry {\n\t\t\tURI validUri = new URI(uri);\n\t\t\treturn validUri.isAbsolute() && validUri.getFragment() == null;\n\t\t}\n\t\tcatch (URISyntaxException ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenIntrospectionAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract an Introspection Request from {@link HttpServletRequest} and then\n * converts it to an {@link OAuth2TokenIntrospectionAuthenticationToken} used for\n * authenticating the request.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2TokenIntrospectionAuthenticationToken\n * @see OAuth2TokenIntrospectionEndpointFilter\n */\npublic final class OAuth2TokenIntrospectionAuthenticationConverter implements AuthenticationConverter {\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// token (REQUIRED)\n\t\tString token = parameters.getFirst(OAuth2ParameterNames.TOKEN);\n\t\tif (!StringUtils.hasText(token) || parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN);\n\t\t}\n\n\t\t// token_type_hint (OPTIONAL)\n\t\tString tokenTypeHint = parameters.getFirst(OAuth2ParameterNames.TOKEN_TYPE_HINT);\n\t\tif (StringUtils.hasText(tokenTypeHint) && parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN_TYPE_HINT);\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.TOKEN) && !key.equals(OAuth2ParameterNames.TOKEN_TYPE_HINT)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\treturn new OAuth2TokenIntrospectionAuthenticationToken(token, clientPrincipal, tokenTypeHint,\n\t\t\t\tadditionalParameters);\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Token Introspection Parameter: \" + parameterName,\n\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\");\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenRevocationAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract a Revoke Token Request from {@link HttpServletRequest} and then\n * converts it to an {@link OAuth2TokenRevocationAuthenticationToken} used for\n * authenticating the request.\n *\n * @author Vivek Babu\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2TokenRevocationAuthenticationToken\n * @see OAuth2TokenRevocationEndpointFilter\n */\npublic final class OAuth2TokenRevocationAuthenticationConverter implements AuthenticationConverter {\n\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// token (REQUIRED)\n\t\tString token = parameters.getFirst(OAuth2ParameterNames.TOKEN);\n\t\tif (!StringUtils.hasText(token) || parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN);\n\t\t}\n\n\t\t// token_type_hint (OPTIONAL)\n\t\tString tokenTypeHint = parameters.getFirst(OAuth2ParameterNames.TOKEN_TYPE_HINT);\n\t\tif (StringUtils.hasText(tokenTypeHint) && parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) {\n\t\t\tthrowError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN_TYPE_HINT);\n\t\t}\n\n\t\treturn new OAuth2TokenRevocationAuthenticationToken(token, clientPrincipal, tokenTypeHint);\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Token Revocation Parameter: \" + parameterName,\n\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\");\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract the parameters from {@link HttpServletRequest} used for\n * authenticating public clients using Proof Key for Code Exchange (PKCE).\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2ClientAuthenticationToken\n * @see OAuth2ClientAuthenticationFilter\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7636\">Proof Key for Code\n * Exchange by OAuth Public Clients</a>\n */\npublic final class PublicClientAuthenticationConverter implements AuthenticationConverter {\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tif (!OAuth2EndpointUtils.matchesPkceTokenRequest(request)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMultiValueMap<String, String> parameters = \"GET\".equals(request.getMethod())\n\t\t\t\t? OAuth2EndpointUtils.getQueryParameters(request) : OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// client_id (REQUIRED for public clients)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\t// code_verifier (REQUIRED)\n\t\tif (parameters.get(PkceParameterNames.CODE_VERIFIER).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tparameters.remove(OAuth2ParameterNames.CLIENT_ID);\n\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> additionalParameters.put(key,\n\t\t\t\t(value.size() == 1) ? value.get(0) : value.toArray(new String[0])));\n\n\t\treturn new OAuth2ClientAuthenticationToken(clientId, ClientAuthenticationMethod.NONE, null,\n\t\t\t\tadditionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/X509ClientCertificateAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.security.cert.X509Certificate;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Attempts to extract a client {@code X509Certificate} chain from\n * {@link HttpServletRequest} and then converts to an\n * {@link OAuth2ClientAuthenticationToken} used for authenticating the client using the\n * {@code tls_client_auth} or {@code self_signed_tls_client_auth} method.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see AuthenticationConverter\n * @see OAuth2ClientAuthenticationToken\n * @see OAuth2ClientAuthenticationFilter\n */\npublic final class X509ClientCertificateAuthenticationConverter implements AuthenticationConverter {\n\n\t@Nullable\n\t@Override\n\tpublic Authentication convert(HttpServletRequest request) {\n\t\tX509Certificate[] clientCertificateChain = (X509Certificate[]) request\n\t\t\t.getAttribute(\"jakarta.servlet.request.X509Certificate\");\n\t\tif (clientCertificateChain == null || clientCertificateChain.length == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);\n\n\t\t// client_id (REQUIRED)\n\t\tString clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);\n\t\tif (!StringUtils.hasText(clientId)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {\n\t\t\tthrow new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t}\n\n\t\tMap<String, Object> additionalParameters = OAuth2EndpointUtils\n\t\t\t.getParametersIfMatchesAuthorizationCodeGrantRequest(request, OAuth2ParameterNames.CLIENT_ID);\n\n\t\tClientAuthenticationMethod clientAuthenticationMethod = (clientCertificateChain.length == 1)\n\t\t\t\t? ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH : ClientAuthenticationMethod.TLS_CLIENT_AUTH;\n\n\t\treturn new OAuth2ClientAuthenticationToken(clientId, clientAuthenticationMethod, clientCertificateChain,\n\t\t\t\tadditionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\\\n org.springframework.security.oauth2.server.authorization.aot.hint.OAuth2AuthorizationServerBeanRegistrationAotProcessor\n\norg.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.oauth2.server.authorization.aot.hint.OAuth2AuthorizationServerRuntimeHints\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql",
    "content": "/*\nIMPORTANT:\n    If using PostgreSQL:\n        - update ALL columns defined with 'timestamp' to 'timestamptz', to ensure that time instants are stored accurately.\n    If using MySQL:\n        - add 'preserveInstants=true&connectionTimeZone=UTC&forceConnectionTimeZoneToSession=true' to JDBC connection URL\n          to ensure that time instants are stored accurately. See https://dev.mysql.com/doc/connector-j/en/connector-j-time-instants.html\n*/\nCREATE TABLE oauth2_registered_client (\n    id varchar(100) NOT NULL,\n    client_id varchar(100) NOT NULL,\n    client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    client_secret varchar(200) DEFAULT NULL,\n    client_secret_expires_at timestamp DEFAULT NULL,\n    client_name varchar(200) NOT NULL,\n    client_authentication_methods varchar(1000) NOT NULL,\n    authorization_grant_types varchar(1000) NOT NULL,\n    redirect_uris varchar(1000) DEFAULT NULL,\n    post_logout_redirect_uris varchar(1000) DEFAULT NULL,\n    scopes varchar(1000) NOT NULL,\n    client_settings varchar(2000) NOT NULL,\n    token_settings varchar(2000) NOT NULL,\n    PRIMARY KEY (id)\n);\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql",
    "content": "CREATE TABLE oauth2_authorization_consent (\n    registered_client_id varchar(100) NOT NULL,\n    principal_name varchar(200) NOT NULL,\n    authorities varchar(1000) NOT NULL,\n    PRIMARY KEY (registered_client_id, principal_name)\n);\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql",
    "content": "/*\nIMPORTANT:\n    If using PostgreSQL:\n        - update ALL columns defined with 'blob' to 'text', as PostgreSQL does not support the 'blob' data type.\n        - update ALL columns defined with 'timestamp' to 'timestamptz', to ensure that time instants are stored accurately.\n    If using MySQL:\n        - add 'preserveInstants=true&connectionTimeZone=UTC&forceConnectionTimeZoneToSession=true' to JDBC connection URL\n          to ensure that time instants are stored accurately. See https://dev.mysql.com/doc/connector-j/en/connector-j-time-instants.html\n*/\nCREATE TABLE oauth2_authorization (\n    id varchar(100) NOT NULL,\n    registered_client_id varchar(100) NOT NULL,\n    principal_name varchar(200) NOT NULL,\n    authorization_grant_type varchar(100) NOT NULL,\n    authorized_scopes varchar(1000) DEFAULT NULL,\n    attributes blob DEFAULT NULL,\n    state varchar(500) DEFAULT NULL,\n    authorization_code_value blob DEFAULT NULL,\n    authorization_code_issued_at timestamp DEFAULT NULL,\n    authorization_code_expires_at timestamp DEFAULT NULL,\n    authorization_code_metadata blob DEFAULT NULL,\n    access_token_value blob DEFAULT NULL,\n    access_token_issued_at timestamp DEFAULT NULL,\n    access_token_expires_at timestamp DEFAULT NULL,\n    access_token_metadata blob DEFAULT NULL,\n    access_token_type varchar(100) DEFAULT NULL,\n    access_token_scopes varchar(1000) DEFAULT NULL,\n    oidc_id_token_value blob DEFAULT NULL,\n    oidc_id_token_issued_at timestamp DEFAULT NULL,\n    oidc_id_token_expires_at timestamp DEFAULT NULL,\n    oidc_id_token_metadata blob DEFAULT NULL,\n    refresh_token_value blob DEFAULT NULL,\n    refresh_token_issued_at timestamp DEFAULT NULL,\n    refresh_token_expires_at timestamp DEFAULT NULL,\n    refresh_token_metadata blob DEFAULT NULL,\n    user_code_value blob DEFAULT NULL,\n    user_code_issued_at timestamp DEFAULT NULL,\n    user_code_expires_at timestamp DEFAULT NULL,\n    user_code_metadata blob DEFAULT NULL,\n    device_code_value blob DEFAULT NULL,\n    device_code_issued_at timestamp DEFAULT NULL,\n    device_code_expires_at timestamp DEFAULT NULL,\n    device_code_metadata blob DEFAULT NULL,\n    PRIMARY KEY (id)\n);\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link InMemoryOAuth2AuthorizationConsentService}.\n *\n * @author Daniel Garnier-Moiroux\n */\npublic class InMemoryOAuth2AuthorizationConsentServiceTests {\n\n\tprivate static final String REGISTERED_CLIENT_ID = \"registered-client-id\";\n\n\tprivate static final String PRINCIPAL_NAME = \"principal-name\";\n\n\tprivate static final OAuth2AuthorizationConsent AUTHORIZATION_CONSENT = OAuth2AuthorizationConsent\n\t\t.withId(REGISTERED_CLIENT_ID, PRINCIPAL_NAME)\n\t\t.authority(new SimpleGrantedAuthority(\"some.authority\"))\n\t\t.build();\n\n\tprivate InMemoryOAuth2AuthorizationConsentService authorizationConsentService;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizationConsentService = new InMemoryOAuth2AuthorizationConsentService();\n\t\tthis.authorizationConsentService.save(AUTHORIZATION_CONSENT);\n\t}\n\n\t@Test\n\tpublic void constructorVarargsWhenAuthorizationConsentNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryOAuth2AuthorizationConsentService((OAuth2AuthorizationConsent) null))\n\t\t\t.withMessage(\"authorizationConsent cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorListWhenAuthorizationConsentsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryOAuth2AuthorizationConsentService((List<OAuth2AuthorizationConsent>) null))\n\t\t\t.withMessage(\"authorizationConsents cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenDuplicateAuthorizationConsentsThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> new InMemoryOAuth2AuthorizationConsentService(AUTHORIZATION_CONSENT, AUTHORIZATION_CONSENT))\n\t\t\t.withMessage(\n\t\t\t\t\t\"The authorizationConsent must be unique. Found duplicate, with registered client id: [registered-client-id] and principal name: [principal-name]\");\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationConsentNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizationConsentService.save(null))\n\t\t\t.withMessage(\"authorizationConsent cannot be null\");\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationConsentNewThenSaved() {\n\t\tOAuth2AuthorizationConsent expectedAuthorizationConsent = OAuth2AuthorizationConsent\n\t\t\t.withId(\"new-client\", \"new-principal\")\n\t\t\t.authority(new SimpleGrantedAuthority(\"new.authority\"))\n\t\t\t.build();\n\n\t\tthis.authorizationConsentService.save(expectedAuthorizationConsent);\n\n\t\tOAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService.findById(\"new-client\",\n\t\t\t\t\"new-principal\");\n\t\tassertThat(authorizationConsent).isEqualTo(expectedAuthorizationConsent);\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationConsentExistsThenUpdated() {\n\t\tOAuth2AuthorizationConsent expectedAuthorizationConsent = OAuth2AuthorizationConsent.from(AUTHORIZATION_CONSENT)\n\t\t\t.authority(new SimpleGrantedAuthority(\"new.authority\"))\n\t\t\t.build();\n\n\t\tthis.authorizationConsentService.save(expectedAuthorizationConsent);\n\n\t\tOAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService\n\t\t\t.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName());\n\t\tassertThat(authorizationConsent).isEqualTo(expectedAuthorizationConsent);\n\t\tassertThat(authorizationConsent).isNotEqualTo(AUTHORIZATION_CONSENT);\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizationConsentNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizationConsentService.remove(null))\n\t\t\t.withMessage(\"authorizationConsent cannot be null\");\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizationConsentProvidedThenRemoved() {\n\t\tthis.authorizationConsentService.remove(AUTHORIZATION_CONSENT);\n\t\tassertThat(this.authorizationConsentService.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(),\n\t\t\t\tAUTHORIZATION_CONSENT.getPrincipalName()))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void findByIdWhenRegisteredClientIdNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizationConsentService.findById(null, \"some-user\"))\n\t\t\t.withMessage(\"registeredClientId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void findByIdWhenPrincipalNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizationConsentService.findById(\"some-client\", null))\n\t\t\t.withMessage(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void findByIdWhenAuthorizationConsentExistsThenFound() {\n\t\tassertThat(this.authorizationConsentService.findById(REGISTERED_CLIENT_ID, PRINCIPAL_NAME))\n\t\t\t.isEqualTo(AUTHORIZATION_CONSENT);\n\t}\n\n\t@Test\n\tpublic void findByIdWhenAuthorizationConsentDoesNotExistThenNull() {\n\t\tthis.authorizationConsentService.save(AUTHORIZATION_CONSENT);\n\t\tassertThat(this.authorizationConsentService.findById(\"unknown-client\", PRINCIPAL_NAME)).isNull();\n\t\tassertThat(this.authorizationConsentService.findById(REGISTERED_CLIENT_ID, \"unknown-user\")).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link InMemoryOAuth2AuthorizationService}.\n *\n * @author Krisztian Toth\n * @author Joe Grandja\n */\npublic class InMemoryOAuth2AuthorizationServiceTests {\n\n\tprivate static final String ID = \"id\";\n\n\tprivate static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build();\n\n\tprivate static final String PRINCIPAL_NAME = \"principal\";\n\n\tprivate static final AuthorizationGrantType AUTHORIZATION_GRANT_TYPE = AuthorizationGrantType.AUTHORIZATION_CODE;\n\n\tprivate static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode(\"code\", Instant.now(),\n\t\t\tInstant.now().plus(5, ChronoUnit.MINUTES));\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);\n\n\tprivate static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);\n\n\tprivate InMemoryOAuth2AuthorizationService authorizationService;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizationService = new InMemoryOAuth2AuthorizationService();\n\t}\n\n\t@Test\n\tpublic void constructorVarargsWhenAuthorizationNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new InMemoryOAuth2AuthorizationService((OAuth2Authorization) null))\n\t\t\t.withMessage(\"authorization cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorListWhenAuthorizationsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new InMemoryOAuth2AuthorizationService((List<OAuth2Authorization>) null))\n\t\t\t.withMessage(\"authorizations cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenDuplicateAuthorizationsThenThrowIllegalArgumentException() {\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new InMemoryOAuth2AuthorizationService(authorization, authorization))\n\t\t\t.withMessage(\"The authorization must be unique. Found duplicate identifier: id\");\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationService.save(null))\n\t\t\t.withMessage(\"authorization cannot be null\");\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationNewThenSaved() {\n\t\tOAuth2Authorization expectedAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tthis.authorizationService.save(expectedAuthorization);\n\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(),\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(expectedAuthorization);\n\t}\n\n\t// gh-222\n\t@Test\n\tpublic void saveWhenAuthorizationExistsThenUpdated() {\n\t\tOAuth2Authorization originalAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tthis.authorizationService.save(originalAuthorization);\n\n\t\tOAuth2Authorization authorization = this.authorizationService.findById(originalAuthorization.getId());\n\t\tassertThat(authorization).isEqualTo(originalAuthorization);\n\n\t\tOAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization)\n\t\t\t.attribute(\"custom-name-1\", \"custom-value-1\")\n\t\t\t.build();\n\t\tthis.authorizationService.save(updatedAuthorization);\n\n\t\tauthorization = this.authorizationService.findById(updatedAuthorization.getId());\n\t\tassertThat(authorization).isEqualTo(updatedAuthorization);\n\t\tassertThat(authorization).isNotEqualTo(originalAuthorization);\n\t}\n\n\t@Test\n\tpublic void saveWhenInitializedAuthorizationsReachMaxThenOldestRemoved() {\n\t\tint maxInitializedAuthorizations = 5;\n\t\tInMemoryOAuth2AuthorizationService authorizationService = new InMemoryOAuth2AuthorizationService(\n\t\t\t\tmaxInitializedAuthorizations);\n\n\t\tOAuth2Authorization initialAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID + \"-initial\")\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.attribute(OAuth2ParameterNames.STATE, \"state-initial\")\n\t\t\t.build();\n\t\tauthorizationService.save(initialAuthorization);\n\n\t\tOAuth2Authorization authorization = authorizationService.findById(initialAuthorization.getId());\n\t\tassertThat(authorization).isEqualTo(initialAuthorization);\n\t\tauthorization = authorizationService.findByToken(initialAuthorization.getAttribute(OAuth2ParameterNames.STATE),\n\t\t\t\tSTATE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(initialAuthorization);\n\n\t\tfor (int i = 0; i < maxInitializedAuthorizations; i++) {\n\t\t\tauthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t\t.id(ID + \"-\" + i)\n\t\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t\t.attribute(OAuth2ParameterNames.STATE, \"state-\" + i)\n\t\t\t\t.build();\n\t\t\tauthorizationService.save(authorization);\n\t\t}\n\n\t\tauthorization = authorizationService.findById(initialAuthorization.getId());\n\t\tassertThat(authorization).isNull();\n\t\tauthorization = authorizationService.findByToken(initialAuthorization.getAttribute(OAuth2ParameterNames.STATE),\n\t\t\t\tSTATE_TOKEN_TYPE);\n\t\tassertThat(authorization).isNull();\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizationNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizationService.remove(null))\n\t\t\t.withMessage(\"authorization cannot be null\");\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizationProvidedThenRemoved() {\n\t\tOAuth2Authorization expectedAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\n\t\tthis.authorizationService.save(expectedAuthorization);\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(),\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(expectedAuthorization);\n\n\t\tthis.authorizationService.remove(expectedAuthorization);\n\t\tauthorization = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(),\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isNull();\n\t}\n\n\t@Test\n\tpublic void findByIdWhenIdNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizationService.findById(null))\n\t\t\t.withMessage(\"id cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenTokenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizationService.findByToken(null, AUTHORIZATION_CODE_TOKEN_TYPE))\n\t\t\t.withMessage(\"token cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenStateExistsThenFound() {\n\t\tString state = \"state\";\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.attribute(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(state, null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenAuthorizationCodeExistsThenFound() {\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(),\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenAccessTokenExistsThenFound() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\t\tInstant.now().minusSeconds(60), Instant.now());\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.accessToken(accessToken)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(accessToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(accessToken.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenIdTokenExistsThenFound() {\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(\"subject\")\n\t\t\t.issuedAt(Instant.now().minusSeconds(60))\n\t\t\t.expiresAt(Instant.now())\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(idToken)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(idToken.getTokenValue(),\n\t\t\t\tID_TOKEN_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(idToken.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenRefreshTokenExistsThenFound() {\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", Instant.now());\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.refreshToken(refreshToken)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(refreshToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.REFRESH_TOKEN);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(refreshToken.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenWrongTokenTypeThenNotFound() {\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", Instant.now());\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.refreshToken(refreshToken)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(refreshToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenTokenDoesNotExistThenNull() {\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(\"access-token\",\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(result).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link JdbcOAuth2AuthorizationConsentService}.\n *\n * @author Ovidiu Popa\n */\npublic class JdbcOAuth2AuthorizationConsentServiceTests {\n\n\tprivate static final String OAUTH2_AUTHORIZATION_CONSENT_SCHEMA_SQL_RESOURCE = \"org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql\";\n\n\tprivate static final String CUSTOM_OAUTH2_AUTHORIZATION_CONSENT_SCHEMA_SQL_RESOURCE = \"org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-consent-schema.sql\";\n\n\tprivate static final String PRINCIPAL_NAME = \"principal-name\";\n\n\tprivate static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build();\n\n\tprivate static final OAuth2AuthorizationConsent AUTHORIZATION_CONSENT = OAuth2AuthorizationConsent\n\t\t.withId(REGISTERED_CLIENT.getId(), PRINCIPAL_NAME)\n\t\t.authority(new SimpleGrantedAuthority(\"SCOPE_scope1\"))\n\t\t.authority(new SimpleGrantedAuthority(\"SCOPE_scope2\"))\n\t\t.authority(new SimpleGrantedAuthority(\"SCOPE_scope3\"))\n\t\t.authority(new SimpleGrantedAuthority(\"authority-a\"))\n\t\t.authority(new SimpleGrantedAuthority(\"authority-b\"))\n\t\t.build();\n\n\tprivate EmbeddedDatabase db;\n\n\tprivate JdbcOperations jdbcOperations;\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate JdbcOAuth2AuthorizationConsentService authorizationConsentService;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.db = createDb();\n\t\tthis.jdbcOperations = new JdbcTemplate(this.db);\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationConsentService = new JdbcOAuth2AuthorizationConsentService(this.jdbcOperations,\n\t\t\t\tthis.registeredClientRepository);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.db.shutdown();\n\t}\n\n\t@Test\n\tpublic void constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new JdbcOAuth2AuthorizationConsentService(null, this.registeredClientRepository))\n\t\t\t\t.withMessage(\"jdbcOperations cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new JdbcOAuth2AuthorizationConsentService(this.jdbcOperations, null))\n\t\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizationConsentRowMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationConsentService.setAuthorizationConsentRowMapper(null))\n\t\t\t\t.withMessage(\"authorizationConsentRowMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizationConsentParametersMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationConsentService.setAuthorizationConsentParametersMapper(null))\n\t\t\t\t.withMessage(\"authorizationConsentParametersMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationConsentNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizationConsentService.save(null))\n\t\t\t\t.withMessage(\"authorizationConsent cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationConsentNewThenSaved() {\n\t\tOAuth2AuthorizationConsent expectedAuthorizationConsent = OAuth2AuthorizationConsent\n\t\t\t.withId(\"new-client\", \"new-principal\")\n\t\t\t.authority(new SimpleGrantedAuthority(\"new.authority\"))\n\t\t\t.build();\n\n\t\tRegisteredClient newRegisteredClient = TestRegisteredClients.registeredClient().id(\"new-client\").build();\n\n\t\tgiven(this.registeredClientRepository.findById(eq(newRegisteredClient.getId())))\n\t\t\t.willReturn(newRegisteredClient);\n\n\t\tthis.authorizationConsentService.save(expectedAuthorizationConsent);\n\n\t\tOAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService.findById(\"new-client\",\n\t\t\t\t\"new-principal\");\n\t\tassertThat(authorizationConsent).isEqualTo(expectedAuthorizationConsent);\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationConsentExistsThenUpdated() {\n\t\tOAuth2AuthorizationConsent expectedAuthorizationConsent = OAuth2AuthorizationConsent.from(AUTHORIZATION_CONSENT)\n\t\t\t.authority(new SimpleGrantedAuthority(\"new.authority\"))\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\n\t\tthis.authorizationConsentService.save(expectedAuthorizationConsent);\n\n\t\tOAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService\n\t\t\t.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName());\n\t\tassertThat(authorizationConsent).isEqualTo(expectedAuthorizationConsent);\n\t\tassertThat(authorizationConsent).isNotEqualTo(AUTHORIZATION_CONSENT);\n\t}\n\n\t@Test\n\tpublic void saveLoadAuthorizationConsentWhenCustomStrategiesSetThenCalled() throws Exception {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\n\t\tJdbcOAuth2AuthorizationConsentService.OAuth2AuthorizationConsentRowMapper authorizationConsentRowMapper = spy(\n\t\t\t\tnew JdbcOAuth2AuthorizationConsentService.OAuth2AuthorizationConsentRowMapper(\n\t\t\t\t\t\tthis.registeredClientRepository));\n\t\tthis.authorizationConsentService.setAuthorizationConsentRowMapper(authorizationConsentRowMapper);\n\t\tJdbcOAuth2AuthorizationConsentService.OAuth2AuthorizationConsentParametersMapper authorizationConsentParametersMapper = spy(\n\t\t\t\tnew JdbcOAuth2AuthorizationConsentService.OAuth2AuthorizationConsentParametersMapper());\n\t\tthis.authorizationConsentService.setAuthorizationConsentParametersMapper(authorizationConsentParametersMapper);\n\n\t\tthis.authorizationConsentService.save(AUTHORIZATION_CONSENT);\n\t\tOAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService\n\t\t\t.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName());\n\t\tassertThat(authorizationConsent).isEqualTo(AUTHORIZATION_CONSENT);\n\t\tverify(authorizationConsentRowMapper).mapRow(any(), anyInt());\n\t\tverify(authorizationConsentParametersMapper).apply(any());\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizationConsentNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizationConsentService.remove(null))\n\t\t\t.withMessage(\"authorizationConsent cannot be null\");\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizationConsentProvidedThenRemoved() {\n\t\tthis.authorizationConsentService.remove(AUTHORIZATION_CONSENT);\n\t\tassertThat(this.authorizationConsentService.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(),\n\t\t\t\tAUTHORIZATION_CONSENT.getPrincipalName()))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void findByIdWhenRegisteredClientIdNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizationConsentService.findById(null, \"some-user\"))\n\t\t\t.withMessage(\"registeredClientId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void findByIdWhenPrincipalNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizationConsentService.findById(\"some-client\", null))\n\t\t\t.withMessage(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void findByIdWhenAuthorizationConsentExistsThenFound() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\n\t\tthis.authorizationConsentService.save(AUTHORIZATION_CONSENT);\n\t\tOAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService\n\t\t\t.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName());\n\t\tassertThat(authorizationConsent).isNotNull();\n\t}\n\n\t@Test\n\tpublic void findByIdWhenAuthorizationConsentDoesNotExistThenNull() {\n\t\tthis.authorizationConsentService.save(AUTHORIZATION_CONSENT);\n\t\tassertThat(this.authorizationConsentService.findById(\"unknown-client\", PRINCIPAL_NAME)).isNull();\n\t\tassertThat(this.authorizationConsentService.findById(REGISTERED_CLIENT.getId(), \"unknown-user\")).isNull();\n\t}\n\n\t@Test\n\tpublic void tableDefinitionWhenCustomThenAbleToOverride() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\n\t\tEmbeddedDatabase db = createDb(CUSTOM_OAUTH2_AUTHORIZATION_CONSENT_SCHEMA_SQL_RESOURCE);\n\t\tOAuth2AuthorizationConsentService authorizationConsentService = new CustomJdbcOAuth2AuthorizationConsentService(\n\t\t\t\tnew JdbcTemplate(db), this.registeredClientRepository);\n\t\tauthorizationConsentService.save(AUTHORIZATION_CONSENT);\n\t\tOAuth2AuthorizationConsent foundAuthorizationConsent1 = authorizationConsentService\n\t\t\t.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName());\n\t\tassertThat(foundAuthorizationConsent1).isEqualTo(AUTHORIZATION_CONSENT);\n\t\tauthorizationConsentService.remove(AUTHORIZATION_CONSENT);\n\t\tOAuth2AuthorizationConsent foundAuthorizationConsent2 = authorizationConsentService\n\t\t\t.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName());\n\t\tassertThat(foundAuthorizationConsent2).isNull();\n\t\tdb.shutdown();\n\t}\n\n\tprivate static EmbeddedDatabase createDb() {\n\t\treturn createDb(OAUTH2_AUTHORIZATION_CONSENT_SCHEMA_SQL_RESOURCE);\n\t}\n\n\tprivate static EmbeddedDatabase createDb(String schema) {\n\t\t// @formatter:off\n\t\treturn new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(schema)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static final class CustomJdbcOAuth2AuthorizationConsentService\n\t\t\textends JdbcOAuth2AuthorizationConsentService {\n\n\t\t// @formatter:off\n\t\tprivate static final String COLUMN_NAMES = \"registeredClientId, \"\n\t\t\t\t+ \"principalName, \"\n\t\t\t\t+ \"authorities\";\n\t\t// @formatter:on\n\n\t\tprivate static final String TABLE_NAME = \"oauth2AuthorizationConsent\";\n\n\t\tprivate static final String PK_FILTER = \"registeredClientId = ? AND principalName = ?\";\n\n\t\t// @formatter:off\n\t\tprivate static final String LOAD_AUTHORIZATION_CONSENT_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t\t+ \" WHERE \" + PK_FILTER;\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tprivate static final String SAVE_AUTHORIZATION_CONSENT_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?)\";\n\t\t// @formatter:on\n\n\t\tprivate static final String REMOVE_AUTHORIZATION_CONSENT_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \"\n\t\t\t\t+ PK_FILTER;\n\n\t\tprivate CustomJdbcOAuth2AuthorizationConsentService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\tsuper(jdbcOperations, registeredClientRepository);\n\t\t\tsetAuthorizationConsentRowMapper(new CustomOAuth2AuthorizationConsentRowMapper(registeredClientRepository));\n\t\t}\n\n\t\t@Override\n\t\tpublic void save(OAuth2AuthorizationConsent authorizationConsent) {\n\t\t\tList<SqlParameterValue> parameters = getAuthorizationConsentParametersMapper().apply(authorizationConsent);\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\t\tgetJdbcOperations().update(SAVE_AUTHORIZATION_CONSENT_SQL, pss);\n\t\t}\n\n\t\t@Override\n\t\tpublic void remove(OAuth2AuthorizationConsent authorizationConsent) {\n\t\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, authorizationConsent.getRegisteredClientId()),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName()) };\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\t\tgetJdbcOperations().update(REMOVE_AUTHORIZATION_CONSENT_SQL, pss);\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {\n\t\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, registeredClientId),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, principalName) };\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\t\tList<OAuth2AuthorizationConsent> result = getJdbcOperations().query(LOAD_AUTHORIZATION_CONSENT_SQL, pss,\n\t\t\t\t\tgetAuthorizationConsentRowMapper());\n\t\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t\t}\n\n\t\tprivate static final class CustomOAuth2AuthorizationConsentRowMapper\n\t\t\t\textends JdbcOAuth2AuthorizationConsentService.OAuth2AuthorizationConsentRowMapper {\n\n\t\t\tprivate CustomOAuth2AuthorizationConsentRowMapper(RegisteredClientRepository registeredClientRepository) {\n\t\t\t\tsuper(registeredClientRepository);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic OAuth2AuthorizationConsent mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\t\tString registeredClientId = rs.getString(\"registeredClientId\");\n\t\t\t\tRegisteredClient registeredClient = getRegisteredClientRepository().findById(registeredClientId);\n\t\t\t\tif (registeredClient == null) {\n\t\t\t\t\tthrow new DataRetrievalFailureException(\"The RegisteredClient with id '\" + registeredClientId\n\t\t\t\t\t\t\t+ \"' was not found in the RegisteredClientRepository.\");\n\t\t\t\t}\n\n\t\t\t\tString principalName = rs.getString(\"principalName\");\n\n\t\t\t\tOAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId,\n\t\t\t\t\t\tprincipalName);\n\t\t\t\tString authorizationConsentAuthorities = rs.getString(\"authorities\");\n\t\t\t\tif (authorizationConsentAuthorities != null) {\n\t\t\t\t\tfor (String authority : StringUtils.commaDelimitedListToSet(authorizationConsentAuthorities)) {\n\t\t\t\t\t\tbuilder.authority(new SimpleGrantedAuthority(authority));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn builder.build();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.security.Principal;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link JdbcOAuth2AuthorizationService}.\n *\n * @author Ovidiu Popa\n * @author Steve Riesenberg\n */\npublic class JdbcOAuth2AuthorizationServiceTests {\n\n\tprivate static final String OAUTH2_AUTHORIZATION_SCHEMA_SQL_RESOURCE = \"org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql\";\n\n\tprivate static final String CUSTOM_OAUTH2_AUTHORIZATION_SCHEMA_SQL_RESOURCE = \"org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-schema.sql\";\n\n\tprivate static final String OAUTH2_AUTHORIZATION_SCHEMA_CLOB_DATA_TYPE_SQL_RESOURCE = \"org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-schema-clob-data-type.sql\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);\n\n\tprivate static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);\n\n\tprivate static final OAuth2TokenType USER_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.USER_CODE);\n\n\tprivate static final OAuth2TokenType DEVICE_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.DEVICE_CODE);\n\n\tprivate static final String ID = \"id\";\n\n\tprivate static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build();\n\n\tprivate static final String PRINCIPAL_NAME = \"principal\";\n\n\tprivate static final AuthorizationGrantType AUTHORIZATION_GRANT_TYPE = AuthorizationGrantType.AUTHORIZATION_CODE;\n\n\tprivate static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode(\"code\",\n\t\t\tInstant.now().truncatedTo(ChronoUnit.MILLIS),\n\t\t\tInstant.now().plus(5, ChronoUnit.MINUTES).truncatedTo(ChronoUnit.MILLIS));\n\n\tprivate EmbeddedDatabase db;\n\n\tprivate JdbcOperations jdbcOperations;\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate JdbcOAuth2AuthorizationService authorizationService;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.db = createDb();\n\t\tthis.jdbcOperations = new JdbcTemplate(this.db);\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = new JdbcOAuth2AuthorizationService(this.jdbcOperations,\n\t\t\t\tthis.registeredClientRepository);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.db.shutdown();\n\t}\n\n\t@Test\n\tpublic void constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new JdbcOAuth2AuthorizationService(null, this.registeredClientRepository))\n\t\t\t\t.withMessage(\"jdbcOperations cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new JdbcOAuth2AuthorizationService(this.jdbcOperations, null))\n\t\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenLobHandlerIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new JdbcOAuth2AuthorizationService(this.jdbcOperations, this.registeredClientRepository, null))\n\t\t\t\t.withMessage(\"lobHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizationRowMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationService.setAuthorizationRowMapper(null))\n\t\t\t\t.withMessage(\"authorizationRowMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizationParametersMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationService.setAuthorizationParametersMapper(null))\n\t\t\t\t.withMessage(\"authorizationParametersMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationService.save(null))\n\t\t\t\t.withMessage(\"authorization cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationNewThenSaved() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2Authorization expectedAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tthis.authorizationService.save(expectedAuthorization);\n\n\t\tOAuth2Authorization authorization = this.authorizationService.findById(ID);\n\t\tassertThat(authorization).isEqualTo(expectedAuthorization);\n\t}\n\n\t@Test\n\tpublic void saveWhenAuthorizationExistsThenUpdated() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2Authorization originalAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tthis.authorizationService.save(originalAuthorization);\n\n\t\tOAuth2Authorization authorization = this.authorizationService.findById(originalAuthorization.getId());\n\t\tassertThat(authorization).isEqualTo(originalAuthorization);\n\n\t\tOAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization)\n\t\t\t.attribute(\"custom-name-1\", \"custom-value-1\")\n\t\t\t.build();\n\t\tthis.authorizationService.save(updatedAuthorization);\n\n\t\tauthorization = this.authorizationService.findById(updatedAuthorization.getId());\n\t\tassertThat(authorization).isEqualTo(updatedAuthorization);\n\t\tassertThat(authorization).isNotEqualTo(originalAuthorization);\n\t}\n\n\t@Test\n\tpublic void saveLoadAuthorizationWhenCustomStrategiesSetThenCalled() throws Exception {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2Authorization originalAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\n\t\tRowMapper<OAuth2Authorization> authorizationRowMapper = spy(\n\t\t\t\tnew JdbcOAuth2AuthorizationService.JsonMapperOAuth2AuthorizationRowMapper(\n\t\t\t\t\t\tthis.registeredClientRepository));\n\t\tthis.authorizationService.setAuthorizationRowMapper(authorizationRowMapper);\n\t\tFunction<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper = spy(\n\t\t\t\tnew JdbcOAuth2AuthorizationService.JsonMapperOAuth2AuthorizationParametersMapper());\n\t\tthis.authorizationService.setAuthorizationParametersMapper(authorizationParametersMapper);\n\n\t\tthis.authorizationService.save(originalAuthorization);\n\t\tOAuth2Authorization authorization = this.authorizationService.findById(originalAuthorization.getId());\n\t\tassertThat(authorization).isEqualTo(originalAuthorization);\n\t\tverify(authorizationRowMapper).mapRow(any(), anyInt());\n\t\tverify(authorizationParametersMapper).apply(any());\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizationNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationService.remove(null))\n\t\t\t\t.withMessage(\"authorization cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void removeWhenAuthorizationProvidedThenRemoved() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2Authorization expectedAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\n\t\tthis.authorizationService.save(expectedAuthorization);\n\t\tOAuth2Authorization authorization = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(),\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(expectedAuthorization);\n\n\t\tthis.authorizationService.remove(authorization);\n\t\tauthorization = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(),\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isNull();\n\t}\n\n\t@Test\n\tpublic void findByIdWhenIdNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationService.findById(null))\n\t\t\t\t.withMessage(\"id cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void findByIdWhenIdEmptyThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationService.findById(\" \"))\n\t\t\t\t.withMessage(\"id cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenTokenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authorizationService.findByToken(null, AUTHORIZATION_CODE_TOKEN_TYPE))\n\t\t\t\t.withMessage(\"token cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenStateExistsThenFound() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tString state = \"state\";\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.attribute(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(state, null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenAuthorizationCodeExistsThenFound() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(),\n\t\t\t\tAUTHORIZATION_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenAccessTokenExistsThenFound() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\t\tInstant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS),\n\t\t\t\tInstant.now().truncatedTo(ChronoUnit.MILLIS));\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.accessToken(accessToken)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(accessToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(accessToken.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenIdTokenExistsThenFound() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(\"subject\")\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(idToken.getTokenValue(),\n\t\t\t\tID_TOKEN_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(idToken.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenRefreshTokenExistsThenFound() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\",\n\t\t\t\tInstant.now().truncatedTo(ChronoUnit.MILLIS),\n\t\t\t\tInstant.now().plus(5, ChronoUnit.MINUTES).truncatedTo(ChronoUnit.MILLIS));\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.refreshToken(refreshToken)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(refreshToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.REFRESH_TOKEN);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(refreshToken.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenDeviceCodeExistsThenFound() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2DeviceCode deviceCode = new OAuth2DeviceCode(\"device-code\", Instant.now().truncatedTo(ChronoUnit.MILLIS),\n\t\t\t\tInstant.now().plus(5, ChronoUnit.MINUTES).truncatedTo(ChronoUnit.MILLIS));\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(deviceCode)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(deviceCode.getTokenValue(),\n\t\t\t\tDEVICE_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(deviceCode.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenUserCodeExistsThenFound() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\t\tOAuth2UserCode userCode = new OAuth2UserCode(\"user-code\", Instant.now().truncatedTo(ChronoUnit.MILLIS),\n\t\t\t\tInstant.now().plus(5, ChronoUnit.MINUTES).truncatedTo(ChronoUnit.MILLIS));\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(userCode)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(userCode.getTokenValue(),\n\t\t\t\tUSER_CODE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t\tresult = this.authorizationService.findByToken(userCode.getTokenValue(), null);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenWrongTokenTypeThenNotFound() {\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\",\n\t\t\t\tInstant.now().truncatedTo(ChronoUnit.MILLIS));\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.refreshToken(refreshToken)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(refreshToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void findByTokenWhenTokenDoesNotExistThenNull() {\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(\"access-token\",\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(result).isNull();\n\t}\n\n\t// gh-18102\n\t@Test\n\tpublic void findByTokenWhenPrincipalHasWebAuthenticationDetailsThenDeserializes() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\n\t\tString state = \"state\";\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(PRINCIPAL_NAME, \"credentials\");\n\t\tprincipal.setDetails(new WebAuthenticationDetails(\"remoteAddress\", \"sessionId\"));\n\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.attribute(OAuth2ParameterNames.STATE, state)\n\t\t\t.attribute(Principal.class.getName(), principal)\n\t\t\t.build();\n\t\tthis.authorizationService.save(authorization);\n\n\t\tOAuth2Authorization result = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE);\n\t\tassertThat(authorization).isEqualTo(result);\n\t}\n\n\t@Test\n\tpublic void tableDefinitionWhenCustomThenAbleToOverride() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\n\t\tEmbeddedDatabase db = createDb(CUSTOM_OAUTH2_AUTHORIZATION_SCHEMA_SQL_RESOURCE);\n\t\tOAuth2AuthorizationService authorizationService = new CustomJdbcOAuth2AuthorizationService(new JdbcTemplate(db),\n\t\t\t\tthis.registeredClientRepository);\n\t\tString state = \"state\";\n\t\tOAuth2Authorization originalAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.attribute(OAuth2ParameterNames.STATE, state)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tauthorizationService.save(originalAuthorization);\n\t\tOAuth2Authorization foundAuthorization1 = authorizationService.findById(originalAuthorization.getId());\n\t\tassertThat(foundAuthorization1).isEqualTo(originalAuthorization);\n\t\tOAuth2Authorization foundAuthorization2 = authorizationService.findByToken(state, STATE_TOKEN_TYPE);\n\t\tassertThat(foundAuthorization2).isEqualTo(originalAuthorization);\n\t\tdb.shutdown();\n\t}\n\n\t@Test\n\tpublic void tableDefinitionWhenClobSqlTypeThenAuthorizationUpdated() {\n\t\tgiven(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).willReturn(REGISTERED_CLIENT);\n\n\t\tEmbeddedDatabase db = createDb(OAUTH2_AUTHORIZATION_SCHEMA_CLOB_DATA_TYPE_SQL_RESOURCE);\n\t\tOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(new JdbcTemplate(db),\n\t\t\t\tthis.registeredClientRepository);\n\t\tOAuth2Authorization originalAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tauthorizationService.save(originalAuthorization);\n\n\t\tOAuth2Authorization authorization = authorizationService.findById(originalAuthorization.getId());\n\t\tassertThat(authorization).isEqualTo(originalAuthorization);\n\n\t\tOAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization)\n\t\t\t.attribute(\"custom-name-1\", \"custom-value-1\")\n\t\t\t.build();\n\t\tauthorizationService.save(updatedAuthorization);\n\n\t\tauthorization = authorizationService.findById(updatedAuthorization.getId());\n\t\tassertThat(authorization).isEqualTo(updatedAuthorization);\n\t\tassertThat(authorization).isNotEqualTo(originalAuthorization);\n\t\tdb.shutdown();\n\t}\n\n\tprivate static EmbeddedDatabase createDb() {\n\t\treturn createDb(OAUTH2_AUTHORIZATION_SCHEMA_SQL_RESOURCE);\n\t}\n\n\tprivate static EmbeddedDatabase createDb(String schema) {\n\t\t// @formatter:off\n\t\treturn new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(schema)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static JsonMapper createSecurityMapper() {\n\t\treturn JsonMapper.builder()\n\t\t\t.addModules(SecurityJacksonModules.getModules(JdbcOAuth2AuthorizationServiceTests.class.getClassLoader()))\n\t\t\t.build();\n\t}\n\n\tprivate static final class CustomJdbcOAuth2AuthorizationService extends JdbcOAuth2AuthorizationService {\n\n\t\t// @formatter:off\n\t\tprivate static final String COLUMN_NAMES = \"id, \"\n\t\t\t\t+ \"registeredClientId, \"\n\t\t\t\t+ \"principalName, \"\n\t\t\t\t+ \"authorizationGrantType, \"\n\t\t\t\t+ \"authorizedScopes, \"\n\t\t\t\t+ \"attributes, \"\n\t\t\t\t+ \"state, \"\n\t\t\t\t+ \"authorizationCodeValue, \"\n\t\t\t\t+ \"authorizationCodeIssuedAt, \"\n\t\t\t\t+ \"authorizationCodeExpiresAt,\"\n\t\t\t\t+ \"authorizationCodeMetadata,\"\n\t\t\t\t+ \"accessTokenValue,\"\n\t\t\t\t+ \"accessTokenIssuedAt,\"\n\t\t\t\t+ \"accessTokenExpiresAt,\"\n\t\t\t\t+ \"accessTokenMetadata,\"\n\t\t\t\t+ \"accessTokenType,\"\n\t\t\t\t+ \"accessTokenScopes,\"\n\t\t\t\t+ \"oidcIdTokenValue,\"\n\t\t\t\t+ \"oidcIdTokenIssuedAt,\"\n\t\t\t\t+ \"oidcIdTokenExpiresAt,\"\n\t\t\t\t+ \"oidcIdTokenMetadata,\"\n\t\t\t\t+ \"refreshTokenValue,\"\n\t\t\t\t+ \"refreshTokenIssuedAt,\"\n\t\t\t\t+ \"refreshTokenExpiresAt,\"\n\t\t\t\t+ \"refreshTokenMetadata,\"\n\t\t\t\t+ \"userCodeValue,\"\n\t\t\t\t+ \"userCodeIssuedAt,\"\n\t\t\t\t+ \"userCodeExpiresAt,\"\n\t\t\t\t+ \"userCodeMetadata,\"\n\t\t\t\t+ \"deviceCodeValue,\"\n\t\t\t\t+ \"deviceCodeIssuedAt,\"\n\t\t\t\t+ \"deviceCodeExpiresAt,\"\n\t\t\t\t+ \"deviceCodeMetadata\";\n\t\t// @formatter:on\n\n\t\tprivate static final String TABLE_NAME = \"oauth2Authorization\";\n\n\t\tprivate static final String PK_FILTER = \"id = ?\";\n\n\t\tprivate static final String UNKNOWN_TOKEN_TYPE_FILTER = \"state = ? OR authorizationCodeValue = ? OR \"\n\t\t\t\t+ \"accessTokenValue = ? OR oidcIdTokenValue = ? OR refreshTokenValue = ? OR userCodeValue = ? OR \"\n\t\t\t\t+ \"deviceCodeValue = ?\";\n\n\t\t// @formatter:off\n\t\tprivate static final String LOAD_AUTHORIZATION_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t\t+ \" WHERE \";\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tprivate static final String SAVE_AUTHORIZATION_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\";\n\t\t// @formatter:on\n\n\t\tprivate static final String REMOVE_AUTHORIZATION_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + PK_FILTER;\n\n\t\tprivate CustomJdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\tsuper(jdbcOperations, registeredClientRepository);\n\t\t\tsetAuthorizationRowMapper(new CustomOAuth2AuthorizationRowMapper(registeredClientRepository));\n\t\t\tsetAuthorizationParametersMapper(new CustomOAuth2AuthorizationParametersMapper());\n\t\t}\n\n\t\t@Override\n\t\tpublic void save(OAuth2Authorization authorization) {\n\t\t\tList<SqlParameterValue> parameters = getAuthorizationParametersMapper().apply(authorization);\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\t\tgetJdbcOperations().update(SAVE_AUTHORIZATION_SQL, pss);\n\t\t}\n\n\t\t@Override\n\t\tpublic void remove(OAuth2Authorization authorization) {\n\t\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, authorization.getId()) };\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\t\tgetJdbcOperations().update(REMOVE_AUTHORIZATION_SQL, pss);\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2Authorization findById(String id) {\n\t\t\treturn findBy(PK_FILTER, id);\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType) {\n\t\t\treturn findBy(UNKNOWN_TOKEN_TYPE_FILTER, token, token, token, token, token, token, token);\n\t\t}\n\n\t\tprivate OAuth2Authorization findBy(String filter, Object... args) {\n\t\t\tList<OAuth2Authorization> result = getJdbcOperations().query(LOAD_AUTHORIZATION_SQL + filter,\n\t\t\t\t\tgetAuthorizationRowMapper(), args);\n\t\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t\t}\n\n\t\t@SuppressWarnings(\"removal\")\n\t\tprivate static final class CustomOAuth2AuthorizationRowMapper\n\t\t\t\textends JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper {\n\n\t\t\tprivate JsonMapper mapper;\n\n\t\t\tprivate CustomOAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {\n\t\t\t\tsuper(registeredClientRepository);\n\t\t\t\tthis.mapper = createSecurityMapper();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tpublic OAuth2Authorization mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\t\tString registeredClientId = rs.getString(\"registeredClientId\");\n\t\t\t\tRegisteredClient registeredClient = getRegisteredClientRepository().findById(registeredClientId);\n\t\t\t\tif (registeredClient == null) {\n\t\t\t\t\tthrow new DataRetrievalFailureException(\"The RegisteredClient with id '\" + registeredClientId\n\t\t\t\t\t\t\t+ \"' was not found in the RegisteredClientRepository.\");\n\t\t\t\t}\n\n\t\t\t\tOAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient);\n\t\t\t\tString id = rs.getString(\"id\");\n\t\t\t\tString principalName = rs.getString(\"principalName\");\n\t\t\t\tString authorizationGrantType = rs.getString(\"authorizationGrantType\");\n\t\t\t\tSet<String> authorizedScopes = Collections.emptySet();\n\t\t\t\tString authorizedScopesString = rs.getString(\"authorizedScopes\");\n\t\t\t\tif (authorizedScopesString != null) {\n\t\t\t\t\tauthorizedScopes = StringUtils.commaDelimitedListToSet(authorizedScopesString);\n\t\t\t\t}\n\t\t\t\tMap<String, Object> attributes = parseMap(rs.getString(\"attributes\"));\n\n\t\t\t\tbuilder.id(id)\n\t\t\t\t\t.principalName(principalName)\n\t\t\t\t\t.authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))\n\t\t\t\t\t.authorizedScopes(authorizedScopes)\n\t\t\t\t\t.attributes((attrs) -> attrs.putAll(attributes));\n\n\t\t\t\tString state = rs.getString(\"state\");\n\t\t\t\tif (StringUtils.hasText(state)) {\n\t\t\t\t\tbuilder.attribute(OAuth2ParameterNames.STATE, state);\n\t\t\t\t}\n\n\t\t\t\tString tokenValue = rs.getString(\"authorizationCodeValue\");\n\t\t\t\tInstant tokenIssuedAt;\n\t\t\t\tInstant tokenExpiresAt;\n\t\t\t\tif (tokenValue != null) {\n\t\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"authorizationCodeIssuedAt\").toInstant();\n\t\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"authorizationCodeExpiresAt\").toInstant();\n\t\t\t\t\tMap<String, Object> authorizationCodeMetadata = parseMap(rs.getString(\"authorizationCodeMetadata\"));\n\n\t\t\t\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(tokenValue, tokenIssuedAt,\n\t\t\t\t\t\t\ttokenExpiresAt);\n\t\t\t\t\tbuilder.token(authorizationCode, (metadata) -> metadata.putAll(authorizationCodeMetadata));\n\t\t\t\t}\n\n\t\t\t\ttokenValue = rs.getString(\"accessTokenValue\");\n\t\t\t\tif (tokenValue != null) {\n\t\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"accessTokenIssuedAt\").toInstant();\n\t\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"accessTokenExpiresAt\").toInstant();\n\t\t\t\t\tMap<String, Object> accessTokenMetadata = parseMap(rs.getString(\"accessTokenMetadata\"));\n\t\t\t\t\tOAuth2AccessToken.TokenType tokenType = null;\n\t\t\t\t\tif (OAuth2AccessToken.TokenType.BEARER.getValue()\n\t\t\t\t\t\t.equalsIgnoreCase(rs.getString(\"accessTokenType\"))) {\n\t\t\t\t\t\ttokenType = OAuth2AccessToken.TokenType.BEARER;\n\t\t\t\t\t}\n\n\t\t\t\t\tSet<String> scopes = Collections.emptySet();\n\t\t\t\t\tString accessTokenScopes = rs.getString(\"accessTokenScopes\");\n\t\t\t\t\tif (accessTokenScopes != null) {\n\t\t\t\t\t\tscopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);\n\t\t\t\t\t}\n\t\t\t\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, tokenIssuedAt,\n\t\t\t\t\t\t\ttokenExpiresAt, scopes);\n\t\t\t\t\tbuilder.token(accessToken, (metadata) -> metadata.putAll(accessTokenMetadata));\n\t\t\t\t}\n\n\t\t\t\ttokenValue = rs.getString(\"oidcIdTokenValue\");\n\t\t\t\tif (tokenValue != null) {\n\t\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"oidcIdTokenIssuedAt\").toInstant();\n\t\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"oidcIdTokenExpiresAt\").toInstant();\n\t\t\t\t\tMap<String, Object> oidcTokenMetadata = parseMap(rs.getString(\"oidcIdTokenMetadata\"));\n\n\t\t\t\t\tOidcIdToken oidcToken = new OidcIdToken(tokenValue, tokenIssuedAt, tokenExpiresAt,\n\t\t\t\t\t\t\t(Map<String, Object>) oidcTokenMetadata\n\t\t\t\t\t\t\t\t.get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME));\n\t\t\t\t\tbuilder.token(oidcToken, (metadata) -> metadata.putAll(oidcTokenMetadata));\n\t\t\t\t}\n\n\t\t\t\ttokenValue = rs.getString(\"refreshTokenValue\");\n\t\t\t\tif (tokenValue != null) {\n\t\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"refreshTokenIssuedAt\").toInstant();\n\t\t\t\t\ttokenExpiresAt = null;\n\t\t\t\t\tTimestamp refreshTokenExpiresAt = rs.getTimestamp(\"refreshTokenExpiresAt\");\n\t\t\t\t\tif (refreshTokenExpiresAt != null) {\n\t\t\t\t\t\ttokenExpiresAt = refreshTokenExpiresAt.toInstant();\n\t\t\t\t\t}\n\t\t\t\t\tMap<String, Object> refreshTokenMetadata = parseMap(rs.getString(\"refreshTokenMetadata\"));\n\n\t\t\t\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(tokenValue, tokenIssuedAt, tokenExpiresAt);\n\t\t\t\t\tbuilder.token(refreshToken, (metadata) -> metadata.putAll(refreshTokenMetadata));\n\t\t\t\t}\n\n\t\t\t\ttokenValue = rs.getString(\"userCodeValue\");\n\t\t\t\tif (tokenValue != null) {\n\t\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"userCodeIssuedAt\").toInstant();\n\t\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"userCodeExpiresAt\").toInstant();\n\t\t\t\t\tMap<String, Object> userCodeMetadata = parseMap(rs.getString(\"userCodeMetadata\"));\n\n\t\t\t\t\tOAuth2UserCode userCode = new OAuth2UserCode(tokenValue, tokenIssuedAt, tokenExpiresAt);\n\t\t\t\t\tbuilder.token(userCode, (metadata) -> metadata.putAll(userCodeMetadata));\n\t\t\t\t}\n\n\t\t\t\ttokenValue = rs.getString(\"deviceCodeValue\");\n\t\t\t\tif (tokenValue != null) {\n\t\t\t\t\ttokenIssuedAt = rs.getTimestamp(\"deviceCodeIssuedAt\").toInstant();\n\t\t\t\t\ttokenExpiresAt = rs.getTimestamp(\"deviceCodeExpiresAt\").toInstant();\n\t\t\t\t\tMap<String, Object> deviceCodeMetadata = parseMap(rs.getString(\"deviceCodeMetadata\"));\n\n\t\t\t\t\tOAuth2UserCode deviceCode = new OAuth2UserCode(tokenValue, tokenIssuedAt, tokenExpiresAt);\n\t\t\t\t\tbuilder.token(deviceCode, (metadata) -> metadata.putAll(deviceCodeMetadata));\n\t\t\t\t}\n\n\t\t\t\treturn builder.build();\n\t\t\t}\n\n\t\t\tprivate Map<String, Object> parseMap(String data) {\n\t\t\t\ttry {\n\t\t\t\t\treturn this.mapper.readValue(data, new TypeReference<>() {\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new IllegalArgumentException(ex.getMessage(), ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\t@SuppressWarnings(\"removal\")\n\t\tprivate static final class CustomOAuth2AuthorizationParametersMapper\n\t\t\t\textends JdbcOAuth2AuthorizationService.OAuth2AuthorizationParametersMapper {\n\n\t\t\tprivate final JsonMapper mapper = createSecurityMapper();\n\n\t\t\t@Override\n\t\t\tpublic List<SqlParameterValue> apply(OAuth2Authorization authorization) {\n\t\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getId()));\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getRegisteredClientId()));\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getPrincipalName()));\n\t\t\t\tparameters\n\t\t\t\t\t.add(new SqlParameterValue(Types.VARCHAR, authorization.getAuthorizationGrantType().getValue()));\n\n\t\t\t\tString authorizedScopes = null;\n\t\t\t\tif (!CollectionUtils.isEmpty(authorization.getAuthorizedScopes())) {\n\t\t\t\t\tauthorizedScopes = StringUtils.collectionToDelimitedString(authorization.getAuthorizedScopes(),\n\t\t\t\t\t\t\t\",\");\n\t\t\t\t}\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, authorizedScopes));\n\n\t\t\t\tString attributes = writeMap(authorization.getAttributes());\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, attributes));\n\n\t\t\t\tString state = null;\n\t\t\t\tString authorizationState = authorization.getAttribute(OAuth2ParameterNames.STATE);\n\t\t\t\tif (StringUtils.hasText(authorizationState)) {\n\t\t\t\t\tstate = authorizationState;\n\t\t\t\t}\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, state));\n\n\t\t\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization\n\t\t\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\t\t\tList<SqlParameterValue> authorizationCodeSqlParameters = toSqlParameterList(authorizationCode);\n\t\t\t\tparameters.addAll(authorizationCodeSqlParameters);\n\n\t\t\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization\n\t\t\t\t\t.getToken(OAuth2AccessToken.class);\n\t\t\t\tList<SqlParameterValue> accessTokenSqlParameters = toSqlParameterList(accessToken);\n\t\t\t\tparameters.addAll(accessTokenSqlParameters);\n\t\t\t\tString accessTokenType = null;\n\t\t\t\tString accessTokenScopes = null;\n\t\t\t\tif (accessToken != null) {\n\t\t\t\t\taccessTokenType = accessToken.getToken().getTokenType().getValue();\n\t\t\t\t\tif (!CollectionUtils.isEmpty(accessToken.getToken().getScopes())) {\n\t\t\t\t\t\taccessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(),\n\t\t\t\t\t\t\t\t\",\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenType));\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenScopes));\n\n\t\t\t\tOAuth2Authorization.Token<OidcIdToken> oidcIdToken = authorization.getToken(OidcIdToken.class);\n\t\t\t\tList<SqlParameterValue> oidcIdTokenSqlParameters = toSqlParameterList(oidcIdToken);\n\t\t\t\tparameters.addAll(oidcIdTokenSqlParameters);\n\n\t\t\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getRefreshToken();\n\t\t\t\tList<SqlParameterValue> refreshTokenSqlParameters = toSqlParameterList(refreshToken);\n\t\t\t\tparameters.addAll(refreshTokenSqlParameters);\n\n\t\t\t\tOAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);\n\t\t\t\tList<SqlParameterValue> userCodeSqlParameters = toSqlParameterList(userCode);\n\t\t\t\tparameters.addAll(userCodeSqlParameters);\n\n\t\t\t\tOAuth2Authorization.Token<OAuth2DeviceCode> deviceCode = authorization.getToken(OAuth2DeviceCode.class);\n\t\t\t\tList<SqlParameterValue> deviceCodeSqlParameters = toSqlParameterList(deviceCode);\n\t\t\t\tparameters.addAll(deviceCodeSqlParameters);\n\n\t\t\t\treturn parameters;\n\t\t\t}\n\n\t\t\tprivate <T extends OAuth2Token> List<SqlParameterValue> toSqlParameterList(\n\t\t\t\t\tOAuth2Authorization.Token<T> token) {\n\t\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\t\t\tString tokenValue = null;\n\t\t\t\tTimestamp tokenIssuedAt = null;\n\t\t\t\tTimestamp tokenExpiresAt = null;\n\t\t\t\tString metadata = null;\n\t\t\t\tif (token != null) {\n\t\t\t\t\ttokenValue = token.getToken().getTokenValue();\n\t\t\t\t\tif (token.getToken().getIssuedAt() != null) {\n\t\t\t\t\t\ttokenIssuedAt = Timestamp.from(token.getToken().getIssuedAt());\n\t\t\t\t\t}\n\t\t\t\t\tif (token.getToken().getExpiresAt() != null) {\n\t\t\t\t\t\ttokenExpiresAt = Timestamp.from(token.getToken().getExpiresAt());\n\t\t\t\t\t}\n\t\t\t\t\tmetadata = writeMap(token.getMetadata());\n\t\t\t\t}\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, tokenValue));\n\t\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP, tokenIssuedAt));\n\t\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP, tokenExpiresAt));\n\t\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, metadata));\n\t\t\t\treturn parameters;\n\t\t\t}\n\n\t\t\tprivate String writeMap(Map<String, Object> data) {\n\t\t\t\ttry {\n\t\t\t\t\treturn this.mapper.writeValueAsString(data);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new IllegalArgumentException(ex.getMessage(), ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizationConsent}.\n *\n * @author Daniel Garnier-Moiroux\n */\npublic class OAuth2AuthorizationConsentTests {\n\n\t@Test\n\tpublic void fromWhenAuthorizationConsentNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationConsent.from(null))\n\t\t\t.withMessage(\"authorizationConsent cannot be null\");\n\t}\n\n\t@Test\n\tpublic void withIdWhenRegisteredClientIdNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationConsent.withId(null, \"some-user\"))\n\t\t\t.withMessage(\"registeredClientId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void withIdWhenPrincipalNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationConsent.withId(\"some-client\", null))\n\t\t\t.withMessage(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthoritiesEmptyThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(\"some-client\", \"some-user\");\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"authorities cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAllAttributesAreProvidedThenAllAttributesAreSet() {\n\t\tOAuth2AuthorizationConsent authorizationConsent = OAuth2AuthorizationConsent.withId(\"some-client\", \"some-user\")\n\t\t\t.scope(\"resource.read\")\n\t\t\t.scope(\"resource.write\")\n\t\t\t.authority(new SimpleGrantedAuthority(\"CLAIM_email\"))\n\t\t\t.build();\n\n\t\tassertThat(authorizationConsent.getRegisteredClientId()).isEqualTo(\"some-client\");\n\t\tassertThat(authorizationConsent.getPrincipalName()).isEqualTo(\"some-user\");\n\t\tassertThat(authorizationConsent.getScopes()).containsExactlyInAnyOrder(\"resource.read\", \"resource.write\");\n\t\tassertThat(authorizationConsent.getAuthorities()).containsExactlyInAnyOrder(\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_resource.read\"), new SimpleGrantedAuthority(\"SCOPE_resource.write\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"CLAIM_email\"));\n\t}\n\n\t@Test\n\tpublic void fromWhenAuthorizationConsentProvidedThenCopied() {\n\t\tOAuth2AuthorizationConsent previousAuthorizationConsent = OAuth2AuthorizationConsent\n\t\t\t.withId(\"some-client\", \"some-principal\")\n\t\t\t.scope(\"first.scope\")\n\t\t\t.scope(\"second.scope\")\n\t\t\t.authority(new SimpleGrantedAuthority(\"CLAIM_email\"))\n\t\t\t.build();\n\n\t\tOAuth2AuthorizationConsent authorizationConsent = OAuth2AuthorizationConsent.from(previousAuthorizationConsent)\n\t\t\t.build();\n\n\t\tassertThat(authorizationConsent.getRegisteredClientId()).isEqualTo(\"some-client\");\n\t\tassertThat(authorizationConsent.getPrincipalName()).isEqualTo(\"some-principal\");\n\t\tassertThat(authorizationConsent.getAuthorities()).containsExactlyInAnyOrder(\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_first.scope\"), new SimpleGrantedAuthority(\"SCOPE_second.scope\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"CLAIM_email\"));\n\t}\n\n\t@Test\n\tpublic void authoritiesThenCustomizesAuthorities() {\n\t\tOAuth2AuthorizationConsent authorizationConsent = OAuth2AuthorizationConsent.withId(\"some-client\", \"some-user\")\n\t\t\t.authority(new SimpleGrantedAuthority(\"some.authority\"))\n\t\t\t.authorities((authorities) -> {\n\t\t\t\tauthorities.clear();\n\t\t\t\tauthorities.add(new SimpleGrantedAuthority(\"other.authority\"));\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationConsent.getAuthorities())\n\t\t\t.containsExactly(new SimpleGrantedAuthority(\"other.authority\"));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata.Builder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizationServerMetadata}.\n *\n * @author Daniel Garnier-Moiroux\n */\npublic class OAuth2AuthorizationServerMetadataTests {\n\n\t// @formatter:off\n\tprivate final Builder minimalBuilder =\n\t\t\tOAuth2AuthorizationServerMetadata.builder()\n\t\t\t\t\t.issuer(\"https://example.com\")\n\t\t\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t\t\t.responseType(\"code\");\n\t// @formatter:on\n\n\t@Test\n\tpublic void buildWhenAllClaimsProvidedThenCreated() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t.pushedAuthorizationRequestEndpoint(\"https://example.com/oauth2/par\")\n\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t.jwkSetUrl(\"https://example.com/oauth2/jwks\")\n\t\t\t.scope(\"openid\")\n\t\t\t.responseType(\"code\")\n\t\t\t.grantType(\"authorization_code\")\n\t\t\t.grantType(\"client_credentials\")\n\t\t\t.tokenRevocationEndpoint(\"https://example.com/oauth2/revoke\")\n\t\t\t.tokenRevocationEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t.tokenIntrospectionEndpoint(\"https://example.com/oauth2/introspect\")\n\t\t\t.tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t.codeChallengeMethod(\"S256\")\n\t\t\t.tlsClientCertificateBoundAccessTokens(true)\n\t\t\t.dPoPSigningAlgorithm(JwsAlgorithms.RS256)\n\t\t\t.dPoPSigningAlgorithm(JwsAlgorithms.ES256)\n\t\t\t.claim(\"a-claim\", \"a-value\")\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url(\"https://example.com\"));\n\t\tassertThat(authorizationServerMetadata.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(authorizationServerMetadata.getPushedAuthorizationRequestEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/par\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url(\"https://example.com/oauth2/token\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(url(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(authorizationServerMetadata.getScopes()).containsExactly(\"openid\");\n\t\tassertThat(authorizationServerMetadata.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(authorizationServerMetadata.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/revoke\"));\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/introspect\"));\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly(\"S256\");\n\t\tassertThat(authorizationServerMetadata.isTlsClientCertificateBoundAccessTokens()).isTrue();\n\t\tassertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).containsExactly(JwsAlgorithms.RS256,\n\t\t\t\tJwsAlgorithms.ES256);\n\t\tassertThat(authorizationServerMetadata.getClaimAsString(\"a-claim\")).isEqualTo(\"a-value\");\n\t}\n\n\t@Test\n\tpublic void buildWhenOnlyRequiredClaimsProvidedThenCreated() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t.responseType(\"code\")\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url(\"https://example.com\"));\n\t\tassertThat(authorizationServerMetadata.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(authorizationServerMetadata.getPushedAuthorizationRequestEndpoint()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url(\"https://example.com/oauth2/token\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getJwkSetUrl()).isNull();\n\t\tassertThat(authorizationServerMetadata.getScopes()).isNull();\n\t\tassertThat(authorizationServerMetadata.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(authorizationServerMetadata.getGrantTypes()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpoint()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).isNull();\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenClaimsProvidedThenCreated() {\n\t\tHashMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, \"https://example.com\");\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT,\n\t\t\t\t\"https://example.com/oauth2/authorize\");\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,\n\t\t\t\t\"https://example.com/oauth2/par\");\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, \"https://example.com/oauth2/token\");\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, \"https://example.com/oauth2/jwks\");\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, Collections.singletonList(\"openid\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED,\n\t\t\t\tCollections.singletonList(\"code\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT,\n\t\t\t\t\"https://example.com/oauth2/revoke\");\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT,\n\t\t\t\t\"https://example.com/oauth2/introspect\");\n\t\tclaims.put(\"some-claim\", \"some-value\");\n\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata\n\t\t\t.withClaims(claims)\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url(\"https://example.com\"));\n\t\tassertThat(authorizationServerMetadata.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(authorizationServerMetadata.getPushedAuthorizationRequestEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/par\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url(\"https://example.com/oauth2/token\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(url(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(authorizationServerMetadata.getScopes()).containsExactly(\"openid\");\n\t\tassertThat(authorizationServerMetadata.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(authorizationServerMetadata.getGrantTypes()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/revoke\"));\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/introspect\"));\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).isNull();\n\t\tassertThat(authorizationServerMetadata.getClaimAsString(\"some-claim\")).isEqualTo(\"some-value\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenClaimsWithUrlsProvidedThenCreated() {\n\t\tHashMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, url(\"https://example.com\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT,\n\t\t\t\turl(\"https://example.com/oauth2/authorize\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT,\n\t\t\t\turl(\"https://example.com/oauth2/par\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, url(\"https://example.com/oauth2/token\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, url(\"https://example.com/oauth2/jwks\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED,\n\t\t\t\tCollections.singletonList(\"code\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT,\n\t\t\t\turl(\"https://example.com/oauth2/revoke\"));\n\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT,\n\t\t\t\turl(\"https://example.com/oauth2/introspect\"));\n\t\tclaims.put(\"some-claim\", \"some-value\");\n\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata\n\t\t\t.withClaims(claims)\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url(\"https://example.com\"));\n\t\tassertThat(authorizationServerMetadata.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(authorizationServerMetadata.getPushedAuthorizationRequestEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/par\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url(\"https://example.com/oauth2/token\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(url(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(authorizationServerMetadata.getScopes()).isNull();\n\t\tassertThat(authorizationServerMetadata.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(authorizationServerMetadata.getGrantTypes()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/revoke\"));\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/introspect\"));\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).isNull();\n\t\tassertThat(authorizationServerMetadata.getClaimAsString(\"some-claim\")).isEqualTo(\"some-value\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationServerMetadata.withClaims(null))\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenMissingRequiredClaimsThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationServerMetadata.withClaims(Collections.emptyMap()))\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenCalledTwiceThenGeneratesTwoConfigurations() {\n\t\tOAuth2AuthorizationServerMetadata first = this.minimalBuilder.grantType(\"client_credentials\").build();\n\n\t\tOAuth2AuthorizationServerMetadata second = this.minimalBuilder.claims((claims) -> {\n\t\t\tList<String> newGrantTypes = new ArrayList<>();\n\t\t\tnewGrantTypes.add(\"authorization_code\");\n\t\t\tnewGrantTypes.add(\"custom_grant\");\n\t\t\tclaims.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, newGrantTypes);\n\t\t}).build();\n\n\t\tassertThat(first.getGrantTypes()).containsExactly(\"client_credentials\");\n\t\tassertThat(second.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\", \"custom_grant\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingIssuerThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.ISSUER));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"issuer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenIssuerNotUrlThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"issuer must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingAuthorizationEndpointThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"authorizationEndpoint cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"authorizationEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenPushedAuthorizationRequestEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.PUSHED_AUTHORIZATION_REQUEST_ENDPOINT, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"pushedAuthorizationRequestEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingTokenEndpointThenThrowsIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"tokenEndpoint cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"tokenEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenEndpointAuthenticationMethodsNotListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, \"not-a-list\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"tokenEndpointAuthenticationMethods must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenEndpointAuthenticationMethodsEmptyListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims.put(\n\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\tCollections.emptyList()));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"tokenEndpointAuthenticationMethods cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenEndpointAuthenticationMethodsAddingOrRemovingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.tokenEndpointAuthenticationMethod(\"should-be-removed\")\n\t\t\t.tokenEndpointAuthenticationMethods((authMethods) -> {\n\t\t\t\tauthMethods.clear();\n\t\t\t\tauthMethods.add(\"some-authentication-method\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods())\n\t\t\t.containsExactly(\"some-authentication-method\");\n\t}\n\n\t@Test\n\tpublic void buildWhenJwksUriNotUrlThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"jwksUri must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesNotListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, \"not-a-list\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"scopes must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesEmptyListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, Collections.emptyList()));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"scopes cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesAddingOrRemovingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder.scope(\"should-be-removed\")\n\t\t\t.scopes((scopes) -> {\n\t\t\t\tscopes.clear();\n\t\t\t\tscopes.add(\"some-scope\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getScopes()).containsExactly(\"some-scope\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingResponseTypesThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"responseTypes cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesNotListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, \"not-a-list\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"responseTypes must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesEmptyListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.emptyList()));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"responseTypes cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesAddingOrRemovingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.responseType(\"should-be-removed\")\n\t\t\t.responseTypes((responseTypes) -> {\n\t\t\t\tresponseTypes.clear();\n\t\t\t\tresponseTypes.add(\"some-response-type\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getResponseTypes()).containsExactly(\"some-response-type\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesNotPresentAndAddingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED))\n\t\t\t.responseTypes((responseTypes) -> responseTypes.add(\"some-response-type\"))\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getResponseTypes()).containsExactly(\"some-response-type\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesNotListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, \"not-a-list\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"grantTypes must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesEmptyListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, Collections.emptyList()));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"grantTypes cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesAddingOrRemovingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.grantType(\"should-be-removed\")\n\t\t\t.grantTypes((grantTypes) -> {\n\t\t\t\tgrantTypes.clear();\n\t\t\t\tgrantTypes.add(\"some-grant-type\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getGrantTypes()).containsExactly(\"some-grant-type\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenRevocationEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.tokenRevocationEndpoint(\"not a valid URL\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"tokenRevocationEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenRevocationEndpointAuthenticationMethodsNotListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, \"not-a-list\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"tokenRevocationEndpointAuthenticationMethods must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenRevocationEndpointAuthenticationMethodsEmptyListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims.put(\n\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\tCollections.emptyList()));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"tokenRevocationEndpointAuthenticationMethods cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenRevocationEndpointAuthenticationMethodsAddingOrRemovingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.tokenRevocationEndpointAuthenticationMethod(\"should-be-removed\")\n\t\t\t.tokenRevocationEndpointAuthenticationMethods((authMethods) -> {\n\t\t\t\tauthMethods.clear();\n\t\t\t\tauthMethods.add(\"some-authentication-method\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods())\n\t\t\t.containsExactly(\"some-authentication-method\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenIntrospectionEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.tokenIntrospectionEndpoint(\"not a valid URL\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"tokenIntrospectionEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenIntrospectionEndpointAuthenticationMethodsNotListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims.put(\n\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\t\"not-a-list\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"tokenIntrospectionEndpointAuthenticationMethods must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenIntrospectionEndpointAuthenticationMethodsEmptyListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims.put(\n\t\t\t\tOAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED,\n\t\t\t\tCollections.emptyList()));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"tokenIntrospectionEndpointAuthenticationMethods cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenIntrospectionEndpointAuthenticationMethodsAddingOrRemovingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.tokenIntrospectionEndpointAuthenticationMethod(\"should-be-removed\")\n\t\t\t.tokenIntrospectionEndpointAuthenticationMethods((authMethods) -> {\n\t\t\t\tauthMethods.clear();\n\t\t\t\tauthMethods.add(\"some-authentication-method\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods())\n\t\t\t.containsExactly(\"some-authentication-method\");\n\t}\n\n\t@Test\n\tpublic void buildWhenCodeChallengeMethodsNotListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, \"not-a-list\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"codeChallengeMethods must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenCodeChallengeMethodsEmptyListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder\n\t\t\t.claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED,\n\t\t\t\t\tCollections.emptyList()));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"codeChallengeMethods cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenCodeChallengeMethodsAddingOrRemovingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.codeChallengeMethod(\"should-be-removed\")\n\t\t\t.codeChallengeMethods((codeChallengeMethods) -> {\n\t\t\t\tcodeChallengeMethods.clear();\n\t\t\t\tcodeChallengeMethods.add(\"some-authentication-method\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly(\"some-authentication-method\");\n\t}\n\n\t@Test\n\tpublic void buildWhenDPoPSigningAlgorithmsNotListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims((claims) -> claims\n\t\t\t.put(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED, \"not-a-list\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"dPoPSigningAlgorithms must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenDPoPSigningAlgorithmsEmptyListThenThrowIllegalArgumentException() {\n\t\tBuilder builder = this.minimalBuilder.claims(\n\t\t\t\t(claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.DPOP_SIGNING_ALG_VALUES_SUPPORTED,\n\t\t\t\t\t\tCollections.emptyList()));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"dPoPSigningAlgorithms cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenDPoPSigningAlgorithmsAddingOrRemovingThenCorrectValues() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.dPoPSigningAlgorithm(JwsAlgorithms.RS256)\n\t\t\t.dPoPSigningAlgorithms((algs) -> {\n\t\t\t\talgs.clear();\n\t\t\t\talgs.add(JwsAlgorithms.ES256);\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerMetadata.getDPoPSigningAlgorithms()).containsExactly(JwsAlgorithms.ES256);\n\t}\n\n\t@Test\n\tpublic void claimWhenNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationServerMetadata.builder().claim(null, \"claim-value\"))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void claimWhenValueNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationServerMetadata.builder().claim(\"claim-name\", null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void claimsWhenRemovingClaimThenNotPresent() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.claim(\"claim-name\", \"claim-value\")\n\t\t\t.claims((claims) -> claims.remove(\"claim-name\"))\n\t\t\t.build();\n\t\tassertThat(authorizationServerMetadata.hasClaim(\"claim-name\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void claimsWhenAddingClaimThenPresent() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder\n\t\t\t.claim(\"claim-name\", \"claim-value\")\n\t\t\t.build();\n\t\tassertThat(authorizationServerMetadata.hasClaim(\"claim-name\")).isTrue();\n\t}\n\n\tprivate static URL url(String urlString) {\n\t\ttry {\n\t\t\treturn new URL(urlString);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(\"urlString must be a valid URL and valid URI\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2Authorization}.\n *\n * @author Krisztian Toth\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationTests {\n\n\tprivate static final String ID = \"id\";\n\n\tprivate static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build();\n\n\tprivate static final String PRINCIPAL_NAME = \"principal\";\n\n\tprivate static final AuthorizationGrantType AUTHORIZATION_GRANT_TYPE = AuthorizationGrantType.AUTHORIZATION_CODE;\n\n\tprivate static final OAuth2AccessToken ACCESS_TOKEN = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\"access-token\", Instant.now(), Instant.now().plusSeconds(300));\n\n\tprivate static final OAuth2RefreshToken REFRESH_TOKEN = new OAuth2RefreshToken(\"refresh-token\", Instant.now());\n\n\tprivate static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode(\"code\", Instant.now(),\n\t\t\tInstant.now().plus(5, ChronoUnit.MINUTES));\n\n\t@Test\n\tpublic void withRegisteredClientWhenRegisteredClientNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2Authorization.withRegisteredClient(null))\n\t\t\t.withMessage(\"registeredClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void fromWhenAuthorizationNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> OAuth2Authorization.from(null))\n\t\t\t.withMessage(\"authorization cannot be null\");\n\t}\n\n\t@Test\n\tpublic void fromWhenAuthorizationProvidedThenCopied() {\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.accessToken(ACCESS_TOKEN)\n\t\t\t.build();\n\t\tOAuth2Authorization authorizationResult = OAuth2Authorization.from(authorization).build();\n\n\t\tassertThat(authorizationResult.getId()).isEqualTo(authorization.getId());\n\t\tassertThat(authorizationResult.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId());\n\t\tassertThat(authorizationResult.getPrincipalName()).isEqualTo(authorization.getPrincipalName());\n\t\tassertThat(authorizationResult.getAuthorizationGrantType())\n\t\t\t.isEqualTo(authorization.getAuthorizationGrantType());\n\t\tassertThat(authorizationResult.getAccessToken()).isEqualTo(authorization.getAccessToken());\n\t\tassertThat(authorizationResult.getToken(OAuth2AuthorizationCode.class))\n\t\t\t.isEqualTo(authorization.getToken(OAuth2AuthorizationCode.class));\n\t\tassertThat(authorizationResult.getAttributes()).isEqualTo(authorization.getAttributes());\n\t}\n\n\t@Test\n\tpublic void buildWhenPrincipalNameNotProvidedThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).build())\n\t\t\t.isInstanceOf(IllegalArgumentException.class)\n\t\t\t.withMessage(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationGrantTypeNotProvidedThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(\n\t\t\t\t() -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).principalName(PRINCIPAL_NAME).build())\n\t\t\t.withMessage(\"authorizationGrantType cannot be null\");\n\t}\n\n\t@Test\n\tpublic void attributeWhenNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(\n\t\t\t\t() -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).attribute(null, AUTHORIZATION_CODE))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void attributeWhenValueNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).attribute(\"name\", null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAllAttributesAreProvidedThenAllAttributesAreSet() {\n\t\tOAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)\n\t\t\t.id(ID)\n\t\t\t.principalName(PRINCIPAL_NAME)\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE)\n\t\t\t.token(AUTHORIZATION_CODE)\n\t\t\t.accessToken(ACCESS_TOKEN)\n\t\t\t.refreshToken(REFRESH_TOKEN)\n\t\t\t.build();\n\n\t\tassertThat(authorization.getId()).isEqualTo(ID);\n\t\tassertThat(authorization.getRegisteredClientId()).isEqualTo(REGISTERED_CLIENT.getId());\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(PRINCIPAL_NAME);\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AUTHORIZATION_GRANT_TYPE);\n\t\tassertThat(authorization.getToken(OAuth2AuthorizationCode.class).getToken()).isEqualTo(AUTHORIZATION_CODE);\n\t\tassertThat(authorization.getAccessToken().getToken()).isEqualTo(ACCESS_TOKEN);\n\t\tassertThat(authorization.getRefreshToken().getToken()).isEqualTo(REFRESH_TOKEN);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2ClientRegistrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2ClientRegistration}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2ClientRegistrationTests {\n\n\t@Test\n\tpublic void buildWhenAllClaimsProvidedThenCreated() throws Exception {\n\t\t// @formatter:off\n\t\tInstant clientIdIssuedAt = Instant.now();\n\t\tInstant clientSecretExpiresAt = clientIdIssuedAt.plus(30, ChronoUnit.DAYS);\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(clientIdIssuedAt)\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientSecretExpiresAt(clientSecretExpiresAt)\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.responseType(OAuth2AuthorizationResponseType.CODE.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.jwkSetUrl(\"https://client.example.com/jwks\")\n\t\t\t\t.claim(\"a-claim\", \"a-value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(clientRegistration.getClientIdIssuedAt()).isEqualTo(clientIdIssuedAt);\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"client-secret\");\n\t\tassertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(clientSecretExpiresAt);\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(\"client-name\");\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(clientRegistration.getResponseTypes()).containsOnly(\"code\");\n\t\tassertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder(\"scope1\", \"scope2\");\n\t\tassertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL(\"https://client.example.com/jwks\"));\n\t\tassertThat(clientRegistration.getClaimAsString(\"a-claim\")).isEqualTo(\"a-value\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenClaimsProvidedThenCreated() throws Exception {\n\t\tInstant clientIdIssuedAt = Instant.now();\n\t\tInstant clientSecretExpiresAt = clientIdIssuedAt.plus(30, ChronoUnit.DAYS);\n\t\tHashMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.CLIENT_ID, \"client-id\");\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, clientIdIssuedAt);\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.CLIENT_SECRET, \"client-secret\");\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, clientSecretExpiresAt);\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.CLIENT_NAME, \"client-name\");\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.REDIRECT_URIS,\n\t\t\t\tCollections.singletonList(\"https://client.example.com\"));\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.GRANT_TYPES,\n\t\t\t\tArrays.asList(AuthorizationGrantType.AUTHORIZATION_CODE.getValue(),\n\t\t\t\t\t\tAuthorizationGrantType.CLIENT_CREDENTIALS.getValue()));\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES, Collections.singletonList(\"code\"));\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.SCOPE, Arrays.asList(\"scope1\", \"scope2\"));\n\t\tclaims.put(OAuth2ClientMetadataClaimNames.JWKS_URI, \"https://client.example.com/jwks\");\n\t\tclaims.put(\"a-claim\", \"a-value\");\n\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.withClaims(claims).build();\n\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(clientRegistration.getClientIdIssuedAt()).isEqualTo(clientIdIssuedAt);\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"client-secret\");\n\t\tassertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(clientSecretExpiresAt);\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(\"client-name\");\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(clientRegistration.getResponseTypes()).containsOnly(\"code\");\n\t\tassertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder(\"scope1\", \"scope2\");\n\t\tassertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL(\"https://client.example.com/jwks\"));\n\t\tassertThat(clientRegistration.getClaimAsString(\"a-claim\")).isEqualTo(\"a-value\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OAuth2ClientRegistration.withClaims(null))\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2ClientRegistration.withClaims(Collections.emptyMap()))\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingClientIdThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder().clientIdIssuedAt(Instant.now());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"client_id cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientSecretAndMissingClientIdThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder().clientSecret(\"client-secret\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"client_id cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientIdIssuedAtNotInstantThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.claim(OAuth2ClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, \"clientIdIssuedAt\");\n\t\t// @formatter:on\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"client_id_issued_at must be of type Instant\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingClientSecretThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t\t.clientSecretExpiresAt(Instant.now().plus(30, ChronoUnit.DAYS));\n\t\t// @formatter:on\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"client_secret cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientSecretExpiresAtNotInstantThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.claim(OAuth2ClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, \"clientSecretExpiresAt\");\n\t\t// @formatter:on\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"client_secret_expires_at must be of type Instant\");\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUrisNotListThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.REDIRECT_URIS, \"redirectUris\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"redirect_uris must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUrisEmptyListThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.REDIRECT_URIS, Collections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"redirect_uris cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUrisAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client1.example.com\")\n\t\t\t\t.redirectUris((redirectUris) -> {\n\t\t\t\t\tredirectUris.clear();\n\t\t\t\t\tredirectUris.add(\"https://client2.example.com\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getRedirectUris()).containsExactly(\"https://client2.example.com\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesNotListThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.GRANT_TYPES, \"grantTypes\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"grant_types must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesEmptyListThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.GRANT_TYPES, Collections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"grant_types cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.grantType(\"authorization_code\")\n\t\t\t\t.grantTypes((grantTypes) -> {\n\t\t\t\t\tgrantTypes.clear();\n\t\t\t\t\tgrantTypes.add(\"client_credentials\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getGrantTypes()).containsExactly(\"client_credentials\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesNotListThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES, \"responseTypes\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"response_types must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesEmptyListThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.RESPONSE_TYPES, Collections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"response_types cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.responseType(\"token\")\n\t\t\t\t.responseTypes((responseTypes) -> {\n\t\t\t\t\tresponseTypes.clear();\n\t\t\t\t\tresponseTypes.add(\"code\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getResponseTypes()).containsExactly(\"code\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesNotListThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.SCOPE, \"scopes\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"scope must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesEmptyListThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.SCOPE, Collections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"scope cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.scope(\"should-be-removed\")\n\t\t\t\t.scopes((scopes) -> {\n\t\t\t\t\tscopes.clear();\n\t\t\t\t\tscopes.add(\"scope1\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getScopes()).containsExactly(\"scope1\");\n\t}\n\n\t@Test\n\tpublic void buildWhenJwksUriNotUrlThenThrowIllegalArgumentException() {\n\t\tOAuth2ClientRegistration.Builder builder = OAuth2ClientRegistration.builder()\n\t\t\t.claim(OAuth2ClientMetadataClaimNames.JWKS_URI, \"not an url\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"jwksUri must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void claimWhenNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2ClientRegistration.builder().claim(null, \"claim-value\"))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void claimWhenValueNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2ClientRegistration.builder().claim(\"claim-name\", null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void claimsWhenRemovingClaimThenNotPresent() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.claim(\"claim-name\", \"claim-value\")\n\t\t\t\t.claims((claims) -> claims.remove(\"claim-name\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.hasClaim(\"claim-name\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void claimsWhenAddingClaimThenPresent() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.claim(\"claim-name\", \"claim-value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.hasClaim(\"claim-name\")).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n */\npublic final class TestOAuth2Authorizations {\n\n\tprivate TestOAuth2Authorizations() {\n\t}\n\n\tpublic static OAuth2Authorization.Builder authorization() {\n\t\treturn authorization(TestRegisteredClients.registeredClient().build());\n\t}\n\n\tpublic static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient) {\n\t\treturn authorization(registeredClient, Collections.emptyMap());\n\t}\n\n\tpublic static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient,\n\t\t\tMap<String, Object> authorizationRequestAdditionalParameters) {\n\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(\"code\", Instant.now(),\n\t\t\t\tInstant.now().plusSeconds(120));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\t\tInstant.now(), Instant.now().plusSeconds(300));\n\t\treturn authorization(registeredClient, authorizationCode, accessToken, Collections.emptyMap(),\n\t\t\t\tauthorizationRequestAdditionalParameters);\n\t}\n\n\tpublic static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient,\n\t\t\tOAuth2AuthorizationCode authorizationCode) {\n\t\treturn authorization(registeredClient, authorizationCode, null, Collections.emptyMap(), Collections.emptyMap());\n\t}\n\n\tpublic static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient,\n\t\t\tOAuth2AccessToken accessToken, Map<String, Object> accessTokenClaims) {\n\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(\"code\", Instant.now(),\n\t\t\t\tInstant.now().plusSeconds(120));\n\t\treturn authorization(registeredClient, authorizationCode, accessToken, accessTokenClaims,\n\t\t\t\tCollections.emptyMap());\n\t}\n\n\tprivate static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient,\n\t\t\tOAuth2AuthorizationCode authorizationCode, OAuth2AccessToken accessToken,\n\t\t\tMap<String, Object> accessTokenClaims, Map<String, Object> authorizationRequestAdditionalParameters) {\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(\"https://provider.com/oauth2/authorize\")\n\t\t\t.clientId(registeredClient.getClientId())\n\t\t\t.redirectUri(registeredClient.getRedirectUris().iterator().next())\n\t\t\t.scopes(registeredClient.getScopes())\n\t\t\t.additionalParameters(authorizationRequestAdditionalParameters)\n\t\t\t.state(\"state\")\n\t\t\t.build();\n\t\tOAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient)\n\t\t\t.id(\"id\")\n\t\t\t.principalName(\"principal\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.authorizedScopes(authorizationRequest.getScopes())\n\t\t\t.token(authorizationCode)\n\t\t\t.attribute(OAuth2ParameterNames.STATE, \"consent-state\")\n\t\t\t.attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest)\n\t\t\t.attribute(Principal.class.getName(),\n\t\t\t\t\tnew TestingAuthenticationToken(\"principal\", null, \"ROLE_A\", \"ROLE_B\"));\n\t\tif (accessToken != null) {\n\t\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", Instant.now(),\n\t\t\t\t\tInstant.now().plus(1, ChronoUnit.HOURS));\n\t\t\tbuilder\n\t\t\t\t.token(accessToken, (metadata) -> metadata.putAll(tokenMetadata(registeredClient, accessTokenClaims)))\n\t\t\t\t.refreshToken(refreshToken);\n\t\t}\n\n\t\treturn builder;\n\t}\n\n\tprivate static Map<String, Object> tokenMetadata(RegisteredClient registeredClient,\n\t\t\tMap<String, Object> tokenClaims) {\n\t\tMap<String, Object> tokenMetadata = new HashMap<>();\n\t\tOAuth2TokenFormat accessTokenFormat = registeredClient.getTokenSettings().getAccessTokenFormat();\n\t\ttokenMetadata.put(OAuth2TokenFormat.class.getName(), accessTokenFormat.getValue());\n\t\ttokenMetadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, false);\n\t\tif (CollectionUtils.isEmpty(tokenClaims)) {\n\t\t\ttokenClaims = defaultTokenClaims();\n\t\t}\n\t\ttokenMetadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, tokenClaims);\n\t\treturn tokenMetadata;\n\t}\n\n\tprivate static Map<String, Object> defaultTokenClaims() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"claim1\", \"value1\");\n\t\tclaims.put(\"claim2\", \"value2\");\n\t\tclaims.put(\"claim3\", \"value3\");\n\t\treturn claims;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/aot/hint/OAuth2AuthorizationServerBeanRegistrationAotProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.aot.hint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nimport org.springframework.beans.factory.aot.BeanRegistrationAotContribution;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.beans.factory.support.RegisteredBean;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2AuthorizationServerBeanRegistrationAotProcessor}.\n *\n * @author William Koch\n */\nclass OAuth2AuthorizationServerBeanRegistrationAotProcessorTests {\n\n\tprivate OAuth2AuthorizationServerBeanRegistrationAotProcessor processor;\n\n\tprivate DefaultListableBeanFactory defaultListableBeanFactory;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.processor = new OAuth2AuthorizationServerBeanRegistrationAotProcessor();\n\t\tthis.defaultListableBeanFactory = new DefaultListableBeanFactory();\n\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { JdbcOAuth2AuthorizationService.class, CustomJdbcOAuth2AuthorizationService.class,\n\t\t\tJdbcRegisteredClientRepository.class, CustomJdbcRegisteredClientRepository.class })\n\tvoid processAheadOfTimeWhenBeanTypeJdbcBasedImplThenReturnContribution(Class<?> beanClass) {\n\t\tthis.defaultListableBeanFactory.registerBeanDefinition(\"beanName\", new RootBeanDefinition(beanClass));\n\n\t\tBeanRegistrationAotContribution aotContribution = this.processor\n\t\t\t.processAheadOfTime(RegisteredBean.of(this.defaultListableBeanFactory, \"beanName\"));\n\n\t\tassertThat(aotContribution).isNotNull();\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { InMemoryOAuth2AuthorizationService.class, InMemoryRegisteredClientRepository.class,\n\t\t\tObject.class })\n\tvoid processAheadOfTimeWhenBeanTypeNotJdbcBasedImplThenDoesNotReturnContribution(Class<?> beanClass) {\n\t\tthis.defaultListableBeanFactory.registerBeanDefinition(\"beanName\", new RootBeanDefinition(beanClass));\n\n\t\tBeanRegistrationAotContribution aotContribution = this.processor\n\t\t\t.processAheadOfTime(RegisteredBean.of(this.defaultListableBeanFactory, \"beanName\"));\n\n\t\tassertThat(aotContribution).isNull();\n\t}\n\n\t@Test\n\tvoid processAheadOfTimeWhenMultipleBeanTypeJdbcBasedImplThenReturnContributionOnce() {\n\t\tthis.defaultListableBeanFactory.registerBeanDefinition(\"oauth2AuthorizationService\",\n\t\t\t\tnew RootBeanDefinition(JdbcOAuth2AuthorizationService.class));\n\n\t\tthis.defaultListableBeanFactory.registerBeanDefinition(\"registeredClientRepository\",\n\t\t\t\tnew RootBeanDefinition(CustomJdbcRegisteredClientRepository.class));\n\n\t\tBeanRegistrationAotContribution firstAotContribution = this.processor\n\t\t\t.processAheadOfTime(RegisteredBean.of(this.defaultListableBeanFactory, \"oauth2AuthorizationService\"));\n\n\t\tBeanRegistrationAotContribution secondAotContribution = this.processor\n\t\t\t.processAheadOfTime(RegisteredBean.of(this.defaultListableBeanFactory, \"registeredClientRepository\"));\n\n\t\tassertThat(firstAotContribution).isNotNull();\n\t\tassertThat(secondAotContribution).isNull();\n\t}\n\n\tstatic class CustomJdbcOAuth2AuthorizationService extends JdbcOAuth2AuthorizationService {\n\n\t\tCustomJdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations,\n\t\t\t\tRegisteredClientRepository registeredClientRepository) {\n\t\t\tsuper(jdbcOperations, registeredClientRepository);\n\t\t}\n\n\t}\n\n\tstatic class CustomJdbcRegisteredClientRepository extends JdbcRegisteredClientRepository {\n\n\t\tCustomJdbcRegisteredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tsuper(jdbcOperations);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link ClientSecretAuthenticationProvider}.\n *\n * @author Patryk Kostrzewa\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n */\npublic class ClientSecretAuthenticationProviderTests {\n\n\t// See RFC 7636: Appendix B. Example for the S256 code_challenge_method\n\t// https://tools.ietf.org/html/rfc7636#appendix-B\n\tprivate static final String S256_CODE_VERIFIER = \"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\n\tprivate static final String S256_CODE_CHALLENGE = \"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\";\n\n\tprivate static final String AUTHORIZATION_CODE = \"code\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate ClientSecretAuthenticationProvider authenticationProvider;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new ClientSecretAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\tthis.authorizationService);\n\t\tthis.passwordEncoder = spy(new PasswordEncoder() {\n\t\t\t@Override\n\t\t\tpublic String encode(CharSequence rawPassword) {\n\t\t\t\treturn NoOpPasswordEncoder.getInstance().encode(rawPassword);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean matches(CharSequence rawPassword, String encodedPassword) {\n\t\t\t\treturn NoOpPasswordEncoder.getInstance().matches(rawPassword, encodedPassword);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean upgradeEncoding(String encodedPassword) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\t\tthis.authenticationProvider.setPasswordEncoder(this.passwordEncoder);\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new ClientSecretAuthenticationProvider(null, this.authorizationService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new ClientSecretAuthenticationProvider(this.registeredClientRepository, null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setPasswordEncoderWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setPasswordEncoder(null))\n\t\t\t.withMessage(\"passwordEncoder cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2ClientAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2ClientAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClientIdThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId() + \"-invalid\", ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnsupportedClientAuthenticationMethodThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_POST,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"authentication_method\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientSecretNotProvidedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"credentials\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClientSecretThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret() + \"-invalid\", null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_SECRET);\n\t\t\t});\n\t\tverify(this.passwordEncoder).matches(any(), any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenExpiredClientSecretThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.clientSecretExpiresAt(Instant.now().minus(1, ChronoUnit.HOURS).truncatedTo(ChronoUnit.SECONDS))\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"client_secret_expires_at\");\n\t\t\t});\n\t\tverify(this.passwordEncoder).matches(any(), any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidCredentialsThenAuthenticated() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.passwordEncoder).matches(any(), any());\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials().toString()).isEqualTo(registeredClient.getClientSecret());\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidCredentialsAndRequiresUpgradingThenClientSecretUpgraded() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.passwordEncoder).matches(any(), any());\n\t\tverify(this.passwordEncoder).upgradeEncoding(any());\n\t\tverify(this.passwordEncoder).encode(any());\n\t\tverify(this.registeredClientRepository).save(any());\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials().toString()).isEqualTo(registeredClient.getClientSecret());\n\t\tassertThat(authenticationResult.getRegisteredClient()).isNotSameAs(registeredClient);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationCodeGrantAndValidCredentialsThenAuthenticated() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), createPkceTokenParameters(S256_CODE_VERIFIER));\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.passwordEncoder).matches(any(), any());\n\t\tverify(this.authorizationService).findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE));\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials().toString()).isEqualTo(registeredClient.getClientSecret());\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceAndInvalidCodeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);\n\t\tparameters.put(OAuth2ParameterNames.CODE, \"invalid-code\");\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), parameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CODE);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceAndMissingCodeVerifierThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createAuthorizationCodeTokenParameters();\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), parameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t\tassertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceAndValidCodeVerifierThenAuthenticated() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), parameters);\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.passwordEncoder).matches(any(), any());\n\t\tverify(this.authorizationService).findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE));\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials().toString()).isEqualTo(registeredClient.getClientSecret());\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t}\n\n\tprivate static OAuth2Authorization createAuthorization(RegisteredClient registeredClient) {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\treturn TestOAuth2Authorizations.authorization(registeredClient, parameters).build();\n\t}\n\n\tprivate static Map<String, Object> createAuthorizationCodeTokenParameters() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\tparameters.put(OAuth2ParameterNames.CODE, AUTHORIZATION_CODE);\n\t\treturn parameters;\n\t}\n\n\tprivate static Map<String, Object> createPkceTokenParameters(String codeVerifier) {\n\t\tMap<String, Object> parameters = createAuthorizationCodeTokenParameters();\n\t\tparameters.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.OctetSequenceKey;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.JwtValidationException;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link JwtClientAssertionAuthenticationProvider}.\n *\n * @author Rafal Lewczuk\n * @author Joe Grandja\n */\npublic class JwtClientAssertionAuthenticationProviderTests {\n\n\t// See RFC 7636: Appendix B. Example for the S256 code_challenge_method\n\t// https://tools.ietf.org/html/rfc7636#appendix-B\n\tprivate static final String S256_CODE_VERIFIER = \"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\n\tprivate static final String S256_CODE_CHALLENGE = \"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\";\n\n\tprivate static final String AUTHORIZATION_CODE = \"code\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = new ClientAuthenticationMethod(\n\t\t\t\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\");\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate JwtClientAssertionAuthenticationProvider authenticationProvider;\n\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new JwtClientAssertionAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\tthis.authorizationService);\n\t\tthis.authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://auth-server.com\")\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null));\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new JwtClientAssertionAuthenticationProvider(null, this.authorizationService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new JwtClientAssertionAuthenticationProvider(this.registeredClientRepository, null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setJwtDecoderFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setJwtDecoderFactory(null))\n\t\t\t.withMessage(\"jwtDecoderFactory cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2ClientAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2ClientAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClientIdThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId() + \"-invalid\", JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD,\n\t\t\t\t\"jwt-assertion\", null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnsupportedClientAuthenticationMethodThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, \"jwt-assertion\", null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"authentication_method\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCredentialsNotProvidedThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"credentials\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidCredentialsThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, \"invalid-jwt-assertion\",\n\t\t\t\tnull);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.withCauseInstanceOf(BadJwtException.class)\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ASSERTION);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClaimsThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256)\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder()\n\t\t\t\t.issuer(\"invalid-iss\")\n\t\t\t\t.subject(\"invalid-sub\")\n\t\t\t\t.audience(Collections.singletonList(\"invalid-aud\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtEncoder jwsEncoder = createEncoder(TestKeys.DEFAULT_ENCODED_SECRET_KEY, \"HmacSHA256\");\n\t\tJwt jwtAssertion = jwsEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD,\n\t\t\t\tjwtAssertion.getTokenValue(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.withCauseInstanceOf(JwtValidationException.class)\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(ex.getError().getDescription()).contains(OAuth2ParameterNames.CLIENT_ASSERTION);\n\t\t\t\tJwtValidationException jwtValidationException = (JwtValidationException) ex.getCause();\n\t\t\t\tassertThat(jwtValidationException.getErrors()).hasSize(4); // iss, sub,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// aud, exp\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidCredentialsThenAuthenticated() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256)\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = jwtClientAssertionClaims(registeredClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtEncoder jwsEncoder = createEncoder(TestKeys.DEFAULT_ENCODED_SECRET_KEY, \"HmacSHA256\");\n\t\tJwt jwtAssertion = jwsEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD,\n\t\t\t\tjwtAssertion.getTokenValue(), null);\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials()).isInstanceOf(Jwt.class);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceAndValidCodeVerifierThenAuthenticated() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, createPkceAuthorizationParametersS256())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);\n\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256)\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = jwtClientAssertionClaims(registeredClient)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtEncoder jwsEncoder = createEncoder(TestKeys.DEFAULT_ENCODED_SECRET_KEY, \"HmacSHA256\");\n\t\tJwt jwtAssertion = jwsEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD,\n\t\t\t\tjwtAssertion.getTokenValue(), parameters);\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE));\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials()).isInstanceOf(Jwt.class);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT);\n\t}\n\n\tprivate JwtClaimsSet.Builder jwtClientAssertionClaims(RegisteredClient registeredClient) {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\t\treturn JwtClaimsSet.builder()\n\t\t\t.issuer(registeredClient.getClientId())\n\t\t\t.subject(registeredClient.getClientId())\n\t\t\t.audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(),\n\t\t\t\t\tthis.authorizationServerSettings.getTokenEndpoint())))\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt);\n\t}\n\n\tprivate static JwtEncoder createEncoder(String secret, String algorithm) {\n\t\tSecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), algorithm);\n\t\tOctetSequenceKey secretKeyJwk = TestJwks.jwk(secretKey).build();\n\t\tJWKSource<SecurityContext> jwkSource = (jwkSelector, securityContext) -> jwkSelector\n\t\t\t.select(new JWKSet(secretKeyJwk));\n\t\treturn new NimbusJwtEncoder(jwkSource);\n\t}\n\n\tprivate static String asUrl(String uri, String path) {\n\t\treturn UriComponentsBuilder.fromUriString(uri).path(path).build().toUriString();\n\t}\n\n\tprivate static Map<String, Object> createAuthorizationCodeTokenParameters() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\tparameters.put(OAuth2ParameterNames.CODE, AUTHORIZATION_CODE);\n\t\treturn parameters;\n\t}\n\n\tprivate static Map<String, Object> createPkceTokenParameters(String codeVerifier) {\n\t\tMap<String, Object> parameters = createAuthorizationCodeTokenParameters();\n\t\tparameters.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);\n\t\treturn parameters;\n\t}\n\n\tprivate static Map<String, Object> createPkceAuthorizationParametersS256() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link JwtClientAssertionDecoderFactory}.\n *\n * @author Joe Grandja\n */\npublic class JwtClientAssertionDecoderFactoryTests {\n\n\tprivate JwtClientAssertionDecoderFactory jwtDecoderFactory = new JwtClientAssertionDecoderFactory();\n\n\t@Test\n\tpublic void setJwtValidatorFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.jwtDecoderFactory.setJwtValidatorFactory(null))\n\t\t\t.withMessage(\"jwtValidatorFactory cannot be null\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenMissingJwkSetUrlThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.jwtDecoderFactory.createDecoder(registeredClient))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).isEqualTo(\"Failed to find a Signature Verifier for Client: '\"\n\t\t\t\t\t\t+ registeredClient.getId() + \"'. Check to ensure you have configured the JWK Set URL.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenMissingClientSecretThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.jwtDecoderFactory.createDecoder(registeredClient))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).isEqualTo(\"Failed to find a Signature Verifier for Client: '\"\n\t\t\t\t\t\t+ registeredClient.getId() + \"'. Check to ensure you have configured the client secret.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenMissingSigningAlgorithmThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.jwtDecoderFactory.createDecoder(registeredClient))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription())\n\t\t\t\t\t.isEqualTo(\"Failed to find a Signature Verifier for Client: '\" + registeredClient.getId()\n\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured a valid JWS Algorithm: 'null'.\");\n\t\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AccessTokenAuthenticationContext}\n *\n * @author Dmitriy Dubson\n */\npublic class OAuth2AccessTokenAuthenticationContextTests {\n\n\tprivate final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient)\n\t\t.build();\n\n\tprivate OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,\n\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());\n\n\tprivate final OAuth2AccessTokenAuthenticationToken accessTokenAuthenticationToken = new OAuth2AccessTokenAuthenticationToken(\n\t\t\tthis.registeredClient, this.clientPrincipal, this.authorization.getAccessToken().getToken(),\n\t\t\tthis.authorization.getRefreshToken().getToken());\n\n\t@Test\n\tpublic void withWhenAuthenticationNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2AccessTokenAuthenticationContext.with(null))\n\t\t\t.withMessage(\"authentication cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setWhenValueNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AccessTokenAuthenticationContext.Builder builder = OAuth2AccessTokenAuthenticationContext\n\t\t\t.with(this.accessTokenAuthenticationToken);\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.accessTokenResponse(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAllValuesProvidedThenAllValuesAreSet() {\n\t\tOAuth2AccessTokenResponse.Builder accessTokenResponseBuilder = OAuth2AccessTokenResponse\n\t\t\t.withToken(this.accessTokenAuthenticationToken.getAccessToken().getTokenValue());\n\t\tOAuth2AccessTokenAuthenticationContext context = OAuth2AccessTokenAuthenticationContext\n\t\t\t.with(this.accessTokenAuthenticationToken)\n\t\t\t.accessTokenResponse(accessTokenResponseBuilder)\n\t\t\t.build();\n\n\t\tassertThat(context.<Authentication>getAuthentication()).isEqualTo(this.accessTokenAuthenticationToken);\n\t\tassertThat(context.getAccessTokenResponse()).isEqualTo(accessTokenResponseBuilder);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AccessTokenAuthenticationToken}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AccessTokenAuthenticationTokenTests {\n\n\tprivate RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,\n\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());\n\n\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\tInstant.now(), Instant.now().plusSeconds(300));\n\n\tprivate OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", Instant.now(),\n\t\t\tInstant.now().plus(1, ChronoUnit.DAYS));\n\n\tprivate Map<String, Object> additionalParameters = Collections.singletonMap(\"custom-param\", \"custom-value\");\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AccessTokenAuthenticationToken(null, this.clientPrincipal, this.accessToken))\n\t\t\t.withMessage(\"registeredClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AccessTokenAuthenticationToken(this.registeredClient, null, this.accessToken))\n\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAccessTokenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> new OAuth2AccessTokenAuthenticationToken(this.registeredClient, this.clientPrincipal, null))\n\t\t\t.withMessage(\"accessToken cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAdditionalParametersNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AccessTokenAuthenticationToken(this.registeredClient, this.clientPrincipal,\n\t\t\t\t\tthis.accessToken, this.refreshToken, null))\n\t\t\t.withMessage(\"additionalParameters cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllValuesProvidedThenCreated() {\n\t\tOAuth2AccessTokenAuthenticationToken authentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tthis.registeredClient, this.clientPrincipal, this.accessToken, this.refreshToken,\n\t\t\t\tthis.additionalParameters);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getRegisteredClient()).isEqualTo(this.registeredClient);\n\t\tassertThat(authentication.getAccessToken()).isEqualTo(this.accessToken);\n\t\tassertThat(authentication.getRefreshToken()).isEqualTo(this.refreshToken);\n\t\tassertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.Principal;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Base64;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.JwtGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2AuthorizationCodeAuthenticationProvider}.\n *\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n */\npublic class OAuth2AuthorizationCodeAuthenticationProviderTests {\n\n\tprivate static final String AUTHORIZATION_CODE = \"code\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate JwtEncoder jwtEncoder;\n\n\tprivate OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer;\n\n\tprivate OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer;\n\n\tprivate OAuth2TokenGenerator<?> tokenGenerator;\n\n\tprivate JwtEncoder dPoPProofJwtEncoder;\n\n\tprivate SessionRegistry sessionRegistry;\n\n\tprivate OAuth2AuthorizationCodeAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.jwtEncoder = mock(JwtEncoder.class);\n\t\tthis.jwtCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tJwtGenerator jwtGenerator = new JwtGenerator(this.jwtEncoder);\n\t\tjwtGenerator.setJwtCustomizer(this.jwtCustomizer);\n\t\tthis.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tOAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();\n\t\taccessTokenGenerator.setAccessTokenCustomizer(this.accessTokenCustomizer);\n\t\tOAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();\n\t\tOAuth2TokenGenerator<OAuth2Token> delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator,\n\t\t\t\taccessTokenGenerator, refreshTokenGenerator);\n\t\tthis.tokenGenerator = spy(new OAuth2TokenGenerator<OAuth2Token>() {\n\t\t\t@Override\n\t\t\tpublic OAuth2Token generate(OAuth2TokenContext context) {\n\t\t\t\treturn delegatingTokenGenerator.generate(context);\n\t\t\t}\n\t\t});\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tthis.dPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\tthis.sessionRegistry = mock(SessionRegistry.class);\n\t\tthis.authenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(this.authorizationService,\n\t\t\t\tthis.tokenGenerator);\n\t\tthis.authenticationProvider.setSessionRegistry(this.sessionRegistry);\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationProvider(null, this.tokenGenerator))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationProvider(this.authorizationService, null))\n\t\t\t.withMessage(\"tokenGenerator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2AuthorizationCodeAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2AuthorizationCodeAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setSessionRegistryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setSessionRegistry(null))\n\t\t\t.withMessage(\"sessionRegistry cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tTestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(),\n\t\t\t\tregisteredClient.getClientSecret());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidCodeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCodeIssuedToAnotherClientThenThrowOAuth2AuthenticationException() {\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = updatedAuthorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCode.isInvalidated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidRedirectUriThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri() + \"-invalid\", null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidatedCodeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(AUTHORIZATION_CODE, Instant.now(),\n\t\t\t\tInstant.now().plusSeconds(120));\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(authorizationCode,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tassertThat(updatedAuthorization.getAccessToken().isInvalidated()).isTrue();\n\t\tassertThat(updatedAuthorization.getRefreshToken().isInvalidated()).isTrue();\n\t}\n\n\t// gh-1233\n\t@Test\n\tpublic void authenticateWhenInvalidatedCodeAndAccessTokenNullThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(AUTHORIZATION_CODE, Instant.now(),\n\t\t\t\tInstant.now().plusSeconds(120));\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient, authorizationCode)\n\t\t\t.token(authorizationCode,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\n\t\tverify(this.authorizationService, never()).save(any());\n\t}\n\n\t// gh-290\n\t@Test\n\tpublic void authenticateWhenExpiredCodeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(AUTHORIZATION_CODE,\n\t\t\t\tInstant.now().minusSeconds(300), Instant.now().minusSeconds(60));\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(authorizationCode)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotGeneratedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\twillAnswer((answer) -> {\n\t\t\tOAuth2TokenContext context = answer.getArgument(0);\n\t\t\tif (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn answer.callRealMethod();\n\t\t\t}\n\t\t}).given(this.tokenGenerator).generate(any());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription()).contains(\"The token generator failed to generate the access token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidRefreshTokenGeneratedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt());\n\n\t\twillAnswer((answer) -> {\n\t\t\tOAuth2TokenContext context = answer.getArgument(0);\n\t\t\tif (OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())) {\n\t\t\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\", Instant.now(),\n\t\t\t\t\t\tInstant.now().plusSeconds(300));\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn answer.callRealMethod();\n\t\t\t}\n\t\t}).given(this.tokenGenerator).generate(any());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription())\n\t\t\t\t\t.contains(\"The token generator failed to generate a valid refresh token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenNotGeneratedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt());\n\n\t\twillAnswer((answer) -> {\n\t\t\tOAuth2TokenContext context = answer.getArgument(0);\n\t\t\tif (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn answer.callRealMethod();\n\t\t\t}\n\t\t}).given(this.tokenGenerator).generate(any());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription()).contains(\"The token generator failed to generate the ID token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidCodeThenReturnAccessToken() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"dpop_proof\", generateDPoPProof(\"http://localhost/oauth2/token\"));\n\t\tadditionalParameters.put(\"dpop_method\", \"POST\");\n\t\tadditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), additionalParameters);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt());\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);\n\t\tverify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture());\n\t\tJwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue();\n\t\tassertThat(jwtEncodingContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(jwtEncodingContext.<Authentication>getPrincipal())\n\t\t\t.isEqualTo(authorization.getAttribute(Principal.class.getName()));\n\t\tassertThat(jwtEncodingContext.getAuthorization()).isEqualTo(authorization);\n\t\tassertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(jwtEncodingContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(jwtEncodingContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authentication);\n\t\tassertThat(jwtEncodingContext.getJwsHeader()).isNotNull();\n\t\tassertThat(jwtEncodingContext.getClaims()).isNotNull();\n\t\tassertThat(jwtEncodingContext.<Jwt>get(OAuth2TokenContext.DPOP_PROOF_KEY)).isNotNull();\n\n\t\tArgumentCaptor<JwtEncoderParameters> jwtEncoderParametersCaptor = ArgumentCaptor\n\t\t\t.forClass(JwtEncoderParameters.class);\n\t\tverify(this.jwtEncoder).encode(jwtEncoderParametersCaptor.capture());\n\t\tJwtClaimsSet jwtClaimsSet = jwtEncoderParametersCaptor.getValue().getClaims();\n\n\t\tSet<String> scopes = jwtClaimsSet.getClaim(OAuth2ParameterNames.SCOPE);\n\t\tassertThat(scopes).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(jwtClaimsSet.getSubject()).isEqualTo(authorization.getPrincipalName());\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\n\t\tassertThat(accessTokenAuthentication.getRegisteredClient().getId())\n\t\t\t.isEqualTo(updatedAuthorization.getRegisteredClientId());\n\t\tassertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(accessTokenAuthentication.getAccessToken())\n\t\t\t.isEqualTo(updatedAuthorization.getAccessToken().getToken());\n\t\tassertThat(accessTokenAuthentication.getAccessToken().getScopes())\n\t\t\t.isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(accessTokenAuthentication.getRefreshToken()).isNotNull();\n\t\tassertThat(accessTokenAuthentication.getRefreshToken())\n\t\t\t.isEqualTo(updatedAuthorization.getRefreshToken().getToken());\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = updatedAuthorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCode.isInvalidated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidCodeAndAuthenticationRequestThenReturnIdToken() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tOAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(\"code\", Instant.now(),\n\t\t\t\tInstant.now().plusSeconds(120));\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient, authorizationCode)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt());\n\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\n\t\tList<SessionInformation> sessions = new ArrayList<>();\n\t\tsessions.add(new SessionInformation(principal.getPrincipal(), \"session3\", Date.from(Instant.now())));\n\t\tsessions.add(new SessionInformation(principal.getPrincipal(), \"session2\",\n\t\t\t\tDate.from(Instant.now().minus(1, ChronoUnit.HOURS))));\n\t\tsessions.add(new SessionInformation(principal.getPrincipal(), \"session1\",\n\t\t\t\tDate.from(Instant.now().minus(2, ChronoUnit.HOURS))));\n\t\tSessionInformation expectedSession = sessions.get(0); // Most recent\n\t\tgiven(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(false))).willReturn(sessions);\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);\n\t\tverify(this.jwtCustomizer, times(2)).customize(jwtEncodingContextCaptor.capture());\n\t\t// Access Token context\n\t\tJwtEncodingContext accessTokenContext = jwtEncodingContextCaptor.getAllValues().get(0);\n\t\tassertThat(accessTokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(accessTokenContext.<Authentication>getPrincipal()).isEqualTo(principal);\n\t\tassertThat(accessTokenContext.getAuthorization()).isEqualTo(authorization);\n\t\tassertThat(accessTokenContext.getAuthorization().getAccessToken()).isNull();\n\t\tassertThat(accessTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(accessTokenContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(accessTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(accessTokenContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authentication);\n\t\tassertThat(accessTokenContext.getJwsHeader()).isNotNull();\n\t\tassertThat(accessTokenContext.getClaims()).isNotNull();\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\taccessTokenContext.getClaims().claims(claims::putAll);\n\t\tassertThat(claims).flatExtracting(OAuth2ParameterNames.SCOPE)\n\t\t\t.containsExactlyInAnyOrder(OidcScopes.OPENID, \"scope1\");\n\t\t// ID Token context\n\t\tJwtEncodingContext idTokenContext = jwtEncodingContextCaptor.getAllValues().get(1);\n\t\tassertThat(idTokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(idTokenContext.<Authentication>getPrincipal()).isEqualTo(principal);\n\t\tassertThat(idTokenContext.getAuthorization()).isNotEqualTo(authorization);\n\t\tassertThat(idTokenContext.getAuthorization().getAccessToken()).isNotNull();\n\t\tassertThat(idTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(idTokenContext.getTokenType().getValue()).isEqualTo(OidcParameterNames.ID_TOKEN);\n\t\tassertThat(idTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(idTokenContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authentication);\n\t\tSessionInformation sessionInformation = idTokenContext.get(SessionInformation.class);\n\t\tassertThat(sessionInformation).isNotNull();\n\t\tassertThat(sessionInformation.getSessionId()).isEqualTo(createHash(expectedSession.getSessionId()));\n\t\tassertThat(idTokenContext.getJwsHeader()).isNotNull();\n\t\tassertThat(idTokenContext.getClaims()).isNotNull();\n\n\t\tverify(this.jwtEncoder, times(2)).encode(any()); // Access token and ID Token\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\n\t\tassertThat(accessTokenAuthentication.getRegisteredClient().getId())\n\t\t\t.isEqualTo(updatedAuthorization.getRegisteredClientId());\n\t\tassertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(accessTokenAuthentication.getAccessToken())\n\t\t\t.isEqualTo(updatedAuthorization.getAccessToken().getToken());\n\t\tSet<String> accessTokenScopes = new HashSet<>(updatedAuthorization.getAuthorizedScopes());\n\t\tassertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(accessTokenScopes);\n\t\tassertThat(accessTokenAuthentication.getRefreshToken()).isNotNull();\n\t\tassertThat(accessTokenAuthentication.getRefreshToken())\n\t\t\t.isEqualTo(updatedAuthorization.getRefreshToken().getToken());\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCodeToken = updatedAuthorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCodeToken.isInvalidated()).isTrue();\n\t\tOAuth2Authorization.Token<OidcIdToken> idToken = updatedAuthorization.getToken(OidcIdToken.class);\n\t\tassertThat(idToken).isNotNull();\n\t\tassertThat(accessTokenAuthentication.getAdditionalParameters())\n\t\t\t.containsExactly(entry(OidcParameterNames.ID_TOKEN, idToken.getToken().getTokenValue()));\n\t}\n\n\t// gh-296\n\t@Test\n\tpublic void authenticateWhenPublicClientThenRefreshTokenNotIssued() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.NONE, null);\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt());\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);\n\t\tverify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture());\n\t\tJwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue();\n\t\tassertThat(jwtEncodingContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(jwtEncodingContext.<Authentication>getPrincipal())\n\t\t\t.isEqualTo(authorization.getAttribute(Principal.class.getName()));\n\t\tassertThat(jwtEncodingContext.getAuthorization()).isEqualTo(authorization);\n\t\tassertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(jwtEncodingContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(jwtEncodingContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authentication);\n\t\tassertThat(jwtEncodingContext.getJwsHeader()).isNotNull();\n\t\tassertThat(jwtEncodingContext.getClaims()).isNotNull();\n\n\t\tArgumentCaptor<JwtEncoderParameters> jwtEncoderParametersCaptor = ArgumentCaptor\n\t\t\t.forClass(JwtEncoderParameters.class);\n\t\tverify(this.jwtEncoder).encode(jwtEncoderParametersCaptor.capture());\n\t\tJwtClaimsSet jwtClaimsSet = jwtEncoderParametersCaptor.getValue().getClaims();\n\n\t\tSet<String> scopes = jwtClaimsSet.getClaim(OAuth2ParameterNames.SCOPE);\n\t\tassertThat(scopes).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(jwtClaimsSet.getSubject()).isEqualTo(authorization.getPrincipalName());\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\n\t\tassertThat(accessTokenAuthentication.getRegisteredClient().getId())\n\t\t\t.isEqualTo(updatedAuthorization.getRegisteredClientId());\n\t\tassertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(accessTokenAuthentication.getAccessToken())\n\t\t\t.isEqualTo(updatedAuthorization.getAccessToken().getToken());\n\t\tassertThat(accessTokenAuthentication.getAccessToken().getScopes())\n\t\t\t.isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(accessTokenAuthentication.getRefreshToken()).isNull();\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = updatedAuthorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCode.isInvalidated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenTokenTimeToLiveConfiguredThenTokenExpirySet() {\n\t\tDuration accessTokenTTL = Duration.ofHours(2);\n\t\tDuration refreshTokenTTL = Duration.ofDays(1);\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.tokenSettings(TokenSettings.builder()\n\t\t\t\t.accessTokenTimeToLive(accessTokenTTL)\n\t\t\t\t.refreshTokenTimeToLive(refreshTokenTTL)\n\t\t\t\t.build())\n\t\t\t.build();\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tInstant accessTokenIssuedAt = Instant.now();\n\t\tInstant accessTokenExpiresAt = accessTokenIssuedAt.plus(accessTokenTTL);\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt(accessTokenIssuedAt, accessTokenExpiresAt));\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\n\t\tassertThat(accessTokenAuthentication.getAccessToken())\n\t\t\t.isEqualTo(updatedAuthorization.getAccessToken().getToken());\n\t\tInstant expectedAccessTokenExpiresAt = accessTokenAuthentication.getAccessToken()\n\t\t\t.getIssuedAt()\n\t\t\t.plus(accessTokenTTL);\n\t\tassertThat(accessTokenAuthentication.getAccessToken().getExpiresAt())\n\t\t\t.isBetween(expectedAccessTokenExpiresAt.minusSeconds(1), expectedAccessTokenExpiresAt.plusSeconds(1));\n\n\t\tassertThat(accessTokenAuthentication.getRefreshToken())\n\t\t\t.isEqualTo(updatedAuthorization.getRefreshToken().getToken());\n\t\tInstant expectedRefreshTokenExpiresAt = accessTokenAuthentication.getRefreshToken()\n\t\t\t.getIssuedAt()\n\t\t\t.plus(refreshTokenTTL);\n\t\tassertThat(accessTokenAuthentication.getRefreshToken().getExpiresAt())\n\t\t\t.isBetween(expectedRefreshTokenExpiresAt.minusSeconds(1), expectedRefreshTokenExpiresAt.plusSeconds(1));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRefreshTokenGrantNotConfiguredThenRefreshTokenNotIssued() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantTypes((grantTypes) -> grantTypes.remove(AuthorizationGrantType.REFRESH_TOKEN))\n\t\t\t.build();\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt());\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertThat(accessTokenAuthentication.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenFormatReferenceThenAccessTokenGeneratorCalled() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.tokenSettings(TokenSettings.builder()\n\t\t\t\t\t\t.accessTokenFormat(OAuth2TokenFormat.REFERENCE)\n\t\t\t\t\t\t.build())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tAUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tthis.authenticationProvider.authenticate(authentication);\n\n\t\tverify(this.accessTokenCustomizer).customize(any());\n\t}\n\n\tprivate static Jwt createJwt() {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\t\treturn createJwt(issuedAt, expiresAt);\n\t}\n\n\tprivate static Jwt createJwt(Instant issuedAt, Instant expiresAt) {\n\t\treturn Jwt.withTokenValue(\"token\")\n\t\t\t.header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName())\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt)\n\t\t\t.build();\n\t}\n\n\tprivate static String createHash(String value) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n\tprivate String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AuthorizationCodeAuthenticationToken}.\n *\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n */\npublic class OAuth2AuthorizationCodeAuthenticationTokenTests {\n\n\tprivate String code = \"code\";\n\n\tprivate RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,\n\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());\n\n\tprivate String redirectUri = \"redirectUri\";\n\n\tprivate Map<String, Object> additionalParameters = Collections.singletonMap(\"param1\", \"value1\");\n\n\t@Test\n\tpublic void constructorWhenCodeNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(null, this.clientPrincipal,\n\t\t\t\t\tthis.redirectUri, null))\n\t\t\t.withMessage(\"code cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(this.code, null, this.redirectUri, null))\n\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalProvidedThenCreated() {\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tthis.code, this.clientPrincipal, this.redirectUri, this.additionalParameters);\n\t\tassertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getCode()).isEqualTo(this.code);\n\t\tassertThat(authentication.getRedirectUri()).isEqualTo(this.redirectUri);\n\t\tassertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);\n\t}\n\n\t@Test\n\tpublic void getAdditionalParametersWhenUpdateThenThrowUnsupportedOperationException() {\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tthis.code, this.clientPrincipal, this.redirectUri, this.additionalParameters);\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class)\n\t\t\t.isThrownBy(() -> authentication.getAdditionalParameters().put(\"another_key\", 1));\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class)\n\t\t\t.isThrownBy(() -> authentication.getAdditionalParameters().remove(\"some_key\"));\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class)\n\t\t\t.isThrownBy(() -> authentication.getAdditionalParameters().clear());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2AuthorizationCodeRequestAuthenticationProvider}.\n *\n * @author Joe Grandja\n * @author Steve Riesenberg\n */\npublic class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"https://provider.com/oauth2/authorize\";\n\n\t// See RFC 7636: Appendix B. Example for the S256 code_challenge_method\n\t// https://tools.ietf.org/html/rfc7636#appendix-B\n\tprivate static final String S256_CODE_VERIFIER = \"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\n\tprivate static final String S256_CODE_CHALLENGE = \"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\";\n\n\tprivate static final String STATE = \"state\";\n\n\tprivate static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2AuthorizationConsentService authorizationConsentService;\n\n\tprivate OAuth2AuthorizationCodeRequestAuthenticationProvider authenticationProvider;\n\n\tprivate TestingAuthenticationToken principal;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authorizationConsentService = mock(OAuth2AuthorizationConsentService.class);\n\t\tthis.authenticationProvider = new OAuth2AuthorizationCodeRequestAuthenticationProvider(\n\t\t\t\tthis.registeredClientRepository, this.authorizationService, this.authorizationConsentService);\n\t\tthis.principal = new TestingAuthenticationToken(\"principalName\", \"password\");\n\t\tthis.principal.setAuthenticated(true);\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationProvider(null, this.authorizationService,\n\t\t\t\t\tthis.authorizationConsentService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\t\tnull, this.authorizationConsentService))\n\t\t\t.isInstanceOf(IllegalArgumentException.class)\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationConsentServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\t\tthis.authorizationService, null))\n\t\t\t.withMessage(\"authorizationConsentService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2AuthorizationCodeRequestAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2AuthorizationCodeRequestAuthenticationToken.class))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void setAuthorizationCodeGeneratorWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthorizationCodeGenerator(null))\n\t\t\t.withMessage(\"authorizationCodeGenerator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthenticationValidator(null))\n\t\t\t.isInstanceOf(IllegalArgumentException.class)\n\t\t\t.withMessage(\"authenticationValidator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationConsentRequiredWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthorizationConsentRequired(null))\n\t\t\t.withMessage(\"authorizationConsentRequired cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClientIdThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ID, null));\n\t}\n\n\t// gh-243\n\t@Test\n\tpublic void authenticateWhenInvalidRedirectUriHostThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, \"https:///invalid\", STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t// gh-243\n\t@Test\n\tpublic void authenticateWhenInvalidRedirectUriFragmentThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, \"https://example.com#fragment\",\n\t\t\t\tSTATE, registeredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnregisteredRedirectUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, \"https://invalid-example.com\", STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t// gh-243\n\t@Test\n\tpublic void authenticateWhenRedirectUriIPv4LoopbackAndDifferentPortThenReturnAuthorizationCode() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.redirectUri(\"https://127.0.0.1:8080\")\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, \"https://127.0.0.1:5000\", STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,\n\t\t\t\tauthenticationResult);\n\t}\n\n\t// gh-243\n\t@Test\n\tpublic void authenticateWhenRedirectUriIPv6LoopbackAndDifferentPortThenReturnAuthorizationCode() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.redirectUri(\"https://[::1]:8080\")\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, \"https://[::1]:5000\", STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,\n\t\t\t\tauthenticationResult);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingRedirectUriAndMultipleRegisteredThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.redirectUri(\"https://example2.com\")\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, null, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationRequestMissingRedirectUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\t// redirect_uri is REQUIRED for OpenID Connect requests\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, null, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientNotAuthorizedToRequestCodeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantTypes(Set::clear)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.UNAUTHORIZED_CLIENT,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ID, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidScopeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tCollections.singleton(\"invalid-scope\"), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_SCOPE,\n\t\t\t\t\tOAuth2ParameterNames.SCOPE, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceRequiredAndMissingCodeChallengeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tPkceParameterNames.CODE_CHALLENGE, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceUnsupportedCodeChallengeMethodThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[0];\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"unsupported\");\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), additionalParameters);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tPkceParameterNames.CODE_CHALLENGE_METHOD, authentication.getRedirectUri()));\n\t}\n\n\t// gh-770\n\t@Test\n\tpublic void authenticateWhenPkceMissingCodeChallengeMethodThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), additionalParameters);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tPkceParameterNames.CODE_CHALLENGE_METHOD, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationRequestWithPromptNoneLoginThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tassertWhenAuthenticationRequestWithPromptThenThrowOAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\t\t\"none login\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationRequestWithPromptNoneConsentThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tassertWhenAuthenticationRequestWithPromptThenThrowOAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\t\t\"none consent\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationRequestWithPromptNoneSelectAccountThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tassertWhenAuthenticationRequestWithPromptThenThrowOAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\t\t\"none select_account\");\n\t}\n\n\tprivate void assertWhenAuthenticationRequestWithPromptThenThrowOAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\tString prompt) {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tMap<String, Object> additionalParameters = createPkceParameters();\n\t\tadditionalParameters.put(\"prompt\", prompt);\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), additionalParameters);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST, \"prompt\",\n\t\t\t\t\tauthentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotAuthenticatedAndPromptNoneThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tthis.principal.setAuthenticated(false);\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tMap<String, Object> additionalParameters = createPkceParameters();\n\t\tadditionalParameters.put(\"prompt\", \"none\");\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), additionalParameters);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, \"login_required\", \"prompt\",\n\t\t\t\t\tauthentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tthis.principal.setAuthenticated(false);\n\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST, \"principal\",\n\t\t\t\t\tauthentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRequireAuthorizationConsentAndPromptNoneThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.scope(OidcScopes.OPENID)\n\t\t\t.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tMap<String, Object> additionalParameters = createPkceParameters();\n\t\tadditionalParameters.put(\"prompt\", \"none\");\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), additionalParameters);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, \"consent_required\", \"prompt\",\n\t\t\t\t\tauthentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRequireAuthorizationConsentThenReturnAuthorizationConsent() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[0];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tOAuth2AuthorizationConsentAuthenticationToken authenticationResult = (OAuth2AuthorizationConsentAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t\tassertThat(authorizationRequest.getAuthorizationUri()).isEqualTo(authentication.getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(authentication.getRedirectUri());\n\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(authentication.getScopes());\n\t\tassertThat(authorizationRequest.getState()).isEqualTo(authentication.getState());\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).isEqualTo(authentication.getAdditionalParameters());\n\n\t\tassertThat(authorization.getRegisteredClientId()).isEqualTo(registeredClient.getId());\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorization.<Authentication>getAttribute(Principal.class.getName())).isEqualTo(this.principal);\n\t\tString state = authorization.getAttribute(OAuth2ParameterNames.STATE);\n\t\tassertThat(state).isNotNull();\n\t\tassertThat(state).isNotEqualTo(authentication.getState());\n\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authenticationResult.getAuthorizationUri()).isEqualTo(authorizationRequest.getAuthorizationUri());\n\t\tassertThat(authenticationResult.getScopes()).isEmpty();\n\t\tassertThat(authenticationResult.getState()).isEqualTo(state);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRequireAuthorizationConsentAndOnlyOpenidScopeRequestedThenAuthorizationConsentNotRequired() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())\n\t\t\t.scopes((scopes) -> {\n\t\t\t\tscopes.clear();\n\t\t\t\tscopes.add(OidcScopes.OPENID);\n\t\t\t})\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,\n\t\t\t\tauthenticationResult);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRequireAuthorizationConsentAndAllPreviouslyApprovedThenAuthorizationConsentNotRequired() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())\n\t\t\t.build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClient.getId(),\n\t\t\t\tthis.principal.getName());\n\t\tregisteredClient.getScopes().forEach(builder::scope);\n\t\tOAuth2AuthorizationConsent previousAuthorizationConsent = builder.build();\n\t\tgiven(this.authorizationConsentService.findById(eq(registeredClient.getId()), eq(this.principal.getName())))\n\t\t\t.willReturn(previousAuthorizationConsent);\n\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,\n\t\t\t\tauthenticationResult);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthorizationConsentRequiredThenUsed() {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tPredicate<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationConsentRequired = mock(\n\t\t\t\tPredicate.class);\n\t\tthis.authenticationProvider.setAuthorizationConsentRequired(authorizationConsentRequired);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,\n\t\t\t\tauthenticationResult);\n\n\t\tverify(authorizationConsentRequired).test(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationCodeRequestValidThenReturnAuthorizationCode() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[0];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,\n\t\t\t\tauthenticationResult);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationCodeRequestWithRequestUriThenReturnAuthorizationCode() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = OAuth2PushedAuthorizationRequestUri\n\t\t\t.create();\n\t\tMap<String, Object> additionalParameters = createPkceParameters();\n\t\tadditionalParameters.put(OAuth2ParameterNames.REQUEST_URI, pushedAuthorizationRequestUri.getRequestUri());\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, additionalParameters)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(pushedAuthorizationRequestUri.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, null, null, null,\n\t\t\t\tadditionalParameters);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,\n\t\t\t\tauthenticationResult);\n\t\tverify(this.authorizationService).remove(eq(authorization));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationCodeRequestWithInvalidRequestUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\t\tOAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = OAuth2PushedAuthorizationRequestUri\n\t\t\t.create();\n\t\tMap<String, Object> additionalParameters = createPkceParameters();\n\t\tadditionalParameters.put(OAuth2ParameterNames.REQUEST_URI, pushedAuthorizationRequestUri.getRequestUri());\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, additionalParameters)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(pushedAuthorizationRequestUri.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, null, null, null,\n\t\t\t\tCollections.singletonMap(OAuth2ParameterNames.REQUEST_URI, \"invalid_request_uri\"));\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REQUEST_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationCodeRequestWithRequestUriIssuedToAnotherClientThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tRegisteredClient anotherRegisteredClient = TestRegisteredClients.registeredClient2().build();\n\n\t\tOAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = OAuth2PushedAuthorizationRequestUri\n\t\t\t.create();\n\t\tMap<String, Object> additionalParameters = createPkceParameters();\n\t\tadditionalParameters.put(OAuth2ParameterNames.REQUEST_URI, pushedAuthorizationRequestUri.getRequestUri());\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, additionalParameters)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(pushedAuthorizationRequestUri.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, anotherRegisteredClient.getClientId(), this.principal, null, null, null,\n\t\t\t\tadditionalParameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ID, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationCodeRequestWithExpiredRequestUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\t\tOAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = OAuth2PushedAuthorizationRequestUri\n\t\t\t.create(Instant.now().minusSeconds(5));\n\t\tMap<String, Object> additionalParameters = createPkceParameters();\n\t\tadditionalParameters.put(OAuth2ParameterNames.REQUEST_URI, pushedAuthorizationRequestUri.getRequestUri());\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, additionalParameters)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(pushedAuthorizationRequestUri.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, null, null, null,\n\t\t\t\tadditionalParameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REQUEST_URI, null));\n\t\tverify(this.authorizationService).remove(eq(authorization));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationCodeNotGeneratedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tOAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator = mock(OAuth2TokenGenerator.class);\n\t\tthis.authenticationProvider.setAuthorizationCodeGenerator(authorizationCodeGenerator);\n\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription())\n\t\t\t\t\t.contains(\"The token generator failed to generate the authorization code.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationValidatorThenUsed() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator = mock(Consumer.class);\n\t\tthis.authenticationProvider.setAuthenticationValidator(authenticationValidator);\n\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,\n\t\t\t\tauthenticationResult);\n\n\t\tverify(authenticationValidator).accept(any());\n\t}\n\n\tprivate void assertAuthorizationCodeRequestWithAuthorizationCodeResult(RegisteredClient registeredClient,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication,\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult) {\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t\tassertThat(authorizationRequest.getAuthorizationUri()).isEqualTo(authentication.getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(registeredClient.getClientId());\n\n\t\tString requestUri = (String) authentication.getAdditionalParameters().get(OAuth2ParameterNames.REQUEST_URI);\n\t\tif (!StringUtils.hasText(requestUri)) {\n\t\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(authentication.getRedirectUri());\n\t\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(authentication.getScopes());\n\t\t\tassertThat(authorizationRequest.getState()).isEqualTo(authentication.getState());\n\t\t}\n\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).isEqualTo(authentication.getAdditionalParameters());\n\t\tassertThat(authorization.getRegisteredClientId()).isEqualTo(registeredClient.getId());\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorization.<Authentication>getAttribute(Principal.class.getName())).isEqualTo(this.principal);\n\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tSet<String> authorizedScopes = authorization.getAuthorizedScopes();\n\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authenticationResult.getAuthorizationUri()).isEqualTo(authorizationRequest.getAuthorizationUri());\n\t\tassertThat(authenticationResult.getRedirectUri()).isEqualTo(authorizationRequest.getRedirectUri());\n\t\tassertThat(authenticationResult.getScopes()).isEqualTo(authorizedScopes);\n\t\tassertThat(authenticationResult.getState()).isEqualTo(authorizationRequest.getState());\n\t\tassertThat(authenticationResult.getAuthorizationCode()).isEqualTo(authorizationCode.getToken());\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t}\n\n\tprivate static void assertAuthenticationException(\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationException authenticationException, String errorCode,\n\t\t\tString parameterName, String redirectUri) {\n\n\t\tOAuth2Error error = authenticationException.getError();\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t\tassertThat(error.getDescription()).contains(parameterName);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationException\n\t\t\t.getAuthorizationCodeRequestAuthentication();\n\t\tassertThat(authorizationCodeRequestAuthentication.getRedirectUri()).isEqualTo(redirectUri);\n\t}\n\n\tprivate static Map<String, Object> createPkceParameters() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationCodeRequestAuthenticationTokenTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"https://provider.com/oauth2/authorize\";\n\n\tprivate static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build();\n\n\tprivate static final TestingAuthenticationToken PRINCIPAL = new TestingAuthenticationToken(\"principalName\",\n\t\t\t\"password\");\n\n\tprivate static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode(\"code\", Instant.now(),\n\t\t\tInstant.now().plus(5, ChronoUnit.MINUTES));\n\n\t@Test\n\tpublic void constructorWhenAuthorizationUriNotProvidedThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationToken(null,\n\t\t\t\t\tREGISTERED_CLIENT.getClientId(), PRINCIPAL, null, null, (Set<String>) null, null))\n\t\t\t.withMessage(\"authorizationUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientIdNotProvidedThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, null, PRINCIPAL,\n\t\t\t\t\tnull, null, (Set<String>) null, null))\n\t\t\t.withMessage(\"clientId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrincipalNotProvidedThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI,\n\t\t\t\t\tREGISTERED_CLIENT.getClientId(), null, null, null, (Set<String>) null, null))\n\t\t\t.withMessage(\"principal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationCodeNotProvidedThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI,\n\t\t\t\t\tREGISTERED_CLIENT.getClientId(), PRINCIPAL, null, null, null, (Set<String>) null))\n\t\t\t.withMessage(\"authorizationCode cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationRequestThenValuesAreSet() {\n\t\tString clientId = REGISTERED_CLIENT.getClientId();\n\t\tString redirectUri = REGISTERED_CLIENT.getRedirectUris().iterator().next();\n\t\tString state = \"state\";\n\t\tSet<String> requestedScopes = REGISTERED_CLIENT.getScopes();\n\t\tMap<String, Object> additionalParameters = Collections.singletonMap(\"param1\", \"value1\");\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, clientId, PRINCIPAL, redirectUri, state, requestedScopes, additionalParameters);\n\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(PRINCIPAL);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(authentication.getAuthorities()).isEmpty();\n\t\tassertThat(authentication.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);\n\t\tassertThat(authentication.getClientId()).isEqualTo(clientId);\n\t\tassertThat(authentication.getRedirectUri()).isEqualTo(redirectUri);\n\t\tassertThat(authentication.getState()).isEqualTo(state);\n\t\tassertThat(authentication.getScopes()).containsExactlyInAnyOrderElementsOf(requestedScopes);\n\t\tassertThat(authentication.getAdditionalParameters()).containsExactlyInAnyOrderEntriesOf(additionalParameters);\n\t\tassertThat(authentication.getAuthorizationCode()).isNull();\n\t\tassertThat(authentication.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationResponseThenValuesAreSet() {\n\t\tString clientId = REGISTERED_CLIENT.getClientId();\n\t\tString redirectUri = REGISTERED_CLIENT.getRedirectUris().iterator().next();\n\t\tString state = \"state\";\n\t\tSet<String> authorizedScopes = REGISTERED_CLIENT.getScopes();\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, clientId, PRINCIPAL, AUTHORIZATION_CODE, redirectUri, state, authorizedScopes);\n\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(PRINCIPAL);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(authentication.getAuthorities()).isEmpty();\n\t\tassertThat(authentication.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);\n\t\tassertThat(authentication.getClientId()).isEqualTo(clientId);\n\t\tassertThat(authentication.getRedirectUri()).isEqualTo(redirectUri);\n\t\tassertThat(authentication.getState()).isEqualTo(state);\n\t\tassertThat(authentication.getScopes()).containsExactlyInAnyOrderElementsOf(authorizedScopes);\n\t\tassertThat(authentication.getAdditionalParameters()).isEmpty();\n\t\tassertThat(authentication.getAuthorizationCode()).isEqualTo(AUTHORIZATION_CODE);\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AuthorizationConsentAuthenticationContext}.\n *\n * @author Steve Riesenberg\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationConsentAuthenticationContextTests {\n\n\tprivate final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient)\n\t\t.build();\n\n\tprivate final Authentication principal = this.authorization.getAttribute(Principal.class.getName());\n\n\tprivate final OAuth2AuthorizationRequest authorizationRequest = this.authorization\n\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\n\tprivate final OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\tthis.authorizationRequest.getAuthorizationUri(), this.registeredClient.getClientId(), this.principal,\n\t\t\t\"state\", null, null);\n\n\tprivate final OAuth2AuthorizationConsent.Builder authorizationConsentBuilder = OAuth2AuthorizationConsent\n\t\t.withId(this.authorization.getRegisteredClientId(), this.authorization.getPrincipalName());\n\n\t@Test\n\tpublic void withWhenAuthenticationNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationConsentAuthenticationContext.with(null))\n\t\t\t.withMessage(\"authentication cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setWhenValueNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizationConsentAuthenticationContext.Builder builder = OAuth2AuthorizationConsentAuthenticationContext\n\t\t\t.with(this.authorizationConsentAuthentication);\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.authorizationConsent(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.registeredClient(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.authorization(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.authorizationRequest(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.put(null, \"\"));\n\t}\n\n\t@Test\n\tpublic void buildWhenRequiredValueNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizationConsentAuthenticationContext.Builder builder = OAuth2AuthorizationConsentAuthenticationContext\n\t\t\t.with(this.authorizationConsentAuthentication);\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(builder::build)\n\t\t\t.withMessage(\"authorizationConsentBuilder cannot be null\");\n\t\tbuilder.authorizationConsent(this.authorizationConsentBuilder);\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(builder::build)\n\t\t\t.withMessage(\"registeredClient cannot be null\");\n\t\tbuilder.registeredClient(this.registeredClient);\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(builder::build)\n\t\t\t.withMessage(\"authorization cannot be null\");\n\t\tbuilder.authorization(this.authorization);\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(builder::build)\n\t\t\t.withMessage(\"authorizationRequest cannot be null\");\n\t\tbuilder.authorizationRequest(this.authorizationRequest);\n\n\t\tbuilder.build();\n\t}\n\n\t@Test\n\tpublic void buildWhenAllValuesProvidedThenAllValuesAreSet() {\n\t\tOAuth2AuthorizationConsentAuthenticationContext context = OAuth2AuthorizationConsentAuthenticationContext\n\t\t\t.with(this.authorizationConsentAuthentication)\n\t\t\t.authorizationConsent(this.authorizationConsentBuilder)\n\t\t\t.registeredClient(this.registeredClient)\n\t\t\t.authorization(this.authorization)\n\t\t\t.authorizationRequest(this.authorizationRequest)\n\t\t\t.put(\"custom-key-1\", \"custom-value-1\")\n\t\t\t.context((ctx) -> ctx.put(\"custom-key-2\", \"custom-value-2\"))\n\t\t\t.build();\n\n\t\tassertThat(context.<Authentication>getAuthentication()).isEqualTo(this.authorizationConsentAuthentication);\n\t\tassertThat(context.getAuthorizationConsent()).isEqualTo(this.authorizationConsentBuilder);\n\t\tassertThat(context.getRegisteredClient()).isEqualTo(this.registeredClient);\n\t\tassertThat(context.getAuthorization()).isEqualTo(this.authorization);\n\t\tassertThat(context.getAuthorizationRequest()).isEqualTo(this.authorizationRequest);\n\t\tassertThat(context.<String>get(\"custom-key-1\")).isEqualTo(\"custom-value-1\");\n\t\tassertThat(context.<String>get(\"custom-key-2\")).isEqualTo(\"custom-value-2\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2AuthorizationConsentAuthenticationProvider}.\n *\n * @author Joe Grandja\n * @author Steve Riesenberg\n */\npublic class OAuth2AuthorizationConsentAuthenticationProviderTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"https://provider.com/oauth2/authorize\";\n\n\tprivate static final String STATE = \"state\";\n\n\tprivate static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2AuthorizationConsentService authorizationConsentService;\n\n\tprivate OAuth2AuthorizationConsentAuthenticationProvider authenticationProvider;\n\n\tprivate TestingAuthenticationToken principal;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authorizationConsentService = mock(OAuth2AuthorizationConsentService.class);\n\t\tthis.authenticationProvider = new OAuth2AuthorizationConsentAuthenticationProvider(\n\t\t\t\tthis.registeredClientRepository, this.authorizationService, this.authorizationConsentService);\n\t\tthis.principal = new TestingAuthenticationToken(\"principalName\", \"password\");\n\t\tthis.principal.setAuthenticated(true);\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(null, this.authorizationService,\n\t\t\t\t\tthis.authorizationConsentService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\t\tnull, this.authorizationConsentService))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationConsentServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\t\tthis.authorizationService, null))\n\t\t\t.withMessage(\"authorizationConsentService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2AuthorizationConsentAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2AuthorizationConsentAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setAuthorizationCodeGeneratorWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthorizationCodeGenerator(null))\n\t\t\t.withMessage(\"authorizationCodeGenerator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationConsentCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthorizationConsentCustomizer(null))\n\t\t\t.withMessage(\"authorizationConsentCustomizer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidStateThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, registeredClient.getScopes(),\n\t\t\t\tnull);\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.STATE, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, registeredClient.getScopes(),\n\t\t\t\tnull);\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tthis.principal.setAuthenticated(false);\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.STATE, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidPrincipalThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName().concat(\"-other\"))\n\t\t\t.build();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, registeredClient.getScopes(),\n\t\t\t\tnull);\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.STATE, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClientIdThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(\"state\"), eq(STATE_TOKEN_TYPE))).willReturn(authorization);\n\t\tRegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, otherRegisteredClient.getClientId(), this.principal, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ID, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDoesNotMatchClientThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tRegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(otherRegisteredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(\"state\"), eq(STATE_TOKEN_TYPE))).willReturn(authorization);\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, registeredClient.getScopes(),\n\t\t\t\tnull);\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ID, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenScopeNotRequestedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tSet<String> authorizedScopes = new HashSet<>(authorizationRequest.getScopes());\n\t\tauthorizedScopes.add(\"scope-not-requested\");\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, authorizedScopes, null);\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_SCOPE,\n\t\t\t\t\tOAuth2ParameterNames.SCOPE, authorizationRequest.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNotApprovedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, new HashSet<>(), null); // No\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// scopes\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// approved\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.ACCESS_DENIED,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ID, authorizationRequest.getRedirectUri()));\n\n\t\tverify(this.authorizationService).remove(eq(authorization));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenApproveAllThenReturnAuthorizationCode() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tSet<String> authorizedScopes = authorizationRequest.getScopes();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, authorizedScopes, null); // Approve\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// all\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// scopes\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization,\n\t\t\t\tauthenticationResult);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthorizationConsentCustomizerThenUsed() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tSet<String> authorizedScopes = authorizationRequest.getScopes();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, authorizedScopes, null); // Approve\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// all\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// scopes\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tConsumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer = mock(Consumer.class);\n\t\tthis.authenticationProvider.setAuthorizationConsentCustomizer(authorizationConsentCustomizer);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization,\n\t\t\t\tauthenticationResult);\n\n\t\tArgumentCaptor<OAuth2AuthorizationConsentAuthenticationContext> authenticationContextCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizationConsentAuthenticationContext.class);\n\t\tverify(authorizationConsentCustomizer).accept(authenticationContextCaptor.capture());\n\n\t\tOAuth2AuthorizationConsentAuthenticationContext authenticationContext = authenticationContextCaptor.getValue();\n\t\tassertThat(authenticationContext.<Authentication>getAuthentication()).isEqualTo(authentication);\n\t\tassertThat(authenticationContext.getAuthorizationConsent()).isNotNull();\n\t\tassertThat(authenticationContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationContext.getAuthorization()).isEqualTo(authorization);\n\t\tassertThat(authenticationContext.getAuthorizationRequest()).isEqualTo(authorizationRequest);\n\t}\n\n\tprivate void assertAuthorizationConsentRequestWithAuthorizationCodeResult(RegisteredClient registeredClient,\n\t\t\tOAuth2Authorization authorization, OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult) {\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tSet<String> authorizedScopes = authorizationRequest.getScopes();\n\n\t\tArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizationConsent.class);\n\t\tverify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());\n\t\tOAuth2AuthorizationConsent authorizationConsent = authorizationConsentCaptor.getValue();\n\n\t\tassertThat(authorizationConsent.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId());\n\t\tassertThat(authorizationConsent.getPrincipalName()).isEqualTo(authorization.getPrincipalName());\n\t\tassertThat(authorizationConsent.getAuthorities()).hasSize(authorizedScopes.size());\n\t\tassertThat(authorizationConsent.getScopes()).containsExactlyInAnyOrderElementsOf(authorizedScopes);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\n\t\tassertThat(updatedAuthorization.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId());\n\t\tassertThat(updatedAuthorization.getPrincipalName()).isEqualTo(authorization.getPrincipalName());\n\t\tassertThat(updatedAuthorization.getAuthorizationGrantType())\n\t\t\t.isEqualTo(authorization.getAuthorizationGrantType());\n\t\tassertThat(updatedAuthorization.<Authentication>getAttribute(Principal.class.getName()))\n\t\t\t.isEqualTo(authorization.<Authentication>getAttribute(Principal.class.getName()));\n\t\tassertThat(updatedAuthorization\n\t\t\t.<OAuth2AuthorizationRequest>getAttribute(OAuth2AuthorizationRequest.class.getName()))\n\t\t\t.isEqualTo(authorizationRequest);\n\t\tOAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = updatedAuthorization\n\t\t\t.getToken(OAuth2AuthorizationCode.class);\n\t\tassertThat(authorizationCode).isNotNull();\n\t\tassertThat(updatedAuthorization.<String>getAttribute(OAuth2ParameterNames.STATE)).isNull();\n\t\tassertThat(updatedAuthorization.getAuthorizedScopes()).isEqualTo(authorizedScopes);\n\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authenticationResult.getAuthorizationUri()).isEqualTo(authorizationRequest.getAuthorizationUri());\n\t\tassertThat(authenticationResult.getRedirectUri()).isEqualTo(authorizationRequest.getRedirectUri());\n\t\tassertThat(authenticationResult.getScopes()).isEqualTo(authorizedScopes);\n\t\tassertThat(authenticationResult.getState()).isEqualTo(authorizationRequest.getState());\n\t\tassertThat(authenticationResult.getAuthorizationCode()).isEqualTo(authorizationCode.getToken());\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenApproveNoneAndRevokePreviouslyApprovedThenAuthorizationConsentRemoved() {\n\t\tString previouslyApprovedScope = \"message.read\";\n\t\tString requestedScope = \"message.write\";\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(previouslyApprovedScope);\n\t\t\tscopes.add(requestedScope);\n\t\t}).build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, new HashSet<>(), null); // No\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// scopes\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// approved\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tOAuth2AuthorizationConsent previousAuthorizationConsent = OAuth2AuthorizationConsent\n\t\t\t.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())\n\t\t\t.scope(previouslyApprovedScope)\n\t\t\t.build();\n\t\tgiven(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()),\n\t\t\t\teq(authorization.getPrincipalName())))\n\t\t\t.willReturn(previousAuthorizationConsent);\n\n\t\t// Revoke all (including previously approved)\n\t\tthis.authenticationProvider.setAuthorizationConsentCustomizer(\n\t\t\t\t(authorizationConsentContext) -> authorizationConsentContext.getAuthorizationConsent()\n\t\t\t\t\t.authorities(Set::clear));\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.ACCESS_DENIED,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ID, authorizationRequest.getRedirectUri()));\n\n\t\tverify(this.authorizationConsentService).remove(eq(previousAuthorizationConsent));\n\t\tverify(this.authorizationService).remove(eq(authorization));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenApproveSomeAndPreviouslyApprovedThenAuthorizationConsentUpdated() {\n\t\tString previouslyApprovedScope = \"message.read\";\n\t\tString requestedScope = \"message.write\";\n\t\tString otherPreviouslyApprovedScope = \"other.scope\";\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(previouslyApprovedScope);\n\t\t\tscopes.add(requestedScope);\n\t\t}).build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tSet<String> requestedScopes = authorizationRequest.getScopes();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, requestedScopes, null);\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tOAuth2AuthorizationConsent previousAuthorizationConsent = OAuth2AuthorizationConsent\n\t\t\t.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())\n\t\t\t.scope(previouslyApprovedScope)\n\t\t\t.scope(otherPreviouslyApprovedScope)\n\t\t\t.build();\n\t\tgiven(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()),\n\t\t\t\teq(authorization.getPrincipalName())))\n\t\t\t.willReturn(previousAuthorizationConsent);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizationConsent.class);\n\t\tverify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());\n\t\tOAuth2AuthorizationConsent updatedAuthorizationConsent = authorizationConsentCaptor.getValue();\n\n\t\tassertThat(updatedAuthorizationConsent.getRegisteredClientId())\n\t\t\t.isEqualTo(previousAuthorizationConsent.getRegisteredClientId());\n\t\tassertThat(updatedAuthorizationConsent.getPrincipalName())\n\t\t\t.isEqualTo(previousAuthorizationConsent.getPrincipalName());\n\t\tassertThat(updatedAuthorizationConsent.getScopes()).containsExactlyInAnyOrder(previouslyApprovedScope,\n\t\t\t\totherPreviouslyApprovedScope, requestedScope);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tassertThat(updatedAuthorization.getAuthorizedScopes()).isEqualTo(requestedScopes);\n\t\tassertThat(authenticationResult.getScopes()).isEqualTo(requestedScopes);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenApproveNoneAndPreviouslyApprovedThenAuthorizationConsentNotUpdated() {\n\t\tString previouslyApprovedScope = \"message.read\";\n\t\tString requestedScope = \"message.write\";\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(previouslyApprovedScope);\n\t\t\tscopes.add(requestedScope);\n\t\t}).build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(this.principal.getName())\n\t\t\t.build();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, new HashSet<>(), null); // No\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// scopes\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// approved\n\t\tgiven(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tOAuth2AuthorizationConsent previousAuthorizationConsent = OAuth2AuthorizationConsent\n\t\t\t.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())\n\t\t\t.scope(previouslyApprovedScope)\n\t\t\t.build();\n\t\tgiven(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()),\n\t\t\t\teq(authorization.getPrincipalName())))\n\t\t\t.willReturn(previousAuthorizationConsent);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationConsentService, never()).save(any());\n\t\tassertThat(authenticationResult.getScopes()).isEqualTo(Collections.singleton(previouslyApprovedScope));\n\t}\n\n\tprivate static void assertAuthenticationException(\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationException authenticationException, String errorCode,\n\t\t\tString parameterName, String redirectUri) {\n\n\t\tOAuth2Error error = authenticationException.getError();\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t\tassertThat(error.getDescription()).contains(parameterName);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationException\n\t\t\t.getAuthorizationCodeRequestAuthentication();\n\t\tassertThat(authorizationCodeRequestAuthentication.getRedirectUri()).isEqualTo(redirectUri);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2ClientAuthenticationToken}.\n *\n * @author Joe Grandja\n * @author Anoop Garlapati\n */\npublic class OAuth2ClientAuthenticationTokenTests {\n\n\t@Test\n\tpublic void constructorWhenClientIdNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2ClientAuthenticationToken(null, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\t\t\"secret\", null))\n\t\t\t.withMessage(\"clientId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientAuthenticationMethodNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2ClientAuthenticationToken(\"clientId\", null, \"clientSecret\", null))\n\t\t\t.withMessage(\"clientAuthenticationMethod cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2ClientAuthenticationToken(null, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\t\t\"clientSecret\"))\n\t\t\t.withMessage(\"registeredClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientCredentialsProvidedThenCreated() {\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\"clientId\",\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, \"secret\", null);\n\t\tassertThat(authentication.isAuthenticated()).isFalse();\n\t\tassertThat(authentication.getPrincipal().toString()).isEqualTo(\"clientId\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"secret\");\n\t\tassertThat(authentication.getRegisteredClient()).isNull();\n\t\tassertThat(authentication.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientProvidedThenCreated() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\tassertThat(authentication.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authentication.getCredentials().toString()).isEqualTo(registeredClient.getClientSecret());\n\t\tassertThat(authentication.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authentication.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.JwtGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2ClientCredentialsAuthenticationProvider}.\n *\n * @author Alexey Nesterov\n * @author Joe Grandja\n */\npublic class OAuth2ClientCredentialsAuthenticationProviderTests {\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate JwtEncoder jwtEncoder;\n\n\tprivate OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer;\n\n\tprivate OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer;\n\n\tprivate OAuth2TokenGenerator<?> tokenGenerator;\n\n\tprivate JwtEncoder dPoPProofJwtEncoder;\n\n\tprivate OAuth2ClientCredentialsAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.jwtEncoder = mock(JwtEncoder.class);\n\t\tthis.jwtCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tJwtGenerator jwtGenerator = new JwtGenerator(this.jwtEncoder);\n\t\tjwtGenerator.setJwtCustomizer(this.jwtCustomizer);\n\t\tthis.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tOAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();\n\t\taccessTokenGenerator.setAccessTokenCustomizer(this.accessTokenCustomizer);\n\t\tOAuth2TokenGenerator<OAuth2Token> delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator,\n\t\t\t\taccessTokenGenerator);\n\t\tthis.tokenGenerator = spy(new OAuth2TokenGenerator<OAuth2Token>() {\n\t\t\t@Override\n\t\t\tpublic OAuth2Token generate(OAuth2TokenContext context) {\n\t\t\t\treturn delegatingTokenGenerator.generate(context);\n\t\t\t}\n\t\t});\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tthis.dPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\tthis.authenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider(this.authorizationService,\n\t\t\t\tthis.tokenGenerator);\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2ClientCredentialsAuthenticationProvider(null, this.tokenGenerator))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2ClientCredentialsAuthenticationProvider(this.authorizationService, null))\n\t\t\t.withMessage(\"tokenGenerator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenSupportedAuthenticationThenTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2ClientCredentialsAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsWhenUnsupportedAuthenticationThenFalse() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2AuthorizationCodeAuthenticationToken.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthenticationValidator(null))\n\t\t\t.withMessage(\"authenticationValidator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tTestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(),\n\t\t\t\tregisteredClient.getClientSecret());\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientNotAuthorizedToRequestTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2()\n\t\t\t.authorizationGrantTypes((grantTypes) -> grantTypes.remove(AuthorizationGrantType.CLIENT_CREDENTIALS))\n\t\t\t.build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidScopeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, Collections.singleton(\"invalid-scope\"), null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenScopeRequestedThenAccessTokenContainsScope() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tSet<String> requestedScope = Collections.singleton(\"scope1\");\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, requestedScope, null);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt(Collections.singleton(\"mapped-scoped\")));\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(requestedScope);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNoScopeRequestedThenAccessTokenDoesNotContainScope() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt(Collections.singleton(\"mapped-scoped\")));\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotGeneratedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\n\t\tdoReturn(null).when(this.tokenGenerator).generate(any());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription()).contains(\"The token generator failed to generate the access token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidAuthenticationThenReturnAccessToken() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"dpop_proof\", generateDPoPProof(\"http://localhost/oauth2/token\"));\n\t\tadditionalParameters.put(\"dpop_method\", \"POST\");\n\t\tadditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, additionalParameters);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt(registeredClient.getScopes()));\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);\n\t\tverify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture());\n\t\tJwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue();\n\t\tassertThat(jwtEncodingContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(jwtEncodingContext.<Authentication>getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(jwtEncodingContext.getAuthorization()).isNull();\n\t\tassertThat(jwtEncodingContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\tassertThat(jwtEncodingContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authentication);\n\t\tassertThat(jwtEncodingContext.getJwsHeader()).isNotNull();\n\t\tassertThat(jwtEncodingContext.getClaims()).isNotNull();\n\t\tassertThat(jwtEncodingContext.<Jwt>get(OAuth2TokenContext.DPOP_PROOF_KEY)).isNotNull();\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\t\tassertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(authorization.getRegisteredClientId()).isEqualTo(clientPrincipal.getRegisteredClient().getId());\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(clientPrincipal.getName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\tassertThat(authorization.getAccessToken()).isNotNull();\n\t\tassertThat(authorization.getAuthorizedScopes()).isNotNull();\n\t\tassertThat(authorization.getAccessToken().getToken().getScopes())\n\t\t\t.isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(authorization.getAccessToken().getToken());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenFormatReferenceThenAccessTokenGeneratorCalled() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2()\n\t\t\t\t.tokenSettings(TokenSettings.builder()\n\t\t\t\t\t\t.accessTokenFormat(OAuth2TokenFormat.REFERENCE)\n\t\t\t\t\t\t.build())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, null, null);\n\n\t\tthis.authenticationProvider.authenticate(authentication);\n\n\t\tverify(this.accessTokenCustomizer).customize(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationValidatorThenUsed() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tclientPrincipal, registeredClient.getScopes(), null);\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tConsumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator = mock(Consumer.class);\n\t\tthis.authenticationProvider.setAuthenticationValidator(authenticationValidator);\n\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt(registeredClient.getScopes()));\n\n\t\tthis.authenticationProvider.authenticate(authentication);\n\n\t\tverify(authenticationValidator).accept(any(OAuth2ClientCredentialsAuthenticationContext.class));\n\t}\n\n\tprivate static Jwt createJwt(Set<String> scope) {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\t\treturn Jwt.withTokenValue(\"token\")\n\t\t\t.header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName())\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt)\n\t\t\t.claim(OAuth2ParameterNames.SCOPE, scope)\n\t\t\t.build();\n\t}\n\n\tprivate String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2ClientCredentialsAuthenticationToken}.\n *\n * @author Alexey Nesterov\n */\npublic class OAuth2ClientCredentialsAuthenticationTokenTests {\n\n\tprivate final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate final OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\n\t\t\tthis.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\tthis.registeredClient.getClientSecret());\n\n\tprivate Set<String> scopes = Collections.singleton(\"scope1\");\n\n\tprivate Map<String, Object> additionalParameters = Collections.singletonMap(\"param1\", \"value1\");\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> new OAuth2ClientCredentialsAuthenticationToken(null, this.scopes, this.additionalParameters))\n\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalProvidedThenCreated() {\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tthis.clientPrincipal, this.scopes, this.additionalParameters);\n\n\t\tassertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getScopes()).isEqualTo(this.scopes);\n\t\tassertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);\n\t}\n\n\t@Test\n\tpublic void constructorWhenScopesProvidedThenCreated() {\n\t\tSet<String> expectedScopes = Collections.singleton(\"test-scope\");\n\n\t\tOAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(\n\t\t\t\tthis.clientPrincipal, expectedScopes, this.additionalParameters);\n\n\t\tassertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getScopes()).isEqualTo(expectedScopes);\n\t\tassertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientRegistrationAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.TestJwsHeaders;\nimport org.springframework.security.oauth2.jwt.TestJwtClaimsSets;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientMetadataClaimNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2ClientRegistrationAuthenticationProvider}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2ClientRegistrationAuthenticationProviderTests {\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\tprivate OAuth2ClientRegistrationAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.passwordEncoder = spy(new PasswordEncoder() {\n\t\t\t@Override\n\t\t\tpublic String encode(CharSequence rawPassword) {\n\t\t\t\treturn NoOpPasswordEncoder.getInstance().encode(rawPassword);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean matches(CharSequence rawPassword, String encodedPassword) {\n\t\t\t\treturn NoOpPasswordEncoder.getInstance().matches(rawPassword, encodedPassword);\n\t\t\t}\n\t\t});\n\t\tthis.authenticationProvider = new OAuth2ClientRegistrationAuthenticationProvider(\n\t\t\t\tthis.registeredClientRepository, this.authorizationService);\n\t\tthis.authenticationProvider.setPasswordEncoder(this.passwordEncoder);\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2ClientRegistrationAuthenticationProvider(null, this.authorizationService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2ClientRegistrationAuthenticationProvider(this.registeredClientRepository, null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setRegisteredClientConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setRegisteredClientConverter(null))\n\t\t\t.withMessage(\"registeredClientConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))\n\t\t\t.withMessage(\"clientRegistrationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setPasswordEncoderWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setPasswordEncoder(null))\n\t\t\t.withMessage(\"passwordEncoder cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2ClientRegistrationAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2ClientRegistrationAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotOAuth2TokenAuthenticationTokenThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(createJwtClientRegistration());\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwt.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotActiveThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.invalidate(jwtAccessToken)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt(Collections.singleton(\"unauthorized.scope\"));\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_unauthorized.scope\"));\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt(new HashSet<>(Arrays.asList(\"client.create\", \"scope1\")));\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\", \"SCOPE_scope1\"));\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidRedirectUriThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.redirectUri(\"invalid uri\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REDIRECT_URI);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ClientMetadataClaimNames.REDIRECT_URIS);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRedirectUriContainsFragmentThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client.example.com#fragment\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REDIRECT_URI);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ClientMetadataClaimNames.REDIRECT_URIS);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidAccessTokenThenReturnClientRegistration() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\t\tOAuth2ClientRegistrationAuthenticationToken authenticationResult = (OAuth2ClientRegistrationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<RegisteredClient> registeredClientCaptor = ArgumentCaptor.forClass(RegisteredClient.class);\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t\tverify(this.registeredClientRepository).save(registeredClientCaptor.capture());\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverify(this.passwordEncoder).encode(any());\n\n\t\t// assert \"initial\" access token is invalidated\n\t\tOAuth2Authorization authorizationResult = authorizationCaptor.getValue();\n\t\tassertThat(authorizationResult.getAccessToken().isInvalidated()).isTrue();\n\t\tif (authorizationResult.getRefreshToken() != null) {\n\t\t\tassertThat(authorizationResult.getRefreshToken().isInvalidated()).isTrue();\n\t\t}\n\n\t\tassertClientRegistration(clientRegistration, authenticationResult.getClientRegistration(),\n\t\t\t\tregisteredClientCaptor.getValue());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenOpenRegistrationThenReturnClientRegistration() {\n\t\tthis.authenticationProvider.setOpenRegistrationAllowed(true);\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientRegistrationAuthenticationToken authentication = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tnull, clientRegistration);\n\t\tOAuth2ClientRegistrationAuthenticationToken authenticationResult = (OAuth2ClientRegistrationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<RegisteredClient> registeredClientCaptor = ArgumentCaptor.forClass(RegisteredClient.class);\n\n\t\tverifyNoInteractions(this.authorizationService);\n\t\tverify(this.registeredClientRepository).save(registeredClientCaptor.capture());\n\t\tverify(this.passwordEncoder).encode(any());\n\n\t\tassertClientRegistration(clientRegistration, authenticationResult.getClientRegistration(),\n\t\t\t\tregisteredClientCaptor.getValue());\n\t}\n\n\tprivate static void assertClientRegistration(OAuth2ClientRegistration clientRegistrationRequest,\n\t\t\tOAuth2ClientRegistration clientRegistrationResult, RegisteredClient registeredClient) {\n\n\t\tassertThat(registeredClient.getId()).isNotNull();\n\t\tassertThat(registeredClient.getClientId()).isNotNull();\n\t\tassertThat(registeredClient.getClientIdIssuedAt()).isNotNull();\n\t\tassertThat(registeredClient.getClientSecret()).isNotNull();\n\t\tassertThat(registeredClient.getClientName()).isEqualTo(clientRegistrationRequest.getClientName());\n\t\tassertThat(registeredClient.getClientAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registeredClient.getRedirectUris()).containsExactly(\"https://client.example.com\");\n\t\tassertThat(registeredClient.getAuthorizationGrantTypes()).containsExactlyInAnyOrder(\n\t\t\t\tAuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\tassertThat(registeredClient.getScopes()).containsExactlyInAnyOrder(\"scope1\", \"scope2\");\n\t\tassertThat(registeredClient.getClientSettings().isRequireProofKey()).isTrue();\n\t\tassertThat(registeredClient.getClientSettings().isRequireAuthorizationConsent()).isTrue();\n\n\t\tassertThat(clientRegistrationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(clientRegistrationResult.getClientIdIssuedAt()).isEqualTo(registeredClient.getClientIdIssuedAt());\n\t\tassertThat(clientRegistrationResult.getClientSecret()).isEqualTo(registeredClient.getClientSecret());\n\t\tassertThat(clientRegistrationResult.getClientSecretExpiresAt())\n\t\t\t.isEqualTo(registeredClient.getClientSecretExpiresAt());\n\t\tassertThat(clientRegistrationResult.getClientName()).isEqualTo(registeredClient.getClientName());\n\t\tassertThat(clientRegistrationResult.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(registeredClient.getRedirectUris());\n\n\t\tList<String> grantTypes = new ArrayList<>();\n\t\tregisteredClient.getAuthorizationGrantTypes()\n\t\t\t.forEach((authorizationGrantType) -> grantTypes.add(authorizationGrantType.getValue()));\n\t\tassertThat(clientRegistrationResult.getGrantTypes()).containsExactlyInAnyOrderElementsOf(grantTypes);\n\n\t\tassertThat(clientRegistrationResult.getResponseTypes())\n\t\t\t.containsExactly(OAuth2AuthorizationResponseType.CODE.getValue());\n\t\tassertThat(clientRegistrationResult.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(registeredClient.getScopes());\n\t\tassertThat(clientRegistrationResult.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(registeredClient.getClientAuthenticationMethods().iterator().next().getValue());\n\t}\n\n\tprivate static Jwt createJwtClientRegistration() {\n\t\treturn createJwt(Collections.singleton(\"client.create\"));\n\t}\n\n\tprivate static Jwt createJwt(Set<String> scopes) {\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = TestJwsHeaders.jwsHeader()\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet()\n\t\t\t\t.claim(OAuth2ParameterNames.SCOPE, scopes)\n\t\t\t\t.build();\n\t\tJwt jwt = Jwt.withTokenValue(\"jwt-access-token\")\n\t\t\t\t.headers((headers) -> headers.putAll(jwsHeader.getHeaders()))\n\t\t\t\t.claims((claims) -> claims.putAll(jwtClaimsSet.getClaims()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\treturn jwt;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link OAuth2DeviceAuthorizationConsentAuthenticationProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"/oauth2/device_authorization\";\n\n\tprivate static final String DEVICE_CODE = \"EfYu_0jEL\";\n\n\tprivate static final String USER_CODE = \"BCDF-GHJK\";\n\n\tprivate static final String STATE = \"abc123\";\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2AuthorizationConsentService authorizationConsentService;\n\n\tprivate OAuth2DeviceAuthorizationConsentAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authorizationConsentService = mock(OAuth2AuthorizationConsentService.class);\n\t\tthis.authenticationProvider = new OAuth2DeviceAuthorizationConsentAuthenticationProvider(\n\t\t\t\tthis.registeredClientRepository, this.authorizationService, this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceAuthorizationConsentAuthenticationProvider(\n\t\t\t\t\t\tnull, this.authorizationService, this.authorizationConsentService))\n\t\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceAuthorizationConsentAuthenticationProvider(\n\t\t\t\t\t\tthis.registeredClientRepository, null, this.authorizationConsentService))\n\t\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationConsentServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceAuthorizationConsentAuthenticationProvider(\n\t\t\t\t\t\tthis.registeredClientRepository, this.authorizationService, null))\n\t\t\t\t.withMessage(\"authorizationConsentService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizationConsentCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthorizationConsentCustomizer(null))\n\t\t\t\t.withMessageContaining(\"authorizationConsentCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2DeviceAuthorizationConsentAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2DeviceAuthorizationConsentAuthenticationToken.class))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.STATE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalIsNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(authorization.getPrincipalName(), null);\n\t\tAuthentication authentication = new OAuth2DeviceAuthorizationConsentAuthenticationToken(AUTHORIZATION_URI,\n\t\t\t\tregisteredClient.getClientId(), principal, USER_CODE, STATE, null, Collections.emptyMap());\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.STATE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNameDoesNotMatchThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"invalid\", null, Collections.emptyList());\n\t\tAuthentication authentication = new OAuth2DeviceAuthorizationConsentAuthenticationToken(AUTHORIZATION_URI,\n\t\t\t\tregisteredClient.getClientId(), principal, USER_CODE, STATE, null, Collections.emptyMap());\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.STATE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRegisteredClientNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\n\t\tverify(this.registeredClientRepository).findByClientId(registeredClient.getClientId());\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService);\n\t\tverifyNoInteractions(this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRegisteredClientDoesNotMatchAuthorizationThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient2);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findByClientId(anyString())).willReturn(registeredClient);\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\n\t\tverify(this.registeredClientRepository).findByClientId(registeredClient.getClientId());\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService);\n\t\tverifyNoInteractions(this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRequestedScopesNotAuthorizedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient()\n\t\t\t.scopes(Set::clear)\n\t\t\t.scope(\"invalid\")\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findByClientId(anyString())).willReturn(registeredClient);\n\t\tAuthentication authentication = createAuthentication(registeredClient2);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SCOPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE);\n\t\t// @formatter:on\n\n\t\tverify(this.registeredClientRepository).findByClientId(registeredClient.getClientId());\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService);\n\t\tverifyNoInteractions(this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthoritiesIsEmptyThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient().scopes(Set::clear).build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient2);\n\t\tAuthentication authentication = createAuthentication(registeredClient2);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findByClientId(anyString())).willReturn(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.ACCESS_DENIED);\n\t\t// @formatter:on\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverify(this.registeredClientRepository).findByClientId(registeredClient.getClientId());\n\t\tverify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName());\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService,\n\t\t\t\tthis.authorizationConsentService);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tassertThat(updatedAuthorization.<String>getAttribute(OAuth2ParameterNames.STATE)).isNull();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2DeviceCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\tassertThat(updatedAuthorization.getToken(OAuth2UserCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthoritiesIsNotEmptyThenAuthorizationConsentSaved() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findByClientId(anyString())).willReturn(registeredClient);\n\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isSameAs(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getUserCode()).isEqualTo(USER_CODE);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverify(this.registeredClientRepository).findByClientId(registeredClient.getClientId());\n\t\tverify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName());\n\t\tverify(this.authorizationConsentService).save(any(OAuth2AuthorizationConsent.class));\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService,\n\t\t\t\tthis.authorizationConsentService);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tassertThat(updatedAuthorization.getPrincipalName()).isEqualTo(authentication.getName());\n\t\tassertThat(updatedAuthorization.getAuthorizedScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t\tassertThat(updatedAuthorization.<String>getAttribute(OAuth2ParameterNames.STATE)).isNull();\n\t\tassertThat(updatedAuthorization.<Set<String>>getAttribute(OAuth2ParameterNames.SCOPE)).isNull();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2DeviceCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(false);\n\t\tassertThat(updatedAuthorization.getToken(OAuth2UserCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenExistingAuthorizationConsentThenUpdated() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(\"additional\").build();\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient()\n\t\t\t.scopes(Set::clear)\n\t\t\t.scope(\"additional\")\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient2);\n\t\tAuthentication authentication = createAuthentication(registeredClient2);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationConsent authorizationConsent =\n\t\t\t\tOAuth2AuthorizationConsent.withId(registeredClient.getId(), authentication.getName())\n\t\t\t\t\t\t.scope(\"scope1\").build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findByClientId(anyString())).willReturn(registeredClient);\n\t\tgiven(this.authorizationConsentService.findById(anyString(), anyString())).willReturn(authorizationConsent);\n\n\t\tOAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isSameAs(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getUserCode()).isEqualTo(USER_CODE);\n\n\t\tArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizationConsent.class);\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverify(this.registeredClientRepository).findByClientId(registeredClient.getClientId());\n\t\tverify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName());\n\t\tverify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());\n\t\tverify(this.authorizationService).save(any(OAuth2Authorization.class));\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService,\n\t\t\t\tthis.authorizationConsentService);\n\n\t\tOAuth2AuthorizationConsent updatedAuthorizationConsent = authorizationConsentCaptor.getValue();\n\t\tassertThat(updatedAuthorizationConsent.getRegisteredClientId()).isEqualTo(registeredClient.getId());\n\t\tassertThat(updatedAuthorizationConsent.getPrincipalName()).isEqualTo(authentication.getName());\n\t\tassertThat(updatedAuthorizationConsent.getScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationConsentCustomizerSetThenUsed() {\n\t\tSimpleGrantedAuthority customAuthority = new SimpleGrantedAuthority(\"test\");\n\t\tthis.authenticationProvider.setAuthorizationConsentCustomizer(\n\t\t\t\t(context) -> context.getAuthorizationConsent().authority(customAuthority));\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(Set::clear).build();\n\t\tOAuth2Authorization authorization = createAuthorization(registeredClient);\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findByClientId(anyString())).willReturn(registeredClient);\n\t\tgiven(this.authorizationConsentService.findById(anyString(), anyString())).willReturn(null);\n\n\t\tOAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isSameAs(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getUserCode()).isEqualTo(USER_CODE);\n\n\t\tArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizationConsent.class);\n\t\tverify(this.authorizationService).findByToken(STATE,\n\t\t\t\tOAuth2DeviceAuthorizationConsentAuthenticationProvider.STATE_TOKEN_TYPE);\n\t\tverify(this.registeredClientRepository).findByClientId(registeredClient.getClientId());\n\t\tverify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName());\n\t\tverify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());\n\t\tverify(this.authorizationService).save(any(OAuth2Authorization.class));\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService,\n\t\t\t\tthis.authorizationConsentService);\n\n\t\tOAuth2AuthorizationConsent updatedAuthorizationConsent = authorizationConsentCaptor.getValue();\n\t\tassertThat(updatedAuthorizationConsent.getRegisteredClientId()).isEqualTo(registeredClient.getId());\n\t\tassertThat(updatedAuthorizationConsent.getPrincipalName()).isEqualTo(authentication.getName());\n\t\tassertThat(updatedAuthorizationConsent.getAuthorities()).containsExactly(customAuthority);\n\t}\n\n\tprivate static OAuth2Authorization createAuthorization(RegisteredClient registeredClient) {\n\t\t// @formatter:off\n\t\treturn TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode())\n\t\t\t\t.attributes(Map::clear)\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static OAuth2DeviceAuthorizationConsentAuthenticationToken createAuthentication(\n\t\t\tRegisteredClient registeredClient) {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", null,\n\t\t\t\tCollections.emptyList());\n\t\tSet<String> authorizedScopes = registeredClient.getScopes();\n\t\tif (authorizedScopes.isEmpty()) {\n\t\t\tauthorizedScopes = null;\n\t\t}\n\t\tMap<String, Object> additionalParameters = null;\n\t\treturn new OAuth2DeviceAuthorizationConsentAuthenticationToken(AUTHORIZATION_URI,\n\t\t\t\tregisteredClient.getClientId(), principal, USER_CODE, STATE, authorizedScopes, additionalParameters);\n\t}\n\n\tprivate static OAuth2DeviceCode createDeviceCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2DeviceCode(DEVICE_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2UserCode createUserCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static Function<OAuth2Authorization.Token<? extends OAuth2Token>, Boolean> isInvalidated() {\n\t\treturn (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link OAuth2DeviceAuthorizationRequestAuthenticationProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"/oauth2/device_authorization\";\n\n\tprivate static final String DEVICE_CODE = \"EfYu_0jEL\";\n\n\tprivate static final String USER_CODE = \"BCDF-GHJK\";\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2DeviceAuthorizationRequestAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new OAuth2DeviceAuthorizationRequestAuthenticationProvider(\n\t\t\t\tthis.authorizationService);\n\t\tmockAuthorizationServerContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceAuthorizationRequestAuthenticationProvider(null))\n\t\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setDeviceCodeGeneratorWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.setDeviceCodeGenerator(null))\n\t\t\t\t.withMessage(\"deviceCodeGenerator cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setUserCodeGeneratorWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.setUserCodeGenerator(null))\n\t\t\t\t.withMessage(\"userCodeGenerator cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2DeviceAuthorizationRequestAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2DeviceAuthorizationRequestAuthenticationToken.class))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\"client-1\",\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null);\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authentication = new OAuth2DeviceAuthorizationRequestAuthenticationToken(\n\t\t\t\tclientPrincipal, AUTHORIZATION_URI, null, null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidGrantTypeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidScopesThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t.build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);\n\t\tAuthentication authentication = new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal,\n\t\t\t\tAUTHORIZATION_URI, Collections.singleton(\"invalid\"), null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SCOPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenOpenIdScopeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t.scope(OidcScopes.OPENID)\n\t\t\t.build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SCOPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDeviceCodeIsNullThenThrowOAuth2AuthenticationException() {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tOAuth2TokenGenerator<OAuth2DeviceCode> deviceCodeGenerator = mock(OAuth2TokenGenerator.class);\n\t\tgiven(deviceCodeGenerator.generate(any(OAuth2TokenContext.class))).willReturn(null);\n\t\tthis.authenticationProvider.setDeviceCodeGenerator(deviceCodeGenerator);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t.build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(\"The token generator failed to generate the device code.\")\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t// @formatter:on\n\n\t\tverify(deviceCodeGenerator).generate(any(OAuth2TokenContext.class));\n\t\tverifyNoMoreInteractions(deviceCodeGenerator);\n\t\tverifyNoInteractions(this.authorizationService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUserCodeIsNullThenThrowOAuth2AuthenticationException() {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tOAuth2TokenGenerator<OAuth2UserCode> userCodeGenerator = mock(OAuth2TokenGenerator.class);\n\t\tgiven(userCodeGenerator.generate(any(OAuth2TokenContext.class))).willReturn(null);\n\t\tthis.authenticationProvider.setUserCodeGenerator(userCodeGenerator);\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t.build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessageContaining(\"The token generator failed to generate the user code.\")\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t// @formatter:on\n\n\t\tverify(userCodeGenerator).generate(any(OAuth2TokenContext.class));\n\t\tverifyNoMoreInteractions(userCodeGenerator);\n\t\tverifyNoInteractions(this.authorizationService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenScopesRequestedThenReturnDeviceCodeAndUserCode() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t.build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t\tassertThat(authenticationResult.getDeviceCode().getTokenValue()).hasSize(128);\n\t\t// 8 chars + 1 dash\n\t\tassertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\t\tassertThat(authorization.getRegisteredClientId()).isEqualTo(registeredClient.getId());\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(authentication.getName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.DEVICE_CODE);\n\t\tassertThat(authorization.getToken(OAuth2DeviceCode.class)).isNotNull();\n\t\tassertThat(authorization.getToken(OAuth2UserCode.class)).isNotNull();\n\t\tassertThat(authorization.<Set<String>>getAttribute(OAuth2ParameterNames.SCOPE))\n\t\t\t.hasSameElementsAs(registeredClient.getScopes());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNoScopesRequestedThenReturnDeviceCodeAndUserCode() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.scopes(Set::clear)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t.build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t\tassertThat(authenticationResult.getDeviceCode().getTokenValue()).hasSize(128);\n\t\t// 8 chars + 1 dash\n\t\tassertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\t\tassertThat(authorization.getRegisteredClientId()).isEqualTo(registeredClient.getId());\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(authentication.getName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.DEVICE_CODE);\n\t\tassertThat(authorization.getToken(OAuth2DeviceCode.class)).isNotNull();\n\t\tassertThat(authorization.getToken(OAuth2UserCode.class)).isNotNull();\n\t\tassertThat(authorization.<Set<String>>getAttribute(OAuth2ParameterNames.SCOPE))\n\t\t\t.hasSameElementsAs(registeredClient.getScopes());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDeviceCodeGeneratorSetThenUsed() {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tOAuth2TokenGenerator<OAuth2DeviceCode> deviceCodeGenerator = mock(OAuth2TokenGenerator.class);\n\t\tgiven(deviceCodeGenerator.generate(any(OAuth2TokenContext.class))).willReturn(createDeviceCode());\n\t\tthis.authenticationProvider.setDeviceCodeGenerator(deviceCodeGenerator);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t.build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t\tassertThat(authenticationResult.getDeviceCode().getTokenValue()).isEqualTo(DEVICE_CODE);\n\t\t// 8 chars + 1 dash\n\t\tassertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9);\n\n\t\tArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);\n\t\tverify(deviceCodeGenerator).generate(tokenContextCaptor.capture());\n\t\tverify(this.authorizationService).save(any(OAuth2Authorization.class));\n\t\tverifyNoMoreInteractions(this.authorizationService, deviceCodeGenerator);\n\n\t\tOAuth2TokenContext tokenContext = tokenContextCaptor.getValue();\n\t\tassertThat(tokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(tokenContext.<Authentication>getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(tokenContext.getAuthorizationServerContext()).isNotNull();\n\t\tassertThat(tokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.DEVICE_CODE);\n\t\tassertThat(tokenContext.<Authentication>getAuthorizationGrant()).isEqualTo(authentication);\n\t\tassertThat(tokenContext.getTokenType())\n\t\t\t.isEqualTo(OAuth2DeviceAuthorizationRequestAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUserCodeGeneratorSetThenUsed() {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tOAuth2TokenGenerator<OAuth2UserCode> userCodeGenerator = mock(OAuth2TokenGenerator.class);\n\t\tgiven(userCodeGenerator.generate(any(OAuth2TokenContext.class))).willReturn(createUserCode());\n\t\tthis.authenticationProvider.setUserCodeGenerator(userCodeGenerator);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t.build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t\tassertThat(authenticationResult.getDeviceCode().getTokenValue()).hasSize(128);\n\t\tassertThat(authenticationResult.getUserCode().getTokenValue()).isEqualTo(USER_CODE);\n\n\t\tArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);\n\t\tverify(userCodeGenerator).generate(tokenContextCaptor.capture());\n\t\tverify(this.authorizationService).save(any(OAuth2Authorization.class));\n\t\tverifyNoMoreInteractions(this.authorizationService, userCodeGenerator);\n\n\t\tOAuth2TokenContext tokenContext = tokenContextCaptor.getValue();\n\t\tassertThat(tokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(tokenContext.<Authentication>getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(tokenContext.getAuthorizationServerContext()).isNotNull();\n\t\tassertThat(tokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.DEVICE_CODE);\n\t\tassertThat(tokenContext.<Authentication>getAuthorizationGrant()).isEqualTo(authentication);\n\t\tassertThat(tokenContext.getTokenType())\n\t\t\t.isEqualTo(OAuth2DeviceAuthorizationRequestAuthenticationProvider.USER_CODE_TOKEN_TYPE);\n\t}\n\n\tprivate static void mockAuthorizationServerContext() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\t\tTestAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(\n\t\t\t\tauthorizationServerSettings, () -> \"https://provider.com\");\n\t\tAuthorizationServerContextHolder.setContext(authorizationServerContext);\n\t}\n\n\tprivate static OAuth2DeviceAuthorizationRequestAuthenticationToken createAuthentication(\n\t\t\tRegisteredClient registeredClient) {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);\n\t\tSet<String> requestedScopes = registeredClient.getScopes();\n\t\tif (requestedScopes.isEmpty()) {\n\t\t\trequestedScopes = null;\n\t\t}\n\t\treturn new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, AUTHORIZATION_URI,\n\t\t\t\trequestedScopes, null);\n\t}\n\n\tprivate static OAuth2DeviceCode createDeviceCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2DeviceCode(DEVICE_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2UserCode createUserCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link OAuth2DeviceCodeAuthenticationProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceCodeAuthenticationProviderTests {\n\n\tprivate static final String DEVICE_CODE = \"EfYu_0jEL\";\n\n\tprivate static final String USER_CODE = \"BCDF-GHJK\";\n\n\tprivate static final String ACCESS_TOKEN = \"abc123\";\n\n\tprivate static final String REFRESH_TOKEN = \"xyz456\";\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2TokenGenerator<OAuth2Token> tokenGenerator;\n\n\tprivate JwtEncoder dPoPProofJwtEncoder;\n\n\tprivate OAuth2DeviceCodeAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.tokenGenerator = mock(OAuth2TokenGenerator.class);\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tthis.dPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\tthis.authenticationProvider = new OAuth2DeviceCodeAuthenticationProvider(this.authorizationService,\n\t\t\t\tthis.tokenGenerator);\n\t\tmockAuthorizationServerContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceCodeAuthenticationProvider(null, this.tokenGenerator))\n\t\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenGeneratorIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceCodeAuthenticationProvider(this.authorizationService, null))\n\t\t\t\t.withMessage(\"tokenGenerator cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2DeviceCodeAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2DeviceCodeAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\"client-1\",\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null);\n\t\tAuthentication authentication = new OAuth2DeviceCodeAuthenticationToken(DEVICE_CODE, clientPrincipal, null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRegisteredClientDoesNotMatchClientIdThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient2().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient2)\n\t\t\t.token(createDeviceCode())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2DeviceCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUserCodeIsNotInvalidatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createDeviceCode())\n\t\t\t.token(createUserCode())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2DeviceCodeAuthenticationProvider.AUTHORIZATION_PENDING);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDeviceCodeAndUserCodeAreInvalidatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createDeviceCode(), withInvalidated())\n\t\t\t.token(createUserCode(), withInvalidated())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.ACCESS_DENIED);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDeviceCodeIsExpiredThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createExpiredDeviceCode())\n\t\t\t.token(createUserCode())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2DeviceCodeAuthenticationProvider.EXPIRED_TOKEN);\n\t\t// @formatter:on\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2DeviceCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenIsNullThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode(), withInvalidated())\n\t\t\t\t.attribute(Principal.class.getName(), authentication.getPrincipal())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).willReturn(null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessage(\"The token generator failed to generate the access token.\")\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverify(this.tokenGenerator).generate(any(OAuth2TokenContext.class));\n\t\tverifyNoMoreInteractions(this.authorizationService, this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRefreshTokenIsNullThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode(), withInvalidated())\n\t\t\t\t.attribute(Principal.class.getName(), authentication.getPrincipal())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).willReturn(createAccessToken(),\n\t\t\t\t(OAuth2RefreshToken) null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessage(\"The token generator failed to generate the refresh token.\")\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverify(this.tokenGenerator, times(2)).generate(any(OAuth2TokenContext.class));\n\t\tverifyNoMoreInteractions(this.authorizationService, this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenTokenGeneratorReturnsWrongTypeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication authentication = createAuthentication(registeredClient);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode(), withInvalidated())\n\t\t\t\t.attribute(Principal.class.getName(), authentication.getPrincipal())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tgiven(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).willReturn(accessToken, accessToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.withMessage(\"The token generator failed to generate the refresh token.\")\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverify(this.tokenGenerator, times(2)).generate(any(OAuth2TokenContext.class));\n\t\tverifyNoMoreInteractions(this.authorizationService, this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidDeviceCodeThenReturnAccessTokenAndRefreshToken() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"dpop_proof\", generateDPoPProof(\"http://localhost/oauth2/token\"));\n\t\tadditionalParameters.put(\"dpop_method\", \"POST\");\n\t\tadditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tOAuth2DeviceCodeAuthenticationToken authentication = new OAuth2DeviceCodeAuthenticationToken(DEVICE_CODE,\n\t\t\t\tclientPrincipal, additionalParameters);\n\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode(), withInvalidated())\n\t\t\t\t.attribute(Principal.class.getName(), authentication.getPrincipal())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2RefreshToken refreshToken = createRefreshToken();\n\t\tgiven(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).willReturn(accessToken, refreshToken);\n\t\tOAuth2AccessTokenAuthenticationToken authenticationResult = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken);\n\t\tassertThat(authenticationResult.getRefreshToken()).isEqualTo(refreshToken);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);\n\t\tverify(this.authorizationService).findByToken(DEVICE_CODE,\n\t\t\t\tOAuth2DeviceCodeAuthenticationProvider.DEVICE_CODE_TOKEN_TYPE);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverify(this.tokenGenerator, times(2)).generate(tokenContextCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService, this.tokenGenerator);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2DeviceCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\t// @formatter:on\n\t\tassertThat(updatedAuthorization.getAccessToken().getToken()).isEqualTo(accessToken);\n\t\tassertThat(updatedAuthorization.getRefreshToken().getToken()).isEqualTo(refreshToken);\n\n\t\tfor (OAuth2TokenContext tokenContext : tokenContextCaptor.getAllValues()) {\n\t\t\tassertThat(tokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\t\tassertThat(tokenContext.<Authentication>getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\t\tassertThat(tokenContext.getAuthorizationServerContext()).isNotNull();\n\t\t\tassertThat(tokenContext.getAuthorization()).isEqualTo(authorization);\n\t\t\tassertThat(tokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\t\tassertThat(tokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.DEVICE_CODE);\n\t\t\tassertThat(tokenContext.<Authentication>getAuthorizationGrant()).isEqualTo(authentication);\n\t\t\tassertThat(tokenContext.<Jwt>get(OAuth2TokenContext.DPOP_PROOF_KEY)).isNotNull();\n\t\t}\n\t\tassertThat(tokenContextCaptor.getAllValues().get(0).getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(tokenContextCaptor.getAllValues().get(1).getTokenType()).isEqualTo(OAuth2TokenType.REFRESH_TOKEN);\n\t}\n\n\tprivate static void mockAuthorizationServerContext() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\t\tTestAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(\n\t\t\t\tauthorizationServerSettings, () -> \"https://provider.com\");\n\t\tAuthorizationServerContextHolder.setContext(authorizationServerContext);\n\t}\n\n\tprivate static OAuth2DeviceCodeAuthenticationToken createAuthentication(RegisteredClient registeredClient) {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);\n\t\treturn new OAuth2DeviceCodeAuthenticationToken(DEVICE_CODE, clientPrincipal, null);\n\t}\n\n\tprivate static OAuth2DeviceCode createDeviceCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2DeviceCode(DEVICE_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2DeviceCode createExpiredDeviceCode() {\n\t\tInstant issuedAt = Instant.now().minus(45, ChronoUnit.MINUTES);\n\t\treturn new OAuth2DeviceCode(DEVICE_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2UserCode createUserCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2AccessToken createAccessToken() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, ACCESS_TOKEN, issuedAt,\n\t\t\t\tissuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2RefreshToken createRefreshToken() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2RefreshToken(REFRESH_TOKEN, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static Consumer<Map<String, Object>> withInvalidated() {\n\t\treturn (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true);\n\t}\n\n\tpublic static Function<OAuth2Authorization.Token<? extends OAuth2Token>, Boolean> isInvalidated() {\n\t\treturn (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME);\n\t}\n\n\tprivate String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link OAuth2DeviceVerificationAuthenticationProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceVerificationAuthenticationProviderTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"/oauth2/device_verification\";\n\n\tprivate static final String DEVICE_CODE = \"EfYu_0jEL\";\n\n\tprivate static final String USER_CODE = \"BCDF-GHJK\";\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2AuthorizationConsentService authorizationConsentService;\n\n\tprivate OAuth2DeviceVerificationAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authorizationConsentService = mock(OAuth2AuthorizationConsentService.class);\n\t\tthis.authenticationProvider = new OAuth2DeviceVerificationAuthenticationProvider(\n\t\t\t\tthis.registeredClientRepository, this.authorizationService, this.authorizationConsentService);\n\t\tmockAuthorizationServerContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceVerificationAuthenticationProvider(\n\t\t\t\t\t\tnull, this.authorizationService, this.authorizationConsentService))\n\t\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceVerificationAuthenticationProvider(\n\t\t\t\t\t\tthis.registeredClientRepository, null, this.authorizationConsentService))\n\t\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationConsentServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceVerificationAuthenticationProvider(\n\t\t\t\t\t\tthis.registeredClientRepository, this.authorizationService, null))\n\t\t\t\t.withMessage(\"authorizationConsentService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizationConsentRequiredWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthorizationConsentRequired(null))\n\t\t\t.withMessage(\"authorizationConsentRequired cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2DeviceVerificationAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2DeviceVerificationAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(null);\n\t\tAuthentication authentication = createAuthentication();\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(USER_CODE,\n\t\t\t\tOAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUserCodeIsInvalidatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t\t.authorization(registeredClient)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode(), withInvalidated())\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(eq(USER_CODE),\n\t\t\t\teq(OAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tAuthentication authentication = createAuthentication();\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(USER_CODE,\n\t\t\t\tOAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUserCodeIsExpiredAndNotInvalidatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t\t.authorization(registeredClient)\n\t\t\t\t// Device code would also be expired but not relevant for this test\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createExpiredUserCode())\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(eq(USER_CODE),\n\t\t\t\teq(OAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tAuthentication authentication = createAuthentication();\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).findByToken(USER_CODE,\n\t\t\t\tOAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tassertThat(updatedAuthorization.getToken(OAuth2UserCode.class)).extracting(isInvalidated()).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t\t.authorization(registeredClient)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode())\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"anonymous\", null);\n\t\tprincipal.setAuthenticated(false);\n\t\tAuthentication authentication = new OAuth2DeviceVerificationAuthenticationToken(principal, USER_CODE,\n\t\t\t\tCollections.emptyMap());\n\t\tgiven(this.authorizationService.findByToken(eq(USER_CODE),\n\t\t\t\teq(OAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(USER_CODE,\n\t\t\t\tOAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.registeredClientRepository, this.authorizationConsentService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationConsentDoesNotExistThenReturnAuthorizationConsentWithState() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode())\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAuthentication authentication = createAuthentication();\n\t\tgiven(this.registeredClientRepository.findById(anyString())).willReturn(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.authorizationConsentService.findById(anyString(), anyString())).willReturn(null);\n\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authenticationResult.getState()).hasSize(44);\n\t\tassertThat(authenticationResult.getRequestedScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t\tassertThat(authenticationResult.getScopes()).isEmpty();\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).findByToken(USER_CODE,\n\t\t\t\tOAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);\n\t\tverify(this.registeredClientRepository).findById(authorization.getRegisteredClientId());\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName());\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService,\n\t\t\t\tthis.authorizationConsentService);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tassertThat(updatedAuthorization.<String>getAttribute(OAuth2ParameterNames.STATE))\n\t\t\t.isEqualTo(authenticationResult.getState());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationConsentExistsAndRequestedScopesMatchThenReturnDeviceVerification() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode())\n\t\t\t\t.attributes(Map::clear)\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAuthentication authentication = createAuthentication();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationConsent authorizationConsent =\n\t\t\t\tOAuth2AuthorizationConsent.withId(registeredClient.getId(), authentication.getName())\n\t\t\t\t\t\t.scope(registeredClient.getScopes().iterator().next())\n\t\t\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findById(anyString())).willReturn(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.authorizationConsentService.findById(anyString(), anyString())).willReturn(authorizationConsent);\n\n\t\tOAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getUserCode()).isEqualTo(USER_CODE);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).findByToken(USER_CODE,\n\t\t\t\tOAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);\n\t\tverify(this.registeredClientRepository).findById(authorization.getRegisteredClientId());\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName());\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService,\n\t\t\t\tthis.authorizationConsentService);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tassertThat(updatedAuthorization.getPrincipalName()).isEqualTo(authentication.getName());\n\t\tassertThat(updatedAuthorization.getAuthorizedScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t\tassertThat(updatedAuthorization.<Authentication>getAttribute(Principal.class.getName()))\n\t\t\t.isEqualTo(authentication.getPrincipal());\n\t\tassertThat(updatedAuthorization.<String>getAttribute(OAuth2ParameterNames.STATE)).isNull();\n\t\t// @formatter:off\n\t\tassertThat(updatedAuthorization.getToken(OAuth2DeviceCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(false);\n\t\tassertThat(updatedAuthorization.getToken(OAuth2UserCode.class))\n\t\t\t\t.extracting(isInvalidated())\n\t\t\t\t.isEqualTo(true);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationConsentExistsAndRequestedScopesDoNotMatchThenReturnAuthorizationConsentWithState() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode())\n\t\t\t\t.attributes(Map::clear)\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAuthentication authentication = createAuthentication();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationConsent authorizationConsent =\n\t\t\t\tOAuth2AuthorizationConsent.withId(registeredClient.getId(), authentication.getName())\n\t\t\t\t\t\t.scope(\"previous\")\n\t\t\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findById(anyString())).willReturn(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\tgiven(this.authorizationConsentService.findById(anyString(), anyString())).willReturn(authorizationConsent);\n\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authenticationResult.getState()).hasSize(44);\n\t\tassertThat(authenticationResult.getRequestedScopes()).hasSameElementsAs(registeredClient.getScopes());\n\t\tassertThat(authenticationResult.getScopes()).containsExactly(\"previous\");\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).findByToken(USER_CODE,\n\t\t\t\tOAuth2DeviceVerificationAuthenticationProvider.USER_CODE_TOKEN_TYPE);\n\t\tverify(this.registeredClientRepository).findById(authorization.getRegisteredClientId());\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName());\n\t\tverifyNoMoreInteractions(this.registeredClientRepository, this.authorizationService,\n\t\t\t\tthis.authorizationConsentService);\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tassertThat(updatedAuthorization.<String>getAttribute(OAuth2ParameterNames.STATE))\n\t\t\t.isEqualTo(authenticationResult.getState());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthorizationConsentRequiredThenUsed() {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tPredicate<OAuth2DeviceVerificationAuthenticationContext> authorizationConsentRequired = mock(Predicate.class);\n\t\tthis.authenticationProvider.setAuthorizationConsentRequired(authorizationConsentRequired);\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)\n\t\t\t\t.token(createDeviceCode())\n\t\t\t\t.token(createUserCode())\n\t\t\t\t.attributes(Map::clear)\n\t\t\t\t.attribute(OAuth2ParameterNames.SCOPE, registeredClient.getScopes())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAuthentication authentication = createAuthentication();\n\t\tgiven(this.registeredClientRepository.findById(anyString())).willReturn(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\n\t\tthis.authenticationProvider.authenticate(authentication);\n\n\t\tverify(authorizationConsentRequired).test(any());\n\t}\n\n\tprivate static void mockAuthorizationServerContext() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\t\tTestAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(\n\t\t\t\tauthorizationServerSettings, () -> \"https://provider.com\");\n\t\tAuthorizationServerContextHolder.setContext(authorizationServerContext);\n\t}\n\n\tprivate static OAuth2DeviceVerificationAuthenticationToken createAuthentication() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"user\", null,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"USER\"));\n\t\treturn new OAuth2DeviceVerificationAuthenticationToken(principal, USER_CODE, Collections.emptyMap());\n\t}\n\n\tprivate static OAuth2DeviceCode createDeviceCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2DeviceCode(DEVICE_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2UserCode createUserCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2UserCode createExpiredUserCode() {\n\t\tInstant issuedAt = Instant.now().minus(45, ChronoUnit.MINUTES);\n\t\treturn new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static Consumer<Map<String, Object>> withInvalidated() {\n\t\treturn (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true);\n\t}\n\n\tprivate static Function<OAuth2Authorization.Token<? extends OAuth2Token>, Boolean> isInvalidated() {\n\t\treturn (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2PushedAuthorizationRequestAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2PushedAuthorizationRequestAuthenticationProvider}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2PushedAuthorizationRequestAuthenticationProviderTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"https://provider.com/oauth2/par\";\n\n\t// See RFC 7636: Appendix B. Example for the S256 code_challenge_method\n\t// https://tools.ietf.org/html/rfc7636#appendix-B\n\tprivate static final String S256_CODE_VERIFIER = \"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\n\tprivate static final String S256_CODE_CHALLENGE = \"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\";\n\n\tprivate static final String STATE = \"state\";\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2PushedAuthorizationRequestAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new OAuth2PushedAuthorizationRequestAuthenticationProvider(\n\t\t\t\tthis.authorizationService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2PushedAuthorizationRequestAuthenticationProvider(null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2PushedAuthorizationRequestAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2PushedAuthorizationRequestAuthenticationToken.class))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthenticationValidator(null))\n\t\t\t.withMessage(\"authenticationValidator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null);\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientNotAuthorizedToRequestCodeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantTypes(Set::clear)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.build();\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1];\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, null,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.UNAUTHORIZED_CLIENT,\n\t\t\t\t\tOAuth2ParameterNames.CLIENT_ID, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidRedirectUriHostThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, \"https:///invalid\", STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidRedirectUriFragmentThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, \"https://example.com#fragment\",\n\t\t\t\tSTATE, registeredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnregisteredRedirectUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, \"https://invalid-example.com\",\n\t\t\t\tSTATE, registeredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRedirectUriIPv4LoopbackAndDifferentPortThenReturnPushedAuthorizationResponse() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.redirectUri(\"https://127.0.0.1:8080\")\n\t\t\t.build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, \"https://127.0.0.1:5000\", STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2PushedAuthorizationRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertPushedAuthorizationResponse(registeredClient, authentication, authenticationResult);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRedirectUriIPv6LoopbackAndDifferentPortThenReturnPushedAuthorizationResponse() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.redirectUri(\"https://[::1]:8080\")\n\t\t\t.build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, \"https://[::1]:5000\", STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2PushedAuthorizationRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertPushedAuthorizationResponse(registeredClient, authentication, authenticationResult);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingRedirectUriAndMultipleRegisteredThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.redirectUri(\"https://example2.com\")\n\t\t\t.build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, null, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationRequestMissingRedirectUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\t// redirect_uri is REQUIRED for OpenID Connect requests\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, null, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidScopeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, STATE,\n\t\t\t\tCollections.singleton(\"invalid-scope\"), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_SCOPE,\n\t\t\t\t\tOAuth2ParameterNames.SCOPE, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceRequiredAndMissingCodeChallengeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), null);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tPkceParameterNames.CODE_CHALLENGE, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceUnsupportedCodeChallengeMethodThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, \"code-challenge\");\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"unsupported\");\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), additionalParameters);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tPkceParameterNames.CODE_CHALLENGE_METHOD, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceMissingCodeChallengeMethodThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, \"code-challenge\");\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), additionalParameters);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\tPkceParameterNames.CODE_CHALLENGE_METHOD, authentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationRequestWithPromptNoneLoginThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tassertWhenAuthenticationRequestWithInvalidPromptThenThrowOAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\t\t\"none login\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationRequestWithPromptNoneConsentThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tassertWhenAuthenticationRequestWithInvalidPromptThenThrowOAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\t\t\"none consent\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationRequestWithPromptNoneSelectAccountThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {\n\t\tassertWhenAuthenticationRequestWithInvalidPromptThenThrowOAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\t\t\"none select_account\");\n\t}\n\n\tprivate void assertWhenAuthenticationRequestWithInvalidPromptThenThrowOAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\tString prompt) {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tMap<String, Object> additionalParameters = createPkceParameters();\n\t\tadditionalParameters.put(\"prompt\", prompt);\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), additionalParameters);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationCodeRequestAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.satisfies((ex) -> assertAuthenticationException(ex, OAuth2ErrorCodes.INVALID_REQUEST, \"prompt\",\n\t\t\t\t\tauthentication.getRedirectUri()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPushedAuthorizationRequestValidThenReturnPushedAuthorizationResponse() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[0];\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2PushedAuthorizationRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertPushedAuthorizationResponse(registeredClient, authentication, authenticationResult);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationValidatorThenUsed() {\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tConsumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator = mock(Consumer.class);\n\t\tthis.authenticationProvider.setAuthenticationValidator(authenticationValidator);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tString redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2];\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), clientPrincipal, redirectUri, STATE,\n\t\t\t\tregisteredClient.getScopes(), createPkceParameters());\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2PushedAuthorizationRequestAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertPushedAuthorizationResponse(registeredClient, authentication, authenticationResult);\n\t\tverify(authenticationValidator).accept(any());\n\t}\n\n\tprivate void assertPushedAuthorizationResponse(RegisteredClient registeredClient,\n\t\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authentication,\n\t\t\tOAuth2PushedAuthorizationRequestAuthenticationToken authenticationResult) {\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t\tassertThat(authorizationRequest.getAuthorizationUri()).isEqualTo(authentication.getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(authentication.getRedirectUri());\n\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(authentication.getScopes());\n\t\tassertThat(authorizationRequest.getState()).isEqualTo(authentication.getState());\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).isEqualTo(authentication.getAdditionalParameters());\n\n\t\tassertThat(authorization.getRegisteredClientId()).isEqualTo(registeredClient.getId());\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(authentication.getName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorization.<String>getAttribute(OAuth2ParameterNames.STATE)).isNotNull();\n\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(authorizationRequest.getClientId());\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getAuthorizationUri()).isEqualTo(authorizationRequest.getAuthorizationUri());\n\t\tassertThat(authenticationResult.getRedirectUri()).isEqualTo(authorizationRequest.getRedirectUri());\n\t\tassertThat(authenticationResult.getScopes()).isEqualTo(authorizationRequest.getScopes());\n\t\tassertThat(authenticationResult.getState()).isEqualTo(authorizationRequest.getState());\n\t\tassertThat(authenticationResult.getRequestUri()).isNotNull();\n\t\tassertThat(authenticationResult.getRequestUriExpiresAt()).isNotNull();\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t}\n\n\tprivate static void assertAuthenticationException(\n\t\t\tOAuth2AuthorizationCodeRequestAuthenticationException authenticationException, String errorCode,\n\t\t\tString parameterName, String redirectUri) {\n\n\t\tOAuth2Error error = authenticationException.getError();\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t\tassertThat(error.getDescription()).contains(parameterName);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationException\n\t\t\t.getAuthorizationCodeRequestAuthentication();\n\t\tassertThat(authorizationCodeRequestAuthentication.getRedirectUri()).isEqualTo(redirectUri);\n\t}\n\n\tprivate static Map<String, Object> createPkceParameters() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;\nimport org.springframework.security.oauth2.server.authorization.token.JwtGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2RefreshTokenAuthenticationProvider}.\n *\n * @author Alexey Nesterov\n * @author Joe Grandja\n * @author Anoop Garlapati\n * @since 7.0\n */\npublic class OAuth2RefreshTokenAuthenticationProviderTests {\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate JwtEncoder jwtEncoder;\n\n\tprivate OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer;\n\n\tprivate OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer;\n\n\tprivate OAuth2TokenGenerator<?> tokenGenerator;\n\n\tprivate JwtEncoder dPoPProofJwtEncoder;\n\n\tprivate OAuth2RefreshTokenAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.jwtEncoder = mock(JwtEncoder.class);\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwt(Collections.singleton(\"scope1\")));\n\t\tthis.jwtCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tJwtGenerator jwtGenerator = new JwtGenerator(this.jwtEncoder);\n\t\tjwtGenerator.setJwtCustomizer(this.jwtCustomizer);\n\t\tthis.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tOAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();\n\t\taccessTokenGenerator.setAccessTokenCustomizer(this.accessTokenCustomizer);\n\t\tOAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();\n\t\tOAuth2TokenGenerator<OAuth2Token> delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator,\n\t\t\t\taccessTokenGenerator, refreshTokenGenerator);\n\t\tthis.tokenGenerator = spy(new OAuth2TokenGenerator<OAuth2Token>() {\n\t\t\t@Override\n\t\t\tpublic OAuth2Token generate(OAuth2TokenContext context) {\n\t\t\t\treturn delegatingTokenGenerator.generate(context);\n\t\t\t}\n\t\t});\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tthis.dPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\tthis.authenticationProvider = new OAuth2RefreshTokenAuthenticationProvider(this.authorizationService,\n\t\t\t\tthis.tokenGenerator);\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2RefreshTokenAuthenticationProvider(null, this.tokenGenerator))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2RefreshTokenAuthenticationProvider(this.authorizationService, null))\n\t\t\t.withMessage(\"tokenGenerator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenSupportedAuthenticationThenTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2RefreshTokenAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsWhenUnsupportedAuthenticationThenFalse() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2ClientCredentialsAuthenticationToken.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidRefreshTokenThenReturnAccessToken() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"dpop_proof\", generateDPoPProof(\"http://localhost/oauth2/token\"));\n\t\tadditionalParameters.put(\"dpop_method\", \"POST\");\n\t\tadditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null,\n\t\t\t\tadditionalParameters);\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);\n\t\tverify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture());\n\t\tJwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue();\n\t\tassertThat(jwtEncodingContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(jwtEncodingContext.<Authentication>getPrincipal())\n\t\t\t.isEqualTo(authorization.getAttribute(Principal.class.getName()));\n\t\tassertThat(jwtEncodingContext.getAuthorization()).isEqualTo(authorization);\n\t\tassertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(jwtEncodingContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);\n\t\tassertThat(jwtEncodingContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authentication);\n\t\tassertThat(jwtEncodingContext.getJwsHeader()).isNotNull();\n\t\tassertThat(jwtEncodingContext.getClaims()).isNotNull();\n\t\tassertThat(jwtEncodingContext.<Jwt>get(OAuth2TokenContext.DPOP_PROOF_KEY)).isNotNull();\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\n\t\tassertThat(accessTokenAuthentication.getRegisteredClient().getId())\n\t\t\t.isEqualTo(updatedAuthorization.getRegisteredClientId());\n\t\tassertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(accessTokenAuthentication.getAccessToken())\n\t\t\t.isEqualTo(updatedAuthorization.getAccessToken().getToken());\n\t\tassertThat(updatedAuthorization.getAccessToken()).isNotEqualTo(authorization.getAccessToken());\n\t\tassertThat(accessTokenAuthentication.getRefreshToken())\n\t\t\t.isEqualTo(updatedAuthorization.getRefreshToken().getToken());\n\t\t// By default, refresh token is reused\n\t\tassertThat(updatedAuthorization.getRefreshToken()).isEqualTo(authorization.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidRefreshTokenThenReturnIdToken() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tOidcIdToken authorizedIdToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(\"subject\")\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.expiresAt(Instant.now().plusSeconds(60))\n\t\t\t.claim(\"sid\", \"sessionId-1234\")\n\t\t\t.claim(IdTokenClaimNames.AUTH_TIME, Date.from(Instant.now()))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(authorizedIdToken)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);\n\t\tverify(this.jwtCustomizer, times(2)).customize(jwtEncodingContextCaptor.capture());\n\t\t// Access Token context\n\t\tJwtEncodingContext accessTokenContext = jwtEncodingContextCaptor.getAllValues().get(0);\n\t\tassertThat(accessTokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(accessTokenContext.<Authentication>getPrincipal())\n\t\t\t.isEqualTo(authorization.getAttribute(Principal.class.getName()));\n\t\tassertThat(accessTokenContext.getAuthorization()).isEqualTo(authorization);\n\t\tassertThat(accessTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(accessTokenContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(accessTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);\n\t\tassertThat(accessTokenContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authentication);\n\t\tassertThat(accessTokenContext.getJwsHeader()).isNotNull();\n\t\tassertThat(accessTokenContext.getClaims()).isNotNull();\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\taccessTokenContext.getClaims().claims(claims::putAll);\n\t\tassertThat(claims).flatExtracting(OAuth2ParameterNames.SCOPE)\n\t\t\t.containsExactlyInAnyOrder(OidcScopes.OPENID, \"scope1\");\n\t\t// ID Token context\n\t\tJwtEncodingContext idTokenContext = jwtEncodingContextCaptor.getAllValues().get(1);\n\t\tassertThat(idTokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(idTokenContext.<Authentication>getPrincipal())\n\t\t\t.isEqualTo(authorization.getAttribute(Principal.class.getName()));\n\t\tassertThat(idTokenContext.getAuthorization()).isNotEqualTo(authorization);\n\t\tassertThat(idTokenContext.getAuthorization().getAccessToken()).isNotEqualTo(authorization.getAccessToken());\n\t\tassertThat(idTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes());\n\t\tassertThat(idTokenContext.getTokenType().getValue()).isEqualTo(OidcParameterNames.ID_TOKEN);\n\t\tassertThat(idTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);\n\t\tassertThat(idTokenContext.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authentication);\n\t\tassertThat(idTokenContext.getJwsHeader()).isNotNull();\n\t\tassertThat(idTokenContext.getClaims()).isNotNull();\n\n\t\tverify(this.jwtEncoder, times(2)).encode(any()); // Access token and ID Token\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\n\t\tassertThat(accessTokenAuthentication.getRegisteredClient().getId())\n\t\t\t.isEqualTo(updatedAuthorization.getRegisteredClientId());\n\t\tassertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(accessTokenAuthentication.getAccessToken())\n\t\t\t.isEqualTo(updatedAuthorization.getAccessToken().getToken());\n\t\tassertThat(updatedAuthorization.getAccessToken()).isNotEqualTo(authorization.getAccessToken());\n\t\tOAuth2Authorization.Token<OidcIdToken> idToken = updatedAuthorization.getToken(OidcIdToken.class);\n\t\tassertThat(idToken).isNotNull();\n\t\tassertThat(accessTokenAuthentication.getAdditionalParameters())\n\t\t\t.containsExactly(entry(OidcParameterNames.ID_TOKEN, idToken.getToken().getTokenValue()));\n\t\tassertThat(accessTokenAuthentication.getRefreshToken())\n\t\t\t.isEqualTo(updatedAuthorization.getRefreshToken().getToken());\n\t\t// By default, refresh token is reused\n\t\tassertThat(updatedAuthorization.getRefreshToken()).isEqualTo(authorization.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenReuseRefreshTokensFalseThenReturnNewRefreshToken() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.tokenSettings(TokenSettings.builder().reuseRefreshTokens(false).build())\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\n\t\tassertThat(accessTokenAuthentication.getRefreshToken())\n\t\t\t.isEqualTo(updatedAuthorization.getRefreshToken().getToken());\n\t\tassertThat(updatedAuthorization.getRefreshToken()).isNotEqualTo(authorization.getRefreshToken());\n\n\t\tArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);\n\t\tverify(this.tokenGenerator, times(2)).generate(tokenContextCaptor.capture());\n\t\t// tokenGenerator is first invoked for generating a new access token and then for\n\t\t// generating the refresh token\n\t\tList<OAuth2TokenContext> tokenContexts = tokenContextCaptor.getAllValues();\n\t\tassertThat(tokenContexts).hasSize(2);\n\t\tassertThat(tokenContexts.get(0).getAuthorization().getAccessToken().getToken().getTokenValue())\n\t\t\t.isEqualTo(\"access-token\");\n\t\tassertThat(tokenContexts.get(1).getAuthorization().getAccessToken().getToken().getTokenValue())\n\t\t\t.isEqualTo(\"refreshed-access-token\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRequestedScopesAuthorizedThenAccessTokenIncludesScopes() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.scope(\"scope2\")\n\t\t\t.scope(\"scope3\")\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tSet<String> authorizedScopes = authorization.getAuthorizedScopes();\n\t\tSet<String> requestedScopes = new HashSet<>(authorizedScopes);\n\t\trequestedScopes.remove(\"scope1\");\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, requestedScopes, null);\n\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(requestedScopes);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRequestedScopesNotAuthorizedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tSet<String> authorizedScopes = authorization.getAuthorizedScopes();\n\t\tSet<String> requestedScopes = new HashSet<>(authorizedScopes);\n\t\trequestedScopes.add(\"unauthorized\");\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, requestedScopes, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidRefreshTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\"invalid\",\n\t\t\t\tclientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tTestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(),\n\t\t\t\tregisteredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\t\"refresh-token\", clientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\t\"refresh-token\", clientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRefreshTokenIssuedToAnotherClientThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient2,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient2.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientNotAuthorizedToRefreshTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantTypes((grantTypes) -> grantTypes.remove(AuthorizationGrantType.REFRESH_TOKEN))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenExpiredRefreshTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tOAuth2RefreshToken expiredRefreshToken = new OAuth2RefreshToken(\"expired-refresh-token\",\n\t\t\t\tInstant.now().minusSeconds(120), Instant.now().minusSeconds(60));\n\t\tauthorization = OAuth2Authorization.from(authorization).token(expiredRefreshToken).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRevokedRefreshTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", Instant.now().minusSeconds(120),\n\t\t\t\tInstant.now().plusSeconds(1000));\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(refreshToken, (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotGeneratedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\twillAnswer((answer) -> {\n\t\t\tOAuth2TokenContext context = answer.getArgument(0);\n\t\t\tif (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn answer.callRealMethod();\n\t\t\t}\n\t\t}).given(this.tokenGenerator).generate(any());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription()).contains(\"The token generator failed to generate the access token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRefreshTokenNotGeneratedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.tokenSettings(TokenSettings.builder().reuseRefreshTokens(false).build())\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\twillAnswer((answer) -> {\n\t\t\tOAuth2TokenContext context = answer.getArgument(0);\n\t\t\tif (OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn answer.callRealMethod();\n\t\t\t}\n\t\t}).given(this.tokenGenerator).generate(any());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription())\n\t\t\t\t\t.contains(\"The token generator failed to generate the refresh token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenNotGeneratedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\twillAnswer((answer) -> {\n\t\t\tOAuth2TokenContext context = answer.getArgument(0);\n\t\t\tif (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn answer.callRealMethod();\n\t\t\t}\n\t\t}).given(this.tokenGenerator).generate(any());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription()).contains(\"The token generator failed to generate the ID token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenFormatReferenceThenAccessTokenGeneratorCalled() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.tokenSettings(TokenSettings.builder()\n\t\t\t\t\t\t.accessTokenFormat(OAuth2TokenFormat.REFERENCE)\n\t\t\t\t\t\t.build())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.REFRESH_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null);\n\n\t\tthis.authenticationProvider.authenticate(authentication);\n\n\t\tverify(this.accessTokenCustomizer).customize(any());\n\t}\n\n\tprivate static Jwt createJwt(Set<String> scope) {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\t\treturn Jwt.withTokenValue(\"refreshed-access-token\")\n\t\t\t.header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName())\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt)\n\t\t\t.claim(OAuth2ParameterNames.SCOPE, scope)\n\t\t\t.build();\n\t}\n\n\tprivate String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2RefreshTokenAuthenticationToken}.\n *\n * @author Alexey Nesterov\n * @since 7.0\n */\npublic class OAuth2RefreshTokenAuthenticationTokenTests {\n\n\tprivate RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,\n\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());\n\n\tprivate Set<String> scopes = Collections.singleton(\"scope1\");\n\n\tprivate Map<String, Object> additionalParameters = Collections.singletonMap(\"param1\", \"value1\");\n\n\t@Test\n\tpublic void constructorWhenRefreshTokenNullOrEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, this.clientPrincipal, this.scopes,\n\t\t\t\t\tthis.additionalParameters))\n\t\t\t.withMessage(\"refreshToken cannot be empty\");\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(\"\", this.clientPrincipal, this.scopes,\n\t\t\t\t\tthis.additionalParameters))\n\t\t\t.withMessage(\"refreshToken cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(\"refresh-token\", null, this.scopes,\n\t\t\t\t\tthis.additionalParameters))\n\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenScopesProvidedThenCreated() {\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\t\"refresh-token\", this.clientPrincipal, this.scopes, this.additionalParameters);\n\t\tassertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN);\n\t\tassertThat(authentication.getRefreshToken()).isEqualTo(\"refresh-token\");\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getScopes()).isEqualTo(this.scopes);\n\t\tassertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeActorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2TokenExchangeActor}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2TokenExchangeActorTests {\n\n\t@Test\n\tpublic void constructorWhenClaimsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2TokenExchangeActor(null))\n\t\t\t\t.withMessage(\"claims cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenRequiredParametersThenCreated() {\n\t\tMap<String, Object> claims = Map.of(\"claim1\", \"value1\");\n\t\tOAuth2TokenExchangeActor actor = new OAuth2TokenExchangeActor(claims);\n\t\tassertThat(actor.getClaims()).isEqualTo(claims);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link OAuth2TokenExchangeAuthenticationProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2TokenExchangeAuthenticationProviderTests {\n\n\tprivate static final Set<String> RESOURCES = Set.of(\"https://mydomain.com/resource1\",\n\t\t\t\"https://mydomain.com/resource2\");\n\n\tprivate static final Set<String> AUDIENCES = Set.of(\"audience1\", \"audience2\");\n\n\tprivate static final String SUBJECT_TOKEN = \"EfYu_0jEL\";\n\n\tprivate static final String ACTOR_TOKEN = \"JlNE_xR1f\";\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2TokenGenerator<OAuth2Token> tokenGenerator;\n\n\tprivate JwtEncoder dPoPProofJwtEncoder;\n\n\tprivate OAuth2TokenExchangeAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.tokenGenerator = mock(OAuth2TokenGenerator.class);\n\t\tJWKSet clientJwkSet = new JWKSet(TestJwks.DEFAULT_EC_JWK);\n\t\tJWKSource<SecurityContext> clientJwkSource = (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet);\n\t\tthis.dPoPProofJwtEncoder = new NimbusJwtEncoder(clientJwkSource);\n\t\tthis.authenticationProvider = new OAuth2TokenExchangeAuthenticationProvider(this.authorizationService,\n\t\t\t\tthis.tokenGenerator);\n\t\tmockAuthorizationServerContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2TokenExchangeAuthenticationProvider(null, this.tokenGenerator))\n\t\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2TokenExchangeAuthenticationProvider(this.authorizationService, null))\n\t\t\t\t.withMessage(\"tokenGenerator cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2TokenExchangeAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2TokenExchangeAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\"client-1\",\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null);\n\t\tAuthentication authentication = new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN,\n\t\t\t\tACCESS_TOKEN_TYPE_VALUE, clientPrincipal, null, null, RESOURCES, AUDIENCES, null, null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidGrantTypeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidRequestedTokenTypeThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t\t.tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE).build())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSubjectTokenNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSubjectTokenNotActiveThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createExpiredAccessToken(SUBJECT_TOKEN))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSubjectTokenTypeJwtAndSubjectTokenFormatReferenceThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createJwtRequest(registeredClient);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(SUBJECT_TOKEN), withTokenFormat(OAuth2TokenFormat.REFERENCE))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSubjectPrincipalNullThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\t// @formatter:off\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createAccessToken(SUBJECT_TOKEN))\n\t\t\t\t.attributes((attributes) -> attributes.remove(Principal.class.getName()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).willReturn(authorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenActorTokenNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(SUBJECT_TOKEN))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization, (OAuth2Authorization) null);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.authorizationService).findByToken(ACTOR_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenActorTokenNotActiveThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(SUBJECT_TOKEN))\n\t\t\t.build();\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createExpiredAccessToken(ACTOR_TOKEN))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization, actorAuthorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.authorizationService).findByToken(ACTOR_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenActorTokenTypeJwtAndActorTokenFormatReferenceThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createJwtRequest(registeredClient);\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(SUBJECT_TOKEN), withTokenFormat(OAuth2TokenFormat.SELF_CONTAINED))\n\t\t\t.build();\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(ACTOR_TOKEN), withTokenFormat(OAuth2TokenFormat.REFERENCE))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization, actorAuthorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.authorizationService).findByToken(ACTOR_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMayActAndActorIssClaimNotAuthorizedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\tMap<String, String> authorizedActorClaims = Map.of(OAuth2TokenClaimNames.ISS, \"issuer\",\n\t\t\t\tOAuth2TokenClaimNames.SUB, \"actor\");\n\t\t// @formatter:off\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createAccessToken(SUBJECT_TOKEN), withClaims(Map.of(\"may_act\", authorizedActorClaims)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMap<String, Object> actorTokenClaims = Map.of(OAuth2TokenClaimNames.ISS, \"invalid-issuer\",\n\t\t\t\tOAuth2TokenClaimNames.SUB, \"actor\");\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(ACTOR_TOKEN), withClaims(actorTokenClaims))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization, actorAuthorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.authorizationService).findByToken(ACTOR_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMayActAndActorSubClaimNotAuthorizedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\tMap<String, String> authorizedActorClaims = Map.of(OAuth2TokenClaimNames.ISS, \"issuer\",\n\t\t\t\tOAuth2TokenClaimNames.SUB, \"actor\");\n\t\t// @formatter:off\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createAccessToken(SUBJECT_TOKEN), withClaims(Map.of(\"may_act\", authorizedActorClaims)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMap<String, Object> actorTokenClaims = Map.of(OAuth2TokenClaimNames.ISS, \"issuer\", OAuth2TokenClaimNames.SUB,\n\t\t\t\t\"invalid-actor\");\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(ACTOR_TOKEN), withClaims(actorTokenClaims))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization, actorAuthorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.authorizationService).findByToken(ACTOR_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMayActAndImpersonationThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createImpersonationRequest(registeredClient);\n\t\tMap<String, String> authorizedActorClaims = Map.of(OAuth2TokenClaimNames.ISS, \"issuer\",\n\t\t\t\tOAuth2TokenClaimNames.SUB, \"actor\");\n\t\t// @formatter:off\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createAccessToken(SUBJECT_TOKEN), withClaims(Map.of(\"may_act\", authorizedActorClaims)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidScopeInRequestThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient,\n\t\t\t\tSet.of(\"invalid\"));\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(SUBJECT_TOKEN))\n\t\t\t.build();\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(ACTOR_TOKEN))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization, actorAuthorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.authorizationService).findByToken(ACTOR_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidScopeInSubjectAuthorizationThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient, Set.of());\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(SUBJECT_TOKEN))\n\t\t\t.authorizedScopes(Set.of(\"invalid\"))\n\t\t\t.build();\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(createAccessToken(ACTOR_TOKEN))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization, actorAuthorization);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE);\n\t\t// @formatter:on\n\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.authorizationService).findByToken(ACTOR_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverifyNoMoreInteractions(this.authorizationService);\n\t\tverifyNoInteractions(this.tokenGenerator);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNoActorTokenAndValidTokenExchangeThenReturnAccessTokenForImpersonation() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"dpop_proof\", generateDPoPProof(\"http://localhost/oauth2/token\"));\n\t\tadditionalParameters.put(\"dpop_method\", \"POST\");\n\t\tadditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = new OAuth2TokenExchangeAuthenticationToken(\n\t\t\t\tJWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, ACCESS_TOKEN_TYPE_VALUE, clientPrincipal, null, null, RESOURCES,\n\t\t\t\tAUDIENCES, registeredClient.getScopes(), additionalParameters);\n\t\tTestingAuthenticationToken userPrincipal = new TestingAuthenticationToken(\"user\", null, \"ROLE_USER\");\n\t\t// @formatter:off\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createAccessToken(SUBJECT_TOKEN))\n\t\t\t\t.attribute(Principal.class.getName(), userPrincipal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization);\n\t\tOAuth2AccessToken accessToken = createAccessToken(\"token-value\");\n\t\tgiven(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).willReturn(accessToken);\n\t\tOAuth2AccessTokenAuthenticationToken authenticationResult = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken);\n\t\tassertThat(authenticationResult.getRefreshToken()).isNull();\n\t\tassertThat(authenticationResult.getAdditionalParameters()).hasSize(1);\n\t\tassertThat(authenticationResult.getAdditionalParameters().get(OAuth2ParameterNames.ISSUED_TOKEN_TYPE))\n\t\t\t.isEqualTo(JWT_TOKEN_TYPE_VALUE);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.tokenGenerator).generate(tokenContextCaptor.capture());\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService, this.tokenGenerator);\n\n\t\tOAuth2TokenContext tokenContext = tokenContextCaptor.getValue();\n\t\tassertThat(tokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(tokenContext.getAuthorization()).isEqualTo(subjectAuthorization);\n\t\tassertThat(tokenContext.<Authentication>getPrincipal()).isSameAs(userPrincipal);\n\t\tassertThat(tokenContext.getAuthorizationServerContext()).isNotNull();\n\t\tassertThat(tokenContext.getAuthorizedScopes()).isEqualTo(authentication.getScopes());\n\t\tassertThat(tokenContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(tokenContext.<Authentication>getAuthorizationGrant()).isEqualTo(authentication);\n\t\tassertThat(tokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(tokenContext.<Jwt>get(OAuth2TokenContext.DPOP_PROOF_KEY)).isNotNull();\n\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(subjectAuthorization.getPrincipalName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(authorization.getAuthorizedScopes()).isEqualTo(authentication.getScopes());\n\t\tassertThat(authorization.<Authentication>getAttribute(Principal.class.getName())).isSameAs(userPrincipal);\n\t\tassertThat(authorization.getAccessToken().getToken()).isEqualTo(accessToken);\n\t\tassertThat(authorization.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNoActorTokenAndPreviousActorThenReturnAccessTokenForImpersonation() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createImpersonationRequest(registeredClient);\n\t\tTestingAuthenticationToken userPrincipal = new TestingAuthenticationToken(\"user\", null, \"ROLE_USER\");\n\t\tOAuth2TokenExchangeActor previousActor = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(OAuth2TokenClaimNames.ISS, \"issuer1\", OAuth2TokenClaimNames.SUB, \"actor\"));\n\t\tOAuth2TokenExchangeCompositeAuthenticationToken subjectPrincipal = new OAuth2TokenExchangeCompositeAuthenticationToken(\n\t\t\t\tuserPrincipal, List.of(previousActor));\n\t\t// @formatter:off\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createAccessToken(SUBJECT_TOKEN))\n\t\t\t\t.attribute(Principal.class.getName(), subjectPrincipal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization);\n\t\tOAuth2AccessToken accessToken = createAccessToken(\"token-value\");\n\t\tgiven(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).willReturn(accessToken);\n\t\tOAuth2AccessTokenAuthenticationToken authenticationResult = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken);\n\t\tassertThat(authenticationResult.getRefreshToken()).isNull();\n\t\tassertThat(authenticationResult.getAdditionalParameters()).hasSize(1);\n\t\tassertThat(authenticationResult.getAdditionalParameters().get(OAuth2ParameterNames.ISSUED_TOKEN_TYPE))\n\t\t\t.isEqualTo(JWT_TOKEN_TYPE_VALUE);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.tokenGenerator).generate(tokenContextCaptor.capture());\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService, this.tokenGenerator);\n\n\t\tOAuth2TokenContext tokenContext = tokenContextCaptor.getValue();\n\t\tassertThat(tokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(tokenContext.getAuthorization()).isEqualTo(subjectAuthorization);\n\t\tassertThat(tokenContext.<Authentication>getPrincipal()).isSameAs(userPrincipal);\n\t\tassertThat(tokenContext.getAuthorizationServerContext()).isNotNull();\n\t\tassertThat(tokenContext.getAuthorizedScopes()).isEqualTo(authentication.getScopes());\n\t\tassertThat(tokenContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(tokenContext.<Authentication>getAuthorizationGrant()).isEqualTo(authentication);\n\t\tassertThat(tokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(subjectAuthorization.getPrincipalName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(authorization.getAuthorizedScopes()).isEqualTo(authentication.getScopes());\n\t\tassertThat(authorization.<Authentication>getAttribute(Principal.class.getName())).isSameAs(userPrincipal);\n\t\tassertThat(authorization.getAccessToken().getToken()).isEqualTo(accessToken);\n\t\tassertThat(authorization.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenActorTokenAndValidTokenExchangeThenReturnAccessTokenForDelegation() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = createDelegationRequest(registeredClient);\n\t\tTestingAuthenticationToken userPrincipal = new TestingAuthenticationToken(\"user\", null, \"ROLE_USER\");\n\t\tOAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(OAuth2TokenClaimNames.ISS, \"issuer1\", OAuth2TokenClaimNames.SUB, \"actor1\"));\n\t\tOAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(OAuth2TokenClaimNames.ISS, \"issuer2\", OAuth2TokenClaimNames.SUB, \"actor2\"));\n\t\tOAuth2TokenExchangeCompositeAuthenticationToken subjectPrincipal = new OAuth2TokenExchangeCompositeAuthenticationToken(\n\t\t\t\tuserPrincipal, List.of(actor1));\n\t\t// @formatter:off\n\t\tOAuth2Authorization subjectAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.token(createAccessToken(SUBJECT_TOKEN), withClaims(Map.of(\"may_act\", actor2.getClaims())))\n\t\t\t\t.attribute(Principal.class.getName(), subjectPrincipal)\n\t\t\t\t.build();\n\t\tOAuth2Authorization actorAuthorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t\t.principalName(actor2.getSubject())\n\t\t\t\t.token(createAccessToken(ACTOR_TOKEN), withClaims(actor2.getClaims()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class)))\n\t\t\t.willReturn(subjectAuthorization, actorAuthorization);\n\t\tOAuth2AccessToken accessToken = createAccessToken(\"token-value\");\n\t\tgiven(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).willReturn(accessToken);\n\t\tOAuth2AccessTokenAuthenticationToken authenticationResult = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t\tassertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken);\n\t\tassertThat(authenticationResult.getRefreshToken()).isNull();\n\t\tassertThat(authenticationResult.getAdditionalParameters()).hasSize(1);\n\t\tassertThat(authenticationResult.getAdditionalParameters().get(OAuth2ParameterNames.ISSUED_TOKEN_TYPE))\n\t\t\t.isEqualTo(JWT_TOKEN_TYPE_VALUE);\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tArgumentCaptor<OAuth2TokenContext> tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class);\n\t\tverify(this.authorizationService).findByToken(SUBJECT_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.authorizationService).findByToken(ACTOR_TOKEN, OAuth2TokenType.ACCESS_TOKEN);\n\t\tverify(this.tokenGenerator).generate(tokenContextCaptor.capture());\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizationService, this.tokenGenerator);\n\n\t\tOAuth2TokenContext tokenContext = tokenContextCaptor.getValue();\n\t\tassertThat(tokenContext.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(tokenContext.getAuthorization()).isEqualTo(subjectAuthorization);\n\t\tassertThat(tokenContext.getAuthorizationServerContext()).isNotNull();\n\t\tassertThat(tokenContext.getAuthorizedScopes()).isEqualTo(authentication.getScopes());\n\t\tassertThat(tokenContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(tokenContext.<Authentication>getAuthorizationGrant()).isEqualTo(authentication);\n\t\tassertThat(tokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\n\t\tOAuth2TokenExchangeCompositeAuthenticationToken tokenContextPrincipal = tokenContext.getPrincipal();\n\t\tassertThat(tokenContextPrincipal.getSubject()).isSameAs(subjectPrincipal.getSubject());\n\t\tassertThat(tokenContextPrincipal.getActors()).containsExactly(actor2, actor1);\n\n\t\tOAuth2Authorization authorization = authorizationCaptor.getValue();\n\t\tassertThat(authorization.getPrincipalName()).isEqualTo(subjectAuthorization.getPrincipalName());\n\t\tassertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(authorization.getAuthorizedScopes()).isEqualTo(authentication.getScopes());\n\t\tassertThat(authorization.getAccessToken().getToken()).isEqualTo(accessToken);\n\t\tassertThat(authorization.getRefreshToken()).isNull();\n\n\t\tOAuth2TokenExchangeCompositeAuthenticationToken authorizationPrincipal = authorization\n\t\t\t.getAttribute(Principal.class.getName());\n\t\tassertThat(authorizationPrincipal).isNotNull();\n\t\tassertThat(authorizationPrincipal.getSubject()).isSameAs(subjectPrincipal.getSubject());\n\t\tassertThat(authorizationPrincipal.getActors()).containsExactly(actor2, actor1);\n\t}\n\n\tprivate static void mockAuthorizationServerContext() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\t\tTestAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(\n\t\t\t\tauthorizationServerSettings, () -> \"https://provider.com\");\n\t\tAuthorizationServerContextHolder.setContext(authorizationServerContext);\n\t}\n\n\tprivate static OAuth2TokenExchangeAuthenticationToken createDelegationRequest(RegisteredClient registeredClient) {\n\t\treturn createDelegationRequest(registeredClient, registeredClient.getScopes());\n\t}\n\n\tprivate static OAuth2TokenExchangeAuthenticationToken createDelegationRequest(RegisteredClient registeredClient,\n\t\t\tSet<String> requestedScopes) {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);\n\t\treturn new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, ACCESS_TOKEN_TYPE_VALUE,\n\t\t\t\tclientPrincipal, ACTOR_TOKEN, ACCESS_TOKEN_TYPE_VALUE, RESOURCES, AUDIENCES, requestedScopes, null);\n\t}\n\n\tprivate static OAuth2TokenExchangeAuthenticationToken createImpersonationRequest(\n\t\t\tRegisteredClient registeredClient) {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);\n\t\treturn new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, ACCESS_TOKEN_TYPE_VALUE,\n\t\t\t\tclientPrincipal, null, null, RESOURCES, AUDIENCES, registeredClient.getScopes(), null);\n\t}\n\n\tprivate static OAuth2TokenExchangeAuthenticationToken createJwtRequest(RegisteredClient registeredClient) {\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);\n\t\treturn new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, JWT_TOKEN_TYPE_VALUE,\n\t\t\t\tclientPrincipal, ACTOR_TOKEN, JWT_TOKEN_TYPE_VALUE, RESOURCES, AUDIENCES, registeredClient.getScopes(),\n\t\t\t\tnull);\n\t}\n\n\tprivate static OAuth2AccessToken createAccessToken(String tokenValue) {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, tokenValue, issuedAt, expiresAt);\n\t}\n\n\tprivate static OAuth2AccessToken createExpiredAccessToken(String tokenValue) {\n\t\tInstant issuedAt = Instant.now().minus(45, ChronoUnit.MINUTES);\n\t\tInstant expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, tokenValue, issuedAt, expiresAt);\n\t}\n\n\tprivate static Consumer<Map<String, Object>> withClaims(Map<String, Object> claims) {\n\t\treturn (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, claims);\n\t}\n\n\tprivate static Consumer<Map<String, Object>> withTokenFormat(OAuth2TokenFormat tokenFormat) {\n\t\treturn (metadata) -> metadata.put(OAuth2TokenFormat.class.getName(), tokenFormat.getValue());\n\t}\n\n\tprivate String generateDPoPProof(String tokenEndpointUri) {\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK\n\t\t\t\t.toPublicJWK()\n\t\t\t\t.toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", \"POST\")\n\t\t\t\t.claim(\"htu\", tokenEndpointUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwt jwt = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\treturn jwt.getTokenValue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2TokenExchangeAuthenticationToken}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2TokenExchangeAuthenticationTokenTests {\n\n\tprivate static final Set<String> RESOURCES = Set.of(\"https://mydomain.com/resource1\",\n\t\t\t\"https://mydomain.com/resource2\");\n\n\tprivate static final Set<String> AUDIENCES = Set.of(\"audience1\", \"audience2\");\n\n\tprivate static final String SUBJECT_TOKEN = \"EfYu_0jEL\";\n\n\tprivate static final String ACTOR_TOKEN = \"JlNE_xR1f\";\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,\n\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());\n\n\tprivate Set<String> scopes = Collections.singleton(\"scope1\");\n\n\tprivate Map<String, Object> additionalParameters = Collections.singletonMap(\"param1\", \"value1\");\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new OAuth2TokenExchangeAuthenticationToken(null, null, null, null, null, null, null, null, null, this.additionalParameters))\n\t\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenRequestedTokenTypeNullOrEmptyThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new OAuth2TokenExchangeAuthenticationToken(null, null, null, this.clientPrincipal, null, null, null, null, null, this.additionalParameters))\n\t\t\t\t.withMessage(\"requestedTokenType cannot be empty\");\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new OAuth2TokenExchangeAuthenticationToken(\"\", null, null, this.clientPrincipal, null, null, null, null, null, this.additionalParameters))\n\t\t\t\t.withMessage(\"requestedTokenType cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenSubjectTokenNullOrEmptyThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, null, null, this.clientPrincipal, null, null, null, null, this.scopes, this.additionalParameters))\n\t\t\t\t.withMessage(\"subjectToken cannot be empty\");\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, \"\", null, this.clientPrincipal, null, null, null, null, this.scopes, this.additionalParameters))\n\t\t\t\t.withMessage(\"subjectToken cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenSubjectTokenTypeNullOrEmptyThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, null, this.clientPrincipal, null, null, null, null, this.scopes, this.additionalParameters))\n\t\t\t\t.withMessage(\"subjectTokenType cannot be empty\");\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new OAuth2TokenExchangeAuthenticationToken(JWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, \"\", this.clientPrincipal, null, null, null, null, this.scopes, this.additionalParameters))\n\t\t\t\t.withMessage(\"subjectTokenType cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenRequiredParametersProvidedThenCreated() {\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = new OAuth2TokenExchangeAuthenticationToken(\n\t\t\t\tJWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, ACCESS_TOKEN_TYPE_VALUE, this.clientPrincipal, null, null, null,\n\t\t\t\tnull, null, this.additionalParameters);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(authentication.getRequestedTokenType()).isEqualTo(JWT_TOKEN_TYPE_VALUE);\n\t\tassertThat(authentication.getSubjectToken()).isEqualTo(SUBJECT_TOKEN);\n\t\tassertThat(authentication.getSubjectTokenType()).isEqualTo(ACCESS_TOKEN_TYPE_VALUE);\n\t\tassertThat(authentication.getActorToken()).isNull();\n\t\tassertThat(authentication.getActorTokenType()).isNull();\n\t\tassertThat(authentication.getResources()).isEmpty();\n\t\tassertThat(authentication.getAudiences()).isEmpty();\n\t\tassertThat(authentication.getScopes()).isEmpty();\n\t\tassertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedThenCreated() {\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = new OAuth2TokenExchangeAuthenticationToken(\n\t\t\t\tJWT_TOKEN_TYPE_VALUE, SUBJECT_TOKEN, ACCESS_TOKEN_TYPE_VALUE, this.clientPrincipal, ACTOR_TOKEN,\n\t\t\t\tACCESS_TOKEN_TYPE_VALUE, RESOURCES, AUDIENCES, this.scopes, this.additionalParameters);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(authentication.getRequestedTokenType()).isEqualTo(JWT_TOKEN_TYPE_VALUE);\n\t\tassertThat(authentication.getSubjectToken()).isEqualTo(SUBJECT_TOKEN);\n\t\tassertThat(authentication.getSubjectTokenType()).isEqualTo(ACCESS_TOKEN_TYPE_VALUE);\n\t\tassertThat(authentication.getActorToken()).isEqualTo(ACTOR_TOKEN);\n\t\tassertThat(authentication.getActorTokenType()).isEqualTo(ACCESS_TOKEN_TYPE_VALUE);\n\t\tassertThat(authentication.getResources()).isEqualTo(RESOURCES);\n\t\tassertThat(authentication.getAudiences()).isEqualTo(AUDIENCES);\n\t\tassertThat(authentication.getScopes()).isEqualTo(this.scopes);\n\t\tassertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeCompositeAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2TokenExchangeCompositeAuthenticationToken}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2TokenExchangeCompositeAuthenticationTokenTests {\n\n\t@Test\n\tpublic void constructorWhenSubjectNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2TokenExchangeCompositeAuthenticationToken(null, null))\n\t\t\t\t.withMessage(\"subject cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenActorsNullThenThrowIllegalArgumentException() {\n\t\tTestingAuthenticationToken subject = new TestingAuthenticationToken(\"subject\", null);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2TokenExchangeCompositeAuthenticationToken(subject, null))\n\t\t\t\t.withMessage(\"actors cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenRequiredParametersProvidedThenCreated() {\n\t\tTestingAuthenticationToken subject = new TestingAuthenticationToken(\"subject\", null);\n\t\tOAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(Map.of(\"claim1\", \"value1\"));\n\t\tOAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(Map.of(\"claim2\", \"value2\"));\n\t\tList<OAuth2TokenExchangeActor> actors = List.of(actor1, actor2);\n\t\tOAuth2TokenExchangeCompositeAuthenticationToken authentication = new OAuth2TokenExchangeCompositeAuthenticationToken(\n\t\t\t\tsubject, actors);\n\t\tassertThat(authentication.getSubject()).isEqualTo(subject);\n\t\tassertThat(authentication.getActors()).isEqualTo(actors);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsSet;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isNull;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2TokenIntrospectionAuthenticationProvider}.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n */\npublic class OAuth2TokenIntrospectionAuthenticationProviderTests {\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2TokenIntrospectionAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new OAuth2TokenIntrospectionAuthenticationProvider(\n\t\t\t\tthis.registeredClientRepository, this.authorizationService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationProvider(null, this.authorizationService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationProvider(this.registeredClientRepository, null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2TokenIntrospectionAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2TokenIntrospectionAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tTestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(),\n\t\t\t\tregisteredClient.getClientSecret());\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\t\"token\", clientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\t\"token\", clientPrincipal, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidTokenThenNotActive() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\t\"token\", clientPrincipal, null, null);\n\t\tOAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull());\n\t\tassertThat(authenticationResult.isAuthenticated()).isFalse();\n\t\tassertThat(authenticationResult.getTokenClaims().getClaims()).hasSize(1);\n\t\tassertThat(authenticationResult.getTokenClaims().isActive()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenTokenInvalidatedThenNotActive() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\tauthorization = OAuth2Authorization.from(authorization).invalidate(accessToken).build();\n\t\tgiven(this.authorizationService.findByToken(eq(accessToken.getTokenValue()), isNull()))\n\t\t\t.willReturn(authorization);\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, null, null);\n\t\tOAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull());\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getTokenClaims().getClaims()).hasSize(1);\n\t\tassertThat(authenticationResult.getTokenClaims().isActive()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenTokenExpiredThenNotActive() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofHours(1));\n\t\tInstant expiresAt = Instant.now().minus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(accessToken)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(accessToken.getTokenValue()), isNull()))\n\t\t\t.willReturn(authorization);\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, null, null);\n\t\tOAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull());\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getTokenClaims().getClaims()).hasSize(1);\n\t\tassertThat(authenticationResult.getTokenClaims().isActive()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenTokenBeforeUseThenNotActive() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant notBefore = issuedAt.plus(Duration.ofMinutes(5));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tMap<String, Object> accessTokenClaims = Collections.singletonMap(OAuth2TokenClaimNames.NBF, notBefore);\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, accessToken, accessTokenClaims)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(accessToken.getTokenValue()), isNull()))\n\t\t\t.willReturn(authorization);\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, null, null);\n\t\tOAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull());\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getTokenClaims().getClaims()).hasSize(1);\n\t\tassertThat(authenticationResult.getTokenClaims().isActive()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidAccessTokenThenActive() {\n\t\tRegisteredClient authorizedClient = TestRegisteredClients.registeredClient().build();\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\t\tissuedAt, expiresAt, new HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\n\t\t// @formatter:off\n\t\tOAuth2TokenClaimsSet claimsSet = OAuth2TokenClaimsSet.builder()\n\t\t\t\t.issuer(\"https://provider.com\")\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.audience(Collections.singletonList(authorizedClient.getClientId()))\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.notBefore(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.id(\"id\")\n\t\t\t\t.claim(OAuth2TokenIntrospectionClaimNames.SCOPE, accessToken.getScopes())\n\t\t\t\t.claim(\"custom-claim\", \"custom-value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(authorizedClient, accessToken, claimsSet.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(accessToken.getTokenValue()), isNull()))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorizedClient.getId()))).willReturn(authorizedClient);\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, null, null);\n\t\tOAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull());\n\t\tverify(this.registeredClientRepository).findById(eq(authorizedClient.getId()));\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tOAuth2TokenIntrospection tokenClaims = authenticationResult.getTokenClaims();\n\t\tassertThat(tokenClaims.isActive()).isTrue();\n\t\tassertThat(tokenClaims.getClientId()).isEqualTo(authorizedClient.getClientId());\n\t\tassertThat(tokenClaims.getIssuedAt()).isEqualTo(accessToken.getIssuedAt());\n\t\tassertThat(tokenClaims.getExpiresAt()).isEqualTo(accessToken.getExpiresAt());\n\t\tassertThat(tokenClaims.getTokenType()).isEqualTo(accessToken.getTokenType().getValue());\n\t\tassertThat(tokenClaims.getNotBefore()).isEqualTo(claimsSet.getNotBefore());\n\t\tassertThat(tokenClaims.getSubject()).isEqualTo(claimsSet.getSubject());\n\t\tassertThat(tokenClaims.getAudience()).containsExactlyInAnyOrderElementsOf(claimsSet.getAudience());\n\t\tassertThat(tokenClaims.getIssuer()).isEqualTo(claimsSet.getIssuer());\n\t\tassertThat(tokenClaims.getId()).isEqualTo(claimsSet.getId());\n\t\tassertThat(tokenClaims.getScopes()).containsExactlyInAnyOrderElementsOf(accessToken.getScopes());\n\t\tassertThat(tokenClaims.<String>getClaim(\"custom-claim\")).isEqualTo(\"custom-value\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidRefreshTokenThenActive() {\n\t\tRegisteredClient authorizedClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();\n\t\tOAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();\n\t\tgiven(this.authorizationService.findByToken(eq(refreshToken.getTokenValue()), isNull()))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorizedClient.getId()))).willReturn(authorizedClient);\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\trefreshToken.getTokenValue(), clientPrincipal, null, null);\n\t\tOAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull());\n\t\tverify(this.registeredClientRepository).findById(eq(authorizedClient.getId()));\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tOAuth2TokenIntrospection tokenClaims = authenticationResult.getTokenClaims();\n\t\tassertThat(tokenClaims.getClaims()).hasSize(4);\n\t\tassertThat(tokenClaims.isActive()).isTrue();\n\t\tassertThat(tokenClaims.getClientId()).isEqualTo(authorizedClient.getClientId());\n\t\tassertThat(tokenClaims.getIssuedAt()).isEqualTo(refreshToken.getIssuedAt());\n\t\tassertThat(tokenClaims.getExpiresAt()).isEqualTo(refreshToken.getExpiresAt());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2TokenIntrospectionAuthenticationToken}.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n */\npublic class OAuth2TokenIntrospectionAuthenticationTokenTests {\n\n\tprivate String token = \"token\";\n\n\tprivate RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,\n\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());\n\n\tprivate OAuth2TokenIntrospection tokenClaims = OAuth2TokenIntrospection.builder(true).build();\n\n\t@Test\n\tpublic void constructorWhenTokenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(null, this.clientPrincipal, null, null))\n\t\t\t.withMessage(\"token cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(this.token, null, null, null))\n\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticatedAndTokenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> new OAuth2TokenIntrospectionAuthenticationToken(null, this.clientPrincipal, this.tokenClaims))\n\t\t\t.withMessage(\"token cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticatedAndClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(this.token, null, this.tokenClaims))\n\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticatedAndTokenClaimsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(this.token, this.clientPrincipal, null))\n\t\t\t.withMessage(\"tokenClaims cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenProvidedThenCreated() {\n\t\tMap<String, Object> additionalParameters = Collections.singletonMap(\"custom-param\", \"custom-value\");\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\tthis.token, this.clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue(), additionalParameters);\n\t\tassertThat(authentication.getToken()).isEqualTo(this.token);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getTokenTypeHint()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\tassertThat(authentication.getAdditionalParameters()).containsExactlyInAnyOrderEntriesOf(additionalParameters);\n\t\tassertThat(authentication.getTokenClaims()).isNotNull();\n\t\tassertThat(authentication.getTokenClaims().isActive()).isFalse();\n\t\tassertThat(authentication.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenClaimsProvidedThenCreated() {\n\t\tOAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\tthis.token, this.clientPrincipal, this.tokenClaims);\n\t\tassertThat(authentication.getToken()).isEqualTo(this.token);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getTokenTypeHint()).isNull();\n\t\tassertThat(authentication.getAdditionalParameters()).isEmpty();\n\t\tassertThat(authentication.getTokenClaims()).isEqualTo(this.tokenClaims);\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isNull;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2TokenRevocationAuthenticationProvider}.\n *\n * @author Vivek Babu\n * @author Joe Grandja\n */\npublic class OAuth2TokenRevocationAuthenticationProviderTests {\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OAuth2TokenRevocationAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new OAuth2TokenRevocationAuthenticationProvider(this.authorizationService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenRevocationAuthenticationProvider(null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2TokenRevocationAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2TokenRevocationAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tTestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(),\n\t\t\t\tregisteredClient.getClientSecret());\n\t\tOAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(\"token\",\n\t\t\t\tclientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\tregisteredClient.getClientSecret(), null);\n\t\tOAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(\"token\",\n\t\t\t\tclientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidTokenThenNotRevoked() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(\"token\",\n\t\t\t\tclientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\tOAuth2TokenRevocationAuthenticationToken authenticationResult = (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isFalse();\n\t\tverify(this.authorizationService, never()).save(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenTokenIssuedToAnotherClientThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(TestRegisteredClients.registeredClient2().build())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(\"token\"), isNull())).willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(\"token\",\n\t\t\t\tclientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidRefreshTokenThenRevoked() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()),\n\t\t\t\tisNull()))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\tauthorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal,\n\t\t\t\tOAuth2TokenType.REFRESH_TOKEN.getValue());\n\n\t\tOAuth2TokenRevocationAuthenticationToken authenticationResult = (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = updatedAuthorization.getRefreshToken();\n\t\tassertThat(refreshToken.isInvalidated()).isTrue();\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();\n\t\tassertThat(accessToken.isInvalidated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidAccessTokenThenRevoked() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(authorization.getAccessToken().getToken().getTokenValue()),\n\t\t\t\tisNull()))\n\t\t\t.willReturn(authorization);\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\tauthorization.getAccessToken().getToken().getTokenValue(), clientPrincipal,\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN.getValue());\n\n\t\tOAuth2TokenRevocationAuthenticationToken authenticationResult = (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\t\tverify(this.authorizationService).save(authorizationCaptor.capture());\n\n\t\tOAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();\n\t\tOAuth2Authorization.Token<OAuth2AccessToken> accessToken = updatedAuthorization.getAccessToken();\n\t\tassertThat(accessToken.isInvalidated()).isTrue();\n\t\tOAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = updatedAuthorization.getRefreshToken();\n\t\tassertThat(refreshToken.isInvalidated()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2TokenRevocationAuthenticationToken}.\n *\n * @author Vivek Babu\n * @author Joe Grandja\n */\npublic class OAuth2TokenRevocationAuthenticationTokenTests {\n\n\tprivate String token = \"token\";\n\n\tprivate RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient,\n\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret());\n\n\tprivate String tokenTypeHint = OAuth2TokenType.ACCESS_TOKEN.getValue();\n\n\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, this.token,\n\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)));\n\n\t@Test\n\tpublic void constructorWhenTokenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> new OAuth2TokenRevocationAuthenticationToken(null, this.clientPrincipal, this.tokenTypeHint))\n\t\t\t.withMessage(\"token cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(this.token, null, this.tokenTypeHint))\n\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenRevokedTokenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(null, this.clientPrincipal))\n\t\t\t.withMessage(\"revokedToken cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenRevokedTokenAndClientPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(this.accessToken, null))\n\t\t\t.withMessage(\"clientPrincipal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenProvidedThenCreated() {\n\t\tOAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\tthis.token, this.clientPrincipal, this.tokenTypeHint);\n\t\tassertThat(authentication.getToken()).isEqualTo(this.token);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getTokenTypeHint()).isEqualTo(this.tokenTypeHint);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRevokedTokenProvidedThenCreated() {\n\t\tOAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\tthis.accessToken, this.clientPrincipal);\n\t\tassertThat(authentication.getToken()).isEqualTo(this.accessToken.getTokenValue());\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);\n\t\tassertThat(authentication.getTokenTypeHint()).isNull();\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link PublicClientAuthenticationProvider}.\n *\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n */\npublic class PublicClientAuthenticationProviderTests {\n\n\t// See RFC 7636: Appendix B. Example for the S256 code_challenge_method\n\t// https://tools.ietf.org/html/rfc7636#appendix-B\n\tprivate static final String S256_CODE_VERIFIER = \"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\n\tprivate static final String S256_CODE_CHALLENGE = \"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\";\n\n\tprivate static final String AUTHORIZATION_CODE = \"code\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate PublicClientAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new PublicClientAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\tthis.authorizationService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new PublicClientAuthenticationProvider(null, this.authorizationService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new PublicClientAuthenticationProvider(this.registeredClientRepository, null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2ClientAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2ClientAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClientIdThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId() + \"-invalid\", ClientAuthenticationMethod.NONE, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnsupportedClientAuthenticationMethodThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.NONE, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"authentication_method\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidCodeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, createPkceAuthorizationParametersS256())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);\n\t\tparameters.put(OAuth2ParameterNames.CODE, \"invalid-code\");\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CODE);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingCodeChallengeThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t\tassertThat(error.getDescription()).contains(PkceParameterNames.CODE_CHALLENGE);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingCodeVerifierThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, createPkceAuthorizationParametersS256())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createAuthorizationCodeTokenParameters();\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t\tassertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenS256MethodAndInvalidCodeVerifierThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, createPkceAuthorizationParametersS256())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(\"invalid-code-verifier\");\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t\t\t\tassertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenS256MethodAndValidCodeVerifierThenAuthenticated() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, createPkceAuthorizationParametersS256())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters);\n\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials()).isNull();\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnsupportedCodeChallengeMethodThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tMap<String, Object> authorizationRequestAdditionalParameters = createPkceAuthorizationParametersS256();\n\t\t// This should never happen: the Authorization endpoint should not allow it\n\t\tauthorizationRequestAdditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD,\n\t\t\t\t\"unsupported-challenge-method\");\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, authorizationRequestAdditionalParameters)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);\n\t}\n\n\tprivate static Map<String, Object> createAuthorizationCodeTokenParameters() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\tparameters.put(OAuth2ParameterNames.CODE, AUTHORIZATION_CODE);\n\t\treturn parameters;\n\t}\n\n\tprivate static Map<String, Object> createPkceTokenParameters(String codeVerifier) {\n\t\tMap<String, Object> parameters = createAuthorizationCodeTokenParameters();\n\t\tparameters.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);\n\t\treturn parameters;\n\t}\n\n\tprivate static Map<String, Object> createPkceAuthorizationParametersS256() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/X509ClientCertificateAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.authentication;\n\nimport java.security.cert.X509Certificate;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.util.Base64;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.util.TestX509Certificates;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link X509ClientCertificateAuthenticationProvider}.\n *\n * @author Joe Grandja\n */\npublic class X509ClientCertificateAuthenticationProviderTests {\n\n\t// See RFC 7636: Appendix B. Example for the S256 code_challenge_method\n\t// https://tools.ietf.org/html/rfc7636#appendix-B\n\tprivate static final String S256_CODE_VERIFIER = \"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\n\tprivate static final String S256_CODE_CHALLENGE = \"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\";\n\n\tprivate static final String AUTHORIZATION_CODE = \"code\";\n\n\tprivate static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);\n\n\tprivate JWKSet selfSignedCertificateJwkSet;\n\n\tprivate MockWebServer server;\n\n\tprivate String clientJwkSetUrl;\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate X509ClientCertificateAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\t// @formatter:off\n\t\tX509Certificate selfSignedCertificate = TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE[0];\n\t\tRSAKey selfSignedRSAKey = new RSAKey.Builder((RSAPublicKey) selfSignedCertificate.getPublicKey())\n\t\t\t\t.keyUse(KeyUse.SIGNATURE)\n\t\t\t\t.keyID(UUID.randomUUID().toString())\n\t\t\t\t.x509CertChain(Collections.singletonList(Base64.encode(selfSignedCertificate.getEncoded())))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.selfSignedCertificateJwkSet = new JWKSet(selfSignedRSAKey);\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.clientJwkSetUrl = this.server.url(\"/jwks\").toString();\n\t\t// @formatter:off\n\t\tMockResponse response = new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setBody(this.selfSignedCertificateJwkSet.toString());\n\t\t// @formatter:on\n\t\tthis.server.enqueue(response);\n\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new X509ClientCertificateAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\tthis.authorizationService);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new X509ClientCertificateAuthenticationProvider(null, this.authorizationService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new X509ClientCertificateAuthenticationProvider(this.registeredClientRepository, null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setCertificateVerifierWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setCertificateVerifier(null))\n\t\t\t.withMessage(\"certificateVerifier cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2ClientAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2ClientAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClientIdThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId() + \"-invalid\", ClientAuthenticationMethod.TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnsupportedClientAuthenticationMethodThenThrowOAuth2AuthenticationException() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"authentication_method\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenX509CertificateNotProvidedThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH, null, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"credentials\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPKIX509CertificateInvalidSubjectDNThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.x509CertificateSubjectDN(\"CN=demo-client-sample-2,OU=Spring Samples,O=Spring,C=US\")\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"x509_certificate_subject_dn\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPKIX509CertificateValidThenAuthenticated() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.x509CertificateSubjectDN(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0].getSubjectX500Principal().getName())\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);\n\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSelfSignedX509CertificateInvalidIssuerThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.jwkSetUrl(this.clientJwkSetUrl)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\t// PKI Certificate will have different issuer\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"x509_certificate_issuer\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSelfSignedX509CertificateMissingClientJwkSetUrlThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"client_jwk_set_url\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSelfSignedX509CertificateInvalidClientJwkSetUrlThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.jwkSetUrl(\"https://this is an invalid URL\")\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(\"jwk_set_uri\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSelfSignedX509CertificateJwkSetResponseErrorStatusThenThrowOAuth2AuthenticationException() {\n\t\tMockResponse jwkSetResponse = new MockResponse().setResponseCode(400);\n\t\tauthenticateWhenSelfSignedX509CertificateJwkSetResponseInvalidThenThrowOAuth2AuthenticationException(\n\t\t\t\tjwkSetResponse, \"jwk_set_response_error\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSelfSignedX509CertificateJwkSetResponseInvalidStatusThenThrowOAuth2AuthenticationException() {\n\t\tMockResponse jwkSetResponse = new MockResponse().setResponseCode(204);\n\t\tauthenticateWhenSelfSignedX509CertificateJwkSetResponseInvalidThenThrowOAuth2AuthenticationException(\n\t\t\t\tjwkSetResponse, \"jwk_set_response_status\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSelfSignedX509CertificateJwkSetResponseInvalidContentThenThrowOAuth2AuthenticationException() {\n\t\tMockResponse jwkSetResponse = new MockResponse().setResponseCode(200).setBody(\"invalid-content\");\n\t\tauthenticateWhenSelfSignedX509CertificateJwkSetResponseInvalidThenThrowOAuth2AuthenticationException(\n\t\t\t\tjwkSetResponse, \"jwk_set_response_body\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSelfSignedX509CertificateJwkSetResponseNoMatchingKeysThenThrowOAuth2AuthenticationException()\n\t\t\tthrows Exception {\n\t\t// @formatter:off\n\t\tX509Certificate pkiCertificate = TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0];\n\t\tRSAKey pkiRSAKey = new RSAKey.Builder((RSAPublicKey) pkiCertificate.getPublicKey())\n\t\t\t\t.keyUse(KeyUse.SIGNATURE)\n\t\t\t\t.keyID(UUID.randomUUID().toString())\n\t\t\t\t.x509CertChain(Collections.singletonList(Base64.encode(pkiCertificate.getEncoded())))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tMockResponse jwkSetResponse = new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setBody(new JWKSet(pkiRSAKey).toString());\n\t\t// @formatter:on\n\n\t\tauthenticateWhenSelfSignedX509CertificateJwkSetResponseInvalidThenThrowOAuth2AuthenticationException(\n\t\t\t\tjwkSetResponse, \"x509_certificate\");\n\t}\n\n\tprivate void authenticateWhenSelfSignedX509CertificateJwkSetResponseInvalidThenThrowOAuth2AuthenticationException(\n\t\t\tfinal MockResponse jwkSetResponse, String expectedErrorDescription) {\n\n\t\t// @formatter:off\n\t\tfinal Dispatcher dispatcher = new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\treturn jwkSetResponse;\n\t\t\t}\n\t\t};\n\t\tthis.server.setDispatcher(dispatcher);\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.jwkSetUrl(this.clientJwkSetUrl)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE, null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting((ex) -> ex.getError())\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\t\t\tassertThat(error.getDescription()).contains(expectedErrorDescription);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSelfSignedX509CertificateValidThenAuthenticated() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.jwkSetUrl(this.clientJwkSetUrl)\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE, null);\n\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials())\n\t\t\t.isEqualTo(TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPkceAndValidCodeVerifierThenAuthenticated() {\n\t\t// @formatter:off\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)\n\t\t\t\t.clientSettings(\n\t\t\t\t\t\tClientSettings.builder()\n\t\t\t\t\t\t\t\t.x509CertificateSubjectDN(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE[0].getSubjectX500Principal().getName())\n\t\t\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, createPkceAuthorizationParametersS256())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tMap<String, Object> parameters = createPkceTokenParameters(S256_CODE_VERIFIER);\n\n\t\tOAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(\n\t\t\t\tregisteredClient.getClientId(), ClientAuthenticationMethod.TLS_CLIENT_AUTH,\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE, parameters);\n\n\t\tOAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE));\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);\n\t\tassertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(authenticationResult.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);\n\t}\n\n\tprivate static Map<String, Object> createPkceAuthorizationParametersS256() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\tparameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);\n\t\treturn parameters;\n\t}\n\n\tprivate static Map<String, Object> createPkceTokenParameters(String codeVerifier) {\n\t\tMap<String, Object> parameters = createAuthorizationCodeTokenParameters();\n\t\tparameters.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);\n\t\treturn parameters;\n\t}\n\n\tprivate static Map<String, Object> createAuthorizationCodeTokenParameters() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\tparameters.put(OAuth2ParameterNames.CODE, AUTHORIZATION_CODE);\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.client;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link InMemoryRegisteredClientRepository}.\n *\n * @author Anoop Garlapati\n * @author Ovidiu Popa\n * @author Joe Grandja\n */\npublic class InMemoryRegisteredClientRepositoryTests {\n\n\tprivate RegisteredClient registration = TestRegisteredClients.registeredClient().build();\n\n\tprivate InMemoryRegisteredClientRepository clients = new InMemoryRegisteredClientRepository(this.registration);\n\n\t@Test\n\tpublic void constructorVarargsRegisteredClientWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tRegisteredClient registration = null;\n\t\t\tnew InMemoryRegisteredClientRepository(registration);\n\t\t}).withMessageContaining(\"registration cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorListRegisteredClientWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tList<RegisteredClient> registrations = null;\n\t\t\tnew InMemoryRegisteredClientRepository(registrations);\n\t\t}).withMessageContaining(\"registrations cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorListRegisteredClientWhenEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tList<RegisteredClient> registrations = Collections.emptyList();\n\t\t\tnew InMemoryRegisteredClientRepository(registrations);\n\t\t}).withMessageContaining(\"registrations cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorListRegisteredClientWhenDuplicateIdThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tRegisteredClient anotherRegistrationWithSameId = TestRegisteredClients.registeredClient2()\n\t\t\t\t.id(this.registration.getId())\n\t\t\t\t.build();\n\t\t\tList<RegisteredClient> registrations = Arrays.asList(this.registration, anotherRegistrationWithSameId);\n\t\t\tnew InMemoryRegisteredClientRepository(registrations);\n\t\t}).withMessageStartingWith(\"Registered client must be unique. Found duplicate identifier:\");\n\t}\n\n\t@Test\n\tpublic void constructorListRegisteredClientWhenDuplicateClientIdThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tRegisteredClient anotherRegistrationWithSameClientId = TestRegisteredClients.registeredClient2()\n\t\t\t\t.clientId(this.registration.getClientId())\n\t\t\t\t.build();\n\t\t\tList<RegisteredClient> registrations = Arrays.asList(this.registration,\n\t\t\t\t\tanotherRegistrationWithSameClientId);\n\t\t\tnew InMemoryRegisteredClientRepository(registrations);\n\t\t}).withMessageStartingWith(\"Registered client must be unique. Found duplicate client identifier:\");\n\t}\n\n\t@Test\n\tpublic void constructorListRegisteredClientWhenDuplicateClientSecretThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tRegisteredClient anotherRegistrationWithSameClientSecret = TestRegisteredClients.registeredClient2()\n\t\t\t\t.clientSecret(this.registration.getClientSecret())\n\t\t\t\t.build();\n\t\t\tList<RegisteredClient> registrations = Arrays.asList(this.registration,\n\t\t\t\t\tanotherRegistrationWithSameClientSecret);\n\t\t\tnew InMemoryRegisteredClientRepository(registrations);\n\t\t}).withMessageStartingWith(\"Registered client must be unique. Found duplicate client secret for identifier:\");\n\t}\n\n\t@Test\n\tpublic void findByIdWhenFoundThenFound() {\n\t\tString id = this.registration.getId();\n\t\tassertThat(this.clients.findById(id)).isEqualTo(this.registration);\n\t}\n\n\t@Test\n\tpublic void findByIdWhenNotFoundThenNull() {\n\t\tString missingId = this.registration.getId() + \"MISSING\";\n\t\tassertThat(this.clients.findById(missingId)).isNull();\n\t}\n\n\t@Test\n\tpublic void findByIdWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.clients.findById(null))\n\t\t\t.withMessageContaining(\"id cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void findByClientIdWhenFoundThenFound() {\n\t\tString clientId = this.registration.getClientId();\n\t\tassertThat(this.clients.findByClientId(clientId)).isEqualTo(this.registration);\n\t}\n\n\t@Test\n\tpublic void findByClientIdWhenNotFoundThenNull() {\n\t\tString missingClientId = this.registration.getClientId() + \"MISSING\";\n\t\tassertThat(this.clients.findByClientId(missingClientId)).isNull();\n\t}\n\n\t@Test\n\tpublic void findByClientIdWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.clients.findByClientId(null))\n\t\t\t.withMessageContaining(\"clientId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void saveWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.clients.save(null))\n\t\t\t.withMessageContaining(\"registeredClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void saveWhenExistingIdThenUpdate() {\n\t\tRegisteredClient registeredClient = createRegisteredClient(this.registration.getId(), \"client-id-2\",\n\t\t\t\t\"client-secret-2\");\n\t\tthis.clients.save(registeredClient);\n\t\tRegisteredClient savedClient = this.clients.findByClientId(registeredClient.getClientId());\n\t\tassertThat(savedClient).isEqualTo(registeredClient);\n\t}\n\n\t@Test\n\tpublic void saveWhenExistingClientIdThenThrowIllegalArgumentException() {\n\t\tRegisteredClient registeredClient = createRegisteredClient(\"client-2\", this.registration.getClientId(),\n\t\t\t\t\"client-secret-2\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.clients.save(registeredClient))\n\t\t\t.withMessage(\"Registered client must be unique. Found duplicate client identifier: \"\n\t\t\t\t\t+ registeredClient.getClientId());\n\t}\n\n\t@Test\n\tpublic void saveWhenExistingClientSecretThenThrowIllegalArgumentException() {\n\t\tRegisteredClient registeredClient = createRegisteredClient(\"client-2\", \"client-id-2\",\n\t\t\t\tthis.registration.getClientSecret());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.clients.save(registeredClient))\n\t\t\t.withMessage(\"Registered client must be unique. Found duplicate client secret for identifier: \"\n\t\t\t\t\t+ registeredClient.getId());\n\t}\n\n\t@Test\n\tpublic void saveWhenSavedAndFindByIdThenFound() {\n\t\tRegisteredClient registeredClient = createRegisteredClient();\n\t\tthis.clients.save(registeredClient);\n\t\tRegisteredClient savedClient = this.clients.findById(registeredClient.getId());\n\t\tassertThat(savedClient).isEqualTo(registeredClient);\n\t}\n\n\t@Test\n\tpublic void saveWhenSavedAndFindByClientIdThenFound() {\n\t\tRegisteredClient registeredClient = createRegisteredClient();\n\t\tthis.clients.save(registeredClient);\n\t\tRegisteredClient savedClient = this.clients.findByClientId(registeredClient.getClientId());\n\t\tassertThat(savedClient).isEqualTo(registeredClient);\n\t}\n\n\tprivate static RegisteredClient createRegisteredClient() {\n\t\treturn createRegisteredClient(\"client-2\", \"client-id-2\", \"client-secret-2\");\n\t}\n\n\tprivate static RegisteredClient createRegisteredClient(String id, String clientId, String clientSecret) {\n\t\t// @formatter:off\n\t\treturn RegisteredClient.withId(id)\n\t\t\t\t.clientId(clientId)\n\t\t\t\t.clientSecret(clientSecret)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.client;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.time.Instant;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.databind.JacksonModule;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.JsonMapperRegisteredClientParametersMapper;\nimport org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.JsonMapperRegisteredClientRowMapper;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link JdbcRegisteredClientRepository}.\n *\n * @author Rafal Lewczuk\n * @author Steve Riesenberg\n * @author Joe Grandja\n * @author Ovidiu Popa\n */\npublic class JdbcRegisteredClientRepositoryTests {\n\n\tprivate static final String OAUTH2_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE = \"/org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql\";\n\n\tprivate static final String OAUTH2_CUSTOM_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE = \"/org/springframework/security/oauth2/server/authorization/client/custom-oauth2-registered-client-schema.sql\";\n\n\tprivate EmbeddedDatabase db;\n\n\tprivate JdbcOperations jdbcOperations;\n\n\tprivate JdbcRegisteredClientRepository registeredClientRepository;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.db = createDb(OAUTH2_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE);\n\t\tthis.jdbcOperations = new JdbcTemplate(this.db);\n\t\tthis.registeredClientRepository = new JdbcRegisteredClientRepository(this.jdbcOperations);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.db.shutdown();\n\t}\n\n\t@Test\n\tpublic void constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new JdbcRegisteredClientRepository(null))\n\t\t\t\t.withMessage(\"jdbcOperations cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setRegisteredClientRowMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.registeredClientRepository.setRegisteredClientRowMapper(null))\n\t\t\t\t.withMessage(\"registeredClientRowMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setRegisteredClientParametersMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.registeredClientRepository.setRegisteredClientParametersMapper(null))\n\t\t\t\t.withMessage(\"registeredClientParametersMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void saveWhenRegisteredClientNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.registeredClientRepository.save(null))\n\t\t\t.withMessageContaining(\"registeredClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void saveWhenRegisteredClientExistsThenUpdated() {\n\t\tRegisteredClient originalRegisteredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(originalRegisteredClient);\n\n\t\tRegisteredClient registeredClient = this.registeredClientRepository.findById(originalRegisteredClient.getId());\n\t\tassertThat(registeredClient).isEqualTo(originalRegisteredClient);\n\n\t\tRegisteredClient updatedRegisteredClient = RegisteredClient.from(originalRegisteredClient)\n\t\t\t.clientId(\"test\")\n\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t.clientName(\"clientName\")\n\t\t\t.scope(\"scope2\")\n\t\t\t.build();\n\n\t\tRegisteredClient expectedUpdatedRegisteredClient = RegisteredClient.from(originalRegisteredClient)\n\t\t\t.clientName(\"clientName\")\n\t\t\t.scope(\"scope2\")\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(updatedRegisteredClient);\n\n\t\tregisteredClient = this.registeredClientRepository.findById(updatedRegisteredClient.getId());\n\t\tassertThat(registeredClient).isEqualTo(expectedUpdatedRegisteredClient);\n\t\tassertThat(registeredClient).isNotEqualTo(originalRegisteredClient);\n\t}\n\n\t@Test\n\tpublic void saveWhenNewThenSaved() {\n\t\tRegisteredClient expectedRegisteredClient = TestRegisteredClients.registeredClient()\n\t\t\t.clientSettings(\n\t\t\t\t\tClientSettings.builder().tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256).build())\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(expectedRegisteredClient);\n\t\tRegisteredClient registeredClient = this.registeredClientRepository.findById(expectedRegisteredClient.getId());\n\t\tassertThat(registeredClient).isEqualTo(expectedRegisteredClient);\n\t}\n\n\t@Test\n\tpublic void saveWhenClientSecretNullThenSaved() {\n\t\tRegisteredClient expectedRegisteredClient = TestRegisteredClients.registeredClient().clientSecret(null).build();\n\t\tthis.registeredClientRepository.save(expectedRegisteredClient);\n\t\tRegisteredClient registeredClient = this.registeredClientRepository.findById(expectedRegisteredClient.getId());\n\t\tassertThat(registeredClient).isEqualTo(expectedRegisteredClient);\n\t}\n\n\t// gh-1641\n\t@Test\n\tpublic void saveWhenMultipleWithClientSecretEmptyThenSaved() {\n\t\tRegisteredClient registeredClient1 = TestRegisteredClients.registeredClient()\n\t\t\t.id(\"registration-1\")\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"\")\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient1);\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient()\n\t\t\t.id(\"registration-2\")\n\t\t\t.clientId(\"client-2\")\n\t\t\t.clientSecret(\"\")\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient2);\n\t}\n\n\t@Test\n\tpublic void saveWhenExistingClientIdThenThrowIllegalArgumentException() {\n\t\tRegisteredClient registeredClient1 = TestRegisteredClients.registeredClient()\n\t\t\t.id(\"registration-1\")\n\t\t\t.clientId(\"client-1\")\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient1);\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient()\n\t\t\t.id(\"registration-2\")\n\t\t\t.clientId(\"client-1\")\n\t\t\t.build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.registeredClientRepository.save(registeredClient2))\n\t\t\t.withMessage(\"Registered client must be unique. Found duplicate client identifier: \"\n\t\t\t\t\t+ registeredClient2.getClientId());\n\t}\n\n\t@Test\n\tpublic void saveWhenExistingClientSecretThenThrowIllegalArgumentException() {\n\t\tRegisteredClient registeredClient1 = TestRegisteredClients.registeredClient()\n\t\t\t.id(\"registration-1\")\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.build();\n\t\tthis.registeredClientRepository.save(registeredClient1);\n\t\tRegisteredClient registeredClient2 = TestRegisteredClients.registeredClient()\n\t\t\t.id(\"registration-2\")\n\t\t\t.clientId(\"client-2\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.registeredClientRepository.save(registeredClient2))\n\t\t\t.withMessage(\"Registered client must be unique. Found duplicate client secret for identifier: \"\n\t\t\t\t\t+ registeredClient2.getId());\n\t}\n\n\t@Test\n\tpublic void saveLoadRegisteredClientWhenCustomStrategiesSetThenCalled() throws Exception {\n\t\tRowMapper<RegisteredClient> registeredClientRowMapper = spy(new JsonMapperRegisteredClientRowMapper());\n\t\tthis.registeredClientRepository.setRegisteredClientRowMapper(registeredClientRowMapper);\n\t\tJsonMapperRegisteredClientParametersMapper clientParametersMapper = new JsonMapperRegisteredClientParametersMapper();\n\t\tFunction<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper = spy(\n\t\t\t\tclientParametersMapper);\n\t\tthis.registeredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\t\tRegisteredClient result = this.registeredClientRepository.findById(registeredClient.getId());\n\t\tassertThat(result).isEqualTo(registeredClient);\n\t\tverify(registeredClientRowMapper).mapRow(any(), anyInt());\n\t\tverify(registeredClientParametersMapper).apply(any());\n\t}\n\n\t@Test\n\tpublic void findByIdWhenIdNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.registeredClientRepository.findById(null))\n\t\t\t\t.withMessage(\"id cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void findByIdWhenExistsThenFound() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\t\tRegisteredClient result = this.registeredClientRepository.findById(registeredClient.getId());\n\t\tassertThat(result).isEqualTo(registeredClient);\n\t}\n\n\t@Test\n\tpublic void findByIdWhenNotExistsThenNotFound() {\n\t\tRegisteredClient result = this.registeredClientRepository.findById(\"not-exists\");\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void findByClientIdWhenClientIdNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.registeredClientRepository.findByClientId(null))\n\t\t\t\t.withMessage(\"clientId cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void findByClientIdWhenExistsThenFound() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tthis.registeredClientRepository.save(registeredClient);\n\t\tRegisteredClient result = this.registeredClientRepository.findByClientId(registeredClient.getClientId());\n\t\tassertThat(result).isEqualTo(registeredClient);\n\t}\n\n\t@Test\n\tpublic void findByClientIdWhenNotExistsThenNotFound() {\n\t\tRegisteredClient result = this.registeredClientRepository.findByClientId(\"not-exists\");\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void tableDefinitionWhenCustomThenAbleToOverride() {\n\t\tEmbeddedDatabase db = createDb(OAUTH2_CUSTOM_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE);\n\t\tCustomJdbcRegisteredClientRepository registeredClientRepository = new CustomJdbcRegisteredClientRepository(\n\t\t\t\tnew JdbcTemplate(db));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tregisteredClientRepository.save(registeredClient);\n\t\tRegisteredClient foundRegisteredClient1 = registeredClientRepository.findById(registeredClient.getId());\n\t\tassertThat(foundRegisteredClient1).isEqualTo(registeredClient);\n\t\tRegisteredClient foundRegisteredClient2 = registeredClientRepository\n\t\t\t.findByClientId(registeredClient.getClientId());\n\t\tassertThat(foundRegisteredClient2).isEqualTo(registeredClient);\n\t\tdb.shutdown();\n\t}\n\n\tprivate static EmbeddedDatabase createDb(String schema) {\n\t\t// @formatter:off\n\t\treturn new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(schema)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static final class CustomJdbcRegisteredClientRepository extends JdbcRegisteredClientRepository {\n\n\t\t// @formatter:off\n\t\tprivate static final String COLUMN_NAMES = \"id, \"\n\t\t\t\t+ \"clientId, \"\n\t\t\t\t+ \"clientIdIssuedAt, \"\n\t\t\t\t+ \"clientSecret, \"\n\t\t\t\t+ \"clientSecretExpiresAt, \"\n\t\t\t\t+ \"clientName, \"\n\t\t\t\t+ \"clientAuthenticationMethods, \"\n\t\t\t\t+ \"authorizationGrantTypes, \"\n\t\t\t\t+ \"redirectUris, \"\n\t\t\t\t+ \"postLogoutRedirectUris, \"\n\t\t\t\t+ \"scopes, \"\n\t\t\t\t+ \"clientSettings,\"\n\t\t\t\t+ \"tokenSettings\";\n\t\t// @formatter:on\n\n\t\tprivate static final String TABLE_NAME = \"oauth2RegisteredClient\";\n\n\t\tprivate static final String LOAD_REGISTERED_CLIENT_SQL = \"SELECT \" + COLUMN_NAMES + \" FROM \" + TABLE_NAME\n\t\t\t\t+ \" WHERE \";\n\n\t\t// @formatter:off\n\t\tprivate static final String INSERT_REGISTERED_CLIENT_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\";\n\t\t// @formatter:on\n\n\t\tprivate CustomJdbcRegisteredClientRepository(JdbcOperations jdbcOperations) {\n\t\t\tsuper(jdbcOperations);\n\t\t\tsetRegisteredClientRowMapper(new CustomRegisteredClientRowMapper());\n\t\t}\n\n\t\t@Override\n\t\tpublic void save(RegisteredClient registeredClient) {\n\t\t\tList<SqlParameterValue> parameters = getRegisteredClientParametersMapper().apply(registeredClient);\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\t\tgetJdbcOperations().update(INSERT_REGISTERED_CLIENT_SQL, pss);\n\t\t}\n\n\t\t@Override\n\t\tpublic RegisteredClient findById(String id) {\n\t\t\treturn findBy(\"id = ?\", id);\n\t\t}\n\n\t\t@Override\n\t\tpublic RegisteredClient findByClientId(String clientId) {\n\t\t\treturn findBy(\"clientId = ?\", clientId);\n\t\t}\n\n\t\tprivate RegisteredClient findBy(String filter, Object... args) {\n\t\t\tList<RegisteredClient> result = getJdbcOperations().query(LOAD_REGISTERED_CLIENT_SQL + filter,\n\t\t\t\t\tgetRegisteredClientRowMapper(), args);\n\t\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t\t}\n\n\t\tprivate static final class CustomRegisteredClientRowMapper implements RowMapper<RegisteredClient> {\n\n\t\t\tprivate final JsonMapper jsonMapper;\n\n\t\t\tprivate CustomRegisteredClientRowMapper() {\n\t\t\t\tList<JacksonModule> modules = SecurityJacksonModules\n\t\t\t\t\t.getModules(CustomRegisteredClientRowMapper.class.getClassLoader());\n\t\t\t\tthis.jsonMapper = JsonMapper.builder().addModules(modules).build();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\t\tTimestamp clientIdIssuedAt = rs.getTimestamp(\"clientIdIssuedAt\");\n\t\t\t\tTimestamp clientSecretExpiresAt = rs.getTimestamp(\"clientSecretExpiresAt\");\n\t\t\t\tSet<String> clientAuthenticationMethods = StringUtils\n\t\t\t\t\t.commaDelimitedListToSet(rs.getString(\"clientAuthenticationMethods\"));\n\t\t\t\tSet<String> authorizationGrantTypes = StringUtils\n\t\t\t\t\t.commaDelimitedListToSet(rs.getString(\"authorizationGrantTypes\"));\n\t\t\t\tSet<String> redirectUris = StringUtils.commaDelimitedListToSet(rs.getString(\"redirectUris\"));\n\t\t\t\tSet<String> postLogoutRedirectUris = StringUtils\n\t\t\t\t\t.commaDelimitedListToSet(rs.getString(\"postLogoutRedirectUris\"));\n\t\t\t\tSet<String> clientScopes = StringUtils.commaDelimitedListToSet(rs.getString(\"scopes\"));\n\n\t\t\t\t// @formatter:off\n\t\t\t\tRegisteredClient.Builder builder = RegisteredClient.withId(rs.getString(\"id\"))\n\t\t\t\t\t\t.clientId(rs.getString(\"clientId\"))\n\t\t\t\t\t\t.clientIdIssuedAt((clientIdIssuedAt != null) ? clientIdIssuedAt.toInstant() : null)\n\t\t\t\t\t\t.clientSecret(rs.getString(\"clientSecret\"))\n\t\t\t\t\t\t.clientSecretExpiresAt((clientSecretExpiresAt != null) ? clientSecretExpiresAt.toInstant() : null)\n\t\t\t\t\t\t.clientName(rs.getString(\"clientName\"))\n\t\t\t\t\t\t.clientAuthenticationMethods((authenticationMethods) ->\n\t\t\t\t\t\t\t\tclientAuthenticationMethods.forEach((authenticationMethod) ->\n\t\t\t\t\t\t\t\t\t\tauthenticationMethods.add(resolveClientAuthenticationMethod(authenticationMethod))))\n\t\t\t\t\t\t.authorizationGrantTypes((grantTypes) ->\n\t\t\t\t\t\t\t\tauthorizationGrantTypes.forEach((grantType) ->\n\t\t\t\t\t\t\t\t\t\tgrantTypes.add(resolveAuthorizationGrantType(grantType))))\n\t\t\t\t\t\t.redirectUris((uris) -> uris.addAll(redirectUris))\n\t\t\t\t\t\t.postLogoutRedirectUris((uris) -> uris.addAll(postLogoutRedirectUris))\n\t\t\t\t\t\t.scopes((scopes) -> scopes.addAll(clientScopes));\n\t\t\t\t// @formatter:on\n\n\t\t\t\tMap<String, Object> clientSettingsMap = parseMap(rs.getString(\"clientSettings\"));\n\t\t\t\tbuilder.clientSettings(ClientSettings.withSettings(clientSettingsMap).build());\n\n\t\t\t\tMap<String, Object> tokenSettingsMap = parseMap(rs.getString(\"tokenSettings\"));\n\t\t\t\tbuilder.tokenSettings(TokenSettings.withSettings(tokenSettingsMap).build());\n\n\t\t\t\treturn builder.build();\n\t\t\t}\n\n\t\t\tprivate Map<String, Object> parseMap(String data) {\n\t\t\t\tfinal ParameterizedTypeReference<Map<String, Object>> typeReference = new ParameterizedTypeReference<>() {\n\t\t\t\t};\n\t\t\t\ttry {\n\t\t\t\t\ttools.jackson.databind.JavaType javaType = this.jsonMapper.getTypeFactory()\n\t\t\t\t\t\t.constructType(typeReference.getType());\n\t\t\t\t\treturn this.jsonMapper.readValue(data, javaType);\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new IllegalArgumentException(ex.getMessage(), ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate static AuthorizationGrantType resolveAuthorizationGrantType(String authorizationGrantType) {\n\t\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) {\n\t\t\t\t\treturn AuthorizationGrantType.AUTHORIZATION_CODE;\n\t\t\t\t}\n\t\t\t\telse if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) {\n\t\t\t\t\treturn AuthorizationGrantType.CLIENT_CREDENTIALS;\n\t\t\t\t}\n\t\t\t\telse if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) {\n\t\t\t\t\treturn AuthorizationGrantType.REFRESH_TOKEN;\n\t\t\t\t}\n\t\t\t\t// Custom authorization grant type\n\t\t\t\treturn new AuthorizationGrantType(authorizationGrantType);\n\t\t\t}\n\n\t\t\tprivate static ClientAuthenticationMethod resolveClientAuthenticationMethod(\n\t\t\t\t\tString clientAuthenticationMethod) {\n\t\t\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue().equals(clientAuthenticationMethod)) {\n\t\t\t\t\treturn ClientAuthenticationMethod.CLIENT_SECRET_BASIC;\n\t\t\t\t}\n\t\t\t\telse if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) {\n\t\t\t\t\treturn ClientAuthenticationMethod.CLIENT_SECRET_POST;\n\t\t\t\t}\n\t\t\t\telse if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) {\n\t\t\t\t\treturn ClientAuthenticationMethod.NONE;\n\t\t\t\t}\n\t\t\t\t// Custom client authentication method\n\t\t\t\treturn new ClientAuthenticationMethod(clientAuthenticationMethod);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.client;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link RegisteredClient}.\n *\n * @author Anoop Garlapati\n */\npublic class RegisteredClientTests {\n\n\tprivate static final String ID = \"registration-1\";\n\n\tprivate static final String CLIENT_ID = \"client-1\";\n\n\tprivate static final String CLIENT_SECRET = \"secret\";\n\n\tprivate static final Set<String> REDIRECT_URIS = Collections.singleton(\"https://example.com\");\n\n\tprivate static final Set<String> POST_LOGOUT_REDIRECT_URIS = Collections\n\t\t.singleton(\"https://example.com/oidc-post-logout\");\n\n\tprivate static final Set<String> SCOPES = Collections\n\t\t.unmodifiableSet(Stream.of(\"openid\", \"profile\", \"email\").collect(Collectors.toSet()));\n\n\tprivate static final Set<ClientAuthenticationMethod> CLIENT_AUTHENTICATION_METHODS = Collections\n\t\t.singleton(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\n\t@Test\n\tpublic void buildWhenAuthorizationGrantTypesNotSetThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenAllAttributesProvidedThenAllAttributesAreSet() {\n\t\tInstant clientIdIssuedAt = Instant.now();\n\t\tInstant clientSecretExpiresAt = clientIdIssuedAt.plus(30, ChronoUnit.DAYS);\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientIdIssuedAt(clientIdIssuedAt)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.clientSecretExpiresAt(clientSecretExpiresAt)\n\t\t\t.clientName(\"client-name\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.postLogoutRedirectUris(\n\t\t\t\t\t(postLogoutRedirectUris) -> postLogoutRedirectUris.addAll(POST_LOGOUT_REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getId()).isEqualTo(ID);\n\t\tassertThat(registration.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(registration.getClientIdIssuedAt()).isEqualTo(clientIdIssuedAt);\n\t\tassertThat(registration.getClientSecret()).isEqualTo(CLIENT_SECRET);\n\t\tassertThat(registration.getClientSecretExpiresAt()).isEqualTo(clientSecretExpiresAt);\n\t\tassertThat(registration.getClientName()).isEqualTo(\"client-name\");\n\t\tassertThat(registration.getAuthorizationGrantTypes())\n\t\t\t.isEqualTo(Collections.singleton(AuthorizationGrantType.AUTHORIZATION_CODE));\n\t\tassertThat(registration.getClientAuthenticationMethods()).isEqualTo(CLIENT_AUTHENTICATION_METHODS);\n\t\tassertThat(registration.getRedirectUris()).isEqualTo(REDIRECT_URIS);\n\t\tassertThat(registration.getPostLogoutRedirectUris()).isEqualTo(POST_LOGOUT_REDIRECT_URIS);\n\t\tassertThat(registration.getScopes()).isEqualTo(SCOPES);\n\t}\n\n\t@Test\n\tpublic void buildWhenIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(null));\n\t}\n\n\t@Test\n\tpublic void buildWhenClientIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(null)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUrisNotProvidedThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUrisConsumerClearsSetThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUri(\"https://example.com\")\n\t\t\t.redirectUris(Set::clear)\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenClientAuthenticationMethodNotProvidedThenDefaultToBasic() {\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getClientAuthenticationMethods())\n\t\t\t.isEqualTo(Collections.singleton(ClientAuthenticationMethod.CLIENT_SECRET_BASIC));\n\t}\n\n\t@Test\n\tpublic void buildWhenScopeIsEmptyThenScopeNotRequired() {\n\t\tRegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void buildWhenScopeConsumerIsProvidedThenConsumerAccepted() {\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getScopes()).isEqualTo(SCOPES);\n\t}\n\n\t@Test\n\tpublic void buildWhenScopeContainsSpaceThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scope(\"openid profile\")\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenScopeContainsInvalidCharacterThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scope(\"an\\\"invalid\\\"scope\")\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUriInvalidThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUri(\"invalid URI\")\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUriContainsFragmentThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUri(\"https://example.com/page#fragment\")\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenPostLogoutRedirectUriInvalidThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.postLogoutRedirectUri(\"invalid URI\")\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenPostLogoutRedirectUriContainsFragmentThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUri(\"https://example.com\")\n\t\t\t.postLogoutRedirectUri(\"https://example.com/index#fragment\")\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenTwoAuthorizationGrantTypesAreProvidedThenBothAreRegistered() {\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getAuthorizationGrantTypes()).containsExactlyInAnyOrder(\n\t\t\t\tAuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationGrantTypesConsumerIsProvidedThenConsumerAccepted() {\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantTypes((authorizationGrantTypes) -> {\n\t\t\t\tauthorizationGrantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\t\t\tauthorizationGrantTypes.add(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\t\t})\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getAuthorizationGrantTypes()).containsExactlyInAnyOrder(\n\t\t\t\tAuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationGrantTypesConsumerClearsSetThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.authorizationGrantTypes(Set::clear)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenTwoClientAuthenticationMethodsAreProvidedThenBothAreRegistered() {\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getClientAuthenticationMethods()).containsExactlyInAnyOrder(\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t}\n\n\t@Test\n\tpublic void buildWhenClientAuthenticationMethodsConsumerIsProvidedThenConsumerAccepted() {\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethods((clientAuthenticationMethods) -> {\n\t\t\t\tclientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\t\t\tclientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\t\t})\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getClientAuthenticationMethods()).containsExactlyInAnyOrder(\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t}\n\n\t@Test\n\tpublic void buildWhenOverrideIdThenOverridden() {\n\t\tString overriddenId = \"override\";\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.id(overriddenId)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getId()).isEqualTo(overriddenId);\n\t}\n\n\t@Test\n\tpublic void buildWhenRegisteredClientProvidedThenMakesACopy() {\n\t\tRegisteredClient registration = TestRegisteredClients.registeredClient().build();\n\t\tRegisteredClient updated = RegisteredClient.from(registration).build();\n\n\t\tassertThat(registration.getId()).isEqualTo(updated.getId());\n\t\tassertThat(registration.getClientId()).isEqualTo(updated.getClientId());\n\t\tassertThat(registration.getClientIdIssuedAt()).isEqualTo(updated.getClientIdIssuedAt());\n\t\tassertThat(registration.getClientSecret()).isEqualTo(updated.getClientSecret());\n\t\tassertThat(registration.getClientSecretExpiresAt()).isEqualTo(updated.getClientSecretExpiresAt());\n\t\tassertThat(registration.getClientName()).isEqualTo(updated.getClientName());\n\t\tassertThat(registration.getClientAuthenticationMethods()).isEqualTo(updated.getClientAuthenticationMethods());\n\t\tassertThat(registration.getClientAuthenticationMethods()).isNotSameAs(updated.getClientAuthenticationMethods());\n\t\tassertThat(registration.getAuthorizationGrantTypes()).isEqualTo(updated.getAuthorizationGrantTypes());\n\t\tassertThat(registration.getAuthorizationGrantTypes()).isNotSameAs(updated.getAuthorizationGrantTypes());\n\t\tassertThat(registration.getRedirectUris()).isEqualTo(updated.getRedirectUris());\n\t\tassertThat(registration.getRedirectUris()).isNotSameAs(updated.getRedirectUris());\n\t\tassertThat(registration.getPostLogoutRedirectUris()).isEqualTo(updated.getPostLogoutRedirectUris());\n\t\tassertThat(registration.getPostLogoutRedirectUris()).isNotSameAs(updated.getPostLogoutRedirectUris());\n\t\tassertThat(registration.getScopes()).isEqualTo(updated.getScopes());\n\t\tassertThat(registration.getScopes()).isNotSameAs(updated.getScopes());\n\t\tassertThat(registration.getClientSettings()).isEqualTo(updated.getClientSettings());\n\t\tassertThat(registration.getClientSettings()).isNotSameAs(updated.getClientSettings());\n\t\tassertThat(registration.getTokenSettings()).isEqualTo(updated.getTokenSettings());\n\t\tassertThat(registration.getTokenSettings()).isNotSameAs(updated.getTokenSettings());\n\t}\n\n\t@Test\n\tpublic void buildWhenRegisteredClientValuesOverriddenThenPropagated() {\n\t\tRegisteredClient registration = TestRegisteredClients.registeredClient().build();\n\t\tString newName = \"client-name\";\n\t\tString newSecret = \"new-secret\";\n\t\tString newScope = \"new-scope\";\n\t\tString newRedirectUri = \"https://another-redirect-uri.com\";\n\t\tString newPostLogoutRedirectUri = \"https://another-post-logout-redirect-uri.com\";\n\t\tRegisteredClient updated = RegisteredClient.from(registration)\n\t\t\t.clientName(newName)\n\t\t\t.clientSecret(newSecret)\n\t\t\t.scopes((scopes) -> {\n\t\t\t\tscopes.clear();\n\t\t\t\tscopes.add(newScope);\n\t\t\t})\n\t\t\t.redirectUris((redirectUris) -> {\n\t\t\t\tredirectUris.clear();\n\t\t\t\tredirectUris.add(newRedirectUri);\n\t\t\t})\n\t\t\t.postLogoutRedirectUris((postLogoutRedirectUris) -> {\n\t\t\t\tpostLogoutRedirectUris.clear();\n\t\t\t\tpostLogoutRedirectUris.add(newPostLogoutRedirectUri);\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(registration.getClientName()).isNotEqualTo(newName);\n\t\tassertThat(updated.getClientName()).isEqualTo(newName);\n\t\tassertThat(registration.getClientSecret()).isNotEqualTo(newSecret);\n\t\tassertThat(updated.getClientSecret()).isEqualTo(newSecret);\n\t\tassertThat(registration.getScopes()).doesNotContain(newScope);\n\t\tassertThat(updated.getScopes()).containsExactly(newScope);\n\t\tassertThat(registration.getRedirectUris()).doesNotContain(newRedirectUri);\n\t\tassertThat(updated.getRedirectUris()).containsExactly(newRedirectUri);\n\t\tassertThat(registration.getPostLogoutRedirectUris()).doesNotContain(newPostLogoutRedirectUri);\n\t\tassertThat(updated.getPostLogoutRedirectUris()).containsExactly(newPostLogoutRedirectUri);\n\t}\n\n\t@Test\n\tpublic void buildWhenPublicClientTypeThenDefaultSettings() {\n\t\tInstant clientIdIssuedAt = Instant.now();\n\t\tRegisteredClient registration = RegisteredClient.withId(ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientIdIssuedAt(clientIdIssuedAt)\n\t\t\t.clientName(\"client-name\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)\n\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(REDIRECT_URIS))\n\t\t\t.scopes((scopes) -> scopes.addAll(SCOPES))\n\t\t\t.build();\n\n\t\tassertThat(registration.getId()).isEqualTo(ID);\n\t\tassertThat(registration.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(registration.getClientIdIssuedAt()).isEqualTo(clientIdIssuedAt);\n\t\tassertThat(registration.getClientName()).isEqualTo(\"client-name\");\n\t\tassertThat(registration.getAuthorizationGrantTypes())\n\t\t\t.isEqualTo(Collections.singleton(AuthorizationGrantType.AUTHORIZATION_CODE));\n\t\tassertThat(registration.getClientAuthenticationMethods())\n\t\t\t.isEqualTo(Collections.singleton(ClientAuthenticationMethod.NONE));\n\t\tassertThat(registration.getRedirectUris()).isEqualTo(REDIRECT_URIS);\n\t\tassertThat(registration.getScopes()).isEqualTo(SCOPES);\n\t\tassertThat(registration.getClientSettings().isRequireProofKey()).isTrue();\n\t\tassertThat(registration.getClientSettings().isRequireAuthorizationConsent()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.client;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\n\n/**\n * @author Anoop Garlapati\n */\npublic final class TestRegisteredClients {\n\n\tprivate TestRegisteredClients() {\n\t}\n\n\tpublic static RegisteredClient.Builder registeredClient() {\n\t\treturn RegisteredClient.withId(\"registration-1\")\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS))\n\t\t\t.clientSecret(\"secret-1\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.redirectUri(\"https://example.com/callback-1\")\n\t\t\t.redirectUri(\"https://example.com/callback-2\")\n\t\t\t.redirectUri(\"https://example.com/callback-3\")\n\t\t\t.postLogoutRedirectUri(\"https://example.com/oidc-post-logout\")\n\t\t\t.scope(\"scope1\");\n\t}\n\n\tpublic static RegisteredClient.Builder registeredClient2() {\n\t\treturn RegisteredClient.withId(\"registration-2\")\n\t\t\t.clientId(\"client-2\")\n\t\t\t.clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS))\n\t\t\t.clientSecret(\"secret-2\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.redirectUri(\"https://example.com\")\n\t\t\t.postLogoutRedirectUri(\"https://example.com/oidc-post-logout\")\n\t\t\t.scope(\"scope1\")\n\t\t\t.scope(\"scope2\");\n\t}\n\n\tpublic static RegisteredClient.Builder registeredPublicClient() {\n\t\treturn RegisteredClient.withId(\"registration-3\")\n\t\t\t.clientId(\"client-3\")\n\t\t\t.clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS))\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)\n\t\t\t.redirectUri(\"https://example.com\")\n\t\t\t.scope(\"scope1\")\n\t\t\t.clientSettings(ClientSettings.builder().requireProofKey(true).build());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/context/TestAuthorizationServerContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.context;\n\nimport java.util.function.Supplier;\n\nimport org.springframework.lang.Nullable;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\n\n/**\n * @author Joe Grandja\n */\npublic class TestAuthorizationServerContext implements AuthorizationServerContext {\n\n\tprivate final AuthorizationServerSettings authorizationServerSettings;\n\n\tprivate final Supplier<String> issuerSupplier;\n\n\tpublic TestAuthorizationServerContext(AuthorizationServerSettings authorizationServerSettings,\n\t\t\t@Nullable Supplier<String> issuerSupplier) {\n\t\tthis.authorizationServerSettings = authorizationServerSettings;\n\t\tthis.issuerSupplier = issuerSupplier;\n\t}\n\n\t@Override\n\tpublic String getIssuer() {\n\t\treturn (this.issuerSupplier != null) ? this.issuerSupplier.get() : getAuthorizationServerSettings().getIssuer();\n\t}\n\n\t@Override\n\tpublic AuthorizationServerSettings getAuthorizationServerSettings() {\n\t\treturn this.authorizationServerSettings;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.http.converter;\n\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizationServerMetadataHttpMessageConverter}\n *\n * @author Daniel Garnier-Moiroux\n */\npublic class OAuth2AuthorizationServerMetadataHttpMessageConverterTests {\n\n\tprivate final OAuth2AuthorizationServerMetadataHttpMessageConverter messageConverter = new OAuth2AuthorizationServerMetadataHttpMessageConverter();\n\n\t@Test\n\tpublic void supportsWhenOAuth2AuthorizationServerMetadataThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OAuth2AuthorizationServerMetadata.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setAuthorizationServerMetadataParametersConverterWhenConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setAuthorizationServerMetadataParametersConverter(null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizationServerMetadataConverterWhenConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setAuthorizationServerMetadataConverter(null));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenRequiredParametersThenSuccess() throws Exception {\n\t\t// @formatter:off\n\t\tString authorizationServerMetadataResponse = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"issuer\\\": \\\"https://example.com\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"authorization_endpoint\\\": \\\"https://example.com/oauth2/authorize\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint\\\": \\\"https://example.com/oauth2/token\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"response_types_supported\\\": [\\\"code\\\"]\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.messageConverter\n\t\t\t.readInternal(OAuth2AuthorizationServerMetadata.class, response);\n\n\t\tassertThat(authorizationServerMetadata.getIssuer()).isEqualTo(new URL(\"https://example.com\"));\n\t\tassertThat(authorizationServerMetadata.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(new URL(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpoint())\n\t\t\t.isEqualTo(new URL(\"https://example.com/oauth2/token\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getJwkSetUrl()).isNull();\n\t\tassertThat(authorizationServerMetadata.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(authorizationServerMetadata.getScopes()).isNull();\n\t\tassertThat(authorizationServerMetadata.getGrantTypes()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpoint()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()).isNull();\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull();\n\t}\n\n\t@Test\n\tpublic void readInternalWhenValidParametersThenSuccess() throws Exception {\n\t\t// @formatter:off\n\t\tString authorizationServerMetadataResponse = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"issuer\\\": \\\"https://example.com\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"authorization_endpoint\\\": \\\"https://example.com/oauth2/authorize\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint\\\": \\\"https://example.com/oauth2/token\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint_auth_methods_supported\\\": [\\\"client_secret_basic\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"jwks_uri\\\": \\\"https://example.com/oauth2/jwks\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"scopes_supported\\\": [\\\"openid\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"response_types_supported\\\": [\\\"code\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"grant_types_supported\\\": [\\\"authorization_code\\\", \\\"client_credentials\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"revocation_endpoint\\\": \\\"https://example.com/oauth2/revoke\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"revocation_endpoint_auth_methods_supported\\\": [\\\"client_secret_basic\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"introspection_endpoint\\\": \\\"https://example.com/oauth2/introspect\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"introspection_endpoint_auth_methods_supported\\\": [\\\"client_secret_basic\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"code_challenge_methods_supported\\\": [\\\"S256\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"custom_claim\\\": \\\"value\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"custom_collection_claim\\\": [\\\"value1\\\", \\\"value2\\\"]\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = this.messageConverter\n\t\t\t.readInternal(OAuth2AuthorizationServerMetadata.class, response);\n\n\t\tassertThat(authorizationServerMetadata.getClaims()).hasSize(15);\n\t\tassertThat(authorizationServerMetadata.getIssuer()).isEqualTo(new URL(\"https://example.com\"));\n\t\tassertThat(authorizationServerMetadata.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(new URL(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpoint())\n\t\t\t.isEqualTo(new URL(\"https://example.com/oauth2/token\"));\n\t\tassertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(new URL(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(authorizationServerMetadata.getScopes()).containsExactly(\"openid\");\n\t\tassertThat(authorizationServerMetadata.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(authorizationServerMetadata.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpoint())\n\t\t\t.isEqualTo(new URL(\"https://example.com/oauth2/revoke\"));\n\t\tassertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint())\n\t\t\t.isEqualTo(new URL(\"https://example.com/oauth2/introspect\"));\n\t\tassertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly(\"S256\");\n\t\tassertThat(authorizationServerMetadata.getClaimAsString(\"custom_claim\")).isEqualTo(\"value\");\n\t\tassertThat(authorizationServerMetadata.getClaimAsStringList(\"custom_collection_claim\"))\n\t\t\t.containsExactlyInAnyOrder(\"value1\", \"value2\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenFailingConverterThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tthis.messageConverter.setAuthorizationServerMetadataConverter((source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t});\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(\"{}\".getBytes(), HttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OAuth2AuthorizationServerMetadata.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OAuth 2.0 Authorization Server Metadata\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n\t@Test\n\tpublic void readInternalWhenInvalidOAuth2AuthorizationServerMetadataThenThrowException() {\n\t\tString authorizationServerMetadataResponse = \"{ \\\"issuer\\\": null }\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OAuth2AuthorizationServerMetadata.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OAuth 2.0 Authorization Server Metadata\")\n\t\t\t.withMessageContaining(\"issuer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenOAuth2AuthorizationServerMetadataThenSuccess() {\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t.jwkSetUrl(\"https://example.com/oauth2/jwks\")\n\t\t\t.scope(\"openid\")\n\t\t\t.responseType(\"code\")\n\t\t\t.grantType(\"authorization_code\")\n\t\t\t.grantType(\"client_credentials\")\n\t\t\t.tokenRevocationEndpoint(\"https://example.com/oauth2/revoke\")\n\t\t\t.tokenRevocationEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t.tokenIntrospectionEndpoint(\"https://example.com/oauth2/introspect\")\n\t\t\t.tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t.codeChallengeMethod(\"S256\")\n\t\t\t.claim(\"custom_claim\", \"value\")\n\t\t\t.claim(\"custom_collection_claim\", Arrays.asList(\"value1\", \"value2\"))\n\t\t\t.build();\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tthis.messageConverter.writeInternal(authorizationServerMetadata, outputMessage);\n\n\t\tString authorizationServerMetadataResponse = outputMessage.getBodyAsString();\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"issuer\\\":\\\"https://example.com\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"authorization_endpoint\\\":\\\"https://example.com/oauth2/authorize\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"token_endpoint\\\":\\\"https://example.com/oauth2/token\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"token_endpoint_auth_methods_supported\\\":[\\\"client_secret_basic\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"jwks_uri\\\":\\\"https://example.com/oauth2/jwks\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"scopes_supported\\\":[\\\"openid\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"response_types_supported\\\":[\\\"code\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"grant_types_supported\\\":[\\\"authorization_code\\\",\\\"client_credentials\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"revocation_endpoint\\\":\\\"https://example.com/oauth2/revoke\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"revocation_endpoint_auth_methods_supported\\\":[\\\"client_secret_basic\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"introspection_endpoint\\\":\\\"https://example.com/oauth2/introspect\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"introspection_endpoint_auth_methods_supported\\\":[\\\"client_secret_basic\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"code_challenge_methods_supported\\\":[\\\"S256\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"custom_claim\\\":\\\"value\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"custom_collection_claim\\\":[\\\"value1\\\",\\\"value2\\\"]\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenWriteFailsThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tConverter<OAuth2AuthorizationServerMetadata, Map<String, Object>> failingConverter = (source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t};\n\t\tthis.messageConverter.setAuthorizationServerMetadataParametersConverter(failingConverter);\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tOAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t.responseType(\"code\")\n\t\t\t.build();\n\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.writeInternal(authorizationServerMetadata, outputMessage))\n\t\t\t.withMessageContaining(\"An error occurred writing the OAuth 2.0 Authorization Server Metadata\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2ClientRegistrationHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.http.converter;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2ClientRegistrationHttpMessageConverter}\n *\n * @author Joe Grandja\n * @since 7.0\n */\npublic class OAuth2ClientRegistrationHttpMessageConverterTests {\n\n\tprivate final OAuth2ClientRegistrationHttpMessageConverter messageConverter = new OAuth2ClientRegistrationHttpMessageConverter();\n\n\t@Test\n\tpublic void supportsWhenOAuth2ClientRegistrationThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OAuth2ClientRegistration.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setClientRegistrationConverter(null))\n\t\t\t.withMessageContaining(\"clientRegistrationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClientRegistrationParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setClientRegistrationParametersConverter(null))\n\t\t\t.withMessageContaining(\"clientRegistrationParametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenValidParametersThenSuccess() throws Exception {\n\t\t// @formatter:off\n\t\tString clientRegistrationRequest = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"client_id\\\": \\\"client-id\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"client_id_issued_at\\\": 1607633867,\\n\"\n\t\t\t\t+ \"\t\t\\\"client_secret\\\": \\\"client-secret\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"client_secret_expires_at\\\": 1607637467,\\n\"\n\t\t\t\t+ \"\t\t\\\"client_name\\\": \\\"client-name\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"redirect_uris\\\": [\\n\"\n\t\t\t\t+ \"\t\t\t\\\"https://client.example.com\\\"\\n\"\n\t\t\t\t+ \"\t\t],\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint_auth_method\\\": \\\"client_secret_basic\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"grant_types\\\": [\\n\"\n\t\t\t\t+ \"\t\t\t\\\"authorization_code\\\",\\n\"\n\t\t\t\t+ \"\t\t\t\\\"client_credentials\\\"\\n\"\n\t\t\t\t+ \"\t\t],\\n\"\n\t\t\t\t+ \"\t\t\\\"response_types\\\":[\\n\"\n\t\t\t\t+ \"\t\t\t\\\"code\\\"\\n\"\n\t\t\t\t+ \"\t\t],\\n\"\n\t\t\t\t+ \"\t\t\\\"scope\\\": \\\"scope1 scope2\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"jwks_uri\\\": \\\"https://client.example.com/jwks\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"a-claim\\\": \\\"a-value\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(clientRegistrationRequest.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2ClientRegistration clientRegistration = this.messageConverter.readInternal(OAuth2ClientRegistration.class,\n\t\t\t\tresponse);\n\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(clientRegistration.getClientIdIssuedAt()).isEqualTo(Instant.ofEpochSecond(1607633867L));\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"client-secret\");\n\t\tassertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(Instant.ofEpochSecond(1607637467L));\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(\"client-name\");\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(clientRegistration.getResponseTypes()).containsOnly(\"code\");\n\t\tassertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder(\"scope1\", \"scope2\");\n\t\tassertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL(\"https://client.example.com/jwks\"));\n\t\tassertThat(clientRegistration.getClaimAsString(\"a-claim\")).isEqualTo(\"a-value\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenClientSecretNoExpiryThenSuccess() {\n\t\t// @formatter:off\n\t\tString clientRegistrationRequest = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"client_id\\\": \\\"client-id\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"client_secret\\\": \\\"client-secret\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"client_secret_expires_at\\\": 0,\\n\"\n\t\t\t\t+ \"\t\t\\\"redirect_uris\\\": [\\n\"\n\t\t\t\t+ \"\t\t\t\\\"https://client.example.com\\\"\\n\"\n\t\t\t\t+ \"\t\t]\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(clientRegistrationRequest.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2ClientRegistration clientRegistration = this.messageConverter.readInternal(OAuth2ClientRegistration.class,\n\t\t\t\tresponse);\n\n\t\tassertThat(clientRegistration.getClaims()).hasSize(3);\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"client-secret\");\n\t\tassertThat(clientRegistration.getClientSecretExpiresAt()).isNull();\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenFailingConverterThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tthis.messageConverter.setClientRegistrationConverter((source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t});\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(\"{}\".getBytes(), HttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OAuth2ClientRegistration.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OAuth 2.0 Client Registration\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenClientRegistrationThenSuccess() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(Instant.ofEpochSecond(1607633867))\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientSecretExpiresAt(Instant.ofEpochSecond(1607637467))\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.responseType(OAuth2AuthorizationResponseType.CODE.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.jwkSetUrl(\"https://client.example.com/jwks\")\n\t\t\t\t.claim(\"a-claim\", \"a-value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tthis.messageConverter.writeInternal(clientRegistration, outputMessage);\n\n\t\tString clientRegistrationResponse = outputMessage.getBodyAsString();\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_id\\\":\\\"client-id\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_id_issued_at\\\":1607633867\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_secret\\\":\\\"client-secret\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_secret_expires_at\\\":1607637467\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_name\\\":\\\"client-name\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"redirect_uris\\\":[\\\"https://client.example.com\\\"]\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"token_endpoint_auth_method\\\":\\\"client_secret_basic\\\"\");\n\t\tassertThat(clientRegistrationResponse)\n\t\t\t.contains(\"\\\"grant_types\\\":[\\\"authorization_code\\\",\\\"client_credentials\\\"]\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"response_types\\\":[\\\"code\\\"]\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"scope\\\":\\\"scope1 scope2\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"jwks_uri\\\":\\\"https://client.example.com/jwks\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"a-claim\\\":\\\"a-value\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenClientSecretNoExpiryThenSuccess() {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tthis.messageConverter.writeInternal(clientRegistration, outputMessage);\n\n\t\tString clientRegistrationResponse = outputMessage.getBodyAsString();\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_id\\\":\\\"client-id\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_secret\\\":\\\"client-secret\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_secret_expires_at\\\":0\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"redirect_uris\\\":[\\\"https://client.example.com\\\"]\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenWriteFailsThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tConverter<OAuth2ClientRegistration, Map<String, Object>> failingConverter = (source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t};\n\t\tthis.messageConverter.setClientRegistrationParametersConverter(failingConverter);\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistration = OAuth2ClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.build();\n\t\t// @formatter:off\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class).isThrownBy(() -> this.messageConverter.writeInternal(clientRegistration, outputMessage))\n\t\t\t\t.withMessageContaining(\"An error occurred writing the OAuth 2.0 Client Registration\")\n\t\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.http.converter;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2TokenIntrospectionHttpMessageConverter}\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n */\npublic class OAuth2TokenIntrospectionHttpMessageConverterTests {\n\n\tprivate final OAuth2TokenIntrospectionHttpMessageConverter messageConverter = new OAuth2TokenIntrospectionHttpMessageConverter();\n\n\t@Test\n\tpublic void supportsWhenOAuth2TokenIntrospectionThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OAuth2TokenIntrospection.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setTokenIntrospectionParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setTokenIntrospectionParametersConverter(null));\n\t}\n\n\t@Test\n\tpublic void setTokenIntrospectionConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setTokenIntrospectionConverter(null));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenValidParametersThenSuccess() throws Exception {\n\t\t// @formatter:off\n\t\tString tokenIntrospectionResponseBody = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"active\\\": true,\\n\"\n\t\t\t\t+ \"\t\t\\\"client_id\\\": \\\"clientId1\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"username\\\": \\\"username1\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"iat\\\": 1607633867,\\n\"\n\t\t\t\t+ \"\t\t\\\"exp\\\": 1607637467,\\n\"\n\t\t\t\t+ \"\t\t\\\"scope\\\": \\\"scope1 scope2\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"token_type\\\": \\\"Bearer\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"nbf\\\": 1607633867,\\n\"\n\t\t\t\t+ \"\t\t\\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"aud\\\": [\\\"audience1\\\", \\\"audience2\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"iss\\\": \\\"https://example.com/issuer1\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"jti\\\": \\\"jwtId1\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(tokenIntrospectionResponseBody.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOAuth2TokenIntrospection tokenIntrospectionResponse = this.messageConverter\n\t\t\t.readInternal(OAuth2TokenIntrospection.class, response);\n\n\t\tassertThat(tokenIntrospectionResponse.isActive()).isTrue();\n\t\tassertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(\"clientId1\");\n\t\tassertThat(tokenIntrospectionResponse.getUsername()).isEqualTo(\"username1\");\n\t\tassertThat(tokenIntrospectionResponse.getIssuedAt()).isEqualTo(Instant.ofEpochSecond(1607633867L));\n\t\tassertThat(tokenIntrospectionResponse.getExpiresAt()).isEqualTo(Instant.ofEpochSecond(1607637467L));\n\t\tassertThat(tokenIntrospectionResponse.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(Arrays.asList(\"scope1\", \"scope2\"));\n\t\tassertThat(tokenIntrospectionResponse.getTokenType()).isEqualTo(\"Bearer\");\n\t\tassertThat(tokenIntrospectionResponse.getNotBefore()).isEqualTo(Instant.ofEpochSecond(1607633867L));\n\t\tassertThat(tokenIntrospectionResponse.getSubject()).isEqualTo(\"subject1\");\n\t\tassertThat(tokenIntrospectionResponse.getAudience())\n\t\t\t.containsExactlyInAnyOrderElementsOf(Arrays.asList(\"audience1\", \"audience2\"));\n\t\tassertThat(tokenIntrospectionResponse.getIssuer()).isEqualTo(new URL(\"https://example.com/issuer1\"));\n\t\tassertThat(tokenIntrospectionResponse.getId()).isEqualTo(\"jwtId1\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenFailingConverterThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tthis.messageConverter.setTokenIntrospectionConverter((source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t});\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(\"{}\".getBytes(), HttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OAuth2TokenIntrospection.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the Token Introspection Response\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenTokenIntrospectionThenSuccess() {\n\t\t// @formatter:off\n\t\tOAuth2TokenIntrospection tokenClaims = OAuth2TokenIntrospection.builder(true)\n\t\t\t\t.clientId(\"clientId1\")\n\t\t\t\t.username(\"username1\")\n\t\t\t\t.issuedAt(Instant.ofEpochSecond(1607633867))\n\t\t\t\t.expiresAt(Instant.ofEpochSecond(1607637467))\n\t\t\t\t.scope(\"scope1 scope2\")\n\t\t\t\t.tokenType(TokenType.BEARER.getValue())\n\t\t\t\t.notBefore(Instant.ofEpochSecond(1607633867))\n\t\t\t\t.subject(\"subject1\")\n\t\t\t\t.audience(\"audience1\")\n\t\t\t\t.audience(\"audience2\")\n\t\t\t\t.issuer(\"https://example.com/issuer1\")\n\t\t\t\t.id(\"jwtId1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tthis.messageConverter.writeInternal(tokenClaims, outputMessage);\n\n\t\tString tokenIntrospectionResponse = outputMessage.getBodyAsString();\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"active\\\":true\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"client_id\\\":\\\"clientId1\\\"\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"username\\\":\\\"username1\\\"\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"iat\\\":1607633867\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"exp\\\":1607637467\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"scope\\\":\\\"scope1 scope2\\\"\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"token_type\\\":\\\"Bearer\\\"\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"nbf\\\":1607633867\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"sub\\\":\\\"subject1\\\"\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"aud\\\":[\\\"audience1\\\",\\\"audience2\\\"]\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"iss\\\":\\\"https://example.com/issuer1\\\"\");\n\t\tassertThat(tokenIntrospectionResponse).contains(\"\\\"jti\\\":\\\"jwtId1\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenWriteFailsThenThrowsException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tConverter<OAuth2TokenIntrospection, Map<String, Object>> failingConverter = (source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t};\n\t\tthis.messageConverter.setTokenIntrospectionParametersConverter(failingConverter);\n\n\t\tOAuth2TokenIntrospection tokenClaims = OAuth2TokenIntrospection.builder().build();\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.writeInternal(tokenClaims, outputMessage))\n\t\t\t.withMessageContaining(\"An error occurred writing the Token Introspection Response\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson/OAuth2AuthorizationServerJacksonModuleTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson;\n\nimport java.security.Principal;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2AuthorizationServerJacksonModule}.\n *\n * @author Steve Riesenberg\n * @author Joe Grandja\n */\n@SuppressWarnings(\"removal\")\npublic class OAuth2AuthorizationServerJacksonModuleTests {\n\n\tprivate static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {\n\t};\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mapper = JsonMapper.builder()\n\t\t\t.addModules(SecurityJacksonModules.getModules(getClass().getClassLoader()))\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void readValueWhenOAuth2AuthorizationAttributesThenSuccess() {\n\t\tAuthentication principal = new UsernamePasswordAuthenticationToken(\"principal\", \"credentials\");\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization()\n\t\t\t.attributes((attrs) -> attrs.put(Principal.class.getName(), principal))\n\t\t\t.build();\n\t\tMap<String, Object> attributes = authorization.getAttributes();\n\t\tString json = this.mapper.writeValueAsString(attributes);\n\t\tassertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(attributes);\n\t}\n\n\t@Test\n\tpublic void readValueWhenOAuth2AccessTokenMetadataThenSuccess() {\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();\n\t\tMap<String, Object> metadata = authorization.getAccessToken().getMetadata();\n\t\tString json = this.mapper.writeValueAsString(metadata);\n\t\tassertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(metadata);\n\t}\n\n\t@Test\n\tpublic void readValueWhenClientSettingsThenSuccess() {\n\t\tClientSettings clientSettings = ClientSettings.builder()\n\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)\n\t\t\t.build();\n\t\tMap<String, Object> clientSettingsMap = clientSettings.getSettings();\n\t\tString json = this.mapper.writeValueAsString(clientSettingsMap);\n\t\tassertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(clientSettingsMap);\n\t}\n\n\t@Test\n\tpublic void readValueWhenTokenSettingsThenSuccess() {\n\t\tTokenSettings tokenSettings = TokenSettings.builder().build();\n\t\tMap<String, Object> tokenSettingsMap = tokenSettings.getSettings();\n\t\tString json = this.mapper.writeValueAsString(tokenSettingsMap);\n\t\tassertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(tokenSettingsMap);\n\t}\n\n\t@Test\n\tpublic void readValueWhenOAuth2TokenExchangeCompositeAuthenticationTokenThenSuccess() {\n\t\tAuthentication subject = new UsernamePasswordAuthenticationToken(\"principal\", \"credentials\");\n\t\tOAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(JwtClaimNames.ISS, \"issuer-1\", JwtClaimNames.SUB, \"actor1\"));\n\t\tOAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(JwtClaimNames.ISS, \"issuer-2\", JwtClaimNames.SUB, \"actor2\"));\n\t\tOAuth2TokenExchangeCompositeAuthenticationToken authentication = new OAuth2TokenExchangeCompositeAuthenticationToken(\n\t\t\t\tsubject, List.of(actor1, actor2));\n\t\tString json = this.mapper.writeValueAsString(authentication);\n\t\tassertThat(this.mapper.readValue(json, OAuth2TokenExchangeCompositeAuthenticationToken.class))\n\t\t\t.isEqualTo(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationServerJackson2ModuleTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.security.Principal;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.Module;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2AuthorizationServerJackson2Module}.\n *\n * @author Steve Riesenberg\n * @author Joe Grandja\n */\n@SuppressWarnings(\"removal\")\npublic class OAuth2AuthorizationServerJackson2ModuleTests {\n\n\tprivate static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {\n\t};\n\n\tprivate ObjectMapper objectMapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.objectMapper = new ObjectMapper();\n\t\tClassLoader classLoader = OAuth2AuthorizationServerJackson2Module.class.getClassLoader();\n\t\tList<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);\n\t\tthis.objectMapper.registerModules(securityModules);\n\t\tthis.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());\n\t}\n\n\t@Test\n\tpublic void readValueWhenOAuth2AuthorizationAttributesThenSuccess() throws Exception {\n\t\tAuthentication principal = new UsernamePasswordAuthenticationToken(\"principal\", \"credentials\");\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization()\n\t\t\t.attributes((attrs) -> attrs.put(Principal.class.getName(), principal))\n\t\t\t.build();\n\t\tMap<String, Object> attributes = authorization.getAttributes();\n\t\tString json = this.objectMapper.writeValueAsString(attributes);\n\t\tassertThat(this.objectMapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(attributes);\n\t}\n\n\t@Test\n\tpublic void readValueWhenOAuth2AccessTokenMetadataThenSuccess() throws Exception {\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();\n\t\tMap<String, Object> metadata = authorization.getAccessToken().getMetadata();\n\t\tString json = this.objectMapper.writeValueAsString(metadata);\n\t\tassertThat(this.objectMapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(metadata);\n\t}\n\n\t@Test\n\tpublic void readValueWhenClientSettingsThenSuccess() throws Exception {\n\t\tClientSettings clientSettings = ClientSettings.builder()\n\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)\n\t\t\t.build();\n\t\tMap<String, Object> clientSettingsMap = clientSettings.getSettings();\n\t\tString json = this.objectMapper.writeValueAsString(clientSettingsMap);\n\t\tassertThat(this.objectMapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(clientSettingsMap);\n\t}\n\n\t@Test\n\tpublic void readValueWhenTokenSettingsThenSuccess() throws Exception {\n\t\tTokenSettings tokenSettings = TokenSettings.builder().build();\n\t\tMap<String, Object> tokenSettingsMap = tokenSettings.getSettings();\n\t\tString json = this.objectMapper.writeValueAsString(tokenSettingsMap);\n\t\tassertThat(this.objectMapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(tokenSettingsMap);\n\t}\n\n\t@Test\n\tpublic void readValueWhenOAuth2TokenExchangeCompositeAuthenticationTokenThenSuccess() throws Exception {\n\t\tAuthentication subject = new UsernamePasswordAuthenticationToken(\"principal\", \"credentials\");\n\t\tOAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(JwtClaimNames.ISS, \"issuer-1\", JwtClaimNames.SUB, \"actor1\"));\n\t\tOAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(\n\t\t\t\tMap.of(JwtClaimNames.ISS, \"issuer-2\", JwtClaimNames.SUB, \"actor2\"));\n\t\tOAuth2TokenExchangeCompositeAuthenticationToken authentication = new OAuth2TokenExchangeCompositeAuthenticationToken(\n\t\t\t\tsubject, List.of(actor1, actor2));\n\t\tString json = this.objectMapper.writeValueAsString(authentication);\n\t\tassertThat(this.objectMapper.readValue(json, OAuth2TokenExchangeCompositeAuthenticationToken.class))\n\t\t\t.isEqualTo(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson2/TestingAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.jackson2;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * This mixin class is used to serialize/deserialize {@link TestingAuthenticationToken}.\n *\n * @author Steve Riesenberg\n * @since 7.0\n * @see TestingAuthenticationToken\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(value = { \"authenticated\" }, ignoreUnknown = true)\npublic class TestingAuthenticationTokenMixin {\n\n\t@JsonCreator\n\tTestingAuthenticationTokenMixin(@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"credentials\") Object credentials,\n\t\t\t@JsonProperty(\"authorities\") List<GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcClientRegistration}.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n */\npublic class OidcClientRegistrationTests {\n\n\t// @formatter:off\n\tprivate final OidcClientRegistration.Builder minimalBuilder =\n\t\t\tOidcClientRegistration.builder()\n\t\t\t\t\t.redirectUri(\"https://client.example.com\");\n\t// @formatter:on\n\n\t@Test\n\tpublic void buildWhenAllClaimsProvidedThenCreated() throws Exception {\n\t\t// @formatter:off\n\t\tInstant clientIdIssuedAt = Instant.now();\n\t\tInstant clientSecretExpiresAt = clientIdIssuedAt.plus(30, ChronoUnit.DAYS);\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(clientIdIssuedAt)\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientSecretExpiresAt(clientSecretExpiresAt)\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.postLogoutRedirectUri(\"https://client.example.com/oidc-post-logout\")\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue())\n\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256.getName())\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.responseType(OAuth2AuthorizationResponseType.CODE.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.jwkSetUrl(\"https://client.example.com/jwks\")\n\t\t\t\t.idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName())\n\t\t\t\t.registrationAccessToken(\"registration-access-token\")\n\t\t\t\t.registrationClientUrl(\"https://auth-server.com/connect/register?client_id=1\")\n\t\t\t\t.claim(\"a-claim\", \"a-value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(clientRegistration.getClientIdIssuedAt()).isEqualTo(clientIdIssuedAt);\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"client-secret\");\n\t\tassertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(clientSecretExpiresAt);\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(\"client-name\");\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t\tassertThat(clientRegistration.getPostLogoutRedirectUris())\n\t\t\t.containsOnly(\"https://client.example.com/oidc-post-logout\");\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue());\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm())\n\t\t\t.isEqualTo(MacAlgorithm.HS256.getName());\n\t\tassertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(clientRegistration.getResponseTypes()).containsOnly(\"code\");\n\t\tassertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder(\"scope1\", \"scope2\");\n\t\tassertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL(\"https://client.example.com/jwks\"));\n\t\tassertThat(clientRegistration.getIdTokenSignedResponseAlgorithm()).isEqualTo(\"RS256\");\n\t\tassertThat(clientRegistration.getRegistrationAccessToken()).isEqualTo(\"registration-access-token\");\n\t\tassertThat(clientRegistration.getRegistrationClientUrl().toString())\n\t\t\t.isEqualTo(\"https://auth-server.com/connect/register?client_id=1\");\n\t\tassertThat(clientRegistration.getClaimAsString(\"a-claim\")).isEqualTo(\"a-value\");\n\t}\n\n\t@Test\n\tpublic void buildWhenOnlyRequiredClaimsProvidedThenCreated() {\n\t\tOidcClientRegistration clientRegistration = this.minimalBuilder.build();\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenClaimsProvidedThenCreated() throws Exception {\n\t\tInstant clientIdIssuedAt = Instant.now();\n\t\tInstant clientSecretExpiresAt = clientIdIssuedAt.plus(30, ChronoUnit.DAYS);\n\t\tHashMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(OidcClientMetadataClaimNames.CLIENT_ID, \"client-id\");\n\t\tclaims.put(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, clientIdIssuedAt);\n\t\tclaims.put(OidcClientMetadataClaimNames.CLIENT_SECRET, \"client-secret\");\n\t\tclaims.put(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, clientSecretExpiresAt);\n\t\tclaims.put(OidcClientMetadataClaimNames.CLIENT_NAME, \"client-name\");\n\t\tclaims.put(OidcClientMetadataClaimNames.REDIRECT_URIS, Collections.singletonList(\"https://client.example.com\"));\n\t\tclaims.put(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS,\n\t\t\t\tCollections.singletonList(\"https://client.example.com/oidc-post-logout\"));\n\t\tclaims.put(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue());\n\t\tclaims.put(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_SIGNING_ALG, MacAlgorithm.HS256.getName());\n\t\tclaims.put(OidcClientMetadataClaimNames.GRANT_TYPES,\n\t\t\t\tArrays.asList(AuthorizationGrantType.AUTHORIZATION_CODE.getValue(),\n\t\t\t\t\t\tAuthorizationGrantType.CLIENT_CREDENTIALS.getValue()));\n\t\tclaims.put(OidcClientMetadataClaimNames.RESPONSE_TYPES, Collections.singletonList(\"code\"));\n\t\tclaims.put(OidcClientMetadataClaimNames.SCOPE, Arrays.asList(\"scope1\", \"scope2\"));\n\t\tclaims.put(OidcClientMetadataClaimNames.JWKS_URI, \"https://client.example.com/jwks\");\n\t\tclaims.put(OidcClientMetadataClaimNames.ID_TOKEN_SIGNED_RESPONSE_ALG, SignatureAlgorithm.RS256.getName());\n\t\tclaims.put(OidcClientMetadataClaimNames.REGISTRATION_ACCESS_TOKEN, \"registration-access-token\");\n\t\tclaims.put(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI,\n\t\t\t\t\"https://auth-server.com/connect/register?client_id=1\");\n\t\tclaims.put(\"a-claim\", \"a-value\");\n\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.withClaims(claims).build();\n\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(clientRegistration.getClientIdIssuedAt()).isEqualTo(clientIdIssuedAt);\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"client-secret\");\n\t\tassertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(clientSecretExpiresAt);\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(\"client-name\");\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t\tassertThat(clientRegistration.getPostLogoutRedirectUris())\n\t\t\t.containsOnly(\"https://client.example.com/oidc-post-logout\");\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue());\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm())\n\t\t\t.isEqualTo(MacAlgorithm.HS256.getName());\n\t\tassertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(clientRegistration.getResponseTypes()).containsOnly(\"code\");\n\t\tassertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder(\"scope1\", \"scope2\");\n\t\tassertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL(\"https://client.example.com/jwks\"));\n\t\tassertThat(clientRegistration.getIdTokenSignedResponseAlgorithm()).isEqualTo(\"RS256\");\n\t\tassertThat(clientRegistration.getRegistrationAccessToken()).isEqualTo(\"registration-access-token\");\n\t\tassertThat(clientRegistration.getRegistrationClientUrl().toString())\n\t\t\t.isEqualTo(\"https://auth-server.com/connect/register?client_id=1\");\n\t\tassertThat(clientRegistration.getClaimAsString(\"a-claim\")).isEqualTo(\"a-value\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OidcClientRegistration.withClaims(null))\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OidcClientRegistration.withClaims(Collections.emptyMap()))\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingClientIdThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.clientIdIssuedAt(Instant.now());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"client_id cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientSecretAndMissingClientIdThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.clientSecret(\"client-secret\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"client_id cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientIdIssuedAtNotInstantThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.claim(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, \"clientIdIssuedAt\");\n\t\t// @formatter:on\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"client_id_issued_at must be of type Instant\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingClientSecretThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t\t.clientSecretExpiresAt(Instant.now().plus(30, ChronoUnit.DAYS));\n\t\t// @formatter:on\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"client_secret cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientSecretExpiresAtNotInstantThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.claim(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, \"clientSecretExpiresAt\");\n\t\t// @formatter:on\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"client_secret_expires_at must be of type Instant\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingRedirectUrisThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = OidcClientRegistration.builder().clientName(\"client-name\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"redirect_uris cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUrisNotListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = OidcClientRegistration.builder()\n\t\t\t.claim(OidcClientMetadataClaimNames.REDIRECT_URIS, \"redirectUris\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"redirect_uris must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUrisEmptyListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = OidcClientRegistration.builder()\n\t\t\t.claim(OidcClientMetadataClaimNames.REDIRECT_URIS, Collections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"redirect_uris cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUrisAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = this.minimalBuilder\n\t\t\t\t.redirectUri(\"https://client1.example.com\")\n\t\t\t\t.redirectUris((redirectUris) -> {\n\t\t\t\t\tredirectUris.clear();\n\t\t\t\t\tredirectUris.add(\"https://client2.example.com\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getRedirectUris()).containsExactly(\"https://client2.example.com\");\n\t}\n\n\t@Test\n\tpublic void buildWhenPostLogoutRedirectUrisNotListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder\n\t\t\t.claim(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, \"postLogoutRedirectUris\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"post_logout_redirect_uris must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenPostLogoutRedirectUrisEmptyListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder\n\t\t\t.claim(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, Collections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"post_logout_redirect_uris cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenPostLogoutRedirectUrisAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = this.minimalBuilder\n\t\t\t\t.postLogoutRedirectUri(\"https://client1.example.com/oidc-post-logout\")\n\t\t\t\t.postLogoutRedirectUris((postLogoutRedirectUris) -> {\n\t\t\t\t\tpostLogoutRedirectUris.clear();\n\t\t\t\t\tpostLogoutRedirectUris.add(\"https://client2.example.com/oidc-post-logout\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getPostLogoutRedirectUris())\n\t\t\t.containsExactly(\"https://client2.example.com/oidc-post-logout\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesNotListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.GRANT_TYPES,\n\t\t\t\t\"grantTypes\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"grant_types must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesEmptyListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.GRANT_TYPES,\n\t\t\t\tCollections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"grant_types cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenGrantTypesAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = this.minimalBuilder\n\t\t\t\t.grantType(\"authorization_code\")\n\t\t\t\t.grantTypes((grantTypes) -> {\n\t\t\t\t\tgrantTypes.clear();\n\t\t\t\t\tgrantTypes.add(\"client_credentials\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getGrantTypes()).containsExactly(\"client_credentials\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesNotListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.RESPONSE_TYPES,\n\t\t\t\t\"responseTypes\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"response_types must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesEmptyListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.RESPONSE_TYPES,\n\t\t\t\tCollections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"response_types cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = this.minimalBuilder\n\t\t\t\t.responseType(\"token\")\n\t\t\t\t.responseTypes((responseTypes) -> {\n\t\t\t\t\tresponseTypes.clear();\n\t\t\t\t\tresponseTypes.add(\"code\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getResponseTypes()).containsExactly(\"code\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesNotListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.SCOPE,\n\t\t\t\t\"scopes\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"scope must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesEmptyListThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.SCOPE,\n\t\t\t\tCollections.emptyList());\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"scope cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesAddingOrRemovingThenCorrectValues() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = this.minimalBuilder\n\t\t\t\t.scope(\"should-be-removed\")\n\t\t\t\t.scopes((scopes) -> {\n\t\t\t\t\tscopes.clear();\n\t\t\t\t\tscopes.add(\"scope1\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.getScopes()).containsExactly(\"scope1\");\n\t}\n\n\t@Test\n\tpublic void buildWhenJwksUriNotUrlThenThrowIllegalArgumentException() {\n\t\tOidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.JWKS_URI,\n\t\t\t\t\"not an url\");\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"jwksUri must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void claimWhenNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OidcClientRegistration.builder().claim(null, \"claim-value\"))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void claimWhenValueNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OidcClientRegistration.builder().claim(\"claim-name\", null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void claimsWhenRemovingClaimThenNotPresent() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = this.minimalBuilder\n\t\t\t\t.claim(\"claim-name\", \"claim-value\")\n\t\t\t\t.claims((claims) -> claims.remove(\"claim-name\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.hasClaim(\"claim-name\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void claimsWhenAddingClaimThenPresent() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = this.minimalBuilder\n\t\t\t\t.claim(\"claim-name\", \"claim-value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(clientRegistration.hasClaim(\"claim-name\")).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfigurationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc;\n\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcProviderConfiguration}.\n *\n * @author Daniel Garnier-Moiroux\n */\npublic class OidcProviderConfigurationTests {\n\n\tprivate final OidcProviderConfiguration.Builder minimalConfigurationBuilder = OidcProviderConfiguration.builder()\n\t\t.issuer(\"https://example.com\")\n\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t.jwkSetUrl(\"https://example.com/oauth2/jwks\")\n\t\t.scope(\"openid\")\n\t\t.responseType(\"code\")\n\t\t.subjectType(\"public\")\n\t\t.idTokenSigningAlgorithm(\"RS256\");\n\n\t@Test\n\tpublic void buildWhenAllRequiredClaimsAndAdditionalClaimsThenCreated() {\n\t\tOidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t.jwkSetUrl(\"https://example.com/oauth2/jwks\")\n\t\t\t.scope(\"openid\")\n\t\t\t.responseType(\"code\")\n\t\t\t.grantType(\"authorization_code\")\n\t\t\t.grantType(\"client_credentials\")\n\t\t\t.subjectType(\"public\")\n\t\t\t.idTokenSigningAlgorithm(\"RS256\")\n\t\t\t.userInfoEndpoint(\"https://example.com/userinfo\")\n\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t.clientRegistrationEndpoint(\"https://example.com/connect/register\")\n\t\t\t.endSessionEndpoint(\"https://example.com/connect/logout\")\n\t\t\t.claim(\"a-claim\", \"a-value\")\n\t\t\t.build();\n\n\t\tassertThat(providerConfiguration.getIssuer()).isEqualTo(url(\"https://example.com\"));\n\t\tassertThat(providerConfiguration.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(url(\"https://example.com/oauth2/token\"));\n\t\tassertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(url(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(providerConfiguration.getScopes()).containsExactly(\"openid\");\n\t\tassertThat(providerConfiguration.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(providerConfiguration.getSubjectTypes()).containsExactly(\"public\");\n\t\tassertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly(\"RS256\");\n\t\tassertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url(\"https://example.com/userinfo\"));\n\t\tassertThat(providerConfiguration.getTokenEndpointAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(providerConfiguration.getClientRegistrationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/connect/register\"));\n\t\tassertThat(providerConfiguration.getEndSessionEndpoint()).isEqualTo(url(\"https://example.com/connect/logout\"));\n\t\tassertThat(providerConfiguration.<String>getClaim(\"a-claim\")).isEqualTo(\"a-value\");\n\t}\n\n\t@Test\n\tpublic void buildWhenOnlyRequiredClaimsThenCreated() {\n\t\tOidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t.jwkSetUrl(\"https://example.com/oauth2/jwks\")\n\t\t\t.scope(\"openid\")\n\t\t\t.responseType(\"code\")\n\t\t\t.subjectType(\"public\")\n\t\t\t.idTokenSigningAlgorithm(\"RS256\")\n\t\t\t.build();\n\n\t\tassertThat(providerConfiguration.getIssuer()).isEqualTo(url(\"https://example.com\"));\n\t\tassertThat(providerConfiguration.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(url(\"https://example.com/oauth2/token\"));\n\t\tassertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(url(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(providerConfiguration.getScopes()).containsExactly(\"openid\");\n\t\tassertThat(providerConfiguration.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(providerConfiguration.getGrantTypes()).isNull();\n\t\tassertThat(providerConfiguration.getSubjectTypes()).containsExactly(\"public\");\n\t\tassertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly(\"RS256\");\n\t\tassertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();\n\t}\n\n\t@Test\n\tpublic void buildWhenClaimsProvidedThenCreated() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(OidcProviderMetadataClaimNames.ISSUER, \"https://example.com\");\n\t\tclaims.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, \"https://example.com/oauth2/authorize\");\n\t\tclaims.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, \"https://example.com/oauth2/token\");\n\t\tclaims.put(OidcProviderMetadataClaimNames.JWKS_URI, \"https://example.com/oauth2/jwks\");\n\t\tclaims.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, Collections.singletonList(\"openid\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList(\"code\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList(\"public\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED,\n\t\t\t\tCollections.singletonList(\"RS256\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, \"https://example.com/userinfo\");\n\t\tclaims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, \"https://example.com/connect/register\");\n\t\tclaims.put(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT, \"https://example.com/connect/logout\");\n\t\tclaims.put(\"some-claim\", \"some-value\");\n\n\t\tOidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build();\n\n\t\tassertThat(providerConfiguration.getIssuer()).isEqualTo(url(\"https://example.com\"));\n\t\tassertThat(providerConfiguration.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(url(\"https://example.com/oauth2/token\"));\n\t\tassertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(url(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(providerConfiguration.getScopes()).containsExactly(\"openid\");\n\t\tassertThat(providerConfiguration.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(providerConfiguration.getGrantTypes()).isNull();\n\t\tassertThat(providerConfiguration.getSubjectTypes()).containsExactly(\"public\");\n\t\tassertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly(\"RS256\");\n\t\tassertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url(\"https://example.com/userinfo\"));\n\t\tassertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(providerConfiguration.getClientRegistrationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/connect/register\"));\n\t\tassertThat(providerConfiguration.getEndSessionEndpoint()).isEqualTo(url(\"https://example.com/connect/logout\"));\n\t\tassertThat(providerConfiguration.<String>getClaim(\"some-claim\")).isEqualTo(\"some-value\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClaimsProvidedWithUrlsThenCreated() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(OidcProviderMetadataClaimNames.ISSUER, url(\"https://example.com\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, url(\"https://example.com/oauth2/authorize\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, url(\"https://example.com/oauth2/token\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.JWKS_URI, url(\"https://example.com/oauth2/jwks\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, Collections.singletonList(\"openid\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList(\"code\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList(\"public\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED,\n\t\t\t\tCollections.singletonList(\"RS256\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, url(\"https://example.com/userinfo\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, url(\"https://example.com/connect/register\"));\n\t\tclaims.put(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT, url(\"https://example.com/connect/logout\"));\n\t\tclaims.put(\"some-claim\", \"some-value\");\n\n\t\tOidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build();\n\n\t\tassertThat(providerConfiguration.getIssuer()).isEqualTo(url(\"https://example.com\"));\n\t\tassertThat(providerConfiguration.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(url(\"https://example.com/oauth2/token\"));\n\t\tassertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(url(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(providerConfiguration.getScopes()).containsExactly(\"openid\");\n\t\tassertThat(providerConfiguration.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(providerConfiguration.getGrantTypes()).isNull();\n\t\tassertThat(providerConfiguration.getSubjectTypes()).containsExactly(\"public\");\n\t\tassertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly(\"RS256\");\n\t\tassertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url(\"https://example.com/userinfo\"));\n\t\tassertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();\n\t\tassertThat(providerConfiguration.getClientRegistrationEndpoint())\n\t\t\t.isEqualTo(url(\"https://example.com/connect/register\"));\n\t\tassertThat(providerConfiguration.getEndSessionEndpoint()).isEqualTo(url(\"https://example.com/connect/logout\"));\n\t\tassertThat(providerConfiguration.<String>getClaim(\"some-claim\")).isEqualTo(\"some-value\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OidcProviderConfiguration.withClaims(null))\n\t\t\t.isInstanceOf(IllegalArgumentException.class)\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void withClaimsWhenMissingRequiredClaimsThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OidcProviderConfiguration.withClaims(Collections.emptyMap()))\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenCalledTwiceThenGeneratesTwoConfigurations() {\n\t\tOidcProviderConfiguration first = this.minimalConfigurationBuilder.grantType(\"client_credentials\").build();\n\n\t\tOidcProviderConfiguration second = this.minimalConfigurationBuilder.claims((claims) -> {\n\t\t\tList<String> newGrantTypes = new ArrayList<>();\n\t\t\tnewGrantTypes.add(\"authorization_code\");\n\t\t\tnewGrantTypes.add(\"custom_grant\");\n\t\t\tclaims.put(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, newGrantTypes);\n\t\t}).build();\n\n\t\tassertThat(first.getGrantTypes()).containsExactly(\"client_credentials\");\n\t\tassertThat(second.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\", \"custom_grant\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingIssuerThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.ISSUER));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"issuer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenIssuerNotUrlThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.ISSUER, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"issuer must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingAuthorizationEndpointThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"authorizationEndpoint cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"authorizationEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingTokenEndpointThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"tokenEndpoint cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"tokenEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingJwksUriThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.JWKS_URI));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"jwksUri cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenJwksUriNotUrlThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.JWKS_URI, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageStartingWith(\"jwksUri must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingResponseTypesThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"responseTypes cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesNotListThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> {\n\t\t\tclaims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED);\n\t\t\tclaims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, \"code\");\n\t\t});\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageContaining(\"responseTypes must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseTypesEmptyListThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> {\n\t\t\tclaims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED);\n\t\t\tclaims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.emptyList());\n\t\t});\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageContaining(\"responseTypes cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingSubjectTypesThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build).withMessage(\"subjectTypes cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenSubjectTypesNotListThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> {\n\t\t\tclaims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED);\n\t\t\tclaims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, \"public\");\n\t\t});\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageContaining(\"subjectTypes must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenSubjectTypesEmptyListThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> {\n\t\t\tclaims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED);\n\t\t\tclaims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.emptyList());\n\t\t});\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageContaining(\"subjectTypes cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenMissingIdTokenSigningAlgorithmsThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"idTokenSigningAlgorithms cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenIdTokenSigningAlgorithmsNotListThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> {\n\t\t\tclaims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);\n\t\t\tclaims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, \"RS256\");\n\t\t});\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageContaining(\"idTokenSigningAlgorithms must be of type List\");\n\t}\n\n\t@Test\n\tpublic void buildWhenIdTokenSigningAlgorithmsEmptyListThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> {\n\t\t\tclaims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);\n\t\t\tclaims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.emptyList());\n\t\t});\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessageContaining(\"idTokenSigningAlgorithms cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenUserInfoEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"userInfoEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientRegistrationEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"clientRegistrationEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void buildWhenEndSessionEndpointNotUrlThenThrowIllegalArgumentException() {\n\t\tOidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT, \"not an url\"));\n\n\t\tassertThatIllegalArgumentException().isThrownBy(builder::build)\n\t\t\t.withMessage(\"endSessionEndpoint must be a valid URL\");\n\t}\n\n\t@Test\n\tpublic void responseTypesWhenAddingOrRemovingThenCorrectValues() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder.responseType(\"should-be-removed\")\n\t\t\t.responseTypes((responseTypes) -> {\n\t\t\t\tresponseTypes.clear();\n\t\t\t\tresponseTypes.add(\"some-response-type\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(configuration.getResponseTypes()).containsExactly(\"some-response-type\");\n\t}\n\n\t@Test\n\tpublic void responseTypesWhenNotPresentAndAddingThenCorrectValues() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED))\n\t\t\t.responseTypes((responseTypes) -> responseTypes.add(\"some-response-type\"))\n\t\t\t.build();\n\n\t\tassertThat(configuration.getResponseTypes()).containsExactly(\"some-response-type\");\n\t}\n\n\t@Test\n\tpublic void subjectTypesWhenAddingOrRemovingThenCorrectValues() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder.subjectType(\"should-be-removed\")\n\t\t\t.subjectTypes((subjectTypes) -> {\n\t\t\t\tsubjectTypes.clear();\n\t\t\t\tsubjectTypes.add(\"some-subject-type\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(configuration.getSubjectTypes()).containsExactly(\"some-subject-type\");\n\t}\n\n\t@Test\n\tpublic void idTokenSigningAlgorithmsWhenAddingOrRemovingThenCorrectValues() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder\n\t\t\t.idTokenSigningAlgorithm(\"should-be-removed\")\n\t\t\t.idTokenSigningAlgorithms((signingAlgorithms) -> {\n\t\t\t\tsigningAlgorithms.clear();\n\t\t\t\tsigningAlgorithms.add(\"ES256\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(configuration.getIdTokenSigningAlgorithms()).containsExactly(\"ES256\");\n\t}\n\n\t@Test\n\tpublic void scopesWhenAddingOrRemovingThenCorrectValues() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder.scope(\"should-be-removed\")\n\t\t\t.scopes((scopes) -> {\n\t\t\t\tscopes.clear();\n\t\t\t\tscopes.add(\"some-scope\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(configuration.getScopes()).containsExactly(\"some-scope\");\n\t}\n\n\t@Test\n\tpublic void grantTypesWhenAddingOrRemovingThenCorrectValues() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder.grantType(\"should-be-removed\")\n\t\t\t.grantTypes((grantTypes) -> {\n\t\t\t\tgrantTypes.clear();\n\t\t\t\tgrantTypes.add(\"some-grant-type\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(configuration.getGrantTypes()).containsExactly(\"some-grant-type\");\n\t}\n\n\t@Test\n\tpublic void tokenEndpointAuthenticationMethodsWhenAddingOrRemovingThenCorrectValues() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder\n\t\t\t.tokenEndpointAuthenticationMethod(\"should-be-removed\")\n\t\t\t.tokenEndpointAuthenticationMethods((authMethods) -> {\n\t\t\t\tauthMethods.clear();\n\t\t\t\tauthMethods.add(\"some-authentication-method\");\n\t\t\t})\n\t\t\t.build();\n\n\t\tassertThat(configuration.getTokenEndpointAuthenticationMethods()).containsExactly(\"some-authentication-method\");\n\t}\n\n\t@Test\n\tpublic void claimWhenNameIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OidcProviderConfiguration.builder().claim(null, \"value\"))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void claimWhenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OidcProviderConfiguration.builder().claim(\"claim-name\", null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void claimsWhenRemovingClaimThenNotPresent() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder.grantType(\"some-grant-type\")\n\t\t\t.claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED))\n\t\t\t.build();\n\t\tassertThat(configuration.getGrantTypes()).isNull();\n\t}\n\n\t@Test\n\tpublic void claimsWhenAddingClaimThenPresent() {\n\t\tOidcProviderConfiguration configuration = this.minimalConfigurationBuilder.claim(\"claim-name\", \"claim-value\")\n\t\t\t.build();\n\t\tassertThat(configuration.hasClaim(\"claim-name\")).isTrue();\n\t}\n\n\tprivate static URL url(String urlString) {\n\t\ttry {\n\t\t\treturn new URL(urlString);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(\"urlString must be a valid URL and valid URI\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.TestJwsHeaders;\nimport org.springframework.security.oauth2.jwt.TestJwtClaimsSets;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.ClientSettings;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OidcClientConfigurationAuthenticationProvider}.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n */\npublic class OidcClientConfigurationAuthenticationProviderTests {\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\tprivate OidcClientConfigurationAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authorizationServerSettings = AuthorizationServerSettings.builder().issuer(\"https://provider.com\").build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null));\n\t\tthis.authenticationProvider = new OidcClientConfigurationAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\tthis.authorizationService);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientConfigurationAuthenticationProvider(null, this.authorizationService))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientConfigurationAuthenticationProvider(this.registeredClientRepository, null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOidcClientRegistrationAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OidcClientRegistrationAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))\n\t\t\t.withMessage(\"clientRegistrationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotOAuth2TokenAuthenticationTokenThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, \"client-id\");\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(createJwtClientConfiguration());\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, \"client-id\");\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientConfiguration();\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.read\"));\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, \"client-id\");\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwt.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotActiveThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientConfiguration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.invalidate(jwtAccessToken)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.read\"));\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, registeredClient.getClientId());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt(Collections.singleton(\"unauthorized.scope\"));\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_unauthorized.scope\"));\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, registeredClient.getClientId());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting((ex) -> ex.getError())\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt(new HashSet<>(Arrays.asList(\"client.read\", \"scope1\")));\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.read\", \"SCOPE_scope1\"));\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, registeredClient.getClientId());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRegisteredClientNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientConfiguration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.read\"));\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, registeredClient.getClientId());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t\tverify(this.registeredClientRepository).findByClientId(eq(registeredClient.getClientId()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenClientIdNotEqualToAuthorizedClientThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientConfiguration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tRegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient()\n\t\t\t.id(\"registration-2\")\n\t\t\t.clientId(\"client-2\")\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(authorizedRegisteredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.read\"));\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, registeredClient.getClientId());\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t\tverify(this.registeredClientRepository).findByClientId(eq(registeredClient.getClientId()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidAccessTokenThenReturnClientRegistration() {\n\t\tJwt jwt = createJwtClientConfiguration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.clientAuthenticationMethods((clientAuthenticationMethods) -> {\n\t\t\t\tclientAuthenticationMethods.clear();\n\t\t\t\tclientAuthenticationMethods.add(ClientAuthenticationMethod.PRIVATE_KEY_JWT);\n\t\t\t})\n\t\t\t.clientSettings(ClientSettings.builder()\n\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t\t.jwkSetUrl(\"https://client.example.com/jwks\")\n\t\t\t\t.build())\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.read\"));\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, registeredClient.getClientId());\n\n\t\tOidcClientRegistrationAuthenticationToken authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t\tverify(this.registeredClientRepository).findByClientId(eq(registeredClient.getClientId()));\n\n\t\t// verify that the \"registration\" access token is not invalidated after it is used\n\t\tverify(this.authorizationService, never()).save(eq(authorization));\n\t\tassertThat(authorization.getAccessToken().isInvalidated()).isFalse();\n\n\t\tOidcClientRegistration clientRegistrationResult = authenticationResult.getClientRegistration();\n\t\tassertThat(clientRegistrationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(clientRegistrationResult.getClientIdIssuedAt()).isEqualTo(registeredClient.getClientIdIssuedAt());\n\t\tassertThat(clientRegistrationResult.getClientSecret()).isEqualTo(registeredClient.getClientSecret());\n\t\tassertThat(clientRegistrationResult.getClientSecretExpiresAt())\n\t\t\t.isEqualTo(registeredClient.getClientSecretExpiresAt());\n\t\tassertThat(clientRegistrationResult.getClientName()).isEqualTo(registeredClient.getClientName());\n\t\tassertThat(clientRegistrationResult.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(registeredClient.getRedirectUris());\n\n\t\tList<String> grantTypes = new ArrayList<>();\n\t\tregisteredClient.getAuthorizationGrantTypes()\n\t\t\t.forEach((authorizationGrantType) -> grantTypes.add(authorizationGrantType.getValue()));\n\t\tassertThat(clientRegistrationResult.getGrantTypes()).containsExactlyInAnyOrderElementsOf(grantTypes);\n\n\t\tassertThat(clientRegistrationResult.getResponseTypes())\n\t\t\t.containsExactly(OAuth2AuthorizationResponseType.CODE.getValue());\n\t\tassertThat(clientRegistrationResult.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(registeredClient.getScopes());\n\t\tassertThat(clientRegistrationResult.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(registeredClient.getClientAuthenticationMethods().iterator().next().getValue());\n\t\tassertThat(clientRegistrationResult.getTokenEndpointAuthenticationSigningAlgorithm())\n\t\t\t.isEqualTo(registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm().getName());\n\t\tassertThat(clientRegistrationResult.getJwkSetUrl().toString())\n\t\t\t.isEqualTo(registeredClient.getClientSettings().getJwkSetUrl());\n\t\tassertThat(clientRegistrationResult.getIdTokenSignedResponseAlgorithm())\n\t\t\t.isEqualTo(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName());\n\n\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\tString expectedRegistrationClientUrl = UriComponentsBuilder\n\t\t\t.fromUriString(authorizationServerContext.getIssuer())\n\t\t\t.path(authorizationServerContext.getAuthorizationServerSettings().getOidcClientRegistrationEndpoint())\n\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())\n\t\t\t.toUriString();\n\n\t\tassertThat(clientRegistrationResult.getRegistrationClientUrl().toString())\n\t\t\t.isEqualTo(expectedRegistrationClientUrl);\n\t\tassertThat(clientRegistrationResult.getRegistrationAccessToken()).isNull();\n\t}\n\n\tprivate static Jwt createJwtClientConfiguration() {\n\t\treturn createJwt(Collections.singleton(\"client.read\"));\n\t}\n\n\tprivate static Jwt createJwt(Set<String> scopes) {\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = TestJwsHeaders.jwsHeader()\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet()\n\t\t\t\t.claim(OAuth2ParameterNames.SCOPE, scopes)\n\t\t\t\t.build();\n\t\tJwt jwt = Jwt.withTokenValue(\"jwt-access-token\")\n\t\t\t\t.headers((headers) -> headers.putAll(jwsHeader.getHeaders()))\n\t\t\t\t.claims((claims) -> claims.putAll(jwtClaimsSet.getClaims()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\treturn jwt;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.TestJwsHeaders;\nimport org.springframework.security.oauth2.jwt.TestJwtClaimsSets;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientMetadataClaimNames;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.token.JwtGenerator;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;\nimport org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OidcClientRegistrationAuthenticationProvider}.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n */\npublic class OidcClientRegistrationAuthenticationProviderTests {\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate JwtEncoder jwtEncoder;\n\n\tprivate OAuth2TokenGenerator<?> tokenGenerator;\n\n\tprivate PasswordEncoder passwordEncoder;\n\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\tprivate OidcClientRegistrationAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.jwtEncoder = mock(JwtEncoder.class);\n\t\tJwtGenerator jwtGenerator = new JwtGenerator(this.jwtEncoder);\n\t\tthis.tokenGenerator = spy(new OAuth2TokenGenerator<Jwt>() {\n\t\t\t@Override\n\t\t\tpublic Jwt generate(OAuth2TokenContext context) {\n\t\t\t\treturn jwtGenerator.generate(context);\n\t\t\t}\n\t\t});\n\t\tthis.passwordEncoder = spy(new PasswordEncoder() {\n\t\t\t@Override\n\t\t\tpublic String encode(CharSequence rawPassword) {\n\t\t\t\treturn NoOpPasswordEncoder.getInstance().encode(rawPassword);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean matches(CharSequence rawPassword, String encodedPassword) {\n\t\t\t\treturn NoOpPasswordEncoder.getInstance().matches(rawPassword, encodedPassword);\n\t\t\t}\n\t\t});\n\t\tthis.authorizationServerSettings = AuthorizationServerSettings.builder().issuer(\"https://provider.com\").build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null));\n\t\tthis.authenticationProvider = new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\tthis.authorizationService, this.tokenGenerator);\n\t\tthis.authenticationProvider.setPasswordEncoder(this.passwordEncoder);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(null, this.authorizationService,\n\t\t\t\t\tthis.tokenGenerator))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository, null,\n\t\t\t\t\tthis.tokenGenerator))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\t\tthis.authorizationService, null))\n\t\t\t.withMessage(\"tokenGenerator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setRegisteredClientConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setRegisteredClientConverter(null))\n\t\t\t.withMessage(\"registeredClientConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))\n\t\t\t.withMessage(\"clientRegistrationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setPasswordEncoderWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setPasswordEncoder(null))\n\t\t\t.withMessage(\"passwordEncoder cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOidcClientRegistrationAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OidcClientRegistrationAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotOAuth2TokenAuthenticationTokenThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(createJwtClientRegistration());\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwt.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotActiveThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.invalidate(jwtAccessToken)\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt(Collections.singleton(\"unauthorized.scope\"));\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_unauthorized.scope\"));\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt(new HashSet<>(Arrays.asList(\"client.create\", \"scope1\")));\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\", \"SCOPE_scope1\"));\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t.build();\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidRedirectUriThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.redirectUri(\"invalid uri\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REDIRECT_URI);\n\t\t\t\tassertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.REDIRECT_URIS);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRedirectUriContainsFragmentThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client.example.com#fragment\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REDIRECT_URI);\n\t\t\t\tassertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.REDIRECT_URIS);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidPostLogoutRedirectUriThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.postLogoutRedirectUri(\"invalid uri\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(\"invalid_client_metadata\");\n\t\t\t\tassertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPostLogoutRedirectUriContainsFragmentThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.postLogoutRedirectUri(\"https://client.example.com/oidc-post-logout#fragment\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(\"invalid_client_metadata\");\n\t\t\t\tassertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidTokenEndpointAuthenticationMethodThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOidcClientRegistration.Builder builder = OidcClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client.example.com\");\n\t\t// @formatter:on\n\n\t\tString invalidClientMetadataErrorCode = \"invalid_client_metadata\";\n\n\t\t// @formatter:off\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256.getName());\n\t\tassertWhenClientRegistrationRequestInvalidThenThrowOAuth2AuthenticationException(principal, builder.build(),\n\t\t\t\tinvalidClientMetadataErrorCode, OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue())\n\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(\"none\");\n\t\tassertWhenClientRegistrationRequestInvalidThenThrowOAuth2AuthenticationException(principal, builder.build(),\n\t\t\t\tinvalidClientMetadataErrorCode, OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue())\n\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256.getName());\n\t\tassertWhenClientRegistrationRequestInvalidThenThrowOAuth2AuthenticationException(principal, builder.build(),\n\t\t\t\tinvalidClientMetadataErrorCode, OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue())\n\t\t\t\t.jwkSetUrl(\"https://client.example.com/jwks\")\n\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256.getName());\n\t\tassertWhenClientRegistrationRequestInvalidThenThrowOAuth2AuthenticationException(principal, builder.build(),\n\t\t\t\tinvalidClientMetadataErrorCode, OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue())\n\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256.getName());\n\t\tassertWhenClientRegistrationRequestInvalidThenThrowOAuth2AuthenticationException(principal, builder.build(),\n\t\t\t\tinvalidClientMetadataErrorCode, OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);\n\t\t// @formatter:on\n\t}\n\n\tprivate void assertWhenClientRegistrationRequestInvalidThenThrowOAuth2AuthenticationException(\n\t\t\tAuthentication principal, OidcClientRegistration clientRegistration, String errorCode,\n\t\t\tString errorDescription) {\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t\t\t\tassertThat(error.getDescription()).contains(errorDescription);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenTokenEndpointAuthenticationSigningAlgorithmNotProvidedThenDefaults() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwtClientConfiguration());\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOidcClientRegistration.Builder builder = OidcClientRegistration.builder()\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.scope(\"scope1\");\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue());\n\t\t// @formatter:on\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, builder.build());\n\t\tOidcClientRegistrationAuthenticationToken authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getClientRegistration().getTokenEndpointAuthenticationSigningAlgorithm())\n\t\t\t.isEqualTo(MacAlgorithm.HS256.getName());\n\t\tassertThat(authenticationResult.getClientRegistration().getClientSecret()).isNotNull();\n\t\tverify(this.passwordEncoder).encode(any());\n\t\treset(this.passwordEncoder);\n\n\t\t// @formatter:off\n\t\tbuilder\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue())\n\t\t\t\t.jwkSetUrl(\"https://client.example.com/jwks\");\n\t\t// @formatter:on\n\t\tauthentication = new OidcClientRegistrationAuthenticationToken(principal, builder.build());\n\t\tauthenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\t\tassertThat(authenticationResult.getClientRegistration().getTokenEndpointAuthenticationSigningAlgorithm())\n\t\t\t.isEqualTo(SignatureAlgorithm.RS256.getName());\n\t\tassertThat(authenticationResult.getClientRegistration().getClientSecret()).isNull();\n\t\tverifyNoInteractions(this.passwordEncoder);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenRegistrationAccessTokenNotGeneratedThenThrowOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tdoReturn(null).when(this.tokenGenerator).generate(any());\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t\t\tassertThat(error.getDescription())\n\t\t\t\t\t.contains(\"The token generator failed to generate the registration access token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidAccessTokenThenReturnClientRegistration() {\n\t\tJwt jwt = createJwtClientRegistration();\n\t\tOAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\tjwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, jwtAccessToken, jwt.getClaims())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.jwtEncoder.encode(any())).willReturn(createJwtClientConfiguration());\n\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.postLogoutRedirectUri(\"https://client.example.com/oidc-post-logout\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, clientRegistration);\n\t\tOidcClientRegistrationAuthenticationToken authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tArgumentCaptor<RegisteredClient> registeredClientCaptor = ArgumentCaptor.forClass(RegisteredClient.class);\n\t\tArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);\n\n\t\tverify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()),\n\t\t\t\teq(OAuth2TokenType.ACCESS_TOKEN));\n\t\tverify(this.registeredClientRepository).save(registeredClientCaptor.capture());\n\t\tverify(this.authorizationService, times(2)).save(authorizationCaptor.capture());\n\t\tverify(this.jwtEncoder).encode(any());\n\t\tverify(this.passwordEncoder).encode(any());\n\n\t\t// assert \"registration\" access token, which should be used for subsequent calls\n\t\t// to client configuration endpoint\n\t\tOAuth2Authorization authorizationResult = authorizationCaptor.getAllValues().get(0);\n\t\tassertThat(authorizationResult.getAccessToken().getToken().getScopes()).containsExactly(\"client.read\");\n\t\tassertThat(authorizationResult.getAccessToken().isActive()).isTrue();\n\t\tassertThat(authorizationResult.getRefreshToken()).isNull();\n\n\t\t// assert \"initial\" access token is invalidated\n\t\tauthorizationResult = authorizationCaptor.getAllValues().get(1);\n\t\tassertThat(authorizationResult.getAccessToken().isInvalidated()).isTrue();\n\t\tif (authorizationResult.getRefreshToken() != null) {\n\t\t\tassertThat(authorizationResult.getRefreshToken().isInvalidated()).isTrue();\n\t\t}\n\n\t\tRegisteredClient registeredClientResult = registeredClientCaptor.getValue();\n\t\tassertThat(registeredClientResult.getId()).isNotNull();\n\t\tassertThat(registeredClientResult.getClientId()).isNotNull();\n\t\tassertThat(registeredClientResult.getClientIdIssuedAt()).isNotNull();\n\t\tassertThat(registeredClientResult.getClientSecret()).isNotNull();\n\t\tassertThat(registeredClientResult.getClientName()).isEqualTo(clientRegistration.getClientName());\n\t\tassertThat(registeredClientResult.getClientAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registeredClientResult.getRedirectUris()).containsExactly(\"https://client.example.com\");\n\t\tassertThat(registeredClientResult.getPostLogoutRedirectUris())\n\t\t\t.containsExactly(\"https://client.example.com/oidc-post-logout\");\n\t\tassertThat(registeredClientResult.getAuthorizationGrantTypes()).containsExactlyInAnyOrder(\n\t\t\t\tAuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\tassertThat(registeredClientResult.getScopes()).containsExactlyInAnyOrder(\"scope1\", \"scope2\");\n\t\tassertThat(registeredClientResult.getClientSettings().isRequireProofKey()).isTrue();\n\t\tassertThat(registeredClientResult.getClientSettings().isRequireAuthorizationConsent()).isTrue();\n\t\tassertThat(registeredClientResult.getTokenSettings().getIdTokenSignatureAlgorithm())\n\t\t\t.isEqualTo(SignatureAlgorithm.RS256);\n\n\t\tOidcClientRegistration clientRegistrationResult = authenticationResult.getClientRegistration();\n\t\tassertThat(clientRegistrationResult.getClientId()).isEqualTo(registeredClientResult.getClientId());\n\t\tassertThat(clientRegistrationResult.getClientIdIssuedAt())\n\t\t\t.isEqualTo(registeredClientResult.getClientIdIssuedAt());\n\t\tassertThat(clientRegistrationResult.getClientSecret()).isEqualTo(registeredClientResult.getClientSecret());\n\t\tassertThat(clientRegistrationResult.getClientSecretExpiresAt())\n\t\t\t.isEqualTo(registeredClientResult.getClientSecretExpiresAt());\n\t\tassertThat(clientRegistrationResult.getClientName()).isEqualTo(registeredClientResult.getClientName());\n\t\tassertThat(clientRegistrationResult.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(registeredClientResult.getRedirectUris());\n\t\tassertThat(clientRegistrationResult.getPostLogoutRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(registeredClientResult.getPostLogoutRedirectUris());\n\n\t\tList<String> grantTypes = new ArrayList<>();\n\t\tregisteredClientResult.getAuthorizationGrantTypes()\n\t\t\t.forEach((authorizationGrantType) -> grantTypes.add(authorizationGrantType.getValue()));\n\t\tassertThat(clientRegistrationResult.getGrantTypes()).containsExactlyInAnyOrderElementsOf(grantTypes);\n\n\t\tassertThat(clientRegistrationResult.getResponseTypes())\n\t\t\t.containsExactly(OAuth2AuthorizationResponseType.CODE.getValue());\n\t\tassertThat(clientRegistrationResult.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(registeredClientResult.getScopes());\n\t\tassertThat(clientRegistrationResult.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(registeredClientResult.getClientAuthenticationMethods().iterator().next().getValue());\n\t\tassertThat(clientRegistrationResult.getIdTokenSignedResponseAlgorithm())\n\t\t\t.isEqualTo(registeredClientResult.getTokenSettings().getIdTokenSignatureAlgorithm().getName());\n\n\t\tAuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();\n\t\tString expectedRegistrationClientUrl = UriComponentsBuilder\n\t\t\t.fromUriString(authorizationServerContext.getIssuer())\n\t\t\t.path(authorizationServerContext.getAuthorizationServerSettings().getOidcClientRegistrationEndpoint())\n\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClientResult.getClientId())\n\t\t\t.toUriString();\n\n\t\tassertThat(clientRegistrationResult.getRegistrationClientUrl().toString())\n\t\t\t.isEqualTo(expectedRegistrationClientUrl);\n\t\tassertThat(clientRegistrationResult.getRegistrationAccessToken()).isEqualTo(jwt.getTokenValue());\n\t}\n\n\tprivate static Jwt createJwtClientRegistration() {\n\t\treturn createJwt(Collections.singleton(\"client.create\"));\n\t}\n\n\tprivate static Jwt createJwtClientConfiguration() {\n\t\treturn createJwt(Collections.singleton(\"client.read\"));\n\t}\n\n\tprivate static Jwt createJwt(Set<String> scopes) {\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = TestJwsHeaders.jwsHeader()\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet()\n\t\t\t\t.claim(OAuth2ParameterNames.SCOPE, scopes)\n\t\t\t\t.build();\n\t\tJwt jwt = Jwt.withTokenValue(\"jwt-access-token\")\n\t\t\t\t.headers((headers) -> headers.putAll(jwsHeader.getHeaders()))\n\t\t\t\t.claims((claims) -> claims.putAll(jwtClaimsSet.getClaims()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\treturn jwt;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcClientRegistrationAuthenticationToken}.\n *\n * @author Joe Grandja\n */\npublic class OidcClientRegistrationAuthenticationTokenTests {\n\n\tprivate TestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\n\tprivate OidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t.redirectUri(\"https://client.example.com\")\n\t\t.build();\n\n\t@Test\n\tpublic void constructorWhenPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(null, this.clientRegistration))\n\t\t\t.withMessage(\"principal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> new OidcClientRegistrationAuthenticationToken(this.principal, (OidcClientRegistration) null))\n\t\t\t.withMessage(\"clientRegistration cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientIdNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, (String) null))\n\t\t\t.withMessage(\"clientId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientIdEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, \"\"))\n\t\t\t.withMessage(\"clientId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenOidcClientRegistrationProvidedThenCreated() {\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tthis.principal, this.clientRegistration);\n\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authentication.getClientId()).isNull();\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(this.principal.isAuthenticated());\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientIdProvidedThenCreated() {\n\t\tOidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tthis.principal, \"client-1\");\n\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getClientRegistration()).isNull();\n\t\tassertThat(authentication.getClientId()).isEqualTo(\"client-1\");\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(this.principal.isAuthenticated());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OidcLogoutAuthenticationProvider}.\n *\n * @author Joe Grandja\n */\npublic class OidcLogoutAuthenticationProviderTests {\n\n\tprivate static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);\n\n\tprivate RegisteredClientRepository registeredClientRepository;\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate SessionRegistry sessionRegistry;\n\n\tprivate AuthorizationServerSettings authorizationServerSettings;\n\n\tprivate OidcLogoutAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registeredClientRepository = mock(RegisteredClientRepository.class);\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.sessionRegistry = mock(SessionRegistry.class);\n\t\tthis.authorizationServerSettings = AuthorizationServerSettings.builder().issuer(\"https://provider.com\").build();\n\t\tTestAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(\n\t\t\t\tthis.authorizationServerSettings, null);\n\t\tAuthorizationServerContextHolder.setContext(authorizationServerContext);\n\t\tthis.authenticationProvider = new OidcLogoutAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\tthis.authorizationService, this.sessionRegistry);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> new OidcLogoutAuthenticationProvider(null, this.authorizationService, this.sessionRegistry))\n\t\t\t.withMessage(\"registeredClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new OidcLogoutAuthenticationProvider(this.registeredClientRepository, null, this.sessionRegistry))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenSessionRegistryNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcLogoutAuthenticationProvider(this.registeredClientRepository,\n\t\t\t\t\tthis.authorizationService, null))\n\t\t\t.withMessage(\"sessionRegistry cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOidcLogoutAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OidcLogoutAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(\"id-token\", principal,\n\t\t\t\t\"session-1\", null, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(error.getDescription()).contains(\"id_token_hint\");\n\t\t\t});\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenInvalidatedThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken, (metadata) -> {\n\t\t\t\tmetadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims());\n\t\t\t\tmetadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true);\n\t\t\t})\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, \"session-1\", null, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(error.getDescription()).contains(\"id_token_hint\");\n\t\t\t});\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingAudienceThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, \"session-1\", null, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(error.getDescription()).contains(IdTokenClaimNames.AUD);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidAudienceThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId() + \"-invalid\"))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, \"session-1\", null, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(error.getDescription()).contains(IdTokenClaimNames.AUD);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidClientIdThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, \"session-1\", registeredClient.getClientId() + \"-invalid\", null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\t\tassertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID);\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidPostLogoutRedirectUriThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, \"session-1\", registeredClient.getClientId(), \"https://example.com/callback-1-invalid\", null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\t\tassertThat(error.getDescription()).contains(\"post_logout_redirect_uri\");\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\t}\n\n\t@Test\n\tpublic void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setAuthenticationValidator(null))\n\t\t\t.withMessage(\"authenticationValidator cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationValidatorThenUsed() throws NoSuchAlgorithmException {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tString sessionId = \"session-1\";\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.claim(\"sid\", createHash(sessionId))\n\t\t\t.build();\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tConsumer<OidcLogoutAuthenticationContext> authenticationValidator = mock(Consumer.class);\n\t\tthis.authenticationProvider.setAuthenticationValidator(authenticationValidator);\n\n\t\tauthenticateValidIdToken(principal, registeredClient, sessionId, idToken);\n\t\tverify(authenticationValidator).accept(any(OidcLogoutAuthenticationContext.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingSubThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tprincipal.setAuthenticated(true);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, \"session-1\", null, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(error.getDescription()).contains(\"sub\");\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\t}\n\n\t// gh-1235\n\t@Test\n\tpublic void authenticateWhenInvalidPrincipalThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tprincipal.setAuthenticated(true);\n\n\t\tTestingAuthenticationToken otherPrincipal = new TestingAuthenticationToken(\"other-principal\", \"credentials\");\n\t\totherPrincipal.setAuthenticated(true);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\totherPrincipal, \"session-1\", null, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(error.getDescription()).contains(\"sub\");\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingSidThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tString sessionId = \"session-1\";\n\t\tList<SessionInformation> sessions = Collections\n\t\t\t.singletonList(new SessionInformation(principal.getPrincipal(), sessionId, Date.from(Instant.now())));\n\t\tgiven(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))).willReturn(sessions);\n\n\t\tprincipal.setAuthenticated(true);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, sessionId, null, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(error.getDescription()).contains(\"sid\");\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidSidThenThrowOAuth2AuthenticationException() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.claim(\"sid\", \"other-session\")\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tString sessionId = \"session-1\";\n\t\tList<SessionInformation> sessions = Collections\n\t\t\t.singletonList(new SessionInformation(principal.getPrincipal(), sessionId, Date.from(Instant.now())));\n\t\tgiven(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))).willReturn(sessions);\n\n\t\tprincipal.setAuthenticated(true);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, sessionId, null, null, null);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(error.getDescription()).contains(\"sid\");\n\t\t\t});\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidIdTokenThenAuthenticated() throws Exception {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tString sessionId = \"session-1\";\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.claim(\"sid\", createHash(sessionId))\n\t\t\t.build();\n\t\tauthenticateValidIdToken(principal, registeredClient, sessionId, idToken);\n\t}\n\n\t// gh-1440\n\t@Test\n\tpublic void authenticateWhenValidExpiredIdTokenThenAuthenticated() throws Exception {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tString sessionId = \"session-1\";\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(principal.getName())\n\t\t\t.audience(Collections.singleton(registeredClient.getClientId()))\n\t\t\t.issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS))\n\t\t\t.expiresAt(Instant.now().minusSeconds(30).truncatedTo(ChronoUnit.MILLIS)) // Expired\n\t\t\t.claim(\"sid\", createHash(sessionId))\n\t\t\t.build();\n\t\tauthenticateValidIdToken(principal, registeredClient, sessionId, idToken);\n\t}\n\n\tprivate void authenticateValidIdToken(Authentication principal, RegisteredClient registeredClient, String sessionId,\n\t\t\tOidcIdToken idToken) {\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.principalName(principal.getName())\n\t\t\t.token(idToken,\n\t\t\t\t\t(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()))\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE)))\n\t\t\t.willReturn(authorization);\n\t\tgiven(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId())))\n\t\t\t.willReturn(registeredClient);\n\n\t\tSessionInformation sessionInformation = new SessionInformation(principal.getPrincipal(), sessionId,\n\t\t\t\tDate.from(Instant.now()));\n\t\tList<SessionInformation> sessions = Collections.singletonList(sessionInformation);\n\t\tgiven(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))).willReturn(sessions);\n\n\t\tprincipal.setAuthenticated(true);\n\t\tString postLogoutRedirectUri = registeredClient.getPostLogoutRedirectUris().toArray(new String[0])[0];\n\t\tString state = \"state\";\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(),\n\t\t\t\tprincipal, sessionId, registeredClient.getClientId(), postLogoutRedirectUri, state);\n\n\t\tOidcLogoutAuthenticationToken authenticationResult = (OidcLogoutAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tverify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE));\n\t\tverify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId()));\n\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(authenticationResult.getCredentials().toString()).isEmpty();\n\t\tassertThat(authenticationResult.getIdToken()).isEqualTo(idToken);\n\t\tassertThat(authenticationResult.getSessionId()).isEqualTo(sessionInformation.getSessionId());\n\t\tassertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());\n\t\tassertThat(authenticationResult.getPostLogoutRedirectUri()).isEqualTo(postLogoutRedirectUri);\n\t\tassertThat(authenticationResult.getState()).isEqualTo(state);\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t}\n\n\tprivate static String createHash(String value) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcLogoutAuthenticationToken}.\n *\n * @author Joe Grandja\n */\npublic class OidcLogoutAuthenticationTokenTests {\n\n\tprivate final String idTokenHint = \"id-token\";\n\n\tprivate final OidcIdToken idToken = OidcIdToken.withTokenValue(this.idTokenHint)\n\t\t.issuer(\"https://provider.com\")\n\t\t.subject(\"principal\")\n\t\t.issuedAt(Instant.now().minusSeconds(60))\n\t\t.expiresAt(Instant.now().plusSeconds(60))\n\t\t.build();\n\n\tprivate final TestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\n\tprivate final String sessionId = \"session-1\";\n\n\tprivate final String clientId = \"client-1\";\n\n\tprivate final String postLogoutRedirectUri = \"https://example.com/oidc-post-logout\";\n\n\tprivate final String state = \"state-1\";\n\n\t@Test\n\tpublic void constructorWhenIdTokenHintEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcLogoutAuthenticationToken(\"\", this.principal, this.sessionId, this.clientId,\n\t\t\t\t\tthis.postLogoutRedirectUri, this.state))\n\t\t\t.withMessage(\"idTokenHint cannot be empty\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcLogoutAuthenticationToken((String) null, this.principal, this.sessionId,\n\t\t\t\t\tthis.clientId, this.postLogoutRedirectUri, this.state))\n\t\t\t.withMessage(\"idTokenHint cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenIdTokenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcLogoutAuthenticationToken((OidcIdToken) null, this.principal, this.sessionId,\n\t\t\t\t\tthis.clientId, this.postLogoutRedirectUri, this.state))\n\t\t\t.withMessage(\"idToken cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcLogoutAuthenticationToken(this.idTokenHint, null, this.sessionId, this.clientId,\n\t\t\t\t\tthis.postLogoutRedirectUri, this.state))\n\t\t\t.withMessage(\"principal cannot be null\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcLogoutAuthenticationToken(this.idToken, null, this.sessionId, this.clientId,\n\t\t\t\t\tthis.postLogoutRedirectUri, this.state))\n\t\t\t.withMessage(\"principal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenIdTokenHintProvidedThenCreated() {\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(this.idTokenHint,\n\t\t\t\tthis.principal, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getIdTokenHint()).isEqualTo(this.idTokenHint);\n\t\tassertThat(authentication.getIdToken()).isNull();\n\t\tassertThat(authentication.getSessionId()).isEqualTo(this.sessionId);\n\t\tassertThat(authentication.getClientId()).isEqualTo(this.clientId);\n\t\tassertThat(authentication.getPostLogoutRedirectUri()).isEqualTo(this.postLogoutRedirectUri);\n\t\tassertThat(authentication.getState()).isEqualTo(this.state);\n\t\tassertThat(authentication.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void constructorWhenIdTokenProvidedThenCreated() {\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(this.idToken, this.principal,\n\t\t\t\tthis.sessionId, this.clientId, this.postLogoutRedirectUri, this.state);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authentication.getCredentials().toString()).isEmpty();\n\t\tassertThat(authentication.getIdTokenHint()).isEqualTo(this.idToken.getTokenValue());\n\t\tassertThat(authentication.getIdToken()).isEqualTo(this.idToken);\n\t\tassertThat(authentication.getSessionId()).isEqualTo(this.sessionId);\n\t\tassertThat(authentication.getClientId()).isEqualTo(this.clientId);\n\t\tassertThat(authentication.getPostLogoutRedirectUri()).isEqualTo(this.postLogoutRedirectUri);\n\t\tassertThat(authentication.getState()).isEqualTo(this.state);\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OidcUserInfoAuthenticationProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class OidcUserInfoAuthenticationProviderTests {\n\n\tprivate OAuth2AuthorizationService authorizationService;\n\n\tprivate OidcUserInfoAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizationService = mock(OAuth2AuthorizationService.class);\n\t\tthis.authenticationProvider = new OidcUserInfoAuthenticationProvider(this.authorizationService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OidcUserInfoAuthenticationProvider(null))\n\t\t\t.withMessage(\"authorizationService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setUserInfoMapperWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authenticationProvider.setUserInfoMapper(null))\n\t\t\t.withMessage(\"userInfoMapper cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOidcUserInfoAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OidcUserInfoAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotOfExpectedTypeThenThrowOAuth2AuthenticationException() {\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(\n\t\t\t\tnew UsernamePasswordAuthenticationToken(null, null));\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\n\t\tverifyNoInteractions(this.authorizationService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {\n\t\tString tokenValue = \"token\";\n\t\tJwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue);\n\t\tprincipal.setAuthenticated(false);\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\n\t\tverifyNoInteractions(this.authorizationService);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotFoundThenThrowOAuth2AuthenticationException() {\n\t\tString tokenValue = \"token\";\n\t\tJwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue);\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\n\t\tverify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotActiveThenThrowOAuth2AuthenticationException() {\n\t\tString tokenValue = \"token\";\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();\n\t\tauthorization = OAuth2Authorization.from(authorization)\n\t\t\t.invalidate(authorization.getAccessToken().getToken())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue);\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\n\t\tverify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() {\n\t\tString tokenValue = \"token\";\n\t\tgiven(this.authorizationService.findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(TestOAuth2Authorizations.authorization().build());\n\n\t\tJwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue);\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\n\t\tverify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenNullThenThrowOAuth2AuthenticationException() {\n\t\tString tokenValue = \"token\";\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization()\n\t\t\t.token(createAuthorization(tokenValue).getAccessToken().getToken())\n\t\t\t.build();\n\t\tgiven(this.authorizationService.findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(authorization);\n\n\t\tJwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue);\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(authentication))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\n\t\tverify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidAccessTokenThenReturnUserInfo() {\n\t\tString tokenValue = \"access-token\";\n\t\tgiven(this.authorizationService.findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)))\n\t\t\t.willReturn(createAuthorization(tokenValue));\n\n\t\tJwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue);\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal);\n\t\tOidcUserInfoAuthenticationToken authenticationResult = (OidcUserInfoAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(authentication);\n\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(authenticationResult.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\n\t\tOidcUserInfo userInfo = authenticationResult.getUserInfo();\n\t\tassertThat(userInfo.getClaims()).hasSize(20);\n\t\tassertThat(userInfo.getSubject()).isEqualTo(\"user1\");\n\t\tassertThat(userInfo.getFullName()).isEqualTo(\"First Last\");\n\t\tassertThat(userInfo.getGivenName()).isEqualTo(\"First\");\n\t\tassertThat(userInfo.getFamilyName()).isEqualTo(\"Last\");\n\t\tassertThat(userInfo.getMiddleName()).isEqualTo(\"Middle\");\n\t\tassertThat(userInfo.getNickName()).isEqualTo(\"User\");\n\t\tassertThat(userInfo.getPreferredUsername()).isEqualTo(\"user\");\n\t\tassertThat(userInfo.getProfile()).isEqualTo(\"https://example.com/user1\");\n\t\tassertThat(userInfo.getPicture()).isEqualTo(\"https://example.com/user1.jpg\");\n\t\tassertThat(userInfo.getWebsite()).isEqualTo(\"https://example.com\");\n\t\tassertThat(userInfo.getEmail()).isEqualTo(\"user1@example.com\");\n\t\tassertThat(userInfo.getEmailVerified()).isEqualTo(true);\n\t\tassertThat(userInfo.getGender()).isEqualTo(\"female\");\n\t\tassertThat(userInfo.getBirthdate()).isEqualTo(\"1970-01-01\");\n\t\tassertThat(userInfo.getZoneInfo()).isEqualTo(\"Europe/Paris\");\n\t\tassertThat(userInfo.getLocale()).isEqualTo(\"en-US\");\n\t\tassertThat(userInfo.getPhoneNumber()).isEqualTo(\"+1 (604) 555-1234;ext=5678\");\n\t\tassertThat(userInfo.getPhoneNumberVerified()).isEqualTo(false);\n\t\tassertThat(userInfo.getAddress().getFormatted())\n\t\t\t.isEqualTo(\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\");\n\t\tassertThat(userInfo.getUpdatedAt()).isEqualTo(Instant.parse(\"1970-01-01T00:00:00Z\"));\n\n\t\tverify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN));\n\t}\n\n\tprivate static OAuth2Authorization createAuthorization(String tokenValue) {\n\t\tInstant now = Instant.now();\n\t\tSet<String> scopes = new HashSet<>(Arrays.asList(OidcScopes.OPENID, OidcScopes.ADDRESS, OidcScopes.EMAIL,\n\t\t\t\tOidcScopes.PHONE, OidcScopes.PROFILE));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, tokenValue, now,\n\t\t\t\tnow.plusSeconds(300), scopes);\n\t\tOidcIdToken idToken = new OidcIdToken(\"id-token\", now, now.plusSeconds(900), createUserInfo().getClaims());\n\n\t\treturn TestOAuth2Authorizations.authorization().token(accessToken).token(idToken).build();\n\t}\n\n\tprivate static JwtAuthenticationToken createJwtAuthenticationToken(String tokenValue) {\n\t\tInstant now = Instant.now();\n\t\t// @formatter:off\n\t\tJwt jwt = Jwt.withTokenValue(tokenValue)\n\t\t\t\t.header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName())\n\t\t\t\t.issuedAt(now)\n\t\t\t\t.expiresAt(now.plusSeconds(300))\n\t\t\t\t.claim(StandardClaimNames.SUB, \"user\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\treturn new JwtAuthenticationToken(jwt, Collections.emptyList());\n\t}\n\n\tprivate static OidcUserInfo createUserInfo() {\n\t\t// @formatter:off\n\t\treturn OidcUserInfo.builder()\n\t\t\t\t.subject(\"user1\")\n\t\t\t\t.name(\"First Last\")\n\t\t\t\t.givenName(\"First\")\n\t\t\t\t.familyName(\"Last\")\n\t\t\t\t.middleName(\"Middle\")\n\t\t\t\t.nickname(\"User\")\n\t\t\t\t.preferredUsername(\"user\")\n\t\t\t\t.profile(\"https://example.com/user1\")\n\t\t\t\t.picture(\"https://example.com/user1.jpg\")\n\t\t\t\t.website(\"https://example.com\")\n\t\t\t\t.email(\"user1@example.com\")\n\t\t\t\t.emailVerified(true)\n\t\t\t\t.gender(\"female\")\n\t\t\t\t.birthdate(\"1970-01-01\")\n\t\t\t\t.zoneinfo(\"Europe/Paris\")\n\t\t\t\t.locale(\"en-US\")\n\t\t\t\t.phoneNumber(\"+1 (604) 555-1234;ext=5678\")\n\t\t\t\t.phoneNumberVerified(false)\n\t\t\t\t.claim(\"address\", Collections.singletonMap(\"formatted\", \"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\"))\n\t\t\t\t.updatedAt(\"1970-01-01T00:00:00Z\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.authentication;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcUserInfoAuthenticationToken}.\n *\n * @author Steve Riesenberg\n */\npublic class OidcUserInfoAuthenticationTokenTests {\n\n\t@Test\n\tpublic void constructorWhenPrincipalNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OidcUserInfoAuthenticationToken(null))\n\t\t\t.withMessage(\"principal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrincipalProvidedThenCreated() {\n\t\tUsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken(null, null);\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(authentication.getUserInfo()).isNull();\n\t\tassertThat(authentication.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrincipalAndUserInfoProvidedThenCreated() {\n\t\tUsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken(null, null);\n\t\tOidcUserInfo userInfo = new OidcUserInfo(Collections.singletonMap(StandardClaimNames.SUB, \"user\"));\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal, userInfo);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(authentication.getUserInfo()).isEqualTo(userInfo);\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.http.converter;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcClientRegistrationHttpMessageConverter}\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @since 7.0\n */\npublic class OidcClientRegistrationHttpMessageConverterTests {\n\n\tprivate final OidcClientRegistrationHttpMessageConverter messageConverter = new OidcClientRegistrationHttpMessageConverter();\n\n\t@Test\n\tpublic void supportsWhenOidcClientRegistrationThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OidcClientRegistration.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setClientRegistrationConverter(null))\n\t\t\t.withMessageContaining(\"clientRegistrationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClientRegistrationParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setClientRegistrationParametersConverter(null))\n\t\t\t.withMessageContaining(\"clientRegistrationParametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenRequiredParametersThenSuccess() {\n\t\t// @formatter:off\n\t\tString clientRegistrationRequest = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"redirect_uris\\\": [\\n\"\n\t\t\t\t+ \"\t\t\t\\\"https://client.example.com\\\"\\n\"\n\t\t\t\t+ \"\t\t]\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(clientRegistrationRequest.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOidcClientRegistration clientRegistration = this.messageConverter.readInternal(OidcClientRegistration.class,\n\t\t\t\tresponse);\n\n\t\tassertThat(clientRegistration.getClaims()).hasSize(1);\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenValidParametersThenSuccess() throws Exception {\n\t\t// @formatter:off\n\t\tString clientRegistrationRequest = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"client_id\\\": \\\"client-id\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"client_id_issued_at\\\": 1607633867,\\n\"\n\t\t\t\t+ \"\t\t\\\"client_secret\\\": \\\"client-secret\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"client_secret_expires_at\\\": 1607637467,\\n\"\n\t\t\t\t+ \"\t\t\\\"client_name\\\": \\\"client-name\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"redirect_uris\\\": [\\n\"\n\t\t\t\t+ \"\t\t\t\\\"https://client.example.com\\\"\\n\"\n\t\t\t\t+ \"\t\t],\\n\"\n\t\t\t\t+ \"\t\t\\\"post_logout_redirect_uris\\\": [\\n\"\n\t\t\t\t+ \"\t\t\t\\\"https://client.example.com/oidc-post-logout\\\"\\n\"\n\t\t\t\t+ \"\t\t],\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint_auth_method\\\": \\\"client_secret_jwt\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint_auth_signing_alg\\\": \\\"HS256\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"grant_types\\\": [\\n\"\n\t\t\t\t+ \"\t\t\t\\\"authorization_code\\\",\\n\"\n\t\t\t\t+ \"\t\t\t\\\"client_credentials\\\"\\n\"\n\t\t\t\t+ \"\t\t],\\n\"\n\t\t\t\t+ \"\t\t\\\"response_types\\\":[\\n\"\n\t\t\t\t+ \"\t\t\t\\\"code\\\"\\n\"\n\t\t\t\t+ \"\t\t],\\n\"\n\t\t\t\t+ \"\t\t\\\"scope\\\": \\\"scope1 scope2\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"jwks_uri\\\": \\\"https://client.example.com/jwks\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"id_token_signed_response_alg\\\": \\\"RS256\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"a-claim\\\": \\\"a-value\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(clientRegistrationRequest.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOidcClientRegistration clientRegistration = this.messageConverter.readInternal(OidcClientRegistration.class,\n\t\t\t\tresponse);\n\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(clientRegistration.getClientIdIssuedAt()).isEqualTo(Instant.ofEpochSecond(1607633867L));\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"client-secret\");\n\t\tassertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(Instant.ofEpochSecond(1607637467L));\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(\"client-name\");\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t\tassertThat(clientRegistration.getPostLogoutRedirectUris())\n\t\t\t.containsOnly(\"https://client.example.com/oidc-post-logout\");\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue());\n\t\tassertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm())\n\t\t\t.isEqualTo(MacAlgorithm.HS256.getName());\n\t\tassertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(clientRegistration.getResponseTypes()).containsOnly(\"code\");\n\t\tassertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder(\"scope1\", \"scope2\");\n\t\tassertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL(\"https://client.example.com/jwks\"));\n\t\tassertThat(clientRegistration.getIdTokenSignedResponseAlgorithm()).isEqualTo(\"RS256\");\n\t\tassertThat(clientRegistration.getClaimAsString(\"a-claim\")).isEqualTo(\"a-value\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenClientSecretNoExpiryThenSuccess() {\n\t\t// @formatter:off\n\t\tString clientRegistrationRequest = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"client_id\\\": \\\"client-id\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"client_secret\\\": \\\"client-secret\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"client_secret_expires_at\\\": 0,\\n\"\n\t\t\t\t+ \"\t\t\\\"redirect_uris\\\": [\\n\"\n\t\t\t\t+ \"\t\t\t\\\"https://client.example.com\\\"\\n\"\n\t\t\t\t+ \"\t\t]\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(clientRegistrationRequest.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOidcClientRegistration clientRegistration = this.messageConverter.readInternal(OidcClientRegistration.class,\n\t\t\t\tresponse);\n\n\t\tassertThat(clientRegistration.getClaims()).hasSize(3);\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"client-secret\");\n\t\tassertThat(clientRegistration.getClientSecretExpiresAt()).isNull();\n\t\tassertThat(clientRegistration.getRedirectUris()).containsOnly(\"https://client.example.com\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenFailingConverterThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tthis.messageConverter.setClientRegistrationConverter((source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t});\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(\"{}\".getBytes(), HttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OidcClientRegistration.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OpenID Client Registration\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenClientRegistrationThenSuccess() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(Instant.ofEpochSecond(1607633867))\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientSecretExpiresAt(Instant.ofEpochSecond(1607637467))\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.postLogoutRedirectUri(\"https://client.example.com/oidc-post-logout\")\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue())\n\t\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256.getName())\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.responseType(OAuth2AuthorizationResponseType.CODE.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.jwkSetUrl(\"https://client.example.com/jwks\")\n\t\t\t\t.idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName())\n\t\t\t\t.registrationAccessToken(\"registration-access-token\")\n\t\t\t\t.registrationClientUrl(\"https://auth-server.com/connect/register?client_id=1\")\n\t\t\t\t.claim(\"a-claim\", \"a-value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tthis.messageConverter.writeInternal(clientRegistration, outputMessage);\n\n\t\tString clientRegistrationResponse = outputMessage.getBodyAsString();\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_id\\\":\\\"client-id\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_id_issued_at\\\":1607633867\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_secret\\\":\\\"client-secret\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_secret_expires_at\\\":1607637467\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_name\\\":\\\"client-name\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"redirect_uris\\\":[\\\"https://client.example.com\\\"]\");\n\t\tassertThat(clientRegistrationResponse)\n\t\t\t.contains(\"\\\"post_logout_redirect_uris\\\":[\\\"https://client.example.com/oidc-post-logout\\\"]\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"token_endpoint_auth_method\\\":\\\"client_secret_jwt\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"token_endpoint_auth_signing_alg\\\":\\\"HS256\\\"\");\n\t\tassertThat(clientRegistrationResponse)\n\t\t\t.contains(\"\\\"grant_types\\\":[\\\"authorization_code\\\",\\\"client_credentials\\\"]\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"response_types\\\":[\\\"code\\\"]\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"scope\\\":\\\"scope1 scope2\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"jwks_uri\\\":\\\"https://client.example.com/jwks\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"id_token_signed_response_alg\\\":\\\"RS256\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"registration_access_token\\\":\\\"registration-access-token\\\"\");\n\t\tassertThat(clientRegistrationResponse)\n\t\t\t.contains(\"\\\"registration_client_uri\\\":\\\"https://auth-server.com/connect/register?client_id=1\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"a-claim\\\":\\\"a-value\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenClientSecretNoExpiryThenSuccess() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tthis.messageConverter.writeInternal(clientRegistration, outputMessage);\n\n\t\tString clientRegistrationResponse = outputMessage.getBodyAsString();\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_id\\\":\\\"client-id\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_secret\\\":\\\"client-secret\\\"\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"client_secret_expires_at\\\":0\");\n\t\tassertThat(clientRegistrationResponse).contains(\"\\\"redirect_uris\\\":[\\\"https://client.example.com\\\"]\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenWriteFailsThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tConverter<OidcClientRegistration, Map<String, Object>> failingConverter = (source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t};\n\t\tthis.messageConverter.setClientRegistrationParametersConverter(failingConverter);\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.build();\n\t\t// @formatter:off\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class).isThrownBy(() -> this.messageConverter.writeInternal(clientRegistration, outputMessage))\n\t\t\t\t.withMessageContaining(\"An error occurred writing the OpenID Client Registration\")\n\t\t\t\t.withMessageContaining(errorMessage);\n\t}\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.http.converter;\n\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcProviderConfigurationHttpMessageConverter}\n *\n * @author Daniel Garnier-Moiroux\n */\npublic class OidcProviderConfigurationHttpMessageConverterTests {\n\n\tprivate final OidcProviderConfigurationHttpMessageConverter messageConverter = new OidcProviderConfigurationHttpMessageConverter();\n\n\t@Test\n\tpublic void supportsWhenOidcProviderConfigurationThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OidcProviderConfiguration.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setProviderConfigurationParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setProviderConfigurationParametersConverter(null));\n\t}\n\n\t@Test\n\tpublic void setProviderConfigurationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setProviderConfigurationConverter(null));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenRequiredParametersThenSuccess() throws Exception {\n\t\t// @formatter:off\n\t\tString providerConfigurationResponse = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"issuer\\\": \\\"https://example.com\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"authorization_endpoint\\\": \\\"https://example.com/oauth2/authorize\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint\\\": \\\"https://example.com/oauth2/token\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"jwks_uri\\\": \\\"https://example.com/oauth2/jwks\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"response_types_supported\\\": [\\\"code\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"subject_types_supported\\\": [\\\"public\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"id_token_signing_alg_values_supported\\\": [\\\"RS256\\\"]\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOidcProviderConfiguration providerConfiguration = this.messageConverter\n\t\t\t.readInternal(OidcProviderConfiguration.class, response);\n\n\t\tassertThat(providerConfiguration.getIssuer()).isEqualTo(new URL(\"https://example.com\"));\n\t\tassertThat(providerConfiguration.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(new URL(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(new URL(\"https://example.com/oauth2/token\"));\n\t\tassertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(new URL(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(providerConfiguration.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(providerConfiguration.getSubjectTypes()).containsExactly(\"public\");\n\t\tassertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly(\"RS256\");\n\t\tassertThat(providerConfiguration.getScopes()).isNull();\n\t\tassertThat(providerConfiguration.getGrantTypes()).isNull();\n\t\tassertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();\n\t}\n\n\t@Test\n\tpublic void readInternalWhenValidParametersThenSuccess() throws Exception {\n\t\t// @formatter:off\n\t\tString providerConfigurationResponse = \"{\\n\"\n\t\t\t\t+ \"\t\t\\\"issuer\\\": \\\"https://example.com\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"authorization_endpoint\\\": \\\"https://example.com/oauth2/authorize\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint\\\": \\\"https://example.com/oauth2/token\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"jwks_uri\\\": \\\"https://example.com/oauth2/jwks\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"userinfo_endpoint\\\": \\\"https://example.com/userinfo\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"scopes_supported\\\": [\\\"openid\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"response_types_supported\\\": [\\\"code\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"grant_types_supported\\\": [\\\"authorization_code\\\", \\\"client_credentials\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"subject_types_supported\\\": [\\\"public\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"id_token_signing_alg_values_supported\\\": [\\\"RS256\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"token_endpoint_auth_methods_supported\\\": [\\\"client_secret_basic\\\"],\\n\"\n\t\t\t\t+ \"\t\t\\\"custom_claim\\\": \\\"value\\\",\\n\"\n\t\t\t\t+ \"\t\t\\\"custom_collection_claim\\\": [\\\"value1\\\", \\\"value2\\\"]\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\t\tOidcProviderConfiguration providerConfiguration = this.messageConverter\n\t\t\t.readInternal(OidcProviderConfiguration.class, response);\n\n\t\tassertThat(providerConfiguration.getIssuer()).isEqualTo(new URL(\"https://example.com\"));\n\t\tassertThat(providerConfiguration.getAuthorizationEndpoint())\n\t\t\t.isEqualTo(new URL(\"https://example.com/oauth2/authorize\"));\n\t\tassertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(new URL(\"https://example.com/oauth2/token\"));\n\t\tassertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(new URL(\"https://example.com/oauth2/jwks\"));\n\t\tassertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(new URL(\"https://example.com/userinfo\"));\n\t\tassertThat(providerConfiguration.getScopes()).containsExactly(\"openid\");\n\t\tassertThat(providerConfiguration.getResponseTypes()).containsExactly(\"code\");\n\t\tassertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder(\"authorization_code\",\n\t\t\t\t\"client_credentials\");\n\t\tassertThat(providerConfiguration.getSubjectTypes()).containsExactly(\"public\");\n\t\tassertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly(\"RS256\");\n\t\tassertThat(providerConfiguration.getTokenEndpointAuthenticationMethods())\n\t\t\t.containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());\n\t\tassertThat(providerConfiguration.<String>getClaim(\"custom_claim\")).isEqualTo(\"value\");\n\t\tassertThat(providerConfiguration.getClaimAsStringList(\"custom_collection_claim\"))\n\t\t\t.containsExactlyInAnyOrder(\"value1\", \"value2\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenFailingConverterThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tthis.messageConverter.setProviderConfigurationConverter((source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t});\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(\"{}\".getBytes(), HttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OidcProviderConfiguration.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OpenID Provider Configuration\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n\t@Test\n\tpublic void readInternalWhenInvalidProviderConfigurationThenThrowException() {\n\t\tString providerConfigurationResponse = \"{ \\\"issuer\\\": null }\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(),\n\t\t\t\tHttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OidcProviderConfiguration.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OpenID Provider Configuration\")\n\t\t\t.withMessageContaining(\"issuer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenProviderConfigurationThenSuccess() {\n\t\tOidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t.jwkSetUrl(\"https://example.com/oauth2/jwks\")\n\t\t\t.userInfoEndpoint(\"https://example.com/userinfo\")\n\t\t\t.scope(\"openid\")\n\t\t\t.responseType(\"code\")\n\t\t\t.grantType(\"authorization_code\")\n\t\t\t.grantType(\"client_credentials\")\n\t\t\t.subjectType(\"public\")\n\t\t\t.idTokenSigningAlgorithm(\"RS256\")\n\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t.claim(\"custom_claim\", \"value\")\n\t\t\t.claim(\"custom_collection_claim\", Arrays.asList(\"value1\", \"value2\"))\n\t\t\t.build();\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tthis.messageConverter.writeInternal(providerConfiguration, outputMessage);\n\n\t\tString providerConfigurationResponse = outputMessage.getBodyAsString();\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"issuer\\\":\\\"https://example.com\\\"\");\n\t\tassertThat(providerConfigurationResponse)\n\t\t\t.contains(\"\\\"authorization_endpoint\\\":\\\"https://example.com/oauth2/authorize\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"token_endpoint\\\":\\\"https://example.com/oauth2/token\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"jwks_uri\\\":\\\"https://example.com/oauth2/jwks\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"userinfo_endpoint\\\":\\\"https://example.com/userinfo\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"scopes_supported\\\":[\\\"openid\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"response_types_supported\\\":[\\\"code\\\"]\");\n\t\tassertThat(providerConfigurationResponse)\n\t\t\t.contains(\"\\\"grant_types_supported\\\":[\\\"authorization_code\\\",\\\"client_credentials\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"subject_types_supported\\\":[\\\"public\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"id_token_signing_alg_values_supported\\\":[\\\"RS256\\\"]\");\n\t\tassertThat(providerConfigurationResponse)\n\t\t\t.contains(\"\\\"token_endpoint_auth_methods_supported\\\":[\\\"client_secret_basic\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"custom_claim\\\":\\\"value\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"custom_collection_claim\\\":[\\\"value1\\\",\\\"value2\\\"]\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenWriteFailsThenThrowsException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tConverter<OidcProviderConfiguration, Map<String, Object>> failingConverter = (source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t};\n\t\tthis.messageConverter.setProviderConfigurationParametersConverter(failingConverter);\n\n\t\tOidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.authorizationEndpoint(\"https://example.com/oauth2/authorize\")\n\t\t\t.tokenEndpoint(\"https://example.com/oauth2/token\")\n\t\t\t.jwkSetUrl(\"https://example.com/oauth2/jwks\")\n\t\t\t.responseType(\"code\")\n\t\t\t.subjectType(\"public\")\n\t\t\t.idTokenSigningAlgorithm(\"RS256\")\n\t\t\t.build();\n\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.writeInternal(providerConfiguration, outputMessage))\n\t\t\t.withMessageContaining(\"An error occurred writing the OpenID Provider Configuration\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.http.converter;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcUserInfoHttpMessageConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class OidcUserInfoHttpMessageConverterTests {\n\n\tprivate final OidcUserInfoHttpMessageConverter messageConverter = new OidcUserInfoHttpMessageConverter();\n\n\t@Test\n\tpublic void supportsWhenOidcUserInfoThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OidcUserInfo.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setUserInfoConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setUserInfoConverter(null));\n\t}\n\n\t@Test\n\tpublic void setUserInfoParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setUserInfoParametersConverter(null));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenValidParametersThenSuccess() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\" +\n\t\t\t\t\"\t\\\"sub\\\": \\\"user1\\\",\\n\" +\n\t\t\t\t\"\t\\\"name\\\": \\\"First Last\\\",\\n\" +\n\t\t\t\t\"\t\\\"given_name\\\": \\\"First\\\",\\n\" +\n\t\t\t\t\"\t\\\"family_name\\\": \\\"Last\\\",\\n\" +\n\t\t\t\t\"\t\\\"middle_name\\\": \\\"Middle\\\",\\n\" +\n\t\t\t\t\"\t\\\"nickname\\\": \\\"User\\\",\\n\" +\n\t\t\t\t\"\t\\\"preferred_username\\\": \\\"user\\\",\\n\" +\n\t\t\t\t\"\t\\\"profile\\\": \\\"https://example.com/user1\\\",\\n\" +\n\t\t\t\t\"\t\\\"picture\\\": \\\"https://example.com/user1.jpg\\\",\\n\" +\n\t\t\t\t\"\t\\\"website\\\": \\\"https://example.com\\\",\\n\" +\n\t\t\t\t\"\t\\\"email\\\": \\\"user1@example.com\\\",\\n\" +\n\t\t\t\t\"\t\\\"email_verified\\\": \\\"true\\\",\\n\" +\n\t\t\t\t\"\t\\\"gender\\\": \\\"female\\\",\\n\" +\n\t\t\t\t\"\t\\\"birthdate\\\": \\\"1970-01-01\\\",\\n\" +\n\t\t\t\t\"\t\\\"zoneinfo\\\": \\\"Europe/Paris\\\",\\n\" +\n\t\t\t\t\"\t\\\"locale\\\": \\\"en-US\\\",\\n\" +\n\t\t\t\t\"\t\\\"phone_number\\\": \\\"+1 (604) 555-1234;ext=5678\\\",\\n\" +\n\t\t\t\t\"\t\\\"phone_number_verified\\\": \\\"false\\\",\\n\" +\n\t\t\t\t\"\t\\\"address\\\": {\\n\" +\n\t\t\t\t\"\t\t\\\"formatted\\\": \\\"Champ de Mars\\\\n5 Av. Anatole France\\\\n75007 Paris\\\\nFrance\\\",\\n\" +\n\t\t\t\t\"\t\t\\\"street_address\\\": \\\"Champ de Mars\\\\n5 Av. Anatole France\\\",\\n\" +\n\t\t\t\t\"\t\t\\\"locality\\\": \\\"Paris\\\",\\n\" +\n\t\t\t\t\"\t\t\\\"postal_code\\\": \\\"75007\\\",\\n\" +\n\t\t\t\t\"\t\t\\\"country\\\": \\\"France\\\"\\n\" +\n\t\t\t\t\"\t},\\n\" +\n\t\t\t\t\"\t\\\"updated_at\\\": \\\"2020-12-10T20:57:47Z\\\"\\n\" +\n\t\t\t\t\"}\\n\";\n\t\t// @formatter:on\n\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(userInfoResponse.getBytes(), HttpStatus.OK);\n\t\tOidcUserInfo oidcUserInfo = this.messageConverter.readInternal(OidcUserInfo.class, response);\n\n\t\tassertThat(oidcUserInfo.getSubject()).isEqualTo(\"user1\");\n\t\tassertThat(oidcUserInfo.getFullName()).isEqualTo(\"First Last\");\n\t\tassertThat(oidcUserInfo.getGivenName()).isEqualTo(\"First\");\n\t\tassertThat(oidcUserInfo.getFamilyName()).isEqualTo(\"Last\");\n\t\tassertThat(oidcUserInfo.getMiddleName()).isEqualTo(\"Middle\");\n\t\tassertThat(oidcUserInfo.getNickName()).isEqualTo(\"User\");\n\t\tassertThat(oidcUserInfo.getPreferredUsername()).isEqualTo(\"user\");\n\t\tassertThat(oidcUserInfo.getProfile()).isEqualTo(\"https://example.com/user1\");\n\t\tassertThat(oidcUserInfo.getPicture()).isEqualTo(\"https://example.com/user1.jpg\");\n\t\tassertThat(oidcUserInfo.getWebsite()).isEqualTo(\"https://example.com\");\n\t\tassertThat(oidcUserInfo.getEmail()).isEqualTo(\"user1@example.com\");\n\t\tassertThat(oidcUserInfo.getEmailVerified()).isTrue();\n\t\tassertThat(oidcUserInfo.getGender()).isEqualTo(\"female\");\n\t\tassertThat(oidcUserInfo.getBirthdate()).isEqualTo(\"1970-01-01\");\n\t\tassertThat(oidcUserInfo.getZoneInfo()).isEqualTo(\"Europe/Paris\");\n\t\tassertThat(oidcUserInfo.getLocale()).isEqualTo(\"en-US\");\n\t\tassertThat(oidcUserInfo.getPhoneNumber()).isEqualTo(\"+1 (604) 555-1234;ext=5678\");\n\t\tassertThat(oidcUserInfo.getPhoneNumberVerified()).isFalse();\n\t\tassertThat(oidcUserInfo.getAddress().getFormatted())\n\t\t\t.isEqualTo(\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\");\n\t\tassertThat(oidcUserInfo.getAddress().getStreetAddress()).isEqualTo(\"Champ de Mars\\n5 Av. Anatole France\");\n\t\tassertThat(oidcUserInfo.getAddress().getLocality()).isEqualTo(\"Paris\");\n\t\tassertThat(oidcUserInfo.getAddress().getPostalCode()).isEqualTo(\"75007\");\n\t\tassertThat(oidcUserInfo.getAddress().getCountry()).isEqualTo(\"France\");\n\t\tassertThat(oidcUserInfo.getUpdatedAt()).isEqualTo(Instant.ofEpochSecond(1607633867));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenFailingConverterThenThrowException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tthis.messageConverter.setUserInfoConverter((source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t});\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(\"{}\".getBytes(), HttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OidcUserInfo.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the UserInfo response\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n\t@Test\n\tpublic void readInternalWhenInvalidResponseThenThrowException() {\n\t\tString userInfoResponse = \"{}\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(userInfoResponse.getBytes(), HttpStatus.OK);\n\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OidcUserInfo.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the UserInfo response\")\n\t\t\t.withMessageContaining(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenOidcUserInfoThenSuccess() {\n\t\tOidcUserInfo userInfo = createUserInfo();\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tthis.messageConverter.writeInternal(userInfo, outputMessage);\n\n\t\tString userInfoResponse = outputMessage.getBodyAsString();\n\t\tassertThat(userInfoResponse).contains(\"\\\"sub\\\":\\\"user1\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"name\\\":\\\"First Last\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"given_name\\\":\\\"First\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"family_name\\\":\\\"Last\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"middle_name\\\":\\\"Middle\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"nickname\\\":\\\"User\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"preferred_username\\\":\\\"user\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"profile\\\":\\\"https://example.com/user1\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"picture\\\":\\\"https://example.com/user1.jpg\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"website\\\":\\\"https://example.com\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"email\\\":\\\"user1@example.com\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"email_verified\\\":true\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"gender\\\":\\\"female\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"birthdate\\\":\\\"1970-01-01\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"zoneinfo\\\":\\\"Europe/Paris\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"locale\\\":\\\"en-US\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"phone_number\\\":\\\"+1 (604) 555-1234;ext=5678\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"phone_number_verified\\\":false\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"address\\\":\");\n\t\tassertThat(userInfoResponse)\n\t\t\t.contains(\"\\\"formatted\\\":\\\"Champ de Mars\\\\n5 Av. Anatole France\\\\n75007 Paris\\\\nFrance\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"updated_at\\\":\\\"2020-12-10T20:57:47Z\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"custom_claim\\\":\\\"value\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"custom_collection_claim\\\":[\\\"value1\\\",\\\"value2\\\"]\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenWriteFailsThenThrowsException() {\n\t\tString errorMessage = \"this is not a valid converter\";\n\t\tConverter<OidcUserInfo, Map<String, Object>> failingConverter = (source) -> {\n\t\t\tthrow new RuntimeException(errorMessage);\n\t\t};\n\t\tthis.messageConverter.setUserInfoParametersConverter(failingConverter);\n\n\t\tOidcUserInfo userInfo = createUserInfo();\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.writeInternal(userInfo, outputMessage))\n\t\t\t.withMessageContaining(\"An error occurred writing the UserInfo response\")\n\t\t\t.withMessageContaining(errorMessage);\n\t}\n\n\tprivate static OidcUserInfo createUserInfo() {\n\t\treturn OidcUserInfo.builder()\n\t\t\t.subject(\"user1\")\n\t\t\t.name(\"First Last\")\n\t\t\t.givenName(\"First\")\n\t\t\t.familyName(\"Last\")\n\t\t\t.middleName(\"Middle\")\n\t\t\t.nickname(\"User\")\n\t\t\t.preferredUsername(\"user\")\n\t\t\t.profile(\"https://example.com/user1\")\n\t\t\t.picture(\"https://example.com/user1.jpg\")\n\t\t\t.website(\"https://example.com\")\n\t\t\t.email(\"user1@example.com\")\n\t\t\t.emailVerified(true)\n\t\t\t.gender(\"female\")\n\t\t\t.birthdate(\"1970-01-01\")\n\t\t\t.zoneinfo(\"Europe/Paris\")\n\t\t\t.locale(\"en-US\")\n\t\t\t.phoneNumber(\"+1 (604) 555-1234;ext=5678\")\n\t\t\t.claim(\"phone_number_verified\", false)\n\t\t\t.claim(\"address\",\n\t\t\t\t\tCollections.singletonMap(\"formatted\", \"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\"))\n\t\t\t.claim(StandardClaimNames.UPDATED_AT, Instant.ofEpochSecond(1607633867))\n\t\t\t.claim(\"custom_claim\", \"value\")\n\t\t\t.claim(\"custom_collection_claim\", Arrays.asList(\"value1\", \"value2\"))\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.util.Collections;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpRequest;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.TestJwsHeaders;\nimport org.springframework.security.oauth2.jwt.TestJwtClaimsSets;\nimport org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcClientRegistrationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OidcClientRegistrationEndpointFilter}.\n *\n * @author Ovidiu Popa\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n */\npublic class OidcClientRegistrationEndpointFilterTests {\n\n\tprivate static final String DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI = \"/connect/register\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OidcClientRegistrationEndpointFilter filter;\n\n\tprivate final HttpMessageConverter<OidcClientRegistration> clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter();\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OidcClientRegistrationEndpointFilter(this.authenticationManager);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OidcClientRegistrationEndpointFilter(null))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcClientRegistrationEndpointFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"clientRegistrationEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotClientRegistrationRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestGetThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestInvalidThenInvalidRequestError() throws Exception {\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setContent(\"invalid content\".getBytes());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\tassertThat(error.getDescription()).startsWith(\"OpenID Client Registration Error: \");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestInvalidTokenThenUnauthorizedError() throws Exception {\n\t\tdoFilterWhenClientRegistrationRequestInvalidThenError(OAuth2ErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestInsufficientTokenScopeThenForbiddenError() throws Exception {\n\t\tdoFilterWhenClientRegistrationRequestInvalidThenError(OAuth2ErrorCodes.INSUFFICIENT_SCOPE,\n\t\t\t\tHttpStatus.FORBIDDEN);\n\t}\n\n\tprivate void doFilterWhenClientRegistrationRequestInvalidThenError(String errorCode, HttpStatus status)\n\t\t\tthrows Exception {\n\t\tJwt jwt = createJwt(\"client.create\");\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(new OAuth2AuthenticationException(errorCode));\n\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistrationRequest = OidcClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\twriteClientRegistrationRequest(request, clientRegistrationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(status.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestValidThenSuccessResponse() throws Exception {\n\t\t// @formatter:off\n\t\tOidcClientRegistration expectedClientRegistrationResponse = createClientRegistration();\n\n\t\tOidcClientRegistration clientRegistrationRequest = OidcClientRegistration.builder()\n\t\t\t\t.clientName(expectedClientRegistrationResponse.getClientName())\n\t\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(expectedClientRegistrationResponse.getRedirectUris()))\n\t\t\t\t.grantTypes((grantTypes) -> grantTypes.addAll(expectedClientRegistrationResponse.getGrantTypes()))\n\t\t\t\t.scopes((scopes) -> scopes.addAll(expectedClientRegistrationResponse.getScopes()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt jwt = createJwt(\"client.create\");\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\n\t\tOidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, expectedClientRegistrationResponse);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(clientRegistrationAuthenticationResult);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\twriteClientRegistrationRequest(request, clientRegistrationRequest);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value());\n\t\tOidcClientRegistration clientRegistrationResponse = readClientRegistrationResponse(response);\n\t\tassertThat(clientRegistrationResponse.getClientId())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientId());\n\t\tassertThat(clientRegistrationResponse.getClientIdIssuedAt()).isBetween(\n\t\t\t\texpectedClientRegistrationResponse.getClientIdIssuedAt().minusSeconds(1),\n\t\t\t\texpectedClientRegistrationResponse.getClientIdIssuedAt().plusSeconds(1));\n\t\tassertThat(clientRegistrationResponse.getClientSecret())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientSecret());\n\t\tassertThat(clientRegistrationResponse.getClientSecretExpiresAt())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientSecretExpiresAt());\n\t\tassertThat(clientRegistrationResponse.getClientName())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientName());\n\t\tassertThat(clientRegistrationResponse.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getRedirectUris());\n\t\tassertThat(clientRegistrationResponse.getGrantTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getGrantTypes());\n\t\tassertThat(clientRegistrationResponse.getResponseTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getResponseTypes());\n\t\tassertThat(clientRegistrationResponse.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getScopes());\n\t\tassertThat(clientRegistrationResponse.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod());\n\t\tassertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getIdTokenSignedResponseAlgorithm());\n\t\tassertThat(clientRegistrationResponse.getRegistrationAccessToken())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getRegistrationAccessToken());\n\t\tassertThat(clientRegistrationResponse.getRegistrationClientUrl())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientConfigurationRequestPutThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"PUT\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientConfigurationRequestMissingClientIdThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientConfigurationRequestEmptyClientIdThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"\");\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientConfigurationRequestMultipleClientIdThenInvalidRequestError() throws Exception {\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-id\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-id2\");\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientConfigurationRequestInvalidTokenThenUnauthorizedError() throws Exception {\n\t\tdoFilterWhenClientConfigurationRequestInvalidThenError(OAuth2ErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientConfigurationRequestInsufficientScopeThenForbiddenError() throws Exception {\n\t\tdoFilterWhenClientConfigurationRequestInvalidThenError(OAuth2ErrorCodes.INSUFFICIENT_SCOPE,\n\t\t\t\tHttpStatus.FORBIDDEN);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientConfigurationRequestInvalidClientThenUnauthorizedError() throws Exception {\n\t\tdoFilterWhenClientConfigurationRequestInvalidThenError(OAuth2ErrorCodes.INVALID_CLIENT,\n\t\t\t\tHttpStatus.UNAUTHORIZED);\n\t}\n\n\tprivate void doFilterWhenClientConfigurationRequestInvalidThenError(String errorCode, HttpStatus status)\n\t\t\tthrows Exception {\n\t\tJwt jwt = createJwt(\"client.read\");\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.read\"));\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(new OAuth2AuthenticationException(errorCode));\n\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setParameter(OAuth2ParameterNames.CLIENT_ID, \"client1\");\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(status.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientConfigurationRequestValidThenSuccessResponse() throws Exception {\n\t\tOidcClientRegistration expectedClientRegistrationResponse = createClientRegistration();\n\n\t\tJwt jwt = createJwt(\"client.read\");\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.read\"));\n\n\t\tOidcClientRegistrationAuthenticationToken clientConfigurationAuthenticationResult = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, expectedClientRegistrationResponse);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(clientConfigurationAuthenticationResult);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setParameter(OAuth2ParameterNames.CLIENT_ID, expectedClientRegistrationResponse.getClientId());\n\t\tupdateQueryString(request);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tOidcClientRegistration clientRegistrationResponse = readClientRegistrationResponse(response);\n\t\tassertThat(clientRegistrationResponse.getClientId())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientId());\n\t\tassertThat(clientRegistrationResponse.getClientIdIssuedAt()).isBetween(\n\t\t\t\texpectedClientRegistrationResponse.getClientIdIssuedAt().minusSeconds(1),\n\t\t\t\texpectedClientRegistrationResponse.getClientIdIssuedAt().plusSeconds(1));\n\t\tassertThat(clientRegistrationResponse.getClientSecret())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientSecret());\n\t\tassertThat(clientRegistrationResponse.getClientSecretExpiresAt())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientSecretExpiresAt());\n\t\tassertThat(clientRegistrationResponse.getClientName())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientName());\n\t\tassertThat(clientRegistrationResponse.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getRedirectUris());\n\t\tassertThat(clientRegistrationResponse.getGrantTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getGrantTypes());\n\t\tassertThat(clientRegistrationResponse.getResponseTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getResponseTypes());\n\t\tassertThat(clientRegistrationResponse.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getScopes());\n\t\tassertThat(clientRegistrationResponse.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod());\n\t\tassertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getIdTokenSignedResponseAlgorithm());\n\t\tassertThat(clientRegistrationResponse.getRegistrationClientUrl())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws ServletException, IOException {\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setParameter(OAuth2ParameterNames.CLIENT_ID, \"client-id\");\n\t\tupdateQueryString(request);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationConverter).convert(request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tOidcClientRegistration expectedClientRegistrationResponse = createClientRegistration();\n\t\tAuthentication principal = new TestingAuthenticationToken(\"principal\", \"Credentials\");\n\n\t\tOidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = new OidcClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, expectedClientRegistrationResponse);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(clientRegistrationAuthenticationResult);\n\t\tAuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(successHandler);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setParameter(OAuth2ParameterNames.CLIENT_ID, expectedClientRegistrationResponse.getClientId());\n\t\tupdateQueryString(request);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(successHandler).onAuthenticationSuccess(request, response, clientRegistrationAuthenticationResult);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN));\n\n\t\tString requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setParameter(OAuth2ParameterNames.CLIENT_ID, \"client1\");\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(eq(request), eq(response),\n\t\t\t\tany(OAuth2AuthenticationException.class));\n\t}\n\n\tprivate static void updateQueryString(MockHttpServletRequest request) {\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(request.getRequestURI());\n\t\trequest.getParameterMap().forEach((key, values) -> {\n\t\t\tif (values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\turiBuilder.queryParam(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\trequest.setQueryString(uriBuilder.build().getQuery());\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n\tprivate void writeClientRegistrationRequest(MockHttpServletRequest request,\n\t\t\tOidcClientRegistration clientRegistration) throws Exception {\n\t\tMockClientHttpRequest httpRequest = new MockClientHttpRequest();\n\t\tthis.clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpRequest);\n\t\trequest.setContent(httpRequest.getBodyAsBytes());\n\t}\n\n\tprivate OidcClientRegistration readClientRegistrationResponse(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class, httpResponse);\n\t}\n\n\tprivate static OidcClientRegistration createClientRegistration() {\n\t\t// @formatter:off\n\t\tOidcClientRegistration clientRegistration = OidcClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t\t.responseType(OAuth2AuthorizationResponseType.CODE.getValue())\n\t\t\t\t.idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.registrationClientUrl(\"https://auth-server:9000/connect/register?client_id=client-id\")\n\t\t\t\t.build();\n\t\treturn clientRegistration;\n\t\t// @formatter:on\n\t}\n\n\tprivate static Jwt createJwt(String scope) {\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = TestJwsHeaders.jwsHeader()\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet()\n\t\t\t\t.claim(OAuth2ParameterNames.SCOPE, Collections.singleton(scope))\n\t\t\t\t.build();\n\t\tJwt jwt = Jwt.withTokenValue(\"jwt-access-token\")\n\t\t\t\t.headers((headers) -> headers.putAll(jwsHeader.getHeaders()))\n\t\t\t\t.claims((claims) -> claims.putAll(jwtClaimsSet.getClaims()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\treturn jwt;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web;\n\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationToken;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OidcLogoutEndpointFilter}.\n *\n * @author Joe Grandja\n */\npublic class OidcLogoutEndpointFilterTests {\n\n\tprivate static final String DEFAULT_OIDC_LOGOUT_ENDPOINT_URI = \"/connect/logout\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OidcLogoutEndpointFilter filter;\n\n\tprivate TestingAuthenticationToken principal;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OidcLogoutEndpointFilter(this.authenticationManager);\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tthis.principal.setAuthenticated(true);\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new OidcLogoutEndpointFilter(null))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenLogoutEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OidcLogoutEndpointFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"logoutEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotLogoutRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenLogoutRequestMissingIdTokenHintThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenRequestInvalidParameterThenError(\n\t\t\t\tcreateLogoutRequest(TestRegisteredClients.registeredClient().build()), \"id_token_hint\",\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> request.removeParameter(\"id_token_hint\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenLogoutRequestMultipleIdTokenHintThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenRequestInvalidParameterThenError(\n\t\t\t\tcreateLogoutRequest(TestRegisteredClients.registeredClient().build()), \"id_token_hint\",\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> request.addParameter(\"id_token_hint\", \"id-token-2\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenLogoutRequestMultipleClientIdThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenRequestInvalidParameterThenError(\n\t\t\t\tcreateLogoutRequest(TestRegisteredClients.registeredClient().build()), OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-2\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenLogoutRequestMultiplePostLogoutRedirectUriThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenRequestInvalidParameterThenError(\n\t\t\t\tcreateLogoutRequest(TestRegisteredClients.registeredClient().build()), \"post_logout_redirect_uri\",\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(\"post_logout_redirect_uri\", \"https://example.com/callback-4\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenLogoutRequestMultipleStateThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenRequestInvalidParameterThenError(\n\t\t\t\tcreateLogoutRequest(TestRegisteredClients.registeredClient().build()), OAuth2ParameterNames.STATE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.STATE, \"state-2\"));\n\t}\n\n\tprivate void doFilterWhenRequestInvalidParameterThenError(MockHttpServletRequest request, String parameterName,\n\t\t\tString errorCode, Consumer<MockHttpServletRequest> requestConsumer) throws Exception {\n\n\t\trequestConsumer.accept(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tassertThat(response.getErrorMessage())\n\t\t\t.isEqualTo(\"[\" + errorCode + \"] OpenID Connect 1.0 Logout Request Parameter: \" + parameterName);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenLogoutRequestAuthenticationExceptionThenErrorResponse() throws Exception {\n\t\tOAuth2Error error = new OAuth2Error(\"errorCode\", \"errorDescription\", \"errorUri\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(new OAuth2AuthenticationException(error));\n\n\t\tMockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tassertThat(response.getErrorMessage()).isEqualTo(error.toString());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.principal);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(\"id-token\", this.principal,\n\t\t\t\tnull, null, null, null);\n\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any())).willReturn((authentication));\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn((authentication));\n\n\t\tMockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationConverter).convert(any());\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(\"id-token\", this.principal,\n\t\t\t\tnull, null, null, null);\n\n\t\tAuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn((authentication));\n\n\t\tMockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), same(authentication));\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willThrow(new AuthenticationServiceException(\"AuthenticationServiceException\"));\n\n\t\tMockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tArgumentCaptor<AuthenticationException> authenticationExceptionCaptor = ArgumentCaptor\n\t\t\t.forClass(AuthenticationException.class);\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(),\n\t\t\t\tauthenticationExceptionCaptor.capture());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(authenticationExceptionCaptor.getValue()).isInstanceOf(OAuth2AuthenticationException.class)\n\t\t\t.extracting((ex) -> ((OAuth2AuthenticationException) ex).getError())\n\t\t\t.satisfies((error) -> {\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\t\tassertThat(error.getDescription()).contains(\"AuthenticationServiceException\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenLogoutRequestAuthenticatedThenLogout() throws Exception {\n\t\tMockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(true);\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(\"id-token\", this.principal,\n\t\t\t\tsession.getId(), null, null, null);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn((authentication));\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/\");\n\t\tassertThat(session.isInvalid()).isTrue();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenLogoutRequestAuthenticatedWithPostLogoutRedirectUriThenPostLogoutRedirect()\n\t\t\tthrows Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tMockHttpServletRequest request = createLogoutRequest(registeredClient);\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(true);\n\n\t\tString postLogoutRedirectUri = registeredClient.getPostLogoutRedirectUris().iterator().next();\n\t\tString state = \"state-1\";\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(\"id-token\", this.principal,\n\t\t\t\tsession.getId(), registeredClient.getClientId(), postLogoutRedirectUri, state);\n\t\tauthentication.setAuthenticated(true);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn((authentication));\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(postLogoutRedirectUri + \"?state=\" + state);\n\t\tassertThat(session.isInvalid()).isTrue();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\tprivate static MockHttpServletRequest createLogoutRequest(RegisteredClient registeredClient) {\n\t\tString requestUri = DEFAULT_OIDC_LOGOUT_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\n\t\trequest.addParameter(\"id_token_hint\", \"id-token\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\trequest.addParameter(\"post_logout_redirect_uri\",\n\t\t\t\tregisteredClient.getPostLogoutRedirectUris().iterator().next());\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"state\");\n\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.web.util.InvalidUrlException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OidcProviderConfigurationEndpointFilter}.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n */\npublic class OidcProviderConfigurationEndpointFilterTests {\n\n\tprivate static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = \"/.well-known/openid-configuration\";\n\n\tprivate final OidcProviderConfigurationEndpointFilter filter = new OidcProviderConfigurationEndpointFilter();\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void setProviderConfigurationCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setProviderConfigurationCustomizer(null))\n\t\t\t.withMessage(\"providerConfigurationCustomizer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotConfigurationRequestThenNotProcessed() throws Exception {\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));\n\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenConfigurationRequestPostThenNotProcessed() throws Exception {\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));\n\n\t\tString requestUri = DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenConfigurationRequestThenConfigurationResponse() throws Exception {\n\t\tString issuer = \"https://example.com\";\n\t\tString authorizationEndpoint = \"/oauth2/v1/authorize\";\n\t\tString pushedAuthorizationRequestEndpoint = \"/oauth2/v1/par\";\n\t\tString tokenEndpoint = \"/oauth2/v1/token\";\n\t\tString jwkSetEndpoint = \"/oauth2/v1/jwks\";\n\t\tString userInfoEndpoint = \"/userinfo\";\n\t\tString logoutEndpoint = \"/connect/logout\";\n\t\tString tokenRevocationEndpoint = \"/oauth2/v1/revoke\";\n\t\tString tokenIntrospectionEndpoint = \"/oauth2/v1/introspect\";\n\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(issuer)\n\t\t\t.authorizationEndpoint(authorizationEndpoint)\n\t\t\t.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint)\n\t\t\t.tokenEndpoint(tokenEndpoint)\n\t\t\t.jwkSetEndpoint(jwkSetEndpoint)\n\t\t\t.oidcUserInfoEndpoint(userInfoEndpoint)\n\t\t\t.oidcLogoutEndpoint(logoutEndpoint)\n\t\t\t.tokenRevocationEndpoint(tokenRevocationEndpoint)\n\t\t\t.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint)\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\n\t\tString requestUri = DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tString providerConfigurationResponse = response.getContentAsString();\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"issuer\\\":\\\"https://example.com\\\"\");\n\t\tassertThat(providerConfigurationResponse)\n\t\t\t.contains(\"\\\"authorization_endpoint\\\":\\\"https://example.com/oauth2/v1/authorize\\\"\");\n\t\tassertThat(providerConfigurationResponse).doesNotContain(\"\\\"pushed_authorization_request_endpoint\\\"\");\n\t\tassertThat(providerConfigurationResponse).doesNotContain(\"\\\"device_authorization_endpoint\\\"\");\n\t\tassertThat(providerConfigurationResponse)\n\t\t\t.contains(\"\\\"token_endpoint\\\":\\\"https://example.com/oauth2/v1/token\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"jwks_uri\\\":\\\"https://example.com/oauth2/v1/jwks\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"scopes_supported\\\":[\\\"openid\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"response_types_supported\\\":[\\\"code\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\n\t\t\t\t\"\\\"grant_types_supported\\\":[\\\"authorization_code\\\",\\\"client_credentials\\\",\\\"refresh_token\\\",\\\"urn:ietf:params:oauth:grant-type:token-exchange\\\"]\");\n\t\tassertThat(providerConfigurationResponse)\n\t\t\t.contains(\"\\\"revocation_endpoint\\\":\\\"https://example.com/oauth2/v1/revoke\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\n\t\t\t\t\"\\\"revocation_endpoint_auth_methods_supported\\\":[\\\"client_secret_basic\\\",\\\"client_secret_post\\\",\\\"client_secret_jwt\\\",\\\"private_key_jwt\\\",\\\"tls_client_auth\\\",\\\"self_signed_tls_client_auth\\\"]\");\n\t\tassertThat(providerConfigurationResponse)\n\t\t\t.contains(\"\\\"introspection_endpoint\\\":\\\"https://example.com/oauth2/v1/introspect\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\n\t\t\t\t\"\\\"introspection_endpoint_auth_methods_supported\\\":[\\\"client_secret_basic\\\",\\\"client_secret_post\\\",\\\"client_secret_jwt\\\",\\\"private_key_jwt\\\",\\\"tls_client_auth\\\",\\\"self_signed_tls_client_auth\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"code_challenge_methods_supported\\\":[\\\"S256\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"tls_client_certificate_bound_access_tokens\\\":true\");\n\t\tassertThat(providerConfigurationResponse).contains(\n\t\t\t\t\"\\\"dpop_signing_alg_values_supported\\\":[\\\"RS256\\\",\\\"RS384\\\",\\\"RS512\\\",\\\"PS256\\\",\\\"PS384\\\",\\\"PS512\\\",\\\"ES256\\\",\\\"ES384\\\",\\\"ES512\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"subject_types_supported\\\":[\\\"public\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"id_token_signing_alg_values_supported\\\":[\\\"RS256\\\"]\");\n\t\tassertThat(providerConfigurationResponse).contains(\"\\\"userinfo_endpoint\\\":\\\"https://example.com/userinfo\\\"\");\n\t\tassertThat(providerConfigurationResponse)\n\t\t\t.contains(\"\\\"end_session_endpoint\\\":\\\"https://example.com/connect/logout\\\"\");\n\t\tassertThat(providerConfigurationResponse).contains(\n\t\t\t\t\"\\\"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\\\"]\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationServerSettingsWithInvalidIssuerThenThrowIllegalArgumentException() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://this is an invalid URL\")\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\n\t\tString requestUri = DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tassertThatExceptionOfType(InvalidUrlException.class)\n\t\t\t.isThrownBy(() -> this.filter.doFilter(request, response, filterChain));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web;\n\nimport java.time.Instant;\nimport java.util.Collections;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OidcUserInfoEndpointFilter}.\n *\n * @author Steve Riesenberg\n */\npublic class OidcUserInfoEndpointFilterTests {\n\n\tprivate static final String DEFAULT_OIDC_USER_INFO_ENDPOINT_URI = \"/userinfo\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OidcUserInfoEndpointFilter filter;\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OidcUserInfoEndpointFilter(this.authenticationManager, DEFAULT_OIDC_USER_INFO_ENDPOINT_URI);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OidcUserInfoEndpointFilter(null))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenUserInfoEndpointUriIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcUserInfoEndpointFilter(this.authenticationManager, \"\"))\n\t\t\t.withMessage(\"userInfoEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotUserInfoRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(request, response);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUserInfoRequestPutThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"PUT\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(this.authenticationManager);\n\t\tverify(filterChain).doFilter(request, response);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUserInfoRequestGetThenSuccess() throws Exception {\n\t\tdoFilterWhenUserInfoRequestThenSuccess(\"GET\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUserInfoRequestPostThenSuccess() throws Exception {\n\t\tdoFilterWhenUserInfoRequestThenSuccess(\"POST\");\n\t}\n\n\tprivate void doFilterWhenUserInfoRequestThenSuccess(String httpMethod) throws Exception {\n\t\tJwtAuthenticationToken principal = createJwtAuthenticationToken();\n\t\tSecurityContextHolder.getContext().setAuthentication(principal);\n\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal,\n\t\t\t\tcreateUserInfo());\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\n\t\tString requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(httpMethod, requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertUserInfoResponse(response.getContentAsString());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUserInfoRequestInvalidTokenThenUnauthorizedError() throws Exception {\n\t\tdoFilterWhenAuthenticationExceptionThenError(OAuth2ErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUserInfoRequestInsufficientScopeThenForbiddenError() throws Exception {\n\t\tdoFilterWhenAuthenticationExceptionThenError(OAuth2ErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN);\n\t}\n\n\tprivate void doFilterWhenAuthenticationExceptionThenError(String oauth2ErrorCode, HttpStatus httpStatus)\n\t\t\tthrows Exception {\n\t\tAuthentication principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tSecurityContextHolder.getContext().setAuthentication(principal);\n\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willThrow(new OAuth2AuthenticationException(oauth2ErrorCode));\n\n\t\tString requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(httpStatus.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(oauth2ErrorCode);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {\n\t\tAuthentication principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal);\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tgiven(authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(new OidcUserInfoAuthenticationToken(principal, createUserInfo()));\n\n\t\tString requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(authenticationConverter).convert(request);\n\t\tverify(this.authenticationManager).authenticate(authentication);\n\t\tassertUserInfoResponse(response.getContentAsString());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tAuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(successHandler);\n\n\t\tAuthentication principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tSecurityContextHolder.getContext().setAuthentication(principal);\n\n\t\tOidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal,\n\t\t\t\tcreateUserInfo());\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\n\t\tString requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(successHandler).onAuthenticationSuccess(request, response, authentication);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tAuthenticationFailureHandler failureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(failureHandler);\n\n\t\tAuthentication principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tSecurityContextHolder.getContext().setAuthentication(principal);\n\n\t\tOAuth2AuthenticationException authenticationException = new OAuth2AuthenticationException(\n\t\t\t\tOAuth2ErrorCodes.INVALID_TOKEN);\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(authenticationException);\n\n\t\tString requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(failureHandler).onAuthenticationFailure(request, response, authenticationException);\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n\tprivate JwtAuthenticationToken createJwtAuthenticationToken() {\n\t\tInstant now = Instant.now();\n\t\t// @formatter:off\n\t\tJwt jwt = Jwt.withTokenValue(\"token\")\n\t\t\t\t.header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName())\n\t\t\t\t.issuedAt(now)\n\t\t\t\t.expiresAt(now.plusSeconds(300))\n\t\t\t\t.claim(StandardClaimNames.SUB, \"user\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\treturn new JwtAuthenticationToken(jwt, Collections.emptyList());\n\t}\n\n\tprivate static OidcUserInfo createUserInfo() {\n\t\treturn OidcUserInfo.builder()\n\t\t\t.subject(\"user1\")\n\t\t\t.name(\"First Last\")\n\t\t\t.givenName(\"First\")\n\t\t\t.familyName(\"Last\")\n\t\t\t.middleName(\"Middle\")\n\t\t\t.nickname(\"User\")\n\t\t\t.preferredUsername(\"user\")\n\t\t\t.profile(\"https://example.com/user1\")\n\t\t\t.picture(\"https://example.com/user1.jpg\")\n\t\t\t.website(\"https://example.com\")\n\t\t\t.email(\"user1@example.com\")\n\t\t\t.emailVerified(true)\n\t\t\t.gender(\"female\")\n\t\t\t.birthdate(\"1970-01-01\")\n\t\t\t.zoneinfo(\"Europe/Paris\")\n\t\t\t.locale(\"en-US\")\n\t\t\t.phoneNumber(\"+1 (604) 555-1234;ext=5678\")\n\t\t\t.phoneNumberVerified(false)\n\t\t\t.address(\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\")\n\t\t\t.updatedAt(\"1970-01-01T00:00:00Z\")\n\t\t\t.build();\n\t}\n\n\tprivate static void assertUserInfoResponse(String userInfoResponse) {\n\t\tassertThat(userInfoResponse).contains(\"\\\"sub\\\":\\\"user1\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"name\\\":\\\"First Last\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"given_name\\\":\\\"First\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"family_name\\\":\\\"Last\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"middle_name\\\":\\\"Middle\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"nickname\\\":\\\"User\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"preferred_username\\\":\\\"user\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"profile\\\":\\\"https://example.com/user1\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"picture\\\":\\\"https://example.com/user1.jpg\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"website\\\":\\\"https://example.com\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"email\\\":\\\"user1@example.com\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"email_verified\\\":true\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"gender\\\":\\\"female\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"birthdate\\\":\\\"1970-01-01\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"zoneinfo\\\":\\\"Europe/Paris\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"locale\\\":\\\"en-US\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"phone_number\\\":\\\"+1 (604) 555-1234;ext=5678\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"phone_number_verified\\\":false\");\n\t\tassertThat(userInfoResponse)\n\t\t\t.contains(\"\\\"address\\\":\\\"Champ de Mars\\\\n5 Av. Anatole France\\\\n75007 Paris\\\\nFrance\\\"\");\n\t\tassertThat(userInfoResponse).contains(\"\\\"updated_at\\\":\\\"1970-01-01T00:00:00Z\\\"\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcLogoutAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.oidc.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcLogoutAuthenticationToken;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OidcLogoutAuthenticationSuccessHandler}.\n *\n * @author Joe Grandja\n */\npublic class OidcLogoutAuthenticationSuccessHandlerTests {\n\n\tprivate TestingAuthenticationToken principal;\n\n\tprivate final OidcLogoutAuthenticationSuccessHandler authenticationSuccessHandler = new OidcLogoutAuthenticationSuccessHandler();\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"credentials\");\n\t\tthis.principal.setAuthenticated(true);\n\t}\n\n\t@Test\n\tpublic void setLogoutHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authenticationSuccessHandler.setLogoutHandler(null))\n\t\t\t\t.withMessage(\"logoutHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessWhenInvalidAuthenticationTypeThenThrowOAuth2AuthenticationException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, this.principal))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessWhenLogoutHandlerSetThenUsed() throws Exception {\n\t\tLogoutHandler logoutHandler = mock(LogoutHandler.class);\n\t\tthis.authenticationSuccessHandler.setLogoutHandler(logoutHandler);\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(true);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\n\t\tOidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(\"id-token\", this.principal,\n\t\t\t\tsession.getId(), null, null, null);\n\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);\n\n\t\tverify(logoutHandler).logout(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(Authentication.class));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettingsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link AuthorizationServerSettings}.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n */\npublic class AuthorizationServerSettingsTests {\n\n\t@Test\n\tpublic void buildWhenDefaultThenDefaultsAreSet() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\n\t\tassertThat(authorizationServerSettings.getIssuer()).isNull();\n\t\tassertThat(authorizationServerSettings.isMultipleIssuersAllowed()).isFalse();\n\t\tassertThat(authorizationServerSettings.getAuthorizationEndpoint()).isEqualTo(\"/oauth2/authorize\");\n\t\tassertThat(authorizationServerSettings.getPushedAuthorizationRequestEndpoint()).isEqualTo(\"/oauth2/par\");\n\t\tassertThat(authorizationServerSettings.getTokenEndpoint()).isEqualTo(\"/oauth2/token\");\n\t\tassertThat(authorizationServerSettings.getJwkSetEndpoint()).isEqualTo(\"/oauth2/jwks\");\n\t\tassertThat(authorizationServerSettings.getTokenRevocationEndpoint()).isEqualTo(\"/oauth2/revoke\");\n\t\tassertThat(authorizationServerSettings.getTokenIntrospectionEndpoint()).isEqualTo(\"/oauth2/introspect\");\n\t\tassertThat(authorizationServerSettings.getClientRegistrationEndpoint()).isEqualTo(\"/oauth2/register\");\n\t\tassertThat(authorizationServerSettings.getOidcClientRegistrationEndpoint()).isEqualTo(\"/connect/register\");\n\t\tassertThat(authorizationServerSettings.getOidcUserInfoEndpoint()).isEqualTo(\"/userinfo\");\n\t\tassertThat(authorizationServerSettings.getOidcLogoutEndpoint()).isEqualTo(\"/connect/logout\");\n\t}\n\n\t@Test\n\tpublic void buildWhenSettingsProvidedThenSet() {\n\t\tString authorizationEndpoint = \"/oauth2/v1/authorize\";\n\t\tString pushedAuthorizationRequestEndpoint = \"/oauth2/v1/par\";\n\t\tString tokenEndpoint = \"/oauth2/v1/token\";\n\t\tString jwkSetEndpoint = \"/oauth2/v1/jwks\";\n\t\tString tokenRevocationEndpoint = \"/oauth2/v1/revoke\";\n\t\tString tokenIntrospectionEndpoint = \"/oauth2/v1/introspect\";\n\t\tString clientRegistrationEndpoint = \"/oauth2/v1/register\";\n\t\tString oidcClientRegistrationEndpoint = \"/connect/v1/register\";\n\t\tString oidcUserInfoEndpoint = \"/connect/v1/userinfo\";\n\t\tString oidcLogoutEndpoint = \"/connect/v1/logout\";\n\t\tString issuer = \"https://example.com:9000\";\n\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(issuer)\n\t\t\t.authorizationEndpoint(authorizationEndpoint)\n\t\t\t.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint)\n\t\t\t.tokenEndpoint(tokenEndpoint)\n\t\t\t.jwkSetEndpoint(jwkSetEndpoint)\n\t\t\t.tokenRevocationEndpoint(tokenRevocationEndpoint)\n\t\t\t.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint)\n\t\t\t.tokenRevocationEndpoint(tokenRevocationEndpoint)\n\t\t\t.clientRegistrationEndpoint(clientRegistrationEndpoint)\n\t\t\t.oidcClientRegistrationEndpoint(oidcClientRegistrationEndpoint)\n\t\t\t.oidcUserInfoEndpoint(oidcUserInfoEndpoint)\n\t\t\t.oidcLogoutEndpoint(oidcLogoutEndpoint)\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerSettings.getIssuer()).isEqualTo(issuer);\n\t\tassertThat(authorizationServerSettings.isMultipleIssuersAllowed()).isFalse();\n\t\tassertThat(authorizationServerSettings.getAuthorizationEndpoint()).isEqualTo(authorizationEndpoint);\n\t\tassertThat(authorizationServerSettings.getPushedAuthorizationRequestEndpoint())\n\t\t\t.isEqualTo(pushedAuthorizationRequestEndpoint);\n\t\tassertThat(authorizationServerSettings.getTokenEndpoint()).isEqualTo(tokenEndpoint);\n\t\tassertThat(authorizationServerSettings.getJwkSetEndpoint()).isEqualTo(jwkSetEndpoint);\n\t\tassertThat(authorizationServerSettings.getTokenRevocationEndpoint()).isEqualTo(tokenRevocationEndpoint);\n\t\tassertThat(authorizationServerSettings.getTokenIntrospectionEndpoint()).isEqualTo(tokenIntrospectionEndpoint);\n\t\tassertThat(authorizationServerSettings.getClientRegistrationEndpoint()).isEqualTo(clientRegistrationEndpoint);\n\t\tassertThat(authorizationServerSettings.getOidcClientRegistrationEndpoint())\n\t\t\t.isEqualTo(oidcClientRegistrationEndpoint);\n\t\tassertThat(authorizationServerSettings.getOidcUserInfoEndpoint()).isEqualTo(oidcUserInfoEndpoint);\n\t\tassertThat(authorizationServerSettings.getOidcLogoutEndpoint()).isEqualTo(oidcLogoutEndpoint);\n\t}\n\n\t@Test\n\tpublic void buildWhenIssuerSetAndMultipleIssuersAllowedTrueThenThrowIllegalArgumentException() {\n\t\tString issuer = \"https://example.com:9000\";\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().issuer(issuer).multipleIssuersAllowed(true).build())\n\t\t\t.withMessage(\n\t\t\t\t\t\"The issuer identifier (\" + issuer + \") cannot be set when isMultipleIssuersAllowed() is true.\");\n\t}\n\n\t@Test\n\tpublic void buildWhenIssuerNotSetAndMultipleIssuersAllowedTrueThenDefaultsAreSet() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.multipleIssuersAllowed(true)\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerSettings.getIssuer()).isNull();\n\t\tassertThat(authorizationServerSettings.isMultipleIssuersAllowed()).isTrue();\n\t\tassertThat(authorizationServerSettings.getAuthorizationEndpoint()).isEqualTo(\"/oauth2/authorize\");\n\t\tassertThat(authorizationServerSettings.getPushedAuthorizationRequestEndpoint()).isEqualTo(\"/oauth2/par\");\n\t\tassertThat(authorizationServerSettings.getTokenEndpoint()).isEqualTo(\"/oauth2/token\");\n\t\tassertThat(authorizationServerSettings.getJwkSetEndpoint()).isEqualTo(\"/oauth2/jwks\");\n\t\tassertThat(authorizationServerSettings.getTokenRevocationEndpoint()).isEqualTo(\"/oauth2/revoke\");\n\t\tassertThat(authorizationServerSettings.getTokenIntrospectionEndpoint()).isEqualTo(\"/oauth2/introspect\");\n\t\tassertThat(authorizationServerSettings.getClientRegistrationEndpoint()).isEqualTo(\"/oauth2/register\");\n\t\tassertThat(authorizationServerSettings.getOidcClientRegistrationEndpoint()).isEqualTo(\"/connect/register\");\n\t\tassertThat(authorizationServerSettings.getOidcUserInfoEndpoint()).isEqualTo(\"/userinfo\");\n\t\tassertThat(authorizationServerSettings.getOidcLogoutEndpoint()).isEqualTo(\"/connect/logout\");\n\t}\n\n\t@Test\n\tpublic void settingWhenCustomThenSet() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.setting(\"name1\", \"value1\")\n\t\t\t.settings((settings) -> settings.put(\"name2\", \"value2\"))\n\t\t\t.build();\n\n\t\tassertThat(authorizationServerSettings.getSettings()).hasSize(15);\n\t\tassertThat(authorizationServerSettings.<String>getSetting(\"name1\")).isEqualTo(\"value1\");\n\t\tassertThat(authorizationServerSettings.<String>getSetting(\"name2\")).isEqualTo(\"value2\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AuthorizationServerSettings.builder().issuer(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizationEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().authorizationEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void pushedAuthorizationRequestEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().pushedAuthorizationRequestEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void tokenEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> AuthorizationServerSettings.builder().tokenEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void tokenRevocationEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().tokenRevocationEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void tokenIntrospectionEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().tokenIntrospectionEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void clientRegistrationEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().clientRegistrationEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void oidcClientRegistrationEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().oidcClientRegistrationEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void oidcUserInfoEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().oidcUserInfoEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void jwksEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().jwkSetEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void oidcLogoutEndpointWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> AuthorizationServerSettings.builder().oidcLogoutEndpoint(null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettingsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ClientSettings}.\n *\n * @author Joe Grandja\n */\npublic class ClientSettingsTests {\n\n\t@Test\n\tpublic void buildWhenDefaultThenDefaultsAreSet() {\n\t\tClientSettings clientSettings = ClientSettings.builder().build();\n\t\tassertThat(clientSettings.getSettings()).hasSize(2);\n\t\tassertThat(clientSettings.isRequireProofKey()).isTrue();\n\t\tassertThat(clientSettings.isRequireAuthorizationConsent()).isFalse();\n\t}\n\n\t@Test\n\tpublic void requireProofKeyWhenTrueThenSet() {\n\t\tClientSettings clientSettings = ClientSettings.builder().requireProofKey(true).build();\n\t\tassertThat(clientSettings.isRequireProofKey()).isTrue();\n\t}\n\n\t@Test\n\tpublic void requireAuthorizationConsentWhenTrueThenSet() {\n\t\tClientSettings clientSettings = ClientSettings.builder().requireAuthorizationConsent(true).build();\n\t\tassertThat(clientSettings.isRequireAuthorizationConsent()).isTrue();\n\t}\n\n\t@Test\n\tpublic void tokenEndpointAuthenticationSigningAlgorithmWhenHS256ThenSet() {\n\t\tClientSettings clientSettings = ClientSettings.builder()\n\t\t\t.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)\n\t\t\t.build();\n\t\tassertThat(clientSettings.getTokenEndpointAuthenticationSigningAlgorithm()).isEqualTo(MacAlgorithm.HS256);\n\t}\n\n\t@Test\n\tpublic void jwkSetUrlWhenProvidedThenSet() {\n\t\tClientSettings clientSettings = ClientSettings.builder().jwkSetUrl(\"https://client.example.com/jwks\").build();\n\t\tassertThat(clientSettings.getJwkSetUrl()).isEqualTo(\"https://client.example.com/jwks\");\n\t}\n\n\t@Test\n\tpublic void x509CertificateSubjectDNWhenProvidedThenSet() {\n\t\tClientSettings clientSettings = ClientSettings.builder()\n\t\t\t.x509CertificateSubjectDN(\"CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US\")\n\t\t\t.build();\n\t\tassertThat(clientSettings.getX509CertificateSubjectDN())\n\t\t\t.isEqualTo(\"CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US\");\n\t}\n\n\t@Test\n\tpublic void settingWhenCustomThenSet() {\n\t\tClientSettings clientSettings = ClientSettings.builder()\n\t\t\t.setting(\"name1\", \"value1\")\n\t\t\t.settings((settings) -> settings.put(\"name2\", \"value2\"))\n\t\t\t.build();\n\t\tassertThat(clientSettings.getSettings()).hasSize(4);\n\t\tassertThat(clientSettings.<String>getSetting(\"name1\")).isEqualTo(\"value1\");\n\t\tassertThat(clientSettings.<String>getSetting(\"name2\")).isEqualTo(\"value2\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.settings;\n\nimport java.time.Duration;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link TokenSettings}.\n *\n * @author Joe Grandja\n */\npublic class TokenSettingsTests {\n\n\t@Test\n\tpublic void buildWhenDefaultThenDefaultsAreSet() {\n\t\tTokenSettings tokenSettings = TokenSettings.builder().build();\n\t\tassertThat(tokenSettings.getSettings()).hasSize(8);\n\t\tassertThat(tokenSettings.getAuthorizationCodeTimeToLive()).isEqualTo(Duration.ofMinutes(5));\n\t\tassertThat(tokenSettings.getAccessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));\n\t\tassertThat(tokenSettings.getAccessTokenFormat()).isEqualTo(OAuth2TokenFormat.SELF_CONTAINED);\n\t\tassertThat(tokenSettings.getDeviceCodeTimeToLive()).isEqualTo(Duration.ofMinutes(5));\n\t\tassertThat(tokenSettings.isReuseRefreshTokens()).isTrue();\n\t\tassertThat(tokenSettings.getRefreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));\n\t\tassertThat(tokenSettings.getIdTokenSignatureAlgorithm()).isEqualTo(SignatureAlgorithm.RS256);\n\t\tassertThat(tokenSettings.isX509CertificateBoundAccessTokens()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authorizationCodeTimeToLiveWhenProvidedThenSet() {\n\t\tDuration authorizationCodeTimeToLive = Duration.ofMinutes(10);\n\t\tTokenSettings tokenSettings = TokenSettings.builder()\n\t\t\t.authorizationCodeTimeToLive(authorizationCodeTimeToLive)\n\t\t\t.build();\n\t\tassertThat(tokenSettings.getAuthorizationCodeTimeToLive()).isEqualTo(authorizationCodeTimeToLive);\n\t}\n\n\t@Test\n\tpublic void authorizationCodeTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().authorizationCodeTimeToLive(null))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"authorizationCodeTimeToLive cannot be null\");\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().authorizationCodeTimeToLive(Duration.ZERO))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"authorizationCodeTimeToLive must be greater than Duration.ZERO\");\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().authorizationCodeTimeToLive(Duration.ofSeconds(-10)))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"authorizationCodeTimeToLive must be greater than Duration.ZERO\");\n\t}\n\n\t@Test\n\tpublic void accessTokenTimeToLiveWhenProvidedThenSet() {\n\t\tDuration accessTokenTimeToLive = Duration.ofMinutes(10);\n\t\tTokenSettings tokenSettings = TokenSettings.builder().accessTokenTimeToLive(accessTokenTimeToLive).build();\n\t\tassertThat(tokenSettings.getAccessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive);\n\t}\n\n\t@Test\n\tpublic void accessTokenTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().accessTokenTimeToLive(null))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"accessTokenTimeToLive cannot be null\");\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().accessTokenTimeToLive(Duration.ZERO))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"accessTokenTimeToLive must be greater than Duration.ZERO\");\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().accessTokenTimeToLive(Duration.ofSeconds(-10)))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"accessTokenTimeToLive must be greater than Duration.ZERO\");\n\t}\n\n\t@Test\n\tpublic void accessTokenFormatWhenProvidedThenSet() {\n\t\tTokenSettings tokenSettings = TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE).build();\n\t\tassertThat(tokenSettings.getAccessTokenFormat()).isEqualTo(OAuth2TokenFormat.REFERENCE);\n\t}\n\n\t@Test\n\tpublic void accessTokenFormatWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().accessTokenFormat(null))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"accessTokenFormat cannot be null\");\n\t}\n\n\t@Test\n\tpublic void reuseRefreshTokensWhenFalseThenSet() {\n\t\tTokenSettings tokenSettings = TokenSettings.builder().reuseRefreshTokens(false).build();\n\t\tassertThat(tokenSettings.isReuseRefreshTokens()).isFalse();\n\t}\n\n\t@Test\n\tpublic void refreshTokenTimeToLiveWhenProvidedThenSet() {\n\t\tDuration refreshTokenTimeToLive = Duration.ofDays(10);\n\t\tTokenSettings tokenSettings = TokenSettings.builder().refreshTokenTimeToLive(refreshTokenTimeToLive).build();\n\t\tassertThat(tokenSettings.getRefreshTokenTimeToLive()).isEqualTo(refreshTokenTimeToLive);\n\t}\n\n\t@Test\n\tpublic void refreshTokenTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().refreshTokenTimeToLive(null))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"refreshTokenTimeToLive cannot be null\");\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().refreshTokenTimeToLive(Duration.ZERO))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"refreshTokenTimeToLive must be greater than Duration.ZERO\");\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> TokenSettings.builder().refreshTokenTimeToLive(Duration.ofSeconds(-10)))\n\t\t\t.extracting(Throwable::getMessage)\n\t\t\t.isEqualTo(\"refreshTokenTimeToLive must be greater than Duration.ZERO\");\n\t}\n\n\t@Test\n\tpublic void idTokenSignatureAlgorithmWhenProvidedThenSet() {\n\t\tSignatureAlgorithm idTokenSignatureAlgorithm = SignatureAlgorithm.RS512;\n\t\tTokenSettings tokenSettings = TokenSettings.builder()\n\t\t\t.idTokenSignatureAlgorithm(idTokenSignatureAlgorithm)\n\t\t\t.build();\n\t\tassertThat(tokenSettings.getIdTokenSignatureAlgorithm()).isEqualTo(idTokenSignatureAlgorithm);\n\t}\n\n\t@Test\n\tpublic void x509CertificateBoundAccessTokensWhenTrueThenSet() {\n\t\tTokenSettings tokenSettings = TokenSettings.builder().x509CertificateBoundAccessTokens(true).build();\n\t\tassertThat(tokenSettings.isX509CertificateBoundAccessTokens()).isTrue();\n\t}\n\n\t@Test\n\tpublic void settingWhenCustomThenSet() {\n\t\tTokenSettings tokenSettings = TokenSettings.builder()\n\t\t\t.setting(\"name1\", \"value1\")\n\t\t\t.settings((settings) -> settings.put(\"name2\", \"value2\"))\n\t\t\t.build();\n\t\tassertThat(tokenSettings.getSettings()).hasSize(10);\n\t\tassertThat(tokenSettings.<String>getSetting(\"name1\")).isEqualTo(\"value1\");\n\t\tassertThat(tokenSettings.<String>getSetting(\"name2\")).isEqualTo(\"value2\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGeneratorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link DelegatingOAuth2TokenGenerator}.\n *\n * @author Joe Grandja\n */\npublic class DelegatingOAuth2TokenGeneratorTests {\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void constructorWhenTokenGeneratorsEmptyThenThrowIllegalArgumentException() {\n\t\tOAuth2TokenGenerator<OAuth2Token>[] tokenGenerators = new OAuth2TokenGenerator[0];\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new DelegatingOAuth2TokenGenerator(tokenGenerators))\n\t\t\t.withMessage(\"tokenGenerators cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenGeneratorsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new DelegatingOAuth2TokenGenerator(null, null))\n\t\t\t.withMessage(\"tokenGenerator cannot be null\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void generateWhenTokenGeneratorSupportedThenReturnToken() {\n\t\tOAuth2TokenGenerator<OAuth2Token> tokenGenerator1 = mock(OAuth2TokenGenerator.class);\n\t\tOAuth2TokenGenerator<OAuth2Token> tokenGenerator2 = mock(OAuth2TokenGenerator.class);\n\t\tOAuth2TokenGenerator<OAuth2Token> tokenGenerator3 = mock(OAuth2TokenGenerator.class);\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\t\tInstant.now(), Instant.now().plusSeconds(300));\n\t\tgiven(tokenGenerator3.generate(any())).willReturn(accessToken);\n\n\t\tDelegatingOAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(tokenGenerator1,\n\t\t\t\ttokenGenerator2, tokenGenerator3);\n\n\t\tOAuth2Token token = delegatingTokenGenerator.generate(DefaultOAuth2TokenContext.builder().build());\n\t\tassertThat(token).isEqualTo(accessToken);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void generateWhenTokenGeneratorNotSupportedThenReturnNull() {\n\t\tOAuth2TokenGenerator<OAuth2Token> tokenGenerator1 = mock(OAuth2TokenGenerator.class);\n\t\tOAuth2TokenGenerator<OAuth2Token> tokenGenerator2 = mock(OAuth2TokenGenerator.class);\n\t\tOAuth2TokenGenerator<OAuth2Token> tokenGenerator3 = mock(OAuth2TokenGenerator.class);\n\n\t\tDelegatingOAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(tokenGenerator1,\n\t\t\t\ttokenGenerator2, tokenGenerator3);\n\n\t\tOAuth2Token token = delegatingTokenGenerator.generate(DefaultOAuth2TokenContext.builder().build());\n\t\tassertThat(token).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.TestJwsHeaders;\nimport org.springframework.security.oauth2.jwt.TestJwtClaimsSets;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link JwtEncodingContext}.\n *\n * @author Joe Grandja\n */\npublic class JwtEncodingContextTests {\n\n\t@Test\n\tpublic void withWhenJwsHeaderNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> JwtEncodingContext.with(null, TestJwtClaimsSets.jwtClaimsSet()))\n\t\t\t.withMessage(\"jwsHeaderBuilder cannot be null\");\n\t}\n\n\t@Test\n\tpublic void withWhenClaimsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> JwtEncodingContext.with(TestJwsHeaders.jwsHeader(), null))\n\t\t\t.withMessage(\"claimsBuilder cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setWhenValueNullThenThrowIllegalArgumentException() {\n\t\tJwtEncodingContext.Builder builder = JwtEncodingContext.with(TestJwsHeaders.jwsHeader(),\n\t\t\t\tTestJwtClaimsSets.jwtClaimsSet());\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.registeredClient(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.principal(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.authorization(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.tokenType(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> builder.authorizationGrantType(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.authorizationGrant(null));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> builder.put(null, \"\"));\n\t}\n\n\t@Test\n\tpublic void buildWhenAllValuesProvidedThenAllValuesAreSet() {\n\t\tJwsHeader.Builder headers = TestJwsHeaders.jwsHeader();\n\t\tJwtClaimsSet.Builder claims = TestJwtClaimsSets.jwtClaimsSet();\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationGrant = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\t\"code\", clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tJwtEncodingContext context = JwtEncodingContext.with(headers, claims)\n\t\t\t.registeredClient(registeredClient)\n\t\t\t.principal(principal)\n\t\t\t.authorization(authorization)\n\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.authorizationGrant(authorizationGrant)\n\t\t\t.put(\"custom-key-1\", \"custom-value-1\")\n\t\t\t.context((ctx) -> ctx.put(\"custom-key-2\", \"custom-value-2\"))\n\t\t\t.build();\n\n\t\tassertThat(context.getJwsHeader()).isEqualTo(headers);\n\t\tassertThat(context.getClaims()).isEqualTo(claims);\n\t\tassertThat(context.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(context.<Authentication>getPrincipal()).isEqualTo(principal);\n\t\tassertThat(context.getAuthorization()).isEqualTo(authorization);\n\t\tassertThat(context.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(context.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(context.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authorizationGrant);\n\t\tassertThat(context.<String>get(\"custom-key-1\")).isEqualTo(\"custom-value-1\");\n\t\tassertThat(context.<String>get(\"custom-key-2\")).isEqualTo(\"custom-value-2\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.security.Principal;\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link JwtGenerator}.\n *\n * @author Joe Grandja\n */\npublic class JwtGeneratorTests {\n\n\tprivate static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);\n\n\tprivate JwtEncoder jwtEncoder;\n\n\tprivate OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer;\n\n\tprivate JwtGenerator jwtGenerator;\n\n\tprivate TestAuthorizationServerContext authorizationServerContext;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.jwtEncoder = mock(JwtEncoder.class);\n\t\tthis.jwtCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tthis.jwtGenerator = new JwtGenerator(this.jwtEncoder);\n\t\tthis.jwtGenerator.setJwtCustomizer(this.jwtCustomizer);\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.build();\n\t\tthis.authorizationServerContext = new TestAuthorizationServerContext(authorizationServerSettings, null);\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwtEncoderNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new JwtGenerator(null))\n\t\t\t.withMessage(\"jwtEncoder cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setJwtCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.jwtGenerator.setJwtCustomizer(null))\n\t\t\t.withMessage(\"jwtCustomizer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.jwtGenerator.setClock(null))\n\t\t\t.withMessage(\"clock cannot be null\");\n\t}\n\n\t@Test\n\tpublic void generateWhenUnsupportedTokenTypeThenReturnNull() {\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.tokenType(new OAuth2TokenType(\"unsupported_token_type\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(this.jwtGenerator.generate(tokenContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void generateWhenUnsupportedTokenFormatThenReturnNull() {\n\t\t// @formatter:off\n\t\tTokenSettings tokenSettings = TokenSettings.builder()\n\t\t\t\t.accessTokenFormat(new OAuth2TokenFormat(\"unsupported_token_format\"))\n\t\t\t\t.build();\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.tokenSettings(tokenSettings)\n\t\t\t\t.build();\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(this.jwtGenerator.generate(tokenContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void generateWhenAccessTokenTypeThenReturnJwt() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\t\"code\", clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(authorization.getAttribute(Principal.class.getName()))\n\t\t\t\t.authorizationServerContext(this.authorizationServerContext)\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizedScopes(authorization.getAuthorizedScopes())\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.authorizationGrant(authentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tClock clock = Clock.offset(Clock.systemUTC(), Duration.ofMinutes(5));\n\t\tthis.jwtGenerator.setClock(clock);\n\n\t\tassertGeneratedTokenType(tokenContext, clock);\n\t}\n\n\t@Test\n\tpublic void generateWhenIdTokenTypeAndAuthorizationCodeGrantThenReturnJwt() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.scope(OidcScopes.OPENID)\n\t\t\t.tokenSettings(TokenSettings.builder().idTokenSignatureAlgorithm(SignatureAlgorithm.ES256).build())\n\t\t\t.build();\n\t\tMap<String, Object> authenticationRequestAdditionalParameters = new HashMap<>();\n\t\tauthenticationRequestAdditionalParameters.put(OidcParameterNames.NONCE, \"nonce\");\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations\n\t\t\t.authorization(registeredClient, authenticationRequestAdditionalParameters)\n\t\t\t.build();\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\t\"code\", clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\t\tSessionInformation sessionInformation = new SessionInformation(principal.getPrincipal(), \"session1\",\n\t\t\t\tDate.from(Instant.now().minus(2, ChronoUnit.HOURS)));\n\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(principal)\n\t\t\t\t.authorizationServerContext(this.authorizationServerContext)\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizedScopes(authorization.getAuthorizedScopes())\n\t\t\t\t.tokenType(ID_TOKEN_TOKEN_TYPE)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.authorizationGrant(authentication)\n\t\t\t\t.put(SessionInformation.class, sessionInformation)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertGeneratedTokenType(tokenContext);\n\t}\n\n\t// gh-1224\n\t@Test\n\tpublic void generateWhenIdTokenTypeAndRefreshTokenGrantThenReturnJwt() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(\"subject\")\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.expiresAt(Instant.now().plusSeconds(60))\n\t\t\t.claim(\"sid\", \"sessionId-1234\")\n\t\t\t.claim(IdTokenClaimNames.AUTH_TIME, Date.from(Instant.now()))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(idToken)\n\t\t\t.build();\n\n\t\tOAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\trefreshToken.getTokenValue(), clientPrincipal, null, null);\n\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(principal)\n\t\t\t\t.authorizationServerContext(this.authorizationServerContext)\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizedScopes(authorization.getAuthorizedScopes())\n\t\t\t\t.tokenType(ID_TOKEN_TOKEN_TYPE)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t\t.authorizationGrant(authentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertGeneratedTokenType(tokenContext);\n\t}\n\n\t// gh-1283\n\t@Test\n\tpublic void generateWhenIdTokenTypeWithoutSidAndRefreshTokenGrantThenReturnJwt() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build();\n\t\tOidcIdToken idToken = OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.subject(\"subject\")\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.expiresAt(Instant.now().plusSeconds(60))\n\t\t\t.build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)\n\t\t\t.token(idToken)\n\t\t\t.build();\n\n\t\tOAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(\n\t\t\t\trefreshToken.getTokenValue(), clientPrincipal, null, null);\n\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(principal)\n\t\t\t\t.authorizationServerContext(this.authorizationServerContext)\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizedScopes(authorization.getAuthorizedScopes())\n\t\t\t\t.tokenType(ID_TOKEN_TOKEN_TYPE)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t\t.authorizationGrant(authentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertGeneratedTokenType(tokenContext);\n\t}\n\n\tprivate void assertGeneratedTokenType(OAuth2TokenContext tokenContext) {\n\t\tassertGeneratedTokenType(tokenContext, Clock.systemUTC());\n\t}\n\n\tprivate void assertGeneratedTokenType(OAuth2TokenContext tokenContext, Clock clock) {\n\t\tthis.jwtGenerator.generate(tokenContext);\n\n\t\tArgumentCaptor<JwtEncodingContext> jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class);\n\t\tverify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture());\n\n\t\tJwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue();\n\t\tassertThat(jwtEncodingContext.getJwsHeader()).isNotNull();\n\t\tassertThat(jwtEncodingContext.getClaims()).isNotNull();\n\t\tassertThat(jwtEncodingContext.getRegisteredClient()).isEqualTo(tokenContext.getRegisteredClient());\n\t\tassertThat(jwtEncodingContext.<Authentication>getPrincipal()).isEqualTo(tokenContext.getPrincipal());\n\t\tassertThat(jwtEncodingContext.getAuthorization()).isEqualTo(tokenContext.getAuthorization());\n\t\tassertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(tokenContext.getAuthorizedScopes());\n\t\tassertThat(jwtEncodingContext.getTokenType()).isEqualTo(tokenContext.getTokenType());\n\t\tassertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(tokenContext.getAuthorizationGrantType());\n\t\tassertThat(jwtEncodingContext.<Authentication>getAuthorizationGrant())\n\t\t\t.isEqualTo(tokenContext.getAuthorizationGrant());\n\n\t\tArgumentCaptor<JwtEncoderParameters> jwtEncoderParametersCaptor = ArgumentCaptor\n\t\t\t.forClass(JwtEncoderParameters.class);\n\t\tverify(this.jwtEncoder).encode(jwtEncoderParametersCaptor.capture());\n\n\t\tJwsHeader jwsHeader = jwtEncoderParametersCaptor.getValue().getJwsHeader();\n\t\tif (OidcParameterNames.ID_TOKEN.equals(tokenContext.getTokenType().getValue())) {\n\t\t\tassertThat(jwsHeader.getAlgorithm())\n\t\t\t\t.isEqualTo(tokenContext.getRegisteredClient().getTokenSettings().getIdTokenSignatureAlgorithm());\n\t\t}\n\t\telse {\n\t\t\tassertThat(jwsHeader.getAlgorithm()).isEqualTo(SignatureAlgorithm.RS256);\n\t\t}\n\n\t\tJwtClaimsSet jwtClaimsSet = jwtEncoderParametersCaptor.getValue().getClaims();\n\t\tassertThat(jwtClaimsSet.getIssuer().toExternalForm())\n\t\t\t.isEqualTo(tokenContext.getAuthorizationServerContext().getIssuer());\n\t\tassertThat(jwtClaimsSet.getSubject()).isEqualTo(tokenContext.getAuthorization().getPrincipalName());\n\t\tassertThat(jwtClaimsSet.getAudience()).containsExactly(tokenContext.getRegisteredClient().getClientId());\n\n\t\tInstant issuedAt = clock.instant();\n\t\tInstant expiresAt;\n\t\tif (tokenContext.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) {\n\t\t\texpiresAt = issuedAt.plus(tokenContext.getRegisteredClient().getTokenSettings().getAccessTokenTimeToLive());\n\t\t}\n\t\telse {\n\t\t\texpiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);\n\t\t}\n\t\tassertThat(jwtClaimsSet.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1));\n\t\tassertThat(jwtClaimsSet.getExpiresAt()).isBetween(expiresAt.minusSeconds(1), expiresAt.plusSeconds(1));\n\t\tassertThat(jwtClaimsSet.getId()).isNotNull();\n\n\t\tif (tokenContext.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) {\n\t\t\tassertThat(jwtClaimsSet.getNotBefore()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1));\n\n\t\t\tSet<String> scopes = jwtClaimsSet.getClaim(OAuth2ParameterNames.SCOPE);\n\t\t\tassertThat(scopes).isEqualTo(tokenContext.getAuthorizedScopes());\n\t\t}\n\t\telse {\n\t\t\tassertThat(jwtClaimsSet.<String>getClaim(IdTokenClaimNames.AZP))\n\t\t\t\t.isEqualTo(tokenContext.getRegisteredClient().getClientId());\n\t\t\tif (tokenContext.getAuthorizationGrantType().equals(AuthorizationGrantType.AUTHORIZATION_CODE)) {\n\t\t\t\tOAuth2AuthorizationRequest authorizationRequest = tokenContext.getAuthorization()\n\t\t\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\t\t\tString nonce = (String) authorizationRequest.getAdditionalParameters().get(OidcParameterNames.NONCE);\n\t\t\t\tassertThat(jwtClaimsSet.<String>getClaim(IdTokenClaimNames.NONCE)).isEqualTo(nonce);\n\n\t\t\t\tSessionInformation sessionInformation = tokenContext.get(SessionInformation.class);\n\t\t\t\tassertThat(jwtClaimsSet.<String>getClaim(\"sid\")).isEqualTo(sessionInformation.getSessionId());\n\t\t\t\tassertThat(jwtClaimsSet.<Date>getClaim(IdTokenClaimNames.AUTH_TIME))\n\t\t\t\t\t.isEqualTo(sessionInformation.getLastRequest());\n\t\t\t}\n\t\t\telse if (tokenContext.getAuthorizationGrantType().equals(AuthorizationGrantType.REFRESH_TOKEN)) {\n\t\t\t\tOidcIdToken currentIdToken = tokenContext.getAuthorization().getToken(OidcIdToken.class).getToken();\n\t\t\t\tassertThat(jwtClaimsSet.<String>getClaim(\"sid\")).isEqualTo(currentIdToken.getClaim(\"sid\"));\n\t\t\t\tassertThat(jwtClaimsSet.<Date>getClaim(IdTokenClaimNames.AUTH_TIME))\n\t\t\t\t\t.isEqualTo(currentIdToken.<Date>getClaim(IdTokenClaimNames.AUTH_TIME));\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGeneratorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.security.Principal;\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;\nimport org.springframework.security.oauth2.server.authorization.settings.TokenSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2AccessTokenGenerator}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AccessTokenGeneratorTests {\n\n\tprivate OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer;\n\n\tprivate OAuth2AccessTokenGenerator accessTokenGenerator;\n\n\tprivate AuthorizationServerContext authorizationServerContext;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class);\n\t\tthis.accessTokenGenerator = new OAuth2AccessTokenGenerator();\n\t\tthis.accessTokenGenerator.setAccessTokenCustomizer(this.accessTokenCustomizer);\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://provider.com\")\n\t\t\t.build();\n\t\tthis.authorizationServerContext = new TestAuthorizationServerContext(authorizationServerSettings, null);\n\t}\n\n\t@Test\n\tpublic void setAccessTokenCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.accessTokenGenerator.setAccessTokenCustomizer(null))\n\t\t\t.withMessage(\"accessTokenCustomizer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.accessTokenGenerator.setClock(null))\n\t\t\t.withMessage(\"clock cannot be null\");\n\t}\n\n\t@Test\n\tpublic void generateWhenUnsupportedTokenTypeThenReturnNull() {\n\t\t// @formatter:off\n\t\tTokenSettings tokenSettings = TokenSettings.builder()\n\t\t\t\t.accessTokenFormat(OAuth2TokenFormat.REFERENCE)\n\t\t\t\t.build();\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.tokenSettings(tokenSettings)\n\t\t\t\t.build();\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.tokenType(new OAuth2TokenType(\"unsupported_token_type\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(this.accessTokenGenerator.generate(tokenContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void generateWhenUnsupportedTokenFormatThenReturnNull() {\n\t\t// @formatter:off\n\t\tTokenSettings tokenSettings = TokenSettings.builder()\n\t\t\t\t.accessTokenFormat(new OAuth2TokenFormat(\"unsupported_token_format\"))\n\t\t\t\t.build();\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.tokenSettings(tokenSettings)\n\t\t\t\t.build();\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(this.accessTokenGenerator.generate(tokenContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void generateWhenReferenceAccessTokenTypeThenReturnAccessToken() {\n\t\t// @formatter:off\n\t\tTokenSettings tokenSettings = TokenSettings.builder()\n\t\t\t\t.accessTokenFormat(OAuth2TokenFormat.REFERENCE)\n\t\t\t\t.build();\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t\t.tokenSettings(tokenSettings)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\t\"code\", clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(principal)\n\t\t\t\t.authorizationServerContext(this.authorizationServerContext)\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.authorizedScopes(authorization.getAuthorizedScopes())\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.authorizationGrant(authentication)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tClock clock = Clock.offset(Clock.systemUTC(), Duration.ofMinutes(5));\n\t\tthis.accessTokenGenerator.setClock(clock);\n\n\t\tOAuth2AccessToken accessToken = this.accessTokenGenerator.generate(tokenContext);\n\t\tassertThat(accessToken).isNotNull();\n\n\t\tInstant issuedAt = clock.instant();\n\t\tInstant expiresAt = issuedAt\n\t\t\t.plus(tokenContext.getRegisteredClient().getTokenSettings().getAccessTokenTimeToLive());\n\t\tassertThat(accessToken.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1));\n\t\tassertThat(accessToken.getExpiresAt()).isBetween(expiresAt.minusSeconds(1), expiresAt.plusSeconds(1));\n\t\tassertThat(accessToken.getScopes()).isEqualTo(tokenContext.getAuthorizedScopes());\n\n\t\tassertThat(accessToken).isInstanceOf(ClaimAccessor.class);\n\t\tOAuth2TokenClaimAccessor accessTokenClaims = ((ClaimAccessor) accessToken)::getClaims;\n\t\tassertThat(accessTokenClaims.getClaims()).isNotEmpty();\n\n\t\tassertThat(accessTokenClaims.getIssuer().toExternalForm())\n\t\t\t.isEqualTo(tokenContext.getAuthorizationServerContext().getIssuer());\n\t\tassertThat(accessTokenClaims.getSubject()).isEqualTo(tokenContext.getPrincipal().getName());\n\t\tassertThat(accessTokenClaims.getAudience())\n\t\t\t.isEqualTo(Collections.singletonList(tokenContext.getRegisteredClient().getClientId()));\n\t\tassertThat(accessTokenClaims.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1));\n\t\tassertThat(accessTokenClaims.getExpiresAt()).isBetween(expiresAt.minusSeconds(1), expiresAt.plusSeconds(1));\n\t\tassertThat(accessTokenClaims.getNotBefore()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1));\n\t\tassertThat(accessTokenClaims.getId()).isNotNull();\n\n\t\tSet<String> scopes = accessTokenClaims.getClaim(OAuth2ParameterNames.SCOPE);\n\t\tassertThat(scopes).isEqualTo(tokenContext.getAuthorizedScopes());\n\n\t\tArgumentCaptor<OAuth2TokenClaimsContext> tokenClaimsContextCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2TokenClaimsContext.class);\n\t\tverify(this.accessTokenCustomizer).customize(tokenClaimsContextCaptor.capture());\n\n\t\tOAuth2TokenClaimsContext tokenClaimsContext = tokenClaimsContextCaptor.getValue();\n\t\tassertThat(tokenClaimsContext.getClaims()).isNotNull();\n\t\tassertThat(tokenClaimsContext.getRegisteredClient()).isEqualTo(tokenContext.getRegisteredClient());\n\t\tassertThat(tokenClaimsContext.<Authentication>getPrincipal()).isEqualTo(tokenContext.getPrincipal());\n\t\tassertThat(tokenClaimsContext.getAuthorizationServerContext())\n\t\t\t.isEqualTo(tokenContext.getAuthorizationServerContext());\n\t\tassertThat(tokenClaimsContext.getAuthorization()).isEqualTo(tokenContext.getAuthorization());\n\t\tassertThat(tokenClaimsContext.getAuthorizedScopes()).isEqualTo(tokenContext.getAuthorizedScopes());\n\t\tassertThat(tokenClaimsContext.getTokenType()).isEqualTo(tokenContext.getTokenType());\n\t\tassertThat(tokenClaimsContext.getAuthorizationGrantType()).isEqualTo(tokenContext.getAuthorizationGrantType());\n\t\tassertThat(tokenClaimsContext.<Authentication>getAuthorizationGrant())\n\t\t\t.isEqualTo(tokenContext.getAuthorizationGrant());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGeneratorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2RefreshTokenGenerator}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2RefreshTokenGeneratorTests {\n\n\tprivate final OAuth2RefreshTokenGenerator tokenGenerator = new OAuth2RefreshTokenGenerator();\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.tokenGenerator.setClock(null))\n\t\t\t.withMessage(\"clock cannot be null\");\n\t}\n\n\t@Test\n\tpublic void generateWhenUnsupportedTokenTypeThenReturnNull() {\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(this.tokenGenerator.generate(tokenContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void generateWhenRefreshTokenTypeThenReturnRefreshToken() {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\t\t// @formatter:off\n\t\tOAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.tokenType(OAuth2TokenType.REFRESH_TOKEN)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tClock clock = Clock.offset(Clock.systemUTC(), Duration.ofMinutes(5));\n\t\tthis.tokenGenerator.setClock(clock);\n\n\t\tOAuth2RefreshToken refreshToken = this.tokenGenerator.generate(tokenContext);\n\t\tassertThat(refreshToken).isNotNull();\n\n\t\tInstant issuedAt = clock.instant();\n\t\tInstant expiresAt = issuedAt\n\t\t\t.plus(tokenContext.getRegisteredClient().getTokenSettings().getRefreshTokenTimeToLive());\n\t\tassertThat(refreshToken.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1));\n\t\tassertThat(refreshToken.getExpiresAt()).isBetween(expiresAt.minusSeconds(1), expiresAt.plusSeconds(1));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.security.Principal;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2TokenClaimsContext}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2TokenClaimsContextTests {\n\n\t@Test\n\tpublic void withWhenClaimsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> OAuth2TokenClaimsContext.with(null))\n\t\t\t.withMessage(\"claimsBuilder cannot be null\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAllValuesProvidedThenAllValuesAreSet() {\n\t\tString issuer = \"https://provider.com\";\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\n\t\t// @formatter:off\n\t\tOAuth2TokenClaimsSet.Builder claims = OAuth2TokenClaimsSet.builder()\n\t\t\t\t.issuer(issuer)\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.audience(Collections.singletonList(\"client-1\"))\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.notBefore(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.id(\"id\");\n\t\t// @formatter:on\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();\n\t\tAuthentication principal = authorization.getAttribute(Principal.class.getName());\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(issuer)\n\t\t\t.build();\n\t\tAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(\n\t\t\t\tauthorizationServerSettings, null);\n\t\tOAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorization\n\t\t\t.getAttribute(OAuth2AuthorizationRequest.class.getName());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationGrant = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\t\"code\", clientPrincipal, authorizationRequest.getRedirectUri(), null);\n\n\t\t// @formatter:off\n\t\tOAuth2TokenClaimsContext context = OAuth2TokenClaimsContext.with(claims)\n\t\t\t\t.registeredClient(registeredClient)\n\t\t\t\t.principal(principal)\n\t\t\t\t.authorizationServerContext(authorizationServerContext)\n\t\t\t\t.authorization(authorization)\n\t\t\t\t.tokenType(OAuth2TokenType.ACCESS_TOKEN)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.authorizationGrant(authorizationGrant)\n\t\t\t\t.put(\"custom-key-1\", \"custom-value-1\")\n\t\t\t\t.context((ctx) -> ctx.put(\"custom-key-2\", \"custom-value-2\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(context.getClaims()).isEqualTo(claims);\n\t\tassertThat(context.getRegisteredClient()).isEqualTo(registeredClient);\n\t\tassertThat(context.<Authentication>getPrincipal()).isEqualTo(principal);\n\t\tassertThat(context.getAuthorizationServerContext()).isEqualTo(authorizationServerContext);\n\t\tassertThat(context.getAuthorization()).isEqualTo(authorization);\n\t\tassertThat(context.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN);\n\t\tassertThat(context.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(context.<OAuth2AuthorizationGrantAuthenticationToken>getAuthorizationGrant())\n\t\t\t.isEqualTo(authorizationGrant);\n\t\tassertThat(context.<String>get(\"custom-key-1\")).isEqualTo(\"custom-value-1\");\n\t\tassertThat(context.<String>get(\"custom-key-2\")).isEqualTo(\"custom-value-2\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSetTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.token;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2TokenClaimsSet}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2TokenClaimsSetTests {\n\n\t@Test\n\tpublic void buildWhenClaimsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2TokenClaimsSet.builder().build())\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAllClaimsProvidedThenAllClaimsAreSet() {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\t\tString customClaimName = \"custom-claim-name\";\n\t\tString customClaimValue = \"custom-claim-value\";\n\n\t\t// @formatter:off\n\t\tOAuth2TokenClaimsSet expectedClaimsSet = OAuth2TokenClaimsSet.builder()\n\t\t\t\t.issuer(\"https://provider.com\")\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.audience(Collections.singletonList(\"client-1\"))\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.notBefore(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.id(\"id\")\n\t\t\t\t.claims((claims) -> claims.put(customClaimName, customClaimValue))\n\t\t\t\t.build();\n\n\t\tOAuth2TokenClaimsSet claimsSet = OAuth2TokenClaimsSet.builder()\n\t\t\t\t.issuer(expectedClaimsSet.getIssuer().toExternalForm())\n\t\t\t\t.subject(expectedClaimsSet.getSubject())\n\t\t\t\t.audience(expectedClaimsSet.getAudience())\n\t\t\t\t.issuedAt(expectedClaimsSet.getIssuedAt())\n\t\t\t\t.notBefore(expectedClaimsSet.getNotBefore())\n\t\t\t\t.expiresAt(expectedClaimsSet.getExpiresAt())\n\t\t\t\t.id(expectedClaimsSet.getId())\n\t\t\t\t.claims((claims) -> claims.put(customClaimName, expectedClaimsSet.getClaim(customClaimName)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(claimsSet.getIssuer()).isEqualTo(expectedClaimsSet.getIssuer());\n\t\tassertThat(claimsSet.getSubject()).isEqualTo(expectedClaimsSet.getSubject());\n\t\tassertThat(claimsSet.getAudience()).isEqualTo(expectedClaimsSet.getAudience());\n\t\tassertThat(claimsSet.getIssuedAt()).isEqualTo(expectedClaimsSet.getIssuedAt());\n\t\tassertThat(claimsSet.getNotBefore()).isEqualTo(expectedClaimsSet.getNotBefore());\n\t\tassertThat(claimsSet.getExpiresAt()).isEqualTo(expectedClaimsSet.getExpiresAt());\n\t\tassertThat(claimsSet.getId()).isEqualTo(expectedClaimsSet.getId());\n\t\tassertThat(claimsSet.<String>getClaim(customClaimName)).isEqualTo(expectedClaimsSet.getClaim(customClaimName));\n\t\tassertThat(claimsSet.getClaims()).isEqualTo(expectedClaimsSet.getClaims());\n\t}\n\n\t@Test\n\tpublic void claimWhenNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2TokenClaimsSet.builder().claim(null, \"value\"))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void claimWhenValueNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2TokenClaimsSet.builder().claim(\"name\", null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/util/TestX509Certificates.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.util;\n\nimport java.security.KeyPair;\nimport java.security.cert.X509Certificate;\n\n/**\n * @author Joe Grandja\n */\npublic final class TestX509Certificates {\n\n\tpublic static final X509Certificate[] DEMO_CLIENT_PKI_CERTIFICATE;\n\tstatic {\n\t\ttry {\n\t\t\t// Generate the Root certificate (Trust Anchor or most-trusted CA)\n\t\t\tKeyPair rootKeyPair = X509CertificateUtils.generateRSAKeyPair();\n\t\t\tString distinguishedName = \"CN=spring-samples-trusted-ca, OU=Spring Samples, O=Spring, C=US\";\n\t\t\tX509Certificate rootCertificate = X509CertificateUtils.createTrustAnchorCertificate(rootKeyPair,\n\t\t\t\t\tdistinguishedName);\n\n\t\t\t// Generate the CA (intermediary) certificate\n\t\t\tKeyPair caKeyPair = X509CertificateUtils.generateRSAKeyPair();\n\t\t\tdistinguishedName = \"CN=spring-samples-ca, OU=Spring Samples, O=Spring, C=US\";\n\t\t\tX509Certificate caCertificate = X509CertificateUtils.createCACertificate(rootCertificate,\n\t\t\t\t\trootKeyPair.getPrivate(), caKeyPair.getPublic(), distinguishedName);\n\n\t\t\t// Generate certificate for demo-client-sample\n\t\t\tKeyPair demoClientKeyPair = X509CertificateUtils.generateRSAKeyPair();\n\t\t\tdistinguishedName = \"CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US\";\n\t\t\tX509Certificate demoClientCertificate = X509CertificateUtils.createEndEntityCertificate(caCertificate,\n\t\t\t\t\tcaKeyPair.getPrivate(), demoClientKeyPair.getPublic(), distinguishedName);\n\n\t\t\tDEMO_CLIENT_PKI_CERTIFICATE = new X509Certificate[] { demoClientCertificate, caCertificate,\n\t\t\t\t\trootCertificate };\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\tpublic static final X509Certificate[] DEMO_CLIENT_SELF_SIGNED_CERTIFICATE;\n\tstatic {\n\t\ttry {\n\t\t\t// Generate self-signed certificate for demo-client-sample\n\t\t\tKeyPair keyPair = X509CertificateUtils.generateRSAKeyPair();\n\t\t\tString distinguishedName = \"CN=demo-client-sample, OU=Spring Samples, O=Spring, C=US\";\n\t\t\tX509Certificate demoClientSelfSignedCertificate = X509CertificateUtils.createTrustAnchorCertificate(keyPair,\n\t\t\t\t\tdistinguishedName);\n\n\t\t\tDEMO_CLIENT_SELF_SIGNED_CERTIFICATE = new X509Certificate[] { demoClientSelfSignedCertificate };\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\tprivate TestX509Certificates() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/util/X509CertificateUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.util;\n\nimport java.math.BigInteger;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.security.Security;\nimport java.security.cert.X509Certificate;\nimport java.security.spec.RSAKeyGenParameterSpec;\nimport java.util.Calendar;\nimport java.util.Date;\n\nimport javax.security.auth.x500.X500Principal;\n\nimport org.bouncycastle.asn1.x509.BasicConstraints;\nimport org.bouncycastle.asn1.x509.Extension;\nimport org.bouncycastle.asn1.x509.KeyUsage;\nimport org.bouncycastle.cert.X509v3CertificateBuilder;\nimport org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;\nimport org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;\nimport org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\nimport org.bouncycastle.operator.ContentSigner;\nimport org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;\n\n/**\n * @author Joe Grandja\n */\npublic final class X509CertificateUtils {\n\n\tprivate static final String BC_PROVIDER = \"BC\";\n\n\tprivate static final String SHA256_RSA_SIGNATURE_ALGORITHM = \"SHA256withRSA\";\n\n\tprivate static final Date DEFAULT_START_DATE;\n\n\tprivate static final Date DEFAULT_END_DATE;\n\n\tstatic {\n\t\tSecurity.addProvider(new BouncyCastleProvider());\n\n\t\t// Setup default certificate start date to yesterday and end date for 1 year\n\t\t// validity\n\t\tCalendar calendar = Calendar.getInstance();\n\t\tcalendar.add(Calendar.DATE, -1);\n\t\tDEFAULT_START_DATE = calendar.getTime();\n\t\tcalendar.add(Calendar.YEAR, 1);\n\t\tDEFAULT_END_DATE = calendar.getTime();\n\t}\n\n\tprivate X509CertificateUtils() {\n\t}\n\n\tpublic static KeyPair generateRSAKeyPair() {\n\t\tKeyPair keyPair;\n\t\ttry {\n\t\t\tKeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"RSA\", BC_PROVIDER);\n\t\t\tkeyPairGenerator.initialize(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4));\n\t\t\tkeyPair = keyPairGenerator.generateKeyPair();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t\treturn keyPair;\n\t}\n\n\tpublic static X509Certificate createTrustAnchorCertificate(KeyPair keyPair, String distinguishedName)\n\t\t\tthrows Exception {\n\t\tX500Principal subject = new X500Principal(distinguishedName);\n\t\tBigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));\n\n\t\tX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(subject, serialNum, DEFAULT_START_DATE,\n\t\t\t\tDEFAULT_END_DATE, subject, keyPair.getPublic());\n\n\t\t// Add Extensions\n\t\tJcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();\n\t\tcertBuilder\n\t\t\t// A BasicConstraints to mark root certificate as CA certificate\n\t\t\t.addExtension(Extension.basicConstraints, true, new BasicConstraints(true))\n\t\t\t.addExtension(Extension.subjectKeyIdentifier, false,\n\t\t\t\t\textensionUtils.createSubjectKeyIdentifier(keyPair.getPublic()));\n\n\t\tContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)\n\t\t\t.build(keyPair.getPrivate());\n\n\t\tJcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);\n\n\t\treturn converter.getCertificate(certBuilder.build(signer));\n\t}\n\n\tpublic static X509Certificate createCACertificate(X509Certificate signerCert, PrivateKey signerKey,\n\t\t\tPublicKey certKey, String distinguishedName) throws Exception {\n\n\t\tX500Principal subject = new X500Principal(distinguishedName);\n\t\tBigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));\n\n\t\tX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(signerCert.getSubjectX500Principal(),\n\t\t\t\tserialNum, DEFAULT_START_DATE, DEFAULT_END_DATE, subject, certKey);\n\n\t\t// Add Extensions\n\t\tJcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();\n\t\tcertBuilder\n\t\t\t// A BasicConstraints to mark as CA certificate and how many CA certificates\n\t\t\t// can follow it in the chain\n\t\t\t// (with 0 meaning the chain ends with the next certificate in the chain).\n\t\t\t.addExtension(Extension.basicConstraints, true, new BasicConstraints(0))\n\t\t\t// KeyUsage specifies what the public key in the certificate can be used for.\n\t\t\t// In this case, it can be used for signing other certificates and/or\n\t\t\t// signing Certificate Revocation Lists (CRLs).\n\t\t\t.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign))\n\t\t\t.addExtension(Extension.authorityKeyIdentifier, false,\n\t\t\t\t\textensionUtils.createAuthorityKeyIdentifier(signerCert))\n\t\t\t.addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(certKey));\n\n\t\tContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)\n\t\t\t.build(signerKey);\n\n\t\tJcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);\n\n\t\treturn converter.getCertificate(certBuilder.build(signer));\n\t}\n\n\tpublic static X509Certificate createEndEntityCertificate(X509Certificate signerCert, PrivateKey signerKey,\n\t\t\tPublicKey certKey, String distinguishedName) throws Exception {\n\n\t\tX500Principal subject = new X500Principal(distinguishedName);\n\t\tBigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));\n\n\t\tX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(signerCert.getSubjectX500Principal(),\n\t\t\t\tserialNum, DEFAULT_START_DATE, DEFAULT_END_DATE, subject, certKey);\n\n\t\tJcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();\n\t\tcertBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false))\n\t\t\t.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature))\n\t\t\t.addExtension(Extension.authorityKeyIdentifier, false,\n\t\t\t\t\textensionUtils.createAuthorityKeyIdentifier(signerCert))\n\t\t\t.addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(certKey));\n\n\t\tContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)\n\t\t\t.build(signerKey);\n\n\t\tJcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);\n\n\t\treturn converter.getCertificate(certBuilder.build(signer));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.OctetSequenceKey;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.oauth2.jose.TestJwks;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link NimbusJwkSetEndpointFilter}.\n *\n * @author Joe Grandja\n */\npublic class NimbusJwkSetEndpointFilterTests {\n\n\tprivate static final String DEFAULT_JWK_SET_ENDPOINT_URI = \"/oauth2/jwks\";\n\n\tprivate List<JWK> jwkList;\n\n\tprivate JWKSource<SecurityContext> jwkSource;\n\n\tprivate NimbusJwkSetEndpointFilter filter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.jwkList = new ArrayList<>();\n\t\tthis.jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(new JWKSet(this.jwkList));\n\t\tthis.filter = new NimbusJwkSetEndpointFilter(this.jwkSource);\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwkSourceNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new NimbusJwkSetEndpointFilter(null))\n\t\t\t.withMessage(\"jwkSource cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwkSetEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new NimbusJwkSetEndpointFilter(this.jwkSource, null))\n\t\t\t.withMessage(\"jwkSetEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotJwkSetRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenJwkSetRequestPostThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_JWK_SET_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAsymmetricKeysThenJwkSetResponse() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tthis.jwkList.add(rsaJwk);\n\t\tECKey ecJwk = TestJwks.DEFAULT_EC_JWK;\n\t\tthis.jwkList.add(ecJwk);\n\n\t\tString requestUri = DEFAULT_JWK_SET_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\n\t\tJWKSet jwkSet = JWKSet.parse(response.getContentAsString());\n\t\tassertThat(jwkSet.getKeys()).hasSize(2);\n\n\t\tRSAKey rsaJwkResult = (RSAKey) jwkSet.getKeyByKeyId(rsaJwk.getKeyID());\n\t\tassertThat(rsaJwkResult).isNotNull();\n\t\tassertThat(rsaJwkResult.toRSAPublicKey()).isEqualTo(rsaJwk.toRSAPublicKey());\n\t\tassertThat(rsaJwkResult.toRSAPrivateKey()).isNull();\n\n\t\tECKey ecJwkResult = (ECKey) jwkSet.getKeyByKeyId(ecJwk.getKeyID());\n\t\tassertThat(ecJwkResult).isNotNull();\n\t\tassertThat(ecJwkResult.toECPublicKey()).isEqualTo(ecJwk.toECPublicKey());\n\t\tassertThat(ecJwkResult.toECPrivateKey()).isNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSymmetricKeysThenJwkSetResponseEmpty() throws Exception {\n\t\tOctetSequenceKey secretJwk = TestJwks.DEFAULT_SECRET_JWK;\n\t\tthis.jwkList.add(secretJwk);\n\n\t\tString requestUri = DEFAULT_JWK_SET_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\n\t\tJWKSet jwkSet = JWKSet.parse(response.getContentAsString());\n\t\tassertThat(jwkSet.getKeys()).isEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.nio.charset.StandardCharsets;\nimport java.text.MessageFormat;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2AuthorizationEndpointFilter}.\n *\n * @author Paurav Munshi\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n * @author Anoop Garlapati\n * @author Dmitriy Dubson\n * @author Greg Li\n * @since 7.0\n */\npublic class OAuth2AuthorizationEndpointFilterTests {\n\n\tprivate static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = \"/oauth2/authorize\";\n\n\tprivate static final String AUTHORIZATION_URI = \"https://provider.com/oauth2/authorize\";\n\n\tprivate static final String STATE = \"state\";\n\n\tprivate static final String REMOTE_ADDRESS = \"remote-address\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OAuth2AuthorizationEndpointFilter filter;\n\n\tprivate TestingAuthenticationToken principal;\n\n\tprivate OAuth2AuthorizationCode authorizationCode;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OAuth2AuthorizationEndpointFilter(this.authenticationManager);\n\t\tthis.principal = new TestingAuthenticationToken(\"principalName\", \"password\");\n\t\tthis.principal.setAuthenticated(true);\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(5, ChronoUnit.MINUTES);\n\t\tthis.authorizationCode = new OAuth2AuthorizationCode(\"code\", issuedAt, expiresAt);\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationEndpointFilter(null))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationEndpointFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"authorizationEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationDetailsSource(null))\n\t\t\t.withMessage(\"authenticationDetailsSource cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setSessionAuthenticationStrategyWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setSessionAuthenticationStrategy(null))\n\t\t\t.withMessage(\"sessionAuthenticationStrategy cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotAuthorizationRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMultipleRequestUriThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.REQUEST_URI, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(OAuth2ParameterNames.REQUEST_URI, OAuth2ParameterNames.REQUEST_URI);\n\t\t\t\t\trequest.addParameter(OAuth2ParameterNames.REQUEST_URI, \"request_uri_2\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMissingResponseTypeThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.RESPONSE_TYPE, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.removeParameter(OAuth2ParameterNames.RESPONSE_TYPE);\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t// gh-2226\n\t@Test\n\tpublic void doFilterWhenPostAuthorizationRequestMissingResponseTypeThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.RESPONSE_TYPE, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.setMethod(\"POST\");\n\t\t\t\t\trequest.removeParameter(OAuth2ParameterNames.RESPONSE_TYPE);\n\t\t\t\t\trequest.setQueryString(null);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMultipleResponseTypeThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.RESPONSE_TYPE, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(OAuth2ParameterNames.RESPONSE_TYPE, \"id_token\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestInvalidResponseTypeThenUnsupportedResponseTypeError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.RESPONSE_TYPE, OAuth2ErrorCodes.UNSUPPORTED_RESPONSE_TYPE, (request) -> {\n\t\t\t\t\trequest.setParameter(OAuth2ParameterNames.RESPONSE_TYPE, \"id_token\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMissingClientIdThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.CLIENT_ID, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.removeParameter(OAuth2ParameterNames.CLIENT_ID);\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMultipleClientIdThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.CLIENT_ID, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-2\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMultipleRedirectUriThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.REDIRECT_URI, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(OAuth2ParameterNames.REDIRECT_URI, \"https://example2.com\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMultipleScopeThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"scope2\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMultipleStateThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tOAuth2ParameterNames.STATE, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"state2\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationConsentRequestMissingStateThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationConsentRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.STATE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> request.removeParameter(OAuth2ParameterNames.STATE));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationConsentRequestMultipleStateThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationConsentRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.STATE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.STATE, \"state2\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMultipleCodeChallengeThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tPkceParameterNames.CODE_CHALLENGE, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(PkceParameterNames.CODE_CHALLENGE, \"code-challenge\");\n\t\t\t\t\trequest.addParameter(PkceParameterNames.CODE_CHALLENGE, \"another-code-challenge\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMultipleCodeChallengeMethodThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),\n\t\t\t\tPkceParameterNames.CODE_CHALLENGE_METHOD, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\t\t\t\trequest.addParameter(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationRequestMultiplePromptThenInvalidRequestError() throws Exception {\n\t\t// Setup OpenID Connect request\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(OidcScopes.OPENID);\n\t\t}).build();\n\t\tdoFilterWhenAuthorizationRequestInvalidParameterThenError(registeredClient, \"prompt\",\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(\"prompt\", \"none\");\n\t\t\t\t\trequest.addParameter(\"prompt\", \"login\");\n\t\t\t\t\tupdateQueryString(request);\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestAuthenticationExceptionThenErrorResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris((redirectUris) -> {\n\t\t\tredirectUris.clear();\n\t\t\tredirectUris.add(\"https://example.com?param=encoded%20parameter%20value\");\n\t\t}).build();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), \"client state\", registeredClient.getScopes(),\n\t\t\t\tnull);\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, \"error description\", \"error uri\");\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willThrow(new OAuth2AuthorizationCodeRequestAuthenticationException(error,\n\t\t\t\t\tauthorizationCodeRequestAuthentication));\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\n\t\t\t\t\"https://example.com?param=encoded%20parameter%20value&error=invalid_request&error_description=error%20description&error_uri=error%20uri&state=client%20state\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.principal);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);\n\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any())).willReturn(authorizationCodeRequestAuthentication);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());\n\t\tauthorizationCodeRequestAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationConverter).convert(any());\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());\n\t\tauthorizationCodeRequestAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult);\n\n\t\tAuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(),\n\t\t\t\tsame(authorizationCodeRequestAuthenticationResult));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);\n\t\tOAuth2Error error = new OAuth2Error(\"errorCode\", \"errorDescription\", \"errorUri\");\n\t\tOAuth2AuthorizationCodeRequestAuthenticationException authenticationException = new OAuth2AuthorizationCodeRequestAuthenticationException(\n\t\t\t\terror, authorizationCodeRequestAuthentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(authenticationException);\n\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), same(authenticationException));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomSessionAuthenticationStrategyThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());\n\t\tauthorizationCodeRequestAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult);\n\n\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class);\n\t\tthis.filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(sessionAuthenticationStrategy).onAuthentication(same(authorizationCodeRequestAuthenticationResult),\n\t\t\t\tany(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\n\t\tAuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\t\tWebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request);\n\t\tgiven(authenticationDetailsSource.buildDetails(request)).willReturn(webAuthenticationDetails);\n\t\tthis.filter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());\n\t\tauthorizationCodeRequestAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationDetailsSource).buildDetails(any());\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestConsentRequiredWithCustomConsentUriThenRedirectConsentResponse()\n\t\t\tthrows Exception {\n\t\tSet<String> requestedScopes = new HashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.addAll(requestedScopes);\n\t\t}).build();\n\t\t// No scopes previously approved\n\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, new HashSet<>(), null);\n\t\tauthorizationConsentAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationConsentAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.setConsentPage(\"/oauth2/custom-consent\");\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getRedirectedUrl())\n\t\t\t.isEqualTo(\"http://localhost/oauth2/custom-consent?scope=scope1%20scope2&client_id=client-1&state=state\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestConsentRequiredThenConsentResponse() throws Exception {\n\t\tSet<String> requestedScopes = new HashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.addAll(requestedScopes);\n\t\t}).build();\n\t\t// No scopes previously approved\n\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, new HashSet<>(), null);\n\t\tauthorizationConsentAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationConsentAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(response.getContentType().equals(new MediaType(\"text\", \"html\", StandardCharsets.UTF_8).toString()));\n\t\tfor (String requestedScope : requestedScopes) {\n\t\t\tassertThat(response.getContentAsString()).contains(scopeCheckbox(requestedScope));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestConsentRequiredWithPreviouslyApprovedThenConsentResponse()\n\t\t\tthrows Exception {\n\t\tSet<String> approvedScopes = new HashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\t\tSet<String> requestedScopes = new HashSet<>(Arrays.asList(\"scope3\", \"scope4\"));\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.addAll(approvedScopes);\n\t\t\tscopes.addAll(requestedScopes);\n\t\t}).build();\n\t\tOAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = new OAuth2AuthorizationConsentAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, STATE, approvedScopes, null);\n\t\tauthorizationConsentAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationConsentAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(response.getContentType().equals(new MediaType(\"text\", \"html\", StandardCharsets.UTF_8).toString()));\n\t\tfor (String requestedScope : requestedScopes) {\n\t\t\tassertThat(response.getContentAsString()).contains(scopeCheckbox(requestedScope));\n\t\t}\n\t\tfor (String approvedScope : approvedScopes) {\n\t\t\tassertThat(response.getContentAsString()).contains(disabledScopeCheckbox(approvedScope));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestAuthenticatedThenAuthorizationResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris((redirectUris) -> {\n\t\t\tredirectUris.clear();\n\t\t\tredirectUris.add(\"https://example.com?param=encoded%20parameter%20value\");\n\t\t}).build();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), \"client state\", registeredClient.getScopes());\n\t\tauthorizationCodeRequestAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\trequest.addParameter(\"custom-param\", \"custom-value-1\", \"custom-value-2\");\n\t\tupdateQueryString(request);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tArgumentCaptor<OAuth2AuthorizationCodeRequestAuthenticationToken> authorizationCodeRequestAuthenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizationCodeRequestAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(authorizationCodeRequestAuthenticationCaptor.capture());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(authorizationCodeRequestAuthenticationCaptor.getValue().getDetails())\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebAuthenticationDetails.class))\n\t\t\t.extracting(WebAuthenticationDetails::getRemoteAddress)\n\t\t\t.isEqualTo(REMOTE_ADDRESS);\n\n\t\t// Assert that multi-valued request parameters are preserved\n\t\tassertThat(authorizationCodeRequestAuthenticationCaptor.getValue().getAdditionalParameters())\n\t\t\t.extracting((params) -> params.get(\"custom-param\"))\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(String[].class))\n\t\t\t.isEqualTo(new String[] { \"custom-value-1\", \"custom-value-2\" });\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getRedirectedUrl())\n\t\t\t.isEqualTo(\"https://example.com?param=encoded%20parameter%20value&code=code&state=client%20state\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPostAuthorizationRequestAuthenticatedThenAuthorizationResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris((redirectUris) -> {\n\t\t\tredirectUris.clear();\n\t\t\tredirectUris.add(\"https://example.com?param=encoded%20parameter%20value\");\n\t\t}).build();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), \"client state\", registeredClient.getScopes());\n\t\tauthorizationCodeRequestAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setQueryString(null);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any(OAuth2AuthorizationCodeRequestAuthenticationToken.class));\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getRedirectedUrl())\n\t\t\t.isEqualTo(\"https://example.com?param=encoded%20parameter%20value&code=code&state=client%20state\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationRequestAuthenticatedThenAuthorizationResponse() throws Exception {\n\t\t// Setup OpenID Connect request\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(OidcScopes.OPENID);\n\t\t}).build();\n\t\tOAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode,\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());\n\t\tauthorizationCodeRequestAuthenticationResult.setAuthenticated(true);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createAuthorizationRequest(registeredClient);\n\t\trequest.setMethod(\"POST\"); // OpenID Connect supports POST method\n\t\trequest.setQueryString(null);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any(OAuth2AuthorizationCodeRequestAuthenticationToken.class));\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getRedirectedUrl())\n\t\t\t.isEqualTo(request.getParameter(OAuth2ParameterNames.REDIRECT_URI) + \"?code=code&state=state\");\n\t}\n\n\tprivate void doFilterWhenAuthorizationRequestInvalidParameterThenError(RegisteredClient registeredClient,\n\t\t\tString parameterName, String errorCode, Consumer<MockHttpServletRequest> requestConsumer) throws Exception {\n\n\t\tdoFilterWhenRequestInvalidParameterThenError(createAuthorizationRequest(registeredClient), parameterName,\n\t\t\t\terrorCode, requestConsumer);\n\t}\n\n\tprivate void doFilterWhenAuthorizationConsentRequestInvalidParameterThenError(RegisteredClient registeredClient,\n\t\t\tString parameterName, String errorCode, Consumer<MockHttpServletRequest> requestConsumer) throws Exception {\n\n\t\tdoFilterWhenRequestInvalidParameterThenError(createAuthorizationConsentRequest(registeredClient), parameterName,\n\t\t\t\terrorCode, requestConsumer);\n\t}\n\n\tprivate void doFilterWhenRequestInvalidParameterThenError(MockHttpServletRequest request, String parameterName,\n\t\t\tString errorCode, Consumer<MockHttpServletRequest> requestConsumer) throws Exception {\n\n\t\trequestConsumer.accept(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tassertThat(response.getErrorMessage()).isEqualTo(\"[\" + errorCode + \"] OAuth 2.0 Parameter: \" + parameterName);\n\t}\n\n\tprivate static MockHttpServletRequest createAuthorizationRequest(RegisteredClient registeredClient) {\n\t\tString requestUri = DEFAULT_AUTHORIZATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\n\t\trequest.addParameter(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2AuthorizationResponseType.CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\trequest.addParameter(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next());\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"state\");\n\t\tupdateQueryString(request);\n\n\t\treturn request;\n\t}\n\n\tprivate static MockHttpServletRequest createAuthorizationConsentRequest(RegisteredClient registeredClient) {\n\t\tString requestUri = DEFAULT_AUTHORIZATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\tregisteredClient.getScopes().forEach((scope) -> request.addParameter(OAuth2ParameterNames.SCOPE, scope));\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"state\");\n\n\t\treturn request;\n\t}\n\n\tprivate static void updateQueryString(MockHttpServletRequest request) {\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(request.getRequestURI());\n\t\trequest.getParameterMap().forEach((key, values) -> {\n\t\t\tif (values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\turiBuilder.queryParam(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\trequest.setQueryString(uriBuilder.build().getQuery());\n\t}\n\n\tprivate static String scopeCheckbox(String scope) {\n\t\treturn MessageFormat.format(\n\t\t\t\t\"<input class=\\\"form-check-input\\\" type=\\\"checkbox\\\" name=\\\"scope\\\" value=\\\"{0}\\\" id=\\\"{0}\\\">\", scope);\n\t}\n\n\tprivate static String disabledScopeCheckbox(String scope) {\n\t\treturn MessageFormat.format(\n\t\t\t\t\"<input class=\\\"form-check-input\\\" type=\\\"checkbox\\\" name=\\\"scope\\\" id=\\\"{0}\\\" checked disabled>\",\n\t\t\t\tscope);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.web.util.InvalidUrlException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2AuthorizationServerMetadataEndpointFilter}.\n *\n * @author Daniel Garnier-Moiroux\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationServerMetadataEndpointFilterTests {\n\n\tprivate static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = \"/.well-known/oauth-authorization-server\";\n\n\tprivate final OAuth2AuthorizationServerMetadataEndpointFilter filter = new OAuth2AuthorizationServerMetadataEndpointFilter();\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void setAuthorizationServerMetadataCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthorizationServerMetadataCustomizer(null))\n\t\t\t.withMessage(\"authorizationServerMetadataCustomizer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotAuthorizationServerMetadataRequestThenNotProcessed() throws Exception {\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));\n\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationServerMetadataRequestPostThenNotProcessed() throws Exception {\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));\n\n\t\tString requestUri = DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationServerMetadataRequestThenMetadataResponse() throws Exception {\n\t\tString issuer = \"https://example.com\";\n\t\tString authorizationEndpoint = \"/oauth2/v1/authorize\";\n\t\tString pushedAuthorizationRequestEndpoint = \"/oauth2/v1/par\";\n\t\tString tokenEndpoint = \"/oauth2/v1/token\";\n\t\tString jwkSetEndpoint = \"/oauth2/v1/jwks\";\n\t\tString tokenRevocationEndpoint = \"/oauth2/v1/revoke\";\n\t\tString tokenIntrospectionEndpoint = \"/oauth2/v1/introspect\";\n\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(issuer)\n\t\t\t.authorizationEndpoint(authorizationEndpoint)\n\t\t\t.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint)\n\t\t\t.tokenEndpoint(tokenEndpoint)\n\t\t\t.jwkSetEndpoint(jwkSetEndpoint)\n\t\t\t.tokenRevocationEndpoint(tokenRevocationEndpoint)\n\t\t\t.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint)\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\n\t\tString requestUri = DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tString authorizationServerMetadataResponse = response.getContentAsString();\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"issuer\\\":\\\"https://example.com\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"authorization_endpoint\\\":\\\"https://example.com/oauth2/v1/authorize\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse).doesNotContain(\"\\\"pushed_authorization_request_endpoint\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse).doesNotContain(\"\\\"device_authorization_endpoint\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"token_endpoint\\\":\\\"https://example.com/oauth2/v1/token\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\n\t\t\t\t\"\\\"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\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"jwks_uri\\\":\\\"https://example.com/oauth2/v1/jwks\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"response_types_supported\\\":[\\\"code\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\n\t\t\t\t\"\\\"grant_types_supported\\\":[\\\"authorization_code\\\",\\\"client_credentials\\\",\\\"refresh_token\\\",\\\"urn:ietf:params:oauth:grant-type:token-exchange\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"revocation_endpoint\\\":\\\"https://example.com/oauth2/v1/revoke\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\n\t\t\t\t\"\\\"revocation_endpoint_auth_methods_supported\\\":[\\\"client_secret_basic\\\",\\\"client_secret_post\\\",\\\"client_secret_jwt\\\",\\\"private_key_jwt\\\",\\\"tls_client_auth\\\",\\\"self_signed_tls_client_auth\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse)\n\t\t\t.contains(\"\\\"introspection_endpoint\\\":\\\"https://example.com/oauth2/v1/introspect\\\"\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\n\t\t\t\t\"\\\"introspection_endpoint_auth_methods_supported\\\":[\\\"client_secret_basic\\\",\\\"client_secret_post\\\",\\\"client_secret_jwt\\\",\\\"private_key_jwt\\\",\\\"tls_client_auth\\\",\\\"self_signed_tls_client_auth\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"code_challenge_methods_supported\\\":[\\\"S256\\\"]\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\"\\\"tls_client_certificate_bound_access_tokens\\\":true\");\n\t\tassertThat(authorizationServerMetadataResponse).contains(\n\t\t\t\t\"\\\"dpop_signing_alg_values_supported\\\":[\\\"RS256\\\",\\\"RS384\\\",\\\"RS512\\\",\\\"PS256\\\",\\\"PS384\\\",\\\"PS512\\\",\\\"ES256\\\",\\\"ES384\\\",\\\"ES512\\\"]\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationServerSettingsWithInvalidIssuerThenThrowIllegalArgumentException() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder()\n\t\t\t.issuer(\"https://this is an invalid URL\")\n\t\t\t.build();\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));\n\n\t\tString requestUri = DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tassertThatExceptionOfType(InvalidUrlException.class)\n\t\t\t.isThrownBy(() -> this.filter.doFilter(request, response, filterChain));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2ClientAuthenticationFilter}.\n *\n * @author Patryk Kostrzewa\n * @author Joe Grandja\n */\npublic class OAuth2ClientAuthenticationFilterTests {\n\n\tprivate String filterProcessesUrl = \"/oauth2/token\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate OAuth2ClientAuthenticationFilter filter;\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(HttpMethod.POST, this.filterProcessesUrl);\n\t\tthis.filter = new OAuth2ClientAuthenticationFilter(this.authenticationManager, this.requestMatcher);\n\t\tthis.authenticationConverter = mock(AuthenticationConverter.class);\n\t\tthis.filter.setAuthenticationConverter(this.authenticationConverter);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2ClientAuthenticationFilter(null, this.requestMatcher))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenRequestMatcherNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2ClientAuthenticationFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"requestMatcher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestDoesNotMatchThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tverifyNoInteractions(this.authenticationConverter);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestMatchesAndEmptyCredentialsThenNotProcessed() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", this.filterProcessesUrl);\n\t\trequest.setServletPath(this.filterProcessesUrl);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tverifyNoInteractions(this.authenticationManager);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestMatchesAndInvalidCredentialsThenInvalidRequestError() throws Exception {\n\t\tgiven(this.authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST));\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", this.filterProcessesUrl);\n\t\trequest.setServletPath(this.filterProcessesUrl);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t// gh-889\n\t@Test\n\tpublic void doFilterWhenRequestMatchesAndClientIdContainsNonPrintableASCIIThenInvalidRequestError()\n\t\t\tthrows Exception {\n\t\t// Hex 00 -> null\n\t\tString clientId = new String(Hex.decode(\"00\"), StandardCharsets.UTF_8);\n\t\tassertWhenInvalidClientIdThenInvalidRequestError(clientId);\n\n\t\t// Hex 0a61 -> line feed + a\n\t\tclientId = new String(Hex.decode(\"0a61\"), StandardCharsets.UTF_8);\n\t\tassertWhenInvalidClientIdThenInvalidRequestError(clientId);\n\n\t\t// Hex 1b -> escape\n\t\tclientId = new String(Hex.decode(\"1b\"), StandardCharsets.UTF_8);\n\t\tassertWhenInvalidClientIdThenInvalidRequestError(clientId);\n\n\t\t// Hex 1b61 -> escape + a\n\t\tclientId = new String(Hex.decode(\"1b61\"), StandardCharsets.UTF_8);\n\t\tassertWhenInvalidClientIdThenInvalidRequestError(clientId);\n\t}\n\n\tprivate void assertWhenInvalidClientIdThenInvalidRequestError(String clientId) throws Exception {\n\t\tgiven(this.authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new OAuth2ClientAuthenticationToken(clientId, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\t\t\"secret\", null));\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", this.filterProcessesUrl);\n\t\trequest.setServletPath(this.filterProcessesUrl);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\t\tverifyNoInteractions(this.authenticationManager);\n\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestMatchesAndBadCredentialsThenInvalidClientError() throws Exception {\n\t\tgiven(this.authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new OAuth2ClientAuthenticationToken(\"clientId\", ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\t\t\t\"invalid-secret\", null));\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class)))\n\t\t\t.willThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT));\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", this.filterProcessesUrl);\n\t\trequest.setServletPath(this.filterProcessesUrl);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(this.authenticationManager).authenticate(any());\n\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestMatchesAndValidCredentialsThenProcessed() throws Exception {\n\t\tfinal String remoteAddress = \"remote-address\";\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tgiven(this.authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new OAuth2ClientAuthenticationToken(registeredClient.getClientId(),\n\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null));\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class)))\n\t\t\t.willReturn(new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()));\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", this.filterProcessesUrl);\n\t\trequest.setServletPath(this.filterProcessesUrl);\n\t\trequest.setRemoteAddr(remoteAddress);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isInstanceOf(OAuth2ClientAuthenticationToken.class);\n\t\tassertThat(((OAuth2ClientAuthenticationToken) authentication).getRegisteredClient())\n\t\t\t.isEqualTo(registeredClient);\n\n\t\tArgumentCaptor<OAuth2ClientAuthenticationToken> authenticationRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2ClientAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(authenticationRequestCaptor.capture());\n\t\tassertThat(authenticationRequestCaptor).extracting(ArgumentCaptor::getValue)\n\t\t\t.extracting(OAuth2ClientAuthenticationToken::getDetails)\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebAuthenticationDetails.class))\n\t\t\t.extracting(WebAuthenticationDetails::getRemoteAddress)\n\t\t\t.isEqualTo(remoteAddress);\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientRegistrationEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.util.Collections;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpRequest;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.TestJwsHeaders;\nimport org.springframework.security.oauth2.jwt.TestJwtClaimsSets;\nimport org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.http.converter.OAuth2ClientRegistrationHttpMessageConverter;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2ClientRegistrationEndpointFilter}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2ClientRegistrationEndpointFilterTests {\n\n\tprivate static final String DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI = \"/oauth2/register\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OAuth2ClientRegistrationEndpointFilter filter;\n\n\tprivate final HttpMessageConverter<OAuth2ClientRegistration> clientRegistrationHttpMessageConverter = new OAuth2ClientRegistrationHttpMessageConverter();\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OAuth2ClientRegistrationEndpointFilter(this.authenticationManager);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2ClientRegistrationEndpointFilter(null))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2ClientRegistrationEndpointFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"clientRegistrationEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotClientRegistrationRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestGetThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestInvalidThenInvalidRequestError() throws Exception {\n\t\tString requestUri = DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setContent(\"invalid content\".getBytes());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\tassertThat(error.getDescription()).startsWith(\"OAuth 2.0 Client Registration Error: \");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestInvalidTokenThenUnauthorizedError() throws Exception {\n\t\tdoFilterWhenClientRegistrationRequestInvalidThenError(OAuth2ErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestInsufficientTokenScopeThenForbiddenError() throws Exception {\n\t\tdoFilterWhenClientRegistrationRequestInvalidThenError(OAuth2ErrorCodes.INSUFFICIENT_SCOPE,\n\t\t\t\tHttpStatus.FORBIDDEN);\n\t}\n\n\tprivate void doFilterWhenClientRegistrationRequestInvalidThenError(String errorCode, HttpStatus status)\n\t\t\tthrows Exception {\n\t\tJwt jwt = createJwt(\"client.create\");\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(new OAuth2AuthenticationException(errorCode));\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistrationRequest = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tString requestUri = DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\twriteClientRegistrationRequest(request, clientRegistrationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(status.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientRegistrationRequestValidThenSuccessResponse() throws Exception {\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration expectedClientRegistrationResponse = createClientRegistration();\n\n\t\tOAuth2ClientRegistration clientRegistrationRequest = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(expectedClientRegistrationResponse.getClientName())\n\t\t\t\t.redirectUris((redirectUris) -> redirectUris.addAll(expectedClientRegistrationResponse.getRedirectUris()))\n\t\t\t\t.grantTypes((grantTypes) -> grantTypes.addAll(expectedClientRegistrationResponse.getGrantTypes()))\n\t\t\t\t.scopes((scopes) -> scopes.addAll(expectedClientRegistrationResponse.getScopes()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt jwt = createJwt(\"client.create\");\n\t\tJwtAuthenticationToken principal = new JwtAuthenticationToken(jwt,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"SCOPE_client.create\"));\n\n\t\tOAuth2ClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, expectedClientRegistrationResponse);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(clientRegistrationAuthenticationResult);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tString requestUri = DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\twriteClientRegistrationRequest(request, clientRegistrationRequest);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value());\n\t\tOAuth2ClientRegistration clientRegistrationResponse = readClientRegistrationResponse(response);\n\t\tassertThat(clientRegistrationResponse.getClientId())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientId());\n\t\tassertThat(clientRegistrationResponse.getClientIdIssuedAt()).isBetween(\n\t\t\t\texpectedClientRegistrationResponse.getClientIdIssuedAt().minusSeconds(1),\n\t\t\t\texpectedClientRegistrationResponse.getClientIdIssuedAt().plusSeconds(1));\n\t\tassertThat(clientRegistrationResponse.getClientSecret())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientSecret());\n\t\tassertThat(clientRegistrationResponse.getClientSecretExpiresAt())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientSecretExpiresAt());\n\t\tassertThat(clientRegistrationResponse.getClientName())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getClientName());\n\t\tassertThat(clientRegistrationResponse.getRedirectUris())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getRedirectUris());\n\t\tassertThat(clientRegistrationResponse.getGrantTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getGrantTypes());\n\t\tassertThat(clientRegistrationResponse.getResponseTypes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getResponseTypes());\n\t\tassertThat(clientRegistrationResponse.getScopes())\n\t\t\t.containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getScopes());\n\t\tassertThat(clientRegistrationResponse.getTokenEndpointAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws ServletException, IOException {\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tString requestUri = DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationConverter).convert(request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tOAuth2ClientRegistration expectedClientRegistrationResponse = createClientRegistration();\n\t\tAuthentication principal = new TestingAuthenticationToken(\"principal\", \"Credentials\");\n\n\t\tOAuth2ClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = new OAuth2ClientRegistrationAuthenticationToken(\n\t\t\t\tprincipal, expectedClientRegistrationResponse);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(clientRegistrationAuthenticationResult);\n\t\tAuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(successHandler);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\t// @formatter:off\n\t\tOAuth2ClientRegistration clientRegistrationRequest = OAuth2ClientRegistration.builder()\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tString requestUri = DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\twriteClientRegistrationRequest(request, clientRegistrationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(successHandler).onAuthenticationSuccess(request, response, clientRegistrationAuthenticationResult);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tString requestUri = DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setContent(\"invalid content\".getBytes());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(eq(request), eq(response),\n\t\t\t\tany(OAuth2AuthenticationException.class));\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n\tprivate void writeClientRegistrationRequest(MockHttpServletRequest request,\n\t\t\tOAuth2ClientRegistration clientRegistration) throws Exception {\n\t\tMockClientHttpRequest httpRequest = new MockClientHttpRequest();\n\t\tthis.clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpRequest);\n\t\trequest.setContent(httpRequest.getBodyAsBytes());\n\t}\n\n\tprivate OAuth2ClientRegistration readClientRegistrationResponse(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.clientRegistrationHttpMessageConverter.read(OAuth2ClientRegistration.class, httpResponse);\n\t}\n\n\tprivate static OAuth2ClientRegistration createClientRegistration() {\n\t\t// @formatter:off\n\t\treturn OAuth2ClientRegistration.builder()\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientIdIssuedAt(Instant.now())\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientName(\"client-name\")\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())\n\t\t\t\t.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t\t\t.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())\n\t\t\t\t.responseType(OAuth2AuthorizationResponseType.CODE.getValue())\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.scope(\"scope2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static Jwt createJwt(String scope) {\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = TestJwsHeaders.jwsHeader()\n\t\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet()\n\t\t\t\t.claim(OAuth2ParameterNames.SCOPE, Collections.singleton(scope))\n\t\t\t\t.build();\n\t\tJwt jwt = Jwt.withTokenValue(\"jwt-access-token\")\n\t\t\t\t.headers((headers) -> headers.putAll(jwsHeader.getHeaders()))\n\t\t\t\t.claims((claims) -> claims.putAll(jwtClaimsSet.getClaims()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\treturn jwt;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Map;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2DeviceAuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2DeviceAuthorizationResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2DeviceAuthorizationEndpointFilter}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceAuthorizationEndpointFilterTests {\n\n\tprivate static final String ISSUER_URI = \"https://provider.com:8090\";\n\n\tprivate static final String REMOTE_ADDRESS = \"remote-address\";\n\n\tprivate static final String AUTHORIZATION_URI = \"/oauth2/device_authorization\";\n\n\tprivate static final String VERIFICATION_URI = \"/oauth2/device_verification\";\n\n\tprivate static final String CLIENT_ID = \"client-1\";\n\n\tprivate static final String DEVICE_CODE = \"EfYu_0jEL\";\n\n\tprivate static final String USER_CODE = \"BCDF-GHJK\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OAuth2DeviceAuthorizationEndpointFilter filter;\n\n\tprivate final HttpMessageConverter<OAuth2DeviceAuthorizationResponse> deviceAuthorizationHttpResponseConverter = new OAuth2DeviceAuthorizationResponseHttpMessageConverter();\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OAuth2DeviceAuthorizationEndpointFilter(this.authenticationManager);\n\t\tmockAuthorizationServerContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationMangerIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceAuthorizationEndpointFilter(null))\n\t\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenDeviceAuthorizationEndpointUriIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceAuthorizationEndpointFilter(this.authenticationManager, null))\n\t\t\t\t.withMessage(\"deviceAuthorizationEndpointUri cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setAuthenticationDetailsSource(null))\n\t\t\t\t.withMessage(\"authenticationDetailsSource cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setVerificationUriWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setVerificationUri(null))\n\t\t\t\t.withMessage(\"verificationUri cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotDeviceAuthorizationRequestThenNotProcessed() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(HttpMethod.GET.name(), \"/path\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverify(filterChain).doFilter(request, response);\n\t\tverifyNoInteractions(this.authenticationManager);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDeviceAuthorizationRequestGetThenNotProcessed() throws Exception {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.setMethod(HttpMethod.GET.name());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverify(filterChain).doFilter(request, response);\n\t\tverifyNoInteractions(this.authenticationManager);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDeviceAuthorizationRequestThenDeviceAuthorizationResponse() throws Exception {\n\t\tAuthentication authenticationResult = createAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthentication clientPrincipal = (Authentication) authenticationResult.getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tArgumentCaptor<OAuth2DeviceAuthorizationRequestAuthenticationToken> deviceAuthorizationRequestAuthenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2DeviceAuthorizationRequestAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(deviceAuthorizationRequestAuthenticationCaptor.capture());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = deviceAuthorizationRequestAuthenticationCaptor\n\t\t\t.getValue();\n\t\tassertThat(deviceAuthorizationRequestAuthentication.getAuthorizationUri()).endsWith(AUTHORIZATION_URI);\n\t\tassertThat(deviceAuthorizationRequestAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(deviceAuthorizationRequestAuthentication.getScopes()).isEmpty();\n\t\tassertThat(deviceAuthorizationRequestAuthentication.getAdditionalParameters()).containsExactly(\n\t\t\t\tMap.entry(\"custom-param-1\", \"custom-value-1\"),\n\t\t\t\tMap.entry(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\t\t// @formatter:off\n\t\tassertThat(deviceAuthorizationRequestAuthentication.getDetails())\n\t\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebAuthenticationDetails.class))\n\t\t\t\t.extracting(WebAuthenticationDetails::getRemoteAddress)\n\t\t\t\t.isEqualTo(REMOTE_ADDRESS);\n\t\t// @formatter:on\n\n\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse = readDeviceAuthorizationResponse(response);\n\t\tString verificationUri = ISSUER_URI + VERIFICATION_URI;\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUri()).isEqualTo(verificationUri);\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUriComplete())\n\t\t\t.isEqualTo(\"%s?%s=%s\".formatted(verificationUri, OAuth2ParameterNames.USER_CODE, USER_CODE));\n\t\tOAuth2DeviceCode deviceCode = deviceAuthorizationResponse.getDeviceCode();\n\t\tassertThat(deviceCode.getTokenValue()).isEqualTo(DEVICE_CODE);\n\t\tassertThat(deviceCode.getExpiresAt()).isAfter(deviceCode.getIssuedAt());\n\t\tOAuth2UserCode userCode = deviceAuthorizationResponse.getUserCode();\n\t\tassertThat(userCode.getTokenValue()).isEqualTo(USER_CODE);\n\t\tassertThat(deviceCode.getExpiresAt()).isAfter(deviceCode.getIssuedAt());\n\t}\n\n\t// gh-1714\n\t@Test\n\tpublic void doFilterWhenDeviceAuthorizationRequestWithContextPathThenVerificationUriIncludesContextPath()\n\t\t\tthrows Exception {\n\t\tAuthentication authenticationResult = createAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthentication clientPrincipal = (Authentication) authenticationResult.getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.setContextPath(\"/contextPath\");\n\t\trequest.setRequestURI(\"/contextPath\" + AUTHORIZATION_URI);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(this.authenticationManager).authenticate(any(OAuth2DeviceAuthorizationRequestAuthenticationToken.class));\n\t\tverifyNoInteractions(filterChain);\n\n\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse = readDeviceAuthorizationResponse(response);\n\t\tString verificationUri = ISSUER_URI + \"/contextPath\" + VERIFICATION_URI;\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUri()).isEqualTo(verificationUri);\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUriComplete())\n\t\t\t.isEqualTo(\"%s?%s=%s\".formatted(verificationUri, OAuth2ParameterNames.USER_CODE, USER_CODE));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenInvalidRequestErrorThenBadRequest() throws Exception {\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tOAuth2AuthenticationException authenticationException = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, \"Invalid request\", \"error-uri\"));\n\t\tgiven(authenticationConverter.convert(any(HttpServletRequest.class))).willThrow(authenticationException);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\n\t\tverify(authenticationConverter).convert(request);\n\t\tverifyNoInteractions(filterChain, this.authenticationManager);\n\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\tassertThat(error.getDescription()).isEqualTo(\"Invalid request\");\n\t\tassertThat(error.getUri()).isEqualTo(\"error-uri\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomDeviceAuthorizationEndpointUriThenUsed() throws Exception {\n\t\tAuthentication authenticationResult = createAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthentication clientPrincipal = (Authentication) authenticationResult.getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.setRequestURI(\"/device\");\n\t\trequest.setServletPath(\"/device\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter = new OAuth2DeviceAuthorizationEndpointFilter(this.authenticationManager, \"/device\");\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationConverterSetThenUsed() throws Exception {\n\t\tAuthentication authenticationResult = createAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthentication clientPrincipal = (Authentication) authenticationResult.getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authenticationRequest = new OAuth2DeviceAuthorizationRequestAuthenticationToken(\n\t\t\t\tclientPrincipal, AUTHORIZATION_URI, null, null);\n\t\tgiven(authenticationConverter.convert(any(HttpServletRequest.class))).willReturn(authenticationRequest);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(authenticationConverter).convert(request);\n\t\tverify(this.authenticationManager).authenticate(authenticationRequest);\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationDetailsSourceSetThenUsed() throws Exception {\n\t\tAuthentication authenticationResult = createAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthentication clientPrincipal = (Authentication) authenticationResult.getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tAuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\t\tgiven(authenticationDetailsSource.buildDetails(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new WebAuthenticationDetails(request));\n\t\tthis.filter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(authenticationDetailsSource).buildDetails(request);\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationSuccessHandlerSetThenUsed() throws Exception {\n\t\tAuthentication authenticationResult = createAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthentication clientPrincipal = (Authentication) authenticationResult.getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tAuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(request, response, authenticationResult);\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationFailureHandlerSetThenUsed() throws Exception {\n\t\tOAuth2AuthenticationException authenticationException = new OAuth2AuthenticationException(\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST);\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willThrow(authenticationException);\n\n\t\tAuthentication clientPrincipal = (Authentication) createAuthentication().getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(request, response, authenticationException);\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\tprivate OAuth2DeviceAuthorizationResponse readDeviceAuthorizationResponse(MockHttpServletResponse response)\n\t\t\tthrows IOException {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.deviceAuthorizationHttpResponseConverter.read(OAuth2DeviceAuthorizationResponse.class,\n\t\t\t\thttpResponse);\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws IOException {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n\tprivate static void mockAuthorizationServerContext() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\t\tTestAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(\n\t\t\t\tauthorizationServerSettings, () -> ISSUER_URI);\n\t\tAuthorizationServerContextHolder.setContext(authorizationServerContext);\n\t}\n\n\tprivate static void mockSecurityContext(Authentication clientPrincipal) {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t}\n\n\tprivate static MockHttpServletRequest createRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.POST.name());\n\t\trequest.setRequestURI(AUTHORIZATION_URI);\n\t\trequest.setServletPath(AUTHORIZATION_URI);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"provider.com\");\n\t\trequest.setServerPort(8090);\n\t\treturn request;\n\t}\n\n\tprivate static OAuth2DeviceAuthorizationRequestAuthenticationToken createAuthentication() {\n\t\tTestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(CLIENT_ID, null);\n\t\treturn new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, null, createDeviceCode(),\n\t\t\t\tcreateUserCode());\n\t}\n\n\tprivate static OAuth2DeviceCode createDeviceCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2DeviceCode(DEVICE_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n\tprivate static OAuth2UserCode createUserCode() {\n\t\tInstant issuedAt = Instant.now();\n\t\treturn new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.nio.charset.StandardCharsets;\nimport java.text.MessageFormat;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2DeviceVerificationEndpointFilter}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceVerificationEndpointFilterTests {\n\n\tprivate static final String ISSUER_URI = \"https://provider.com\";\n\n\tprivate static final String REMOTE_ADDRESS = \"remote-address\";\n\n\tprivate static final String AUTHORIZATION_URI = \"/oauth2/device_authorization\";\n\n\tprivate static final String VERIFICATION_URI = \"/oauth2/device_verification\";\n\n\tprivate static final String CLIENT_ID = \"client-1\";\n\n\tprivate static final String STATE = \"12345\";\n\n\tprivate static final String USER_CODE = \"BCDF-GHJK\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OAuth2DeviceVerificationEndpointFilter filter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OAuth2DeviceVerificationEndpointFilter(this.authenticationManager);\n\t\tmockAuthorizationServerContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceVerificationEndpointFilter(null))\n\t\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenDeviceVerificationEndpointUriIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OAuth2DeviceVerificationEndpointFilter(this.authenticationManager, null))\n\t\t\t\t.withMessage(\"deviceVerificationEndpointUri cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setAuthenticationDetailsSource(null))\n\t\t\t\t.withMessage(\"authenticationDetailsSource cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotDeviceVerificationRequestThenNotProcessed() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(HttpMethod.GET.name(), \"/path\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverify(filterChain).doFilter(request, response);\n\t\tverifyNoInteractions(this.authenticationManager);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDeviceAuthorizationConsentRequestThenSuccess() throws Exception {\n\t\tAuthentication authenticationResult = createDeviceVerificationAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthentication clientPrincipal = (Authentication) authenticationResult.getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.setMethod(HttpMethod.POST.name());\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"scope-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"scope-2\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo(\"/?success\");\n\n\t\tArgumentCaptor<OAuth2DeviceAuthorizationConsentAuthenticationToken> authenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2DeviceAuthorizationConsentAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(authenticationCaptor.capture());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken deviceAuthorizationConsentAuthentication = authenticationCaptor\n\t\t\t.getValue();\n\t\tassertThat(deviceAuthorizationConsentAuthentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);\n\t\tassertThat(deviceAuthorizationConsentAuthentication.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(deviceAuthorizationConsentAuthentication.getPrincipal())\n\t\t\t.isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(deviceAuthorizationConsentAuthentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(deviceAuthorizationConsentAuthentication.getScopes()).containsExactly(\"scope-1\", \"scope-2\");\n\t\tassertThat(deviceAuthorizationConsentAuthentication.getAdditionalParameters()).containsExactly(\n\t\t\t\tMap.entry(\"custom-param-1\", \"custom-value-1\"),\n\t\t\t\tMap.entry(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDeviceVerificationRequestAndConsentNotRequiredThenSuccess() throws Exception {\n\t\tAuthentication authenticationResult = createDeviceVerificationAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthentication clientPrincipal = (Authentication) authenticationResult.getPrincipal();\n\t\tmockSecurityContext(clientPrincipal);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo(\"/?success\");\n\n\t\tArgumentCaptor<OAuth2DeviceVerificationAuthenticationToken> authenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2DeviceVerificationAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(authenticationCaptor.capture());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tOAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication = authenticationCaptor.getValue();\n\t\tassertThat(deviceVerificationAuthentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(deviceVerificationAuthentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(deviceVerificationAuthentication.getAdditionalParameters())\n\t\t\t.containsExactly(Map.entry(\"custom-param-1\", \"custom-value-1\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDeviceVerificationRequestAndConsentRequiredThenConsentScreen() throws Exception {\n\t\tAuthentication authenticationResult = createDeviceAuthorizationConsentAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(response.getContentType())\n\t\t\t.isEqualTo(new MediaType(\"text\", \"html\", StandardCharsets.UTF_8).toString());\n\t\tassertThat(response.getContentAsString()).contains(scopeCheckbox(\"scope-1\"));\n\t\tassertThat(response.getContentAsString()).contains(scopeCheckbox(\"scope-2\"));\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDeviceVerificationRequestAndConsentRequiredWithPreviouslyApprovedThenConsentScreen()\n\t\t\tthrows Exception {\n\t\tAuthentication authenticationResult = createDeviceAuthorizationConsentAuthenticationWithAuthorizedScopes();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(response.getContentType())\n\t\t\t.isEqualTo(new MediaType(\"text\", \"html\", StandardCharsets.UTF_8).toString());\n\t\tassertThat(response.getContentAsString()).contains(disabledScopeCheckbox(\"scope-1\"));\n\t\tassertThat(response.getContentAsString()).contains(scopeCheckbox(\"scope-2\"));\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDeviceVerificationRequestAndConsentRequiredAndConsentPageSetThenRedirect()\n\t\t\tthrows Exception {\n\t\tAuthentication authentication = createDeviceAuthorizationConsentAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authentication);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerPort(443);\n\t\trequest.setServerName(\"provider.com\");\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.setConsentPage(\"/consent\");\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tString redirectUri = UriComponentsBuilder.fromUriString(\"https://provider.com/consent\")\n\t\t\t.queryParam(OAuth2ParameterNames.SCOPE, \"scope-1 scope-2\")\n\t\t\t.queryParam(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID)\n\t\t\t.queryParam(OAuth2ParameterNames.STATE, STATE)\n\t\t\t.queryParam(OAuth2ParameterNames.USER_CODE, USER_CODE)\n\t\t\t.toUriString();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo(redirectUri);\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationConverterSetThenUsed() throws Exception {\n\t\tAuthentication authenticationResult = createDeviceVerificationAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tOAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication = new OAuth2DeviceVerificationAuthenticationToken(\n\t\t\t\t(Authentication) authenticationResult.getPrincipal(), USER_CODE, Collections.emptyMap());\n\t\tgiven(authenticationConverter.convert(any(HttpServletRequest.class)))\n\t\t\t.willReturn(deviceVerificationAuthentication);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo(\"/?success\");\n\n\t\tverify(authenticationConverter).convert(request);\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationDetailsSourceSetThenUsed() throws Exception {\n\t\tAuthentication authenticationResult = createDeviceVerificationAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tAuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\t\tgiven(authenticationDetailsSource.buildDetails(any(HttpServletRequest.class)))\n\t\t\t.willReturn(new WebAuthenticationDetails(request));\n\t\tthis.filter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo(\"/?success\");\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(authenticationDetailsSource).buildDetails(request);\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationSuccessHandlerSetThenUsed() throws Exception {\n\t\tAuthentication authenticationResult = createDeviceVerificationAuthentication();\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authenticationResult);\n\n\t\tAuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(request, response, authenticationResult);\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationFailureHandlerSetThenUsed() throws Exception {\n\t\tOAuth2AuthenticationException authenticationException = new OAuth2AuthenticationException(\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST);\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willThrow(authenticationException);\n\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\tupdateQueryString(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(request, response, authenticationException);\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\tprivate static void mockAuthorizationServerContext() {\n\t\tAuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().build();\n\t\tTestAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(\n\t\t\t\tauthorizationServerSettings, () -> ISSUER_URI);\n\t\tAuthorizationServerContextHolder.setContext(authorizationServerContext);\n\t}\n\n\tprivate static void mockSecurityContext(Authentication clientPrincipal) {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t}\n\n\tprivate static OAuth2DeviceVerificationAuthenticationToken createDeviceVerificationAuthentication() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"user\", null);\n\t\treturn new OAuth2DeviceVerificationAuthenticationToken(principal, CLIENT_ID, USER_CODE);\n\t}\n\n\tprivate static Authentication createDeviceAuthorizationConsentAuthentication() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"user\", null);\n\t\tSet<String> requestedScopes = new HashSet<>();\n\t\trequestedScopes.add(\"scope-1\");\n\t\trequestedScopes.add(\"scope-2\");\n\t\treturn new OAuth2DeviceAuthorizationConsentAuthenticationToken(AUTHORIZATION_URI, CLIENT_ID, principal,\n\t\t\t\tUSER_CODE, STATE, requestedScopes, new HashSet<>());\n\t}\n\n\tprivate static Authentication createDeviceAuthorizationConsentAuthenticationWithAuthorizedScopes() {\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"user\", null);\n\t\tSet<String> requestedScopes = new HashSet<>();\n\t\trequestedScopes.add(\"scope-1\");\n\t\trequestedScopes.add(\"scope-2\");\n\t\tSet<String> authorizedScopes = new HashSet<>();\n\t\tauthorizedScopes.add(\"scope-1\");\n\t\treturn new OAuth2DeviceAuthorizationConsentAuthenticationToken(AUTHORIZATION_URI, CLIENT_ID, principal,\n\t\t\t\tUSER_CODE, STATE, requestedScopes, authorizedScopes);\n\t}\n\n\tprivate static MockHttpServletRequest createRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.GET.name());\n\t\trequest.setRequestURI(VERIFICATION_URI);\n\t\trequest.setServletPath(VERIFICATION_URI);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\t\treturn request;\n\t}\n\n\tprivate static void updateQueryString(MockHttpServletRequest request) {\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(request.getRequestURI());\n\t\trequest.getParameterMap().forEach((key, values) -> {\n\t\t\tif (values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\turiBuilder.queryParam(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\trequest.setQueryString(uriBuilder.build().getQuery());\n\t}\n\n\tprivate static String scopeCheckbox(String scope) {\n\t\treturn MessageFormat.format(\n\t\t\t\t\"<input class=\\\"form-check-input\\\" type=\\\"checkbox\\\" name=\\\"scope\\\" value=\\\"{0}\\\" id=\\\"{0}\\\">\", scope);\n\t}\n\n\tprivate static String disabledScopeCheckbox(String scope) {\n\t\treturn MessageFormat.format(\n\t\t\t\t\"<input class=\\\"form-check-input\\\" type=\\\"checkbox\\\" name=\\\"scope\\\" id=\\\"{0}\\\" checked disabled>\",\n\t\t\t\tscope);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2PushedAuthorizationRequestEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.time.Instant;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2PushedAuthorizationRequestAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;\nimport org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;\nimport org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2PushedAuthorizationRequestEndpointFilter}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2PushedAuthorizationRequestEndpointFilterTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"https://provider.com/oauth2/par\";\n\n\tprivate static final String STATE = \"state\";\n\n\tprivate static final String REMOTE_ADDRESS = \"remote-address\";\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OAuth2PushedAuthorizationRequestEndpointFilter filter;\n\n\tprivate TestingAuthenticationToken clientPrincipal;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OAuth2PushedAuthorizationRequestEndpointFilter(this.authenticationManager);\n\t\tthis.clientPrincipal = new TestingAuthenticationToken(\"client-id\", \"client-secret\");\n\t\tthis.clientPrincipal.setAuthenticated(true);\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(this.clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tAuthorizationServerContextHolder\n\t\t\t.setContext(new TestAuthorizationServerContext(AuthorizationServerSettings.builder().build(), null));\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t\tAuthorizationServerContextHolder.resetContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2PushedAuthorizationRequestEndpointFilter(null))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenPushedAuthorizationRequestEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2PushedAuthorizationRequestEndpointFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"pushedAuthorizationRequestEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationDetailsSource(null))\n\t\t\t.withMessage(\"authenticationDetailsSource cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotPushedAuthorizationRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestIncludesRequestUriThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.REQUEST_URI,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.REQUEST_URI, OAuth2ParameterNames.REQUEST_URI));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestMultipleResponseTypeThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.RESPONSE_TYPE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.RESPONSE_TYPE, \"id_token\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestInvalidResponseTypeThenUnsupportedResponseTypeError()\n\t\t\tthrows Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.RESPONSE_TYPE,\n\t\t\t\tOAuth2ErrorCodes.UNSUPPORTED_RESPONSE_TYPE,\n\t\t\t\t(request) -> request.setParameter(OAuth2ParameterNames.RESPONSE_TYPE, \"id_token\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestMissingClientIdThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> request.removeParameter(OAuth2ParameterNames.CLIENT_ID));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestMultipleClientIdThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.CLIENT_ID,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-2\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestMultipleRedirectUriThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.REDIRECT_URI,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.REDIRECT_URI, \"https://example2.com\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestMultipleScopeThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.SCOPE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.SCOPE, \"scope2\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestMultipleStateThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.STATE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.STATE, \"state2\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestMultipleCodeChallengeThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), PkceParameterNames.CODE_CHALLENGE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(PkceParameterNames.CODE_CHALLENGE, \"code-challenge\");\n\t\t\t\t\trequest.addParameter(PkceParameterNames.CODE_CHALLENGE, \"another-code-challenge\");\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestMultipleCodeChallengeMethodThenInvalidRequestError()\n\t\t\tthrows Exception {\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(\n\t\t\t\tTestRegisteredClients.registeredClient().build(), PkceParameterNames.CODE_CHALLENGE_METHOD,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\t\t\t\trequest.addParameter(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthenticationRequestMultiplePromptThenInvalidRequestError() throws Exception {\n\t\t// Setup OpenID Connect request\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {\n\t\t\tscopes.clear();\n\t\t\tscopes.add(OidcScopes.OPENID);\n\t\t}).build();\n\t\tdoFilterWhenPushedAuthorizationRequestInvalidParameterThenError(registeredClient, \"prompt\",\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> {\n\t\t\t\t\trequest.addParameter(\"prompt\", \"none\");\n\t\t\t\t\trequest.addParameter(\"prompt\", \"login\");\n\t\t\t\t});\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestAuthenticationExceptionThenErrorResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, \"error description\", \"error uri\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(new OAuth2AuthenticationException(error));\n\n\t\tMockHttpServletRequest request = createPushedAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error errorResponse = readError(response);\n\t\tassertThat(errorResponse.getErrorCode()).isEqualTo(error.getErrorCode());\n\t\tassertThat(errorResponse.getDescription()).isEqualTo(error.getDescription());\n\t\tassertThat(errorResponse.getUri()).isEqualTo(error.getUri());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.clientPrincipal);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken pushedAuthorizationRequestAuthenticationResult = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.clientPrincipal,\n\t\t\t\tOAuth2ParameterNames.REQUEST_URI, Instant.now().plusSeconds(30),\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());\n\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any())).willReturn(pushedAuthorizationRequestAuthenticationResult);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(pushedAuthorizationRequestAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createPushedAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationConverter).convert(any());\n\t\tverify(this.authenticationManager).authenticate(any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken pushedAuthorizationRequestAuthenticationResult = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.clientPrincipal,\n\t\t\t\tOAuth2ParameterNames.REQUEST_URI, Instant.now().plusSeconds(30),\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(pushedAuthorizationRequestAuthenticationResult);\n\n\t\tAuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n\n\t\tMockHttpServletRequest request = createPushedAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(),\n\t\t\t\tsame(pushedAuthorizationRequestAuthenticationResult));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tOAuth2Error error = new OAuth2Error(\"errorCode\", \"errorDescription\", \"errorUri\");\n\t\tOAuth2AuthenticationException authenticationException = new OAuth2AuthenticationException(error);\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(authenticationException);\n\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tMockHttpServletRequest request = createPushedAuthorizationRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(this.authenticationManager).authenticate(any());\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), same(authenticationException));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tMockHttpServletRequest request = createPushedAuthorizationRequest(registeredClient);\n\n\t\tAuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\t\tWebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request);\n\t\tgiven(authenticationDetailsSource.buildDetails(request)).willReturn(webAuthenticationDetails);\n\t\tthis.filter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken pushedAuthorizationRequestAuthenticationResult = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.clientPrincipal,\n\t\t\t\tOAuth2ParameterNames.REQUEST_URI, Instant.now().plusSeconds(30),\n\t\t\t\tregisteredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());\n\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(pushedAuthorizationRequestAuthenticationResult);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationDetailsSource).buildDetails(any());\n\t\tverify(this.authenticationManager).authenticate(any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPushedAuthorizationRequestAuthenticatedThenPushedAuthorizationResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tString requestUri = OAuth2ParameterNames.REQUEST_URI;\n\t\tInstant requestUriExpiresAt = Instant.now().plusSeconds(30);\n\t\tOAuth2PushedAuthorizationRequestAuthenticationToken pushedAuthorizationRequestAuthenticationResult = new OAuth2PushedAuthorizationRequestAuthenticationToken(\n\t\t\t\tAUTHORIZATION_URI, registeredClient.getClientId(), this.clientPrincipal, requestUri,\n\t\t\t\trequestUriExpiresAt, registeredClient.getRedirectUris().iterator().next(), STATE,\n\t\t\t\tregisteredClient.getScopes());\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(pushedAuthorizationRequestAuthenticationResult);\n\n\t\tMockHttpServletRequest request = createPushedAuthorizationRequest(registeredClient);\n\t\trequest.addParameter(\"custom-param\", \"custom-value-1\", \"custom-value-2\");\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tArgumentCaptor<OAuth2PushedAuthorizationRequestAuthenticationToken> pushedAuthorizationRequestAuthenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2PushedAuthorizationRequestAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(pushedAuthorizationRequestAuthenticationCaptor.capture());\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(pushedAuthorizationRequestAuthenticationCaptor.getValue().getDetails())\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebAuthenticationDetails.class))\n\t\t\t.extracting(WebAuthenticationDetails::getRemoteAddress)\n\t\t\t.isEqualTo(REMOTE_ADDRESS);\n\n\t\t// Assert that multi-valued request parameters are preserved\n\t\tassertThat(pushedAuthorizationRequestAuthenticationCaptor.getValue().getAdditionalParameters())\n\t\t\t.extracting((params) -> params.get(\"custom-param\"))\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(String[].class))\n\t\t\t.isEqualTo(new String[] { \"custom-value-1\", \"custom-value-2\" });\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value());\n\t\tMap<String, Object> responseParameters = readPushedAuthorizationResponse(response);\n\t\tassertThat(responseParameters.get(OAuth2ParameterNames.REQUEST_URI)).isEqualTo(requestUri);\n\t\tInstant requestUriExpiry = Instant.now()\n\t\t\t.plusSeconds(Long.parseLong(String.valueOf(responseParameters.get(\"expires_in\"))));\n\t\tassertThat(requestUriExpiry).isBetween(requestUriExpiresAt.minusSeconds(1), requestUriExpiresAt.plusSeconds(1));\n\t}\n\n\tprivate void doFilterWhenPushedAuthorizationRequestInvalidParameterThenError(RegisteredClient registeredClient,\n\t\t\tString parameterName, String errorCode, Consumer<MockHttpServletRequest> requestConsumer) throws Exception {\n\n\t\tdoFilterWhenRequestInvalidParameterThenError(createPushedAuthorizationRequest(registeredClient), parameterName,\n\t\t\t\terrorCode, requestConsumer);\n\t}\n\n\tprivate void doFilterWhenRequestInvalidParameterThenError(MockHttpServletRequest request, String parameterName,\n\t\t\tString errorCode, Consumer<MockHttpServletRequest> requestConsumer) throws Exception {\n\n\t\trequestConsumer.accept(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t\tassertThat(error.getDescription()).isEqualTo(\"OAuth 2.0 Parameter: \" + parameterName);\n\t}\n\n\tprivate static MockHttpServletRequest createPushedAuthorizationRequest(RegisteredClient registeredClient) {\n\t\tString requestUri = AuthorizationServerContextHolder.getContext()\n\t\t\t.getAuthorizationServerSettings()\n\t\t\t.getPushedAuthorizationRequestEndpoint();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\n\t\trequest.addParameter(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2AuthorizationResponseType.CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\trequest.addParameter(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next());\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"state\");\n\n\t\treturn request;\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Map<String, Object> readPushedAuthorizationResponse(MockHttpServletResponse response) throws Exception {\n\t\tfinal ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t\t};\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn (Map<String, Object>) this.jsonMessageConverter.read(STRING_OBJECT_MAP.getType(), null, httpResponse);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2TokenEndpointFilter}.\n *\n * @author Madhu Bhat\n * @author Joe Grandja\n * @author Daniel Garnier-Moiroux\n */\npublic class OAuth2TokenEndpointFilterTests {\n\n\tprivate static final String DEFAULT_TOKEN_ENDPOINT_URI = \"/oauth2/token\";\n\n\tprivate static final String REMOTE_ADDRESS = \"remote-address\";\n\n\tprivate static final String ACCESS_TOKEN_TYPE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OAuth2TokenEndpointFilter filter;\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tprivate final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OAuth2TokenEndpointFilter(this.authenticationManager);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenEndpointFilter(null, \"tokenEndpointUri\"))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenEndpointFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"tokenEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationDetailsSource(null))\n\t\t\t.withMessage(\"authenticationDetailsSource cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotTokenRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRequestGetThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRequestMissingGrantTypeThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.removeParameter(OAuth2ParameterNames.GRANT_TYPE);\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.GRANT_TYPE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRequestMultipleGrantTypeThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.GRANT_TYPE,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRequestInvalidGrantTypeThenUnsupportedGrantTypeError() throws Exception {\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.setParameter(OAuth2ParameterNames.GRANT_TYPE, \"invalid-grant-type\");\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.GRANT_TYPE,\n\t\t\t\tOAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRequestMissingCodeThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.removeParameter(OAuth2ParameterNames.CODE);\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.CODE, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\trequest);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRequestMultipleCodeThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code-2\");\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.CODE, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\trequest);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRequestMultipleRedirectUriThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.addParameter(OAuth2ParameterNames.REDIRECT_URI, \"https://example2.com\");\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.REDIRECT_URI,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRequestMultipleDPoPHeaderThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.addHeader(OAuth2AccessToken.TokenType.DPOP.getValue(), \"dpop-proof-jwt\");\n\t\trequest.addHeader(OAuth2AccessToken.TokenType.DPOP.getValue(), \"dpop-proof-jwt-2\");\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2AccessToken.TokenType.DPOP.getValue(),\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationCodeTokenRequestThenAccessTokenResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", Instant.now(),\n\t\t\t\tInstant.now().plus(Duration.ofDays(1)));\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tregisteredClient, clientPrincipal, accessToken, refreshToken);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(accessTokenAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(registeredClient);\n\t\trequest.addHeader(OAuth2AccessToken.TokenType.DPOP.getValue(), \"dpop-proof-jwt\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tArgumentCaptor<OAuth2AuthorizationCodeAuthenticationToken> authorizationCodeAuthenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizationCodeAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(authorizationCodeAuthenticationCaptor.capture());\n\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = authorizationCodeAuthenticationCaptor\n\t\t\t.getValue();\n\t\tassertThat(authorizationCodeAuthentication.getCode())\n\t\t\t.isEqualTo(request.getParameter(OAuth2ParameterNames.CODE));\n\t\tassertThat(authorizationCodeAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(authorizationCodeAuthentication.getRedirectUri())\n\t\t\t.isEqualTo(request.getParameter(OAuth2ParameterNames.REDIRECT_URI));\n\t\tMap<String, Object> expectedAdditionalParameters = new HashMap<>();\n\t\texpectedAdditionalParameters.put(\"custom-param-1\", \"custom-value-1\");\n\t\texpectedAdditionalParameters.put(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" });\n\t\texpectedAdditionalParameters.put(\"dpop_proof\", \"dpop-proof-jwt\");\n\t\texpectedAdditionalParameters.put(\"dpop_method\", \"POST\");\n\t\texpectedAdditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tassertThat(authorizationCodeAuthentication.getAdditionalParameters())\n\t\t\t.containsExactlyInAnyOrderEntriesOf(expectedAdditionalParameters);\n\t\tassertThat(authorizationCodeAuthentication.getDetails())\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebAuthenticationDetails.class))\n\t\t\t.extracting(WebAuthenticationDetails::getRemoteAddress)\n\t\t\t.isEqualTo(REMOTE_ADDRESS);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tOAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);\n\n\t\tOAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken();\n\t\tassertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType());\n\t\tassertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue());\n\t\tassertThat(accessTokenResult.getIssuedAt()).isBetween(accessToken.getIssuedAt().minusSeconds(1),\n\t\t\t\taccessToken.getIssuedAt().plusSeconds(1));\n\t\tassertThat(accessTokenResult.getExpiresAt()).isBetween(accessToken.getExpiresAt().minusSeconds(1),\n\t\t\t\taccessToken.getExpiresAt().plusSeconds(1));\n\t\tassertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes());\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(refreshToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientCredentialsTokenRequestMultipleScopeThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createClientCredentialsTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient2().build());\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"profile\");\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\trequest);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenClientCredentialsTokenRequestThenAccessTokenResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tregisteredClient, clientPrincipal, accessToken);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(accessTokenAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createClientCredentialsTokenRequest(registeredClient);\n\t\trequest.addHeader(OAuth2AccessToken.TokenType.DPOP.getValue(), \"dpop-proof-jwt\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tArgumentCaptor<OAuth2ClientCredentialsAuthenticationToken> clientCredentialsAuthenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2ClientCredentialsAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(clientCredentialsAuthenticationCaptor.capture());\n\n\t\tOAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = clientCredentialsAuthenticationCaptor\n\t\t\t.getValue();\n\t\tassertThat(clientCredentialsAuthentication.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(clientCredentialsAuthentication.getScopes()).isEqualTo(registeredClient.getScopes());\n\t\tMap<String, Object> expectedAdditionalParameters = new HashMap<>();\n\t\texpectedAdditionalParameters.put(\"custom-param-1\", \"custom-value-1\");\n\t\texpectedAdditionalParameters.put(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" });\n\t\texpectedAdditionalParameters.put(\"dpop_proof\", \"dpop-proof-jwt\");\n\t\texpectedAdditionalParameters.put(\"dpop_method\", \"POST\");\n\t\texpectedAdditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tassertThat(clientCredentialsAuthentication.getAdditionalParameters())\n\t\t\t.containsExactlyInAnyOrderEntriesOf(expectedAdditionalParameters);\n\t\tassertThat(clientCredentialsAuthentication.getDetails())\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebAuthenticationDetails.class))\n\t\t\t.extracting(WebAuthenticationDetails::getRemoteAddress)\n\t\t\t.isEqualTo(REMOTE_ADDRESS);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\t// For gh-281, check that expires_in is a number\n\t\tassertThat(new ObjectMapper().readValue(response.getContentAsByteArray(), Map.class)\n\t\t\t.get(OAuth2ParameterNames.EXPIRES_IN)).isInstanceOf(Number.class);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);\n\n\t\tOAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken();\n\t\tassertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType());\n\t\tassertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue());\n\t\tassertThat(accessTokenResult.getIssuedAt()).isBetween(accessToken.getIssuedAt().minusSeconds(1),\n\t\t\t\taccessToken.getIssuedAt().plusSeconds(1));\n\t\tassertThat(accessTokenResult.getExpiresAt()).isBetween(accessToken.getExpiresAt().minusSeconds(1),\n\t\t\t\taccessToken.getExpiresAt().plusSeconds(1));\n\t\tassertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRefreshTokenRequestMissingRefreshTokenThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createRefreshTokenTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.removeParameter(OAuth2ParameterNames.REFRESH_TOKEN);\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.REFRESH_TOKEN,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRefreshTokenRequestMultipleRefreshTokenThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createRefreshTokenTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.addParameter(OAuth2ParameterNames.REFRESH_TOKEN, \"refresh-token-2\");\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.REFRESH_TOKEN,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRefreshTokenRequestMultipleScopeThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createRefreshTokenTokenRequest(\n\t\t\t\tTestRegisteredClients.registeredClient().build());\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"profile\");\n\n\t\tdoFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\trequest);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRefreshTokenRequestThenAccessTokenResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", Instant.now());\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tregisteredClient, clientPrincipal, accessToken, refreshToken);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(accessTokenAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createRefreshTokenTokenRequest(registeredClient);\n\t\trequest.addHeader(OAuth2AccessToken.TokenType.DPOP.getValue(), \"dpop-proof-jwt\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tArgumentCaptor<OAuth2RefreshTokenAuthenticationToken> refreshTokenAuthenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2RefreshTokenAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(refreshTokenAuthenticationCaptor.capture());\n\n\t\tOAuth2RefreshTokenAuthenticationToken refreshTokenAuthenticationToken = refreshTokenAuthenticationCaptor\n\t\t\t.getValue();\n\t\tassertThat(refreshTokenAuthenticationToken.getRefreshToken()).isEqualTo(refreshToken.getTokenValue());\n\t\tassertThat(refreshTokenAuthenticationToken.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(refreshTokenAuthenticationToken.getScopes()).isEqualTo(registeredClient.getScopes());\n\t\tMap<String, Object> expectedAdditionalParameters = new HashMap<>();\n\t\texpectedAdditionalParameters.put(\"custom-param-1\", \"custom-value-1\");\n\t\texpectedAdditionalParameters.put(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" });\n\t\texpectedAdditionalParameters.put(\"dpop_proof\", \"dpop-proof-jwt\");\n\t\texpectedAdditionalParameters.put(\"dpop_method\", \"POST\");\n\t\texpectedAdditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tassertThat(refreshTokenAuthenticationToken.getAdditionalParameters())\n\t\t\t.containsExactlyInAnyOrderEntriesOf(expectedAdditionalParameters);\n\t\tassertThat(refreshTokenAuthenticationToken.getDetails())\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebAuthenticationDetails.class))\n\t\t\t.extracting(WebAuthenticationDetails::getRemoteAddress)\n\t\t\t.isEqualTo(REMOTE_ADDRESS);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tOAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);\n\n\t\tOAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken();\n\t\tassertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType());\n\t\tassertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue());\n\t\tassertThat(accessTokenResult.getIssuedAt()).isBetween(accessToken.getIssuedAt().minusSeconds(1),\n\t\t\t\taccessToken.getIssuedAt().plusSeconds(1));\n\t\tassertThat(accessTokenResult.getExpiresAt()).isBetween(accessToken.getExpiresAt().minusSeconds(1),\n\t\t\t\taccessToken.getExpiresAt().plusSeconds(1));\n\t\tassertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes());\n\n\t\tOAuth2RefreshToken refreshTokenResult = accessTokenResponse.getRefreshToken();\n\t\tassertThat(refreshTokenResult.getTokenValue()).isEqualTo(refreshToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenExchangeRequestThenAccessTokenResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", Instant.now());\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tregisteredClient, clientPrincipal, accessToken, refreshToken);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(accessTokenAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenExchangeTokenRequest(registeredClient);\n\t\trequest.addHeader(OAuth2AccessToken.TokenType.DPOP.getValue(), \"dpop-proof-jwt\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tArgumentCaptor<OAuth2TokenExchangeAuthenticationToken> tokenExchangeAuthenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2TokenExchangeAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(tokenExchangeAuthenticationCaptor.capture());\n\n\t\tOAuth2TokenExchangeAuthenticationToken tokenExchangeAuthenticationToken = tokenExchangeAuthenticationCaptor\n\t\t\t.getValue();\n\t\tassertThat(tokenExchangeAuthenticationToken.getSubjectToken()).isEqualTo(\"subject-token\");\n\t\tassertThat(tokenExchangeAuthenticationToken.getSubjectTokenType()).isEqualTo(ACCESS_TOKEN_TYPE);\n\t\tassertThat(tokenExchangeAuthenticationToken.getPrincipal()).isEqualTo(clientPrincipal);\n\t\tassertThat(tokenExchangeAuthenticationToken.getScopes()).isEqualTo(registeredClient.getScopes());\n\t\tMap<String, Object> expectedAdditionalParameters = new HashMap<>();\n\t\texpectedAdditionalParameters.put(\"custom-param-1\", \"custom-value-1\");\n\t\texpectedAdditionalParameters.put(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" });\n\t\texpectedAdditionalParameters.put(\"dpop_proof\", \"dpop-proof-jwt\");\n\t\texpectedAdditionalParameters.put(\"dpop_method\", \"POST\");\n\t\texpectedAdditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tassertThat(tokenExchangeAuthenticationToken.getAdditionalParameters())\n\t\t\t.containsExactlyInAnyOrderEntriesOf(expectedAdditionalParameters);\n\t\tassertThat(tokenExchangeAuthenticationToken.getDetails())\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebAuthenticationDetails.class))\n\t\t\t.extracting(WebAuthenticationDetails::getRemoteAddress)\n\t\t\t.isEqualTo(REMOTE_ADDRESS);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tOAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);\n\n\t\tOAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken();\n\t\tassertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType());\n\t\tassertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue());\n\t\tassertThat(accessTokenResult.getIssuedAt()).isBetween(accessToken.getIssuedAt().minusSeconds(1),\n\t\t\t\taccessToken.getIssuedAt().plusSeconds(1));\n\t\tassertThat(accessTokenResult.getExpiresAt()).isBetween(accessToken.getExpiresAt().minusSeconds(1),\n\t\t\t\taccessToken.getExpiresAt().plusSeconds(1));\n\t\tassertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes());\n\n\t\tOAuth2RefreshToken refreshTokenResult = accessTokenResponse.getRefreshToken();\n\t\tassertThat(refreshTokenResult.getTokenValue()).isEqualTo(refreshToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(registeredClient);\n\n\t\tAuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\t\tWebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request);\n\t\tgiven(authenticationDetailsSource.buildDetails(any())).willReturn(webAuthenticationDetails);\n\t\tthis.filter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tregisteredClient, clientPrincipal, accessToken);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(accessTokenAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationDetailsSource).buildDetails(any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\t\"code\", clientPrincipal, null, null);\n\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any())).willReturn(authorizationCodeAuthentication);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tregisteredClient, clientPrincipal, accessToken);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(accessTokenAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationConverter).convert(any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tAuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken(\n\t\t\t\tregisteredClient, clientPrincipal, accessToken);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(accessTokenAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(registeredClient);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\t\tMockHttpServletRequest request = createAuthorizationCodeTokenRequest(registeredClient);\n\t\trequest.removeParameter(OAuth2ParameterNames.GRANT_TYPE);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());\n\t}\n\n\tprivate void doFilterWhenTokenRequestInvalidParameterThenError(String parameterName, String errorCode,\n\t\t\tMockHttpServletRequest request) throws Exception {\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t\tassertThat(error.getDescription()).isEqualTo(\"OAuth 2.0 Parameter: \" + parameterName);\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n\tprivate OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);\n\t}\n\n\tprivate static MockHttpServletRequest createAuthorizationCodeTokenRequest(RegisteredClient registeredClient) {\n\t\tString[] redirectUris = registeredClient.getRedirectUris().toArray(new String[0]);\n\n\t\tString requestUri = DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\trequest.addParameter(OAuth2ParameterNames.REDIRECT_URI, redirectUris[0]);\n\t\t// The client does not need to send the client ID param, but we are resilient in\n\t\t// case they do\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\n\t\treturn request;\n\t}\n\n\tprivate static MockHttpServletRequest createClientCredentialsTokenRequest(RegisteredClient registeredClient) {\n\t\tString requestUri = DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\n\t\treturn request;\n\t}\n\n\tprivate static MockHttpServletRequest createRefreshTokenTokenRequest(RegisteredClient registeredClient) {\n\t\tString requestUri = DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.REFRESH_TOKEN, \"refresh-token\");\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\n\t\treturn request;\n\t}\n\n\tprivate static MockHttpServletRequest createTokenExchangeTokenRequest(RegisteredClient registeredClient) {\n\t\tString requestUri = DEFAULT_TOKEN_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.setRemoteAddr(REMOTE_ADDRESS);\n\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, \"subject-token\");\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE);\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE,\n\t\t\t\tStringUtils.collectionToDelimitedString(registeredClient.getScopes(), \" \"));\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashSet;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.oauth2.server.authorization.http.converter.OAuth2TokenIntrospectionHttpMessageConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2TokenIntrospectionEndpointFilter}.\n *\n * @author Gerardo Roza\n * @author Joe Grandja\n */\npublic class OAuth2TokenIntrospectionEndpointFilterTests {\n\n\tprivate static final String DEFAULT_TOKEN_INTROSPECTION_ENDPOINT_URI = \"/oauth2/introspect\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OAuth2TokenIntrospectionEndpointFilter filter;\n\n\tprivate final HttpMessageConverter<OAuth2TokenIntrospection> tokenIntrospectionHttpResponseConverter = new OAuth2TokenIntrospectionHttpMessageConverter();\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OAuth2TokenIntrospectionEndpointFilter(this.authenticationManager);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenIntrospectionEndpointFilter(null))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenIntrospectionEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenIntrospectionEndpointFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"tokenIntrospectionEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotTokenIntrospectionRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenIntrospectionRequestGetThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_TOKEN_INTROSPECTION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenIntrospectionRequestMissingTokenThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createTokenIntrospectionRequest(\"token\",\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\trequest.removeParameter(OAuth2ParameterNames.TOKEN);\n\n\t\tdoFilterWhenTokenIntrospectionRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenIntrospectionRequestMultipleTokenThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createTokenIntrospectionRequest(\"token\",\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.TOKEN, \"other-token\");\n\n\t\tdoFilterWhenTokenIntrospectionRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenIntrospectionRequestMultipleTokenTypeHintThenInvalidRequestError() throws Exception {\n\t\tMockHttpServletRequest request = createTokenIntrospectionRequest(\"token\",\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.TOKEN_TYPE_HINT, OAuth2TokenType.ACCESS_TOKEN.getValue());\n\n\t\tdoFilterWhenTokenIntrospectionRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN_TYPE_HINT,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenIntrospectionRequestValidThenSuccessResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\t// @formatter:off\n\t\tOAuth2TokenIntrospection tokenClaims = OAuth2TokenIntrospection.builder(true)\n\t\t\t\t.clientId(\"authorized-client-id\")\n\t\t\t\t.username(\"authorizing-username\")\n\t\t\t\t.issuedAt(accessToken.getIssuedAt())\n\t\t\t\t.expiresAt(accessToken.getExpiresAt())\n\t\t\t\t.scopes((scopes) -> scopes.addAll(accessToken.getScopes()))\n\t\t\t\t.tokenType(accessToken.getTokenType().getValue())\n\t\t\t\t.notBefore(accessToken.getIssuedAt())\n\t\t\t\t.subject(\"authorizing-subject\")\n\t\t\t\t.audience(\"authorized-client-id\")\n\t\t\t\t.issuer(\"https://provider.com\")\n\t\t\t\t.id(\"jti\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthenticationResult = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, tokenClaims);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(tokenIntrospectionAuthenticationResult);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenIntrospectionRequest(accessToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tArgumentCaptor<OAuth2TokenIntrospectionAuthenticationToken> tokenIntrospectionAuthentication = ArgumentCaptor\n\t\t\t.forClass(OAuth2TokenIntrospectionAuthenticationToken.class);\n\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(this.authenticationManager).authenticate(tokenIntrospectionAuthentication.capture());\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(tokenIntrospectionAuthentication.getValue().getAdditionalParameters()).contains(\n\t\t\t\tentry(\"custom-param-1\", \"custom-value-1\"),\n\t\t\t\tentry(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\n\t\tOAuth2TokenIntrospection tokenIntrospectionResponse = readTokenIntrospectionResponse(response);\n\t\tassertThat(tokenIntrospectionResponse.isActive()).isEqualTo(tokenClaims.isActive());\n\t\tassertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(tokenClaims.getClientId());\n\t\tassertThat(tokenIntrospectionResponse.getUsername()).isEqualTo(tokenClaims.getUsername());\n\t\tassertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween(tokenClaims.getIssuedAt().minusSeconds(1),\n\t\t\t\ttokenClaims.getIssuedAt().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween(tokenClaims.getExpiresAt().minusSeconds(1),\n\t\t\t\ttokenClaims.getExpiresAt().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getScopes()).containsExactlyInAnyOrderElementsOf(tokenClaims.getScopes());\n\t\tassertThat(tokenIntrospectionResponse.getTokenType()).isEqualTo(tokenClaims.getTokenType());\n\t\tassertThat(tokenIntrospectionResponse.getNotBefore()).isBetween(tokenClaims.getNotBefore().minusSeconds(1),\n\t\t\t\ttokenClaims.getNotBefore().plusSeconds(1));\n\t\tassertThat(tokenIntrospectionResponse.getSubject()).isEqualTo(tokenClaims.getSubject());\n\t\tassertThat(tokenIntrospectionResponse.getAudience())\n\t\t\t.containsExactlyInAnyOrderElementsOf(tokenClaims.getAudience());\n\t\tassertThat(tokenIntrospectionResponse.getIssuer()).isEqualTo(tokenClaims.getIssuer());\n\t\tassertThat(tokenIntrospectionResponse.getId()).isEqualTo(tokenClaims.getId());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue(), null);\n\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any())).willReturn(tokenIntrospectionAuthentication);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(tokenIntrospectionAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenIntrospectionRequest(accessToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationConverter).convert(any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = new OAuth2TokenIntrospectionAuthenticationToken(\n\t\t\t\taccessToken.getTokenValue(), clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue(), null);\n\n\t\tAuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(tokenIntrospectionAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenIntrospectionRequest(accessToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(OAuth2AuthenticationException.class);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenIntrospectionRequest(accessToken.getTokenValue(),\n\t\t\t\tOAuth2TokenType.ACCESS_TOKEN.getValue());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());\n\t}\n\n\tprivate void doFilterWhenTokenIntrospectionRequestInvalidParameterThenError(String parameterName, String errorCode,\n\t\t\tMockHttpServletRequest request) throws Exception {\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t\tassertThat(error.getDescription()).isEqualTo(\"OAuth 2.0 Token Introspection Parameter: \" + parameterName);\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n\tprivate OAuth2TokenIntrospection readTokenIntrospectionResponse(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.tokenIntrospectionHttpResponseConverter.read(OAuth2TokenIntrospection.class, httpResponse);\n\t}\n\n\tprivate static MockHttpServletRequest createTokenIntrospectionRequest(String token, String tokenTypeHint) {\n\t\tString requestUri = DEFAULT_TOKEN_INTROSPECTION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\trequest.addParameter(OAuth2ParameterNames.TOKEN, token);\n\t\trequest.addParameter(OAuth2ParameterNames.TOKEN_TYPE_HINT, tokenTypeHint);\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.OAuth2TokenType;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2TokenRevocationEndpointFilter}.\n *\n * @author Vivek Babu\n * @author Joe Grandja\n */\npublic class OAuth2TokenRevocationEndpointFilterTests {\n\n\tprivate static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = \"/oauth2/revoke\";\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate OAuth2TokenRevocationEndpointFilter filter;\n\n\tprivate final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter();\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = new OAuth2TokenRevocationEndpointFilter(this.authenticationManager);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenRevocationEndpointFilter(null))\n\t\t\t.withMessage(\"authenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenRevocationEndpointUriNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OAuth2TokenRevocationEndpointFilter(this.authenticationManager, null))\n\t\t\t.withMessage(\"tokenRevocationEndpointUri cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationDetailsSource(null))\n\t\t\t.withMessage(\"authenticationDetailsSource cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationConverter(null))\n\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"authenticationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null))\n\t\t\t.withMessage(\"authenticationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotTokenRevocationRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRevocationRequestGetThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRevocationRequestMissingTokenThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenTokenRevocationRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> request.removeParameter(OAuth2ParameterNames.TOKEN));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRevocationRequestMultipleTokenThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenTokenRevocationRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t(request) -> request.addParameter(OAuth2ParameterNames.TOKEN, \"token-2\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRevocationRequestMultipleTokenTypeHintThenInvalidRequestError() throws Exception {\n\t\tdoFilterWhenTokenRevocationRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN_TYPE_HINT,\n\t\t\t\tOAuth2ErrorCodes.INVALID_REQUEST, (request) -> request\n\t\t\t\t\t.addParameter(OAuth2ParameterNames.TOKEN_TYPE_HINT, OAuth2TokenType.ACCESS_TOKEN.getValue()));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenRevocationRequestValidThenSuccessResponse() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\taccessToken, clientPrincipal);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(tokenRevocationAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenRevocationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\t\tverify(this.authenticationManager).authenticate(any());\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tMockHttpServletRequest request = createTokenRevocationRequest();\n\n\t\tAuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource = mock(\n\t\t\t\tAuthenticationDetailsSource.class);\n\t\tWebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request);\n\t\tgiven(authenticationDetailsSource.buildDetails(any())).willReturn(webAuthenticationDetails);\n\t\tthis.filter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\taccessToken, clientPrincipal);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(tokenRevocationAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationDetailsSource).buildDetails(any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\taccessToken, clientPrincipal);\n\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any())).willReturn(tokenRevocationAuthentication);\n\t\tthis.filter.setAuthenticationConverter(authenticationConverter);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(tokenRevocationAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenRevocationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationConverter).convert(any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofHours(1)),\n\t\t\t\tnew HashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tOAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken(\n\t\t\t\taccessToken, clientPrincipal);\n\n\t\tAuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);\n\t\tthis.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(tokenRevocationAuthentication);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenRevocationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {\n\t\tRegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\t\tAuthentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,\n\t\t\t\tClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret());\n\n\t\tAuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.filter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(OAuth2AuthenticationException.class);\n\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(clientPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest request = createTokenRevocationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any());\n\t}\n\n\tprivate void doFilterWhenTokenRevocationRequestInvalidParameterThenError(String parameterName, String errorCode,\n\t\t\tConsumer<MockHttpServletRequest> requestConsumer) throws Exception {\n\n\t\tMockHttpServletRequest request = createTokenRevocationRequest();\n\t\trequestConsumer.accept(request);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tOAuth2Error error = readError(response);\n\t\tassertThat(error.getErrorCode()).isEqualTo(errorCode);\n\t\tassertThat(error.getDescription()).isEqualTo(\"OAuth 2.0 Token Revocation Parameter: \" + parameterName);\n\t}\n\n\tprivate OAuth2Error readError(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);\n\t}\n\n\tprivate static MockHttpServletRequest createTokenRevocationRequest() {\n\t\tString requestUri = DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\n\t\trequest.addParameter(OAuth2ParameterNames.TOKEN, \"token\");\n\t\trequest.addParameter(OAuth2ParameterNames.TOKEN_TYPE_HINT, OAuth2TokenType.ACCESS_TOKEN.getValue());\n\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\n\n/**\n * Tests for {@link ClientSecretBasicAuthenticationConverter}.\n *\n * @author Patryk Kostrzewa\n * @author Joe Grandja\n */\npublic class ClientSecretBasicAuthenticationConverterTests {\n\n\tprivate ClientSecretBasicAuthenticationConverter converter = new ClientSecretBasicAuthenticationConverter();\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderEmptyThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderNotBasicThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Bearer token\");\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderBasicWithMissingCredentialsThenThrowOAuth2AuthenticationException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderBasicWithInvalidBase64ThenThrowOAuth2AuthenticationException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic clientId:secret\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderBasicWithMissingSecretThenThrowOAuth2AuthenticationException()\n\t\t\tthrows Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + encodeBasicAuth(\"clientId\", \"\"));\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderBasicWithValidCredentialsThenReturnClientAuthenticationToken()\n\t\t\tthrows Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + encodeBasicAuth(\"clientId\", \"secret\"));\n\t\tOAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"clientId\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"secret\");\n\t\tassertThat(authentication.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void convertWhenConfidentialClientWithPkceParametersThenAdditionalParametersIncluded() throws Exception {\n\t\tMockHttpServletRequest request = createPkceTokenRequest();\n\t\trequest.addParameter(\"custom-param\", \"custom-value-1\", \"custom-value-2\");\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + encodeBasicAuth(\"clientId\", \"secret\"));\n\t\tOAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"clientId\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"secret\");\n\t\tassertThat(authentication.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(authentication.getAdditionalParameters()).containsOnly(\n\t\t\t\tentry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tentry(OAuth2ParameterNames.CODE, \"code\"), entry(PkceParameterNames.CODE_VERIFIER, \"code-verifier-1\"),\n\t\t\t\tentry(\"custom-param\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\t}\n\n\tprivate static String encodeBasicAuth(String clientId, String secret) throws Exception {\n\t\tclientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());\n\t\tsecret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());\n\t\tString credentialsString = clientId + \":\" + secret;\n\t\tbyte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));\n\t\treturn new String(encodedBytes, StandardCharsets.UTF_8);\n\t}\n\n\tprivate static MockHttpServletRequest createPkceTokenRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\trequest.addParameter(PkceParameterNames.CODE_VERIFIER, \"code-verifier-1\");\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\n\n/**\n * Tests for {@link ClientSecretPostAuthenticationConverter}.\n *\n * @author Anoop Garlapati\n */\npublic class ClientSecretPostAuthenticationConverterTests {\n\n\tprivate final ClientSecretPostAuthenticationConverter converter = new ClientSecretPostAuthenticationConverter();\n\n\t@Test\n\tpublic void convertWhenMissingClientIdThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleClientIdsThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-2\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingClientSecretThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleClientSecretsThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_SECRET, \"client-secret-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_SECRET, \"client-secret-2\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenPostWithValidCredentialsThenReturnClientAuthenticationToken() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_SECRET, \"client-secret\");\n\t\tOAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"client-1\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"client-secret\");\n\t\tassertThat(authentication.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t}\n\n\t@Test\n\tpublic void convertWhenConfidentialClientWithPkceParametersThenAdditionalParametersIncluded() {\n\t\tMockHttpServletRequest request = createPkceTokenRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_SECRET, \"client-secret\");\n\t\trequest.addParameter(\"custom-param\", \"custom-value-1\", \"custom-value-2\");\n\t\tOAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"client-1\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"client-secret\");\n\t\tassertThat(authentication.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tassertThat(authentication.getAdditionalParameters()).containsOnly(\n\t\t\t\tentry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tentry(OAuth2ParameterNames.CODE, \"code\"), entry(PkceParameterNames.CODE_VERIFIER, \"code-verifier-1\"),\n\t\t\t\tentry(\"custom-param\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\t}\n\n\tprivate static MockHttpServletRequest createPkceTokenRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\trequest.addParameter(PkceParameterNames.CODE_VERIFIER, \"code-verifier-1\");\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\n\n/**\n * Tests for {@link JwtClientAssertionAuthenticationConverter}.\n *\n * @author Rafal Lewczuk\n */\npublic class JwtClientAssertionAuthenticationConverterTests {\n\n\tprivate static final String JWT_BEARER_TYPE = \"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\";\n\n\tprivate final JwtClientAssertionAuthenticationConverter converter = new JwtClientAssertionAuthenticationConverter();\n\n\t@Test\n\tpublic void convertWhenMissingClientAssertionTypeThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION, \"jwt-assertion\");\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingClientAssertionThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, JWT_BEARER_TYPE);\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleClientAssertionTypeThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, JWT_BEARER_TYPE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, \"other-client-assertion-type\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION, \"jwt-assertion\");\n\t\tassertThrown(request, OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenNotJwtAssertionTypeThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, \"other-client-assertion-type\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION, \"other-assertion\");\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleClientAssertionThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, JWT_BEARER_TYPE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION, \"jwt-assertion\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION, \"other-jwt-assertion\");\n\t\tassertThrown(request, OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingClientIdThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, JWT_BEARER_TYPE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION, \"jwt-assertion\");\n\t\tassertThrown(request, OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleClientIdThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, JWT_BEARER_TYPE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION, \"jwt-assertion\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-2\");\n\t\tassertThrown(request, OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenJwtAssertionThenReturnClientAuthenticationToken() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, JWT_BEARER_TYPE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ASSERTION, \"jwt-assertion\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\t\tOAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"client-1\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"jwt-assertion\");\n\t\tassertThat(authentication.getClientAuthenticationMethod().getValue()).isEqualTo(JWT_BEARER_TYPE);\n\t\tassertThat(authentication.getAdditionalParameters()).containsOnly(\n\t\t\t\tentry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tentry(OAuth2ParameterNames.CODE, \"code\"), entry(\"custom-param-1\", \"custom-value-1\"),\n\t\t\t\tentry(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\t}\n\n\tprivate void assertThrown(MockHttpServletRequest request, String errorCode) {\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(errorCode);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AccessTokenResponseAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.server.authorization.OAuth2Authorization;\nimport org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationContext;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.client.RegisteredClient;\nimport org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AccessTokenResponseAuthenticationSuccessHandler}.\n *\n * @author Dmitriy Dubson\n */\npublic class OAuth2AccessTokenResponseAuthenticationSuccessHandlerTests {\n\n\tprivate final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();\n\n\tprivate final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\n\tprivate final OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(\n\t\t\tthis.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,\n\t\t\tthis.registeredClient.getClientSecret());\n\n\tprivate final OAuth2AccessTokenResponseAuthenticationSuccessHandler authenticationSuccessHandler = new OAuth2AccessTokenResponseAuthenticationSuccessHandler();\n\n\t@Test\n\tpublic void setAccessTokenResponseCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authenticationSuccessHandler.setAccessTokenResponseCustomizer(null))\n\t\t\t\t.withMessage(\"accessTokenResponseCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessWhenAuthenticationProvidedThenAccessTokenResponse() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient).build();\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\tOAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();\n\t\tMap<String, Object> additionalParameters = Collections.singletonMap(\"param1\", \"value1\");\n\t\tAuthentication authentication = new OAuth2AccessTokenAuthenticationToken(this.registeredClient,\n\t\t\t\tthis.clientPrincipal, accessToken, refreshToken, additionalParameters);\n\n\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);\n\n\t\tOAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(accessToken.getTokenValue());\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(accessToken.getTokenType());\n\t\tassertThat(accessTokenResponse.getAccessToken().getIssuedAt())\n\t\t\t.isBetween(accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt())\n\t\t\t.isBetween(accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNotNull();\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(refreshToken.getTokenValue());\n\t\tassertThat(accessTokenResponse.getAdditionalParameters())\n\t\t\t.containsExactlyInAnyOrderEntriesOf(Map.of(\"param1\", \"value1\"));\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessWhenInvalidAuthenticationTypeThenThrowOAuth2AuthenticationException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationSuccessHandler.onAuthenticationSuccess(request, response,\n\t\t\t\t\tnew TestingAuthenticationToken(this.clientPrincipal, null)))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR);\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessWhenAccessTokenResponseCustomizerSetThenAccessTokenResponseCustomized()\n\t\t\tthrows Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\n\t\tOAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient).build();\n\t\tOAuth2AccessToken accessToken = authorization.getAccessToken().getToken();\n\t\tOAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();\n\t\tMap<String, Object> additionalParameters = Collections.singletonMap(\"param1\", \"value1\");\n\t\tAuthentication authentication = new OAuth2AccessTokenAuthenticationToken(this.registeredClient,\n\t\t\t\tthis.clientPrincipal, accessToken, refreshToken, additionalParameters);\n\n\t\tConsumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer = (authenticationContext) -> {\n\t\t\tOAuth2AccessTokenAuthenticationToken accessTokenAuthentication = authenticationContext.getAuthentication();\n\t\t\tMap<String, Object> additionalParams = new HashMap<>(accessTokenAuthentication.getAdditionalParameters());\n\t\t\tadditionalParams.put(\"authorization_id\", authorization.getId());\n\t\t\tauthenticationContext.getAccessTokenResponse().additionalParameters(additionalParams);\n\t\t};\n\t\tthis.authenticationSuccessHandler.setAccessTokenResponseCustomizer(accessTokenResponseCustomizer);\n\n\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);\n\n\t\tOAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(accessToken.getTokenValue());\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(accessToken.getTokenType());\n\t\tassertThat(accessTokenResponse.getAccessToken().getIssuedAt())\n\t\t\t.isBetween(accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt())\n\t\t\t.isBetween(accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNotNull();\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(refreshToken.getTokenValue());\n\t\tassertThat(accessTokenResponse.getAdditionalParameters())\n\t\t\t.containsExactlyInAnyOrderEntriesOf(Map.of(\"param1\", \"value1\", \"authorization_id\", \"id\"));\n\t}\n\n\tprivate OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response) throws Exception {\n\t\tMockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(),\n\t\t\t\tHttpStatus.valueOf(response.getStatus()));\n\t\treturn this.accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2DeviceAuthorizationConsentAuthenticationConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceAuthorizationConsentAuthenticationConverterTests {\n\n\tprivate static final String VERIFICATION_URI = \"/oauth2/device_verification\";\n\n\tprivate static final String USER_CODE = \"BCDF-GHJK\";\n\n\tprivate static final String CLIENT_ID = \"client-1\";\n\n\tprivate static final String STATE = \"abc123\";\n\n\tprivate OAuth2DeviceAuthorizationConsentAuthenticationConverter converter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.converter = new OAuth2DeviceAuthorizationConsentAuthenticationConverter();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void convertWhenGetThenReturnNull() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.setMethod(HttpMethod.GET.name());\n\t\tassertThat(this.converter.convert(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingStateThenReturnNull() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\tassertThat(this.converter.convert(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingClientIdThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenEmptyClientIdThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleClientIdParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"another\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingUserCodeThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.USER_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenEmptyUserCodeThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, \"\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.USER_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenInvalidUserCodeThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, \"LONG-USER-CODE\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.USER_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleUserCodeParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, \"another\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.USER_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenEmptyStateParameterThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.STATE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleStateParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"another\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.STATE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingPrincipalThenReturnDeviceAuthorizationConsentAuthentication() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken authentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);\n\t\tassertThat(authentication.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(authentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authentication.getScopes()).isEmpty();\n\t\tassertThat(authentication.getAdditionalParameters()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingScopeThenReturnDeviceAuthorizationConsentAuthentication() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken authentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);\n\t\tassertThat(authentication.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(authentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authentication.getScopes()).isEmpty();\n\t\tassertThat(authentication.getAdditionalParameters()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenAllParametersThenReturnDeviceAuthorizationConsentAuthentication() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"message.read\");\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"message.write\");\n\t\trequest.addParameter(\"param-1\", \"value-1\");\n\t\trequest.addParameter(\"param-2\", \"value-1\", \"value-2\");\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken authentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);\n\t\tassertThat(authentication.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(authentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authentication.getScopes()).containsExactly(\"message.read\", \"message.write\");\n\t\tassertThat(authentication.getAdditionalParameters()).containsExactly(Map.entry(\"param-1\", \"value-1\"),\n\t\t\t\tMap.entry(\"param-2\", new String[] { \"value-1\", \"value-2\" }));\n\t}\n\n\t@Test\n\tpublic void convertWhenNonNormalizedUserCodeThenReturnDeviceAuthorizationConsentAuthentication() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, STATE);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE.toLowerCase().replace(\"-\", \" . \"));\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2DeviceAuthorizationConsentAuthenticationToken authentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);\n\t\tassertThat(authentication.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(authentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authentication.getScopes()).isEmpty();\n\t\tassertThat(authentication.getAdditionalParameters()).isEmpty();\n\t}\n\n\tprivate static MockHttpServletRequest createRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.POST.name());\n\t\trequest.setRequestURI(VERIFICATION_URI);\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2DeviceAuthorizationRequestAuthenticationConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceAuthorizationRequestAuthenticationConverterTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"/oauth2/device_authorization\";\n\n\tprivate static final String CLIENT_ID = \"client-1\";\n\n\tprivate OAuth2DeviceAuthorizationRequestAuthenticationConverter converter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.converter = new OAuth2DeviceAuthorizationRequestAuthenticationConverter();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleScopeParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"message.read\");\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"message.write\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SCOPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingScopeThenReturnDeviceAuthorizationRequestAuthenticationToken() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(CLIENT_ID, null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(authentication.getAuthorizationUri()).endsWith(AUTHORIZATION_URI);\n\t\tassertThat(authentication.getScopes()).isEmpty();\n\t\tassertThat(authentication.getAdditionalParameters()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenAllParametersThenReturnDeviceAuthorizationRequestAuthenticationToken() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"message.read message.write\");\n\t\trequest.addParameter(\"param-1\", \"value-1\");\n\t\trequest.addParameter(\"param-2\", \"value-1\", \"value-2\");\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(CLIENT_ID, null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2DeviceAuthorizationRequestAuthenticationToken authentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(authentication.getAuthorizationUri()).endsWith(AUTHORIZATION_URI);\n\t\tassertThat(authentication.getScopes()).containsExactly(\"message.read\", \"message.write\");\n\t\tassertThat(authentication.getAdditionalParameters()).containsExactly(Map.entry(\"param-1\", \"value-1\"),\n\t\t\t\tMap.entry(\"param-2\", new String[] { \"value-1\", \"value-2\" }));\n\t}\n\n\tprivate static MockHttpServletRequest createRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.POST.name());\n\t\trequest.setRequestURI(AUTHORIZATION_URI);\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2DeviceCodeAuthenticationConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceCodeAuthenticationConverterTests {\n\n\tprivate static final String CLIENT_ID = \"client-1\";\n\n\tprivate static final String TOKEN_URI = \"/oauth2/token\";\n\n\tprivate static final String DEVICE_CODE = \"EfYu_0jEL\";\n\n\tprivate OAuth2DeviceCodeAuthenticationConverter converter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.converter = new OAuth2DeviceCodeAuthenticationConverter();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingGrantTypeThenReturnNull() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingDeviceCodeThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.DEVICE_CODE.getValue());\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.DEVICE_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleDeviceCodeParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.DEVICE_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.DEVICE_CODE, DEVICE_CODE);\n\t\trequest.addParameter(OAuth2ParameterNames.DEVICE_CODE, \"another\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.DEVICE_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenAllParametersThenReturnDeviceCodeAuthenticationToken() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.DEVICE_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.DEVICE_CODE, DEVICE_CODE);\n\t\trequest.addParameter(\"param-1\", \"value-1\");\n\t\trequest.addParameter(\"param-2\", \"value-1\", \"value-2\");\n\t\trequest.addHeader(OAuth2AccessToken.TokenType.DPOP.getValue(), \"dpop-proof-jwt\");\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(CLIENT_ID, null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2DeviceCodeAuthenticationToken authentication = (OAuth2DeviceCodeAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getDeviceCode()).isEqualTo(DEVICE_CODE);\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tMap<String, Object> expectedAdditionalParameters = new HashMap<>();\n\t\texpectedAdditionalParameters.put(\"param-1\", \"value-1\");\n\t\texpectedAdditionalParameters.put(\"param-2\", new String[] { \"value-1\", \"value-2\" });\n\t\texpectedAdditionalParameters.put(\"dpop_proof\", \"dpop-proof-jwt\");\n\t\texpectedAdditionalParameters.put(\"dpop_method\", \"POST\");\n\t\texpectedAdditionalParameters.put(\"dpop_target_uri\", \"http://localhost/oauth2/token\");\n\t\tassertThat(authentication.getAdditionalParameters())\n\t\t\t.containsExactlyInAnyOrderEntriesOf(expectedAdditionalParameters);\n\t}\n\n\tprivate static MockHttpServletRequest createRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.POST.name());\n\t\trequest.setRequestURI(TOKEN_URI);\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationToken;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2DeviceVerificationAuthenticationConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceVerificationAuthenticationConverterTests {\n\n\tprivate static final String VERIFICATION_URI = \"/oauth2/device_verification\";\n\n\tprivate static final String USER_CODE = \"BCDF-GHJK\";\n\n\tprivate OAuth2DeviceVerificationAuthenticationConverter converter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.converter = new OAuth2DeviceVerificationAuthenticationConverter();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void convertWhenPutThenReturnNull() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.setMethod(HttpMethod.PUT.name());\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenStateThenReturnNull() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"abc123\");\n\t\tupdateQueryString(request);\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingUserCodeThenReturnNull() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenEmptyUserCodeParameterThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, \"\");\n\t\tupdateQueryString(request);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.USER_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenInvalidUserCodeParameterThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, \"LONG-USER-CODE\");\n\t\tupdateQueryString(request);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.USER_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleUserCodeParameterThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, \"another\");\n\t\tupdateQueryString(request);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.USER_CODE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingPrincipalThenReturnDeviceVerificationAuthentication() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE.toLowerCase().replace(\"-\", \" . \"));\n\t\tupdateQueryString(request);\n\n\t\tOAuth2DeviceVerificationAuthenticationToken authentication = (OAuth2DeviceVerificationAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(authentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authentication.getAdditionalParameters()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenNonNormalizedUserCodeThenReturnDeviceVerificationAuthentication() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE.toLowerCase().replace(\"-\", \" . \"));\n\t\tupdateQueryString(request);\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2DeviceVerificationAuthenticationToken authentication = (OAuth2DeviceVerificationAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(authentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authentication.getAdditionalParameters()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenAllParametersThenReturnDeviceVerificationAuthentication() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);\n\t\trequest.addParameter(\"param-1\", \"value-1\");\n\t\trequest.addParameter(\"param-2\", \"value-1\", \"value-2\");\n\t\tupdateQueryString(request);\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2DeviceVerificationAuthenticationToken authentication = (OAuth2DeviceVerificationAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);\n\t\tassertThat(authentication.getUserCode()).isEqualTo(USER_CODE);\n\t\tassertThat(authentication.getAdditionalParameters()).containsExactly(Map.entry(\"param-1\", \"value-1\"),\n\t\t\t\tMap.entry(\"param-2\", new String[] { \"value-1\", \"value-2\" }));\n\t}\n\n\tprivate static MockHttpServletRequest createRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.GET.name());\n\t\trequest.setRequestURI(VERIFICATION_URI);\n\t\treturn request;\n\t}\n\n\tprivate static void updateQueryString(MockHttpServletRequest request) {\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(request.getRequestURI());\n\t\trequest.getParameterMap().forEach((key, values) -> {\n\t\t\tif (values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\turiBuilder.queryParam(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\trequest.setQueryString(uriBuilder.build().getQuery());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ErrorAuthenticationFailureHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2ErrorAuthenticationFailureHandler}.\n *\n * @author Dmitriy Dubson\n */\npublic class OAuth2ErrorAuthenticationFailureHandlerTests {\n\n\tprivate final OAuth2ErrorAuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler();\n\n\t@Test\n\tpublic void setErrorResponseConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.authenticationFailureHandler.setErrorResponseConverter(null))\n\t\t\t\t.withMessage(\"errorResponseConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void onAuthenticationFailureWhenValidExceptionThenErrorResponse() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, \"error description\", \"error uri\");\n\t\tAuthenticationException authenticationException = new OAuth2AuthenticationException(error);\n\n\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, authenticationException);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tassertThat(response.getContentAsString()).contains(\"invalid_request\");\n\t\tassertThat(response.getContentAsString()).contains(\"error description\");\n\t\tassertThat(response.getContentAsString()).contains(\"error uri\");\n\t}\n\n\t@Test\n\tpublic void onAuthenticationFailureWhenInvalidExceptionThenStatusResponse() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationException authenticationException = new BadCredentialsException(\"Not a valid exception.\");\n\n\t\tHttpMessageConverter<OAuth2Error> errorResponseConverter = mock(HttpMessageConverter.class);\n\t\tthis.authenticationFailureHandler.setErrorResponseConverter(errorResponseConverter);\n\n\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, authenticationException);\n\n\t\tverifyNoInteractions(errorResponseConverter);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tassertThat(response.getContentAsString()).doesNotContain(OAuth2ParameterNames.ERROR);\n\t\tassertThat(response.getContentAsString()).doesNotContain(OAuth2ParameterNames.ERROR_DESCRIPTION);\n\t\tassertThat(response.getContentAsString()).doesNotContain(OAuth2ParameterNames.ERROR_URI);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenExchangeAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2TokenExchangeAuthenticationConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2TokenExchangeAuthenticationConverterTests {\n\n\tprivate static final String CLIENT_ID = \"client-1\";\n\n\tprivate static final String TOKEN_URI = \"/oauth2/token\";\n\n\tprivate static final String SUBJECT_TOKEN = \"EfYu_0jEL\";\n\n\tprivate static final String ACTOR_TOKEN = \"JlNE_xR1f\";\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate OAuth2TokenExchangeAuthenticationConverter converter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.converter = new OAuth2TokenExchangeAuthenticationConverter();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingGrantTypeThenReturnNull() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenInvalidResourceThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.RESOURCE, \"invalid\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.RESOURCE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenResourceContainsFragmentThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.RESOURCE, \"https://mydomain.com/#fragment\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.RESOURCE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleScopeParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"one\");\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"two\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SCOPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleRequestedTokenTypeParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\trequest.addParameter(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenInvalidRequestedTokenTypeThenUnsupportedTokenTypeError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, \"invalid\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.UNSUPPORTED_TOKEN_TYPE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingSubjectTokenThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SUBJECT_TOKEN)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleSubjectTokenParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, \"another\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SUBJECT_TOKEN)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingSubjectTokenTypeThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleSubjectTokenTypeParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenInvalidSubjectTokenTypeThenUnsupportedTokenTypeError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, \"invalid\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.UNSUPPORTED_TOKEN_TYPE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleActorTokenParametersThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\trequest.addParameter(OAuth2ParameterNames.ACTOR_TOKEN, ACTOR_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.ACTOR_TOKEN, \"another\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.ACTOR_TOKEN)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenActorTokenAndMissingActorTokenTypeThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\trequest.addParameter(OAuth2ParameterNames.ACTOR_TOKEN, ACTOR_TOKEN);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.ACTOR_TOKEN_TYPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenActorTokenTypeAndMissingActorTokenThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\trequest.addParameter(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.ACTOR_TOKEN)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenInvalidActorTokenTypeThenUnsupportedTokenTypeError() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\trequest.addParameter(OAuth2ParameterNames.ACTOR_TOKEN, ACTOR_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, \"invalid\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.converter.convert(request))\n\t\t\t\t.withMessageContaining(OAuth2ParameterNames.ACTOR_TOKEN_TYPE)\n\t\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t\t.isEqualTo(OAuth2ErrorCodes.UNSUPPORTED_TOKEN_TYPE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenAllParametersThenTokenExchangeAuthenticationToken() {\n\t\tMockHttpServletRequest request = createRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.RESOURCE, \"https://mydomain.com/resource1\");\n\t\trequest.addParameter(OAuth2ParameterNames.RESOURCE, \"https://mydomain.com/resource2\");\n\t\trequest.addParameter(OAuth2ParameterNames.AUDIENCE, \"audience1\");\n\t\trequest.addParameter(OAuth2ParameterNames.AUDIENCE, \"audience2\");\n\t\trequest.addParameter(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\trequest.addParameter(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN, SUBJECT_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\trequest.addParameter(OAuth2ParameterNames.ACTOR_TOKEN, ACTOR_TOKEN);\n\t\trequest.addParameter(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE);\n\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(CLIENT_ID, null));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tOAuth2TokenExchangeAuthenticationToken authentication = (OAuth2TokenExchangeAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getResources()).containsExactly(\"https://mydomain.com/resource1\",\n\t\t\t\t\"https://mydomain.com/resource2\");\n\t\tassertThat(authentication.getAudiences()).containsExactly(\"audience1\", \"audience2\");\n\t\tassertThat(authentication.getScopes()).containsExactly(\"one\", \"two\");\n\t\tassertThat(authentication.getRequestedTokenType()).isEqualTo(JWT_TOKEN_TYPE_VALUE);\n\t\tassertThat(authentication.getSubjectToken()).isEqualTo(SUBJECT_TOKEN);\n\t\tassertThat(authentication.getSubjectTokenType()).isEqualTo(ACCESS_TOKEN_TYPE_VALUE);\n\t\tassertThat(authentication.getActorToken()).isEqualTo(ACTOR_TOKEN);\n\t\tassertThat(authentication.getActorTokenType()).isEqualTo(JWT_TOKEN_TYPE_VALUE);\n\t}\n\n\tprivate static MockHttpServletRequest createRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.POST.name());\n\t\trequest.setRequestURI(TOKEN_URI);\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\n\n/**\n * Tests for {@link PublicClientAuthenticationConverter}.\n *\n * @author Joe Grandja\n */\npublic class PublicClientAuthenticationConverterTests {\n\n\tprivate PublicClientAuthenticationConverter converter = new PublicClientAuthenticationConverter();\n\n\t@Test\n\tpublic void convertWhenNotPublicClientThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingClientIdThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createPkceTokenRequest();\n\t\trequest.removeParameter(OAuth2ParameterNames.CLIENT_ID);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleClientIdThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createPkceTokenRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-2\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleCodeVerifierThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = createPkceTokenRequest();\n\t\trequest.addParameter(PkceParameterNames.CODE_VERIFIER, \"code-verifier-2\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenPublicClientThenReturnClientAuthenticationToken() {\n\t\tMockHttpServletRequest request = createPkceTokenRequest();\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\t\tOAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"client-1\");\n\t\tassertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.NONE);\n\t\tassertThat(authentication.getAdditionalParameters()).containsOnly(\n\t\t\t\tentry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tentry(OAuth2ParameterNames.CODE, \"code\"), entry(PkceParameterNames.CODE_VERIFIER, \"code-verifier-1\"),\n\t\t\t\tentry(\"custom-param-1\", \"custom-value-1\"),\n\t\t\t\tentry(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\t}\n\n\tprivate static MockHttpServletRequest createPkceTokenRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(PkceParameterNames.CODE_VERIFIER, \"code-verifier-1\");\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/X509ClientCertificateAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.authorization.web.authentication;\n\nimport java.security.cert.X509Certificate;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;\nimport org.springframework.security.oauth2.server.authorization.util.TestX509Certificates;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.entry;\n\n/**\n * Tests for {@link X509ClientCertificateAuthenticationConverter}.\n *\n * @author Joe Grandja\n */\npublic class X509ClientCertificateAuthenticationConverterTests {\n\n\tprivate final X509ClientCertificateAuthenticationConverter converter = new X509ClientCertificateAuthenticationConverter();\n\n\t@Test\n\tpublic void convertWhenMissingX509CertificateThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenEmptyX509CertificateThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"jakarta.servlet.request.X509Certificate\", new X509Certificate[0]);\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMissingClientIdThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"jakarta.servlet.request.X509Certificate\",\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleClientIdThenInvalidRequestError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"jakarta.servlet.request.X509Certificate\",\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-2\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(\"errorCode\")\n\t\t\t.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void convertWhenPkiX509CertificateThenReturnClientAuthenticationToken() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"jakarta.servlet.request.X509Certificate\",\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\t\tOAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"client-1\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);\n\t\tassertThat(authentication.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.TLS_CLIENT_AUTH);\n\t\tassertThat(authentication.getAdditionalParameters()).containsOnly(\n\t\t\t\tentry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tentry(OAuth2ParameterNames.CODE, \"code\"), entry(\"custom-param-1\", \"custom-value-1\"),\n\t\t\t\tentry(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\t}\n\n\t@Test\n\tpublic void convertWhenSelfSignedX509CertificateThenReturnClientAuthenticationToken() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"jakarta.servlet.request.X509Certificate\",\n\t\t\t\tTestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);\n\t\trequest.addParameter(OAuth2ParameterNames.CLIENT_ID, \"client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\trequest.addParameter(\"custom-param-1\", \"custom-value-1\");\n\t\trequest.addParameter(\"custom-param-2\", \"custom-value-1\", \"custom-value-2\");\n\t\tOAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"client-1\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(TestX509Certificates.DEMO_CLIENT_SELF_SIGNED_CERTIFICATE);\n\t\tassertThat(authentication.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH);\n\t\tassertThat(authentication.getAdditionalParameters()).containsOnly(\n\t\t\t\tentry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tentry(OAuth2ParameterNames.CODE, \"code\"), entry(\"custom-param-1\", \"custom-value-1\"),\n\t\t\t\tentry(\"custom-param-2\", new String[] { \"custom-value-1\", \"custom-value-2\" }));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/resources/org/springframework/security/oauth2/server/authorization/client/custom-oauth2-registered-client-schema.sql",
    "content": "CREATE TABLE oauth2RegisteredClient (\n    id varchar(100) NOT NULL,\n    clientId varchar(100) NOT NULL,\n    clientIdIssuedAt timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,\n    clientSecret varchar(200) DEFAULT NULL,\n    clientSecretExpiresAt timestamp DEFAULT NULL,\n    clientName varchar(200) NOT NULL,\n    clientAuthenticationMethods varchar(1000) NOT NULL,\n    authorizationGrantTypes varchar(1000) NOT NULL,\n    redirectUris varchar(1000) DEFAULT NULL,\n    postLogoutRedirectUris varchar(1000) DEFAULT NULL,\n    scopes varchar(1000) NOT NULL,\n    clientSettings varchar(2000) NOT NULL,\n    tokenSettings varchar(2000) NOT NULL,\n    PRIMARY KEY (id)\n);\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/resources/org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-consent-schema.sql",
    "content": "CREATE TABLE oauth2AuthorizationConsent (\n    registeredClientId varchar(100) NOT NULL,\n    principalName varchar(200) NOT NULL,\n    authorities varchar(1000) NOT NULL,\n    PRIMARY KEY (registeredClientId, principalName)\n);\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/resources/org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-schema-clob-data-type.sql",
    "content": "CREATE TABLE oauth2_authorization (\n    id varchar(100) NOT NULL,\n    registered_client_id varchar(100) NOT NULL,\n    principal_name varchar(200) NOT NULL,\n    authorization_grant_type varchar(100) NOT NULL,\n    authorized_scopes varchar(1000) DEFAULT NULL,\n    attributes varchar(4000) DEFAULT NULL,\n    state varchar(500) DEFAULT NULL,\n    authorization_code_value clob DEFAULT NULL,\n    authorization_code_issued_at timestamp DEFAULT NULL,\n    authorization_code_expires_at timestamp DEFAULT NULL,\n    authorization_code_metadata varchar(2000) DEFAULT NULL,\n    access_token_value clob DEFAULT NULL,\n    access_token_issued_at timestamp DEFAULT NULL,\n    access_token_expires_at timestamp DEFAULT NULL,\n    access_token_metadata varchar(2000) DEFAULT NULL,\n    access_token_type varchar(100) DEFAULT NULL,\n    access_token_scopes varchar(1000) DEFAULT NULL,\n    oidc_id_token_value clob DEFAULT NULL,\n    oidc_id_token_issued_at timestamp DEFAULT NULL,\n    oidc_id_token_expires_at timestamp DEFAULT NULL,\n    oidc_id_token_metadata varchar(2000) DEFAULT NULL,\n    refresh_token_value clob DEFAULT NULL,\n    refresh_token_issued_at timestamp DEFAULT NULL,\n    refresh_token_expires_at timestamp DEFAULT NULL,\n    refresh_token_metadata varchar(2000) DEFAULT NULL,\n    user_code_value clob DEFAULT NULL,\n    user_code_issued_at timestamp DEFAULT NULL,\n    user_code_expires_at timestamp DEFAULT NULL,\n    user_code_metadata varchar(2000) DEFAULT NULL,\n    device_code_value clob DEFAULT NULL,\n    device_code_issued_at timestamp DEFAULT NULL,\n    device_code_expires_at timestamp DEFAULT NULL,\n    device_code_metadata varchar(2000) DEFAULT NULL,\n    PRIMARY KEY (id)\n);\n"
  },
  {
    "path": "oauth2/oauth2-authorization-server/src/test/resources/org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-schema.sql",
    "content": "CREATE TABLE oauth2Authorization (\n    id varchar(100) NOT NULL,\n    registeredClientId varchar(100) NOT NULL,\n    principalName varchar(200) NOT NULL,\n    authorizationGrantType varchar(100) NOT NULL,\n    authorizedScopes varchar(1000) DEFAULT NULL,\n    attributes varchar(4000) DEFAULT NULL,\n    state varchar(500) DEFAULT NULL,\n    authorizationCodeValue varchar(1000) DEFAULT NULL,\n    authorizationCodeIssuedAt timestamp DEFAULT NULL,\n    authorizationCodeExpiresAt timestamp DEFAULT NULL,\n    authorizationCodeMetadata varchar(2000) DEFAULT NULL,\n    accessTokenValue varchar(1000) DEFAULT NULL,\n    accessTokenIssuedAt timestamp DEFAULT NULL,\n    accessTokenExpiresAt timestamp DEFAULT NULL,\n    accessTokenMetadata varchar(2000) DEFAULT NULL,\n    accessTokenType varchar(100) DEFAULT NULL,\n    accessTokenScopes varchar(1000) DEFAULT NULL,\n    oidcIdTokenValue varchar(1000) DEFAULT NULL,\n    oidcIdTokenIssuedAt timestamp DEFAULT NULL,\n    oidcIdTokenExpiresAt timestamp DEFAULT NULL,\n    oidcIdTokenMetadata varchar(2000) DEFAULT NULL,\n    refreshTokenValue varchar(1000) DEFAULT NULL,\n    refreshTokenIssuedAt timestamp DEFAULT NULL,\n    refreshTokenExpiresAt timestamp DEFAULT NULL,\n    refreshTokenMetadata varchar(2000) DEFAULT NULL,\n    userCodeValue varchar(1000) DEFAULT NULL,\n    userCodeIssuedAt timestamp DEFAULT NULL,\n    userCodeExpiresAt timestamp DEFAULT NULL,\n    userCodeMetadata varchar(2000) DEFAULT NULL,\n    deviceCodeValue varchar(1000) DEFAULT NULL,\n    deviceCodeIssuedAt timestamp DEFAULT NULL,\n    deviceCodeExpiresAt timestamp DEFAULT NULL,\n    deviceCodeMetadata varchar(2000) DEFAULT NULL,\n    PRIMARY KEY (id)\n);\n"
  },
  {
    "path": "oauth2/oauth2-client/spring-security-oauth2-client.gradle",
    "content": "plugins {\n\tid 'javadoc-warnings-error'\n\tid 'security-nullability'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi project(':spring-security-oauth2-core')\n\tapi project(':spring-security-web')\n\tapi 'org.springframework:spring-core'\n\tapi 'com.nimbusds:oauth2-oidc-sdk'\n\n\toptional project(':spring-security-oauth2-jose')\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'org.springframework:spring-webflux'\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\toptional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'\n\toptional 'org.springframework:spring-jdbc'\n\toptional 'org.springframework:spring-r2dbc'\n\toptional 'tools.jackson.core:jackson-databind'\n\n\ttestImplementation project(path: ':spring-security-oauth2-core', configuration: 'tests')\n\ttestImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests')\n\ttestImplementation project(path: ':spring-security-web', configuration: 'tests')\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\ttestImplementation 'io.micrometer:context-propagation'\n\ttestImplementation 'io.projectreactor.netty:reactor-netty'\n\ttestImplementation 'io.projectreactor:reactor-test'\n\ttestImplementation 'org.skyscreamer:jsonassert'\n\ttestImplementation 'io.r2dbc:r2dbc-h2:1.0.0.RELEASE'\n\ttestImplementation 'io.r2dbc:r2dbc-spi-test:0.9.1.RELEASE'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/AuthorizationCodeOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#AUTHORIZATION_CODE authorization_code} grant.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientProvider\n */\npublic final class AuthorizationCodeOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {\n\n\t/**\n\t * Attempt to authorize the {@link OAuth2AuthorizationContext#getClientRegistration()\n\t * client} in the provided {@code context}. Returns {@code null} if authorization is\n\t * not supported, e.g. the client's\n\t * {@link ClientRegistration#getAuthorizationGrantType() authorization grant type} is\n\t * not {@link AuthorizationGrantType#AUTHORIZATION_CODE authorization_code} OR the\n\t * client is already authorized.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return {@code null} if authorization is not supported or the client is already\n\t * authorized\n\t * @throws ClientAuthorizationRequiredException in order to trigger authorization in\n\t * which the {@link OAuth2AuthorizationRequestRedirectFilter} will catch and initiate\n\t * the authorization request\n\t */\n\t@Override\n\tpublic @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(\n\t\t\t\tcontext.getClientRegistration().getAuthorizationGrantType()) && context.getAuthorizedClient() == null) {\n\t\t\t// ClientAuthorizationRequiredException is caught by\n\t\t\t// OAuth2AuthorizationRequestRedirectFilter which initiates authorization\n\t\t\tthrow new ClientAuthorizationRequiredException(context.getClientRegistration().getRegistrationId());\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/AuthorizationCodeReactiveOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of a {@link ReactiveOAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#AUTHORIZATION_CODE authorization_code} grant.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see ReactiveOAuth2AuthorizedClientProvider\n */\npublic final class AuthorizationCodeReactiveOAuth2AuthorizedClientProvider\n\t\timplements ReactiveOAuth2AuthorizedClientProvider {\n\n\t/**\n\t * Attempt to authorize the {@link OAuth2AuthorizationContext#getClientRegistration()\n\t * client} in the provided {@code context}. Returns an empty {@code Mono} if\n\t * authorization is not supported, e.g. the client's\n\t * {@link ClientRegistration#getAuthorizationGrantType() authorization grant type} is\n\t * not {@link AuthorizationGrantType#AUTHORIZATION_CODE authorization_code} OR the\n\t * client is already authorized.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return an empty {@code Mono} if authorization is not supported or the client is\n\t * already authorized\n\t * @throws ClientAuthorizationRequiredException in order to trigger authorization in\n\t * which the {@link OAuth2AuthorizationRequestRedirectWebFilter} will catch and\n\t * initiate the authorization request\n\t */\n\t@Override\n\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(\n\t\t\t\tcontext.getClientRegistration().getAuthorizationGrantType()) && context.getAuthorizedClient() == null) {\n\t\t\t// ClientAuthorizationRequiredException is caught by\n\t\t\t// OAuth2AuthorizationRequestRedirectWebFilter which initiates authorization\n\t\t\treturn Mono.error(() -> new ClientAuthorizationRequiredException(\n\t\t\t\t\tcontext.getClientRegistration().getRegistrationId()));\n\t\t}\n\t\treturn Mono.empty();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/AuthorizedClientServiceOAuth2AuthorizedClientManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientManager} that is capable of\n * operating outside of the context of a {@code HttpServletRequest}, e.g. in a\n * scheduled/background thread and/or in the service-tier.\n *\n * <p>\n * (When operating <em>within</em> the context of a {@code HttpServletRequest}, use\n * {@link DefaultOAuth2AuthorizedClientManager} instead.)\n *\n * <h2>Authorized Client Persistence</h2>\n *\n * <p>\n * This manager utilizes an {@link OAuth2AuthorizedClientService} to persist\n * {@link OAuth2AuthorizedClient}s.\n *\n * <p>\n * By default, when an authorization attempt succeeds, the {@link OAuth2AuthorizedClient}\n * will be saved in the {@link OAuth2AuthorizedClientService}. This functionality can be\n * changed by configuring a custom {@link OAuth2AuthorizationSuccessHandler} via\n * {@link #setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler)}.\n *\n * <p>\n * By default, when an authorization attempt fails due to an\n * {@value OAuth2ErrorCodes#INVALID_GRANT} error, the previously saved\n * {@link OAuth2AuthorizedClient} will be removed from the\n * {@link OAuth2AuthorizedClientService}. (The {@value OAuth2ErrorCodes#INVALID_GRANT}\n * error can occur when a refresh token that is no longer valid is used to retrieve a new\n * access token.) This functionality can be changed by configuring a custom\n * {@link OAuth2AuthorizationFailureHandler} via\n * {@link #setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)}.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientManager\n * @see OAuth2AuthorizedClientProvider\n * @see OAuth2AuthorizedClientService\n * @see OAuth2AuthorizationSuccessHandler\n * @see OAuth2AuthorizationFailureHandler\n */\npublic final class AuthorizedClientServiceOAuth2AuthorizedClientManager implements OAuth2AuthorizedClientManager {\n\n\tprivate static final OAuth2AuthorizedClientProvider DEFAULT_AUTHORIZED_CLIENT_PROVIDER = OAuth2AuthorizedClientProviderBuilder\n\t\t.builder()\n\t\t.clientCredentials()\n\t\t.build();\n\n\tprivate final ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate final OAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate OAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper;\n\n\tprivate OAuth2AuthorizationSuccessHandler authorizationSuccessHandler;\n\n\tprivate OAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t/**\n\t * Constructs an {@code AuthorizedClientServiceOAuth2AuthorizedClientManager} using\n\t * the provided parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientService the authorized client service\n\t */\n\tpublic AuthorizedClientServiceOAuth2AuthorizedClientManager(\n\t\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientService authorizedClientService) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientService, \"authorizedClientService cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClientService = authorizedClientService;\n\t\tthis.authorizedClientProvider = DEFAULT_AUTHORIZED_CLIENT_PROVIDER;\n\t\tthis.contextAttributesMapper = new DefaultContextAttributesMapper();\n\t\tthis.authorizationSuccessHandler = (authorizedClient, principal, attributes) -> authorizedClientService\n\t\t\t.saveAuthorizedClient(authorizedClient, principal);\n\t\tthis.authorizationFailureHandler = new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> authorizedClientService\n\t\t\t\t\t.removeAuthorizedClient(clientRegistrationId, principal.getName()));\n\t}\n\n\t@Override\n\tpublic @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {\n\t\tAssert.notNull(authorizeRequest, \"authorizeRequest cannot be null\");\n\t\tString clientRegistrationId = authorizeRequest.getClientRegistrationId();\n\t\tOAuth2AuthorizedClient authorizedClient = authorizeRequest.getAuthorizedClient();\n\t\tAuthentication principal = authorizeRequest.getPrincipal();\n\t\tOAuth2AuthorizationContext.Builder contextBuilder;\n\t\tif (authorizedClient != null) {\n\t\t\tcontextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);\n\t\t}\n\t\telse {\n\t\t\tClientRegistration clientRegistration = this.clientRegistrationRepository\n\t\t\t\t.findByRegistrationId(clientRegistrationId);\n\t\t\tAssert.notNull(clientRegistration,\n\t\t\t\t\t\"Could not find ClientRegistration with id '\" + clientRegistrationId + \"'\");\n\t\t\tauthorizedClient = this.authorizedClientService.loadAuthorizedClient(clientRegistrationId,\n\t\t\t\t\tprincipal.getName());\n\t\t\tif (authorizedClient != null) {\n\t\t\t\tcontextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcontextBuilder = OAuth2AuthorizationContext.withClientRegistration(clientRegistration);\n\t\t\t}\n\t\t}\n\t\tOAuth2AuthorizationContext authorizationContext = buildAuthorizationContext(authorizeRequest, principal,\n\t\t\t\tcontextBuilder);\n\t\ttry {\n\t\t\tauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tthis.authorizationFailureHandler.onAuthorizationFailure(ex, principal, Collections.emptyMap());\n\t\t\tthrow ex;\n\t\t}\n\t\tif (authorizedClient != null) {\n\t\t\tthis.authorizationSuccessHandler.onAuthorizationSuccess(authorizedClient, principal,\n\t\t\t\t\tCollections.emptyMap());\n\t\t}\n\t\telse {\n\t\t\t// In the case of re-authorization, the returned `authorizedClient` may be\n\t\t\t// null if re-authorization is not supported.\n\t\t\t// For these cases, return the provided\n\t\t\t// `authorizationContext.authorizedClient`.\n\t\t\tif (authorizationContext.getAuthorizedClient() != null) {\n\t\t\t\treturn authorizationContext.getAuthorizedClient();\n\t\t\t}\n\t\t}\n\t\treturn authorizedClient;\n\t}\n\n\tprivate OAuth2AuthorizationContext buildAuthorizationContext(OAuth2AuthorizeRequest authorizeRequest,\n\t\t\tAuthentication principal, OAuth2AuthorizationContext.Builder contextBuilder) {\n\t\t// @formatter:off\n\t\treturn contextBuilder.principal(principal)\n\t\t\t\t.attributes((attributes) -> {\n\t\t\t\t\tMap<String, Object> contextAttributes = this.contextAttributesMapper.apply(authorizeRequest);\n\t\t\t\t\tif (!CollectionUtils.isEmpty(contextAttributes)) {\n\t\t\t\t\t\tattributes.putAll(contextAttributes);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizedClientProvider} used for authorizing (or\n\t * re-authorizing) an OAuth 2.0 Client.\n\t * @param authorizedClientProvider the {@link OAuth2AuthorizedClientProvider} used for\n\t * authorizing (or re-authorizing) an OAuth 2.0 Client\n\t */\n\tpublic void setAuthorizedClientProvider(OAuth2AuthorizedClientProvider authorizedClientProvider) {\n\t\tAssert.notNull(authorizedClientProvider, \"authorizedClientProvider cannot be null\");\n\t\tthis.authorizedClientProvider = authorizedClientProvider;\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping attribute(s) from the\n\t * {@link OAuth2AuthorizeRequest} to a {@code Map} of attributes to be associated to\n\t * the {@link OAuth2AuthorizationContext#getAttributes() authorization context}.\n\t * @param contextAttributesMapper the {@code Function} used for supplying the\n\t * {@code Map} of attributes to the {@link OAuth2AuthorizationContext#getAttributes()\n\t * authorization context}\n\t */\n\tpublic void setContextAttributesMapper(\n\t\t\tFunction<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper) {\n\t\tAssert.notNull(contextAttributesMapper, \"contextAttributesMapper cannot be null\");\n\t\tthis.contextAttributesMapper = contextAttributesMapper;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizationSuccessHandler} that handles successful\n\t * authorizations.\n\t *\n\t * <p>\n\t * The default saves {@link OAuth2AuthorizedClient}s in the\n\t * {@link OAuth2AuthorizedClientService}.\n\t * @param authorizationSuccessHandler the {@link OAuth2AuthorizationSuccessHandler}\n\t * that handles successful authorizations\n\t * @since 5.3\n\t */\n\tpublic void setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler authorizationSuccessHandler) {\n\t\tAssert.notNull(authorizationSuccessHandler, \"authorizationSuccessHandler cannot be null\");\n\t\tthis.authorizationSuccessHandler = authorizationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizationFailureHandler} that handles authorization\n\t * failures.\n\t *\n\t * <p>\n\t * A {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} is used by\n\t * default.\n\t * @param authorizationFailureHandler the {@link OAuth2AuthorizationFailureHandler}\n\t * that handles authorization failures\n\t * @since 5.3\n\t * @see RemoveAuthorizedClientOAuth2AuthorizationFailureHandler\n\t */\n\tpublic void setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\tthis.authorizationFailureHandler = authorizationFailureHandler;\n\t}\n\n\t/**\n\t * The default implementation of the {@link #setContextAttributesMapper(Function)\n\t * contextAttributesMapper}.\n\t */\n\tpublic static class DefaultContextAttributesMapper\n\t\t\timplements Function<OAuth2AuthorizeRequest, Map<String, Object>> {\n\n\t\t@Override\n\t\tpublic Map<String, Object> apply(OAuth2AuthorizeRequest authorizeRequest) {\n\t\t\tMap<String, Object> contextAttributes = Collections.emptyMap();\n\t\t\tString scope = authorizeRequest.getAttribute(OAuth2ParameterNames.SCOPE);\n\t\t\tif (StringUtils.hasText(scope)) {\n\t\t\t\tcontextAttributes = new HashMap<>();\n\t\t\t\tcontextAttributes.put(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME,\n\t\t\t\t\t\tStringUtils.delimitedListToStringArray(scope, \" \"));\n\t\t\t}\n\t\t\treturn contextAttributes;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of a {@link ReactiveOAuth2AuthorizedClientManager} that is capable of\n * operating outside of the context of a {@link ServerWebExchange}, e.g. in a\n * scheduled/background thread and/or in the service-tier.\n *\n * <p>\n * (When operating <em>within</em> the context of a {@link ServerWebExchange}, use\n * {@link DefaultReactiveOAuth2AuthorizedClientManager} instead.)\n * </p>\n *\n * <p>\n * This is a reactive equivalent of\n * {@link org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager}.\n * </p>\n *\n * <h2>Authorized Client Persistence</h2>\n *\n * <p>\n * This client manager utilizes a {@link ReactiveOAuth2AuthorizedClientService} to persist\n * {@link OAuth2AuthorizedClient}s.\n * </p>\n *\n * <p>\n * By default, when an authorization attempt succeeds, the {@link OAuth2AuthorizedClient}\n * will be saved in the authorized client service. This functionality can be changed by\n * configuring a custom {@link ReactiveOAuth2AuthorizationSuccessHandler} via\n * {@link #setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler)}.\n * </p>\n *\n * <p>\n * By default, when an authorization attempt fails due to an\n * {@value org.springframework.security.oauth2.core.OAuth2ErrorCodes#INVALID_GRANT} error,\n * the previously saved {@link OAuth2AuthorizedClient} will be removed from the authorized\n * client service. (The\n * {@value org.springframework.security.oauth2.core.OAuth2ErrorCodes#INVALID_GRANT} error\n * generally occurs when a refresh token that is no longer valid is used to retrieve a new\n * access token.) This functionality can be changed by configuring a custom\n * {@link ReactiveOAuth2AuthorizationFailureHandler} via\n * {@link #setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler)}.\n * </p>\n *\n * @author Ankur Pathak\n * @author Phil Clay\n * @since 5.2.2\n * @see ReactiveOAuth2AuthorizedClientManager\n * @see ReactiveOAuth2AuthorizedClientProvider\n * @see ReactiveOAuth2AuthorizedClientService\n * @see ReactiveOAuth2AuthorizationSuccessHandler\n * @see ReactiveOAuth2AuthorizationFailureHandler\n */\npublic final class AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager\n\t\timplements ReactiveOAuth2AuthorizedClientManager {\n\n\tprivate static final ReactiveOAuth2AuthorizedClientProvider DEFAULT_AUTHORIZED_CLIENT_PROVIDER = ReactiveOAuth2AuthorizedClientProviderBuilder\n\t\t.builder()\n\t\t.clientCredentials()\n\t\t.build();\n\n\tprivate final ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate final ReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = DEFAULT_AUTHORIZED_CLIENT_PROVIDER;\n\n\tprivate Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper = new DefaultContextAttributesMapper();\n\n\tprivate ReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler;\n\n\tprivate ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t/**\n\t * Constructs an {@code AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager}\n\t * using the provided parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientService the authorized client service\n\t */\n\tpublic AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tReactiveOAuth2AuthorizedClientService authorizedClientService) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientService, \"authorizedClientService cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClientService = authorizedClientService;\n\t\tthis.authorizationSuccessHandler = (authorizedClient, principal, attributes) -> authorizedClientService\n\t\t\t.saveAuthorizedClient(authorizedClient, principal);\n\t\tthis.authorizationFailureHandler = new RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> this.authorizedClientService\n\t\t\t\t\t.removeAuthorizedClient(clientRegistrationId, principal.getName()));\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {\n\t\tAssert.notNull(authorizeRequest, \"authorizeRequest cannot be null\");\n\t\treturn createAuthorizationContext(authorizeRequest)\n\t\t\t.flatMap((authorizationContext) -> authorize(authorizationContext, authorizeRequest.getPrincipal()));\n\t}\n\n\tprivate Mono<OAuth2AuthorizationContext> createAuthorizationContext(OAuth2AuthorizeRequest authorizeRequest) {\n\t\tString clientRegistrationId = authorizeRequest.getClientRegistrationId();\n\t\tAuthentication principal = authorizeRequest.getPrincipal();\n\t\treturn Mono.justOrEmpty(authorizeRequest.getAuthorizedClient())\n\t\t\t.map(OAuth2AuthorizationContext::withAuthorizedClient)\n\t\t\t.switchIfEmpty(Mono.defer(() -> this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId)\n\t\t\t\t.flatMap((clientRegistration) -> this.authorizedClientService\n\t\t\t\t\t.loadAuthorizedClient(clientRegistrationId, principal.getName())\n\t\t\t\t\t.map(OAuth2AuthorizationContext::withAuthorizedClient)\n\t\t\t\t\t.switchIfEmpty(Mono\n\t\t\t\t\t\t.fromSupplier(() -> OAuth2AuthorizationContext.withClientRegistration(clientRegistration))))\n\t\t\t\t.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(\n\t\t\t\t\t\t\"Could not find ClientRegistration with id '\" + clientRegistrationId + \"'\")))))\n\t\t\t.flatMap((contextBuilder) -> this.contextAttributesMapper.apply(authorizeRequest)\n\t\t\t\t.defaultIfEmpty(Collections.emptyMap())\n\t\t\t\t.map((contextAttributes) -> {\n\t\t\t\t\tOAuth2AuthorizationContext.Builder builder = contextBuilder.principal(principal);\n\t\t\t\t\tif (!contextAttributes.isEmpty()) {\n\t\t\t\t\t\tbuilder = builder.attributes((attributes) -> attributes.putAll(contextAttributes));\n\t\t\t\t\t}\n\t\t\t\t\treturn builder.build();\n\t\t\t\t}));\n\t}\n\n\t/**\n\t * Performs authorization and then delegates to either the\n\t * {@link #authorizationSuccessHandler} or {@link #authorizationFailureHandler},\n\t * depending on the authorization result.\n\t * @param authorizationContext the context to authorize\n\t * @param principal the principle to authorize\n\t * @return a {@link Mono} that emits the authorized client after the authorization\n\t * attempt succeeds and the {@link #authorizationSuccessHandler} has completed, or\n\t * completes with an exception after the authorization attempt fails and the\n\t * {@link #authorizationFailureHandler} has completed\n\t */\n\tprivate Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext authorizationContext,\n\t\t\tAuthentication principal) {\n\t\treturn this.authorizedClientProvider.authorize(authorizationContext)\n\t\t\t// Delegate to the authorizationSuccessHandler of the successful\n\t\t\t// authorization\n\t\t\t.flatMap((authorizedClient) -> this.authorizationSuccessHandler\n\t\t\t\t.onAuthorizationSuccess(authorizedClient, principal, Collections.emptyMap())\n\t\t\t\t.thenReturn(authorizedClient))\n\t\t\t// Delegate to the authorizationFailureHandler of the failed authorization\n\t\t\t.onErrorResume(OAuth2AuthorizationException.class,\n\t\t\t\t\t(authorizationException) -> this.authorizationFailureHandler\n\t\t\t\t\t\t.onAuthorizationFailure(authorizationException, principal, Collections.emptyMap())\n\t\t\t\t\t\t.then(Mono.error(authorizationException)))\n\t\t\t.switchIfEmpty(Mono.defer(() -> Mono.justOrEmpty(authorizationContext.getAuthorizedClient())));\n\t}\n\n\t/**\n\t * Sets the {@link ReactiveOAuth2AuthorizedClientProvider} used for authorizing (or\n\t * re-authorizing) an OAuth 2.0 Client.\n\t * @param authorizedClientProvider the {@link ReactiveOAuth2AuthorizedClientProvider}\n\t * used for authorizing (or re-authorizing) an OAuth 2.0 Client\n\t */\n\tpublic void setAuthorizedClientProvider(ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider) {\n\t\tAssert.notNull(authorizedClientProvider, \"authorizedClientProvider cannot be null\");\n\t\tthis.authorizedClientProvider = authorizedClientProvider;\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping attribute(s) from the\n\t * {@link OAuth2AuthorizeRequest} to a {@code Map} of attributes to be associated to\n\t * the {@link OAuth2AuthorizationContext#getAttributes() authorization context}.\n\t * @param contextAttributesMapper the {@code Function} used for supplying the\n\t * {@code Map} of attributes to the {@link OAuth2AuthorizationContext#getAttributes()\n\t * authorization context}\n\t */\n\tpublic void setContextAttributesMapper(\n\t\t\tFunction<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper) {\n\t\tAssert.notNull(contextAttributesMapper, \"contextAttributesMapper cannot be null\");\n\t\tthis.contextAttributesMapper = contextAttributesMapper;\n\t}\n\n\t/**\n\t * Sets the handler that handles successful authorizations.\n\t *\n\t * The default saves {@link OAuth2AuthorizedClient}s in the\n\t * {@link ReactiveOAuth2AuthorizedClientService}.\n\t * @param authorizationSuccessHandler the handler that handles successful\n\t * authorizations.\n\t * @since 5.3\n\t */\n\tpublic void setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler) {\n\t\tAssert.notNull(authorizationSuccessHandler, \"authorizationSuccessHandler cannot be null\");\n\t\tthis.authorizationSuccessHandler = authorizationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the handler that handles authorization failures.\n\t *\n\t * <p>\n\t * A {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} is used\n\t * by default.\n\t * </p>\n\t * @param authorizationFailureHandler the handler that handles authorization failures.\n\t * @since 5.3\n\t * @see RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler\n\t */\n\tpublic void setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\tthis.authorizationFailureHandler = authorizationFailureHandler;\n\t}\n\n\t/**\n\t * The default implementation of the {@link #setContextAttributesMapper(Function)\n\t * contextAttributesMapper}.\n\t */\n\tpublic static class DefaultContextAttributesMapper\n\t\t\timplements Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> {\n\n\t\tprivate final AuthorizedClientServiceOAuth2AuthorizedClientManager.DefaultContextAttributesMapper mapper = new AuthorizedClientServiceOAuth2AuthorizedClientManager.DefaultContextAttributesMapper();\n\n\t\t@Override\n\t\tpublic Mono<Map<String, Object>> apply(OAuth2AuthorizeRequest authorizeRequest) {\n\t\t\treturn Mono.fromCallable(() -> this.mapper.apply(authorizeRequest));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ClientAuthorizationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.io.Serial;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.util.Assert;\n\n/**\n * This exception is thrown on the client side when an attempt to authenticate or\n * authorize an OAuth 2.0 client fails.\n *\n * @author Phil Clay\n * @since 5.3\n * @see OAuth2AuthorizedClient\n */\npublic class ClientAuthorizationException extends OAuth2AuthorizationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 4710713969265443271L;\n\n\tprivate final String clientRegistrationId;\n\n\t/**\n\t * Constructs a {@code ClientAuthorizationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param clientRegistrationId the identifier for the client's registration\n\t */\n\tpublic ClientAuthorizationException(OAuth2Error error, String clientRegistrationId) {\n\t\tthis(error, clientRegistrationId, error.toString());\n\t}\n\n\t/**\n\t * Constructs a {@code ClientAuthorizationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param message the exception message\n\t */\n\tpublic ClientAuthorizationException(OAuth2Error error, String clientRegistrationId, String message) {\n\t\tsuper(error, message);\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tthis.clientRegistrationId = clientRegistrationId;\n\t}\n\n\t/**\n\t * Constructs a {@code ClientAuthorizationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param cause the root cause\n\t */\n\tpublic ClientAuthorizationException(OAuth2Error error, String clientRegistrationId, Throwable cause) {\n\t\tthis(error, clientRegistrationId, error.toString(), cause);\n\t}\n\n\t/**\n\t * Constructs a {@code ClientAuthorizationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param message the exception message\n\t * @param cause the root cause\n\t */\n\tpublic ClientAuthorizationException(OAuth2Error error, String clientRegistrationId, String message,\n\t\t\tThrowable cause) {\n\t\tsuper(error, message, cause);\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tthis.clientRegistrationId = clientRegistrationId;\n\t}\n\n\t/**\n\t * Returns the identifier for the client's registration.\n\t * @return the identifier for the client's registration\n\t */\n\tpublic String getClientRegistrationId() {\n\t\treturn this.clientRegistrationId;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ClientAuthorizationRequiredException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.io.Serial;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\n/**\n * This exception is thrown when an OAuth 2.0 Client is required to obtain authorization\n * from the Resource Owner.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AuthorizedClient\n */\npublic class ClientAuthorizationRequiredException extends ClientAuthorizationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5738646355203953667L;\n\n\tprivate static final String CLIENT_AUTHORIZATION_REQUIRED_ERROR_CODE = \"client_authorization_required\";\n\n\t/**\n\t * Constructs a {@code ClientAuthorizationRequiredException} using the provided\n\t * parameters.\n\t * @param clientRegistrationId the identifier for the client's registration\n\t */\n\tpublic ClientAuthorizationRequiredException(String clientRegistrationId) {\n\t\tsuper(new OAuth2Error(CLIENT_AUTHORIZATION_REQUIRED_ERROR_CODE,\n\t\t\t\t\"Authorization required for Client Registration Id: \" + clientRegistrationId, null),\n\t\t\t\tclientRegistrationId);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ClientCredentialsOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.RestClientClientCredentialsTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#CLIENT_CREDENTIALS client_credentials} grant.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientProvider\n * @see RestClientClientCredentialsTokenResponseClient\n */\npublic final class ClientCredentialsOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {\n\n\tprivate OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient = new RestClientClientCredentialsTokenResponseClient();\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Attempt to authorize (or re-authorize) the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * {@code context}. Returns {@code null} if authorization (or re-authorization) is not\n\t * supported, e.g. the client's {@link ClientRegistration#getAuthorizationGrantType()\n\t * authorization grant type} is not {@link AuthorizationGrantType#CLIENT_CREDENTIALS\n\t * client_credentials} OR the {@link OAuth2AuthorizedClient#getAccessToken() access\n\t * token} is not expired.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if authorization (or\n\t * re-authorization) is not supported\n\t */\n\t@Override\n\tpublic @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tClientRegistration clientRegistration = context.getClientRegistration();\n\t\tif (!AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();\n\t\tif (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {\n\t\t\t// If client is already authorized but access token is NOT expired than no\n\t\t\t// need for re-authorization\n\t\t\treturn null;\n\t\t}\n\t\t// As per spec, in section 4.4.3 Access Token Response\n\t\t// https://tools.ietf.org/html/rfc6749#section-4.4.3\n\t\t// A refresh token SHOULD NOT be included.\n\t\t//\n\t\t// Therefore, renewing an expired access token (re-authorization)\n\t\t// is the same as acquiring a new access token (authorization).\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tOAuth2AccessTokenResponse tokenResponse = getTokenResponse(clientRegistration, clientCredentialsGrantRequest);\n\t\treturn new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),\n\t\t\t\ttokenResponse.getAccessToken());\n\t}\n\n\tprivate OAuth2AccessTokenResponse getTokenResponse(ClientRegistration clientRegistration,\n\t\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {\n\t\ttry {\n\t\t\treturn this.accessTokenResponseClient.getTokenResponse(clientCredentialsGrantRequest);\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tthrow new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex);\n\t\t}\n\t}\n\n\tprivate boolean hasTokenExpired(OAuth2Token token) {\n\t\tInstant expiresAt = token.getExpiresAt();\n\t\treturn expiresAt != null && this.clock.instant().isAfter(expiresAt.minus(this.clockSkew));\n\t}\n\n\t/**\n\t * Sets the client used when requesting an access token credential at the Token\n\t * Endpoint for the {@code client_credentials} grant.\n\t * @param accessTokenResponseClient the client used when requesting an access token\n\t * credential at the Token Endpoint for the {@code client_credentials} grant\n\t */\n\tpublic void setAccessTokenResponseClient(\n\t\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is\n\t * 60 seconds.\n\t *\n\t * <p>\n\t * An access token is considered expired if\n\t * {@code OAuth2AccessToken#getExpiresAt() - clockSkew} is before the current time\n\t * {@code clock#instant()}.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access\n\t * token expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ClientCredentialsReactiveOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.WebClientReactiveClientCredentialsTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of a {@link ReactiveOAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#CLIENT_CREDENTIALS client_credentials} grant.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see ReactiveOAuth2AuthorizedClientProvider\n * @see WebClientReactiveClientCredentialsTokenResponseClient\n */\npublic final class ClientCredentialsReactiveOAuth2AuthorizedClientProvider\n\t\timplements ReactiveOAuth2AuthorizedClientProvider {\n\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient = new WebClientReactiveClientCredentialsTokenResponseClient();\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Attempt to authorize (or re-authorize) the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * {@code context}. Returns an empty {@code Mono} if authorization (or\n\t * re-authorization) is not supported, e.g. the client's\n\t * {@link ClientRegistration#getAuthorizationGrantType() authorization grant type} is\n\t * not {@link AuthorizationGrantType#CLIENT_CREDENTIALS client_credentials} OR the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} is not expired.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or an empty {@code Mono} if\n\t * authorization (or re-authorization) is not supported\n\t */\n\t@Override\n\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tClientRegistration clientRegistration = context.getClientRegistration();\n\t\tif (!AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tOAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();\n\t\tif (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {\n\t\t\t// If client is already authorized but access token is NOT expired than no\n\t\t\t// need for re-authorization\n\t\t\treturn Mono.empty();\n\t\t}\n\t\t// As per spec, in section 4.4.3 Access Token Response\n\t\t// https://tools.ietf.org/html/rfc6749#section-4.4.3\n\t\t// A refresh token SHOULD NOT be included.\n\t\t//\n\t\t// Therefore, renewing an expired access token (re-authorization)\n\t\t// is the same as acquiring a new access token (authorization).\n\t\treturn Mono.just(new OAuth2ClientCredentialsGrantRequest(clientRegistration))\n\t\t\t.flatMap(this.accessTokenResponseClient::getTokenResponse)\n\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t(ex) -> new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex))\n\t\t\t.map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),\n\t\t\t\t\ttokenResponse.getAccessToken()));\n\t}\n\n\tprivate boolean hasTokenExpired(OAuth2Token token) {\n\t\tInstant expiresAt = token.getExpiresAt();\n\t\treturn expiresAt != null && this.clock.instant().isAfter(expiresAt.minus(this.clockSkew));\n\t}\n\n\t/**\n\t * Sets the client used when requesting an access token credential at the Token\n\t * Endpoint for the {@code client_credentials} grant.\n\t * @param accessTokenResponseClient the client used when requesting an access token\n\t * credential at the Token Endpoint for the {@code client_credentials} grant\n\t */\n\tpublic void setAccessTokenResponseClient(\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is\n\t * 60 seconds.\n\t *\n\t * <p>\n\t * An access token is considered expired if\n\t * {@code OAuth2AccessToken#getExpiresAt() - clockSkew} is before the current time\n\t * {@code clock#instant()}.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access\n\t * token expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/DelegatingOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientProvider} that simply delegates to\n * its internal {@code List} of {@link OAuth2AuthorizedClientProvider}(s).\n * <p>\n * Each provider is given a chance to\n * {@link OAuth2AuthorizedClientProvider#authorize(OAuth2AuthorizationContext) authorize}\n * the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n * context with the first {@code non-null} {@link OAuth2AuthorizedClient} being returned.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientProvider\n */\npublic final class DelegatingOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {\n\n\tprivate final List<OAuth2AuthorizedClientProvider> authorizedClientProviders;\n\n\t/**\n\t * Constructs a {@code DelegatingOAuth2AuthorizedClientProvider} using the provided\n\t * parameters.\n\t * @param authorizedClientProviders a list of\n\t * {@link OAuth2AuthorizedClientProvider}(s)\n\t */\n\tpublic DelegatingOAuth2AuthorizedClientProvider(OAuth2AuthorizedClientProvider... authorizedClientProviders) {\n\t\tAssert.notEmpty(authorizedClientProviders, \"authorizedClientProviders cannot be empty\");\n\t\tthis.authorizedClientProviders = Collections.unmodifiableList(Arrays.asList(authorizedClientProviders));\n\t}\n\n\t/**\n\t * Constructs a {@code DelegatingOAuth2AuthorizedClientProvider} using the provided\n\t * parameters.\n\t * @param authorizedClientProviders a {@code List} of\n\t * {@link OAuth2AuthorizedClientProvider}(s)\n\t */\n\tpublic DelegatingOAuth2AuthorizedClientProvider(List<OAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tAssert.notEmpty(authorizedClientProviders, \"authorizedClientProviders cannot be empty\");\n\t\tthis.authorizedClientProviders = Collections.unmodifiableList(new ArrayList<>(authorizedClientProviders));\n\t}\n\n\t@Override\n\tpublic @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tfor (OAuth2AuthorizedClientProvider authorizedClientProvider : this.authorizedClientProviders) {\n\t\t\tOAuth2AuthorizedClient oauth2AuthorizedClient = authorizedClientProvider.authorize(context);\n\t\t\tif (oauth2AuthorizedClient != null) {\n\t\t\t\treturn oauth2AuthorizedClient;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/DelegatingReactiveOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of a {@link ReactiveOAuth2AuthorizedClientProvider} that simply\n * delegates to its internal {@code List} of\n * {@link ReactiveOAuth2AuthorizedClientProvider}(s).\n * <p>\n * Each provider is given a chance to\n * {@link ReactiveOAuth2AuthorizedClientProvider#authorize(OAuth2AuthorizationContext)\n * authorize} the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the\n * provided context with the first available {@link OAuth2AuthorizedClient} being\n * returned.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see ReactiveOAuth2AuthorizedClientProvider\n */\npublic final class DelegatingReactiveOAuth2AuthorizedClientProvider implements ReactiveOAuth2AuthorizedClientProvider {\n\n\tprivate final List<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders;\n\n\t/**\n\t * Constructs a {@code DelegatingReactiveOAuth2AuthorizedClientProvider} using the\n\t * provided parameters.\n\t * @param authorizedClientProviders a list of\n\t * {@link ReactiveOAuth2AuthorizedClientProvider}(s)\n\t */\n\tpublic DelegatingReactiveOAuth2AuthorizedClientProvider(\n\t\t\tReactiveOAuth2AuthorizedClientProvider... authorizedClientProviders) {\n\t\tAssert.notEmpty(authorizedClientProviders, \"authorizedClientProviders cannot be empty\");\n\t\tthis.authorizedClientProviders = Collections.unmodifiableList(Arrays.asList(authorizedClientProviders));\n\t}\n\n\t/**\n\t * Constructs a {@code DelegatingReactiveOAuth2AuthorizedClientProvider} using the\n\t * provided parameters.\n\t * @param authorizedClientProviders a {@code List} of\n\t * {@link OAuth2AuthorizedClientProvider}(s)\n\t */\n\tpublic DelegatingReactiveOAuth2AuthorizedClientProvider(\n\t\t\tList<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders) {\n\t\tAssert.notEmpty(authorizedClientProviders, \"authorizedClientProviders cannot be empty\");\n\t\tthis.authorizedClientProviders = Collections.unmodifiableList(new ArrayList<>(authorizedClientProviders));\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\treturn Flux.fromIterable(this.authorizedClientProviders)\n\t\t\t.concatMap((authorizedClientProvider) -> authorizedClientProvider.authorize(context))\n\t\t\t.next();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/InMemoryOAuth2AuthorizedClientService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthorizedClientService} that stores {@link OAuth2AuthorizedClient\n * Authorized Client(s)} in-memory.\n *\n * @author Joe Grandja\n * @author Vedran Pavic\n * @since 5.0\n * @see OAuth2AuthorizedClientService\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientId\n * @see ClientRegistration\n * @see Authentication\n */\npublic final class InMemoryOAuth2AuthorizedClientService implements OAuth2AuthorizedClientService {\n\n\tprivate final Map<OAuth2AuthorizedClientId, OAuth2AuthorizedClient> authorizedClients;\n\n\tprivate final ClientRegistrationRepository clientRegistrationRepository;\n\n\t/**\n\t * Constructs an {@code InMemoryOAuth2AuthorizedClientService} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t */\n\tpublic InMemoryOAuth2AuthorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClients = new ConcurrentHashMap<>();\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryOAuth2AuthorizedClientService} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClients the initial {@code Map} of authorized client(s) keyed by\n\t * {@link OAuth2AuthorizedClientId}\n\t * @since 5.2\n\t */\n\tpublic InMemoryOAuth2AuthorizedClientService(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tMap<OAuth2AuthorizedClientId, OAuth2AuthorizedClient> authorizedClients) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notEmpty(authorizedClients, \"authorizedClients cannot be empty\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClients = new ConcurrentHashMap<>(authorizedClients);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends OAuth2AuthorizedClient> @Nullable T loadAuthorizedClient(String clientRegistrationId,\n\t\t\tString principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tClientRegistration registration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId);\n\t\tif (registration == null) {\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthorizedClient cachedAuthorizedClient = this.authorizedClients\n\t\t\t.get(new OAuth2AuthorizedClientId(clientRegistrationId, principalName));\n\t\tif (cachedAuthorizedClient == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (T) new OAuth2AuthorizedClient(registration, cachedAuthorizedClient.getPrincipalName(),\n\t\t\t\tcachedAuthorizedClient.getAccessToken(), cachedAuthorizedClient.getRefreshToken());\n\t}\n\n\t@Override\n\tpublic void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tthis.authorizedClients.put(new OAuth2AuthorizedClientId(\n\t\t\t\tauthorizedClient.getClientRegistration().getRegistrationId(), principal.getName()), authorizedClient);\n\t}\n\n\t@Override\n\tpublic void removeAuthorizedClient(String clientRegistrationId, String principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tClientRegistration registration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId);\n\t\tif (registration != null) {\n\t\t\tthis.authorizedClients.remove(new OAuth2AuthorizedClientId(clientRegistrationId, principalName));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/InMemoryReactiveOAuth2AuthorizedClientService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthorizedClientService} that stores {@link OAuth2AuthorizedClient\n * Authorized Client(s)} in-memory.\n *\n * @author Rob Winch\n * @author Vedran Pavic\n * @since 5.1\n * @see OAuth2AuthorizedClientService\n * @see OAuth2AuthorizedClient\n * @see ClientRegistration\n * @see Authentication\n */\npublic final class InMemoryReactiveOAuth2AuthorizedClientService implements ReactiveOAuth2AuthorizedClientService {\n\n\tprivate final Map<OAuth2AuthorizedClientId, OAuth2AuthorizedClient> authorizedClients = new ConcurrentHashMap<>();\n\n\tprivate final ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t/**\n\t * Constructs an {@code InMemoryReactiveOAuth2AuthorizedClientService} using the\n\t * provided parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t */\n\tpublic InMemoryReactiveOAuth2AuthorizedClientService(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId,\n\t\t\tString principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\treturn (Mono<T>) this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId)\n\t\t\t.flatMap((clientRegistration) -> {\n\t\t\t\tOAuth2AuthorizedClientId id = new OAuth2AuthorizedClientId(clientRegistrationId, principalName);\n\t\t\t\tOAuth2AuthorizedClient cachedAuthorizedClient = this.authorizedClients.get(id);\n\t\t\t\tif (cachedAuthorizedClient == null) {\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t}\n\t\t\t\treturn Mono\n\t\t\t\t\t.just(new OAuth2AuthorizedClient(clientRegistration, cachedAuthorizedClient.getPrincipalName(),\n\t\t\t\t\t\t\tcachedAuthorizedClient.getAccessToken(), cachedAuthorizedClient.getRefreshToken()));\n\t\t\t});\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\treturn Mono.fromRunnable(() -> {\n\t\t\tOAuth2AuthorizedClientId identifier = new OAuth2AuthorizedClientId(\n\t\t\t\t\tauthorizedClient.getClientRegistration().getRegistrationId(), principal.getName());\n\t\t\tthis.authorizedClients.put(identifier, authorizedClient);\n\t\t});\n\t}\n\n\t@Override\n\tpublic Mono<Void> removeAuthorizedClient(String clientRegistrationId, String principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\t// @formatter:off\n\t\treturn this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId)\n\t\t\t\t.map((clientRegistration) -> new OAuth2AuthorizedClientId(clientRegistrationId, principalName))\n\t\t\t\t.doOnNext(this.authorizedClients::remove)\n\t\t\t\t.then(Mono.empty());\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.nio.charset.StandardCharsets;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.jdbc.support.lob.DefaultLobHandler;\nimport org.springframework.jdbc.support.lob.LobCreator;\nimport org.springframework.jdbc.support.lob.LobHandler;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A JDBC implementation of an {@link OAuth2AuthorizedClientService} that uses a\n * {@link JdbcOperations} for {@link OAuth2AuthorizedClient} persistence.\n *\n * <p>\n * <b>NOTE:</b> This {@code OAuth2AuthorizedClientService} depends on the table definition\n * described in\n * \"classpath:org/springframework/security/oauth2/client/oauth2-client-schema.sql\" and\n * therefore MUST be defined in the database schema.\n *\n * @author Joe Grandja\n * @author Stav Shamir\n * @author Craig Andrews\n * @since 5.3\n * @see OAuth2AuthorizedClientService\n * @see OAuth2AuthorizedClient\n * @see JdbcOperations\n * @see RowMapper\n */\npublic class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClientService {\n\n\t// @formatter:off\n\tprivate static final String COLUMN_NAMES = \"client_registration_id, \"\n\t\t\t+ \"principal_name, \"\n\t\t\t+ \"access_token_type, \"\n\t\t\t+ \"access_token_value, \"\n\t\t\t+ \"access_token_issued_at, \"\n\t\t\t+ \"access_token_expires_at, \"\n\t\t\t+ \"access_token_scopes, \"\n\t\t\t+ \"refresh_token_value, \"\n\t\t\t+ \"refresh_token_issued_at\";\n\t// @formatter:on\n\n\tprivate static final String TABLE_NAME = \"oauth2_authorized_client\";\n\n\tprivate static final String PK_FILTER = \"client_registration_id = ? AND principal_name = ?\";\n\n\t// @formatter:off\n\tprivate static final String LOAD_AUTHORIZED_CLIENT_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + PK_FILTER;\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String SAVE_AUTHORIZED_CLIENT_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\";\n\t// @formatter:on\n\n\tprivate static final String REMOVE_AUTHORIZED_CLIENT_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + PK_FILTER;\n\n\t// @formatter:off\n\tprivate static final String UPDATE_AUTHORIZED_CLIENT_SQL = \"UPDATE \" + TABLE_NAME\n\t\t\t+ \" SET access_token_type = ?, access_token_value = ?, access_token_issued_at = ?,\"\n\t\t\t+ \" access_token_expires_at = ?, access_token_scopes = ?,\"\n\t\t\t+ \" refresh_token_value = ?, refresh_token_issued_at = ?\"\n\t\t\t+ \" WHERE \" + PK_FILTER;\n\t// @formatter:on\n\n\tprotected final JdbcOperations jdbcOperations;\n\n\tprotected RowMapper<OAuth2AuthorizedClient> authorizedClientRowMapper;\n\n\tprotected Function<OAuth2AuthorizedClientHolder, List<SqlParameterValue>> authorizedClientParametersMapper;\n\n\tprotected final LobHandler lobHandler;\n\n\t/**\n\t * Constructs a {@code JdbcOAuth2AuthorizedClientService} using the provided\n\t * parameters.\n\t * @param jdbcOperations the JDBC operations\n\t * @param clientRegistrationRepository the repository of client registrations\n\t */\n\tpublic JdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,\n\t\t\tClientRegistrationRepository clientRegistrationRepository) {\n\t\tthis(jdbcOperations, clientRegistrationRepository, new DefaultLobHandler());\n\t}\n\n\t/**\n\t * Constructs a {@code JdbcOAuth2AuthorizedClientService} using the provided\n\t * parameters.\n\t * @param jdbcOperations the JDBC operations\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param lobHandler the handler for large binary fields and large text fields\n\t * @since 5.5\n\t */\n\tpublic JdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,\n\t\t\tClientRegistrationRepository clientRegistrationRepository, LobHandler lobHandler) {\n\t\tAssert.notNull(jdbcOperations, \"jdbcOperations cannot be null\");\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(lobHandler, \"lobHandler cannot be null\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t\tthis.lobHandler = lobHandler;\n\t\tOAuth2AuthorizedClientRowMapper authorizedClientRowMapper = new OAuth2AuthorizedClientRowMapper(\n\t\t\t\tclientRegistrationRepository);\n\t\tauthorizedClientRowMapper.setLobHandler(lobHandler);\n\t\tthis.authorizedClientRowMapper = authorizedClientRowMapper;\n\t\tthis.authorizedClientParametersMapper = new OAuth2AuthorizedClientParametersMapper();\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends OAuth2AuthorizedClient> @Nullable T loadAuthorizedClient(String clientRegistrationId,\n\t\t\tString principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, clientRegistrationId),\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, principalName) };\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\tList<OAuth2AuthorizedClient> result = this.jdbcOperations.query(LOAD_AUTHORIZED_CLIENT_SQL, pss,\n\t\t\t\tthis.authorizedClientRowMapper);\n\t\treturn !result.isEmpty() ? (T) result.get(0) : null;\n\t}\n\n\t@Override\n\tpublic void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tint rows = updateAuthorizedClient(authorizedClient, principal);\n\t\tif (rows == 0) {\n\t\t\tinsertAuthorizedClient(authorizedClient, principal);\n\t\t}\n\t}\n\n\tprivate int updateAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\tList<SqlParameterValue> parameters = this.authorizedClientParametersMapper\n\t\t\t.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));\n\t\tSqlParameterValue clientRegistrationIdParameter = parameters.remove(0);\n\t\tSqlParameterValue principalNameParameter = parameters.remove(0);\n\t\tparameters.add(clientRegistrationIdParameter);\n\t\tparameters.add(principalNameParameter);\n\t\ttry (LobCreator lobCreator = this.lobHandler.getLobCreator()) {\n\t\t\tPreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,\n\t\t\t\t\tparameters.toArray());\n\t\t\treturn this.jdbcOperations.update(UPDATE_AUTHORIZED_CLIENT_SQL, pss);\n\t\t}\n\t}\n\n\tprivate void insertAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\tList<SqlParameterValue> parameters = this.authorizedClientParametersMapper\n\t\t\t.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));\n\t\ttry (LobCreator lobCreator = this.lobHandler.getLobCreator()) {\n\t\t\tPreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,\n\t\t\t\t\tparameters.toArray());\n\t\t\tthis.jdbcOperations.update(SAVE_AUTHORIZED_CLIENT_SQL, pss);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeAuthorizedClient(String clientRegistrationId, String principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, clientRegistrationId),\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, principalName) };\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\tthis.jdbcOperations.update(REMOVE_AUTHORIZED_CLIENT_SQL, pss);\n\t}\n\n\t/**\n\t * Sets the {@link RowMapper} used for mapping the current row in\n\t * {@code java.sql.ResultSet} to {@link OAuth2AuthorizedClient}. The default is\n\t * {@link OAuth2AuthorizedClientRowMapper}.\n\t * @param authorizedClientRowMapper the {@link RowMapper} used for mapping the current\n\t * row in {@code java.sql.ResultSet} to {@link OAuth2AuthorizedClient}\n\t */\n\tpublic final void setAuthorizedClientRowMapper(RowMapper<OAuth2AuthorizedClient> authorizedClientRowMapper) {\n\t\tAssert.notNull(authorizedClientRowMapper, \"authorizedClientRowMapper cannot be null\");\n\t\tthis.authorizedClientRowMapper = authorizedClientRowMapper;\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping {@link OAuth2AuthorizedClientHolder} to\n\t * a {@code List} of {@link SqlParameterValue}. The default is\n\t * {@link OAuth2AuthorizedClientParametersMapper}.\n\t * @param authorizedClientParametersMapper the {@code Function} used for mapping\n\t * {@link OAuth2AuthorizedClientHolder} to a {@code List} of {@link SqlParameterValue}\n\t */\n\tpublic final void setAuthorizedClientParametersMapper(\n\t\t\tFunction<OAuth2AuthorizedClientHolder, List<SqlParameterValue>> authorizedClientParametersMapper) {\n\t\tAssert.notNull(authorizedClientParametersMapper, \"authorizedClientParametersMapper cannot be null\");\n\t\tthis.authorizedClientParametersMapper = authorizedClientParametersMapper;\n\t}\n\n\t/**\n\t * The default {@link RowMapper} that maps the current row in\n\t * {@code java.sql.ResultSet} to {@link OAuth2AuthorizedClient}.\n\t */\n\tpublic static class OAuth2AuthorizedClientRowMapper implements RowMapper<OAuth2AuthorizedClient> {\n\n\t\tprotected final ClientRegistrationRepository clientRegistrationRepository;\n\n\t\tprotected LobHandler lobHandler = new DefaultLobHandler();\n\n\t\tpublic OAuth2AuthorizedClientRowMapper(ClientRegistrationRepository clientRegistrationRepository) {\n\t\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\t}\n\n\t\tpublic final void setLobHandler(LobHandler lobHandler) {\n\t\t\tAssert.notNull(lobHandler, \"lobHandler cannot be null\");\n\t\t\tthis.lobHandler = lobHandler;\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2AuthorizedClient mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\tString clientRegistrationId = rs.getString(\"client_registration_id\");\n\t\t\tClientRegistration clientRegistration = this.clientRegistrationRepository\n\t\t\t\t.findByRegistrationId(clientRegistrationId);\n\t\t\tif (clientRegistration == null) {\n\t\t\t\tthrow new DataRetrievalFailureException(\n\t\t\t\t\t\t\"The ClientRegistration with id '\" + clientRegistrationId + \"' exists in the data source, \"\n\t\t\t\t\t\t\t\t+ \"however, it was not found in the ClientRegistrationRepository.\");\n\t\t\t}\n\t\t\tOAuth2AccessToken.TokenType tokenType = null;\n\t\t\tif (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString(\"access_token_type\"))) {\n\t\t\t\ttokenType = OAuth2AccessToken.TokenType.BEARER;\n\t\t\t}\n\t\t\tOAuth2AccessToken.TokenType tokenTypeToUse = (tokenType != null) ? tokenType\n\t\t\t\t\t: OAuth2AccessToken.TokenType.BEARER;\n\t\t\tString tokenValue = new String(this.lobHandler.getBlobAsBytes(rs, \"access_token_value\"),\n\t\t\t\t\tStandardCharsets.UTF_8);\n\t\t\tTimestamp issuedAtTs = rs.getTimestamp(\"access_token_issued_at\");\n\t\t\tTimestamp expiresAtTs = rs.getTimestamp(\"access_token_expires_at\");\n\t\t\tInstant issuedAt = (issuedAtTs != null) ? issuedAtTs.toInstant() : null;\n\t\t\tInstant expiresAt = (expiresAtTs != null) ? expiresAtTs.toInstant() : null;\n\t\t\tSet<String> scopes = Collections.emptySet();\n\t\t\tString accessTokenScopes = rs.getString(\"access_token_scopes\");\n\t\t\tif (accessTokenScopes != null) {\n\t\t\t\tscopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);\n\t\t\t}\n\t\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(tokenTypeToUse, tokenValue, issuedAt, expiresAt,\n\t\t\t\t\tscopes);\n\t\t\tOAuth2RefreshToken refreshToken = null;\n\t\t\tbyte[] refreshTokenValue = this.lobHandler.getBlobAsBytes(rs, \"refresh_token_value\");\n\t\t\tif (refreshTokenValue != null) {\n\t\t\t\ttokenValue = new String(refreshTokenValue, StandardCharsets.UTF_8);\n\t\t\t\tissuedAt = null;\n\t\t\t\tTimestamp refreshTokenIssuedAt = rs.getTimestamp(\"refresh_token_issued_at\");\n\t\t\t\tif (refreshTokenIssuedAt != null) {\n\t\t\t\t\tissuedAt = refreshTokenIssuedAt.toInstant();\n\t\t\t\t}\n\t\t\t\trefreshToken = new OAuth2RefreshToken(tokenValue, issuedAt);\n\t\t\t}\n\t\t\tString principalName = rs.getString(\"principal_name\");\n\t\t\treturn new OAuth2AuthorizedClient(clientRegistration, principalName, accessToken, refreshToken);\n\t\t}\n\n\t}\n\n\t/**\n\t * The default {@code Function} that maps {@link OAuth2AuthorizedClientHolder} to a\n\t * {@code List} of {@link SqlParameterValue}.\n\t */\n\tpublic static class OAuth2AuthorizedClientParametersMapper\n\t\t\timplements Function<OAuth2AuthorizedClientHolder, List<SqlParameterValue>> {\n\n\t\t@Override\n\t\tpublic List<SqlParameterValue> apply(OAuth2AuthorizedClientHolder authorizedClientHolder) {\n\t\t\tOAuth2AuthorizedClient authorizedClient = authorizedClientHolder.getAuthorizedClient();\n\t\t\tAuthentication principal = authorizedClientHolder.getPrincipal();\n\t\t\tClientRegistration clientRegistration = authorizedClient.getClientRegistration();\n\t\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\t\t\tOAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken();\n\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, clientRegistration.getRegistrationId()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, principal.getName()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, accessToken.getTokenType().getValue()));\n\t\t\tparameters\n\t\t\t\t.add(new SqlParameterValue(Types.BLOB, accessToken.getTokenValue().getBytes(StandardCharsets.UTF_8)));\n\t\t\tInstant accessTokenIssuedAt = accessToken.getIssuedAt();\n\t\t\tInstant accessTokenExpiresAt = accessToken.getExpiresAt();\n\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP,\n\t\t\t\t\t(accessTokenIssuedAt != null) ? Timestamp.from(accessTokenIssuedAt) : null));\n\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP,\n\t\t\t\t\t(accessTokenExpiresAt != null) ? Timestamp.from(accessTokenExpiresAt) : null));\n\t\t\tString accessTokenScopes = null;\n\t\t\tif (!CollectionUtils.isEmpty(accessToken.getScopes())) {\n\t\t\t\taccessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getScopes(), \",\");\n\t\t\t}\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenScopes));\n\t\t\tbyte[] refreshTokenValue = null;\n\t\t\tTimestamp refreshTokenIssuedAt = null;\n\t\t\tif (refreshToken != null) {\n\t\t\t\trefreshTokenValue = refreshToken.getTokenValue().getBytes(StandardCharsets.UTF_8);\n\t\t\t\tif (refreshToken.getIssuedAt() != null) {\n\t\t\t\t\trefreshTokenIssuedAt = Timestamp.from(refreshToken.getIssuedAt());\n\t\t\t\t}\n\t\t\t}\n\t\t\tparameters.add(new SqlParameterValue(Types.BLOB, refreshTokenValue));\n\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP, refreshTokenIssuedAt));\n\t\t\treturn parameters;\n\t\t}\n\n\t}\n\n\t/**\n\t * A holder for an {@link OAuth2AuthorizedClient} and End-User {@link Authentication}\n\t * (Resource Owner).\n\t */\n\tpublic static final class OAuth2AuthorizedClientHolder {\n\n\t\tprivate final OAuth2AuthorizedClient authorizedClient;\n\n\t\tprivate final Authentication principal;\n\n\t\t/**\n\t\t * Constructs an {@code OAuth2AuthorizedClientHolder} using the provided\n\t\t * parameters.\n\t\t * @param authorizedClient the authorized client\n\t\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t\t */\n\t\tpublic OAuth2AuthorizedClientHolder(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.authorizedClient = authorizedClient;\n\t\t\tthis.principal = principal;\n\t\t}\n\n\t\t/**\n\t\t * Returns the {@link OAuth2AuthorizedClient}.\n\t\t * @return the {@link OAuth2AuthorizedClient}\n\t\t */\n\t\tpublic OAuth2AuthorizedClient getAuthorizedClient() {\n\t\t\treturn this.authorizedClient;\n\t\t}\n\n\t\t/**\n\t\t * Returns the End-User {@link Authentication} (Resource Owner).\n\t\t * @return the End-User {@link Authentication} (Resource Owner)\n\t\t */\n\t\tpublic Authentication getPrincipal() {\n\t\t\treturn this.principal;\n\t\t}\n\n\t}\n\n\tprivate static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {\n\n\t\tprotected final LobCreator lobCreator;\n\n\t\tprivate LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) {\n\t\t\tsuper(args);\n\t\t\tthis.lobCreator = lobCreator;\n\t\t}\n\n\t\t@Override\n\t\tprotected void doSetValue(PreparedStatement ps, int parameterPosition, @Nullable Object argValue)\n\t\t\t\tthrows SQLException {\n\t\t\tif (argValue instanceof SqlParameterValue paramValue) {\n\t\t\t\tif (paramValue.getSqlType() == Types.BLOB) {\n\t\t\t\t\tif (paramValue.getValue() != null) {\n\t\t\t\t\t\tAssert.isInstanceOf(byte[].class, paramValue.getValue(),\n\t\t\t\t\t\t\t\t\"Value of blob parameter must be byte[]\");\n\t\t\t\t\t}\n\t\t\t\t\tbyte[] valueBytes = (byte[]) paramValue.getValue();\n\t\t\t\t\tthis.lobCreator.setBlobAsBytes(ps, parameterPosition, valueBytes);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsuper.doSetValue(ps, parameterPosition, argValue);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JwtBearerOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.RestClientJwtBearerTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#JWT_BEARER jwt-bearer} grant.\n *\n * @author Joe Grandja\n * @since 5.5\n * @see OAuth2AuthorizedClientProvider\n * @see RestClientJwtBearerTokenResponseClient\n */\npublic final class JwtBearerOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {\n\n\tprivate OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient = new RestClientJwtBearerTokenResponseClient();\n\n\tprivate Function<OAuth2AuthorizationContext, @Nullable Jwt> jwtAssertionResolver = this::resolveJwtAssertion;\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Attempt to authorize (or re-authorize) the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * {@code context}. Returns {@code null} if authorization (or re-authorization) is not\n\t * supported, e.g. the client's {@link ClientRegistration#getAuthorizationGrantType()\n\t * authorization grant type} is not {@link AuthorizationGrantType#JWT_BEARER\n\t * jwt-bearer} OR the {@link OAuth2AuthorizedClient#getAccessToken() access token} is\n\t * not expired.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if authorization is not\n\t * supported\n\t */\n\t@Override\n\tpublic @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tClientRegistration clientRegistration = context.getClientRegistration();\n\t\tif (!AuthorizationGrantType.JWT_BEARER.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();\n\t\tif (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {\n\t\t\t// If client is already authorized but access token is NOT expired than no\n\t\t\t// need for re-authorization\n\t\t\treturn null;\n\t\t}\n\t\tJwt jwt = this.jwtAssertionResolver.apply(context);\n\t\tif (jwt == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// As per spec, in section 4.1 Using Assertions as Authorization Grants\n\t\t// https://tools.ietf.org/html/rfc7521#section-4.1\n\t\t//\n\t\t// An assertion used in this context is generally a short-lived\n\t\t// representation of the authorization grant, and authorization servers\n\t\t// SHOULD NOT issue access tokens with a lifetime that exceeds the\n\t\t// validity period of the assertion by a significant period. In\n\t\t// practice, that will usually mean that refresh tokens are not issued\n\t\t// in response to assertion grant requests, and access tokens will be\n\t\t// issued with a reasonably short lifetime. Clients can refresh an\n\t\t// expired access token by requesting a new one using the same\n\t\t// assertion, if it is still valid, or with a new assertion.\n\t\tJwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest(clientRegistration, jwt);\n\t\tOAuth2AccessTokenResponse tokenResponse = getTokenResponse(clientRegistration, jwtBearerGrantRequest);\n\t\treturn new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),\n\t\t\t\ttokenResponse.getAccessToken());\n\t}\n\n\tprivate @Nullable Jwt resolveJwtAssertion(OAuth2AuthorizationContext context) {\n\t\tif (!(context.getPrincipal().getPrincipal() instanceof Jwt)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (Jwt) context.getPrincipal().getPrincipal();\n\t}\n\n\tprivate OAuth2AccessTokenResponse getTokenResponse(ClientRegistration clientRegistration,\n\t\t\tJwtBearerGrantRequest jwtBearerGrantRequest) {\n\t\ttry {\n\t\t\treturn this.accessTokenResponseClient.getTokenResponse(jwtBearerGrantRequest);\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tthrow new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex);\n\t\t}\n\t}\n\n\tprivate boolean hasTokenExpired(OAuth2Token token) {\n\t\tInstant expiresAt = token.getExpiresAt();\n\t\treturn expiresAt != null && this.clock.instant().isAfter(expiresAt.minus(this.clockSkew));\n\t}\n\n\t/**\n\t * Sets the client used when requesting an access token credential at the Token\n\t * Endpoint for the {@code jwt-bearer} grant.\n\t * @param accessTokenResponseClient the client used when requesting an access token\n\t * credential at the Token Endpoint for the {@code jwt-bearer} grant\n\t */\n\tpublic void setAccessTokenResponseClient(\n\t\t\tOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t/**\n\t * Sets the resolver used for resolving the {@link Jwt} assertion.\n\t * @param jwtAssertionResolver the resolver used for resolving the {@link Jwt}\n\t * assertion\n\t * @since 5.7\n\t */\n\tpublic void setJwtAssertionResolver(Function<OAuth2AuthorizationContext, @Nullable Jwt> jwtAssertionResolver) {\n\t\tAssert.notNull(jwtAssertionResolver, \"jwtAssertionResolver cannot be null\");\n\t\tthis.jwtAssertionResolver = jwtAssertionResolver;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is\n\t * 60 seconds.\n\t *\n\t * <p>\n\t * An access token is considered expired if\n\t * {@code OAuth2AccessToken#getExpiresAt() - clockSkew} is before the current time\n\t * {@code clock#instant()}.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access\n\t * token expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JwtBearerReactiveOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.WebClientReactiveJwtBearerTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link ReactiveOAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#JWT_BEARER jwt-bearer} grant.\n *\n * @author Steve Riesenberg\n * @since 5.6\n * @see ReactiveOAuth2AuthorizedClientProvider\n * @see WebClientReactiveJwtBearerTokenResponseClient\n */\npublic final class JwtBearerReactiveOAuth2AuthorizedClientProvider implements ReactiveOAuth2AuthorizedClientProvider {\n\n\tprivate ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient = new WebClientReactiveJwtBearerTokenResponseClient();\n\n\tprivate Function<OAuth2AuthorizationContext, Mono<Jwt>> jwtAssertionResolver = this::resolveJwtAssertion;\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Attempt to authorize (or re-authorize) the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * {@code context}. Returns an empty {@code Mono} if authorization (or\n\t * re-authorization) is not supported, e.g. the client's\n\t * {@link ClientRegistration#getAuthorizationGrantType() authorization grant type} is\n\t * not {@link AuthorizationGrantType#JWT_BEARER jwt-bearer} OR the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} is not expired.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or an empty {@code Mono} if\n\t * authorization is not supported\n\t */\n\t@Override\n\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tClientRegistration clientRegistration = context.getClientRegistration();\n\t\tif (!AuthorizationGrantType.JWT_BEARER.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tOAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();\n\t\tif (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {\n\t\t\t// If client is already authorized but access token is NOT expired than no\n\t\t\t// need for re-authorization\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\t// As per spec, in section 4.1 Using Assertions as Authorization Grants\n\t\t// https://tools.ietf.org/html/rfc7521#section-4.1\n\t\t//\n\t\t// An assertion used in this context is generally a short-lived\n\t\t// representation of the authorization grant, and authorization servers\n\t\t// SHOULD NOT issue access tokens with a lifetime that exceeds the\n\t\t// validity period of the assertion by a significant period. In\n\t\t// practice, that will usually mean that refresh tokens are not issued\n\t\t// in response to assertion grant requests, and access tokens will be\n\t\t// issued with a reasonably short lifetime. Clients can refresh an\n\t\t// expired access token by requesting a new one using the same\n\t\t// assertion, if it is still valid, or with a new assertion.\n\n\t\t// @formatter:off\n\t\treturn this.jwtAssertionResolver.apply(context)\n\t\t\t\t.map((jwt) -> new JwtBearerGrantRequest(clientRegistration, jwt))\n\t\t\t\t.flatMap(this.accessTokenResponseClient::getTokenResponse)\n\t\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t\t(ex) -> new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(),\n\t\t\t\t\t\t\t\tex))\n\t\t\t\t.map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),\n\t\t\t\t\t\ttokenResponse.getAccessToken()));\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Jwt> resolveJwtAssertion(OAuth2AuthorizationContext context) {\n\t\t// @formatter:off\n\t\treturn Mono.just(context)\n\t\t\t\t.flatMap((ctx) -> {\n\t\t\t\t\tObject principal = ctx.getPrincipal().getPrincipal();\n\t\t\t\t\treturn (principal instanceof Jwt)\n\t\t\t\t\t\t\t? Mono.just((Jwt) principal)\n\t\t\t\t\t\t\t: Mono.empty();\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate boolean hasTokenExpired(OAuth2Token token) {\n\t\tInstant expiresAt = token.getExpiresAt();\n\t\treturn expiresAt != null && this.clock.instant().isAfter(expiresAt.minus(this.clockSkew));\n\t}\n\n\t/**\n\t * Sets the client used when requesting an access token credential at the Token\n\t * Endpoint for the {@code jwt-bearer} grant.\n\t * @param accessTokenResponseClient the client used when requesting an access token\n\t * credential at the Token Endpoint for the {@code jwt-bearer} grant\n\t */\n\tpublic void setAccessTokenResponseClient(\n\t\t\tReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t/**\n\t * Sets the resolver used for resolving the {@link Jwt} assertion.\n\t * @param jwtAssertionResolver the resolver used for resolving the {@link Jwt}\n\t * assertion\n\t * @since 5.7\n\t */\n\tpublic void setJwtAssertionResolver(Function<OAuth2AuthorizationContext, Mono<Jwt>> jwtAssertionResolver) {\n\t\tAssert.notNull(jwtAssertionResolver, \"jwtAssertionResolver cannot be null\");\n\t\tthis.jwtAssertionResolver = jwtAssertionResolver;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is\n\t * 60 seconds.\n\t *\n\t * <p>\n\t * An access token is considered expired if\n\t * {@code OAuth2AccessToken#getExpiresAt() - clockSkew} is before the current time\n\t * {@code clock#instant()}.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access\n\t * token expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A context that holds authorization-specific state and is used by an\n * {@link OAuth2AuthorizedClientProvider} when attempting to authorize (or re-authorize)\n * an OAuth 2.0 Client.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientProvider\n */\npublic final class OAuth2AuthorizationContext {\n\n\t/**\n\t * The name of the {@link #getAttribute(String) attribute} in the context associated\n\t * to the value for the \"request scope(s)\". The value of the attribute is a\n\t * {@code String[]} of scope(s) to be requested by the {@link #getClientRegistration()\n\t * client}.\n\t */\n\tpublic static final String REQUEST_SCOPE_ATTRIBUTE_NAME = OAuth2AuthorizationContext.class.getName()\n\t\t.concat(\".REQUEST_SCOPE\");\n\n\tprivate @Nullable ClientRegistration clientRegistration;\n\n\tprivate @Nullable OAuth2AuthorizedClient authorizedClient;\n\n\tprivate @Nullable Authentication principal;\n\n\tprivate @Nullable Map<String, Object> attributes;\n\n\tprivate OAuth2AuthorizationContext() {\n\t}\n\n\t/**\n\t * Returns the {@link ClientRegistration client registration}.\n\t * @return the {@link ClientRegistration}\n\t */\n\tpublic ClientRegistration getClientRegistration() {\n\t\tAssert.notNull(this.clientRegistration, \"clientRegistration cannot be null\");\n\t\treturn this.clientRegistration;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizedClient authorized client} or {@code null} if the\n\t * {@link #withClientRegistration(ClientRegistration) client registration} was\n\t * supplied.\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if the client\n\t * registration was supplied\n\t */\n\tpublic @Nullable OAuth2AuthorizedClient getAuthorizedClient() {\n\t\treturn this.authorizedClient;\n\t}\n\n\t/**\n\t * Returns the {@code Principal} (to be) associated to the authorized client.\n\t * @return the {@code Principal} (to be) associated to the authorized client\n\t */\n\tpublic Authentication getPrincipal() {\n\t\tAssert.notNull(this.principal, \"principal cannot be null\");\n\t\treturn this.principal;\n\t}\n\n\t/**\n\t * Returns the attributes associated to the context.\n\t * @return a {@code Map} of the attributes associated to the context\n\t */\n\tpublic Map<String, Object> getAttributes() {\n\t\tAssert.notNull(this.attributes, \"attributes cannot be null\");\n\t\treturn this.attributes;\n\t}\n\n\t/**\n\t * Returns the value of an attribute associated to the context or {@code null} if not\n\t * available.\n\t * @param name the name of the attribute\n\t * @param <T> the type of the attribute\n\t * @return the value of the attribute associated to the context\n\t */\n\t@Nullable\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T getAttribute(String name) {\n\t\treturn (T) this.getAttributes().get(name);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder} initialized with the {@link ClientRegistration}.\n\t * @param clientRegistration the {@link ClientRegistration client registration}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withClientRegistration(ClientRegistration clientRegistration) {\n\t\treturn new Builder(clientRegistration);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder} initialized with the {@link OAuth2AuthorizedClient}.\n\t * @param authorizedClient the {@link OAuth2AuthorizedClient authorized client}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withAuthorizedClient(OAuth2AuthorizedClient authorizedClient) {\n\t\treturn new Builder(authorizedClient);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AuthorizationContext}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate @Nullable ClientRegistration clientRegistration;\n\n\t\tprivate @Nullable OAuth2AuthorizedClient authorizedClient;\n\n\t\tprivate @Nullable Authentication principal;\n\n\t\tprivate @Nullable Map<String, Object> attributes;\n\n\t\tprivate Builder(ClientRegistration clientRegistration) {\n\t\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\t\tthis.clientRegistration = clientRegistration;\n\t\t}\n\n\t\tprivate Builder(OAuth2AuthorizedClient authorizedClient) {\n\t\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\t\tthis.authorizedClient = authorizedClient;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code Principal} (to be) associated to the authorized client.\n\t\t * @param principal the {@code Principal} (to be) associated to the authorized\n\t\t * client\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder principal(Authentication principal) {\n\t\t\tthis.principal = principal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides a {@link Consumer} access to the attributes associated to the context.\n\t\t * @param attributesConsumer a {@link Consumer} of the attributes associated to\n\t\t * the context\n\t\t * @return the {@link OAuth2AuthorizeRequest.Builder}\n\t\t */\n\t\tpublic Builder attributes(Consumer<Map<String, Object>> attributesConsumer) {\n\t\t\tif (this.attributes == null) {\n\t\t\t\tthis.attributes = new HashMap<>();\n\t\t\t}\n\t\t\tattributesConsumer.accept(this.attributes);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets an attribute associated to the context.\n\t\t * @param name the name of the attribute\n\t\t * @param value the value of the attribute\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder attribute(String name, Object value) {\n\t\t\tif (this.attributes == null) {\n\t\t\t\tthis.attributes = new HashMap<>();\n\t\t\t}\n\t\t\tthis.attributes.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AuthorizationContext}.\n\t\t * @return a {@link OAuth2AuthorizationContext}\n\t\t */\n\t\tpublic OAuth2AuthorizationContext build() {\n\t\t\tAssert.notNull(this.principal, \"principal cannot be null\");\n\t\t\tOAuth2AuthorizationContext context = new OAuth2AuthorizationContext();\n\t\t\tif (this.authorizedClient != null) {\n\t\t\t\tcontext.clientRegistration = this.authorizedClient.getClientRegistration();\n\t\t\t\tcontext.authorizedClient = this.authorizedClient;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcontext.clientRegistration = this.clientRegistration;\n\t\t\t}\n\t\t\tcontext.principal = this.principal;\n\t\t\tcontext.attributes = Collections.unmodifiableMap(CollectionUtils.isEmpty(this.attributes)\n\t\t\t\t\t? Collections.emptyMap() : new LinkedHashMap<>(this.attributes));\n\t\t\treturn context;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Map;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\n\n/**\n * Handles when an OAuth 2.0 Client fails to authorize (or re-authorize) via the\n * Authorization Server or Resource Server.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientManager\n */\n@FunctionalInterface\npublic interface OAuth2AuthorizationFailureHandler {\n\n\t/**\n\t * Called when an OAuth 2.0 Client fails to authorize (or re-authorize) via the\n\t * Authorization Server or Resource Server.\n\t * @param authorizationException the exception that contains details about what failed\n\t * @param principal the {@code Principal} associated with the attempted authorization\n\t * @param attributes an immutable {@code Map} of (optional) attributes present under\n\t * certain conditions. For example, this might contain a\n\t * {@code jakarta.servlet.http.HttpServletRequest} and\n\t * {@code jakarta.servlet.http.HttpServletResponse} if the authorization was performed\n\t * within the context of a {@code jakarta.servlet.ServletContext}.\n\t */\n\tvoid onAuthorizationFailure(OAuth2AuthorizationException authorizationException, Authentication principal,\n\t\t\tMap<String, Object> attributes);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Map;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Handles when an OAuth 2.0 Client has been successfully authorized (or re-authorized)\n * via the Authorization Server.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientManager\n */\n@FunctionalInterface\npublic interface OAuth2AuthorizationSuccessHandler {\n\n\t/**\n\t * Called when an OAuth 2.0 Client has been successfully authorized (or re-authorized)\n\t * via the Authorization Server.\n\t * @param authorizedClient the client that was successfully authorized (or\n\t * re-authorized)\n\t * @param principal the {@code Principal} associated with the authorized client\n\t * @param attributes an immutable {@code Map} of (optional) attributes present under\n\t * certain conditions. For example, this might contain a\n\t * {@code jakarta.servlet.http.HttpServletRequest} and\n\t * {@code jakarta.servlet.http.HttpServletResponse} if the authorization was performed\n\t * within the context of a {@code jakarta.servlet.ServletContext}.\n\t */\n\tvoid onAuthorizationSuccess(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tMap<String, Object> attributes);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizeRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Represents a request the {@link OAuth2AuthorizedClientManager} uses to\n * {@link OAuth2AuthorizedClientManager#authorize(OAuth2AuthorizeRequest) authorize} (or\n * re-authorize) the {@link ClientRegistration client} identified by the provided\n * {@link #getClientRegistrationId() clientRegistrationId}.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientManager\n */\npublic final class OAuth2AuthorizeRequest {\n\n\tprivate @Nullable String clientRegistrationId;\n\n\tprivate @Nullable OAuth2AuthorizedClient authorizedClient;\n\n\tprivate @Nullable Authentication principal;\n\n\tprivate @Nullable Map<String, Object> attributes;\n\n\tprivate OAuth2AuthorizeRequest() {\n\t}\n\n\t/**\n\t * Returns the identifier for the {@link ClientRegistration client registration}.\n\t * @return the identifier for the client registration\n\t */\n\tpublic String getClientRegistrationId() {\n\t\tAssert.notNull(this.clientRegistrationId, \"clientRegistrationId cannot be null\");\n\t\treturn this.clientRegistrationId;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizedClient authorized client} or {@code null} if it\n\t * was not provided.\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if it was not provided\n\t */\n\tpublic @Nullable OAuth2AuthorizedClient getAuthorizedClient() {\n\t\treturn this.authorizedClient;\n\t}\n\n\t/**\n\t * Returns the {@code Principal} (to be) associated to the authorized client.\n\t * @return the {@code Principal} (to be) associated to the authorized client\n\t */\n\tpublic Authentication getPrincipal() {\n\t\tAssert.notNull(this.principal, \"principal cannot be null\");\n\t\treturn this.principal;\n\t}\n\n\t/**\n\t * Returns the attributes associated to the request.\n\t * @return a {@code Map} of the attributes associated to the request\n\t */\n\tpublic Map<String, Object> getAttributes() {\n\t\tAssert.notNull(this.attributes, \"attributes cannot be null\");\n\t\treturn this.attributes;\n\t}\n\n\t/**\n\t * Returns the value of an attribute associated to the request or {@code null} if not\n\t * available.\n\t * @param name the name of the attribute\n\t * @param <T> the type of the attribute\n\t * @return the value of the attribute associated to the request\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> @Nullable T getAttribute(String name) {\n\t\treturn (T) this.getAttributes().get(name);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder} initialized with the identifier for the\n\t * {@link ClientRegistration client registration}.\n\t * @param clientRegistrationId the identifier for the {@link ClientRegistration client\n\t * registration}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withClientRegistrationId(String clientRegistrationId) {\n\t\treturn new Builder(clientRegistrationId);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder} initialized with the {@link OAuth2AuthorizedClient\n\t * authorized client}.\n\t * @param authorizedClient the {@link OAuth2AuthorizedClient authorized client}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withAuthorizedClient(OAuth2AuthorizedClient authorizedClient) {\n\t\treturn new Builder(authorizedClient);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AuthorizeRequest}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate @Nullable String clientRegistrationId;\n\n\t\tprivate @Nullable OAuth2AuthorizedClient authorizedClient;\n\n\t\tprivate @Nullable Authentication principal;\n\n\t\tprivate @Nullable Map<String, Object> attributes;\n\n\t\tprivate Builder(String clientRegistrationId) {\n\t\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\t\tthis.clientRegistrationId = clientRegistrationId;\n\t\t}\n\n\t\tprivate Builder(OAuth2AuthorizedClient authorizedClient) {\n\t\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\t\tthis.authorizedClient = authorizedClient;\n\t\t}\n\n\t\t/**\n\t\t * Sets the name of the {@code Principal} (to be) associated to the authorized\n\t\t * client.\n\t\t * @param principalName the name of the {@code Principal} (to be) associated to\n\t\t * the authorized client\n\t\t * @return the {@link Builder}\n\t\t * @since 5.3\n\t\t */\n\t\tpublic Builder principal(String principalName) {\n\t\t\treturn principal(createAuthentication(principalName));\n\t\t}\n\n\t\tprivate static Authentication createAuthentication(final String principalName) {\n\t\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\t\treturn new AbstractAuthenticationToken((Collection<? extends GrantedAuthority>) null) {\n\n\t\t\t\t@Override\n\t\t\t\tpublic Object getCredentials() {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Object getPrincipal() {\n\t\t\t\t\treturn principalName;\n\t\t\t\t}\n\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code Principal} (to be) associated to the authorized client.\n\t\t * @param principal the {@code Principal} (to be) associated to the authorized\n\t\t * client\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder principal(Authentication principal) {\n\t\t\tthis.principal = principal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides a {@link Consumer} access to the attributes associated to the request.\n\t\t * @param attributesConsumer a {@link Consumer} of the attributes associated to\n\t\t * the request\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder attributes(Consumer<Map<String, Object>> attributesConsumer) {\n\t\t\tif (this.attributes == null) {\n\t\t\t\tthis.attributes = new HashMap<>();\n\t\t\t}\n\t\t\tattributesConsumer.accept(this.attributes);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets an attribute associated to the request.\n\t\t * @param name the name of the attribute\n\t\t * @param value the value of the attribute\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder attribute(String name, Object value) {\n\t\t\tif (this.attributes == null) {\n\t\t\t\tthis.attributes = new HashMap<>();\n\t\t\t}\n\t\t\tthis.attributes.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AuthorizeRequest}.\n\t\t * @return a {@link OAuth2AuthorizeRequest}\n\t\t */\n\t\tpublic OAuth2AuthorizeRequest build() {\n\t\t\tAssert.notNull(this.principal, \"principal cannot be null\");\n\t\t\tOAuth2AuthorizeRequest authorizeRequest = new OAuth2AuthorizeRequest();\n\t\t\tif (this.authorizedClient != null) {\n\t\t\t\tauthorizeRequest.clientRegistrationId = this.authorizedClient.getClientRegistration()\n\t\t\t\t\t.getRegistrationId();\n\t\t\t\tauthorizeRequest.authorizedClient = this.authorizedClient;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tauthorizeRequest.clientRegistrationId = this.clientRegistrationId;\n\t\t\t}\n\t\t\tauthorizeRequest.principal = this.principal;\n\t\t\tauthorizeRequest.attributes = Collections.unmodifiableMap(CollectionUtils.isEmpty(this.attributes)\n\t\t\t\t\t? Collections.emptyMap() : new LinkedHashMap<>(this.attributes));\n\t\t\treturn authorizeRequest;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.util.Assert;\n\n/**\n * A representation of an OAuth 2.0 &quot;Authorized Client&quot;.\n * <p>\n * A client is considered &quot;authorized&quot; when the End-User (Resource Owner) has\n * granted authorization to the client to access its protected resources.\n * <p>\n * This class associates the {@link #getClientRegistration() Client} to the\n * {@link #getAccessToken() Access Token} granted/authorized by the\n * {@link #getPrincipalName() Resource Owner}.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see ClientRegistration\n * @see OAuth2AccessToken\n * @see OAuth2RefreshToken\n */\npublic class OAuth2AuthorizedClient implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final ClientRegistration clientRegistration;\n\n\tprivate final String principalName;\n\n\tprivate final OAuth2AccessToken accessToken;\n\n\tprivate final @Nullable OAuth2RefreshToken refreshToken;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizedClient} using the provided parameters.\n\t * @param clientRegistration the authorized client's registration\n\t * @param principalName the name of the End-User {@code Principal} (Resource Owner)\n\t * @param accessToken the access token credential granted\n\t */\n\tpublic OAuth2AuthorizedClient(ClientRegistration clientRegistration, String principalName,\n\t\t\tOAuth2AccessToken accessToken) {\n\t\tthis(clientRegistration, principalName, accessToken, null);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizedClient} using the provided parameters.\n\t * @param clientRegistration the authorized client's registration\n\t * @param principalName the name of the End-User {@code Principal} (Resource Owner)\n\t * @param accessToken the access token credential granted\n\t * @param refreshToken the refresh token credential granted\n\t */\n\tpublic OAuth2AuthorizedClient(ClientRegistration clientRegistration, String principalName,\n\t\t\tOAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tthis.clientRegistration = clientRegistration;\n\t\tthis.principalName = principalName;\n\t\tthis.accessToken = accessToken;\n\t\tthis.refreshToken = refreshToken;\n\t}\n\n\t/**\n\t * Returns the authorized client's {@link ClientRegistration registration}.\n\t * @return the {@link ClientRegistration}\n\t */\n\tpublic ClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n\t/**\n\t * Returns the End-User's {@code Principal} name.\n\t * @return the End-User's {@code Principal} name\n\t */\n\tpublic String getPrincipalName() {\n\t\treturn this.principalName;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessToken access token} credential granted.\n\t * @return the {@link OAuth2AccessToken}\n\t */\n\tpublic OAuth2AccessToken getAccessToken() {\n\t\treturn this.accessToken;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2RefreshToken refresh token} credential granted.\n\t * @return the {@link OAuth2RefreshToken}\n\t * @since 5.1\n\t */\n\tpublic @Nullable OAuth2RefreshToken getRefreshToken() {\n\t\treturn this.refreshToken;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientId.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.util.Assert;\n\n/**\n * The identifier for {@link OAuth2AuthorizedClient}.\n *\n * @author Vedran Pavic\n * @since 5.2\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientService\n */\npublic final class OAuth2AuthorizedClientId implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String clientRegistrationId;\n\n\tprivate final String principalName;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizedClientId} using the provided parameters.\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principalName the name of the End-User {@code Principal} (Resource Owner)\n\t */\n\tpublic OAuth2AuthorizedClientId(String clientRegistrationId, String principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tthis.clientRegistrationId = clientRegistrationId;\n\t\tthis.principalName = principalName;\n\t}\n\n\t/**\n\t * Returns the identifier for the {@link ClientRegistration client registration}.\n\t * @return the identifier for the client registration\n\t * @since 6.3\n\t */\n\tpublic String getClientRegistrationId() {\n\t\treturn this.clientRegistrationId;\n\t}\n\n\t/**\n\t * Returns the name of the End-User {@code Principal} (Resource Owner).\n\t * @return the name of the End-User\n\t * @since 6.3\n\t */\n\tpublic String getPrincipalName() {\n\t\treturn this.principalName;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2AuthorizedClientId that = (OAuth2AuthorizedClientId) obj;\n\t\treturn Objects.equals(this.clientRegistrationId, that.clientRegistrationId)\n\t\t\t\t&& Objects.equals(this.principalName, that.principalName);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.clientRegistrationId, this.principalName);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\n\n/**\n * Implementations of this interface are responsible for the overall management of\n * {@link OAuth2AuthorizedClient Authorized Client(s)}.\n *\n * <p>\n * The primary responsibilities include:\n * <ol>\n * <li>Authorizing (or re-authorizing) an OAuth 2.0 Client by leveraging an\n * {@link OAuth2AuthorizedClientProvider}(s).</li>\n * <li>Delegating the persistence of an {@link OAuth2AuthorizedClient}, typically using an\n * {@link OAuth2AuthorizedClientService} OR {@link OAuth2AuthorizedClientRepository}.</li>\n * </ol>\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientProvider\n * @see OAuth2AuthorizedClientService\n * @see OAuth2AuthorizedClientRepository\n */\n@FunctionalInterface\npublic interface OAuth2AuthorizedClientManager {\n\n\t/**\n\t * Attempt to authorize or re-authorize (if required) the {@link ClientRegistration\n\t * client} identified by the provided\n\t * {@link OAuth2AuthorizeRequest#getClientRegistrationId() clientRegistrationId}.\n\t * Implementations must return {@code null} if authorization is not supported for the\n\t * specified client, e.g. the associated {@link OAuth2AuthorizedClientProvider}(s)\n\t * does not support the {@link ClientRegistration#getAuthorizationGrantType()\n\t * authorization grant} type configured for the client.\n\t *\n\t * <p>\n\t * In the case of re-authorization, implementations must return the provided\n\t * {@link OAuth2AuthorizeRequest#getAuthorizedClient() authorized client} if\n\t * re-authorization is not supported for the client OR is not required, e.g. a\n\t * {@link OAuth2AuthorizedClient#getRefreshToken() refresh token} is not available OR\n\t * the {@link OAuth2AuthorizedClient#getAccessToken() access token} is not expired.\n\t * @param authorizeRequest the authorize request\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if authorization is not\n\t * supported for the specified client\n\t */\n\t@Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\n\n/**\n * A strategy for authorizing (or re-authorizing) an OAuth 2.0 Client. Implementations\n * will typically implement a specific {@link AuthorizationGrantType authorization grant}\n * type.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizationContext\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.3\">Section\n * 1.3 Authorization Grant</a>\n */\n@FunctionalInterface\npublic interface OAuth2AuthorizedClientProvider {\n\n\t/**\n\t * Attempt to authorize (or re-authorize) the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * context. Implementations must return {@code null} if authorization is not supported\n\t * for the specified client, e.g. the provider doesn't support the\n\t * {@link ClientRegistration#getAuthorizationGrantType() authorization grant} type\n\t * configured for the client.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if authorization is not\n\t * supported for the specified client\n\t */\n\t@Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProviderBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.util.Assert;\n\n/**\n * A builder that builds a {@link DelegatingOAuth2AuthorizedClientProvider} composed of\n * one or more {@link OAuth2AuthorizedClientProvider}(s) that implement specific\n * authorization grants. The supported authorization grants are\n * {@link #authorizationCode() authorization_code}, {@link #refreshToken() refresh_token}\n * and {@link #clientCredentials() client_credentials}. In addition to the standard\n * authorization grants, an implementation of an extension grant may be supplied via\n * {@link #provider(OAuth2AuthorizedClientProvider)}.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientProvider\n * @see AuthorizationCodeOAuth2AuthorizedClientProvider\n * @see RefreshTokenOAuth2AuthorizedClientProvider\n * @see ClientCredentialsOAuth2AuthorizedClientProvider\n * @see DelegatingOAuth2AuthorizedClientProvider\n */\npublic final class OAuth2AuthorizedClientProviderBuilder {\n\n\tprivate final Map<Class<?>, Builder> builders = new LinkedHashMap<>();\n\n\tprivate OAuth2AuthorizedClientProviderBuilder() {\n\t}\n\n\t/**\n\t * Returns a new {@link OAuth2AuthorizedClientProviderBuilder} for configuring the\n\t * supported authorization grant(s).\n\t * @return the {@link OAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic static OAuth2AuthorizedClientProviderBuilder builder() {\n\t\treturn new OAuth2AuthorizedClientProviderBuilder();\n\t}\n\n\t/**\n\t * Configures an {@link OAuth2AuthorizedClientProvider} to be composed with the\n\t * {@link DelegatingOAuth2AuthorizedClientProvider}. This may be used for\n\t * implementations of extension authorization grants.\n\t * @return the {@link OAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic OAuth2AuthorizedClientProviderBuilder provider(OAuth2AuthorizedClientProvider provider) {\n\t\tAssert.notNull(provider, \"provider cannot be null\");\n\t\tthis.builders.computeIfAbsent(provider.getClass(), (k) -> () -> provider);\n\t\treturn OAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code authorization_code} grant.\n\t * @return the {@link OAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic OAuth2AuthorizedClientProviderBuilder authorizationCode() {\n\t\tthis.builders.computeIfAbsent(AuthorizationCodeOAuth2AuthorizedClientProvider.class,\n\t\t\t\t(k) -> new AuthorizationCodeGrantBuilder());\n\t\treturn OAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code refresh_token} grant.\n\t * @return the {@link OAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic OAuth2AuthorizedClientProviderBuilder refreshToken() {\n\t\tthis.builders.computeIfAbsent(RefreshTokenOAuth2AuthorizedClientProvider.class,\n\t\t\t\t(k) -> new RefreshTokenGrantBuilder());\n\t\treturn OAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code refresh_token} grant.\n\t * @param builderConsumer a {@code Consumer} of {@link RefreshTokenGrantBuilder} used\n\t * for further configuration\n\t * @return the {@link OAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic OAuth2AuthorizedClientProviderBuilder refreshToken(Consumer<RefreshTokenGrantBuilder> builderConsumer) {\n\t\tRefreshTokenGrantBuilder builder = (RefreshTokenGrantBuilder) this.builders\n\t\t\t.computeIfAbsent(RefreshTokenOAuth2AuthorizedClientProvider.class, (k) -> new RefreshTokenGrantBuilder());\n\t\tbuilderConsumer.accept(builder);\n\t\treturn OAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code client_credentials} grant.\n\t * @return the {@link OAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic OAuth2AuthorizedClientProviderBuilder clientCredentials() {\n\t\tthis.builders.computeIfAbsent(ClientCredentialsOAuth2AuthorizedClientProvider.class,\n\t\t\t\t(k) -> new ClientCredentialsGrantBuilder());\n\t\treturn OAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code client_credentials} grant.\n\t * @param builderConsumer a {@code Consumer} of {@link ClientCredentialsGrantBuilder}\n\t * used for further configuration\n\t * @return the {@link OAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic OAuth2AuthorizedClientProviderBuilder clientCredentials(\n\t\t\tConsumer<ClientCredentialsGrantBuilder> builderConsumer) {\n\t\tClientCredentialsGrantBuilder builder = (ClientCredentialsGrantBuilder) this.builders.computeIfAbsent(\n\t\t\t\tClientCredentialsOAuth2AuthorizedClientProvider.class, (k) -> new ClientCredentialsGrantBuilder());\n\t\tbuilderConsumer.accept(builder);\n\t\treturn OAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Builds an instance of {@link DelegatingOAuth2AuthorizedClientProvider} composed of\n\t * one or more {@link OAuth2AuthorizedClientProvider}(s).\n\t * @return the {@link DelegatingOAuth2AuthorizedClientProvider}\n\t */\n\tpublic OAuth2AuthorizedClientProvider build() {\n\t\tList<OAuth2AuthorizedClientProvider> authorizedClientProviders = new ArrayList<>();\n\t\tfor (Builder builder : this.builders.values()) {\n\t\t\tauthorizedClientProviders.add(builder.build());\n\t\t}\n\t\treturn new DelegatingOAuth2AuthorizedClientProvider(authorizedClientProviders);\n\t}\n\n\tinterface Builder {\n\n\t\tOAuth2AuthorizedClientProvider build();\n\n\t}\n\n\t/**\n\t * A builder for the {@code client_credentials} grant.\n\t */\n\tpublic final class ClientCredentialsGrantBuilder implements Builder {\n\n\t\tprivate @Nullable OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;\n\n\t\tprivate @Nullable Duration clockSkew;\n\n\t\tprivate @Nullable Clock clock;\n\n\t\tprivate ClientCredentialsGrantBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the client used when requesting an access token credential at the Token\n\t\t * Endpoint.\n\t\t * @param accessTokenResponseClient the client used when requesting an access\n\t\t * token credential at the Token Endpoint\n\t\t * @return the {@link ClientCredentialsGrantBuilder}\n\t\t */\n\t\tpublic ClientCredentialsGrantBuilder accessTokenResponseClient(\n\t\t\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient) {\n\t\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the maximum acceptable clock skew, which is used when checking the access\n\t\t * token expiry. An access token is considered expired if\n\t\t * {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time\n\t\t * {@code clock#instant()}.\n\t\t * @param clockSkew the maximum acceptable clock skew\n\t\t * @return the {@link ClientCredentialsGrantBuilder}\n\t\t * @see ClientCredentialsOAuth2AuthorizedClientProvider#setClockSkew(Duration)\n\t\t */\n\t\tpublic ClientCredentialsGrantBuilder clockSkew(Duration clockSkew) {\n\t\t\tthis.clockSkew = clockSkew;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the\n\t\t * access token expiry.\n\t\t * @param clock the clock\n\t\t * @return the {@link ClientCredentialsGrantBuilder}\n\t\t */\n\t\tpublic ClientCredentialsGrantBuilder clock(Clock clock) {\n\t\t\tthis.clock = clock;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds an instance of {@link ClientCredentialsOAuth2AuthorizedClientProvider}.\n\t\t * @return the {@link ClientCredentialsOAuth2AuthorizedClientProvider}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2AuthorizedClientProvider build() {\n\t\t\tClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();\n\t\t\tif (this.accessTokenResponseClient != null) {\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\t\t}\n\t\t\tif (this.clockSkew != null) {\n\t\t\t\tauthorizedClientProvider.setClockSkew(this.clockSkew);\n\t\t\t}\n\t\t\tif (this.clock != null) {\n\t\t\t\tauthorizedClientProvider.setClock(this.clock);\n\t\t\t}\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for the {@code authorization_code} grant.\n\t */\n\tpublic final class AuthorizationCodeGrantBuilder implements Builder {\n\n\t\tprivate AuthorizationCodeGrantBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Builds an instance of {@link AuthorizationCodeOAuth2AuthorizedClientProvider}.\n\t\t * @return the {@link AuthorizationCodeOAuth2AuthorizedClientProvider}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2AuthorizedClientProvider build() {\n\t\t\treturn new AuthorizationCodeOAuth2AuthorizedClientProvider();\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for the {@code refresh_token} grant.\n\t */\n\tpublic final class RefreshTokenGrantBuilder implements Builder {\n\n\t\tprivate @Nullable OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient;\n\n\t\tprivate @Nullable ApplicationEventPublisher eventPublisher;\n\n\t\tprivate @Nullable Duration clockSkew;\n\n\t\tprivate @Nullable Clock clock;\n\n\t\tprivate RefreshTokenGrantBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the client used when requesting an access token credential at the Token\n\t\t * Endpoint.\n\t\t * @param accessTokenResponseClient the client used when requesting an access\n\t\t * token credential at the Token Endpoint\n\t\t * @return the {@link RefreshTokenGrantBuilder}\n\t\t */\n\t\tpublic RefreshTokenGrantBuilder accessTokenResponseClient(\n\t\t\t\tOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient) {\n\t\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link ApplicationEventPublisher} used when an access token is\n\t\t * refreshed.\n\t\t * @param eventPublisher the {@link ApplicationEventPublisher}\n\t\t * @return the {@link RefreshTokenGrantBuilder}\n\t\t * @since 6.5\n\t\t */\n\t\tpublic RefreshTokenGrantBuilder eventPublisher(ApplicationEventPublisher eventPublisher) {\n\t\t\tthis.eventPublisher = eventPublisher;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the maximum acceptable clock skew, which is used when checking the access\n\t\t * token expiry. An access token is considered expired if\n\t\t * {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time\n\t\t * {@code clock#instant()}.\n\t\t * @param clockSkew the maximum acceptable clock skew\n\t\t * @return the {@link RefreshTokenGrantBuilder}\n\t\t * @see RefreshTokenOAuth2AuthorizedClientProvider#setClockSkew(Duration)\n\t\t */\n\t\tpublic RefreshTokenGrantBuilder clockSkew(Duration clockSkew) {\n\t\t\tthis.clockSkew = clockSkew;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the\n\t\t * access token expiry.\n\t\t * @param clock the clock\n\t\t * @return the {@link RefreshTokenGrantBuilder}\n\t\t */\n\t\tpublic RefreshTokenGrantBuilder clock(Clock clock) {\n\t\t\tthis.clock = clock;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds an instance of {@link RefreshTokenOAuth2AuthorizedClientProvider}.\n\t\t * @return the {@link RefreshTokenOAuth2AuthorizedClientProvider}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2AuthorizedClientProvider build() {\n\t\t\tRefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider();\n\t\t\tif (this.accessTokenResponseClient != null) {\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\t\t}\n\t\t\tif (this.eventPublisher != null) {\n\t\t\t\tauthorizedClientProvider.setApplicationEventPublisher(this.eventPublisher);\n\t\t\t}\n\t\t\tif (this.clockSkew != null) {\n\t\t\t\tauthorizedClientProvider.setClockSkew(this.clockSkew);\n\t\t\t}\n\t\t\tif (this.clock != null) {\n\t\t\t\tauthorizedClientProvider.setClock(this.clock);\n\t\t\t}\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\n/**\n * Implementations of this interface are responsible for the management of\n * {@link OAuth2AuthorizedClient Authorized Client(s)}, which provide the purpose of\n * associating an {@link OAuth2AuthorizedClient#getAccessToken() Access Token} credential\n * to a {@link OAuth2AuthorizedClient#getClientRegistration() Client} and Resource Owner,\n * who is the {@link OAuth2AuthorizedClient#getPrincipalName() Principal} that originally\n * granted the authorization.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2AuthorizedClient\n * @see ClientRegistration\n * @see Authentication\n * @see OAuth2AccessToken\n */\npublic interface OAuth2AuthorizedClientService {\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizedClient} associated to the provided client\n\t * registration identifier and End-User's {@code Principal} name or {@code null} if\n\t * not available.\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principalName the name of the End-User {@code Principal} (Resource Owner)\n\t * @param <T> a type of OAuth2AuthorizedClient\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if not available\n\t */\n\t<T extends OAuth2AuthorizedClient> @Nullable T loadAuthorizedClient(String clientRegistrationId,\n\t\t\tString principalName);\n\n\t/**\n\t * Saves the {@link OAuth2AuthorizedClient} associating it to the provided End-User\n\t * {@link Authentication} (Resource Owner).\n\t * @param authorizedClient the authorized client\n\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t */\n\tvoid saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal);\n\n\t/**\n\t * Removes the {@link OAuth2AuthorizedClient} associated to the provided client\n\t * registration identifier and End-User's {@code Principal} name.\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principalName the name of the End-User {@code Principal} (Resource Owner)\n\t */\n\tvoid removeAuthorizedClient(String clientRegistrationId, String principalName);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/R2dbcReactiveOAuth2AuthorizedClientService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneOffset;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport io.r2dbc.spi.Row;\nimport io.r2dbc.spi.RowMetadata;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.r2dbc.core.DatabaseClient;\nimport org.springframework.r2dbc.core.DatabaseClient.GenericExecuteSpec;\nimport org.springframework.r2dbc.core.Parameter;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A R2DBC implementation of {@link ReactiveOAuth2AuthorizedClientService} that uses a\n * {@link DatabaseClient} for {@link OAuth2AuthorizedClient} persistence.\n *\n * <p>\n * <b>NOTE:</b> This {@code ReactiveOAuth2AuthorizedClientService} depends on the table\n * definition described in\n * \"classpath:org/springframework/security/oauth2/client/oauth2-client-schema.sql\" and\n * therefore MUST be defined in the database schema.\n *\n * @author Ovidiu Popa\n * @since 5.5\n * @see ReactiveOAuth2AuthorizedClientService\n * @see OAuth2AuthorizedClient\n * @see DatabaseClient\n *\n */\npublic class R2dbcReactiveOAuth2AuthorizedClientService implements ReactiveOAuth2AuthorizedClientService {\n\n\t// @formatter:off\n\tprivate static final String COLUMN_NAMES =\n\t\t\t\"client_registration_id, \" +\n\t\t\t\"principal_name, \" +\n\t\t\t\"access_token_type, \" +\n\t\t\t\"access_token_value, \" +\n\t\t\t\"access_token_issued_at, \" +\n\t\t\t\"access_token_expires_at, \" +\n\t\t\t\"access_token_scopes, \" +\n\t\t\t\"refresh_token_value, \" +\n\t\t\t\"refresh_token_issued_at\";\n\t// @formatter:on\n\n\tprivate static final String TABLE_NAME = \"oauth2_authorized_client\";\n\n\tprivate static final String PK_FILTER = \"client_registration_id = :clientRegistrationId AND principal_name = :principalName\";\n\n\t// @formatter:off\n\tprivate static final String LOAD_AUTHORIZED_CLIENT_SQL = \"SELECT \" + COLUMN_NAMES + \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + PK_FILTER;\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String SAVE_AUTHORIZED_CLIENT_SQL = \"INSERT INTO \" + TABLE_NAME + \" (\" + COLUMN_NAMES + \")\" +\n\t\t\t\"VALUES (:clientRegistrationId, :principalName, :accessTokenType, :accessTokenValue,\" +\n\t\t\t\t\" :accessTokenIssuedAt, :accessTokenExpiresAt, :accessTokenScopes, :refreshTokenValue,\" +\n\t\t\t\t\" :refreshTokenIssuedAt)\";\n\t// @formatter:on\n\n\tprivate static final String REMOVE_AUTHORIZED_CLIENT_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + PK_FILTER;\n\n\t// @formatter:off\n\tprivate static final String UPDATE_AUTHORIZED_CLIENT_SQL = \"UPDATE \" + TABLE_NAME +\n\t\t\t\" SET access_token_type = :accessTokenType, \" +\n\t\t\t\" access_token_value = :accessTokenValue, \" +\n\t\t\t\" access_token_issued_at = :accessTokenIssuedAt,\" +\n\t\t\t\" access_token_expires_at = :accessTokenExpiresAt, \" +\n\t\t\t\" access_token_scopes = :accessTokenScopes,\" +\n\t\t\t\" refresh_token_value = :refreshTokenValue, \" +\n\t\t\t\" refresh_token_issued_at = :refreshTokenIssuedAt\" +\n\t\t\t\" WHERE \" +\n\t\t\tPK_FILTER;\n\t// @formatter:on\n\n\tprotected final DatabaseClient databaseClient;\n\n\tprotected final ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprotected Function<OAuth2AuthorizedClientHolder, Map<String, Parameter>> authorizedClientParametersMapper;\n\n\tprotected BiFunction<Row, RowMetadata, OAuth2AuthorizedClientHolder> authorizedClientRowMapper;\n\n\t/**\n\t * Constructs a {@code R2dbcReactiveOAuth2AuthorizedClientService} using the provided\n\t * parameters.\n\t * @param databaseClient the database client\n\t * @param clientRegistrationRepository the repository of client registrations\n\t */\n\tpublic R2dbcReactiveOAuth2AuthorizedClientService(DatabaseClient databaseClient,\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(databaseClient, \"databaseClient cannot be null\");\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.databaseClient = databaseClient;\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClientParametersMapper = new OAuth2AuthorizedClientParametersMapper();\n\t\tthis.authorizedClientRowMapper = new OAuth2AuthorizedClientRowMapper();\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId,\n\t\t\tString principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\n\t\treturn (Mono<T>) this.databaseClient.sql(LOAD_AUTHORIZED_CLIENT_SQL)\n\t\t\t.bind(\"clientRegistrationId\", clientRegistrationId)\n\t\t\t.bind(\"principalName\", principalName)\n\t\t\t.map(this.authorizedClientRowMapper)\n\t\t\t.first()\n\t\t\t.flatMap(this::getAuthorizedClient);\n\t}\n\n\tprivate Mono<OAuth2AuthorizedClient> getAuthorizedClient(OAuth2AuthorizedClientHolder authorizedClientHolder) {\n\t\treturn this.clientRegistrationRepository.findByRegistrationId(authorizedClientHolder.getClientRegistrationId())\n\t\t\t.switchIfEmpty(Mono.error(dataRetrievalFailureException(authorizedClientHolder.getClientRegistrationId())))\n\t\t\t.map((clientRegistration) -> new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\t\tauthorizedClientHolder.getPrincipalName(), authorizedClientHolder.getAccessToken(),\n\t\t\t\t\tauthorizedClientHolder.getRefreshToken()));\n\t}\n\n\tprivate static Throwable dataRetrievalFailureException(String clientRegistrationId) {\n\t\treturn new DataRetrievalFailureException(\"The ClientRegistration with id '\" + clientRegistrationId\n\t\t\t\t+ \"' exists in the data source, however, it was not found in the ReactiveClientRegistrationRepository.\");\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\treturn this\n\t\t\t.loadAuthorizedClient(authorizedClient.getClientRegistration().getRegistrationId(), principal.getName())\n\t\t\t.flatMap((dbAuthorizedClient) -> updateAuthorizedClient(authorizedClient, principal))\n\t\t\t.switchIfEmpty(Mono.defer(() -> insertAuthorizedClient(authorizedClient, principal)))\n\t\t\t.then();\n\t}\n\n\tprivate Mono<Long> updateAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\tGenericExecuteSpec executeSpec = this.databaseClient.sql(UPDATE_AUTHORIZED_CLIENT_SQL);\n\t\tfor (Entry<String, Parameter> entry : this.authorizedClientParametersMapper\n\t\t\t.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal))\n\t\t\t.entrySet()) {\n\t\t\texecuteSpec = executeSpec.bind(entry.getKey(), entry.getValue());\n\t\t}\n\t\treturn executeSpec.fetch().rowsUpdated();\n\t}\n\n\tprivate Mono<Long> insertAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\tGenericExecuteSpec executeSpec = this.databaseClient.sql(SAVE_AUTHORIZED_CLIENT_SQL);\n\t\tfor (Entry<String, Parameter> entry : this.authorizedClientParametersMapper\n\t\t\t.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal))\n\t\t\t.entrySet()) {\n\t\t\texecuteSpec = executeSpec.bind(entry.getKey(), entry.getValue());\n\t\t}\n\t\treturn executeSpec.fetch().rowsUpdated();\n\t}\n\n\t@Override\n\tpublic Mono<Void> removeAuthorizedClient(String clientRegistrationId, String principalName) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\treturn this.databaseClient.sql(REMOVE_AUTHORIZED_CLIENT_SQL)\n\t\t\t.bind(\"clientRegistrationId\", clientRegistrationId)\n\t\t\t.bind(\"principalName\", principalName)\n\t\t\t.then();\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping {@link OAuth2AuthorizedClientHolder} to\n\t * a {@code Map} of {@link String} and {@link Parameter}. The default is\n\t * {@link OAuth2AuthorizedClientParametersMapper}.\n\t * @param authorizedClientParametersMapper the {@code Function} used for mapping\n\t * {@link OAuth2AuthorizedClientHolder} to a {@code Map} of {@link String} and\n\t * {@link Parameter}\n\t */\n\tpublic final void setAuthorizedClientParametersMapper(\n\t\t\tFunction<OAuth2AuthorizedClientHolder, Map<String, Parameter>> authorizedClientParametersMapper) {\n\t\tAssert.notNull(authorizedClientParametersMapper, \"authorizedClientParametersMapper cannot be null\");\n\t\tthis.authorizedClientParametersMapper = authorizedClientParametersMapper;\n\t}\n\n\t/**\n\t * Sets the {@link BiFunction} used for mapping the current {@code io.r2dbc.spi.Row}\n\t * to {@link OAuth2AuthorizedClientHolder}. The default is\n\t * {@link OAuth2AuthorizedClientRowMapper}.\n\t * @param authorizedClientRowMapper the {@link BiFunction} used for mapping the\n\t * current {@code io.r2dbc.spi.Row} to {@link OAuth2AuthorizedClientHolder}\n\t */\n\tpublic final void setAuthorizedClientRowMapper(\n\t\t\tBiFunction<Row, RowMetadata, OAuth2AuthorizedClientHolder> authorizedClientRowMapper) {\n\t\tAssert.notNull(authorizedClientRowMapper, \"authorizedClientRowMapper cannot be null\");\n\t\tthis.authorizedClientRowMapper = authorizedClientRowMapper;\n\t}\n\n\t/**\n\t * A holder for {@link OAuth2AuthorizedClient} data and End-User\n\t * {@link Authentication} (Resource Owner).\n\t */\n\tpublic static final class OAuth2AuthorizedClientHolder {\n\n\t\tprivate final String clientRegistrationId;\n\n\t\tprivate final String principalName;\n\n\t\tprivate final OAuth2AccessToken accessToken;\n\n\t\tprivate final @Nullable OAuth2RefreshToken refreshToken;\n\n\t\t/**\n\t\t * Constructs an {@code OAuth2AuthorizedClientHolder} using the provided\n\t\t * parameters.\n\t\t * @param authorizedClient the authorized client\n\t\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t\t */\n\t\tpublic OAuth2AuthorizedClientHolder(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.clientRegistrationId = authorizedClient.getClientRegistration().getRegistrationId();\n\t\t\tthis.principalName = principal.getName();\n\t\t\tthis.accessToken = authorizedClient.getAccessToken();\n\t\t\tthis.refreshToken = authorizedClient.getRefreshToken();\n\t\t}\n\n\t\t/**\n\t\t * Constructs an {@code OAuth2AuthorizedClientHolder} using the provided\n\t\t * parameters.\n\t\t * @param clientRegistrationId the client registration id\n\t\t * @param principalName the principal name of the End-User (Resource Owner)\n\t\t * @param accessToken the access token\n\t\t * @param refreshToken the refresh token\n\t\t */\n\t\tpublic OAuth2AuthorizedClientHolder(String clientRegistrationId, String principalName,\n\t\t\t\tOAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {\n\t\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\t\tthis.clientRegistrationId = clientRegistrationId;\n\t\t\tthis.principalName = principalName;\n\t\t\tthis.accessToken = accessToken;\n\t\t\tthis.refreshToken = refreshToken;\n\t\t}\n\n\t\tpublic String getClientRegistrationId() {\n\t\t\treturn this.clientRegistrationId;\n\t\t}\n\n\t\tpublic String getPrincipalName() {\n\t\t\treturn this.principalName;\n\t\t}\n\n\t\tpublic OAuth2AccessToken getAccessToken() {\n\t\t\treturn this.accessToken;\n\t\t}\n\n\t\tpublic @Nullable OAuth2RefreshToken getRefreshToken() {\n\t\t\treturn this.refreshToken;\n\t\t}\n\n\t}\n\n\t/**\n\t * The default {@code Function} that maps {@link OAuth2AuthorizedClientHolder} to a\n\t * {@code Map} of {@link String} and {@link Parameter}.\n\t */\n\tpublic static class OAuth2AuthorizedClientParametersMapper\n\t\t\timplements Function<OAuth2AuthorizedClientHolder, Map<String, Parameter>> {\n\n\t\t@Override\n\t\tpublic Map<String, Parameter> apply(OAuth2AuthorizedClientHolder authorizedClientHolder) {\n\n\t\t\tfinal Map<String, Parameter> parameters = new HashMap<>();\n\n\t\t\tfinal OAuth2AccessToken accessToken = authorizedClientHolder.getAccessToken();\n\t\t\tfinal OAuth2RefreshToken refreshToken = authorizedClientHolder.getRefreshToken();\n\n\t\t\tparameters.put(\"clientRegistrationId\",\n\t\t\t\t\tParameter.fromOrEmpty(authorizedClientHolder.getClientRegistrationId(), String.class));\n\t\t\tparameters.put(\"principalName\",\n\t\t\t\t\tParameter.fromOrEmpty(authorizedClientHolder.getPrincipalName(), String.class));\n\t\t\tparameters.put(\"accessTokenType\",\n\t\t\t\t\tParameter.fromOrEmpty(accessToken.getTokenType().getValue(), String.class));\n\t\t\tparameters.put(\"accessTokenValue\", Parameter.fromOrEmpty(\n\t\t\t\t\tByteBuffer.wrap(accessToken.getTokenValue().getBytes(StandardCharsets.UTF_8)), ByteBuffer.class));\n\t\t\tInstant accessTokenIssuedAt = accessToken.getIssuedAt();\n\t\t\tInstant accessTokenExpiresAt = accessToken.getExpiresAt();\n\t\t\tparameters.put(\"accessTokenIssuedAt\", Parameter.fromOrEmpty(\n\t\t\t\t\t(accessTokenIssuedAt != null) ? LocalDateTime.ofInstant(accessTokenIssuedAt, ZoneOffset.UTC) : null,\n\t\t\t\t\tLocalDateTime.class));\n\t\t\tparameters.put(\"accessTokenExpiresAt\",\n\t\t\t\t\tParameter.fromOrEmpty(\n\t\t\t\t\t\t\t(accessTokenExpiresAt != null)\n\t\t\t\t\t\t\t\t\t? LocalDateTime.ofInstant(accessTokenExpiresAt, ZoneOffset.UTC) : null,\n\t\t\t\t\t\t\tLocalDateTime.class));\n\t\t\tString accessTokenScopes = null;\n\t\t\tif (!CollectionUtils.isEmpty(accessToken.getScopes())) {\n\t\t\t\taccessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getScopes(), \",\");\n\n\t\t\t}\n\t\t\tparameters.put(\"accessTokenScopes\", Parameter.fromOrEmpty(accessTokenScopes, String.class));\n\t\t\tByteBuffer refreshTokenValue = null;\n\t\t\tLocalDateTime refreshTokenIssuedAt = null;\n\t\t\tif (refreshToken != null) {\n\t\t\t\trefreshTokenValue = ByteBuffer.wrap(refreshToken.getTokenValue().getBytes(StandardCharsets.UTF_8));\n\t\t\t\tif (refreshToken.getIssuedAt() != null) {\n\t\t\t\t\trefreshTokenIssuedAt = LocalDateTime.ofInstant(refreshToken.getIssuedAt(), ZoneOffset.UTC);\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tparameters.put(\"refreshTokenValue\", Parameter.fromOrEmpty(refreshTokenValue, ByteBuffer.class));\n\t\t\tparameters.put(\"refreshTokenIssuedAt\", Parameter.fromOrEmpty(refreshTokenIssuedAt, LocalDateTime.class));\n\t\t\treturn parameters;\n\t\t}\n\n\t}\n\n\t/**\n\t * The default {@link BiFunction} that maps the current {@code io.r2dbc.spi.Row} to a\n\t * {@link OAuth2AuthorizedClientHolder}.\n\t */\n\tpublic static class OAuth2AuthorizedClientRowMapper\n\t\t\timplements BiFunction<Row, RowMetadata, OAuth2AuthorizedClientHolder> {\n\n\t\t@Override\n\t\tpublic OAuth2AuthorizedClientHolder apply(Row row, RowMetadata rowMetadata) {\n\t\t\tString clientRegistrationId = row.get(\"client_registration_id\", String.class);\n\t\t\tAssert.hasText(clientRegistrationId, \"client_registration_id cannot be empty\");\n\t\t\tString principalName = row.get(\"principal_name\", String.class);\n\t\t\tAssert.hasText(principalName, \"principal_name cannot be empty\");\n\t\t\tOAuth2AccessToken.TokenType tokenType = OAuth2AccessToken.TokenType.BEARER;\n\t\t\tString accessTokenType = row.get(\"access_token_type\", String.class);\n\t\t\tif (StringUtils.hasText(accessTokenType)\n\t\t\t\t\t&& !OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(accessTokenType)) {\n\t\t\t\ttokenType = new OAuth2AccessToken.TokenType(accessTokenType);\n\t\t\t}\n\t\t\tByteBuffer accessTokenValueBuffer = row.get(\"access_token_value\", ByteBuffer.class);\n\t\t\tAssert.notNull(accessTokenValueBuffer, \"access_token_value cannot be null\");\n\t\t\tString tokenValue = new String(accessTokenValueBuffer.array(), StandardCharsets.UTF_8);\n\t\t\tInstant issuedAt = null;\n\t\t\tLocalDateTime issuedAtLdt = row.get(\"access_token_issued_at\", LocalDateTime.class);\n\t\t\tif (issuedAtLdt != null) {\n\t\t\t\tissuedAt = issuedAtLdt.toInstant(ZoneOffset.UTC);\n\t\t\t}\n\t\t\tInstant expiresAt = null;\n\t\t\tLocalDateTime expiresAtLdt = row.get(\"access_token_expires_at\", LocalDateTime.class);\n\t\t\tif (expiresAtLdt != null) {\n\t\t\t\texpiresAt = expiresAtLdt.toInstant(ZoneOffset.UTC);\n\t\t\t}\n\n\t\t\tSet<String> scopes = Collections.emptySet();\n\t\t\tString accessTokenScopes = row.get(\"access_token_scopes\", String.class);\n\t\t\tif (accessTokenScopes != null) {\n\t\t\t\tscopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);\n\t\t\t}\n\t\t\tfinal OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, issuedAt, expiresAt,\n\t\t\t\t\tscopes);\n\n\t\t\tOAuth2RefreshToken refreshToken = null;\n\t\t\tByteBuffer refreshTokenValueBuffer = row.get(\"refresh_token_value\", ByteBuffer.class);\n\t\t\tif (refreshTokenValueBuffer != null) {\n\t\t\t\ttokenValue = new String(refreshTokenValueBuffer.array(), StandardCharsets.UTF_8);\n\t\t\t\tissuedAt = null;\n\t\t\t\tissuedAtLdt = row.get(\"refresh_token_issued_at\", LocalDateTime.class);\n\t\t\t\tif (issuedAtLdt != null) {\n\t\t\t\t\tissuedAt = issuedAtLdt.toInstant(ZoneOffset.UTC);\n\t\t\t\t}\n\t\t\t\trefreshToken = new OAuth2RefreshToken(tokenValue, issuedAt);\n\t\t\t}\n\n\t\t\treturn new OAuth2AuthorizedClientHolder(clientRegistrationId, principalName, accessToken, refreshToken);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Map;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\n\n/**\n * Handles when an OAuth 2.0 Client fails to authorize (or re-authorize) via the\n * authorization server or resource server.\n *\n * @author Phil Clay\n * @since 5.3\n */\n@FunctionalInterface\npublic interface ReactiveOAuth2AuthorizationFailureHandler {\n\n\t/**\n\t * Called when an OAuth 2.0 Client fails to authorize (or re-authorize) via the\n\t * authorization server or resource server.\n\t * @param authorizationException the exception that contains details about what failed\n\t * @param principal the {@code Principal} that was attempted to be authorized\n\t * @param attributes an immutable {@code Map} of extra optional attributes present\n\t * under certain conditions. For example, this might contain a\n\t * {@link org.springframework.web.server.ServerWebExchange ServerWebExchange} if the\n\t * authorization was performed within the context of a {@code ServerWebExchange}.\n\t * @return an empty {@link Mono} that completes after this handler has finished\n\t * handling the event.\n\t */\n\tMono<Void> onAuthorizationFailure(OAuth2AuthorizationException authorizationException, Authentication principal,\n\t\t\tMap<String, Object> attributes);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Map;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Handles when an OAuth 2.0 Client has been successfully authorized (or re-authorized)\n * via the authorization server.\n *\n * @author Phil Clay\n * @since 5.3\n */\n@FunctionalInterface\npublic interface ReactiveOAuth2AuthorizationSuccessHandler {\n\n\t/**\n\t * Called when an OAuth 2.0 Client has been successfully authorized (or re-authorized)\n\t * via the authorization server.\n\t * @param authorizedClient the client that was successfully authorized\n\t * @param principal the {@code Principal} associated with the authorized client\n\t * @param attributes an immutable {@code Map} of extra optional attributes present\n\t * under certain conditions. For example, this might contain a\n\t * {@link org.springframework.web.server.ServerWebExchange ServerWebExchange} if the\n\t * authorization was performed within the context of a {@code ServerWebExchange}.\n\t * @return an empty {@link Mono} that completes after this handler has finished\n\t * handling the event.\n\t */\n\tMono<Void> onAuthorizationSuccess(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tMap<String, Object> attributes);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizedClientManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\n\n/**\n * Implementations of this interface are responsible for the overall management of\n * {@link OAuth2AuthorizedClient Authorized Client(s)}.\n *\n * <p>\n * The primary responsibilities include:\n * <ol>\n * <li>Authorizing (or re-authorizing) an OAuth 2.0 Client by leveraging a\n * {@link ReactiveOAuth2AuthorizedClientProvider}(s).</li>\n * <li>Delegating the persistence of an {@link OAuth2AuthorizedClient}, typically using a\n * {@link ReactiveOAuth2AuthorizedClientService} OR\n * {@link ServerOAuth2AuthorizedClientRepository}.</li>\n * </ol>\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClient\n * @see ReactiveOAuth2AuthorizedClientProvider\n * @see ReactiveOAuth2AuthorizedClientService\n * @see ServerOAuth2AuthorizedClientRepository\n */\n@FunctionalInterface\npublic interface ReactiveOAuth2AuthorizedClientManager {\n\n\t/**\n\t * Attempt to authorize or re-authorize (if required) the {@link ClientRegistration\n\t * client} identified by the provided\n\t * {@link OAuth2AuthorizeRequest#getClientRegistrationId() clientRegistrationId}.\n\t * Implementations must return an empty {@code Mono} if authorization is not supported\n\t * for the specified client, e.g. the associated\n\t * {@link ReactiveOAuth2AuthorizedClientProvider}(s) does not support the\n\t * {@link ClientRegistration#getAuthorizationGrantType() authorization grant} type\n\t * configured for the client.\n\t *\n\t * <p>\n\t * In the case of re-authorization, implementations must return the provided\n\t * {@link OAuth2AuthorizeRequest#getAuthorizedClient() authorized client} if\n\t * re-authorization is not supported for the client OR is not required, e.g. a\n\t * {@link OAuth2AuthorizedClient#getRefreshToken() refresh token} is not available OR\n\t * the {@link OAuth2AuthorizedClient#getAccessToken() access token} is not expired.\n\t * @param authorizeRequest the authorize request\n\t * @return the {@link OAuth2AuthorizedClient} or an empty {@code Mono} if\n\t * authorization is not supported for the specified client\n\t */\n\tMono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\n\n/**\n * A strategy for authorizing (or re-authorizing) an OAuth 2.0 Client. Implementations\n * will typically implement a specific {@link AuthorizationGrantType authorization grant}\n * type.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizationContext\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.3\">Section\n * 1.3 Authorization Grant</a>\n */\n@FunctionalInterface\npublic interface ReactiveOAuth2AuthorizedClientProvider {\n\n\t/**\n\t * Attempt to authorize (or re-authorize) the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * context. Implementations must return an empty {@code Mono} if authorization is not\n\t * supported for the specified client, e.g. the provider doesn't support the\n\t * {@link ClientRegistration#getAuthorizationGrantType() authorization grant} type\n\t * configured for the client.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or an empty {@code Mono} if\n\t * authorization is not supported for the specified client\n\t */\n\tMono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizedClientProviderBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.util.Assert;\n\n/**\n * A builder that builds a {@link DelegatingReactiveOAuth2AuthorizedClientProvider}\n * composed of one or more {@link ReactiveOAuth2AuthorizedClientProvider}(s) that\n * implement specific authorization grants. The supported authorization grants are\n * {@link #authorizationCode() authorization_code}, {@link #refreshToken() refresh_token}\n * and {@link #clientCredentials() client_credentials}. In addition to the standard\n * authorization grants, an implementation of an extension grant may be supplied via\n * {@link #provider(ReactiveOAuth2AuthorizedClientProvider)}.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see ReactiveOAuth2AuthorizedClientProvider\n * @see AuthorizationCodeReactiveOAuth2AuthorizedClientProvider\n * @see RefreshTokenReactiveOAuth2AuthorizedClientProvider\n * @see ClientCredentialsReactiveOAuth2AuthorizedClientProvider\n * @see DelegatingReactiveOAuth2AuthorizedClientProvider\n */\npublic final class ReactiveOAuth2AuthorizedClientProviderBuilder {\n\n\tprivate final Map<Class<?>, Builder> builders = new LinkedHashMap<>();\n\n\tprivate ReactiveOAuth2AuthorizedClientProviderBuilder() {\n\t}\n\n\t/**\n\t * Returns a new {@link ReactiveOAuth2AuthorizedClientProviderBuilder} for configuring\n\t * the supported authorization grant(s).\n\t * @return the {@link ReactiveOAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic static ReactiveOAuth2AuthorizedClientProviderBuilder builder() {\n\t\treturn new ReactiveOAuth2AuthorizedClientProviderBuilder();\n\t}\n\n\t/**\n\t * Configures a {@link ReactiveOAuth2AuthorizedClientProvider} to be composed with the\n\t * {@link DelegatingReactiveOAuth2AuthorizedClientProvider}. This may be used for\n\t * implementations of extension authorization grants.\n\t * @return the {@link ReactiveOAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic ReactiveOAuth2AuthorizedClientProviderBuilder provider(ReactiveOAuth2AuthorizedClientProvider provider) {\n\t\tAssert.notNull(provider, \"provider cannot be null\");\n\t\tthis.builders.computeIfAbsent(provider.getClass(), (k) -> () -> provider);\n\t\treturn ReactiveOAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code authorization_code} grant.\n\t * @return the {@link ReactiveOAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic ReactiveOAuth2AuthorizedClientProviderBuilder authorizationCode() {\n\t\tthis.builders.computeIfAbsent(AuthorizationCodeReactiveOAuth2AuthorizedClientProvider.class,\n\t\t\t\t(k) -> new AuthorizationCodeGrantBuilder());\n\t\treturn ReactiveOAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code refresh_token} grant.\n\t * @return the {@link ReactiveOAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic ReactiveOAuth2AuthorizedClientProviderBuilder refreshToken() {\n\t\tthis.builders.computeIfAbsent(RefreshTokenReactiveOAuth2AuthorizedClientProvider.class,\n\t\t\t\t(k) -> new RefreshTokenGrantBuilder());\n\t\treturn ReactiveOAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code refresh_token} grant.\n\t * @param builderConsumer a {@code Consumer} of {@link RefreshTokenGrantBuilder} used\n\t * for further configuration\n\t * @return the {@link ReactiveOAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic ReactiveOAuth2AuthorizedClientProviderBuilder refreshToken(\n\t\t\tConsumer<RefreshTokenGrantBuilder> builderConsumer) {\n\t\tRefreshTokenGrantBuilder builder = (RefreshTokenGrantBuilder) this.builders.computeIfAbsent(\n\t\t\t\tRefreshTokenReactiveOAuth2AuthorizedClientProvider.class, (k) -> new RefreshTokenGrantBuilder());\n\t\tbuilderConsumer.accept(builder);\n\t\treturn ReactiveOAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code client_credentials} grant.\n\t * @return the {@link ReactiveOAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic ReactiveOAuth2AuthorizedClientProviderBuilder clientCredentials() {\n\t\tthis.builders.computeIfAbsent(ClientCredentialsReactiveOAuth2AuthorizedClientProvider.class,\n\t\t\t\t(k) -> new ClientCredentialsGrantBuilder());\n\t\treturn ReactiveOAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Configures support for the {@code client_credentials} grant.\n\t * @param builderConsumer a {@code Consumer} of {@link ClientCredentialsGrantBuilder}\n\t * used for further configuration\n\t * @return the {@link ReactiveOAuth2AuthorizedClientProviderBuilder}\n\t */\n\tpublic ReactiveOAuth2AuthorizedClientProviderBuilder clientCredentials(\n\t\t\tConsumer<ClientCredentialsGrantBuilder> builderConsumer) {\n\t\tClientCredentialsGrantBuilder builder = (ClientCredentialsGrantBuilder) this.builders.computeIfAbsent(\n\t\t\t\tClientCredentialsReactiveOAuth2AuthorizedClientProvider.class,\n\t\t\t\t(k) -> new ClientCredentialsGrantBuilder());\n\t\tbuilderConsumer.accept(builder);\n\t\treturn ReactiveOAuth2AuthorizedClientProviderBuilder.this;\n\t}\n\n\t/**\n\t * Builds an instance of {@link DelegatingReactiveOAuth2AuthorizedClientProvider}\n\t * composed of one or more {@link ReactiveOAuth2AuthorizedClientProvider}(s).\n\t * @return the {@link DelegatingReactiveOAuth2AuthorizedClientProvider}\n\t */\n\tpublic ReactiveOAuth2AuthorizedClientProvider build() {\n\t\tList<ReactiveOAuth2AuthorizedClientProvider> authorizedClientProviders = this.builders.values()\n\t\t\t.stream()\n\t\t\t.map(Builder::build)\n\t\t\t.collect(Collectors.toList());\n\t\treturn new DelegatingReactiveOAuth2AuthorizedClientProvider(authorizedClientProviders);\n\t}\n\n\tinterface Builder {\n\n\t\tReactiveOAuth2AuthorizedClientProvider build();\n\n\t}\n\n\t/**\n\t * A builder for the {@code authorization_code} grant.\n\t */\n\tpublic final class AuthorizationCodeGrantBuilder implements Builder {\n\n\t\tprivate AuthorizationCodeGrantBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Builds an instance of\n\t\t * {@link AuthorizationCodeReactiveOAuth2AuthorizedClientProvider}.\n\t\t * @return the {@link AuthorizationCodeReactiveOAuth2AuthorizedClientProvider}\n\t\t */\n\t\t@Override\n\t\tpublic ReactiveOAuth2AuthorizedClientProvider build() {\n\t\t\treturn new AuthorizationCodeReactiveOAuth2AuthorizedClientProvider();\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for the {@code client_credentials} grant.\n\t */\n\tpublic final class ClientCredentialsGrantBuilder implements Builder {\n\n\t\tprivate @Nullable ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;\n\n\t\tprivate @Nullable Duration clockSkew;\n\n\t\tprivate @Nullable Clock clock;\n\n\t\tprivate ClientCredentialsGrantBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the client used when requesting an access token credential at the Token\n\t\t * Endpoint.\n\t\t * @param accessTokenResponseClient the client used when requesting an access\n\t\t * token credential at the Token Endpoint\n\t\t * @return the {@link ClientCredentialsGrantBuilder}\n\t\t */\n\t\tpublic ClientCredentialsGrantBuilder accessTokenResponseClient(\n\t\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient) {\n\t\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the maximum acceptable clock skew, which is used when checking the access\n\t\t * token expiry. An access token is considered expired if\n\t\t * {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time\n\t\t * {@code clock#instant()}.\n\t\t * @param clockSkew the maximum acceptable clock skew\n\t\t * @return the {@link ClientCredentialsGrantBuilder}\n\t\t * @see ClientCredentialsReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)\n\t\t */\n\t\tpublic ClientCredentialsGrantBuilder clockSkew(Duration clockSkew) {\n\t\t\tthis.clockSkew = clockSkew;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the\n\t\t * access token expiry.\n\t\t * @param clock the clock\n\t\t * @return the {@link ClientCredentialsGrantBuilder}\n\t\t */\n\t\tpublic ClientCredentialsGrantBuilder clock(Clock clock) {\n\t\t\tthis.clock = clock;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds an instance of\n\t\t * {@link ClientCredentialsReactiveOAuth2AuthorizedClientProvider}.\n\t\t * @return the {@link ClientCredentialsReactiveOAuth2AuthorizedClientProvider}\n\t\t */\n\t\t@Override\n\t\tpublic ReactiveOAuth2AuthorizedClientProvider build() {\n\t\t\tClientCredentialsReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();\n\t\t\tif (this.accessTokenResponseClient != null) {\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\t\t}\n\t\t\tif (this.clockSkew != null) {\n\t\t\t\tauthorizedClientProvider.setClockSkew(this.clockSkew);\n\t\t\t}\n\t\t\tif (this.clock != null) {\n\t\t\t\tauthorizedClientProvider.setClock(this.clock);\n\t\t\t}\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for the {@code refresh_token} grant.\n\t */\n\tpublic final class RefreshTokenGrantBuilder implements Builder {\n\n\t\tprivate @Nullable ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient;\n\n\t\tprivate @Nullable ReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler;\n\n\t\tprivate @Nullable Duration clockSkew;\n\n\t\tprivate @Nullable Clock clock;\n\n\t\tprivate RefreshTokenGrantBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the client used when requesting an access token credential at the Token\n\t\t * Endpoint.\n\t\t * @param accessTokenResponseClient the client used when requesting an access\n\t\t * token credential at the Token Endpoint\n\t\t * @return the {@link RefreshTokenGrantBuilder}\n\t\t */\n\t\tpublic RefreshTokenGrantBuilder accessTokenResponseClient(\n\t\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient) {\n\t\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets a {@link ReactiveOAuth2AuthorizationSuccessHandler} to use for handling\n\t\t * successful refresh token response, see\n\t\t * {@link RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler}.\n\t\t * @param authorizationSuccessHandler the\n\t\t * {@link ReactiveOAuth2AuthorizationSuccessHandler} to use\n\t\t * @return the {@link RefreshTokenGrantBuilder}\n\t\t * @since 7.1\n\t\t */\n\t\tpublic RefreshTokenGrantBuilder authorizationSuccessHandler(\n\t\t\t\tReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler) {\n\t\t\tthis.authorizationSuccessHandler = authorizationSuccessHandler;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the maximum acceptable clock skew, which is used when checking the access\n\t\t * token expiry. An access token is considered expired if\n\t\t * {@code OAuth2Token#getExpiresAt() - clockSkew} is before the current time\n\t\t * {@code clock#instant()}.\n\t\t * @param clockSkew the maximum acceptable clock skew\n\t\t * @return the {@link RefreshTokenGrantBuilder}\n\t\t * @see RefreshTokenReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)\n\t\t */\n\t\tpublic RefreshTokenGrantBuilder clockSkew(Duration clockSkew) {\n\t\t\tthis.clockSkew = clockSkew;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the\n\t\t * access token expiry.\n\t\t * @param clock the clock\n\t\t * @return the {@link RefreshTokenGrantBuilder}\n\t\t */\n\t\tpublic RefreshTokenGrantBuilder clock(Clock clock) {\n\t\t\tthis.clock = clock;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds an instance of\n\t\t * {@link RefreshTokenReactiveOAuth2AuthorizedClientProvider}.\n\t\t * @return the {@link RefreshTokenReactiveOAuth2AuthorizedClientProvider}\n\t\t */\n\t\t@Override\n\t\tpublic ReactiveOAuth2AuthorizedClientProvider build() {\n\t\t\tRefreshTokenReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = new RefreshTokenReactiveOAuth2AuthorizedClientProvider();\n\t\t\tif (this.accessTokenResponseClient != null) {\n\t\t\t\tauthorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\t\t}\n\t\t\tif (this.authorizationSuccessHandler != null) {\n\t\t\t\tauthorizedClientProvider.setAuthorizationSuccessHandler(this.authorizationSuccessHandler);\n\t\t\t}\n\t\t\tif (this.clockSkew != null) {\n\t\t\t\tauthorizedClientProvider.setClockSkew(this.clockSkew);\n\t\t\t}\n\t\t\tif (this.clock != null) {\n\t\t\t\tauthorizedClientProvider.setClock(this.clock);\n\t\t\t}\n\t\t\treturn authorizedClientProvider;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizedClientService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\n/**\n * Implementations of this interface are responsible for the management of\n * {@link OAuth2AuthorizedClient Authorized Client(s)}, which provide the purpose of\n * associating an {@link OAuth2AuthorizedClient#getAccessToken() Access Token} credential\n * to a {@link OAuth2AuthorizedClient#getClientRegistration() Client} and Resource Owner,\n * who is the {@link OAuth2AuthorizedClient#getPrincipalName() Principal} that originally\n * granted the authorization.\n *\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2AuthorizedClient\n * @see ClientRegistration\n * @see Authentication\n * @see OAuth2AccessToken\n */\npublic interface ReactiveOAuth2AuthorizedClientService {\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizedClient} associated to the provided client\n\t * registration identifier and End-User's {@code Principal} name or {@code null} if\n\t * not available.\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principalName the name of the End-User {@code Principal} (Resource Owner)\n\t * @param <T> a type of OAuth2AuthorizedClient\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if not available\n\t */\n\t<T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId, String principalName);\n\n\t/**\n\t * Saves the {@link OAuth2AuthorizedClient} associating it to the provided End-User\n\t * {@link Authentication} (Resource Owner).\n\t * @param authorizedClient the authorized client\n\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t */\n\tMono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal);\n\n\t/**\n\t * Removes the {@link OAuth2AuthorizedClient} associated to the provided client\n\t * registration identifier and End-User's {@code Principal} name.\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principalName the name of the End-User {@code Principal} (Resource Owner)\n\t */\n\tMono<Void> removeAuthorizedClient(String clientRegistrationId, String principalName);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.net.URL;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.oidc.authentication.ReactiveOidcIdTokenDecoderFactory;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ReactiveOAuth2AuthorizationSuccessHandler} that refreshes an {@link OidcUser}\n * in the {@link SecurityContext} if the refreshed {@link OidcIdToken} is valid according\n * to <a href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse\">OpenID\n * Connect Core 1.0 - Section 12.2 Successful Refresh Response</a>\n *\n * @author Evgeniy Cheban\n * @since 7.1\n * @see RefreshTokenReactiveOAuth2AuthorizedClientProvider\n */\npublic final class RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler\n\t\timplements ReactiveOAuth2AuthorizationSuccessHandler {\n\n\tprivate static final String INVALID_ID_TOKEN_ERROR_CODE = \"invalid_id_token\";\n\n\tprivate static final String INVALID_NONCE_ERROR_CODE = \"invalid_nonce\";\n\n\tprivate static final String REFRESH_TOKEN_RESPONSE_ERROR_URI = \"https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse\";\n\n\t// @formatter:off\n\tprivate static final Mono<ServerWebExchange> currentServerWebExchangeMono = Mono.deferContextual(Mono::just)\n\t\t\t.filter((c) -> c.hasKey(ServerWebExchange.class))\n\t\t\t.map((c) -> c.get(ServerWebExchange.class));\n\t// @formatter:on\n\n\tprivate ServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\n\tprivate ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();\n\n\tprivate ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = new OidcReactiveOAuth2UserService();\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = (authorities) -> authorities;\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\t@Override\n\tpublic Mono<Void> onAuthorizationSuccess(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tMap<String, Object> attributes) {\n\t\t// The response must contain the openid scope.\n\t\tif (!authorizedClient.getAccessToken().getScopes().contains(OidcScopes.OPENID)) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\t// The response must contain an id_token.\n\t\tString idToken = extractIdToken(attributes);\n\t\tif (!StringUtils.hasText(idToken)) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tif (!(principal instanceof OAuth2AuthenticationToken authenticationToken)\n\t\t\t\t|| authenticationToken.getClass() != OAuth2AuthenticationToken.class) {\n\t\t\t// If the application customizes the authentication result, then a custom\n\t\t\t// handler should be provided.\n\t\t\treturn Mono.empty();\n\t\t}\n\t\t// The current principal must be an OidcUser.\n\t\tif (!(authenticationToken.getPrincipal() instanceof OidcUser existingOidcUser)) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tClientRegistration clientRegistration = authorizedClient.getClientRegistration();\n\t\t// The registrationId must match the one used to log in.\n\t\tif (!authenticationToken.getAuthorizedClientRegistrationId().equals(clientRegistration.getRegistrationId())) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\t// Create, validate OidcIdToken and refresh OidcUser in the SecurityContext.\n\t\treturn Mono.justOrEmpty((ServerWebExchange) attributes.get(ServerWebExchange.class.getName()))\n\t\t\t.switchIfEmpty(currentServerWebExchangeMono)\n\t\t\t.flatMap((exchange) -> {\n\t\t\t\tReactiveJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);\n\t\t\t\treturn jwtDecoder.decode(idToken).onErrorMap(JwtException.class, (ex) -> {\n\t\t\t\t\tOAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(),\n\t\t\t\t\t\t\tnull);\n\t\t\t\t\treturn new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex);\n\t\t\t\t})\n\t\t\t\t\t.map((jwt) -> new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(),\n\t\t\t\t\t\t\tjwt.getClaims()))\n\t\t\t\t\t.doOnNext((oidcIdToken) -> validateIdToken(existingOidcUser, oidcIdToken))\n\t\t\t\t\t.flatMap((oidcIdToken) -> {\n\t\t\t\t\t\tOidcUserRequest userRequest = new OidcUserRequest(clientRegistration,\n\t\t\t\t\t\t\t\tauthorizedClient.getAccessToken(), oidcIdToken);\n\t\t\t\t\t\treturn this.userService.loadUser(userRequest);\n\t\t\t\t\t})\n\t\t\t\t\t.flatMap((oidcUser) -> refreshSecurityContext(exchange, clientRegistration, authenticationToken,\n\t\t\t\t\t\t\toidcUser));\n\t\t\t});\n\t}\n\n\t/**\n\t * Sets a {@link ServerSecurityContextRepository} to use for refreshing a\n\t * {@link SecurityContext}, defaults to\n\t * {@link WebSessionServerSecurityContextRepository}.\n\t * @param serverSecurityContextRepository the {@link ServerSecurityContextRepository}\n\t * to use\n\t */\n\tpublic void setServerSecurityContextRepository(ServerSecurityContextRepository serverSecurityContextRepository) {\n\t\tAssert.notNull(serverSecurityContextRepository, \"serverSecurityContextRepository cannot be null\");\n\t\tthis.serverSecurityContextRepository = serverSecurityContextRepository;\n\t}\n\n\t/**\n\t * Sets a {@link ReactiveJwtDecoderFactory} to use for decoding refreshed oidc\n\t * id-token, defaults to {@link ReactiveOidcIdTokenDecoderFactory}.\n\t * @param jwtDecoderFactory the {@link ReactiveJwtDecoderFactory} to use\n\t */\n\tpublic void setJwtDecoderFactory(ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory) {\n\t\tAssert.notNull(jwtDecoderFactory, \"jwtDecoderFactory cannot be null\");\n\t\tthis.jwtDecoderFactory = jwtDecoderFactory;\n\t}\n\n\t/**\n\t * Sets a {@link ReactiveOAuth2UserService} to use for loading an {@link OidcUser}\n\t * from refreshed oidc id-token, defaults to {@link OidcReactiveOAuth2UserService}.\n\t * @param userService the {@link ReactiveOAuth2UserService} to use\n\t */\n\tpublic void setUserService(ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService) {\n\t\tAssert.notNull(userService, \"userService cannot be null\");\n\t\tthis.userService = userService;\n\t}\n\n\t/**\n\t * Sets a {@link GrantedAuthoritiesMapper} to use for mapping\n\t * {@link GrantedAuthority}s, defaults to no-op implementation.\n\t * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} to use\n\t */\n\tpublic void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tAssert.notNull(authoritiesMapper, \"authoritiesMapper cannot be null\");\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OidcIdToken#getIssuedAt()} to match the existing\n\t * {@link OidcUser#getIdToken()}'s issuedAt time, defaults to 60 seconds.\n\t * @param clockSkew the maximum acceptable clock skew to use\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\tprivate @Nullable String extractIdToken(Map<String, Object> attributes) {\n\t\tif (attributes.get(OidcParameterNames.ID_TOKEN) instanceof String idToken) {\n\t\t\treturn idToken;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate void validateIdToken(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\t// OpenID Connect Core 1.0 - Section 12.2 Successful Refresh Response\n\t\t// If an ID Token is returned as a result of a token refresh request, the\n\t\t// following requirements apply:\n\t\t// its iss Claim Value MUST be the same as in the ID Token issued when the\n\t\t// original authentication occurred,\n\t\tvalidateIssuer(existingOidcUser, idToken);\n\t\t// its sub Claim Value MUST be the same as in the ID Token issued when the\n\t\t// original authentication occurred,\n\t\tvalidateSubject(existingOidcUser, idToken);\n\t\t// its iat Claim MUST represent the time that the new ID Token is issued,\n\t\tvalidateIssuedAt(existingOidcUser, idToken);\n\t\t// its aud Claim Value MUST be the same as in the ID Token issued when the\n\t\t// original authentication occurred,\n\t\tvalidateAudience(existingOidcUser, idToken);\n\t\t// if the ID Token contains an auth_time Claim, its value MUST represent the time\n\t\t// of the original authentication - not the time that the new ID token is issued,\n\t\tvalidateAuthenticatedAt(existingOidcUser, idToken);\n\t\t// it SHOULD NOT have a nonce Claim, even when the ID Token issued at the time of\n\t\t// the original authentication contained nonce; however, if it is present, its\n\t\t// value MUST be the same as in the ID Token issued at the time of the original\n\t\t// authentication,\n\t\tvalidateNonce(existingOidcUser, idToken);\n\t}\n\n\tprivate void validateIssuer(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tURL idTokenIssuer = idToken.getIssuer();\n\t\tURL existingIdTokenIssuer = existingOidcUser.getIdToken().getIssuer();\n\t\tif (idTokenIssuer == null || existingIdTokenIssuer == null\n\t\t\t\t|| !idTokenIssuer.toString().equals(existingIdTokenIssuer.toString())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid issuer\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate void validateSubject(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tif (!Objects.equals(idToken.getSubject(), existingOidcUser.getIdToken().getSubject())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid subject\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate void validateIssuedAt(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tInstant idTokenIssuedAt = idToken.getIssuedAt();\n\t\tInstant existingIdTokenIssuedAt = existingOidcUser.getIdToken().getIssuedAt();\n\t\tif (idTokenIssuedAt == null || existingIdTokenIssuedAt == null\n\t\t\t\t|| !idTokenIssuedAt.isAfter(existingIdTokenIssuedAt.minus(this.clockSkew))) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid issued at time\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate void validateAudience(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tif (!isValidAudience(existingOidcUser, idToken)) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid audience\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate boolean isValidAudience(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tList<String> idTokenAudiences = idToken.getAudience();\n\t\tList<String> existingIdTokenAudiences = existingOidcUser.getIdToken().getAudience();\n\t\tif (idTokenAudiences == null || existingIdTokenAudiences == null\n\t\t\t\t|| idTokenAudiences.size() != existingIdTokenAudiences.size()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (String audience : idTokenAudiences) {\n\t\t\tif (!existingIdTokenAudiences.contains(audience)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void validateAuthenticatedAt(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tif (idToken.getAuthenticatedAt() == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!idToken.getAuthenticatedAt().equals(existingOidcUser.getIdToken().getAuthenticatedAt())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid authenticated at time\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate void validateNonce(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tif (!StringUtils.hasText(idToken.getNonce())) {\n\t\t\treturn;\n\t\t}\n\t\tif (!idToken.getNonce().equals(existingOidcUser.getIdToken().getNonce())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE, \"Invalid nonce\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate Mono<Void> refreshSecurityContext(ServerWebExchange exchange, ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthenticationToken authenticationToken, OidcUser oidcUser) {\n\t\tCollection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper\n\t\t\t.mapAuthorities(oidcUser.getAuthorities());\n\t\tOAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(oidcUser, mappedAuthorities,\n\t\t\t\tclientRegistration.getRegistrationId());\n\t\tauthenticationResult.setDetails(authenticationToken.getDetails());\n\t\tSecurityContext securityContext = new SecurityContextImpl(authenticationResult);\n\t\treturn this.serverSecurityContextRepository.save(exchange, securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RefreshTokenOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.RestClientRefreshTokenTokenResponseClient;\nimport org.springframework.security.oauth2.client.event.OAuth2AuthorizedClientRefreshedEvent;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#REFRESH_TOKEN refresh_token} grant.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientProvider\n * @see RestClientRefreshTokenTokenResponseClient\n */\npublic final class RefreshTokenOAuth2AuthorizedClientProvider\n\t\timplements OAuth2AuthorizedClientProvider, ApplicationEventPublisherAware {\n\n\tprivate OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient = new RestClientRefreshTokenTokenResponseClient();\n\n\tprivate @Nullable ApplicationEventPublisher applicationEventPublisher;\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Attempt to re-authorize the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * {@code context}. Returns {@code null} if re-authorization is not supported, e.g.\n\t * the client is not authorized OR the {@link OAuth2AuthorizedClient#getRefreshToken()\n\t * refresh token} is not available for the authorized client OR the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} is not expired.\n\t *\n\t * <p>\n\t * The following {@link OAuth2AuthorizationContext#getAttributes() context attributes}\n\t * are supported:\n\t * <ol>\n\t * <li>{@link OAuth2AuthorizationContext#REQUEST_SCOPE_ATTRIBUTE_NAME} (optional) - a\n\t * {@code String[]} of scope(s) to be requested by the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client}</li>\n\t * </ol>\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if re-authorization is\n\t * not supported\n\t */\n\t@Override\n\tpublic @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tOAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();\n\t\tif (authorizedClient == null || authorizedClient.getRefreshToken() == null\n\t\t\t\t|| !hasTokenExpired(authorizedClient.getAccessToken())) {\n\t\t\treturn null;\n\t\t}\n\t\tObject requestScope = context.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);\n\t\tSet<String> scopes = Collections.emptySet();\n\t\tif (requestScope != null) {\n\t\t\tAssert.isInstanceOf(String[].class, requestScope, \"The context attribute must be of type String[] '\"\n\t\t\t\t\t+ OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME + \"'\");\n\t\t\tscopes = new HashSet<>(Arrays.asList((String[]) requestScope));\n\t\t}\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tauthorizedClient.getClientRegistration(), authorizedClient.getAccessToken(),\n\t\t\t\tauthorizedClient.getRefreshToken(), scopes);\n\t\tOAuth2AccessTokenResponse tokenResponse = getTokenResponse(authorizedClient, refreshTokenGrantRequest);\n\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = new OAuth2AuthorizedClient(\n\t\t\t\tauthorizedClient.getClientRegistration(), context.getPrincipal().getName(),\n\t\t\t\ttokenResponse.getAccessToken(), tokenResponse.getRefreshToken());\n\n\t\tif (this.applicationEventPublisher != null) {\n\t\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\t\ttokenResponse, refreshedAuthorizedClient);\n\t\t\tthis.applicationEventPublisher.publishEvent(authorizedClientRefreshedEvent);\n\t\t}\n\n\t\treturn refreshedAuthorizedClient;\n\t}\n\n\tprivate OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizedClient authorizedClient,\n\t\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {\n\t\ttry {\n\t\t\treturn this.accessTokenResponseClient.getTokenResponse(refreshTokenGrantRequest);\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tthrow new ClientAuthorizationException(ex.getError(),\n\t\t\t\t\tauthorizedClient.getClientRegistration().getRegistrationId(), ex);\n\t\t}\n\t}\n\n\tprivate boolean hasTokenExpired(OAuth2Token token) {\n\t\tInstant expiresAt = token.getExpiresAt();\n\t\treturn expiresAt != null && this.clock.instant().isAfter(expiresAt.minus(this.clockSkew));\n\t}\n\n\t/**\n\t * Sets the client used when requesting an access token credential at the Token\n\t * Endpoint for the {@code refresh_token} grant.\n\t * @param accessTokenResponseClient the client used when requesting an access token\n\t * credential at the Token Endpoint for the {@code refresh_token} grant\n\t */\n\tpublic void setAccessTokenResponseClient(\n\t\t\tOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is\n\t * 60 seconds.\n\t *\n\t * <p>\n\t * An access token is considered expired if\n\t * {@code OAuth2AccessToken#getExpiresAt() - clockSkew} is before the current time\n\t * {@code clock#instant()}.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access\n\t * token expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tAssert.notNull(applicationEventPublisher, \"applicationEventPublisher cannot be null\");\n\t\tthis.applicationEventPublisher = applicationEventPublisher;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RefreshTokenReactiveOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.WebClientReactiveRefreshTokenTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\n\n/**\n * An implementation of a {@link ReactiveOAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#REFRESH_TOKEN refresh_token} grant.\n *\n * @author Joe Grandja\n * @author Evgeniy Cheban\n * @since 5.2\n * @see ReactiveOAuth2AuthorizedClientProvider\n * @see WebClientReactiveRefreshTokenTokenResponseClient\n */\npublic final class RefreshTokenReactiveOAuth2AuthorizedClientProvider\n\t\timplements ReactiveOAuth2AuthorizedClientProvider {\n\n\tprivate static final boolean josePresent = ClassUtils.isPresent(\n\t\t\t\"org.springframework.security.oauth2.jwt.ReactiveJwtDecoder\",\n\t\t\tRefreshTokenReactiveOAuth2AuthorizedClientProvider.class.getClassLoader());\n\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient = new WebClientReactiveRefreshTokenTokenResponseClient();\n\n\tprivate ReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler = (authorizedClient, principal,\n\t\t\tattributes) -> Mono.empty();\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\tpublic RefreshTokenReactiveOAuth2AuthorizedClientProvider() {\n\t\tif (josePresent) {\n\t\t\tthis.authorizationSuccessHandler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\t}\n\t}\n\n\t/**\n\t * Attempt to re-authorize the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * {@code context}. Returns an empty {@code Mono} if re-authorization is not\n\t * supported, e.g. the client is not authorized OR the\n\t * {@link OAuth2AuthorizedClient#getRefreshToken() refresh token} is not available for\n\t * the authorized client OR the {@link OAuth2AuthorizedClient#getAccessToken() access\n\t * token} is not expired.\n\t *\n\t * <p>\n\t * The following {@link OAuth2AuthorizationContext#getAttributes() context attributes}\n\t * are supported:\n\t * <ol>\n\t * <li>{@code \"org.springframework.security.oauth2.client.REQUEST_SCOPE\"} (optional) -\n\t * a {@code String[]} of scope(s) to be requested by the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client}</li>\n\t * </ol>\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or an empty {@code Mono} if\n\t * re-authorization is not supported\n\t */\n\t@Override\n\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tOAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();\n\t\tif (authorizedClient == null || authorizedClient.getRefreshToken() == null\n\t\t\t\t|| !hasTokenExpired(authorizedClient.getAccessToken())) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tObject requestScope = context.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);\n\t\tSet<String> scopes = Collections.emptySet();\n\t\tif (requestScope != null) {\n\t\t\tAssert.isInstanceOf(String[].class, requestScope, \"The context attribute must be of type String[] '\"\n\t\t\t\t\t+ OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME + \"'\");\n\t\t\tscopes = new HashSet<>(Arrays.asList((String[]) requestScope));\n\t\t}\n\t\tClientRegistration clientRegistration = context.getClientRegistration();\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tauthorizedClient.getAccessToken(), authorizedClient.getRefreshToken(), scopes);\n\t\treturn Mono.just(refreshTokenGrantRequest)\n\t\t\t.flatMap(this.accessTokenResponseClient::getTokenResponse)\n\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t(e) -> new ClientAuthorizationException(e.getError(), clientRegistration.getRegistrationId(), e))\n\t\t\t.flatMap((tokenResponse) -> {\n\t\t\t\tOAuth2AuthorizedClient refreshedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\t\t\tcontext.getPrincipal().getName(), tokenResponse.getAccessToken(),\n\t\t\t\t\t\ttokenResponse.getRefreshToken());\n\t\t\t\tMap<String, Object> attributes = new HashMap<>(context.getAttributes());\n\t\t\t\tattributes.putAll(tokenResponse.getAdditionalParameters());\n\t\t\t\treturn this.authorizationSuccessHandler\n\t\t\t\t\t.onAuthorizationSuccess(refreshedClient, context.getPrincipal(),\n\t\t\t\t\t\t\tCollections.unmodifiableMap(attributes))\n\t\t\t\t\t.thenReturn(refreshedClient);\n\t\t\t});\n\t}\n\n\t/**\n\t * Sets the client used when requesting an access token credential at the Token\n\t * Endpoint for the {@code refresh_token} grant.\n\t * @param accessTokenResponseClient the client used when requesting an access token\n\t * credential at the Token Endpoint for the {@code refresh_token} grant\n\t */\n\tpublic void setAccessTokenResponseClient(\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t/**\n\t * Sets a {@link ReactiveOAuth2AuthorizationSuccessHandler} to use for handling\n\t * successful refresh token response. Defaults to\n\t * {@link RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler}, when\n\t * {@code spring-security-oauth2-jose} is available on the classpath.\n\t * @param authorizationSuccessHandler the\n\t * {@link ReactiveOAuth2AuthorizationSuccessHandler} to use\n\t * @since 7.1\n\t */\n\tpublic void setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler) {\n\t\tAssert.notNull(authorizationSuccessHandler, \"authorizationSuccessHandler cannot be null\");\n\t\tthis.authorizationSuccessHandler = authorizationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is\n\t * 60 seconds.\n\t *\n\t * <p>\n\t * An access token is considered expired if\n\t * {@code OAuth2AccessToken#getExpiresAt() - clockSkew} is before the current time\n\t * {@code clock#instant()}.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access\n\t * token expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n\tprivate boolean hasTokenExpired(OAuth2Token token) {\n\t\tInstant expiresAt = token.getExpiresAt();\n\t\treturn expiresAt != null && this.clock.instant().isAfter(expiresAt.minus(this.clockSkew));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientOAuth2AuthorizationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2AuthorizationFailureHandler} that removes an\n * {@link OAuth2AuthorizedClient} when the {@link OAuth2Error#getErrorCode()} matches one\n * of the configured {@link OAuth2ErrorCodes OAuth 2.0 error codes}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientRepository\n * @see OAuth2AuthorizedClientService\n */\npublic class RemoveAuthorizedClientOAuth2AuthorizationFailureHandler implements OAuth2AuthorizationFailureHandler {\n\n\t/**\n\t * The default OAuth 2.0 error codes that will trigger removal of an\n\t * {@link OAuth2AuthorizedClient}.\n\t * @see OAuth2ErrorCodes\n\t */\n\tpublic static final Set<String> DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES;\n\tstatic {\n\t\tSet<String> codes = new LinkedHashSet<>();\n\t\t// Returned from Resource Servers when an access token provided is expired,\n\t\t// revoked, malformed, or invalid for other reasons. Note that this is needed\n\t\t// because ServletOAuth2AuthorizedClientExchangeFilterFunction delegates this type\n\t\t// of failure received from a Resource Server to this failure handler.\n\t\tcodes.add(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t// Returned from Authorization Servers when the authorization grant or refresh\n\t\t// token is invalid, expired, revoked, does not match the redirection URI used in\n\t\t// the authorization request, or was issued to another client.\n\t\tcodes.add(OAuth2ErrorCodes.INVALID_GRANT);\n\t\tDEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES = Collections.unmodifiableSet(codes);\n\t}\n\n\t/**\n\t * The OAuth 2.0 error codes which will trigger removal of an\n\t * {@link OAuth2AuthorizedClient}.\n\t * @see OAuth2ErrorCodes\n\t */\n\tprivate final Set<String> removeAuthorizedClientErrorCodes;\n\n\t/**\n\t * A delegate that removes an {@link OAuth2AuthorizedClient} from an\n\t * {@link OAuth2AuthorizedClientRepository} or {@link OAuth2AuthorizedClientService}\n\t * if the error code is one of the {@link #removeAuthorizedClientErrorCodes}.\n\t */\n\tprivate final OAuth2AuthorizedClientRemover delegate;\n\n\t/**\n\t * Constructs a {@code RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} using\n\t * the provided parameters.\n\t * @param authorizedClientRemover the {@link OAuth2AuthorizedClientRemover} used for\n\t * removing an {@link OAuth2AuthorizedClient} if the error code is one of the\n\t * {@link #DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES}.\n\t */\n\tpublic RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(\n\t\t\tOAuth2AuthorizedClientRemover authorizedClientRemover) {\n\t\tthis(authorizedClientRemover, DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES);\n\t}\n\n\t/**\n\t * Constructs a {@code RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} using\n\t * the provided parameters.\n\t * @param authorizedClientRemover the {@link OAuth2AuthorizedClientRemover} used for\n\t * removing an {@link OAuth2AuthorizedClient} if the error code is one of the\n\t * {@link #removeAuthorizedClientErrorCodes}.\n\t * @param removeAuthorizedClientErrorCodes the OAuth 2.0 error codes which will\n\t * trigger removal of an authorized client.\n\t * @see OAuth2ErrorCodes\n\t */\n\tpublic RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(\n\t\t\tOAuth2AuthorizedClientRemover authorizedClientRemover, Set<String> removeAuthorizedClientErrorCodes) {\n\t\tAssert.notNull(authorizedClientRemover, \"authorizedClientRemover cannot be null\");\n\t\tAssert.notNull(removeAuthorizedClientErrorCodes, \"removeAuthorizedClientErrorCodes cannot be null\");\n\t\tthis.removeAuthorizedClientErrorCodes = Collections\n\t\t\t.unmodifiableSet(new HashSet<>(removeAuthorizedClientErrorCodes));\n\t\tthis.delegate = authorizedClientRemover;\n\t}\n\n\t@Override\n\tpublic void onAuthorizationFailure(OAuth2AuthorizationException authorizationException, Authentication principal,\n\t\t\tMap<String, Object> attributes) {\n\t\tif (authorizationException instanceof ClientAuthorizationException clientAuthorizationException\n\t\t\t\t&& hasRemovalErrorCode(authorizationException)) {\n\t\t\tthis.delegate.removeAuthorizedClient(clientAuthorizationException.getClientRegistrationId(), principal,\n\t\t\t\t\tattributes);\n\t\t}\n\t}\n\n\t/**\n\t * Returns true if the given exception has an error code that indicates that the\n\t * authorized client should be removed.\n\t * @param authorizationException the exception that caused the authorization failure\n\t * @return true if the given exception has an error code that indicates that the\n\t * authorized client should be removed.\n\t */\n\tprivate boolean hasRemovalErrorCode(OAuth2AuthorizationException authorizationException) {\n\t\treturn this.removeAuthorizedClientErrorCodes.contains(authorizationException.getError().getErrorCode());\n\t}\n\n\t/**\n\t * Removes an {@link OAuth2AuthorizedClient} from an\n\t * {@link OAuth2AuthorizedClientRepository} or {@link OAuth2AuthorizedClientService}.\n\t */\n\t@FunctionalInterface\n\tpublic interface OAuth2AuthorizedClientRemover {\n\n\t\t/**\n\t\t * Removes the {@link OAuth2AuthorizedClient} associated to the provided client\n\t\t * registration identifier and End-User {@link Authentication} (Resource Owner).\n\t\t * @param clientRegistrationId the identifier for the client's registration\n\t\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t\t * @param attributes an immutable {@code Map} of (optional) attributes present\n\t\t * under certain conditions. For example, this might contain a\n\t\t * {@code jakarta.servlet.http.HttpServletRequest} and\n\t\t * {@code jakarta.servlet.http.HttpServletResponse} if the authorization was\n\t\t * performed within the context of a {@code jakarta.servlet.ServletContext}.\n\t\t */\n\t\tvoid removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\t\tMap<String, Object> attributes);\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveOAuth2AuthorizationFailureHandler} that removes an\n * {@link OAuth2AuthorizedClient} when the {@link OAuth2Error#getErrorCode()} matches one\n * of the configured {@link OAuth2ErrorCodes OAuth 2.0 error codes}.\n *\n * @author Phil Clay\n * @since 5.3\n */\npublic class RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler\n\t\timplements ReactiveOAuth2AuthorizationFailureHandler {\n\n\t/**\n\t * The default OAuth 2.0 error codes that will trigger removal of the authorized\n\t * client.\n\t * @see OAuth2ErrorCodes\n\t */\n\tpublic static final Set<String> DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES;\n\tstatic {\n\t\tSet<String> codes = new LinkedHashSet<>();\n\t\t// Returned from resource servers when an access token provided is expired,\n\t\t// revoked, malformed, or invalid for other reasons. Note that this is needed\n\t\t// because the ServerOAuth2AuthorizedClientExchangeFilterFunction delegates this\n\t\t// type of failure received from a resource server to this failure handler.\n\t\tcodes.add(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t// Returned from authorization servers when a refresh token is invalid, expired,\n\t\t// revoked, does not match the redirection URI used in the authorization request,\n\t\t// or was issued to another client.\n\t\tcodes.add(OAuth2ErrorCodes.INVALID_GRANT);\n\t\tDEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES = Collections.unmodifiableSet(codes);\n\t}\n\n\t/**\n\t * A delegate that removes an {@link OAuth2AuthorizedClient} from a\n\t * {@link ServerOAuth2AuthorizedClientRepository} or\n\t * {@link ReactiveOAuth2AuthorizedClientService} if the error code is one of the\n\t * {@link #removeAuthorizedClientErrorCodes}.\n\t */\n\tprivate final OAuth2AuthorizedClientRemover delegate;\n\n\t/**\n\t * The OAuth 2.0 Error Codes which will trigger removal of an authorized client.\n\t * @see OAuth2ErrorCodes\n\t */\n\tprivate final Set<String> removeAuthorizedClientErrorCodes;\n\n\t/**\n\t * Constructs a\n\t * {@code RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} using the\n\t * provided parameters.\n\t * @param authorizedClientRemover the {@link OAuth2AuthorizedClientRemover} used for\n\t * removing an {@link OAuth2AuthorizedClient} if the error code is one of the\n\t * {@link #DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES}.\n\t */\n\tpublic RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(\n\t\t\tOAuth2AuthorizedClientRemover authorizedClientRemover) {\n\t\tthis(authorizedClientRemover, DEFAULT_REMOVE_AUTHORIZED_CLIENT_ERROR_CODES);\n\t}\n\n\t/**\n\t * Constructs a\n\t * {@code RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} using the\n\t * provided parameters.\n\t * @param authorizedClientRemover the {@link OAuth2AuthorizedClientRemover} used for\n\t * removing an {@link OAuth2AuthorizedClient} if the error code is one of the\n\t * {@link #removeAuthorizedClientErrorCodes}.\n\t * @param removeAuthorizedClientErrorCodes the OAuth 2.0 error codes which will\n\t * trigger removal of an authorized client.\n\t * @see OAuth2ErrorCodes\n\t */\n\tpublic RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(\n\t\t\tOAuth2AuthorizedClientRemover authorizedClientRemover, Set<String> removeAuthorizedClientErrorCodes) {\n\t\tAssert.notNull(authorizedClientRemover, \"authorizedClientRemover cannot be null\");\n\t\tAssert.notNull(removeAuthorizedClientErrorCodes, \"removeAuthorizedClientErrorCodes cannot be null\");\n\t\tthis.removeAuthorizedClientErrorCodes = Collections\n\t\t\t.unmodifiableSet(new HashSet<>(removeAuthorizedClientErrorCodes));\n\t\tthis.delegate = authorizedClientRemover;\n\t}\n\n\t@Override\n\tpublic Mono<Void> onAuthorizationFailure(OAuth2AuthorizationException authorizationException,\n\t\t\tAuthentication principal, Map<String, Object> attributes) {\n\t\tif (authorizationException instanceof ClientAuthorizationException clientAuthorizationException\n\t\t\t\t&& hasRemovalErrorCode(authorizationException)) {\n\t\t\treturn this.delegate.removeAuthorizedClient(clientAuthorizationException.getClientRegistrationId(),\n\t\t\t\t\tprincipal, attributes);\n\t\t}\n\t\treturn Mono.empty();\n\t}\n\n\t/**\n\t * Returns true if the given exception has an error code that indicates that the\n\t * authorized client should be removed.\n\t * @param authorizationException the exception that caused the authorization failure\n\t * @return true if the given exception has an error code that indicates that the\n\t * authorized client should be removed.\n\t */\n\tprivate boolean hasRemovalErrorCode(OAuth2AuthorizationException authorizationException) {\n\t\treturn this.removeAuthorizedClientErrorCodes.contains(authorizationException.getError().getErrorCode());\n\t}\n\n\t/**\n\t * Removes an {@link OAuth2AuthorizedClient} from a\n\t * {@link ServerOAuth2AuthorizedClientRepository} or\n\t * {@link ReactiveOAuth2AuthorizedClientService}.\n\t */\n\t@FunctionalInterface\n\tpublic interface OAuth2AuthorizedClientRemover {\n\n\t\t/**\n\t\t * Removes the {@link OAuth2AuthorizedClient} associated to the provided client\n\t\t * registration identifier and End-User {@link Authentication} (Resource Owner).\n\t\t * @param clientRegistrationId the identifier for the client's registration\n\t\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t\t * @param attributes an immutable {@code Map} of extra optional attributes present\n\t\t * under certain conditions. For example, this might contain a\n\t\t * {@link org.springframework.web.server.ServerWebExchange ServerWebExchange} if\n\t\t * the authorization was performed within the context of a\n\t\t * {@code ServerWebExchange}.\n\t\t * @return an empty {@link Mono} that completes after this handler has finished\n\t\t * handling the event.\n\t\t */\n\t\tMono<Void> removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\t\tMap<String, Object> attributes);\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/TokenExchangeOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.RestClientTokenExchangeTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#TOKEN_EXCHANGE token-exchange} grant.\n *\n * @author Steve Riesenberg\n * @since 6.3\n * @see OAuth2AuthorizedClientProvider\n * @see RestClientTokenExchangeTokenResponseClient\n */\npublic final class TokenExchangeOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {\n\n\tprivate OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient = new RestClientTokenExchangeTokenResponseClient();\n\n\tprivate Function<OAuth2AuthorizationContext, @Nullable OAuth2Token> subjectTokenResolver = this::resolveSubjectToken;\n\n\tprivate Function<OAuth2AuthorizationContext, @Nullable OAuth2Token> actorTokenResolver = (context) -> null;\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Attempt to authorize (or re-authorize) the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * {@code context}. Returns {@code null} if authorization (or re-authorization) is not\n\t * supported, e.g. the client's {@link ClientRegistration#getAuthorizationGrantType()\n\t * authorization grant type} is not {@link AuthorizationGrantType#TOKEN_EXCHANGE\n\t * token-exchange} OR the {@link OAuth2AuthorizedClient#getAccessToken() access token}\n\t * is not expired.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if authorization is not\n\t * supported\n\t */\n\t@Override\n\tpublic @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tClientRegistration clientRegistration = context.getClientRegistration();\n\t\tif (!AuthorizationGrantType.TOKEN_EXCHANGE.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();\n\t\tif (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {\n\t\t\t// If client is already authorized but access token is NOT expired than no\n\t\t\t// need for re-authorization\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2Token subjectToken = this.subjectTokenResolver.apply(context);\n\t\tif (subjectToken == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tOAuth2Token actorToken = this.actorTokenResolver.apply(context);\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, subjectToken,\n\t\t\t\tactorToken);\n\t\tOAuth2AccessTokenResponse tokenResponse = getTokenResponse(clientRegistration, grantRequest);\n\n\t\treturn new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),\n\t\t\t\ttokenResponse.getAccessToken(), tokenResponse.getRefreshToken());\n\t}\n\n\tprivate @Nullable OAuth2Token resolveSubjectToken(OAuth2AuthorizationContext context) {\n\t\tif (context.getPrincipal().getPrincipal() instanceof OAuth2Token accessToken) {\n\t\t\treturn accessToken;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate OAuth2AccessTokenResponse getTokenResponse(ClientRegistration clientRegistration,\n\t\t\tTokenExchangeGrantRequest tokenExchangeGrantRequest) {\n\t\ttry {\n\t\t\treturn this.accessTokenResponseClient.getTokenResponse(tokenExchangeGrantRequest);\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tthrow new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex);\n\t\t}\n\t}\n\n\tprivate boolean hasTokenExpired(OAuth2Token token) {\n\t\tInstant expiresAt = token.getExpiresAt();\n\t\treturn expiresAt != null && this.clock.instant().isAfter(expiresAt.minus(this.clockSkew));\n\t}\n\n\t/**\n\t * Sets the client used when requesting an access token credential at the Token\n\t * Endpoint for the {@code token-exchange} grant.\n\t * @param accessTokenResponseClient the client used when requesting an access token\n\t * credential at the Token Endpoint for the {@code token-exchange} grant\n\t */\n\tpublic void setAccessTokenResponseClient(\n\t\t\tOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t/**\n\t * Sets the resolver used for resolving the {@link OAuth2Token subject token}.\n\t * @param subjectTokenResolver the resolver used for resolving the {@link OAuth2Token\n\t * subject token}\n\t */\n\tpublic void setSubjectTokenResolver(\n\t\t\tFunction<OAuth2AuthorizationContext, @Nullable OAuth2Token> subjectTokenResolver) {\n\t\tAssert.notNull(subjectTokenResolver, \"subjectTokenResolver cannot be null\");\n\t\tthis.subjectTokenResolver = subjectTokenResolver;\n\t}\n\n\t/**\n\t * Sets the resolver used for resolving the {@link OAuth2Token actor token}.\n\t * @param actorTokenResolver the resolver used for resolving the {@link OAuth2Token\n\t * actor token}\n\t */\n\tpublic void setActorTokenResolver(Function<OAuth2AuthorizationContext, @Nullable OAuth2Token> actorTokenResolver) {\n\t\tAssert.notNull(actorTokenResolver, \"actorTokenResolver cannot be null\");\n\t\tthis.actorTokenResolver = actorTokenResolver;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is\n\t * 60 seconds.\n\t *\n\t * <p>\n\t * An access token is considered expired if\n\t * {@code OAuth2AccessToken#getExpiresAt() - clockSkew} is before the current time\n\t * {@code clock#instant()}.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access\n\t * token expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/TokenExchangeReactiveOAuth2AuthorizedClientProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.WebClientReactiveTokenExchangeTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link ReactiveOAuth2AuthorizedClientProvider} for the\n * {@link AuthorizationGrantType#TOKEN_EXCHANGE token-exchange} grant.\n *\n * @author Steve Riesenberg\n * @since 6.3\n * @see ReactiveOAuth2AuthorizedClientProvider\n * @see WebClientReactiveTokenExchangeTokenResponseClient\n */\npublic final class TokenExchangeReactiveOAuth2AuthorizedClientProvider\n\t\timplements ReactiveOAuth2AuthorizedClientProvider {\n\n\tprivate ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient = new WebClientReactiveTokenExchangeTokenResponseClient();\n\n\tprivate Function<OAuth2AuthorizationContext, Mono<OAuth2Token>> subjectTokenResolver = this::resolveSubjectToken;\n\n\tprivate Function<OAuth2AuthorizationContext, Mono<OAuth2Token>> actorTokenResolver = (context) -> Mono.empty();\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Attempt to authorize (or re-authorize) the\n\t * {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided\n\t * {@code context}. Returns an empty {@code Mono} if authorization (or\n\t * re-authorization) is not supported, e.g. the client's\n\t * {@link ClientRegistration#getAuthorizationGrantType() authorization grant type} is\n\t * not {@link AuthorizationGrantType#TOKEN_EXCHANGE token-exchange} OR the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} is not expired.\n\t * @param context the context that holds authorization-specific state for the client\n\t * @return the {@link OAuth2AuthorizedClient} or an empty {@code Mono} if\n\t * authorization is not supported\n\t */\n\t@Override\n\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {\n\t\tAssert.notNull(context, \"context cannot be null\");\n\t\tClientRegistration clientRegistration = context.getClientRegistration();\n\t\tif (!AuthorizationGrantType.TOKEN_EXCHANGE.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tOAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();\n\t\tif (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {\n\t\t\t// If client is already authorized but access token is NOT expired than no\n\t\t\t// need for re-authorization\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\treturn this.subjectTokenResolver.apply(context)\n\t\t\t.flatMap((subjectToken) -> this.actorTokenResolver.apply(context)\n\t\t\t\t.map((actorToken) -> new TokenExchangeGrantRequest(clientRegistration, subjectToken, actorToken))\n\t\t\t\t.switchIfEmpty(Mono.just(new TokenExchangeGrantRequest(clientRegistration, subjectToken, null))))\n\t\t\t.flatMap(this.accessTokenResponseClient::getTokenResponse)\n\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t(ex) -> new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex))\n\t\t\t.map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),\n\t\t\t\t\ttokenResponse.getAccessToken(), tokenResponse.getRefreshToken()));\n\t}\n\n\tprivate Mono<OAuth2Token> resolveSubjectToken(OAuth2AuthorizationContext context) {\n\t\t// @formatter:off\n\t\treturn Mono.just(context)\n\t\t\t\t.flatMap((ctx) -> Mono.justOrEmpty(ctx.getPrincipal())\n\t\t\t\t\t\t.flatMap((auth) -> Mono.justOrEmpty(auth.getPrincipal())))\n\t\t\t\t.filter((principal) -> principal instanceof OAuth2Token)\n\t\t\t\t.cast(OAuth2Token.class);\n\t\t// @formatter:on\n\t}\n\n\tprivate boolean hasTokenExpired(OAuth2Token token) {\n\t\tInstant expiresAt = token.getExpiresAt();\n\t\treturn expiresAt != null && this.clock.instant().isAfter(expiresAt.minus(this.clockSkew));\n\t}\n\n\t/**\n\t * Sets the client used when requesting an access token credential at the Token\n\t * Endpoint for the {@code token-exchange} grant.\n\t * @param accessTokenResponseClient the client used when requesting an access token\n\t * credential at the Token Endpoint for the {@code token-exchange} grant\n\t */\n\tpublic void setAccessTokenResponseClient(\n\t\t\tReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t/**\n\t * Sets the resolver used for resolving the {@link OAuth2Token subject token}.\n\t * @param subjectTokenResolver the resolver used for resolving the {@link OAuth2Token\n\t * subject token}\n\t */\n\tpublic void setSubjectTokenResolver(Function<OAuth2AuthorizationContext, Mono<OAuth2Token>> subjectTokenResolver) {\n\t\tAssert.notNull(subjectTokenResolver, \"subjectTokenResolver cannot be null\");\n\t\tthis.subjectTokenResolver = subjectTokenResolver;\n\t}\n\n\t/**\n\t * Sets the resolver used for resolving the {@link OAuth2Token actor token}.\n\t * @param actorTokenResolver the resolver used for resolving the {@link OAuth2Token\n\t * actor token}\n\t */\n\tpublic void setActorTokenResolver(Function<OAuth2AuthorizationContext, Mono<OAuth2Token>> actorTokenResolver) {\n\t\tAssert.notNull(actorTokenResolver, \"actorTokenResolver cannot be null\");\n\t\tthis.actorTokenResolver = actorTokenResolver;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is\n\t * 60 seconds.\n\t *\n\t * <p>\n\t * An access token is considered expired if\n\t * {@code OAuth2AccessToken#getExpiresAt() - clockSkew} is before the current time\n\t * {@code clock#instant()}.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access\n\t * token expiry.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/annotation/ClientRegistrationId.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.core.annotation.AliasFor;\n\n/**\n * This annotation can be added to the method of an interface based HTTP client created\n * using {@link org.springframework.web.service.invoker.HttpServiceProxyFactory} to\n * automatically associate an OAuth token with the request.\n *\n * @author Rob Winch\n * @since 7.0\n * @see org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface ClientRegistrationId {\n\n\t/**\n\t * Sets the client registration identifier.\n\t * @return the client registration identifier\n\t */\n\t@AliasFor(\"value\")\n\tString registrationId() default \"\";\n\n\t/**\n\t * The default attribute for this annotation. This is an alias for\n\t * {@link #registrationId()}. For example,\n\t * {@code @RegisteredOAuth2AuthorizedClient(\"login-client\")} is equivalent to\n\t * {@code @RegisteredOAuth2AuthorizedClient(registrationId=\"login-client\")}.\n\t * @return the client registration identifier\n\t */\n\t@AliasFor(\"registrationId\")\n\tString value() default \"\";\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/annotation/RegisteredOAuth2AuthorizedClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.web.method.annotation.OAuth2AuthorizedClientArgumentResolver;\n\n/**\n * This annotation may be used to resolve a method parameter to an argument value of type\n * {@link OAuth2AuthorizedClient}.\n *\n * <p>\n * For example: <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;GetMapping(\"/authorized-client\")\n *     public String authorizedClient(@RegisteredOAuth2AuthorizedClient(\"login-client\") OAuth2AuthorizedClient authorizedClient) {\n *         // do something with authorizedClient\n *     }\n * }\n * </pre>\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AuthorizedClientArgumentResolver\n */\n@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface RegisteredOAuth2AuthorizedClient {\n\n\t/**\n\t * Sets the client registration identifier.\n\t * @return the client registration identifier\n\t */\n\t@AliasFor(\"value\")\n\tString registrationId() default \"\";\n\n\t/**\n\t * The default attribute for this annotation. This is an alias for\n\t * {@link #registrationId()}. For example,\n\t * {@code @RegisteredOAuth2AuthorizedClient(\"login-client\")} is equivalent to\n\t * {@code @RegisteredOAuth2AuthorizedClient(registrationId=\"login-client\")}.\n\t * @return the client registration identifier\n\t */\n\t@AliasFor(\"registrationId\")\n\tString value() default \"\";\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Annotations for OAuth2 Client (e.g. method parameters).\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/aot/hint/OAuth2ClientRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.aot.hint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.util.ClassUtils;\n\n/**\n * {@link RuntimeHintsRegistrar} for OAuth2 Client\n *\n * @author Marcus Da Coregio\n * @since 6.0\n */\nclass OAuth2ClientRuntimeHints implements RuntimeHintsRegistrar {\n\n\tprivate static final boolean r2dbcPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = ClassUtils.getDefaultClassLoader();\n\t\tr2dbcPresent = ClassUtils.isPresent(\"io.r2dbc.spi.Row\", classLoader)\n\t\t\t\t&& ClassUtils.isPresent(\"org.springframework.r2dbc.core.DatabaseClient\", classLoader);\n\t}\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\tregisterOAuth2ClientSchemaFilesHints(hints);\n\t\tif (r2dbcPresent) {\n\t\t\tregisterR2dbcHints(hints);\n\t\t}\n\t}\n\n\tprivate void registerOAuth2ClientSchemaFilesHints(RuntimeHints hints) {\n\t\thints.resources()\n\t\t\t.registerPattern(\"org/springframework/security/oauth2/client/oauth2-client-schema.sql\")\n\t\t\t.registerPattern(\"org/springframework/security/oauth2/client/oauth2-client-schema-postgres.sql\");\n\t}\n\n\tprivate void registerR2dbcHints(RuntimeHints hints) {\n\t\t// Register R2DBC OAuth2 client service types\n\t\thints.reflection()\n\t\t\t.registerType(\n\t\t\t\t\tTypeReference\n\t\t\t\t\t\t.of(\"org.springframework.security.oauth2.client.R2dbcReactiveOAuth2AuthorizedClientService\"),\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));\n\n\t\t// Register OAuth2 client types that may be serialized in R2DBC scenarios\n\t\thints.reflection()\n\t\t\t.registerTypes(java.util.List.of(\n\t\t\t\t\tTypeReference.of(\"org.springframework.security.oauth2.client.OAuth2AuthorizedClient\"),\n\t\t\t\t\tTypeReference\n\t\t\t\t\t\t.of(\"org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken\")),\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Runtime hints for OAuth2 Client AOT support.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AbstractAuthenticationToken} that represents an OAuth\n * 2.0 {@link Authentication}.\n * <p>\n * The {@link Authentication} associates an {@link OAuth2User} {@code Principal} to the\n * identifier of the {@link #getAuthorizedClientRegistrationId() Authorized Client}, which\n * the End-User ({@code Principal}) granted authorization to so that it can access its\n * protected resources at the UserInfo Endpoint.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AbstractAuthenticationToken\n * @see OAuth2User\n * @see OAuth2AuthorizedClient\n */\npublic class OAuth2AuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final OAuth2User principal;\n\n\tprivate final String authorizedClientRegistrationId;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthenticationToken} using the provided parameters.\n\t * @param principal the user {@code Principal} registered with the OAuth 2.0 Provider\n\t * @param authorities the authorities granted to the user\n\t * @param authorizedClientRegistrationId the registration identifier of the\n\t * {@link OAuth2AuthorizedClient Authorized Client}\n\t */\n\tpublic OAuth2AuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities,\n\t\t\tString authorizedClientRegistrationId) {\n\t\tsuper(authorities);\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.hasText(authorizedClientRegistrationId, \"authorizedClientRegistrationId cannot be empty\");\n\t\tthis.principal = principal;\n\t\tthis.authorizedClientRegistrationId = authorizedClientRegistrationId;\n\t\tthis.setAuthenticated(true);\n\t}\n\n\tprotected OAuth2AuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tAssert.notNull(builder.principal, \"principal cannot be null\");\n\t\tAssert.hasText(builder.authorizedClientRegistrationId, \"authorizedClientRegistrationId cannot be empty\");\n\t\tthis.principal = builder.principal;\n\t\tthis.authorizedClientRegistrationId = builder.authorizedClientRegistrationId;\n\t}\n\n\t@Override\n\tpublic OAuth2User getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\t// Credentials are never exposed (by the Provider) for an OAuth2 User\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the registration identifier of the {@link OAuth2AuthorizedClient Authorized\n\t * Client}.\n\t * @return the registration identifier of the Authorized Client.\n\t */\n\tpublic String getAuthorizedClientRegistrationId() {\n\t\treturn this.authorizedClientRegistrationId;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link OAuth2AuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate OAuth2User principal;\n\n\t\tprivate String authorizedClientRegistrationId;\n\n\t\tprotected Builder(OAuth2AuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.principal;\n\t\t\tthis.authorizedClientRegistrationId = token.authorizedClientRegistrationId;\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.isInstanceOf(OAuth2User.class, principal, \"principal must be of type OAuth2User\");\n\t\t\tthis.principal = (OAuth2User) principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Use this\n\t\t * {@link org.springframework.security.oauth2.client.registration.ClientRegistration}\n\t\t * {@code registrationId}.\n\t\t * @param authorizedClientRegistrationId the registration id to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t * @see OAuth2AuthenticationToken#getAuthorizedClientRegistrationId\n\t\t */\n\t\tpublic B authorizedClientRegistrationId(String authorizedClientRegistrationId) {\n\t\t\tthis.authorizedClientRegistrationId = authorizedClientRegistrationId;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2AuthenticationToken build() {\n\t\t\treturn new OAuth2AuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Objects;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AuthenticationProvider} for the OAuth 2.0 Authorization\n * Code Grant.\n *\n * <p>\n * This {@link AuthenticationProvider} is responsible for authenticating an Authorization\n * Code credential with the Authorization Server's Token Endpoint and if valid, exchanging\n * it for an Access Token credential.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AuthorizationCodeAuthenticationToken\n * @see OAuth2AccessTokenResponseClient\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant Flow</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token\n * Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token\n * Response</a>\n */\npublic class OAuth2AuthorizationCodeAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String INVALID_STATE_PARAMETER_ERROR_CODE = \"invalid_state_parameter\";\n\n\tprivate final OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param accessTokenResponseClient the client used for requesting the access token\n\t * credential from the Token Endpoint\n\t */\n\tpublic OAuth2AuthorizationCodeAuthenticationProvider(\n\t\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication;\n\t\tOAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationResponse();\n\t\tif (authorizationResponse.statusError()) {\n\t\t\tOAuth2Error error = authorizationResponse.getError();\n\t\t\tAssert.notNull(error, \"error cannot be null when status is error\");\n\t\t\tthrow new OAuth2AuthorizationException(error);\n\t\t}\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest();\n\t\tif (!Objects.equals(authorizationResponse.getState(), authorizationRequest.getState())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);\n\t\t\tthrow new OAuth2AuthorizationException(oauth2Error);\n\t\t}\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseClient.getTokenResponse(\n\t\t\t\tnew OAuth2AuthorizationCodeGrantRequest(authorizationCodeAuthentication.getClientRegistration(),\n\t\t\t\t\t\tauthorizationCodeAuthentication.getAuthorizationExchange()));\n\t\tOAuth2AuthorizationCodeAuthenticationToken authenticationResult = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tauthorizationCodeAuthentication.getClientRegistration(),\n\t\t\t\tauthorizationCodeAuthentication.getAuthorizationExchange(), accessTokenResponse.getAccessToken(),\n\t\t\t\taccessTokenResponse.getRefreshToken(), accessTokenResponse.getAdditionalParameters());\n\t\tauthenticationResult.setDetails(authorizationCodeAuthentication.getDetails());\n\t\treturn authenticationResult;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AbstractAuthenticationToken} for the OAuth 2.0 Authorization Code Grant.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see AbstractAuthenticationToken\n * @see ClientRegistration\n * @see OAuth2AuthorizationExchange\n * @see OAuth2AccessToken\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant Flow</a>\n */\npublic class OAuth2AuthorizationCodeAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate Map<String, Object> additionalParameters = new HashMap<>();\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizationExchange authorizationExchange;\n\n\tprivate @Nullable OAuth2AccessToken accessToken;\n\n\tprivate @Nullable OAuth2RefreshToken refreshToken;\n\n\t/**\n\t * This constructor should be used when the Authorization Request/Response is\n\t * complete.\n\t * @param clientRegistration the client registration\n\t * @param authorizationExchange the authorization exchange\n\t */\n\tpublic OAuth2AuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthorizationExchange authorizationExchange) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tAssert.notNull(authorizationExchange, \"authorizationExchange cannot be null\");\n\t\tthis.clientRegistration = clientRegistration;\n\t\tthis.authorizationExchange = authorizationExchange;\n\t}\n\n\t/**\n\t * This constructor should be used when the Access Token Request/Response is complete,\n\t * which indicates that the Authorization Code Grant flow has fully completed.\n\t * @param clientRegistration the client registration\n\t * @param authorizationExchange the authorization exchange\n\t * @param accessToken the access token credential\n\t */\n\tpublic OAuth2AuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthorizationExchange authorizationExchange, OAuth2AccessToken accessToken) {\n\t\tthis(clientRegistration, authorizationExchange, accessToken, null);\n\t}\n\n\t/**\n\t * This constructor should be used when the Access Token Request/Response is complete,\n\t * which indicates that the Authorization Code Grant flow has fully completed.\n\t * @param clientRegistration the client registration\n\t * @param authorizationExchange the authorization exchange\n\t * @param accessToken the access token credential\n\t * @param refreshToken the refresh token credential\n\t */\n\tpublic OAuth2AuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthorizationExchange authorizationExchange, OAuth2AccessToken accessToken,\n\t\t\t@Nullable OAuth2RefreshToken refreshToken) {\n\t\tthis(clientRegistration, authorizationExchange, accessToken, refreshToken, Collections.emptyMap());\n\t}\n\n\tpublic OAuth2AuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthorizationExchange authorizationExchange, OAuth2AccessToken accessToken,\n\t\t\t@Nullable OAuth2RefreshToken refreshToken, Map<String, Object> additionalParameters) {\n\t\tthis(clientRegistration, authorizationExchange);\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tthis.accessToken = accessToken;\n\t\tthis.refreshToken = refreshToken;\n\t\tthis.setAuthenticated(true);\n\t\tthis.additionalParameters.putAll(additionalParameters);\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.clientRegistration.getClientId();\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn (this.accessToken != null) ? this.accessToken.getTokenValue()\n\t\t\t\t: this.authorizationExchange.getAuthorizationResponse().getCode();\n\t}\n\n\t/**\n\t * Returns the {@link ClientRegistration client registration}.\n\t * @return the {@link ClientRegistration}\n\t */\n\tpublic ClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationExchange authorization exchange}.\n\t * @return the {@link OAuth2AuthorizationExchange}\n\t */\n\tpublic OAuth2AuthorizationExchange getAuthorizationExchange() {\n\t\treturn this.authorizationExchange;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessToken access token}.\n\t * @return the {@link OAuth2AccessToken}\n\t */\n\tpublic @Nullable OAuth2AccessToken getAccessToken() {\n\t\treturn this.accessToken;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2RefreshToken refresh token}.\n\t * @return the {@link OAuth2RefreshToken}\n\t */\n\tpublic @Nullable OAuth2RefreshToken getRefreshToken() {\n\t\treturn this.refreshToken;\n\t}\n\n\t/**\n\t * Returns the additional parameters\n\t * @return the additional parameters\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Objects;\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an\n * {@link org.springframework.security.authentication.AuthenticationProvider} for OAuth\n * 2.0 Login, which leverages the OAuth 2.0 Authorization Code Grant Flow.\n *\n * This {@link org.springframework.security.authentication.AuthenticationProvider} is\n * responsible for authenticating an Authorization Code credential with the Authorization\n * Server's Token Endpoint and if valid, exchanging it for an Access Token credential.\n * <p>\n * It will also obtain the user attributes of the End-User (Resource Owner) from the\n * UserInfo Endpoint using an\n * {@link org.springframework.security.oauth2.client.userinfo.OAuth2UserService}, which\n * will create a {@code Principal} in the form of an {@link OAuth2User}. The\n * {@code OAuth2User} is then associated to the {@link OAuth2LoginAuthenticationToken} to\n * complete the authentication.\n *\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2LoginAuthenticationToken\n * @see ReactiveOAuth2AccessTokenResponseClient\n * @see ReactiveOAuth2UserService\n * @see OAuth2User\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant Flow</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token\n * Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token\n * Response</a>\n */\npublic class OAuth2AuthorizationCodeReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate static final String INVALID_STATE_PARAMETER_ERROR_CODE = \"invalid_state_parameter\";\n\n\tprivate final ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\tpublic OAuth2AuthorizationCodeReactiveAuthenticationManager(\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\treturn Mono.defer(() -> {\n\t\t\tOAuth2AuthorizationCodeAuthenticationToken token = (OAuth2AuthorizationCodeAuthenticationToken) authentication;\n\t\t\tOAuth2AuthorizationResponse authorizationResponse = token.getAuthorizationExchange()\n\t\t\t\t.getAuthorizationResponse();\n\t\t\tif (authorizationResponse.statusError()) {\n\t\t\t\tOAuth2Error error = authorizationResponse.getError();\n\t\t\t\tAssert.notNull(error, \"error cannot be null when status is error\");\n\t\t\t\treturn Mono.error(new OAuth2AuthorizationException(error));\n\t\t\t}\n\t\t\tOAuth2AuthorizationRequest authorizationRequest = token.getAuthorizationExchange()\n\t\t\t\t.getAuthorizationRequest();\n\t\t\tif (!Objects.equals(authorizationResponse.getState(), authorizationRequest.getState())) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);\n\t\t\t\treturn Mono.error(new OAuth2AuthorizationException(oauth2Error));\n\t\t\t}\n\t\t\tOAuth2AuthorizationCodeGrantRequest authzRequest = new OAuth2AuthorizationCodeGrantRequest(\n\t\t\t\t\ttoken.getClientRegistration(), token.getAuthorizationExchange());\n\t\t\treturn this.accessTokenResponseClient.getTokenResponse(authzRequest).map(onSuccess(token));\n\t\t});\n\t}\n\n\tprivate Function<OAuth2AccessTokenResponse, OAuth2AuthorizationCodeAuthenticationToken> onSuccess(\n\t\t\tOAuth2AuthorizationCodeAuthenticationToken token) {\n\t\treturn (accessTokenResponse) -> {\n\t\t\tClientRegistration registration = token.getClientRegistration();\n\t\t\tOAuth2AuthorizationExchange exchange = token.getAuthorizationExchange();\n\t\t\tOAuth2AccessToken accessToken = accessTokenResponse.getAccessToken();\n\t\t\tOAuth2RefreshToken refreshToken = accessTokenResponse.getRefreshToken();\n\t\t\treturn new OAuth2AuthorizationCodeAuthenticationToken(registration, exchange, accessToken, refreshToken,\n\t\t\t\t\taccessTokenResponse.getAdditionalParameters());\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AuthenticationProvider} for OAuth 2.0 Login, which\n * leverages the OAuth 2.0 Authorization Code Grant Flow.\n *\n * This {@link AuthenticationProvider} is responsible for authenticating an Authorization\n * Code credential with the Authorization Server's Token Endpoint and if valid, exchanging\n * it for an Access Token credential.\n * <p>\n * It will also obtain the user attributes of the End-User (Resource Owner) from the\n * UserInfo Endpoint using an {@link OAuth2UserService}, which will create a\n * {@code Principal} in the form of an {@link OAuth2User}. The {@code OAuth2User} is then\n * associated to the {@link OAuth2LoginAuthenticationToken} to complete the\n * authentication.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2LoginAuthenticationToken\n * @see OAuth2AccessTokenResponseClient\n * @see OAuth2UserService\n * @see OAuth2User\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant Flow</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token\n * Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token\n * Response</a>\n */\npublic class OAuth2LoginAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY;\n\n\tprivate final OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider;\n\n\tprivate final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = ((authorities) -> authorities);\n\n\t/**\n\t * Constructs an {@code OAuth2LoginAuthenticationProvider} using the provided\n\t * parameters.\n\t * @param accessTokenResponseClient the client used for requesting the access token\n\t * credential from the Token Endpoint\n\t * @param userService the service used for obtaining the user attributes of the\n\t * End-User from the UserInfo Endpoint\n\t */\n\tpublic OAuth2LoginAuthenticationProvider(\n\t\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,\n\t\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> userService) {\n\t\tAssert.notNull(userService, \"userService cannot be null\");\n\t\tthis.authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(\n\t\t\t\taccessTokenResponseClient);\n\t\tthis.userService = userService;\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2LoginAuthenticationToken loginAuthenticationToken = (OAuth2LoginAuthenticationToken) authentication;\n\t\t// Section 3.1.2.1 Authentication Request -\n\t\t// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest scope\n\t\t// REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope value.\n\t\tif (loginAuthenticationToken.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest()\n\t\t\t.getScopes()\n\t\t\t.contains(\"openid\")) {\n\t\t\t// This is an OpenID Connect Authentication Request so return null\n\t\t\t// and let OidcAuthorizationCodeAuthenticationProvider handle it instead\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken;\n\t\ttry {\n\t\t\tauthorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) this.authorizationCodeAuthenticationProvider\n\t\t\t\t.authenticate(\n\t\t\t\t\t\tnew OAuth2AuthorizationCodeAuthenticationToken(loginAuthenticationToken.getClientRegistration(),\n\t\t\t\t\t\t\t\tloginAuthenticationToken.getAuthorizationExchange()));\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tOAuth2Error oauth2Error = ex.getError();\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);\n\t\t}\n\t\tOAuth2AccessToken accessToken = authorizationCodeAuthenticationToken.getAccessToken();\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tMap<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();\n\t\tOAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(\n\t\t\t\tloginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));\n\t\tAssert.notNull(oauth2User, \"oauth2User cannot be null\");\n\t\tCollection<GrantedAuthority> authorities = new HashSet<>(oauth2User.getAuthorities());\n\t\tCollection<GrantedAuthority> mappedAuthorities = new LinkedHashSet<>(\n\t\t\t\tthis.authoritiesMapper.mapAuthorities(authorities));\n\t\tmappedAuthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\tOAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(\n\t\t\t\tloginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(),\n\t\t\t\toauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());\n\t\tauthenticationResult.setDetails(loginAuthenticationToken.getDetails());\n\t\treturn authenticationResult;\n\t}\n\n\t/**\n\t * Sets the {@link GrantedAuthoritiesMapper} used for mapping\n\t * {@link OAuth2User#getAuthorities()} to a new set of authorities which will be\n\t * associated to the {@link OAuth2LoginAuthenticationToken}.\n\t * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the\n\t * user's authorities\n\t */\n\tpublic final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tAssert.notNull(authoritiesMapper, \"authoritiesMapper cannot be null\");\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AbstractAuthenticationToken} for OAuth 2.0 Login, which leverages the OAuth\n * 2.0 Authorization Code Grant Flow.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AbstractAuthenticationToken\n * @see OAuth2User\n * @see ClientRegistration\n * @see OAuth2AuthorizationExchange\n * @see OAuth2AccessToken\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant Flow</a>\n */\npublic class OAuth2LoginAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate @Nullable OAuth2User principal;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizationExchange authorizationExchange;\n\n\tprivate @Nullable OAuth2AccessToken accessToken;\n\n\tprivate @Nullable OAuth2RefreshToken refreshToken;\n\n\t/**\n\t * This constructor should be used when the Authorization Request/Response is\n\t * complete.\n\t * @param clientRegistration the client registration\n\t * @param authorizationExchange the authorization exchange\n\t */\n\tpublic OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthorizationExchange authorizationExchange) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tAssert.notNull(authorizationExchange, \"authorizationExchange cannot be null\");\n\t\tthis.clientRegistration = clientRegistration;\n\t\tthis.authorizationExchange = authorizationExchange;\n\t\tthis.setAuthenticated(false);\n\t}\n\n\t/**\n\t * This constructor should be used when the Access Token Request/Response is complete,\n\t * which indicates that the Authorization Code Grant flow has fully completed and\n\t * OAuth 2.0 Login has been achieved.\n\t * @param clientRegistration the client registration\n\t * @param authorizationExchange the authorization exchange\n\t * @param principal the user {@code Principal} registered with the OAuth 2.0 Provider\n\t * @param authorities the authorities granted to the user\n\t * @param accessToken the access token credential\n\t */\n\tpublic OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthorizationExchange authorizationExchange, OAuth2User principal,\n\t\t\tCollection<? extends GrantedAuthority> authorities, OAuth2AccessToken accessToken) {\n\t\tthis(clientRegistration, authorizationExchange, principal, authorities, accessToken, null);\n\t}\n\n\t/**\n\t * This constructor should be used when the Access Token Request/Response is complete,\n\t * which indicates that the Authorization Code Grant flow has fully completed and\n\t * OAuth 2.0 Login has been achieved.\n\t * @param clientRegistration the client registration\n\t * @param authorizationExchange the authorization exchange\n\t * @param principal the user {@code Principal} registered with the OAuth 2.0 Provider\n\t * @param authorities the authorities granted to the user\n\t * @param accessToken the access token credential\n\t * @param refreshToken the refresh token credential\n\t */\n\tpublic OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthorizationExchange authorizationExchange, OAuth2User principal,\n\t\t\tCollection<? extends GrantedAuthority> authorities, OAuth2AccessToken accessToken,\n\t\t\t@Nullable OAuth2RefreshToken refreshToken) {\n\t\tsuper(authorities);\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tAssert.notNull(authorizationExchange, \"authorizationExchange cannot be null\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tthis.clientRegistration = clientRegistration;\n\t\tthis.authorizationExchange = authorizationExchange;\n\t\tthis.principal = principal;\n\t\tthis.accessToken = accessToken;\n\t\tthis.refreshToken = refreshToken;\n\t\tthis.setAuthenticated(true);\n\t}\n\n\t@Override\n\tpublic @Nullable OAuth2User getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * Returns the {@link ClientRegistration client registration}.\n\t * @return the {@link ClientRegistration}\n\t */\n\tpublic ClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationExchange authorization exchange}.\n\t * @return the {@link OAuth2AuthorizationExchange}\n\t */\n\tpublic OAuth2AuthorizationExchange getAuthorizationExchange() {\n\t\treturn this.authorizationExchange;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessToken access token}.\n\t * @return the {@link OAuth2AccessToken}\n\t */\n\tpublic @Nullable OAuth2AccessToken getAccessToken() {\n\t\treturn this.accessToken;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2RefreshToken refresh token}.\n\t * @return the {@link OAuth2RefreshToken}\n\t * @since 5.1\n\t */\n\tpublic @Nullable OAuth2RefreshToken getRefreshToken() {\n\t\treturn this.refreshToken;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an\n * {@link org.springframework.security.authentication.AuthenticationProvider} for OAuth\n * 2.0 Login, which leverages the OAuth 2.0 Authorization Code Grant Flow.\n *\n * This {@link org.springframework.security.authentication.AuthenticationProvider} is\n * responsible for authenticating an Authorization Code credential with the Authorization\n * Server's Token Endpoint and if valid, exchanging it for an Access Token credential.\n * <p>\n * It will also obtain the user attributes of the End-User (Resource Owner) from the\n * UserInfo Endpoint using an\n * {@link org.springframework.security.oauth2.client.userinfo.OAuth2UserService}, which\n * will create a {@code Principal} in the form of an {@link OAuth2User}. The\n * {@code OAuth2User} is then associated to the {@link OAuth2LoginAuthenticationToken} to\n * complete the authentication.\n *\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2LoginAuthenticationToken\n * @see org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient\n * @see org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService\n * @see OAuth2User\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant Flow</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token\n * Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token\n * Response</a>\n */\npublic class OAuth2LoginReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate final ReactiveAuthenticationManager authorizationCodeManager;\n\n\tprivate final ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService;\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = ((authorities) -> authorities);\n\n\tpublic OAuth2LoginReactiveAuthenticationManager(\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,\n\t\t\tReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tAssert.notNull(userService, \"userService cannot be null\");\n\t\tthis.authorizationCodeManager = new OAuth2AuthorizationCodeReactiveAuthenticationManager(\n\t\t\t\taccessTokenResponseClient);\n\t\tthis.userService = userService;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\treturn Mono.defer(() -> {\n\t\t\tOAuth2AuthorizationCodeAuthenticationToken token = (OAuth2AuthorizationCodeAuthenticationToken) authentication;\n\t\t\t// Section 3.1.2.1 Authentication Request -\n\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest scope\n\t\t\t// REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope value.\n\t\t\tif (token.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains(\"openid\")) {\n\t\t\t\t// This is an OpenID Connect Authentication Request so return null\n\t\t\t\t// and let OidcAuthorizationCodeReactiveAuthenticationManager handle it\n\t\t\t\t// instead once one is created\n\t\t\t\treturn Mono.empty();\n\t\t\t}\n\t\t\treturn this.authorizationCodeManager.authenticate(token)\n\t\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t\t(e) -> new OAuth2AuthenticationException(e.getError(), e.getError().toString(), e))\n\t\t\t\t.cast(OAuth2AuthorizationCodeAuthenticationToken.class)\n\t\t\t\t.flatMap(this::onSuccess);\n\t\t});\n\t}\n\n\t/**\n\t * Sets the {@link GrantedAuthoritiesMapper} used for mapping\n\t * {@link OAuth2User#getAuthorities()} to a new set of authorities which will be\n\t * associated to the {@link OAuth2LoginAuthenticationToken}.\n\t * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the\n\t * user's authorities\n\t * @since 5.4\n\t */\n\tpublic final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tAssert.notNull(authoritiesMapper, \"authoritiesMapper cannot be null\");\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\tprivate Mono<OAuth2LoginAuthenticationToken> onSuccess(OAuth2AuthorizationCodeAuthenticationToken authentication) {\n\t\tOAuth2AccessToken accessToken = authentication.getAccessToken();\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tMap<String, Object> additionalParameters = authentication.getAdditionalParameters();\n\t\tOAuth2UserRequest userRequest = new OAuth2UserRequest(authentication.getClientRegistration(), accessToken,\n\t\t\t\tadditionalParameters);\n\t\treturn this.userService.loadUser(userRequest).map((oauth2User) -> {\n\t\t\tCollection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper\n\t\t\t\t.mapAuthorities(oauth2User.getAuthorities());\n\t\t\tOAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(\n\t\t\t\t\tauthentication.getClientRegistration(), authentication.getAuthorizationExchange(), oauth2User,\n\t\t\t\t\tmappedAuthorities, accessToken, authentication.getRefreshToken());\n\t\t\treturn authenticationResult;\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes and interfaces for authenticating and authorizing a client with an\n * OAuth 2.0 Authorization Server using a specific authorization grant flow.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractOAuth2AuthorizationGrantRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\n\n/**\n * Base implementation of an OAuth 2.0 Authorization Grant request that holds an\n * authorization grant credential and is used when initiating a request to the\n * Authorization Server's Token Endpoint.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AuthorizationGrantType\n * @see ClientRegistration\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.3\">Section\n * 1.3 Authorization Grant</a>\n */\npublic abstract class AbstractOAuth2AuthorizationGrantRequest {\n\n\tprivate final AuthorizationGrantType authorizationGrantType;\n\n\tprivate final ClientRegistration clientRegistration;\n\n\t/**\n\t * Sub-class constructor.\n\t * @param authorizationGrantType the authorization grant type\n\t * @param clientRegistration the client registration\n\t * @since 5.5\n\t */\n\tprotected AbstractOAuth2AuthorizationGrantRequest(AuthorizationGrantType authorizationGrantType,\n\t\t\tClientRegistration clientRegistration) {\n\t\tAssert.notNull(authorizationGrantType, \"authorizationGrantType cannot be null\");\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tthis.authorizationGrantType = authorizationGrantType;\n\t\tthis.clientRegistration = clientRegistration;\n\t}\n\n\t/**\n\t * Returns the authorization grant type.\n\t * @return the authorization grant type\n\t */\n\tpublic AuthorizationGrantType getGrantType() {\n\t\treturn this.authorizationGrantType;\n\t}\n\n\t/**\n\t * Returns the {@link ClientRegistration client registration}.\n\t * @return the {@link ClientRegistration}\n\t * @since 5.5\n\t */\n\tpublic ClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractRestClientOAuth2AccessTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.util.function.Consumer;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.converter.FormHttpMessageConverter;\nimport org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.RestClient;\nimport org.springframework.web.client.RestClient.RequestHeadersSpec;\nimport org.springframework.web.client.RestClientException;\n\n/**\n * Abstract base class for {@link RestClient}-based implementations of\n * {@link OAuth2AccessTokenResponseClient} that communicate to the Authorization Server's\n * Token Endpoint.\n * <p>\n * Submits a form request body specific to the type of grant request and accepts a JSON\n * response body containing an OAuth 2.0 Access Token Response or OAuth 2.0 Error\n * Response.\n *\n * @param <T> type of grant request\n * @author Steve Riesenberg\n * @since 6.4\n * @see <a href=\"https://tools.ietf.org/html/rfc6749#section-3.2\">RFC-6749 Token\n * Endpoint</a>\n * @see RestClientAuthorizationCodeTokenResponseClient\n * @see RestClientClientCredentialsTokenResponseClient\n * @see RestClientRefreshTokenTokenResponseClient\n * @see RestClientJwtBearerTokenResponseClient\n * @see RestClientTokenExchangeTokenResponseClient\n * @see DefaultOAuth2TokenRequestHeadersConverter\n */\npublic abstract class AbstractRestClientOAuth2AccessTokenResponseClient<T extends AbstractOAuth2AuthorizationGrantRequest>\n\t\timplements OAuth2AccessTokenResponseClient<T> {\n\n\tprivate static final String INVALID_TOKEN_RESPONSE_ERROR_CODE = \"invalid_token_response\";\n\n\t// @formatter:off\n\tprivate RestClient restClient = RestClient.builder()\n\t\t\t.configureMessageConverters((messageConverters) -> {\n\t\t\t\tmessageConverters.addCustomConverter(new FormHttpMessageConverter());\n\t\t\t\tmessageConverters.addCustomConverter(new OAuth2AccessTokenResponseHttpMessageConverter());\n\t\t\t})\n\t\t\t.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())\n\t\t\t.build();\n\t// @formatter:on\n\n\tprivate Converter<T, RequestHeadersSpec<?>> requestEntityConverter = this::validatingPopulateRequest;\n\n\tprivate Converter<T, HttpHeaders> headersConverter = new DefaultOAuth2TokenRequestHeadersConverter<>();\n\n\tprivate Converter<T, MultiValueMap<String, String>> parametersConverter = new DefaultOAuth2TokenRequestParametersConverter<>();\n\n\tprivate Consumer<MultiValueMap<String, String>> parametersCustomizer = (parameters) -> {\n\t};\n\n\tAbstractRestClientOAuth2AccessTokenResponseClient() {\n\t}\n\n\t@Override\n\tpublic OAuth2AccessTokenResponse getTokenResponse(T grantRequest) {\n\t\tAssert.notNull(grantRequest, \"grantRequest cannot be null\");\n\t\ttry {\n\t\t\t// @formatter:off\n\t\t\tOAuth2AccessTokenResponse accessTokenResponse = this.requestEntityConverter.convert(grantRequest)\n\t\t\t\t\t.retrieve()\n\t\t\t\t\t.body(OAuth2AccessTokenResponse.class);\n\t\t\t// @formatter:on\n\t\t\tif (accessTokenResponse == null) {\n\t\t\t\tOAuth2Error error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,\n\t\t\t\t\t\t\"Empty OAuth 2.0 Access Token Response\", null);\n\t\t\t\tthrow new OAuth2AuthorizationException(error);\n\t\t\t}\n\t\t\treturn accessTokenResponse;\n\t\t}\n\t\tcatch (RestClientException ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,\n\t\t\t\t\t\"An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: \"\n\t\t\t\t\t\t\t+ ex.getMessage(),\n\t\t\t\t\tnull);\n\t\t\tthrow new OAuth2AuthorizationException(error, ex);\n\t\t}\n\t}\n\n\tprivate RequestHeadersSpec<?> validatingPopulateRequest(T grantRequest) {\n\t\tvalidateClientAuthenticationMethod(grantRequest);\n\t\treturn populateRequest(grantRequest);\n\t}\n\n\tprivate void validateClientAuthenticationMethod(T grantRequest) {\n\t\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\t\tClientAuthenticationMethod clientAuthenticationMethod = clientRegistration.getClientAuthenticationMethod();\n\t\tboolean supportedClientAuthenticationMethod = clientAuthenticationMethod.equals(ClientAuthenticationMethod.NONE)\n\t\t\t\t|| clientAuthenticationMethod.equals(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t|| clientAuthenticationMethod.equals(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tif (!supportedClientAuthenticationMethod) {\n\t\t\tthrow new IllegalArgumentException(String.format(\n\t\t\t\t\t\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default. Client [%s] is using [%s] instead. Please use a supported client authentication method, or use `set/addParametersConverter` or `set/addHeadersConverter` to supply an instance that supports [%s].\",\n\t\t\t\t\tclientRegistration.getRegistrationId(), clientAuthenticationMethod, clientAuthenticationMethod));\n\t\t}\n\t}\n\n\tprivate RequestHeadersSpec<?> populateRequest(T grantRequest) {\n\t\tMultiValueMap<String, String> parameters = this.parametersConverter.convert(grantRequest);\n\t\tif (parameters == null) {\n\t\t\tparameters = new LinkedMultiValueMap<>();\n\t\t}\n\t\tthis.parametersCustomizer.accept(parameters);\n\n\t\treturn this.restClient.post()\n\t\t\t.uri(grantRequest.getClientRegistration().getProviderDetails().getTokenUri())\n\t\t\t.headers((headers) -> {\n\t\t\t\tHttpHeaders headersToAdd = this.headersConverter.convert(grantRequest);\n\t\t\t\tif (headersToAdd != null) {\n\t\t\t\t\theaders.addAll(headersToAdd);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.body(parameters);\n\t}\n\n\t/**\n\t * Sets the {@link RestClient} used when requesting the OAuth 2.0 Access Token\n\t * Response.\n\t * @param restClient the {@link RestClient} used when requesting the Access Token\n\t * Response\n\t */\n\tpublic final void setRestClient(RestClient restClient) {\n\t\tAssert.notNull(restClient, \"restClient cannot be null\");\n\t\tthis.restClient = restClient;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders}\n\t * used in the OAuth 2.0 Access Token Request headers.\n\t * @param headersConverter the {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} to {@link HttpHeaders}\n\t */\n\tpublic final void setHeadersConverter(Converter<T, HttpHeaders> headersConverter) {\n\t\tAssert.notNull(headersConverter, \"headersConverter cannot be null\");\n\t\tthis.headersConverter = headersConverter;\n\t\tthis.requestEntityConverter = this::populateRequest;\n\t}\n\n\t/**\n\t * Add (compose) the provided {@code headersConverter} to the current\n\t * {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders}\n\t * used in the OAuth 2.0 Access Token Request headers.\n\t * @param headersConverter the {@link Converter} to add (compose) to the current\n\t * {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} to a {@link HttpHeaders}\n\t */\n\tpublic final void addHeadersConverter(Converter<T, HttpHeaders> headersConverter) {\n\t\tAssert.notNull(headersConverter, \"headersConverter cannot be null\");\n\t\tConverter<T, HttpHeaders> currentHeadersConverter = this.headersConverter;\n\t\tthis.headersConverter = (authorizationGrantRequest) -> {\n\t\t\t// Append headers using a Composite Converter\n\t\t\tHttpHeaders headers = currentHeadersConverter.convert(authorizationGrantRequest);\n\t\t\tif (headers == null) {\n\t\t\t\theaders = new HttpHeaders();\n\t\t\t}\n\t\t\tHttpHeaders headersToAdd = headersConverter.convert(authorizationGrantRequest);\n\t\t\tif (headersToAdd != null) {\n\t\t\t\theaders.addAll(headersToAdd);\n\t\t\t}\n\t\t\treturn headers;\n\t\t};\n\t\tthis.requestEntityConverter = this::populateRequest;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap}\n\t * used in the OAuth 2.0 Access Token Request body.\n\t * @param parametersConverter the {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} to {@link MultiValueMap}\n\t */\n\tpublic final void setParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) {\n\t\tAssert.notNull(parametersConverter, \"parametersConverter cannot be null\");\n\t\tif (parametersConverter instanceof DefaultOAuth2TokenRequestParametersConverter) {\n\t\t\tthis.parametersConverter = parametersConverter;\n\t\t}\n\t\telse {\n\t\t\tConverter<T, MultiValueMap<String, String>> defaultParametersConverter = new DefaultOAuth2TokenRequestParametersConverter<>();\n\t\t\tthis.parametersConverter = (authorizationGrantRequest) -> {\n\t\t\t\tMultiValueMap<String, String> parameters = defaultParametersConverter\n\t\t\t\t\t.convert(authorizationGrantRequest);\n\t\t\t\tMultiValueMap<String, String> parametersToSet = parametersConverter.convert(authorizationGrantRequest);\n\t\t\t\tif (parametersToSet != null) {\n\t\t\t\t\tparameters.putAll(parametersToSet);\n\t\t\t\t}\n\t\t\t\treturn parameters;\n\t\t\t};\n\t\t}\n\t\tthis.requestEntityConverter = this::populateRequest;\n\t}\n\n\t/**\n\t * Add (compose) the provided {@code parametersConverter} to the current\n\t * {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap}\n\t * used in the OAuth 2.0 Access Token Request body.\n\t * @param parametersConverter the {@link Converter} to add (compose) to the current\n\t * {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} to a {@link MultiValueMap}\n\t */\n\tpublic final void addParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) {\n\t\tAssert.notNull(parametersConverter, \"parametersConverter cannot be null\");\n\t\tConverter<T, MultiValueMap<String, String>> currentParametersConverter = this.parametersConverter;\n\t\tthis.parametersConverter = (authorizationGrantRequest) -> {\n\t\t\tMultiValueMap<String, String> parameters = currentParametersConverter.convert(authorizationGrantRequest);\n\t\t\tif (parameters == null) {\n\t\t\t\tparameters = new LinkedMultiValueMap<>();\n\t\t\t}\n\t\t\tMultiValueMap<String, String> parametersToAdd = parametersConverter.convert(authorizationGrantRequest);\n\t\t\tif (parametersToAdd != null) {\n\t\t\t\tparameters.addAll(parametersToAdd);\n\t\t\t}\n\t\t\treturn parameters;\n\t\t};\n\t\tthis.requestEntityConverter = this::populateRequest;\n\t}\n\n\t/**\n\t * Sets the {@link Consumer} used for customizing the OAuth 2.0 Access Token\n\t * parameters, which allows for parameters to be added, overwritten or removed.\n\t * @param parametersCustomizer the {@link Consumer} to customize the parameters\n\t */\n\tpublic void setParametersCustomizer(Consumer<MultiValueMap<String, String>> parametersCustomizer) {\n\t\tAssert.notNull(parametersCustomizer, \"parametersCustomizer cannot be null\");\n\t\tthis.parametersCustomizer = parametersCustomizer;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractWebClientReactiveOAuth2AccessTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.util.function.Consumer;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.web.reactive.function.OAuth2BodyExtractors;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.reactive.function.BodyExtractor;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;\n\n/**\n * Abstract base class for all of the {@code WebClientReactive*TokenResponseClient}s that\n * communicate to the Authorization Server's Token Endpoint.\n *\n * <p>\n * Submits a form request body specific to the type of grant request.\n * </p>\n *\n * <p>\n * Accepts a JSON response body containing an OAuth 2.0 Access token or error.\n * </p>\n *\n * @param <T> type of grant request\n * @author Phil Clay\n * @author Steve Riesenberg\n * @since 5.3\n * @see <a href=\"https://tools.ietf.org/html/rfc6749#section-3.2\">RFC-6749 Token\n * Endpoint</a>\n * @see WebClientReactiveAuthorizationCodeTokenResponseClient\n * @see WebClientReactiveClientCredentialsTokenResponseClient\n * @see WebClientReactiveRefreshTokenTokenResponseClient\n * @see DefaultOAuth2TokenRequestHeadersConverter\n */\npublic abstract class AbstractWebClientReactiveOAuth2AccessTokenResponseClient<T extends AbstractOAuth2AuthorizationGrantRequest>\n\t\timplements ReactiveOAuth2AccessTokenResponseClient<T> {\n\n\tprivate WebClient webClient = WebClient.builder().build();\n\n\tprivate Converter<T, RequestHeadersSpec<?>> requestEntityConverter = this::validatingPopulateRequest;\n\n\tprivate Converter<T, HttpHeaders> headersConverter = new DefaultOAuth2TokenRequestHeadersConverter<>();\n\n\tprivate Converter<T, MultiValueMap<String, String>> parametersConverter = new DefaultOAuth2TokenRequestParametersConverter<>();\n\n\tprivate Consumer<MultiValueMap<String, String>> parametersCustomizer = (parameters) -> {\n\t};\n\n\tprivate BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> bodyExtractor = OAuth2BodyExtractors\n\t\t.oauth2AccessTokenResponse();\n\n\tAbstractWebClientReactiveOAuth2AccessTokenResponseClient() {\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AccessTokenResponse> getTokenResponse(T grantRequest) {\n\t\tAssert.notNull(grantRequest, \"grantRequest cannot be null\");\n\t\t// @formatter:off\n\t\treturn Mono.defer(() -> this.requestEntityConverter.convert(grantRequest)\n\t\t\t\t.exchangeToMono((response) -> response.body(this.bodyExtractor)));\n\t\t// @formatter:on\n\t}\n\n\tprivate RequestHeadersSpec<?> validatingPopulateRequest(T grantRequest) {\n\t\tvalidateClientAuthenticationMethod(grantRequest);\n\t\treturn populateRequest(grantRequest);\n\t}\n\n\tprivate void validateClientAuthenticationMethod(T grantRequest) {\n\t\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\t\tClientAuthenticationMethod clientAuthenticationMethod = clientRegistration.getClientAuthenticationMethod();\n\t\tboolean supportedClientAuthenticationMethod = clientAuthenticationMethod.equals(ClientAuthenticationMethod.NONE)\n\t\t\t\t|| clientAuthenticationMethod.equals(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t|| clientAuthenticationMethod.equals(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tif (!supportedClientAuthenticationMethod) {\n\t\t\tthrow new IllegalArgumentException(String.format(\n\t\t\t\t\t\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default. Client [%s] is using [%s] instead. Please use a supported client authentication method, or use `set/addParametersConverter` or `set/addHeadersConverter` to supply an instance that supports [%s].\",\n\t\t\t\t\tclientRegistration.getRegistrationId(), clientAuthenticationMethod, clientAuthenticationMethod));\n\t\t}\n\t}\n\n\tprivate RequestHeadersSpec<?> populateRequest(T grantRequest) {\n\t\tMultiValueMap<String, String> parameters = this.parametersConverter.convert(grantRequest);\n\t\tif (parameters == null) {\n\t\t\tparameters = new LinkedMultiValueMap<>();\n\t\t}\n\t\tthis.parametersCustomizer.accept(parameters);\n\n\t\treturn this.webClient.post()\n\t\t\t.uri(grantRequest.getClientRegistration().getProviderDetails().getTokenUri())\n\t\t\t.headers((headers) -> {\n\t\t\t\tHttpHeaders headersToAdd = this.headersConverter.convert(grantRequest);\n\t\t\t\tif (headersToAdd != null) {\n\t\t\t\t\theaders.addAll(headersToAdd);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.body(BodyInserters.fromFormData(parameters));\n\t}\n\n\t/**\n\t * Sets the {@link WebClient} used when requesting the OAuth 2.0 Access Token\n\t * Response.\n\t * @param webClient the {@link WebClient} used when requesting the Access Token\n\t * Response\n\t */\n\tpublic final void setWebClient(WebClient webClient) {\n\t\tAssert.notNull(webClient, \"webClient cannot be null\");\n\t\tthis.webClient = webClient;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders}\n\t * used in the OAuth 2.0 Access Token Request headers.\n\t * @param headersConverter the {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} to {@link HttpHeaders}\n\t * @since 5.6\n\t */\n\tpublic final void setHeadersConverter(Converter<T, HttpHeaders> headersConverter) {\n\t\tAssert.notNull(headersConverter, \"headersConverter cannot be null\");\n\t\tthis.headersConverter = headersConverter;\n\t\tthis.requestEntityConverter = this::populateRequest;\n\t}\n\n\t/**\n\t * Add (compose) the provided {@code headersConverter} to the current\n\t * {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders}\n\t * used in the OAuth 2.0 Access Token Request headers.\n\t * @param headersConverter the {@link Converter} to add (compose) to the current\n\t * {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} to a {@link HttpHeaders}\n\t * @since 5.6\n\t */\n\tpublic final void addHeadersConverter(Converter<T, HttpHeaders> headersConverter) {\n\t\tAssert.notNull(headersConverter, \"headersConverter cannot be null\");\n\t\tConverter<T, HttpHeaders> currentHeadersConverter = this.headersConverter;\n\t\tthis.headersConverter = (authorizationGrantRequest) -> {\n\t\t\t// Append headers using a Composite Converter\n\t\t\tHttpHeaders headers = currentHeadersConverter.convert(authorizationGrantRequest);\n\t\t\tif (headers == null) {\n\t\t\t\theaders = new HttpHeaders();\n\t\t\t}\n\t\t\tHttpHeaders headersToAdd = headersConverter.convert(authorizationGrantRequest);\n\t\t\tif (headersToAdd != null) {\n\t\t\t\theaders.addAll(headersToAdd);\n\t\t\t}\n\t\t\treturn headers;\n\t\t};\n\t\tthis.requestEntityConverter = this::populateRequest;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap}\n\t * used in the OAuth 2.0 Access Token Request body.\n\t * @param parametersConverter the {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} to {@link MultiValueMap}\n\t * @since 5.6\n\t */\n\tpublic final void setParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) {\n\t\tAssert.notNull(parametersConverter, \"parametersConverter cannot be null\");\n\t\tif (parametersConverter instanceof DefaultOAuth2TokenRequestParametersConverter) {\n\t\t\tthis.parametersConverter = parametersConverter;\n\t\t}\n\t\telse {\n\t\t\tConverter<T, MultiValueMap<String, String>> defaultParametersConverter = new DefaultOAuth2TokenRequestParametersConverter<>();\n\t\t\tthis.parametersConverter = (authorizationGrantRequest) -> {\n\t\t\t\tMultiValueMap<String, String> parameters = defaultParametersConverter\n\t\t\t\t\t.convert(authorizationGrantRequest);\n\t\t\t\tMultiValueMap<String, String> parametersToSet = parametersConverter.convert(authorizationGrantRequest);\n\t\t\t\tif (parametersToSet != null) {\n\t\t\t\t\tparameters.putAll(parametersToSet);\n\t\t\t\t}\n\t\t\t\treturn parameters;\n\t\t\t};\n\t\t}\n\t\tthis.requestEntityConverter = this::populateRequest;\n\t}\n\n\t/**\n\t * Add (compose) the provided {@code parametersConverter} to the current\n\t * {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap}\n\t * used in the OAuth 2.0 Access Token Request body.\n\t * @param parametersConverter the {@link Converter} to add (compose) to the current\n\t * {@link Converter} used for converting the\n\t * {@link AbstractOAuth2AuthorizationGrantRequest} to a {@link MultiValueMap}\n\t * @since 5.6\n\t */\n\tpublic final void addParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) {\n\t\tAssert.notNull(parametersConverter, \"parametersConverter cannot be null\");\n\t\tConverter<T, MultiValueMap<String, String>> currentParametersConverter = this.parametersConverter;\n\t\tthis.parametersConverter = (authorizationGrantRequest) -> {\n\t\t\tMultiValueMap<String, String> parameters = currentParametersConverter.convert(authorizationGrantRequest);\n\t\t\tif (parameters == null) {\n\t\t\t\tparameters = new LinkedMultiValueMap<>();\n\t\t\t}\n\t\t\tMultiValueMap<String, String> parametersToAdd = parametersConverter.convert(authorizationGrantRequest);\n\t\t\tif (parametersToAdd != null) {\n\t\t\t\tparameters.addAll(parametersToAdd);\n\t\t\t}\n\t\t\treturn parameters;\n\t\t};\n\t\tthis.requestEntityConverter = this::populateRequest;\n\t}\n\n\t/**\n\t * Sets the {@link Consumer} used for customizing the OAuth 2.0 Access Token\n\t * parameters, which allows for parameters to be added, overwritten or removed.\n\t * @param parametersCustomizer the {@link Consumer} to customize the parameters\n\t */\n\tpublic void setParametersCustomizer(Consumer<MultiValueMap<String, String>> parametersCustomizer) {\n\t\tAssert.notNull(parametersCustomizer, \"parametersCustomizer cannot be null\");\n\t\tthis.parametersCustomizer = parametersCustomizer;\n\t}\n\n\t/**\n\t * Sets the {@link BodyExtractor} that will be used to decode the\n\t * {@link OAuth2AccessTokenResponse}\n\t * @param bodyExtractor the {@link BodyExtractor} that will be used to decode the\n\t * {@link OAuth2AccessTokenResponse}\n\t * @since 5.6\n\t */\n\tpublic final void setBodyExtractor(\n\t\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> bodyExtractor) {\n\t\tAssert.notNull(bodyExtractor, \"bodyExtractor cannot be null\");\n\t\tthis.bodyExtractor = bodyExtractor;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/DefaultOAuth2TokenRequestHeadersConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\n/**\n * Default {@link Converter} used to convert an\n * {@link AbstractOAuth2AuthorizationGrantRequest} to the {@link HttpHeaders} of a\n * {@link RequestEntity} representation of an OAuth 2.0 Access Token Request for the\n * specific Authorization Grant.\n *\n * @author Peter Eastham\n * @author Steve Riesenberg\n * @since 6.3\n * @see AbstractRestClientOAuth2AccessTokenResponseClient\n * @see AbstractWebClientReactiveOAuth2AccessTokenResponseClient\n */\npublic final class DefaultOAuth2TokenRequestHeadersConverter<T extends AbstractOAuth2AuthorizationGrantRequest>\n\t\timplements Converter<T, HttpHeaders> {\n\n\tprivate List<MediaType> accept = List.of(MediaType.APPLICATION_JSON);\n\n\tprivate MediaType contentType = MediaType.APPLICATION_FORM_URLENCODED;\n\n\tprivate boolean encodeClientCredentials = true;\n\n\t/**\n\t * Populates the default headers for the token request.\n\t * @param grantRequest the authorization grant request\n\t * @return the headers populated for the token request\n\t */\n\t@Override\n\tpublic HttpHeaders convert(T grantRequest) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setAccept(this.accept);\n\t\theaders.setContentType(this.contentType);\n\t\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.equals(clientRegistration.getClientAuthenticationMethod())) {\n\t\t\tString clientId = encodeClientCredentialIfRequired(clientRegistration.getClientId());\n\t\t\tString clientSecret = encodeClientCredentialIfRequired(clientRegistration.getClientSecret());\n\t\t\theaders.setBasicAuth(clientId, clientSecret);\n\t\t}\n\t\treturn headers;\n\t}\n\n\tprivate String encodeClientCredentialIfRequired(String clientCredential) {\n\t\tif (!this.encodeClientCredentials) {\n\t\t\treturn clientCredential;\n\t\t}\n\t\treturn URLEncoder.encode(clientCredential, StandardCharsets.UTF_8);\n\t}\n\n\t/**\n\t * Sets whether the client credentials of the {@code Authorization} header will be\n\t * encoded using the {@code application/x-www-form-urlencoded} encoding algorithm\n\t * according to RFC 6749. Default is {@code true}.\n\t * @param encodeClientCredentials whether the client credentials will be encoded\n\t * @see <a target=\"_blank\" href=\n\t * \"https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1\">2.3.1 Client\n\t * Password</a>\n\t */\n\tpublic void setEncodeClientCredentials(boolean encodeClientCredentials) {\n\t\tthis.encodeClientCredentials = encodeClientCredentials;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/DefaultOAuth2TokenRequestParametersConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\n\n/**\n * Default {@link Converter} used to convert an\n * {@link AbstractOAuth2AuthorizationGrantRequest} to the default {@link MultiValueMap\n * parameters} of an OAuth 2.0 Access Token Request.\n * <p>\n * This implementation provides grant-type specific parameters for the following grant\n * types:\n *\n * <ul>\n * <li>{@code authorization_code}</li>\n * <li>{@code refresh_token}</li>\n * <li>{@code client_credentials}</li>\n * <li>{@code urn:ietf:params:oauth:grant-type:jwt-bearer}</li>\n * <li>{@code urn:ietf:params:oauth:grant-type:token-exchange}</li>\n * </ul>\n *\n * In addition, the following default parameters are provided:\n *\n * <ul>\n * <li>{@code grant_type} - always provided</li>\n * <li>{@code client_id} - provided unless the {@code clientAuthenticationMethod} is\n * {@code client_secret_basic}</li>\n * <li>{@code client_secret} - provided when the {@code clientAuthenticationMethod} is\n * {@code client_secret_post}</li>\n * </ul>\n *\n * @param <T> type of grant request\n * @author Steve Riesenberg\n * @since 6.4\n * @see AbstractWebClientReactiveOAuth2AccessTokenResponseClient\n * @see AbstractRestClientOAuth2AccessTokenResponseClient\n */\npublic final class DefaultOAuth2TokenRequestParametersConverter<T extends AbstractOAuth2AuthorizationGrantRequest>\n\t\timplements Converter<T, MultiValueMap<String, String>> {\n\n\tprivate final Converter<T, @Nullable MultiValueMap<String, String>> defaultParametersConverter = createDefaultParametersConverter();\n\n\t@Override\n\tpublic MultiValueMap<String, String> convert(T grantRequest) {\n\t\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, grantRequest.getGrantType().getValue());\n\t\tif (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC\n\t\t\t.equals(clientRegistration.getClientAuthenticationMethod())) {\n\t\t\tparameters.set(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());\n\t\t}\n\t\tif (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod())) {\n\t\t\tparameters.set(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());\n\t\t}\n\n\t\tMultiValueMap<String, String> defaultParameters = this.defaultParametersConverter.convert(grantRequest);\n\t\tif (defaultParameters != null) {\n\t\t\tparameters.addAll(defaultParameters);\n\t\t}\n\n\t\treturn parameters;\n\t}\n\n\tprivate static <T extends AbstractOAuth2AuthorizationGrantRequest> Converter<T, @Nullable MultiValueMap<String, String>> createDefaultParametersConverter() {\n\t\treturn (grantRequest) -> {\n\t\t\tif (grantRequest instanceof OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {\n\t\t\t\treturn OAuth2AuthorizationCodeGrantRequest.defaultParameters(authorizationCodeGrantRequest);\n\t\t\t}\n\t\t\telse if (grantRequest instanceof OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) {\n\t\t\t\treturn OAuth2ClientCredentialsGrantRequest.defaultParameters(clientCredentialsGrantRequest);\n\t\t\t}\n\t\t\telse if (grantRequest instanceof OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) {\n\t\t\t\treturn OAuth2RefreshTokenGrantRequest.defaultParameters(refreshTokenGrantRequest);\n\t\t\t}\n\t\t\telse if (grantRequest instanceof JwtBearerGrantRequest jwtBearerGrantRequest) {\n\t\t\t\treturn JwtBearerGrantRequest.defaultParameters(jwtBearerGrantRequest);\n\t\t\t}\n\t\t\telse if (grantRequest instanceof TokenExchangeGrantRequest tokenExchangeGrantRequest) {\n\t\t\t\treturn TokenExchangeGrantRequest.defaultParameters(tokenExchangeGrantRequest);\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JwtBearerGrantRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * A JWT Bearer Grant request that holds a {@link Jwt} assertion.\n *\n * @author Joe Grandja\n * @since 5.5\n * @see AbstractOAuth2AuthorizationGrantRequest\n * @see ClientRegistration\n * @see Jwt\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7523#section-2.1\">Section\n * 2.1 Using JWTs as Authorization Grants</a>\n */\npublic class JwtBearerGrantRequest extends AbstractOAuth2AuthorizationGrantRequest {\n\n\tprivate final Jwt jwt;\n\n\t/**\n\t * Constructs a {@code JwtBearerGrantRequest} using the provided parameters.\n\t * @param clientRegistration the client registration\n\t * @param jwt the JWT assertion\n\t */\n\tpublic JwtBearerGrantRequest(ClientRegistration clientRegistration, Jwt jwt) {\n\t\tsuper(AuthorizationGrantType.JWT_BEARER, clientRegistration);\n\t\tAssert.isTrue(AuthorizationGrantType.JWT_BEARER.equals(clientRegistration.getAuthorizationGrantType()),\n\t\t\t\t\"clientRegistration.authorizationGrantType must be AuthorizationGrantType.JWT_BEARER\");\n\t\tAssert.notNull(jwt, \"jwt cannot be null\");\n\t\tthis.jwt = jwt;\n\t}\n\n\t/**\n\t * Returns the {@link Jwt JWT} assertion.\n\t * @return the {@link Jwt} assertion\n\t */\n\tpublic Jwt getJwt() {\n\t\treturn this.jwt;\n\t}\n\n\t/**\n\t * Populate default parameters for the JWT Bearer Grant.\n\t * @param grantRequest the authorization grant request\n\t * @return a {@link MultiValueMap} of the parameters used in the OAuth 2.0 Access\n\t * Token Request body\n\t */\n\tstatic MultiValueMap<String, String> defaultParameters(JwtBearerGrantRequest grantRequest) {\n\t\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tStringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \" \"));\n\t\t}\n\t\tparameters.set(OAuth2ParameterNames.ASSERTION, grantRequest.getJwt().getTokenValue());\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.KeyType;\nimport com.nimbusds.jose.jwk.source.ImmutableJWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\n\n/**\n * A {@link Converter} that customizes the OAuth 2.0 Access Token Request parameters by\n * adding a signed JSON Web Token (JWS) to be used for client authentication at the\n * Authorization Server's Token Endpoint. The private/secret key used for signing the JWS\n * is supplied by the {@code com.nimbusds.jose.jwk.JWK} resolver provided via the\n * constructor.\n *\n * <p>\n * <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK.\n *\n * @param <T> the type of {@link AbstractOAuth2AuthorizationGrantRequest}\n * @author Joe Grandja\n * @author Steve Riesenberg\n * @since 5.5\n * @see Converter\n * @see com.nimbusds.jose.jwk.JWK\n * @see RestClientAuthorizationCodeTokenResponseClient#addParametersConverter(Converter)\n * @see RestClientClientCredentialsTokenResponseClient#addParametersConverter(Converter)\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7523#section-2.2\">2.2\n * Using JWTs for Client Authentication</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7521#section-4.2\">4.2\n * Using Assertions for Client Authentication</a>\n * @see <a target=\"_blank\" href=\"https://connect2id.com/products/nimbus-jose-jwt\">Nimbus\n * JOSE + JWT SDK</a>\n */\npublic final class NimbusJwtClientAuthenticationParametersConverter<T extends AbstractOAuth2AuthorizationGrantRequest>\n\t\timplements Converter<T, @Nullable MultiValueMap<String, String>> {\n\n\tprivate static final String INVALID_KEY_ERROR_CODE = \"invalid_key\";\n\n\tprivate static final String INVALID_ALGORITHM_ERROR_CODE = \"invalid_algorithm\";\n\n\tprivate static final String CLIENT_ASSERTION_TYPE_VALUE = \"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\";\n\n\tprivate final Function<ClientRegistration, JWK> jwkResolver;\n\n\tprivate final Map<String, JwsEncoderHolder> jwsEncoders = new ConcurrentHashMap<>();\n\n\tprivate Consumer<JwtClientAuthenticationContext<T>> jwtClientAssertionCustomizer = (context) -> {\n\t};\n\n\t/**\n\t * Constructs a {@code NimbusJwtClientAuthenticationParametersConverter} using the\n\t * provided parameters.\n\t * @param jwkResolver the resolver that provides the {@code com.nimbusds.jose.jwk.JWK}\n\t * associated to the {@link ClientRegistration client}\n\t */\n\tpublic NimbusJwtClientAuthenticationParametersConverter(Function<ClientRegistration, JWK> jwkResolver) {\n\t\tAssert.notNull(jwkResolver, \"jwkResolver cannot be null\");\n\t\tthis.jwkResolver = jwkResolver;\n\t}\n\n\t@Override\n\tpublic @Nullable MultiValueMap<String, String> convert(T authorizationGrantRequest) {\n\t\tAssert.notNull(authorizationGrantRequest, \"authorizationGrantRequest cannot be null\");\n\n\t\tClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration();\n\t\tif (!ClientAuthenticationMethod.PRIVATE_KEY_JWT.equals(clientRegistration.getClientAuthenticationMethod())\n\t\t\t\t&& !ClientAuthenticationMethod.CLIENT_SECRET_JWT\n\t\t\t\t\t.equals(clientRegistration.getClientAuthenticationMethod())) {\n\t\t\treturn null;\n\t\t}\n\n\t\tJWK jwk = this.jwkResolver.apply(clientRegistration);\n\t\tif (jwk == null) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_KEY_ERROR_CODE,\n\t\t\t\t\t\"Failed to resolve JWK signing key for client registration '\"\n\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId() + \"'.\",\n\t\t\t\t\tnull);\n\t\t\tthrow new OAuth2AuthorizationException(oauth2Error);\n\t\t}\n\n\t\tJwsAlgorithm jwsAlgorithm = resolveAlgorithm(jwk);\n\t\tif (jwsAlgorithm == null) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ALGORITHM_ERROR_CODE,\n\t\t\t\t\t\"Unable to resolve JWS (signing) algorithm from JWK associated to client registration '\"\n\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId() + \"'.\",\n\t\t\t\t\tnull);\n\t\t\tthrow new OAuth2AuthorizationException(oauth2Error);\n\t\t}\n\n\t\tJwsHeader.Builder headersBuilder = JwsHeader.with(jwsAlgorithm);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofSeconds(60));\n\n\t\t// @formatter:off\n\t\tJwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder()\n\t\t\t\t.issuer(clientRegistration.getClientId())\n\t\t\t\t.subject(clientRegistration.getClientId())\n\t\t\t\t.audience(Collections.singletonList(clientRegistration.getProviderDetails().getTokenUri()))\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.expiresAt(expiresAt);\n\t\t// @formatter:on\n\n\t\tJwtClientAuthenticationContext<T> jwtClientAssertionContext = new JwtClientAuthenticationContext<>(\n\t\t\t\tauthorizationGrantRequest, headersBuilder, claimsBuilder);\n\t\tthis.jwtClientAssertionCustomizer.accept(jwtClientAssertionContext);\n\n\t\tJwsHeader jwsHeader = headersBuilder.build();\n\t\tJwtClaimsSet jwtClaimsSet = claimsBuilder.build();\n\n\t\tJwsEncoderHolder jwsEncoderHolder = this.jwsEncoders.compute(clientRegistration.getRegistrationId(),\n\t\t\t\t(clientRegistrationId, currentJwsEncoderHolder) -> {\n\t\t\t\t\tif (currentJwsEncoderHolder != null && currentJwsEncoderHolder.getJwk().equals(jwk)) {\n\t\t\t\t\t\treturn currentJwsEncoderHolder;\n\t\t\t\t\t}\n\t\t\t\t\tJWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(jwk));\n\t\t\t\t\treturn new JwsEncoderHolder(new NimbusJwtEncoder(jwkSource), jwk);\n\t\t\t\t});\n\n\t\tJwtEncoder jwsEncoder = jwsEncoderHolder.getJwsEncoder();\n\t\tJwt jws = jwsEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, CLIENT_ASSERTION_TYPE_VALUE);\n\t\tparameters.set(OAuth2ParameterNames.CLIENT_ASSERTION, jws.getTokenValue());\n\n\t\treturn parameters;\n\t}\n\n\tprivate static @Nullable JwsAlgorithm resolveAlgorithm(JWK jwk) {\n\t\tJwsAlgorithm jwsAlgorithm = null;\n\n\t\tif (jwk.getAlgorithm() != null) {\n\t\t\tjwsAlgorithm = SignatureAlgorithm.from(jwk.getAlgorithm().getName());\n\t\t\tif (jwsAlgorithm == null) {\n\t\t\t\tjwsAlgorithm = MacAlgorithm.from(jwk.getAlgorithm().getName());\n\t\t\t}\n\t\t}\n\n\t\tif (jwsAlgorithm == null) {\n\t\t\tif (KeyType.RSA.equals(jwk.getKeyType())) {\n\t\t\t\tjwsAlgorithm = SignatureAlgorithm.RS256;\n\t\t\t}\n\t\t\telse if (KeyType.EC.equals(jwk.getKeyType())) {\n\t\t\t\tjwsAlgorithm = SignatureAlgorithm.ES256;\n\t\t\t}\n\t\t\telse if (KeyType.OCT.equals(jwk.getKeyType())) {\n\t\t\t\tjwsAlgorithm = MacAlgorithm.HS256;\n\t\t\t}\n\t\t}\n\n\t\treturn jwsAlgorithm;\n\t}\n\n\t/**\n\t * Sets the {@link Consumer} to be provided the\n\t * {@link JwtClientAuthenticationContext}, which contains the\n\t * {@link JwsHeader.Builder} and {@link JwtClaimsSet.Builder} for further\n\t * customization.\n\t * @param jwtClientAssertionCustomizer the {@link Consumer} to be provided the\n\t * {@link JwtClientAuthenticationContext}\n\t * @since 5.7\n\t */\n\tpublic void setJwtClientAssertionCustomizer(\n\t\t\tConsumer<JwtClientAuthenticationContext<T>> jwtClientAssertionCustomizer) {\n\t\tAssert.notNull(jwtClientAssertionCustomizer, \"jwtClientAssertionCustomizer cannot be null\");\n\t\tthis.jwtClientAssertionCustomizer = jwtClientAssertionCustomizer;\n\t}\n\n\tprivate static final class JwsEncoderHolder {\n\n\t\tprivate final JwtEncoder jwsEncoder;\n\n\t\tprivate final JWK jwk;\n\n\t\tprivate JwsEncoderHolder(JwtEncoder jwsEncoder, JWK jwk) {\n\t\t\tthis.jwsEncoder = jwsEncoder;\n\t\t\tthis.jwk = jwk;\n\t\t}\n\n\t\tprivate JwtEncoder getJwsEncoder() {\n\t\t\treturn this.jwsEncoder;\n\t\t}\n\n\t\tprivate JWK getJwk() {\n\t\t\treturn this.jwk;\n\t\t}\n\n\t}\n\n\t/**\n\t * A context that holds client authentication-specific state and is used by\n\t * {@link NimbusJwtClientAuthenticationParametersConverter} when attempting to\n\t * customize the JSON Web Token (JWS) client assertion.\n\t *\n\t * @param <T> the type of {@link AbstractOAuth2AuthorizationGrantRequest}\n\t * @since 5.7\n\t */\n\tpublic static final class JwtClientAuthenticationContext<T extends AbstractOAuth2AuthorizationGrantRequest> {\n\n\t\tprivate final T authorizationGrantRequest;\n\n\t\tprivate final JwsHeader.Builder headers;\n\n\t\tprivate final JwtClaimsSet.Builder claims;\n\n\t\tprivate JwtClientAuthenticationContext(T authorizationGrantRequest, JwsHeader.Builder headers,\n\t\t\t\tJwtClaimsSet.Builder claims) {\n\t\t\tthis.authorizationGrantRequest = authorizationGrantRequest;\n\t\t\tthis.headers = headers;\n\t\t\tthis.claims = claims;\n\t\t}\n\n\t\t/**\n\t\t * Returns the {@link AbstractOAuth2AuthorizationGrantRequest authorization grant\n\t\t * request}.\n\t\t * @return the {@link AbstractOAuth2AuthorizationGrantRequest authorization grant\n\t\t * request}\n\t\t */\n\t\tpublic T getAuthorizationGrantRequest() {\n\t\t\treturn this.authorizationGrantRequest;\n\t\t}\n\n\t\t/**\n\t\t * Returns the {@link JwsHeader.Builder} to be used to customize headers of the\n\t\t * JSON Web Token (JWS).\n\t\t * @return the {@link JwsHeader.Builder} to be used to customize headers of the\n\t\t * JSON Web Token (JWS)\n\t\t */\n\t\tpublic JwsHeader.Builder getHeaders() {\n\t\t\treturn this.headers;\n\t\t}\n\n\t\t/**\n\t\t * Returns the {@link JwtClaimsSet.Builder} to be used to customize claims of the\n\t\t * JSON Web Token (JWS).\n\t\t * @return the {@link JwtClaimsSet.Builder} to be used to customize claims of the\n\t\t * JSON Web Token (JWS)\n\t\t */\n\t\tpublic JwtClaimsSet.Builder getClaims() {\n\t\t\treturn this.claims;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AccessTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\n/**\n * A strategy for &quot;exchanging&quot; an authorization grant credential (e.g. an\n * Authorization Code) for an access token credential at the Authorization Server's Token\n * Endpoint.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AbstractOAuth2AuthorizationGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see AuthorizationGrantType\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.3\">Section\n * 1.3 Authorization Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token Request\n * (Authorization Code Grant)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token Response\n * (Authorization Code Grant)</a>\n */\n@FunctionalInterface\npublic interface OAuth2AccessTokenResponseClient<T extends AbstractOAuth2AuthorizationGrantRequest> {\n\n\t/**\n\t * Exchanges the authorization grant credential, provided in the authorization grant\n\t * request, for an access token credential at the Authorization Server's Token\n\t * Endpoint.\n\t * @param authorizationGrantRequest the authorization grant request that contains the\n\t * authorization grant credential\n\t * @return an {@link OAuth2AccessTokenResponse} that contains the\n\t * {@link OAuth2AccessTokenResponse#getAccessToken() access token} credential\n\t * @throws OAuth2AuthorizationException if an error occurs while attempting to\n\t * exchange for the access token credential\n\t */\n\tOAuth2AccessTokenResponse getTokenResponse(T authorizationGrantRequest);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\n\n/**\n * An OAuth 2.0 Authorization Code Grant request that holds an Authorization Code\n * credential, which was granted by the Resource Owner to the\n * {@link #getClientRegistration() Client}.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AbstractOAuth2AuthorizationGrantRequest\n * @see ClientRegistration\n * @see OAuth2AuthorizationExchange\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-1.3.1\">Section 1.3.1 Authorization Code\n * Grant</a>\n */\npublic class OAuth2AuthorizationCodeGrantRequest extends AbstractOAuth2AuthorizationGrantRequest {\n\n\tprivate final OAuth2AuthorizationExchange authorizationExchange;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeGrantRequest} using the provided\n\t * parameters.\n\t * @param clientRegistration the client registration\n\t * @param authorizationExchange the authorization exchange\n\t */\n\tpublic OAuth2AuthorizationCodeGrantRequest(ClientRegistration clientRegistration,\n\t\t\tOAuth2AuthorizationExchange authorizationExchange) {\n\t\tsuper(AuthorizationGrantType.AUTHORIZATION_CODE, clientRegistration);\n\t\tAssert.notNull(authorizationExchange, \"authorizationExchange cannot be null\");\n\t\tthis.authorizationExchange = authorizationExchange;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationExchange authorization exchange}.\n\t * @return the {@link OAuth2AuthorizationExchange}\n\t */\n\tpublic OAuth2AuthorizationExchange getAuthorizationExchange() {\n\t\treturn this.authorizationExchange;\n\t}\n\n\t/**\n\t * Populate default parameters for the Authorization Code Grant.\n\t * @param grantRequest the authorization grant request\n\t * @return a {@link MultiValueMap} of the parameters used in the OAuth 2.0 Access\n\t * Token Request body\n\t */\n\tstatic MultiValueMap<String, String> defaultParameters(OAuth2AuthorizationCodeGrantRequest grantRequest) {\n\t\tOAuth2AuthorizationExchange authorizationExchange = grantRequest.getAuthorizationExchange();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.CODE, authorizationExchange.getAuthorizationResponse().getCode());\n\t\tString redirectUri = authorizationExchange.getAuthorizationRequest().getRedirectUri();\n\t\tif (redirectUri != null) {\n\t\t\tparameters.set(OAuth2ParameterNames.REDIRECT_URI, redirectUri);\n\t\t}\n\t\tString codeVerifier = authorizationExchange.getAuthorizationRequest()\n\t\t\t.getAttribute(PkceParameterNames.CODE_VERIFIER);\n\t\tif (codeVerifier != null) {\n\t\t\tparameters.set(PkceParameterNames.CODE_VERIFIER, codeVerifier);\n\t\t}\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * An OAuth 2.0 Client Credentials Grant request that holds the client's credentials in\n * {@link #getClientRegistration()}.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see AbstractOAuth2AuthorizationGrantRequest\n * @see ClientRegistration\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-1.3.4\">Section 1.3.4 Client Credentials\n * Grant</a>\n */\npublic class OAuth2ClientCredentialsGrantRequest extends AbstractOAuth2AuthorizationGrantRequest {\n\n\t/**\n\t * Constructs an {@code OAuth2ClientCredentialsGrantRequest} using the provided\n\t * parameters.\n\t * @param clientRegistration the client registration\n\t */\n\tpublic OAuth2ClientCredentialsGrantRequest(ClientRegistration clientRegistration) {\n\t\tsuper(AuthorizationGrantType.CLIENT_CREDENTIALS, clientRegistration);\n\t\tAssert.isTrue(AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType()),\n\t\t\t\t\"clientRegistration.authorizationGrantType must be AuthorizationGrantType.CLIENT_CREDENTIALS\");\n\t}\n\n\t/**\n\t * Populate default parameters for the Client Credentials Grant.\n\t * @param grantRequest the authorization grant request\n\t * @return a {@link MultiValueMap} of the parameters used in the OAuth 2.0 Access\n\t * Token Request body\n\t */\n\tstatic MultiValueMap<String, String> defaultParameters(OAuth2ClientCredentialsGrantRequest grantRequest) {\n\t\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tStringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \" \"));\n\t\t}\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * An OAuth 2.0 Refresh Token Grant request that holds the {@link OAuth2RefreshToken\n * refresh token} credential granted to the {@link #getClientRegistration() client}.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see AbstractOAuth2AuthorizationGrantRequest\n * @see OAuth2RefreshToken\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-6\">Section 6\n * Refreshing an Access Token</a>\n */\npublic class OAuth2RefreshTokenGrantRequest extends AbstractOAuth2AuthorizationGrantRequest {\n\n\tprivate final OAuth2AccessToken accessToken;\n\n\tprivate final OAuth2RefreshToken refreshToken;\n\n\tprivate final Set<String> scopes;\n\n\t/**\n\t * Constructs an {@code OAuth2RefreshTokenGrantRequest} using the provided parameters.\n\t * @param clientRegistration the authorized client's registration\n\t * @param accessToken the access token credential granted\n\t * @param refreshToken the refresh token credential granted\n\t */\n\tpublic OAuth2RefreshTokenGrantRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken,\n\t\t\tOAuth2RefreshToken refreshToken) {\n\t\tthis(clientRegistration, accessToken, refreshToken, Collections.emptySet());\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2RefreshTokenGrantRequest} using the provided parameters.\n\t * @param clientRegistration the authorized client's registration\n\t * @param accessToken the access token credential granted\n\t * @param refreshToken the refresh token credential granted\n\t * @param scopes the scopes to request\n\t */\n\tpublic OAuth2RefreshTokenGrantRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken,\n\t\t\tOAuth2RefreshToken refreshToken, Set<String> scopes) {\n\t\tsuper(AuthorizationGrantType.REFRESH_TOKEN, clientRegistration);\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tAssert.notNull(refreshToken, \"refreshToken cannot be null\");\n\t\tthis.accessToken = accessToken;\n\t\tthis.refreshToken = refreshToken;\n\t\tthis.scopes = Collections\n\t\t\t.unmodifiableSet((scopes != null) ? new LinkedHashSet<>(scopes) : Collections.emptySet());\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessToken access token} credential granted.\n\t * @return the {@link OAuth2AccessToken}\n\t */\n\tpublic OAuth2AccessToken getAccessToken() {\n\t\treturn this.accessToken;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2RefreshToken refresh token} credential granted.\n\t * @return the {@link OAuth2RefreshToken}\n\t */\n\tpublic OAuth2RefreshToken getRefreshToken() {\n\t\treturn this.refreshToken;\n\t}\n\n\t/**\n\t * Returns the scope(s) to request.\n\t * @return the scope(s) to request\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n\t/**\n\t * Populate default parameters for the Refresh Token Grant.\n\t * @param grantRequest the authorization grant request\n\t * @return a {@link MultiValueMap} of the parameters used in the OAuth 2.0 Access\n\t * Token Request body\n\t */\n\tstatic MultiValueMap<String, String> defaultParameters(OAuth2RefreshTokenGrantRequest grantRequest) {\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tif (!CollectionUtils.isEmpty(grantRequest.getScopes())) {\n\t\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tStringUtils.collectionToDelimitedString(grantRequest.getScopes(), \" \"));\n\t\t}\n\t\tparameters.set(OAuth2ParameterNames.REFRESH_TOKEN, grantRequest.getRefreshToken().getTokenValue());\n\t\treturn parameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/ReactiveOAuth2AccessTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\n/**\n * A reactive strategy for &quot;exchanging&quot; an authorization grant credential (e.g.\n * an Authorization Code) for an access token credential at the Authorization Server's\n * Token Endpoint.\n *\n * @author Rob Winch\n * @since 5.1\n * @see AbstractOAuth2AuthorizationGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see AuthorizationGrantType\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.3\">Section\n * 1.3 Authorization Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token Request\n * (Authorization Code Grant)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token Response\n * (Authorization Code Grant)</a>\n */\n@FunctionalInterface\npublic interface ReactiveOAuth2AccessTokenResponseClient<T extends AbstractOAuth2AuthorizationGrantRequest> {\n\n\t/**\n\t * Exchanges the authorization grant credential, provided in the authorization grant\n\t * request, for an access token credential at the Authorization Server's Token\n\t * Endpoint.\n\t * @param authorizationGrantRequest the authorization grant request that contains the\n\t * authorization grant credential\n\t * @return an {@link OAuth2AccessTokenResponse} that contains the\n\t * {@link OAuth2AccessTokenResponse#getAccessToken() access token} credential\n\t * @throws OAuth2AuthorizationException if an error occurs while attempting to\n\t * exchange for the access token credential\n\t */\n\tMono<OAuth2AccessTokenResponse> getTokenResponse(T authorizationGrantRequest);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/RestClientAuthorizationCodeTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\n/**\n * An implementation of {@link OAuth2AccessTokenResponseClient} that &quot;exchanges&quot;\n * an authorization code for an access token at the Authorization Server's Token Endpoint.\n *\n * @author Steve Riesenberg\n * @since 6.4\n * @see OAuth2AccessTokenResponseClient\n * @see OAuth2AuthorizationCodeGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token Request\n * (Authorization Code Grant)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token Response\n * (Authorization Code Grant)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7636#section-4.2\">Section\n * 4.2 Client Creates the Code Challenge</a>\n */\npublic final class RestClientAuthorizationCodeTokenResponseClient\n\t\textends AbstractRestClientOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/RestClientClientCredentialsTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\n/**\n * An implementation of {@link OAuth2AccessTokenResponseClient} that &quot;exchanges&quot;\n * client credentials for an access token at the Authorization Server's Token Endpoint.\n *\n * @author Steve Riesenberg\n * @since 6.4\n * @see OAuth2AccessTokenResponseClient\n * @see OAuth2ClientCredentialsGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token Request\n * (Authorization Code Grant)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token Response\n * (Authorization Code Grant)</a>\n */\npublic final class RestClientClientCredentialsTokenResponseClient\n\t\textends AbstractRestClientOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/RestClientJwtBearerTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\n/**\n * An implementation of {@link OAuth2AccessTokenResponseClient} that &quot;exchanges&quot;\n * a JWT for an access token at the Authorization Server's Token Endpoint.\n *\n * @author Steve Riesenberg\n * @since 6.4\n * @see OAuth2AccessTokenResponseClient\n * @see JwtBearerGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7523#section-2.1\">Section\n * 2.1 Using JWTs as Authorization Grants</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7521#section-4.1\">Section\n * 4.1 Using Assertions as Authorization Grants</a>\n */\npublic final class RestClientJwtBearerTokenResponseClient\n\t\textends AbstractRestClientOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/RestClientRefreshTokenTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * An implementation of {@link OAuth2AccessTokenResponseClient} that &quot;exchanges&quot;\n * a refresh token for an access token at the Authorization Server's Token Endpoint.\n *\n * @author Steve Riesenberg\n * @since 6.4\n * @see OAuth2AccessTokenResponseClient\n * @see OAuth2RefreshTokenGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-6\">Section 6\n * Refreshing an Access Token</a>\n */\npublic final class RestClientRefreshTokenTokenResponseClient\n\t\textends AbstractRestClientOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> {\n\n\t@Override\n\tpublic OAuth2AccessTokenResponse getTokenResponse(OAuth2RefreshTokenGrantRequest grantRequest) {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = super.getTokenResponse(grantRequest);\n\t\treturn populateTokenResponse(grantRequest, accessTokenResponse);\n\t}\n\n\tprivate OAuth2AccessTokenResponse populateTokenResponse(OAuth2RefreshTokenGrantRequest grantRequest,\n\t\t\tOAuth2AccessTokenResponse accessTokenResponse) {\n\t\tif (!CollectionUtils.isEmpty(accessTokenResponse.getAccessToken().getScopes())\n\t\t\t\t&& accessTokenResponse.getRefreshToken() != null) {\n\t\t\treturn accessTokenResponse;\n\t\t}\n\t\tOAuth2AccessTokenResponse.Builder tokenResponseBuilder = OAuth2AccessTokenResponse\n\t\t\t.withResponse(accessTokenResponse);\n\t\tif (CollectionUtils.isEmpty(accessTokenResponse.getAccessToken().getScopes())) {\n\t\t\ttokenResponseBuilder.scopes(grantRequest.getAccessToken().getScopes());\n\t\t}\n\t\tif (accessTokenResponse.getRefreshToken() == null) {\n\t\t\t// Reuse existing refresh token\n\t\t\ttokenResponseBuilder.refreshToken(grantRequest.getRefreshToken().getTokenValue());\n\t\t}\n\t\treturn tokenResponseBuilder.build();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/RestClientTokenExchangeTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\n/**\n * An implementation of {@link OAuth2AccessTokenResponseClient} that &quot;exchanges&quot;\n * a subject token (and optionally an actor token) for an access token at the\n * Authorization Server's Token Endpoint.\n *\n * @author Steve Riesenberg\n * @since 6.4\n * @see OAuth2AccessTokenResponseClient\n * @see TokenExchangeGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8693#section-2.1\">Section\n * 2.1 Request</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8693#section-2.2\">Section\n * 2.2 Response</a>\n */\npublic final class RestClientTokenExchangeTokenResponseClient\n\t\textends AbstractRestClientOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/TokenExchangeGrantRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * A Token Exchange Grant request that holds the {@link OAuth2Token subject token} and\n * optional {@link OAuth2Token actor token}.\n *\n * @author Steve Riesenberg\n * @since 6.3\n * @see AbstractOAuth2AuthorizationGrantRequest\n * @see ClientRegistration\n * @see OAuth2Token\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8693#section-1.1\">Section\n * 1.1 Delegation vs. Impersonation Semantics</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8693#section-2.1\">Section\n * 2.1 Request</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8693#section-2.2\">Section\n * 2.2 Response</a>\n */\npublic class TokenExchangeGrantRequest extends AbstractOAuth2AuthorizationGrantRequest {\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate final OAuth2Token subjectToken;\n\n\tprivate final @Nullable OAuth2Token actorToken;\n\n\t/**\n\t * Constructs a {@code TokenExchangeGrantRequest} using the provided parameters.\n\t * @param clientRegistration the client registration\n\t * @param subjectToken the subject token\n\t * @param actorToken the actor token\n\t */\n\tpublic TokenExchangeGrantRequest(ClientRegistration clientRegistration, OAuth2Token subjectToken,\n\t\t\t@Nullable OAuth2Token actorToken) {\n\t\tsuper(AuthorizationGrantType.TOKEN_EXCHANGE, clientRegistration);\n\t\tAssert.isTrue(AuthorizationGrantType.TOKEN_EXCHANGE.equals(clientRegistration.getAuthorizationGrantType()),\n\t\t\t\t\"clientRegistration.authorizationGrantType must be AuthorizationGrantType.TOKEN_EXCHANGE\");\n\t\tAssert.notNull(subjectToken, \"subjectToken cannot be null\");\n\t\tthis.subjectToken = subjectToken;\n\t\tthis.actorToken = actorToken;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Token subject token}.\n\t * @return the {@link OAuth2Token subject token}\n\t */\n\tpublic OAuth2Token getSubjectToken() {\n\t\treturn this.subjectToken;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Token actor token}.\n\t * @return the {@link OAuth2Token actor token}, or {@code null} if not present\n\t */\n\tpublic @Nullable OAuth2Token getActorToken() {\n\t\treturn this.actorToken;\n\t}\n\n\t/**\n\t * Populate default parameters for the Token Exchange Grant.\n\t * @param grantRequest the authorization grant request\n\t * @return a {@link MultiValueMap} of the parameters used in the OAuth 2.0 Access\n\t * Token Request body\n\t */\n\tstatic MultiValueMap<String, String> defaultParameters(TokenExchangeGrantRequest grantRequest) {\n\t\tClientRegistration clientRegistration = grantRequest.getClientRegistration();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tparameters.set(OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tStringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \" \"));\n\t\t}\n\t\tparameters.set(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE);\n\t\tOAuth2Token subjectToken = grantRequest.getSubjectToken();\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN, subjectToken.getTokenValue());\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, tokenType(subjectToken));\n\t\tOAuth2Token actorToken = grantRequest.getActorToken();\n\t\tif (actorToken != null) {\n\t\t\tparameters.set(OAuth2ParameterNames.ACTOR_TOKEN, actorToken.getTokenValue());\n\t\t\tparameters.set(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, tokenType(actorToken));\n\t\t}\n\t\treturn parameters;\n\t}\n\n\tprivate static String tokenType(OAuth2Token token) {\n\t\treturn (token instanceof Jwt) ? JWT_TOKEN_TYPE_VALUE : ACCESS_TOKEN_TYPE_VALUE;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveAuthorizationCodeTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\n/**\n * An implementation of a {@link ReactiveOAuth2AccessTokenResponseClient} that\n * &quot;exchanges&quot; an authorization code credential for an access token credential\n * at the Authorization Server's Token Endpoint.\n *\n * <p>\n * <b>NOTE:</b> This implementation uses the Nimbus OAuth 2.0 SDK internally.\n *\n * @author Rob Winch\n * @since 5.1\n * @see ReactiveOAuth2AccessTokenResponseClient\n * @see OAuth2AuthorizationCodeGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see <a target=\"_blank\" href=\n * \"https://connect2id.com/products/nimbus-oauth-openid-connect-sdk\">Nimbus OAuth 2.0\n * SDK</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token Request\n * (Authorization Code Grant)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token Response\n * (Authorization Code Grant)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7636#section-4.2\">Section\n * 4.2 Client Creates the Code Challenge</a>\n */\npublic class WebClientReactiveAuthorizationCodeTokenResponseClient\n\t\textends AbstractWebClientReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\n/**\n * An implementation of a {@link ReactiveOAuth2AccessTokenResponseClient} that\n * &quot;exchanges&quot; a client credential for an access token credential at the\n * Authorization Server's Token Endpoint.\n *\n * @author Rob Winch\n * @since 5.1\n * @see ReactiveOAuth2AccessTokenResponseClient\n * @see OAuth2AuthorizationCodeGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see <a target=\"_blank\" href=\n * \"https://connect2id.com/products/nimbus-oauth-openid-connect-sdk\">Nimbus OAuth 2.0\n * SDK</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token Request\n * (Authorization Code Grant)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token Response\n * (Authorization Code Grant)</a>\n */\npublic class WebClientReactiveClientCredentialsTokenResponseClient\n\t\textends AbstractWebClientReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveJwtBearerTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.web.reactive.function.client.WebClient;\n\n/**\n * The default implementation of an {@link ReactiveOAuth2AccessTokenResponseClient} for\n * the {@link AuthorizationGrantType#JWT_BEARER jwt-bearer} grant. This implementation\n * uses {@link WebClient} when requesting an access token credential at the Authorization\n * Server's Token Endpoint.\n *\n * @author Steve Riesenberg\n * @since 5.6\n * @see ReactiveOAuth2AccessTokenResponseClient\n * @see JwtBearerGrantRequest\n * @see OAuth2AccessToken\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7523#section-2.1\">Section\n * 2.1 Using JWTs as Authorization Grants</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7521#section-4.1\">Section\n * 4.1 Using Assertions as Authorization Grants</a>\n */\npublic final class WebClientReactiveJwtBearerTokenResponseClient\n\t\textends AbstractWebClientReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveRefreshTokenTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.reactive.function.client.WebClient;\n\n/**\n * An implementation of a {@link ReactiveOAuth2AccessTokenResponseClient} for the\n * {@link AuthorizationGrantType#REFRESH_TOKEN refresh_token} grant. This implementation\n * uses {@link WebClient} when requesting an access token credential at the Authorization\n * Server's Token Endpoint.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see ReactiveOAuth2AccessTokenResponseClient\n * @see OAuth2RefreshTokenGrantRequest\n * @see OAuth2AccessTokenResponse\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-6\">Section 6\n * Refreshing an Access Token</a>\n */\npublic final class WebClientReactiveRefreshTokenTokenResponseClient\n\t\textends AbstractWebClientReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> {\n\n\t@Override\n\tpublic Mono<OAuth2AccessTokenResponse> getTokenResponse(OAuth2RefreshTokenGrantRequest grantRequest) {\n\t\treturn super.getTokenResponse(grantRequest)\n\t\t\t.map((accessTokenResponse) -> populateTokenResponse(grantRequest, accessTokenResponse));\n\t}\n\n\tprivate OAuth2AccessTokenResponse populateTokenResponse(OAuth2RefreshTokenGrantRequest grantRequest,\n\t\t\tOAuth2AccessTokenResponse accessTokenResponse) {\n\t\tif (!CollectionUtils.isEmpty(accessTokenResponse.getAccessToken().getScopes())\n\t\t\t\t&& accessTokenResponse.getRefreshToken() != null) {\n\t\t\treturn accessTokenResponse;\n\t\t}\n\t\tOAuth2AccessTokenResponse.Builder tokenResponseBuilder = OAuth2AccessTokenResponse\n\t\t\t.withResponse(accessTokenResponse);\n\t\tif (CollectionUtils.isEmpty(accessTokenResponse.getAccessToken().getScopes())) {\n\t\t\ttokenResponseBuilder.scopes(grantRequest.getAccessToken().getScopes());\n\t\t}\n\t\tif (accessTokenResponse.getRefreshToken() == null) {\n\t\t\t// Reuse existing refresh token\n\t\t\ttokenResponseBuilder.refreshToken(grantRequest.getRefreshToken().getTokenValue());\n\t\t}\n\t\treturn tokenResponseBuilder.build();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveTokenExchangeTokenResponseClient.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.web.reactive.function.client.WebClient;\n\n/**\n * The default implementation of an {@link ReactiveOAuth2AccessTokenResponseClient} for\n * the {@link AuthorizationGrantType#TOKEN_EXCHANGE token-exchange} grant. This\n * implementation uses {@link WebClient} when requesting an access token credential at the\n * Authorization Server's Token Endpoint.\n *\n * @author Steve Riesenberg\n * @since 6.3\n * @see ReactiveOAuth2AccessTokenResponseClient\n * @see TokenExchangeGrantRequest\n * @see OAuth2AccessToken\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8693#section-2.1\">Section\n * 2.1 Request</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8693#section-2.2\">Section\n * 2.2 Response</a>\n */\npublic final class WebClientReactiveTokenExchangeTokenResponseClient\n\t\textends AbstractWebClientReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes and interfaces providing support to the client for initiating requests to the\n * Authorization Server's Protocol Endpoints.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/event/OAuth2AuthorizedClientRefreshedEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.event;\n\nimport java.io.Serial;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.util.Assert;\n\n/**\n * An event that is published when an {@link OAuth2AuthorizedClient} is refreshed as a\n * result of using a {@code refresh_token} to obtain an OAuth 2.0 Access Token Response.\n *\n * @author Steve Riesenberg\n * @since 6.5\n */\npublic final class OAuth2AuthorizedClientRefreshedEvent extends ApplicationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2178028089321556476L;\n\n\tprivate final OAuth2AuthorizedClient authorizedClient;\n\n\t/**\n\t * Creates a new instance with the provided parameters.\n\t * @param accessTokenResponse the {@link OAuth2AccessTokenResponse} that triggered the\n\t * event\n\t * @param authorizedClient the refreshed {@link OAuth2AuthorizedClient}\n\t */\n\tpublic OAuth2AuthorizedClientRefreshedEvent(OAuth2AccessTokenResponse accessTokenResponse,\n\t\t\tOAuth2AuthorizedClient authorizedClient) {\n\t\tsuper(accessTokenResponse);\n\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\tthis.authorizedClient = authorizedClient;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessTokenResponse} that triggered the event.\n\t * @return the access token response\n\t */\n\tpublic OAuth2AccessTokenResponse getAccessTokenResponse() {\n\t\treturn (OAuth2AccessTokenResponse) this.getSource();\n\t}\n\n\t/**\n\t * Returns the refreshed {@link OAuth2AuthorizedClient}.\n\t * @return the authorized client\n\t */\n\tpublic OAuth2AuthorizedClient getAuthorizedClient() {\n\t\treturn this.authorizedClient;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/event/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Events for OAuth2 Client lifecycle.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.event;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.http;\n\nimport java.io.IOException;\nimport java.net.URI;\n\nimport com.nimbusds.oauth2.sdk.token.BearerTokenError;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.client.ClientHttpResponse;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.DefaultResponseErrorHandler;\nimport org.springframework.web.client.ResponseErrorHandler;\n\n/**\n * A {@link ResponseErrorHandler} that handles an {@link OAuth2Error OAuth 2.0 Error}.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see ResponseErrorHandler\n * @see OAuth2Error\n */\npublic class OAuth2ErrorResponseErrorHandler implements ResponseErrorHandler {\n\n\tprivate HttpMessageConverter<OAuth2Error> oauth2ErrorConverter = new OAuth2ErrorHttpMessageConverter();\n\n\tprivate final ResponseErrorHandler defaultErrorHandler = new DefaultResponseErrorHandler();\n\n\t@Override\n\tpublic boolean hasError(ClientHttpResponse response) throws IOException {\n\t\treturn this.defaultErrorHandler.hasError(response);\n\t}\n\n\t@Override\n\tpublic void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {\n\t\tif (HttpStatus.BAD_REQUEST.value() != response.getStatusCode().value()) {\n\t\t\tthis.defaultErrorHandler.handleError(url, method, response);\n\t\t}\n\t\t// A Bearer Token Error may be in the WWW-Authenticate response header\n\t\t// See https://tools.ietf.org/html/rfc6750#section-3\n\t\tOAuth2Error oauth2Error = this.readErrorFromWwwAuthenticate(response.getHeaders());\n\t\tif (oauth2Error == null) {\n\t\t\toauth2Error = this.oauth2ErrorConverter.read(OAuth2Error.class, response);\n\t\t}\n\t\tthrow new OAuth2AuthorizationException(oauth2Error);\n\t}\n\n\tprivate @Nullable OAuth2Error readErrorFromWwwAuthenticate(HttpHeaders headers) {\n\t\tString wwwAuthenticateHeader = headers.getFirst(HttpHeaders.WWW_AUTHENTICATE);\n\t\tif (!StringUtils.hasText(wwwAuthenticateHeader)) {\n\t\t\treturn null;\n\t\t}\n\t\tBearerTokenError bearerTokenError = getBearerToken(wwwAuthenticateHeader);\n\t\tif (bearerTokenError == null) {\n\t\t\treturn new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, null, null);\n\t\t}\n\t\tString errorCode = (bearerTokenError.getCode() != null) ? bearerTokenError.getCode()\n\t\t\t\t: OAuth2ErrorCodes.SERVER_ERROR;\n\t\tString errorDescription = bearerTokenError.getDescription();\n\t\tString errorUri = (bearerTokenError.getURI() != null) ? bearerTokenError.getURI().toString() : null;\n\t\treturn new OAuth2Error(errorCode, errorDescription, errorUri);\n\t}\n\n\tprivate @Nullable BearerTokenError getBearerToken(String wwwAuthenticateHeader) {\n\t\ttry {\n\t\t\treturn BearerTokenError.parse(wwwAuthenticateHeader);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link HttpMessageConverter} for an OAuth 2.0 Error.\n\t * @param oauth2ErrorConverter A {@link HttpMessageConverter} for an\n\t * {@link OAuth2Error OAuth 2.0 Error}.\n\t * @since 5.7\n\t */\n\tpublic final void setErrorConverter(HttpMessageConverter<OAuth2Error> oauth2ErrorConverter) {\n\t\tAssert.notNull(oauth2ErrorConverter, \"oauth2ErrorConverter cannot be null\");\n\t\tthis.oauth2ErrorConverter = oauth2ErrorConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * HTTP client support for OAuth2 Client.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.http;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/ClientRegistrationDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.ValueDeserializer;\nimport tools.jackson.databind.util.StdConverter;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.util.Assert;\n\n/**\n * A {@code JsonDeserializer} for {@link ClientRegistration}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see ClientRegistration\n * @see ClientRegistrationMixin\n */\nfinal class ClientRegistrationDeserializer extends ValueDeserializer<ClientRegistration> {\n\n\tprivate static final StdConverter<JsonNode, ClientAuthenticationMethod> CLIENT_AUTHENTICATION_METHOD_CONVERTER = new StdConverters.ClientAuthenticationMethodConverter();\n\n\tprivate static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();\n\n\tprivate static final StdConverter<JsonNode, AuthenticationMethod> AUTHENTICATION_METHOD_CONVERTER = new StdConverters.AuthenticationMethodConverter();\n\n\t@Override\n\tpublic ClientRegistration deserialize(JsonParser parser, DeserializationContext context) {\n\t\tJsonNode clientRegistrationNode = context.readTree(parser);\n\t\tJsonNode providerDetailsNode = JsonNodeUtils.findObjectNode(clientRegistrationNode, \"providerDetails\");\n\t\tJsonNode userInfoEndpointNode = JsonNodeUtils.findObjectNode(providerDetailsNode, \"userInfoEndpoint\");\n\t\tString registrationId = JsonNodeUtils.findStringValue(clientRegistrationNode, \"registrationId\");\n\t\tAssert.hasText(registrationId, \"registrationId cannot be null or empty\");\n\t\tString clientId = JsonNodeUtils.findStringValue(clientRegistrationNode, \"clientId\");\n\t\tAssert.hasText(clientId, \"clientId cannot be null or empty\");\n\t\tString clientSecret = JsonNodeUtils.findStringValue(clientRegistrationNode, \"clientSecret\");\n\t\tString redirectUri = JsonNodeUtils.findStringValue(clientRegistrationNode, \"redirectUri\");\n\t\tSet<String> scopes = JsonNodeUtils.findValue(clientRegistrationNode, \"scopes\", JsonNodeUtils.STRING_SET,\n\t\t\t\tcontext);\n\t\tString clientName = JsonNodeUtils.findStringValue(clientRegistrationNode, \"clientName\");\n\t\tString authorizationUri = JsonNodeUtils.findStringValue(providerDetailsNode, \"authorizationUri\");\n\t\tString tokenUri = JsonNodeUtils.findStringValue(providerDetailsNode, \"tokenUri\");\n\t\tAssert.hasText(tokenUri, \"tokenUri cannot be null or empty\");\n\t\tString userInfoUri = JsonNodeUtils.findStringValue(userInfoEndpointNode, \"uri\");\n\t\tString userNameAttributeName = JsonNodeUtils.findStringValue(userInfoEndpointNode, \"userNameAttributeName\");\n\t\tString jwkSetUri = JsonNodeUtils.findStringValue(providerDetailsNode, \"jwkSetUri\");\n\t\tString issuerUri = JsonNodeUtils.findStringValue(providerDetailsNode, \"issuerUri\");\n\t\tMap<String, Object> configurationMetadata = JsonNodeUtils.findValue(providerDetailsNode,\n\t\t\t\t\"configurationMetadata\", JsonNodeUtils.STRING_OBJECT_MAP, context);\n\t\tClientRegistration.Builder builder = ClientRegistration.withRegistrationId(registrationId)\n\t\t\t.clientId(clientId)\n\t\t\t.clientSecret(clientSecret)\n\t\t\t.clientAuthenticationMethod(CLIENT_AUTHENTICATION_METHOD_CONVERTER\n\t\t\t\t.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, \"clientAuthenticationMethod\")))\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE_CONVERTER\n\t\t\t\t.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, \"authorizationGrantType\")))\n\t\t\t.redirectUri(redirectUri)\n\t\t\t.scope(scopes)\n\t\t\t.clientName(clientName)\n\t\t\t.authorizationUri(authorizationUri)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AUTHENTICATION_METHOD_CONVERTER\n\t\t\t\t.convert(JsonNodeUtils.findObjectNode(userInfoEndpointNode, \"authenticationMethod\")))\n\t\t\t.userNameAttributeName(userNameAttributeName)\n\t\t\t.jwkSetUri(jwkSetUri)\n\t\t\t.issuerUri(issuerUri)\n\t\t\t.providerConfigurationMetadata(\n\t\t\t\t\t(configurationMetadata != null) ? configurationMetadata : java.util.Collections.emptyMap());\n\t\treturn builder.build();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/ClientRegistrationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\n\n/**\n * This mixin class is used to serialize/deserialize {@link ClientRegistration}. It also\n * registers a custom deserializer {@link ClientRegistrationDeserializer}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see ClientRegistration\n * @see ClientRegistrationDeserializer\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = ClientRegistrationDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class ClientRegistrationMixin {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/DefaultOAuth2UserMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\n\n/**\n * This mixin class is used to serialize/deserialize {@link DefaultOAuth2User}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see DefaultOAuth2User\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class DefaultOAuth2UserMixin {\n\n\t@JsonCreator\n\tDefaultOAuth2UserMixin(@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"attributes\") Map<String, Object> attributes,\n\t\t\t@JsonProperty(\"nameAttributeKey\") String nameAttributeKey) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/DefaultOidcUserMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\n\n/**\n * This mixin class is used to serialize/deserialize {@link DefaultOidcUser}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see DefaultOidcUser\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties({ \"attributes\" })\nabstract class DefaultOidcUserMixin {\n\n\t@JsonCreator\n\tDefaultOidcUserMixin(@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"idToken\") OidcIdToken idToken, @JsonProperty(\"userInfo\") OidcUserInfo userInfo,\n\t\t\t@JsonProperty(\"nameAttributeKey\") String nameAttributeKey) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/JsonNodeUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\n\n/**\n * Utility class for {@code JsonNode}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n */\nabstract class JsonNodeUtils {\n\n\tstatic final TypeReference<Set<String>> STRING_SET = new TypeReference<>() {\n\t};\n\n\tstatic final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {\n\t};\n\n\tstatic @Nullable String findStringValue(@Nullable JsonNode jsonNode, String fieldName) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isString()) ? value.stringValue() : null;\n\t}\n\n\tstatic <T> @Nullable T findValue(@Nullable JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,\n\t\t\tDeserializationContext context) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isContainer())\n\t\t\t\t? context.readTreeAsValue(value, context.getTypeFactory().constructType(valueTypeReference)) : null;\n\t}\n\n\tstatic @Nullable JsonNode findObjectNode(@Nullable JsonNode jsonNode, String fieldName) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isObject()) ? value : null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2AccessTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.time.Instant;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AccessToken}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AccessToken\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2AccessTokenMixin {\n\n\t@JsonCreator\n\tOAuth2AccessTokenMixin(\n\t\t\t@JsonProperty(\"tokenType\") @JsonDeserialize(\n\t\t\t\t\tconverter = StdConverters.AccessTokenTypeConverter.class) OAuth2AccessToken.TokenType tokenType,\n\t\t\t@JsonProperty(\"tokenValue\") String tokenValue, @JsonProperty(\"issuedAt\") Instant issuedAt,\n\t\t\t@JsonProperty(\"expiresAt\") Instant expiresAt, @JsonProperty(\"scopes\") Set<String> scopes) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthenticationExceptionMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\n/**\n * This mixin class is used to serialize/deserialize\n * {@link OAuth2AuthenticationException}.\n *\n * @author Sebastien Deleuze\n * @author Dennis Neufeld\n * @author Steve Riesenberg\n * @since 7.0\n * @see OAuth2AuthenticationException\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties({ \"cause\", \"stackTrace\", \"suppressedExceptions\" })\nabstract class OAuth2AuthenticationExceptionMixin {\n\n\t@JsonProperty(\"error\")\n\tabstract OAuth2Error getError();\n\n\t@JsonProperty(\"detailMessage\")\n\tabstract String getMessage();\n\n\t@JsonCreator\n\tOAuth2AuthenticationExceptionMixin(@JsonProperty(\"error\") OAuth2Error error,\n\t\t\t@JsonProperty(\"detailMessage\") String message) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AuthenticationToken}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0O\n * @see OAuth2AuthenticationToken\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties({ \"authenticated\" })\nabstract class OAuth2AuthenticationTokenMixin {\n\n\t@JsonCreator\n\tOAuth2AuthenticationTokenMixin(@JsonProperty(\"principal\") OAuth2User principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"authorizedClientRegistrationId\") String authorizedClientRegistrationId) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthorizationRequestDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.Map;\n\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.core.exc.StreamReadException;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.ValueDeserializer;\nimport tools.jackson.databind.util.StdConverter;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;\nimport org.springframework.util.Assert;\n\n/**\n * A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestMixin\n */\nfinal class OAuth2AuthorizationRequestDeserializer extends ValueDeserializer<OAuth2AuthorizationRequest> {\n\n\tprivate static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();\n\n\t@Override\n\tpublic OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context) {\n\t\tJsonNode root = context.readTree(parser);\n\t\treturn deserialize(parser, context, root);\n\t}\n\n\tprivate OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context, JsonNode root) {\n\t\tAuthorizationGrantType authorizationGrantType = AUTHORIZATION_GRANT_TYPE_CONVERTER\n\t\t\t.convert(JsonNodeUtils.findObjectNode(root, \"authorizationGrantType\"));\n\t\tBuilder builder = getBuilder(parser, authorizationGrantType);\n\t\tString authorizationUri = JsonNodeUtils.findStringValue(root, \"authorizationUri\");\n\t\tAssert.hasText(authorizationUri, \"authorizationUri cannot be null or empty\");\n\t\tbuilder.authorizationUri(authorizationUri);\n\t\tString clientId = JsonNodeUtils.findStringValue(root, \"clientId\");\n\t\tAssert.hasText(clientId, \"clientId cannot be null or empty\");\n\t\tbuilder.clientId(clientId);\n\t\tbuilder.redirectUri(JsonNodeUtils.findStringValue(root, \"redirectUri\"));\n\t\tbuilder.scopes(JsonNodeUtils.findValue(root, \"scopes\", JsonNodeUtils.STRING_SET, context));\n\t\tbuilder.state(JsonNodeUtils.findStringValue(root, \"state\"));\n\t\tMap<String, Object> additionalParameters = JsonNodeUtils.findValue(root, \"additionalParameters\",\n\t\t\t\tJsonNodeUtils.STRING_OBJECT_MAP, context);\n\t\tbuilder.additionalParameters(\n\t\t\t\t(additionalParameters != null) ? additionalParameters : java.util.Collections.emptyMap());\n\t\tString authorizationRequestUri = JsonNodeUtils.findStringValue(root, \"authorizationRequestUri\");\n\t\tAssert.hasText(authorizationRequestUri, \"authorizationRequestUri cannot be null or empty\");\n\t\tbuilder.authorizationRequestUri(authorizationRequestUri);\n\t\tMap<String, Object> attributes = JsonNodeUtils.findValue(root, \"attributes\", JsonNodeUtils.STRING_OBJECT_MAP,\n\t\t\t\tcontext);\n\t\tbuilder.attributes((attributes != null) ? attributes : java.util.Collections.emptyMap());\n\t\treturn builder.build();\n\t}\n\n\tprivate Builder getBuilder(JsonParser parser, AuthorizationGrantType authorizationGrantType) {\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {\n\t\t\treturn OAuth2AuthorizationRequest.authorizationCode();\n\t\t}\n\t\tthrow new StreamReadException(parser, \"Invalid authorizationGrantType\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthorizationRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.\n * It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestDeserializer\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2AuthorizationRequestMixin {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthorizedClientMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AuthorizedClient}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2AuthorizedClient\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2AuthorizedClientMixin {\n\n\t@JsonCreator\n\tOAuth2AuthorizedClientMixin(@JsonProperty(\"clientRegistration\") ClientRegistration clientRegistration,\n\t\t\t@JsonProperty(\"principalName\") String principalName,\n\t\t\t@JsonProperty(\"accessToken\") OAuth2AccessToken accessToken,\n\t\t\t@JsonProperty(\"refreshToken\") OAuth2RefreshToken refreshToken) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2ClientJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\n\n/**\n * Jackson {@code Module} for {@code spring-security-oauth2-client}, that registers the\n * following mix-in annotations:\n *\n * <ul>\n * <li>{@link OAuth2AuthorizationRequestMixin}</li>\n * <li>{@link ClientRegistrationMixin}</li>\n * <li>{@link OAuth2AccessTokenMixin}</li>\n * <li>{@link OAuth2RefreshTokenMixin}</li>\n * <li>{@link OAuth2AuthorizedClientMixin}</li>\n * <li>{@link OAuth2UserAuthorityMixin}</li>\n * <li>{@link DefaultOAuth2UserMixin}</li>\n * <li>{@link OidcIdTokenMixin}</li>\n * <li>{@link OidcUserInfoMixin}</li>\n * <li>{@link OidcUserAuthorityMixin}</li>\n * <li>{@link DefaultOidcUserMixin}</li>\n * <li>{@link OAuth2AuthenticationTokenMixin}</li>\n * <li>{@link OAuth2AuthenticationExceptionMixin}</li>\n * <li>{@link OAuth2ErrorMixin}</li>\n * </ul>\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n */\n@SuppressWarnings(\"serial\")\npublic class OAuth2ClientJacksonModule extends SecurityJacksonModule {\n\n\tpublic OAuth2ClientJacksonModule() {\n\t\tsuper(OAuth2ClientJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(OAuth2AuthenticationException.class)\n\t\t\t.allowIfSubType(DefaultOidcUser.class)\n\t\t\t.allowIfSubType(OAuth2AuthorizationRequest.class)\n\t\t\t.allowIfSubType(OAuth2Error.class)\n\t\t\t.allowIfSubType(OAuth2AuthorizedClient.class)\n\t\t\t.allowIfSubType(OidcIdToken.class)\n\t\t\t.allowIfSubType(OidcUserInfo.class)\n\t\t\t.allowIfSubType(DefaultOAuth2User.class)\n\t\t\t.allowIfSubType(ClientRegistration.class)\n\t\t\t.allowIfSubType(OAuth2AccessToken.class)\n\t\t\t.allowIfSubType(OAuth2RefreshToken.class)\n\t\t\t.allowIfSubType(OAuth2AuthenticationToken.class)\n\t\t\t.allowIfSubType(OidcUserAuthority.class)\n\t\t\t.allowIfSubType(OAuth2UserAuthority.class);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);\n\t\tcontext.setMixIn(ClientRegistration.class, ClientRegistrationMixin.class);\n\t\tcontext.setMixIn(OAuth2AccessToken.class, OAuth2AccessTokenMixin.class);\n\t\tcontext.setMixIn(OAuth2RefreshToken.class, OAuth2RefreshTokenMixin.class);\n\t\tcontext.setMixIn(OAuth2AuthorizedClient.class, OAuth2AuthorizedClientMixin.class);\n\t\tcontext.setMixIn(OAuth2UserAuthority.class, OAuth2UserAuthorityMixin.class);\n\t\tcontext.setMixIn(DefaultOAuth2User.class, DefaultOAuth2UserMixin.class);\n\t\tcontext.setMixIn(OidcIdToken.class, OidcIdTokenMixin.class);\n\t\tcontext.setMixIn(OidcUserInfo.class, OidcUserInfoMixin.class);\n\t\tcontext.setMixIn(OidcUserAuthority.class, OidcUserAuthorityMixin.class);\n\t\tcontext.setMixIn(DefaultOidcUser.class, DefaultOidcUserMixin.class);\n\t\tcontext.setMixIn(OAuth2AuthenticationToken.class, OAuth2AuthenticationTokenMixin.class);\n\t\tcontext.setMixIn(OAuth2AuthenticationException.class, OAuth2AuthenticationExceptionMixin.class);\n\t\tcontext.setMixIn(OAuth2Error.class, OAuth2ErrorMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2ErrorMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2Error} as part of\n * {@link org.springframework.security.oauth2.core.OAuth2AuthenticationException}.\n *\n * @author Sebastien Deleuze\n * @author Dennis Neufeld\n * @since 7.0\n * @see OAuth2Error\n * @see OAuth2AuthenticationExceptionMixin\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2ErrorMixin {\n\n\t@JsonCreator\n\tOAuth2ErrorMixin(@JsonProperty(\"errorCode\") String errorCode, @JsonProperty(\"description\") String description,\n\t\t\t@JsonProperty(\"uri\") String uri) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2RefreshTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.time.Instant;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2RefreshToken}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2RefreshToken\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2RefreshTokenMixin {\n\n\t@JsonCreator\n\tOAuth2RefreshTokenMixin(@JsonProperty(\"tokenValue\") String tokenValue, @JsonProperty(\"issuedAt\") Instant issuedAt) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OAuth2UserAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2UserAuthority}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2UserAuthority\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OAuth2UserAuthorityMixin {\n\n\t@JsonCreator\n\tOAuth2UserAuthorityMixin(@JsonProperty(\"authority\") String authority,\n\t\t\t@JsonProperty(\"attributes\") Map<String, Object> attributes) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OidcIdTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.time.Instant;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OidcIdToken}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OidcIdToken\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OidcIdTokenMixin {\n\n\t@JsonCreator\n\tOidcIdTokenMixin(@JsonProperty(\"tokenValue\") String tokenValue, @JsonProperty(\"issuedAt\") Instant issuedAt,\n\t\t\t@JsonProperty(\"expiresAt\") Instant expiresAt, @JsonProperty(\"claims\") Map<String, Object> claims) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OidcUserAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OidcUserAuthority}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OidcUserAuthority\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties({ \"attributes\" })\nabstract class OidcUserAuthorityMixin {\n\n\t@JsonCreator\n\tOidcUserAuthorityMixin(@JsonProperty(\"authority\") String authority, @JsonProperty(\"idToken\") OidcIdToken idToken,\n\t\t\t@JsonProperty(\"userInfo\") OidcUserInfo userInfo) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/OidcUserInfoMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OidcUserInfo}.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n * @see OidcUserInfo\n * @see OAuth2ClientJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class OidcUserInfoMixin {\n\n\t@JsonCreator\n\tOidcUserInfoMixin(@JsonProperty(\"claims\") Map<String, Object> claims) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/StdConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.util.StdConverter;\n\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.util.Assert;\n\n/**\n * {@code StdConverter} implementations.\n *\n * @author Sebastien Deleuze\n * @author Joe Grandja\n * @since 7.0\n */\nabstract class StdConverters {\n\n\tstatic final class AccessTokenTypeConverter extends StdConverter<JsonNode, OAuth2AccessToken.TokenType> {\n\n\t\t@Override\n\t\tpublic OAuth2AccessToken.@Nullable TokenType convert(JsonNode jsonNode) {\n\t\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\t\tif (value != null && OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn OAuth2AccessToken.TokenType.BEARER;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tstatic final class ClientAuthenticationMethodConverter extends StdConverter<JsonNode, ClientAuthenticationMethod> {\n\n\t\t@Override\n\t\tpublic ClientAuthenticationMethod convert(JsonNode jsonNode) {\n\t\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\t\tAssert.hasText(value, \"value cannot be null or empty\");\n\t\t\treturn ClientAuthenticationMethod.valueOf(value);\n\t\t}\n\n\t}\n\n\tstatic final class AuthorizationGrantTypeConverter extends StdConverter<JsonNode, AuthorizationGrantType> {\n\n\t\t@Override\n\t\tpublic AuthorizationGrantType convert(JsonNode jsonNode) {\n\t\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\t\tAssert.hasText(value, \"value cannot be null or empty\");\n\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthorizationGrantType.AUTHORIZATION_CODE;\n\t\t\t}\n\t\t\tif (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthorizationGrantType.CLIENT_CREDENTIALS;\n\t\t\t}\n\t\t\treturn new AuthorizationGrantType(value);\n\t\t}\n\n\t}\n\n\tstatic final class AuthenticationMethodConverter extends StdConverter<JsonNode, AuthenticationMethod> {\n\n\t\t@Override\n\t\tpublic @Nullable AuthenticationMethod convert(JsonNode jsonNode) {\n\t\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\t\tif (value != null && AuthenticationMethod.HEADER.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthenticationMethod.HEADER;\n\t\t\t}\n\t\t\tif (value != null && AuthenticationMethod.FORM.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthenticationMethod.FORM;\n\t\t\t}\n\t\t\tif (value != null && AuthenticationMethod.QUERY.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthenticationMethod.QUERY;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 3+ serialization support for OAuth2 client.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.jackson;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/ClientRegistrationDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.util.StdConverter;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.util.Assert;\n\n/**\n * A {@code JsonDeserializer} for {@link ClientRegistration}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see ClientRegistration\n * @see ClientRegistrationMixin\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.ClientRegistrationDeserializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\nfinal class ClientRegistrationDeserializer extends JsonDeserializer<ClientRegistration> {\n\n\tprivate static final StdConverter<JsonNode, ClientAuthenticationMethod> CLIENT_AUTHENTICATION_METHOD_CONVERTER = new StdConverters.ClientAuthenticationMethodConverter();\n\n\tprivate static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();\n\n\tprivate static final StdConverter<JsonNode, AuthenticationMethod> AUTHENTICATION_METHOD_CONVERTER = new StdConverters.AuthenticationMethodConverter();\n\n\t@Override\n\tpublic ClientRegistration deserialize(JsonParser parser, DeserializationContext context) throws IOException {\n\t\tObjectMapper mapper = (ObjectMapper) parser.getCodec();\n\t\tJsonNode clientRegistrationNode = mapper.readTree(parser);\n\t\tJsonNode providerDetailsNode = JsonNodeUtils.findObjectNode(clientRegistrationNode, \"providerDetails\");\n\t\tJsonNode userInfoEndpointNode = JsonNodeUtils.findObjectNode(providerDetailsNode, \"userInfoEndpoint\");\n\t\tString registrationId = JsonNodeUtils.findStringValue(clientRegistrationNode, \"registrationId\");\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\tString clientId = JsonNodeUtils.findStringValue(clientRegistrationNode, \"clientId\");\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\tString clientSecret = JsonNodeUtils.findStringValue(clientRegistrationNode, \"clientSecret\");\n\t\tString redirectUri = JsonNodeUtils.findStringValue(clientRegistrationNode, \"redirectUri\");\n\t\tSet<String> scopes = JsonNodeUtils.findValue(clientRegistrationNode, \"scopes\", JsonNodeUtils.STRING_SET,\n\t\t\t\tmapper);\n\t\tString clientName = JsonNodeUtils.findStringValue(clientRegistrationNode, \"clientName\");\n\t\tString authorizationUri = JsonNodeUtils.findStringValue(providerDetailsNode, \"authorizationUri\");\n\t\tString tokenUri = JsonNodeUtils.findStringValue(providerDetailsNode, \"tokenUri\");\n\t\tAssert.hasText(tokenUri, \"tokenUri cannot be empty\");\n\t\tString userInfoUri = JsonNodeUtils.findStringValue(userInfoEndpointNode, \"uri\");\n\t\tString userNameAttributeName = JsonNodeUtils.findStringValue(userInfoEndpointNode, \"userNameAttributeName\");\n\t\tString jwkSetUri = JsonNodeUtils.findStringValue(providerDetailsNode, \"jwkSetUri\");\n\t\tString issuerUri = JsonNodeUtils.findStringValue(providerDetailsNode, \"issuerUri\");\n\t\tMap<String, Object> configurationMetadata = JsonNodeUtils.findValue(providerDetailsNode,\n\t\t\t\t\"configurationMetadata\", JsonNodeUtils.STRING_OBJECT_MAP, mapper);\n\t\tClientRegistration.Builder builder = ClientRegistration.withRegistrationId(registrationId)\n\t\t\t.clientId(clientId)\n\t\t\t.clientSecret(clientSecret)\n\t\t\t.clientAuthenticationMethod(CLIENT_AUTHENTICATION_METHOD_CONVERTER\n\t\t\t\t.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, \"clientAuthenticationMethod\")))\n\t\t\t.authorizationGrantType(AUTHORIZATION_GRANT_TYPE_CONVERTER\n\t\t\t\t.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, \"authorizationGrantType\")))\n\t\t\t.redirectUri(redirectUri)\n\t\t\t.scope(scopes)\n\t\t\t.clientName(clientName)\n\t\t\t.authorizationUri(authorizationUri)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AUTHENTICATION_METHOD_CONVERTER\n\t\t\t\t.convert(JsonNodeUtils.findObjectNode(userInfoEndpointNode, \"authenticationMethod\")))\n\t\t\t.userNameAttributeName(userNameAttributeName)\n\t\t\t.jwkSetUri(jwkSetUri)\n\t\t\t.issuerUri(issuerUri)\n\t\t\t.providerConfigurationMetadata(configurationMetadata);\n\t\treturn builder.build();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/ClientRegistrationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\n\n/**\n * This mixin class is used to serialize/deserialize {@link ClientRegistration}. It also\n * registers a custom deserializer {@link ClientRegistrationDeserializer}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see ClientRegistration\n * @see ClientRegistrationDeserializer\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.ClientRegistrationMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = ClientRegistrationDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class ClientRegistrationMixin {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/DefaultOAuth2UserMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\n\n/**\n * This mixin class is used to serialize/deserialize {@link DefaultOAuth2User}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see DefaultOAuth2User\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.DefaultOAuth2UserMixin} based\n * on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class DefaultOAuth2UserMixin {\n\n\t@JsonCreator\n\tDefaultOAuth2UserMixin(@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"attributes\") Map<String, Object> attributes,\n\t\t\t@JsonProperty(\"nameAttributeKey\") String nameAttributeKey) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/DefaultOidcUserMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\n\n/**\n * This mixin class is used to serialize/deserialize {@link DefaultOidcUser}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see DefaultOidcUser\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.DefaultOidcUserMixin} based\n * on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(value = { \"attributes\" }, ignoreUnknown = true)\nabstract class DefaultOidcUserMixin {\n\n\t@JsonCreator\n\tDefaultOidcUserMixin(@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"idToken\") OidcIdToken idToken, @JsonProperty(\"userInfo\") OidcUserInfo userInfo,\n\t\t\t@JsonProperty(\"nameAttributeKey\") String nameAttributeKey) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/JsonNodeUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Utility class for {@code JsonNode}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.JsonNodeUtils} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\nabstract class JsonNodeUtils {\n\n\tstatic final TypeReference<Set<String>> STRING_SET = new TypeReference<>() {\n\t};\n\n\tstatic final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {\n\t};\n\n\tstatic @Nullable String findStringValue(@Nullable JsonNode jsonNode, String fieldName) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isTextual()) ? value.asText() : null;\n\t}\n\n\tstatic <T> @Nullable T findValue(@Nullable JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,\n\t\t\tObjectMapper mapper) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isContainerNode()) ? mapper.convertValue(value, valueTypeReference) : null;\n\t}\n\n\tstatic @Nullable JsonNode findObjectNode(@Nullable JsonNode jsonNode, String fieldName) {\n\t\tif (jsonNode == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJsonNode value = jsonNode.findValue(fieldName);\n\t\treturn (value != null && value.isObject()) ? value : null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2AccessTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.time.Instant;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AccessToken}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2AccessToken\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2AccessTokenMixin} based\n * on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2AccessTokenMixin {\n\n\t@JsonCreator\n\tOAuth2AccessTokenMixin(\n\t\t\t@JsonProperty(\"tokenType\") @JsonDeserialize(\n\t\t\t\t\tconverter = StdConverters.AccessTokenTypeConverter.class) OAuth2AccessToken.TokenType tokenType,\n\t\t\t@JsonProperty(\"tokenValue\") String tokenValue, @JsonProperty(\"issuedAt\") Instant issuedAt,\n\t\t\t@JsonProperty(\"expiresAt\") Instant expiresAt, @JsonProperty(\"scopes\") Set<String> scopes) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationExceptionMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\n/**\n * This mixin class is used to serialize/deserialize\n * {@link OAuth2AuthenticationException}.\n *\n * @author Dennis Neufeld\n * @author Steve Riesenberg\n * @since 5.3.4\n * @see OAuth2AuthenticationException\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationExceptionMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true, value = { \"cause\", \"stackTrace\", \"suppressedExceptions\" })\nabstract class OAuth2AuthenticationExceptionMixin {\n\n\t@JsonProperty(\"error\")\n\tabstract OAuth2Error getError();\n\n\t@JsonProperty(\"detailMessage\")\n\tabstract String getMessage();\n\n\t@JsonCreator\n\tOAuth2AuthenticationExceptionMixin(@JsonProperty(\"error\") OAuth2Error error,\n\t\t\t@JsonProperty(\"detailMessage\") String message) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AuthenticationToken}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2AuthenticationToken\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationTokenMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(value = { \"authenticated\" }, ignoreUnknown = true)\nabstract class OAuth2AuthenticationTokenMixin {\n\n\t@JsonCreator\n\tOAuth2AuthenticationTokenMixin(@JsonProperty(\"principal\") OAuth2User principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"authorizedClientRegistrationId\") String authorizedClientRegistrationId) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizationRequestDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.util.StdConverter;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;\nimport org.springframework.util.Assert;\n\n/**\n * A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestMixin\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2AuthorizationRequestDeserializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\nfinal class OAuth2AuthorizationRequestDeserializer extends JsonDeserializer<OAuth2AuthorizationRequest> {\n\n\tprivate static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();\n\n\t@Override\n\tpublic OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context)\n\t\t\tthrows IOException {\n\t\tObjectMapper mapper = (ObjectMapper) parser.getCodec();\n\t\tJsonNode root = mapper.readTree(parser);\n\t\treturn deserialize(parser, mapper, root);\n\t}\n\n\tprivate OAuth2AuthorizationRequest deserialize(JsonParser parser, ObjectMapper mapper, JsonNode root)\n\t\t\tthrows JsonParseException {\n\t\tAuthorizationGrantType authorizationGrantType = AUTHORIZATION_GRANT_TYPE_CONVERTER\n\t\t\t.convert(JsonNodeUtils.findObjectNode(root, \"authorizationGrantType\"));\n\t\tBuilder builder = getBuilder(parser, authorizationGrantType);\n\t\tString authorizationUri = JsonNodeUtils.findStringValue(root, \"authorizationUri\");\n\t\tAssert.hasText(authorizationUri, \"authorizationUri cannot be empty\");\n\t\tbuilder.authorizationUri(authorizationUri);\n\t\tString clientId = JsonNodeUtils.findStringValue(root, \"clientId\");\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\tbuilder.clientId(clientId);\n\t\tbuilder.redirectUri(JsonNodeUtils.findStringValue(root, \"redirectUri\"));\n\t\tbuilder.scopes(JsonNodeUtils.findValue(root, \"scopes\", JsonNodeUtils.STRING_SET, mapper));\n\t\tbuilder.state(JsonNodeUtils.findStringValue(root, \"state\"));\n\t\tMap<String, Object> additionalParameters = JsonNodeUtils.findValue(root, \"additionalParameters\",\n\t\t\t\tJsonNodeUtils.STRING_OBJECT_MAP, mapper);\n\t\tbuilder.additionalParameters((additionalParameters != null) ? additionalParameters : Collections.emptyMap());\n\t\tString authorizationRequestUri = JsonNodeUtils.findStringValue(root, \"authorizationRequestUri\");\n\t\tif (authorizationRequestUri != null) {\n\t\t\tbuilder.authorizationRequestUri(authorizationRequestUri);\n\t\t}\n\t\tMap<String, Object> attributes = JsonNodeUtils.findValue(root, \"attributes\", JsonNodeUtils.STRING_OBJECT_MAP,\n\t\t\t\tmapper);\n\t\tbuilder.attributes((attributes != null) ? attributes : Collections.emptyMap());\n\t\treturn builder.build();\n\t}\n\n\tprivate OAuth2AuthorizationRequest.Builder getBuilder(JsonParser parser,\n\t\t\tAuthorizationGrantType authorizationGrantType) throws JsonParseException {\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {\n\t\t\treturn OAuth2AuthorizationRequest.authorizationCode();\n\t\t}\n\t\tthrow new JsonParseException(parser, \"Invalid authorizationGrantType\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizationRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.\n * It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestDeserializer\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2AuthorizationRequestMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2AuthorizationRequestMixin {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizedClientMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2AuthorizedClient}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2AuthorizedClient\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2AuthorizedClientMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2AuthorizedClientMixin {\n\n\t@JsonCreator\n\tOAuth2AuthorizedClientMixin(@JsonProperty(\"clientRegistration\") ClientRegistration clientRegistration,\n\t\t\t@JsonProperty(\"principalName\") String principalName,\n\t\t\t@JsonProperty(\"accessToken\") OAuth2AccessToken accessToken,\n\t\t\t@JsonProperty(\"refreshToken\") OAuth2RefreshToken refreshToken) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2ClientJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\n\n/**\n * Jackson {@code Module} for {@code spring-security-oauth2-client}, that registers the\n * following mix-in annotations:\n *\n * <ul>\n * <li>{@link OAuth2AuthorizationRequestMixin}</li>\n * <li>{@link ClientRegistrationMixin}</li>\n * <li>{@link OAuth2AccessTokenMixin}</li>\n * <li>{@link OAuth2RefreshTokenMixin}</li>\n * <li>{@link OAuth2AuthorizedClientMixin}</li>\n * <li>{@link OAuth2UserAuthorityMixin}</li>\n * <li>{@link DefaultOAuth2UserMixin}</li>\n * <li>{@link OidcIdTokenMixin}</li>\n * <li>{@link OidcUserInfoMixin}</li>\n * <li>{@link OidcUserAuthorityMixin}</li>\n * <li>{@link DefaultOidcUserMixin}</li>\n * <li>{@link OAuth2AuthenticationTokenMixin}</li>\n * <li>{@link OAuth2AuthenticationExceptionMixin}</li>\n * <li>{@link OAuth2ErrorMixin}</li>\n * </ul>\n *\n * If not already enabled, default typing will be automatically enabled as type info is\n * required to properly serialize/deserialize objects. In order to use this module just\n * add it to your {@code ObjectMapper} configuration.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new OAuth2ClientJackson2Module());\n * </pre>\n *\n * <b>NOTE:</b> Use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get a list\n * of all security modules.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see SecurityJackson2Modules\n * @see OAuth2AuthorizationRequestMixin\n * @see ClientRegistrationMixin\n * @see OAuth2AccessTokenMixin\n * @see OAuth2RefreshTokenMixin\n * @see OAuth2AuthorizedClientMixin\n * @see OAuth2UserAuthorityMixin\n * @see DefaultOAuth2UserMixin\n * @see OidcIdTokenMixin\n * @see OidcUserInfoMixin\n * @see OidcUserAuthorityMixin\n * @see DefaultOidcUserMixin\n * @see OAuth2AuthenticationTokenMixin\n * @see OAuth2AuthenticationExceptionMixin\n * @see OAuth2ErrorMixin\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings({ \"serial\", \"removal\" })\npublic class OAuth2ClientJackson2Module extends SimpleModule {\n\n\tpublic OAuth2ClientJackson2Module() {\n\t\tsuper(OAuth2ClientJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tSecurityJackson2Modules.enableDefaultTyping(context.getOwner());\n\t\tcontext.setMixInAnnotations(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);\n\t\tcontext.setMixInAnnotations(ClientRegistration.class, ClientRegistrationMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2AccessToken.class, OAuth2AccessTokenMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2RefreshToken.class, OAuth2RefreshTokenMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2AuthorizedClient.class, OAuth2AuthorizedClientMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2UserAuthority.class, OAuth2UserAuthorityMixin.class);\n\t\tcontext.setMixInAnnotations(DefaultOAuth2User.class, DefaultOAuth2UserMixin.class);\n\t\tcontext.setMixInAnnotations(OidcIdToken.class, OidcIdTokenMixin.class);\n\t\tcontext.setMixInAnnotations(OidcUserInfo.class, OidcUserInfoMixin.class);\n\t\tcontext.setMixInAnnotations(OidcUserAuthority.class, OidcUserAuthorityMixin.class);\n\t\tcontext.setMixInAnnotations(DefaultOidcUser.class, DefaultOidcUserMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2AuthenticationToken.class, OAuth2AuthenticationTokenMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2AuthenticationException.class, OAuth2AuthenticationExceptionMixin.class);\n\t\tcontext.setMixInAnnotations(OAuth2Error.class, OAuth2ErrorMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2ErrorMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2Error} as part of\n * {@link org.springframework.security.oauth2.core.OAuth2AuthenticationException}.\n *\n * @author Dennis Neufeld\n * @since 5.3.4\n * @see OAuth2Error\n * @see OAuth2AuthenticationExceptionMixin\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2ErrorMixin} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2ErrorMixin {\n\n\t@JsonCreator\n\tOAuth2ErrorMixin(@JsonProperty(\"errorCode\") String errorCode, @JsonProperty(\"description\") String description,\n\t\t\t@JsonProperty(\"uri\") String uri) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2RefreshTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.time.Instant;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2RefreshToken}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2RefreshToken\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2RefreshTokenMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2RefreshTokenMixin {\n\n\t@JsonCreator\n\tOAuth2RefreshTokenMixin(@JsonProperty(\"tokenValue\") String tokenValue, @JsonProperty(\"issuedAt\") Instant issuedAt) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OAuth2UserAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OAuth2UserAuthority}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OAuth2UserAuthority\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OAuth2UserAuthorityMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OAuth2UserAuthorityMixin {\n\n\t@JsonCreator\n\tOAuth2UserAuthorityMixin(@JsonProperty(\"authority\") String authority,\n\t\t\t@JsonProperty(\"attributes\") Map<String, Object> attributes) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OidcIdTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.time.Instant;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OidcIdToken}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OidcIdToken\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OidcIdTokenMixin} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OidcIdTokenMixin {\n\n\t@JsonCreator\n\tOidcIdTokenMixin(@JsonProperty(\"tokenValue\") String tokenValue, @JsonProperty(\"issuedAt\") Instant issuedAt,\n\t\t\t@JsonProperty(\"expiresAt\") Instant expiresAt, @JsonProperty(\"claims\") Map<String, Object> claims) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OidcUserAuthorityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OidcUserAuthority}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OidcUserAuthority\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OidcUserAuthorityMixin} based\n * on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(value = { \"attributes\" }, ignoreUnknown = true)\nabstract class OidcUserAuthorityMixin {\n\n\t@JsonCreator\n\tOidcUserAuthorityMixin(@JsonProperty(\"authority\") String authority, @JsonProperty(\"idToken\") OidcIdToken idToken,\n\t\t\t@JsonProperty(\"userInfo\") OidcUserInfo userInfo) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/OidcUserInfoMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\n\n/**\n * This mixin class is used to serialize/deserialize {@link OidcUserInfo}.\n *\n * @author Joe Grandja\n * @since 5.3\n * @see OidcUserInfo\n * @see OAuth2ClientJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.OidcUserInfoMixin} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class OidcUserInfoMixin {\n\n\t@JsonCreator\n\tOidcUserInfoMixin(@JsonProperty(\"claims\") Map<String, Object> claims) {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.util.StdConverter;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.util.Assert;\n\n/**\n * {@code StdConverter} implementations.\n *\n * @author Joe Grandja\n * @since 5.3\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.oauth2.client.jackson.StdConverters} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\nabstract class StdConverters {\n\n\tstatic final class AccessTokenTypeConverter extends StdConverter<JsonNode, OAuth2AccessToken.TokenType> {\n\n\t\t@Override\n\t\tpublic OAuth2AccessToken.@Nullable TokenType convert(JsonNode jsonNode) {\n\t\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\t\tif (value != null && OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn OAuth2AccessToken.TokenType.BEARER;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tstatic final class ClientAuthenticationMethodConverter extends StdConverter<JsonNode, ClientAuthenticationMethod> {\n\n\t\t@Override\n\t\tpublic ClientAuthenticationMethod convert(JsonNode jsonNode) {\n\t\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\t\tAssert.hasText(value, \"value cannot be null or empty\");\n\t\t\treturn ClientAuthenticationMethod.valueOf(value);\n\t\t}\n\n\t}\n\n\tstatic final class AuthorizationGrantTypeConverter extends StdConverter<JsonNode, AuthorizationGrantType> {\n\n\t\t@Override\n\t\tpublic AuthorizationGrantType convert(JsonNode jsonNode) {\n\t\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\t\tAssert.hasText(value, \"value cannot be null or empty\");\n\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthorizationGrantType.AUTHORIZATION_CODE;\n\t\t\t}\n\t\t\tif (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthorizationGrantType.CLIENT_CREDENTIALS;\n\t\t\t}\n\t\t\treturn new AuthorizationGrantType(value);\n\t\t}\n\n\t}\n\n\tstatic final class AuthenticationMethodConverter extends StdConverter<JsonNode, AuthenticationMethod> {\n\n\t\t@Override\n\t\tpublic @Nullable AuthenticationMethod convert(JsonNode jsonNode) {\n\t\t\tString value = JsonNodeUtils.findStringValue(jsonNode, \"value\");\n\t\t\tif (value != null && AuthenticationMethod.HEADER.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthenticationMethod.HEADER;\n\t\t\t}\n\t\t\tif (value != null && AuthenticationMethod.FORM.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthenticationMethod.FORM;\n\t\t\t}\n\t\t\tif (value != null && AuthenticationMethod.QUERY.getValue().equalsIgnoreCase(value)) {\n\t\t\t\treturn AuthenticationMethod.QUERY;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 2 serialization support for OAuth2 client.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/DefaultOidcIdTokenValidatorFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.util.function.Function;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtValidators;\n\n/**\n * @author Joe Grandja\n * @since 5.2\n */\nclass DefaultOidcIdTokenValidatorFactory implements Function<ClientRegistration, OAuth2TokenValidator<Jwt>> {\n\n\t@Override\n\tpublic OAuth2TokenValidator<Jwt> apply(ClientRegistration clientRegistration) {\n\t\treturn JwtValidators.createDefaultWithValidators(new OidcIdTokenValidator(clientRegistration));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AuthenticationProvider} for the OpenID Connect Core 1.0\n * Authorization Code Grant Flow.\n * <p>\n * This {@link AuthenticationProvider} is responsible for authenticating an Authorization\n * Code credential with the Authorization Server's Token Endpoint and if valid, exchanging\n * it for an Access Token credential.\n * <p>\n * It will also obtain the user attributes of the End-User (Resource Owner) from the\n * UserInfo Endpoint using an {@link OAuth2UserService}, which will create a\n * {@code Principal} in the form of an {@link OidcUser}. The {@code OidcUser} is then\n * associated to the {@link OAuth2LoginAuthenticationToken} to complete the\n * authentication.\n *\n * @author Joe Grandja\n * @author Mark Heckler\n * @since 5.0\n * @see OAuth2LoginAuthenticationToken\n * @see OAuth2AccessTokenResponseClient\n * @see OidcUserService\n * @see OidcUser\n * @see OidcIdTokenDecoderFactory\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth\">Section 3.1\n * Authorization Code Grant Flow</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest\">Section 3.1.3.1\n * Token Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse\">Section 3.1.3.3\n * Token Response</a>\n */\npublic class OidcAuthorizationCodeAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String INVALID_STATE_PARAMETER_ERROR_CODE = \"invalid_state_parameter\";\n\n\tprivate static final String INVALID_ID_TOKEN_ERROR_CODE = \"invalid_id_token\";\n\n\tprivate static final String INVALID_NONCE_ERROR_CODE = \"invalid_nonce\";\n\n\tprivate final OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\tprivate final OAuth2UserService<OidcUserRequest, OidcUser> userService;\n\n\tprivate JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new OidcIdTokenDecoderFactory();\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = ((authorities) -> authorities);\n\n\t/**\n\t * Constructs an {@code OidcAuthorizationCodeAuthenticationProvider} using the\n\t * provided parameters.\n\t * @param accessTokenResponseClient the client used for requesting the access token\n\t * credential from the Token Endpoint\n\t * @param userService the service used for obtaining the user attributes of the\n\t * End-User from the UserInfo Endpoint\n\t */\n\tpublic OidcAuthorizationCodeAuthenticationProvider(\n\t\t\tOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,\n\t\t\tOAuth2UserService<OidcUserRequest, OidcUser> userService) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tAssert.notNull(userService, \"userService cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t\tthis.userService = userService;\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken) authentication;\n\t\t// Section 3.1.2.1 Authentication Request -\n\t\t// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\n\t\t// scope\n\t\t// REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope value.\n\t\tif (!authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest()\n\t\t\t.getScopes()\n\t\t\t.contains(OidcScopes.OPENID)) {\n\t\t\t// This is NOT an OpenID Connect Authentication Request so return null\n\t\t\t// and let OAuth2LoginAuthenticationProvider handle it instead\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest();\n\t\tOAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationResponse();\n\t\tif (authorizationResponse.statusError()) {\n\t\t\tOAuth2Error error = authorizationResponse.getError();\n\t\t\tAssert.notNull(error, \"error cannot be null when status is error\");\n\t\t\tthrow new OAuth2AuthenticationException(error, error.toString());\n\t\t}\n\t\tif (!Objects.equals(authorizationResponse.getState(), authorizationRequest.getState())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t\tOAuth2AccessTokenResponse accessTokenResponse = getResponse(authorizationCodeAuthentication);\n\t\tClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();\n\t\tMap<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();\n\t\tif (!additionalParameters.containsKey(OidcParameterNames.ID_TOKEN)) {\n\t\t\tOAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE,\n\t\t\t\t\t\"Missing (required) ID Token in Token Response for Client Registration: \"\n\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId(),\n\t\t\t\t\tnull);\n\t\t\tthrow new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString());\n\t\t}\n\t\tOidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);\n\t\tvalidateNonce(authorizationRequest, idToken);\n\t\tOidcUser oidcUser = this.userService.loadUser(new OidcUserRequest(clientRegistration,\n\t\t\t\taccessTokenResponse.getAccessToken(), idToken, additionalParameters));\n\t\tAssert.notNull(oidcUser, \"oidcUser cannot be null\");\n\t\tCollection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper\n\t\t\t.mapAuthorities(oidcUser.getAuthorities());\n\t\tOAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(\n\t\t\t\tauthorizationCodeAuthentication.getClientRegistration(),\n\t\t\t\tauthorizationCodeAuthentication.getAuthorizationExchange(), oidcUser, mappedAuthorities,\n\t\t\t\taccessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken());\n\t\tauthenticationResult.setDetails(authorizationCodeAuthentication.getDetails());\n\t\treturn authenticationResult;\n\t}\n\n\tprivate OAuth2AccessTokenResponse getResponse(OAuth2LoginAuthenticationToken authorizationCodeAuthentication) {\n\t\ttry {\n\t\t\treturn this.accessTokenResponseClient.getTokenResponse(\n\t\t\t\t\tnew OAuth2AuthorizationCodeGrantRequest(authorizationCodeAuthentication.getClientRegistration(),\n\t\t\t\t\t\t\tauthorizationCodeAuthentication.getAuthorizationExchange()));\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tOAuth2Error oauth2Error = ex.getError();\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);\n\t\t}\n\t}\n\n\tprivate void validateNonce(OAuth2AuthorizationRequest authorizationRequest, OidcIdToken idToken) {\n\t\tString requestNonce = authorizationRequest.getAttribute(OidcParameterNames.NONCE);\n\t\tif (requestNonce == null) {\n\t\t\treturn;\n\t\t}\n\t\tString nonceHash = getNonceHash(requestNonce);\n\t\tString nonceHashClaim = idToken.getNonce();\n\t\tif (nonceHashClaim == null || !nonceHashClaim.equals(nonceHash)) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate String getNonceHash(String requestNonce) {\n\t\ttry {\n\t\t\treturn createHash(requestNonce);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link JwtDecoderFactory} used for {@link OidcIdToken} signature\n\t * verification. The factory returns a {@link JwtDecoder} associated to the provided\n\t * {@link ClientRegistration}.\n\t * @param jwtDecoderFactory the {@link JwtDecoderFactory} used for {@link OidcIdToken}\n\t * signature verification\n\t * @since 5.2\n\t */\n\tpublic final void setJwtDecoderFactory(JwtDecoderFactory<ClientRegistration> jwtDecoderFactory) {\n\t\tAssert.notNull(jwtDecoderFactory, \"jwtDecoderFactory cannot be null\");\n\t\tthis.jwtDecoderFactory = jwtDecoderFactory;\n\t}\n\n\t/**\n\t * Sets the {@link GrantedAuthoritiesMapper} used for mapping\n\t * {@link OidcUser#getAuthorities()}} to a new set of authorities which will be\n\t * associated to the {@link OAuth2LoginAuthenticationToken}.\n\t * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the\n\t * user's authorities\n\t */\n\tpublic final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tAssert.notNull(authoritiesMapper, \"authoritiesMapper cannot be null\");\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tprivate OidcIdToken createOidcToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AccessTokenResponse accessTokenResponse) {\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);\n\t\tJwt jwt = getJwt(accessTokenResponse, jwtDecoder);\n\t\tOidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(),\n\t\t\t\tjwt.getClaims());\n\t\treturn idToken;\n\t}\n\n\tprivate Jwt getJwt(OAuth2AccessTokenResponse accessTokenResponse, JwtDecoder jwtDecoder) {\n\t\ttry {\n\t\t\tMap<String, Object> parameters = accessTokenResponse.getAdditionalParameters();\n\t\t\tString idToken = (String) parameters.get(OidcParameterNames.ID_TOKEN);\n\t\t\tAssert.hasText(idToken, \"id_token parameter cannot be null or empty\");\n\t\t\treturn jwtDecoder.decode(idToken);\n\t\t}\n\t\tcatch (JwtException ex) {\n\t\t\tOAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(), null);\n\t\t\tthrow new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex);\n\t\t}\n\t}\n\n\tstatic String createHash(String nonce) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(nonce.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an\n * {@link org.springframework.security.authentication.AuthenticationProvider} for OAuth\n * 2.0 Login, which leverages the OAuth 2.0 Authorization Code Grant Flow.\n * <p>\n * This {@link org.springframework.security.authentication.AuthenticationProvider} is\n * responsible for authenticating an Authorization Code credential with the Authorization\n * Server's Token Endpoint and if valid, exchanging it for an Access Token credential.\n * <p>\n * It will also obtain the user attributes of the End-User (Resource Owner) from the\n * UserInfo Endpoint using an\n * {@link org.springframework.security.oauth2.client.userinfo.OAuth2UserService}, which\n * will create a {@code Principal} in the form of an {@link OAuth2User}. The\n * {@code OAuth2User} is then associated to the {@link OAuth2LoginAuthenticationToken} to\n * complete the authentication.\n *\n * @author Rob Winch\n * @author Mark Heckler\n * @since 5.1\n * @see OAuth2LoginAuthenticationToken\n * @see ReactiveOAuth2AccessTokenResponseClient\n * @see ReactiveOAuth2UserService\n * @see OAuth2User\n * @see ReactiveOidcIdTokenDecoderFactory\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant Flow</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.3\">Section 4.1.3 Access Token\n * Request</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.4\">Section 4.1.4 Access Token\n * Response</a>\n */\npublic class OidcAuthorizationCodeReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate static final String INVALID_STATE_PARAMETER_ERROR_CODE = \"invalid_state_parameter\";\n\n\tprivate static final String INVALID_ID_TOKEN_ERROR_CODE = \"invalid_id_token\";\n\n\tprivate static final String INVALID_NONCE_ERROR_CODE = \"invalid_nonce\";\n\n\tprivate final ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\tprivate final ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService;\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = ((authorities) -> authorities);\n\n\tprivate ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();\n\n\tpublic OidcAuthorizationCodeReactiveAuthenticationManager(\n\t\t\tReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient,\n\t\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService) {\n\t\tAssert.notNull(accessTokenResponseClient, \"accessTokenResponseClient cannot be null\");\n\t\tAssert.notNull(userService, \"userService cannot be null\");\n\t\tthis.accessTokenResponseClient = accessTokenResponseClient;\n\t\tthis.userService = userService;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\treturn Mono.defer(() -> {\n\t\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication;\n\t\t\t// Section 3.1.2.1 Authentication Request -\n\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\n\t\t\t// scope REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope\n\t\t\t// value.\n\t\t\tif (!authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t\t.getAuthorizationRequest()\n\t\t\t\t.getScopes()\n\t\t\t\t.contains(\"openid\")) {\n\t\t\t\t// This is an OpenID Connect Authentication Request so return empty\n\t\t\t\t// and let OAuth2LoginReactiveAuthenticationManager handle it instead\n\t\t\t\treturn Mono.empty();\n\t\t\t}\n\t\t\tOAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t\t.getAuthorizationRequest();\n\t\t\tOAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication\n\t\t\t\t.getAuthorizationExchange()\n\t\t\t\t.getAuthorizationResponse();\n\t\t\tif (authorizationResponse.statusError()) {\n\t\t\t\tOAuth2Error error = authorizationResponse.getError();\n\t\t\t\tAssert.notNull(error, \"error cannot be null when status is error\");\n\t\t\t\treturn Mono.error(new OAuth2AuthenticationException(error, error.toString()));\n\t\t\t}\n\t\t\tif (!Objects.equals(authorizationResponse.getState(), authorizationRequest.getState())) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_STATE_PARAMETER_ERROR_CODE);\n\t\t\t\treturn Mono.error(new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()));\n\t\t\t}\n\t\t\tOAuth2AuthorizationCodeGrantRequest authzRequest = new OAuth2AuthorizationCodeGrantRequest(\n\t\t\t\t\tauthorizationCodeAuthentication.getClientRegistration(),\n\t\t\t\t\tauthorizationCodeAuthentication.getAuthorizationExchange());\n\t\t\treturn this.accessTokenResponseClient.getTokenResponse(authzRequest)\n\t\t\t\t.flatMap((accessTokenResponse) -> authenticationResult(authorizationCodeAuthentication,\n\t\t\t\t\t\taccessTokenResponse))\n\t\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t\t(e) -> new OAuth2AuthenticationException(e.getError(), e.getError().toString(), e))\n\t\t\t\t.onErrorMap(JwtException.class, (e) -> {\n\t\t\t\t\tOAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, e.getMessage(),\n\t\t\t\t\t\t\tnull);\n\t\t\t\t\treturn new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), e);\n\t\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Sets the {@link ReactiveJwtDecoderFactory} used for {@link OidcIdToken} signature\n\t * verification. The factory returns a {@link ReactiveJwtDecoder} associated to the\n\t * provided {@link ClientRegistration}.\n\t * @param jwtDecoderFactory the {@link ReactiveJwtDecoderFactory} used for\n\t * {@link OidcIdToken} signature verification\n\t * @since 5.2\n\t */\n\tpublic final void setJwtDecoderFactory(ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory) {\n\t\tAssert.notNull(jwtDecoderFactory, \"jwtDecoderFactory cannot be null\");\n\t\tthis.jwtDecoderFactory = jwtDecoderFactory;\n\t}\n\n\t/**\n\t * Sets the {@link GrantedAuthoritiesMapper} used for mapping\n\t * {@link OidcUser#getAuthorities()} to a new set of authorities which will be\n\t * associated to the {@link OAuth2LoginAuthenticationToken}.\n\t * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the\n\t * user's authorities\n\t * @since 5.4\n\t */\n\tpublic final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tAssert.notNull(authoritiesMapper, \"authoritiesMapper cannot be null\");\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\tprivate Mono<OAuth2LoginAuthenticationToken> authenticationResult(\n\t\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication,\n\t\t\tOAuth2AccessTokenResponse accessTokenResponse) {\n\t\tOAuth2AccessToken accessToken = accessTokenResponse.getAccessToken();\n\t\tClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();\n\t\tMap<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();\n\t\tif (!additionalParameters.containsKey(OidcParameterNames.ID_TOKEN)) {\n\t\t\tOAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE,\n\t\t\t\t\t\"Missing (required) ID Token in Token Response for Client Registration: \"\n\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId(),\n\t\t\t\t\tnull);\n\t\t\treturn Mono.error(new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString()));\n\t\t}\n\t\t// @formatter:off\n\t\treturn createOidcToken(clientRegistration, accessTokenResponse)\n\t\t\t\t.doOnNext((idToken) -> validateNonce(authorizationCodeAuthentication, idToken))\n\t\t\t\t.map((idToken) -> new OidcUserRequest(clientRegistration, accessToken, idToken, additionalParameters))\n\t\t\t\t.flatMap(this.userService::loadUser)\n\t\t\t\t.map((oauth2User) -> {\n\t\t\t\t\tCollection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper\n\t\t\t\t\t\t\t.mapAuthorities(oauth2User.getAuthorities());\n\t\t\t\t\treturn new OAuth2LoginAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(),\n\t\t\t\t\t\t\tauthorizationCodeAuthentication.getAuthorizationExchange(), oauth2User, mappedAuthorities,\n\t\t\t\t\t\t\taccessToken, accessTokenResponse.getRefreshToken());\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<OidcIdToken> createOidcToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AccessTokenResponse accessTokenResponse) {\n\t\tReactiveJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);\n\t\tString rawIdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);\n\t\tAssert.hasText(rawIdToken, \"id_token parameter cannot be null or empty\");\n\t\t// @formatter:off\n\t\treturn jwtDecoder.decode(rawIdToken)\n\t\t\t\t.map((jwt) ->\n\t\t\t\t\t\tnew OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims())\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate static Mono<OidcIdToken> validateNonce(\n\t\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication, OidcIdToken idToken) {\n\t\tString requestNonce = authorizationCodeAuthentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest()\n\t\t\t.getAttribute(OidcParameterNames.NONCE);\n\t\tif (requestNonce != null) {\n\t\t\tString nonceHash = getNonceHash(requestNonce);\n\t\t\tString nonceHashClaim = idToken.getNonce();\n\t\t\tif (nonceHashClaim == null || !nonceHashClaim.equals(nonceHash)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t}\n\n\t\treturn Mono.just(idToken);\n\t}\n\n\tprivate static String getNonceHash(String requestNonce) {\n\t\ttry {\n\t\t\treturn createHash(requestNonce);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tstatic String createHash(String nonce) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(nonce.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizedClientRefreshedEventListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.net.URL;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.event.OAuth2AuthorizedClientRefreshedEvent;\nimport org.springframework.security.oauth2.client.oidc.authentication.event.OidcUserRefreshedEvent;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link ApplicationListener} that listens for events of type\n * {@link OAuth2AuthorizedClientRefreshedEvent} and publishes an event of type\n * {@link OidcUserRefreshedEvent} in order to refresh an {@link OidcUser}.\n *\n * @author Steve Riesenberg\n * @since 6.5\n * @see org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider\n * @see OAuth2AuthorizedClientRefreshedEvent\n * @see OidcUserRefreshedEvent\n */\npublic final class OidcAuthorizedClientRefreshedEventListener\n\t\timplements ApplicationEventPublisherAware, ApplicationListener<OAuth2AuthorizedClientRefreshedEvent> {\n\n\tprivate static final String INVALID_ID_TOKEN_ERROR_CODE = \"invalid_id_token\";\n\n\tprivate static final String INVALID_NONCE_ERROR_CODE = \"invalid_nonce\";\n\n\tprivate static final String REFRESH_TOKEN_RESPONSE_ERROR_URI = \"https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse\";\n\n\tprivate OAuth2UserService<OidcUserRequest, OidcUser> userService = new OidcUserService();\n\n\tprivate JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new OidcIdTokenDecoderFactory();\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = (authorities) -> authorities;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable ApplicationEventPublisher applicationEventPublisher;\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\t@Override\n\tpublic void onApplicationEvent(OAuth2AuthorizedClientRefreshedEvent event) {\n\t\tif (this.applicationEventPublisher == null) {\n\t\t\treturn;\n\t\t}\n\n\t\t// The response must contain the openid scope\n\t\tOAuth2AccessTokenResponse accessTokenResponse = event.getAccessTokenResponse();\n\t\tif (!accessTokenResponse.getAccessToken().getScopes().contains(OidcScopes.OPENID)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// The response must contain an id_token\n\t\tMap<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();\n\t\tif (!StringUtils.hasText((String) additionalParameters.get(OidcParameterNames.ID_TOKEN))) {\n\t\t\treturn;\n\t\t}\n\n\t\t// The current authentication must be an OAuth2AuthenticationToken\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (!(authentication instanceof OAuth2AuthenticationToken authenticationToken)\n\t\t\t\t|| authenticationToken.getClass() != OAuth2AuthenticationToken.class) {\n\t\t\t// This event listener only handles the default authentication result. If the\n\t\t\t// application customizes the authentication result, then a custom event\n\t\t\t// handler should be provided.\n\t\t\treturn;\n\t\t}\n\n\t\t// The current principal must be an OidcUser\n\t\tif (!(authenticationToken.getPrincipal() instanceof OidcUser existingOidcUser)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// The registrationId must match the one used to log in\n\t\tClientRegistration clientRegistration = event.getAuthorizedClient().getClientRegistration();\n\t\tif (!authenticationToken.getAuthorizedClientRegistrationId().equals(clientRegistration.getRegistrationId())) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Refresh the OidcUser and send a user refreshed event\n\t\tOidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);\n\t\tvalidateIdToken(existingOidcUser, idToken);\n\t\tOidcUserRequest userRequest = new OidcUserRequest(clientRegistration, accessTokenResponse.getAccessToken(),\n\t\t\t\tidToken, additionalParameters);\n\t\tOidcUser oidcUser = this.userService.loadUser(userRequest);\n\t\tAssert.notNull(oidcUser, \"oidcUser cannot be null\");\n\t\tCollection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper\n\t\t\t.mapAuthorities(oidcUser.getAuthorities());\n\t\tOAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(oidcUser, mappedAuthorities,\n\t\t\t\tclientRegistration.getRegistrationId());\n\t\tauthenticationResult.setDetails(authenticationToken.getDetails());\n\t\tOidcUserRefreshedEvent oidcUserRefreshedEvent = new OidcUserRefreshedEvent(accessTokenResponse,\n\t\t\t\texistingOidcUser, oidcUser, authenticationResult);\n\t\tthis.applicationEventPublisher.publishEvent(oidcUserRefreshedEvent);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Sets the {@link JwtDecoderFactory} used for {@link OidcIdToken} signature\n\t * verification. The factory returns a {@link JwtDecoder} associated to the provided\n\t * {@link ClientRegistration}.\n\t * @param jwtDecoderFactory the {@link JwtDecoderFactory} used for {@link OidcIdToken}\n\t * signature verification\n\t */\n\tpublic void setJwtDecoderFactory(JwtDecoderFactory<ClientRegistration> jwtDecoderFactory) {\n\t\tAssert.notNull(jwtDecoderFactory, \"jwtDecoderFactory cannot be null\");\n\t\tthis.jwtDecoderFactory = jwtDecoderFactory;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2UserService} used for obtaining the user attributes of the\n\t * End-User from the UserInfo Endpoint.\n\t * @param userService the service used for obtaining the user attributes of the\n\t * End-User from the UserInfo Endpoint\n\t */\n\tpublic void setUserService(OAuth2UserService<OidcUserRequest, OidcUser> userService) {\n\t\tAssert.notNull(userService, \"userService cannot be null\");\n\t\tthis.userService = userService;\n\t}\n\n\t/**\n\t * Sets the {@link GrantedAuthoritiesMapper} used for mapping\n\t * {@link OidcUser#getAuthorities()}} to a new set of authorities which will be\n\t * associated to the {@link OAuth2LoginAuthenticationToken}.\n\t * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the\n\t * user's authorities\n\t */\n\tpublic void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tAssert.notNull(authoritiesMapper, \"authoritiesMapper cannot be null\");\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\t/**\n\t * Sets the {@link ApplicationEventPublisher} to be used.\n\t * @param applicationEventPublisher event publisher to be used\n\t */\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tAssert.notNull(applicationEventPublisher, \"applicationEventPublisher cannot be null\");\n\t\tthis.applicationEventPublisher = applicationEventPublisher;\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew, which is used when checking the\n\t * {@link OidcIdToken#getIssuedAt() issuedAt} time. The default is 60 seconds.\n\t * @param clockSkew the maximum acceptable clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\tprivate OidcIdToken createOidcToken(ClientRegistration clientRegistration,\n\t\t\tOAuth2AccessTokenResponse accessTokenResponse) {\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration);\n\t\tJwt jwt = getJwt(accessTokenResponse, jwtDecoder);\n\t\treturn new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims());\n\t}\n\n\tprivate Jwt getJwt(OAuth2AccessTokenResponse accessTokenResponse, JwtDecoder jwtDecoder) {\n\t\ttry {\n\t\t\tMap<String, Object> parameters = accessTokenResponse.getAdditionalParameters();\n\t\t\tString idToken = (String) parameters.get(OidcParameterNames.ID_TOKEN);\n\t\t\tAssert.hasText(idToken, \"id_token parameter cannot be null or empty\");\n\t\t\treturn jwtDecoder.decode(idToken);\n\t\t}\n\t\tcatch (JwtException ex) {\n\t\t\tOAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(), null);\n\t\t\tthrow new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex);\n\t\t}\n\t}\n\n\tprivate void validateIdToken(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\t// OpenID Connect Core 1.0 - Section 12.2 Successful Refresh Response\n\t\t// If an ID Token is returned as a result of a token refresh request, the\n\t\t// following requirements apply:\n\t\t// its iss Claim Value MUST be the same as in the ID Token issued when the\n\t\t// original authentication occurred,\n\t\tvalidateIssuer(existingOidcUser, idToken);\n\t\t// its sub Claim Value MUST be the same as in the ID Token issued when the\n\t\t// original authentication occurred,\n\t\tvalidateSubject(existingOidcUser, idToken);\n\t\t// its iat Claim MUST represent the time that the new ID Token is issued,\n\t\tvalidateIssuedAt(existingOidcUser, idToken);\n\t\t// its aud Claim Value MUST be the same as in the ID Token issued when the\n\t\t// original authentication occurred,\n\t\tvalidateAudience(existingOidcUser, idToken);\n\t\t// if the ID Token contains an auth_time Claim, its value MUST represent the time\n\t\t// of the original authentication - not the time that the new ID token is issued,\n\t\tvalidateAuthenticatedAt(existingOidcUser, idToken);\n\t\t// it SHOULD NOT have a nonce Claim, even when the ID Token issued at the time of\n\t\t// the original authentication contained nonce; however, if it is present, its\n\t\t// value MUST be the same as in the ID Token issued at the time of the original\n\t\t// authentication,\n\t\tvalidateNonce(existingOidcUser, idToken);\n\t}\n\n\tprivate void validateIssuer(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tURL idTokenIssuer = idToken.getIssuer();\n\t\tURL existingIdTokenIssuer = existingOidcUser.getIdToken().getIssuer();\n\t\tif (idTokenIssuer == null || existingIdTokenIssuer == null\n\t\t\t\t|| !idTokenIssuer.toString().equals(existingIdTokenIssuer.toString())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid issuer\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate void validateSubject(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tString idTokenSubject = idToken.getSubject();\n\t\tString existingIdTokenSubject = existingOidcUser.getIdToken().getSubject();\n\t\tif (idTokenSubject == null || existingIdTokenSubject == null\n\t\t\t\t|| !idTokenSubject.equals(existingIdTokenSubject)) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid subject\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate void validateIssuedAt(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tInstant idTokenIssuedAt = idToken.getIssuedAt();\n\t\tInstant existingIdTokenIssuedAt = existingOidcUser.getIdToken().getIssuedAt();\n\t\tif (idTokenIssuedAt == null || existingIdTokenIssuedAt == null\n\t\t\t\t|| !idTokenIssuedAt.isAfter(existingIdTokenIssuedAt.minus(this.clockSkew))) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid issued at time\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate void validateAudience(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tif (!isValidAudience(existingOidcUser, idToken)) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid audience\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate boolean isValidAudience(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tList<String> idTokenAudiences = idToken.getAudience();\n\t\tList<String> existingIdTokenAudiences = existingOidcUser.getIdToken().getAudience();\n\t\tif (idTokenAudiences == null || existingIdTokenAudiences == null\n\t\t\t\t|| idTokenAudiences.size() != existingIdTokenAudiences.size()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (String audience : idTokenAudiences) {\n\t\t\tif (!existingIdTokenAudiences.contains(audience)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void validateAuthenticatedAt(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tif (idToken.getAuthenticatedAt() == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!Objects.equals(idToken.getAuthenticatedAt(), existingOidcUser.getIdToken().getAuthenticatedAt())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, \"Invalid authenticated at time\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n\tprivate void validateNonce(OidcUser existingOidcUser, OidcIdToken idToken) {\n\t\tif (!StringUtils.hasText(idToken.getNonce())) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!Objects.equals(idToken.getNonce(), existingOidcUser.getIdToken().getNonce())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_NONCE_ERROR_CODE, \"Invalid nonce\",\n\t\t\t\t\tREFRESH_TOKEN_RESPONSE_ERROR_URI);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenDecoderFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.net.URL;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.JwtTimestampValidator;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder} used for\n * {@link OidcIdToken} signature verification. The provided {@link JwtDecoder} is\n * associated to a specific {@link ClientRegistration}.\n *\n * @author Joe Grandja\n * @author Rafael Dominguez\n * @author Mark Heckler\n * @since 5.2\n * @see JwtDecoderFactory\n * @see ClientRegistration\n * @see OidcIdToken\n */\npublic final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<ClientRegistration> {\n\n\tprivate static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = \"missing_signature_verifier\";\n\n\tprivate static final Map<JwsAlgorithm, String> JCA_ALGORITHM_MAPPINGS;\n\tstatic {\n\t\tMap<JwsAlgorithm, String> mappings = new HashMap<>();\n\t\tmappings.put(MacAlgorithm.HS256, \"HmacSHA256\");\n\t\tmappings.put(MacAlgorithm.HS384, \"HmacSHA384\");\n\t\tmappings.put(MacAlgorithm.HS512, \"HmacSHA512\");\n\t\tJCA_ALGORITHM_MAPPINGS = Collections.unmodifiableMap(mappings);\n\t};\n\n\tprivate static final ClaimTypeConverter DEFAULT_CLAIM_TYPE_CONVERTER = createDefaultClaimTypeConverter();\n\n\tprivate Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = new DefaultOidcIdTokenValidatorFactory();\n\n\tprivate Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver = (\n\t\t\tclientRegistration) -> SignatureAlgorithm.RS256;\n\n\tprivate Function<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory = (\n\t\t\tclientRegistration) -> DEFAULT_CLAIM_TYPE_CONVERTER;\n\n\t/**\n\t * Returns the default {@link Converter}'s used for type conversion of claim values\n\t * for an {@link OidcIdToken}.\n\t * @return a {@link Map} of {@link Converter}'s keyed by {@link IdTokenClaimNames\n\t * claim name}\n\t * @since 6.3\n\t */\n\tpublic static ClaimTypeConverter createDefaultClaimTypeConverter() {\n\t\treturn new ClaimTypeConverter(createDefaultClaimTypeConverters());\n\t}\n\n\t/**\n\t * Returns the default {@link Converter}'s used for type conversion of claim values\n\t * for an {@link OidcIdToken}.\n\t * @return a {@link Map} of {@link Converter}'s keyed by {@link IdTokenClaimNames\n\t * claim name}\n\t */\n\tpublic static Map<String, Converter<Object, ?>> createDefaultClaimTypeConverters() {\n\t\tConverter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));\n\t\tConverter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));\n\t\tConverter<Object, ?> urlConverter = getConverter(TypeDescriptor.valueOf(URL.class));\n\t\tConverter<Object, ?> stringConverter = getConverter(TypeDescriptor.valueOf(String.class));\n\t\tConverter<Object, ?> collectionStringConverter = getConverter(\n\t\t\t\tTypeDescriptor.collection(Collection.class, TypeDescriptor.valueOf(String.class)));\n\t\tMap<String, Converter<Object, ?>> converters = new HashMap<>();\n\t\tconverters.put(IdTokenClaimNames.ISS, urlConverter);\n\t\tconverters.put(IdTokenClaimNames.AUD, collectionStringConverter);\n\t\tconverters.put(IdTokenClaimNames.NONCE, stringConverter);\n\t\tconverters.put(IdTokenClaimNames.EXP, instantConverter);\n\t\tconverters.put(IdTokenClaimNames.IAT, instantConverter);\n\t\tconverters.put(IdTokenClaimNames.AUTH_TIME, instantConverter);\n\t\tconverters.put(IdTokenClaimNames.AMR, collectionStringConverter);\n\t\tconverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);\n\t\tconverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);\n\t\tconverters.put(StandardClaimNames.UPDATED_AT, instantConverter);\n\t\treturn converters;\n\t}\n\n\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\tTypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);\n\t\treturn (source) -> ClaimConversionService.getSharedInstance()\n\t\t\t.convert(source, sourceDescriptor, targetDescriptor);\n\t}\n\n\t@Override\n\tpublic JwtDecoder createDecoder(ClientRegistration clientRegistration) {\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tNimbusJwtDecoder jwtDecoder = buildDecoder(clientRegistration);\n\t\tjwtDecoder.setJwtValidator(this.jwtValidatorFactory.apply(clientRegistration));\n\t\tConverter<Map<String, Object>, Map<String, Object>> claimTypeConverter = this.claimTypeConverterFactory\n\t\t\t.apply(clientRegistration);\n\t\tif (claimTypeConverter != null) {\n\t\t\tjwtDecoder.setClaimSetConverter(claimTypeConverter);\n\t\t}\n\t\treturn jwtDecoder;\n\t}\n\n\tprivate NimbusJwtDecoder buildDecoder(ClientRegistration clientRegistration) {\n\t\tJwsAlgorithm jwsAlgorithm = this.jwsAlgorithmResolver.apply(clientRegistration);\n\t\tif (jwsAlgorithm != null && SignatureAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {\n\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation\n\t\t\t//\n\t\t\t// 6. If the ID Token is received via direct communication between the Client\n\t\t\t// and the Token Endpoint (which it is in this flow),\n\t\t\t// the TLS server validation MAY be used to validate the issuer in place of\n\t\t\t// checking the token signature.\n\t\t\t// The Client MUST validate the signature of all other ID Tokens according to\n\t\t\t// JWS [JWS]\n\t\t\t// using the algorithm specified in the JWT alg Header Parameter.\n\t\t\t// The Client MUST use the keys provided by the Issuer.\n\t\t\t//\n\t\t\t// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by\n\t\t\t// the Client\n\t\t\t// in the id_token_signed_response_alg parameter during Registration.\n\n\t\t\tString jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();\n\t\t\tif (!StringUtils.hasText(jwkSetUri)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,\n\t\t\t\t\t\t\"Failed to find a Signature Verifier for Client Registration: '\"\n\t\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId()\n\t\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured the JwkSet URI.\",\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\treturn NimbusJwtDecoder.withJwkSetUri(jwkSetUri).jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm).build();\n\t\t}\n\t\tif (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {\n\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation\n\t\t\t//\n\t\t\t// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as\n\t\t\t// HS256, HS384, or HS512,\n\t\t\t// the octets of the UTF-8 representation of the client_secret\n\t\t\t// corresponding to the client_id contained in the aud (audience) Claim\n\t\t\t// are used as the key to validate the signature.\n\t\t\t// For MAC based algorithms, the behavior is unspecified if the aud is\n\t\t\t// multi-valued or\n\t\t\t// if an azp value is present that is different than the aud value.\n\t\t\tString clientSecret = clientRegistration.getClientSecret();\n\t\t\tif (!StringUtils.hasText(clientSecret)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,\n\t\t\t\t\t\t\"Failed to find a Signature Verifier for Client Registration: '\"\n\t\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId()\n\t\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured the client secret.\",\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\tSecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\tJCA_ALGORITHM_MAPPINGS.get(jwsAlgorithm));\n\t\t\treturn NimbusJwtDecoder.withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();\n\t\t}\n\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,\n\t\t\t\t\"Failed to find a Signature Verifier for Client Registration: '\"\n\t\t\t\t\t\t+ clientRegistration.getRegistrationId()\n\t\t\t\t\t\t+ \"'. Check to ensure you have configured a valid JWS Algorithm: '\" + jwsAlgorithm + \"'\",\n\t\t\t\tnull);\n\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t}\n\n\t/**\n\t * Sets the factory that provides an {@link OAuth2TokenValidator}, which is used by\n\t * the {@link JwtDecoder}. The default composes {@link JwtTimestampValidator} and\n\t * {@link OidcIdTokenValidator}.\n\t * @param jwtValidatorFactory the factory that provides an\n\t * {@link OAuth2TokenValidator}\n\t */\n\tpublic void setJwtValidatorFactory(Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {\n\t\tAssert.notNull(jwtValidatorFactory, \"jwtValidatorFactory cannot be null\");\n\t\tthis.jwtValidatorFactory = jwtValidatorFactory;\n\t}\n\n\t/**\n\t * Sets the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}\n\t * used for the signature or MAC on the {@link OidcIdToken ID Token}. The default\n\t * resolves to {@link SignatureAlgorithm#RS256 RS256} for all\n\t * {@link ClientRegistration clients}.\n\t * @param jwsAlgorithmResolver the resolver that provides the expected\n\t * {@link JwsAlgorithm JWS algorithm} for a specific {@link ClientRegistration client}\n\t */\n\tpublic void setJwsAlgorithmResolver(Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver) {\n\t\tAssert.notNull(jwsAlgorithmResolver, \"jwsAlgorithmResolver cannot be null\");\n\t\tthis.jwsAlgorithmResolver = jwsAlgorithmResolver;\n\t}\n\n\t/**\n\t * Sets the factory that provides a {@link Converter} used for type conversion of\n\t * claim values for an {@link OidcIdToken}. The default is {@link ClaimTypeConverter}\n\t * for all {@link ClientRegistration clients}.\n\t * @param claimTypeConverterFactory the factory that provides a {@link Converter} used\n\t * for type conversion of claim values for a specific {@link ClientRegistration\n\t * client}\n\t */\n\tpublic void setClaimTypeConverterFactory(\n\t\t\tFunction<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory) {\n\t\tAssert.notNull(claimTypeConverterFactory, \"claimTypeConverterFactory cannot be null\");\n\t\tthis.claimTypeConverterFactory = claimTypeConverterFactory;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.net.URL;\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * An {@link OAuth2TokenValidator} responsible for validating the claims in an\n * {@link OidcIdToken ID Token}.\n *\n * @author Rob Winch\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2TokenValidator\n * @see Jwt\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation\">ID Token\n * Validation</a>\n */\npublic final class OidcIdTokenValidator implements OAuth2TokenValidator<Jwt> {\n\n\tprivate static final Duration DEFAULT_CLOCK_SKEW = Duration.ofSeconds(60);\n\n\tprivate final ClientRegistration clientRegistration;\n\n\tprivate Duration clockSkew = DEFAULT_CLOCK_SKEW;\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\tpublic OidcIdTokenValidator(ClientRegistration clientRegistration) {\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tthis.clientRegistration = clientRegistration;\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt idToken) {\n\t\t// 3.1.3.7 ID Token Validation\n\t\t// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation\n\t\tMap<String, Object> invalidClaims = validateRequiredClaims(idToken);\n\t\tif (!invalidClaims.isEmpty()) {\n\t\t\treturn OAuth2TokenValidatorResult.failure(invalidIdToken(invalidClaims));\n\t\t}\n\t\t// 2. The Issuer Identifier for the OpenID Provider (which is typically obtained\n\t\t// during Discovery)\n\t\t// MUST exactly match the value of the iss (issuer) Claim.\n\t\tString metadataIssuer = this.clientRegistration.getProviderDetails().getIssuerUri();\n\t\tURL issuer = idToken.getIssuer();\n\t\tif (metadataIssuer != null && issuer != null && !Objects.equals(metadataIssuer, issuer.toExternalForm())) {\n\t\t\tinvalidClaims.put(IdTokenClaimNames.ISS, issuer);\n\t\t}\n\t\t// 3. The Client MUST validate that the aud (audience) Claim contains its\n\t\t// client_id value\n\t\t// registered at the Issuer identified by the iss (issuer) Claim as an audience.\n\t\t// The aud (audience) Claim MAY contain an array with more than one element.\n\t\t// The ID Token MUST be rejected if the ID Token does not list the Client as a\n\t\t// valid audience,\n\t\t// or if it contains additional audiences not trusted by the Client.\n\t\tList<String> audience = idToken.getAudience();\n\t\tif (audience == null || !audience.contains(this.clientRegistration.getClientId())) {\n\t\t\tinvalidClaims.put(IdTokenClaimNames.AUD, audience);\n\t\t}\n\t\t// 4. If the ID Token contains multiple audiences,\n\t\t// the Client SHOULD verify that an azp Claim is present.\n\t\tString authorizedParty = idToken.getClaimAsString(IdTokenClaimNames.AZP);\n\t\tif (audience != null && audience.size() > 1 && authorizedParty == null) {\n\t\t\tinvalidClaims.put(IdTokenClaimNames.AZP, authorizedParty);\n\t\t}\n\t\t// 5. If an azp (authorized party) Claim is present,\n\t\t// the Client SHOULD verify that its client_id is the Claim Value.\n\t\tif (authorizedParty != null && !authorizedParty.equals(this.clientRegistration.getClientId())) {\n\t\t\tinvalidClaims.put(IdTokenClaimNames.AZP, authorizedParty);\n\t\t}\n\t\t// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by the\n\t\t// Client\n\t\t// in the id_token_signed_response_alg parameter during Registration.\n\t\t// TODO Depends on gh-4413\n\t\t// 9. The current time MUST be before the time represented by the exp Claim.\n\t\tInstant now = Instant.now(this.clock);\n\t\tInstant expiresAt = idToken.getExpiresAt();\n\t\tif (expiresAt != null && now.minus(this.clockSkew).isAfter(expiresAt)) {\n\t\t\tinvalidClaims.put(IdTokenClaimNames.EXP, expiresAt);\n\t\t}\n\t\t// 10. The iat Claim can be used to reject tokens that were issued too far away\n\t\t// from the current time,\n\t\t// limiting the amount of time that nonces need to be stored to prevent attacks.\n\t\t// The acceptable range is Client specific.\n\t\tInstant issuedAt = idToken.getIssuedAt();\n\t\tif (issuedAt != null && now.plus(this.clockSkew).isBefore(issuedAt)) {\n\t\t\tinvalidClaims.put(IdTokenClaimNames.IAT, issuedAt);\n\t\t}\n\t\tif (!invalidClaims.isEmpty()) {\n\t\t\treturn OAuth2TokenValidatorResult.failure(invalidIdToken(invalidClaims));\n\t\t}\n\t\treturn OAuth2TokenValidatorResult.success();\n\t}\n\n\t/**\n\t * Sets the maximum acceptable clock skew. The default is 60 seconds. The clock skew\n\t * is used when validating the {@link JwtClaimNames#EXP exp} and\n\t * {@link JwtClaimNames#IAT iat} claims.\n\t * @param clockSkew the maximum acceptable clock skew\n\t * @since 5.2\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)} when validating the\n\t * {@link JwtClaimNames#EXP exp} and {@link JwtClaimNames#IAT iat} claims.\n\t * @param clock the clock\n\t * @since 5.3\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n\tprivate static OAuth2Error invalidIdToken(Map<String, Object> invalidClaims) {\n\t\treturn new OAuth2Error(\"invalid_id_token\", \"The ID Token contains invalid claims: \" + invalidClaims,\n\t\t\t\t\"https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation\");\n\t}\n\n\tprivate static Map<String, Object> validateRequiredClaims(Jwt idToken) {\n\t\tMap<String, Object> requiredClaims = new HashMap<>();\n\t\tURL issuer = idToken.getIssuer();\n\t\tif (issuer == null) {\n\t\t\trequiredClaims.put(IdTokenClaimNames.ISS, issuer);\n\t\t}\n\t\tString subject = idToken.getSubject();\n\t\tif (subject == null) {\n\t\t\trequiredClaims.put(IdTokenClaimNames.SUB, subject);\n\t\t}\n\t\tList<String> audience = idToken.getAudience();\n\t\tif (CollectionUtils.isEmpty(audience)) {\n\t\t\trequiredClaims.put(IdTokenClaimNames.AUD, audience);\n\t\t}\n\t\tInstant expiresAt = idToken.getExpiresAt();\n\t\tif (expiresAt == null) {\n\t\t\trequiredClaims.put(IdTokenClaimNames.EXP, expiresAt);\n\t\t}\n\t\tInstant issuedAt = idToken.getIssuedAt();\n\t\tif (issuedAt == null) {\n\t\t\trequiredClaims.put(IdTokenClaimNames.IAT, issuedAt);\n\t\t}\n\t\treturn requiredClaims;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/ReactiveOidcIdTokenDecoderFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.net.URL;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport javax.crypto.spec.SecretKeySpec;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtTimestampValidator;\nimport org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link ReactiveJwtDecoderFactory factory} that provides a {@link ReactiveJwtDecoder}\n * used for {@link OidcIdToken} signature verification. The provided\n * {@link ReactiveJwtDecoder} is associated to a specific {@link ClientRegistration}.\n *\n * @author Joe Grandja\n * @author Rafael Dominguez\n * @author Mark Heckler\n * @author Ubaid ur Rehman\n * @since 5.2\n * @see ReactiveJwtDecoderFactory\n * @see ClientRegistration\n * @see OidcIdToken\n */\npublic final class ReactiveOidcIdTokenDecoderFactory implements ReactiveJwtDecoderFactory<ClientRegistration> {\n\n\tprivate static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = \"missing_signature_verifier\";\n\n\tprivate static final Map<JwsAlgorithm, String> JCA_ALGORITHM_MAPPINGS;\n\tstatic {\n\t\tMap<JwsAlgorithm, String> mappings = new HashMap<>();\n\t\tmappings.put(MacAlgorithm.HS256, \"HmacSHA256\");\n\t\tmappings.put(MacAlgorithm.HS384, \"HmacSHA384\");\n\t\tmappings.put(MacAlgorithm.HS512, \"HmacSHA512\");\n\t\tJCA_ALGORITHM_MAPPINGS = Collections.unmodifiableMap(mappings);\n\t}\n\n\tprivate static final ClaimTypeConverter DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(\n\t\t\tcreateDefaultClaimTypeConverters());\n\n\tprivate Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = new DefaultOidcIdTokenValidatorFactory();\n\n\tprivate Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver = (\n\t\t\tclientRegistration) -> SignatureAlgorithm.RS256;\n\n\tprivate Function<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory = (\n\t\t\tclientRegistration) -> DEFAULT_CLAIM_TYPE_CONVERTER;\n\n\t/**\n\t * Returns the default {@link Converter}'s used for type conversion of claim values\n\t * for an {@link OidcIdToken}.\n\t * @return a {@link Map} of {@link Converter}'s keyed by {@link IdTokenClaimNames\n\t * claim name}\n\t */\n\tpublic static Map<String, Converter<Object, ?>> createDefaultClaimTypeConverters() {\n\t\tConverter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));\n\t\tConverter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));\n\t\tConverter<Object, ?> urlConverter = getConverter(TypeDescriptor.valueOf(URL.class));\n\t\tConverter<Object, ?> stringConverter = getConverter(TypeDescriptor.valueOf(String.class));\n\t\tConverter<Object, ?> collectionStringConverter = getConverter(\n\t\t\t\tTypeDescriptor.collection(Collection.class, TypeDescriptor.valueOf(String.class)));\n\t\tMap<String, Converter<Object, ?>> converters = new HashMap<>();\n\t\tconverters.put(IdTokenClaimNames.ISS, urlConverter);\n\t\tconverters.put(IdTokenClaimNames.AUD, collectionStringConverter);\n\t\tconverters.put(IdTokenClaimNames.NONCE, stringConverter);\n\t\tconverters.put(IdTokenClaimNames.EXP, instantConverter);\n\t\tconverters.put(IdTokenClaimNames.IAT, instantConverter);\n\t\tconverters.put(IdTokenClaimNames.AUTH_TIME, instantConverter);\n\t\tconverters.put(IdTokenClaimNames.AMR, collectionStringConverter);\n\t\tconverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);\n\t\tconverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);\n\t\tconverters.put(StandardClaimNames.UPDATED_AT, instantConverter);\n\t\treturn converters;\n\t}\n\n\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\tfinal TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);\n\t\treturn (source) -> ClaimConversionService.getSharedInstance()\n\t\t\t.convert(source, sourceDescriptor, targetDescriptor);\n\t}\n\n\t@Override\n\tpublic ReactiveJwtDecoder createDecoder(ClientRegistration clientRegistration) {\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tNimbusReactiveJwtDecoder jwtDecoder = buildDecoder(clientRegistration);\n\t\tjwtDecoder.setJwtValidator(this.jwtValidatorFactory.apply(clientRegistration));\n\t\tConverter<Map<String, Object>, Map<String, Object>> claimTypeConverter = this.claimTypeConverterFactory\n\t\t\t.apply(clientRegistration);\n\t\tif (claimTypeConverter != null) {\n\t\t\tjwtDecoder.setClaimSetConverter(claimTypeConverter);\n\t\t}\n\t\treturn jwtDecoder;\n\t}\n\n\tprivate NimbusReactiveJwtDecoder buildDecoder(ClientRegistration clientRegistration) {\n\t\tJwsAlgorithm jwsAlgorithm = this.jwsAlgorithmResolver.apply(clientRegistration);\n\t\tif (jwsAlgorithm != null && SignatureAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {\n\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation\n\t\t\t//\n\t\t\t// 6. If the ID Token is received via direct communication between the Client\n\t\t\t// and the Token Endpoint (which it is in this flow),\n\t\t\t// the TLS server validation MAY be used to validate the issuer in place of\n\t\t\t// checking the token signature.\n\t\t\t// The Client MUST validate the signature of all other ID Tokens according to\n\t\t\t// JWS [JWS]\n\t\t\t// using the algorithm specified in the JWT alg Header Parameter.\n\t\t\t// The Client MUST use the keys provided by the Issuer.\n\t\t\t//\n\t\t\t// 7. The alg value SHOULD be the default of RS256 or the algorithm sent by\n\t\t\t// the Client\n\t\t\t// in the id_token_signed_response_alg parameter during Registration.\n\t\t\tString jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();\n\t\t\tif (!StringUtils.hasText(jwkSetUri)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,\n\t\t\t\t\t\t\"Failed to find a Signature Verifier for Client Registration: '\"\n\t\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId()\n\t\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured the JwkSet URI.\",\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\treturn NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)\n\t\t\t\t.jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm)\n\t\t\t\t.build();\n\t\t}\n\t\tif (jwsAlgorithm != null && MacAlgorithm.class.isAssignableFrom(jwsAlgorithm.getClass())) {\n\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation\n\t\t\t//\n\t\t\t// 8. If the JWT alg Header Parameter uses a MAC based algorithm such as\n\t\t\t// HS256, HS384, or HS512,\n\t\t\t// the octets of the UTF-8 representation of the client_secret\n\t\t\t// corresponding to the client_id contained in the aud (audience) Claim\n\t\t\t// are used as the key to validate the signature.\n\t\t\t// For MAC based algorithms, the behavior is unspecified if the aud is\n\t\t\t// multi-valued or\n\t\t\t// if an azp value is present that is different than the aud value.\n\n\t\t\tString clientSecret = clientRegistration.getClientSecret();\n\t\t\tif (!StringUtils.hasText(clientSecret)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,\n\t\t\t\t\t\t\"Failed to find a Signature Verifier for Client Registration: '\"\n\t\t\t\t\t\t\t\t+ clientRegistration.getRegistrationId()\n\t\t\t\t\t\t\t\t+ \"'. Check to ensure you have configured the client secret.\",\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\tSecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\tJCA_ALGORITHM_MAPPINGS.get(jwsAlgorithm));\n\t\t\treturn NimbusReactiveJwtDecoder.withSecretKey(secretKeySpec)\n\t\t\t\t.macAlgorithm((MacAlgorithm) jwsAlgorithm)\n\t\t\t\t.build();\n\t\t}\n\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_SIGNATURE_VERIFIER_ERROR_CODE,\n\t\t\t\t\"Failed to find a Signature Verifier for Client Registration: '\"\n\t\t\t\t\t\t+ clientRegistration.getRegistrationId()\n\t\t\t\t\t\t+ \"'. Check to ensure you have configured a valid JWS Algorithm: '\" + jwsAlgorithm + \"'\",\n\t\t\t\tnull);\n\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t}\n\n\t/**\n\t * Sets the factory that provides an {@link OAuth2TokenValidator}, which is used by\n\t * the {@link ReactiveJwtDecoder}. The default composes {@link JwtTimestampValidator}\n\t * and {@link OidcIdTokenValidator}.\n\t * @param jwtValidatorFactory the factory that provides an\n\t * {@link OAuth2TokenValidator}\n\t */\n\tpublic void setJwtValidatorFactory(Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {\n\t\tAssert.notNull(jwtValidatorFactory, \"jwtValidatorFactory cannot be null\");\n\t\tthis.jwtValidatorFactory = jwtValidatorFactory;\n\t}\n\n\t/**\n\t * Sets the resolver that provides the expected {@link JwsAlgorithm JWS algorithm}\n\t * used for the signature or MAC on the {@link OidcIdToken ID Token}. The default\n\t * resolves to {@link SignatureAlgorithm#RS256 RS256} for all\n\t * {@link ClientRegistration clients}.\n\t * @param jwsAlgorithmResolver the resolver that provides the expected\n\t * {@link JwsAlgorithm JWS algorithm} for a specific {@link ClientRegistration client}\n\t */\n\tpublic void setJwsAlgorithmResolver(Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver) {\n\t\tAssert.notNull(jwsAlgorithmResolver, \"jwsAlgorithmResolver cannot be null\");\n\t\tthis.jwsAlgorithmResolver = jwsAlgorithmResolver;\n\t}\n\n\t/**\n\t * Sets the factory that provides a {@link Converter} used for type conversion of\n\t * claim values for an {@link OidcIdToken}. The default is {@link ClaimTypeConverter}\n\t * for all {@link ClientRegistration clients}.\n\t * @param claimTypeConverterFactory the factory that provides a {@link Converter} used\n\t * for type conversion of claim values for a specific {@link ClientRegistration\n\t * client}\n\t */\n\tpublic void setClaimTypeConverterFactory(\n\t\t\tFunction<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory) {\n\t\tAssert.notNull(claimTypeConverterFactory, \"claimTypeConverterFactory cannot be null\");\n\t\tthis.claimTypeConverterFactory = claimTypeConverterFactory;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/event/OidcUserRefreshedEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication.event;\n\nimport java.io.Serial;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.util.Assert;\n\n/**\n * An event that is published when an {@link OidcUser} is refreshed as a result of using a\n * {@code refresh_token} to obtain an OAuth 2.0 Access Token Response that contains an\n * {@code id_token}.\n *\n * @author Steve Riesenberg\n * @since 6.5\n * @see org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizedClientRefreshedEventListener\n */\npublic final class OidcUserRefreshedEvent extends ApplicationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2657442604286019694L;\n\n\tprivate final OidcUser oldOidcUser;\n\n\tprivate final OidcUser newOidcUser;\n\n\tprivate final Authentication authentication;\n\n\t/**\n\t * Creates a new instance with the provided parameters.\n\t * @param accessTokenResponse the {@link OAuth2AccessTokenResponse} that triggered the\n\t * event\n\t * @param oldOidcUser the original {@link OidcUser}\n\t * @param newOidcUser the refreshed {@link OidcUser}\n\t * @param authentication the authentication result\n\t */\n\tpublic OidcUserRefreshedEvent(OAuth2AccessTokenResponse accessTokenResponse, OidcUser oldOidcUser,\n\t\t\tOidcUser newOidcUser, Authentication authentication) {\n\t\tsuper(accessTokenResponse);\n\t\tAssert.notNull(oldOidcUser, \"oldOidcUser cannot be null\");\n\t\tAssert.notNull(newOidcUser, \"newOidcUser cannot be null\");\n\t\tAssert.notNull(authentication, \"authentication cannot be null\");\n\t\tthis.oldOidcUser = oldOidcUser;\n\t\tthis.newOidcUser = newOidcUser;\n\t\tthis.authentication = authentication;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessTokenResponse} that triggered the event.\n\t * @return the access token response\n\t */\n\tpublic OAuth2AccessTokenResponse getAccessTokenResponse() {\n\t\treturn (OAuth2AccessTokenResponse) this.getSource();\n\t}\n\n\t/**\n\t * Returns the original {@link OidcUser}.\n\t * @return the original user\n\t */\n\tpublic OidcUser getOldOidcUser() {\n\t\treturn this.oldOidcUser;\n\t}\n\n\t/**\n\t * Returns the refreshed {@link OidcUser}.\n\t * @return the refreshed user\n\t */\n\tpublic OidcUser getNewOidcUser() {\n\t\treturn this.newOidcUser;\n\t}\n\n\t/**\n\t * Returns the authentication result.\n\t * @return the authentication result\n\t */\n\tpublic Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/event/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Events for OpenID Connect 1.0 authentication.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.oidc.authentication.event;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/LogoutTokenClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication.logout;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ClaimAccessor} for the &quot;claims&quot; that can be returned in OIDC Logout\n * Tokens\n *\n * @author Josh Cummings\n * @since 6.2\n * @see OidcLogoutToken\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken\">OIDC\n * Back-Channel Logout Token</a>\n */\npublic interface LogoutTokenClaimAccessor extends ClaimAccessor {\n\n\t/**\n\t * Returns the Issuer identifier {@code (iss)}.\n\t * @return the Issuer identifier\n\t */\n\tdefault URL getIssuer() {\n\t\tURL issuer = this.getClaimAsURL(LogoutTokenClaimNames.ISS);\n\t\tAssert.notNull(issuer, \"issuer cannot be null\");\n\t\treturn issuer;\n\t}\n\n\t/**\n\t * Returns the Subject identifier {@code (sub)}.\n\t * @return the Subject identifier\n\t */\n\tdefault @Nullable String getSubject() {\n\t\treturn this.getClaimAsString(LogoutTokenClaimNames.SUB);\n\t}\n\n\t/**\n\t * Returns the Audience(s) {@code (aud)} that this ID Token is intended for.\n\t * @return the Audience(s) that this ID Token is intended for\n\t */\n\tdefault List<String> getAudience() {\n\t\tList<String> audience = this.getClaimAsStringList(LogoutTokenClaimNames.AUD);\n\t\tAssert.notNull(audience, \"audience cannot be null\");\n\t\treturn audience;\n\t}\n\n\t/**\n\t * Returns the time at which the ID Token was issued {@code (iat)}.\n\t * @return the time at which the ID Token was issued\n\t */\n\tdefault @Nullable Instant getIssuedAt() {\n\t\treturn this.getClaimAsInstant(LogoutTokenClaimNames.IAT);\n\t}\n\n\t/**\n\t * Returns a {@link Map} that identifies this token as a logout token\n\t * @return the identifying {@link Map}\n\t */\n\tdefault Map<String, Object> getEvents() {\n\t\tMap<String, Object> events = getClaimAsMap(LogoutTokenClaimNames.EVENTS);\n\t\tAssert.notNull(events, \"events cannot be null\");\n\t\treturn events;\n\t}\n\n\t/**\n\t * Returns a {@code String} value {@code (sid)} representing the OIDC Provider session\n\t * @return the value representing the OIDC Provider session\n\t */\n\tdefault @Nullable String getSessionId() {\n\t\treturn getClaimAsString(LogoutTokenClaimNames.SID);\n\t}\n\n\t/**\n\t * Returns the JWT ID {@code (jti)} claim which provides a unique identifier for the\n\t * JWT.\n\t * @return the JWT ID claim which provides a unique identifier for the JWT\n\t */\n\tdefault String getId() {\n\t\tString jti = this.getClaimAsString(LogoutTokenClaimNames.JTI);\n\t\tAssert.hasText(jti, \"jti cannot be empty\");\n\t\treturn jti;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/LogoutTokenClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication.logout;\n\n/**\n * The names of the &quot;claims&quot; defined by the OpenID Back-Channel Logout 1.0\n * specification that can be returned in a Logout Token.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see OidcLogoutToken\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken\">OIDC\n * Back-Channel Logout Token</a>\n */\npublic final class LogoutTokenClaimNames {\n\n\t/**\n\t * {@code jti} - the JTI identifier\n\t */\n\tpublic static final String JTI = \"jti\";\n\n\t/**\n\t * {@code iss} - the Issuer identifier\n\t */\n\tpublic static final String ISS = \"iss\";\n\n\t/**\n\t * {@code sub} - the Subject identifier\n\t */\n\tpublic static final String SUB = \"sub\";\n\n\t/**\n\t * {@code aud} - the Audience(s) that the ID Token is intended for\n\t */\n\tpublic static final String AUD = \"aud\";\n\n\t/**\n\t * {@code iat} - the time at which the ID Token was issued\n\t */\n\tpublic static final String IAT = \"iat\";\n\n\t/**\n\t * {@code events} - a JSON object that identifies this token as a logout token\n\t */\n\tpublic static final String EVENTS = \"events\";\n\n\t/**\n\t * {@code sid} - the session id for the OIDC provider\n\t */\n\tpublic static final String SID = \"sid\";\n\n\tprivate LogoutTokenClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/OidcLogoutToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication.logout;\n\nimport java.io.Serial;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.AbstractOAuth2Token;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AbstractOAuth2Token} representing an OpenID Backchannel\n * Logout Token.\n *\n * <p>\n * The {@code OidcLogoutToken} is a security token that contains &quot;claims&quot; about\n * terminating sessions for a given OIDC Provider session id or End User.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see AbstractOAuth2Token\n * @see LogoutTokenClaimAccessor\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken\">Logout\n * Token</a>\n */\npublic class OidcLogoutToken extends AbstractOAuth2Token implements LogoutTokenClaimAccessor {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5705409698230609696L;\n\n\tprivate static final String BACKCHANNEL_LOGOUT_TOKEN_EVENT_NAME = \"http://schemas.openid.net/event/backchannel-logout\";\n\n\tprivate final Map<String, Object> claims;\n\n\t/**\n\t * Constructs a {@link OidcLogoutToken} using the provided parameters.\n\t * @param tokenValue the Logout Token value\n\t * @param issuedAt the time at which the Logout Token was issued {@code (iat)}\n\t * @param claims the claims about the logout statement\n\t */\n\tOidcLogoutToken(String tokenValue, @Nullable Instant issuedAt, Map<String, Object> claims) {\n\t\tsuper(tokenValue, issuedAt, Instant.MAX);\n\t\tAssert.notNull(claims, \"claims must not be null\");\n\t\tthis.claims = Collections.unmodifiableMap(claims);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * Create a {@link OidcLogoutToken.Builder} based on the given token value\n\t * @param tokenValue the token value to use\n\t * @return the {@link OidcLogoutToken.Builder} for further configuration\n\t */\n\tpublic static Builder withTokenValue(String tokenValue) {\n\t\treturn new Builder(tokenValue);\n\t}\n\n\t/**\n\t * A builder for {@link OidcLogoutToken}s\n\t *\n\t * @author Josh Cummings\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate String tokenValue;\n\n\t\tprivate final Map<String, Object> claims = new LinkedHashMap<>();\n\n\t\tprivate Builder(String tokenValue) {\n\t\t\tthis.tokenValue = tokenValue;\n\t\t\tthis.claims.put(LogoutTokenClaimNames.EVENTS,\n\t\t\t\t\tCollections.singletonMap(BACKCHANNEL_LOGOUT_TOKEN_EVENT_NAME, Collections.emptyMap()));\n\t\t}\n\n\t\t/**\n\t\t * Use this token value in the resulting {@link OidcLogoutToken}\n\t\t * @param tokenValue The token value to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder tokenValue(String tokenValue) {\n\t\t\tthis.tokenValue = tokenValue;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this claim in the resulting {@link OidcLogoutToken}\n\t\t * @param name The claim name\n\t\t * @param value The claim value\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claim(String name, Object value) {\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #claim(String, Object)} declared so far with\n\t\t * the possibility to add, replace, or remove.\n\t\t * @param claimsConsumer the consumer\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this audience in the resulting {@link OidcLogoutToken}\n\t\t * @param audience The audience(s) to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder audience(Collection<String> audience) {\n\t\t\treturn claim(LogoutTokenClaimNames.AUD, audience);\n\t\t}\n\n\t\t/**\n\t\t * Use this issued-at timestamp in the resulting {@link OidcLogoutToken}\n\t\t * @param issuedAt The issued-at timestamp to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder issuedAt(Instant issuedAt) {\n\t\t\treturn claim(LogoutTokenClaimNames.IAT, issuedAt);\n\t\t}\n\n\t\t/**\n\t\t * Use this issuer in the resulting {@link OidcLogoutToken}\n\t\t * @param issuer The issuer to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder issuer(String issuer) {\n\t\t\treturn claim(LogoutTokenClaimNames.ISS, issuer);\n\t\t}\n\n\t\t/**\n\t\t * Use this id to identify the resulting {@link OidcLogoutToken}\n\t\t * @param jti The unique identifier to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder jti(String jti) {\n\t\t\treturn claim(LogoutTokenClaimNames.JTI, jti);\n\t\t}\n\n\t\t/**\n\t\t * Use this subject in the resulting {@link OidcLogoutToken}\n\t\t * @param subject The subject to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder subject(String subject) {\n\t\t\treturn claim(LogoutTokenClaimNames.SUB, subject);\n\t\t}\n\n\t\t/**\n\t\t * A JSON object that identifies this token as a logout token\n\t\t * @param events The JSON object to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder events(Map<String, Object> events) {\n\t\t\treturn claim(LogoutTokenClaimNames.EVENTS, events);\n\t\t}\n\n\t\t/**\n\t\t * Use this session id to correlate the OIDC Provider session\n\t\t * @param sessionId The session id to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder sessionId(String sessionId) {\n\t\t\treturn claim(LogoutTokenClaimNames.SID, sessionId);\n\t\t}\n\n\t\tpublic OidcLogoutToken build() {\n\t\t\tAssert.notNull(this.claims.get(LogoutTokenClaimNames.ISS), \"issuer must not be null\");\n\t\t\tAssert.isInstanceOf(Collection.class, this.claims.get(LogoutTokenClaimNames.AUD),\n\t\t\t\t\t\"audience must be a collection\");\n\t\t\tAssert.notEmpty((Collection<?>) this.claims.get(LogoutTokenClaimNames.AUD), \"audience must not be empty\");\n\t\t\tAssert.notNull(this.claims.get(LogoutTokenClaimNames.JTI), \"jti must not be null\");\n\t\t\tAssert.isTrue(hasLogoutTokenIdentifyingMember(),\n\t\t\t\t\t\"logout token must contain an events claim that contains a member called \" + \"'\"\n\t\t\t\t\t\t\t+ BACKCHANNEL_LOGOUT_TOKEN_EVENT_NAME + \"' whose value is an empty Map\");\n\t\t\tAssert.isNull(this.claims.get(\"nonce\"), \"logout token must not contain a nonce claim\");\n\t\t\tObject iatClaim = this.claims.get(IdTokenClaimNames.IAT);\n\t\t\tInstant iat = (iatClaim != null) ? toInstant(iatClaim) : null;\n\t\t\treturn new OidcLogoutToken(this.tokenValue, iat, this.claims);\n\t\t}\n\n\t\tprivate boolean hasLogoutTokenIdentifyingMember() {\n\t\t\tif (!(this.claims.get(LogoutTokenClaimNames.EVENTS) instanceof Map<?, ?> events)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!(events.get(BACKCHANNEL_LOGOUT_TOKEN_EVENT_NAME) instanceof Map<?, ?> object)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn object.isEmpty();\n\t\t}\n\n\t\tprivate @Nullable Instant toInstant(@Nullable Object timestamp) {\n\t\t\tif (timestamp != null) {\n\t\t\t\tAssert.isInstanceOf(Instant.class, timestamp, \"timestamps must be of type Instant\");\n\t\t\t}\n\t\t\treturn (Instant) timestamp;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for OpenID Connect 1.0 Logout.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.oidc.authentication.logout;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes and interfaces for authenticating and authorizing a client with an\n * OpenID Connect 1.0 Provider using a specific authorization grant flow.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/InMemoryReactiveOidcSessionRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.server.session;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;\n\n/**\n * An in-memory implementation of\n * {@link org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry}\n *\n * @author Josh Cummings\n * @since 6.2\n */\npublic final class InMemoryReactiveOidcSessionRegistry implements ReactiveOidcSessionRegistry {\n\n\tprivate final InMemoryOidcSessionRegistry delegate = new InMemoryOidcSessionRegistry();\n\n\t@Override\n\tpublic Mono<Void> saveSessionInformation(OidcSessionInformation info) {\n\t\tthis.delegate.saveSessionInformation(info);\n\t\treturn Mono.empty();\n\t}\n\n\t@Override\n\tpublic Mono<OidcSessionInformation> removeSessionInformation(String clientSessionId) {\n\t\treturn Mono.justOrEmpty(this.delegate.removeSessionInformation(clientSessionId));\n\t}\n\n\t@Override\n\tpublic Flux<OidcSessionInformation> removeSessionInformation(OidcLogoutToken token) {\n\t\treturn Flux.fromIterable(this.delegate.removeSessionInformation(token));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/ReactiveOidcSessionRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.server.session;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;\n\n/**\n * A registry to record the tie between the OIDC Provider session and the Client session.\n * This is handy when a provider makes a logout request that indicates the OIDC Provider\n * session or the End User.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken\">Logout\n * Token</a>\n */\npublic interface ReactiveOidcSessionRegistry {\n\n\t/**\n\t * Register a OIDC Provider session with the provided client session. Generally\n\t * speaking, the client session should be the session tied to the current login.\n\t * @param info the {@link OidcSessionInformation} to use\n\t */\n\tMono<Void> saveSessionInformation(OidcSessionInformation info);\n\n\t/**\n\t * Deregister the OIDC Provider session tied to the provided client session. Generally\n\t * speaking, the client session should be the session tied to the current logout.\n\t * @param clientSessionId the client session\n\t * @return any found {@link OidcSessionInformation}, could be {@code null}\n\t */\n\tMono<OidcSessionInformation> removeSessionInformation(String clientSessionId);\n\n\t/**\n\t * Deregister the OIDC Provider sessions referenced by the provided OIDC Logout Token\n\t * by its session id or its subject. Note that the issuer and audience should also\n\t * match the corresponding values found in each {@link OidcSessionInformation}\n\t * returned.\n\t * @param logoutToken the {@link OidcLogoutToken}\n\t * @return any found {@link OidcSessionInformation}s, could be empty\n\t */\n\tFlux<OidcSessionInformation> removeSessionInformation(OidcLogoutToken logoutToken);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive support for OpenID Connect 1.0 Session Management.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.oidc.server.session;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/InMemoryOidcSessionRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.session;\n\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Predicate;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimNames;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\n\n/**\n * An in-memory implementation of {@link OidcSessionRegistry}\n *\n * @author Josh Cummings\n * @since 6.2\n */\npublic final class InMemoryOidcSessionRegistry implements OidcSessionRegistry {\n\n\tprivate final Log logger = LogFactory.getLog(InMemoryOidcSessionRegistry.class);\n\n\tprivate final Map<String, OidcSessionInformation> sessions = new ConcurrentHashMap<>();\n\n\t@Override\n\tpublic void saveSessionInformation(OidcSessionInformation info) {\n\t\tthis.sessions.put(info.getSessionId(), info);\n\t}\n\n\t@Override\n\tpublic OidcSessionInformation removeSessionInformation(String clientSessionId) {\n\t\tOidcSessionInformation information = this.sessions.remove(clientSessionId);\n\t\tif (information != null) {\n\t\t\tthis.logger.trace(\"Removed client session\");\n\t\t}\n\t\treturn information;\n\t}\n\n\t@Override\n\tpublic Iterable<OidcSessionInformation> removeSessionInformation(OidcLogoutToken token) {\n\t\tList<String> audience = token.getAudience();\n\t\tString issuer = token.getIssuer().toString();\n\t\tString subject = token.getSubject();\n\t\tString providerSessionId = token.getSessionId();\n\t\tPredicate<OidcSessionInformation> matcher = (providerSessionId != null)\n\t\t\t\t? sessionIdMatcher(audience, issuer, providerSessionId) : subjectMatcher(audience, issuer, subject);\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tString message = \"Looking up sessions by issuer [%s] and %s [%s]\";\n\t\t\tif (providerSessionId != null) {\n\t\t\t\tthis.logger.trace(String.format(message, issuer, LogoutTokenClaimNames.SID, providerSessionId));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.logger.trace(String.format(message, issuer, LogoutTokenClaimNames.SUB, subject));\n\t\t\t}\n\t\t}\n\t\tint size = this.sessions.size();\n\t\tSet<OidcSessionInformation> infos = new HashSet<>();\n\t\tthis.sessions.values().removeIf((info) -> {\n\t\t\tboolean result = matcher.test(info);\n\t\t\tif (result) {\n\t\t\t\tinfos.add(info);\n\t\t\t}\n\t\t\treturn result;\n\t\t});\n\t\tif (infos.isEmpty()) {\n\t\t\tthis.logger.debug(\"Failed to remove any sessions since none matched\");\n\t\t}\n\t\telse if (this.logger.isTraceEnabled()) {\n\t\t\tString message = \"Found and removed %d session(s) from mapping of %d session(s)\";\n\t\t\tthis.logger.trace(String.format(message, infos.size(), size));\n\t\t}\n\t\treturn infos;\n\t}\n\n\tprivate static Predicate<OidcSessionInformation> sessionIdMatcher(List<String> audience, String issuer,\n\t\t\tString sessionId) {\n\t\treturn (session) -> {\n\t\t\tList<String> thatAudience = session.getPrincipal().getAudience();\n\t\t\tURL thatIssuerUrl = session.getPrincipal().getIssuer();\n\t\t\tif (thatIssuerUrl == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tString thatIssuer = thatIssuerUrl.toString();\n\t\t\tString thatSessionId = session.getPrincipal().getClaimAsString(LogoutTokenClaimNames.SID);\n\t\t\tif (thatAudience == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn !Collections.disjoint(audience, thatAudience) && issuer.equals(thatIssuer)\n\t\t\t\t\t&& sessionId.equals(thatSessionId);\n\t\t};\n\t}\n\n\tprivate static Predicate<OidcSessionInformation> subjectMatcher(List<String> audience, String issuer,\n\t\t\t@Nullable String subject) {\n\t\treturn (session) -> {\n\t\t\tif (subject == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tList<String> thatAudience = session.getPrincipal().getAudience();\n\t\t\tURL thatIssuerUrl = session.getPrincipal().getIssuer();\n\t\t\tif (thatIssuerUrl == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tString thatIssuer = thatIssuerUrl.toString();\n\t\t\tString thatSubject = session.getPrincipal().getSubject();\n\t\t\tif (thatAudience == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn !Collections.disjoint(audience, thatAudience) && issuer.equals(thatIssuer)\n\t\t\t\t\t&& subject.equals(thatSubject);\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionInformation.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.session;\n\nimport java.io.Serial;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\n\n/**\n * A {@link SessionInformation} extension that enforces the principal be of type\n * {@link OidcUser}.\n *\n * @author Josh Cummings\n * @since 6.2\n */\npublic class OidcSessionInformation extends SessionInformation {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1703808683027974918L;\n\n\tprivate final Map<String, String> authorities;\n\n\t/**\n\t * Construct an {@link OidcSessionInformation}\n\t * @param sessionId the Client's session id\n\t * @param authorities any material that authorizes operating on the session\n\t * @param user the OIDC Provider's session and end user\n\t */\n\tpublic OidcSessionInformation(String sessionId, Map<String, String> authorities, OidcUser user) {\n\t\tsuper(user, sessionId, new Date());\n\t\tthis.authorities = (authorities != null) ? new LinkedHashMap<>(authorities) : Collections.emptyMap();\n\t}\n\n\t/**\n\t * Any material needed to authorize operations on this session\n\t * @return the {@link Map} of credentials\n\t */\n\tpublic Map<String, String> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic OidcUser getPrincipal() {\n\t\treturn (OidcUser) super.getPrincipal();\n\t}\n\n\t/**\n\t * Copy this {@link OidcSessionInformation}, using a new session identifier\n\t * @param sessionId the new session identifier to use\n\t * @return a new {@link OidcSessionInformation} instance\n\t */\n\tpublic OidcSessionInformation withSessionId(String sessionId) {\n\t\treturn new OidcSessionInformation(sessionId, getAuthorities(), getPrincipal());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionRegistry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.session;\n\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\n\n/**\n * A registry to record the tie between the OIDC Provider session and the Client session.\n * This is handy when a provider makes a logout request that indicates the OIDC Provider\n * session or the End User.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken\">Logout\n * Token</a>\n */\npublic interface OidcSessionRegistry {\n\n\t/**\n\t * Register a OIDC Provider session with the provided client session. Generally\n\t * speaking, the client session should be the session tied to the current login.\n\t * @param info the {@link OidcSessionInformation} to use\n\t */\n\tvoid saveSessionInformation(OidcSessionInformation info);\n\n\t/**\n\t * Deregister the OIDC Provider session tied to the provided client session. Generally\n\t * speaking, the client session should be the session tied to the current logout.\n\t * @param clientSessionId the client session\n\t * @return any found {@link OidcSessionInformation}, could be {@code null}\n\t */\n\tOidcSessionInformation removeSessionInformation(String clientSessionId);\n\n\t/**\n\t * Deregister the OIDC Provider sessions referenced by the provided OIDC Logout Token\n\t * by its session id or its subject. Note that the issuer and audience should also\n\t * match the corresponding values found in each {@link OidcSessionInformation}\n\t * returned.\n\t * @param logoutToken the {@link OidcLogoutToken}\n\t * @return any found {@link OidcSessionInformation}s, could be empty\n\t */\n\tIterable<OidcSessionInformation> removeSessionInformation(OidcLogoutToken logoutToken);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for OpenID Connect 1.0 Session Management.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.oidc.session;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcReactiveOAuth2UserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link ReactiveOAuth2UserService} that supports OpenID Connect\n * 1.0 Provider's.\n *\n * @author Rob Winch\n * @since 5.1\n * @see ReactiveOAuth2UserService\n * @see OidcUserRequest\n * @see OidcUser\n * @see DefaultOidcUser\n * @see OidcUserInfo\n */\npublic class OidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {\n\n\tprivate static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = \"invalid_user_info_response\";\n\n\tprivate static final Converter<Map<String, Object>, Map<String, Object>> DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(\n\t\t\tcreateDefaultClaimTypeConverters());\n\n\tprivate ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService = new DefaultReactiveOAuth2UserService();\n\n\tprivate Function<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory = (\n\t\t\tclientRegistration) -> DEFAULT_CLAIM_TYPE_CONVERTER;\n\n\tprivate Predicate<OidcUserRequest> retrieveUserInfo = OidcUserRequestUtils::shouldRetrieveUserInfo;\n\n\tprivate Converter<OidcUserSource, Mono<OidcUser>> oidcUserConverter = (source) -> Mono\n\t\t.just(OidcUserRequestUtils.getUser(source));\n\n\t/**\n\t * Returns the default {@link Converter}'s used for type conversion of claim values\n\t * for an {@link OidcUserInfo}.\n\t * @return a {@link Map} of {@link Converter}'s keyed by {@link StandardClaimNames\n\t * claim name}\n\t * @since 5.2\n\t */\n\tpublic static Map<String, Converter<Object, ?>> createDefaultClaimTypeConverters() {\n\t\tConverter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));\n\t\tConverter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));\n\t\tMap<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();\n\t\tclaimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);\n\t\tclaimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);\n\t\tclaimTypeConverters.put(StandardClaimNames.UPDATED_AT, instantConverter);\n\t\treturn claimTypeConverters;\n\t}\n\n\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\tfinal TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);\n\t\treturn (source) -> ClaimConversionService.getSharedInstance()\n\t\t\t.convert(source, sourceDescriptor, targetDescriptor);\n\t}\n\n\t@Override\n\tpublic Mono<OidcUser> loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {\n\t\tAssert.notNull(userRequest, \"userRequest cannot be null\");\n\t\t// @formatter:off\n\t\treturn Mono.just(userRequest)\n\t\t\t.filter(this.retrieveUserInfo::test)\n\t\t\t.flatMap(this.oauth2UserService::loadUser)\n\t\t\t.flatMap((oauth2User) -> toOidcUser(userRequest, oauth2User))\n\t\t\t.switchIfEmpty(Mono.defer(() -> this.oidcUserConverter.convert(new OidcUserSource(userRequest))));\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<OidcUser> toOidcUser(OidcUserRequest userRequest, OAuth2User oauth2User) {\n\t\treturn Mono.defer(() -> {\n\t\t\tMap<String, Object> claims = convertClaims(oauth2User.getAttributes(), userRequest.getClientRegistration());\n\t\t\tOidcUserInfo userInfo = new OidcUserInfo(claims);\n\t\t\tString subject = userInfo.getSubject();\n\t\t\tif (subject == null || !subject.equals(userRequest.getIdToken().getSubject())) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\tOidcUserSource source = new OidcUserSource(userRequest, userInfo, oauth2User);\n\t\t\treturn this.oidcUserConverter.convert(source);\n\t\t});\n\t}\n\n\tprivate Map<String, Object> convertClaims(Map<String, Object> claims, ClientRegistration clientRegistration) {\n\t\tConverter<Map<String, Object>, Map<String, Object>> claimTypeConverter = this.claimTypeConverterFactory\n\t\t\t.apply(clientRegistration);\n\t\treturn (claimTypeConverter != null) ? claimTypeConverter.convert(claims)\n\t\t\t\t: DEFAULT_CLAIM_TYPE_CONVERTER.convert(claims);\n\t}\n\n\tpublic void setOauth2UserService(ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService) {\n\t\tAssert.notNull(oauth2UserService, \"oauth2UserService cannot be null\");\n\t\tthis.oauth2UserService = oauth2UserService;\n\t}\n\n\t/**\n\t * Sets the factory that provides a {@link Converter} used for type conversion of\n\t * claim values for an {@link OidcUserInfo}. The default is {@link ClaimTypeConverter}\n\t * for all {@link ClientRegistration clients}.\n\t * @param claimTypeConverterFactory the factory that provides a {@link Converter} used\n\t * for type conversion of claim values for a specific {@link ClientRegistration\n\t * client}\n\t * @since 5.2\n\t */\n\tpublic final void setClaimTypeConverterFactory(\n\t\t\tFunction<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory) {\n\t\tAssert.notNull(claimTypeConverterFactory, \"claimTypeConverterFactory cannot be null\");\n\t\tthis.claimTypeConverterFactory = claimTypeConverterFactory;\n\t}\n\n\t/**\n\t * Sets the {@code Predicate} used to determine if the UserInfo Endpoint should be\n\t * called to retrieve information about the End-User (Resource Owner).\n\t * <p>\n\t * By default, the UserInfo Endpoint is called if all the following are true:\n\t * <ul>\n\t * <li>The user info endpoint is defined on the ClientRegistration</li>\n\t * <li>The Client Registration uses the\n\t * {@link AuthorizationGrantType#AUTHORIZATION_CODE}</li>\n\t * </ul>\n\t * @param retrieveUserInfo the {@code Predicate} used to determine if the UserInfo\n\t * Endpoint should be called\n\t * @since 6.3\n\t */\n\tpublic final void setRetrieveUserInfo(Predicate<OidcUserRequest> retrieveUserInfo) {\n\t\tAssert.notNull(retrieveUserInfo, \"retrieveUserInfo cannot be null\");\n\t\tthis.retrieveUserInfo = retrieveUserInfo;\n\t}\n\n\t/**\n\t * Allows converting from the {@link OidcUserSource} to and {@link OidcUser}.\n\t * @param oidcUserConverter the {@link Converter} to use. Cannot be null.\n\t */\n\tpublic void setOidcUserConverter(Converter<OidcUserSource, Mono<OidcUser>> oidcUserConverter) {\n\t\tAssert.notNull(oidcUserConverter, \"oidcUserConverter cannot be null\");\n\t\tthis.oidcUserConverter = oidcUserConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.util.Assert;\n\n/**\n * Represents a request the {@link OidcUserService} uses when initiating a request to the\n * UserInfo Endpoint.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see ClientRegistration\n * @see OAuth2AccessToken\n * @see OidcIdToken\n * @see OidcUserService\n */\npublic class OidcUserRequest extends OAuth2UserRequest {\n\n\tprivate final OidcIdToken idToken;\n\n\t/**\n\t * Constructs an {@code OidcUserRequest} using the provided parameters.\n\t * @param clientRegistration the client registration\n\t * @param accessToken the access token credential\n\t * @param idToken the ID Token\n\t */\n\tpublic OidcUserRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken, OidcIdToken idToken) {\n\t\tthis(clientRegistration, accessToken, idToken, Collections.emptyMap());\n\t}\n\n\t/**\n\t * Constructs an {@code OidcUserRequest} using the provided parameters.\n\t * @param clientRegistration the client registration\n\t * @param accessToken the access token credential\n\t * @param idToken the ID Token\n\t * @param additionalParameters the additional parameters, may be empty\n\t * @since 5.1\n\t */\n\tpublic OidcUserRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken, OidcIdToken idToken,\n\t\t\tMap<String, Object> additionalParameters) {\n\t\tsuper(clientRegistration, accessToken, additionalParameters);\n\t\tAssert.notNull(idToken, \"idToken cannot be null\");\n\t\tthis.idToken = idToken;\n\t}\n\n\t/**\n\t * Returns the {@link OidcIdToken ID Token} containing claims about the user.\n\t * @return the {@link OidcIdToken} containing claims about the user.\n\t */\n\tpublic OidcIdToken getIdToken() {\n\t\treturn this.idToken;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.util.StringUtils;\n\n/**\n * Utilities for working with the {@link OidcUserRequest}\n *\n * @author Rob Winch\n * @since 5.1\n */\nfinal class OidcUserRequestUtils {\n\n\t/**\n\t * Determines if an {@link OidcUserRequest} should attempt to retrieve the user info.\n\t * Will return true if all the following are true:\n\t *\n\t * <ul>\n\t * <li>The user info endpoint is defined on the ClientRegistration</li>\n\t * <li>The Client Registration uses the\n\t * {@link AuthorizationGrantType#AUTHORIZATION_CODE}</li>\n\t * </ul>\n\t * @param userRequest\n\t * @return\n\t */\n\tstatic boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) {\n\t\t// Auto-disabled if UserInfo Endpoint URI is not provided\n\t\tClientRegistration.ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();\n\t\tif (StringUtils.hasLength(providerDetails.getUserInfoEndpoint().getUri())\n\t\t\t\t&& AuthorizationGrantType.AUTHORIZATION_CODE\n\t\t\t\t\t.equals(userRequest.getClientRegistration().getAuthorizationGrantType())) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tstatic OidcUser getUser(OidcUserSource userMetadata) {\n\t\tOidcUserRequest userRequest = userMetadata.getUserRequest();\n\t\tOidcUserInfo userInfo = userMetadata.getUserInfo();\n\t\tSet<GrantedAuthority> authorities = new LinkedHashSet<>();\n\t\tClientRegistration.ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();\n\t\tString userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();\n\t\tif (StringUtils.hasText(userNameAttributeName)) {\n\t\t\tauthorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo, userNameAttributeName));\n\t\t}\n\t\telse {\n\t\t\tauthorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo));\n\t\t}\n\t\tOAuth2AccessToken token = userRequest.getAccessToken();\n\t\tfor (String scope : token.getScopes()) {\n\t\t\tauthorities.add(new SimpleGrantedAuthority(\"SCOPE_\" + scope));\n\t\t}\n\t\tif (StringUtils.hasText(userNameAttributeName)) {\n\t\t\treturn new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo, userNameAttributeName);\n\t\t}\n\t\treturn new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo);\n\t}\n\n\tprivate OidcUserRequestUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2UserService} that supports OpenID Connect 1.0\n * Provider's.\n *\n * @author Joe Grandja\n * @author Steve Riesenberg\n * @since 5.0\n * @see OAuth2UserService\n * @see OidcUserRequest\n * @see OidcUser\n * @see DefaultOidcUser\n * @see OidcUserInfo\n */\npublic class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcUser> {\n\n\tprivate static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = \"invalid_user_info_response\";\n\n\tprivate static final Converter<Map<String, Object>, Map<String, Object>> DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(\n\t\t\tcreateDefaultClaimTypeConverters());\n\n\tprivate OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService = new DefaultOAuth2UserService();\n\n\tprivate Function<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory = (\n\t\t\tclientRegistration) -> DEFAULT_CLAIM_TYPE_CONVERTER;\n\n\tprivate Predicate<OidcUserRequest> retrieveUserInfo = OidcUserRequestUtils::shouldRetrieveUserInfo;\n\n\tprivate Converter<OidcUserSource, OidcUser> oidcUserConverter = OidcUserRequestUtils::getUser;\n\n\t/**\n\t * Returns the default {@link Converter}'s used for type conversion of claim values\n\t * for an {@link OidcUserInfo}.\n\t * @return a {@link Map} of {@link Converter}'s keyed by {@link StandardClaimNames\n\t * claim name}\n\t * @since 5.2\n\t */\n\tpublic static Map<String, Converter<Object, ?>> createDefaultClaimTypeConverters() {\n\t\tConverter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));\n\t\tConverter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));\n\t\tMap<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();\n\t\tclaimTypeConverters.put(StandardClaimNames.EMAIL_VERIFIED, booleanConverter);\n\t\tclaimTypeConverters.put(StandardClaimNames.PHONE_NUMBER_VERIFIED, booleanConverter);\n\t\tclaimTypeConverters.put(StandardClaimNames.UPDATED_AT, instantConverter);\n\t\treturn claimTypeConverters;\n\t}\n\n\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\tTypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);\n\t\treturn (source) -> ClaimConversionService.getSharedInstance()\n\t\t\t.convert(source, sourceDescriptor, targetDescriptor);\n\t}\n\n\t@Override\n\tpublic OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {\n\t\tAssert.notNull(userRequest, \"userRequest cannot be null\");\n\t\tOidcUserInfo userInfo = null;\n\t\tOAuth2User oauth2User = null;\n\t\tif (this.retrieveUserInfo.test(userRequest)) {\n\t\t\toauth2User = this.oauth2UserService.loadUser(userRequest);\n\t\t\tAssert.notNull(oauth2User, \"oauth2User cannot be null\");\n\t\t\tMap<String, Object> claims = getClaims(userRequest, oauth2User);\n\t\t\tuserInfo = new OidcUserInfo(claims);\n\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse\n\t\t\t// 1) The sub (subject) Claim MUST always be returned in the UserInfo Response\n\t\t\tif (userInfo.getSubject() == null) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\t// 2) Due to the possibility of token substitution attacks (see Section\n\t\t\t// 16.11),\n\t\t\t// the UserInfo Response is not guaranteed to be about the End-User\n\t\t\t// identified by the sub (subject) element of the ID Token.\n\t\t\t// The sub Claim in the UserInfo Response MUST be verified to exactly match\n\t\t\t// the sub Claim in the ID Token; if they do not match,\n\t\t\t// the UserInfo Response values MUST NOT be used.\n\t\t\tif (!userInfo.getSubject().equals(userRequest.getIdToken().getSubject())) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t}\n\t\tOidcUserSource source = new OidcUserSource(userRequest, userInfo, oauth2User);\n\t\treturn this.oidcUserConverter.convert(source);\n\t}\n\n\tprivate Map<String, Object> getClaims(OidcUserRequest userRequest, OAuth2User oauth2User) {\n\t\tConverter<Map<String, Object>, Map<String, Object>> converter = this.claimTypeConverterFactory\n\t\t\t.apply(userRequest.getClientRegistration());\n\t\tif (converter != null) {\n\t\t\treturn converter.convert(oauth2User.getAttributes());\n\t\t}\n\t\treturn DEFAULT_CLAIM_TYPE_CONVERTER.convert(oauth2User.getAttributes());\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2UserService} used when requesting the user info resource.\n\t * @param oauth2UserService the {@link OAuth2UserService} used when requesting the\n\t * user info resource.\n\t * @since 5.1\n\t */\n\tpublic final void setOauth2UserService(OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService) {\n\t\tAssert.notNull(oauth2UserService, \"oauth2UserService cannot be null\");\n\t\tthis.oauth2UserService = oauth2UserService;\n\t}\n\n\t/**\n\t * Sets the factory that provides a {@link Converter} used for type conversion of\n\t * claim values for an {@link OidcUserInfo}. The default is {@link ClaimTypeConverter}\n\t * for all {@link ClientRegistration clients}.\n\t * @param claimTypeConverterFactory the factory that provides a {@link Converter} used\n\t * for type conversion of claim values for a specific {@link ClientRegistration\n\t * client}\n\t * @since 5.2\n\t */\n\tpublic final void setClaimTypeConverterFactory(\n\t\t\tFunction<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory) {\n\t\tAssert.notNull(claimTypeConverterFactory, \"claimTypeConverterFactory cannot be null\");\n\t\tthis.claimTypeConverterFactory = claimTypeConverterFactory;\n\t}\n\n\t/**\n\t * Sets the {@code Predicate} used to determine if the UserInfo Endpoint should be\n\t * called to retrieve information about the End-User (Resource Owner).\n\t * <p>\n\t * By default, the UserInfo Endpoint is called if all the following are true:\n\t * <ul>\n\t * <li>The user info endpoint is defined on the ClientRegistration</li>\n\t * <li>The Client Registration uses the\n\t * {@link AuthorizationGrantType#AUTHORIZATION_CODE}</li>\n\t * </ul>\n\t * @param retrieveUserInfo the {@code Predicate} used to determine if the UserInfo\n\t * Endpoint should be called\n\t * @since 6.3\n\t */\n\tpublic final void setRetrieveUserInfo(Predicate<OidcUserRequest> retrieveUserInfo) {\n\t\tAssert.notNull(retrieveUserInfo, \"retrieveUserInfo cannot be null\");\n\t\tthis.retrieveUserInfo = retrieveUserInfo;\n\t}\n\n\t/**\n\t * Allows converting from the {@link OidcUserSource} to and {@link OidcUser}.\n\t * @param oidcUserConverter the {@link Converter} to use. Cannot be null.\n\t */\n\tpublic void setOidcUserConverter(Converter<OidcUserSource, OidcUser> oidcUserConverter) {\n\t\tAssert.notNull(oidcUserConverter, \"oidcUserConverter cannot be null\");\n\t\tthis.oidcUserConverter = oidcUserConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * The source for the converter to\n * {@link org.springframework.security.oauth2.core.oidc.user.OidcUser}.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic class OidcUserSource {\n\n\tprivate final OidcUserRequest userRequest;\n\n\tprivate final @Nullable OidcUserInfo userInfo;\n\n\tprivate final @Nullable OAuth2User oauth2User;\n\n\tpublic OidcUserSource(OidcUserRequest userRequest) {\n\t\tthis(userRequest, null, null);\n\t}\n\n\tpublic OidcUserSource(OidcUserRequest userRequest, @Nullable OidcUserInfo userInfo,\n\t\t\t@Nullable OAuth2User oauth2User) {\n\t\tAssert.notNull(userRequest, \"userRequest cannot be null\");\n\t\tthis.userRequest = userRequest;\n\t\tthis.userInfo = userInfo;\n\t\tthis.oauth2User = oauth2User;\n\t}\n\n\tpublic OidcUserRequest getUserRequest() {\n\t\treturn this.userRequest;\n\t}\n\n\tpublic @Nullable OidcUserInfo getUserInfo() {\n\t\treturn this.userInfo;\n\t}\n\n\tpublic @Nullable OAuth2User getOauth2User() {\n\t\treturn this.oauth2User;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes and interfaces providing support to the client for initiating requests to the\n * OpenID Connect 1.0 Provider's UserInfo Endpoint.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/web/logout/OidcClientInitiatedLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.web.logout;\n\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A logout success handler for initiating OIDC logout through the user agent.\n *\n * @author Josh Cummings\n * @since 5.2\n * @see <a href=\n * \"https://openid.net/specs/openid-connect-rpinitiated-1_0.html\">RP-Initiated Logout</a>\n * @see org.springframework.security.web.authentication.logout.LogoutSuccessHandler\n */\npublic class OidcClientInitiatedLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {\n\n\tprivate final ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate @Nullable String postLogoutRedirectUri;\n\n\tpublic OidcClientInitiatedLogoutSuccessHandler(ClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.postLogoutRedirectUri = null;\n\t}\n\n\t@Override\n\tprotected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tString targetUrl = null;\n\t\tif (authentication instanceof OAuth2AuthenticationToken && authentication.getPrincipal() instanceof OidcUser) {\n\t\t\tString registrationId = ((OAuth2AuthenticationToken) authentication).getAuthorizedClientRegistrationId();\n\t\t\tClientRegistration clientRegistration = this.clientRegistrationRepository\n\t\t\t\t.findByRegistrationId(registrationId);\n\t\t\tif (clientRegistration != null) {\n\t\t\t\tURI endSessionEndpoint = this.endSessionEndpoint(clientRegistration);\n\t\t\t\tif (endSessionEndpoint != null) {\n\t\t\t\t\tString idToken = idToken(authentication);\n\t\t\t\t\tString postLogoutRedirectUri = postLogoutRedirectUri(request, clientRegistration);\n\t\t\t\t\ttargetUrl = endpointUri(endSessionEndpoint, idToken, postLogoutRedirectUri);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn (targetUrl != null) ? targetUrl : super.determineTargetUrl(request, response, authentication);\n\t}\n\n\tprivate @Nullable URI endSessionEndpoint(@Nullable ClientRegistration clientRegistration) {\n\t\tif (clientRegistration != null) {\n\t\t\tProviderDetails providerDetails = clientRegistration.getProviderDetails();\n\t\t\tObject endSessionEndpoint = providerDetails.getConfigurationMetadata().get(\"end_session_endpoint\");\n\t\t\tif (endSessionEndpoint != null) {\n\t\t\t\treturn URI.create(endSessionEndpoint.toString());\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String idToken(Authentication authentication) {\n\t\tObject principal = authentication.getPrincipal();\n\t\tString idToken = (principal instanceof OidcUser oidcUser) ? oidcUser.getIdToken().getTokenValue() : null;\n\t\tAssert.notNull(idToken, \"idToken cannot be null\");\n\t\treturn idToken;\n\t}\n\n\tprivate @Nullable String postLogoutRedirectUri(HttpServletRequest request,\n\t\t\t@Nullable ClientRegistration clientRegistration) {\n\t\tif (this.postLogoutRedirectUri == null || clientRegistration == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:off\n\t\tUriComponents uriComponents = UriComponentsBuilder\n\t\t\t\t.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t\t.replacePath(request.getContextPath())\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.fragment(null)\n\t\t\t\t.build();\n\n\t\tMap<String, String> uriVariables = new HashMap<>();\n\t\tString scheme = uriComponents.getScheme();\n\t\turiVariables.put(\"baseScheme\", (scheme != null) ? scheme : \"\");\n\t\turiVariables.put(\"baseUrl\", uriComponents.toUriString());\n\n\t\tString host = uriComponents.getHost();\n\t\turiVariables.put(\"baseHost\", (host != null) ? host : \"\");\n\n\t\tString path = uriComponents.getPath();\n\t\turiVariables.put(\"basePath\", (path != null) ? path : \"\");\n\n\t\tint port = uriComponents.getPort();\n\t\turiVariables.put(\"basePort\", (port == -1) ? \"\" : \":\" + port);\n\n\t\turiVariables.put(\"registrationId\", clientRegistration.getRegistrationId());\n\n\t\treturn UriComponentsBuilder.fromUriString(this.postLogoutRedirectUri)\n\t\t\t\t.buildAndExpand(uriVariables)\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate String endpointUri(URI endSessionEndpoint, String idToken, @Nullable String postLogoutRedirectUri) {\n\t\tUriComponentsBuilder builder = UriComponentsBuilder.fromUri(endSessionEndpoint);\n\t\tbuilder.queryParam(\"id_token_hint\", idToken);\n\t\tif (postLogoutRedirectUri != null) {\n\t\t\tbuilder.queryParam(\"post_logout_redirect_uri\", postLogoutRedirectUri);\n\t\t}\n\t\t// @formatter:off\n\t\treturn builder.encode(StandardCharsets.UTF_8)\n\t\t\t\t.build()\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Set the post logout redirect uri template.\n\t *\n\t * <br />\n\t * The supported uri template variables are: {@code {baseScheme}}, {@code {baseHost}},\n\t * {@code {basePort}} and {@code {basePath}}.\n\t *\n\t * <br />\n\t * <b>NOTE:</b> {@code \"{baseUrl}\"} is also supported, which is the same as\n\t * {@code \"{baseScheme}://{baseHost}{basePort}{basePath}\"}\n\t *\n\t * <pre>\n\t * \thandler.setPostLogoutRedirectUri(\"{baseUrl}\");\n\t * </pre>\n\t *\n\t * will make so that {@code post_logout_redirect_uri} will be set to the base url for\n\t * the client application.\n\t * @param postLogoutRedirectUri - A template for creating the\n\t * {@code post_logout_redirect_uri} query parameter\n\t * @since 5.3\n\t */\n\tpublic void setPostLogoutRedirectUri(String postLogoutRedirectUri) {\n\t\tAssert.notNull(postLogoutRedirectUri, \"postLogoutRedirectUri cannot be null\");\n\t\tthis.postLogoutRedirectUri = postLogoutRedirectUri;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/web/logout/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for OpenID Connect 1.0 Logout (servlet).\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.oidc.web.logout;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/web/server/logout/OidcClientInitiatedServerLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.web.server.logout;\n\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A reactive logout success handler for initiating OIDC logout through the user agent.\n *\n * @author Josh Cummings\n * @since 5.2\n * @see <a href=\n * \"https://openid.net/specs/openid-connect-rpinitiated-1_0.html\">RP-Initiated Logout</a>\n * @see org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler\n */\npublic class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogoutSuccessHandler {\n\n\tprivate ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();\n\n\tprivate final RedirectServerLogoutSuccessHandler serverLogoutSuccessHandler = new RedirectServerLogoutSuccessHandler();\n\n\tprivate final ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate @Nullable String postLogoutRedirectUri;\n\n\tprivate Converter<RedirectUriParameters, Mono<String>> redirectUriResolver = new DefaultRedirectUriResolver();\n\n\t/**\n\t * Constructs an {@link OidcClientInitiatedServerLogoutSuccessHandler} with the\n\t * provided parameters\n\t * @param clientRegistrationRepository The\n\t * {@link ReactiveClientRegistrationRepository} to use to derive the\n\t * end_session_endpoint value\n\t */\n\tpublic OidcClientInitiatedServerLogoutSuccessHandler(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t}\n\n\t@Override\n\tpublic Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication) {\n\t\t// @formatter:off\n\t\treturn Mono.just(authentication)\n\t\t\t\t.filter(OAuth2AuthenticationToken.class::isInstance)\n\t\t\t\t.filter((token) -> authentication.getPrincipal() instanceof OidcUser)\n\t\t\t\t.map(OAuth2AuthenticationToken.class::cast)\n\t\t\t\t.map(OAuth2AuthenticationToken::getAuthorizedClientRegistrationId)\n\t\t\t\t.flatMap(this.clientRegistrationRepository::findByRegistrationId)\n\t\t\t\t.flatMap((clientRegistration) ->\n\t\t\t\t\tthis.redirectUriResolver.convert(\n\t\t\t\t\t\t\tnew RedirectUriParameters(exchange.getExchange(), authentication, clientRegistration))\n\t\t\t\t)\n\t\t\t\t.switchIfEmpty(\n\t\t\t\t\t\tthis.serverLogoutSuccessHandler.onLogoutSuccess(exchange, authentication).then(Mono.empty())\n\t\t\t\t)\n\t\t\t\t.flatMap((endpointUri) -> this.redirectStrategy.sendRedirect(exchange.getExchange(), URI.create(endpointUri)));\n\t\t// @formatter:on\n\t}\n\n\tprivate @Nullable URI endSessionEndpoint(@Nullable ClientRegistration clientRegistration) {\n\t\tif (clientRegistration != null) {\n\t\t\tObject endSessionEndpoint = clientRegistration.getProviderDetails()\n\t\t\t\t.getConfigurationMetadata()\n\t\t\t\t.get(\"end_session_endpoint\");\n\t\t\tif (endSessionEndpoint != null) {\n\t\t\t\treturn URI.create(endSessionEndpoint.toString());\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String endpointUri(URI endSessionEndpoint, String idToken, @Nullable String postLogoutRedirectUri) {\n\t\tUriComponentsBuilder builder = UriComponentsBuilder.fromUri(endSessionEndpoint);\n\t\tbuilder.queryParam(\"id_token_hint\", idToken);\n\t\tif (postLogoutRedirectUri != null) {\n\t\t\tbuilder.queryParam(\"post_logout_redirect_uri\", postLogoutRedirectUri);\n\t\t}\n\t\treturn builder.encode(StandardCharsets.UTF_8).build().toUriString();\n\t}\n\n\tprivate String idToken(Authentication authentication) {\n\t\tObject principal = authentication.getPrincipal();\n\t\tString idToken = (principal instanceof OidcUser oidcUser) ? oidcUser.getIdToken().getTokenValue() : null;\n\t\tAssert.notNull(idToken, \"idToken cannot be null\");\n\t\treturn idToken;\n\t}\n\n\tprivate @Nullable String postLogoutRedirectUri(ServerHttpRequest request, ClientRegistration clientRegistration) {\n\t\tif (this.postLogoutRedirectUri == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:off\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI())\n\t\t\t\t.replacePath(request.getPath().contextPath().value())\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.fragment(null)\n\t\t\t\t.build();\n\n\t\tMap<String, String> uriVariables = new HashMap<>();\n\t\tString scheme = uriComponents.getScheme();\n\t\turiVariables.put(\"baseScheme\", (scheme != null) ? scheme : \"\");\n\t\turiVariables.put(\"baseUrl\", uriComponents.toUriString());\n\n\t\tString host = uriComponents.getHost();\n\t\turiVariables.put(\"baseHost\", (host != null) ? host : \"\");\n\n\t\tString path = uriComponents.getPath();\n\t\turiVariables.put(\"basePath\", (path != null) ? path : \"\");\n\n\t\tint port = uriComponents.getPort();\n\t\turiVariables.put(\"basePort\", (port == -1) ? \"\" : \":\" + port);\n\n\t\turiVariables.put(\"registrationId\", clientRegistration.getRegistrationId());\n\n\t\treturn UriComponentsBuilder.fromUriString(this.postLogoutRedirectUri)\n\t\t\t\t.buildAndExpand(uriVariables)\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Set the post logout redirect uri template.\n\t *\n\t * <br />\n\t * The supported uri template variables are: {@code {baseScheme}}, {@code {baseHost}},\n\t * {@code {basePort}} and {@code {basePath}}.\n\t *\n\t * <br />\n\t * <b>NOTE:</b> {@code {baseUrl}} is also supported, which is the same as\n\t * {@code \"{baseScheme}://{baseHost}{basePort}{basePath}\"}\n\t *\n\t * <pre>\n\t * \thandler.setPostLogoutRedirectUri(\"{baseUrl}\");\n\t * </pre>\n\t *\n\t * will make so that {@code post_logout_redirect_uri} will be set to the base url for\n\t * the client application.\n\t * @param postLogoutRedirectUri - A template for creating the\n\t * {@code post_logout_redirect_uri} query parameter\n\t * @since 5.3\n\t */\n\tpublic void setPostLogoutRedirectUri(String postLogoutRedirectUri) {\n\t\tAssert.notNull(postLogoutRedirectUri, \"postLogoutRedirectUri cannot be null\");\n\t\tthis.postLogoutRedirectUri = postLogoutRedirectUri;\n\t}\n\n\t/**\n\t * The URL to redirect to after successfully logging out when not originally an OIDC\n\t * login\n\t * @param logoutSuccessUrl the url to redirect to. Default is \"/login?logout\".\n\t */\n\tpublic void setLogoutSuccessUrl(URI logoutSuccessUrl) {\n\t\tAssert.notNull(logoutSuccessUrl, \"logoutSuccessUrl cannot be null\");\n\t\tthis.serverLogoutSuccessHandler.setLogoutSuccessUrl(logoutSuccessUrl);\n\t}\n\n\t/**\n\t * Set the {@link Converter} that converts {@link RedirectUriParameters} to redirect\n\t * URI\n\t * @param redirectUriResolver {@link Converter}\n\t * @since 6.5\n\t */\n\tpublic void setRedirectUriResolver(Converter<RedirectUriParameters, Mono<String>> redirectUriResolver) {\n\t\tAssert.notNull(redirectUriResolver, \"redirectUriResolver cannot be null\");\n\t\tthis.redirectUriResolver = redirectUriResolver;\n\t}\n\n\t/**\n\t * Set the {@link ServerRedirectStrategy} to use, default\n\t * {@link DefaultServerRedirectStrategy}\n\t * @param redirectStrategy {@link ServerRedirectStrategy}\n\t * @since 6.5\n\t */\n\tpublic void setRedirectStrategy(ServerRedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n\t/**\n\t * Parameters, required for redirect URI resolving.\n\t *\n\t * @author Max Batischev\n\t * @since 6.5\n\t */\n\tpublic static final class RedirectUriParameters {\n\n\t\tprivate final ServerWebExchange serverWebExchange;\n\n\t\tprivate final Authentication authentication;\n\n\t\tprivate final ClientRegistration clientRegistration;\n\n\t\tpublic RedirectUriParameters(ServerWebExchange serverWebExchange, Authentication authentication,\n\t\t\t\tClientRegistration clientRegistration) {\n\t\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\t\tAssert.notNull(serverWebExchange, \"serverWebExchange cannot be null\");\n\t\t\tAssert.notNull(authentication, \"authentication cannot be null\");\n\t\t\tthis.serverWebExchange = serverWebExchange;\n\t\t\tthis.authentication = authentication;\n\t\t\tthis.clientRegistration = clientRegistration;\n\t\t}\n\n\t\tpublic ServerWebExchange getServerWebExchange() {\n\t\t\treturn this.serverWebExchange;\n\t\t}\n\n\t\tpublic Authentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\tpublic ClientRegistration getClientRegistration() {\n\t\t\treturn this.clientRegistration;\n\t\t}\n\n\t}\n\n\t/**\n\t * Default {@link Converter} for redirect uri resolving.\n\t *\n\t * @since 6.5\n\t */\n\tprivate final class DefaultRedirectUriResolver implements Converter<RedirectUriParameters, Mono<String>> {\n\n\t\t@Override\n\t\tpublic Mono<String> convert(RedirectUriParameters redirectUriParameters) {\n\t\t\t// @formatter:off\n\t\t\treturn Mono.just(redirectUriParameters.authentication)\n\t\t\t\t.flatMap((authentication) -> {\n\t\t\t\t\tURI endSessionEndpoint = endSessionEndpoint(redirectUriParameters.clientRegistration);\n\t\t\t\t\tif (endSessionEndpoint == null) {\n\t\t\t\t\t\treturn Mono.empty();\n\t\t\t\t\t}\n\t\t\t\t\tString idToken = idToken(authentication);\n\t\t\t\t\tString postLogoutRedirectUri = postLogoutRedirectUri(\n\t\t\t\t\t\t\tredirectUriParameters.serverWebExchange.getRequest(), redirectUriParameters.clientRegistration);\n\t\t\t\t\treturn Mono.just(endpointUri(endSessionEndpoint, idToken, postLogoutRedirectUri));\n\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/web/server/logout/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for OpenID Connect 1.0 Logout (reactive).\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.oidc.web.server.logout;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core classes and interfaces providing support for OAuth 2.0 Client.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A representation of a client registration with an OAuth 2.0 or OpenID Connect 1.0\n * Provider.\n *\n * @author Joe Grandja\n * @author Michael Sosa\n * @since 5.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-2\">Section 2\n * Client Registration</a>\n */\npublic final class ClientRegistration implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate @Nullable String registrationId;\n\n\tprivate @Nullable String clientId;\n\n\tprivate @Nullable String clientSecret;\n\n\tprivate @Nullable ClientAuthenticationMethod clientAuthenticationMethod;\n\n\tprivate @Nullable AuthorizationGrantType authorizationGrantType;\n\n\tprivate @Nullable String redirectUri;\n\n\tprivate Set<String> scopes = Collections.emptySet();\n\n\tprivate ProviderDetails providerDetails = new ProviderDetails();\n\n\tprivate @Nullable String clientName;\n\n\tprivate @Nullable ClientSettings clientSettings;\n\n\tprivate ClientRegistration() {\n\t}\n\n\t/**\n\t * Returns the identifier for the registration.\n\t * @return the identifier for the registration\n\t */\n\tpublic String getRegistrationId() {\n\t\tAssert.notNull(this.registrationId, \"registrationId cannot be null\");\n\t\treturn this.registrationId;\n\t}\n\n\t/**\n\t * Returns the client identifier.\n\t * @return the client identifier\n\t */\n\tpublic String getClientId() {\n\t\tAssert.notNull(this.clientId, \"clientId cannot be null\");\n\t\treturn this.clientId;\n\t}\n\n\t/**\n\t * Returns the client secret.\n\t * @return the client secret\n\t */\n\tpublic String getClientSecret() {\n\t\tAssert.notNull(this.clientSecret, \"clientSecret cannot be null\");\n\t\treturn this.clientSecret;\n\t}\n\n\t/**\n\t * Returns the {@link ClientAuthenticationMethod authentication method} used when\n\t * authenticating the client with the authorization server.\n\t * @return the {@link ClientAuthenticationMethod}\n\t */\n\tpublic ClientAuthenticationMethod getClientAuthenticationMethod() {\n\t\tAssert.notNull(this.clientAuthenticationMethod, \"clientAuthenticationMethod cannot be null\");\n\t\treturn this.clientAuthenticationMethod;\n\t}\n\n\t/**\n\t * Returns the {@link AuthorizationGrantType authorization grant type} used for the\n\t * client.\n\t * @return the {@link AuthorizationGrantType}\n\t */\n\tpublic AuthorizationGrantType getAuthorizationGrantType() {\n\t\tAssert.notNull(this.authorizationGrantType, \"authorizationGrantType cannot be null\");\n\t\treturn this.authorizationGrantType;\n\t}\n\n\t/**\n\t * Returns the uri (or uri template) for the redirection endpoint.\n\t *\n\t * <br />\n\t * The supported uri template variables are: {baseScheme}, {baseHost}, {basePort},\n\t * {basePath} and {registrationId}.\n\t *\n\t * <br />\n\t * <b>NOTE:</b> {baseUrl} is also supported, which is the same as\n\t * {baseScheme}://{baseHost}{basePort}{basePath}.\n\t *\n\t * <br />\n\t * Configuring uri template variables is especially useful when the client is running\n\t * behind a Proxy Server. This ensures that the X-Forwarded-* headers are used when\n\t * expanding the redirect-uri.\n\t * @return the uri (or uri template) for the redirection endpoint\n\t * @since 5.4\n\t */\n\tpublic @Nullable String getRedirectUri() {\n\t\treturn this.redirectUri;\n\t}\n\n\t/**\n\t * Returns the scope(s) used for the client.\n\t * @return the {@code Set} of scope(s)\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n\t/**\n\t * Returns the details of the provider.\n\t * @return the {@link ProviderDetails}\n\t */\n\tpublic ProviderDetails getProviderDetails() {\n\t\treturn this.providerDetails;\n\t}\n\n\t/**\n\t * Returns the logical name of the client or registration.\n\t * @return the client or registration name\n\t */\n\tpublic String getClientName() {\n\t\tAssert.notNull(this.clientName, \"clientName cannot be null\");\n\t\treturn this.clientName;\n\t}\n\n\t/**\n\t * Returns the {@link ClientSettings client configuration settings}.\n\t * @return the {@link ClientSettings}\n\t */\n\tpublic ClientSettings getClientSettings() {\n\t\tAssert.notNull(this.clientSettings, \"clientSettings cannot be null\");\n\t\treturn this.clientSettings;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn \"ClientRegistration{\"\n\t\t\t\t+ \"registrationId='\" + this.registrationId + '\\''\n\t\t\t\t+ \", clientId='\" + this.clientId + '\\''\n\t\t\t\t+ \", clientSecret='\" + this.clientSecret + '\\''\n\t\t\t\t+ \", clientAuthenticationMethod=\" + this.clientAuthenticationMethod\n\t\t\t\t+ \", authorizationGrantType=\" + this.authorizationGrantType\n\t\t\t\t+ \", redirectUri='\" + this.redirectUri\n\t\t\t\t+ '\\'' + \", scopes=\" + this.scopes\n\t\t\t\t+ \", providerDetails=\" + this.providerDetails\n\t\t\t\t+ \", clientName='\" + this.clientName + '\\''\n\t\t\t\t+ \", clientSettings='\" + this.clientSettings + '\\''\n\t\t\t\t+ '}';\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided registration\n\t * identifier.\n\t * @param registrationId the identifier for the registration\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withRegistrationId(String registrationId) {\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\treturn new Builder(registrationId);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided\n\t * {@link ClientRegistration}.\n\t * @param clientRegistration the {@link ClientRegistration} to copy from\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withClientRegistration(ClientRegistration clientRegistration) {\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\treturn new Builder(clientRegistration);\n\t}\n\n\t/**\n\t * Details of the Provider.\n\t */\n\tpublic class ProviderDetails implements Serializable {\n\n\t\tprivate static final long serialVersionUID = 620L;\n\n\t\tprivate @Nullable String authorizationUri;\n\n\t\tprivate @Nullable String tokenUri;\n\n\t\tprivate UserInfoEndpoint userInfoEndpoint = new UserInfoEndpoint();\n\n\t\tprivate @Nullable String jwkSetUri;\n\n\t\tprivate @Nullable String issuerUri;\n\n\t\tprivate Map<String, Object> configurationMetadata = Collections.emptyMap();\n\n\t\tProviderDetails() {\n\t\t}\n\n\t\t/**\n\t\t * Returns the uri for the authorization endpoint.\n\t\t * @return the uri for the authorization endpoint, or {@code null} if not set\n\t\t * (e.g. for grant types that do not use the authorization endpoint)\n\t\t */\n\t\tpublic @Nullable String getAuthorizationUri() {\n\t\t\treturn this.authorizationUri;\n\t\t}\n\n\t\t/**\n\t\t * Returns the uri for the token endpoint.\n\t\t * @return the uri for the token endpoint\n\t\t */\n\t\tpublic String getTokenUri() {\n\t\t\tAssert.notNull(this.tokenUri, \"tokenUri cannot be null\");\n\t\t\treturn this.tokenUri;\n\t\t}\n\n\t\t/**\n\t\t * Returns the details of the {@link UserInfoEndpoint UserInfo Endpoint}.\n\t\t * @return the {@link UserInfoEndpoint}\n\t\t */\n\t\tpublic UserInfoEndpoint getUserInfoEndpoint() {\n\t\t\treturn this.userInfoEndpoint;\n\t\t}\n\n\t\t/**\n\t\t * Returns the uri for the JSON Web Key (JWK) Set endpoint.\n\t\t * @return the uri for the JSON Web Key (JWK) Set endpoint, or {@code null} if not\n\t\t * set\n\t\t */\n\t\tpublic @Nullable String getJwkSetUri() {\n\t\t\treturn this.jwkSetUri;\n\t\t}\n\n\t\t/**\n\t\t * Returns the issuer identifier uri for the OpenID Connect 1.0 provider or the\n\t\t * OAuth 2.0 Authorization Server.\n\t\t * @return the issuer identifier uri for the OpenID Connect 1.0 provider or the\n\t\t * OAuth 2.0 Authorization Server, or {@code null} if not set\n\t\t * @since 5.4\n\t\t */\n\t\tpublic @Nullable String getIssuerUri() {\n\t\t\treturn this.issuerUri;\n\t\t}\n\n\t\t/**\n\t\t * Returns a {@code Map} of the metadata describing the provider's configuration.\n\t\t * @return a {@code Map} of the metadata describing the provider's configuration\n\t\t * @since 5.1\n\t\t */\n\t\tpublic Map<String, Object> getConfigurationMetadata() {\n\t\t\treturn this.configurationMetadata;\n\t\t}\n\n\t\t/**\n\t\t * Details of the UserInfo Endpoint.\n\t\t */\n\t\tpublic class UserInfoEndpoint implements Serializable {\n\n\t\t\tprivate static final long serialVersionUID = 620L;\n\n\t\t\tprivate @Nullable String uri;\n\n\t\t\tprivate AuthenticationMethod authenticationMethod = AuthenticationMethod.HEADER;\n\n\t\t\tprivate @Nullable String userNameAttributeName;\n\n\t\t\tUserInfoEndpoint() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Returns the uri for the user info endpoint.\n\t\t\t * @return the uri for the user info endpoint, or {@code null} if not set\n\t\t\t */\n\t\t\tpublic @Nullable String getUri() {\n\t\t\t\treturn this.uri;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Returns the authentication method for the user info endpoint.\n\t\t\t * @return the {@link AuthenticationMethod} for the user info endpoint.\n\t\t\t * @since 5.1\n\t\t\t */\n\t\t\tpublic AuthenticationMethod getAuthenticationMethod() {\n\t\t\t\treturn this.authenticationMethod;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Returns the attribute name used to access the user's name from the user\n\t\t\t * info response.\n\t\t\t * @return the attribute name used to access the user's name from the user\n\t\t\t * info response, or {@code null} if not set\n\t\t\t */\n\t\t\tpublic @Nullable String getUserNameAttributeName() {\n\t\t\t\treturn this.userNameAttributeName;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for {@link ClientRegistration}.\n\t */\n\tpublic static final class Builder implements Serializable {\n\n\t\tprivate static final long serialVersionUID = 620L;\n\n\t\tprivate static final Log logger = LogFactory.getLog(Builder.class);\n\n\t\tprivate static final List<AuthorizationGrantType> AUTHORIZATION_GRANT_TYPES = Arrays.asList(\n\t\t\t\tAuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS,\n\t\t\t\tAuthorizationGrantType.REFRESH_TOKEN);\n\n\t\tprivate @Nullable String registrationId;\n\n\t\tprivate @Nullable String clientId;\n\n\t\tprivate @Nullable String clientSecret;\n\n\t\tprivate @Nullable ClientAuthenticationMethod clientAuthenticationMethod;\n\n\t\tprivate @Nullable AuthorizationGrantType authorizationGrantType;\n\n\t\tprivate @Nullable String redirectUri;\n\n\t\tprivate @Nullable Set<String> scopes;\n\n\t\tprivate @Nullable String authorizationUri;\n\n\t\tprivate @Nullable String tokenUri;\n\n\t\tprivate @Nullable String userInfoUri;\n\n\t\tprivate AuthenticationMethod userInfoAuthenticationMethod = AuthenticationMethod.HEADER;\n\n\t\tprivate @Nullable String userNameAttributeName;\n\n\t\tprivate @Nullable String jwkSetUri;\n\n\t\tprivate @Nullable String issuerUri;\n\n\t\tprivate Map<String, Object> configurationMetadata = Collections.emptyMap();\n\n\t\tprivate @Nullable String clientName;\n\n\t\tprivate ClientSettings clientSettings = ClientSettings.builder().build();\n\n\t\tprivate Builder(String registrationId) {\n\t\t\tthis.registrationId = registrationId;\n\t\t}\n\n\t\tprivate Builder(ClientRegistration clientRegistration) {\n\t\t\tthis.registrationId = clientRegistration.registrationId;\n\t\t\tthis.clientId = clientRegistration.clientId;\n\t\t\tthis.clientSecret = clientRegistration.clientSecret;\n\t\t\tthis.clientAuthenticationMethod = clientRegistration.clientAuthenticationMethod;\n\t\t\tthis.authorizationGrantType = clientRegistration.authorizationGrantType;\n\t\t\tthis.redirectUri = clientRegistration.redirectUri;\n\t\t\tthis.scopes = new HashSet<>(clientRegistration.getScopes());\n\t\t\tthis.authorizationUri = clientRegistration.providerDetails.authorizationUri;\n\t\t\tthis.tokenUri = clientRegistration.providerDetails.tokenUri;\n\t\t\tthis.userInfoUri = clientRegistration.providerDetails.userInfoEndpoint.uri;\n\t\t\tthis.userInfoAuthenticationMethod = clientRegistration.providerDetails.userInfoEndpoint.authenticationMethod;\n\t\t\tthis.userNameAttributeName = clientRegistration.providerDetails.userInfoEndpoint.userNameAttributeName;\n\t\t\tthis.jwkSetUri = clientRegistration.providerDetails.jwkSetUri;\n\t\t\tthis.issuerUri = clientRegistration.providerDetails.issuerUri;\n\t\t\tthis.configurationMetadata = new HashMap<>(clientRegistration.providerDetails.configurationMetadata);\n\t\t\tthis.clientName = clientRegistration.clientName;\n\t\t\tif (clientRegistration.clientSettings != null) {\n\t\t\t\tthis.clientSettings = clientRegistration.clientSettings;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Sets the registration id.\n\t\t * @param registrationId the registration id\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder registrationId(String registrationId) {\n\t\t\tthis.registrationId = registrationId;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the client identifier.\n\t\t * @param clientId the client identifier\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientId(String clientId) {\n\t\t\tthis.clientId = clientId;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the client secret.\n\t\t * @param clientSecret the client secret\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientSecret(@Nullable String clientSecret) {\n\t\t\tthis.clientSecret = clientSecret;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link ClientAuthenticationMethod authentication method} used when\n\t\t * authenticating the client with the authorization server.\n\t\t * @param clientAuthenticationMethod the authentication method used for the client\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientAuthenticationMethod(ClientAuthenticationMethod clientAuthenticationMethod) {\n\t\t\tthis.clientAuthenticationMethod = clientAuthenticationMethod;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link AuthorizationGrantType authorization grant type} used for the\n\t\t * client.\n\t\t * @param authorizationGrantType the authorization grant type used for the client\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder authorizationGrantType(AuthorizationGrantType authorizationGrantType) {\n\t\t\tthis.authorizationGrantType = authorizationGrantType;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the uri (or uri template) for the redirection endpoint.\n\t\t *\n\t\t * <br />\n\t\t * The supported uri template variables are: {baseScheme}, {baseHost}, {basePort},\n\t\t * {basePath} and {registrationId}.\n\t\t *\n\t\t * <br />\n\t\t * <b>NOTE:</b> {baseUrl} is also supported, which is the same as\n\t\t * {baseScheme}://{baseHost}{basePort}{basePath}.\n\t\t *\n\t\t * <br />\n\t\t * Configuring uri template variables is especially useful when the client is\n\t\t * running behind a Proxy Server. This ensures that the X-Forwarded-* headers are\n\t\t * used when expanding the redirect-uri.\n\t\t * @param redirectUri the uri (or uri template) for the redirection endpoint\n\t\t * @return the {@link Builder}\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Builder redirectUri(@Nullable String redirectUri) {\n\t\t\tthis.redirectUri = redirectUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the scope(s) used for the client.\n\t\t * @param scope the scope(s) used for the client\n\t\t * @return the {@link Builder}\n\t\t */\n\t\t// @formatter:off\n\t\tpublic Builder scope(String @Nullable... scope) {\n\t\t\tif (scope != null && scope.length > 0) {\n\t\t\t\tthis.scopes = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(scope)));\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\t// @formatter:on\n\n\t\t/**\n\t\t * Sets the scope(s) used for the client.\n\t\t * @param scope the scope(s) used for the client\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder scope(@Nullable Collection<String> scope) {\n\t\t\tif (scope != null && !scope.isEmpty()) {\n\t\t\t\tthis.scopes = Collections.unmodifiableSet(new LinkedHashSet<>(scope));\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the uri for the authorization endpoint.\n\t\t * @param authorizationUri the uri for the authorization endpoint\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder authorizationUri(@Nullable String authorizationUri) {\n\t\t\tthis.authorizationUri = authorizationUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the uri for the token endpoint.\n\t\t * @param tokenUri the uri for the token endpoint\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder tokenUri(String tokenUri) {\n\t\t\tthis.tokenUri = tokenUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the uri for the user info endpoint.\n\t\t * @param userInfoUri the uri for the user info endpoint\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder userInfoUri(@Nullable String userInfoUri) {\n\t\t\tthis.userInfoUri = userInfoUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the authentication method for the user info endpoint.\n\t\t * @param userInfoAuthenticationMethod the authentication method for the user info\n\t\t * endpoint\n\t\t * @return the {@link Builder}\n\t\t * @since 5.1\n\t\t */\n\t\tpublic Builder userInfoAuthenticationMethod(AuthenticationMethod userInfoAuthenticationMethod) {\n\t\t\tthis.userInfoAuthenticationMethod = userInfoAuthenticationMethod;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the attribute name used to access the user's name from the user info\n\t\t * response.\n\t\t * @param userNameAttributeName the attribute name used to access the user's name\n\t\t * from the user info response\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder userNameAttributeName(@Nullable String userNameAttributeName) {\n\t\t\tthis.userNameAttributeName = userNameAttributeName;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the uri for the JSON Web Key (JWK) Set endpoint.\n\t\t * @param jwkSetUri the uri for the JSON Web Key (JWK) Set endpoint\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder jwkSetUri(@Nullable String jwkSetUri) {\n\t\t\tthis.jwkSetUri = jwkSetUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the issuer identifier uri for the OpenID Connect 1.0 provider or the OAuth\n\t\t * 2.0 Authorization Server.\n\t\t * @param issuerUri the issuer identifier uri for the OpenID Connect 1.0 provider\n\t\t * or the OAuth 2.0 Authorization Server\n\t\t * @return the {@link Builder}\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Builder issuerUri(@Nullable String issuerUri) {\n\t\t\tthis.issuerUri = issuerUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the metadata describing the provider's configuration.\n\t\t * @param configurationMetadata the metadata describing the provider's\n\t\t * configuration\n\t\t * @return the {@link Builder}\n\t\t * @since 5.1\n\t\t */\n\t\tpublic Builder providerConfigurationMetadata(@Nullable Map<String, Object> configurationMetadata) {\n\t\t\tif (configurationMetadata != null) {\n\t\t\t\tthis.configurationMetadata = new LinkedHashMap<>(configurationMetadata);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the logical name of the client or registration.\n\t\t * @param clientName the client or registration name\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientName(@Nullable String clientName) {\n\t\t\tthis.clientName = clientName;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link ClientSettings client configuration settings}.\n\t\t * @param clientSettings the client configuration settings\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder clientSettings(ClientSettings clientSettings) {\n\t\t\tthis.clientSettings = clientSettings;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link ClientRegistration}.\n\t\t * @return a {@link ClientRegistration}\n\t\t */\n\t\tpublic ClientRegistration build() {\n\t\t\tAssert.notNull(this.authorizationGrantType, \"authorizationGrantType cannot be null\");\n\t\t\tif (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(this.authorizationGrantType)) {\n\t\t\t\tthis.validateClientCredentialsGrantType();\n\t\t\t}\n\t\t\telse if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType)) {\n\t\t\t\tthis.validateAuthorizationCodeGrantType();\n\t\t\t}\n\t\t\tthis.validateAuthorizationGrantTypes();\n\t\t\tthis.validateScopes();\n\t\t\treturn this.create();\n\t\t}\n\n\t\tprivate ClientRegistration create() {\n\t\t\tClientRegistration clientRegistration = new ClientRegistration();\n\t\t\tclientRegistration.registrationId = this.registrationId;\n\t\t\tclientRegistration.clientId = this.clientId;\n\t\t\tclientRegistration.clientSecret = StringUtils.hasText(this.clientSecret) ? this.clientSecret : \"\";\n\t\t\tclientRegistration.clientAuthenticationMethod = (this.clientAuthenticationMethod != null)\n\t\t\t\t\t? this.clientAuthenticationMethod : deduceClientAuthenticationMethod();\n\t\t\tclientRegistration.authorizationGrantType = this.authorizationGrantType;\n\t\t\tclientRegistration.redirectUri = this.redirectUri;\n\t\t\tclientRegistration.scopes = (this.scopes != null) ? this.scopes : Collections.emptySet();\n\t\t\tclientRegistration.providerDetails = createProviderDetails(clientRegistration);\n\t\t\tclientRegistration.clientName = StringUtils.hasText(this.clientName) ? this.clientName\n\t\t\t\t\t: this.registrationId;\n\t\t\tclientRegistration.clientSettings = this.clientSettings;\n\t\t\treturn clientRegistration;\n\t\t}\n\n\t\tprivate ClientAuthenticationMethod deduceClientAuthenticationMethod() {\n\t\t\tAssert.notNull(this.authorizationGrantType, \"authorizationGrantType cannot be null\");\n\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType)\n\t\t\t\t\t&& (!StringUtils.hasText(this.clientSecret))) {\n\t\t\t\treturn ClientAuthenticationMethod.NONE;\n\t\t\t}\n\t\t\treturn ClientAuthenticationMethod.CLIENT_SECRET_BASIC;\n\t\t}\n\n\t\tprivate ProviderDetails createProviderDetails(ClientRegistration clientRegistration) {\n\t\t\tProviderDetails providerDetails = clientRegistration.new ProviderDetails();\n\t\t\tproviderDetails.authorizationUri = this.authorizationUri;\n\t\t\tproviderDetails.tokenUri = this.tokenUri;\n\t\t\tproviderDetails.userInfoEndpoint.uri = this.userInfoUri;\n\t\t\tproviderDetails.userInfoEndpoint.authenticationMethod = this.userInfoAuthenticationMethod;\n\t\t\tproviderDetails.userInfoEndpoint.userNameAttributeName = this.userNameAttributeName;\n\t\t\tproviderDetails.jwkSetUri = this.jwkSetUri;\n\t\t\tproviderDetails.issuerUri = this.issuerUri;\n\t\t\tproviderDetails.configurationMetadata = Collections.unmodifiableMap(this.configurationMetadata);\n\t\t\treturn providerDetails;\n\t\t}\n\n\t\tprivate void validateAuthorizationCodeGrantType() {\n\t\t\tAssert.notNull(this.authorizationGrantType, \"authorizationGrantType cannot be null\");\n\t\t\tAssert.isTrue(AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType),\n\t\t\t\t\t() -> \"authorizationGrantType must be \" + AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\t\tAssert.hasText(this.registrationId, \"registrationId cannot be empty\");\n\t\t\tAssert.hasText(this.clientId, \"clientId cannot be empty\");\n\t\t\tAssert.hasText(this.redirectUri, \"redirectUri cannot be empty\");\n\t\t\tAssert.hasText(this.authorizationUri, \"authorizationUri cannot be empty\");\n\t\t\tAssert.hasText(this.tokenUri, \"tokenUri cannot be empty\");\n\t\t}\n\n\t\tprivate void validateClientCredentialsGrantType() {\n\t\t\tAssert.notNull(this.authorizationGrantType, \"authorizationGrantType cannot be null\");\n\t\t\tAssert.isTrue(AuthorizationGrantType.CLIENT_CREDENTIALS.equals(this.authorizationGrantType),\n\t\t\t\t\t() -> \"authorizationGrantType must be \" + AuthorizationGrantType.CLIENT_CREDENTIALS.getValue());\n\t\t\tAssert.hasText(this.registrationId, \"registrationId cannot be empty\");\n\t\t\tAssert.hasText(this.clientId, \"clientId cannot be empty\");\n\t\t\tAssert.hasText(this.tokenUri, \"tokenUri cannot be empty\");\n\t\t}\n\n\t\tprivate void validateAuthorizationGrantTypes() {\n\t\t\tAssert.notNull(this.authorizationGrantType, \"authorizationGrantType cannot be null\");\n\t\t\tfor (AuthorizationGrantType authorizationGrantType : AUTHORIZATION_GRANT_TYPES) {\n\t\t\t\tif (authorizationGrantType.getValue().equalsIgnoreCase(this.authorizationGrantType.getValue())\n\t\t\t\t\t\t&& !authorizationGrantType.equals(this.authorizationGrantType)) {\n\t\t\t\t\tlogger.warn(LogMessage.format(\n\t\t\t\t\t\t\t\"AuthorizationGrantType: %s does not match the pre-defined constant %s and won't match a valid OAuth2AuthorizedClientProvider\",\n\t\t\t\t\t\t\tthis.authorizationGrantType, authorizationGrantType));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType)\n\t\t\t\t\t&& this.clientSettings.isRequireProofKey()) {\n\t\t\t\tthis.clientSettings = ClientSettings.builder().requireProofKey(false).build();\n\t\t\t}\n\t\t}\n\n\t\tprivate void validateScopes() {\n\t\t\tif (this.scopes == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (String scope : this.scopes) {\n\t\t\t\tAssert.isTrue(validateScope(scope), \"scope \\\"\" + scope + \"\\\" contains invalid characters\");\n\t\t\t}\n\t\t}\n\n\t\tprivate static boolean validateScope(String scope) {\n\t\t\treturn scope.chars()\n\t\t\t\t.allMatch((c) -> withinTheRangeOf(c, 0x21, 0x21) || withinTheRangeOf(c, 0x23, 0x5B)\n\t\t\t\t\t\t|| withinTheRangeOf(c, 0x5D, 0x7E));\n\t\t}\n\n\t\tprivate static boolean withinTheRangeOf(int c, int min, int max) {\n\t\t\treturn c >= min && c <= max;\n\t\t}\n\n\t}\n\n\t/**\n\t * A facility for client configuration settings.\n\t *\n\t * @author DingHao\n\t * @since 6.5\n\t */\n\tpublic static final class ClientSettings implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 7495627155437124692L;\n\n\t\tprivate boolean requireProofKey;\n\n\t\tprivate ClientSettings() {\n\n\t\t}\n\n\t\tpublic boolean isRequireProofKey() {\n\t\t\treturn this.requireProofKey;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (!(o instanceof ClientSettings that)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn this.requireProofKey == that.requireProofKey;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hashCode(this.requireProofKey);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"ClientSettings{\" + \"requireProofKey=\" + this.requireProofKey + '}';\n\t\t}\n\n\t\tpublic static Builder builder() {\n\t\t\treturn new Builder();\n\t\t}\n\n\t\tpublic static final class Builder {\n\n\t\t\tprivate boolean requireProofKey = true;\n\n\t\t\tprivate Builder() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Set to {@code true} if the client is required to provide a proof key\n\t\t\t * challenge and verifier when performing the Authorization Code Grant flow.\n\t\t\t * @param requireProofKey {@code true} if the client is required to provide a\n\t\t\t * proof key challenge and verifier, {@code false} otherwise\n\t\t\t * @return the {@link Builder} for further configuration\n\t\t\t */\n\t\t\tpublic Builder requireProofKey(boolean requireProofKey) {\n\t\t\t\tthis.requireProofKey = requireProofKey;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic ClientSettings build() {\n\t\t\t\tClientSettings clientSettings = new ClientSettings();\n\t\t\t\tclientSettings.requireProofKey = this.requireProofKey;\n\t\t\t\treturn clientSettings;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A repository for OAuth 2.0 / OpenID Connect 1.0 {@link ClientRegistration}(s).\n *\n * <p>\n * <b>NOTE:</b> Client registration information is ultimately stored and owned by the\n * associated Authorization Server. Therefore, this repository provides the capability to\n * store a sub-set copy of the <i>primary</i> client registration information externally\n * from the Authorization Server.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see ClientRegistration\n */\npublic interface ClientRegistrationRepository {\n\n\t/**\n\t * Returns the client registration identified by the provided {@code registrationId},\n\t * or {@code null} if not found.\n\t * @param registrationId the registration identifier\n\t * @return the {@link ClientRegistration} if found, otherwise {@code null}\n\t */\n\t@Nullable ClientRegistration findByRegistrationId(String registrationId);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport com.nimbusds.oauth2.sdk.ParseException;\nimport com.nimbusds.oauth2.sdk.as.AuthorizationServerMetadata;\nimport com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;\nimport net.minidev.json.JSONObject;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.client.SimpleClientHttpRequestFactory;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.util.Assert;\nimport org.springframework.web.client.HttpClientErrorException;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Allows creating a {@link ClientRegistration.Builder} from an <a href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig\">OpenID\n * Provider Configuration</a> or\n * <a href=\"https://tools.ietf.org/html/rfc8414#section-3\">Authorization Server\n * Metadata</a> based on provided issuer.\n *\n * @author Rob Winch\n * @author Josh Cummings\n * @author Rafiullah Hamedy\n * @author Evgeniy Cheban\n * @since 5.1\n */\npublic final class ClientRegistrations {\n\n\tprivate static final String OIDC_METADATA_PATH = \"/.well-known/openid-configuration\";\n\n\tprivate static final String OAUTH_METADATA_PATH = \"/.well-known/oauth-authorization-server\";\n\n\tprivate static final RestTemplate rest = new RestTemplate();\n\n\tstatic {\n\t\tSimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();\n\t\trequestFactory.setConnectTimeout(30_000);\n\t\trequestFactory.setReadTimeout(30_000);\n\t\trest.setRequestFactory(requestFactory);\n\t}\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> typeReference = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate ClientRegistrations() {\n\t}\n\n\t/**\n\t * Creates a {@link ClientRegistration.Builder} using the provided map representation\n\t * of an <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">OpenID\n\t * Provider Configuration Response</a> to initialize the\n\t * {@link ClientRegistration.Builder}.\n\t *\n\t * <p>\n\t * This is useful when the OpenID Provider Configuration is not available at a\n\t * well-known location, or if custom validation is needed for the issuer location\n\t * (e.g. if the issuer is only accessible from a back-channel URI that is different\n\t * from the issuer value in the configuration).\n\t * </p>\n\t *\n\t * <p>\n\t * Example usage:\n\t * </p>\n\t * <pre>\n\t * RequestEntity&lt;Void&gt; request = RequestEntity.get(metadataEndpoint).build();\n\t * ParameterizedTypeReference&lt;Map&lt;String, Object&gt;&gt; typeReference = new ParameterizedTypeReference&lt;&gt;() {};\n\t * Map&lt;String, Object&gt; configuration = rest.exchange(request, typeReference).getBody();\n\t * // Validate configuration.get(\"issuer\") as per in the OIDC specification\n\t * ClientRegistration registration = ClientRegistrations.fromOidcConfiguration(configuration)\n\t *     .clientId(\"client-id\")\n\t *     .clientSecret(\"client-secret\")\n\t *     .build();\n\t * </pre>\n\t * @param configuration the OpenID Provider configuration map\n\t * @return the {@link ClientRegistration} built from the configuration\n\t */\n\tpublic static ClientRegistration.Builder fromOidcConfiguration(Map<String, Object> configuration) {\n\t\tOIDCProviderMetadata metadata = parseInput(configuration, OIDCProviderMetadata::parse);\n\t\tClientRegistration.Builder builder = withProviderConfiguration(metadata, metadata.getIssuer().getValue());\n\t\tbuilder.jwkSetUri(metadata.getJWKSetURI().toASCIIString());\n\t\tif (metadata.getUserInfoEndpointURI() != null) {\n\t\t\tbuilder.userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString());\n\t\t}\n\t\treturn builder;\n\t}\n\n\t/**\n\t * Creates a {@link ClientRegistration.Builder} using the provided <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * by making an <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">OpenID\n\t * Provider Configuration Request</a> and using the values in the <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">OpenID\n\t * Provider Configuration Response</a> to initialize the\n\t * {@link ClientRegistration.Builder}.\n\t *\n\t * <p>\n\t * For example, if the issuer provided is \"https://example.com\", then an \"OpenID\n\t * Provider Configuration Request\" will be made to\n\t * \"https://example.com/.well-known/openid-configuration\". The result is expected to\n\t * be an \"OpenID Provider Configuration Response\".\n\t * </p>\n\t *\n\t * <p>\n\t * Example usage:\n\t * </p>\n\t * <pre>\n\t * ClientRegistration registration = ClientRegistrations.fromOidcIssuerLocation(\"https://example.com\")\n\t *     .clientId(\"client-id\")\n\t *     .clientSecret(\"client-secret\")\n\t *     .build();\n\t * </pre>\n\t * @param issuer the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return a {@link ClientRegistration.Builder} that was initialized by the OpenID\n\t * Provider Configuration.\n\t */\n\tpublic static ClientRegistration.Builder fromOidcIssuerLocation(String issuer) {\n\t\tAssert.hasText(issuer, \"issuer cannot be empty\");\n\t\treturn getBuilder(issuer, oidc(issuer));\n\t}\n\n\t/**\n\t * Creates a {@link ClientRegistration.Builder} using the provided <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * by querying three different discovery endpoints serially, using the values in the\n\t * first successful response to initialize. If an endpoint returns anything other than\n\t * a 200 or a 4xx, the method will exit without attempting subsequent endpoints.\n\t *\n\t * The three endpoints are computed as follows, given that the {@code issuer} is\n\t * composed of a {@code host} and a {@code path}:\n\t *\n\t * <ol>\n\t * <li>{@code host/.well-known/openid-configuration/path}, as defined in\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-5\">RFC 8414's Compatibility\n\t * Notes</a>.</li>\n\t * <li>{@code issuer/.well-known/openid-configuration}, as defined in <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">\n\t * OpenID Provider Configuration</a>.</li>\n\t * <li>{@code host/.well-known/oauth-authorization-server/path}, as defined in\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-3.1\">Authorization Server\n\t * Metadata Request</a>.</li>\n\t * </ol>\n\t *\n\t * Note that the second endpoint is the equivalent of calling\n\t * {@link ClientRegistrations#fromOidcIssuerLocation(String)}.\n\t *\n\t * <p>\n\t * Example usage:\n\t * </p>\n\t * <pre>\n\t * ClientRegistration registration = ClientRegistrations.fromIssuerLocation(\"https://example.com\")\n\t *     .clientId(\"client-id\")\n\t *     .clientSecret(\"client-secret\")\n\t *     .build();\n\t * </pre>\n\t * @param issuer\n\t * @return a {@link ClientRegistration.Builder} that was initialized by one of the\n\t * described endpoints\n\t */\n\tpublic static ClientRegistration.Builder fromIssuerLocation(String issuer) {\n\t\tAssert.hasText(issuer, \"issuer cannot be empty\");\n\t\treturn getBuilder(issuer, oidc(issuer), oidcRfc8414(issuer), oauth(issuer));\n\t}\n\n\tstatic Supplier<ClientRegistration.Builder> oidc(String issuer) {\n\t\tUriComponents uri = oidcUri(issuer);\n\t\t// @formatter:on\n\t\treturn () -> {\n\t\t\tRequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build();\n\t\t\tMap<String, Object> configuration = rest.exchange(request, typeReference).getBody();\n\t\t\tAssert.notNull(configuration, \"OIDC provider configuration cannot be null\");\n\t\t\tOIDCProviderMetadata metadata = parse(configuration, OIDCProviderMetadata::parse);\n\t\t\tClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer)\n\t\t\t\t.jwkSetUri(metadata.getJWKSetURI().toASCIIString());\n\t\t\tif (metadata.getUserInfoEndpointURI() != null) {\n\t\t\t\tbuilder.userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString());\n\t\t\t}\n\t\t\treturn builder;\n\t\t};\n\t}\n\n\tstatic UriComponents oidcUri(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(uri.getPath() + OIDC_METADATA_PATH)\n\t\t\t\t.build();\n\t}\n\n\tstatic Supplier<ClientRegistration.Builder> oidcRfc8414(String issuer) {\n\t\tUriComponents uri = oidcRfc8414Uri(issuer);\n\t\t// @formatter:on\n\t\treturn getRfc8414Builder(issuer, uri);\n\t}\n\n\tstatic UriComponents oidcRfc8414Uri(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(OIDC_METADATA_PATH + uri.getPath())\n\t\t\t\t.build();\n\t}\n\n\tstatic Supplier<ClientRegistration.Builder> oauth(String issuer) {\n\t\tUriComponents uri = oauthUri(issuer);\n\t\treturn getRfc8414Builder(issuer, uri);\n\t}\n\n\tstatic UriComponents oauthUri(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(OAUTH_METADATA_PATH + uri.getPath())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static Supplier<ClientRegistration.Builder> getRfc8414Builder(String issuer, UriComponents uri) {\n\t\treturn () -> {\n\t\t\tRequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build();\n\t\t\tMap<String, Object> configuration = rest.exchange(request, typeReference).getBody();\n\t\t\tAssert.notNull(configuration, \"Authorization server configuration cannot be null\");\n\t\t\tAuthorizationServerMetadata metadata = parse(configuration, AuthorizationServerMetadata::parse);\n\t\t\tClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer);\n\t\t\tURI jwkSetUri = metadata.getJWKSetURI();\n\t\t\tif (jwkSetUri != null) {\n\t\t\t\tbuilder.jwkSetUri(jwkSetUri.toASCIIString());\n\t\t\t}\n\t\t\tString userinfoEndpoint = (String) configuration.get(\"userinfo_endpoint\");\n\t\t\tif (userinfoEndpoint != null) {\n\t\t\t\tbuilder.userInfoUri(userinfoEndpoint);\n\t\t\t}\n\t\t\treturn builder;\n\t\t};\n\t}\n\n\t@SafeVarargs\n\tprivate static ClientRegistration.Builder getBuilder(String issuer,\n\t\t\tSupplier<ClientRegistration.Builder>... suppliers) {\n\t\tString errorMessage = \"Unable to resolve Configuration with the provided Issuer of \\\"\" + issuer + \"\\\"\";\n\t\tList<String> errors = new ArrayList<>();\n\t\tfor (Supplier<ClientRegistration.Builder> supplier : suppliers) {\n\t\t\ttry {\n\t\t\t\treturn supplier.get();\n\t\t\t}\n\t\t\tcatch (HttpClientErrorException ex) {\n\t\t\t\tif (!ex.getStatusCode().is4xxClientError()) {\n\t\t\t\t\tthrow ex;\n\t\t\t\t}\n\t\t\t\terrors.add(ex.getMessage());\n\t\t\t\t// else try another endpoint\n\t\t\t}\n\t\t\tcatch (IllegalArgumentException | IllegalStateException ex) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tcatch (RuntimeException ex) {\n\t\t\t\tthrow new IllegalArgumentException(errorMessage, ex);\n\t\t\t}\n\t\t}\n\t\tif (!errors.isEmpty()) {\n\t\t\tthrow new IllegalArgumentException(errorMessage + \", errors: \" + errors);\n\t\t}\n\t\tthrow new IllegalArgumentException(errorMessage);\n\t}\n\n\tprivate static <T> T parseInput(Map<String, Object> body, ThrowingFunction<JSONObject, T, ParseException> parser) {\n\t\ttry {\n\t\t\treturn parse(body, parser);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static <T> T parse(Map<String, Object> body, ThrowingFunction<JSONObject, T, ParseException> parser) {\n\t\ttry {\n\t\t\treturn parser.apply(new JSONObject(body));\n\t\t}\n\t\tcatch (ParseException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprivate static ClientRegistration.Builder withProviderConfiguration(AuthorizationServerMetadata metadata,\n\t\t\tString issuer) {\n\t\tString metadataIssuer = metadata.getIssuer().getValue();\n\t\tAssert.state(issuer.equals(metadataIssuer),\n\t\t\t\t() -> \"The Issuer \\\"\" + metadataIssuer + \"\\\" provided in the configuration metadata did \"\n\t\t\t\t\t\t+ \"not match the requested issuer \\\"\" + issuer + \"\\\"\");\n\t\tString name = URI.create(issuer).getHost();\n\t\tClientAuthenticationMethod method = getClientAuthenticationMethod(metadata.getTokenEndpointAuthMethods());\n\t\tURI authorizationEndpointURI = metadata.getAuthorizationEndpointURI();\n\t\tURI tokenEndpointURI = metadata.getTokenEndpointURI();\n\t\tClientAuthenticationMethod authMethod = (method != null) ? method\n\t\t\t\t: ClientAuthenticationMethod.CLIENT_SECRET_BASIC;\n\t\tMap<String, Object> configurationMetadata = new LinkedHashMap<>(metadata.toJSONObject());\n\t\t// @formatter:off\n\t\tClientRegistration.Builder builder = ClientRegistration.withRegistrationId(name)\n\t\t\t\t.userNameAttributeName(IdTokenClaimNames.SUB)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.clientAuthenticationMethod(authMethod)\n\t\t\t\t.redirectUri(\"{baseUrl}/{action}/oauth2/code/{registrationId}\")\n\t\t\t\t.authorizationUri((authorizationEndpointURI != null) ? authorizationEndpointURI.toASCIIString() : null)\n\t\t\t\t.providerConfigurationMetadata(configurationMetadata)\n\t\t\t\t.issuerUri(issuer)\n\t\t\t\t.clientName(issuer);\n\t\tif (tokenEndpointURI != null) {\n\t\t\tbuilder.tokenUri(tokenEndpointURI.toASCIIString());\n\t\t}\n\t\treturn builder;\n\t\t// @formatter:on\n\t}\n\n\tprivate static @Nullable ClientAuthenticationMethod getClientAuthenticationMethod(\n\t\t\tList<com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod> metadataAuthMethods) {\n\t\tif (metadataAuthMethods == null || metadataAuthMethods\n\t\t\t.contains(com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) {\n\t\t\t// If null, the default includes client_secret_basic\n\t\t\treturn ClientAuthenticationMethod.CLIENT_SECRET_BASIC;\n\t\t}\n\t\tif (metadataAuthMethods.contains(com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.CLIENT_SECRET_POST)) {\n\t\t\treturn ClientAuthenticationMethod.CLIENT_SECRET_POST;\n\t\t}\n\t\tif (metadataAuthMethods.contains(com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.NONE)) {\n\t\t\treturn ClientAuthenticationMethod.NONE;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate interface ThrowingFunction<S, T, E extends Throwable> {\n\n\t\tT apply(S src) throws E;\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ClientRegistrationRepository} that stores {@link ClientRegistration}(s)\n * in-memory.\n *\n * @author Joe Grandja\n * @author Rob Winch\n * @author Vedran Pavic\n * @since 5.0\n * @see ClientRegistrationRepository\n * @see ClientRegistration\n */\npublic final class InMemoryClientRegistrationRepository\n\t\timplements ClientRegistrationRepository, Iterable<ClientRegistration> {\n\n\tprivate final Map<String, ClientRegistration> registrations;\n\n\t/**\n\t * Constructs an {@code InMemoryClientRegistrationRepository} using the provided\n\t * parameters.\n\t * @param registrations the client registration(s)\n\t */\n\tpublic InMemoryClientRegistrationRepository(ClientRegistration... registrations) {\n\t\tthis(Arrays.asList(registrations));\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryClientRegistrationRepository} using the provided\n\t * parameters.\n\t * @param registrations the client registration(s)\n\t */\n\tpublic InMemoryClientRegistrationRepository(List<ClientRegistration> registrations) {\n\t\tthis(createRegistrationsMap(registrations));\n\t}\n\n\tprivate static Map<String, ClientRegistration> createRegistrationsMap(List<ClientRegistration> registrations) {\n\t\tAssert.notEmpty(registrations, \"registrations cannot be empty\");\n\t\treturn toUnmodifiableConcurrentMap(registrations);\n\t}\n\n\tprivate static Map<String, ClientRegistration> toUnmodifiableConcurrentMap(List<ClientRegistration> registrations) {\n\t\tConcurrentHashMap<String, ClientRegistration> result = new ConcurrentHashMap<>();\n\t\tfor (ClientRegistration registration : registrations) {\n\t\t\tAssert.state(!result.containsKey(registration.getRegistrationId()),\n\t\t\t\t\t() -> String.format(\"Duplicate key %s\", registration.getRegistrationId()));\n\t\t\tresult.put(registration.getRegistrationId(), registration);\n\t\t}\n\t\treturn Collections.unmodifiableMap(result);\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryClientRegistrationRepository} using the provided\n\t * {@code Map} of {@link ClientRegistration#getRegistrationId() registration id} to\n\t * {@link ClientRegistration}.\n\t * @param registrations the {@code Map} of client registration(s)\n\t * @since 5.2\n\t */\n\tpublic InMemoryClientRegistrationRepository(Map<String, ClientRegistration> registrations) {\n\t\tAssert.notNull(registrations, \"registrations cannot be null\");\n\t\tthis.registrations = registrations;\n\t}\n\n\t@Override\n\tpublic @Nullable ClientRegistration findByRegistrationId(String registrationId) {\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\treturn this.registrations.get(registrationId);\n\t}\n\n\t/**\n\t * Returns an {@code Iterator} of {@link ClientRegistration}.\n\t * @return an {@code Iterator<ClientRegistration>}\n\t */\n\t@Override\n\tpublic Iterator<ClientRegistration> iterator() {\n\t\treturn this.registrations.values().iterator();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\n\n/**\n * A Reactive {@link ClientRegistrationRepository} that stores\n * {@link ClientRegistration}(s) in-memory.\n *\n * @author Rob Winch\n * @author Ebert Toribio\n * @since 5.1\n * @see ClientRegistrationRepository\n * @see ClientRegistration\n */\npublic final class InMemoryReactiveClientRegistrationRepository\n\t\timplements ReactiveClientRegistrationRepository, Iterable<ClientRegistration> {\n\n\tprivate final Map<String, ClientRegistration> clientIdToClientRegistration;\n\n\t/**\n\t * Constructs an {@code InMemoryReactiveClientRegistrationRepository} using the\n\t * provided parameters.\n\t * @param registrations the client registration(s)\n\t */\n\tpublic InMemoryReactiveClientRegistrationRepository(ClientRegistration... registrations) {\n\t\tthis(toList(registrations));\n\t}\n\n\tprivate static List<ClientRegistration> toList(ClientRegistration... registrations) {\n\t\tAssert.notEmpty(registrations, \"registrations cannot be null or empty\");\n\t\treturn Arrays.asList(registrations);\n\t}\n\n\t/**\n\t * Constructs an {@code InMemoryReactiveClientRegistrationRepository} using the\n\t * provided parameters.\n\t * @param registrations the client registration(s)\n\t */\n\tpublic InMemoryReactiveClientRegistrationRepository(List<ClientRegistration> registrations) {\n\t\tthis.clientIdToClientRegistration = toUnmodifiableConcurrentMap(registrations);\n\t}\n\n\t@Override\n\tpublic Mono<ClientRegistration> findByRegistrationId(String registrationId) {\n\t\treturn Mono.justOrEmpty(this.clientIdToClientRegistration.get(registrationId));\n\t}\n\n\t/**\n\t * Returns an {@code Iterator} of {@link ClientRegistration}.\n\t * @return an {@code Iterator<ClientRegistration>}\n\t */\n\t@Override\n\tpublic Iterator<ClientRegistration> iterator() {\n\t\treturn this.clientIdToClientRegistration.values().iterator();\n\t}\n\n\tprivate static Map<String, ClientRegistration> toUnmodifiableConcurrentMap(List<ClientRegistration> registrations) {\n\t\tAssert.notEmpty(registrations, \"registrations cannot be null or empty\");\n\t\tConcurrentHashMap<String, ClientRegistration> result = new ConcurrentHashMap<>();\n\t\tfor (ClientRegistration registration : registrations) {\n\t\t\tAssert.notNull(registration, \"no registration can be null\");\n\t\t\tif (result.containsKey(registration.getRegistrationId())) {\n\t\t\t\tthrow new IllegalStateException(String.format(\"Duplicate key %s\", registration.getRegistrationId()));\n\t\t\t}\n\t\t\tresult.put(registration.getRegistrationId(), registration);\n\t\t}\n\t\treturn Collections.unmodifiableMap(result);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ReactiveClientRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport reactor.core.publisher.Mono;\n\n/**\n * A reactive repository for OAuth 2.0 / OpenID Connect 1.0 {@link ClientRegistration}(s).\n *\n * <p>\n * <b>NOTE:</b> Client registration information is ultimately stored and owned by the\n * associated Authorization Server. Therefore, this repository provides the capability to\n * store a sub-set copy of the <i>primary</i> client registration information externally\n * from the Authorization Server.\n *\n * @author Rob Winch\n * @since 5.1\n * @see ClientRegistration\n */\npublic interface ReactiveClientRegistrationRepository {\n\n\t/**\n\t * Returns the client registration identified by the provided {@code registrationId},\n\t * or {@code null} if not found.\n\t * @param registrationId the registration identifier\n\t * @return the {@link ClientRegistration} if found, otherwise {@code null}\n\t */\n\tMono<ClientRegistration> findByRegistrationId(String registrationId);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/SupplierClientRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.util.Iterator;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.function.SingletonSupplier;\n\n/**\n * A {@link ClientRegistrationRepository} that lazily calls to retrieve\n * {@link ClientRegistration}(s) when requested.\n *\n * @author Justin Tay\n * @since 6.2\n * @see ClientRegistrationRepository\n * @see ClientRegistration\n */\npublic final class SupplierClientRegistrationRepository\n\t\timplements ClientRegistrationRepository, Iterable<ClientRegistration> {\n\n\tprivate final Supplier<? extends ClientRegistrationRepository> repositorySupplier;\n\n\t/**\n\t * Constructs an {@code SupplierClientRegistrationRepository} using the provided\n\t * parameters.\n\t * @param repositorySupplier the client registration repository supplier\n\t */\n\tpublic <T extends ClientRegistrationRepository & Iterable<ClientRegistration>> SupplierClientRegistrationRepository(\n\t\t\tSupplier<T> repositorySupplier) {\n\t\tAssert.notNull(repositorySupplier, \"repositorySupplier cannot be null\");\n\t\tthis.repositorySupplier = SingletonSupplier.of(repositorySupplier);\n\t}\n\n\t@Override\n\tpublic @Nullable ClientRegistration findByRegistrationId(String registrationId) {\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\treturn this.repositorySupplier.get().findByRegistrationId(registrationId);\n\t}\n\n\t/**\n\t * Returns an {@code Iterator} of {@link ClientRegistration}.\n\t * @return an {@code Iterator<ClientRegistration>}\n\t */\n\t@Override\n\tpublic Iterator<ClientRegistration> iterator() {\n\t\treturn ((Iterable<ClientRegistration>) this.repositorySupplier.get()).iterator();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes and interfaces that provide support for\n * {@link org.springframework.security.oauth2.client.registration.ClientRegistration}.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.registration;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.ResponseErrorHandler;\nimport org.springframework.web.client.RestClientException;\nimport org.springframework.web.client.RestOperations;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.client.UnknownContentTypeException;\n\n/**\n * An implementation of an {@link OAuth2UserService} that supports standard OAuth 2.0\n * Provider's.\n * <p>\n * For standard OAuth 2.0 Provider's, the attribute name used to access the user's name\n * from the UserInfo response is required and therefore must be available via\n * {@link ClientRegistration.ProviderDetails.UserInfoEndpoint#getUserNameAttributeName()\n * UserInfoEndpoint.getUserNameAttributeName()}.\n * <p>\n * <b>NOTE:</b> Attribute names are <b>not</b> standardized between providers and\n * therefore will vary. Please consult the provider's API documentation for the set of\n * supported user attribute names.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2UserService\n * @see OAuth2UserRequest\n * @see OAuth2User\n * @see DefaultOAuth2User\n */\npublic class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {\n\n\tprivate static final String MISSING_USER_INFO_URI_ERROR_CODE = \"missing_user_info_uri\";\n\n\tprivate static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE = \"missing_user_name_attribute\";\n\n\tprivate static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = \"invalid_user_info_response\";\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> PARAMETERIZED_RESPONSE_TYPE = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate Converter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter = new OAuth2UserRequestEntityConverter();\n\n\tprivate Converter<OAuth2UserRequest, Converter<Map<String, Object>, Map<String, Object>>> attributesConverter = (\n\t\t\trequest) -> (attributes) -> attributes;\n\n\tprivate RestOperations restOperations;\n\n\tpublic DefaultOAuth2UserService() {\n\t\tRestTemplate restTemplate = new RestTemplate();\n\t\trestTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());\n\t\tthis.restOperations = restTemplate;\n\t}\n\n\t@Override\n\tpublic OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {\n\t\tAssert.notNull(userRequest, \"userRequest cannot be null\");\n\t\tString userNameAttributeName = getUserNameAttributeName(userRequest);\n\t\tRequestEntity<?> request = this.requestEntityConverter.convert(userRequest);\n\t\tResponseEntity<Map<String, Object>> response = getResponse(userRequest, request);\n\t\tOAuth2AccessToken token = userRequest.getAccessToken();\n\t\tMap<String, Object> body = response.getBody();\n\t\tAssert.notNull(body, \"userInfo response body cannot be null\");\n\t\tMap<String, Object> attributes = this.attributesConverter.convert(userRequest).convert(body);\n\t\tCollection<GrantedAuthority> authorities = getAuthorities(token, attributes, userNameAttributeName);\n\t\treturn new DefaultOAuth2User(authorities, attributes, userNameAttributeName);\n\t}\n\n\t/**\n\t * Use this strategy to adapt user attributes into a format understood by Spring\n\t * Security; by default, the original attributes are preserved.\n\t *\n\t * <p>\n\t * This can be helpful, for example, if the user attribute is nested. Since Spring\n\t * Security needs the username attribute to be at the top level, you can use this\n\t * method to do:\n\t *\n\t * <pre>\n\t *     DefaultOAuth2UserService userService = new DefaultOAuth2UserService();\n\t *     userService.setAttributesConverter((userRequest) -> (attributes) ->\n\t *         Map&lt;String, Object&gt; userObject = (Map&lt;String, Object&gt;) attributes.get(\"user\");\n\t *         attributes.put(\"user-name\", userObject.get(\"user-name\"));\n\t *         return attributes;\n\t *     });\n\t * </pre>\n\t * @param attributesConverter the attribute adaptation strategy to use\n\t * @since 6.3\n\t */\n\tpublic void setAttributesConverter(\n\t\t\tConverter<OAuth2UserRequest, Converter<Map<String, Object>, Map<String, Object>>> attributesConverter) {\n\t\tAssert.notNull(attributesConverter, \"attributesConverter cannot be null\");\n\t\tthis.attributesConverter = attributesConverter;\n\t}\n\n\tprivate ResponseEntity<Map<String, Object>> getResponse(OAuth2UserRequest userRequest, RequestEntity<?> request) {\n\t\ttry {\n\t\t\treturn this.restOperations.exchange(request, PARAMETERIZED_RESPONSE_TYPE);\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tOAuth2Error oauth2Error = ex.getError();\n\t\t\tStringBuilder errorDetails = new StringBuilder();\n\t\t\terrorDetails.append(\"Error details: [\");\n\t\t\terrorDetails.append(\"UserInfo Uri: \")\n\t\t\t\t.append(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri());\n\t\t\terrorDetails.append(\", Error Code: \").append(oauth2Error.getErrorCode());\n\t\t\tif (oauth2Error.getDescription() != null) {\n\t\t\t\terrorDetails.append(\", Error Description: \").append(oauth2Error.getDescription());\n\t\t\t}\n\t\t\terrorDetails.append(\"]\");\n\t\t\toauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,\n\t\t\t\t\t\"An error occurred while attempting to retrieve the UserInfo Resource: \" + errorDetails.toString(),\n\t\t\t\t\tnull);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);\n\t\t}\n\t\tcatch (UnknownContentTypeException ex) {\n\t\t\tString errorMessage = \"An error occurred while attempting to retrieve the UserInfo Resource from '\"\n\t\t\t\t\t+ userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()\n\t\t\t\t\t+ \"': response contains invalid content type '\" + ex.getContentType().toString() + \"'. \"\n\t\t\t\t\t+ \"The UserInfo Response should return a JSON object (content type 'application/json') \"\n\t\t\t\t\t+ \"that contains a collection of name and value pairs of the claims about the authenticated End-User. \"\n\t\t\t\t\t+ \"Please ensure the UserInfo Uri in UserInfoEndpoint for Client Registration '\"\n\t\t\t\t\t+ userRequest.getClientRegistration().getRegistrationId() + \"' conforms to the UserInfo Endpoint, \"\n\t\t\t\t\t+ \"as defined in OpenID Connect 1.0: 'https://openid.net/specs/openid-connect-core-1_0.html#UserInfo'\";\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, errorMessage, null);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);\n\t\t}\n\t\tcatch (RestClientException ex) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,\n\t\t\t\t\t\"An error occurred while attempting to retrieve the UserInfo Resource: \" + ex.getMessage(), null);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);\n\t\t}\n\t}\n\n\tprivate String getUserNameAttributeName(OAuth2UserRequest userRequest) {\n\t\tif (!StringUtils\n\t\t\t.hasText(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri())) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_INFO_URI_ERROR_CODE,\n\t\t\t\t\t\"Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: \"\n\t\t\t\t\t\t\t+ userRequest.getClientRegistration().getRegistrationId(),\n\t\t\t\t\tnull);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t\tString userNameAttributeName = userRequest.getClientRegistration()\n\t\t\t.getProviderDetails()\n\t\t\t.getUserInfoEndpoint()\n\t\t\t.getUserNameAttributeName();\n\t\tif (!StringUtils.hasText(userNameAttributeName)) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE,\n\t\t\t\t\t\"Missing required \\\"user name\\\" attribute name in UserInfoEndpoint for Client Registration: \"\n\t\t\t\t\t\t\t+ userRequest.getClientRegistration().getRegistrationId(),\n\t\t\t\t\tnull);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t\treturn userNameAttributeName;\n\t}\n\n\tprivate Collection<GrantedAuthority> getAuthorities(OAuth2AccessToken token, Map<String, Object> attributes,\n\t\t\tString userNameAttributeName) {\n\t\tCollection<GrantedAuthority> authorities = new LinkedHashSet<>();\n\t\tauthorities.add(new OAuth2UserAuthority(attributes, userNameAttributeName));\n\t\tfor (String authority : token.getScopes()) {\n\t\t\tauthorities.add(new SimpleGrantedAuthority(\"SCOPE_\" + authority));\n\t\t}\n\t\treturn authorities;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the {@link OAuth2UserRequest} to a\n\t * {@link RequestEntity} representation of the UserInfo Request.\n\t * @param requestEntityConverter the {@link Converter} used for converting to a\n\t * {@link RequestEntity} representation of the UserInfo Request\n\t * @since 5.1\n\t */\n\tpublic final void setRequestEntityConverter(Converter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter) {\n\t\tAssert.notNull(requestEntityConverter, \"requestEntityConverter cannot be null\");\n\t\tthis.requestEntityConverter = requestEntityConverter;\n\t}\n\n\t/**\n\t * Sets the {@link RestOperations} used when requesting the UserInfo resource.\n\t *\n\t * <p>\n\t * <b>NOTE:</b> At a minimum, the supplied {@code restOperations} must be configured\n\t * with the following:\n\t * <ol>\n\t * <li>{@link ResponseErrorHandler} - {@link OAuth2ErrorResponseErrorHandler}</li>\n\t * </ol>\n\t * @param restOperations the {@link RestOperations} used when requesting the UserInfo\n\t * resource\n\t * @since 5.1\n\t */\n\tpublic final void setRestOperations(RestOperations restOperations) {\n\t\tAssert.notNull(restOperations, \"restOperations cannot be null\");\n\t\tthis.restOperations = restOperations;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.nimbusds.oauth2.sdk.ErrorObject;\nimport com.nimbusds.openid.connect.sdk.UserInfoErrorResponse;\nimport net.minidev.json.JSONObject;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.function.UnsupportedMediaTypeException;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.WebClient;\n\n/**\n * An implementation of an {@link ReactiveOAuth2UserService} that supports standard OAuth\n * 2.0 Provider's.\n * <p>\n * For standard OAuth 2.0 Provider's, the attribute name used to access the user's name\n * from the UserInfo response is required and therefore must be available via\n * {@link org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails.UserInfoEndpoint#getUserNameAttributeName()\n * UserInfoEndpoint.getUserNameAttributeName()}.\n * <p>\n * <b>NOTE:</b> Attribute names are <b>not</b> standardized between providers and\n * therefore will vary. Please consult the provider's API documentation for the set of\n * supported user attribute names.\n *\n * @author Rob Winch\n * @since 5.1\n * @see ReactiveOAuth2UserService\n * @see OAuth2UserRequest\n * @see OAuth2User\n * @see DefaultOAuth2User\n */\npublic class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {\n\n\tprivate static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = \"invalid_user_info_response\";\n\n\tprivate static final String MISSING_USER_INFO_URI_ERROR_CODE = \"missing_user_info_uri\";\n\n\tprivate static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE = \"missing_user_name_attribute\";\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate static final ParameterizedTypeReference<Map<String, String>> STRING_STRING_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate Converter<OAuth2UserRequest, Converter<Map<String, Object>, Map<String, Object>>> attributesConverter = (\n\t\t\trequest) -> (attributes) -> attributes;\n\n\tprivate WebClient webClient = WebClient.create();\n\n\t@Override\n\tpublic Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {\n\t\treturn Mono.defer(() -> {\n\t\t\tAssert.notNull(userRequest, \"userRequest cannot be null\");\n\t\t\tString userInfoUri = userRequest.getClientRegistration()\n\t\t\t\t.getProviderDetails()\n\t\t\t\t.getUserInfoEndpoint()\n\t\t\t\t.getUri();\n\t\t\tif (!StringUtils.hasText(userInfoUri)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_INFO_URI_ERROR_CODE,\n\t\t\t\t\t\t\"Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: \"\n\t\t\t\t\t\t\t\t+ userRequest.getClientRegistration().getRegistrationId(),\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\tString userNameAttributeName = userRequest.getClientRegistration()\n\t\t\t\t.getProviderDetails()\n\t\t\t\t.getUserInfoEndpoint()\n\t\t\t\t.getUserNameAttributeName();\n\t\t\tif (!StringUtils.hasText(userNameAttributeName)) {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE,\n\t\t\t\t\t\t\"Missing required \\\"user name\\\" attribute name in UserInfoEndpoint for Client Registration: \"\n\t\t\t\t\t\t\t\t+ userRequest.getClientRegistration().getRegistrationId(),\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t}\n\t\t\tAuthenticationMethod authenticationMethod = userRequest.getClientRegistration()\n\t\t\t\t.getProviderDetails()\n\t\t\t\t.getUserInfoEndpoint()\n\t\t\t\t.getAuthenticationMethod();\n\t\t\tWebClient.RequestHeadersSpec<?> requestHeadersSpec = getRequestHeaderSpec(userRequest, userInfoUri,\n\t\t\t\t\tauthenticationMethod);\n\t\t\t// @formatter:off\n\t\t\tMono<Map<String, Object>> userAttributes = requestHeadersSpec.retrieve()\n\t\t\t\t\t.onStatus(HttpStatusCode::isError, (response) ->\n\t\t\t\t\t\tparse(response)\n\t\t\t\t\t\t\t.map((userInfoErrorResponse) -> {\n\t\t\t\t\t\t\t\tString description = userInfoErrorResponse.getErrorObject().getDescription();\n\t\t\t\t\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, description,\n\t\t\t\t\t\t\t\t\tnull);\n\t\t\t\t\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\t.bodyToMono(DefaultReactiveOAuth2UserService.STRING_OBJECT_MAP)\n\t\t\t\t\t.mapNotNull((attributes) -> this.attributesConverter.convert(userRequest).convert(attributes));\n\t\t\treturn userAttributes.map((attrs) -> {\n\t\t\t\tGrantedAuthority authority = new OAuth2UserAuthority(attrs, userNameAttributeName);\n\t\t\t\tSet<GrantedAuthority> authorities = new HashSet<>();\n\t\t\t\tauthorities.add(authority);\n\t\t\t\tOAuth2AccessToken token = userRequest.getAccessToken();\n\t\t\t\tfor (String scope : token.getScopes()) {\n\t\t\t\t\tauthorities.add(new SimpleGrantedAuthority(\"SCOPE_\" + scope));\n\t\t\t\t}\n\n\t\t\t\treturn new DefaultOAuth2User(authorities, attrs, userNameAttributeName);\n\t\t\t})\n\t\t\t.onErrorMap((ex) -> (ex instanceof UnsupportedMediaTypeException\n\t\t\t\t\t|| (ex.getCause() != null && ex.getCause() instanceof UnsupportedMediaTypeException)), (ex) -> {\n\t\t\t\tUnsupportedMediaTypeException umte = (ex instanceof UnsupportedMediaTypeException)\n\t\t\t\t\t\t? (UnsupportedMediaTypeException) ex : (UnsupportedMediaTypeException) ex.getCause();\n\t\t\t\tString contentType = (umte != null && umte.getContentType() != null)\n\t\t\t\t\t\t? umte.getContentType().toString() : \"unknown\";\n\t\t\t\tString errorMessage = \"An error occurred while attempting to retrieve the UserInfo Resource from '\"\n\t\t\t\t\t\t+ userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint()\n\t\t\t\t\t\t\t\t.getUri()\n\t\t\t\t\t\t+ \"': response contains invalid content type '\" + contentType + \"'. \"\n\t\t\t\t\t\t+ \"The UserInfo Response should return a JSON object (content type 'application/json') \"\n\t\t\t\t\t\t+ \"that contains a collection of name and value pairs of the claims about the authenticated End-User. \"\n\t\t\t\t\t\t+ \"Please ensure the UserInfo Uri in UserInfoEndpoint for Client Registration '\"\n\t\t\t\t\t\t+ userRequest.getClientRegistration().getRegistrationId()\n\t\t\t\t\t\t+ \"' conforms to the UserInfo Endpoint, \"\n\t\t\t\t\t\t+ \"as defined in OpenID Connect 1.0: 'https://openid.net/specs/openid-connect-core-1_0.html#UserInfo'\";\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, errorMessage,\n\t\t\t\t\t\tnull);\n\t\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);\n\t\t\t})\n\t\t\t.onErrorMap((ex) -> {\n\t\t\t\tOAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,\n\t\t\t\t\t\t\"An error occurred reading the UserInfo response: \" + ex.getMessage(), null);\n\t\t\t\treturn new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);\n\t\t\t});\n\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate WebClient.RequestHeadersSpec<?> getRequestHeaderSpec(OAuth2UserRequest userRequest, String userInfoUri,\n\t\t\tAuthenticationMethod authenticationMethod) {\n\t\tif (AuthenticationMethod.FORM.equals(authenticationMethod)) {\n\t\t\t// @formatter:off\n\t\t\treturn this.webClient.post()\n\t\t\t\t\t.uri(userInfoUri)\n\t\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t\t.bodyValue(\"access_token=\" + userRequest.getAccessToken().getTokenValue());\n\t\t\t// @formatter:on\n\t\t}\n\t\t// @formatter:off\n\t\treturn this.webClient.get()\n\t\t\t\t.uri(userInfoUri)\n\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.headers((headers) -> headers\n\t\t\t\t\t\t.setBearerAuth(userRequest.getAccessToken().getTokenValue())\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Use this strategy to adapt user attributes into a format understood by Spring\n\t * Security; by default, the original attributes are preserved.\n\t *\n\t * <p>\n\t * This can be helpful, for example, if the user attribute is nested. Since Spring\n\t * Security needs the username attribute to be at the top level, you can use this\n\t * method to do:\n\t *\n\t * <pre>\n\t *     DefaultReactiveOAuth2UserService userService = new DefaultReactiveOAuth2UserService();\n\t *     userService.setAttributesConverter((userRequest) -> (attributes) ->\n\t *         Map&lt;String, Object&gt; userObject = (Map&lt;String, Object&gt;) attributes.get(\"user\");\n\t *         attributes.put(\"user-name\", userObject.get(\"user-name\"));\n\t *         return attributes;\n\t *     });\n\t * </pre>\n\t * @param attributesConverter the attribute adaptation strategy to use\n\t * @since 6.3\n\t */\n\tpublic void setAttributesConverter(\n\t\t\tConverter<OAuth2UserRequest, Converter<Map<String, Object>, Map<String, Object>>> attributesConverter) {\n\t\tAssert.notNull(attributesConverter, \"attributesConverter cannot be null\");\n\t\tthis.attributesConverter = attributesConverter;\n\t}\n\n\t/**\n\t * Sets the {@link WebClient} used for retrieving the user endpoint\n\t * @param webClient the client to use\n\t */\n\tpublic void setWebClient(WebClient webClient) {\n\t\tAssert.notNull(webClient, \"webClient cannot be null\");\n\t\tthis.webClient = webClient;\n\t}\n\n\tprivate static Mono<UserInfoErrorResponse> parse(ClientResponse httpResponse) {\n\t\tString wwwAuth = httpResponse.headers().asHttpHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE);\n\t\tif (StringUtils.hasLength(wwwAuth)) {\n\t\t\t// Bearer token error?\n\t\t\treturn Mono.fromCallable(() -> UserInfoErrorResponse.parse(wwwAuth));\n\t\t}\n\t\t// Other error?\n\t\treturn httpResponse.bodyToMono(STRING_STRING_MAP)\n\t\t\t.map((body) -> new UserInfoErrorResponse(ErrorObject.parse(new JSONObject(body))));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DelegatingOAuth2UserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2UserService} that simply delegates to its internal\n * {@code List} of {@link OAuth2UserService}(s).\n * <p>\n * Each {@link OAuth2UserService} is given a chance to\n * {@link OAuth2UserService#loadUser(OAuth2UserRequest) load} an {@link OAuth2User} with\n * the first {@code non-null} {@link OAuth2User} being returned.\n *\n * @param <R> The type of OAuth 2.0 User Request\n * @param <U> The type of OAuth 2.0 User\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2UserService\n * @see OAuth2UserRequest\n * @see OAuth2User\n */\npublic class DelegatingOAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User>\n\t\timplements OAuth2UserService<R, U> {\n\n\tprivate final List<OAuth2UserService<R, U>> userServices;\n\n\t/**\n\t * Constructs a {@code DelegatingOAuth2UserService} using the provided parameters.\n\t * @param userServices a {@code List} of {@link OAuth2UserService}(s)\n\t */\n\tpublic DelegatingOAuth2UserService(List<OAuth2UserService<R, U>> userServices) {\n\t\tAssert.notEmpty(userServices, \"userServices cannot be empty\");\n\t\tthis.userServices = Collections.unmodifiableList(new ArrayList<>(userServices));\n\t}\n\n\t@Override\n\tpublic @Nullable U loadUser(R userRequest) throws OAuth2AuthenticationException {\n\t\tAssert.notNull(userRequest, \"userRequest cannot be null\");\n\t\t// @formatter:off\n\t\treturn this.userServices.stream()\n\t\t\t\t.map((userService) -> userService.loadUser(userRequest))\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(null);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/OAuth2UserRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Represents a request the {@link OAuth2UserService} uses when initiating a request to\n * the UserInfo Endpoint.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see ClientRegistration\n * @see OAuth2AccessToken\n * @see OAuth2UserService\n */\npublic class OAuth2UserRequest {\n\n\tprivate final ClientRegistration clientRegistration;\n\n\tprivate final OAuth2AccessToken accessToken;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\t/**\n\t * Constructs an {@code OAuth2UserRequest} using the provided parameters.\n\t * @param clientRegistration the client registration\n\t * @param accessToken the access token\n\t */\n\tpublic OAuth2UserRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken) {\n\t\tthis(clientRegistration, accessToken, Collections.emptyMap());\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2UserRequest} using the provided parameters.\n\t * @param clientRegistration the client registration\n\t * @param accessToken the access token\n\t * @param additionalParameters the additional parameters, may be empty\n\t * @since 5.1\n\t */\n\tpublic OAuth2UserRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken,\n\t\t\tMap<String, Object> additionalParameters) {\n\t\tAssert.notNull(clientRegistration, \"clientRegistration cannot be null\");\n\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\tthis.clientRegistration = clientRegistration;\n\t\tthis.accessToken = accessToken;\n\t\tthis.additionalParameters = Collections.unmodifiableMap(CollectionUtils.isEmpty(additionalParameters)\n\t\t\t\t? Collections.emptyMap() : new LinkedHashMap<>(additionalParameters));\n\t}\n\n\t/**\n\t * Returns the {@link ClientRegistration client registration}.\n\t * @return the {@link ClientRegistration}\n\t */\n\tpublic ClientRegistration getClientRegistration() {\n\t\treturn this.clientRegistration;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessToken access token}.\n\t * @return the {@link OAuth2AccessToken}\n\t */\n\tpublic OAuth2AccessToken getAccessToken() {\n\t\treturn this.accessToken;\n\t}\n\n\t/**\n\t * Returns the additional parameters that may be used in the request.\n\t * @return a {@code Map} of the additional parameters, may be empty.\n\t * @since 5.1\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/OAuth2UserRequestEntityConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.net.URI;\nimport java.util.Collections;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@link Converter} that converts the provided {@link OAuth2UserRequest} to a\n * {@link RequestEntity} representation of a request for the UserInfo Endpoint.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see Converter\n * @see OAuth2UserRequest\n * @see RequestEntity\n */\npublic class OAuth2UserRequestEntityConverter implements Converter<OAuth2UserRequest, RequestEntity<?>> {\n\n\tprivate static final MediaType DEFAULT_CONTENT_TYPE = MediaType\n\t\t.valueOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE + \";charset=UTF-8\");\n\n\t/**\n\t * Returns the {@link RequestEntity} used for the UserInfo Request.\n\t * @param userRequest the user request\n\t * @return the {@link RequestEntity} used for the UserInfo Request\n\t */\n\t@Override\n\tpublic RequestEntity<?> convert(OAuth2UserRequest userRequest) {\n\t\tClientRegistration clientRegistration = userRequest.getClientRegistration();\n\t\tString userInfoUri = clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri();\n\t\tAssert.hasText(userInfoUri, \"UserInfo Endpoint Uri is required\");\n\t\tHttpMethod httpMethod = getHttpMethod(clientRegistration);\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));\n\t\tURI uri = UriComponentsBuilder.fromUriString(userInfoUri).build().toUri();\n\n\t\tRequestEntity<?> request;\n\t\tif (HttpMethod.POST.equals(httpMethod)) {\n\t\t\theaders.setContentType(DEFAULT_CONTENT_TYPE);\n\t\t\tMultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();\n\t\t\tformParameters.add(OAuth2ParameterNames.ACCESS_TOKEN, userRequest.getAccessToken().getTokenValue());\n\t\t\trequest = new RequestEntity<>(formParameters, headers, httpMethod, uri);\n\t\t}\n\t\telse {\n\t\t\theaders.setBearerAuth(userRequest.getAccessToken().getTokenValue());\n\t\t\trequest = new RequestEntity<>(headers, httpMethod, uri);\n\t\t}\n\n\t\treturn request;\n\t}\n\n\tprivate HttpMethod getHttpMethod(ClientRegistration clientRegistration) {\n\t\tif (AuthenticationMethod.FORM\n\t\t\t.equals(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())) {\n\t\t\treturn HttpMethod.POST;\n\t\t}\n\t\treturn HttpMethod.GET;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/OAuth2UserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\n\n/**\n * Implementations of this interface are responsible for obtaining the user attributes of\n * the End-User (Resource Owner) from the UserInfo Endpoint using the\n * {@link OAuth2UserRequest#getAccessToken() Access Token} granted to the\n * {@link OAuth2UserRequest#getClientRegistration() Client} and returning an\n * {@link AuthenticatedPrincipal} in the form of an {@link OAuth2User}.\n *\n * @param <R> The type of OAuth 2.0 User Request\n * @param <U> The type of OAuth 2.0 User\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2UserRequest\n * @see OAuth2User\n * @see AuthenticatedPrincipal\n */\n@FunctionalInterface\npublic interface OAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User> {\n\n\t/**\n\t * Returns an {@link OAuth2User} after obtaining the user attributes of the End-User\n\t * from the UserInfo Endpoint.\n\t * @param userRequest the user request\n\t * @return an {@link OAuth2User}\n\t * @throws OAuth2AuthenticationException if an error occurs while attempting to obtain\n\t * the user attributes from the UserInfo Endpoint\n\t */\n\t@Nullable U loadUser(R userRequest) throws OAuth2AuthenticationException;\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/ReactiveOAuth2UserService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\n\n/**\n * Implementations of this interface are responsible for obtaining the user attributes of\n * the End-User (Resource Owner) from the UserInfo Endpoint using the\n * {@link OAuth2UserRequest#getAccessToken() Access Token} granted to the\n * {@link OAuth2UserRequest#getClientRegistration() Client} and returning an\n * {@link AuthenticatedPrincipal} in the form of an {@link OAuth2User}.\n *\n * @param <R> The type of OAuth 2.0 User Request\n * @param <U> The type of OAuth 2.0 User\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2UserRequest\n * @see OAuth2User\n * @see AuthenticatedPrincipal\n */\n@FunctionalInterface\npublic interface ReactiveOAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User> {\n\n\t/**\n\t * Returns an {@link OAuth2User} after obtaining the user attributes of the End-User\n\t * from the UserInfo Endpoint.\n\t * @param userRequest the user request\n\t * @return an {@link OAuth2User}\n\t * @throws OAuth2AuthenticationException if an error occurs while attempting to obtain\n\t * the user attributes from the UserInfo Endpoint\n\t */\n\tMono<U> loadUser(R userRequest) throws OAuth2AuthenticationException;\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes and interfaces providing support to the client for initiating requests to the\n * OAuth 2.0 Authorization Server's UserInfo Endpoint.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/AuthenticatedPrincipalOAuth2AuthorizedClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientRepository} that delegates to the\n * provided {@link OAuth2AuthorizedClientService} if the current {@code Principal} is\n * authenticated, otherwise, to the default (or provided)\n * {@link OAuth2AuthorizedClientRepository} if the current request is unauthenticated (or\n * anonymous). The default {@code OAuth2AuthorizedClientRepository} is\n * {@link HttpSessionOAuth2AuthorizedClientRepository}.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AuthorizedClientRepository\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientService\n * @see HttpSessionOAuth2AuthorizedClientRepository\n */\npublic final class AuthenticatedPrincipalOAuth2AuthorizedClientRepository implements OAuth2AuthorizedClientRepository {\n\n\tprivate final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate final OAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate OAuth2AuthorizedClientRepository anonymousAuthorizedClientRepository = new HttpSessionOAuth2AuthorizedClientRepository();\n\n\t/**\n\t * Constructs a {@code AuthenticatedPrincipalOAuth2AuthorizedClientRepository} using\n\t * the provided parameters.\n\t * @param authorizedClientService the authorized client service\n\t */\n\tpublic AuthenticatedPrincipalOAuth2AuthorizedClientRepository(\n\t\t\tOAuth2AuthorizedClientService authorizedClientService) {\n\t\tAssert.notNull(authorizedClientService, \"authorizedClientService cannot be null\");\n\t\tthis.authorizedClientService = authorizedClientService;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizedClientRepository} used for requests that are\n\t * unauthenticated (or anonymous). The default is\n\t * {@link HttpSessionOAuth2AuthorizedClientRepository}.\n\t * @param anonymousAuthorizedClientRepository the repository used for requests that\n\t * are unauthenticated (or anonymous)\n\t */\n\tpublic void setAnonymousAuthorizedClientRepository(\n\t\t\tOAuth2AuthorizedClientRepository anonymousAuthorizedClientRepository) {\n\t\tAssert.notNull(anonymousAuthorizedClientRepository, \"anonymousAuthorizedClientRepository cannot be null\");\n\t\tthis.anonymousAuthorizedClientRepository = anonymousAuthorizedClientRepository;\n\t}\n\n\t@Override\n\tpublic <T extends OAuth2AuthorizedClient> @Nullable T loadAuthorizedClient(String clientRegistrationId,\n\t\t\tAuthentication principal, HttpServletRequest request) {\n\t\tif (this.isPrincipalAuthenticated(principal)) {\n\t\t\treturn this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, principal.getName());\n\t\t}\n\t\treturn this.anonymousAuthorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal, request);\n\t}\n\n\t@Override\n\tpublic void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.isPrincipalAuthenticated(principal)) {\n\t\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);\n\t\t}\n\t\telse {\n\t\t\tthis.anonymousAuthorizedClientRepository.saveAuthorizedClient(authorizedClient, principal, request,\n\t\t\t\t\tresponse);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.isPrincipalAuthenticated(principal)) {\n\t\t\tthis.authorizedClientService.removeAuthorizedClient(clientRegistrationId, principal.getName());\n\t\t}\n\t\telse {\n\t\t\tthis.anonymousAuthorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal, request,\n\t\t\t\t\tresponse);\n\t\t}\n\t}\n\n\tprivate boolean isPrincipalAuthenticated(Authentication authentication) {\n\t\treturn this.authenticationTrustResolver.isAuthenticated(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/AuthorizationRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\n\n/**\n * Implementations of this interface are responsible for the persistence of\n * {@link OAuth2AuthorizationRequest} between requests.\n *\n * <p>\n * Used by the {@link OAuth2AuthorizationRequestRedirectFilter} for persisting the\n * Authorization Request before it initiates the authorization code grant flow. As well,\n * used by the {@link OAuth2LoginAuthenticationFilter} for resolving the associated\n * Authorization Request when handling the callback of the Authorization Response.\n *\n * @param <T> The type of OAuth 2.0 Authorization Request\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2AuthorizationRequest\n * @see HttpSessionOAuth2AuthorizationRequestRepository\n */\npublic interface AuthorizationRequestRepository<T extends OAuth2AuthorizationRequest> {\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest} associated to the provided\n\t * {@code HttpServletRequest} or {@code null} if not available.\n\t * @param request the {@code HttpServletRequest}\n\t * @return the {@link OAuth2AuthorizationRequest} or {@code null} if not available\n\t */\n\t@Nullable T loadAuthorizationRequest(HttpServletRequest request);\n\n\t/**\n\t * Persists the {@link OAuth2AuthorizationRequest} associating it to the provided\n\t * {@code HttpServletRequest} and/or {@code HttpServletResponse}.\n\t * @param authorizationRequest the {@link OAuth2AuthorizationRequest}\n\t * @param request the {@code HttpServletRequest}\n\t * @param response the {@code HttpServletResponse}\n\t */\n\tvoid saveAuthorizationRequest(T authorizationRequest, HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Removes and returns the {@link OAuth2AuthorizationRequest} associated to the\n\t * provided {@code HttpServletRequest} and {@code HttpServletResponse} or if not\n\t * available returns {@code null}.\n\t * @param request the {@code HttpServletRequest}\n\t * @param response the {@code HttpServletResponse}\n\t * @return the {@link OAuth2AuthorizationRequest} or {@code null} if not available\n\t * @since 5.1\n\t */\n\t@Nullable T removeAuthorizationRequest(HttpServletRequest request, HttpServletResponse response);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/ClientAttributes.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.util.Assert;\n\n/**\n * Used for accessing the attribute that stores the\n * {@link ClientRegistration#getRegistrationId()}. This ensures that\n * {@link org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor}\n * aligns with all of ways of setting on both\n * {@link org.springframework.web.client.RestClient} and\n * {@link org.springframework.web.reactive.function.client.WebClient}.\n *\n * @see org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor\n * @see org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver\n * @see org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction\n * @see org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction\n */\npublic final class ClientAttributes {\n\n\tprivate static final String CLIENT_REGISTRATION_ID_ATTR_NAME = ClientRegistration.class.getName()\n\t\t.concat(\".CLIENT_REGISTRATION_ID\");\n\n\t/**\n\t * Resolves the {@link ClientRegistration#getRegistrationId() clientRegistrationId} to\n\t * be used to look up the {@link OAuth2AuthorizedClient}.\n\t * @param attributes the attributes to search.\n\t * @return the registration id to use.\n\t */\n\tpublic static @Nullable String resolveClientRegistrationId(Map<String, Object> attributes) {\n\t\treturn (String) attributes.get(CLIENT_REGISTRATION_ID_ATTR_NAME);\n\t}\n\n\t/**\n\t * Produces a Consumer that adds the {@link ClientRegistration#getRegistrationId()\n\t * clientRegistrationId} to be used to look up the {@link OAuth2AuthorizedClient}.\n\t * @param clientRegistrationId the {@link ClientRegistration#getRegistrationId()\n\t * clientRegistrationId} to be used to look up the {@link OAuth2AuthorizedClient}\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> clientRegistrationId(String clientRegistrationId) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\treturn (attributes) -> attributes.put(CLIENT_REGISTRATION_ID_ATTR_NAME, clientRegistrationId);\n\t}\n\n\tprivate ClientAttributes() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/DefaultOAuth2AuthorizationRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * An implementation of an {@link OAuth2AuthorizationRequestResolver} that attempts to\n * resolve an {@link OAuth2AuthorizationRequest} from the provided\n * {@code HttpServletRequest} using the default request {@code URI} pattern\n * {@code /oauth2/authorization/{registrationId}}.\n *\n * <p>\n * <b>NOTE:</b> The default base {@code URI} {@code /oauth2/authorization} may be\n * overridden via its constructor\n * {@link #DefaultOAuth2AuthorizationRequestResolver(ClientRegistrationRepository, String)}.\n *\n * @author Joe Grandja\n * @author Rob Winch\n * @author Eddú Meléndez\n * @author Mark Heckler\n * @since 5.1\n * @see OAuth2AuthorizationRequestResolver\n * @see OAuth2AuthorizationRequestRedirectFilter\n */\npublic final class DefaultOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {\n\n\t/**\n\t * The default base {@code URI} used for authorization requests.\n\t */\n\tpublic static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = \"/oauth2/authorization\";\n\n\tprivate static final String REGISTRATION_ID_URI_VARIABLE_NAME = \"registrationId\";\n\n\tprivate static final char PATH_DELIMITER = '/';\n\n\tprivate static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder());\n\n\tprivate static final StringKeyGenerator DEFAULT_SECURE_KEY_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\tprivate static final Consumer<OAuth2AuthorizationRequest.Builder> DEFAULT_PKCE_APPLIER = OAuth2AuthorizationRequestCustomizers\n\t\t.withPkce();\n\n\tprivate final ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate final RequestMatcher authorizationRequestMatcher;\n\n\tprivate Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer = (customizer) -> {\n\t};\n\n\t/**\n\t * Constructs a {@code DefaultOAuth2AuthorizationRequestResolver} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * authorization requests\n\t */\n\tpublic DefaultOAuth2AuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {\n\t\tthis(clientRegistrationRepository, DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);\n\t}\n\n\t/**\n\t * Constructs a {@code DefaultOAuth2AuthorizationRequestResolver} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizationRequestBaseUri the base {@code URI} used for resolving\n\t * authorization requests\n\t */\n\tpublic DefaultOAuth2AuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tString authorizationRequestBaseUri) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.hasText(authorizationRequestBaseUri, \"authorizationRequestBaseUri cannot be empty\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizationRequestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(authorizationRequestBaseUri + \"/{\" + REGISTRATION_ID_URI_VARIABLE_NAME + \"}\");\n\t}\n\n\t@Override\n\tpublic @Nullable OAuth2AuthorizationRequest resolve(HttpServletRequest request) {\n\t\tString registrationId = resolveRegistrationId(request);\n\t\tif (registrationId == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString redirectUriAction = getAction(request, \"login\");\n\t\treturn resolve(request, registrationId, redirectUriAction);\n\t}\n\n\t@Override\n\tpublic @Nullable OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId) {\n\t\tif (registrationId == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString redirectUriAction = getAction(request, \"authorize\");\n\t\treturn resolve(request, registrationId, redirectUriAction);\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} to be provided the\n\t * {@link OAuth2AuthorizationRequest.Builder} allowing for further customizations.\n\t * @param authorizationRequestCustomizer the {@code Consumer} to be provided the\n\t * {@link OAuth2AuthorizationRequest.Builder}\n\t * @since 5.3\n\t * @see OAuth2AuthorizationRequestCustomizers\n\t */\n\tpublic void setAuthorizationRequestCustomizer(\n\t\t\tConsumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer) {\n\t\tAssert.notNull(authorizationRequestCustomizer, \"authorizationRequestCustomizer cannot be null\");\n\t\tthis.authorizationRequestCustomizer = authorizationRequestCustomizer;\n\t}\n\n\tprivate String getAction(HttpServletRequest request, String defaultAction) {\n\t\tString action = request.getParameter(\"action\");\n\t\tif (action == null) {\n\t\t\treturn defaultAction;\n\t\t}\n\t\treturn action;\n\t}\n\n\tprivate @Nullable OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId,\n\t\t\tString redirectUriAction) {\n\t\tif (registrationId == null) {\n\t\t\treturn null;\n\t\t}\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);\n\t\tif (clientRegistration == null) {\n\t\t\tthrow new InvalidClientRegistrationIdException(\"Invalid Client Registration with Id: \" + registrationId);\n\t\t}\n\t\tOAuth2AuthorizationRequest.Builder builder = getBuilder(clientRegistration);\n\n\t\tString redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);\n\n\t\tString authorizationUri = clientRegistration.getProviderDetails().getAuthorizationUri();\n\t\tAssert.hasText(authorizationUri, \"Authorization URI is required\");\n\t\t// @formatter:off\n\t\tbuilder.clientId(clientRegistration.getClientId())\n\t\t\t\t.authorizationUri(authorizationUri)\n\t\t\t\t.redirectUri(redirectUriStr)\n\t\t\t\t.scopes(clientRegistration.getScopes())\n\t\t\t\t.state(DEFAULT_STATE_GENERATOR.generateKey());\n\t\t// @formatter:on\n\n\t\tthis.authorizationRequestCustomizer.accept(builder);\n\n\t\treturn builder.build();\n\t}\n\n\tprivate OAuth2AuthorizationRequest.Builder getBuilder(ClientRegistration clientRegistration) {\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t\t.attributes((attrs) ->\n\t\t\t\t\t\t\tattrs.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()));\n\t\t\t// @formatter:on\n\t\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())\n\t\t\t\t\t&& clientRegistration.getScopes().contains(OidcScopes.OPENID)) {\n\t\t\t\t// Section 3.1.2.1 Authentication Request -\n\t\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest scope\n\t\t\t\t// REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope\n\t\t\t\t// value.\n\t\t\t\tapplyNonce(builder);\n\t\t\t}\n\t\t\tif (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())\n\t\t\t\t\t|| clientRegistration.getClientSettings().isRequireProofKey()) {\n\t\t\t\tDEFAULT_PKCE_APPLIER.accept(builder);\n\t\t\t}\n\t\t\treturn builder;\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Invalid Authorization Grant Type (\" + clientRegistration.getAuthorizationGrantType().getValue()\n\t\t\t\t\t\t+ \") for Client Registration with Id: \" + clientRegistration.getRegistrationId());\n\t}\n\n\tprivate @Nullable String resolveRegistrationId(HttpServletRequest request) {\n\t\tif (this.authorizationRequestMatcher.matches(request)) {\n\t\t\treturn this.authorizationRequestMatcher.matcher(request)\n\t\t\t\t.getVariables()\n\t\t\t\t.get(REGISTRATION_ID_URI_VARIABLE_NAME);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Expands the {@link ClientRegistration#getRedirectUri()} with following provided\n\t * variables:<br/>\n\t * - baseUrl (e.g. https://localhost/app) <br/>\n\t * - baseScheme (e.g. https) <br/>\n\t * - baseHost (e.g. localhost) <br/>\n\t * - basePort (e.g. :8080) <br/>\n\t * - basePath (e.g. /app) <br/>\n\t * - registrationId (e.g. google) <br/>\n\t * - action (e.g. login) <br/>\n\t * <p/>\n\t * Null variables are provided as empty strings.\n\t * <p/>\n\t * Default redirectUri is:\n\t * {@code org.springframework.security.config.oauth2.client.CommonOAuth2Provider#DEFAULT_REDIRECT_URL}\n\t * @return expanded URI\n\t */\n\tprivate static String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration,\n\t\t\tString action) {\n\t\tMap<String, String> uriVariables = new HashMap<>();\n\t\turiVariables.put(\"registrationId\", clientRegistration.getRegistrationId());\n\t\t// @formatter:off\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t\t.replacePath(request.getContextPath())\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.fragment(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tString scheme = uriComponents.getScheme();\n\t\turiVariables.put(\"baseScheme\", (scheme != null) ? scheme : \"\");\n\t\tString host = uriComponents.getHost();\n\t\turiVariables.put(\"baseHost\", (host != null) ? host : \"\");\n\t\t// following logic is based on HierarchicalUriComponents#toUriString()\n\t\tint port = uriComponents.getPort();\n\t\turiVariables.put(\"basePort\", (port == -1) ? \"\" : \":\" + port);\n\t\tString path = uriComponents.getPath();\n\t\tif (StringUtils.hasLength(path)) {\n\t\t\tif (path.charAt(0) != PATH_DELIMITER) {\n\t\t\t\tpath = PATH_DELIMITER + path;\n\t\t\t}\n\t\t}\n\t\turiVariables.put(\"basePath\", (path != null) ? path : \"\");\n\t\turiVariables.put(\"baseUrl\", uriComponents.toUriString());\n\t\turiVariables.put(\"action\", (action != null) ? action : \"\");\n\t\treturn UriComponentsBuilder.fromUriString(Objects.requireNonNull(clientRegistration.getRedirectUri()))\n\t\t\t.buildAndExpand(uriVariables)\n\t\t\t.toUriString();\n\t}\n\n\t/**\n\t * Creates nonce and its hash for use in OpenID Connect 1.0 Authentication Requests.\n\t * @param builder where the {@link OidcParameterNames#NONCE} and hash is stored for\n\t * the authentication request\n\t *\n\t * @since 5.2\n\t * @see <a target=\"_blank\" href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\">3.1.2.1.\n\t * Authentication Request</a>\n\t */\n\tprivate static void applyNonce(OAuth2AuthorizationRequest.Builder builder) {\n\t\ttry {\n\t\t\tString nonce = DEFAULT_SECURE_KEY_GENERATOR.generateKey();\n\t\t\tString nonceHash = createHash(nonce);\n\t\t\tbuilder.attributes((attrs) -> attrs.put(OidcParameterNames.NONCE, nonce));\n\t\t\tbuilder.additionalParameters((params) -> params.put(OidcParameterNames.NONCE, nonceHash));\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t}\n\t}\n\n\tprivate static String createHash(String value) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/DefaultOAuth2AuthorizedClientManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationContext;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationSuccessHandler;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;\nimport org.springframework.security.oauth2.client.RemoveAuthorizedClientOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\n/**\n * The default implementation of an {@link OAuth2AuthorizedClientManager} for use within\n * the context of a {@code HttpServletRequest}.\n *\n * <p>\n * (When operating <em>outside</em> of the context of a {@code HttpServletRequest}, use\n * {@link AuthorizedClientServiceOAuth2AuthorizedClientManager} instead.)\n *\n * <h2>Authorized Client Persistence</h2>\n *\n * <p>\n * This manager utilizes an {@link OAuth2AuthorizedClientRepository} to persist\n * {@link OAuth2AuthorizedClient}s.\n *\n * <p>\n * By default, when an authorization attempt succeeds, the {@link OAuth2AuthorizedClient}\n * will be saved in the {@link OAuth2AuthorizedClientRepository}. This functionality can\n * be changed by configuring a custom {@link OAuth2AuthorizationSuccessHandler} via\n * {@link #setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler)}.\n *\n * <p>\n * By default, when an authorization attempt fails due to an\n * {@value OAuth2ErrorCodes#INVALID_GRANT} error, the previously saved\n * {@link OAuth2AuthorizedClient} will be removed from the\n * {@link OAuth2AuthorizedClientRepository}. (The {@value OAuth2ErrorCodes#INVALID_GRANT}\n * error can occur when a refresh token that is no longer valid is used to retrieve a new\n * access token.) This functionality can be changed by configuring a custom\n * {@link OAuth2AuthorizationFailureHandler} via\n * {@link #setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)}.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see OAuth2AuthorizedClientManager\n * @see OAuth2AuthorizedClientProvider\n * @see OAuth2AuthorizationSuccessHandler\n * @see OAuth2AuthorizationFailureHandler\n */\npublic final class DefaultOAuth2AuthorizedClientManager implements OAuth2AuthorizedClientManager {\n\n\t// @formatter:off\n\tprivate static final OAuth2AuthorizedClientProvider DEFAULT_AUTHORIZED_CLIENT_PROVIDER = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken()\n\t\t\t.clientCredentials()\n\t\t\t.build();\n\t// @formatter:on\n\n\tprivate final ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate final OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate OAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper;\n\n\tprivate OAuth2AuthorizationSuccessHandler authorizationSuccessHandler;\n\n\tprivate OAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t/**\n\t * Constructs a {@code DefaultOAuth2AuthorizedClientManager} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientRepository the repository of authorized clients\n\t */\n\tpublic DefaultOAuth2AuthorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\tthis.authorizedClientProvider = DEFAULT_AUTHORIZED_CLIENT_PROVIDER;\n\t\tthis.contextAttributesMapper = new DefaultContextAttributesMapper();\n\t\tthis.authorizationSuccessHandler = (authorizedClient, principal, attributes) -> {\n\t\t\tHttpServletRequest request = (HttpServletRequest) attributes.get(HttpServletRequest.class.getName());\n\t\t\tHttpServletResponse response = (HttpServletResponse) attributes.get(HttpServletResponse.class.getName());\n\t\t\tAssert.notNull(request, \"HttpServletRequest is required\");\n\t\t\tAssert.notNull(response, \"HttpServletResponse is required\");\n\t\t\tauthorizedClientRepository.saveAuthorizedClient(authorizedClient, principal, request, response);\n\t\t};\n\t\tthis.authorizationFailureHandler = new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> {\n\t\t\t\t\tHttpServletRequest request = (HttpServletRequest) attributes\n\t\t\t\t\t\t.get(HttpServletRequest.class.getName());\n\t\t\t\t\tHttpServletResponse response = (HttpServletResponse) attributes\n\t\t\t\t\t\t.get(HttpServletResponse.class.getName());\n\t\t\t\t\tAssert.notNull(request, \"HttpServletRequest is required\");\n\t\t\t\t\tAssert.notNull(response, \"HttpServletResponse is required\");\n\t\t\t\t\tauthorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal, request,\n\t\t\t\t\t\t\tresponse);\n\t\t\t\t});\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {\n\t\tAssert.notNull(authorizeRequest, \"authorizeRequest cannot be null\");\n\t\tString clientRegistrationId = authorizeRequest.getClientRegistrationId();\n\t\tOAuth2AuthorizedClient authorizedClient = authorizeRequest.getAuthorizedClient();\n\t\tAuthentication principal = authorizeRequest.getPrincipal();\n\t\tHttpServletRequest servletRequest = getHttpServletRequestOrDefault(authorizeRequest.getAttributes());\n\t\tAssert.notNull(servletRequest, \"servletRequest cannot be null\");\n\t\tHttpServletResponse servletResponse = getHttpServletResponseOrDefault(authorizeRequest.getAttributes());\n\t\tAssert.notNull(servletResponse, \"servletResponse cannot be null\");\n\t\tOAuth2AuthorizationContext.Builder contextBuilder;\n\t\tif (authorizedClient != null) {\n\t\t\tcontextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);\n\t\t}\n\t\telse {\n\t\t\tauthorizedClient = this.authorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal,\n\t\t\t\t\tservletRequest);\n\t\t\tif (authorizedClient != null) {\n\t\t\t\tcontextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tClientRegistration clientRegistration = this.clientRegistrationRepository\n\t\t\t\t\t.findByRegistrationId(clientRegistrationId);\n\t\t\t\tAssert.notNull(clientRegistration,\n\t\t\t\t\t\t\"Could not find ClientRegistration with id '\" + clientRegistrationId + \"'\");\n\t\t\t\tcontextBuilder = OAuth2AuthorizationContext.withClientRegistration(clientRegistration);\n\t\t\t}\n\t\t}\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = contextBuilder.principal(principal)\n\t\t\t\t.attributes((attributes) -> {\n\t\t\t\t\tMap<String, Object> contextAttributes = this.contextAttributesMapper.apply(authorizeRequest);\n\t\t\t\t\tif (!CollectionUtils.isEmpty(contextAttributes)) {\n\t\t\t\t\t\tattributes.putAll(contextAttributes);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\ttry {\n\t\t\tauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tthis.authorizationFailureHandler.onAuthorizationFailure(ex, principal,\n\t\t\t\t\tcreateAttributes(servletRequest, servletResponse));\n\t\t\tthrow ex;\n\t\t}\n\t\tif (authorizedClient != null) {\n\t\t\tthis.authorizationSuccessHandler.onAuthorizationSuccess(authorizedClient, principal,\n\t\t\t\t\tcreateAttributes(servletRequest, servletResponse));\n\t\t}\n\t\telse {\n\t\t\t// In the case of re-authorization, the returned `authorizedClient` may be\n\t\t\t// null if re-authorization is not supported.\n\t\t\t// For these cases, return the provided\n\t\t\t// `authorizationContext.authorizedClient`.\n\t\t\tif (authorizationContext.getAuthorizedClient() != null) {\n\t\t\t\treturn authorizationContext.getAuthorizedClient();\n\t\t\t}\n\t\t}\n\t\treturn authorizedClient;\n\t}\n\n\tprivate static Map<String, Object> createAttributes(HttpServletRequest servletRequest,\n\t\t\tHttpServletResponse servletResponse) {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(HttpServletRequest.class.getName(), servletRequest);\n\t\tattributes.put(HttpServletResponse.class.getName(), servletResponse);\n\t\treturn attributes;\n\t}\n\n\tprivate static @Nullable HttpServletRequest getHttpServletRequestOrDefault(Map<String, Object> attributes) {\n\t\tHttpServletRequest servletRequest = (HttpServletRequest) attributes.get(HttpServletRequest.class.getName());\n\t\tif (servletRequest == null) {\n\t\t\tRequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n\t\t\tif (requestAttributes instanceof ServletRequestAttributes) {\n\t\t\t\tservletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();\n\t\t\t}\n\t\t}\n\t\treturn servletRequest;\n\t}\n\n\tprivate static @Nullable HttpServletResponse getHttpServletResponseOrDefault(Map<String, Object> attributes) {\n\t\tHttpServletResponse servletResponse = (HttpServletResponse) attributes.get(HttpServletResponse.class.getName());\n\t\tif (servletResponse == null) {\n\t\t\tRequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n\t\t\tif (requestAttributes instanceof ServletRequestAttributes) {\n\t\t\t\tservletResponse = ((ServletRequestAttributes) requestAttributes).getResponse();\n\t\t\t}\n\t\t}\n\t\treturn servletResponse;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizedClientProvider} used for authorizing (or\n\t * re-authorizing) an OAuth 2.0 Client.\n\t * @param authorizedClientProvider the {@link OAuth2AuthorizedClientProvider} used for\n\t * authorizing (or re-authorizing) an OAuth 2.0 Client\n\t */\n\tpublic void setAuthorizedClientProvider(OAuth2AuthorizedClientProvider authorizedClientProvider) {\n\t\tAssert.notNull(authorizedClientProvider, \"authorizedClientProvider cannot be null\");\n\t\tthis.authorizedClientProvider = authorizedClientProvider;\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping attribute(s) from the\n\t * {@link OAuth2AuthorizeRequest} to a {@code Map} of attributes to be associated to\n\t * the {@link OAuth2AuthorizationContext#getAttributes() authorization context}.\n\t * @param contextAttributesMapper the {@code Function} used for supplying the\n\t * {@code Map} of attributes to the {@link OAuth2AuthorizationContext#getAttributes()\n\t * authorization context}\n\t */\n\tpublic void setContextAttributesMapper(\n\t\t\tFunction<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper) {\n\t\tAssert.notNull(contextAttributesMapper, \"contextAttributesMapper cannot be null\");\n\t\tthis.contextAttributesMapper = contextAttributesMapper;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizationSuccessHandler} that handles successful\n\t * authorizations.\n\t *\n\t * <p>\n\t * The default saves {@link OAuth2AuthorizedClient}s in the\n\t * {@link OAuth2AuthorizedClientRepository}.\n\t * @param authorizationSuccessHandler the {@link OAuth2AuthorizationSuccessHandler}\n\t * that handles successful authorizations\n\t * @since 5.3\n\t */\n\tpublic void setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler authorizationSuccessHandler) {\n\t\tAssert.notNull(authorizationSuccessHandler, \"authorizationSuccessHandler cannot be null\");\n\t\tthis.authorizationSuccessHandler = authorizationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizationFailureHandler} that handles authorization\n\t * failures.\n\t *\n\t * <p>\n\t * A {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} is used by\n\t * default.\n\t * @param authorizationFailureHandler the {@link OAuth2AuthorizationFailureHandler}\n\t * that handles authorization failures\n\t * @since 5.3\n\t * @see RemoveAuthorizedClientOAuth2AuthorizationFailureHandler\n\t */\n\tpublic void setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\tthis.authorizationFailureHandler = authorizationFailureHandler;\n\t}\n\n\t/**\n\t * The default implementation of the {@link #setContextAttributesMapper(Function)\n\t * contextAttributesMapper}.\n\t */\n\tpublic static class DefaultContextAttributesMapper\n\t\t\timplements Function<OAuth2AuthorizeRequest, Map<String, Object>> {\n\n\t\t@Override\n\t\tpublic Map<String, Object> apply(OAuth2AuthorizeRequest authorizeRequest) {\n\t\t\tMap<String, Object> contextAttributes = Collections.emptyMap();\n\t\t\tHttpServletRequest servletRequest = getHttpServletRequestOrDefault(authorizeRequest.getAttributes());\n\t\t\tif (servletRequest != null) {\n\t\t\t\tString scope = servletRequest.getParameter(OAuth2ParameterNames.SCOPE);\n\t\t\t\tif (StringUtils.hasText(scope)) {\n\t\t\t\t\tcontextAttributes = new HashMap<>();\n\t\t\t\t\tcontextAttributes.put(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME,\n\t\t\t\t\t\t\tStringUtils.delimitedListToStringArray(scope, \" \"));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn contextAttributes;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/DefaultReactiveOAuth2AuthorizedClientManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationContext;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizationSuccessHandler;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;\nimport org.springframework.security.oauth2.client.RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * The default implementation of a {@link ReactiveOAuth2AuthorizedClientManager} for use\n * within the context of a {@link ServerWebExchange}.\n *\n * <p>\n * (When operating <em>outside</em> of the context of a {@link ServerWebExchange}, use\n * {@link org.springframework.security.oauth2.client.AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager\n * AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager} instead.)\n * </p>\n *\n * <p>\n * This is a reactive equivalent of {@link DefaultOAuth2AuthorizedClientManager}.\n * </p>\n *\n * <h2>Authorized Client Persistence</h2>\n *\n * <p>\n * This client manager utilizes a {@link ServerOAuth2AuthorizedClientRepository} to\n * persist {@link OAuth2AuthorizedClient}s.\n * </p>\n *\n * <p>\n * By default, when an authorization attempt succeeds, the {@link OAuth2AuthorizedClient}\n * will be saved in the authorized client repository. This functionality can be changed by\n * configuring a custom {@link ReactiveOAuth2AuthorizationSuccessHandler} via\n * {@link #setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler)}.\n * </p>\n *\n * <p>\n * By default, when an authorization attempt fails due to an\n * {@value org.springframework.security.oauth2.core.OAuth2ErrorCodes#INVALID_GRANT} error,\n * the previously saved {@link OAuth2AuthorizedClient} will be removed from the authorized\n * client repository. (The\n * {@value org.springframework.security.oauth2.core.OAuth2ErrorCodes#INVALID_GRANT} error\n * generally occurs when a refresh token that is no longer valid is used to retrieve a new\n * access token.) This functionality can be changed by configuring a custom\n * {@link ReactiveOAuth2AuthorizationFailureHandler} via\n * {@link #setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler)}.\n * </p>\n *\n * @author Joe Grandja\n * @author Phil Clay\n * @since 5.2\n * @see ReactiveOAuth2AuthorizedClientManager\n * @see ReactiveOAuth2AuthorizedClientProvider\n * @see ReactiveOAuth2AuthorizationSuccessHandler\n * @see ReactiveOAuth2AuthorizationFailureHandler\n */\npublic final class DefaultReactiveOAuth2AuthorizedClientManager implements ReactiveOAuth2AuthorizedClientManager {\n\n\t// @formatter:off\n\tprivate static final ReactiveOAuth2AuthorizedClientProvider DEFAULT_AUTHORIZED_CLIENT_PROVIDER = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken()\n\t\t\t.clientCredentials()\n\t\t\t.build();\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final Mono<ServerWebExchange> currentServerWebExchangeMono = Mono.deferContextual(Mono::just)\n\t\t\t.filter((c) -> c.hasKey(ServerWebExchange.class))\n\t\t\t.map((c) -> c.get(ServerWebExchange.class));\n\t// @formatter:on\n\n\tprivate final ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate final ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = DEFAULT_AUTHORIZED_CLIENT_PROVIDER;\n\n\tprivate Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper = new DefaultContextAttributesMapper();\n\n\tprivate ReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler;\n\n\tprivate ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t/**\n\t * Constructs a {@code DefaultReactiveOAuth2AuthorizedClientManager} using the\n\t * provided parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientRepository the repository of authorized clients\n\t */\n\tpublic DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\tthis.authorizationSuccessHandler = (authorizedClient, principal, attributes) -> {\n\t\t\tServerWebExchange exchange = (ServerWebExchange) attributes.get(ServerWebExchange.class.getName());\n\t\t\tif (exchange != null) {\n\t\t\t\treturn authorizedClientRepository.saveAuthorizedClient(authorizedClient, principal, exchange);\n\t\t\t}\n\t\t\treturn Mono.empty();\n\t\t};\n\t\tthis.authorizationFailureHandler = new RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> {\n\t\t\t\t\tServerWebExchange exchange = (ServerWebExchange) attributes.get(ServerWebExchange.class.getName());\n\t\t\t\t\tif (exchange != null) {\n\t\t\t\t\t\treturn authorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal,\n\t\t\t\t\t\t\t\texchange);\n\t\t\t\t\t}\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t});\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {\n\t\tAssert.notNull(authorizeRequest, \"authorizeRequest cannot be null\");\n\t\tString clientRegistrationId = authorizeRequest.getClientRegistrationId();\n\t\tAuthentication principal = authorizeRequest.getPrincipal();\n\t\t// @formatter:off\n\t\treturn Mono.justOrEmpty(authorizeRequest.<ServerWebExchange>getAttribute(ServerWebExchange.class.getName()))\n\t\t\t\t.switchIfEmpty(currentServerWebExchangeMono)\n\t\t\t\t.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(\"serverWebExchange cannot be null\")))\n\t\t\t\t.flatMap((serverWebExchange) -> Mono\n\t\t\t\t\t\t.justOrEmpty(authorizeRequest.getAuthorizedClient())\n\t\t\t\t\t\t.switchIfEmpty(Mono.defer(() -> loadAuthorizedClient(clientRegistrationId, principal, serverWebExchange)))\n\t\t\t\t\t\t.flatMap((authorizedClient) -> // Re-authorize\n\t\t\t\t\t\t\tauthorizationContext(authorizeRequest, authorizedClient)\n\t\t\t\t\t\t\t\t\t.flatMap((authorizationContext) -> authorize(authorizationContext, principal, serverWebExchange))\n\t\t\t\t\t\t\t\t\t// Default to the existing authorizedClient if the\n\t\t\t\t\t\t\t\t\t// client was not re-authorized\n\t\t\t\t\t\t\t\t\t.defaultIfEmpty((authorizeRequest.getAuthorizedClient() != null)\n\t\t\t\t\t\t\t\t\t\t\t? authorizeRequest.getAuthorizedClient() : authorizedClient)\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.switchIfEmpty(Mono.defer(() ->\n\t\t\t\t\t\t\t// Authorize\n\t\t\t\t\t\t\tthis.clientRegistrationRepository.findByRegistrationId(clientRegistrationId)\n\t\t\t\t\t\t\t\t\t.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(\n\t\t\t\t\t\t\t\t\t\t\t\"Could not find ClientRegistration with id '\" + clientRegistrationId + \"'\")))\n\t\t\t\t\t\t\t\t\t.flatMap((clientRegistration) -> authorizationContext(authorizeRequest,\n\t\t\t\t\t\t\t\t\t\t\tclientRegistration))\n\t\t\t\t\t\t\t\t\t.flatMap((authorizationContext) -> authorize(authorizationContext, principal,\n\t\t\t\t\t\t\t\t\t\t\tserverWebExchange))))\n\t\t\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<OAuth2AuthorizedClient> loadAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\tServerWebExchange serverWebExchange) {\n\t\treturn this.authorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal, serverWebExchange);\n\t}\n\n\t/**\n\t * Performs authorization and then delegates to either the\n\t * {@link #authorizationSuccessHandler} or {@link #authorizationFailureHandler},\n\t * depending on the authorization result.\n\t * @param authorizationContext the context to authorize\n\t * @param principal the principle to authorize\n\t * @param serverWebExchange the currently active exchange\n\t * @return a {@link Mono} that emits the authorized client after the authorization\n\t * attempt succeeds and the {@link #authorizationSuccessHandler} has completed, or\n\t * completes with an exception after the authorization attempt fails and the\n\t * {@link #authorizationFailureHandler} has completed\n\t */\n\tprivate Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext authorizationContext,\n\t\t\tAuthentication principal, ServerWebExchange serverWebExchange) {\n\t\t// @formatter:off\n\t\treturn this.authorizedClientProvider.authorize(authorizationContext)\n\t\t\t\t// Delegate to the authorizationSuccessHandler of the successful\n\t\t\t\t// authorization\n\t\t\t\t.flatMap((authorizedClient) ->\n\t\t\t\t\t\tthis.authorizationSuccessHandler\n\t\t\t\t\t\t\t.onAuthorizationSuccess(authorizedClient, principal, createAttributes(serverWebExchange))\n\t\t\t\t\t\t\t.thenReturn(authorizedClient)\n\t\t\t\t)\n\t\t\t\t// Delegate to the authorizationFailureHandler of the failed authorization\n\t\t\t\t.onErrorResume(OAuth2AuthorizationException.class, (authorizationException) ->\n\t\t\t\t\t\tthis.authorizationFailureHandler\n\t\t\t\t\t\t\t\t.onAuthorizationFailure(authorizationException, principal, createAttributes(serverWebExchange))\n\t\t\t\t\t\t\t\t.then(Mono.error(authorizationException))\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate Map<String, Object> createAttributes(ServerWebExchange serverWebExchange) {\n\t\treturn Collections.singletonMap(ServerWebExchange.class.getName(), serverWebExchange);\n\t}\n\n\tprivate Mono<OAuth2AuthorizationContext> authorizationContext(OAuth2AuthorizeRequest authorizeRequest,\n\t\t\tOAuth2AuthorizedClient authorizedClient) {\n\t\t// @formatter:off\n\t\treturn Mono.just(authorizeRequest)\n\t\t\t\t.flatMap(this.contextAttributesMapper)\n\t\t\t\t.map((attrs) -> OAuth2AuthorizationContext\n\t\t\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t\t\t.principal(authorizeRequest.getPrincipal())\n\t\t\t\t\t\t.attributes((attributes) -> {\n\t\t\t\t\t\t\tif (!CollectionUtils.isEmpty(attrs)) {\n\t\t\t\t\t\t\t\tattributes.putAll(attrs);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.build());\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<OAuth2AuthorizationContext> authorizationContext(OAuth2AuthorizeRequest authorizeRequest,\n\t\t\tClientRegistration clientRegistration) {\n\t\t// @formatter:off\n\t\treturn Mono.just(authorizeRequest)\n\t\t\t\t.flatMap(this.contextAttributesMapper)\n\t\t\t\t.map((attrs) -> OAuth2AuthorizationContext.withClientRegistration(clientRegistration)\n\t\t\t\t\t\t.principal(authorizeRequest.getPrincipal())\n\t\t\t\t\t\t.attributes((attributes) -> {\n\t\t\t\t\t\t\tif (!CollectionUtils.isEmpty(attrs)) {\n\t\t\t\t\t\t\t\tattributes.putAll(attrs);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.build()\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Sets the {@link ReactiveOAuth2AuthorizedClientProvider} used for authorizing (or\n\t * re-authorizing) an OAuth 2.0 Client.\n\t * @param authorizedClientProvider the {@link ReactiveOAuth2AuthorizedClientProvider}\n\t * used for authorizing (or re-authorizing) an OAuth 2.0 Client\n\t */\n\tpublic void setAuthorizedClientProvider(ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider) {\n\t\tAssert.notNull(authorizedClientProvider, \"authorizedClientProvider cannot be null\");\n\t\tthis.authorizedClientProvider = authorizedClientProvider;\n\t}\n\n\t/**\n\t * Sets the {@code Function} used for mapping attribute(s) from the\n\t * {@link OAuth2AuthorizeRequest} to a {@code Map} of attributes to be associated to\n\t * the {@link OAuth2AuthorizationContext#getAttributes() authorization context}.\n\t * @param contextAttributesMapper the {@code Function} used for supplying the\n\t * {@code Map} of attributes to the {@link OAuth2AuthorizationContext#getAttributes()\n\t * authorization context}\n\t */\n\tpublic void setContextAttributesMapper(\n\t\t\tFunction<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper) {\n\t\tAssert.notNull(contextAttributesMapper, \"contextAttributesMapper cannot be null\");\n\t\tthis.contextAttributesMapper = contextAttributesMapper;\n\t}\n\n\t/**\n\t * Sets the handler that handles successful authorizations.\n\t *\n\t * The default saves {@link OAuth2AuthorizedClient}s in the\n\t * {@link ServerOAuth2AuthorizedClientRepository}.\n\t * @param authorizationSuccessHandler the handler that handles successful\n\t * authorizations.\n\t * @since 5.3\n\t */\n\tpublic void setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler) {\n\t\tAssert.notNull(authorizationSuccessHandler, \"authorizationSuccessHandler cannot be null\");\n\t\tthis.authorizationSuccessHandler = authorizationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the handler that handles authorization failures.\n\t *\n\t * <p>\n\t * A {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} is used\n\t * by default.\n\t * </p>\n\t * @param authorizationFailureHandler the handler that handles authorization failures.\n\t * @since 5.3\n\t * @see RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler\n\t */\n\tpublic void setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\tthis.authorizationFailureHandler = authorizationFailureHandler;\n\t}\n\n\t/**\n\t * The default implementation of the {@link #setContextAttributesMapper(Function)\n\t * contextAttributesMapper}.\n\t */\n\tpublic static class DefaultContextAttributesMapper\n\t\t\timplements Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> {\n\n\t\t@Override\n\t\tpublic Mono<Map<String, Object>> apply(OAuth2AuthorizeRequest authorizeRequest) {\n\t\t\tServerWebExchange serverWebExchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());\n\t\t\t// @formatter:off\n\t\t\treturn Mono.justOrEmpty(serverWebExchange)\n\t\t\t\t\t.switchIfEmpty(currentServerWebExchangeMono)\n\t\t\t\t\t.flatMap((exchange) -> {\n\t\t\t\t\t\tMap<String, Object> contextAttributes = new HashMap<>();\n\t\t\t\t\t\tcontextAttributes.put(ServerWebExchange.class.getName(), serverWebExchange);\n\t\t\t\t\t\tString scope = exchange.getRequest().getQueryParams().getFirst(OAuth2ParameterNames.SCOPE);\n\t\t\t\t\t\tif (StringUtils.hasText(scope)) {\n\t\t\t\t\t\t\tcontextAttributes.put(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME,\n\t\t\t\t\t\t\t\t\tStringUtils.delimitedListToStringArray(scope, \" \"));\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn Mono.just(Collections.unmodifiableMap(contextAttributes));\n\t\t\t\t\t})\n\t\t\t\t\t.defaultIfEmpty(Collections.emptyMap());\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/HttpSessionOAuth2AuthorizationRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AuthorizationRequestRepository} that stores\n * {@link OAuth2AuthorizationRequest} in the {@code HttpSession}.\n *\n * @author Joe Grandja\n * @author Rob Winch\n * @author Craig Andrews\n * @since 5.0\n * @see AuthorizationRequestRepository\n * @see OAuth2AuthorizationRequest\n */\npublic final class HttpSessionOAuth2AuthorizationRequestRepository\n\t\timplements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {\n\n\tprivate static final String DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME = HttpSessionOAuth2AuthorizationRequestRepository.class\n\t\t.getName() + \".AUTHORIZATION_REQUEST\";\n\n\tprivate final String sessionAttributeName = DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME;\n\n\t@Override\n\tpublic @Nullable OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tString stateParameter = getStateParameter(request);\n\t\tif (stateParameter == null) {\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthorizationRequest authorizationRequest = getAuthorizationRequest(request);\n\t\treturn (authorizationRequest != null && stateParameter.equals(authorizationRequest.getState()))\n\t\t\t\t? authorizationRequest : null;\n\t}\n\n\t@Override\n\tpublic void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tAssert.notNull(response, \"response cannot be null\");\n\t\tif (authorizationRequest == null) {\n\t\t\tremoveAuthorizationRequest(request, response);\n\t\t\treturn;\n\t\t}\n\t\tString state = authorizationRequest.getState();\n\t\tAssert.hasText(state, \"authorizationRequest.state cannot be empty\");\n\t\trequest.getSession().setAttribute(this.sessionAttributeName, authorizationRequest);\n\t}\n\n\t@Override\n\tpublic @Nullable OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tAssert.notNull(response, \"response cannot be null\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = loadAuthorizationRequest(request);\n\t\tif (authorizationRequest != null) {\n\t\t\trequest.getSession().removeAttribute(this.sessionAttributeName);\n\t\t}\n\t\treturn authorizationRequest;\n\t}\n\n\t/**\n\t * Gets the state parameter from the {@link HttpServletRequest}\n\t * @param request the request to use\n\t * @return the state parameter or null if not found\n\t */\n\tprivate @Nullable String getStateParameter(HttpServletRequest request) {\n\t\treturn request.getParameter(OAuth2ParameterNames.STATE);\n\t}\n\n\tprivate @Nullable OAuth2AuthorizationRequest getAuthorizationRequest(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\treturn (session != null) ? (OAuth2AuthorizationRequest) session.getAttribute(this.sessionAttributeName) : null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/HttpSessionOAuth2AuthorizedClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientRepository} that stores\n * {@link OAuth2AuthorizedClient}'s in the {@code HttpSession}.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AuthorizedClientRepository\n * @see OAuth2AuthorizedClient\n */\npublic final class HttpSessionOAuth2AuthorizedClientRepository implements OAuth2AuthorizedClientRepository {\n\n\tprivate static final String DEFAULT_AUTHORIZED_CLIENTS_ATTR_NAME = HttpSessionOAuth2AuthorizedClientRepository.class\n\t\t.getName() + \".AUTHORIZED_CLIENTS\";\n\n\tprivate final String sessionAttributeName = DEFAULT_AUTHORIZED_CLIENTS_ATTR_NAME;\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic <T extends OAuth2AuthorizedClient> @Nullable T loadAuthorizedClient(String clientRegistrationId,\n\t\t\tAuthentication principal, HttpServletRequest request) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\treturn (T) this.getAuthorizedClients(request).get(clientRegistrationId);\n\t}\n\n\t@Override\n\tpublic void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tAssert.notNull(response, \"response cannot be null\");\n\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = this.getAuthorizedClients(request);\n\t\tauthorizedClients.put(authorizedClient.getClientRegistration().getRegistrationId(), authorizedClient);\n\t\trequest.getSession().setAttribute(this.sessionAttributeName, authorizedClients);\n\t}\n\n\t@Override\n\tpublic void removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = this.getAuthorizedClients(request);\n\t\tif (!authorizedClients.isEmpty()) {\n\t\t\tif (authorizedClients.remove(clientRegistrationId) != null) {\n\t\t\t\tif (!authorizedClients.isEmpty()) {\n\t\t\t\t\trequest.getSession().setAttribute(this.sessionAttributeName, authorizedClients);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\trequest.getSession().removeAttribute(this.sessionAttributeName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Map<String, OAuth2AuthorizedClient> getAuthorizedClients(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = (session != null)\n\t\t\t\t? (Map<String, OAuth2AuthorizedClient>) session.getAttribute(this.sessionAttributeName) : null;\n\t\tif (authorizedClients == null) {\n\t\t\tauthorizedClients = new HashMap<>();\n\t\t}\n\t\treturn authorizedClients;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/InvalidClientRegistrationIdException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\n/**\n * @author Steve Riesenberg\n * @since 5.8\n */\n@SuppressWarnings(\"serial\")\nclass InvalidClientRegistrationIdException extends IllegalArgumentException {\n\n\t/**\n\t * @param message the exception message\n\t */\n\tInvalidClientRegistrationIdException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.io.IOException;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Authorization Code Grant, which handles the\n * processing of the OAuth 2.0 Authorization Response.\n *\n * <p>\n * The OAuth 2.0 Authorization Response is processed as follows:\n *\n * <ul>\n * <li>Assuming the End-User (Resource Owner) has granted access to the Client, the\n * Authorization Server will append the {@link OAuth2ParameterNames#CODE code} and\n * {@link OAuth2ParameterNames#STATE state} parameters to the\n * {@link OAuth2ParameterNames#REDIRECT_URI redirect_uri} (provided in the Authorization\n * Request) and redirect the End-User's user-agent back to this {@code Filter} (the\n * Client).</li>\n * <li>This {@code Filter} will then create an\n * {@link OAuth2AuthorizationCodeAuthenticationToken} with the\n * {@link OAuth2ParameterNames#CODE code} received and delegate it to the\n * {@link AuthenticationManager} to authenticate.</li>\n * <li>Upon a successful authentication, an {@link OAuth2AuthorizedClient Authorized\n * Client} is created by associating the\n * {@link OAuth2AuthorizationCodeAuthenticationToken#getClientRegistration() client} to\n * the {@link OAuth2AuthorizationCodeAuthenticationToken#getAccessToken() access token}\n * and current {@code Principal} and saving it via the\n * {@link OAuth2AuthorizedClientRepository}.</li>\n * </ul>\n *\n * @author Joe Grandja\n * @author Parikshit Dutta\n * @since 5.1\n * @see OAuth2AuthorizationCodeAuthenticationToken\n * @see OAuth2AuthorizationCodeAuthenticationProvider\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationResponse\n * @see AuthorizationRequestRepository\n * @see OAuth2AuthorizationRequestRedirectFilter\n * @see ClientRegistrationRepository\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientRepository\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.2\">Section 4.1.2 Authorization\n * Response</a>\n */\npublic class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate final OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\n\tprivate final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate RequestCache requestCache = new HttpSessionRequestCache();\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationCodeGrantFilter} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientRepository the authorized client repository\n\t * @param authenticationManager the authentication manager\n\t */\n\tpublic OAuth2AuthorizationCodeGrantFilter(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository, AuthenticationManager authenticationManager) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t/**\n\t * Sets the repository for stored {@link OAuth2AuthorizationRequest}'s.\n\t * @param authorizationRequestRepository the repository for stored\n\t * {@link OAuth2AuthorizationRequest}'s\n\t */\n\tpublic final void setAuthorizationRequestRepository(\n\t\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\tAssert.notNull(authorizationRequestRepository, \"authorizationRequestRepository cannot be null\");\n\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t}\n\n\t/**\n\t * Sets the {@link RequestCache} used for loading a previously saved request (if\n\t * available) and replaying it after completing the processing of the OAuth 2.0\n\t * Authorization Response.\n\t * @param requestCache the cache used for loading a previously saved request (if\n\t * available)\n\t * @since 5.4\n\t */\n\tpublic final void setRequestCache(RequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (matchesAuthorizationResponse(request)) {\n\t\t\tprocessAuthorizationResponse(request, response);\n\t\t\treturn;\n\t\t}\n\t\tfilterChain.doFilter(request, response);\n\t}\n\n\tprivate boolean matchesAuthorizationResponse(HttpServletRequest request) {\n\t\tMultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());\n\t\tif (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository\n\t\t\t.loadAuthorizationRequest(request);\n\t\tif (authorizationRequest == null) {\n\t\t\treturn false;\n\t\t}\n\t\tAssert.notNull(authorizationRequest.getRedirectUri(), \"redirectUri cannot be null\");\n\t\tUriComponents requestUri = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request)).build();\n\t\tUriComponents redirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri()).build();\n\t\tSet<Map.Entry<String, List<String>>> requestUriParameters = new LinkedHashSet<>(\n\t\t\t\trequestUri.getQueryParams().entrySet());\n\t\tSet<Map.Entry<String, List<String>>> redirectUriParameters = new LinkedHashSet<>(\n\t\t\t\tredirectUri.getQueryParams().entrySet());\n\t\t// Remove the additional request parameters (if any) from the authorization\n\t\t// response (request)\n\t\t// before doing an exact comparison with the authorizationRequest.getRedirectUri()\n\t\t// parameters (if any)\n\t\trequestUriParameters.retainAll(redirectUriParameters);\n\t\tif (Objects.equals(requestUri.getScheme(), redirectUri.getScheme())\n\t\t\t\t&& Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo())\n\t\t\t\t&& Objects.equals(requestUri.getHost(), redirectUri.getHost())\n\t\t\t\t&& Objects.equals(requestUri.getPort(), redirectUri.getPort())\n\t\t\t\t&& Objects.equals(requestUri.getPath(), redirectUri.getPath())\n\t\t\t\t&& Objects.equals(requestUriParameters.toString(), redirectUriParameters.toString())) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void processAuthorizationResponse(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows IOException {\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository\n\t\t\t.removeAuthorizationRequest(request, response);\n\t\tAssert.notNull(authorizationRequest, \"authorizationRequest cannot be null\");\n\t\tString registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID);\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);\n\t\tAssert.notNull(clientRegistration, \"Client registration not found with id: \" + registrationId);\n\t\tMultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());\n\t\tString redirectUri = UrlUtils.buildFullRequestUrl(request);\n\t\tOAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params,\n\t\t\t\tredirectUri);\n\t\tOAuth2AuthorizationCodeAuthenticationToken authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tclientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));\n\t\tauthenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\tOAuth2AuthorizationCodeAuthenticationToken authenticationResult;\n\t\ttry {\n\t\t\tauthenticationResult = (OAuth2AuthorizationCodeAuthenticationToken) this.authenticationManager\n\t\t\t\t.authenticate(authenticationRequest);\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\tOAuth2Error error = ex.getError();\n\t\t\tString errorRedirectUri = authorizationRequest.getRedirectUri();\n\t\t\tAssert.hasText(errorRedirectUri, \"redirectUri cannot be empty\");\n\t\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(errorRedirectUri)\n\t\t\t\t.queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode());\n\t\t\tif (StringUtils.hasLength(error.getDescription())) {\n\t\t\t\turiBuilder.queryParam(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription());\n\t\t\t}\n\t\t\tif (StringUtils.hasLength(error.getUri())) {\n\t\t\t\turiBuilder.queryParam(OAuth2ParameterNames.ERROR_URI, error.getUri());\n\t\t\t}\n\t\t\tthis.redirectStrategy.sendRedirect(request, response, uriBuilder.build().encode().toString());\n\t\t\treturn;\n\t\t}\n\t\tAuthentication currentAuthentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tString principalName = (currentAuthentication != null) ? currentAuthentication.getName() : \"anonymousUser\";\n\t\tAuthentication principal = (currentAuthentication != null) ? currentAuthentication\n\t\t\t\t: new AnonymousAuthenticationToken(\"anonymous\", principalName,\n\t\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tAssert.notNull(authenticationResult.getAccessToken(), \"accessToken cannot be null\");\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(\n\t\t\t\tauthenticationResult.getClientRegistration(), principalName, authenticationResult.getAccessToken(),\n\t\t\t\tauthenticationResult.getRefreshToken());\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, principal, request, response);\n\t\tString redirectUrl = authorizationRequest.getRedirectUri();\n\t\tSavedRequest savedRequest = this.requestCache.getRequest(request, response);\n\t\tif (savedRequest != null) {\n\t\t\tredirectUrl = savedRequest.getRedirectUrl();\n\t\t\tthis.requestCache.removeRequest(request, response);\n\t\t}\n\t\tAssert.hasText(redirectUrl, \"redirectUrl cannot be empty\");\n\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUrl);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestCustomizers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\n\n/**\n * A factory of customizers that customize the {@link OAuth2AuthorizationRequest OAuth 2.0\n * Authorization Request} via the {@link OAuth2AuthorizationRequest.Builder}.\n *\n * @author Joe Grandja\n * @since 5.7\n * @see OAuth2AuthorizationRequest.Builder\n * @see DefaultOAuth2AuthorizationRequestResolver#setAuthorizationRequestCustomizer(Consumer)\n * @see DefaultServerOAuth2AuthorizationRequestResolver#setAuthorizationRequestCustomizer(Consumer)\n */\npublic final class OAuth2AuthorizationRequestCustomizers {\n\n\tprivate static final StringKeyGenerator DEFAULT_SECURE_KEY_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\tprivate OAuth2AuthorizationRequestCustomizers() {\n\t}\n\n\t/**\n\t * Returns a {@code Consumer} to be provided the\n\t * {@link OAuth2AuthorizationRequest.Builder} that adds the\n\t * {@link PkceParameterNames#CODE_CHALLENGE code_challenge} and, usually,\n\t * {@link PkceParameterNames#CODE_CHALLENGE_METHOD code_challenge_method} parameters\n\t * to the OAuth 2.0 Authorization Request. The {@code code_verifier} is stored in\n\t * {@link OAuth2AuthorizationRequest#getAttribute(String)} under the key\n\t * {@link PkceParameterNames#CODE_VERIFIER code_verifier} for subsequent use in the\n\t * OAuth 2.0 Access Token Request.\n\t * @return a {@code Consumer} to be provided the\n\t * {@link OAuth2AuthorizationRequest.Builder} that adds the PKCE parameters\n\t * @see <a target=\"_blank\" href=\n\t * \"https://datatracker.ietf.org/doc/html/rfc7636#section-1.1\">1.1. Protocol Flow</a>\n\t * @see <a target=\"_blank\" href=\n\t * \"https://datatracker.ietf.org/doc/html/rfc7636#section-4.1\">4.1. Client Creates a\n\t * Code Verifier</a>\n\t * @see <a target=\"_blank\" href=\n\t * \"https://datatracker.ietf.org/doc/html/rfc7636#section-4.2\">4.2. Client Creates the\n\t * Code Challenge</a>\n\t */\n\tpublic static Consumer<OAuth2AuthorizationRequest.Builder> withPkce() {\n\t\treturn OAuth2AuthorizationRequestCustomizers::applyPkce;\n\t}\n\n\tprivate static void applyPkce(OAuth2AuthorizationRequest.Builder builder) {\n\t\tif (isPkceAlreadyApplied(builder)) {\n\t\t\treturn;\n\t\t}\n\n\t\tString codeVerifier = DEFAULT_SECURE_KEY_GENERATOR.generateKey();\n\n\t\tbuilder.attributes((attrs) -> attrs.put(PkceParameterNames.CODE_VERIFIER, codeVerifier));\n\n\t\tbuilder.additionalParameters((params) -> {\n\t\t\ttry {\n\t\t\t\tString codeChallenge = createHash(codeVerifier);\n\t\t\t\tparams.put(PkceParameterNames.CODE_CHALLENGE, codeChallenge);\n\t\t\t\tparams.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\t\t}\n\t\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\t\tparams.put(PkceParameterNames.CODE_CHALLENGE, codeVerifier);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static boolean isPkceAlreadyApplied(OAuth2AuthorizationRequest.Builder builder) {\n\t\tAtomicBoolean pkceApplied = new AtomicBoolean(false);\n\t\tbuilder.additionalParameters((params) -> {\n\t\t\tif (params.containsKey(PkceParameterNames.CODE_CHALLENGE)) {\n\t\t\t\tpkceApplied.set(true);\n\t\t\t}\n\t\t});\n\t\treturn pkceApplied.get();\n\t}\n\n\tprivate static String createHash(String value) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestRedirectFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.ThrowableAnalyzer;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * This {@code Filter} initiates the authorization code grant flow by redirecting the\n * End-User's user-agent to the Authorization Server's Authorization Endpoint.\n *\n * <p>\n * It builds the OAuth 2.0 Authorization Request, which is used as the redirect\n * {@code URI} to the Authorization Endpoint. The redirect {@code URI} will include the\n * client identifier, requested scope(s), state, response type, and a redirection URI\n * which the authorization server will send the user-agent back to once access is granted\n * (or denied) by the End-User (Resource Owner).\n *\n * <p>\n * By default, this {@code Filter} responds to authorization requests at the {@code URI}\n * {@code /oauth2/authorization/{registrationId}} using the default\n * {@link OAuth2AuthorizationRequestResolver}. The {@code URI} template variable\n * {@code {registrationId}} represents the {@link ClientRegistration#getRegistrationId()\n * registration identifier} of the client that is used for initiating the OAuth 2.0\n * Authorization Request.\n *\n * <p>\n * The default base {@code URI} {@code /oauth2/authorization} may be overridden via the\n * constructor\n * {@link #OAuth2AuthorizationRequestRedirectFilter(ClientRegistrationRepository, String)},\n * or alternatively, an {@code OAuth2AuthorizationRequestResolver} may be provided to the\n * constructor\n * {@link #OAuth2AuthorizationRequestRedirectFilter(OAuth2AuthorizationRequestResolver)}\n * to override the resolving of authorization requests.\n *\n * @author Joe Grandja\n * @author Rob Winch\n * @since 5.0\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestResolver\n * @see AuthorizationRequestRepository\n * @see ClientRegistration\n * @see ClientRegistrationRepository\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.1\">Section 4.1.1 Authorization Request\n * (Authorization Code)</a>\n */\npublic class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default base {@code URI} used for authorization requests.\n\t */\n\tpublic static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = DefaultOAuth2AuthorizationRequestResolver.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;\n\n\tprivate final ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();\n\n\tprivate RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate OAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\n\tprivate RequestCache requestCache = new HttpSessionRequestCache();\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = this::unsuccessfulRedirectForAuthorization;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationRequestRedirectFilter} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t */\n\tpublic OAuth2AuthorizationRequestRedirectFilter(ClientRegistrationRepository clientRegistrationRepository) {\n\t\tthis(new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository));\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationRequestRedirectFilter} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizationRequestBaseUri the base {@code URI} used for authorization\n\t * requests\n\t */\n\tpublic OAuth2AuthorizationRequestRedirectFilter(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tString authorizationRequestBaseUri) {\n\t\tthis(new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, authorizationRequestBaseUri));\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationRequestRedirectFilter} using the provided\n\t * parameters.\n\t * @param authorizationRequestResolver the resolver used for resolving authorization\n\t * requests\n\t * @since 5.1\n\t */\n\tpublic OAuth2AuthorizationRequestRedirectFilter(OAuth2AuthorizationRequestResolver authorizationRequestResolver) {\n\t\tAssert.notNull(authorizationRequestResolver, \"authorizationRequestResolver cannot be null\");\n\t\tthis.authorizationRequestResolver = authorizationRequestResolver;\n\t}\n\n\t/**\n\t * Sets the redirect strategy for Authorization Endpoint redirect URI.\n\t * @param authorizationRedirectStrategy the redirect strategy\n\t */\n\tpublic void setAuthorizationRedirectStrategy(RedirectStrategy authorizationRedirectStrategy) {\n\t\tAssert.notNull(authorizationRedirectStrategy, \"authorizationRedirectStrategy cannot be null\");\n\t\tthis.authorizationRedirectStrategy = authorizationRedirectStrategy;\n\t}\n\n\t/**\n\t * Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.\n\t * @param authorizationRequestRepository the repository used for storing\n\t * {@link OAuth2AuthorizationRequest}'s\n\t */\n\tpublic final void setAuthorizationRequestRepository(\n\t\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\tAssert.notNull(authorizationRequestRepository, \"authorizationRequestRepository cannot be null\");\n\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t}\n\n\t/**\n\t * Sets the {@link RequestCache} used for storing the current request before\n\t * redirecting the OAuth 2.0 Authorization Request.\n\t * @param requestCache the cache used for storing the current request\n\t */\n\tpublic final void setRequestCache(RequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationFailureHandler} used to handle errors redirecting to\n\t * the Authorization Server's Authorization Endpoint.\n\t * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used\n\t * to handle errors redirecting to the Authorization Server's Authorization Endpoint\n\t * @since 6.3\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\ttry {\n\t\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);\n\t\t\tif (authorizationRequest != null) {\n\t\t\t\tthis.sendRedirectForAuthorization(request, response, authorizationRequest);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tAuthenticationException wrappedException = new OAuth2AuthorizationRequestException(ex);\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, wrappedException);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// Check to see if we need to handle ClientAuthorizationRequiredException\n\t\t\tThrowable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);\n\t\t\tClientAuthorizationRequiredException authzEx = (ClientAuthorizationRequiredException) this.throwableAnalyzer\n\t\t\t\t.getFirstThrowableOfType(ClientAuthorizationRequiredException.class, causeChain);\n\t\t\tif (authzEx != null) {\n\t\t\t\ttry {\n\t\t\t\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request,\n\t\t\t\t\t\t\tauthzEx.getClientRegistrationId());\n\t\t\t\t\tif (authorizationRequest == null) {\n\t\t\t\t\t\tthrow authzEx;\n\t\t\t\t\t}\n\t\t\t\t\tthis.requestCache.saveRequest(request, response);\n\t\t\t\t\tthis.sendRedirectForAuthorization(request, response, authorizationRequest);\n\t\t\t\t}\n\t\t\t\tcatch (Exception failed) {\n\t\t\t\t\tAuthenticationException wrappedException = new OAuth2AuthorizationRequestException(ex);\n\t\t\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, wrappedException);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (ex instanceof ServletException) {\n\t\t\t\tthrow (ServletException) ex;\n\t\t\t}\n\t\t\tif (ex instanceof RuntimeException) {\n\t\t\t\tthrow (RuntimeException) ex;\n\t\t\t}\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprivate void sendRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,\n\t\t\tOAuth2AuthorizationRequest authorizationRequest) throws IOException {\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {\n\t\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t\t}\n\t\tthis.authorizationRedirectStrategy.sendRedirect(request, response,\n\t\t\t\tauthorizationRequest.getAuthorizationRequestUri());\n\t}\n\n\tprivate void unsuccessfulRedirectForAuthorization(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException ex) throws IOException {\n\t\tThrowable cause = ex.getCause();\n\t\tif (cause != null) {\n\t\t\tLogMessage message = LogMessage.format(\"Authorization Request failed: %s\", cause);\n\t\t\tif (InvalidClientRegistrationIdException.class.isAssignableFrom(cause.getClass())) {\n\t\t\t\t// Log an invalid registrationId at WARN level to allow these errors to be\n\t\t\t\t// tuned separately from other errors\n\t\t\t\tthis.logger.warn(message, ex);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.logger.error(message, ex);\n\t\t\t}\n\t\t}\n\t\tresponse.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(),\n\t\t\t\tHttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());\n\t}\n\n\tprivate static final class DefaultThrowableAnalyzer extends ThrowableAnalyzer {\n\n\t\t@Override\n\t\tprotected void initExtractorMap() {\n\t\t\tsuper.initExtractorMap();\n\t\t\tregisterExtractor(ServletException.class, (throwable) -> {\n\t\t\t\tThrowableAnalyzer.verifyThrowableHierarchy(throwable, ServletException.class);\n\t\t\t\treturn ((ServletException) throwable).getRootCause();\n\t\t\t});\n\t\t}\n\n\t}\n\n\tprivate static final class OAuth2AuthorizationRequestException extends AuthenticationException {\n\n\t\tOAuth2AuthorizationRequestException(Throwable cause) {\n\t\t\tsuper(cause.getMessage(), cause);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\n\n/**\n * Implementations of this interface are capable of resolving an\n * {@link OAuth2AuthorizationRequest} from the provided {@code HttpServletRequest}. Used\n * by the {@link OAuth2AuthorizationRequestRedirectFilter} for resolving Authorization\n * Requests.\n *\n * @author Joe Grandja\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestRedirectFilter\n */\npublic interface OAuth2AuthorizationRequestResolver {\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest} resolved from the provided\n\t * {@code HttpServletRequest} or {@code null} if not available.\n\t * @param request the {@code HttpServletRequest}\n\t * @return the resolved {@link OAuth2AuthorizationRequest} or {@code null} if not\n\t * available\n\t */\n\t@Nullable OAuth2AuthorizationRequest resolve(HttpServletRequest request);\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest} resolved from the provided\n\t * {@code HttpServletRequest} or {@code null} if not available.\n\t * @param request the {@code HttpServletRequest}\n\t * @param clientRegistrationId the clientRegistrationId to use\n\t * @return the resolved {@link OAuth2AuthorizationRequest} or {@code null} if not\n\t * available\n\t */\n\t@Nullable OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationResponseUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Utility methods for an OAuth 2.0 Authorization Response.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AuthorizationResponse\n */\nfinal class OAuth2AuthorizationResponseUtils {\n\n\tprivate OAuth2AuthorizationResponseUtils() {\n\t}\n\n\tstatic MultiValueMap<String, String> toMultiMap(Map<String, String[]> map) {\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>(map.size());\n\t\tmap.forEach((key, values) -> {\n\t\t\tif (values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\tparams.add(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn params;\n\t}\n\n\tstatic boolean isAuthorizationResponse(MultiValueMap<String, String> request) {\n\t\treturn isAuthorizationResponseSuccess(request) || isAuthorizationResponseError(request);\n\t}\n\n\tstatic boolean isAuthorizationResponseSuccess(MultiValueMap<String, String> request) {\n\t\treturn StringUtils.hasText(request.getFirst(OAuth2ParameterNames.CODE))\n\t\t\t\t&& StringUtils.hasText(request.getFirst(OAuth2ParameterNames.STATE));\n\t}\n\n\tstatic boolean isAuthorizationResponseError(MultiValueMap<String, String> request) {\n\t\treturn StringUtils.hasText(request.getFirst(OAuth2ParameterNames.ERROR))\n\t\t\t\t&& StringUtils.hasText(request.getFirst(OAuth2ParameterNames.STATE));\n\t}\n\n\tstatic OAuth2AuthorizationResponse convert(MultiValueMap<String, String> request, String redirectUri) {\n\t\tString code = request.getFirst(OAuth2ParameterNames.CODE);\n\t\tString errorCode = request.getFirst(OAuth2ParameterNames.ERROR);\n\t\tString state = request.getFirst(OAuth2ParameterNames.STATE);\n\t\tif (StringUtils.hasText(code)) {\n\t\t\tOAuth2AuthorizationResponse.Builder builder = OAuth2AuthorizationResponse.success(code)\n\t\t\t\t.redirectUri(redirectUri);\n\t\t\tif (state != null) {\n\t\t\t\tbuilder.state(state);\n\t\t\t}\n\t\t\treturn builder.build();\n\t\t}\n\t\tif (!StringUtils.hasText(errorCode)) {\n\t\t\terrorCode = \"unknown_error\";\n\t\t}\n\t\tString errorDescription = request.getFirst(OAuth2ParameterNames.ERROR_DESCRIPTION);\n\t\tString errorUri = request.getFirst(OAuth2ParameterNames.ERROR_URI);\n\t\tOAuth2AuthorizationResponse.Builder builder = OAuth2AuthorizationResponse.error(errorCode)\n\t\t\t.redirectUri(redirectUri);\n\t\tif (errorDescription != null) {\n\t\t\tbuilder.errorDescription(errorDescription);\n\t\t}\n\t\tif (errorUri != null) {\n\t\t\tbuilder.errorUri(errorUri);\n\t\t}\n\t\tif (state != null) {\n\t\t\tbuilder.state(state);\n\t\t}\n\t\treturn builder.build();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizedClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\n/**\n * Implementations of this interface are responsible for the persistence of\n * {@link OAuth2AuthorizedClient Authorized Client(s)} between requests.\n *\n * <p>\n * The primary purpose of an {@link OAuth2AuthorizedClient Authorized Client} is to\n * associate an {@link OAuth2AuthorizedClient#getAccessToken() Access Token} credential to\n * a {@link OAuth2AuthorizedClient#getClientRegistration() Client} and Resource Owner, who\n * is the {@link OAuth2AuthorizedClient#getPrincipalName() Principal} that originally\n * granted the authorization.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AuthorizedClient\n * @see ClientRegistration\n * @see Authentication\n * @see OAuth2AccessToken\n */\npublic interface OAuth2AuthorizedClientRepository {\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizedClient} associated to the provided client\n\t * registration identifier and End-User {@link Authentication} (Resource Owner) or\n\t * {@code null} if not available.\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t * @param request the {@code HttpServletRequest}\n\t * @param <T> a type of OAuth2AuthorizedClient\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if not available\n\t */\n\t<T extends OAuth2AuthorizedClient> @Nullable T loadAuthorizedClient(String clientRegistrationId,\n\t\t\tAuthentication principal, HttpServletRequest request);\n\n\t/**\n\t * Saves the {@link OAuth2AuthorizedClient} associating it to the provided End-User\n\t * {@link Authentication} (Resource Owner).\n\t * @param authorizedClient the authorized client\n\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t * @param request the {@code HttpServletRequest}\n\t * @param response the {@code HttpServletResponse}\n\t */\n\tvoid saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tHttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Removes the {@link OAuth2AuthorizedClient} associated to the provided client\n\t * registration identifier and End-User {@link Authentication} (Resource Owner).\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t * @param request the {@code HttpServletRequest}\n\t * @param response the {@code HttpServletResponse}\n\t */\n\tvoid removeAuthorizedClient(String clientRegistrationId, Authentication principal, HttpServletRequest request,\n\t\t\tHttpServletResponse response);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * An implementation of an {@link AbstractAuthenticationProcessingFilter} for OAuth 2.0\n * Login.\n *\n * <p>\n * This authentication {@code Filter} handles the processing of an OAuth 2.0 Authorization\n * Response for the authorization code grant flow and delegates an\n * {@link OAuth2LoginAuthenticationToken} to the {@link AuthenticationManager} to log in\n * the End-User.\n *\n * <p>\n * The OAuth 2.0 Authorization Response is processed as follows:\n *\n * <ul>\n * <li>Assuming the End-User (Resource Owner) has granted access to the Client, the\n * Authorization Server will append the {@link OAuth2ParameterNames#CODE code} and\n * {@link OAuth2ParameterNames#STATE state} parameters to the\n * {@link OAuth2ParameterNames#REDIRECT_URI redirect_uri} (provided in the Authorization\n * Request) and redirect the End-User's user-agent back to this {@code Filter} (the\n * Client).</li>\n * <li>This {@code Filter} will then create an {@link OAuth2LoginAuthenticationToken} with\n * the {@link OAuth2ParameterNames#CODE code} received and delegate it to the\n * {@link AuthenticationManager} to authenticate.</li>\n * <li>Upon a successful authentication, an {@link OAuth2AuthenticationToken} is created\n * (representing the End-User {@code Principal}) and associated to the\n * {@link OAuth2AuthorizedClient Authorized Client} using the\n * {@link OAuth2AuthorizedClientRepository}.</li>\n * <li>Finally, the {@link OAuth2AuthenticationToken} is returned and ultimately stored in\n * the {@link SecurityContextRepository} to complete the authentication processing.</li>\n * </ul>\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AbstractAuthenticationProcessingFilter\n * @see OAuth2LoginAuthenticationToken\n * @see OAuth2AuthenticationToken\n * @see OAuth2LoginAuthenticationProvider\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationResponse\n * @see AuthorizationRequestRepository\n * @see OAuth2AuthorizationRequestRedirectFilter\n * @see ClientRegistrationRepository\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientRepository\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.2\">Section 4.1.2 Authorization\n * Response</a>\n */\npublic class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n\t/**\n\t * The default {@code URI} where this {@code Filter} processes authentication\n\t * requests.\n\t */\n\tpublic static final String DEFAULT_FILTER_PROCESSES_URI = \"/login/oauth2/code/*\";\n\n\tprivate static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = \"authorization_request_not_found\";\n\n\tprivate static final String CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE = \"client_registration_not_found\";\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\n\tprivate Converter<OAuth2LoginAuthenticationToken, OAuth2AuthenticationToken> authenticationResultConverter = this::createAuthenticationResult;\n\n\t/**\n\t * Constructs an {@code OAuth2LoginAuthenticationFilter} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientService the authorized client service\n\t */\n\tpublic OAuth2LoginAuthenticationFilter(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientService authorizedClientService) {\n\t\tthis(clientRegistrationRepository, authorizedClientService, DEFAULT_FILTER_PROCESSES_URI);\n\t\tRequestMatcher processUri = pathPattern(DEFAULT_FILTER_PROCESSES_URI);\n\t\tsetRequiresAuthenticationRequestMatcher(processUri);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2LoginAuthenticationFilter} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientService the authorized client service\n\t * @param filterProcessesUrl the {@code URI} where this {@code Filter} will process\n\t * the authentication requests\n\t */\n\tpublic OAuth2LoginAuthenticationFilter(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientService authorizedClientService, String filterProcessesUrl) {\n\t\tthis(clientRegistrationRepository,\n\t\t\t\tnew AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService),\n\t\t\t\tfilterProcessesUrl);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2LoginAuthenticationFilter} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientRepository the authorized client repository\n\t * @param filterProcessesUrl the {@code URI} where this {@code Filter} will process\n\t * the authentication requests\n\t * @since 5.1\n\t */\n\tpublic OAuth2LoginAuthenticationFilter(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository, String filterProcessesUrl) {\n\t\tsuper(filterProcessesUrl);\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t}\n\n\t@Override\n\tpublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows AuthenticationException {\n\t\tMultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());\n\t\tif (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository\n\t\t\t.removeAuthorizationRequest(request, response);\n\t\tif (authorizationRequest == null) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t\tString registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID);\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\tClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);\n\t\tif (clientRegistration == null) {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE,\n\t\t\t\t\t\"Client Registration not found with Id: \" + registrationId, null);\n\t\t\tthrow new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());\n\t\t}\n\t\t// @formatter:off\n\t\tString redirectUri = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.build()\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params,\n\t\t\t\tredirectUri);\n\t\tObject authenticationDetails = this.authenticationDetailsSource.buildDetails(request);\n\t\tOAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(clientRegistration,\n\t\t\t\tnew OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));\n\t\tauthenticationRequest.setDetails(authenticationDetails);\n\t\tOAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken) this\n\t\t\t.getAuthenticationManager()\n\t\t\t.authenticate(authenticationRequest);\n\t\tOAuth2AuthenticationToken oauth2Authentication = this.authenticationResultConverter\n\t\t\t.convert(authenticationResult);\n\t\tAssert.notNull(oauth2Authentication, \"authentication result cannot be null\");\n\t\toauth2Authentication.setDetails(authenticationDetails);\n\t\tAssert.notNull(authenticationResult.getAccessToken(), \"accessToken cannot be null\");\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(\n\t\t\t\tauthenticationResult.getClientRegistration(), oauth2Authentication.getName(),\n\t\t\t\tauthenticationResult.getAccessToken(), authenticationResult.getRefreshToken());\n\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);\n\t\treturn oauth2Authentication;\n\t}\n\n\t/**\n\t * Sets the repository for stored {@link OAuth2AuthorizationRequest}'s.\n\t * @param authorizationRequestRepository the repository for stored\n\t * {@link OAuth2AuthorizationRequest}'s\n\t */\n\tpublic final void setAuthorizationRequestRepository(\n\t\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\tAssert.notNull(authorizationRequestRepository, \"authorizationRequestRepository cannot be null\");\n\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t}\n\n\t/**\n\t * Sets the converter responsible for converting from\n\t * {@link OAuth2LoginAuthenticationToken} to {@link OAuth2AuthenticationToken}\n\t * authentication result.\n\t * @param authenticationResultConverter the converter for\n\t * {@link OAuth2AuthenticationToken}'s\n\t * @since 5.6\n\t */\n\tpublic final void setAuthenticationResultConverter(\n\t\t\tConverter<OAuth2LoginAuthenticationToken, OAuth2AuthenticationToken> authenticationResultConverter) {\n\t\tAssert.notNull(authenticationResultConverter, \"authenticationResultConverter cannot be null\");\n\t\tthis.authenticationResultConverter = authenticationResultConverter;\n\t}\n\n\tprivate OAuth2AuthenticationToken createAuthenticationResult(OAuth2LoginAuthenticationToken authenticationResult) {\n\t\tAssert.notNull(authenticationResult.getPrincipal(), \"principal cannot be null\");\n\t\treturn new OAuth2AuthenticationToken(authenticationResult.getPrincipal(), authenticationResult.getAuthorities(),\n\t\t\t\tauthenticationResult.getClientRegistration().getRegistrationId());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport java.lang.reflect.Method;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.security.oauth2.client.annotation.ClientRegistrationId;\nimport org.springframework.security.oauth2.client.web.ClientAttributes;\nimport org.springframework.web.service.invoker.HttpRequestValues;\n\n/**\n * Invokes {@link ClientAttributes#clientRegistrationId(String)} with the value specified\n * by {@link ClientRegistrationId} on the request.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic final class ClientRegistrationIdProcessor implements HttpRequestValues.Processor {\n\n\tpublic static ClientRegistrationIdProcessor DEFAULT_INSTANCE = new ClientRegistrationIdProcessor();\n\n\tprivate SecurityAnnotationScanner<ClientRegistrationId> securityAnnotationScanner = SecurityAnnotationScanners\n\t\t.requireUnique(ClientRegistrationId.class);\n\n\t@Override\n\tpublic void process(Method method, MethodParameter[] parameters, @Nullable Object[] arguments,\n\t\t\tHttpRequestValues.Builder builder) {\n\t\tClientRegistrationId registeredId = this.securityAnnotationScanner.scan(method, method.getDeclaringClass());\n\n\t\tif (registeredId != null) {\n\t\t\tString registrationId = registeredId.registrationId();\n\t\t\tbuilder.configureAttributes(ClientAttributes.clientRegistrationId(registrationId));\n\t\t}\n\t}\n\n\tprivate ClientRegistrationIdProcessor() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/OAuth2ClientHttpRequestInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpRequest;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.http.client.ClientHttpRequestExecution;\nimport org.springframework.http.client.ClientHttpRequestInterceptor;\nimport org.springframework.http.client.ClientHttpResponse;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.RemoveAuthorizedClientOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestClientResponseException;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\n/**\n * Provides an easy mechanism for using an {@link OAuth2AuthorizedClient} to make OAuth\n * 2.0 requests by including the {@link OAuth2AuthorizedClient#getAccessToken() access\n * token} as a bearer token.\n *\n * <p>\n * Example usage:\n *\n * <pre>\n * OAuth2ClientHttpRequestInterceptor requestInterceptor =\n *     new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);\n * RestClient restClient = RestClient.builder()\n *     .requestInterceptor(requestInterceptor)\n *     .build();\n * String response = restClient.get()\n *     .uri(uri)\n *     .retrieve()\n *     .body(String.class);\n * </pre>\n *\n * <h3>Authentication and Authorization Failures</h3>\n *\n * <p>\n * This interceptor has the ability to forward authentication (HTTP 401 Unauthorized) and\n * authorization (HTTP 403 Forbidden) failures from an OAuth 2.0 Resource Server to an\n * {@link OAuth2AuthorizationFailureHandler}. A\n * {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} can be used to remove\n * the cached {@link OAuth2AuthorizedClient}, so that future requests will result in a new\n * token being retrieved from an Authorization Server, and sent to the Resource Server.\n *\n * <p>\n * Use either {@link #authorizationFailureHandler(OAuth2AuthorizedClientRepository)} or\n * {@link #authorizationFailureHandler(OAuth2AuthorizedClientService)} to create a\n * {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} which can be provided\n * to {@link #setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)}.\n *\n * <p>\n * For example:\n *\n * <pre>\n * OAuth2AuthorizationFailureHandler authorizationFailureHandler =\n *     OAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(authorizedClientRepository);\n * requestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler);\n * </pre>\n *\n * @author Steve Riesenberg\n * @since 6.4\n * @see OAuth2AuthorizedClientManager\n * @see OAuth2AuthorizedClientProvider\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizationFailureHandler\n */\npublic final class OAuth2ClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {\n\n\t// @formatter:off\n\tprivate static final Map<HttpStatusCode, String> OAUTH2_ERROR_CODES = Map.of(\n\t\t\tHttpStatus.UNAUTHORIZED, OAuth2ErrorCodes.INVALID_TOKEN,\n\t\t\tHttpStatus.FORBIDDEN, OAuth2ErrorCodes.INSUFFICIENT_SCOPE\n\t);\n\t// @formatter:on\n\n\tprivate static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(\"anonymous\",\n\t\t\t\"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tprivate final OAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate ClientRegistrationIdResolver clientRegistrationIdResolver = new RequestAttributeClientRegistrationIdResolver();\n\n\tprivate PrincipalResolver principalResolver = new SecurityContextHolderPrincipalResolver();\n\n\t// @formatter:off\n\tprivate OAuth2AuthorizationFailureHandler authorizationFailureHandler =\n\t\t\t(clientRegistrationId, principal, attributes) -> { };\n\t// @formatter:on\n\n\t/**\n\t * Constructs a {@code OAuth2ClientHttpRequestInterceptor} using the provided\n\t * parameters.\n\t * @param authorizedClientManager the {@link OAuth2AuthorizedClientManager} which\n\t * manages the authorized client(s)\n\t */\n\tpublic OAuth2ClientHttpRequestInterceptor(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tAssert.notNull(authorizedClientManager, \"authorizedClientManager cannot be null\");\n\t\tthis.authorizedClientManager = authorizedClientManager;\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizationFailureHandler} that handles authentication and\n\t * authorization failures when communicating to the OAuth 2.0 Resource Server.\n\t *\n\t * <p>\n\t * For example, a {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} is\n\t * typically used to remove the cached {@link OAuth2AuthorizedClient}, so that the\n\t * same token is no longer used in future requests to the Resource Server.\n\t * @param authorizationFailureHandler the {@link OAuth2AuthorizationFailureHandler}\n\t * that handles authentication and authorization failures\n\t * @see #authorizationFailureHandler(OAuth2AuthorizedClientRepository)\n\t * @see #authorizationFailureHandler(OAuth2AuthorizedClientService)\n\t */\n\tpublic void setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\tthis.authorizationFailureHandler = authorizationFailureHandler;\n\t}\n\n\t/**\n\t * Provides an {@link OAuth2AuthorizationFailureHandler} that handles authentication\n\t * and authorization failures when communicating to the OAuth 2.0 Resource Server\n\t * using a {@link OAuth2AuthorizedClientRepository}.\n\t *\n\t * <p>\n\t * When this method is used, authentication (HTTP 401) and authorization (HTTP 403)\n\t * failures returned from an OAuth 2.0 Resource Server will be forwarded to a\n\t * {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler}, which will\n\t * potentially remove the {@link OAuth2AuthorizedClient} from the given\n\t * {@link OAuth2AuthorizedClientRepository}, depending on the OAuth 2.0 error code\n\t * returned. Authentication failures returned from an OAuth 2.0 Resource Server\n\t * typically indicate that the token is invalid, and should not be used in future\n\t * requests. Removing the authorized client from the repository will ensure that the\n\t * existing token will not be sent for future requests to the Resource Server, and a\n\t * new token is retrieved from the Authorization Server and used for future requests\n\t * to the Resource Server.\n\t * @param authorizedClientRepository the repository of authorized clients\n\t * @see #setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)\n\t */\n\tpublic static OAuth2AuthorizationFailureHandler authorizationFailureHandler(\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\treturn new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> {\n\t\t\t\t\tHttpServletRequest request = (HttpServletRequest) attributes\n\t\t\t\t\t\t.get(HttpServletRequest.class.getName());\n\t\t\t\t\tHttpServletResponse response = (HttpServletResponse) attributes\n\t\t\t\t\t\t.get(HttpServletResponse.class.getName());\n\t\t\t\t\tif (request != null && response != null) {\n\t\t\t\t\t\tauthorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal, request,\n\t\t\t\t\t\t\t\tresponse);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\t/**\n\t * Provides an {@link OAuth2AuthorizationFailureHandler} that handles authentication\n\t * and authorization failures when communicating to the OAuth 2.0 Resource Server\n\t * using a {@link OAuth2AuthorizedClientService}.\n\t *\n\t * <p>\n\t * When this method is used, authentication (HTTP 401) and authorization (HTTP 403)\n\t * failures returned from an OAuth 2.0 Resource Server will be forwarded to a\n\t * {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler}, which will\n\t * potentially remove the {@link OAuth2AuthorizedClient} from the given\n\t * {@link OAuth2AuthorizedClientService}, depending on the OAuth 2.0 error code\n\t * returned. Authentication failures returned from an OAuth 2.0 Resource Server\n\t * typically indicate that the token is invalid, and should not be used in future\n\t * requests. Removing the authorized client from the repository will ensure that the\n\t * existing token will not be sent for future requests to the Resource Server, and a\n\t * new token is retrieved from the Authorization Server and used for future requests\n\t * to the Resource Server.\n\t * @param authorizedClientService the service used to manage authorized clients\n\t * @see #setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)\n\t */\n\tpublic static OAuth2AuthorizationFailureHandler authorizationFailureHandler(\n\t\t\tOAuth2AuthorizedClientService authorizedClientService) {\n\t\tAssert.notNull(authorizedClientService, \"authorizedClientService cannot be null\");\n\t\treturn new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> authorizedClientService\n\t\t\t\t\t.removeAuthorizedClient(clientRegistrationId, principal.getName()));\n\t}\n\n\t/**\n\t * Sets the strategy for resolving a {@code clientRegistrationId} from an intercepted\n\t * request.\n\t * @param clientRegistrationIdResolver the strategy for resolving a\n\t * {@code clientRegistrationId} from an intercepted request\n\t */\n\tpublic void setClientRegistrationIdResolver(ClientRegistrationIdResolver clientRegistrationIdResolver) {\n\t\tAssert.notNull(clientRegistrationIdResolver, \"clientRegistrationIdResolver cannot be null\");\n\t\tthis.clientRegistrationIdResolver = clientRegistrationIdResolver;\n\t}\n\n\t/**\n\t * Sets the strategy for resolving a {@link Authentication principal} from an\n\t * intercepted request.\n\t * @param principalResolver the strategy for resolving a {@link Authentication\n\t * principal}\n\t */\n\tpublic void setPrincipalResolver(PrincipalResolver principalResolver) {\n\t\tAssert.notNull(principalResolver, \"principalResolver cannot be null\");\n\t\tthis.principalResolver = principalResolver;\n\t}\n\n\t@Override\n\tpublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)\n\t\t\tthrows IOException {\n\t\tAuthentication principal = this.principalResolver.resolve(request);\n\t\tif (principal == null) {\n\t\t\tprincipal = ANONYMOUS_AUTHENTICATION;\n\t\t}\n\n\t\tauthorizeClient(request, principal);\n\t\ttry {\n\t\t\tClientHttpResponse response = execution.execute(request, body);\n\t\t\thandleAuthorizationFailure(request, principal, response.getHeaders(), response.getStatusCode());\n\t\t\treturn response;\n\t\t}\n\t\tcatch (RestClientResponseException ex) {\n\t\t\tHttpHeaders responseHeaders = ex.getResponseHeaders();\n\t\t\thandleAuthorizationFailure(request, principal,\n\t\t\t\t\t(responseHeaders != null) ? responseHeaders : new HttpHeaders(), ex.getStatusCode());\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (OAuth2AuthorizationException ex) {\n\t\t\thandleAuthorizationFailure(ex, principal);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tprivate void authorizeClient(HttpRequest request, Authentication principal) {\n\t\tString clientRegistrationId = this.clientRegistrationIdResolver.resolve(request);\n\t\tif (clientRegistrationId == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)\n\t\t\t.principal(principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tif (authorizedClient != null) {\n\t\t\trequest.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue());\n\t\t}\n\t}\n\n\tprivate void handleAuthorizationFailure(HttpRequest request, Authentication principal, HttpHeaders headers,\n\t\t\tHttpStatusCode httpStatus) {\n\t\tOAuth2Error error = resolveOAuth2ErrorIfPossible(headers, httpStatus);\n\t\tif (error == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tString clientRegistrationId = this.clientRegistrationIdResolver.resolve(request);\n\t\tif (clientRegistrationId == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tClientAuthorizationException authorizationException = new ClientAuthorizationException(error,\n\t\t\t\tclientRegistrationId);\n\t\thandleAuthorizationFailure(authorizationException, principal);\n\t}\n\n\tprivate static @Nullable OAuth2Error resolveOAuth2ErrorIfPossible(HttpHeaders headers, HttpStatusCode httpStatus) {\n\t\tString wwwAuthenticateHeader = headers.getFirst(HttpHeaders.WWW_AUTHENTICATE);\n\t\tif (wwwAuthenticateHeader != null) {\n\t\t\tMap<String, String> parameters = parseWwwAuthenticateHeader(wwwAuthenticateHeader);\n\t\t\tif (parameters.containsKey(OAuth2ParameterNames.ERROR)) {\n\t\t\t\treturn new OAuth2Error(parameters.get(OAuth2ParameterNames.ERROR),\n\t\t\t\t\t\tparameters.get(OAuth2ParameterNames.ERROR_DESCRIPTION),\n\t\t\t\t\t\tparameters.get(OAuth2ParameterNames.ERROR_URI));\n\t\t\t}\n\t\t}\n\n\t\tString errorCode = OAUTH2_ERROR_CODES.get(httpStatus);\n\t\tif (errorCode != null) {\n\t\t\treturn new OAuth2Error(errorCode, null, \"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate static Map<String, String> parseWwwAuthenticateHeader(String wwwAuthenticateHeader) {\n\t\tif (!StringUtils.hasLength(wwwAuthenticateHeader)\n\t\t\t\t|| !StringUtils.startsWithIgnoreCase(wwwAuthenticateHeader, \"bearer\")) {\n\t\t\treturn Map.of();\n\t\t}\n\n\t\tString headerValue = wwwAuthenticateHeader.substring(\"bearer\".length()).stripLeading();\n\t\tMap<String, String> parameters = new HashMap<>();\n\t\tfor (String kvPair : StringUtils.delimitedListToStringArray(headerValue, \",\")) {\n\t\t\tString[] kv = StringUtils.split(kvPair, \"=\");\n\t\t\tif (kv == null || kv.length <= 1) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tparameters.put(kv[0].trim(), kv[1].trim().replace(\"\\\"\", \"\"));\n\t\t}\n\n\t\treturn parameters;\n\t}\n\n\tprivate void handleAuthorizationFailure(OAuth2AuthorizationException authorizationException,\n\t\t\tAuthentication principal) {\n\t\tServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder\n\t\t\t.getRequestAttributes();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tif (requestAttributes != null) {\n\t\t\tattributes.put(HttpServletRequest.class.getName(), requestAttributes.getRequest());\n\t\t\tif (requestAttributes.getResponse() != null) {\n\t\t\t\tattributes.put(HttpServletResponse.class.getName(), requestAttributes.getResponse());\n\t\t\t}\n\t\t}\n\n\t\tthis.authorizationFailureHandler.onAuthorizationFailure(authorizationException, principal, attributes);\n\t}\n\n\t/**\n\t * A strategy for resolving a {@code clientRegistrationId} from an intercepted\n\t * request.\n\t */\n\t@FunctionalInterface\n\tpublic interface ClientRegistrationIdResolver {\n\n\t\t/**\n\t\t * Resolve the {@code clientRegistrationId} from the current request, which is\n\t\t * used to obtain an {@link OAuth2AuthorizedClient}.\n\t\t * @param request the intercepted request, containing HTTP method, URI, headers,\n\t\t * and request attributes\n\t\t * @return the {@code clientRegistrationId} to be used for resolving an\n\t\t * {@link OAuth2AuthorizedClient}.\n\t\t */\n\t\t@Nullable String resolve(HttpRequest request);\n\n\t}\n\n\t/**\n\t * A strategy for resolving a {@link Authentication principal} from an intercepted\n\t * request.\n\t */\n\t@FunctionalInterface\n\tpublic interface PrincipalResolver {\n\n\t\t/**\n\t\t * Resolve the {@link Authentication principal} from the current request, which is\n\t\t * used to obtain an {@link OAuth2AuthorizedClient}.\n\t\t * @param request the intercepted request, containing HTTP method, URI, headers,\n\t\t * and request attributes\n\t\t * @return the {@link Authentication principal} to be used for resolving an\n\t\t * {@link OAuth2AuthorizedClient}.\n\t\t */\n\t\t@Nullable Authentication resolve(HttpRequest request);\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/RequestAttributeClientRegistrationIdResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpRequest;\nimport org.springframework.http.client.ClientHttpRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.web.ClientAttributes;\n\n/**\n * A strategy for resolving a {@code clientRegistrationId} from an intercepted request\n * using {@link ClientHttpRequest#getAttributes() attributes}.\n *\n * @author Steve Riesenberg\n * @since 6.4\n * @see OAuth2ClientHttpRequestInterceptor\n */\npublic final class RequestAttributeClientRegistrationIdResolver\n\t\timplements OAuth2ClientHttpRequestInterceptor.ClientRegistrationIdResolver {\n\n\t@Override\n\tpublic @Nullable String resolve(HttpRequest request) {\n\t\treturn ClientAttributes.resolveClientRegistrationId(request.getAttributes());\n\t}\n\n\t/**\n\t * Modifies the {@link ClientHttpRequest#getAttributes() attributes} to include the\n\t * {@link ClientRegistration#getRegistrationId() clientRegistrationId} to be used to\n\t * look up the {@link OAuth2AuthorizedClient}.\n\t * @param clientRegistrationId the {@link ClientRegistration#getRegistrationId()\n\t * clientRegistrationId} to be used to look up the {@link OAuth2AuthorizedClient}\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> clientRegistrationId(String clientRegistrationId) {\n\t\treturn ClientAttributes.clientRegistrationId(clientRegistrationId);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/RequestAttributePrincipalResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpRequest;\nimport org.springframework.http.client.ClientHttpRequest;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.util.Assert;\n\n/**\n * A strategy for resolving a {@link Authentication principal} from an intercepted request\n * using {@link ClientHttpRequest#getAttributes() attributes}.\n *\n * @author Steve Riesenberg\n * @since 6.4\n */\npublic class RequestAttributePrincipalResolver implements OAuth2ClientHttpRequestInterceptor.PrincipalResolver {\n\n\tprivate static final String PRINCIPAL_ATTR_NAME = RequestAttributePrincipalResolver.class.getName()\n\t\t.concat(\".principal\");\n\n\t@Override\n\tpublic @Nullable Authentication resolve(HttpRequest request) {\n\t\treturn (Authentication) request.getAttributes().get(PRINCIPAL_ATTR_NAME);\n\t}\n\n\t/**\n\t * Modifies the {@link ClientHttpRequest#getAttributes() attributes} to include the\n\t * {@link Authentication principal} to be used to look up the\n\t * {@link OAuth2AuthorizedClient}.\n\t * @param principal the {@link Authentication principal} to be used to look up the\n\t * {@link OAuth2AuthorizedClient}\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> principal(Authentication principal) {\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\treturn (attributes) -> attributes.put(PRINCIPAL_ATTR_NAME, principal);\n\t}\n\n\t/**\n\t * Modifies the {@link ClientHttpRequest#getAttributes() attributes} to include the\n\t * {@link Authentication principal} to be used to look up the\n\t * {@link OAuth2AuthorizedClient}.\n\t * @param principalName the {@code principalName} to be used to look up the\n\t * {@link OAuth2AuthorizedClient}\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> principal(String principalName) {\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\tAuthentication principal = createAuthentication(principalName);\n\t\treturn (attributes) -> attributes.put(PRINCIPAL_ATTR_NAME, principal);\n\t}\n\n\tprivate static Authentication createAuthentication(String principalName) {\n\t\treturn new AbstractAuthenticationToken(Collections.emptySet()) {\n\t\t\t@Override\n\t\t\tpublic Object getPrincipal() {\n\t\t\t\treturn principalName;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable Object getCredentials() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/SecurityContextHolderPrincipalResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\n/**\n * A strategy for resolving a {@link Authentication principal} from an intercepted request\n * using the {@link SecurityContextHolder}.\n *\n * @author Steve Riesenberg\n * @since 6.4\n */\npublic class SecurityContextHolderPrincipalResolver implements OAuth2ClientHttpRequestInterceptor.PrincipalResolver {\n\n\tprivate final SecurityContextHolderStrategy securityContextHolderStrategy;\n\n\t/**\n\t * Constructs a {@code SecurityContextHolderPrincipalResolver}.\n\t */\n\tpublic SecurityContextHolderPrincipalResolver() {\n\t\tthis(SecurityContextHolder.getContextHolderStrategy());\n\t}\n\n\t/**\n\t * Constructs a {@code SecurityContextHolderPrincipalResolver} using the provided\n\t * parameters.\n\t * @param securityContextHolderStrategy the {@link SecurityContextHolderStrategy} to\n\t * use for resolving the {@link Authentication principal}\n\t */\n\tpublic SecurityContextHolderPrincipalResolver(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication resolve(HttpRequest request) {\n\t\treturn this.securityContextHolderStrategy.getContext().getAuthentication();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes for OAuth2 Client with RestClient and WebClient.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web.client;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/support/OAuth2RestClientHttpServiceGroupConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client.support;\n\nimport org.springframework.http.client.ClientHttpRequestInterceptor;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor;\nimport org.springframework.security.oauth2.client.web.client.OAuth2ClientHttpRequestInterceptor;\nimport org.springframework.web.client.RestClient;\nimport org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer;\nimport org.springframework.web.service.invoker.HttpRequestValues;\n\n/**\n * Simplify adding OAuth2 support to interface based rest clients that use\n * {@link RestClient}.\n *\n * It will add {@link OAuth2ClientHttpRequestInterceptor} to the {@link RestClient} and\n * {@link ClientRegistrationIdProcessor} to the\n * {@link org.springframework.web.service.invoker.HttpServiceProxyFactory}.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic final class OAuth2RestClientHttpServiceGroupConfigurer implements RestClientHttpServiceGroupConfigurer {\n\n\tprivate final HttpRequestValues.Processor processor = ClientRegistrationIdProcessor.DEFAULT_INSTANCE;\n\n\tprivate final ClientHttpRequestInterceptor interceptor;\n\n\tprivate OAuth2RestClientHttpServiceGroupConfigurer(ClientHttpRequestInterceptor interceptor) {\n\t\tthis.interceptor = interceptor;\n\t}\n\n\t@Override\n\tpublic void configureGroups(Groups<RestClient.Builder> groups) {\n\t\t// @formatter:off\n\t\tgroups.forEachClient((group, client) ->\n\t\t\tclient.requestInterceptor(this.interceptor)\n\t\t);\n\t\tgroups.forEachProxyFactory((group, factory) ->\n\t\t\tfactory.httpRequestValuesProcessor(this.processor)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tpublic static OAuth2RestClientHttpServiceGroupConfigurer from(\n\t\t\tOAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tOAuth2ClientHttpRequestInterceptor interceptor = new OAuth2ClientHttpRequestInterceptor(\n\t\t\t\tauthorizedClientManager);\n\t\treturn new OAuth2RestClientHttpServiceGroupConfigurer(interceptor);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/support/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes for OAuth2 Client with RestClient.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web.client.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/method/annotation/OAuth2AuthorizedClientArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.method.annotation;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.support.WebDataBinderFactory;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.method.support.ModelAndViewContainer;\n\n/**\n * An implementation of a {@link HandlerMethodArgumentResolver} that is capable of\n * resolving a method parameter to an argument value of type\n * {@link OAuth2AuthorizedClient}.\n *\n * <p>\n * For example: <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;GetMapping(\"/authorized-client\")\n *     public String authorizedClient(@RegisteredOAuth2AuthorizedClient(\"login-client\") OAuth2AuthorizedClient authorizedClient) {\n *         // do something with authorizedClient\n *     }\n * }\n * </pre>\n *\n * @author Joe Grandja\n * @since 5.1\n * @see RegisteredOAuth2AuthorizedClient\n */\npublic final class OAuth2AuthorizedClientArgumentResolver implements HandlerMethodArgumentResolver {\n\n\tprivate static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(\"anonymous\",\n\t\t\t\"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizedClientArgumentResolver} using the provided\n\t * parameters.\n\t * @param authorizedClientManager the {@link OAuth2AuthorizedClientManager} which\n\t * manages the authorized client(s)\n\t * @since 5.2\n\t */\n\tpublic OAuth2AuthorizedClientArgumentResolver(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tAssert.notNull(authorizedClientManager, \"authorizedClientManager cannot be null\");\n\t\tthis.authorizedClientManager = authorizedClientManager;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizedClientArgumentResolver} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientRepository the repository of authorized clients\n\t */\n\tpublic OAuth2AuthorizedClientArgumentResolver(ClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository,\n\t\t\t\tauthorizedClientRepository);\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\tClass<?> parameterType = parameter.getParameterType();\n\t\treturn (OAuth2AuthorizedClient.class.isAssignableFrom(parameterType) && (AnnotatedElementUtils\n\t\t\t.findMergedAnnotation(parameter.getParameter(), RegisteredOAuth2AuthorizedClient.class) != null));\n\t}\n\n\t@Override\n\tpublic @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,\n\t\t\tNativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {\n\t\tString clientRegistrationId = this.resolveClientRegistrationId(parameter);\n\t\tif (!StringUtils.hasLength(clientRegistrationId)) {\n\t\t\tthrow new IllegalArgumentException(\"Unable to resolve the Client Registration Identifier. \"\n\t\t\t\t\t+ \"It must be provided via @RegisteredOAuth2AuthorizedClient(\\\"client1\\\") or \"\n\t\t\t\t\t+ \"@RegisteredOAuth2AuthorizedClient(registrationId = \\\"client1\\\").\");\n\t\t}\n\t\tAuthentication principal = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (principal == null) {\n\t\t\tprincipal = ANONYMOUS_AUTHENTICATION;\n\t\t}\n\t\tHttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);\n\t\tHttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);\n\t\tAssert.notNull(servletRequest, \"HttpServletRequest is required for OAuth2 authorized client resolution\");\n\t\tAssert.notNull(servletResponse, \"HttpServletResponse is required for OAuth2 authorized client resolution\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(clientRegistrationId)\n\t\t\t\t.principal(principal)\n\t\t\t\t.attribute(HttpServletRequest.class.getName(), servletRequest)\n\t\t\t\t.attribute(HttpServletResponse.class.getName(), servletResponse)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\treturn this.authorizedClientManager.authorize(authorizeRequest);\n\t}\n\n\tprivate @Nullable String resolveClientRegistrationId(MethodParameter parameter) {\n\t\tRegisteredOAuth2AuthorizedClient authorizedClientAnnotation = AnnotatedElementUtils\n\t\t\t.findMergedAnnotation(parameter.getParameter(), RegisteredOAuth2AuthorizedClient.class);\n\t\tif (authorizedClientAnnotation == null) {\n\t\t\treturn null;\n\t\t}\n\t\tAuthentication principal = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (StringUtils.hasLength(authorizedClientAnnotation.registrationId())) {\n\t\t\treturn authorizedClientAnnotation.registrationId();\n\t\t}\n\t\tif (StringUtils.hasLength(authorizedClientAnnotation.value())) {\n\t\t\treturn authorizedClientAnnotation.value();\n\t\t}\n\t\tif (principal != null && OAuth2AuthenticationToken.class.isAssignableFrom(principal.getClass())) {\n\t\t\treturn ((OAuth2AuthenticationToken) principal).getAuthorizedClientRegistrationId();\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/method/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Method annotation support for OAuth2 client.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web.method.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Client {@code Filter}'s and supporting classes and interfaces.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.ClientAttributes;\nimport org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Provides an easy mechanism for using an {@link OAuth2AuthorizedClient} to make OAuth2\n * requests by including the token as a Bearer Token.\n *\n * <h3>Authentication and Authorization Failures</h3>\n *\n * <p>\n * Since 5.3, this filter function has the ability to forward authentication (HTTP 401\n * Unauthorized) and authorization (HTTP 403 Forbidden) failures from an OAuth 2.0\n * Resource Server to a {@link ReactiveOAuth2AuthorizationFailureHandler}. A\n * {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} can be used to\n * remove the cached {@link OAuth2AuthorizedClient}, so that future requests will result\n * in a new token being retrieved from an Authorization Server, and sent to the Resource\n * Server.\n * </p>\n *\n * <p>\n * If the\n * {@link #ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveClientRegistrationRepository, ServerOAuth2AuthorizedClientRepository)}\n * constructor is used, a\n * {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} will be\n * configured automatically.\n * </p>\n *\n * <p>\n * If the\n * {@link #ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)}\n * constructor is used, a\n * {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} will\n * <em>NOT</em> be configured automatically. It is recommended that you configure one via\n * {@link #setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler)}.\n * </p>\n *\n * @author Rob Winch\n * @author Joe Grandja\n * @author Phil Clay\n * @author Evgeniy Cheban\n * @since 5.1\n */\npublic final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements ExchangeFilterFunction {\n\n\t/**\n\t * The request attribute name used to locate the {@link OAuth2AuthorizedClient}.\n\t */\n\tprivate static final String OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME = OAuth2AuthorizedClient.class.getName();\n\n\t/**\n\t * The request attribute name used to locate the\n\t * {@link org.springframework.web.server.ServerWebExchange}.\n\t */\n\tprivate static final String SERVER_WEB_EXCHANGE_ATTR_NAME = ServerWebExchange.class.getName();\n\n\tprivate static final AnonymousAuthenticationToken ANONYMOUS_USER_TOKEN = new AnonymousAuthenticationToken(\n\t\t\t\"anonymous\", \"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\n\tprivate final Mono<Authentication> currentAuthenticationMono = ReactiveSecurityContextHolder.getContext()\n\t\t.flatMap((ctx) -> Mono.justOrEmpty(ctx.getAuthentication()))\n\t\t.defaultIfEmpty(ANONYMOUS_USER_TOKEN);\n\n\t// @formatter:off\n\tprivate final Mono<String> clientRegistrationIdMono = this.currentAuthenticationMono\n\t\t\t.filter((t) -> this.defaultOAuth2AuthorizedClient && t instanceof OAuth2AuthenticationToken)\n\t\t\t.cast(OAuth2AuthenticationToken.class)\n\t\t\t.map(OAuth2AuthenticationToken::getAuthorizedClientRegistrationId);\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate final Mono<ServerWebExchange> currentServerWebExchangeMono = Mono.deferContextual(Mono::just)\n\t\t\t.filter((c) -> c.hasKey(ServerWebExchange.class))\n\t\t\t.map((c) -> c.get(ServerWebExchange.class));\n\t// @formatter:on\n\n\tprivate final ReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate boolean defaultOAuth2AuthorizedClient;\n\n\tprivate @Nullable String defaultClientRegistrationId;\n\n\tprivate ClientResponseHandler clientResponseHandler;\n\n\tprivate ServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\n\t/**\n\t * Constructs a {@code ServerOAuth2AuthorizedClientExchangeFilterFunction} using the\n\t * provided parameters.\n\t *\n\t * <p>\n\t * When this constructor is used, authentication (HTTP 401) and authorization (HTTP\n\t * 403) failures returned from a OAuth 2.0 Resource Server will <em>NOT</em> be\n\t * forwarded to a {@link ReactiveOAuth2AuthorizationFailureHandler}. Therefore, future\n\t * requests to the Resource Server will most likely use the same (most likely invalid)\n\t * token, resulting in the same errors returned from the Resource Server. It is\n\t * recommended to configure a\n\t * {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} via\n\t * {@link #setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler)}\n\t * so that authentication and authorization failures returned from a Resource Server\n\t * will result in removing the authorized client, so that a new token is retrieved for\n\t * future requests.\n\t * </p>\n\t * @param authorizedClientManager the {@link ReactiveOAuth2AuthorizedClientManager}\n\t * which manages the authorized client(s)\n\t * @since 5.2\n\t */\n\tpublic ServerOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\tReactiveOAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tAssert.notNull(authorizedClientManager, \"authorizedClientManager cannot be null\");\n\t\tthis.authorizedClientManager = authorizedClientManager;\n\t\tthis.clientResponseHandler = (request, responseMono) -> responseMono;\n\t}\n\n\t/**\n\t * Constructs a {@code ServerOAuth2AuthorizedClientExchangeFilterFunction} using the\n\t * provided parameters.\n\t *\n\t * <p>\n\t * Since 5.3, when this constructor is used, authentication (HTTP 401) and\n\t * authorization (HTTP 403) failures returned from an OAuth 2.0 Resource Server will\n\t * be forwarded to a\n\t * {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler}, which will\n\t * potentially remove the {@link OAuth2AuthorizedClient} from the given\n\t * {@link ServerOAuth2AuthorizedClientRepository}, depending on the OAuth 2.0 error\n\t * code returned. Authentication failures returned from an OAuth 2.0 Resource Server\n\t * typically indicate that the token is invalid, and should not be used in future\n\t * requests. Removing the authorized client from the repository will ensure that the\n\t * existing token will not be sent for future requests to the Resource Server, and a\n\t * new token is retrieved from Authorization Server and used for future requests to\n\t * the Resource Server.\n\t * </p>\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientRepository the repository of authorized clients\n\t */\n\tpublic ServerOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler = new RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> {\n\t\t\t\t\tServerWebExchange exchange = (ServerWebExchange) attributes.get(ServerWebExchange.class.getName());\n\t\t\t\t\tif (exchange != null) {\n\t\t\t\t\t\treturn authorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal,\n\t\t\t\t\t\t\t\texchange);\n\t\t\t\t\t}\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t});\n\t\tthis.authorizedClientManager = createDefaultAuthorizedClientManager(clientRegistrationRepository,\n\t\t\t\tauthorizedClientRepository, authorizationFailureHandler);\n\t\tthis.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);\n\t}\n\n\tprivate static ReactiveOAuth2AuthorizedClientManager createDefaultAuthorizedClientManager(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository,\n\t\t\tReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\t// gh-7544\n\t\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizationFailureHandler(authorizationFailureHandler);\n\t\treturn authorizedClientManager;\n\t}\n\n\t/**\n\t * Modifies the {@link ClientRequest#attributes()} to include the\n\t * {@link OAuth2AuthorizedClient} to be used for providing the Bearer Token. Example\n\t * usage:\n\t *\n\t * <pre>\n\t * WebClient webClient = WebClient.builder()\n\t *    .filter(new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager))\n\t *    .build();\n\t * Mono&lt;String&gt; response = webClient\n\t *    .get()\n\t *    .uri(uri)\n\t *    .attributes(oauth2AuthorizedClient(authorizedClient))\n\t *    // ...\n\t *    .retrieve()\n\t *    .bodyToMono(String.class);\n\t * </pre>\n\t *\n\t * An attempt to automatically refresh the token will be made if all of the following\n\t * are true:\n\t *\n\t * <ul>\n\t * <li>A refresh token is present on the OAuth2AuthorizedClient</li>\n\t * <li>The access token will be expired in 1 minute (the default)</li>\n\t * <li>The {@link ReactiveSecurityContextHolder} will be used to attempt to save the\n\t * token. If it is empty, then the principal name on the OAuth2AuthorizedClient will\n\t * be used to create an Authentication for saving.</li>\n\t * </ul>\n\t * @param authorizedClient the {@link OAuth2AuthorizedClient} to use.\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> oauth2AuthorizedClient(OAuth2AuthorizedClient authorizedClient) {\n\t\treturn (attributes) -> attributes.put(OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME, authorizedClient);\n\t}\n\n\tprivate static @Nullable OAuth2AuthorizedClient oauth2AuthorizedClient(ClientRequest request) {\n\t\treturn (OAuth2AuthorizedClient) request.attributes().get(OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME);\n\t}\n\n\t/**\n\t * Modifies the {@link ClientRequest#attributes()} to include the\n\t * {@link ServerWebExchange} to be used for providing the Bearer Token. Example usage:\n\t *\n\t * <pre>\n\t * WebClient webClient = WebClient.builder()\n\t *    .filter(new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager))\n\t *    .build();\n\t * Mono&lt;String&gt; response = webClient\n\t *    .get()\n\t *    .uri(uri)\n\t *    .attributes(serverWebExchange(serverWebExchange))\n\t *    // ...\n\t *    .retrieve()\n\t *    .bodyToMono(String.class);\n\t * </pre>\n\t * @param serverWebExchange the {@link ServerWebExchange} to use\n\t * @return the {@link Consumer} to populate the client request attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> serverWebExchange(ServerWebExchange serverWebExchange) {\n\t\treturn (attributes) -> attributes.put(SERVER_WEB_EXCHANGE_ATTR_NAME, serverWebExchange);\n\t}\n\n\tprivate static @Nullable ServerWebExchange serverWebExchange(ClientRequest request) {\n\t\treturn (ServerWebExchange) request.attributes().get(SERVER_WEB_EXCHANGE_ATTR_NAME);\n\t}\n\n\t/**\n\t * Modifies the {@link ClientRequest#attributes()} to include the\n\t * {@link ClientRegistration#getRegistrationId()} to be used to look up the\n\t * {@link OAuth2AuthorizedClient}.\n\t * @param clientRegistrationId the {@link ClientRegistration#getRegistrationId()} to\n\t * be used to look up the {@link OAuth2AuthorizedClient}.\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> clientRegistrationId(String clientRegistrationId) {\n\t\treturn ClientAttributes.clientRegistrationId(clientRegistrationId);\n\t}\n\n\tprivate static @Nullable String clientRegistrationId(ClientRequest request) {\n\t\tOAuth2AuthorizedClient authorizedClient = oauth2AuthorizedClient(request);\n\t\tif (authorizedClient != null) {\n\t\t\treturn authorizedClient.getClientRegistration().getRegistrationId();\n\t\t}\n\t\treturn ClientAttributes.resolveClientRegistrationId(request.attributes());\n\t}\n\n\t/**\n\t * If true, a default {@link OAuth2AuthorizedClient} can be discovered from the\n\t * current Authentication. It is recommended to be cautious with this feature since\n\t * all HTTP requests will receive the access token if it can be resolved from the\n\t * current Authentication.\n\t * @param defaultOAuth2AuthorizedClient true if a default\n\t * {@link OAuth2AuthorizedClient} should be used, else false. Default is false.\n\t */\n\tpublic void setDefaultOAuth2AuthorizedClient(boolean defaultOAuth2AuthorizedClient) {\n\t\tthis.defaultOAuth2AuthorizedClient = defaultOAuth2AuthorizedClient;\n\t}\n\n\t/**\n\t * If set, will be used as the default {@link ClientRegistration#getRegistrationId()}.\n\t * It is recommended to be cautious with this feature since all HTTP requests will\n\t * receive the access token.\n\t * @param clientRegistrationId the id to use\n\t */\n\tpublic void setDefaultClientRegistrationId(String clientRegistrationId) {\n\t\tthis.defaultClientRegistrationId = clientRegistrationId;\n\t}\n\n\t@Override\n\tpublic Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {\n\t\t// @formatter:off\n\t\treturn authorizedClient(request)\n\t\t\t\t.map((authorizedClient) -> bearer(request, authorizedClient))\n\t\t\t\t.flatMap((requestWithBearer) -> exchangeAndHandleResponse(requestWithBearer, next))\n\t\t\t\t.switchIfEmpty(Mono.defer(() -> exchangeAndHandleResponse(request, next)));\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<ClientResponse> exchangeAndHandleResponse(ClientRequest request, ExchangeFunction next) {\n\t\t// Re-request an Authentication from serverSecurityContextRepository since it\n\t\t// might have been changed during provider invocation.\n\t\treturn effectiveAuthentication(request).flatMap((authentication) -> next.exchange(request)\n\t\t\t.transform((responseMono) -> this.clientResponseHandler.handleResponse(request, responseMono))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication)));\n\t}\n\n\tprivate Mono<OAuth2AuthorizedClient> authorizedClient(ClientRequest request) {\n\t\tOAuth2AuthorizedClient authorizedClientFromAttrs = oauth2AuthorizedClient(request);\n\t\t// @formatter:off\n\t\treturn Mono.justOrEmpty(authorizedClientFromAttrs)\n\t\t\t\t.switchIfEmpty(Mono.defer(() -> authorizeRequest(request)\n\t\t\t\t\t\t.flatMap(this.authorizedClientManager::authorize))\n\t\t\t\t)\n\t\t\t\t.flatMap((authorizedClient) -> reauthorizeRequest(request, authorizedClient)\n\t\t\t\t\t\t.flatMap(this.authorizedClientManager::authorize)\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<OAuth2AuthorizeRequest> authorizeRequest(ClientRequest request) {\n\t\tMono<String> clientRegistrationId = effectiveClientRegistrationId(request);\n\t\tMono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);\n\t\t// @formatter:off\n\t\treturn Mono.zip(clientRegistrationId, this.currentAuthenticationMono, serverWebExchange)\n\t\t\t\t.map((t3) -> {\n\t\t\t\t\tOAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest\n\t\t\t\t\t\t\t.withClientRegistrationId(t3.getT1())\n\t\t\t\t\t\t\t.principal(t3.getT2());\n\t\t\t\t\tt3.getT3().ifPresent((exchange) -> builder.attribute(ServerWebExchange.class.getName(), exchange));\n\t\t\t\t\treturn builder.build();\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Authentication> effectiveAuthentication(ClientRequest request) {\n\t\t// @formatter:off\n\t\treturn effectiveServerWebExchange(request)\n\t\t\t.filter(Optional::isPresent)\n\t\t\t.map(Optional::get)\n\t\t\t.flatMap(this.serverSecurityContextRepository::load)\n\t\t\t.flatMap((ctx) -> Mono.justOrEmpty(ctx.getAuthentication()))\n\t\t\t.switchIfEmpty(this.currentAuthenticationMono);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Returns a {@link Mono} the emits the {@code clientRegistrationId} that is active\n\t * for the given request.\n\t * @param request the request for which to retrieve the {@code clientRegistrationId}\n\t * @return a mono that emits the {@code clientRegistrationId} that is active for the\n\t * given request.\n\t */\n\tprivate Mono<String> effectiveClientRegistrationId(ClientRequest request) {\n\t\t// @formatter:off\n\t\treturn Mono.justOrEmpty(clientRegistrationId(request))\n\t\t\t\t.switchIfEmpty(Mono.justOrEmpty(this.defaultClientRegistrationId))\n\t\t\t\t.switchIfEmpty(this.clientRegistrationIdMono);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Returns a {@link Mono} that emits an {@link Optional} for the\n\t * {@link ServerWebExchange} that is active for the given request.\n\t *\n\t * <p>\n\t * The returned {@link Mono} will never complete empty. Instead, it will emit an empty\n\t * {@link Optional} if no exchange is active.\n\t * </p>\n\t * @param request the request for which to retrieve the exchange\n\t * @return a {@link Mono} that emits an {@link Optional} for the\n\t * {@link ServerWebExchange} that is active for the given request.\n\t */\n\tprivate Mono<Optional<ServerWebExchange>> effectiveServerWebExchange(ClientRequest request) {\n\t\t// @formatter:off\n\t\treturn Mono.justOrEmpty(serverWebExchange(request))\n\t\t\t\t.switchIfEmpty(this.currentServerWebExchangeMono)\n\t\t\t\t.map(Optional::of)\n\t\t\t\t.defaultIfEmpty(Optional.empty());\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<OAuth2AuthorizeRequest> reauthorizeRequest(ClientRequest request,\n\t\t\tOAuth2AuthorizedClient authorizedClient) {\n\t\tMono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);\n\t\t// @formatter:off\n\t\treturn Mono.zip(this.currentAuthenticationMono, serverWebExchange)\n\t\t\t\t.map((t2) -> {\n\t\t\t\t\tOAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withAuthorizedClient(authorizedClient)\n\t\t\t\t\t\t\t.principal(t2.getT1());\n\t\t\t\t\tt2.getT2().ifPresent((exchange) -> builder.attribute(ServerWebExchange.class.getName(), exchange));\n\t\t\t\t\treturn builder.build();\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate ClientRequest bearer(ClientRequest request, OAuth2AuthorizedClient authorizedClient) {\n\t\t// @formatter:off\n\t\treturn ClientRequest.from(request)\n\t\t\t\t.headers((headers) -> headers.setBearerAuth(authorizedClient.getAccessToken().getTokenValue()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Sets the handler that handles authentication and authorization failures when\n\t * communicating to the OAuth 2.0 Resource Server.\n\t *\n\t * <p>\n\t * For example, a\n\t * {@link RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler} is\n\t * typically used to remove the cached {@link OAuth2AuthorizedClient}, so that the\n\t * same token is no longer used in future requests to the Resource Server.\n\t * </p>\n\t *\n\t * <p>\n\t * The failure handler used by default depends on which constructor was used to\n\t * construct this {@link ServerOAuth2AuthorizedClientExchangeFilterFunction}. See the\n\t * constructors for more details.\n\t * </p>\n\t * @param authorizationFailureHandler the handler that handles authentication and\n\t * authorization failures.\n\t * @since 5.3\n\t */\n\tpublic void setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\tthis.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);\n\t}\n\n\t/**\n\t * Sets a {@link ServerSecurityContextRepository} to use for re-obtaining a\n\t * {@link SecurityContext} if it has been refreshed during provider invocation,\n\t * defaults to {@link WebSessionServerSecurityContextRepository}.\n\t * @param serverSecurityContextRepository the {@link ServerSecurityContextRepository}\n\t * to use\n\t * @since 7.1\n\t */\n\tpublic void setServerSecurityContextRepository(ServerSecurityContextRepository serverSecurityContextRepository) {\n\t\tAssert.notNull(serverSecurityContextRepository, \"serverSecurityContextRepository cannot be null\");\n\t\tthis.serverSecurityContextRepository = serverSecurityContextRepository;\n\t}\n\n\t@FunctionalInterface\n\tprivate interface ClientResponseHandler {\n\n\t\tMono<ClientResponse> handleResponse(ClientRequest request, Mono<ClientResponse> response);\n\n\t}\n\n\t/**\n\t * Forwards authentication and authorization failures to a\n\t * {@link ReactiveOAuth2AuthorizationFailureHandler}.\n\t *\n\t * @since 5.3\n\t */\n\tprivate final class AuthorizationFailureForwarder implements ClientResponseHandler {\n\n\t\t/**\n\t\t * A map of HTTP Status Code to OAuth 2.0 Error codes for HTTP status codes that\n\t\t * should be interpreted as authentication or authorization failures.\n\t\t */\n\t\tprivate final Map<HttpStatusCode, String> httpStatusToOAuth2ErrorCodeMap;\n\n\t\t/**\n\t\t * The {@link ReactiveOAuth2AuthorizationFailureHandler} to notify when an\n\t\t * authentication/authorization failure occurs.\n\t\t */\n\t\tprivate final ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t\tprivate AuthorizationFailureForwarder(ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\t\tthis.authorizationFailureHandler = authorizationFailureHandler;\n\t\t\tMap<HttpStatusCode, String> httpStatusToOAuth2Error = new HashMap<>();\n\t\t\thttpStatusToOAuth2Error.put(HttpStatus.UNAUTHORIZED, OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\thttpStatusToOAuth2Error.put(HttpStatus.FORBIDDEN, OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t\tthis.httpStatusToOAuth2ErrorCodeMap = Collections.unmodifiableMap(httpStatusToOAuth2Error);\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<ClientResponse> handleResponse(ClientRequest request, Mono<ClientResponse> responseMono) {\n\t\t\t// @formatter:off\n\t\t\treturn responseMono\n\t\t\t\t\t.flatMap((response) -> handleResponse(request, response).thenReturn(response))\n\t\t\t\t\t.onErrorResume(WebClientResponseException.class,\n\t\t\t\t\t\t\t(e) -> handleWebClientResponseException(request, e).then(Mono.error(e))\n\t\t\t\t\t)\n\t\t\t\t\t.onErrorResume(OAuth2AuthorizationException.class,\n\t\t\t\t\t\t\t(e) -> handleAuthorizationException(request, e).then(Mono.error(e)));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate Mono<Void> handleResponse(ClientRequest request, ClientResponse response) {\n\t\t\t// @formatter:off\n\t\t\treturn Mono.justOrEmpty(resolveErrorIfPossible(response))\n\t\t\t\t\t.flatMap((oauth2Error) -> {\n\t\t\t\t\t\tMono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);\n\t\t\t\t\t\tMono<String> clientRegistrationId = effectiveClientRegistrationId(request);\n\t\t\t\t\t\treturn Mono\n\t\t\t\t\t\t\t\t.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,\n\t\t\t\t\t\t\t\t\t\tserverWebExchange, clientRegistrationId)\n\t\t\t\t\t\t\t\t.flatMap((zipped) -> handleAuthorizationFailure(zipped.getT1(), zipped.getT2(),\n\t\t\t\t\t\t\t\t\t\tnew ClientAuthorizationException(oauth2Error, zipped.getT3())));\n\t\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate @Nullable OAuth2Error resolveErrorIfPossible(ClientResponse response) {\n\t\t\t// Try to resolve from 'WWW-Authenticate' header\n\t\t\tif (!response.headers().header(HttpHeaders.WWW_AUTHENTICATE).isEmpty()) {\n\t\t\t\tString wwwAuthenticateHeader = response.headers().header(HttpHeaders.WWW_AUTHENTICATE).get(0);\n\t\t\t\tMap<String, String> authParameters = parseAuthParameters(wwwAuthenticateHeader);\n\t\t\t\tif (authParameters.containsKey(OAuth2ParameterNames.ERROR)) {\n\t\t\t\t\treturn new OAuth2Error(authParameters.get(OAuth2ParameterNames.ERROR),\n\t\t\t\t\t\t\tauthParameters.get(OAuth2ParameterNames.ERROR_DESCRIPTION),\n\t\t\t\t\t\t\tauthParameters.get(OAuth2ParameterNames.ERROR_URI));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn resolveErrorIfPossible(response.statusCode());\n\t\t}\n\n\t\tprivate @Nullable OAuth2Error resolveErrorIfPossible(HttpStatusCode statusCode) {\n\t\t\tif (this.httpStatusToOAuth2ErrorCodeMap.containsKey(statusCode)) {\n\t\t\t\treturn new OAuth2Error(this.httpStatusToOAuth2ErrorCodeMap.get(statusCode), null,\n\t\t\t\t\t\t\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate Map<String, String> parseAuthParameters(String wwwAuthenticateHeader) {\n\t\t\t// @formatter:off\n\t\t\treturn Stream.of(wwwAuthenticateHeader)\n\t\t\t\t\t.filter((header) -> StringUtils.hasLength(header))\n\t\t\t\t\t.filter((header) -> header.toLowerCase(Locale.ENGLISH).startsWith(\"bearer\"))\n\t\t\t\t\t.map((header) -> header.substring(\"bearer\".length()))\n\t\t\t\t\t.map((header) -> header.split(\",\"))\n\t\t\t\t\t.flatMap(Stream::of)\n\t\t\t\t\t.map((parameter) -> parameter.split(\"=\"))\n\t\t\t\t\t.filter((parameter) -> parameter.length > 1)\n\t\t\t\t\t.collect(Collectors.toMap((parameters) -> parameters[0].trim(),\n\t\t\t\t\t\t\t(parameters) -> parameters[1].trim().replace(\"\\\"\", \"\"))\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t/**\n\t\t * Handles the given http status code returned from a resource server by notifying\n\t\t * the authorization failure handler if the http status code is in the\n\t\t * {@link #httpStatusToOAuth2ErrorCodeMap}.\n\t\t * @param request the request being processed\n\t\t * @param exception The root cause exception for the failure\n\t\t * @return a {@link Mono} that completes empty after the authorization failure\n\t\t * handler completes.\n\t\t */\n\t\tprivate Mono<Void> handleWebClientResponseException(ClientRequest request,\n\t\t\t\tWebClientResponseException exception) {\n\t\t\treturn Mono.justOrEmpty(resolveErrorIfPossible(exception.getStatusCode())).flatMap((oauth2Error) -> {\n\t\t\t\tMono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);\n\t\t\t\tMono<String> clientRegistrationId = effectiveClientRegistrationId(request);\n\t\t\t\treturn Mono\n\t\t\t\t\t.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,\n\t\t\t\t\t\t\tserverWebExchange, clientRegistrationId)\n\t\t\t\t\t.flatMap((zipped) -> handleAuthorizationFailure(zipped.getT1(), zipped.getT2(),\n\t\t\t\t\t\t\tnew ClientAuthorizationException(oauth2Error, zipped.getT3(), exception)));\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Handles the given OAuth2AuthorizationException that occurred downstream by\n\t\t * notifying the authorization failure handler.\n\t\t * @param request the request being processed\n\t\t * @param exception the authorization exception to include in the failure event.\n\t\t * @return a {@link Mono} that completes empty after the authorization failure\n\t\t * handler completes.\n\t\t */\n\t\tprivate Mono<Void> handleAuthorizationException(ClientRequest request, OAuth2AuthorizationException exception) {\n\t\t\tMono<Optional<ServerWebExchange>> serverWebExchange = effectiveServerWebExchange(request);\n\t\t\treturn Mono\n\t\t\t\t.zip(ServerOAuth2AuthorizedClientExchangeFilterFunction.this.currentAuthenticationMono,\n\t\t\t\t\t\tserverWebExchange)\n\t\t\t\t.flatMap((zipped) -> handleAuthorizationFailure(zipped.getT1(), zipped.getT2(), exception));\n\t\t}\n\n\t\t/**\n\t\t * Delegates to the authorization failure handler of the failed authorization.\n\t\t * @param principal the principal associated with the failed authorization attempt\n\t\t * @param exchange the currently active exchange\n\t\t * @param exception the authorization exception to include in the failure event.\n\t\t * @return a {@link Mono} that completes empty after the authorization failure\n\t\t * handler completes.\n\t\t */\n\t\tprivate Mono<Void> handleAuthorizationFailure(Authentication principal, Optional<ServerWebExchange> exchange,\n\t\t\t\tOAuth2AuthorizationException exception) {\n\t\t\treturn this.authorizationFailureHandler.onAuthorizationFailure(exception, principal,\n\t\t\t\t\tcreateAttributes(exchange.orElse(null)));\n\t\t}\n\n\t\tprivate Map<String, Object> createAttributes(@Nullable ServerWebExchange exchange) {\n\t\t\tif (exchange == null) {\n\t\t\t\treturn Collections.emptyMap();\n\t\t\t}\n\t\t\treturn Collections.singletonMap(ServerWebExchange.class.getName(), exchange);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\nimport reactor.util.context.Context;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.HttpStatusCode;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;\nimport org.springframework.security.oauth2.client.RemoveAuthorizedClientOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.ClientAttributes;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\n\n/**\n * Provides an easy mechanism for using an {@link OAuth2AuthorizedClient} to make OAuth\n * 2.0 requests by including the {@link OAuth2AuthorizedClient#getAccessToken() access\n * token} as a bearer token.\n *\n * <p>\n * <b>NOTE:</b>This class is intended to be used in a {@code Servlet} environment.\n *\n * <p>\n * Example usage:\n *\n * <pre>\n * ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n * WebClient webClient = WebClient.builder()\n *    .apply(oauth2.oauth2Configuration())\n *    .build();\n * Mono&lt;String&gt; response = webClient\n *    .get()\n *    .uri(uri)\n *    .attributes(oauth2AuthorizedClient(authorizedClient))\n *    // ...\n *    .retrieve()\n *    .bodyToMono(String.class);\n * </pre>\n *\n * <h3>Authentication and Authorization Failures</h3>\n *\n * <p>\n * Since 5.3, this filter function has the ability to forward authentication (HTTP 401\n * Unauthorized) and authorization (HTTP 403 Forbidden) failures from an OAuth 2.0\n * Resource Server to a {@link OAuth2AuthorizationFailureHandler}. A\n * {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} can be used to remove\n * the cached {@link OAuth2AuthorizedClient}, so that future requests will result in a new\n * token being retrieved from an Authorization Server, and sent to the Resource Server.\n *\n * <p>\n * If the\n * {@link #ServletOAuth2AuthorizedClientExchangeFilterFunction(ClientRegistrationRepository, OAuth2AuthorizedClientRepository)}\n * constructor is used, a {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler}\n * will be configured automatically.\n *\n * <p>\n * If the\n * {@link #ServletOAuth2AuthorizedClientExchangeFilterFunction(OAuth2AuthorizedClientManager)}\n * constructor is used, a {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler}\n * will <em>NOT</em> be configured automatically. It is recommended that you configure one\n * via {@link #setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)}.\n *\n * @author Rob Winch\n * @author Joe Grandja\n * @author Roman Matiushchenko\n * @since 5.1\n * @see OAuth2AuthorizedClientManager\n * @see DefaultOAuth2AuthorizedClientManager\n * @see OAuth2AuthorizedClientProvider\n * @see OAuth2AuthorizedClientProviderBuilder\n */\npublic final class ServletOAuth2AuthorizedClientExchangeFilterFunction implements ExchangeFilterFunction {\n\n\t// Same key as in\n\t// SecurityReactorContextConfiguration.SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES\n\tstatic final String SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY = \"org.springframework.security.SECURITY_CONTEXT_ATTRIBUTES\";\n\n\t/**\n\t * The request attribute name used to locate the {@link OAuth2AuthorizedClient}.\n\t */\n\tprivate static final String OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME = OAuth2AuthorizedClient.class.getName();\n\n\tprivate static final String AUTHENTICATION_ATTR_NAME = Authentication.class.getName();\n\n\tprivate static final String HTTP_SERVLET_REQUEST_ATTR_NAME = HttpServletRequest.class.getName();\n\n\tprivate static final String HTTP_SERVLET_RESPONSE_ATTR_NAME = HttpServletResponse.class.getName();\n\n\tprivate static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(\"anonymous\",\n\t\t\t\"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable OAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate boolean defaultOAuth2AuthorizedClient;\n\n\tprivate @Nullable String defaultClientRegistrationId;\n\n\tprivate @Nullable ClientResponseHandler clientResponseHandler;\n\n\tpublic ServletOAuth2AuthorizedClientExchangeFilterFunction() {\n\t}\n\n\t/**\n\t * Constructs a {@code ServletOAuth2AuthorizedClientExchangeFilterFunction} using the\n\t * provided parameters.\n\t *\n\t * <p>\n\t * When this constructor is used, authentication (HTTP 401) and authorization (HTTP\n\t * 403) failures returned from an OAuth 2.0 Resource Server will <em>NOT</em> be\n\t * forwarded to an {@link OAuth2AuthorizationFailureHandler}. Therefore, future\n\t * requests to the Resource Server will most likely use the same (likely invalid)\n\t * token, resulting in the same errors returned from the Resource Server. It is\n\t * recommended to configure a\n\t * {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} via\n\t * {@link #setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)} so that\n\t * authentication and authorization failures returned from a Resource Server will\n\t * result in removing the authorized client, so that a new token is retrieved for\n\t * future requests.\n\t * @param authorizedClientManager the {@link OAuth2AuthorizedClientManager} which\n\t * manages the authorized client(s)\n\t * @since 5.2\n\t */\n\tpublic ServletOAuth2AuthorizedClientExchangeFilterFunction(OAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tAssert.notNull(authorizedClientManager, \"authorizedClientManager cannot be null\");\n\t\tthis.authorizedClientManager = authorizedClientManager;\n\t\tthis.clientResponseHandler = (request, responseMono) -> responseMono;\n\t}\n\n\t/**\n\t * Constructs a {@code ServletOAuth2AuthorizedClientExchangeFilterFunction} using the\n\t * provided parameters.\n\t *\n\t * <p>\n\t * Since 5.3, when this constructor is used, authentication (HTTP 401) and\n\t * authorization (HTTP 403) failures returned from an OAuth 2.0 Resource Server will\n\t * be forwarded to a {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler},\n\t * which will potentially remove the {@link OAuth2AuthorizedClient} from the given\n\t * {@link OAuth2AuthorizedClientRepository}, depending on the OAuth 2.0 error code\n\t * returned. Authentication failures returned from an OAuth 2.0 Resource Server\n\t * typically indicate that the token is invalid, and should not be used in future\n\t * requests. Removing the authorized client from the repository will ensure that the\n\t * existing token will not be sent for future requests to the Resource Server, and a\n\t * new token is retrieved from the Authorization Server and used for future requests\n\t * to the Resource Server.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientRepository the repository of authorized clients\n\t */\n\tpublic ServletOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\tClientRegistrationRepository clientRegistrationRepository,\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tOAuth2AuthorizationFailureHandler authorizationFailureHandler = new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> removeAuthorizedClient(authorizedClientRepository,\n\t\t\t\t\t\tclientRegistrationId, principal, attributes));\n\t\tDefaultOAuth2AuthorizedClientManager defaultAuthorizedClientManager = new DefaultOAuth2AuthorizedClientManager(\n\t\t\t\tclientRegistrationRepository, authorizedClientRepository);\n\t\tdefaultAuthorizedClientManager.setAuthorizationFailureHandler(authorizationFailureHandler);\n\t\tthis.authorizedClientManager = defaultAuthorizedClientManager;\n\t\tthis.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);\n\t}\n\n\tprivate void removeAuthorizedClient(OAuth2AuthorizedClientRepository authorizedClientRepository,\n\t\t\tString clientRegistrationId, Authentication principal, Map<String, Object> attributes) {\n\t\tHttpServletRequest request = getRequest(attributes);\n\t\tHttpServletResponse response = getResponse(attributes);\n\t\tif (request != null && response != null) {\n\t\t\tauthorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal, request, response);\n\t\t}\n\t}\n\n\t/**\n\t * If true, a default {@link OAuth2AuthorizedClient} can be discovered from the\n\t * current Authentication. It is recommended to be cautious with this feature since\n\t * all HTTP requests will receive the access token if it can be resolved from the\n\t * current Authentication.\n\t * @param defaultOAuth2AuthorizedClient true if a default\n\t * {@link OAuth2AuthorizedClient} should be used, else false. Default is false.\n\t */\n\tpublic void setDefaultOAuth2AuthorizedClient(boolean defaultOAuth2AuthorizedClient) {\n\t\tthis.defaultOAuth2AuthorizedClient = defaultOAuth2AuthorizedClient;\n\t}\n\n\t/**\n\t * If set, will be used as the default {@link ClientRegistration#getRegistrationId()}.\n\t * It is recommended to be cautious with this feature since all HTTP requests will\n\t * receive the access token.\n\t * @param clientRegistrationId the id to use\n\t */\n\tpublic void setDefaultClientRegistrationId(String clientRegistrationId) {\n\t\tthis.defaultClientRegistrationId = clientRegistrationId;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Configures the builder with {@link #defaultRequest()} and adds this as a\n\t * {@link ExchangeFilterFunction}\n\t * @return the {@link Consumer} to configure the builder\n\t */\n\tpublic Consumer<WebClient.Builder> oauth2Configuration() {\n\t\treturn (builder) -> builder.defaultRequest(defaultRequest()).filter(this);\n\t}\n\n\t/**\n\t * Provides defaults for the {@link HttpServletRequest} and the\n\t * {@link HttpServletResponse} using {@link RequestContextHolder}. It also provides\n\t * defaults for the {@link Authentication} using {@link SecurityContextHolder}. It\n\t * also can default the {@link OAuth2AuthorizedClient} using the\n\t * {@link #clientRegistrationId(String)} or the\n\t * {@link #authentication(Authentication)}.\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic Consumer<WebClient.RequestHeadersSpec<?>> defaultRequest() {\n\t\treturn (spec) -> spec.attributes((attrs) -> {\n\t\t\tpopulateDefaultRequestResponse(attrs);\n\t\t\tpopulateDefaultAuthentication(attrs);\n\t\t});\n\t}\n\n\t/**\n\t * Modifies the {@link ClientRequest#attributes()} to include the\n\t * {@link OAuth2AuthorizedClient} to be used for providing the Bearer Token.\n\t * @param authorizedClient the {@link OAuth2AuthorizedClient} to use.\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> oauth2AuthorizedClient(OAuth2AuthorizedClient authorizedClient) {\n\t\treturn (attributes) -> {\n\t\t\tif (authorizedClient == null) {\n\t\t\t\tattributes.remove(OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tattributes.put(OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME, authorizedClient);\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Modifies the {@link ClientRequest#attributes()} to include the\n\t * {@link ClientRegistration#getRegistrationId()} to be used to look up the\n\t * {@link OAuth2AuthorizedClient}.\n\t * @param clientRegistrationId the {@link ClientRegistration#getRegistrationId()} to\n\t * be used to look up the {@link OAuth2AuthorizedClient}.\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> clientRegistrationId(String clientRegistrationId) {\n\t\treturn ClientAttributes.clientRegistrationId(clientRegistrationId);\n\t}\n\n\t/**\n\t * Modifies the {@link ClientRequest#attributes()} to include the\n\t * {@link Authentication} used to look up and save the {@link OAuth2AuthorizedClient}.\n\t * The value is defaulted in\n\t * {@link ServletOAuth2AuthorizedClientExchangeFilterFunction#defaultRequest()}\n\t * @param authentication the {@link Authentication} to use.\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> authentication(Authentication authentication) {\n\t\treturn (attributes) -> attributes.put(AUTHENTICATION_ATTR_NAME, authentication);\n\t}\n\n\t/**\n\t * Modifies the {@link ClientRequest#attributes()} to include the\n\t * {@link HttpServletRequest} used to look up and save the\n\t * {@link OAuth2AuthorizedClient}. The value is defaulted in\n\t * {@link ServletOAuth2AuthorizedClientExchangeFilterFunction#defaultRequest()}\n\t * @param request the {@link HttpServletRequest} to use.\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> httpServletRequest(HttpServletRequest request) {\n\t\treturn (attributes) -> attributes.put(HTTP_SERVLET_REQUEST_ATTR_NAME, request);\n\t}\n\n\t/**\n\t * Modifies the {@link ClientRequest#attributes()} to include the\n\t * {@link HttpServletResponse} used to save the {@link OAuth2AuthorizedClient}. The\n\t * value is defaulted in\n\t * {@link ServletOAuth2AuthorizedClientExchangeFilterFunction#defaultRequest()}\n\t * @param response the {@link HttpServletResponse} to use.\n\t * @return the {@link Consumer} to populate the attributes\n\t */\n\tpublic static Consumer<Map<String, Object>> httpServletResponse(HttpServletResponse response) {\n\t\treturn (attributes) -> attributes.put(HTTP_SERVLET_RESPONSE_ATTR_NAME, response);\n\t}\n\n\t/**\n\t * Sets the {@link OAuth2AuthorizationFailureHandler} that handles authentication and\n\t * authorization failures when communicating to the OAuth 2.0 Resource Server.\n\t *\n\t * <p>\n\t * For example, a {@link RemoveAuthorizedClientOAuth2AuthorizationFailureHandler} is\n\t * typically used to remove the cached {@link OAuth2AuthorizedClient}, so that the\n\t * same token is no longer used in future requests to the Resource Server.\n\t *\n\t * <p>\n\t * The failure handler used by default depends on which constructor was used to\n\t * construct this {@link ServletOAuth2AuthorizedClientExchangeFilterFunction}. See the\n\t * constructors for more details.\n\t * @param authorizationFailureHandler the {@link OAuth2AuthorizationFailureHandler}\n\t * that handles authentication and authorization failures\n\t * @since 5.3\n\t */\n\tpublic void setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\tthis.clientResponseHandler = new AuthorizationFailureForwarder(authorizationFailureHandler);\n\t}\n\n\t@Override\n\tpublic Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {\n\t\t// @formatter:off\n\t\treturn mergeRequestAttributesIfNecessary(request)\n\t\t\t\t.filter((req) -> req.attribute(OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME).isPresent())\n\t\t\t\t.flatMap((req) -> {\n\t\t\t\t\tOAuth2AuthorizedClient authorizedClient = getOAuth2AuthorizedClient(req.attributes());\n\t\t\t\t\treturn (authorizedClient != null) ? reauthorizeClient(authorizedClient, req) : Mono.empty();\n\t\t\t\t})\n\t\t\t\t.switchIfEmpty(\n\t\t\t\t\t\tMono.defer(() ->\n\t\t\t\t\t\t\tmergeRequestAttributesIfNecessary(request)\n\t\t\t\t\t\t\t\t.flatMap((req) -> {\n\t\t\t\t\t\t\t\t\tString clientRegistrationId = resolveClientRegistrationId(req);\n\t\t\t\t\t\t\t\t\treturn (clientRegistrationId != null) ? authorizeClient(clientRegistrationId, req) : Mono.empty();\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t.map((authorizedClient) -> bearer(request, authorizedClient))\n\t\t\t\t.flatMap((requestWithBearer) -> exchangeAndHandleResponse(requestWithBearer, next))\n\t\t\t\t.switchIfEmpty(Mono.defer(() -> exchangeAndHandleResponse(request, next)));\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<ClientResponse> exchangeAndHandleResponse(ClientRequest request, ExchangeFunction next) {\n\t\treturn next.exchange(request).transform((responseMono) -> {\n\t\t\tif (this.clientResponseHandler != null) {\n\t\t\t\treturn this.clientResponseHandler.handleResponse(request, responseMono);\n\t\t\t}\n\t\t\treturn responseMono;\n\t\t});\n\t}\n\n\tprivate Mono<ClientRequest> mergeRequestAttributesIfNecessary(ClientRequest request) {\n\t\tif (request.attribute(HTTP_SERVLET_REQUEST_ATTR_NAME).isEmpty()\n\t\t\t\t|| request.attribute(HTTP_SERVLET_RESPONSE_ATTR_NAME).isEmpty()\n\t\t\t\t|| request.attribute(AUTHENTICATION_ATTR_NAME).isEmpty()) {\n\t\t\treturn mergeRequestAttributesFromContext(request);\n\t\t}\n\t\treturn Mono.just(request);\n\t}\n\n\tprivate Mono<ClientRequest> mergeRequestAttributesFromContext(ClientRequest request) {\n\t\tClientRequest.Builder builder = ClientRequest.from(request);\n\t\treturn Mono.deferContextual(Mono::just)\n\t\t\t.cast(Context.class)\n\t\t\t.map((ctx) -> builder.attributes((attrs) -> populateRequestAttributes(attrs, ctx)))\n\t\t\t.map(ClientRequest.Builder::build);\n\t}\n\n\tprivate void populateRequestAttributes(Map<String, Object> attrs, Context ctx) {\n\t\t// NOTE: SecurityReactorContextConfiguration.SecurityReactorContextSubscriber adds\n\t\t// this key\n\t\tif (!ctx.hasKey(SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY)) {\n\t\t\treturn;\n\t\t}\n\t\tMap<Object, Object> contextAttributes = ctx.get(SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY);\n\t\tHttpServletRequest servletRequest = (HttpServletRequest) contextAttributes.get(HttpServletRequest.class);\n\t\tif (servletRequest != null) {\n\t\t\tattrs.putIfAbsent(HTTP_SERVLET_REQUEST_ATTR_NAME, servletRequest);\n\t\t}\n\t\tHttpServletResponse servletResponse = (HttpServletResponse) contextAttributes.get(HttpServletResponse.class);\n\t\tif (servletResponse != null) {\n\t\t\tattrs.putIfAbsent(HTTP_SERVLET_RESPONSE_ATTR_NAME, servletResponse);\n\t\t}\n\t\tAuthentication authentication = (Authentication) contextAttributes.get(Authentication.class);\n\t\tif (authentication != null) {\n\t\t\tattrs.putIfAbsent(AUTHENTICATION_ATTR_NAME, authentication);\n\t\t}\n\t}\n\n\tprivate void populateDefaultRequestResponse(Map<String, Object> attrs) {\n\t\tif (attrs.containsKey(HTTP_SERVLET_REQUEST_ATTR_NAME) && attrs.containsKey(HTTP_SERVLET_RESPONSE_ATTR_NAME)) {\n\t\t\treturn;\n\t\t}\n\t\tRequestAttributes context = RequestContextHolder.getRequestAttributes();\n\t\tif (context instanceof ServletRequestAttributes) {\n\t\t\tattrs.putIfAbsent(HTTP_SERVLET_REQUEST_ATTR_NAME, ((ServletRequestAttributes) context).getRequest());\n\t\t\tattrs.putIfAbsent(HTTP_SERVLET_RESPONSE_ATTR_NAME, ((ServletRequestAttributes) context).getResponse());\n\t\t}\n\t}\n\n\tprivate void populateDefaultAuthentication(Map<String, Object> attrs) {\n\t\tif (attrs.containsKey(AUTHENTICATION_ATTR_NAME)) {\n\t\t\treturn;\n\t\t}\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tattrs.putIfAbsent(AUTHENTICATION_ATTR_NAME, authentication);\n\t}\n\n\tprivate @Nullable String resolveClientRegistrationId(ClientRequest request) {\n\t\tMap<String, Object> attrs = request.attributes();\n\t\tString clientRegistrationId = getClientRegistrationId(attrs);\n\t\tif (clientRegistrationId == null) {\n\t\t\tclientRegistrationId = this.defaultClientRegistrationId;\n\t\t}\n\t\tAuthentication authentication = getAuthentication(attrs);\n\t\tif (clientRegistrationId == null && this.defaultOAuth2AuthorizedClient\n\t\t\t\t&& authentication instanceof OAuth2AuthenticationToken) {\n\t\t\tclientRegistrationId = ((OAuth2AuthenticationToken) authentication).getAuthorizedClientRegistrationId();\n\t\t}\n\t\treturn clientRegistrationId;\n\t}\n\n\tprivate Mono<OAuth2AuthorizedClient> authorizeClient(String clientRegistrationId, ClientRequest request) {\n\t\tOAuth2AuthorizedClientManager authorizedClientManager = this.authorizedClientManager;\n\t\tif (authorizedClientManager == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tMap<String, Object> attrs = request.attributes();\n\t\tAuthentication authentication = getAuthentication(attrs);\n\t\tif (authentication == null) {\n\t\t\tauthentication = ANONYMOUS_AUTHENTICATION;\n\t\t}\n\t\tHttpServletRequest servletRequest = getRequest(attrs);\n\t\tHttpServletResponse servletResponse = getResponse(attrs);\n\t\tif (servletRequest == null || servletResponse == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tOAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)\n\t\t\t.principal(authentication);\n\t\tbuilder.attributes((attributes) -> addToAttributes(attributes, servletRequest, servletResponse));\n\t\tOAuth2AuthorizeRequest authorizeRequest = builder.build();\n\t\t// NOTE: 'authorizedClientManager.authorize()' needs to be executed on a dedicated\n\t\t// thread via subscribeOn(Schedulers.boundedElastic()) since it performs a\n\t\t// blocking I/O operation using RestClient internally\n\t\treturn Mono.fromSupplier(() -> authorizedClientManager.authorize(authorizeRequest))\n\t\t\t.subscribeOn(Schedulers.boundedElastic());\n\t}\n\n\tprivate Mono<OAuth2AuthorizedClient> reauthorizeClient(OAuth2AuthorizedClient authorizedClient,\n\t\t\tClientRequest request) {\n\t\tOAuth2AuthorizedClientManager authorizedClientManager = this.authorizedClientManager;\n\t\tif (authorizedClientManager == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tMap<String, Object> attrs = request.attributes();\n\t\tAuthentication authentication = getAuthentication(attrs);\n\t\tif (authentication == null) {\n\t\t\tauthentication = createAuthentication(authorizedClient.getPrincipalName());\n\t\t}\n\t\tHttpServletRequest servletRequest = getRequest(attrs);\n\t\tHttpServletResponse servletResponse = getResponse(attrs);\n\t\tif (servletRequest == null || servletResponse == null) {\n\t\t\treturn Mono.just(authorizedClient);\n\t\t}\n\t\tOAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withAuthorizedClient(authorizedClient)\n\t\t\t.principal(authentication);\n\t\tbuilder.attributes((attributes) -> addToAttributes(attributes, servletRequest, servletResponse));\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = builder.build();\n\t\t// NOTE: 'authorizedClientManager.authorize()' needs to be executed on a dedicated\n\t\t// thread via subscribeOn(Schedulers.boundedElastic()) since it performs a\n\t\t// blocking I/O operation using RestClient internally\n\t\treturn Mono.fromSupplier(() -> authorizedClientManager.authorize(reauthorizeRequest))\n\t\t\t.subscribeOn(Schedulers.boundedElastic());\n\t}\n\n\tprivate void addToAttributes(Map<String, Object> attributes, HttpServletRequest servletRequest,\n\t\t\tHttpServletResponse servletResponse) {\n\t\tif (servletRequest != null) {\n\t\t\tattributes.put(HTTP_SERVLET_REQUEST_ATTR_NAME, servletRequest);\n\t\t}\n\t\tif (servletResponse != null) {\n\t\t\tattributes.put(HTTP_SERVLET_RESPONSE_ATTR_NAME, servletResponse);\n\t\t}\n\t}\n\n\tprivate ClientRequest bearer(ClientRequest request, OAuth2AuthorizedClient authorizedClient) {\n\t\t// @formatter:off\n\t\treturn ClientRequest.from(request)\n\t\t\t\t.headers((headers) -> headers.setBearerAuth(authorizedClient.getAccessToken().getTokenValue()))\n\t\t\t\t.attributes(oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tstatic @Nullable OAuth2AuthorizedClient getOAuth2AuthorizedClient(Map<String, Object> attrs) {\n\t\treturn (OAuth2AuthorizedClient) attrs.get(OAUTH2_AUTHORIZED_CLIENT_ATTR_NAME);\n\t}\n\n\tstatic @Nullable String getClientRegistrationId(Map<String, Object> attrs) {\n\t\treturn ClientAttributes.resolveClientRegistrationId(attrs);\n\t}\n\n\tstatic @Nullable Authentication getAuthentication(Map<String, Object> attrs) {\n\t\treturn (Authentication) attrs.get(AUTHENTICATION_ATTR_NAME);\n\t}\n\n\tstatic @Nullable HttpServletRequest getRequest(Map<String, Object> attrs) {\n\t\treturn (HttpServletRequest) attrs.get(HTTP_SERVLET_REQUEST_ATTR_NAME);\n\t}\n\n\tstatic @Nullable HttpServletResponse getResponse(Map<String, Object> attrs) {\n\t\treturn (HttpServletResponse) attrs.get(HTTP_SERVLET_RESPONSE_ATTR_NAME);\n\t}\n\n\tprivate static Authentication createAuthentication(final String principalName) {\n\t\tAssert.hasText(principalName, \"principalName cannot be empty\");\n\t\treturn new AbstractAuthenticationToken((Collection<? extends GrantedAuthority>) null) {\n\n\t\t\t@Override\n\t\t\tpublic Object getCredentials() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object getPrincipal() {\n\t\t\t\treturn principalName;\n\t\t\t}\n\t\t};\n\t}\n\n\t@FunctionalInterface\n\tprivate interface ClientResponseHandler {\n\n\t\tMono<ClientResponse> handleResponse(ClientRequest request, Mono<ClientResponse> response);\n\n\t}\n\n\t/**\n\t * Forwards authentication and authorization failures to an\n\t * {@link OAuth2AuthorizationFailureHandler}.\n\t *\n\t * @since 5.3\n\t */\n\tprivate static final class AuthorizationFailureForwarder implements ClientResponseHandler {\n\n\t\t/**\n\t\t * A map of HTTP status code to OAuth 2.0 error code for HTTP status codes that\n\t\t * should be interpreted as authentication or authorization failures.\n\t\t */\n\t\tprivate final Map<HttpStatusCode, String> httpStatusToOAuth2ErrorCodeMap;\n\n\t\t/**\n\t\t * The {@link OAuth2AuthorizationFailureHandler} to notify when an\n\t\t * authentication/authorization failure occurs.\n\t\t */\n\t\tprivate final OAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t\tprivate AuthorizationFailureForwarder(OAuth2AuthorizationFailureHandler authorizationFailureHandler) {\n\t\t\tAssert.notNull(authorizationFailureHandler, \"authorizationFailureHandler cannot be null\");\n\t\t\tthis.authorizationFailureHandler = authorizationFailureHandler;\n\t\t\tMap<HttpStatusCode, String> httpStatusToOAuth2Error = new HashMap<>();\n\t\t\thttpStatusToOAuth2Error.put(HttpStatus.UNAUTHORIZED, OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\thttpStatusToOAuth2Error.put(HttpStatus.FORBIDDEN, OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t\tthis.httpStatusToOAuth2ErrorCodeMap = Collections.unmodifiableMap(httpStatusToOAuth2Error);\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<ClientResponse> handleResponse(ClientRequest request, Mono<ClientResponse> responseMono) {\n\t\t\treturn responseMono.flatMap((response) -> handleResponse(request, response).thenReturn(response))\n\t\t\t\t.onErrorResume(WebClientResponseException.class,\n\t\t\t\t\t\t(e) -> handleWebClientResponseException(request, e).then(Mono.error(e)))\n\t\t\t\t.onErrorResume(OAuth2AuthorizationException.class,\n\t\t\t\t\t\t(e) -> handleAuthorizationException(request, e).then(Mono.error(e)));\n\t\t}\n\n\t\tprivate Mono<Void> handleResponse(ClientRequest request, ClientResponse response) {\n\t\t\t// @formatter:off\n\t\t\treturn Mono.justOrEmpty(resolveErrorIfPossible(response))\n\t\t\t\t\t.flatMap((oauth2Error) -> {\n\t\t\t\t\t\tMap<String, Object> attrs = request.attributes();\n\t\t\t\t\t\tOAuth2AuthorizedClient authorizedClient = getOAuth2AuthorizedClient(attrs);\n\t\t\t\t\t\tif (authorizedClient == null) {\n\t\t\t\t\t\t\treturn Mono.empty();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tClientAuthorizationException authorizationException = new ClientAuthorizationException(oauth2Error,\n\t\t\t\t\t\t\t\tauthorizedClient.getClientRegistration().getRegistrationId());\n\t\t\t\t\t\tAuthentication principal = createAuthentication(authorizedClient.getPrincipalName());\n\t\t\t\t\t\tHttpServletRequest servletRequest = getRequest(attrs);\n\t\t\t\t\t\tHttpServletResponse servletResponse = getResponse(attrs);\n\t\t\t\t\t\treturn handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);\n\t\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate @Nullable OAuth2Error resolveErrorIfPossible(ClientResponse response) {\n\t\t\t// Try to resolve from 'WWW-Authenticate' header\n\t\t\tif (!response.headers().header(HttpHeaders.WWW_AUTHENTICATE).isEmpty()) {\n\t\t\t\tString wwwAuthenticateHeader = response.headers().header(HttpHeaders.WWW_AUTHENTICATE).get(0);\n\t\t\t\tMap<String, String> authParameters = parseAuthParameters(wwwAuthenticateHeader);\n\t\t\t\tif (authParameters.containsKey(OAuth2ParameterNames.ERROR)) {\n\t\t\t\t\treturn new OAuth2Error(authParameters.get(OAuth2ParameterNames.ERROR),\n\t\t\t\t\t\t\tauthParameters.get(OAuth2ParameterNames.ERROR_DESCRIPTION),\n\t\t\t\t\t\t\tauthParameters.get(OAuth2ParameterNames.ERROR_URI));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn resolveErrorIfPossible(response.statusCode());\n\t\t}\n\n\t\tprivate @Nullable OAuth2Error resolveErrorIfPossible(HttpStatusCode statusCode) {\n\t\t\tif (this.httpStatusToOAuth2ErrorCodeMap.containsKey(statusCode)) {\n\t\t\t\treturn new OAuth2Error(this.httpStatusToOAuth2ErrorCodeMap.get(statusCode), null,\n\t\t\t\t\t\t\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate Map<String, String> parseAuthParameters(String wwwAuthenticateHeader) {\n\t\t\t// @formatter:off\n\t\t\treturn Stream.of(wwwAuthenticateHeader).filter((header) -> StringUtils.hasLength(header))\n\t\t\t\t\t.filter((header) -> header.toLowerCase(Locale.ENGLISH).startsWith(\"bearer\"))\n\t\t\t\t\t.map((header) -> header.substring(\"bearer\".length()))\n\t\t\t\t\t.map((header) -> header.split(\",\"))\n\t\t\t\t\t.flatMap(Stream::of)\n\t\t\t\t\t.map((parameter) -> parameter.split(\"=\"))\n\t\t\t\t\t.filter((parameter) -> parameter.length > 1)\n\t\t\t\t\t.collect(Collectors.toMap((parameters) -> parameters[0].trim(),\n\t\t\t\t\t\t\t(parameters) -> parameters[1].trim().replace(\"\\\"\", \"\"))\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t/**\n\t\t * Handles the given http status code returned from a resource server by notifying\n\t\t * the authorization failure handler if the http status code is in the\n\t\t * {@link #httpStatusToOAuth2ErrorCodeMap}.\n\t\t * @param request the request being processed\n\t\t * @param exception The root cause exception for the failure\n\t\t * @return a {@link Mono} that completes empty after the authorization failure\n\t\t * handler completes\n\t\t */\n\t\tprivate Mono<Void> handleWebClientResponseException(ClientRequest request,\n\t\t\t\tWebClientResponseException exception) {\n\t\t\treturn Mono.justOrEmpty(resolveErrorIfPossible(exception.getStatusCode())).flatMap((oauth2Error) -> {\n\t\t\t\tMap<String, Object> attrs = request.attributes();\n\t\t\t\tOAuth2AuthorizedClient authorizedClient = getOAuth2AuthorizedClient(attrs);\n\t\t\t\tif (authorizedClient == null) {\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t}\n\t\t\t\tClientAuthorizationException authorizationException = new ClientAuthorizationException(oauth2Error,\n\t\t\t\t\t\tauthorizedClient.getClientRegistration().getRegistrationId(), exception);\n\t\t\t\tAuthentication principal = createAuthentication(authorizedClient.getPrincipalName());\n\t\t\t\tHttpServletRequest servletRequest = getRequest(attrs);\n\t\t\t\tHttpServletResponse servletResponse = getResponse(attrs);\n\t\t\t\treturn handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Handles the given {@link OAuth2AuthorizationException} that occurred downstream\n\t\t * by notifying the authorization failure handler.\n\t\t * @param request the request being processed\n\t\t * @param authorizationException the authorization exception to include in the\n\t\t * failure event\n\t\t * @return a {@link Mono} that completes empty after the authorization failure\n\t\t * handler completes\n\t\t */\n\t\tprivate Mono<Void> handleAuthorizationException(ClientRequest request,\n\t\t\t\tOAuth2AuthorizationException authorizationException) {\n\t\t\treturn Mono.justOrEmpty(request).flatMap((req) -> {\n\t\t\t\tMap<String, Object> attrs = req.attributes();\n\t\t\t\tOAuth2AuthorizedClient authorizedClient = getOAuth2AuthorizedClient(attrs);\n\t\t\t\tif (authorizedClient == null) {\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t}\n\t\t\t\tAuthentication principal = createAuthentication(authorizedClient.getPrincipalName());\n\t\t\t\tHttpServletRequest servletRequest = getRequest(attrs);\n\t\t\t\tHttpServletResponse servletResponse = getResponse(attrs);\n\t\t\t\treturn handleAuthorizationFailure(authorizationException, principal, servletRequest, servletResponse);\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Delegates the failed authorization to the\n\t\t * {@link OAuth2AuthorizationFailureHandler}.\n\t\t * @param exception the {@link OAuth2AuthorizationException} to include in the\n\t\t * failure event\n\t\t * @param principal the principal associated with the failed authorization attempt\n\t\t * @param servletRequest the currently active {@code HttpServletRequest}\n\t\t * @param servletResponse the currently active {@code HttpServletResponse}\n\t\t * @return a {@link Mono} that completes empty after the\n\t\t * {@link OAuth2AuthorizationFailureHandler} completes\n\t\t */\n\t\tprivate Mono<Void> handleAuthorizationFailure(OAuth2AuthorizationException exception, Authentication principal,\n\t\t\t\t@Nullable HttpServletRequest servletRequest, @Nullable HttpServletResponse servletResponse) {\n\t\t\tRunnable runnable = () -> this.authorizationFailureHandler.onAuthorizationFailure(exception, principal,\n\t\t\t\t\tcreateAttributes(servletRequest, servletResponse));\n\t\t\t// @formatter:off\n\t\t\treturn Mono.fromRunnable(runnable)\n\t\t\t\t\t.subscribeOn(Schedulers.boundedElastic())\n\t\t\t\t\t.then();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate static Map<String, Object> createAttributes(@Nullable HttpServletRequest servletRequest,\n\t\t\t\t@Nullable HttpServletResponse servletResponse) {\n\t\t\tMap<String, Object> attributes = new HashMap<>();\n\t\t\tattributes.put(HttpServletRequest.class.getName(), servletRequest);\n\t\t\tattributes.put(HttpServletResponse.class.getName(), servletResponse);\n\t\t\treturn attributes;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Exchange filter functions for OAuth2 Client with WebClient.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web.reactive.function.client;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/support/OAuth2WebClientHttpServiceGroupConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client.support;\n\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor;\nimport org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;\nimport org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer;\nimport org.springframework.web.service.invoker.HttpRequestValues;\n\n/**\n * Simplify adding OAuth2 support to interface based rest clients that use\n * {@link WebClient}.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic final class OAuth2WebClientHttpServiceGroupConfigurer implements WebClientHttpServiceGroupConfigurer {\n\n\tprivate final HttpRequestValues.Processor processor = ClientRegistrationIdProcessor.DEFAULT_INSTANCE;\n\n\tprivate final ExchangeFilterFunction filter;\n\n\tprivate OAuth2WebClientHttpServiceGroupConfigurer(ExchangeFilterFunction filter) {\n\t\tthis.filter = filter;\n\t}\n\n\t@Override\n\tpublic void configureGroups(Groups<WebClient.Builder> groups) {\n\t\t// @formatter:off\n\t\tgroups.forEachClient((group, client) ->\n\t\t\tclient.filter(this.filter)\n\t\t);\n\t\tgroups.forEachProxyFactory((group, factory) ->\n\t\t\tfactory.httpRequestValuesProcessor(this.processor)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Create an instance for Reactive web applications from the provided\n\t * {@link ReactiveOAuth2AuthorizedClientManager}.\n\t *\n\t * It will add {@link ServerOAuth2AuthorizedClientExchangeFilterFunction} to the\n\t * {@link WebClient} and {@link ClientRegistrationIdProcessor} to the\n\t * {@link org.springframework.web.service.invoker.HttpServiceProxyFactory}.\n\t * @param authorizedClientManager the manager to use.\n\t * @return the {@link OAuth2WebClientHttpServiceGroupConfigurer}.\n\t */\n\tpublic static OAuth2WebClientHttpServiceGroupConfigurer from(\n\t\t\tReactiveOAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tServerOAuth2AuthorizedClientExchangeFilterFunction filter = new ServerOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\t\tauthorizedClientManager);\n\t\treturn new OAuth2WebClientHttpServiceGroupConfigurer(filter);\n\t}\n\n\t/**\n\t * Create an instance for Servlet based environments from the provided\n\t * {@link OAuth2AuthorizedClientManager}.\n\t *\n\t * It will add {@link ServletOAuth2AuthorizedClientExchangeFilterFunction} to the\n\t * {@link WebClient} and {@link ClientRegistrationIdProcessor} to the\n\t * {@link org.springframework.web.service.invoker.HttpServiceProxyFactory}.\n\t * @param authorizedClientManager the manager to use.\n\t * @return the {@link OAuth2WebClientHttpServiceGroupConfigurer}.\n\t */\n\tpublic static OAuth2WebClientHttpServiceGroupConfigurer from(\n\t\t\tOAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tServletOAuth2AuthorizedClientExchangeFilterFunction filter = new ServletOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\t\tauthorizedClientManager);\n\t\treturn new OAuth2WebClientHttpServiceGroupConfigurer(filter);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/support/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes (reactive) for OAuth2 Client with WebClient.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web.reactive.function.client.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/result/method/annotation/OAuth2AuthorizedClientArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.result.method.annotation;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.BindingContext;\nimport org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of a {@link HandlerMethodArgumentResolver} that is capable of\n * resolving a method parameter to an argument value of type\n * {@link OAuth2AuthorizedClient}.\n *\n * <p>\n * For example: <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;GetMapping(\"/authorized-client\")\n *     public Mono&lt;String&gt; authorizedClient(@RegisteredOAuth2AuthorizedClient(\"login-client\") OAuth2AuthorizedClient authorizedClient) {\n *         // do something with authorizedClient\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @author Joe Grandja\n * @since 5.1\n * @see RegisteredOAuth2AuthorizedClient\n */\npublic final class OAuth2AuthorizedClientArgumentResolver implements HandlerMethodArgumentResolver {\n\n\tprivate static final AnonymousAuthenticationToken ANONYMOUS_USER_TOKEN = new AnonymousAuthenticationToken(\n\t\t\t\"anonymous\", \"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\n\tprivate ReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizedClientArgumentResolver} using the provided\n\t * parameters.\n\t * @param authorizedClientManager the {@link ReactiveOAuth2AuthorizedClientManager}\n\t * which manages the authorized client(s)\n\t * @since 5.2\n\t */\n\tpublic OAuth2AuthorizedClientArgumentResolver(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {\n\t\tAssert.notNull(authorizedClientManager, \"authorizedClientManager cannot be null\");\n\t\tthis.authorizedClientManager = authorizedClientManager;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizedClientArgumentResolver} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t * @param authorizedClientRepository the repository of authorized clients\n\t */\n\tpublic OAuth2AuthorizedClientArgumentResolver(ReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository,\n\t\t\t\tauthorizedClientRepository);\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn AnnotatedElementUtils.findMergedAnnotation(parameter.getParameter(),\n\t\t\t\tRegisteredOAuth2AuthorizedClient.class) != null;\n\t}\n\n\t@Override\n\tpublic Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,\n\t\t\tServerWebExchange exchange) {\n\t\treturn Mono.defer(() -> {\n\t\t\tRegisteredOAuth2AuthorizedClient authorizedClientAnnotation = AnnotatedElementUtils\n\t\t\t\t.findMergedAnnotation(parameter.getParameter(), RegisteredOAuth2AuthorizedClient.class);\n\t\t\tif (authorizedClientAnnotation == null) {\n\t\t\t\treturn Mono.empty();\n\t\t\t}\n\t\t\tString clientRegistrationId = StringUtils.hasLength(authorizedClientAnnotation.registrationId())\n\t\t\t\t\t? authorizedClientAnnotation.registrationId() : null;\n\t\t\treturn authorizeRequest(clientRegistrationId, exchange).flatMap(this.authorizedClientManager::authorize);\n\t\t});\n\t}\n\n\tprivate Mono<OAuth2AuthorizeRequest> authorizeRequest(@Nullable String registrationId, ServerWebExchange exchange) {\n\t\tMono<Authentication> defaultedAuthentication = currentAuthentication();\n\t\tMono<String> defaultedRegistrationId = Mono.justOrEmpty(registrationId)\n\t\t\t.switchIfEmpty(clientRegistrationId(defaultedAuthentication))\n\t\t\t.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(\n\t\t\t\t\t\"The clientRegistrationId could not be resolved. Please provide one\")));\n\t\tMono<ServerWebExchange> defaultedExchange = Mono.justOrEmpty(exchange)\n\t\t\t.switchIfEmpty(currentServerWebExchange());\n\t\treturn Mono.zip(defaultedRegistrationId, defaultedAuthentication, defaultedExchange)\n\t\t\t.map((zipped) -> OAuth2AuthorizeRequest.withClientRegistrationId(zipped.getT1())\n\t\t\t\t.principal(zipped.getT2())\n\t\t\t\t.attribute(ServerWebExchange.class.getName(), zipped.getT3())\n\t\t\t\t.build());\n\t}\n\n\tprivate Mono<Authentication> currentAuthentication() {\n\t\t// @formatter:off\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.flatMap((ctx) -> Mono.justOrEmpty(ctx.getAuthentication()))\n\t\t\t\t.defaultIfEmpty(ANONYMOUS_USER_TOKEN);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<String> clientRegistrationId(Mono<Authentication> authentication) {\n\t\treturn authentication.filter((t) -> t instanceof OAuth2AuthenticationToken)\n\t\t\t.cast(OAuth2AuthenticationToken.class)\n\t\t\t.map(OAuth2AuthenticationToken::getAuthorizedClientRegistrationId);\n\t}\n\n\tprivate Mono<ServerWebExchange> currentServerWebExchange() {\n\t\t// @formatter:off\n\t\treturn Mono.deferContextual(Mono::just)\n\t\t\t\t.filter((c) -> c.hasKey(ServerWebExchange.class))\n\t\t\t\t.map((c) -> c.get(ServerWebExchange.class));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/result/method/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Method annotation support (reactive) for OAuth2 client.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web.reactive.result.method.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of an {@link ServerOAuth2AuthorizedClientRepository} that delegates\n * to the provided {@link ServerOAuth2AuthorizedClientRepository} if the current\n * {@code Principal} is authenticated, otherwise, to the default (or provided)\n * {@link ServerOAuth2AuthorizedClientRepository} if the current request is\n * unauthenticated (or anonymous). The default\n * {@code ReactiveOAuth2AuthorizedClientRepository} is\n * {@link WebSessionServerOAuth2AuthorizedClientRepository}.\n *\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2AuthorizedClientRepository\n * @see OAuth2AuthorizedClient\n * @see OAuth2AuthorizedClientService\n * @see HttpSessionOAuth2AuthorizedClientRepository\n */\npublic final class AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository\n\t\timplements ServerOAuth2AuthorizedClientRepository {\n\n\tprivate final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate final ReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate ServerOAuth2AuthorizedClientRepository anonymousAuthorizedClientRepository = new WebSessionServerOAuth2AuthorizedClientRepository();\n\n\t/**\n\t * Creates an instance\n\t * @param authorizedClientService the authorized client service\n\t */\n\tpublic AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(\n\t\t\tReactiveOAuth2AuthorizedClientService authorizedClientService) {\n\t\tAssert.notNull(authorizedClientService, \"authorizedClientService cannot be null\");\n\t\tthis.authorizedClientService = authorizedClientService;\n\t}\n\n\t/**\n\t * Sets the {@link ServerOAuth2AuthorizedClientRepository} used for requests that are\n\t * unauthenticated (or anonymous). The default is\n\t * {@link WebSessionServerOAuth2AuthorizedClientRepository}.\n\t * @param anonymousAuthorizedClientRepository the repository used for requests that\n\t * are unauthenticated (or anonymous)\n\t */\n\tpublic void setAnonymousAuthorizedClientRepository(\n\t\t\tServerOAuth2AuthorizedClientRepository anonymousAuthorizedClientRepository) {\n\t\tAssert.notNull(anonymousAuthorizedClientRepository, \"anonymousAuthorizedClientRepository cannot be null\");\n\t\tthis.anonymousAuthorizedClientRepository = anonymousAuthorizedClientRepository;\n\t}\n\n\t@Override\n\tpublic <T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId,\n\t\t\tAuthentication principal, ServerWebExchange exchange) {\n\t\tif (this.isPrincipalAuthenticated(principal)) {\n\t\t\treturn this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, principal.getName());\n\t\t}\n\t\treturn this.anonymousAuthorizedClientRepository.loadAuthorizedClient(clientRegistrationId, principal, exchange);\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tServerWebExchange exchange) {\n\t\tif (this.isPrincipalAuthenticated(principal)) {\n\t\t\treturn this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);\n\t\t}\n\t\treturn this.anonymousAuthorizedClientRepository.saveAuthorizedClient(authorizedClient, principal, exchange);\n\t}\n\n\t@Override\n\tpublic Mono<Void> removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\tServerWebExchange exchange) {\n\t\tif (this.isPrincipalAuthenticated(principal)) {\n\t\t\treturn this.authorizedClientService.removeAuthorizedClient(clientRegistrationId, principal.getName());\n\t\t}\n\t\treturn this.anonymousAuthorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal,\n\t\t\t\texchange);\n\t}\n\n\tprivate boolean isPrincipalAuthenticated(Authentication authentication) {\n\t\treturn this.authenticationTrustResolver.isAuthenticated(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestCustomizers;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ResponseStatusException;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * The default implementation of {@link ServerOAuth2AuthorizationRequestResolver}.\n *\n * The {@link ClientRegistration#getRegistrationId()} is extracted from the request using\n * the {@link #DEFAULT_AUTHORIZATION_REQUEST_PATTERN}. The injected\n * {@link ReactiveClientRegistrationRepository} is then used to resolve the\n * {@link ClientRegistration} and create the {@link OAuth2AuthorizationRequest}.\n *\n * @author Rob Winch\n * @author Mark Heckler\n * @author Joe Grandja\n * @since 5.1\n */\npublic class DefaultServerOAuth2AuthorizationRequestResolver implements ServerOAuth2AuthorizationRequestResolver {\n\n\t/**\n\t * The name of the path variable that contains the\n\t * {@link ClientRegistration#getRegistrationId()}\n\t */\n\tpublic static final String DEFAULT_REGISTRATION_ID_URI_VARIABLE_NAME = \"registrationId\";\n\n\t/**\n\t * The default pattern used to resolve the\n\t * {@link ClientRegistration#getRegistrationId()}\n\t */\n\tpublic static final String DEFAULT_AUTHORIZATION_REQUEST_PATTERN = \"/oauth2/authorization/{\"\n\t\t\t+ DEFAULT_REGISTRATION_ID_URI_VARIABLE_NAME + \"}\";\n\n\tprivate static final char PATH_DELIMITER = '/';\n\n\tprivate static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder());\n\n\tprivate static final StringKeyGenerator DEFAULT_SECURE_KEY_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\tprivate static final Consumer<OAuth2AuthorizationRequest.Builder> DEFAULT_PKCE_APPLIER = OAuth2AuthorizationRequestCustomizers\n\t\t.withPkce();\n\n\tprivate final ServerWebExchangeMatcher authorizationRequestMatcher;\n\n\tprivate final ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer = (customizer) -> {\n\t};\n\n\t/**\n\t * Creates a new instance\n\t * @param clientRegistrationRepository the repository to resolve the\n\t * {@link ClientRegistration}\n\t */\n\tpublic DefaultServerOAuth2AuthorizationRequestResolver(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\tthis(clientRegistrationRepository,\n\t\t\t\tnew PathPatternParserServerWebExchangeMatcher(DEFAULT_AUTHORIZATION_REQUEST_PATTERN));\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param clientRegistrationRepository the repository to resolve the\n\t * {@link ClientRegistration}\n\t * @param authorizationRequestMatcher the matcher that determines if the request is a\n\t * match and extracts the {@link #DEFAULT_REGISTRATION_ID_URI_VARIABLE_NAME} from the\n\t * path variables.\n\t */\n\tpublic DefaultServerOAuth2AuthorizationRequestResolver(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tServerWebExchangeMatcher authorizationRequestMatcher) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizationRequestMatcher, \"authorizationRequestMatcher cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\tthis.authorizationRequestMatcher = authorizationRequestMatcher;\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AuthorizationRequest> resolve(ServerWebExchange exchange) {\n\t\t// @formatter:off\n\t\treturn this.authorizationRequestMatcher\n\t\t\t\t.matches(exchange)\n\t\t\t\t.filter((matchResult) -> matchResult.isMatch())\n\t\t\t\t.map(ServerWebExchangeMatcher.MatchResult::getVariables)\n\t\t\t\t.flatMap((variables) -> Mono\n\t\t\t\t\t.justOrEmpty((String) variables.get(DEFAULT_REGISTRATION_ID_URI_VARIABLE_NAME)))\n\t\t\t\t.flatMap((clientRegistrationId) -> resolve(exchange, clientRegistrationId));\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AuthorizationRequest> resolve(ServerWebExchange exchange, String clientRegistrationId) {\n\t\treturn findByRegistrationId(exchange, clientRegistrationId)\n\t\t\t.map((clientRegistration) -> authorizationRequest(exchange, clientRegistration));\n\t}\n\n\t/**\n\t * Sets the {@code Consumer} to be provided the\n\t * {@link OAuth2AuthorizationRequest.Builder} allowing for further customizations.\n\t * @param authorizationRequestCustomizer the {@code Consumer} to be provided the\n\t * {@link OAuth2AuthorizationRequest.Builder}\n\t * @since 5.3\n\t * @see OAuth2AuthorizationRequestCustomizers\n\t */\n\tpublic final void setAuthorizationRequestCustomizer(\n\t\t\tConsumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer) {\n\t\tAssert.notNull(authorizationRequestCustomizer, \"authorizationRequestCustomizer cannot be null\");\n\t\tthis.authorizationRequestCustomizer = authorizationRequestCustomizer;\n\t}\n\n\tprivate Mono<ClientRegistration> findByRegistrationId(ServerWebExchange exchange, String clientRegistration) {\n\t\t// @formatter:off\n\t\treturn this.clientRegistrationRepository.findByRegistrationId(clientRegistration)\n\t\t\t\t.switchIfEmpty(Mono.error(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, \"Invalid client registration id\")));\n\t\t// @formatter:on\n\t}\n\n\tprivate OAuth2AuthorizationRequest authorizationRequest(ServerWebExchange exchange,\n\t\t\tClientRegistration clientRegistration) {\n\t\tOAuth2AuthorizationRequest.Builder builder = getBuilder(clientRegistration);\n\t\tString redirectUriStr = expandRedirectUri(exchange.getRequest(), clientRegistration);\n\n\t\tString authorizationUri = clientRegistration.getProviderDetails().getAuthorizationUri();\n\t\tAssert.hasText(authorizationUri, \"Authorization URI is required\");\n\n\t\t// @formatter:off\n\t\tbuilder.clientId(clientRegistration.getClientId())\n\t\t\t\t.authorizationUri(authorizationUri)\n\t\t\t\t.redirectUri(redirectUriStr)\n\t\t\t\t.scopes(clientRegistration.getScopes())\n\t\t\t\t.state(DEFAULT_STATE_GENERATOR.generateKey());\n\t\t// @formatter:on\n\n\t\tthis.authorizationRequestCustomizer.accept(builder);\n\n\t\treturn builder.build();\n\t}\n\n\tprivate OAuth2AuthorizationRequest.Builder getBuilder(ClientRegistration clientRegistration) {\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationRequest.Builder builder = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t\t.attributes((attrs) ->\n\t\t\t\t\t\t\tattrs.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()));\n\t\t\t// @formatter:on\n\t\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())\n\t\t\t\t\t&& clientRegistration.getScopes().contains(OidcScopes.OPENID)) {\n\t\t\t\t// Section 3.1.2.1 Authentication Request -\n\t\t\t\t// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\n\t\t\t\t// scope\n\t\t\t\t// REQUIRED. OpenID Connect requests MUST contain the \"openid\" scope\n\t\t\t\t// value.\n\t\t\t\tapplyNonce(builder);\n\t\t\t}\n\t\t\tif (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())\n\t\t\t\t\t|| clientRegistration.getClientSettings().isRequireProofKey()) {\n\t\t\t\tDEFAULT_PKCE_APPLIER.accept(builder);\n\t\t\t}\n\t\t\treturn builder;\n\t\t}\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Invalid Authorization Grant Type (\" + clientRegistration.getAuthorizationGrantType().getValue()\n\t\t\t\t\t\t+ \") for Client Registration with Id: \" + clientRegistration.getRegistrationId());\n\t}\n\n\t/**\n\t * Expands the {@link ClientRegistration#getRedirectUri()} with following provided\n\t * variables:<br/>\n\t * - baseUrl (e.g. https://localhost/app) <br/>\n\t * - baseScheme (e.g. https) <br/>\n\t * - baseHost (e.g. localhost) <br/>\n\t * - basePort (e.g. :8080) <br/>\n\t * - basePath (e.g. /app) <br/>\n\t * - registrationId (e.g. google) <br/>\n\t * - action (e.g. login) <br/>\n\t * <p/>\n\t * Null variables are provided as empty strings.\n\t * <p/>\n\t * Default redirectUri is:\n\t * {@code org.springframework.security.config.oauth2.client.CommonOAuth2Provider#DEFAULT_REDIRECT_URL}\n\t * @return expanded URI\n\t */\n\tprivate static String expandRedirectUri(ServerHttpRequest request, ClientRegistration clientRegistration) {\n\t\tMap<String, String> uriVariables = new HashMap<>();\n\t\turiVariables.put(\"registrationId\", clientRegistration.getRegistrationId());\n\t\t// @formatter:off\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI())\n\t\t\t\t.replacePath(request.getPath().contextPath().value())\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.fragment(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tString scheme = uriComponents.getScheme();\n\t\turiVariables.put(\"baseScheme\", (scheme != null) ? scheme : \"\");\n\t\tString host = uriComponents.getHost();\n\t\turiVariables.put(\"baseHost\", (host != null) ? host : \"\");\n\t\t// following logic is based on HierarchicalUriComponents#toUriString()\n\t\tint port = uriComponents.getPort();\n\t\turiVariables.put(\"basePort\", (port == -1) ? \"\" : \":\" + port);\n\t\tString path = uriComponents.getPath();\n\t\tif (StringUtils.hasLength(path)) {\n\t\t\tif (path.charAt(0) != PATH_DELIMITER) {\n\t\t\t\tpath = PATH_DELIMITER + path;\n\t\t\t}\n\t\t}\n\t\turiVariables.put(\"basePath\", (path != null) ? path : \"\");\n\t\turiVariables.put(\"baseUrl\", uriComponents.toUriString());\n\t\tString action = \"\";\n\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {\n\t\t\taction = \"login\";\n\t\t}\n\t\turiVariables.put(\"action\", action);\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUriString(Objects.requireNonNull(clientRegistration.getRedirectUri()))\n\t\t\t\t.buildAndExpand(uriVariables)\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Creates nonce and its hash for use in OpenID Connect 1.0 Authentication Requests.\n\t * @param builder where the {@link OidcParameterNames#NONCE} and hash is stored for\n\t * the authentication request\n\t *\n\t * @since 5.2\n\t * @see <a target=\"_blank\" href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest\">3.1.2.1.\n\t * Authentication Request</a>\n\t */\n\tprivate static void applyNonce(OAuth2AuthorizationRequest.Builder builder) {\n\t\ttry {\n\t\t\tString nonce = DEFAULT_SECURE_KEY_GENERATOR.generateKey();\n\t\t\tString nonceHash = createHash(nonce);\n\t\t\tbuilder.attributes((attrs) -> attrs.put(OidcParameterNames.NONCE, nonce));\n\t\t\tbuilder.additionalParameters((params) -> params.put(OidcParameterNames.NONCE, nonceHash));\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t}\n\t}\n\n\tprivate static String createHash(String value) throws NoSuchAlgorithmException {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.net.URI;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Filter} for the OAuth 2.0 Authorization Code Grant, which handles the\n * processing of the OAuth 2.0 Authorization Response.\n *\n * <p>\n * The OAuth 2.0 Authorization Response is processed as follows:\n *\n * <ul>\n * <li>Assuming the End-User (Resource Owner) has granted access to the Client, the\n * Authorization Server will append the {@link OAuth2ParameterNames#CODE code} and\n * {@link OAuth2ParameterNames#STATE state} parameters to the\n * {@link OAuth2ParameterNames#REDIRECT_URI redirect_uri} (provided in the Authorization\n * Request) and redirect the End-User's user-agent back to this {@code Filter} (the\n * Client).</li>\n * <li>This {@code Filter} will then create an\n * {@link OAuth2AuthorizationCodeAuthenticationToken} with the\n * {@link OAuth2ParameterNames#CODE code} received and delegate it to the\n * {@link ReactiveAuthenticationManager} to authenticate.</li>\n * <li>Upon a successful authentication, an {@link OAuth2AuthorizedClient Authorized\n * Client} is created by associating the\n * {@link OAuth2AuthorizationCodeAuthenticationToken#getClientRegistration() client} to\n * the {@link OAuth2AuthorizationCodeAuthenticationToken#getAccessToken() access token}\n * and current {@code Principal} and saving it via the\n * {@link ServerOAuth2AuthorizedClientRepository}.</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Joe Grandja\n * @author Parikshit Dutta\n * @since 5.1\n * @see OAuth2AuthorizationCodeAuthenticationToken\n * @see org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeReactiveAuthenticationManager\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationResponse\n * @see AuthorizationRequestRepository\n * @see org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter\n * @see ReactiveClientRegistrationRepository\n * @see OAuth2AuthorizedClient\n * @see ServerOAuth2AuthorizedClientRepository\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.2\">Section 4.1.2 Authorization\n * Response</a>\n */\npublic class OAuth2AuthorizationCodeGrantWebFilter implements WebFilter {\n\n\tprivate final ReactiveAuthenticationManager authenticationManager;\n\n\tprivate final ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new WebSessionOAuth2ServerAuthorizationRequestRepository();\n\n\tprivate ServerAuthenticationSuccessHandler authenticationSuccessHandler;\n\n\tprivate ServerAuthenticationConverter authenticationConverter;\n\n\tprivate boolean defaultAuthenticationConverter;\n\n\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler;\n\n\tprivate ServerWebExchangeMatcher requiresAuthenticationMatcher;\n\n\tprivate ServerRequestCache requestCache = new WebSessionServerRequestCache();\n\n\tprivate AnonymousAuthenticationToken anonymousToken = new AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tpublic OAuth2AuthorizationCodeGrantWebFilter(ReactiveAuthenticationManager authenticationManager,\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\tthis.requiresAuthenticationMatcher = this::matchesAuthorizationResponse;\n\t\tServerOAuth2AuthorizationCodeAuthenticationTokenConverter authenticationConverter = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(\n\t\t\t\tclientRegistrationRepository);\n\t\tauthenticationConverter.setAuthorizationRequestRepository(this.authorizationRequestRepository);\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\tthis.defaultAuthenticationConverter = true;\n\t\tRedirectServerAuthenticationSuccessHandler authenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler();\n\t\tauthenticationSuccessHandler.setRequestCache(this.requestCache);\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t\tthis.authenticationFailureHandler = (webFilterExchange, exception) -> Mono.error(exception);\n\t}\n\n\tpublic OAuth2AuthorizationCodeGrantWebFilter(ReactiveAuthenticationManager authenticationManager,\n\t\t\tServerAuthenticationConverter authenticationConverter,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientRepository cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t\tthis.requiresAuthenticationMatcher = this::matchesAuthorizationResponse;\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\tRedirectServerAuthenticationSuccessHandler authenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler();\n\t\tauthenticationSuccessHandler.setRequestCache(this.requestCache);\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t\tthis.authenticationFailureHandler = (webFilterExchange, exception) -> Mono.error(exception);\n\t}\n\n\t/**\n\t * Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s. The\n\t * default is {@link WebSessionOAuth2ServerAuthorizationRequestRepository}.\n\t * @param authorizationRequestRepository the repository used for storing\n\t * {@link OAuth2AuthorizationRequest}'s\n\t * @since 5.2\n\t */\n\tpublic final void setAuthorizationRequestRepository(\n\t\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\tAssert.notNull(authorizationRequestRepository, \"authorizationRequestRepository cannot be null\");\n\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t\tupdateDefaultAuthenticationConverter();\n\t}\n\n\tprivate void updateDefaultAuthenticationConverter() {\n\t\tif (this.defaultAuthenticationConverter) {\n\t\t\t((ServerOAuth2AuthorizationCodeAuthenticationTokenConverter) this.authenticationConverter)\n\t\t\t\t.setAuthorizationRequestRepository(this.authorizationRequestRepository);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link ServerRequestCache} used for loading a previously saved request (if\n\t * available) and replaying it after completing the processing of the OAuth 2.0\n\t * Authorization Response.\n\t * @param requestCache the cache used for loading a previously saved request (if\n\t * available)\n\t * @since 5.4\n\t */\n\tpublic final void setRequestCache(ServerRequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t\tupdateDefaultAuthenticationSuccessHandler();\n\t}\n\n\tprivate void updateDefaultAuthenticationSuccessHandler() {\n\t\t((RedirectServerAuthenticationSuccessHandler) this.authenticationSuccessHandler)\n\t\t\t.setRequestCache(this.requestCache);\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t// @formatter:off\n\t\treturn this.requiresAuthenticationMatcher.matches(exchange)\n\t\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t\t.flatMap((matchResult) -> this.authenticationConverter.convert(exchange)\n\t\t\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t\t(ex) -> new OAuth2AuthenticationException(ex.getError(), ex.getError().toString())\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t\t.flatMap((token) -> authenticate(exchange, chain, token))\n\t\t\t\t.onErrorResume(AuthenticationException.class, (e) ->\n\t\t\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(new WebFilterExchange(exchange, chain), e)\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Void> authenticate(ServerWebExchange exchange, WebFilterChain chain, Authentication token) {\n\t\tWebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain);\n\t\treturn this.authenticationManager.authenticate(token)\n\t\t\t.onErrorMap(OAuth2AuthorizationException.class,\n\t\t\t\t\t(ex) -> new OAuth2AuthenticationException(ex.getError(), ex.getError().toString()))\n\t\t\t.switchIfEmpty(Mono\n\t\t\t\t.defer(() -> Mono.error(new IllegalStateException(\"No provider found for \" + token.getClass()))))\n\t\t\t.flatMap((authentication) -> onAuthenticationSuccess(authentication, webFilterExchange))\n\t\t\t.onErrorResume(AuthenticationException.class,\n\t\t\t\t\t(e) -> this.authenticationFailureHandler.onAuthenticationFailure(webFilterExchange, e));\n\t}\n\n\tprivate Mono<Void> onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) {\n\t\tOAuth2AuthorizationCodeAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeAuthenticationToken) authentication;\n\t\tAssert.notNull(authenticationResult.getAccessToken(), \"accessToken cannot be null\");\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(\n\t\t\t\tauthenticationResult.getClientRegistration(), authenticationResult.getName(),\n\t\t\t\tauthenticationResult.getAccessToken(), authenticationResult.getRefreshToken());\n\t\t// @formatter:off\n\t\treturn this.authenticationSuccessHandler.onAuthenticationSuccess(webFilterExchange, authentication)\n\t\t\t\t.then(ReactiveSecurityContextHolder.getContext()\n\t\t\t\t\t\t.flatMap((ctx) -> Mono.justOrEmpty(ctx.getAuthentication()))\n\t\t\t\t\t\t.defaultIfEmpty(this.anonymousToken)\n\t\t\t\t\t\t.flatMap((principal) -> this.authorizedClientRepository\n\t\t\t\t\t\t\t\t.saveAuthorizedClient(authorizedClient, principal, webFilterExchange.getExchange())\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<ServerWebExchangeMatcher.MatchResult> matchesAuthorizationResponse(ServerWebExchange exchange) {\n\t\t// @formatter:off\n\t\treturn Mono.just(exchange)\n\t\t\t\t.filter((exch) ->\n\t\t\t\t\t\tOAuth2AuthorizationResponseUtils.isAuthorizationResponse(exch.getRequest().getQueryParams())\n\t\t\t\t)\n\t\t\t\t.flatMap((exch) -> this.authorizationRequestRepository.loadAuthorizationRequest(exchange)\n\t\t\t\t\t\t.flatMap((authorizationRequest) -> matchesRedirectUri(exch.getRequest().getURI(),\n\t\t\t\t\t\t\t\tauthorizationRequest.getRedirectUri())))\n\t\t\t\t.switchIfEmpty(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\t// @formatter:on\n\t}\n\n\tprivate static Mono<ServerWebExchangeMatcher.MatchResult> matchesRedirectUri(URI authorizationResponseUri,\n\t\t\t@Nullable String authorizationRequestRedirectUri) {\n\t\tif (authorizationRequestRedirectUri == null) {\n\t\t\treturn ServerWebExchangeMatcher.MatchResult.notMatch();\n\t\t}\n\t\tUriComponents requestUri = UriComponentsBuilder.fromUri(authorizationResponseUri).build();\n\t\tUriComponents redirectUri = UriComponentsBuilder.fromUriString(authorizationRequestRedirectUri).build();\n\t\tSet<Map.Entry<String, List<String>>> requestUriParameters = new LinkedHashSet<>(\n\t\t\t\trequestUri.getQueryParams().entrySet());\n\t\tSet<Map.Entry<String, List<String>>> redirectUriParameters = new LinkedHashSet<>(\n\t\t\t\tredirectUri.getQueryParams().entrySet());\n\t\t// Remove the additional request parameters (if any) from the authorization\n\t\t// response (request)\n\t\t// before doing an exact comparison with the authorizationRequest.getRedirectUri()\n\t\t// parameters (if any)\n\t\trequestUriParameters.retainAll(redirectUriParameters);\n\t\tif (Objects.equals(requestUri.getScheme(), redirectUri.getScheme())\n\t\t\t\t&& Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo())\n\t\t\t\t&& Objects.equals(requestUri.getHost(), redirectUri.getHost())\n\t\t\t\t&& Objects.equals(requestUri.getPort(), redirectUri.getPort())\n\t\t\t\t&& Objects.equals(requestUri.getPath(), redirectUri.getPath())\n\t\t\t\t&& Objects.equals(requestUriParameters.toString(), redirectUriParameters.toString())) {\n\t\t\treturn ServerWebExchangeMatcher.MatchResult.match();\n\t\t}\n\t\treturn ServerWebExchangeMatcher.MatchResult.notMatch();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationRequestRedirectWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * This {@code WebFilter} initiates the authorization code grant flow by redirecting the\n * End-User's user-agent to the Authorization Server's Authorization Endpoint.\n *\n * <p>\n * It builds the OAuth 2.0 Authorization Request, which is used as the redirect\n * {@code URI} to the Authorization Endpoint. The redirect {@code URI} will include the\n * client identifier, requested scope(s), state, response type, and a redirection URI\n * which the authorization server will send the user-agent back to once access is granted\n * (or denied) by the End-User (Resource Owner).\n *\n * <p>\n * By default, this {@code Filter} responds to authorization requests at the {@code URI}\n * {@code /oauth2/authorization/{registrationId}}. The {@code URI} template variable\n * {@code {registrationId}} represents the {@link ClientRegistration#getRegistrationId()\n * registration identifier} of the client that is used for initiating the OAuth 2.0\n * Authorization Request.\n *\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2AuthorizationRequest\n * @see AuthorizationRequestRepository\n * @see ClientRegistration\n * @see ClientRegistrationRepository\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-4.1\">Section\n * 4.1 Authorization Code Grant</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.1\">Section 4.1.1 Authorization Request\n * (Authorization Code)</a>\n */\npublic class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {\n\n\tprivate ServerRedirectStrategy authorizationRedirectStrategy = new DefaultServerRedirectStrategy();\n\n\tprivate final ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;\n\n\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new WebSessionOAuth2ServerAuthorizationRequestRepository();\n\n\tprivate ServerRequestCache requestCache = new WebSessionServerRequestCache();\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationRequestRedirectFilter} using the provided\n\t * parameters.\n\t * @param clientRegistrationRepository the repository of client registrations\n\t */\n\tpublic OAuth2AuthorizationRequestRedirectWebFilter(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\tthis.authorizationRequestResolver = new DefaultServerOAuth2AuthorizationRequestResolver(\n\t\t\t\tclientRegistrationRepository);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationRequestRedirectFilter} using the provided\n\t * parameters.\n\t * @param authorizationRequestResolver the resolver to use\n\t */\n\tpublic OAuth2AuthorizationRequestRedirectWebFilter(\n\t\t\tServerOAuth2AuthorizationRequestResolver authorizationRequestResolver) {\n\t\tAssert.notNull(authorizationRequestResolver, \"authorizationRequestResolver cannot be null\");\n\t\tthis.authorizationRequestResolver = authorizationRequestResolver;\n\t}\n\n\t/**\n\t * Sets the redirect strategy for Authorization Endpoint redirect URI.\n\t * @param authorizationRedirectStrategy the redirect strategy\n\t */\n\tpublic void setAuthorizationRedirectStrategy(ServerRedirectStrategy authorizationRedirectStrategy) {\n\t\tAssert.notNull(authorizationRedirectStrategy, \"authorizationRedirectStrategy cannot be null\");\n\t\tthis.authorizationRedirectStrategy = authorizationRedirectStrategy;\n\t}\n\n\t/**\n\t * Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.\n\t * @param authorizationRequestRepository the repository used for storing\n\t * {@link OAuth2AuthorizationRequest}'s\n\t */\n\tpublic final void setAuthorizationRequestRepository(\n\t\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\tAssert.notNull(authorizationRequestRepository, \"authorizationRequestRepository cannot be null\");\n\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t}\n\n\t/**\n\t * The request cache to use to save the request before sending a redirect.\n\t * @param requestCache the cache to redirect to.\n\t */\n\tpublic void setRequestCache(ServerRequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t// @formatter:off\n\t\treturn this.authorizationRequestResolver.resolve(exchange)\n\t\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t\t.onErrorResume(ClientAuthorizationRequiredException.class,\n\t\t\t\t\t\t(ex) -> this.requestCache.saveRequest(exchange).then(\n\t\t\t\t\t\t\t\tthis.authorizationRequestResolver.resolve(exchange, ex.getClientRegistrationId()))\n\t\t\t\t)\n\t\t\t\t.flatMap((clientRegistration) -> sendRedirectForAuthorization(exchange, clientRegistration));\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Void> sendRedirectForAuthorization(ServerWebExchange exchange,\n\t\t\tOAuth2AuthorizationRequest authorizationRequest) {\n\t\treturn Mono.defer(() -> {\n\t\t\tMono<Void> saveAuthorizationRequest = Mono.empty();\n\t\t\tif (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {\n\t\t\t\tsaveAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t\t\t.saveAuthorizationRequest(authorizationRequest, exchange);\n\t\t\t}\n\t\t\t// @formatter:off\n\t\t\tURI redirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.toUri();\n\t\t\t// @formatter:on\n\t\t\treturn saveAuthorizationRequest\n\t\t\t\t.then(this.authorizationRedirectStrategy.sendRedirect(exchange, redirectUri));\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationResponseUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\n/**\n * Utility methods for an OAuth 2.0 Authorization Response.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AuthorizationResponse\n */\nfinal class OAuth2AuthorizationResponseUtils {\n\n\tprivate OAuth2AuthorizationResponseUtils() {\n\t}\n\n\tstatic MultiValueMap<String, String> toMultiMap(Map<String, String[]> map) {\n\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<>(map.size());\n\t\tmap.forEach((key, values) -> {\n\t\t\tif (values.length > 0) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\tparams.add(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn params;\n\t}\n\n\tstatic boolean isAuthorizationResponse(MultiValueMap<String, String> request) {\n\t\treturn isAuthorizationResponseSuccess(request) || isAuthorizationResponseError(request);\n\t}\n\n\tstatic boolean isAuthorizationResponseSuccess(MultiValueMap<String, String> request) {\n\t\treturn StringUtils.hasText(request.getFirst(OAuth2ParameterNames.CODE))\n\t\t\t\t&& StringUtils.hasText(request.getFirst(OAuth2ParameterNames.STATE));\n\t}\n\n\tstatic boolean isAuthorizationResponseError(MultiValueMap<String, String> request) {\n\t\treturn StringUtils.hasText(request.getFirst(OAuth2ParameterNames.ERROR))\n\t\t\t\t&& StringUtils.hasText(request.getFirst(OAuth2ParameterNames.STATE));\n\t}\n\n\tstatic OAuth2AuthorizationResponse convert(MultiValueMap<String, String> request, String redirectUri) {\n\t\tString code = request.getFirst(OAuth2ParameterNames.CODE);\n\t\tString errorCode = request.getFirst(OAuth2ParameterNames.ERROR);\n\t\tString state = request.getFirst(OAuth2ParameterNames.STATE);\n\t\tif (StringUtils.hasText(code)) {\n\t\t\tOAuth2AuthorizationResponse.Builder builder = OAuth2AuthorizationResponse.success(code)\n\t\t\t\t.redirectUri(redirectUri);\n\t\t\tif (state != null) {\n\t\t\t\tbuilder.state(state);\n\t\t\t}\n\t\t\treturn builder.build();\n\t\t}\n\t\tif (!StringUtils.hasText(errorCode)) {\n\t\t\terrorCode = \"unknown_error\";\n\t\t}\n\t\tString errorDescription = request.getFirst(OAuth2ParameterNames.ERROR_DESCRIPTION);\n\t\tString errorUri = request.getFirst(OAuth2ParameterNames.ERROR_URI);\n\t\tOAuth2AuthorizationResponse.Builder builder = OAuth2AuthorizationResponse.error(errorCode)\n\t\t\t.redirectUri(redirectUri);\n\t\tif (errorDescription != null) {\n\t\t\tbuilder.errorDescription(errorDescription);\n\t\t}\n\t\tif (errorUri != null) {\n\t\t\tbuilder.errorUri(errorUri);\n\t\t}\n\t\tif (state != null) {\n\t\t\tbuilder.state(state);\n\t\t}\n\t\treturn builder.build();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/ServerAuthorizationRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;\nimport org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Implementations of this interface are responsible for the persistence of\n * {@link OAuth2AuthorizationRequest} between requests.\n *\n * <p>\n * Used by the {@link OAuth2AuthorizationRequestRedirectFilter} for persisting the\n * Authorization Request before it initiates the authorization code grant flow. As well,\n * used by the {@link OAuth2LoginAuthenticationFilter} for resolving the associated\n * Authorization Request when handling the callback of the Authorization Response.\n *\n * @param <T> The type of OAuth 2.0 Authorization Request\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2AuthorizationRequest\n * @see HttpSessionOAuth2AuthorizationRequestRepository\n */\npublic interface ServerAuthorizationRequestRepository<T extends OAuth2AuthorizationRequest> {\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest} associated to the provided\n\t * {@code HttpServletRequest} or {@code null} if not available.\n\t * @param exchange the {@code ServerWebExchange}\n\t * @return the {@link OAuth2AuthorizationRequest} or {@code null} if not available\n\t */\n\tMono<T> loadAuthorizationRequest(ServerWebExchange exchange);\n\n\t/**\n\t * Persists the {@link OAuth2AuthorizationRequest} associating it to the provided\n\t * {@code HttpServletRequest} and/or {@code HttpServletResponse}.\n\t * @param authorizationRequest the {@link OAuth2AuthorizationRequest}\n\t * @param exchange the {@code ServerWebExchange}\n\t */\n\tMono<Void> saveAuthorizationRequest(T authorizationRequest, ServerWebExchange exchange);\n\n\t/**\n\t * Removes and returns the {@link OAuth2AuthorizationRequest} associated to the\n\t * provided {@code HttpServletRequest} or if not available returns {@code null}.\n\t * @param exchange the {@code ServerWebExchange}\n\t * @return the removed {@link OAuth2AuthorizationRequest} or {@code null} if not\n\t * available\n\t */\n\tMono<T> removeAuthorizationRequest(ServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/ServerOAuth2AuthorizationCodeAuthenticationTokenConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Converts from a {@link ServerWebExchange} to an\n * {@link OAuth2AuthorizationCodeAuthenticationToken} that can be authenticated. The\n * converter does not validate any errors it only performs a conversion.\n *\n * @author Rob Winch\n * @since 5.1\n * @see org.springframework.security.web.server.authentication.AuthenticationWebFilter#setServerAuthenticationConverter(ServerAuthenticationConverter)\n */\npublic class ServerOAuth2AuthorizationCodeAuthenticationTokenConverter implements ServerAuthenticationConverter {\n\n\tstatic final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = \"authorization_request_not_found\";\n\n\tstatic final String CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE = \"client_registration_not_found\";\n\n\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new WebSessionOAuth2ServerAuthorizationRequestRepository();\n\n\tprivate final ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tpublic ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(\n\t\t\tReactiveClientRegistrationRepository clientRegistrationRepository) {\n\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t}\n\n\t/**\n\t * Sets the {@link ServerAuthorizationRequestRepository} to be used. The default is\n\t * {@link WebSessionOAuth2ServerAuthorizationRequestRepository}.\n\t * @param authorizationRequestRepository the repository to use.\n\t */\n\tpublic void setAuthorizationRequestRepository(\n\t\t\tServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {\n\t\tAssert.notNull(authorizationRequestRepository, \"authorizationRequestRepository cannot be null\");\n\t\tthis.authorizationRequestRepository = authorizationRequestRepository;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> convert(ServerWebExchange serverWebExchange) {\n\t\t// @formatter:off\n\t\treturn this.authorizationRequestRepository.removeAuthorizationRequest(serverWebExchange)\n\t\t\t\t.switchIfEmpty(oauth2AuthorizationException(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE))\n\t\t\t\t.flatMap((authorizationRequest) -> authenticationRequest(serverWebExchange, authorizationRequest));\n\t\t// @formatter:on\n\t}\n\n\tprivate <T> Mono<T> oauth2AuthorizationException(String errorCode) {\n\t\treturn Mono.defer(() -> {\n\t\t\tOAuth2Error oauth2Error = new OAuth2Error(errorCode);\n\t\t\treturn Mono.error(new OAuth2AuthorizationException(oauth2Error));\n\t\t});\n\t}\n\n\tprivate Mono<OAuth2AuthorizationCodeAuthenticationToken> authenticationRequest(ServerWebExchange exchange,\n\t\t\tOAuth2AuthorizationRequest authorizationRequest) {\n\t\t// @formatter:off\n\t\treturn Mono.just(authorizationRequest)\n\t\t\t\t.map(OAuth2AuthorizationRequest::getAttributes).flatMap((attributes) -> {\n\t\t\t\t\tString id = (String) attributes.get(OAuth2ParameterNames.REGISTRATION_ID);\n\t\t\t\t\tif (id == null) {\n\t\t\t\t\t\treturn oauth2AuthorizationException(CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE);\n\t\t\t\t\t}\n\t\t\t\t\treturn this.clientRegistrationRepository.findByRegistrationId(id);\n\t\t\t\t})\n\t\t\t\t.switchIfEmpty(oauth2AuthorizationException(CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE))\n\t\t\t\t.map((clientRegistration) -> {\n\t\t\t\t\tOAuth2AuthorizationResponse authorizationResponse = convertResponse(exchange);\n\t\t\t\t\tOAuth2AuthorizationCodeAuthenticationToken authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\t\t\t\tclientRegistration,\n\t\t\t\t\t\t\tnew OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));\n\t\t\t\t\treturn authenticationRequest;\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate static OAuth2AuthorizationResponse convertResponse(ServerWebExchange exchange) {\n\t\tString redirectUri = UriComponentsBuilder.fromUri(exchange.getRequest().getURI()).build().toUriString();\n\t\treturn OAuth2AuthorizationResponseUtils.convert(exchange.getRequest().getQueryParams(), redirectUri);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/ServerOAuth2AuthorizationRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Implementations of this interface are capable of resolving an\n * {@link OAuth2AuthorizationRequest} from the provided {@code ServerWebExchange}. Used by\n * the {@link OAuth2AuthorizationRequestRedirectWebFilter} for resolving Authorization\n * Requests.\n *\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationRequestRedirectWebFilter\n */\npublic interface ServerOAuth2AuthorizationRequestResolver {\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest} resolved from the provided\n\t * {@code HttpServletRequest} or {@code null} if not available.\n\t * @param exchange the {@code ServerWebExchange}\n\t * @return the resolved {@link OAuth2AuthorizationRequest} or {@code null} if not\n\t * available\n\t */\n\tMono<OAuth2AuthorizationRequest> resolve(ServerWebExchange exchange);\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest} resolved from the provided\n\t * {@code HttpServletRequest} or {@code null} if not available.\n\t * @param exchange the {@code ServerWebExchange}\n\t * @param clientRegistrationId the client registration id\n\t * @return the resolved {@link OAuth2AuthorizationRequest} or {@code null} if not\n\t * available\n\t */\n\tMono<OAuth2AuthorizationRequest> resolve(ServerWebExchange exchange, String clientRegistrationId);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/ServerOAuth2AuthorizedClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Implementations of this interface are responsible for the persistence of\n * {@link OAuth2AuthorizedClient Authorized Client(s)} between requests.\n *\n * <p>\n * The primary purpose of an {@link OAuth2AuthorizedClient Authorized Client} is to\n * associate an {@link OAuth2AuthorizedClient#getAccessToken() Access Token} credential to\n * a {@link OAuth2AuthorizedClient#getClientRegistration() Client} and Resource Owner, who\n * is the {@link OAuth2AuthorizedClient#getPrincipalName() Principal} that originally\n * granted the authorization.\n *\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2AuthorizedClient\n * @see ClientRegistration\n * @see Authentication\n * @see OAuth2AccessToken\n */\npublic interface ServerOAuth2AuthorizedClientRepository {\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizedClient} associated to the provided client\n\t * registration identifier and End-User {@link Authentication} (Resource Owner) or\n\t * {@code null} if not available.\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t * @param exchange the {@code ServerWebExchange}\n\t * @param <T> a type of OAuth2AuthorizedClient\n\t * @return the {@link OAuth2AuthorizedClient} or {@code null} if not available\n\t */\n\t<T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId,\n\t\t\tAuthentication principal, ServerWebExchange exchange);\n\n\t/**\n\t * Saves the {@link OAuth2AuthorizedClient} associating it to the provided End-User\n\t * {@link Authentication} (Resource Owner).\n\t * @param authorizedClient the authorized client\n\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t * @param exchange the {@code ServerWebExchange}\n\t */\n\tMono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tServerWebExchange exchange);\n\n\t/**\n\t * Removes the {@link OAuth2AuthorizedClient} associated to the provided client\n\t * registration identifier and End-User {@link Authentication} (Resource Owner).\n\t * @param clientRegistrationId the identifier for the client's registration\n\t * @param principal the End-User {@link Authentication} (Resource Owner)\n\t * @param exchange the {@code ServerWebExchange}\n\t */\n\tMono<Void> removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\tServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\n/**\n * An implementation of an {@link ServerAuthorizationRequestRepository} that stores\n * {@link OAuth2AuthorizationRequest} in the {@code WebSession}.\n *\n * @author Rob Winch\n * @author Steve Riesenberg\n * @since 5.1\n * @see AuthorizationRequestRepository\n * @see OAuth2AuthorizationRequest\n */\npublic final class WebSessionOAuth2ServerAuthorizationRequestRepository\n\t\timplements ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> {\n\n\tprivate static final String DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME = WebSessionOAuth2ServerAuthorizationRequestRepository.class\n\t\t.getName() + \".AUTHORIZATION_REQUEST\";\n\n\tprivate final String sessionAttributeName = DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME;\n\n\t@Override\n\tpublic Mono<OAuth2AuthorizationRequest> loadAuthorizationRequest(ServerWebExchange exchange) {\n\t\tString state = getStateParameter(exchange);\n\t\tif (state == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\t// @formatter:off\n\t\treturn getSessionAttributes(exchange)\n\t\t\t\t.filter((sessionAttrs) -> sessionAttrs.containsKey(this.sessionAttributeName))\n\t\t\t\t.flatMap((sessionAttrs) -> Mono.justOrEmpty(getAuthorizationRequest(sessionAttrs)))\n\t\t\t\t.filter((authorizationRequest) -> state.equals(authorizationRequest.getState()));\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest,\n\t\t\tServerWebExchange exchange) {\n\t\tAssert.notNull(authorizationRequest, \"authorizationRequest cannot be null\");\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\t// @formatter:off\n\t\treturn getSessionAttributes(exchange)\n\t\t\t\t.doOnNext((sessionAttrs) -> {\n\t\t\t\t\tAssert.hasText(authorizationRequest.getState(), \"authorizationRequest.state cannot be empty\");\n\t\t\t\t\tsessionAttrs.put(this.sessionAttributeName, authorizationRequest);\n\t\t\t\t})\n\t\t\t\t.then();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AuthorizationRequest> removeAuthorizationRequest(ServerWebExchange exchange) {\n\t\tString state = getStateParameter(exchange);\n\t\tif (state == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\t// @formatter:off\n\t\treturn getSessionAttributes(exchange)\n\t\t\t\t.filter((sessionAttrs) -> sessionAttrs.containsKey(this.sessionAttributeName))\n\t\t\t\t.flatMap((sessionAttrs) -> {\n\t\t\t\t\tOAuth2AuthorizationRequest authorizationRequest = (OAuth2AuthorizationRequest) sessionAttrs\n\t\t\t\t\t\t\t.get(this.sessionAttributeName);\n\t\t\t\t\tif (authorizationRequest != null && state.equals(authorizationRequest.getState())) {\n\t\t\t\t\t\tsessionAttrs.remove(this.sessionAttributeName);\n\t\t\t\t\t\treturn Mono.just(authorizationRequest);\n\t\t\t\t\t}\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Gets the state parameter from the {@link ServerHttpRequest}\n\t * @param exchange the exchange to use\n\t * @return the state parameter or null if not found\n\t */\n\tprivate @Nullable String getStateParameter(ServerWebExchange exchange) {\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\treturn exchange.getRequest().getQueryParams().getFirst(OAuth2ParameterNames.STATE);\n\t}\n\n\tprivate Mono<Map<String, Object>> getSessionAttributes(ServerWebExchange exchange) {\n\t\treturn exchange.getSession().map(WebSession::getAttributes);\n\t}\n\n\tprivate @Nullable OAuth2AuthorizationRequest getAuthorizationRequest(Map<String, Object> sessionAttrs) {\n\t\treturn (OAuth2AuthorizationRequest) sessionAttrs.get(this.sessionAttributeName);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/WebSessionServerOAuth2AuthorizedClientRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\n/**\n * An implementation of an {@link OAuth2AuthorizedClientRepository} that stores\n * {@link OAuth2AuthorizedClient}'s in the {@code HttpSession}.\n *\n * @author Rob Winch\n * @since 5.1\n * @see OAuth2AuthorizedClientRepository\n * @see OAuth2AuthorizedClient\n */\npublic final class WebSessionServerOAuth2AuthorizedClientRepository implements ServerOAuth2AuthorizedClientRepository {\n\n\tprivate static final String DEFAULT_AUTHORIZED_CLIENTS_ATTR_NAME = WebSessionServerOAuth2AuthorizedClientRepository.class\n\t\t.getName() + \".AUTHORIZED_CLIENTS\";\n\n\tprivate final String sessionAttributeName = DEFAULT_AUTHORIZED_CLIENTS_ATTR_NAME;\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId,\n\t\t\tAuthentication principal, ServerWebExchange exchange) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\t// @formatter:off\n\t\treturn exchange.getSession()\n\t\t\t\t.map(this::getAuthorizedClients)\n\t\t\t\t.flatMap((clients) -> Mono.justOrEmpty((T) clients.get(clientRegistrationId)));\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\tServerWebExchange exchange) {\n\t\tAssert.notNull(authorizedClient, \"authorizedClient cannot be null\");\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\t// @formatter:off\n\t\treturn exchange.getSession()\n\t\t\t\t.doOnSuccess((session) -> {\n\t\t\t\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = getAuthorizedClients(session);\n\t\t\t\t\tauthorizedClients.put(authorizedClient.getClientRegistration().getRegistrationId(), authorizedClient);\n\t\t\t\t\tsession.getAttributes().put(this.sessionAttributeName, authorizedClients);\n\t\t\t\t})\n\t\t\t\t.then(Mono.empty());\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Mono<Void> removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\tServerWebExchange exchange) {\n\t\tAssert.hasText(clientRegistrationId, \"clientRegistrationId cannot be empty\");\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\t// @formatter:off\n\t\treturn exchange.getSession()\n\t\t\t\t.doOnSuccess((session) -> {\n\t\t\t\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = getAuthorizedClients(session);\n\t\t\t\t\tauthorizedClients.remove(clientRegistrationId);\n\t\t\t\t\tif (authorizedClients.isEmpty()) {\n\t\t\t\t\t\tsession.getAttributes().remove(this.sessionAttributeName);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tsession.getAttributes().put(this.sessionAttributeName, authorizedClients);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.then(Mono.empty());\n\t\t// @formatter:on\n\t}\n\n\tprivate Map<String, OAuth2AuthorizedClient> getAuthorizedClients(WebSession session) {\n\t\tAssert.notNull(session, \"session cannot be null\");\n\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = session.getAttribute(this.sessionAttributeName);\n\t\treturn (authorizedClients != null) ? authorizedClients : new HashMap<>();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/authentication/OAuth2LoginAuthenticationWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.AuthenticationWebFilter;\nimport org.springframework.util.Assert;\n\n/**\n * A specialized {@link AuthenticationWebFilter} that converts from an\n * {@link OAuth2LoginAuthenticationToken} to an {@link OAuth2AuthenticationToken} and\n * saves the {@link OAuth2AuthorizedClient}\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic class OAuth2LoginAuthenticationWebFilter extends AuthenticationWebFilter {\n\n\tprivate final ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t/**\n\t * Creates an instance\n\t * @param authenticationManager the authentication manager to use\n\t * @param authorizedClientRepository\n\t */\n\tpublic OAuth2LoginAuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager,\n\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository) {\n\t\tsuper(authenticationManager);\n\t\tAssert.notNull(authorizedClientRepository, \"authorizedClientService cannot be null\");\n\t\tthis.authorizedClientRepository = authorizedClientRepository;\n\t}\n\n\t@Override\n\tprotected Mono<Void> onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) {\n\t\tOAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken) authentication;\n\t\tAssert.notNull(authenticationResult.getAccessToken(), \"accessToken cannot be null\");\n\t\tAssert.notNull(authenticationResult.getPrincipal(), \"principal cannot be null\");\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(\n\t\t\t\tauthenticationResult.getClientRegistration(), authenticationResult.getName(),\n\t\t\t\tauthenticationResult.getAccessToken(), authenticationResult.getRefreshToken());\n\t\tOAuth2AuthenticationToken result = new OAuth2AuthenticationToken(authenticationResult.getPrincipal(),\n\t\t\t\tauthenticationResult.getAuthorities(),\n\t\t\t\tauthenticationResult.getClientRegistration().getRegistrationId());\n\t\t// @formatter:off\n\t\treturn this.authorizedClientRepository\n\t\t\t\t.saveAuthorizedClient(authorizedClient, authenticationResult, webFilterExchange.getExchange())\n\t\t\t\t.then(super.onAuthenticationSuccess(result, webFilterExchange));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Client {@code WebFilter}'s and supporting classes and interfaces.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web.server.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Client {@code WebFilter}'s and supporting classes and interfaces.\n */\n@NullMarked\npackage org.springframework.security.oauth2.client.web.server;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.oauth2.client.aot.hint.OAuth2ClientRuntimeHints\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/resources/org/springframework/security/oauth2/client/oauth2-client-schema-postgres.sql",
    "content": "CREATE TABLE oauth2_authorized_client (\n  client_registration_id varchar(100) NOT NULL,\n  principal_name varchar(200) NOT NULL,\n  access_token_type varchar(100) NOT NULL,\n  access_token_value bytea NOT NULL,\n  access_token_issued_at timestamp NOT NULL,\n  access_token_expires_at timestamp NOT NULL,\n  access_token_scopes varchar(1000) DEFAULT NULL,\n  refresh_token_value bytea DEFAULT NULL,\n  refresh_token_issued_at timestamp DEFAULT NULL,\n  created_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,\n  PRIMARY KEY (client_registration_id, principal_name)\n);\n"
  },
  {
    "path": "oauth2/oauth2-client/src/main/resources/org/springframework/security/oauth2/client/oauth2-client-schema.sql",
    "content": "CREATE TABLE oauth2_authorized_client (\n  client_registration_id varchar(100) NOT NULL,\n  principal_name varchar(200) NOT NULL,\n  access_token_type varchar(100) NOT NULL,\n  access_token_value blob NOT NULL,\n  access_token_issued_at timestamp NOT NULL,\n  access_token_expires_at timestamp NOT NULL,\n  access_token_scopes varchar(1000) DEFAULT NULL,\n  refresh_token_value blob DEFAULT NULL,\n  refresh_token_issued_at timestamp DEFAULT NULL,\n  created_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,\n  PRIMARY KEY (client_registration_id, principal_name)\n);\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/AuthorizationCodeOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link AuthorizationCodeOAuth2AuthorizedClientProvider}.\n *\n * @author Joe Grandja\n */\npublic class AuthorizationCodeOAuth2AuthorizedClientProviderTests {\n\n\tprivate AuthorizationCodeOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientProvider = new AuthorizationCodeOAuth2AuthorizedClientProvider();\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, \"principal\",\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"read\", \"write\"));\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientProvider.authorize(null));\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotAuthorizationCodeThenUnableToAuthorize() {\n\t\tClientRegistration clientCredentialsClient = TestClientRegistrations.clientCredentials().build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientCredentialsClient).principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizationCodeAndAuthorizedThenNotAuthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient).principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizationCodeAndNotAuthorizedThenAuthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration).principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(authorizationContext));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/AuthorizationCodeReactiveOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link AuthorizationCodeReactiveOAuth2AuthorizedClientProvider}.\n *\n * @author Joe Grandja\n */\npublic class AuthorizationCodeReactiveOAuth2AuthorizedClientProviderTests {\n\n\tprivate AuthorizationCodeReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientProvider = new AuthorizationCodeReactiveOAuth2AuthorizedClientProvider();\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, \"principal\",\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"read\", \"write\"));\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientProvider.authorize(null).block());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotAuthorizationCodeThenUnableToAuthorize() {\n\t\tClientRegistration clientCredentialsClient = TestClientRegistrations.clientCredentials().build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientCredentialsClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizationCodeAndAuthorizedThenNotAuthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizationCodeAndNotAuthorizedThenAuthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(authorizationContext).block());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/AuthorizedClientServiceOAuth2AuthorizedClientManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link AuthorizedClientServiceOAuth2AuthorizedClientManager}.\n *\n * @author Joe Grandja\n */\npublic class AuthorizedClientServiceOAuth2AuthorizedClientManagerTests {\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate OAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate Function contextAttributesMapper;\n\n\tprivate OAuth2AuthorizationSuccessHandler authorizationSuccessHandler;\n\n\tprivate OAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\tprivate AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Authentication principal;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate ArgumentCaptor<OAuth2AuthorizationContext> authorizationContextCaptor;\n\n\t@SuppressWarnings(\"unchecked\")\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tthis.authorizedClientService = mock(OAuth2AuthorizedClientService.class);\n\t\tthis.authorizedClientProvider = mock(OAuth2AuthorizedClientProvider.class);\n\t\tthis.contextAttributesMapper = mock(Function.class);\n\t\tthis.authorizationSuccessHandler = spy(new OAuth2AuthorizationSuccessHandler() {\n\t\t\t@Override\n\t\t\tpublic void onAuthorizationSuccess(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\t\t\tMap<String, Object> attributes) {\n\t\t\t\tAuthorizedClientServiceOAuth2AuthorizedClientManagerTests.this.authorizedClientService\n\t\t\t\t\t.saveAuthorizedClient(authorizedClient, principal);\n\t\t\t}\n\t\t});\n\t\tthis.authorizationFailureHandler = spy(new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(\n\t\t\t\t(clientRegistrationId, principal, attributes) -> this.authorizedClientService\n\t\t\t\t\t.removeAuthorizedClient(clientRegistrationId, principal.getName())));\n\t\tthis.authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientService);\n\t\tthis.authorizedClientManager.setAuthorizedClientProvider(this.authorizedClientProvider);\n\t\tthis.authorizedClientManager.setContextAttributesMapper(this.contextAttributesMapper);\n\t\tthis.authorizedClientManager.setAuthorizationSuccessHandler(this.authorizationSuccessHandler);\n\t\tthis.authorizedClientManager.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, this.principal.getName(),\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"read\", \"write\"), TestOAuth2RefreshTokens.refreshToken());\n\t\tthis.authorizationContextCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationContext.class);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new AuthorizedClientServiceOAuth2AuthorizedClientManager(null, this.authorizedClientService))\n\t\t\t\t.withMessage(\"clientRegistrationRepository cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenOAuth2AuthorizedClientServiceIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new AuthorizedClientServiceOAuth2AuthorizedClientManager(this.clientRegistrationRepository, null))\n\t\t\t\t.withMessage(\"authorizedClientService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientProviderWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizedClientProvider(null))\n\t\t\t\t.withMessage(\"authorizedClientProvider cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setContextAttributesMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.setContextAttributesMapper(null))\n\t\t\t\t.withMessage(\"contextAttributesMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizationSuccessHandler(null))\n\t\t\t\t.withMessage(\"authorizationSuccessHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizationFailureHandler(null))\n\t\t\t\t.withMessage(\"authorizationFailureHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(null))\n\t\t\t\t.withMessage(\"authorizeRequest cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientRegistrationNotFoundThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(\"invalid-registration-id\")\n\n\t\t\t\t.principal(this.principal).build();\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest))\n\t\t\t\t.withMessage(\"Could not find ClientRegistration with id 'invalid-registration-id'\");\n\t\t// @formatter:on\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndUnsupportedProviderThenNotAuthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(this.clientRegistration);\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isNull();\n\t\tverifyNoInteractions(this.authorizationSuccessHandler);\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndSupportedProviderThenAuthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(this.clientRegistration);\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(this.authorizedClient);\n\t\tverify(this.authorizationSuccessHandler).onAuthorizationSuccess(eq(this.authorizedClient), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(eq(this.authorizedClient), eq(this.principal));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndSupportedProviderThenReauthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(this.clientRegistration);\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal.getName())))\n\t\t\t.willReturn(this.authorizedClient);\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(reauthorizedClient);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(reauthorizedClient);\n\t\tverify(this.authorizationSuccessHandler).onAuthorizationSuccess(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenUnsupportedProviderThenNotReauthorized() {\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(reauthorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(this.authorizedClient);\n\t\tverifyNoInteractions(this.authorizationSuccessHandler);\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenSupportedProviderThenReauthorized() {\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(reauthorizedClient);\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(reauthorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(reauthorizedClient);\n\t\tverify(this.authorizationSuccessHandler).onAuthorizationSuccess(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenRequestAttributeScopeThenMappedToContext() {\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(reauthorizedClient);\n\t\t// Override the mock with the default\n\t\tthis.authorizedClientManager.setContextAttributesMapper(\n\t\t\t\tnew AuthorizedClientServiceOAuth2AuthorizedClientManager.DefaultContextAttributesMapper());\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.attribute(OAuth2ParameterNames.SCOPE, \"read write\")\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizationContext.getAttributes())\n\t\t\t.containsKey(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);\n\t\tString[] requestScopeAttribute = authorizationContext\n\t\t\t.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);\n\t\tassertThat(requestScopeAttribute).contains(\"read\", \"write\");\n\t\tassertThat(authorizedClient).isSameAs(reauthorizedClient);\n\t\tverify(this.authorizationSuccessHandler).onAuthorizationSuccess(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal));\n\t}\n\n\t@Test\n\tpublic void reauthorizeWhenErrorCodeMatchThenRemoveAuthorizedClient() {\n\t\tClientAuthorizationException authorizationException = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, null, null),\n\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willThrow(authorizationException);\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(reauthorizeRequest))\n\t\t\t.isEqualTo(authorizationException);\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(eq(authorizationException), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientService).removeAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal.getName()));\n\t}\n\n\t@Test\n\tpublic void reauthorizeWhenErrorCodeDoesNotMatchThenDoNotRemoveAuthorizedClient() {\n\t\tClientAuthorizationException authorizationException = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(\"non-matching-error-code\", null, null), this.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willThrow(authorizationException);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(reauthorizeRequest))\n\t\t\t\t.isEqualTo(authorizationException);\n\t\t// @formatter:on\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(eq(authorizationException), eq(this.principal),\n\t\t\t\tany());\n\t\tverifyNoInteractions(this.authorizedClientService);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/AuthorizedClientServiceReactiveOAuth2AuthorizedClientManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager}.\n *\n * @author Ankur Pathak\n * @author Phil Clay\n */\npublic class AuthorizedClientServiceReactiveOAuth2AuthorizedClientManagerTests {\n\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate ReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper;\n\n\tprivate AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Authentication principal;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate ArgumentCaptor<OAuth2AuthorizationContext> authorizationContextCaptor;\n\n\tprivate PublisherProbe<Void> saveAuthorizedClientProbe;\n\n\tprivate PublisherProbe<Void> removeAuthorizedClientProbe;\n\n\t@SuppressWarnings(\"unchecked\")\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.clientRegistrationRepository = mock(ReactiveClientRegistrationRepository.class);\n\t\tthis.authorizedClientService = mock(ReactiveOAuth2AuthorizedClientService.class);\n\t\tthis.saveAuthorizedClientProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizedClientService.saveAuthorizedClient(any(), any()))\n\t\t\t.willReturn(this.saveAuthorizedClientProbe.mono());\n\t\tthis.removeAuthorizedClientProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizedClientService.removeAuthorizedClient(any(), any()))\n\t\t\t.willReturn(this.removeAuthorizedClientProbe.mono());\n\t\tthis.authorizedClientProvider = mock(ReactiveOAuth2AuthorizedClientProvider.class);\n\t\tthis.contextAttributesMapper = mock(Function.class);\n\t\tgiven(this.contextAttributesMapper.apply(any())).willReturn(Mono.empty());\n\t\tthis.authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientService);\n\t\tthis.authorizedClientManager.setAuthorizedClientProvider(this.authorizedClientProvider);\n\t\tthis.authorizedClientManager.setContextAttributesMapper(this.contextAttributesMapper);\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, this.principal.getName(),\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"read\", \"write\"), TestOAuth2RefreshTokens.refreshToken());\n\t\tthis.authorizationContextCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationContext.class);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(null,\n\t\t\t\t\tthis.authorizedClientService))\n\t\t\t.withMessage(\"clientRegistrationRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenOAuth2AuthorizedClientServiceIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(\n\t\t\t\t\tthis.clientRegistrationRepository, null))\n\t\t\t.withMessage(\"authorizedClientService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientProviderWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizedClientProvider(null))\n\t\t\t.withMessage(\"authorizedClientProvider cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setContextAttributesMapperWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setContextAttributesMapper(null))\n\t\t\t.withMessage(\"contextAttributesMapper cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizationSuccessHandler(null))\n\t\t\t.withMessage(\"authorizationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizationFailureHandler(null))\n\t\t\t.withMessage(\"authorizationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientManager.authorize(null))\n\t\t\t.withMessage(\"authorizeRequest cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientRegistrationNotFoundThenThrowIllegalArgumentException() {\n\t\tString clientRegistrationId = \"invalid-registration-id\";\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId)).willReturn(Mono.empty());\n\t\tStepVerifier.create(this.authorizedClientManager.authorize(authorizeRequest))\n\t\t\t.verifyError(IllegalArgumentException.class);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndUnsupportedProviderThenNotAuthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.authorizedClientProvider.authorize(any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMono<OAuth2AuthorizedClient> authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tStepVerifier.create(authorizedClient).verifyComplete();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(OAuth2AuthorizedClient.class),\n\t\t\t\teq(this.principal));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndSupportedProviderThenAuthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tMono<OAuth2AuthorizedClient> authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tStepVerifier.create(authorizedClient).expectNext(this.authorizedClient).verifyComplete();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(eq(this.authorizedClient), eq(this.principal));\n\t\tthis.saveAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientService, never()).removeAuthorizedClient(any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndSupportedProviderAndCustomSuccessHandlerThenInvokeCustomSuccessHandler() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tPublisherProbe<Void> authorizationSuccessHandlerProbe = PublisherProbe.empty();\n\t\tthis.authorizedClientManager\n\t\t\t.setAuthorizationSuccessHandler((client, principal, attributes) -> authorizationSuccessHandlerProbe.mono());\n\t\tMono<OAuth2AuthorizedClient> authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tStepVerifier.create(authorizedClient).expectNext(this.authorizedClient).verifyComplete();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tauthorizationSuccessHandlerProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(), any());\n\t\tverify(this.authorizedClientService, never()).removeAuthorizedClient(any(), any());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenInvalidTokenThenRemoveAuthorizedClient() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tClientAuthorizationException exception = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, null, null),\n\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService).removeAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal.getName()));\n\t\tthis.removeAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(), any());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenInvalidGrantThenRemoveAuthorizedClient() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tClientAuthorizationException exception = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, null, null),\n\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService).removeAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal.getName()));\n\t\tthis.removeAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(), any());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenServerErrorThenDoNotRemoveAuthorizedClient() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tClientAuthorizationException exception = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, null, null),\n\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService, never()).removeAuthorizedClient(any(), any());\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(), any());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenOAuth2AuthorizationExceptionThenDoNotRemoveAuthorizedClient() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationException exception = new OAuth2AuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, null, null));\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService, never()).removeAuthorizedClient(any(), any());\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(), any());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenOAuth2AuthorizationExceptionAndCustomFailureHandlerThenInvokeCustomFailureHandler() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationException exception = new OAuth2AuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, null, null));\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tPublisherProbe<Void> authorizationFailureHandlerProbe = PublisherProbe.empty();\n\t\tthis.authorizedClientManager\n\t\t\t.setAuthorizationFailureHandler((client, principal, attributes) -> authorizationFailureHandlerProbe.mono());\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tauthorizationFailureHandlerProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientService, never()).removeAuthorizedClient(any(), any());\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndSupportedProviderThenReauthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal.getName())))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(reauthorizedClient));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tMono<OAuth2AuthorizedClient> authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\t// @formatter:off\n\t\tStepVerifier.create(authorizedClient)\n\t\t\t\t.expectNext(reauthorizedClient)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal));\n\t\tthis.saveAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientService, never()).removeAuthorizedClient(any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenUnsupportedProviderThenNotReauthorized() {\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class))).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMono<OAuth2AuthorizedClient> authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest);\n\t\tStepVerifier.create(authorizedClient).expectNext(this.authorizedClient).verifyComplete();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(reauthorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService, never()).saveAuthorizedClient(any(OAuth2AuthorizedClient.class),\n\t\t\t\teq(this.principal));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenSupportedProviderThenReauthorized() {\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(reauthorizedClient));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMono<OAuth2AuthorizedClient> authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest);\n\t\tStepVerifier.create(authorizedClient).expectNext(reauthorizedClient).verifyComplete();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(reauthorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal));\n\t\tthis.saveAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientService, never()).removeAuthorizedClient(any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenRequestAttributeScopeThenMappedToContext() {\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(reauthorizedClient));\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.attribute(OAuth2ParameterNames.SCOPE, \"read write\")\n\t\t\t.build();\n\t\tthis.authorizedClientManager.setContextAttributesMapper(\n\t\t\t\tnew AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager.DefaultContextAttributesMapper());\n\t\tMono<OAuth2AuthorizedClient> authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest);\n\t\t// @formatter:off\n\t\tStepVerifier.create(authorizedClient)\n\t\t\t\t.expectNext(reauthorizedClient)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal));\n\t\tthis.saveAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientService, never()).removeAuthorizedClient(any(), any());\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizationContext.getAttributes())\n\t\t\t.containsKey(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);\n\t\tString[] requestScopeAttribute = authorizationContext\n\t\t\t.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);\n\t\tassertThat(requestScopeAttribute).contains(\"read\", \"write\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/ClientCredentialsOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link ClientCredentialsOAuth2AuthorizedClientProvider}.\n *\n * @author Joe Grandja\n */\npublic class ClientCredentialsOAuth2AuthorizedClientProviderTests {\n\n\tprivate ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();\n\t\tthis.accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials().build();\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseClientWhenClientIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAccessTokenResponseClient(null))\n\t\t\t\t.isInstanceOf(IllegalArgumentException.class).withMessage(\"accessTokenResponseClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(null))\n\t\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(-1)))\n\t\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(null))\n\t\t\t\t.withMessage(\"context cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotClientCredentialsThenUnableToAuthorize() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAndNotAuthorizedThenAuthorize() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAndTokenExpiredThenReauthorize() {\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(60));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), accessToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAndTokenNotExpiredThenNotReauthorize() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t// gh-7511\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAndTokenNotExpiredButClockSkewForcesExpiryThenReauthorize() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.minus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), expiresInOneMinAccessToken);\n\t\t// Shorten the lifespan of the access token by 90 seconds, which will ultimately\n\t\t// force it to expire on the client\n\t\tthis.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/ClientCredentialsReactiveOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link ClientCredentialsReactiveOAuth2AuthorizedClientProvider}.\n *\n * @author Joe Grandja\n */\npublic class ClientCredentialsReactiveOAuth2AuthorizedClientProviderTests {\n\n\tprivate ClientCredentialsReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientProvider = new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();\n\t\tthis.accessTokenResponseClient = mock(ReactiveOAuth2AccessTokenResponseClient.class);\n\t\tthis.authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials().build();\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseClientWhenClientIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAccessTokenResponseClient(null))\n\t\t\t\t.withMessage(\"accessTokenResponseClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(null))\n\t\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(-1)))\n\t\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(null).block())\n\t\t\t\t.withMessage(\"context cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotClientCredentialsThenUnableToAuthorize() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAndNotAuthorizedThenAuthorize() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAndTokenExpiredThenReauthorize() {\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(60));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), accessToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAndTokenNotExpiredThenNotReauthorize() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t// gh-7511\n\t@Test\n\tpublic void authorizeWhenClientCredentialsAndTokenNotExpiredButClockSkewForcesExpiryThenReauthorize() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.minus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), expiresInOneMinAccessToken);\n\t\t// Shorten the lifespan of the access token by 90 seconds, which will ultimately\n\t\t// force it to expire on the client\n\t\tthis.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext)\n\t\t\t.block();\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/DelegatingOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link DelegatingOAuth2AuthorizedClientProvider}.\n *\n * @author Joe Grandja\n */\npublic class DelegatingOAuth2AuthorizedClientProviderTests {\n\n\t@Test\n\tpublic void constructorWhenProvidersIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingOAuth2AuthorizedClientProvider(new OAuth2AuthorizedClientProvider[0]));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingOAuth2AuthorizedClientProvider(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\tDelegatingOAuth2AuthorizedClientProvider delegate = new DelegatingOAuth2AuthorizedClientProvider(\n\t\t\t\tmock(OAuth2AuthorizedClientProvider.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> delegate.authorize(null))\n\t\t\t.withMessage(\"context cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenProviderCanAuthorizeThenReturnAuthorizedClient() {\n\t\tAuthentication principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider = mock(OAuth2AuthorizedClientProvider.class);\n\t\tgiven(authorizedClientProvider.authorize(any())).willReturn(authorizedClient);\n\t\tDelegatingOAuth2AuthorizedClientProvider delegate = new DelegatingOAuth2AuthorizedClientProvider(\n\t\t\t\tmock(OAuth2AuthorizedClientProvider.class), mock(OAuth2AuthorizedClientProvider.class),\n\t\t\t\tauthorizedClientProvider);\n\t\tOAuth2AuthorizationContext context = OAuth2AuthorizationContext.withClientRegistration(clientRegistration)\n\t\t\t.principal(principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient reauthorizedClient = delegate.authorize(context);\n\t\tassertThat(reauthorizedClient).isSameAs(authorizedClient);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenProviderCantAuthorizeThenReturnNull() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2AuthorizationContext context = OAuth2AuthorizationContext.withClientRegistration(clientRegistration)\n\t\t\t.principal(new TestingAuthenticationToken(\"principal\", \"password\"))\n\t\t\t.build();\n\t\tDelegatingOAuth2AuthorizedClientProvider delegate = new DelegatingOAuth2AuthorizedClientProvider(\n\t\t\t\tmock(OAuth2AuthorizedClientProvider.class), mock(OAuth2AuthorizedClientProvider.class));\n\t\tassertThat(delegate.authorize(context)).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/DelegatingReactiveOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link DelegatingReactiveOAuth2AuthorizedClientProvider}.\n *\n * @author Joe Grandja\n */\npublic class DelegatingReactiveOAuth2AuthorizedClientProviderTests {\n\n\t@Test\n\tpublic void constructorWhenProvidersIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingReactiveOAuth2AuthorizedClientProvider(\n\t\t\t\tnew ReactiveOAuth2AuthorizedClientProvider[0]));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingReactiveOAuth2AuthorizedClientProvider(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\tDelegatingReactiveOAuth2AuthorizedClientProvider delegate = new DelegatingReactiveOAuth2AuthorizedClientProvider(\n\t\t\t\tmock(ReactiveOAuth2AuthorizedClientProvider.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> delegate.authorize(null).block())\n\t\t\t.withMessage(\"context cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenProviderCanAuthorizeThenReturnAuthorizedClient() {\n\t\tAuthentication principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider1 = mock(\n\t\t\t\tReactiveOAuth2AuthorizedClientProvider.class);\n\t\tgiven(authorizedClientProvider1.authorize(any())).willReturn(Mono.empty());\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider2 = mock(\n\t\t\t\tReactiveOAuth2AuthorizedClientProvider.class);\n\t\tgiven(authorizedClientProvider2.authorize(any())).willReturn(Mono.empty());\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider3 = mock(\n\t\t\t\tReactiveOAuth2AuthorizedClientProvider.class);\n\t\tgiven(authorizedClientProvider3.authorize(any())).willReturn(Mono.just(authorizedClient));\n\t\tDelegatingReactiveOAuth2AuthorizedClientProvider delegate = new DelegatingReactiveOAuth2AuthorizedClientProvider(\n\t\t\t\tauthorizedClientProvider1, authorizedClientProvider2, authorizedClientProvider3);\n\t\tOAuth2AuthorizationContext context = OAuth2AuthorizationContext.withClientRegistration(clientRegistration)\n\t\t\t.principal(principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient reauthorizedClient = delegate.authorize(context).block();\n\t\tassertThat(reauthorizedClient).isSameAs(authorizedClient);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenProviderCantAuthorizeThenReturnNull() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2AuthorizationContext context = OAuth2AuthorizationContext.withClientRegistration(clientRegistration)\n\t\t\t.principal(new TestingAuthenticationToken(\"principal\", \"password\"))\n\t\t\t.build();\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider1 = mock(\n\t\t\t\tReactiveOAuth2AuthorizedClientProvider.class);\n\t\tgiven(authorizedClientProvider1.authorize(any())).willReturn(Mono.empty());\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider2 = mock(\n\t\t\t\tReactiveOAuth2AuthorizedClientProvider.class);\n\t\tgiven(authorizedClientProvider2.authorize(any())).willReturn(Mono.empty());\n\t\tDelegatingReactiveOAuth2AuthorizedClientProvider delegate = new DelegatingReactiveOAuth2AuthorizedClientProvider(\n\t\t\t\tauthorizedClientProvider1, authorizedClientProvider2);\n\t\tassertThat(delegate.authorize(context).block()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/InMemoryOAuth2AuthorizedClientServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatObject;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\n\n/**\n * Tests for {@link InMemoryOAuth2AuthorizedClientService}.\n *\n * @author Joe Grandja\n * @author Vedran Pavic\n */\npublic class InMemoryOAuth2AuthorizedClientServiceTests {\n\n\tprivate String principalName1 = \"principal-1\";\n\n\tprivate String principalName2 = \"principal-2\";\n\n\tprivate ClientRegistration registration1 = TestClientRegistrations.clientRegistration().build();\n\n\tprivate ClientRegistration registration2 = TestClientRegistrations.clientRegistration2().build();\n\n\tprivate ClientRegistration registration3 = TestClientRegistrations.clientRegistration()\n\t\t.clientId(\"client-3\")\n\t\t.registrationId(\"registration-3\")\n\t\t.build();\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository = new InMemoryClientRegistrationRepository(\n\t\t\tthis.registration1, this.registration2, this.registration3);\n\n\tprivate InMemoryOAuth2AuthorizedClientService authorizedClientService = new InMemoryOAuth2AuthorizedClientService(\n\t\t\tthis.clientRegistrationRepository);\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new InMemoryOAuth2AuthorizedClientService(null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository, null))\n\t\t\t\t.withMessage(\"authorizedClients cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientsProvidedThenUseProvidedAuthorizedClients() {\n\t\tString registrationId = this.registration3.getRegistrationId();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration3, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tMap<OAuth2AuthorizedClientId, OAuth2AuthorizedClient> authorizedClients = Collections.singletonMap(\n\t\t\t\tnew OAuth2AuthorizedClientId(this.registration3.getRegistrationId(), this.principalName1),\n\t\t\t\tauthorizedClient);\n\t\tClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(eq(registrationId))).willReturn(this.registration3);\n\t\tInMemoryOAuth2AuthorizedClientService authorizedClientService = new InMemoryOAuth2AuthorizedClientService(\n\t\t\t\tclientRegistrationRepository, authorizedClients);\n\t\tassertThatObject(authorizedClientService.loadAuthorizedClient(registrationId, this.principalName1)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(null, this.principalName1));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.authorizedClientService.loadAuthorizedClient(this.registration1.getRegistrationId(), null));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationNotFoundThenReturnNull() {\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(\"registration-not-found\", this.principalName1);\n\t\tassertThat(authorizedClient).isNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationFoundButNotAssociatedToPrincipalThenReturnNull() {\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), \"principal-not-found\");\n\t\tassertThat(authorizedClient).isNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationFoundAndAssociatedToPrincipalThenReturnAuthorizedClient() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tgiven(authentication.getName()).willReturn(this.principalName1);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, authentication);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), this.principalName1);\n\t\tassertThat(loadedAuthorizedClient).satisfies(isEqualTo(authorizedClient));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIsUpdatedThenReturnAuthorizedClientWithUpdatedClientRegistration() {\n\t\tClientRegistration updatedRegistration = ClientRegistration.withClientRegistration(this.registration1)\n\t\t\t.clientSecret(\"updated secret\")\n\t\t\t.build();\n\n\t\tClientRegistrationRepository clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tgiven(clientRegistrationRepository.findByRegistrationId(this.registration1.getRegistrationId()))\n\t\t\t.willReturn(this.registration1, updatedRegistration);\n\n\t\tInMemoryOAuth2AuthorizedClientService authorizedClientService = new InMemoryOAuth2AuthorizedClientService(\n\t\t\t\tclientRegistrationRepository);\n\n\t\tOAuth2AuthorizedClient cachedAuthorizedClient = new OAuth2AuthorizedClient(this.registration1,\n\t\t\t\tthis.principalName1, mock(OAuth2AccessToken.class), mock(OAuth2RefreshToken.class));\n\t\tauthorizedClientService.saveAuthorizedClient(cachedAuthorizedClient,\n\t\t\t\tnew TestingAuthenticationToken(this.principalName1, null));\n\n\t\tOAuth2AuthorizedClient authorizedClientWithUpdatedRegistration = new OAuth2AuthorizedClient(updatedRegistration,\n\t\t\t\tthis.principalName1, mock(OAuth2AccessToken.class), mock(OAuth2RefreshToken.class));\n\t\tOAuth2AuthorizedClient firstLoadedClient = authorizedClientService\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), this.principalName1);\n\t\tOAuth2AuthorizedClient secondLoadedClient = authorizedClientService\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), this.principalName1);\n\t\tassertThat(firstLoadedClient).satisfies(isEqualTo(cachedAuthorizedClient));\n\t\tassertThat(secondLoadedClient).satisfies(isEqualTo(authorizedClientWithUpdatedRegistration));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthorizedClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(null, mock(Authentication.class)));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenPrincipalIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.authorizedClientService.saveAuthorizedClient(mock(OAuth2AuthorizedClient.class), null));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenSavedThenCanLoad() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tgiven(authentication.getName()).willReturn(this.principalName2);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration3, this.principalName2,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, authentication);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.registration3.getRegistrationId(), this.principalName2);\n\t\tassertThat(loadedAuthorizedClient).satisfies(isEqualTo(authorizedClient));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientService.removeAuthorizedClient(null, this.principalName2));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientService\n\t\t\t.removeAuthorizedClient(this.registration3.getRegistrationId(), null));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenSavedThenRemoved() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tgiven(authentication.getName()).willReturn(this.principalName2);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName2,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, authentication);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.registration2.getRegistrationId(), this.principalName2);\n\t\tassertThat(loadedAuthorizedClient).isNotNull();\n\t\tthis.authorizedClientService.removeAuthorizedClient(this.registration2.getRegistrationId(),\n\t\t\t\tthis.principalName2);\n\t\tloadedAuthorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.registration2.getRegistrationId(), this.principalName2);\n\t\tassertThat(loadedAuthorizedClient).isNull();\n\t}\n\n\tprivate static Consumer<OAuth2AuthorizedClient> isEqualTo(OAuth2AuthorizedClient expected) {\n\t\treturn (actual) -> {\n\t\t\tassertThat(actual).isNotNull();\n\t\t\tassertThat(actual.getClientRegistration().getRegistrationId())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getRegistrationId());\n\t\t\tassertThat(actual.getClientRegistration().getClientName())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getClientName());\n\t\t\tassertThat(actual.getClientRegistration().getRedirectUri())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getRedirectUri());\n\t\t\tassertThat(actual.getClientRegistration().getAuthorizationGrantType())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getAuthorizationGrantType());\n\t\t\tassertThat(actual.getClientRegistration().getClientAuthenticationMethod())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getClientAuthenticationMethod());\n\t\t\tassertThat(actual.getClientRegistration().getClientId())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getClientId());\n\t\t\tassertThat(actual.getClientRegistration().getClientSecret())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getClientSecret());\n\t\t\tassertThat(actual.getPrincipalName()).isEqualTo(expected.getPrincipalName());\n\t\t\tassertThat(actual.getAccessToken().getTokenType()).isEqualTo(expected.getAccessToken().getTokenType());\n\t\t\tassertThat(actual.getAccessToken().getTokenValue()).isEqualTo(expected.getAccessToken().getTokenValue());\n\t\t\tassertThat(actual.getAccessToken().getIssuedAt()).isEqualTo(expected.getAccessToken().getIssuedAt());\n\t\t\tassertThat(actual.getAccessToken().getExpiresAt()).isEqualTo(expected.getAccessToken().getExpiresAt());\n\t\t\tassertThat(actual.getAccessToken().getScopes()).isEqualTo(expected.getAccessToken().getScopes());\n\t\t\tif (expected.getRefreshToken() != null) {\n\t\t\t\tassertThat(actual.getRefreshToken()).isNotNull();\n\t\t\t\tassertThat(actual.getRefreshToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(expected.getRefreshToken().getTokenValue());\n\t\t\t\tassertThat(actual.getRefreshToken().getIssuedAt()).isEqualTo(expected.getRefreshToken().getIssuedAt());\n\t\t\t\tassertThat(actual.getRefreshToken().getExpiresAt())\n\t\t\t\t\t.isEqualTo(expected.getRefreshToken().getExpiresAt());\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/InMemoryReactiveOAuth2AuthorizedClientServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class InMemoryReactiveOAuth2AuthorizedClientServiceTests {\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate InMemoryReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate String clientRegistrationId = \"github\";\n\n\tprivate String principalName = \"username\";\n\n\tprivate Authentication principal = new TestingAuthenticationToken(this.principalName, \"notused\");\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate OAuth2RefreshToken refreshToken;\n\n\t// @formatter:off\n\tprivate ClientRegistration clientRegistration = ClientRegistration.withRegistrationId(this.clientRegistrationId)\n\t\t\t.redirectUri(\"{baseUrl}/{action}/oauth2/code/{registrationId}\")\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.scope(\"read:user\")\n\t\t\t.authorizationUri(\"https://github.com/login/oauth/authorize\")\n\t\t\t.tokenUri(\"https://github.com/login/oauth/access_token\")\n\t\t\t.userInfoUri(\"https://api.github.com/user\")\n\t\t\t.userNameAttributeName(\"id\")\n\t\t\t.clientName(\"GitHub\")\n\t\t\t.clientId(\"clientId\")\n\t\t\t.clientSecret(\"clientSecret\")\n\t\t\t.build();\n\t// @formatter:on\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientService = new InMemoryReactiveOAuth2AuthorizedClientService(\n\t\t\t\tthis.clientRegistrationRepository);\n\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofDays(1));\n\t\tthis.accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\", issuedAt, expiresAt);\n\t\tthis.refreshToken = new OAuth2RefreshToken(\"refresh\", issuedAt, expiresAt);\n\t}\n\n\t@Test\n\tpublic void constructorNullClientRegistrationRepositoryThenThrowsIllegalArgumentException() {\n\t\tthis.clientRegistrationRepository = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryReactiveOAuth2AuthorizedClientService(this.clientRegistrationRepository));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIdNullThenIllegalArgumentException() {\n\t\tthis.clientRegistrationId = null;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIdEmptyThenIllegalArgumentException() {\n\t\tthis.clientRegistrationId = \"\";\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenPrincipalNameNullThenIllegalArgumentException() {\n\t\tthis.principalName = null;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenPrincipalNameEmptyThenIllegalArgumentException() {\n\t\tthis.principalName = \"\";\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIdNotFoundThenEmpty() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(this.clientRegistrationId))\n\t\t\t.willReturn(Mono.empty());\n\t\tStepVerifier\n\t\t\t.create(this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName))\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationFoundAndNotAuthorizedClientThenEmpty() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(this.clientRegistrationId))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tStepVerifier\n\t\t\t.create(this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName))\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationFoundThenFound() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(this.clientRegistrationId))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principalName, this.accessToken);\n\t\t// @formatter:off\n\t\tMono<OAuth2AuthorizedClient> saveAndLoad = this.authorizedClientService\n\t\t\t\t.saveAuthorizedClient(authorizedClient, this.principal)\n\t\t\t\t.then(this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\tStepVerifier.create(saveAndLoad)\n\t\t\t\t.assertNext(isEqualTo(authorizedClient))\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void loadAuthorizedClientWhenClientRegistrationIsUpdatedThenReturnsAuthorizedClientWithUpdatedClientRegistration() {\n\t\tClientRegistration updatedRegistration = ClientRegistration.withClientRegistration(this.clientRegistration)\n\t\t\t.clientSecret(\"updated secret\")\n\t\t\t.build();\n\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(this.clientRegistrationId))\n\t\t\t.willReturn(Mono.just(this.clientRegistration), Mono.just(updatedRegistration));\n\n\t\tOAuth2AuthorizedClient cachedAuthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principalName, this.accessToken, this.refreshToken);\n\t\tOAuth2AuthorizedClient authorizedClientWithChangedRegistration = new OAuth2AuthorizedClient(updatedRegistration,\n\t\t\t\tthis.principalName, this.accessToken, this.refreshToken);\n\n\t\tFlux<OAuth2AuthorizedClient> saveAndLoadTwice = this.authorizedClientService\n\t\t\t.saveAuthorizedClient(cachedAuthorizedClient, this.principal)\n\t\t\t.then(this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName))\n\t\t\t.concatWith(\n\t\t\t\t\tthis.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\tStepVerifier.create(saveAndLoadTwice)\n\t\t\t.assertNext(isEqualTo(cachedAuthorizedClient))\n\t\t\t.assertNext(isEqualTo(authorizedClientWithChangedRegistration))\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthorizedClientNullThenIllegalArgumentException() {\n\t\tOAuth2AuthorizedClient authorizedClient = null;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, this.principal));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenPrincipalNullThenIllegalArgumentException() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principalName, this.accessToken);\n\t\tthis.principal = null;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, this.principal));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientRegistrationIdNullThenIllegalArgumentException() {\n\t\tthis.clientRegistrationId = null;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientRegistrationIdEmptyThenIllegalArgumentException() {\n\t\tthis.clientRegistrationId = \"\";\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenPrincipalNameNullThenIllegalArgumentException() {\n\t\tthis.principalName = null;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.removeAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenPrincipalNameEmptyThenIllegalArgumentException() {\n\t\tthis.principalName = \"\";\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.removeAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientIdThenNoException() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(this.clientRegistrationId))\n\t\t\t.willReturn(Mono.empty());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principalName, this.accessToken);\n\t\t// @formatter:off\n\t\tMono<Void> saveAndDeleteAndLoad = this.authorizedClientService.saveAuthorizedClient(authorizedClient, this.principal)\n\t\t\t\t.then(this.authorizedClientService\n\t\t\t\t\t\t.removeAuthorizedClient(this.clientRegistrationId, this.principalName)\n\t\t\t\t);\n\t\tStepVerifier.create(saveAndDeleteAndLoad)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientRegistrationFoundRemovedThenNotFound() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(this.clientRegistrationId))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principalName, this.accessToken);\n\t\t// @formatter:off\n\t\tMono<OAuth2AuthorizedClient> saveAndDeleteAndLoad = this.authorizedClientService.saveAuthorizedClient(authorizedClient, this.principal)\n\t\t\t\t.then(this.authorizedClientService.removeAuthorizedClient(this.clientRegistrationId,\n\t\t\t\t\t\tthis.principalName))\n\t\t\t\t.then(this.authorizedClientService.loadAuthorizedClient(this.clientRegistrationId, this.principalName));\n\t\tStepVerifier.create(saveAndDeleteAndLoad)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\tprivate static Consumer<OAuth2AuthorizedClient> isEqualTo(OAuth2AuthorizedClient expected) {\n\t\treturn (actual) -> {\n\t\t\tassertThat(actual).isNotNull();\n\t\t\tassertThat(actual.getClientRegistration().getRegistrationId())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getRegistrationId());\n\t\t\tassertThat(actual.getClientRegistration().getClientName())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getClientName());\n\t\t\tassertThat(actual.getClientRegistration().getRedirectUri())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getRedirectUri());\n\t\t\tassertThat(actual.getClientRegistration().getAuthorizationGrantType())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getAuthorizationGrantType());\n\t\t\tassertThat(actual.getClientRegistration().getClientAuthenticationMethod())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getClientAuthenticationMethod());\n\t\t\tassertThat(actual.getClientRegistration().getClientId())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getClientId());\n\t\t\tassertThat(actual.getClientRegistration().getClientSecret())\n\t\t\t\t.isEqualTo(expected.getClientRegistration().getClientSecret());\n\t\t\tassertThat(actual.getPrincipalName()).isEqualTo(expected.getPrincipalName());\n\t\t\tassertThat(actual.getAccessToken().getTokenType()).isEqualTo(expected.getAccessToken().getTokenType());\n\t\t\tassertThat(actual.getAccessToken().getTokenValue()).isEqualTo(expected.getAccessToken().getTokenValue());\n\t\t\tassertThat(actual.getAccessToken().getIssuedAt()).isEqualTo(expected.getAccessToken().getIssuedAt());\n\t\t\tassertThat(actual.getAccessToken().getExpiresAt()).isEqualTo(expected.getAccessToken().getExpiresAt());\n\t\t\tassertThat(actual.getAccessToken().getScopes()).isEqualTo(expected.getAccessToken().getScopes());\n\t\t\tif (expected.getRefreshToken() != null) {\n\t\t\t\tassertThat(actual.getRefreshToken()).isNotNull();\n\t\t\t\tassertThat(actual.getRefreshToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(expected.getRefreshToken().getTokenValue());\n\t\t\t\tassertThat(actual.getRefreshToken().getIssuedAt()).isEqualTo(expected.getRefreshToken().getIssuedAt());\n\t\t\t\tassertThat(actual.getRefreshToken().getExpiresAt())\n\t\t\t\t\t.isEqualTo(expected.getRefreshToken().getExpiresAt());\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.nio.charset.StandardCharsets;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.within;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link JdbcOAuth2AuthorizedClientService}.\n *\n * @author Joe Grandja\n * @author Stav Shamir\n */\npublic class JdbcOAuth2AuthorizedClientServiceTests {\n\n\tprivate static final String OAUTH2_CLIENT_SCHEMA_SQL_RESOURCE = \"org/springframework/security/oauth2/client/oauth2-client-schema.sql\";\n\n\tprivate static int principalId = 1000;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate EmbeddedDatabase db;\n\n\tprivate JdbcOperations jdbcOperations;\n\n\tprivate JdbcOAuth2AuthorizedClientService authorizedClientService;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(this.clientRegistration);\n\t\tthis.db = createDb();\n\t\tthis.jdbcOperations = new JdbcTemplate(this.db);\n\t\tthis.authorizedClientService = new JdbcOAuth2AuthorizedClientService(this.jdbcOperations,\n\t\t\t\tthis.clientRegistrationRepository);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.db.shutdown();\n\t}\n\n\t@Test\n\tpublic void constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new JdbcOAuth2AuthorizedClientService(null, this.clientRegistrationRepository))\n\t\t\t\t.withMessage(\"jdbcOperations cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new JdbcOAuth2AuthorizedClientService(this.jdbcOperations, null))\n\t\t\t.withMessage(\"clientRegistrationRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientRowMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.setAuthorizedClientRowMapper(null))\n\t\t\t\t.withMessage(\"authorizedClientRowMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientParametersMapperWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.setAuthorizedClientParametersMapper(null))\n\t\t\t\t.withMessage(\"authorizedClientParametersMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(null, \"principalName\"))\n\t\t\t\t.withMessage(\"clientRegistrationId cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientService\n\t\t\t\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), null))\n\t\t\t\t.withMessage(\"principalName cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenDoesNotExistThenReturnNull() {\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(\"registration-not-found\", \"principalName\");\n\t\tassertThat(authorizedClient).isNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenExistsThenReturnAuthorizedClient() {\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(expected, principal);\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName());\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(expected.getClientRegistration());\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expected.getPrincipalName());\n\t\tassertThat(authorizedClient.getAccessToken().getTokenType())\n\t\t\t.isEqualTo(expected.getAccessToken().getTokenType());\n\t\tassertThat(authorizedClient.getAccessToken().getTokenValue())\n\t\t\t.isEqualTo(expected.getAccessToken().getTokenValue());\n\t\tassertThat(authorizedClient.getAccessToken().getIssuedAt()).isCloseTo(expected.getAccessToken().getIssuedAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\tassertThat(authorizedClient.getAccessToken().getExpiresAt()).isCloseTo(expected.getAccessToken().getExpiresAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\tassertThat(authorizedClient.getAccessToken().getScopes()).isEqualTo(expected.getAccessToken().getScopes());\n\t\tassertThat(authorizedClient.getRefreshToken().getTokenValue())\n\t\t\t.isEqualTo(expected.getRefreshToken().getTokenValue());\n\t\tassertThat(authorizedClient.getRefreshToken().getIssuedAt()).isCloseTo(expected.getRefreshToken().getIssuedAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenExistsButNotFoundInClientRegistrationRepositoryThenThrowDataRetrievalFailureException() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(null);\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(expected, principal);\n\t\tassertThatExceptionOfType(DataRetrievalFailureException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService\n\t\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName()))\n\t\t\t.withMessage(\"The ClientRegistration with id '\" + this.clientRegistration.getRegistrationId()\n\t\t\t\t\t+ \"' exists in the data source, however, it was not found in the ClientRegistrationRepository.\");\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthorizedClientIsNullThenThrowIllegalArgumentException() {\n\t\tAuthentication principal = createPrincipal();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(null, principal))\n\t\t\t.withMessage(\"authorizedClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenPrincipalIsNullThenThrowIllegalArgumentException() {\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, null))\n\t\t\t.withMessage(\"principal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenSaveThenLoadReturnsSaved() {\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(expected, principal);\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName());\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(expected.getClientRegistration());\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expected.getPrincipalName());\n\t\tassertThat(authorizedClient.getAccessToken().getTokenType())\n\t\t\t.isEqualTo(expected.getAccessToken().getTokenType());\n\t\tassertThat(authorizedClient.getAccessToken().getTokenValue())\n\t\t\t.isEqualTo(expected.getAccessToken().getTokenValue());\n\t\tassertThat(authorizedClient.getAccessToken().getIssuedAt()).isCloseTo(expected.getAccessToken().getIssuedAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\tassertThat(authorizedClient.getAccessToken().getExpiresAt()).isCloseTo(expected.getAccessToken().getExpiresAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\tassertThat(authorizedClient.getAccessToken().getScopes()).isEqualTo(expected.getAccessToken().getScopes());\n\t\tassertThat(authorizedClient.getRefreshToken().getTokenValue())\n\t\t\t.isEqualTo(expected.getRefreshToken().getTokenValue());\n\t\tassertThat(authorizedClient.getRefreshToken().getIssuedAt()).isCloseTo(expected.getRefreshToken().getIssuedAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\t// Test save/load of NOT NULL attributes only\n\t\tprincipal = createPrincipal();\n\t\texpected = createAuthorizedClient(principal, this.clientRegistration, true);\n\t\tthis.authorizedClientService.saveAuthorizedClient(expected, principal);\n\t\tauthorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName());\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(expected.getClientRegistration());\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expected.getPrincipalName());\n\t\tassertThat(authorizedClient.getAccessToken().getTokenType())\n\t\t\t.isEqualTo(expected.getAccessToken().getTokenType());\n\t\tassertThat(authorizedClient.getAccessToken().getTokenValue())\n\t\t\t.isEqualTo(expected.getAccessToken().getTokenValue());\n\t\tassertThat(authorizedClient.getAccessToken().getIssuedAt()).isCloseTo(expected.getAccessToken().getIssuedAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\tassertThat(authorizedClient.getAccessToken().getExpiresAt()).isCloseTo(expected.getAccessToken().getExpiresAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\tassertThat(authorizedClient.getAccessToken().getScopes()).isEmpty();\n\t\tassertThat(authorizedClient.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenSaveClientWithExistingPrimaryKeyThenUpdate() {\n\t\t// Given a saved authorized client\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);\n\t\t// When a client with the same principal and registration id is saved\n\t\tOAuth2AuthorizedClient updatedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(updatedClient, principal);\n\t\t// Then the saved client is updated\n\t\tOAuth2AuthorizedClient savedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName());\n\t\tassertThat(savedClient).isNotNull();\n\t\tassertThat(savedClient.getClientRegistration()).isEqualTo(updatedClient.getClientRegistration());\n\t\tassertThat(savedClient.getPrincipalName()).isEqualTo(updatedClient.getPrincipalName());\n\t\tassertThat(savedClient.getAccessToken().getTokenType())\n\t\t\t.isEqualTo(updatedClient.getAccessToken().getTokenType());\n\t\tassertThat(savedClient.getAccessToken().getTokenValue())\n\t\t\t.isEqualTo(updatedClient.getAccessToken().getTokenValue());\n\t\tassertThat(savedClient.getAccessToken().getIssuedAt()).isCloseTo(updatedClient.getAccessToken().getIssuedAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\tassertThat(savedClient.getAccessToken().getExpiresAt()).isCloseTo(updatedClient.getAccessToken().getExpiresAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t\tassertThat(savedClient.getAccessToken().getScopes()).isEqualTo(updatedClient.getAccessToken().getScopes());\n\t\tassertThat(savedClient.getRefreshToken().getTokenValue())\n\t\t\t.isEqualTo(updatedClient.getRefreshToken().getTokenValue());\n\t\tassertThat(savedClient.getRefreshToken().getIssuedAt()).isCloseTo(updatedClient.getRefreshToken().getIssuedAt(),\n\t\t\t\twithin(1, ChronoUnit.MILLIS));\n\t}\n\n\t@Test\n\tpublic void saveLoadAuthorizedClientWhenCustomStrategiesSetThenCalled() throws Exception {\n\t\tJdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientRowMapper authorizedClientRowMapper = spy(\n\t\t\t\tnew JdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientRowMapper(\n\t\t\t\t\t\tthis.clientRegistrationRepository));\n\t\tthis.authorizedClientService.setAuthorizedClientRowMapper(authorizedClientRowMapper);\n\t\tJdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientParametersMapper authorizedClientParametersMapper = spy(\n\t\t\t\tnew JdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientParametersMapper());\n\t\tthis.authorizedClientService.setAuthorizedClientParametersMapper(authorizedClientParametersMapper);\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);\n\t\tthis.authorizedClientService.loadAuthorizedClient(this.clientRegistration.getRegistrationId(),\n\t\t\t\tprincipal.getName());\n\t\tverify(authorizedClientRowMapper).mapRow(any(), anyInt());\n\t\tverify(authorizedClientParametersMapper, atLeastOnce()).apply(any());\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientService.removeAuthorizedClient(null, \"principalName\"))\n\t\t\t.withMessage(\"clientRegistrationId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientService\n\t\t\t\t.removeAuthorizedClient(this.clientRegistration.getRegistrationId(), null))\n\t\t\t.withMessage(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenExistsThenRemoved() {\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);\n\t\tauthorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName());\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tthis.authorizedClientService.removeAuthorizedClient(this.clientRegistration.getRegistrationId(),\n\t\t\t\tprincipal.getName());\n\t\tauthorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName());\n\t\tassertThat(authorizedClient).isNull();\n\t}\n\n\t@Test\n\tpublic void tableDefinitionWhenCustomThenAbleToOverride() {\n\t\tCustomTableDefinitionJdbcOAuth2AuthorizedClientService customAuthorizedClientService = new CustomTableDefinitionJdbcOAuth2AuthorizedClientService(\n\t\t\t\tnew JdbcTemplate(createDb(\"custom-oauth2-client-schema.sql\")), this.clientRegistrationRepository);\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tcustomAuthorizedClientService.saveAuthorizedClient(authorizedClient, principal);\n\t\tauthorizedClient = customAuthorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName());\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tcustomAuthorizedClientService.removeAuthorizedClient(this.clientRegistration.getRegistrationId(),\n\t\t\t\tprincipal.getName());\n\t\tauthorizedClient = customAuthorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName());\n\t\tassertThat(authorizedClient).isNull();\n\t}\n\n\tprivate static EmbeddedDatabase createDb() {\n\t\treturn createDb(OAUTH2_CLIENT_SCHEMA_SQL_RESOURCE);\n\t}\n\n\tprivate static EmbeddedDatabase createDb(String schema) {\n\t\t// @formatter:off\n\t\treturn new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(schema)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static Authentication createPrincipal() {\n\t\treturn new TestingAuthenticationToken(\"principal-\" + principalId++, \"password\");\n\t}\n\n\tprivate static OAuth2AuthorizedClient createAuthorizedClient(Authentication principal,\n\t\t\tClientRegistration clientRegistration) {\n\t\treturn createAuthorizedClient(principal, clientRegistration, false);\n\t}\n\n\tprivate static OAuth2AuthorizedClient createAuthorizedClient(Authentication principal,\n\t\t\tClientRegistration clientRegistration, boolean requiredAttributesOnly) {\n\t\tOAuth2AccessToken accessToken;\n\t\tif (!requiredAttributesOnly) {\n\t\t\taccessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\t}\n\t\telse {\n\t\t\taccessToken = TestOAuth2AccessTokens.noScopes();\n\t\t}\n\t\tOAuth2RefreshToken refreshToken = null;\n\t\tif (!requiredAttributesOnly) {\n\t\t\trefreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\t}\n\t\treturn new OAuth2AuthorizedClient(clientRegistration, principal.getName(), accessToken, refreshToken);\n\t}\n\n\tprivate static final class CustomTableDefinitionJdbcOAuth2AuthorizedClientService\n\t\t\textends JdbcOAuth2AuthorizedClientService {\n\n\t\tprivate static final String COLUMN_NAMES = \"clientRegistrationId, \" + \"principalName, \" + \"accessTokenType, \"\n\t\t\t\t+ \"accessTokenValue, \" + \"accessTokenIssuedAt, \" + \"accessTokenExpiresAt, \" + \"accessTokenScopes, \"\n\t\t\t\t+ \"refreshTokenValue, \" + \"refreshTokenIssuedAt\";\n\n\t\tprivate static final String TABLE_NAME = \"oauth2AuthorizedClient\";\n\n\t\tprivate static final String PK_FILTER = \"clientRegistrationId = ? AND principalName = ?\";\n\n\t\tprivate static final String LOAD_AUTHORIZED_CLIENT_SQL = \"SELECT \" + COLUMN_NAMES + \" FROM \" + TABLE_NAME\n\t\t\t\t+ \" WHERE \" + PK_FILTER;\n\n\t\tprivate static final String SAVE_AUTHORIZED_CLIENT_SQL = \"INSERT INTO \" + TABLE_NAME + \" (\" + COLUMN_NAMES\n\t\t\t\t+ \") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\";\n\n\t\tprivate static final String REMOVE_AUTHORIZED_CLIENT_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + PK_FILTER;\n\n\t\tprivate CustomTableDefinitionJdbcOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,\n\t\t\t\tClientRegistrationRepository clientRegistrationRepository) {\n\t\t\tsuper(jdbcOperations, clientRegistrationRepository);\n\t\t\tsetAuthorizedClientRowMapper(new OAuth2AuthorizedClientRowMapper(clientRegistrationRepository));\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId,\n\t\t\t\tString principalName) {\n\t\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, clientRegistrationId),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, principalName) };\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\t\tList<OAuth2AuthorizedClient> result = this.jdbcOperations.query(LOAD_AUTHORIZED_CLIENT_SQL, pss,\n\t\t\t\t\tthis.authorizedClientRowMapper);\n\t\t\treturn !result.isEmpty() ? (T) result.get(0) : null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\t\tList<SqlParameterValue> parameters = this.authorizedClientParametersMapper\n\t\t\t\t.apply(new OAuth2AuthorizedClientHolder(authorizedClient, principal));\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\t\tthis.jdbcOperations.update(SAVE_AUTHORIZED_CLIENT_SQL, pss);\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeAuthorizedClient(String clientRegistrationId, String principalName) {\n\t\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, clientRegistrationId),\n\t\t\t\t\tnew SqlParameterValue(Types.VARCHAR, principalName) };\n\t\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\t\tthis.jdbcOperations.update(REMOVE_AUTHORIZED_CLIENT_SQL, pss);\n\t\t}\n\n\t\tprivate static final class OAuth2AuthorizedClientRowMapper implements RowMapper<OAuth2AuthorizedClient> {\n\n\t\t\tprivate final ClientRegistrationRepository clientRegistrationRepository;\n\n\t\t\tprivate OAuth2AuthorizedClientRowMapper(ClientRegistrationRepository clientRegistrationRepository) {\n\t\t\t\tAssert.notNull(clientRegistrationRepository, \"clientRegistrationRepository cannot be null\");\n\t\t\t\tthis.clientRegistrationRepository = clientRegistrationRepository;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic OAuth2AuthorizedClient mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\t\tString clientRegistrationId = rs.getString(\"clientRegistrationId\");\n\t\t\t\tClientRegistration clientRegistration = this.clientRegistrationRepository\n\t\t\t\t\t.findByRegistrationId(clientRegistrationId);\n\t\t\t\tif (clientRegistration == null) {\n\t\t\t\t\tthrow new DataRetrievalFailureException(\n\t\t\t\t\t\t\t\"The ClientRegistration with id '\" + clientRegistrationId + \"' exists in the data source, \"\n\t\t\t\t\t\t\t\t\t+ \"however, it was not found in the ClientRegistrationRepository.\");\n\t\t\t\t}\n\t\t\t\tOAuth2AccessToken.TokenType tokenType = null;\n\t\t\t\tif (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString(\"accessTokenType\"))) {\n\t\t\t\t\ttokenType = OAuth2AccessToken.TokenType.BEARER;\n\t\t\t\t}\n\t\t\t\tString tokenValue = new String(rs.getBytes(\"accessTokenValue\"), StandardCharsets.UTF_8);\n\t\t\t\tInstant issuedAt = rs.getTimestamp(\"accessTokenIssuedAt\").toInstant();\n\t\t\t\tInstant expiresAt = rs.getTimestamp(\"accessTokenExpiresAt\").toInstant();\n\t\t\t\tSet<String> scopes = Collections.emptySet();\n\t\t\t\tString accessTokenScopes = rs.getString(\"accessTokenScopes\");\n\t\t\t\tif (accessTokenScopes != null) {\n\t\t\t\t\tscopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);\n\t\t\t\t}\n\t\t\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, issuedAt, expiresAt,\n\t\t\t\t\t\tscopes);\n\t\t\t\tOAuth2RefreshToken refreshToken = null;\n\t\t\t\tbyte[] refreshTokenValue = rs.getBytes(\"refreshTokenValue\");\n\t\t\t\tif (refreshTokenValue != null) {\n\t\t\t\t\ttokenValue = new String(refreshTokenValue, StandardCharsets.UTF_8);\n\t\t\t\t\tissuedAt = null;\n\t\t\t\t\tTimestamp refreshTokenIssuedAt = rs.getTimestamp(\"refreshTokenIssuedAt\");\n\t\t\t\t\tif (refreshTokenIssuedAt != null) {\n\t\t\t\t\t\tissuedAt = refreshTokenIssuedAt.toInstant();\n\t\t\t\t\t}\n\t\t\t\t\trefreshToken = new OAuth2RefreshToken(tokenValue, issuedAt);\n\t\t\t\t}\n\t\t\t\tString principalName = rs.getString(\"principalName\");\n\t\t\t\treturn new OAuth2AuthorizedClient(clientRegistration, principalName, accessToken, refreshToken);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/JwtBearerOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link JwtBearerOAuth2AuthorizedClientProvider}.\n *\n * @author Hassene Laaribi\n * @author Joe Grandja\n */\npublic class JwtBearerOAuth2AuthorizedClientProviderTests {\n\n\tprivate JwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Jwt jwtAssertion;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();\n\t\tthis.accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\t// @formatter:off\n\t\tthis.clientRegistration = ClientRegistration.withRegistrationId(\"jwt-bearer\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.tokenUri(\"https://example.com/oauth2/token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.jwtAssertion = TestJwts.jwt().build();\n\t\tthis.principal = new TestingAuthenticationToken(this.jwtAssertion, this.jwtAssertion);\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseClientWhenClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAccessTokenResponseClient(null))\n\t\t\t.withMessage(\"accessTokenResponseClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setJwtAssertionResolverWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setJwtAssertionResolver(null))\n\t\t\t.withMessage(\"jwtAssertionResolver cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(null))\n\t\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(-1)))\n\t\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(null))\n\t\t\t\t.withMessage(\"context cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotJwtBearerThenUnableToAuthorize() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials().build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndTokenNotExpiredThenNotReauthorize() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.scopes(\"read\", \"write\"));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndTokenExpiredThenReauthorize() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.minus(Duration.ofMinutes(30));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), accessToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndTokenNotExpiredButClockSkewForcesExpiryThenReauthorize() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.plus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), expiresInOneMinAccessToken);\n\t\t// Shorten the lifespan of the access token by 90 seconds, which will ultimately\n\t\t// force it to expire on the client\n\t\tthis.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndNotAuthorizedAndJwtDoesNotResolveThenUnableToAuthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(new TestingAuthenticationToken(\"user\", \"password\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndNotAuthorizedAndJwtResolvesThenAuthorize() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenCustomJwtAssertionResolverSetThenUsed() {\n\t\tFunction<OAuth2AuthorizationContext, Jwt> jwtAssertionResolver = mock(Function.class);\n\t\tgiven(jwtAssertionResolver.apply(any())).willReturn(this.jwtAssertion);\n\t\tthis.authorizedClientProvider.setJwtAssertionResolver(jwtAssertionResolver);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tverify(jwtAssertionResolver).apply(any());\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/JwtBearerReactiveOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link JwtBearerReactiveOAuth2AuthorizedClientProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class JwtBearerReactiveOAuth2AuthorizedClientProviderTests {\n\n\tprivate JwtBearerReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Jwt jwtAssertion;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientProvider = new JwtBearerReactiveOAuth2AuthorizedClientProvider();\n\t\tthis.accessTokenResponseClient = mock(ReactiveOAuth2AccessTokenResponseClient.class);\n\t\tthis.authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\t// @formatter:off\n\t\tthis.clientRegistration = ClientRegistration.withRegistrationId(\"jwt-bearer\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.tokenUri(\"https://example.com/oauth2/token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.jwtAssertion = TestJwts.jwt().build();\n\t\tthis.principal = new TestingAuthenticationToken(this.jwtAssertion, this.jwtAssertion);\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseClientWhenClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAccessTokenResponseClient(null))\n\t\t\t.withMessage(\"accessTokenResponseClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setJwtAssertionResolverWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setJwtAssertionResolver(null))\n\t\t\t.withMessage(\"jwtAssertionResolver cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(null))\n\t\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(-1)))\n\t\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(null).block())\n\t\t\t\t.withMessage(\"context cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotJwtBearerThenUnableToAuthorize() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials().build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndTokenNotExpiredThenNotReauthorize() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndTokenExpiredThenReauthorize() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.minus(Duration.ofMinutes(30));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), accessToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClockSetThenCalled() {\n\t\tClock clock = mock(Clock.class);\n\t\tgiven(clock.instant()).willReturn(Instant.now());\n\t\tthis.authorizedClientProvider.setClock(clock);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t\tverify(clock).instant();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndTokenNotExpiredButClockSkewForcesExpiryThenReauthorize() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.plus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), expiresInOneMinAccessToken);\n\t\t// Shorten the lifespan of the access token by 90 seconds, which will ultimately\n\t\t// force it to expire on the client\n\t\tthis.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext)\n\t\t\t.block();\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndNotAuthorizedAndJwtDoesNotResolveThenUnableToAuthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(new TestingAuthenticationToken(\"user\", \"password\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenInvalidRequestThenThrowClientAuthorizationException() {\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(\n\t\t\t\tMono.error(new OAuth2AuthorizationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST))));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(authorizationContext).block())\n\t\t\t\t.withMessageContaining(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenJwtBearerAndNotAuthorizedAndJwtResolvesThenAuthorize() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenCustomJwtAssertionResolverSetThenUsed() {\n\t\tFunction<OAuth2AuthorizationContext, Mono<Jwt>> jwtAssertionResolver = mock(Function.class);\n\t\tgiven(jwtAssertionResolver.apply(any())).willReturn(Mono.just(this.jwtAssertion));\n\t\tthis.authorizedClientProvider.setJwtAssertionResolver(jwtAssertionResolver);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tverify(jwtAssertionResolver).apply(any());\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/MockResponses.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\nimport okhttp3.mockwebserver.MockResponse;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\n\n/**\n * @author Steve Riesenberg\n */\npublic final class MockResponses {\n\n\tprivate MockResponses() {\n\t}\n\n\tpublic static MockResponse json(String path) {\n\t\ttry {\n\t\t\tString json = new ClassPathResource(path).getContentAsString(StandardCharsets.UTF_8);\n\t\t\treturn new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setBody(json);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new RuntimeException(\"Unable to read %s as a classpath resource\".formatted(path), ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/OAuth2AuthorizationContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\n\n/**\n * Tests for {@link OAuth2AuthorizationContext}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationContextTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, \"principal\",\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"read\", \"write\"));\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t}\n\n\t@Test\n\tpublic void withClientRegistrationWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationContext.withClientRegistration(null).build())\n\t\t\t.withMessage(\"clientRegistration cannot be null\");\n\t}\n\n\t@Test\n\tpublic void withAuthorizedClientWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationContext.withAuthorizedClient(null).build())\n\t\t\t.withMessage(\"authorizedClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void withClientRegistrationWhenPrincipalIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationContext.withClientRegistration(this.clientRegistration).build())\n\t\t\t.withMessage(\"principal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void withAuthorizedClientWhenAllValuesProvidedThenAllValuesAreSet() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attributes) -> {\n\t\t\t\t\tattributes.put(\"attribute1\", \"value1\");\n\t\t\t\t\tattributes.put(\"attribute2\", \"value2\");\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(authorizationContext.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isSameAs(this.principal);\n\t\tassertThat(authorizationContext.getAttributes()).contains(entry(\"attribute1\", \"value1\"),\n\t\t\t\tentry(\"attribute2\", \"value2\"));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/OAuth2AuthorizeRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\n\n/**\n * Tests for {@link OAuth2AuthorizeRequest}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizeRequestTests {\n\n\tprivate ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\n\tprivate Authentication principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\n\tprivate OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.scopes(\"read\", \"write\"),\n\t\t\tTestOAuth2RefreshTokens.refreshToken());\n\n\t@Test\n\tpublic void withClientRegistrationIdWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizeRequest.withClientRegistrationId(null))\n\t\t\t.withMessage(\"clientRegistrationId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void withAuthorizedClientWhenAuthorizedClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizeRequest.withAuthorizedClient(null))\n\t\t\t.withMessage(\"authorizedClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void withClientRegistrationIdWhenPrincipalIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> OAuth2AuthorizeRequest.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t\t\t.build())\n\t\t\t.withMessage(\"principal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void withClientRegistrationIdWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> OAuth2AuthorizeRequest.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t\t\t.principal((String) null)\n\t\t\t\t\t\t.build())\n\t\t\t.withMessage(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void withClientRegistrationIdWhenAllValuesProvidedThenAllValuesAreSet() {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.attributes((attrs) -> {\n\t\t\t\tattrs.put(\"name1\", \"value1\");\n\t\t\t\tattrs.put(\"name2\", \"value2\");\n\t\t\t})\n\t\t\t.build();\n\t\tassertThat(authorizeRequest.getClientRegistrationId()).isEqualTo(this.clientRegistration.getRegistrationId());\n\t\tassertThat(authorizeRequest.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizeRequest.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizeRequest.getAttributes()).contains(entry(\"name1\", \"value1\"), entry(\"name2\", \"value2\"));\n\t}\n\n\t@Test\n\tpublic void withAuthorizedClientWhenAllValuesProvidedThenAllValuesAreSet() {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.attributes((attrs) -> {\n\t\t\t\tattrs.put(\"name1\", \"value1\");\n\t\t\t\tattrs.put(\"name2\", \"value2\");\n\t\t\t})\n\t\t\t.build();\n\t\tassertThat(authorizeRequest.getClientRegistrationId())\n\t\t\t.isEqualTo(this.authorizedClient.getClientRegistration().getRegistrationId());\n\t\tassertThat(authorizeRequest.getAuthorizedClient()).isEqualTo(this.authorizedClient);\n\t\tassertThat(authorizeRequest.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizeRequest.getAttributes()).contains(entry(\"name1\", \"value1\"), entry(\"name2\", \"value2\"));\n\t}\n\n\t@Test\n\tpublic void withClientRegistrationIdWhenPrincipalNameProvidedThenPrincipalCreated() {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(\"principalName\")\n\t\t\t.build();\n\t\tassertThat(authorizeRequest.getClientRegistrationId()).isEqualTo(this.clientRegistration.getRegistrationId());\n\t\tassertThat(authorizeRequest.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizeRequest.getPrincipal().getName()).isEqualTo(\"principalName\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientIdTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizedClientId}.\n *\n * @author Vedran Pavic\n */\npublic class OAuth2AuthorizedClientIdTests {\n\n\t@Test\n\tpublic void constructorWhenRegistrationIdNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AuthorizedClientId(null, \"test-principal\"))\n\t\t\t.withMessage(\"clientRegistrationId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrincipalNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AuthorizedClientId(\"test-client\", null))\n\t\t\t.withMessage(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void equalsWhenSameRegistrationIdAndPrincipalThenShouldReturnTrue() {\n\t\tOAuth2AuthorizedClientId id1 = new OAuth2AuthorizedClientId(\"test-client\", \"test-principal\");\n\t\tOAuth2AuthorizedClientId id2 = new OAuth2AuthorizedClientId(\"test-client\", \"test-principal\");\n\t\tassertThat(id1.equals(id2)).isTrue();\n\t}\n\n\t@Test\n\tpublic void equalsWhenDifferentRegistrationIdAndSamePrincipalThenShouldReturnFalse() {\n\t\tOAuth2AuthorizedClientId id1 = new OAuth2AuthorizedClientId(\"test-client1\", \"test-principal\");\n\t\tOAuth2AuthorizedClientId id2 = new OAuth2AuthorizedClientId(\"test-client2\", \"test-principal\");\n\t\tassertThat(id1.equals(id2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void equalsWhenSameRegistrationIdAndDifferentPrincipalThenShouldReturnFalse() {\n\t\tOAuth2AuthorizedClientId id1 = new OAuth2AuthorizedClientId(\"test-client\", \"test-principal1\");\n\t\tOAuth2AuthorizedClientId id2 = new OAuth2AuthorizedClientId(\"test-client\", \"test-principal2\");\n\t\tassertThat(id1.equals(id2)).isFalse();\n\t}\n\n\t@Test\n\tpublic void hashCodeWhenSameRegistrationIdAndPrincipalThenShouldReturnSame() {\n\t\tOAuth2AuthorizedClientId id1 = new OAuth2AuthorizedClientId(\"test-client\", \"test-principal\");\n\t\tOAuth2AuthorizedClientId id2 = new OAuth2AuthorizedClientId(\"test-client\", \"test-principal\");\n\t\tassertThat(id1.hashCode()).isEqualTo(id2.hashCode());\n\t}\n\n\t@Test\n\tpublic void hashCodeWhenDifferentRegistrationIdAndSamePrincipalThenShouldNotReturnSame() {\n\t\tOAuth2AuthorizedClientId id1 = new OAuth2AuthorizedClientId(\"test-client1\", \"test-principal\");\n\t\tOAuth2AuthorizedClientId id2 = new OAuth2AuthorizedClientId(\"test-client2\", \"test-principal\");\n\t\tassertThat(id1.hashCode()).isNotEqualTo(id2.hashCode());\n\t}\n\n\t@Test\n\tpublic void hashCodeWhenSameRegistrationIdAndDifferentPrincipalThenShouldNotReturnSame() {\n\t\tOAuth2AuthorizedClientId id1 = new OAuth2AuthorizedClientId(\"test-client\", \"test-principal1\");\n\t\tOAuth2AuthorizedClientId id2 = new OAuth2AuthorizedClientId(\"test-client\", \"test-principal2\");\n\t\tassertThat(id1.hashCode()).isNotEqualTo(id2.hashCode());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProviderBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.FormHttpMessageConverter;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.RestClientClientCredentialsTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.RestClientRefreshTokenTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.test.web.client.ExpectedCount;\nimport org.springframework.test.web.client.MockRestServiceServer;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.client.ExpectedCount.once;\nimport static org.springframework.test.web.client.ExpectedCount.times;\nimport static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;\nimport static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;\n\n/**\n * Tests for {@link OAuth2AuthorizedClientProviderBuilder}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizedClientProviderBuilderTests {\n\n\tprivate RestClientClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient;\n\n\tprivate RestClientRefreshTokenTokenResponseClient refreshTokenTokenResponseClient;\n\n\tprivate Authentication principal;\n\n\tprivate MockRestServiceServer server;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tRestClient.Builder restClientBuilder = RestClient.builder().messageConverters((messageConverters) -> {\n\t\t\tmessageConverters.clear();\n\t\t\tmessageConverters.add(new FormHttpMessageConverter());\n\t\t\tmessageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());\n\t\t});\n\t\tthis.server = MockRestServiceServer.bindTo(restClientBuilder).build();\n\t\tRestClient restClient = restClientBuilder.build();\n\t\tthis.refreshTokenTokenResponseClient = new RestClientRefreshTokenTokenResponseClient();\n\t\tthis.refreshTokenTokenResponseClient.setRestClient(restClient);\n\t\tthis.clientCredentialsTokenResponseClient = new RestClientClientCredentialsTokenResponseClient();\n\t\tthis.clientCredentialsTokenResponseClient.setRestClient(restClient);\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t}\n\n\t@Test\n\tpublic void providerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2AuthorizedClientProviderBuilder.builder().provider(null));\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeProviderThenProviderAuthorizes() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(TestClientRegistrations.clientRegistration().build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t.isThrownBy(() -> authorizedClientProvider.authorize(authorizationContext));\n\t}\n\n\t@Test\n\tpublic void buildWhenRefreshTokenProviderThenProviderReauthorizes() {\n\t\tmockAccessTokenResponse(once());\n\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.refreshToken((configurer) -> configurer.accessTokenResponseClient(this.refreshTokenTokenResponseClient))\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(\n\t\t\t\tTestClientRegistrations.clientRegistration().build(), this.principal.getName(), expiredAccessToken(),\n\t\t\t\tTestOAuth2RefreshTokens.refreshToken());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(reauthorizedClient).isNotNull();\n\t\tthis.server.verify();\n\t}\n\n\t@Test\n\tpublic void buildWhenClientCredentialsProviderThenProviderAuthorizes() {\n\t\tmockAccessTokenResponse(once());\n\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.clientCredentials(\n\t\t\t\t\t(configurer) -> configurer.accessTokenResponseClient(this.clientCredentialsTokenResponseClient))\n\t\t\t.build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(TestClientRegistrations.clientCredentials().build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tthis.server.verify();\n\t}\n\n\t@Test\n\tpublic void buildWhenAllProvidersThenProvidersAuthorize() {\n\t\tmockAccessTokenResponse(times(2));\n\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken((configurer) -> configurer.accessTokenResponseClient(this.refreshTokenTokenResponseClient))\n\t\t\t.clientCredentials(\n\t\t\t\t\t(configurer) -> configurer.accessTokenResponseClient(this.clientCredentialsTokenResponseClient))\n\t\t\t.build();\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\t// authorization_code\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationCodeContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t.isThrownBy(() -> authorizedClientProvider.authorize(authorizationCodeContext));\n\t\t// refresh_token\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\tthis.principal.getName(), expiredAccessToken(), TestOAuth2RefreshTokens.refreshToken());\n\t\tOAuth2AuthorizationContext refreshTokenContext = OAuth2AuthorizationContext\n\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient reauthorizedClient = authorizedClientProvider.authorize(refreshTokenContext);\n\t\tassertThat(reauthorizedClient).isNotNull();\n\t\t// client_credentials\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext clientCredentialsContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(TestClientRegistrations.clientCredentials().build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tauthorizedClient = authorizedClientProvider.authorize(clientCredentialsContext);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tthis.server.verify();\n\t}\n\n\t@Test\n\tpublic void buildWhenCustomProviderThenProviderCalled() {\n\t\tOAuth2AuthorizedClientProvider customProvider = mock(OAuth2AuthorizedClientProvider.class);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.provider(customProvider)\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(TestClientRegistrations.clientRegistration().build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tauthorizedClientProvider.authorize(authorizationContext);\n\t\tverify(customProvider).authorize(any(OAuth2AuthorizationContext.class));\n\t}\n\n\tprivate OAuth2AccessToken expiredAccessToken() {\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(60));\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\", issuedAt, expiresAt);\n\t}\n\n\tprivate void mockAccessTokenResponse(ExpectedCount expectedCount) {\n\t\tthis.server.expect(expectedCount, requestTo(\"https://example.com/login/oauth/access_token\"))\n\t\t\t.andRespond(withSuccess().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.body(new ClassPathResource(\"access-token-response.json\")));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizedClient}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizedClientTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate String principalName;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.principalName = \"principal\";\n\t\tthis.accessToken = TestOAuth2AccessTokens.noScopes();\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizedClient(null, this.principalName, this.accessToken));\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizedClient(this.clientRegistration, null, this.accessToken));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAccessTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizedClient(this.clientRegistration, this.principalName, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principalName, this.accessToken);\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principalName);\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(this.accessToken);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/R2dbcReactiveOAuth2AuthorizedClientServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashSet;\n\nimport io.r2dbc.h2.H2ConnectionFactory;\nimport io.r2dbc.spi.ConnectionFactory;\nimport io.r2dbc.spi.Result;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.dao.DataRetrievalFailureException;\nimport org.springframework.r2dbc.connection.init.CompositeDatabasePopulator;\nimport org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;\nimport org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;\nimport org.springframework.r2dbc.core.DatabaseClient;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link R2dbcReactiveOAuth2AuthorizedClientService}\n *\n * @author Ovidiu Popa\n *\n */\npublic class R2dbcReactiveOAuth2AuthorizedClientServiceTests {\n\n\tprivate static final String OAUTH2_CLIENT_SCHEMA_SQL_RESOURCE = \"org/springframework/security/oauth2/client/oauth2-client-schema.sql\";\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate DatabaseClient databaseClient;\n\n\tprivate static int principalId = 1000;\n\n\tprivate R2dbcReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tfinal ConnectionFactory connectionFactory = createDb();\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.clientRegistrationRepository = mock(ReactiveClientRegistrationRepository.class);\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(anyString()))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tthis.databaseClient = DatabaseClient.create(connectionFactory);\n\t\tthis.authorizedClientService = new R2dbcReactiveOAuth2AuthorizedClientService(this.databaseClient,\n\t\t\t\tthis.clientRegistrationRepository);\n\t}\n\n\t@Test\n\tpublic void constructorWhenDatabaseClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new R2dbcReactiveOAuth2AuthorizedClientService(null, this.clientRegistrationRepository))\n\t\t\t.withMessageContaining(\"databaseClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new R2dbcReactiveOAuth2AuthorizedClientService(this.databaseClient, null))\n\t\t\t.withMessageContaining(\"clientRegistrationRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService.loadAuthorizedClient(null, \"principalName\"))\n\t\t\t.withMessageContaining(\"clientRegistrationId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService\n\t\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), null))\n\t\t\t.withMessageContaining(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenDoesNotExistThenReturnNull() {\n\t\tthis.authorizedClientService.loadAuthorizedClient(\"registration-not-found\", \"principalName\")\n\t\t\t.as(StepVerifier::create)\n\t\t\t.expectNextCount(0)\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenExistsThenReturnAuthorizedClient() {\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(expected, principal)\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\n\t\tthis.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName())\n\t\t\t.as(StepVerifier::create)\n\t\t\t.assertNext((authorizedClient) -> {\n\t\t\t\tassertThat(authorizedClient).isNotNull();\n\t\t\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(expected.getClientRegistration());\n\t\t\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expected.getPrincipalName());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getTokenType())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getTokenType());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getTokenValue());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getIssuedAt())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getIssuedAt());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getExpiresAt())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getExpiresAt());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getScopes())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getScopes());\n\t\t\t\tassertThat(authorizedClient.getRefreshToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(expected.getRefreshToken().getTokenValue());\n\t\t\t\tassertThat(authorizedClient.getRefreshToken().getIssuedAt())\n\t\t\t\t\t.isEqualTo(expected.getRefreshToken().getIssuedAt());\n\t\t\t})\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenExistsButNotFoundInClientRegistrationRepositoryThenThrowDataRetrievalFailureException() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.empty());\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);\n\n\t\tthis.authorizedClientService.saveAuthorizedClient(expected, principal)\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\n\t\tthis.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName())\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyErrorSatisfies((exception) -> assertThat(exception).isInstanceOf(DataRetrievalFailureException.class)\n\t\t\t\t.hasMessage(\"The ClientRegistration with id '\" + this.clientRegistration.getRegistrationId()\n\t\t\t\t\t\t+ \"' exists in the data source, however, it was not found in the ReactiveClientRegistrationRepository.\"));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthorizedClientIsNullThenThrowIllegalArgumentException() {\n\t\tAuthentication principal = createPrincipal();\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(null, principal))\n\t\t\t.withMessageContaining(\"authorizedClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenPrincipalIsNullThenThrowIllegalArgumentException() {\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, null))\n\t\t\t.withMessageContaining(\"principal cannot be null\");\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenSaveThenLoadReturnsSaved() {\n\t\tAuthentication principal = createPrincipal();\n\t\tfinal OAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);\n\n\t\tthis.authorizedClientService.saveAuthorizedClient(expected, principal)\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\n\t\tthis.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName())\n\t\t\t.as(StepVerifier::create)\n\t\t\t.assertNext((authorizedClient) -> {\n\t\t\t\tassertThat(authorizedClient).isNotNull();\n\t\t\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(expected.getClientRegistration());\n\t\t\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expected.getPrincipalName());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getTokenType())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getTokenType());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getTokenValue());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getIssuedAt())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getIssuedAt());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getExpiresAt())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getExpiresAt());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getScopes())\n\t\t\t\t\t.isEqualTo(expected.getAccessToken().getScopes());\n\t\t\t\tassertThat(authorizedClient.getRefreshToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(expected.getRefreshToken().getTokenValue());\n\t\t\t\tassertThat(authorizedClient.getRefreshToken().getIssuedAt())\n\t\t\t\t\t.isEqualTo(expected.getRefreshToken().getIssuedAt());\n\t\t\t})\n\t\t\t.verifyComplete();\n\n\t\t// Test save/load of NOT NULL attributes only\n\t\tprincipal = createPrincipal();\n\t\tOAuth2AuthorizedClient updatedExpectedPrincipal = createAuthorizedClient(principal, this.clientRegistration,\n\t\t\t\ttrue);\n\t\tthis.authorizedClientService.saveAuthorizedClient(updatedExpectedPrincipal, principal)\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\n\t\tthis.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName())\n\t\t\t.as(StepVerifier::create)\n\t\t\t.assertNext((authorizedClient) -> {\n\t\t\t\tassertThat(authorizedClient).isNotNull();\n\t\t\t\tassertThat(authorizedClient.getClientRegistration())\n\t\t\t\t\t.isEqualTo(updatedExpectedPrincipal.getClientRegistration());\n\t\t\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(updatedExpectedPrincipal.getPrincipalName());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getTokenType())\n\t\t\t\t\t.isEqualTo(updatedExpectedPrincipal.getAccessToken().getTokenType());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(updatedExpectedPrincipal.getAccessToken().getTokenValue());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getIssuedAt())\n\t\t\t\t\t.isEqualTo(updatedExpectedPrincipal.getAccessToken().getIssuedAt());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getExpiresAt())\n\t\t\t\t\t.isEqualTo(updatedExpectedPrincipal.getAccessToken().getExpiresAt());\n\t\t\t\tassertThat(authorizedClient.getAccessToken().getScopes()).isEmpty();\n\t\t\t\tassertThat(authorizedClient.getRefreshToken()).isNull();\n\t\t\t})\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenSaveClientWithExistingPrimaryKeyThenUpdate() {\n\t\t// Given a saved authorized client\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, principal)\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\n\t\t// When a client with the same principal and registration id is saved\n\t\tOAuth2AuthorizedClient updatedAuthorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\t\tthis.authorizedClientService.saveAuthorizedClient(updatedAuthorizedClient, principal)\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\n\t\t// Then the saved client is updated\n\t\tthis.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName())\n\t\t\t.as(StepVerifier::create)\n\t\t\t.assertNext((savedClient) -> {\n\t\t\t\tassertThat(savedClient).isNotNull();\n\t\t\t\tassertThat(savedClient.getClientRegistration())\n\t\t\t\t\t.isEqualTo(updatedAuthorizedClient.getClientRegistration());\n\t\t\t\tassertThat(savedClient.getPrincipalName()).isEqualTo(updatedAuthorizedClient.getPrincipalName());\n\t\t\t\tassertThat(savedClient.getAccessToken().getTokenType())\n\t\t\t\t\t.isEqualTo(updatedAuthorizedClient.getAccessToken().getTokenType());\n\t\t\t\tassertThat(savedClient.getAccessToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(updatedAuthorizedClient.getAccessToken().getTokenValue());\n\t\t\t\tassertThat(savedClient.getAccessToken().getIssuedAt())\n\t\t\t\t\t.isEqualTo(updatedAuthorizedClient.getAccessToken().getIssuedAt());\n\t\t\t\tassertThat(savedClient.getAccessToken().getExpiresAt())\n\t\t\t\t\t.isEqualTo(updatedAuthorizedClient.getAccessToken().getExpiresAt());\n\t\t\t\tassertThat(savedClient.getAccessToken().getScopes())\n\t\t\t\t\t.isEqualTo(updatedAuthorizedClient.getAccessToken().getScopes());\n\t\t\t\tassertThat(savedClient.getRefreshToken().getTokenValue())\n\t\t\t\t\t.isEqualTo(updatedAuthorizedClient.getRefreshToken().getTokenValue());\n\t\t\t\tassertThat(savedClient.getRefreshToken().getIssuedAt())\n\t\t\t\t\t.isEqualTo(updatedAuthorizedClient.getRefreshToken().getIssuedAt());\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService.removeAuthorizedClient(null, \"principalName\"))\n\t\t\t.withMessageContaining(\"clientRegistrationId cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService\n\t\t\t\t.removeAuthorizedClient(this.clientRegistration.getRegistrationId(), null))\n\t\t\t.withMessageContaining(\"principalName cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenExistsThenRemoved() {\n\t\tAuthentication principal = createPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);\n\n\t\tthis.authorizedClientService.saveAuthorizedClient(authorizedClient, principal)\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\n\t\tthis.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName())\n\t\t\t.as(StepVerifier::create)\n\t\t\t.assertNext((dbAuthorizedClient) -> assertThat(dbAuthorizedClient).isNotNull())\n\t\t\t.verifyComplete();\n\n\t\tthis.authorizedClientService\n\t\t\t.removeAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName())\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\n\t\tthis.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.clientRegistration.getRegistrationId(), principal.getName())\n\t\t\t.as(StepVerifier::create)\n\t\t\t.expectNextCount(0)\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientRowMapperWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService.setAuthorizedClientRowMapper(null))\n\t\t\t.withMessageContaining(\"authorizedClientRowMapper cannot be nul\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientParametersMapperWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientService.setAuthorizedClientParametersMapper(null))\n\t\t\t.withMessageContaining(\"authorizedClientParametersMapper cannot be nul\");\n\t}\n\n\tprivate static ConnectionFactory createDb() {\n\t\tConnectionFactory connectionFactory = H2ConnectionFactory.inMemory(\"oauth-test\");\n\n\t\tMono.from(connectionFactory.create())\n\t\t\t.flatMapMany((connection) -> Flux\n\t\t\t\t.from(connection.createStatement(\"drop table oauth2_authorized_client\").execute())\n\t\t\t\t.flatMap(Result::getRowsUpdated)\n\t\t\t\t.onErrorResume((e) -> Mono.empty())\n\t\t\t\t.thenMany(connection.close()))\n\t\t\t.as(StepVerifier::create)\n\t\t\t.verifyComplete();\n\t\tConnectionFactoryInitializer createDb = createDb(OAUTH2_CLIENT_SCHEMA_SQL_RESOURCE);\n\t\tcreateDb.setConnectionFactory(connectionFactory);\n\t\tcreateDb.afterPropertiesSet();\n\t\treturn connectionFactory;\n\t}\n\n\tprivate static ConnectionFactoryInitializer createDb(String schema) {\n\t\tConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();\n\n\t\tCompositeDatabasePopulator populator = new CompositeDatabasePopulator();\n\t\tpopulator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource(schema)));\n\t\tinitializer.setDatabasePopulator(populator);\n\t\treturn initializer;\n\t}\n\n\tprivate static Authentication createPrincipal() {\n\t\treturn new TestingAuthenticationToken(\"principal-\" + principalId++, \"password\");\n\t}\n\n\tprivate static OAuth2AuthorizedClient createAuthorizedClient(Authentication principal,\n\t\t\tClientRegistration clientRegistration) {\n\t\treturn createAuthorizedClient(principal, clientRegistration, false);\n\t}\n\n\tprivate static OAuth2AuthorizedClient createAuthorizedClient(Authentication principal,\n\t\t\tClientRegistration clientRegistration, boolean requiredAttributesOnly) {\n\t\tInstant issuedAt = Instant.ofEpochSecond(1234567890, 123456000);\n\t\tOAuth2AccessToken accessToken;\n\t\tif (!requiredAttributesOnly) {\n\t\t\taccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"scopes\", issuedAt,\n\t\t\t\t\tissuedAt.plus(Duration.ofDays(1)), new HashSet<>(Arrays.asList(\"read\", \"write\")));\n\t\t}\n\t\telse {\n\t\t\taccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"no-scopes\", issuedAt,\n\t\t\t\t\tissuedAt.plus(Duration.ofDays(1)));\n\t\t}\n\t\tOAuth2RefreshToken refreshToken = null;\n\t\tif (!requiredAttributesOnly) {\n\t\t\trefreshToken = new OAuth2RefreshToken(\"refresh-token\", issuedAt);\n\t\t}\n\t\treturn new OAuth2AuthorizedClient(clientRegistration, principal.getName(), accessToken, refreshToken);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizedClientProviderBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link ReactiveOAuth2AuthorizedClientProviderBuilder}.\n *\n * @author Joe Grandja\n */\npublic class ReactiveOAuth2AuthorizedClientProviderBuilderTests {\n\n\tprivate ClientRegistration.Builder clientRegistrationBuilder;\n\n\tprivate Authentication principal;\n\n\tprivate MockWebServer server;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistrationBuilder = TestClientRegistrations.clientRegistration().tokenUri(tokenUri);\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void providerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> ReactiveOAuth2AuthorizedClientProviderBuilder.builder().provider(null));\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeProviderThenProviderAuthorizes() {\n\t\t// @formatter:off\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder\n\t\t\t\t.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistrationBuilder.build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t.isThrownBy(() -> authorizedClientProvider.authorize(authorizationContext).block());\n\t}\n\n\t@Test\n\tpublic void buildWhenRefreshTokenProviderThenProviderReauthorizes() throws Exception {\n\t\tString accessTokenSuccessResponse = \"{\\n\" + \"\t\\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\" + \"   \\\"expires_in\\\": \\\"3600\\\"\\n\" + \"}\\n\";\n\t\tthis.server.enqueue(jsonResponse(accessTokenSuccessResponse));\n\t\t// @formatter:off\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder\n\t\t\t\t.builder()\n\t\t\t\t.refreshToken()\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),\n\t\t\t\tthis.principal.getName(), expiredAccessToken(), TestOAuth2RefreshTokens.refreshToken());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(reauthorizedClient).isNotNull();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(1);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(\"grant_type=refresh_token\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientCredentialsProviderThenProviderAuthorizes() throws Exception {\n\t\tString accessTokenSuccessResponse = \"{\\n\" + \"\t\\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\" + \"   \\\"expires_in\\\": \\\"3600\\\"\\n\" + \"}\\n\";\n\t\tthis.server.enqueue(jsonResponse(accessTokenSuccessResponse));\n\t\t// @formatter:off\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder\n\t\t\t\t.builder()\n\t\t\t\t.clientCredentials()\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistrationBuilder\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(1);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(\"grant_type=client_credentials\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAllProvidersThenProvidersAuthorize() throws Exception {\n\t\tString accessTokenSuccessResponse = \"{\\n\" + \"\t\\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\" + \"   \\\"expires_in\\\": \\\"3600\\\"\\n\" + \"}\\n\";\n\t\tthis.server.enqueue(jsonResponse(accessTokenSuccessResponse));\n\t\tthis.server.enqueue(jsonResponse(accessTokenSuccessResponse));\n\t\tthis.server.enqueue(jsonResponse(accessTokenSuccessResponse));\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder\n\t\t\t.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken()\n\t\t\t.clientCredentials()\n\t\t\t.build();\n\t\t// authorization_code\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationCodeContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistrationBuilder.build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t.isThrownBy(() -> authorizedClientProvider.authorize(authorizationCodeContext).block());\n\t\t// refresh_token\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),\n\t\t\t\tthis.principal.getName(), expiredAccessToken(), TestOAuth2RefreshTokens.refreshToken());\n\t\tOAuth2AuthorizationContext refreshTokenContext = OAuth2AuthorizationContext\n\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient reauthorizedClient = authorizedClientProvider.authorize(refreshTokenContext).block();\n\t\tassertThat(reauthorizedClient).isNotNull();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(1);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(\"grant_type=refresh_token\");\n\t\t// client_credentials\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext clientCredentialsContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistrationBuilder\n\t\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tauthorizedClient = authorizedClientProvider.authorize(clientCredentialsContext).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(2);\n\t\trecordedRequest = this.server.takeRequest();\n\t\tformParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(\"grant_type=client_credentials\");\n\t}\n\n\t@Test\n\tpublic void buildWhenCustomProviderThenProviderCalled() {\n\t\tReactiveOAuth2AuthorizedClientProvider customProvider = mock(ReactiveOAuth2AuthorizedClientProvider.class);\n\t\tgiven(customProvider.authorize(any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder\n\t\t\t\t.builder()\n\t\t\t\t.provider(customProvider)\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistrationBuilder.build())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tauthorizedClientProvider.authorize(authorizationContext).block();\n\t\tverify(customProvider).authorize(any(OAuth2AuthorizationContext.class));\n\t}\n\n\tprivate OAuth2AccessToken expiredAccessToken() {\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(60));\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\", issuedAt, expiresAt);\n\t}\n\n\tprivate MockResponse jsonResponse(String json) {\n\t\treturn new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(json);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThatException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler}.\n *\n * @author Evgeniy Cheban\n */\nclass RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandlerTests {\n\n\t@Test\n\tvoid setServerSecurityContextRepositoryWhenNullThenException() {\n\t\tassertThatException()\n\t\t\t.isThrownBy(() -> new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler()\n\t\t\t\t.setServerSecurityContextRepository(null))\n\t\t\t.withMessage(\"serverSecurityContextRepository cannot be null\");\n\t}\n\n\t@Test\n\tvoid setJwtDecoderFactoryWhenNullThenException() {\n\t\tassertThatException()\n\t\t\t.isThrownBy(() -> new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler().setJwtDecoderFactory(null))\n\t\t\t.withMessage(\"jwtDecoderFactory cannot be null\");\n\t}\n\n\t@Test\n\tvoid setAuthoritiesMapperWhenNullThenException() {\n\t\tassertThatException()\n\t\t\t.isThrownBy(() -> new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler().setAuthoritiesMapper(null))\n\t\t\t.withMessage(\"authoritiesMapper cannot be null\");\n\t}\n\n\t@Test\n\tvoid setUserServiceWhenNullThenException() {\n\t\tassertThatException()\n\t\t\t.isThrownBy(() -> new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler().setUserService(null))\n\t\t\t.withMessage(\"userService cannot be null\");\n\t}\n\n\t@Test\n\tvoid setClockSkewWhenNullThenException() {\n\t\tassertThatException()\n\t\t\t.isThrownBy(() -> new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler().setClockSkew(null))\n\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t}\n\n\t@Test\n\tvoid onAuthorizationSuccessWhenIdTokenValidThenSecurityContextRefreshed() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,\n\t\t\t\tprincipal.getAuthorities(), clientRegistration.getRegistrationId());\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\taccessToken, null);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tMap<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,\n\t\t\t\tOidcParameterNames.ID_TOKEN, \"id-token-1234\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"iss\", principal.getIssuer());\n\t\tclaims.put(\"sub\", principal.getSubject());\n\t\tclaims.put(\"aud\", principal.getAudience());\n\t\tclaims.put(\"nonce\", principal.getNonce());\n\t\tJwt jwt = mock(Jwt.class);\n\t\tgiven(jwt.getTokenValue()).willReturn(\"id-token-1234\");\n\t\tgiven(jwt.getIssuedAt()).willReturn(principal.getIssuedAt());\n\t\tgiven(jwt.getClaims()).willReturn(claims);\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);\n\t\tgiven(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(principal));\n\t\tWebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tRefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\thandler.setJwtDecoderFactory(reactiveJwtDecoderFactory);\n\t\thandler.setUserService(userService);\n\t\thandler.setServerSecurityContextRepository(serverSecurityContextRepository);\n\t\tStepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))\n\t\t\t.verifyComplete();\n\t\tStepVerifier.create(serverSecurityContextRepository.load(exchange).map(SecurityContext::getAuthentication))\n\t\t\t.expectNext(authenticationToken)\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tvoid onAuthorizationSuccessWhenIdTokenIssuerNotSameThenException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,\n\t\t\t\tprincipal.getAuthorities(), clientRegistration.getRegistrationId());\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\taccessToken, null);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tMap<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,\n\t\t\t\tOidcParameterNames.ID_TOKEN, \"id-token-1234\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"iss\", \"https://issuer.com\");\n\t\tclaims.put(\"sub\", principal.getSubject());\n\t\tclaims.put(\"aud\", principal.getAudience());\n\t\tclaims.put(\"nonce\", principal.getNonce());\n\t\tJwt jwt = mock(Jwt.class);\n\t\tgiven(jwt.getTokenValue()).willReturn(\"id-token-1234\");\n\t\tgiven(jwt.getIssuedAt()).willReturn(principal.getIssuedAt());\n\t\tgiven(jwt.getClaims()).willReturn(claims);\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);\n\t\tgiven(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(principal));\n\t\tWebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tRefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\thandler.setJwtDecoderFactory(reactiveJwtDecoderFactory);\n\t\thandler.setUserService(userService);\n\t\thandler.setServerSecurityContextRepository(serverSecurityContextRepository);\n\t\tStepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))\n\t\t\t.verifyErrorMessage(\"[invalid_id_token] Invalid issuer\");\n\t}\n\n\t@Test\n\tvoid onAuthorizationSuccessWhenIdTokenSubNotSameThenException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,\n\t\t\t\tprincipal.getAuthorities(), clientRegistration.getRegistrationId());\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\taccessToken, null);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tMap<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,\n\t\t\t\tOidcParameterNames.ID_TOKEN, \"id-token-1234\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"iss\", principal.getIssuer());\n\t\tclaims.put(\"sub\", \"invalid_sub\");\n\t\tclaims.put(\"aud\", principal.getAudience());\n\t\tclaims.put(\"nonce\", principal.getNonce());\n\t\tJwt jwt = mock(Jwt.class);\n\t\tgiven(jwt.getTokenValue()).willReturn(\"id-token-1234\");\n\t\tgiven(jwt.getIssuedAt()).willReturn(principal.getIssuedAt());\n\t\tgiven(jwt.getClaims()).willReturn(claims);\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);\n\t\tgiven(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(principal));\n\t\tWebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tRefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\thandler.setJwtDecoderFactory(reactiveJwtDecoderFactory);\n\t\thandler.setUserService(userService);\n\t\thandler.setServerSecurityContextRepository(serverSecurityContextRepository);\n\t\tStepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))\n\t\t\t.verifyErrorMessage(\"[invalid_id_token] Invalid subject\");\n\t}\n\n\t@Test\n\tvoid onAuthorizationSuccessWhenIdTokenIatNotAfterThenException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,\n\t\t\t\tprincipal.getAuthorities(), clientRegistration.getRegistrationId());\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\taccessToken, null);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tMap<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,\n\t\t\t\tOidcParameterNames.ID_TOKEN, \"id-token-1234\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"iss\", principal.getIssuer());\n\t\tclaims.put(\"sub\", principal.getSubject());\n\t\tclaims.put(\"aud\", principal.getAudience());\n\t\tclaims.put(\"nonce\", principal.getNonce());\n\t\tJwt jwt = mock(Jwt.class);\n\t\tgiven(jwt.getTokenValue()).willReturn(\"id-token-1234\");\n\t\tgiven(jwt.getIssuedAt()).willReturn(principal.getIssuedAt().minus(Duration.ofDays(1)));\n\t\tgiven(jwt.getClaims()).willReturn(claims);\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);\n\t\tgiven(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(principal));\n\t\tWebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tRefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\thandler.setJwtDecoderFactory(reactiveJwtDecoderFactory);\n\t\thandler.setUserService(userService);\n\t\thandler.setServerSecurityContextRepository(serverSecurityContextRepository);\n\t\tStepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))\n\t\t\t.verifyErrorMessage(\"[invalid_id_token] Invalid issued at time\");\n\t}\n\n\t@Test\n\tvoid onAuthorizationSuccessWhenIdTokenAudEmptyThenException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,\n\t\t\t\tprincipal.getAuthorities(), clientRegistration.getRegistrationId());\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\taccessToken, null);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tMap<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,\n\t\t\t\tOidcParameterNames.ID_TOKEN, \"id-token-1234\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"iss\", principal.getIssuer());\n\t\tclaims.put(\"sub\", principal.getSubject());\n\t\tclaims.put(\"aud\", Collections.emptyList());\n\t\tclaims.put(\"nonce\", principal.getNonce());\n\t\tJwt jwt = mock(Jwt.class);\n\t\tgiven(jwt.getTokenValue()).willReturn(\"id-token-1234\");\n\t\tgiven(jwt.getIssuedAt()).willReturn(principal.getIssuedAt());\n\t\tgiven(jwt.getClaims()).willReturn(claims);\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);\n\t\tgiven(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(principal));\n\t\tWebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tRefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\thandler.setJwtDecoderFactory(reactiveJwtDecoderFactory);\n\t\thandler.setUserService(userService);\n\t\thandler.setServerSecurityContextRepository(serverSecurityContextRepository);\n\t\tStepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))\n\t\t\t.verifyErrorMessage(\"[invalid_id_token] Invalid audience\");\n\t}\n\n\t@Test\n\tvoid onAuthorizationSuccessWhenIdTokenAudNotContainThenException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,\n\t\t\t\tprincipal.getAuthorities(), clientRegistration.getRegistrationId());\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\taccessToken, null);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tMap<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,\n\t\t\t\tOidcParameterNames.ID_TOKEN, \"id-token-1234\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"iss\", principal.getIssuer());\n\t\tclaims.put(\"sub\", principal.getSubject());\n\t\tclaims.put(\"aud\", List.of(\"invalid_client-id\"));\n\t\tclaims.put(\"nonce\", principal.getNonce());\n\t\tJwt jwt = mock(Jwt.class);\n\t\tgiven(jwt.getTokenValue()).willReturn(\"id-token-1234\");\n\t\tgiven(jwt.getIssuedAt()).willReturn(principal.getIssuedAt());\n\t\tgiven(jwt.getClaims()).willReturn(claims);\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);\n\t\tgiven(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(principal));\n\t\tWebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tRefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\thandler.setJwtDecoderFactory(reactiveJwtDecoderFactory);\n\t\thandler.setUserService(userService);\n\t\thandler.setServerSecurityContextRepository(serverSecurityContextRepository);\n\t\tStepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))\n\t\t\t.verifyErrorMessage(\"[invalid_id_token] Invalid audience\");\n\t}\n\n\t@Test\n\tvoid onAuthorizationSuccessWhenIdTokenAuthTimeNotSameThenException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,\n\t\t\t\tprincipal.getAuthorities(), clientRegistration.getRegistrationId());\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\taccessToken, null);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tMap<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,\n\t\t\t\tOidcParameterNames.ID_TOKEN, \"id-token-1234\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"iss\", principal.getIssuer());\n\t\tclaims.put(\"sub\", principal.getSubject());\n\t\tclaims.put(\"aud\", principal.getAudience());\n\t\tclaims.put(\"auth_time\", principal.getIssuedAt());\n\t\tclaims.put(\"nonce\", principal.getNonce());\n\t\tJwt jwt = mock(Jwt.class);\n\t\tgiven(jwt.getTokenValue()).willReturn(\"id-token-1234\");\n\t\tgiven(jwt.getIssuedAt()).willReturn(principal.getIssuedAt());\n\t\tgiven(jwt.getClaims()).willReturn(claims);\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);\n\t\tgiven(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(principal));\n\t\tWebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tRefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\thandler.setJwtDecoderFactory(reactiveJwtDecoderFactory);\n\t\thandler.setUserService(userService);\n\t\thandler.setServerSecurityContextRepository(serverSecurityContextRepository);\n\t\tStepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))\n\t\t\t.verifyErrorMessage(\"[invalid_id_token] Invalid authenticated at time\");\n\t}\n\n\t@Test\n\tvoid onAuthorizationSuccessWhenIdTokenNonceNotSameThenException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tOAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,\n\t\t\t\tprincipal.getAuthorities(), clientRegistration.getRegistrationId());\n\t\tOAuth2AccessToken accessToken = createAccessToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),\n\t\t\t\taccessToken, null);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tMap<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,\n\t\t\t\tOidcParameterNames.ID_TOKEN, \"id-token-1234\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"iss\", principal.getIssuer());\n\t\tclaims.put(\"sub\", principal.getSubject());\n\t\tclaims.put(\"aud\", principal.getAudience());\n\t\tclaims.put(\"nonce\", \"invalid_nonce\");\n\t\tJwt jwt = mock(Jwt.class);\n\t\tgiven(jwt.getTokenValue()).willReturn(\"id-token-1234\");\n\t\tgiven(jwt.getIssuedAt()).willReturn(principal.getIssuedAt());\n\t\tgiven(jwt.getClaims()).willReturn(claims);\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));\n\t\tReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);\n\t\tgiven(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);\n\t\tReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(userService.loadUser(any())).willReturn(Mono.just(principal));\n\t\tWebSessionServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tRefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();\n\t\thandler.setJwtDecoderFactory(reactiveJwtDecoderFactory);\n\t\thandler.setUserService(userService);\n\t\thandler.setServerSecurityContextRepository(serverSecurityContextRepository);\n\t\tStepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))\n\t\t\t.verifyErrorMessage(\"[invalid_nonce] Invalid nonce\");\n\t}\n\n\tprivate static OAuth2AccessToken createAccessToken() {\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(60));\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\", issuedAt, expiresAt,\n\t\t\t\tSet.of(OidcScopes.OPENID));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/RefreshTokenOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashSet;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.event.OAuth2AuthorizedClientRefreshedEvent;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link RefreshTokenOAuth2AuthorizedClientProvider}.\n *\n * @author Joe Grandja\n */\npublic class RefreshTokenOAuth2AuthorizedClientProviderTests {\n\n\tprivate RefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Authentication principal;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider();\n\t\tthis.accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(60));\n\t\tOAuth2AccessToken expiredAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, this.principal.getName(),\n\t\t\t\texpiredAccessToken, TestOAuth2RefreshTokens.refreshToken());\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseClientWhenClientIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAccessTokenResponseClient(null))\n\t\t\t\t.withMessage(\"accessTokenResponseClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(null))\n\t\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(-1)))\n\t\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(null))\n\t\t\t\t.withMessage(\"context cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedThenUnableToReauthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndRefreshTokenIsNullThenUnableToReauthorize() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), this.authorizedClient.getAccessToken());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndAccessTokenNotExpiredThenNotReauthorize() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), this.authorizedClient.getRefreshToken());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t}\n\n\t// gh-7511\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndAccessTokenNotExpiredButClockSkewForcesExpiryThenReauthorize() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"new-refresh-token\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.minus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), expiresInOneMinAccessToken, this.authorizedClient.getRefreshToken());\n\t\t// Shorten the lifespan of the access token by 90 seconds, which will ultimately\n\t\t// force it to expire on the client\n\t\tthis.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndAccessTokenExpiredThenReauthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses\n\t\t\t\t.accessTokenResponse()\n\t\t\t\t.refreshToken(\"new-refresh-token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndRequestScopeProvidedThenScopeRequested() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses\n\t\t\t\t.accessTokenResponse()\n\t\t\t\t.refreshToken(\"new-refresh-token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tString[] requestScope = new String[] { \"read\", \"write\" };\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME, requestScope)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizedClientProvider.authorize(authorizationContext);\n\t\tArgumentCaptor<OAuth2RefreshTokenGrantRequest> refreshTokenGrantRequestArgCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2RefreshTokenGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(refreshTokenGrantRequestArgCaptor.capture());\n\t\tassertThat(refreshTokenGrantRequestArgCaptor.getValue().getScopes())\n\t\t\t.isEqualTo(new HashSet<>(Arrays.asList(requestScope)));\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndInvalidRequestScopeProvidedThenThrowIllegalArgumentException() {\n\t\tString invalidRequestScope = \"read write\";\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME, invalidRequestScope)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(authorizationContext))\n\t\t\t.withMessageStartingWith(\"The context attribute must be of type String[] '\"\n\t\t\t\t\t+ OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME + \"'\");\n\t}\n\n\t@Test\n\tpublic void shouldPublishEventWhenTokenRefreshed() {\n\t\tOAuth2TokenRefreshedAwareEventPublisher eventPublisher = new OAuth2TokenRefreshedAwareEventPublisher();\n\t\tthis.authorizedClientProvider.setApplicationEventPublisher(eventPublisher);\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses\n\t\t\t\t.accessTokenResponse()\n\t\t\t\t.refreshToken(\"new-refresh-token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(eventPublisher.flag).isTrue();\n\t}\n\n\t@Test\n\tpublic void shouldNotPublishEventWhenTokenNotRefreshed() {\n\t\tOAuth2TokenRefreshedAwareEventPublisher eventPublisher = new OAuth2TokenRefreshedAwareEventPublisher();\n\t\tthis.authorizedClientProvider.setApplicationEventPublisher(eventPublisher);\n\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), this.authorizedClient.getRefreshToken());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(eventPublisher.flag).isFalse();\n\t}\n\n\tprivate static class OAuth2TokenRefreshedAwareEventPublisher implements ApplicationEventPublisher {\n\n\t\tBoolean flag = false;\n\n\t\t@Override\n\t\tpublic void publishEvent(Object event) {\n\t\t\tif (OAuth2AuthorizedClientRefreshedEvent.class.isAssignableFrom(event.getClass())) {\n\t\t\t\tthis.flag = true;\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/RefreshTokenReactiveOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashSet;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link RefreshTokenReactiveOAuth2AuthorizedClientProvider}.\n *\n * @author Joe Grandja\n * @author Evgeniy Cheban\n */\npublic class RefreshTokenReactiveOAuth2AuthorizedClientProviderTests {\n\n\tprivate RefreshTokenReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Authentication principal;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientProvider = new RefreshTokenReactiveOAuth2AuthorizedClientProvider();\n\t\tthis.accessTokenResponseClient = mock(ReactiveOAuth2AccessTokenResponseClient.class);\n\t\tthis.authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofMinutes(60));\n\t\tOAuth2AccessToken expiredAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, this.principal.getName(),\n\t\t\t\texpiredAccessToken, TestOAuth2RefreshTokens.refreshToken());\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseClientWhenClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAccessTokenResponseClient(null))\n\t\t\t.withMessage(\"accessTokenResponseClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationSuccessHandlerWhenHandlerIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAuthorizationSuccessHandler(null))\n\t\t\t\t.withMessage(\"authorizationSuccessHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(null))\n\t\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(-1)))\n\t\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(null).block())\n\t\t\t\t.withMessage(\"context cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedThenUnableToReauthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndRefreshTokenIsNullThenUnableToReauthorize() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), this.authorizedClient.getAccessToken());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndAccessTokenNotExpiredThenNotReauthorize() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), this.authorizedClient.getRefreshToken());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t}\n\n\t// gh-7511\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndAccessTokenNotExpiredButClockSkewForcesExpiryThenReauthorize() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"new-refresh-token\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.minus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), expiresInOneMinAccessToken, this.authorizedClient.getRefreshToken());\n\t\t// Shorten the lifespan of the access token by 90 seconds, which will ultimately\n\t\t// force it to expire on the client\n\t\tthis.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext)\n\t\t\t.block();\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndAccessTokenExpiredThenReauthorize() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"new-refresh-token\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext)\n\t\t\t.block();\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndRequestScopeProvidedThenScopeRequested() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"new-refresh-token\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tString[] requestScope = new String[] { \"read\", \"write\" };\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME, requestScope)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tArgumentCaptor<OAuth2RefreshTokenGrantRequest> refreshTokenGrantRequestArgCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2RefreshTokenGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(refreshTokenGrantRequestArgCaptor.capture());\n\t\tassertThat(refreshTokenGrantRequestArgCaptor.getValue().getScopes())\n\t\t\t.isEqualTo(new HashSet<>(Arrays.asList(requestScope)));\n\t}\n\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndInvalidRequestScopeProvidedThenThrowIllegalArgumentException() {\n\t\tString invalidRequestScope = \"read write\";\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME, invalidRequestScope)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(authorizationContext).block())\n\t\t\t.withMessageStartingWith(\"The context attribute must be of type String[] '\"\n\t\t\t\t\t+ OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME + \"'\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/TokenExchangeOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link TokenExchangeOAuth2AuthorizedClientProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class TokenExchangeOAuth2AuthorizedClientProviderTests {\n\n\tprivate TokenExchangeOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2Token subjectToken;\n\n\tprivate OAuth2Token actorToken;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();\n\t\tthis.accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\t// @formatter:off\n\t\tthis.clientRegistration = ClientRegistration.withRegistrationId(\"token-exchange\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.tokenUri(\"https://example.com/oauth2/token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.subjectToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.actorToken = TestOAuth2AccessTokens.noScopes();\n\t\tthis.principal = new TestingAuthenticationToken(this.subjectToken, this.subjectToken);\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseClientWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAccessTokenResponseClient(null))\n\t\t\t\t.withMessage(\"accessTokenResponseClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setSubjectTokenResolverWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setSubjectTokenResolver(null))\n\t\t\t\t.withMessage(\"subjectTokenResolver cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setActorTokenResolverWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setActorTokenResolver(null))\n\t\t\t\t.withMessage(\"actorTokenResolver cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(null))\n\t\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(-1)))\n\t\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(null))\n\t\t\t\t.withMessage(\"context cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotTokenExchangeThenUnableToAuthorize() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials().build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t\tverifyNoInteractions(this.accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndTokenNotExpiredThenNotReauthorized() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.scopes(\"read\", \"write\"));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t\tverifyNoInteractions(this.accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenInvalidRequestThenThrowClientAuthorizationException() {\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willThrow(new OAuth2AuthorizationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST)));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(authorizationContext))\n\t\t\t\t.withMessageContaining(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\t// @formatter:on\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndTokenExpiredThenReauthorized() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.minus(Duration.ofMinutes(30));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), accessToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(reauthorizedClient).isNotNull();\n\t\tassertThat(reauthorizedClient).isNotEqualTo(authorizedClient);\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndTokenNotExpiredButClockSkewForcesExpiryThenReauthorized() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.plus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), expiresInOneMinAccessToken);\n\t\t// Shorten the lifespan of the access token by 90 seconds, which will ultimately\n\t\t// force it to expire on the client\n\t\tthis.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(reauthorizedClient).isNotNull();\n\t\tassertThat(reauthorizedClient).isNotEqualTo(authorizedClient);\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndNotAuthorizedAndSubjectTokenDoesNotResolveThenUnableToAuthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(new TestingAuthenticationToken(\"user\", \"password\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t\tverifyNoInteractions(this.accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndNotAuthorizedAndSubjectTokenResolvesThenAuthorized() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenCustomSubjectTokenResolverSetThenCalled() {\n\t\tFunction<OAuth2AuthorizationContext, OAuth2Token> subjectTokenResolver = mock(Function.class);\n\t\tgiven(subjectTokenResolver.apply(any(OAuth2AuthorizationContext.class))).willReturn(this.subjectToken);\n\t\tthis.authorizedClientProvider.setSubjectTokenResolver(subjectTokenResolver);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tverify(subjectTokenResolver).apply(authorizationContext);\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenCustomActorTokenResolverSetThenCalled() {\n\t\tFunction<OAuth2AuthorizationContext, OAuth2Token> actorTokenResolver = mock(Function.class);\n\t\tgiven(actorTokenResolver.apply(any(OAuth2AuthorizationContext.class))).willReturn(this.actorToken);\n\t\tthis.authorizedClientProvider.setActorTokenResolver(actorTokenResolver);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tverify(actorTokenResolver).apply(authorizationContext);\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isEqualTo(this.actorToken);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClockSetThenCalled() {\n\t\tClock clock = mock(Clock.class);\n\t\tgiven(clock.instant()).willReturn(Instant.now());\n\t\tthis.authorizedClientProvider.setClock(clock);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext)).isNull();\n\t\tverify(clock).instant();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/TokenExchangeReactiveOAuth2AuthorizedClientProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link TokenExchangeReactiveOAuth2AuthorizedClientProvider}.\n *\n * @author Steve Riesenberg\n */\npublic class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {\n\n\tprivate TokenExchangeReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2Token subjectToken;\n\n\tprivate OAuth2Token actorToken;\n\n\tprivate Authentication principal;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authorizedClientProvider = new TokenExchangeReactiveOAuth2AuthorizedClientProvider();\n\t\tthis.accessTokenResponseClient = mock(ReactiveOAuth2AccessTokenResponseClient.class);\n\t\tthis.authorizedClientProvider.setAccessTokenResponseClient(this.accessTokenResponseClient);\n\t\t// @formatter:off\n\t\tthis.clientRegistration = ClientRegistration.withRegistrationId(\"token-exchange\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.tokenUri(\"https://example.com/oauth2/token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.subjectToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.actorToken = TestOAuth2AccessTokens.noScopes();\n\t\tthis.principal = new TestingAuthenticationToken(this.subjectToken, this.subjectToken);\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseClientWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setAccessTokenResponseClient(null))\n\t\t\t\t.withMessage(\"accessTokenResponseClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setSubjectTokenResolverWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setSubjectTokenResolver(null))\n\t\t\t\t.withMessage(\"subjectTokenResolver cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setActorTokenResolverWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setActorTokenResolver(null))\n\t\t\t\t.withMessage(\"actorTokenResolver cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(null))\n\t\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(-1)))\n\t\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.setClock(null))\n\t\t\t\t.withMessage(\"clock cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(null).block())\n\t\t\t\t.withMessage(\"context cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authorizeWhenNotTokenExchangeThenUnableToAuthorize() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials().build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t\tverifyNoInteractions(this.accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndTokenNotExpiredThenNotReauthorized() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.scopes(\"read\", \"write\"));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t\tverifyNoInteractions(this.accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenInvalidRequestThenThrowClientAuthorizationException() {\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class))).willReturn(\n\t\t\t\tMono.error(new OAuth2AuthorizationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST))));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.authorizedClientProvider.authorize(authorizationContext).block())\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST))\n\t\t\t\t.withMessageContaining(\"[invalid_request]\");\n\t\t// @formatter:on\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndTokenExpiredThenReauthorized() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.minus(Duration.ofMinutes(30));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\",\n\t\t\t\tissuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), accessToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext)\n\t\t\t.block();\n\t\tassertThat(reauthorizedClient).isNotNull();\n\t\tassertThat(reauthorizedClient).isNotEqualTo(authorizedClient);\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndTokenNotExpiredButClockSkewForcesExpiryThenReauthorized() {\n\t\tInstant now = Instant.now();\n\t\tInstant issuedAt = now.minus(Duration.ofMinutes(60));\n\t\tInstant expiresAt = now.plus(Duration.ofMinutes(1));\n\t\tOAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token-1234\", issuedAt, expiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), expiresInOneMinAccessToken);\n\t\t// Shorten the lifespan of the access token by 90 seconds, which will ultimately\n\t\t// force it to expire on the client\n\t\tthis.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient reauthorizedClient = this.authorizedClientProvider.authorize(authorizationContext)\n\t\t\t.block();\n\t\tassertThat(reauthorizedClient).isNotNull();\n\t\tassertThat(reauthorizedClient).isNotEqualTo(authorizedClient);\n\t\tassertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndNotAuthorizedAndSubjectTokenDoesNotResolveThenUnableToAuthorize() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(new TestingAuthenticationToken(\"user\", \"password\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t\tverifyNoInteractions(this.accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenTokenExchangeAndNotAuthorizedAndSubjectTokenResolvesThenAuthorized() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenCustomSubjectTokenResolverSetThenCalled() {\n\t\tFunction<OAuth2AuthorizationContext, Mono<OAuth2Token>> subjectTokenResolver = mock(Function.class);\n\t\tgiven(subjectTokenResolver.apply(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(this.subjectToken));\n\t\tthis.authorizedClientProvider.setSubjectTokenResolver(subjectTokenResolver);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\t\tTestingAuthenticationToken principal = new TestingAuthenticationToken(\"user\", \"password\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tverify(subjectTokenResolver).apply(authorizationContext);\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void authorizeWhenCustomActorTokenResolverSetThenCalled() {\n\t\tFunction<OAuth2AuthorizationContext, Mono<OAuth2Token>> actorTokenResolver = mock(Function.class);\n\t\tgiven(actorTokenResolver.apply(any(OAuth2AuthorizationContext.class))).willReturn(Mono.just(this.actorToken));\n\t\tthis.authorizedClientProvider.setActorTokenResolver(actorTokenResolver);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withClientRegistration(this.clientRegistration)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientProvider.authorize(authorizationContext).block();\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t\tverify(actorTokenResolver).apply(authorizationContext);\n\t\tArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor\n\t\t\t.forClass(TokenExchangeGrantRequest.class);\n\t\tverify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());\n\t\tTokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue();\n\t\tassertThat(grantRequest.getSubjectToken()).isEqualTo(this.subjectToken);\n\t\tassertThat(grantRequest.getActorToken()).isEqualTo(this.actorToken);\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClockSetThenCalled() {\n\t\tClock clock = mock(Clock.class);\n\t\tgiven(clock.instant()).willReturn(Instant.now());\n\t\tthis.authorizedClientProvider.setClock(clock);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes());\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationContext authorizationContext = OAuth2AuthorizationContext\n\t\t\t\t.withAuthorizedClient(authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.authorizedClientProvider.authorize(authorizationContext).block()).isNull();\n\t\tverify(clock).instant();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/aot/hint/OAuth2ClientRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.aot.hint;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2ClientRuntimeHints}\n */\nclass OAuth2ClientRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getOAuth2ClientSchemaFiles\")\n\tvoid oauth2ClientSchemaFilesHasHints(String schemaFile) {\n\t\tassertThat(RuntimeHintsPredicates.resource().forResource(schemaFile)).accepts(this.hints);\n\t}\n\n\tprivate static Stream<String> getOAuth2ClientSchemaFiles() {\n\t\treturn Stream.of(\"org/springframework/security/oauth2/client/oauth2-client-schema.sql\",\n\t\t\t\t\"org/springframework/security/oauth2/client/oauth2-client-schema-postgres.sql\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OAuth2AuthenticationToken}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthenticationTokenTests {\n\n\tprivate OAuth2User principal;\n\n\tprivate Collection<? extends GrantedAuthority> authorities;\n\n\tprivate String authorizedClientRegistrationId;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.principal = mock(OAuth2User.class);\n\t\tthis.authorities = Collections.emptyList();\n\t\tthis.authorizedClientRegistrationId = \"client-registration-1\";\n\t}\n\n\t@Test\n\tpublic void constructorWhenPrincipalIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new OAuth2AuthenticationToken(null, this.authorities, this.authorizedClientRegistrationId));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIsNullThenCreated() {\n\t\tnew OAuth2AuthenticationToken(this.principal, null, this.authorizedClientRegistrationId);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIsEmptyThenCreated() {\n\t\tnew OAuth2AuthenticationToken(this.principal, Collections.emptyList(), this.authorizedClientRegistrationId);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthenticationToken(this.principal, this.authorities, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(this.principal, this.authorities,\n\t\t\t\tthis.authorizedClientRegistrationId);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(this.authorities);\n\t\tassertThat(authentication.getAuthorizedClientRegistrationId()).isEqualTo(this.authorizedClientRegistrationId);\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void toBuilderWhenApplyThenCopies() {\n\t\tOAuth2AuthenticationToken factorOne = new OAuth2AuthenticationToken(TestOAuth2Users.create(),\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"), \"alice\");\n\t\tOAuth2AuthenticationToken factorTwo = new OAuth2AuthenticationToken(TestOAuth2Users.create(),\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"), \"bob\");\n\t\tOAuth2AuthenticationToken result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.authorizedClientRegistrationId(factorTwo.getAuthorizedClientRegistrationId())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(result.getAuthorizedClientRegistrationId()).isSameAs(factorTwo.getAuthorizedClientRegistrationId());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OAuth2AuthorizationCodeAuthenticationProvider}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationCodeAuthenticationProviderTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizationRequest authorizationRequest;\n\n\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\tprivate OAuth2AuthorizationCodeAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizationRequest = TestOAuth2AuthorizationRequests.request().build();\n\t\tthis.accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.authenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(this.accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAccessTokenResponseClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationProvider(null));\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2AuthorizationCodeAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2AuthorizationCodeAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.error()\n\t\t\t.errorCode(OAuth2ErrorCodes.INVALID_REQUEST)\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(\n\t\t\t\t\tnew OAuth2AuthorizationCodeAuthenticationToken(this.clientRegistration, authorizationExchange)))\n\t\t\t.withMessageContaining(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationResponseStateNotEqualAuthorizationRequestStateThenThrowOAuth2AuthorizationException() {\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.success()\n\t\t\t.state(\"67890\")\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(\n\t\t\t\t\tnew OAuth2AuthorizationCodeAuthenticationToken(this.clientRegistration, authorizationExchange)))\n\t\t\t.withMessageContaining(\"invalid_state_parameter\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationSuccessResponseThenExchangedForAccessToken() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.refreshToken(\"refresh\")\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tTestOAuth2AuthorizationResponses.success().build());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(\n\t\t\t\t\tnew OAuth2AuthorizationCodeAuthenticationToken(this.clientRegistration, authorizationExchange));\n\t\tassertThat(authenticationResult.isAuthenticated()).isTrue();\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(this.clientRegistration.getClientId());\n\t\tassertThat(authenticationResult.getCredentials())\n\t\t\t.isEqualTo(accessTokenResponse.getAccessToken().getTokenValue());\n\t\tassertThat(authenticationResult.getAuthorities()).isEqualTo(Collections.emptyList());\n\t\tassertThat(authenticationResult.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authenticationResult.getAuthorizationExchange()).isEqualTo(authorizationExchange);\n\t\tassertThat(authenticationResult.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(authenticationResult.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t}\n\n\t// gh-5368\n\t@Test\n\tpublic void authenticateWhenAuthorizationSuccessResponseThenAdditionalParametersIncluded() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tTestOAuth2AuthorizationResponses.success().build());\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = (OAuth2AuthorizationCodeAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(\n\t\t\t\t\tnew OAuth2AuthorizationCodeAuthenticationToken(this.clientRegistration, authorizationExchange));\n\t\tassertThat(authentication.getAdditionalParameters())\n\t\t\t.containsAllEntriesOf(accessTokenResponse.getAdditionalParameters());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizationCodeAuthenticationToken}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationCodeAuthenticationTokenTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizationExchange authorizationExchange;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizationExchange = new OAuth2AuthorizationExchange(TestOAuth2AuthorizationRequests.request().build(),\n\t\t\t\tTestOAuth2AuthorizationResponses.success().code(\"code\").build());\n\t\tthis.accessToken = TestOAuth2AccessTokens.noScopes();\n\t}\n\n\t@Test\n\tpublic void constructorAuthorizationRequestResponseWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(null, this.authorizationExchange));\n\t}\n\n\t@Test\n\tpublic void constructorAuthorizationRequestResponseWhenAuthorizationExchangeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(this.clientRegistration, null));\n\t}\n\n\t@Test\n\tpublic void constructorAuthorizationRequestResponseWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tthis.clientRegistration, this.authorizationExchange);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientRegistration.getClientId());\n\t\tassertThat(authentication.getCredentials())\n\t\t\t.isEqualTo(this.authorizationExchange.getAuthorizationResponse().getCode());\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(Collections.emptyList());\n\t\tassertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);\n\t\tassertThat(authentication.getAccessToken()).isNull();\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(false);\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(null,\n\t\t\t\tthis.authorizationExchange, this.accessToken));\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenAuthorizationExchangeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new OAuth2AuthorizationCodeAuthenticationToken(this.clientRegistration, null, this.accessToken));\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenAccessTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(this.clientRegistration,\n\t\t\t\t\tthis.authorizationExchange, null));\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tthis.clientRegistration, this.authorizationExchange, this.accessToken);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.clientRegistration.getClientId());\n\t\tassertThat(authentication.getCredentials()).isEqualTo(this.accessToken.getTokenValue());\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(Collections.emptyList());\n\t\tassertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);\n\t\tassertThat(authentication.getAccessToken()).isEqualTo(this.accessToken);\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(true);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class OAuth2AuthorizationCodeReactiveAuthenticationManagerTests {\n\n\t@Mock\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\tprivate OAuth2AuthorizationCodeReactiveAuthenticationManager manager;\n\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration();\n\n\tprivate OAuth2AuthorizationRequest.Builder authorizationRequest = TestOAuth2AuthorizationRequests.request();\n\n\tprivate OAuth2AuthorizationResponse.Builder authorizationResponse = TestOAuth2AuthorizationResponses.success();\n\n\tprivate OAuth2AccessTokenResponse.Builder tokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.manager = new OAuth2AuthorizationCodeReactiveAuthenticationManager(this.accessTokenResponseClient);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenErrorThenOAuth2AuthorizationException() {\n\t\tthis.authorizationResponse = TestOAuth2AuthorizationResponses.error();\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(this::authenticate);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenStateNotEqualThenOAuth2AuthorizationException() {\n\t\tthis.authorizationRequest.state(\"notequal\");\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(this::authenticate);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidThenSuccess() {\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(this.tokenResponse.build()));\n\t\tOAuth2AuthorizationCodeAuthenticationToken result = authenticate();\n\t\tassertThat(result).isNotNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenEmptyThenEmpty() {\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.empty());\n\t\tOAuth2AuthorizationCodeAuthenticationToken result = authenticate();\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenOAuth2AuthorizationExceptionThenOAuth2AuthorizationException() {\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any()))\n\t\t\t.willReturn(Mono.error(() -> new OAuth2AuthorizationException(new OAuth2Error(\"error\"))));\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(() -> authenticate());\n\t}\n\n\tprivate OAuth2AuthorizationCodeAuthenticationToken authenticate() {\n\t\tOAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(this.authorizationRequest.build(),\n\t\t\t\tthis.authorizationResponse.build());\n\t\tOAuth2AuthorizationCodeAuthenticationToken token = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tthis.registration.build(), exchange);\n\t\treturn (OAuth2AuthorizationCodeAuthenticationToken) this.manager.authenticate(token).block();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyCollection;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2LoginAuthenticationProvider}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2LoginAuthenticationProviderTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizationRequest authorizationRequest;\n\n\tprivate OAuth2AuthorizationResponse authorizationResponse;\n\n\tprivate OAuth2AuthorizationExchange authorizationExchange;\n\n\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\tprivate OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;\n\n\tprivate OAuth2LoginAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizationRequest = TestOAuth2AuthorizationRequests.request().scope(\"scope1\", \"scope2\").build();\n\t\tthis.authorizationResponse = TestOAuth2AuthorizationResponses.success().build();\n\t\tthis.authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tthis.authorizationResponse);\n\t\tthis.accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.userService = mock(OAuth2UserService.class);\n\t\tthis.authenticationProvider = new OAuth2LoginAuthenticationProvider(this.accessTokenResponseClient,\n\t\t\t\tthis.userService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAccessTokenResponseClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationProvider(null, this.userService));\n\t}\n\n\t@Test\n\tpublic void constructorWhenUserServiceIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationProvider(this.accessTokenResponseClient, null));\n\t}\n\n\t@Test\n\tpublic void setAuthoritiesMapperWhenAuthoritiesMapperIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authenticationProvider.setAuthoritiesMapper(null));\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2LoginAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2LoginAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationRequestContainsOpenidScopeThenReturnNull() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.scope(\"openid\")\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tthis.authorizationResponse);\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, authorizationExchange));\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationErrorResponseThenThrowOAuth2AuthenticationException() {\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.error()\n\t\t\t.errorCode(OAuth2ErrorCodes.INVALID_REQUEST)\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider\n\t\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, authorizationExchange)))\n\t\t\t.withMessageContaining(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationResponseStateNotEqualAuthorizationRequestStateThenThrowOAuth2AuthenticationException() {\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.success()\n\t\t\t.state(\"67890\")\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider\n\t\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, authorizationExchange)))\n\t\t\t.withMessageContaining(\"invalid_state_parameter\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenLoginSuccessThenReturnAuthentication() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.accessTokenSuccessResponse();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User principal = mock(OAuth2User.class);\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tgiven(principal.getAuthorities()).willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> authorities);\n\t\tgiven(this.userService.loadUser(any())).willReturn(principal);\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange));\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(authentication.getAuthorities()).containsAll(authorities);\n\t\tassertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);\n\t\tassertThat(authentication.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());\n\t\tassertThat(authentication.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthoritiesMapperSetThenReturnMappedAuthorities() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.accessTokenSuccessResponse();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User principal = mock(OAuth2User.class);\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tgiven(principal.getAuthorities()).willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> authorities);\n\t\tgiven(this.userService.loadUser(any())).willReturn(principal);\n\t\tList<GrantedAuthority> mappedAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_OAUTH2_USER\");\n\t\tGrantedAuthoritiesMapper authoritiesMapper = mock(GrantedAuthoritiesMapper.class);\n\t\tgiven(authoritiesMapper.mapAuthorities(anyCollection()))\n\t\t\t.willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> mappedAuthorities);\n\t\tthis.authenticationProvider.setAuthoritiesMapper(authoritiesMapper);\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange));\n\t\tverify(authoritiesMapper).mapAuthorities(any());\n\t\tSecurityAssertions.assertThat(authentication).authorities().containsAll(mappedAuthorities);\n\t}\n\n\t// gh-5368\n\t@Test\n\tpublic void authenticateWhenTokenSuccessResponseThenAdditionalParametersAddedToUserRequest() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.accessTokenSuccessResponse();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tOAuth2User principal = mock(OAuth2User.class);\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tgiven(principal.getAuthorities()).willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> authorities);\n\t\tArgumentCaptor<OAuth2UserRequest> userRequestArgCaptor = ArgumentCaptor.forClass(OAuth2UserRequest.class);\n\t\tgiven(this.userService.loadUser(userRequestArgCaptor.capture())).willReturn(principal);\n\t\tthis.authenticationProvider\n\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange));\n\t\tassertThat(userRequestArgCaptor.getValue().getAdditionalParameters())\n\t\t\t.containsAllEntriesOf(accessTokenResponse.getAdditionalParameters());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenLoginSuccessThenIssuesFactor() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = accessTokenSuccessResponse();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tgiven(this.userService.loadUser(any())).willReturn(TestOAuth2Users.create());\n\t\tAuthentication request = new OAuth2LoginAuthenticationToken(this.clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tAuthentication result = this.authenticationProvider.authenticate(request);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.AUTHORIZATION_CODE_AUTHORITY);\n\t}\n\n\tprivate OAuth2AccessTokenResponse accessTokenSuccessResponse() {\n\t\tInstant expiresAt = Instant.now().plusSeconds(5);\n\t\tSet<String> scopes = new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\t// @formatter:off\n\t\treturn OAuth2AccessTokenResponse.withToken(\"access-token-1234\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(expiresAt.getEpochSecond())\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.refreshToken(\"refresh-token-1234\")\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OAuth2LoginAuthenticationToken}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2LoginAuthenticationTokenTests {\n\n\tprivate OAuth2User principal;\n\n\tprivate Collection<? extends GrantedAuthority> authorities;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizationExchange authorizationExchange;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.principal = mock(OAuth2User.class);\n\t\tthis.authorities = Collections.emptyList();\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizationExchange = new OAuth2AuthorizationExchange(TestOAuth2AuthorizationRequests.request().build(),\n\t\t\t\tTestOAuth2AuthorizationResponses.success().code(\"code\").build());\n\t\tthis.accessToken = TestOAuth2AccessTokens.noScopes();\n\t}\n\n\t@Test\n\tpublic void constructorAuthorizationRequestResponseWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationToken(null, this.authorizationExchange));\n\t}\n\n\t@Test\n\tpublic void constructorAuthorizationRequestResponseWhenAuthorizationExchangeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationToken(this.clientRegistration, null));\n\t}\n\n\t@Test\n\tpublic void constructorAuthorizationRequestResponseWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2LoginAuthenticationToken authentication = new OAuth2LoginAuthenticationToken(this.clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tassertThat(authentication.getPrincipal()).isNull();\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(Collections.emptyList());\n\t\tassertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);\n\t\tassertThat(authentication.getAccessToken()).isNull();\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(false);\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2LoginAuthenticationToken(null,\n\t\t\t\tthis.authorizationExchange, this.principal, this.authorities, this.accessToken));\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenAuthorizationExchangeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationToken(this.clientRegistration, null, this.principal,\n\t\t\t\t\tthis.authorities, this.accessToken));\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenPrincipalIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange,\n\t\t\t\t\tnull, this.authorities, this.accessToken));\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenAuthoritiesIsNullThenCreated() {\n\t\tnew OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange, this.principal, null,\n\t\t\t\tthis.accessToken);\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenAuthoritiesIsEmptyThenCreated() {\n\t\tnew OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange, this.principal,\n\t\t\t\tCollections.emptyList(), this.accessToken);\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenAccessTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange,\n\t\t\t\t\tthis.principal, this.authorities, null));\n\t}\n\n\t@Test\n\tpublic void constructorTokenRequestResponseWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2LoginAuthenticationToken authentication = new OAuth2LoginAuthenticationToken(this.clientRegistration,\n\t\t\t\tthis.authorizationExchange, this.principal, this.authorities, this.accessToken);\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(this.authorities);\n\t\tassertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);\n\t\tassertThat(authentication.getAccessToken()).isEqualTo(this.accessToken);\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(true);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.stubbing.Answer;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyCollection;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class OAuth2LoginReactiveAuthenticationManagerTests {\n\n\t@Mock\n\tprivate ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService;\n\n\t@Mock\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\t@Mock\n\tprivate ReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration();\n\n\tOAuth2AuthorizationResponse.Builder authorizationResponseBldr = OAuth2AuthorizationResponse.success(\"code\")\n\t\t.state(\"state\");\n\n\tprivate OAuth2LoginReactiveAuthenticationManager manager;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.manager = new OAuth2LoginReactiveAuthenticationManager(this.accessTokenResponseClient, this.userService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullAccessTokenResponseClientThenIllegalArgumentException() {\n\t\tthis.accessTokenResponseClient = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new OAuth2LoginReactiveAuthenticationManager(this.accessTokenResponseClient, this.userService));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullUserServiceThenIllegalArgumentException() {\n\t\tthis.userService = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new OAuth2LoginReactiveAuthenticationManager(this.accessTokenResponseClient, this.userService));\n\t}\n\n\t@Test\n\tpublic void setAuthoritiesMapperWhenAuthoritiesMapperIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.manager.setAuthoritiesMapper(null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNoSubscriptionThenDoesNothing() {\n\t\t// we didn't do anything because it should cause a ClassCastException (as verified\n\t\t// below)\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"a\", \"b\");\n\t\tthis.manager.authenticate(token);\n\t\tassertThatExceptionOfType(Throwable.class).isThrownBy(() -> this.manager.authenticate(token).block());\n\t}\n\n\t@Test\n\t@Disabled\n\tpublic void authenticationWhenOidcThenEmpty() {\n\t\tthis.registration.scope(\"openid\");\n\t\tassertThat(this.manager.authenticate(loginToken()).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticationWhenErrorThenOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tthis.authorizationResponseBldr = OAuth2AuthorizationResponse\n\t\t\t\t.error(\"error\")\n\t\t\t\t.state(\"state\");\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(loginToken()).block());\n\t}\n\n\t@Test\n\tpublic void authenticationWhenStateDoesNotMatchThenOAuth2AuthenticationException() {\n\t\tthis.authorizationResponseBldr.state(\"notmatch\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(loginToken()).block());\n\t}\n\n\t@Test\n\tpublic void authenticationWhenOAuth2UserNotFoundThenEmpty() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"foo\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tgiven(this.userService.loadUser(any())).willReturn(Mono.empty());\n\t\tassertThat(this.manager.authenticate(loginToken()).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticationWhenOAuth2UserFoundThenSuccess() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"foo\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tDefaultOAuth2User user = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"),\n\t\t\t\tCollections.singletonMap(\"user\", \"rob\"), \"user\");\n\t\tgiven(this.userService.loadUser(any())).willReturn(Mono.just(user));\n\t\tOAuth2LoginAuthenticationToken result = (OAuth2LoginAuthenticationToken) this.manager.authenticate(loginToken())\n\t\t\t.block();\n\t\tassertThat(result.getPrincipal()).isEqualTo(user);\n\t\tassertThat(result.getAuthorities()).containsOnlyElementsOf(user.getAuthorities());\n\t\tassertThat(result.isAuthenticated()).isTrue();\n\t}\n\n\t// gh-5368\n\t@Test\n\tpublic void authenticateWhenTokenSuccessResponseThenAdditionalParametersAddedToUserRequest() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"foo\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tDefaultOAuth2User user = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"),\n\t\t\t\tCollections.singletonMap(\"user\", \"rob\"), \"user\");\n\t\tArgumentCaptor<OAuth2UserRequest> userRequestArgCaptor = ArgumentCaptor.forClass(OAuth2UserRequest.class);\n\t\tgiven(this.userService.loadUser(userRequestArgCaptor.capture())).willReturn(Mono.just(user));\n\t\tthis.manager.authenticate(loginToken()).block();\n\t\tassertThat(userRequestArgCaptor.getValue().getAdditionalParameters())\n\t\t\t.containsAllEntriesOf(accessTokenResponse.getAdditionalParameters());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthoritiesMapperSetThenReturnMappedAuthorities() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"foo\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tDefaultOAuth2User user = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"),\n\t\t\t\tCollections.singletonMap(\"user\", \"rob\"), \"user\");\n\t\tgiven(this.userService.loadUser(any())).willReturn(Mono.just(user));\n\t\tList<GrantedAuthority> mappedAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_OAUTH_USER\");\n\t\tGrantedAuthoritiesMapper authoritiesMapper = mock(GrantedAuthoritiesMapper.class);\n\t\tgiven(authoritiesMapper.mapAuthorities(anyCollection()))\n\t\t\t.willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> mappedAuthorities);\n\t\tthis.manager.setAuthoritiesMapper(authoritiesMapper);\n\t\tOAuth2LoginAuthenticationToken result = (OAuth2LoginAuthenticationToken) this.manager.authenticate(loginToken())\n\t\t\t.block();\n\t\tassertThat(result.getAuthorities()).isEqualTo(mappedAuthorities);\n\t}\n\n\tprivate OAuth2AuthorizationCodeAuthenticationToken loginToken() {\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.state(\"state\")\n\t\t\t.clientId(clientRegistration.getClientId())\n\t\t\t.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t.scopes(clientRegistration.getScopes())\n\t\t\t.build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = this.authorizationResponseBldr\n\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\treturn new OAuth2AuthorizationCodeAuthenticationToken(clientRegistration, authorizationExchange);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/TestOAuth2AuthenticationTokens.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\n\n/**\n * @author Josh Cummings\n * @since 5.2\n */\npublic final class TestOAuth2AuthenticationTokens {\n\n\tprivate TestOAuth2AuthenticationTokens() {\n\t}\n\n\tpublic static OAuth2AuthenticationToken authenticated() {\n\t\tDefaultOAuth2User principal = TestOAuth2Users.create();\n\t\tString registrationId = \"registration-id\";\n\t\treturn new OAuth2AuthenticationToken(principal, principal.getAuthorities(), registrationId);\n\t}\n\n\tpublic static OAuth2AuthenticationToken oidcAuthenticated() {\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tString registrationId = \"registration-id\";\n\t\treturn new OAuth2AuthenticationToken(principal, principal.getAuthorities(), registrationId);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/TestOAuth2AuthorizationCodeAuthenticationTokens.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.authentication;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic final class TestOAuth2AuthorizationCodeAuthenticationTokens {\n\n\tprivate TestOAuth2AuthorizationCodeAuthenticationTokens() {\n\t}\n\n\tpublic static OAuth2AuthorizationCodeAuthenticationToken unauthenticated() {\n\t\tClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success();\n\t\treturn new OAuth2AuthorizationCodeAuthenticationToken(registration, exchange);\n\t}\n\n\tpublic static OAuth2AuthorizationCodeAuthenticationToken authenticated() {\n\t\tClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success();\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tOAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\treturn new OAuth2AuthorizationCodeAuthenticationToken(registration, exchange, accessToken, refreshToken);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultOAuth2TokenRequestHeadersConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.nio.charset.StandardCharsets;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link DefaultOAuth2TokenRequestHeadersConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class DefaultOAuth2TokenRequestHeadersConverterTests {\n\n\tprivate static final MediaType APPLICATION_FORM_URLENCODED_UTF8 = new MediaType(\n\t\t\tMediaType.APPLICATION_FORM_URLENCODED, StandardCharsets.UTF_8);\n\n\tprivate DefaultOAuth2TokenRequestHeadersConverter<OAuth2ClientCredentialsGrantRequest> converter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.converter = new DefaultOAuth2TokenRequestHeadersConverter<>();\n\t}\n\n\t@Test\n\tpublic void convertWhenEncodeClientCredentialsTrueThenConvertsWithUrlEncoding() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t\t.clientId(\"clientId\")\n\t\t\t\t.clientSecret(\"clientSecret=\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tHttpHeaders defaultHeaders = this.converter.convert(grantRequest);\n\t\tassertThat(defaultHeaders.getAccept()).containsExactly(MediaType.APPLICATION_JSON);\n\t\tassertThat(defaultHeaders.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED);\n\t\tassertThat(defaultHeaders.getFirst(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50SWQ6Y2xpZW50U2VjcmV0JTNE\");\n\t}\n\n\t@Test\n\tpublic void convertWhenEncodeClientCredentialsFalseThenConvertsWithoutUrlEncoding() {\n\t\tthis.converter.setEncodeClientCredentials(false);\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t\t.clientId(\"clientId\")\n\t\t\t\t.clientSecret(\"clientSecret=\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tHttpHeaders defaultHeaders = this.converter.convert(grantRequest);\n\t\tassertThat(defaultHeaders.getAccept()).containsExactly(MediaType.APPLICATION_JSON);\n\t\tassertThat(defaultHeaders.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED);\n\t\tassertThat(defaultHeaders.getFirst(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50SWQ6Y2xpZW50U2VjcmV0PQ==\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultOAuth2TokenRequestParametersConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link DefaultOAuth2TokenRequestParametersConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class DefaultOAuth2TokenRequestParametersConverterTests {\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.scope(\"read\", \"write\");\n\t}\n\n\t@Test\n\tpublic void convertWhenGrantRequestIsAuthorizationCodeThenParametersProvided() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.clientId(\"client-1\")\n\t\t\t.state(\"state\")\n\t\t\t.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t.attributes(Map.of(PkceParameterNames.CODE_VERIFIER, \"code-verifier\"))\n\t\t\t.scopes(clientRegistration.getScopes())\n\t\t\t.build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponse.success(\"code\")\n\t\t\t.state(\"state\")\n\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tauthorizationExchange);\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenRequestParametersConverter<OAuth2AuthorizationCodeGrantRequest> parametersConverter =\n\t\t\tnew DefaultOAuth2TokenRequestParametersConverter<>();\n\t\t// @formatter:on\n\t\tMultiValueMap<String, String> parameters = parametersConverter.convert(grantRequest);\n\t\tassertThat(parameters).hasSize(6);\n\t\tassertThat(parameters.get(OAuth2ParameterNames.GRANT_TYPE))\n\t\t\t.containsExactly(AuthorizationGrantType.AUTHORIZATION_CODE.getValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_ID)).containsExactly(clientRegistration.getClientId());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_SECRET))\n\t\t\t.containsExactly(clientRegistration.getClientSecret());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CODE)).containsExactly(authorizationResponse.getCode());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.REDIRECT_URI))\n\t\t\t.containsExactly(clientRegistration.getRedirectUri());\n\t\tassertThat(parameters.get(PkceParameterNames.CODE_VERIFIER))\n\t\t\t.containsExactly(authorizationRequest.<String>getAttribute(PkceParameterNames.CODE_VERIFIER));\n\t}\n\n\t@Test\n\tpublic void convertWhenGrantRequestIsClientCredentialsThenParametersProvided() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenRequestParametersConverter<OAuth2ClientCredentialsGrantRequest> parametersConverter =\n\t\t\tnew DefaultOAuth2TokenRequestParametersConverter<>();\n\t\t// @formatter:on\n\t\tMultiValueMap<String, String> parameters = parametersConverter.convert(grantRequest);\n\t\tassertThat(parameters).hasSize(4);\n\t\tassertThat(parameters.get(OAuth2ParameterNames.GRANT_TYPE))\n\t\t\t.containsExactly(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_ID)).containsExactly(clientRegistration.getClientId());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_SECRET))\n\t\t\t.containsExactly(clientRegistration.getClientSecret());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.SCOPE))\n\t\t\t.containsExactly(StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \" \"));\n\t}\n\n\t@Test\n\tpublic void convertWhenGrantRequestIsRefreshTokenThenParametersProvided() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.build();\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tOAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\taccessToken, refreshToken, clientRegistration.getScopes());\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenRequestParametersConverter<OAuth2RefreshTokenGrantRequest> parametersConverter =\n\t\t\tnew DefaultOAuth2TokenRequestParametersConverter<>();\n\t\t// @formatter:on\n\t\tMultiValueMap<String, String> parameters = parametersConverter.convert(grantRequest);\n\t\tassertThat(parameters).hasSize(5);\n\t\tassertThat(parameters.get(OAuth2ParameterNames.GRANT_TYPE))\n\t\t\t.containsExactly(AuthorizationGrantType.REFRESH_TOKEN.getValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_ID)).containsExactly(clientRegistration.getClientId());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_SECRET))\n\t\t\t.containsExactly(clientRegistration.getClientSecret());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.REFRESH_TOKEN)).containsExactly(refreshToken.getTokenValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.SCOPE))\n\t\t\t.containsExactly(StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \" \"));\n\t}\n\n\t@Test\n\tpublic void convertWhenGrantRequestIsJwtBearerThenParametersProvided() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t.build();\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, jwt);\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenRequestParametersConverter<JwtBearerGrantRequest> parametersConverter =\n\t\t\tnew DefaultOAuth2TokenRequestParametersConverter<>();\n\t\t// @formatter:on\n\t\tMultiValueMap<String, String> parameters = parametersConverter.convert(grantRequest);\n\t\tassertThat(parameters).hasSize(5);\n\t\tassertThat(parameters.get(OAuth2ParameterNames.GRANT_TYPE))\n\t\t\t.containsExactly(AuthorizationGrantType.JWT_BEARER.getValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_ID)).containsExactly(clientRegistration.getClientId());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_SECRET))\n\t\t\t.containsExactly(clientRegistration.getClientSecret());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.ASSERTION)).containsExactly(jwt.getTokenValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.SCOPE))\n\t\t\t.containsExactly(StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \" \"));\n\t}\n\n\t@Test\n\tpublic void convertWhenGrantRequestIsTokenExchangeThenParametersProvided() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.build();\n\t\tOAuth2Token subjectToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tOAuth2Token actorToken = TestJwts.jwt().build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, subjectToken,\n\t\t\t\tactorToken);\n\t\t// @formatter:off\n\t\tDefaultOAuth2TokenRequestParametersConverter<TokenExchangeGrantRequest> parametersConverter =\n\t\t\tnew DefaultOAuth2TokenRequestParametersConverter<>();\n\t\t// @formatter:on\n\t\tMultiValueMap<String, String> parameters = parametersConverter.convert(grantRequest);\n\t\tassertThat(parameters).hasSize(9);\n\t\tassertThat(parameters.get(OAuth2ParameterNames.GRANT_TYPE))\n\t\t\t.containsExactly(AuthorizationGrantType.TOKEN_EXCHANGE.getValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_ID)).containsExactly(clientRegistration.getClientId());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.CLIENT_SECRET))\n\t\t\t.containsExactly(clientRegistration.getClientSecret());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.SCOPE))\n\t\t\t.containsExactly(StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \" \"));\n\t\tassertThat(parameters.get(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE)).containsExactly(ACCESS_TOKEN_TYPE_VALUE);\n\t\tassertThat(parameters.get(OAuth2ParameterNames.SUBJECT_TOKEN)).containsExactly(subjectToken.getTokenValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE)).containsExactly(ACCESS_TOKEN_TYPE_VALUE);\n\t\tassertThat(parameters.get(OAuth2ParameterNames.ACTOR_TOKEN)).containsExactly(actorToken.getTokenValue());\n\t\tassertThat(parameters.get(OAuth2ParameterNames.ACTOR_TOKEN_TYPE)).containsExactly(JWT_TOKEN_TYPE_VALUE);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/JwtBearerGrantRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link JwtBearerGrantRequest}.\n *\n * @author Hassene Laaribi\n */\npublic class JwtBearerGrantRequestTests {\n\n\tprivate final ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t.build();\n\n\tprivate final Jwt jwtAssertion = TestJwts.jwt().build();\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new JwtBearerGrantRequest(null, this.jwtAssertion))\n\t\t\t.withMessage(\"clientRegistration cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwtIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new JwtBearerGrantRequest(this.clientRegistration, null))\n\t\t\t.withMessage(\"jwt cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationInvalidGrantTypeThenThrowIllegalArgumentException() {\n\t\tClientRegistration registration = TestClientRegistrations.clientCredentials().build();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new JwtBearerGrantRequest(registration, this.jwtAssertion))\n\t\t\t.withMessage(\"clientRegistration.authorizationGrantType must be AuthorizationGrantType.JWT_BEARER\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenValidParametersProvidedThenCreated() {\n\t\tJwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest(this.clientRegistration,\n\t\t\t\tthis.jwtAssertion);\n\t\tassertThat(jwtBearerGrantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER);\n\t\tassertThat(jwtBearerGrantRequest.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(jwtBearerGrantRequest.getJwt()).isSameAs(this.jwtAssertion);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.Collections;\nimport java.util.UUID;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.OctetSequenceKey;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JoseHeaderNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.jwt.NimbusJwtDecoder;\nimport org.springframework.util.MultiValueMap;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link NimbusJwtClientAuthenticationParametersConverter}.\n *\n * @author Joe Grandja\n */\npublic class NimbusJwtClientAuthenticationParametersConverterTests {\n\n\tprivate Function<ClientRegistration, JWK> jwkResolver;\n\n\tprivate NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> converter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.jwkResolver = mock(Function.class);\n\t\tthis.converter = new NimbusJwtClientAuthenticationParametersConverter<>(this.jwkResolver);\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwkResolverNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new NimbusJwtClientAuthenticationParametersConverter<>(null))\n\t\t\t.withMessage(\"jwkResolver cannot be null\");\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationGrantRequestNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.converter.convert(null))\n\t\t\t.withMessage(\"authorizationGrantRequest cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setJwtClientAssertionCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.converter.setJwtClientAssertionCustomizer(null))\n\t\t\t.withMessage(\"jwtClientAssertionCustomizer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void convertWhenOtherClientAuthenticationMethodThenNotCustomized() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tassertThat(this.converter.convert(clientCredentialsGrantRequest)).isNull();\n\t\tverifyNoInteractions(this.jwkResolver);\n\t}\n\n\t@Test\n\tpublic void convertWhenJwkNotResolvedThenThrowOAuth2AuthorizationException() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.converter.convert(clientCredentialsGrantRequest))\n\t\t\t.withMessage(\"[invalid_key] Failed to resolve JWK signing key for client registration '\"\n\t\t\t\t\t+ clientRegistration.getRegistrationId() + \"'.\");\n\t}\n\n\t@Test\n\tpublic void convertWhenPrivateKeyJwtClientAuthenticationMethodThenCustomized() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkResolver.apply(any())).willReturn(rsaJwk);\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tMultiValueMap<String, String> parameters = this.converter.convert(clientCredentialsGrantRequest);\n\n\t\tassertThat(parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE))\n\t\t\t.isEqualTo(\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\");\n\t\tString encodedJws = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION);\n\t\tassertThat(encodedJws).isNotNull();\n\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(rsaJwk.toRSAPublicKey()).build();\n\t\tJwt jws = jwtDecoder.decode(encodedJws);\n\n\t\tassertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName());\n\t\tassertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk.getKeyID());\n\t\tassertThat(jws.<String>getClaim(JwtClaimNames.ISS)).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(jws.getSubject()).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(jws.getAudience())\n\t\t\t.isEqualTo(Collections.singletonList(clientRegistration.getProviderDetails().getTokenUri()));\n\t\tassertThat(jws.getId()).isNotNull();\n\t\tassertThat(jws.getIssuedAt()).isNotNull();\n\t\tassertThat(jws.getExpiresAt()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenClientSecretJwtClientAuthenticationMethodThenCustomized() {\n\t\tOctetSequenceKey secretJwk = TestJwks.DEFAULT_SECRET_JWK;\n\t\tgiven(this.jwkResolver.apply(any())).willReturn(secretJwk);\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tMultiValueMap<String, String> parameters = this.converter.convert(clientCredentialsGrantRequest);\n\n\t\tassertThat(parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE))\n\t\t\t.isEqualTo(\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\");\n\t\tString encodedJws = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION);\n\t\tassertThat(encodedJws).isNotNull();\n\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(secretJwk.toSecretKey()).build();\n\t\tJwt jws = jwtDecoder.decode(encodedJws);\n\n\t\tassertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.ALG, MacAlgorithm.HS256.getName());\n\t\tassertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.KID, secretJwk.getKeyID());\n\t\tassertThat(jws.<String>getClaim(JwtClaimNames.ISS)).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(jws.getSubject()).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(jws.getAudience())\n\t\t\t.isEqualTo(Collections.singletonList(clientRegistration.getProviderDetails().getTokenUri()));\n\t\tassertThat(jws.getId()).isNotNull();\n\t\tassertThat(jws.getIssuedAt()).isNotNull();\n\t\tassertThat(jws.getExpiresAt()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenJwtClientAssertionCustomizerSetThenUsed() {\n\t\tOctetSequenceKey secretJwk = TestJwks.DEFAULT_SECRET_JWK;\n\t\tgiven(this.jwkResolver.apply(any())).willReturn(secretJwk);\n\n\t\tString headerName = \"custom-header\";\n\t\tString headerValue = \"header-value\";\n\t\tString claimName = \"custom-claim\";\n\t\tString claimValue = \"claim-value\";\n\t\tthis.converter.setJwtClientAssertionCustomizer((context) -> {\n\t\t\tcontext.getHeaders().header(headerName, headerValue);\n\t\t\tcontext.getClaims().claim(claimName, claimValue);\n\t\t});\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tMultiValueMap<String, String> parameters = this.converter.convert(clientCredentialsGrantRequest);\n\n\t\tassertThat(parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE))\n\t\t\t.isEqualTo(\"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\");\n\t\tString encodedJws = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION);\n\t\tassertThat(encodedJws).isNotNull();\n\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(secretJwk.toSecretKey()).build();\n\t\tJwt jws = jwtDecoder.decode(encodedJws);\n\n\t\tassertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.ALG, MacAlgorithm.HS256.getName());\n\t\tassertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.KID, secretJwk.getKeyID());\n\t\tassertThat(jws.getHeaders()).containsEntry(headerName, headerValue);\n\t\tassertThat(jws.<String>getClaim(JwtClaimNames.ISS)).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(jws.getSubject()).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(jws.getAudience())\n\t\t\t.isEqualTo(Collections.singletonList(clientRegistration.getProviderDetails().getTokenUri()));\n\t\tassertThat(jws.getId()).isNotNull();\n\t\tassertThat(jws.getIssuedAt()).isNotNull();\n\t\tassertThat(jws.getExpiresAt()).isNotNull();\n\t\tassertThat(jws.getClaimAsString(claimName)).isEqualTo(claimValue);\n\t}\n\n\t// gh-9814\n\t@Test\n\tpublic void convertWhenClientKeyChangesThenNewKeyUsed() throws Exception {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tRSAKey rsaJwk1 = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkResolver.apply(eq(clientRegistration))).willReturn(rsaJwk1);\n\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tMultiValueMap<String, String> parameters = this.converter.convert(clientCredentialsGrantRequest);\n\n\t\tString encodedJws = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION);\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(rsaJwk1.toRSAPublicKey()).build();\n\t\tjwtDecoder.decode(encodedJws);\n\n\t\tRSAKey rsaJwk2 = generateRsaJwk();\n\t\tgiven(this.jwkResolver.apply(eq(clientRegistration))).willReturn(rsaJwk2);\n\n\t\tparameters = this.converter.convert(clientCredentialsGrantRequest);\n\n\t\tencodedJws = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION);\n\t\tjwtDecoder = NimbusJwtDecoder.withPublicKey(rsaJwk2.toRSAPublicKey()).build();\n\t\tjwtDecoder.decode(encodedJws);\n\t}\n\n\tprivate static RSAKey generateRsaJwk() {\n\t\tKeyPair keyPair;\n\t\ttry {\n\t\t\tKeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"RSA\");\n\t\t\tkeyPairGenerator.initialize(2048);\n\t\t\tkeyPair = keyPairGenerator.generateKeyPair();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t\tRSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();\n\t\tRSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();\n\t\t// @formatter:off\n\t\treturn new RSAKey.Builder(publicKey)\n\t\t\t\t.privateKey(privateKey)\n\t\t\t\t.keyID(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizationCodeGrantRequest}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationCodeGrantRequestTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizationExchange authorizationExchange;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizationExchange = TestOAuth2AuthorizationExchanges.success();\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeGrantRequest(null, this.authorizationExchange));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationExchangeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeGrantRequest(this.clientRegistration, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest = new OAuth2AuthorizationCodeGrantRequest(\n\t\t\t\tthis.clientRegistration, this.authorizationExchange);\n\t\tassertThat(authorizationCodeGrantRequest.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationCodeGrantRequest.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);\n\t\tassertThat(authorizationCodeGrantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2ClientCredentialsGrantRequest}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2ClientCredentialsGrantRequestTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.clientRegistration = ClientRegistration.withRegistrationId(\"registration-1\")\n\t\t\t\t.clientId(\"client-1\")\n\t\t\t\t.clientSecret(\"secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.tokenUri(\"https://provider.com/oauth2/token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2ClientCredentialsGrantRequest(null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenValidParametersProvidedThenCreated() {\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration);\n\t\tassertThat(clientCredentialsGrantRequest.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(clientCredentialsGrantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2RefreshTokenGrantRequest}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2RefreshTokenGrantRequestTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate OAuth2RefreshToken refreshToken;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2RefreshTokenGrantRequest(null, this.accessToken, this.refreshToken))\n\t\t\t.withMessage(\"clientRegistration cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAccessTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2RefreshTokenGrantRequest(this.clientRegistration, null, this.refreshToken))\n\t\t\t.withMessage(\"accessToken cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenRefreshTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2RefreshTokenGrantRequest(this.clientRegistration, this.accessToken, null))\n\t\t\t.withMessage(\"refreshToken cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenValidParametersProvidedThenCreated() {\n\t\tSet<String> scopes = new HashSet<>(Arrays.asList(\"read\", \"write\"));\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistration, this.accessToken, this.refreshToken, scopes);\n\t\tassertThat(refreshTokenGrantRequest.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(refreshTokenGrantRequest.getAccessToken()).isSameAs(this.accessToken);\n\t\tassertThat(refreshTokenGrantRequest.getRefreshToken()).isSameAs(this.refreshToken);\n\t\tassertThat(refreshTokenGrantRequest.getScopes()).isEqualTo(scopes);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/RestClientAuthorizationCodeTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.function.Consumer;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.FormHttpMessageConverter;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link RestClientAuthorizationCodeTokenResponseClient}.\n *\n * @author Steve Riesenberg\n */\npublic class RestClientAuthorizationCodeTokenResponseClientTests {\n\n\tprivate RestClientAuthorizationCodeTokenResponseClient tokenResponseClient;\n\n\tprivate MockWebServer server;\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\tprivate OAuth2AuthorizationExchange authorizationExchange;\n\n\t@BeforeEach\n\tpublic void setUp() throws IOException {\n\t\tthis.tokenResponseClient = new RestClientAuthorizationCodeTokenResponseClient();\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.scope(\"read\", \"write\");\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.clientId(\"client-1\")\n\t\t\t.state(\"state\")\n\t\t\t.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t.scopes(clientRegistration.getScopes())\n\t\t\t.build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponse.success(\"code\")\n\t\t\t.state(\"state\")\n\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t.build();\n\t\tthis.authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse);\n\t}\n\n\t@AfterEach\n\tpublic void cleanUp() throws IOException {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setRestClientWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setRestClient(null))\n\t\t\t\t.withMessage(\"restClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersCustomizer(null))\n\t\t\t.withMessage(\"parametersCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenGrantRequestIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(null))\n\t\t\t\t.withMessage(\"grantRequest cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.CODE, \"code\")\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretBasicThenAuthorizationHeaderIsSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_SECRET, \"secret\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\")\n\t\t\t\t.havingRootCause().withMessage(\"tokenType cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessTokenHasNoScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenInvalidResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(301));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest request = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessage(\"[invalid_token_response] Empty OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"server-error-response.json\").setResponseCode(500));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest request = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-grant-response.json\").setResponseCode(400));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest request = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT))\n\t\t\t\t.withMessage(\"[invalid_grant] Invalid grant\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tConverter<OAuth2AuthorizationCodeGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.addHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tConverter<OAuth2AuthorizationCodeGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.setHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tConverter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(param(\"custom-parameter-name\", \"custom-parameter-value\"));\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.CODE, \"custom-code\");\n\t\tparameters.set(OAuth2ParameterNames.REDIRECT_URI, \"custom-uri\");\n\t\tthis.tokenResponseClient.setParametersConverter((authorizationGrantRequest) -> parameters);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.CODE, \"custom-code\"),\n\t\t\t\tparam(OAuth2ParameterNames.REDIRECT_URI, \"custom-uri\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tConverter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.addParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.CODE, \"code\"),\n\t\t\t\tparam(\"custom-parameter-name\", \"custom-parameter-value\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.tokenResponseClient.setParametersCustomizer(parametersCustomizer);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenRestClientSetThenCalled() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tRestClient restClient = RestClient.builder().messageConverters((messageConverters) -> {\n\t\t\tmessageConverters.add(0, new FormHttpMessageConverter());\n\t\t\tmessageConverters.add(1, new OAuth2AccessTokenResponseHttpMessageConverter());\n\t\t}).build();\n\t\tRestClient customClient = spy(restClient);\n\t\tthis.tokenResponseClient.setRestClient(customClient);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2AuthorizationCodeGrantRequest grantRequest = new OAuth2AuthorizationCodeGrantRequest(clientRegistration,\n\t\t\t\tthis.authorizationExchange);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(customClient).post();\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/RestClientClientCredentialsTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.FormHttpMessageConverter;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link RestClientClientCredentialsTokenResponseClient}.\n *\n * @author Steve Riesenberg\n */\npublic class RestClientClientCredentialsTokenResponseClientTests {\n\n\tprivate RestClientClientCredentialsTokenResponseClient tokenResponseClient;\n\n\tprivate MockWebServer server;\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\t@BeforeEach\n\tpublic void setUp() throws IOException {\n\t\tthis.tokenResponseClient = new RestClientClientCredentialsTokenResponseClient();\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.scope(\"read\", \"write\");\n\t}\n\n\t@AfterEach\n\tpublic void cleanUp() throws IOException {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setRestClientWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setRestClient(null))\n\t\t\t\t.withMessage(\"restClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersCustomizer(null))\n\t\t\t.withMessage(\"parametersCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenGrantRequestIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(null))\n\t\t\t\t.withMessage(\"grantRequest cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretBasicThenAuthorizationHeaderIsSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_SECRET, \"secret\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\")\n\t\t\t\t.havingRootCause().withMessage(\"tokenType cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessTokenHasNoScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenRequestDoesNotIncludeScopeThenAccessTokenHasNoScope() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(\"no-scope\")\n\t\t\t\t.clientId(\"client-1\")\n\t\t\t\t.clientSecret(\"secret\")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.tokenUri(this.server.url(\"/oauth2/token\").toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(formParameters).doesNotContain(OAuth2ParameterNames.SCOPE);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenInvalidResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(301));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessage(\"[invalid_token_response] Empty OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"server-error-response.json\").setResponseCode(500));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-grant-response.json\").setResponseCode(400));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT))\n\t\t\t\t.withMessage(\"[invalid_grant] Invalid grant\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.addHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.setHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(param(\"custom-parameter-name\", \"custom-parameter-value\"));\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter((authorizationGrantRequest) -> parameters);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, \"one two\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.addParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \")),\n\t\t\t\tparam(\"custom-parameter-name\", \"custom-parameter-value\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.tokenResponseClient.setParametersCustomizer(parametersCustomizer);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenRestClientSetThenCalled() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tRestClient restClient = RestClient.builder().messageConverters((messageConverters) -> {\n\t\t\tmessageConverters.add(0, new FormHttpMessageConverter());\n\t\t\tmessageConverters.add(1, new OAuth2AccessTokenResponseHttpMessageConverter());\n\t\t}).build();\n\t\tRestClient customClient = spy(restClient);\n\t\tthis.tokenResponseClient.setRestClient(customClient);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2ClientCredentialsGrantRequest grantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(customClient).post();\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/RestClientJwtBearerTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link RestClientJwtBearerTokenResponseClient}.\n *\n * @author Steve Riesenberg\n */\npublic class RestClientJwtBearerTokenResponseClientTests {\n\n\tprivate RestClientJwtBearerTokenResponseClient tokenResponseClient;\n\n\tprivate MockWebServer server;\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\tprivate Jwt jwtAssertion;\n\n\t@BeforeEach\n\tpublic void setUp() throws IOException {\n\t\tthis.tokenResponseClient = new RestClientJwtBearerTokenResponseClient();\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.scope(\"read\", \"write\");\n\t\tthis.jwtAssertion = TestJwts.jwt().build();\n\t}\n\n\t@AfterEach\n\tpublic void cleanUp() throws IOException {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setRestClientWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setRestClient(null))\n\t\t\t\t.withMessage(\"restClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersCustomizer(null))\n\t\t\t.withMessage(\"parametersCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenGrantRequestIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(null))\n\t\t\t\t.withMessage(\"grantRequest cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.JWT_BEARER.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.ASSERTION, this.jwtAssertion.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretBasicThenAuthorizationHeaderIsSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_SECRET, \"secret\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\")\n\t\t\t\t.havingRootCause().withMessage(\"tokenType cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessTokenHasNoScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenInvalidResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(301));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessage(\"[invalid_token_response] Empty OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"server-error-response.json\").setResponseCode(500));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-grant-response.json\").setResponseCode(400));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT))\n\t\t\t\t.withMessage(\"[invalid_grant] Invalid grant\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConverter<JwtBearerGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.addHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConverter<JwtBearerGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.setHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.ASSERTION, \"custom-assertion\");\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\t// The client_id parameter is omitted for testing purposes\n\t\tthis.tokenResponseClient.setParametersConverter((authorizationGrantRequest) -> parameters);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, \"one two\"),\n\t\t\t\tparam(OAuth2ParameterNames.ASSERTION, \"custom-assertion\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConverter<JwtBearerGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(param(\"custom-parameter-name\", \"custom-parameter-value\"));\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConverter<JwtBearerGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.addParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.JWT_BEARER.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.ASSERTION, this.jwtAssertion.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \")),\n\t\t\t\tparam(\"custom-parameter-name\", \"custom-parameter-value\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.tokenResponseClient.setParametersCustomizer(parametersCustomizer);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenRestClientSetThenCalled() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tRestClient customClient = mock();\n\t\tgiven(customClient.post()).willReturn(RestClient.builder().build().post());\n\t\tthis.tokenResponseClient.setRestClient(customClient);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest grantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(customClient).post();\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/RestClientRefreshTokenTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.FormHttpMessageConverter;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link RestClientRefreshTokenTokenResponseClient}.\n *\n * @author Steve Riesenberg\n */\npublic class RestClientRefreshTokenTokenResponseClientTests {\n\n\tprivate RestClientRefreshTokenTokenResponseClient tokenResponseClient;\n\n\tprivate MockWebServer server;\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate OAuth2RefreshToken refreshToken;\n\n\t@BeforeEach\n\tpublic void setUp() throws IOException {\n\t\tthis.tokenResponseClient = new RestClientRefreshTokenTokenResponseClient();\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.scope(\"read\", \"write\");\n\t\tthis.accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t}\n\n\t@AfterEach\n\tpublic void cleanUp() throws IOException {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setRestClientWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setRestClient(null))\n\t\t\t\t.withMessage(\"restClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersCustomizer(null))\n\t\t\t.withMessage(\"parametersCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenGrantRequestIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(null))\n\t\t\t\t.withMessage(\"grantRequest cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken, scopes);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REFRESH_TOKEN, this.refreshToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(this.refreshToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretBasicThenAuthorizationHeaderIsSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_SECRET, \"secret\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\")\n\t\t\t\t.havingRootCause().withMessage(\"tokenType cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessTokenHasRequestedScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken, scopes);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenRequestDoesNotIncludeScopeThenAccessTokenHasResponseScope() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REFRESH_TOKEN, this.refreshToken.getTokenValue())\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(formParameters).doesNotContain(OAuth2ParameterNames.SCOPE);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenInvalidResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(301));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessage(\"[invalid_token_response] Empty OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"server-error-response.json\").setResponseCode(500));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-grant-response.json\").setResponseCode(400));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT))\n\t\t\t\t.withMessage(\"[invalid_grant] Invalid grant\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tConverter<OAuth2RefreshTokenGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.addHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tConverter<OAuth2RefreshTokenGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.setHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tConverter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(param(\"custom-parameter-name\", \"custom-parameter-value\"));\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.REFRESH_TOKEN, \"custom-token\");\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\tthis.tokenResponseClient.setParametersConverter((authorizationGrantRequest) -> parameters);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.REFRESH_TOKEN, \"custom-token\"),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, \"one two\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken, scopes);\n\t\tConverter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.addParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REFRESH_TOKEN, this.refreshToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \")),\n\t\t\t\tparam(\"custom-parameter-name\", \"custom-parameter-value\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.tokenResponseClient.setParametersCustomizer(parametersCustomizer);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenRestClientSetThenCalled() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tRestClient restClient = RestClient.builder().messageConverters((messageConverters) -> {\n\t\t\tmessageConverters.add(0, new FormHttpMessageConverter());\n\t\t\tmessageConverters.add(1, new OAuth2AccessTokenResponseHttpMessageConverter());\n\t\t}).build();\n\t\tRestClient customClient = spy(restClient);\n\t\tthis.tokenResponseClient.setRestClient(customClient);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tOAuth2RefreshTokenGrantRequest grantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(customClient).post();\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/RestClientTokenExchangeTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link RestClientTokenExchangeTokenResponseClient}.\n *\n * @author Steve Riesenberg\n */\npublic class RestClientTokenExchangeTokenResponseClientTests {\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate RestClientTokenExchangeTokenResponseClient tokenResponseClient;\n\n\tprivate MockWebServer server;\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\tprivate OAuth2Token subjectToken;\n\n\tprivate OAuth2Token actorToken;\n\n\t@BeforeEach\n\tpublic void setUp() throws IOException {\n\t\tthis.tokenResponseClient = new RestClientTokenExchangeTokenResponseClient();\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.scope(\"read\", \"write\");\n\t\tthis.subjectToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.actorToken = null;\n\t}\n\n\t@AfterEach\n\tpublic void cleanUp() throws IOException {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setRestClientWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setRestClient(null))\n\t\t\t\t.withMessage(\"restClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersCustomizer(null))\n\t\t\t.withMessage(\"parametersCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenGrantRequestIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(null))\n\t\t\t\t.withMessage(\"grantRequest cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSubjectTokenIsJwtThenSubjectTokenTypeIsJwt() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tthis.subjectToken = TestJwts.jwt().build();\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenActorTokenIsNotNullThenActorParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tthis.actorToken = TestOAuth2AccessTokens.noScopes();\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.ACTOR_TOKEN, this.actorToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenActorTokenIsJwtThenActorTokenTypeIsJwt() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tthis.actorToken = TestJwts.jwt().build();\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.ACTOR_TOKEN, this.actorToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretBasicThenAuthorizationHeaderIsSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_SECRET, \"secret\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\")\n\t\t\t\t.havingRootCause().withMessage(\"tokenType cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessTokenHasNoScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenInvalidResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(301));\n\t\tTokenExchangeGrantRequest request = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessage(\"[invalid_token_response] Empty OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"server-error-response.json\").setResponseCode(500));\n\t\tTokenExchangeGrantRequest request = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-grant-response.json\").setResponseCode(400));\n\t\tTokenExchangeGrantRequest request = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request))\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT))\n\t\t\t\t.withMessage(\"[invalid_grant] Invalid grant\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest))\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tConverter<TokenExchangeGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.addHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tConverter<TokenExchangeGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.setHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tConverter<TokenExchangeGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(param(\"custom-parameter-name\", \"custom-parameter-value\"));\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN, \"custom-token\");\n\t\tthis.tokenResponseClient.setParametersConverter((authorizationGrantRequest) -> parameters);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, \"custom-token\"),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, \"one two\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tSet<String> scopes = clientRegistration.getScopes();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tConverter<TokenExchangeGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.addParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(scopes, \" \")),\n\t\t\t\tparam(\"custom-parameter-name\", \"custom-parameter-value\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.tokenResponseClient.setParametersCustomizer(parametersCustomizer);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenRestClientSetThenCalled() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tRestClient customClient = mock();\n\t\tgiven(customClient.post()).willReturn(RestClient.builder().build().post());\n\t\tthis.tokenResponseClient.setRestClient(customClient);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest);\n\t\tverify(customClient).post();\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/TokenExchangeGrantRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link TokenExchangeGrantRequest}.\n *\n * @author Steve Riesenberg\n */\npublic class TokenExchangeGrantRequestTests {\n\n\tprivate final ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t.build();\n\n\tprivate final OAuth2Token subjectToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\n\tprivate final OAuth2Token actorToken = TestOAuth2AccessTokens.noScopes();\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new TokenExchangeGrantRequest(null, this.subjectToken, this.actorToken))\n\t\t\t\t.withMessage(\"clientRegistration cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenSubjectTokenIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new TokenExchangeGrantRequest(this.clientRegistration, null, this.actorToken))\n\t\t\t\t.withMessage(\"subjectToken cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenActorTokenIsNullThenCreated() {\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration,\n\t\t\t\tthis.subjectToken, null);\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(grantRequest.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(grantRequest.getSubjectToken()).isSameAs(this.subjectToken);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationInvalidGrantTypeThenThrowIllegalArgumentException() {\n\t\tClientRegistration registration = TestClientRegistrations.clientCredentials().build();\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new TokenExchangeGrantRequest(registration, this.subjectToken, this.actorToken))\n\t\t\t\t.withMessage(\"clientRegistration.authorizationGrantType must be AuthorizationGrantType.TOKEN_EXCHANGE\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenValidParametersProvidedThenCreated() {\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration,\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tassertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE);\n\t\tassertThat(grantRequest.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(grantRequest.getSubjectToken()).isSameAs(this.subjectToken);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveAuthorizationCodeTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport javax.crypto.spec.SecretKeySpec;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.reactive.function.BodyExtractor;\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class WebClientReactiveAuthorizationCodeTokenResponseClientTests {\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\tprivate WebClientReactiveAuthorizationCodeTokenResponseClient tokenResponseClient = new WebClientReactiveAuthorizationCodeTokenResponseClient();\n\n\tprivate MockWebServer server;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().tokenUri(tokenUri);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-openid-profile-2.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient\n\t\t\t.getTokenResponse(authorizationCodeGrantRequest())\n\t\t\t.block();\n\t\tString body = this.server.takeRequest().getBody().readUtf8();\n\t\tassertThat(body).isEqualTo(\n\t\t\t\t\"grant_type=authorization_code&code=code&redirect_uri=%7BbaseUrl%7D%2F%7Baction%7D%2Foauth2%2Fcode%2F%7BregistrationId%7D\");\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"openid\", \"profile\");\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(\"refresh-token-1234\");\n\t\tassertThat(accessTokenResponse.getAdditionalParameters()).hasSize(2);\n\t\tassertThat(accessTokenResponse.getAdditionalParameters()).containsEntry(\"custom_parameter_1\", \"custom-value-1\");\n\t\tassertThat(accessTokenResponse.getAdditionalParameters()).containsEntry(\"custom_parameter_2\", \"custom-value-2\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretJwtThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// Configure Jwt client authentication converter\n\t\tSecretKeySpec secretKey = new SecretKeySpec(\n\t\t\t\tclientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8), \"HmacSHA256\");\n\t\tJWK jwk = TestJwks.jwk(secretKey).build();\n\t\tFunction<ClientRegistration, JWK> jwkResolver = (registration) -> jwk;\n\t\tconfigureJwtClientAuthenticationConverter(jwkResolver);\n\n\t\tthis.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest(clientRegistration)).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=authorization_code\",\n\t\t\t\t\"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer\",\n\t\t\t\t\"client_assertion=\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationPrivateKeyJwtThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// Configure Jwt client authentication converter\n\t\tJWK jwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tFunction<ClientRegistration, JWK> jwkResolver = (registration) -> jwk;\n\t\tconfigureJwtClientAuthenticationConverter(jwkResolver);\n\n\t\tthis.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest(clientRegistration)).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=authorization_code\",\n\t\t\t\t\"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer\",\n\t\t\t\t\"client_assertion=\");\n\t}\n\n\tprivate void configureJwtClientAuthenticationConverter(Function<ClientRegistration, JWK> jwkResolver) {\n\t\tNimbusJwtClientAuthenticationParametersConverter<OAuth2AuthorizationCodeGrantRequest> jwtClientAuthenticationConverter = new NimbusJwtClientAuthenticationParametersConverter<>(\n\t\t\t\tjwkResolver);\n\t\tthis.tokenResponseClient.addParametersConverter(jwtClientAuthenticationConverter);\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"unauthorized-client-response.json\").setResponseCode(500));\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest()).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"unauthorized_client\"))\n\t\t\t.withMessageContaining(\"unauthorized_client\");\n\t}\n\n\t// gh-5594\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"server-error-response.json\").setResponseCode(500));\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest()).block())\n\t\t\t.withMessageContaining(\"server_error\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest()).block())\n\t\t\t.withMessageContaining(\"invalid_token_response\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseIncludesScopeThenReturnAccessTokenResponseUsingResponseScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-openid-profile.json\"));\n\t\tthis.clientRegistration.scope(\"openid\", \"profile\", \"email\", \"address\");\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient\n\t\t\t.getTokenResponse(authorizationCodeGrantRequest())\n\t\t\t.block();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"openid\", \"profile\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenReturnAccessTokenResponseWithNoScopes() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.clientRegistration.scope(\"openid\", \"profile\", \"email\", \"address\");\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient\n\t\t\t.getTokenResponse(authorizationCodeGrantRequest())\n\t\t\t.block();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\tprivate OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest() {\n\t\treturn authorizationCodeGrantRequest(this.clientRegistration.build());\n\t}\n\n\tprivate OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest(ClientRegistration registration) {\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.clientId(registration.getClientId())\n\t\t\t.state(\"state\")\n\t\t\t.authorizationUri(registration.getProviderDetails().getAuthorizationUri())\n\t\t\t.redirectUri(registration.getRedirectUri())\n\t\t\t.scopes(registration.getScopes())\n\t\t\t.build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponse.success(\"code\")\n\t\t\t.state(\"state\")\n\t\t\t.redirectUri(registration.getRedirectUri())\n\t\t\t.build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\treturn new OAuth2AuthorizationCodeGrantRequest(registration, authorizationExchange);\n\t}\n\n\t@Test\n\tpublic void setWebClientNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.setWebClient(null));\n\t}\n\n\t@Test\n\tpublic void setCustomWebClientThenCustomWebClientIsUsed() {\n\t\tWebClient customClient = mock();\n\t\tgiven(customClient.post()).willReturn(WebClient.builder().build().post());\n\t\tthis.tokenResponseClient.setWebClient(customClient);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.clientRegistration.scope(\"openid\", \"profile\", \"email\", \"address\");\n\t\tOAuth2AccessTokenResponse response = this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest())\n\t\t\t.block();\n\t\tverify(customClient, atLeastOnce()).post();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenOAuth2AuthorizationRequestContainsPkceParametersThenTokenRequestBodyShouldContainCodeVerifier()\n\t\t\tthrows Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(pkceAuthorizationCodeGrantRequest()).block();\n\t\tString body = this.server.takeRequest().getBody().readUtf8();\n\t\tassertThat(body).isEqualTo(\n\t\t\t\t\"grant_type=authorization_code&client_id=client-id&code=code&redirect_uri=%7BbaseUrl%7D%2F%7Baction%7D%2Foauth2%2Fcode%2F%7BregistrationId%7D&code_verifier=code-verifier-1234\");\n\t}\n\n\tprivate OAuth2AuthorizationCodeGrantRequest pkceAuthorizationCodeGrantRequest() {\n\t\tClientRegistration registration = this.clientRegistration.clientAuthenticationMethod(null)\n\t\t\t.clientSecret(null)\n\t\t\t.build();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(PkceParameterNames.CODE_VERIFIER, \"code-verifier-1234\");\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE, \"code-challenge-1234\");\n\t\tadditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.clientId(registration.getClientId())\n\t\t\t\t.state(\"state\")\n\t\t\t\t.authorizationUri(registration.getProviderDetails().getAuthorizationUri())\n\t\t\t\t.redirectUri(registration.getRedirectUri())\n\t\t\t\t.scopes(registration.getScopes())\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponse\n\t\t\t\t.success(\"code\")\n\t\t\t\t.state(\"state\")\n\t\t\t\t.redirectUri(registration.getRedirectUri())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\treturn new OAuth2AuthorizationCodeGrantRequest(registration, authorizationExchange);\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.setHeadersConverter(null))\n\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.addHeadersConverter(null))\n\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void convertWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tOAuth2AuthorizationCodeGrantRequest request = authorizationCodeGrantRequest();\n\t\tConverter<OAuth2AuthorizationCodeGrantRequest, HttpHeaders> addedHeadersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(addedHeadersConverter.convert(request)).willReturn(headers);\n\t\tthis.tokenResponseClient.addHeadersConverter(addedHeadersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(addedHeadersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t\tassertThat(actualRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void convertWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tOAuth2AuthorizationCodeGrantRequest request = authorizationCodeGrantRequest();\n\t\tClientRegistration clientRegistration = request.getClientRegistration();\n\t\tConverter<OAuth2AuthorizationCodeGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());\n\t\tgiven(headersConverter.convert(request)).willReturn(headers);\n\t\tthis.tokenResponseClient.setHeadersConverter(headersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(headersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.setParametersConverter(null))\n\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.addParametersConverter(null))\n\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tOAuth2AuthorizationCodeGrantRequest request = authorizationCodeGrantRequest();\n\t\tConverter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> addedParametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(addedParametersConverter.convert(request)).willReturn(parameters);\n\t\tthis.tokenResponseClient.addParametersConverter(addedParametersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(addedParametersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=authorization_code\",\n\t\t\t\t\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tOAuth2AuthorizationCodeGrantRequest request = authorizationCodeGrantRequest();\n\t\tConverter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(request)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter(parametersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(parametersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tOAuth2AuthorizationCodeGrantRequest request = authorizationCodeGrantRequest();\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.CODE, \"custom-code\");\n\t\tparameters.set(OAuth2ParameterNames.REDIRECT_URI, \"custom-uri\");\n\t\tthis.tokenResponseClient.setParametersConverter((grantRequest) -> parameters);\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tString formParameters = this.server.takeRequest().getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-id\"),\n\t\t\t\tparam(OAuth2ParameterNames.CODE, \"custom-code\"),\n\t\t\t\tparam(OAuth2ParameterNames.REDIRECT_URI, \"custom-uri\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tOAuth2AuthorizationCodeGrantRequest request = authorizationCodeGrantRequest();\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.tokenResponseClient.setParametersCustomizer(parametersCustomizer);\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t// gh-10260\n\t@Test\n\tpublic void getTokenResponseWhenSuccessCustomResponseThenReturnAccessTokenResponse() {\n\n\t\tWebClientReactiveAuthorizationCodeTokenResponseClient customClient = new WebClientReactiveAuthorizationCodeTokenResponseClient();\n\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = mock();\n\t\tOAuth2AccessTokenResponse response = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(extractor.extract(any(), any())).willReturn(Mono.just(response));\n\n\t\tcustomClient.setBodyExtractor(extractor);\n\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = customClient.getTokenResponse(authorizationCodeGrantRequest())\n\t\t\t.block();\n\t\tassertThat(accessTokenResponse.getAccessToken()).isNotNull();\n\n\t}\n\n\t// gh-13144\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tOAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest = authorizationCodeGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest).block());\n\t}\n\n\t// gh-13144\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tOAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest = authorizationCodeGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest).block());\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport javax.crypto.spec.SecretKeySpec;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.reactive.function.BodyExtractor;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.validateMockitoUsage;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\npublic class WebClientReactiveClientCredentialsTokenResponseClientTests {\n\n\tprivate MockWebServer server;\n\n\tprivate WebClientReactiveClientCredentialsTokenResponseClient client = new WebClientReactiveClientCredentialsTokenResponseClient();\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.tokenUri(this.server.url(\"/oauth2/token\").uri().toASCIIString());\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tvalidateMockitoUsage();\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeaderThenSuccess() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-create.json\"));\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.build());\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tString body = actualRequest.getBody().readUtf8();\n\t\tassertThat(response.getAccessToken()).isNotNull();\n\t\tassertThat(response.getAccessToken().getScopes()).containsExactly(\"create\");\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t\tassertThat(body).isEqualTo(\"grant_type=client_credentials&scope=read%3Auser\");\n\t}\n\n\t// gh-9610\n\t@Test\n\tpublic void getTokenResponseWhenSpecialCharactersThenSuccessWithEncodedClientCredentials() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-create.json\"));\n\t\tString clientCredentialWithAnsiKeyboardSpecialCharacters = \"~!@#$%^&*()_+{}|:\\\"<>?`-=[]\\\\;',./ \";\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.clientId(clientCredentialWithAnsiKeyboardSpecialCharacters)\n\t\t\t\t\t.clientSecret(clientCredentialWithAnsiKeyboardSpecialCharacters)\n\t\t\t\t\t.build());\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tString body = actualRequest.getBody().readUtf8();\n\t\tassertThat(response.getAccessToken()).isNotNull();\n\t\tassertThat(response.getAccessToken().getScopes()).containsExactly(\"create\");\n\t\tString urlEncodedClientCredentialecret = URLEncoder.encode(clientCredentialWithAnsiKeyboardSpecialCharacters,\n\t\t\t\tStandardCharsets.UTF_8.toString());\n\t\tString clientCredentials = Base64.getEncoder()\n\t\t\t.encodeToString((urlEncodedClientCredentialecret + \":\" + urlEncodedClientCredentialecret)\n\t\t\t\t.getBytes(StandardCharsets.UTF_8));\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Basic \" + clientCredentials);\n\t\tassertThat(body).isEqualTo(\"grant_type=client_credentials&scope=read%3Auser\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenPostThenSuccess() throws Exception {\n\t\tClientRegistration registration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.build();\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-create.json\"));\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(registration);\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tString body = actualRequest.getBody().readUtf8();\n\t\tassertThat(response.getAccessToken()).isNotNull();\n\t\tassertThat(response.getAccessToken().getScopes()).containsExactly(\"create\");\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(body).isEqualTo(\n\t\t\t\t\"grant_type=client_credentials&client_id=client-id&client_secret=client-secret&scope=read%3Auser\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretJwtThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// Configure Jwt client authentication converter\n\t\tSecretKeySpec secretKey = new SecretKeySpec(\n\t\t\t\tclientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8), \"HmacSHA256\");\n\t\tJWK jwk = TestJwks.jwk(secretKey).build();\n\t\tFunction<ClientRegistration, JWK> jwkResolver = (registration) -> jwk;\n\t\tconfigureJwtClientAuthenticationConverter(jwkResolver);\n\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tthis.client.getTokenResponse(request).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=client_credentials\",\n\t\t\t\t\"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer\",\n\t\t\t\t\"client_assertion=\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationPrivateKeyJwtThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// Configure Jwt client authentication converter\n\t\tJWK jwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tFunction<ClientRegistration, JWK> jwkResolver = (registration) -> jwk;\n\t\tconfigureJwtClientAuthenticationConverter(jwkResolver);\n\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(clientRegistration);\n\t\tthis.client.getTokenResponse(request).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=client_credentials\",\n\t\t\t\t\"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer\",\n\t\t\t\t\"client_assertion=\");\n\t}\n\n\tprivate void configureJwtClientAuthenticationConverter(Function<ClientRegistration, JWK> jwkResolver) {\n\t\tNimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> jwtClientAuthenticationConverter = new NimbusJwtClientAuthenticationParametersConverter<>(\n\t\t\t\tjwkResolver);\n\t\tthis.client.addParametersConverter(jwtClientAuthenticationConverter);\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenNoScopeThenReturnAccessTokenResponseWithNoScopes() {\n\t\tClientRegistration registration = this.clientRegistration.build();\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(registration);\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tassertThat(response.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void setWebClientNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.setWebClient(null));\n\t}\n\n\t@Test\n\tpublic void setWebClientCustomThenCustomClientIsUsed() {\n\t\tWebClient customClient = mock();\n\t\tgiven(customClient.post()).willReturn(WebClient.builder().build().post());\n\t\tthis.client.setWebClient(customClient);\n\t\tClientRegistration registration = this.clientRegistration.build();\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(registration);\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tverify(customClient, atLeastOnce()).post();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenInvalidResponse() throws WebClientResponseException {\n\t\tClientRegistration registration = this.clientRegistration.build();\n\t\tenqueueUnexpectedResponse();\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(registration);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(request).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t.withMessageContaining(\"[invalid_token_response]\")\n\t\t\t.withMessageContaining(\"Empty OAuth 2.0 Access Token Response\");\n\t}\n\n\tprivate void enqueueUnexpectedResponse() {\n\t\t// @formatter:off\n\t\tMockResponse response = new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setResponseCode(301);\n\t\t// @formatter:on\n\t\tthis.server.enqueue(response);\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.setHeadersConverter(null))\n\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.addHeadersConverter(null))\n\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void convertWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.build());\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, HttpHeaders> addedHeadersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(addedHeadersConverter.convert(request)).willReturn(headers);\n\t\tthis.client.addHeadersConverter(addedHeadersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(addedHeadersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t\tassertThat(actualRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void convertWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.build());\n\t\tClientRegistration clientRegistration = request.getClientRegistration();\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());\n\t\tgiven(headersConverter.convert(request)).willReturn(headers);\n\t\tthis.client.setHeadersConverter(headersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(headersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.setParametersConverter(null))\n\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.addParametersConverter(null))\n\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.build());\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> addedParametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(addedParametersConverter.convert(request)).willReturn(parameters);\n\t\tthis.client.addParametersConverter(addedParametersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(addedParametersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=client_credentials\",\n\t\t\t\t\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.build());\n\t\tConverter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(request)).willReturn(parameters);\n\t\tthis.client.setParametersConverter(parametersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(parametersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.build());\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\tthis.client.setParametersConverter((grantRequest) -> parameters);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tString formParameters = this.server.takeRequest().getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-id\"),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, \"one two\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.build());\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.client.setParametersCustomizer(parametersCustomizer);\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t// gh-10260\n\t@Test\n\tpublic void getTokenResponseWhenSuccessCustomResponseThenReturnAccessTokenResponse() {\n\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\n\t\tWebClientReactiveClientCredentialsTokenResponseClient customClient = new WebClientReactiveClientCredentialsTokenResponseClient();\n\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = mock();\n\t\tOAuth2AccessTokenResponse response = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(extractor.extract(any(), any())).willReturn(Mono.just(response));\n\n\t\tcustomClient.setBodyExtractor(extractor);\n\n\t\tOAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tthis.clientRegistration.build());\n\n\t\tOAuth2AccessTokenResponse accessTokenResponse = customClient.getTokenResponse(request).block();\n\t\tassertThat(accessTokenResponse.getAccessToken()).isNotNull();\n\n\t}\n\n\t// gh-13144\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(clientCredentialsGrantRequest).block());\n\t}\n\n\t// gh-13144\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tOAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(\n\t\t\t\tclientRegistration);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(clientCredentialsGrantRequest).block());\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveJwtBearerTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.function.Consumer;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.reactive.function.BodyExtractor;\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link WebClientReactiveJwtBearerTokenResponseClient}.\n *\n * @author Steve Riesenberg\n */\npublic class WebClientReactiveJwtBearerTokenResponseClientTests {\n\n\t// @formatter:off\n\tprivate static final String DEFAULT_ACCESS_TOKEN_RESPONSE = \"{\\n\"\n\t\t\t+ \"  \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"  \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"  \\\"expires_in\\\": 3600\\n\"\n\t\t\t+ \"}\\n\";\n\t// @formatter:on\n\n\tprivate WebClientReactiveJwtBearerTokenResponseClient client;\n\n\tprivate MockWebServer server;\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\tprivate Jwt jwtAssertion;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.client = new WebClientReactiveJwtBearerTokenResponseClient();\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.scope(\"read\", \"write\");\n\t\tthis.jwtAssertion = TestJwts.jwt().build();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setWebClientWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.setWebClient(null))\n\t\t\t.withMessage(\"webClient cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.setHeadersConverter(null))\n\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.addHeadersConverter(null))\n\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setBodyExtractorWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.setBodyExtractor(null))\n\t\t\t.withMessage(\"bodyExtractor cannot be null\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.getTokenResponse(null))\n\t\t\t.withMessage(\"grantRequest cannot be null\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenInvalidResponseThenThrowOAuth2AuthorizationException() {\n\t\tClientRegistration registration = this.clientRegistration.build();\n\t\tenqueueUnexpectedResponse();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(registration, this.jwtAssertion);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(request).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t.withMessage(\"[invalid_token_response] Empty OAuth 2.0 Access Token Response\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tClientRegistration registration = this.clientRegistration.build();\n\t\tenqueueServerErrorResponse();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(registration, this.jwtAssertion);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(request).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR))\n\t\t\t.withMessageContaining(\"[server_error]\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tClientRegistration registration = this.clientRegistration.build();\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-grant-response.json\").setResponseCode(400));\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(registration, this.jwtAssertion);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(request).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT))\n\t\t\t.withMessageContaining(\"[invalid_grant]\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenResponseIsNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tClientRegistration registration = this.clientRegistration.build();\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(registration, this.jwtAssertion);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(request).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred parsing the Access Token response\")\n\t\t\t.withMessageContaining(\"Unsupported token_type: not-bearer\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenWebClientSetThenCalled() {\n\t\tWebClient customClient = mock();\n\t\tgiven(customClient.post()).willReturn(WebClient.builder().build().post());\n\t\tthis.client.setWebClient(customClient);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration registration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(registration, this.jwtAssertion);\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(customClient).post();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConverter<JwtBearerGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());\n\t\tgiven(headersConverter.convert(request)).willReturn(headers);\n\t\tthis.client.setHeadersConverter(headersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(headersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConverter<JwtBearerGrantRequest, HttpHeaders> addedHeadersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(addedHeadersConverter.convert(request)).willReturn(headers);\n\t\tthis.client.addHeadersConverter(addedHeadersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(addedHeadersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t\tassertThat(actualRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.setParametersConverter(null))\n\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.client.addParametersConverter(null))\n\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConverter<JwtBearerGrantRequest, MultiValueMap<String, String>> addedParametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(addedParametersConverter.convert(request)).willReturn(parameters);\n\t\tthis.client.addParametersConverter(addedParametersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(addedParametersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\n\t\t\t\t\"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer\",\n\t\t\t\t\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConverter<JwtBearerGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(request)).willReturn(parameters);\n\t\tthis.client.setParametersConverter(parametersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(parametersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.ASSERTION, \"custom-assertion\");\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\tthis.client.setParametersConverter((grantRequest) -> parameters);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tString formParameters = this.server.takeRequest().getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-id\"),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, \"one two\"),\n\t\t\t\tparam(OAuth2ParameterNames.ASSERTION, \"custom-assertion\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.client.setParametersCustomizer(parametersCustomizer);\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenBodyExtractorSetThenCalled() {\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> bodyExtractor = mock();\n\t\tOAuth2AccessTokenResponse response = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(bodyExtractor.extract(any(), any())).willReturn(Mono.just(response));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tthis.client.setBodyExtractor(bodyExtractor);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.client.getTokenResponse(request).block();\n\t\tverify(bodyExtractor).extract(any(), any());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenClientSecretBasicThenSuccess() throws Exception {\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tassertThat(response).isNotNull();\n\t\tassertThat(response.getAccessToken().getScopes()).containsExactly(\"read\", \"write\");\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t\tassertThat(actualRequest.getBody().readUtf8()).isEqualTo(\n\t\t\t\t\"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&scope=read+write&assertion=token\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenClientSecretPostThenSuccess() throws Exception {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tassertThat(response).isNotNull();\n\t\tassertThat(response.getAccessToken().getScopes()).containsExactly(\"read\", \"write\");\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(actualRequest.getBody().readUtf8()).isEqualTo(\n\t\t\t\t\"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&client_id=client-id&client_secret=client-secret&scope=read+write&assertion=token\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenResponseIncludesScopeThenAccessTokenHasResponseScope() throws Exception {\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tassertThat(response).isNotNull();\n\t\tassertThat(response.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenResponseDoesNotIncludeScopeThenReturnAccessTokenResponseWithNoScopes()\n\t\t\tthrows Exception {\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tJwtBearerGrantRequest request = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tOAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block();\n\t\tassertThat(response).isNotNull();\n\t\tassertThat(response.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t// gh-13144\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tJwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(jwtBearerGrantRequest).block());\n\t}\n\n\t// gh-13144\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tJwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest(clientRegistration, this.jwtAssertion);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.client.getTokenResponse(jwtBearerGrantRequest).block());\n\t}\n\n\tprivate void enqueueUnexpectedResponse() {\n\t\t// @formatter:off\n\t\tMockResponse response = new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setResponseCode(301);\n\t\t// @formatter:on\n\t\tthis.server.enqueue(response);\n\t}\n\n\tprivate void enqueueServerErrorResponse() {\n\t\t// @formatter:off\n\t\tMockResponse response = new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setResponseCode(500)\n\t\t\t\t.setBody(\"{}\");\n\t\t// @formatter:on\n\t\tthis.server.enqueue(response);\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveRefreshTokenTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport javax.crypto.spec.SecretKeySpec;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.reactive.function.BodyExtractor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link WebClientReactiveRefreshTokenTokenResponseClient}.\n *\n * @author Joe Grandja\n */\npublic class WebClientReactiveRefreshTokenTokenResponseClientTests {\n\n\tprivate WebClientReactiveRefreshTokenTokenResponseClient tokenResponseClient = new WebClientReactiveRefreshTokenTokenResponseClient();\n\n\tprivate ClientRegistration.Builder clientRegistrationBuilder;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate OAuth2RefreshToken refreshToken;\n\n\tprivate MockWebServer server;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistrationBuilder = TestClientRegistrations.clientRegistration().tokenUri(tokenUri);\n\t\tthis.accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setWebClientWhenClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.setWebClient(null));\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.getTokenResponse(null).block());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient\n\t\t\t.getTokenResponse(refreshTokenGrantRequest)\n\t\t\t.block();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(\"grant_type=refresh_token\");\n\t\tassertThat(formParameters).contains(\"refresh_token=refresh-token\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes())\n\t\t\t.containsExactly(this.accessToken.getScopes().toArray(new String[0]));\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(this.refreshToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.build();\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tthis.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest).block();\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(\"client_id=client-id\");\n\t\tassertThat(formParameters).contains(\"client_secret=client-secret\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretJwtThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t\t.clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// Configure Jwt client authentication converter\n\t\tSecretKeySpec secretKey = new SecretKeySpec(\n\t\t\t\tclientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8), \"HmacSHA256\");\n\t\tJWK jwk = TestJwks.jwk(secretKey).build();\n\t\tFunction<ClientRegistration, JWK> jwkResolver = (registration) -> jwk;\n\t\tconfigureJwtClientAuthenticationConverter(jwkResolver);\n\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tthis.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=refresh_token\",\n\t\t\t\t\"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer\",\n\t\t\t\t\"client_assertion=\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationPrivateKeyJwtThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// Configure Jwt client authentication converter\n\t\tJWK jwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tFunction<ClientRegistration, JWK> jwkResolver = (registration) -> jwk;\n\t\tconfigureJwtClientAuthenticationConverter(jwkResolver);\n\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tthis.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest).block();\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=refresh_token\",\n\t\t\t\t\"client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer\",\n\t\t\t\t\"client_assertion=\");\n\t}\n\n\tprivate void configureJwtClientAuthenticationConverter(Function<ClientRegistration, JWK> jwkResolver) {\n\t\tNimbusJwtClientAuthenticationParametersConverter<OAuth2RefreshTokenGrantRequest> jwtClientAuthenticationConverter = new NimbusJwtClientAuthenticationParametersConverter<>(\n\t\t\t\tjwkResolver);\n\t\tthis.tokenResponseClient.addParametersConverter(jwtClientAuthenticationConverter);\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest).block())\n\t\t\t.withMessageContaining(\"[invalid_token_response]\")\n\t\t\t.withMessageContaining(\"An error occurred parsing the Access Token response\")\n\t\t\t.withCauseInstanceOf(Throwable.class);\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken,\n\t\t\t\tCollections.singleton(\"read\"));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient\n\t\t\t.getTokenResponse(refreshTokenGrantRequest)\n\t\t\t.block();\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(\"scope=read\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"unauthorized-client-response.json\").setResponseCode(400));\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"unauthorized_client\"))\n\t\t\t.withMessageContaining(\"[unauthorized_client]\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(500));\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t.withMessageContaining(\"[invalid_token_response]\")\n\t\t\t.withMessageContaining(\"Empty OAuth 2.0 Access Token Response\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.setHeadersConverter(null))\n\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.addHeadersConverter(null))\n\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tConverter<OAuth2RefreshTokenGrantRequest, HttpHeaders> addedHeadersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(addedHeadersConverter.convert(request)).willReturn(headers);\n\t\tthis.tokenResponseClient.addHeadersConverter(addedHeadersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(addedHeadersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t\tassertThat(actualRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t// gh-10130\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tClientRegistration clientRegistration = request.getClientRegistration();\n\t\tConverter<OAuth2RefreshTokenGrantRequest, HttpHeaders> headersConverter1 = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());\n\t\tgiven(headersConverter1.convert(request)).willReturn(headers);\n\t\tthis.tokenResponseClient.setHeadersConverter(headersConverter1);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(headersConverter1).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=\");\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.setParametersConverter(null))\n\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tokenResponseClient.addParametersConverter(null))\n\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tConverter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> addedParametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(addedParametersConverter.convert(request)).willReturn(parameters);\n\t\tthis.tokenResponseClient.addParametersConverter(addedParametersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(addedParametersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"grant_type=refresh_token\",\n\t\t\t\t\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tConverter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(request)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter(parametersConverter);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(parametersConverter).convert(request);\n\t\tRecordedRequest actualRequest = this.server.takeRequest();\n\t\tassertThat(actualRequest.getBody().readUtf8()).contains(\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistrationBuilder.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.REFRESH_TOKEN, \"custom-token\");\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\tthis.tokenResponseClient.setParametersConverter((grantRequest) -> parameters);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tString formParameters = this.server.takeRequest().getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-id\"),\n\t\t\t\tparam(OAuth2ParameterNames.REFRESH_TOKEN, \"custom-token\"),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, \"one two\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tOAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.tokenResponseClient.setParametersCustomizer(parametersCustomizer);\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t// gh-10260\n\t@Test\n\tpublic void getTokenResponseWhenSuccessCustomResponseThenReturnAccessTokenResponse() {\n\n\t\tWebClientReactiveRefreshTokenTokenResponseClient customClient = new WebClientReactiveRefreshTokenTokenResponseClient();\n\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = mock();\n\t\tOAuth2AccessTokenResponse response = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(extractor.extract(any(), any())).willReturn(Mono.just(response));\n\n\t\tcustomClient.setBodyExtractor(extractor);\n\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(\n\t\t\t\tthis.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken);\n\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tOAuth2AccessTokenResponse accessTokenResponse = customClient.getTokenResponse(refreshTokenGrantRequest).block();\n\t\tassertThat(accessTokenResponse.getAccessToken()).isNotNull();\n\n\t}\n\n\t// gh-13144\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest).block());\n\t}\n\n\t// gh-13144\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tOAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration,\n\t\t\t\tthis.accessToken, this.refreshToken);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest).block());\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveTokenExchangeTokenResponseClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.endpoint;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.function.Consumer;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.security.oauth2.client.MockResponses;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.function.BodyExtractor;\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link WebClientReactiveTokenExchangeTokenResponseClient}.\n *\n * @author Steve Riesenberg\n */\npublic class WebClientReactiveTokenExchangeTokenResponseClientTests {\n\n\tprivate static final String ACCESS_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:access_token\";\n\n\tprivate static final String JWT_TOKEN_TYPE_VALUE = \"urn:ietf:params:oauth:token-type:jwt\";\n\n\tprivate WebClientReactiveTokenExchangeTokenResponseClient tokenResponseClient;\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\tprivate OAuth2Token subjectToken;\n\n\tprivate OAuth2Token actorToken;\n\n\tprivate MockWebServer server;\n\n\t@BeforeEach\n\tpublic void setUp() throws IOException {\n\t\tthis.tokenResponseClient = new WebClientReactiveTokenExchangeTokenResponseClient();\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString tokenUri = this.server.url(\"/oauth2/token\").toString();\n\t\tthis.clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.clientId(\"client-1\")\n\t\t\t.clientSecret(\"secret\")\n\t\t\t.authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)\n\t\t\t.tokenUri(tokenUri)\n\t\t\t.scope(\"read\", \"write\");\n\t\tthis.subjectToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.actorToken = null;\n\t}\n\n\t@AfterEach\n\tpublic void cleanUp() throws IOException {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setWebClientWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setWebClient(null))\n\t\t\t\t.withMessage(\"webClient cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addHeadersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addHeadersConverter(null))\n\t\t\t\t.withMessage(\"headersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void addParametersConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.addParametersConverter(null))\n\t\t\t\t.withMessage(\"parametersConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setBodyExtractorWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.setBodyExtractor(null))\n\t\t\t\t.withMessage(\"bodyExtractor cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenGrantRequestIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(null))\n\t\t\t\t.withMessage(\"grantRequest cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(this.clientRegistration.build().getScopes(), \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSubjectTokenIsJwtThenSubjectTokenTypeIsJwt() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tthis.subjectToken = TestJwts.jwt().build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(this.clientRegistration.build().getScopes(), \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenActorTokenIsNotNullThenActorParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tthis.actorToken = TestOAuth2AccessTokens.noScopes();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.ACTOR_TOKEN, this.actorToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(this.clientRegistration.build().getScopes(), \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenActorTokenIsJwtThenActorTokenTypeIsJwt() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read-write.json\"));\n\t\tInstant expiresAtBefore = Instant.now().plusSeconds(3600);\n\t\tthis.actorToken = TestJwts.jwt().build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tInstant expiresAtAfter = Instant.now().plusSeconds(3600);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString());\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.CONTENT_TYPE))\n\t\t\t.isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.ACTOR_TOKEN, this.actorToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.ACTOR_TOKEN_TYPE, JWT_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(this.clientRegistration.build().getScopes(), \" \"))\n\t\t);\n\t\t// @formatter:on\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactlyInAnyOrder(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretBasicThenAuthorizationHeaderIsSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)\n\t\t\t.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_SECRET, \"secret\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-token-type-response.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest).block())\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessageContaining(\"[invalid_token_response] An error occurred parsing the Access Token response\")\n\t\t\t\t.havingRootCause().withMessage(\"Unsupported token_type: not-bearer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasResponseScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response-read.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessTokenHasNoScope() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tassertThat(accessTokenResponse).isNotNull();\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenInvalidResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(301));\n\t\tTokenExchangeGrantRequest request = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request).block())\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token_response\"))\n\t\t\t\t.withMessage(\"[invalid_token_response] Empty OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"server-error-response.json\").setResponseCode(500));\n\t\tTokenExchangeGrantRequest request = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request).block())\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR))\n\t\t\t\t.withMessage(\"[server_error] A server error occurred\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationException() {\n\t\tthis.server.enqueue(MockResponses.json(\"invalid-grant-response.json\").setResponseCode(400));\n\t\tTokenExchangeGrantRequest request = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(request).block())\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT))\n\t\t\t\t.withMessage(\"[invalid_grant] Invalid grant\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenCustomClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(new ClientAuthenticationMethod(\"basic\"))\n\t\t\t.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest).block())\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenUnsupportedClientAuthenticationMethodThenIllegalArgument() {\n\t\tClientRegistration clientRegistration = this.clientRegistration\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)\n\t\t\t.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> this.tokenResponseClient.getTokenResponse(grantRequest).block())\n\t\t\t\t.withMessageContaining(\"This class supports `client_secret_basic`, `client_secret_post`, and `none` by default.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tConverter<TokenExchangeGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.addHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith(\"Basic \");\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenHeadersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tConverter<TokenExchangeGrantRequest, HttpHeaders> headersConverter = mock();\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.put(\"custom-header-name\", Collections.singletonList(\"custom-header-value\"));\n\t\tgiven(headersConverter.convert(grantRequest)).willReturn(headers);\n\t\tthis.tokenResponseClient.setHeadersConverter(headersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tverify(headersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tassertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull();\n\t\tassertThat(recordedRequest.getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tConverter<TokenExchangeGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.setParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\tassertThat(formParameters).contains(\"custom-parameter-name=custom-parameter-value\");\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterSetThenAbleToOverrideDefaultParameters() throws Exception {\n\t\tthis.clientRegistration.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.set(OAuth2ParameterNames.GRANT_TYPE, \"custom\");\n\t\tparameters.set(OAuth2ParameterNames.SCOPE, \"one two\");\n\t\tparameters.set(OAuth2ParameterNames.SUBJECT_TOKEN, \"custom-token\");\n\t\tthis.tokenResponseClient.setParametersConverter((request) -> parameters);\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tString formParameters = this.server.takeRequest().getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, \"custom\"),\n\t\t\t\tparam(OAuth2ParameterNames.CLIENT_ID, \"client-1\"),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, \"one two\"),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, \"custom-token\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersConverterAddedThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tConverter<TokenExchangeGrantRequest, MultiValueMap<String, String>> parametersConverter = mock();\n\t\tMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();\n\t\tparameters.add(\"custom-parameter-name\", \"custom-parameter-value\");\n\t\tgiven(parametersConverter.convert(grantRequest)).willReturn(parameters);\n\t\tthis.tokenResponseClient.addParametersConverter(parametersConverter);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tverify(parametersConverter).convert(grantRequest);\n\t\tRecordedRequest recordedRequest = this.server.takeRequest();\n\t\tString formParameters = recordedRequest.getBody().readUtf8();\n\t\t// @formatter:off\n\t\tassertThat(formParameters).contains(\n\t\t\t\tparam(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.TOKEN_EXCHANGE.getValue()),\n\t\t\t\tparam(OAuth2ParameterNames.REQUESTED_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN, this.subjectToken.getTokenValue()),\n\t\t\t\tparam(OAuth2ParameterNames.SUBJECT_TOKEN_TYPE, ACCESS_TOKEN_TYPE_VALUE),\n\t\t\t\tparam(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(this.clientRegistration.build().getScopes(), \" \")),\n\t\t\t\tparam(\"custom-parameter-name\", \"custom-parameter-value\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenParametersCustomizerSetThenCalled() throws Exception {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tTokenExchangeGrantRequest request = new TokenExchangeGrantRequest(this.clientRegistration.build(),\n\t\t\t\tthis.subjectToken, this.actorToken);\n\t\tConsumer<MultiValueMap<String, String>> parametersCustomizer = mock();\n\t\tthis.tokenResponseClient.setParametersCustomizer(parametersCustomizer);\n\t\tthis.tokenResponseClient.getTokenResponse(request).block();\n\t\tverify(parametersCustomizer).accept(any());\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenBodyExtractorSetThenCalled() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> bodyExtractor = mock();\n\t\tOAuth2AccessTokenResponse response = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(bodyExtractor.extract(any(ReactiveHttpInputMessage.class), any(BodyExtractor.Context.class)))\n\t\t\t.willReturn(Mono.just(response));\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tthis.tokenResponseClient.setBodyExtractor(bodyExtractor);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tverify(bodyExtractor).extract(any(ReactiveHttpInputMessage.class), any(BodyExtractor.Context.class));\n\t}\n\n\t@Test\n\tpublic void getTokenResponseWhenWebClientSetThenCalled() {\n\t\tthis.server.enqueue(MockResponses.json(\"access-token-response.json\"));\n\t\tWebClient customClient = mock();\n\t\tgiven(customClient.post()).willReturn(WebClient.builder().build().post());\n\t\tthis.tokenResponseClient.setWebClient(customClient);\n\t\tClientRegistration clientRegistration = this.clientRegistration.build();\n\t\tTokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, this.subjectToken,\n\t\t\t\tthis.actorToken);\n\t\tthis.tokenResponseClient.getTokenResponse(grantRequest).block();\n\t\tverify(customClient).post();\n\t}\n\n\tprivate static String param(String parameterName, String parameterValue) {\n\t\treturn \"%s=%s\".formatted(parameterName, URLEncoder.encode(parameterValue, StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.http;\n\nimport java.io.IOException;\nimport java.net.URI;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.client.ClientHttpResponse;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.http.MockHttpInputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2ErrorResponseErrorHandler}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2ErrorResponseErrorHandlerTests {\n\n\tprivate OAuth2ErrorResponseErrorHandler errorHandler = new OAuth2ErrorResponseErrorHandler();\n\n\tprivate URI anyURi = URI.create(\"/any\");\n\n\tprivate HttpMethod anyMethod = HttpMethod.GET;\n\n\t@Test\n\tpublic void handleErrorWhenErrorResponseBodyThenHandled() {\n\t\t// @formatter:off\n\t\tString errorResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"error\\\": \\\"unauthorized_client\\\",\\n\"\n\t\t\t\t+ \"   \\\"error_description\\\": \\\"The client is not authorized\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(errorResponse.getBytes(), HttpStatus.BAD_REQUEST);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.errorHandler.handleError(this.anyURi, this.anyMethod, response))\n\t\t\t.withMessage(\"[unauthorized_client] The client is not authorized\");\n\t}\n\n\t@Test\n\tpublic void handleErrorWhenOAuth2ErrorConverterSetThenCalled() throws IOException {\n\t\tHttpMessageConverter<OAuth2Error> oauth2ErrorConverter = mock(HttpMessageConverter.class);\n\t\tthis.errorHandler.setErrorConverter(oauth2ErrorConverter);\n\t\t// @formatter:off\n\t\tString errorResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"errorCode\\\": \\\"unauthorized_client\\\",\\n\"\n\t\t\t\t+ \"   \\\"errorSummary\\\": \\\"The client is not authorized\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(errorResponse.getBytes(), HttpStatus.BAD_REQUEST);\n\t\tgiven(oauth2ErrorConverter.read(any(), any()))\n\t\t\t.willReturn(new OAuth2Error(\"unauthorized_client\", \"The client is not authorized\", null));\n\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.errorHandler.handleError(this.anyURi, this.anyMethod, response))\n\t\t\t.withMessage(\"[unauthorized_client] The client is not authorized\");\n\t\tverify(oauth2ErrorConverter).read(eq(OAuth2Error.class), eq(response));\n\t}\n\n\t@Test\n\tpublic void handleErrorWhenErrorResponseWwwAuthenticateHeaderThenHandled() {\n\t\tString wwwAuthenticateHeader = \"Bearer realm=\\\"auth-realm\\\" error=\\\"insufficient_scope\\\" error_description=\\\"The access token expired\\\"\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(new byte[0], HttpStatus.BAD_REQUEST);\n\t\tresponse.getHeaders().add(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticateHeader);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.errorHandler.handleError(this.anyURi, this.anyMethod, response))\n\t\t\t.withMessage(\"[insufficient_scope] The access token expired\");\n\t}\n\n\t@Test\n\tpublic void handleErrorWhenErrorResponseWithInvalidWwwAuthenticateHeaderThenHandled() {\n\t\tString invalidWwwAuthenticateHeader = \"Unauthorized\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(new byte[0], HttpStatus.BAD_REQUEST);\n\t\tresponse.getHeaders().add(HttpHeaders.WWW_AUTHENTICATE, invalidWwwAuthenticateHeader);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.errorHandler.handleError(this.anyURi, this.anyMethod, response))\n\t\t\t.withMessage(\"[server_error] \");\n\t}\n\n\t@Test\n\tpublic void handleErrorWhenErrorResponseWithInvalidStatusCodeThenHandled() {\n\t\tCustomMockClientHttpResponse response = new CustomMockClientHttpResponse(new byte[0], 596);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.errorHandler.handleError(this.anyURi, this.anyMethod, response))\n\t\t\t.withMessage(\"No matching constant for [596]\");\n\t}\n\n\tprivate static final class CustomMockClientHttpResponse extends MockHttpInputMessage implements ClientHttpResponse {\n\n\t\tprivate final int statusCode;\n\n\t\tprivate CustomMockClientHttpResponse(byte[] content, int statusCode) {\n\t\t\tsuper(content);\n\t\t\tthis.statusCode = statusCode;\n\t\t}\n\n\t\t@Override\n\t\tpublic HttpStatus getStatusCode() throws IOException {\n\t\t\treturn HttpStatus.valueOf(this.statusCode);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getStatusText() throws IOException {\n\t\t\tHttpStatus httpStatus = HttpStatus.valueOf(this.statusCode);\n\t\t\treturn (httpStatus != null) ? httpStatus.getReasonPhrase() : \"\";\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\ttry {\n\t\t\t\tgetBody().close();\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthenticationExceptionMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.exc.ValueInstantiationException;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for\n * {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationExceptionMixin}.\n *\n * @author Dennis Neufeld\n * @since 5.3.4\n */\npublic class OAuth2AuthenticationExceptionMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(\"[authorization_request_not_found]\", \"Authorization Request Not Found\", \"/foo/bar\"),\n\t\t\t\t\"Authorization Request Not Found\");\n\t\tString serializedJson = this.mapper.writeValueAsString(exception);\n\t\tString expected = asJson(exception);\n\t\tJSONAssert.assertEquals(expected, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(\"[authorization_request_not_found]\"));\n\t\tString serializedJson = this.mapper.writeValueAsString(exception);\n\t\tString expected = asJson(exception);\n\t\tJSONAssert.assertEquals(expected, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tString json = asJson(new OAuth2AuthenticationException(new OAuth2Error(\"[authorization_request_not_found]\")));\n\t\tassertThatExceptionOfType(ValueInstantiationException.class)\n\t\t\t.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthenticationException.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tOAuth2AuthenticationException expected = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(\"[authorization_request_not_found]\", \"Authorization Request Not Found\", \"/foo/bar\"),\n\t\t\t\t\"Authorization Request Not Found\");\n\t\tOAuth2AuthenticationException exception = this.mapper.readValue(asJson(expected),\n\t\t\t\tOAuth2AuthenticationException.class);\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getCause()).isNull();\n\t\tassertThat(exception.getMessage()).isEqualTo(expected.getMessage());\n\t\tOAuth2Error oauth2Error = exception.getError();\n\t\tassertThat(oauth2Error).isNotNull();\n\t\tassertThat(oauth2Error.getErrorCode()).isEqualTo(expected.getError().getErrorCode());\n\t\tassertThat(oauth2Error.getDescription()).isEqualTo(expected.getError().getDescription());\n\t\tassertThat(oauth2Error.getUri()).isEqualTo(expected.getError().getUri());\n\t}\n\n\t@Test\n\tpublic void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {\n\t\tOAuth2AuthenticationException expected = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(\"[authorization_request_not_found]\"));\n\t\tOAuth2AuthenticationException exception = this.mapper.readValue(asJson(expected),\n\t\t\t\tOAuth2AuthenticationException.class);\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getCause()).isNull();\n\t\tassertThat(exception.getMessage()).isNull();\n\t\tOAuth2Error oauth2Error = exception.getError();\n\t\tassertThat(oauth2Error).isNotNull();\n\t\tassertThat(oauth2Error.getErrorCode()).isEqualTo(expected.getError().getErrorCode());\n\t\tassertThat(oauth2Error.getDescription()).isNull();\n\t\tassertThat(oauth2Error.getUri()).isNull();\n\t}\n\n\tprivate String asJson(OAuth2AuthenticationException exception) {\n\t\tOAuth2Error error = exception.getError();\n\t\t// @formatter:off\n\t\treturn \"\\n{\"\n\t\t\t\t+ \"\\n  \\\"@class\\\": \\\"org.springframework.security.oauth2.core.OAuth2AuthenticationException\\\",\"\n\t\t\t\t+ \"\\n  \\\"error\\\":\"\n\t\t\t\t+ \"\\n  {\"\n\t\t\t\t+ \"\\n    \\\"@class\\\":\\\"org.springframework.security.oauth2.core.OAuth2Error\\\",\"\n\t\t\t\t+ \"\\n    \\\"errorCode\\\":\\\"\" + error.getErrorCode() + \"\\\",\"\n\t\t\t\t+ \"\\n    \\\"description\\\":\" + jsonStringOrNull(error.getDescription()) + \",\"\n\t\t\t\t+ \"\\n    \\\"uri\\\":\" + jsonStringOrNull(error.getUri())\n\t\t\t\t+ \"\\n  },\"\n\t\t\t\t+ \"\\n  \\\"detailMessage\\\":\" + jsonStringOrNull(exception.getMessage())\n\t\t\t\t+ \"\\n}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate String jsonStringOrNull(String input) {\n\t\treturn (input != null) ? \"\\\"\" + input + \"\\\"\" : \"null\";\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.fasterxml.jackson.datatype.jsr310.DecimalUtils;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.databind.DeserializationFeature;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.TestOAuth2AuthenticationTokens;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for\n * {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationTokenMixin}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthenticationTokenMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder()\n\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n\t\t\t// see https://github.com/FasterXML/jackson-databind/issues/3052 for details\n\t\t\t.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\t// OidcUser\n\t\tOAuth2AuthenticationToken authentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();\n\t\tString expectedJson = asJson(authentication);\n\t\tString json = this.mapper.writeValueAsString(authentication);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t\t// OAuth2User\n\t\tauthentication = TestOAuth2AuthenticationTokens.authenticated();\n\t\texpectedJson = asJson(authentication);\n\t\tjson = this.mapper.writeValueAsString(authentication);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tprincipal = new DefaultOidcUser(principal.getAuthorities(), principal.getIdToken());\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, Collections.emptyList(),\n\t\t\t\t\"registration-id\");\n\t\tString expectedJson = asJson(authentication);\n\t\tString json = this.mapper.writeValueAsString(authentication);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tOAuth2AuthenticationToken authentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();\n\t\tString json = asJson(authentication);\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\t// OidcUser\n\t\tOAuth2AuthenticationToken expectedAuthentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();\n\t\tString json = asJson(expectedAuthentication);\n\t\tOAuth2AuthenticationToken authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());\n\t\tassertThat(authentication.getAuthorizedClientRegistrationId())\n\t\t\t.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());\n\t\tDefaultOidcUser expectedOidcUser = (DefaultOidcUser) expectedAuthentication.getPrincipal();\n\t\tDefaultOidcUser oidcUser = (DefaultOidcUser) authentication.getPrincipal();\n\t\tassertThat(oidcUser.getAuthorities().containsAll(expectedOidcUser.getAuthorities())).isTrue();\n\t\tassertThat(oidcUser.getAttributes()).containsExactlyEntriesOf(expectedOidcUser.getAttributes());\n\t\tassertThat(oidcUser.getName()).isEqualTo(expectedOidcUser.getName());\n\t\tOidcIdToken expectedIdToken = expectedOidcUser.getIdToken();\n\t\tOidcIdToken idToken = oidcUser.getIdToken();\n\t\tassertThat(idToken.getTokenValue()).isEqualTo(expectedIdToken.getTokenValue());\n\t\tassertThat(idToken.getIssuedAt()).isEqualTo(expectedIdToken.getIssuedAt());\n\t\tassertThat(idToken.getExpiresAt()).isEqualTo(expectedIdToken.getExpiresAt());\n\t\tassertThat(idToken.getClaims()).containsExactlyEntriesOf(expectedIdToken.getClaims());\n\t\tOidcUserInfo expectedUserInfo = expectedOidcUser.getUserInfo();\n\t\tOidcUserInfo userInfo = oidcUser.getUserInfo();\n\t\tassertThat(userInfo.getClaims()).containsExactlyEntriesOf(expectedUserInfo.getClaims());\n\t\t// OAuth2User\n\t\texpectedAuthentication = TestOAuth2AuthenticationTokens.authenticated();\n\t\tjson = asJson(expectedAuthentication);\n\t\tauthentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());\n\t\tassertThat(authentication.getAuthorizedClientRegistrationId())\n\t\t\t.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());\n\t\tDefaultOAuth2User expectedOauth2User = (DefaultOAuth2User) expectedAuthentication.getPrincipal();\n\t\tDefaultOAuth2User oauth2User = (DefaultOAuth2User) authentication.getPrincipal();\n\t\tassertThat(oauth2User.getAuthorities().containsAll(expectedOauth2User.getAuthorities())).isTrue();\n\t\tassertThat(oauth2User.getAttributes()).containsExactlyEntriesOf(expectedOauth2User.getAttributes());\n\t\tassertThat(oauth2User.getName()).isEqualTo(expectedOauth2User.getName());\n\t}\n\n\t@Test\n\tpublic void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {\n\t\tDefaultOidcUser expectedPrincipal = TestOidcUsers.create();\n\t\texpectedPrincipal = new DefaultOidcUser(expectedPrincipal.getAuthorities(), expectedPrincipal.getIdToken());\n\t\tOAuth2AuthenticationToken expectedAuthentication = new OAuth2AuthenticationToken(expectedPrincipal,\n\t\t\t\tCollections.emptyList(), \"registration-id\");\n\t\tString json = asJson(expectedAuthentication);\n\t\tOAuth2AuthenticationToken authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);\n\t\tassertThat(authentication.getAuthorities()).isEmpty();\n\t\tassertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());\n\t\tassertThat(authentication.getAuthorizedClientRegistrationId())\n\t\t\t.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());\n\t\tDefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();\n\t\tassertThat(principal.getAuthorities().containsAll(expectedPrincipal.getAuthorities())).isTrue();\n\t\tassertThat(principal.getAttributes()).containsExactlyEntriesOf(expectedPrincipal.getAttributes());\n\t\tassertThat(principal.getName()).isEqualTo(expectedPrincipal.getName());\n\t\tOidcIdToken expectedIdToken = expectedPrincipal.getIdToken();\n\t\tOidcIdToken idToken = principal.getIdToken();\n\t\tassertThat(idToken.getTokenValue()).isEqualTo(expectedIdToken.getTokenValue());\n\t\tassertThat(idToken.getIssuedAt()).isEqualTo(expectedIdToken.getIssuedAt());\n\t\tassertThat(idToken.getExpiresAt()).isEqualTo(expectedIdToken.getExpiresAt());\n\t\tassertThat(idToken.getClaims()).containsExactlyEntriesOf(expectedIdToken.getClaims());\n\t\tassertThat(principal.getUserInfo()).isNull();\n\t}\n\n\tprivate static String asJson(OAuth2AuthenticationToken authentication) {\n\t\tString principalJson = (authentication.getPrincipal() instanceof DefaultOidcUser)\n\t\t\t\t? asJson((DefaultOidcUser) authentication.getPrincipal())\n\t\t\t\t: asJson((DefaultOAuth2User) authentication.getPrincipal());\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"  \\\"@class\\\": \\\"org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken\\\",\\n\" +\n\t\t\t\t\"  \\\"principal\\\": \" + principalJson + \",\\n\" +\n\t\t\t\t\"  \\\"authorities\\\": \" + asJson(authentication.getAuthorities(), \"java.util.Collections$UnmodifiableRandomAccessList\") + \",\\n\" +\n\t\t\t\t\"  \\\"authorizedClientRegistrationId\\\": \\\"\" + authentication.getAuthorizedClientRegistrationId() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"details\\\": null\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(DefaultOAuth2User oauth2User) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.core.user.DefaultOAuth2User\\\",\\n\" +\n\t\t\t\t\"    \\\"authorities\\\": \" + asJson(oauth2User.getAuthorities(), \"java.util.Collections$UnmodifiableSet\") + \",\\n\" +\n\t\t\t\t\"    \\\"attributes\\\": {\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\\n\" +\n\t\t\t\t\"      \\\"username\\\": \\\"user\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"nameAttributeKey\\\": \\\"username\\\"\\n\" +\n\t\t\t\t\"  }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(DefaultOidcUser oidcUser) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser\\\",\\n\" +\n\t\t\t\t\"    \\\"authorities\\\": \" + asJson(oidcUser.getAuthorities(), \"java.util.Collections$UnmodifiableSet\") + \",\\n\" +\n\t\t\t\t\"    \\\"idToken\\\": \" + asJson(oidcUser.getIdToken()) + \",\\n\" +\n\t\t\t\t\"    \\\"userInfo\\\": \" + asJson(oidcUser.getUserInfo()) + \",\\n\" +\n\t\t\t\t\"    \\\"nameAttributeKey\\\": \\\"\" + IdTokenClaimNames.SUB + \"\\\"\\n\" +\n\t\t\t\t\"  }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(Collection<? extends GrantedAuthority> authorities, String classTypeInfo) {\n\t\tOAuth2UserAuthority oauth2UserAuthority = null;\n\t\tOidcUserAuthority oidcUserAuthority = null;\n\t\tList<SimpleGrantedAuthority> simpleAuthorities = new ArrayList<>();\n\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\tif (authority instanceof OidcUserAuthority) {\n\t\t\t\toidcUserAuthority = (OidcUserAuthority) authority;\n\t\t\t}\n\t\t\telse if (authority instanceof OAuth2UserAuthority) {\n\t\t\t\toauth2UserAuthority = (OAuth2UserAuthority) authority;\n\t\t\t}\n\t\t\telse if (authority instanceof SimpleGrantedAuthority) {\n\t\t\t\tsimpleAuthorities.add((SimpleGrantedAuthority) authority);\n\t\t\t}\n\t\t}\n\t\tString authoritiesJson = (oidcUserAuthority != null) ? asJson(oidcUserAuthority)\n\t\t\t\t: (oauth2UserAuthority != null) ? asJson(oauth2UserAuthority) : \"\";\n\t\tif (!simpleAuthorities.isEmpty()) {\n\t\t\tif (StringUtils.hasLength(authoritiesJson)) {\n\t\t\t\tauthoritiesJson += \",\";\n\t\t\t}\n\t\t\tauthoritiesJson += asJson(simpleAuthorities);\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"[\\n\" +\n\t\t\t\t\"      \\\"\" + classTypeInfo + \"\\\",\\n\" +\n\t\t\t\t\"      [\" + authoritiesJson + \"]\\n\" +\n\t\t\t\t\"    ]\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OAuth2UserAuthority oauth2UserAuthority) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"          \\\"@class\\\": \\\"org.springframework.security.oauth2.core.user.OAuth2UserAuthority\\\",\\n\" +\n\t\t\t\t\"          \\\"authority\\\": \\\"\" + oauth2UserAuthority.getAuthority() + \"\\\",\\n\" +\n\t\t\t\t\"          \\\"userNameAttributeName\\\": \\\"username\\\",\\n\" +\n\t\t\t\t\"          \\\"attributes\\\": {\\n\" +\n\t\t\t\t\"            \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\\n\" +\n\t\t\t\t\"            \\\"username\\\": \\\"user\\\"\\n\" +\n\t\t\t\t\"          }\\n\" +\n\t\t\t\t\"        }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OidcUserAuthority oidcUserAuthority) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"          \\\"@class\\\": \\\"org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority\\\",\\n\" +\n\t\t\t\t\"          \\\"authority\\\": \\\"\" + oidcUserAuthority.getAuthority() + \"\\\",\\n\" +\n\t\t\t\t\"          \\\"userNameAttributeName\\\": \\\"\" + oidcUserAuthority.getUserNameAttributeName() + \"\\\",\\n\" +\n\t\t\t\t\"          \\\"idToken\\\": \" + asJson(oidcUserAuthority.getIdToken()) + \",\\n\" +\n\t\t\t\t\"          \\\"userInfo\\\": \" + asJson(oidcUserAuthority.getUserInfo()) + \"\\n\" +\n\t\t\t\t\"        }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(List<SimpleGrantedAuthority> simpleAuthorities) {\n\t\t// @formatter:off\n\t\treturn simpleAuthorities.stream()\n\t\t\t\t.map((authority) -> \"{\\n\" +\n\t\t\t\t\t\t\"        \\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\\n\" +\n\t\t\t\t\t\t\"        \\\"authority\\\": \\\"\" + authority.getAuthority() + \"\\\"\\n\" +\n\t\t\t\t\t\t\"      }\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OidcIdToken idToken) {\n\t\tString aud = \"\";\n\t\tif (!CollectionUtils.isEmpty(idToken.getAudience())) {\n\t\t\taud = StringUtils.collectionToDelimitedString(idToken.getAudience(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"org.springframework.security.oauth2.core.oidc.OidcIdToken\\\",\\n\" +\n\t\t\t\t\"      \\\"tokenValue\\\": \\\"\" + idToken.getTokenValue() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"issuedAt\\\": \" + toString(idToken.getIssuedAt()) + \",\\n\" +\n\t\t\t\t\"      \\\"expiresAt\\\": \" + toString(idToken.getExpiresAt()) + \",\\n\" +\n\t\t\t\t\"      \\\"claims\\\": {\\n\" +\n\t\t\t\t\"        \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\\n\" +\n\t\t\t\t\"        \\\"iat\\\": [\\n\" +\n\t\t\t\t\"          \\\"java.time.Instant\\\",\\n\" +\n\t\t\t\t\"          \" + toString(idToken.getIssuedAt()) + \"\\n\" +\n\t\t\t\t\"        ],\\n\" +\n\t\t\t\t\"        \\\"exp\\\": [\\n\" +\n\t\t\t\t\"          \\\"java.time.Instant\\\",\\n\" +\n\t\t\t\t\"          \" + toString(idToken.getExpiresAt()) + \"\\n\" +\n\t\t\t\t\"        ],\\n\" +\n\t\t\t\t\"        \\\"sub\\\": \\\"\" + idToken.getSubject() + \"\\\",\\n\" +\n\t\t\t\t\"        \\\"iss\\\": \\\"\" + idToken.getIssuer() + \"\\\",\\n\" +\n\t\t\t\t\"        \\\"aud\\\": [\\n\" +\n\t\t\t\t\"          \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"          [\" + aud + \"]\\n\" +\n\t\t\t\t\"        ],\\n\" +\n\t\t\t\t\"        \\\"azp\\\": \\\"\" + idToken.getAuthorizedParty() + \"\\\"\\n\" +\n\t\t\t\t\"      }\\n\" +\n\t\t\t\t\"    }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OidcUserInfo userInfo) {\n\t\tif (userInfo == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"org.springframework.security.oauth2.core.oidc.OidcUserInfo\\\",\\n\" +\n\t\t\t\t\"      \\\"claims\\\": {\\n\" +\n\t\t\t\t\"        \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\\n\" +\n\t\t\t\t\"        \\\"sub\\\": \\\"\" + userInfo.getSubject() + \"\\\",\\n\" +\n\t\t\t\t\"        \\\"name\\\": \\\"\" + userInfo.getClaim(StandardClaimNames.NAME) + \"\\\"\\n\" +\n\t\t\t\t\"      }\\n\" +\n\t\t\t\t\"    }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String toString(Instant instant) {\n\t\tif (instant == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn DecimalUtils.toBigDecimal(instant.getEpochSecond(), instant.getNano()).toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthorizationRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.exc.StreamReadException;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for\n * {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthorizationRequestMixin}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationRequestMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\tprivate OAuth2AuthorizationRequest.Builder authorizationRequestBuilder;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\t// @formatter:off\n\t\tthis.authorizationRequestBuilder = TestOAuth2AuthorizationRequests.request()\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.additionalParameters(additionalParameters);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build();\n\t\tString expectedJson = asJson(authorizationRequest);\n\t\tString json = this.mapper.writeValueAsString(authorizationRequest);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder\n\t\t\t\t.scopes(null)\n\t\t\t\t.state(null)\n\t\t\t\t.additionalParameters(Map::clear)\n\t\t\t\t.attributes(Map::clear)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tString expectedJson = asJson(authorizationRequest);\n\t\tString json = this.mapper.writeValueAsString(authorizationRequest);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tString json = asJson(this.authorizationRequestBuilder.build());\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthorizationRequest.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tOAuth2AuthorizationRequest expectedAuthorizationRequest = this.authorizationRequestBuilder.build();\n\t\tString json = asJson(expectedAuthorizationRequest);\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.mapper.readValue(json, OAuth2AuthorizationRequest.class);\n\t\tassertThat(authorizationRequest.getAuthorizationUri())\n\t\t\t.isEqualTo(expectedAuthorizationRequest.getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(expectedAuthorizationRequest.getGrantType());\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(expectedAuthorizationRequest.getResponseType());\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(expectedAuthorizationRequest.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedAuthorizationRequest.getRedirectUri());\n\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(expectedAuthorizationRequest.getScopes());\n\t\tassertThat(authorizationRequest.getState()).isEqualTo(expectedAuthorizationRequest.getState());\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.containsExactlyEntriesOf(expectedAuthorizationRequest.getAdditionalParameters());\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(expectedAuthorizationRequest.getAuthorizationRequestUri());\n\t\tassertThat(authorizationRequest.getAttributes())\n\t\t\t.containsExactlyEntriesOf(expectedAuthorizationRequest.getAttributes());\n\t}\n\n\t@Test\n\tpublic void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest expectedAuthorizationRequest = this.authorizationRequestBuilder.scopes(null)\n\t\t\t\t.state(null)\n\t\t\t\t.additionalParameters(Map::clear)\n\t\t\t\t.attributes(Map::clear)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tString json = asJson(expectedAuthorizationRequest);\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.mapper.readValue(json, OAuth2AuthorizationRequest.class);\n\t\tassertThat(authorizationRequest.getAuthorizationUri())\n\t\t\t.isEqualTo(expectedAuthorizationRequest.getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(expectedAuthorizationRequest.getGrantType());\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(expectedAuthorizationRequest.getResponseType());\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(expectedAuthorizationRequest.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedAuthorizationRequest.getRedirectUri());\n\t\tassertThat(authorizationRequest.getScopes()).isEmpty();\n\t\tassertThat(authorizationRequest.getState()).isNull();\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).isEmpty();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(expectedAuthorizationRequest.getAuthorizationRequestUri());\n\t\tassertThat(authorizationRequest.getAttributes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void deserializeWhenInvalidAuthorizationGrantTypeThenThrowJsonParseException() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build();\n\t\tString json = asJson(authorizationRequest).replace(\"authorization_code\", \"client_credentials\");\n\t\tassertThatExceptionOfType(StreamReadException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(json, OAuth2AuthorizationRequest.class))\n\t\t\t.withMessageContaining(\"Invalid authorizationGrantType\");\n\t}\n\n\tprivate static String asJson(OAuth2AuthorizationRequest authorizationRequest) {\n\t\tString scopes = \"\";\n\t\tif (!CollectionUtils.isEmpty(authorizationRequest.getScopes())) {\n\t\t\tscopes = StringUtils.collectionToDelimitedString(authorizationRequest.getScopes(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\tString additionalParameters = \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\"\";\n\t\tif (!CollectionUtils.isEmpty(authorizationRequest.getAdditionalParameters())) {\n\t\t\tadditionalParameters += \",\" + authorizationRequest.getAdditionalParameters()\n\t\t\t\t.keySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((key) -> \"\\\"\" + key + \"\\\": \\\"\" + authorizationRequest.getAdditionalParameters().get(key) + \"\\\"\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\t\tString attributes = \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\"\";\n\t\tif (!CollectionUtils.isEmpty(authorizationRequest.getAttributes())) {\n\t\t\tattributes += \",\" + authorizationRequest.getAttributes()\n\t\t\t\t.keySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((key) -> \"\\\"\" + key + \"\\\": \\\"\" + authorizationRequest.getAttributes().get(key) + \"\\\"\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"  \\\"@class\\\": \\\"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\\\",\\n\" +\n\t\t\t\t\"  \\\"authorizationUri\\\": \\\"\" + authorizationRequest.getAuthorizationUri() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"authorizationGrantType\\\": {\\n\" +\n\t\t\t\t\"    \\\"value\\\": \\\"\" + authorizationRequest.getGrantType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"  },\\n\" +\n\t\t\t\t\"  \\\"responseType\\\": {\\n\" +\n\t\t\t\t\"    \\\"value\\\": \\\"\" + authorizationRequest.getResponseType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"  },\\n\" +\n\t\t\t\t\"  \\\"clientId\\\": \\\"\" + authorizationRequest.getClientId() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"redirectUri\\\": \\\"\" + authorizationRequest.getRedirectUri() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"scopes\\\": [\\n\" +\n\t\t\t\t\"    \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"    [\" + scopes + \"]\\n\" +\n\t\t\t\t\"  ],\\n\" +\n\t\t\t\t\"  \\\"state\\\": \" + ((authorizationRequest.getState() != null) ? \"\\\"\" + authorizationRequest.getState() + \"\\\"\" : \"null\") + \",\\n\" +\n\t\t\t\t\"  \\\"additionalParameters\\\": {\\n\" +\n\t\t\t\t\"    \" + additionalParameters + \"\\n\" +\n\t\t\t\t\"  },\\n\" +\n\t\t\t\t\"  \\\"authorizationRequestUri\\\": \\\"\" + authorizationRequest.getAuthorizationRequestUri() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"attributes\\\": {\\n\" +\n\t\t\t\t\"    \" + attributes + \"\\n\" +\n\t\t\t\t\"  }\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/OAuth2AuthorizedClientMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.time.Instant;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport com.fasterxml.jackson.datatype.jsr310.DecimalUtils;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for\n * {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthorizedClientMixin}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizedClientMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\tprivate ClientRegistration.Builder clientRegistrationBuilder;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate OAuth2RefreshToken refreshToken;\n\n\tprivate String principalName;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t\tMap<String, Object> providerConfigurationMetadata = new LinkedHashMap<>();\n\t\tproviderConfigurationMetadata.put(\"config1\", \"value1\");\n\t\tproviderConfigurationMetadata.put(\"config2\", \"value2\");\n\t\t// @formatter:off\n\t\tthis.clientRegistrationBuilder = TestClientRegistrations.clientRegistration()\n\t\t\t\t.authorizationGrantType(new AuthorizationGrantType(\"custom-grant\"))\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.providerConfigurationMetadata(providerConfigurationMetadata);\n\t\t// @formatter:on\n\t\tthis.accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\tthis.principalName = \"principal-name\";\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),\n\t\t\t\tthis.principalName, this.accessToken, this.refreshToken);\n\t\tString expectedJson = asJson(authorizedClient);\n\t\tString json = this.mapper.writeValueAsString(authorizedClient);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.clientName(null)\n\t\t\t\t.userInfoUri(null)\n\t\t\t\t.userNameAttributeName(null)\n\t\t\t\t.jwkSetUri(null)\n\t\t\t\t.issuerUri(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, this.principalName,\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tString expectedJson = asJson(authorizedClient);\n\t\tString json = this.mapper.writeValueAsString(authorizedClient);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),\n\t\t\t\tthis.principalName, this.accessToken);\n\t\tString json = asJson(authorizedClient);\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthorizedClient.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tClientRegistration expectedClientRegistration = this.clientRegistrationBuilder.build();\n\t\tOAuth2AccessToken expectedAccessToken = this.accessToken;\n\t\tOAuth2RefreshToken expectedRefreshToken = this.refreshToken;\n\t\tOAuth2AuthorizedClient expectedAuthorizedClient = new OAuth2AuthorizedClient(expectedClientRegistration,\n\t\t\t\tthis.principalName, expectedAccessToken, expectedRefreshToken);\n\t\tString json = asJson(expectedAuthorizedClient);\n\t\tOAuth2AuthorizedClient authorizedClient = this.mapper.readValue(json, OAuth2AuthorizedClient.class);\n\t\tClientRegistration clientRegistration = authorizedClient.getClientRegistration();\n\t\tassertThat(clientRegistration.getRegistrationId()).isEqualTo(expectedClientRegistration.getRegistrationId());\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(expectedClientRegistration.getClientId());\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(expectedClientRegistration.getClientSecret());\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistration.getClientAuthenticationMethod());\n\t\tassertThat(clientRegistration.getAuthorizationGrantType())\n\t\t\t.isEqualTo(expectedClientRegistration.getAuthorizationGrantType());\n\t\tassertThat(clientRegistration.getRedirectUri()).isEqualTo(expectedClientRegistration.getRedirectUri());\n\t\tassertThat(clientRegistration.getScopes()).isEqualTo(expectedClientRegistration.getScopes());\n\t\tassertThat(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getAuthorizationUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getTokenUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getTokenUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(\n\t\t\t\texpectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName());\n\t\tassertThat(clientRegistration.getProviderDetails().getJwkSetUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getJwkSetUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getIssuerUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getIssuerUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata())\n\t\t\t.containsExactlyEntriesOf(clientRegistration.getProviderDetails().getConfigurationMetadata());\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(expectedClientRegistration.getClientName());\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expectedAuthorizedClient.getPrincipalName());\n\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(expectedAccessToken.getTokenType());\n\t\tassertThat(accessToken.getScopes()).isEqualTo(expectedAccessToken.getScopes());\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(expectedAccessToken.getTokenValue());\n\t\tassertThat(accessToken.getIssuedAt()).isEqualTo(expectedAccessToken.getIssuedAt());\n\t\tassertThat(accessToken.getExpiresAt()).isEqualTo(expectedAccessToken.getExpiresAt());\n\t\tOAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken();\n\t\tassertThat(refreshToken.getTokenValue()).isEqualTo(expectedRefreshToken.getTokenValue());\n\t\tassertThat(refreshToken.getIssuedAt()).isEqualTo(expectedRefreshToken.getIssuedAt());\n\t\tassertThat(refreshToken.getExpiresAt()).isEqualTo(expectedRefreshToken.getExpiresAt());\n\t}\n\n\t@Test\n\tpublic void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {\n\t\t// @formatter:off\n\t\tClientRegistration expectedClientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.clientName(null)\n\t\t\t\t.userInfoUri(null)\n\t\t\t\t.userNameAttributeName(null)\n\t\t\t\t.jwkSetUri(null)\n\t\t\t\t.issuerUri(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AccessToken expectedAccessToken = TestOAuth2AccessTokens.noScopes();\n\t\tOAuth2AuthorizedClient expectedAuthorizedClient = new OAuth2AuthorizedClient(expectedClientRegistration,\n\t\t\t\tthis.principalName, expectedAccessToken);\n\t\tString json = asJson(expectedAuthorizedClient);\n\t\tOAuth2AuthorizedClient authorizedClient = this.mapper.readValue(json, OAuth2AuthorizedClient.class);\n\t\tClientRegistration clientRegistration = authorizedClient.getClientRegistration();\n\t\tassertThat(clientRegistration.getRegistrationId()).isEqualTo(expectedClientRegistration.getRegistrationId());\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(expectedClientRegistration.getClientId());\n\t\tassertThat(clientRegistration.getClientSecret()).isEmpty();\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistration.getClientAuthenticationMethod());\n\t\tassertThat(clientRegistration.getAuthorizationGrantType())\n\t\t\t.isEqualTo(expectedClientRegistration.getAuthorizationGrantType());\n\t\tassertThat(clientRegistration.getRedirectUri()).isEqualTo(expectedClientRegistration.getRedirectUri());\n\t\tassertThat(clientRegistration.getScopes()).isEqualTo(expectedClientRegistration.getScopes());\n\t\tassertThat(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getAuthorizationUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getTokenUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getTokenUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri()).isNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getJwkSetUri()).isNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expectedAuthorizedClient.getPrincipalName());\n\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(expectedAccessToken.getTokenType());\n\t\tassertThat(accessToken.getScopes()).isEmpty();\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(expectedAccessToken.getTokenValue());\n\t\tassertThat(accessToken.getIssuedAt()).isEqualTo(expectedAccessToken.getIssuedAt());\n\t\tassertThat(accessToken.getExpiresAt()).isEqualTo(expectedAccessToken.getExpiresAt());\n\t\tassertThat(authorizedClient.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tvoid deserializeWhenClientSettingsPropertyDoesNotExistThenDefaulted() throws JacksonException {\n\t\t// ClientRegistration.clientSettings was added later, so old values will be\n\t\t// serialized without that property\n\t\t// this test checks for passivity\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.build();\n\t\tClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();\n\t\tClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();\n\t\tString scopes = \"\";\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tscopes = StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\tString configurationMetadata = \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\"\";\n\t\tif (!CollectionUtils.isEmpty(providerDetails.getConfigurationMetadata())) {\n\t\t\tconfigurationMetadata += \",\" + providerDetails.getConfigurationMetadata()\n\t\t\t\t.keySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((key) -> \"\\\"\" + key + \"\\\": \\\"\" + providerDetails.getConfigurationMetadata().get(key) + \"\\\"\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\t\t// @formatter:off\n\t\tString json = \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration\\\",\\n\" +\n\t\t\t\t\"    \\\"registrationId\\\": \\\"\" + clientRegistration.getRegistrationId() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientId\\\": \\\"\" + clientRegistration.getClientId() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientSecret\\\": \\\"\" + clientRegistration.getClientSecret() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientAuthenticationMethod\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + clientRegistration.getClientAuthenticationMethod().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"authorizationGrantType\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + clientRegistration.getAuthorizationGrantType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"redirectUri\\\": \\\"\" + clientRegistration.getRedirectUri() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"scopes\\\": [\\n\" +\n\t\t\t\t\"      \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"      [\" + scopes + \"]\\n\" +\n\t\t\t\t\"    ],\\n\" +\n\t\t\t\t\"    \\\"providerDetails\\\": {\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\\\",\\n\" +\n\t\t\t\t\"      \\\"authorizationUri\\\": \\\"\" + providerDetails.getAuthorizationUri() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"tokenUri\\\": \\\"\" + providerDetails.getTokenUri() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"userInfoEndpoint\\\": {\\n\" +\n\t\t\t\t\"        \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\\\",\\n\" +\n\t\t\t\t\"        \\\"uri\\\": \" + ((userInfoEndpoint.getUri() != null) ? \"\\\"\" + userInfoEndpoint.getUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"        \\\"authenticationMethod\\\": {\\n\" +\n\t\t\t\t\"          \\\"value\\\": \\\"\" + userInfoEndpoint.getAuthenticationMethod().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"        },\\n\" +\n\t\t\t\t\"        \\\"userNameAttributeName\\\": \" + ((userInfoEndpoint.getUserNameAttributeName() != null) ? \"\\\"\" + userInfoEndpoint.getUserNameAttributeName() + \"\\\"\" : null) + \"\\n\" +\n\t\t\t\t\"      },\\n\" +\n\t\t\t\t\"      \\\"jwkSetUri\\\": \" + ((providerDetails.getJwkSetUri() != null) ? \"\\\"\" + providerDetails.getJwkSetUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"      \\\"issuerUri\\\": \" + ((providerDetails.getIssuerUri() != null) ? \"\\\"\" + providerDetails.getIssuerUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"      \\\"configurationMetadata\\\": {\\n\" +\n\t\t\t\t\"        \" + configurationMetadata + \"\\n\" +\n\t\t\t\t\"      }\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"clientName\\\": \\\"\" + clientRegistration.getClientName() + \"\\\"\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t\t// validate the test input\n\t\tassertThat(json).doesNotContain(\"clientSettings\");\n\t\tClientRegistration registration = this.mapper.readValue(json, ClientRegistration.class);\n\t\t// the default value of requireProofKey is false\n\t\tassertThat(registration.getClientSettings().isRequireProofKey()).isFalse();\n\t}\n\n\tprivate static String asJson(OAuth2AuthorizedClient authorizedClient) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"  \\\"@class\\\": \\\"org.springframework.security.oauth2.client.OAuth2AuthorizedClient\\\",\\n\" +\n\t\t\t\t\"  \\\"clientRegistration\\\": \" + asJson(authorizedClient.getClientRegistration()) + \",\\n\" +\n\t\t\t\t\"  \\\"principalName\\\": \\\"\" + authorizedClient.getPrincipalName() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"accessToken\\\": \" + asJson(authorizedClient.getAccessToken()) + \",\\n\" +\n\t\t\t\t\"  \\\"refreshToken\\\": \" + asJson(authorizedClient.getRefreshToken()) + \"\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(ClientRegistration clientRegistration) {\n\t\tClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();\n\t\tClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();\n\t\tString scopes = \"\";\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tscopes = StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\tString configurationMetadata = \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\"\";\n\t\tif (!CollectionUtils.isEmpty(providerDetails.getConfigurationMetadata())) {\n\t\t\tconfigurationMetadata += \",\" + providerDetails.getConfigurationMetadata()\n\t\t\t\t.keySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((key) -> \"\\\"\" + key + \"\\\": \\\"\" + providerDetails.getConfigurationMetadata().get(key) + \"\\\"\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration\\\",\\n\" +\n\t\t\t\t\"    \\\"registrationId\\\": \\\"\" + clientRegistration.getRegistrationId() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientId\\\": \\\"\" + clientRegistration.getClientId() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientSecret\\\": \\\"\" + clientRegistration.getClientSecret() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientAuthenticationMethod\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + clientRegistration.getClientAuthenticationMethod().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"authorizationGrantType\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + clientRegistration.getAuthorizationGrantType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"redirectUri\\\": \\\"\" + clientRegistration.getRedirectUri() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"scopes\\\": [\\n\" +\n\t\t\t\t\"      \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"      [\" + scopes + \"]\\n\" +\n\t\t\t\t\"    ],\\n\" +\n\t\t\t\t\"    \\\"providerDetails\\\": {\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\\\",\\n\" +\n\t\t\t\t\"      \\\"authorizationUri\\\": \\\"\" + providerDetails.getAuthorizationUri() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"tokenUri\\\": \\\"\" + providerDetails.getTokenUri() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"userInfoEndpoint\\\": {\\n\" +\n\t\t\t\t\"        \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\\\",\\n\" +\n\t\t\t\t\"        \\\"uri\\\": \" + ((userInfoEndpoint.getUri() != null) ? \"\\\"\" + userInfoEndpoint.getUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"        \\\"authenticationMethod\\\": {\\n\" +\n\t\t\t\t\"          \\\"value\\\": \\\"\" + userInfoEndpoint.getAuthenticationMethod().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"        },\\n\" +\n\t\t\t\t\"        \\\"userNameAttributeName\\\": \" + ((userInfoEndpoint.getUserNameAttributeName() != null) ? \"\\\"\" + userInfoEndpoint.getUserNameAttributeName() + \"\\\"\" : null) + \"\\n\" +\n\t\t\t\t\"      },\\n\" +\n\t\t\t\t\"      \\\"jwkSetUri\\\": \" + ((providerDetails.getJwkSetUri() != null) ? \"\\\"\" + providerDetails.getJwkSetUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"      \\\"issuerUri\\\": \" + ((providerDetails.getIssuerUri() != null) ? \"\\\"\" + providerDetails.getIssuerUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"      \\\"configurationMetadata\\\": {\\n\" +\n\t\t\t\t\"        \" + configurationMetadata + \"\\n\" +\n\t\t\t\t\"      }\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"clientName\\\": \\\"\" + clientRegistration.getClientName() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientSettings\\\": {\\n\" +\n\t\t\t\t\"      \\\"requireProofKey\\\": \" + clientRegistration.getClientSettings().isRequireProofKey() + \"\\n\" +\n\t\t\t\t\"    }\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OAuth2AccessToken accessToken) {\n\t\tString scopes = \"\";\n\t\tif (!CollectionUtils.isEmpty(accessToken.getScopes())) {\n\t\t\tscopes = StringUtils.collectionToDelimitedString(accessToken.getScopes(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.core.OAuth2AccessToken\\\",\\n\" +\n\t\t\t\t\"    \\\"tokenType\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + accessToken.getTokenType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"tokenValue\\\": \\\"\" + accessToken.getTokenValue() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"issuedAt\\\": \" + toString(accessToken.getIssuedAt()) + \",\\n\" +\n\t\t\t\t\"    \\\"expiresAt\\\": \" + toString(accessToken.getExpiresAt()) + \",\\n\" +\n\t\t\t\t\"    \\\"scopes\\\": [\\n\" +\n\t\t\t\t\"      \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"      [\" + scopes + \"]\\n\" +\n\t\t\t\t\"    ]\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OAuth2RefreshToken refreshToken) {\n\t\tif (refreshToken == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.core.OAuth2RefreshToken\\\",\\n\" +\n\t\t\t\t\"    \\\"tokenValue\\\": \\\"\" + refreshToken.getTokenValue() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"issuedAt\\\": \" + toString(refreshToken.getIssuedAt()) + \",\\n\" +\n\t\t\t\t\"    \\\"expiresAt\\\": \" + toString(refreshToken.getExpiresAt()) + \"\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String toString(Instant instant) {\n\t\tif (instant == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn DecimalUtils.toBigDecimal(instant.getEpochSecond(), instant.getNano()).toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson/StdConvertersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.node.JsonNodeFactory;\nimport tools.jackson.databind.node.ObjectNode;\nimport tools.jackson.databind.util.StdConverter;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class StdConvertersTests {\n\n\tprivate final StdConverter<JsonNode, ClientAuthenticationMethod> clientAuthenticationMethodConverter = new org.springframework.security.oauth2.client.jackson.StdConverters.ClientAuthenticationMethodConverter();\n\n\t@ParameterizedTest\n\t@MethodSource(\"convertWhenClientAuthenticationMethodConvertedThenDeserializes\")\n\tvoid convertWhenClientAuthenticationMethodConvertedThenDeserializes(String clientAuthenticationMethod) {\n\t\tObjectNode jsonNode = JsonNodeFactory.instance.objectNode();\n\t\tjsonNode.put(\"value\", clientAuthenticationMethod);\n\t\tClientAuthenticationMethod actual = this.clientAuthenticationMethodConverter.convert(jsonNode);\n\t\tassertThat(actual.getValue()).isEqualTo(clientAuthenticationMethod);\n\t}\n\n\tstatic Stream<Arguments> convertWhenClientAuthenticationMethodConvertedThenDeserializes() {\n\t\treturn Stream.of(Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),\n\t\t\t\tArguments.of(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),\n\t\t\t\tArguments.of(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),\n\t\t\t\tArguments.of(ClientAuthenticationMethod.NONE.getValue()), Arguments.of(\"custom_method\"));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationExceptionMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AuthenticationExceptionMixin}.\n *\n * @author Dennis Neufeld\n * @since 5.3.4\n */\n@SuppressWarnings(\"removal\")\npublic class OAuth2AuthenticationExceptionMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(\"[authorization_request_not_found]\", \"Authorization Request Not Found\", \"/foo/bar\"),\n\t\t\t\t\"Authorization Request Not Found\");\n\t\tString serializedJson = this.mapper.writeValueAsString(exception);\n\t\tString expected = asJson(exception);\n\t\tJSONAssert.assertEquals(expected, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(\"[authorization_request_not_found]\"));\n\t\tString serializedJson = this.mapper.writeValueAsString(exception);\n\t\tString expected = asJson(exception);\n\t\tJSONAssert.assertEquals(expected, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tString json = asJson(new OAuth2AuthenticationException(new OAuth2Error(\"[authorization_request_not_found]\")));\n\t\tassertThatExceptionOfType(JsonProcessingException.class)\n\t\t\t.isThrownBy(() -> new ObjectMapper().readValue(json, OAuth2AuthenticationException.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tOAuth2AuthenticationException expected = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(\"[authorization_request_not_found]\", \"Authorization Request Not Found\", \"/foo/bar\"),\n\t\t\t\t\"Authorization Request Not Found\");\n\t\tOAuth2AuthenticationException exception = this.mapper.readValue(asJson(expected),\n\t\t\t\tOAuth2AuthenticationException.class);\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getCause()).isNull();\n\t\tassertThat(exception.getMessage()).isEqualTo(expected.getMessage());\n\t\tOAuth2Error oauth2Error = exception.getError();\n\t\tassertThat(oauth2Error).isNotNull();\n\t\tassertThat(oauth2Error.getErrorCode()).isEqualTo(expected.getError().getErrorCode());\n\t\tassertThat(oauth2Error.getDescription()).isEqualTo(expected.getError().getDescription());\n\t\tassertThat(oauth2Error.getUri()).isEqualTo(expected.getError().getUri());\n\t}\n\n\t@Test\n\tpublic void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {\n\t\tOAuth2AuthenticationException expected = new OAuth2AuthenticationException(\n\t\t\t\tnew OAuth2Error(\"[authorization_request_not_found]\"));\n\t\tOAuth2AuthenticationException exception = this.mapper.readValue(asJson(expected),\n\t\t\t\tOAuth2AuthenticationException.class);\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getCause()).isNull();\n\t\tassertThat(exception.getMessage()).isNull();\n\t\tOAuth2Error oauth2Error = exception.getError();\n\t\tassertThat(oauth2Error).isNotNull();\n\t\tassertThat(oauth2Error.getErrorCode()).isEqualTo(expected.getError().getErrorCode());\n\t\tassertThat(oauth2Error.getDescription()).isNull();\n\t\tassertThat(oauth2Error.getUri()).isNull();\n\t}\n\n\tprivate String asJson(OAuth2AuthenticationException exception) {\n\t\tOAuth2Error error = exception.getError();\n\t\t// @formatter:off\n\t\treturn \"\\n{\"\n\t\t\t\t+ \"\\n  \\\"@class\\\": \\\"org.springframework.security.oauth2.core.OAuth2AuthenticationException\\\",\"\n\t\t\t\t+ \"\\n  \\\"error\\\":\"\n\t\t\t\t+ \"\\n  {\"\n\t\t\t\t+ \"\\n    \\\"@class\\\":\\\"org.springframework.security.oauth2.core.OAuth2Error\\\",\"\n\t\t\t\t+ \"\\n    \\\"errorCode\\\":\\\"\" + error.getErrorCode() + \"\\\",\"\n\t\t\t\t+ \"\\n    \\\"description\\\":\" + jsonStringOrNull(error.getDescription()) + \",\"\n\t\t\t\t+ \"\\n    \\\"uri\\\":\" + jsonStringOrNull(error.getUri())\n\t\t\t\t+ \"\\n  },\"\n\t\t\t\t+ \"\\n  \\\"detailMessage\\\":\" + jsonStringOrNull(exception.getMessage())\n\t\t\t\t+ \"\\n}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate String jsonStringOrNull(String input) {\n\t\treturn (input != null) ? \"\\\"\" + input + \"\\\"\" : \"null\";\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.DecimalUtils;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.TestOAuth2AuthenticationTokens;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AuthenticationTokenMixin}.\n *\n * @author Joe Grandja\n */\n@SuppressWarnings(\"removal\")\npublic class OAuth2AuthenticationTokenMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\n\t\t// see https://github.com/FasterXML/jackson-databind/issues/3052 for details\n\t\tthis.mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\t// OidcUser\n\t\tOAuth2AuthenticationToken authentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();\n\t\tString expectedJson = asJson(authentication);\n\t\tString json = this.mapper.writeValueAsString(authentication);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t\t// OAuth2User\n\t\tauthentication = TestOAuth2AuthenticationTokens.authenticated();\n\t\texpectedJson = asJson(authentication);\n\t\tjson = this.mapper.writeValueAsString(authentication);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {\n\t\tDefaultOidcUser principal = TestOidcUsers.create();\n\t\tprincipal = new DefaultOidcUser(principal.getAuthorities(), principal.getIdToken());\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, Collections.emptyList(),\n\t\t\t\t\"registration-id\");\n\t\tString expectedJson = asJson(authentication);\n\t\tString json = this.mapper.writeValueAsString(authentication);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tOAuth2AuthenticationToken authentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();\n\t\tString json = asJson(authentication);\n\t\tassertThatExceptionOfType(JsonProcessingException.class)\n\t\t\t.isThrownBy(() -> new ObjectMapper().readValue(json, OAuth2AuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\t// OidcUser\n\t\tOAuth2AuthenticationToken expectedAuthentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();\n\t\tString json = asJson(expectedAuthentication);\n\t\tOAuth2AuthenticationToken authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());\n\t\tassertThat(authentication.getAuthorizedClientRegistrationId())\n\t\t\t.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());\n\t\tDefaultOidcUser expectedOidcUser = (DefaultOidcUser) expectedAuthentication.getPrincipal();\n\t\tDefaultOidcUser oidcUser = (DefaultOidcUser) authentication.getPrincipal();\n\t\tassertThat(oidcUser.getAuthorities().containsAll(expectedOidcUser.getAuthorities())).isTrue();\n\t\tassertThat(oidcUser.getAttributes()).containsExactlyEntriesOf(expectedOidcUser.getAttributes());\n\t\tassertThat(oidcUser.getName()).isEqualTo(expectedOidcUser.getName());\n\t\tOidcIdToken expectedIdToken = expectedOidcUser.getIdToken();\n\t\tOidcIdToken idToken = oidcUser.getIdToken();\n\t\tassertThat(idToken.getTokenValue()).isEqualTo(expectedIdToken.getTokenValue());\n\t\tassertThat(idToken.getIssuedAt()).isEqualTo(expectedIdToken.getIssuedAt());\n\t\tassertThat(idToken.getExpiresAt()).isEqualTo(expectedIdToken.getExpiresAt());\n\t\tassertThat(idToken.getClaims()).containsExactlyEntriesOf(expectedIdToken.getClaims());\n\t\tOidcUserInfo expectedUserInfo = expectedOidcUser.getUserInfo();\n\t\tOidcUserInfo userInfo = oidcUser.getUserInfo();\n\t\tassertThat(userInfo.getClaims()).containsExactlyEntriesOf(expectedUserInfo.getClaims());\n\t\t// OAuth2User\n\t\texpectedAuthentication = TestOAuth2AuthenticationTokens.authenticated();\n\t\tjson = asJson(expectedAuthentication);\n\t\tauthentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);\n\t\tassertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());\n\t\tassertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());\n\t\tassertThat(authentication.getAuthorizedClientRegistrationId())\n\t\t\t.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());\n\t\tDefaultOAuth2User expectedOauth2User = (DefaultOAuth2User) expectedAuthentication.getPrincipal();\n\t\tDefaultOAuth2User oauth2User = (DefaultOAuth2User) authentication.getPrincipal();\n\t\tassertThat(oauth2User.getAuthorities().containsAll(expectedOauth2User.getAuthorities())).isTrue();\n\t\tassertThat(oauth2User.getAttributes()).containsExactlyEntriesOf(expectedOauth2User.getAttributes());\n\t\tassertThat(oauth2User.getName()).isEqualTo(expectedOauth2User.getName());\n\t}\n\n\t@Test\n\tpublic void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {\n\t\tDefaultOidcUser expectedPrincipal = TestOidcUsers.create();\n\t\texpectedPrincipal = new DefaultOidcUser(expectedPrincipal.getAuthorities(), expectedPrincipal.getIdToken());\n\t\tOAuth2AuthenticationToken expectedAuthentication = new OAuth2AuthenticationToken(expectedPrincipal,\n\t\t\t\tCollections.emptyList(), \"registration-id\");\n\t\tString json = asJson(expectedAuthentication);\n\t\tOAuth2AuthenticationToken authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);\n\t\tassertThat(authentication.getAuthorities()).isEmpty();\n\t\tassertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());\n\t\tassertThat(authentication.getAuthorizedClientRegistrationId())\n\t\t\t.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());\n\t\tDefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();\n\t\tassertThat(principal.getAuthorities().containsAll(expectedPrincipal.getAuthorities())).isTrue();\n\t\tassertThat(principal.getAttributes()).containsExactlyEntriesOf(expectedPrincipal.getAttributes());\n\t\tassertThat(principal.getName()).isEqualTo(expectedPrincipal.getName());\n\t\tOidcIdToken expectedIdToken = expectedPrincipal.getIdToken();\n\t\tOidcIdToken idToken = principal.getIdToken();\n\t\tassertThat(idToken.getTokenValue()).isEqualTo(expectedIdToken.getTokenValue());\n\t\tassertThat(idToken.getIssuedAt()).isEqualTo(expectedIdToken.getIssuedAt());\n\t\tassertThat(idToken.getExpiresAt()).isEqualTo(expectedIdToken.getExpiresAt());\n\t\tassertThat(idToken.getClaims()).containsExactlyEntriesOf(expectedIdToken.getClaims());\n\t\tassertThat(principal.getUserInfo()).isNull();\n\t}\n\n\tprivate static String asJson(OAuth2AuthenticationToken authentication) {\n\t\tString principalJson = (authentication.getPrincipal() instanceof DefaultOidcUser)\n\t\t\t\t? asJson((DefaultOidcUser) authentication.getPrincipal())\n\t\t\t\t: asJson((DefaultOAuth2User) authentication.getPrincipal());\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"  \\\"@class\\\": \\\"org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken\\\",\\n\" +\n\t\t\t\t\"  \\\"principal\\\": \" + principalJson + \",\\n\" +\n\t\t\t\t\"  \\\"authorities\\\": \" + asJson(authentication.getAuthorities(), \"java.util.Collections$UnmodifiableRandomAccessList\") + \",\\n\" +\n\t\t\t\t\"  \\\"authorizedClientRegistrationId\\\": \\\"\" + authentication.getAuthorizedClientRegistrationId() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"details\\\": null\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(DefaultOAuth2User oauth2User) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.core.user.DefaultOAuth2User\\\",\\n\" +\n\t\t\t\t\"    \\\"authorities\\\": \" + asJson(oauth2User.getAuthorities(), \"java.util.Collections$UnmodifiableSet\") + \",\\n\" +\n\t\t\t\t\"    \\\"attributes\\\": {\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\\n\" +\n\t\t\t\t\"      \\\"username\\\": \\\"user\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"nameAttributeKey\\\": \\\"username\\\"\\n\" +\n\t\t\t\t\"  }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(DefaultOidcUser oidcUser) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser\\\",\\n\" +\n\t\t\t\t\"    \\\"authorities\\\": \" + asJson(oidcUser.getAuthorities(), \"java.util.Collections$UnmodifiableSet\") + \",\\n\" +\n\t\t\t\t\"    \\\"idToken\\\": \" + asJson(oidcUser.getIdToken()) + \",\\n\" +\n\t\t\t\t\"    \\\"userInfo\\\": \" + asJson(oidcUser.getUserInfo()) + \",\\n\" +\n\t\t\t\t\"    \\\"nameAttributeKey\\\": \\\"\" + IdTokenClaimNames.SUB + \"\\\"\\n\" +\n\t\t\t\t\"  }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(Collection<? extends GrantedAuthority> authorities, String classTypeInfo) {\n\t\tOAuth2UserAuthority oauth2UserAuthority = null;\n\t\tOidcUserAuthority oidcUserAuthority = null;\n\t\tList<SimpleGrantedAuthority> simpleAuthorities = new ArrayList<>();\n\t\tfor (GrantedAuthority authority : authorities) {\n\t\t\tif (authority instanceof OidcUserAuthority) {\n\t\t\t\toidcUserAuthority = (OidcUserAuthority) authority;\n\t\t\t}\n\t\t\telse if (authority instanceof OAuth2UserAuthority) {\n\t\t\t\toauth2UserAuthority = (OAuth2UserAuthority) authority;\n\t\t\t}\n\t\t\telse if (authority instanceof SimpleGrantedAuthority) {\n\t\t\t\tsimpleAuthorities.add((SimpleGrantedAuthority) authority);\n\t\t\t}\n\t\t}\n\t\tString authoritiesJson = (oidcUserAuthority != null) ? asJson(oidcUserAuthority)\n\t\t\t\t: (oauth2UserAuthority != null) ? asJson(oauth2UserAuthority) : \"\";\n\t\tif (!simpleAuthorities.isEmpty()) {\n\t\t\tif (StringUtils.hasLength(authoritiesJson)) {\n\t\t\t\tauthoritiesJson += \",\";\n\t\t\t}\n\t\t\tauthoritiesJson += asJson(simpleAuthorities);\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"[\\n\" +\n\t\t\t\t\"      \\\"\" + classTypeInfo + \"\\\",\\n\" +\n\t\t\t\t\"      [\" + authoritiesJson + \"]\\n\" +\n\t\t\t\t\"    ]\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OAuth2UserAuthority oauth2UserAuthority) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"          \\\"@class\\\": \\\"org.springframework.security.oauth2.core.user.OAuth2UserAuthority\\\",\\n\" +\n\t\t\t\t\"          \\\"authority\\\": \\\"\" + oauth2UserAuthority.getAuthority() + \"\\\",\\n\" +\n\t\t\t\t\"          \\\"userNameAttributeName\\\": \\\"username\\\",\\n\" +\n\t\t\t\t\"          \\\"attributes\\\": {\\n\" +\n\t\t\t\t\"            \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\\n\" +\n\t\t\t\t\"            \\\"username\\\": \\\"user\\\"\\n\" +\n\t\t\t\t\"          }\\n\" +\n\t\t\t\t\"        }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OidcUserAuthority oidcUserAuthority) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"          \\\"@class\\\": \\\"org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority\\\",\\n\" +\n\t\t\t\t\"          \\\"authority\\\": \\\"\" + oidcUserAuthority.getAuthority() + \"\\\",\\n\" +\n\t\t\t\t\"          \\\"userNameAttributeName\\\": \\\"\" + oidcUserAuthority.getUserNameAttributeName() + \"\\\",\\n\" +\n\t\t\t\t\"          \\\"idToken\\\": \" + asJson(oidcUserAuthority.getIdToken()) + \",\\n\" +\n\t\t\t\t\"          \\\"userInfo\\\": \" + asJson(oidcUserAuthority.getUserInfo()) + \"\\n\" +\n\t\t\t\t\"        }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(List<SimpleGrantedAuthority> simpleAuthorities) {\n\t\t// @formatter:off\n\t\treturn simpleAuthorities.stream()\n\t\t\t\t.map((authority) -> \"{\\n\" +\n\t\t\t\t\t\t\"        \\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\\n\" +\n\t\t\t\t\t\t\"        \\\"authority\\\": \\\"\" + authority.getAuthority() + \"\\\"\\n\" +\n\t\t\t\t\t\t\"      }\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OidcIdToken idToken) {\n\t\tString aud = \"\";\n\t\tif (!CollectionUtils.isEmpty(idToken.getAudience())) {\n\t\t\taud = StringUtils.collectionToDelimitedString(idToken.getAudience(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"org.springframework.security.oauth2.core.oidc.OidcIdToken\\\",\\n\" +\n\t\t\t\t\"      \\\"tokenValue\\\": \\\"\" + idToken.getTokenValue() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"issuedAt\\\": \" + toString(idToken.getIssuedAt()) + \",\\n\" +\n\t\t\t\t\"      \\\"expiresAt\\\": \" + toString(idToken.getExpiresAt()) + \",\\n\" +\n\t\t\t\t\"      \\\"claims\\\": {\\n\" +\n\t\t\t\t\"        \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\\n\" +\n\t\t\t\t\"        \\\"iat\\\": [\\n\" +\n\t\t\t\t\"          \\\"java.time.Instant\\\",\\n\" +\n\t\t\t\t\"          \" + toString(idToken.getIssuedAt()) + \"\\n\" +\n\t\t\t\t\"        ],\\n\" +\n\t\t\t\t\"        \\\"exp\\\": [\\n\" +\n\t\t\t\t\"          \\\"java.time.Instant\\\",\\n\" +\n\t\t\t\t\"          \" + toString(idToken.getExpiresAt()) + \"\\n\" +\n\t\t\t\t\"        ],\\n\" +\n\t\t\t\t\"        \\\"sub\\\": \\\"\" + idToken.getSubject() + \"\\\",\\n\" +\n\t\t\t\t\"        \\\"iss\\\": \\\"\" + idToken.getIssuer() + \"\\\",\\n\" +\n\t\t\t\t\"        \\\"aud\\\": [\\n\" +\n\t\t\t\t\"          \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"          [\" + aud + \"]\\n\" +\n\t\t\t\t\"        ],\\n\" +\n\t\t\t\t\"        \\\"azp\\\": \\\"\" + idToken.getAuthorizedParty() + \"\\\"\\n\" +\n\t\t\t\t\"      }\\n\" +\n\t\t\t\t\"    }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OidcUserInfo userInfo) {\n\t\tif (userInfo == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"org.springframework.security.oauth2.core.oidc.OidcUserInfo\\\",\\n\" +\n\t\t\t\t\"      \\\"claims\\\": {\\n\" +\n\t\t\t\t\"        \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\\n\" +\n\t\t\t\t\"        \\\"sub\\\": \\\"\" + userInfo.getSubject() + \"\\\",\\n\" +\n\t\t\t\t\"        \\\"name\\\": \\\"\" + userInfo.getClaim(StandardClaimNames.NAME) + \"\\\"\\n\" +\n\t\t\t\t\"      }\\n\" +\n\t\t\t\t\"    }\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String toString(Instant instant) {\n\t\tif (instant == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn DecimalUtils.toBigDecimal(instant.getEpochSecond(), instant.getNano()).toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizationRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AuthorizationRequestMixin}.\n *\n * @author Joe Grandja\n */\n@SuppressWarnings(\"removal\")\npublic class OAuth2AuthorizationRequestMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\tprivate OAuth2AuthorizationRequest.Builder authorizationRequestBuilder;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\t// @formatter:off\n\t\tthis.authorizationRequestBuilder = TestOAuth2AuthorizationRequests.request()\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.additionalParameters(additionalParameters);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build();\n\t\tString expectedJson = asJson(authorizationRequest);\n\t\tString json = this.mapper.writeValueAsString(authorizationRequest);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder\n\t\t\t\t.scopes(null)\n\t\t\t\t.state(null)\n\t\t\t\t.additionalParameters(Map::clear)\n\t\t\t\t.attributes(Map::clear)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tString expectedJson = asJson(authorizationRequest);\n\t\tString json = this.mapper.writeValueAsString(authorizationRequest);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tString json = asJson(this.authorizationRequestBuilder.build());\n\t\tassertThatExceptionOfType(JsonProcessingException.class)\n\t\t\t.isThrownBy(() -> new ObjectMapper().readValue(json, OAuth2AuthorizationRequest.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tOAuth2AuthorizationRequest expectedAuthorizationRequest = this.authorizationRequestBuilder.build();\n\t\tString json = asJson(expectedAuthorizationRequest);\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.mapper.readValue(json, OAuth2AuthorizationRequest.class);\n\t\tassertThat(authorizationRequest.getAuthorizationUri())\n\t\t\t.isEqualTo(expectedAuthorizationRequest.getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(expectedAuthorizationRequest.getGrantType());\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(expectedAuthorizationRequest.getResponseType());\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(expectedAuthorizationRequest.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedAuthorizationRequest.getRedirectUri());\n\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(expectedAuthorizationRequest.getScopes());\n\t\tassertThat(authorizationRequest.getState()).isEqualTo(expectedAuthorizationRequest.getState());\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.containsExactlyEntriesOf(expectedAuthorizationRequest.getAdditionalParameters());\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(expectedAuthorizationRequest.getAuthorizationRequestUri());\n\t\tassertThat(authorizationRequest.getAttributes())\n\t\t\t.containsExactlyEntriesOf(expectedAuthorizationRequest.getAttributes());\n\t}\n\n\t@Test\n\tpublic void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest expectedAuthorizationRequest = this.authorizationRequestBuilder.scopes(null)\n\t\t\t\t.state(null)\n\t\t\t\t.additionalParameters(Map::clear)\n\t\t\t\t.attributes(Map::clear)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tString json = asJson(expectedAuthorizationRequest);\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.mapper.readValue(json, OAuth2AuthorizationRequest.class);\n\t\tassertThat(authorizationRequest.getAuthorizationUri())\n\t\t\t.isEqualTo(expectedAuthorizationRequest.getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(expectedAuthorizationRequest.getGrantType());\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(expectedAuthorizationRequest.getResponseType());\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(expectedAuthorizationRequest.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedAuthorizationRequest.getRedirectUri());\n\t\tassertThat(authorizationRequest.getScopes()).isEmpty();\n\t\tassertThat(authorizationRequest.getState()).isNull();\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).isEmpty();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(expectedAuthorizationRequest.getAuthorizationRequestUri());\n\t\tassertThat(authorizationRequest.getAttributes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void deserializeWhenInvalidAuthorizationGrantTypeThenThrowJsonParseException() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build();\n\t\tString json = asJson(authorizationRequest).replace(\"authorization_code\", \"client_credentials\");\n\t\tassertThatExceptionOfType(JsonParseException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(json, OAuth2AuthorizationRequest.class))\n\t\t\t.withMessageContaining(\"Invalid authorizationGrantType\");\n\t}\n\n\tprivate static String asJson(OAuth2AuthorizationRequest authorizationRequest) {\n\t\tString scopes = \"\";\n\t\tif (!CollectionUtils.isEmpty(authorizationRequest.getScopes())) {\n\t\t\tscopes = StringUtils.collectionToDelimitedString(authorizationRequest.getScopes(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\tString additionalParameters = \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\"\";\n\t\tif (!CollectionUtils.isEmpty(authorizationRequest.getAdditionalParameters())) {\n\t\t\tadditionalParameters += \",\" + authorizationRequest.getAdditionalParameters()\n\t\t\t\t.keySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((key) -> \"\\\"\" + key + \"\\\": \\\"\" + authorizationRequest.getAdditionalParameters().get(key) + \"\\\"\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\t\tString attributes = \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\"\";\n\t\tif (!CollectionUtils.isEmpty(authorizationRequest.getAttributes())) {\n\t\t\tattributes += \",\" + authorizationRequest.getAttributes()\n\t\t\t\t.keySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((key) -> \"\\\"\" + key + \"\\\": \\\"\" + authorizationRequest.getAttributes().get(key) + \"\\\"\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"  \\\"@class\\\": \\\"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\\\",\\n\" +\n\t\t\t\t\"  \\\"authorizationUri\\\": \\\"\" + authorizationRequest.getAuthorizationUri() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"authorizationGrantType\\\": {\\n\" +\n\t\t\t\t\"    \\\"value\\\": \\\"\" + authorizationRequest.getGrantType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"  },\\n\" +\n\t\t\t\t\"  \\\"responseType\\\": {\\n\" +\n\t\t\t\t\"    \\\"value\\\": \\\"\" + authorizationRequest.getResponseType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"  },\\n\" +\n\t\t\t\t\"  \\\"clientId\\\": \\\"\" + authorizationRequest.getClientId() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"redirectUri\\\": \\\"\" + authorizationRequest.getRedirectUri() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"scopes\\\": [\\n\" +\n\t\t\t\t\"    \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"    [\" + scopes + \"]\\n\" +\n\t\t\t\t\"  ],\\n\" +\n\t\t\t\t\"  \\\"state\\\": \" + ((authorizationRequest.getState() != null) ? \"\\\"\" + authorizationRequest.getState() + \"\\\"\" : \"null\") + \",\\n\" +\n\t\t\t\t\"  \\\"additionalParameters\\\": {\\n\" +\n\t\t\t\t\"    \" + additionalParameters + \"\\n\" +\n\t\t\t\t\"  },\\n\" +\n\t\t\t\t\"  \\\"authorizationRequestUri\\\": \\\"\" + authorizationRequest.getAuthorizationRequestUri() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"attributes\\\": {\\n\" +\n\t\t\t\t\"    \" + attributes + \"\\n\" +\n\t\t\t\t\"  }\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizedClientMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.time.Instant;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.DecimalUtils;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AuthorizedClientMixin}.\n *\n * @author Joe Grandja\n */\n@SuppressWarnings(\"removal\")\npublic class OAuth2AuthorizedClientMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\tprivate ClientRegistration.Builder clientRegistrationBuilder;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate OAuth2RefreshToken refreshToken;\n\n\tprivate String principalName;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t\tMap<String, Object> providerConfigurationMetadata = new LinkedHashMap<>();\n\t\tproviderConfigurationMetadata.put(\"config1\", \"value1\");\n\t\tproviderConfigurationMetadata.put(\"config2\", \"value2\");\n\t\t// @formatter:off\n\t\tthis.clientRegistrationBuilder = TestClientRegistrations.clientRegistration()\n\t\t\t\t.authorizationGrantType(new AuthorizationGrantType(\"custom-grant\"))\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.providerConfigurationMetadata(providerConfigurationMetadata);\n\t\t// @formatter:on\n\t\tthis.accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\tthis.principalName = \"principal-name\";\n\t}\n\n\t@Test\n\tpublic void serializeWhenMixinRegisteredThenSerializes() throws Exception {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),\n\t\t\t\tthis.principalName, this.accessToken, this.refreshToken);\n\t\tString expectedJson = asJson(authorizedClient);\n\t\tString json = this.mapper.writeValueAsString(authorizedClient);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.clientName(null)\n\t\t\t\t.userInfoUri(null)\n\t\t\t\t.userNameAttributeName(null)\n\t\t\t\t.jwkSetUri(null)\n\t\t\t\t.issuerUri(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, this.principalName,\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tString expectedJson = asJson(authorizedClient);\n\t\tString json = this.mapper.writeValueAsString(authorizedClient);\n\t\tJSONAssert.assertEquals(expectedJson, json, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),\n\t\t\t\tthis.principalName, this.accessToken);\n\t\tString json = asJson(authorizedClient);\n\t\tassertThatExceptionOfType(JsonProcessingException.class)\n\t\t\t.isThrownBy(() -> new ObjectMapper().readValue(json, OAuth2AuthorizedClient.class));\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {\n\t\tClientRegistration expectedClientRegistration = this.clientRegistrationBuilder.build();\n\t\tOAuth2AccessToken expectedAccessToken = this.accessToken;\n\t\tOAuth2RefreshToken expectedRefreshToken = this.refreshToken;\n\t\tOAuth2AuthorizedClient expectedAuthorizedClient = new OAuth2AuthorizedClient(expectedClientRegistration,\n\t\t\t\tthis.principalName, expectedAccessToken, expectedRefreshToken);\n\t\tString json = asJson(expectedAuthorizedClient);\n\t\tOAuth2AuthorizedClient authorizedClient = this.mapper.readValue(json, OAuth2AuthorizedClient.class);\n\t\tClientRegistration clientRegistration = authorizedClient.getClientRegistration();\n\t\tassertThat(clientRegistration.getRegistrationId()).isEqualTo(expectedClientRegistration.getRegistrationId());\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(expectedClientRegistration.getClientId());\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(expectedClientRegistration.getClientSecret());\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistration.getClientAuthenticationMethod());\n\t\tassertThat(clientRegistration.getAuthorizationGrantType())\n\t\t\t.isEqualTo(expectedClientRegistration.getAuthorizationGrantType());\n\t\tassertThat(clientRegistration.getRedirectUri()).isEqualTo(expectedClientRegistration.getRedirectUri());\n\t\tassertThat(clientRegistration.getScopes()).isEqualTo(expectedClientRegistration.getScopes());\n\t\tassertThat(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getAuthorizationUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getTokenUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getTokenUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(\n\t\t\t\texpectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName());\n\t\tassertThat(clientRegistration.getProviderDetails().getJwkSetUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getJwkSetUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getIssuerUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getIssuerUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata())\n\t\t\t.containsExactlyEntriesOf(clientRegistration.getProviderDetails().getConfigurationMetadata());\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(expectedClientRegistration.getClientName());\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expectedAuthorizedClient.getPrincipalName());\n\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(expectedAccessToken.getTokenType());\n\t\tassertThat(accessToken.getScopes()).isEqualTo(expectedAccessToken.getScopes());\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(expectedAccessToken.getTokenValue());\n\t\tassertThat(accessToken.getIssuedAt()).isEqualTo(expectedAccessToken.getIssuedAt());\n\t\tassertThat(accessToken.getExpiresAt()).isEqualTo(expectedAccessToken.getExpiresAt());\n\t\tOAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken();\n\t\tassertThat(refreshToken.getTokenValue()).isEqualTo(expectedRefreshToken.getTokenValue());\n\t\tassertThat(refreshToken.getIssuedAt()).isEqualTo(expectedRefreshToken.getIssuedAt());\n\t\tassertThat(refreshToken.getExpiresAt()).isEqualTo(expectedRefreshToken.getExpiresAt());\n\t}\n\n\t@Test\n\tpublic void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {\n\t\t// @formatter:off\n\t\tClientRegistration expectedClientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.clientName(null)\n\t\t\t\t.userInfoUri(null)\n\t\t\t\t.userNameAttributeName(null)\n\t\t\t\t.jwkSetUri(null)\n\t\t\t\t.issuerUri(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AccessToken expectedAccessToken = TestOAuth2AccessTokens.noScopes();\n\t\tOAuth2AuthorizedClient expectedAuthorizedClient = new OAuth2AuthorizedClient(expectedClientRegistration,\n\t\t\t\tthis.principalName, expectedAccessToken);\n\t\tString json = asJson(expectedAuthorizedClient);\n\t\tOAuth2AuthorizedClient authorizedClient = this.mapper.readValue(json, OAuth2AuthorizedClient.class);\n\t\tClientRegistration clientRegistration = authorizedClient.getClientRegistration();\n\t\tassertThat(clientRegistration.getRegistrationId()).isEqualTo(expectedClientRegistration.getRegistrationId());\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(expectedClientRegistration.getClientId());\n\t\tassertThat(clientRegistration.getClientSecret()).isEmpty();\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistration.getClientAuthenticationMethod());\n\t\tassertThat(clientRegistration.getAuthorizationGrantType())\n\t\t\t.isEqualTo(expectedClientRegistration.getAuthorizationGrantType());\n\t\tassertThat(clientRegistration.getRedirectUri()).isEqualTo(expectedClientRegistration.getRedirectUri());\n\t\tassertThat(clientRegistration.getScopes()).isEqualTo(expectedClientRegistration.getScopes());\n\t\tassertThat(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getAuthorizationUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getTokenUri())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getTokenUri());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri()).isNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())\n\t\t\t.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());\n\t\tassertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getJwkSetUri()).isNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(clientRegistration.getRegistrationId());\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(expectedAuthorizedClient.getPrincipalName());\n\t\tOAuth2AccessToken accessToken = authorizedClient.getAccessToken();\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(expectedAccessToken.getTokenType());\n\t\tassertThat(accessToken.getScopes()).isEmpty();\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(expectedAccessToken.getTokenValue());\n\t\tassertThat(accessToken.getIssuedAt()).isEqualTo(expectedAccessToken.getIssuedAt());\n\t\tassertThat(accessToken.getExpiresAt()).isEqualTo(expectedAccessToken.getExpiresAt());\n\t\tassertThat(authorizedClient.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tvoid deserializeWhenClientSettingsPropertyDoesNotExistThenDefaulted() throws JsonProcessingException {\n\t\t// ClientRegistration.clientSettings was added later, so old values will be\n\t\t// serialized without that property\n\t\t// this test checks for passivity\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.build();\n\t\tClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();\n\t\tClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();\n\t\tString scopes = \"\";\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tscopes = StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\tString configurationMetadata = \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\"\";\n\t\tif (!CollectionUtils.isEmpty(providerDetails.getConfigurationMetadata())) {\n\t\t\tconfigurationMetadata += \",\" + providerDetails.getConfigurationMetadata()\n\t\t\t\t.keySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((key) -> \"\\\"\" + key + \"\\\": \\\"\" + providerDetails.getConfigurationMetadata().get(key) + \"\\\"\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\t\t// @formatter:off\n\t\tString json = \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration\\\",\\n\" +\n\t\t\t\t\"    \\\"registrationId\\\": \\\"\" + clientRegistration.getRegistrationId() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientId\\\": \\\"\" + clientRegistration.getClientId() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientSecret\\\": \\\"\" + clientRegistration.getClientSecret() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientAuthenticationMethod\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + clientRegistration.getClientAuthenticationMethod().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"authorizationGrantType\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + clientRegistration.getAuthorizationGrantType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"redirectUri\\\": \\\"\" + clientRegistration.getRedirectUri() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"scopes\\\": [\\n\" +\n\t\t\t\t\"      \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"      [\" + scopes + \"]\\n\" +\n\t\t\t\t\"    ],\\n\" +\n\t\t\t\t\"    \\\"providerDetails\\\": {\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\\\",\\n\" +\n\t\t\t\t\"      \\\"authorizationUri\\\": \\\"\" + providerDetails.getAuthorizationUri() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"tokenUri\\\": \\\"\" + providerDetails.getTokenUri() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"userInfoEndpoint\\\": {\\n\" +\n\t\t\t\t\"        \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\\\",\\n\" +\n\t\t\t\t\"        \\\"uri\\\": \" + ((userInfoEndpoint.getUri() != null) ? \"\\\"\" + userInfoEndpoint.getUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"        \\\"authenticationMethod\\\": {\\n\" +\n\t\t\t\t\"          \\\"value\\\": \\\"\" + userInfoEndpoint.getAuthenticationMethod().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"        },\\n\" +\n\t\t\t\t\"        \\\"userNameAttributeName\\\": \" + ((userInfoEndpoint.getUserNameAttributeName() != null) ? \"\\\"\" + userInfoEndpoint.getUserNameAttributeName() + \"\\\"\" : null) + \"\\n\" +\n\t\t\t\t\"      },\\n\" +\n\t\t\t\t\"      \\\"jwkSetUri\\\": \" + ((providerDetails.getJwkSetUri() != null) ? \"\\\"\" + providerDetails.getJwkSetUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"      \\\"issuerUri\\\": \" + ((providerDetails.getIssuerUri() != null) ? \"\\\"\" + providerDetails.getIssuerUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"      \\\"configurationMetadata\\\": {\\n\" +\n\t\t\t\t\"        \" + configurationMetadata + \"\\n\" +\n\t\t\t\t\"      }\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"clientName\\\": \\\"\" + clientRegistration.getClientName() + \"\\\"\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t\t// validate the test input\n\t\tassertThat(json).doesNotContain(\"clientSettings\");\n\t\tClientRegistration registration = this.mapper.readValue(json, ClientRegistration.class);\n\t\t// the default value of requireProofKey is false\n\t\tassertThat(registration.getClientSettings().isRequireProofKey()).isFalse();\n\t}\n\n\tprivate static String asJson(OAuth2AuthorizedClient authorizedClient) {\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"  \\\"@class\\\": \\\"org.springframework.security.oauth2.client.OAuth2AuthorizedClient\\\",\\n\" +\n\t\t\t\t\"  \\\"clientRegistration\\\": \" + asJson(authorizedClient.getClientRegistration()) + \",\\n\" +\n\t\t\t\t\"  \\\"principalName\\\": \\\"\" + authorizedClient.getPrincipalName() + \"\\\",\\n\" +\n\t\t\t\t\"  \\\"accessToken\\\": \" + asJson(authorizedClient.getAccessToken()) + \",\\n\" +\n\t\t\t\t\"  \\\"refreshToken\\\": \" + asJson(authorizedClient.getRefreshToken()) + \"\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(ClientRegistration clientRegistration) {\n\t\tClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();\n\t\tClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();\n\t\tString scopes = \"\";\n\t\tif (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {\n\t\t\tscopes = StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\tString configurationMetadata = \"\\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\"\";\n\t\tif (!CollectionUtils.isEmpty(providerDetails.getConfigurationMetadata())) {\n\t\t\tconfigurationMetadata += \",\" + providerDetails.getConfigurationMetadata()\n\t\t\t\t.keySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((key) -> \"\\\"\" + key + \"\\\": \\\"\" + providerDetails.getConfigurationMetadata().get(key) + \"\\\"\")\n\t\t\t\t.collect(Collectors.joining(\",\"));\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration\\\",\\n\" +\n\t\t\t\t\"    \\\"registrationId\\\": \\\"\" + clientRegistration.getRegistrationId() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientId\\\": \\\"\" + clientRegistration.getClientId() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientSecret\\\": \\\"\" + clientRegistration.getClientSecret() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientAuthenticationMethod\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + clientRegistration.getClientAuthenticationMethod().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"authorizationGrantType\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + clientRegistration.getAuthorizationGrantType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"redirectUri\\\": \\\"\" + clientRegistration.getRedirectUri() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"scopes\\\": [\\n\" +\n\t\t\t\t\"      \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"      [\" + scopes + \"]\\n\" +\n\t\t\t\t\"    ],\\n\" +\n\t\t\t\t\"    \\\"providerDetails\\\": {\\n\" +\n\t\t\t\t\"      \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\\\",\\n\" +\n\t\t\t\t\"      \\\"authorizationUri\\\": \\\"\" + providerDetails.getAuthorizationUri() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"tokenUri\\\": \\\"\" + providerDetails.getTokenUri() + \"\\\",\\n\" +\n\t\t\t\t\"      \\\"userInfoEndpoint\\\": {\\n\" +\n\t\t\t\t\"        \\\"@class\\\": \\\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\\\",\\n\" +\n\t\t\t\t\"        \\\"uri\\\": \" + ((userInfoEndpoint.getUri() != null) ? \"\\\"\" + userInfoEndpoint.getUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"        \\\"authenticationMethod\\\": {\\n\" +\n\t\t\t\t\"          \\\"value\\\": \\\"\" + userInfoEndpoint.getAuthenticationMethod().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"        },\\n\" +\n\t\t\t\t\"        \\\"userNameAttributeName\\\": \" + ((userInfoEndpoint.getUserNameAttributeName() != null) ? \"\\\"\" + userInfoEndpoint.getUserNameAttributeName() + \"\\\"\" : null) + \"\\n\" +\n\t\t\t\t\"      },\\n\" +\n\t\t\t\t\"      \\\"jwkSetUri\\\": \" + ((providerDetails.getJwkSetUri() != null) ? \"\\\"\" + providerDetails.getJwkSetUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"      \\\"issuerUri\\\": \" + ((providerDetails.getIssuerUri() != null) ? \"\\\"\" + providerDetails.getIssuerUri() + \"\\\"\" : null) + \",\\n\" +\n\t\t\t\t\"      \\\"configurationMetadata\\\": {\\n\" +\n\t\t\t\t\"        \" + configurationMetadata + \"\\n\" +\n\t\t\t\t\"      }\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"clientName\\\": \\\"\" + clientRegistration.getClientName() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"clientSettings\\\": {\\n\" +\n\t\t\t\t\"      \\\"requireProofKey\\\": \" + clientRegistration.getClientSettings().isRequireProofKey() + \"\\n\" +\n\t\t\t\t\"    }\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OAuth2AccessToken accessToken) {\n\t\tString scopes = \"\";\n\t\tif (!CollectionUtils.isEmpty(accessToken.getScopes())) {\n\t\t\tscopes = StringUtils.collectionToDelimitedString(accessToken.getScopes(), \",\", \"\\\"\", \"\\\"\");\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.core.OAuth2AccessToken\\\",\\n\" +\n\t\t\t\t\"    \\\"tokenType\\\": {\\n\" +\n\t\t\t\t\"      \\\"value\\\": \\\"\" + accessToken.getTokenType().getValue() + \"\\\"\\n\" +\n\t\t\t\t\"    },\\n\" +\n\t\t\t\t\"    \\\"tokenValue\\\": \\\"\" + accessToken.getTokenValue() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"issuedAt\\\": \" + toString(accessToken.getIssuedAt()) + \",\\n\" +\n\t\t\t\t\"    \\\"expiresAt\\\": \" + toString(accessToken.getExpiresAt()) + \",\\n\" +\n\t\t\t\t\"    \\\"scopes\\\": [\\n\" +\n\t\t\t\t\"      \\\"java.util.Collections$UnmodifiableSet\\\",\\n\" +\n\t\t\t\t\"      [\" + scopes + \"]\\n\" +\n\t\t\t\t\"    ]\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String asJson(OAuth2RefreshToken refreshToken) {\n\t\tif (refreshToken == null) {\n\t\t\treturn null;\n\t\t}\n\t\t// @formatter:off\n\t\treturn \"{\\n\" +\n\t\t\t\t\"    \\\"@class\\\": \\\"org.springframework.security.oauth2.core.OAuth2RefreshToken\\\",\\n\" +\n\t\t\t\t\"    \\\"tokenValue\\\": \\\"\" + refreshToken.getTokenValue() + \"\\\",\\n\" +\n\t\t\t\t\"    \\\"issuedAt\\\": \" + toString(refreshToken.getIssuedAt()) + \",\\n\" +\n\t\t\t\t\"    \\\"expiresAt\\\": \" + toString(refreshToken.getExpiresAt()) + \"\\n\" +\n\t\t\t\t\"}\";\n\t\t// @formatter:on\n\t}\n\n\tprivate static String toString(Instant instant) {\n\t\tif (instant == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn DecimalUtils.toBigDecimal(instant.getEpochSecond(), instant.getNano()).toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.jackson2;\n\nimport java.util.stream.Stream;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.node.JsonNodeFactory;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\nimport com.fasterxml.jackson.databind.util.StdConverter;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\npublic class StdConvertersTests {\n\n\tprivate final StdConverter<JsonNode, ClientAuthenticationMethod> clientAuthenticationMethodConverter = new StdConverters.ClientAuthenticationMethodConverter();\n\n\t@ParameterizedTest\n\t@MethodSource(\"convertWhenClientAuthenticationMethodConvertedThenDeserializes\")\n\tvoid convertWhenClientAuthenticationMethodConvertedThenDeserializes(String clientAuthenticationMethod) {\n\t\tObjectNode jsonNode = JsonNodeFactory.instance.objectNode();\n\t\tjsonNode.put(\"value\", clientAuthenticationMethod);\n\t\tClientAuthenticationMethod actual = this.clientAuthenticationMethodConverter.convert(jsonNode);\n\t\tassertThat(actual.getValue()).isEqualTo(clientAuthenticationMethod);\n\t}\n\n\tstatic Stream<Arguments> convertWhenClientAuthenticationMethodConvertedThenDeserializes() {\n\t\treturn Stream.of(Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),\n\t\t\t\tArguments.of(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),\n\t\t\t\tArguments.of(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),\n\t\t\t\tArguments.of(ClientAuthenticationMethod.NONE.getValue()), Arguments.of(\"custom_method\"));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyCollection;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OidcAuthorizationCodeAuthenticationProvider}.\n *\n * @author Joe Grandja\n * @author Mark Heckler\n */\npublic class OidcAuthorizationCodeAuthenticationProviderTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizationRequest authorizationRequest;\n\n\tprivate OAuth2AuthorizationResponse authorizationResponse;\n\n\tprivate OAuth2AuthorizationExchange authorizationExchange;\n\n\tprivate OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\tprivate OAuth2AccessTokenResponse accessTokenResponse;\n\n\tprivate OAuth2UserService<OidcUserRequest, OidcUser> userService;\n\n\tprivate OidcAuthorizationCodeAuthenticationProvider authenticationProvider;\n\n\tprivate StringKeyGenerator secureKeyGenerator = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\tprivate String nonceHash;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().clientId(\"client1\").build();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\ttry {\n\t\t\tString nonce = this.secureKeyGenerator.generateKey();\n\t\t\tthis.nonceHash = OidcAuthorizationCodeAuthenticationProvider.createHash(nonce);\n\t\t\tattributes.put(OidcParameterNames.NONCE, nonce);\n\t\t\tadditionalParameters.put(OidcParameterNames.NONCE, this.nonceHash);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t}\n\t\t// @formatter:off\n\t\tthis.authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t\t.scope(\"openid\", \"profile\", \"email\")\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\tthis.authorizationResponse = TestOAuth2AuthorizationResponses.success()\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tthis.authorizationResponse);\n\t\tthis.accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);\n\t\tthis.accessTokenResponse = this.accessTokenSuccessResponse();\n\t\tthis.userService = mock(OAuth2UserService.class);\n\t\tthis.authenticationProvider = new OidcAuthorizationCodeAuthenticationProvider(this.accessTokenResponseClient,\n\t\t\t\tthis.userService);\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(this.accessTokenResponse);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAccessTokenResponseClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcAuthorizationCodeAuthenticationProvider(null, this.userService));\n\t}\n\n\t@Test\n\tpublic void constructorWhenUserServiceIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcAuthorizationCodeAuthenticationProvider(this.accessTokenResponseClient, null));\n\t}\n\n\t@Test\n\tpublic void setJwtDecoderFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authenticationProvider.setJwtDecoderFactory(null));\n\t}\n\n\t@Test\n\tpublic void setAuthoritiesMapperWhenAuthoritiesMapperIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authenticationProvider.setAuthoritiesMapper(null));\n\t}\n\n\t@Test\n\tpublic void supportsWhenTypeOAuth2LoginAuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.authenticationProvider.supports(OAuth2LoginAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationRequestDoesNotContainOpenidScopeThenReturnNull() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tthis.authorizationResponse);\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, authorizationExchange));\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationErrorResponseThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.error()\n\t\t\t\t.errorCode(OAuth2ErrorCodes.INVALID_SCOPE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider\n\t\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, authorizationExchange)))\n\t\t\t.withMessageContaining(OAuth2ErrorCodes.INVALID_SCOPE);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthorizationResponseStateNotEqualAuthorizationRequestStateThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.success()\n\t\t\t\t.state(\"89012\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(this.authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider\n\t\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, authorizationExchange)))\n\t\t\t.withMessageContaining(\"invalid_state_parameter\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenTokenResponseDoesNotContainIdTokenThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withResponse(this.accessTokenSuccessResponse())\n\t\t\t\t.additionalParameters(Collections.emptyMap())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider\n\t\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange)))\n\t\t\t.withMessageContaining(\"invalid_id_token\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenJwkSetUriNotSetThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.jwkSetUri(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider\n\t\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(clientRegistration, this.authorizationExchange)))\n\t\t\t.withMessageContaining(\"missing_signature_verifier\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenValidationErrorThenThrowOAuth2AuthenticationException() {\n\t\tJwtDecoder jwtDecoder = mock(JwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(anyString())).willThrow(new JwtException(\"ID Token Validation Error\"));\n\t\tthis.authenticationProvider.setJwtDecoderFactory((registration) -> jwtDecoder);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider\n\t\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange)))\n\t\t\t.withMessageContaining(\"[invalid_id_token] ID Token Validation Error\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenInvalidNonceThenThrowOAuth2AuthenticationException() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://provider.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"subject1\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client1\", \"client2\"));\n\t\tclaims.put(IdTokenClaimNames.AZP, \"client1\");\n\t\tclaims.put(IdTokenClaimNames.NONCE, \"invalid-nonce-hash\");\n\t\tthis.setUpIdToken(claims);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider\n\t\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange)))\n\t\t\t.withMessageContaining(\"[invalid_nonce]\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenLoginSuccessThenReturnAuthentication() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://provider.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"subject1\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client1\", \"client2\"));\n\t\tclaims.put(IdTokenClaimNames.AZP, \"client1\");\n\t\tclaims.put(IdTokenClaimNames.NONCE, this.nonceHash);\n\t\tthis.setUpIdToken(claims);\n\t\tOidcUser principal = mock(OidcUser.class);\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tgiven(principal.getAuthorities()).willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> authorities);\n\t\tgiven(this.userService.loadUser(any())).willReturn(principal);\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange));\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"\");\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(authorities);\n\t\tassertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);\n\t\tassertThat(authentication.getAccessToken()).isEqualTo(this.accessTokenResponse.getAccessToken());\n\t\tassertThat(authentication.getRefreshToken()).isEqualTo(this.accessTokenResponse.getRefreshToken());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthoritiesMapperSetThenReturnMappedAuthorities() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://provider.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"subject1\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client1\", \"client2\"));\n\t\tclaims.put(IdTokenClaimNames.AZP, \"client1\");\n\t\tclaims.put(IdTokenClaimNames.NONCE, this.nonceHash);\n\t\tthis.setUpIdToken(claims);\n\t\tOidcUser principal = mock(OidcUser.class);\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tgiven(principal.getAuthorities()).willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> authorities);\n\t\tgiven(this.userService.loadUser(any())).willReturn(principal);\n\t\tList<GrantedAuthority> mappedAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_OIDC_USER\");\n\t\tGrantedAuthoritiesMapper authoritiesMapper = mock(GrantedAuthoritiesMapper.class);\n\t\tgiven(authoritiesMapper.mapAuthorities(anyCollection()))\n\t\t\t.willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> mappedAuthorities);\n\t\tthis.authenticationProvider.setAuthoritiesMapper(authoritiesMapper);\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) this.authenticationProvider\n\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange));\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(mappedAuthorities);\n\t}\n\n\t// gh-5368\n\t@Test\n\tpublic void authenticateWhenTokenSuccessResponseThenAdditionalParametersAddedToUserRequest() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://provider.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"subject1\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client1\", \"client2\"));\n\t\tclaims.put(IdTokenClaimNames.AZP, \"client1\");\n\t\tclaims.put(IdTokenClaimNames.NONCE, this.nonceHash);\n\t\tthis.setUpIdToken(claims);\n\t\tOidcUser principal = mock(OidcUser.class);\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tgiven(principal.getAuthorities()).willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> authorities);\n\t\tArgumentCaptor<OidcUserRequest> userRequestArgCaptor = ArgumentCaptor.forClass(OidcUserRequest.class);\n\t\tgiven(this.userService.loadUser(userRequestArgCaptor.capture())).willReturn(principal);\n\t\tthis.authenticationProvider\n\t\t\t.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange));\n\t\tassertThat(userRequestArgCaptor.getValue().getAdditionalParameters())\n\t\t\t.containsAllEntriesOf(this.accessTokenResponse.getAdditionalParameters());\n\t}\n\n\tprivate void setUpIdToken(Map<String, Object> claims) {\n\t\tJwt idToken = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\tJwtDecoder jwtDecoder = mock(JwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(anyString())).willReturn(idToken);\n\t\tthis.authenticationProvider.setJwtDecoderFactory((registration) -> jwtDecoder);\n\t}\n\n\tprivate OAuth2AccessTokenResponse accessTokenSuccessResponse() {\n\t\tInstant expiresAt = Instant.now().plusSeconds(5);\n\t\tSet<String> scopes = new LinkedHashSet<>(Arrays.asList(\"openid\", \"profile\", \"email\"));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, \"id-token\");\n\t\t// @formatter:off\n\t\treturn OAuth2AccessTokenResponse.withToken(\"access-token-1234\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(expiresAt.getEpochSecond())\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.refreshToken(\"refresh-token-1234\")\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.stubbing.Answer;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyCollection;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @author Joe Grandja\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class OidcAuthorizationCodeReactiveAuthenticationManagerTests {\n\n\t@Mock\n\tprivate ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService;\n\n\t@Mock\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;\n\n\t@Mock\n\tprivate ReactiveJwtDecoder jwtDecoder;\n\n\t// @formatter:off\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration()\n\t\t\t.scope(\"openid\");\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate OAuth2AuthorizationResponse.Builder authorizationResponseBldr = OAuth2AuthorizationResponse.success(\"code\")\n\t\t\t.state(\"state\");\n\t// @formatter:on\n\n\tprivate OidcIdToken idToken = TestOidcIdTokens.idToken().build();\n\n\tprivate OidcAuthorizationCodeReactiveAuthenticationManager manager;\n\n\tprivate StringKeyGenerator secureKeyGenerator = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\tprivate String nonceHash;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.manager = new OidcAuthorizationCodeReactiveAuthenticationManager(this.accessTokenResponseClient,\n\t\t\t\tthis.userService);\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullAccessTokenResponseClientThenIllegalArgumentException() {\n\t\tthis.accessTokenResponseClient = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcAuthorizationCodeReactiveAuthenticationManager(this.accessTokenResponseClient,\n\t\t\t\t\tthis.userService));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullUserServiceThenIllegalArgumentException() {\n\t\tthis.userService = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcAuthorizationCodeReactiveAuthenticationManager(this.accessTokenResponseClient,\n\t\t\t\t\tthis.userService));\n\t}\n\n\t@Test\n\tpublic void setJwtDecoderFactoryWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.manager.setJwtDecoderFactory(null));\n\t}\n\n\t@Test\n\tpublic void setAuthoritiesMapperWhenAuthoritiesMapperIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.manager.setAuthoritiesMapper(null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNoSubscriptionThenDoesNothing() {\n\t\t// we didn't do anything because it should cause a ClassCastException (as verified\n\t\t// below)\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"a\", \"b\");\n\t\tthis.manager.authenticate(token);\n\t\tassertThatExceptionOfType(Throwable.class).isThrownBy(() -> this.manager.authenticate(token).block());\n\t}\n\n\t@Test\n\tpublic void authenticationWhenNotOidcThenEmpty() {\n\t\tthis.registration.scope(\"notopenid\");\n\t\tassertThat(this.manager.authenticate(loginToken()).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticationWhenErrorThenOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tthis.authorizationResponseBldr = OAuth2AuthorizationResponse.error(\"error\")\n\t\t\t\t.state(\"state\");\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(loginToken()).block());\n\t}\n\n\t@Test\n\tpublic void authenticationWhenStateDoesNotMatchThenOAuth2AuthenticationException() {\n\t\tthis.authorizationResponseBldr.state(\"notmatch\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(loginToken()).block());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenValidationErrorThenOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"foo\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.additionalParameters(Collections.singletonMap(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tgiven(this.jwtDecoder.decode(any())).willThrow(new JwtException(\"ID Token Validation Error\"));\n\t\tthis.manager.setJwtDecoderFactory((c) -> this.jwtDecoder);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(loginToken()).block())\n\t\t\t.withMessageContaining(\"[invalid_id_token] ID Token Validation Error\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIdTokenInvalidNonceThenOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"foo\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.additionalParameters(\n\t\t\t\t\t\tCollections.singletonMap(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = loginToken();\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://issuer.example.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"sub\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client-id\"));\n\t\tclaims.put(IdTokenClaimNames.NONCE, \"invalid-nonce-hash\");\n\t\tJwt idToken = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tgiven(this.jwtDecoder.decode(any())).willReturn(Mono.just(idToken));\n\t\tthis.manager.setJwtDecoderFactory((c) -> this.jwtDecoder);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(authorizationCodeAuthentication).block())\n\t\t\t.withMessageContaining(\"[invalid_nonce]\");\n\t}\n\n\t@Test\n\tpublic void authenticationWhenOAuth2UserNotFoundThenEmpty() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"foo\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.additionalParameters(Collections.singletonMap(OidcParameterNames.ID_TOKEN,\n\t\t\t\t\t\t\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = loginToken();\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://issuer.example.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"rob\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client-id\"));\n\t\tclaims.put(IdTokenClaimNames.NONCE, this.nonceHash);\n\t\tJwt idToken = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tgiven(this.userService.loadUser(any())).willReturn(Mono.empty());\n\t\tgiven(this.jwtDecoder.decode(any())).willReturn(Mono.just(idToken));\n\t\tthis.manager.setJwtDecoderFactory((c) -> this.jwtDecoder);\n\t\tassertThat(this.manager.authenticate(authorizationCodeAuthentication).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticationWhenOAuth2UserFoundThenSuccess() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withToken(\"foo\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.additionalParameters(\n\t\t\t\t\t\tCollections.singletonMap(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = loginToken();\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://issuer.example.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"rob\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client-id\"));\n\t\tclaims.put(IdTokenClaimNames.NONCE, this.nonceHash);\n\t\tJwt idToken = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tDefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), this.idToken);\n\t\tgiven(this.userService.loadUser(any())).willReturn(Mono.just(user));\n\t\tgiven(this.jwtDecoder.decode(any())).willReturn(Mono.just(idToken));\n\t\tthis.manager.setJwtDecoderFactory((c) -> this.jwtDecoder);\n\t\tOAuth2LoginAuthenticationToken result = (OAuth2LoginAuthenticationToken) this.manager\n\t\t\t.authenticate(authorizationCodeAuthentication)\n\t\t\t.block();\n\t\tassertThat(result.getPrincipal()).isEqualTo(user);\n\t\tassertThat(result.getAuthorities()).containsOnlyElementsOf(user.getAuthorities());\n\t\tassertThat(result.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticationWhenRefreshTokenThenRefreshTokenInAuthorizedClient() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withToken(\"foo\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.additionalParameters(\n\t\t\t\t\t\tCollections.singletonMap(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue()))\n\t\t\t\t.refreshToken(\"refresh-token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = loginToken();\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://issuer.example.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"rob\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client-id\"));\n\t\tclaims.put(IdTokenClaimNames.NONCE, this.nonceHash);\n\t\tJwt idToken = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tDefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), this.idToken);\n\t\tgiven(this.userService.loadUser(any())).willReturn(Mono.just(user));\n\t\tgiven(this.jwtDecoder.decode(any())).willReturn(Mono.just(idToken));\n\t\tthis.manager.setJwtDecoderFactory((c) -> this.jwtDecoder);\n\t\tOAuth2LoginAuthenticationToken result = (OAuth2LoginAuthenticationToken) this.manager\n\t\t\t.authenticate(authorizationCodeAuthentication)\n\t\t\t.block();\n\t\tassertThat(result.getPrincipal()).isEqualTo(user);\n\t\tassertThat(result.getAuthorities()).containsOnlyElementsOf(user.getAuthorities());\n\t\tassertThat(result.isAuthenticated()).isTrue();\n\t\tassertThat(result.getRefreshToken().getTokenValue()).isNotNull();\n\t}\n\n\t// gh-5368\n\t@Test\n\tpublic void authenticateWhenTokenSuccessResponseThenAdditionalParametersAddedToUserRequest() {\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue());\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withToken(\"foo\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = loginToken();\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://issuer.example.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"rob\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Arrays.asList(clientRegistration.getClientId()));\n\t\tclaims.put(IdTokenClaimNames.NONCE, this.nonceHash);\n\t\tJwt idToken = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tDefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), this.idToken);\n\t\tArgumentCaptor<OidcUserRequest> userRequestArgCaptor = ArgumentCaptor.forClass(OidcUserRequest.class);\n\t\tgiven(this.userService.loadUser(userRequestArgCaptor.capture())).willReturn(Mono.just(user));\n\t\tgiven(this.jwtDecoder.decode(any())).willReturn(Mono.just(idToken));\n\t\tthis.manager.setJwtDecoderFactory((c) -> this.jwtDecoder);\n\t\tthis.manager.authenticate(authorizationCodeAuthentication).block();\n\t\tassertThat(userRequestArgCaptor.getValue().getAdditionalParameters())\n\t\t\t.containsAllEntriesOf(accessTokenResponse.getAdditionalParameters());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthoritiesMapperSetThenReturnMappedAuthorities() {\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"foo\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.additionalParameters(\n\t\t\t\t\t\tCollections.singletonMap(OidcParameterNames.ID_TOKEN, this.idToken.getTokenValue()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = loginToken();\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://issuer.example.com\");\n\t\tclaims.put(IdTokenClaimNames.SUB, \"rob\");\n\t\tclaims.put(IdTokenClaimNames.AUD, Collections.singletonList(clientRegistration.getClientId()));\n\t\tclaims.put(IdTokenClaimNames.NONCE, this.nonceHash);\n\t\tJwt idToken = TestJwts.jwt().claims((c) -> c.putAll(claims)).build();\n\t\tgiven(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\tDefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), this.idToken);\n\t\tArgumentCaptor<OidcUserRequest> userRequestArgCaptor = ArgumentCaptor.forClass(OidcUserRequest.class);\n\t\tgiven(this.userService.loadUser(userRequestArgCaptor.capture())).willReturn(Mono.just(user));\n\t\tList<GrantedAuthority> mappedAuthorities = AuthorityUtils.createAuthorityList(\"ROLE_OIDC_USER\");\n\t\tGrantedAuthoritiesMapper authoritiesMapper = mock(GrantedAuthoritiesMapper.class);\n\t\tgiven(authoritiesMapper.mapAuthorities(anyCollection()))\n\t\t\t.willAnswer((Answer<List<GrantedAuthority>>) (invocation) -> mappedAuthorities);\n\t\tgiven(this.jwtDecoder.decode(any())).willReturn(Mono.just(idToken));\n\t\tthis.manager.setJwtDecoderFactory((c) -> this.jwtDecoder);\n\t\tthis.manager.setAuthoritiesMapper(authoritiesMapper);\n\t\tAuthentication result = this.manager.authenticate(authorizationCodeAuthentication).block();\n\t\tassertThat(result.getAuthorities()).isEqualTo(mappedAuthorities);\n\t}\n\n\tprivate OAuth2AuthorizationCodeAuthenticationToken loginToken() {\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\ttry {\n\t\t\tString nonce = this.secureKeyGenerator.generateKey();\n\t\t\tthis.nonceHash = OidcAuthorizationCodeReactiveAuthenticationManager.createHash(nonce);\n\t\t\tattributes.put(OidcParameterNames.NONCE, nonce);\n\t\t\tadditionalParameters.put(OidcParameterNames.NONCE, this.nonceHash);\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t}\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.state(\"state\")\n\t\t\t\t.clientId(clientRegistration.getClientId())\n\t\t\t\t.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())\n\t\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t\t.scopes(clientRegistration.getScopes())\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.attributes(attributes).build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = this.authorizationResponseBldr\n\t\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\treturn new OAuth2AuthorizationCodeAuthenticationToken(clientRegistration, authorizationExchange);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizedClientRefreshedEventListenerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.event.OAuth2AuthorizedClientRefreshedEvent;\nimport org.springframework.security.oauth2.client.oidc.authentication.event.OidcUserRefreshedEvent;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;\nimport org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link OidcAuthorizedClientRefreshedEventListener}.\n *\n * @author Steve Riesenberg\n */\npublic class OidcAuthorizedClientRefreshedEventListenerTests {\n\n\tprivate static final String INVALID_ID_TOKEN_ERROR = \"invalid_id_token\";\n\n\tprivate static final String INVALID_NONCE_ERROR = \"invalid_nonce\";\n\n\tprivate static final String SUBJECT = \"surfer-dude\";\n\n\tprivate static final String ACCESS_TOKEN_VALUE = \"hang-ten\";\n\n\tprivate static final String REFRESH_TOKEN_VALUE = \"surfs-up\";\n\n\tprivate static final String ID_TOKEN_VALUE = \"beach-break\";\n\n\tprivate OidcAuthorizedClientRefreshedEventListener eventListener;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy;\n\n\tprivate JwtDecoder jwtDecoder;\n\n\tprivate OidcUserService userService;\n\n\tprivate ApplicationEventPublisher applicationEventPublisher;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate OAuth2AccessTokenResponse accessTokenResponse;\n\n\tprivate Jwt jwt;\n\n\tprivate OidcUser oidcUser;\n\n\tprivate OAuth2AuthenticationToken authentication;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.jwtDecoder = mock(JwtDecoder.class);\n\t\tthis.userService = mock(OidcUserService.class);\n\t\tthis.securityContextHolderStrategy = mock(SecurityContextHolderStrategy.class);\n\t\tthis.applicationEventPublisher = mock(ApplicationEventPublisher.class);\n\n\t\tthis.eventListener = new OidcAuthorizedClientRefreshedEventListener();\n\t\tthis.eventListener.setUserService(this.userService);\n\t\tthis.eventListener.setJwtDecoderFactory((clientRegistration) -> this.jwtDecoder);\n\t\tthis.eventListener.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tthis.eventListener.setApplicationEventPublisher(this.applicationEventPublisher);\n\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().scope(OidcScopes.OPENID).build();\n\t\tthis.authorizedClient = createAuthorizedClient(this.clientRegistration);\n\t\tthis.accessTokenResponse = createAccessTokenResponse(OidcScopes.OPENID);\n\t\tthis.jwt = createJwt().build();\n\t\tthis.oidcUser = createOidcUser();\n\t\tthis.authentication = createAuthenticationToken(this.clientRegistration, createOidcUser());\n\t}\n\n\t@Test\n\tpublic void setSecurityContextHolderStrategyWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.eventListener.setSecurityContextHolderStrategy(null))\n\t\t\t.withMessage(\"securityContextHolderStrategy cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setJwtDecoderFactoryWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.eventListener.setJwtDecoderFactory(null))\n\t\t\t.withMessage(\"jwtDecoderFactory cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setUserServiceWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.eventListener.setUserService(null))\n\t\t\t.withMessage(\"userService cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthoritiesMapperWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.eventListener.setAuthoritiesMapper(null))\n\t\t\t.withMessage(\"authoritiesMapper cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setApplicationEventPublisherWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.eventListener.setApplicationEventPublisher(null))\n\t\t\t.withMessage(\"applicationEventPublisher cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.eventListener.setClockSkew(null))\n\t\t\t.withMessage(\"clockSkew cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.eventListener.setClockSkew(Duration.ofMillis(-1)))\n\t\t\t.withMessage(\"clockSkew must be >= 0\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenAccessTokenResponseMissingIdTokenThenOidcUserRefreshedEventNotPublished() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()\n\t\t\t.scopes(Set.of(OidcScopes.OPENID))\n\t\t\t.build();\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\taccessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\t\tverifyNoInteractions(this.securityContextHolderStrategy, this.jwtDecoder, this.userService,\n\t\t\t\tthis.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenAccessTokenResponseMissingOpenidScopeThenOidcUserRefreshedEventNotPublished() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.oidcAccessTokenResponse()\n\t\t\t.scopes(Set.of())\n\t\t\t.build();\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\taccessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\t\tverifyNoInteractions(this.securityContextHolderStrategy, this.jwtDecoder, this.userService,\n\t\t\t\tthis.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenAuthenticationIsNotOAuth2ThenOidcUserRefreshedEventNotPublished() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(SUBJECT, null);\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy);\n\t\tverifyNoInteractions(this.jwtDecoder, this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenAuthenticationIsCustomThenOidcUserRefreshedEventNotPublished() {\n\t\tOAuth2AuthenticationToken authentication = new CustomOAuth2AuthenticationToken(this.oidcUser,\n\t\t\t\tthis.oidcUser.getAuthorities(), this.clientRegistration.getRegistrationId());\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy);\n\t\tverifyNoInteractions(this.jwtDecoder, this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenPrincipalIsOAuth2UserThenOidcUserRefreshedEventNotPublished() {\n\t\tMap<String, Object> attributes = Map.of(StandardClaimNames.SUB, SUBJECT);\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"OAUTH2_USER\"), attributes,\n\t\t\t\tStandardClaimNames.SUB);\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(oauth2User,\n\t\t\t\toauth2User.getAuthorities(), this.clientRegistration.getRegistrationId());\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy);\n\t\tverifyNoInteractions(this.jwtDecoder, this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenClientRegistrationIdDoesNotMatchThenOidcUserRefreshedEventNotPublished() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.registrationId(\"test\")\n\t\t\t.build();\n\t\tOAuth2AuthenticationToken authentication = createAuthenticationToken(clientRegistration, this.oidcUser);\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy);\n\t\tverifyNoInteractions(this.jwtDecoder, this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenAccessTokenResponseIncludesIdTokenThenOidcUserRefreshedEventPublished() {\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(this.jwt);\n\t\tgiven(this.userService.loadUser(any(OidcUserRequest.class))).willReturn(this.oidcUser);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\n\t\tArgumentCaptor<OidcUserRequest> userRequestCaptor = ArgumentCaptor.forClass(OidcUserRequest.class);\n\t\tArgumentCaptor<OidcUserRefreshedEvent> userRefreshedEventCaptor = ArgumentCaptor\n\t\t\t.forClass(OidcUserRefreshedEvent.class);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverify(this.jwtDecoder).decode(this.jwt.getTokenValue());\n\t\tverify(this.userService).loadUser(userRequestCaptor.capture());\n\t\tverify(this.applicationEventPublisher).publishEvent(userRefreshedEventCaptor.capture());\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy, this.jwtDecoder, this.userService,\n\t\t\t\tthis.applicationEventPublisher);\n\n\t\tOidcUserRequest userRequest = userRequestCaptor.getValue();\n\t\tassertThat(userRequest.getClientRegistration()).isSameAs(this.clientRegistration);\n\t\tassertThat(userRequest.getAccessToken()).isSameAs(this.accessTokenResponse.getAccessToken());\n\t\tassertThat(userRequest.getIdToken().getTokenValue()).isEqualTo(this.jwt.getTokenValue());\n\n\t\tOidcUserRefreshedEvent userRefreshedEvent = userRefreshedEventCaptor.getValue();\n\t\tassertThat(userRefreshedEvent.getAccessTokenResponse()).isSameAs(this.accessTokenResponse);\n\t\tassertThat(userRefreshedEvent.getOldOidcUser()).isSameAs(this.authentication.getPrincipal());\n\t\tassertThat(userRefreshedEvent.getNewOidcUser()).isSameAs(this.oidcUser);\n\t\tassertThat(userRefreshedEvent.getOldOidcUser()).isNotSameAs(userRefreshedEvent.getNewOidcUser());\n\t\tassertThat(userRefreshedEvent.getAuthentication()).isNotSameAs(this.authentication);\n\t\tassertThat(userRefreshedEvent.getAuthentication()).isInstanceOf(OAuth2AuthenticationToken.class);\n\n\t\tOAuth2AuthenticationToken authenticationResult = (OAuth2AuthenticationToken) userRefreshedEvent\n\t\t\t.getAuthentication();\n\t\tassertThat(authenticationResult.getPrincipal()).isEqualTo(this.oidcUser);\n\t\tassertThat(authenticationResult.getAuthorities()).containsExactlyElementsOf(this.oidcUser.getAuthorities());\n\t\tassertThat(authenticationResult.getAuthorizedClientRegistrationId())\n\t\t\t.isEqualTo(this.clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenIdTokenIssuerDoesNotMatchThenThrowsOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt().issuer(\"https://invalid.url\").build();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.eventListener.onApplicationEvent(authorizedClientRefreshedEvent))\n\t\t\t.withMessageContaining(\"Invalid issuer\")\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t.isEqualTo(INVALID_ID_TOKEN_ERROR);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverify(this.jwtDecoder).decode(this.jwt.getTokenValue());\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy, this.jwtDecoder);\n\t\tverifyNoInteractions(this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenIdTokenSubjectDoesNotMatchThenThrowsOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt().subject(\"invalid\").build();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.eventListener.onApplicationEvent(authorizedClientRefreshedEvent))\n\t\t\t.withMessageContaining(\"Invalid subject\")\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t.isEqualTo(INVALID_ID_TOKEN_ERROR);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverify(this.jwtDecoder).decode(this.jwt.getTokenValue());\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy, this.jwtDecoder);\n\t\tverifyNoInteractions(this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenIdTokenIssuedAtIsBeforeThenThrowsOAuth2AuthenticationException() {\n\t\tInstant issuedAt = this.oidcUser.getIssuedAt().minus(2, ChronoUnit.MINUTES);\n\t\tJwt jwt = createJwt().issuedAt(issuedAt).build();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.eventListener.onApplicationEvent(authorizedClientRefreshedEvent))\n\t\t\t.withMessageContaining(\"Invalid issued at time\")\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t.isEqualTo(INVALID_ID_TOKEN_ERROR);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverify(this.jwtDecoder).decode(this.jwt.getTokenValue());\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy, this.jwtDecoder);\n\t\tverifyNoInteractions(this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenIdTokenAudienceDoesNotMatchThenThrowsOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt().audience(List.of(\"audience1\", \"audience3\")).build();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.eventListener.onApplicationEvent(authorizedClientRefreshedEvent))\n\t\t\t.withMessageContaining(\"Invalid audience\")\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t.isEqualTo(INVALID_ID_TOKEN_ERROR);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverify(this.jwtDecoder).decode(this.jwt.getTokenValue());\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy, this.jwtDecoder);\n\t\tverifyNoInteractions(this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenIdTokenAuthenticatedAtDoesNotMatchThenThrowsOAuth2AuthenticationException() {\n\t\tInstant authTime = this.oidcUser.getAuthenticatedAt().plus(5, ChronoUnit.SECONDS);\n\t\tJwt jwt = createJwt().claim(IdTokenClaimNames.AUTH_TIME, authTime).build();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.eventListener.onApplicationEvent(authorizedClientRefreshedEvent))\n\t\t\t.withMessageContaining(\"Invalid authenticated at time\")\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t.isEqualTo(INVALID_ID_TOKEN_ERROR);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverify(this.jwtDecoder).decode(this.jwt.getTokenValue());\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy, this.jwtDecoder);\n\t\tverifyNoInteractions(this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenIdTokenAuthenticatedAtMatchesThenOidcUserRefreshedEventPublished() {\n\t\tInstant authTime = this.authentication.getPrincipal().getAttribute(IdTokenClaimNames.AUTH_TIME);\n\t\tJwt jwt = createJwt().claim(IdTokenClaimNames.AUTH_TIME, authTime).build();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\t\tgiven(this.userService.loadUser(any(OidcUserRequest.class))).willReturn(this.oidcUser);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\n\t\tverify(this.applicationEventPublisher).publishEvent(any(OidcUserRefreshedEvent.class));\n\t\tverifyNoMoreInteractions(this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenIdTokenNonceDoesNotMatchThenThrowsOAuth2AuthenticationException() {\n\t\tJwt jwt = createJwt().claim(IdTokenClaimNames.NONCE, \"invalid\").build();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.eventListener.onApplicationEvent(authorizedClientRefreshedEvent))\n\t\t\t.withMessageContaining(\"Invalid nonce\")\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t.isEqualTo(INVALID_NONCE_ERROR);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverify(this.jwtDecoder).decode(this.jwt.getTokenValue());\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy, this.jwtDecoder);\n\t\tverifyNoInteractions(this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenIdTokenNonceMatchesThenOidcUserRefreshedEventPublished() {\n\t\tJwt jwt = createJwt().claim(IdTokenClaimNames.NONCE, this.oidcUser.getNonce()).build();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(jwt);\n\t\tgiven(this.userService.loadUser(any(OidcUserRequest.class))).willReturn(this.oidcUser);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\n\t\tverify(this.applicationEventPublisher).publishEvent(any(OidcUserRefreshedEvent.class));\n\t\tverifyNoMoreInteractions(this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenInvalidIdTokenThenThrowsOAuth2AuthenticationException() {\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willThrow(new JwtException(\"Invalid token\"));\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.eventListener.onApplicationEvent(authorizedClientRefreshedEvent))\n\t\t\t.extracting(OAuth2AuthenticationException::getError)\n\t\t\t.extracting(OAuth2Error::getErrorCode)\n\t\t\t.isEqualTo(INVALID_ID_TOKEN_ERROR);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t\tverify(this.jwtDecoder).decode(this.jwt.getTokenValue());\n\t\tverifyNoMoreInteractions(this.securityContextHolderStrategy, this.jwtDecoder);\n\t\tverifyNoInteractions(this.userService, this.applicationEventPublisher);\n\t}\n\n\t@Test\n\tpublic void onApplicationEventWhenCustomAuthoritiesMapperSetThenUsed() {\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(this.authentication);\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(securityContext);\n\t\tgiven(this.jwtDecoder.decode(anyString())).willReturn(this.jwt);\n\t\tgiven(this.userService.loadUser(any(OidcUserRequest.class))).willReturn(this.oidcUser);\n\n\t\tGrantedAuthoritiesMapper grantedAuthoritiesMapper = mock(GrantedAuthoritiesMapper.class);\n\t\tthis.eventListener.setAuthoritiesMapper(grantedAuthoritiesMapper);\n\n\t\tOAuth2AuthorizedClientRefreshedEvent authorizedClientRefreshedEvent = new OAuth2AuthorizedClientRefreshedEvent(\n\t\t\t\tthis.accessTokenResponse, this.authorizedClient);\n\t\tthis.eventListener.onApplicationEvent(authorizedClientRefreshedEvent);\n\n\t\tverify(grantedAuthoritiesMapper).mapAuthorities(this.oidcUser.getAuthorities());\n\t\tverifyNoMoreInteractions(grantedAuthoritiesMapper);\n\t}\n\n\tprivate static OAuth2AuthorizedClient createAuthorizedClient(ClientRegistration clientRegistration) {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(30, ChronoUnit.SECONDS);\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, ACCESS_TOKEN_VALUE,\n\t\t\t\tissuedAt, expiresAt, clientRegistration.getScopes());\n\n\t\treturn new OAuth2AuthorizedClient(clientRegistration, SUBJECT, accessToken);\n\t}\n\n\tprivate static OAuth2AccessTokenResponse createAccessTokenResponse(String... scope) {\n\t\tSet<String> scopes = Set.of(scope);\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tif (scopes.contains(OidcScopes.OPENID)) {\n\t\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, ID_TOKEN_VALUE);\n\t\t}\n\n\t\treturn OAuth2AccessTokenResponse.withToken(ACCESS_TOKEN_VALUE)\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.scopes(scopes)\n\t\t\t.refreshToken(REFRESH_TOKEN_VALUE)\n\t\t\t.expiresIn(60L)\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t}\n\n\tprivate static Jwt.Builder createJwt() {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.MINUTES);\n\t\treturn TestJwts.jwt()\n\t\t\t.issuer(\"https://surf.school\")\n\t\t\t.subject(SUBJECT)\n\t\t\t.tokenValue(ID_TOKEN_VALUE)\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt)\n\t\t\t.audience(List.of(\"audience1\", \"audience2\"));\n\t}\n\n\tprivate static OidcUser createOidcUser() {\n\t\tInstant issuedAt = Instant.now().minus(30, ChronoUnit.SECONDS);\n\t\tInstant expiresAt = issuedAt.plus(5, ChronoUnit.MINUTES);\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(IdTokenClaimNames.ISS, \"https://surf.school\");\n\t\tclaims.put(IdTokenClaimNames.SUB, SUBJECT);\n\t\tclaims.put(IdTokenClaimNames.IAT, issuedAt);\n\t\tclaims.put(IdTokenClaimNames.EXP, expiresAt);\n\t\tclaims.put(IdTokenClaimNames.AUD, List.of(\"audience1\", \"audience2\"));\n\t\tclaims.put(IdTokenClaimNames.AUTH_TIME, issuedAt);\n\t\tclaims.put(IdTokenClaimNames.NONCE, \"nonce\");\n\t\tOidcIdToken idToken = new OidcIdToken(ID_TOKEN_VALUE, issuedAt, expiresAt, claims);\n\n\t\treturn new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"OIDC_USER\"), idToken);\n\t}\n\n\tprivate static OAuth2AuthenticationToken createAuthenticationToken(ClientRegistration clientRegistration,\n\t\t\tOidcUser oidcUser) {\n\t\treturn new OAuth2AuthenticationToken(oidcUser, oidcUser.getAuthorities(),\n\t\t\t\tclientRegistration.getRegistrationId());\n\t}\n\n\tprivate static final class CustomOAuth2AuthenticationToken extends OAuth2AuthenticationToken {\n\n\t\tCustomOAuth2AuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities,\n\t\t\t\tString authorizedClientRegistrationId) {\n\t\t\tsuper(principal, authorities, authorizedClientRegistrationId);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenDecoderFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Joe Grandja\n * @author Rafael Dominguez\n * @author Ivan Golovko\n * @since 5.2\n */\npublic class OidcIdTokenDecoderFactoryTests {\n\n\t// @formatter:off\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations\n\t\t\t.clientRegistration()\n\t\t\t.scope(\"openid\");\n\t// @formatter:on\n\n\tprivate OidcIdTokenDecoderFactory idTokenDecoderFactory;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.idTokenDecoderFactory = new OidcIdTokenDecoderFactory();\n\t}\n\n\t@Test\n\tpublic void createDefaultClaimTypeConvertersWhenCalledThenDefaultsAreCorrect() {\n\t\tMap<String, Converter<Object, ?>> claimTypeConverters = OidcIdTokenDecoderFactory\n\t\t\t.createDefaultClaimTypeConverters();\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.ISS);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.AUD);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.NONCE);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.EXP);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.IAT);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.AUTH_TIME);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.AMR);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.EMAIL_VERIFIED);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.PHONE_NUMBER_VERIFIED);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.UPDATED_AT);\n\t}\n\n\t@Test\n\tpublic void setJwtValidatorFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.idTokenDecoderFactory.setJwtValidatorFactory(null));\n\t}\n\n\t@Test\n\tpublic void setJwsAlgorithmResolverWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.idTokenDecoderFactory.setJwsAlgorithmResolver(null));\n\t}\n\n\t@Test\n\tpublic void setClaimTypeConverterFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.setClaimTypeConverterFactory(null));\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenClientRegistrationNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(null));\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenJwsAlgorithmDefaultAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))\n\t\t\t.withMessage(\"[missing_signature_verifier] Failed to find a Signature Verifier \"\n\t\t\t\t\t+ \"for Client Registration: 'registration-id'. \"\n\t\t\t\t\t+ \"Check to ensure you have configured the JwkSet URI.\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenJwsAlgorithmEcAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {\n\t\tthis.idTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> SignatureAlgorithm.ES256);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))\n\t\t\t.withMessage(\"[missing_signature_verifier] Failed to find a Signature Verifier \"\n\t\t\t\t\t+ \"for Client Registration: 'registration-id'. \"\n\t\t\t\t\t+ \"Check to ensure you have configured the JwkSet URI.\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenJwsAlgorithmHmacAndClientSecretNullThenThrowOAuth2AuthenticationException() {\n\t\tthis.idTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> MacAlgorithm.HS256);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.clientSecret(null).build()))\n\t\t\t.withMessage(\"[missing_signature_verifier] Failed to find a Signature Verifier \"\n\t\t\t\t\t+ \"for Client Registration: 'registration-id'. \"\n\t\t\t\t\t+ \"Check to ensure you have configured the client secret.\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenJwsAlgorithmNullThenThrowOAuth2AuthenticationException() {\n\t\tthis.idTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.build()))\n\t\t\t.withMessage(\"[missing_signature_verifier] Failed to find a Signature Verifier \"\n\t\t\t\t\t+ \"for Client Registration: 'registration-id'. \"\n\t\t\t\t\t+ \"Check to ensure you have configured a valid JWS Algorithm: 'null'\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenClientRegistrationValidThenReturnDecoder() {\n\t\tassertThat(this.idTokenDecoderFactory.createDecoder(this.registration.build())).isNotNull();\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenCustomJwtValidatorFactorySetThenApplied() {\n\t\tFunction<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory = mock(Function.class);\n\t\tthis.idTokenDecoderFactory.setJwtValidatorFactory(customJwtValidatorFactory);\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tgiven(customJwtValidatorFactory.apply(same(clientRegistration)))\n\t\t\t.willReturn(new OidcIdTokenValidator(clientRegistration));\n\t\tthis.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tverify(customJwtValidatorFactory).apply(same(clientRegistration));\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenCustomJwsAlgorithmResolverSetThenApplied() {\n\t\tFunction<ClientRegistration, JwsAlgorithm> customJwsAlgorithmResolver = mock(Function.class);\n\t\tthis.idTokenDecoderFactory.setJwsAlgorithmResolver(customJwsAlgorithmResolver);\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tgiven(customJwsAlgorithmResolver.apply(same(clientRegistration))).willReturn(MacAlgorithm.HS256);\n\t\tthis.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tverify(customJwsAlgorithmResolver).apply(same(clientRegistration));\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenCustomClaimTypeConverterFactorySetThenApplied() {\n\t\tFunction<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> customClaimTypeConverterFactory = mock(\n\t\t\t\tFunction.class);\n\t\tthis.idTokenDecoderFactory.setClaimTypeConverterFactory(customClaimTypeConverterFactory);\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tgiven(customClaimTypeConverterFactory.apply(same(clientRegistration)))\n\t\t\t.willReturn(new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));\n\t\tthis.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tverify(customClaimTypeConverterFactory).apply(same(clientRegistration));\n\t}\n\n\t// gh-16647\n\t@Test\n\tpublic void createDecoderWhenCachingRemovedThenReturnNewDecoder() {\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tJwtDecoder decoder1 = this.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tJwtDecoder decoder2 = this.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tassertThat(decoder1).isNotSameAs(decoder2);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\nimport org.springframework.security.oauth2.jwt.Jwt;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @author Joe Grandja\n * @since 5.1\n */\npublic class OidcIdTokenValidatorTests {\n\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration();\n\n\tprivate Map<String, Object> headers = new HashMap<>();\n\n\tprivate Map<String, Object> claims = new HashMap<>();\n\n\tprivate Instant issuedAt = Instant.now();\n\n\tprivate Instant expiresAt = this.issuedAt.plusSeconds(3600);\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.headers.put(\"alg\", JwsAlgorithms.RS256);\n\t\tthis.claims.put(IdTokenClaimNames.ISS, \"https://example.com\");\n\t\tthis.claims.put(IdTokenClaimNames.SUB, \"rob\");\n\t\tthis.claims.put(IdTokenClaimNames.AUD, Collections.singletonList(\"client-id\"));\n\t}\n\n\t@Test\n\tpublic void validateWhenValidThenNoErrors() {\n\t\tassertThat(this.validateIdToken()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNullThenThrowIllegalArgumentException() {\n\t\tOidcIdTokenValidator idTokenValidator = new OidcIdTokenValidator(this.registration.build());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> idTokenValidator.setClockSkew(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockSkewWhenNegativeSecondsThenThrowIllegalArgumentException() {\n\t\tOidcIdTokenValidator idTokenValidator = new OidcIdTokenValidator(this.registration.build());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> idTokenValidator.setClockSkew(Duration.ofSeconds(-1)));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClockWhenNullThenThrowIllegalArgumentException() {\n\t\tOidcIdTokenValidator idTokenValidator = new OidcIdTokenValidator(this.registration.build());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> idTokenValidator.setClock(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenIssuerNullThenHasErrors() {\n\t\tthis.claims.remove(IdTokenClaimNames.ISS);\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.ISS));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenMetadataIssuerMismatchThenHasErrors() {\n\t\t/*\n\t\t * When the issuer is set in the provider metadata, and it does not match the\n\t\t * issuer in the ID Token, the validation must fail\n\t\t */\n\t\tthis.registration = this.registration.issuerUri(\"https://somethingelse.com\");\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.ISS));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenMetadataIssuerMatchThenNoErrors() {\n\t\t/*\n\t\t * When the issuer is set in the provider metadata, and it does match the issuer\n\t\t * in the ID Token, the validation must succeed\n\t\t */\n\t\tthis.registration = this.registration.issuerUri(\"https://example.com\");\n\t\tassertThat(this.validateIdToken()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void validateWhenSubNullThenHasErrors() {\n\t\tthis.claims.remove(IdTokenClaimNames.SUB);\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.SUB));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenAudNullThenHasErrors() {\n\t\tthis.claims.remove(IdTokenClaimNames.AUD);\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.AUD));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenIssuedAtNullThenHasErrors() {\n\t\tthis.issuedAt = null;\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.IAT));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenExpiresAtNullThenHasErrors() {\n\t\tthis.expiresAt = null;\n\t\tassertThat(this.validateIdToken()).hasSize(1)\n\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.EXP));\n\t}\n\n\t@Test\n\tpublic void validateWhenAudMultipleAndAzpNullThenHasErrors() {\n\t\tthis.claims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client-id\", \"other\"));\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.AZP));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenAzpNotClientIdThenHasErrors() {\n\t\tthis.claims.put(IdTokenClaimNames.AZP, \"other\");\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.AZP));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenMultipleAudAzpClientIdThenNoErrors() {\n\t\tthis.claims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client-id\", \"other\"));\n\t\tthis.claims.put(IdTokenClaimNames.AZP, \"client-id\");\n\t\tassertThat(this.validateIdToken()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void validateWhenMultipleAudAzpNotClientIdThenHasErrors() {\n\t\tthis.claims.put(IdTokenClaimNames.AUD, Arrays.asList(\"client-id-1\", \"client-id-2\"));\n\t\tthis.claims.put(IdTokenClaimNames.AZP, \"other-client\");\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.AZP));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenAudNotClientIdThenHasErrors() {\n\t\tthis.claims.put(IdTokenClaimNames.AUD, Collections.singletonList(\"other-client\"));\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.AUD));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenExpiredAnd60secClockSkewThenNoErrors() {\n\t\tthis.issuedAt = Instant.now().minus(Duration.ofSeconds(60));\n\t\tthis.expiresAt = this.issuedAt.plus(Duration.ofSeconds(30));\n\t\tthis.clockSkew = Duration.ofSeconds(60);\n\t\tassertThat(this.validateIdToken()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void validateWhenExpiredAnd0secClockSkewThenHasErrors() {\n\t\tthis.issuedAt = Instant.now().minus(Duration.ofSeconds(60));\n\t\tthis.expiresAt = this.issuedAt.plus(Duration.ofSeconds(30));\n\t\tthis.clockSkew = Duration.ofSeconds(0);\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.EXP));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenIssuedAt5minAheadAnd5minClockSkewThenNoErrors() {\n\t\tthis.issuedAt = Instant.now().plus(Duration.ofMinutes(5));\n\t\tthis.expiresAt = this.issuedAt.plus(Duration.ofSeconds(60));\n\t\tthis.clockSkew = Duration.ofMinutes(5);\n\t\tassertThat(this.validateIdToken()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void validateWhenIssuedAt1minAheadAnd0minClockSkewThenHasErrors() {\n\t\tthis.issuedAt = Instant.now().plus(Duration.ofMinutes(1));\n\t\tthis.expiresAt = this.issuedAt.plus(Duration.ofSeconds(60));\n\t\tthis.clockSkew = Duration.ofMinutes(0);\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.IAT));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenExpiresAtBeforeNowThenHasErrors() {\n\t\tthis.issuedAt = Instant.now().minus(Duration.ofSeconds(10));\n\t\tthis.expiresAt = this.issuedAt.plus(Duration.ofSeconds(5));\n\t\tthis.clockSkew = Duration.ofSeconds(0);\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.EXP));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenMissingClaimsThenHasErrors() {\n\t\tthis.claims.remove(IdTokenClaimNames.SUB);\n\t\tthis.claims.remove(IdTokenClaimNames.AUD);\n\t\tthis.issuedAt = null;\n\t\tthis.expiresAt = null;\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.SUB))\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.AUD))\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.IAT))\n\t\t\t\t.allMatch((msg) -> msg.contains(IdTokenClaimNames.EXP));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateFormatError() {\n\t\tthis.claims.remove(IdTokenClaimNames.SUB);\n\t\tthis.claims.remove(IdTokenClaimNames.AUD);\n\t\t// @formatter:off\n\t\tassertThat(this.validateIdToken())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.extracting(OAuth2Error::getDescription)\n\t\t\t\t.allMatch((msg) -> msg.equals(\"The ID Token contains invalid claims: {sub=null, aud=null}\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate Collection<OAuth2Error> validateIdToken() {\n\t\t// @formatter:off\n\t\tJwt idToken = Jwt.withTokenValue(\"token\")\n\t\t\t\t.issuedAt(this.issuedAt)\n\t\t\t\t.expiresAt(this.expiresAt)\n\t\t\t\t.headers((h) -> h.putAll(this.headers))\n\t\t\t\t.claims((c) -> c.putAll(this.claims))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOidcIdTokenValidator validator = new OidcIdTokenValidator(this.registration.build());\n\t\tvalidator.setClockSkew(this.clockSkew);\n\t\treturn validator.validate(idToken).getErrors();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/ReactiveOidcIdTokenDecoderFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Joe Grandja\n * @author Rafael Dominguez\n * @author Ubaid ur Rehman\n * @author Ivan Golovko\n * @since 5.2\n */\npublic class ReactiveOidcIdTokenDecoderFactoryTests {\n\n\t// @formatter:off\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration()\n\t\t\t.scope(\"openid\");\n\t// @formatter:on\n\n\tprivate ReactiveOidcIdTokenDecoderFactory idTokenDecoderFactory;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.idTokenDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();\n\t}\n\n\t@Test\n\tpublic void createDefaultClaimTypeConvertersWhenCalledThenDefaultsAreCorrect() {\n\t\tMap<String, Converter<Object, ?>> claimTypeConverters = ReactiveOidcIdTokenDecoderFactory\n\t\t\t.createDefaultClaimTypeConverters();\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.ISS);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.AUD);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.NONCE);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.EXP);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.IAT);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.AUTH_TIME);\n\t\tassertThat(claimTypeConverters).containsKey(IdTokenClaimNames.AMR);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.EMAIL_VERIFIED);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.PHONE_NUMBER_VERIFIED);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.UPDATED_AT);\n\t}\n\n\t@Test\n\tpublic void setJwtValidatorFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.idTokenDecoderFactory.setJwtValidatorFactory(null));\n\t}\n\n\t@Test\n\tpublic void setJwsAlgorithmResolverWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.idTokenDecoderFactory.setJwsAlgorithmResolver(null));\n\t}\n\n\t@Test\n\tpublic void setClaimTypeConverterFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.setClaimTypeConverterFactory(null));\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenClientRegistrationNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(null));\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenJwsAlgorithmDefaultAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))\n\t\t\t.withMessage(\"[missing_signature_verifier] Failed to find a Signature Verifier \"\n\t\t\t\t\t+ \"for Client Registration: 'registration-id'. \"\n\t\t\t\t\t+ \"Check to ensure you have configured the JwkSet URI.\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenJwsAlgorithmEcAndJwkSetUriEmptyThenThrowOAuth2AuthenticationException() {\n\t\tthis.idTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> SignatureAlgorithm.ES256);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.jwkSetUri(null).build()))\n\t\t\t.withMessage(\"[missing_signature_verifier] Failed to find a Signature Verifier \"\n\t\t\t\t\t+ \"for Client Registration: 'registration-id'. \"\n\t\t\t\t\t+ \"Check to ensure you have configured the JwkSet URI.\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenJwsAlgorithmHmacAndClientSecretNullThenThrowOAuth2AuthenticationException() {\n\t\tthis.idTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> MacAlgorithm.HS256);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.clientSecret(null).build()))\n\t\t\t.withMessage(\"[missing_signature_verifier] Failed to find a Signature Verifier \"\n\t\t\t\t\t+ \"for Client Registration: 'registration-id'. \"\n\t\t\t\t\t+ \"Check to ensure you have configured the client secret.\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenJwsAlgorithmNullThenThrowOAuth2AuthenticationException() {\n\t\tthis.idTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> null);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.idTokenDecoderFactory.createDecoder(this.registration.build()))\n\t\t\t.withMessage(\"[missing_signature_verifier] Failed to find a Signature Verifier \"\n\t\t\t\t\t+ \"for Client Registration: 'registration-id'. \"\n\t\t\t\t\t+ \"Check to ensure you have configured a valid JWS Algorithm: 'null'\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenClientRegistrationValidThenReturnDecoder() {\n\t\tassertThat(this.idTokenDecoderFactory.createDecoder(this.registration.build())).isNotNull();\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenCustomJwtValidatorFactorySetThenApplied() {\n\t\tFunction<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory = mock(Function.class);\n\t\tthis.idTokenDecoderFactory.setJwtValidatorFactory(customJwtValidatorFactory);\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tgiven(customJwtValidatorFactory.apply(same(clientRegistration)))\n\t\t\t.willReturn(new OidcIdTokenValidator(clientRegistration));\n\t\tthis.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tverify(customJwtValidatorFactory).apply(same(clientRegistration));\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenCustomJwsAlgorithmResolverSetThenApplied() {\n\t\tFunction<ClientRegistration, JwsAlgorithm> customJwsAlgorithmResolver = mock(Function.class);\n\t\tthis.idTokenDecoderFactory.setJwsAlgorithmResolver(customJwsAlgorithmResolver);\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tgiven(customJwsAlgorithmResolver.apply(same(clientRegistration))).willReturn(MacAlgorithm.HS256);\n\t\tthis.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tverify(customJwsAlgorithmResolver).apply(same(clientRegistration));\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenCustomClaimTypeConverterFactorySetThenApplied() {\n\t\tFunction<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> customClaimTypeConverterFactory = mock(\n\t\t\t\tFunction.class);\n\t\tthis.idTokenDecoderFactory.setClaimTypeConverterFactory(customClaimTypeConverterFactory);\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tgiven(customClaimTypeConverterFactory.apply(same(clientRegistration)))\n\t\t\t.willReturn(new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));\n\t\tthis.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tverify(customClaimTypeConverterFactory).apply(same(clientRegistration));\n\t}\n\n\t// gh-16647\n\t@Test\n\tpublic void createDecoderWhenCachingRemovedThenReturnNewDecoder() {\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\tReactiveJwtDecoder decoder1 = this.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tReactiveJwtDecoder decoder2 = this.idTokenDecoderFactory.createDecoder(clientRegistration);\n\t\tassertThat(decoder1).isNotSameAs(decoder2);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/logout/TestOidcLogoutTokens.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.authentication.logout;\n\nimport java.time.Instant;\nimport java.util.Collections;\n\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\n\npublic final class TestOidcLogoutTokens {\n\n\tpublic static OidcLogoutToken.Builder withUser(OidcUser user) {\n\t\tOidcLogoutToken.Builder builder = OidcLogoutToken.withTokenValue(\"token\")\n\t\t\t.audience(Collections.singleton(\"client-id\"))\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.issuer(user.getIssuer().toString())\n\t\t\t.jti(\"id\")\n\t\t\t.subject(user.getSubject());\n\t\tif (user.hasClaim(LogoutTokenClaimNames.SID)) {\n\t\t\tbuilder.sessionId(user.getClaimAsString(LogoutTokenClaimNames.SID));\n\t\t}\n\t\treturn builder;\n\t}\n\n\tpublic static OidcLogoutToken.Builder withSessionId(String issuer, String sessionId) {\n\t\treturn OidcLogoutToken.withTokenValue(\"token\")\n\t\t\t.audience(Collections.singleton(\"client-id\"))\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.issuer(issuer)\n\t\t\t.jti(\"id\")\n\t\t\t.sessionId(sessionId);\n\t}\n\n\tpublic static OidcLogoutToken.Builder withSubject(String issuer, String subject) {\n\t\treturn OidcLogoutToken.withTokenValue(\"token\")\n\t\t\t.audience(Collections.singleton(\"client-id\"))\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.issuer(issuer)\n\t\t\t.jti(\"id\")\n\t\t\t.subject(subject);\n\t}\n\n\tprivate TestOidcLogoutTokens() {\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/session/InMemoryOidcSessionRegistryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.session;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken;\nimport org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link InMemoryOidcSessionRegistry}\n */\npublic class InMemoryOidcSessionRegistryTests {\n\n\t@Test\n\tpublic void registerWhenDefaultsThenStoresSessionInformation() {\n\t\tInMemoryOidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry();\n\t\tString sessionId = \"client\";\n\t\tOidcSessionInformation info = TestOidcSessionInformations.create(sessionId);\n\t\tsessionRegistry.saveSessionInformation(info);\n\t\tOidcLogoutToken logoutToken = TestOidcLogoutTokens.withUser(info.getPrincipal()).build();\n\t\tIterable<OidcSessionInformation> infos = sessionRegistry.removeSessionInformation(logoutToken);\n\t\tassertThat(infos).containsExactly(info);\n\t}\n\n\t@Test\n\tpublic void registerWhenIdTokenHasSessionIdThenStoresSessionInformation() {\n\t\tInMemoryOidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry();\n\t\tOidcIdToken idToken = TestOidcIdTokens.idToken().claim(\"sid\", \"provider\").build();\n\t\tOidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken);\n\t\tOidcSessionInformation info = TestOidcSessionInformations.create(\"client\", user);\n\t\tsessionRegistry.saveSessionInformation(info);\n\t\tOidcLogoutToken logoutToken = TestOidcLogoutTokens.withSessionId(idToken.getIssuer().toString(), \"provider\")\n\t\t\t.build();\n\t\tIterable<OidcSessionInformation> infos = sessionRegistry.removeSessionInformation(logoutToken);\n\t\tassertThat(infos).containsExactly(info);\n\t}\n\n\t@Test\n\tpublic void unregisterWhenMultipleSessionsThenRemovesAllMatching() {\n\t\tInMemoryOidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry();\n\t\tOidcIdToken idToken = TestOidcIdTokens.idToken().claim(\"sid\", \"providerOne\").subject(\"otheruser\").build();\n\t\tOidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken);\n\t\tOidcSessionInformation oneSession = TestOidcSessionInformations.create(\"clientOne\", user);\n\t\tsessionRegistry.saveSessionInformation(oneSession);\n\t\tidToken = TestOidcIdTokens.idToken().claim(\"sid\", \"providerTwo\").build();\n\t\tuser = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken);\n\t\tOidcSessionInformation twoSession = TestOidcSessionInformations.create(\"clientTwo\", user);\n\t\tsessionRegistry.saveSessionInformation(twoSession);\n\t\tidToken = TestOidcIdTokens.idToken().claim(\"sid\", \"providerThree\").build();\n\t\tuser = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken);\n\t\tOidcSessionInformation threeSession = TestOidcSessionInformations.create(\"clientThree\", user);\n\t\tsessionRegistry.saveSessionInformation(threeSession);\n\t\tOidcLogoutToken logoutToken = TestOidcLogoutTokens\n\t\t\t.withSubject(idToken.getIssuer().toString(), idToken.getSubject())\n\t\t\t.build();\n\t\tIterable<OidcSessionInformation> infos = sessionRegistry.removeSessionInformation(logoutToken);\n\t\tassertThat(infos).containsExactlyInAnyOrder(twoSession, threeSession);\n\t\tlogoutToken = TestOidcLogoutTokens.withSubject(idToken.getIssuer().toString(), \"otheruser\").build();\n\t\tinfos = sessionRegistry.removeSessionInformation(logoutToken);\n\t\tassertThat(infos).containsExactly(oneSession);\n\t}\n\n\t@Test\n\tpublic void unregisterWhenNoSessionsThenEmptyList() {\n\t\tInMemoryOidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry();\n\t\tOidcIdToken idToken = TestOidcIdTokens.idToken().claim(\"sid\", \"provider\").build();\n\t\tOidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken);\n\t\tOidcSessionInformation info = TestOidcSessionInformations.create(\"client\", user);\n\t\tsessionRegistry.saveSessionInformation(info);\n\t\tOidcLogoutToken logoutToken = TestOidcLogoutTokens.withSessionId(idToken.getIssuer().toString(), \"wrong\")\n\t\t\t.build();\n\t\tIterable<?> infos = sessionRegistry.removeSessionInformation(logoutToken);\n\t\tassertThat(infos).isNotNull();\n\t\tassertThat(infos).isEmpty();\n\t\tlogoutToken = TestOidcLogoutTokens.withSessionId(\"https://wrong\", \"provider\").build();\n\t\tinfos = sessionRegistry.removeSessionInformation(logoutToken);\n\t\tassertThat(infos).isNotNull();\n\t\tassertThat(infos).isEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/session/TestOidcSessionInformations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.session;\n\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\n\n/**\n * Sample {@link OidcSessionInformation} instances\n */\npublic final class TestOidcSessionInformations {\n\n\tpublic static OidcSessionInformation create() {\n\t\treturn create(\"sessionId\");\n\t}\n\n\tpublic static OidcSessionInformation create(String sessionId) {\n\t\treturn create(sessionId, TestOidcUsers.create());\n\t}\n\n\tpublic static OidcSessionInformation create(String sessionId, OidcUser user) {\n\t\treturn new OidcSessionInformation(sessionId, Map.of(\"_csrf\", \"token\"), user);\n\t}\n\n\tprivate TestOidcSessionInformations() {\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcReactiveOAuth2UserServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class OidcReactiveOAuth2UserServiceTests {\n\n\t@Mock\n\tprivate ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService;\n\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration()\n\t\t.userNameAttributeName(IdTokenClaimNames.SUB);\n\n\tprivate OidcIdToken idToken = TestOidcIdTokens.idToken().build();\n\n\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\tInstant.now(), Instant.now().plus(Duration.ofDays(1)), Collections.singleton(\"read:user\"));\n\n\tprivate OidcReactiveOAuth2UserService userService = new OidcReactiveOAuth2UserService();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.userService.setOauth2UserService(this.oauth2UserService);\n\t}\n\n\t@Test\n\tpublic void createDefaultClaimTypeConvertersWhenCalledThenDefaultsAreCorrect() {\n\t\tMap<String, Converter<Object, ?>> claimTypeConverters = OidcReactiveOAuth2UserService\n\t\t\t.createDefaultClaimTypeConverters();\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.EMAIL_VERIFIED);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.PHONE_NUMBER_VERIFIED);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.UPDATED_AT);\n\t}\n\n\t@Test\n\tpublic void setClaimTypeConverterFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userService.setClaimTypeConverterFactory(null));\n\t}\n\n\t@Test\n\tpublic void setRetrieveUserInfoWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.userService.setRetrieveUserInfo(null))\n\t\t\t\t.withMessage(\"retrieveUserInfo cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoUriNullThenUserInfoNotRetrieved() {\n\t\tthis.registration.userInfoUri(null);\n\t\tOidcUser user = this.userService.loadUser(userRequest()).block();\n\t\tassertThat(user.getUserInfo()).isNull();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenOAuth2UserEmptyThenNullUserInfo() {\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(Mono.empty());\n\t\tOidcUser user = this.userService.loadUser(userRequest()).block();\n\t\tassertThat(user.getUserInfo()).isNull();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenOAuth2UserSubjectNullThenOAuth2AuthenticationException() {\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"),\n\t\t\t\tCollections.singletonMap(\"user\", \"rob\"), \"user\");\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(Mono.just(oauth2User));\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(userRequest()).block());\n\t}\n\n\t@Test\n\tpublic void loadUserWhenOAuth2UserSubjectNotEqualThenOAuth2AuthenticationException() {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(StandardClaimNames.SUB, \"not-equal\");\n\t\tattributes.put(\"user\", \"rob\");\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), attributes,\n\t\t\t\t\"user\");\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(Mono.just(oauth2User));\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(userRequest()).block());\n\t}\n\n\t@Test\n\tpublic void loadUserWhenOAuth2UserThenUserInfoNotNull() {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(StandardClaimNames.SUB, \"subject\");\n\t\tattributes.put(\"user\", \"rob\");\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), attributes,\n\t\t\t\t\"user\");\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(Mono.just(oauth2User));\n\t\tassertThat(this.userService.loadUser(userRequest()).block().getUserInfo()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenOAuth2UserAndUser() {\n\t\tthis.registration.userNameAttributeName(\"user\");\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(StandardClaimNames.SUB, \"subject\");\n\t\tattributes.put(\"user\", \"rob\");\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), attributes,\n\t\t\t\t\"user\");\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(Mono.just(oauth2User));\n\t\tassertThat(this.userService.loadUser(userRequest()).block().getName()).isEqualTo(\"rob\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenCustomClaimTypeConverterFactorySetThenApplied() {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(StandardClaimNames.SUB, \"subject\");\n\t\tattributes.put(\"user\", \"rob\");\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), attributes,\n\t\t\t\t\"user\");\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(Mono.just(oauth2User));\n\t\tOidcUserRequest userRequest = userRequest();\n\t\tFunction<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> customClaimTypeConverterFactory = mock(\n\t\t\t\tFunction.class);\n\t\tthis.userService.setClaimTypeConverterFactory(customClaimTypeConverterFactory);\n\t\tgiven(customClaimTypeConverterFactory.apply(same(userRequest.getClientRegistration())))\n\t\t\t.willReturn(new ClaimTypeConverter(OidcReactiveOAuth2UserService.createDefaultClaimTypeConverters()));\n\t\tthis.userService.loadUser(userRequest).block().getUserInfo();\n\t\tverify(customClaimTypeConverterFactory).apply(same(userRequest.getClientRegistration()));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenCustomRetrieveUserInfoSetThenUsed() {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(StandardClaimNames.SUB, \"subject\");\n\t\tattributes.put(\"user\", \"steve\");\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"), attributes,\n\t\t\t\t\"user\");\n\t\tgiven(this.oauth2UserService.loadUser(any())).willReturn(Mono.just(oauth2User));\n\t\tPredicate<OidcUserRequest> customRetrieveUserInfo = mock(Predicate.class);\n\t\tthis.userService.setRetrieveUserInfo(customRetrieveUserInfo);\n\t\tgiven(customRetrieveUserInfo.test(any(OidcUserRequest.class))).willReturn(true);\n\t\t// @formatter:off\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(\n\t\t\t\tthis.accessToken.getTokenType(),\n\t\t\t\tthis.accessToken.getTokenValue(),\n\t\t\t\tthis.accessToken.getIssuedAt(),\n\t\t\t\tthis.accessToken.getExpiresAt(),\n\t\t\t\tCollections.emptySet());\n\t\t// @formatter:on\n\t\tOidcUserRequest userRequest = new OidcUserRequest(this.registration.build(), accessToken, this.idToken);\n\t\tOidcUser oidcUser = this.userService.loadUser(userRequest).block();\n\t\tassertThat(oidcUser).isNotNull();\n\t\tassertThat(oidcUser.getUserInfo()).isNotNull();\n\t\tverify(customRetrieveUserInfo).test(userRequest);\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenContainsScopesThenIndividualScopeAuthorities() {\n\t\tOidcReactiveOAuth2UserService userService = new OidcReactiveOAuth2UserService();\n\t\tuserService.setRetrieveUserInfo((oidcUserRequest) -> false);\n\t\tOidcUserRequest request = new OidcUserRequest(TestClientRegistrations.clientRegistration().build(),\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"message:read\", \"message:write\"), TestOidcIdTokens.idToken().build());\n\t\tOidcUser user = userService.loadUser(request).block();\n\t\tassertThat(user.getAuthorities()).hasSize(3);\n\t\tIterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();\n\t\tassertThat(authorities.next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tassertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority(\"SCOPE_message:read\"));\n\t\tassertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenDoesNotContainScopesThenNoScopeAuthorities() {\n\t\tOidcReactiveOAuth2UserService userService = new OidcReactiveOAuth2UserService();\n\t\tuserService.setRetrieveUserInfo((oidcUserRequest) -> false);\n\t\tOidcUserRequest request = new OidcUserRequest(TestClientRegistrations.clientRegistration().build(),\n\t\t\t\tTestOAuth2AccessTokens.noScopes(), TestOidcIdTokens.idToken().build());\n\t\tOidcUser user = userService.loadUser(request).block();\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tIterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();\n\t\tassertThat(authorities.next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tOAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(\"OIDC_USER\");\n\t\tassertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());\n\t\tassertThat(userAuthority.getUserNameAttributeName()).isEqualTo(\"id\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenCustomOidcUserConverterSetThenUsed() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.userInfoUri(\"https://example.com/user\")\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(StandardClaimNames.SUB)\n\t\t\t.build();\n\t\tthis.accessToken = TestOAuth2AccessTokens.scopes(clientRegistration.getScopes().toArray(new String[0]));\n\t\tConverter<OidcUserSource, Mono<OidcUser>> oidcUserConverter = mock(Converter.class);\n\t\tString nameAttributeKey = IdTokenClaimNames.SUB;\n\t\tOidcUser actualUser = new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"a\", \"b\"), this.idToken,\n\t\t\t\tnameAttributeKey);\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(actualUser.getAuthorities(), actualUser.getClaims(),\n\t\t\t\tnameAttributeKey);\n\t\tReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2 = mock(ReactiveOAuth2UserService.class);\n\t\tgiven(oauth2.loadUser(any())).willReturn(Mono.just(oauth2User));\n\t\tgiven(oidcUserConverter.convert(any())).willReturn(Mono.just(actualUser));\n\t\tthis.userService.setOauth2UserService(oauth2);\n\t\tthis.userService.setOidcUserConverter(oidcUserConverter);\n\t\tOidcUserRequest userRequest = new OidcUserRequest(clientRegistration, this.accessToken, this.idToken);\n\t\tOidcUser user = this.userService.loadUser(userRequest).block();\n\t\tassertThat(user).isEqualTo(actualUser);\n\t\tArgumentCaptor<OidcUserSource> metadataCptr = ArgumentCaptor.forClass(OidcUserSource.class);\n\t\tverify(oidcUserConverter).convert(metadataCptr.capture());\n\t\tOidcUserSource metadata = metadataCptr.getValue();\n\t\tassertThat(metadata.getUserRequest()).isEqualTo(userRequest);\n\t\tassertThat(metadata.getOauth2User()).isEqualTo(oauth2User);\n\t\tassertThat(metadata.getUserInfo()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenNestedUserInfoSuccessThenReturnUser() throws IOException {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"user\\\": {\\\"user-name\\\": \\\"user1\\\"},\\n\"\n\t\t\t\t+ \"   \\\"sub\\\" : \\\"\" + this.idToken.getSubject() + \"\\\",\\n\"\n\t\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.start();\n\t\t\tenqueueApplicationJsonBody(server, userInfoResponse);\n\t\t\tString userInfoUri = server.url(\"/user\").toString();\n\t\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.userInfoUri(userInfoUri)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t\t.build();\n\t\t\tOidcReactiveOAuth2UserService userService = new OidcReactiveOAuth2UserService();\n\t\t\tDefaultReactiveOAuth2UserService oAuth2UserService = new DefaultReactiveOAuth2UserService();\n\t\t\toAuth2UserService.setAttributesConverter((request) -> (attributes) -> {\n\t\t\t\tMap<String, Object> user = (Map<String, Object>) attributes.get(\"user\");\n\t\t\t\tattributes.put(\"user-name\", user.get(\"user-name\"));\n\t\t\t\treturn attributes;\n\t\t\t});\n\t\t\tuserService.setOauth2UserService(oAuth2UserService);\n\t\t\tOAuth2User user = userService\n\t\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken))\n\t\t\t\t.block();\n\t\t\tassertThat(user.getName()).isEqualTo(\"user1\");\n\t\t\tassertThat(user.getAttributes()).hasSize(13);\n\t\t\tassertThat(((Map<?, ?>) user.getAttribute(\"user\")).get(\"user-name\")).isEqualTo(\"user1\");\n\t\t\tassertThat((String) user.getAttribute(\"first-name\")).isEqualTo(\"first\");\n\t\t\tassertThat((String) user.getAttribute(\"last-name\")).isEqualTo(\"last\");\n\t\t\tassertThat((String) user.getAttribute(\"middle-name\")).isEqualTo(\"middle\");\n\t\t\tassertThat((String) user.getAttribute(\"address\")).isEqualTo(\"address\");\n\t\t\tassertThat((String) user.getAttribute(\"email\")).isEqualTo(\"user1@example.com\");\n\t\t\tassertThat(user.getAuthorities()).hasSize(2);\n\t\t\tassertThat(user.getAuthorities().iterator().next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\t\tOAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();\n\t\t\tassertThat(userAuthority.getAuthority()).isEqualTo(\"OIDC_USER\");\n\t\t\tassertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());\n\t\t\tassertThat(userAuthority.getUserNameAttributeName()).isEqualTo(\"user-name\");\n\t\t}\n\t}\n\n\tprivate OidcUserRequest userRequest() {\n\t\treturn new OidcUserRequest(this.registration.build(), this.accessToken, this.idToken);\n\t}\n\n\tprivate void enqueueApplicationJsonBody(MockWebServer server, String json) {\n\t\tserver.enqueue(\n\t\t\t\tnew MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(json));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcUserRequest}.\n *\n * @author Joe Grandja\n */\npublic class OidcUserRequestTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate OidcIdToken idToken;\n\n\tprivate Map<String, Object> additionalParameters;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\", Instant.now(),\n\t\t\t\tInstant.now().plusSeconds(60), new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tthis.idToken = TestOidcIdTokens.idToken().authorizedParty(this.clientRegistration.getClientId()).build();\n\t\tthis.additionalParameters = new HashMap<>();\n\t\tthis.additionalParameters.put(\"param1\", \"value1\");\n\t\tthis.additionalParameters.put(\"param2\", \"value2\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcUserRequest(null, this.accessToken, this.idToken));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAccessTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcUserRequest(this.clientRegistration, null, this.idToken));\n\t}\n\n\t@Test\n\tpublic void constructorWhenIdTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OidcUserRequest(this.clientRegistration, this.accessToken, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOidcUserRequest userRequest = new OidcUserRequest(this.clientRegistration, this.accessToken, this.idToken,\n\t\t\t\tthis.additionalParameters);\n\t\tassertThat(userRequest.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(userRequest.getAccessToken()).isEqualTo(this.accessToken);\n\t\tassertThat(userRequest.getIdToken()).isEqualTo(this.idToken);\n\t\tassertThat(userRequest.getAdditionalParameters()).containsAllEntriesOf(this.additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class OidcUserRequestUtilsTests {\n\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration();\n\n\tOidcIdToken idToken = TestOidcIdTokens.idToken().build();\n\n\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\", Instant.now(),\n\t\t\tInstant.now().plus(Duration.ofDays(1)), Collections.singleton(\"read:user\"));\n\n\t@Test\n\tpublic void shouldRetrieveUserInfoWhenUserInfoUriThenTrue() {\n\t\tassertThat(OidcUserRequestUtils.shouldRetrieveUserInfo(userRequest())).isTrue();\n\t}\n\n\t@Test\n\tpublic void shouldRetrieveUserInfoWhenNoUserInfoUriThenFalse() {\n\t\tthis.registration.userInfoUri(null);\n\t\tassertThat(OidcUserRequestUtils.shouldRetrieveUserInfo(userRequest())).isFalse();\n\t}\n\n\t@Test\n\tpublic void shouldRetrieveUserInfoWhenNotAuthorizationCodeThenFalse() {\n\t\tthis.registration.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\tassertThat(OidcUserRequestUtils.shouldRetrieveUserInfo(userRequest())).isFalse();\n\t}\n\n\tprivate OidcUserRequest userRequest() {\n\t\treturn new OidcUserRequest(this.registration.build(), this.accessToken, this.idToken);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.userinfo;\n\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;\nimport org.springframework.security.oauth2.client.userinfo.OAuth2UserService;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OidcUserService}.\n *\n * @author Joe Grandja\n */\npublic class OidcUserServiceTests {\n\n\tprivate ClientRegistration.Builder clientRegistrationBuilder;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate OidcIdToken idToken;\n\n\tprivate OidcUserService userService = new OidcUserService();\n\n\tprivate MockWebServer server;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.clientRegistrationBuilder = TestClientRegistrations.clientRegistration()\n\t\t\t.userInfoUri(null)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(StandardClaimNames.SUB);\n\t\tthis.accessToken = TestOAuth2AccessTokens.scopes(OidcScopes.OPENID, OidcScopes.PROFILE);\n\t\tMap<String, Object> idTokenClaims = new HashMap<>();\n\t\tidTokenClaims.put(IdTokenClaimNames.ISS, \"https://provider.com\");\n\t\tidTokenClaims.put(IdTokenClaimNames.SUB, \"subject1\");\n\t\tthis.idToken = new OidcIdToken(\"access-token\", Instant.MIN, Instant.MAX, idTokenClaims);\n\t\tthis.userService.setOauth2UserService(new DefaultOAuth2UserService());\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void createDefaultClaimTypeConvertersWhenCalledThenDefaultsAreCorrect() {\n\t\tMap<String, Converter<Object, ?>> claimTypeConverters = OidcUserService.createDefaultClaimTypeConverters();\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.EMAIL_VERIFIED);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.PHONE_NUMBER_VERIFIED);\n\t\tassertThat(claimTypeConverters).containsKey(StandardClaimNames.UPDATED_AT);\n\t}\n\n\t@Test\n\tpublic void setOauth2UserServiceWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userService.setOauth2UserService(null));\n\t}\n\n\t@Test\n\tpublic void setClaimTypeConverterFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userService.setClaimTypeConverterFactory(null));\n\t}\n\n\t@Test\n\tpublic void setRetrieveUserInfoWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.userService.setRetrieveUserInfo(null))\n\t\t\t\t.withMessage(\"retrieveUserInfo cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setOidcUserConverterWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.userService.setOidcUserConverter(null))\n\t\t\t\t.withMessage(\"oidcUserConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userService.loadUser(null));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoUriIsNullThenUserInfoEndpointNotRequested() {\n\t\tOidcUser user = this.userService\n\t\t\t.loadUser(new OidcUserRequest(this.clientRegistrationBuilder.build(), this.accessToken, this.idToken));\n\t\tassertThat(user.getUserInfo()).isNull();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenCustomRetrieveUserInfoSetThenUsed() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tthis.accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tPredicate<OidcUserRequest> customRetrieveUserInfo = mock(Predicate.class);\n\t\tgiven(customRetrieveUserInfo.test(any(OidcUserRequest.class))).willReturn(true);\n\t\tthis.userService.setRetrieveUserInfo(customRetrieveUserInfo);\n\t\tOidcUser user = this.userService\n\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));\n\t\tassertThat(user.getUserInfo()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenCustomOidcUserConverterSetThenUsed() {\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(\"https://example.com/user\")\n\t\t\t.build();\n\t\tthis.accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tConverter<OidcUserSource, OidcUser> oidcUserConverter = mock(Converter.class);\n\t\tString nameAttributeKey = IdTokenClaimNames.SUB;\n\t\tOidcUser actualUser = new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"a\", \"b\"), this.idToken,\n\t\t\t\tnameAttributeKey);\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(actualUser.getAuthorities(), actualUser.getClaims(),\n\t\t\t\tnameAttributeKey);\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2 = mock(OAuth2UserService.class);\n\t\tgiven(oauth2.loadUser(any())).willReturn(oauth2User);\n\t\tgiven(oidcUserConverter.convert(any())).willReturn(actualUser);\n\t\tthis.userService.setOauth2UserService(oauth2);\n\t\tthis.userService.setOidcUserConverter(oidcUserConverter);\n\t\tOidcUserRequest userRequest = new OidcUserRequest(clientRegistration, this.accessToken, this.idToken);\n\t\tOidcUser user = this.userService.loadUser(userRequest);\n\t\tassertThat(user).isEqualTo(actualUser);\n\t\tArgumentCaptor<OidcUserSource> metadataCptr = ArgumentCaptor.forClass(OidcUserSource.class);\n\t\tverify(oidcUserConverter).convert(metadataCptr.capture());\n\t\tOidcUserSource metadata = metadataCptr.getValue();\n\t\tassertThat(metadata.getUserRequest()).isEqualTo(userRequest);\n\t\tassertThat(metadata.getOauth2User()).isEqualTo(oauth2User);\n\t\tassertThat(metadata.getUserInfo()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseThenReturnUser() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tOidcUser user = this.userService\n\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));\n\t\tassertThat(user.getIdToken()).isNotNull();\n\t\tassertThat(user.getUserInfo()).isNotNull();\n\t\tassertThat(user.getUserInfo().getClaims()).hasSize(6);\n\t\tassertThat(user.getIdToken()).isEqualTo(this.idToken);\n\t\tassertThat(user.getName()).isEqualTo(\"subject1\");\n\t\tassertThat(user.getUserInfo().getSubject()).isEqualTo(\"subject1\");\n\t\tassertThat(user.getUserInfo().getFullName()).isEqualTo(\"first last\");\n\t\tassertThat(user.getUserInfo().getGivenName()).isEqualTo(\"first\");\n\t\tassertThat(user.getUserInfo().getFamilyName()).isEqualTo(\"last\");\n\t\tassertThat(user.getUserInfo().getPreferredUsername()).isEqualTo(\"user1\");\n\t\tassertThat(user.getUserInfo().getEmail()).isEqualTo(\"user1@example.com\");\n\t\tassertThat(user.getAuthorities()).hasSize(3);\n\t\tassertThat(user.getAuthorities().iterator().next()).isInstanceOf(OidcUserAuthority.class);\n\t\tOidcUserAuthority userAuthority = (OidcUserAuthority) user.getAuthorities().iterator().next();\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(\"OIDC_USER\");\n\t\tassertThat(userAuthority.getIdToken()).isEqualTo(user.getIdToken());\n\t\tassertThat(userAuthority.getUserInfo()).isEqualTo(user.getUserInfo());\n\t}\n\n\t// gh-5447\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseAndUserInfoSubjectIsNullThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"email\\\": \\\"full_name@provider.com\\\",\\n\"\n\t\t\t\t+ \"   \\\"name\\\": \\\"full name\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userNameAttributeName(StandardClaimNames.EMAIL)\n\t\t\t.build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService\n\t\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)))\n\t\t\t.withMessageContaining(\"invalid_user_info_response\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseAndUserInfoSubjectNotSameAsIdTokenSubjectThenThrowOAuth2AuthenticationException() {\n\t\tString userInfoResponse = \"{\\n\" + \"\t\\\"sub\\\": \\\"other-subject\\\"\\n\" + \"}\\n\";\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService\n\t\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)))\n\t\t\t.withMessageContaining(\"invalid_user_info_response\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseInvalidThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\";\n\t\t\t// \"}\\n\"; // Make the JSON invalid/malformed\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService\n\t\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenServerErrorThenThrowOAuth2AuthenticationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(500));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService\n\t\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource: 500 Server Error\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoUriInvalidThenThrowOAuth2AuthenticationException() {\n\t\tString userInfoUri = \"https://invalid-provider.com/user\";\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService\n\t\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenCustomUserNameAttributeNameThenGetNameReturnsCustomUserName() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userNameAttributeName(StandardClaimNames.EMAIL)\n\t\t\t.build();\n\t\tOidcUser user = this.userService\n\t\t\t.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));\n\t\tassertThat(user.getName()).isEqualTo(\"user1@example.com\");\n\t}\n\n\t// gh-5294\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseThenAcceptHeaderJson() throws Exception {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tthis.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));\n\t\tassertThat(this.server.takeRequest(1, TimeUnit.SECONDS).getHeader(HttpHeaders.ACCEPT))\n\t\t\t.isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t}\n\n\t// gh-5500\n\t@Test\n\tpublic void loadUserWhenAuthenticationMethodHeaderSuccessResponseThenHttpMethodGet() throws Exception {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tthis.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));\n\t\tRecordedRequest request = this.server.takeRequest();\n\t\tassertThat(request.getMethod()).isEqualTo(HttpMethod.GET.name());\n\t\tassertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(request.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t// gh-5500\n\t@Test\n\tpublic void loadUserWhenAuthenticationMethodFormSuccessResponseThenHttpMethodPost() throws Exception {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t.build();\n\t\tthis.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));\n\t\tRecordedRequest request = this.server.takeRequest();\n\t\tassertThat(request.getMethod()).isEqualTo(HttpMethod.POST.name());\n\t\tassertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(request.getHeader(HttpHeaders.CONTENT_TYPE)).contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tassertThat(request.getBody().readUtf8()).isEqualTo(\"access_token=\" + this.accessToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void loadUserWhenCustomClaimTypeConverterFactorySetThenApplied() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tFunction<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> customClaimTypeConverterFactory = mock(\n\t\t\t\tFunction.class);\n\t\tthis.userService.setClaimTypeConverterFactory(customClaimTypeConverterFactory);\n\t\tgiven(customClaimTypeConverterFactory.apply(same(clientRegistration)))\n\t\t\t.willReturn(new ClaimTypeConverter(OidcUserService.createDefaultClaimTypeConverters()));\n\t\tthis.userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));\n\t\tverify(customClaimTypeConverterFactory).apply(same(clientRegistration));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenContainsScopesThenIndividualScopeAuthorities() {\n\t\tOidcUserService userService = new OidcUserService();\n\t\tuserService.setRetrieveUserInfo((req) -> false);\n\t\tOidcUserRequest request = new OidcUserRequest(TestClientRegistrations.clientRegistration().build(),\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"message:read\", \"message:write\"), TestOidcIdTokens.idToken().build());\n\t\tOidcUser user = userService.loadUser(request);\n\t\tassertThat(user.getAuthorities()).hasSize(3);\n\t\tIterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();\n\t\tassertThat(authorities.next()).isInstanceOf(OidcUserAuthority.class);\n\t\tassertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority(\"SCOPE_message:read\"));\n\t\tassertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenDoesNotContainScopesThenNoScopeAuthorities() {\n\t\tOidcUserService userService = new OidcUserService();\n\t\tOidcUserRequest request = new OidcUserRequest(this.clientRegistrationBuilder.build(),\n\t\t\t\tTestOAuth2AccessTokens.noScopes(), this.idToken);\n\t\tOidcUser user = userService.loadUser(request);\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tIterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();\n\t\tassertThat(authorities.next()).isInstanceOf(OidcUserAuthority.class);\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenDoesNotContainScopesAndUserInfoUriThenUserInfoRequested() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"sub\\\": \\\"subject1\\\",\\n\"\n\t\t\t\t+ \"   \\\"name\\\": \\\"first last\\\",\\n\"\n\t\t\t\t+ \"   \\\"given_name\\\": \\\"first\\\",\\n\"\n\t\t\t\t+ \"   \\\"family_name\\\": \\\"last\\\",\\n\"\n\t\t\t\t+ \"   \\\"preferred_username\\\": \\\"user1\\\",\\n\"\n\t\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();\n\t\tOidcUserService userService = new OidcUserService();\n\t\tOidcUserRequest request = new OidcUserRequest(clientRegistration, TestOAuth2AccessTokens.noScopes(),\n\t\t\t\tthis.idToken);\n\t\tOidcUser user = userService.loadUser(request);\n\t\tassertThat(user.getUserInfo()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenNestedUserInfoSuccessThenReturnUser() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"user\\\": {\\\"user-name\\\": \\\"user1\\\"},\\n\"\n\t\t\t\t+ \"   \\\"sub\\\" : \\\"subject1\\\",\\n\"\n\t\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tOidcUserService userService = new OidcUserService();\n\t\tDefaultOAuth2UserService oAuth2UserService = new DefaultOAuth2UserService();\n\t\toAuth2UserService.setAttributesConverter((request) -> (attributes) -> {\n\t\t\tMap<String, Object> user = (Map<String, Object>) attributes.get(\"user\");\n\t\t\tattributes.put(\"user-name\", user.get(\"user-name\"));\n\t\t\treturn attributes;\n\t\t});\n\t\tuserService.setOauth2UserService(oAuth2UserService);\n\t\tOAuth2User user = userService.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));\n\t\tassertThat(user.getName()).isEqualTo(\"user1\");\n\t\tassertThat(user.getAttributes()).hasSize(9);\n\t\tassertThat(((Map<?, ?>) user.getAttribute(\"user\")).get(\"user-name\")).isEqualTo(\"user1\");\n\t\tassertThat((String) user.getAttribute(\"first-name\")).isEqualTo(\"first\");\n\t\tassertThat((String) user.getAttribute(\"last-name\")).isEqualTo(\"last\");\n\t\tassertThat((String) user.getAttribute(\"middle-name\")).isEqualTo(\"middle\");\n\t\tassertThat((String) user.getAttribute(\"address\")).isEqualTo(\"address\");\n\t\tassertThat((String) user.getAttribute(\"email\")).isEqualTo(\"user1@example.com\");\n\t\tassertThat(user.getAuthorities()).hasSize(3);\n\t\tassertThat(user.getAuthorities().iterator().next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tOAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(\"OIDC_USER\");\n\t\tassertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());\n\t\tassertThat(userAuthority.getUserNameAttributeName()).isEqualTo(\"user-name\");\n\t}\n\n\tprivate MockResponse jsonResponse(String json) {\n\t\t// @formatter:off\n\t\treturn new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setBody(json);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/web/logout/OidcClientInitiatedLogoutSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.web.logout;\n\nimport java.io.IOException;\nimport java.util.Collections;\n\nimport jakarta.servlet.ServletException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OidcClientInitiatedLogoutSuccessHandler}\n */\n@ExtendWith(MockitoExtension.class)\npublic class OidcClientInitiatedLogoutSuccessHandlerTests {\n\n\t// @formatter:off\n\tClientRegistration registration = TestClientRegistrations\n\t\t\t.clientRegistration()\n\t\t\t.providerConfigurationMetadata(Collections.singletonMap(\"end_session_endpoint\", \"https://endpoint\"))\n\t\t\t.build();\n\t// @formatter:on\n\n\tClientRegistrationRepository repository = new InMemoryClientRegistrationRepository(this.registration);\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tOidcClientInitiatedLogoutSuccessHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.handler = new OidcClientInitiatedLogoutSuccessHandler(this.repository);\n\t}\n\n\t@Test\n\tpublic void logoutWhenOidcRedirectUrlConfiguredThenRedirects() throws IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tthis.request.setUserPrincipal(token);\n\t\tthis.handler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\"https://endpoint?id_token_hint=id-token\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenNotOAuth2AuthenticationThenDefaults() throws IOException, ServletException {\n\t\tAuthentication token = mock(Authentication.class);\n\t\tthis.request.setUserPrincipal(token);\n\t\tthis.handler.setDefaultTargetUrl(\"https://default\");\n\t\tthis.handler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\"https://default\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenNotOidcUserThenDefaults() throws IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOAuth2Users.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tthis.request.setUserPrincipal(token);\n\t\tthis.handler.setDefaultTargetUrl(\"https://default\");\n\t\tthis.handler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\"https://default\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenClientRegistrationHasNoEndSessionEndpointThenDefaults() throws Exception {\n\t\tClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\t\tClientRegistrationRepository repository = new InMemoryClientRegistrationRepository(registration);\n\t\tOidcClientInitiatedLogoutSuccessHandler handler = new OidcClientInitiatedLogoutSuccessHandler(repository);\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, registration.getRegistrationId());\n\t\tthis.request.setUserPrincipal(token);\n\t\thandler.setDefaultTargetUrl(\"https://default\");\n\t\thandler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\"https://default\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutBaseUrlRedirectUriTemplateThenBuildsItForRedirect()\n\t\t\tthrows IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tthis.handler.setPostLogoutRedirectUri(\"{baseUrl}\");\n\t\tthis.request.setScheme(\"https\");\n\t\tthis.request.setServerPort(443);\n\t\tthis.request.setServerName(\"rp.example.org\");\n\t\tthis.request.setUserPrincipal(token);\n\t\tthis.handler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\n\t\t\t\t\"https://endpoint?\" + \"id_token_hint=id-token&\" + \"post_logout_redirect_uri=https://rp.example.org\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutRedirectUriTemplateThenBuildsItForRedirect()\n\t\t\tthrows IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tthis.handler.setPostLogoutRedirectUri(\"{baseScheme}://{baseHost}{basePort}{basePath}\");\n\t\tthis.request.setScheme(\"https\");\n\t\tthis.request.setServerPort(443);\n\t\tthis.request.setServerName(\"rp.example.org\");\n\t\tthis.request.setUserPrincipal(token);\n\t\tthis.handler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\n\t\t\t\t\"https://endpoint?\" + \"id_token_hint=id-token&\" + \"post_logout_redirect_uri=https://rp.example.org\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutRedirectUriTemplateWithOtherPortThenBuildsItForRedirect()\n\t\t\tthrows IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tthis.handler.setPostLogoutRedirectUri(\"{baseScheme}://{baseHost}{basePort}{basePath}\");\n\t\tthis.request.setScheme(\"https\");\n\t\tthis.request.setServerPort(400);\n\t\tthis.request.setServerName(\"rp.example.org\");\n\t\tthis.request.setUserPrincipal(token);\n\t\tthis.handler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\"https://endpoint?\" + \"id_token_hint=id-token&\"\n\t\t\t\t+ \"post_logout_redirect_uri=https://rp.example.org:400\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutRedirectUriTemplateThenBuildsItForRedirectExpanded()\n\t\t\tthrows IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tthis.handler.setPostLogoutRedirectUri(\"{baseUrl}/{registrationId}\");\n\t\tthis.request.setScheme(\"https\");\n\t\tthis.request.setServerPort(443);\n\t\tthis.request.setServerName(\"rp.example.org\");\n\t\tthis.request.setUserPrincipal(token);\n\t\tthis.handler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(String.format(\n\t\t\t\t\"https://endpoint?\" + \"id_token_hint=id-token&\" + \"post_logout_redirect_uri=https://rp.example.org/%s\",\n\t\t\t\tthis.registration.getRegistrationId()));\n\t}\n\n\t// gh-9511\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutRedirectUriWithQueryParametersThenBuildsItForRedirect()\n\t\t\tthrows IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tthis.handler.setPostLogoutRedirectUri(\"https://rp.example.org/context?forwardUrl=secured%3Fparam%3Dtrue\");\n\t\tthis.request.setUserPrincipal(token);\n\t\tthis.handler.onLogoutSuccess(this.request, this.response, token);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\"https://endpoint?id_token_hint=id-token&\"\n\t\t\t\t+ \"post_logout_redirect_uri=https://rp.example.org/context?forwardUrl%3Dsecured%253Fparam%253Dtrue\");\n\t}\n\n\t@Test\n\tpublic void setPostLogoutRedirectUriTemplateWhenGivenNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setPostLogoutRedirectUri((String) null));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/web/server/logout/OidcClientInitiatedServerLogoutSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.oidc.web.server.logout;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.util.Collections;\nimport java.util.Objects;\n\nimport jakarta.servlet.ServletException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OidcClientInitiatedServerLogoutSuccessHandler}\n */\npublic class OidcClientInitiatedServerLogoutSuccessHandlerTests {\n\n\t// @formatter:off\n\tClientRegistration registration = TestClientRegistrations\n\t\t\t.clientRegistration()\n\t\t\t.providerConfigurationMetadata(Collections.singletonMap(\"end_session_endpoint\", \"https://endpoint\"))\n\t\t\t.build();\n\t// @formatter:on\n\n\tReactiveClientRegistrationRepository repository = new InMemoryReactiveClientRegistrationRepository(\n\t\t\tthis.registration);\n\n\tServerWebExchange exchange;\n\n\tWebFilterChain chain;\n\n\tOidcClientInitiatedServerLogoutSuccessHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.exchange = mock(ServerWebExchange.class);\n\t\tgiven(this.exchange.getResponse()).willReturn(new MockServerHttpResponse());\n\t\tgiven(this.exchange.getRequest()).willReturn(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.chain = mock(WebFilterChain.class);\n\t\tthis.handler = new OidcClientInitiatedServerLogoutSuccessHandler(this.repository);\n\t}\n\n\t@Test\n\tpublic void logoutWhenOidcRedirectUrlConfiguredThenRedirects() {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\tthis.handler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\"https://endpoint?id_token_hint=id-token\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenNotOAuth2AuthenticationThenDefaults() {\n\t\tAuthentication token = mock(Authentication.class);\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\tthis.handler.setLogoutSuccessUrl(URI.create(\"https://default\"));\n\t\tthis.handler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\"https://default\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenNotOidcUserThenDefaults() {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOAuth2Users.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\tthis.handler.setLogoutSuccessUrl(URI.create(\"https://default\"));\n\t\tthis.handler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\"https://default\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenClientRegistrationHasNoEndSessionEndpointThenDefaults() {\n\t\tClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\t\tReactiveClientRegistrationRepository repository = new InMemoryReactiveClientRegistrationRepository(\n\t\t\t\tregistration);\n\t\tOidcClientInitiatedServerLogoutSuccessHandler handler = new OidcClientInitiatedServerLogoutSuccessHandler(\n\t\t\t\trepository);\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, registration.getRegistrationId());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\thandler.setLogoutSuccessUrl(URI.create(\"https://default\"));\n\t\thandler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\"https://default\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutBaseUrlRedirectUriTemplateThenBuildsItForRedirect()\n\t\t\tthrows IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"https://rp.example.org/\").build();\n\t\tgiven(this.exchange.getRequest()).willReturn(request);\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\tthis.handler.setPostLogoutRedirectUri(\"{baseUrl}\");\n\t\tthis.handler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\n\t\t\t\t\"https://endpoint?\" + \"id_token_hint=id-token&\" + \"post_logout_redirect_uri=https://rp.example.org\");\n\t}\n\n\t// gh-11379\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutRedirectUriWithQueryParametersThenBuildsItForRedirect() {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tthis.handler.setPostLogoutRedirectUri(\"https://rp.example.org/context?forwardUrl=secured%3Fparam%3Dtrue\");\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\tthis.handler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\"https://endpoint?id_token_hint=id-token&\"\n\t\t\t\t+ \"post_logout_redirect_uri=https://rp.example.org/context?forwardUrl%3Dsecured%253Fparam%253Dtrue\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutRedirectUriTemplateThenBuildsItForRedirect() {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"https://rp.example.org/\").build();\n\t\tgiven(this.exchange.getRequest()).willReturn(request);\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\tthis.handler.setPostLogoutRedirectUri(\"{baseScheme}://{baseHost}{basePort}{basePath}\");\n\t\tthis.handler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\n\t\t\t\t\"https://endpoint?\" + \"id_token_hint=id-token&\" + \"post_logout_redirect_uri=https://rp.example.org\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutRedirectUriTemplateWithOtherPortThenBuildsItForRedirect() {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"https://rp.example.org:400\").build();\n\t\tgiven(this.exchange.getRequest()).willReturn(request);\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\tthis.handler.setPostLogoutRedirectUri(\"{baseScheme}://{baseHost}{basePort}{basePath}\");\n\t\tthis.handler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\"https://endpoint?\" + \"id_token_hint=id-token&\"\n\t\t\t\t+ \"post_logout_redirect_uri=https://rp.example.org:400\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenUsingPostLogoutRedirectUriTemplateThenBuildsItForRedirectExpanded()\n\t\t\tthrows IOException, ServletException {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"https://rp.example.org/\").build();\n\t\tgiven(this.exchange.getRequest()).willReturn(request);\n\t\tWebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);\n\t\tthis.handler.setPostLogoutRedirectUri(\"{baseUrl}/{registrationId}\");\n\t\tthis.handler.onLogoutSuccess(f, token).block();\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(String.format(\n\t\t\t\t\"https://endpoint?\" + \"id_token_hint=id-token&\" + \"post_logout_redirect_uri=https://rp.example.org/%s\",\n\t\t\t\tthis.registration.getRegistrationId()));\n\t}\n\n\t@Test\n\tpublic void setPostLogoutRedirectUriTemplateWhenGivenNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setPostLogoutRedirectUri((String) null));\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomRedirectUriResolverSetThenRedirects() {\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tWebFilterExchange filterExchange = new WebFilterExchange(this.exchange, this.chain);\n\t\tgiven(this.exchange.getRequest())\n\t\t\t.willReturn(MockServerHttpRequest.get(\"/\").queryParam(\"location\", \"https://test.com\").build());\n\t\t// @formatter:off\n\t\tthis.handler.setRedirectUriResolver((params) -> Mono.just(\n\t\t\t\t\t\tObjects.requireNonNull(params.getServerWebExchange()\n\t\t\t\t\t\t\t\t.getRequest()\n\t\t\t\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t\t\t\t.getFirst(\"location\"))));\n\t\t// @formatter:on\n\t\tthis.handler.onLogoutSuccess(filterExchange, token).block();\n\n\t\tassertThat(redirectedUrl(this.exchange)).isEqualTo(\"https://test.com\");\n\t}\n\n\t@Test\n\tpublic void setRedirectStrategyWhenGivenNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setRedirectStrategy(null));\n\t}\n\n\t@Test\n\tpublic void logoutWhenCustomRedirectStrategySetThenCustomRedirectStrategyUsed() {\n\t\tServerRedirectStrategy redirectStrategy = mock(ServerRedirectStrategy.class);\n\t\tgiven(redirectStrategy.sendRedirect(any(), any())).willReturn(Mono.empty());\n\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());\n\t\tWebFilterExchange filterExchange = new WebFilterExchange(this.exchange, this.chain);\n\t\tgiven(this.exchange.getRequest())\n\t\t\t.willReturn(MockServerHttpRequest.get(\"/\").queryParam(\"location\", \"https://test.com\").build());\n\t\tthis.handler.setRedirectStrategy(redirectStrategy);\n\n\t\tthis.handler.onLogoutSuccess(filterExchange, token).block();\n\n\t\tverify(redirectStrategy, times(1)).sendRedirect(any(), any());\n\t}\n\n\tprivate String redirectedUrl(ServerWebExchange exchange) {\n\t\treturn exchange.getResponse().getHeaders().getFirst(\"Location\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ClientRegistration}.\n *\n * @author Joe Grandja\n */\npublic class ClientRegistrationTests {\n\n\tprivate static final String REGISTRATION_ID = \"registration-1\";\n\n\tprivate static final String CLIENT_ID = \"client-1\";\n\n\tprivate static final String CLIENT_SECRET = \"secret\";\n\n\tprivate static final String REDIRECT_URI = \"https://example.com\";\n\n\tprivate static final Set<String> SCOPES = Collections\n\t\t.unmodifiableSet(Stream.of(\"openid\", \"profile\", \"email\").collect(Collectors.toSet()));\n\n\tprivate static final String AUTHORIZATION_URI = \"https://provider.com/oauth2/authorization\";\n\n\tprivate static final String TOKEN_URI = \"https://provider.com/oauth2/token\";\n\n\tprivate static final String JWK_SET_URI = \"https://provider.com/oauth2/keys\";\n\n\tprivate static final String ISSUER_URI = \"https://provider.com\";\n\n\tprivate static final String CLIENT_NAME = \"Client 1\";\n\n\tprivate static final Map<String, Object> PROVIDER_CONFIGURATION_METADATA = Collections\n\t\t.unmodifiableMap(createProviderConfigurationMetadata());\n\n\tprivate static Map<String, Object> createProviderConfigurationMetadata() {\n\t\tMap<String, Object> configurationMetadata = new LinkedHashMap<>();\n\t\tconfigurationMetadata.put(\"config-1\", \"value-1\");\n\t\tconfigurationMetadata.put(\"config-2\", \"value-2\");\n\t\treturn configurationMetadata;\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationGrantTypeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t.authorizationGrantType(null)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantAllAttributesProvidedThenAllAttributesAreSet() {\n\t\t// @formatter:off\n\t\tClientRegistration registration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.issuerUri(ISSUER_URI)\n\t\t\t\t.providerConfigurationMetadata(PROVIDER_CONFIGURATION_METADATA)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(REGISTRATION_ID);\n\t\tassertThat(registration.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(registration.getClientSecret()).isEqualTo(CLIENT_SECRET);\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(registration.getRedirectUri()).isEqualTo(REDIRECT_URI);\n\t\tassertThat(registration.getScopes()).isEqualTo(SCOPES);\n\t\tassertThat(registration.getProviderDetails().getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);\n\t\tassertThat(registration.getProviderDetails().getTokenUri()).isEqualTo(TOKEN_URI);\n\t\tassertThat(registration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())\n\t\t\t.isEqualTo(AuthenticationMethod.FORM);\n\t\tassertThat(registration.getProviderDetails().getJwkSetUri()).isEqualTo(JWK_SET_URI);\n\t\tassertThat(registration.getProviderDetails().getIssuerUri()).isEqualTo(ISSUER_URI);\n\t\tassertThat(registration.getProviderDetails().getConfigurationMetadata())\n\t\t\t.isEqualTo(PROVIDER_CONFIGURATION_METADATA);\n\t\tassertThat(registration.getClientName()).isEqualTo(CLIENT_NAME);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tClientRegistration.withRegistrationId(null)\n\t\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantClientIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t\t.clientId(null)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t\t.build()\n\t\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantClientSecretIsNullThenDefaultToEmpty() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantClientAuthenticationMethodNotProvidedThenDefaultToBasic() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantClientAuthenticationMethodNotProvidedAndClientSecretNullThenDefaultToNone() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.NONE);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantClientAuthenticationMethodNotProvidedAndClientSecretBlankThenDefaultToNone() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(\" \")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.NONE);\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantRedirectUriIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t.redirectUri(null)\n\t\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t// gh-5494\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantScopeIsNullThenScopeNotRequired() {\n\t\t// @formatter:off\n\t\tClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope((String[]) null)\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantAuthorizationUriIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t\t.authorizationUri(null)\n\t\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantTokenUriIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t\t.tokenUri(null)\n\t\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantClientNameNotProvidedThenDefaultToRegistrationId() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantScopeDoesNotContainOpenidThenJwkSetUriNotRequired() {\n\t\t// @formatter:off\n\t\tClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(\"scope1\")\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t// gh-5494\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantScopeIsNullThenJwkSetUriNotRequired() {\n\t\t// @formatter:off\n\t\tClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantProviderConfigurationMetadataIsNullThenDefaultToEmpty() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t\t.providerConfigurationMetadata(null)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isNotNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeGrantProviderConfigurationMetadataEmptyThenIsEmpty() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t\t.providerConfigurationMetadata(Collections.emptyMap())\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isNotNull();\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void buildWhenOverrideRegistrationIdThenOverridden() {\n\t\tString overriddenId = \"override\";\n\t\t// @formatter:off\n\t\tClientRegistration registration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.registrationId(overriddenId)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.jwkSetUri(JWK_SET_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(overriddenId);\n\t}\n\n\t@Test\n\tpublic void buildWhenClientCredentialsGrantAllAttributesProvidedThenAllAttributesAreSet() {\n\t\t// @formatter:off\n\t\tClientRegistration registration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(REGISTRATION_ID);\n\t\tassertThat(registration.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(registration.getClientSecret()).isEqualTo(CLIENT_SECRET);\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\tassertThat(registration.getScopes()).isEqualTo(SCOPES);\n\t\tassertThat(registration.getProviderDetails().getTokenUri()).isEqualTo(TOKEN_URI);\n\t\tassertThat(registration.getClientName()).isEqualTo(CLIENT_NAME);\n\t}\n\n\t@Test\n\tpublic void buildWhenClientCredentialsGrantRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ClientRegistration.withRegistrationId(null)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenClientCredentialsGrantClientIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t.clientId(null)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void buildWhenClientCredentialsGrantClientSecretIsNullThenDefaultToEmpty() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void buildWhenClientCredentialsGrantClientAuthenticationMethodNotProvidedThenDefaultToBasic() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void buildWhenClientCredentialsGrantTokenUriIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t.tokenUri(null)\n\t\t\t.build());\n\t}\n\n\t// gh-6256\n\t@Test\n\tpublic void buildWhenScopesContainASpaceThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> TestClientRegistrations.clientCredentials().scope(\"openid profile email\").build());\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesContainAnInvalidCharacterThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> TestClientRegistrations.clientCredentials().scope(\"an\\\"invalid\\\"scope\").build());\n\t}\n\n\t@Test\n\tpublic void buildWhenCustomGrantAllAttributesProvidedThenAllAttributesAreSet() {\n\t\tAuthorizationGrantType customGrantType = new AuthorizationGrantType(\"CUSTOM\");\n\t\t// @formatter:off\n\t\tClientRegistration registration = ClientRegistration\n\t\t\t\t.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(customGrantType)\n\t\t\t\t.scope(SCOPES.toArray(new String[0]))\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.clientName(CLIENT_NAME)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(REGISTRATION_ID);\n\t\tassertThat(registration.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(registration.getClientSecret()).isEqualTo(CLIENT_SECRET);\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(customGrantType);\n\t\tassertThat(registration.getScopes()).isEqualTo(SCOPES);\n\t\tassertThat(registration.getProviderDetails().getTokenUri()).isEqualTo(TOKEN_URI);\n\t\tassertThat(registration.getClientName()).isEqualTo(CLIENT_NAME);\n\t}\n\n\t@Test\n\tpublic void buildWhenClientRegistrationProvidedThenMakesACopy() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tClientRegistration updated = ClientRegistration.withClientRegistration(clientRegistration).build();\n\t\tassertThat(clientRegistration.getScopes()).isEqualTo(updated.getScopes());\n\t\tassertThat(clientRegistration.getScopes()).isNotSameAs(updated.getScopes());\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata())\n\t\t\t.isEqualTo(updated.getProviderDetails().getConfigurationMetadata());\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata())\n\t\t\t.isNotSameAs(updated.getProviderDetails().getConfigurationMetadata());\n\t}\n\n\t@Test\n\tpublic void buildWhenClientRegistrationProvidedThenEachPropertyMatches() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tClientRegistration updated = ClientRegistration.withClientRegistration(clientRegistration).build();\n\t\tassertThat(clientRegistration.getRegistrationId()).isEqualTo(updated.getRegistrationId());\n\t\tassertThat(clientRegistration.getClientId()).isEqualTo(updated.getClientId());\n\t\tassertThat(clientRegistration.getClientSecret()).isEqualTo(updated.getClientSecret());\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(updated.getClientAuthenticationMethod());\n\t\tassertThat(clientRegistration.getAuthorizationGrantType()).isEqualTo(updated.getAuthorizationGrantType());\n\t\tassertThat(clientRegistration.getRedirectUri()).isEqualTo(updated.getRedirectUri());\n\t\tassertThat(clientRegistration.getScopes()).isEqualTo(updated.getScopes());\n\t\tClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();\n\t\tClientRegistration.ProviderDetails updatedProviderDetails = updated.getProviderDetails();\n\t\tassertThat(providerDetails.getAuthorizationUri()).isEqualTo(updatedProviderDetails.getAuthorizationUri());\n\t\tassertThat(providerDetails.getTokenUri()).isEqualTo(updatedProviderDetails.getTokenUri());\n\t\tClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();\n\t\tClientRegistration.ProviderDetails.UserInfoEndpoint updatedUserInfoEndpoint = updatedProviderDetails\n\t\t\t.getUserInfoEndpoint();\n\t\tassertThat(userInfoEndpoint.getUri()).isEqualTo(updatedUserInfoEndpoint.getUri());\n\t\tassertThat(userInfoEndpoint.getAuthenticationMethod())\n\t\t\t.isEqualTo(updatedUserInfoEndpoint.getAuthenticationMethod());\n\t\tassertThat(userInfoEndpoint.getUserNameAttributeName())\n\t\t\t.isEqualTo(updatedUserInfoEndpoint.getUserNameAttributeName());\n\t\tassertThat(providerDetails.getJwkSetUri()).isEqualTo(updatedProviderDetails.getJwkSetUri());\n\t\tassertThat(providerDetails.getIssuerUri()).isEqualTo(updatedProviderDetails.getIssuerUri());\n\t\tassertThat(providerDetails.getConfigurationMetadata())\n\t\t\t.isEqualTo(updatedProviderDetails.getConfigurationMetadata());\n\t\tassertThat(clientRegistration.getClientName()).isEqualTo(updated.getClientName());\n\t}\n\n\t@Test\n\tpublic void buildWhenClientRegistrationValuesOverriddenThenPropagated() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\t// @formatter:off\n\t\tClientRegistration updated = ClientRegistration.withClientRegistration(clientRegistration)\n\t\t\t\t.clientSecret(\"a-new-secret\")\n\t\t\t\t.scope(\"a-new-scope\")\n\t\t\t\t.providerConfigurationMetadata(Collections.singletonMap(\"a-new-config\", \"a-new-value\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientSecret()).isNotEqualTo(updated.getClientSecret());\n\t\tassertThat(updated.getClientSecret()).isEqualTo(\"a-new-secret\");\n\t\tassertThat(clientRegistration.getScopes()).doesNotContain(\"a-new-scope\");\n\t\tassertThat(updated.getScopes()).containsExactly(\"a-new-scope\");\n\t\tassertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).doesNotContainKey(\"a-new-config\")\n\t\t\t.doesNotContainValue(\"a-new-value\");\n\t\tassertThat(updated.getProviderDetails().getConfigurationMetadata()).containsOnlyKeys(\"a-new-config\")\n\t\t\t.containsValue(\"a-new-value\");\n\t}\n\n\t// gh-8903\n\t@Test\n\tpublic void buildWhenCustomClientAuthenticationMethodProvidedThenSet() {\n\t\tClientAuthenticationMethod clientAuthenticationMethod = new ClientAuthenticationMethod(\"tls_client_auth\");\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.clientAuthenticationMethod(clientAuthenticationMethod)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(clientRegistration.getClientAuthenticationMethod()).isEqualTo(clientAuthenticationMethod);\n\t}\n\n\t// gh-16382\n\t@Test\n\tvoid buildWhenDefaultClientSettingsThenDefaulted() {\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t.build();\n\n\t\t// should not be null\n\t\tassertThat(clientRegistration.getClientSettings()).isNotNull();\n\t\tassertThat(clientRegistration.getClientSettings().isRequireProofKey()).isTrue();\n\t}\n\n\t// gh-16382\n\t@Test\n\tvoid buildWhenNewAuthorizationCodeAndPkceThenBuilds() {\n\t\tClientRegistration.ClientSettings pkceEnabled = ClientRegistration.ClientSettings.builder()\n\t\t\t.requireProofKey(true)\n\t\t\t.build();\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSettings(pkceEnabled)\n\t\t\t.authorizationGrantType(new AuthorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()))\n\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t.build();\n\n\t\tassertThat(clientRegistration.getClientSettings().isRequireProofKey()).isTrue();\n\t}\n\n\t@Test\n\tvoid buildWhenNewAuthorizationCodeAndPkceDisabledThenBuilds() {\n\t\tClientRegistration.ClientSettings pkceDisabled = ClientRegistration.ClientSettings.builder()\n\t\t\t.requireProofKey(false)\n\t\t\t.build();\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSettings(pkceDisabled)\n\t\t\t.authorizationGrantType(new AuthorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()))\n\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t.build();\n\n\t\tassertThat(clientRegistration.getClientSettings().isRequireProofKey()).isFalse();\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"invalidPkceGrantTypes\")\n\tvoid buildWhenInvalidGrantTypeForPkceThenPkceDisabled(AuthorizationGrantType invalidGrantType) {\n\t\tClientRegistration.ClientSettings pkceEnabled = ClientRegistration.ClientSettings.builder()\n\t\t\t.requireProofKey(true)\n\t\t\t.build();\n\t\tClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.clientSettings(pkceEnabled)\n\t\t\t.authorizationGrantType(invalidGrantType)\n\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t.tokenUri(TOKEN_URI)\n\t\t\t.build();\n\n\t\tassertThat(clientRegistration.getClientSettings().isRequireProofKey()).isFalse();\n\t}\n\n\tstatic List<AuthorizationGrantType> invalidPkceGrantTypes() {\n\t\treturn Arrays.stream(AuthorizationGrantType.class.getFields())\n\t\t\t.filter((field) -> Modifier.isFinal(field.getModifiers())\n\t\t\t\t\t&& field.getType() == AuthorizationGrantType.class)\n\t\t\t.map((field) -> getStaticValue(field, AuthorizationGrantType.class))\n\t\t\t.filter((grantType) -> grantType != AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t// ensure works with .equals\n\t\t\t.map((grantType) -> new AuthorizationGrantType(grantType.getValue()))\n\t\t\t.collect(Collectors.toList());\n\t}\n\n\tprivate static <T> T getStaticValue(Field field, Class<T> clazz) {\n\t\ttry {\n\t\t\treturn (T) field.get(null);\n\t\t}\n\t\tcatch (IllegalAccessException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.net.URI;\nimport java.util.Arrays;\nimport java.util.Map;\n\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.web.util.UriComponents;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * @author Rob Winch\n * @author Rafiullah Hamedy\n * @author Evgeniy Cheban\n * @since 5.1\n */\npublic class ClientRegistrationsTests {\n\n\t/**\n\t * Contains all optional parameters that are found in ClientRegistration\n\t */\n\t// @formatter:off\n\tprivate static final String DEFAULT_RESPONSE = \"{\\n\"\n\t\t\t+ \"    \\\"authorization_endpoint\\\": \\\"https://example.com/o/oauth2/v2/auth\\\", \\n\"\n\t\t\t+ \"    \\\"claims_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"aud\\\", \\n\"\n\t\t\t+ \"        \\\"email\\\", \\n\"\n\t\t\t+ \"        \\\"email_verified\\\", \\n\"\n\t\t\t+ \"        \\\"exp\\\", \\n\"\n\t\t\t+ \"        \\\"family_name\\\", \\n\"\n\t\t\t+ \"        \\\"given_name\\\", \\n\"\n\t\t\t+ \"        \\\"iat\\\", \\n\"\n\t\t\t+ \"        \\\"iss\\\", \\n\"\n\t\t\t+ \"        \\\"locale\\\", \\n\"\n\t\t\t+ \"        \\\"name\\\", \\n\"\n\t\t\t+ \"        \\\"picture\\\", \\n\"\n\t\t\t+ \"        \\\"sub\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"code_challenge_methods_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"plain\\\", \\n\"\n\t\t\t+ \"        \\\"S256\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"id_token_signing_alg_values_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"RS256\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"issuer\\\": \\\"https://example.com\\\", \\n\"\n\t\t\t+ \"    \\\"jwks_uri\\\": \\\"https://example.com/oauth2/v3/certs\\\", \\n\"\n\t\t\t+ \"    \\\"response_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"code\\\", \\n\"\n\t\t\t+ \"        \\\"token\\\", \\n\"\n\t\t\t+ \"        \\\"id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token\\\", \\n\"\n\t\t\t+ \"        \\\"code id_token\\\", \\n\"\n\t\t\t+ \"        \\\"token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"none\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"revocation_endpoint\\\": \\\"https://example.com/o/oauth2/revoke\\\", \\n\"\n\t\t\t+ \"    \\\"scopes_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"openid\\\", \\n\"\n\t\t\t+ \"        \\\"email\\\", \\n\"\n\t\t\t+ \"        \\\"profile\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"subject_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"public\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"grant_types_supported\\\" : [\\\"authorization_code\\\"], \\n\"\n\t\t\t+ \"    \\\"token_endpoint\\\": \\\"https://example.com/oauth2/v4/token\\\", \\n\"\n\t\t\t+ \"    \\\"token_endpoint_auth_methods_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"client_secret_post\\\", \\n\"\n\t\t\t+ \"        \\\"client_secret_basic\\\", \\n\"\n\t\t\t+ \"        \\\"none\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"userinfo_endpoint\\\": \\\"https://example.com/oauth2/v3/userinfo\\\"\\n\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate MockWebServer server;\n\n\tprivate JsonMapper mapper = new JsonMapper();\n\n\tprivate Map<String, Object> response;\n\n\tprivate String issuer;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.response = this.mapper.readValue(DEFAULT_RESPONSE, new TypeReference<Map<String, Object>>() {\n\t\t});\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void issuerWhenAllInformationThenSuccess() throws Exception {\n\t\tClientRegistration registration = registration(\"\").build();\n\t\tClientRegistration.ProviderDetails provider = registration.getProviderDetails();\n\t\tassertIssuerMetadata(registration, provider);\n\t\tassertThat(provider.getUserInfoEndpoint().getUri()).isEqualTo(\"https://example.com/oauth2/v3/userinfo\");\n\t}\n\n\t/**\n\t *\n\t * Test compatibility with OpenID v1 discovery endpoint by making a <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">OpenID\n\t * Provider Configuration Request</a> as highlighted\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-5\"> Compatibility Notes</a> of\n\t * <a href=\"https://tools.ietf.org/html/rfc8414\">RFC 8414</a> specification.\n\t */\n\t@Test\n\tpublic void issuerWhenOidcFallbackAllInformationThenSuccess() throws Exception {\n\t\tClientRegistration registration = registrationOidcFallback(\"issuer1\", null).build();\n\t\tClientRegistration.ProviderDetails provider = registration.getProviderDetails();\n\t\tassertIssuerMetadata(registration, provider);\n\t\tassertThat(provider.getUserInfoEndpoint().getUri()).isEqualTo(\"https://example.com/oauth2/v3/userinfo\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2AllInformationThenSuccess() throws Exception {\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\tClientRegistration.ProviderDetails provider = registration.getProviderDetails();\n\t\tassertIssuerMetadata(registration, provider);\n\t}\n\n\tprivate void assertIssuerMetadata(ClientRegistration registration, ClientRegistration.ProviderDetails provider) {\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(URI.create(this.issuer).getHost());\n\t\tassertThat(registration.getClientName()).isEqualTo(this.issuer);\n\t\tassertThat(registration.getScopes()).isEmpty();\n\t\tassertThat(provider.getAuthorizationUri()).isEqualTo(\"https://example.com/o/oauth2/v2/auth\");\n\t\tassertThat(provider.getTokenUri()).isEqualTo(\"https://example.com/oauth2/v4/token\");\n\t\tassertThat(provider.getJwkSetUri()).isEqualTo(\"https://example.com/oauth2/v3/certs\");\n\t\tassertThat(provider.getIssuerUri()).isEqualTo(this.issuer);\n\t\tassertThat(provider.getConfigurationMetadata()).containsKeys(\"authorization_endpoint\", \"claims_supported\",\n\t\t\t\t\"code_challenge_methods_supported\", \"id_token_signing_alg_values_supported\", \"issuer\", \"jwks_uri\",\n\t\t\t\t\"response_types_supported\", \"revocation_endpoint\", \"scopes_supported\", \"subject_types_supported\",\n\t\t\t\t\"grant_types_supported\", \"token_endpoint\", \"token_endpoint_auth_methods_supported\",\n\t\t\t\t\"userinfo_endpoint\");\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenResponseMissingJwksUriThenThrowsIllegalArgumentException() throws Exception {\n\t\tthis.response.remove(\"jwks_uri\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> registration(\"\").build());\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseMissingJwksUriThenThrowsIllegalArgumentException() throws Exception {\n\t\tthis.response.remove(\"jwks_uri\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> registrationOidcFallback(\"issuer1\", null).build());\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseMissingJwksUriThenThenSuccess() throws Exception {\n\t\tthis.response.remove(\"jwks_uri\");\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\tClientRegistration.ProviderDetails provider = registration.getProviderDetails();\n\t\tassertThat(provider.getJwkSetUri()).isNull();\n\t}\n\n\t// gh-8187\n\t@Test\n\tpublic void issuerWhenResponseMissingUserInfoUriThenSuccess() throws Exception {\n\t\tthis.response.remove(\"userinfo_endpoint\");\n\t\tClientRegistration registration = registration(\"\").build();\n\t\tassertThat(registration.getProviderDetails().getUserInfoEndpoint().getUri()).isNull();\n\t}\n\n\t@Test\n\tpublic void issuerWhenContainsTrailingSlashThenSuccess() throws Exception {\n\t\tassertThat(registration(\"\")).isNotNull();\n\t\tassertThat(this.issuer).endsWith(\"/\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackContainsTrailingSlashThenSuccess() throws Exception {\n\t\tassertThat(registrationOidcFallback(\"\", null)).isNotNull();\n\t\tassertThat(this.issuer).endsWith(\"/\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ContainsTrailingSlashThenSuccess() throws Exception {\n\t\tassertThat(registrationOAuth2(\"\", null)).isNotNull();\n\t\tassertThat(this.issuer).endsWith(\"/\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenGrantTypesSupportedNullThenDefaulted() throws Exception {\n\t\tthis.response.remove(\"grant_types_supported\");\n\t\tClientRegistration registration = registration(\"\").build();\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2GrantTypesSupportedNullThenDefaulted() throws Exception {\n\t\tthis.response.remove(\"grant_types_supported\");\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t}\n\n\t// gh-9828\n\t@Test\n\tpublic void issuerWhenImplicitGrantTypeThenSuccess() throws Exception {\n\t\tthis.response.put(\"grant_types_supported\", Arrays.asList(\"implicit\"));\n\t\tClientRegistration registration = registration(\"\").build();\n\t\t// The authorization_code grant type is still the default\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t}\n\n\t// gh-9828\n\t@Test\n\tpublic void issuerWhenOAuth2JwtBearerGrantTypeThenSuccess() throws Exception {\n\t\tthis.response.put(\"grant_types_supported\", Arrays.asList(\"urn:ietf:params:oauth:grant-type:jwt-bearer\"));\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\t// The authorization_code grant type is still the default\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t}\n\n\t// gh-9795\n\t@Test\n\tpublic void issuerWhenResponseAuthorizationEndpointIsNullThenSuccess() throws Exception {\n\t\tthis.response.put(\"grant_types_supported\", Arrays.asList(\"urn:ietf:params:oauth:grant-type:jwt-bearer\"));\n\t\tthis.response.remove(\"authorization_endpoint\");\n\t\tClientRegistration registration = registration(\"\").authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t.build();\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER);\n\t\tClientRegistration.ProviderDetails provider = registration.getProviderDetails();\n\t\tassertThat(provider.getAuthorizationUri()).isNull();\n\t}\n\n\t// gh-9795\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseAuthorizationEndpointIsNullThenSuccess() throws Exception {\n\t\tthis.response.put(\"grant_types_supported\", Arrays.asList(\"urn:ietf:params:oauth:grant-type:jwt-bearer\"));\n\t\tthis.response.remove(\"authorization_endpoint\");\n\t\tClientRegistration registration = registrationOAuth2(\"\", null)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t.build();\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER);\n\t\tClientRegistration.ProviderDetails provider = registration.getProviderDetails();\n\t\tassertThat(provider.getAuthorizationUri()).isNull();\n\t}\n\n\t@Test\n\tpublic void issuerWhenTokenEndpointAuthMethodsNullThenDefaulted() throws Exception {\n\t\tthis.response.remove(\"token_endpoint_auth_methods_supported\");\n\t\tClientRegistration registration = registration(\"\").build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2TokenEndpointAuthMethodsNullThenDefaulted() throws Exception {\n\t\tthis.response.remove(\"token_endpoint_auth_methods_supported\");\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t// gh-9780\n\t@Test\n\tpublic void issuerWhenClientSecretBasicAuthMethodThenMethodIsBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_basic\"));\n\t\tClientRegistration registration = registration(\"\").build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t// gh-9780\n\t@Test\n\tpublic void issuerWhenOAuth2ClientSecretBasicAuthMethodThenMethodIsBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_basic\"));\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void issuerWhenTokenEndpointAuthMethodsPostThenMethodIsPost() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_post\"));\n\t\tClientRegistration registration = registration(\"\").build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2TokenEndpointAuthMethodsPostThenMethodIsPost() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_post\"));\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t}\n\n\t// gh-9780\n\t@Test\n\tpublic void issuerWhenClientSecretJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_jwt\"));\n\t\tClientRegistration registration = registration(\"\").build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t// gh-9780\n\t@Test\n\tpublic void issuerWhenOAuth2ClientSecretJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_jwt\"));\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t// gh-9780\n\t@Test\n\tpublic void issuerWhenPrivateKeyJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"private_key_jwt\"));\n\t\tClientRegistration registration = registration(\"\").build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t// gh-9780\n\t@Test\n\tpublic void issuerWhenOAuth2PrivateKeyJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"private_key_jwt\"));\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void issuerWhenTokenEndpointAuthMethodsNoneThenMethodIsNone() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"none\"));\n\t\tClientRegistration registration = registration(\"\").build();\n\t\tassertThat(registration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.NONE);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2TokenEndpointAuthMethodsNoneThenMethodIsNone() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"none\"));\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\tassertThat(registration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.NONE);\n\t}\n\n\t// gh-9780\n\t@Test\n\tpublic void issuerWhenTlsClientAuthMethodThenSuccess() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"tls_client_auth\"));\n\t\tClientRegistration registration = registration(\"\").build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t// gh-9780\n\t@Test\n\tpublic void issuerWhenOAuth2TlsClientAuthMethodThenSuccess() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"tls_client_auth\"));\n\t\tClientRegistration registration = registrationOAuth2(\"\", null).build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2EmptyStringThenMeaningfulErrorMessage() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ClientRegistrations.fromIssuerLocation(\"\"))\n\t\t\t\t.withMessageContaining(\"issuer cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenEmptyStringThenMeaningfulErrorMessage() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ClientRegistrations.fromOidcIssuerLocation(\"\"))\n\t\t\t\t.withMessageContaining(\"issuer cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOpenIdConfigurationDoesNotMatchThenMeaningfulErrorMessage() throws Exception {\n\t\tthis.issuer = createIssuerFromServer(\"\");\n\t\tString body = this.mapper.writeValueAsString(this.response);\n\t\tMockResponse mockResponse = new MockResponse().setBody(body)\n\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\tthis.server.enqueue(mockResponse);\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> ClientRegistrations.fromOidcIssuerLocation(this.issuer))\n\t\t\t\t.withMessageContaining(\"The Issuer \\\"https://example.com\\\" provided in the configuration metadata did \"\n\t\t\t\t\t\t+ \"not match the requested issuer \\\"\" + this.issuer + \"\\\"\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ConfigurationDoesNotMatchThenMeaningfulErrorMessage() throws Exception {\n\t\tthis.issuer = createIssuerFromServer(\"\");\n\t\tString body = this.mapper.writeValueAsString(this.response);\n\t\tMockResponse mockResponse = new MockResponse().setBody(body)\n\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\tthis.server.enqueue(mockResponse);\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> ClientRegistrations.fromIssuerLocation(this.issuer))\n\t\t\t\t.withMessageContaining(\"The Issuer \\\"https://example.com\\\" provided in the configuration metadata \"\n\t\t\t\t\t\t+ \"did not match the requested issuer \\\"\" + this.issuer + \"\\\"\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationAllInformationThenSuccess() throws Exception {\n\t\tClientRegistration registration = registration(this.response).build();\n\t\tClientRegistration.ProviderDetails provider = registration.getProviderDetails();\n\t\tassertIssuerMetadata(registration, provider);\n\t\tassertThat(provider.getUserInfoEndpoint().getUri()).isEqualTo(\"https://example.com/oauth2/v3/userinfo\");\n\t}\n\n\tprivate ClientRegistration.Builder registration(Map<String, Object> configuration) {\n\t\tthis.issuer = \"https://example.com\";\n\t\treturn ClientRegistrations.fromOidcConfiguration(configuration)\n\t\t\t.clientId(\"client-id\")\n\t\t\t.clientSecret(\"client-secret\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationResponseMissingJwksUriThenThrowsIllegalArgumentException() throws Exception {\n\t\tthis.response.remove(\"jwks_uri\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> registration(this.response).build());\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationResponseMissingUserInfoUriThenSuccess() throws Exception {\n\t\tthis.response.remove(\"userinfo_endpoint\");\n\t\tClientRegistration registration = registration(this.response).build();\n\t\tassertThat(registration.getProviderDetails().getUserInfoEndpoint().getUri()).isNull();\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationGrantTypesSupportedNullThenDefaulted() throws Exception {\n\t\tthis.response.remove(\"grant_types_supported\");\n\t\tClientRegistration registration = registration(this.response).build();\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationImplicitGrantTypeThenSuccess() throws Exception {\n\t\tthis.response.put(\"grant_types_supported\", Arrays.asList(\"implicit\"));\n\t\tClientRegistration registration = registration(this.response).build();\n\t\t// The authorization_code grant type is still the default\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationResponseAuthorizationEndpointIsNullThenSuccess() throws Exception {\n\t\tthis.response.put(\"grant_types_supported\", Arrays.asList(\"urn:ietf:params:oauth:grant-type:jwt-bearer\"));\n\t\tthis.response.remove(\"authorization_endpoint\");\n\t\tClientRegistration registration = registration(this.response)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t.build();\n\t\tassertThat(registration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER);\n\t\tClientRegistration.ProviderDetails provider = registration.getProviderDetails();\n\t\tassertThat(provider.getAuthorizationUri()).isNull();\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationTokenEndpointAuthMethodsNullThenDefaulted() throws Exception {\n\t\tthis.response.remove(\"token_endpoint_auth_methods_supported\");\n\t\tClientRegistration registration = registration(this.response).build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationClientSecretBasicAuthMethodThenMethodIsBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_basic\"));\n\t\tClientRegistration registration = registration(this.response).build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationTokenEndpointAuthMethodsPostThenMethodIsPost() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_post\"));\n\t\tClientRegistration registration = registration(this.response).build();\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationClientSecretJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"client_secret_jwt\"));\n\t\tClientRegistration registration = registration(this.response).build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationPrivateKeyJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"private_key_jwt\"));\n\t\tClientRegistration registration = registration(this.response).build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationTokenEndpointAuthMethodsNoneThenMethodIsNone() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"none\"));\n\t\tClientRegistration registration = registration(this.response).build();\n\t\tassertThat(registration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.NONE);\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcConfigurationTlsClientAuthMethodThenSuccess() throws Exception {\n\t\tthis.response.put(\"token_endpoint_auth_methods_supported\", Arrays.asList(\"tls_client_auth\"));\n\t\tClientRegistration registration = registration(this.response).build();\n\t\t// The client_secret_basic auth method is still the default\n\t\tassertThat(registration.getClientAuthenticationMethod())\n\t\t\t.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);\n\t}\n\n\t// gh-15852\n\t@Test\n\tpublic void oidcWhenHostContainsUnderscoreThenRetains() {\n\t\tUriComponents oidc = ClientRegistrations.oidcUri(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oidc.getHost()).isEqualTo(\"elated_sutherland\");\n\t\tUriComponents oauth = ClientRegistrations.oauthUri(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oauth.getHost()).isEqualTo(\"elated_sutherland\");\n\t\tUriComponents oidcRfc8414 = ClientRegistrations.oidcRfc8414Uri(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oidcRfc8414.getHost()).isEqualTo(\"elated_sutherland\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenAllEndpointsFailedThenExceptionIncludesFailureInformation() {\n\t\tthis.issuer = createIssuerFromServer(\"issuer1\");\n\t\tthis.server.setDispatcher(new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\tint responseCode = switch (request.getPath()) {\n\t\t\t\t\tcase \"/issuer1/.well-known/openid-configuration\" -> 405;\n\t\t\t\t\tcase \"/.well-known/openid-configuration/issuer1\" -> 400;\n\t\t\t\t\tdefault -> 404;\n\t\t\t\t};\n\t\t\t\treturn new MockResponse().setResponseCode(responseCode);\n\t\t\t}\n\t\t});\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> ClientRegistrations.fromIssuerLocation(this.issuer).build())\n\t\t\t.withMessageContaining(\"405\")\n\t\t\t.withMessageContaining(\"400\")\n\t\t\t.withMessageContaining(\"404\");\n\t}\n\n\tprivate ClientRegistration.Builder registration(String path) throws Exception {\n\t\tthis.issuer = createIssuerFromServer(path);\n\t\tthis.response.put(\"issuer\", this.issuer);\n\t\tString body = this.mapper.writeValueAsString(this.response);\n\t\t// @formatter:off\n\t\tMockResponse mockResponse = new MockResponse()\n\t\t\t\t.setBody(body)\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE,\n\t\t\t\tMediaType.APPLICATION_JSON_VALUE);\n\t\tthis.server.enqueue(mockResponse);\n\t\treturn ClientRegistrations.fromOidcIssuerLocation(this.issuer)\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\");\n\t\t// @formatter:on\n\t}\n\n\tprivate ClientRegistration.Builder registrationOAuth2(String path, String body) throws Exception {\n\t\tthis.issuer = createIssuerFromServer(path);\n\t\tthis.response.put(\"issuer\", this.issuer);\n\t\tthis.issuer = this.server.url(path).toString();\n\t\tfinal String responseBody = (body != null) ? body : this.mapper.writeValueAsString(this.response);\n\t\tfinal Dispatcher dispatcher = new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\treturn switch (request.getPath()) {\n\t\t\t\t\tcase \"/.well-known/oauth-authorization-server/issuer1\",\n\t\t\t\t\t\t\t\"/.well-known/oauth-authorization-server/\" ->\n\t\t\t\t\t\tbuildSuccessMockResponse(responseBody);\n\t\t\t\t\tdefault -> new MockResponse().setResponseCode(404);\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\t\tthis.server.setDispatcher(dispatcher);\n\t\t// @formatter:off\n\t\treturn ClientRegistrations.fromIssuerLocation(this.issuer)\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\");\n\t\t// @formatter:on\n\t}\n\n\tprivate String createIssuerFromServer(String path) {\n\t\treturn this.server.url(path).toString();\n\t}\n\n\t/**\n\t * Simulates a situation when the ClientRegistration is used with a legacy application\n\t * where the OIDC Discovery Endpoint is \"/issuer1/.well-known/openid-configuration\"\n\t * instead of \"/.well-known/openid-configuration/issuer1\" in which case the first\n\t * attempt results in HTTP 404 and the subsequent call results in 200 OK.\n\t *\n\t * @see <a href=\"https://tools.ietf.org/html/rfc8414#section-5\">Section 5</a> for more\n\t * details.\n\t */\n\tprivate ClientRegistration.Builder registrationOidcFallback(String path, String body) throws Exception {\n\t\tthis.issuer = createIssuerFromServer(path);\n\t\tthis.response.put(\"issuer\", this.issuer);\n\t\tString responseBody = (body != null) ? body : this.mapper.writeValueAsString(this.response);\n\t\tfinal Dispatcher dispatcher = new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\treturn switch (request.getPath()) {\n\t\t\t\t\tcase \"/issuer1/.well-known/openid-configuration\", \"/.well-known/openid-configuration/\" ->\n\t\t\t\t\t\tbuildSuccessMockResponse(responseBody);\n\t\t\t\t\tdefault -> new MockResponse().setResponseCode(404);\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\t\tthis.server.setDispatcher(dispatcher);\n\t\treturn ClientRegistrations.fromIssuerLocation(this.issuer).clientId(\"client-id\").clientSecret(\"client-secret\");\n\t}\n\n\tprivate MockResponse buildSuccessMockResponse(String body) {\n\t\t// @formatter:off\n\t\treturn new MockResponse().setResponseCode(200)\n\t\t\t\t.setBody(body)\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/InMemoryClientRegistrationRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * Tests for {@link InMemoryClientRegistrationRepository}.\n *\n * @author Rob Winch\n * @author Vedran Pavic\n * @since 5.0\n */\npublic class InMemoryClientRegistrationRepositoryTests {\n\n\tprivate ClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\n\tprivate InMemoryClientRegistrationRepository clients = new InMemoryClientRegistrationRepository(this.registration);\n\n\t@Test\n\tpublic void constructorListClientRegistrationWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryClientRegistrationRepository((List<ClientRegistration>) null));\n\t}\n\n\t@Test\n\tpublic void constructorListClientRegistrationWhenEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryClientRegistrationRepository(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void constructorMapClientRegistrationWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryClientRegistrationRepository((Map<String, ClientRegistration>) null));\n\t}\n\n\t@Test\n\tpublic void constructorMapClientRegistrationWhenEmptyMapThenRepositoryIsEmpty() {\n\t\tInMemoryClientRegistrationRepository clients = new InMemoryClientRegistrationRepository(new HashMap<>());\n\t\tassertThat(clients).isEmpty();\n\t}\n\n\t@Test\n\tpublic void constructorListClientRegistrationWhenDuplicateIdThenIllegalArgumentException() {\n\t\tList<ClientRegistration> registrations = Arrays.asList(this.registration, this.registration);\n\t\tassertThatIllegalStateException().isThrownBy(() -> new InMemoryClientRegistrationRepository(registrations));\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenFoundThenFound() {\n\t\tString id = this.registration.getRegistrationId();\n\t\tassertThat(this.clients.findByRegistrationId(id)).isEqualTo(this.registration);\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenNotFoundThenNull() {\n\t\tString id = this.registration.getRegistrationId() + \"MISSING\";\n\t\tassertThat(this.clients.findByRegistrationId(id)).isNull();\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenNullIdThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.clients.findByRegistrationId(null));\n\t}\n\n\t@Test\n\tpublic void iteratorWhenRemoveThenThrowsUnsupportedOperationException() {\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(this.clients.iterator()::remove);\n\t}\n\n\t@Test\n\tpublic void iteratorWhenGetThenContainsAll() {\n\t\tassertThat(this.clients).containsOnly(this.registration);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.test.StepVerifier;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class InMemoryReactiveClientRegistrationRepositoryTests {\n\n\tprivate ClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\n\tprivate InMemoryReactiveClientRegistrationRepository repository;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.repository = new InMemoryReactiveClientRegistrationRepository(this.registration);\n\t}\n\n\t@Test\n\tpublic void constructorWhenZeroVarArgsThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new InMemoryReactiveClientRegistrationRepository());\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationArrayThenIllegalArgumentException() {\n\t\tClientRegistration[] registrations = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryReactiveClientRegistrationRepository(registrations));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationListThenIllegalArgumentException() {\n\t\tList<ClientRegistration> registrations = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryReactiveClientRegistrationRepository(registrations));\n\t}\n\n\t@Test\n\tpublic void constructorListClientRegistrationWhenDuplicateIdThenIllegalArgumentException() {\n\t\tList<ClientRegistration> registrations = Arrays.asList(this.registration, this.registration);\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> new InMemoryReactiveClientRegistrationRepository(registrations));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenIllegalArgumentException() {\n\t\tClientRegistration registration = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new InMemoryReactiveClientRegistrationRepository(registration));\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenValidIdThenFound() {\n\t\t// @formatter:off\n\t\tStepVerifier.create(this.repository.findByRegistrationId(this.registration.getRegistrationId()))\n\t\t\t\t.expectNext(this.registration)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenNotValidIdThenEmpty() {\n\t\tStepVerifier.create(this.repository.findByRegistrationId(this.registration.getRegistrationId() + \"invalid\"))\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void iteratorWhenContainsGithubThenContains() {\n\t\tassertThat(this.repository).containsOnly(this.registration);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/SupplierClientRegistrationRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport java.util.Collections;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link SupplierClientRegistrationRepository}.\n *\n * @author Justin Tay\n * @since 6.2\n */\n@ExtendWith(MockitoExtension.class)\npublic class SupplierClientRegistrationRepositoryTests {\n\n\tprivate ClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\n\tprivate SupplierClientRegistrationRepository clients = new SupplierClientRegistrationRepository(\n\t\t\t() -> new InMemoryClientRegistrationRepository(this.registration));\n\n\t@Mock\n\tSupplier<InMemoryClientRegistrationRepository> clientRegistrationRepositorySupplier;\n\n\t@Test\n\tpublic void constructorMapClientRegistrationWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SupplierClientRegistrationRepository(null));\n\t}\n\n\t@Test\n\tpublic void constructorMapClientRegistrationWhenEmptyMapThenRepositoryIsEmpty() {\n\t\tSupplierClientRegistrationRepository clients = new SupplierClientRegistrationRepository(\n\t\t\t\t() -> new InMemoryClientRegistrationRepository(Collections.emptyMap()));\n\t\tassertThat(clients).isEmpty();\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenFoundThenFound() {\n\t\tString id = this.registration.getRegistrationId();\n\t\tassertThat(this.clients.findByRegistrationId(id)).isEqualTo(this.registration);\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenNotFoundThenNull() {\n\t\tString id = this.registration.getRegistrationId() + \"MISSING\";\n\t\tassertThat(this.clients.findByRegistrationId(id)).isNull();\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenNullIdThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.clients.findByRegistrationId(null));\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdThenSingletonSupplierCached() {\n\t\tSupplierClientRegistrationRepository test = new SupplierClientRegistrationRepository(\n\t\t\t\tthis.clientRegistrationRepositorySupplier);\n\t\tgiven(this.clientRegistrationRepositorySupplier.get())\n\t\t\t.willReturn(new InMemoryClientRegistrationRepository(this.registration));\n\t\tString id = this.registration.getRegistrationId();\n\t\tassertThat(test.findByRegistrationId(id)).isEqualTo(this.registration);\n\t\tid = this.registration.getRegistrationId();\n\t\tassertThat(test.findByRegistrationId(id)).isEqualTo(this.registration);\n\t\tverify(this.clientRegistrationRepositorySupplier, times(1)).get();\n\t}\n\n\t@Test\n\tpublic void iteratorWhenRemoveThenThrowsUnsupportedOperationException() {\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(this.clients.iterator()::remove);\n\t}\n\n\t@Test\n\tpublic void iteratorWhenGetThenContainsAll() {\n\t\tassertThat(this.clients).containsOnly(this.registration);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/TestClientRegistrations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.registration;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic final class TestClientRegistrations {\n\n\tprivate TestClientRegistrations() {\n\t}\n\n\tpublic static ClientRegistration.Builder clientRegistration() {\n\t\t// @formatter:off\n\t\treturn ClientRegistration.withRegistrationId(\"registration-id\")\n\t\t\t\t.redirectUri(\"{baseUrl}/{action}/oauth2/code/{registrationId}\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.scope(\"read:user\")\n\t\t\t\t.authorizationUri(\"https://example.com/login/oauth/authorize\")\n\t\t\t\t.tokenUri(\"https://example.com/login/oauth/access_token\")\n\t\t\t\t.jwkSetUri(\"https://example.com/oauth2/jwk\")\n\t\t\t\t.issuerUri(\"https://example.com\")\n\t\t\t\t.userInfoUri(\"https://api.example.com/user\")\n\t\t\t\t.userNameAttributeName(\"id\")\n\t\t\t\t.clientName(\"Client Name\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\");\n\t\t// @formatter:on\n\t}\n\n\tpublic static ClientRegistration.Builder clientRegistration2() {\n\t\t// @formatter:off\n\t\treturn ClientRegistration.withRegistrationId(\"registration-id-2\")\n\t\t\t\t.redirectUri(\"{baseUrl}/{action}/oauth2/code/{registrationId}\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.scope(\"read:user\")\n\t\t\t\t.authorizationUri(\"https://example.com/login/oauth/authorize\")\n\t\t\t\t.tokenUri(\"https://example.com/login/oauth/access_token\")\n\t\t\t\t.userInfoUri(\"https://api.example.com/user\")\n\t\t\t\t.userNameAttributeName(\"id\")\n\t\t\t\t.clientName(\"Client Name\")\n\t\t\t\t.clientId(\"client-id-2\")\n\t\t\t\t.clientSecret(\"client-secret\");\n\t\t// @formatter:on\n\t}\n\n\tpublic static ClientRegistration.Builder clientCredentials() {\n\t\t// @formatter:off\n\t\treturn clientRegistration()\n\t\t\t\t.registrationId(\"client-credentials\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.web.client.RestOperations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.nullable;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link DefaultOAuth2UserService}.\n *\n * @author Joe Grandja\n * @author Eddú Meléndez\n */\npublic class DefaultOAuth2UserServiceTests {\n\n\tprivate ClientRegistration.Builder clientRegistrationBuilder;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate DefaultOAuth2UserService userService = new DefaultOAuth2UserService();\n\n\tprivate MockWebServer server;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\t// @formatter:off\n\t\tthis.clientRegistrationBuilder = TestClientRegistrations.clientRegistration()\n\t\t\t\t.userInfoUri(null)\n\t\t\t\t.userNameAttributeName(null);\n\t\t// @formatter:on\n\t\tthis.accessToken = TestOAuth2AccessTokens.noScopes();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void setRequestEntityConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userService.setRequestEntityConverter(null));\n\t}\n\n\t@Test\n\tpublic void setRestOperationsWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userService.setRestOperations(null));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userService.loadUser(null));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoUriIsNullThenThrowOAuth2AuthenticationException() {\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)))\n\t\t\t.withMessageContaining(\"missing_user_info_uri\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserNameAttributeNameIsNullThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder\n\t\t\t\t.userInfoUri(\"https://provider.com/user\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)))\n\t\t\t.withMessageContaining(\"missing_user_name_attribute\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseThenReturnUser() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"user-name\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tOAuth2User user = this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken));\n\t\tassertThat(user.getName()).isEqualTo(\"user1\");\n\t\tassertThat(user.getAttributes()).hasSize(6);\n\t\tassertThat((String) user.getAttribute(\"user-name\")).isEqualTo(\"user1\");\n\t\tassertThat((String) user.getAttribute(\"first-name\")).isEqualTo(\"first\");\n\t\tassertThat((String) user.getAttribute(\"last-name\")).isEqualTo(\"last\");\n\t\tassertThat((String) user.getAttribute(\"middle-name\")).isEqualTo(\"middle\");\n\t\tassertThat((String) user.getAttribute(\"address\")).isEqualTo(\"address\");\n\t\tassertThat((String) user.getAttribute(\"email\")).isEqualTo(\"user1@example.com\");\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tOAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(\"OAUTH2_USER\");\n\t\tassertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());\n\t\tassertThat(userAuthority.getUserNameAttributeName()).isEqualTo(\"user-name\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenNestedUserInfoSuccessThenReturnUser() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"user\\\": {\\\"user-name\\\": \\\"user1\\\"},\\n\"\n\t\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tDefaultOAuth2UserService userService = new DefaultOAuth2UserService();\n\t\tuserService.setAttributesConverter((request) -> (attributes) -> {\n\t\t\tMap<String, Object> user = (Map<String, Object>) attributes.get(\"user\");\n\t\t\tattributes.put(\"user-name\", user.get(\"user-name\"));\n\t\t\treturn attributes;\n\t\t});\n\t\tOAuth2User user = userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken));\n\t\tassertThat(user.getName()).isEqualTo(\"user1\");\n\t\tassertThat(user.getAttributes()).hasSize(7);\n\t\tassertThat(((Map<?, ?>) user.getAttribute(\"user\")).get(\"user-name\")).isEqualTo(\"user1\");\n\t\tassertThat((String) user.getAttribute(\"first-name\")).isEqualTo(\"first\");\n\t\tassertThat((String) user.getAttribute(\"last-name\")).isEqualTo(\"last\");\n\t\tassertThat((String) user.getAttribute(\"middle-name\")).isEqualTo(\"middle\");\n\t\tassertThat((String) user.getAttribute(\"address\")).isEqualTo(\"address\");\n\t\tassertThat((String) user.getAttribute(\"email\")).isEqualTo(\"user1@example.com\");\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tOAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(\"OAUTH2_USER\");\n\t\tassertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());\n\t\tassertThat(userAuthority.getUserNameAttributeName()).isEqualTo(\"user-name\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseInvalidThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"\t\\\"user-name\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\";\n\t\t// \"}\\n\"; // Make the JSON invalid/malformed\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoErrorResponseWwwAuthenticateHeaderThenThrowOAuth2AuthenticationException() {\n\t\tString wwwAuthenticateHeader = \"Bearer realm=\\\"auth-realm\\\" error=\\\"insufficient_scope\\\" error_description=\\\"The access token expired\\\"\";\n\t\tMockResponse response = new MockResponse();\n\t\tresponse.setHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticateHeader);\n\t\tresponse.setResponseCode(400);\n\t\tthis.server.enqueue(response);\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource\")\n\t\t\t.withMessageContaining(\"Error Code: insufficient_scope, Error Description: The access token expired\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoErrorResponseThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tString userInfoErrorResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"error\\\": \\\"invalid_token\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoErrorResponse).setResponseCode(400));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource\")\n\t\t\t.withMessageContaining(\"Error Code: invalid_token\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenServerErrorThenThrowOAuth2AuthenticationException() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(500));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource: 500 Server Error\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoUriInvalidThenThrowOAuth2AuthenticationException() {\n\t\tString userInfoUri = \"https://invalid-provider.com/user\";\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource\");\n\t}\n\n\t// gh-5294\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseThenAcceptHeaderJson() throws Exception {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"user-name\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tthis.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken));\n\t\tassertThat(this.server.takeRequest(1, TimeUnit.SECONDS).getHeader(HttpHeaders.ACCEPT))\n\t\t\t.isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t}\n\n\t// gh-5500\n\t@Test\n\tpublic void loadUserWhenAuthenticationMethodHeaderSuccessResponseThenHttpMethodGet() throws Exception {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"user-name\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tthis.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken));\n\t\tRecordedRequest request = this.server.takeRequest();\n\t\tassertThat(request.getMethod()).isEqualTo(HttpMethod.GET.name());\n\t\tassertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(request.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t// gh-5500\n\t@Test\n\tpublic void loadUserWhenAuthenticationMethodFormSuccessResponseThenHttpMethodPost() throws Exception {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"user-name\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tthis.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken));\n\t\tRecordedRequest request = this.server.takeRequest();\n\t\tassertThat(request.getMethod()).isEqualTo(HttpMethod.POST.name());\n\t\tassertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(request.getHeader(HttpHeaders.CONTENT_TYPE)).contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tassertThat(request.getBody().readUtf8()).isEqualTo(\"access_token=\" + this.accessToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenContainsScopesThenIndividualScopeAuthorities() {\n\t\tMap<String, Object> body = new HashMap<>();\n\t\tbody.put(\"id\", \"id\");\n\t\tDefaultOAuth2UserService userService = withMockResponse(body);\n\t\tOAuth2UserRequest request = new OAuth2UserRequest(TestClientRegistrations.clientRegistration().build(),\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"message:read\", \"message:write\"));\n\t\tOAuth2User user = userService.loadUser(request);\n\t\tassertThat(user.getAuthorities()).hasSize(3);\n\t\tIterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();\n\t\tassertThat(authorities.next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tassertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority(\"SCOPE_message:read\"));\n\t\tassertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenDoesNotContainScopesThenNoScopeAuthorities() {\n\t\tMap<String, Object> body = new HashMap<>();\n\t\tbody.put(\"id\", \"id\");\n\t\tDefaultOAuth2UserService userService = withMockResponse(body);\n\t\tOAuth2UserRequest request = new OAuth2UserRequest(TestClientRegistrations.clientRegistration().build(),\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tOAuth2User user = userService.loadUser(request);\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tIterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();\n\t\tassertThat(authorities.next()).isInstanceOf(OAuth2UserAuthority.class);\n\t}\n\n\t// gh-8764\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseInvalidContentTypeThenThrowOAuth2AuthenticationException() {\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tMockResponse response = new MockResponse();\n\t\tresponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);\n\t\tresponse.setBody(\"invalid content type\");\n\t\tthis.server.enqueue(response);\n\t\tClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource \"\n\t\t\t\t\t\t\t+ \"from '\" + userInfoUri + \"': response contains invalid content type 'text/plain'.\");\n\t}\n\n\t@Test\n\tpublic void setAttributesConverterWhenNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.userService.setAttributesConverter(null));\n\t}\n\n\tprivate DefaultOAuth2UserService withMockResponse(Map<String, Object> response) {\n\t\tResponseEntity<Map<String, Object>> responseEntity = new ResponseEntity<>(response, HttpStatus.OK);\n\t\tConverter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter = mock(Converter.class);\n\t\tRestOperations rest = mock(RestOperations.class);\n\t\tgiven(rest.exchange(nullable(RequestEntity.class), any(ParameterizedTypeReference.class)))\n\t\t\t.willReturn(responseEntity);\n\t\tDefaultOAuth2UserService userService = new DefaultOAuth2UserService();\n\t\tuserService.setRequestEntityConverter(requestEntityConverter);\n\t\tuserService.setRestOperations(rest);\n\t\treturn userService;\n\t}\n\n\tprivate MockResponse jsonResponse(String json) {\n\t\treturn new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(json);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\n\n/**\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 5.1\n */\npublic class DefaultReactiveOAuth2UserServiceTests {\n\n\tprivate ClientRegistration.Builder clientRegistration;\n\n\tprivate DefaultReactiveOAuth2UserService userService = new DefaultReactiveOAuth2UserService();\n\n\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\",\n\t\t\tInstant.now(), Instant.now().plus(Duration.ofDays(1)));\n\n\tprivate MockWebServer server;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\t// @formatter:off\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.userInfoUri(userInfoUri);\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserRequestIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2UserRequest request = null;\n\t\tStepVerifier.create(this.userService.loadUser(request)).expectError(IllegalArgumentException.class).verify();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoUriIsNullThenThrowOAuth2AuthenticationException() {\n\t\tthis.clientRegistration.userInfoUri(null);\n\t\tStepVerifier.create(this.userService.loadUser(oauth2UserRequest()))\n\t\t\t.expectErrorSatisfies((ex) -> assertThat(ex).isInstanceOf(OAuth2AuthenticationException.class)\n\t\t\t\t.hasMessageContaining(\"missing_user_info_uri\"))\n\t\t\t.verify();\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserNameAttributeNameIsNullThenThrowOAuth2AuthenticationException() {\n\t\tthis.clientRegistration.userNameAttributeName(null);\n\t\t// @formatter:off\n\t\tStepVerifier.create(this.userService.loadUser(oauth2UserRequest()))\n\t\t\t\t.expectErrorSatisfies((ex) -> assertThat(ex)\n\t\t\t\t\t\t.isInstanceOf(OAuth2AuthenticationException.class)\n\t\t\t\t\t\t.hasMessageContaining(\"missing_user_name_attribute\")\n\t\t\t\t)\n\t\t\t\t.verify();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseThenReturnUser() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"id\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tenqueueApplicationJsonBody(userInfoResponse);\n\t\tOAuth2User user = this.userService.loadUser(oauth2UserRequest()).block();\n\t\tassertThat(user.getName()).isEqualTo(\"user1\");\n\t\tassertThat(user.getAttributes()).hasSize(6);\n\t\tassertThat((String) user.getAttribute(\"id\")).isEqualTo(\"user1\");\n\t\tassertThat((String) user.getAttribute(\"first-name\")).isEqualTo(\"first\");\n\t\tassertThat((String) user.getAttribute(\"last-name\")).isEqualTo(\"last\");\n\t\tassertThat((String) user.getAttribute(\"middle-name\")).isEqualTo(\"middle\");\n\t\tassertThat((String) user.getAttribute(\"address\")).isEqualTo(\"address\");\n\t\tassertThat((String) user.getAttribute(\"email\")).isEqualTo(\"user1@example.com\");\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tOAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(\"OAUTH2_USER\");\n\t\tassertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());\n\t\tassertThat(userAuthority.getUserNameAttributeName()).isEqualTo(\"id\");\n\t}\n\n\t// gh-9336\n\t@Test\n\tpublic void loadUserWhenUserInfo201CreatedResponseThenReturnUser() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"id\\\": \\\"user1\\\",\\n\"\n\t\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(201)\n\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.setBody(userInfoResponse));\n\t\tassertThatNoException().isThrownBy(() -> this.userService.loadUser(oauth2UserRequest()).block());\n\t}\n\n\t@Test\n\tpublic void loadUserWhenNestedUserInfoSuccessThenReturnUser() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t\t+ \"   \\\"user\\\": {\\\"user-name\\\": \\\"user1\\\"},\\n\"\n\t\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tenqueueApplicationJsonBody(userInfoResponse);\n\t\tString userInfoUri = this.server.url(\"/user\").toString();\n\t\tClientRegistration clientRegistration = this.clientRegistration.userInfoUri(userInfoUri)\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)\n\t\t\t.userNameAttributeName(\"user-name\")\n\t\t\t.build();\n\t\tDefaultReactiveOAuth2UserService userService = new DefaultReactiveOAuth2UserService();\n\t\tuserService.setAttributesConverter((request) -> (attributes) -> {\n\t\t\tMap<String, Object> user = (Map<String, Object>) attributes.get(\"user\");\n\t\t\tattributes.put(\"user-name\", user.get(\"user-name\"));\n\t\t\treturn attributes;\n\t\t});\n\t\tOAuth2User user = userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)).block();\n\t\tassertThat(user.getName()).isEqualTo(\"user1\");\n\t\tassertThat(user.getAttributes()).hasSize(7);\n\t\tassertThat(((Map<?, ?>) user.getAttribute(\"user\")).get(\"user-name\")).isEqualTo(\"user1\");\n\t\tassertThat((String) user.getAttribute(\"first-name\")).isEqualTo(\"first\");\n\t\tassertThat((String) user.getAttribute(\"last-name\")).isEqualTo(\"last\");\n\t\tassertThat((String) user.getAttribute(\"middle-name\")).isEqualTo(\"middle\");\n\t\tassertThat((String) user.getAttribute(\"address\")).isEqualTo(\"address\");\n\t\tassertThat((String) user.getAttribute(\"email\")).isEqualTo(\"user1@example.com\");\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tOAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(\"OAUTH2_USER\");\n\t\tassertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());\n\t\tassertThat(userAuthority.getUserNameAttributeName()).isEqualTo(\"user-name\");\n\t}\n\n\t// gh-5500\n\t@Test\n\tpublic void loadUserWhenAuthenticationMethodHeaderSuccessResponseThenHttpMethodGet() throws Exception {\n\t\tthis.clientRegistration.userInfoAuthenticationMethod(AuthenticationMethod.HEADER);\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"id\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tenqueueApplicationJsonBody(userInfoResponse);\n\t\tthis.userService.loadUser(oauth2UserRequest()).block();\n\t\tRecordedRequest request = this.server.takeRequest();\n\t\tassertThat(request.getMethod()).isEqualTo(HttpMethod.GET.name());\n\t\tassertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(request.getHeader(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t// gh-5500\n\t@Test\n\tpublic void loadUserWhenAuthenticationMethodFormSuccessResponseThenHttpMethodPost() throws Exception {\n\t\tthis.clientRegistration.userInfoAuthenticationMethod(AuthenticationMethod.FORM);\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"   \\\"id\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tenqueueApplicationJsonBody(userInfoResponse);\n\t\tthis.userService.loadUser(oauth2UserRequest()).block();\n\t\tRecordedRequest request = this.server.takeRequest();\n\t\tassertThat(request.getMethod()).isEqualTo(HttpMethod.POST.name());\n\t\tassertThat(request.getHeader(HttpHeaders.ACCEPT)).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tassertThat(request.getHeader(HttpHeaders.CONTENT_TYPE)).contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\tassertThat(request.getBody().readUtf8()).isEqualTo(\"access_token=\" + this.accessToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseInvalidThenThrowOAuth2AuthenticationException() {\n\t\t// @formatter:off\n\t\tString userInfoResponse = \"{\\n\"\n\t\t\t+ \"\t\\\"id\\\": \\\"user1\\\",\\n\"\n\t\t\t+ \"   \\\"first-name\\\": \\\"first\\\",\\n\"\n\t\t\t+ \"   \\\"last-name\\\": \\\"last\\\",\\n\"\n\t\t\t+ \"   \\\"middle-name\\\": \\\"middle\\\",\\n\"\n\t\t\t+ \"   \\\"address\\\": \\\"address\\\",\\n\"\n\t\t\t+ \"   \\\"email\\\": \\\"user1@example.com\\\"\\n\";\n\t\t// \"}\\n\"; // Make the JSON invalid/malformed\n\t\t// @formatter:on\n\t\tenqueueApplicationJsonBody(userInfoResponse);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(oauth2UserRequest()).block())\n\t\t\t.withMessageContaining(\"invalid_user_info_response\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoErrorResponseThenThrowOAuth2AuthenticationException() {\n\t\tthis.server.enqueue(new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.setResponseCode(500)\n\t\t\t.setBody(\"{}\"));\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(oauth2UserRequest()).block())\n\t\t\t.withMessageContaining(\"invalid_user_info_response\");\n\t}\n\n\t@Test\n\tpublic void loadUserWhenUserInfoUriInvalidThenThrowOAuth2AuthenticationException() {\n\t\tthis.clientRegistration.userInfoUri(\"https://invalid-provider.com/user\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(oauth2UserRequest()).block());\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenContainsScopesThenIndividualScopeAuthorities() {\n\t\tMap<String, Object> body = new HashMap<>();\n\t\tbody.put(\"id\", \"id\");\n\t\tDefaultReactiveOAuth2UserService userService = withMockResponse(body);\n\t\tOAuth2UserRequest request = new OAuth2UserRequest(TestClientRegistrations.clientRegistration().build(),\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"message:read\", \"message:write\"));\n\t\tOAuth2User user = userService.loadUser(request).block();\n\t\tassertThat(user.getAuthorities()).hasSize(3);\n\t\tIterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();\n\t\tassertThat(authorities.next()).isInstanceOf(OAuth2UserAuthority.class);\n\t\tassertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority(\"SCOPE_message:read\"));\n\t\tassertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void loadUserWhenTokenDoesNotContainScopesThenNoScopeAuthorities() {\n\t\tMap<String, Object> body = new HashMap<>();\n\t\tbody.put(\"id\", \"id\");\n\t\tDefaultReactiveOAuth2UserService userService = withMockResponse(body);\n\t\tOAuth2UserRequest request = new OAuth2UserRequest(TestClientRegistrations.clientRegistration().build(),\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tOAuth2User user = userService.loadUser(request).block();\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tIterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();\n\t\tassertThat(authorities.next()).isInstanceOf(OAuth2UserAuthority.class);\n\t}\n\n\t// gh-8764\n\t@Test\n\tpublic void loadUserWhenUserInfoSuccessResponseInvalidContentTypeThenThrowOAuth2AuthenticationException() {\n\t\tMockResponse response = new MockResponse();\n\t\tresponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);\n\t\tresponse.setBody(\"invalid content type\");\n\t\tthis.server.enqueue(response);\n\t\tOAuth2UserRequest userRequest = oauth2UserRequest();\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.userService.loadUser(userRequest).block())\n\t\t\t.withMessageContaining(\"[invalid_user_info_response] An error occurred while attempting to \"\n\t\t\t\t\t+ \"retrieve the UserInfo Resource from '\"\n\t\t\t\t\t+ userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri() + \"': \"\n\t\t\t\t\t+ \"response contains invalid content type 'text/plain'\");\n\t}\n\n\t@Test\n\tpublic void setAttributesConverterWhenNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.userService.setAttributesConverter(null));\n\t}\n\n\tprivate DefaultReactiveOAuth2UserService withMockResponse(Map<String, Object> body) {\n\t\tWebClient real = WebClient.builder().build();\n\t\tWebClient.RequestHeadersUriSpec spec = spy(real.post());\n\t\tWebClient rest = spy(WebClient.class);\n\t\tWebClient.ResponseSpec clientResponse = mock(WebClient.ResponseSpec.class);\n\t\tgiven(rest.get()).willReturn(spec);\n\t\tgiven(spec.retrieve()).willReturn(clientResponse);\n\t\tgiven(clientResponse.onStatus(any(Predicate.class), any(Function.class))).willReturn(clientResponse);\n\t\tgiven(clientResponse.bodyToMono(any(ParameterizedTypeReference.class))).willReturn(Mono.just(body));\n\t\tDefaultReactiveOAuth2UserService userService = new DefaultReactiveOAuth2UserService();\n\t\tuserService.setWebClient(rest);\n\t\treturn userService;\n\t}\n\n\tprivate OAuth2UserRequest oauth2UserRequest() {\n\t\treturn new OAuth2UserRequest(this.clientRegistration.build(), this.accessToken);\n\t}\n\n\tprivate void enqueueApplicationJsonBody(String json) {\n\t\tthis.server.enqueue(\n\t\t\t\tnew MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(json));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DelegatingOAuth2UserServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.user.OAuth2User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link DelegatingOAuth2UserService}.\n *\n * @author Joe Grandja\n */\npublic class DelegatingOAuth2UserServiceTests {\n\n\t@Test\n\tpublic void constructorWhenUserServicesIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingOAuth2UserService<>(null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenUserServicesIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingOAuth2UserService<>(Collections.emptyList()));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void loadUserWhenUserRequestIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> userService = mock(OAuth2UserService.class);\n\t\tDelegatingOAuth2UserService<OAuth2UserRequest, OAuth2User> delegatingUserService = new DelegatingOAuth2UserService<>(\n\t\t\t\tArrays.asList(userService, userService));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> delegatingUserService.loadUser(null));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void loadUserWhenUserServiceCanLoadThenReturnUser() {\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> userService1 = mock(OAuth2UserService.class);\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> userService2 = mock(OAuth2UserService.class);\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> userService3 = mock(OAuth2UserService.class);\n\t\tOAuth2User mockUser = mock(OAuth2User.class);\n\t\tgiven(userService3.loadUser(any(OAuth2UserRequest.class))).willReturn(mockUser);\n\t\tDelegatingOAuth2UserService<OAuth2UserRequest, OAuth2User> delegatingUserService = new DelegatingOAuth2UserService<>(\n\t\t\t\tArrays.asList(userService1, userService2, userService3));\n\t\tOAuth2User loadedUser = delegatingUserService.loadUser(mock(OAuth2UserRequest.class));\n\t\tassertThat(loadedUser).isEqualTo(mockUser);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void loadUserWhenUserServiceCannotLoadThenReturnNull() {\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> userService1 = mock(OAuth2UserService.class);\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> userService2 = mock(OAuth2UserService.class);\n\t\tOAuth2UserService<OAuth2UserRequest, OAuth2User> userService3 = mock(OAuth2UserService.class);\n\t\tDelegatingOAuth2UserService<OAuth2UserRequest, OAuth2User> delegatingUserService = new DelegatingOAuth2UserService<>(\n\t\t\t\tArrays.asList(userService1, userService2, userService3));\n\t\tOAuth2User loadedUser = delegatingUserService.loadUser(mock(OAuth2UserRequest.class));\n\t\tassertThat(loadedUser).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/OAuth2UserRequestEntityConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.LinkedHashSet;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.MultiValueMap;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2UserRequestEntityConverter}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2UserRequestEntityConverterTests {\n\n\tprivate OAuth2UserRequestEntityConverter converter = new OAuth2UserRequestEntityConverter();\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void convertWhenAuthenticationMethodHeaderThenGetRequest() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2UserRequest userRequest = new OAuth2UserRequest(clientRegistration, this.createAccessToken());\n\t\tRequestEntity<?> requestEntity = this.converter.convert(userRequest);\n\t\tassertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(requestEntity.getUrl().toASCIIString())\n\t\t\t.isEqualTo(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri());\n\t\tHttpHeaders headers = requestEntity.getHeaders();\n\t\tassertThat(headers.getAccept()).contains(MediaType.APPLICATION_JSON);\n\t\tassertThat(headers.getFirst(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Bearer \" + userRequest.getAccessToken().getTokenValue());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void convertWhenAuthenticationMethodFormThenPostRequest() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.userInfoAuthenticationMethod(AuthenticationMethod.FORM)\n\t\t\t.build();\n\t\tOAuth2UserRequest userRequest = new OAuth2UserRequest(clientRegistration, this.createAccessToken());\n\t\tRequestEntity<?> requestEntity = this.converter.convert(userRequest);\n\t\tassertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST);\n\t\tassertThat(requestEntity.getUrl().toASCIIString())\n\t\t\t.isEqualTo(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri());\n\t\tHttpHeaders headers = requestEntity.getHeaders();\n\t\tassertThat(headers.getAccept()).contains(MediaType.APPLICATION_JSON);\n\t\tassertThat(headers.getContentType())\n\t\t\t.isEqualTo(MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE + \";charset=UTF-8\"));\n\t\tMultiValueMap<String, String> formParameters = (MultiValueMap<String, String>) requestEntity.getBody();\n\t\tassertThat(formParameters.getFirst(OAuth2ParameterNames.ACCESS_TOKEN))\n\t\t\t.isEqualTo(userRequest.getAccessToken().getTokenValue());\n\t}\n\n\tprivate OAuth2AccessToken createAccessToken() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\",\n\t\t\t\tInstant.now(), Instant.now().plusSeconds(3600), new LinkedHashSet<>(Arrays.asList(\"read\", \"write\")));\n\t\treturn accessToken;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/OAuth2UserRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.userinfo;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2UserRequest}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2UserRequestTests {\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprivate Map<String, Object> additionalParameters;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\t// @formatter:off\n\t\tthis.clientRegistration = ClientRegistration.withRegistrationId(\"registration-1\")\n\t\t\t\t.clientId(\"client-1\")\n\t\t\t\t.clientSecret(\"secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"https://client.com\")\n\t\t\t\t.scope(new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\")))\n\t\t\t\t.authorizationUri(\"https://provider.com/oauth2/authorization\")\n\t\t\t\t.tokenUri(\"https://provider.com/oauth2/token\")\n\t\t\t\t.clientName(\"Client 1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token-1234\", Instant.now(),\n\t\t\t\tInstant.now().plusSeconds(60), new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\")));\n\t\tthis.additionalParameters = new HashMap<>();\n\t\tthis.additionalParameters.put(\"param1\", \"value1\");\n\t\tthis.additionalParameters.put(\"param2\", \"value2\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2UserRequest(null, this.accessToken));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAccessTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2UserRequest(this.clientRegistration, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2UserRequest userRequest = new OAuth2UserRequest(this.clientRegistration, this.accessToken,\n\t\t\t\tthis.additionalParameters);\n\t\tassertThat(userRequest.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(userRequest.getAccessToken()).isEqualTo(this.accessToken);\n\t\tassertThat(userRequest.getAdditionalParameters()).containsAllEntriesOf(this.additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/AuthenticatedPrincipalOAuth2AuthorizedClientRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link AuthenticatedPrincipalOAuth2AuthorizedClientRepository}.\n *\n * @author Joe Grandja\n */\npublic class AuthenticatedPrincipalOAuth2AuthorizedClientRepositoryTests {\n\n\tprivate String registrationId = \"registrationId\";\n\n\tprivate String principalName = \"principalName\";\n\n\tprivate OAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate OAuth2AuthorizedClientRepository anonymousAuthorizedClientRepository;\n\n\tprivate AuthenticatedPrincipalOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientService = mock(OAuth2AuthorizedClientService.class);\n\t\tthis.anonymousAuthorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class);\n\t\tthis.authorizedClientRepository = new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(\n\t\t\t\tthis.authorizedClientService);\n\t\tthis.authorizedClientRepository\n\t\t\t.setAnonymousAuthorizedClientRepository(this.anonymousAuthorizedClientRepository);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientServiceIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientRepositoryWhenAuthorizedClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.setAnonymousAuthorizedClientRepository(null));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenAuthenticatedPrincipalThenLoadFromService() {\n\t\tAuthentication authentication = this.createAuthenticatedPrincipal();\n\t\tthis.authorizedClientRepository.loadAuthorizedClient(this.registrationId, authentication, this.request);\n\t\tverify(this.authorizedClientService).loadAuthorizedClient(this.registrationId, this.principalName);\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenAnonymousPrincipalThenLoadFromAnonymousRepository() {\n\t\tAuthentication authentication = this.createAnonymousPrincipal();\n\t\tthis.authorizedClientRepository.loadAuthorizedClient(this.registrationId, authentication, this.request);\n\t\tverify(this.anonymousAuthorizedClientRepository).loadAuthorizedClient(this.registrationId, authentication,\n\t\t\t\tthis.request);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthenticatedPrincipalThenSaveToService() {\n\t\tAuthentication authentication = this.createAuthenticatedPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, authentication, this.request,\n\t\t\t\tthis.response);\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(authorizedClient, authentication);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAnonymousPrincipalThenSaveToAnonymousRepository() {\n\t\tAuthentication authentication = this.createAnonymousPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, authentication, this.request,\n\t\t\t\tthis.response);\n\t\tverify(this.anonymousAuthorizedClientRepository).saveAuthorizedClient(authorizedClient, authentication,\n\t\t\t\tthis.request, this.response);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenAuthenticatedPrincipalThenRemoveFromService() {\n\t\tAuthentication authentication = this.createAuthenticatedPrincipal();\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId, authentication, this.request,\n\t\t\t\tthis.response);\n\t\tverify(this.authorizedClientService).removeAuthorizedClient(this.registrationId, this.principalName);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenAnonymousPrincipalThenRemoveFromAnonymousRepository() {\n\t\tAuthentication authentication = this.createAnonymousPrincipal();\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId, authentication, this.request,\n\t\t\t\tthis.response);\n\t\tverify(this.anonymousAuthorizedClientRepository).removeAuthorizedClient(this.registrationId, authentication,\n\t\t\t\tthis.request, this.response);\n\t}\n\n\tprivate Authentication createAuthenticatedPrincipal() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(this.principalName, \"password\");\n\t\tauthentication.setAuthenticated(true);\n\t\treturn authentication;\n\t}\n\n\tprivate Authentication createAnonymousPrincipal() {\n\t\treturn new AnonymousAuthenticationToken(\"key-1234\", \"anonymousUser\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/DefaultOAuth2AuthorizationRequestResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.Mockito;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.web.util.InvalidUrlException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * Tests for {@link DefaultOAuth2AuthorizationRequestResolver}.\n *\n * @author Joe Grandja\n */\npublic class DefaultOAuth2AuthorizationRequestResolverTests {\n\n\tprivate ClientRegistration registration1;\n\n\tprivate ClientRegistration registration2;\n\n\tprivate ClientRegistration pkceClientRegistration;\n\n\tprivate ClientRegistration fineRedirectUriTemplateRegistration;\n\n\tprivate ClientRegistration publicClientRegistration;\n\n\tprivate ClientRegistration oidcRegistration;\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate final String authorizationRequestBaseUri = \"/oauth2/authorization\";\n\n\tprivate DefaultOAuth2AuthorizationRequestResolver resolver;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registration1 = TestClientRegistrations.clientRegistration().build();\n\t\tthis.registration2 = TestClientRegistrations.clientRegistration2().build();\n\n\t\tthis.pkceClientRegistration = pkceClientRegistration().build();\n\t\tthis.fineRedirectUriTemplateRegistration = fineRedirectUriTemplateClientRegistration().build();\n\t\t// @formatter:off\n\t\tthis.publicClientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.registrationId(\"public-client-registration-id\")\n\t\t\t\t.clientId(\"public-client-id\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.build();\n\t\tthis.oidcRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t\t.registrationId(\"oidc-registration-id\")\n\t\t\t\t.scope(OidcScopes.OPENID)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.clientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1,\n\t\t\t\tthis.registration2, this.pkceClientRegistration, this.fineRedirectUriTemplateRegistration,\n\t\t\t\tthis.publicClientRegistration, this.oidcRegistration);\n\t\tthis.resolver = new DefaultOAuth2AuthorizationRequestResolver(this.clientRegistrationRepository,\n\t\t\t\tthis.authorizationRequestBaseUri);\n\t}\n\n\t@Test\n\tvoid authorizationRequestBaseUriEqualToRedirectFilter() {\n\t\tassertThat(DefaultOAuth2AuthorizationRequestResolver.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI)\n\t\t\t.isEqualTo(OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2AuthorizationRequestResolver(null, this.authorizationRequestBaseUri));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationRequestBaseUriIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2AuthorizationRequestResolver(this.clientRegistrationRepository, null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizationRequestCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.resolver.setAuthorizationRequestCustomizer(null));\n\t}\n\n\t@Test\n\tpublic void resolveWhenNotAuthorizationRequestThenDoesNotResolve() {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest).isNull();\n\t}\n\n\t// gh-8650\n\t@Test\n\tpublic void resolveWhenNotAuthorizationRequestThenRequestBodyNotConsumed() throws IOException {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = post(requestUri).build();\n\t\trequest.setContent(\"foo\".getBytes(StandardCharsets.UTF_8));\n\t\trequest.setCharacterEncoding(StandardCharsets.UTF_8.name());\n\t\tHttpServletRequest spyRequest = Mockito.spy(request);\n\t\tthis.resolver.resolve(spyRequest);\n\t\tMockito.verify(spyRequest, Mockito.never()).getReader();\n\t\tMockito.verify(spyRequest, Mockito.never()).getInputStream();\n\t\tMockito.verify(spyRequest, Mockito.never()).getParameter(ArgumentMatchers.anyString());\n\t\tMockito.verify(spyRequest, Mockito.never()).getParameterMap();\n\t\tMockito.verify(spyRequest, Mockito.never()).getParameterNames();\n\t\tMockito.verify(spyRequest, Mockito.never()).getParameterValues(ArgumentMatchers.anyString());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestWithInvalidClientThenThrowIllegalArgumentException() {\n\t\tClientRegistration clientRegistration = this.registration1;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId()\n\t\t\t\t+ \"-invalid\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t\t.withMessage(\"Invalid Client Registration with Id: \" + clientRegistration.getRegistrationId() + \"-invalid\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestWithValidClientThenResolves() {\n\t\tClientRegistration clientRegistration = this.registration1;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationUri())\n\t\t\t.isEqualTo(clientRegistration.getProviderDetails().getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"http://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(clientRegistration.getScopes());\n\t\tassertThat(authorizationRequest.getState()).isNotNull();\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.doesNotContainKey(OAuth2ParameterNames.REGISTRATION_ID);\n\t\tassertThat(authorizationRequest.getAttributes()).containsExactly(\n\t\t\t\tentry(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()),\n\t\t\t\tentry(PkceParameterNames.CODE_VERIFIER,\n\t\t\t\t\t\tauthorizationRequest.getAttributes().get(PkceParameterNames.CODE_VERIFIER)));\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenClientAuthorizationRequiredExceptionAvailableThenResolves() {\n\t\tClientRegistration clientRegistration = this.registration2;\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request,\n\t\t\t\tclientRegistration.getRegistrationId());\n\t\tassertThat(authorizationRequest).isNotNull();\n\t\tassertThat(authorizationRequest.getAttributes()).containsExactly(\n\t\t\t\tentry(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()),\n\t\t\t\tentry(PkceParameterNames.CODE_VERIFIER,\n\t\t\t\t\t\tauthorizationRequest.getAttributes().get(PkceParameterNames.CODE_VERIFIER)));\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestRedirectUriTemplatedThenRedirectUriExpanded() {\n\t\tClientRegistration clientRegistration = this.registration2;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getRedirectUri()).isNotEqualTo(clientRegistration.getRedirectUri());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"http://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestRedirectUriTemplatedThenHttpRedirectUriWithExtraVarsExpanded() {\n\t\tClientRegistration clientRegistration = this.fineRedirectUriTemplateRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(\"localhost:8080\" + requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getRedirectUri()).isNotEqualTo(clientRegistration.getRedirectUri());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"http://localhost:8080/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestRedirectUriTemplatedThenHttpsRedirectUriWithExtraVarsExpanded() {\n\t\tClientRegistration clientRegistration = this.fineRedirectUriTemplateRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(\"https://localhost:8081\" + requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getRedirectUri()).isNotEqualTo(clientRegistration.getRedirectUri());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"https://localhost:8081/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestIncludesPort80ThenExpandedRedirectUriWithExtraVarsExcludesPort() {\n\t\tClientRegistration clientRegistration = this.fineRedirectUriTemplateRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(\"http://localhost\" + requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getRedirectUri()).isNotEqualTo(clientRegistration.getRedirectUri());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"http://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestIncludesPort443ThenExpandedRedirectUriWithExtraVarsExcludesPort() {\n\t\tClientRegistration clientRegistration = this.fineRedirectUriTemplateRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(\"https://localhost:443\" + requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getRedirectUri()).isNotEqualTo(clientRegistration.getRedirectUri());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"https://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestHasNoPortThenInvalidUrlException() {\n\t\tClientRegistration clientRegistration = this.fineRedirectUriTemplateRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).port(-1).build();\n\t\tassertThatExceptionOfType(InvalidUrlException.class).isThrownBy(() -> this.resolver.resolve(request));\n\t}\n\n\t// gh-5520\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestRedirectUriTemplatedThenRedirectUriExpandedExcludesQueryString() {\n\t\tClientRegistration clientRegistration = this.registration2;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri + \"?foo=bar\").build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getRedirectUri()).isNotEqualTo(clientRegistration.getRedirectUri());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"http://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestIncludesPort80ThenExpandedRedirectUriExcludesPort() {\n\t\tClientRegistration clientRegistration = this.registration1;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id\"\n\t\t\t\t\t+ \"&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestIncludesPort443ThenExpandedRedirectUriExcludesPort() {\n\t\tClientRegistration clientRegistration = this.registration1;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(\"https://example.com:443\" + requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=https://example.com/login/oauth2/code/registration-id\"\n\t\t\t\t\t+ \"&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenClientAuthorizationRequiredExceptionAvailableThenRedirectUriIsAuthorize() {\n\t\tClientRegistration clientRegistration = this.registration1;\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request,\n\t\t\t\tclientRegistration.getRegistrationId());\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/authorize/oauth2/code/registration-id&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestOAuth2LoginThenRedirectUriIsLogin() {\n\t\tClientRegistration clientRegistration = this.registration2;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id-2&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id-2\"\n\t\t\t\t\t+ \"&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestHasActionParameterAuthorizeThenRedirectUriIsAuthorize() {\n\t\tClientRegistration clientRegistration = this.registration1;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).param(\"action\", \"authorize\").build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/authorize/oauth2/code/registration-id&\"\n\t\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestHasActionParameterLoginThenRedirectUriIsLogin() {\n\t\tClientRegistration clientRegistration = this.registration2;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).param(\"action\", \"login\").build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id-2&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id-2&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestWithValidPublicClientThenResolves() {\n\t\tClientRegistration clientRegistration = this.publicClientRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationUri())\n\t\t\t.isEqualTo(clientRegistration.getProviderDetails().getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"http://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(clientRegistration.getScopes());\n\t\tassertThat(authorizationRequest.getState()).isNotNull();\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.doesNotContainKey(OAuth2ParameterNames.REGISTRATION_ID);\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).containsKey(PkceParameterNames.CODE_CHALLENGE);\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.contains(entry(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\"));\n\t\tassertThat(authorizationRequest.getAttributes())\n\t\t\t.contains(entry(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()));\n\t\tassertThat(authorizationRequest.getAttributes()).containsKey(PkceParameterNames.CODE_VERIFIER);\n\t\tassertThat((String) authorizationRequest.getAttribute(PkceParameterNames.CODE_VERIFIER))\n\t\t\t.matches(\"^([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){128}$\");\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=public-client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/public-client-registration-id&\"\n\t\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\" + \"code_challenge_method=S256\");\n\t}\n\n\t// gh-6548\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestApplyPkceToConfidentialClientsThenApplied() {\n\t\tClientRegistration clientRegistration = this.registration1;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertPkceApplied(authorizationRequest, clientRegistration);\n\n\t\tclientRegistration = this.registration2;\n\t\trequestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\trequest = get(requestUri).build();\n\t\tauthorizationRequest = this.resolver.resolve(request);\n\t\tassertPkceApplied(authorizationRequest, clientRegistration);\n\t}\n\n\tprivate void assertPkceApplied(OAuth2AuthorizationRequest authorizationRequest,\n\t\t\tClientRegistration clientRegistration) {\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).containsKey(PkceParameterNames.CODE_CHALLENGE);\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.contains(entry(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\"));\n\t\tassertThat(authorizationRequest.getAttributes()).containsKey(PkceParameterNames.CODE_VERIFIER);\n\t\tassertThat((String) authorizationRequest.getAttribute(PkceParameterNames.CODE_VERIFIER))\n\t\t\t.matches(\"^([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){128}$\");\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&\" + \"client_id=\"\n\t\t\t\t\t+ clientRegistration.getClientId() + \"&\" + \"scope=read:user&\" + \"state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId() + \"&\"\n\t\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\" + \"code_challenge_method=S256\");\n\t}\n\n\tprivate void assertPkceNotApplied(OAuth2AuthorizationRequest authorizationRequest,\n\t\t\tClientRegistration clientRegistration) {\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).doesNotContainKey(PkceParameterNames.CODE_CHALLENGE);\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.doesNotContainKey(PkceParameterNames.CODE_CHALLENGE_METHOD);\n\t\tassertThat(authorizationRequest.getAttributes()).doesNotContainKey(PkceParameterNames.CODE_VERIFIER);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&\" + \"client_id=\"\n\t\t\t\t\t+ clientRegistration.getClientId() + \"&\" + \"scope=read:user&\" + \"state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthenticationRequestWithValidOidcClientThenResolves() {\n\t\tClientRegistration clientRegistration = this.oidcRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationUri())\n\t\t\t.isEqualTo(clientRegistration.getProviderDetails().getAuthorizationUri());\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(clientRegistration.getClientId());\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"http://localhost/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(clientRegistration.getScopes());\n\t\tassertThat(authorizationRequest.getState()).isNotNull();\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.doesNotContainKey(OAuth2ParameterNames.REGISTRATION_ID);\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).containsKey(OidcParameterNames.NONCE);\n\t\tassertThat(authorizationRequest.getAttributes())\n\t\t\t.contains(entry(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()));\n\t\tassertThat(authorizationRequest.getAttributes()).containsKey(OidcParameterNames.NONCE);\n\t\tassertThat((String) authorizationRequest.getAttribute(OidcParameterNames.NONCE))\n\t\t\t.matches(\"^([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){128}$\");\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=openid&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/oidc-registration-id&\"\n\t\t\t\t\t+ \"nonce=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t// gh-7696\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestCustomizerRemovesNonceThenQueryExcludesNonce() {\n\t\tClientRegistration clientRegistration = this.oidcRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tthis.resolver.setAuthorizationRequestCustomizer(\n\t\t\t\t(builder) -> builder.additionalParameters((params) -> params.remove(OidcParameterNames.NONCE))\n\t\t\t\t\t.attributes((attrs) -> attrs.remove(OidcParameterNames.NONCE)));\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).doesNotContainKey(OidcParameterNames.NONCE);\n\t\tassertThat(authorizationRequest.getAttributes()).doesNotContainKey(OidcParameterNames.NONCE);\n\t\tassertThat(authorizationRequest.getAttributes()).containsKey(OAuth2ParameterNames.REGISTRATION_ID);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=openid&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/oidc-registration-id&\"\n\t\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestCustomizerAddsParameterThenQueryIncludesParameter() {\n\t\tClientRegistration clientRegistration = this.oidcRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tthis.resolver.setAuthorizationRequestCustomizer((builder) -> builder.authorizationRequestUri((uriBuilder) -> {\n\t\t\turiBuilder.queryParam(\"param1\", \"value1\");\n\t\t\treturn uriBuilder.build();\n\t\t}));\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=openid&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/oidc-registration-id&\"\n\t\t\t\t\t+ \"nonce=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}\"\n\t\t\t\t\t+ \"&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256&param1=value1\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestCustomizerOverridesParameterThenQueryIncludesParameter() {\n\t\tClientRegistration clientRegistration = this.oidcRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tthis.resolver.setAuthorizationRequestCustomizer((builder) -> builder.parameters((params) -> {\n\t\t\tparams.put(\"appid\", params.get(\"client_id\"));\n\t\t\tparams.remove(\"client_id\");\n\t\t}));\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).matches(\n\t\t\t\t\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&\" + \"scope=openid&state=.{15,}&\"\n\t\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/oidc-registration-id&\"\n\t\t\t\t\t\t+ \"nonce=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}\"\n\t\t\t\t\t\t+ \"&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256&appid=client-id\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestNoProvideAuthorizationRequestBaseUri() {\n\t\tOAuth2AuthorizationRequestResolver resolver = new DefaultOAuth2AuthorizationRequestResolver(\n\t\t\t\tthis.clientRegistrationRepository);\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + this.registration2.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getRedirectUri())\n\t\t\t.isEqualTo(\"http://localhost/login/oauth2/code/\" + this.registration2.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestProvideCodeChallengeMethod() {\n\t\tClientRegistration clientRegistration = this.pkceClientRegistration;\n\t\tString requestUri = this.authorizationRequestBaseUri + \"/\" + clientRegistration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);\n\t\tassertThat(authorizationRequest.getAdditionalParameters().containsKey(PkceParameterNames.CODE_CHALLENGE_METHOD))\n\t\t\t.isTrue();\n\t}\n\n\tprivate static ClientRegistration.Builder pkceClientRegistration() {\n\t\treturn ClientRegistration.withRegistrationId(\"pkce\")\n\t\t\t.redirectUri(\"{baseUrl}/{action}/oauth2/code/{registrationId}\")\n\t\t\t.clientSettings(ClientRegistration.ClientSettings.builder().requireProofKey(true).build())\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.scope(\"read:user\")\n\t\t\t.authorizationUri(\"https://example.com/login/oauth/authorize\")\n\t\t\t.tokenUri(\"https://example.com/login/oauth/access_token\")\n\t\t\t.userInfoUri(\"https://api.example.com/user\")\n\t\t\t.userNameAttributeName(\"id\")\n\t\t\t.clientName(\"Client Name\")\n\t\t\t.clientId(\"client-id-3\")\n\t\t\t.clientSecret(\"client-secret\");\n\t}\n\n\tprivate static ClientRegistration.Builder fineRedirectUriTemplateClientRegistration() {\n\t\t// @formatter:off\n\t\treturn ClientRegistration.withRegistrationId(\"fine-redirect-uri-template-client-registration\")\n\t\t\t\t.redirectUri(\"{baseScheme}://{baseHost}{basePort}{basePath}/{action}/oauth2/code/{registrationId}\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.scope(\"read:user\")\n\t\t\t\t.authorizationUri(\"https://example.com/login/oauth/authorize\")\n\t\t\t\t.tokenUri(\"https://example.com/login/oauth/access_token\")\n\t\t\t\t.userInfoUri(\"https://api.example.com/user\")\n\t\t\t\t.userNameAttributeName(\"id\")\n\t\t\t\t.clientName(\"Fine Redirect Uri Template Client\")\n\t\t\t\t.clientId(\"fine-redirect-uri-template-client\")\n\t\t\t\t.clientSecret(\"client-secret\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/DefaultOAuth2AuthorizedClientManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationContext;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationSuccessHandler;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.RemoveAuthorizedClientOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link DefaultOAuth2AuthorizedClientManager}.\n *\n * @author Joe Grandja\n */\npublic class DefaultOAuth2AuthorizedClientManagerTests {\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate OAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate Function contextAttributesMapper;\n\n\tprivate OAuth2AuthorizationSuccessHandler authorizationSuccessHandler;\n\n\tprivate OAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\tprivate DefaultOAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Authentication principal;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate ArgumentCaptor<OAuth2AuthorizationContext> authorizationContextCaptor;\n\n\t@SuppressWarnings(\"unchecked\")\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tthis.authorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class);\n\t\tthis.authorizedClientProvider = mock(OAuth2AuthorizedClientProvider.class);\n\t\tthis.contextAttributesMapper = mock(Function.class);\n\t\tthis.authorizationSuccessHandler = spy(new OAuth2AuthorizationSuccessHandler() {\n\t\t\t@Override\n\t\t\tpublic void onAuthorizationSuccess(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\t\t\tMap<String, Object> attributes) {\n\t\t\t\tDefaultOAuth2AuthorizedClientManagerTests.this.authorizedClientRepository.saveAuthorizedClient(\n\t\t\t\t\t\tauthorizedClient, principal,\n\t\t\t\t\t\t(HttpServletRequest) attributes.get(HttpServletRequest.class.getName()),\n\t\t\t\t\t\t(HttpServletResponse) attributes.get(HttpServletResponse.class.getName()));\n\t\t\t}\n\t\t});\n\t\tthis.authorizationFailureHandler = spy(\n\t\t\t\tnew RemoveAuthorizedClientOAuth2AuthorizationFailureHandler((clientRegistrationId, principal,\n\t\t\t\t\t\tattributes) -> this.authorizedClientRepository.removeAuthorizedClient(clientRegistrationId,\n\t\t\t\t\t\t\t\tprincipal, (HttpServletRequest) attributes.get(HttpServletRequest.class.getName()),\n\t\t\t\t\t\t\t\t(HttpServletResponse) attributes.get(HttpServletResponse.class.getName()))));\n\t\tthis.authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(this.clientRegistrationRepository,\n\t\t\t\tthis.authorizedClientRepository);\n\t\tthis.authorizedClientManager.setAuthorizedClientProvider(this.authorizedClientProvider);\n\t\tthis.authorizedClientManager.setContextAttributesMapper(this.contextAttributesMapper);\n\t\tthis.authorizedClientManager.setAuthorizationSuccessHandler(this.authorizationSuccessHandler);\n\t\tthis.authorizedClientManager.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, this.principal.getName(),\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"read\", \"write\"), TestOAuth2RefreshTokens.refreshToken());\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.authorizationContextCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationContext.class);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2AuthorizedClientManager(null, this.authorizedClientRepository))\n\t\t\t.withMessage(\"clientRegistrationRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenOAuth2AuthorizedClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2AuthorizedClientManager(this.clientRegistrationRepository, null))\n\t\t\t.withMessage(\"authorizedClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientProviderWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizedClientProvider(null))\n\t\t\t.withMessage(\"authorizedClientProvider cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setContextAttributesMapperWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setContextAttributesMapper(null))\n\t\t\t.withMessage(\"contextAttributesMapper cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizationSuccessHandler(null))\n\t\t\t.withMessage(\"authorizationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizationFailureHandler(null))\n\t\t\t.withMessage(\"authorizationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientManager.authorize(null))\n\t\t\t.withMessage(\"authorizeRequest cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenHttpServletRequestIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest))\n\t\t\t.withMessage(\"servletRequest cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenHttpServletResponseIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.attribute(HttpServletRequest.class.getName(), this.request)\n\t\t\t.build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest))\n\t\t\t.withMessage(\"servletResponse cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientRegistrationNotFoundThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(\"invalid-registration-id\")\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest))\n\t\t\t.withMessage(\"Could not find ClientRegistration with id 'invalid-registration-id'\");\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndUnsupportedProviderThenNotAuthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(this.clientRegistration);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isNull();\n\t\tverifyNoInteractions(this.authorizationSuccessHandler);\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndSupportedProviderThenAuthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(this.clientRegistration);\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(this.authorizedClient);\n\t\tverify(this.authorizationSuccessHandler).onAuthorizationSuccess(eq(this.authorizedClient), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(eq(this.authorizedClient), eq(this.principal),\n\t\t\t\teq(this.request), eq(this.response));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndSupportedProviderThenReauthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(this.clientRegistration);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal), eq(this.request)))\n\t\t\t.willReturn(this.authorizedClient);\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(reauthorizedClient);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(any());\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(reauthorizedClient);\n\t\tverify(this.authorizationSuccessHandler).onAuthorizationSuccess(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\teq(this.request), eq(this.response));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenUnsupportedProviderThenNotReauthorized() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(reauthorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(this.authorizedClient);\n\t\tverifyNoInteractions(this.authorizationSuccessHandler);\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(OAuth2AuthorizedClient.class),\n\t\t\t\teq(this.principal), eq(this.request), eq(this.response));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenSupportedProviderThenReauthorized() {\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(reauthorizedClient);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(reauthorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(reauthorizedClient);\n\t\tverify(this.authorizationSuccessHandler).onAuthorizationSuccess(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\teq(this.request), eq(this.response));\n\t}\n\n\t@Test\n\tpublic void reauthorizeWhenRequestParameterScopeThenMappedToContext() {\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(reauthorizedClient);\n\t\t// Override the mock with the default\n\t\tthis.authorizedClientManager\n\t\t\t.setContextAttributesMapper(new DefaultOAuth2AuthorizedClientManager.DefaultContextAttributesMapper());\n\t\tthis.request.addParameter(OAuth2ParameterNames.SCOPE, \"read write\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizedClientManager.authorize(reauthorizeRequest);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tString[] requestScopeAttribute = authorizationContext\n\t\t\t.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);\n\t\tassertThat(requestScopeAttribute).contains(\"read\", \"write\");\n\t}\n\n\t@Test\n\tpublic void reauthorizeWhenErrorCodeMatchThenRemoveAuthorizedClient() {\n\t\tClientAuthorizationException authorizationException = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, null, null),\n\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willThrow(authorizationException);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(reauthorizeRequest))\n\t\t\t.isEqualTo(authorizationException);\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(eq(authorizationException), eq(this.principal),\n\t\t\t\tany());\n\t\tverify(this.authorizedClientRepository).removeAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal), eq(this.request), eq(this.response));\n\t}\n\n\t@Test\n\tpublic void reauthorizeWhenErrorCodeDoesNotMatchThenDoNotRemoveAuthorizedClient() {\n\t\tClientAuthorizationException authorizationException = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(\"non-matching-error-code\", null, null), this.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willThrow(authorizationException);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t\t.principal(this.principal)\n\t\t\t\t.attributes((attrs) -> {\n\t\t\t\t\tattrs.put(HttpServletRequest.class.getName(), this.request);\n\t\t\t\t\tattrs.put(HttpServletResponse.class.getName(), this.response);\n\t\t\t\t})\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(reauthorizeRequest))\n\t\t\t.isEqualTo(authorizationException);\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(eq(authorizationException), eq(this.principal),\n\t\t\t\tany());\n\t\tverifyNoInteractions(this.authorizedClientRepository);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/DefaultReactiveOAuth2AuthorizedClientManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.Collections;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.function.Function;\n\nimport io.micrometer.context.ContextExecutorService;\nimport io.micrometer.context.ContextSnapshotFactory;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Hooks;\nimport reactor.core.publisher.Mono;\nimport reactor.test.publisher.PublisherProbe;\nimport reactor.util.context.Context;\n\nimport org.springframework.core.task.SimpleAsyncTaskExecutor;\nimport org.springframework.core.task.support.ExecutorServiceAdapter;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationContext;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link DefaultReactiveOAuth2AuthorizedClientManager}.\n *\n * @author Joe Grandja\n */\npublic class DefaultReactiveOAuth2AuthorizedClientManagerTests {\n\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider;\n\n\tprivate Function contextAttributesMapper;\n\n\tprivate DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate Authentication principal;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate MockServerWebExchange serverWebExchange;\n\n\tprivate Context context;\n\n\tprivate ArgumentCaptor<OAuth2AuthorizationContext> authorizationContextCaptor;\n\n\tprivate PublisherProbe<OAuth2AuthorizedClient> loadAuthorizedClientProbe;\n\n\tprivate PublisherProbe<Void> saveAuthorizedClientProbe;\n\n\tprivate PublisherProbe<Void> removeAuthorizedClientProbe;\n\n\t@SuppressWarnings(\"unchecked\")\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.clientRegistrationRepository = mock(ReactiveClientRegistrationRepository.class);\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(anyString())).willReturn(Mono.empty());\n\t\tthis.authorizedClientRepository = mock(ServerOAuth2AuthorizedClientRepository.class);\n\t\tthis.loadAuthorizedClientProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(Authentication.class),\n\t\t\t\tany(ServerWebExchange.class)))\n\t\t\t.willReturn(this.loadAuthorizedClientProbe.mono());\n\t\tthis.saveAuthorizedClientProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizedClientRepository.saveAuthorizedClient(any(OAuth2AuthorizedClient.class),\n\t\t\t\tany(Authentication.class), any(ServerWebExchange.class)))\n\t\t\t.willReturn(this.saveAuthorizedClientProbe.mono());\n\t\tthis.removeAuthorizedClientProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizedClientRepository.removeAuthorizedClient(any(String.class), any(Authentication.class),\n\t\t\t\tany(ServerWebExchange.class)))\n\t\t\t.willReturn(this.removeAuthorizedClientProbe.mono());\n\t\tthis.authorizedClientProvider = mock(ReactiveOAuth2AuthorizedClientProvider.class);\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class))).willReturn(Mono.empty());\n\t\tthis.contextAttributesMapper = mock(Function.class);\n\t\tgiven(this.contextAttributesMapper.apply(any())).willReturn(Mono.just(Collections.emptyMap()));\n\t\tthis.authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tthis.authorizedClientManager.setAuthorizedClientProvider(this.authorizedClientProvider);\n\t\tthis.authorizedClientManager.setContextAttributesMapper(this.contextAttributesMapper);\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.principal = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, this.principal.getName(),\n\t\t\t\tTestOAuth2AccessTokens.scopes(\"read\", \"write\"), TestOAuth2RefreshTokens.refreshToken());\n\t\tthis.serverWebExchange = MockServerWebExchange.builder(MockServerHttpRequest.get(\"/\")).build();\n\t\tthis.context = Context.of(ServerWebExchange.class, this.serverWebExchange);\n\t\tthis.authorizationContextCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationContext.class);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultReactiveOAuth2AuthorizedClientManager(null, this.authorizedClientRepository))\n\t\t\t.withMessage(\"clientRegistrationRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenOAuth2AuthorizedClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultReactiveOAuth2AuthorizedClientManager(this.clientRegistrationRepository, null))\n\t\t\t.withMessage(\"authorizedClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientProviderWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizedClientProvider(null))\n\t\t\t.withMessage(\"authorizedClientProvider cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationSuccessHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizationSuccessHandler(null))\n\t\t\t.withMessage(\"authorizationSuccessHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationFailureHandlerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setAuthorizationFailureHandler(null))\n\t\t\t.withMessage(\"authorizationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setContextAttributesMapperWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.setContextAttributesMapper(null))\n\t\t\t.withMessage(\"contextAttributesMapper cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientManager.authorize(null).block())\n\t\t\t.withMessage(\"authorizeRequest cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenExchangeIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest).block())\n\t\t\t.withMessage(\"serverWebExchange cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenClientRegistrationNotFoundThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(\"invalid-registration-id\")\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.authorizedClientManager.authorize(authorizeRequest).contextWrite(this.context).block())\n\t\t\t.withMessage(\"Could not find ClientRegistration with id 'invalid-registration-id'\");\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndUnsupportedProviderThenNotAuthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest)\n\t\t\t.contextWrite(this.context)\n\t\t\t.block();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isNull();\n\t\tthis.loadAuthorizedClientProbe.assertWasSubscribed();\n\t\tthis.saveAuthorizedClientProbe.assertWasNotSubscribed();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndSupportedProviderThenAuthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest)\n\t\t\t.contextWrite(this.context)\n\t\t\t.block();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(this.authorizedClient);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(eq(this.authorizedClient), eq(this.principal),\n\t\t\t\teq(this.serverWebExchange));\n\t\tthis.saveAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientRepository, never()).removeAuthorizedClient(any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenNotAuthorizedAndSupportedProviderAndCustomSuccessHandlerThenInvokeCustomSuccessHandler() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tPublisherProbe<Void> authorizationSuccessHandlerProbe = PublisherProbe.empty();\n\t\tthis.authorizedClientManager\n\t\t\t.setAuthorizationSuccessHandler((client, principal, attributes) -> authorizationSuccessHandlerProbe.mono());\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest)\n\t\t\t.contextWrite(this.context)\n\t\t\t.block();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(this.authorizedClient);\n\t\tauthorizationSuccessHandlerProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), any(), any());\n\t\tverify(this.authorizedClientRepository, never()).removeAuthorizedClient(any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenInvalidTokenThenRemoveAuthorizedClient() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tClientAuthorizationException exception = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, null, null),\n\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.authorizedClientManager.authorize(authorizeRequest).contextWrite(this.context).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientRepository).removeAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal), eq(this.serverWebExchange));\n\t\tthis.removeAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenInvalidGrantThenRemoveAuthorizedClient() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tClientAuthorizationException exception = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, null, null),\n\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.authorizedClientManager.authorize(authorizeRequest).contextWrite(this.context).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientRepository).removeAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal), eq(this.serverWebExchange));\n\t\tthis.removeAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenServerErrorThenDoNotRemoveAuthorizedClient() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tClientAuthorizationException exception = new ClientAuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, null, null),\n\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tassertThatExceptionOfType(ClientAuthorizationException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.authorizedClientManager.authorize(authorizeRequest).contextWrite(this.context).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientRepository, never()).removeAuthorizedClient(any(), any(), any());\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenOAuth2AuthorizationExceptionThenDoNotRemoveAuthorizedClient() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizationException exception = new OAuth2AuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, null, null));\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.authorizedClientManager.authorize(authorizeRequest).contextWrite(this.context).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tverify(this.authorizedClientRepository, never()).removeAuthorizedClient(any(), any(), any());\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenOAuth2AuthorizationExceptionAndCustomFailureHandlerThenInvokeCustomFailureHandler() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizationException exception = new OAuth2AuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, null, null));\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.error(exception));\n\t\tPublisherProbe<Void> authorizationFailureHandlerProbe = PublisherProbe.empty();\n\t\tthis.authorizedClientManager\n\t\t\t.setAuthorizationFailureHandler((client, principal, attributes) -> authorizationFailureHandlerProbe.mono());\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.authorizedClientManager.authorize(authorizeRequest).contextWrite(this.context).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(authorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isNull();\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tauthorizationFailureHandlerProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientRepository, never()).removeAuthorizedClient(any(), any(), any());\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void authorizeWhenAuthorizedAndSupportedProviderThenReauthorized() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tthis.loadAuthorizedClientProbe = PublisherProbe.of(Mono.just(this.authorizedClient));\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(eq(this.clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.principal), eq(this.serverWebExchange)))\n\t\t\t.willReturn(this.loadAuthorizedClientProbe.mono());\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(reauthorizedClient));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest)\n\t\t\t.contextWrite(this.context)\n\t\t\t.block();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(any());\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(reauthorizedClient);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\teq(this.serverWebExchange));\n\t\tthis.saveAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientRepository, never()).removeAuthorizedClient(any(), any(), any());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenUnsupportedProviderThenNotReauthorized() {\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest)\n\t\t\t.contextWrite(this.context)\n\t\t\t.block();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(reauthorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(this.authorizedClient);\n\t\tthis.saveAuthorizedClientProbe.assertWasNotSubscribed();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void reauthorizeWhenSupportedProviderThenReauthorized() {\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(reauthorizedClient));\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(reauthorizeRequest)\n\t\t\t.contextWrite(this.context)\n\t\t\t.block();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tverify(this.contextAttributesMapper).apply(eq(reauthorizeRequest));\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tassertThat(authorizationContext.getClientRegistration()).isEqualTo(this.clientRegistration);\n\t\tassertThat(authorizationContext.getAuthorizedClient()).isSameAs(this.authorizedClient);\n\t\tassertThat(authorizationContext.getPrincipal()).isEqualTo(this.principal);\n\t\tassertThat(authorizedClient).isSameAs(reauthorizedClient);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(eq(reauthorizedClient), eq(this.principal),\n\t\t\t\teq(this.serverWebExchange));\n\t\tthis.saveAuthorizedClientProbe.assertWasSubscribed();\n\t\tverify(this.authorizedClientRepository, never()).removeAuthorizedClient(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void reauthorizeWhenRequestParameterScopeThenMappedToContext() {\n\t\tOAuth2AuthorizedClient reauthorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,\n\t\t\t\tthis.principal.getName(), TestOAuth2AccessTokens.noScopes(), TestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(reauthorizedClient));\n\t\t// Override the mock with the default\n\t\tthis.authorizedClientManager.setContextAttributesMapper(\n\t\t\t\tnew DefaultReactiveOAuth2AuthorizedClientManager.DefaultContextAttributesMapper());\n\t\tthis.serverWebExchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.get(\"/\").queryParam(OAuth2ParameterNames.SCOPE, \"read write\"))\n\t\t\t.build();\n\t\tthis.context = Context.of(ServerWebExchange.class, this.serverWebExchange);\n\t\tOAuth2AuthorizeRequest reauthorizeRequest = OAuth2AuthorizeRequest.withAuthorizedClient(this.authorizedClient)\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\t\tthis.authorizedClientManager.authorize(reauthorizeRequest).contextWrite(this.context).block();\n\t\tverify(this.authorizedClientProvider).authorize(this.authorizationContextCaptor.capture());\n\t\tOAuth2AuthorizationContext authorizationContext = this.authorizationContextCaptor.getValue();\n\t\tString[] requestScopeAttribute = authorizationContext\n\t\t\t.getAttribute(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME);\n\t\tassertThat(requestScopeAttribute).contains(\"read\", \"write\");\n\t}\n\n\t@Test\n\tpublic void authorizeWhenBlockingExecutionAndContextPropagationEnabledThenContextPropagated()\n\t\t\tthrows InterruptedException {\n\t\tHooks.enableAutomaticContextPropagation();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(this.clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientProvider.authorize(any(OAuth2AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tOAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest\n\t\t\t.withClientRegistrationId(this.clientRegistration.getRegistrationId())\n\t\t\t.principal(this.principal)\n\t\t\t.build();\n\n\t\tCountDownLatch countDownLatch = new CountDownLatch(1);\n\t\tRunnable task = () -> {\n\t\t\ttry {\n\t\t\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest)\n\t\t\t\t\t.block();\n\t\t\t\tassertThat(authorizedClient).isSameAs(this.authorizedClient);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tcountDownLatch.countDown();\n\t\t\t}\n\t\t};\n\t\ttry (SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor()) {\n\t\t\tContextSnapshotFactory contextSnapshotFactory = ContextSnapshotFactory.builder().build();\n\t\t\tExecutorService executorService = ContextExecutorService.wrap(new ExecutorServiceAdapter(taskExecutor),\n\t\t\t\t\tcontextSnapshotFactory);\n\t\t\tMono.fromRunnable(() -> executorService.execute(task)).contextWrite(this.context).block();\n\t\t}\n\n\t\tcountDownLatch.await();\n\t\tverify(this.authorizedClientProvider).authorize(any(OAuth2AuthorizationContext.class));\n\t}\n\n\tprivate Mono<ServerWebExchange> currentServerWebExchange() {\n\t\treturn Mono.deferContextual(Mono::just)\n\t\t\t.filter((c) -> c.hasKey(ServerWebExchange.class))\n\t\t\t.map((c) -> c.get(ServerWebExchange.class));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/HttpSessionOAuth2AuthorizationRequestRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link HttpSessionOAuth2AuthorizationRequestRepository}.\n *\n * @author Joe Grandja\n * @author Craig Andrews\n */\npublic class HttpSessionOAuth2AuthorizationRequestRepositoryTests {\n\n\tprivate HttpSessionOAuth2AuthorizationRequestRepository authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenHttpServletRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizationRequestRepository.loadAuthorizationRequest(null));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenNotSavedThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"state-1234\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository\n\t\t\t.loadAuthorizationRequest(request);\n\t\tassertThat(authorizationRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenSavedThenReturnAuthorizationRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, authorizationRequest.getState());\n\t\tOAuth2AuthorizationRequest loadedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.loadAuthorizationRequest(request);\n\t\tassertThat(loadedAuthorizationRequest).isEqualTo(authorizationRequest);\n\t}\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenSavedAndStateParameterNullThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request,\n\t\t\t\tnew MockHttpServletResponse());\n\t\tassertThat(this.authorizationRequestRepository.loadAuthorizationRequest(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenHttpServletRequestIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizationRequestRepository\n\t\t\t.saveAuthorizationRequest(authorizationRequest, null, new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenHttpServletResponseIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizationRequestRepository\n\t\t\t.saveAuthorizationRequest(authorizationRequest, new MockHttpServletRequest(), null));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenStateNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().state(null).build();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest,\n\t\t\t\t\tnew MockHttpServletRequest(), new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenNotNullThenSaved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request,\n\t\t\t\tnew MockHttpServletResponse());\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, authorizationRequest.getState());\n\t\tOAuth2AuthorizationRequest loadedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.loadAuthorizationRequest(request);\n\t\tassertThat(loadedAuthorizationRequest).isEqualTo(authorizationRequest);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenNoExistingSessionAndDistributedSessionThenSaved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(new MockDistributedHttpSession());\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request,\n\t\t\t\tnew MockHttpServletResponse());\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, authorizationRequest.getState());\n\t\tOAuth2AuthorizationRequest loadedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.loadAuthorizationRequest(request);\n\t\tassertThat(loadedAuthorizationRequest).isEqualTo(authorizationRequest);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenExistingSessionAndDistributedSessionThenSaved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(new MockDistributedHttpSession());\n\t\tOAuth2AuthorizationRequest authorizationRequest1 = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest1, request,\n\t\t\t\tnew MockHttpServletResponse());\n\t\tOAuth2AuthorizationRequest authorizationRequest2 = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest2, request,\n\t\t\t\tnew MockHttpServletResponse());\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, authorizationRequest2.getState());\n\t\tOAuth2AuthorizationRequest loadedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.loadAuthorizationRequest(request);\n\t\tassertThat(loadedAuthorizationRequest).isEqualTo(authorizationRequest2);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenNullThenRemoved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, authorizationRequest.getState());\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(null, request, response);\n\t\tOAuth2AuthorizationRequest loadedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.loadAuthorizationRequest(request);\n\t\tassertThat(loadedAuthorizationRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthorizationRequestWhenHttpServletRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizationRequestRepository\n\t\t\t.removeAuthorizationRequest(null, new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizationRequestWhenHttpServletResponseIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizationRequestRepository\n\t\t\t.removeAuthorizationRequest(new MockHttpServletRequest(), null));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizationRequestWhenSavedThenRemoved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, authorizationRequest.getState());\n\t\tOAuth2AuthorizationRequest removedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.removeAuthorizationRequest(request, response);\n\t\tOAuth2AuthorizationRequest loadedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.loadAuthorizationRequest(request);\n\t\tassertThat(removedAuthorizationRequest).isNotNull();\n\t\tassertThat(loadedAuthorizationRequest).isNull();\n\t}\n\n\t// gh-5263\n\t@Test\n\tpublic void removeAuthorizationRequestWhenSavedThenRemovedFromSession() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tOAuth2AuthorizationRequest authorizationRequest = createAuthorizationRequest().build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, authorizationRequest.getState());\n\t\tOAuth2AuthorizationRequest removedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.removeAuthorizationRequest(request, response);\n\t\tString sessionAttributeName = HttpSessionOAuth2AuthorizationRequestRepository.class.getName()\n\t\t\t\t+ \".AUTHORIZATION_REQUEST\";\n\t\tassertThat(removedAuthorizationRequest).isNotNull();\n\t\tassertThat(request.getSession().getAttribute(sessionAttributeName)).isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthorizationRequestWhenNotSavedThenNotRemoved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"state-1234\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tOAuth2AuthorizationRequest removedAuthorizationRequest = this.authorizationRequestRepository\n\t\t\t.removeAuthorizationRequest(request, response);\n\t\tassertThat(removedAuthorizationRequest).isNull();\n\t}\n\n\tprotected OAuth2AuthorizationRequest.Builder createAuthorizationRequest() {\n\t\treturn OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(\"https://example.com/oauth2/authorize\")\n\t\t\t.clientId(\"client-id-1234\")\n\t\t\t.state(\"state-1234\");\n\t}\n\n\tstatic class MockDistributedHttpSession extends MockHttpSession {\n\n\t\t@Override\n\t\tpublic Object getAttribute(String name) {\n\t\t\treturn wrap(super.getAttribute(name));\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAttribute(String name, Object value) {\n\t\t\tsuper.setAttribute(name, wrap(value));\n\t\t}\n\n\t\tprivate Object wrap(Object object) {\n\t\t\tif (object instanceof Map) {\n\t\t\t\tobject = new HashMap<>((Map<Object, Object>) object);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/HttpSessionOAuth2AuthorizedClientRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link HttpSessionOAuth2AuthorizedClientRepository}.\n *\n * @author Joe Grandja\n */\npublic class HttpSessionOAuth2AuthorizedClientRepositoryTests {\n\n\tprivate String principalName1 = \"principalName-1\";\n\n\tprivate ClientRegistration registration1 = TestClientRegistrations.clientRegistration().build();\n\n\tprivate ClientRegistration registration2 = TestClientRegistrations.clientRegistration2().build();\n\n\tprivate String registrationId1 = this.registration1.getRegistrationId();\n\n\tprivate String registrationId2 = this.registration2.getRegistrationId();\n\n\tprivate HttpSessionOAuth2AuthorizedClientRepository authorizedClientRepository = new HttpSessionOAuth2AuthorizedClientRepository();\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.loadAuthorizedClient(null, null, this.request));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenPrincipalNameIsNullThenExceptionNotThrown() {\n\t\tthis.authorizedClientRepository.loadAuthorizedClient(this.registrationId1, null, this.request);\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.loadAuthorizedClient(this.registrationId1, null, null));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationNotFoundThenReturnNull() {\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(\"registration-not-found\", null, this.request);\n\t\tassertThat(authorizedClient).isNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenSavedThenReturnAuthorizedClient() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.request, this.response);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId1, null, this.request);\n\t\tassertThat(loadedAuthorizedClient).isEqualTo(authorizedClient);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthorizedClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.authorizedClientRepository.saveAuthorizedClient(null, null, this.request, this.response));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthenticationIsNullThenExceptionNotThrown() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientRepository\n\t\t\t.saveAuthorizedClient(authorizedClient, null, null, this.response));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenResponseIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.request, null));\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenSavedThenSavedToSession() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.request, this.response);\n\t\tHttpSession session = this.request.getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = (Map<String, OAuth2AuthorizedClient>) session\n\t\t\t.getAttribute(HttpSessionOAuth2AuthorizedClientRepository.class.getName() + \".AUTHORIZED_CLIENTS\");\n\t\tassertThat(authorizedClients).isNotEmpty();\n\t\tassertThat(authorizedClients).hasSize(1);\n\t\tassertThat(authorizedClients.values().iterator().next()).isSameAs(authorizedClient);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.authorizedClientRepository.removeAuthorizedClient(null, null, this.request, this.response));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenPrincipalNameIsNullThenExceptionNotThrown() {\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.authorizedClientRepository\n\t\t\t.removeAuthorizedClient(this.registrationId1, null, null, this.response));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenResponseIsNullThenExceptionNotThrown() {\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, this.request, null);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenNotSavedThenSessionNotCreated() {\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId2, null, this.request, this.response);\n\t\tassertThat(this.request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClient1SavedAndClient2RemovedThenClient1NotRemoved() {\n\t\tOAuth2AuthorizedClient authorizedClient1 = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient1, null, this.request, this.response);\n\t\t// Remove registrationId2 (never added so is not removed either)\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId2, null, this.request, this.response);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient1 = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId1, null, this.request);\n\t\tassertThat(loadedAuthorizedClient1).isNotNull();\n\t\tassertThat(loadedAuthorizedClient1).isSameAs(authorizedClient1);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenSavedThenRemoved() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.request, this.response);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId2, null, this.request);\n\t\tassertThat(loadedAuthorizedClient).isSameAs(authorizedClient);\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId2, null, this.request, this.response);\n\t\tloadedAuthorizedClient = this.authorizedClientRepository.loadAuthorizedClient(this.registrationId2, null,\n\t\t\t\tthis.request);\n\t\tassertThat(loadedAuthorizedClient).isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenSavedThenRemovedFromSession() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.request, this.response);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId1, null, this.request);\n\t\tassertThat(loadedAuthorizedClient).isSameAs(authorizedClient);\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, this.request, this.response);\n\t\tHttpSession session = this.request.getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\tassertThat(session\n\t\t\t.getAttribute(HttpSessionOAuth2AuthorizedClientRepository.class.getName() + \".AUTHORIZED_CLIENTS\"))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClient1Client2SavedAndClient1RemovedThenClient2NotRemoved() {\n\t\tOAuth2AuthorizedClient authorizedClient1 = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient1, null, this.request, this.response);\n\t\tOAuth2AuthorizedClient authorizedClient2 = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient2, null, this.request, this.response);\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, this.request, this.response);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient2 = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId2, null, this.request);\n\t\tassertThat(loadedAuthorizedClient2).isNotNull();\n\t\tassertThat(loadedAuthorizedClient2).isSameAs(authorizedClient2);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests for {@link OAuth2AuthorizationCodeGrantFilter}.\n *\n * @author Joe Grandja\n * @author Parikshit Dutta\n */\npublic class OAuth2AuthorizationCodeGrantFilterTests {\n\n\tprivate ClientRegistration registration1;\n\n\tprivate String principalName1 = \"principal-1\";\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\tprivate OAuth2AuthorizationCodeGrantFilter filter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.registration1 = TestClientRegistrations.clientRegistration().build();\n\t\tthis.clientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1);\n\t\tthis.authorizedClientService = new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository);\n\t\tthis.authorizedClientRepository = new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(\n\t\t\t\tthis.authorizedClientService);\n\t\tthis.authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.filter = spy(new OAuth2AuthorizationCodeGrantFilter(this.clientRegistrationRepository,\n\t\t\t\tthis.authorizedClientRepository, this.authenticationManager));\n\t\tthis.filter.setAuthorizationRequestRepository(this.authorizationRequestRepository);\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(this.principalName1, \"password\");\n\t\tauthentication.setAuthenticated(true);\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AuthorizationCodeGrantFilter(null,\n\t\t\t\tthis.authorizedClientRepository, this.authenticationManager));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeGrantFilter(this.clientRegistrationRepository, null,\n\t\t\t\t\tthis.authenticationManager));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeGrantFilter(this.clientRegistrationRepository,\n\t\t\t\t\tthis.authorizedClientRepository, null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizationRequestRepositoryWhenAuthorizationRequestRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRequestRepository(null));\n\t}\n\n\t@Test\n\tpublic void setRequestCacheWhenRequestCacheIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestCache(null));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotAuthorizationResponseThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\t// NOTE: A valid Authorization Response contains either a 'code' or 'error'\n\t\t// parameter.\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestNotFoundThenNotProcessed() throws Exception {\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/path\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestRedirectUriDoesNotMatchThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/callback/client-1\";\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(requestUri);\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tauthorizationResponse.setRequestURI(requestUri + \"-no-match\");\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t// gh-7963\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestRedirectUriParametersMatchThenProcessed() throws Exception {\n\t\t// 1) redirect_uri with query parameters\n\t\tString requestUri = \"/callback/client-1\";\n\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\tparameters.put(\"param1\", \"value1\");\n\t\tparameters.put(\"param2\", \"value2\");\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(requestUri, parameters);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverifyNoInteractions(filterChain);\n\t\t// 2) redirect_uri with query parameters AND authorization response additional\n\t\t// parameters\n\t\tMap<String, String> additionalParameters = new LinkedHashMap<>();\n\t\tadditionalParameters.put(\"auth-param1\", \"value1\");\n\t\tadditionalParameters.put(\"auth-param2\", \"value2\");\n\t\tresponse = new MockHttpServletResponse();\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tauthorizationResponse = createAuthorizationResponse(authorizationRequest, additionalParameters);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t// gh-7963\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestRedirectUriParametersDoesNotMatchThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/callback/client-1\";\n\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\tparameters.put(\"param1\", \"value1\");\n\t\tparameters.put(\"param2\", \"value2\");\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(requestUri, parameters);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\t// 1) Parameter value\n\t\tMap<String, String> parametersNotMatch = new LinkedHashMap<>(parameters);\n\t\tparametersNotMatch.put(\"param2\", \"value8\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(\n\t\t\t\tcreateAuthorizationRequest(requestUri, parametersNotMatch));\n\t\tauthorizationResponse.setSession(authorizationRequest.getSession());\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverify(filterChain, times(1)).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\t// 2) Parameter order\n\t\tparametersNotMatch = new LinkedHashMap<>();\n\t\tparametersNotMatch.put(\"param2\", \"value2\");\n\t\tparametersNotMatch.put(\"param1\", \"value1\");\n\t\tauthorizationResponse = createAuthorizationResponse(createAuthorizationRequest(requestUri, parametersNotMatch));\n\t\tauthorizationResponse.setSession(authorizationRequest.getSession());\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverify(filterChain, times(2)).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\t// 3) Parameter missing\n\t\tparametersNotMatch = new LinkedHashMap<>(parameters);\n\t\tparametersNotMatch.remove(\"param2\");\n\t\tauthorizationResponse = createAuthorizationResponse(createAuthorizationRequest(requestUri, parametersNotMatch));\n\t\tauthorizationResponse.setSession(authorizationRequest.getSession());\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverify(filterChain, times(3)).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestMatchThenAuthorizationRequestRemoved() throws Exception {\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/callback/client-1\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tassertThat(this.authorizationRequestRepository.loadAuthorizationRequest(authorizationResponse)).isNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationFailsThenHandleOAuth2AuthorizationException() throws Exception {\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/callback/client-1\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT);\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class)))\n\t\t\t.willThrow(new OAuth2AuthorizationException(error));\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"http://localhost/callback/client-1?error=invalid_grant\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationSucceedsThenAuthorizedClientSavedToService() throws Exception {\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/callback/client-1\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientService\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), this.principalName1);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration1);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principalName1);\n\t\tassertThat(authorizedClient.getAccessToken()).isNotNull();\n\t\tassertThat(authorizedClient.getRefreshToken()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationSucceedsThenRedirected() throws Exception {\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/callback/client-1\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"http://localhost/callback/client-1\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/callback/client-1\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext())\n\t\t\t.willReturn(new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"password\")));\n\t\tthis.filter.setSecurityContextHolderStrategy(strategy);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverify(strategy).getContext();\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"http://localhost/callback/client-1\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationSucceedsAndHasSavedRequestThenRedirectToSavedRequest() throws Exception {\n\t\tString requestUri = \"/saved-request\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestCache requestCache = new HttpSessionRequestCache();\n\t\trequestCache.saveRequest(request, response);\n\t\trequest.setRequestURI(\"/callback/client-1\");\n\t\trequest.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\trequest.addParameter(OAuth2ParameterNames.STATE, \"state\");\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tString redirectUrl = requestCache.getRequest(request, response).getRedirectUrl();\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(redirectUrl);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationSucceedsAndRequestCacheConfiguredThenRequestCacheUsed() throws Exception {\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/callback/client-1\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tRequestCache requestCache = mock(RequestCache.class);\n\t\tSavedRequest savedRequest = mock(SavedRequest.class);\n\t\tString redirectUrl = \"https://example.com/saved-request?success\";\n\t\tgiven(savedRequest.getRedirectUrl()).willReturn(redirectUrl);\n\t\tgiven(requestCache.getRequest(any(), any())).willReturn(savedRequest);\n\t\tthis.filter.setRequestCache(requestCache);\n\t\tauthorizationRequest.setRequestURI(\"/saved-request\");\n\t\trequestCache.saveRequest(authorizationRequest, response);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tverify(requestCache).getRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(redirectUrl);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationSucceedsAndAnonymousAccessThenAuthorizedClientSavedToHttpSession()\n\t\t\tthrows Exception {\n\t\tAnonymousAuthenticationToken anonymousPrincipal = new AnonymousAuthenticationToken(\"key-1234\", \"anonymousUser\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(anonymousPrincipal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/callback/client-1\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), anonymousPrincipal, authorizationResponse);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration1);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(anonymousPrincipal.getName());\n\t\tassertThat(authorizedClient.getAccessToken()).isNotNull();\n\t\tHttpSession session = authorizationResponse.getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = (Map<String, OAuth2AuthorizedClient>) session\n\t\t\t.getAttribute(HttpSessionOAuth2AuthorizedClientRepository.class.getName() + \".AUTHORIZED_CLIENTS\");\n\t\tassertThat(authorizedClients).isNotEmpty();\n\t\tassertThat(authorizedClients).hasSize(1);\n\t\tassertThat(authorizedClients.values().iterator().next()).isSameAs(authorizedClient);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationSucceedsAndAnonymousAccessNullAuthenticationThenAuthorizedClientSavedToHttpSession()\n\t\t\tthrows Exception {\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tSecurityContextHolder.setContext(securityContext); // null Authentication\n\t\tMockHttpServletRequest authorizationRequest = createAuthorizationRequest(\"/callback/client-1\");\n\t\tMockHttpServletRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(authorizationRequest, response, this.registration1);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tthis.filter.doFilter(authorizationResponse, response, filterChain);\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), null, authorizationResponse);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration1);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(\"anonymousUser\");\n\t\tassertThat(authorizedClient.getAccessToken()).isNotNull();\n\t\tHttpSession session = authorizationResponse.getSession(false);\n\t\tassertThat(session).isNotNull();\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tMap<String, OAuth2AuthorizedClient> authorizedClients = (Map<String, OAuth2AuthorizedClient>) session\n\t\t\t.getAttribute(HttpSessionOAuth2AuthorizedClientRepository.class.getName() + \".AUTHORIZED_CLIENTS\");\n\t\tassertThat(authorizedClients).isNotEmpty();\n\t\tassertThat(authorizedClients).hasSize(1);\n\t\tassertThat(authorizedClients.values().iterator().next()).isSameAs(authorizedClient);\n\t}\n\n\tprivate static MockHttpServletRequest createAuthorizationRequest(String requestUri) {\n\t\treturn createAuthorizationRequest(requestUri, new LinkedHashMap<>());\n\t}\n\n\tprivate static MockHttpServletRequest createAuthorizationRequest(String requestUri,\n\t\t\tMap<String, String> parameters) {\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tif (!CollectionUtils.isEmpty(parameters)) {\n\t\t\tparameters.forEach(request::addParameter);\n\t\t\trequest.setQueryString(parameters.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.map((e) -> e.getKey() + \"=\" + e.getValue())\n\t\t\t\t.collect(Collectors.joining(\"&\")));\n\t\t}\n\t\treturn request;\n\t}\n\n\tprivate static MockHttpServletRequest createAuthorizationResponse(MockHttpServletRequest authorizationRequest) {\n\t\treturn createAuthorizationResponse(authorizationRequest, new LinkedHashMap<>());\n\t}\n\n\tprivate static MockHttpServletRequest createAuthorizationResponse(MockHttpServletRequest authorizationRequest,\n\t\t\tMap<String, String> additionalParameters) {\n\t\tMockHttpServletRequest authorizationResponse = new MockHttpServletRequest(authorizationRequest.getMethod(),\n\t\t\t\tauthorizationRequest.getRequestURI());\n\t\tauthorizationResponse.setServletPath(authorizationRequest.getRequestURI());\n\t\tauthorizationRequest.getParameterMap().forEach(authorizationResponse::addParameter);\n\t\tauthorizationResponse.addParameter(OAuth2ParameterNames.CODE, \"code\");\n\t\tauthorizationResponse.addParameter(OAuth2ParameterNames.STATE, \"state\");\n\t\tadditionalParameters.forEach(authorizationResponse::addParameter);\n\t\tauthorizationResponse.setQueryString(authorizationResponse.getParameterMap()\n\t\t\t.entrySet()\n\t\t\t.stream()\n\t\t\t.map((e) -> e.getKey() + \"=\" + e.getValue()[0])\n\t\t\t.collect(Collectors.joining(\"&\")));\n\t\tauthorizationResponse.setSession(authorizationRequest.getSession());\n\t\treturn authorizationResponse;\n\t}\n\n\tprivate void setUpAuthorizationRequest(HttpServletRequest request, HttpServletResponse response,\n\t\t\tClientRegistration registration) {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, registration.getRegistrationId());\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.attributes(attributes)\n\t\t\t.redirectUri(UrlUtils.buildFullRequestUrl(request))\n\t\t\t.build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t}\n\n\tprivate void setUpAuthenticationResult(ClientRegistration registration) {\n\t\tOAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(\n\t\t\t\tregistration, TestOAuth2AuthorizationExchanges.success(), TestOAuth2AccessTokens.noScopes(),\n\t\t\t\tTestOAuth2RefreshTokens.refreshToken());\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestRedirectFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests for {@link OAuth2AuthorizationRequestRedirectFilter}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationRequestRedirectFilterTests {\n\n\tprivate ClientRegistration registration1;\n\n\tprivate ClientRegistration registration2;\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizationRequestRedirectFilter filter;\n\n\tprivate RequestCache requestCache;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registration1 = TestClientRegistrations.clientRegistration().build();\n\t\tthis.registration2 = TestClientRegistrations.clientRegistration2().build();\n\t\tthis.clientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1,\n\t\t\t\tthis.registration2);\n\t\tthis.filter = new OAuth2AuthorizationRequestRedirectFilter(this.clientRegistrationRepository);\n\t\tthis.requestCache = mock(RequestCache.class);\n\t\tthis.filter.setRequestCache(this.requestCache);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationRequestRedirectFilter((ClientRegistrationRepository) null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationRequestBaseUriIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationRequestRedirectFilter(this.clientRegistrationRepository, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationRequestResolverIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationRequestRedirectFilter((OAuth2AuthorizationRequestResolver) null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizationRequestRepositoryWhenAuthorizationRequestRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRequestRepository(null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizationRedirectStrategyWhenAuthorizationRedirectStrategyIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRedirectStrategy(null));\n\t}\n\n\t@Test\n\tpublic void setRequestCacheWhenRequestCacheIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestCache(null));\n\t}\n\n\t@Test\n\tpublic void setAuthenticationFailureHandlerIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotAuthorizationRequestThenNextFilter() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestWithInvalidClientThenStatusInternalServerError() throws Exception {\n\t\tString requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/\"\n\t\t\t\t+ this.registration1.getRegistrationId() + \"-invalid\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.value());\n\t\tassertThat(response.getErrorMessage()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestWithInvalidClientAndCustomFailureHandlerThenCustomError()\n\t\t\tthrows Exception {\n\t\tString requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/\"\n\t\t\t\t+ this.registration1.getRegistrationId() + \"-invalid\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.setAuthenticationFailureHandler((request1, response1, ex) -> {\n\t\t\tThrowable cause = ex.getCause();\n\t\t\tif (InvalidClientRegistrationIdException.class.isAssignableFrom(cause.getClass())) {\n\t\t\t\tresponse1.sendError(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tresponse1.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(),\n\t\t\t\t\t\tHttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());\n\t\t\t}\n\t\t});\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t\tassertThat(response.getErrorMessage()).isEqualTo(HttpStatus.BAD_REQUEST.getReasonPhrase());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestOAuth2LoginThenRedirectForAuthorization() throws Exception {\n\t\tString requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/\"\n\t\t\t\t+ this.registration1.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tassertThat(response.getRedirectedUrl()).matches(\"https://example.com/login/oauth/authorize\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=client-id&\" + \"scope=read:user&state=.{15,}&\"\n\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestOAuth2LoginThenAuthorizationRequestSaved() throws Exception {\n\t\tString requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/\"\n\t\t\t\t+ this.registration2.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = mock(\n\t\t\t\tAuthorizationRequestRepository.class);\n\t\tthis.filter.setAuthorizationRequestRepository(authorizationRequestRepository);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tverify(authorizationRequestRepository).saveAuthorizationRequest(any(OAuth2AuthorizationRequest.class),\n\t\t\t\tany(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthorizationRequestBaseUriThenRedirectForAuthorization() throws Exception {\n\t\tString authorizationRequestBaseUri = \"/custom/authorization\";\n\t\tthis.filter = new OAuth2AuthorizationRequestRedirectFilter(this.clientRegistrationRepository,\n\t\t\t\tauthorizationRequestBaseUri);\n\t\tString requestUri = authorizationRequestBaseUri + \"/\" + this.registration1.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tassertThat(response.getRedirectedUrl()).matches(\"https://example.com/login/oauth/authorize\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=client-id&\" + \"scope=read:user&state=.{15,}&\"\n\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotAuthorizationRequestAndClientAuthorizationRequiredExceptionThrownThenRedirectForAuthorization()\n\t\t\tthrows Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\twillThrow(new ClientAuthorizationRequiredException(this.registration1.getRegistrationId())).given(filterChain)\n\t\t\t.doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tassertThat(response.getRedirectedUrl()).matches(\"https://example.com/login/oauth/authorize\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=client-id&\" + \"scope=read:user&state=.{15,}&\"\n\t\t\t\t+ \"redirect_uri=http://localhost/authorize/oauth2/code/registration-id&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t\tverify(this.requestCache).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotAuthorizationRequestAndClientAuthorizationRequiredExceptionThrownButAuthorizationRequestNotResolvedThenStatusInternalServerError()\n\t\t\tthrows Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\twillThrow(new ClientAuthorizationRequiredException(this.registration1.getRegistrationId())).given(filterChain)\n\t\t\t.doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tOAuth2AuthorizationRequestResolver resolver = mock(OAuth2AuthorizationRequestResolver.class);\n\t\tOAuth2AuthorizationRequestRedirectFilter filter = new OAuth2AuthorizationRequestRedirectFilter(resolver);\n\t\tfilter.doFilter(request, response, filterChain);\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.value());\n\t\tassertThat(response.getErrorMessage()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());\n\t}\n\n\t// gh-4911\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestAndAdditionalParametersProvidedThenAuthorizationRequestIncludesAdditionalParameters()\n\t\t\tthrows Exception {\n\t\tString requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/\"\n\t\t\t\t+ this.registration1.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\trequest.addParameter(\"idp\", \"https://other.provider.com\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tOAuth2AuthorizationRequestResolver defaultAuthorizationRequestResolver = new DefaultOAuth2AuthorizationRequestResolver(\n\t\t\t\tthis.clientRegistrationRepository,\n\t\t\t\tOAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);\n\t\tOAuth2AuthorizationRequestResolver resolver = mock(OAuth2AuthorizationRequestResolver.class);\n\t\tOAuth2AuthorizationRequest result = OAuth2AuthorizationRequest\n\t\t\t.from(defaultAuthorizationRequestResolver.resolve(request))\n\t\t\t.additionalParameters(Collections.singletonMap(\"idp\", request.getParameter(\"idp\")))\n\t\t\t.build();\n\t\tgiven(resolver.resolve(any())).willReturn(result);\n\t\tOAuth2AuthorizationRequestRedirectFilter filter = new OAuth2AuthorizationRequestRedirectFilter(resolver);\n\t\tfilter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tassertThat(response.getRedirectedUrl()).matches(\"https://example.com/login/oauth/authorize\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=client-id&\" + \"scope=read:user&state=.{15,}&\"\n\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id&\"\n\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256&idp=https://other.provider.com\");\n\t}\n\n\t// gh-4911, gh-5244\n\t@Test\n\tpublic void doFilterWhenAuthorizationRequestAndCustomAuthorizationRequestUriSetThenCustomAuthorizationRequestUriUsed()\n\t\t\tthrows Exception {\n\t\tString requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/\"\n\t\t\t\t+ this.registration1.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tString loginHintParamName = \"login_hint\";\n\t\trequest.addParameter(loginHintParamName, \"user@provider.com\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tOAuth2AuthorizationRequestResolver defaultAuthorizationRequestResolver = new DefaultOAuth2AuthorizationRequestResolver(\n\t\t\t\tthis.clientRegistrationRepository,\n\t\t\t\tOAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);\n\t\tOAuth2AuthorizationRequestResolver resolver = mock(OAuth2AuthorizationRequestResolver.class);\n\t\tOAuth2AuthorizationRequest defaultAuthorizationRequest = defaultAuthorizationRequestResolver.resolve(request);\n\t\tMap<String, Object> additionalParameters = new HashMap<>(defaultAuthorizationRequest.getAdditionalParameters());\n\t\tadditionalParameters.put(loginHintParamName, request.getParameter(loginHintParamName));\n\t\t// @formatter:off\n\t\tString customAuthorizationRequestUri = UriComponentsBuilder\n\t\t\t\t.fromUriString(defaultAuthorizationRequest.getAuthorizationRequestUri())\n\t\t\t\t.queryParam(loginHintParamName, additionalParameters.get(loginHintParamName))\n\t\t\t\t.build(true)\n\t\t\t\t.toUriString();\n\t\tOAuth2AuthorizationRequest result = OAuth2AuthorizationRequest\n\t\t\t\t.from(defaultAuthorizationRequestResolver.resolve(request))\n\t\t\t\t.additionalParameters(Collections.singletonMap(\"idp\", request.getParameter(\"idp\")))\n\t\t\t\t.authorizationRequestUri(customAuthorizationRequestUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(resolver.resolve(any())).willReturn(result);\n\t\tOAuth2AuthorizationRequestRedirectFilter filter = new OAuth2AuthorizationRequestRedirectFilter(resolver);\n\t\tfilter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tassertThat(response.getRedirectedUrl()).matches(\"https://example.com/login/oauth/authorize\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=client-id&\" + \"scope=read:user&state=.{15,}&\"\n\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id&\"\n\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256&login_hint=user@provider\\\\.com\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthorizationRedirectStrategySetThenCustomAuthorizationRedirectStrategyUsed()\n\t\t\tthrows Exception {\n\t\tString requestUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + \"/\"\n\t\t\t\t+ this.registration1.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tRedirectStrategy customRedirectStrategy = (httpRequest, httpResponse, url) -> {\n\t\t\tString redirectUrl = httpResponse.encodeRedirectURL(url);\n\t\t\thttpResponse.setStatus(HttpStatus.OK.value());\n\t\t\thttpResponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);\n\t\t\thttpResponse.getWriter().write(redirectUrl);\n\t\t\thttpResponse.getWriter().flush();\n\t\t};\n\t\tthis.filter.setAuthorizationRedirectStrategy(customRedirectStrategy);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(response.getContentType()).isEqualTo(MediaType.TEXT_PLAIN_VALUE);\n\t\tassertThat(response.getContentAsString(StandardCharsets.UTF_8))\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=http://localhost/login/oauth2/code/registration-id&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t// gh-11602\n\n\t@Test\n\tpublic void doFilterWhenNotAuthorizationRequestAndClientAuthorizationRequiredExceptionThrownThenSaveRequestBeforeCommitted()\n\t\t\tthrows Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\twillAnswer((invocation) -> assertThat((invocation.<HttpServletResponse>getArgument(1)).isCommitted()).isFalse())\n\t\t\t.given(this.requestCache)\n\t\t\t.saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\twillThrow(new ClientAuthorizationRequiredException(this.registration1.getRegistrationId())).given(filterChain)\n\t\t\t.doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.isCommitted()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests for {@link OAuth2LoginAuthenticationFilter}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2LoginAuthenticationFilterTests {\n\n\tprivate ClientRegistration registration1;\n\n\tprivate ClientRegistration registration2;\n\n\tprivate String principalName1 = \"principal-1\";\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate OAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\tprivate AuthenticationFailureHandler failureHandler;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate AuthenticationDetailsSource authenticationDetailsSource;\n\n\tprivate OAuth2LoginAuthenticationToken loginAuthentication;\n\n\tprivate OAuth2LoginAuthenticationFilter filter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.registration1 = TestClientRegistrations.clientRegistration().build();\n\t\tthis.registration2 = TestClientRegistrations.clientRegistration2().build();\n\t\tthis.clientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1,\n\t\t\t\tthis.registration2);\n\t\tthis.authorizedClientService = new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository);\n\t\tthis.authorizedClientRepository = new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(\n\t\t\t\tthis.authorizedClientService);\n\t\tthis.authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();\n\t\tthis.failureHandler = mock(AuthenticationFailureHandler.class);\n\t\tthis.authenticationManager = mock(AuthenticationManager.class);\n\t\tthis.authenticationDetailsSource = mock(AuthenticationDetailsSource.class);\n\t\tthis.filter = spy(new OAuth2LoginAuthenticationFilter(this.clientRegistrationRepository,\n\t\t\t\tthis.authorizedClientRepository, OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI));\n\t\tthis.filter.setAuthorizationRequestRepository(this.authorizationRequestRepository);\n\t\tthis.filter.setAuthenticationFailureHandler(this.failureHandler);\n\t\tthis.filter.setAuthenticationManager(this.authenticationManager);\n\t\tthis.filter.setAuthenticationDetailsSource(this.authenticationDetailsSource);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationFilter(null, this.authorizedClientService));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientServiceIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationFilter(this.clientRegistrationRepository, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationFilter(this.clientRegistrationRepository,\n\t\t\t\t\t(OAuth2AuthorizedClientRepository) null,\n\t\t\t\t\tOAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI));\n\t}\n\n\t@Test\n\tpublic void constructorWhenFilterProcessesUrlIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2LoginAuthenticationFilter(this.clientRegistrationRepository,\n\t\t\t\t\tthis.authorizedClientRepository, null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizationRequestRepositoryWhenAuthorizationRequestRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRequestRepository(null));\n\t}\n\n\t// gh-10033\n\t@Test\n\tpublic void setAuthenticationResultConverterWhenAuthenticationResultConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationResultConverter(null));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotAuthorizationResponseThenNextFilter() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tverify(this.filter, never()).attemptAuthentication(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationResponseInvalidThenInvalidRequestError() throws Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration1.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).build();\n\t\t// NOTE:\n\t\t// A valid Authorization Response contains either a 'code' or 'error' parameter.\n\t\t// Don't set it to force an invalid Authorization Response.\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tArgumentCaptor<AuthenticationException> authenticationExceptionArgCaptor = ArgumentCaptor\n\t\t\t.forClass(AuthenticationException.class);\n\t\tverify(this.failureHandler).onAuthenticationFailure(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), authenticationExceptionArgCaptor.capture());\n\t\tassertThat(authenticationExceptionArgCaptor.getValue()).isInstanceOf(OAuth2AuthenticationException.class);\n\t\tOAuth2AuthenticationException authenticationException = (OAuth2AuthenticationException) authenticationExceptionArgCaptor\n\t\t\t.getValue();\n\t\tassertThat(authenticationException.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationResponseAuthorizationRequestNotFoundThenAuthorizationRequestNotFoundError()\n\t\t\tthrows Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration2.getRegistrationId();\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, \"state\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tArgumentCaptor<AuthenticationException> authenticationExceptionArgCaptor = ArgumentCaptor\n\t\t\t.forClass(AuthenticationException.class);\n\t\tverify(this.failureHandler).onAuthenticationFailure(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), authenticationExceptionArgCaptor.capture());\n\t\tassertThat(authenticationExceptionArgCaptor.getValue()).isInstanceOf(OAuth2AuthenticationException.class);\n\t\tOAuth2AuthenticationException authenticationException = (OAuth2AuthenticationException) authenticationExceptionArgCaptor\n\t\t\t.getValue();\n\t\tassertThat(authenticationException.getError().getErrorCode()).isEqualTo(\"authorization_request_not_found\");\n\t}\n\n\t// gh-5251\n\t@Test\n\tpublic void doFilterWhenAuthorizationResponseClientRegistrationNotFoundThenClientRegistrationNotFoundError()\n\t\t\tthrows Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration2.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\t// @formatter:off\n\t\tClientRegistration registrationNotFound = ClientRegistration.withRegistrationId(\"registration-not-found\")\n\t\t\t\t.clientId(\"client-1\")\n\t\t\t\t.clientSecret(\"secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n\t\t\t\t.scope(\"user\")\n\t\t\t\t.authorizationUri(\"https://provider.com/oauth2/authorize\")\n\t\t\t\t.tokenUri(\"https://provider.com/oauth2/token\")\n\t\t\t\t.userInfoUri(\"https://provider.com/oauth2/user\")\n\t\t\t\t.userNameAttributeName(\"id\")\n\t\t\t\t.clientName(\"client-1\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.setUpAuthorizationRequest(request, response, registrationNotFound, state);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tArgumentCaptor<AuthenticationException> authenticationExceptionArgCaptor = ArgumentCaptor\n\t\t\t.forClass(AuthenticationException.class);\n\t\tverify(this.failureHandler).onAuthenticationFailure(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), authenticationExceptionArgCaptor.capture());\n\t\tassertThat(authenticationExceptionArgCaptor.getValue()).isInstanceOf(OAuth2AuthenticationException.class);\n\t\tOAuth2AuthenticationException authenticationException = (OAuth2AuthenticationException) authenticationExceptionArgCaptor\n\t\t\t.getValue();\n\t\tassertThat(authenticationException.getError().getErrorCode()).isEqualTo(\"client_registration_not_found\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationResponseValidThenAuthorizationRequestRemoved() throws Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration2.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration2, state);\n\t\tthis.setUpAuthenticationResult(this.registration2);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(this.authorizationRequestRepository.loadAuthorizationRequest(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationResponseValidThenAuthorizedClientSaved() throws Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration1.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration1, state);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registration1.getRegistrationId(), this.loginAuthentication, request);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration1);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principalName1);\n\t\tassertThat(authorizedClient.getAccessToken()).isNotNull();\n\t\tassertThat(authorizedClient.getRefreshToken()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomFilterProcessesUrlThenFilterProcesses() throws Exception {\n\t\tString filterProcessesUrl = \"/login/oauth2/custom/*\";\n\t\tthis.filter = spy(new OAuth2LoginAuthenticationFilter(this.clientRegistrationRepository,\n\t\t\t\tthis.authorizedClientRepository, filterProcessesUrl));\n\t\tthis.filter.setAuthenticationManager(this.authenticationManager);\n\t\tString requestUri = \"/login/oauth2/custom/\" + this.registration2.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration2, state);\n\t\tthis.setUpAuthenticationResult(this.registration2);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tverifyNoMoreInteractions(filterChain);\n\t\tverify(this.filter).attemptAuthentication(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t// gh-5890\n\t@Test\n\tpublic void doFilterWhenAuthorizationResponseHasDefaultPort80ThenRedirectUriMatchingExcludesPort()\n\t\t\tthrows Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration2.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration2, state);\n\t\tthis.setUpAuthenticationResult(this.registration2);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tArgumentCaptor<Authentication> authenticationArgCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationManager).authenticate(authenticationArgCaptor.capture());\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) authenticationArgCaptor\n\t\t\t.getValue();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest();\n\t\tOAuth2AuthorizationResponse authorizationResponse = authentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationResponse();\n\t\tString expectedRedirectUri = \"http://localhost/login/oauth2/code/registration-id-2\";\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedRedirectUri);\n\t\tassertThat(authorizationResponse.getRedirectUri()).isEqualTo(expectedRedirectUri);\n\t}\n\n\t// gh-5890\n\t@Test\n\tpublic void doFilterWhenAuthorizationResponseHasDefaultPort443ThenRedirectUriMatchingExcludesPort()\n\t\t\tthrows Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration2.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(\"https://example.com:443\" + requestUri)\n\t\t\t.param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration2, state);\n\t\tthis.setUpAuthenticationResult(this.registration2);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tArgumentCaptor<Authentication> authenticationArgCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationManager).authenticate(authenticationArgCaptor.capture());\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) authenticationArgCaptor\n\t\t\t.getValue();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest();\n\t\tOAuth2AuthorizationResponse authorizationResponse = authentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationResponse();\n\t\tString expectedRedirectUri = \"https://example.com/login/oauth2/code/registration-id-2\";\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedRedirectUri);\n\t\tassertThat(authorizationResponse.getRedirectUri()).isEqualTo(expectedRedirectUri);\n\t}\n\n\t// gh-5890\n\t@Test\n\tpublic void doFilterWhenAuthorizationResponseHasNonDefaultPortThenRedirectUriMatchingIncludesPort()\n\t\t\tthrows Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration2.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(\"https://example.com:9090\" + requestUri)\n\t\t\t.param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration2, state);\n\t\tthis.setUpAuthenticationResult(this.registration2);\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tArgumentCaptor<Authentication> authenticationArgCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.authenticationManager).authenticate(authenticationArgCaptor.capture());\n\t\tOAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) authenticationArgCaptor\n\t\t\t.getValue();\n\t\tOAuth2AuthorizationRequest authorizationRequest = authentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationRequest();\n\t\tOAuth2AuthorizationResponse authorizationResponse = authentication.getAuthorizationExchange()\n\t\t\t.getAuthorizationResponse();\n\t\tString expectedRedirectUri = \"https://example.com:9090/login/oauth2/code/registration-id-2\";\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedRedirectUri);\n\t\tassertThat(authorizationResponse.getRedirectUri()).isEqualTo(expectedRedirectUri);\n\t}\n\n\t// gh-6866\n\t@Test\n\tpublic void attemptAuthenticationShouldSetAuthenticationDetailsOnAuthenticationResult() throws Exception {\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration1.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tWebAuthenticationDetails webAuthenticationDetails = mock(WebAuthenticationDetails.class);\n\t\tgiven(this.authenticationDetailsSource.buildDetails(any())).willReturn(webAuthenticationDetails);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration2, state);\n\t\tthis.setUpAuthenticationResult(this.registration2);\n\t\tAuthentication result = this.filter.attemptAuthentication(request, response);\n\t\tassertThat(result.getDetails()).isEqualTo(webAuthenticationDetails);\n\t}\n\n\t// gh-10033\n\t@Test\n\tpublic void attemptAuthenticationWhenAuthenticationResultIsNullThenIllegalArgumentException() throws Exception {\n\t\tthis.filter.setAuthenticationResultConverter((authentication) -> null);\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration1.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration1, state);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.attemptAuthentication(request, response));\n\t}\n\n\t// gh-10033\n\t@Test\n\tpublic void attemptAuthenticationWhenAuthenticationResultConverterSetThenUsed() {\n\t\tthis.filter.setAuthenticationResultConverter(\n\t\t\t\t(authentication) -> new CustomOAuth2AuthenticationToken(authentication.getPrincipal(),\n\t\t\t\t\t\tauthentication.getAuthorities(), authentication.getClientRegistration().getRegistrationId()));\n\t\tString requestUri = \"/login/oauth2/code/\" + this.registration1.getRegistrationId();\n\t\tString state = \"state\";\n\t\tMockHttpServletRequest request = get(requestUri).param(OAuth2ParameterNames.CODE, \"code\")\n\t\t\t.param(OAuth2ParameterNames.STATE, state)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.setUpAuthorizationRequest(request, response, this.registration1, state);\n\t\tthis.setUpAuthenticationResult(this.registration1);\n\t\tAuthentication authenticationResult = this.filter.attemptAuthentication(request, response);\n\t\tassertThat(authenticationResult).isInstanceOf(CustomOAuth2AuthenticationToken.class);\n\t}\n\n\tprivate void setUpAuthorizationRequest(HttpServletRequest request, HttpServletResponse response,\n\t\t\tClientRegistration registration, String state) {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, registration.getRegistrationId());\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(registration.getProviderDetails().getAuthorizationUri())\n\t\t\t.clientId(registration.getClientId())\n\t\t\t.redirectUri(expandRedirectUri(request, registration))\n\t\t\t.scopes(registration.getScopes())\n\t\t\t.state(state)\n\t\t\t.attributes(attributes)\n\t\t\t.build();\n\t\tthis.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);\n\t}\n\n\tprivate String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration) {\n\t\tString baseUrl = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t.replaceQuery(null)\n\t\t\t.replacePath(request.getContextPath())\n\t\t\t.build()\n\t\t\t.toUriString();\n\t\tMap<String, String> uriVariables = new HashMap<>();\n\t\turiVariables.put(\"baseUrl\", baseUrl);\n\t\turiVariables.put(\"action\", \"login\");\n\t\turiVariables.put(\"registrationId\", clientRegistration.getRegistrationId());\n\t\treturn UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri())\n\t\t\t.buildAndExpand(uriVariables)\n\t\t\t.toUriString();\n\t}\n\n\tprivate void setUpAuthenticationResult(ClientRegistration registration) {\n\t\tOAuth2User user = mock(OAuth2User.class);\n\t\tgiven(user.getName()).willReturn(this.principalName1);\n\t\tthis.loginAuthentication = mock(OAuth2LoginAuthenticationToken.class);\n\t\tgiven(this.loginAuthentication.getPrincipal()).willReturn(user);\n\t\tgiven(this.loginAuthentication.getName()).willReturn(this.principalName1);\n\t\tgiven(this.loginAuthentication.getAuthorities()).willReturn(AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tgiven(this.loginAuthentication.getClientRegistration()).willReturn(registration);\n\t\tgiven(this.loginAuthentication.getAuthorizationExchange())\n\t\t\t.willReturn(TestOAuth2AuthorizationExchanges.success());\n\t\tgiven(this.loginAuthentication.getAccessToken()).willReturn(mock(OAuth2AccessToken.class));\n\t\tgiven(this.loginAuthentication.getRefreshToken()).willReturn(mock(OAuth2RefreshToken.class));\n\t\tgiven(this.loginAuthentication.isAuthenticated()).willReturn(true);\n\t\tgiven(this.authenticationManager.authenticate(any(Authentication.class))).willReturn(this.loginAuthentication);\n\t}\n\n\tprivate static final class CustomOAuth2AuthenticationToken extends OAuth2AuthenticationToken {\n\n\t\tCustomOAuth2AuthenticationToken(OAuth2User principal, Collection<? extends GrantedAuthority> authorities,\n\t\t\t\tString authorizedClientRegistrationId) {\n\t\t\tsuper(principal, authorities, authorizedClientRegistrationId);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/AbstractMockServerClientRegistrationIdProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport java.io.IOException;\n\nimport okhttp3.HttpUrl;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.annotation.ClientRegistrationId;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.web.service.annotation.GetExchange;\nimport org.springframework.web.service.invoker.HttpExchangeAdapter;\nimport org.springframework.web.service.invoker.HttpServiceProxyFactory;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Base class for integration testing {@link ClientRegistrationIdProcessor} with\n * {@link MockWebServer}.\n *\n * @author Rob Winch\n * @since 7.0\n */\nabstract class AbstractMockServerClientRegistrationIdProcessorTests {\n\n\tstatic final String REGISTRATION_ID = \"okta\";\n\n\tprivate final MockWebServer server = new MockWebServer();\n\n\tprivate OAuth2AccessToken accessToken;\n\n\tprotected String baseUrl;\n\n\tprotected OAuth2AuthorizedClient authorizedClient;\n\n\t@BeforeEach\n\tvoid setup() throws IOException {\n\t\tthis.server.start();\n\t\tHttpUrl url = this.server.url(\"/range/\");\n\t\tthis.baseUrl = url.toString();\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.registrationId(REGISTRATION_ID)\n\t\t\t.build();\n\t\tthis.accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(clientRegistration, \"user\", this.accessToken);\n\t}\n\n\t@AfterEach\n\tvoid cleanup() throws IOException {\n\t\tif (this.server != null) {\n\t\t\tthis.server.shutdown();\n\t\t}\n\t}\n\n\tvoid testWithAdapter(HttpExchangeAdapter adapter) throws InterruptedException {\n\t\tClientRegistrationIdProcessor processor = ClientRegistrationIdProcessor.DEFAULT_INSTANCE;\n\t\tHttpServiceProxyFactory factory = HttpServiceProxyFactory.builder()\n\t\t\t.exchangeAdapter(adapter)\n\t\t\t.httpRequestValuesProcessor(processor)\n\t\t\t.build();\n\t\tMessageClient messages = factory.createClient(MessageClient.class);\n\n\t\tthis.server.enqueue(new MockResponse().setBody(\"Hello OAuth2!\").setResponseCode(200));\n\t\tassertThat(messages.getMessage()).isEqualTo(\"Hello OAuth2!\");\n\n\t\tString authorizationHeader = this.server.takeRequest().getHeader(HttpHeaders.AUTHORIZATION);\n\t\tassertOAuthTokenValue(authorizationHeader, this.accessToken);\n\n\t}\n\n\tprivate static void assertOAuthTokenValue(String value, OAuth2AccessToken accessToken) {\n\t\tString tokenType = accessToken.getTokenType().getValue();\n\t\tString tokenValue = accessToken.getTokenValue();\n\t\tassertThat(value).isEqualTo(\"%s %s\".formatted(tokenType, tokenValue));\n\t}\n\n\tinterface MessageClient {\n\n\t\t@GetExchange(\"/message\")\n\t\t@ClientRegistrationId(REGISTRATION_ID)\n\t\tString getMessage();\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorRestClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.web.client.RestClient;\nimport org.springframework.web.client.support.RestClientAdapter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * Runs tests of {@link ClientRegistrationIdProcessor} with {@link RestClient} to ensure\n * that all the parts work together properly.\n *\n * @author Rob Winch\n * @since 7.0\n */\n@ExtendWith(MockitoExtension.class)\nclass ClientRegistrationIdProcessorRestClientTests extends AbstractMockServerClientRegistrationIdProcessorTests {\n\n\t@Mock\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@Test\n\tvoid clientRegistrationIdProcessorWorksWithRestClientAdapter() throws InterruptedException {\n\t\tOAuth2ClientHttpRequestInterceptor interceptor = new OAuth2ClientHttpRequestInterceptor(\n\t\t\t\tthis.authorizedClientManager);\n\t\tRestClient.Builder builder = RestClient.builder().requestInterceptor(interceptor).baseUrl(this.baseUrl);\n\n\t\tArgumentCaptor<OAuth2AuthorizeRequest> authorizeRequest = ArgumentCaptor.forClass(OAuth2AuthorizeRequest.class);\n\t\tgiven(this.authorizedClientManager.authorize(authorizeRequest.capture())).willReturn(authorizedClient);\n\n\t\ttestWithAdapter(RestClientAdapter.create(builder.build()));\n\n\t\tassertThat(authorizeRequest.getValue().getClientRegistrationId()).isEqualTo(REGISTRATION_ID);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.annotation.AnnotationConfigurationException;\nimport org.springframework.security.oauth2.client.annotation.ClientRegistrationId;\nimport org.springframework.security.oauth2.client.web.ClientAttributes;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.web.service.invoker.HttpRequestValues;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Unit tests for {@link ClientRegistrationIdProcessor}.\n *\n * @author Rob Winch\n * @since 7.0\n * @see ClientRegistrationIdProcessorWebClientTests\n * @see ClientRegistrationIdProcessorRestClientTests\n */\nclass ClientRegistrationIdProcessorTests {\n\n\tprivate static final String REGISTRATION_ID = \"registrationId\";\n\n\tClientRegistrationIdProcessor processor = ClientRegistrationIdProcessor.DEFAULT_INSTANCE;\n\n\t@Test\n\tvoid processWhenClientRegistrationIdPresentThenSet() {\n\t\tHttpRequestValues.Builder builder = HttpRequestValues.builder();\n\t\tMethod hasClientRegistrationId = ReflectionUtils.findMethod(RestService.class, \"hasClientRegistrationId\");\n\t\tthis.processor.process(hasClientRegistrationId, null, null, builder);\n\n\t\tString registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes());\n\t\tassertThat(registrationId).isEqualTo(REGISTRATION_ID);\n\t}\n\n\t@Test\n\tvoid processWhenMetaClientRegistrationIdPresentThenSet() {\n\t\tHttpRequestValues.Builder builder = HttpRequestValues.builder();\n\t\tMethod hasClientRegistrationId = ReflectionUtils.findMethod(RestService.class, \"hasMetaClientRegistrationId\");\n\t\tthis.processor.process(hasClientRegistrationId, null, null, builder);\n\n\t\tString registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes());\n\t\tassertThat(registrationId).isEqualTo(REGISTRATION_ID);\n\t}\n\n\t@Test\n\tvoid processWhenNoClientRegistrationIdPresentThenNull() {\n\t\tHttpRequestValues.Builder builder = HttpRequestValues.builder();\n\t\tMethod hasClientRegistrationId = ReflectionUtils.findMethod(RestService.class, \"noClientRegistrationId\");\n\t\tthis.processor.process(hasClientRegistrationId, null, null, builder);\n\n\t\tString registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes());\n\t\tassertThat(registrationId).isNull();\n\t}\n\n\t@Test\n\tvoid processWhenClientRegistrationIdPresentOnDeclaringClassThenSet() {\n\t\tHttpRequestValues.Builder builder = HttpRequestValues.builder();\n\t\tMethod declaringClassHasClientRegistrationId = ReflectionUtils.findMethod(TypeAnnotatedRestService.class,\n\t\t\t\t\"declaringClassHasClientRegistrationId\");\n\t\tthis.processor.process(declaringClassHasClientRegistrationId, null, null, builder);\n\n\t\tString registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes());\n\t\tassertThat(registrationId).isEqualTo(REGISTRATION_ID);\n\t}\n\n\t@Test\n\tvoid processWhenDuplicateClientRegistrationIdPresentOnAggregateServiceThenException() {\n\t\tHttpRequestValues.Builder builder = HttpRequestValues.builder();\n\t\tMethod shouldFailDueToDuplicateClientRegistrationId = ReflectionUtils.findMethod(AggregateRestService.class,\n\t\t\t\t\"shouldFailDueToDuplicateClientRegistrationId\");\n\n\t\tassertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(\n\t\t\t\t() -> this.processor.process(shouldFailDueToDuplicateClientRegistrationId, null, null, builder));\n\t}\n\n\tinterface RestService {\n\n\t\t@ClientRegistrationId(REGISTRATION_ID)\n\t\tvoid hasClientRegistrationId();\n\n\t\t@MetaClientRegistrationId\n\t\tvoid hasMetaClientRegistrationId();\n\n\t\tvoid noClientRegistrationId();\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ClientRegistrationId(REGISTRATION_ID)\n\t@interface MetaClientRegistrationId {\n\n\t}\n\n\t@ClientRegistrationId(REGISTRATION_ID)\n\tinterface TypeAnnotatedRestService {\n\n\t\tvoid declaringClassHasClientRegistrationId();\n\n\t}\n\n\t@ClientRegistrationId(\"a\")\n\tinterface ARestService {\n\n\t}\n\n\t@ClientRegistrationId(\"b\")\n\tinterface BRestService {\n\n\t}\n\n\tinterface AggregateRestService extends ARestService, BRestService {\n\n\t\tvoid shouldFailDueToDuplicateClientRegistrationId();\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorWebClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;\nimport org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.support.WebClientAdapter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Runs tests for {@link ClientRegistrationIdProcessor} with {@link WebClient} to ensure\n * that all the parts work together properly.\n *\n * @author Rob Winch\n * @since 7.0\n */\n@ExtendWith(MockitoExtension.class)\nclass ClientRegistrationIdProcessorWebClientTests extends AbstractMockServerClientRegistrationIdProcessorTests {\n\n\t@Test\n\tvoid clientRegistrationIdProcessorWorksWithReactiveWebClient() throws InterruptedException {\n\t\tReactiveOAuth2AuthorizedClientManager authorizedClientManager = mock(\n\t\t\t\tReactiveOAuth2AuthorizedClientManager.class);\n\t\tServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServerOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\t\tauthorizedClientManager);\n\n\t\tWebClient.Builder builder = WebClient.builder().filter(oauth2Client).baseUrl(this.baseUrl);\n\n\t\tArgumentCaptor<OAuth2AuthorizeRequest> authorizeRequest = ArgumentCaptor.forClass(OAuth2AuthorizeRequest.class);\n\t\tgiven(authorizedClientManager.authorize(authorizeRequest.capture()))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\n\t\ttestWithAdapter(WebClientAdapter.create(builder.build()));\n\n\t\tassertThat(authorizeRequest.getValue().getClientRegistrationId()).isEqualTo(REGISTRATION_ID);\n\t}\n\n\t@Test\n\tvoid clientRegistrationIdProcessorWorksWithServletWebClient() throws InterruptedException {\n\t\tOAuth2AuthorizedClientManager authorizedClientManager = mock(OAuth2AuthorizedClientManager.class);\n\n\t\tServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\t\tauthorizedClientManager);\n\n\t\tWebClient.Builder builder = WebClient.builder()\n\t\t\t.defaultRequest((requestSpec) -> requestSpec.attributes((attrs) -> {\n\t\t\t\tattrs.put(HttpServletRequest.class.getName(), new MockHttpServletRequest());\n\t\t\t\tattrs.put(HttpServletResponse.class.getName(), new MockHttpServletResponse());\n\t\t\t}))\n\t\t\t.filter(oauth2Client)\n\t\t\t.baseUrl(this.baseUrl);\n\n\t\tArgumentCaptor<OAuth2AuthorizeRequest> authorizeRequest = ArgumentCaptor.forClass(OAuth2AuthorizeRequest.class);\n\t\tgiven(authorizedClientManager.authorize(authorizeRequest.capture())).willReturn(this.authorizedClient);\n\n\t\ttestWithAdapter(WebClientAdapter.create(builder.build()));\n\n\t\tassertThat(authorizeRequest.getValue().getClientRegistrationId()).isEqualTo(REGISTRATION_ID);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/OAuth2ClientHttpRequestInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpRequest;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.test.web.client.MockRestServiceServer;\nimport org.springframework.test.web.client.RequestMatcher;\nimport org.springframework.test.web.client.ResponseCreator;\nimport org.springframework.web.client.HttpClientErrorException;\nimport org.springframework.web.client.HttpServerErrorException;\nimport org.springframework.web.client.RestClient;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.test.web.client.match.MockRestRequestMatchers.header;\nimport static org.springframework.test.web.client.match.MockRestRequestMatchers.headerDoesNotExist;\nimport static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;\nimport static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;\nimport static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;\n\n/**\n * Tests for {@link OAuth2ClientHttpRequestInterceptor}.\n *\n * @author Steve Riesenberg\n */\n@ExtendWith(MockitoExtension.class)\npublic class OAuth2ClientHttpRequestInterceptorTests {\n\n\tprivate static final String REQUEST_URI = \"/resources\";\n\n\tprivate static final String ERROR_DESCRIPTION = \"The request requires higher privileges than provided by the access token.\";\n\n\tprivate static final String ERROR_URI = \"https://tools.ietf.org/html/rfc6750#section-3.1\";\n\n\t@Mock\n\tprivate OAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@Mock\n\tprivate OAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t@Mock\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Mock\n\tprivate OAuth2AuthorizedClientService authorizedClientService;\n\n\t@Mock\n\tprivate OAuth2ClientHttpRequestInterceptor.ClientRegistrationIdResolver clientRegistrationIdResolver;\n\n\t@Mock\n\tprivate OAuth2ClientHttpRequestInterceptor.PrincipalResolver principalResolver;\n\n\t@Captor\n\tprivate ArgumentCaptor<OAuth2AuthorizeRequest> authorizeRequestCaptor;\n\n\t@Captor\n\tprivate ArgumentCaptor<OAuth2AuthorizationException> authorizationExceptionCaptor;\n\n\t@Captor\n\tprivate ArgumentCaptor<Authentication> authenticationCaptor;\n\n\t@Captor\n\tprivate ArgumentCaptor<Map<String, Object>> attributesCaptor;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate OAuth2AuthenticationToken principal;\n\n\tprivate OAuth2ClientHttpRequestInterceptor requestInterceptor;\n\n\tprivate MockRestServiceServer server;\n\n\tprivate RestClient restClient;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, \"user\", accessToken);\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"OAUTH2_USER\");\n\t\tMap<String, Object> attributes = Map.of(StandardClaimNames.SUB, \"user\");\n\t\tOAuth2User user = new DefaultOAuth2User(authorities, attributes, StandardClaimNames.SUB);\n\t\tthis.principal = new OAuth2AuthenticationToken(user, authorities, \"login-client\");\n\t\tthis.requestInterceptor = new OAuth2ClientHttpRequestInterceptor(this.authorizedClientManager);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t\tRequestContextHolder.resetRequestAttributes();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientManagerIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2ClientHttpRequestInterceptor(null))\n\t\t\t.withMessage(\"authorizedClientManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthorizationFailureHandlerWhenNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.requestInterceptor.setAuthorizationFailureHandler(null))\n\t\t\t.withMessage(\"authorizationFailureHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizationFailureHandlerWhenAuthorizedClientRepositoryIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2ClientHttpRequestInterceptor\n\t\t\t\t.authorizationFailureHandler((OAuth2AuthorizedClientRepository) null))\n\t\t\t.withMessage(\"authorizedClientRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorizationFailureHandlerWhenAuthorizedClientServiceIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> OAuth2ClientHttpRequestInterceptor\n\t\t\t\t.authorizationFailureHandler((OAuth2AuthorizedClientService) null))\n\t\t\t.withMessage(\"authorizedClientService cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setClientRegistrationIdResolverWhenNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.requestInterceptor.setClientRegistrationIdResolver(null))\n\t\t\t.withMessage(\"clientRegistrationIdResolver cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setPrincipalResolverWhenNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.requestInterceptor.setPrincipalResolver(null))\n\t\t\t.withMessage(\"principalResolver cannot be null\");\n\t}\n\n\t@Test\n\tpublic void interceptWhenAnonymousThenAuthorizationHeaderNotSet() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(headerDoesNotExist(HttpHeaders.AUTHORIZATION))\n\t\t\t.andRespond(withApplicationJson());\n\t\tperformRequest(withDefaults());\n\t\tthis.server.verify();\n\t\tverifyNoInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAnonymousAndAuthorizedThenAuthorizationHeaderSet() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withApplicationJson());\n\t\tperformRequest(withClientRegistrationId());\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(this.authorizeRequestCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager);\n\t\tverifyNoInteractions(this.authorizationFailureHandler);\n\t\tOAuth2AuthorizeRequest authorizeRequest = this.authorizeRequestCaptor.getValue();\n\t\tassertThat(authorizeRequest.getClientRegistrationId()).isEqualTo(this.clientRegistration.getRegistrationId());\n\t\tassertThat(authorizeRequest.getPrincipal()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAnonymousAndNotAuthorizedThenAuthorizationHeaderNotSet() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class))).willReturn(null);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(headerDoesNotExist(HttpHeaders.AUTHORIZATION))\n\t\t\t.andRespond(withApplicationJson());\n\t\tperformRequest(withClientRegistrationId());\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(this.authorizeRequestCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager);\n\t\tverifyNoInteractions(this.authorizationFailureHandler);\n\t\tOAuth2AuthorizeRequest authorizeRequest = this.authorizeRequestCaptor.getValue();\n\t\tassertThat(authorizeRequest.getClientRegistrationId()).isEqualTo(this.clientRegistration.getRegistrationId());\n\t\tassertThat(authorizeRequest.getPrincipal()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAuthenticatedAndAuthorizedThenAuthorizationHeaderSet() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withApplicationJson());\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tperformRequest(withClientRegistrationId());\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(this.authorizeRequestCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager);\n\t\tverifyNoInteractions(this.authorizationFailureHandler);\n\t\tOAuth2AuthorizeRequest authorizeRequest = this.authorizeRequestCaptor.getValue();\n\t\tassertThat(authorizeRequest.getClientRegistrationId()).isEqualTo(this.clientRegistration.getRegistrationId());\n\t\tassertThat(authorizeRequest.getPrincipal()).isEqualTo(this.principal);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAuthenticatedAndNotAuthorizedThenAuthorizationHeaderNotSet() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class))).willReturn(null);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(headerDoesNotExist(HttpHeaders.AUTHORIZATION))\n\t\t\t.andRespond(withApplicationJson());\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tperformRequest(withClientRegistrationId());\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(this.authorizeRequestCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager);\n\t\tverifyNoInteractions(this.authorizationFailureHandler);\n\t\tOAuth2AuthorizeRequest authorizeRequest = this.authorizeRequestCaptor.getValue();\n\t\tassertThat(authorizeRequest.getClientRegistrationId()).isEqualTo(this.clientRegistration.getRegistrationId());\n\t\tassertThat(authorizeRequest.getPrincipal()).isInstanceOf(OAuth2AuthenticationToken.class);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAnonymousAndUnauthorizedThenDoesNotCallAuthorizationFailureHandler() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(headerDoesNotExist(HttpHeaders.AUTHORIZATION))\n\t\t\t.andRespond(withWwwAuthenticateHeader(HttpStatus.UNAUTHORIZED));\n\t\tassertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> performRequest(withDefaults()))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED));\n\t\tthis.server.verify();\n\t\tverifyNoInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAnonymousAndOAuth2ErrorInWwwAuthenticateHeaderThenCallsAuthorizationFailureHandlerWithInsufficientScopeError() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withWwwAuthenticateHeader(HttpStatus.OK));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tperformRequest(withClientRegistrationId());\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t\tassertThat(this.authorizationExceptionCaptor.getValue()).isInstanceOfSatisfying(\n\t\t\t\tClientAuthorizationException.class,\n\t\t\t\thasOAuth2Error(OAuth2ErrorCodes.INSUFFICIENT_SCOPE, ERROR_DESCRIPTION));\n\t\tassertThat(this.authenticationCaptor.getValue()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(entry(HttpServletRequest.class.getName(), request),\n\t\t\t\tentry(HttpServletResponse.class.getName(), response));\n\t}\n\n\t@Test\n\tpublic void interceptWhenAuthenticatedAndOAuth2ErrorInWwwAuthenticateHeaderThenCallsAuthorizationFailureHandlerWithInsufficientScopeError() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withWwwAuthenticateHeader(HttpStatus.OK));\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tperformRequest(withClientRegistrationId());\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t\tassertThat(this.authorizationExceptionCaptor.getValue()).isInstanceOfSatisfying(\n\t\t\t\tClientAuthorizationException.class,\n\t\t\t\thasOAuth2Error(OAuth2ErrorCodes.INSUFFICIENT_SCOPE, ERROR_DESCRIPTION));\n\t\tassertThat(this.authenticationCaptor.getValue()).isEqualTo(this.principal);\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(entry(HttpServletRequest.class.getName(), request),\n\t\t\t\tentry(HttpServletResponse.class.getName(), response));\n\t}\n\n\t@Test\n\tpublic void interceptWhenUnauthorizedAndOAuth2ErrorInWwwAuthenticateHeaderThenCallsAuthorizationFailureHandlerWithInsufficientScopeError() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withWwwAuthenticateHeader(HttpStatus.UNAUTHORIZED));\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tassertThatExceptionOfType(HttpClientErrorException.class)\n\t\t\t.isThrownBy(() -> performRequest(withClientRegistrationId()))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED));\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t\tassertThat(this.authorizationExceptionCaptor.getValue()).isInstanceOfSatisfying(\n\t\t\t\tClientAuthorizationException.class,\n\t\t\t\thasOAuth2Error(OAuth2ErrorCodes.INSUFFICIENT_SCOPE, ERROR_DESCRIPTION));\n\t\tassertThat(this.authenticationCaptor.getValue()).isEqualTo(this.principal);\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(entry(HttpServletRequest.class.getName(), request),\n\t\t\t\tentry(HttpServletResponse.class.getName(), response));\n\t}\n\n\t@Test\n\tpublic void interceptWhenForbiddenAndOAuth2ErrorInWwwAuthenticateHeaderThenCallsAuthorizationFailureHandlerWithInsufficientScopeError() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withWwwAuthenticateHeader(HttpStatus.FORBIDDEN));\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tassertThatExceptionOfType(HttpClientErrorException.class)\n\t\t\t.isThrownBy(() -> performRequest(withClientRegistrationId()))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN));\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t\tassertThat(this.authorizationExceptionCaptor.getValue()).isInstanceOfSatisfying(\n\t\t\t\tClientAuthorizationException.class,\n\t\t\t\thasOAuth2Error(OAuth2ErrorCodes.INSUFFICIENT_SCOPE, ERROR_DESCRIPTION));\n\t\tassertThat(this.authenticationCaptor.getValue()).isEqualTo(this.principal);\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(entry(HttpServletRequest.class.getName(), request),\n\t\t\t\tentry(HttpServletResponse.class.getName(), response));\n\t}\n\n\t@Test\n\tpublic void interceptWhenUnauthorizedThenCallsAuthorizationFailureHandlerWithInvalidTokenError() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withStatus(HttpStatus.UNAUTHORIZED));\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tassertThatExceptionOfType(HttpClientErrorException.class)\n\t\t\t.isThrownBy(() -> performRequest(withClientRegistrationId()))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED));\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t\tassertThat(this.authorizationExceptionCaptor.getValue()).isInstanceOfSatisfying(\n\t\t\t\tClientAuthorizationException.class, hasOAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, null));\n\t\tassertThat(this.authenticationCaptor.getValue()).isEqualTo(this.principal);\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(entry(HttpServletRequest.class.getName(), request),\n\t\t\t\tentry(HttpServletResponse.class.getName(), response));\n\t}\n\n\t@Test\n\tpublic void interceptWhenForbiddenThenCallsAuthorizationFailureHandlerWithInsufficientScopeError() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withStatus(HttpStatus.FORBIDDEN));\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tassertThatExceptionOfType(HttpClientErrorException.class)\n\t\t\t.isThrownBy(() -> performRequest(withClientRegistrationId()))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN));\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t\tassertThat(this.authorizationExceptionCaptor.getValue()).isInstanceOfSatisfying(\n\t\t\t\tClientAuthorizationException.class, hasOAuth2Error(OAuth2ErrorCodes.INSUFFICIENT_SCOPE, null));\n\t\tassertThat(this.authenticationCaptor.getValue()).isEqualTo(this.principal);\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(entry(HttpServletRequest.class.getName(), request),\n\t\t\t\tentry(HttpServletResponse.class.getName(), response));\n\t}\n\n\t@Test\n\tpublic void interceptWhenInternalServerErrorThenDoesNotCallAuthorizationFailureHandler() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));\n\t\tassertThatExceptionOfType(HttpServerErrorException.class)\n\t\t\t.isThrownBy(() -> performRequest(withClientRegistrationId()))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR));\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverifyNoMoreInteractions(this.authorizedClientManager);\n\t\tverifyNoInteractions(this.authorizationFailureHandler);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAuthorizationExceptionThenCallsAuthorizationFailureHandlerWithException() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tOAuth2AuthorizationException authorizationException = new OAuth2AuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN));\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withException(authorizationException));\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> performRequest(withClientRegistrationId()))\n\t\t\t.isEqualTo(authorizationException);\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizationFailureHandler);\n\t\tassertThat(this.authorizationExceptionCaptor.getValue()).isEqualTo(authorizationException);\n\t\tassertThat(this.authenticationCaptor.getValue()).isEqualTo(this.principal);\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(entry(HttpServletRequest.class.getName(), request),\n\t\t\t\tentry(HttpServletResponse.class.getName(), response));\n\t}\n\n\t@Test\n\tpublic void interceptWhenUnauthorizedAndAuthorizationFailureHandlerSetWithAuthorizedClientRepositoryThenAuthorizedClientRemoved() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(\n\t\t\t\tOAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(this.authorizedClientRepository));\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withStatus(HttpStatus.UNAUTHORIZED));\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tassertThatExceptionOfType(HttpClientErrorException.class)\n\t\t\t.isThrownBy(() -> performRequest(withClientRegistrationId()))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED));\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizedClientRepository).removeAuthorizedClient(this.clientRegistration.getRegistrationId(),\n\t\t\t\tthis.principal, request, response);\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizedClientRepository);\n\t}\n\n\t@Test\n\tpublic void interceptWhenUnauthorizedAndAuthorizationFailureHandlerSetWithAuthorizedClientServiceThenAuthorizedClientRemoved() {\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(\n\t\t\t\tOAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(this.authorizedClientService));\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withStatus(HttpStatus.UNAUTHORIZED));\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tassertThatExceptionOfType(HttpClientErrorException.class)\n\t\t\t.isThrownBy(() -> performRequest(withClientRegistrationId()))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED));\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(any(OAuth2AuthorizeRequest.class));\n\t\tverify(this.authorizedClientService).removeAuthorizedClient(this.clientRegistration.getRegistrationId(),\n\t\t\t\tthis.principal.getName());\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.authorizedClientService);\n\t}\n\n\t@Test\n\tpublic void interceptWhenCustomClientRegistrationIdResolverSetThenUsed() {\n\t\tthis.requestInterceptor.setClientRegistrationIdResolver(this.clientRegistrationIdResolver);\n\t\tthis.requestInterceptor.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tString clientRegistrationId = \"test-client\";\n\t\tgiven(this.clientRegistrationIdResolver.resolve(any(HttpRequest.class))).willReturn(clientRegistrationId);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withApplicationJson());\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(this.principal);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tperformRequest(withDefaults());\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(this.authorizeRequestCaptor.capture());\n\t\tverify(this.clientRegistrationIdResolver).resolve(any(HttpRequest.class));\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.clientRegistrationIdResolver);\n\t\tverifyNoInteractions(this.authorizationFailureHandler);\n\t\tOAuth2AuthorizeRequest authorizeRequest = this.authorizeRequestCaptor.getValue();\n\t\tassertThat(authorizeRequest.getClientRegistrationId()).isEqualTo(clientRegistrationId);\n\t\tassertThat(authorizeRequest.getPrincipal()).isEqualTo(this.principal);\n\t}\n\n\t@Test\n\tpublic void interceptWhenCustomPrincipalResolverSetThenUsed() {\n\t\tthis.requestInterceptor.setPrincipalResolver(this.principalResolver);\n\t\tgiven(this.authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class)))\n\t\t\t.willReturn(this.authorizedClient);\n\n\t\tbindToRestClient(withRequestInterceptor());\n\t\tthis.server.expect(requestTo(REQUEST_URI))\n\t\t\t.andExpect(hasAuthorizationHeader(this.authorizedClient.getAccessToken()))\n\t\t\t.andRespond(withApplicationJson());\n\t\tgiven(this.principalResolver.resolve(any(HttpRequest.class))).willReturn(this.principal);\n\t\tperformRequest(withClientRegistrationId());\n\t\tthis.server.verify();\n\t\tverify(this.authorizedClientManager).authorize(this.authorizeRequestCaptor.capture());\n\t\tverify(this.principalResolver).resolve(any(HttpRequest.class));\n\t\tverifyNoMoreInteractions(this.authorizedClientManager, this.principalResolver);\n\t\tOAuth2AuthorizeRequest authorizeRequest = this.authorizeRequestCaptor.getValue();\n\t\tassertThat(authorizeRequest.getClientRegistrationId()).isEqualTo(this.clientRegistration.getRegistrationId());\n\t\tassertThat(authorizeRequest.getPrincipal()).isEqualTo(this.principal);\n\t}\n\n\tprivate void bindToRestClient(Consumer<RestClient.Builder> customizer) {\n\t\tRestClient.Builder builder = RestClient.builder();\n\t\tcustomizer.accept(builder);\n\t\tthis.server = MockRestServiceServer.bindTo(builder).build();\n\t\tthis.restClient = builder.build();\n\t}\n\n\tprivate Consumer<RestClient.Builder> withRequestInterceptor() {\n\t\treturn (builder) -> builder.requestInterceptor(this.requestInterceptor);\n\t}\n\n\tprivate static RequestMatcher hasAuthorizationHeader(OAuth2AccessToken accessToken) {\n\t\tString tokenType = accessToken.getTokenType().getValue();\n\t\tString tokenValue = accessToken.getTokenValue();\n\t\treturn header(HttpHeaders.AUTHORIZATION, \"%s %s\".formatted(tokenType, tokenValue));\n\t}\n\n\tprivate static ResponseCreator withApplicationJson() {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\treturn withSuccess().headers(headers).body(\"{}\");\n\t}\n\n\tprivate static ResponseCreator withWwwAuthenticateHeader(HttpStatus httpStatus) {\n\t\tString wwwAuthenticateHeader = \"Bearer error=\\\"insufficient_scope\\\", \"\n\t\t\t\t+ \"error_description=\\\"The request requires higher privileges than provided by the access token.\\\", \"\n\t\t\t\t+ \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\";\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.set(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticateHeader);\n\t\treturn withStatus(httpStatus).headers(headers);\n\t}\n\n\tprivate static ResponseCreator withException(OAuth2AuthorizationException ex) {\n\t\treturn (request) -> {\n\t\t\tthrow ex;\n\t\t};\n\t}\n\n\tprivate void performRequest(Consumer<RestClient.RequestHeadersSpec<?>> customizer) {\n\t\tRestClient.RequestHeadersSpec<?> spec = this.restClient.get().uri(REQUEST_URI);\n\t\tcustomizer.accept(spec);\n\t\tspec.retrieve().toBodilessEntity();\n\t}\n\n\tprivate static Consumer<RestClient.RequestHeadersSpec<?>> withDefaults() {\n\t\treturn (spec) -> {\n\t\t};\n\t}\n\n\tprivate Consumer<RestClient.RequestHeadersSpec<?>> withClientRegistrationId() {\n\t\treturn (spec) -> spec.attributes(RequestAttributeClientRegistrationIdResolver\n\t\t\t.clientRegistrationId(this.clientRegistration.getRegistrationId()));\n\t}\n\n\tprivate Consumer<ClientAuthorizationException> hasOAuth2Error(String errorCode, String errorDescription) {\n\t\treturn (ex) -> {\n\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.clientRegistration.getRegistrationId());\n\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(errorCode);\n\t\t\tassertThat(ex.getError().getDescription()).isEqualTo(errorDescription);\n\t\t\tassertThat(ex.getError().getUri()).isEqualTo(ERROR_URI);\n\t\t\tassertThat(ex).hasNoCause();\n\t\t\tassertThat(ex).hasMessageContaining(errorCode);\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/support/OAuth2RestClientHttpServiceGroupConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.client.support;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor;\nimport org.springframework.security.oauth2.client.web.client.OAuth2ClientHttpRequestInterceptor;\nimport org.springframework.web.client.RestClient;\nimport org.springframework.web.service.invoker.HttpServiceProxyFactory;\nimport org.springframework.web.service.registry.HttpServiceGroupConfigurer;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link OAuth2RestClientHttpServiceGroupConfigurer}.\n *\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\nclass OAuth2RestClientHttpServiceGroupConfigurerTests {\n\n\t@Mock\n\tprivate OAuth2AuthorizedClientManager authoriedClientManager;\n\n\t@Mock\n\tprivate HttpServiceGroupConfigurer.Groups<RestClient.Builder> groups;\n\n\t@Captor\n\tArgumentCaptor<HttpServiceGroupConfigurer.ProxyFactoryCallback> forProxyFactory;\n\n\t@Mock\n\tprivate HttpServiceProxyFactory.Builder factoryBuilder;\n\n\t@Captor\n\tprivate ArgumentCaptor<HttpServiceGroupConfigurer.ClientCallback<RestClient.Builder>> configureClient;\n\n\t@Mock\n\tprivate RestClient.Builder clientBuilder;\n\n\t@Test\n\tvoid configureGroupsConfigureProxyFactory() {\n\n\t\tOAuth2RestClientHttpServiceGroupConfigurer configurer = OAuth2RestClientHttpServiceGroupConfigurer\n\t\t\t.from(this.authoriedClientManager);\n\n\t\tconfigurer.configureGroups(this.groups);\n\t\tverify(this.groups).forEachProxyFactory(this.forProxyFactory.capture());\n\n\t\tthis.forProxyFactory.getValue().withProxyFactory(null, this.factoryBuilder);\n\n\t\tverify(this.factoryBuilder).httpRequestValuesProcessor(ClientRegistrationIdProcessor.DEFAULT_INSTANCE);\n\t}\n\n\t@Test\n\tvoid configureGroupsConfigureClient() {\n\t\tOAuth2RestClientHttpServiceGroupConfigurer configurer = OAuth2RestClientHttpServiceGroupConfigurer\n\t\t\t.from(this.authoriedClientManager);\n\n\t\tconfigurer.configureGroups(this.groups);\n\t\tverify(this.groups).forEachClient(this.configureClient.capture());\n\n\t\tthis.configureClient.getValue().withClient(null, this.clientBuilder);\n\n\t\tverify(this.clientBuilder).requestInterceptor(any(OAuth2ClientHttpRequestInterceptor.class));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/method/annotation/OAuth2AuthorizedClientArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.method.annotation;\n\nimport java.lang.reflect.Method;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OAuth2AuthorizedClientArgumentResolver}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizedClientArgumentResolverTests {\n\n\tprivate TestingAuthenticationToken authentication;\n\n\tprivate String principalName = \"principal-1\";\n\n\tprivate ClientRegistration registration1;\n\n\tprivate ClientRegistration registration2;\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClient authorizedClient1;\n\n\tprivate OAuth2AuthorizedClient authorizedClient2;\n\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate OAuth2AuthorizedClientArgumentResolver argumentResolver;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authentication = new TestingAuthenticationToken(this.principalName, \"password\");\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(this.authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\t// @formatter:off\n\t\tthis.registration1 = ClientRegistration.withRegistrationId(\"client1\")\n\t\t\t\t.clientId(\"client-1\")\n\t\t\t\t.clientSecret(\"secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"{baseUrl}/login/oauth2/code/{registrationId}\")\n\t\t\t\t.scope(\"user\")\n\t\t\t\t.authorizationUri(\"https://provider.com/oauth2/authorize\")\n\t\t\t\t.tokenUri(\"https://provider.com/oauth2/token\")\n\t\t\t\t.userInfoUri(\"https://provider.com/oauth2/user\")\n\t\t\t\t.userNameAttributeName(\"id\")\n\t\t\t\t.clientName(\"client-1\")\n\t\t\t\t.build();\n\t\tthis.registration2 = ClientRegistration.withRegistrationId(\"client2\")\n\t\t\t\t.clientId(\"client-2\")\n\t\t\t\t.clientSecret(\"secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.tokenUri(\"https://provider.com/oauth2/token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.clientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1,\n\t\t\t\tthis.registration2);\n\t\tthis.authorizedClientRepository = mock(OAuth2AuthorizedClientRepository.class);\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t.authorizationCode()\n\t\t\t.refreshToken()\n\t\t\t.clientCredentials()\n\t\t\t.build();\n\t\tDefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\t\tthis.argumentResolver = new OAuth2AuthorizedClientArgumentResolver(authorizedClientManager);\n\t\tthis.authorizedClient1 = new OAuth2AuthorizedClient(this.registration1, this.principalName,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(eq(this.registration1.getRegistrationId()),\n\t\t\t\tany(Authentication.class), any(HttpServletRequest.class)))\n\t\t\t.willReturn(this.authorizedClient1);\n\t\tthis.authorizedClient2 = new OAuth2AuthorizedClient(this.registration2, this.principalName,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(eq(this.registration2.getRegistrationId()),\n\t\t\t\tany(Authentication.class), any(HttpServletRequest.class)))\n\t\t\t.willReturn(this.authorizedClient2);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizedClientArgumentResolver(null, this.authorizedClientRepository));\n\t}\n\n\t@Test\n\tpublic void constructorWhenOAuth2AuthorizedClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizedClientArgumentResolver(this.clientRegistrationRepository, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientManagerIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AuthorizedClientArgumentResolver(null));\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenParameterTypeOAuth2AuthorizedClientThenTrue() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThat(this.argumentResolver.supportsParameter(methodParameter)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenParameterTypeOAuth2AuthorizedClientWithoutAnnotationThenFalse() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClientWithoutAnnotation\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThat(this.argumentResolver.supportsParameter(methodParameter)).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenParameterTypeUnsupportedThenFalse() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeUnsupported\", String.class);\n\t\tassertThat(this.argumentResolver.supportsParameter(methodParameter)).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenParameterTypeUnsupportedWithoutAnnotationThenFalse() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeUnsupportedWithoutAnnotation\",\n\t\t\t\tString.class);\n\t\tassertThat(this.argumentResolver.supportsParameter(methodParameter)).isFalse();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenRegistrationIdEmptyAndNotOAuth2AuthenticationThenThrowIllegalArgumentException() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"registrationIdEmpty\", OAuth2AuthorizedClient.class);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.argumentResolver.resolveArgument(methodParameter, null, null, null))\n\t\t\t.withMessage(\"Unable to resolve the Client Registration Identifier. It must be provided via \"\n\t\t\t\t\t+ \"@RegisteredOAuth2AuthorizedClient(\\\"client1\\\") or \"\n\t\t\t\t\t+ \"@RegisteredOAuth2AuthorizedClient(registrationId = \\\"client1\\\").\");\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenRegistrationIdEmptyAndOAuth2AuthenticationThenResolves() throws Exception {\n\t\tOAuth2AuthenticationToken authentication = mock(OAuth2AuthenticationToken.class);\n\t\tgiven(authentication.getAuthorizedClientRegistrationId()).willReturn(\"client1\");\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"registrationIdEmpty\", OAuth2AuthorizedClient.class);\n\t\tassertThat(this.argumentResolver.resolveArgument(methodParameter, null,\n\t\t\t\tnew ServletWebRequest(this.request, this.response), null))\n\t\t\t.isSameAs(this.authorizedClient1);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAuthorizedClientFoundThenResolves() throws Exception {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThat(this.argumentResolver.resolveArgument(methodParameter, null,\n\t\t\t\tnew ServletWebRequest(this.request, this.response), null))\n\t\t\t.isSameAs(this.authorizedClient1);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(this.authentication));\n\t\tthis.argumentResolver.setSecurityContextHolderStrategy(strategy);\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThat(this.argumentResolver.resolveArgument(methodParameter, null,\n\t\t\t\tnew ServletWebRequest(this.request, this.response), null))\n\t\t\t.isSameAs(this.authorizedClient1);\n\t\tverify(strategy, atLeastOnce()).getContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenRegistrationIdInvalidThenThrowIllegalArgumentException() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"registrationIdInvalid\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.argumentResolver.resolveArgument(methodParameter, null,\n\t\t\t\t\tnew ServletWebRequest(this.request, this.response), null))\n\t\t\t.withMessage(\"Could not find ClientRegistration with id 'invalid'\");\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAuthorizedClientNotFoundForAuthorizationCodeClientThenThrowClientAuthorizationRequiredException() {\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(), any(HttpServletRequest.class)))\n\t\t\t.willReturn(null);\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class).isThrownBy(() -> this.argumentResolver\n\t\t\t.resolveArgument(methodParameter, null, new ServletWebRequest(this.request, this.response), null));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void resolveArgumentWhenAuthorizedClientNotFoundForClientCredentialsClientThenResolvesFromTokenResponseClient()\n\t\t\tthrows Exception {\n\t\tOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = mock(\n\t\t\t\tOAuth2AccessTokenResponseClient.class);\n\t\tClientCredentialsOAuth2AuthorizedClientProvider clientCredentialsAuthorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider();\n\t\tclientCredentialsAuthorizedClientProvider.setAccessTokenResponseClient(clientCredentialsTokenResponseClient);\n\t\tDefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(clientCredentialsAuthorizedClientProvider);\n\t\tthis.argumentResolver = new OAuth2AuthorizedClientArgumentResolver(authorizedClientManager);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"access-token-1234\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(3600)\n\t\t\t.build();\n\t\tgiven(clientCredentialsTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(), any(HttpServletRequest.class)))\n\t\t\t.willReturn(null);\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"clientCredentialsClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tOAuth2AuthorizedClient authorizedClient = (OAuth2AuthorizedClient) this.argumentResolver\n\t\t\t.resolveArgument(methodParameter, null, new ServletWebRequest(this.request, this.response), null);\n\t\tassertThat(authorizedClient).isNotNull();\n\t\tassertThat(authorizedClient.getClientRegistration()).isSameAs(this.registration2);\n\t\tassertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principalName);\n\t\tassertThat(authorizedClient.getAccessToken()).isSameAs(accessTokenResponse.getAccessToken());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(eq(authorizedClient), eq(this.authentication),\n\t\t\t\tany(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\tprivate MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {\n\t\tMethod method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);\n\t\treturn new MethodParameter(method, 0);\n\t}\n\n\tstatic class TestController {\n\n\t\tvoid paramTypeAuthorizedClient(\n\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"client1\") OAuth2AuthorizedClient authorizedClient) {\n\t\t}\n\n\t\tvoid paramTypeAuthorizedClientWithoutAnnotation(OAuth2AuthorizedClient authorizedClient) {\n\t\t}\n\n\t\tvoid paramTypeUnsupported(@RegisteredOAuth2AuthorizedClient(\"client1\") String param) {\n\t\t}\n\n\t\tvoid paramTypeUnsupportedWithoutAnnotation(String param) {\n\t\t}\n\n\t\tvoid registrationIdEmpty(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {\n\t\t}\n\n\t\tvoid registrationIdInvalid(\n\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"invalid\") OAuth2AuthorizedClient authorizedClient) {\n\t\t}\n\n\t\tvoid clientCredentialsClient(\n\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"client2\") OAuth2AuthorizedClient authorizedClient) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/MockExchangeFunction.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\n\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 5.1\n */\npublic class MockExchangeFunction implements ExchangeFunction {\n\n\tprivate final AtomicReference<Authentication> authenticationCaptor = new AtomicReference<>();\n\n\tprivate List<ClientRequest> requests = new ArrayList<>();\n\n\tprivate ClientResponse response = mock(ClientResponse.class);\n\n\tpublic Authentication getCapturedAuthentication() {\n\t\treturn this.authenticationCaptor.get();\n\t}\n\n\tpublic ClientRequest getRequest() {\n\t\treturn this.requests.get(this.requests.size() - 1);\n\t}\n\n\tpublic List<ClientRequest> getRequests() {\n\t\treturn this.requests;\n\t}\n\n\tpublic ClientResponse getResponse() {\n\t\treturn this.response;\n\t}\n\n\t@Override\n\tpublic Mono<ClientResponse> exchange(ClientRequest request) {\n\t\treturn Mono.defer(() -> {\n\t\t\tthis.requests.add(request);\n\t\t\treturn captureAuthentication().then(Mono.just(this.response));\n\t\t});\n\t}\n\n\tprivate Mono<Authentication> captureAuthentication() {\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t.doOnNext(this.authenticationCaptor::set);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.gen.RSAKeyGenerator;\nimport com.nimbusds.jose.jwk.source.ImmutableJWKSet;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AbstractOAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoder;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.InstanceOfAssertFactories.type;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Phil Clay\n * @author Evgeniy Cheban\n */\npublic class ServerOAuth2AuthorizedClientExchangeFilterFunctionITests {\n\n\tprivate final AuthenticationCapturingExchangeFilterFunction authenticationCapturingFilter = new AuthenticationCapturingExchangeFilterFunction();\n\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate ServerOAuth2AuthorizedClientExchangeFilterFunction authorizedClientFilter;\n\n\tprivate MockWebServer server;\n\n\tprivate String serverUrl;\n\n\tprivate WebClient webClient;\n\n\tprivate Authentication authentication;\n\n\tprivate MockServerWebExchange exchange;\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tthis.clientRegistrationRepository = mock(ReactiveClientRegistrationRepository.class);\n\t\tfinal ServerOAuth2AuthorizedClientRepository delegate = new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(\n\t\t\t\tnew InMemoryReactiveOAuth2AuthorizedClientService(this.clientRegistrationRepository));\n\t\tthis.authorizedClientRepository = spy(new ServerOAuth2AuthorizedClientRepository() {\n\t\t\t@Override\n\t\t\tpublic <T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId,\n\t\t\t\t\tAuthentication principal, ServerWebExchange exchange) {\n\t\t\t\treturn delegate.loadAuthorizedClient(clientRegistrationId, principal, exchange);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\t\t\tServerWebExchange exchange) {\n\t\t\t\treturn delegate.saveAuthorizedClient(authorizedClient, principal, exchange);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<Void> removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\t\t\tServerWebExchange exchange) {\n\t\t\t\treturn delegate.removeAuthorizedClient(clientRegistrationId, principal, exchange);\n\t\t\t}\n\t\t});\n\t\tthis.authorizedClientFilter = new ServerOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.serverUrl = this.server.url(\"/\").toString();\n\t\t// @formatter:off\n\t\tthis.webClient = WebClient.builder()\n\t\t\t\t.filter(this.authorizedClientFilter)\n\t\t\t\t.filter(this.authenticationCapturingFilter)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authentication = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tthis.exchange = MockServerWebExchange.builder(MockServerHttpRequest.get(\"/\").build()).build();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void requestWhenNotAuthorizedThenAuthorizeAndSendRequest() {\n\t\t// @formatter:off\n\t\tString accessTokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": \\\"3600\\\",\\n\"\n\t\t\t+ \"   \\\"scope\\\": \\\"read write\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\tString clientResponse = \"{\\n\"\n\t\t\t+ \"   \\\"attribute1\\\": \\\"value1\\\",\\n\"\n\t\t\t+ \"   \\\"attribute2\\\": \\\"value2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(clientRegistration));\n\t\t// @formatter:off\n\t\tthis.webClient.get()\n\t\t\t\t.uri(this.serverUrl)\n\t\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t\t\t.clientRegistrationId(clientRegistration.getRegistrationId()))\n\t\t\t\t.retrieve()\n\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t.contextWrite(Context.of(ServerWebExchange.class, this.exchange))\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(2);\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), eq(this.exchange));\n\t\tassertThat(authorizedClientCaptor.getValue().getClientRegistration()).isSameAs(clientRegistration);\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizedButExpiredThenRefreshAndSendRequest() {\n\t\t// @formatter:off\n\t\tString accessTokenResponse = \"{\\n\"\n\t\t\t+ \"\t\\\"access_token\\\": \\\"refreshed-access-token\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": \\\"3600\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\tString clientResponse = \"{\\n\"\n\t\t\t+ \"\t\\\"attribute1\\\": \\\"value1\\\",\\n\"\n\t\t\t+ \"\t\\\"attribute2\\\": \\\"value2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(clientRegistration));\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"expired-access-token\", issuedAt, expiresAt, new HashSet<>(Arrays.asList(\"read\", \"write\")));\n\t\tOAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\tthis.authentication.getName(), accessToken, refreshToken);\n\t\tdoReturn(Mono.just(authorizedClient)).when(this.authorizedClientRepository)\n\t\t\t.loadAuthorizedClient(eq(clientRegistration.getRegistrationId()), eq(this.authentication),\n\t\t\t\t\teq(this.exchange));\n\t\tthis.webClient.get()\n\t\t\t.uri(this.serverUrl)\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.clientRegistrationId(clientRegistration.getRegistrationId()))\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t.contextWrite(Context.of(ServerWebExchange.class, this.exchange))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication))\n\t\t\t.block();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(2);\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), eq(this.exchange));\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = authorizedClientCaptor.getValue();\n\t\tassertThat(refreshedAuthorizedClient.getClientRegistration()).isSameAs(clientRegistration);\n\t\tassertThat(refreshedAuthorizedClient.getAccessToken().getTokenValue()).isEqualTo(\"refreshed-access-token\");\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizedButExpiredThenRefreshSecurityContext() throws Exception {\n\t\t// Current OIDC user in the SecurityContext.\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.issuerUri(this.serverUrl)\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.jwkSetUri(this.serverUrl + \"oauth2/jwk\")\n\t\t\t.userInfoUri(this.serverUrl + \"user\")\n\t\t\t.userNameAttributeName(\"username\")\n\t\t\t.build();\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tOidcIdToken existingIdToken = OidcIdToken.withTokenValue(\"id-token-1234\")\n\t\t\t.issuer(this.serverUrl)\n\t\t\t.subject(\"subject-1234\")\n\t\t\t.audience(List.of(clientRegistration.getClientId()))\n\t\t\t.issuedAt(issuedAt)\n\t\t\t.expiresAt(expiresAt)\n\t\t\t.build();\n\t\tOidcUser existingOidcUser = new DefaultOidcUser(Collections.emptyList(), existingIdToken);\n\t\tthis.authentication = new OAuth2AuthenticationToken(existingOidcUser, Collections.emptyList(),\n\t\t\t\tclientRegistration.getRegistrationId());\n\t\t// Generate new OIDC ID token with refreshed user information.\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).type(\"JWT\").build();\n\t\tJwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder()\n\t\t\t.subject(\"subject-1234\")\n\t\t\t.audience(List.of(clientRegistration.getClientId()))\n\t\t\t.issuer(this.serverUrl)\n\t\t\t.issuedAt(Instant.now())\n\t\t\t.expiresAt(Instant.now().plusSeconds(3600))\n\t\t\t.build();\n\t\tRSAKey key = new RSAKeyGenerator(2048).generate();\n\t\tJWKSet jwkSet = new JWKSet(key);\n\t\tJwtEncoder jwtEncoder = new NimbusJwtEncoder(new ImmutableJWKSet<>(jwkSet));\n\t\tString idToken = jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)).getTokenValue();\n\t\t// @formatter:off\n\t\tString accessTokenResponse = \"\"\"\n\t\t\t{\n\t\t\t\t\"access_token\": \"refreshed-access-token\",\n\t\t\t\t\"id_token\": \"%s\",\n\t\t\t\t\"token_type\": \"bearer\",\n\t\t\t\t\"expires_in\": \"3600\"\n\t\t\t}\n\t\t\t\"\"\".formatted(idToken);\n\t\tString userInfoResponse = \"\"\"\n\t\t\t{\n\t\t\t\t\"sub\": \"subject-1234\",\n\t\t\t\t\"username\": \"refreshed-username\"\n\t\t\t}\n\t\t\t\"\"\";\n\t\tString clientResponse = \"\"\"\n\t\t\t{\n\t\t\t\t\"attribute1\": \"value1\",\n\t\t\t\t\"attribute2\": \"value2\"\n\t\t\t}\n\t\t\t\"\"\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(jwkSet.toString()));\n\t\tthis.server.enqueue(jsonResponse(userInfoResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(clientRegistration));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"expired-access-token\", issuedAt, expiresAt,\n\t\t\t\tnew HashSet<>(Arrays.asList(\"read\", \"write\", OidcScopes.OPENID)));\n\t\tOAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\tthis.authentication.getName(), accessToken, refreshToken);\n\t\tdoReturn(Mono.just(authorizedClient)).when(this.authorizedClientRepository)\n\t\t\t.loadAuthorizedClient(eq(clientRegistration.getRegistrationId()), eq(this.authentication),\n\t\t\t\t\teq(this.exchange));\n\t\tthis.webClient.get()\n\t\t\t.uri(this.serverUrl)\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.clientRegistrationId(clientRegistration.getRegistrationId()))\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t.contextWrite(Context.of(ServerWebExchange.class, this.exchange))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication))\n\t\t\t.block();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(4);\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), eq(this.exchange));\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = authorizedClientCaptor.getValue();\n\t\tassertThat(refreshedAuthorizedClient.getClientRegistration()).isSameAs(clientRegistration);\n\t\tassertThat(refreshedAuthorizedClient.getAccessToken().getTokenValue()).isEqualTo(\"refreshed-access-token\");\n\t\tWebSessionServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\t// Capture and verify that the refreshed Authentication object was propagated to\n\t\t// the next ExchangeFilterFunction's context.\n\t\tAuthentication capturedAuthentication = this.authenticationCapturingFilter.getCapturedAuthentication();\n\t\tassertThat(capturedAuthentication).isNotNull();\n\t\tAuthentication refreshedAuthentication = securityContextRepository.load(this.exchange)\n\t\t\t.mapNotNull(SecurityContext::getAuthentication)\n\t\t\t.block();\n\t\tassertThat(refreshedAuthentication).isNotNull();\n\t\tassertThat(refreshedAuthentication).isSameAs(capturedAuthentication);\n\t\tassertThat(refreshedAuthentication).asInstanceOf(type(OAuth2AuthenticationToken.class))\n\t\t\t.extracting(OAuth2AuthenticationToken::getPrincipal)\n\t\t\t.asInstanceOf(type(OidcUser.class))\n\t\t\t.satisfies((oidcUser) -> {\n\t\t\t\t// Verify that the OidcUser's attributes match the id_token's claims.\n\t\t\t\tassertThat(oidcUser.getIdToken()).extracting(AbstractOAuth2Token::getTokenValue).isEqualTo(idToken);\n\t\t\t\tassertThat(oidcUser.getSubject()).isEqualTo(\"subject-1234\");\n\t\t\t\tassertThat(oidcUser.getName()).isEqualTo(\"refreshed-username\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void requestMultipleWhenNoneAuthorizedThenAuthorizeAndSendRequest() {\n\t\t// @formatter:off\n\t\tString accessTokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": \\\"3600\\\",\\n\"\n\t\t\t+ \"   \\\"scope\\\": \\\"read write\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\tString clientResponse = \"{\\n\"\n\t\t\t+ \"   \\\"attribute1\\\": \\\"value1\\\",\\n\"\n\t\t\t+ \"   \\\"attribute2\\\": \\\"value2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\t// Client 1\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration1 = TestClientRegistrations.clientCredentials()\n\t\t\t.registrationId(\"client-1\")\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration1.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(clientRegistration1));\n\t\t// Client 2\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration2 = TestClientRegistrations.clientCredentials()\n\t\t\t.registrationId(\"client-2\")\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration2.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(clientRegistration2));\n\t\t// @formatter:off\n\t\tthis.webClient.get()\n\t\t\t\t.uri(this.serverUrl)\n\t\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t\t\t.clientRegistrationId(clientRegistration1.getRegistrationId()))\n\t\t\t\t.retrieve()\n\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t.flatMap((response) -> this.webClient.get()\n\t\t\t\t\t\t.uri(this.serverUrl)\n\t\t\t\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t\t\t\t\t.clientRegistrationId(clientRegistration2.getRegistrationId()))\n\t\t\t\t\t\t.retrieve()\n\t\t\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t)\n\t\t\t\t.contextWrite(Context.of(ServerWebExchange.class, this.exchange))\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(4);\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository, times(2)).saveAuthorizedClient(authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), eq(this.exchange));\n\t\tassertThat(authorizedClientCaptor.getAllValues().get(0).getClientRegistration()).isSameAs(clientRegistration1);\n\t\tassertThat(authorizedClientCaptor.getAllValues().get(1).getClientRegistration()).isSameAs(clientRegistration2);\n\t}\n\n\t/**\n\t * When a non-expired {@link OAuth2AuthorizedClient} exists but the resource server\n\t * returns 401, then remove the {@link OAuth2AuthorizedClient} from the repository.\n\t */\n\t@Test\n\tpublic void requestWhenUnauthorizedThenReAuthorize() {\n\t\t// @formatter:off\n\t\tString accessTokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": \\\"3600\\\",\\n\"\n\t\t\t+ \"   \\\"scope\\\": \\\"read write\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\tString clientResponse = \"{\\n\"\n\t\t\t+ \"   \\\"attribute1\\\": \\\"value1\\\",\\n\"\n\t\t\t+ \"   \\\"attribute2\\\": \\\"value2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(HttpStatus.UNAUTHORIZED.value()));\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(clientRegistration));\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes(\"read\", \"write\");\n\t\tOAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\tthis.authentication.getName(), accessToken, refreshToken);\n\t\tdoReturn(Mono.just(authorizedClient)).doReturn(Mono.empty())\n\t\t\t.when(this.authorizedClientRepository)\n\t\t\t.loadAuthorizedClient(eq(clientRegistration.getRegistrationId()), eq(this.authentication),\n\t\t\t\t\teq(this.exchange));\n\t\t// @formatter:off\n\t\tMono<String> requestMono = this.webClient.get()\n\t\t\t\t.uri(this.serverUrl)\n\t\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t\t\t.clientRegistrationId(clientRegistration.getRegistrationId()))\n\t\t\t\t.retrieve()\n\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t.contextWrite(Context.of(ServerWebExchange.class, this.exchange))\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\t// @formatter:on\n\t\t// first try should fail, and remove the cached authorized client\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(WebClientResponseException.class)\n\t\t\t\t.isThrownBy(requestMono::block)\n\t\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED));\n\t\t// @formatter:on\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(1);\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), any(), any());\n\t\tverify(this.authorizedClientRepository).removeAuthorizedClient(eq(clientRegistration.getRegistrationId()),\n\t\t\t\teq(this.authentication), eq(this.exchange));\n\t\t// second try should retrieve the authorized client and succeed\n\t\trequestMono.block();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(3);\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), eq(this.exchange));\n\t\tassertThat(authorizedClientCaptor.getValue().getClientRegistration()).isSameAs(clientRegistration);\n\t}\n\n\tprivate MockResponse jsonResponse(String json) {\n\t\t// @formatter:off\n\t\treturn new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setBody(json);\n\t\t// @formatter:on\n\t}\n\n\tprivate static final class AuthenticationCapturingExchangeFilterFunction implements ExchangeFilterFunction {\n\n\t\tprivate final AtomicReference<Authentication> authenticationCaptor = new AtomicReference<>();\n\n\t\t@Override\n\t\tpublic Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {\n\t\t\treturn ReactiveSecurityContextHolder.getContext().flatMap((ctx) -> {\n\t\t\t\tthis.authenticationCaptor.set(ctx.getAuthentication());\n\t\t\t\treturn next.exchange(request);\n\t\t\t});\n\t\t}\n\n\t\tprivate Authentication getCapturedAuthentication() {\n\t\t\treturn this.authenticationCaptor.get();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client;\n\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.publisher.PublisherProbe;\nimport reactor.util.context.Context;\n\nimport org.springframework.core.codec.ByteBufferEncoder;\nimport org.springframework.core.codec.CharSequenceEncoder;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.codec.EncoderHttpMessageWriter;\nimport org.springframework.http.codec.FormHttpMessageWriter;\nimport org.springframework.http.codec.HttpMessageWriter;\nimport org.springframework.http.codec.ResourceHttpMessageWriter;\nimport org.springframework.http.codec.ServerSentEventHttpMessageWriter;\nimport org.springframework.http.codec.json.Jackson2JsonEncoder;\nimport org.springframework.http.codec.multipart.MultipartHttpMessageWriter;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.mock.http.client.reactive.MockClientHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.client.AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.ClientCredentialsReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.JwtBearerReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizationSuccessHandler;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.TestOAuth2Users;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.web.reactive.function.BodyInserter;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @author Evgeniy Cheban\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServerOAuth2AuthorizedClientExchangeFilterFunctionTests {\n\n\t@Mock\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Mock\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient;\n\n\t@Mock\n\tprivate ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient;\n\n\t@Mock\n\tprivate ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient;\n\n\t@Mock\n\tprivate ReactiveOAuth2AuthorizationSuccessHandler authorizationSuccessHandler;\n\n\t@Mock\n\tprivate ReactiveOAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t@Captor\n\tprivate ArgumentCaptor<OAuth2AuthorizationException> authorizationExceptionCaptor;\n\n\t@Captor\n\tprivate ArgumentCaptor<Authentication> authenticationCaptor;\n\n\t@Captor\n\tprivate ArgumentCaptor<Map<String, Object>> attributesCaptor;\n\n\tprivate ServerWebExchange serverWebExchange = MockServerWebExchange.builder(MockServerHttpRequest.get(\"/\")).build();\n\n\t@Captor\n\tprivate ArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor;\n\n\tprivate ServerOAuth2AuthorizedClientExchangeFilterFunction function;\n\n\tprivate MockExchangeFunction exchange = new MockExchangeFunction();\n\n\tprivate ClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\n\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token-0\",\n\t\t\tInstant.now(), Instant.now().plus(Duration.ofDays(1)));\n\n\tprivate DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tJwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerReactiveOAuth2AuthorizedClientProvider();\n\t\tjwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(this.jwtBearerTokenResponseClient);\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder\n\t\t\t\t.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken(\n\t\t\t\t\t\t(configurer) -> configurer\n\t\t\t\t\t\t\t\t.authorizationSuccessHandler(this.authorizationSuccessHandler)\n\t\t\t\t\t\t\t\t.accessTokenResponseClient(this.refreshTokenTokenResponseClient))\n\t\t\t\t.clientCredentials(\n\t\t\t\t\t\t(configurer) -> configurer.accessTokenResponseClient(this.clientCredentialsTokenResponseClient))\n\t\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tthis.authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\t\tthis.function = new ServerOAuth2AuthorizedClientExchangeFilterFunction(this.authorizedClientManager);\n\t}\n\n\tprivate void setupMocks() {\n\t\tsetupMockSaveAuthorizedClient();\n\t\tsetupMockHeaders();\n\t}\n\n\tprivate void setupMockSaveAuthorizedClient() {\n\t\tgiven(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())).willReturn(Mono.empty());\n\t}\n\n\tprivate void setupMockHeaders() {\n\t\tgiven(this.exchange.getResponse().headers()).willReturn(mock(ClientResponse.Headers.class));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientManagerIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerOAuth2AuthorizedClientExchangeFilterFunction(null));\n\t}\n\n\t@Test\n\tpublic void setServerSecurityContextRepositoryWhenHandlerIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerOAuth2AuthorizedClientExchangeFilterFunction(this.authorizedClientManager)\n\t\t\t\t.setServerSecurityContextRepository(null));\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizedClientNullThenAuthorizationHeaderNull() {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION)).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizedClientThenAuthorizationHeader() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.build();\n\t\t// @formatter:off\n\t\tthis.function.filter(request, this.exchange).contextWrite(serverWebExchange())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void filterWhenExistingAuthorizationThenSingleAuthorizationHeader() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Existing\")\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block();\n\t\tHttpHeaders headers = this.exchange.getRequest().headers();\n\t\tassertThat(headers.get(HttpHeaders.AUTHORIZATION)).containsOnly(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void filterWhenClientCredentialsTokenExpiredThenGetNewToken() {\n\t\tsetupMocks();\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withToken(\"new-token\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(360)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.clientCredentialsTokenResponseClient.getTokenResponse(any()))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\t\tClientRegistration registration = TestClientRegistrations.clientCredentials().build();\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(),\n\t\t\t\tthis.accessToken.getTokenValue(), issuedAt, accessTokenExpiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(registration, \"principalName\", accessToken,\n\t\t\t\tnull);\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"test\", \"this\");\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tverify(this.clientCredentialsTokenResponseClient).getTokenResponse(any());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), eq(authentication), any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request1 = requests.get(0);\n\t\tassertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer new-token\");\n\t\tassertThat(request1.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request1.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request1)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenClientCredentialsTokenNotExpiredThenUseCurrentToken() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"test\", \"this\");\n\t\tClientRegistration registration = TestClientRegistrations.clientCredentials().build();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(registration, \"principalName\",\n\t\t\t\tthis.accessToken, null);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tverify(this.clientCredentialsTokenResponseClient, never()).getTokenResponse(any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request1 = requests.get(0);\n\t\tassertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request1.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request1.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request1)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenRefreshRequiredThenRefresh() {\n\t\tsetupMocks();\n\t\tOAuth2AccessTokenResponse response = OAuth2AccessTokenResponse.withToken(\"token-1\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(3600)\n\t\t\t.refreshToken(\"refresh-1\")\n\t\t\t.build();\n\t\tgiven(this.refreshTokenTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(response));\n\t\tgiven(this.authorizationSuccessHandler.onAuthorizationSuccess(any(), any(), any())).willReturn(Mono.empty());\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tthis.accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(), this.accessToken.getTokenValue(),\n\t\t\t\tissuedAt, accessTokenExpiresAt);\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", issuedAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"test\", \"this\");\n\t\t// @formatter:off\n\t\tDefaultOAuth2User refreshedUser = TestOAuth2Users.create();\n\t\tOAuth2AuthenticationToken refreshedAuthentication = new OAuth2AuthenticationToken(refreshedUser, refreshedUser.getAuthorities(), this.registration.getRegistrationId());\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(refreshedAuthentication);\n\t\tServerSecurityContextRepository securityContextRepository = mock(ServerSecurityContextRepository.class);\n\t\tgiven(securityContextRepository.load(this.serverWebExchange)).willReturn(Mono.just(securityContext));\n\t\tthis.function.setServerSecurityContextRepository(securityContextRepository);\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t.block();\n\t\tAuthentication currentAuthentication = this.exchange.getCapturedAuthentication();\n\t\tassertThat(currentAuthentication).isSameAs(refreshedAuthentication);\n\t\t// @formatter:on\n\t\tverify(this.refreshTokenTokenResponseClient).getTokenResponse(any());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(this.authorizedClientCaptor.capture(),\n\t\t\t\teq(authentication), any());\n\t\tverify(securityContextRepository).load(this.serverWebExchange);\n\t\tOAuth2AuthorizedClient newAuthorizedClient = this.authorizedClientCaptor.getValue();\n\t\tassertThat(newAuthorizedClient.getAccessToken()).isEqualTo(response.getAccessToken());\n\t\tassertThat(newAuthorizedClient.getRefreshToken()).isEqualTo(response.getRefreshToken());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-1\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenRefreshRequiredAndEmptyReactiveSecurityContextThenSaved() {\n\t\tsetupMocks();\n\t\tOAuth2AccessTokenResponse response = OAuth2AccessTokenResponse.withToken(\"token-1\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(3600)\n\t\t\t.refreshToken(\"refresh-1\")\n\t\t\t.build();\n\t\tgiven(this.refreshTokenTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(response));\n\t\tgiven(this.authorizationSuccessHandler.onAuthorizationSuccess(any(), any(), any())).willReturn(Mono.empty());\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tthis.accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(), this.accessToken.getTokenValue(),\n\t\t\t\tissuedAt, accessTokenExpiresAt);\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", issuedAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tverify(this.refreshTokenTokenResponseClient).getTokenResponse(any());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), any(), any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-1\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenJwtBearerClientNotAuthorizedThenExchangeToken() {\n\t\tsetupMocks();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"exchanged-token\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(360)\n\t\t\t.build();\n\t\tgiven(this.jwtBearerTokenResponseClient.getTokenResponse(any())).willReturn(Mono.just(accessTokenResponse));\n\t\t// @formatter:off\n\t\tClientRegistration registration = ClientRegistration.withRegistrationId(\"jwt-bearer\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.tokenUri(\"https://example.com/oauth/token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(registration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(registration));\n\t\tJwt jwtAssertion = TestJwts.jwt().build();\n\t\tAuthentication jwtAuthentication = new TestingAuthenticationToken(jwtAssertion, jwtAssertion);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(eq(registration.getRegistrationId()),\n\t\t\t\teq(jwtAuthentication), any()))\n\t\t\t.willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId(registration.getRegistrationId()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(jwtAuthentication))\n\t\t\t.contextWrite(serverWebExchange())\n\t\t\t.block();\n\t\tverify(this.jwtBearerTokenResponseClient).getTokenResponse(any());\n\t\tverify(this.authorizedClientRepository).loadAuthorizedClient(eq(registration.getRegistrationId()),\n\t\t\t\teq(jwtAuthentication), any());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), eq(jwtAuthentication), any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request1 = requests.get(0);\n\t\tassertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer exchanged-token\");\n\t\tassertThat(request1.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request1.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request1)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenRefreshTokenNullThenShouldRefreshFalse() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenNotExpiredThenShouldRefreshFalse() {\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenUnauthorizedThenInvokeFailureHandler() {\n\t\tsetupMockHeaders();\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tPublisherProbe<Void> publisherProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizationFailureHandler.onAuthorizationFailure(any(), any(), any()))\n\t\t\t.willReturn(publisherProbe.mono());\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.exchange.getResponse().statusCode()).willReturn(HttpStatus.UNAUTHORIZED);\n\t\tthis.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block();\n\t\tassertThat(publisherProbe.wasSubscribed()).isTrue();\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t.isInstanceOfSatisfying(ClientAuthorizationException.class, (ex) -> {\n\t\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token\");\n\t\t\t\tassertThat(ex).hasNoCause();\n\t\t\t\tassertThat(ex).hasMessageContaining(\"[invalid_token]\");\n\t\t\t});\n\t\tassertThat(this.authenticationCaptor.getValue()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(this.attributesCaptor.getValue())\n\t\t\t.containsExactly(entry(ServerWebExchange.class.getName(), this.serverWebExchange));\n\t}\n\n\t@Test\n\tpublic void filterWhenUnauthorizedWithWebClientExceptionThenInvokeFailureHandler() {\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tPublisherProbe<Void> publisherProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizationFailureHandler.onAuthorizationFailure(any(), any(), any()))\n\t\t\t.willReturn(publisherProbe.mono());\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tWebClientResponseException exception = WebClientResponseException.create(HttpStatus.UNAUTHORIZED.value(),\n\t\t\t\tHttpStatus.UNAUTHORIZED.getReasonPhrase(), HttpHeaders.EMPTY, new byte[0], StandardCharsets.UTF_8);\n\t\tExchangeFunction throwingExchangeFunction = (r) -> Mono.error(exception);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(WebClientResponseException.class)\n\t\t\t\t.isThrownBy(() -> this.function\n\t\t\t\t\t.filter(request, throwingExchangeFunction)\n\t\t\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.isEqualTo(exception);\n\t\t// @formatter:on\n\t\tassertThat(publisherProbe.wasSubscribed()).isTrue();\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\t// @formatter:off\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t\t.isInstanceOfSatisfying(ClientAuthorizationException.class, (ex) -> {\n\t\t\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(\"invalid_token\");\n\t\t\t\t\tassertThat(ex).hasCause(exception);\n\t\t\t\t\tassertThat(ex).hasMessageContaining(\"[invalid_token]\");\n\t\t\t\t});\n\t\t// @formatter:on\n\t\tassertThat(this.authenticationCaptor.getValue()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(this.attributesCaptor.getValue())\n\t\t\t.containsExactly(entry(ServerWebExchange.class.getName(), this.serverWebExchange));\n\t}\n\n\t@Test\n\tpublic void filterWhenForbiddenThenInvokeFailureHandler() {\n\t\tsetupMockHeaders();\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tPublisherProbe<Void> publisherProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizationFailureHandler.onAuthorizationFailure(any(), any(), any()))\n\t\t\t.willReturn(publisherProbe.mono());\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.exchange.getResponse().statusCode()).willReturn(HttpStatus.FORBIDDEN);\n\t\tthis.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block();\n\t\tassertThat(publisherProbe.wasSubscribed()).isTrue();\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t.isInstanceOfSatisfying(ClientAuthorizationException.class, (ex) -> {\n\t\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(\"insufficient_scope\");\n\t\t\t\tassertThat(ex).hasNoCause();\n\t\t\t\tassertThat(ex).hasMessageContaining(\"[insufficient_scope]\");\n\t\t\t});\n\t\tassertThat(this.authenticationCaptor.getValue()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(this.attributesCaptor.getValue())\n\t\t\t.containsExactly(entry(ServerWebExchange.class.getName(), this.serverWebExchange));\n\t}\n\n\t@Test\n\tpublic void filterWhenForbiddenWithWebClientExceptionThenInvokeFailureHandler() {\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tPublisherProbe<Void> publisherProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizationFailureHandler.onAuthorizationFailure(any(), any(), any()))\n\t\t\t.willReturn(publisherProbe.mono());\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.build();\n\t\tWebClientResponseException exception = WebClientResponseException.create(HttpStatus.FORBIDDEN.value(),\n\t\t\t\tHttpStatus.FORBIDDEN.getReasonPhrase(), HttpHeaders.EMPTY, new byte[0], StandardCharsets.UTF_8);\n\t\tExchangeFunction throwingExchangeFunction = (r) -> Mono.error(exception);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(WebClientResponseException.class)\n\t\t\t\t.isThrownBy(() -> this.function\n\t\t\t\t\t.filter(request, throwingExchangeFunction)\n\t\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.isEqualTo(exception);\n\t\t// @formatter:on\n\t\tassertThat(publisherProbe.wasSubscribed()).isTrue();\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t.isInstanceOfSatisfying(ClientAuthorizationException.class, (ex) -> {\n\t\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(\"insufficient_scope\");\n\t\t\t\tassertThat(ex).hasCause(exception);\n\t\t\t\tassertThat(ex).hasMessageContaining(\"[insufficient_scope]\");\n\t\t\t});\n\t\tassertThat(this.authenticationCaptor.getValue()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(this.attributesCaptor.getValue())\n\t\t\t.containsExactly(entry(ServerWebExchange.class.getName(), this.serverWebExchange));\n\t}\n\n\t@Test\n\tpublic void filterWhenWWWAuthenticateHeaderIncludesErrorThenInvokeFailureHandler() {\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tPublisherProbe<Void> publisherProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizationFailureHandler.onAuthorizationFailure(any(), any(), any()))\n\t\t\t.willReturn(publisherProbe.mono());\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.build();\n\t\tString wwwAuthenticateHeader = \"Bearer error=\\\"insufficient_scope\\\", \"\n\t\t\t\t+ \"error_description=\\\"The request requires higher privileges than provided by the access token.\\\", \"\n\t\t\t\t+ \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\";\n\t\tClientResponse.Headers headers = mock(ClientResponse.Headers.class);\n\t\tgiven(headers.header(eq(HttpHeaders.WWW_AUTHENTICATE)))\n\t\t\t.willReturn(Collections.singletonList(wwwAuthenticateHeader));\n\t\tgiven(this.exchange.getResponse().headers()).willReturn(headers);\n\t\tthis.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block();\n\t\tassertThat(publisherProbe.wasSubscribed()).isTrue();\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t.isInstanceOfSatisfying(ClientAuthorizationException.class, (ex) -> {\n\t\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t\t\tassertThat(ex.getError().getDescription())\n\t\t\t\t\t.isEqualTo(\"The request requires higher privileges than provided by the access token.\");\n\t\t\t\tassertThat(ex.getError().getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t\t\tassertThat(ex).hasNoCause();\n\t\t\t\tassertThat(ex).hasMessageContaining(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t\t});\n\t\tassertThat(this.authenticationCaptor.getValue()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(this.attributesCaptor.getValue())\n\t\t\t.containsExactly(entry(ServerWebExchange.class.getName(), this.serverWebExchange));\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizationExceptionThenInvokeFailureHandler() {\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tPublisherProbe<Void> publisherProbe = PublisherProbe.empty();\n\t\tgiven(this.authorizationFailureHandler.onAuthorizationFailure(any(), any(), any()))\n\t\t\t.willReturn(publisherProbe.mono());\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.build();\n\t\tOAuth2AuthorizationException exception = new OAuth2AuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, null, null));\n\t\tExchangeFunction throwingExchangeFunction = (r) -> Mono.error(exception);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(\n\t\t\t\t() -> this.function.filter(request, throwingExchangeFunction).contextWrite(serverWebExchange()).block())\n\t\t\t.isEqualTo(exception);\n\t\tassertThat(publisherProbe.wasSubscribed()).isTrue();\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue()).isSameAs(exception);\n\t\tassertThat(this.authenticationCaptor.getValue()).isInstanceOf(AnonymousAuthenticationToken.class);\n\t\tassertThat(this.attributesCaptor.getValue())\n\t\t\t.containsExactly(entry(ServerWebExchange.class.getName(), this.serverWebExchange));\n\t}\n\n\t@Test\n\tpublic void filterWhenOtherHttpStatusShouldNotInvokeFailureHandler() {\n\t\tsetupMockHeaders();\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.build();\n\t\tgiven(this.exchange.getResponse().statusCode()).willReturn(HttpStatus.BAD_REQUEST);\n\t\tthis.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block();\n\t\tverify(this.authorizationFailureHandler, never()).onAuthorizationFailure(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void filterWhenClientRegistrationIdThenAuthorizedClientResolved() {\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any()))\n\t\t\t.willReturn(Mono.just(authorizedClient));\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.clientRegistrationId(this.registration.getRegistrationId()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block();\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultClientRegistrationIdThenAuthorizedClientResolved() {\n\t\tthis.function.setDefaultClientRegistrationId(this.registration.getRegistrationId());\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any()))\n\t\t\t.willReturn(Mono.just(authorizedClient));\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tthis.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block();\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenClientRegistrationIdFromAuthenticationThenAuthorizedClientResolved() {\n\t\tthis.function.setDefaultOAuth2AuthorizedClient(true);\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any()))\n\t\t\t.willReturn(Mono.just(authorizedClient));\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tOAuth2User user = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"),\n\t\t\t\tCollections.singletonMap(\"user\", \"rob\"), \"user\");\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(user, user.getAuthorities(),\n\t\t\t\t\"client-id\");\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t.contextWrite(serverWebExchange())\n\t\t\t.block();\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultOAuth2AuthorizedClientFalseThenEmpty() {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tOAuth2User user = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"),\n\t\t\t\tCollections.singletonMap(\"user\", \"rob\"), \"user\");\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(user, user.getAuthorities(),\n\t\t\t\t\"client-id\");\n\t\t// @formatter:off\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tverifyNoMoreInteractions(this.clientRegistrationRepository, this.authorizedClientRepository);\n\t}\n\n\t@Test\n\tpublic void filterWhenClientRegistrationIdAndServerWebExchangeFromContextThenServerWebExchangeFromContext() {\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any()))\n\t\t\t.willReturn(Mono.just(authorizedClient));\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId(this.registration.getRegistrationId()))\n\t\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t\t.contextWrite(serverWebExchange())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tverify(this.authorizedClientRepository).loadAuthorizedClient(eq(this.registration.getRegistrationId()), any(),\n\t\t\t\teq(this.serverWebExchange));\n\t}\n\n\t// gh-7544\n\t@Test\n\tpublic void filterWhenClientCredentialsClientNotAuthorizedAndOutsideRequestContextThenGetNewToken() {\n\t\tsetupMockHeaders();\n\t\tReactiveOAuth2AuthorizedClientService authorizedClientServiceDelegate = new InMemoryReactiveOAuth2AuthorizedClientService(\n\t\t\t\tthis.clientRegistrationRepository);\n\t\tReactiveOAuth2AuthorizedClientService authorizedClientService = new ReactiveOAuth2AuthorizedClientService() {\n\t\t\t@Override\n\t\t\tpublic <T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId,\n\t\t\t\t\tString principalName) {\n\t\t\t\treturn authorizedClientServiceDelegate.loadAuthorizedClient(clientRegistrationId, principalName);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {\n\t\t\t\treturn authorizedClientServiceDelegate.saveAuthorizedClient(authorizedClient, principal);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<Void> removeAuthorizedClient(String clientRegistrationId, String principalName) {\n\t\t\t\treturn authorizedClientServiceDelegate.removeAuthorizedClient(clientRegistrationId, principalName);\n\t\t\t}\n\t\t};\n\t\tauthorizedClientService = spy(authorizedClientService);\n\t\tAuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, authorizedClientService);\n\t\tClientCredentialsReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();\n\t\tauthorizedClientProvider.setAccessTokenResponseClient(this.clientCredentialsTokenResponseClient);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\t\tthis.function = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"new-token\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(360)\n\t\t\t.build();\n\t\tgiven(this.clientCredentialsTokenResponseClient.getTokenResponse(any()))\n\t\t\t.willReturn(Mono.just(accessTokenResponse));\n\t\tClientRegistration registration = TestClientRegistrations.clientCredentials().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(registration.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(registration));\n\t\t// @formatter:off\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t\t.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId(registration.getRegistrationId()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverify(authorizedClientService).loadAuthorizedClient(any(), any());\n\t\tverify(this.clientCredentialsTokenResponseClient).getTokenResponse(any());\n\t\tverify(authorizedClientService).saveAuthorizedClient(any(), any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request1 = requests.get(0);\n\t\tassertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer new-token\");\n\t\tassertThat(request1.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request1.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request1)).isEmpty();\n\t}\n\n\tprivate Context serverWebExchange() {\n\t\treturn Context.of(ServerWebExchange.class, this.serverWebExchange);\n\t}\n\n\tprivate static String getBody(ClientRequest request) {\n\t\tfinal List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();\n\t\tmessageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));\n\t\tmessageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));\n\t\tmessageWriters.add(new ResourceHttpMessageWriter());\n\t\tJackson2JsonEncoder jsonEncoder = new Jackson2JsonEncoder();\n\t\tmessageWriters.add(new EncoderHttpMessageWriter<>(jsonEncoder));\n\t\tmessageWriters.add(new ServerSentEventHttpMessageWriter(jsonEncoder));\n\t\tmessageWriters.add(new FormHttpMessageWriter());\n\t\tmessageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));\n\t\tmessageWriters.add(new MultipartHttpMessageWriter(messageWriters));\n\t\tBodyInserter.Context context = new BodyInserter.Context() {\n\t\t\t@Override\n\t\t\tpublic List<HttpMessageWriter<?>> messageWriters() {\n\t\t\t\treturn messageWriters;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<ServerHttpRequest> serverRequest() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Map<String, Object> hints() {\n\t\t\t\treturn new HashMap<>();\n\t\t\t}\n\t\t};\n\t\tMockClientHttpRequest body = new MockClientHttpRequest(HttpMethod.GET, \"/\");\n\t\trequest.body().insert(body, context).block();\n\t\treturn body.getBodyAsString().block();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionITests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.util.context.Context;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Joe Grandja\n */\npublic class ServletOAuth2AuthorizedClientExchangeFilterFunctionITests {\n\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate ServletOAuth2AuthorizedClientExchangeFilterFunction authorizedClientFilter;\n\n\tprivate MockWebServer server;\n\n\tprivate String serverUrl;\n\n\tprivate WebClient webClient;\n\n\tprivate Authentication authentication;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tthis.clientRegistrationRepository = mock(ClientRegistrationRepository.class);\n\t\tfinal OAuth2AuthorizedClientRepository delegate = new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(\n\t\t\t\tnew InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository));\n\t\tthis.authorizedClientRepository = spy(new OAuth2AuthorizedClientRepository() {\n\t\t\t@Override\n\t\t\tpublic <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId,\n\t\t\t\t\tAuthentication principal, HttpServletRequest request) {\n\t\t\t\treturn delegate.loadAuthorizedClient(clientRegistrationId, principal, request);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\t\t\tdelegate.saveAuthorizedClient(authorizedClient, principal, request, response);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\t\t\tdelegate.removeAuthorizedClient(clientRegistrationId, principal, request, response);\n\t\t\t}\n\t\t});\n\t\tthis.authorizedClientFilter = new ServletOAuth2AuthorizedClientExchangeFilterFunction(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.serverUrl = this.server.url(\"/\").toString();\n\t\tthis.webClient = WebClient.builder().apply(this.authorizedClientFilter.oauth2Configuration()).build();\n\t\tthis.authentication = new TestingAuthenticationToken(\"principal\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(this.request, this.response));\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t\tSecurityContextHolder.clearContext();\n\t\tRequestContextHolder.resetRequestAttributes();\n\t}\n\n\t@Test\n\tpublic void requestWhenNotAuthorizedThenAuthorizeAndSendRequest() {\n\t\t// @formatter:off\n\t\tString accessTokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": \\\"3600\\\",\\n\"\n\t\t\t+ \"   \\\"scope\\\": \\\"read write\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\tString clientResponse = \"{\\n\"\n\t\t\t+ \"\t\\\"attribute1\\\": \\\"value1\\\",\\n\"\n\t\t\t+ \"\t\\\"attribute2\\\": \\\"value2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(clientRegistration);\n\t\tthis.webClient.get()\n\t\t\t.uri(this.serverUrl)\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.clientRegistrationId(clientRegistration.getRegistrationId()))\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t.block();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(2);\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), eq(this.request), eq(this.response));\n\t\tassertThat(authorizedClientCaptor.getValue().getClientRegistration()).isSameAs(clientRegistration);\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizedButExpiredThenRefreshAndSendRequest() {\n\t\t// @formatter:off\n\t\tString accessTokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"refreshed-access-token\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": \\\"3600\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\tString clientResponse = \"{\\n\"\n\t\t\t+ \"\t\\\"attribute1\\\": \\\"value1\\\",\\n\"\n\t\t\t+ \"\t\\\"attribute2\\\": \\\"value2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId())))\n\t\t\t.willReturn(clientRegistration);\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant expiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"expired-access-token\", issuedAt, expiresAt, new HashSet<>(Arrays.asList(\"read\", \"write\")));\n\t\tOAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration,\n\t\t\t\tthis.authentication.getName(), accessToken, refreshToken);\n\t\tdoReturn(authorizedClient).when(this.authorizedClientRepository)\n\t\t\t.loadAuthorizedClient(eq(clientRegistration.getRegistrationId()), eq(this.authentication),\n\t\t\t\t\teq(this.request));\n\t\tthis.webClient.get()\n\t\t\t.uri(this.serverUrl)\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.clientRegistrationId(clientRegistration.getRegistrationId()))\n\t\t\t.retrieve()\n\t\t\t.bodyToMono(String.class)\n\t\t\t.block();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(2);\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), eq(this.request), eq(this.response));\n\t\tOAuth2AuthorizedClient refreshedAuthorizedClient = authorizedClientCaptor.getValue();\n\t\tassertThat(refreshedAuthorizedClient.getClientRegistration()).isSameAs(clientRegistration);\n\t\tassertThat(refreshedAuthorizedClient.getAccessToken().getTokenValue()).isEqualTo(\"refreshed-access-token\");\n\t}\n\n\t@Test\n\tpublic void requestMultipleWhenNoneAuthorizedThenAuthorizeAndSendRequest() {\n\t\t// @formatter:off\n\t\tString accessTokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": \\\"3600\\\",\\n\"\n\t\t\t+ \"   \\\"scope\\\": \\\"read write\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\tString clientResponse = \"{\\n\"\n\t\t\t+ \"   \\\"attribute1\\\": \\\"value1\\\",\\n\"\n\t\t\t+ \"   \\\"attribute2\\\": \\\"value2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\t// Client 1\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration1 = TestClientRegistrations.clientCredentials()\n\t\t\t.registrationId(\"client-1\")\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration1.getRegistrationId())))\n\t\t\t.willReturn(clientRegistration1);\n\t\t// Client 2\n\t\tthis.server.enqueue(jsonResponse(accessTokenResponse));\n\t\tthis.server.enqueue(jsonResponse(clientResponse));\n\t\tClientRegistration clientRegistration2 = TestClientRegistrations.clientCredentials()\n\t\t\t.registrationId(\"client-2\")\n\t\t\t.tokenUri(this.serverUrl)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration2.getRegistrationId())))\n\t\t\t.willReturn(clientRegistration2);\n\t\t// @formatter:off\n\t\tthis.webClient.get()\n\t\t\t\t.uri(this.serverUrl)\n\t\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId(clientRegistration1.getRegistrationId()))\n\t\t\t\t.retrieve()\n\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t.flatMap((response) -> this.webClient\n\t\t\t\t\t.get()\n\t\t\t\t\t.uri(this.serverUrl)\n\t\t\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId(clientRegistration2.getRegistrationId()))\n\t\t\t\t\t.retrieve()\n\t\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t)\n\t\t\t\t.contextWrite(context())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(4);\n\t\tArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthorizedClient.class);\n\t\tverify(this.authorizedClientRepository, times(2)).saveAuthorizedClient(authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), eq(this.request), eq(this.response));\n\t\tassertThat(authorizedClientCaptor.getAllValues().get(0).getClientRegistration()).isSameAs(clientRegistration1);\n\t\tassertThat(authorizedClientCaptor.getAllValues().get(1).getClientRegistration()).isSameAs(clientRegistration2);\n\t}\n\n\tprivate Context context() {\n\t\tMap<Object, Object> contextAttributes = new HashMap<>();\n\t\tcontextAttributes.put(HttpServletRequest.class, this.request);\n\t\tcontextAttributes.put(HttpServletResponse.class, this.response);\n\t\tcontextAttributes.put(Authentication.class, this.authentication);\n\t\treturn Context.of(ServletOAuth2AuthorizedClientExchangeFilterFunction.SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY,\n\t\t\t\tcontextAttributes);\n\t}\n\n\tprivate MockResponse jsonResponse(String json) {\n\t\t// @formatter:off\n\t\treturn new MockResponse()\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.setBody(json);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client;\n\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.core.codec.ByteBufferEncoder;\nimport org.springframework.core.codec.CharSequenceEncoder;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.codec.EncoderHttpMessageWriter;\nimport org.springframework.http.codec.FormHttpMessageWriter;\nimport org.springframework.http.codec.HttpMessageWriter;\nimport org.springframework.http.codec.ResourceHttpMessageWriter;\nimport org.springframework.http.codec.ServerSentEventHttpMessageWriter;\nimport org.springframework.http.codec.json.Jackson2JsonEncoder;\nimport org.springframework.http.codec.multipart.MultipartHttpMessageWriter;\nimport org.springframework.http.converter.FormHttpMessageConverter;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.mock.http.client.reactive.MockClientHttpRequest;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.client.ClientAuthorizationException;\nimport org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;\nimport org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;\nimport org.springframework.security.oauth2.client.endpoint.RestClientRefreshTokenTokenResponseClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;\nimport org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.test.web.client.MockRestServiceServer;\nimport org.springframework.web.client.RestClient;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\nimport org.springframework.web.reactive.function.BodyInserter;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;\nimport static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServletOAuth2AuthorizedClientExchangeFilterFunctionTests {\n\n\t@Mock\n\tprivate OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Mock\n\tprivate ClientRegistrationRepository clientRegistrationRepository;\n\n\t@Mock\n\tprivate OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient;\n\n\t@Mock\n\tprivate OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient;\n\n\t@Mock\n\tprivate OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient;\n\n\t@Mock\n\tprivate OAuth2AuthorizationFailureHandler authorizationFailureHandler;\n\n\t@Captor\n\tprivate ArgumentCaptor<OAuth2AuthorizationException> authorizationExceptionCaptor;\n\n\t@Captor\n\tprivate ArgumentCaptor<Authentication> authenticationCaptor;\n\n\t@Captor\n\tprivate ArgumentCaptor<Map<String, Object>> attributesCaptor;\n\n\t@Mock\n\tprivate WebClient.RequestHeadersSpec<?> spec;\n\n\t@Captor\n\tprivate ArgumentCaptor<Consumer<Map<String, Object>>> attrs;\n\n\t@Captor\n\tprivate ArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor;\n\n\tprivate DefaultOAuth2AuthorizedClientManager authorizedClientManager;\n\n\t/**\n\t * Used for get the attributes from defaultRequest.\n\t */\n\tprivate Map<String, Object> result = new HashMap<>();\n\n\tprivate ServletOAuth2AuthorizedClientExchangeFilterFunction function;\n\n\tprivate MockExchangeFunction exchange = new MockExchangeFunction();\n\n\tprivate Authentication authentication;\n\n\tprivate ClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\n\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token-0\",\n\t\t\tInstant.now(), Instant.now().plus(Duration.ofDays(1)));\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authentication = new TestingAuthenticationToken(\"test\", \"this\");\n\t\tJwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();\n\t\tjwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(this.jwtBearerTokenResponseClient);\n\t\t// @formatter:off\n\t\tOAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken(\n\t\t\t\t\t\t(configurer) -> configurer.accessTokenResponseClient(this.refreshTokenTokenResponseClient))\n\t\t\t\t.clientCredentials(\n\t\t\t\t\t\t(configurer) -> configurer.accessTokenResponseClient(this.clientCredentialsTokenResponseClient))\n\t\t\t\t.provider(jwtBearerAuthorizedClientProvider)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(this.clientRegistrationRepository,\n\t\t\t\tthis.authorizedClientRepository);\n\t\tthis.authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\t\tthis.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(this.authorizedClientManager);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tRequestContextHolder.resetRequestAttributes();\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientManagerIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServletOAuth2AuthorizedClientExchangeFilterFunction(null));\n\t}\n\n\t@Test\n\tpublic void defaultRequestRequestResponseWhenNullRequestContextThenRequestAndResponseNull() {\n\t\tMap<String, Object> attrs = getDefaultRequestAttributes();\n\t\tassertThat(ServletOAuth2AuthorizedClientExchangeFilterFunction.getRequest(attrs)).isNull();\n\t\tassertThat(ServletOAuth2AuthorizedClientExchangeFilterFunction.getResponse(attrs)).isNull();\n\t}\n\n\t@Test\n\tpublic void defaultRequestRequestResponseWhenRequestContextThenRequestAndResponseSet() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));\n\t\tMap<String, Object> attrs = getDefaultRequestAttributes();\n\t\tassertThat(ServletOAuth2AuthorizedClientExchangeFilterFunction.getRequest(attrs)).isEqualTo(request);\n\t\tassertThat(ServletOAuth2AuthorizedClientExchangeFilterFunction.getResponse(attrs)).isEqualTo(response);\n\t}\n\n\t@Test\n\tpublic void defaultRequestAuthenticationWhenSecurityContextEmptyThenAuthenticationNull() {\n\t\tMap<String, Object> attrs = getDefaultRequestAttributes();\n\t\tassertThat(ServletOAuth2AuthorizedClientExchangeFilterFunction.getAuthentication(attrs)).isNull();\n\t}\n\n\t@Test\n\tpublic void defaultRequestAuthenticationWhenAuthenticationSetThenAuthenticationSet() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.authentication);\n\t\tMap<String, Object> attrs = getDefaultRequestAttributes();\n\t\tassertThat(ServletOAuth2AuthorizedClientExchangeFilterFunction.getAuthentication(attrs))\n\t\t\t.isEqualTo(this.authentication);\n\t\tverifyNoInteractions(this.authorizedClientRepository);\n\t}\n\n\t@Test\n\tpublic void defaultRequestAuthenticationWhenCustomSecurityContextHolderStrategyThenAuthenticationSet() {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(this.authentication));\n\t\tthis.function.setSecurityContextHolderStrategy(strategy);\n\t\tMap<String, Object> attrs = getDefaultRequestAttributes();\n\t\tassertThat(ServletOAuth2AuthorizedClientExchangeFilterFunction.getAuthentication(attrs))\n\t\t\t.isEqualTo(this.authentication);\n\t\tverify(strategy).getContext();\n\t\tverifyNoInteractions(this.authorizedClientRepository);\n\t}\n\n\tprivate Map<String, Object> getDefaultRequestAttributes() {\n\t\tthis.function.defaultRequest().accept(this.spec);\n\t\tverify(this.spec).attributes(this.attrs.capture());\n\t\tthis.attrs.getValue().accept(this.result);\n\t\treturn this.result;\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizedClientNullThenAuthorizationHeaderNull() {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION)).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizedClientThenAuthorizationHeader() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void filterWhenExistingAuthorizationThenSingleAuthorizationHeader() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Existing\")\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tHttpHeaders headers = this.exchange.getRequest().headers();\n\t\tassertThat(headers.get(HttpHeaders.AUTHORIZATION)).containsOnly(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void filterWhenRefreshRequiredThenRefresh() {\n\t\tOAuth2AccessTokenResponse response = OAuth2AccessTokenResponse.withToken(\"token-1\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(3600)\n\t\t\t.refreshToken(\"refresh-1\")\n\t\t\t.build();\n\t\tgiven(this.refreshTokenTokenResponseClient.getTokenResponse(any())).willReturn(response);\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tthis.accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(), this.accessToken.getTokenValue(),\n\t\t\t\tissuedAt, accessTokenExpiresAt);\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", issuedAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.authentication(this.authentication))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverify(this.refreshTokenTokenResponseClient).getTokenResponse(any());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(this.authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), any(), any());\n\t\tOAuth2AuthorizedClient newAuthorizedClient = this.authorizedClientCaptor.getValue();\n\t\tassertThat(newAuthorizedClient.getAccessToken()).isEqualTo(response.getAccessToken());\n\t\tassertThat(newAuthorizedClient.getRefreshToken()).isEqualTo(response.getRefreshToken());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-1\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenRefreshRequiredThenRefreshAndResponseDoesNotContainRefreshToken() {\n\t\tOAuth2AccessTokenResponse response = OAuth2AccessTokenResponse.withToken(\"token-1\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(3600)\n\t\t\t// .refreshToken(xxx) // No refreshToken in response\n\t\t\t.build();\n\t\tRestClient.Builder builder = RestClient.builder().messageConverters((messageConverters) -> {\n\t\t\tmessageConverters.clear();\n\t\t\tmessageConverters.add(new FormHttpMessageConverter());\n\t\t\tmessageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());\n\t\t});\n\t\tMockRestServiceServer server = MockRestServiceServer.bindTo(builder).build();\n\t\tRestClient refreshTokenClient = builder.build();\n\t\tserver.expect(requestTo(\"https://example.com/login/oauth/access_token\"))\n\t\t\t.andRespond(withSuccess().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.body(new ClassPathResource(\"access-token-response-1.json\")));\n\t\tRestClientRefreshTokenTokenResponseClient refreshTokenTokenResponseClient = new RestClientRefreshTokenTokenResponseClient();\n\t\trefreshTokenTokenResponseClient.setRestClient(refreshTokenClient);\n\t\tRefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider();\n\t\tauthorizedClientProvider.setAccessTokenResponseClient(refreshTokenTokenResponseClient);\n\t\tDefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\t\tthis.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tthis.accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(), this.accessToken.getTokenValue(),\n\t\t\t\tissuedAt, accessTokenExpiresAt);\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", issuedAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.authentication(this.authentication))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tserver.verify();\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(this.authorizedClientCaptor.capture(),\n\t\t\t\teq(this.authentication), any(), any());\n\t\tOAuth2AuthorizedClient newAuthorizedClient = this.authorizedClientCaptor.getValue();\n\t\tassertThat(newAuthorizedClient.getAccessToken().getTokenValue())\n\t\t\t.isEqualTo(response.getAccessToken().getTokenValue());\n\t\tassertThat(newAuthorizedClient.getRefreshToken().getTokenValue()).isEqualTo(refreshToken.getTokenValue());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-1\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenClientCredentialsTokenNotExpiredThenUseCurrentToken() {\n\t\tthis.registration = TestClientRegistrations.clientCredentials().build();\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, null);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.authentication(this.authentication))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverify(this.authorizedClientRepository, never()).saveAuthorizedClient(any(), eq(this.authentication), any(),\n\t\t\t\tany());\n\t\tverify(this.clientCredentialsTokenResponseClient, never()).getTokenResponse(any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request1 = requests.get(0);\n\t\tassertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request1.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request1.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request1)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenClientCredentialsTokenExpiredThenGetNewToken() {\n\t\tthis.registration = TestClientRegistrations.clientCredentials().build();\n\t\tOAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();\n\t\tgiven(this.clientCredentialsTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tthis.accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(), this.accessToken.getTokenValue(),\n\t\t\t\tissuedAt, accessTokenExpiresAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, null);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.authentication(this.authentication))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), eq(this.authentication), any(), any());\n\t\tverify(this.clientCredentialsTokenResponseClient).getTokenResponse(any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request1 = requests.get(0);\n\t\tassertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token\");\n\t\tassertThat(request1.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request1.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request1)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenJwtBearerClientNotAuthorizedThenExchangeToken() {\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"exchanged-token\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(360)\n\t\t\t.build();\n\t\tgiven(this.jwtBearerTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);\n\t\t// @formatter:off\n\t\tClientRegistration registration = ClientRegistration.withRegistrationId(\"jwt-bearer\")\n\t\t\t\t.clientId(\"client-id\")\n\t\t\t\t.clientSecret(\"client-secret\")\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)\n\t\t\t\t.scope(\"read\", \"write\")\n\t\t\t\t.tokenUri(\"https://example.com/oauth/token\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(registration.getRegistrationId())))\n\t\t\t.willReturn(registration);\n\t\tJwt jwtAssertion = TestJwts.jwt().build();\n\t\tAuthentication jwtAuthentication = new TestingAuthenticationToken(jwtAssertion, jwtAssertion);\n\t\tMockHttpServletRequest servletRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse servletResponse = new MockHttpServletResponse();\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.clientRegistrationId(registration.getRegistrationId()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.authentication(jwtAuthentication))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletRequest(servletRequest))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletResponse(servletResponse))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverify(this.jwtBearerTokenResponseClient).getTokenResponse(any());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), eq(jwtAuthentication), any(), any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request1 = requests.get(0);\n\t\tassertThat(request1.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer exchanged-token\");\n\t\tassertThat(request1.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request1.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request1)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenRefreshRequiredAndEmptyReactiveSecurityContextThenSaved() {\n\t\tOAuth2AccessTokenResponse response = OAuth2AccessTokenResponse.withToken(\"token-1\")\n\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t.expiresIn(3600)\n\t\t\t.refreshToken(\"refresh-1\")\n\t\t\t.build();\n\t\tgiven(this.refreshTokenTokenResponseClient.getTokenResponse(any())).willReturn(response);\n\t\tInstant issuedAt = Instant.now().minus(Duration.ofDays(1));\n\t\tInstant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));\n\t\tthis.accessToken = new OAuth2AccessToken(this.accessToken.getTokenType(), this.accessToken.getTokenValue(),\n\t\t\t\tissuedAt, accessTokenExpiresAt);\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", issuedAt);\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverify(this.refreshTokenTokenResponseClient).getTokenResponse(any());\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), any(), any(), any());\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-1\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenRefreshTokenNullThenShouldRefreshFalse() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenNotExpiredThenShouldRefreshFalse() {\n\t\tOAuth2RefreshToken refreshToken = new OAuth2RefreshToken(\"refresh-token\", this.accessToken.getIssuedAt());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken, refreshToken);\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletRequest(new MockHttpServletRequest()))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction\n\t\t\t\t.httpServletResponse(new MockHttpServletResponse()))\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(1);\n\t\tClientRequest request0 = requests.get(0);\n\t\tassertThat(request0.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request0.url().toASCIIString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(request0.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request0)).isEmpty();\n\t}\n\n\t// gh-6483\n\t@Test\n\tpublic void filterWhenChainedThenDefaultsStillAvailable() throws Exception {\n\t\tthis.function.setDefaultOAuth2AuthorizedClient(true);\n\t\tMockHttpServletRequest servletRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse servletResponse = new MockHttpServletResponse();\n\t\tOAuth2User user = mock(OAuth2User.class);\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tOAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(user, authorities,\n\t\t\t\tthis.registration.getRegistrationId());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(\n\t\t\t\teq(authentication.getAuthorizedClientRegistrationId()), eq(authentication), eq(servletRequest)))\n\t\t\t.willReturn(authorizedClient);\n\t\t// Default request attributes set\n\t\tfinal ClientRequest request1 = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example1.com\"))\n\t\t\t.attributes((attrs) -> attrs.putAll(getDefaultRequestAttributes()))\n\t\t\t.build();\n\t\t// Default request attributes NOT set\n\t\tfinal ClientRequest request2 = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example2.com\")).build();\n\t\tContext context = context(servletRequest, servletResponse, authentication);\n\t\tthis.function.filter(request1, this.exchange)\n\t\t\t.flatMap((response) -> this.function.filter(request2, this.exchange))\n\t\t\t.contextWrite(context)\n\t\t\t.block();\n\t\tList<ClientRequest> requests = this.exchange.getRequests();\n\t\tassertThat(requests).hasSize(2);\n\t\tClientRequest request = requests.get(0);\n\t\tassertThat(request.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request.url().toASCIIString()).isEqualTo(\"https://example1.com\");\n\t\tassertThat(request.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request)).isEmpty();\n\t\trequest = requests.get(1);\n\t\tassertThat(request.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo(\"Bearer token-0\");\n\t\tassertThat(request.url().toASCIIString()).isEqualTo(\"https://example2.com\");\n\t\tassertThat(request.method()).isEqualTo(HttpMethod.GET);\n\t\tassertThat(getBody(request)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenUnauthorizedThenInvokeFailureHandler() {\n\t\tassertHttpStatusInvokesFailureHandler(HttpStatus.UNAUTHORIZED, OAuth2ErrorCodes.INVALID_TOKEN);\n\t}\n\n\t@Test\n\tpublic void filterWhenForbiddenThenInvokeFailureHandler() {\n\t\tassertHttpStatusInvokesFailureHandler(HttpStatus.FORBIDDEN, OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t}\n\n\tprivate void assertHttpStatusInvokesFailureHandler(HttpStatus httpStatus, String expectedErrorCode) {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tMockHttpServletRequest servletRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse servletResponse = new MockHttpServletResponse();\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletRequest(servletRequest))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletResponse(servletResponse))\n\t\t\t.build();\n\t\tgiven(this.exchange.getResponse().statusCode()).willReturn(httpStatus);\n\t\tgiven(this.exchange.getResponse().headers()).willReturn(mock(ClientResponse.Headers.class));\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t.isInstanceOfSatisfying(ClientAuthorizationException.class, (ex) -> {\n\t\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(expectedErrorCode);\n\t\t\t\tassertThat(ex).hasNoCause();\n\t\t\t\tassertThat(ex).hasMessageContaining(expectedErrorCode);\n\t\t\t});\n\t\tassertThat(this.authenticationCaptor.getValue().getName()).isEqualTo(authorizedClient.getPrincipalName());\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(\n\t\t\t\tentry(HttpServletRequest.class.getName(), servletRequest),\n\t\t\t\tentry(HttpServletResponse.class.getName(), servletResponse));\n\t}\n\n\t@Test\n\tpublic void filterWhenWWWAuthenticateHeaderIncludesErrorThenInvokeFailureHandler() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tMockHttpServletRequest servletRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse servletResponse = new MockHttpServletResponse();\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletRequest(servletRequest))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletResponse(servletResponse))\n\t\t\t.build();\n\t\tString wwwAuthenticateHeader = \"Bearer error=\\\"insufficient_scope\\\", \"\n\t\t\t\t+ \"error_description=\\\"The request requires higher privileges than provided by the access token.\\\", \"\n\t\t\t\t+ \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\";\n\t\tClientResponse.Headers headers = mock(ClientResponse.Headers.class);\n\t\tgiven(headers.header(eq(HttpHeaders.WWW_AUTHENTICATE)))\n\t\t\t.willReturn(Collections.singletonList(wwwAuthenticateHeader));\n\t\tgiven(this.exchange.getResponse().headers()).willReturn(headers);\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t.isInstanceOfSatisfying(ClientAuthorizationException.class, (ex) -> {\n\t\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t\t\tassertThat(ex.getError().getDescription())\n\t\t\t\t\t.isEqualTo(\"The request requires higher privileges than provided by the access token.\");\n\t\t\t\tassertThat(ex.getError().getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t\t\tassertThat(ex).hasNoCause();\n\t\t\t\tassertThat(ex).hasMessageContaining(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t\t\t});\n\t\tassertThat(this.authenticationCaptor.getValue().getName()).isEqualTo(authorizedClient.getPrincipalName());\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(\n\t\t\t\tentry(HttpServletRequest.class.getName(), servletRequest),\n\t\t\t\tentry(HttpServletResponse.class.getName(), servletResponse));\n\t}\n\n\t@Test\n\tpublic void filterWhenUnauthorizedWithWebClientExceptionThenInvokeFailureHandler() {\n\t\tassertHttpStatusWithWebClientExceptionInvokesFailureHandler(HttpStatus.UNAUTHORIZED,\n\t\t\t\tOAuth2ErrorCodes.INVALID_TOKEN);\n\t}\n\n\t@Test\n\tpublic void filterWhenForbiddenWithWebClientExceptionThenInvokeFailureHandler() {\n\t\tassertHttpStatusWithWebClientExceptionInvokesFailureHandler(HttpStatus.FORBIDDEN,\n\t\t\t\tOAuth2ErrorCodes.INSUFFICIENT_SCOPE);\n\t}\n\n\tprivate void assertHttpStatusWithWebClientExceptionInvokesFailureHandler(HttpStatus httpStatus,\n\t\t\tString expectedErrorCode) {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tMockHttpServletRequest servletRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse servletResponse = new MockHttpServletResponse();\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletRequest(servletRequest))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletResponse(servletResponse))\n\t\t\t.build();\n\t\tWebClientResponseException exception = WebClientResponseException.create(httpStatus.value(),\n\t\t\t\thttpStatus.getReasonPhrase(), HttpHeaders.EMPTY, new byte[0], StandardCharsets.UTF_8);\n\t\tExchangeFunction throwingExchangeFunction = (r) -> Mono.error(exception);\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tassertThatExceptionOfType(WebClientResponseException.class)\n\t\t\t.isThrownBy(() -> this.function.filter(request, throwingExchangeFunction).block())\n\t\t\t.isEqualTo(exception);\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t.isInstanceOfSatisfying(ClientAuthorizationException.class, (ex) -> {\n\t\t\t\tassertThat(ex.getClientRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(expectedErrorCode);\n\t\t\t\tassertThat(ex).hasCause(exception);\n\t\t\t\tassertThat(ex).hasMessageContaining(expectedErrorCode);\n\t\t\t});\n\t\tassertThat(this.authenticationCaptor.getValue().getName()).isEqualTo(authorizedClient.getPrincipalName());\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(\n\t\t\t\tentry(HttpServletRequest.class.getName(), servletRequest),\n\t\t\t\tentry(HttpServletResponse.class.getName(), servletResponse));\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizationExceptionThenInvokeFailureHandler() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tMockHttpServletRequest servletRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse servletResponse = new MockHttpServletResponse();\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletRequest(servletRequest))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletResponse(servletResponse))\n\t\t\t.build();\n\t\tOAuth2AuthorizationException authorizationException = new OAuth2AuthorizationException(\n\t\t\t\tnew OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN));\n\t\tExchangeFunction throwingExchangeFunction = (r) -> Mono.error(authorizationException);\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t.isThrownBy(() -> this.function.filter(request, throwingExchangeFunction).block())\n\t\t\t.isEqualTo(authorizationException);\n\t\tverify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(),\n\t\t\t\tthis.authenticationCaptor.capture(), this.attributesCaptor.capture());\n\t\tassertThat(this.authorizationExceptionCaptor.getValue())\n\t\t\t.isInstanceOfSatisfying(OAuth2AuthorizationException.class, (ex) -> {\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(authorizationException.getError().getErrorCode());\n\t\t\t\tassertThat(ex).hasNoCause();\n\t\t\t\tassertThat(ex).hasMessageContaining(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t});\n\t\tassertThat(this.authenticationCaptor.getValue().getName()).isEqualTo(authorizedClient.getPrincipalName());\n\t\tassertThat(this.attributesCaptor.getValue()).containsExactly(\n\t\t\t\tentry(HttpServletRequest.class.getName(), servletRequest),\n\t\t\t\tentry(HttpServletResponse.class.getName(), servletResponse));\n\t}\n\n\t@Test\n\tpublic void filterWhenOtherHttpStatusThenDoesNotInvokeFailureHandler() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, \"principalName\",\n\t\t\t\tthis.accessToken);\n\t\tMockHttpServletRequest servletRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse servletResponse = new MockHttpServletResponse();\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletRequest(servletRequest))\n\t\t\t.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletResponse(servletResponse))\n\t\t\t.build();\n\t\tgiven(this.exchange.getResponse().statusCode()).willReturn(HttpStatus.BAD_REQUEST);\n\t\tgiven(this.exchange.getResponse().headers()).willReturn(mock(ClientResponse.Headers.class));\n\t\tthis.function.setAuthorizationFailureHandler(this.authorizationFailureHandler);\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tverifyNoInteractions(this.authorizationFailureHandler);\n\t}\n\n\tprivate Context context(HttpServletRequest servletRequest, HttpServletResponse servletResponse,\n\t\t\tAuthentication authentication) {\n\t\tMap<Object, Object> contextAttributes = new HashMap<>();\n\t\tcontextAttributes.put(HttpServletRequest.class, servletRequest);\n\t\tcontextAttributes.put(HttpServletResponse.class, servletResponse);\n\t\tcontextAttributes.put(Authentication.class, authentication);\n\t\treturn Context.of(ServletOAuth2AuthorizedClientExchangeFilterFunction.SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY,\n\t\t\t\tcontextAttributes);\n\t}\n\n\tprivate static String getBody(ClientRequest request) {\n\t\tfinal List<HttpMessageWriter<?>> messageWriters = new ArrayList<>();\n\t\tmessageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));\n\t\tmessageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.textPlainOnly()));\n\t\tmessageWriters.add(new ResourceHttpMessageWriter());\n\t\tJackson2JsonEncoder jsonEncoder = new Jackson2JsonEncoder();\n\t\tmessageWriters.add(new EncoderHttpMessageWriter<>(jsonEncoder));\n\t\tmessageWriters.add(new ServerSentEventHttpMessageWriter(jsonEncoder));\n\t\tmessageWriters.add(new FormHttpMessageWriter());\n\t\tmessageWriters.add(new EncoderHttpMessageWriter<>(CharSequenceEncoder.allMimeTypes()));\n\t\tmessageWriters.add(new MultipartHttpMessageWriter(messageWriters));\n\t\tBodyInserter.Context context = new BodyInserter.Context() {\n\t\t\t@Override\n\t\t\tpublic List<HttpMessageWriter<?>> messageWriters() {\n\t\t\t\treturn messageWriters;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<ServerHttpRequest> serverRequest() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Map<String, Object> hints() {\n\t\t\t\treturn new HashMap<>();\n\t\t\t}\n\t\t};\n\t\tMockClientHttpRequest body = new MockClientHttpRequest(HttpMethod.GET, \"/\");\n\t\trequest.body().insert(body, context).block();\n\t\treturn body.getBodyAsString().block();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/support/OAuth2WebClientHttpServiceGroupConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.function.client.support;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.service.invoker.HttpServiceProxyFactory;\nimport org.springframework.web.service.registry.HttpServiceGroupConfigurer;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link OAuth2WebClientHttpServiceGroupConfigurer}.\n *\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\nclass OAuth2WebClientHttpServiceGroupConfigurerTests {\n\n\t@Mock\n\tprivate OAuth2AuthorizedClientManager authoriedClientManager;\n\n\t@Mock\n\tprivate HttpServiceGroupConfigurer.Groups<WebClient.Builder> groups;\n\n\t@Captor\n\tArgumentCaptor<HttpServiceGroupConfigurer.ProxyFactoryCallback> forProxyFactory;\n\n\t@Mock\n\tprivate HttpServiceProxyFactory.Builder factoryBuilder;\n\n\t@Captor\n\tprivate ArgumentCaptor<HttpServiceGroupConfigurer.ClientCallback<WebClient.Builder>> configureClient;\n\n\t@Mock\n\tprivate WebClient.Builder clientBuilder;\n\n\t@Test\n\tvoid configureGroupsConfigureProxyFactory() {\n\n\t\tOAuth2WebClientHttpServiceGroupConfigurer configurer = OAuth2WebClientHttpServiceGroupConfigurer\n\t\t\t.from(this.authoriedClientManager);\n\n\t\tconfigurer.configureGroups(this.groups);\n\t\tverify(this.groups).forEachProxyFactory(this.forProxyFactory.capture());\n\n\t\tthis.forProxyFactory.getValue().withProxyFactory(null, this.factoryBuilder);\n\n\t\tverify(this.factoryBuilder).httpRequestValuesProcessor(ClientRegistrationIdProcessor.DEFAULT_INSTANCE);\n\t}\n\n\t@Test\n\tvoid configureGroupsConfigureClient() {\n\t\tOAuth2WebClientHttpServiceGroupConfigurer configurer = OAuth2WebClientHttpServiceGroupConfigurer\n\t\t\t.from(this.authoriedClientManager);\n\n\t\tconfigurer.configureGroups(this.groups);\n\t\tverify(this.groups).forEachClient(this.configureClient.capture());\n\n\t\tthis.configureClient.getValue().withClient(null, this.clientBuilder);\n\n\t\tverify(this.clientBuilder).filter(any(ExchangeFilterFunction.class));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/result/method/annotation/OAuth2AuthorizedClientArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.reactive.result.method.annotation;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class OAuth2AuthorizedClientArgumentResolverTests {\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Mock\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate ServerWebExchange serverWebExchange = MockServerWebExchange.builder(MockServerHttpRequest.get(\"/\")).build();\n\n\tprivate OAuth2AuthorizedClientArgumentResolver argumentResolver;\n\n\tprivate ClientRegistration clientRegistration;\n\n\tprivate OAuth2AuthorizedClient authorizedClient;\n\n\tprivate Authentication authentication = new TestingAuthenticationToken(\"test\", \"this\");\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\t// @formatter:off\n\t\tReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder\n\t\t\t\t.builder()\n\t\t\t\t.authorizationCode()\n\t\t\t\t.refreshToken()\n\t\t\t\t.clientCredentials()\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tDefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tauthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);\n\t\tthis.argumentResolver = new OAuth2AuthorizedClientArgumentResolver(authorizedClientManager);\n\t\tthis.clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tthis.authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, this.authentication.getName(),\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizedClientArgumentResolver(null, this.authorizedClientRepository));\n\t}\n\n\t@Test\n\tpublic void constructorWhenOAuth2AuthorizedClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizedClientArgumentResolver(this.clientRegistrationRepository, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientManagerIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AuthorizedClientArgumentResolver(null));\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenParameterTypeOAuth2AuthorizedClientThenTrue() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThat(this.argumentResolver.supportsParameter(methodParameter)).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenParameterTypeOAuth2AuthorizedClientWithoutAnnotationThenFalse() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClientWithoutAnnotation\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThat(this.argumentResolver.supportsParameter(methodParameter)).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWhenParameterTypeUnsupportedWithoutAnnotationThenFalse() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeUnsupportedWithoutAnnotation\",\n\t\t\t\tString.class);\n\t\tassertThat(this.argumentResolver.supportsParameter(methodParameter)).isFalse();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenRegistrationIdEmptyAndNotOAuth2AuthenticationThenThrowIllegalArgumentException() {\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"registrationIdEmpty\", OAuth2AuthorizedClient.class);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> resolveArgument(methodParameter))\n\t\t\t.withMessage(\"The clientRegistrationId could not be resolved. Please provide one\");\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenRegistrationIdEmptyAndOAuth2AuthenticationThenResolves() {\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(), any()))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tthis.authentication = mock(OAuth2AuthenticationToken.class);\n\t\tgiven(((OAuth2AuthenticationToken) this.authentication).getAuthorizedClientRegistrationId())\n\t\t\t.willReturn(\"client1\");\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"registrationIdEmpty\", OAuth2AuthorizedClient.class);\n\t\tresolveArgument(methodParameter);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenParameterTypeOAuth2AuthorizedClientAndCurrentAuthenticationNullThenResolves() {\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(), any()))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tthis.authentication = null;\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThat(resolveArgument(methodParameter)).isSameAs(this.authorizedClient);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenOAuth2AuthorizedClientFoundThenResolves() {\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(), any()))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(), any()))\n\t\t\t.willReturn(Mono.just(this.authorizedClient));\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThat(resolveArgument(methodParameter)).isSameAs(this.authorizedClient);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenOAuth2AuthorizedClientNotFoundThenThrowClientAuthorizationRequiredException() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(anyString(), any(), any())).willReturn(Mono.empty());\n\t\tMethodParameter methodParameter = this.getMethodParameter(\"paramTypeAuthorizedClient\",\n\t\t\t\tOAuth2AuthorizedClient.class);\n\t\tassertThatExceptionOfType(ClientAuthorizationRequiredException.class)\n\t\t\t.isThrownBy(() -> resolveArgument(methodParameter));\n\t}\n\n\tprivate Object resolveArgument(MethodParameter methodParameter) {\n\t\treturn this.argumentResolver.resolveArgument(methodParameter, null, null)\n\t\t\t.contextWrite((this.authentication != null)\n\t\t\t\t\t? ReactiveSecurityContextHolder.withAuthentication(this.authentication) : Context.empty())\n\t\t\t.contextWrite(serverWebExchange())\n\t\t\t.block();\n\t}\n\n\tprivate Context serverWebExchange() {\n\t\treturn Context.of(ServerWebExchange.class, this.serverWebExchange);\n\t}\n\n\tprivate MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {\n\t\tMethod method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);\n\t\treturn new MethodParameter(method, 0);\n\t}\n\n\tstatic class TestController {\n\n\t\tvoid paramTypeAuthorizedClient(\n\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"client1\") OAuth2AuthorizedClient authorizedClient) {\n\t\t}\n\n\t\tvoid paramTypeAuthorizedClientWithoutAnnotation(OAuth2AuthorizedClient authorizedClient) {\n\t\t}\n\n\t\tvoid paramTypeUnsupported(@RegisteredOAuth2AuthorizedClient(\"client1\") String param) {\n\t\t}\n\n\t\tvoid paramTypeUnsupportedWithoutAnnotation(String param) {\n\t\t}\n\n\t\tvoid registrationIdEmpty(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/AuthenticatedPrincipalServerOAuth2AuthorizedClientRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;\nimport org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\npublic class AuthenticatedPrincipalServerOAuth2AuthorizedClientRepositoryTests {\n\n\tprivate String registrationId = \"registrationId\";\n\n\tprivate String principalName = \"principalName\";\n\n\tprivate ReactiveOAuth2AuthorizedClientService authorizedClientService;\n\n\tprivate ServerOAuth2AuthorizedClientRepository anonymousAuthorizedClientRepository;\n\n\tprivate AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientService = mock(ReactiveOAuth2AuthorizedClientService.class);\n\t\tthis.anonymousAuthorizedClientRepository = mock(ServerOAuth2AuthorizedClientRepository.class);\n\t\tthis.authorizedClientRepository = new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(\n\t\t\t\tthis.authorizedClientService);\n\t\tthis.authorizedClientRepository\n\t\t\t.setAnonymousAuthorizedClientRepository(this.anonymousAuthorizedClientRepository);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientServiceIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(null));\n\t}\n\n\t@Test\n\tpublic void setAuthorizedClientRepositoryWhenAuthorizedClientRepositoryIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.setAnonymousAuthorizedClientRepository(null));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenAuthenticatedPrincipalThenLoadFromService() {\n\t\tgiven(this.authorizedClientService.loadAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\tAuthentication authentication = this.createAuthenticatedPrincipal();\n\t\tthis.authorizedClientRepository.loadAuthorizedClient(this.registrationId, authentication, this.exchange)\n\t\t\t.block();\n\t\tverify(this.authorizedClientService).loadAuthorizedClient(this.registrationId, this.principalName);\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenAnonymousPrincipalThenLoadFromAnonymousRepository() {\n\t\tgiven(this.anonymousAuthorizedClientRepository.loadAuthorizedClient(any(), any(), any()))\n\t\t\t.willReturn(Mono.empty());\n\t\tAuthentication authentication = this.createAnonymousPrincipal();\n\t\tthis.authorizedClientRepository.loadAuthorizedClient(this.registrationId, authentication, this.exchange)\n\t\t\t.block();\n\t\tverify(this.anonymousAuthorizedClientRepository).loadAuthorizedClient(this.registrationId, authentication,\n\t\t\t\tthis.exchange);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthenticatedPrincipalThenSaveToService() {\n\t\tgiven(this.authorizedClientService.saveAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\tAuthentication authentication = this.createAuthenticatedPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, authentication, this.exchange).block();\n\t\tverify(this.authorizedClientService).saveAuthorizedClient(authorizedClient, authentication);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAnonymousPrincipalThenSaveToAnonymousRepository() {\n\t\tgiven(this.anonymousAuthorizedClientRepository.saveAuthorizedClient(any(), any(), any()))\n\t\t\t.willReturn(Mono.empty());\n\t\tAuthentication authentication = this.createAnonymousPrincipal();\n\t\tOAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, authentication, this.exchange).block();\n\t\tverify(this.anonymousAuthorizedClientRepository).saveAuthorizedClient(authorizedClient, authentication,\n\t\t\t\tthis.exchange);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenAuthenticatedPrincipalThenRemoveFromService() {\n\t\tgiven(this.authorizedClientService.removeAuthorizedClient(any(), any())).willReturn(Mono.empty());\n\t\tAuthentication authentication = this.createAuthenticatedPrincipal();\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId, authentication, this.exchange)\n\t\t\t.block();\n\t\tverify(this.authorizedClientService).removeAuthorizedClient(this.registrationId, this.principalName);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenAnonymousPrincipalThenRemoveFromAnonymousRepository() {\n\t\tgiven(this.anonymousAuthorizedClientRepository.removeAuthorizedClient(any(), any(), any()))\n\t\t\t.willReturn(Mono.empty());\n\t\tAuthentication authentication = this.createAnonymousPrincipal();\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId, authentication, this.exchange)\n\t\t\t.block();\n\t\tverify(this.anonymousAuthorizedClientRepository).removeAuthorizedClient(this.registrationId, authentication,\n\t\t\t\tthis.exchange);\n\t}\n\n\tprivate Authentication createAuthenticatedPrincipal() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(this.principalName, \"password\");\n\t\tauthentication.setAuthenticated(true);\n\t\treturn authentication;\n\t}\n\n\tprivate Authentication createAnonymousPrincipal() {\n\t\treturn new AnonymousAuthenticationToken(\"key-1234\", \"anonymousUser\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/DefaultServerOAuth2AuthorizationRequestResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.PkceParameterNames;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\nimport org.springframework.web.server.ResponseStatusException;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class DefaultServerOAuth2AuthorizationRequestResolverTests {\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\tprivate DefaultServerOAuth2AuthorizationRequestResolver resolver;\n\n\tprivate ClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.resolver = new DefaultServerOAuth2AuthorizationRequestResolver(this.clientRegistrationRepository);\n\t}\n\n\t@Test\n\tpublic void setAuthorizationRequestCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.resolver.setAuthorizationRequestCustomizer(null));\n\t}\n\n\t@Test\n\tpublic void resolveWhenNotMatchThenNull() {\n\t\tassertThat(resolve(\"/\")).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenClientRegistrationNotFoundMatchThenBadRequest() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.empty());\n\t\tassertThatExceptionOfType(ResponseStatusException.class)\n\t\t\t.isThrownBy(() -> resolve(\"/oauth2/authorization/not-found-id\"))\n\t\t\t.satisfies((ex) -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST));\n\t}\n\n\t@Test\n\tpublic void resolveWhenClientRegistrationFoundThenWorks() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.just(this.registration));\n\t\tOAuth2AuthorizationRequest request = resolve(\"/oauth2/authorization/not-found-id\");\n\t\tassertThat(request.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.*?&\" + \"redirect_uri=/login/oauth2/code/registration-id\"\n\t\t\t\t\t+ \"&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenForwardedHeadersClientRegistrationFoundThenWorks() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.just(this.registration));\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> httpRequest = MockServerHttpRequest\n\t\t\t\t.get(\"/oauth2/authorization/id\")\n\t\t\t\t.header(\"X-Forwarded-Host\", \"evil.com\");\n\t\t// @formatter:on\n\t\tServerWebExchange exchange = MockServerWebExchange.from(httpRequest);\n\t\tOAuth2AuthorizationRequest request = this.resolver.resolve(exchange).block();\n\t\tassertThat(request.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.*?&\" + \"redirect_uri=/login/oauth2/code/registration-id\"\n\t\t\t\t\t+ \"&code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestWithValidPublicClientThenResolves() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration()\n\t\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)\n\t\t\t\t.clientSecret(null)\n\t\t\t\t.build()));\n\t\tOAuth2AuthorizationRequest request = resolve(\"/oauth2/authorization/registration-id\");\n\t\tassertThat((String) request.getAttribute(PkceParameterNames.CODE_VERIFIER))\n\t\t\t.matches(\"^([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){128}$\");\n\t\tassertThat(request.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=read:user&state=.*?&\" + \"redirect_uri=/login/oauth2/code/registration-id&\"\n\t\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\" + \"code_challenge_method=S256\");\n\t}\n\n\t// gh-6548\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestApplyPkceToConfidentialClientsThenApplied() {\n\t\tClientRegistration registration1 = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(registration1.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(registration1));\n\t\tClientRegistration registration2 = TestClientRegistrations.clientRegistration2().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(eq(registration2.getRegistrationId())))\n\t\t\t.willReturn(Mono.just(registration2));\n\n\t\tOAuth2AuthorizationRequest request = resolve(\"/oauth2/authorization/\" + registration1.getRegistrationId());\n\t\tassertPkceApplied(request, registration1);\n\n\t\trequest = resolve(\"/oauth2/authorization/\" + registration2.getRegistrationId());\n\t\tassertPkceApplied(request, registration2);\n\t}\n\n\t@Test\n\tvoid resolveWhenRequireProofKeyTrueThenPkceEnabled() {\n\t\tClientRegistration.ClientSettings pkceEnabled = ClientRegistration.ClientSettings.builder()\n\t\t\t.requireProofKey(true)\n\t\t\t.build();\n\t\tClientRegistration clientWithPkceEnabled = TestClientRegistrations.clientRegistration()\n\t\t\t.clientSettings(pkceEnabled)\n\t\t\t.build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(clientWithPkceEnabled));\n\n\t\tOAuth2AuthorizationRequest request = resolve(\n\t\t\t\t\"/oauth2/authorization/\" + clientWithPkceEnabled.getRegistrationId());\n\t\tassertPkceApplied(request, clientWithPkceEnabled);\n\t}\n\n\tprivate void assertPkceApplied(OAuth2AuthorizationRequest authorizationRequest,\n\t\t\tClientRegistration clientRegistration) {\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).containsKey(PkceParameterNames.CODE_CHALLENGE);\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.contains(entry(PkceParameterNames.CODE_CHALLENGE_METHOD, \"S256\"));\n\t\tassertThat(authorizationRequest.getAttributes()).containsKey(PkceParameterNames.CODE_VERIFIER);\n\t\tassertThat((String) authorizationRequest.getAttribute(PkceParameterNames.CODE_VERIFIER))\n\t\t\t.matches(\"^([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){128}$\");\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&\" + \"client_id=\"\n\t\t\t\t\t+ clientRegistration.getClientId() + \"&\" + \"scope=read:user&\" + \"state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=/login/oauth2/code/\" + clientRegistration.getRegistrationId() + \"&\"\n\t\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\" + \"code_challenge_method=S256\");\n\t}\n\n\tprivate void assertPkceNotApplied(OAuth2AuthorizationRequest authorizationRequest,\n\t\t\tClientRegistration clientRegistration) {\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).doesNotContainKey(PkceParameterNames.CODE_CHALLENGE);\n\t\tassertThat(authorizationRequest.getAdditionalParameters())\n\t\t\t.doesNotContainKey(PkceParameterNames.CODE_CHALLENGE_METHOD);\n\t\tassertThat(authorizationRequest.getAttributes()).doesNotContainKey(PkceParameterNames.CODE_VERIFIER);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&\" + \"client_id=\"\n\t\t\t\t\t+ clientRegistration.getClientId() + \"&\" + \"scope=read:user&\" + \"state=.{15,}&\"\n\t\t\t\t\t+ \"redirect_uri=/login/oauth2/code/\" + clientRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthenticationRequestWithValidOidcClientThenResolves() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().scope(OidcScopes.OPENID).build()));\n\t\tOAuth2AuthorizationRequest request = resolve(\"/oauth2/authorization/registration-id\");\n\t\tassertThat((String) request.getAttribute(OidcParameterNames.NONCE)).matches(\"^([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){128}$\");\n\t\tassertThat(request.getAuthorizationRequestUri()).matches(\"https://example.com/login/oauth/authorize\\\\?\"\n\t\t\t\t+ \"response_type=code&client_id=client-id&\" + \"scope=openid&state=.*?&\"\n\t\t\t\t+ \"redirect_uri=/login/oauth2/code/registration-id&\" + \"nonce=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\"\n\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\" + \"code_challenge_method=S256\");\n\t}\n\n\t// gh-7696\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestCustomizerRemovesNonceThenQueryExcludesNonce() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().scope(OidcScopes.OPENID).build()));\n\t\tthis.resolver.setAuthorizationRequestCustomizer(\n\t\t\t\t(builder) -> builder.additionalParameters((params) -> params.remove(OidcParameterNames.NONCE))\n\t\t\t\t\t.attributes((attrs) -> attrs.remove(OidcParameterNames.NONCE)));\n\t\tOAuth2AuthorizationRequest authorizationRequest = resolve(\"/oauth2/authorization/registration-id\");\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).doesNotContainKey(OidcParameterNames.NONCE);\n\t\tassertThat(authorizationRequest.getAttributes()).doesNotContainKey(OidcParameterNames.NONCE);\n\t\tassertThat(authorizationRequest.getAttributes()).containsKey(OAuth2ParameterNames.REGISTRATION_ID);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=openid&state=.{15,}&\" + \"redirect_uri=/login/oauth2/code/registration-id&\"\n\t\t\t\t\t+ \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\" + \"code_challenge_method=S256\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestCustomizerAddsParameterThenQueryIncludesParameter() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().scope(OidcScopes.OPENID).build()));\n\t\tthis.resolver.setAuthorizationRequestCustomizer((builder) -> builder.authorizationRequestUri((uriBuilder) -> {\n\t\t\turiBuilder.queryParam(\"param1\", \"value1\");\n\t\t\treturn uriBuilder.build();\n\t\t}));\n\t\tOAuth2AuthorizationRequest authorizationRequest = resolve(\"/oauth2/authorization/registration-id\");\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=openid&state=.{15,}&\" + \"redirect_uri=/login/oauth2/code/registration-id&\"\n\t\t\t\t\t+ \"nonce=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\" + \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\"\n\t\t\t\t\t+ \"code_challenge_method=S256&\" + \"param1=value1\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthorizationRequestCustomizerOverridesParameterThenQueryIncludesParameter() {\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(TestClientRegistrations.clientRegistration().scope(OidcScopes.OPENID).build()));\n\t\tthis.resolver.setAuthorizationRequestCustomizer((builder) -> builder.parameters((params) -> {\n\t\t\tparams.put(\"appid\", params.get(\"client_id\"));\n\t\t\tparams.remove(\"client_id\");\n\t\t}));\n\t\tOAuth2AuthorizationRequest authorizationRequest = resolve(\"/oauth2/authorization/registration-id\");\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.matches(\"https://example.com/login/oauth/authorize\\\\?\" + \"response_type=code&\"\n\t\t\t\t\t+ \"scope=openid&state=.{15,}&\" + \"redirect_uri=/login/oauth2/code/registration-id&\"\n\t\t\t\t\t+ \"nonce=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\" + \"code_challenge=([a-zA-Z0-9\\\\-\\\\.\\\\_\\\\~]){43}&\"\n\t\t\t\t\t+ \"code_challenge_method=S256&\" + \"appid=client-id\");\n\t}\n\n\tprivate OAuth2AuthorizationRequest resolve(String path) {\n\t\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(path));\n\t\treturn this.resolver.resolve(exchange).block();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationCodeGrantWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.net.URI;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.oauth2.client.authentication.TestOAuth2AuthorizationCodeAuthenticationTokens;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.handler.DefaultWebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * @author Rob Winch\n * @author Parikshit Dutta\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class OAuth2AuthorizationCodeGrantWebFilterTests {\n\n\tprivate OAuth2AuthorizationCodeGrantWebFilter filter;\n\n\t@Mock\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Mock\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t@Mock\n\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.filter = new OAuth2AuthorizationCodeGrantWebFilter(this.authenticationManager,\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tthis.filter.setAuthorizationRequestRepository(this.authorizationRequestRepository);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenIllegalArgumentException() {\n\t\tthis.authenticationManager = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeGrantWebFilter(this.authenticationManager,\n\t\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryNullThenIllegalArgumentException() {\n\t\tthis.clientRegistrationRepository = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeGrantWebFilter(this.authenticationManager,\n\t\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizedClientRepositoryNullThenIllegalArgumentException() {\n\t\tthis.authorizedClientRepository = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationCodeGrantWebFilter(this.authenticationManager,\n\t\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository));\n\t}\n\n\t@Test\n\tpublic void setRequestCacheWhenRequestCacheIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestCache(null));\n\t}\n\n\t@Test\n\tpublic void filterWhenNotMatchThenAuthenticationManagerNotCalled() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tDefaultWebFilterChain chain = new DefaultWebFilterChain((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.emptyList());\n\t\tthis.filter.filter(exchange, chain).block();\n\t\tverifyNoInteractions(this.authenticationManager);\n\t}\n\n\t@Test\n\tpublic void filterWhenMatchThenAuthorizedClientSaved() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.just(clientRegistration));\n\t\tMockServerHttpRequest authorizationRequest = createAuthorizationRequest(\"/authorization/callback\");\n\t\tOAuth2AuthorizationRequest oauth2AuthorizationRequest = createOAuth2AuthorizationRequest(authorizationRequest,\n\t\t\t\tclientRegistration);\n\t\tgiven(this.authorizationRequestRepository.loadAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tgiven(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(TestOAuth2AuthorizationCodeAuthenticationTokens.authenticated()));\n\t\tMockServerHttpRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse);\n\t\tDefaultWebFilterChain chain = new DefaultWebFilterChain((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.emptyList());\n\t\tthis.filter.filter(exchange, chain).block();\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), any(AnonymousAuthenticationToken.class),\n\t\t\t\tany());\n\t}\n\n\t// gh-7966\n\t@Test\n\tpublic void filterWhenAuthorizationRequestRedirectUriParametersMatchThenProcessed() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.just(clientRegistration));\n\t\tgiven(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(TestOAuth2AuthorizationCodeAuthenticationTokens.authenticated()));\n\t\t// 1) redirect_uri with query parameters\n\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\tparameters.put(\"param1\", \"value1\");\n\t\tparameters.put(\"param2\", \"value2\");\n\t\tMockServerHttpRequest authorizationRequest = createAuthorizationRequest(\"/authorization/callback\", parameters);\n\t\tOAuth2AuthorizationRequest oauth2AuthorizationRequest = createOAuth2AuthorizationRequest(authorizationRequest,\n\t\t\t\tclientRegistration);\n\t\tgiven(this.authorizationRequestRepository.loadAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tMockServerHttpRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse);\n\t\tDefaultWebFilterChain chain = new DefaultWebFilterChain((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.emptyList());\n\t\tthis.filter.filter(exchange, chain).block();\n\t\tverify(this.authenticationManager, times(1)).authenticate(any());\n\t\t// 2) redirect_uri with query parameters AND authorization response additional\n\t\t// parameters\n\t\tMap<String, String> additionalParameters = new LinkedHashMap<>();\n\t\tadditionalParameters.put(\"auth-param1\", \"value1\");\n\t\tadditionalParameters.put(\"auth-param2\", \"value2\");\n\t\tauthorizationResponse = createAuthorizationResponse(authorizationRequest, additionalParameters);\n\t\texchange = MockServerWebExchange.from(authorizationResponse);\n\t\tthis.filter.filter(exchange, chain).block();\n\t\tverify(this.authenticationManager, times(2)).authenticate(any());\n\t}\n\n\t// gh-7966\n\t@Test\n\tpublic void filterWhenAuthorizationRequestRedirectUriParametersNotMatchThenNotProcessed() {\n\t\tString requestUri = \"/authorization/callback\";\n\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\tparameters.put(\"param1\", \"value1\");\n\t\tparameters.put(\"param2\", \"value2\");\n\t\tMockServerHttpRequest authorizationRequest = createAuthorizationRequest(requestUri, parameters);\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tOAuth2AuthorizationRequest oauth2AuthorizationRequest = createOAuth2AuthorizationRequest(authorizationRequest,\n\t\t\t\tclientRegistration);\n\t\tgiven(this.authorizationRequestRepository.loadAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\t// 1) Parameter value\n\t\tMap<String, String> parametersNotMatch = new LinkedHashMap<>(parameters);\n\t\tparametersNotMatch.put(\"param2\", \"value8\");\n\t\tMockServerHttpRequest authorizationResponse = createAuthorizationResponse(\n\t\t\t\tcreateAuthorizationRequest(requestUri, parametersNotMatch));\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse);\n\t\tDefaultWebFilterChain chain = new DefaultWebFilterChain((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.emptyList());\n\t\tthis.filter.filter(exchange, chain).block();\n\t\tverifyNoInteractions(this.authenticationManager);\n\t\t// 2) Parameter order\n\t\tparametersNotMatch = new LinkedHashMap<>();\n\t\tparametersNotMatch.put(\"param2\", \"value2\");\n\t\tparametersNotMatch.put(\"param1\", \"value1\");\n\t\tauthorizationResponse = createAuthorizationResponse(createAuthorizationRequest(requestUri, parametersNotMatch));\n\t\texchange = MockServerWebExchange.from(authorizationResponse);\n\t\tthis.filter.filter(exchange, chain).block();\n\t\tverifyNoInteractions(this.authenticationManager);\n\t\t// 3) Parameter missing\n\t\tparametersNotMatch = new LinkedHashMap<>(parameters);\n\t\tparametersNotMatch.remove(\"param2\");\n\t\tauthorizationResponse = createAuthorizationResponse(createAuthorizationRequest(requestUri, parametersNotMatch));\n\t\texchange = MockServerWebExchange.from(authorizationResponse);\n\t\tthis.filter.filter(exchange, chain).block();\n\t\tverifyNoInteractions(this.authenticationManager);\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizationSucceedsAndRequestCacheConfiguredThenRequestCacheUsed() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.just(clientRegistration));\n\t\tgiven(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(TestOAuth2AuthorizationCodeAuthenticationTokens.authenticated()));\n\t\tMockServerHttpRequest authorizationRequest = createAuthorizationRequest(\"/authorization/callback\");\n\t\tOAuth2AuthorizationRequest oauth2AuthorizationRequest = createOAuth2AuthorizationRequest(authorizationRequest,\n\t\t\t\tclientRegistration);\n\t\tgiven(this.authorizationRequestRepository.loadAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tMockServerHttpRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse);\n\t\tDefaultWebFilterChain chain = new DefaultWebFilterChain((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.emptyList());\n\t\tServerRequestCache requestCache = mock(ServerRequestCache.class);\n\t\tgiven(requestCache.getRedirectUri(any(ServerWebExchange.class)))\n\t\t\t.willReturn(Mono.just(URI.create(\"/saved-request\")));\n\t\tthis.filter.setRequestCache(requestCache);\n\t\tthis.filter.filter(exchange, chain).block();\n\t\tverify(requestCache).getRedirectUri(exchange);\n\t\tassertThat(exchange.getResponse().getHeaders().getLocation().toString()).isEqualTo(\"/saved-request\");\n\t}\n\n\t// gh-8609\n\t@Test\n\tpublic void filterWhenAuthenticationConverterThrowsOAuth2AuthorizationExceptionThenMappedToOAuth2AuthenticationException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.empty());\n\t\tMockServerHttpRequest authorizationRequest = createAuthorizationRequest(\"/authorization/callback\");\n\t\tOAuth2AuthorizationRequest oauth2AuthorizationRequest = createOAuth2AuthorizationRequest(authorizationRequest,\n\t\t\t\tclientRegistration);\n\t\tgiven(this.authorizationRequestRepository.loadAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tMockServerHttpRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse);\n\t\tDefaultWebFilterChain chain = new DefaultWebFilterChain((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.emptyList());\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.filter.filter(exchange, chain).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError()).extracting(\"errorCode\")\n\t\t\t\t.isEqualTo(\"client_registration_not_found\"));\n\t\tverifyNoInteractions(this.authenticationManager);\n\t}\n\n\t// gh-8609\n\t@Test\n\tpublic void filterWhenAuthenticationManagerThrowsOAuth2AuthorizationExceptionThenMappedToOAuth2AuthenticationException() {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.just(clientRegistration));\n\t\tMockServerHttpRequest authorizationRequest = createAuthorizationRequest(\"/authorization/callback\");\n\t\tOAuth2AuthorizationRequest oauth2AuthorizationRequest = createOAuth2AuthorizationRequest(authorizationRequest,\n\t\t\t\tclientRegistration);\n\t\tgiven(this.authorizationRequestRepository.loadAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(oauth2AuthorizationRequest));\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.error(new OAuth2AuthorizationException(new OAuth2Error(\"authorization_error\"))));\n\t\tMockServerHttpRequest authorizationResponse = createAuthorizationResponse(authorizationRequest);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(authorizationResponse);\n\t\tDefaultWebFilterChain chain = new DefaultWebFilterChain((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.emptyList());\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.filter.filter(exchange, chain).block())\n\t\t\t.satisfies((ex) -> assertThat(ex.getError()).extracting(\"errorCode\").isEqualTo(\"authorization_error\"));\n\t}\n\n\tprivate static OAuth2AuthorizationRequest createOAuth2AuthorizationRequest(\n\t\t\tMockServerHttpRequest authorizationRequest, ClientRegistration registration) {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, registration.getRegistrationId());\n\t\t// @formatter:off\n\t\treturn TestOAuth2AuthorizationRequests.request()\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.redirectUri(authorizationRequest.getURI().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static MockServerHttpRequest createAuthorizationRequest(String requestUri) {\n\t\treturn createAuthorizationRequest(requestUri, new LinkedHashMap<>());\n\t}\n\n\tprivate static MockServerHttpRequest createAuthorizationRequest(String requestUri, Map<String, String> parameters) {\n\t\tMockServerHttpRequest.BaseBuilder<?> builder = MockServerHttpRequest.get(requestUri);\n\t\tif (!CollectionUtils.isEmpty(parameters)) {\n\t\t\tparameters.forEach(builder::queryParam);\n\t\t}\n\t\treturn builder.build();\n\t}\n\n\tprivate static MockServerHttpRequest createAuthorizationResponse(MockServerHttpRequest authorizationRequest) {\n\t\treturn createAuthorizationResponse(authorizationRequest, new LinkedHashMap<>());\n\t}\n\n\tprivate static MockServerHttpRequest createAuthorizationResponse(MockServerHttpRequest authorizationRequest,\n\t\t\tMap<String, String> additionalParameters) {\n\t\tMockServerHttpRequest.BaseBuilder<?> builder = MockServerHttpRequest\n\t\t\t.get(authorizationRequest.getURI().toString());\n\t\tbuilder.queryParam(OAuth2ParameterNames.CODE, \"code\");\n\t\tbuilder.queryParam(OAuth2ParameterNames.STATE, \"state\");\n\t\tadditionalParameters.forEach(builder::queryParam);\n\t\tbuilder.cookies(authorizationRequest.getCookies());\n\t\treturn builder.build();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/OAuth2AuthorizationRequestRedirectWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.test.web.reactive.server.FluxExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.server.handler.FilteringWebHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class OAuth2AuthorizationRequestRedirectWebFilterTests {\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRepository;\n\n\t@Mock\n\tprivate ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authzRequestRepository;\n\n\t@Mock\n\tprivate ServerRequestCache requestCache;\n\n\tprivate ClientRegistration registration = TestClientRegistrations.clientRegistration().build();\n\n\tprivate OAuth2AuthorizationRequestRedirectWebFilter filter;\n\n\tprivate WebTestClient client;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.filter = new OAuth2AuthorizationRequestRedirectWebFilter(this.clientRepository);\n\t\tthis.filter.setAuthorizationRequestRepository(this.authzRequestRepository);\n\t\tFilteringWebHandler webHandler = new FilteringWebHandler((e) -> e.getResponse().setComplete(),\n\t\t\t\tArrays.asList(this.filter));\n\t\tthis.client = WebTestClient.bindToWebHandler(webHandler).build();\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientRegistrationRepositoryNullThenIllegalArgumentException() {\n\t\tthis.clientRepository = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationRequestRedirectWebFilter(this.clientRepository));\n\t}\n\n\t@Test\n\tpublic void setterWhenAuthorizationRedirectStrategyNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthorizationRedirectStrategy(null));\n\t}\n\n\t@Test\n\tpublic void filterWhenDoesNotMatchThenClientRegistrationRepositoryNotSubscribed() {\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().isOk();\n\t\t// @formatter:on\n\t\tverifyNoInteractions(this.clientRepository, this.authzRequestRepository);\n\t}\n\n\t@Test\n\tpublic void filterWhenDoesMatchThenClientRegistrationRepositoryNotSubscribed() {\n\t\tgiven(this.clientRepository.findByRegistrationId(this.registration.getRegistrationId()))\n\t\t\t.willReturn(Mono.just(this.registration));\n\t\tgiven(this.authzRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tFluxExchangeResult<String> result = this.client.get()\n\t\t\t\t.uri(\"https://example.com/oauth2/authorization/registration-id\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.returnResult(String.class);\n\t\t// @formatter:on\n\t\tresult.assertWithDiagnostics(() -> {\n\t\t\tURI location = result.getResponseHeaders().getLocation();\n\t\t\tassertThat(location).hasScheme(\"https\")\n\t\t\t\t.hasHost(\"example.com\")\n\t\t\t\t.hasPath(\"/login/oauth/authorize\")\n\t\t\t\t.hasParameter(\"response_type\", \"code\")\n\t\t\t\t.hasParameter(\"client_id\", \"client-id\")\n\t\t\t\t.hasParameter(\"scope\", \"read:user\")\n\t\t\t\t.hasParameter(\"state\")\n\t\t\t\t.hasParameter(\"redirect_uri\", \"https://example.com/login/oauth2/code/registration-id\");\n\t\t});\n\t\tverify(this.authzRequestRepository).saveAuthorizationRequest(any(), any());\n\t}\n\n\t// gh-5520\n\t@Test\n\tpublic void filterWhenDoesMatchThenResolveRedirectUriExpandedExcludesQueryString() {\n\t\tgiven(this.clientRepository.findByRegistrationId(this.registration.getRegistrationId()))\n\t\t\t.willReturn(Mono.just(this.registration));\n\t\tgiven(this.authzRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tFluxExchangeResult<String> result = this.client.get()\n\t\t\t\t.uri(\"https://example.com/oauth2/authorization/registration-id?foo=bar\").exchange().expectStatus()\n\t\t\t\t.is3xxRedirection().returnResult(String.class);\n\t\tresult.assertWithDiagnostics(() -> {\n\t\t\tURI location = result.getResponseHeaders().getLocation();\n\t\t\tassertThat(location)\n\t\t\t\t\t.hasScheme(\"https\")\n\t\t\t\t\t.hasHost(\"example.com\")\n\t\t\t\t\t.hasPath(\"/login/oauth/authorize\")\n\t\t\t\t\t.hasParameter(\"response_type\", \"code\")\n\t\t\t\t\t.hasParameter(\"client_id\", \"client-id\")\n\t\t\t\t\t.hasParameter(\"scope\", \"read:user\")\n\t\t\t\t\t.hasParameter(\"state\")\n\t\t\t\t\t.hasParameter(\"redirect_uri\", \"https://example.com/login/oauth2/code/registration-id\");\n\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void filterWhenExceptionThenRedirected() {\n\t\tgiven(this.clientRepository.findByRegistrationId(this.registration.getRegistrationId()))\n\t\t\t.willReturn(Mono.just(this.registration));\n\t\tgiven(this.authzRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());\n\t\tFilteringWebHandler webHandler = new FilteringWebHandler(\n\t\t\t\t(e) -> Mono.error(new ClientAuthorizationRequiredException(this.registration.getRegistrationId())),\n\t\t\t\tArrays.asList(this.filter));\n\t\t// @formatter:off\n\t\tthis.client = WebTestClient.bindToWebHandler(webHandler)\n\t\t\t\t.build();\n\t\tFluxExchangeResult<String> result = this.client.get()\n\t\t\t\t.uri(\"https://example.com/foo\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.returnResult(String.class);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void filterWhenExceptionThenSaveRequestSessionAttribute() {\n\t\tgiven(this.clientRepository.findByRegistrationId(this.registration.getRegistrationId()))\n\t\t\t.willReturn(Mono.just(this.registration));\n\t\tgiven(this.authzRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());\n\t\tthis.filter.setRequestCache(this.requestCache);\n\t\tgiven(this.requestCache.saveRequest(any())).willReturn(Mono.empty());\n\t\tFilteringWebHandler webHandler = new FilteringWebHandler(\n\t\t\t\t(e) -> Mono.error(new ClientAuthorizationRequiredException(this.registration.getRegistrationId())),\n\t\t\t\tArrays.asList(this.filter));\n\t\t// @formatter:off\n\t\tthis.client = WebTestClient.bindToWebHandler(webHandler)\n\t\t\t\t.build();\n\t\tthis.client.get()\n\t\t\t\t.uri(\"https://example.com/foo\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.returnResult(String.class);\n\t\t// @formatter:on\n\t\tverify(this.requestCache).saveRequest(any());\n\t}\n\n\t@Test\n\tpublic void filterWhenPathMatchesThenRequestSessionAttributeNotSaved() {\n\t\tgiven(this.clientRepository.findByRegistrationId(this.registration.getRegistrationId()))\n\t\t\t.willReturn(Mono.just(this.registration));\n\t\tgiven(this.authzRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());\n\t\tthis.filter.setRequestCache(this.requestCache);\n\t\t// @formatter:off\n\t\tthis.client.get()\n\t\t\t\t.uri(\"https://example.com/oauth2/authorization/registration-id\")\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus().is3xxRedirection()\n\t\t\t\t.returnResult(String.class);\n\t\t// @formatter:on\n\t\tverifyNoInteractions(this.requestCache);\n\t}\n\n\t@Test\n\tpublic void filterWhenCustomRedirectStrategySetThenRedirectUriInResponseBody() {\n\t\tgiven(this.clientRepository.findByRegistrationId(this.registration.getRegistrationId()))\n\t\t\t.willReturn(Mono.just(this.registration));\n\t\tgiven(this.authzRequestRepository.saveAuthorizationRequest(any(), any())).willReturn(Mono.empty());\n\t\tServerRedirectStrategy customRedirectStrategy = (exchange, location) -> {\n\t\t\tServerHttpResponse response = exchange.getResponse();\n\t\t\tresponse.setStatusCode(HttpStatus.OK);\n\t\t\tresponse.getHeaders().setContentType(MediaType.TEXT_PLAIN);\n\t\t\tDataBuffer buffer = exchange.getResponse()\n\t\t\t\t.bufferFactory()\n\t\t\t\t.wrap(location.toASCIIString().getBytes(StandardCharsets.UTF_8));\n\n\t\t\treturn exchange.getResponse().writeWith(Flux.just(buffer));\n\t\t};\n\t\tthis.filter.setAuthorizationRedirectStrategy(customRedirectStrategy);\n\t\tthis.filter.setRequestCache(this.requestCache);\n\n\t\tFluxExchangeResult<String> result = this.client.get()\n\t\t\t.uri(\"https://example.com/oauth2/authorization/registration-id\")\n\t\t\t.exchange()\n\t\t\t.expectHeader()\n\t\t\t.contentType(MediaType.TEXT_PLAIN)\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.returnResult(String.class);\n\n\t\t// @formatter:off\n\t\tStepVerifier.create(result.getResponseBody())\n\t\t\t\t.assertNext((uri) -> {\n\t\t\t\t\tURI location = URI.create(uri);\n\n\t\t\t\t\tassertThat(location)\n\t\t\t\t\t\t\t.hasScheme(\"https\")\n\t\t\t\t\t\t\t.hasHost(\"example.com\")\n\t\t\t\t\t\t\t.hasPath(\"/login/oauth/authorize\")\n\t\t\t\t\t\t\t.hasParameter(\"response_type\", \"code\")\n\t\t\t\t\t\t\t.hasParameter(\"client_id\", \"client-id\")\n\t\t\t\t\t\t\t.hasParameter(\"scope\", \"read:user\")\n\t\t\t\t\t\t\t.hasParameter(\"state\")\n\t\t\t\t\t\t\t.hasParameter(\"redirect_uri\", \"https://example.com/login/oauth2/code/registration-id\");\n\t\t\t\t})\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\n\t\tverifyNoInteractions(this.requestCache);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/ServerOAuth2AuthorizationCodeAuthenticationTokenConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.ClientAuthenticationMethod;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServerOAuth2AuthorizationCodeAuthenticationTokenConverterTests {\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Mock\n\tprivate ServerAuthorizationRequestRepository authorizationRequestRepository;\n\n\tprivate String clientRegistrationId = \"github\";\n\n\t// @formatter:off\n\tprivate ClientRegistration clientRegistration = ClientRegistration.withRegistrationId(this.clientRegistrationId)\n\t\t\t.redirectUri(\"{baseUrl}/{action}/oauth2/code/{registrationId}\")\n\t\t\t.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)\n\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t.scope(\"read:user\")\n\t\t\t.authorizationUri(\"https://github.com/login/oauth/authorize\")\n\t\t\t.tokenUri(\"https://github.com/login/oauth/access_token\")\n\t\t\t.userInfoUri(\"https://api.github.com/user\")\n\t\t\t.userNameAttributeName(\"id\")\n\t\t\t.clientName(\"GitHub\")\n\t\t\t.clientId(\"clientId\")\n\t\t\t.clientSecret(\"clientSecret\")\n\t\t\t.build();\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate OAuth2AuthorizationRequest.Builder authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(\"https://example.com/oauth2/authorize\")\n\t\t\t.clientId(\"client-id\")\n\t\t\t.redirectUri(\"http://localhost/client-1\")\n\t\t\t.state(\"state\")\n\t\t\t.attributes(Collections.singletonMap(OAuth2ParameterNames.REGISTRATION_ID, this.clientRegistrationId));\n\t// @formatter:on\n\n\tprivate final MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\");\n\n\tprivate ServerOAuth2AuthorizationCodeAuthenticationTokenConverter converter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.converter = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(\n\t\t\t\tthis.clientRegistrationRepository);\n\t\tthis.converter.setAuthorizationRequestRepository(this.authorizationRequestRepository);\n\t}\n\n\t@Test\n\tpublic void applyWhenAuthorizationRequestEmptyThenOAuth2AuthorizationException() {\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any())).willReturn(Mono.empty());\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(() -> applyConverter());\n\t}\n\n\t@Test\n\tpublic void applyWhenAttributesMissingThenOAuth2AuthorizationException() {\n\t\tthis.authorizationRequest.attributes(Map::clear);\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(this.authorizationRequest.build()));\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(() -> applyConverter())\n\t\t\t.withMessageContaining(\n\t\t\t\t\tServerOAuth2AuthorizationCodeAuthenticationTokenConverter.CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE);\n\t}\n\n\t@Test\n\tpublic void applyWhenClientRegistrationMissingThenOAuth2AuthorizationException() {\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(this.authorizationRequest.build()));\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.empty());\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(() -> applyConverter())\n\t\t\t.withMessageContaining(\n\t\t\t\t\tServerOAuth2AuthorizationCodeAuthenticationTokenConverter.CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE);\n\t}\n\n\t@Test\n\tpublic void applyWhenCodeParameterNotFoundThenErrorCode() {\n\t\tthis.request.queryParam(OAuth2ParameterNames.ERROR, \"error\");\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(this.authorizationRequest.build()));\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tassertThat(applyConverter().getAuthorizationExchange().getAuthorizationResponse().getError().getErrorCode())\n\t\t\t.isEqualTo(\"error\");\n\t}\n\n\t@Test\n\tpublic void applyWhenCodeParameterFoundThenCode() {\n\t\tthis.request.queryParam(OAuth2ParameterNames.CODE, \"code\");\n\t\tgiven(this.authorizationRequestRepository.removeAuthorizationRequest(any()))\n\t\t\t.willReturn(Mono.just(this.authorizationRequest.build()));\n\t\tgiven(this.clientRegistrationRepository.findByRegistrationId(any()))\n\t\t\t.willReturn(Mono.just(this.clientRegistration));\n\t\tOAuth2AuthorizationCodeAuthenticationToken result = applyConverter();\n\t\tOAuth2AuthorizationResponse exchange = result.getAuthorizationExchange().getAuthorizationResponse();\n\t\tassertThat(exchange.getError()).isNull();\n\t\tassertThat(exchange.getCode()).isEqualTo(\"code\");\n\t}\n\n\tprivate OAuth2AuthorizationCodeAuthenticationToken applyConverter() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\treturn (OAuth2AuthorizationCodeAuthenticationToken) this.converter.convert(exchange).block();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class WebSessionOAuth2ServerAuthorizationRequestRepositoryTests {\n\n\tprivate WebSessionOAuth2ServerAuthorizationRequestRepository repository = new WebSessionOAuth2ServerAuthorizationRequestRepository();\n\n\t// @formatter:off\n\tprivate OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(\"https://example.com/oauth2/authorize\")\n\t\t\t.clientId(\"client-id\")\n\t\t\t.redirectUri(\"http://localhost/client-1\")\n\t\t\t.state(\"state\")\n\t\t\t.build();\n\t// @formatter:on\n\n\tprivate ServerWebExchange exchange = MockServerWebExchange\n\t\t.from(MockServerHttpRequest.get(\"/\").queryParam(OAuth2ParameterNames.STATE, \"state\"));\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenNullExchangeThenIllegalArgumentException() {\n\t\tthis.exchange = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repository.loadAuthorizationRequest(this.exchange));\n\t}\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenNoSessionThenEmpty() {\n\t\t// @formatter:off\n\t\tStepVerifier.create(this.repository.loadAuthorizationRequest(this.exchange))\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t\tassertSessionStartedIs(false);\n\t}\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenSessionAndNoRequestThenEmpty() {\n\t\t// @formatter:off\n\t\tMono<OAuth2AuthorizationRequest> setAttrThenLoad = this.exchange.getSession()\n\t\t\t\t.map(WebSession::getAttributes)\n\t\t\t\t.doOnNext((attrs) -> attrs.put(\"foo\", \"bar\"))\n\t\t\t\t.then(this.repository.loadAuthorizationRequest(this.exchange));\n\t\tStepVerifier.create(setAttrThenLoad)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenNoStateParamThenEmpty() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\t// @formatter:off\n\t\tMono<OAuth2AuthorizationRequest> saveAndLoad = this.repository\n\t\t\t\t.saveAuthorizationRequest(this.authorizationRequest, this.exchange)\n\t\t\t\t.then(this.repository.loadAuthorizationRequest(this.exchange));\n\t\tStepVerifier.create(saveAndLoad)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void loadAuthorizationRequestWhenSavedThenAuthorizationRequest() {\n\t\t// @formatter:off\n\t\tMono<OAuth2AuthorizationRequest> saveAndLoad = this.repository\n\t\t\t\t.saveAuthorizationRequest(this.authorizationRequest, this.exchange)\n\t\t\t\t.then(this.repository.loadAuthorizationRequest(this.exchange));\n\t\tStepVerifier.create(saveAndLoad)\n\t\t\t\t.expectNext(this.authorizationRequest)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenAuthorizationRequestNullThenThrowsIllegalArgumentException() {\n\t\tthis.authorizationRequest = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.repository.saveAuthorizationRequest(this.authorizationRequest, this.exchange));\n\t\tassertSessionStartedIs(false);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizationRequestWhenExchangeNullThenThrowsIllegalArgumentException() {\n\t\tthis.exchange = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.repository.saveAuthorizationRequest(this.authorizationRequest, this.exchange));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizationRequestWhenExchangeNullThenThrowsIllegalArgumentException() {\n\t\tthis.exchange = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.repository.removeAuthorizationRequest(this.exchange));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizationRequestWhenNotPresentThenThrowsIllegalArgumentException() {\n\t\tStepVerifier.create(this.repository.removeAuthorizationRequest(this.exchange)).verifyComplete();\n\t\tassertSessionStartedIs(false);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizationRequestWhenPresentThenFoundAndRemoved() {\n\t\t// @formatter:off\n\t\tMono<OAuth2AuthorizationRequest> saveAndRemove = this.repository\n\t\t\t\t.saveAuthorizationRequest(this.authorizationRequest, this.exchange)\n\t\t\t\t.then(this.repository.removeAuthorizationRequest(this.exchange));\n\t\tStepVerifier.create(saveAndRemove)\n\t\t\t\t.expectNext(this.authorizationRequest)\n\t\t\t\t.verifyComplete();\n\t\tStepVerifier.create(this.exchange\n\t\t\t\t\t.getSession()\n\t\t\t\t\t.map(WebSession::getAttributes)\n\t\t\t\t\t.map(Map::isEmpty)\n\t\t\t\t)\n\t\t\t\t.expectNext(true).verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\t// gh-5599\n\t@Test\n\tpublic void removeAuthorizationRequestWhenStateMissingThenNoErrors() {\n\t\t// @formatter:off\n\t\tMockServerHttpRequest otherState = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.queryParam(OAuth2ParameterNames.STATE, \"other\")\n\t\t\t\t.build();\n\t\tServerWebExchange otherStateExchange = this.exchange.mutate()\n\t\t\t\t.request(otherState)\n\t\t\t\t.build();\n\t\tMono<OAuth2AuthorizationRequest> saveAndRemove = this.repository\n\t\t\t\t.saveAuthorizationRequest(this.authorizationRequest, this.exchange)\n\t\t\t\t.then(this.repository.removeAuthorizationRequest(otherStateExchange));\n\t\tStepVerifier.create(saveAndRemove)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n\tprivate void assertSessionStartedIs(boolean expected) {\n\t\t// @formatter:off\n\t\tMono<Boolean> isStarted = this.exchange.getSession()\n\t\t\t\t.map(WebSession::isStarted);\n\t\tStepVerifier.create(isStarted)\n\t\t\t\t.expectNext(expected)\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/WebSessionServerOAuth2AuthorizedClientRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class WebSessionServerOAuth2AuthorizedClientRepositoryTests {\n\n\tprivate WebSessionServerOAuth2AuthorizedClientRepository authorizedClientRepository = new WebSessionServerOAuth2AuthorizedClientRepository();\n\n\tprivate MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\n\tprivate ClientRegistration registration1 = TestClientRegistrations.clientRegistration().build();\n\n\tprivate ClientRegistration registration2 = TestClientRegistrations.clientRegistration2().build();\n\n\tprivate String registrationId1 = this.registration1.getRegistrationId();\n\n\tprivate String registrationId2 = this.registration2.getRegistrationId();\n\n\tprivate String principalName1 = \"principalName-1\";\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.loadAuthorizedClient(null, null, this.exchange).block());\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenPrincipalNameIsNullThenExceptionNotThrown() {\n\t\tthis.authorizedClientRepository.loadAuthorizedClient(this.registrationId1, null, this.exchange).block();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.authorizedClientRepository.loadAuthorizedClient(this.registrationId1, null, null).block());\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenClientRegistrationNotFoundThenReturnNull() {\n\t\tOAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(\"registration-not-found\", null, this.exchange)\n\t\t\t.block();\n\t\tassertThat(authorizedClient).isNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthorizedClientWhenSavedThenReturnAuthorizedClient() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.exchange).block();\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId1, null, this.exchange)\n\t\t\t.block();\n\t\tassertThat(loadedAuthorizedClient).isEqualTo(authorizedClient);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthorizedClientIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.saveAuthorizedClient(null, null, this.exchange).block());\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenAuthenticationIsNullThenExceptionNotThrown() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.exchange).block();\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, null).block());\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenSavedThenSavedToSession() {\n\t\tOAuth2AuthorizedClient expected = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(expected, null, this.exchange).block();\n\t\tOAuth2AuthorizedClient result = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId2, null, this.exchange)\n\t\t\t.block();\n\t\tassertThat(result).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.removeAuthorizedClient(null, null, this.exchange));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenPrincipalNameIsNullThenExceptionNotThrown() {\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, this.exchange);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, null));\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenNotSavedThenSessionNotCreated() {\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId2, null, this.exchange);\n\t\tassertThat(this.exchange.getSession().block().isStarted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClient1SavedAndClient2RemovedThenClient1NotRemoved() {\n\t\tOAuth2AuthorizedClient authorizedClient1 = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient1, null, this.exchange).block();\n\t\t// Remove registrationId2 (never added so is not removed either)\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId2, null, this.exchange);\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient1 = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId1, null, this.exchange)\n\t\t\t.block();\n\t\tassertThat(loadedAuthorizedClient1).isNotNull();\n\t\tassertThat(loadedAuthorizedClient1).isSameAs(authorizedClient1);\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenSavedThenRemoved() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.exchange).block();\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId2, null, this.exchange)\n\t\t\t.block();\n\t\tassertThat(loadedAuthorizedClient).isSameAs(authorizedClient);\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId2, null, this.exchange).block();\n\t\tloadedAuthorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId2, null, this.exchange)\n\t\t\t.block();\n\t\tassertThat(loadedAuthorizedClient).isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenSavedThenRemovedFromSession() {\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, this.exchange).block();\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId1, null, this.exchange)\n\t\t\t.block();\n\t\tassertThat(loadedAuthorizedClient).isSameAs(authorizedClient);\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, this.exchange).block();\n\t\tWebSession session = this.exchange.getSession().block();\n\t\tassertThat(session).isNotNull();\n\t\tassertThat(session.getAttributes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenClient1Client2SavedAndClient1RemovedThenClient2NotRemoved() {\n\t\tOAuth2AuthorizedClient authorizedClient1 = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient1, null, this.exchange).block();\n\t\tOAuth2AuthorizedClient authorizedClient2 = new OAuth2AuthorizedClient(this.registration2, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\tthis.authorizedClientRepository.saveAuthorizedClient(authorizedClient2, null, this.exchange).block();\n\t\tthis.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, this.exchange).block();\n\t\tOAuth2AuthorizedClient loadedAuthorizedClient2 = this.authorizedClientRepository\n\t\t\t.loadAuthorizedClient(this.registrationId2, null, this.exchange)\n\t\t\t.block();\n\t\tassertThat(loadedAuthorizedClient2).isNotNull();\n\t\tassertThat(loadedAuthorizedClient2).isSameAs(authorizedClient2);\n\t}\n\n\t@Test\n\tpublic void saveAuthorizedClientWhenSessionIsNullThenThrowIllegalArgumentException() {\n\t\tServerWebExchange exchange = mock(ServerWebExchange.class);\n\t\tgiven(exchange.getSession()).willReturn(Mono.empty());\n\t\tOAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration1, this.principalName1,\n\t\t\t\tmock(OAuth2AccessToken.class));\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, exchange).block())\n\t\t\t.withMessage(\"session cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void removeAuthorizedClientWhenSessionIsNullThenThrowIllegalArgumentException() {\n\t\tServerWebExchange exchange = mock(ServerWebExchange.class);\n\t\tgiven(exchange.getSession()).willReturn(Mono.empty());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authorizedClientRepository.removeAuthorizedClient(this.registrationId1, null, exchange).block())\n\t\t\t.withMessage(\"session cannot be null\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/server/authentication/OAuth2LoginAuthenticationWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.client.web.server.authentication;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.handler.DefaultWebFilterChain;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class OAuth2LoginAuthenticationWebFilterTests {\n\n\t@Mock\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t@Mock\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate OAuth2LoginAuthenticationWebFilter filter;\n\n\tprivate WebFilterExchange webFilterExchange;\n\n\tprivate ClientRegistration.Builder registration = TestClientRegistrations.clientRegistration();\n\n\tprivate OAuth2AuthorizationResponse.Builder authorizationResponseBldr = OAuth2AuthorizationResponse.success(\"code\")\n\t\t.state(\"state\");\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.filter = new OAuth2LoginAuthenticationWebFilter(this.authenticationManager,\n\t\t\t\tthis.authorizedClientRepository);\n\t\tthis.webFilterExchange = new WebFilterExchange(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\")),\n\t\t\t\tnew DefaultWebFilterChain((exchange) -> exchange.getResponse().setComplete(), Collections.emptyList()));\n\t\tgiven(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())).willReturn(Mono.empty());\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessWhenOAuth2LoginAuthenticationTokenThenSavesAuthorizedClient() {\n\t\tthis.filter.onAuthenticationSuccess(loginToken(), this.webFilterExchange).block();\n\t\tverify(this.authorizedClientRepository).saveAuthorizedClient(any(), any(), any());\n\t}\n\n\tprivate OAuth2LoginAuthenticationToken loginToken() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\t\tInstant.now(), Instant.now().plus(Duration.ofDays(1)), Collections.singleton(\"user\"));\n\t\tDefaultOAuth2User user = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"ROLE_USER\"),\n\t\t\t\tCollections.singletonMap(\"user\", \"rob\"), \"user\");\n\t\tClientRegistration clientRegistration = this.registration.build();\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.state(\"state\")\n\t\t\t\t.clientId(clientRegistration.getClientId())\n\t\t\t\t.authorizationUri(clientRegistration.getProviderDetails()\n\t\t\t\t.getAuthorizationUri())\n\t\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t\t.scopes(clientRegistration.getScopes())\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = this.authorizationResponseBldr\n\t\t\t\t.redirectUri(clientRegistration.getRedirectUri())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\treturn new OAuth2LoginAuthenticationToken(clientRegistration, authorizationExchange, user,\n\t\t\t\tuser.getAuthorities(), accessToken);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/access-token-response-1.json",
    "content": "{\n  \"access_token\": \"token-1\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/access-token-response-create.json",
    "content": "{\n  \"access_token\": \"access-token-1234\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"scope\": \"create\"\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/access-token-response-openid-profile-2.json",
    "content": "{\n  \"access_token\": \"access-token-1234\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"scope\": \"openid profile\",\n  \"refresh_token\": \"refresh-token-1234\",\n  \"custom_parameter_1\":  \"custom-value-1\",\n  \"custom_parameter_2\": \"custom-value-2\"\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/access-token-response-openid-profile.json",
    "content": "{\n  \"access_token\": \"access-token-1234\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"scope\": \"openid profile\"\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/access-token-response-read-write.json",
    "content": "{\n  \"access_token\": \"access-token-1234\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"scope\": \"read write\"\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/access-token-response-read.json",
    "content": "{\n  \"access_token\": \"access-token-1234\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"scope\": \"read\"\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/access-token-response.json",
    "content": "{\n  \"access_token\": \"access-token-1234\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/custom-oauth2-client-schema.sql",
    "content": "CREATE TABLE oauth2AuthorizedClient (\n  clientRegistrationId varchar(100) NOT NULL,\n  principalName varchar(200) NOT NULL,\n  accessTokenType varchar(100) NOT NULL,\n  accessTokenValue blob NOT NULL,\n  accessTokenIssuedAt timestamp NOT NULL,\n  accessTokenExpiresAt timestamp NOT NULL,\n  accessTokenScopes varchar(1000) DEFAULT NULL,\n  refreshTokenValue blob DEFAULT NULL,\n  refreshTokenIssuedAt timestamp DEFAULT NULL,\n  createdAt timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,\n  PRIMARY KEY (clientRegistrationId, principalName)\n);\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/invalid-grant-response.json",
    "content": "{\n  \"error\": \"invalid_grant\",\n  \"error_description\": \"Invalid grant\"\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/invalid-token-type-response.json",
    "content": "{\n  \"access_token\": \"access-token-1234\",\n  \"token_type\": \"not-bearer\",\n  \"expires_in\": 3600\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/server-error-response.json",
    "content": "{\n  \"error\": \"server_error\",\n  \"error_description\": \"A server error occurred\"\n}"
  },
  {
    "path": "oauth2/oauth2-client/src/test/resources/unauthorized-client-response.json",
    "content": "{\n  \"error\": \"unauthorized_client\",\n  \"error_description\": \"Unauthorized client\"\n}"
  },
  {
    "path": "oauth2/oauth2-core/spring-security-oauth2-core.gradle",
    "content": "plugins {\n\tid 'compile-warnings-error'\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-web'\n\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\toptional 'com.nimbusds:oauth2-oidc-sdk'\n\toptional 'org.springframework:spring-webflux'\n\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/AbstractOAuth2Token.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serializable;\nimport java.time.Instant;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Base class for OAuth 2.0 Token implementations.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2Token\n * @see OAuth2AccessToken\n * @see OAuth2RefreshToken\n */\npublic abstract class AbstractOAuth2Token implements OAuth2Token, Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String tokenValue;\n\n\tprivate final @Nullable Instant issuedAt;\n\n\tprivate final @Nullable Instant expiresAt;\n\n\t/**\n\t * Sub-class constructor.\n\t * @param tokenValue the token value\n\t */\n\tprotected AbstractOAuth2Token(String tokenValue) {\n\t\tthis(tokenValue, null, null);\n\t}\n\n\t/**\n\t * Sub-class constructor.\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the token was issued, may be {@code null}\n\t * @param expiresAt the expiration time on or after which the token MUST NOT be\n\t * accepted, may be {@code null}\n\t */\n\tprotected AbstractOAuth2Token(String tokenValue, @Nullable Instant issuedAt, @Nullable Instant expiresAt) {\n\t\tAssert.hasText(tokenValue, \"tokenValue cannot be empty\");\n\t\tif (issuedAt != null && expiresAt != null) {\n\t\t\tAssert.isTrue(expiresAt.isAfter(issuedAt), \"expiresAt must be after issuedAt\");\n\t\t}\n\t\tthis.tokenValue = tokenValue;\n\t\tthis.issuedAt = issuedAt;\n\t\tthis.expiresAt = expiresAt;\n\t}\n\n\t/**\n\t * Returns the token value.\n\t * @return the token value\n\t */\n\tpublic String getTokenValue() {\n\t\treturn this.tokenValue;\n\t}\n\n\t/**\n\t * Returns the time at which the token was issued.\n\t * @return the time the token was issued or {@code null}\n\t */\n\tpublic @Nullable Instant getIssuedAt() {\n\t\treturn this.issuedAt;\n\t}\n\n\t/**\n\t * Returns the expiration time on or after which the token MUST NOT be accepted.\n\t * @return the token expiration time or {@code null}\n\t */\n\tpublic @Nullable Instant getExpiresAt() {\n\t\treturn this.expiresAt;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tAbstractOAuth2Token other = (AbstractOAuth2Token) obj;\n\t\tif (!this.getTokenValue().equals(other.getTokenValue())) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.getIssuedAt() != null) ? !this.getIssuedAt().equals(other.getIssuedAt())\n\t\t\t\t: other.getIssuedAt() != null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn (this.getExpiresAt() != null) ? this.getExpiresAt().equals(other.getExpiresAt())\n\t\t\t\t: other.getExpiresAt() == null;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.getTokenValue().hashCode();\n\t\tresult = 31 * result + ((this.getIssuedAt() != null) ? this.getIssuedAt().hashCode() : 0);\n\t\tresult = 31 * result + ((this.getExpiresAt() != null) ? this.getExpiresAt().hashCode() : 0);\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/AuthenticationMethod.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serializable;\n\nimport org.springframework.util.Assert;\n\n/**\n * The authentication method used when sending bearer access tokens in resource requests\n * to resource servers.\n *\n * @author MyeongHyeon Lee\n * @since 5.1\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6750#section-2\">Section 2\n * Authenticated Requests</a>\n */\npublic final class AuthenticationMethod implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tpublic static final AuthenticationMethod HEADER = new AuthenticationMethod(\"header\");\n\n\tpublic static final AuthenticationMethod FORM = new AuthenticationMethod(\"form\");\n\n\tpublic static final AuthenticationMethod QUERY = new AuthenticationMethod(\"query\");\n\n\tprivate final String value;\n\n\t/**\n\t * Constructs an {@code AuthenticationMethod} using the provided value.\n\t * @param value the value of the authentication method type\n\t */\n\tpublic AuthenticationMethod(String value) {\n\t\tAssert.hasText(value, \"value cannot be empty\");\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Returns the value of the authentication method type.\n\t * @return the value of the authentication method type\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tAuthenticationMethod that = (AuthenticationMethod) obj;\n\t\treturn this.getValue().equals(that.getValue());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.getValue().hashCode();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/AuthorizationGrantType.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serializable;\n\nimport org.springframework.util.Assert;\n\n/**\n * An authorization grant is a credential representing the resource owner's authorization\n * (to access its protected resources) to the client and used by the client to obtain an\n * access token.\n *\n * <p>\n * The OAuth 2.0 Authorization Framework defines the standard grant types: authorization\n * code, refresh token and client credentials. It also provides an extensibility mechanism\n * for defining additional grant types.\n *\n * @author Joe Grandja\n * @author Steve Riesenberg\n * @since 5.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.3\">Section\n * 1.3 Authorization Grant</a>\n */\npublic final class AuthorizationGrantType implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tpublic static final AuthorizationGrantType AUTHORIZATION_CODE = new AuthorizationGrantType(\"authorization_code\");\n\n\tpublic static final AuthorizationGrantType REFRESH_TOKEN = new AuthorizationGrantType(\"refresh_token\");\n\n\tpublic static final AuthorizationGrantType CLIENT_CREDENTIALS = new AuthorizationGrantType(\"client_credentials\");\n\n\t/**\n\t * @since 5.5\n\t */\n\tpublic static final AuthorizationGrantType JWT_BEARER = new AuthorizationGrantType(\n\t\t\t\"urn:ietf:params:oauth:grant-type:jwt-bearer\");\n\n\t/**\n\t * @since 6.1\n\t */\n\tpublic static final AuthorizationGrantType DEVICE_CODE = new AuthorizationGrantType(\n\t\t\t\"urn:ietf:params:oauth:grant-type:device_code\");\n\n\t/**\n\t * @since 6.3\n\t */\n\tpublic static final AuthorizationGrantType TOKEN_EXCHANGE = new AuthorizationGrantType(\n\t\t\t\"urn:ietf:params:oauth:grant-type:token-exchange\");\n\n\tprivate final String value;\n\n\t/**\n\t * Constructs an {@code AuthorizationGrantType} using the provided value.\n\t * @param value the value of the authorization grant type\n\t */\n\tpublic AuthorizationGrantType(String value) {\n\t\tAssert.hasText(value, \"value cannot be empty\");\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Returns the value of the authorization grant type.\n\t * @return the value of the authorization grant type\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tAuthorizationGrantType that = (AuthorizationGrantType) obj;\n\t\treturn this.getValue().equals(that.getValue());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.getValue().hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AuthorizationGrantType{\" + \"value='\" + this.value + '\\'' + '}';\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.util.Assert;\n\n/**\n * An &quot;accessor&quot; for a set of claims that may be used for assertions.\n *\n * @author Joe Grandja\n * @since 5.0\n */\npublic interface ClaimAccessor {\n\n\t/**\n\t * Returns a set of claims that may be used for assertions.\n\t * @return a {@code Map} of claims\n\t */\n\tMap<String, Object> getClaims();\n\n\t/**\n\t * Returns the claim value as a {@code T} type. The claim value is expected to be of\n\t * type {@code T}.\n\t * @param claim the name of the claim\n\t * @param <T> the type of the claim value\n\t * @return the claim value, or {@code null} if the claim does not exist\n\t * @since 5.2\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tdefault <T> @Nullable T getClaim(String claim) {\n\t\treturn !hasClaim(claim) ? null : (T) getClaims().get(claim);\n\t}\n\n\t/**\n\t * Returns {@code true} if the claim exists in {@link #getClaims()}, otherwise\n\t * {@code false}.\n\t * @param claim the name of the claim\n\t * @return {@code true} if the claim exists, otherwise {@code false}\n\t * @since 5.5\n\t */\n\tdefault boolean hasClaim(String claim) {\n\t\tAssert.notNull(claim, \"claim cannot be null\");\n\t\treturn getClaims().containsKey(claim);\n\t}\n\n\t/**\n\t * Returns the claim value as a {@code String} or {@code null} if it does not exist or\n\t * is equal to {@code null}.\n\t * @param claim the name of the claim\n\t * @return the claim value or {@code null} if it does not exist or is equal to\n\t * {@code null}\n\t */\n\tdefault @Nullable String getClaimAsString(String claim) {\n\t\treturn !hasClaim(claim) ? null\n\t\t\t\t: ClaimConversionService.getSharedInstance().convert(getClaims().get(claim), String.class);\n\t}\n\n\t/**\n\t * Returns the claim value as a {@code Boolean} or {@code null} if the claim does not\n\t * exist.\n\t * @param claim the name of the claim\n\t * @return the claim value or {@code null} if the claim does not exist\n\t * @throws IllegalArgumentException if the claim value cannot be converted to a\n\t * {@code Boolean}\n\t */\n\tdefault @Nullable Boolean getClaimAsBoolean(String claim) {\n\t\tif (!hasClaim(claim)) {\n\t\t\treturn null;\n\t\t}\n\t\tObject claimValue = getClaims().get(claim);\n\t\tif (claimValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\tBoolean convertedValue = ClaimConversionService.getSharedInstance().convert(claimValue, Boolean.class);\n\t\tAssert.notNull(convertedValue,\n\t\t\t\t() -> \"Unable to convert claim '\" + claim + \"' of type '\" + claimValue.getClass() + \"' to Boolean.\");\n\t\treturn convertedValue;\n\t}\n\n\t/**\n\t * Returns the claim value as an {@code Instant} or {@code null} if it does not exist.\n\t * @param claim the name of the claim\n\t * @return the claim value or {@code null} if it does not exist\n\t * @throws IllegalArgumentException if the claim value cannot be converted to an\n\t * {@code Instant}\n\t */\n\tdefault @Nullable Instant getClaimAsInstant(String claim) {\n\t\tif (!hasClaim(claim)) {\n\t\t\treturn null;\n\t\t}\n\t\tObject claimValue = getClaims().get(claim);\n\t\tif (claimValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\tInstant convertedValue = ClaimConversionService.getSharedInstance().convert(claimValue, Instant.class);\n\t\tAssert.notNull(convertedValue,\n\t\t\t\t() -> \"Unable to convert claim '\" + claim + \"' of type '\" + claimValue.getClass() + \"' to Instant.\");\n\t\treturn convertedValue;\n\t}\n\n\t/**\n\t * Returns the claim value as an {@code URL} or {@code null} if it does not exist.\n\t * @param claim the name of the claim\n\t * @return the claim value or {@code null} if it does not exist\n\t * @throws IllegalArgumentException if the claim value cannot be converted to a\n\t * {@code URL}\n\t */\n\tdefault @Nullable URL getClaimAsURL(String claim) {\n\t\tif (!hasClaim(claim)) {\n\t\t\treturn null;\n\t\t}\n\t\tObject claimValue = getClaims().get(claim);\n\t\tif (claimValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\tURL convertedValue = ClaimConversionService.getSharedInstance().convert(claimValue, URL.class);\n\t\tAssert.notNull(convertedValue,\n\t\t\t\t() -> \"Unable to convert claim '\" + claim + \"' of type '\" + claimValue.getClass() + \"' to URL.\");\n\t\treturn convertedValue;\n\t}\n\n\t/**\n\t * Returns the claim value as a {@code Map<String, Object>} or {@code null} if the\n\t * claim does not exist.\n\t * @param claim the name of the claim\n\t * @return the claim value or {@code null} if the claim does not exist\n\t * @throws IllegalArgumentException if the claim value cannot be converted to a\n\t * {@code Map}\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tdefault @Nullable Map<String, Object> getClaimAsMap(String claim) {\n\t\tif (!hasClaim(claim)) {\n\t\t\treturn null;\n\t\t}\n\t\tObject claimValue = getClaims().get(claim);\n\t\tif (claimValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\tfinal TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);\n\t\tfinal TypeDescriptor targetDescriptor = TypeDescriptor.map(Map.class, TypeDescriptor.valueOf(String.class),\n\t\t\t\tTypeDescriptor.valueOf(Object.class));\n\t\tMap<String, Object> convertedValue = (Map<String, Object>) ClaimConversionService.getSharedInstance()\n\t\t\t.convert(claimValue, sourceDescriptor, targetDescriptor);\n\t\tAssert.notNull(convertedValue,\n\t\t\t\t() -> \"Unable to convert claim '\" + claim + \"' of type '\" + claimValue.getClass() + \"' to Map.\");\n\t\treturn convertedValue;\n\t}\n\n\t/**\n\t * Returns the claim value as a {@code List<String>} or {@code null} if the claim does\n\t * not exist.\n\t * @param claim the name of the claim\n\t * @return the claim value or {@code null} if the claim does not exist\n\t * @throws IllegalArgumentException if the claim value cannot be converted to a\n\t * {@code List}\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tdefault @Nullable List<String> getClaimAsStringList(String claim) {\n\t\tif (!hasClaim(claim)) {\n\t\t\treturn null;\n\t\t}\n\t\tObject claimValue = getClaims().get(claim);\n\t\tif (claimValue == null) {\n\t\t\treturn null;\n\t\t}\n\t\tfinal TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);\n\t\tfinal TypeDescriptor targetDescriptor = TypeDescriptor.collection(List.class,\n\t\t\t\tTypeDescriptor.valueOf(String.class));\n\t\tList<String> convertedValue = (List<String>) ClaimConversionService.getSharedInstance()\n\t\t\t.convert(claimValue, sourceDescriptor, targetDescriptor);\n\t\tAssert.notNull(convertedValue,\n\t\t\t\t() -> \"Unable to convert claim '\" + claim + \"' of type '\" + claimValue.getClass() + \"' to List.\");\n\t\treturn convertedValue;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serializable;\n\nimport org.springframework.util.Assert;\n\n/**\n * The authentication method used when authenticating the client with the authorization\n * server.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-2.3\">Section\n * 2.3 Client Authentication</a>\n */\npublic final class ClientAuthenticationMethod implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\t/**\n\t * @since 5.5\n\t */\n\tpublic static final ClientAuthenticationMethod CLIENT_SECRET_BASIC = new ClientAuthenticationMethod(\n\t\t\t\"client_secret_basic\");\n\n\t/**\n\t * @since 5.5\n\t */\n\tpublic static final ClientAuthenticationMethod CLIENT_SECRET_POST = new ClientAuthenticationMethod(\n\t\t\t\"client_secret_post\");\n\n\t/**\n\t * @since 5.5\n\t */\n\tpublic static final ClientAuthenticationMethod CLIENT_SECRET_JWT = new ClientAuthenticationMethod(\n\t\t\t\"client_secret_jwt\");\n\n\t/**\n\t * @since 5.5\n\t */\n\tpublic static final ClientAuthenticationMethod PRIVATE_KEY_JWT = new ClientAuthenticationMethod(\"private_key_jwt\");\n\n\t/**\n\t * @since 5.2\n\t */\n\tpublic static final ClientAuthenticationMethod NONE = new ClientAuthenticationMethod(\"none\");\n\n\t/**\n\t * @since 6.3\n\t */\n\tpublic static final ClientAuthenticationMethod TLS_CLIENT_AUTH = new ClientAuthenticationMethod(\"tls_client_auth\");\n\n\t/**\n\t * @since 6.3\n\t */\n\tpublic static final ClientAuthenticationMethod SELF_SIGNED_TLS_CLIENT_AUTH = new ClientAuthenticationMethod(\n\t\t\t\"self_signed_tls_client_auth\");\n\n\tprivate final String value;\n\n\t/**\n\t * Constructs a {@code ClientAuthenticationMethod} using the provided value.\n\t * @param value the value of the client authentication method\n\t */\n\tpublic ClientAuthenticationMethod(String value) {\n\t\tAssert.hasText(value, \"value cannot be empty\");\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Returns the value of the client authentication method.\n\t * @return the value of the client authentication method\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\tstatic ClientAuthenticationMethod[] methods() {\n\t\treturn new ClientAuthenticationMethod[] { CLIENT_SECRET_BASIC, CLIENT_SECRET_POST, CLIENT_SECRET_JWT,\n\t\t\t\tPRIVATE_KEY_JWT, NONE, TLS_CLIENT_AUTH, SELF_SIGNED_TLS_CLIENT_AUTH };\n\t}\n\n\t/**\n\t * A factory to construct a {@link ClientAuthenticationMethod} based on a string,\n\t * returning any constant value that matches.\n\t * @param method the client authentication method\n\t * @return a {@link ClientAuthenticationMethod}; specifically the corresponding\n\t * constant, if any\n\t * @since 6.5\n\t */\n\tpublic static ClientAuthenticationMethod valueOf(String method) {\n\t\tfor (ClientAuthenticationMethod m : methods()) {\n\t\t\tif (m.getValue().equals(method)) {\n\t\t\t\treturn m;\n\t\t\t}\n\t\t}\n\t\treturn new ClientAuthenticationMethod(method);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tClientAuthenticationMethod that = (ClientAuthenticationMethod) obj;\n\t\treturn getValue().equals(that.getValue());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn getValue().hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.value;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/DefaultOAuth2AuthenticatedPrincipal.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.util.Assert;\n\n/**\n * A domain object that wraps the attributes of an OAuth 2.0 token.\n *\n * @author Clement Ng\n * @author Josh Cummings\n * @since 5.2\n */\npublic final class DefaultOAuth2AuthenticatedPrincipal implements OAuth2AuthenticatedPrincipal, Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 4631662622577433065L;\n\n\tprivate final Map<String, Object> attributes;\n\n\tprivate final Collection<GrantedAuthority> authorities;\n\n\tprivate final String name;\n\n\t/**\n\t * Constructs an {@code DefaultOAuth2AuthenticatedPrincipal} using the provided\n\t * parameters.\n\t * @param attributes the attributes of the OAuth 2.0 token\n\t * @param authorities the authorities of the OAuth 2.0 token\n\t */\n\tpublic DefaultOAuth2AuthenticatedPrincipal(Map<String, Object> attributes,\n\t\t\tCollection<GrantedAuthority> authorities) {\n\t\tthis(null, attributes, authorities);\n\t}\n\n\t/**\n\t * Constructs an {@code DefaultOAuth2AuthenticatedPrincipal} using the provided\n\t * parameters.\n\t * @param name the name attached to the OAuth 2.0 token, may be {@code null}\n\t * @param attributes the attributes of the OAuth 2.0 token\n\t * @param authorities the authorities of the OAuth 2.0 token, may be {@code null}\n\t */\n\tpublic DefaultOAuth2AuthenticatedPrincipal(@Nullable String name, Map<String, Object> attributes,\n\t\t\t@Nullable Collection<GrantedAuthority> authorities) {\n\t\tAssert.notEmpty(attributes, \"attributes cannot be empty\");\n\t\tthis.attributes = Collections.unmodifiableMap(attributes);\n\t\tthis.authorities = (authorities != null) ? Collections.unmodifiableCollection(authorities)\n\t\t\t\t: AuthorityUtils.NO_AUTHORITIES;\n\t\t// Ensure name is never null - use 'sub' attribute as fallback, then empty string\n\t\t// This satisfies AuthenticatedPrincipal.getName() contract which never returns\n\t\t// null\n\t\tString resolvedName = (name != null) ? name : (String) this.attributes.get(\"sub\");\n\t\tthis.name = (resolvedName != null) ? resolvedName : \"\";\n\t}\n\n\t/**\n\t * Gets the attributes of the OAuth 2.0 token in map form.\n\t * @return a {@link Map} of the attribute's objects keyed by the attribute's names\n\t */\n\t@Override\n\tpublic Map<String, Object> getAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/DelegatingOAuth2TokenValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport org.springframework.util.Assert;\n\n/**\n * A composite validator\n *\n * @param <T> the type of {@link OAuth2Token} this validator validates\n * @author Josh Cummings\n * @since 5.1\n */\npublic final class DelegatingOAuth2TokenValidator<T extends OAuth2Token> implements OAuth2TokenValidator<T> {\n\n\tprivate final Collection<OAuth2TokenValidator<T>> tokenValidators;\n\n\t/**\n\t * Constructs a {@code DelegatingOAuth2TokenValidator} using the provided validators.\n\t * @param tokenValidators the {@link Collection} of {@link OAuth2TokenValidator}s to\n\t * use\n\t */\n\tpublic DelegatingOAuth2TokenValidator(Collection<OAuth2TokenValidator<T>> tokenValidators) {\n\t\tAssert.notNull(tokenValidators, \"tokenValidators cannot be null\");\n\t\tthis.tokenValidators = new ArrayList<>(tokenValidators);\n\t}\n\n\t/**\n\t * Constructs a {@code DelegatingOAuth2TokenValidator} using the provided validators.\n\t * @param tokenValidators the collection of {@link OAuth2TokenValidator}s to use\n\t */\n\t@SafeVarargs\n\tpublic DelegatingOAuth2TokenValidator(OAuth2TokenValidator<T>... tokenValidators) {\n\t\tthis(Arrays.asList(tokenValidators));\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(T token) {\n\t\tCollection<OAuth2Error> errors = new ArrayList<>();\n\t\tfor (OAuth2TokenValidator<T> validator : this.tokenValidators) {\n\t\t\terrors.addAll(validator.validate(token).getErrors());\n\t\t}\n\t\treturn OAuth2TokenValidatorResult.failure(errors);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2AccessToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AbstractOAuth2Token} representing an OAuth 2.0 Access\n * Token.\n *\n * <p>\n * An access token is a credential that represents an authorization granted by the\n * resource owner to the client. It is primarily used by the client to access protected\n * resources on either a resource server or the authorization server that originally\n * issued the access token.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.4\">Section\n * 1.4 Access Token</a>\n */\npublic class OAuth2AccessToken extends AbstractOAuth2Token {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3041884478533441940L;\n\n\tprivate final TokenType tokenType;\n\n\tprivate final Set<String> scopes;\n\n\t/**\n\t * Constructs an {@code OAuth2AccessToken} using the provided parameters.\n\t * @param tokenType the token type\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the token was issued, may be {@code null}\n\t * @param expiresAt the expiration time on or after which the token MUST NOT be\n\t * accepted, may be {@code null}\n\t */\n\tpublic OAuth2AccessToken(TokenType tokenType, String tokenValue, @Nullable Instant issuedAt,\n\t\t\t@Nullable Instant expiresAt) {\n\t\tthis(tokenType, tokenValue, issuedAt, expiresAt, Collections.emptySet());\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AccessToken} using the provided parameters.\n\t * @param tokenType the token type\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the token was issued, may be {@code null}\n\t * @param expiresAt the expiration time on or after which the token MUST NOT be\n\t * accepted, may be {@code null}\n\t * @param scopes the scope(s) associated to the token\n\t */\n\tpublic OAuth2AccessToken(TokenType tokenType, String tokenValue, @Nullable Instant issuedAt,\n\t\t\t@Nullable Instant expiresAt, Set<String> scopes) {\n\t\tsuper(tokenValue, issuedAt, expiresAt);\n\t\tAssert.notNull(tokenType, \"tokenType cannot be null\");\n\t\tthis.tokenType = tokenType;\n\t\tthis.scopes = Collections.unmodifiableSet((scopes != null) ? scopes : Collections.emptySet());\n\t}\n\n\t/**\n\t * Returns the {@link TokenType token type}.\n\t * @return the {@link TokenType}\n\t */\n\tpublic TokenType getTokenType() {\n\t\treturn this.tokenType;\n\t}\n\n\t/**\n\t * Returns the scope(s) associated to the token.\n\t * @return the scope(s) associated to the token\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n\t/**\n\t * Access Token Types.\n\t *\n\t * @see <a target=\"_blank\" href=\n\t * \"https://tools.ietf.org/html/rfc6749#section-7.1\">Section 7.1 Access Token\n\t * Types</a>\n\t */\n\tpublic static final class TokenType implements Serializable {\n\n\t\tprivate static final long serialVersionUID = 620L;\n\n\t\tpublic static final TokenType BEARER = new TokenType(\"Bearer\");\n\n\t\t/**\n\t\t * @since 6.5\n\t\t */\n\t\tpublic static final TokenType DPOP = new TokenType(\"DPoP\");\n\n\t\tprivate final String value;\n\n\t\t/**\n\t\t * Constructs a {@code TokenType} using the provided value.\n\t\t * @param value the value of the token type\n\t\t * @since 6.5\n\t\t */\n\t\tpublic TokenType(String value) {\n\t\t\tAssert.hasText(value, \"value cannot be empty\");\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t/**\n\t\t * Returns the value of the token type.\n\t\t * @return the value of the token type\n\t\t */\n\t\tpublic String getValue() {\n\t\t\treturn this.value;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (this == obj) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tTokenType that = (TokenType) obj;\n\t\t\treturn this.getValue().equalsIgnoreCase(that.getValue());\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn this.getValue().hashCode();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2AuthenticatedPrincipal.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * An {@link AuthenticatedPrincipal} that represents the principal associated with an\n * OAuth 2.0 token.\n *\n * @author Josh Cummings\n * @since 5.2\n */\npublic interface OAuth2AuthenticatedPrincipal extends AuthenticatedPrincipal {\n\n\t/**\n\t * Get the OAuth 2.0 token attribute by name\n\t * @param name the name of the attribute\n\t * @param <A> the type of the attribute\n\t * @return the attribute or {@code null} otherwise\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tdefault <A> @Nullable A getAttribute(String name) {\n\t\treturn (A) getAttributes().get(name);\n\t}\n\n\t/**\n\t * Get the OAuth 2.0 token attributes\n\t * @return the OAuth 2.0 token attributes\n\t */\n\tMap<String, Object> getAttributes();\n\n\t/**\n\t * Get the {@link Collection} of {@link GrantedAuthority}s associated with this OAuth\n\t * 2.0 token\n\t * @return the OAuth 2.0 token authorities\n\t */\n\tCollection<? extends GrantedAuthority> getAuthorities();\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2AuthenticationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.util.Assert;\n\n/**\n * This exception is thrown for all OAuth 2.0 related {@link Authentication} errors.\n *\n * <p>\n * There are a number of scenarios where an error may occur, for example:\n * <ul>\n * <li>The authorization request or token request is missing a required parameter</li>\n * <li>Missing or invalid client identifier</li>\n * <li>Invalid or mismatching redirection URI</li>\n * <li>The requested scope is invalid, unknown, or malformed</li>\n * <li>The resource owner or authorization server denied the access request</li>\n * <li>Client authentication failed</li>\n * <li>The provided authorization grant (authorization code, resource owner credentials)\n * is invalid, expired, or revoked</li>\n * </ul>\n *\n * @author Joe Grandja\n * @since 5.0\n */\npublic class OAuth2AuthenticationException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7832130893085581438L;\n\n\tprivate final OAuth2Error error;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthenticationException} using the provided parameters.\n\t * @param errorCode the {@link OAuth2ErrorCodes OAuth 2.0 Error Code}\n\t * @since 5.5\n\t */\n\tpublic OAuth2AuthenticationException(String errorCode) {\n\t\tthis(new OAuth2Error(errorCode));\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthenticationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t */\n\tpublic OAuth2AuthenticationException(OAuth2Error error) {\n\t\tthis(error, error.getDescription());\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthenticationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param cause the root cause\n\t */\n\tpublic OAuth2AuthenticationException(OAuth2Error error, Throwable cause) {\n\t\tthis(error, cause.getMessage(), cause);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthenticationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param message the detail message, may be {@code null}\n\t */\n\tpublic OAuth2AuthenticationException(OAuth2Error error, @Nullable String message) {\n\t\tthis(error, message, null);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthenticationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param message the detail message, may be {@code null}\n\t * @param cause the root cause, may be {@code null}\n\t */\n\tpublic OAuth2AuthenticationException(OAuth2Error error, @Nullable String message, @Nullable Throwable cause) {\n\t\tsuper(message);\n\t\tAssert.notNull(error, \"error cannot be null\");\n\t\tthis.error = error;\n\t\tif (cause != null) {\n\t\t\tinitCause(cause);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Error OAuth 2.0 Error}.\n\t * @return the {@link OAuth2Error}\n\t */\n\tpublic OAuth2Error getError() {\n\t\treturn this.error;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2AuthorizationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serial;\n\nimport org.springframework.util.Assert;\n\n/**\n * Base exception for OAuth 2.0 Authorization errors.\n *\n * @author Joe Grandja\n * @since 5.1\n */\npublic class OAuth2AuthorizationException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5470222190376181102L;\n\n\tprivate final OAuth2Error error;\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t */\n\tpublic OAuth2AuthorizationException(OAuth2Error error) {\n\t\tthis(error, error.toString());\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param message the exception message\n\t * @since 5.3\n\t */\n\tpublic OAuth2AuthorizationException(OAuth2Error error, String message) {\n\t\tsuper(message);\n\t\tAssert.notNull(error, \"error must not be null\");\n\t\tthis.error = error;\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param cause the root cause\n\t */\n\tpublic OAuth2AuthorizationException(OAuth2Error error, Throwable cause) {\n\t\tthis(error, error.toString(), cause);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2AuthorizationException} using the provided parameters.\n\t * @param error the {@link OAuth2Error OAuth 2.0 Error}\n\t * @param message the exception message\n\t * @param cause the root cause\n\t * @since 5.3\n\t */\n\tpublic OAuth2AuthorizationException(OAuth2Error error, String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t\tAssert.notNull(error, \"error must not be null\");\n\t\tthis.error = error;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Error OAuth 2.0 Error}.\n\t * @return the {@link OAuth2Error}\n\t */\n\tpublic OAuth2Error getError() {\n\t\treturn this.error;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2DeviceCode.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serial;\nimport java.time.Instant;\n\n/**\n * An implementation of an {@link AbstractOAuth2Token} representing a device code as part\n * of the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 6.1\n * @see OAuth2UserCode\n * @see <a target=\"_blank\" href= \"https://tools.ietf.org/html/rfc8628#section-3.2\">Section\n * 3.2 Device Authorization Response</a>\n */\npublic class OAuth2DeviceCode extends AbstractOAuth2Token {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -864134962034523562L;\n\n\t/**\n\t * Constructs an {@code OAuth2DeviceCode} using the provided parameters.\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the token was issued\n\t * @param expiresAt the time at which the token expires\n\t */\n\tpublic OAuth2DeviceCode(String tokenValue, Instant issuedAt, Instant expiresAt) {\n\t\tsuper(tokenValue, issuedAt, expiresAt);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2Error.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A representation of an OAuth 2.0 Error.\n *\n * <p>\n * At a minimum, an error response will contain an error code. The error code may be one\n * of the standard codes defined by the specification, or a new code defined in the OAuth\n * Extensions Error Registry, for cases where protocol extensions require additional error\n * code(s) above the standard codes.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2ErrorCodes\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-11.4\">Section\n * 11.4 OAuth Extensions Error Registry</a>\n */\npublic class OAuth2Error implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String errorCode;\n\n\tprivate final @Nullable String description;\n\n\tprivate final @Nullable String uri;\n\n\t/**\n\t * Constructs an {@code OAuth2Error} using the provided parameters.\n\t * @param errorCode the error code\n\t */\n\tpublic OAuth2Error(String errorCode) {\n\t\tthis(errorCode, null, null);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2Error} using the provided parameters.\n\t * @param errorCode the error code\n\t * @param description the error description, may be {@code null}\n\t * @param uri the error uri, may be {@code null}\n\t */\n\tpublic OAuth2Error(String errorCode, @Nullable String description, @Nullable String uri) {\n\t\tAssert.hasText(errorCode, \"errorCode cannot be empty\");\n\t\tthis.errorCode = errorCode;\n\t\tthis.description = description;\n\t\tthis.uri = uri;\n\t}\n\n\t/**\n\t * Returns the error code.\n\t * @return the error code\n\t */\n\tpublic final String getErrorCode() {\n\t\treturn this.errorCode;\n\t}\n\n\t/**\n\t * Returns the error description.\n\t * @return the error description, or {@code null} if not available\n\t */\n\tpublic final @Nullable String getDescription() {\n\t\treturn this.description;\n\t}\n\n\t/**\n\t * Returns the error uri.\n\t * @return the error uri, or {@code null} if not available\n\t */\n\tpublic final @Nullable String getUri() {\n\t\treturn this.uri;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"[\" + this.getErrorCode() + \"] \" + ((this.getDescription() != null) ? this.getDescription() : \"\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2ErrorCodes.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\n/**\n * Standard error codes defined by the OAuth 2.0 Authorization Framework.\n *\n * @author Joe Grandja\n * @since 5.0\n */\npublic final class OAuth2ErrorCodes {\n\n\t/**\n\t * {@code invalid_request} - The request is missing a required parameter, includes an\n\t * invalid parameter value, includes a parameter more than once, or is otherwise\n\t * malformed.\n\t */\n\tpublic static final String INVALID_REQUEST = \"invalid_request\";\n\n\t/**\n\t * {@code unauthorized_client} - The client is not authorized to request an\n\t * authorization code or access token using this method.\n\t */\n\tpublic static final String UNAUTHORIZED_CLIENT = \"unauthorized_client\";\n\n\t/**\n\t * {@code access_denied} - The resource owner or authorization server denied the\n\t * request.\n\t */\n\tpublic static final String ACCESS_DENIED = \"access_denied\";\n\n\t/**\n\t * {@code unsupported_response_type} - The authorization server does not support\n\t * obtaining an authorization code or access token using this method.\n\t */\n\tpublic static final String UNSUPPORTED_RESPONSE_TYPE = \"unsupported_response_type\";\n\n\t/**\n\t * {@code invalid_scope} - The requested scope is invalid, unknown, malformed or\n\t * exceeds the scope granted by the resource owner.\n\t */\n\tpublic static final String INVALID_SCOPE = \"invalid_scope\";\n\n\t/**\n\t * {@code insufficient_scope} - The request requires higher privileges than provided\n\t * by the access token. The resource server SHOULD respond with the HTTP 403\n\t * (Forbidden) status code and MAY include the \"scope\" attribute with the scope\n\t * necessary to access the protected resource.\n\t *\n\t * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-3.1\">RFC-6750 - Section\n\t * 3.1 - Error Codes</a>\n\t */\n\tpublic static final String INSUFFICIENT_SCOPE = \"insufficient_scope\";\n\n\t/**\n\t * {@code invalid_token} - The access token provided is expired, revoked, malformed,\n\t * or invalid for other reasons. The resource SHOULD respond with the HTTP 401\n\t * (Unauthorized) status code. The client MAY request a new access token and retry the\n\t * protected resource request.\n\t *\n\t * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-3.1\">RFC-6750 - Section\n\t * 3.1 - Error Codes</a>\n\t */\n\tpublic static final String INVALID_TOKEN = \"invalid_token\";\n\n\t/**\n\t * {@code server_error} - The authorization server encountered an unexpected condition\n\t * that prevented it from fulfilling the request. (This error code is needed because a\n\t * 500 Internal Server Error HTTP status code cannot be returned to the client via a\n\t * HTTP redirect.)\n\t */\n\tpublic static final String SERVER_ERROR = \"server_error\";\n\n\t/**\n\t * {@code temporarily_unavailable} - The authorization server is currently unable to\n\t * handle the request due to a temporary overloading or maintenance of the server.\n\t * (This error code is needed because a 503 Service Unavailable HTTP status code\n\t * cannot be returned to the client via an HTTP redirect.)\n\t */\n\tpublic static final String TEMPORARILY_UNAVAILABLE = \"temporarily_unavailable\";\n\n\t/**\n\t * {@code invalid_client} - Client authentication failed (e.g., unknown client, no\n\t * client authentication included, or unsupported authentication method). The\n\t * authorization server MAY return a HTTP 401 (Unauthorized) status code to indicate\n\t * which HTTP authentication schemes are supported. If the client attempted to\n\t * authenticate via the &quot;Authorization&quot; request header field, the\n\t * authorization server MUST respond with a HTTP 401 (Unauthorized) status code and\n\t * include the &quot;WWW-Authenticate&quot; response header field matching the\n\t * authentication scheme used by the client.\n\t */\n\tpublic static final String INVALID_CLIENT = \"invalid_client\";\n\n\t/**\n\t * {@code invalid_grant} - The provided authorization grant (e.g., authorization code,\n\t * resource owner credentials) or refresh token is invalid, expired, revoked, does not\n\t * match the redirection URI used in the authorization request, or was issued to\n\t * another client.\n\t */\n\tpublic static final String INVALID_GRANT = \"invalid_grant\";\n\n\t/**\n\t * {@code unsupported_grant_type} - The authorization grant type is not supported by\n\t * the authorization server.\n\t */\n\tpublic static final String UNSUPPORTED_GRANT_TYPE = \"unsupported_grant_type\";\n\n\t/**\n\t * {@code unsupported_token_type} - The authorization server does not support the\n\t * revocation of the presented token type.\n\t *\n\t * @since 5.5\n\t * @see <a href=\"https://tools.ietf.org/html/rfc7009#section-2.2.1\">RFC-7009 - Section\n\t * 2.2.1 - Error Response</a>\n\t */\n\tpublic static final String UNSUPPORTED_TOKEN_TYPE = \"unsupported_token_type\";\n\n\t/**\n\t * {@code invalid_redirect_uri} - The value of one or more redirection URIs is\n\t * invalid.\n\t *\n\t * @since 5.6\n\t * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2\">RFC-7591\n\t * - Section 3.2.2 - Client Registration Error Response</a>\n\t */\n\tpublic static final String INVALID_REDIRECT_URI = \"invalid_redirect_uri\";\n\n\t/**\n\t * {@code invalid_dpop_proof} - The DPoP Proof JWT is invalid.\n\t *\n\t * @since 6.5\n\t * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc9449\">RFC-9449 - OAuth 2.0\n\t * Demonstrating Proof of Possession (DPoP)</a>\n\t */\n\tpublic static final String INVALID_DPOP_PROOF = \"invalid_dpop_proof\";\n\n\tprivate OAuth2ErrorCodes() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2RefreshToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serial;\nimport java.time.Instant;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An implementation of an {@link AbstractOAuth2Token} representing an OAuth 2.0 Refresh\n * Token.\n *\n * <p>\n * A refresh token is a credential that represents an authorization granted by the\n * resource owner to the client. It is used by the client to obtain a new access token\n * when the current access token becomes invalid or expires, or to obtain additional\n * access tokens with identical or narrower scope.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AccessToken\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-1.5\">Section\n * 1.5 Refresh Token</a>\n */\npublic class OAuth2RefreshToken extends AbstractOAuth2Token {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -4114856398229602435L;\n\n\t/**\n\t * Constructs an {@code OAuth2RefreshToken} using the provided parameters.\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the token was issued, may be {@code null}\n\t */\n\tpublic OAuth2RefreshToken(String tokenValue, @Nullable Instant issuedAt) {\n\t\tthis(tokenValue, issuedAt, null);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2RefreshToken} using the provided parameters.\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the token was issued, may be {@code null}\n\t * @param expiresAt the time at which the token expires, may be {@code null}\n\t * @since 5.5\n\t */\n\tpublic OAuth2RefreshToken(String tokenValue, @Nullable Instant issuedAt, @Nullable Instant expiresAt) {\n\t\tsuper(tokenValue, issuedAt, expiresAt);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2Token.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.time.Instant;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Core interface representing an OAuth 2.0 Token.\n *\n * @author Joe Grandja\n * @since 5.5\n * @see AbstractOAuth2Token\n */\npublic interface OAuth2Token {\n\n\t/**\n\t * Returns the token value.\n\t * @return the token value\n\t */\n\tString getTokenValue();\n\n\t/**\n\t * Returns the time at which the token was issued.\n\t * @return the time the token was issued or {@code null}\n\t */\n\tdefault @Nullable Instant getIssuedAt() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the expiration time on or after which the token MUST NOT be accepted.\n\t * @return the token expiration time or {@code null}\n\t */\n\tdefault @Nullable Instant getExpiresAt() {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2TokenIntrospectionClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A {@link ClaimAccessor} for the &quot;claims&quot; that may be contained in the\n * Introspection Response.\n *\n * @author David Kovac\n * @since 5.6\n * @see ClaimAccessor\n * @see OAuth2TokenIntrospectionClaimNames\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc7662#section-2.2\">Introspection Response</a>\n */\npublic interface OAuth2TokenIntrospectionClaimAccessor extends ClaimAccessor {\n\n\t/**\n\t * Returns the indicator {@code (active)} whether or not the token is currently active\n\t * @return the indicator whether or not the token is currently active\n\t */\n\tdefault boolean isActive() {\n\t\treturn Boolean.TRUE.equals(getClaimAsBoolean(OAuth2TokenIntrospectionClaimNames.ACTIVE));\n\t}\n\n\t/**\n\t * Returns a human-readable identifier {@code (username)} for the resource owner that\n\t * authorized the token, or {@code null} if it does not exist.\n\t * @return a human-readable identifier for the resource owner that authorized the\n\t * token, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getUsername() {\n\t\treturn getClaimAsString(OAuth2TokenIntrospectionClaimNames.USERNAME);\n\t}\n\n\t/**\n\t * Returns the client identifier {@code (client_id)} for the token, or {@code null} if\n\t * it does not exist.\n\t * @return the client identifier for the token, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getClientId() {\n\t\treturn getClaimAsString(OAuth2TokenIntrospectionClaimNames.CLIENT_ID);\n\t}\n\n\t/**\n\t * Returns the scopes {@code (scope)} associated with the token, or {@code null} if it\n\t * does not exist.\n\t * @return the scopes associated with the token, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable List<String> getScopes() {\n\t\treturn getClaimAsStringList(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t}\n\n\t/**\n\t * Returns the type of the token {@code (token_type)}, for example {@code bearer}, or\n\t * {@code null} if it does not exist.\n\t * @return the type of the token, for example {@code bearer}, or {@code null} if it\n\t * does not exist\n\t */\n\tdefault @Nullable String getTokenType() {\n\t\treturn getClaimAsString(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE);\n\t}\n\n\t/**\n\t * Returns a timestamp {@code (exp)} indicating when the token expires, or\n\t * {@code null} if it does not exist.\n\t * @return a timestamp indicating when the token expires, or {@code null} if it does\n\t * not exist\n\t */\n\tdefault @Nullable Instant getExpiresAt() {\n\t\treturn getClaimAsInstant(OAuth2TokenIntrospectionClaimNames.EXP);\n\t}\n\n\t/**\n\t * Returns a timestamp {@code (iat)} indicating when the token was issued, or\n\t * {@code null} if it does not exist.\n\t * @return a timestamp indicating when the token was issued, or {@code null} if it\n\t * does not exist\n\t */\n\tdefault @Nullable Instant getIssuedAt() {\n\t\treturn getClaimAsInstant(OAuth2TokenIntrospectionClaimNames.IAT);\n\t}\n\n\t/**\n\t * Returns a timestamp {@code (nbf)} indicating when the token is not to be used\n\t * before, or {@code null} if it does not exist.\n\t * @return a timestamp indicating when the token is not to be used before, or\n\t * {@code null} if it does not exist\n\t */\n\tdefault @Nullable Instant getNotBefore() {\n\t\treturn getClaimAsInstant(OAuth2TokenIntrospectionClaimNames.NBF);\n\t}\n\n\t/**\n\t * Returns usually a machine-readable identifier {@code (sub)} of the resource owner\n\t * who authorized the token, or {@code null} if it does not exist.\n\t * @return usually a machine-readable identifier of the resource owner who authorized\n\t * the token, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getSubject() {\n\t\treturn getClaimAsString(OAuth2TokenIntrospectionClaimNames.SUB);\n\t}\n\n\t/**\n\t * Returns the intended audience {@code (aud)} for the token, or {@code null} if it\n\t * does not exist.\n\t * @return the intended audience for the token, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable List<String> getAudience() {\n\t\treturn getClaimAsStringList(OAuth2TokenIntrospectionClaimNames.AUD);\n\t}\n\n\t/**\n\t * Returns the issuer {@code (iss)} of the token, or {@code null} if it does not\n\t * exist.\n\t * @return the issuer of the token, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable URL getIssuer() {\n\t\treturn getClaimAsURL(OAuth2TokenIntrospectionClaimNames.ISS);\n\t}\n\n\t/**\n\t * Returns the identifier {@code (jti)} for the token, or {@code null} if it does not\n\t * exist.\n\t * @return the identifier for the token, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getId() {\n\t\treturn getClaimAsString(OAuth2TokenIntrospectionClaimNames.JTI);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2TokenIntrospectionClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\n/**\n * The names of the &quot;Introspection Claims&quot; defined by an\n * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7662#section-2.2\">Introspection\n * Response</a>.\n *\n * @author Josh Cummings\n * @since 5.6\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7662#section-2.2\">OAuth\n * 2.0 Token Introspection (RFC7662)</a>\n * @see <a target=\"_blank\" href=\n * \"https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#token-introspection-response\">OAuth\n * Parameters (IANA)</a>\n */\npublic final class OAuth2TokenIntrospectionClaimNames {\n\n\t/**\n\t * {@code active} - Indicator whether or not the token is currently active\n\t */\n\tpublic static final String ACTIVE = \"active\";\n\n\t/**\n\t * {@code username} - A human-readable identifier for the resource owner that\n\t * authorized the token\n\t */\n\tpublic static final String USERNAME = \"username\";\n\n\t/**\n\t * {@code client_id} - The Client identifier for the token\n\t */\n\tpublic static final String CLIENT_ID = \"client_id\";\n\n\t/**\n\t * {@code scope} - The scopes for the token\n\t */\n\tpublic static final String SCOPE = \"scope\";\n\n\t/**\n\t * {@code token_type} - The type of the token, for example {@code bearer}.\n\t */\n\tpublic static final String TOKEN_TYPE = \"token_type\";\n\n\t/**\n\t * {@code exp} - A timestamp indicating when the token expires\n\t */\n\tpublic static final String EXP = \"exp\";\n\n\t/**\n\t * {@code iat} - A timestamp indicating when the token was issued\n\t */\n\tpublic static final String IAT = \"iat\";\n\n\t/**\n\t * {@code nbf} - A timestamp indicating when the token is not to be used before\n\t */\n\tpublic static final String NBF = \"nbf\";\n\n\t/**\n\t * {@code sub} - Usually a machine-readable identifier of the resource owner who\n\t * authorized the token\n\t */\n\tpublic static final String SUB = \"sub\";\n\n\t/**\n\t * {@code aud} - The intended audience for the token\n\t */\n\tpublic static final String AUD = \"aud\";\n\n\t/**\n\t * {@code iss} - The issuer of the token\n\t */\n\tpublic static final String ISS = \"iss\";\n\n\t/**\n\t * {@code jti} - The identifier for the token\n\t */\n\tpublic static final String JTI = \"jti\";\n\n\tprivate OAuth2TokenIntrospectionClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2TokenValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\n/**\n * Implementations of this interface are responsible for &quot;verifying&quot; the\n * validity and/or constraints of the attributes contained in an OAuth 2.0 Token.\n *\n * @author Joe Grandja\n * @author Josh Cummings\n * @since 5.1\n */\n@FunctionalInterface\npublic interface OAuth2TokenValidator<T extends OAuth2Token> {\n\n\t/**\n\t * Verify the validity and/or constraints of the provided OAuth 2.0 Token.\n\t * @param token an OAuth 2.0 token\n\t * @return OAuth2TokenValidationResult the success or failure detail of the validation\n\t */\n\tOAuth2TokenValidatorResult validate(T token);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2TokenValidatorResult.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.springframework.util.Assert;\n\n/**\n * A result emitted from an {@link OAuth2TokenValidator} validation attempt\n *\n * @author Josh Cummings\n * @since 5.1\n */\npublic final class OAuth2TokenValidatorResult {\n\n\tstatic final OAuth2TokenValidatorResult NO_ERRORS = new OAuth2TokenValidatorResult(Collections.emptyList());\n\n\tprivate final Collection<OAuth2Error> errors;\n\n\tprivate OAuth2TokenValidatorResult(Collection<OAuth2Error> errors) {\n\t\tAssert.notNull(errors, \"errors cannot be null\");\n\t\tthis.errors = new ArrayList<>(errors);\n\t}\n\n\t/**\n\t * Say whether this result indicates success\n\t * @return whether this result has errors\n\t */\n\tpublic boolean hasErrors() {\n\t\treturn !this.errors.isEmpty();\n\t}\n\n\t/**\n\t * Return error details regarding the validation attempt\n\t * @return the collection of results in this result, if any; returns an empty list\n\t * otherwise\n\t */\n\tpublic Collection<OAuth2Error> getErrors() {\n\t\treturn this.errors;\n\t}\n\n\t/**\n\t * Construct a successful {@link OAuth2TokenValidatorResult}\n\t * @return an {@link OAuth2TokenValidatorResult} with no errors\n\t */\n\tpublic static OAuth2TokenValidatorResult success() {\n\t\treturn NO_ERRORS;\n\t}\n\n\t/**\n\t * Construct a failure {@link OAuth2TokenValidatorResult} with the provided detail\n\t * @param errors the list of errors\n\t * @return an {@link OAuth2TokenValidatorResult} with the errors specified\n\t */\n\tpublic static OAuth2TokenValidatorResult failure(OAuth2Error... errors) {\n\t\treturn failure(Arrays.asList(errors));\n\t}\n\n\t/**\n\t * Construct a failure {@link OAuth2TokenValidatorResult} with the provided detail\n\t * @param errors the list of errors\n\t * @return an {@link OAuth2TokenValidatorResult} with the errors specified\n\t */\n\tpublic static OAuth2TokenValidatorResult failure(Collection<OAuth2Error> errors) {\n\t\treturn (errors.isEmpty()) ? NO_ERRORS : new OAuth2TokenValidatorResult(errors);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2UserCode.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.Serial;\nimport java.time.Instant;\n\n/**\n * An implementation of an {@link AbstractOAuth2Token} representing a user code as part of\n * the OAuth 2.0 Device Authorization Grant.\n *\n * @author Steve Riesenberg\n * @since 6.1\n * @see OAuth2DeviceCode\n * @see <a target=\"_blank\" href= \"https://tools.ietf.org/html/rfc8628#section-3.2\">Section\n * 3.2 Device Authorization Response</a>\n */\npublic class OAuth2UserCode extends AbstractOAuth2Token {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3948612521903348476L;\n\n\t/**\n\t * Constructs an {@code OAuth2UserCode} using the provided parameters.\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the token was issued\n\t * @param expiresAt the time at which the token expires\n\t */\n\tpublic OAuth2UserCode(String tokenValue, Instant issuedAt, Instant expiresAt) {\n\t\tsuper(tokenValue, issuedAt, expiresAt);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/DefaultOAuth2AuthorizationManagerFactory.java",
    "content": "/*\n * Copyright 2025-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.authorization;\n\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.authorization.DefaultAuthorizationManagerFactory;\nimport org.springframework.util.Assert;\n\n/**\n * A factory for creating different kinds of {@link AuthorizationManager} instances.\n *\n * @param <T> the type of object that the authorization check is being done on\n * @author Ngoc Nhan\n * @since 7.1\n */\npublic final class DefaultOAuth2AuthorizationManagerFactory<T> implements OAuth2AuthorizationManagerFactory<T> {\n\n\tprivate String scopePrefix = \"SCOPE_\";\n\n\tprivate final AuthorizationManagerFactory<T> authorizationManagerFactory;\n\n\tpublic DefaultOAuth2AuthorizationManagerFactory() {\n\t\tthis(new DefaultAuthorizationManagerFactory<>());\n\t}\n\n\tpublic DefaultOAuth2AuthorizationManagerFactory(AuthorizationManagerFactory<T> authorizationManagerFactory) {\n\t\tAssert.notNull(authorizationManagerFactory, \"authorizationManagerFactory can not be null\");\n\t\tthis.authorizationManagerFactory = authorizationManagerFactory;\n\t}\n\n\t/**\n\t * Sets the prefix used to create an authority name from a scope name. Can be an empty\n\t * string.\n\t * @param scopePrefix the scope prefix to use\n\t */\n\tpublic void setScopePrefix(String scopePrefix) {\n\t\tAssert.notNull(scopePrefix, \"scopePrefix can not be null\");\n\t\tthis.scopePrefix = scopePrefix;\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasScope(String scope) {\n\t\tAssert.notNull(scope, \"scope can not be null\");\n\t\tassertScope(scope);\n\t\treturn this.authorizationManagerFactory.hasAuthority(this.scopePrefix + scope);\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasAnyScope(String... scopes) {\n\t\treturn this.authorizationManagerFactory.hasAnyAuthority(this.mappedScopes(scopes));\n\t}\n\n\t@Override\n\tpublic AuthorizationManager<T> hasAllScopes(String... scopes) {\n\t\treturn this.authorizationManagerFactory.hasAllAuthorities(this.mappedScopes(scopes));\n\t}\n\n\tprivate String[] mappedScopes(String... scopes) {\n\t\tAssert.notNull(scopes, \"scopes can not be null\");\n\t\tString[] mappedScopes = new String[scopes.length];\n\t\tfor (int i = 0; i < scopes.length; i++) {\n\t\t\tassertScope(scopes[i]);\n\t\t\tmappedScopes[i] = this.scopePrefix + scopes[i];\n\t\t}\n\t\treturn mappedScopes;\n\t}\n\n\tprivate void assertScope(String scope) {\n\t\tAssert.isTrue(!scope.startsWith(this.scopePrefix), () -> scope + \" should not start with '\" + this.scopePrefix\n\t\t\t\t+ \"' since '\" + this.scopePrefix\n\t\t\t\t+ \"' is automatically prepended when using hasScope and hasAnyScope. Consider using AuthorizationManagerFactory#hasAuthority or #hasAnyAuthority instead.\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagerFactory.java",
    "content": "/*\n * Copyright 2025-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.authorization;\n\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\n/**\n * A factory for creating different kinds of {@link AuthorizationManager} instances.\n *\n * @param <T> the type of object that the authorization check is being done on\n * @author Ngoc Nhan\n * @since 7.1\n */\npublic interface OAuth2AuthorizationManagerFactory<T> {\n\n\t/**\n\t * Create an {@link AuthorizationManager} that requires an {@link Authentication} to\n\t * have a {@code SCOPE_scope} authority.\n\t *\n\t * <p>\n\t * For example, if you call {@code hasScope(\"read\")}, then this will require that each\n\t * authentication have a {@link org.springframework.security.core.GrantedAuthority}\n\t * whose value is {@code SCOPE_read}.\n\t *\n\t * <p>\n\t * This would equivalent to calling\n\t * {@code AuthorityAuthorizationManager#hasAuthority(\"SCOPE_read\")}.\n\t * @param scope the scope value to require\n\t * @return an {@link AuthorizationManager} that requires a {@code \"SCOPE_scope\"}\n\t * authority\n\t */\n\tdefault AuthorizationManager<T> hasScope(String scope) {\n\t\treturn OAuth2AuthorizationManagers.hasScope(scope);\n\t}\n\n\t/**\n\t * Create an {@link AuthorizationManager} that requires an {@link Authentication} to\n\t * have at least one authority among {@code SCOPE_scope1}, {@code SCOPE_scope2}, ...\n\t * {@code SCOPE_scopeN}.\n\t *\n\t * <p>\n\t * For example, if you call {@code hasAnyScope(\"read\", \"write\")}, then this will\n\t * require that each authentication have at least a\n\t * {@link org.springframework.security.core.GrantedAuthority} whose value is either\n\t * {@code SCOPE_read} or {@code SCOPE_write}.\n\t *\n\t * <p>\n\t * This would equivalent to calling\n\t * {@code AuthorityAuthorizationManager#hasAnyAuthority(\"SCOPE_read\", \"SCOPE_write\")}.\n\t * @param scopes the scope values to allow\n\t * @return an {@link AuthorizationManager} that requires at least one authority among\n\t * {@code \"SCOPE_scope1\"}, {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}.\n\t */\n\tdefault AuthorizationManager<T> hasAnyScope(String... scopes) {\n\t\treturn OAuth2AuthorizationManagers.hasAnyScope(scopes);\n\t}\n\n\t/**\n\t * Create an {@link AuthorizationManager} that requires an {@link Authentication} to\n\t * have all authorities {@code SCOPE_scope1}, {@code SCOPE_scope2}, ...\n\t * {@code SCOPE_scopeN}.\n\t *\n\t * <p>\n\t * For example, if you call {@code hasAllScopes(\"read\", \"write\")}, then each\n\t * {@link org.springframework.security.core.Authentication} must have all\n\t * {@link org.springframework.security.core.GrantedAuthority} values of\n\t * {@code SCOPE_read} and {@code SCOPE_write}.\n\t *\n\t * <p>\n\t * This would be equivalent to calling\n\t * {@code AllAuthoritiesAuthorizationManager#hasAllAuthorities(\"SCOPE_read\", \"SCOPE_write\")}.\n\t * @param scopes the scope values to require\n\t * @return an {@link AuthorizationManager} that requires all authorities\n\t * {@code SCOPE_scope1}, {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}.\n\t */\n\tdefault AuthorizationManager<T> hasAllScopes(String... scopes) {\n\t\treturn OAuth2AuthorizationManagers.hasAllScopes(scopes);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.authorization;\n\nimport org.springframework.security.authorization.AllAuthoritiesAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * A convenience class for creating OAuth 2.0-specific {@link AuthorizationManager}s.\n *\n * @author Mario Petrovski\n * @author Josh Cummings\n * @since 6.2\n * @see AuthorityAuthorizationManager\n * @see AllAuthoritiesAuthorizationManager\n */\npublic final class OAuth2AuthorizationManagers {\n\n\tprivate OAuth2AuthorizationManagers() {\n\t}\n\n\t/**\n\t * Create an {@link AuthorizationManager} that requires an {@link Authentication} to\n\t * have a {@code SCOPE_scope} authority.\n\t *\n\t * <p>\n\t * For example, if you call {@code hasScope(\"read\")}, then this will require that each\n\t * authentication have a {@link org.springframework.security.core.GrantedAuthority}\n\t * whose value is {@code SCOPE_read}.\n\t *\n\t * <p>\n\t * This would equivalent to calling\n\t * {@code AuthorityAuthorizationManager#hasAuthority(\"SCOPE_read\")}.\n\t * @param scope the scope value to require\n\t * @param <T> the secure object\n\t * @return an {@link AuthorizationManager} that requires a {@code \"SCOPE_scope\"}\n\t * authority\n\t */\n\tpublic static <T> AuthorizationManager<T> hasScope(String scope) {\n\t\tassertScope(scope);\n\t\treturn AuthorityAuthorizationManager.hasAuthority(\"SCOPE_\" + scope);\n\t}\n\n\t/**\n\t * Create an {@link AuthorizationManager} that requires an {@link Authentication} to\n\t * have at least one authority among {@code SCOPE_scope1}, {@code SCOPE_scope2}, ...\n\t * {@code SCOPE_scopeN}.\n\t *\n\t * <p>\n\t * For example, if you call {@code hasAnyScope(\"read\", \"write\")}, then this will\n\t * require that each authentication have at least a\n\t * {@link org.springframework.security.core.GrantedAuthority} whose value is either\n\t * {@code SCOPE_read} or {@code SCOPE_write}.\n\t *\n\t * <p>\n\t * This would equivalent to calling\n\t * {@code AuthorityAuthorizationManager#hasAnyAuthority(\"SCOPE_read\", \"SCOPE_write\")}.\n\t * @param scopes the scope values to allow\n\t * @param <T> the secure object\n\t * @return an {@link AuthorizationManager} that requires at least one authority among\n\t * {@code \"SCOPE_scope1\"}, {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}.\n\t *\n\t */\n\tpublic static <T> AuthorizationManager<T> hasAnyScope(String... scopes) {\n\t\tString[] mappedScopes = new String[scopes.length];\n\t\tfor (int i = 0; i < scopes.length; i++) {\n\t\t\tassertScope(scopes[i]);\n\t\t\tmappedScopes[i] = \"SCOPE_\" + scopes[i];\n\t\t}\n\t\treturn AuthorityAuthorizationManager.hasAnyAuthority(mappedScopes);\n\t}\n\n\t/**\n\t * Create an {@link AuthorizationManager} that requires an {@link Authentication} to\n\t * have all authorities {@code SCOPE_scope1}, {@code SCOPE_scope2}, ...\n\t * {@code SCOPE_scopeN}.\n\t *\n\t * <p>\n\t * For example, if you call {@code hasAllScopes(\"read\", \"write\")}, then each\n\t * {@link org.springframework.security.core.Authentication} must have all\n\t * {@link org.springframework.security.core.GrantedAuthority} values of\n\t * {@code SCOPE_read} and {@code SCOPE_write}.\n\t *\n\t * <p>\n\t * This would be equivalent to calling\n\t * {@code AllAuthoritiesAuthorizationManager#hasAllAuthorities(\"SCOPE_read\", \"SCOPE_write\")}.\n\t * @param scopes the scope values to require\n\t * @return an {@link AuthorizationManager} that requires all authorities\n\t * {@code SCOPE_scope1}, {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}.\n\t * @since 7.1\n\t */\n\tpublic static <T> AuthorizationManager<T> hasAllScopes(String... scopes) {\n\t\tString[] mappedScopes = new String[scopes.length];\n\t\tfor (int i = 0; i < scopes.length; i++) {\n\t\t\tassertScope(scopes[i]);\n\t\t\tmappedScopes[i] = \"SCOPE_\" + scopes[i];\n\t\t}\n\t\treturn AllAuthoritiesAuthorizationManager.hasAllAuthorities(mappedScopes);\n\t}\n\n\tprivate static void assertScope(String scope) {\n\t\tAssert.isTrue(!scope.startsWith(\"SCOPE_\"),\n\t\t\t\t() -> scope + \" should not start with SCOPE_ since SCOPE_\"\n\t\t\t\t\t\t+ \" is automatically prepended when using hasScope and hasAnyScope. Consider using \"\n\t\t\t\t\t\t+ \" AuthorityAuthorizationManager#hasAuthority or #hasAnyAuthority instead.\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2ReactiveAuthorizationManagers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.authorization;\n\nimport org.springframework.security.authorization.AuthorityReactiveAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * A convenience class for creating OAuth 2.0-specific {@link AuthorizationManager}s.\n *\n * @author Josh Cummings\n * @since 6.2\n * @see AuthorityReactiveAuthorizationManager\n */\npublic final class OAuth2ReactiveAuthorizationManagers {\n\n\tprivate OAuth2ReactiveAuthorizationManagers() {\n\t}\n\n\t/**\n\t * Create a {@link ReactiveAuthorizationManager} that requires an\n\t * {@link Authentication} to have a {@code SCOPE_scope} authority.\n\t *\n\t * <p>\n\t * For example, if you call {@code hasScope(\"read\")}, then this will require that each\n\t * authentication have a {@link org.springframework.security.core.GrantedAuthority}\n\t * whose value is {@code SCOPE_read}.\n\t *\n\t * <p>\n\t * This would equivalent to calling\n\t * {@code AuthorityReactiveAuthorizationManager#hasAuthority(\"SCOPE_read\")}.\n\t * @param scope the scope value to require\n\t * @param <T> the secure object\n\t * @return an {@link ReactiveAuthorizationManager} that requires a\n\t * {@code \"SCOPE_scope\"} authority\n\t */\n\tpublic static <T> ReactiveAuthorizationManager<T> hasScope(String scope) {\n\t\tassertScope(scope);\n\t\treturn AuthorityReactiveAuthorizationManager.hasAuthority(\"SCOPE_\" + scope);\n\t}\n\n\t/**\n\t * Create a {@link ReactiveAuthorizationManager} that requires an\n\t * {@link Authentication} to have at least one authority among {@code SCOPE_scope1},\n\t * {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}.\n\t *\n\t * <p>\n\t * For example, if you call {@code hasAnyScope(\"read\", \"write\")}, then this will\n\t * require that each authentication have at least a\n\t * {@link org.springframework.security.core.GrantedAuthority} whose value is either\n\t * {@code SCOPE_read} or {@code SCOPE_write}.\n\t *\n\t * <p>\n\t * This would equivalent to calling\n\t * {@code AuthorityReactiveAuthorizationManager#hasAnyAuthority(\"SCOPE_read\", \"SCOPE_write\")}.\n\t * @param scopes the scope values to allow\n\t * @param <T> the secure object\n\t * @return an {@link ReactiveAuthorizationManager} that requires at least one\n\t * authority among {@code \"SCOPE_scope1\"}, {@code SCOPE_scope2}, ...\n\t * {@code SCOPE_scopeN}.\n\t */\n\tpublic static <T> ReactiveAuthorizationManager<T> hasAnyScope(String... scopes) {\n\t\tString[] mappedScopes = new String[scopes.length];\n\t\tfor (int i = 0; i < scopes.length; i++) {\n\t\t\tassertScope(scopes[i]);\n\t\t\tmappedScopes[i] = \"SCOPE_\" + scopes[i];\n\t\t}\n\t\treturn AuthorityReactiveAuthorizationManager.hasAnyAuthority(mappedScopes);\n\t}\n\n\tprivate static void assertScope(String scope) {\n\t\tAssert.isTrue(!scope.startsWith(\"SCOPE_\"),\n\t\t\t\t() -> scope + \" should not start with SCOPE_ since SCOPE_\"\n\t\t\t\t\t\t+ \" is automatically prepended when using hasScope and hasAnyScope. Consider using \"\n\t\t\t\t\t\t+ \" AuthorityReactiveAuthorizationManager#hasAuthority or #hasAnyAuthority instead.\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes that provide OAuth 2.0 authorization managers.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.authorization;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ClaimConversionService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.ConversionService;\nimport org.springframework.core.convert.converter.ConverterRegistry;\nimport org.springframework.core.convert.support.GenericConversionService;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\n\n/**\n * A {@link ConversionService} configured with converters that provide type conversion for\n * claim values.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see GenericConversionService\n * @see ClaimAccessor\n */\npublic final class ClaimConversionService extends GenericConversionService {\n\n\tprivate static volatile @Nullable ClaimConversionService sharedInstance;\n\n\tprivate ClaimConversionService() {\n\t\taddConverters(this);\n\t}\n\n\t/**\n\t * Returns a shared instance of {@code ClaimConversionService}.\n\t * @return a shared instance of {@code ClaimConversionService}\n\t */\n\tpublic static ClaimConversionService getSharedInstance() {\n\t\tClaimConversionService sharedInstance = ClaimConversionService.sharedInstance;\n\t\tif (sharedInstance == null) {\n\t\t\tsynchronized (ClaimConversionService.class) {\n\t\t\t\tsharedInstance = ClaimConversionService.sharedInstance;\n\t\t\t\tif (sharedInstance == null) {\n\t\t\t\t\tsharedInstance = new ClaimConversionService();\n\t\t\t\t\tClaimConversionService.sharedInstance = sharedInstance;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sharedInstance;\n\t}\n\n\t/**\n\t * Adds the converters that provide type conversion for claim values to the provided\n\t * {@link ConverterRegistry}.\n\t * @param converterRegistry the registry of converters to add to\n\t */\n\tpublic static void addConverters(ConverterRegistry converterRegistry) {\n\t\tconverterRegistry.addConverter(new ObjectToStringConverter());\n\t\tconverterRegistry.addConverter(new ObjectToBooleanConverter());\n\t\tconverterRegistry.addConverter(new ObjectToInstantConverter());\n\t\tconverterRegistry.addConverter(new ObjectToURLConverter());\n\t\tconverterRegistry.addConverter(new ObjectToListStringConverter());\n\t\tconverterRegistry.addConverter(new ObjectToMapStringObjectConverter());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ClaimTypeConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A {@link Converter} that provides type conversion for claim values.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see Converter\n */\npublic final class ClaimTypeConverter implements Converter<Map<String, Object>, Map<String, Object>> {\n\n\tprivate final Map<String, Converter<Object, ?>> claimTypeConverters;\n\n\t/**\n\t * Constructs a {@code ClaimTypeConverter} using the provided parameters.\n\t * @param claimTypeConverters a {@link Map} of {@link Converter}(s) keyed by claim\n\t * name\n\t */\n\tpublic ClaimTypeConverter(Map<String, Converter<Object, ?>> claimTypeConverters) {\n\t\tAssert.notEmpty(claimTypeConverters, \"claimTypeConverters cannot be empty\");\n\t\tAssert.noNullElements(claimTypeConverters.values().toArray(), \"Converter(s) cannot be null\");\n\t\tthis.claimTypeConverters = Collections.unmodifiableMap(new LinkedHashMap<>(claimTypeConverters));\n\t}\n\n\t@Override\n\tpublic Map<String, Object> convert(Map<String, Object> claims) {\n\t\tif (CollectionUtils.isEmpty(claims)) {\n\t\t\treturn claims;\n\t\t}\n\t\tMap<String, Object> result = new HashMap<>(claims);\n\t\tthis.claimTypeConverters.forEach((claimName, typeConverter) -> {\n\t\t\tif (claims.containsKey(claimName)) {\n\t\t\t\tObject claim = claims.get(claimName);\n\t\t\t\tObject mappedClaim = typeConverter.convert(claim);\n\t\t\t\tif (mappedClaim != null) {\n\t\t\t\t\tresult.put(claimName, mappedClaim);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToBooleanConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.util.Collections;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.GenericConverter;\n\n/**\n * @author Joe Grandja\n * @since 5.2\n */\nfinal class ObjectToBooleanConverter implements GenericConverter {\n\n\t@Override\n\tpublic Set<ConvertiblePair> getConvertibleTypes() {\n\t\treturn Collections.singleton(new ConvertiblePair(Object.class, Boolean.class));\n\t}\n\n\t@Override\n\tpublic @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\tif (source == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (source instanceof Boolean) {\n\t\t\treturn source;\n\t\t}\n\t\tif (source instanceof String) {\n\t\t\treturn Boolean.valueOf((String) source);\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToInstantConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.GenericConverter;\n\n/**\n * @author Joe Grandja\n * @since 5.2\n */\nfinal class ObjectToInstantConverter implements GenericConverter {\n\n\t@Override\n\tpublic Set<ConvertiblePair> getConvertibleTypes() {\n\t\treturn Collections.singleton(new ConvertiblePair(Object.class, Instant.class));\n\t}\n\n\t@Override\n\tpublic @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\tif (source == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (source instanceof Instant) {\n\t\t\treturn source;\n\t\t}\n\t\tif (source instanceof Date) {\n\t\t\treturn ((Date) source).toInstant();\n\t\t}\n\t\tif (source instanceof Number) {\n\t\t\treturn Instant.ofEpochSecond(((Number) source).longValue());\n\t\t}\n\t\ttry {\n\t\t\treturn Instant.ofEpochSecond(Long.parseLong(source.toString()));\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// Ignore\n\t\t}\n\t\ttry {\n\t\t\treturn Instant.parse(source.toString());\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// Ignore\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToListStringConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.ConditionalGenericConverter;\nimport org.springframework.util.ClassUtils;\n\n/**\n * @author Joe Grandja\n * @since 5.2\n */\nfinal class ObjectToListStringConverter implements ConditionalGenericConverter {\n\n\t@Override\n\tpublic Set<ConvertiblePair> getConvertibleTypes() {\n\t\tSet<ConvertiblePair> convertibleTypes = new LinkedHashSet<>();\n\t\tconvertibleTypes.add(new ConvertiblePair(Object.class, List.class));\n\t\tconvertibleTypes.add(new ConvertiblePair(Object.class, Collection.class));\n\t\treturn convertibleTypes;\n\t}\n\n\t@Override\n\tpublic boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\tTypeDescriptor typeDescriptor = targetType.getElementTypeDescriptor();\n\t\treturn typeDescriptor == null || typeDescriptor.getType().equals(String.class) || sourceType == null\n\t\t\t\t|| ClassUtils.isAssignable(sourceType.getType(), typeDescriptor.getType());\n\t}\n\n\t@Override\n\tpublic @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\tif (source == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (source instanceof Collection) {\n\t\t\tCollection<String> results = new ArrayList<>();\n\t\t\tfor (Object object : ((Collection<?>) source)) {\n\t\t\t\tif (object != null) {\n\t\t\t\t\tresults.add(object.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn results;\n\t\t}\n\t\treturn Collections.singletonList(source.toString());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToMapStringObjectConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.ConditionalGenericConverter;\n\n/**\n * @author Joe Grandja\n * @since 5.2\n */\nfinal class ObjectToMapStringObjectConverter implements ConditionalGenericConverter {\n\n\t@Override\n\tpublic Set<ConvertiblePair> getConvertibleTypes() {\n\t\treturn Collections.singleton(new ConvertiblePair(Object.class, Map.class));\n\t}\n\n\t@Override\n\tpublic boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\tTypeDescriptor mapKeyTypeDescriptor = targetType.getMapKeyTypeDescriptor();\n\t\treturn targetType.getElementTypeDescriptor() == null\n\t\t\t\t|| (mapKeyTypeDescriptor != null && mapKeyTypeDescriptor.getType().equals(String.class));\n\t}\n\n\t@Override\n\tpublic @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\tif (source == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!(source instanceof Map)) {\n\t\t\treturn null;\n\t\t}\n\t\tMap<?, ?> sourceMap = (Map<?, ?>) source;\n\t\tMap<String, Object> result = new HashMap<>();\n\t\tsourceMap.forEach((k, v) -> result.put(k.toString(), v));\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToStringConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.util.Collections;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.GenericConverter;\n\n/**\n * @author Joe Grandja\n * @since 5.2\n */\nfinal class ObjectToStringConverter implements GenericConverter {\n\n\t@Override\n\tpublic Set<ConvertiblePair> getConvertibleTypes() {\n\t\treturn Collections.singleton(new ConvertiblePair(Object.class, String.class));\n\t}\n\n\t@Override\n\tpublic @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\treturn (source != null) ? source.toString() : null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ObjectToURLConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.GenericConverter;\n\n/**\n * @author Joe Grandja\n * @since 5.2\n */\nfinal class ObjectToURLConverter implements GenericConverter {\n\n\t@Override\n\tpublic Set<ConvertiblePair> getConvertibleTypes() {\n\t\treturn Collections.singleton(new ConvertiblePair(Object.class, URL.class));\n\t}\n\n\t@Override\n\tpublic @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {\n\t\tif (source == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (source instanceof URL) {\n\t\t\treturn source;\n\t\t}\n\t\ttry {\n\t\t\treturn new URI(source.toString()).toURL();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// Ignore\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes that provide claim type converters for OAuth 2.0 and OpenID Connect.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.converter;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link Converter} that converts the provided OAuth 2.0 Access Token Response\n * parameters to an {@link OAuth2AccessTokenResponse}.\n *\n * @author Steve Riesenberg\n * @since 5.6\n */\npublic final class DefaultMapOAuth2AccessTokenResponseConverter\n\t\timplements Converter<Map<String, Object>, OAuth2AccessTokenResponse> {\n\n\tprivate static final Set<String> TOKEN_RESPONSE_PARAMETER_NAMES = new HashSet<>(\n\t\t\tArrays.asList(OAuth2ParameterNames.ACCESS_TOKEN, OAuth2ParameterNames.EXPIRES_IN,\n\t\t\t\t\tOAuth2ParameterNames.REFRESH_TOKEN, OAuth2ParameterNames.SCOPE, OAuth2ParameterNames.TOKEN_TYPE));\n\n\t@Override\n\tpublic OAuth2AccessTokenResponse convert(Map<String, Object> source) {\n\t\tString accessToken = getParameterValue(source, OAuth2ParameterNames.ACCESS_TOKEN);\n\t\tif (accessToken == null) {\n\t\t\tthrow new IllegalArgumentException(\"Missing required parameter: \" + OAuth2ParameterNames.ACCESS_TOKEN);\n\t\t}\n\t\tOAuth2AccessToken.TokenType accessTokenType = getAccessTokenType(source);\n\t\tlong expiresIn = getExpiresIn(source);\n\t\tSet<String> scopes = getScopes(source);\n\t\tString refreshToken = getParameterValue(source, OAuth2ParameterNames.REFRESH_TOKEN);\n\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>();\n\t\tfor (Map.Entry<String, Object> entry : source.entrySet()) {\n\t\t\tif (!TOKEN_RESPONSE_PARAMETER_NAMES.contains(entry.getKey())) {\n\t\t\t\tadditionalParameters.put(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\t\t// @formatter:off\n\t\treturn OAuth2AccessTokenResponse.withToken(accessToken)\n\t\t\t\t.tokenType(accessTokenType)\n\t\t\t\t.expiresIn(expiresIn)\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.refreshToken(refreshToken)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static OAuth2AccessToken.@Nullable TokenType getAccessTokenType(\n\t\t\tMap<String, Object> tokenResponseParameters) {\n\t\tif (OAuth2AccessToken.TokenType.BEARER.getValue()\n\t\t\t.equalsIgnoreCase(getParameterValue(tokenResponseParameters, OAuth2ParameterNames.TOKEN_TYPE))) {\n\t\t\treturn OAuth2AccessToken.TokenType.BEARER;\n\t\t}\n\t\telse if (OAuth2AccessToken.TokenType.DPOP.getValue()\n\t\t\t.equalsIgnoreCase(getParameterValue(tokenResponseParameters, OAuth2ParameterNames.TOKEN_TYPE))) {\n\t\t\treturn OAuth2AccessToken.TokenType.DPOP;\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static long getExpiresIn(Map<String, Object> tokenResponseParameters) {\n\t\treturn getParameterValue(tokenResponseParameters, OAuth2ParameterNames.EXPIRES_IN, 0L);\n\t}\n\n\tprivate static Set<String> getScopes(Map<String, Object> tokenResponseParameters) {\n\t\tif (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {\n\t\t\tString scope = getParameterValue(tokenResponseParameters, OAuth2ParameterNames.SCOPE);\n\t\t\treturn new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, \" \")));\n\t\t}\n\t\treturn Collections.emptySet();\n\t}\n\n\tprivate static @Nullable String getParameterValue(Map<String, Object> tokenResponseParameters,\n\t\t\tString parameterName) {\n\t\tObject obj = tokenResponseParameters.get(parameterName);\n\t\treturn (obj != null) ? obj.toString() : null;\n\t}\n\n\tprivate static long getParameterValue(Map<String, Object> tokenResponseParameters, String parameterName,\n\t\t\tlong defaultValue) {\n\t\tlong parameterValue = defaultValue;\n\n\t\tObject obj = tokenResponseParameters.get(parameterName);\n\t\tif (obj != null) {\n\t\t\t// Final classes Long and Integer do not need to be coerced\n\t\t\tif (obj.getClass() == Long.class) {\n\t\t\t\tparameterValue = (Long) obj;\n\t\t\t}\n\t\t\telse if (obj.getClass() == Integer.class) {\n\t\t\t\tparameterValue = (Integer) obj;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Attempt to coerce to a long (typically from a String)\n\t\t\t\ttry {\n\t\t\t\t\tparameterValue = Long.parseLong(obj.toString());\n\t\t\t\t}\n\t\t\t\tcatch (NumberFormatException ignored) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn parameterValue;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/DefaultOAuth2AccessTokenResponseMapConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link Converter} that converts the provided {@link OAuth2AccessTokenResponse} to a\n * {@code Map} representation of the OAuth 2.0 Access Token Response parameters.\n *\n * @author Steve Riesenberg\n * @since 5.6\n */\npublic final class DefaultOAuth2AccessTokenResponseMapConverter\n\t\timplements Converter<OAuth2AccessTokenResponse, Map<String, Object>> {\n\n\t@Override\n\tpublic Map<String, Object> convert(OAuth2AccessTokenResponse tokenResponse) {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(OAuth2ParameterNames.ACCESS_TOKEN, tokenResponse.getAccessToken().getTokenValue());\n\t\tparameters.put(OAuth2ParameterNames.TOKEN_TYPE, tokenResponse.getAccessToken().getTokenType().getValue());\n\t\tparameters.put(OAuth2ParameterNames.EXPIRES_IN, getExpiresIn(tokenResponse));\n\t\tif (!CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {\n\t\t\tparameters.put(OAuth2ParameterNames.SCOPE,\n\t\t\t\t\tStringUtils.collectionToDelimitedString(tokenResponse.getAccessToken().getScopes(), \" \"));\n\t\t}\n\t\tif (tokenResponse.getRefreshToken() != null) {\n\t\t\tparameters.put(OAuth2ParameterNames.REFRESH_TOKEN, tokenResponse.getRefreshToken().getTokenValue());\n\t\t}\n\t\tif (!CollectionUtils.isEmpty(tokenResponse.getAdditionalParameters())) {\n\t\t\tfor (Map.Entry<String, Object> entry : tokenResponse.getAdditionalParameters().entrySet()) {\n\t\t\t\tparameters.put(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\t\treturn parameters;\n\t}\n\n\tprivate static long getExpiresIn(OAuth2AccessTokenResponse tokenResponse) {\n\t\tif (tokenResponse.getAccessToken().getExpiresAt() != null) {\n\t\t\treturn ChronoUnit.SECONDS.between(Instant.now(), tokenResponse.getAccessToken().getExpiresAt());\n\t\t}\n\t\treturn -1;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AccessTokenResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A representation of an OAuth 2.0 Access Token Response.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2AccessToken\n * @see OAuth2RefreshToken\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-5.1\">Section\n * 5.1 Access Token Response</a>\n */\npublic final class OAuth2AccessTokenResponse {\n\n\tprivate @Nullable OAuth2AccessToken accessToken;\n\n\tprivate @Nullable OAuth2RefreshToken refreshToken;\n\n\tprivate @Nullable Map<String, Object> additionalParameters;\n\n\tprivate OAuth2AccessTokenResponse() {\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AccessToken Access Token}.\n\t * @return the {@link OAuth2AccessToken}\n\t */\n\tpublic OAuth2AccessToken getAccessToken() {\n\t\tAssert.notNull(this.accessToken, \"accessToken cannot be null\");\n\t\treturn this.accessToken;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2RefreshToken Refresh Token}.\n\t * @return the {@link OAuth2RefreshToken}, or {@code null} if not present in the\n\t * response\n\t * @since 5.1\n\t */\n\tpublic @Nullable OAuth2RefreshToken getRefreshToken() {\n\t\treturn this.refreshToken;\n\t}\n\n\t/**\n\t * Returns the additional parameters returned in the response.\n\t * @return a {@code Map} of the additional parameters returned in the response, may be\n\t * empty.\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\tAssert.notNull(this.additionalParameters, \"additionalParameters cannot be null\");\n\t\treturn this.additionalParameters;\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided access token value.\n\t * @param tokenValue the value of the access token\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withToken(String tokenValue) {\n\t\treturn new Builder(tokenValue);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided response.\n\t * @param response the response to initialize the builder with\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withResponse(OAuth2AccessTokenResponse response) {\n\t\treturn new Builder(response);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AccessTokenResponse}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate String tokenValue;\n\n\t\tprivate OAuth2AccessToken.@Nullable TokenType tokenType;\n\n\t\tprivate @Nullable Instant issuedAt;\n\n\t\tprivate @Nullable Instant expiresAt;\n\n\t\tprivate long expiresIn;\n\n\t\tprivate @Nullable Set<String> scopes;\n\n\t\tprivate @Nullable String refreshToken;\n\n\t\tprivate @Nullable Map<String, Object> additionalParameters;\n\n\t\tprivate Builder(OAuth2AccessTokenResponse response) {\n\t\t\tOAuth2AccessToken accessToken = response.getAccessToken();\n\t\t\tthis.tokenValue = accessToken.getTokenValue();\n\t\t\tthis.tokenType = accessToken.getTokenType();\n\t\t\tthis.issuedAt = accessToken.getIssuedAt();\n\t\t\tthis.expiresAt = accessToken.getExpiresAt();\n\t\t\tthis.scopes = accessToken.getScopes();\n\t\t\tthis.refreshToken = (response.getRefreshToken() != null) ? response.getRefreshToken().getTokenValue()\n\t\t\t\t\t: null;\n\t\t\tthis.additionalParameters = response.getAdditionalParameters();\n\t\t}\n\n\t\tprivate Builder(String tokenValue) {\n\t\t\tthis.tokenValue = tokenValue;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link OAuth2AccessToken.TokenType token type}.\n\t\t * @param tokenType the type of token issued, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder tokenType(OAuth2AccessToken.@Nullable TokenType tokenType) {\n\t\t\tthis.tokenType = tokenType;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the lifetime (in seconds) of the access token.\n\t\t * @param expiresIn the lifetime of the access token, in seconds.\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder expiresIn(long expiresIn) {\n\t\t\tthis.expiresIn = expiresIn;\n\t\t\tthis.expiresAt = null;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the scope(s) associated to the access token.\n\t\t * @param scopes the scope(s) associated to the access token, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder scopes(@Nullable Set<String> scopes) {\n\t\t\tthis.scopes = scopes;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the refresh token associated to the access token.\n\t\t * @param refreshToken the refresh token associated to the access token, may be\n\t\t * {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder refreshToken(@Nullable String refreshToken) {\n\t\t\tthis.refreshToken = refreshToken;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the additional parameters returned in the response.\n\t\t * @param additionalParameters the additional parameters returned in the response,\n\t\t * may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder additionalParameters(@Nullable Map<String, Object> additionalParameters) {\n\t\t\tthis.additionalParameters = additionalParameters;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AccessTokenResponse}.\n\t\t * @return a {@link OAuth2AccessTokenResponse}\n\t\t */\n\t\tpublic OAuth2AccessTokenResponse build() {\n\t\t\tAssert.notNull(this.tokenType, \"tokenType cannot be null\");\n\t\t\tInstant issuedAt = getIssuedAt();\n\t\t\tInstant expiresAt = getExpiresAt();\n\t\t\t// Convert nullable scopes to non-null for constructor\n\t\t\tSet<String> scopesToUse = (this.scopes != null) ? this.scopes : Collections.emptySet();\n\t\t\tOAuth2AccessTokenResponse accessTokenResponse = new OAuth2AccessTokenResponse();\n\t\t\taccessTokenResponse.accessToken = new OAuth2AccessToken(this.tokenType, this.tokenValue, issuedAt,\n\t\t\t\t\texpiresAt, scopesToUse);\n\t\t\tif (StringUtils.hasText(this.refreshToken)) {\n\t\t\t\taccessTokenResponse.refreshToken = new OAuth2RefreshToken(this.refreshToken, issuedAt);\n\t\t\t}\n\t\t\taccessTokenResponse.additionalParameters = Collections\n\t\t\t\t.unmodifiableMap(CollectionUtils.isEmpty(this.additionalParameters) ? Collections.emptyMap()\n\t\t\t\t\t\t: this.additionalParameters);\n\t\t\treturn accessTokenResponse;\n\t\t}\n\n\t\tprivate Instant getIssuedAt() {\n\t\t\tif (this.issuedAt == null) {\n\t\t\t\tthis.issuedAt = Instant.now();\n\t\t\t}\n\t\t\treturn this.issuedAt;\n\t\t}\n\n\t\t/**\n\t\t * expires_in is RECOMMENDED, as per spec\n\t\t * https://tools.ietf.org/html/rfc6749#section-5.1 Therefore, expires_in may not\n\t\t * be returned in the Access Token response which would result in the default\n\t\t * value of 0. For these instances, default the expiresAt to +1 second from\n\t\t * issuedAt time.\n\t\t * @return\n\t\t */\n\t\tprivate Instant getExpiresAt() {\n\t\t\tif (this.expiresAt == null) {\n\t\t\t\tInstant issuedAt = getIssuedAt();\n\t\t\t\tthis.expiresAt = (this.expiresIn > 0) ? issuedAt.plusSeconds(this.expiresIn) : issuedAt.plusSeconds(1);\n\t\t\t}\n\t\t\treturn this.expiresAt;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationExchange.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport org.springframework.util.Assert;\n\n/**\n * An &quot;exchange&quot; of an OAuth 2.0 Authorization Request and Response for the\n * authorization code grant type.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2AuthorizationRequest\n * @see OAuth2AuthorizationResponse\n */\npublic final class OAuth2AuthorizationExchange implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final OAuth2AuthorizationRequest authorizationRequest;\n\n\tprivate final OAuth2AuthorizationResponse authorizationResponse;\n\n\t/**\n\t * Constructs a new {@code OAuth2AuthorizationExchange} with the provided\n\t * Authorization Request and Authorization Response.\n\t * @param authorizationRequest the {@link OAuth2AuthorizationRequest Authorization\n\t * Request}\n\t * @param authorizationResponse the {@link OAuth2AuthorizationResponse Authorization\n\t * Response}\n\t */\n\tpublic OAuth2AuthorizationExchange(OAuth2AuthorizationRequest authorizationRequest,\n\t\t\tOAuth2AuthorizationResponse authorizationResponse) {\n\t\tAssert.notNull(authorizationRequest, \"authorizationRequest cannot be null\");\n\t\tAssert.notNull(authorizationResponse, \"authorizationResponse cannot be null\");\n\t\tthis.authorizationRequest = authorizationRequest;\n\t\tthis.authorizationResponse = authorizationResponse;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationRequest Authorization Request}.\n\t * @return the {@link OAuth2AuthorizationRequest}\n\t */\n\tpublic OAuth2AuthorizationRequest getAuthorizationRequest() {\n\t\treturn this.authorizationRequest;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationResponse Authorization Response}.\n\t * @return the {@link OAuth2AuthorizationResponse}\n\t */\n\tpublic OAuth2AuthorizationResponse getAuthorizationResponse() {\n\t\treturn this.authorizationResponse;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.DefaultUriBuilderFactory;\nimport org.springframework.web.util.UriBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * A representation of an OAuth 2.0 Authorization Request for the authorization code grant\n * type.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AuthorizationGrantType\n * @see OAuth2AuthorizationResponseType\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.1\">Section 4.1.1 Authorization Code\n * Grant Request</a>\n */\npublic class OAuth2AuthorizationRequest implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String authorizationUri;\n\n\tprivate final AuthorizationGrantType authorizationGrantType;\n\n\tprivate final OAuth2AuthorizationResponseType responseType;\n\n\tprivate final String clientId;\n\n\tprivate final @Nullable String redirectUri;\n\n\tprivate final Set<String> scopes;\n\n\tprivate final @Nullable String state;\n\n\tprivate final Map<String, Object> additionalParameters;\n\n\tprivate final String authorizationRequestUri;\n\n\tprivate final Map<String, Object> attributes;\n\n\tprotected OAuth2AuthorizationRequest(AbstractBuilder<?, ?> builder) {\n\t\tAssert.notNull(builder.authorizationUri, \"authorizationUri cannot be null\");\n\t\tAssert.notNull(builder.clientId, \"clientId cannot be null\");\n\t\tAssert.hasText(builder.authorizationUri, \"authorizationUri cannot be empty\");\n\t\tAssert.hasText(builder.clientId, \"clientId cannot be empty\");\n\t\tthis.authorizationUri = builder.authorizationUri;\n\t\tthis.authorizationGrantType = builder.authorizationGrantType;\n\t\tthis.responseType = builder.responseType;\n\t\tthis.clientId = builder.clientId;\n\t\tthis.redirectUri = builder.redirectUri;\n\t\tthis.scopes = Collections.unmodifiableSet(\n\t\t\t\tCollectionUtils.isEmpty(builder.scopes) ? Collections.emptySet() : new LinkedHashSet<>(builder.scopes));\n\t\tthis.state = builder.state;\n\t\tthis.additionalParameters = Collections.unmodifiableMap(builder.additionalParameters);\n\t\tString builderAuthorizationRequestUri = builder.authorizationRequestUri;\n\t\tthis.authorizationRequestUri = StringUtils.hasText(builderAuthorizationRequestUri)\n\t\t\t\t? builderAuthorizationRequestUri : builder.buildAuthorizationRequestUri();\n\t\tthis.attributes = Collections.unmodifiableMap(builder.attributes);\n\t}\n\n\t/**\n\t * Returns the uri for the authorization endpoint.\n\t * @return the uri for the authorization endpoint\n\t */\n\tpublic String getAuthorizationUri() {\n\t\treturn this.authorizationUri;\n\t}\n\n\t/**\n\t * Returns the {@link AuthorizationGrantType grant type}.\n\t * @return the {@link AuthorizationGrantType}\n\t */\n\tpublic AuthorizationGrantType getGrantType() {\n\t\treturn this.authorizationGrantType;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2AuthorizationResponseType response type}.\n\t * @return the {@link OAuth2AuthorizationResponseType}\n\t */\n\tpublic OAuth2AuthorizationResponseType getResponseType() {\n\t\treturn this.responseType;\n\t}\n\n\t/**\n\t * Returns the client identifier.\n\t * @return the client identifier\n\t */\n\tpublic String getClientId() {\n\t\treturn this.clientId;\n\t}\n\n\t/**\n\t * Returns the uri for the redirection endpoint, or {@code null} if not present.\n\t * @return the uri for the redirection endpoint, or {@code null}\n\t */\n\tpublic @Nullable String getRedirectUri() {\n\t\treturn this.redirectUri;\n\t}\n\n\t/**\n\t * Returns the scope(s).\n\t * @return the scope(s), or an empty {@code Set} if not available\n\t */\n\tpublic Set<String> getScopes() {\n\t\treturn this.scopes;\n\t}\n\n\t/**\n\t * Returns the state, or {@code null} if not present.\n\t * @return the state, or {@code null}\n\t */\n\tpublic @Nullable String getState() {\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Returns the additional parameter(s) used in the request.\n\t * @return a {@code Map} of the additional parameter(s), or an empty {@code Map} if\n\t * not available\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\treturn this.additionalParameters;\n\t}\n\n\t/**\n\t * Returns the attribute(s) associated to the request.\n\t * @return a {@code Map} of the attribute(s), or an empty {@code Map} if not available\n\t * @since 5.2\n\t */\n\tpublic Map<String, Object> getAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\t/**\n\t * Returns the value of an attribute associated to the request.\n\t * @param <T> the type of the attribute\n\t * @param name the name of the attribute\n\t * @return the value of the attribute associated to the request, or {@code null} if\n\t * not available\n\t * @since 5.2\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> @Nullable T getAttribute(String name) {\n\t\treturn (T) this.getAttributes().get(name);\n\t}\n\n\t/**\n\t * Returns the {@code URI} string representation of the OAuth 2.0 Authorization\n\t * Request.\n\t *\n\t * <p>\n\t * <b>NOTE:</b> The {@code URI} string is encoded in the\n\t * {@code application/x-www-form-urlencoded} MIME format.\n\t * @return the {@code URI} string representation of the OAuth 2.0 Authorization\n\t * Request\n\t * @since 5.1\n\t */\n\tpublic String getAuthorizationRequestUri() {\n\t\treturn this.authorizationRequestUri;\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the authorization code grant type.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder authorizationCode() {\n\t\treturn new Builder();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2AuthorizationRequest that = (OAuth2AuthorizationRequest) obj;\n\n\t\treturn Objects.equals(this.authorizationUri, that.authorizationUri)\n\t\t\t\t&& Objects.equals(this.authorizationGrantType, that.authorizationGrantType)\n\t\t\t\t&& Objects.equals(this.responseType, that.responseType) && Objects.equals(this.clientId, that.clientId)\n\t\t\t\t&& Objects.equals(this.redirectUri, that.redirectUri) && Objects.equals(this.scopes, that.scopes)\n\t\t\t\t&& Objects.equals(this.state, that.state)\n\t\t\t\t&& Objects.equals(this.additionalParameters, that.additionalParameters)\n\t\t\t\t&& Objects.equals(this.authorizationRequestUri, that.authorizationRequestUri)\n\t\t\t\t&& Objects.equals(this.attributes, that.attributes);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.authorizationUri, this.clientId, this.authorizationGrantType, this.responseType,\n\t\t\t\tthis.redirectUri, this.scopes, this.state, this.additionalParameters, this.authorizationRequestUri,\n\t\t\t\tthis.attributes);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the values from the provided\n\t * {@code authorizationRequest}.\n\t * @param authorizationRequest the authorization request used for initializing the\n\t * {@link Builder}\n\t * @return the {@link Builder}\n\t * @since 5.1\n\t */\n\tpublic static Builder from(OAuth2AuthorizationRequest authorizationRequest) {\n\t\tAssert.notNull(authorizationRequest, \"authorizationRequest cannot be null\");\n\t\t// @formatter:off\n\t\treturn new Builder()\n\t\t\t\t.authorizationUri(authorizationRequest.getAuthorizationUri())\n\t\t\t\t.clientId(authorizationRequest.getClientId())\n\t\t\t\t.redirectUri(authorizationRequest.getRedirectUri())\n\t\t\t\t.scopes(authorizationRequest.getScopes())\n\t\t\t\t.state(authorizationRequest.getState())\n\t\t\t\t.additionalParameters(authorizationRequest.getAdditionalParameters())\n\t\t\t\t.attributes(authorizationRequest.getAttributes());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AuthorizationRequest}.\n\t */\n\tpublic static class Builder extends AbstractBuilder<OAuth2AuthorizationRequest, Builder> {\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AuthorizationRequest}.\n\t\t * @return a {@link OAuth2AuthorizationRequest}\n\t\t */\n\t\t@Override\n\t\tpublic OAuth2AuthorizationRequest build() {\n\t\t\treturn new OAuth2AuthorizationRequest(this);\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for subclasses of {@link OAuth2AuthorizationRequest}.\n\t *\n\t * @param <T> the type of authorization request\n\t * @param <B> the type of the builder\n\t */\n\tprotected abstract static class AbstractBuilder<T extends OAuth2AuthorizationRequest, B extends AbstractBuilder<T, B>> {\n\n\t\tprivate @Nullable String authorizationUri;\n\n\t\tprivate final AuthorizationGrantType authorizationGrantType = AuthorizationGrantType.AUTHORIZATION_CODE;\n\n\t\tprivate final OAuth2AuthorizationResponseType responseType = OAuth2AuthorizationResponseType.CODE;\n\n\t\tprivate @Nullable String clientId;\n\n\t\tprivate @Nullable String redirectUri;\n\n\t\tprivate @Nullable Set<String> scopes;\n\n\t\tprivate @Nullable String state;\n\n\t\tprivate Map<String, Object> additionalParameters = new LinkedHashMap<>();\n\n\t\tprivate Consumer<Map<String, Object>> parametersConsumer = (params) -> {\n\t\t};\n\n\t\tprivate Map<String, Object> attributes = new LinkedHashMap<>();\n\n\t\tprivate @Nullable String authorizationRequestUri;\n\n\t\tprivate Function<UriBuilder, URI> authorizationRequestUriFunction = (builder) -> builder.build();\n\n\t\tprivate final DefaultUriBuilderFactory uriBuilderFactory;\n\n\t\tprotected AbstractBuilder() {\n\t\t\tthis.uriBuilderFactory = new DefaultUriBuilderFactory();\n\t\t\t// The supplied authorizationUri may contain encoded parameters\n\t\t\t// so disable encoding in UriBuilder and instead apply encoding within this\n\t\t\t// builder\n\t\t\tthis.uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected final B getThis() {\n\t\t\t// avoid unchecked casts in subclasses by using \"getThis()\" instead of \"(B)\n\t\t\t// this\"\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the uri for the authorization endpoint.\n\t\t * @param authorizationUri the uri for the authorization endpoint\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B authorizationUri(String authorizationUri) {\n\t\t\tthis.authorizationUri = authorizationUri;\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the client identifier.\n\t\t * @param clientId the client identifier\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B clientId(String clientId) {\n\t\t\tthis.clientId = clientId;\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the uri for the redirection endpoint.\n\t\t * @param redirectUri the uri for the redirection endpoint, may be {@code null}\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B redirectUri(@Nullable String redirectUri) {\n\t\t\tthis.redirectUri = redirectUri;\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the scope(s).\n\t\t * @param scope the scope(s), may be {@code null}\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B scope(@Nullable String... scope) {\n\t\t\tif (scope != null && scope.length > 0) {\n\t\t\t\treturn scopes(new LinkedHashSet<>(Arrays.asList(scope)));\n\t\t\t}\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the scope(s).\n\t\t * @param scopes the scope(s), may be {@code null}\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B scopes(@Nullable Set<String> scopes) {\n\t\t\tthis.scopes = scopes;\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the state.\n\t\t * @param state the state, may be {@code null}\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B state(@Nullable String state) {\n\t\t\tthis.state = state;\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the additional parameter(s) used in the request.\n\t\t * @param additionalParameters the additional parameter(s) used in the request\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B additionalParameters(Map<String, Object> additionalParameters) {\n\t\t\tif (!CollectionUtils.isEmpty(additionalParameters)) {\n\t\t\t\tthis.additionalParameters.putAll(additionalParameters);\n\t\t\t}\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} to be provided access to the additional parameter(s)\n\t\t * allowing the ability to add, replace, or remove.\n\t\t * @param additionalParametersConsumer a {@code Consumer} of the additional\n\t\t * parameters\n\t\t * @return the {@link AbstractBuilder}\n\t\t * @since 5.3\n\t\t */\n\t\tpublic B additionalParameters(Consumer<Map<String, Object>> additionalParametersConsumer) {\n\t\t\tif (additionalParametersConsumer != null) {\n\t\t\t\tadditionalParametersConsumer.accept(this.additionalParameters);\n\t\t\t}\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} to be provided access to all the parameters allowing the\n\t\t * ability to add, replace, or remove.\n\t\t * @param parametersConsumer a {@code Consumer} of all the parameters\n\t\t * @return the {@link AbstractBuilder}\n\t\t * @since 5.3\n\t\t */\n\t\tpublic B parameters(Consumer<Map<String, Object>> parametersConsumer) {\n\t\t\tif (parametersConsumer != null) {\n\t\t\t\tthis.parametersConsumer = parametersConsumer;\n\t\t\t}\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the attributes associated to the request.\n\t\t * @param attributes the attributes associated to the request\n\t\t * @return the {@link AbstractBuilder}\n\t\t * @since 5.2\n\t\t */\n\t\tpublic B attributes(Map<String, Object> attributes) {\n\t\t\tif (!CollectionUtils.isEmpty(attributes)) {\n\t\t\t\tthis.attributes.putAll(attributes);\n\t\t\t}\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} to be provided access to the attribute(s) allowing the\n\t\t * ability to add, replace, or remove.\n\t\t * @param attributesConsumer a {@code Consumer} of the attribute(s)\n\t\t * @return the {@link AbstractBuilder}\n\t\t * @since 5.3\n\t\t */\n\t\tpublic B attributes(Consumer<Map<String, Object>> attributesConsumer) {\n\t\t\tif (attributesConsumer != null) {\n\t\t\t\tattributesConsumer.accept(this.attributes);\n\t\t\t}\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code URI} string representation of the OAuth 2.0 Authorization\n\t\t * Request.\n\t\t *\n\t\t * <p>\n\t\t * <b>NOTE:</b> The {@code URI} string is <b>required</b> to be encoded in the\n\t\t * {@code application/x-www-form-urlencoded} MIME format.\n\t\t * @param authorizationRequestUri the {@code URI} string representation of the\n\t\t * OAuth 2.0 Authorization Request\n\t\t * @return the {@link AbstractBuilder}\n\t\t * @since 5.1\n\t\t */\n\t\tpublic B authorizationRequestUri(String authorizationRequestUri) {\n\t\t\tthis.authorizationRequestUri = authorizationRequestUri;\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Function} to be provided a {@code UriBuilder} representation of the\n\t\t * OAuth 2.0 Authorization Request allowing for further customizations.\n\t\t * @param authorizationRequestUriFunction a {@code Function} to be provided a\n\t\t * {@code UriBuilder} representation of the OAuth 2.0 Authorization Request\n\t\t * @return the {@link AbstractBuilder}\n\t\t * @since 5.3\n\t\t */\n\t\tpublic B authorizationRequestUri(Function<UriBuilder, URI> authorizationRequestUriFunction) {\n\t\t\tif (authorizationRequestUriFunction != null) {\n\t\t\t\tthis.authorizationRequestUriFunction = authorizationRequestUriFunction;\n\t\t\t}\n\t\t\treturn getThis();\n\t\t}\n\n\t\tpublic abstract T build();\n\n\t\tprivate String buildAuthorizationRequestUri() {\n\t\t\tMap<String, Object> parameters = getParameters(); // Not encoded\n\t\t\tthis.parametersConsumer.accept(parameters);\n\t\t\tMultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();\n\t\t\tparameters.forEach((k, v) -> {\n\t\t\t\tString key = encodeQueryParam(k);\n\t\t\t\tif (v instanceof Iterable) {\n\t\t\t\t\t((Iterable<?>) v).forEach((value) -> queryParams.add(key, encodeQueryParam(String.valueOf(value))));\n\t\t\t\t}\n\t\t\t\telse if (v != null && v.getClass().isArray()) {\n\t\t\t\t\tObject[] values = (Object[]) v;\n\t\t\t\t\tfor (Object value : values) {\n\t\t\t\t\t\tqueryParams.add(key, encodeQueryParam(String.valueOf(value)));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tqueryParams.set(key, encodeQueryParam(String.valueOf(v)));\n\t\t\t\t}\n\t\t\t});\n\t\t\tAssert.notNull(this.authorizationUri, \"authorizationUri cannot be null\");\n\t\t\tUriBuilder uriBuilder = this.uriBuilderFactory.uriString(this.authorizationUri).queryParams(queryParams);\n\t\t\treturn this.authorizationRequestUriFunction.apply(uriBuilder).toString();\n\t\t}\n\n\t\tprotected Map<String, Object> getParameters() {\n\t\t\tMap<String, Object> parameters = new LinkedHashMap<>();\n\t\t\tparameters.put(OAuth2ParameterNames.RESPONSE_TYPE, this.responseType.getValue());\n\t\t\tparameters.put(OAuth2ParameterNames.CLIENT_ID, this.clientId);\n\t\t\tif (!CollectionUtils.isEmpty(this.scopes)) {\n\t\t\t\tparameters.put(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(this.scopes, \" \"));\n\t\t\t}\n\t\t\tif (this.state != null) {\n\t\t\t\tparameters.put(OAuth2ParameterNames.STATE, this.state);\n\t\t\t}\n\t\t\tif (this.redirectUri != null) {\n\t\t\t\tparameters.put(OAuth2ParameterNames.REDIRECT_URI, this.redirectUri);\n\t\t\t}\n\t\t\tparameters.putAll(this.additionalParameters);\n\t\t\treturn parameters;\n\t\t}\n\n\t\t// Encode query parameter value according to RFC 3986\n\t\tprivate static String encodeQueryParam(String value) {\n\t\t\treturn UriUtils.encodeQueryParam(value, StandardCharsets.UTF_8);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A representation of an OAuth 2.0 Authorization Response for the authorization code\n * grant type.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2Error\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-4.1.2\">Section 4.1.2 Authorization\n * Response</a>\n */\npublic final class OAuth2AuthorizationResponse implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate @Nullable String redirectUri;\n\n\tprivate @Nullable String state;\n\n\tprivate @Nullable String code;\n\n\tprivate @Nullable OAuth2Error error;\n\n\tprivate OAuth2AuthorizationResponse() {\n\t}\n\n\t/**\n\t * Returns the uri where the response was redirected to.\n\t * @return the uri where the response was redirected to\n\t */\n\tpublic String getRedirectUri() {\n\t\tAssert.notNull(this.redirectUri, \"redirectUri cannot be null\");\n\t\treturn this.redirectUri;\n\t}\n\n\t/**\n\t * Returns the state, or {@code null} if not present.\n\t * @return the state, or {@code null}\n\t */\n\tpublic @Nullable String getState() {\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Returns the authorization code, or {@code null} if the response is an error\n\t * response.\n\t * @return the authorization code, or {@code null}\n\t */\n\tpublic @Nullable String getCode() {\n\t\treturn this.code;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2Error OAuth 2.0 Error} if the Authorization Request\n\t * failed, otherwise {@code null}.\n\t * @return the {@link OAuth2Error} if the Authorization Request failed, otherwise\n\t * {@code null}\n\t */\n\tpublic @Nullable OAuth2Error getError() {\n\t\treturn this.error;\n\t}\n\n\t/**\n\t * Returns {@code true} if the Authorization Request succeeded, otherwise\n\t * {@code false}.\n\t * @return {@code true} if the Authorization Request succeeded, otherwise\n\t * {@code false}\n\t */\n\tpublic boolean statusOk() {\n\t\treturn !this.statusError();\n\t}\n\n\t/**\n\t * Returns {@code true} if the Authorization Request failed, otherwise {@code false}.\n\t * @return {@code true} if the Authorization Request failed, otherwise {@code false}\n\t */\n\tpublic boolean statusError() {\n\t\treturn (this.error != null && this.error.getErrorCode() != null);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the authorization code.\n\t * @param code the authorization code\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder success(String code) {\n\t\tAssert.hasText(code, \"code cannot be empty\");\n\t\treturn new Builder().code(code);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the error code.\n\t * @param errorCode the error code\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder error(String errorCode) {\n\t\tAssert.hasText(errorCode, \"errorCode cannot be empty\");\n\t\treturn new Builder().errorCode(errorCode);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2AuthorizationResponse}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate @Nullable String redirectUri;\n\n\t\tprivate @Nullable String state;\n\n\t\tprivate @Nullable String code;\n\n\t\tprivate @Nullable String errorCode;\n\n\t\tprivate @Nullable String errorDescription;\n\n\t\tprivate @Nullable String errorUri;\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the uri where the response was redirected to.\n\t\t * @param redirectUri the uri where the response was redirected to\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder redirectUri(String redirectUri) {\n\t\t\tthis.redirectUri = redirectUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the state.\n\t\t * @param state the state\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder state(String state) {\n\t\t\tthis.state = state;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the authorization code.\n\t\t * @param code the authorization code\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder code(String code) {\n\t\t\tthis.code = code;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the error code.\n\t\t * @param errorCode the error code\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder errorCode(String errorCode) {\n\t\t\tthis.errorCode = errorCode;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the error description.\n\t\t * @param errorDescription the error description\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder errorDescription(String errorDescription) {\n\t\t\tthis.errorDescription = errorDescription;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the error uri.\n\t\t * @param errorUri the error uri\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder errorUri(String errorUri) {\n\t\t\tthis.errorUri = errorUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2AuthorizationResponse}.\n\t\t * @return a {@link OAuth2AuthorizationResponse}\n\t\t */\n\t\tpublic OAuth2AuthorizationResponse build() {\n\t\t\tif (StringUtils.hasText(this.code) && StringUtils.hasText(this.errorCode)) {\n\t\t\t\tthrow new IllegalArgumentException(\"code and errorCode cannot both be set\");\n\t\t\t}\n\t\t\tAssert.hasText(this.redirectUri, \"redirectUri cannot be empty\");\n\t\t\tOAuth2AuthorizationResponse authorizationResponse = new OAuth2AuthorizationResponse();\n\t\t\tauthorizationResponse.redirectUri = this.redirectUri;\n\t\t\tauthorizationResponse.state = this.state;\n\t\t\tif (StringUtils.hasText(this.code)) {\n\t\t\t\tauthorizationResponse.code = this.code;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tAssert.notNull(this.errorCode, \"errorCode cannot be null when code is not present\");\n\t\t\t\tAssert.hasText(this.errorCode, \"errorCode cannot be empty when code is not present\");\n\t\t\t\tauthorizationResponse.error = new OAuth2Error(this.errorCode, this.errorDescription, this.errorUri);\n\t\t\t}\n\t\t\treturn authorizationResponse;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationResponseType.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.io.Serializable;\n\nimport org.springframework.util.Assert;\n\n/**\n * The {@code response_type} parameter is consumed by the authorization endpoint which is\n * used by the authorization code grant type. The client sets the {@code response_type}\n * parameter with the desired grant type before initiating the authorization request.\n *\n * <p>\n * The {@code response_type} parameter value may be &quot;code&quot; for requesting an\n * authorization code.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc6749#section-3.1.1\">Section 3.1.1 Response Type</a>\n */\npublic final class OAuth2AuthorizationResponseType implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tpublic static final OAuth2AuthorizationResponseType CODE = new OAuth2AuthorizationResponseType(\"code\");\n\n\tprivate final String value;\n\n\tpublic OAuth2AuthorizationResponseType(String value) {\n\t\tAssert.hasText(value, \"value cannot be empty\");\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Returns the value of the authorization response type.\n\t * @return the value of the authorization response type\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2AuthorizationResponseType that = (OAuth2AuthorizationResponseType) obj;\n\t\treturn this.getValue().equals(that.getValue());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.getValue().hashCode();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2DeviceAuthorizationResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.OAuth2DeviceCode;\nimport org.springframework.security.oauth2.core.OAuth2UserCode;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A representation of an OAuth 2.0 Device Authorization Response.\n *\n * @author Steve Riesenberg\n * @since 6.1\n * @see OAuth2DeviceCode\n * @see OAuth2UserCode\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc8628#section-3.2\">Section\n * 3.2 Device Authorization Response</a>\n */\npublic final class OAuth2DeviceAuthorizationResponse {\n\n\tprivate @Nullable OAuth2DeviceCode deviceCode;\n\n\tprivate @Nullable OAuth2UserCode userCode;\n\n\tprivate @Nullable String verificationUri;\n\n\tprivate @Nullable String verificationUriComplete;\n\n\tprivate long interval;\n\n\tprivate @Nullable Map<String, Object> additionalParameters;\n\n\tprivate OAuth2DeviceAuthorizationResponse() {\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2DeviceCode Device Code}.\n\t * @return the {@link OAuth2DeviceCode}\n\t */\n\tpublic OAuth2DeviceCode getDeviceCode() {\n\t\tAssert.notNull(this.deviceCode, \"deviceCode cannot be null\");\n\t\treturn this.deviceCode;\n\t}\n\n\t/**\n\t * Returns the {@link OAuth2UserCode User Code}.\n\t * @return the {@link OAuth2UserCode}\n\t */\n\tpublic OAuth2UserCode getUserCode() {\n\t\tAssert.notNull(this.userCode, \"userCode cannot be null\");\n\t\treturn this.userCode;\n\t}\n\n\t/**\n\t * Returns the end-user verification URI.\n\t * @return the end-user verification URI\n\t */\n\tpublic String getVerificationUri() {\n\t\tAssert.notNull(this.verificationUri, \"verificationUri cannot be null\");\n\t\treturn this.verificationUri;\n\t}\n\n\t/**\n\t * Returns the end-user verification URI that includes the user code, or {@code null}\n\t * if not present.\n\t * @return the end-user verification URI that includes the user code, or {@code null}\n\t */\n\tpublic @Nullable String getVerificationUriComplete() {\n\t\treturn this.verificationUriComplete;\n\t}\n\n\t/**\n\t * Returns the minimum amount of time (in seconds) that the client should wait between\n\t * polling requests to the token endpoint.\n\t * @return the minimum amount of time between polling requests\n\t */\n\tpublic long getInterval() {\n\t\treturn this.interval;\n\t}\n\n\t/**\n\t * Returns the additional parameters returned in the response.\n\t * @return a {@code Map} of the additional parameters returned in the response, may be\n\t * empty.\n\t */\n\tpublic Map<String, Object> getAdditionalParameters() {\n\t\tAssert.notNull(this.additionalParameters, \"additionalParameters cannot be null\");\n\t\treturn this.additionalParameters;\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided device code and user\n\t * code values.\n\t * @param deviceCode the value of the device code\n\t * @param userCode the value of the user code\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(String deviceCode, String userCode) {\n\t\tAssert.hasText(deviceCode, \"deviceCode cannot be empty\");\n\t\tAssert.hasText(userCode, \"userCode cannot be empty\");\n\t\treturn new Builder(deviceCode, userCode);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided device code and user\n\t * code.\n\t * @param deviceCode the {@link OAuth2DeviceCode}\n\t * @param userCode the {@link OAuth2UserCode}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(OAuth2DeviceCode deviceCode, OAuth2UserCode userCode) {\n\t\tAssert.notNull(deviceCode, \"deviceCode cannot be null\");\n\t\tAssert.notNull(userCode, \"userCode cannot be null\");\n\t\treturn new Builder(deviceCode, userCode);\n\t}\n\n\t/**\n\t * A builder for {@link OAuth2DeviceAuthorizationResponse}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final String deviceCode;\n\n\t\tprivate final String userCode;\n\n\t\tprivate @Nullable String verificationUri;\n\n\t\tprivate @Nullable String verificationUriComplete;\n\n\t\tprivate long expiresIn;\n\n\t\tprivate long interval;\n\n\t\tprivate @Nullable Map<String, Object> additionalParameters;\n\n\t\tprivate Builder(OAuth2DeviceCode deviceCode, OAuth2UserCode userCode) {\n\t\t\tthis.deviceCode = deviceCode.getTokenValue();\n\t\t\tthis.userCode = userCode.getTokenValue();\n\t\t\tthis.expiresIn = ChronoUnit.SECONDS.between(deviceCode.getIssuedAt(), deviceCode.getExpiresAt());\n\t\t}\n\n\t\tprivate Builder(String deviceCode, String userCode) {\n\t\t\tthis.deviceCode = deviceCode;\n\t\t\tthis.userCode = userCode;\n\t\t}\n\n\t\t/**\n\t\t * Sets the end-user verification URI.\n\t\t * @param verificationUri the end-user verification URI\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder verificationUri(String verificationUri) {\n\t\t\tthis.verificationUri = verificationUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the end-user verification URI that includes the user code.\n\t\t * @param verificationUriComplete the end-user verification URI that includes the\n\t\t * user code, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder verificationUriComplete(@Nullable String verificationUriComplete) {\n\t\t\tthis.verificationUriComplete = verificationUriComplete;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the lifetime (in seconds) of the device code and user code.\n\t\t * @param expiresIn the lifetime (in seconds) of the device code and user code\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder expiresIn(long expiresIn) {\n\t\t\tthis.expiresIn = expiresIn;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the minimum amount of time (in seconds) that the client should wait\n\t\t * between polling requests to the token endpoint.\n\t\t * @param interval the minimum amount of time between polling requests\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder interval(long interval) {\n\t\t\tthis.interval = interval;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the additional parameters returned in the response.\n\t\t * @param additionalParameters the additional parameters returned in the response\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder additionalParameters(Map<String, Object> additionalParameters) {\n\t\t\tthis.additionalParameters = additionalParameters;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link OAuth2DeviceAuthorizationResponse}.\n\t\t * @return a {@link OAuth2DeviceAuthorizationResponse}\n\t\t */\n\t\tpublic OAuth2DeviceAuthorizationResponse build() {\n\t\t\tAssert.hasText(this.verificationUri, \"verificationUri cannot be empty\");\n\t\t\tAssert.isTrue(this.expiresIn > 0, \"expiresIn must be greater than zero\");\n\n\t\t\tInstant issuedAt = Instant.now();\n\t\t\tInstant expiresAt = issuedAt.plusSeconds(this.expiresIn);\n\t\t\tOAuth2DeviceCode deviceCode = new OAuth2DeviceCode(this.deviceCode, issuedAt, expiresAt);\n\t\t\tOAuth2UserCode userCode = new OAuth2UserCode(this.userCode, issuedAt, expiresAt);\n\n\t\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse = new OAuth2DeviceAuthorizationResponse();\n\t\t\tdeviceAuthorizationResponse.deviceCode = deviceCode;\n\t\t\tdeviceAuthorizationResponse.userCode = userCode;\n\t\t\tdeviceAuthorizationResponse.verificationUri = this.verificationUri;\n\t\t\tdeviceAuthorizationResponse.verificationUriComplete = this.verificationUriComplete;\n\t\t\tdeviceAuthorizationResponse.interval = this.interval;\n\t\t\tdeviceAuthorizationResponse.additionalParameters = Collections\n\t\t\t\t.unmodifiableMap(CollectionUtils.isEmpty(this.additionalParameters) ? Collections.emptyMap()\n\t\t\t\t\t\t: this.additionalParameters);\n\n\t\t\treturn deviceAuthorizationResponse;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2ParameterNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\n/**\n * Standard and custom (non-standard) parameter names defined in the OAuth Parameters\n * Registry and used by the authorization endpoint, token endpoint and token revocation\n * endpoint.\n *\n * @author Joe Grandja\n * @author Steve Riesenberg\n * @since 5.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc6749#section-11.2\">11.2\n * OAuth Parameters Registry</a>\n */\npublic final class OAuth2ParameterNames {\n\n\t/**\n\t * {@code grant_type} - used in Access Token Request.\n\t */\n\tpublic static final String GRANT_TYPE = \"grant_type\";\n\n\t/**\n\t * {@code response_type} - used in Authorization Request.\n\t */\n\tpublic static final String RESPONSE_TYPE = \"response_type\";\n\n\t/**\n\t * {@code client_id} - used in Authorization Request and Access Token Request.\n\t */\n\tpublic static final String CLIENT_ID = \"client_id\";\n\n\t/**\n\t * {@code client_secret} - used in Access Token Request.\n\t */\n\tpublic static final String CLIENT_SECRET = \"client_secret\";\n\n\t/**\n\t * {@code client_assertion_type} - used in Access Token Request.\n\t * @since 5.5\n\t */\n\tpublic static final String CLIENT_ASSERTION_TYPE = \"client_assertion_type\";\n\n\t/**\n\t * {@code client_assertion} - used in Access Token Request.\n\t * @since 5.5\n\t */\n\tpublic static final String CLIENT_ASSERTION = \"client_assertion\";\n\n\t/**\n\t * {@code assertion} - used in Access Token Request.\n\t * @since 5.5\n\t */\n\tpublic static final String ASSERTION = \"assertion\";\n\n\t/**\n\t * {@code redirect_uri} - used in Authorization Request and Access Token Request.\n\t */\n\tpublic static final String REDIRECT_URI = \"redirect_uri\";\n\n\t/**\n\t * {@code scope} - used in Authorization Request, Authorization Response, Access Token\n\t * Request and Access Token Response.\n\t */\n\tpublic static final String SCOPE = \"scope\";\n\n\t/**\n\t * {@code state} - used in Authorization Request and Authorization Response.\n\t */\n\tpublic static final String STATE = \"state\";\n\n\t/**\n\t * {@code code} - used in Authorization Response and Access Token Request.\n\t */\n\tpublic static final String CODE = \"code\";\n\n\t/**\n\t * {@code access_token} - used in Authorization Response and Access Token Response.\n\t */\n\tpublic static final String ACCESS_TOKEN = \"access_token\";\n\n\t/**\n\t * {@code token_type} - used in Authorization Response and Access Token Response.\n\t */\n\tpublic static final String TOKEN_TYPE = \"token_type\";\n\n\t/**\n\t * {@code expires_in} - used in Authorization Response and Access Token Response.\n\t */\n\tpublic static final String EXPIRES_IN = \"expires_in\";\n\n\t/**\n\t * {@code refresh_token} - used in Access Token Request and Access Token Response.\n\t */\n\tpublic static final String REFRESH_TOKEN = \"refresh_token\";\n\n\t/**\n\t * {@code error} - used in Authorization Response and Access Token Response.\n\t */\n\tpublic static final String ERROR = \"error\";\n\n\t/**\n\t * {@code error_description} - used in Authorization Response and Access Token\n\t * Response.\n\t */\n\tpublic static final String ERROR_DESCRIPTION = \"error_description\";\n\n\t/**\n\t * {@code error_uri} - used in Authorization Response and Access Token Response.\n\t */\n\tpublic static final String ERROR_URI = \"error_uri\";\n\n\t/**\n\t * Non-standard parameter (used internally).\n\t */\n\tpublic static final String REGISTRATION_ID = \"registration_id\";\n\n\t/**\n\t * {@code token} - used in Token Revocation Request.\n\t * @since 5.5\n\t */\n\tpublic static final String TOKEN = \"token\";\n\n\t/**\n\t * {@code token_type_hint} - used in Token Revocation Request.\n\t * @since 5.5\n\t */\n\tpublic static final String TOKEN_TYPE_HINT = \"token_type_hint\";\n\n\t/**\n\t * {@code device_code} - used in Device Authorization Response and Device Access Token\n\t * Request.\n\t * @since 6.1\n\t */\n\tpublic static final String DEVICE_CODE = \"device_code\";\n\n\t/**\n\t * {@code user_code} - used in Device Authorization Response.\n\t * @since 6.1\n\t */\n\tpublic static final String USER_CODE = \"user_code\";\n\n\t/**\n\t * {@code verification_uri} - used in Device Authorization Response.\n\t * @since 6.1\n\t */\n\tpublic static final String VERIFICATION_URI = \"verification_uri\";\n\n\t/**\n\t * {@code verification_uri_complete} - used in Device Authorization Response.\n\t * @since 6.1\n\t */\n\tpublic static final String VERIFICATION_URI_COMPLETE = \"verification_uri_complete\";\n\n\t/**\n\t * {@code interval} - used in Device Authorization Response.\n\t * @since 6.1\n\t */\n\tpublic static final String INTERVAL = \"interval\";\n\n\t/**\n\t * {@code audience} - used in Token Exchange Access Token Request.\n\t * @since 6.3\n\t */\n\tpublic static final String AUDIENCE = \"audience\";\n\n\t/**\n\t * {@code resource} - used in Token Exchange Access Token Request.\n\t * @since 6.3\n\t */\n\tpublic static final String RESOURCE = \"resource\";\n\n\t/**\n\t * {@code requested_token_type} - used in Token Exchange Access Token Request.\n\t * @since 6.3\n\t */\n\tpublic static final String REQUESTED_TOKEN_TYPE = \"requested_token_type\";\n\n\t/**\n\t * {@code issued_token_type} - used in Token Exchange Access Token Response.\n\t * @since 6.3\n\t */\n\tpublic static final String ISSUED_TOKEN_TYPE = \"issued_token_type\";\n\n\t/**\n\t * {@code subject_token} - used in Token Exchange Access Token Request.\n\t * @since 6.3\n\t */\n\tpublic static final String SUBJECT_TOKEN = \"subject_token\";\n\n\t/**\n\t * {@code subject_token_type} - used in Token Exchange Access Token Request.\n\t * @since 6.3\n\t */\n\tpublic static final String SUBJECT_TOKEN_TYPE = \"subject_token_type\";\n\n\t/**\n\t * {@code actor_token} - used in Token Exchange Access Token Request.\n\t * @since 6.3\n\t */\n\tpublic static final String ACTOR_TOKEN = \"actor_token\";\n\n\t/**\n\t * {@code actor_token_type} - used in Token Exchange Access Token Request.\n\t * @since 6.3\n\t */\n\tpublic static final String ACTOR_TOKEN_TYPE = \"actor_token_type\";\n\n\t/**\n\t * {@code request_uri} - used in Pushed Authorization Response and Authorization\n\t * Request.\n\t * @since 6.5\n\t */\n\tpublic static final String REQUEST_URI = \"request_uri\";\n\n\tprivate OAuth2ParameterNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/PkceParameterNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\n/**\n * Standard parameter names defined in the OAuth Parameters Registry and used by the\n * authorization endpoint and token endpoint.\n *\n * @author Stephen Doxsee\n * @author Kevin Bolduc\n * @since 5.2\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7636#section-6.1\">6.1\n * OAuth Parameters Registry</a>\n */\npublic final class PkceParameterNames {\n\n\t/**\n\t * {@code code_challenge} - used in Authorization Request.\n\t */\n\tpublic static final String CODE_CHALLENGE = \"code_challenge\";\n\n\t/**\n\t * {@code code_challenge_method} - used in Authorization Request.\n\t */\n\tpublic static final String CODE_CHALLENGE_METHOD = \"code_challenge_method\";\n\n\t/**\n\t * {@code code_verifier} - used in Token Request.\n\t */\n\tpublic static final String CODE_VERIFIER = \"code_verifier\";\n\n\tprivate PkceParameterNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes that model the OAuth 2.0 Request and Response messages from the\n * Authorization Endpoint and Token Endpoint.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/GenericHttpMessageConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\n\n/**\n * {@link GenericHttpMessageConverter} implementation that delegates to a\n * {@link SmartHttpMessageConverter}.\n *\n * @param <T> the converted object type\n * @author Sebastien Deleuze\n * @since 7.0\n */\nfinal class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {\n\n\tprivate final SmartHttpMessageConverter<T> smartConverter;\n\n\tGenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {\n\t\tthis.smartConverter = smartConverter;\n\t}\n\n\t@Override\n\tpublic boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forType(type), mediaType);\n\t}\n\n\t@Override\n\tpublic T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\treturn this.smartConverter.getSupportedMediaTypes();\n\t}\n\n\t@Override\n\tpublic T read(Class<? extends T> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(clazz, inputMessage);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, contentType, outputMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/HttpMessageConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.GsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JsonbHttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Utility methods for {@link HttpMessageConverter}'s.\n *\n * @author Joe Grandja\n * @author luamas\n * @since 5.1\n */\nfinal class HttpMessageConverters {\n\n\tprivate static final boolean jacksonPresent;\n\n\tprivate static final boolean jackson2Present;\n\n\tprivate static final boolean gsonPresent;\n\n\tprivate static final boolean jsonbPresent;\n\n\tstatic {\n\t\tClassLoader classLoader = HttpMessageConverters.class.getClassLoader();\n\t\tjacksonPresent = ClassUtils.isPresent(\"tools.jackson.databind.json.JsonMapper\", classLoader);\n\t\tjackson2Present = ClassUtils.isPresent(\"com.fasterxml.jackson.databind.ObjectMapper\", classLoader)\n\t\t\t\t&& ClassUtils.isPresent(\"com.fasterxml.jackson.core.JsonGenerator\", classLoader);\n\t\tgsonPresent = ClassUtils.isPresent(\"com.google.gson.Gson\", classLoader);\n\t\tjsonbPresent = ClassUtils.isPresent(\"jakarta.json.bind.Jsonb\", classLoader);\n\t}\n\n\tprivate HttpMessageConverters() {\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tstatic @Nullable GenericHttpMessageConverter<Object> getJsonMessageConverter() {\n\t\tif (jacksonPresent) {\n\t\t\treturn new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());\n\t\t}\n\t\tif (jackson2Present) {\n\t\t\treturn new MappingJackson2HttpMessageConverter();\n\t\t}\n\t\tif (gsonPresent) {\n\t\t\treturn new GsonHttpMessageConverter();\n\t\t}\n\t\tif (jsonbPresent) {\n\t\t\treturn new JsonbHttpMessageConverter();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.endpoint.DefaultMapOAuth2AccessTokenResponseConverter;\nimport org.springframework.security.oauth2.core.endpoint.DefaultOAuth2AccessTokenResponseMapConverter;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OAuth2AccessTokenResponse OAuth 2.0 Access\n * Token Response}.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see AbstractHttpMessageConverter\n * @see OAuth2AccessTokenResponse\n */\npublic class OAuth2AccessTokenResponseHttpMessageConverter\n\t\textends AbstractHttpMessageConverter<OAuth2AccessTokenResponse> {\n\n\tprivate static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter;\n\n\tprivate Converter<Map<String, Object>, OAuth2AccessTokenResponse> accessTokenResponseConverter = new DefaultMapOAuth2AccessTokenResponseConverter();\n\n\tprivate Converter<OAuth2AccessTokenResponse, Map<String, Object>> accessTokenResponseParametersConverter = new DefaultOAuth2AccessTokenResponseMapConverter();\n\n\tpublic OAuth2AccessTokenResponseHttpMessageConverter() {\n\t\tsuper(DEFAULT_CHARSET, MediaType.APPLICATION_JSON, new MediaType(\"application\", \"*+json\"));\n\t\tGenericHttpMessageConverter<Object> converter = HttpMessageConverters.getJsonMessageConverter();\n\t\tAssert.notNull(converter, \"Unable to locate a supported JSON message converter\");\n\t\tthis.jsonMessageConverter = converter;\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OAuth2AccessTokenResponse.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OAuth2AccessTokenResponse readInternal(Class<? extends OAuth2AccessTokenResponse> clazz,\n\t\t\tHttpInputMessage inputMessage) throws HttpMessageNotReadableException {\n\t\ttry {\n\t\t\tMap<String, Object> tokenResponseParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.accessTokenResponseConverter.convert(tokenResponseParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the OAuth 2.0 Access Token Response: \" + ex.getMessage(), ex,\n\t\t\t\t\tinputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OAuth2AccessTokenResponse tokenResponse, HttpOutputMessage outputMessage)\n\t\t\tthrows HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, Object> tokenResponseParameters = this.accessTokenResponseParametersConverter\n\t\t\t\t.convert(tokenResponse);\n\t\t\tthis.jsonMessageConverter.write(tokenResponseParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the OAuth 2.0 Access Token Response: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the OAuth 2.0 Access Token Response\n\t * parameters to an {@link OAuth2AccessTokenResponse}.\n\t * @param accessTokenResponseConverter the {@link Converter} used for converting to an\n\t * {@link OAuth2AccessTokenResponse}\n\t * @since 5.6\n\t */\n\tpublic final void setAccessTokenResponseConverter(\n\t\t\tConverter<Map<String, Object>, OAuth2AccessTokenResponse> accessTokenResponseConverter) {\n\t\tAssert.notNull(accessTokenResponseConverter, \"accessTokenResponseConverter cannot be null\");\n\t\tthis.accessTokenResponseConverter = accessTokenResponseConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the\n\t * {@link OAuth2AccessTokenResponse} to a {@code Map} representation of the OAuth 2.0\n\t * Access Token Response parameters.\n\t * @param accessTokenResponseParametersConverter the {@link Converter} used for\n\t * converting to a {@code Map} representation of the Access Token Response parameters\n\t * @since 5.6\n\t */\n\tpublic final void setAccessTokenResponseParametersConverter(\n\t\t\tConverter<OAuth2AccessTokenResponse, Map<String, Object>> accessTokenResponseParametersConverter) {\n\t\tAssert.notNull(accessTokenResponseParametersConverter, \"accessTokenResponseParametersConverter cannot be null\");\n\t\tthis.accessTokenResponseParametersConverter = accessTokenResponseParametersConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2DeviceAuthorizationResponseHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2DeviceAuthorizationResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OAuth2DeviceAuthorizationResponse OAuth\n * 2.0 Device Authorization Response}.\n *\n * @author Steve Riesenberg\n * @since 6.1\n * @see AbstractHttpMessageConverter\n * @see OAuth2DeviceAuthorizationResponse\n */\npublic class OAuth2DeviceAuthorizationResponseHttpMessageConverter\n\t\textends AbstractHttpMessageConverter<OAuth2DeviceAuthorizationResponse> {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter;\n\n\tprivate Converter<Map<String, Object>, OAuth2DeviceAuthorizationResponse> deviceAuthorizationResponseConverter = new DefaultMapOAuth2DeviceAuthorizationResponseConverter();\n\n\tprivate Converter<OAuth2DeviceAuthorizationResponse, Map<String, Object>> deviceAuthorizationResponseParametersConverter = new DefaultOAuth2DeviceAuthorizationResponseMapConverter();\n\n\tpublic OAuth2DeviceAuthorizationResponseHttpMessageConverter() {\n\t\tGenericHttpMessageConverter<Object> converter = HttpMessageConverters.getJsonMessageConverter();\n\t\tAssert.notNull(converter, \"Unable to locate a supported JSON message converter\");\n\t\tthis.jsonMessageConverter = converter;\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OAuth2DeviceAuthorizationResponse.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OAuth2DeviceAuthorizationResponse readInternal(Class<? extends OAuth2DeviceAuthorizationResponse> clazz,\n\t\t\tHttpInputMessage inputMessage) throws HttpMessageNotReadableException {\n\n\t\ttry {\n\t\t\tMap<String, Object> deviceAuthorizationResponseParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.deviceAuthorizationResponseConverter.convert(deviceAuthorizationResponseParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the OAuth 2.0 Device Authorization Response: \" + ex.getMessage(), ex,\n\t\t\t\t\tinputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OAuth2DeviceAuthorizationResponse deviceAuthorizationResponse,\n\t\t\tHttpOutputMessage outputMessage) throws HttpMessageNotWritableException {\n\n\t\ttry {\n\t\t\tMap<String, Object> deviceAuthorizationResponseParameters = this.deviceAuthorizationResponseParametersConverter\n\t\t\t\t.convert(deviceAuthorizationResponse);\n\t\t\tthis.jsonMessageConverter.write(deviceAuthorizationResponseParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the OAuth 2.0 Device Authorization Response: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the OAuth 2.0 Device Authorization\n\t * Response parameters to an {@link OAuth2DeviceAuthorizationResponse}.\n\t * @param deviceAuthorizationResponseConverter the {@link Converter} used for\n\t * converting to an {@link OAuth2DeviceAuthorizationResponse}\n\t */\n\tpublic final void setDeviceAuthorizationResponseConverter(\n\t\t\tConverter<Map<String, Object>, OAuth2DeviceAuthorizationResponse> deviceAuthorizationResponseConverter) {\n\t\tAssert.notNull(deviceAuthorizationResponseConverter, \"deviceAuthorizationResponseConverter cannot be null\");\n\t\tthis.deviceAuthorizationResponseConverter = deviceAuthorizationResponseConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the\n\t * {@link OAuth2DeviceAuthorizationResponse} to a {@code Map} representation of the\n\t * OAuth 2.0 Device Authorization Response parameters.\n\t * @param deviceAuthorizationResponseParametersConverter the {@link Converter} used\n\t * for converting to a {@code Map} representation of the Device Authorization Response\n\t * parameters\n\t */\n\tpublic final void setDeviceAuthorizationResponseParametersConverter(\n\t\t\tConverter<OAuth2DeviceAuthorizationResponse, Map<String, Object>> deviceAuthorizationResponseParametersConverter) {\n\t\tAssert.notNull(deviceAuthorizationResponseParametersConverter,\n\t\t\t\t\"deviceAuthorizationResponseParametersConverter cannot be null\");\n\t\tthis.deviceAuthorizationResponseParametersConverter = deviceAuthorizationResponseParametersConverter;\n\t}\n\n\tprivate static final class DefaultMapOAuth2DeviceAuthorizationResponseConverter\n\t\t\timplements Converter<Map<String, Object>, OAuth2DeviceAuthorizationResponse> {\n\n\t\tprivate static final Set<String> DEVICE_AUTHORIZATION_RESPONSE_PARAMETER_NAMES = new HashSet<>(\n\t\t\t\tArrays.asList(OAuth2ParameterNames.DEVICE_CODE, OAuth2ParameterNames.USER_CODE,\n\t\t\t\t\t\tOAuth2ParameterNames.VERIFICATION_URI, OAuth2ParameterNames.VERIFICATION_URI_COMPLETE,\n\t\t\t\t\t\tOAuth2ParameterNames.EXPIRES_IN, OAuth2ParameterNames.INTERVAL));\n\n\t\t@Override\n\t\tpublic OAuth2DeviceAuthorizationResponse convert(Map<String, Object> parameters) {\n\t\t\tString deviceCode = getParameterValue(parameters, OAuth2ParameterNames.DEVICE_CODE);\n\t\t\tAssert.notNull(deviceCode, \"Missing required parameter: \" + OAuth2ParameterNames.DEVICE_CODE);\n\t\t\tString userCode = getParameterValue(parameters, OAuth2ParameterNames.USER_CODE);\n\t\t\tAssert.notNull(userCode, \"Missing required parameter: \" + OAuth2ParameterNames.USER_CODE);\n\t\t\tString verificationUri = getParameterValue(parameters, OAuth2ParameterNames.VERIFICATION_URI);\n\t\t\tAssert.notNull(verificationUri, \"Missing required parameter: \" + OAuth2ParameterNames.VERIFICATION_URI);\n\t\t\tString verificationUriComplete = getParameterValue(parameters,\n\t\t\t\t\tOAuth2ParameterNames.VERIFICATION_URI_COMPLETE);\n\t\t\tlong expiresIn = getParameterValue(parameters, OAuth2ParameterNames.EXPIRES_IN, 0L);\n\t\t\tlong interval = getParameterValue(parameters, OAuth2ParameterNames.INTERVAL, 0L);\n\t\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>();\n\t\t\tparameters.forEach((key, value) -> {\n\t\t\t\tif (!DEVICE_AUTHORIZATION_RESPONSE_PARAMETER_NAMES.contains(key)) {\n\t\t\t\t\tadditionalParameters.put(key, value);\n\t\t\t\t}\n\t\t\t});\n\t\t\t// @formatter:off\n\t\t\treturn OAuth2DeviceAuthorizationResponse.with(deviceCode, userCode)\n\t\t\t\t\t.verificationUri(verificationUri)\n\t\t\t\t\t.verificationUriComplete(verificationUriComplete)\n\t\t\t\t\t.expiresIn(expiresIn)\n\t\t\t\t\t.interval(interval)\n\t\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate static @Nullable String getParameterValue(Map<String, Object> parameters, String parameterName) {\n\t\t\tObject obj = parameters.get(parameterName);\n\t\t\treturn (obj != null) ? obj.toString() : null;\n\t\t}\n\n\t\tprivate static long getParameterValue(Map<String, Object> parameters, String parameterName, long defaultValue) {\n\t\t\tlong parameterValue = defaultValue;\n\n\t\t\tObject obj = parameters.get(parameterName);\n\t\t\tif (obj != null) {\n\t\t\t\t// Final classes Long and Integer do not need to be coerced\n\t\t\t\tif (obj.getClass() == Long.class) {\n\t\t\t\t\tparameterValue = (Long) obj;\n\t\t\t\t}\n\t\t\t\telse if (obj.getClass() == Integer.class) {\n\t\t\t\t\tparameterValue = (Integer) obj;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// Attempt to coerce to a long (typically from a String)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tparameterValue = Long.parseLong(obj.toString());\n\t\t\t\t\t}\n\t\t\t\t\tcatch (NumberFormatException ignored) {\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn parameterValue;\n\t\t}\n\n\t}\n\n\tprivate static final class DefaultOAuth2DeviceAuthorizationResponseMapConverter\n\t\t\timplements Converter<OAuth2DeviceAuthorizationResponse, Map<String, Object>> {\n\n\t\t@Override\n\t\tpublic Map<String, Object> convert(OAuth2DeviceAuthorizationResponse deviceAuthorizationResponse) {\n\t\t\tMap<String, Object> parameters = new HashMap<>();\n\t\t\tparameters.put(OAuth2ParameterNames.DEVICE_CODE,\n\t\t\t\t\tdeviceAuthorizationResponse.getDeviceCode().getTokenValue());\n\t\t\tparameters.put(OAuth2ParameterNames.USER_CODE, deviceAuthorizationResponse.getUserCode().getTokenValue());\n\t\t\tparameters.put(OAuth2ParameterNames.VERIFICATION_URI, deviceAuthorizationResponse.getVerificationUri());\n\t\t\tif (StringUtils.hasText(deviceAuthorizationResponse.getVerificationUriComplete())) {\n\t\t\t\tparameters.put(OAuth2ParameterNames.VERIFICATION_URI_COMPLETE,\n\t\t\t\t\t\tdeviceAuthorizationResponse.getVerificationUriComplete());\n\t\t\t}\n\t\t\tparameters.put(OAuth2ParameterNames.EXPIRES_IN, getExpiresIn(deviceAuthorizationResponse));\n\t\t\tif (deviceAuthorizationResponse.getInterval() > 0) {\n\t\t\t\tparameters.put(OAuth2ParameterNames.INTERVAL, deviceAuthorizationResponse.getInterval());\n\t\t\t}\n\t\t\tif (!CollectionUtils.isEmpty(deviceAuthorizationResponse.getAdditionalParameters())) {\n\t\t\t\tparameters.putAll(deviceAuthorizationResponse.getAdditionalParameters());\n\t\t\t}\n\t\t\treturn parameters;\n\t\t}\n\n\t\tprivate static long getExpiresIn(OAuth2DeviceAuthorizationResponse deviceAuthorizationResponse) {\n\t\t\tif (deviceAuthorizationResponse.getDeviceCode().getExpiresAt() != null) {\n\t\t\t\tInstant issuedAt = (deviceAuthorizationResponse.getDeviceCode().getIssuedAt() != null)\n\t\t\t\t\t\t? deviceAuthorizationResponse.getDeviceCode().getIssuedAt() : Instant.now();\n\t\t\t\treturn ChronoUnit.SECONDS.between(issuedAt, deviceAuthorizationResponse.getDeviceCode().getExpiresAt());\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link HttpMessageConverter} for an {@link OAuth2Error OAuth 2.0 Error}.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see AbstractHttpMessageConverter\n * @see OAuth2Error\n */\npublic class OAuth2ErrorHttpMessageConverter extends AbstractHttpMessageConverter<OAuth2Error> {\n\n\tprivate static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final GenericHttpMessageConverter<Object> jsonMessageConverter;\n\n\tprotected Converter<Map<String, String>, OAuth2Error> errorConverter = new OAuth2ErrorConverter();\n\n\tprotected Converter<OAuth2Error, Map<String, String>> errorParametersConverter = new OAuth2ErrorParametersConverter();\n\n\tpublic OAuth2ErrorHttpMessageConverter() {\n\t\tsuper(DEFAULT_CHARSET, MediaType.APPLICATION_JSON, new MediaType(\"application\", \"*+json\"));\n\t\tGenericHttpMessageConverter<Object> converter = HttpMessageConverters.getJsonMessageConverter();\n\t\tAssert.notNull(converter, \"Unable to locate a supported JSON message converter\");\n\t\tthis.jsonMessageConverter = converter;\n\t}\n\n\t@Override\n\tprotected boolean supports(Class<?> clazz) {\n\t\treturn OAuth2Error.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tprotected OAuth2Error readInternal(Class<? extends OAuth2Error> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows HttpMessageNotReadableException {\n\t\ttry {\n\t\t\t// gh-8157: Parse parameter values as Object in order to handle potential JSON\n\t\t\t// Object and then convert values to String\n\t\t\tMap<String, Object> errorParameters = (Map<String, Object>) this.jsonMessageConverter\n\t\t\t\t.read(STRING_OBJECT_MAP.getType(), null, inputMessage);\n\t\t\treturn this.errorConverter.convert(errorParameters.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.collect(Collectors.toMap(Map.Entry::getKey, (entry) -> String.valueOf(entry.getValue()))));\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the OAuth 2.0 Error: \" + ex.getMessage(), ex, inputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OAuth2Error oauth2Error, HttpOutputMessage outputMessage)\n\t\t\tthrows HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, String> errorParameters = this.errorParametersConverter.convert(oauth2Error);\n\t\t\tthis.jsonMessageConverter.write(errorParameters, STRING_OBJECT_MAP.getType(), MediaType.APPLICATION_JSON,\n\t\t\t\t\toutputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the OAuth 2.0 Error: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the OAuth 2.0 Error parameters to an\n\t * {@link OAuth2Error}.\n\t * @param errorConverter the {@link Converter} used for converting to an\n\t * {@link OAuth2Error}\n\t */\n\tpublic final void setErrorConverter(Converter<Map<String, String>, OAuth2Error> errorConverter) {\n\t\tAssert.notNull(errorConverter, \"errorConverter cannot be null\");\n\t\tthis.errorConverter = errorConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the {@link OAuth2Error} to a\n\t * {@code Map} representation of the OAuth 2.0 Error parameters.\n\t * @param errorParametersConverter the {@link Converter} used for converting to a\n\t * {@code Map} representation of the Error parameters\n\t */\n\tpublic final void setErrorParametersConverter(\n\t\t\tConverter<OAuth2Error, Map<String, String>> errorParametersConverter) {\n\t\tAssert.notNull(errorParametersConverter, \"errorParametersConverter cannot be null\");\n\t\tthis.errorParametersConverter = errorParametersConverter;\n\t}\n\n\t/**\n\t * A {@link Converter} that converts the provided OAuth 2.0 Error parameters to an\n\t * {@link OAuth2Error}.\n\t */\n\tprivate static class OAuth2ErrorConverter implements Converter<Map<String, String>, OAuth2Error> {\n\n\t\t@Override\n\t\tpublic OAuth2Error convert(Map<String, String> parameters) {\n\t\t\tString errorCode = parameters.get(OAuth2ParameterNames.ERROR);\n\t\t\tAssert.hasText(errorCode, \"errorCode cannot be empty\");\n\t\t\tString errorDescription = parameters.get(OAuth2ParameterNames.ERROR_DESCRIPTION);\n\t\t\tString errorUri = parameters.get(OAuth2ParameterNames.ERROR_URI);\n\t\t\treturn new OAuth2Error(errorCode, errorDescription, errorUri);\n\t\t}\n\n\t}\n\n\t/**\n\t * A {@link Converter} that converts the provided {@link OAuth2Error} to a {@code Map}\n\t * representation of OAuth 2.0 Error parameters.\n\t */\n\tprivate static class OAuth2ErrorParametersConverter implements Converter<OAuth2Error, Map<String, String>> {\n\n\t\t@Override\n\t\tpublic Map<String, String> convert(OAuth2Error oauth2Error) {\n\t\t\tMap<String, String> parameters = new HashMap<>();\n\t\t\tparameters.put(OAuth2ParameterNames.ERROR, oauth2Error.getErrorCode());\n\t\t\tif (StringUtils.hasText(oauth2Error.getDescription())) {\n\t\t\t\tparameters.put(OAuth2ParameterNames.ERROR_DESCRIPTION, oauth2Error.getDescription());\n\t\t\t}\n\t\t\tif (StringUtils.hasText(oauth2Error.getUri())) {\n\t\t\t\tparameters.put(OAuth2ParameterNames.ERROR_URI, oauth2Error.getUri());\n\t\t\t}\n\t\t\treturn parameters;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/converter/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * HTTP message converters for OAuth 2.0 and OpenID Connect protocol messages.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/http/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes that provide HTTP message conversion for OAuth 2.0 and OpenID Connect.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.http;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/AddressStandardClaim.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * The Address Claim represents a physical mailing address defined by the OpenID Connect\n * Core 1.0 specification that can be returned either in the UserInfo Response or the ID\n * Token.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#AddressClaim\">Address Claim</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse\">UserInfo\n * Response</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IDToken\">ID Token</a>\n */\npublic interface AddressStandardClaim {\n\n\t/**\n\t * Returns the full mailing address, formatted for display, or {@code null} if it does\n\t * not exist.\n\t * @return the full mailing address, or {@code null} if it does not exist\n\t */\n\t@Nullable String getFormatted();\n\n\t/**\n\t * Returns the full street address, which may include house number, street name, P.O.\n\t * Box, etc., or {@code null} if it does not exist.\n\t * @return the full street address, or {@code null} if it does not exist\n\t */\n\t@Nullable String getStreetAddress();\n\n\t/**\n\t * Returns the city or locality, or {@code null} if it does not exist.\n\t * @return the city or locality, or {@code null} if it does not exist\n\t */\n\t@Nullable String getLocality();\n\n\t/**\n\t * Returns the state, province, prefecture, or region, or {@code null} if it does not\n\t * exist.\n\t * @return the state, province, prefecture, or region, or {@code null} if it does not\n\t * exist\n\t */\n\t@Nullable String getRegion();\n\n\t/**\n\t * Returns the zip code or postal code, or {@code null} if it does not exist.\n\t * @return the zip code or postal code, or {@code null} if it does not exist\n\t */\n\t@Nullable String getPostalCode();\n\n\t/**\n\t * Returns the country, or {@code null} if it does not exist.\n\t * @return the country, or {@code null} if it does not exist\n\t */\n\t@Nullable String getCountry();\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/DefaultAddressStandardClaim.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * The default implementation of an {@link AddressStandardClaim Address Claim}.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AddressStandardClaim\n */\npublic final class DefaultAddressStandardClaim implements AddressStandardClaim {\n\n\tprivate @Nullable String formatted;\n\n\tprivate @Nullable String streetAddress;\n\n\tprivate @Nullable String locality;\n\n\tprivate @Nullable String region;\n\n\tprivate @Nullable String postalCode;\n\n\tprivate @Nullable String country;\n\n\tprivate DefaultAddressStandardClaim() {\n\t}\n\n\t@Override\n\tpublic @Nullable String getFormatted() {\n\t\treturn this.formatted;\n\t}\n\n\t@Override\n\tpublic @Nullable String getStreetAddress() {\n\t\treturn this.streetAddress;\n\t}\n\n\t@Override\n\tpublic @Nullable String getLocality() {\n\t\treturn this.locality;\n\t}\n\n\t@Override\n\tpublic @Nullable String getRegion() {\n\t\treturn this.region;\n\t}\n\n\t@Override\n\tpublic @Nullable String getPostalCode() {\n\t\treturn this.postalCode;\n\t}\n\n\t@Override\n\tpublic @Nullable String getCountry() {\n\t\treturn this.country;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || !AddressStandardClaim.class.isAssignableFrom(obj.getClass())) {\n\t\t\treturn false;\n\t\t}\n\t\tAddressStandardClaim other = (AddressStandardClaim) obj;\n\t\tif ((this.getFormatted() != null) ? !this.getFormatted().equals(other.getFormatted())\n\t\t\t\t: other.getFormatted() != null) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.getStreetAddress() != null) ? !this.getStreetAddress().equals(other.getStreetAddress())\n\t\t\t\t: other.getStreetAddress() != null) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.getLocality() != null) ? !this.getLocality().equals(other.getLocality())\n\t\t\t\t: other.getLocality() != null) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.getRegion() != null) ? !this.getRegion().equals(other.getRegion()) : other.getRegion() != null) {\n\t\t\treturn false;\n\t\t}\n\t\tif ((this.getPostalCode() != null) ? !this.getPostalCode().equals(other.getPostalCode())\n\t\t\t\t: other.getPostalCode() != null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn (this.getCountry() != null) ? this.getCountry().equals(other.getCountry()) : other.getCountry() == null;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = (this.getFormatted() != null) ? this.getFormatted().hashCode() : 0;\n\t\tresult = 31 * result + ((this.getStreetAddress() != null) ? this.getStreetAddress().hashCode() : 0);\n\t\tresult = 31 * result + ((this.getLocality() != null) ? this.getLocality().hashCode() : 0);\n\t\tresult = 31 * result + ((this.getRegion() != null) ? this.getRegion().hashCode() : 0);\n\t\tresult = 31 * result + ((this.getPostalCode() != null) ? this.getPostalCode().hashCode() : 0);\n\t\tresult = 31 * result + ((this.getCountry() != null) ? this.getCountry().hashCode() : 0);\n\t\treturn result;\n\t}\n\n\t/**\n\t * A builder for {@link DefaultAddressStandardClaim}.\n\t */\n\tpublic static class Builder {\n\n\t\tprivate static final String FORMATTED_FIELD_NAME = \"formatted\";\n\n\t\tprivate static final String STREET_ADDRESS_FIELD_NAME = \"street_address\";\n\n\t\tprivate static final String LOCALITY_FIELD_NAME = \"locality\";\n\n\t\tprivate static final String REGION_FIELD_NAME = \"region\";\n\n\t\tprivate static final String POSTAL_CODE_FIELD_NAME = \"postal_code\";\n\n\t\tprivate static final String COUNTRY_FIELD_NAME = \"country\";\n\n\t\tprivate @Nullable String formatted;\n\n\t\tprivate @Nullable String streetAddress;\n\n\t\tprivate @Nullable String locality;\n\n\t\tprivate @Nullable String region;\n\n\t\tprivate @Nullable String postalCode;\n\n\t\tprivate @Nullable String country;\n\n\t\t/**\n\t\t * Default constructor.\n\t\t */\n\t\tpublic Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Constructs and initializes the address attributes using the provided\n\t\t * {@code addressFields}.\n\t\t * @param addressFields the fields used to initialize the address attributes\n\t\t */\n\t\tpublic Builder(Map<String, Object> addressFields) {\n\t\t\tthis.formatted((String) addressFields.get(FORMATTED_FIELD_NAME));\n\t\t\tthis.streetAddress((String) addressFields.get(STREET_ADDRESS_FIELD_NAME));\n\t\t\tthis.locality((String) addressFields.get(LOCALITY_FIELD_NAME));\n\t\t\tthis.region((String) addressFields.get(REGION_FIELD_NAME));\n\t\t\tthis.postalCode((String) addressFields.get(POSTAL_CODE_FIELD_NAME));\n\t\t\tthis.country((String) addressFields.get(COUNTRY_FIELD_NAME));\n\t\t}\n\n\t\t/**\n\t\t * Sets the full mailing address, formatted for display.\n\t\t * @param formatted the full mailing address, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder formatted(@Nullable String formatted) {\n\t\t\tthis.formatted = formatted;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the full street address, which may include house number, street name, P.O.\n\t\t * Box, etc.\n\t\t * @param streetAddress the full street address, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder streetAddress(@Nullable String streetAddress) {\n\t\t\tthis.streetAddress = streetAddress;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the city or locality.\n\t\t * @param locality the city or locality, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder locality(@Nullable String locality) {\n\t\t\tthis.locality = locality;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the state, province, prefecture, or region.\n\t\t * @param region the state, province, prefecture, or region, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder region(@Nullable String region) {\n\t\t\tthis.region = region;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the zip code or postal code.\n\t\t * @param postalCode the zip code or postal code, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder postalCode(@Nullable String postalCode) {\n\t\t\tthis.postalCode = postalCode;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the country.\n\t\t * @param country the country, may be {@code null}\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder country(@Nullable String country) {\n\t\t\tthis.country = country;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link DefaultAddressStandardClaim}.\n\t\t * @return a {@link AddressStandardClaim}\n\t\t */\n\t\tpublic AddressStandardClaim build() {\n\t\t\tDefaultAddressStandardClaim address = new DefaultAddressStandardClaim();\n\t\t\taddress.formatted = this.formatted;\n\t\t\taddress.streetAddress = this.streetAddress;\n\t\t\taddress.locality = this.locality;\n\t\t\taddress.region = this.region;\n\t\t\taddress.postalCode = this.postalCode;\n\t\t\taddress.country = this.country;\n\t\t\treturn address;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/IdTokenClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\n\n/**\n * A {@link ClaimAccessor} for the &quot;claims&quot; that can be returned in the ID\n * Token, which provides information about the authentication of an End-User by an\n * Authorization Server.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see ClaimAccessor\n * @see StandardClaimAccessor\n * @see StandardClaimNames\n * @see IdTokenClaimNames\n * @see OidcIdToken\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IDToken\">ID Token</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\">Standard\n * Claims</a>\n */\npublic interface IdTokenClaimAccessor extends StandardClaimAccessor {\n\n\t/**\n\t * Returns the Issuer identifier {@code (iss)}, or {@code null} if it does not exist.\n\t * @return the Issuer identifier, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable URL getIssuer() {\n\t\treturn this.getClaimAsURL(IdTokenClaimNames.ISS);\n\t}\n\n\t/**\n\t * Returns the Subject identifier {@code (sub)}, or {@code null} if it does not exist.\n\t * @return the Subject identifier, or {@code null} if it does not exist\n\t */\n\t@Override\n\tdefault @Nullable String getSubject() {\n\t\treturn this.getClaimAsString(IdTokenClaimNames.SUB);\n\t}\n\n\t/**\n\t * Returns the Audience(s) {@code (aud)} that this ID Token is intended for, or\n\t * {@code null} if it does not exist.\n\t * @return the Audience(s) that this ID Token is intended for, or {@code null} if it\n\t * does not exist\n\t */\n\tdefault @Nullable List<String> getAudience() {\n\t\treturn this.getClaimAsStringList(IdTokenClaimNames.AUD);\n\t}\n\n\t/**\n\t * Returns the Expiration time {@code (exp)} on or after which the ID Token MUST NOT\n\t * be accepted, or {@code null} if it does not exist.\n\t * @return the Expiration time on or after which the ID Token MUST NOT be accepted, or\n\t * {@code null} if it does not exist\n\t */\n\tdefault @Nullable Instant getExpiresAt() {\n\t\treturn this.getClaimAsInstant(IdTokenClaimNames.EXP);\n\t}\n\n\t/**\n\t * Returns the time at which the ID Token was issued {@code (iat)}, or {@code null} if\n\t * it does not exist.\n\t * @return the time at which the ID Token was issued, or {@code null} if it does not\n\t * exist\n\t */\n\tdefault @Nullable Instant getIssuedAt() {\n\t\treturn this.getClaimAsInstant(IdTokenClaimNames.IAT);\n\t}\n\n\t/**\n\t * Returns the time when the End-User authentication occurred {@code (auth_time)}, or\n\t * {@code null} if it does not exist.\n\t * @return the time when the End-User authentication occurred, or {@code null} if it\n\t * does not exist\n\t */\n\tdefault @Nullable Instant getAuthenticatedAt() {\n\t\treturn this.getClaimAsInstant(IdTokenClaimNames.AUTH_TIME);\n\t}\n\n\t/**\n\t * Returns a {@code String} value {@code (nonce)} used to associate a Client session\n\t * with an ID Token, and to mitigate replay attacks, or {@code null} if it does not\n\t * exist.\n\t * @return the nonce used to associate a Client session with an ID Token, or\n\t * {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getNonce() {\n\t\treturn this.getClaimAsString(IdTokenClaimNames.NONCE);\n\t}\n\n\t/**\n\t * Returns the Authentication Context Class Reference {@code (acr)}, or {@code null}\n\t * if it does not exist.\n\t * @return the Authentication Context Class Reference, or {@code null} if it does not\n\t * exist\n\t */\n\tdefault @Nullable String getAuthenticationContextClass() {\n\t\treturn this.getClaimAsString(IdTokenClaimNames.ACR);\n\t}\n\n\t/**\n\t * Returns the Authentication Methods References {@code (amr)}, or {@code null} if it\n\t * does not exist.\n\t * @return the Authentication Methods References, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable List<String> getAuthenticationMethods() {\n\t\treturn this.getClaimAsStringList(IdTokenClaimNames.AMR);\n\t}\n\n\t/**\n\t * Returns the Authorized party {@code (azp)} to which the ID Token was issued, or\n\t * {@code null} if it does not exist.\n\t * @return the Authorized party to which the ID Token was issued, or {@code null} if\n\t * it does not exist\n\t */\n\tdefault @Nullable String getAuthorizedParty() {\n\t\treturn this.getClaimAsString(IdTokenClaimNames.AZP);\n\t}\n\n\t/**\n\t * Returns the Access Token hash value {@code (at_hash)}, or {@code null} if it does\n\t * not exist.\n\t * @return the Access Token hash value, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getAccessTokenHash() {\n\t\treturn this.getClaimAsString(IdTokenClaimNames.AT_HASH);\n\t}\n\n\t/**\n\t * Returns the Authorization Code hash value {@code (c_hash)}, or {@code null} if it\n\t * does not exist.\n\t * @return the Authorization Code hash value, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getAuthorizationCodeHash() {\n\t\treturn this.getClaimAsString(IdTokenClaimNames.C_HASH);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/IdTokenClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\n/**\n * The names of the &quot;claims&quot; defined by the OpenID Connect Core 1.0\n * specification that can be returned in the ID Token.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OidcIdToken\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IDToken\">ID Token</a>\n */\n\npublic final class IdTokenClaimNames {\n\n\t/**\n\t * {@code iss} - the Issuer identifier\n\t */\n\tpublic static final String ISS = \"iss\";\n\n\t/**\n\t * {@code sub} - the Subject identifier\n\t */\n\tpublic static final String SUB = \"sub\";\n\n\t/**\n\t * {@code aud} - the Audience(s) that the ID Token is intended for\n\t */\n\tpublic static final String AUD = \"aud\";\n\n\t/**\n\t * {@code exp} - the Expiration time on or after which the ID Token MUST NOT be\n\t * accepted\n\t */\n\tpublic static final String EXP = \"exp\";\n\n\t/**\n\t * {@code iat} - the time at which the ID Token was issued\n\t */\n\tpublic static final String IAT = \"iat\";\n\n\t/**\n\t * {@code auth_time} - the time when the End-User authentication occurred\n\t */\n\tpublic static final String AUTH_TIME = \"auth_time\";\n\n\t/**\n\t * {@code nonce} - a {@code String} value used to associate a Client session with an\n\t * ID Token, and to mitigate replay attacks.\n\t */\n\tpublic static final String NONCE = \"nonce\";\n\n\t/**\n\t * {@code acr} - the Authentication Context Class Reference\n\t */\n\tpublic static final String ACR = \"acr\";\n\n\t/**\n\t * {@code amr} - the Authentication Methods References\n\t */\n\tpublic static final String AMR = \"amr\";\n\n\t/**\n\t * {@code azp} - the Authorized party to which the ID Token was issued\n\t */\n\tpublic static final String AZP = \"azp\";\n\n\t/**\n\t * {@code at_hash} - the Access Token hash value\n\t */\n\tpublic static final String AT_HASH = \"at_hash\";\n\n\t/**\n\t * {@code c_hash} - the Authorization Code hash value\n\t */\n\tpublic static final String C_HASH = \"c_hash\";\n\n\tprivate IdTokenClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/OidcIdToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.io.Serial;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.AbstractOAuth2Token;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AbstractOAuth2Token} representing an OpenID Connect Core\n * 1.0 ID Token.\n *\n * <p>\n * The {@code OidcIdToken} is a security token that contains &quot;claims&quot; about the\n * authentication of an End-User by an Authorization Server.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AbstractOAuth2Token\n * @see IdTokenClaimAccessor\n * @see StandardClaimAccessor\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IDToken\">ID Token</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\">Standard\n * Claims</a>\n */\npublic class OidcIdToken extends AbstractOAuth2Token implements IdTokenClaimAccessor {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1840734870428968020L;\n\n\tprivate final Map<String, Object> claims;\n\n\t/**\n\t * Constructs a {@code OidcIdToken} using the provided parameters.\n\t * @param tokenValue the ID Token value\n\t * @param issuedAt the time at which the ID Token was issued {@code (iat)}, may be\n\t * {@code null}\n\t * @param expiresAt the expiration time {@code (exp)} on or after which the ID Token\n\t * MUST NOT be accepted, may be {@code null}\n\t * @param claims the claims about the authentication of the End-User\n\t */\n\tpublic OidcIdToken(String tokenValue, @Nullable Instant issuedAt, @Nullable Instant expiresAt,\n\t\t\tMap<String, Object> claims) {\n\t\tsuper(tokenValue, issuedAt, expiresAt);\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\tthis.claims = Collections.unmodifiableMap(new LinkedHashMap<>(claims));\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * Create a {@link Builder} based on the given token value\n\t * @param tokenValue the token value to use\n\t * @return the {@link Builder} for further configuration\n\t * @since 5.3\n\t */\n\tpublic static Builder withTokenValue(String tokenValue) {\n\t\treturn new Builder(tokenValue);\n\t}\n\n\t/**\n\t * A builder for {@link OidcIdToken}s\n\t *\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate String tokenValue;\n\n\t\tprivate final Map<String, Object> claims = new LinkedHashMap<>();\n\n\t\tprivate Builder(String tokenValue) {\n\t\t\tthis.tokenValue = tokenValue;\n\t\t}\n\n\t\t/**\n\t\t * Use this token value in the resulting {@link OidcIdToken}\n\t\t * @param tokenValue The token value to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder tokenValue(String tokenValue) {\n\t\t\tthis.tokenValue = tokenValue;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this claim in the resulting {@link OidcIdToken}\n\t\t * @param name The claim name\n\t\t * @param value The claim value\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claim(String name, Object value) {\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #claim(String, Object)} declared so far with\n\t\t * the possibility to add, replace, or remove.\n\t\t * @param claimsConsumer the consumer\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this access token hash in the resulting {@link OidcIdToken}\n\t\t * @param accessTokenHash The access token hash to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder accessTokenHash(String accessTokenHash) {\n\t\t\treturn claim(IdTokenClaimNames.AT_HASH, accessTokenHash);\n\t\t}\n\n\t\t/**\n\t\t * Use this audience in the resulting {@link OidcIdToken}\n\t\t * @param audience The audience(s) to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder audience(Collection<String> audience) {\n\t\t\treturn claim(IdTokenClaimNames.AUD, audience);\n\t\t}\n\n\t\t/**\n\t\t * Use this authentication {@link Instant} in the resulting {@link OidcIdToken}\n\t\t * @param authenticatedAt The authentication {@link Instant} to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder authTime(Instant authenticatedAt) {\n\t\t\treturn claim(IdTokenClaimNames.AUTH_TIME, authenticatedAt);\n\t\t}\n\n\t\t/**\n\t\t * Use this authentication context class reference in the resulting\n\t\t * {@link OidcIdToken}\n\t\t * @param authenticationContextClass The authentication context class reference to\n\t\t * use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder authenticationContextClass(String authenticationContextClass) {\n\t\t\treturn claim(IdTokenClaimNames.ACR, authenticationContextClass);\n\t\t}\n\n\t\t/**\n\t\t * Use these authentication methods in the resulting {@link OidcIdToken}\n\t\t * @param authenticationMethods The authentication methods to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder authenticationMethods(List<String> authenticationMethods) {\n\t\t\treturn claim(IdTokenClaimNames.AMR, authenticationMethods);\n\t\t}\n\n\t\t/**\n\t\t * Use this authorization code hash in the resulting {@link OidcIdToken}\n\t\t * @param authorizationCodeHash The authorization code hash to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder authorizationCodeHash(String authorizationCodeHash) {\n\t\t\treturn claim(IdTokenClaimNames.C_HASH, authorizationCodeHash);\n\t\t}\n\n\t\t/**\n\t\t * Use this authorized party in the resulting {@link OidcIdToken}\n\t\t * @param authorizedParty The authorized party to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder authorizedParty(String authorizedParty) {\n\t\t\treturn claim(IdTokenClaimNames.AZP, authorizedParty);\n\t\t}\n\n\t\t/**\n\t\t * Use this expiration in the resulting {@link OidcIdToken}\n\t\t * @param expiresAt The expiration to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder expiresAt(Instant expiresAt) {\n\t\t\treturn this.claim(IdTokenClaimNames.EXP, expiresAt);\n\t\t}\n\n\t\t/**\n\t\t * Use this issued-at timestamp in the resulting {@link OidcIdToken}\n\t\t * @param issuedAt The issued-at timestamp to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder issuedAt(Instant issuedAt) {\n\t\t\treturn this.claim(IdTokenClaimNames.IAT, issuedAt);\n\t\t}\n\n\t\t/**\n\t\t * Use this issuer in the resulting {@link OidcIdToken}\n\t\t * @param issuer The issuer to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder issuer(String issuer) {\n\t\t\treturn this.claim(IdTokenClaimNames.ISS, issuer);\n\t\t}\n\n\t\t/**\n\t\t * Use this nonce in the resulting {@link OidcIdToken}\n\t\t * @param nonce The nonce to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder nonce(String nonce) {\n\t\t\treturn this.claim(IdTokenClaimNames.NONCE, nonce);\n\t\t}\n\n\t\t/**\n\t\t * Use this subject in the resulting {@link OidcIdToken}\n\t\t * @param subject The subject to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder subject(String subject) {\n\t\t\treturn this.claim(IdTokenClaimNames.SUB, subject);\n\t\t}\n\n\t\t/**\n\t\t * Build the {@link OidcIdToken}\n\t\t * @return The constructed {@link OidcIdToken}\n\t\t */\n\t\tpublic OidcIdToken build() {\n\t\t\tInstant iat = toInstant(this.claims.get(IdTokenClaimNames.IAT));\n\t\t\tInstant exp = toInstant(this.claims.get(IdTokenClaimNames.EXP));\n\t\t\treturn new OidcIdToken(this.tokenValue, iat, exp, this.claims);\n\t\t}\n\n\t\tprivate @Nullable Instant toInstant(@Nullable Object timestamp) {\n\t\t\tif (timestamp != null) {\n\t\t\t\tAssert.isInstanceOf(Instant.class, timestamp, \"timestamps must be of type Instant\");\n\t\t\t}\n\t\t\treturn (Instant) timestamp;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/OidcScopes.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\n/**\n * The scope values defined by the OpenID Connect Core 1.0 specification that can be used\n * to request {@link StandardClaimNames claims}.\n * <p>\n * The scope(s) associated to an {@link OAuth2AccessToken} determine what claims\n * (resources) will be available when they are used to access OAuth 2.0 Protected\n * Endpoints, such as the UserInfo Endpoint.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see StandardClaimNames\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims\">Requesting Claims\n * using Scope Values</a>\n */\npublic final class OidcScopes {\n\n\t/**\n\t * The {@code openid} scope is required for OpenID Connect Authentication Requests.\n\t */\n\tpublic static final String OPENID = \"openid\";\n\n\t/**\n\t * The {@code profile} scope requests access to the default profile claims, which are:\n\t * {@code name, family_name, given_name, middle_name, nickname, preferred_username,\n\t * profile, picture, website, gender, birthdate, zoneinfo, locale, updated_at}.\n\t */\n\tpublic static final String PROFILE = \"profile\";\n\n\t/**\n\t * The {@code email} scope requests access to the {@code email} and\n\t * {@code email_verified} claims.\n\t */\n\tpublic static final String EMAIL = \"email\";\n\n\t/**\n\t * The {@code address} scope requests access to the {@code address} claim.\n\t */\n\tpublic static final String ADDRESS = \"address\";\n\n\t/**\n\t * The {@code phone} scope requests access to the {@code phone_number} and\n\t * {@code phone_number_verified} claims.\n\t */\n\tpublic static final String PHONE = \"phone\";\n\n\tprivate OidcScopes() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/OidcUserInfo.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.io.Serializable;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.util.Assert;\n\n/**\n * A representation of a UserInfo Response that is returned from the OAuth 2.0 Protected\n * Resource UserInfo Endpoint.\n *\n * <p>\n * The {@code OidcUserInfo} contains a set of &quot;Standard Claims&quot; about the\n * authentication of an End-User.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see StandardClaimAccessor\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse\">UserInfo\n * Response</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#UserInfo\">UserInfo Endpoint</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\">Standard\n * Claims</a>\n */\npublic class OidcUserInfo implements StandardClaimAccessor, Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final Map<String, Object> claims;\n\n\t/**\n\t * Constructs a {@code OidcUserInfo} using the provided parameters.\n\t * @param claims the claims about the authentication of the End-User\n\t */\n\tpublic OidcUserInfo(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\tthis.claims = Collections.unmodifiableMap(new LinkedHashMap<>(claims));\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOidcUserInfo that = (OidcUserInfo) obj;\n\t\treturn this.getClaims().equals(that.getClaims());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.getClaims().hashCode();\n\t}\n\n\t/**\n\t * Create a {@link Builder}\n\t * @return the {@link Builder} for further configuration\n\t * @since 5.3\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * A builder for {@link OidcUserInfo}s\n\t *\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final Map<String, Object> claims = new LinkedHashMap<>();\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Use this claim in the resulting {@link OidcUserInfo}\n\t\t * @param name The claim name\n\t\t * @param value The claim value\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claim(String name, Object value) {\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #claim(String, Object)} declared so far with\n\t\t * the possibility to add, replace, or remove.\n\t\t * @param claimsConsumer the consumer\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this address in the resulting {@link OidcUserInfo}\n\t\t * @param address The address to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder address(String address) {\n\t\t\treturn this.claim(StandardClaimNames.ADDRESS, address);\n\t\t}\n\n\t\t/**\n\t\t * Use this birthdate in the resulting {@link OidcUserInfo}\n\t\t * @param birthdate The birthdate to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder birthdate(String birthdate) {\n\t\t\treturn this.claim(StandardClaimNames.BIRTHDATE, birthdate);\n\t\t}\n\n\t\t/**\n\t\t * Use this email in the resulting {@link OidcUserInfo}\n\t\t * @param email The email to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder email(String email) {\n\t\t\treturn this.claim(StandardClaimNames.EMAIL, email);\n\t\t}\n\n\t\t/**\n\t\t * Use this verified-email indicator in the resulting {@link OidcUserInfo}\n\t\t * @param emailVerified The verified-email indicator to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder emailVerified(Boolean emailVerified) {\n\t\t\treturn this.claim(StandardClaimNames.EMAIL_VERIFIED, emailVerified);\n\t\t}\n\n\t\t/**\n\t\t * Use this family name in the resulting {@link OidcUserInfo}\n\t\t * @param familyName The family name to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder familyName(String familyName) {\n\t\t\treturn claim(StandardClaimNames.FAMILY_NAME, familyName);\n\t\t}\n\n\t\t/**\n\t\t * Use this gender in the resulting {@link OidcUserInfo}\n\t\t * @param gender The gender to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder gender(String gender) {\n\t\t\treturn this.claim(StandardClaimNames.GENDER, gender);\n\t\t}\n\n\t\t/**\n\t\t * Use this given name in the resulting {@link OidcUserInfo}\n\t\t * @param givenName The given name to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder givenName(String givenName) {\n\t\t\treturn claim(StandardClaimNames.GIVEN_NAME, givenName);\n\t\t}\n\n\t\t/**\n\t\t * Use this locale in the resulting {@link OidcUserInfo}\n\t\t * @param locale The locale to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder locale(String locale) {\n\t\t\treturn this.claim(StandardClaimNames.LOCALE, locale);\n\t\t}\n\n\t\t/**\n\t\t * Use this middle name in the resulting {@link OidcUserInfo}\n\t\t * @param middleName The middle name to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder middleName(String middleName) {\n\t\t\treturn claim(StandardClaimNames.MIDDLE_NAME, middleName);\n\t\t}\n\n\t\t/**\n\t\t * Use this name in the resulting {@link OidcUserInfo}\n\t\t * @param name The name to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder name(String name) {\n\t\t\treturn claim(StandardClaimNames.NAME, name);\n\t\t}\n\n\t\t/**\n\t\t * Use this nickname in the resulting {@link OidcUserInfo}\n\t\t * @param nickname The nickname to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder nickname(String nickname) {\n\t\t\treturn claim(StandardClaimNames.NICKNAME, nickname);\n\t\t}\n\n\t\t/**\n\t\t * Use this picture in the resulting {@link OidcUserInfo}\n\t\t * @param picture The picture to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder picture(String picture) {\n\t\t\treturn this.claim(StandardClaimNames.PICTURE, picture);\n\t\t}\n\n\t\t/**\n\t\t * Use this phone number in the resulting {@link OidcUserInfo}\n\t\t * @param phoneNumber The phone number to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder phoneNumber(String phoneNumber) {\n\t\t\treturn this.claim(StandardClaimNames.PHONE_NUMBER, phoneNumber);\n\t\t}\n\n\t\t/**\n\t\t * Use this verified-phone-number indicator in the resulting {@link OidcUserInfo}\n\t\t * @param phoneNumberVerified The verified-phone-number indicator to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t * @since 5.8\n\t\t */\n\t\tpublic Builder phoneNumberVerified(Boolean phoneNumberVerified) {\n\t\t\treturn this.claim(StandardClaimNames.PHONE_NUMBER_VERIFIED, phoneNumberVerified);\n\t\t}\n\n\t\t/**\n\t\t * Use this preferred username in the resulting {@link OidcUserInfo}\n\t\t * @param preferredUsername The preferred username to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder preferredUsername(String preferredUsername) {\n\t\t\treturn claim(StandardClaimNames.PREFERRED_USERNAME, preferredUsername);\n\t\t}\n\n\t\t/**\n\t\t * Use this profile in the resulting {@link OidcUserInfo}\n\t\t * @param profile The profile to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder profile(String profile) {\n\t\t\treturn claim(StandardClaimNames.PROFILE, profile);\n\t\t}\n\n\t\t/**\n\t\t * Use this subject in the resulting {@link OidcUserInfo}\n\t\t * @param subject The subject to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder subject(String subject) {\n\t\t\treturn this.claim(StandardClaimNames.SUB, subject);\n\t\t}\n\n\t\t/**\n\t\t * Use this updated-at {@link Instant} in the resulting {@link OidcUserInfo}\n\t\t * @param updatedAt The updated-at {@link Instant} to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder updatedAt(String updatedAt) {\n\t\t\treturn this.claim(StandardClaimNames.UPDATED_AT, updatedAt);\n\t\t}\n\n\t\t/**\n\t\t * Use this website in the resulting {@link OidcUserInfo}\n\t\t * @param website The website to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder website(String website) {\n\t\t\treturn this.claim(StandardClaimNames.WEBSITE, website);\n\t\t}\n\n\t\t/**\n\t\t * Use this zoneinfo in the resulting {@link OidcUserInfo}\n\t\t * @param zoneinfo The zoneinfo to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder zoneinfo(String zoneinfo) {\n\t\t\treturn this.claim(StandardClaimNames.ZONEINFO, zoneinfo);\n\t\t}\n\n\t\t/**\n\t\t * Build the {@link OidcUserInfo}\n\t\t * @return The constructed {@link OidcUserInfo}\n\t\t */\n\t\tpublic OidcUserInfo build() {\n\t\t\treturn new OidcUserInfo(this.claims);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/StandardClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.time.Instant;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A {@link ClaimAccessor} for the &quot;Standard Claims&quot; that can be returned either\n * in the UserInfo Response or the ID Token.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see ClaimAccessor\n * @see StandardClaimNames\n * @see OidcUserInfo\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse\">UserInfo\n * Response</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\">Standard\n * Claims</a>\n */\npublic interface StandardClaimAccessor extends ClaimAccessor {\n\n\t/**\n\t * Returns the Subject identifier {@code (sub)}, or {@code null} if it does not exist.\n\t * @return the Subject identifier, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getSubject() {\n\t\treturn this.getClaimAsString(StandardClaimNames.SUB);\n\t}\n\n\t/**\n\t * Returns the user's full name {@code (name)} in displayable form, or {@code null} if\n\t * it does not exist.\n\t * @return the user's full name, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getFullName() {\n\t\treturn this.getClaimAsString(StandardClaimNames.NAME);\n\t}\n\n\t/**\n\t * Returns the user's given name(s) or first name(s) {@code (given_name)}, or\n\t * {@code null} if it does not exist.\n\t * @return the user's given name(s), or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getGivenName() {\n\t\treturn this.getClaimAsString(StandardClaimNames.GIVEN_NAME);\n\t}\n\n\t/**\n\t * Returns the user's surname(s) or last name(s) {@code (family_name)}, or\n\t * {@code null} if it does not exist.\n\t * @return the user's family names(s), or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getFamilyName() {\n\t\treturn this.getClaimAsString(StandardClaimNames.FAMILY_NAME);\n\t}\n\n\t/**\n\t * Returns the user's middle name(s) {@code (middle_name)}, or {@code null} if it does\n\t * not exist.\n\t * @return the user's middle name(s), or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getMiddleName() {\n\t\treturn this.getClaimAsString(StandardClaimNames.MIDDLE_NAME);\n\t}\n\n\t/**\n\t * Returns the user's nick name {@code (nickname)} that may or may not be the same as\n\t * the {@code (given_name)}, or {@code null} if it does not exist.\n\t * @return the user's nick name, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getNickName() {\n\t\treturn this.getClaimAsString(StandardClaimNames.NICKNAME);\n\t}\n\n\t/**\n\t * Returns the preferred username {@code (preferred_username)} that the user wishes to\n\t * be referred to, or {@code null} if it does not exist.\n\t * @return the user's preferred user name, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getPreferredUsername() {\n\t\treturn this.getClaimAsString(StandardClaimNames.PREFERRED_USERNAME);\n\t}\n\n\t/**\n\t * Returns the URL of the user's profile page {@code (profile)}, or {@code null} if it\n\t * does not exist.\n\t * @return the URL of the user's profile page, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getProfile() {\n\t\treturn this.getClaimAsString(StandardClaimNames.PROFILE);\n\t}\n\n\t/**\n\t * Returns the URL of the user's profile picture {@code (picture)}, or {@code null} if\n\t * it does not exist.\n\t * @return the URL of the user's profile picture, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getPicture() {\n\t\treturn this.getClaimAsString(StandardClaimNames.PICTURE);\n\t}\n\n\t/**\n\t * Returns the URL of the user's web page or blog {@code (website)}, or {@code null}\n\t * if it does not exist.\n\t * @return the URL of the user's web page or blog, or {@code null} if it does not\n\t * exist\n\t */\n\tdefault @Nullable String getWebsite() {\n\t\treturn this.getClaimAsString(StandardClaimNames.WEBSITE);\n\t}\n\n\t/**\n\t * Returns the user's preferred e-mail address {@code (email)}, or {@code null} if it\n\t * does not exist.\n\t * @return the user's preferred e-mail address, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getEmail() {\n\t\treturn this.getClaimAsString(StandardClaimNames.EMAIL);\n\t}\n\n\t/**\n\t * Returns {@code true} if the user's e-mail address has been verified\n\t * {@code (email_verified)}, otherwise {@code false}, or {@code null} if it does not\n\t * exist.\n\t * @return {@code true} if the user's e-mail address has been verified, otherwise\n\t * {@code false}, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable Boolean getEmailVerified() {\n\t\treturn this.getClaimAsBoolean(StandardClaimNames.EMAIL_VERIFIED);\n\t}\n\n\t/**\n\t * Returns the user's gender {@code (gender)}, or {@code null} if it does not exist.\n\t * @return the user's gender, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getGender() {\n\t\treturn this.getClaimAsString(StandardClaimNames.GENDER);\n\t}\n\n\t/**\n\t * Returns the user's birth date {@code (birthdate)}, or {@code null} if it does not\n\t * exist.\n\t * @return the user's birth date, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getBirthdate() {\n\t\treturn this.getClaimAsString(StandardClaimNames.BIRTHDATE);\n\t}\n\n\t/**\n\t * Returns the user's time zone {@code (zoneinfo)}, or {@code null} if it does not\n\t * exist.\n\t * @return the user's time zone, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getZoneInfo() {\n\t\treturn this.getClaimAsString(StandardClaimNames.ZONEINFO);\n\t}\n\n\t/**\n\t * Returns the user's locale {@code (locale)}, or {@code null} if it does not exist.\n\t * @return the user's locale, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getLocale() {\n\t\treturn this.getClaimAsString(StandardClaimNames.LOCALE);\n\t}\n\n\t/**\n\t * Returns the user's preferred phone number {@code (phone_number)}, or {@code null}\n\t * if it does not exist.\n\t * @return the user's preferred phone number, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable String getPhoneNumber() {\n\t\treturn this.getClaimAsString(StandardClaimNames.PHONE_NUMBER);\n\t}\n\n\t/**\n\t * Returns {@code true} if the user's phone number has been verified\n\t * {@code (phone_number_verified)}, otherwise {@code false}, or {@code null} if it\n\t * does not exist.\n\t * @return {@code true} if the user's phone number has been verified, otherwise\n\t * {@code false}, or {@code null} if it does not exist\n\t */\n\tdefault @Nullable Boolean getPhoneNumberVerified() {\n\t\treturn this.getClaimAsBoolean(StandardClaimNames.PHONE_NUMBER_VERIFIED);\n\t}\n\n\t/**\n\t * Returns the user's preferred postal address {@code (address)}.\n\t * @return the user's preferred postal address\n\t */\n\tdefault AddressStandardClaim getAddress() {\n\t\tMap<String, Object> addressFields = this.getClaimAsMap(StandardClaimNames.ADDRESS);\n\t\treturn (!CollectionUtils.isEmpty(addressFields) ? new DefaultAddressStandardClaim.Builder(addressFields).build()\n\t\t\t\t: new DefaultAddressStandardClaim.Builder().build());\n\t}\n\n\t/**\n\t * Returns the time the user's information was last updated {@code (updated_at)}, or\n\t * {@code null} if it does not exist.\n\t * @return the time the user's information was last updated, or {@code null} if it\n\t * does not exist\n\t */\n\tdefault @Nullable Instant getUpdatedAt() {\n\t\treturn this.getClaimAsInstant(StandardClaimNames.UPDATED_AT);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/StandardClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\n/**\n * The names of the &quot;Standard Claims&quot; defined by the OpenID Connect Core 1.0\n * specification that can be returned either in the UserInfo Response or the ID Token.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\">Standard\n * Claims</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse\">UserInfo\n * Response</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IDToken\">ID Token</a>\n */\npublic final class StandardClaimNames {\n\n\t/**\n\t * {@code sub} - the Subject identifier\n\t */\n\tpublic static final String SUB = \"sub\";\n\n\t/**\n\t * {@code name} - the user's full name\n\t */\n\tpublic static final String NAME = \"name\";\n\n\t/**\n\t * {@code given_name} - the user's given name(s) or first name(s)\n\t */\n\tpublic static final String GIVEN_NAME = \"given_name\";\n\n\t/**\n\t * {@code family_name} - the user's surname(s) or last name(s)\n\t */\n\tpublic static final String FAMILY_NAME = \"family_name\";\n\n\t/**\n\t * {@code middle_name} - the user's middle name(s)\n\t */\n\tpublic static final String MIDDLE_NAME = \"middle_name\";\n\n\t/**\n\t * {@code nickname} - the user's nick name that may or may not be the same as the\n\t * {@code given_name}\n\t */\n\tpublic static final String NICKNAME = \"nickname\";\n\n\t/**\n\t * {@code preferred_username} - the preferred username that the user wishes to be\n\t * referred to\n\t */\n\tpublic static final String PREFERRED_USERNAME = \"preferred_username\";\n\n\t/**\n\t * {@code profile} - the URL of the user's profile page\n\t */\n\tpublic static final String PROFILE = \"profile\";\n\n\t/**\n\t * {@code picture} - the URL of the user's profile picture\n\t */\n\tpublic static final String PICTURE = \"picture\";\n\n\t/**\n\t * {@code website} - the URL of the user's web page or blog\n\t */\n\tpublic static final String WEBSITE = \"website\";\n\n\t/**\n\t * {@code email} - the user's preferred e-mail address\n\t */\n\tpublic static final String EMAIL = \"email\";\n\n\t/**\n\t * {@code email_verified} - {@code true} if the user's e-mail address has been\n\t * verified, otherwise {@code false}\n\t */\n\tpublic static final String EMAIL_VERIFIED = \"email_verified\";\n\n\t/**\n\t * {@code gender} - the user's gender\n\t */\n\tpublic static final String GENDER = \"gender\";\n\n\t/**\n\t * {@code birthdate} - the user's birth date\n\t */\n\tpublic static final String BIRTHDATE = \"birthdate\";\n\n\t/**\n\t * {@code zoneinfo} - the user's time zone\n\t */\n\tpublic static final String ZONEINFO = \"zoneinfo\";\n\n\t/**\n\t * {@code locale} - the user's locale\n\t */\n\tpublic static final String LOCALE = \"locale\";\n\n\t/**\n\t * {@code phone_number} - the user's preferred phone number\n\t */\n\tpublic static final String PHONE_NUMBER = \"phone_number\";\n\n\t/**\n\t * {@code phone_number_verified} - {@code true} if the user's phone number has been\n\t * verified, otherwise {@code false}\n\t */\n\tpublic static final String PHONE_NUMBER_VERIFIED = \"phone_number_verified\";\n\n\t/**\n\t * {@code address} - the user's preferred postal address\n\t */\n\tpublic static final String ADDRESS = \"address\";\n\n\t/**\n\t * {@code updated_at} - the time the user's information was last updated\n\t */\n\tpublic static final String UPDATED_AT = \"updated_at\";\n\n\tprivate StandardClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/endpoint/OidcParameterNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc.endpoint;\n\n/**\n * Standard parameter names defined in the OAuth Parameters Registry and used by the\n * authorization endpoint and token endpoint.\n *\n * @author Joe Grandja\n * @author Mark Heckler\n * @since 5.0\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#OAuthParametersRegistry\">18.2\n * OAuth Parameters Registration</a>\n */\npublic final class OidcParameterNames {\n\n\t/**\n\t * {@code id_token} - used in the Access Token Response.\n\t */\n\tpublic static final String ID_TOKEN = \"id_token\";\n\n\t/**\n\t * {@code nonce} - used in the Authentication Request.\n\t */\n\tpublic static final String NONCE = \"nonce\";\n\n\tprivate OidcParameterNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/endpoint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support classes that model the OpenID Connect Core 1.0 Request and Response messages\n * from the Authorization Endpoint and Token Endpoint.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.oidc.endpoint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core classes and interfaces providing support for OpenID Connect Core 1.0.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.oidc;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc.user;\n\nimport java.io.Serial;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\n\n/**\n * The default implementation of an {@link OidcUser}.\n *\n * <p>\n * The default claim used for accessing the &quot;name&quot; of the user {@code Principal}\n * from {@link #getClaims()} is {@link IdTokenClaimNames#SUB}.\n *\n * @author Joe Grandja\n * @author Vedran Pavic\n * @since 5.0\n * @see OidcUser\n * @see DefaultOAuth2User\n * @see OidcIdToken\n * @see OidcUserInfo\n */\npublic class DefaultOidcUser extends DefaultOAuth2User implements OidcUser {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2378469202439157250L;\n\n\tprivate final OidcIdToken idToken;\n\n\tprivate final @Nullable OidcUserInfo userInfo;\n\n\t/**\n\t * Constructs a {@code DefaultOidcUser} using the provided parameters.\n\t * @param authorities the authorities granted to the user, may be {@code null}\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t */\n\tpublic DefaultOidcUser(@Nullable Collection<? extends GrantedAuthority> authorities, OidcIdToken idToken) {\n\t\tthis(authorities, idToken, IdTokenClaimNames.SUB);\n\t}\n\n\t/**\n\t * Constructs a {@code DefaultOidcUser} using the provided parameters.\n\t * @param authorities the authorities granted to the user, may be {@code null}\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t * @param nameAttributeKey the key used to access the user's &quot;name&quot; from\n\t * {@link #getAttributes()}\n\t */\n\tpublic DefaultOidcUser(@Nullable Collection<? extends GrantedAuthority> authorities, OidcIdToken idToken,\n\t\t\tString nameAttributeKey) {\n\t\tthis(authorities, idToken, null, nameAttributeKey);\n\t}\n\n\t/**\n\t * Constructs a {@code DefaultOidcUser} using the provided parameters.\n\t * @param authorities the authorities granted to the user, may be {@code null}\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,\n\t * may be {@code null}\n\t */\n\tpublic DefaultOidcUser(@Nullable Collection<? extends GrantedAuthority> authorities, OidcIdToken idToken,\n\t\t\t@Nullable OidcUserInfo userInfo) {\n\t\tthis(authorities, idToken, userInfo, IdTokenClaimNames.SUB);\n\t}\n\n\t/**\n\t * Constructs a {@code DefaultOidcUser} using the provided parameters.\n\t * @param authorities the authorities granted to the user, may be {@code null}\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,\n\t * may be {@code null}\n\t * @param nameAttributeKey the key used to access the user's &quot;name&quot; from\n\t * {@link #getAttributes()}\n\t */\n\tpublic DefaultOidcUser(@Nullable Collection<? extends GrantedAuthority> authorities, OidcIdToken idToken,\n\t\t\t@Nullable OidcUserInfo userInfo, String nameAttributeKey) {\n\t\tsuper(authorities, OidcUserAuthority.collectClaims(idToken, userInfo), nameAttributeKey);\n\t\tthis.idToken = idToken;\n\t\tthis.userInfo = userInfo;\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.getAttributes();\n\t}\n\n\t@Override\n\tpublic OidcIdToken getIdToken() {\n\t\treturn this.idToken;\n\t}\n\n\t@Override\n\tpublic @Nullable OidcUserInfo getUserInfo() {\n\t\treturn this.userInfo;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc.user;\n\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimAccessor;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimAccessor;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\n\n/**\n * A representation of a user {@code Principal} that is registered with an OpenID Connect\n * 1.0 Provider.\n *\n * <p>\n * An {@code OidcUser} contains &quot;claims&quot; about the authentication of the\n * End-User. The claims are aggregated from the {@link OidcIdToken} and the\n * {@link OidcUserInfo} (if available).\n *\n * <p>\n * Implementation instances of this interface represent an {@link AuthenticatedPrincipal}\n * which is associated to an {@link Authentication} object and may be accessed via\n * {@link Authentication#getPrincipal()}.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see DefaultOidcUser\n * @see OAuth2User\n * @see OidcIdToken\n * @see OidcUserInfo\n * @see IdTokenClaimAccessor\n * @see StandardClaimAccessor\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IDToken\">ID Token</a>\n * @see <a target=\"_blank\" href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\">Standard\n * Claims</a>\n */\npublic interface OidcUser extends OAuth2User, IdTokenClaimAccessor {\n\n\t/**\n\t * Returns the claims about the user. The claims are aggregated from\n\t * {@link #getIdToken()} and {@link #getUserInfo()} (if available).\n\t * @return a {@code Map} of claims about the user\n\t */\n\t@Override\n\tMap<String, Object> getClaims();\n\n\t/**\n\t * Returns the {@link OidcUserInfo UserInfo} containing claims about the user, or\n\t * {@code null} if not present.\n\t * @return the {@link OidcUserInfo} containing claims about the user, or {@code null}\n\t */\n\t@Nullable OidcUserInfo getUserInfo();\n\n\t/**\n\t * Returns the {@link OidcIdToken ID Token} containing claims about the user.\n\t * @return the {@link OidcIdToken} containing claims about the user.\n\t */\n\tOidcIdToken getIdToken();\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthority.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc.user;\n\nimport java.io.Serial;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link GrantedAuthority} that may be associated to an {@link OidcUser}.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OidcUser\n */\npublic class OidcUserAuthority extends OAuth2UserAuthority {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -4675866280835753141L;\n\n\tprivate final OidcIdToken idToken;\n\n\tprivate final @Nullable OidcUserInfo userInfo;\n\n\t/**\n\t * Constructs a {@code OidcUserAuthority} using the provided parameters.\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t */\n\tpublic OidcUserAuthority(OidcIdToken idToken) {\n\t\tthis(idToken, null);\n\t}\n\n\t/**\n\t * Constructs a {@code OidcUserAuthority} using the provided parameters and defaults\n\t * {@link #getAuthority()} to {@code OIDC_USER}.\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,\n\t * may be {@code null}\n\t */\n\tpublic OidcUserAuthority(OidcIdToken idToken, @Nullable OidcUserInfo userInfo) {\n\t\tthis(\"OIDC_USER\", idToken, userInfo);\n\t}\n\n\t/**\n\t * Constructs a {@code OidcUserAuthority} using the provided parameters and defaults\n\t * {@link #getAuthority()} to {@code OIDC_USER}.\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,\n\t * may be {@code null}\n\t * @param userNameAttributeName the attribute name used to access the user's name from\n\t * the attributes, may be {@code null}\n\t * @since 6.4\n\t */\n\tpublic OidcUserAuthority(OidcIdToken idToken, @Nullable OidcUserInfo userInfo,\n\t\t\t@Nullable String userNameAttributeName) {\n\t\tthis(\"OIDC_USER\", idToken, userInfo, userNameAttributeName);\n\t}\n\n\t/**\n\t * Constructs a {@code OidcUserAuthority} using the provided parameters.\n\t * @param authority the authority granted to the user\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,\n\t * may be {@code null}\n\t */\n\tpublic OidcUserAuthority(String authority, OidcIdToken idToken, @Nullable OidcUserInfo userInfo) {\n\t\tthis(authority, idToken, userInfo, IdTokenClaimNames.SUB);\n\t}\n\n\t/**\n\t * Constructs a {@code OidcUserAuthority} using the provided parameters.\n\t * @param authority the authority granted to the user\n\t * @param idToken the {@link OidcIdToken ID Token} containing claims about the user\n\t * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,\n\t * may be {@code null}\n\t * @param userNameAttributeName the attribute name used to access the user's name from\n\t * the attributes, may be {@code null}\n\t * @since 6.4\n\t */\n\tpublic OidcUserAuthority(String authority, OidcIdToken idToken, @Nullable OidcUserInfo userInfo,\n\t\t\t@Nullable String userNameAttributeName) {\n\t\tsuper(authority, collectClaims(idToken, userInfo), userNameAttributeName);\n\t\tthis.idToken = idToken;\n\t\tthis.userInfo = userInfo;\n\t}\n\n\t/**\n\t * Returns the {@link OidcIdToken ID Token} containing claims about the user.\n\t * @return the {@link OidcIdToken} containing claims about the user.\n\t */\n\tpublic OidcIdToken getIdToken() {\n\t\treturn this.idToken;\n\t}\n\n\t/**\n\t * Returns the {@link OidcUserInfo UserInfo} containing claims about the user, may be\n\t * {@code null}.\n\t * @return the {@link OidcUserInfo} containing claims about the user, or {@code null}\n\t */\n\tpublic @Nullable OidcUserInfo getUserInfo() {\n\t\treturn this.userInfo;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!super.equals(obj)) {\n\t\t\treturn false;\n\t\t}\n\t\tOidcUserAuthority that = (OidcUserAuthority) obj;\n\t\tif (!this.getIdToken().equals(that.getIdToken())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(this.getUserInfo(), that.getUserInfo());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = super.hashCode();\n\t\tresult = 31 * result + this.getIdToken().hashCode();\n\t\tresult = 31 * result + ((this.getUserInfo() != null) ? this.getUserInfo().hashCode() : 0);\n\t\treturn result;\n\t}\n\n\tstatic Map<String, Object> collectClaims(OidcIdToken idToken, @Nullable OidcUserInfo userInfo) {\n\t\tAssert.notNull(idToken, \"idToken cannot be null\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tif (userInfo != null) {\n\t\t\tclaims.putAll(userInfo.getClaims());\n\t\t}\n\t\tclaims.putAll(idToken.getClaims());\n\t\treturn claims;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Provides a model for an OpenID Connect Core 1.0 representation of a user\n * {@code Principal}.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.oidc.user;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core classes and interfaces providing support for the OAuth 2.0 Authorization\n * Framework.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.user;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.util.Assert;\n\n/**\n * The default implementation of an {@link OAuth2User}.\n *\n * <p>\n * User attribute names are <b>not</b> standardized between providers and therefore it is\n * required to supply the <i>key</i> for the user's &quot;name&quot; attribute to one of\n * the constructors. The <i>key</i> will be used for accessing the &quot;name&quot; of the\n * {@code Principal} (user) via {@link #getAttributes()} and returning it from\n * {@link #getName()}.\n *\n * @author Joe Grandja\n * @author Eddú Meléndez\n * @author Park Hyojong\n * @since 5.0\n * @see OAuth2User\n */\npublic class DefaultOAuth2User implements OAuth2User, Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final Set<GrantedAuthority> authorities;\n\n\tprivate final Map<String, Object> attributes;\n\n\tprivate final String nameAttributeKey;\n\n\t/**\n\t * Constructs a {@code DefaultOAuth2User} using the provided parameters.\n\t * @param authorities the authorities granted to the user, may be {@code null}\n\t * @param attributes the attributes about the user\n\t * @param nameAttributeKey the key used to access the user's &quot;name&quot; from\n\t * {@link #getAttributes()}\n\t */\n\tpublic DefaultOAuth2User(@Nullable Collection<? extends GrantedAuthority> authorities,\n\t\t\tMap<String, Object> attributes, String nameAttributeKey) {\n\t\tAssert.notEmpty(attributes, \"attributes cannot be empty\");\n\t\tAssert.hasText(nameAttributeKey, \"nameAttributeKey cannot be empty\");\n\t\tAssert.notNull(attributes.get(nameAttributeKey),\n\t\t\t\t\"Attribute value for '\" + nameAttributeKey + \"' cannot be null\");\n\t\tthis.authorities = (authorities != null)\n\t\t\t\t? Collections.unmodifiableSet(new LinkedHashSet<>(this.sortAuthorities(authorities)))\n\t\t\t\t: Collections.unmodifiableSet(new LinkedHashSet<>(AuthorityUtils.NO_AUTHORITIES));\n\t\tthis.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(attributes));\n\t\tthis.nameAttributeKey = nameAttributeKey;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\tObject nameAttributeValue = this.getAttribute(this.nameAttributeKey);\n\t\tAssert.notNull(nameAttributeValue, \"name attribute value cannot be null\");\n\t\treturn nameAttributeValue.toString();\n\t}\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\tprivate Set<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {\n\t\tSortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<>(\n\t\t\t\tComparator.comparing(GrantedAuthority::getAuthority));\n\t\tsortedAuthorities.addAll(authorities);\n\t\treturn sortedAuthorities;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tDefaultOAuth2User that = (DefaultOAuth2User) obj;\n\t\tif (!this.getName().equals(that.getName())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!this.getAuthorities().equals(that.getAuthorities())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.getAttributes().equals(that.getAttributes());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.getName().hashCode();\n\t\tresult = 31 * result + this.getAuthorities().hashCode();\n\t\tresult = 31 * result + this.getAttributes().hashCode();\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"Name: [\");\n\t\tsb.append(this.getName());\n\t\tsb.append(\"], Granted Authorities: [\");\n\t\tsb.append(getAuthorities());\n\t\tsb.append(\"], User Attributes: [\");\n\t\tsb.append(getAttributes());\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/OAuth2User.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.user;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\n\n/**\n * A representation of a user {@code Principal} that is registered with an OAuth 2.0\n * Provider.\n *\n * <p>\n * An OAuth 2.0 user is composed of one or more attributes, for example, first name,\n * middle name, last name, email, phone number, address, etc. Each user attribute has a\n * &quot;name&quot; and &quot;value&quot; and is keyed by the &quot;name&quot; in\n * {@link #getAttributes()}.\n *\n * <p>\n * <b>NOTE:</b> Attribute names are <b>not</b> standardized between providers and\n * therefore will vary. Please consult the provider's API documentation for the set of\n * supported user attribute names.\n *\n * <p>\n * Implementation instances of this interface represent an\n * {@link OAuth2AuthenticatedPrincipal} which is associated to an {@link Authentication}\n * object and may be accessed via {@link Authentication#getPrincipal()}.\n *\n * @author Joe Grandja\n * @author Eddú Meléndez\n * @since 5.0\n * @see DefaultOAuth2User\n * @see OAuth2AuthenticatedPrincipal\n */\npublic interface OAuth2User extends OAuth2AuthenticatedPrincipal {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/OAuth2UserAuthority.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.user;\n\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link GrantedAuthority} that may be associated to an {@link OAuth2User}.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see OAuth2User\n */\npublic class OAuth2UserAuthority implements GrantedAuthority {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String authority;\n\n\tprivate final Map<String, Object> attributes;\n\n\tprivate final @Nullable String userNameAttributeName;\n\n\t/**\n\t * Constructs a {@code OAuth2UserAuthority} using the provided parameters and defaults\n\t * {@link #getAuthority()} to {@code OAUTH2_USER}.\n\t * @param attributes the attributes about the user\n\t */\n\tpublic OAuth2UserAuthority(Map<String, Object> attributes) {\n\t\tthis(\"OAUTH2_USER\", attributes);\n\t}\n\n\t/**\n\t * Constructs a {@code OAuth2UserAuthority} using the provided parameters and defaults\n\t * {@link #getAuthority()} to {@code OAUTH2_USER}.\n\t * @param attributes the attributes about the user\n\t * @param userNameAttributeName the attribute name used to access the user's name from\n\t * the attributes\n\t * @since 6.4\n\t */\n\tpublic OAuth2UserAuthority(Map<String, Object> attributes, @Nullable String userNameAttributeName) {\n\t\tthis(\"OAUTH2_USER\", attributes, userNameAttributeName);\n\t}\n\n\t/**\n\t * Constructs a {@code OAuth2UserAuthority} using the provided parameters.\n\t * @param authority the authority granted to the user\n\t * @param attributes the attributes about the user\n\t */\n\tpublic OAuth2UserAuthority(String authority, Map<String, Object> attributes) {\n\t\tthis(authority, attributes, null);\n\t}\n\n\t/**\n\t * Constructs a {@code OAuth2UserAuthority} using the provided parameters.\n\t * @param authority the authority granted to the user\n\t * @param attributes the attributes about the user\n\t * @param userNameAttributeName the attribute name used to access the user's name from\n\t * the attributes, may be {@code null}\n\t * @since 6.4\n\t */\n\tpublic OAuth2UserAuthority(String authority, Map<String, Object> attributes,\n\t\t\t@Nullable String userNameAttributeName) {\n\t\tAssert.hasText(authority, \"authority cannot be empty\");\n\t\tAssert.notEmpty(attributes, \"attributes cannot be empty\");\n\t\tthis.authority = authority;\n\t\tthis.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(attributes));\n\t\tthis.userNameAttributeName = userNameAttributeName;\n\t}\n\n\t@Override\n\tpublic String getAuthority() {\n\t\treturn this.authority;\n\t}\n\n\t/**\n\t * Returns the attributes about the user.\n\t * @return a {@code Map} of attributes about the user\n\t */\n\tpublic Map<String, Object> getAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\t/**\n\t * Returns the attribute name used to access the user's name from the attributes.\n\t * @return the attribute name used to access the user's name from the attributes, or\n\t * {@code null} if not available\n\t * @since 6.4\n\t */\n\tpublic @Nullable String getUserNameAttributeName() {\n\t\treturn this.userNameAttributeName;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || this.getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOAuth2UserAuthority that = (OAuth2UserAuthority) obj;\n\t\tif (!this.getAuthority().equals(that.getAuthority())) {\n\t\t\treturn false;\n\t\t}\n\t\tMap<String, Object> thatAttributes = that.getAttributes();\n\t\tif (getAttributes().size() != thatAttributes.size()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (Map.Entry<String, Object> e : getAttributes().entrySet()) {\n\t\t\tString key = e.getKey();\n\t\t\tObject value = convertURLIfNecessary(e.getValue());\n\t\t\tif (value == null) {\n\t\t\t\tif (!(thatAttributes.get(key) == null && thatAttributes.containsKey(key))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tObject thatValue = thatAttributes.get(key);\n\t\t\t\tObject convertedThatValue = convertURLIfNecessary(thatValue);\n\t\t\t\tif (!value.equals(convertedThatValue)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.getAuthority().hashCode();\n\t\tresult = 31 * result;\n\t\tfor (Map.Entry<String, Object> e : getAttributes().entrySet()) {\n\t\t\tObject key = e.getKey();\n\t\t\tObject value = convertURLIfNecessary(e.getValue());\n\t\t\tresult += Objects.hashCode(key) ^ Objects.hashCode(value);\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.getAuthority();\n\t}\n\n\t/**\n\t * @return {@code URL} converted to a string since {@code URL} shouldn't be used for\n\t * equality/hashCode. For other instances the value is returned as is (including\n\t * null).\n\t */\n\tprivate static @Nullable Object convertURLIfNecessary(@Nullable Object value) {\n\t\treturn (value instanceof URL) ? ((URL) value).toExternalForm() : value;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Provides a model for an OAuth 2.0 representation of a user {@code Principal}.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.user;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Web support classes for OAuth 2.0 and OpenID Connect.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.web;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2AccessTokenResponseBodyExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.web.reactive.function;\n\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.nimbusds.oauth2.sdk.AccessTokenResponse;\nimport com.nimbusds.oauth2.sdk.ErrorObject;\nimport com.nimbusds.oauth2.sdk.ParseException;\nimport com.nimbusds.oauth2.sdk.TokenErrorResponse;\nimport com.nimbusds.oauth2.sdk.TokenResponse;\nimport com.nimbusds.oauth2.sdk.token.AccessToken;\nimport net.minidev.json.JSONObject;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.web.reactive.function.BodyExtractor;\nimport org.springframework.web.reactive.function.BodyExtractors;\n\n/**\n * Provides a way to create an {@link OAuth2AccessTokenResponse} from a\n * {@link ReactiveHttpInputMessage}\n *\n * @author Rob Winch\n * @since 5.1\n */\nclass OAuth2AccessTokenResponseBodyExtractor\n\t\timplements BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> {\n\n\tprivate static final String INVALID_TOKEN_RESPONSE_ERROR_CODE = \"invalid_token_response\";\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tOAuth2AccessTokenResponseBodyExtractor() {\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AccessTokenResponse> extract(ReactiveHttpInputMessage inputMessage, Context context) {\n\t\tBodyExtractor<Mono<Map<String, Object>>, ReactiveHttpInputMessage> delegate = BodyExtractors\n\t\t\t.toMono(STRING_OBJECT_MAP);\n\t\treturn delegate.extract(inputMessage, context)\n\t\t\t.onErrorMap((ex) -> new OAuth2AuthorizationException(\n\t\t\t\t\tinvalidTokenResponse(\"An error occurred parsing the Access Token response: \" + ex.getMessage()),\n\t\t\t\t\tex))\n\t\t\t.switchIfEmpty(Mono.error(() -> new OAuth2AuthorizationException(\n\t\t\t\t\tinvalidTokenResponse(\"Empty OAuth 2.0 Access Token Response\"))))\n\t\t\t.map(OAuth2AccessTokenResponseBodyExtractor::parse)\n\t\t\t.flatMap(OAuth2AccessTokenResponseBodyExtractor::oauth2AccessTokenResponse)\n\t\t\t.map(OAuth2AccessTokenResponseBodyExtractor::oauth2AccessTokenResponse);\n\t}\n\n\tprivate static TokenResponse parse(Map<String, Object> json) {\n\t\ttry {\n\t\t\treturn TokenResponse.parse(new JSONObject(json));\n\t\t}\n\t\tcatch (ParseException ex) {\n\t\t\tOAuth2Error oauth2Error = invalidTokenResponse(\n\t\t\t\t\t\"An error occurred parsing the Access Token response: \" + ex.getMessage());\n\t\t\tthrow new OAuth2AuthorizationException(oauth2Error, ex);\n\t\t}\n\t}\n\n\tprivate static OAuth2Error invalidTokenResponse(String message) {\n\t\treturn new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE, message, null);\n\t}\n\n\tprivate static Mono<AccessTokenResponse> oauth2AccessTokenResponse(TokenResponse tokenResponse) {\n\t\tif (tokenResponse.indicatesSuccess()) {\n\t\t\treturn Mono.just(tokenResponse).cast(AccessTokenResponse.class);\n\t\t}\n\t\tTokenErrorResponse tokenErrorResponse = (TokenErrorResponse) tokenResponse;\n\t\tErrorObject errorObject = tokenErrorResponse.getErrorObject();\n\t\tOAuth2Error oauth2Error = getOAuth2Error(errorObject);\n\t\treturn Mono.error(new OAuth2AuthorizationException(oauth2Error));\n\t}\n\n\tprivate static OAuth2Error getOAuth2Error(ErrorObject errorObject) {\n\t\tif (errorObject == null) {\n\t\t\treturn new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR);\n\t\t}\n\t\tString code = (errorObject.getCode() != null) ? errorObject.getCode() : OAuth2ErrorCodes.SERVER_ERROR;\n\t\tString description = errorObject.getDescription();\n\t\tString uri = (errorObject.getURI() != null) ? errorObject.getURI().toString() : null;\n\t\treturn new OAuth2Error(code, description, uri);\n\t}\n\n\tprivate static OAuth2AccessTokenResponse oauth2AccessTokenResponse(AccessTokenResponse accessTokenResponse) {\n\t\tAccessToken accessToken = accessTokenResponse.getTokens().getAccessToken();\n\t\tOAuth2AccessToken.TokenType accessTokenType = null;\n\t\tif (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(accessToken.getType().getValue())) {\n\t\t\taccessTokenType = OAuth2AccessToken.TokenType.BEARER;\n\t\t}\n\t\tlong expiresIn = accessToken.getLifetime();\n\t\tSet<String> scopes = (accessToken.getScope() != null)\n\t\t\t\t? new LinkedHashSet<>(accessToken.getScope().toStringList()) : Collections.emptySet();\n\t\tString refreshToken = null;\n\t\tif (accessTokenResponse.getTokens().getRefreshToken() != null) {\n\t\t\trefreshToken = accessTokenResponse.getTokens().getRefreshToken().getValue();\n\t\t}\n\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>(accessTokenResponse.getCustomParameters());\n\t\t// @formatter:off\n\t\treturn OAuth2AccessTokenResponse.withToken(accessToken.getValue())\n\t\t\t\t.tokenType(accessTokenType)\n\t\t\t\t.expiresIn(expiresIn)\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.refreshToken(refreshToken)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2BodyExtractors.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.web.reactive.function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.web.reactive.function.BodyExtractor;\n\n/**\n * Static factory methods for OAuth2 {@link BodyExtractor} implementations.\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic abstract class OAuth2BodyExtractors {\n\n\t/**\n\t * Extractor to decode an {@link OAuth2AccessTokenResponse}\n\t * @return a BodyExtractor for {@link OAuth2AccessTokenResponse}\n\t */\n\tpublic static BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> oauth2AccessTokenResponse() {\n\t\treturn new OAuth2AccessTokenResponseBodyExtractor();\n\t}\n\n\tprivate OAuth2BodyExtractors() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/function/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive functional web support classes for OAuth 2.0 and OpenID Connect.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.web.reactive.function;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive web support classes for OAuth 2.0 and OpenID Connect.\n */\n@NullMarked\npackage org.springframework.security.oauth2.core.web.reactive;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/AuthenticationMethodTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link AuthenticationMethod}.\n *\n * @author MyeongHyeon Lee\n */\npublic class AuthenticationMethodTests {\n\n\t@Test\n\tpublic void constructorWhenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AuthenticationMethod(null))\n\t\t\t.withMessage(\"value cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenHeaderAuthenticationTypeThenReturnHeader() {\n\t\tassertThat(AuthenticationMethod.HEADER.getValue()).isEqualTo(\"header\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenFormAuthenticationTypeThenReturnForm() {\n\t\tassertThat(AuthenticationMethod.FORM.getValue()).isEqualTo(\"form\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenFormAuthenticationTypeThenReturnQuery() {\n\t\tassertThat(AuthenticationMethod.QUERY.getValue()).isEqualTo(\"query\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/AuthorizationGrantTypeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link AuthorizationGrantType}.\n *\n * @author Joe Grandja\n */\npublic class AuthorizationGrantTypeTests {\n\n\t@Test\n\tpublic void constructorWhenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AuthorizationGrantType(null));\n\t}\n\n\t@Test\n\tpublic void getValueWhenAuthorizationCodeGrantTypeThenReturnAuthorizationCode() {\n\t\tassertThat(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()).isEqualTo(\"authorization_code\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenRefreshTokenGrantTypeThenReturnRefreshToken() {\n\t\tassertThat(AuthorizationGrantType.REFRESH_TOKEN.getValue()).isEqualTo(\"refresh_token\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenJwtBearerGrantTypeThenReturnJwtBearer() {\n\t\tassertThat(AuthorizationGrantType.JWT_BEARER.getValue())\n\t\t\t.isEqualTo(\"urn:ietf:params:oauth:grant-type:jwt-bearer\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/ClaimAccessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatObject;\n\n/**\n * Tests for {@link ClaimAccessor}.\n *\n * @author Joe Grandja\n */\npublic class ClaimAccessorTests {\n\n\tprivate Map<String, Object> claims = new HashMap<>();\n\n\tprivate ClaimAccessor claimAccessor = (() -> this.claims);\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.claims.clear();\n\t}\n\n\t// gh-5192\n\t@Test\n\tpublic void getClaimAsInstantWhenDateTypeThenReturnInstant() {\n\t\tInstant expectedClaimValue = Instant.now();\n\t\tString claimName = \"date\";\n\t\tthis.claims.put(claimName, Date.from(expectedClaimValue));\n\t\tassertThat(this.claimAccessor.getClaimAsInstant(claimName)).isBetween(expectedClaimValue.minusSeconds(1),\n\t\t\t\texpectedClaimValue.plusSeconds(1));\n\t}\n\n\t// gh-5191\n\t@Test\n\tpublic void getClaimAsInstantWhenLongTypeSecondsThenReturnInstant() {\n\t\tInstant expectedClaimValue = Instant.now();\n\t\tString claimName = \"longSeconds\";\n\t\tthis.claims.put(claimName, expectedClaimValue.getEpochSecond());\n\t\tassertThat(this.claimAccessor.getClaimAsInstant(claimName)).isBetween(expectedClaimValue.minusSeconds(1),\n\t\t\t\texpectedClaimValue.plusSeconds(1));\n\t}\n\n\t@Test\n\tpublic void getClaimAsInstantWhenInstantTypeThenReturnInstant() {\n\t\tInstant expectedClaimValue = Instant.now();\n\t\tString claimName = \"instant\";\n\t\tthis.claims.put(claimName, expectedClaimValue);\n\t\tassertThat(this.claimAccessor.getClaimAsInstant(claimName)).isBetween(expectedClaimValue.minusSeconds(1),\n\t\t\t\texpectedClaimValue.plusSeconds(1));\n\t}\n\n\t// gh-5250\n\t@Test\n\tpublic void getClaimAsInstantWhenIntegerTypeSecondsThenReturnInstant() {\n\t\tInstant expectedClaimValue = Instant.now();\n\t\tString claimName = \"integerSeconds\";\n\t\tthis.claims.put(claimName, Long.valueOf(expectedClaimValue.getEpochSecond()).intValue());\n\t\tassertThat(this.claimAccessor.getClaimAsInstant(claimName)).isBetween(expectedClaimValue.minusSeconds(1),\n\t\t\t\texpectedClaimValue.plusSeconds(1));\n\t}\n\n\t// gh-5250\n\t@Test\n\tpublic void getClaimAsInstantWhenDoubleTypeSecondsThenReturnInstant() {\n\t\tInstant expectedClaimValue = Instant.now();\n\t\tString claimName = \"doubleSeconds\";\n\t\tthis.claims.put(claimName, Long.valueOf(expectedClaimValue.getEpochSecond()).doubleValue());\n\t\tassertThat(this.claimAccessor.getClaimAsInstant(claimName)).isBetween(expectedClaimValue.minusSeconds(1),\n\t\t\t\texpectedClaimValue.plusSeconds(1));\n\t}\n\n\t// gh-5608\n\t@Test\n\tpublic void getClaimAsStringWhenValueIsNullThenReturnNull() {\n\t\tString claimName = \"claim-with-null-value\";\n\t\tthis.claims.put(claimName, null);\n\t\tassertThat(this.claimAccessor.getClaimAsString(claimName)).isNull();\n\t}\n\n\t@Test\n\tpublic void getClaimAsBooleanWhenBooleanTypeThenReturnBoolean() {\n\t\tBoolean expectedClaimValue = Boolean.TRUE;\n\t\tString claimName = \"boolean\";\n\t\tthis.claims.put(claimName, expectedClaimValue);\n\t\tassertThat(this.claimAccessor.getClaimAsBoolean(claimName)).isEqualTo(expectedClaimValue);\n\t}\n\n\t@Test\n\tpublic void getClaimAsBooleanWhenStringTypeThenReturnBoolean() {\n\t\tBoolean expectedClaimValue = Boolean.TRUE;\n\t\tString claimName = \"boolean\";\n\t\tthis.claims.put(claimName, expectedClaimValue.toString());\n\t\tassertThat(this.claimAccessor.getClaimAsBoolean(claimName)).isEqualTo(expectedClaimValue);\n\t}\n\n\t// gh-10148\n\t@Test\n\tpublic void getClaimAsBooleanWhenNonBooleanTypeThenThrowIllegalArgumentException() {\n\t\tString claimName = \"boolean\";\n\t\tMap<Object, Object> claimValue = new HashMap<>();\n\t\tthis.claims.put(claimName, claimValue);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.claimAccessor.getClaimAsBoolean(claimName))\n\t\t\t.withMessage(\n\t\t\t\t\t\"Unable to convert claim '\" + claimName + \"' of type '\" + claimValue.getClass() + \"' to Boolean.\");\n\t}\n\n\t@Test\n\tpublic void getClaimAsMapWhenNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getClaimAsMap(\"map\")).isNull();\n\t}\n\n\t@Test\n\tpublic void getClaimAsMapWhenMapTypeThenReturnMap() {\n\t\tMap<Object, Object> expectedClaimValue = Collections.emptyMap();\n\t\tString claimName = \"map\";\n\t\tthis.claims.put(claimName, expectedClaimValue);\n\t\tassertThat(this.claimAccessor.getClaimAsMap(claimName)).isEqualTo(expectedClaimValue);\n\t}\n\n\t@Test\n\tpublic void getClaimAsMapWhenValueIsNullThenReturnNull() {\n\t\tString claimName = \"map\";\n\t\tthis.claims.put(claimName, null);\n\t\tassertThat(this.claimAccessor.getClaimAsMap(claimName)).isNull();\n\t}\n\n\t@Test\n\tpublic void getClaimAsMapWhenNonMapTypeThenThrowIllegalArgumentException() {\n\t\tString claimName = \"map\";\n\t\tthis.claims.put(claimName, \"map\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.claimAccessor.getClaimAsMap(claimName));\n\t}\n\n\t@Test\n\tpublic void getClaimAsStringListWhenNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getClaimAsStringList(\"list\")).isNull();\n\t}\n\n\t@Test\n\tpublic void getClaimAsStringListWhenStringListTypeThenReturnList() {\n\t\tList<String> expectedClaimValue = Collections.emptyList();\n\t\tString claimName = \"list\";\n\t\tthis.claims.put(claimName, expectedClaimValue);\n\t\tassertThat(this.claimAccessor.getClaimAsStringList(claimName)).isEqualTo(expectedClaimValue);\n\t}\n\n\t@Test\n\tpublic void getClaimAsStringListWhenNonListTypeThenReturnList() {\n\t\tList<String> expectedClaimValue = Collections.singletonList(\"list\");\n\t\tString claimName = \"list\";\n\t\tthis.claims.put(claimName, expectedClaimValue.get(0));\n\t\tassertThat(this.claimAccessor.getClaimAsStringList(claimName)).isEqualTo(expectedClaimValue);\n\t}\n\n\t@Test\n\tpublic void getClaimAsStringListWhenValueIsNullThenReturnNull() {\n\t\tString claimName = \"list\";\n\t\tthis.claims.put(claimName, null);\n\t\tassertThat(this.claimAccessor.getClaimAsStringList(claimName)).isNull();\n\t}\n\n\t@Test\n\tpublic void getClaimWhenNotExistingThenReturnNull() {\n\t\tString claimName = \"list\";\n\t\tList<String> actualClaimValue = this.claimAccessor.getClaim(claimName);\n\t\tassertThat(actualClaimValue).isNull();\n\t}\n\n\t@Test\n\tpublic void getClaimWhenValueIsConvertedThenReturnList() {\n\t\tList<String> expectedClaimValue = Arrays.asList(\"item1\", \"item2\");\n\t\tString claimName = \"list\";\n\t\tthis.claims.put(claimName, expectedClaimValue);\n\t\tList<String> actualClaimValue = this.claimAccessor.getClaim(claimName);\n\t\tassertThat(actualClaimValue).containsOnlyElementsOf(expectedClaimValue);\n\t}\n\n\t@Test\n\tpublic void getClaimWhenValueIsConvertedThenReturnBoolean() {\n\t\tboolean expectedClaimValue = true;\n\t\tString claimName = \"boolean\";\n\t\tthis.claims.put(claimName, expectedClaimValue);\n\t\tboolean actualClaimValue = this.claimAccessor.getClaim(claimName);\n\t\tassertThat(actualClaimValue).isEqualTo(expectedClaimValue);\n\t}\n\n\t@Test\n\tpublic void getClaimWhenValueIsNotConvertedThenThrowClassCastException() {\n\t\tString expectedClaimValue = \"true\";\n\t\tString claimName = \"boolean\";\n\t\tthis.claims.put(claimName, expectedClaimValue);\n\t\tassertThatObject(this.claimAccessor.getClaim(claimName)).isNotInstanceOf(Boolean.class);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/ClientAuthenticationMethodTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\n\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ClientAuthenticationMethod}.\n *\n * @author Joe Grandja\n */\npublic class ClientAuthenticationMethodTests {\n\n\t@Test\n\tpublic void constructorWhenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ClientAuthenticationMethod(null));\n\t}\n\n\t@Test\n\tpublic void getValueWhenAuthenticationMethodClientSecretBasicThenReturnClientSecretBasic() {\n\t\tassertThat(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()).isEqualTo(\"client_secret_basic\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenAuthenticationMethodClientSecretPostThenReturnClientSecretPost() {\n\t\tassertThat(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()).isEqualTo(\"client_secret_post\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenAuthenticationMethodClientSecretJwtThenReturnClientSecretJwt() {\n\t\tassertThat(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()).isEqualTo(\"client_secret_jwt\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenAuthenticationMethodPrivateKeyJwtThenReturnPrivateKeyJwt() {\n\t\tassertThat(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()).isEqualTo(\"private_key_jwt\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenAuthenticationMethodNoneThenReturnNone() {\n\t\tassertThat(ClientAuthenticationMethod.NONE.getValue()).isEqualTo(\"none\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenAuthenticationMethodTlsClientAuthThenReturnTlsClientAuth() {\n\t\tassertThat(ClientAuthenticationMethod.TLS_CLIENT_AUTH.getValue()).isEqualTo(\"tls_client_auth\");\n\t}\n\n\t@Test\n\tpublic void getValueWhenAuthenticationMethodSelfSignedTlsClientAuthThenReturnSelfSignedTlsClientAuth() {\n\t\tassertThat(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH.getValue())\n\t\t\t.isEqualTo(\"self_signed_tls_client_auth\");\n\t}\n\n\t@Test\n\tpublic void valueOfWhenAnyAuthenticationMethodThenConstructs() {\n\t\tString string = new String(\"any\");\n\t\tClientAuthenticationMethod method = ClientAuthenticationMethod.valueOf(string);\n\t\tassertThat(method.getValue()).isSameAs(string);\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> valueOfWhenMatchesStaticThenReturnsStatic() {\n\t\treturn Stream.of(ClientAuthenticationMethod.methods())\n\t\t\t.map((method) -> DynamicTest.dynamicTest(testName(method.getValue()),\n\t\t\t\t\t() -> assertThat(ClientAuthenticationMethod.valueOf(method.getValue())).isSameAs(method)));\n\t}\n\n\tString testName(String method) {\n\t\tString methodName = StringUtils.capitalize(method.replaceAll(\"_\", \"\"));\n\t\treturn \"valueOfWhen\" + methodName + \"ThenReturnsStatic\" + methodName;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/DefaultOAuth2AuthenticatedPrincipalTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link DefaultOAuth2AuthenticatedPrincipal}\n *\n * @author Josh Cummings\n */\npublic class DefaultOAuth2AuthenticatedPrincipalTests {\n\n\tString name = \"test-subject\";\n\n\tMap<String, Object> attributes = Collections.singletonMap(\"sub\", this.name);\n\n\tCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"SCOPE_read\");\n\n\t@Test\n\tpublic void constructorWhenAttributesIsNullOrEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2AuthenticatedPrincipal(null, this.authorities));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2AuthenticatedPrincipal(Collections.emptyMap(), this.authorities));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIsNullOrEmptyThenNoAuthorities() {\n\t\tCollection<? extends GrantedAuthority> authorities = new DefaultOAuth2AuthenticatedPrincipal(this.attributes,\n\t\t\t\tnull)\n\t\t\t.getAuthorities();\n\t\tassertThat(authorities).isEmpty();\n\t\tauthorities = new DefaultOAuth2AuthenticatedPrincipal(this.attributes, Collections.emptyList())\n\t\t\t.getAuthorities();\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void constructorWhenNameIsNullThenFallsbackToSubAttribute() {\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(null, this.attributes,\n\t\t\t\tthis.authorities);\n\t\tassertThat(principal.getName()).isEqualTo(this.attributes.get(\"sub\"));\n\t}\n\n\t@Test\n\tpublic void getNameWhenInConstructorThenReturns() {\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(\"other-subject\",\n\t\t\t\tthis.attributes, this.authorities);\n\t\tassertThat(principal.getName()).isEqualTo(\"other-subject\");\n\t}\n\n\t@Test\n\tpublic void getAttributeWhenGivenKeyThenReturnsValue() {\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(this.attributes,\n\t\t\t\tthis.authorities);\n\t\tassertThat((String) principal.getAttribute(\"sub\")).isEqualTo(\"test-subject\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/DelegatingOAuth2TokenValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for verifying {@link DelegatingOAuth2TokenValidator}\n *\n * @author Josh Cummings\n */\npublic class DelegatingOAuth2TokenValidatorTests {\n\n\tprivate static final OAuth2Error DETAIL = new OAuth2Error(\"error\", \"description\", \"uri\");\n\n\t@Test\n\tpublic void validateWhenNoValidatorsConfiguredThenReturnsSuccessfulResult() {\n\t\tDelegatingOAuth2TokenValidator<OAuth2Token> tokenValidator = new DelegatingOAuth2TokenValidator<>();\n\t\tOAuth2Token token = mock(OAuth2Token.class);\n\t\tassertThat(tokenValidator.validate(token).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void validateWhenAnyValidatorFailsThenReturnsFailureResultContainingDetailFromFailingValidator() {\n\t\tOAuth2TokenValidator<OAuth2Token> success = mock(OAuth2TokenValidator.class);\n\t\tOAuth2TokenValidator<OAuth2Token> failure = mock(OAuth2TokenValidator.class);\n\t\tgiven(success.validate(any(OAuth2Token.class))).willReturn(OAuth2TokenValidatorResult.success());\n\t\tgiven(failure.validate(any(OAuth2Token.class))).willReturn(OAuth2TokenValidatorResult.failure(DETAIL));\n\t\tDelegatingOAuth2TokenValidator<OAuth2Token> tokenValidator = new DelegatingOAuth2TokenValidator<>(\n\t\t\t\tArrays.asList(success, failure));\n\t\tOAuth2Token token = mock(OAuth2Token.class);\n\t\tOAuth2TokenValidatorResult result = tokenValidator.validate(token);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors()).containsExactly(DETAIL);\n\t}\n\n\t@Test\n\tpublic void validateWhenMultipleValidatorsFailThenReturnsFailureResultContainingAllDetails() {\n\t\tOAuth2TokenValidator<OAuth2Token> firstFailure = mock(OAuth2TokenValidator.class);\n\t\tOAuth2TokenValidator<OAuth2Token> secondFailure = mock(OAuth2TokenValidator.class);\n\t\tOAuth2Error otherDetail = new OAuth2Error(\"another-error\");\n\t\tgiven(firstFailure.validate(any(OAuth2Token.class))).willReturn(OAuth2TokenValidatorResult.failure(DETAIL));\n\t\tgiven(secondFailure.validate(any(OAuth2Token.class)))\n\t\t\t.willReturn(OAuth2TokenValidatorResult.failure(otherDetail));\n\t\tDelegatingOAuth2TokenValidator<OAuth2Token> tokenValidator = new DelegatingOAuth2TokenValidator<>(firstFailure,\n\t\t\t\tsecondFailure);\n\t\tOAuth2Token token = mock(OAuth2Token.class);\n\t\tOAuth2TokenValidatorResult result = tokenValidator.validate(token);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors()).containsExactly(DETAIL, otherDetail);\n\t}\n\n\t@Test\n\tpublic void validateWhenAllValidatorsSucceedThenReturnsSuccessfulResult() {\n\t\tOAuth2TokenValidator<OAuth2Token> firstSuccess = mock(OAuth2TokenValidator.class);\n\t\tOAuth2TokenValidator<OAuth2Token> secondSuccess = mock(OAuth2TokenValidator.class);\n\t\tgiven(firstSuccess.validate(any(OAuth2Token.class))).willReturn(OAuth2TokenValidatorResult.success());\n\t\tgiven(secondSuccess.validate(any(OAuth2Token.class))).willReturn(OAuth2TokenValidatorResult.success());\n\t\tDelegatingOAuth2TokenValidator<OAuth2Token> tokenValidator = new DelegatingOAuth2TokenValidator<>(\n\t\t\t\tArrays.asList(firstSuccess, secondSuccess));\n\t\tOAuth2Token token = mock(OAuth2Token.class);\n\t\tOAuth2TokenValidatorResult result = tokenValidator.validate(token);\n\t\tassertThat(result.hasErrors()).isFalse();\n\t\tassertThat(result.getErrors()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void constructorWhenInvokedWithNullValidatorListThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new DelegatingOAuth2TokenValidator<>((Collection<OAuth2TokenValidator<OAuth2Token>>) null));\n\t}\n\n\t@Test\n\tpublic void constructorsWhenInvokedWithSameInputsThenResultInSameOutputs() {\n\t\tOAuth2TokenValidator<OAuth2Token> firstSuccess = mock(OAuth2TokenValidator.class);\n\t\tOAuth2TokenValidator<OAuth2Token> secondSuccess = mock(OAuth2TokenValidator.class);\n\t\tgiven(firstSuccess.validate(any(OAuth2Token.class))).willReturn(OAuth2TokenValidatorResult.success());\n\t\tgiven(secondSuccess.validate(any(OAuth2Token.class))).willReturn(OAuth2TokenValidatorResult.success());\n\t\tDelegatingOAuth2TokenValidator<OAuth2Token> firstValidator = new DelegatingOAuth2TokenValidator<>(\n\t\t\t\tArrays.asList(firstSuccess, secondSuccess));\n\t\tDelegatingOAuth2TokenValidator<OAuth2Token> secondValidator = new DelegatingOAuth2TokenValidator<>(firstSuccess,\n\t\t\t\tsecondSuccess);\n\t\tOAuth2Token token = mock(OAuth2Token.class);\n\t\tfirstValidator.validate(token);\n\t\tsecondValidator.validate(token);\n\t\tverify(firstSuccess, times(2)).validate(token);\n\t\tverify(secondSuccess, times(2)).validate(token);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/OAuth2AccessTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.util.SerializationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AccessToken}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AccessTokenTests {\n\n\tprivate static final OAuth2AccessToken.TokenType TOKEN_TYPE = OAuth2AccessToken.TokenType.BEARER;\n\n\tprivate static final String TOKEN_VALUE = \"access-token\";\n\n\tprivate static final Instant ISSUED_AT = Instant.now();\n\n\tprivate static final Instant EXPIRES_AT = Instant.from(ISSUED_AT).plusSeconds(60);\n\n\tprivate static final Set<String> SCOPES = new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\n\t@Test\n\tpublic void tokenTypeGetValueWhenTokenTypeBearerThenReturnBearer() {\n\t\tassertThat(OAuth2AccessToken.TokenType.BEARER.getValue()).isEqualTo(\"Bearer\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenTypeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AccessToken(null, TOKEN_VALUE, ISSUED_AT, EXPIRES_AT));\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AccessToken(TOKEN_TYPE, null, ISSUED_AT, EXPIRES_AT));\n\t}\n\n\t@Test\n\tpublic void constructorWhenIssuedAtAfterExpiresAtThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AccessToken(TOKEN_TYPE, TOKEN_VALUE,\n\t\t\t\tInstant.from(EXPIRES_AT).plusSeconds(1), EXPIRES_AT));\n\t}\n\n\t@Test\n\tpublic void constructorWhenExpiresAtBeforeIssuedAtThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2AccessToken(TOKEN_TYPE, TOKEN_VALUE, ISSUED_AT,\n\t\t\t\tInstant.from(ISSUED_AT).minusSeconds(1)));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(TOKEN_TYPE, TOKEN_VALUE, ISSUED_AT, EXPIRES_AT, SCOPES);\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(TOKEN_TYPE);\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(TOKEN_VALUE);\n\t\tassertThat(accessToken.getIssuedAt()).isEqualTo(ISSUED_AT);\n\t\tassertThat(accessToken.getExpiresAt()).isEqualTo(EXPIRES_AT);\n\t\tassertThat(accessToken.getScopes()).isEqualTo(SCOPES);\n\t}\n\n\t// gh-5492\n\t@Test\n\tpublic void constructorWhenCreatedThenIsSerializableAndDeserializable() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(TOKEN_TYPE, TOKEN_VALUE, ISSUED_AT, EXPIRES_AT, SCOPES);\n\t\tbyte[] serialized = SerializationUtils.serialize(accessToken);\n\t\taccessToken = (OAuth2AccessToken) SerializationUtils.deserialize(serialized);\n\t\tassertThat(serialized).isNotNull();\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(TOKEN_TYPE);\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(TOKEN_VALUE);\n\t\tassertThat(accessToken.getIssuedAt()).isEqualTo(ISSUED_AT);\n\t\tassertThat(accessToken.getExpiresAt()).isEqualTo(EXPIRES_AT);\n\t\tassertThat(accessToken.getScopes()).isEqualTo(SCOPES);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/OAuth2ErrorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2Error}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2ErrorTests {\n\n\tprivate static final String ERROR_CODE = \"error-code\";\n\n\tprivate static final String ERROR_DESCRIPTION = \"error-description\";\n\n\tprivate static final String ERROR_URI = \"error-uri\";\n\n\t@Test\n\tpublic void constructorWhenErrorCodeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2Error(null, ERROR_DESCRIPTION, ERROR_URI));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2Error error = new OAuth2Error(ERROR_CODE, ERROR_DESCRIPTION, ERROR_URI);\n\t\tassertThat(error.getErrorCode()).isEqualTo(ERROR_CODE);\n\t\tassertThat(error.getDescription()).isEqualTo(ERROR_DESCRIPTION);\n\t\tassertThat(error.getUri()).isEqualTo(ERROR_URI);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/OAuth2RefreshTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2RefreshToken}.\n *\n * @author Alexey Nesterov\n */\npublic class OAuth2RefreshTokenTests {\n\n\tprivate static final String TOKEN_VALUE = \"refresh-token\";\n\n\tprivate static final Instant ISSUED_AT = Instant.now();\n\n\tprivate static final Instant EXPIRES_AT = Instant.from(ISSUED_AT).plusSeconds(60);\n\n\t@Test\n\tpublic void constructorWhenTokenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2RefreshToken(null, ISSUED_AT, EXPIRES_AT))\n\t\t\t.withMessage(\"tokenValue cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenIssuedAtAfterExpiresAtThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2RefreshToken(TOKEN_VALUE, Instant.from(EXPIRES_AT).plusSeconds(1), EXPIRES_AT))\n\t\t\t.withMessage(\"expiresAt must be after issuedAt\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenExpiresAtBeforeIssuedAtThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2RefreshToken(TOKEN_VALUE, ISSUED_AT, Instant.from(ISSUED_AT).minusSeconds(1)))\n\t\t\t.withMessage(\"expiresAt must be after issuedAt\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2RefreshToken token = new OAuth2RefreshToken(TOKEN_VALUE, ISSUED_AT, EXPIRES_AT);\n\t\tassertThat(token.getTokenValue()).isEqualTo(TOKEN_VALUE);\n\t\tassertThat(token.getIssuedAt()).isEqualTo(ISSUED_AT);\n\t\tassertThat(token.getExpiresAt()).isEqualTo(EXPIRES_AT);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/OAuth2TokenIntrospectionClaimAccessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2TokenIntrospectionClaimAccessor}.\n *\n * @author David Kovac\n */\npublic class OAuth2TokenIntrospectionClaimAccessorTests {\n\n\tprivate final Map<String, Object> claims = new HashMap<>();\n\n\tprivate final OAuth2TokenIntrospectionClaimAccessor claimAccessor = (() -> this.claims);\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.claims.clear();\n\t}\n\n\t@Test\n\tpublic void isActiveWhenActiveClaimNotExistingThenReturnFalse() {\n\t\tassertThat(this.claimAccessor.isActive()).isFalse();\n\t}\n\n\t@Test\n\tpublic void isActiveWhenActiveClaimValueIsNullThenReturnFalse() {\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, null);\n\t\tassertThat(this.claimAccessor.isActive()).isFalse();\n\t}\n\n\t@Test\n\tpublic void isActiveWhenActiveClaimValueIsTrueThenReturnTrue() {\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, \"true\");\n\t\tassertThat(this.claimAccessor.isActive()).isTrue();\n\t}\n\n\t@Test\n\tpublic void getUsernameWhenUsernameClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getUsername()).isNull();\n\t}\n\n\t@Test\n\tpublic void getUsernameWhenUsernameClaimExistingThenReturnUsername() {\n\t\tString expectedUsernameValue = \"username\";\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.USERNAME, expectedUsernameValue);\n\t\tassertThat(this.claimAccessor.getUsername()).isEqualTo(expectedUsernameValue);\n\t}\n\n\t@Test\n\tpublic void getClientIdWhenClientIdClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getUsername()).isNull();\n\t}\n\n\t@Test\n\tpublic void getClientIdWhenClientIdClaimExistingThenReturnClientId() {\n\t\tString expectedClientIdValue = \"clientId\";\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, expectedClientIdValue);\n\t\tassertThat(this.claimAccessor.getClientId()).isEqualTo(expectedClientIdValue);\n\t}\n\n\t@Test\n\tpublic void getScopesWhenScopeClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getScopes()).isNull();\n\t}\n\n\t@Test\n\tpublic void getScopesWhenScopeClaimExistingThenReturnScope() {\n\t\tList<String> expectedScopeValue = Arrays.asList(\"scope1\", \"scope2\");\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, expectedScopeValue);\n\t\tassertThat(this.claimAccessor.getScopes()).hasSameElementsAs(expectedScopeValue);\n\t}\n\n\t@Test\n\tpublic void getTokenTypeWhenTokenTypeClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getTokenType()).isNull();\n\t}\n\n\t@Test\n\tpublic void getTokenTypeWhenTokenTypeClaimExistingThenReturnTokenType() {\n\t\tString expectedTokenTypeValue = \"tokenType\";\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE, expectedTokenTypeValue);\n\t\tassertThat(this.claimAccessor.getTokenType()).isEqualTo(expectedTokenTypeValue);\n\t}\n\n\t@Test\n\tpublic void getExpiresAtWhenExpiresAtClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getExpiresAt()).isNull();\n\t}\n\n\t@Test\n\tpublic void getExpiresAtWhenExpiresAtClaimExistingThenReturnExpiresAt() {\n\t\tInstant expectedExpiresAtValue = Instant.now();\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.EXP, expectedExpiresAtValue);\n\t\tassertThat(this.claimAccessor.getExpiresAt()).isEqualTo(expectedExpiresAtValue);\n\t}\n\n\t@Test\n\tpublic void getIssuedAtWhenIssuedAtClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getExpiresAt()).isNull();\n\t}\n\n\t@Test\n\tpublic void getIssuedAtWhenIssuedAtClaimExistingThenReturnIssuedAt() {\n\t\tInstant expectedIssuedAtValue = Instant.now();\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.IAT, expectedIssuedAtValue);\n\t\tassertThat(this.claimAccessor.getIssuedAt()).isEqualTo(expectedIssuedAtValue);\n\t}\n\n\t@Test\n\tpublic void getNotBeforeWhenNotBeforeClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getNotBefore()).isNull();\n\t}\n\n\t@Test\n\tpublic void getNotBeforeWhenNotBeforeClaimExistingThenReturnNotBefore() {\n\t\tInstant expectedNotBeforeValue = Instant.now();\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.NBF, expectedNotBeforeValue);\n\t\tassertThat(this.claimAccessor.getNotBefore()).isEqualTo(expectedNotBeforeValue);\n\t}\n\n\t@Test\n\tpublic void getSubjectWhenSubjectClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getSubject()).isNull();\n\t}\n\n\t@Test\n\tpublic void getSubjectWhenSubjectClaimExistingThenReturnSubject() {\n\t\tString expectedSubjectValue = \"subject\";\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.SUB, expectedSubjectValue);\n\t\tassertThat(this.claimAccessor.getSubject()).isEqualTo(expectedSubjectValue);\n\t}\n\n\t@Test\n\tpublic void getAudienceWhenAudienceClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getAudience()).isNull();\n\t}\n\n\t@Test\n\tpublic void getAudienceWhenAudienceClaimExistingThenReturnAudience() {\n\t\tList<String> expectedAudienceValue = Arrays.asList(\"audience1\", \"audience2\");\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.AUD, expectedAudienceValue);\n\t\tassertThat(this.claimAccessor.getAudience()).hasSameElementsAs(expectedAudienceValue);\n\t}\n\n\t@Test\n\tpublic void getIssuerWhenIssuerClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getIssuer()).isNull();\n\t}\n\n\t@Test\n\tpublic void getIssuerWhenIssuerClaimExistingThenReturnIssuer() throws MalformedURLException {\n\t\tURL expectedIssuerValue = new URL(\"https://issuer.com\");\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.ISS, expectedIssuerValue);\n\t\tassertThat(this.claimAccessor.getIssuer()).isEqualTo(expectedIssuerValue);\n\t}\n\n\t@Test\n\tpublic void getIdWhenJtiClaimNotExistingThenReturnNull() {\n\t\tassertThat(this.claimAccessor.getId()).isNull();\n\t}\n\n\t@Test\n\tpublic void getIdWhenJtiClaimExistingThenReturnId() {\n\t\tString expectedIdValue = \"id\";\n\t\tthis.claims.put(OAuth2TokenIntrospectionClaimNames.JTI, expectedIdValue);\n\t\tassertThat(this.claimAccessor.getId()).isEqualTo(expectedIdValue);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/OAuth2TokenValidatorResultTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for verifying {@link OAuth2TokenValidatorResult}\n *\n * @author Josh Cummings\n */\npublic class OAuth2TokenValidatorResultTests {\n\n\tprivate static final OAuth2Error DETAIL = new OAuth2Error(\"error\", \"description\", \"uri\");\n\n\t@Test\n\tpublic void successWhenInvokedThenReturnsSuccessfulResult() {\n\t\tOAuth2TokenValidatorResult success = OAuth2TokenValidatorResult.success();\n\t\tassertThat(success.hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void failureWhenInvokedWithDetailReturnsFailureResultIncludingDetail() {\n\t\tOAuth2TokenValidatorResult failure = OAuth2TokenValidatorResult.failure(DETAIL);\n\t\tassertThat(failure.hasErrors()).isTrue();\n\t\tassertThat(failure.getErrors()).containsExactly(DETAIL);\n\t}\n\n\t@Test\n\tpublic void failureWhenInvokedWithMultipleDetailsReturnsFailureResultIncludingAll() {\n\t\tOAuth2TokenValidatorResult failure = OAuth2TokenValidatorResult.failure(DETAIL, DETAIL);\n\t\tassertThat(failure.hasErrors()).isTrue();\n\t\tassertThat(failure.getErrors()).containsExactly(DETAIL, DETAIL);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/TestOAuth2AccessTokens.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashSet;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic final class TestOAuth2AccessTokens {\n\n\tprivate TestOAuth2AccessTokens() {\n\t}\n\n\tpublic static OAuth2AccessToken noScopes() {\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"no-scopes\", Instant.now(),\n\t\t\t\tInstant.now().plus(Duration.ofDays(1)));\n\t}\n\n\tpublic static OAuth2AccessToken scopes(String... scopes) {\n\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"scopes\", Instant.now(),\n\t\t\t\tInstant.now().plus(Duration.ofDays(1)), new HashSet<>(Arrays.asList(scopes)));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/TestOAuth2RefreshTokens.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.time.Instant;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic final class TestOAuth2RefreshTokens {\n\n\tprivate TestOAuth2RefreshTokens() {\n\t}\n\n\tpublic static OAuth2RefreshToken refreshToken() {\n\t\treturn new OAuth2RefreshToken(\"refresh-token\", Instant.now());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagerFactoryTests.java",
    "content": "/*\n * Copyright 2025-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.authorization;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorityAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManagerFactories;\nimport org.springframework.security.authorization.AuthorizationResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2AuthorizationManagerFactory}.\n *\n * @author Ngoc Nhan\n */\npublic class OAuth2AuthorizationManagerFactoryTests {\n\n\tprivate static final String MSG_READ = \"message:read\";\n\n\tprivate static final String MSG_WRITE = \"message:write\";\n\n\tprivate static final String SCOPE_MSG_READ = \"SCOPE_message:read\";\n\n\tprivate static final String SCOPE_MSG_WRITE = \"SCOPE_message:write\";\n\n\t@Test\n\tpublic void hasScopeReturnsAuthorityAuthorizationManagerByDefault() {\n\t\tOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasScope(MSG_READ);\n\t\tassertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasAnyScopeReturnsAuthorityAuthorizationManagerByDefault() {\n\t\tOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasAnyScope(MSG_READ, MSG_WRITE);\n\t\tassertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasAllScopesReturnsAuthorityAuthorizationManagerByDefault() {\n\t\tOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>();\n\t\tAuthorizationManager<String> authorizationManager = factory.hasAnyScope(MSG_READ, MSG_WRITE);\n\t\tassertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class);\n\t}\n\n\t@Test\n\tpublic void hasScopeWhenSetAuthorizationManagerFactories() {\n\t\tDefaultOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>(\n\t\t\t\tAuthorizationManagerFactories.<String>multiFactor().requireFactors(SCOPE_MSG_READ).build());\n\t\tassertUserGranted(factory.hasScope(MSG_READ), SCOPE_MSG_READ);\n\t\tassertUserDenied(factory.hasScope(MSG_WRITE), SCOPE_MSG_READ);\n\t}\n\n\t@Test\n\tpublic void hasAnyScopeWhenSetAuthorizationManagerFactories() {\n\t\tDefaultOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>(\n\t\t\t\tAuthorizationManagerFactories.<String>multiFactor().requireFactors(SCOPE_MSG_READ).build());\n\t\tassertUserGranted(factory.hasAnyScope(MSG_READ), SCOPE_MSG_READ);\n\t\tassertUserDenied(factory.hasAnyScope(MSG_WRITE), SCOPE_MSG_READ);\n\t}\n\n\t@Test\n\tpublic void hasAllScopesWhenSetAuthorizationManagerFactories() {\n\t\tDefaultOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>(\n\t\t\t\tAuthorizationManagerFactories.<String>multiFactor()\n\t\t\t\t\t.requireFactors(SCOPE_MSG_READ, SCOPE_MSG_WRITE)\n\t\t\t\t\t.build());\n\t\tassertUserGranted(factory.hasAllScopes(MSG_READ, MSG_WRITE), SCOPE_MSG_READ, SCOPE_MSG_WRITE);\n\t\tassertUserDenied(factory.hasAllScopes(MSG_READ, MSG_WRITE), SCOPE_MSG_READ);\n\t}\n\n\tprivate void assertUserGranted(AuthorizationManager<String> manager, String... authorities) {\n\t\tAuthorizationResult authorizationResult = createAuthorizationResult(manager, authorities);\n\t\tassertThat(authorizationResult).isNotNull();\n\t\tassertThat(authorizationResult.isGranted()).isTrue();\n\t}\n\n\tprivate void assertUserDenied(AuthorizationManager<String> manager, String... authorities) {\n\t\tAuthorizationResult authorizationResult = createAuthorizationResult(manager, authorities);\n\t\tassertThat(authorizationResult).isNotNull();\n\t\tassertThat(authorizationResult.isGranted()).isFalse();\n\t}\n\n\tprivate AuthorizationResult createAuthorizationResult(AuthorizationManager<String> manager, String... authorities) {\n\t\tTestingAuthenticationToken authenticatedUser = new TestingAuthenticationToken(\"user\", \"pass\", authorities);\n\t\treturn manager.authorize(() -> authenticatedUser, \"\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.authorization;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2AuthorizationManagers}\n *\n * @author Mario Petrovski\n * @author Josh Cummings\n */\npublic class OAuth2AuthorizationManagersTests {\n\n\t@Test\n\tvoid hasScopeWhenInvalidScopeThenThrowIllegalArgument() {\n\t\tString scope = \"SCOPE_invalid\";\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationManagers.hasScope(scope))\n\t\t\t.withMessageContaining(\"SCOPE_invalid should not start with SCOPE_\");\n\t}\n\n\t@Test\n\tvoid hasAnyScopeWhenInvalidScopeThenThrowIllegalArgument() {\n\t\tString[] scopes = { \"read\", \"write\", \"SCOPE_invalid\" };\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2AuthorizationManagers.hasAnyScope(scopes))\n\t\t\t.withMessageContaining(\"SCOPE_invalid should not start with SCOPE_\");\n\t}\n\n\t@Test\n\tvoid hasScopeWhenValidScopeThenAuthorizationManager() {\n\t\tString scope = \"read\";\n\t\tAuthorizationManager<Object> authorizationManager = OAuth2AuthorizationManagers.hasScope(scope);\n\t\tauthorizationManager.verify(() -> hasScope(scope), new Object());\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> authorizationManager.verify(() -> hasScope(\"wrong\"), new Object()));\n\t}\n\n\t@Test\n\tvoid hasAnyScopeWhenValidScopesThenAuthorizationManager() {\n\t\tString[] scopes = { \"read\", \"write\" };\n\t\tAuthorizationManager<Object> authorizationManager = OAuth2AuthorizationManagers.hasAnyScope(scopes);\n\t\tfor (String scope : scopes) {\n\t\t\tauthorizationManager.verify(() -> hasScope(scope), new Object());\n\t\t}\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> authorizationManager.verify(() -> hasScope(\"wrong\"), new Object()));\n\t}\n\n\tAuthentication hasScope(String scope) {\n\t\treturn new TestingAuthenticationToken(\"user\", \"pass\", \"SCOPE_\" + scope);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2ReactiveAuthorizationManagersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.authorization;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OAuth2ReactiveAuthorizationManagers}\n *\n * @author Josh Cummings\n */\npublic class OAuth2ReactiveAuthorizationManagersTests {\n\n\t@Test\n\tvoid hasScopeWhenInvalidScopeThenThrowIllegalArgument() {\n\t\tString scope = \"SCOPE_invalid\";\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2ReactiveAuthorizationManagers.hasScope(scope))\n\t\t\t.withMessageContaining(\"SCOPE_invalid should not start with SCOPE_\");\n\t}\n\n\t@Test\n\tvoid hasAnyScopeWhenInvalidScopeThenThrowIllegalArgument() {\n\t\tString[] scopes = { \"read\", \"write\", \"SCOPE_invalid\" };\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> OAuth2ReactiveAuthorizationManagers.hasAnyScope(scopes))\n\t\t\t.withMessageContaining(\"SCOPE_invalid should not start with SCOPE_\");\n\t}\n\n\t@Test\n\tvoid hasScopeWhenValidScopeThenAuthorizationManager() {\n\t\tString scope = \"read\";\n\t\tReactiveAuthorizationManager<Object> authorizationManager = OAuth2ReactiveAuthorizationManagers.hasScope(scope);\n\t\tauthorizationManager.verify(hasScope(scope), new Object()).block();\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> authorizationManager.verify(hasScope(\"wrong\"), new Object()).block());\n\t}\n\n\t@Test\n\tvoid hasAnyScopeWhenValidScopesThenAuthorizationManager() {\n\t\tString[] scopes = { \"read\", \"write\" };\n\t\tReactiveAuthorizationManager<Object> authorizationManager = OAuth2ReactiveAuthorizationManagers\n\t\t\t.hasAnyScope(scopes);\n\t\tfor (String scope : scopes) {\n\t\t\tauthorizationManager.verify(hasScope(scope), new Object()).block();\n\t\t}\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> authorizationManager.verify(hasScope(\"wrong\"), new Object()).block());\n\t}\n\n\tMono<Authentication> hasScope(String scope) {\n\t\treturn Mono.just(new TestingAuthenticationToken(\"user\", \"pass\", \"SCOPE_\" + scope));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/converter/ClaimConversionServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport net.minidev.json.JSONArray;\nimport net.minidev.json.JSONObject;\nimport org.assertj.core.util.Lists;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.ConversionService;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ClaimConversionService}.\n *\n * @author Joe Grandja\n * @since 5.2\n */\npublic class ClaimConversionServiceTests {\n\n\tprivate final ConversionService conversionService = ClaimConversionService.getSharedInstance();\n\n\t@Test\n\tpublic void convertStringWhenNullThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(null, String.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertStringWhenStringThenReturnSame() {\n\t\tassertThat(this.conversionService.convert(\"string\", String.class)).isSameAs(\"string\");\n\t}\n\n\t@Test\n\tpublic void convertStringWhenNumberThenConverts() {\n\t\tassertThat(this.conversionService.convert(1234, String.class)).isEqualTo(\"1234\");\n\t}\n\n\t@Test\n\tpublic void convertBooleanWhenNullThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(null, Boolean.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertBooleanWhenBooleanThenReturnSame() {\n\t\tassertThat(this.conversionService.convert(Boolean.TRUE, Boolean.class)).isSameAs(Boolean.TRUE);\n\t}\n\n\t@Test\n\tpublic void convertBooleanWhenStringTrueThenConverts() {\n\t\tassertThat(this.conversionService.convert(\"true\", Boolean.class)).isEqualTo(Boolean.TRUE);\n\t}\n\n\t@Test\n\tpublic void convertBooleanWhenNotConvertibleThenReturnBooleanFalse() {\n\t\tassertThat(this.conversionService.convert(\"not-convertible-boolean\", Boolean.class)).isEqualTo(Boolean.FALSE);\n\t}\n\n\t@Test\n\tpublic void convertInstantWhenNullThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(null, Instant.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertInstantWhenInstantThenReturnSame() {\n\t\tInstant instant = Instant.now();\n\t\tassertThat(this.conversionService.convert(instant, Instant.class)).isSameAs(instant);\n\t}\n\n\t@Test\n\tpublic void convertInstantWhenDateThenConverts() {\n\t\tInstant instant = new Date().toInstant();\n\t\tassertThat(this.conversionService.convert(Date.from(instant), Instant.class)).isEqualTo(instant);\n\t}\n\n\t@Test\n\tpublic void convertInstantWhenNumberThenConverts() {\n\t\tInstant instant = Instant.now();\n\t\tassertThat(this.conversionService.convert(instant.getEpochSecond(), Instant.class))\n\t\t\t.isEqualTo(instant.truncatedTo(ChronoUnit.SECONDS));\n\t}\n\n\t@Test\n\tpublic void convertInstantWhenStringThenConverts() {\n\t\tInstant instant = Instant.now();\n\t\tassertThat(this.conversionService.convert(String.valueOf(instant.getEpochSecond()), Instant.class))\n\t\t\t.isEqualTo(instant.truncatedTo(ChronoUnit.SECONDS));\n\t\tassertThat(this.conversionService.convert(String.valueOf(instant.toString()), Instant.class))\n\t\t\t.isEqualTo(instant);\n\t}\n\n\t@Test\n\tpublic void convertInstantWhenNotConvertibleThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(\"not-convertible-instant\", Instant.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertUrlWhenNullThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(null, URL.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertUrlWhenUrlThenReturnSame() throws Exception {\n\t\tURL url = new URL(\"https://localhost\");\n\t\tassertThat(this.conversionService.convert(url, URL.class)).isSameAs(url);\n\t}\n\n\t@Test\n\tpublic void convertUrlWhenStringThenConverts() throws Exception {\n\t\tString urlString = \"https://localhost\";\n\t\tURL url = new URL(urlString);\n\t\tassertThat(this.conversionService.convert(urlString, URL.class)).isEqualTo(url);\n\t}\n\n\t@Test\n\tpublic void convertUrlWhenNotConvertibleThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(\"not-convertible-url\", URL.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertCollectionStringWhenNullThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(null, Collection.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertCollectionStringWhenListStringThenReturnNotSameButEqual() {\n\t\tList<String> list = Lists.list(\"1\", \"2\", \"3\", \"4\");\n\t\tassertThat(this.conversionService.convert(list, Collection.class)).isNotSameAs(list).isEqualTo(list);\n\t}\n\n\t@Test\n\tpublic void convertCollectionStringWhenListNumberThenConverts() {\n\t\tassertThat(this.conversionService.convert(Lists.list(1, 2, 3, 4), Collection.class))\n\t\t\t.isEqualTo(Lists.list(\"1\", \"2\", \"3\", \"4\"));\n\t}\n\n\t@Test\n\tpublic void convertListStringWhenJsonArrayThenConverts() {\n\t\tJSONArray jsonArray = new JSONArray();\n\t\tjsonArray.add(\"1\");\n\t\tjsonArray.add(\"2\");\n\t\tjsonArray.add(\"3\");\n\t\tjsonArray.add(null);\n\t\tassertThat(this.conversionService.convert(jsonArray, List.class)).isNotInstanceOf(JSONArray.class)\n\t\t\t.isEqualTo(Lists.list(\"1\", \"2\", \"3\"));\n\t}\n\n\t@Test\n\tpublic void convertCollectionStringWhenNotConvertibleThenReturnSingletonList() {\n\t\tString string = \"not-convertible-collection\";\n\t\tassertThat(this.conversionService.convert(string, Collection.class))\n\t\t\t.isEqualTo(Collections.singletonList(string));\n\t}\n\n\t@Test\n\tpublic void convertListStringWhenNullThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(null, List.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertListStringWhenListStringThenReturnNotSameButEqual() {\n\t\tList<String> list = Lists.list(\"1\", \"2\", \"3\", \"4\");\n\t\tassertThat(this.conversionService.convert(list, List.class)).isNotSameAs(list).isEqualTo(list);\n\t}\n\n\t@Test\n\tpublic void convertListStringWhenListNumberThenConverts() {\n\t\tassertThat(this.conversionService.convert(Lists.list(1, 2, 3, 4), List.class))\n\t\t\t.isEqualTo(Lists.list(\"1\", \"2\", \"3\", \"4\"));\n\t}\n\n\t@Test\n\tpublic void convertListStringWhenNotConvertibleThenReturnSingletonList() {\n\t\tString string = \"not-convertible-list\";\n\t\tassertThat(this.conversionService.convert(string, List.class)).isEqualTo(Collections.singletonList(string));\n\t}\n\n\t@Test\n\tpublic void convertMapStringObjectWhenNullThenReturnNull() {\n\t\tassertThat(this.conversionService.convert(null, Map.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertMapStringObjectWhenMapStringObjectThenReturnNotSameButEqual() {\n\t\tMap<String, Object> mapStringObject = new HashMap<String, Object>() {\n\t\t\t{\n\t\t\t\tput(\"key1\", \"value1\");\n\t\t\t\tput(\"key2\", \"value2\");\n\t\t\t\tput(\"key3\", \"value3\");\n\t\t\t}\n\t\t};\n\t\tassertThat(this.conversionService.convert(mapStringObject, Map.class)).isNotSameAs(mapStringObject)\n\t\t\t.isEqualTo(mapStringObject);\n\t}\n\n\t@Test\n\tpublic void convertMapStringObjectWhenMapIntegerObjectThenConverts() {\n\t\tMap<String, Object> mapStringObject = new HashMap<String, Object>() {\n\t\t\t{\n\t\t\t\tput(\"1\", \"value1\");\n\t\t\t\tput(\"2\", \"value2\");\n\t\t\t\tput(\"3\", \"value3\");\n\t\t\t}\n\t\t};\n\t\tMap<Integer, Object> mapIntegerObject = new HashMap<Integer, Object>() {\n\t\t\t{\n\t\t\t\tput(1, \"value1\");\n\t\t\t\tput(2, \"value2\");\n\t\t\t\tput(3, \"value3\");\n\t\t\t}\n\t\t};\n\t\tassertThat(this.conversionService.convert(mapIntegerObject, Map.class)).isEqualTo(mapStringObject);\n\t}\n\n\t@Test\n\tpublic void convertMapStringObjectWhenJsonObjectThenConverts() {\n\t\tJSONObject jsonObject = new JSONObject();\n\t\tjsonObject.put(\"1\", \"value1\");\n\t\tjsonObject.put(\"2\", \"value2\");\n\n\t\tMap<String, Object> mapStringObject = new HashMap<String, Object>() {\n\t\t\t{\n\t\t\t\tput(\"1\", \"value1\");\n\t\t\t\tput(\"2\", \"value2\");\n\t\t\t}\n\t\t};\n\t\tassertThat(this.conversionService.convert(jsonObject, Map.class)).isNotInstanceOf(JSONObject.class)\n\t\t\t.isEqualTo(mapStringObject);\n\t}\n\n\t@Test\n\tpublic void convertMapStringObjectWhenNotConvertibleThenReturnNull() {\n\t\tList<String> notConvertibleList = Lists.list(\"1\", \"2\", \"3\", \"4\");\n\t\tassertThat(this.conversionService.convert(notConvertibleList, Map.class)).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/converter/ClaimTypeConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.converter;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport net.minidev.json.JSONArray;\nimport net.minidev.json.JSONObject;\nimport org.assertj.core.util.Lists;\nimport org.assertj.core.util.Maps;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ClaimTypeConverter}.\n *\n * @author Joe Grandja\n * @since 5.2\n */\npublic class ClaimTypeConverterTests {\n\n\tprivate static final String STRING_CLAIM = \"string-claim\";\n\n\tprivate static final String BOOLEAN_CLAIM = \"boolean-claim\";\n\n\tprivate static final String INSTANT_CLAIM = \"instant-claim\";\n\n\tprivate static final String URL_CLAIM = \"url-claim\";\n\n\tprivate static final String COLLECTION_STRING_CLAIM = \"collection-string-claim\";\n\n\tprivate static final String LIST_STRING_CLAIM = \"list-string-claim\";\n\n\tprivate static final String MAP_STRING_OBJECT_CLAIM = \"map-string-object-claim\";\n\n\tprivate static final String JSON_ARRAY_CLAIM = \"json-array-claim\";\n\n\tprivate static final String JSON_OBJECT_CLAIM = \"json-object-claim\";\n\n\tprivate ClaimTypeConverter claimTypeConverter;\n\n\t@BeforeEach\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void setup() {\n\t\tConverter<Object, ?> stringConverter = getConverter(TypeDescriptor.valueOf(String.class));\n\t\tConverter<Object, ?> booleanConverter = getConverter(TypeDescriptor.valueOf(Boolean.class));\n\t\tConverter<Object, ?> instantConverter = getConverter(TypeDescriptor.valueOf(Instant.class));\n\t\tConverter<Object, ?> urlConverter = getConverter(TypeDescriptor.valueOf(URL.class));\n\t\tConverter<Object, ?> collectionStringConverter = getConverter(\n\t\t\t\tTypeDescriptor.collection(Collection.class, TypeDescriptor.valueOf(String.class)));\n\t\tConverter<Object, ?> listStringConverter = getConverter(\n\t\t\t\tTypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));\n\t\tConverter<Object, ?> mapStringObjectConverter = getConverter(TypeDescriptor.map(Map.class,\n\t\t\t\tTypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Object.class)));\n\t\tMap<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();\n\t\tclaimTypeConverters.put(STRING_CLAIM, stringConverter);\n\t\tclaimTypeConverters.put(BOOLEAN_CLAIM, booleanConverter);\n\t\tclaimTypeConverters.put(INSTANT_CLAIM, instantConverter);\n\t\tclaimTypeConverters.put(URL_CLAIM, urlConverter);\n\t\tclaimTypeConverters.put(COLLECTION_STRING_CLAIM, collectionStringConverter);\n\t\tclaimTypeConverters.put(LIST_STRING_CLAIM, listStringConverter);\n\t\tclaimTypeConverters.put(MAP_STRING_OBJECT_CLAIM, mapStringObjectConverter);\n\t\tthis.claimTypeConverter = new ClaimTypeConverter(claimTypeConverters);\n\t}\n\n\tprivate static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {\n\t\tfinal TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(Object.class);\n\t\treturn (source) -> ClaimConversionService.getSharedInstance()\n\t\t\t.convert(source, sourceDescriptor, targetDescriptor);\n\t}\n\n\t@Test\n\tpublic void constructorWhenConvertersNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ClaimTypeConverter(null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenConvertersHasNullConverterThenThrowIllegalArgumentException() {\n\t\tMap<String, Converter<Object, ?>> claimTypeConverters = new HashMap<>();\n\t\tclaimTypeConverters.put(\"claim1\", null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ClaimTypeConverter(claimTypeConverters));\n\t}\n\n\t@Test\n\tpublic void convertWhenClaimsEmptyThenReturnSame() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tassertThat(this.claimTypeConverter.convert(claims)).isSameAs(claims);\n\t}\n\n\t@Test\n\tpublic void convertWhenAllClaimsRequireConversionThenConvertAll() throws Exception {\n\t\tInstant instant = Instant.now();\n\t\tURL url = new URL(\"https://localhost\");\n\t\tList<Number> listNumber = Lists.list(1, 2, 3, 4);\n\t\tList<String> listString = Lists.list(\"1\", \"2\", \"3\", \"4\");\n\t\tMap<Integer, Object> mapIntegerObject = new HashMap<>();\n\t\tmapIntegerObject.put(1, \"value1\");\n\t\tMap<String, Object> mapStringObject = new HashMap<>();\n\t\tmapStringObject.put(\"1\", \"value1\");\n\t\tJSONArray jsonArray = new JSONArray();\n\t\tjsonArray.add(\"1\");\n\t\tList<String> jsonArrayListString = Lists.list(\"1\");\n\t\tJSONObject jsonObject = new JSONObject();\n\t\tjsonObject.put(\"1\", \"value1\");\n\t\tMap<String, Object> jsonObjectMap = Maps.newHashMap(\"1\", \"value1\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(STRING_CLAIM, Boolean.TRUE);\n\t\tclaims.put(BOOLEAN_CLAIM, \"true\");\n\t\tclaims.put(INSTANT_CLAIM, instant.toString());\n\t\tclaims.put(URL_CLAIM, url.toExternalForm());\n\t\tclaims.put(COLLECTION_STRING_CLAIM, listNumber);\n\t\tclaims.put(LIST_STRING_CLAIM, listNumber);\n\t\tclaims.put(MAP_STRING_OBJECT_CLAIM, mapIntegerObject);\n\t\tclaims.put(JSON_ARRAY_CLAIM, jsonArray);\n\t\tclaims.put(JSON_OBJECT_CLAIM, jsonObject);\n\t\tclaims = this.claimTypeConverter.convert(claims);\n\t\tassertThat(claims).containsEntry(STRING_CLAIM, \"true\");\n\t\tassertThat(claims).containsEntry(BOOLEAN_CLAIM, Boolean.TRUE);\n\t\tassertThat(claims).containsEntry(INSTANT_CLAIM, instant);\n\t\tassertThat(claims).containsEntry(URL_CLAIM, url);\n\t\tassertThat(claims).containsEntry(COLLECTION_STRING_CLAIM, listString);\n\t\tassertThat(claims).containsEntry(LIST_STRING_CLAIM, listString);\n\t\tassertThat(claims).containsEntry(MAP_STRING_OBJECT_CLAIM, mapStringObject);\n\t\tassertThat(claims).containsEntry(JSON_ARRAY_CLAIM, jsonArrayListString);\n\t\tassertThat(claims).containsEntry(JSON_OBJECT_CLAIM, jsonObjectMap);\n\t}\n\n\t@Test\n\tpublic void convertWhenNoClaimsRequireConversionThenConvertNone() throws Exception {\n\t\tString string = \"value\";\n\t\tBoolean bool = Boolean.TRUE;\n\t\tInstant instant = Instant.now();\n\t\tURL url = new URL(\"https://localhost\");\n\t\tList<String> listString = Lists.list(\"1\", \"2\", \"3\", \"4\");\n\t\tMap<String, Object> mapStringObject = new HashMap<>();\n\t\tmapStringObject.put(\"1\", \"value1\");\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(STRING_CLAIM, string);\n\t\tclaims.put(BOOLEAN_CLAIM, bool);\n\t\tclaims.put(INSTANT_CLAIM, instant);\n\t\tclaims.put(URL_CLAIM, url);\n\t\tclaims.put(COLLECTION_STRING_CLAIM, listString);\n\t\tclaims.put(LIST_STRING_CLAIM, listString);\n\t\tclaims.put(MAP_STRING_OBJECT_CLAIM, mapStringObject);\n\t\tclaims = this.claimTypeConverter.convert(claims);\n\t\tassertThat(claims.get(STRING_CLAIM)).isSameAs(string);\n\t\tassertThat(claims.get(BOOLEAN_CLAIM)).isSameAs(bool);\n\t\tassertThat(claims.get(INSTANT_CLAIM)).isSameAs(instant);\n\t\tassertThat(claims.get(URL_CLAIM)).isSameAs(url);\n\t\tassertThat(claims.get(COLLECTION_STRING_CLAIM)).isNotSameAs(listString).isEqualTo(listString);\n\t\tassertThat(claims.get(LIST_STRING_CLAIM)).isNotSameAs(listString).isEqualTo(listString);\n\t\tassertThat(claims.get(MAP_STRING_OBJECT_CLAIM)).isNotSameAs(mapStringObject).isEqualTo(mapStringObject);\n\t}\n\n\t@Test\n\tpublic void convertWhenConverterNotAvailableThenDoesNotConvert() {\n\t\tMap<String, Object> claims = new HashMap<>();\n\t\tclaims.put(\"claim1\", \"value1\");\n\t\tclaims = this.claimTypeConverter.convert(claims);\n\t\tassertThat(claims.get(\"claim1\")).isSameAs(\"value1\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.time.Duration;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2RefreshToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link DefaultMapOAuth2AccessTokenResponseConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class DefaultMapOAuth2AccessTokenResponseConverterTests {\n\n\tprivate Converter<Map<String, Object>, OAuth2AccessTokenResponse> messageConverter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.messageConverter = new DefaultMapOAuth2AccessTokenResponseConverter();\n\t}\n\n\t@Test\n\tpublic void shouldConvertFull() {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"access_token\", \"access-token-1234\");\n\t\tmap.put(\"token_type\", \"bearer\");\n\t\tmap.put(\"expires_in\", \"3600\");\n\t\tmap.put(\"scope\", \"read write\");\n\t\tmap.put(\"refresh_token\", \"refresh-token-1234\");\n\t\tmap.put(\"custom_parameter_1\", \"custom-value-1\");\n\t\tmap.put(\"custom_parameter_2\", \"custom-value-2\");\n\t\tOAuth2AccessTokenResponse converted = this.messageConverter.convert(map);\n\t\tOAuth2AccessToken accessToken = converted.getAccessToken();\n\t\tassertThat(accessToken).isNotNull();\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tSet<String> scopes = accessToken.getScopes();\n\t\tassertThat(scopes).isNotNull();\n\t\tassertThat(scopes).hasSize(2);\n\t\tassertThat(scopes).contains(\"read\");\n\t\tassertThat(scopes).contains(\"write\");\n\t\tassertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds())\n\t\t\t.isEqualTo(3600);\n\t\tOAuth2RefreshToken refreshToken = converted.getRefreshToken();\n\t\tassertThat(refreshToken).isNotNull();\n\t\tassertThat(refreshToken.getTokenValue()).isEqualTo(\"refresh-token-1234\");\n\t\tMap<String, Object> additionalParameters = converted.getAdditionalParameters();\n\t\tassertThat(additionalParameters).isNotNull();\n\t\tassertThat(additionalParameters).hasSize(2);\n\t\tassertThat(additionalParameters).containsEntry(\"custom_parameter_1\", \"custom-value-1\");\n\t\tassertThat(additionalParameters).containsEntry(\"custom_parameter_2\", \"custom-value-2\");\n\t}\n\n\t@Test\n\tpublic void shouldConvertMinimal() {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"access_token\", \"access-token-1234\");\n\t\tmap.put(\"token_type\", \"bearer\");\n\t\tOAuth2AccessTokenResponse converted = this.messageConverter.convert(map);\n\t\tOAuth2AccessToken accessToken = converted.getAccessToken();\n\t\tassertThat(accessToken).isNotNull();\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tSet<String> scopes = accessToken.getScopes();\n\t\tassertThat(scopes).isNotNull();\n\t\tassertThat(scopes).isEmpty();\n\t\tassertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()).isEqualTo(1);\n\t\tOAuth2RefreshToken refreshToken = converted.getRefreshToken();\n\t\tassertThat(refreshToken).isNull();\n\t\tMap<String, Object> additionalParameters = converted.getAdditionalParameters();\n\t\tassertThat(additionalParameters).isNotNull();\n\t\tassertThat(additionalParameters).isEmpty();\n\t}\n\n\t@Test\n\tpublic void shouldConvertDPoPToken() {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"access_token\", \"access-token-1234\");\n\t\tmap.put(\"token_type\", \"dpop\");\n\t\tOAuth2AccessTokenResponse converted = this.messageConverter.convert(map);\n\t\tOAuth2AccessToken accessToken = converted.getAccessToken();\n\t\tassertThat(accessToken).isNotNull();\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.DPOP);\n\t}\n\n\t@Test\n\tpublic void shouldConvertWithUnsupportedExpiresIn() {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"access_token\", \"access-token-1234\");\n\t\tmap.put(\"token_type\", \"bearer\");\n\t\tmap.put(\"expires_in\", \"2100-01-01-abc\");\n\t\tOAuth2AccessTokenResponse converted = this.messageConverter.convert(map);\n\t\tOAuth2AccessToken accessToken = converted.getAccessToken();\n\t\tassertThat(accessToken).isNotNull();\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tSet<String> scopes = accessToken.getScopes();\n\t\tassertThat(scopes).isNotNull();\n\t\tassertThat(scopes).isEmpty();\n\t\tassertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()).isEqualTo(1);\n\t\tOAuth2RefreshToken refreshToken = converted.getRefreshToken();\n\t\tassertThat(refreshToken).isNull();\n\t\tMap<String, Object> additionalParameters = converted.getAdditionalParameters();\n\t\tassertThat(additionalParameters).isNotNull();\n\t\tassertThat(additionalParameters).isEmpty();\n\t}\n\n\t// gh-9685\n\t@Test\n\tpublic void shouldConvertWithNumericExpiresIn() {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"access_token\", \"access-token-1234\");\n\t\tmap.put(\"token_type\", \"bearer\");\n\t\tmap.put(\"expires_in\", 3600);\n\t\tOAuth2AccessTokenResponse converted = this.messageConverter.convert(map);\n\t\tOAuth2AccessToken accessToken = converted.getAccessToken();\n\t\tassertThat(accessToken).isNotNull();\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds())\n\t\t\t.isEqualTo(3600);\n\t}\n\n\t// gh-9685\n\t@Test\n\tpublic void shouldConvertWithObjectAdditionalParameter() {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"access_token\", \"access-token-1234\");\n\t\tmap.put(\"token_type\", \"bearer\");\n\t\tmap.put(\"expires_in\", \"3600\");\n\t\tmap.put(\"scope\", \"read write\");\n\t\tmap.put(\"refresh_token\", \"refresh-token-1234\");\n\t\tMap<String, Object> nestedObject = new LinkedHashMap<>();\n\t\tnestedObject.put(\"a\", \"first value\");\n\t\tnestedObject.put(\"b\", \"second value\");\n\t\tmap.put(\"custom_parameter_1\", nestedObject);\n\t\tmap.put(\"custom_parameter_2\", \"custom-value-2\");\n\t\tOAuth2AccessTokenResponse converted = this.messageConverter.convert(map);\n\t\tOAuth2AccessToken accessToken = converted.getAccessToken();\n\t\tassertThat(accessToken).isNotNull();\n\t\tassertThat(accessToken.getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tSet<String> scopes = accessToken.getScopes();\n\t\tassertThat(scopes).isNotNull();\n\t\tassertThat(scopes).hasSize(2);\n\t\tassertThat(scopes).contains(\"read\");\n\t\tassertThat(scopes).contains(\"write\");\n\t\tassertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds())\n\t\t\t.isEqualTo(3600);\n\t\tOAuth2RefreshToken refreshToken = converted.getRefreshToken();\n\t\tassertThat(refreshToken).isNotNull();\n\t\tassertThat(refreshToken.getTokenValue()).isEqualTo(\"refresh-token-1234\");\n\t\tMap<String, Object> additionalParameters = converted.getAdditionalParameters();\n\t\tassertThat(additionalParameters).isNotNull();\n\t\tassertThat(additionalParameters).hasSize(2);\n\t\tassertThat(additionalParameters).containsEntry(\"custom_parameter_1\", nestedObject);\n\t\tassertThat(additionalParameters).containsEntry(\"custom_parameter_2\", \"custom-value-2\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultOAuth2AccessTokenResponseMapConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link DefaultOAuth2AccessTokenResponseMapConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class DefaultOAuth2AccessTokenResponseMapConverterTests {\n\n\tprivate Converter<OAuth2AccessTokenResponse, Map<String, Object>> messageConverter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.messageConverter = new DefaultOAuth2AccessTokenResponseMapConverter();\n\t}\n\n\t@Test\n\tpublic void shouldConvertFull() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"custom_parameter_1\", \"custom-value-1\");\n\t\tadditionalParameters.put(\"custom_parameter_2\", \"custom-value-2\");\n\t\tSet<String> scopes = new HashSet<>();\n\t\tscopes.add(\"read\");\n\t\tscopes.add(\"write\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse build = OAuth2AccessTokenResponse.withToken(\"access-token-value-1234\")\n\t\t\t\t.expiresIn(3699)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.refreshToken(\"refresh-token-value-1234\")\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMap<String, Object> result = this.messageConverter.convert(build);\n\t\tassertThat(result).hasSize(7);\n\t\tassertThat(result).containsEntry(\"access_token\", \"access-token-value-1234\");\n\t\tassertThat(result).containsEntry(\"refresh_token\", \"refresh-token-value-1234\");\n\t\tassertThat(result).containsEntry(\"scope\", \"read write\");\n\t\tassertThat(result).containsEntry(\"token_type\", \"Bearer\");\n\t\tassertThat(result.get(\"expires_in\")).isNotNull();\n\t\tassertThat(result).containsEntry(\"custom_parameter_1\", \"custom-value-1\");\n\t\tassertThat(result).containsEntry(\"custom_parameter_2\", \"custom-value-2\");\n\t}\n\n\t@Test\n\tpublic void shouldConvertMinimal() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse build = OAuth2AccessTokenResponse.withToken(\"access-token-value-1234\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMap<String, Object> result = this.messageConverter.convert(build);\n\t\tassertThat(result).hasSize(3);\n\t\tassertThat(result).containsEntry(\"access_token\", \"access-token-value-1234\");\n\t\tassertThat(result).containsEntry(\"token_type\", \"Bearer\");\n\t\tassertThat(result.get(\"expires_in\")).isNotNull();\n\t}\n\n\t// gh-9685\n\t@Test\n\tpublic void shouldConvertWithObjectAdditionalParameter() {\n\t\tMap<String, Object> nestedObject = new LinkedHashMap<>();\n\t\tnestedObject.put(\"a\", \"first value\");\n\t\tnestedObject.put(\"b\", \"second value\");\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"custom_parameter_1\", nestedObject);\n\t\tadditionalParameters.put(\"custom_parameter_2\", \"custom-value-2\");\n\t\tSet<String> scopes = new HashSet<>();\n\t\tscopes.add(\"read\");\n\t\tscopes.add(\"write\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse build = OAuth2AccessTokenResponse.withToken(\"access-token-value-1234\")\n\t\t\t\t.expiresIn(3699)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.refreshToken(\"refresh-token-value-1234\")\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMap<String, Object> result = this.messageConverter.convert(build);\n\t\tassertThat(result).hasSize(7);\n\t\tassertThat(result).containsEntry(\"access_token\", \"access-token-value-1234\");\n\t\tassertThat(result).containsEntry(\"refresh_token\", \"refresh-token-value-1234\");\n\t\tassertThat(result).containsEntry(\"scope\", \"read write\");\n\t\tassertThat(result).containsEntry(\"token_type\", \"Bearer\");\n\t\tassertThat(result.get(\"expires_in\")).isNotNull();\n\t\tassertThat(result).containsEntry(\"custom_parameter_1\", nestedObject);\n\t\tassertThat(result).containsEntry(\"custom_parameter_2\", \"custom-value-2\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AccessTokenResponseTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AccessTokenResponse}.\n *\n * @author Luander Ribeiro\n * @author Joe Grandja\n */\npublic class OAuth2AccessTokenResponseTests {\n\n\tprivate static final String TOKEN_VALUE = \"access-token\";\n\n\tprivate static final String REFRESH_TOKEN_VALUE = \"refresh-token\";\n\n\tprivate static final long EXPIRES_IN = Instant.now().plusSeconds(5).toEpochMilli();\n\n\t@Test\n\tpublic void buildWhenTokenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tOAuth2AccessTokenResponse.withToken(null)\n\t\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t\t.expiresIn(EXPIRES_IN)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildWhenTokenTypeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tOAuth2AccessTokenResponse.withToken(TOKEN_VALUE)\n\t\t\t\t\t.tokenType(null)\n\t\t\t\t\t.expiresIn(EXPIRES_IN)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildWhenExpiresInIsZeroThenExpiresAtOneSecondAfterIssueAt() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse tokenResponse = OAuth2AccessTokenResponse.withToken(TOKEN_VALUE)\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(0)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(tokenResponse.getAccessToken().getExpiresAt())\n\t\t\t.isEqualTo(tokenResponse.getAccessToken().getIssuedAt().plusSeconds(1));\n\t}\n\n\t@Test\n\tpublic void buildWhenExpiresInIsNegativeThenExpiresAtOneSecondAfterIssueAt() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse tokenResponse = OAuth2AccessTokenResponse.withToken(TOKEN_VALUE)\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(-1L)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(tokenResponse.getAccessToken().getExpiresAt())\n\t\t\t.isEqualTo(tokenResponse.getAccessToken().getIssuedAt().plusSeconds(1));\n\t}\n\n\t@Test\n\tpublic void buildWhenAllAttributesProvidedThenAllAttributesAreSet() {\n\t\tInstant expiresAt = Instant.now().plusSeconds(5);\n\t\tSet<String> scopes = new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse tokenResponse = OAuth2AccessTokenResponse.withToken(TOKEN_VALUE)\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(expiresAt.toEpochMilli())\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.refreshToken(REFRESH_TOKEN_VALUE)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(tokenResponse.getAccessToken()).isNotNull();\n\t\tassertThat(tokenResponse.getAccessToken().getTokenValue()).isEqualTo(TOKEN_VALUE);\n\t\tassertThat(tokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(tokenResponse.getAccessToken().getIssuedAt()).isNotNull();\n\t\tassertThat(tokenResponse.getAccessToken().getExpiresAt()).isAfterOrEqualTo(expiresAt);\n\t\tassertThat(tokenResponse.getAccessToken().getScopes()).isEqualTo(scopes);\n\t\tassertThat(tokenResponse.getRefreshToken().getTokenValue()).isEqualTo(REFRESH_TOKEN_VALUE);\n\t\tassertThat(tokenResponse.getAdditionalParameters()).isEqualTo(additionalParameters);\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseThenAllAttributesAreSet() {\n\t\tInstant expiresAt = Instant.now().plusSeconds(5);\n\t\tSet<String> scopes = new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse tokenResponse = OAuth2AccessTokenResponse.withToken(TOKEN_VALUE)\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(expiresAt.toEpochMilli())\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.refreshToken(REFRESH_TOKEN_VALUE)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AccessTokenResponse withResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse).build();\n\t\tassertThat(withResponse.getAccessToken().getTokenValue())\n\t\t\t.isEqualTo(tokenResponse.getAccessToken().getTokenValue());\n\t\tassertThat(withResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(withResponse.getAccessToken().getIssuedAt()).isEqualTo(tokenResponse.getAccessToken().getIssuedAt());\n\t\tassertThat(withResponse.getAccessToken().getExpiresAt())\n\t\t\t.isEqualTo(tokenResponse.getAccessToken().getExpiresAt());\n\t\tassertThat(withResponse.getAccessToken().getScopes()).isEqualTo(tokenResponse.getAccessToken().getScopes());\n\t\tassertThat(withResponse.getRefreshToken().getTokenValue())\n\t\t\t.isEqualTo(tokenResponse.getRefreshToken().getTokenValue());\n\t\tassertThat(withResponse.getAdditionalParameters()).isEqualTo(tokenResponse.getAdditionalParameters());\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseAndRefreshNullThenRefreshNull() {\n\t\tInstant expiresAt = Instant.now().plusSeconds(5);\n\t\tSet<String> scopes = new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse tokenResponse = OAuth2AccessTokenResponse.withToken(TOKEN_VALUE)\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(expiresAt.toEpochMilli())\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tOAuth2AccessTokenResponse withResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse).build();\n\t\tassertThat(withResponse.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void buildWhenResponseAndExpiresInThenExpiresAtEqualToIssuedAtPlusExpiresIn() {\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse tokenResponse = OAuth2AccessTokenResponse.withToken(TOKEN_VALUE)\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tlong expiresIn = 30;\n\t\tOAuth2AccessTokenResponse withResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse)\n\t\t\t.expiresIn(expiresIn)\n\t\t\t.build();\n\t\tassertThat(withResponse.getAccessToken().getExpiresAt())\n\t\t\t.isEqualTo(withResponse.getAccessToken().getIssuedAt().plusSeconds(expiresIn));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationExchangeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizationExchange}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationExchangeTests {\n\n\t@Test\n\tpublic void constructorWhenAuthorizationRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new OAuth2AuthorizationExchange(null, TestOAuth2AuthorizationResponses.success().build()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorizationResponseIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2AuthorizationExchange(TestOAuth2AuthorizationRequests.request().build(), null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRequiredArgsProvidedThenCreated() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request().build();\n\t\tOAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.success().build();\n\t\tOAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest,\n\t\t\t\tauthorizationResponse);\n\t\tassertThat(authorizationExchange.getAuthorizationRequest()).isEqualTo(authorizationRequest);\n\t\tassertThat(authorizationExchange.getAuthorizationResponse()).isEqualTo(authorizationResponse);\n\t}\n\n\t@Test\n\tvoid oauth2AuthorizationExchangeShouldBeSerializable() throws IOException {\n\t\tOAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success();\n\t\ttry (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n\t\t\t\tObjectOutputStream objectOutputStream = new ObjectOutputStream(baos)) {\n\t\t\tobjectOutputStream.writeObject(exchange);\n\t\t\tobjectOutputStream.flush();\n\t\t\tassertThat(baos.size()).isNotZero();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.net.URI;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizationRequest}.\n *\n * @author Luander Ribeiro\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationRequestTests {\n\n\tprivate static final String AUTHORIZATION_URI = \"https://provider.com/oauth2/authorize\";\n\n\tprivate static final String CLIENT_ID = \"client-id\";\n\n\tprivate static final String REDIRECT_URI = \"https://example.com\";\n\n\tprivate static final Set<String> SCOPES = new LinkedHashSet<>(Arrays.asList(\"scope1\", \"scope2\"));\n\n\tprivate static final String STATE = \"state\";\n\n\t@Test\n\tpublic void buildWhenAuthorizationUriIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> OAuth2AuthorizationRequest\n\t\t\t\t\t\t.authorizationCode()\n\t\t\t\t\t\t.authorizationUri(null)\n\t\t\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t\t.scopes(SCOPES)\n\t\t\t\t\t\t.state(STATE)\n\t\t\t\t\t\t.build()\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenClientIdIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t\t\t.clientId(null)\n\t\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t\t.scopes(SCOPES)\n\t\t\t\t\t\t.state(STATE)\n\t\t\t\t\t\t.build()\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenRedirectUriIsNullForAuthorizationCodeThenDoesNotThrowAnyException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(null)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(STATE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenScopesIsNullThenDoesNotThrowAnyException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scopes(null)\n\t\t\t\t.state(STATE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenStateIsNullThenDoesNotThrowAnyException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenAdditionalParametersEmptyThenDoesNotThrowAnyException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(STATE)\n\t\t\t\t.additionalParameters(Map::clear)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationCodeThenGrantTypeResponseTypeIsSet() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(null)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(STATE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t}\n\n\t@Test\n\tpublic void buildWhenAllValuesProvidedThenAllValuesAreSet() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(\"attribute1\", \"value1\");\n\t\tattributes.put(\"attribute2\", \"value2\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(STATE)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.authorizationRequestUri(AUTHORIZATION_URI)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(authorizationRequest.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);\n\t\tassertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t\tassertThat(authorizationRequest.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(authorizationRequest.getRedirectUri()).isEqualTo(REDIRECT_URI);\n\t\tassertThat(authorizationRequest.getScopes()).isEqualTo(SCOPES);\n\t\tassertThat(authorizationRequest.getState()).isEqualTo(STATE);\n\t\tassertThat(authorizationRequest.getAdditionalParameters()).isEqualTo(additionalParameters);\n\t\tassertThat(authorizationRequest.getAttributes()).isEqualTo(attributes);\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isEqualTo(AUTHORIZATION_URI);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationRequestUriSetThenOverridesDefault() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(STATE)\n\t\t\t\t.authorizationRequestUri(AUTHORIZATION_URI)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isEqualTo(AUTHORIZATION_URI);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationRequestUriFunctionSetThenOverridesDefault() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(STATE)\n\t\t\t\t.authorizationRequestUri((uriBuilder) -> URI.create(AUTHORIZATION_URI))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isEqualTo(AUTHORIZATION_URI);\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationRequestUriNotSetThenDefaultSet() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t.scopes(SCOPES)\n\t\t\t.state(STATE)\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isEqualTo(\"https://provider.com/oauth2/authorize?\"\n\t\t\t\t+ \"response_type=code&client_id=client-id&\" + \"scope=scope1%20scope2&state=state&\"\n\t\t\t\t+ \"redirect_uri=https://example.com&param1=value1&param2=value2\");\n\t}\n\n\t@Test\n\tpublic void buildWhenRequiredParametersSetThenAuthorizationRequestUriIncludesRequiredParametersOnly() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t.clientId(CLIENT_ID)\n\t\t\t.build();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(\"https://provider.com/oauth2/authorize?response_type=code&client_id=client-id\");\n\t}\n\n\t@Test\n\tpublic void fromWhenAuthorizationRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationRequest.from(null));\n\t}\n\n\t@Test\n\tpublic void fromWhenAuthorizationRequestProvidedThenValuesAreCopied() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(\"attribute1\", \"value1\");\n\t\tattributes.put(\"attribute2\", \"value2\");\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(STATE)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.build();\n\t\tOAuth2AuthorizationRequest authorizationRequestCopy = OAuth2AuthorizationRequest.from(authorizationRequest)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(authorizationRequestCopy.getAuthorizationUri())\n\t\t\t.isEqualTo(authorizationRequest.getAuthorizationUri());\n\t\tassertThat(authorizationRequestCopy.getGrantType()).isEqualTo(authorizationRequest.getGrantType());\n\t\tassertThat(authorizationRequestCopy.getResponseType()).isEqualTo(authorizationRequest.getResponseType());\n\t\tassertThat(authorizationRequestCopy.getClientId()).isEqualTo(authorizationRequest.getClientId());\n\t\tassertThat(authorizationRequestCopy.getRedirectUri()).isEqualTo(authorizationRequest.getRedirectUri());\n\t\tassertThat(authorizationRequestCopy.getScopes()).isEqualTo(authorizationRequest.getScopes());\n\t\tassertThat(authorizationRequestCopy.getState()).isEqualTo(authorizationRequest.getState());\n\t\tassertThat(authorizationRequestCopy.getAdditionalParameters())\n\t\t\t.isEqualTo(authorizationRequest.getAdditionalParameters());\n\t\tassertThat(authorizationRequestCopy.getAttributes()).isEqualTo(authorizationRequest.getAttributes());\n\t\tassertThat(authorizationRequestCopy.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(authorizationRequest.getAuthorizationRequestUri());\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationUriIncludesQueryParameterThenAuthorizationRequestUrlIncludesIt() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.authorizationUri(AUTHORIZATION_URI + \"?param1=value1&param2=value2\")\n\t\t\t.build();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isEqualTo(\"https://provider.com/oauth2/authorize?\"\n\t\t\t\t+ \"param1=value1&param2=value2&\" + \"response_type=code&client_id=client-id&state=state&\"\n\t\t\t\t+ \"redirect_uri=https://example.com/authorize/oauth2/code/registration-id\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAuthorizationUriIncludesEscapedQueryParameterThenAuthorizationRequestUrlIncludesIt() {\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.authorizationUri(AUTHORIZATION_URI\n\t\t\t\t\t+ \"?claims=%7B%22userinfo%22%3A%7B%22email_verified%22%3A%7B%22essential%22%3Atrue%7D%7D%7D\")\n\t\t\t.build();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isEqualTo(\"https://provider.com/oauth2/authorize?\"\n\t\t\t\t+ \"claims=%7B%22userinfo%22%3A%7B%22email_verified%22%3A%7B%22essential%22%3Atrue%7D%7D%7D&\"\n\t\t\t\t+ \"response_type=code&client_id=client-id&state=state&\"\n\t\t\t\t+ \"redirect_uri=https://example.com/authorize/oauth2/code/registration-id\");\n\t}\n\n\t@Test\n\tpublic void buildWhenNonAsciiAdditionalParametersThenProperlyEncoded() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"item amount\", \"19.95\" + '\\u20ac');\n\t\tadditionalParameters.put(\"item name\", \"H\" + '\\u00c5' + \"M\" + '\\u00d6');\n\t\tadditionalParameters.put('\\u00e2' + \"ge\", \"4\" + '\\u00bd');\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isEqualTo(\n\t\t\t\t\"https://example.com/login/oauth/authorize?\" + \"response_type=code&client_id=client-id&state=state&\"\n\t\t\t\t\t\t+ \"redirect_uri=https://example.com/authorize/oauth2/code/registration-id&\"\n\t\t\t\t\t\t+ \"item%20amount=19.95%E2%82%AC&%C3%A2ge=4%C2%BD&item%20name=H%C3%85M%C3%96\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAdditionalParametersContainsArrayThenProperlyEncoded() {\n\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>();\n\t\tadditionalParameters.put(\"item1\", new String[] { \"1\", \"2\" });\n\t\tadditionalParameters.put(\"item2\", \"value2\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(\"https://example.com/login/oauth/authorize?response_type=code&client_id=client-id&state=state&\"\n\t\t\t\t\t+ \"redirect_uri=https://example.com/authorize/oauth2/code/registration-id&\"\n\t\t\t\t\t+ \"item1=1&item1=2&item2=value2\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAdditionalParametersContainsIterableThenProperlyEncoded() {\n\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>();\n\t\tadditionalParameters.put(\"item1\", Arrays.asList(\"1\", \"2\"));\n\t\tadditionalParameters.put(\"item2\", \"value2\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(\"https://example.com/login/oauth/authorize?response_type=code&client_id=client-id&state=state&\"\n\t\t\t\t\t+ \"redirect_uri=https://example.com/authorize/oauth2/code/registration-id&\"\n\t\t\t\t\t+ \"item1=1&item1=2&item2=value2\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAdditionalParametersContainsNullThenAuthorizationRequestUriContainsNull() {\n\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>();\n\t\tadditionalParameters.put(\"item1\", null);\n\t\tadditionalParameters.put(\"item2\", \"value2\");\n\t\tOAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request()\n\t\t\t.additionalParameters(additionalParameters)\n\t\t\t.build();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri()).isNotNull();\n\t\tassertThat(authorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(\"https://example.com/login/oauth/authorize?response_type=code&client_id=client-id&state=state&\"\n\t\t\t\t\t+ \"redirect_uri=https://example.com/authorize/oauth2/code/registration-id&\"\n\t\t\t\t\t+ \"item1=null&item2=value2\");\n\t}\n\n\t@Test\n\tpublic void equalsWhenAllFieldsEqualEqualsThenTrue() {\n\t\tOAuth2AuthorizationRequest authorizationRequest1 = TestOAuth2AuthorizationRequests.allFields().build();\n\n\t\tOAuth2AuthorizationRequest authorizationRequest2 = TestOAuth2AuthorizationRequests.allFields().build();\n\n\t\tassertThat(authorizationRequest1).isNotSameAs(authorizationRequest2);\n\t\tassertThat(authorizationRequest1).isEqualTo(authorizationRequest2);\n\t}\n\n\t@Test\n\tpublic void hashCodeWhenAllFieldsEqualThenHashCodesAreEqual() {\n\t\tOAuth2AuthorizationRequest authorizationRequest1 = TestOAuth2AuthorizationRequests.allFields().build();\n\n\t\tOAuth2AuthorizationRequest authorizationRequest2 = TestOAuth2AuthorizationRequests.allFields().build();\n\n\t\tint authorizationRequest1HashCode = authorizationRequest1.hashCode();\n\t\tint authorizationRequest2HashCode = authorizationRequest2.hashCode();\n\n\t\tassertThat(authorizationRequest1).isNotSameAs(authorizationRequest2);\n\t\tassertThat(authorizationRequest1HashCode).isEqualTo(authorizationRequest2HashCode);\n\t}\n\n\t@Test\n\tpublic void buildWhenExtendedTypeAndAllValuesProvidedThenAllValuesAreSet() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"param1\", \"value1\");\n\t\tadditionalParameters.put(\"param2\", \"value2\");\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(\"attribute1\", \"value1\");\n\t\tattributes.put(\"attribute2\", \"value2\");\n\t\t// @formatter:off\n\t\tTestOidcAuthorizationRequest oidcAuthorizationRequest = TestOidcAuthorizationRequest.builder()\n\t\t\t\t.authorizationUri(AUTHORIZATION_URI)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.scopes(SCOPES)\n\t\t\t\t.state(STATE)\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.attributes(attributes)\n\t\t\t\t.nonce(\"nonce1234\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(oidcAuthorizationRequest.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);\n\t\tassertThat(oidcAuthorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);\n\t\tassertThat(oidcAuthorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE);\n\t\tassertThat(oidcAuthorizationRequest.getClientId()).isEqualTo(CLIENT_ID);\n\t\tassertThat(oidcAuthorizationRequest.getRedirectUri()).isEqualTo(REDIRECT_URI);\n\t\tassertThat(oidcAuthorizationRequest.getScopes()).isEqualTo(SCOPES);\n\t\tassertThat(oidcAuthorizationRequest.getState()).isEqualTo(STATE);\n\t\tassertThat(oidcAuthorizationRequest.getAdditionalParameters()).isEqualTo(additionalParameters);\n\t\tassertThat(oidcAuthorizationRequest.getAttributes()).isEqualTo(attributes);\n\t\tassertThat(oidcAuthorizationRequest.getNonce()).isEqualTo(\"nonce1234\");\n\t\tassertThat(oidcAuthorizationRequest.getAuthorizationRequestUri())\n\t\t\t.isEqualTo(\"https://provider.com/oauth2/authorize?\" + \"response_type=code&client_id=client-id&\"\n\t\t\t\t\t+ \"scope=scope1%20scope2&state=state&\"\n\t\t\t\t\t+ \"redirect_uri=https://example.com&param1=value1&param2=value2&nonce=nonce1234\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationResponseTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2AuthorizationResponse}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationResponseTests {\n\n\tprivate static final String AUTH_CODE = \"auth-code\";\n\n\tprivate static final String REDIRECT_URI = \"https://example.com\";\n\n\tprivate static final String STATE = \"state\";\n\n\tprivate static final String ERROR_CODE = \"error-code\";\n\n\tprivate static final String ERROR_DESCRIPTION = \"error-description\";\n\n\tprivate static final String ERROR_URI = \"error-uri\";\n\n\t@Test\n\tpublic void buildSuccessResponseWhenAuthCodeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationResponse.success(null)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.state(STATE)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildSuccessResponseWhenRedirectUriIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationResponse.success(AUTH_CODE)\n\t\t\t\t\t.redirectUri(null)\n\t\t\t\t\t.state(STATE)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildSuccessResponseWhenStateIsNullThenDoesNotThrowAnyException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationResponse.success(AUTH_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.state(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildSuccessResponseWhenAllAttributesProvidedThenAllAttributesAreSet() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponse.success(AUTH_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.state(STATE)\n\t\t\t\t.build();\n\t\tassertThat(authorizationResponse.getCode())\n\t\t\t\t.isEqualTo(AUTH_CODE);\n\t\tassertThat(authorizationResponse.getRedirectUri())\n\t\t\t\t.isEqualTo(REDIRECT_URI);\n\t\tassertThat(authorizationResponse.getState())\n\t\t\t\t.isEqualTo(STATE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildSuccessResponseWhenErrorCodeIsSetThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationResponse.success(AUTH_CODE)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.state(STATE)\n\t\t\t\t\t.errorCode(ERROR_CODE)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildErrorResponseWhenErrorCodeIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationResponse.error(null)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.state(STATE)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildErrorResponseWhenRedirectUriIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationResponse.error(ERROR_CODE)\n\t\t\t\t\t.redirectUri(null)\n\t\t\t\t\t.state(STATE)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Test\n\tpublic void buildErrorResponseWhenStateIsNullThenDoesNotThrowAnyException() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationResponse.error(ERROR_CODE)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.state(null)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildErrorResponseWhenAllAttributesProvidedThenAllAttributesAreSet() {\n\t\t// @formatter:off\n\t\tOAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponse.error(ERROR_CODE)\n\t\t\t\t.errorDescription(ERROR_DESCRIPTION)\n\t\t\t\t.errorUri(ERROR_URI)\n\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t.state(STATE)\n\t\t\t\t.build();\n\t\tassertThat(authorizationResponse.getError().getErrorCode())\n\t\t\t\t.isEqualTo(ERROR_CODE);\n\t\tassertThat(authorizationResponse.getError().getDescription())\n\t\t\t\t.isEqualTo(ERROR_DESCRIPTION);\n\t\tassertThat(authorizationResponse.getError().getUri())\n\t\t\t\t.isEqualTo(ERROR_URI);\n\t\tassertThat(authorizationResponse.getRedirectUri())\n\t\t\t\t.isEqualTo(REDIRECT_URI);\n\t\tassertThat(authorizationResponse.getState())\n\t\t\t\t.isEqualTo(STATE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildErrorResponseWhenAuthCodeIsSetThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tOAuth2AuthorizationResponse.error(ERROR_CODE)\n\t\t\t\t\t.redirectUri(REDIRECT_URI)\n\t\t\t\t\t.state(STATE)\n\t\t\t\t\t.code(AUTH_CODE)\n\t\t\t\t\t.build()\n\t\t// @formatter:on\n\t\t);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/OAuth2AuthorizationResponseTypeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OAuth2AuthorizationResponseType}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AuthorizationResponseTypeTests {\n\n\t@Test\n\tpublic void getValueWhenResponseTypeCodeThenReturnCode() {\n\t\tassertThat(OAuth2AuthorizationResponseType.CODE.getValue()).isEqualTo(\"code\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AccessTokenResponses.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.oidc.OidcScopes;\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic final class TestOAuth2AccessTokenResponses {\n\n\tprivate TestOAuth2AccessTokenResponses() {\n\t}\n\n\tpublic static OAuth2AccessTokenResponse.Builder accessTokenResponse() {\n\t\t// @formatter:off\n\t\treturn OAuth2AccessTokenResponse\n\t\t\t\t.withToken(\"token\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER);\n\t\t// @formatter:on\n\t}\n\n\tpublic static OAuth2AccessTokenResponse.Builder oidcAccessTokenResponse() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(OidcParameterNames.ID_TOKEN, \"id-token\");\n\t\treturn accessTokenResponse().scopes(Collections.singleton(OidcScopes.OPENID))\n\t\t\t.additionalParameters(additionalParameters);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AuthorizationExchanges.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\n/**\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 5.1\n */\npublic final class TestOAuth2AuthorizationExchanges {\n\n\tprivate TestOAuth2AuthorizationExchanges() {\n\t}\n\n\tpublic static OAuth2AuthorizationExchange success() {\n\t\tOAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().build();\n\t\tOAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build();\n\t\treturn new OAuth2AuthorizationExchange(request, response);\n\t}\n\n\tpublic static OAuth2AuthorizationExchange failure() {\n\t\tOAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().build();\n\t\tOAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.error().build();\n\t\treturn new OAuth2AuthorizationExchange(request, response);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AuthorizationRequests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic final class TestOAuth2AuthorizationRequests {\n\n\tprivate TestOAuth2AuthorizationRequests() {\n\t}\n\n\tpublic static OAuth2AuthorizationRequest.Builder request() {\n\t\tString registrationId = \"registration-id\";\n\t\tString clientId = \"client-id\";\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2ParameterNames.REGISTRATION_ID, registrationId);\n\t\t// @formatter:off\n\t\treturn OAuth2AuthorizationRequest.authorizationCode()\n\t\t\t\t.authorizationUri(\"https://example.com/login/oauth/authorize\")\n\t\t\t\t.clientId(clientId)\n\t\t\t\t.redirectUri(\"https://example.com/authorize/oauth2/code/registration-id\")\n\t\t\t\t.state(\"state\")\n\t\t\t\t.attributes(attributes);\n\t\t// @formatter:on\n\t}\n\n\tpublic static OAuth2AuthorizationRequest.Builder oidcRequest() {\n\t\treturn request().scope(\"openid\");\n\t}\n\n\tpublic static OAuth2AuthorizationRequest.Builder allFields() {\n\t\t// @formatter:off\n\t\treturn request()\n\t\t\t\t.authorizationRequestUri(\"https://example.com\")\n\t\t\t\t.additionalParameters(Map.of(\"someAdditionalParameterKey\", \"someAdditionalParameterValue\"))\n\t\t\t\t.parameters((parametersMap) ->\n\t\t\t\t\t\tparametersMap.put(\"someParameterKey\", \"someParameterValue\"))\n\t\t\t\t.scope(\"someScope\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOAuth2AuthorizationResponses.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic final class TestOAuth2AuthorizationResponses {\n\n\tprivate TestOAuth2AuthorizationResponses() {\n\t}\n\n\tpublic static OAuth2AuthorizationResponse.Builder success() {\n\t\t// @formatter:off\n\t\treturn OAuth2AuthorizationResponse.success(\"authorization-code\")\n\t\t\t\t.state(\"state\")\n\t\t\t\t.redirectUri(\"https://example.com/authorize/oauth2/code/registration-id\");\n\t\t// @formatter:on\n\t}\n\n\tpublic static OAuth2AuthorizationResponse.Builder error() {\n\t\t// @formatter:off\n\t\treturn OAuth2AuthorizationResponse.error(\"error\")\n\t\t\t\t.redirectUri(\"https://example.com/authorize/oauth2/code/registration-id\")\n\t\t\t\t.errorUri(\"https://example.com/error\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/TestOidcAuthorizationRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.endpoint;\n\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;\n\n/**\n * @author Joe Grandja\n */\npublic class TestOidcAuthorizationRequest extends OAuth2AuthorizationRequest {\n\n\tprivate final String nonce;\n\n\tprotected TestOidcAuthorizationRequest(Builder builder) {\n\t\tsuper(builder);\n\t\tthis.nonce = builder.nonce;\n\t}\n\n\tpublic String getNonce() {\n\t\treturn this.nonce;\n\t}\n\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\tpublic static class Builder extends AbstractBuilder<TestOidcAuthorizationRequest, Builder> {\n\n\t\tprivate String nonce;\n\n\t\tpublic Builder nonce(String nonce) {\n\t\t\tthis.nonce = nonce;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic TestOidcAuthorizationRequest build() {\n\t\t\treturn new TestOidcAuthorizationRequest(this);\n\t\t}\n\n\t\t@Override\n\t\tprotected Map<String, Object> getParameters() {\n\t\t\tMap<String, Object> parameters = super.getParameters();\n\t\t\tif (this.nonce != null) {\n\t\t\t\tparameters.put(OidcParameterNames.NONCE, this.nonce);\n\t\t\t}\n\t\t\treturn parameters;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2AccessTokenResponseHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OAuth2AccessTokenResponseHttpMessageConverter}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2AccessTokenResponseHttpMessageConverterTests {\n\n\tprivate OAuth2AccessTokenResponseHttpMessageConverter messageConverter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.messageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();\n\t}\n\n\t@Test\n\tpublic void supportsWhenOAuth2AccessTokenResponseThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OAuth2AccessTokenResponse.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseConverterWhenConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setAccessTokenResponseConverter(null));\n\t}\n\n\t@Test\n\tpublic void setAccessTokenResponseParametersConverterWhenConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setAccessTokenResponseParametersConverter(null));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenSuccessfulTokenResponseThenReadOAuth2AccessTokenResponse() throws Exception {\n\t\t// @formatter:off\n\t\tString tokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": \\\"3600\\\",\\n\"\n\t\t\t+ \"   \\\"scope\\\": \\\"read write\\\",\\n\"\n\t\t\t+ \"   \\\"refresh_token\\\": \\\"refresh-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"custom_parameter_1\\\": \\\"custom-value-1\\\",\\n\"\n\t\t\t+ \"   \\\"custom_parameter_2\\\": \\\"custom-value-2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(tokenResponse.getBytes(), HttpStatus.OK);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.messageConverter\n\t\t\t.readInternal(OAuth2AccessTokenResponse.class, response);\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt())\n\t\t\t.isBeforeOrEqualTo(Instant.now().plusSeconds(3600));\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(\"refresh-token-1234\");\n\t\tassertThat(accessTokenResponse.getAdditionalParameters()).containsExactly(\n\t\t\t\tentry(\"custom_parameter_1\", \"custom-value-1\"), entry(\"custom_parameter_2\", \"custom-value-2\"));\n\t}\n\n\t// gh-6463\n\t@Test\n\tpublic void readInternalWhenSuccessfulTokenResponseWithObjectThenReadOAuth2AccessTokenResponse() {\n\t\t// @formatter:off\n\t\tString tokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": 3600,\\n\"\n\t\t\t+ \"   \\\"scope\\\": \\\"read write\\\",\\n\"\n\t\t\t+ \"   \\\"refresh_token\\\": \\\"refresh-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"custom_object_1\\\": {\\\"name1\\\": \\\"value1\\\"},\\n\"\n\t\t\t+ \"   \\\"custom_object_2\\\": [\\\"value1\\\", \\\"value2\\\"],\\n\"\n\t\t\t+ \"   \\\"custom_parameter_1\\\": \\\"custom-value-1\\\",\\n\"\n\t\t\t+ \"   \\\"custom_parameter_2\\\": \\\"custom-value-2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(tokenResponse.getBytes(), HttpStatus.OK);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.messageConverter\n\t\t\t.readInternal(OAuth2AccessTokenResponse.class, response);\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt())\n\t\t\t.isBeforeOrEqualTo(Instant.now().plusSeconds(3600));\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly(\"read\", \"write\");\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(\"refresh-token-1234\");\n\t\tMap<String, String> additionalParameters = accessTokenResponse.getAdditionalParameters()\n\t\t\t.entrySet()\n\t\t\t.stream()\n\t\t\t.collect(Collectors.toMap(Map.Entry::getKey, (entry) -> String.valueOf(entry.getValue())));\n\t\tassertThat(additionalParameters).containsExactly(entry(\"custom_object_1\", \"{name1=value1}\"),\n\t\t\t\tentry(\"custom_object_2\", \"[value1, value2]\"), entry(\"custom_parameter_1\", \"custom-value-1\"),\n\t\t\t\tentry(\"custom_parameter_2\", \"custom-value-2\"));\n\t}\n\n\t// gh-8108\n\t@Test\n\tpublic void readInternalWhenSuccessfulTokenResponseWithNullValueThenReadOAuth2AccessTokenResponse() {\n\t\t// @formatter:off\n\t\tString tokenResponse = \"{\\n\"\n\t\t\t+ \"   \\\"access_token\\\": \\\"access-token-1234\\\",\\n\"\n\t\t\t+ \"   \\\"token_type\\\": \\\"bearer\\\",\\n\"\n\t\t\t+ \"   \\\"expires_in\\\": 3600,\\n\"\n\t\t\t+ \"   \\\"scope\\\": null,\\n\"\n\t\t\t+ \"   \\\"refresh_token\\\": \\\"refresh-token-1234\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(tokenResponse.getBytes(), HttpStatus.OK);\n\t\tOAuth2AccessTokenResponse accessTokenResponse = this.messageConverter\n\t\t\t.readInternal(OAuth2AccessTokenResponse.class, response);\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo(\"access-token-1234\");\n\t\tassertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(accessTokenResponse.getAccessToken().getExpiresAt())\n\t\t\t.isBeforeOrEqualTo(Instant.now().plusSeconds(3600));\n\t\tassertThat(accessTokenResponse.getAccessToken().getScopes()).isEmpty();\n\t\tassertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(\"refresh-token-1234\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenConversionFailsThenThrowHttpMessageNotReadableException() {\n\t\tConverter tokenResponseConverter = mock(Converter.class);\n\t\tgiven(tokenResponseConverter.convert(any())).willThrow(RuntimeException.class);\n\t\tthis.messageConverter.setAccessTokenResponseConverter(tokenResponseConverter);\n\t\tString tokenResponse = \"{}\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(tokenResponse.getBytes(), HttpStatus.OK);\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OAuth2AccessTokenResponse.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OAuth 2.0 Access Token Response\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenOAuth2AccessTokenResponseThenWriteTokenResponse() throws Exception {\n\t\tInstant expiresAt = Instant.now().plusSeconds(3600);\n\t\tSet<String> scopes = new LinkedHashSet<>(Arrays.asList(\"read\", \"write\"));\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"custom_parameter_1\", \"custom-value-1\");\n\t\tadditionalParameters.put(\"custom_parameter_2\", \"custom-value-2\");\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(\"access-token-1234\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(expiresAt.toEpochMilli())\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.refreshToken(\"refresh-token-1234\")\n\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tthis.messageConverter.writeInternal(accessTokenResponse, outputMessage);\n\t\tString tokenResponse = outputMessage.getBodyAsString();\n\t\tassertThat(tokenResponse).contains(\"\\\"access_token\\\":\\\"access-token-1234\\\"\");\n\t\tassertThat(tokenResponse).contains(\"\\\"token_type\\\":\\\"Bearer\\\"\");\n\t\tassertThat(tokenResponse).contains(\"\\\"expires_in\\\"\");\n\t\tassertThat(tokenResponse).contains(\"\\\"scope\\\":\\\"read write\\\"\");\n\t\tassertThat(tokenResponse).contains(\"\\\"refresh_token\\\":\\\"refresh-token-1234\\\"\");\n\t\tassertThat(tokenResponse).contains(\"\\\"custom_parameter_1\\\":\\\"custom-value-1\\\"\");\n\t\tassertThat(tokenResponse).contains(\"\\\"custom_parameter_2\\\":\\\"custom-value-2\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenConversionFailsThenThrowHttpMessageNotWritableException() {\n\t\tConverter tokenResponseParametersConverter = mock(Converter.class);\n\t\tgiven(tokenResponseParametersConverter.convert(any())).willThrow(RuntimeException.class);\n\t\tthis.messageConverter.setAccessTokenResponseParametersConverter(tokenResponseParametersConverter);\n\t\t// @formatter:off\n\t\tOAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse\n\t\t\t\t.withToken(\"access-token-1234\")\n\t\t\t\t.tokenType(OAuth2AccessToken.TokenType.BEARER)\n\t\t\t\t.expiresIn(Instant.now().plusSeconds(3600)\n\t\t\t\t.toEpochMilli())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.writeInternal(accessTokenResponse, outputMessage))\n\t\t\t.withMessageContaining(\"An error occurred writing the OAuth 2.0 Access Token Response\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2DeviceAuthorizationResponseHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2DeviceAuthorizationResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OAuth2DeviceAuthorizationResponseHttpMessageConverter}.\n *\n * @author Steve Riesenberg\n */\npublic class OAuth2DeviceAuthorizationResponseHttpMessageConverterTests {\n\n\tprivate OAuth2DeviceAuthorizationResponseHttpMessageConverter messageConverter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.messageConverter = new OAuth2DeviceAuthorizationResponseHttpMessageConverter();\n\t}\n\n\t@Test\n\tpublic void supportsWhenOAuth2DeviceAuthorizationResponseThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OAuth2DeviceAuthorizationResponse.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setDeviceAuthorizationResponseConverterWhenConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setDeviceAuthorizationResponseConverter(null));\n\t}\n\n\t@Test\n\tpublic void setDeviceAuthorizationResponseParametersConverterWhenConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.messageConverter.setDeviceAuthorizationResponseParametersConverter(null));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenSuccessfulResponseWithAllParametersThenReadOAuth2DeviceAuthorizationResponse() {\n\t\t// @formatter:off\n\t\tString authorizationResponse = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"device_code\": \"GmRhm_DnyEy\",\n\t\t\t\t\t\"user_code\": \"WDJB-MJHT\",\n\t\t\t\t\t\"verification_uri\": \"https://example.com/device\",\n\t\t\t\t\t\"verification_uri_complete\": \"https://example.com/device?user_code=WDJB-MJHT\",\n\t\t\t\t\t\"expires_in\": 1800,\n\t\t\t\t\t\"interval\": 5,\n\t\t\t\t\t\"custom_parameter_1\": \"custom-value-1\",\n\t\t\t\t\t\"custom_parameter_2\": \"custom-value-2\"\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(authorizationResponse.getBytes(), HttpStatus.OK);\n\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse = this.messageConverter\n\t\t\t.readInternal(OAuth2DeviceAuthorizationResponse.class, response);\n\t\tassertThat(deviceAuthorizationResponse.getDeviceCode().getTokenValue()).isEqualTo(\"GmRhm_DnyEy\");\n\t\tassertThat(deviceAuthorizationResponse.getDeviceCode().getIssuedAt()).isNotNull();\n\t\tassertThat(deviceAuthorizationResponse.getDeviceCode().getExpiresAt())\n\t\t\t.isBeforeOrEqualTo(Instant.now().plusSeconds(1800));\n\t\tassertThat(deviceAuthorizationResponse.getUserCode().getTokenValue()).isEqualTo(\"WDJB-MJHT\");\n\t\tassertThat(deviceAuthorizationResponse.getUserCode().getIssuedAt())\n\t\t\t.isEqualTo(deviceAuthorizationResponse.getDeviceCode().getIssuedAt());\n\t\tassertThat(deviceAuthorizationResponse.getUserCode().getExpiresAt())\n\t\t\t.isEqualTo(deviceAuthorizationResponse.getDeviceCode().getExpiresAt());\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUri()).isEqualTo(\"https://example.com/device\");\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUriComplete())\n\t\t\t.isEqualTo(\"https://example.com/device?user_code=WDJB-MJHT\");\n\t\tassertThat(deviceAuthorizationResponse.getInterval()).isEqualTo(5);\n\t\tassertThat(deviceAuthorizationResponse.getAdditionalParameters()).containsExactly(\n\t\t\t\tentry(\"custom_parameter_1\", \"custom-value-1\"), entry(\"custom_parameter_2\", \"custom-value-2\"));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenSuccessfulResponseWithNullValuesThenReadOAuth2DeviceAuthorizationResponse() {\n\t\t// @formatter:off\n\t\tString authorizationResponse = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"device_code\": \"GmRhm_DnyEy\",\n\t\t\t\t\t\"user_code\": \"WDJB-MJHT\",\n\t\t\t\t\t\"verification_uri\": \"https://example.com/device\",\n\t\t\t\t\t\"verification_uri_complete\": null,\n\t\t\t\t\t\"expires_in\": 1800,\n\t\t\t\t\t\"interval\": null\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(authorizationResponse.getBytes(), HttpStatus.OK);\n\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse = this.messageConverter\n\t\t\t.readInternal(OAuth2DeviceAuthorizationResponse.class, response);\n\t\tassertThat(deviceAuthorizationResponse.getDeviceCode().getTokenValue()).isEqualTo(\"GmRhm_DnyEy\");\n\t\tassertThat(deviceAuthorizationResponse.getDeviceCode().getIssuedAt()).isNotNull();\n\t\tassertThat(deviceAuthorizationResponse.getDeviceCode().getExpiresAt())\n\t\t\t.isBeforeOrEqualTo(Instant.now().plusSeconds(1800));\n\t\tassertThat(deviceAuthorizationResponse.getUserCode().getTokenValue()).isEqualTo(\"WDJB-MJHT\");\n\t\tassertThat(deviceAuthorizationResponse.getUserCode().getIssuedAt())\n\t\t\t.isEqualTo(deviceAuthorizationResponse.getDeviceCode().getIssuedAt());\n\t\tassertThat(deviceAuthorizationResponse.getUserCode().getExpiresAt())\n\t\t\t.isEqualTo(deviceAuthorizationResponse.getDeviceCode().getExpiresAt());\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUri()).isEqualTo(\"https://example.com/device\");\n\t\tassertThat(deviceAuthorizationResponse.getVerificationUriComplete()).isNull();\n\t\tassertThat(deviceAuthorizationResponse.getInterval()).isEqualTo(0);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void readInternalWhenConversionFailsThenThrowHttpMessageNotReadableException() {\n\t\tConverter<Map<String, Object>, OAuth2DeviceAuthorizationResponse> deviceAuthorizationResponseConverter = mock(\n\t\t\t\tConverter.class);\n\t\tgiven(deviceAuthorizationResponseConverter.convert(any())).willThrow(RuntimeException.class);\n\t\tthis.messageConverter.setDeviceAuthorizationResponseConverter(deviceAuthorizationResponseConverter);\n\t\tString authorizationResponse = \"{}\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(authorizationResponse.getBytes(), HttpStatus.OK);\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OAuth2DeviceAuthorizationResponse.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OAuth 2.0 Device Authorization Response\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenOAuth2DeviceAuthorizationResponseThenWriteResponse() {\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tadditionalParameters.put(\"custom_parameter_1\", \"custom-value-1\");\n\t\tadditionalParameters.put(\"custom_parameter_2\", \"custom-value-2\");\n\t\t// @formatter:off\n\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse =\n\t\t\t\tOAuth2DeviceAuthorizationResponse.with(\"GmRhm_DnyEy\", \"WDJB-MJHT\")\n\t\t\t\t\t\t.verificationUri(\"https://example.com/device\")\n\t\t\t\t\t\t.verificationUriComplete(\"https://example.com/device?user_code=WDJB-MJHT\")\n\t\t\t\t\t\t.expiresIn(1800)\n\t\t\t\t\t\t.interval(5)\n\t\t\t\t\t\t.additionalParameters(additionalParameters)\n\t\t\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tthis.messageConverter.writeInternal(deviceAuthorizationResponse, outputMessage);\n\t\tString authorizationResponse = outputMessage.getBodyAsString();\n\t\tassertThat(authorizationResponse).contains(\"\\\"device_code\\\":\\\"GmRhm_DnyEy\\\"\");\n\t\tassertThat(authorizationResponse).contains(\"\\\"user_code\\\":\\\"WDJB-MJHT\\\"\");\n\t\tassertThat(authorizationResponse).contains(\"\\\"verification_uri\\\":\\\"https://example.com/device\\\"\");\n\t\tassertThat(authorizationResponse)\n\t\t\t.contains(\"\\\"verification_uri_complete\\\":\\\"https://example.com/device?user_code=WDJB-MJHT\\\"\");\n\t\tassertThat(authorizationResponse).contains(\"\\\"expires_in\\\":1800\");\n\t\tassertThat(authorizationResponse).contains(\"\\\"interval\\\":5\");\n\t\tassertThat(authorizationResponse).contains(\"\\\"custom_parameter_1\\\":\\\"custom-value-1\\\"\");\n\t\tassertThat(authorizationResponse).contains(\"\\\"custom_parameter_2\\\":\\\"custom-value-2\\\"\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void writeInternalWhenConversionFailsThenThrowHttpMessageNotWritableException() {\n\t\tConverter<OAuth2DeviceAuthorizationResponse, Map<String, Object>> deviceAuthorizationResponseParametersConverter = mock(\n\t\t\t\tConverter.class);\n\t\tgiven(deviceAuthorizationResponseParametersConverter.convert(any())).willThrow(RuntimeException.class);\n\t\tthis.messageConverter\n\t\t\t.setDeviceAuthorizationResponseParametersConverter(deviceAuthorizationResponseParametersConverter);\n\t\t// @formatter:off\n\t\tOAuth2DeviceAuthorizationResponse deviceAuthorizationResponse =\n\t\t\t\tOAuth2DeviceAuthorizationResponse.with(\"GmRhm_DnyEy\", \"WDJB-MJHT\")\n\t\t\t\t\t\t.verificationUri(\"https://example.com/device\")\n\t\t\t\t\t\t.expiresIn(1800)\n\t\t\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.writeInternal(deviceAuthorizationResponse, outputMessage))\n\t\t\t.withMessageContaining(\"An error occurred writing the OAuth 2.0 Device Authorization Response\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/http/converter/OAuth2ErrorHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.http.converter;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.mock.http.MockHttpOutputMessage;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.OAuth2Error;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OAuth2ErrorHttpMessageConverter}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2ErrorHttpMessageConverterTests {\n\n\tprivate OAuth2ErrorHttpMessageConverter messageConverter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.messageConverter = new OAuth2ErrorHttpMessageConverter();\n\t}\n\n\t@Test\n\tpublic void supportsWhenOAuth2ErrorThenTrue() {\n\t\tassertThat(this.messageConverter.supports(OAuth2Error.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setErrorConverterWhenConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setErrorConverter(null));\n\t}\n\n\t@Test\n\tpublic void setErrorParametersConverterWhenConverterIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setErrorParametersConverter(null));\n\t}\n\n\t@Test\n\tpublic void readInternalWhenErrorResponseThenReadOAuth2Error() throws Exception {\n\t\t// @formatter:off\n\t\tString errorResponse = \"{\\n\"\n\t\t\t+ \"   \\\"error\\\": \\\"unauthorized_client\\\",\\n\"\n\t\t\t+ \"   \\\"error_description\\\": \\\"The client is not authorized\\\",\\n\"\n\t\t\t+ \"   \\\"error_uri\\\": \\\"https://tools.ietf.org/html/rfc6749#section-5.2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(errorResponse.getBytes(), HttpStatus.BAD_REQUEST);\n\t\tOAuth2Error oauth2Error = this.messageConverter.readInternal(OAuth2Error.class, response);\n\t\tassertThat(oauth2Error.getErrorCode()).isEqualTo(\"unauthorized_client\");\n\t\tassertThat(oauth2Error.getDescription()).isEqualTo(\"The client is not authorized\");\n\t\tassertThat(oauth2Error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6749#section-5.2\");\n\t}\n\n\t// gh-8157\n\t@Test\n\tpublic void readInternalWhenErrorResponseWithObjectThenReadOAuth2Error() throws Exception {\n\t\t// @formatter:off\n\t\tString errorResponse = \"{\\n\"\n\t\t\t+ \"   \\\"error\\\": \\\"unauthorized_client\\\",\\n\"\n\t\t\t+ \"   \\\"error_description\\\": \\\"The client is not authorized\\\",\\n\"\n\t\t\t+ \"   \\\"error_codes\\\": [65001],\\n\"\n\t\t\t+ \"   \\\"error_uri\\\": \\\"https://tools.ietf.org/html/rfc6749#section-5.2\\\"\\n\"\n\t\t\t+ \"}\\n\";\n\t\t// @formatter:on\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(errorResponse.getBytes(), HttpStatus.BAD_REQUEST);\n\t\tOAuth2Error oauth2Error = this.messageConverter.readInternal(OAuth2Error.class, response);\n\t\tassertThat(oauth2Error.getErrorCode()).isEqualTo(\"unauthorized_client\");\n\t\tassertThat(oauth2Error.getDescription()).isEqualTo(\"The client is not authorized\");\n\t\tassertThat(oauth2Error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6749#section-5.2\");\n\t}\n\n\t@Test\n\tpublic void readInternalWhenConversionFailsThenThrowHttpMessageNotReadableException() {\n\t\tConverter errorConverter = mock(Converter.class);\n\t\tgiven(errorConverter.convert(any())).willThrow(RuntimeException.class);\n\t\tthis.messageConverter.setErrorConverter(errorConverter);\n\t\tString errorResponse = \"{}\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(errorResponse.getBytes(), HttpStatus.BAD_REQUEST);\n\t\tassertThatExceptionOfType(HttpMessageNotReadableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.readInternal(OAuth2Error.class, response))\n\t\t\t.withMessageContaining(\"An error occurred reading the OAuth 2.0 Error\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenOAuth2ErrorThenWriteErrorResponse() throws Exception {\n\t\tOAuth2Error oauth2Error = new OAuth2Error(\"unauthorized_client\", \"The client is not authorized\",\n\t\t\t\t\"https://tools.ietf.org/html/rfc6749#section-5.2\");\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tthis.messageConverter.writeInternal(oauth2Error, outputMessage);\n\t\tString errorResponse = outputMessage.getBodyAsString();\n\t\tassertThat(errorResponse).contains(\"\\\"error\\\":\\\"unauthorized_client\\\"\");\n\t\tassertThat(errorResponse).contains(\"\\\"error_description\\\":\\\"The client is not authorized\\\"\");\n\t\tassertThat(errorResponse).contains(\"\\\"error_uri\\\":\\\"https://tools.ietf.org/html/rfc6749#section-5.2\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeInternalWhenConversionFailsThenThrowHttpMessageNotWritableException() {\n\t\tConverter errorParametersConverter = mock(Converter.class);\n\t\tgiven(errorParametersConverter.convert(any())).willThrow(RuntimeException.class);\n\t\tthis.messageConverter.setErrorParametersConverter(errorParametersConverter);\n\t\tOAuth2Error oauth2Error = new OAuth2Error(\"unauthorized_client\", \"The client is not authorized\",\n\t\t\t\t\"https://tools.ietf.org/html/rfc6749#section-5.2\");\n\t\tMockHttpOutputMessage outputMessage = new MockHttpOutputMessage();\n\t\tassertThatExceptionOfType(HttpMessageNotWritableException.class)\n\t\t\t.isThrownBy(() -> this.messageConverter.writeInternal(oauth2Error, outputMessage))\n\t\t\t.withMessageContaining(\"An error occurred writing the OAuth 2.0 Error\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/DefaultAddressStandardClaimTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link DefaultAddressStandardClaim}.\n *\n * @author Joe Grandja\n */\npublic class DefaultAddressStandardClaimTests {\n\n\tstatic final String FORMATTED_FIELD_NAME = \"formatted\";\n\tstatic final String STREET_ADDRESS_FIELD_NAME = \"street_address\";\n\tstatic final String LOCALITY_FIELD_NAME = \"locality\";\n\tstatic final String REGION_FIELD_NAME = \"region\";\n\tstatic final String POSTAL_CODE_FIELD_NAME = \"postal_code\";\n\tstatic final String COUNTRY_FIELD_NAME = \"country\";\n\tstatic final String FORMATTED = \"formatted\";\n\tstatic final String STREET_ADDRESS = \"street_address\";\n\tstatic final String LOCALITY = \"locality\";\n\tstatic final String REGION = \"region\";\n\tstatic final String POSTAL_CODE = \"postal_code\";\n\tstatic final String COUNTRY = \"country\";\n\n\t@Test\n\tpublic void buildWhenAllAttributesProvidedThenAllAttributesAreSet() {\n\t\t// @formatter:off\n\t\tAddressStandardClaim addressStandardClaim = new DefaultAddressStandardClaim.Builder()\n\t\t\t\t.formatted(FORMATTED)\n\t\t\t\t.streetAddress(STREET_ADDRESS)\n\t\t\t\t.locality(LOCALITY)\n\t\t\t\t.region(REGION)\n\t\t\t\t.postalCode(POSTAL_CODE)\n\t\t\t\t.country(COUNTRY)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(addressStandardClaim.getFormatted()).isEqualTo(FORMATTED);\n\t\tassertThat(addressStandardClaim.getStreetAddress()).isEqualTo(STREET_ADDRESS);\n\t\tassertThat(addressStandardClaim.getLocality()).isEqualTo(LOCALITY);\n\t\tassertThat(addressStandardClaim.getRegion()).isEqualTo(REGION);\n\t\tassertThat(addressStandardClaim.getPostalCode()).isEqualTo(POSTAL_CODE);\n\t\tassertThat(addressStandardClaim.getCountry()).isEqualTo(COUNTRY);\n\t}\n\n\t@Test\n\tpublic void buildWhenAllAttributesProvidedToConstructorThenAllAttributesAreSet() {\n\t\tMap<String, Object> addressFields = new HashMap<>();\n\t\taddressFields.put(FORMATTED_FIELD_NAME, FORMATTED);\n\t\taddressFields.put(STREET_ADDRESS_FIELD_NAME, STREET_ADDRESS);\n\t\taddressFields.put(LOCALITY_FIELD_NAME, LOCALITY);\n\t\taddressFields.put(REGION_FIELD_NAME, REGION);\n\t\taddressFields.put(POSTAL_CODE_FIELD_NAME, POSTAL_CODE);\n\t\taddressFields.put(COUNTRY_FIELD_NAME, COUNTRY);\n\t\tAddressStandardClaim addressStandardClaim = new DefaultAddressStandardClaim.Builder(addressFields).build();\n\t\tassertThat(addressStandardClaim.getFormatted()).isEqualTo(FORMATTED);\n\t\tassertThat(addressStandardClaim.getStreetAddress()).isEqualTo(STREET_ADDRESS);\n\t\tassertThat(addressStandardClaim.getLocality()).isEqualTo(LOCALITY);\n\t\tassertThat(addressStandardClaim.getRegion()).isEqualTo(REGION);\n\t\tassertThat(addressStandardClaim.getPostalCode()).isEqualTo(POSTAL_CODE);\n\t\tassertThat(addressStandardClaim.getCountry()).isEqualTo(COUNTRY);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcIdTokenBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcUserInfo}\n */\npublic class OidcIdTokenBuilderTests {\n\n\t@Test\n\tpublic void buildWhenCalledTwiceThenGeneratesTwoOidcIdTokens() {\n\t\tOidcIdToken.Builder idTokenBuilder = OidcIdToken.withTokenValue(\"token\");\n\t\t// @formatter:off\n\t\tOidcIdToken first = idTokenBuilder.tokenValue(\"V1\")\n\t\t\t\t.claim(\"TEST_CLAIM_1\", \"C1\")\n\t\t\t\t.build();\n\t\tOidcIdToken second = idTokenBuilder.tokenValue(\"V2\")\n\t\t\t\t.claim(\"TEST_CLAIM_1\", \"C2\")\n\t\t\t\t.claim(\"TEST_CLAIM_2\", \"C3\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(first.getClaims()).hasSize(1);\n\t\tassertThat(first.getClaims()).containsEntry(\"TEST_CLAIM_1\", \"C1\");\n\t\tassertThat(first.getTokenValue()).isEqualTo(\"V1\");\n\t\tassertThat(second.getClaims()).hasSize(2);\n\t\tassertThat(second.getClaims()).containsEntry(\"TEST_CLAIM_1\", \"C2\");\n\t\tassertThat(second.getClaims()).containsEntry(\"TEST_CLAIM_2\", \"C3\");\n\t\tassertThat(second.getTokenValue()).isEqualTo(\"V2\");\n\t}\n\n\t@Test\n\tpublic void expiresAtWhenUsingGenericOrNamedClaimMethodRequiresInstant() {\n\t\tOidcIdToken.Builder idTokenBuilder = OidcIdToken.withTokenValue(\"token\");\n\t\tInstant now = Instant.now();\n\t\tOidcIdToken idToken = idTokenBuilder.expiresAt(now).build();\n\t\tassertThat(idToken.getExpiresAt()).isSameAs(now);\n\t\tidToken = idTokenBuilder.expiresAt(now).build();\n\t\tassertThat(idToken.getExpiresAt()).isSameAs(now);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> idTokenBuilder.claim(IdTokenClaimNames.EXP, \"not an instant\").build());\n\t}\n\n\t@Test\n\tpublic void issuedAtWhenUsingGenericOrNamedClaimMethodRequiresInstant() {\n\t\tOidcIdToken.Builder idTokenBuilder = OidcIdToken.withTokenValue(\"token\");\n\t\tInstant now = Instant.now();\n\t\tOidcIdToken idToken = idTokenBuilder.issuedAt(now).build();\n\t\tassertThat(idToken.getIssuedAt()).isSameAs(now);\n\t\tidToken = idTokenBuilder.issuedAt(now).build();\n\t\tassertThat(idToken.getIssuedAt()).isSameAs(now);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> idTokenBuilder.claim(IdTokenClaimNames.IAT, \"not an instant\").build());\n\t}\n\n\t@Test\n\tpublic void subjectWhenUsingGenericOrNamedClaimMethodThenLastOneWins() {\n\t\tOidcIdToken.Builder idTokenBuilder = OidcIdToken.withTokenValue(\"token\");\n\t\tString generic = new String(\"sub\");\n\t\tString named = new String(\"sub\");\n\t\t// @formatter:off\n\t\tOidcIdToken idToken = idTokenBuilder\n\t\t\t\t.subject(named)\n\t\t\t\t.claim(IdTokenClaimNames.SUB, generic)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(idToken.getSubject()).isSameAs(generic);\n\t\tidToken = idTokenBuilder.claim(IdTokenClaimNames.SUB, generic).subject(named).build();\n\t\tassertThat(idToken.getSubject()).isSameAs(named);\n\t}\n\n\t@Test\n\tpublic void claimsWhenRemovingAClaimThenIsNotPresent() {\n\t\t// @formatter:off\n\t\tOidcIdToken.Builder idTokenBuilder = OidcIdToken.withTokenValue(\"token\")\n\t\t\t\t.claim(\"needs\", \"a claim\");\n\t\tOidcIdToken idToken = idTokenBuilder.subject(\"sub\")\n\t\t\t\t.claims((claims) -> claims.remove(IdTokenClaimNames.SUB))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(idToken.getSubject()).isNull();\n\t}\n\n\t@Test\n\tpublic void claimsWhenAddingAClaimThenIsPresent() {\n\t\tOidcIdToken.Builder idTokenBuilder = OidcIdToken.withTokenValue(\"token\");\n\t\tString name = new String(\"name\");\n\t\tString value = new String(\"value\");\n\t\t// @formatter:off\n\t\tOidcIdToken idToken = idTokenBuilder\n\t\t\t\t.claims((claims) -> claims.put(name, value))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(idToken.getClaims()).hasSize(1);\n\t\tassertThat(idToken.getClaims().get(name)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcIdTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcIdToken}.\n *\n * @author Joe Grandja\n */\npublic class OidcIdTokenTests {\n\n\tprivate static final String ISS_CLAIM = \"iss\";\n\n\tprivate static final String SUB_CLAIM = \"sub\";\n\n\tprivate static final String AUD_CLAIM = \"aud\";\n\n\tprivate static final String IAT_CLAIM = \"iat\";\n\n\tprivate static final String EXP_CLAIM = \"exp\";\n\n\tprivate static final String AUTH_TIME_CLAIM = \"auth_time\";\n\n\tprivate static final String NONCE_CLAIM = \"nonce\";\n\n\tprivate static final String ACR_CLAIM = \"acr\";\n\n\tprivate static final String AMR_CLAIM = \"amr\";\n\n\tprivate static final String AZP_CLAIM = \"azp\";\n\n\tprivate static final String AT_HASH_CLAIM = \"at_hash\";\n\n\tprivate static final String C_HASH_CLAIM = \"c_hash\";\n\n\tprivate static final String ISS_VALUE = \"https://provider.com\";\n\n\tprivate static final String SUB_VALUE = \"subject1\";\n\n\tprivate static final List<String> AUD_VALUE = Arrays.asList(\"aud1\", \"aud2\");\n\n\tprivate static final long IAT_VALUE = Instant.now().toEpochMilli();\n\n\tprivate static final long EXP_VALUE = Instant.now().plusSeconds(60).toEpochMilli();\n\n\tprivate static final long AUTH_TIME_VALUE = Instant.now().minusSeconds(5).toEpochMilli();\n\n\tprivate static final String NONCE_VALUE = \"nonce\";\n\n\tprivate static final String ACR_VALUE = \"acr\";\n\n\tprivate static final List<String> AMR_VALUE = Arrays.asList(\"amr1\", \"amr2\");\n\n\tprivate static final String AZP_VALUE = \"azp\";\n\n\tprivate static final String AT_HASH_VALUE = \"at_hash\";\n\n\tprivate static final String C_HASH_VALUE = \"c_hash\";\n\n\tprivate static final Map<String, Object> CLAIMS;\n\n\tprivate static final String ID_TOKEN_VALUE = \"id-token-value\";\n\tstatic {\n\t\tCLAIMS = new HashMap<>();\n\t\tCLAIMS.put(ISS_CLAIM, ISS_VALUE);\n\t\tCLAIMS.put(SUB_CLAIM, SUB_VALUE);\n\t\tCLAIMS.put(AUD_CLAIM, AUD_VALUE);\n\t\tCLAIMS.put(IAT_CLAIM, IAT_VALUE);\n\t\tCLAIMS.put(EXP_CLAIM, EXP_VALUE);\n\t\tCLAIMS.put(AUTH_TIME_CLAIM, AUTH_TIME_VALUE);\n\t\tCLAIMS.put(NONCE_CLAIM, NONCE_VALUE);\n\t\tCLAIMS.put(ACR_CLAIM, ACR_VALUE);\n\t\tCLAIMS.put(AMR_CLAIM, AMR_VALUE);\n\t\tCLAIMS.put(AZP_CLAIM, AZP_VALUE);\n\t\tCLAIMS.put(AT_HASH_CLAIM, AT_HASH_VALUE);\n\t\tCLAIMS.put(C_HASH_CLAIM, C_HASH_VALUE);\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new OidcIdToken(null, Instant.ofEpochMilli(IAT_VALUE), Instant.ofEpochMilli(EXP_VALUE), CLAIMS));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClaimsIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OidcIdToken(ID_TOKEN_VALUE,\n\t\t\t\tInstant.ofEpochMilli(IAT_VALUE), Instant.ofEpochMilli(EXP_VALUE), Collections.emptyMap()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenParametersProvidedAndValidThenCreated() {\n\t\tOidcIdToken idToken = new OidcIdToken(ID_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE),\n\t\t\t\tInstant.ofEpochMilli(EXP_VALUE), CLAIMS);\n\t\tassertThat(idToken.getClaims()).isEqualTo(CLAIMS);\n\t\tassertThat(idToken.getTokenValue()).isEqualTo(ID_TOKEN_VALUE);\n\t\tassertThat(idToken.getIssuer().toString()).isEqualTo(ISS_VALUE);\n\t\tassertThat(idToken.getSubject()).isEqualTo(SUB_VALUE);\n\t\tassertThat(idToken.getAudience()).isEqualTo(AUD_VALUE);\n\t\tassertThat(idToken.getIssuedAt().toEpochMilli()).isEqualTo(IAT_VALUE);\n\t\tassertThat(idToken.getExpiresAt().toEpochMilli()).isEqualTo(EXP_VALUE);\n\t\tassertThat(idToken.getAuthenticatedAt().getEpochSecond()).isEqualTo(AUTH_TIME_VALUE);\n\t\tassertThat(idToken.getNonce()).isEqualTo(NONCE_VALUE);\n\t\tassertThat(idToken.getAuthenticationContextClass()).isEqualTo(ACR_VALUE);\n\t\tassertThat(idToken.getAuthenticationMethods()).isEqualTo(AMR_VALUE);\n\t\tassertThat(idToken.getAuthorizedParty()).isEqualTo(AZP_VALUE);\n\t\tassertThat(idToken.getAccessTokenHash()).isEqualTo(AT_HASH_VALUE);\n\t\tassertThat(idToken.getAuthorizationCodeHash()).isEqualTo(C_HASH_VALUE);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcUserInfoBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OidcUserInfo}\n */\npublic class OidcUserInfoBuilderTests {\n\n\t@Test\n\tpublic void buildWhenCalledTwiceThenGeneratesTwoOidcUserInfos() {\n\t\tOidcUserInfo.Builder userInfoBuilder = OidcUserInfo.builder();\n\t\t// @formatter:off\n\t\tOidcUserInfo first = userInfoBuilder\n\t\t\t\t.claim(\"TEST_CLAIM_1\", \"C1\")\n\t\t\t\t.build();\n\t\tOidcUserInfo second = userInfoBuilder\n\t\t\t\t.claim(\"TEST_CLAIM_1\", \"C2\")\n\t\t\t\t.claim(\"TEST_CLAIM_2\", \"C3\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(first.getClaims()).hasSize(1);\n\t\tassertThat(first.getClaims()).containsEntry(\"TEST_CLAIM_1\", \"C1\");\n\t\tassertThat(second.getClaims()).hasSize(2);\n\t\tassertThat(second.getClaims()).containsEntry(\"TEST_CLAIM_1\", \"C2\");\n\t\tassertThat(second.getClaims()).containsEntry(\"TEST_CLAIM_2\", \"C3\");\n\t}\n\n\t@Test\n\tpublic void subjectWhenUsingGenericOrNamedClaimMethodThenLastOneWins() {\n\t\tOidcUserInfo.Builder userInfoBuilder = OidcUserInfo.builder();\n\t\tString generic = new String(\"sub\");\n\t\tString named = new String(\"sub\");\n\t\t// @formatter:off\n\t\tOidcUserInfo userInfo = userInfoBuilder\n\t\t\t\t.subject(named)\n\t\t\t\t.claim(IdTokenClaimNames.SUB, generic)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(userInfo.getSubject()).isSameAs(generic);\n\t\t// @formatter:off\n\t\tuserInfo = userInfoBuilder\n\t\t\t\t.claim(IdTokenClaimNames.SUB, generic)\n\t\t\t\t.subject(named)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(userInfo.getSubject()).isSameAs(named);\n\t}\n\n\t@Test\n\tpublic void claimsWhenRemovingAClaimThenIsNotPresent() {\n\t\t// @formatter:off\n\t\tOidcUserInfo.Builder userInfoBuilder = OidcUserInfo.builder()\n\t\t\t\t.claim(\"needs\", \"a claim\");\n\t\tOidcUserInfo userInfo = userInfoBuilder.subject(\"sub\")\n\t\t\t\t.claims((claims) -> claims.remove(IdTokenClaimNames.SUB))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(userInfo.getSubject()).isNull();\n\t}\n\n\t@Test\n\tpublic void claimsWhenAddingAClaimThenIsPresent() {\n\t\tOidcUserInfo.Builder userInfoBuilder = OidcUserInfo.builder();\n\t\tString name = new String(\"name\");\n\t\tString value = new String(\"value\");\n\t\t// @formatter:off\n\t\tOidcUserInfo userInfo = userInfoBuilder\n\t\t\t\t.claims((claims) -> claims.put(name, value))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(userInfo.getClaims()).hasSize(1);\n\t\tassertThat(userInfo.getClaims().get(name)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcUserInfoTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcUserInfo}.\n *\n * @author Joe Grandja\n */\npublic class OidcUserInfoTests {\n\n\tprivate static final String SUB_CLAIM = \"sub\";\n\n\tprivate static final String NAME_CLAIM = \"name\";\n\n\tprivate static final String GIVEN_NAME_CLAIM = \"given_name\";\n\n\tprivate static final String FAMILY_NAME_CLAIM = \"family_name\";\n\n\tprivate static final String MIDDLE_NAME_CLAIM = \"middle_name\";\n\n\tprivate static final String NICKNAME_CLAIM = \"nickname\";\n\n\tprivate static final String PREFERRED_USERNAME_CLAIM = \"preferred_username\";\n\n\tprivate static final String PROFILE_CLAIM = \"profile\";\n\n\tprivate static final String PICTURE_CLAIM = \"picture\";\n\n\tprivate static final String WEBSITE_CLAIM = \"website\";\n\n\tprivate static final String EMAIL_CLAIM = \"email\";\n\n\tprivate static final String EMAIL_VERIFIED_CLAIM = \"email_verified\";\n\n\tprivate static final String GENDER_CLAIM = \"gender\";\n\n\tprivate static final String BIRTHDATE_CLAIM = \"birthdate\";\n\n\tprivate static final String ZONEINFO_CLAIM = \"zoneinfo\";\n\n\tprivate static final String LOCALE_CLAIM = \"locale\";\n\n\tprivate static final String PHONE_NUMBER_CLAIM = \"phone_number\";\n\n\tprivate static final String PHONE_NUMBER_VERIFIED_CLAIM = \"phone_number_verified\";\n\n\tprivate static final String ADDRESS_CLAIM = \"address\";\n\n\tprivate static final String UPDATED_AT_CLAIM = \"updated_at\";\n\n\tprivate static final String SUB_VALUE = \"subject1\";\n\n\tprivate static final String NAME_VALUE = \"full_name\";\n\n\tprivate static final String GIVEN_NAME_VALUE = \"given_name\";\n\n\tprivate static final String FAMILY_NAME_VALUE = \"family_name\";\n\n\tprivate static final String MIDDLE_NAME_VALUE = \"middle_name\";\n\n\tprivate static final String NICKNAME_VALUE = \"nickname\";\n\n\tprivate static final String PREFERRED_USERNAME_VALUE = \"preferred_username\";\n\n\tprivate static final String PROFILE_VALUE = \"profile\";\n\n\tprivate static final String PICTURE_VALUE = \"picture\";\n\n\tprivate static final String WEBSITE_VALUE = \"website\";\n\n\tprivate static final String EMAIL_VALUE = \"email\";\n\n\tprivate static final Boolean EMAIL_VERIFIED_VALUE = true;\n\n\tprivate static final String GENDER_VALUE = \"gender\";\n\n\tprivate static final String BIRTHDATE_VALUE = \"birthdate\";\n\n\tprivate static final String ZONEINFO_VALUE = \"zoneinfo\";\n\n\tprivate static final String LOCALE_VALUE = \"locale\";\n\n\tprivate static final String PHONE_NUMBER_VALUE = \"phone_number\";\n\n\tprivate static final Boolean PHONE_NUMBER_VERIFIED_VALUE = true;\n\n\tprivate static final Map<String, Object> ADDRESS_VALUE;\n\n\tprivate static final long UPDATED_AT_VALUE = Instant.now().minusSeconds(60).toEpochMilli();\n\n\tprivate static final Map<String, Object> CLAIMS;\n\tstatic {\n\t\tCLAIMS = new HashMap<>();\n\t\tCLAIMS.put(SUB_CLAIM, SUB_VALUE);\n\t\tCLAIMS.put(NAME_CLAIM, NAME_VALUE);\n\t\tCLAIMS.put(GIVEN_NAME_CLAIM, GIVEN_NAME_VALUE);\n\t\tCLAIMS.put(FAMILY_NAME_CLAIM, FAMILY_NAME_VALUE);\n\t\tCLAIMS.put(MIDDLE_NAME_CLAIM, MIDDLE_NAME_VALUE);\n\t\tCLAIMS.put(NICKNAME_CLAIM, NICKNAME_VALUE);\n\t\tCLAIMS.put(PREFERRED_USERNAME_CLAIM, PREFERRED_USERNAME_VALUE);\n\t\tCLAIMS.put(PROFILE_CLAIM, PROFILE_VALUE);\n\t\tCLAIMS.put(PICTURE_CLAIM, PICTURE_VALUE);\n\t\tCLAIMS.put(WEBSITE_CLAIM, WEBSITE_VALUE);\n\t\tCLAIMS.put(EMAIL_CLAIM, EMAIL_VALUE);\n\t\tCLAIMS.put(EMAIL_VERIFIED_CLAIM, EMAIL_VERIFIED_VALUE);\n\t\tCLAIMS.put(GENDER_CLAIM, GENDER_VALUE);\n\t\tCLAIMS.put(BIRTHDATE_CLAIM, BIRTHDATE_VALUE);\n\t\tCLAIMS.put(ZONEINFO_CLAIM, ZONEINFO_VALUE);\n\t\tCLAIMS.put(LOCALE_CLAIM, LOCALE_VALUE);\n\t\tCLAIMS.put(PHONE_NUMBER_CLAIM, PHONE_NUMBER_VALUE);\n\t\tCLAIMS.put(PHONE_NUMBER_VERIFIED_CLAIM, PHONE_NUMBER_VERIFIED_VALUE);\n\t\tADDRESS_VALUE = new HashMap<>();\n\t\tADDRESS_VALUE.put(DefaultAddressStandardClaimTests.FORMATTED_FIELD_NAME,\n\t\t\t\tDefaultAddressStandardClaimTests.FORMATTED);\n\t\tADDRESS_VALUE.put(DefaultAddressStandardClaimTests.STREET_ADDRESS_FIELD_NAME,\n\t\t\t\tDefaultAddressStandardClaimTests.STREET_ADDRESS);\n\t\tADDRESS_VALUE.put(DefaultAddressStandardClaimTests.LOCALITY_FIELD_NAME,\n\t\t\t\tDefaultAddressStandardClaimTests.LOCALITY);\n\t\tADDRESS_VALUE.put(DefaultAddressStandardClaimTests.REGION_FIELD_NAME, DefaultAddressStandardClaimTests.REGION);\n\t\tADDRESS_VALUE.put(DefaultAddressStandardClaimTests.POSTAL_CODE_FIELD_NAME,\n\t\t\t\tDefaultAddressStandardClaimTests.POSTAL_CODE);\n\t\tADDRESS_VALUE.put(DefaultAddressStandardClaimTests.COUNTRY_FIELD_NAME,\n\t\t\t\tDefaultAddressStandardClaimTests.COUNTRY);\n\t\tCLAIMS.put(ADDRESS_CLAIM, ADDRESS_VALUE);\n\t\tCLAIMS.put(UPDATED_AT_CLAIM, UPDATED_AT_VALUE);\n\t}\n\n\t@Test\n\tpublic void constructorWhenClaimsIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OidcUserInfo(Collections.emptyMap()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenParametersProvidedAndValidThenCreated() {\n\t\tOidcUserInfo userInfo = new OidcUserInfo(CLAIMS);\n\t\tassertThat(userInfo.getClaims()).isEqualTo(CLAIMS);\n\t\tassertThat(userInfo.getSubject()).isEqualTo(SUB_VALUE);\n\t\tassertThat(userInfo.getFullName()).isEqualTo(NAME_VALUE);\n\t\tassertThat(userInfo.getGivenName()).isEqualTo(GIVEN_NAME_VALUE);\n\t\tassertThat(userInfo.getFamilyName()).isEqualTo(FAMILY_NAME_VALUE);\n\t\tassertThat(userInfo.getMiddleName()).isEqualTo(MIDDLE_NAME_VALUE);\n\t\tassertThat(userInfo.getNickName()).isEqualTo(NICKNAME_VALUE);\n\t\tassertThat(userInfo.getPreferredUsername()).isEqualTo(PREFERRED_USERNAME_VALUE);\n\t\tassertThat(userInfo.getProfile()).isEqualTo(PROFILE_VALUE);\n\t\tassertThat(userInfo.getPicture()).isEqualTo(PICTURE_VALUE);\n\t\tassertThat(userInfo.getWebsite()).isEqualTo(WEBSITE_VALUE);\n\t\tassertThat(userInfo.getEmail()).isEqualTo(EMAIL_VALUE);\n\t\tassertThat(userInfo.getEmailVerified()).isEqualTo(EMAIL_VERIFIED_VALUE);\n\t\tassertThat(userInfo.getGender()).isEqualTo(GENDER_VALUE);\n\t\tassertThat(userInfo.getBirthdate()).isEqualTo(BIRTHDATE_VALUE);\n\t\tassertThat(userInfo.getZoneInfo()).isEqualTo(ZONEINFO_VALUE);\n\t\tassertThat(userInfo.getLocale()).isEqualTo(LOCALE_VALUE);\n\t\tassertThat(userInfo.getPhoneNumber()).isEqualTo(PHONE_NUMBER_VALUE);\n\t\tassertThat(userInfo.getPhoneNumberVerified()).isEqualTo(PHONE_NUMBER_VERIFIED_VALUE);\n\t\tassertThat(userInfo.getAddress()).isEqualTo(new DefaultAddressStandardClaim.Builder(ADDRESS_VALUE).build());\n\t\tassertThat(userInfo.getUpdatedAt().getEpochSecond()).isEqualTo(UPDATED_AT_VALUE);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/TestOidcIdTokens.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc;\n\nimport java.time.Instant;\nimport java.util.List;\n\n/**\n * Test {@link OidcIdToken}s\n *\n * @author Josh Cummings\n */\npublic final class TestOidcIdTokens {\n\n\tprivate TestOidcIdTokens() {\n\t}\n\n\tpublic static OidcIdToken.Builder idToken() {\n\t\t// @formatter:off\n\t\treturn OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t\t.issuer(\"https://example.com\")\n\t\t\t\t.audience(List.of(\"client-id\"))\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.expiresAt(Instant.now()\n\t\t\t\t.plusSeconds(86400))\n\t\t\t\t.claim(\"id\", \"id\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc.user;\n\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link DefaultOidcUser}.\n *\n * @author Vedran Pavic\n * @author Joe Grandja\n */\npublic class DefaultOidcUserTests {\n\n\tprivate static final SimpleGrantedAuthority AUTHORITY = new SimpleGrantedAuthority(\"ROLE_USER\");\n\n\tprivate static final Set<GrantedAuthority> AUTHORITIES = Collections.singleton(AUTHORITY);\n\n\tprivate static final String SUBJECT = \"test-subject\";\n\n\tprivate static final String EMAIL = \"test-subject@example.com\";\n\n\tprivate static final String NAME = \"test-name\";\n\n\tprivate static final Map<String, Object> ID_TOKEN_CLAIMS = new HashMap<>();\n\n\tprivate static final Map<String, Object> USER_INFO_CLAIMS = new HashMap<>();\n\tstatic {\n\t\tID_TOKEN_CLAIMS.put(IdTokenClaimNames.ISS, \"https://example.com\");\n\t\tID_TOKEN_CLAIMS.put(IdTokenClaimNames.SUB, SUBJECT);\n\t\tUSER_INFO_CLAIMS.put(StandardClaimNames.NAME, NAME);\n\t\tUSER_INFO_CLAIMS.put(StandardClaimNames.EMAIL, EMAIL);\n\t}\n\tprivate static final OidcIdToken ID_TOKEN = new OidcIdToken(\"id-token-value\", Instant.EPOCH, Instant.MAX,\n\t\t\tID_TOKEN_CLAIMS);\n\n\tprivate static final OidcUserInfo USER_INFO = new OidcUserInfo(USER_INFO_CLAIMS);\n\n\t@Test\n\tpublic void constructorWhenIdTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DefaultOidcUser(AUTHORITIES, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNameAttributeKeyInvalidThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DefaultOidcUser(AUTHORITIES, ID_TOKEN, \"invalid\"));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIsNullThenCreatedWithEmptyAuthorities() {\n\t\tDefaultOidcUser user = new DefaultOidcUser(null, ID_TOKEN);\n\t\tassertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);\n\t\tassertThat(user.getIdToken()).isEqualTo(ID_TOKEN);\n\t\tassertThat(user.getName()).isEqualTo(SUBJECT);\n\t\tassertThat(user.getAuthorities()).isEmpty();\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIsEmptyThenCreated() {\n\t\tDefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, ID_TOKEN);\n\t\tassertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);\n\t\tassertThat(user.getIdToken()).isEqualTo(ID_TOKEN);\n\t\tassertThat(user.getName()).isEqualTo(SUBJECT);\n\t\tassertThat(user.getAuthorities()).isEmpty();\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIdTokenProvidedThenCreated() {\n\t\tDefaultOidcUser user = new DefaultOidcUser(AUTHORITIES, ID_TOKEN);\n\t\tassertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);\n\t\tassertThat(user.getIdToken()).isEqualTo(ID_TOKEN);\n\t\tassertThat(user.getName()).isEqualTo(SUBJECT);\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isEqualTo(AUTHORITY);\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIdTokenNameAttributeKeyProvidedThenCreated() {\n\t\tDefaultOidcUser user = new DefaultOidcUser(AUTHORITIES, ID_TOKEN, IdTokenClaimNames.SUB);\n\t\tassertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);\n\t\tassertThat(user.getIdToken()).isEqualTo(ID_TOKEN);\n\t\tassertThat(user.getName()).isEqualTo(SUBJECT);\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isEqualTo(AUTHORITY);\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIdTokenUserInfoProvidedThenCreated() {\n\t\tDefaultOidcUser user = new DefaultOidcUser(AUTHORITIES, ID_TOKEN, USER_INFO);\n\t\tassertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB,\n\t\t\t\tStandardClaimNames.NAME, StandardClaimNames.EMAIL);\n\t\tassertThat(user.getIdToken()).isEqualTo(ID_TOKEN);\n\t\tassertThat(user.getUserInfo()).isEqualTo(USER_INFO);\n\t\tassertThat(user.getName()).isEqualTo(SUBJECT);\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isEqualTo(AUTHORITY);\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB,\n\t\t\t\tStandardClaimNames.NAME, StandardClaimNames.EMAIL);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tDefaultOidcUser user = new DefaultOidcUser(AUTHORITIES, ID_TOKEN, USER_INFO, StandardClaimNames.EMAIL);\n\t\tassertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB,\n\t\t\t\tStandardClaimNames.NAME, StandardClaimNames.EMAIL);\n\t\tassertThat(user.getIdToken()).isEqualTo(ID_TOKEN);\n\t\tassertThat(user.getUserInfo()).isEqualTo(USER_INFO);\n\t\tassertThat(user.getName()).isEqualTo(EMAIL);\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isEqualTo(AUTHORITY);\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB,\n\t\t\t\tStandardClaimNames.NAME, StandardClaimNames.EMAIL);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthorityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc.user;\n\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.StandardClaimNames;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OidcUserAuthority}.\n *\n * @author Joe Grandja\n */\npublic class OidcUserAuthorityTests {\n\n\tprivate static final String AUTHORITY = \"ROLE_USER\";\n\n\tprivate static final String SUBJECT = \"test-subject\";\n\n\tprivate static final String EMAIL = \"test-subject@example.com\";\n\n\tprivate static final String NAME = \"test-name\";\n\n\tprivate static final Map<String, Object> ID_TOKEN_CLAIMS = new HashMap<>();\n\n\tprivate static final Map<String, Object> USER_INFO_CLAIMS = new HashMap<>();\n\tstatic {\n\t\tID_TOKEN_CLAIMS.put(IdTokenClaimNames.ISS, \"https://example.com\");\n\t\tID_TOKEN_CLAIMS.put(IdTokenClaimNames.SUB, SUBJECT);\n\t\tUSER_INFO_CLAIMS.put(StandardClaimNames.NAME, NAME);\n\t\tUSER_INFO_CLAIMS.put(StandardClaimNames.EMAIL, EMAIL);\n\t}\n\tprivate static final OidcIdToken ID_TOKEN = new OidcIdToken(\"id-token-value\", Instant.EPOCH, Instant.MAX,\n\t\t\tID_TOKEN_CLAIMS);\n\n\tprivate static final OidcUserInfo USER_INFO = new OidcUserInfo(USER_INFO_CLAIMS);\n\n\t@Test\n\tpublic void constructorWhenIdTokenIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OidcUserAuthority(null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenUserInfoIsNullThenDoesNotThrowAnyException() {\n\t\tnew OidcUserAuthority(ID_TOKEN, null);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorityIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OidcUserAuthority(null, ID_TOKEN, USER_INFO));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOidcUserAuthority userAuthority = new OidcUserAuthority(AUTHORITY, ID_TOKEN, USER_INFO);\n\t\tassertThat(userAuthority.getIdToken()).isEqualTo(ID_TOKEN);\n\t\tassertThat(userAuthority.getUserInfo()).isEqualTo(USER_INFO);\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(AUTHORITY);\n\t\tassertThat(userAuthority.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB,\n\t\t\t\tStandardClaimNames.NAME, StandardClaimNames.EMAIL);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/TestOidcUsers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.oidc.user;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\n\n/**\n * @author Joe Grandja\n */\npublic final class TestOidcUsers {\n\n\tprivate TestOidcUsers() {\n\t}\n\n\tpublic static DefaultOidcUser create() {\n\t\tOidcIdToken idToken = idToken();\n\t\tOidcUserInfo userInfo = userInfo();\n\t\treturn new DefaultOidcUser(authorities(idToken, userInfo), idToken, userInfo);\n\t}\n\n\tprivate static OidcIdToken idToken() {\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plusSeconds(3600);\n\t\t// @formatter:off\n\t\treturn OidcIdToken.withTokenValue(\"id-token\")\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.issuer(\"http://localhost/issuer\")\n\t\t\t\t.audience(Collections.unmodifiableSet(new LinkedHashSet<>(Collections.singletonList(\"client-id\"))))\n\t\t\t\t.authorizedParty(\"client\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static OidcUserInfo userInfo() {\n\t\treturn OidcUserInfo.builder().subject(\"subject\").name(\"full name\").build();\n\t}\n\n\tprivate static Collection<? extends GrantedAuthority> authorities(OidcIdToken idToken, OidcUserInfo userInfo) {\n\t\treturn new LinkedHashSet<>(Arrays.asList(new OidcUserAuthority(idToken, userInfo),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_read\"), new SimpleGrantedAuthority(\"SCOPE_write\")));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.user;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.util.SerializationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link DefaultOAuth2User}.\n *\n * @author Vedran Pavic\n * @author Joe Grandja\n * @author Park Hyojong\n */\npublic class DefaultOAuth2UserTests {\n\n\tprivate static final SimpleGrantedAuthority AUTHORITY = new SimpleGrantedAuthority(\"ROLE_USER\");\n\n\tprivate static final Set<GrantedAuthority> AUTHORITIES = Collections.singleton(AUTHORITY);\n\n\tprivate static final String ATTRIBUTE_NAME_KEY = \"username\";\n\n\tprivate static final String USERNAME = \"test\";\n\n\tprivate static final Map<String, Object> ATTRIBUTES = Collections.singletonMap(ATTRIBUTE_NAME_KEY, USERNAME);\n\n\t@Test\n\tpublic void constructorWhenAttributesIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2User(AUTHORITIES, null, ATTRIBUTE_NAME_KEY));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAttributesIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2User(AUTHORITIES, Collections.emptyMap(), ATTRIBUTE_NAME_KEY));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAttributeValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DefaultOAuth2User(AUTHORITIES,\n\t\t\t\tCollections.singletonMap(ATTRIBUTE_NAME_KEY, null), ATTRIBUTE_NAME_KEY));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNameAttributeKeyIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNameAttributeKeyIsInvalidThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, \"invalid\"));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIsNullThenCreatedWithEmptyAuthorities() {\n\t\tDefaultOAuth2User user = new DefaultOAuth2User(null, ATTRIBUTES, ATTRIBUTE_NAME_KEY);\n\t\tassertThat(user.getName()).isEqualTo(USERNAME);\n\t\tassertThat(user.getAuthorities()).isEmpty();\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(ATTRIBUTE_NAME_KEY);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIsEmptyThenCreated() {\n\t\tDefaultOAuth2User user = new DefaultOAuth2User(Collections.emptySet(), ATTRIBUTES, ATTRIBUTE_NAME_KEY);\n\t\tassertThat(user.getName()).isEqualTo(USERNAME);\n\t\tassertThat(user.getAuthorities()).isEmpty();\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(ATTRIBUTE_NAME_KEY);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tDefaultOAuth2User user = new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, ATTRIBUTE_NAME_KEY);\n\t\tassertThat(user.getName()).isEqualTo(USERNAME);\n\t\tassertThat(user.getAuthorities()).hasSize(1);\n\t\tassertThat(user.getAuthorities().iterator().next()).isEqualTo(AUTHORITY);\n\t\tassertThat(user.getAttributes()).containsOnlyKeys(ATTRIBUTE_NAME_KEY);\n\t}\n\n\t// gh-4917\n\t@Test\n\tpublic void constructorWhenCreatedThenIsSerializable() {\n\t\tDefaultOAuth2User user = new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, ATTRIBUTE_NAME_KEY);\n\t\tSerializationUtils.serialize(user);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/OAuth2UserAuthorityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.user;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2UserAuthority}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2UserAuthorityTests {\n\n\tprivate static final String AUTHORITY = \"ROLE_USER\";\n\n\tprivate static final Map<String, Object> ATTRIBUTES = Collections.singletonMap(\"username\", \"test\");\n\n\tprivate static final OAuth2UserAuthority AUTHORITY_WITH_OBJECTURL;\n\n\tprivate static final OAuth2UserAuthority AUTHORITY_WITH_STRINGURL;\n\n\tstatic {\n\t\ttry {\n\t\t\tAUTHORITY_WITH_OBJECTURL = new OAuth2UserAuthority(\n\t\t\t\t\tCollections.singletonMap(\"someurl\", new URL(\"https://localhost\")));\n\t\t\tAUTHORITY_WITH_STRINGURL = new OAuth2UserAuthority(\n\t\t\t\t\tCollections.singletonMap(\"someurl\", \"https://localhost\"));\n\t\t}\n\t\tcatch (MalformedURLException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthorityIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2UserAuthority(null, ATTRIBUTES));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAttributesIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OAuth2UserAuthority(AUTHORITY, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAttributesIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2UserAuthority(AUTHORITY, Collections.emptyMap()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2UserAuthority userAuthority = new OAuth2UserAuthority(AUTHORITY, ATTRIBUTES);\n\t\tassertThat(userAuthority.getAuthority()).isEqualTo(AUTHORITY);\n\t\tassertThat(userAuthority.getAttributes()).isEqualTo(ATTRIBUTES);\n\t}\n\n\t@Test\n\tpublic void equalsRegardlessOfUrlType() {\n\t\tassertThat(AUTHORITY_WITH_OBJECTURL).isEqualTo(AUTHORITY_WITH_OBJECTURL);\n\t\tassertThat(AUTHORITY_WITH_STRINGURL).isEqualTo(AUTHORITY_WITH_STRINGURL);\n\n\t\tassertThat(AUTHORITY_WITH_OBJECTURL).isEqualTo(AUTHORITY_WITH_STRINGURL);\n\t\tassertThat(AUTHORITY_WITH_STRINGURL).isEqualTo(AUTHORITY_WITH_OBJECTURL);\n\t}\n\n\t@Test\n\tpublic void hashCodeIsSameRegardlessOfUrlType() {\n\t\tassertThat(AUTHORITY_WITH_OBJECTURL.hashCode()).isEqualTo(AUTHORITY_WITH_OBJECTURL.hashCode());\n\t\tassertThat(AUTHORITY_WITH_STRINGURL.hashCode()).isEqualTo(AUTHORITY_WITH_STRINGURL.hashCode());\n\n\t\tassertThat(AUTHORITY_WITH_OBJECTURL.hashCode()).isEqualTo(AUTHORITY_WITH_STRINGURL.hashCode());\n\t\tassertThat(AUTHORITY_WITH_STRINGURL.hashCode()).isEqualTo(AUTHORITY_WITH_OBJECTURL.hashCode());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/TestOAuth2Users.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.user;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\n\n/**\n * @author Rob Winch\n */\npublic final class TestOAuth2Users {\n\n\tprivate TestOAuth2Users() {\n\t}\n\n\tpublic static DefaultOAuth2User create() {\n\t\tString nameAttributeKey = \"username\";\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(nameAttributeKey, \"user\");\n\t\tCollection<GrantedAuthority> authorities = authorities(attributes, nameAttributeKey);\n\t\treturn new DefaultOAuth2User(authorities, attributes, nameAttributeKey);\n\t}\n\n\tprivate static Collection<GrantedAuthority> authorities(Map<String, Object> attributes,\n\t\t\tString userNameAttributeName) {\n\t\treturn new LinkedHashSet<>(Arrays.asList(new OAuth2UserAuthority(attributes, userNameAttributeName),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_read\"), new SimpleGrantedAuthority(\"SCOPE_write\")));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2BodyExtractorsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core.web.reactive.function;\n\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ReactiveHttpInputMessage;\nimport org.springframework.http.codec.ClientCodecConfigurer;\nimport org.springframework.http.codec.HttpMessageReader;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.mock.http.client.reactive.MockClientHttpResponse;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthorizationException;\nimport org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;\nimport org.springframework.web.reactive.function.BodyExtractor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class OAuth2BodyExtractorsTests {\n\n\tprivate BodyExtractor.Context context;\n\n\tprivate Map<String, Object> hints;\n\n\t@BeforeEach\n\tpublic void createContext() {\n\t\tList<HttpMessageReader<?>> messageReaders = ClientCodecConfigurer.create().getReaders();\n\t\tthis.hints = new HashMap<>();\n\t\tthis.context = new BodyExtractor.Context() {\n\t\t\t@Override\n\t\t\tpublic List<HttpMessageReader<?>> messageReaders() {\n\t\t\t\treturn messageReaders;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<ServerHttpResponse> serverResponse() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Map<String, Object> hints() {\n\t\t\t\treturn OAuth2BodyExtractorsTests.this.hints;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Test\n\tpublic void oauth2AccessTokenResponseWhenInvalidJsonThenException() {\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = OAuth2BodyExtractors\n\t\t\t.oauth2AccessTokenResponse();\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK);\n\t\tresponse.getHeaders().setContentType(MediaType.APPLICATION_JSON);\n\t\tresponse.setBody(\"{\");\n\t\tMono<OAuth2AccessTokenResponse> result = extractor.extract(response, this.context);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(result::block)\n\t\t\t\t.withMessageContaining(\"An error occurred parsing the Access Token response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2AccessTokenResponseWhenEmptyThenException() {\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = OAuth2BodyExtractors\n\t\t\t.oauth2AccessTokenResponse();\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK);\n\t\tMono<OAuth2AccessTokenResponse> result = extractor.extract(response, this.context);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthorizationException.class)\n\t\t\t\t.isThrownBy(result::block)\n\t\t\t\t.withMessageContaining(\"Empty OAuth 2.0 Access Token Response\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2AccessTokenResponseWhenValidThenCreated() {\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = OAuth2BodyExtractors\n\t\t\t.oauth2AccessTokenResponse();\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK);\n\t\tresponse.getHeaders().setContentType(MediaType.APPLICATION_JSON);\n\t\t// @formatter:off\n\t\tresponse.setBody(\n\t\t\t\t\"{\\n\"\n\t\t\t+ \"       \\\"access_token\\\":\\\"2YotnFZFEjr1zCsicMWpAA\\\",\\n\"\n\t\t\t+ \"       \\\"token_type\\\":\\\"Bearer\\\",\\n\"\n\t\t\t+ \"       \\\"expires_in\\\":3600,\\n\"\n\t\t\t+ \"       \\\"refresh_token\\\":\\\"tGzv3JOkF0XG5Qx2TlKWIA\\\",\\n\"\n\t\t\t+ \"       \\\"example_parameter\\\":\\\"example_value\\\"\\n\"\n\t\t\t+ \"     }\");\n\t\t// @formatter:on\n\t\tInstant now = Instant.now();\n\t\tOAuth2AccessTokenResponse result = extractor.extract(response, this.context).block();\n\t\tassertThat(result.getAccessToken().getTokenValue()).isEqualTo(\"2YotnFZFEjr1zCsicMWpAA\");\n\t\tassertThat(result.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(result.getAccessToken().getExpiresAt()).isBetween(now.plusSeconds(3600), now.plusSeconds(3600 + 2));\n\t\tassertThat(result.getRefreshToken().getTokenValue()).isEqualTo(\"tGzv3JOkF0XG5Qx2TlKWIA\");\n\t\tassertThat(result.getAdditionalParameters()).containsEntry(\"example_parameter\", \"example_value\");\n\t}\n\n\t@Test\n\t// gh-6087\n\tpublic void oauth2AccessTokenResponseWhenMultipleAttributeTypesThenCreated() {\n\t\tBodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> extractor = OAuth2BodyExtractors\n\t\t\t.oauth2AccessTokenResponse();\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(HttpStatus.OK);\n\t\tresponse.getHeaders().setContentType(MediaType.APPLICATION_JSON);\n\t\t// @formatter:off\n\t\tresponse.setBody(\n\t\t\t\t\"{\\n\"\n\t\t\t+ \"       \\\"access_token\\\":\\\"2YotnFZFEjr1zCsicMWpAA\\\",\\n\"\n\t\t\t+ \"       \\\"token_type\\\":\\\"Bearer\\\",\\n\"\n\t\t\t+ \"       \\\"expires_in\\\":3600,\\n\"\n\t\t\t+ \"       \\\"refresh_token\\\":\\\"tGzv3JOkF0XG5Qx2TlKWIA\\\",\\n\"\n\t\t\t+ \"       \\\"subjson\\\":{}, \\n\"\n\t\t\t+ \"\t\t  \\\"list\\\":[]  \\n\"\n\t\t\t+ \"     }\");\n\t\t// @formatter:on\n\t\tInstant now = Instant.now();\n\t\tOAuth2AccessTokenResponse result = extractor.extract(response, this.context).block();\n\t\tassertThat(result.getAccessToken().getTokenValue()).isEqualTo(\"2YotnFZFEjr1zCsicMWpAA\");\n\t\tassertThat(result.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);\n\t\tassertThat(result.getAccessToken().getExpiresAt()).isBetween(now.plusSeconds(3600), now.plusSeconds(3600 + 2));\n\t\tassertThat(result.getRefreshToken().getTokenValue()).isEqualTo(\"tGzv3JOkF0XG5Qx2TlKWIA\");\n\t\tassertThat(result.getAdditionalParameters().get(\"subjson\")).isInstanceOfAny(Map.class);\n\t\tassertThat(result.getAdditionalParameters().get(\"list\")).isInstanceOfAny(List.class);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/spring-security-oauth2-jose.gradle",
    "content": "plugins {\n\tid 'javadoc-warnings-error'\n\tid 'security-nullability'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi project(':spring-security-oauth2-core')\n\tapi 'org.springframework:spring-core'\n\tapi 'com.nimbusds:nimbus-jose-jwt'\n\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'org.springframework:spring-webflux'\n\n\ttestImplementation \"org.bouncycastle:bcpkix-jdk18on\"\n\ttestImplementation \"org.bouncycastle:bcprov-jdk18on\"\n\ttestImplementation \"jakarta.servlet:jakarta.servlet-api\"\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\ttestImplementation 'io.projectreactor.netty:reactor-netty'\n\ttestImplementation 'tools.jackson.core:jackson-databind'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/JwaAlgorithm.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose;\n\n/**\n * Super interface for cryptographic algorithms defined by the JSON Web Algorithms (JWA)\n * specification and used by JSON Web Signature (JWS) to digitally sign or create a MAC of\n * the contents and JSON Web Encryption (JWE) to encrypt the contents.\n *\n * @author Joe Grandja\n * @since 5.5\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7518\">JSON Web Algorithms\n * (JWA)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516\">JSON Web Encryption\n * (JWE)</a>\n */\npublic interface JwaAlgorithm {\n\n\t/**\n\t * Returns the algorithm name.\n\t * @return the algorithm name\n\t */\n\tString getName();\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/JwsAlgorithm.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose.jws;\n\nimport org.springframework.security.oauth2.jose.JwaAlgorithm;\n\n/**\n * Super interface for cryptographic algorithms defined by the JSON Web Algorithms (JWA)\n * specification and used by JSON Web Signature (JWS) to digitally sign or create a MAC of\n * the contents of the JWS Protected Header and JWS Payload.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see JwaAlgorithm\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7518\">JSON Web Algorithms\n * (JWA)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc7518#section-3\">Cryptographic Algorithms for Digital\n * Signatures and MACs</a>\n */\npublic interface JwsAlgorithm extends JwaAlgorithm {\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/JwsAlgorithms.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose.jws;\n\n/**\n * The cryptographic algorithms defined by the JSON Web Algorithms (JWA) specification and\n * used by JSON Web Signature (JWS) to digitally sign or create a MAC of the contents of\n * the JWS Protected Header and JWS Payload.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7518\">JSON Web Algorithms\n * (JWA)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc7518#section-3\">Cryptographic Algorithms for Digital\n * Signatures and MACs</a>\n */\npublic final class JwsAlgorithms {\n\n\t/**\n\t * HMAC using SHA-256 (Required)\n\t */\n\tpublic static final String HS256 = \"HS256\";\n\n\t/**\n\t * HMAC using SHA-384 (Optional)\n\t */\n\tpublic static final String HS384 = \"HS384\";\n\n\t/**\n\t * HMAC using SHA-512 (Optional)\n\t */\n\tpublic static final String HS512 = \"HS512\";\n\n\t/**\n\t * RSASSA-PKCS1-v1_5 using SHA-256 (Recommended)\n\t */\n\tpublic static final String RS256 = \"RS256\";\n\n\t/**\n\t * RSASSA-PKCS1-v1_5 using SHA-384 (Optional)\n\t */\n\tpublic static final String RS384 = \"RS384\";\n\n\t/**\n\t * RSASSA-PKCS1-v1_5 using SHA-512 (Optional)\n\t */\n\tpublic static final String RS512 = \"RS512\";\n\n\t/**\n\t * ECDSA using P-256 and SHA-256 (Recommended+)\n\t */\n\tpublic static final String ES256 = \"ES256\";\n\n\t/**\n\t * ECDSA using P-384 and SHA-384 (Optional)\n\t */\n\tpublic static final String ES384 = \"ES384\";\n\n\t/**\n\t * ECDSA using P-521 and SHA-512 (Optional)\n\t */\n\tpublic static final String ES512 = \"ES512\";\n\n\t/**\n\t * RSASSA-PSS using SHA-256 and MGF1 with SHA-256 (Optional)\n\t */\n\tpublic static final String PS256 = \"PS256\";\n\n\t/**\n\t * RSASSA-PSS using SHA-384 and MGF1 with SHA-384 (Optional)\n\t */\n\tpublic static final String PS384 = \"PS384\";\n\n\t/**\n\t * RSASSA-PSS using SHA-512 and MGF1 with SHA-512 (Optional)\n\t */\n\tpublic static final String PS512 = \"PS512\";\n\n\tprivate JwsAlgorithms() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/MacAlgorithm.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose.jws;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An enumeration of the cryptographic algorithms defined by the JSON Web Algorithms (JWA)\n * specification and used by JSON Web Signature (JWS) to create a MAC of the contents of\n * the JWS Protected Header and JWS Payload.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see JwsAlgorithm\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7518\">JSON Web Algorithms\n * (JWA)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc7518#section-3\">Cryptographic Algorithms for Digital\n * Signatures and MACs</a>\n */\npublic enum MacAlgorithm implements JwsAlgorithm {\n\n\t/**\n\t * HMAC using SHA-256 (Required)\n\t */\n\tHS256(JwsAlgorithms.HS256),\n\n\t/**\n\t * HMAC using SHA-384 (Optional)\n\t */\n\tHS384(JwsAlgorithms.HS384),\n\n\t/**\n\t * HMAC using SHA-512 (Optional)\n\t */\n\tHS512(JwsAlgorithms.HS512);\n\n\tprivate final String name;\n\n\tMacAlgorithm(String name) {\n\t\tthis.name = name;\n\t}\n\n\t/**\n\t * Returns the algorithm name.\n\t * @return the algorithm name\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\t/**\n\t * Attempt to resolve the provided algorithm name to a {@code MacAlgorithm}.\n\t * @param name the algorithm name\n\t * @return the resolved {@code MacAlgorithm}, or {@code null} if not found\n\t */\n\tpublic static @Nullable MacAlgorithm from(String name) {\n\t\tfor (MacAlgorithm algorithm : values()) {\n\t\t\tif (algorithm.getName().equals(name)) {\n\t\t\t\treturn algorithm;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithm.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose.jws;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An enumeration of the cryptographic algorithms defined by the JSON Web Algorithms (JWA)\n * specification and used by JSON Web Signature (JWS) to digitally sign the contents of\n * the JWS Protected Header and JWS Payload.\n *\n * @author Joe Grandja\n * @since 5.2\n * @see JwsAlgorithm\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7518\">JSON Web Algorithms\n * (JWA)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc7518#section-3\">Cryptographic Algorithms for Digital\n * Signatures and MACs</a>\n */\npublic enum SignatureAlgorithm implements JwsAlgorithm {\n\n\t/**\n\t * RSASSA-PKCS1-v1_5 using SHA-256 (Recommended)\n\t */\n\tRS256(JwsAlgorithms.RS256),\n\n\t/**\n\t * RSASSA-PKCS1-v1_5 using SHA-384 (Optional)\n\t */\n\tRS384(JwsAlgorithms.RS384),\n\n\t/**\n\t * RSASSA-PKCS1-v1_5 using SHA-512 (Optional)\n\t */\n\tRS512(JwsAlgorithms.RS512),\n\n\t/**\n\t * ECDSA using P-256 and SHA-256 (Recommended+)\n\t */\n\tES256(JwsAlgorithms.ES256),\n\n\t/**\n\t * ECDSA using P-384 and SHA-384 (Optional)\n\t */\n\tES384(JwsAlgorithms.ES384),\n\n\t/**\n\t * ECDSA using P-521 and SHA-512 (Optional)\n\t */\n\tES512(JwsAlgorithms.ES512),\n\n\t/**\n\t * RSASSA-PSS using SHA-256 and MGF1 with SHA-256 (Optional)\n\t */\n\tPS256(JwsAlgorithms.PS256),\n\n\t/**\n\t * RSASSA-PSS using SHA-384 and MGF1 with SHA-384 (Optional)\n\t */\n\tPS384(JwsAlgorithms.PS384),\n\n\t/**\n\t * RSASSA-PSS using SHA-512 and MGF1 with SHA-512 (Optional)\n\t */\n\tPS512(JwsAlgorithms.PS512);\n\n\tprivate final String name;\n\n\tSignatureAlgorithm(String name) {\n\t\tthis.name = name;\n\t}\n\n\t/**\n\t * Returns the algorithm name.\n\t * @return the algorithm name\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\t/**\n\t * Attempt to resolve the provided algorithm name to a {@code SignatureAlgorithm}.\n\t * @param name the algorithm name\n\t * @return the resolved {@code SignatureAlgorithm}, or {@code null} if not found\n\t */\n\tpublic static @Nullable SignatureAlgorithm from(String name) {\n\t\tfor (SignatureAlgorithm value : values()) {\n\t\t\tif (value.getName().equals(name)) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/jws/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core classes and interfaces providing support for JSON Web Signature (JWS).\n */\n@NullMarked\npackage org.springframework.security.oauth2.jose.jws;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jose/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core classes and interfaces providing support for JavaScript Object Signing and\n * Encryption (JOSE).\n */\n@NullMarked\npackage org.springframework.security.oauth2.jose;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/BadJwtException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.io.Serial;\n\n/**\n * An exception similar to\n * {@link org.springframework.security.authentication.BadCredentialsException} that\n * indicates a {@link Jwt} that is invalid in some way.\n *\n * @author Josh Cummings\n * @since 5.3\n */\npublic class BadJwtException extends JwtException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 7748429527132280501L;\n\n\tpublic BadJwtException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic BadJwtException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URI;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.util.Assert;\n\n/**\n * A context class that holds a DPoP Proof {@link Jwt} and additional parameters\n * associated to an Access Token request or a Protected Resource request.\n *\n * @author Joe Grandja\n * @since 6.5\n * @see DPoPProofJwtDecoderFactory\n */\npublic final class DPoPProofContext {\n\n\tprivate final String dPoPProof;\n\n\tprivate final String method;\n\n\tprivate final String targetUri;\n\n\tprivate final @Nullable OAuth2Token accessToken;\n\n\tprivate DPoPProofContext(String dPoPProof, String method, String targetUri, @Nullable OAuth2Token accessToken) {\n\t\tthis.dPoPProof = dPoPProof;\n\t\tthis.method = method;\n\t\tthis.targetUri = targetUri;\n\t\tthis.accessToken = accessToken;\n\t}\n\n\t/**\n\t * Returns the DPoP Proof {@link Jwt}.\n\t * @return the DPoP Proof {@link Jwt}\n\t */\n\tpublic String getDPoPProof() {\n\t\treturn this.dPoPProof;\n\t}\n\n\t/**\n\t * Returns the value of the HTTP method of the request to which the DPoP Proof\n\t * {@link Jwt} is attached.\n\t * @return the value of the HTTP method of the request to which the DPoP Proof\n\t * {@link Jwt} is attached\n\t */\n\tpublic String getMethod() {\n\t\treturn this.method;\n\t}\n\n\t/**\n\t * Returns the value of the HTTP target URI of the request to which the DPoP Proof\n\t * {@link Jwt} is attached, without query and fragment parts.\n\t * @return the value of the HTTP target URI of the request to which the DPoP Proof\n\t * {@link Jwt} is attached\n\t */\n\tpublic String getTargetUri() {\n\t\treturn this.targetUri;\n\t}\n\n\t/**\n\t * Returns the access token if the request is a Protected Resource request.\n\t * @param <T> the type of the access token\n\t * @return the access token if the request is a Protected Resource request or\n\t * {@code null}\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic @Nullable <T extends OAuth2Token> T getAccessToken() {\n\t\treturn (T) this.accessToken;\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the DPoP Proof {@link Jwt}.\n\t * @param dPoPProof the DPoP Proof {@link Jwt}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder withDPoPProof(String dPoPProof) {\n\t\treturn new Builder(dPoPProof);\n\t}\n\n\t/**\n\t * A builder for {@link DPoPProofContext}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate String dPoPProof;\n\n\t\tprivate @Nullable String method;\n\n\t\tprivate @Nullable String targetUri;\n\n\t\tprivate @Nullable OAuth2Token accessToken;\n\n\t\tprivate Builder(String dPoPProof) {\n\t\t\tAssert.hasText(dPoPProof, \"dPoPProof cannot be empty\");\n\t\t\tthis.dPoPProof = dPoPProof;\n\t\t}\n\n\t\t/**\n\t\t * Sets the value of the HTTP method of the request to which the DPoP Proof\n\t\t * {@link Jwt} is attached.\n\t\t * @param method the value of the HTTP method of the request to which the DPoP\n\t\t * Proof {@link Jwt} is attached\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder method(String method) {\n\t\t\tthis.method = method;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the value of the HTTP target URI of the request to which the DPoP Proof\n\t\t * {@link Jwt} is attached, without query and fragment parts.\n\t\t * @param targetUri the value of the HTTP target URI of the request to which the\n\t\t * DPoP Proof {@link Jwt} is attached\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder targetUri(String targetUri) {\n\t\t\tthis.targetUri = targetUri;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the access token if the request is a Protected Resource request.\n\t\t * @param accessToken the access token if the request is a Protected Resource\n\t\t * request\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder accessToken(@Nullable OAuth2Token accessToken) {\n\t\t\tthis.accessToken = accessToken;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link DPoPProofContext}.\n\t\t * @return a {@link DPoPProofContext}\n\t\t */\n\t\tpublic DPoPProofContext build() {\n\t\t\tAssert.hasText(this.method, \"method cannot be empty\");\n\t\t\tAssert.hasText(this.targetUri, \"targetUri cannot be empty\");\n\t\t\tvalidate();\n\t\t\treturn new DPoPProofContext(this.dPoPProof, this.method, this.targetUri, this.accessToken);\n\t\t}\n\n\t\tprivate void validate() {\n\t\t\tif (!\"GET\".equals(this.method) && !\"HEAD\".equals(this.method) && !\"POST\".equals(this.method)\n\t\t\t\t\t&& !\"PUT\".equals(this.method) && !\"PATCH\".equals(this.method) && !\"DELETE\".equals(this.method)\n\t\t\t\t\t&& !\"OPTIONS\".equals(this.method) && !\"TRACE\".equals(this.method)) {\n\t\t\t\tthrow new IllegalArgumentException(\"method is invalid\");\n\t\t\t}\n\t\t\tURI uri;\n\t\t\ttry {\n\t\t\t\turi = new URI(this.targetUri);\n\t\t\t\turi.toURL();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(\"targetUri must be a valid URL\", ex);\n\t\t\t}\n\t\t\tif (uri.getQuery() != null || uri.getFragment() != null) {\n\t\t\t\tthrow new IllegalArgumentException(\"targetUri cannot contain query or fragment parts\");\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.JOSEObjectType;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;\nimport com.nimbusds.jose.proc.JOSEObjectTypeVerifier;\nimport com.nimbusds.jose.proc.JWSKeySelector;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jwt.proc.ConfigurableJWTProcessor;\nimport com.nimbusds.jwt.proc.DefaultJWTProcessor;\n\nimport org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder} for the\n * specified {@link DPoPProofContext} and is used for authenticating a DPoP Proof\n * {@link Jwt}.\n *\n * @author Joe Grandja\n * @since 6.5\n * @see JwtDecoderFactory\n * @see DPoPProofContext\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc9449\">RFC 9449\n * OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>\n */\npublic final class DPoPProofJwtDecoderFactory implements JwtDecoderFactory<DPoPProofContext> {\n\n\t/**\n\t * The default {@code OAuth2TokenValidator<Jwt>} factory that validates the\n\t * {@code htm}, {@code htu}, {@code jti} and {@code iat} claims of the DPoP Proof\n\t * {@link Jwt}.\n\t */\n\tpublic static final Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> DEFAULT_JWT_VALIDATOR_FACTORY = defaultJwtValidatorFactory();\n\n\tprivate static final JOSEObjectTypeVerifier<SecurityContext> DPOP_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\tnew JOSEObjectType(\"dpop+jwt\"));\n\n\tprivate Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = DEFAULT_JWT_VALIDATOR_FACTORY;\n\n\t@Override\n\tpublic JwtDecoder createDecoder(DPoPProofContext dPoPProofContext) {\n\t\tAssert.notNull(dPoPProofContext, \"dPoPProofContext cannot be null\");\n\t\tNimbusJwtDecoder jwtDecoder = buildDecoder();\n\t\tjwtDecoder.setJwtValidator(this.jwtValidatorFactory.apply(dPoPProofContext));\n\t\treturn jwtDecoder;\n\t}\n\n\t/**\n\t * Sets the factory that provides an {@link OAuth2TokenValidator} for the specified\n\t * {@link DPoPProofContext} and is used by the {@link JwtDecoder}. The default\n\t * {@code OAuth2TokenValidator<Jwt>} factory is\n\t * {@link #DEFAULT_JWT_VALIDATOR_FACTORY}.\n\t * @param jwtValidatorFactory the factory that provides an\n\t * {@link OAuth2TokenValidator} for the specified {@link DPoPProofContext}\n\t */\n\tpublic void setJwtValidatorFactory(Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {\n\t\tAssert.notNull(jwtValidatorFactory, \"jwtValidatorFactory cannot be null\");\n\t\tthis.jwtValidatorFactory = jwtValidatorFactory;\n\t}\n\n\tprivate static NimbusJwtDecoder buildDecoder() {\n\t\tConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\tjwtProcessor.setJWSTypeVerifier(DPOP_TYPE_VERIFIER);\n\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector());\n\t\t// Override the default Nimbus claims set verifier and use jwtValidatorFactory for\n\t\t// claims validation\n\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t});\n\t\treturn new NimbusJwtDecoder(jwtProcessor);\n\t}\n\n\tprivate static JWSKeySelector<SecurityContext> jwsKeySelector() {\n\t\treturn (header, context) -> {\n\t\t\tJWSAlgorithm algorithm = header.getAlgorithm();\n\t\t\tif (!JWSAlgorithm.Family.RSA.contains(algorithm) && !JWSAlgorithm.Family.EC.contains(algorithm)) {\n\t\t\t\tthrow new BadJwtException(\"Unsupported alg parameter in JWS Header: \" + algorithm.getName());\n\t\t\t}\n\n\t\t\tJWK jwk = header.getJWK();\n\t\t\tif (jwk == null) {\n\t\t\t\tthrow new BadJwtException(\"Missing jwk parameter in JWS Header.\");\n\t\t\t}\n\t\t\tif (jwk.isPrivate()) {\n\t\t\t\tthrow new BadJwtException(\"Invalid jwk parameter in JWS Header.\");\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (JWSAlgorithm.Family.RSA.contains(algorithm) && jwk instanceof RSAKey rsaKey) {\n\t\t\t\t\treturn Collections.singletonList(rsaKey.toRSAPublicKey());\n\t\t\t\t}\n\t\t\t\telse if (JWSAlgorithm.Family.EC.contains(algorithm) && jwk instanceof ECKey ecKey) {\n\t\t\t\t\treturn Collections.singletonList(ecKey.toECPublicKey());\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (JOSEException ex) {\n\t\t\t\tthrow new BadJwtException(\"Invalid jwk parameter in JWS Header.\");\n\t\t\t}\n\n\t\t\tthrow new BadJwtException(\"Invalid alg / jwk parameter in JWS Header: alg=\" + algorithm.getName()\n\t\t\t\t\t+ \", jwk.kty=\" + jwk.getKeyType().getValue());\n\t\t};\n\t}\n\n\tprivate static Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> defaultJwtValidatorFactory() {\n\t\treturn (context) -> new DelegatingOAuth2TokenValidator<>(\n\t\t\t\tnew JwtClaimValidator<>(\"htm\", context.getMethod()::equals),\n\t\t\t\tnew JwtClaimValidator<>(\"htu\", context.getTargetUri()::equals), new JtiClaimValidator(),\n\t\t\t\tnew JwtIssuedAtValidator(true));\n\t}\n\n\tprivate static final class JtiClaimValidator implements OAuth2TokenValidator<Jwt> {\n\n\t\tprivate static final Map<String, Long> JTI_CACHE = Collections.synchronizedMap(new JtiCache());\n\n\t\t@Override\n\t\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\t\tAssert.notNull(jwt, \"DPoP proof jwt cannot be null\");\n\t\t\tString jti = jwt.getId();\n\t\t\tif (!StringUtils.hasText(jti)) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"jti claim is required.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\n\t\t\t// Enforce single-use to protect against DPoP proof replay\n\t\t\tString jtiHash;\n\t\t\ttry {\n\t\t\t\tjtiHash = computeSHA256(jti);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"jti claim is invalid.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\t\t\tInstant expiry = Instant.now().plus(1, ChronoUnit.HOURS);\n\t\t\tif ((JTI_CACHE.putIfAbsent(jtiHash, expiry.toEpochMilli())) != null) {\n\t\t\t\t// Already used\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"jti claim is invalid.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t}\n\n\t\tprivate static OAuth2Error createOAuth2Error(String reason) {\n\t\t\treturn new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF, reason, null);\n\t\t}\n\n\t\tprivate static String computeSHA256(String value) throws Exception {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.UTF_8));\n\t\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t\t}\n\n\t\tprivate static final class JtiCache extends LinkedHashMap<String, Long> {\n\n\t\t\tprivate static final int MAX_SIZE = 1000;\n\n\t\t\t@Override\n\t\t\tprotected boolean removeEldestEntry(Map.Entry<String, Long> eldest) {\n\t\t\t\tif (size() > MAX_SIZE) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tInstant expiry = Instant.ofEpochMilli(eldest.getValue());\n\t\t\t\treturn Instant.now().isAfter(expiry);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JWKS.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.Date;\nimport java.util.Set;\n\nimport javax.crypto.SecretKey;\n\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.crypto.impl.ECDSA;\nimport com.nimbusds.jose.jwk.Curve;\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.KeyOperation;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.OctetSequenceKey;\nimport com.nimbusds.jose.jwk.RSAKey;\n\nfinal class JWKS {\n\n\tprivate JWKS() {\n\n\t}\n\n\tstatic OctetSequenceKey.Builder signing(SecretKey key) throws JOSEException {\n\t\tDate issued = new Date();\n\t\treturn new OctetSequenceKey.Builder(key).keyOperations(Set.of(KeyOperation.SIGN))\n\t\t\t.keyUse(KeyUse.SIGNATURE)\n\t\t\t.algorithm(JWSAlgorithm.HS256)\n\t\t\t.keyIDFromThumbprint()\n\t\t\t.issueTime(issued)\n\t\t\t.notBeforeTime(issued);\n\t}\n\n\tstatic ECKey.Builder signingWithEc(ECPublicKey pub, ECPrivateKey key) throws JOSEException {\n\t\tDate issued = new Date();\n\t\tCurve curve = Curve.forECParameterSpec(pub.getParams());\n\t\tJWSAlgorithm algorithm = computeAlgorithm(curve);\n\t\treturn new ECKey.Builder(curve, pub).privateKey(key)\n\t\t\t.keyOperations(Set.of(KeyOperation.SIGN))\n\t\t\t.keyUse(KeyUse.SIGNATURE)\n\t\t\t.algorithm(algorithm)\n\t\t\t.keyIDFromThumbprint()\n\t\t\t.issueTime(issued)\n\t\t\t.notBeforeTime(issued);\n\t}\n\n\tprivate static JWSAlgorithm computeAlgorithm(Curve curve) {\n\t\ttry {\n\t\t\treturn ECDSA.resolveAlgorithm(curve);\n\t\t}\n\t\tcatch (JOSEException ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tstatic RSAKey.Builder signingWithRsa(RSAPublicKey pub, RSAPrivateKey key) throws JOSEException {\n\t\tDate issued = new Date();\n\t\treturn new RSAKey.Builder(pub).privateKey(key)\n\t\t\t.keyUse(KeyUse.SIGNATURE)\n\t\t\t.keyOperations(Set.of(KeyOperation.SIGN))\n\t\t\t.algorithm(JWSAlgorithm.RS256)\n\t\t\t.keyIDFromThumbprint()\n\t\t\t.issueTime(issued)\n\t\t\t.notBeforeTime(issued);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JoseHeader.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.jose.JwaAlgorithm;\nimport org.springframework.util.Assert;\n\n/**\n * The JOSE header is a JSON object representing the header parameters of a JSON Web\n * Token, whether the JWT is a JWS or JWE, that describe the cryptographic operations\n * applied to the JWT and optionally, additional properties of the JWT.\n *\n * @author Anoop Garlapati\n * @author Joe Grandja\n * @since 5.6\n * @see Jwt\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519#section-5\">JWT JOSE\n * Header</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515#section-4\">JWS JOSE\n * Header</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516#section-4\">JWE JOSE\n * Header</a>\n */\nclass JoseHeader {\n\n\tprivate final Map<String, Object> headers;\n\n\tprotected JoseHeader(Map<String, Object> headers) {\n\t\tAssert.notEmpty(headers, \"headers cannot be empty\");\n\t\tthis.headers = Collections.unmodifiableMap(new HashMap<>(headers));\n\t}\n\n\t/**\n\t * Returns the {@link JwaAlgorithm JWA algorithm} used to digitally sign the JWS or\n\t * encrypt the JWE.\n\t * @return the {@link JwaAlgorithm}\n\t */\n\tpublic <T extends JwaAlgorithm> T getAlgorithm() {\n\t\tT algorithm = getHeader(JoseHeaderNames.ALG);\n\t\tAssert.notNull(algorithm, \"algorithm cannot be null\");\n\t\treturn algorithm;\n\t}\n\n\t/**\n\t * Returns the JWK Set URL that refers to the resource of a set of JSON-encoded public\n\t * keys, one of which corresponds to the key used to digitally sign the JWS or encrypt\n\t * the JWE.\n\t * @return the JWK Set URL, or {@code null} if the header is absent\n\t */\n\tpublic @Nullable URL getJwkSetUrl() {\n\t\treturn getHeader(JoseHeaderNames.JKU);\n\t}\n\n\t/**\n\t * Returns the JSON Web Key which is the public key that corresponds to the key used\n\t * to digitally sign the JWS or encrypt the JWE.\n\t * @return the JSON Web Key, or {@code null} if the header is absent\n\t */\n\tpublic @Nullable Map<String, Object> getJwk() {\n\t\treturn getHeader(JoseHeaderNames.JWK);\n\t}\n\n\t/**\n\t * Returns the key ID that is a hint indicating which key was used to secure the JWS\n\t * or JWE.\n\t * @return the key ID, or {@code null} if the header is absent\n\t */\n\tpublic @Nullable String getKeyId() {\n\t\treturn getHeader(JoseHeaderNames.KID);\n\t}\n\n\t/**\n\t * Returns the X.509 URL that refers to the resource for the X.509 public key\n\t * certificate or certificate chain corresponding to the key used to digitally sign\n\t * the JWS or encrypt the JWE.\n\t * @return the X.509 URL, or {@code null} if the header is absent\n\t */\n\tpublic @Nullable URL getX509Url() {\n\t\treturn getHeader(JoseHeaderNames.X5U);\n\t}\n\n\t/**\n\t * Returns the X.509 certificate chain that contains the X.509 public key certificate\n\t * or certificate chain corresponding to the key used to digitally sign the JWS or\n\t * encrypt the JWE. The certificate or certificate chain is represented as a\n\t * {@code List} of certificate value {@code String}s. Each {@code String} in the\n\t * {@code List} is a Base64-encoded DER PKIX certificate value.\n\t * @return the X.509 certificate chain, or {@code null} if the header is absent\n\t */\n\tpublic @Nullable List<String> getX509CertificateChain() {\n\t\treturn getHeader(JoseHeaderNames.X5C);\n\t}\n\n\t/**\n\t * Returns the X.509 certificate SHA-1 thumbprint that is a base64url-encoded SHA-1\n\t * thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate\n\t * corresponding to the key used to digitally sign the JWS or encrypt the JWE.\n\t * @return the X.509 certificate SHA-1 thumbprint, or {@code null} if the header is\n\t * absent\n\t * @deprecated The SHA-1 algorithm has been proven to be vulnerable to collision\n\t * attacks and should not be used. See the <a target=\"_blank\" href=\n\t * \"https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html\">Google\n\t * Security Blog</a> for more info.\n\t * @see <a target=\"_blank\" href=\n\t * \"https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html\">Announcing\n\t * the first SHA1 collision</a>\n\t */\n\t@Deprecated\n\tpublic @Nullable String getX509SHA1Thumbprint() {\n\t\treturn getHeader(JoseHeaderNames.X5T);\n\t}\n\n\t/**\n\t * Returns the X.509 certificate SHA-256 thumbprint that is a base64url-encoded\n\t * SHA-256 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate\n\t * corresponding to the key used to digitally sign the JWS or encrypt the JWE.\n\t * @return the X.509 certificate SHA-256 thumbprint, or {@code null} if the header is\n\t * absent\n\t */\n\tpublic @Nullable String getX509SHA256Thumbprint() {\n\t\treturn getHeader(JoseHeaderNames.X5T_S256);\n\t}\n\n\t/**\n\t * Returns the type header that declares the media type of the JWS/JWE.\n\t * @return the type header, or {@code null} if the header is absent\n\t */\n\tpublic @Nullable String getType() {\n\t\treturn getHeader(JoseHeaderNames.TYP);\n\t}\n\n\t/**\n\t * Returns the content type header that declares the media type of the secured content\n\t * (the payload).\n\t * @return the content type header, or {@code null} if the header is absent\n\t */\n\tpublic @Nullable String getContentType() {\n\t\treturn getHeader(JoseHeaderNames.CTY);\n\t}\n\n\t/**\n\t * Returns the critical headers that indicates which extensions to the JWS/JWE/JWA\n\t * specifications are being used that MUST be understood and processed.\n\t * @return the critical headers, or {@code null} if the header is absent\n\t */\n\tpublic @Nullable Set<String> getCritical() {\n\t\treturn getHeader(JoseHeaderNames.CRIT);\n\t}\n\n\t/**\n\t * Returns the headers.\n\t * @return the headers\n\t */\n\tpublic Map<String, Object> getHeaders() {\n\t\treturn this.headers;\n\t}\n\n\t/**\n\t * Returns the header value.\n\t * @param name the header name\n\t * @param <T> the type of the header value\n\t * @return the header value, or {@code null} if the header is absent\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> @Nullable T getHeader(String name) {\n\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\treturn (T) getHeaders().get(name);\n\t}\n\n\t/**\n\t * A builder for subclasses of {@link JoseHeader}.\n\t */\n\tabstract static class AbstractBuilder<T extends JoseHeader, B extends AbstractBuilder<T, B>> {\n\n\t\tprivate final Map<String, Object> headers = new HashMap<>();\n\n\t\tprotected AbstractBuilder() {\n\t\t}\n\n\t\tprotected Map<String, Object> getHeaders() {\n\t\t\treturn this.headers;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected final B getThis() {\n\t\t\treturn (B) this; // avoid unchecked casts in subclasses by using \"getThis()\"\n\t\t\t\t\t\t\t\t// instead of \"(B) this\"\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link JwaAlgorithm JWA algorithm} used to digitally sign the JWS or\n\t\t * encrypt the JWE.\n\t\t * @param jwaAlgorithm the {@link JwaAlgorithm}\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B algorithm(JwaAlgorithm jwaAlgorithm) {\n\t\t\treturn header(JoseHeaderNames.ALG, jwaAlgorithm);\n\t\t}\n\n\t\t/**\n\t\t * Sets the JWK Set URL that refers to the resource of a set of JSON-encoded\n\t\t * public keys, one of which corresponds to the key used to digitally sign the JWS\n\t\t * or encrypt the JWE.\n\t\t * @param jwkSetUrl the JWK Set URL\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B jwkSetUrl(String jwkSetUrl) {\n\t\t\treturn header(JoseHeaderNames.JKU, convertAsURL(JoseHeaderNames.JKU, jwkSetUrl));\n\t\t}\n\n\t\t/**\n\t\t * Sets the JSON Web Key which is the public key that corresponds to the key used\n\t\t * to digitally sign the JWS or encrypt the JWE.\n\t\t * @param jwk the JSON Web Key\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B jwk(Map<String, Object> jwk) {\n\t\t\treturn header(JoseHeaderNames.JWK, jwk);\n\t\t}\n\n\t\t/**\n\t\t * Sets the key ID that is a hint indicating which key was used to secure the JWS\n\t\t * or JWE.\n\t\t * @param keyId the key ID\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B keyId(String keyId) {\n\t\t\treturn header(JoseHeaderNames.KID, keyId);\n\t\t}\n\n\t\t/**\n\t\t * Sets the X.509 URL that refers to the resource for the X.509 public key\n\t\t * certificate or certificate chain corresponding to the key used to digitally\n\t\t * sign the JWS or encrypt the JWE.\n\t\t * @param x509Url the X.509 URL\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B x509Url(String x509Url) {\n\t\t\treturn header(JoseHeaderNames.X5U, convertAsURL(JoseHeaderNames.X5U, x509Url));\n\t\t}\n\n\t\t/**\n\t\t * Sets the X.509 certificate chain that contains the X.509 public key certificate\n\t\t * or certificate chain corresponding to the key used to digitally sign the JWS or\n\t\t * encrypt the JWE. The certificate or certificate chain is represented as a\n\t\t * {@code List} of certificate value {@code String}s. Each {@code String} in the\n\t\t * {@code List} is a Base64-encoded DER PKIX certificate value.\n\t\t * @param x509CertificateChain the X.509 certificate chain\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B x509CertificateChain(List<String> x509CertificateChain) {\n\t\t\treturn header(JoseHeaderNames.X5C, x509CertificateChain);\n\t\t}\n\n\t\t/**\n\t\t * Sets the X.509 certificate SHA-1 thumbprint that is a base64url-encoded SHA-1\n\t\t * thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate\n\t\t * corresponding to the key used to digitally sign the JWS or encrypt the JWE.\n\t\t * @param x509SHA1Thumbprint the X.509 certificate SHA-1 thumbprint\n\t\t * @return the {@link AbstractBuilder}\n\t\t * @deprecated The SHA-1 algorithm has been proven to be vulnerable to collision\n\t\t * attacks and should not be used. See the <a target=\"_blank\" href=\n\t\t * \"https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html\">Google\n\t\t * Security Blog</a> for more info.\n\t\t * @see <a target=\"_blank\" href=\n\t\t * \"https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html\">Announcing\n\t\t * the first SHA1 collision</a>\n\t\t */\n\t\t@Deprecated\n\t\tpublic B x509SHA1Thumbprint(String x509SHA1Thumbprint) {\n\t\t\treturn header(JoseHeaderNames.X5T, x509SHA1Thumbprint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the X.509 certificate SHA-256 thumbprint that is a base64url-encoded\n\t\t * SHA-256 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate\n\t\t * corresponding to the key used to digitally sign the JWS or encrypt the JWE.\n\t\t * @param x509SHA256Thumbprint the X.509 certificate SHA-256 thumbprint\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B x509SHA256Thumbprint(String x509SHA256Thumbprint) {\n\t\t\treturn header(JoseHeaderNames.X5T_S256, x509SHA256Thumbprint);\n\t\t}\n\n\t\t/**\n\t\t * Sets the type header that declares the media type of the JWS/JWE.\n\t\t * @param type the type header\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B type(String type) {\n\t\t\treturn header(JoseHeaderNames.TYP, type);\n\t\t}\n\n\t\t/**\n\t\t * Sets the content type header that declares the media type of the secured\n\t\t * content (the payload).\n\t\t * @param contentType the content type header\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B contentType(String contentType) {\n\t\t\treturn header(JoseHeaderNames.CTY, contentType);\n\t\t}\n\n\t\t/**\n\t\t * Sets the critical header that indicates which extensions to the JWS/JWE/JWA\n\t\t * specifications are being used that MUST be understood and processed.\n\t\t * @param name the critical header name\n\t\t * @param value the critical header value\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic B criticalHeader(String name, Object value) {\n\t\t\theader(name, value);\n\t\t\tgetHeaders().computeIfAbsent(JoseHeaderNames.CRIT, (k) -> new HashSet<String>());\n\t\t\t((Set<String>) getHeaders().get(JoseHeaderNames.CRIT)).add(name);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Sets the header.\n\t\t * @param name the header name\n\t\t * @param value the header value\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B header(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.headers.put(name, value);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} to be provided access to the headers allowing the ability to\n\t\t * add, replace, or remove.\n\t\t * @param headersConsumer a {@code Consumer} of the headers\n\t\t * @return the {@link AbstractBuilder}\n\t\t */\n\t\tpublic B headers(Consumer<Map<String, Object>> headersConsumer) {\n\t\t\theadersConsumer.accept(this.headers);\n\t\t\treturn getThis();\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link JoseHeader}.\n\t\t * @return a {@link JoseHeader}\n\t\t */\n\t\tpublic abstract T build();\n\n\t\tprivate static URL convertAsURL(String header, String value) {\n\t\t\tURL convertedValue = ClaimConversionService.getSharedInstance().convert(value, URL.class);\n\t\t\tAssert.notNull(convertedValue,\n\t\t\t\t\t() -> \"Unable to convert header '\" + header + \"' of type '\" + value.getClass() + \"' to URL.\");\n\t\t\treturn convertedValue;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JoseHeaderNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\n/**\n * The Registered Header Parameter Names defined by the JSON Web Token (JWT), JSON Web\n * Signature (JWS) and JSON Web Encryption (JWE) specifications that may be contained in\n * the JOSE Header of a JWT.\n *\n * @author Anoop Garlapati\n * @author Joe Grandja\n * @since 5.6\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519#section-5\">JWT JOSE\n * Header</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515#section-4\">JWS JOSE\n * Header</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516#section-4\">JWE JOSE\n * Header</a>\n */\npublic final class JoseHeaderNames {\n\n\t/**\n\t * {@code alg} - the algorithm header identifies the cryptographic algorithm used to\n\t * secure a JWS or JWE\n\t */\n\tpublic static final String ALG = \"alg\";\n\n\t/**\n\t * {@code jku} - the JWK Set URL header is a URI that refers to a resource for a set\n\t * of JSON-encoded public keys, one of which corresponds to the key used to digitally\n\t * sign a JWS or encrypt a JWE\n\t */\n\tpublic static final String JKU = \"jku\";\n\n\t/**\n\t * {@code jwk} - the JSON Web Key header is the public key that corresponds to the key\n\t * used to digitally sign a JWS or encrypt a JWE\n\t */\n\tpublic static final String JWK = \"jwk\";\n\n\t/**\n\t * {@code kid} - the key ID header is a hint indicating which key was used to secure a\n\t * JWS or JWE\n\t */\n\tpublic static final String KID = \"kid\";\n\n\t/**\n\t * {@code x5u} - the X.509 URL header is a URI that refers to a resource for the X.509\n\t * public key certificate or certificate chain corresponding to the key used to\n\t * digitally sign a JWS or encrypt a JWE\n\t */\n\tpublic static final String X5U = \"x5u\";\n\n\t/**\n\t * {@code x5c} - the X.509 certificate chain header contains the X.509 public key\n\t * certificate or certificate chain corresponding to the key used to digitally sign a\n\t * JWS or encrypt a JWE\n\t */\n\tpublic static final String X5C = \"x5c\";\n\n\t/**\n\t * {@code x5t} - the X.509 certificate SHA-1 thumbprint header is a base64url-encoded\n\t * SHA-1 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate\n\t * corresponding to the key used to digitally sign a JWS or encrypt a JWE\n\t * @deprecated The SHA-1 algorithm has been proven to be vulnerable to collision\n\t * attacks and should not be used. See the <a target=\"_blank\" href=\n\t * \"https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html\">Google\n\t * Security Blog</a> for more info.\n\t * @see <a target=\"_blank\" href=\n\t * \"https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html\">Announcing\n\t * the first SHA1 collision</a>\n\t */\n\t@Deprecated\n\tpublic static final String X5T = \"x5t\";\n\n\t/**\n\t * {@code x5t#S256} - the X.509 certificate SHA-256 thumbprint header is a\n\t * base64url-encoded SHA-256 thumbprint (a.k.a. digest) of the DER encoding of the\n\t * X.509 certificate corresponding to the key used to digitally sign a JWS or encrypt\n\t * a JWE\n\t */\n\tpublic static final String X5T_S256 = \"x5t#S256\";\n\n\t/**\n\t * {@code typ} - the type header is used by JWS/JWE applications to declare the media\n\t * type of a JWS/JWE\n\t */\n\tpublic static final String TYP = \"typ\";\n\n\t/**\n\t * {@code cty} - the content type header is used by JWS/JWE applications to declare\n\t * the media type of the secured content (the payload)\n\t */\n\tpublic static final String CTY = \"cty\";\n\n\t/**\n\t * {@code crit} - the critical header indicates that extensions to the JWS/JWE/JWA\n\t * specifications are being used that MUST be understood and processed\n\t */\n\tpublic static final String CRIT = \"crit\";\n\n\tprivate JoseHeaderNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwsHeader.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.util.Assert;\n\n/**\n * The JSON Web Signature (JWS) header is a JSON object representing the header parameters\n * of a JSON Web Token, that describe the cryptographic operations used to digitally sign\n * or create a MAC of the contents of the JWS Protected Header and JWS Payload.\n *\n * @author Joe Grandja\n * @since 5.6\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515#section-4\">JWS JOSE\n * Header</a>\n */\npublic final class JwsHeader extends JoseHeader {\n\n\tprivate JwsHeader(Map<String, Object> headers) {\n\t\tsuper(headers);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic JwsAlgorithm getAlgorithm() {\n\t\treturn super.getAlgorithm();\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided {@link JwsAlgorithm}.\n\t * @param jwsAlgorithm the {@link JwsAlgorithm}\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder with(JwsAlgorithm jwsAlgorithm) {\n\t\treturn new Builder(jwsAlgorithm);\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided {@code headers}.\n\t * @param headers the headers\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder from(JwsHeader headers) {\n\t\treturn new Builder(headers);\n\t}\n\n\t/**\n\t * A builder for {@link JwsHeader}.\n\t */\n\tpublic static final class Builder extends AbstractBuilder<JwsHeader, Builder> {\n\n\t\tprivate Builder(JwsAlgorithm jwsAlgorithm) {\n\t\t\tAssert.notNull(jwsAlgorithm, \"jwsAlgorithm cannot be null\");\n\t\t\talgorithm(jwsAlgorithm);\n\t\t}\n\n\t\tprivate Builder(JwsHeader headers) {\n\t\t\tAssert.notNull(headers, \"headers cannot be null\");\n\t\t\tgetHeaders().putAll(headers.getHeaders());\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link JwsHeader}.\n\t\t * @return a {@link JwsHeader}\n\t\t */\n\t\t@Override\n\t\tpublic JwsHeader build() {\n\t\t\treturn new JwsHeader(getHeaders());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/Jwt.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.io.Serial;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.AbstractOAuth2Token;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AbstractOAuth2Token} representing a JSON Web Token\n * (JWT).\n *\n * <p>\n * JWTs represent a set of &quot;claims&quot; as a JSON object that may be encoded in a\n * JSON Web Signature (JWS) and/or JSON Web Encryption (JWE) structure. The JSON object,\n * also known as the JWT Claims Set, consists of one or more claim name/value pairs. The\n * claim name is a {@code String} and the claim value is an arbitrary JSON object.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see AbstractOAuth2Token\n * @see JwtClaimAccessor\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519\">JSON Web Token\n * (JWT)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516\">JSON Web Encryption\n * (JWE)</a>\n */\npublic class Jwt extends AbstractOAuth2Token implements JwtClaimAccessor {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 4872843562494199108L;\n\n\tprivate final Map<String, Object> headers;\n\n\tprivate final Map<String, Object> claims;\n\n\t/**\n\t * Constructs a {@code Jwt} using the provided parameters.\n\t * @param tokenValue the token value\n\t * @param issuedAt the time at which the JWT was issued, may be {@code null}\n\t * @param expiresAt the expiration time on or after which the JWT MUST NOT be\n\t * accepted, may be {@code null}\n\t * @param headers the JOSE header(s)\n\t * @param claims the JWT Claims Set\n\t *\n\t */\n\tpublic Jwt(String tokenValue, @Nullable Instant issuedAt, @Nullable Instant expiresAt, Map<String, Object> headers,\n\t\t\tMap<String, Object> claims) {\n\t\tsuper(tokenValue, issuedAt, expiresAt);\n\t\tAssert.notEmpty(headers, \"headers cannot be empty\");\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\tthis.headers = Collections.unmodifiableMap(new LinkedHashMap<>(headers));\n\t\tthis.claims = Collections.unmodifiableMap(new LinkedHashMap<>(claims));\n\t}\n\n\t/**\n\t * Returns the JOSE header(s).\n\t * @return a {@code Map} of the JOSE header(s)\n\t */\n\tpublic Map<String, Object> getHeaders() {\n\t\treturn this.headers;\n\t}\n\n\t/**\n\t * Returns the JWT Claims Set.\n\t * @return a {@code Map} of the JWT Claims Set\n\t */\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * Return a {@link Jwt.Builder}\n\t * @return A {@link Jwt.Builder}\n\t */\n\tpublic static Builder withTokenValue(String tokenValue) {\n\t\treturn new Builder(tokenValue);\n\t}\n\n\t/**\n\t * Helps configure a {@link Jwt}\n\t *\n\t * @author Jérôme Wacongne &lt;ch4mp&#64;c4-soft.com&gt;\n\t * @author Josh Cummings\n\t * @since 5.2\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate String tokenValue;\n\n\t\tprivate final Map<String, Object> claims = new LinkedHashMap<>();\n\n\t\tprivate final Map<String, Object> headers = new LinkedHashMap<>();\n\n\t\tprivate Builder(String tokenValue) {\n\t\t\tthis.tokenValue = tokenValue;\n\t\t}\n\n\t\t/**\n\t\t * Use this token value in the resulting {@link Jwt}\n\t\t * @param tokenValue The token value to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder tokenValue(String tokenValue) {\n\t\t\tthis.tokenValue = tokenValue;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this claim in the resulting {@link Jwt}\n\t\t * @param name The claim name\n\t\t * @param value The claim value\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claim(String name, Object value) {\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #claim(String, Object)} declared so far with\n\t\t * the possibility to add, replace, or remove.\n\t\t * @param claimsConsumer the consumer\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this header in the resulting {@link Jwt}\n\t\t * @param name The header name\n\t\t * @param value The header value\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder header(String name, Object value) {\n\t\t\tthis.headers.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #header(String, Object)} declared so far with\n\t\t * the possibility to add, replace, or remove.\n\t\t * @param headersConsumer the consumer\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder headers(Consumer<Map<String, Object>> headersConsumer) {\n\t\t\theadersConsumer.accept(this.headers);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this audience in the resulting {@link Jwt}\n\t\t * @param audience The audience(s) to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder audience(Collection<String> audience) {\n\t\t\treturn claim(JwtClaimNames.AUD, audience);\n\t\t}\n\n\t\t/**\n\t\t * Use this expiration in the resulting {@link Jwt}\n\t\t * @param expiresAt The expiration to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder expiresAt(Instant expiresAt) {\n\t\t\tthis.claim(JwtClaimNames.EXP, expiresAt);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this identifier in the resulting {@link Jwt}\n\t\t * @param jti The identifier to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder jti(String jti) {\n\t\t\tthis.claim(JwtClaimNames.JTI, jti);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this issued-at timestamp in the resulting {@link Jwt}\n\t\t * @param issuedAt The issued-at timestamp to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder issuedAt(Instant issuedAt) {\n\t\t\tthis.claim(JwtClaimNames.IAT, issuedAt);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this issuer in the resulting {@link Jwt}\n\t\t * @param issuer The issuer to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder issuer(String issuer) {\n\t\t\tthis.claim(JwtClaimNames.ISS, issuer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this not-before timestamp in the resulting {@link Jwt}\n\t\t * @param notBefore The not-before timestamp to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder notBefore(Instant notBefore) {\n\t\t\tthis.claim(JwtClaimNames.NBF, notBefore);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this subject in the resulting {@link Jwt}\n\t\t * @param subject The subject to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder subject(String subject) {\n\t\t\tthis.claim(JwtClaimNames.SUB, subject);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the {@link Jwt}\n\t\t * @return The constructed {@link Jwt}\n\t\t */\n\t\tpublic Jwt build() {\n\t\t\tInstant iat = toInstant(this.claims.get(JwtClaimNames.IAT));\n\t\t\tInstant exp = toInstant(this.claims.get(JwtClaimNames.EXP));\n\t\t\treturn new Jwt(this.tokenValue, iat, exp, this.headers, this.claims);\n\t\t}\n\n\t\tprivate @Nullable Instant toInstant(@Nullable Object timestamp) {\n\t\t\tif (timestamp != null) {\n\t\t\t\tAssert.isInstanceOf(Instant.class, timestamp, \"timestamps must be of type Instant\");\n\t\t\t}\n\t\t\treturn (Instant) timestamp;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtAudienceValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Collection;\n\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\n\n/**\n * Validates that the \"aud\" claim in a {@link Jwt} matches a configured value.\n *\n * @author Vedran Pavic\n * @since 6.5\n */\npublic final class JwtAudienceValidator implements OAuth2TokenValidator<Jwt> {\n\n\tprivate final JwtClaimValidator<Collection<String>> validator;\n\n\t/**\n\t * Constructs a {@link JwtAudienceValidator} using the provided parameters\n\t * @param audience - The audience that each {@link Jwt} should have.\n\t */\n\tpublic JwtAudienceValidator(String audience) {\n\t\tAssert.notNull(audience, \"audience cannot be null\");\n\t\tthis.validator = new JwtClaimValidator<>(JwtClaimNames.AUD,\n\t\t\t\t(claimValue) -> (claimValue != null) && claimValue.contains(audience));\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt token) {\n\t\tAssert.notNull(token, \"token cannot be null\");\n\t\treturn this.validator.validate(token);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\n\n/**\n * A {@link ClaimAccessor} for the &quot;claims&quot; that may be contained in the JSON\n * object JWT Claims Set of a JSON Web Token (JWT).\n *\n * @author Joe Grandja\n * @since 5.0\n * @see ClaimAccessor\n * @see JwtClaimNames\n * @see Jwt\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc7519#section-4.1\">Registered Claim Names</a>\n */\npublic interface JwtClaimAccessor extends ClaimAccessor {\n\n\t/**\n\t * Returns the Issuer {@code (iss)} claim which identifies the principal that issued\n\t * the JWT.\n\t * @return the Issuer identifier, or {@code null} if the claim is missing\n\t */\n\tdefault @Nullable URL getIssuer() {\n\t\treturn this.getClaimAsURL(JwtClaimNames.ISS);\n\t}\n\n\t/**\n\t * Returns the Subject {@code (sub)} claim which identifies the principal that is the\n\t * subject of the JWT.\n\t * @return the Subject identifier, or {@code null} if the claim is missing\n\t */\n\tdefault @Nullable String getSubject() {\n\t\treturn this.getClaimAsString(JwtClaimNames.SUB);\n\t}\n\n\t/**\n\t * Returns the Audience {@code (aud)} claim which identifies the recipient(s) that the\n\t * JWT is intended for.\n\t * @return the Audience(s) that this JWT intended for, or {@code null} if the claim is\n\t * missing\n\t */\n\tdefault @Nullable List<String> getAudience() {\n\t\treturn this.getClaimAsStringList(JwtClaimNames.AUD);\n\t}\n\n\t/**\n\t * Returns the Expiration time {@code (exp)} claim which identifies the expiration\n\t * time on or after which the JWT MUST NOT be accepted for processing.\n\t * @return the Expiration time on or after which the JWT MUST NOT be accepted for\n\t * processing, or {@code null} if the claim is missing\n\t */\n\tdefault @Nullable Instant getExpiresAt() {\n\t\treturn this.getClaimAsInstant(JwtClaimNames.EXP);\n\t}\n\n\t/**\n\t * Returns the Not Before {@code (nbf)} claim which identifies the time before which\n\t * the JWT MUST NOT be accepted for processing.\n\t * @return the Not Before time before which the JWT MUST NOT be accepted for\n\t * processing, or {@code null} if the claim is missing\n\t */\n\tdefault @Nullable Instant getNotBefore() {\n\t\treturn this.getClaimAsInstant(JwtClaimNames.NBF);\n\t}\n\n\t/**\n\t * Returns the Issued at {@code (iat)} claim which identifies the time at which the\n\t * JWT was issued.\n\t * @return the Issued at claim which identifies the time at which the JWT was issued,\n\t * or {@code null} if the claim is missing\n\t */\n\tdefault @Nullable Instant getIssuedAt() {\n\t\treturn this.getClaimAsInstant(JwtClaimNames.IAT);\n\t}\n\n\t/**\n\t * Returns the JWT ID {@code (jti)} claim which provides a unique identifier for the\n\t * JWT.\n\t * @return the JWT ID claim which provides a unique identifier for the JWT, or\n\t * {@code null} if the claim is missing\n\t */\n\tdefault @Nullable String getId() {\n\t\treturn this.getClaimAsString(JwtClaimNames.JTI);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\n/**\n * The Registered Claim Names defined by the JSON Web Token (JWT) specification that may\n * be contained in the JSON object JWT Claims Set.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see JwtClaimAccessor\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519#section-4\">JWT\n * Claims</a>\n */\npublic final class JwtClaimNames {\n\n\t/**\n\t * {@code iss} - the Issuer claim identifies the principal that issued the JWT\n\t */\n\tpublic static final String ISS = \"iss\";\n\n\t/**\n\t * {@code sub} - the Subject claim identifies the principal that is the subject of the\n\t * JWT\n\t */\n\tpublic static final String SUB = \"sub\";\n\n\t/**\n\t * {@code aud} - the Audience claim identifies the recipient(s) that the JWT is\n\t * intended for\n\t */\n\tpublic static final String AUD = \"aud\";\n\n\t/**\n\t * {@code exp} - the Expiration time claim identifies the expiration time on or after\n\t * which the JWT MUST NOT be accepted for processing\n\t */\n\tpublic static final String EXP = \"exp\";\n\n\t/**\n\t * {@code nbf} - the Not Before claim identifies the time before which the JWT MUST\n\t * NOT be accepted for processing\n\t */\n\tpublic static final String NBF = \"nbf\";\n\n\t/**\n\t * {@code iat} - The Issued at claim identifies the time at which the JWT was issued\n\t */\n\tpublic static final String IAT = \"iat\";\n\n\t/**\n\t * {@code jti} - The JWT ID claim provides a unique identifier for the JWT\n\t */\n\tpublic static final String JTI = \"jti\";\n\n\tprivate JwtClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.function.Predicate;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\n\n/**\n * Validates a claim in a {@link Jwt} against a provided\n * {@link java.util.function.Predicate}\n *\n * @author Zeeshan Adnan\n * @since 5.3\n */\npublic final class JwtClaimValidator<T> implements OAuth2TokenValidator<Jwt> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final String claim;\n\n\tprivate final Predicate<T> test;\n\n\tprivate final OAuth2Error error;\n\n\t/**\n\t * Constructs a {@link JwtClaimValidator} using the provided parameters\n\t * @param claim - is the name of the claim in {@link Jwt} to validate.\n\t * @param test - is the predicate function for the claim to test against.\n\t */\n\tpublic JwtClaimValidator(String claim, Predicate<T> test) {\n\t\tAssert.notNull(claim, \"claim can not be null\");\n\t\tAssert.notNull(test, \"test can not be null\");\n\t\tthis.claim = claim;\n\t\tthis.test = test;\n\t\tthis.error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, \"The \" + this.claim + \" claim is not valid\",\n\t\t\t\t\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt token) {\n\t\tAssert.notNull(token, \"token cannot be null\");\n\t\tT claimValue = token.getClaim(this.claim);\n\t\tif (claimValue != null) {\n\t\t\tif (this.test.test(claimValue)) {\n\t\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(this.error.getDescription());\n\t\treturn OAuth2TokenValidatorResult.failure(this.error);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimsSet.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.util.Assert;\n\n/**\n * The {@link Jwt JWT} Claims Set is a JSON object representing the claims conveyed by a\n * JSON Web Token.\n *\n * @author Anoop Garlapati\n * @author Joe Grandja\n * @since 5.6\n * @see Jwt\n * @see JwtClaimAccessor\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519#section-4\">JWT Claims\n * Set</a>\n */\npublic final class JwtClaimsSet implements JwtClaimAccessor {\n\n\tprivate final Map<String, Object> claims;\n\n\tprivate JwtClaimsSet(Map<String, Object> claims) {\n\t\tthis.claims = Collections.unmodifiableMap(new HashMap<>(claims));\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Returns a new {@link Builder}, initialized with the provided {@code claims}.\n\t * @param claims a JWT claims set\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder from(JwtClaimsSet claims) {\n\t\treturn new Builder(claims);\n\t}\n\n\t/**\n\t * A builder for {@link JwtClaimsSet}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final Map<String, Object> claims = new HashMap<>();\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\tprivate Builder(JwtClaimsSet claims) {\n\t\t\tAssert.notNull(claims, \"claims cannot be null\");\n\t\t\tthis.claims.putAll(claims.getClaims());\n\t\t}\n\n\t\t/**\n\t\t * Sets the issuer {@code (iss)} claim, which identifies the principal that issued\n\t\t * the JWT.\n\t\t * @param issuer the issuer identifier\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder issuer(String issuer) {\n\t\t\treturn claim(JwtClaimNames.ISS, issuer);\n\t\t}\n\n\t\t/**\n\t\t * Sets the subject {@code (sub)} claim, which identifies the principal that is\n\t\t * the subject of the JWT.\n\t\t * @param subject the subject identifier\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder subject(String subject) {\n\t\t\treturn claim(JwtClaimNames.SUB, subject);\n\t\t}\n\n\t\t/**\n\t\t * Sets the audience {@code (aud)} claim, which identifies the recipient(s) that\n\t\t * the JWT is intended for.\n\t\t * @param audience the audience that this JWT is intended for\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder audience(List<String> audience) {\n\t\t\treturn claim(JwtClaimNames.AUD, audience);\n\t\t}\n\n\t\t/**\n\t\t * Sets the expiration time {@code (exp)} claim, which identifies the time on or\n\t\t * after which the JWT MUST NOT be accepted for processing.\n\t\t * @param expiresAt the time on or after which the JWT MUST NOT be accepted for\n\t\t * processing\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder expiresAt(Instant expiresAt) {\n\t\t\treturn claim(JwtClaimNames.EXP, expiresAt);\n\t\t}\n\n\t\t/**\n\t\t * Sets the not before {@code (nbf)} claim, which identifies the time before which\n\t\t * the JWT MUST NOT be accepted for processing.\n\t\t * @param notBefore the time before which the JWT MUST NOT be accepted for\n\t\t * processing\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder notBefore(Instant notBefore) {\n\t\t\treturn claim(JwtClaimNames.NBF, notBefore);\n\t\t}\n\n\t\t/**\n\t\t * Sets the issued at {@code (iat)} claim, which identifies the time at which the\n\t\t * JWT was issued.\n\t\t * @param issuedAt the time at which the JWT was issued\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder issuedAt(Instant issuedAt) {\n\t\t\treturn claim(JwtClaimNames.IAT, issuedAt);\n\t\t}\n\n\t\t/**\n\t\t * Sets the JWT ID {@code (jti)} claim, which provides a unique identifier for the\n\t\t * JWT.\n\t\t * @param jti the unique identifier for the JWT\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder id(String jti) {\n\t\t\treturn claim(JwtClaimNames.JTI, jti);\n\t\t}\n\n\t\t/**\n\t\t * Sets the claim.\n\t\t * @param name the claim name\n\t\t * @param value the claim value\n\t\t * @return the {@link Builder}\n\t\t */\n\t\tpublic Builder claim(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} to be provided access to the claims allowing the ability to\n\t\t * add, replace, or remove.\n\t\t * @param claimsConsumer a {@code Consumer} of the claims\n\t\t */\n\t\tpublic Builder claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link JwtClaimsSet}.\n\t\t * @return a {@link JwtClaimsSet}\n\t\t */\n\t\tpublic JwtClaimsSet build() {\n\t\t\tAssert.notEmpty(this.claims, \"claims cannot be empty\");\n\n\t\t\t// The value of the 'iss' claim is a String or URL (StringOrURI).\n\t\t\t// Attempt to convert to URL.\n\t\t\tObject issuer = this.claims.get(JwtClaimNames.ISS);\n\t\t\tif (issuer != null) {\n\t\t\t\tURL convertedValue = ClaimConversionService.getSharedInstance().convert(issuer, URL.class);\n\t\t\t\tif (convertedValue != null) {\n\t\t\t\t\tthis.claims.put(JwtClaimNames.ISS, convertedValue);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn new JwtClaimsSet(this.claims);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\n/**\n * Implementations of this interface are responsible for &quot;decoding&quot; a JSON Web\n * Token (JWT) from its compact claims representation format to a {@link Jwt}.\n *\n * <p>\n * JWTs may be represented using the JWS Compact Serialization format for a JSON Web\n * Signature (JWS) structure or JWE Compact Serialization format for a JSON Web Encryption\n * (JWE) structure. Therefore, implementors are responsible for verifying a JWS and/or\n * decrypting a JWE.\n *\n * @author Joe Grandja\n * @since 5.0\n * @see Jwt\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519\">JSON Web Token\n * (JWT)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516\">JSON Web Encryption\n * (JWE)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515#section-3.1\">JWS\n * Compact Serialization</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516#section-3.1\">JWE\n * Compact Serialization</a>\n */\n@FunctionalInterface\npublic interface JwtDecoder {\n\n\t/**\n\t * Decodes the JWT from its compact claims representation format and returns a\n\t * {@link Jwt}.\n\t * @param token the JWT value\n\t * @return a {@link Jwt}\n\t * @throws JwtException if an error occurs while attempting to decode the JWT\n\t */\n\tJwt decode(String token) throws JwtException;\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\n/**\n * A factory for {@link JwtDecoder}(s). This factory should be supplied with a type that\n * provides contextual information used to create a specific {@code JwtDecoder}.\n *\n * @param <C> The type that provides contextual information used to create a specific\n * {@code JwtDecoder}.\n * @author Joe Grandja\n * @since 5.2\n * @see JwtDecoder\n */\n@FunctionalInterface\npublic interface JwtDecoderFactory<C> {\n\n\t/**\n\t * Creates a {@code JwtDecoder} using the supplied \"contextual\" type.\n\t * @param context the type that provides contextual information\n\t * @return a {@link JwtDecoder}\n\t */\n\tJwtDecoder createDecoder(C context);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderInitializationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.io.Serial;\n\n/**\n * An exception thrown when a {@link JwtDecoder} or {@link ReactiveJwtDecoder}'s lazy\n * initialization fails.\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic class JwtDecoderInitializationException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2786360018315628982L;\n\n\tpublic JwtDecoderInitializationException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.KeySourceException;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.KeyType;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.JWSKeySelector;\nimport com.nimbusds.jose.proc.JWSVerificationKeySelector;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jwt.proc.ConfigurableJWTProcessor;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.client.SimpleClientHttpRequestFactory;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.util.Assert;\nimport org.springframework.web.client.HttpClientErrorException;\nimport org.springframework.web.client.RestOperations;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Allows resolving configuration from an <a href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig\">OpenID\n * Provider Configuration</a> or\n * <a href=\"https://tools.ietf.org/html/rfc8414#section-3.1\">Authorization Server Metadata\n * Request</a> based on provided issuer and method invoked.\n *\n * @author Thomas Vitale\n * @author Rafiullah Hamedy\n * @since 5.2\n */\nfinal class JwtDecoderProviderConfigurationUtils {\n\n\tprivate static final String OIDC_METADATA_PATH = \"/.well-known/openid-configuration\";\n\n\tprivate static final String OAUTH_METADATA_PATH = \"/.well-known/oauth-authorization-server\";\n\n\tprivate static final RestTemplate rest = new RestTemplate();\n\n\tstatic {\n\t\tint connectTimeout = Integer.parseInt(System.getProperty(\"sun.net.client.defaultConnectTimeout\", \"30000\"));\n\t\tint readTimeout = Integer.parseInt(System.getProperty(\"sun.net.client.defaultReadTimeout\", \"30000\"));\n\t\tSimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();\n\t\trequestFactory.setConnectTimeout(connectTimeout);\n\t\trequestFactory.setReadTimeout(readTimeout);\n\t\trest.setRequestFactory(requestFactory);\n\t}\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate JwtDecoderProviderConfigurationUtils() {\n\t}\n\n\tstatic Map<String, Object> getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) {\n\t\treturn getConfiguration(oidcIssuerLocation, rest, oidc(oidcIssuerLocation));\n\t}\n\n\tstatic Map<String, Object> getConfigurationForIssuerLocation(String issuer, RestOperations rest) {\n\t\treturn getConfiguration(issuer, rest, oidc(issuer), oidcRfc8414(issuer), oauth(issuer));\n\t}\n\n\tstatic Map<String, Object> getConfigurationForIssuerLocation(String issuer) {\n\t\treturn getConfigurationForIssuerLocation(issuer, rest);\n\t}\n\n\tstatic void validateIssuer(Map<String, Object> configuration, String issuer) {\n\t\tString metadataIssuer = getMetadataIssuer(configuration);\n\t\tAssert.state(issuer.equals(metadataIssuer), () -> \"The Issuer \\\"\" + metadataIssuer\n\t\t\t\t+ \"\\\" provided in the configuration did not \" + \"match the requested issuer \\\"\" + issuer + \"\\\"\");\n\t}\n\n\tstatic <C extends SecurityContext> void addJWSAlgorithms(ConfigurableJWTProcessor<C> jwtProcessor) {\n\t\tJWSKeySelector<C> selector = jwtProcessor.getJWSKeySelector();\n\t\tif (selector instanceof JWSVerificationKeySelector) {\n\t\t\tJWKSource<C> jwkSource = ((JWSVerificationKeySelector<C>) selector).getJWKSource();\n\t\t\tSet<JWSAlgorithm> algorithms = getJWSAlgorithms(jwkSource);\n\t\t\tselector = new JWSVerificationKeySelector<>(algorithms, jwkSource);\n\t\t\tjwtProcessor.setJWSKeySelector(selector);\n\t\t}\n\t}\n\n\tstatic <C extends SecurityContext> Set<JWSAlgorithm> getJWSAlgorithms(JWKSource<C> jwkSource) {\n\t\tJWKMatcher jwkMatcher = new JWKMatcher.Builder().publicOnly(true)\n\t\t\t.keyUses(KeyUse.SIGNATURE, null)\n\t\t\t.keyTypes(KeyType.RSA, KeyType.EC)\n\t\t\t.build();\n\t\tSet<JWSAlgorithm> jwsAlgorithms = new HashSet<>();\n\t\ttry {\n\t\t\tList<? extends JWK> jwks = jwkSource.get(new JWKSelector(jwkMatcher), null);\n\t\t\tfor (JWK jwk : jwks) {\n\t\t\t\tif (jwk.getAlgorithm() != null) {\n\t\t\t\t\tJWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(jwk.getAlgorithm().getName());\n\t\t\t\t\tjwsAlgorithms.add(jwsAlgorithm);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (jwk.getKeyType() == KeyType.RSA) {\n\t\t\t\t\t\tjwsAlgorithms.addAll(JWSAlgorithm.Family.RSA);\n\t\t\t\t\t}\n\t\t\t\t\telse if (jwk.getKeyType() == KeyType.EC) {\n\t\t\t\t\t\tjwsAlgorithms.addAll(JWSAlgorithm.Family.EC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (KeySourceException ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t\tAssert.notEmpty(jwsAlgorithms, \"Failed to find any algorithms from the JWK set\");\n\t\treturn jwsAlgorithms;\n\t}\n\n\tstatic Set<SignatureAlgorithm> getSignatureAlgorithms(JWKSource<SecurityContext> jwkSource) {\n\t\tSet<JWSAlgorithm> jwsAlgorithms = getJWSAlgorithms(jwkSource);\n\t\tSet<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();\n\t\tfor (JWSAlgorithm jwsAlgorithm : jwsAlgorithms) {\n\t\t\tSignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(jwsAlgorithm.getName());\n\t\t\tif (signatureAlgorithm != null) {\n\t\t\t\tsignatureAlgorithms.add(signatureAlgorithm);\n\t\t\t}\n\t\t}\n\t\treturn signatureAlgorithms;\n\t}\n\n\tprivate static String getMetadataIssuer(Map<String, Object> configuration) {\n\t\tif (configuration.containsKey(\"issuer\")) {\n\t\t\treturn configuration.get(\"issuer\").toString();\n\t\t}\n\t\treturn \"(unavailable)\";\n\t}\n\n\tprivate static Map<String, Object> getConfiguration(String issuer, RestOperations rest, UriComponents... uris) {\n\t\tString errorMessage = \"Unable to resolve the Configuration with the provided Issuer of \" + \"\\\"\" + issuer + \"\\\"\";\n\t\tfor (UriComponents uri : uris) {\n\t\t\ttry {\n\t\t\t\tRequestEntity<Void> request = RequestEntity.get(uri.toUriString()).build();\n\t\t\t\tResponseEntity<Map<String, Object>> response = rest.exchange(request, STRING_OBJECT_MAP);\n\t\t\t\tMap<String, Object> configuration = response.getBody();\n\t\t\t\tAssert.notNull(configuration, \"configuration must not be null\");\n\t\t\t\tAssert.isTrue(configuration.get(\"jwks_uri\") != null, \"The public JWK set URI must not be null\");\n\t\t\t\treturn configuration;\n\t\t\t}\n\t\t\tcatch (IllegalArgumentException ex) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tcatch (RuntimeException ex) {\n\t\t\t\tif (!(ex instanceof HttpClientErrorException\n\t\t\t\t\t\t&& ((HttpClientErrorException) ex).getStatusCode().is4xxClientError())) {\n\t\t\t\t\tthrow new IllegalArgumentException(errorMessage, ex);\n\t\t\t\t}\n\t\t\t\t// else try another endpoint\n\t\t\t}\n\t\t}\n\t\tthrow new IllegalArgumentException(errorMessage);\n\t}\n\n\tstatic UriComponents oidc(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(uri.getPath() + OIDC_METADATA_PATH)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tstatic UriComponents oidcRfc8414(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(OIDC_METADATA_PATH + uri.getPath())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tstatic UriComponents oauth(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(OAUTH_METADATA_PATH + uri.getPath())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.util.Assert;\n\n/**\n * Allows creating a {@link JwtDecoder} from an <a href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig\">OpenID\n * Provider Configuration</a> or\n * <a href=\"https://tools.ietf.org/html/rfc8414#section-3.1\">Authorization Server Metadata\n * Request</a> based on provided issuer and method invoked.\n *\n * @author Josh Cummings\n * @author Rafiullah Hamedy\n * @since 5.1\n */\npublic final class JwtDecoders {\n\n\tprivate JwtDecoders() {\n\t}\n\n\t/**\n\t * Creates a {@link JwtDecoder} using the provided <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * by making an <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">OpenID\n\t * Provider Configuration Request</a> and using the values in the <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">OpenID\n\t * Provider Configuration Response</a> to initialize the {@link JwtDecoder}.\n\t * @param oidcIssuerLocation the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return a {@link JwtDecoder} that was initialized by the OpenID Provider\n\t * Configuration.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {\n\t\tAssert.hasText(oidcIssuerLocation, \"oidcIssuerLocation cannot be empty\");\n\t\tMap<String, Object> configuration = JwtDecoderProviderConfigurationUtils\n\t\t\t.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);\n\t\treturn (T) withProviderConfiguration(configuration, oidcIssuerLocation);\n\t}\n\n\t/**\n\t * Creates a {@link JwtDecoder} using the provided <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * by querying three different discovery endpoints serially, using the values in the\n\t * first successful response to initialize. If an endpoint returns anything other than\n\t * a 200 or a 4xx, the method will exit without attempting subsequent endpoints.\n\t *\n\t * The three endpoints are computed as follows, given that the {@code issuer} is\n\t * composed of a {@code host} and a {@code path}:\n\t *\n\t * <ol>\n\t * <li>{@code host/.well-known/openid-configuration/path}, as defined in\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-5\">RFC 8414's Compatibility\n\t * Notes</a>.</li>\n\t * <li>{@code issuer/.well-known/openid-configuration}, as defined in <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">\n\t * OpenID Provider Configuration</a>.</li>\n\t * <li>{@code host/.well-known/oauth-authorization-server/path}, as defined in\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-3.1\">Authorization Server\n\t * Metadata Request</a>.</li>\n\t * </ol>\n\t *\n\t * Note that the second endpoint is the equivalent of calling\n\t * {@link JwtDecoders#fromOidcIssuerLocation(String)}\n\t * @param issuer the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return a {@link JwtDecoder} that was initialized by one of the described endpoints\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T extends JwtDecoder> T fromIssuerLocation(String issuer) {\n\t\tAssert.hasText(issuer, \"issuer cannot be empty\");\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);\n\t\tjwtDecoder.setJwtValidator(jwtValidator);\n\t\treturn (T) jwtDecoder;\n\t}\n\n\t/**\n\t * Validate provided issuer and build {@link JwtDecoder} from <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">OpenID\n\t * Provider Configuration Response</a> and\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-3.2\">Authorization Server\n\t * Metadata Response</a>.\n\t * @param configuration the configuration values\n\t * @param issuer the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return {@link JwtDecoder}\n\t */\n\tprivate static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {\n\t\tJwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);\n\t\tObject jwksUri = configuration.get(\"jwks_uri\");\n\t\tAssert.notNull(jwksUri, \"The public JWK Set URI must not be null\");\n\t\tString jwkSetUri = jwksUri.toString();\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)\n\t\t\t.jwtProcessorCustomizer(JwtDecoderProviderConfigurationUtils::addJWSAlgorithms)\n\t\t\t.build();\n\t\tjwtDecoder.setJwtValidator(jwtValidator);\n\t\treturn jwtDecoder;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\n/**\n * Implementations of this interface are responsible for encoding a JSON Web Token (JWT)\n * to its compact claims representation format.\n *\n * <p>\n * JWTs may be represented using the JWS Compact Serialization format for a JSON Web\n * Signature (JWS) structure or JWE Compact Serialization format for a JSON Web Encryption\n * (JWE) structure. Therefore, implementors are responsible for signing a JWS and/or\n * encrypting a JWE.\n *\n * @author Anoop Garlapati\n * @author Joe Grandja\n * @since 5.6\n * @see Jwt\n * @see JwtEncoderParameters\n * @see JwtDecoder\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519\">JSON Web Token\n * (JWT)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516\">JSON Web Encryption\n * (JWE)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515#section-3.1\">JWS\n * Compact Serialization</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516#section-3.1\">JWE\n * Compact Serialization</a>\n */\n@FunctionalInterface\npublic interface JwtEncoder {\n\n\t/**\n\t * Encode the JWT to its compact claims representation format.\n\t * @param parameters the parameters containing the JOSE header and JWT Claims Set\n\t * @return a {@link Jwt}\n\t * @throws JwtEncodingException if an error occurs while attempting to encode the JWT\n\t */\n\tJwt encode(JwtEncoderParameters parameters) throws JwtEncodingException;\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtEncoderParameters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A holder of parameters containing the JWS headers and JWT Claims Set.\n *\n * @author Joe Grandja\n * @since 5.6\n * @see JwsHeader\n * @see JwtClaimsSet\n * @see JwtEncoder\n */\npublic final class JwtEncoderParameters {\n\n\tprivate final @Nullable JwsHeader jwsHeader;\n\n\tprivate final JwtClaimsSet claims;\n\n\tprivate JwtEncoderParameters(@Nullable JwsHeader jwsHeader, JwtClaimsSet claims) {\n\t\tthis.jwsHeader = jwsHeader;\n\t\tthis.claims = claims;\n\t}\n\n\t/**\n\t * Returns a new {@link JwtEncoderParameters}, initialized with the provided\n\t * {@link JwtClaimsSet}.\n\t * @param claims the {@link JwtClaimsSet}\n\t * @return the {@link JwtEncoderParameters}\n\t */\n\tpublic static JwtEncoderParameters from(JwtClaimsSet claims) {\n\t\tAssert.notNull(claims, \"claims cannot be null\");\n\t\treturn new JwtEncoderParameters(null, claims);\n\t}\n\n\t/**\n\t * Returns a new {@link JwtEncoderParameters}, initialized with the provided\n\t * {@link JwsHeader} and {@link JwtClaimsSet}.\n\t * @param jwsHeader the {@link JwsHeader}\n\t * @param claims the {@link JwtClaimsSet}\n\t * @return the {@link JwtEncoderParameters}\n\t */\n\tpublic static JwtEncoderParameters from(JwsHeader jwsHeader, JwtClaimsSet claims) {\n\t\tAssert.notNull(jwsHeader, \"jwsHeader cannot be null\");\n\t\tAssert.notNull(claims, \"claims cannot be null\");\n\t\treturn new JwtEncoderParameters(jwsHeader, claims);\n\t}\n\n\t/**\n\t * Returns the {@link JwsHeader JWS headers}.\n\t * @return the {@link JwsHeader}, or {@code null} if not specified\n\t */\n\tpublic @Nullable JwsHeader getJwsHeader() {\n\t\treturn this.jwsHeader;\n\t}\n\n\t/**\n\t * Returns the {@link JwtClaimsSet claims}.\n\t * @return the {@link JwtClaimsSet}\n\t */\n\tpublic JwtClaimsSet getClaims() {\n\t\treturn this.claims;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtEncodingException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.io.Serial;\n\n/**\n * This exception is thrown when an error occurs while attempting to encode a JSON Web\n * Token (JWT).\n *\n * @author Joe Grandja\n * @since 5.6\n */\npublic class JwtEncodingException extends JwtException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 6581840872589902213L;\n\n\t/**\n\t * Constructs a {@code JwtEncodingException} using the provided parameters.\n\t * @param message the detail message\n\t */\n\tpublic JwtEncodingException(String message) {\n\t\tsuper(message);\n\t}\n\n\t/**\n\t * Constructs a {@code JwtEncodingException} using the provided parameters.\n\t * @param message the detail message\n\t * @param cause the root cause\n\t */\n\tpublic JwtEncodingException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.io.Serial;\n\n/**\n * Base exception for all JSON Web Token (JWT) related errors.\n *\n * @author Joe Grandja\n * @since 5.0\n */\npublic class JwtException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3070197880233583797L;\n\n\t/**\n\t * Constructs a {@code JwtException} using the provided parameters.\n\t * @param message the detail message\n\t */\n\tpublic JwtException(String message) {\n\t\tsuper(message);\n\t}\n\n\t/**\n\t * Constructs a {@code JwtException} using the provided parameters.\n\t * @param message the detail message\n\t * @param cause the root cause\n\t */\n\tpublic JwtException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuedAtValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link OAuth2TokenValidator} responsible for validating the {@link JwtClaimNames#IAT\n * \"iat\"} claim in the {@link Jwt}.\n *\n * @author Joe Grandja\n * @since 6.5\n * @see OAuth2TokenValidator\n * @see Jwt\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc7519\">JSON Web\n * Token (JWT)</a>\n */\npublic final class JwtIssuedAtValidator implements OAuth2TokenValidator<Jwt> {\n\n\tprivate final boolean required;\n\n\tprivate Duration clockSkew = Duration.ofSeconds(60);\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * Constructs a {@code JwtIssuedAtValidator} with the defaults.\n\t */\n\tpublic JwtIssuedAtValidator() {\n\t\tthis(false);\n\t}\n\n\t/**\n\t * Constructs a {@code JwtIssuedAtValidator} using the provided parameters.\n\t * @param required {@code true} if the {@link JwtClaimNames#IAT \"iat\"} claim is\n\t * REQUIRED in the {@link Jwt}, {@code false} otherwise\n\t */\n\tpublic JwtIssuedAtValidator(boolean required) {\n\t\tthis.required = required;\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\tAssert.notNull(jwt, \"jwt cannot be null\");\n\t\tInstant issuedAt = jwt.getIssuedAt();\n\t\tif (issuedAt == null && this.required) {\n\t\t\tOAuth2Error error = createOAuth2Error(\"iat claim is required.\");\n\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t}\n\n\t\tif (issuedAt != null) {\n\t\t\t// Check time window of validity\n\t\t\tInstant now = Instant.now(this.clock);\n\t\t\tInstant notBefore = now.minus(this.clockSkew);\n\t\t\tInstant notAfter = now.plus(this.clockSkew);\n\t\t\tif (issuedAt.isBefore(notBefore) || issuedAt.isAfter(notAfter)) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"iat claim is invalid.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\t\t}\n\t\treturn OAuth2TokenValidatorResult.success();\n\t}\n\n\t/**\n\t * Sets the clock skew. The default is 60 seconds.\n\t * @param clockSkew the clock skew\n\t */\n\tpublic void setClockSkew(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tAssert.isTrue(clockSkew.getSeconds() >= 0, \"clockSkew must be >= 0\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Sets the {@link Clock} used in {@link Instant#now(Clock)}.\n\t * @param clock the clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n\tprivate static OAuth2Error createOAuth2Error(String reason) {\n\t\treturn new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, reason, null);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.function.Predicate;\n\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\n\n/**\n * Validates the \"iss\" claim in a {@link Jwt}, that is matches a configured value\n *\n * @author Josh Cummings\n * @since 5.1\n */\npublic final class JwtIssuerValidator implements OAuth2TokenValidator<Jwt> {\n\n\tprivate final JwtClaimValidator<Object> validator;\n\n\t/**\n\t * Constructs a {@link JwtIssuerValidator} using the provided parameters\n\t * @param issuer - The issuer that each {@link Jwt} should have.\n\t */\n\tpublic JwtIssuerValidator(String issuer) {\n\t\tAssert.notNull(issuer, \"issuer cannot be null\");\n\n\t\tPredicate<Object> testClaimValue = (claimValue) -> (claimValue != null) && issuer.equals(claimValue.toString());\n\t\tthis.validator = new JwtClaimValidator<>(JwtClaimNames.ISS, testClaimValue);\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt token) {\n\t\tAssert.notNull(token, \"token cannot be null\");\n\t\treturn this.validator.validate(token);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtTimestampValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\n\n/**\n * An implementation of {@link OAuth2TokenValidator} for verifying claims in a Jwt-based\n * access token\n *\n * <p>\n * Because clocks can differ between the Jwt source, say the Authorization Server, and its\n * destination, say the Resource Server, there is a default clock leeway exercised when\n * deciding if the current time is within the Jwt's specified operating window\n *\n * @author Josh Cummings\n * @since 5.1\n * @see Jwt\n * @see OAuth2TokenValidator\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519\">JSON Web Token\n * (JWT)</a>\n */\npublic final class JwtTimestampValidator implements OAuth2TokenValidator<Jwt> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final Duration DEFAULT_MAX_CLOCK_SKEW = Duration.of(60, ChronoUnit.SECONDS);\n\n\tprivate final Duration clockSkew;\n\n\tprivate boolean allowEmptyExpiryClaim = true;\n\n\tprivate boolean allowEmptyNotBeforeClaim = true;\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\t/**\n\t * A basic instance with no custom verification and the default max clock skew\n\t */\n\tpublic JwtTimestampValidator() {\n\t\tthis(DEFAULT_MAX_CLOCK_SKEW);\n\t}\n\n\tpublic JwtTimestampValidator(Duration clockSkew) {\n\t\tAssert.notNull(clockSkew, \"clockSkew cannot be null\");\n\t\tthis.clockSkew = clockSkew;\n\t}\n\n\t/**\n\t * Whether to allow the {@code exp} header to be empty. The default value is\n\t * {@code true}\n\t *\n\t * @since 7.0\n\t */\n\tpublic void setAllowEmptyExpiryClaim(boolean allowEmptyExpiryClaim) {\n\t\tthis.allowEmptyExpiryClaim = allowEmptyExpiryClaim;\n\t}\n\n\t/**\n\t * Whether to allow the {@code nbf} header to be empty. The default value is\n\t * {@code true}\n\t *\n\t * @since 7.0\n\t */\n\tpublic void setAllowEmptyNotBeforeClaim(boolean allowEmptyNotBeforeClaim) {\n\t\tthis.allowEmptyNotBeforeClaim = allowEmptyNotBeforeClaim;\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\tAssert.notNull(jwt, \"jwt cannot be null\");\n\t\tInstant expiry = jwt.getExpiresAt();\n\t\tif (!this.allowEmptyExpiryClaim && ObjectUtils.isEmpty(expiry)) {\n\t\t\treturn createOAuth2Error(\"exp is required\");\n\t\t}\n\t\tif (expiry != null) {\n\t\t\tif (Instant.now(this.clock).minus(this.clockSkew).isAfter(expiry)) {\n\t\t\t\treturn createOAuth2Error(String.format(\"Jwt expired at %s\", jwt.getExpiresAt()));\n\t\t\t}\n\t\t}\n\t\tInstant notBefore = jwt.getNotBefore();\n\t\tif (!this.allowEmptyNotBeforeClaim && ObjectUtils.isEmpty(notBefore)) {\n\t\t\treturn createOAuth2Error(\"nbf is required\");\n\t\t}\n\t\tif (notBefore != null) {\n\t\t\tif (Instant.now(this.clock).plus(this.clockSkew).isBefore(notBefore)) {\n\t\t\t\treturn createOAuth2Error(String.format(\"Jwt used before %s\", jwt.getNotBefore()));\n\t\t\t}\n\t\t}\n\t\treturn OAuth2TokenValidatorResult.success();\n\t}\n\n\tprivate OAuth2TokenValidatorResult createOAuth2Error(String reason) {\n\t\tthis.logger.debug(reason);\n\t\treturn OAuth2TokenValidatorResult.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, reason,\n\t\t\t\t\"https://tools.ietf.org/html/rfc6750#section-3.1\"));\n\t}\n\n\t/**\n\t * Use this {@link Clock} with {@link Instant#now()} for assessing timestamp validity\n\t * @param clock\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock cannot be null\");\n\t\tthis.clock = clock;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtTypeValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A validator for the {@code typ} header. Specifically for indicating the header values\n * that a given {@link JwtDecoder} will support.\n *\n * @author Josh Cummings\n * @since 6.5\n */\npublic final class JwtTypeValidator implements OAuth2TokenValidator<Jwt> {\n\n\tprivate final Collection<String> validTypes;\n\n\tprivate boolean allowEmpty;\n\n\tpublic JwtTypeValidator(Collection<String> validTypes) {\n\t\tAssert.notEmpty(validTypes, \"validTypes cannot be empty\");\n\t\tthis.validTypes = new ArrayList<>(validTypes);\n\t}\n\n\tpublic JwtTypeValidator(String... validTypes) {\n\t\tthis(List.of(validTypes));\n\t}\n\n\t/**\n\t * Require that the {@code typ} header be {@code JWT} or absent\n\t */\n\tpublic static JwtTypeValidator jwt() {\n\t\tJwtTypeValidator validator = new JwtTypeValidator(List.of(\"JWT\"));\n\t\tvalidator.setAllowEmpty(true);\n\t\treturn validator;\n\t}\n\n\t/**\n\t * Whether to allow the {@code typ} header to be empty. The default value is\n\t * {@code false}\n\t */\n\tpublic void setAllowEmpty(boolean allowEmpty) {\n\t\tthis.allowEmpty = allowEmpty;\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt token) {\n\t\tString typ = (String) token.getHeaders().get(JoseHeaderNames.TYP);\n\t\tif (this.allowEmpty && !StringUtils.hasText(typ)) {\n\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t}\n\t\tfor (String validType : this.validTypes) {\n\t\t\tif (validType.equalsIgnoreCase(typ)) {\n\t\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t\t}\n\t\t}\n\t\treturn OAuth2TokenValidatorResult.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN,\n\t\t\t\t\"the given typ value needs to be one of \" + this.validTypes,\n\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.9\"));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.io.Serial;\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\n\n/**\n * An exception that results from an unsuccessful {@link OAuth2TokenValidatorResult}\n *\n * @author Josh Cummings\n * @since 5.1\n */\npublic class JwtValidationException extends BadJwtException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 134652048447295615L;\n\n\tprivate final Collection<OAuth2Error> errors;\n\n\t/**\n\t * Constructs a {@link JwtValidationException} using the provided parameters\n\t *\n\t * While each {@link OAuth2Error} does contain an error description, this constructor\n\t * can take an overarching description that encapsulates the composition of failures\n\t *\n\t * That said, it is appropriate to pass one of the messages from the error list in as\n\t * the exception description, for example:\n\t *\n\t * <pre>\n\t * \tif ( result.hasErrors() ) {\n\t *  \tCollection&lt;OAuth2Error&gt; errors = result.getErrors();\n\t *  \tthrow new JwtValidationException(errors.iterator().next().getDescription(), errors);\n\t * \t}\n\t * </pre>\n\t * @param message - the exception message\n\t * @param errors - a list of {@link OAuth2Error}s with extra detail about the\n\t * validation result\n\t */\n\tpublic JwtValidationException(String message, Collection<OAuth2Error> errors) {\n\t\tsuper(message);\n\t\tAssert.notEmpty(errors, \"errors cannot be empty\");\n\t\tthis.errors = new ArrayList<>(errors);\n\t}\n\n\t/**\n\t * Return the list of {@link OAuth2Error}s associated with this exception\n\t * @return the list of {@link OAuth2Error}s associated with this exception\n\t */\n\tpublic Collection<OAuth2Error> getErrors() {\n\t\treturn this.errors;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtValidators.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\nimport org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Provides factory methods for creating {@code OAuth2TokenValidator<Jwt>}\n *\n * @author Josh Cummings\n * @author Rob Winch\n * @since 5.1\n */\npublic final class JwtValidators {\n\n\tprivate JwtValidators() {\n\t}\n\n\t/**\n\t * <p>\n\t * Create a {@link Jwt} Validator that contains all standard validators when an issuer\n\t * is known.\n\t * </p>\n\t * <p>\n\t * User's wanting to leverage the defaults plus additional validation can add the\n\t * result of this method to {@code DelegatingOAuth2TokenValidator} along with the\n\t * additional validators.\n\t * </p>\n\t * @param issuer the issuer\n\t * @return - a delegating validator containing all standard validators as well as any\n\t * supplied\n\t */\n\tpublic static OAuth2TokenValidator<Jwt> createDefaultWithIssuer(String issuer) {\n\t\treturn createDefaultWithValidators(new JwtIssuerValidator(issuer));\n\t}\n\n\t/**\n\t * <p>\n\t * Create a {@link Jwt} Validator that contains all standard validators.\n\t * </p>\n\t * <p>\n\t * User's wanting to leverage the defaults plus additional validation can add the\n\t * result of this method to {@code DelegatingOAuth2TokenValidator} along with the\n\t * additional validators.\n\t * </p>\n\t * @return - a delegating validator containing all standard validators as well as any\n\t * supplied\n\t */\n\tpublic static OAuth2TokenValidator<Jwt> createDefault() {\n\t\treturn new DelegatingOAuth2TokenValidator<>(Arrays.asList(JwtTypeValidator.jwt(), new JwtTimestampValidator(),\n\t\t\t\tnew X509CertificateThumbprintValidator(\n\t\t\t\t\t\tX509CertificateThumbprintValidator.DEFAULT_X509_CERTIFICATE_SUPPLIER)));\n\t}\n\n\t/**\n\t * <p>\n\t * Create a {@link Jwt} default validator with standard validators and additional\n\t * validators.\n\t * </p>\n\t * @param validators additional validators\n\t * @return - a delegating validator containing all standard validators with additional\n\t * validators\n\t * @since 6.3\n\t */\n\tpublic static OAuth2TokenValidator<Jwt> createDefaultWithValidators(List<OAuth2TokenValidator<Jwt>> validators) {\n\t\tAssert.notEmpty(validators, \"validators cannot be null or empty\");\n\t\tList<OAuth2TokenValidator<Jwt>> tokenValidators = new ArrayList<>(validators);\n\t\tX509CertificateThumbprintValidator x509CertificateThumbprintValidator = CollectionUtils\n\t\t\t.findValueOfType(tokenValidators, X509CertificateThumbprintValidator.class);\n\t\tif (x509CertificateThumbprintValidator == null) {\n\t\t\ttokenValidators.add(0, new X509CertificateThumbprintValidator(\n\t\t\t\t\tX509CertificateThumbprintValidator.DEFAULT_X509_CERTIFICATE_SUPPLIER));\n\t\t}\n\t\tJwtTimestampValidator jwtTimestampValidator = CollectionUtils.findValueOfType(tokenValidators,\n\t\t\t\tJwtTimestampValidator.class);\n\t\tif (jwtTimestampValidator == null) {\n\t\t\ttokenValidators.add(0, new JwtTimestampValidator());\n\t\t}\n\t\tJwtTypeValidator typeValidator = CollectionUtils.findValueOfType(tokenValidators, JwtTypeValidator.class);\n\t\tif (typeValidator == null) {\n\t\t\ttokenValidators.add(0, JwtTypeValidator.jwt());\n\t\t}\n\t\treturn new DelegatingOAuth2TokenValidator<>(tokenValidators);\n\t}\n\n\t/**\n\t * <p>\n\t * Create a {@link Jwt} default validator with standard validators and additional\n\t * validators.\n\t * </p>\n\t * @param validators additional validators\n\t * @return - a delegating validator containing all standard validators with additional\n\t * validators\n\t * @since 6.3\n\t */\n\tpublic static OAuth2TokenValidator<Jwt> createDefaultWithValidators(OAuth2TokenValidator<Jwt>... validators) {\n\t\tAssert.notEmpty(validators, \"validators cannot be null or empty\");\n\t\tList<OAuth2TokenValidator<Jwt>> tokenValidators = new ArrayList<>(Arrays.asList(validators));\n\t\treturn createDefaultWithValidators(tokenValidators);\n\t}\n\n\t/**\n\t * Return a {@link AtJwtBuilder} for building a validator that conforms to\n\t * <a href=\"https://datatracker.ietf.org/doc/html/rfc9068\">RFC 9068</a>.\n\t * @return the {@link AtJwtBuilder} for configuration\n\t * @since 6.5\n\t */\n\tpublic static AtJwtBuilder createAtJwtValidator() {\n\t\treturn new AtJwtBuilder();\n\t}\n\n\tprivate static RequireClaimValidator require(String claim) {\n\t\treturn new RequireClaimValidator(claim);\n\t}\n\n\t/**\n\t * A class for building a validator that conforms to\n\t * <a href=\"https://datatracker.ietf.org/doc/html/rfc9068\">RFC 9068</a>.\n\t *\n\t * <p>\n\t * To comply with this spec, this builder needs you to specify at least the\n\t * {@link #audience} and {@link #issuer}.\n\t *\n\t * <p>\n\t * While building, the claims are keyed by claim name to allow for simplified lookup\n\t * and replacement in {@link #validators}.\n\t *\n\t * @author Josh Cummings\n\t * @author Giacomo Baso\n\t * @since 6.5\n\t */\n\tpublic static final class AtJwtBuilder {\n\n\t\tMap<String, OAuth2TokenValidator<Jwt>> validators = new LinkedHashMap<>();\n\n\t\tprivate AtJwtBuilder() {\n\t\t\tJwtTimestampValidator timestamps = new JwtTimestampValidator();\n\t\t\tthis.validators.put(JoseHeaderNames.TYP, new JwtTypeValidator(List.of(\"at+jwt\", \"application/at+jwt\")));\n\t\t\tthis.validators.put(JwtClaimNames.EXP, require(JwtClaimNames.EXP).and(timestamps));\n\t\t\tthis.validators.put(JwtClaimNames.SUB, require(JwtClaimNames.SUB));\n\t\t\tthis.validators.put(JwtClaimNames.IAT, require(JwtClaimNames.IAT).and(timestamps));\n\t\t\tthis.validators.put(JwtClaimNames.JTI, require(JwtClaimNames.JTI));\n\t\t\tthis.validators.put(\"client_id\", require(\"client_id\"));\n\t\t}\n\n\t\t/**\n\t\t * Validate that each token has this <a href=\n\t\t * \"https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1\">issuer</a>.\n\t\t * @param issuer the required issuer\n\t\t * @return the {@link AtJwtBuilder} for further configuration\n\t\t */\n\t\tpublic AtJwtBuilder issuer(String issuer) {\n\t\t\treturn validators((v) -> v.put(JwtClaimNames.ISS, new JwtIssuerValidator(issuer)));\n\t\t}\n\n\t\t/**\n\t\t * Validate that each token has this <a href=\n\t\t * \"https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3\">audience</a>.\n\t\t * @param audience the required audience\n\t\t * @return the {@link AtJwtBuilder} for further configuration\n\t\t */\n\t\tpublic AtJwtBuilder audience(String audience) {\n\t\t\treturn validators((v) -> v.put(JwtClaimNames.AUD, new JwtAudienceValidator(audience)));\n\t\t}\n\n\t\t/**\n\t\t * Validate that each token has this <a href=\n\t\t * \"https://datatracker.ietf.org/doc/html/rfc8693#name-client_id-client-identifier\">client_id</a>.\n\t\t * @param clientId the client identifier to use\n\t\t * @return the {@link AtJwtBuilder} for further configuration\n\t\t */\n\t\tpublic AtJwtBuilder clientId(String clientId) {\n\t\t\treturn validators((v) -> v.put(\"client_id\", require(\"client_id\").isEqualTo(clientId)));\n\t\t}\n\n\t\t/**\n\t\t * Mutate the list of validators by claim name.\n\t\t *\n\t\t * <p>\n\t\t * For example, to add a validator for\n\t\t * <a href=\"https://datatracker.ietf.org/doc/html/rfc9068#section-2.2.1\">azp</a>\n\t\t * do: <code>\n\t\t * \tbuilder.validators((v) -> v.put(\"acr\", myValidator()));\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * A validator is required for all required RFC 9068 claims.\n\t\t * @param validators the mutator for the map of validators\n\t\t * @return the {@link AtJwtBuilder} for further configuration\n\t\t */\n\t\tpublic AtJwtBuilder validators(Consumer<Map<String, OAuth2TokenValidator<Jwt>>> validators) {\n\t\t\tvalidators.accept(this.validators);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the validator\n\t\t * @return the RFC 9068 validator\n\t\t */\n\t\tpublic OAuth2TokenValidator<Jwt> build() {\n\t\t\tList.of(JoseHeaderNames.TYP, JwtClaimNames.EXP, JwtClaimNames.SUB, JwtClaimNames.IAT, JwtClaimNames.JTI,\n\t\t\t\t\tJwtClaimNames.ISS, JwtClaimNames.AUD, \"client_id\")\n\t\t\t\t.forEach((name) -> Assert.isTrue(this.validators.containsKey(name), name + \" must be validated\"));\n\t\t\treturn new DelegatingOAuth2TokenValidator<>(this.validators.values());\n\t\t}\n\n\t}\n\n\tprivate static final class RequireClaimValidator implements OAuth2TokenValidator<Jwt> {\n\n\t\tprivate final String claimName;\n\n\t\tRequireClaimValidator(String claimName) {\n\t\t\tthis.claimName = claimName;\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2TokenValidatorResult validate(Jwt token) {\n\t\t\tif (token.getClaim(this.claimName) == null) {\n\t\t\t\treturn OAuth2TokenValidatorResult\n\t\t\t\t\t.failure(new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, this.claimName + \" must have a value\",\n\t\t\t\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc9068#name-data-structure\"));\n\t\t\t}\n\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t}\n\n\t\tOAuth2TokenValidator<Jwt> isEqualTo(String value) {\n\t\t\treturn and(satisfies((jwt) -> value.equals(jwt.getClaim(this.claimName))));\n\t\t}\n\n\t\tOAuth2TokenValidator<Jwt> satisfies(Predicate<Jwt> predicate) {\n\t\t\treturn and((jwt) -> {\n\t\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, this.claimName + \" is not valid\",\n\t\t\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc9068#name-data-structure\");\n\t\t\t\tif (predicate.test(jwt)) {\n\t\t\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t\t\t}\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t});\n\t\t}\n\n\t\tOAuth2TokenValidator<Jwt> and(OAuth2TokenValidator<Jwt> that) {\n\t\t\treturn (jwt) -> {\n\t\t\t\tOAuth2TokenValidatorResult result = validate(jwt);\n\t\t\t\treturn (result.hasErrors()) ? result : that.validate(jwt);\n\t\t\t};\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URI;\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.ConversionService;\nimport org.springframework.core.convert.TypeDescriptor;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.core.converter.ClaimConversionService;\nimport org.springframework.security.oauth2.core.converter.ClaimTypeConverter;\nimport org.springframework.util.Assert;\n\n/**\n * Converts a JWT claim set, claim by claim. Can be configured with custom converters by\n * claim name.\n *\n * @author Josh Cummings\n * @since 5.1\n * @see ClaimTypeConverter\n */\npublic final class MappedJwtClaimSetConverter implements Converter<Map<String, Object>, Map<String, Object>> {\n\n\tprivate static final ConversionService CONVERSION_SERVICE = ClaimConversionService.getSharedInstance();\n\n\tprivate static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);\n\n\tprivate static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);\n\n\tprivate static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class);\n\n\tprivate static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class);\n\n\tprivate final Map<String, Converter<Object, ? extends @Nullable Object>> claimTypeConverters;\n\n\t/**\n\t * Constructs a {@link MappedJwtClaimSetConverter} with the provided arguments\n\t *\n\t * This will completely replace any set of default converters.\n\t *\n\t * A converter that returns {@code null} removes the claim from the claim set. A\n\t * converter that returns a non-{@code null} value adds or replaces that claim in the\n\t * claim set.\n\t * @param claimTypeConverters The {@link Map} of converters to use\n\t */\n\tpublic MappedJwtClaimSetConverter(Map<String, Converter<Object, ? extends @Nullable Object>> claimTypeConverters) {\n\t\tAssert.notNull(claimTypeConverters, \"claimTypeConverters cannot be null\");\n\t\tthis.claimTypeConverters = claimTypeConverters;\n\t}\n\n\t/**\n\t * Construct a {@link MappedJwtClaimSetConverter}, overriding individual claim\n\t * converters with the provided {@link Map} of {@link Converter}s.\n\t *\n\t * For example, the following would give an instance that is configured with only the\n\t * default claim converters:\n\t *\n\t * <pre>\n\t * \tMappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\t * </pre>\n\t *\n\t * Or, the following would supply a custom converter for the subject, leaving the\n\t * other defaults in place:\n\t *\n\t * <pre>\n\t * \tMappedJwtClaimsSetConverter.withDefaults(\n\t * \t\tCollections.singletonMap(JwtClaimNames.SUB, new UserDetailsServiceJwtSubjectConverter()));\n\t * </pre>\n\t *\n\t * To completely replace the underlying {@link Map} of converters, see\n\t * {@link MappedJwtClaimSetConverter#MappedJwtClaimSetConverter(Map)}.\n\t *\n\t * A converter that returns {@code null} removes the claim from the claim set. A\n\t * converter that returns a non-{@code null} value adds or replaces that claim in the\n\t * claim set.\n\t * @param claimTypeConverters\n\t * @return An instance of {@link MappedJwtClaimSetConverter} that contains the\n\t * converters provided, plus any defaults that were not overridden.\n\t */\n\tpublic static MappedJwtClaimSetConverter withDefaults(\n\t\t\tMap<String, Converter<Object, ? extends @Nullable Object>> claimTypeConverters) {\n\t\tAssert.notNull(claimTypeConverters, \"claimTypeConverters cannot be null\");\n\t\tConverter<Object, ? extends @Nullable Object> stringConverter = getConverter(STRING_TYPE_DESCRIPTOR);\n\t\tConverter<Object, ? extends @Nullable Object> collectionStringConverter = getConverter(\n\t\t\t\tTypeDescriptor.collection(Collection.class, STRING_TYPE_DESCRIPTOR));\n\t\tMap<String, Converter<Object, ? extends @Nullable Object>> claimNameToConverter = new HashMap<>();\n\t\tclaimNameToConverter.put(JwtClaimNames.AUD, collectionStringConverter);\n\t\tclaimNameToConverter.put(JwtClaimNames.EXP, MappedJwtClaimSetConverter::convertInstant);\n\t\tclaimNameToConverter.put(JwtClaimNames.IAT, MappedJwtClaimSetConverter::convertInstant);\n\t\tclaimNameToConverter.put(JwtClaimNames.ISS, MappedJwtClaimSetConverter::convertIssuer);\n\t\tclaimNameToConverter.put(JwtClaimNames.JTI, stringConverter);\n\t\tclaimNameToConverter.put(JwtClaimNames.NBF, MappedJwtClaimSetConverter::convertInstant);\n\t\tclaimNameToConverter.put(JwtClaimNames.SUB, stringConverter);\n\t\tclaimNameToConverter.putAll(claimTypeConverters);\n\t\treturn new MappedJwtClaimSetConverter(claimNameToConverter);\n\t}\n\n\tprivate static Converter<Object, ? extends @Nullable Object> getConverter(TypeDescriptor targetDescriptor) {\n\t\treturn (source) -> CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor);\n\t}\n\n\tprivate static @Nullable Instant convertInstant(Object source) {\n\t\tif (source == null) {\n\t\t\treturn null;\n\t\t}\n\t\tInstant result = (Instant) CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, INSTANT_TYPE_DESCRIPTOR);\n\t\tAssert.state(result != null, () -> \"Could not coerce \" + source + \" into an Instant\");\n\t\treturn result;\n\t}\n\n\tprivate static @Nullable String convertIssuer(Object source) {\n\t\tif (source == null) {\n\t\t\treturn null;\n\t\t}\n\t\tURL result = (URL) CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, URL_TYPE_DESCRIPTOR);\n\t\tif (result != null) {\n\t\t\treturn result.toExternalForm();\n\t\t}\n\t\tif (source instanceof String && ((String) source).contains(\":\")) {\n\t\t\ttry {\n\t\t\t\treturn new URI((String) source).toString();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalStateException(\"Could not coerce \" + source + \" into a URI String\", ex);\n\t\t\t}\n\t\t}\n\t\treturn (String) CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, STRING_TYPE_DESCRIPTOR);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> convert(Map<String, Object> claims) {\n\t\tAssert.notNull(claims, \"claims cannot be null\");\n\t\tMap<String, Object> mappedClaims = new HashMap<>(claims);\n\t\tfor (Map.Entry<String, Converter<Object, ? extends @Nullable Object>> entry : this.claimTypeConverters\n\t\t\t.entrySet()) {\n\t\t\tString claimName = entry.getKey();\n\t\t\tConverter<Object, ? extends @Nullable Object> converter = entry.getValue();\n\t\t\tObject claim = claims.get(claimName);\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\tObject mappedClaim = converter.convert(claim);\n\t\t\tmappedClaims.compute(claimName, (key, value) -> mappedClaim);\n\t\t}\n\t\tInstant issuedAt = (Instant) mappedClaims.get(JwtClaimNames.IAT);\n\t\tInstant expiresAt = (Instant) mappedClaims.get(JwtClaimNames.EXP);\n\t\tif (issuedAt == null && expiresAt != null) {\n\t\t\tmappedClaims.put(JwtClaimNames.IAT, expiresAt.minusSeconds(1));\n\t\t}\n\t\treturn mappedClaims;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URI;\nimport java.security.interfaces.RSAPublicKey;\nimport java.text.ParseException;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport javax.crypto.SecretKey;\n\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.JOSEObjectType;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.KeySourceException;\nimport com.nimbusds.jose.RemoteKeySourceException;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSetCacheRefreshEvaluator;\nimport com.nimbusds.jose.jwk.source.JWKSetSource;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.jwk.source.JWKSourceBuilder;\nimport com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;\nimport com.nimbusds.jose.proc.JOSEObjectTypeVerifier;\nimport com.nimbusds.jose.proc.JWSKeySelector;\nimport com.nimbusds.jose.proc.JWSVerificationKeySelector;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jose.proc.SingleKeyJWSKeySelector;\nimport com.nimbusds.jwt.JWT;\nimport com.nimbusds.jwt.JWTClaimsSet;\nimport com.nimbusds.jwt.JWTParser;\nimport com.nimbusds.jwt.PlainJWT;\nimport com.nimbusds.jwt.proc.ConfigurableJWTProcessor;\nimport com.nimbusds.jwt.proc.DefaultJWTProcessor;\nimport com.nimbusds.jwt.proc.JWTProcessor;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.support.NoOpCache;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.client.SimpleClientHttpRequestFactory;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestOperations;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * A low-level Nimbus implementation of {@link JwtDecoder} which takes a raw Nimbus\n * configuration.\n *\n * @author Josh Cummings\n * @author Joe Grandja\n * @author Mykyta Bezverkhyi\n * @author Daeho Kwon\n * @author Andrey Litvitski\n * @since 5.2\n */\npublic final class NimbusJwtDecoder implements JwtDecoder {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final String DECODING_ERROR_MESSAGE_TEMPLATE = \"An error occurred while attempting to decode the Jwt: %s\";\n\n\tprivate final JWTProcessor<SecurityContext> jwtProcessor;\n\n\tprivate Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = MappedJwtClaimSetConverter\n\t\t.withDefaults(Collections.emptyMap());\n\n\tprivate OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefault();\n\n\t/**\n\t * Configures a {@link NimbusJwtDecoder} with the given parameters\n\t * @param jwtProcessor - the {@link JWTProcessor} to use\n\t */\n\tpublic NimbusJwtDecoder(JWTProcessor<SecurityContext> jwtProcessor) {\n\t\tAssert.notNull(jwtProcessor, \"jwtProcessor cannot be null\");\n\t\tthis.jwtProcessor = jwtProcessor;\n\t}\n\n\t/**\n\t * Use this {@link Jwt} Validator\n\t * @param jwtValidator - the Jwt Validator to use\n\t */\n\tpublic void setJwtValidator(OAuth2TokenValidator<Jwt> jwtValidator) {\n\t\tAssert.notNull(jwtValidator, \"jwtValidator cannot be null\");\n\t\tthis.jwtValidator = jwtValidator;\n\t}\n\n\t/**\n\t * Use the following {@link Converter} for manipulating the JWT's claim set\n\t * @param claimSetConverter the {@link Converter} to use\n\t */\n\tpublic void setClaimSetConverter(Converter<Map<String, Object>, Map<String, Object>> claimSetConverter) {\n\t\tAssert.notNull(claimSetConverter, \"claimSetConverter cannot be null\");\n\t\tthis.claimSetConverter = claimSetConverter;\n\t}\n\n\t/**\n\t * Decode and validate the JWT from its compact claims representation format\n\t * @param token the JWT value\n\t * @return a validated {@link Jwt}\n\t * @throws JwtException when the token is malformed or otherwise invalid\n\t */\n\t@Override\n\tpublic Jwt decode(String token) throws JwtException {\n\t\tJWT jwt = parse(token);\n\t\tif (jwt instanceof PlainJWT) {\n\t\t\tthis.logger.trace(\"Failed to decode unsigned token\");\n\t\t\tthrow new BadJwtException(\"Unsupported algorithm of \" + jwt.getHeader().getAlgorithm());\n\t\t}\n\t\tJwt createdJwt = createJwt(token, jwt);\n\t\treturn validateJwt(createdJwt);\n\t}\n\n\tprivate JWT parse(String token) {\n\t\ttry {\n\t\t\treturn JWTParser.parse(token);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthis.logger.trace(\"Failed to parse token\", ex);\n\t\t\tif (ex instanceof ParseException) {\n\t\t\t\tthrow new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, \"Malformed token\"), ex);\n\t\t\t}\n\t\t\tthrow new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);\n\t\t}\n\t}\n\n\tprivate Jwt createJwt(String token, JWT parsedJwt) {\n\t\ttry {\n\t\t\t// Verify the signature\n\t\t\tJWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null);\n\t\t\tMap<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());\n\t\t\tMap<String, Object> claims = this.claimSetConverter.convert(jwtClaimsSet.getClaims());\n\t\t\t// @formatter:off\n\t\t\treturn Jwt.withTokenValue(token)\n\t\t\t\t\t.headers((h) -> h.putAll(headers))\n\t\t\t\t\t.claims((c) -> c.putAll(claims))\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\t\tcatch (RemoteKeySourceException ex) {\n\t\t\tthis.logger.trace(\"Failed to retrieve JWK set\", ex);\n\t\t\tif (ex.getCause() instanceof ParseException) {\n\t\t\t\tthrow new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, \"Malformed Jwk set\"), ex);\n\t\t\t}\n\t\t\tthrow new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);\n\t\t}\n\t\tcatch (JOSEException ex) {\n\t\t\tthis.logger.trace(\"Failed to process JWT\", ex);\n\t\t\tthrow new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthis.logger.trace(\"Failed to process JWT\", ex);\n\t\t\tif (ex.getCause() instanceof ParseException) {\n\t\t\t\tthrow new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, \"Malformed payload\"), ex);\n\t\t\t}\n\t\t\tthrow new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);\n\t\t}\n\t}\n\n\tprivate Jwt validateJwt(Jwt jwt) {\n\t\tOAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt);\n\t\tif (result.hasErrors()) {\n\t\t\tCollection<OAuth2Error> errors = result.getErrors();\n\t\t\tString validationErrorString = getJwtValidationExceptionMessage(errors);\n\t\t\tthrow new JwtValidationException(validationErrorString, errors);\n\t\t}\n\t\treturn jwt;\n\t}\n\n\tprivate String getJwtValidationExceptionMessage(Collection<OAuth2Error> errors) {\n\t\tfor (OAuth2Error oAuth2Error : errors) {\n\t\t\tif (StringUtils.hasLength(oAuth2Error.getDescription())) {\n\t\t\t\treturn String.format(DECODING_ERROR_MESSAGE_TEMPLATE, oAuth2Error.getDescription());\n\t\t\t}\n\t\t}\n\t\treturn \"Unable to validate Jwt\";\n\t}\n\n\t/**\n\t * Use the given <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * by making an <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">OpenID\n\t * Provider Configuration Request</a> and using the values in the <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">OpenID\n\t * Provider Configuration Response</a> to derive the needed\n\t * <a href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK Set</a> uri.\n\t * @param issuer the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return a {@link JwkSetUriJwtDecoderBuilder} that will derive the JWK Set uri when\n\t * {@link JwkSetUriJwtDecoderBuilder#build} is called\n\t * @since 6.1\n\t * @see JwtDecoders\n\t */\n\tpublic static JwkSetUriJwtDecoderBuilder withIssuerLocation(String issuer) {\n\t\treturn new JwkSetUriJwtDecoderBuilder((rest) -> {\n\t\t\tMap<String, Object> configuration = JwtDecoderProviderConfigurationUtils\n\t\t\t\t.getConfigurationForIssuerLocation(issuer, rest);\n\t\t\tJwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);\n\t\t\tObject jwksUri = configuration.get(\"jwks_uri\");\n\t\t\tAssert.notNull(jwksUri, \"The public JWK Set URI must not be null\");\n\t\t\treturn jwksUri.toString();\n\t\t}, JwtDecoderProviderConfigurationUtils::getJWSAlgorithms);\n\t}\n\n\t/**\n\t * Use the given <a href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK Set</a>\n\t * uri.\n\t * @param jwkSetUri the JWK Set uri to use\n\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t */\n\tpublic static JwkSetUriJwtDecoderBuilder withJwkSetUri(String jwkSetUri) {\n\t\treturn new JwkSetUriJwtDecoderBuilder(jwkSetUri);\n\t}\n\n\t/**\n\t * Use the given public key to validate JWTs\n\t * @param key the public key to use\n\t * @return a {@link PublicKeyJwtDecoderBuilder} for further configurations\n\t */\n\tpublic static PublicKeyJwtDecoderBuilder withPublicKey(RSAPublicKey key) {\n\t\treturn new PublicKeyJwtDecoderBuilder(key);\n\t}\n\n\t/**\n\t * Use the given {@code SecretKey} to validate the MAC on a JSON Web Signature (JWS).\n\t * @param secretKey the {@code SecretKey} used to validate the MAC\n\t * @return a {@link SecretKeyJwtDecoderBuilder} for further configurations\n\t */\n\tpublic static SecretKeyJwtDecoderBuilder withSecretKey(SecretKey secretKey) {\n\t\treturn new SecretKeyJwtDecoderBuilder(secretKey);\n\t}\n\n\t/**\n\t * Use the given {@code JWKSource} to create a JwkSourceJwtDecoderBuilder.\n\t * @param jwkSource the JWK Source to use\n\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t * @since 7.0\n\t */\n\tpublic static JwkSourceJwtDecoderBuilder withJwkSource(JWKSource<SecurityContext> jwkSource) {\n\t\treturn new JwkSourceJwtDecoderBuilder(jwkSource);\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusJwtDecoder} instances based on a\n\t * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK Set</a>\n\t * uri.\n\t */\n\tpublic static final class JwkSetUriJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\t\tJOSEObjectType.JWT, null);\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {\n\t\t};\n\n\t\tprivate final Function<RestOperations, String> jwkSetUri;\n\n\t\tprivate Function<JWKSource<SecurityContext>, Set<JWSAlgorithm>> defaultAlgorithms = (source) -> Set\n\t\t\t.of(JWSAlgorithm.RS256);\n\n\t\tprivate JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;\n\n\t\tprivate final Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();\n\n\t\tprivate RestOperations restOperations = new RestTemplateWithNimbusDefaultTimeouts();\n\n\t\tprivate Cache cache = new NoOpCache(\"default\");\n\n\t\tprivate Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;\n\n\t\tprivate JwkSetUriJwtDecoderBuilder(String jwkSetUri) {\n\t\t\tAssert.hasText(jwkSetUri, \"jwkSetUri cannot be empty\");\n\t\t\tthis.jwkSetUri = (rest) -> jwkSetUri;\n\t\t\tthis.jwtProcessorCustomizer = (processor) -> {\n\t\t\t};\n\t\t}\n\n\t\tprivate JwkSetUriJwtDecoderBuilder(Function<RestOperations, String> jwkSetUri,\n\t\t\t\tFunction<JWKSource<SecurityContext>, Set<JWSAlgorithm>> defaultAlgorithms) {\n\t\t\tAssert.notNull(jwkSetUri, \"jwkSetUri function cannot be null\");\n\t\t\tAssert.notNull(defaultAlgorithms, \"defaultAlgorithms function cannot be null\");\n\t\t\tthis.jwkSetUri = jwkSetUri;\n\t\t\tthis.defaultAlgorithms = defaultAlgorithms;\n\t\t\tthis.jwtProcessorCustomizer = (processor) -> {\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Whether to use Nimbus's {@code typ} header verification. This is {@code false}\n\t\t * by default.\n\t\t *\n\t\t * <p>\n\t\t * By turning on this feature, {@link NimbusJwtDecoder} will delegate checking the\n\t\t * {@code typ} header to Nimbus by using Nimbus's default\n\t\t * {@link JOSEObjectTypeVerifier}.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * When this is set to {@code false}, this: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * Is equivalent to this: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\t *     \t\tnew JwtIssuerValidator(issuer), JwtTypeValidator.jwt());\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * The difference is that by setting this to {@code false}, it allows you to\n\t\t * provide validation by type, like for {@code at+jwt}: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(new MyAtJwtValidator());\n\t\t * </code>\n\t\t * @param shouldValidateTypHeader whether Nimbus should validate the typ header or\n\t\t * not\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t\t * @since 6.5\n\t\t */\n\t\tpublic JwkSetUriJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {\n\t\t\tthis.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Append the given signing\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithm</a> to the set of algorithms to use.\n\t\t * @param signatureAlgorithm the algorithm to use\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic JwkSetUriJwtDecoderBuilder jwsAlgorithm(SignatureAlgorithm signatureAlgorithm) {\n\t\t\tAssert.notNull(signatureAlgorithm, \"signatureAlgorithm cannot be null\");\n\t\t\tthis.signatureAlgorithms.add(signatureAlgorithm);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure the list of\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithms</a> to use with the given {@link Consumer}.\n\t\t * @param signatureAlgorithmsConsumer a {@link Consumer} for further configuring\n\t\t * the algorithm list\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic JwkSetUriJwtDecoderBuilder jwsAlgorithms(Consumer<Set<SignatureAlgorithm>> signatureAlgorithmsConsumer) {\n\t\t\tAssert.notNull(signatureAlgorithmsConsumer, \"signatureAlgorithmsConsumer cannot be null\");\n\t\t\tsignatureAlgorithmsConsumer.accept(this.signatureAlgorithms);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link RestOperations} to coordinate with the authorization\n\t\t * servers indicated in the\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK Set</a> uri as well\n\t\t * as the <a href=\n\t\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>.\n\t\t * @param restOperations the {@link RestOperations} instance to use\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic JwkSetUriJwtDecoderBuilder restOperations(RestOperations restOperations) {\n\t\t\tAssert.notNull(restOperations, \"restOperations cannot be null\");\n\t\t\tthis.restOperations = restOperations;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Cache} to store\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK Set</a>.\n\t\t * @param cache the {@link Cache} to be used to store JWK Set\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic JwkSetUriJwtDecoderBuilder cache(Cache cache) {\n\t\t\tAssert.notNull(cache, \"cache cannot be null\");\n\t\t\tthis.cache = cache;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Enables discovery of supported JWS algorithms from the remote JWK Set.\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configuration\n\t\t * @since 7.0.0\n\t\t */\n\t\tpublic JwkSetUriJwtDecoderBuilder discoverJwsAlgorithms() {\n\t\t\tthis.defaultAlgorithms = JwtDecoderProviderConfigurationUtils::getJWSAlgorithms;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Consumer} to customize the {@link JWTProcessor\n\t\t * ConfigurableJWTProcessor} before passing it to the build\n\t\t * {@link NimbusJwtDecoder}.\n\t\t * @param jwtProcessorCustomizer the callback used to alter the processor\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic JwkSetUriJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tConsumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = jwtProcessorCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t\tJWSKeySelector<SecurityContext> jwsKeySelector(JWKSource<SecurityContext> jwkSource) {\n\t\t\tif (this.signatureAlgorithms.isEmpty()) {\n\t\t\t\treturn new JWSVerificationKeySelector<>(this.defaultAlgorithms.apply(jwkSource), jwkSource);\n\t\t\t}\n\t\t\tSet<JWSAlgorithm> jwsAlgorithms = new HashSet<>();\n\t\t\tfor (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) {\n\t\t\t\tJWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());\n\t\t\t\tjwsAlgorithms.add(jwsAlgorithm);\n\t\t\t}\n\t\t\treturn new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource);\n\t\t}\n\n\t\tJWKSource<SecurityContext> jwkSource() {\n\t\t\tString jwkSetUri = this.jwkSetUri.apply(this.restOperations);\n\t\t\treturn JWKSourceBuilder.create(new SpringJWKSource<>(this.restOperations, this.cache, jwkSetUri))\n\t\t\t\t.refreshAheadCache(false)\n\t\t\t\t.rateLimited(false)\n\t\t\t\t.cache(this.cache instanceof NoOpCache)\n\t\t\t\t.build();\n\t\t}\n\n\t\tJWTProcessor<SecurityContext> processor() {\n\t\t\tJWKSource<SecurityContext> jwkSource = jwkSource();\n\t\t\tConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\t\tjwtProcessor.setJWSTypeVerifier(this.typeVerifier);\n\t\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector(jwkSource));\n\t\t\t// Spring Security validates the claim set independent from Nimbus\n\t\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t\t});\n\t\t\tthis.jwtProcessorCustomizer.accept(jwtProcessor);\n\t\t\treturn jwtProcessor;\n\t\t}\n\n\t\t/**\n\t\t * Build the configured {@link NimbusJwtDecoder}.\n\t\t * @return the configured {@link NimbusJwtDecoder}\n\t\t */\n\t\tpublic NimbusJwtDecoder build() {\n\t\t\treturn new NimbusJwtDecoder(processor());\n\t\t}\n\n\t\tprivate static final class SpringJWKSource<C extends SecurityContext> implements JWKSetSource<C> {\n\n\t\t\tprivate static final MediaType APPLICATION_JWK_SET_JSON = new MediaType(\"application\", \"jwk-set+json\");\n\n\t\t\tprivate final ReentrantLock reentrantLock = new ReentrantLock();\n\n\t\t\tprivate final RestOperations restOperations;\n\n\t\t\tprivate final Cache cache;\n\n\t\t\tprivate final String jwkSetUri;\n\n\t\t\tprivate @Nullable JWKSet jwkSet;\n\n\t\t\tprivate SpringJWKSource(RestOperations restOperations, Cache cache, String jwkSetUri) {\n\t\t\t\tAssert.notNull(restOperations, \"restOperations cannot be null\");\n\t\t\t\tthis.restOperations = restOperations;\n\t\t\t\tthis.cache = cache;\n\t\t\t\tthis.jwkSetUri = jwkSetUri;\n\t\t\t\tString jwks = this.cache.get(this.jwkSetUri, String.class);\n\t\t\t\tif (jwks != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.jwkSet = JWKSet.parse(jwks);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (ParseException ignored) {\n\t\t\t\t\t\t// Ignore invalid cache value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate String fetchJwks() throws Exception {\n\t\t\t\tHttpHeaders headers = new HttpHeaders();\n\t\t\t\theaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON));\n\t\t\t\tRequestEntity<Void> request = new RequestEntity<>(headers, HttpMethod.GET, URI.create(this.jwkSetUri));\n\t\t\t\tResponseEntity<String> response = this.restOperations.exchange(request, String.class);\n\t\t\t\tString jwks = response.getBody();\n\t\t\t\tAssert.notNull(jwks, \"JWK Set response body must not be null\");\n\t\t\t\tthis.jwkSet = JWKSet.parse(jwks);\n\t\t\t\treturn jwks;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic JWKSet getJWKSet(JWKSetCacheRefreshEvaluator refreshEvaluator, long currentTime, C context)\n\t\t\t\t\tthrows KeySourceException {\n\t\t\t\ttry {\n\t\t\t\t\tthis.reentrantLock.lock();\n\t\t\t\t\tif (refreshEvaluator.requiresRefresh(this.jwkSet)) {\n\t\t\t\t\t\tthis.cache.invalidate();\n\t\t\t\t\t}\n\t\t\t\t\tthis.cache.get(this.jwkSetUri, this::fetchJwks);\n\t\t\t\t\tAssert.notNull(this.jwkSet, \"JWK Set must not be null\");\n\t\t\t\t\treturn this.jwkSet;\n\t\t\t\t}\n\t\t\t\tcatch (Cache.ValueRetrievalException ex) {\n\t\t\t\t\tThrowable cause = ex.getCause();\n\t\t\t\t\tif (cause instanceof RemoteKeySourceException keys) {\n\t\t\t\t\t\tthrow keys;\n\t\t\t\t\t}\n\t\t\t\t\tif (cause != null) {\n\t\t\t\t\t\tthrow new RemoteKeySourceException(cause.getMessage(), cause);\n\t\t\t\t\t}\n\t\t\t\t\tthrow new RemoteKeySourceException(ex.getMessage(), null);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tthis.reentrantLock.unlock();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() {\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * A RestTemplate with timeouts configured to avoid blocking indefinitely when\n\t * fetching JWK Sets while holding the reentrantLock.\n\t */\n\tprivate static final class RestTemplateWithNimbusDefaultTimeouts extends RestTemplate {\n\n\t\tprivate RestTemplateWithNimbusDefaultTimeouts() {\n\t\t\tSimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();\n\t\t\trequestFactory.setConnectTimeout(JWKSourceBuilder.DEFAULT_HTTP_CONNECT_TIMEOUT);\n\t\t\trequestFactory.setReadTimeout(JWKSourceBuilder.DEFAULT_HTTP_READ_TIMEOUT);\n\t\t\tsetRequestFactory(requestFactory);\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusJwtDecoder} instances based on a public key.\n\t */\n\tpublic static final class PublicKeyJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\t\tJOSEObjectType.JWT, null);\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {\n\t\t};\n\n\t\tprivate JWSAlgorithm jwsAlgorithm;\n\n\t\tprivate JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;\n\n\t\tprivate final RSAPublicKey key;\n\n\t\tprivate Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;\n\n\t\tprivate PublicKeyJwtDecoderBuilder(RSAPublicKey key) {\n\t\t\tAssert.notNull(key, \"key cannot be null\");\n\t\t\tthis.jwsAlgorithm = JWSAlgorithm.RS256;\n\t\t\tthis.key = key;\n\t\t\tthis.jwtProcessorCustomizer = (processor) -> {\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Whether to use Nimbus's {@code typ} header verification. This is {@code false}\n\t\t * by default.\n\t\t *\n\t\t * <p>\n\t\t * By turning on this feature, {@link NimbusJwtDecoder} will delegate checking the\n\t\t * {@code typ} header to Nimbus by using Nimbus's default\n\t\t * {@link JOSEObjectTypeVerifier}.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * When this is set to {@code false}, this: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * Is equivalent to this: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\t *     \t\tnew JwtIssuerValidator(issuer), JwtTypeValidator.jwt());\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * The difference is that by setting this to {@code false}, it allows you to\n\t\t * provide validation by type, like for {@code at+jwt}: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(new MyAtJwtValidator());\n\t\t * </code>\n\t\t * @param shouldValidateTypHeader whether Nimbus should validate the typ header or\n\t\t * not\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t\t * @since 6.5\n\t\t */\n\t\tpublic PublicKeyJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {\n\t\t\tthis.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given signing\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithm</a>. The value should be one of\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7518#section-3.3\" target=\n\t\t * \"_blank\">RS256, RS384, or RS512</a>.\n\t\t * @param signatureAlgorithm the algorithm to use\n\t\t * @return a {@link PublicKeyJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic PublicKeyJwtDecoderBuilder signatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {\n\t\t\tAssert.notNull(signatureAlgorithm, \"signatureAlgorithm cannot be null\");\n\t\t\tthis.jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Consumer} to customize the {@link JWTProcessor\n\t\t * ConfigurableJWTProcessor} before passing it to the build\n\t\t * {@link NimbusJwtDecoder}.\n\t\t * @param jwtProcessorCustomizer the callback used to alter the processor\n\t\t * @return a {@link PublicKeyJwtDecoderBuilder} for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic PublicKeyJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tConsumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = jwtProcessorCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t\tJWTProcessor<SecurityContext> processor() {\n\t\t\tAssert.state(JWSAlgorithm.Family.RSA.contains(this.jwsAlgorithm),\n\t\t\t\t\t() -> \"The provided key is of type RSA; however the signature algorithm is of some other type: \"\n\t\t\t\t\t\t\t+ this.jwsAlgorithm + \". Please indicate one of RS256, RS384, or RS512.\");\n\t\t\tJWSKeySelector<SecurityContext> jwsKeySelector = new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.key);\n\t\t\tDefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\t\tjwtProcessor.setJWSTypeVerifier(this.typeVerifier);\n\t\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector);\n\t\t\t// Spring Security validates the claim set independent from Nimbus\n\t\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t\t});\n\t\t\tthis.jwtProcessorCustomizer.accept(jwtProcessor);\n\t\t\treturn jwtProcessor;\n\t\t}\n\n\t\t/**\n\t\t * Build the configured {@link NimbusJwtDecoder}.\n\t\t * @return the configured {@link NimbusJwtDecoder}\n\t\t */\n\t\tpublic NimbusJwtDecoder build() {\n\t\t\treturn new NimbusJwtDecoder(processor());\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusJwtDecoder} instances based on a\n\t * {@code SecretKey}.\n\t */\n\tpublic static final class SecretKeyJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\t\tJOSEObjectType.JWT, null);\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {\n\t\t};\n\n\t\tprivate final SecretKey secretKey;\n\n\t\tprivate JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;\n\n\t\tprivate JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;\n\n\t\tprivate Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;\n\n\t\tprivate SecretKeyJwtDecoderBuilder(SecretKey secretKey) {\n\t\t\tAssert.notNull(secretKey, \"secretKey cannot be null\");\n\t\t\tthis.secretKey = secretKey;\n\t\t\tthis.jwtProcessorCustomizer = (processor) -> {\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Whether to use Nimbus's {@code typ} header verification. This is {@code false}\n\t\t * by default.\n\t\t *\n\t\t * <p>\n\t\t * By turning on this feature, {@link NimbusJwtDecoder} will delegate checking the\n\t\t * {@code typ} header to Nimbus by using Nimbus's default\n\t\t * {@link JOSEObjectTypeVerifier}.\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * When this is set to {@code false}, this: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * Is equivalent to this: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\t *     \t\tnew JwtIssuerValidator(issuer), JwtTypeValidator.jwt());\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * The difference is that by setting this to {@code false}, it allows you to\n\t\t * provide validation by type, like for {@code at+jwt}: <code>\n\t\t *     NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(new MyAtJwtValidator());\n\t\t * </code>\n\t\t * @param shouldValidateTypHeader whether Nimbus should validate the typ header or\n\t\t * not\n\t\t * @return a {@link JwkSetUriJwtDecoderBuilder} for further configurations\n\t\t * @since 6.5\n\t\t */\n\t\tpublic SecretKeyJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {\n\t\t\tthis.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithm</a> when generating the MAC. The value should be one of\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7518#section-3.2\" target=\n\t\t * \"_blank\">HS256, HS384 or HS512</a>.\n\t\t * @param macAlgorithm the MAC algorithm to use\n\t\t * @return a {@link SecretKeyJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic SecretKeyJwtDecoderBuilder macAlgorithm(MacAlgorithm macAlgorithm) {\n\t\t\tAssert.notNull(macAlgorithm, \"macAlgorithm cannot be null\");\n\t\t\tthis.jwsAlgorithm = JWSAlgorithm.parse(macAlgorithm.getName());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Consumer} to customize the {@link JWTProcessor\n\t\t * ConfigurableJWTProcessor} before passing it to the build\n\t\t * {@link NimbusJwtDecoder}.\n\t\t * @param jwtProcessorCustomizer the callback used to alter the processor\n\t\t * @return a {@link SecretKeyJwtDecoderBuilder} for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic SecretKeyJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tConsumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = jwtProcessorCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the configured {@link NimbusJwtDecoder}.\n\t\t * @return the configured {@link NimbusJwtDecoder}\n\t\t */\n\t\tpublic NimbusJwtDecoder build() {\n\t\t\treturn new NimbusJwtDecoder(processor());\n\t\t}\n\n\t\tJWTProcessor<SecurityContext> processor() {\n\t\t\tJWSKeySelector<SecurityContext> jwsKeySelector = new SingleKeyJWSKeySelector<>(this.jwsAlgorithm,\n\t\t\t\t\tthis.secretKey);\n\t\t\tDefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector);\n\t\t\tjwtProcessor.setJWSTypeVerifier(this.typeVerifier);\n\t\t\t// Spring Security validates the claim set independent from Nimbus\n\t\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t\t});\n\t\t\tthis.jwtProcessorCustomizer.accept(jwtProcessor);\n\t\t\treturn jwtProcessor;\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusJwtDecoder} instances based on a\n\t * {@code JWKSource}.\n\t *\n\t * @since 7.0\n\t */\n\tpublic static final class JwkSourceJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {\n\t\t};\n\n\t\tprivate final Function<JWKSource<SecurityContext>, Set<JWSAlgorithm>> defaultAlgorithms = (source) -> Set\n\t\t\t.of(JWSAlgorithm.RS256);\n\n\t\tprivate final JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;\n\n\t\tprivate final Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();\n\n\t\tprivate Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;\n\n\t\tprivate final JWKSource<SecurityContext> jwkSource;\n\n\t\tprivate JwkSourceJwtDecoderBuilder(JWKSource<SecurityContext> jwkSource) {\n\t\t\tAssert.notNull(jwkSource, \"jwkSource cannot be null\");\n\t\t\tthis.jwkSource = jwkSource;\n\t\t\tthis.jwtProcessorCustomizer = (processor) -> {\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Append the given signing\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithm</a> to the set of algorithms to use.\n\t\t * @param signatureAlgorithm the algorithm to use\n\t\t * @return a {@link JwkSourceJwtDecoderBuilder } for further configurations\n\t\t */\n\t\tpublic JwkSourceJwtDecoderBuilder jwsAlgorithm(SignatureAlgorithm signatureAlgorithm) {\n\t\t\tAssert.notNull(signatureAlgorithm, \"signatureAlgorithm cannot be null\");\n\t\t\tthis.signatureAlgorithms.add(signatureAlgorithm);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure the list of\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithms</a> to use with the given {@link Consumer}.\n\t\t * @param signatureAlgorithmsConsumer a {@link Consumer} for further configuring\n\t\t * the algorithm list\n\t\t * @return a {@link JwkSourceJwtDecoderBuilder } for further configurations\n\t\t */\n\t\tpublic JwkSourceJwtDecoderBuilder jwsAlgorithms(Consumer<Set<SignatureAlgorithm>> signatureAlgorithmsConsumer) {\n\t\t\tAssert.notNull(signatureAlgorithmsConsumer, \"signatureAlgorithmsConsumer cannot be null\");\n\t\t\tsignatureAlgorithmsConsumer.accept(this.signatureAlgorithms);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Consumer} to customize the {@link JWTProcessor\n\t\t * ConfigurableJWTProcessor} before passing it to the build\n\t\t * {@link NimbusJwtDecoder}.\n\t\t * @param jwtProcessorCustomizer the callback used to alter the processor\n\t\t * @return a {@link JwkSourceJwtDecoderBuilder } for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic JwkSourceJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tConsumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = jwtProcessorCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t\tJWSKeySelector<SecurityContext> jwsKeySelector(JWKSource<SecurityContext> jwkSource) {\n\t\t\tif (this.signatureAlgorithms.isEmpty()) {\n\t\t\t\treturn new JWSVerificationKeySelector<>(this.defaultAlgorithms.apply(jwkSource), jwkSource);\n\t\t\t}\n\t\t\tSet<JWSAlgorithm> jwsAlgorithms = new HashSet<>();\n\t\t\tfor (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) {\n\t\t\t\tJWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());\n\t\t\t\tjwsAlgorithms.add(jwsAlgorithm);\n\t\t\t}\n\t\t\treturn new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource);\n\t\t}\n\n\t\tJWTProcessor<SecurityContext> processor() {\n\t\t\tConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\t\tjwtProcessor.setJWSTypeVerifier(this.typeVerifier);\n\t\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector(this.jwkSource));\n\t\t\t// Spring Security validates the claim set independent from Nimbus\n\t\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t\t});\n\t\t\tthis.jwtProcessorCustomizer.accept(jwtProcessor);\n\t\t\treturn jwtProcessor;\n\t\t}\n\n\t\t/**\n\t\t * Build the configured {@link NimbusJwtDecoder}.\n\t\t * @return the configured {@link NimbusJwtDecoder}\n\t\t */\n\t\tpublic NimbusJwtDecoder build() {\n\t\t\treturn new NimbusJwtDecoder(processor());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URI;\nimport java.net.URL;\nimport java.security.KeyPair;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\n\nimport javax.crypto.SecretKey;\n\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.JOSEObjectType;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.JWSHeader;\nimport com.nimbusds.jose.JWSSigner;\nimport com.nimbusds.jose.crypto.MACSigner;\nimport com.nimbusds.jose.crypto.factories.DefaultJWSSignerFactory;\nimport com.nimbusds.jose.jwk.Curve;\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.KeyType;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.OctetSequenceKey;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.ImmutableJWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jose.produce.JWSSignerFactory;\nimport com.nimbusds.jose.util.Base64;\nimport com.nimbusds.jose.util.Base64URL;\nimport com.nimbusds.jwt.JWTClaimsSet;\nimport com.nimbusds.jwt.SignedJWT;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.util.function.ThrowingBiFunction;\nimport org.springframework.util.function.ThrowingFunction;\n\n/**\n * An implementation of a {@link JwtEncoder} that encodes a JSON Web Token (JWT) using the\n * JSON Web Signature (JWS) Compact Serialization format. The private/secret key used for\n * signing the JWS is supplied by the {@code com.nimbusds.jose.jwk.source.JWKSource}\n * provided via the constructor.\n *\n * <p>\n * <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK.\n *\n * @author Joe Grandja\n * @author Josh Cummings\n * @author Suraj Bhadrike\n * @since 5.6\n * @see JwtEncoder\n * @see com.nimbusds.jose.jwk.source.JWKSource\n * @see com.nimbusds.jose.jwk.JWK\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519\">JSON Web Token\n * (JWT)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515#section-3.1\">JWS\n * Compact Serialization</a>\n * @see <a target=\"_blank\" href=\"https://connect2id.com/products/nimbus-jose-jwt\">Nimbus\n * JOSE + JWT SDK</a>\n */\npublic final class NimbusJwtEncoder implements JwtEncoder {\n\n\tprivate static final String ENCODING_ERROR_MESSAGE_TEMPLATE = \"An error occurred while attempting to encode the Jwt: %s\";\n\n\tprivate static final JwsHeader DEFAULT_JWS_HEADER = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\n\tprivate static final JWSSignerFactory JWS_SIGNER_FACTORY = new DefaultJWSSignerFactory();\n\n\tprivate final JwsHeader defaultJwsHeader;\n\n\tprivate final Map<JWK, JWSSigner> jwsSigners = new ConcurrentHashMap<>();\n\n\tprivate final JWKSource<SecurityContext> jwkSource;\n\n\tprivate Converter<List<JWK>, JWK> jwkSelector = (jwks) -> {\n\t\tthrow new JwtEncodingException(\n\t\t\t\tString.format(\n\t\t\t\t\t\t\"Failed to select a key since there are multiple for the signing algorithm [%s]; \"\n\t\t\t\t\t\t\t\t+ \"please specify a selector in NimbusJwsEncoder#setJwkSelector\",\n\t\t\t\t\t\tjwks.get(0).getAlgorithm()));\n\t};\n\n\t/**\n\t * Constructs a {@code NimbusJwtEncoder} using the provided parameters.\n\t * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource}\n\t */\n\tpublic NimbusJwtEncoder(JWKSource<SecurityContext> jwkSource) {\n\t\tthis.defaultJwsHeader = DEFAULT_JWS_HEADER;\n\t\tAssert.notNull(jwkSource, \"jwkSource cannot be null\");\n\t\tthis.jwkSource = jwkSource;\n\t}\n\n\tprivate NimbusJwtEncoder(JWK jwk) {\n\t\tAssert.notNull(jwk, \"jwk cannot be null\");\n\t\tthis.jwkSource = new ImmutableJWKSet<>(new JWKSet(jwk));\n\t\tJwsAlgorithm algorithm = SignatureAlgorithm.from(jwk.getAlgorithm().getName());\n\t\tif (algorithm == null) {\n\t\t\talgorithm = MacAlgorithm.from(jwk.getAlgorithm().getName());\n\t\t}\n\t\tAssert.notNull(algorithm, \"Failed to derive supported algorithm from \" + jwk.getAlgorithm());\n\t\tJwsHeader.Builder builder = JwsHeader.with(algorithm).type(\"JWT\").keyId(jwk.getKeyID());\n\t\tURI x509Url = jwk.getX509CertURL();\n\t\tif (x509Url != null) {\n\t\t\tbuilder.x509Url(jwk.getX509CertURL().toASCIIString());\n\t\t}\n\t\tList<Base64> certs = jwk.getX509CertChain();\n\t\tif (certs != null) {\n\t\t\tbuilder.x509CertificateChain(certs.stream().map(Base64::toString).toList());\n\t\t}\n\t\tBase64URL thumbprint = jwk.getX509CertSHA256Thumbprint();\n\t\tif (thumbprint != null) {\n\t\t\tbuilder.x509SHA256Thumbprint(thumbprint.toString());\n\t\t}\n\t\tthis.defaultJwsHeader = builder.build();\n\t}\n\n\t/**\n\t * Use this strategy to reduce the list of matching JWKs when there is more than one.\n\t * <p>\n\t * For example, you can call {@code setJwkSelector(List::getFirst)} in order to have\n\t * this encoder select the first match.\n\t *\n\t * <p>\n\t * By default, the class with throw an exception.\n\t * @since 6.5\n\t */\n\tpublic void setJwkSelector(Converter<List<JWK>, JWK> jwkSelector) {\n\t\tAssert.notNull(jwkSelector, \"jwkSelector cannot be null\");\n\t\tthis.jwkSelector = jwkSelector;\n\t}\n\n\t@Override\n\tpublic Jwt encode(JwtEncoderParameters parameters) throws JwtEncodingException {\n\t\tAssert.notNull(parameters, \"parameters cannot be null\");\n\n\t\tJwsHeader headers = parameters.getJwsHeader();\n\t\tif (headers == null) {\n\t\t\theaders = this.defaultJwsHeader;\n\t\t}\n\n\t\tJwtClaimsSet claims = parameters.getClaims();\n\n\t\tJWK jwk = selectJwk(headers);\n\t\theaders = addKeyIdentifierHeadersIfNecessary(headers, jwk);\n\n\t\tString jws = serialize(headers, claims, jwk);\n\n\t\treturn new Jwt(jws, claims.getIssuedAt(), claims.getExpiresAt(), headers.getHeaders(), claims.getClaims());\n\t}\n\n\tprivate JWK selectJwk(JwsHeader headers) {\n\t\tList<JWK> jwks;\n\t\ttry {\n\t\t\tJWKSelector jwkSelector = new JWKSelector(createJwkMatcher(headers));\n\t\t\tjwks = this.jwkSource.get(jwkSelector, null);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,\n\t\t\t\t\t\"Failed to select a JWK signing key -> \" + ex.getMessage()), ex);\n\t\t}\n\t\tif (jwks.isEmpty()) {\n\t\t\tthrow new JwtEncodingException(\n\t\t\t\t\tString.format(ENCODING_ERROR_MESSAGE_TEMPLATE, \"Failed to select a JWK signing key\"));\n\t\t}\n\t\tif (jwks.size() == 1) {\n\t\t\treturn jwks.get(0);\n\t\t}\n\t\treturn this.jwkSelector.convert(jwks);\n\t}\n\n\tprivate String serialize(JwsHeader headers, JwtClaimsSet claims, JWK jwk) {\n\t\tJWSHeader jwsHeader = convert(headers);\n\t\tJWTClaimsSet jwtClaimsSet = convert(claims);\n\n\t\tJWSSigner jwsSigner = this.jwsSigners.computeIfAbsent(jwk, NimbusJwtEncoder::createSigner);\n\n\t\tSignedJWT signedJwt = new SignedJWT(jwsHeader, jwtClaimsSet);\n\t\ttry {\n\t\t\tsignedJwt.sign(jwsSigner);\n\t\t}\n\t\tcatch (JOSEException ex) {\n\t\t\tthrow new JwtEncodingException(\n\t\t\t\t\tString.format(ENCODING_ERROR_MESSAGE_TEMPLATE, \"Failed to sign the JWT -> \" + ex.getMessage()), ex);\n\t\t}\n\t\treturn signedJwt.serialize();\n\t}\n\n\tprivate static @Nullable JWKMatcher createJwkMatcher(JwsHeader headers) {\n\t\tJwsAlgorithm algorithm = headers.getAlgorithm();\n\t\tAssert.notNull(algorithm, \"JWS header algorithm must not be null\");\n\t\tJWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(algorithm.getName());\n\n\t\tif (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm) || JWSAlgorithm.Family.EC.contains(jwsAlgorithm)) {\n\t\t\t// @formatter:off\n\t\t\treturn new JWKMatcher.Builder()\n\t\t\t\t\t.keyType(KeyType.forAlgorithm(jwsAlgorithm))\n\t\t\t\t\t.keyID(headers.getKeyId())\n\t\t\t\t\t.keyUses(KeyUse.SIGNATURE, null)\n\t\t\t\t\t.algorithms(jwsAlgorithm, null)\n\t\t\t\t\t.x509CertSHA256Thumbprint(Base64URL.from(headers.getX509SHA256Thumbprint()))\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\t\telse if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {\n\t\t\t// @formatter:off\n\t\t\treturn new JWKMatcher.Builder()\n\t\t\t\t\t.keyType(KeyType.forAlgorithm(jwsAlgorithm))\n\t\t\t\t\t.keyID(headers.getKeyId())\n\t\t\t\t\t.privateOnly(true)\n\t\t\t\t\t.algorithms(jwsAlgorithm, null)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate static JwsHeader addKeyIdentifierHeadersIfNecessary(JwsHeader headers, JWK jwk) {\n\t\t// Check if headers have already been added\n\t\tif (StringUtils.hasText(headers.getKeyId()) && StringUtils.hasText(headers.getX509SHA256Thumbprint())) {\n\t\t\treturn headers;\n\t\t}\n\t\t// Check if headers can be added from JWK\n\t\tif (!StringUtils.hasText(jwk.getKeyID()) && jwk.getX509CertSHA256Thumbprint() == null) {\n\t\t\treturn headers;\n\t\t}\n\n\t\tJwsHeader.Builder headersBuilder = JwsHeader.from(headers);\n\t\tif (!StringUtils.hasText(headers.getKeyId()) && StringUtils.hasText(jwk.getKeyID())) {\n\t\t\theadersBuilder.keyId(jwk.getKeyID());\n\t\t}\n\t\tif (!StringUtils.hasText(headers.getX509SHA256Thumbprint()) && jwk.getX509CertSHA256Thumbprint() != null) {\n\t\t\theadersBuilder.x509SHA256Thumbprint(jwk.getX509CertSHA256Thumbprint().toString());\n\t\t}\n\n\t\treturn headersBuilder.build();\n\t}\n\n\tprivate static JWSSigner createSigner(JWK jwk) {\n\t\ttry {\n\t\t\treturn JWS_SIGNER_FACTORY.createJWSSigner(jwk);\n\t\t}\n\t\tcatch (JOSEException ex) {\n\t\t\tthrow new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,\n\t\t\t\t\t\"Failed to create a JWS Signer -> \" + ex.getMessage()), ex);\n\t\t}\n\t}\n\n\tprivate static JWSHeader convert(JwsHeader headers) {\n\t\tJwsAlgorithm algorithm = headers.getAlgorithm();\n\t\tAssert.notNull(algorithm, \"JWS header algorithm must not be null\");\n\t\tJWSHeader.Builder builder = new JWSHeader.Builder(JWSAlgorithm.parse(algorithm.getName()));\n\n\t\tif (headers.getJwkSetUrl() != null) {\n\t\t\tbuilder.jwkURL(convertAsURI(JoseHeaderNames.JKU, headers.getJwkSetUrl()));\n\t\t}\n\n\t\tMap<String, Object> jwk = headers.getJwk();\n\t\tif (!CollectionUtils.isEmpty(jwk)) {\n\t\t\ttry {\n\t\t\t\tbuilder.jwk(JWK.parse(jwk));\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,\n\t\t\t\t\t\t\"Unable to convert '\" + JoseHeaderNames.JWK + \"' JOSE header\"), ex);\n\t\t\t}\n\t\t}\n\n\t\tString keyId = headers.getKeyId();\n\t\tif (StringUtils.hasText(keyId)) {\n\t\t\tbuilder.keyID(keyId);\n\t\t}\n\n\t\tif (headers.getX509Url() != null) {\n\t\t\tbuilder.x509CertURL(convertAsURI(JoseHeaderNames.X5U, headers.getX509Url()));\n\t\t}\n\n\t\tList<String> x509CertificateChain = headers.getX509CertificateChain();\n\t\tif (!CollectionUtils.isEmpty(x509CertificateChain)) {\n\t\t\tList<Base64> x5cList = new ArrayList<>();\n\t\t\tx509CertificateChain.forEach((x5c) -> x5cList.add(new Base64(x5c)));\n\t\t\tif (!x5cList.isEmpty()) {\n\t\t\t\tbuilder.x509CertChain(x5cList);\n\t\t\t}\n\t\t}\n\n\t\tString x509SHA1Thumbprint = headers.getX509SHA1Thumbprint();\n\t\tif (StringUtils.hasText(x509SHA1Thumbprint)) {\n\t\t\tbuilder.x509CertThumbprint(new Base64URL(x509SHA1Thumbprint));\n\t\t}\n\n\t\tString x509SHA256Thumbprint = headers.getX509SHA256Thumbprint();\n\t\tif (StringUtils.hasText(x509SHA256Thumbprint)) {\n\t\t\tbuilder.x509CertSHA256Thumbprint(new Base64URL(x509SHA256Thumbprint));\n\t\t}\n\n\t\tString type = headers.getType();\n\t\tif (StringUtils.hasText(type)) {\n\t\t\tbuilder.type(new JOSEObjectType(type));\n\t\t}\n\n\t\tString contentType = headers.getContentType();\n\t\tif (StringUtils.hasText(contentType)) {\n\t\t\tbuilder.contentType(contentType);\n\t\t}\n\n\t\tSet<String> critical = headers.getCritical();\n\t\tif (!CollectionUtils.isEmpty(critical)) {\n\t\t\tbuilder.criticalParams(critical);\n\t\t}\n\n\t\tMap<String, Object> customHeaders = new HashMap<>();\n\t\theaders.getHeaders().forEach((name, value) -> {\n\t\t\tif (!JWSHeader.getRegisteredParameterNames().contains(name)) {\n\t\t\t\tcustomHeaders.put(name, value);\n\t\t\t}\n\t\t});\n\t\tif (!customHeaders.isEmpty()) {\n\t\t\tbuilder.customParams(customHeaders);\n\t\t}\n\n\t\treturn builder.build();\n\t}\n\n\tprivate static JWTClaimsSet convert(JwtClaimsSet claims) {\n\t\tJWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();\n\n\t\t// NOTE: The value of the 'iss' claim is a String or URL (StringOrURI).\n\t\tObject issuer = claims.getClaim(JwtClaimNames.ISS);\n\t\tif (issuer != null) {\n\t\t\tbuilder.issuer(issuer.toString());\n\t\t}\n\n\t\tString subject = claims.getSubject();\n\t\tif (StringUtils.hasText(subject)) {\n\t\t\tbuilder.subject(subject);\n\t\t}\n\n\t\tList<String> audience = claims.getAudience();\n\t\tif (!CollectionUtils.isEmpty(audience)) {\n\t\t\tbuilder.audience(audience);\n\t\t}\n\n\t\tInstant expiresAt = claims.getExpiresAt();\n\t\tif (expiresAt != null) {\n\t\t\tbuilder.expirationTime(Date.from(expiresAt));\n\t\t}\n\n\t\tInstant notBefore = claims.getNotBefore();\n\t\tif (notBefore != null) {\n\t\t\tbuilder.notBeforeTime(Date.from(notBefore));\n\t\t}\n\n\t\tInstant issuedAt = claims.getIssuedAt();\n\t\tif (issuedAt != null) {\n\t\t\tbuilder.issueTime(Date.from(issuedAt));\n\t\t}\n\n\t\tString jwtId = claims.getId();\n\t\tif (StringUtils.hasText(jwtId)) {\n\t\t\tbuilder.jwtID(jwtId);\n\t\t}\n\n\t\tMap<String, Object> customClaims = new HashMap<>();\n\t\tclaims.getClaims().forEach((name, value) -> {\n\t\t\tif (!JWTClaimsSet.getRegisteredNames().contains(name)) {\n\t\t\t\tcustomClaims.put(name, value);\n\t\t\t}\n\t\t});\n\t\tif (!customClaims.isEmpty()) {\n\t\t\tcustomClaims.forEach(builder::claim);\n\t\t}\n\n\t\treturn builder.build();\n\t}\n\n\tprivate static URI convertAsURI(String header, URL url) {\n\t\ttry {\n\t\t\treturn url.toURI();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,\n\t\t\t\t\t\"Unable to convert '\" + header + \"' JOSE header to a URI\"), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Creates a builder for constructing a {@link NimbusJwtEncoder} using the provided\n\t * @param publicKey the {@link RSAPublicKey} and @Param privateKey the\n\t * {@link RSAPrivateKey} to use for signing JWTs\n\t * @return a {@link RsaKeyPairJwtEncoderBuilder}\n\t * @since 7.0\n\t */\n\tpublic static RsaKeyPairJwtEncoderBuilder withKeyPair(RSAPublicKey publicKey, RSAPrivateKey privateKey) {\n\t\treturn new RsaKeyPairJwtEncoderBuilder(publicKey, privateKey);\n\t}\n\n\t/**\n\t * Creates a builder for constructing a {@link NimbusJwtEncoder} using the provided\n\t * @param publicKey the {@link ECPublicKey} and @param privateKey the\n\t * {@link ECPrivateKey} to use for signing JWTs\n\t * @return a {@link EcKeyPairJwtEncoderBuilder}\n\t * @since 7.0\n\t */\n\tpublic static EcKeyPairJwtEncoderBuilder withKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey) {\n\t\treturn new EcKeyPairJwtEncoderBuilder(publicKey, privateKey);\n\t}\n\n\t/**\n\t * Creates a builder for constructing a {@link NimbusJwtEncoder} using the provided\n\t * @param secretKey\n\t * @return a {@link SecretKeyJwtEncoderBuilder} for configuring the {@link JWK}\n\t * @since 7.0\n\t */\n\tpublic static SecretKeyJwtEncoderBuilder withSecretKey(SecretKey secretKey) {\n\t\treturn new SecretKeyJwtEncoderBuilder(secretKey);\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusJwtEncoder} instances configured with a\n\t * {@link SecretKey}.\n\t *\n\t * @since 7.0\n\t */\n\tpublic static final class SecretKeyJwtEncoderBuilder {\n\n\t\tprivate static final ThrowingFunction<SecretKey, OctetSequenceKey.Builder> defaultJwk = JWKS::signing;\n\n\t\tprivate final OctetSequenceKey.Builder builder;\n\n\t\tprivate final Set<JWSAlgorithm> allowedAlgorithms;\n\n\t\tprivate SecretKeyJwtEncoderBuilder(SecretKey secretKey) {\n\t\t\tAssert.notNull(secretKey, \"secretKey cannot be null\");\n\t\t\tSet<JWSAlgorithm> allowedAlgorithms = computeAllowedAlgorithms(secretKey);\n\t\t\tAssert.notEmpty(allowedAlgorithms,\n\t\t\t\t\t\"This key is too small for any standard JWK symmetric signing algorithm\");\n\t\t\tthis.allowedAlgorithms = allowedAlgorithms;\n\t\t\tthis.builder = defaultJwk.apply(secretKey, IllegalArgumentException::new)\n\t\t\t\t.algorithm(this.allowedAlgorithms.iterator().next());\n\t\t}\n\n\t\tprivate Set<JWSAlgorithm> computeAllowedAlgorithms(SecretKey secretKey) {\n\t\t\ttry {\n\t\t\t\treturn new MACSigner(secretKey).supportedJWSAlgorithms();\n\t\t\t}\n\t\t\tcatch (JOSEException ex) {\n\t\t\t\tthrow new IllegalArgumentException(ex);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Sets the JWS algorithm to use for signing. Defaults to\n\t\t * {@link JWSAlgorithm#HS256}. Must be an HMAC-based algorithm (HS256, HS384, or\n\t\t * HS512).\n\t\t * @param macAlgorithm the {@link MacAlgorithm} to use\n\t\t * @return this builder instance for method chaining\n\t\t */\n\t\tpublic SecretKeyJwtEncoderBuilder algorithm(MacAlgorithm macAlgorithm) {\n\t\t\tAssert.notNull(macAlgorithm, \"macAlgorithm cannot be null\");\n\t\t\tJWSAlgorithm jws = JWSAlgorithm.parse(macAlgorithm.getName());\n\t\t\tAssert.isTrue(this.allowedAlgorithms.contains(jws), String\n\t\t\t\t.format(\"This key can only support \" + \"the following algorithms: [%s]\", this.allowedAlgorithms));\n\t\t\tthis.builder.algorithm(JWSAlgorithm.parse(macAlgorithm.getName()));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Post-process the {@link JWK} using the given {@link Consumer}. For example, you\n\t\t * may use this to override the default {@code kid}\n\t\t * @param jwkPostProcessor the post-processor to use\n\t\t * @return this builder instance for method chaining\n\t\t */\n\t\tpublic SecretKeyJwtEncoderBuilder jwkPostProcessor(Consumer<OctetSequenceKey.Builder> jwkPostProcessor) {\n\t\t\tAssert.notNull(jwkPostProcessor, \"jwkPostProcessor cannot be null\");\n\t\t\tjwkPostProcessor.accept(this.builder);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link NimbusJwtEncoder} instance.\n\t\t * @return the configured {@link NimbusJwtEncoder}\n\t\t * @throws IllegalStateException if the configured JWS algorithm is not compatible\n\t\t * with a {@link SecretKey}.\n\t\t */\n\t\tpublic NimbusJwtEncoder build() {\n\t\t\treturn new NimbusJwtEncoder(this.builder.build());\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusJwtEncoder} instances configured with a\n\t * {@link KeyPair}.\n\t *\n\t * @since 7.0\n\t */\n\tpublic static final class RsaKeyPairJwtEncoderBuilder {\n\n\t\tprivate static final ThrowingBiFunction<RSAPublicKey, RSAPrivateKey, RSAKey.Builder> defaultKid = JWKS::signingWithRsa;\n\n\t\tprivate final RSAKey.Builder builder;\n\n\t\tprivate RsaKeyPairJwtEncoderBuilder(RSAPublicKey publicKey, RSAPrivateKey privateKey) {\n\t\t\tAssert.notNull(publicKey, \"publicKey cannot be null\");\n\t\t\tAssert.notNull(privateKey, \"privateKey cannot be null\");\n\t\t\tthis.builder = defaultKid.apply(publicKey, privateKey);\n\t\t}\n\n\t\t/**\n\t\t * Sets the JWS algorithm to use for signing. Defaults to\n\t\t * {@link SignatureAlgorithm#RS256}. Must be an RSA-based algorithm\n\t\t * @param signatureAlgorithm the {@link SignatureAlgorithm} to use\n\t\t * @return this builder instance for method chaining\n\t\t */\n\t\tpublic RsaKeyPairJwtEncoderBuilder algorithm(SignatureAlgorithm signatureAlgorithm) {\n\t\t\tAssert.notNull(signatureAlgorithm, \"signatureAlgorithm cannot be null\");\n\t\t\tthis.builder.algorithm(JWSAlgorithm.parse(signatureAlgorithm.getName()));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add commentMore actions Post-process the {@link JWK} using the given\n\t\t * {@link Consumer}. For example, you may use this to override the default\n\t\t * {@code kid}\n\t\t * @param jwkPostProcessor the post-processor to use\n\t\t * @return this builder instance for method chaining\n\t\t */\n\t\tpublic RsaKeyPairJwtEncoderBuilder jwkPostProcessor(Consumer<RSAKey.Builder> jwkPostProcessor) {\n\t\t\tAssert.notNull(jwkPostProcessor, \"jwkPostProcessor cannot be null\");\n\t\t\tjwkPostProcessor.accept(this.builder);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link NimbusJwtEncoder} instance.\n\t\t * @return the configured {@link NimbusJwtEncoder}\n\t\t */\n\t\tpublic NimbusJwtEncoder build() {\n\t\t\treturn new NimbusJwtEncoder(this.builder.build());\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusJwtEncoder} instances configured with a\n\t * {@link ECPublicKey} and {@link ECPrivateKey}.\n\t * <p>\n\t * This builder is used to create a {@link NimbusJwtEncoder}\n\t *\n\t * @since 7.0\n\t */\n\tpublic static final class EcKeyPairJwtEncoderBuilder {\n\n\t\tprivate static final ThrowingBiFunction<ECPublicKey, ECPrivateKey, ECKey.Builder> defaultKid = JWKS::signingWithEc;\n\n\t\tprivate final ECKey.Builder builder;\n\n\t\tprivate EcKeyPairJwtEncoderBuilder(ECPublicKey publicKey, ECPrivateKey privateKey) {\n\t\t\tAssert.notNull(publicKey, \"publicKey cannot be null\");\n\t\t\tAssert.notNull(privateKey, \"privateKey cannot be null\");\n\t\t\tCurve curve = Curve.forECParameterSpec(publicKey.getParams());\n\t\t\tAssert.notNull(curve, \"Unable to determine Curve for EC public key.\");\n\t\t\tthis.builder = defaultKid.apply(publicKey, privateKey);\n\t\t}\n\n\t\t/**\n\t\t * Post-process the {@link JWK} using the given {@link Consumer}. For example, you\n\t\t * may use this to override the default {@code kid}\n\t\t * @param jwkPostProcessor the post-processor to use\n\t\t * @return this builder instance for method chaining\n\t\t */\n\t\tpublic EcKeyPairJwtEncoderBuilder jwkPostProcessor(Consumer<ECKey.Builder> jwkPostProcessor) {\n\t\t\tAssert.notNull(jwkPostProcessor, \"jwkPostProcessor cannot be null\");\n\t\t\tjwkPostProcessor.accept(this.builder);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link NimbusJwtEncoder} instance.\n\t\t * @return the configured {@link NimbusJwtEncoder}\n\t\t */\n\t\tpublic NimbusJwtEncoder build() {\n\t\t\treturn new NimbusJwtEncoder(this.builder.build());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.security.interfaces.RSAPublicKey;\nimport java.time.Duration;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport javax.crypto.SecretKey;\n\nimport com.nimbusds.jose.Header;\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.JOSEObjectType;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.JWSHeader;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.source.JWKSecurityContextJWKSet;\nimport com.nimbusds.jose.proc.BadJOSEException;\nimport com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;\nimport com.nimbusds.jose.proc.JOSEObjectTypeVerifier;\nimport com.nimbusds.jose.proc.JWKSecurityContext;\nimport com.nimbusds.jose.proc.JWSKeySelector;\nimport com.nimbusds.jose.proc.JWSVerificationKeySelector;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jose.proc.SingleKeyJWSKeySelector;\nimport com.nimbusds.jwt.JWT;\nimport com.nimbusds.jwt.JWTClaimsSet;\nimport com.nimbusds.jwt.JWTParser;\nimport com.nimbusds.jwt.PlainJWT;\nimport com.nimbusds.jwt.SignedJWT;\nimport com.nimbusds.jwt.proc.ConfigurableJWTProcessor;\nimport com.nimbusds.jwt.proc.DefaultJWTProcessor;\nimport com.nimbusds.jwt.proc.JWTProcessor;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.util.function.Tuple2;\nimport reactor.util.function.Tuples;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.function.client.WebClient;\n\n/**\n * An implementation of a {@link ReactiveJwtDecoder} that &quot;decodes&quot; a JSON Web\n * Token (JWT) and additionally verifies its digital signature if the JWT is a JSON Web\n * Signature (JWS).\n *\n * <p>\n * <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK internally.\n *\n * @author Rob Winch\n * @author Joe Grandja\n * @since 5.1\n * @see ReactiveJwtDecoder\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519\">JSON Web Token\n * (JWT)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7517\">JSON Web Key\n * (JWK)</a>\n * @see <a target=\"_blank\" href=\"https://connect2id.com/products/nimbus-jose-jwt\">Nimbus\n * JOSE + JWT SDK</a>\n */\npublic final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {\n\n\tprivate final Converter<JWT, Mono<JWTClaimsSet>> jwtProcessor;\n\n\tprivate OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefault();\n\n\tprivate Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = MappedJwtClaimSetConverter\n\t\t.withDefaults(Collections.emptyMap());\n\n\t/**\n\t * Constructs a {@code NimbusReactiveJwtDecoder} using the provided parameters.\n\t * @param jwkSetUrl the JSON Web Key (JWK) Set {@code URL}\n\t */\n\tpublic NimbusReactiveJwtDecoder(String jwkSetUrl) {\n\t\tthis(withJwkSetUri(jwkSetUrl).processor());\n\t}\n\n\t/**\n\t * Constructs a {@code NimbusReactiveJwtDecoder} using the provided parameters.\n\t * @param publicKey the {@code RSAPublicKey} used to verify the signature\n\t * @since 5.2\n\t */\n\tpublic NimbusReactiveJwtDecoder(RSAPublicKey publicKey) {\n\t\tthis(withPublicKey(publicKey).processor());\n\t}\n\n\t/**\n\t * Constructs a {@code NimbusReactiveJwtDecoder} using the provided parameters.\n\t * @param jwtProcessor the {@link Converter} used to process and verify the signed Jwt\n\t * and return the Jwt Claim Set\n\t * @since 5.2\n\t */\n\tpublic NimbusReactiveJwtDecoder(Converter<JWT, Mono<JWTClaimsSet>> jwtProcessor) {\n\t\tthis.jwtProcessor = jwtProcessor;\n\t}\n\n\t/**\n\t * Use the provided {@link OAuth2TokenValidator} to validate incoming {@link Jwt}s.\n\t * @param jwtValidator the {@link OAuth2TokenValidator} to use\n\t */\n\tpublic void setJwtValidator(OAuth2TokenValidator<Jwt> jwtValidator) {\n\t\tAssert.notNull(jwtValidator, \"jwtValidator cannot be null\");\n\t\tthis.jwtValidator = jwtValidator;\n\t}\n\n\t/**\n\t * Use the following {@link Converter} for manipulating the JWT's claim set\n\t * @param claimSetConverter the {@link Converter} to use\n\t */\n\tpublic void setClaimSetConverter(Converter<Map<String, Object>, Map<String, Object>> claimSetConverter) {\n\t\tAssert.notNull(claimSetConverter, \"claimSetConverter cannot be null\");\n\t\tthis.claimSetConverter = claimSetConverter;\n\t}\n\n\t@Override\n\tpublic Mono<Jwt> decode(String token) {\n\t\ttry {\n\t\t\tJWT jwt = JWTParser.parse(token);\n\t\t\tif (jwt instanceof PlainJWT) {\n\t\t\t\treturn Mono.error(new BadJwtException(\"Unsupported algorithm of \" + jwt.getHeader().getAlgorithm()));\n\t\t\t}\n\t\t\treturn this.decode(jwt);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn Mono.error(new BadJwtException(\n\t\t\t\t\t\"An error occurred while attempting to decode the Jwt: \" + ex.getMessage(), ex));\n\t\t}\n\t}\n\n\tprivate Mono<Jwt> decode(JWT parsedToken) {\n\t\ttry {\n\t\t\t// @formatter:off\n\t\t\treturn this.jwtProcessor.convert(parsedToken)\n\t\t\t\t\t.map((set) -> createJwt(parsedToken, set))\n\t\t\t\t\t.map(this::validateJwt)\n\t\t\t\t\t.onErrorMap((ex) -> !(ex instanceof IllegalStateException) && !(ex instanceof JwtException),\n\t\t\t\t\t\t\t(ex) -> new JwtException(\"An error occurred while attempting to decode the Jwt: \", ex));\n\t\t\t// @formatter:on\n\t\t}\n\t\tcatch (JwtException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tthrow new JwtException(\"An error occurred while attempting to decode the Jwt: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\tprivate Jwt createJwt(JWT parsedJwt, JWTClaimsSet jwtClaimsSet) {\n\t\ttry {\n\t\t\tMap<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());\n\t\t\tMap<String, Object> claims = this.claimSetConverter.convert(jwtClaimsSet.getClaims());\n\t\t\treturn Jwt.withTokenValue(parsedJwt.getParsedString())\n\t\t\t\t.headers((h) -> h.putAll(headers))\n\t\t\t\t.claims((c) -> c.putAll(claims))\n\t\t\t\t.build();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new BadJwtException(\"An error occurred while attempting to decode the Jwt: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\tprivate Jwt validateJwt(Jwt jwt) {\n\t\tOAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt);\n\t\tif (result.hasErrors()) {\n\t\t\tCollection<OAuth2Error> errors = result.getErrors();\n\t\t\tString validationErrorString = getJwtValidationExceptionMessage(errors);\n\t\t\tthrow new JwtValidationException(validationErrorString, errors);\n\t\t}\n\t\treturn jwt;\n\t}\n\n\tprivate String getJwtValidationExceptionMessage(Collection<OAuth2Error> errors) {\n\t\tfor (OAuth2Error oAuth2Error : errors) {\n\t\t\tif (StringUtils.hasLength(oAuth2Error.getDescription())) {\n\t\t\t\treturn oAuth2Error.getDescription();\n\t\t\t}\n\t\t}\n\t\treturn \"Unable to validate Jwt\";\n\t}\n\n\t/**\n\t * Use the given <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * by making an <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">OpenID\n\t * Provider Configuration Request</a> and using the values in the <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">OpenID\n\t * Provider Configuration Response</a> to derive the needed\n\t * <a href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK Set</a> uri.\n\t * @param issuer the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return a {@link NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder} that will derive the\n\t * JWK Set uri when {@link NimbusJwtDecoder.JwkSetUriJwtDecoderBuilder#build} is\n\t * called\n\t * @since 6.1\n\t * @see JwtDecoders\n\t */\n\tpublic static JwkSetUriReactiveJwtDecoderBuilder withIssuerLocation(String issuer) {\n\t\treturn new JwkSetUriReactiveJwtDecoderBuilder(\n\t\t\t\t(web) -> ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(issuer, web)\n\t\t\t\t\t.flatMap((configuration) -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tJwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (IllegalStateException ex) {\n\t\t\t\t\t\t\treturn Mono.error(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tObject jwksUri = configuration.get(\"jwks_uri\");\n\t\t\t\t\t\tAssert.notNull(jwksUri, \"The public JWK Set URI must not be null\");\n\t\t\t\t\t\treturn Mono.just(jwksUri.toString());\n\t\t\t\t\t}),\n\t\t\t\tReactiveJwtDecoderProviderConfigurationUtils::getJWSAlgorithms);\n\t}\n\n\t/**\n\t * Use the given <a href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK Set</a>\n\t * uri to validate JWTs.\n\t * @param jwkSetUri the JWK Set uri to use\n\t * @return a {@link JwkSetUriReactiveJwtDecoderBuilder} for further configurations\n\t *\n\t * @since 5.2\n\t */\n\tpublic static JwkSetUriReactiveJwtDecoderBuilder withJwkSetUri(String jwkSetUri) {\n\t\treturn new JwkSetUriReactiveJwtDecoderBuilder(jwkSetUri);\n\t}\n\n\t/**\n\t * Use the given public key to validate JWTs\n\t * @param key the public key to use\n\t * @return a {@link PublicKeyReactiveJwtDecoderBuilder} for further configurations\n\t *\n\t * @since 5.2\n\t */\n\tpublic static PublicKeyReactiveJwtDecoderBuilder withPublicKey(RSAPublicKey key) {\n\t\treturn new PublicKeyReactiveJwtDecoderBuilder(key);\n\t}\n\n\t/**\n\t * Use the given {@code SecretKey} to validate the MAC on a JSON Web Signature (JWS).\n\t * @param secretKey the {@code SecretKey} used to validate the MAC\n\t * @return a {@link SecretKeyReactiveJwtDecoderBuilder} for further configurations\n\t *\n\t * @since 5.2\n\t */\n\tpublic static SecretKeyReactiveJwtDecoderBuilder withSecretKey(SecretKey secretKey) {\n\t\treturn new SecretKeyReactiveJwtDecoderBuilder(secretKey);\n\t}\n\n\t/**\n\t * Use the given {@link Function} to validate JWTs\n\t * @param source the {@link Function}\n\t * @return a {@link JwkSourceReactiveJwtDecoderBuilder} for further configurations\n\t *\n\t * @since 5.2\n\t */\n\tpublic static JwkSourceReactiveJwtDecoderBuilder withJwkSource(Function<SignedJWT, Flux<JWK>> source) {\n\t\treturn new JwkSourceReactiveJwtDecoderBuilder(source);\n\t}\n\n\tprivate static <C extends SecurityContext> JWTClaimsSet createClaimsSet(JWTProcessor<C> jwtProcessor,\n\t\t\tJWT parsedToken, @Nullable C context) {\n\t\ttry {\n\t\t\treturn jwtProcessor.process(parsedToken, context);\n\t\t}\n\t\tcatch (BadJOSEException ex) {\n\t\t\tthrow new BadJwtException(\"Failed to validate the token\", ex);\n\t\t}\n\t\tcatch (JOSEException ex) {\n\t\t\tthrow new JwtException(\"Failed to validate the token\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusReactiveJwtDecoder} instances based on a\n\t * <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK Set</a>\n\t * uri.\n\t *\n\t * @since 5.2\n\t */\n\tpublic static final class JwkSetUriReactiveJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<JWKSecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\t\tJOSEObjectType.JWT, null);\n\n\t\tprivate static final JOSEObjectTypeVerifier<JWKSecurityContext> NO_TYPE_VERIFIER = (header, context) -> {\n\t\t};\n\n\t\tprivate static final Duration FOREVER = Duration.ofMillis(Long.MAX_VALUE);\n\n\t\tprivate Function<WebClient, Mono<String>> jwkSetUri;\n\n\t\tprivate Function<ReactiveRemoteJWKSource, Mono<Set<JWSAlgorithm>>> defaultAlgorithms = (source) -> Mono\n\t\t\t.just(Set.of(JWSAlgorithm.RS256));\n\n\t\tprivate JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = NO_TYPE_VERIFIER;\n\n\t\tprivate Set<SignatureAlgorithm> signatureAlgorithms = new HashSet<>();\n\n\t\tprivate WebClient webClient = WebClient.create();\n\n\t\tprivate BiFunction<ReactiveRemoteJWKSource, ConfigurableJWTProcessor<JWKSecurityContext>, Mono<ConfigurableJWTProcessor<JWKSecurityContext>>> jwtProcessorCustomizer;\n\n\t\tprivate JwkSetUriReactiveJwtDecoderBuilder(String jwkSetUri) {\n\t\t\tAssert.hasText(jwkSetUri, \"jwkSetUri cannot be empty\");\n\t\t\tthis.jwkSetUri = (web) -> Mono.just(jwkSetUri);\n\t\t\tthis.jwtProcessorCustomizer = (source, processor) -> Mono.just(processor);\n\t\t}\n\n\t\tprivate JwkSetUriReactiveJwtDecoderBuilder(Function<WebClient, Mono<String>> jwkSetUri,\n\t\t\t\tFunction<ReactiveRemoteJWKSource, Mono<Set<JWSAlgorithm>>> defaultAlgorithms) {\n\t\t\tAssert.notNull(jwkSetUri, \"jwkSetUri cannot be null\");\n\t\t\tAssert.notNull(defaultAlgorithms, \"defaultAlgorithms cannot be null\");\n\t\t\tthis.jwkSetUri = jwkSetUri;\n\t\t\tthis.defaultAlgorithms = defaultAlgorithms;\n\t\t\tthis.jwtProcessorCustomizer = (source, processor) -> Mono.just(processor);\n\t\t}\n\n\t\t/**\n\t\t * Append the given signing\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithm</a> to the set of algorithms to use.\n\t\t * @param signatureAlgorithm the algorithm to use\n\t\t * @return a {@link JwkSetUriReactiveJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic JwkSetUriReactiveJwtDecoderBuilder jwsAlgorithm(SignatureAlgorithm signatureAlgorithm) {\n\t\t\tAssert.notNull(signatureAlgorithm, \"sig cannot be null\");\n\t\t\tthis.signatureAlgorithms.add(signatureAlgorithm);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Whether to use Nimbus's typ header verification. This is {@code true} by\n\t\t * default, however it may change to {@code false} in a future major release.\n\t\t *\n\t\t * <p>\n\t\t * By turning off this feature, {@link NimbusReactiveJwtDecoder} expects\n\t\t * applications to check the {@code typ} header themselves in order to determine\n\t\t * what kind of validation is needed\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * This is done for you when you use {@link JwtValidators} to construct a\n\t\t * validator.\n\t\t *\n\t\t * <p>\n\t\t * That means that this: <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer).build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * Is equivalent to this: <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\t *     \t\tnew JwtIssuerValidator(issuer), JwtTypeValidator.jwt());\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * The difference is that by setting this to {@code false}, it allows you to\n\t\t * provide validation by type, like for {@code at+jwt}:\n\t\t *\n\t\t * <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(new MyAtJwtValidator());\n\t\t * </code>\n\t\t * @param shouldValidateTypHeader whether Nimbus should validate the typ header or\n\t\t * not\n\t\t * @return a {@link JwkSetUriReactiveJwtDecoderBuilder} for further configurations\n\t\t * @since 6.5\n\t\t */\n\t\tpublic JwkSetUriReactiveJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {\n\t\t\tthis.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure the list of\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithms</a> to use with the given {@link Consumer}.\n\t\t * @param signatureAlgorithmsConsumer a {@link Consumer} for further configuring\n\t\t * the algorithm list\n\t\t * @return a {@link JwkSetUriReactiveJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic JwkSetUriReactiveJwtDecoderBuilder jwsAlgorithms(\n\t\t\t\tConsumer<Set<SignatureAlgorithm>> signatureAlgorithmsConsumer) {\n\t\t\tAssert.notNull(signatureAlgorithmsConsumer, \"signatureAlgorithmsConsumer cannot be null\");\n\t\t\tsignatureAlgorithmsConsumer.accept(this.signatureAlgorithms);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link WebClient} to coordinate with the authorization servers\n\t\t * indicated in the <a href=\"https://tools.ietf.org/html/rfc7517#section-5\">JWK\n\t\t * Set</a> uri as well as the <a href=\n\t\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>.\n\t\t * @param webClient\n\t\t * @return a {@link JwkSetUriReactiveJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic JwkSetUriReactiveJwtDecoderBuilder webClient(WebClient webClient) {\n\t\t\tAssert.notNull(webClient, \"webClient cannot be null\");\n\t\t\tthis.webClient = webClient;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Consumer} to customize the {@link JWTProcessor\n\t\t * ConfigurableJWTProcessor} before passing it to the build\n\t\t * {@link NimbusReactiveJwtDecoder}.\n\t\t * @param jwtProcessorCustomizer the callback used to alter the processor\n\t\t * @return a {@link JwkSetUriReactiveJwtDecoderBuilder} for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic JwkSetUriReactiveJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tConsumer<ConfigurableJWTProcessor<JWKSecurityContext>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = (source, processor) -> {\n\t\t\t\tjwtProcessorCustomizer.accept(processor);\n\t\t\t\treturn Mono.just(processor);\n\t\t\t};\n\t\t\treturn this;\n\t\t}\n\n\t\tJwkSetUriReactiveJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tBiFunction<ReactiveRemoteJWKSource, ConfigurableJWTProcessor<JWKSecurityContext>, Mono<ConfigurableJWTProcessor<JWKSecurityContext>>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = jwtProcessorCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the configured {@link NimbusReactiveJwtDecoder}.\n\t\t * @return the configured {@link NimbusReactiveJwtDecoder}\n\t\t */\n\t\tpublic NimbusReactiveJwtDecoder build() {\n\t\t\treturn new NimbusReactiveJwtDecoder(processor());\n\t\t}\n\n\t\tMono<JWSKeySelector<JWKSecurityContext>> jwsKeySelector(ReactiveRemoteJWKSource source) {\n\t\t\tJWKSecurityContextJWKSet jwkSource = new JWKSecurityContextJWKSet();\n\t\t\tif (this.signatureAlgorithms.isEmpty()) {\n\t\t\t\treturn this.defaultAlgorithms.apply(source)\n\t\t\t\t\t.map((algorithms) -> new JWSVerificationKeySelector<>(algorithms, jwkSource));\n\t\t\t}\n\t\t\tSet<JWSAlgorithm> jwsAlgorithms = new HashSet<>();\n\t\t\tfor (SignatureAlgorithm signatureAlgorithm : this.signatureAlgorithms) {\n\t\t\t\tJWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());\n\t\t\t\tjwsAlgorithms.add(jwsAlgorithm);\n\t\t\t}\n\t\t\treturn Mono.just(new JWSVerificationKeySelector<>(jwsAlgorithms, jwkSource));\n\t\t}\n\n\t\tConverter<JWT, Mono<JWTClaimsSet>> processor() {\n\t\t\tDefaultJWTProcessor<JWKSecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t\t});\n\t\t\tReactiveRemoteJWKSource source = new ReactiveRemoteJWKSource(this.jwkSetUri.apply(this.webClient));\n\t\t\tsource.setWebClient(this.webClient);\n\t\t\tMono<JWSKeySelector<JWKSecurityContext>> jwsKeySelector = jwsKeySelector(source);\n\t\t\tMono<Tuple2<ConfigurableJWTProcessor<JWKSecurityContext>, Function<JWSAlgorithm, Boolean>>> jwtProcessorMono = jwsKeySelector\n\t\t\t\t.flatMap((selector) -> {\n\t\t\t\t\tjwtProcessor.setJWSKeySelector(selector);\n\t\t\t\t\tjwtProcessor.setJWSTypeVerifier(this.typeVerifier);\n\t\t\t\t\treturn this.jwtProcessorCustomizer.apply(source, jwtProcessor);\n\t\t\t\t})\n\t\t\t\t.map((processor) -> Tuples.of(processor, getExpectedJwsAlgorithms(processor.getJWSKeySelector())))\n\t\t\t\t.cache((processor) -> FOREVER, (ex) -> Duration.ZERO, () -> Duration.ZERO);\n\t\t\treturn (jwt) -> {\n\t\t\t\treturn jwtProcessorMono.flatMap((tuple) -> {\n\t\t\t\t\tConfigurableJWTProcessor<JWKSecurityContext> processor = tuple.getT1();\n\t\t\t\t\tFunction<JWSAlgorithm, Boolean> expectedJwsAlgorithms = tuple.getT2();\n\t\t\t\t\tJWKSelector selector = createSelector(expectedJwsAlgorithms, jwt.getHeader());\n\t\t\t\t\treturn source.get(selector)\n\t\t\t\t\t\t.onErrorMap((ex) -> new IllegalStateException(\"Could not obtain the keys\", ex))\n\t\t\t\t\t\t.map((jwkList) -> createClaimsSet(processor, jwt, new JWKSecurityContext(jwkList)));\n\t\t\t\t});\n\t\t\t};\n\t\t}\n\n\t\tprivate Function<JWSAlgorithm, Boolean> getExpectedJwsAlgorithms(JWSKeySelector<?> jwsKeySelector) {\n\t\t\tif (jwsKeySelector instanceof JWSVerificationKeySelector) {\n\t\t\t\treturn ((JWSVerificationKeySelector<?>) jwsKeySelector)::isAllowed;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"Unsupported key selector type \" + jwsKeySelector.getClass());\n\t\t}\n\n\t\tprivate JWKSelector createSelector(Function<JWSAlgorithm, Boolean> expectedJwsAlgorithms, Header header) {\n\t\t\tJWSHeader jwsHeader = (JWSHeader) header;\n\t\t\tif (!expectedJwsAlgorithms.apply(jwsHeader.getAlgorithm())) {\n\t\t\t\tthrow new BadJwtException(\"Unsupported algorithm of \" + header.getAlgorithm());\n\t\t\t}\n\t\t\treturn new JWKSelector(JWKMatcher.forJWSHeader(jwsHeader));\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusReactiveJwtDecoder} instances based on a public\n\t * key.\n\t *\n\t * @since 5.2\n\t */\n\tpublic static final class PublicKeyReactiveJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\t\tJOSEObjectType.JWT, null);\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {\n\t\t};\n\n\t\tprivate final RSAPublicKey key;\n\n\t\tprivate JWSAlgorithm jwsAlgorithm;\n\n\t\tprivate JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;\n\n\t\tprivate Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;\n\n\t\tprivate PublicKeyReactiveJwtDecoderBuilder(RSAPublicKey key) {\n\t\t\tAssert.notNull(key, \"key cannot be null\");\n\t\t\tthis.key = key;\n\t\t\tthis.jwsAlgorithm = JWSAlgorithm.RS256;\n\t\t\tthis.jwtProcessorCustomizer = (processor) -> {\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Use the given signing\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithm</a>. The value should be one of\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7518#section-3.3\" target=\n\t\t * \"_blank\">RS256, RS384, or RS512</a>.\n\t\t * @param signatureAlgorithm the algorithm to use\n\t\t * @return a {@link PublicKeyReactiveJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic PublicKeyReactiveJwtDecoderBuilder signatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {\n\t\t\tAssert.notNull(signatureAlgorithm, \"signatureAlgorithm cannot be null\");\n\t\t\tthis.jwsAlgorithm = JWSAlgorithm.parse(signatureAlgorithm.getName());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Whether to use Nimbus's typ header verification. This is {@code true} by\n\t\t * default, however it may change to {@code false} in a future major release.\n\t\t *\n\t\t * <p>\n\t\t * By turning off this feature, {@link NimbusReactiveJwtDecoder} expects\n\t\t * applications to check the {@code typ} header themselves in order to determine\n\t\t * what kind of validation is needed\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * This is done for you when you use {@link JwtValidators} to construct a\n\t\t * validator.\n\t\t *\n\t\t * <p>\n\t\t * That means that this: <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer).build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * Is equivalent to this: <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withPublicKey(key)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\t *     \t\tnew JwtIssuerValidator(issuer), JwtTypeValidator.jwt());\n\t\t *     \t\tnew JwtIssuerValidator(issuer), JwtTypeValidator.jwt());\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * The difference is that by setting this to {@code false}, it allows you to\n\t\t * provide validation by type, like for {@code at+jwt}:\n\t\t *\n\t\t * <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withPublicKey(key)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(new MyAtJwtValidator());\n\t\t * </code>\n\t\t * @param shouldValidateTypHeader whether Nimbus should validate the typ header or\n\t\t * not\n\t\t * @return a {@link PublicKeyReactiveJwtDecoderBuilder} for further configurations\n\t\t * @since 6.5\n\t\t */\n\t\tpublic PublicKeyReactiveJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {\n\t\t\tthis.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Consumer} to customize the {@link JWTProcessor\n\t\t * ConfigurableJWTProcessor} before passing it to the build\n\t\t * {@link NimbusReactiveJwtDecoder}.\n\t\t * @param jwtProcessorCustomizer the callback used to alter the processor\n\t\t * @return a {@link PublicKeyReactiveJwtDecoderBuilder} for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic PublicKeyReactiveJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tConsumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = jwtProcessorCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the configured {@link NimbusReactiveJwtDecoder}.\n\t\t * @return the configured {@link NimbusReactiveJwtDecoder}\n\t\t */\n\t\tpublic NimbusReactiveJwtDecoder build() {\n\t\t\treturn new NimbusReactiveJwtDecoder(processor());\n\t\t}\n\n\t\tConverter<JWT, Mono<JWTClaimsSet>> processor() {\n\t\t\tAssert.state(JWSAlgorithm.Family.RSA.contains(this.jwsAlgorithm),\n\t\t\t\t\t() -> \"The provided key is of type RSA; however the signature algorithm is of some other type: \"\n\t\t\t\t\t\t\t+ this.jwsAlgorithm + \". Please indicate one of RS256, RS384, or RS512.\");\n\t\t\tJWSKeySelector<SecurityContext> jwsKeySelector = new SingleKeyJWSKeySelector<>(this.jwsAlgorithm, this.key);\n\t\t\tDefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector);\n\t\t\tjwtProcessor.setJWSTypeVerifier(this.typeVerifier);\n\t\t\t// Spring Security validates the claim set independent from Nimbus\n\t\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t\t});\n\t\t\tthis.jwtProcessorCustomizer.accept(jwtProcessor);\n\t\t\treturn (jwt) -> Mono.fromCallable(() -> createClaimsSet(jwtProcessor, jwt, null));\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusReactiveJwtDecoder} instances based on a\n\t * {@code SecretKey}.\n\t *\n\t * @since 5.2\n\t */\n\tpublic static final class SecretKeyReactiveJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\t\tJOSEObjectType.JWT, null);\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {\n\t\t};\n\n\t\tprivate final SecretKey secretKey;\n\n\t\tprivate JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256;\n\n\t\tprivate JOSEObjectTypeVerifier<SecurityContext> typeVerifier = NO_TYPE_VERIFIER;\n\n\t\tprivate Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;\n\n\t\tprivate SecretKeyReactiveJwtDecoderBuilder(SecretKey secretKey) {\n\t\t\tAssert.notNull(secretKey, \"secretKey cannot be null\");\n\t\t\tthis.secretKey = secretKey;\n\t\t\tthis.jwtProcessorCustomizer = (processor) -> {\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Use the given\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithm</a> when generating the MAC.\n\t\t *\n\t\t * The value should be one of\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7518#section-3.2\" target=\n\t\t * \"_blank\">HS256, HS384 or HS512</a>.\n\t\t * @param macAlgorithm the MAC algorithm to use\n\t\t * @return a {@link SecretKeyReactiveJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic SecretKeyReactiveJwtDecoderBuilder macAlgorithm(MacAlgorithm macAlgorithm) {\n\t\t\tAssert.notNull(macAlgorithm, \"macAlgorithm cannot be null\");\n\t\t\tthis.jwsAlgorithm = JWSAlgorithm.parse(macAlgorithm.getName());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Whether to use Nimbus's typ header verification. This is {@code true} by\n\t\t * default, however it may change to {@code false} in a future major release.\n\t\t *\n\t\t * <p>\n\t\t * By turning off this feature, {@link NimbusReactiveJwtDecoder} expects\n\t\t * applications to check the {@code typ} header themselves in order to determine\n\t\t * what kind of validation is needed\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * This is done for you when you use {@link JwtValidators} to construct a\n\t\t * validator.\n\t\t *\n\t\t * <p>\n\t\t * That means that this: <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer).build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * Is equivalent to this: <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withSecretKey(key)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\t *     \t\tnew JwtIssuerValidator(issuer), JwtTypeValidator.jwt());\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * The difference is that by setting this to {@code false}, it allows you to\n\t\t * provide validation by type, like for {@code at+jwt}:\n\t\t *\n\t\t * <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withSecretKey(key)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(new MyAtJwtValidator());\n\t\t * </code>\n\t\t * @param shouldValidateTypHeader whether Nimbus should validate the typ header or\n\t\t * not\n\t\t * @return a {@link PublicKeyReactiveJwtDecoderBuilder} for further configurations\n\t\t * @since 6.5\n\t\t */\n\t\tpublic SecretKeyReactiveJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {\n\t\t\tthis.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Consumer} to customize the {@link JWTProcessor\n\t\t * ConfigurableJWTProcessor} before passing it to the build\n\t\t * {@link NimbusReactiveJwtDecoder}.\n\t\t * @param jwtProcessorCustomizer the callback used to alter the processor\n\t\t * @return a {@link SecretKeyReactiveJwtDecoderBuilder} for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic SecretKeyReactiveJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tConsumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = jwtProcessorCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the configured {@link NimbusReactiveJwtDecoder}.\n\t\t * @return the configured {@link NimbusReactiveJwtDecoder}\n\t\t */\n\t\tpublic NimbusReactiveJwtDecoder build() {\n\t\t\treturn new NimbusReactiveJwtDecoder(processor());\n\t\t}\n\n\t\tConverter<JWT, Mono<JWTClaimsSet>> processor() {\n\t\t\tJWSKeySelector<SecurityContext> jwsKeySelector = new SingleKeyJWSKeySelector<>(this.jwsAlgorithm,\n\t\t\t\t\tthis.secretKey);\n\t\t\tDefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector);\n\t\t\tjwtProcessor.setJWSTypeVerifier(this.typeVerifier);\n\t\t\t// Spring Security validates the claim set independent from Nimbus\n\t\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t\t});\n\t\t\tthis.jwtProcessorCustomizer.accept(jwtProcessor);\n\t\t\treturn (jwt) -> Mono.fromCallable(() -> createClaimsSet(jwtProcessor, jwt, null));\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusReactiveJwtDecoder} instances.\n\t *\n\t * @since 5.2\n\t */\n\tpublic static final class JwkSourceReactiveJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<JWKSecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\t\tJOSEObjectType.JWT, null);\n\n\t\tprivate static final JOSEObjectTypeVerifier<JWKSecurityContext> NO_TYPE_VERIFIER = (header, context) -> {\n\t\t};\n\n\t\tprivate final Function<SignedJWT, Flux<JWK>> jwkSource;\n\n\t\tprivate JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256;\n\n\t\tprivate JOSEObjectTypeVerifier<JWKSecurityContext> typeVerifier = NO_TYPE_VERIFIER;\n\n\t\tprivate Consumer<ConfigurableJWTProcessor<JWKSecurityContext>> jwtProcessorCustomizer;\n\n\t\tprivate JwkSourceReactiveJwtDecoderBuilder(Function<SignedJWT, Flux<JWK>> jwkSource) {\n\t\t\tAssert.notNull(jwkSource, \"jwkSource cannot be null\");\n\t\t\tthis.jwkSource = jwkSource;\n\t\t\tthis.jwtProcessorCustomizer = (processor) -> {\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Use the given signing\n\t\t * <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\" target=\n\t\t * \"_blank\">algorithm</a>.\n\t\t * @param jwsAlgorithm the algorithm to use\n\t\t * @return a {@link JwkSourceReactiveJwtDecoderBuilder} for further configurations\n\t\t */\n\t\tpublic JwkSourceReactiveJwtDecoderBuilder jwsAlgorithm(JwsAlgorithm jwsAlgorithm) {\n\t\t\tAssert.notNull(jwsAlgorithm, \"jwsAlgorithm cannot be null\");\n\t\t\tthis.jwsAlgorithm = JWSAlgorithm.parse(jwsAlgorithm.getName());\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Whether to use Nimbus's typ header verification. This is {@code true} by\n\t\t * default, however it may change to {@code false} in a future major release.\n\t\t *\n\t\t * <p>\n\t\t * By turning off this feature, {@link NimbusReactiveJwtDecoder} expects\n\t\t * applications to check the {@code typ} header themselves in order to determine\n\t\t * what kind of validation is needed\n\t\t * </p>\n\t\t *\n\t\t * <p>\n\t\t * This is done for you when you use {@link JwtValidators} to construct a\n\t\t * validator.\n\t\t *\n\t\t * <p>\n\t\t * That means that this: <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSource(issuer).build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer);\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * Is equivalent to this: <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSource(key)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(\n\t\t *     \t\tnew JwtIssuerValidator(issuer), JwtTypeValidator.jwt());\n\t\t * </code>\n\t\t *\n\t\t * <p>\n\t\t * The difference is that by setting this to {@code false}, it allows you to\n\t\t * provide validation by type, like for {@code at+jwt}:\n\t\t *\n\t\t * <code>\n\t\t *     NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSource(key)\n\t\t *         .validateType(false)\n\t\t *         .build();\n\t\t *     jwtDecoder.setJwtValidator(new MyAtJwtValidator());\n\t\t * </code>\n\t\t * @param shouldValidateTypHeader whether Nimbus should validate the typ header or\n\t\t * not\n\t\t * @return a {@link JwkSourceReactiveJwtDecoderBuilder} for further configurations\n\t\t * @since 6.5\n\t\t */\n\t\tpublic JwkSourceReactiveJwtDecoderBuilder validateType(boolean shouldValidateTypHeader) {\n\t\t\tthis.typeVerifier = shouldValidateTypHeader ? JWT_TYPE_VERIFIER : NO_TYPE_VERIFIER;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Consumer} to customize the {@link JWTProcessor\n\t\t * ConfigurableJWTProcessor} before passing it to the build\n\t\t * {@link NimbusReactiveJwtDecoder}.\n\t\t * @param jwtProcessorCustomizer the callback used to alter the processor\n\t\t * @return a {@link JwkSourceReactiveJwtDecoderBuilder} for further configurations\n\t\t * @since 5.4\n\t\t */\n\t\tpublic JwkSourceReactiveJwtDecoderBuilder jwtProcessorCustomizer(\n\t\t\t\tConsumer<ConfigurableJWTProcessor<JWKSecurityContext>> jwtProcessorCustomizer) {\n\t\t\tAssert.notNull(jwtProcessorCustomizer, \"jwtProcessorCustomizer cannot be null\");\n\t\t\tthis.jwtProcessorCustomizer = jwtProcessorCustomizer;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the configured {@link NimbusReactiveJwtDecoder}.\n\t\t * @return the configured {@link NimbusReactiveJwtDecoder}\n\t\t */\n\t\tpublic NimbusReactiveJwtDecoder build() {\n\t\t\treturn new NimbusReactiveJwtDecoder(processor());\n\t\t}\n\n\t\tConverter<JWT, Mono<JWTClaimsSet>> processor() {\n\t\t\tJWKSecurityContextJWKSet jwkSource = new JWKSecurityContextJWKSet();\n\t\t\tJWSKeySelector<JWKSecurityContext> jwsKeySelector = new JWSVerificationKeySelector<>(this.jwsAlgorithm,\n\t\t\t\t\tjwkSource);\n\t\t\tDefaultJWTProcessor<JWKSecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector);\n\t\t\tjwtProcessor.setJWSTypeVerifier(this.typeVerifier);\n\t\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t\t});\n\t\t\tthis.jwtProcessorCustomizer.accept(jwtProcessor);\n\t\t\treturn (jwt) -> {\n\t\t\t\tif (jwt instanceof SignedJWT) {\n\t\t\t\t\treturn this.jwkSource.apply((SignedJWT) jwt)\n\t\t\t\t\t\t.onErrorMap((e) -> new IllegalStateException(\"Could not obtain the keys\", e))\n\t\t\t\t\t\t.collectList()\n\t\t\t\t\t\t.map((jwks) -> createClaimsSet(jwtProcessor, jwt, new JWKSecurityContext(jwks)));\n\t\t\t\t}\n\t\t\t\tthrow new BadJwtException(\"Unsupported algorithm of \" + jwt.getHeader().getAlgorithm());\n\t\t\t};\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJWKSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.List;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport reactor.core.publisher.Mono;\n\n/**\n * A reactive version of {@link com.nimbusds.jose.jwk.source.JWKSource}\n *\n * @author Rob Winch\n * @since 5.1\n */\ninterface ReactiveJWKSource {\n\n\tMono<List<JWK>> get(JWKSelector jwkSelector);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJWKSourceAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.List;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport reactor.core.publisher.Mono;\n\n/**\n * Adapts a {@link JWKSource} to a {@link ReactiveJWKSource} which must be non-blocking.\n *\n * @author Rob Winch\n * @since 5.1\n */\nclass ReactiveJWKSourceAdapter implements ReactiveJWKSource {\n\n\tprivate final JWKSource<SecurityContext> source;\n\n\t/**\n\t * Creates a new instance\n\t * @param source\n\t */\n\tReactiveJWKSourceAdapter(JWKSource<SecurityContext> source) {\n\t\tthis.source = source;\n\t}\n\n\t@Override\n\tpublic Mono<List<JWK>> get(JWKSelector jwkSelector) {\n\t\treturn Mono.fromCallable(() -> this.source.get(jwkSelector, null));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport reactor.core.publisher.Mono;\n\n/**\n * Implementations of this interface are responsible for &quot;decoding&quot; a JSON Web\n * Token (JWT) from its compact claims representation format to a {@link Jwt}.\n *\n * <p>\n * JWTs may be represented using the JWS Compact Serialization format for a JSON Web\n * Signature (JWS) structure or JWE Compact Serialization format for a JSON Web Encryption\n * (JWE) structure. Therefore, implementors are responsible for verifying a JWS and/or\n * decrypting a JWE.\n *\n * @author Rob Winch\n * @since 5.1\n * @see Jwt\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7519\">JSON Web Token\n * (JWT)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515\">JSON Web Signature\n * (JWS)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516\">JSON Web Encryption\n * (JWE)</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7515#section-3.1\">JWS\n * Compact Serialization</a>\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/html/rfc7516#section-3.1\">JWE\n * Compact Serialization</a>\n */\n@FunctionalInterface\npublic interface ReactiveJwtDecoder {\n\n\t/**\n\t * Decodes the JWT from its compact claims representation format and returns a\n\t * {@link Jwt}.\n\t * @param token the JWT value\n\t * @return a {@link Jwt}\n\t * @throws JwtException if an error occurs while attempting to decode the JWT\n\t */\n\tMono<Jwt> decode(String token) throws JwtException;\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\n/**\n * A factory for {@link ReactiveJwtDecoder}(s). This factory should be supplied with a\n * type that provides contextual information used to create a specific\n * {@code ReactiveJwtDecoder}.\n *\n * @param <C> The type that provides contextual information used to create a specific\n * {@code ReactiveJwtDecoder}.\n * @author Joe Grandja\n * @since 5.2\n * @see ReactiveJwtDecoder\n */\n@FunctionalInterface\npublic interface ReactiveJwtDecoderFactory<C> {\n\n\t/**\n\t * Creates a {@code ReactiveJwtDecoder} using the supplied \"contextual\" type.\n\t * @param context the type that provides contextual information\n\t * @return a {@link ReactiveJwtDecoder}\n\t */\n\tReactiveJwtDecoder createDecoder(C context);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.KeySourceException;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.KeyType;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.JWSKeySelector;\nimport com.nimbusds.jose.proc.JWSVerificationKeySelector;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jwt.proc.ConfigurableJWTProcessor;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.util.Assert;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nfinal class ReactiveJwtDecoderProviderConfigurationUtils {\n\n\tprivate static final String OIDC_METADATA_PATH = \"/.well-known/openid-configuration\";\n\n\tprivate static final String OAUTH_METADATA_PATH = \"/.well-known/oauth-authorization-server\";\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tstatic <C extends SecurityContext> Mono<ConfigurableJWTProcessor<C>> addJWSAlgorithms(\n\t\t\tReactiveRemoteJWKSource jwkSource, ConfigurableJWTProcessor<C> jwtProcessor) {\n\t\tJWSKeySelector<C> selector = jwtProcessor.getJWSKeySelector();\n\t\tif (!(selector instanceof JWSVerificationKeySelector)) {\n\t\t\treturn Mono.just(jwtProcessor);\n\t\t}\n\t\tJWKSource<C> delegate = ((JWSVerificationKeySelector<C>) selector).getJWKSource();\n\t\treturn getJWSAlgorithms(jwkSource).map((algorithms) -> new JWSVerificationKeySelector<>(algorithms, delegate))\n\t\t\t.map((replacement) -> {\n\t\t\t\tjwtProcessor.setJWSKeySelector(replacement);\n\t\t\t\treturn jwtProcessor;\n\t\t\t});\n\t}\n\n\tstatic Mono<Set<JWSAlgorithm>> getJWSAlgorithms(ReactiveRemoteJWKSource jwkSource) {\n\t\tJWKMatcher jwkMatcher = new JWKMatcher.Builder().publicOnly(true)\n\t\t\t.keyUses(KeyUse.SIGNATURE, null)\n\t\t\t.keyTypes(KeyType.RSA, KeyType.EC)\n\t\t\t.build();\n\t\treturn jwkSource.get(new JWKSelector(jwkMatcher)).map((jwks) -> {\n\t\t\tSet<JWSAlgorithm> jwsAlgorithms = new HashSet<>();\n\t\t\tfor (JWK jwk : jwks) {\n\t\t\t\tif (jwk.getAlgorithm() != null) {\n\t\t\t\t\tJWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(jwk.getAlgorithm().getName());\n\t\t\t\t\tjwsAlgorithms.add(jwsAlgorithm);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif (jwk.getKeyType() == KeyType.RSA) {\n\t\t\t\t\t\tjwsAlgorithms.addAll(JWSAlgorithm.Family.RSA);\n\t\t\t\t\t}\n\t\t\t\t\telse if (jwk.getKeyType() == KeyType.EC) {\n\t\t\t\t\t\tjwsAlgorithms.addAll(JWSAlgorithm.Family.EC);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tAssert.notEmpty(jwsAlgorithms, \"Failed to find any algorithms from the JWK set\");\n\t\t\treturn jwsAlgorithms;\n\t\t}).onErrorMap(KeySourceException.class, IllegalStateException::new);\n\t}\n\n\tstatic Mono<Map<String, Object>> getConfigurationForIssuerLocation(String issuer, WebClient web) {\n\t\treturn getConfiguration(issuer, web, oidc(issuer), oidcRfc8414(issuer), oauth(issuer));\n\t}\n\n\tstatic UriComponents oidc(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(uri.getPath() + OIDC_METADATA_PATH)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tstatic UriComponents oidcRfc8414(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(OIDC_METADATA_PATH + uri.getPath())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tstatic UriComponents oauth(String issuer) {\n\t\tUriComponents uri = UriComponentsBuilder.fromUriString(issuer).build();\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.newInstance().uriComponents(uri)\n\t\t\t\t.replacePath(OAUTH_METADATA_PATH + uri.getPath())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static Mono<Map<String, Object>> getConfiguration(String issuer, WebClient web, UriComponents... uris) {\n\t\tString errorMessage = \"Unable to resolve the Configuration with the provided Issuer of \" + \"\\\"\" + issuer + \"\\\"\";\n\t\treturn Flux.just(uris)\n\t\t\t.concatMap((uri) -> web.get().uri(uri.toUriString()).retrieve().bodyToMono(STRING_OBJECT_MAP))\n\t\t\t.flatMap((configuration) -> {\n\t\t\t\tif (configuration.get(\"jwks_uri\") == null) {\n\t\t\t\t\treturn Mono.error(() -> new IllegalArgumentException(\"The public JWK set URI must not be null\"));\n\t\t\t\t}\n\t\t\t\treturn Mono.just(configuration);\n\t\t\t})\n\t\t\t.onErrorContinue((ex) -> ex instanceof WebClientResponseException\n\t\t\t\t\t&& ((WebClientResponseException) ex).getStatusCode().is4xxClientError(), (ex, object) -> {\n\t\t\t\t\t})\n\t\t\t.onErrorMap(RuntimeException.class,\n\t\t\t\t\t(ex) -> (ex instanceof IllegalArgumentException) ? ex\n\t\t\t\t\t\t\t: new IllegalArgumentException(errorMessage, ex))\n\t\t\t.next()\n\t\t\t.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(errorMessage)));\n\t}\n\n\tprivate ReactiveJwtDecoderProviderConfigurationUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.util.Assert;\n\n/**\n * Allows creating a {@link ReactiveJwtDecoder} from an <a href=\n * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig\">OpenID\n * Provider Configuration</a> or\n * <a href=\"https://tools.ietf.org/html/rfc8414#section-3.1\">Authorization Server Metadata\n * Request</a> based on provided issuer and method invoked.\n *\n * @author Josh Cummings\n * @since 5.1\n */\npublic final class ReactiveJwtDecoders {\n\n\tprivate ReactiveJwtDecoders() {\n\t}\n\n\t/**\n\t * Creates a {@link ReactiveJwtDecoder} using the provided <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * by making an <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">OpenID\n\t * Provider Configuration Request</a> and using the values in the <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">OpenID\n\t * Provider Configuration Response</a> to initialize the {@link ReactiveJwtDecoder}.\n\t * @param oidcIssuerLocation the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return a {@link ReactiveJwtDecoder} that was initialized by the OpenID Provider\n\t * Configuration.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T extends ReactiveJwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {\n\t\tAssert.hasText(oidcIssuerLocation, \"oidcIssuerLocation cannot be empty\");\n\t\tMap<String, Object> configuration = JwtDecoderProviderConfigurationUtils\n\t\t\t.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);\n\t\treturn (T) withProviderConfiguration(configuration, oidcIssuerLocation);\n\t}\n\n\t/**\n\t * Creates a {@link ReactiveJwtDecoder} using the provided <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * by querying three different discovery endpoints serially, using the values in the\n\t * first successful response to initialize. If an endpoint returns anything other than\n\t * a 200 or a 4xx, the method will exit without attempting subsequent endpoints.\n\t *\n\t * The three endpoints are computed as follows, given that the {@code issuer} is\n\t * composed of a {@code host} and a {@code path}:\n\t *\n\t * <ol>\n\t * <li>{@code host/.well-known/openid-configuration/path}, as defined in\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-5\">RFC 8414's Compatibility\n\t * Notes</a>.</li>\n\t * <li>{@code issuer/.well-known/openid-configuration}, as defined in <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\">\n\t * OpenID Provider Configuration</a>.</li>\n\t * <li>{@code host/.well-known/oauth-authorization-server/path}, as defined in\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-3.1\">Authorization Server\n\t * Metadata Request</a>.</li>\n\t * </ol>\n\t *\n\t * Note that the second endpoint is the equivalent of calling\n\t * {@link ReactiveJwtDecoders#fromOidcIssuerLocation(String)}\n\t * @param issuer the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return a {@link ReactiveJwtDecoder} that was initialized by one of the described\n\t * endpoints\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T extends ReactiveJwtDecoder> T fromIssuerLocation(String issuer) {\n\t\tAssert.hasText(issuer, \"issuer cannot be empty\");\n\t\tMap<String, Object> configuration = JwtDecoderProviderConfigurationUtils\n\t\t\t.getConfigurationForIssuerLocation(issuer);\n\t\treturn (T) withProviderConfiguration(configuration, issuer);\n\t}\n\n\t/**\n\t * Build {@link ReactiveJwtDecoder} from <a href=\n\t * \"https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\">OpenID\n\t * Provider Configuration Response</a> and\n\t * <a href=\"https://tools.ietf.org/html/rfc8414#section-3.2\">Authorization Server\n\t * Metadata Response</a>.\n\t * @param configuration the configuration values\n\t * @param issuer the <a href=\n\t * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a>\n\t * @return {@link ReactiveJwtDecoder}\n\t */\n\tprivate static ReactiveJwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {\n\t\tJwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);\n\t\tObject jwksUri = configuration.get(\"jwks_uri\");\n\t\tAssert.notNull(jwksUri, \"The public JWK Set URI must not be null\");\n\t\tString jwkSetUri = jwksUri.toString();\n\t\tNimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri)\n\t\t\t.jwtProcessorCustomizer(ReactiveJwtDecoderProviderConfigurationUtils::addJWSAlgorithms)\n\t\t\t.build();\n\t\tjwtDecoder.setJwtValidator(jwtValidator);\n\t\treturn jwtDecoder;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveRemoteJWKSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.text.ParseException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport com.nimbusds.jose.RemoteKeySourceException;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.reactive.function.client.WebClient;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\nclass ReactiveRemoteJWKSource implements ReactiveJWKSource {\n\n\t/**\n\t * The cached JWK set.\n\t */\n\tprivate final AtomicReference<Mono<JWKSet>> cachedJWKSet = new AtomicReference<>(Mono.empty());\n\n\t/**\n\t * The cached JWK set URL.\n\t */\n\tprivate final AtomicReference<String> cachedJwkSetUrl = new AtomicReference<>();\n\n\tprivate WebClient webClient = WebClient.create();\n\n\tprivate final Mono<String> jwkSetUrlProvider;\n\n\tReactiveRemoteJWKSource(String jwkSetURL) {\n\t\tAssert.hasText(jwkSetURL, \"jwkSetURL cannot be empty\");\n\t\tthis.jwkSetUrlProvider = Mono.just(jwkSetURL);\n\t}\n\n\tReactiveRemoteJWKSource(Mono<String> jwkSetUrlProvider) {\n\t\tAssert.notNull(jwkSetUrlProvider, \"jwkSetUrlProvider cannot be null\");\n\t\tthis.jwkSetUrlProvider = Mono.fromCallable(this.cachedJwkSetUrl::get)\n\t\t\t.switchIfEmpty(Mono.defer(() -> jwkSetUrlProvider.doOnNext(this.cachedJwkSetUrl::set)));\n\t}\n\n\t@Override\n\tpublic Mono<List<JWK>> get(JWKSelector jwkSelector) {\n\t\t// @formatter:off\n\t\treturn this.cachedJWKSet.get()\n\t\t\t\t.switchIfEmpty(Mono.defer(this::getJWKSet))\n\t\t\t\t.flatMap((jwkSet) -> get(jwkSelector, jwkSet))\n\t\t\t\t.switchIfEmpty(Mono.defer(() -> getJWKSet()\n\t\t\t\t\t\t.map(jwkSelector::select))\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<List<JWK>> get(JWKSelector jwkSelector, JWKSet jwkSet) {\n\t\treturn Mono.defer(() -> {\n\t\t\t// Run the selector on the JWK set\n\t\t\tList<JWK> matches = jwkSelector.select(jwkSet);\n\t\t\tif (!matches.isEmpty()) {\n\t\t\t\t// Success\n\t\t\t\treturn Mono.just(matches);\n\t\t\t}\n\t\t\t// Refresh the JWK set if the sought key ID is not in the cached JWK set\n\t\t\t// Looking for JWK with specific ID?\n\t\t\tString soughtKeyID = getFirstSpecifiedKeyID(jwkSelector.getMatcher());\n\t\t\tif (soughtKeyID == null) {\n\t\t\t\t// No key ID specified, return no matches\n\t\t\t\treturn Mono.just(Collections.emptyList());\n\t\t\t}\n\t\t\tif (jwkSet.getKeyByKeyId(soughtKeyID) != null) {\n\t\t\t\t// The key ID exists in the cached JWK set, matching\n\t\t\t\t// failed for some other reason, return no matches\n\t\t\t\treturn Mono.just(Collections.emptyList());\n\t\t\t}\n\t\t\treturn Mono.empty();\n\t\t});\n\t}\n\n\t/**\n\t * Updates the cached JWK set from the configured URL.\n\t * @return The updated JWK set.\n\t * @throws RemoteKeySourceException If JWK retrieval failed.\n\t */\n\tprivate Mono<JWKSet> getJWKSet() {\n\t\t// @formatter:off\n\t\treturn this.jwkSetUrlProvider\n\t\t\t\t.flatMap((jwkSetURL) -> this.webClient.get()\n\t\t\t\t\t.uri(jwkSetURL)\n\t\t\t\t\t.retrieve()\n\t\t\t\t\t.bodyToMono(String.class)\n\t\t\t\t)\n\t\t\t\t.map(this::parse)\n\t\t\t\t.doOnNext((jwkSet) -> this.cachedJWKSet\n\t\t\t\t\t.set(Mono.just(jwkSet))\n\t\t\t\t)\n\t\t\t\t.cache();\n\t\t// @formatter:on\n\t}\n\n\tprivate JWKSet parse(String body) {\n\t\ttry {\n\t\t\treturn JWKSet.parse(body);\n\t\t}\n\t\tcatch (ParseException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the first specified key ID (kid) for a JWK matcher.\n\t * @param jwkMatcher The JWK matcher. Must not be {@code null}.\n\t * @return The first key ID, {@code null} if none.\n\t */\n\tprotected static @Nullable String getFirstSpecifiedKeyID(final JWKMatcher jwkMatcher) {\n\t\tSet<String> keyIDs = jwkMatcher.getKeyIDs();\n\t\tif (keyIDs == null || keyIDs.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn keyIDs.iterator().next();\n\t}\n\n\tvoid setWebClient(WebClient webClient) {\n\t\tthis.webClient = webClient;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/SupplierJwtDecoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.function.Supplier;\n\nimport org.springframework.util.function.SingletonSupplier;\n\n/**\n * A {@link JwtDecoder} that lazily initializes another {@link JwtDecoder}\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class SupplierJwtDecoder implements JwtDecoder {\n\n\tprivate final Supplier<JwtDecoder> delegate;\n\n\tpublic SupplierJwtDecoder(Supplier<JwtDecoder> jwtDecoderSupplier) {\n\t\tthis.delegate = SingletonSupplier.of(() -> {\n\t\t\ttry {\n\t\t\t\treturn jwtDecoderSupplier.get();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow wrapException(ex);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Jwt decode(String token) throws JwtException {\n\t\treturn this.delegate.get().decode(token);\n\t}\n\n\tprivate JwtDecoderInitializationException wrapException(Exception ex) {\n\t\treturn new JwtDecoderInitializationException(\"Failed to lazily resolve the supplied JwtDecoder instance\", ex);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/SupplierReactiveJwtDecoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Duration;\nimport java.util.function.Supplier;\n\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\n\n/**\n * A {@link ReactiveJwtDecoder} that lazily initializes another {@link ReactiveJwtDecoder}\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class SupplierReactiveJwtDecoder implements ReactiveJwtDecoder {\n\n\tprivate static final Duration FOREVER = Duration.ofMillis(Long.MAX_VALUE);\n\n\tprivate Mono<ReactiveJwtDecoder> jwtDecoderMono;\n\n\tpublic SupplierReactiveJwtDecoder(Supplier<ReactiveJwtDecoder> supplier) {\n\t\t// @formatter:off\n\t\tthis.jwtDecoderMono = Mono.fromSupplier(supplier)\n\t\t\t\t.subscribeOn(Schedulers.boundedElastic())\n\t\t\t\t.publishOn(Schedulers.parallel())\n\t\t\t\t.onErrorMap(this::wrapException)\n\t\t\t\t.cache((delegate) -> FOREVER, (ex) -> Duration.ZERO, () -> Duration.ZERO);\n\t\t// @formatter:on\n\t}\n\n\tprivate JwtDecoderInitializationException wrapException(Throwable t) {\n\t\treturn new JwtDecoderInitializationException(\"Failed to lazily resolve the supplied JwtDecoder instance\", t);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Mono<Jwt> decode(String token) throws JwtException {\n\t\treturn this.jwtDecoderMono.flatMap((decoder) -> decoder.decode(token));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/X509CertificateThumbprintValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.security.MessageDigest;\nimport java.security.cert.X509Certificate;\nimport java.util.Base64;\nimport java.util.Map;\nimport java.util.function.Supplier;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\n\n/**\n * An {@link OAuth2TokenValidator} responsible for validating the {@code x5t#S256} claim\n * (if available) in the {@link Jwt} against the SHA-256 Thumbprint of the supplied\n * {@code X509Certificate}.\n *\n * @author Joe Grandja\n * @since 6.3\n * @see OAuth2TokenValidator\n * @see Jwt\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8705#section-3\">3. Mutual-TLS Client\n * Certificate-Bound Access Tokens</a>\n * @see <a target=\"_blank\" href=\n * \"https://datatracker.ietf.org/doc/html/rfc8705#section-3.1\">3.1. JWT Certificate\n * Thumbprint Confirmation Method</a>\n */\nfinal class X509CertificateThumbprintValidator implements OAuth2TokenValidator<Jwt> {\n\n\tstatic final Supplier<X509Certificate> DEFAULT_X509_CERTIFICATE_SUPPLIER = new DefaultX509CertificateSupplier();\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final Supplier<X509Certificate> x509CertificateSupplier;\n\n\tX509CertificateThumbprintValidator(Supplier<X509Certificate> x509CertificateSupplier) {\n\t\tAssert.notNull(x509CertificateSupplier, \"x509CertificateSupplier cannot be null\");\n\t\tthis.x509CertificateSupplier = x509CertificateSupplier;\n\t}\n\n\t@Override\n\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\tMap<String, Object> confirmationMethodClaim = jwt.getClaim(\"cnf\");\n\t\tString x509CertificateThumbprintClaim = null;\n\t\tif (!CollectionUtils.isEmpty(confirmationMethodClaim) && confirmationMethodClaim.containsKey(\"x5t#S256\")) {\n\t\t\tx509CertificateThumbprintClaim = (String) confirmationMethodClaim.get(\"x5t#S256\");\n\t\t}\n\t\tif (x509CertificateThumbprintClaim == null) {\n\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t}\n\n\t\tX509Certificate x509Certificate = this.x509CertificateSupplier.get();\n\t\tif (x509Certificate == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN,\n\t\t\t\t\t\"Unable to obtain X509Certificate from current request.\", null);\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(error.toString());\n\t\t\t}\n\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t}\n\n\t\tString x509CertificateThumbprint;\n\t\ttry {\n\t\t\tx509CertificateThumbprint = computeSHA256Thumbprint(x509Certificate);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN,\n\t\t\t\t\t\"Failed to compute SHA-256 Thumbprint for X509Certificate.\", null);\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(error.toString());\n\t\t\t}\n\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t}\n\n\t\tif (!x509CertificateThumbprint.equals(x509CertificateThumbprintClaim)) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN,\n\t\t\t\t\t\"Invalid SHA-256 Thumbprint for X509Certificate.\", null);\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(error.toString());\n\t\t\t}\n\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t}\n\n\t\treturn OAuth2TokenValidatorResult.success();\n\t}\n\n\tstatic String computeSHA256Thumbprint(X509Certificate x509Certificate) throws Exception {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(x509Certificate.getEncoded());\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n\tprivate static final class DefaultX509CertificateSupplier implements Supplier<X509Certificate> {\n\n\t\t@Override\n\t\tpublic @Nullable X509Certificate get() {\n\t\t\tRequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n\t\t\tif (requestAttributes == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tX509Certificate[] clientCertificateChain = (X509Certificate[]) requestAttributes\n\t\t\t\t.getAttribute(\"jakarta.servlet.request.X509Certificate\", RequestAttributes.SCOPE_REQUEST);\n\n\t\t\treturn (clientCertificateChain != null && clientCertificateChain.length > 0) ? clientCertificateChain[0]\n\t\t\t\t\t: null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core classes and interfaces providing support for JSON Web Token (JWT).\n */\n@NullMarked\npackage org.springframework.security.oauth2.jwt;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestJwks.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose;\n\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.UUID;\n\nimport javax.crypto.SecretKey;\n\nimport com.nimbusds.jose.jwk.Curve;\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.OctetSequenceKey;\nimport com.nimbusds.jose.jwk.RSAKey;\n\n/**\n * @author Joe Grandja\n */\npublic final class TestJwks {\n\n\tprivate static final KeyPairGenerator rsaKeyPairGenerator;\n\tstatic {\n\t\ttry {\n\t\t\trsaKeyPairGenerator = KeyPairGenerator.getInstance(\"RSA\");\n\t\t\trsaKeyPairGenerator.initialize(2048);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\t// @formatter:off\n\tpublic static final RSAKey DEFAULT_RSA_JWK =\n\t\t\tjwk(\n\t\t\t\t\tTestKeys.DEFAULT_PUBLIC_KEY,\n\t\t\t\t\tTestKeys.DEFAULT_PRIVATE_KEY\n\t\t\t).build();\n\t// @formatter:on\n\n\t// @formatter:off\n\tpublic static final ECKey DEFAULT_EC_JWK =\n\t\t\tjwk(\n\t\t\t\t\t(ECPublicKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPublic(),\n\t\t\t\t\t(ECPrivateKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPrivate()\n\t\t\t).build();\n\t// @formatter:on\n\n\t// @formatter:off\n\tpublic static final OctetSequenceKey DEFAULT_SECRET_JWK =\n\t\t\tjwk(\n\t\t\t\t\tTestKeys.DEFAULT_SECRET_KEY\n\t\t\t).build();\n\t// @formatter:on\n\n\tprivate TestJwks() {\n\t}\n\n\tpublic static RSAKey.Builder generateRsa() {\n\t\tKeyPair keyPair = rsaKeyPairGenerator.generateKeyPair();\n\t\tRSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();\n\t\tRSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();\n\t\t// @formatter:off\n\t\treturn jwk(publicKey, privateKey)\n\t\t\t\t.keyID(UUID.randomUUID().toString());\n\t\t// @formatter:on\n\t}\n\n\tpublic static RSAKey.Builder rsa() {\n\t\treturn jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY);\n\t}\n\n\tpublic static RSAKey.Builder jwk(RSAPublicKey publicKey, RSAPrivateKey privateKey) {\n\t\t// @formatter:off\n\t\treturn new RSAKey.Builder(publicKey)\n\t\t\t\t.privateKey(privateKey)\n\t\t\t\t.keyID(\"rsa-jwk-kid\");\n\t\t// @formatter:on\n\t}\n\n\tpublic static ECKey.Builder jwk(ECPublicKey publicKey, ECPrivateKey privateKey) {\n\t\t// @formatter:off\n\t\tCurve curve = Curve.forECParameterSpec(publicKey.getParams());\n\t\treturn new ECKey.Builder(curve, publicKey)\n\t\t\t\t.privateKey(privateKey)\n\t\t\t\t.keyID(\"ec-jwk-kid\");\n\t\t// @formatter:on\n\t}\n\n\tpublic static OctetSequenceKey.Builder jwk(SecretKey secretKey) {\n\t\t// @formatter:off\n\t\treturn new OctetSequenceKey.Builder(secretKey)\n\t\t\t\t.keyID(\"secret-jwk-kid\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestKeys.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose;\n\nimport java.math.BigInteger;\nimport java.security.KeyFactory;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.ECFieldFp;\nimport java.security.spec.ECParameterSpec;\nimport java.security.spec.ECPoint;\nimport java.security.spec.EllipticCurve;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.util.Base64;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\n/**\n * @author Joe Grandja\n * @since 5.2\n */\npublic final class TestKeys {\n\n\tpublic static final KeyFactory kf;\n\tstatic {\n\t\ttry {\n\t\t\tkf = KeyFactory.getInstance(\"RSA\");\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\tpublic static final String DEFAULT_ENCODED_SECRET_KEY = \"bCzY/M48bbkwBEWjmNSIEPfwApcvXOnkCxORBEbPr+4=\";\n\n\tpublic static final SecretKey DEFAULT_SECRET_KEY = new SecretKeySpec(\n\t\t\tBase64.getDecoder().decode(DEFAULT_ENCODED_SECRET_KEY), \"AES\");\n\n\t// @formatter:off\n\tpublic static final String DEFAULT_RSA_PUBLIC_KEY = \"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd\"\n\t\t\t+ \"7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv\"\n\t\t\t+ \"c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6\"\n\t\t\t+ \"iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2\"\n\t\t\t+ \"kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o\"\n\t\t\t+ \"RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj\"\n\t\t\t+ \"KwIDAQAB\";\n\t// @formatter:on\n\n\tpublic static final RSAPublicKey DEFAULT_PUBLIC_KEY;\n\tstatic {\n\t\tX509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(DEFAULT_RSA_PUBLIC_KEY));\n\t\ttry {\n\t\t\tDEFAULT_PUBLIC_KEY = (RSAPublicKey) kf.generatePublic(spec);\n\t\t}\n\t\tcatch (InvalidKeySpecException ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\t// @formatter:off\n\tpublic static final String DEFAULT_RSA_PRIVATE_KEY = \"MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcWWomvlNGyQhA\"\n\t\t\t+ \"iB0TcN3sP2VuhZ1xNRPxr58lHswC9Cbtdc2hiSbe/sxAvU1i0O8vaXwICdzRZ1JM\"\n\t\t\t+ \"g1TohG9zkqqjZDhyw1f1Ic6YR/OhE6NCpqERy97WMFeW6gJd1i5inHj/W19GAbqK\"\n\t\t\t+ \"LhSHGHqIjyo0wlBf58t+qFt9h/EFBVE/LAGQBsg/jHUQCxsLoVI2aSELGIw2oSDF\"\n\t\t\t+ \"oiljwLaQl0n9khX5ZbiegN3OkqodzCYHwWyu6aVVj8M1W9RIMiKmKr09s/gf31Nc\"\n\t\t\t+ \"3WjvjqhFo1rTuurWGgKAxJLL7zlJqAKjGWbIT4P6h/1Kwxjw6X23St3OmhsG6HIn\"\n\t\t\t+ \"+jl1++MrAgMBAAECggEBAMf820wop3pyUOwI3aLcaH7YFx5VZMzvqJdNlvpg1jbE\"\n\t\t\t+ \"E2Sn66b1zPLNfOIxLcBG8x8r9Ody1Bi2Vsqc0/5o3KKfdgHvnxAB3Z3dPh2WCDek\"\n\t\t\t+ \"lCOVClEVoLzziTuuTdGO5/CWJXdWHcVzIjPxmK34eJXioiLaTYqN3XKqKMdpD0ZG\"\n\t\t\t+ \"mtNTGvGf+9fQ4i94t0WqIxpMpGt7NM4RHy3+Onggev0zLiDANC23mWrTsUgect/7\"\n\t\t\t+ \"62TYg8g1bKwLAb9wCBT+BiOuCc2wrArRLOJgUkj/F4/gtrR9ima34SvWUyoUaKA0\"\n\t\t\t+ \"bi4YBX9l8oJwFGHbU9uFGEMnH0T/V0KtIB7qetReywkCgYEA9cFyfBIQrYISV/OA\"\n\t\t\t+ \"+Z0bo3vh2aL0QgKrSXZ924cLt7itQAHNZ2ya+e3JRlTczi5mnWfjPWZ6eJB/8MlH\"\n\t\t\t+ \"Gpn12o/POEkU+XjZZSPe1RWGt5g0S3lWqyx9toCS9ACXcN9tGbaqcFSVI73zVTRA\"\n\t\t\t+ \"8J9grR0fbGn7jaTlTX2tnlOTQ60CgYEA5YjYpEq4L8UUMFkuj+BsS3u0oEBnzuHd\"\n\t\t\t+ \"I9LEHmN+CMPosvabQu5wkJXLuqo2TxRnAznsA8R3pCLkdPGoWMCiWRAsCn979TdY\"\n\t\t\t+ \"QbqO2qvBAD2Q19GtY7lIu6C35/enQWzJUMQE3WW0OvjLzZ0l/9mA2FBRR+3F9A1d\"\n\t\t\t+ \"rBdnmv0c3TcCgYEAi2i+ggVZcqPbtgrLOk5WVGo9F1GqUBvlgNn30WWNTx4zIaEk\"\n\t\t\t+ \"HSxtyaOLTxtq2odV7Kr3LGiKxwPpn/T+Ief+oIp92YcTn+VfJVGw4Z3BezqbR8lA\"\n\t\t\t+ \"Uf/+HF5ZfpMrVXtZD4Igs3I33Duv4sCuqhEvLWTc44pHifVloozNxYfRfU0CgYBN\"\n\t\t\t+ \"HXa7a6cJ1Yp829l62QlJKtx6Ymj95oAnQu5Ez2ROiZMqXRO4nucOjGUP55Orac1a\"\n\t\t\t+ \"FiGm+mC/skFS0MWgW8evaHGDbWU180wheQ35hW6oKAb7myRHtr4q20ouEtQMdQIF\"\n\t\t\t+ \"snV39G1iyqeeAsf7dxWElydXpRi2b68i3BIgzhzebQKBgQCdUQuTsqV9y/JFpu6H\"\n\t\t\t+ \"c5TVvhG/ubfBspI5DhQqIGijnVBzFT//UfIYMSKJo75qqBEyP2EJSmCsunWsAFsM\"\n\t\t\t+ \"TszuiGTkrKcZy9G0wJqPztZZl2F2+bJgnA6nBEV7g5PA4Af+QSmaIhRwqGDAuROR\"\n\t\t\t+ \"47jndeyIaMTNETEmOnms+as17g==\";\n\t// @formatter:on\n\n\tpublic static final RSAPrivateKey DEFAULT_PRIVATE_KEY;\n\tstatic {\n\t\tPKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(DEFAULT_RSA_PRIVATE_KEY));\n\t\ttry {\n\t\t\tDEFAULT_PRIVATE_KEY = (RSAPrivateKey) kf.generatePrivate(spec);\n\t\t}\n\t\tcatch (InvalidKeySpecException ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tpublic static final KeyPair DEFAULT_RSA_KEY_PAIR = new KeyPair(DEFAULT_PUBLIC_KEY, DEFAULT_PRIVATE_KEY);\n\n\tpublic static final KeyPair DEFAULT_EC_KEY_PAIR = generateEcKeyPair();\n\n\tstatic KeyPair generateEcKeyPair() {\n\t\tEllipticCurve ellipticCurve = new EllipticCurve(\n\t\t\t\tnew ECFieldFp(new BigInteger(\n\t\t\t\t\t\t\"115792089210356248762697446949407573530086143415290314195533631308867097853951\")),\n\t\t\t\tnew BigInteger(\"115792089210356248762697446949407573530086143415290314195533631308867097853948\"),\n\t\t\t\tnew BigInteger(\"41058363725152142129326129780047268409114441015993725554835256314039467401291\"));\n\t\tECPoint ecPoint = new ECPoint(\n\t\t\t\tnew BigInteger(\"48439561293906451759052585252797914202762949526041747995844080717082404635286\"),\n\t\t\t\tnew BigInteger(\"36134250956749795798585127919587881956611106672985015071877198253568414405109\"));\n\t\tECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, ecPoint,\n\t\t\t\tnew BigInteger(\"115792089210356248762697446949407573529996955224135760342422259061068512044369\"), 1);\n\n\t\tKeyPair keyPair;\n\t\ttry {\n\t\t\tKeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"EC\");\n\t\t\tkeyPairGenerator.initialize(ecParameterSpec);\n\t\t\tkeyPair = keyPairGenerator.generateKeyPair();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t\treturn keyPair;\n\t}\n\n\tprivate TestKeys() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestX509Certificates.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose;\n\nimport java.security.KeyPair;\nimport java.security.cert.X509Certificate;\n\n/**\n * @author Joe Grandja\n * @since 6.3\n */\npublic final class TestX509Certificates {\n\n\tpublic static final X509Certificate[] DEFAULT_PKI_CERTIFICATE;\n\tstatic {\n\t\ttry {\n\t\t\t// Generate the Root certificate (Trust Anchor or most-trusted CA)\n\t\t\tKeyPair rootKeyPair = X509CertificateUtils.generateRSAKeyPair();\n\t\t\tString distinguishedName = \"CN=spring-samples-trusted-ca, OU=Spring Samples, O=Spring, C=US\";\n\t\t\tX509Certificate rootCertificate = X509CertificateUtils.createTrustAnchorCertificate(rootKeyPair,\n\t\t\t\t\tdistinguishedName);\n\n\t\t\t// Generate the CA (intermediary) certificate\n\t\t\tKeyPair caKeyPair = X509CertificateUtils.generateRSAKeyPair();\n\t\t\tdistinguishedName = \"CN=spring-samples-ca, OU=Spring Samples, O=Spring, C=US\";\n\t\t\tX509Certificate caCertificate = X509CertificateUtils.createCACertificate(rootCertificate,\n\t\t\t\t\trootKeyPair.getPrivate(), caKeyPair.getPublic(), distinguishedName);\n\n\t\t\t// Generate certificate for subject1\n\t\t\tKeyPair subject1KeyPair = X509CertificateUtils.generateRSAKeyPair();\n\t\t\tdistinguishedName = \"CN=subject1, OU=Spring Samples, O=Spring, C=US\";\n\t\t\tX509Certificate subject1Certificate = X509CertificateUtils.createEndEntityCertificate(caCertificate,\n\t\t\t\t\tcaKeyPair.getPrivate(), subject1KeyPair.getPublic(), distinguishedName);\n\n\t\t\tDEFAULT_PKI_CERTIFICATE = new X509Certificate[] { subject1Certificate, caCertificate, rootCertificate };\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\tpublic static final X509Certificate[] DEFAULT_SELF_SIGNED_CERTIFICATE;\n\tstatic {\n\t\ttry {\n\t\t\t// Generate self-signed certificate for subject1\n\t\t\tKeyPair keyPair = X509CertificateUtils.generateRSAKeyPair();\n\t\t\tString distinguishedName = \"CN=subject1, OU=Spring Samples, O=Spring, C=US\";\n\t\t\tX509Certificate subject1SelfSignedCertificate = X509CertificateUtils.createTrustAnchorCertificate(keyPair,\n\t\t\t\t\tdistinguishedName);\n\n\t\t\tDEFAULT_SELF_SIGNED_CERTIFICATE = new X509Certificate[] { subject1SelfSignedCertificate };\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\tprivate TestX509Certificates() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/X509CertificateUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose;\n\nimport java.math.BigInteger;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.security.Security;\nimport java.security.cert.X509Certificate;\nimport java.security.spec.RSAKeyGenParameterSpec;\nimport java.util.Calendar;\nimport java.util.Date;\n\nimport javax.security.auth.x500.X500Principal;\n\nimport org.bouncycastle.asn1.x509.BasicConstraints;\nimport org.bouncycastle.asn1.x509.Extension;\nimport org.bouncycastle.asn1.x509.KeyUsage;\nimport org.bouncycastle.cert.X509v3CertificateBuilder;\nimport org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;\nimport org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;\nimport org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\nimport org.bouncycastle.operator.ContentSigner;\nimport org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;\n\n/**\n * @author Joe Grandja\n * @since 6.3\n */\nfinal class X509CertificateUtils {\n\n\tprivate static final String BC_PROVIDER = \"BC\";\n\n\tprivate static final String SHA256_RSA_SIGNATURE_ALGORITHM = \"SHA256withRSA\";\n\n\tprivate static final Date DEFAULT_START_DATE;\n\n\tprivate static final Date DEFAULT_END_DATE;\n\n\tstatic {\n\t\tSecurity.addProvider(new BouncyCastleProvider());\n\n\t\t// Setup default certificate start date to yesterday and end date for 1 year\n\t\t// validity\n\t\tCalendar calendar = Calendar.getInstance();\n\t\tcalendar.add(Calendar.DATE, -1);\n\t\tDEFAULT_START_DATE = calendar.getTime();\n\t\tcalendar.add(Calendar.YEAR, 1);\n\t\tDEFAULT_END_DATE = calendar.getTime();\n\t}\n\n\tprivate X509CertificateUtils() {\n\t}\n\n\tstatic KeyPair generateRSAKeyPair() {\n\t\tKeyPair keyPair;\n\t\ttry {\n\t\t\tKeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"RSA\", BC_PROVIDER);\n\t\t\tkeyPairGenerator.initialize(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4));\n\t\t\tkeyPair = keyPairGenerator.generateKeyPair();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t\treturn keyPair;\n\t}\n\n\tstatic X509Certificate createTrustAnchorCertificate(KeyPair keyPair, String distinguishedName) throws Exception {\n\t\tX500Principal subject = new X500Principal(distinguishedName);\n\t\tBigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));\n\n\t\tX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(subject, serialNum, DEFAULT_START_DATE,\n\t\t\t\tDEFAULT_END_DATE, subject, keyPair.getPublic());\n\n\t\t// Add Extensions\n\t\tJcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();\n\t\tcertBuilder\n\t\t\t// A BasicConstraints to mark root certificate as CA certificate\n\t\t\t.addExtension(Extension.basicConstraints, true, new BasicConstraints(true))\n\t\t\t.addExtension(Extension.subjectKeyIdentifier, false,\n\t\t\t\t\textensionUtils.createSubjectKeyIdentifier(keyPair.getPublic()));\n\n\t\tContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)\n\t\t\t.build(keyPair.getPrivate());\n\n\t\tJcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);\n\n\t\treturn converter.getCertificate(certBuilder.build(signer));\n\t}\n\n\tstatic X509Certificate createCACertificate(X509Certificate signerCert, PrivateKey signerKey, PublicKey certKey,\n\t\t\tString distinguishedName) throws Exception {\n\n\t\tX500Principal subject = new X500Principal(distinguishedName);\n\t\tBigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));\n\n\t\tX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(signerCert.getSubjectX500Principal(),\n\t\t\t\tserialNum, DEFAULT_START_DATE, DEFAULT_END_DATE, subject, certKey);\n\n\t\t// Add Extensions\n\t\tJcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();\n\t\tcertBuilder\n\t\t\t// A BasicConstraints to mark as CA certificate and how many CA certificates\n\t\t\t// can follow it in the chain\n\t\t\t// (with 0 meaning the chain ends with the next certificate in the chain).\n\t\t\t.addExtension(Extension.basicConstraints, true, new BasicConstraints(0))\n\t\t\t// KeyUsage specifies what the public key in the certificate can be used for.\n\t\t\t// In this case, it can be used for signing other certificates and/or\n\t\t\t// signing Certificate Revocation Lists (CRLs).\n\t\t\t.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign))\n\t\t\t.addExtension(Extension.authorityKeyIdentifier, false,\n\t\t\t\t\textensionUtils.createAuthorityKeyIdentifier(signerCert))\n\t\t\t.addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(certKey));\n\n\t\tContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)\n\t\t\t.build(signerKey);\n\n\t\tJcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);\n\n\t\treturn converter.getCertificate(certBuilder.build(signer));\n\t}\n\n\tstatic X509Certificate createEndEntityCertificate(X509Certificate signerCert, PrivateKey signerKey,\n\t\t\tPublicKey certKey, String distinguishedName) throws Exception {\n\n\t\tX500Principal subject = new X500Principal(distinguishedName);\n\t\tBigInteger serialNum = new BigInteger(Long.toString(new SecureRandom().nextLong()));\n\n\t\tX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(signerCert.getSubjectX500Principal(),\n\t\t\t\tserialNum, DEFAULT_START_DATE, DEFAULT_END_DATE, subject, certKey);\n\n\t\tJcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();\n\t\tcertBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false))\n\t\t\t.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature))\n\t\t\t.addExtension(Extension.authorityKeyIdentifier, false,\n\t\t\t\t\textensionUtils.createAuthorityKeyIdentifier(signerCert))\n\t\t\t.addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(certKey));\n\n\t\tContentSigner signer = new JcaContentSignerBuilder(SHA256_RSA_SIGNATURE_ALGORITHM).setProvider(BC_PROVIDER)\n\t\t\t.build(signerKey);\n\n\t\tJcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC_PROVIDER);\n\n\t\treturn converter.getCertificate(certBuilder.build(signer));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/jws/MacAlgorithmTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose.jws;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link MacAlgorithm}\n *\n * @author Joe Grandja\n * @since 5.2\n */\npublic class MacAlgorithmTests {\n\n\t@Test\n\tpublic void fromWhenAlgorithmValidThenResolves() {\n\t\tassertThat(MacAlgorithm.from(JwsAlgorithms.HS256)).isEqualTo(MacAlgorithm.HS256);\n\t\tassertThat(MacAlgorithm.from(JwsAlgorithms.HS384)).isEqualTo(MacAlgorithm.HS384);\n\t\tassertThat(MacAlgorithm.from(JwsAlgorithms.HS512)).isEqualTo(MacAlgorithm.HS512);\n\t}\n\n\t@Test\n\tpublic void fromWhenAlgorithmInvalidThenDoesNotResolve() {\n\t\tassertThat(MacAlgorithm.from(\"invalid\")).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/jws/SignatureAlgorithmTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jose.jws;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link SignatureAlgorithm}\n *\n * @author Joe Grandja\n * @since 5.2\n */\npublic class SignatureAlgorithmTests {\n\n\t@Test\n\tpublic void fromWhenAlgorithmValidThenResolves() {\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.RS256)).isEqualTo(SignatureAlgorithm.RS256);\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.RS384)).isEqualTo(SignatureAlgorithm.RS384);\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.RS512)).isEqualTo(SignatureAlgorithm.RS512);\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.ES256)).isEqualTo(SignatureAlgorithm.ES256);\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.ES384)).isEqualTo(SignatureAlgorithm.ES384);\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.ES512)).isEqualTo(SignatureAlgorithm.ES512);\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.PS256)).isEqualTo(SignatureAlgorithm.PS256);\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.PS384)).isEqualTo(SignatureAlgorithm.PS384);\n\t\tassertThat(SignatureAlgorithm.from(JwsAlgorithms.PS512)).isEqualTo(SignatureAlgorithm.PS512);\n\t}\n\n\t@Test\n\tpublic void fromWhenAlgorithmInvalidThenDoesNotResolve() {\n\t\tassertThat(SignatureAlgorithm.from(\"invalid\")).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link DPoPProofJwtDecoderFactory}.\n *\n * @author Joe Grandja\n */\npublic class DPoPProofJwtDecoderFactoryTests {\n\n\tprivate JWKSource<SecurityContext> jwkSource;\n\n\tprivate NimbusJwtEncoder jwtEncoder;\n\n\tprivate DPoPProofJwtDecoderFactory jwtDecoderFactory = new DPoPProofJwtDecoderFactory();\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.jwkSource = mock(JWKSource.class);\n\t\tthis.jwtEncoder = new NimbusJwtEncoder(this.jwkSource);\n\t}\n\n\t@Test\n\tpublic void setJwtValidatorFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.jwtDecoderFactory.setJwtValidatorFactory(null))\n\t\t\t.withMessage(\"jwtValidatorFactory cannot be null\");\n\t}\n\n\t@Test\n\tpublic void createDecoderWhenContextNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.jwtDecoderFactory.createDecoder(null))\n\t\t\t.withMessage(\"dPoPProofContext cannot be null\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenJoseTypeInvalidThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"invalid-type\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"JOSE header typ (type) invalid-type not allowed\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwkMissingThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n//\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"Missing jwk parameter in JWS Header.\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenMethodInvalidThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(\"POST\")\t\t// Mismatch\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"The htm claim is not valid\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenTargetUriInvalidThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(\"https://resource2\")\t\t// Mismatch\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"The htu claim is not valid\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenJtiMissingThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n//\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"jti claim is required\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenJtiAlreadyUsedThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tjwtDecoder.decode(dPoPProofContext.getDPoPProof());\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"jti claim is invalid\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenIatMissingThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n//\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"iat claim is required\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenIatBeforeTimeWindowThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\tClock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault());\n\t\tJwtIssuedAtValidator issuedAtValidator = new JwtIssuedAtValidator(true);\n\t\tissuedAtValidator.setClock(clock);\n\t\tFunction<DPoPProofContext, OAuth2TokenValidator<Jwt>> validatorFactory = (context) -> issuedAtValidator;\n\t\tDPoPProofJwtDecoderFactory jwtDecoderFactory = new DPoPProofJwtDecoderFactory();\n\t\tjwtDecoderFactory.setJwtValidatorFactory(validatorFactory);\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tInstant issuedAt = Instant.now(clock).minus(Duration.ofSeconds(65));\t\t// now minus 65 seconds\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"iat claim is invalid\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenIatAfterTimeWindowThenThrowBadJwtException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\tClock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault());\n\t\tJwtIssuedAtValidator issuedAtValidator = new JwtIssuedAtValidator(true);\n\t\tissuedAtValidator.setClock(clock);\n\t\tFunction<DPoPProofContext, OAuth2TokenValidator<Jwt>> validatorFactory = (context) -> issuedAtValidator;\n\t\tDPoPProofJwtDecoderFactory jwtDecoderFactory = new DPoPProofJwtDecoderFactory();\n\t\tjwtDecoderFactory.setJwtValidatorFactory(validatorFactory);\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tInstant issuedAt = Instant.now(clock).plus(Duration.ofSeconds(65));\t\t// now plus 65 seconds\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = jwtDecoderFactory.createDecoder(dPoPProofContext);\n\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))\n\t\t\t.withMessageContaining(\"iat claim is invalid\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenDPoPProofValidThenDecoded() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tString method = \"GET\";\n\t\tString targetUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = rsaJwk.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", targetUri)\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\t// @formatter:off\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPProof.getTokenValue())\n\t\t\t\t.method(method)\n\t\t\t\t.targetUri(targetUri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext);\n\t\tjwtDecoder.decode(dPoPProof.getTokenValue());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwsHeaderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link JwsHeader}.\n *\n * @author Joe Grandja\n */\npublic class JwsHeaderTests {\n\n\t@Test\n\tpublic void withWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> JwsHeader.with(null))\n\t\t\t.withMessage(\"jwsAlgorithm cannot be null\");\n\t}\n\n\t@Test\n\tpublic void fromWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> JwsHeader.from(null))\n\t\t\t.withMessage(\"headers cannot be null\");\n\t}\n\n\t@Test\n\tpublic void fromWhenHeadersProvidedThenCopied() {\n\t\tJwsHeader expectedJwsHeader = TestJwsHeaders.jwsHeader().build();\n\t\tJwsHeader jwsHeader = JwsHeader.from(expectedJwsHeader).build();\n\t\tassertThat(jwsHeader.getHeaders()).isEqualTo(expectedJwsHeader.getHeaders());\n\t}\n\n\t@Test\n\tpublic void buildWhenAllHeadersProvidedThenAllHeadersAreSet() {\n\t\tJwsHeader expectedJwsHeader = TestJwsHeaders.jwsHeader().build();\n\n\t\t// @formatter:off\n\t\tJwsHeader jwsHeader = JwsHeader.with(expectedJwsHeader.getAlgorithm())\n\t\t\t\t.jwkSetUrl(expectedJwsHeader.getJwkSetUrl().toExternalForm())\n\t\t\t\t.jwk(expectedJwsHeader.getJwk())\n\t\t\t\t.keyId(expectedJwsHeader.getKeyId())\n\t\t\t\t.x509Url(expectedJwsHeader.getX509Url().toExternalForm())\n\t\t\t\t.x509CertificateChain(expectedJwsHeader.getX509CertificateChain())\n\t\t\t\t.x509SHA1Thumbprint(expectedJwsHeader.getX509SHA1Thumbprint())\n\t\t\t\t.x509SHA256Thumbprint(expectedJwsHeader.getX509SHA256Thumbprint())\n\t\t\t\t.type(expectedJwsHeader.getType())\n\t\t\t\t.contentType(expectedJwsHeader.getContentType())\n\t\t\t\t.criticalHeader(\"critical-header1-name\", \"critical-header1-value\")\n\t\t\t\t.criticalHeader(\"critical-header2-name\", \"critical-header2-value\")\n\t\t\t\t.headers((headers) -> headers.put(\"custom-header-name\", \"custom-header-value\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(jwsHeader.getAlgorithm()).isEqualTo(expectedJwsHeader.getAlgorithm());\n\t\tassertThat(jwsHeader.getJwkSetUrl()).isEqualTo(expectedJwsHeader.getJwkSetUrl());\n\t\tassertThat(jwsHeader.getJwk()).isEqualTo(expectedJwsHeader.getJwk());\n\t\tassertThat(jwsHeader.getKeyId()).isEqualTo(expectedJwsHeader.getKeyId());\n\t\tassertThat(jwsHeader.getX509Url()).isEqualTo(expectedJwsHeader.getX509Url());\n\t\tassertThat(jwsHeader.getX509CertificateChain()).isEqualTo(expectedJwsHeader.getX509CertificateChain());\n\t\tassertThat(jwsHeader.getX509SHA1Thumbprint()).isEqualTo(expectedJwsHeader.getX509SHA1Thumbprint());\n\t\tassertThat(jwsHeader.getX509SHA256Thumbprint()).isEqualTo(expectedJwsHeader.getX509SHA256Thumbprint());\n\t\tassertThat(jwsHeader.getType()).isEqualTo(expectedJwsHeader.getType());\n\t\tassertThat(jwsHeader.getContentType()).isEqualTo(expectedJwsHeader.getContentType());\n\t\tassertThat(jwsHeader.getCritical()).containsExactlyInAnyOrder(\"critical-header1-name\", \"critical-header2-name\");\n\t\tassertThat(jwsHeader.<String>getHeader(\"critical-header1-name\")).isEqualTo(\"critical-header1-value\");\n\t\tassertThat(jwsHeader.<String>getHeader(\"critical-header2-name\")).isEqualTo(\"critical-header2-value\");\n\t\tassertThat(jwsHeader.<String>getHeader(\"custom-header-name\")).isEqualTo(\"custom-header-value\");\n\t}\n\n\t@Test\n\tpublic void headerWhenNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> JwsHeader.with(SignatureAlgorithm.RS256).header(null, \"value\"))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void headerWhenValueNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> JwsHeader.with(SignatureAlgorithm.RS256).header(\"name\", null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n\t@Test\n\tpublic void getHeaderWhenNullThenThrowIllegalArgumentException() {\n\t\tJwsHeader jwsHeader = TestJwsHeaders.jwsHeader().build();\n\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> jwsHeader.getHeader(null))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtAudienceValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link JwtAudienceValidator}.\n *\n * @author Vedran Pavic\n */\nclass JwtAudienceValidatorTests {\n\n\tprivate final JwtAudienceValidator validator = new JwtAudienceValidator(\"audience\");\n\n\t@Test\n\tvoid givenJwtWithMatchingAudienceThenShouldValidate() {\n\t\tJwt jwt = TestJwts.jwt().audience(List.of(\"audience\")).build();\n\t\tOAuth2TokenValidatorResult result = this.validator.validate(jwt);\n\t\tassertThat(result).isEqualTo(OAuth2TokenValidatorResult.success());\n\t}\n\n\t@Test\n\tvoid givenJwtWithoutMatchingAudienceThenShouldValidate() {\n\t\tJwt jwt = TestJwts.jwt().audience(List.of(\"other\")).build();\n\t\tOAuth2TokenValidatorResult result = this.validator.validate(jwt);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t}\n\n\t@Test\n\tvoid givenJwtWithoutAudienceThenShouldValidate() {\n\t\tJwt jwt = TestJwts.jwt().audience(null).build();\n\t\tOAuth2TokenValidatorResult result = this.validator.validate(jwt);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtBuilderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link Jwt.Builder}.\n *\n * @author Jérôme Wacongne &lt;ch4mp&#64;c4-soft.com&gt;\n * @author Josh Cummings\n */\npublic class JwtBuilderTests {\n\n\t@Test\n\tpublic void buildWhenCalledTwiceThenGeneratesTwoJwts() {\n\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\");\n\t\t// @formatter:off\n\t\tJwt first = jwtBuilder.tokenValue(\"V1\")\n\t\t\t\t.header(\"TEST_HEADER_1\", \"H1\")\n\t\t\t\t.claim(\"TEST_CLAIM_1\", \"C1\")\n\t\t\t\t.build();\n\t\tJwt second = jwtBuilder.tokenValue(\"V2\")\n\t\t\t\t.header(\"TEST_HEADER_1\", \"H2\")\n\t\t\t\t.header(\"TEST_HEADER_2\", \"H3\")\n\t\t\t\t.claim(\"TEST_CLAIM_1\", \"C2\")\n\t\t\t\t.claim(\"TEST_CLAIM_2\", \"C3\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(first.getHeaders()).hasSize(1);\n\t\tassertThat(first.getHeaders()).containsEntry(\"TEST_HEADER_1\", \"H1\");\n\t\tassertThat(first.getClaims()).hasSize(1);\n\t\tassertThat(first.getClaims()).containsEntry(\"TEST_CLAIM_1\", \"C1\");\n\t\tassertThat(first.getTokenValue()).isEqualTo(\"V1\");\n\t\tassertThat(second.getHeaders()).hasSize(2);\n\t\tassertThat(second.getHeaders()).containsEntry(\"TEST_HEADER_1\", \"H2\");\n\t\tassertThat(second.getHeaders()).containsEntry(\"TEST_HEADER_2\", \"H3\");\n\t\tassertThat(second.getClaims()).hasSize(2);\n\t\tassertThat(second.getClaims()).containsEntry(\"TEST_CLAIM_1\", \"C2\");\n\t\tassertThat(second.getClaims()).containsEntry(\"TEST_CLAIM_2\", \"C3\");\n\t\tassertThat(second.getTokenValue()).isEqualTo(\"V2\");\n\t}\n\n\t@Test\n\tpublic void expiresAtWhenUsingGenericOrNamedClaimMethodRequiresInstant() {\n\t\t// @formatter:off\n\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.header(\"needs\", \"a header\");\n\t\t// @formatter:on\n\t\tInstant now = Instant.now();\n\t\tJwt jwt = jwtBuilder.expiresAt(now).build();\n\t\tassertThat(jwt.getExpiresAt()).isSameAs(now);\n\t\tjwt = jwtBuilder.expiresAt(now).build();\n\t\tassertThat(jwt.getExpiresAt()).isSameAs(now);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> jwtBuilder.claim(JwtClaimNames.EXP, \"not an instant\").build());\n\t}\n\n\t@Test\n\tpublic void issuedAtWhenUsingGenericOrNamedClaimMethodRequiresInstant() {\n\t\t// @formatter:off\n\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.header(\"needs\", \"a header\");\n\t\t// @formatter:on\n\t\tInstant now = Instant.now();\n\t\tJwt jwt = jwtBuilder.issuedAt(now).build();\n\t\tassertThat(jwt.getIssuedAt()).isSameAs(now);\n\t\tjwt = jwtBuilder.issuedAt(now).build();\n\t\tassertThat(jwt.getIssuedAt()).isSameAs(now);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> jwtBuilder.claim(JwtClaimNames.IAT, \"not an instant\").build());\n\t}\n\n\t@Test\n\tpublic void subjectWhenUsingGenericOrNamedClaimMethodThenLastOneWins() {\n\t\t// @formatter:off\n\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.header(\"needs\", \"a header\");\n\t\t// @formatter:on\n\t\tString generic = new String(\"sub\");\n\t\tString named = new String(\"sub\");\n\t\tJwt jwt = jwtBuilder.subject(named).claim(JwtClaimNames.SUB, generic).build();\n\t\tassertThat(jwt.getSubject()).isSameAs(generic);\n\t\tjwt = jwtBuilder.claim(JwtClaimNames.SUB, generic).subject(named).build();\n\t\tassertThat(jwt.getSubject()).isSameAs(named);\n\t}\n\n\t@Test\n\tpublic void claimsWhenRemovingAClaimThenIsNotPresent() {\n\t\t// @formatter:off\n\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.claim(\"needs\", \"a claim\")\n\t\t\t\t.header(\"needs\", \"a header\");\n\t\tJwt jwt = jwtBuilder.subject(\"sub\")\n\t\t\t\t.claims((claims) -> claims.remove(JwtClaimNames.SUB))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(jwt.getSubject()).isNull();\n\t}\n\n\t@Test\n\tpublic void claimsWhenAddingAClaimThenIsPresent() {\n\t\t// @formatter:off\n\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.header(\"needs\", \"a header\");\n\t\t// @formatter:on\n\t\tString name = new String(\"name\");\n\t\tString value = new String(\"value\");\n\t\tJwt jwt = jwtBuilder.claims((claims) -> claims.put(name, value)).build();\n\t\tassertThat(jwt.getClaims()).hasSize(1);\n\t\tassertThat(jwt.getClaims().get(name)).isSameAs(value);\n\t}\n\n\t@Test\n\tpublic void headersWhenRemovingAClaimThenIsNotPresent() {\n\t\t// @formatter:off\n\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.claim(\"needs\", \"a claim\")\n\t\t\t\t.header(\"needs\", \"a header\");\n\t\tJwt jwt = jwtBuilder.header(\"alg\", \"none\")\n\t\t\t\t.headers((headers) -> headers.remove(\"alg\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(jwt.getHeaders().get(\"alg\")).isNull();\n\t}\n\n\t@Test\n\tpublic void headersWhenAddingAClaimThenIsPresent() {\n\t\t// @formatter:off\n\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.claim(\"needs\", \"a claim\");\n\t\t// @formatter:on\n\t\tString name = new String(\"name\");\n\t\tString value = new String(\"value\");\n\t\t// @formatter:off\n\t\tJwt jwt = jwtBuilder.headers((headers) -> headers.put(name, value))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(jwt.getHeaders()).hasSize(1);\n\t\tassertThat(jwt.getHeaders().get(name)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Collection;\nimport java.util.Objects;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link JwtClaimValidator}.\n *\n * @author Zeeshan Adnan\n */\npublic class JwtClaimValidatorTests {\n\n\tprivate static final Predicate<String> test = (claim) -> claim.equals(\"http://test\");\n\n\tprivate final JwtClaimValidator<String> validator = new JwtClaimValidator<>(JwtClaimNames.ISS, test);\n\n\t@Test\n\tpublic void validateWhenClaimPassesTheTestThenReturnsSuccess() {\n\t\tJwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, \"http://test\").build();\n\t\tassertThat(this.validator.validate(jwt)).isEqualTo(OAuth2TokenValidatorResult.success());\n\t}\n\n\t@Test\n\tpublic void validateWhenClaimFailsTheTestThenReturnsFailure() {\n\t\tJwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, \"http://abc\").build();\n\t\tCollection<OAuth2Error> details = this.validator.validate(jwt).getErrors();\n\t\tassertThat(this.validator.validate(jwt).getErrors()).isNotEmpty();\n\t\tassertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));\n\t}\n\n\t@Test\n\tpublic void validateWhenClaimIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new JwtClaimValidator<>(null, test));\n\t}\n\n\t@Test\n\tpublic void validateWhenTestIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new JwtClaimValidator<>(JwtClaimNames.ISS, null));\n\t}\n\n\t@Test\n\tpublic void validateWhenJwtIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.validator.validate(null));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimsSetTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link JwtClaimsSet}.\n *\n * @author Joe Grandja\n */\npublic class JwtClaimsSetTests {\n\n\t@Test\n\tpublic void buildWhenClaimsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> JwtClaimsSet.builder().build())\n\t\t\t.withMessage(\"claims cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void buildWhenAllClaimsProvidedThenAllClaimsAreSet() {\n\t\tJwtClaimsSet expectedJwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\t// @formatter:off\n\t\tJwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder()\n\t\t\t\t.issuer(expectedJwtClaimsSet.getIssuer().toExternalForm())\n\t\t\t\t.subject(expectedJwtClaimsSet.getSubject())\n\t\t\t\t.audience(expectedJwtClaimsSet.getAudience())\n\t\t\t\t.issuedAt(expectedJwtClaimsSet.getIssuedAt())\n\t\t\t\t.notBefore(expectedJwtClaimsSet.getNotBefore())\n\t\t\t\t.expiresAt(expectedJwtClaimsSet.getExpiresAt())\n\t\t\t\t.id(expectedJwtClaimsSet.getId())\n\t\t\t\t.claims((claims) -> claims.put(\"custom-claim-name\", \"custom-claim-value\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(jwtClaimsSet.getIssuer()).isEqualTo(expectedJwtClaimsSet.getIssuer());\n\t\tassertThat(jwtClaimsSet.getSubject()).isEqualTo(expectedJwtClaimsSet.getSubject());\n\t\tassertThat(jwtClaimsSet.getAudience()).isEqualTo(expectedJwtClaimsSet.getAudience());\n\t\tassertThat(jwtClaimsSet.getIssuedAt()).isEqualTo(expectedJwtClaimsSet.getIssuedAt());\n\t\tassertThat(jwtClaimsSet.getNotBefore()).isEqualTo(expectedJwtClaimsSet.getNotBefore());\n\t\tassertThat(jwtClaimsSet.getExpiresAt()).isEqualTo(expectedJwtClaimsSet.getExpiresAt());\n\t\tassertThat(jwtClaimsSet.getId()).isEqualTo(expectedJwtClaimsSet.getId());\n\t\tassertThat(jwtClaimsSet.<String>getClaim(\"custom-claim-name\")).isEqualTo(\"custom-claim-value\");\n\t\tassertThat(jwtClaimsSet.getClaims()).isEqualTo(expectedJwtClaimsSet.getClaims());\n\t}\n\n\t@Test\n\tpublic void fromWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> JwtClaimsSet.from(null))\n\t\t\t.withMessage(\"claims cannot be null\");\n\t}\n\n\t@Test\n\tpublic void fromWhenClaimsProvidedThenCopied() {\n\t\tJwtClaimsSet expectedJwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\t\tJwtClaimsSet jwtClaimsSet = JwtClaimsSet.from(expectedJwtClaimsSet).build();\n\t\tassertThat(jwtClaimsSet.getClaims()).isEqualTo(expectedJwtClaimsSet.getClaims());\n\t}\n\n\t@Test\n\tpublic void claimWhenNameNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> JwtClaimsSet.builder().claim(null, \"value\"))\n\t\t\t.withMessage(\"name cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void claimWhenValueNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> JwtClaimsSet.builder().claim(\"name\", null))\n\t\t\t.withMessage(\"value cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport com.nimbusds.jose.Algorithm;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.jwk.Curve;\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jose.util.Base64URL;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.web.util.UriComponents;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.isNull;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\n\npublic class JwtDecoderProviderConfigurationUtilsTests {\n\n\t@Test\n\tpublic void getSignatureAlgorithmsWhenJwkSetSpecifiesAlgorithmThenUses() throws Exception {\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\tRSAKey key = new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyUse(KeyUse.SIGNATURE)\n\t\t\t.algorithm(JWSAlgorithm.RS384)\n\t\t\t.build();\n\t\tgiven(jwkSource.get(any(JWKSelector.class), isNull())).willReturn(Collections.singletonList(key));\n\t\tSet<SignatureAlgorithm> algorithms = JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource);\n\t\tassertThat(algorithms).containsOnly(SignatureAlgorithm.RS384);\n\t}\n\n\t@Test\n\tpublic void getSignatureAlgorithmsWhenJwkSetIsEmptyThenIllegalArgumentException() throws Exception {\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\tgiven(jwkSource.get(any(JWKSelector.class), isNull())).willReturn(Collections.emptyList());\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource));\n\t}\n\n\t@Test\n\tpublic void getSignatureAlgorithmsWhenJwkSetSpecifiesFamilyThenUses() throws Exception {\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\t// Test parameters are from Anders Rundgren, public only\n\t\tECKey ecKey = new ECKey.Builder(Curve.P_256, new Base64URL(\"3l2Da_flYc-AuUTm2QzxgyvJxYM_2TeB9DMlwz7j1PE\"),\n\t\t\t\tnew Base64URL(\"-kjT7Wrfhwsi9SG6H4UXiyUiVE9GHCLauslksZ3-_t0\"))\n\t\t\t.keyUse(KeyUse.SIGNATURE)\n\t\t\t.build();\n\t\tRSAKey rsaKey = new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyUse(KeyUse.ENCRYPTION).build();\n\t\tgiven(jwkSource.get(any(JWKSelector.class), isNull())).willReturn(Arrays.asList(ecKey, rsaKey));\n\t\tSet<SignatureAlgorithm> algorithms = JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource);\n\t\tassertThat(algorithms).contains(SignatureAlgorithm.ES256, SignatureAlgorithm.ES384, SignatureAlgorithm.ES512);\n\t}\n\n\t// gh-9651\n\t@Test\n\tpublic void getSignatureAlgorithmsWhenAlgorithmThenParses() throws Exception {\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\tRSAKey key = new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyUse(KeyUse.SIGNATURE)\n\t\t\t.algorithm(new Algorithm(JwsAlgorithms.RS256))\n\t\t\t.build();\n\t\tgiven(jwkSource.get(any(JWKSelector.class), isNull())).willReturn(Collections.singletonList(key));\n\t\tSet<SignatureAlgorithm> algorithms = JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource);\n\t\tassertThat(algorithms).containsOnly(SignatureAlgorithm.RS256);\n\t}\n\n\t// gh-15852\n\t@Test\n\tpublic void oidcWhenHostContainsUnderscoreThenRetains() {\n\t\tUriComponents oidc = JwtDecoderProviderConfigurationUtils.oidc(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oidc.getHost()).isEqualTo(\"elated_sutherland\");\n\t\tUriComponents oauth = JwtDecoderProviderConfigurationUtils.oauth(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oauth.getHost()).isEqualTo(\"elated_sutherland\");\n\t\tUriComponents oidcRfc8414 = JwtDecoderProviderConfigurationUtils\n\t\t\t.oidcRfc8414(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oidcRfc8414.getHost()).isEqualTo(\"elated_sutherland\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecodersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport okhttp3.HttpUrl;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * Tests for {@link JwtDecoders}\n *\n * @author Josh Cummings\n * @author Rafiullah Hamedy\n */\npublic class JwtDecodersTests {\n\n\t/**\n\t * Contains those parameters required to construct a JwtDecoder as well as any\n\t * required parameters\n\t */\n\t// @formatter:off\n\tprivate static final String DEFAULT_RESPONSE_TEMPLATE = \"{\\n\"\n\t\t\t+ \"    \\\"authorization_endpoint\\\": \\\"https://example.com/o/oauth2/v2/auth\\\", \\n\"\n\t\t\t+ \"    \\\"id_token_signing_alg_values_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"RS256\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"issuer\\\": \\\"%s\\\", \\n\"\n\t\t\t+ \"    \\\"jwks_uri\\\": \\\"%s/.well-known/jwks.json\\\", \\n\"\n\t\t\t+ \"    \\\"response_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"code\\\", \\n\"\n\t\t\t+ \"        \\\"token\\\", \\n\"\n\t\t\t+ \"        \\\"id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token\\\", \\n\"\n\t\t\t+ \"        \\\"code id_token\\\", \\n\"\n\t\t\t+ \"        \\\"token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"none\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"subject_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"public\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"token_endpoint\\\": \\\"https://example.com/oauth2/v4/token\\\"\\n\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate static final String JWK_SET = \"{\\\"keys\\\":[{\\\"kty\\\":\\\"RSA\\\",\\\"e\\\":\\\"AQAB\\\",\\\"use\\\":\\\"sig\\\",\\\"kid\\\":\\\"one\\\",\\\"n\\\":\\\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\\\"}]}\";\n\n\tprivate static final String ISSUER_MISMATCH = \"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvd3Jvbmdpc3N1ZXIiLCJleHAiOjQ2ODcyNTYwNDl9.Ax8LMI6rhB9Pv_CE3kFi1JPuLj9gZycifWrLeDpkObWEEVAsIls9zAhNFyJlG-Oo7up6_mDhZgeRfyKnpSF5GhKJtXJDCzwg0ZDVUE6rS0QadSxsMMGbl7c4y0lG_7TfLX2iWeNJukJj_oSW9KzW4FsBp1BoocWjrreesqQU3fZHbikH-c_Fs2TsAIpHnxflyEzfOFWpJ8D4DtzHXqfvieMwpy42xsPZK3LR84zlasf0Ne1tC_hLHvyHRdAXwn0CMoKxc7-8j0r9Mq8kAzUsPn9If7bMLqGkxUcTPdk5x7opAUajDZx95SXHLmtztNtBa2S6EfPJXuPKG6tM5Wq5Ug\";\n\n\tprivate static final String OIDC_METADATA_PATH = \"/.well-known/openid-configuration\";\n\n\tprivate static final String OAUTH_METADATA_PATH = \"/.well-known/oauth-authorization-server\";\n\n\tprivate MockWebServer server;\n\n\tprivate String issuer;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.issuer = createIssuerFromServer() + \"path\";\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void issuerWhenResponseIsTypicalThenReturnedDecoderValidatesIssuer() {\n\t\tprepareConfigurationResponse();\n\t\tJwtDecoder decoder = JwtDecoders.fromOidcIssuerLocation(this.issuer);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(ISSUER_MISMATCH))\n\t\t\t\t.withMessageContaining(\"The iss claim is not valid\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsTypicalThenReturnedDecoderValidatesIssuer() {\n\t\tprepareConfigurationResponseOidc();\n\t\tJwtDecoder decoder = JwtDecoders.fromIssuerLocation(this.issuer);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(ISSUER_MISMATCH))\n\t\t\t\t.withMessageContaining(\"The iss claim is not valid\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsTypicalThenReturnedDecoderValidatesIssuer() {\n\t\tprepareConfigurationResponseOAuth2();\n\t\tJwtDecoder decoder = JwtDecoders.fromIssuerLocation(this.issuer);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(ISSUER_MISMATCH))\n\t\t\t\t.withMessageContaining(\"The iss claim is not valid\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenContainsTrailingSlashThenSuccess() {\n\t\tthis.issuer += \"/\";\n\t\tprepareConfigurationResponse();\n\t\tJwtDecoder jwtDecoder = JwtDecoders.fromOidcIssuerLocation(this.issuer);\n\t\tassertThat(jwtDecoder).isNotNull();\n\t\tassertThat(this.issuer).endsWith(\"/\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackContainsTrailingSlashThenSuccess() {\n\t\tthis.issuer += \"/\";\n\t\tprepareConfigurationResponseOidc();\n\t\tJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(this.issuer);\n\t\tassertThat(jwtDecoder).isNotNull();\n\t\tassertThat(this.issuer).endsWith(\"/\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ContainsTrailingSlashThenSuccess() {\n\t\tthis.issuer += \"/\";\n\t\tprepareConfigurationResponseOAuth2();\n\t\tJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(this.issuer);\n\t\tassertThat(jwtDecoder).isNotNull();\n\t\tassertThat(this.issuer).endsWith(\"/\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenResponseIsNonCompliantThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponse(\"{ \\\"missing_required_keys\\\" : \\\"and_values\\\" }\");\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t.isThrownBy(() -> JwtDecoders.fromOidcIssuerLocation(this.issuer));\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsNonCompliantThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOidc(\"{ \\\"missing_required_keys\\\" : \\\"and_values\\\" }\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsNonCompliantThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOAuth2(\"{ \\\"missing_required_keys\\\" : \\\"and_values\\\" }\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {\n\t\tprepareConfigurationResponse(this.buildResponseWithMissingJwksUri());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromOidcIssuerLocation(this.issuer))\n\t\t\t\t.withMessage(\"The public JWK set URI must not be null\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {\n\t\tprepareConfigurationResponseOidc(this.buildResponseWithMissingJwksUri());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(this.issuer))\n\t\t\t\t.isInstanceOf(IllegalArgumentException.class)\n\t\t\t\t.withMessage(\"The public JWK set URI must not be null\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {\n\t\tprepareConfigurationResponseOAuth2(this.buildResponseWithMissingJwksUri());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(this.issuer))\n\t\t\t\t.withMessage(\"The public JWK set URI must not be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenResponseIsMalformedThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponse(\"malformed\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromOidcIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsMalformedThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOidc(\"malformed\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsMalformedThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOAuth2(\"malformed\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenRespondingIssuerMismatchesRequestedIssuerThenThrowsIllegalStateException() {\n\t\tprepareConfigurationResponse(String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer + \"/wrong\", this.issuer));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromOidcIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackRespondingIssuerMismatchesRequestedIssuerThenThrowsIllegalStateException() {\n\t\tprepareConfigurationResponseOidc(String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer + \"/wrong\", this.issuer));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2RespondingIssuerMismatchesRequestedIssuerThenThrowsIllegalStateException() {\n\t\tprepareConfigurationResponseOAuth2(\n\t\t\t\tString.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer + \"/wrong\", this.issuer));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenRequestedIssuerIsUnresponsiveThenThrowsIllegalArgumentException() throws Exception {\n\t\tthis.server.shutdown();\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromOidcIssuerLocation(\"https://issuer\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackRequestedIssuerIsUnresponsiveThenThrowsIllegalArgumentException()\n\t\t\tthrows Exception {\n\t\tthis.server.shutdown();\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> JwtDecoders.fromIssuerLocation(\"https://issuer\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate void prepareConfigurationResponse() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponse(body);\n\t}\n\n\tprivate void prepareConfigurationResponse(String body) {\n\t\tthis.server.enqueue(response(body));\n\t\tthis.server.enqueue(response(JWK_SET));\n\t}\n\n\tprivate void prepareConfigurationResponseOidc() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponseOidc(body);\n\t}\n\n\tprivate void prepareConfigurationResponseOidc(String body) {\n\t\tMap<String, MockResponse> responses = new HashMap<>();\n\t\tresponses.put(oidc(), response(body));\n\t\tresponses.put(jwks(), response(JWK_SET));\n\t\tprepareConfigurationResponses(responses);\n\t}\n\n\tprivate void prepareConfigurationResponseOAuth2() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponseOAuth2(body);\n\t}\n\n\tprivate void prepareConfigurationResponseOAuth2(String body) {\n\t\tMap<String, MockResponse> responses = new HashMap<>();\n\t\tresponses.put(oauth(), response(body));\n\t\tresponses.put(jwks(), response(JWK_SET));\n\t\tprepareConfigurationResponses(responses);\n\t}\n\n\tprivate void prepareConfigurationResponses(Map<String, MockResponse> responses) {\n\t\tDispatcher dispatcher = new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\t// @formatter:off\n\t\t\t\treturn Optional.of(request)\n\t\t\t\t\t.map(RecordedRequest::getRequestUrl)\n\t\t\t\t\t.map(HttpUrl::toString)\n\t\t\t\t\t.map(responses::get)\n\t\t\t\t\t.orElse(new MockResponse().setResponseCode(404));\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t};\n\t\tthis.server.setDispatcher(dispatcher);\n\t}\n\n\tprivate String createIssuerFromServer() {\n\t\treturn this.server.url(\"\").toString();\n\t}\n\n\tprivate String oidc() {\n\t\tURI uri = URI.create(this.issuer);\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUri(uri)\n\t\t\t\t.replacePath(uri.getPath() + OIDC_METADATA_PATH)\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate String oauth() {\n\t\tURI uri = URI.create(this.issuer);\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUri(uri)\n\t\t\t\t.replacePath(OAUTH_METADATA_PATH + uri.getPath())\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate String jwks() {\n\t\treturn this.issuer + \"/.well-known/jwks.json\";\n\t}\n\n\tprivate MockResponse response(String body) {\n\t\t// @formatter:off\n\t\treturn new MockResponse()\n\t\t\t\t.setBody(body)\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\t// @formatter:on\n\t}\n\n\tpublic String buildResponseWithMissingJwksUri() {\n\t\tJsonMapper mapper = new JsonMapper();\n\t\tMap<String, Object> response = mapper.readValue(DEFAULT_RESPONSE_TEMPLATE,\n\t\t\t\tnew TypeReference<Map<String, Object>>() {\n\t\t\t\t});\n\t\tresponse.remove(\"jwks_uri\");\n\t\treturn mapper.writeValueAsString(response);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtIssuerValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Josh Cummings\n * @since 5.1\n */\npublic class JwtIssuerValidatorTests {\n\n\tprivate static final String ISSUER = \"https://issuer\";\n\n\tprivate final JwtIssuerValidator validator = new JwtIssuerValidator(ISSUER);\n\n\t@Test\n\tpublic void validateWhenIssuerMatchesThenReturnsSuccess() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"iss\", ISSUER).build();\n\t\t// @formatter:off\n\t\tassertThat(this.validator.validate(jwt))\n\t\t\t\t.isEqualTo(OAuth2TokenValidatorResult.success());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenIssuerUrlMatchesThenReturnsSuccess() throws MalformedURLException {\n\t\tJwt jwt = TestJwts.jwt().claim(\"iss\", new URL(ISSUER)).build();\n\n\t\tassertThat(this.validator.validate(jwt)).isEqualTo(OAuth2TokenValidatorResult.success());\n\t}\n\n\t@Test\n\tpublic void validateWhenIssuerMismatchesThenReturnsError() {\n\t\tJwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, \"https://other\").build();\n\t\tOAuth2TokenValidatorResult result = this.validator.validate(jwt);\n\t\tassertThat(result.getErrors()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void validateWhenIssuerUrlMismatchesThenReturnsError() throws MalformedURLException {\n\t\tJwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, new URL(\"https://other\")).build();\n\n\t\tOAuth2TokenValidatorResult result = this.validator.validate(jwt);\n\n\t\tassertThat(result.getErrors()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void validateWhenJwtHasNoIssuerThenReturnsError() {\n\t\tJwt jwt = TestJwts.jwt().claim(JwtClaimNames.AUD, \"https://aud\").build();\n\t\tOAuth2TokenValidatorResult result = this.validator.validate(jwt);\n\t\tassertThat(result.getErrors()).isNotEmpty();\n\t}\n\n\t// gh-6073\n\t@Test\n\tpublic void validateWhenIssuerMatchesAndIsNotAUriThenReturnsSuccess() {\n\t\tJwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, \"issuer\").build();\n\t\tJwtIssuerValidator validator = new JwtIssuerValidator(\"issuer\");\n\t\t// @formatter:off\n\t\tassertThat(validator.validate(jwt))\n\t\t\t\t.isEqualTo(OAuth2TokenValidatorResult.success());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void validateWhenJwtIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.validator.validate(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullIssuerIsGivenThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new JwtIssuerValidator(null));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link Jwt}.\n *\n * @author Joe Grandja\n */\npublic class JwtTests {\n\n\tprivate static final String ISS_CLAIM = \"iss\";\n\n\tprivate static final String SUB_CLAIM = \"sub\";\n\n\tprivate static final String AUD_CLAIM = \"aud\";\n\n\tprivate static final String EXP_CLAIM = \"exp\";\n\n\tprivate static final String NBF_CLAIM = \"nbf\";\n\n\tprivate static final String IAT_CLAIM = \"iat\";\n\n\tprivate static final String JTI_CLAIM = \"jti\";\n\n\tprivate static final String ISS_VALUE = \"https://provider.com\";\n\n\tprivate static final String SUB_VALUE = \"subject1\";\n\n\tprivate static final List<String> AUD_VALUE = Arrays.asList(\"aud1\", \"aud2\");\n\n\tprivate static final long EXP_VALUE = Instant.now().plusSeconds(60).toEpochMilli();\n\n\tprivate static final long NBF_VALUE = Instant.now().plusSeconds(5).toEpochMilli();\n\n\tprivate static final long IAT_VALUE = Instant.now().toEpochMilli();\n\n\tprivate static final String JTI_VALUE = \"jwt-id-1\";\n\n\tprivate static final Map<String, Object> HEADERS;\n\n\tprivate static final Map<String, Object> CLAIMS;\n\n\tprivate static final String JWT_TOKEN_VALUE = \"jwt-token-value\";\n\tstatic {\n\t\tHEADERS = new HashMap<>();\n\t\tHEADERS.put(\"alg\", JwsAlgorithms.RS256);\n\t\tCLAIMS = new HashMap<>();\n\t\tCLAIMS.put(ISS_CLAIM, ISS_VALUE);\n\t\tCLAIMS.put(SUB_CLAIM, SUB_VALUE);\n\t\tCLAIMS.put(AUD_CLAIM, AUD_VALUE);\n\t\tCLAIMS.put(EXP_CLAIM, EXP_VALUE);\n\t\tCLAIMS.put(NBF_CLAIM, NBF_VALUE);\n\t\tCLAIMS.put(IAT_CLAIM, IAT_VALUE);\n\t\tCLAIMS.put(JTI_CLAIM, JTI_VALUE);\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenValueIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new Jwt(null, Instant.ofEpochMilli(IAT_VALUE), Instant.ofEpochMilli(EXP_VALUE), HEADERS, CLAIMS));\n\t}\n\n\t@Test\n\tpublic void constructorWhenHeadersIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Jwt(JWT_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE),\n\t\t\t\tInstant.ofEpochMilli(EXP_VALUE), Collections.emptyMap(), CLAIMS));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClaimsIsEmptyThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Jwt(JWT_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE),\n\t\t\t\tInstant.ofEpochMilli(EXP_VALUE), HEADERS, Collections.emptyMap()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenParametersProvidedAndValidThenCreated() {\n\t\tJwt jwt = new Jwt(JWT_TOKEN_VALUE, Instant.ofEpochMilli(IAT_VALUE), Instant.ofEpochMilli(EXP_VALUE), HEADERS,\n\t\t\t\tCLAIMS);\n\t\tassertThat(jwt.getTokenValue()).isEqualTo(JWT_TOKEN_VALUE);\n\t\tassertThat(jwt.getHeaders()).isEqualTo(HEADERS);\n\t\tassertThat(jwt.getClaims()).isEqualTo(CLAIMS);\n\t\tassertThat(jwt.getIssuer().toString()).isEqualTo(ISS_VALUE);\n\t\tassertThat(jwt.getSubject()).isEqualTo(SUB_VALUE);\n\t\tassertThat(jwt.getAudience()).isEqualTo(AUD_VALUE);\n\t\tassertThat(jwt.getExpiresAt().toEpochMilli()).isEqualTo(EXP_VALUE);\n\t\tassertThat(jwt.getNotBefore().getEpochSecond()).isEqualTo(NBF_VALUE);\n\t\tassertThat(jwt.getIssuedAt().toEpochMilli()).isEqualTo(IAT_VALUE);\n\t\tassertThat(jwt.getId()).isEqualTo(JTI_VALUE);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtTimestampValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests verifying {@link JwtTimestampValidator}\n *\n * @author Josh Cummings\n */\npublic class JwtTimestampValidatorTests {\n\n\tprivate static final Clock MOCK_NOW = Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault());\n\n\tprivate static final String MOCK_TOKEN_VALUE = \"token\";\n\n\tprivate static final Instant MOCK_ISSUED_AT = Instant.MIN;\n\n\tprivate static final Map<String, Object> MOCK_HEADER = Collections.singletonMap(\"alg\", JwsAlgorithms.RS256);\n\n\tprivate static final Map<String, Object> MOCK_CLAIM_SET = Collections.singletonMap(\"some\", \"claim\");\n\n\t@Test\n\tpublic void validateWhenJwtIsExpiredThenErrorMessageIndicatesExpirationTime() {\n\t\tInstant oneHourAgo = Instant.now().minusSeconds(3600);\n\t\tJwt jwt = TestJwts.jwt().expiresAt(oneHourAgo).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tCollection<OAuth2Error> details = jwtValidator.validate(jwt).getErrors();\n\t\t// @formatter:off\n\t\tCollection<String> messages = details.stream()\n\t\t\t\t.map(OAuth2Error::getDescription)\n\t\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t\tassertThat(messages).contains(\"Jwt expired at \" + oneHourAgo);\n\t\tassertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));\n\t}\n\n\t@Test\n\tpublic void validateWhenJwtIsTooEarlyThenErrorMessageIndicatesNotBeforeTime() {\n\t\tInstant oneHourFromNow = Instant.now().plusSeconds(3600);\n\t\tJwt jwt = TestJwts.jwt().notBefore(oneHourFromNow).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tCollection<OAuth2Error> details = jwtValidator.validate(jwt).getErrors();\n\t\t// @formatter:off\n\t\tCollection<String> messages = details.stream()\n\t\t\t\t.map(OAuth2Error::getDescription)\n\t\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t\tassertThat(messages).contains(\"Jwt used before \" + oneHourFromNow);\n\t\tassertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));\n\t}\n\n\t@Test\n\tpublic void validateWhenConfiguredWithClockSkewThenValidatesUsingThatSkew() {\n\t\tDuration oneDayOff = Duration.ofDays(1);\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator(oneDayOff);\n\t\tInstant now = Instant.now();\n\t\tInstant almostOneDayAgo = now.minus(oneDayOff).plusSeconds(10);\n\t\tInstant almostOneDayFromNow = now.plus(oneDayOff).minusSeconds(10);\n\t\tInstant justOverOneDayAgo = now.minus(oneDayOff).minusSeconds(10);\n\t\tInstant justOverOneDayFromNow = now.plus(oneDayOff).plusSeconds(10);\n\t\tJwt jwt = TestJwts.jwt().expiresAt(almostOneDayAgo).notBefore(almostOneDayFromNow).build();\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t\tjwt = TestJwts.jwt().expiresAt(justOverOneDayAgo).build();\n\t\tOAuth2TokenValidatorResult result = jwtValidator.validate(jwt);\n\t\t// @formatter:off\n\t\tCollection<String> messages = result.getErrors()\n\t\t\t\t.stream()\n\t\t\t\t.map(OAuth2Error::getDescription)\n\t\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(messages).contains(\"Jwt expired at \" + justOverOneDayAgo);\n\t\tjwt = TestJwts.jwt().notBefore(justOverOneDayFromNow).build();\n\t\tresult = jwtValidator.validate(jwt);\n\t\t// @formatter:off\n\t\tmessages = result.getErrors()\n\t\t\t\t.stream()\n\t\t\t\t.map(OAuth2Error::getDescription)\n\t\t\t\t.collect(Collectors.toList());\n\t\t// @formatter:on\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\tassertThat(messages).contains(\"Jwt used before \" + justOverOneDayFromNow);\n\t}\n\n\t@Test\n\tpublic void validateWhenConfiguredWithFixedClockThenValidatesUsingFixedTime() {\n\t\tJwt jwt = TestJwts.jwt().expiresAt(Instant.now(MOCK_NOW)).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator(Duration.ofNanos(0));\n\t\tjwtValidator.setClock(MOCK_NOW);\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t\tjwt = TestJwts.jwt().notBefore(Instant.now(MOCK_NOW)).build();\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void validateWhenNeitherExpiryNorNotBeforeIsSpecifiedThenReturnsSuccessfulResult() {\n\t\tJwt jwt = TestJwts.jwt().claims((c) -> {\n\t\t\tc.remove(JwtClaimNames.EXP);\n\t\t\tc.remove(JwtClaimNames.NBF);\n\t\t}).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void validateWhenExpiryIsSpecifiedThenReturnsSuccessfulResult() {\n\t\tJwt jwt = TestJwts.jwt().claims((c) -> c.remove(JwtClaimNames.EXP)).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void validateWhenNotBeforeIsSpecifiedThenReturnsSuccessfulResult() {\n\t\tJwt jwt = TestJwts.jwt().claims((c) -> c.remove(JwtClaimNames.EXP)).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void validateWhenNotBeforeIsValidAndExpiryIsNotSpecifiedThenReturnsSuccessfulResult() {\n\t\tJwt jwt = TestJwts.jwt().claims((c) -> c.remove(JwtClaimNames.EXP)).notBefore(Instant.MIN).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void validateWhenNotAllowEmptyExpiryClaimAndNotBeforeIsValidAndExpiryIsNotSpecifiedThenReturnsSuccessfulResult() {\n\t\tJwt jwt = TestJwts.jwt().claims((c) -> c.remove(JwtClaimNames.EXP)).notBefore(Instant.MIN).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tjwtValidator.setAllowEmptyExpiryClaim(false);\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isTrue();\n\t}\n\n\t@Test\n\tpublic void validateWhenNotAllowEmptyNotBeforeClaimAndNotBeforeIsNotSpecifiedThenReturnsSuccessfulResult() {\n\t\tJwt jwt = TestJwts.jwt().claims((c) -> c.remove(JwtClaimNames.NBF)).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tjwtValidator.setAllowEmptyNotBeforeClaim(false);\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isTrue();\n\t}\n\n\t@Test\n\tpublic void validateWhenExpiryIsValidAndNotBeforeIsNotSpecifiedThenReturnsSuccessfulResult() {\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void validateWhenBothExpiryAndNotBeforeAreValidThenReturnsSuccessfulResult() {\n\t\tJwt jwt = TestJwts.jwt().expiresAt(Instant.now(MOCK_NOW)).notBefore(Instant.now(MOCK_NOW)).build();\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator(Duration.ofNanos(0));\n\t\tjwtValidator.setClock(MOCK_NOW);\n\t\tassertThat(jwtValidator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void setClockWhenInvokedWithNullThenThrowsIllegalArgumentException() {\n\t\tJwtTimestampValidator jwtValidator = new JwtTimestampValidator();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> jwtValidator.setClock(null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenInvokedWithNullDurationThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new JwtTimestampValidator(null));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtTypeValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass JwtTypeValidatorTests {\n\n\t@Test\n\tvoid constructorWhenJwtThenRequiresJwtOrEmpty() {\n\t\tJwt.Builder jwt = TestJwts.jwt();\n\t\tJwtTypeValidator validator = JwtTypeValidator.jwt();\n\t\tassertThat(validator.validate(jwt.build()).hasErrors()).isFalse();\n\t\tjwt.header(JoseHeaderNames.TYP, \"JWT\");\n\t\tassertThat(validator.validate(jwt.build()).hasErrors()).isFalse();\n\t\tjwt.header(JoseHeaderNames.TYP, \"at+jwt\");\n\t\tassertThat(validator.validate(jwt.build()).hasErrors()).isTrue();\n\t}\n\n\t@Test\n\tvoid constructorWhenCustomThenEnforces() {\n\t\tJwt.Builder jwt = TestJwts.jwt();\n\t\tJwtTypeValidator validator = new JwtTypeValidator(\"JOSE\");\n\t\tassertThat(validator.validate(jwt.build()).hasErrors()).isTrue();\n\t\tjwt.header(JoseHeaderNames.TYP, \"JWT\");\n\t\tassertThat(validator.validate(jwt.build()).hasErrors()).isTrue();\n\t\tjwt.header(JoseHeaderNames.TYP, \"JOSE\");\n\t\tassertThat(validator.validate(jwt.build()).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tvoid validateWhenTypHeaderHasDifferentCaseThenSuccess() {\n\t\tJwt.Builder jwt = TestJwts.jwt();\n\t\tJwtTypeValidator validator = new JwtTypeValidator(\"at+jwt\");\n\t\tjwt.header(JoseHeaderNames.TYP, \"AT+JWT\");\n\t\tassertThat(validator.validate(jwt.build()).hasErrors()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtValidatorsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatException;\n\n/**\n * Tests for {@link JwtValidators}.\n *\n * @author Max Batischev\n * @author Giacomo Baso\n */\npublic class JwtValidatorsTests {\n\n\tprivate static final String ISS_CLAIM = \"iss\";\n\n\t@Test\n\tpublic void createWhenJwtIssuerValidatorIsPresentThenCreateDefaultValidatorWithJwtIssuerValidator() {\n\t\tOAuth2TokenValidator<Jwt> validator = JwtValidators\n\t\t\t.createDefaultWithValidators(new JwtIssuerValidator(ISS_CLAIM));\n\n\t\tassertThat(containsByType(validator, JwtIssuerValidator.class)).isTrue();\n\t\tassertThat(containsByType(validator, JwtTimestampValidator.class)).isTrue();\n\t\tassertThat(containsByType(validator, X509CertificateThumbprintValidator.class)).isTrue();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void createWhenJwtTimestampValidatorIsPresentThenCreateDefaultValidatorWithOnlyOneJwtTimestampValidator() {\n\t\tOAuth2TokenValidator<Jwt> validator = JwtValidators.createDefaultWithValidators(new JwtTimestampValidator());\n\n\t\tDelegatingOAuth2TokenValidator<Jwt> delegatingOAuth2TokenValidator = (DelegatingOAuth2TokenValidator<Jwt>) validator;\n\t\tCollection<OAuth2TokenValidator<Jwt>> tokenValidators = (Collection<OAuth2TokenValidator<Jwt>>) ReflectionTestUtils\n\t\t\t.getField(delegatingOAuth2TokenValidator, \"tokenValidators\");\n\n\t\tassertThat(containsByType(validator, JwtTimestampValidator.class)).isTrue();\n\t\tassertThat(containsByType(validator, X509CertificateThumbprintValidator.class)).isTrue();\n\t\tassertThat(containsByType(validator, JwtTypeValidator.class)).isTrue();\n\t\tassertThat(Objects.requireNonNull(tokenValidators).size()).isEqualTo(3);\n\t}\n\n\t@Test\n\tpublic void createWhenEmptyValidatorsThenThrowsException() {\n\t\tassertThatException().isThrownBy(() -> JwtValidators.createDefaultWithValidators(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void createAtJwtWhenIssuerClientIdAudienceThenBuilds() {\n\t\tJwt.Builder builder = TestJwts.jwt();\n\t\tOAuth2TokenValidator<Jwt> validator = JwtValidators.createAtJwtValidator()\n\t\t\t.audience(\"audience\")\n\t\t\t.clientId(\"clientId\")\n\t\t\t.issuer(\"issuer\")\n\t\t\t.build();\n\n\t\tOAuth2TokenValidatorResult result = validator.validate(builder.build());\n\t\tassertThat(result.getErrors().toString()).contains(\"at+jwt\")\n\t\t\t.contains(\"aud\")\n\t\t\t.contains(\"client_id\")\n\t\t\t.contains(\"iss\");\n\n\t\tresult = validator.validate(builder.header(JoseHeaderNames.TYP, \"JWT\").build());\n\t\tassertThat(result.getErrors().toString()).contains(\"at+jwt\");\n\n\t\tresult = validator.validate(builder.header(JoseHeaderNames.TYP, \"at+jwt\").build());\n\t\tassertThat(result.getErrors().toString()).doesNotContain(\"at+jwt\");\n\n\t\tresult = validator.validate(builder.header(JoseHeaderNames.TYP, \"application/at+jwt\").build());\n\t\tassertThat(result.getErrors().toString()).doesNotContain(\"at+jwt\");\n\n\t\tresult = validator.validate(builder.audience(List.of(\"audience\")).build());\n\t\tassertThat(result.getErrors().toString()).doesNotContain(\"aud\");\n\n\t\tresult = validator.validate(builder.claim(\"client_id\", \"clientId\").build());\n\t\tassertThat(result.getErrors().toString()).doesNotContain(\"client_id\");\n\n\t\tresult = validator.validate(builder.issuer(\"issuer\").build());\n\t\tassertThat(result.getErrors().toString()).doesNotContain(\"iss\");\n\t}\n\n\t@Test\n\tvoid createAtJwtWhenClientIdIsNotPresentThenRequireClientIdWithAnyValue() {\n\t\tJwt.Builder builder = TestJwts.jwt();\n\t\tOAuth2TokenValidator<Jwt> validator = JwtValidators.createAtJwtValidator()\n\t\t\t.audience(\"audience\")\n\t\t\t.issuer(\"issuer\")\n\t\t\t.build();\n\n\t\tOAuth2TokenValidatorResult result = validator.validate(builder.build());\n\t\tassertThat(result.getErrors().toString()).contains(\"at+jwt\")\n\t\t\t.contains(\"aud\")\n\t\t\t.contains(\"client_id\")\n\t\t\t.contains(\"iss\");\n\n\t\tresult = validator.validate(builder.claim(\"client_id\", \"clientId\").build());\n\t\tassertThat(result.getErrors().toString()).doesNotContain(\"client_id\");\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate boolean containsByType(OAuth2TokenValidator<Jwt> validator, Class<? extends OAuth2TokenValidator<?>> type) {\n\t\tDelegatingOAuth2TokenValidator<Jwt> delegatingOAuth2TokenValidator = (DelegatingOAuth2TokenValidator<Jwt>) validator;\n\t\tCollection<OAuth2TokenValidator<Jwt>> tokenValidators = (Collection<OAuth2TokenValidator<Jwt>>) ReflectionTestUtils\n\t\t\t.getField(delegatingOAuth2TokenValidator, \"tokenValidators\");\n\n\t\tOAuth2TokenValidator<?> tokenValidator = CollectionUtils\n\t\t\t.findValueOfType(Objects.requireNonNull(tokenValidators), type);\n\t\treturn tokenValidator != null;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URI;\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link MappedJwtClaimSetConverter}\n *\n * @author Josh Cummings\n */\npublic class MappedJwtClaimSetConverterTests {\n\n\t@Test\n\tpublic void convertWhenUsingCustomExpiresAtConverterThenIssuedAtConverterStillConsultsIt() {\n\t\tInstant at = Instant.ofEpochMilli(1000000000000L);\n\t\tConverter<Object, Instant> expiresAtConverter = mock(Converter.class);\n\t\tgiven(expiresAtConverter.convert(any())).willReturn(at);\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter\n\t\t\t.withDefaults(Collections.singletonMap(JwtClaimNames.EXP, expiresAtConverter));\n\t\tMap<String, Object> source = new HashMap<>();\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target).containsEntry(JwtClaimNames.IAT, Instant.ofEpochMilli(at.toEpochMilli()).minusSeconds(1));\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingDefaultsThenBasesIssuedAtOffOfExpiration() {\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\t\tMap<String, Object> source = Collections.singletonMap(JwtClaimNames.EXP, 1000000000L);\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target).containsEntry(JwtClaimNames.EXP, Instant.ofEpochSecond(1000000000L));\n\t\tassertThat(target).containsEntry(JwtClaimNames.IAT, Instant.ofEpochSecond(1000000000L).minusSeconds(1));\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingDefaultsThenCoercesAudienceAccordingToJwtSpec() {\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\t\tMap<String, Object> source = Collections.singletonMap(JwtClaimNames.AUD, \"audience\");\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target.get(JwtClaimNames.AUD)).isInstanceOf(Collection.class);\n\t\tassertThat(target).containsEntry(JwtClaimNames.AUD, Arrays.asList(\"audience\"));\n\t\tsource = Collections.singletonMap(JwtClaimNames.AUD, Arrays.asList(\"one\", \"two\"));\n\t\ttarget = converter.convert(source);\n\t\tassertThat(target.get(JwtClaimNames.AUD)).isInstanceOf(Collection.class);\n\t\tassertThat(target).containsEntry(JwtClaimNames.AUD, Arrays.asList(\"one\", \"two\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingDefaultsThenCoercesAllAttributesInJwtSpec() {\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\t\tMap<String, Object> source = new HashMap<>();\n\t\tsource.put(JwtClaimNames.JTI, 1);\n\t\tsource.put(JwtClaimNames.AUD, \"audience\");\n\t\tsource.put(JwtClaimNames.EXP, 2000000000L);\n\t\tsource.put(JwtClaimNames.IAT, new Date(1000000000000L));\n\t\tsource.put(JwtClaimNames.ISS, \"https://any.url\");\n\t\tsource.put(JwtClaimNames.NBF, 1000000000);\n\t\tsource.put(JwtClaimNames.SUB, 1234);\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target).containsEntry(JwtClaimNames.JTI, \"1\");\n\t\tassertThat(target).containsEntry(JwtClaimNames.AUD, Arrays.asList(\"audience\"));\n\t\tassertThat(target).containsEntry(JwtClaimNames.EXP, Instant.ofEpochSecond(2000000000L));\n\t\tassertThat(target).containsEntry(JwtClaimNames.IAT, Instant.ofEpochSecond(1000000000L));\n\t\tassertThat(target).containsEntry(JwtClaimNames.ISS, \"https://any.url\");\n\t\tassertThat(target).containsEntry(JwtClaimNames.NBF, Instant.ofEpochSecond(1000000000L));\n\t\tassertThat(target).containsEntry(JwtClaimNames.SUB, \"1234\");\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingCustomConverterThenAllOtherDefaultsAreStillUsed() {\n\t\tConverter<Object, String> claimConverter = mock(Converter.class);\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter\n\t\t\t.withDefaults(Collections.singletonMap(JwtClaimNames.SUB, claimConverter));\n\t\tgiven(claimConverter.convert(any(Object.class))).willReturn(\"1234\");\n\t\tMap<String, Object> source = new HashMap<>();\n\t\tsource.put(JwtClaimNames.JTI, 1);\n\t\tsource.put(JwtClaimNames.AUD, \"audience\");\n\t\tsource.put(JwtClaimNames.EXP, Instant.ofEpochSecond(2000000000L));\n\t\tsource.put(JwtClaimNames.IAT, new Date(1000000000000L));\n\t\tsource.put(JwtClaimNames.ISS, URI.create(\"https://any.url\"));\n\t\tsource.put(JwtClaimNames.NBF, \"1000000000\");\n\t\tsource.put(JwtClaimNames.SUB, 2345);\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target).containsEntry(JwtClaimNames.JTI, \"1\");\n\t\tassertThat(target).containsEntry(JwtClaimNames.AUD, Arrays.asList(\"audience\"));\n\t\tassertThat(target).containsEntry(JwtClaimNames.EXP, Instant.ofEpochSecond(2000000000L));\n\t\tassertThat(target).containsEntry(JwtClaimNames.IAT, Instant.ofEpochSecond(1000000000L));\n\t\tassertThat(target).containsEntry(JwtClaimNames.ISS, \"https://any.url\");\n\t\tassertThat(target).containsEntry(JwtClaimNames.NBF, Instant.ofEpochSecond(1000000000L));\n\t\tassertThat(target).containsEntry(JwtClaimNames.SUB, \"1234\");\n\t}\n\n\t// gh-10135\n\t@Test\n\tpublic void convertWhenConverterReturnsNullThenClaimIsRemoved() {\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter\n\t\t\t.withDefaults(Collections.singletonMap(JwtClaimNames.NBF, (nbfClaimValue) -> null));\n\t\tMap<String, Object> source = Collections.singletonMap(JwtClaimNames.NBF, Instant.now());\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target).doesNotContainKey(JwtClaimNames.NBF);\n\t}\n\n\t@Test\n\tpublic void convertWhenClaimValueIsNullThenClaimIsRemoved() {\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\t\tMap<String, Object> source = Collections.singletonMap(JwtClaimNames.ISS, null);\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target).doesNotContainKey(JwtClaimNames.ISS);\n\t}\n\n\t@Test\n\tpublic void convertWhenConverterReturnsValueWhenEntryIsMissingThenEntryIsAdded() {\n\t\tConverter<Object, String> claimConverter = mock(Converter.class);\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter\n\t\t\t.withDefaults(Collections.singletonMap(\"custom-claim\", claimConverter));\n\t\tgiven(claimConverter.convert(any())).willReturn(\"custom-value\");\n\t\tMap<String, Object> source = new HashMap<>();\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target).containsEntry(\"custom-claim\", \"custom-value\");\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingConstructorThenOnlyConvertersInThatMapAreUsedForConversion() {\n\t\tConverter<Object, String> claimConverter = mock(Converter.class);\n\t\tMappedJwtClaimSetConverter converter = new MappedJwtClaimSetConverter(\n\t\t\t\tCollections.singletonMap(JwtClaimNames.SUB, claimConverter));\n\t\tgiven(claimConverter.convert(any(Object.class))).willReturn(\"1234\");\n\t\tMap<String, Object> source = new HashMap<>();\n\t\tsource.put(JwtClaimNames.JTI, new Object());\n\t\tsource.put(JwtClaimNames.AUD, new Object());\n\t\tsource.put(JwtClaimNames.EXP, Instant.ofEpochSecond(1L));\n\t\tsource.put(JwtClaimNames.IAT, Instant.ofEpochSecond(1L));\n\t\tsource.put(JwtClaimNames.ISS, new Object());\n\t\tsource.put(JwtClaimNames.NBF, new Object());\n\t\tsource.put(JwtClaimNames.SUB, new Object());\n\t\tMap<String, Object> target = converter.convert(source);\n\t\tassertThat(target).containsEntry(JwtClaimNames.JTI, source.get(JwtClaimNames.JTI));\n\t\tassertThat(target).containsEntry(JwtClaimNames.AUD, source.get(JwtClaimNames.AUD));\n\t\tassertThat(target).containsEntry(JwtClaimNames.EXP, source.get(JwtClaimNames.EXP));\n\t\tassertThat(target).containsEntry(JwtClaimNames.IAT, source.get(JwtClaimNames.IAT));\n\t\tassertThat(target).containsEntry(JwtClaimNames.ISS, source.get(JwtClaimNames.ISS));\n\t\tassertThat(target).containsEntry(JwtClaimNames.NBF, source.get(JwtClaimNames.NBF));\n\t\tassertThat(target).containsEntry(JwtClaimNames.SUB, \"1234\");\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingDefaultsThenFailedConversionThrowsIllegalStateException() {\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\t\tMap<String, Object> badIssuer = Collections.singletonMap(JwtClaimNames.ISS, \"https://badly formed iss\");\n\t\tassertThatIllegalStateException().isThrownBy(() -> converter.convert(badIssuer));\n\t\tMap<String, Object> badIssuedAt = Collections.singletonMap(JwtClaimNames.IAT, \"badly-formed-iat\");\n\t\tassertThatIllegalStateException().isThrownBy(() -> converter.convert(badIssuedAt));\n\t\tMap<String, Object> badExpiresAt = Collections.singletonMap(JwtClaimNames.EXP, \"badly-formed-exp\");\n\t\tassertThatIllegalStateException().isThrownBy(() -> converter.convert(badExpiresAt));\n\t\tMap<String, Object> badNotBefore = Collections.singletonMap(JwtClaimNames.NBF, \"badly-formed-nbf\");\n\t\tassertThatIllegalStateException().isThrownBy(() -> converter.convert(badNotBefore));\n\t}\n\n\t// gh-6073\n\t@Test\n\tpublic void convertWhenIssuerIsNotAUriThenConvertsToString() {\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\t\tMap<String, Object> nonUriIssuer = Collections.singletonMap(JwtClaimNames.ISS, \"issuer\");\n\t\tMap<String, Object> target = converter.convert(nonUriIssuer);\n\t\tassertThat(target).containsEntry(JwtClaimNames.ISS, \"issuer\");\n\t}\n\n\t// gh-6073\n\t@Test\n\tpublic void convertWhenIssuerIsOfTypeURLThenConvertsToString() throws Exception {\n\t\tMappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());\n\t\tMap<String, Object> issuer = Collections.singletonMap(JwtClaimNames.ISS, new URL(\"https://issuer\"));\n\t\tMap<String, Object> target = converter.convert(issuer);\n\t\tassertThat(target).containsEntry(JwtClaimNames.ISS, \"https://issuer\");\n\t}\n\n\t@Test\n\tpublic void constructWhenAnyParameterIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MappedJwtClaimSetConverter(null));\n\t}\n\n\t@Test\n\tpublic void withDefaultsWhenAnyParameterIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> MappedJwtClaimSetConverter.withDefaults(null));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJweEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport com.nimbusds.jose.EncryptionMethod;\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.JOSEObjectType;\nimport com.nimbusds.jose.JWEAlgorithm;\nimport com.nimbusds.jose.JWEHeader;\nimport com.nimbusds.jose.JWEObject;\nimport com.nimbusds.jose.Payload;\nimport com.nimbusds.jose.crypto.RSAEncrypter;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.KeyType;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jose.util.Base64;\nimport com.nimbusds.jose.util.Base64URL;\nimport com.nimbusds.jwt.JWTClaimsSet;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.jose.JwaAlgorithm;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for proofing out future support of JWE.\n *\n * @author Joe Grandja\n */\npublic class NimbusJweEncoderTests {\n\n\t// @formatter:off\n\tprivate static final JweHeader DEFAULT_JWE_HEADER =\n\t\t\tJweHeader.with(JweAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM.getName()).build();\n\t// @formatter:on\n\n\tprivate List<JWK> jwkList;\n\n\tprivate JWKSource<SecurityContext> jwkSource;\n\n\tprivate NimbusJweEncoder jweEncoder;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.jwkList = new ArrayList<>();\n\t\tthis.jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(new JWKSet(this.jwkList));\n\t\tthis.jweEncoder = new NimbusJweEncoder(this.jwkSource);\n\t}\n\n\t@Test\n\tpublic void encodeWhenJwtClaimsSetThenEncodes() {\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tthis.jwkList.add(rsaJwk);\n\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\t// @formatter:off\n\t\t// **********************\n\t\t// Assume future API:\n\t\t// \t\tJwtEncoderParameters.with(JweHeader jweHeader, JwtClaimsSet claims)\n\t\t// **********************\n\t\t// @formatter:on\n\t\tJwt encodedJwe = this.jweEncoder.encode(JwtEncoderParameters.from(jwtClaimsSet));\n\n\t\tassertThat(encodedJwe.getHeaders()).containsEntry(JoseHeaderNames.ALG, DEFAULT_JWE_HEADER.getAlgorithm());\n\t\tassertThat(encodedJwe.getHeaders()).containsEntry(\"enc\", DEFAULT_JWE_HEADER.<String>getHeader(\"enc\"));\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.JKU)).isNull();\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.JWK)).isNull();\n\t\tassertThat(encodedJwe.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk.getKeyID());\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.X5U)).isNull();\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.X5C)).isNull();\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.X5T)).isNull();\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.X5T_S256)).isNull();\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.TYP)).isNull();\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.CTY)).isNull();\n\t\tassertThat(encodedJwe.getHeaders().get(JoseHeaderNames.CRIT)).isNull();\n\n\t\tassertThat(encodedJwe.getIssuer()).isEqualTo(jwtClaimsSet.getIssuer());\n\t\tassertThat(encodedJwe.getSubject()).isEqualTo(jwtClaimsSet.getSubject());\n\t\tassertThat(encodedJwe.getAudience()).isEqualTo(jwtClaimsSet.getAudience());\n\t\tassertThat(encodedJwe.getExpiresAt()).isEqualTo(jwtClaimsSet.getExpiresAt());\n\t\tassertThat(encodedJwe.getNotBefore()).isEqualTo(jwtClaimsSet.getNotBefore());\n\t\tassertThat(encodedJwe.getIssuedAt()).isEqualTo(jwtClaimsSet.getIssuedAt());\n\t\tassertThat(encodedJwe.getId()).isEqualTo(jwtClaimsSet.getId());\n\t\tassertThat(encodedJwe.<String>getClaim(\"custom-claim-name\")).isEqualTo(\"custom-claim-value\");\n\n\t\tassertThat(encodedJwe.getTokenValue()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void encodeWhenNestedJwsThenEncodes() {\n\t\t// See Nimbus example -> Nested signed and encrypted JWT\n\t\t// https://connect2id.com/products/nimbus-jose-jwt/examples/signed-and-encrypted-jwt\n\n\t\tRSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;\n\t\tthis.jwkList.add(rsaJwk);\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\t// @formatter:off\n\t\t// **********************\n\t\t// Assume future API:\n\t\t// \t\tJwtEncoderParameters.with(JwsHeader jwsHeader, JweHeader jweHeader, JwtClaimsSet claims)\n\t\t// **********************\n\t\t// @formatter:on\n\t\tJwt encodedJweNestedJws = this.jweEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tassertThat(encodedJweNestedJws.getHeaders()).containsEntry(JoseHeaderNames.ALG,\n\t\t\t\tDEFAULT_JWE_HEADER.getAlgorithm());\n\t\tassertThat(encodedJweNestedJws.getHeaders()).containsEntry(\"enc\", DEFAULT_JWE_HEADER.<String>getHeader(\"enc\"));\n\t\tassertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.JKU)).isNull();\n\t\tassertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.JWK)).isNull();\n\t\tassertThat(encodedJweNestedJws.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk.getKeyID());\n\t\tassertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.X5U)).isNull();\n\t\tassertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.X5C)).isNull();\n\t\tassertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.X5T)).isNull();\n\t\tassertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.X5T_S256)).isNull();\n\t\tassertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.TYP)).isNull();\n\t\tassertThat(encodedJweNestedJws.getHeaders()).containsEntry(JoseHeaderNames.CTY, \"JWT\");\n\t\tassertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.CRIT)).isNull();\n\n\t\tassertThat(encodedJweNestedJws.getIssuer()).isEqualTo(jwtClaimsSet.getIssuer());\n\t\tassertThat(encodedJweNestedJws.getSubject()).isEqualTo(jwtClaimsSet.getSubject());\n\t\tassertThat(encodedJweNestedJws.getAudience()).isEqualTo(jwtClaimsSet.getAudience());\n\t\tassertThat(encodedJweNestedJws.getExpiresAt()).isEqualTo(jwtClaimsSet.getExpiresAt());\n\t\tassertThat(encodedJweNestedJws.getNotBefore()).isEqualTo(jwtClaimsSet.getNotBefore());\n\t\tassertThat(encodedJweNestedJws.getIssuedAt()).isEqualTo(jwtClaimsSet.getIssuedAt());\n\t\tassertThat(encodedJweNestedJws.getId()).isEqualTo(jwtClaimsSet.getId());\n\t\tassertThat(encodedJweNestedJws.<String>getClaim(\"custom-claim-name\")).isEqualTo(\"custom-claim-value\");\n\n\t\tassertThat(encodedJweNestedJws.getTokenValue()).isNotNull();\n\t}\n\n\tenum JweAlgorithm implements JwaAlgorithm {\n\n\t\tRSA_OAEP_256(\"RSA-OAEP-256\");\n\n\t\tprivate final String name;\n\n\t\tJweAlgorithm(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t}\n\n\tprivate static final class JweHeader extends JoseHeader {\n\n\t\tprivate JweHeader(Map<String, Object> headers) {\n\t\t\tsuper(headers);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\t@Override\n\t\tpublic JweAlgorithm getAlgorithm() {\n\t\t\treturn super.getAlgorithm();\n\t\t}\n\n\t\tprivate static Builder with(JweAlgorithm jweAlgorithm, String enc) {\n\t\t\treturn new Builder(jweAlgorithm, enc);\n\t\t}\n\n\t\tprivate static Builder from(JweHeader headers) {\n\t\t\treturn new Builder(headers);\n\t\t}\n\n\t\tprivate static final class Builder extends AbstractBuilder<JweHeader, Builder> {\n\n\t\t\tprivate Builder(JweAlgorithm jweAlgorithm, String enc) {\n\t\t\t\tAssert.notNull(jweAlgorithm, \"jweAlgorithm cannot be null\");\n\t\t\t\tAssert.hasText(enc, \"enc cannot be empty\");\n\t\t\t\talgorithm(jweAlgorithm);\n\t\t\t\theader(\"enc\", enc);\n\t\t\t}\n\n\t\t\tprivate Builder(JweHeader headers) {\n\t\t\t\tAssert.notNull(headers, \"headers cannot be null\");\n\t\t\t\tConsumer<Map<String, Object>> headersConsumer = (h) -> h.putAll(headers.getHeaders());\n\t\t\t\theaders(headersConsumer);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic JweHeader build() {\n\t\t\t\treturn new JweHeader(getHeaders());\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tprivate static final class NimbusJweEncoder implements JwtEncoder {\n\n\t\tprivate static final String ENCODING_ERROR_MESSAGE_TEMPLATE = \"An error occurred while attempting to encode the Jwt: %s\";\n\n\t\tprivate static final Converter<JweHeader, JWEHeader> JWE_HEADER_CONVERTER = new JweHeaderConverter();\n\n\t\tprivate static final Converter<JwtClaimsSet, JWTClaimsSet> JWT_CLAIMS_SET_CONVERTER = new JwtClaimsSetConverter();\n\n\t\tprivate final JWKSource<SecurityContext> jwkSource;\n\n\t\tprivate final JwtEncoder jwsEncoder;\n\n\t\tprivate NimbusJweEncoder(JWKSource<SecurityContext> jwkSource) {\n\t\t\tAssert.notNull(jwkSource, \"jwkSource cannot be null\");\n\t\t\tthis.jwkSource = jwkSource;\n\t\t\tthis.jwsEncoder = new NimbusJwtEncoder(jwkSource);\n\t\t}\n\n\t\t@Override\n\t\tpublic Jwt encode(JwtEncoderParameters parameters) throws JwtEncodingException {\n\t\t\tAssert.notNull(parameters, \"parameters cannot be null\");\n\n\t\t\t// @formatter:off\n\t\t\t// **********************\n\t\t\t// Assume future API:\n\t\t\t// \t\tJwtEncoderParameters.getJweHeader()\n\t\t\t// **********************\n\t\t\t// @formatter:on\n\t\t\tJweHeader jweHeader = DEFAULT_JWE_HEADER; // Assume this is accessed via\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// JwtEncoderParameters.getJweHeader()\n\n\t\t\tJwsHeader jwsHeader = parameters.getJwsHeader();\n\t\t\tJwtClaimsSet claims = parameters.getClaims();\n\n\t\t\tJWK jwk = selectJwk(jweHeader);\n\t\t\tjweHeader = addKeyIdentifierHeadersIfNecessary(jweHeader, jwk);\n\n\t\t\tJWEHeader jweHeader2 = JWE_HEADER_CONVERTER.convert(jweHeader);\n\t\t\tJWTClaimsSet jwtClaimsSet = JWT_CLAIMS_SET_CONVERTER.convert(claims);\n\n\t\t\tString payload;\n\t\t\tif (jwsHeader != null) {\n\t\t\t\t// Sign then encrypt\n\t\t\t\tJwt jws = this.jwsEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\t\t\t\tpayload = jws.getTokenValue();\n\n\t\t\t\t// @formatter:off\n\t\t\t\tjweHeader = JweHeader.from(jweHeader)\n\t\t\t\t\t\t.contentType(\"JWT\")\t\t// Indicates Nested JWT (REQUIRED)\n\t\t\t\t\t\t.build();\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Encrypt only\n\t\t\t\tpayload = jwtClaimsSet.toString();\n\t\t\t}\n\n\t\t\tJWEObject jweObject = new JWEObject(jweHeader2, new Payload(payload));\n\t\t\ttry {\n\t\t\t\t// FIXME\n\t\t\t\t// Resolve type of JWEEncrypter using the JWK key type\n\t\t\t\t// For now, assuming RSA key type\n\t\t\t\tjweObject.encrypt(new RSAEncrypter(jwk.toRSAKey()));\n\t\t\t}\n\t\t\tcatch (JOSEException ex) {\n\t\t\t\tthrow new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,\n\t\t\t\t\t\t\"Failed to encrypt the JWT -> \" + ex.getMessage()), ex);\n\t\t\t}\n\t\t\tString jwe = jweObject.serialize();\n\n\t\t\t// NOTE:\n\t\t\t// For the Nested JWS use case, we lose access to the JWS Header in the\n\t\t\t// returned JWT.\n\t\t\t// If this is needed, we can simply add the new method Jwt.getNestedHeaders().\n\t\t\treturn new Jwt(jwe, claims.getIssuedAt(), claims.getExpiresAt(), jweHeader.getHeaders(),\n\t\t\t\t\tclaims.getClaims());\n\t\t}\n\n\t\tprivate JWK selectJwk(JweHeader headers) {\n\t\t\tList<JWK> jwks;\n\t\t\ttry {\n\t\t\t\tJWKSelector jwkSelector = new JWKSelector(createJwkMatcher(headers));\n\t\t\t\tjwks = this.jwkSource.get(jwkSelector, null);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,\n\t\t\t\t\t\t\"Failed to select a JWK encryption key -> \" + ex.getMessage()), ex);\n\t\t\t}\n\n\t\t\tif (jwks.size() > 1) {\n\t\t\t\tthrow new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,\n\t\t\t\t\t\t\"Found multiple JWK encryption keys for algorithm '\" + headers.getAlgorithm().getName() + \"'\"));\n\t\t\t}\n\n\t\t\tif (jwks.isEmpty()) {\n\t\t\t\tthrow new JwtEncodingException(\n\t\t\t\t\t\tString.format(ENCODING_ERROR_MESSAGE_TEMPLATE, \"Failed to select a JWK encryption key\"));\n\t\t\t}\n\n\t\t\treturn jwks.get(0);\n\t\t}\n\n\t\tprivate static JWKMatcher createJwkMatcher(JweHeader headers) {\n\t\t\tJWEAlgorithm jweAlgorithm = JWEAlgorithm.parse(headers.getAlgorithm().getName());\n\n\t\t\t// @formatter:off\n\t\t\treturn new JWKMatcher.Builder()\n\t\t\t\t\t.keyType(KeyType.forAlgorithm(jweAlgorithm))\n\t\t\t\t\t.keyID(headers.getKeyId())\n\t\t\t\t\t.keyUses(KeyUse.ENCRYPTION, null)\n\t\t\t\t\t.algorithms(jweAlgorithm, null)\n\t\t\t\t\t.x509CertSHA256Thumbprint(Base64URL.from(headers.getX509SHA256Thumbprint()))\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate static JweHeader addKeyIdentifierHeadersIfNecessary(JweHeader headers, JWK jwk) {\n\t\t\t// Check if headers have already been added\n\t\t\tif (StringUtils.hasText(headers.getKeyId()) && StringUtils.hasText(headers.getX509SHA256Thumbprint())) {\n\t\t\t\treturn headers;\n\t\t\t}\n\t\t\t// Check if headers can be added from JWK\n\t\t\tif (!StringUtils.hasText(jwk.getKeyID()) && jwk.getX509CertSHA256Thumbprint() == null) {\n\t\t\t\treturn headers;\n\t\t\t}\n\n\t\t\tJweHeader.Builder headersBuilder = JweHeader.from(headers);\n\t\t\tif (!StringUtils.hasText(headers.getKeyId()) && StringUtils.hasText(jwk.getKeyID())) {\n\t\t\t\theadersBuilder.keyId(jwk.getKeyID());\n\t\t\t}\n\t\t\tif (!StringUtils.hasText(headers.getX509SHA256Thumbprint()) && jwk.getX509CertSHA256Thumbprint() != null) {\n\t\t\t\theadersBuilder.x509SHA256Thumbprint(jwk.getX509CertSHA256Thumbprint().toString());\n\t\t\t}\n\n\t\t\treturn headersBuilder.build();\n\t\t}\n\n\t}\n\n\tprivate static class JweHeaderConverter implements Converter<JweHeader, JWEHeader> {\n\n\t\t@Override\n\t\tpublic JWEHeader convert(JweHeader headers) {\n\t\t\tJWEAlgorithm jweAlgorithm = JWEAlgorithm.parse(headers.getAlgorithm().getName());\n\t\t\tEncryptionMethod encryptionMethod = EncryptionMethod.parse(headers.getHeader(\"enc\"));\n\t\t\tJWEHeader.Builder builder = new JWEHeader.Builder(jweAlgorithm, encryptionMethod);\n\n\t\t\tURL jwkSetUri = headers.getJwkSetUrl();\n\t\t\tif (jwkSetUri != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbuilder.jwkURL(jwkSetUri.toURI());\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Unable to convert '\" + JoseHeaderNames.JKU + \"' JOSE header to a URI\", ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tMap<String, Object> jwk = headers.getJwk();\n\t\t\tif (!CollectionUtils.isEmpty(jwk)) {\n\t\t\t\ttry {\n\t\t\t\t\tbuilder.jwk(JWK.parse(jwk));\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Unable to convert '\" + JoseHeaderNames.JWK + \"' JOSE header\",\n\t\t\t\t\t\t\tex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString keyId = headers.getKeyId();\n\t\t\tif (StringUtils.hasText(keyId)) {\n\t\t\t\tbuilder.keyID(keyId);\n\t\t\t}\n\n\t\t\tURL x509Uri = headers.getX509Url();\n\t\t\tif (x509Uri != null) {\n\t\t\t\ttry {\n\t\t\t\t\tbuilder.x509CertURL(x509Uri.toURI());\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Unable to convert '\" + JoseHeaderNames.X5U + \"' JOSE header to a URI\", ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tList<String> x509CertificateChain = headers.getX509CertificateChain();\n\t\t\tif (!CollectionUtils.isEmpty(x509CertificateChain)) {\n\t\t\t\tbuilder.x509CertChain(x509CertificateChain.stream().map(Base64::new).collect(Collectors.toList()));\n\t\t\t}\n\n\t\t\tString x509SHA1Thumbprint = headers.getX509SHA1Thumbprint();\n\t\t\tif (StringUtils.hasText(x509SHA1Thumbprint)) {\n\t\t\t\tbuilder.x509CertThumbprint(new Base64URL(x509SHA1Thumbprint));\n\t\t\t}\n\n\t\t\tString x509SHA256Thumbprint = headers.getX509SHA256Thumbprint();\n\t\t\tif (StringUtils.hasText(x509SHA256Thumbprint)) {\n\t\t\t\tbuilder.x509CertSHA256Thumbprint(new Base64URL(x509SHA256Thumbprint));\n\t\t\t}\n\n\t\t\tString type = headers.getType();\n\t\t\tif (StringUtils.hasText(type)) {\n\t\t\t\tbuilder.type(new JOSEObjectType(type));\n\t\t\t}\n\n\t\t\tString contentType = headers.getContentType();\n\t\t\tif (StringUtils.hasText(contentType)) {\n\t\t\t\tbuilder.contentType(contentType);\n\t\t\t}\n\n\t\t\tSet<String> critical = headers.getCritical();\n\t\t\tif (!CollectionUtils.isEmpty(critical)) {\n\t\t\t\tbuilder.criticalParams(critical);\n\t\t\t}\n\n\t\t\tMap<String, Object> customHeaders = headers.getHeaders()\n\t\t\t\t.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.filter((header) -> !JWEHeader.getRegisteredParameterNames().contains(header.getKey()))\n\t\t\t\t.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n\t\t\tif (!CollectionUtils.isEmpty(customHeaders)) {\n\t\t\t\tbuilder.customParams(customHeaders);\n\t\t\t}\n\n\t\t\treturn builder.build();\n\t\t}\n\n\t}\n\n\tprivate static class JwtClaimsSetConverter implements Converter<JwtClaimsSet, JWTClaimsSet> {\n\n\t\t@Override\n\t\tpublic JWTClaimsSet convert(JwtClaimsSet claims) {\n\t\t\tJWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();\n\n\t\t\t// NOTE: The value of the 'iss' claim is a String or URL (StringOrURI).\n\t\t\tObject issuer = claims.getClaim(JwtClaimNames.ISS);\n\t\t\tif (issuer != null) {\n\t\t\t\tbuilder.issuer(issuer.toString());\n\t\t\t}\n\n\t\t\tString subject = claims.getSubject();\n\t\t\tif (StringUtils.hasText(subject)) {\n\t\t\t\tbuilder.subject(subject);\n\t\t\t}\n\n\t\t\tList<String> audience = claims.getAudience();\n\t\t\tif (!CollectionUtils.isEmpty(audience)) {\n\t\t\t\tbuilder.audience(audience);\n\t\t\t}\n\n\t\t\tInstant expiresAt = claims.getExpiresAt();\n\t\t\tif (expiresAt != null) {\n\t\t\t\tbuilder.expirationTime(Date.from(expiresAt));\n\t\t\t}\n\n\t\t\tInstant notBefore = claims.getNotBefore();\n\t\t\tif (notBefore != null) {\n\t\t\t\tbuilder.notBeforeTime(Date.from(notBefore));\n\t\t\t}\n\n\t\t\tInstant issuedAt = claims.getIssuedAt();\n\t\t\tif (issuedAt != null) {\n\t\t\t\tbuilder.issueTime(Date.from(issuedAt));\n\t\t\t}\n\n\t\t\tString jwtId = claims.getId();\n\t\t\tif (StringUtils.hasText(jwtId)) {\n\t\t\t\tbuilder.jwtID(jwtId);\n\t\t\t}\n\n\t\t\tMap<String, Object> customClaims = new HashMap<>();\n\t\t\tclaims.getClaims().forEach((name, value) -> {\n\t\t\t\tif (!JWTClaimsSet.getRegisteredNames().contains(name)) {\n\t\t\t\t\tcustomClaims.put(name, value);\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (!customClaims.isEmpty()) {\n\t\t\t\tcustomClaims.forEach(builder::claim);\n\t\t\t}\n\n\t\t\treturn builder.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.security.KeyFactory;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.EncodedKeySpec;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.text.ParseException;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.crypto.SecretKey;\n\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.JOSEObjectType;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.JWSHeader;\nimport com.nimbusds.jose.JWSSigner;\nimport com.nimbusds.jose.crypto.MACSigner;\nimport com.nimbusds.jose.crypto.RSASSASigner;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.BadJOSEException;\nimport com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;\nimport com.nimbusds.jose.proc.JWSKeySelector;\nimport com.nimbusds.jose.proc.JWSVerificationKeySelector;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jwt.JWTClaimsSet;\nimport com.nimbusds.jwt.SignedJWT;\nimport com.nimbusds.jwt.proc.BadJWTException;\nimport com.nimbusds.jwt.proc.DefaultJWTProcessor;\nimport com.nimbusds.jwt.proc.JWTProcessor;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.concurrent.ConcurrentMapCache;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.web.client.RestClientException;\nimport org.springframework.web.client.RestOperations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link NimbusJwtDecoder}\n *\n * @author Josh Cummings\n * @author Joe Grandja\n * @author Mykyta Bezverkhyi\n * @author Andrey Litvitski\n */\npublic class NimbusJwtDecoderTests {\n\n\tprivate static final String JWK_SET = \"{\\\"keys\\\":[{\\\"kty\\\":\\\"RSA\\\",\\\"e\\\":\\\"AQAB\\\",\\\"use\\\":\\\"sig\\\",\\\"kid\\\":\\\"one\\\",\\\"n\\\":\\\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\\\"},{\\\"kty\\\":\\\"EC\\\",\\\"key_ops\\\":[\\\"sign\\\",\\\"verify\\\"],\\\"alg\\\":\\\"ES256\\\",\\\"kid\\\":\\\"330e6e41-2c66-47c0-8a82-02a1aa5576ec\\\",\\\"crv\\\":\\\"P-256\\\",\\\"x\\\":\\\"pHzT2rtgIGKaQVd69a8H2D--YOkH4ook0v-mUpOjVX4\\\",\\\"y\\\":\\\"3Fn4_BnsUJU9qCa7pvZyt8hedAOqkWAf5KqW9DR1ZZk\\\"}]}\";\n\n\tprivate static final String MALFORMED_TOKEN = \"eyJhbGciOiJSUzI1NiJ9.eyJuYmYiOnt9LCJleHAiOjQ2ODQyMjUwODd9\";\n\n\tprivate static final String NEW_KID_JWK_SET = \"{\\\"keys\\\":[{\\\"kty\\\":\\\"RSA\\\",\\\"e\\\":\\\"AQAB\\\",\\\"kid\\\":\\\"two\\\",\\\"n\\\":\\\"ra9UJw4I0fCHuOqr1xWJsh-qcVeZWtKEU3uoqq1sAg5fG67dujNCm_Q16yuO0ZdDiU0vlJkbc_MXFAvm4ZxdJ_qR7PAneV-BOGNtLpSaiPclscCy3m7zjRWkaqwt9ZZEsdK5UqXyPlBpcYhNKsmnQGjnX4sYb7d8b2jSCM_qto48-6451rbyEhXXywtFy_JqtTpbsw_IIdQHMr1O-MdSjsQxX9kkvZwPU8LsC-CcqlcsZ7mnpOhmIXaf4tbRwAaluXwYft0yykFsp8e5C4t9mMs9Vu8AB5gT8o-D_ovXd2qh4k3ejzVpYLtzD4nbfvPJA_TXmjhn-9GOPAqkzfON2Q\\\"}]}\";\n\n\tprivate static final String MALFORMED_JWK_SET = \"malformed\";\n\n\tprivate static final String SIGNED_JWT = \"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJzY3AiOlsibWVzc2FnZTpyZWFkIl0sImV4cCI6NDY4Mzg5Nzc3Nn0.LtMVtIiRIwSyc3aX35Zl0JVwLTcQZAB3dyBOMHNaHCKUljwMrf20a_gT79LfhjDzE_fUVUmFiAO32W1vFnYpZSVaMDUgeIOIOpxfoe9shj_uYenAwIS-_UxqGVIJiJoXNZh_MK80ShNpvsQwamxWEEOAMBtpWNiVYNDMdfgho9n3o5_Z7Gjy8RLBo1tbDREbO9kTFwGIxm_EYpezmRCRq4w1DdS6UDW321hkwMxPnCMSWOvp-hRpmgY2yjzLgPJ6Aucmg9TJ8jloAP1DjJoF1gRR7NTAk8LOGkSjTzVYDYMbCF51YdpojhItSk80YzXiEsv1mTz4oMM49jXBmfXFMA\";\n\n\tprivate static final String NEW_KID_SIGNED_JWT = \"eyJraWQiOiJ0d28iLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjIxMzMyNzg4MjV9.DQJn_qg0HfZ_sjlx9MJkdCjkp9t-0zOj3FzVp_UPzx6RCcBb8Jk373dNgcyfOP5CS29wv5gKX6geWEDj5cgqcJdTS53zqOaLETdNnKACd056SkPqgTLJv12gdJx7tr5WbBqRB9Y0ce96vbH6wwQGfqU_1Lz1RhZ7ZZuvIuWLp75ujld7dOshScg728Z9BQsiFOH_yFp09XraO15spwTXp9RO5TJRUSLih-5V3sdxHa5rPTm6by7me8I_l4iMJN81Z95_O7sbLeYH-4zZ-3T49uPyAC5suEOd-P5aFP89zPKh9Y3Uviu2OyvpUuXmpUjTtdAKf3p96dOEeLJvT3hkSg\";\n\n\tprivate static final String MALFORMED_JWT = \"eyJhbGciOiJSUzI1NiJ9.eyJuYmYiOnt9LCJleHAiOjQ2ODQyMjUwODd9.guoQvujdWvd3xw7FYQEn4D6-gzM_WqFvXdmvAUNSLbxG7fv2_LLCNujPdrBHJoYPbOwS1BGNxIKQWS1tylvqzmr1RohQ-RZ2iAM1HYQzboUlkoMkcd8ENM__ELqho8aNYBfqwkNdUOyBFoy7Syu_w2SoJADw2RTjnesKO6CVVa05bW118pDS4xWxqC4s7fnBjmZoTn4uQ-Kt9YSQZQk8YQxkJSiyanozzgyfgXULA6mPu1pTNU3FVFaK1i1av_xtH_zAPgb647ZeaNe4nahgqC5h8nhOlm8W2dndXbwAt29nd2ZWBsru_QwZz83XSKLhTPFz-mPBByZZDsyBbIHf9A\";\n\n\tprivate static final String UNSIGNED_JWT = \"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOi0yMDMzMjI0OTcsImp0aSI6IjEyMyIsInR5cCI6IkpXVCJ9.\";\n\n\tprivate static final String EMPTY_EXP_CLAIM_JWT = \"eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJhdWRpZW5jZSJ9.D1eT0jpBEpuh74p-YT-uF81Z7rkVqIpUtJ5hWWFiVShZ9s8NIntK4Q1GlvlziiySSaVYaXtpTmDB3c8r-Z5Mj4ibihiueCSq7jaPD3sA8IMQKL-L6Uol8MSD_lSFE2n3fVBTxFeaejBKfZsDxnhzgpy8g7PncR47w8NHs-7tKO4qw7G_SV3hkNpDNoqZTfMImxyWEebgKM2pJAhN4das2CO1KAjYMfEByLcgYncE8fzdYPJhMFo2XRRSQABoeUBuKSAwIntBaOGvcb-qII_Hefc5U0cmpNItG75F2XfX803plKI4FFpAxJsbPKWSQmhs6bZOrhx0x74pY5LS3ghmJw\";\n\n\tprivate static final String JWK_SET_URI = \"https://issuer/.well-known/jwks.json\";\n\n\tprivate static final String RS512_SIGNED_JWT = \"eyJhbGciOiJSUzUxMiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjE5NzQzMjYxMTl9.LKAx-60EBfD7jC1jb1eKcjO4uLvf3ssISV-8tN-qp7gAjSvKvj4YA9-V2mIb6jcS1X_xGmNy6EIimZXpWaBR3nJmeu-jpe85u4WaW2Ztr8ecAi-dTO7ZozwdtljKuBKKvj4u1nF70zyCNl15AozSG0W1ASrjUuWrJtfyDG6WoZ8VfNMuhtU-xUYUFvscmeZKUYQcJ1KS-oV5tHeF8aNiwQoiPC_9KXCOZtNEJFdq6-uzFdHxvOP2yex5Gbmg5hXonauIFXG2ZPPGdXzm-5xkhBpgM8U7A_6wb3So8wBvLYYm2245QUump63AJRAy8tQpwt4n9MvQxQgS3z9R-NK92A\";\n\n\tprivate static final String RS256_SIGNED_JWT = \"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjE5NzQzMjYzMzl9.CT-H2OWEqmSs1NWmnta5ealLFvM8OlbQTjGhfRcKLNxrTrzsOkqBJl-AN3k16BQU7mS32o744TiiZ29NcDlxPsr1MqTlN86-dobPiuNIDLp3A1bOVdXMcVFuMYkrNv0yW0tGS9OjEqsCCuZDkZ1by6AhsHLbGwRY-6AQdcRouZygGpOQu1hNun5j8q5DpSTY4AXKARIFlF-O3OpVbPJ0ebr3Ki-i3U9p_55H0e4-wx2bqcApWlqgofl1I8NKWacbhZgn81iibup2W7E0CzCzh71u1Mcy3xk1sYePx-dwcxJnHmxJReBBWjJZEAeCrkbnn_OCuo2fA-EQyNJtlN5F2w\";\n\n\tprivate static final String ES256_SIGNED_JWT = \"eyJhbGciOiJFUzI1NiIsImtpZCI6IjMzMGU2ZTQxLTJjNjYtNDdjMC04YTgyLTAyYTFhYTU1NzZlYyJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QifQ.5zoc1PBa4HaZZsR0twJQVNeeCs4oAvohnGLGJrF9NqufTl-14B_ylH1ZT1xpiVFPeJnyYFUoC22QOXT-_XKVGg\";\n\n\tprivate static final String VERIFY_KEY = \"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq4yKxb6SNePdDmQi9xFCrP6QvHosErQzryknQTTTffs0t3cy3Er3lIceuhZ7yQNSCDfPFqG8GoyoKhuChRiA5D+J2ab7bqTa1QJKfnCyERoscftgN2fXPHjHoiKbpGV2tMVw8mXl//tePOAiKbMJaBUnlAvJgkk1rVm08dSwpLC1sr2M19euf9jwnRGkMRZuhp9iCPgECRke5T8Ixpv0uQjSmGHnWUKTFlbj8sM83suROR1Ue64JSGScANc5vk3huJ/J97qTC+K2oKj6L8d9O8dpc4obijEOJwpydNvTYDgbiivYeSB00KS9jlBkQ5B2QqLvLVEygDl3dp59nGx6YQIDAQAB\";\n\n\tprivate static final MediaType APPLICATION_JWK_SET_JSON = new MediaType(\"application\", \"jwk-set+json\");\n\n\tprivate static KeyFactory kf;\n\n\tNimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(withoutSigning());\n\n\t@BeforeAll\n\tpublic static void keyFactory() throws NoSuchAlgorithmException {\n\t\tkf = KeyFactory.getInstance(\"RSA\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwtProcessorIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new NimbusJwtDecoder(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClaimSetConverterWhenIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.setClaimSetConverter(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setJwtValidatorWhenNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.setJwtValidator(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwtInvalidThenThrowJwtException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtException.class)\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.decode(\"invalid\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-5168\n\t@Test\n\tpublic void decodeWhenExpClaimNullThenDoesNotThrowException() {\n\t\tthis.jwtDecoder.decode(EMPTY_EXP_CLAIM_JWT);\n\t}\n\n\t@Test\n\tpublic void decodeWhenIatClaimNullThenDoesNotThrowException() {\n\t\tthis.jwtDecoder.decode(SIGNED_JWT);\n\t}\n\n\t// gh-5457\n\t@Test\n\tpublic void decodeWhenPlainJwtThenExceptionDoesNotMentionClass() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.decode(UNSIGNED_JWT))\n\t\t\t\t.withMessageContaining(\"Unsupported algorithm of none\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwtIsMalformedThenReturnsStockException() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.decode(MALFORMED_JWT))\n\t\t\t\t.withMessage(\"An error occurred while attempting to decode the Jwt: Malformed payload\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenTokenMalformedThenReturnsMalformedTokenMessage() {\n\t\tassertThatExceptionOfType(BadJwtException.class).isThrownBy(() -> this.jwtDecoder.decode(MALFORMED_TOKEN))\n\t\t\t.withMessage(\"An error occurred while attempting to decode the Jwt: Malformed token\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwtFailsValidationThenReturnsCorrespondingErrorMessage() {\n\t\tOAuth2Error failure = new OAuth2Error(\"mock-error\", \"mock-description\", \"mock-uri\");\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class);\n\t\tgiven(jwtValidator.validate(any(Jwt.class))).willReturn(OAuth2TokenValidatorResult.failure(failure));\n\t\tthis.jwtDecoder.setJwtValidator(jwtValidator);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.decode(SIGNED_JWT))\n\t\t\t\t.withMessageContaining(\"mock-description\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwtValidationHasTwoErrorsThenJwtExceptionMessageShowsFirstError() {\n\t\tOAuth2Error firstFailure = new OAuth2Error(\"mock-error\", \"mock-description\", \"mock-uri\");\n\t\tOAuth2Error secondFailure = new OAuth2Error(\"another-error\", \"another-description\", \"another-uri\");\n\t\tOAuth2TokenValidatorResult result = OAuth2TokenValidatorResult.failure(firstFailure, secondFailure);\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class);\n\t\tgiven(jwtValidator.validate(any(Jwt.class))).willReturn(result);\n\t\tthis.jwtDecoder.setJwtValidator(jwtValidator);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.decode(SIGNED_JWT))\n\t\t\t\t.withMessageContaining(\"mock-description\")\n\t\t\t\t.satisfies((ex) -> assertThat(ex)\n\t\t\t\t\t\t.hasFieldOrPropertyWithValue(\"errors\", Arrays.asList(firstFailure, secondFailure))\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenReadingErrorPickTheFirstErrorMessage() {\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class);\n\t\tthis.jwtDecoder.setJwtValidator(jwtValidator);\n\t\tOAuth2Error errorEmpty = new OAuth2Error(\"mock-error\", \"\", \"mock-uri\");\n\t\tOAuth2Error error = new OAuth2Error(\"mock-error\", \"mock-description\", \"mock-uri\");\n\t\tOAuth2Error error2 = new OAuth2Error(\"mock-error-second\", \"mock-description-second\", \"mock-uri-second\");\n\t\tOAuth2TokenValidatorResult result = OAuth2TokenValidatorResult.failure(errorEmpty, error, error2);\n\t\tgiven(jwtValidator.validate(any(Jwt.class))).willReturn(result);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.decode(SIGNED_JWT))\n\t\t\t\t.withMessageContaining(\"mock-description\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingSignedJwtThenReturnsClaimsGivenByClaimSetConverter() {\n\t\tConverter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class);\n\t\tgiven(claimSetConverter.convert(any(Map.class))).willReturn(Collections.singletonMap(\"custom\", \"value\"));\n\t\tthis.jwtDecoder.setClaimSetConverter(claimSetConverter);\n\t\tJwt jwt = this.jwtDecoder.decode(SIGNED_JWT);\n\t\tassertThat(jwt.getClaims()).hasSize(1);\n\t\tassertThat(jwt.getClaims()).containsEntry(\"custom\", \"value\");\n\t}\n\n\t// gh-7885\n\t@Test\n\tpublic void decodeWhenClaimSetConverterFailsThenBadJwtException() {\n\t\tConverter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class);\n\t\tthis.jwtDecoder.setClaimSetConverter(claimSetConverter);\n\t\tgiven(claimSetConverter.convert(any(Map.class))).willThrow(new IllegalArgumentException(\"bad conversion\"));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.jwtDecoder.decode(SIGNED_JWT));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenSignedThenOk() {\n\t\tNimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(withSigning(JWK_SET));\n\t\tJwt jwt = jwtDecoder.decode(SIGNED_JWT);\n\t\tassertThat(jwt.hasClaim(JwtClaimNames.EXP)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwkResponseIsMalformedThenReturnsStockException() {\n\t\tNimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(withSigning(MALFORMED_JWK_SET));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtException.class)\n\t\t\t\t.isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT))\n\t\t\t\t.isNotInstanceOf(BadJwtException.class)\n\t\t\t\t.withMessage(\"An error occurred while attempting to decode the Jwt: Malformed Jwk set\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwkEndpointIsUnresponsiveThenReturnsJwtException() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString jwkSetUri = server.url(\"/.well-known/jwks.json\").toString();\n\t\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();\n\t\t\tserver.shutdown();\n\t\t\t// @formatter:off\n\t\t\tassertThatExceptionOfType(JwtException.class)\n\t\t\t\t\t.isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT))\n\t\t\t\t\t.isNotInstanceOf(BadJwtException.class)\n\t\t\t\t\t.withMessageContaining(\"An error occurred while attempting to decode the Jwt\");\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwkEndpointIsUnresponsiveAndCacheIsConfiguredThenReturnsJwtException() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tCache cache = new ConcurrentMapCache(\"test-jwk-set-cache\");\n\t\t\tString jwkSetUri = server.url(\"/.well-known/jwks.json\").toString();\n\t\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).cache(cache).build();\n\t\t\tserver.shutdown();\n\t\t\t// @formatter:off\n\t\t\tassertThatExceptionOfType(JwtException.class)\n\t\t\t\t\t.isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT))\n\t\t\t\t\t.isNotInstanceOf(BadJwtException.class)\n\t\t\t\t\t.withMessageContaining(\"An error occurred while attempting to decode the Jwt\");\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void decodeWhenIssuerLocationThenOk() {\n\t\tString issuer = \"https://example.org/issuer\";\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), any(ParameterizedTypeReference.class)))\n\t\t\t.willReturn(new ResponseEntity<>(Map.of(\"issuer\", issuer, \"jwks_uri\", issuer + \"/jwks\"), HttpStatus.OK));\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));\n\t\tJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(restOperations).build();\n\t\tJwt jwt = jwtDecoder.decode(SIGNED_JWT);\n\t\tassertThat(jwt.hasClaim(JwtClaimNames.EXP)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void decodeWhenDiscoverJwsAlgorithmsThenOk() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));\n\t\tJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t.discoverJwsAlgorithms()\n\t\t\t.restOperations(restOperations)\n\t\t\t.build();\n\t\tJwt jwt = jwtDecoder.decode(ES256_SIGNED_JWT);\n\t\tassertThat(jwt.hasClaim(JwtClaimNames.EXP)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void withJwkSetUriWhenNullOrEmptyThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusJwtDecoder.withJwkSetUri(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void jwsAlgorithmWhenNullThenThrowsException() {\n\t\tNimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> builder.jwsAlgorithm(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void restOperationsWhenNullThenThrowsException() {\n\t\tNimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> builder.restOperations(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void cacheWhenNullThenThrowsException() {\n\t\tNimbusJwtDecoder.JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> builder.cache(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withPublicKeyWhenNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusJwtDecoder.withPublicKey(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenSignatureAlgorithmMismatchesKeyTypeThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> NimbusJwtDecoder.withPublicKey(key())\n\t\t\t\t\t\t.signatureAlgorithm(SignatureAlgorithm.ES256)\n\t\t\t\t\t\t.build()\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingPublicKeyThenSuccessfullyDecodes() throws Exception {\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(key()).build();\n\t\t// @formatter:off\n\t\tassertThat(decoder.decode(RS256_SIGNED_JWT))\n\t\t\t\t.extracting(Jwt::getSubject)\n\t\t\t\t.isEqualTo(\"test-subject\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingPublicKeyWithRs512ThenSuccessfullyDecodes() throws Exception {\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(key())\n\t\t\t\t.signatureAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(RS512_SIGNED_JWT))\n\t\t\t\t.extracting(Jwt::getSubject)\n\t\t\t\t.isEqualTo(\"test-subject\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-7049\n\t@Test\n\tpublic void decodeWhenUsingPublicKeyWithKidThenStillUsesKey() throws Exception {\n\t\tRSAPublicKey publicKey = TestKeys.DEFAULT_PUBLIC_KEY;\n\t\tRSAPrivateKey privateKey = TestKeys.DEFAULT_PRIVATE_KEY;\n\t\t// @formatter:off\n\t\tJWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256)\n\t\t\t\t.keyID(\"one\")\n\t\t\t\t.build();\n\t\tJWTClaimsSet claimsSet = new JWTClaimsSet.Builder()\n\t\t\t\t.subject(\"test-subject\")\n\t\t\t\t.expirationTime(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSignedJWT signedJwt = signedJwt(privateKey, header, claimsSet);\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder\n\t\t\t\t.withPublicKey(publicKey)\n\t\t\t\t.signatureAlgorithm(SignatureAlgorithm.RS256)\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(signedJwt.serialize()))\n\t\t\t\t.extracting(Jwt::getSubject)\n\t\t\t\t.isEqualTo(\"test-subject\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenSignatureMismatchesAlgorithmThenThrowsException() throws Exception {\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(key())\n\t\t\t.signatureAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t.build();\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(RS256_SIGNED_JWT));\n\t\t// @formatter:on\n\t}\n\n\t// gh-8730\n\t@Test\n\tpublic void withPublicKeyWhenUsingCustomTypeHeaderThenSuccessfullyDecodes() throws Exception {\n\t\tRSAPublicKey publicKey = TestKeys.DEFAULT_PUBLIC_KEY;\n\t\tRSAPrivateKey privateKey = TestKeys.DEFAULT_PRIVATE_KEY;\n\t\tJWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).type(new JOSEObjectType(\"JWS\")).build();\n\t\tJWTClaimsSet claimsSet = new JWTClaimsSet.Builder().expirationTime(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t.build();\n\t\tSignedJWT signedJwt = signedJwt(privateKey, header, claimsSet);\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(publicKey)\n\t\t\t\t.signatureAlgorithm(SignatureAlgorithm.RS256)\n\t\t\t\t.build();\n\t\tdecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(new JwtTypeValidator(\"JWS\")));\n\t\t// @formatter:on\n\t\tassertThat(decoder.decode(signedJwt.serialize()).hasClaim(JwtClaimNames.EXP)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void withPublicKeyWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusJwtDecoder.withPublicKey(key()).jwtProcessorCustomizer(null))\n\t\t\t\t.withMessage(\"jwtProcessorCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withSecretKeyWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusJwtDecoder.withSecretKey(null))\n\t\t\t\t.withMessage(\"secretKey cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withSecretKeyWhenMacAlgorithmNullThenThrowsIllegalArgumentException() {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusJwtDecoder.withSecretKey(secretKey).macAlgorithm(null))\n\t\t\t\t.withMessage(\"macAlgorithm cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingSecretKeyThenSuccessfullyDecodes() throws Exception {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\tMacAlgorithm macAlgorithm = MacAlgorithm.HS256;\n\t\t// @formatter:off\n\t\tJWTClaimsSet claimsSet = new JWTClaimsSet.Builder()\n\t\t\t\t.subject(\"test-subject\")\n\t\t\t\t.expirationTime(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSignedJWT signedJWT = signedJwt(secretKey, macAlgorithm, claimsSet);\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withSecretKey(secretKey)\n\t\t\t\t.macAlgorithm(macAlgorithm)\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(signedJWT.serialize()))\n\t\t\t\t.extracting(Jwt::getSubject)\n\t\t\t\t.isEqualTo(\"test-subject\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingSecretKeyAndIncorrectAlgorithmThenThrowsJwtException() throws Exception {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\tMacAlgorithm macAlgorithm = MacAlgorithm.HS256;\n\t\t// @formatter:off\n\t\tJWTClaimsSet claimsSet = new JWTClaimsSet.Builder()\n\t\t\t\t.subject(\"test-subject\")\n\t\t\t\t.expirationTime(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSignedJWT signedJWT = signedJwt(secretKey, macAlgorithm, claimsSet);\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withSecretKey(secretKey)\n\t\t\t\t.macAlgorithm(MacAlgorithm.HS512)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(signedJWT.serialize()));\n\t\t// @formatter:on\n\t}\n\n\t// gh-7056\n\t@Test\n\tpublic void decodeWhenUsingSecretKeyWithKidThenStillUsesKey() throws Exception {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\t// @formatter:off\n\t\tJWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS256)\n\t\t\t\t.keyID(\"one\")\n\t\t\t\t.build();\n\t\tJWTClaimsSet claimsSet = new JWTClaimsSet.Builder()\n\t\t\t\t.subject(\"test-subject\")\n\t\t\t\t.expirationTime(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSignedJWT signedJwt = signedJwt(secretKey, header, claimsSet);\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withSecretKey(secretKey)\n\t\t\t\t.macAlgorithm(MacAlgorithm.HS256)\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(signedJwt.serialize()))\n\t\t\t\t.extracting(Jwt::getSubject)\n\t\t\t\t.isEqualTo(\"test-subject\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withJwkSourceWhenDefaultsThenUsesProvidedJwkSource() throws Exception {\n\t\tJWKSource<SecurityContext> source = mock(JWKSource.class);\n\t\tgiven(source.get(any(), any())).willReturn(JWKSet.parse(JWK_SET).getKeys());\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSource(source).build();\n\t\tJwt jwt = decoder.decode(SIGNED_JWT);\n\t\tassertThat(jwt.getClaimAsString(\"sub\")).isEqualTo(\"test-subject\");\n\t}\n\n\t// gh-8730\n\t@Test\n\tpublic void withSecretKeyWhenUsingCustomTypeHeaderThenSuccessfullyDecodes() throws Exception {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\t// @formatter:off\n\t\tJWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS256)\n\t\t\t\t.type(new JOSEObjectType(\"JWS\"))\n\t\t\t\t.build();\n\t\tJWTClaimsSet claimsSet = new JWTClaimsSet.Builder()\n\t\t\t\t.expirationTime(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSignedJWT signedJwt = signedJwt(secretKey, header, claimsSet);\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder decoder = NimbusJwtDecoder.withSecretKey(secretKey)\n\t\t\t\t.macAlgorithm(MacAlgorithm.HS256)\n\t\t\t\t.build();\n\t\tdecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(new JwtTypeValidator(\"JWS\")));\n\t\t// @formatter:on\n\t\tassertThat(decoder.decode(signedJwt.serialize()).hasClaim(JwtClaimNames.EXP)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void withSecretKeyWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusJwtDecoder.withSecretKey(secretKey).jwtProcessorCustomizer(null))\n\t\t\t\t.withMessage(\"jwtProcessorCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void jwsKeySelectorWhenNoAlgorithmThenReturnsRS256Selector() {\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\tJWSKeySelector<SecurityContext> jwsKeySelector = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t.jwsKeySelector(jwkSource);\n\t\tassertThat(jwsKeySelector instanceof JWSVerificationKeySelector);\n\t\tJWSVerificationKeySelector<?> jwsVerificationKeySelector = (JWSVerificationKeySelector<?>) jwsKeySelector;\n\t\tassertThat(jwsVerificationKeySelector.isAllowed(JWSAlgorithm.RS256)).isTrue();\n\t}\n\n\t@Test\n\tpublic void jwsKeySelectorWhenOneAlgorithmThenReturnsSingleSelector() {\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\t// @formatter:off\n\t\tJWSKeySelector<SecurityContext> jwsKeySelector = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.jwsAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t\t.jwsKeySelector(jwkSource);\n\t\t// @formatter:on\n\t\tassertThat(jwsKeySelector instanceof JWSVerificationKeySelector);\n\t\tJWSVerificationKeySelector<?> jwsVerificationKeySelector = (JWSVerificationKeySelector<?>) jwsKeySelector;\n\t\tassertThat(jwsVerificationKeySelector.isAllowed(JWSAlgorithm.RS512)).isTrue();\n\t}\n\n\t@Test\n\tpublic void jwsKeySelectorWhenMultipleAlgorithmThenReturnsCompositeSelector() {\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\t// @formatter:off\n\t\tJWSKeySelector<SecurityContext> jwsKeySelector = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.jwsAlgorithm(SignatureAlgorithm.RS256)\n\t\t\t\t.jwsAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t\t.jwsKeySelector(jwkSource);\n\t\t// @formatter:on\n\t\tassertThat(jwsKeySelector instanceof JWSVerificationKeySelector);\n\t\tJWSVerificationKeySelector<?> jwsAlgorithmMapKeySelector = (JWSVerificationKeySelector<?>) jwsKeySelector;\n\t\tassertThat(jwsAlgorithmMapKeySelector.isAllowed(JWSAlgorithm.RS256)).isTrue();\n\t\tassertThat(jwsAlgorithmMapKeySelector.isAllowed(JWSAlgorithm.RS512)).isTrue();\n\t}\n\n\t// gh-7290\n\t@Test\n\tpublic void decodeWhenJwkSetRequestedThenAcceptHeaderJsonAndJwkSetJson() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));\n\t\t// @formatter:off\n\t\tJWTProcessor<SecurityContext> processor = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.processor();\n\t\t// @formatter:on\n\t\tNimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(processor);\n\t\tjwtDecoder.decode(SIGNED_JWT);\n\t\tArgumentCaptor<RequestEntity> requestEntityCaptor = ArgumentCaptor.forClass(RequestEntity.class);\n\t\tverify(restOperations).exchange(requestEntityCaptor.capture(), eq(String.class));\n\t\tList<MediaType> acceptHeader = requestEntityCaptor.getValue().getHeaders().getAccept();\n\t\tassertThat(acceptHeader).contains(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON);\n\t}\n\n\t@Test\n\tpublic void decodeWhenCacheThenStoreRetrievedJwkSetToCache() {\n\t\tCache cache = new ConcurrentMapCache(\"test-jwk-set-cache\");\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.cache(cache)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tjwtDecoder.decode(SIGNED_JWT);\n\t\tassertThat(cache.get(JWK_SET_URI, String.class)).isEqualTo(JWK_SET);\n\t\tArgumentCaptor<RequestEntity> requestEntityCaptor = ArgumentCaptor.forClass(RequestEntity.class);\n\t\tverify(restOperations).exchange(requestEntityCaptor.capture(), eq(String.class));\n\t\tverifyNoMoreInteractions(restOperations);\n\t\tList<MediaType> acceptHeader = requestEntityCaptor.getValue().getHeaders().getAccept();\n\t\tassertThat(acceptHeader).contains(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON);\n\t}\n\n\t@Test\n\tpublic void decodeWhenCacheStoredThenAbleToRetrieveJwkSetFromCache() {\n\t\tCache cache = new ConcurrentMapCache(\"test-jwk-set-cache\");\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder1 = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.cache(cache)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tjwtDecoder1.decode(SIGNED_JWT);\n\t\tassertThat(cache.get(JWK_SET_URI, String.class)).isEqualTo(JWK_SET);\n\t\tverify(restOperations).exchange(any(RequestEntity.class), eq(String.class));\n\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder2 = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.cache(cache)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tjwtDecoder2.decode(SIGNED_JWT);\n\t\tverifyNoMoreInteractions(restOperations);\n\t}\n\n\t// gh-11621\n\t@Test\n\tpublic void decodeWhenCacheThenRetrieveFromCache() throws Exception {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tCache cache = new ConcurrentMapCache(\"cache\");\n\t\tcache.put(JWK_SET_URI, JWK_SET);\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.cache(cache)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tjwtDecoder.decode(SIGNED_JWT);\n\t\tassertThat(cache.get(JWK_SET_URI, String.class)).isSameAs(JWK_SET);\n\t\tverifyNoInteractions(restOperations);\n\t}\n\n\t// gh-11621\n\t@Test\n\tpublic void decodeWhenCacheAndUnknownKidShouldTriggerFetchOfJwkSet() throws JOSEException {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tCache cache = new ConcurrentMapCache(\"cache\");\n\t\tcache.put(JWK_SET_URI, JWK_SET);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willReturn(new ResponseEntity<>(NEW_KID_JWK_SET, HttpStatus.OK));\n\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.cache(cache)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// Decode JWT with new KID\n\t\tjwtDecoder.decode(NEW_KID_SIGNED_JWT);\n\n\t\tArgumentCaptor<RequestEntity> requestEntityCaptor = ArgumentCaptor.forClass(RequestEntity.class);\n\t\tverify(restOperations).exchange(requestEntityCaptor.capture(), eq(String.class));\n\t\tverifyNoMoreInteractions(restOperations);\n\t\tassertThat(requestEntityCaptor.getValue().getHeaders().getAccept()).contains(MediaType.APPLICATION_JSON,\n\t\t\t\tAPPLICATION_JWK_SET_JSON);\n\t}\n\n\t// gh-11621\n\t@Test\n\tpublic void decodeWithoutCacheSpecifiedAndUnknownKidShouldTriggerFetchOfJwkSet() throws JOSEException {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class))).willReturn(\n\t\t\t\tnew ResponseEntity<>(JWK_SET, HttpStatus.OK), new ResponseEntity<>(NEW_KID_JWK_SET, HttpStatus.OK));\n\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tjwtDecoder.decode(SIGNED_JWT);\n\n\t\t// Decode JWT with new KID\n\t\tjwtDecoder.decode(NEW_KID_SIGNED_JWT);\n\n\t\tArgumentCaptor<RequestEntity> requestEntityCaptor = ArgumentCaptor.forClass(RequestEntity.class);\n\t\tverify(restOperations, times(2)).exchange(requestEntityCaptor.capture(), eq(String.class));\n\t\tverifyNoMoreInteractions(restOperations);\n\t\tList<RequestEntity> requestEntities = requestEntityCaptor.getAllValues();\n\t\tassertThat(requestEntities.get(0).getHeaders().getAccept()).contains(MediaType.APPLICATION_JSON,\n\t\t\t\tAPPLICATION_JWK_SET_JSON);\n\t\tassertThat(requestEntities.get(1).getHeaders().getAccept()).contains(MediaType.APPLICATION_JSON,\n\t\t\t\tAPPLICATION_JWK_SET_JSON);\n\t}\n\n\t@Test\n\tpublic void decodeWhenCacheIsConfiguredAndValueLoaderErrorsThenThrowsJwtException() {\n\t\tCache cache = new ConcurrentMapCache(\"test-jwk-set-cache\");\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willThrow(new RestClientException(\"Cannot retrieve JWK Set\"));\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.cache(cache)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(JwtException.class)\n\t\t\t\t.isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT))\n\t\t\t\t.isNotInstanceOf(BadJwtException.class)\n\t\t\t\t.withMessageContaining(\"An error occurred while attempting to decode the Jwt\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-11621\n\t@Test\n\tpublic void decodeWhenCacheIsConfiguredAndParseFailsOnCachedValueThenExceptionIgnored() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tCache cache = new ConcurrentMapCache(\"cache\");\n\t\tcache.put(JWK_SET_URI, JWK_SET);\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.cache(cache)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tjwtDecoder.decode(SIGNED_JWT);\n\t\tassertThat(cache.get(JWK_SET_URI, String.class)).isSameAs(JWK_SET);\n\t\tverifyNoInteractions(restOperations);\n\n\t}\n\n\t// gh-8730\n\t@Test\n\tpublic void withJwkSetUriWhenUsingCustomTypeHeaderThenRefuseOmittedType() throws Exception {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));\n\t\t// @formatter:off\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.jwtProcessorCustomizer((p) -> p\n\t\t\t\t\t\t.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType(\"JWS\")))\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT))\n\t\t\t\t.withMessageContaining(\"An error occurred while attempting to decode the Jwt: \"\n\t\t\t\t\t\t+ \"Required JOSE header typ (type) parameter is missing\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withJwkSetUriWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI).jwtProcessorCustomizer(null))\n\t\t\t\t.withMessage(\"jwtProcessorCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenPublicKeyValidateTypeFalseThenSkipsNimbusTypeValidation() throws Exception {\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(TestKeys.DEFAULT_PUBLIC_KEY)\n\t\t\t.validateType(false)\n\t\t\t.build();\n\t\tjwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());\n\t\tRSAPrivateKey privateKey = TestKeys.DEFAULT_PRIVATE_KEY;\n\t\tSignedJWT jwt = signedJwt(privateKey,\n\t\t\t\tnew JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JOSE).build(),\n\t\t\t\tnew JWTClaimsSet.Builder().subject(\"subject\").build());\n\t\tjwtDecoder.decode(jwt.serialize());\n\t}\n\n\t@Test\n\tpublic void decodeWhenSecretKeyValidateTypeFalseThenSkipsNimbusTypeValidation() throws Exception {\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(TestKeys.DEFAULT_SECRET_KEY)\n\t\t\t.validateType(false)\n\t\t\t.build();\n\t\tjwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());\n\t\tSignedJWT jwt = signedJwt(TestKeys.DEFAULT_SECRET_KEY,\n\t\t\t\tnew JWSHeader.Builder(JWSAlgorithm.HS256).type(JOSEObjectType.JOSE).build(),\n\t\t\t\tnew JWTClaimsSet.Builder().subject(\"subject\").build());\n\t\tjwtDecoder.decode(jwt.serialize());\n\t}\n\n\tprivate RSAPublicKey key() throws InvalidKeySpecException {\n\t\tbyte[] decoded = Base64.getDecoder().decode(VERIFY_KEY.getBytes());\n\t\tEncodedKeySpec spec = new X509EncodedKeySpec(decoded);\n\t\treturn (RSAPublicKey) kf.generatePublic(spec);\n\t}\n\n\tprivate SignedJWT signedJwt(SecretKey secretKey, MacAlgorithm jwsAlgorithm, JWTClaimsSet claimsSet)\n\t\t\tthrows Exception {\n\t\treturn signedJwt(secretKey, new JWSHeader(JWSAlgorithm.parse(jwsAlgorithm.getName())), claimsSet);\n\t}\n\n\tprivate SignedJWT signedJwt(SecretKey secretKey, JWSHeader header, JWTClaimsSet claimsSet) throws Exception {\n\t\tJWSSigner signer = new MACSigner(secretKey);\n\t\treturn signedJwt(signer, header, claimsSet);\n\t}\n\n\tprivate SignedJWT signedJwt(PrivateKey privateKey, JWSHeader header, JWTClaimsSet claimsSet) throws Exception {\n\t\tJWSSigner signer = new RSASSASigner(privateKey);\n\t\treturn signedJwt(signer, header, claimsSet);\n\t}\n\n\tprivate SignedJWT signedJwt(JWSSigner signer, JWSHeader header, JWTClaimsSet claimsSet) throws Exception {\n\t\tSignedJWT signedJWT = new SignedJWT(header, claimsSet);\n\t\tsignedJWT.sign(signer);\n\t\treturn signedJWT;\n\t}\n\n\tprivate static JWTProcessor<SecurityContext> withSigning(String jwkResponse) {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(String.class)))\n\t\t\t.willReturn(new ResponseEntity<>(jwkResponse, HttpStatus.OK));\n\t\t// @formatter:off\n\t\treturn NimbusJwtDecoder.withJwkSetUri(JWK_SET_URI)\n\t\t\t\t.restOperations(restOperations)\n\t\t\t\t.processor();\n\t\t// @formatter:on\n\t}\n\n\tprivate static JWTProcessor<SecurityContext> withoutSigning() {\n\t\treturn new MockJwtProcessor();\n\t}\n\n\tprivate static class MockJwtProcessor extends DefaultJWTProcessor<SecurityContext> {\n\n\t\t@Override\n\t\tpublic JWTClaimsSet process(SignedJWT signedJWT, SecurityContext context) throws BadJOSEException {\n\t\t\ttry {\n\t\t\t\treturn signedJWT.getJWTClaimsSet();\n\t\t\t}\n\t\t\tcatch (ParseException ex) {\n\t\t\t\t// Payload not a JSON object\n\t\t\t\tthrow new BadJWTException(ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoderDecoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.security.GeneralSecurityException;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.ECGenParameterSpec;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.crypto.KeyGenerator;\nimport javax.crypto.SecretKey;\n\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.crypto.impl.ECDSA;\nimport com.nimbusds.jose.jwk.Curve;\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.KeyOperation;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.source.ImmutableJWKSet;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Use {@link NimbusJwtDecoder} to decode JWT's encoded with {@link NimbusJwtEncoder}\n *\n * @author Ziqin Wang\n */\nclass NimbusJwtEncoderDecoderTests {\n\n\t@Test\n\tvoid encodeAndDecodeHS256() throws GeneralSecurityException {\n\t\tKeyGenerator keyGenerator = KeyGenerator.getInstance(\"HmacSHA256\");\n\t\tSecretKey secretKey = keyGenerator.generateKey();\n\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withSecretKey(secretKey).build();\n\t\tJwtClaimsSet claims = TestJwtClaimsSets.jwtClaimsSet().build();\n\t\tString jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();\n\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(secretKey).build();\n\t\tJwt decodedJwt = jwtDecoder.decode(jwt);\n\n\t\tassertThat(decodedJwt.getSubject()).isEqualTo(\"subject\");\n\t}\n\n\t@Test\n\tvoid encodeAndDecodeRS256() throws GeneralSecurityException {\n\t\tKeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"RSA\");\n\t\tkeyPairGenerator.initialize(2048);\n\t\tKeyPair keyPair = keyPairGenerator.generateKeyPair();\n\t\tRSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();\n\t\tRSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();\n\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withKeyPair(publicKey, privateKey).build();\n\t\tJwtClaimsSet claims = TestJwtClaimsSets.jwtClaimsSet().build();\n\t\tString jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();\n\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(publicKey).build();\n\t\tJwt decodedJwt = jwtDecoder.decode(jwt);\n\n\t\tassertThat(decodedJwt.getSubject()).isEqualTo(\"subject\");\n\t}\n\n\t@Test\n\tvoid encodeAndDecodeES256() throws GeneralSecurityException, JOSEException {\n\t\tKeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"EC\");\n\t\tkeyPairGenerator.initialize(new ECGenParameterSpec(\"secp256r1\"));\n\t\tKeyPair keyPair = keyPairGenerator.generateKeyPair();\n\t\tECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();\n\t\tECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();\n\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withKeyPair(publicKey, privateKey).build();\n\t\tJwtClaimsSet claims = TestJwtClaimsSets.jwtClaimsSet().build();\n\t\tString jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();\n\n\t\tCurve curve = Curve.forECParameterSpec(publicKey.getParams());\n\t\tJWK jwk = new ECKey.Builder(curve, publicKey).keyOperations(Set.of(KeyOperation.VERIFY))\n\t\t\t.keyUse(KeyUse.SIGNATURE)\n\t\t\t.algorithm(ECDSA.resolveAlgorithm(curve))\n\t\t\t.keyIDFromThumbprint()\n\t\t\t.build();\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSource(new ImmutableJWKSet<>(new JWKSet(jwk)))\n\t\t\t.jwsAlgorithm(Objects.requireNonNull(SignatureAlgorithm.from(jwk.getAlgorithm().getName())))\n\t\t\t.build();\n\t\tJwt decodedJwt = jwtDecoder.decode(jwt);\n\n\t\tassertThat(decodedJwt.getSubject()).isEqualTo(\"subject\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport com.nimbusds.jose.JOSEException;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.KeySourceException;\nimport com.nimbusds.jose.jwk.Curve;\nimport com.nimbusds.jose.jwk.ECKey;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport com.nimbusds.jose.jwk.OctetSequenceKey;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.gen.ECKeyGenerator;\nimport com.nimbusds.jose.jwk.gen.RSAKeyGenerator;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport com.nimbusds.jose.util.Base64URL;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.invocation.InvocationOnMock;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link NimbusJwtEncoder}.\n *\n * @author Joe Grandja\n */\npublic class NimbusJwtEncoderTests {\n\n\tprivate List<JWK> jwkList;\n\n\tprivate JWKSource<SecurityContext> jwkSource;\n\n\tprivate NimbusJwtEncoder jwtEncoder;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.jwkList = new ArrayList<>();\n\t\tthis.jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(new JWKSet(this.jwkList));\n\t\tthis.jwtEncoder = new NimbusJwtEncoder(this.jwkSource);\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwkSourceNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new NimbusJwtEncoder(null))\n\t\t\t.withMessage(\"jwkSource cannot be null\");\n\t}\n\n\t@Test\n\tpublic void encodeWhenParametersNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.jwtEncoder.encode(null))\n\t\t\t.withMessage(\"parameters cannot be null\");\n\t}\n\n\t@Test\n\tpublic void encodeWhenClaimsNullThenThrowIllegalArgumentException() {\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, null)))\n\t\t\t.withMessage(\"claims cannot be null\");\n\t}\n\n\t@Test\n\tpublic void encodeWhenJwkSelectFailedThenThrowJwtEncodingException() throws Exception {\n\t\tthis.jwkSource = mock(JWKSource.class);\n\t\tthis.jwtEncoder = new NimbusJwtEncoder(this.jwkSource);\n\t\tgiven(this.jwkSource.get(any(), any())).willThrow(new KeySourceException(\"key source error\"));\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tassertThatExceptionOfType(JwtEncodingException.class)\n\t\t\t.isThrownBy(() -> this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)))\n\t\t\t.withMessageContaining(\"Failed to select a JWK signing key -> key source error\");\n\t}\n\n\t@Test\n\tpublic void encodeWhenJwkMultipleSelectedThenThrowJwtEncodingException() throws Exception {\n\t\tRSAKey rsaJwk = TestJwks.rsa().algorithm(JWSAlgorithm.RS256).build();\n\t\tthis.jwkList.add(rsaJwk);\n\t\tthis.jwkList.add(rsaJwk);\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tassertThatExceptionOfType(JwtEncodingException.class)\n\t\t\t.isThrownBy(() -> this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)))\n\t\t\t.withMessageContaining(\"Failed to select a key since there are multiple for the signing algorithm [RS256]\");\n\t}\n\n\t@Test\n\tpublic void encodeWhenJwkSelectEmptyThenThrowJwtEncodingException() {\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tassertThatExceptionOfType(JwtEncodingException.class)\n\t\t\t.isThrownBy(() -> this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)))\n\t\t\t.withMessageContaining(\"Failed to select a JWK signing key\");\n\t}\n\n\t@Test\n\tpublic void encodeWhenHeadersNotProvidedThenDefaulted() {\n\t\t// @formatter:off\n\t\tRSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t\t.keyID(\"rsa-jwk-1\")\n\t\t\t\t.build();\n\t\tthis.jwkList.add(rsaJwk);\n\t\t// @formatter:on\n\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tJwt encodedJws = this.jwtEncoder.encode(JwtEncoderParameters.from(jwtClaimsSet));\n\n\t\tassertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.ALG, SignatureAlgorithm.RS256);\n\t}\n\n\t@Test\n\tpublic void encodeWhenJwkSelectWithProvidedKidThenSelected() {\n\t\t// @formatter:off\n\t\tRSAKey rsaJwk1 = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t\t.keyID(\"rsa-jwk-1\")\n\t\t\t\t.build();\n\t\tthis.jwkList.add(rsaJwk1);\n\t\tRSAKey rsaJwk2 = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t\t.keyID(\"rsa-jwk-2\")\n\t\t\t\t.build();\n\t\tthis.jwkList.add(rsaJwk2);\n\t\t// @formatter:on\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).keyId(rsaJwk2.getKeyID()).build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tJwt encodedJws = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tassertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk2.getKeyID());\n\t}\n\n\t@Test\n\tpublic void encodeWhenJwkSelectWithProvidedX5TS256ThenSelected() {\n\t\t// @formatter:off\n\t\tRSAKey rsaJwk1 = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t\t.x509CertSHA256Thumbprint(new Base64URL(\"x509CertSHA256Thumbprint-1\"))\n\t\t\t\t.keyID(null)\n\t\t\t\t.build();\n\t\tthis.jwkList.add(rsaJwk1);\n\t\tRSAKey rsaJwk2 = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t\t.x509CertSHA256Thumbprint(new Base64URL(\"x509CertSHA256Thumbprint-2\"))\n\t\t\t\t.keyID(null)\n\t\t\t\t.build();\n\t\tthis.jwkList.add(rsaJwk2);\n\t\t// @formatter:on\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t.x509SHA256Thumbprint(rsaJwk1.getX509CertSHA256Thumbprint().toString())\n\t\t\t.build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tJwt encodedJws = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tassertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.X5T_S256,\n\t\t\t\trsaJwk1.getX509CertSHA256Thumbprint().toString());\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.KID)).isNull();\n\t}\n\n\t@Test\n\tpublic void encodeWhenJwkUseEncryptionThenThrowJwtEncodingException() throws Exception {\n\t\t// @formatter:off\n\t\tRSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t\t.keyUse(KeyUse.ENCRYPTION)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tthis.jwkSource = mock(JWKSource.class);\n\t\tthis.jwtEncoder = new NimbusJwtEncoder(this.jwkSource);\n\t\tgiven(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tassertThatExceptionOfType(JwtEncodingException.class)\n\t\t\t.isThrownBy(() -> this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"Failed to create a JWS Signer -> The JWK use must be sig (signature) or unspecified\");\n\t}\n\n\t@Test\n\tpublic void encodeWhenSuccessThenDecodes() throws Exception {\n\t\t// @formatter:off\n\t\tRSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t\t.keyID(\"rsa-jwk-1\")\n\t\t\t\t.x509CertSHA256Thumbprint(new Base64URL(\"x509CertSHA256Thumbprint-1\"))\n\t\t\t\t.build();\n\t\tthis.jwkList.add(rsaJwk);\n\t\t// @formatter:on\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tJwt encodedJws = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tassertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.ALG, jwsHeader.getAlgorithm());\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.JKU)).isNull();\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.JWK)).isNull();\n\t\tassertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk.getKeyID());\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5U)).isNull();\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5C)).isNull();\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5T)).isNull();\n\t\tassertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.X5T_S256,\n\t\t\t\trsaJwk.getX509CertSHA256Thumbprint().toString());\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.TYP)).isNull();\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.CTY)).isNull();\n\t\tassertThat(encodedJws.getHeaders().get(JoseHeaderNames.CRIT)).isNull();\n\n\t\tassertThat(encodedJws.getIssuer()).isEqualTo(jwtClaimsSet.getIssuer());\n\t\tassertThat(encodedJws.getSubject()).isEqualTo(jwtClaimsSet.getSubject());\n\t\tassertThat(encodedJws.getAudience()).isEqualTo(jwtClaimsSet.getAudience());\n\t\tassertThat(encodedJws.getExpiresAt()).isEqualTo(jwtClaimsSet.getExpiresAt());\n\t\tassertThat(encodedJws.getNotBefore()).isEqualTo(jwtClaimsSet.getNotBefore());\n\t\tassertThat(encodedJws.getIssuedAt()).isEqualTo(jwtClaimsSet.getIssuedAt());\n\t\tassertThat(encodedJws.getId()).isEqualTo(jwtClaimsSet.getId());\n\t\tassertThat(encodedJws.<String>getClaim(\"custom-claim-name\")).isEqualTo(\"custom-claim-value\");\n\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(rsaJwk.toRSAPublicKey()).build();\n\t\tjwtDecoder.decode(encodedJws.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void encodeWhenKeysRotatedThenNewKeyUsed() throws Exception {\n\t\tTestJWKSource jwkSource = new TestJWKSource();\n\t\tJWKSource<SecurityContext> jwkSourceDelegate = spy(new JWKSource<SecurityContext>() {\n\t\t\t@Override\n\t\t\tpublic List<JWK> get(JWKSelector jwkSelector, SecurityContext context) {\n\t\t\t\treturn jwkSource.get(jwkSelector, context);\n\t\t\t}\n\t\t});\n\t\tNimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSourceDelegate);\n\n\t\tJwkListResultCaptor jwkListResultCaptor = new JwkListResultCaptor();\n\t\twillAnswer(jwkListResultCaptor).given(jwkSourceDelegate).get(any(), any());\n\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256).build();\n\t\tJwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();\n\n\t\tJwt encodedJws = jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tJWK jwk1 = jwkListResultCaptor.getResult().get(0);\n\t\tNimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(((RSAKey) jwk1).toRSAPublicKey()).build();\n\t\tjwtDecoder.decode(encodedJws.getTokenValue());\n\n\t\tjwkSource.rotate(); // Simulate key rotation\n\n\t\tencodedJws = jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet));\n\n\t\tJWK jwk2 = jwkListResultCaptor.getResult().get(0);\n\t\tjwtDecoder = NimbusJwtDecoder.withPublicKey(((RSAKey) jwk2).toRSAPublicKey()).build();\n\t\tjwtDecoder.decode(encodedJws.getTokenValue());\n\n\t\tassertThat(jwk1.getKeyID()).isNotEqualTo(jwk2.getKeyID());\n\t}\n\n\t@Test\n\tpublic void encodeWhenMultipleKeysThenJwkSelectorUsed() throws Exception {\n\t\tJWK jwk = TestJwks.rsa().algorithm(JWSAlgorithm.RS256).build();\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\tgiven(jwkSource.get(any(), any())).willReturn(List.of(jwk, jwk));\n\t\tConverter<List<JWK>, JWK> selector = mock(Converter.class);\n\t\tgiven(selector.convert(any())).willReturn(TestJwks.DEFAULT_RSA_JWK);\n\n\t\tNimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);\n\t\tjwtEncoder.setJwkSelector(selector);\n\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder().subject(\"sub\").build();\n\t\tjwtEncoder.encode(JwtEncoderParameters.from(claims));\n\n\t\tverify(selector).convert(any());\n\t}\n\n\t@Test\n\tpublic void encodeWhenSingleKeyThenJwkSelectorIsNotUsed() throws Exception {\n\t\tJWK jwk = TestJwks.rsa().algorithm(JWSAlgorithm.RS256).build();\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\tgiven(jwkSource.get(any(), any())).willReturn(List.of(jwk));\n\t\tConverter<List<JWK>, JWK> selector = mock(Converter.class);\n\n\t\tNimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);\n\t\tjwtEncoder.setJwkSelector(selector);\n\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder().subject(\"sub\").build();\n\t\tjwtEncoder.encode(JwtEncoderParameters.from(claims));\n\n\t\tverifyNoInteractions(selector);\n\t}\n\n\t@Test\n\tpublic void encodeWhenNoKeysThenJwkSelectorIsNotUsed() throws Exception {\n\t\tJWKSource<SecurityContext> jwkSource = mock(JWKSource.class);\n\t\tgiven(jwkSource.get(any(), any())).willReturn(List.of());\n\t\tConverter<List<JWK>, JWK> selector = mock(Converter.class);\n\n\t\tNimbusJwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource);\n\t\tjwtEncoder.setJwkSelector(selector);\n\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder().subject(\"sub\").build();\n\t\tassertThatExceptionOfType(JwtEncodingException.class)\n\t\t\t.isThrownBy(() -> jwtEncoder.encode(JwtEncoderParameters.from(claims)));\n\n\t\tverifyNoInteractions(selector);\n\t}\n\n\t// Default algorithm\n\t@Test\n\tvoid keyPairBuilderWithRsaDefaultAlgorithm() throws JOSEException {\n\t\tRSAKeyGenerator generator = new RSAKeyGenerator(2048);\n\t\tRSAKey key = generator.generate();\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withKeyPair(key.toRSAPublicKey(), key.toRSAPrivateKey()).build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsKey(JoseHeaderNames.KID);\n\t}\n\n\t@Test\n\tvoid keyPairBuilderWithEcDefaultAlgorithm() throws JOSEException {\n\t\tECKeyGenerator generator = new ECKeyGenerator(Curve.P_256);\n\t\tECKey key = generator.generate();\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withKeyPair(key.toECPublicKey(), key.toECPrivateKey()).build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsKey(JoseHeaderNames.KID);\n\t}\n\n\t@Test\n\tvoid keyPairBuilderWithSecretKeyDefaultAlgorithm() {\n\t\tSecretKey key = TestKeys.DEFAULT_SECRET_KEY;\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withSecretKey(key).build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsKey(JoseHeaderNames.KID);\n\t}\n\n\t// With custom algorithm\n\t@Test\n\tvoid keyPairBuilderWithRsaWithAlgorithm() throws JOSEException {\n\t\tRSAKeyGenerator generator = new RSAKeyGenerator(2048);\n\t\tRSAKey key = generator.generate();\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withKeyPair(key.toRSAPublicKey(), key.toRSAPrivateKey())\n\t\t\t.algorithm(SignatureAlgorithm.RS384)\n\t\t\t.build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.ALG, SignatureAlgorithm.RS384);\n\t\tassertThat(jwt.getHeaders()).containsKey(JoseHeaderNames.KID);\n\t}\n\n\t@Test\n\tvoid keyPairBuilderWithEcWithAlgorithm() throws JOSEException {\n\t\tECKeyGenerator generator = new ECKeyGenerator(Curve.P_384);\n\t\tECKey key = generator.generate();\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withKeyPair(key.toECPublicKey(), key.toECPrivateKey()).build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.ALG, SignatureAlgorithm.ES384);\n\t\tassertThat(jwt.getHeaders()).containsKey(JoseHeaderNames.KID);\n\t}\n\n\t@Test\n\tvoid keyPairBuilderWithSecretKeyWithAlgorithm() {\n\t\tString keyStr = UUID.randomUUID().toString();\n\t\tkeyStr += keyStr;\n\t\tSecretKey Key = new SecretKeySpec(keyStr.getBytes(), \"AES\");\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withSecretKey(Key).algorithm(MacAlgorithm.HS512).build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.ALG, MacAlgorithm.HS512);\n\t\tassertThat(jwt.getHeaders()).containsKey(JoseHeaderNames.KID);\n\t}\n\n\t@Test\n\tvoid keyPairBuilderWhenShortSecretThenHigherAlgorithmNotSupported() {\n\t\tString keyStr = UUID.randomUUID().toString();\n\t\tSecretKey Key = new SecretKeySpec(keyStr.getBytes(), \"AES\");\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> NimbusJwtEncoder.withSecretKey(Key).algorithm(MacAlgorithm.HS512).build());\n\t}\n\n\t@Test\n\tvoid keyPairBuilderWhenTooShortSecretThenException() {\n\t\tSecretKey Key = new SecretKeySpec(\"key\".getBytes(), \"AES\");\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> NimbusJwtEncoder.withSecretKey(Key));\n\t}\n\n\t// with custom jwkPostProcessor\n\t@Test\n\tvoid keyPairBuilderWithRsaWithAlgorithmAndJwkSource() throws JOSEException {\n\t\tRSAKeyGenerator generator = new RSAKeyGenerator(2048);\n\t\tRSAKey key = generator.generate();\n\t\tString keyId = UUID.randomUUID().toString();\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withKeyPair(key.toRSAPublicKey(), key.toRSAPrivateKey())\n\t\t\t.algorithm(SignatureAlgorithm.RS384)\n\t\t\t.jwkPostProcessor((builder) -> builder.keyID(keyId))\n\t\t\t.build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.ALG, SignatureAlgorithm.RS384);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.KID, keyId);\n\t}\n\n\t@Test\n\tvoid keyPairBuilderWithEcWithAlgorithmAndJwkSource() throws JOSEException {\n\t\tECKeyGenerator generator = new ECKeyGenerator(Curve.P_256);\n\t\tECKey key = generator.generate();\n\t\tString keyId = UUID.randomUUID().toString();\n\t\tConsumer<ECKey.Builder> jwkPostProcessor = (builder) -> builder.keyID(keyId);\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withKeyPair(key.toECPublicKey(), key.toECPrivateKey())\n\t\t\t.jwkPostProcessor(jwkPostProcessor)\n\t\t\t.build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.ALG, SignatureAlgorithm.ES256);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.KID, keyId);\n\t}\n\n\t@Test\n\tvoid keyPairBuilderWithSecretKeyWithAlgorithmAndJwkSource() {\n\t\tfinal String keyStr = UUID.randomUUID().toString();\n\t\tSecretKey key = new SecretKeySpec(keyStr.getBytes(), \"HS256\");\n\t\tString keyId = UUID.randomUUID().toString();\n\t\tConsumer<OctetSequenceKey.Builder> jwkPostProcessor = (builder) -> builder.keyID(keyId);\n\t\tNimbusJwtEncoder jwtEncoder = NimbusJwtEncoder.withSecretKey(key).jwkPostProcessor(jwkPostProcessor).build();\n\t\tJwtClaimsSet claims = buildClaims();\n\t\tJwt jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims));\n\t\tassertJwt(jwt);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.ALG, MacAlgorithm.HS256);\n\t\tassertThat(jwt.getHeaders()).containsEntry(JoseHeaderNames.KID, keyId);\n\t}\n\n\tprivate JwtClaimsSet buildClaims() {\n\t\tInstant now = Instant.now();\n\t\treturn JwtClaimsSet.builder()\n\t\t\t.issuer(\"https://example.com\")\n\t\t\t.subject(\"subject\")\n\t\t\t.audience(Collections.singletonList(\"audience\"))\n\t\t\t.issuedAt(now)\n\t\t\t.notBefore(now)\n\t\t\t.expiresAt(now.plus(1, ChronoUnit.HOURS))\n\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t.claim(\"custom\", \"value\")\n\t\t\t.build();\n\t}\n\n\tprivate static void assertJwt(Jwt jwt) {\n\t\tassertThat(jwt.getIssuer().toString()).isEqualTo(\"https://example.com\");\n\t\tassertThat(jwt.getSubject()).isEqualTo(\"subject\");\n\t\tassertThat(jwt.getAudience()).containsExactly(\"audience\");\n\t\tassertThat(jwt.getIssuedAt()).isNotNull();\n\t\tassertThat(jwt.getNotBefore()).isNotNull();\n\t\tassertThat(jwt.getExpiresAt()).isNotNull();\n\t\tassertThat(jwt.getId()).isNotNull();\n\t\tassertThat(jwt.getClaim(\"custom\").toString()).isEqualTo(\"value\");\n\t}\n\n\tprivate static final class JwkListResultCaptor implements Answer<List<JWK>> {\n\n\t\tprivate List<JWK> result;\n\n\t\tprivate List<JWK> getResult() {\n\t\t\treturn this.result;\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\t@Override\n\t\tpublic List<JWK> answer(InvocationOnMock invocationOnMock) throws Throwable {\n\t\t\tthis.result = (List<JWK>) invocationOnMock.callRealMethod();\n\t\t\treturn this.result;\n\t\t}\n\n\t}\n\n\tprivate static final class TestJWKSource implements JWKSource<SecurityContext> {\n\n\t\tprivate int keyId = 1000;\n\n\t\tprivate JWKSet jwkSet;\n\n\t\tprivate TestJWKSource() {\n\t\t\tinit();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<JWK> get(JWKSelector jwkSelector, SecurityContext context) {\n\t\t\treturn jwkSelector.select(this.jwkSet);\n\t\t}\n\n\t\tprivate void init() {\n\t\t\t// @formatter:off\n\t\t\tRSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t\t\t.keyID(\"rsa-jwk-\" + this.keyId++)\n\t\t\t\t\t.build();\n\t\t\tECKey ecJwk = TestJwks.jwk((ECPublicKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPublic(), (ECPrivateKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPrivate())\n\t\t\t\t\t.keyID(\"ec-jwk-\" + this.keyId++)\n\t\t\t\t\t.build();\n\t\t\tOctetSequenceKey secretJwk = TestJwks.jwk(TestKeys.DEFAULT_SECRET_KEY)\n\t\t\t\t\t.keyID(\"secret-jwk-\" + this.keyId++)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\t\t\tthis.jwkSet = new JWKSet(Arrays.asList(rsaJwk, ecJwk, secretJwk));\n\t\t}\n\n\t\tprivate void rotate() {\n\t\t\tinit();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.UnknownHostException;\nimport java.security.KeyFactory;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.EncodedKeySpec;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.text.ParseException;\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport javax.crypto.SecretKey;\n\nimport com.nimbusds.jose.JOSEObjectType;\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.JWSHeader;\nimport com.nimbusds.jose.JWSSigner;\nimport com.nimbusds.jose.crypto.MACSigner;\nimport com.nimbusds.jose.crypto.RSASSASigner;\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.RSAKey;\nimport com.nimbusds.jose.jwk.source.JWKSecurityContextJWKSet;\nimport com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;\nimport com.nimbusds.jose.proc.JWKSecurityContext;\nimport com.nimbusds.jose.proc.JWSKeySelector;\nimport com.nimbusds.jose.proc.JWSVerificationKeySelector;\nimport com.nimbusds.jwt.JWTClaimsSet;\nimport com.nimbusds.jwt.SignedJWT;\nimport com.nimbusds.jwt.proc.ConfigurableJWTProcessor;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jose.jws.MacAlgorithm;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @author Joe Grandja\n * @since 5.1\n */\npublic class NimbusReactiveJwtDecoderTests {\n\n\tprivate String expired = \"eyJraWQiOiJrZXktaWQtMSIsImFsZyI6IlJTMjU2In0.eyJzY29wZSI6Im1lc3NhZ2U6cmVhZCIsImV4cCI6MTUyOTkzNzYzMX0.Dt5jFOKkB8zAmjciwvlGkj4LNStXWH0HNIfr8YYajIthBIpVgY5Hg_JL8GBmUFzKDgyusT0q60OOg8_Pdi4Lu-VTWyYutLSlNUNayMlyBaVEWfyZJnh2_OwMZr1vRys6HF-o1qZldhwcfvczHg61LwPa1ISoqaAltDTzBu9cGISz2iBUCuR0x71QhbuRNyJdjsyS96NqiM_TspyiOSxmlNch2oAef1MssOQ23CrKilIvEDsz_zk5H94q7rH0giWGdEHCENESsTJS0zvzH6r2xIWjd5WnihFpCPkwznEayxaEhrdvJqT_ceyXCIfY4m3vujPQHNDG0UshpwvDuEbPUg\";\n\n\tprivate String messageReadToken = \"eyJraWQiOiJrZXktaWQtMSIsImFsZyI6IlJTMjU2In0.eyJzY29wZSI6Im1lc3NhZ2U6cmVhZCIsImV4cCI6OTIyMzM3MjAwNjA5NjM3NX0.bnQ8IJDXmQbmIXWku0YT1HOyV_3d0iQSA_0W2CmPyELhsxFETzBEEcZ0v0xCBiswDT51rwD83wbX3YXxb84fM64AhpU8wWOxLjha4J6HJX2JnlG47ydaAVD7eWGSYTavyyQ-CwUjQWrfMVcObFZLYG11ydzRYOR9-aiHcK3AobcTcS8jZFeI8EGQV_Cd3IJ018uFCf6VnXLv7eV2kRt08Go2RiPLW47ExvD7Dzzz_wDBKfb4pNem7fDvuzB3UPcp5m9QvLZicnbS_6AvDi6P1y_DFJf-1T5gkGmX5piDH1L1jg2Yl6tjmXbk5B3VhsyjJuXE6gzq1d-xie0Z1NVOxw\";\n\n\tprivate String unsignedToken = \"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOi0yMDMzMjI0OTcsImp0aSI6IjEyMyIsInR5cCI6IkpXVCJ9.\";\n\n\t// @formatter:off\n\tprivate String jwkSet = \"{\\n\"\n\t\t\t+ \"   \\\"keys\\\":[\\n\"\n\t\t\t+ \"      {\\n\"\n\t\t\t+ \"         \\\"kty\\\":\\\"RSA\\\",\\n\"\n\t\t\t+ \"         \\\"e\\\":\\\"AQAB\\\",\\n\"\n\t\t\t+ \"         \\\"use\\\":\\\"sig\\\",\\n\"\n\t\t\t+ \"         \\\"kid\\\":\\\"key-id-1\\\",\\n\"\n\t\t\t+ \"         \\\"n\\\":\\\"qL48v1clgFw-Evm145pmh8nRYiNt72Gupsshn7Qs8dxEydCRp1DPOV_PahPk1y2nvldBNIhfNL13JOAiJ6BTiF-2ICuICAhDArLMnTH61oL1Hepq8W1xpa9gxsnL1P51thvfmiiT4RTW57koy4xIWmIp8ZXXfYgdH2uHJ9R0CQBuYKe7nEOObjxCFWC8S30huOfW2cYtv0iB23h6w5z2fDLjddX6v_FXM7ktcokgpm3_XmvT_-bL6_GGwz9k6kJOyMTubecr-WT__le8ikY66zlplYXRQh6roFfFCL21Pt8xN5zrk-0AMZUnmi8F2S2ztSBmAVJ7H71ELXsURBVZpw\\\"\\n\"\n\t\t\t+ \"      }\\n\"\n\t\t\t+ \"   ]\\n\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate String jwkSetUri = \"https://issuer/certs\";\n\n\tprivate String rsa512 = \"eyJhbGciOiJSUzUxMiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjE5NzQzMjYxMTl9.LKAx-60EBfD7jC1jb1eKcjO4uLvf3ssISV-8tN-qp7gAjSvKvj4YA9-V2mIb6jcS1X_xGmNy6EIimZXpWaBR3nJmeu-jpe85u4WaW2Ztr8ecAi-dTO7ZozwdtljKuBKKvj4u1nF70zyCNl15AozSG0W1ASrjUuWrJtfyDG6WoZ8VfNMuhtU-xUYUFvscmeZKUYQcJ1KS-oV5tHeF8aNiwQoiPC_9KXCOZtNEJFdq6-uzFdHxvOP2yex5Gbmg5hXonauIFXG2ZPPGdXzm-5xkhBpgM8U7A_6wb3So8wBvLYYm2245QUump63AJRAy8tQpwt4n9MvQxQgS3z9R-NK92A\";\n\n\tprivate String rsa256 = \"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXN1YmplY3QiLCJleHAiOjE5NzQzMjYzMzl9.CT-H2OWEqmSs1NWmnta5ealLFvM8OlbQTjGhfRcKLNxrTrzsOkqBJl-AN3k16BQU7mS32o744TiiZ29NcDlxPsr1MqTlN86-dobPiuNIDLp3A1bOVdXMcVFuMYkrNv0yW0tGS9OjEqsCCuZDkZ1by6AhsHLbGwRY-6AQdcRouZygGpOQu1hNun5j8q5DpSTY4AXKARIFlF-O3OpVbPJ0ebr3Ki-i3U9p_55H0e4-wx2bqcApWlqgofl1I8NKWacbhZgn81iibup2W7E0CzCzh71u1Mcy3xk1sYePx-dwcxJnHmxJReBBWjJZEAeCrkbnn_OCuo2fA-EQyNJtlN5F2w\";\n\n\tprivate String publicKey = \"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq4yKxb6SNePdDmQi9xFCrP6QvHosErQzryknQTTTffs0t3cy3Er3lIceuhZ7yQNSCDfPFqG8GoyoKhuChRiA5D+J2ab7bqTa1QJKfnCyERoscftgN2fXPHjHoiKbpGV2tMVw8mXl//tePOAiKbMJaBUnlAvJgkk1rVm08dSwpLC1sr2M19euf9jwnRGkMRZuhp9iCPgECRke5T8Ixpv0uQjSmGHnWUKTFlbj8sM83suROR1Ue64JSGScANc5vk3huJ/J97qTC+K2oKj6L8d9O8dpc4obijEOJwpydNvTYDgbiivYeSB00KS9jlBkQ5B2QqLvLVEygDl3dp59nGx6YQIDAQAB\";\n\n\tprivate MockWebServer server;\n\n\tprivate NimbusReactiveJwtDecoder decoder;\n\n\tprivate static KeyFactory kf;\n\n\t@BeforeAll\n\tpublic static void keyFactory() throws NoSuchAlgorithmException {\n\t\tkf = KeyFactory.getInstance(\"RSA\");\n\t}\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.server.enqueue(new MockResponse().setBody(this.jwkSet));\n\t\tthis.decoder = new NimbusReactiveJwtDecoder(this.server.url(\"/certs\").toString());\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void decodeWhenInvalidUrl() {\n\t\tthis.decoder = new NimbusReactiveJwtDecoder(\"https://s\");\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> this.decoder.decode(this.messageReadToken).block())\n\t\t\t\t.withStackTraceContaining(UnknownHostException.class.getSimpleName());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenMessageReadScopeThenSuccess() {\n\t\tJwt jwt = this.decoder.decode(this.messageReadToken).block();\n\t\tassertThat(jwt.getClaims()).containsEntry(\"scope\", \"message:read\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenRSAPublicKeyThenSuccess() throws Exception {\n\t\tbyte[] bytes = Base64.getDecoder()\n\t\t\t.decode(\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqL48v1clgFw+Evm145pmh8nRYiNt72Gupsshn7Qs8dxEydCRp1DPOV/PahPk1y2nvldBNIhfNL13JOAiJ6BTiF+2ICuICAhDArLMnTH61oL1Hepq8W1xpa9gxsnL1P51thvfmiiT4RTW57koy4xIWmIp8ZXXfYgdH2uHJ9R0CQBuYKe7nEOObjxCFWC8S30huOfW2cYtv0iB23h6w5z2fDLjddX6v/FXM7ktcokgpm3/XmvT/+bL6/GGwz9k6kJOyMTubecr+WT//le8ikY66zlplYXRQh6roFfFCL21Pt8xN5zrk+0AMZUnmi8F2S2ztSBmAVJ7H71ELXsURBVZpwIDAQAB\");\n\t\tRSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance(\"RSA\")\n\t\t\t.generatePublic(new X509EncodedKeySpec(bytes));\n\t\tthis.decoder = new NimbusReactiveJwtDecoder(publicKey);\n\t\tString noKeyId = \"eyJhbGciOiJSUzI1NiJ9.eyJzY29wZSI6IiIsImV4cCI6OTIyMzM3MjAwNjA5NjM3NX0.hNVuHSUkxdLZrDfqdmKcOi0ggmNaDuB4ZPxPtJl1gwBiXzIGN6Hwl24O2BfBZiHFKUTQDs4_RvzD71mEG3DvUrcKmdYWqIB1l8KNmxQLUDG-cAPIpJmRJgCh50tf8OhOE_Cb9E1HcsOUb47kT9iz-VayNBcmo6BmyZLdEGhsdGBrc3Mkz2dd_0PF38I2Hf_cuSjn9gBjFGtiPEXJvob3PEjVTSx_zvodT8D9p3An1R3YBZf5JSd1cQisrXgDX2k1Jmf7UKKWzgfyCgnEtRWWbsUdPqo3rSEY9GDC1iSQXsFTTC1FT_JJDkwzGf011fsU5O_Ko28TARibmKTCxAKNRQ\";\n\t\tthis.decoder.decode(noKeyId).block();\n\t}\n\n\t@Test\n\tpublic void decodeWhenIssuedAtThenSuccess() {\n\t\tString withIssuedAt = \"eyJraWQiOiJrZXktaWQtMSIsImFsZyI6IlJTMjU2In0.eyJzY29wZSI6IiIsImV4cCI6OTIyMzM3MjAwNjA5NjM3NSwiaWF0IjoxNTI5OTQyNDQ4fQ.LBzAJO-FR-uJDHST61oX4kimuQjz6QMJPW_mvEXRB6A-fMQWpfTQ089eboipAqsb33XnwWth9ELju9HMWLk0FjlWVVzwObh9FcoKelmPNR8mZIlFG-pAYGgSwi8HufyLabXHntFavBiFtqwp_z9clSOFK1RxWvt3lywEbGgtCKve0BXOjfKWiH1qe4QKGixH-NFxidvz8Qd5WbJwyb9tChC6ZKoKPv7Jp-N5KpxkY-O2iUtINvn4xOSactUsvKHgF8ZzZjvJGzG57r606OZXaNtoElQzjAPU5xDGg5liuEJzfBhvqiWCLRmSuZ33qwp3aoBnFgEw0B85gsNe3ggABg\";\n\t\tJwt jwt = this.decoder.decode(withIssuedAt).block();\n\t\tassertThat(jwt.getClaims()).containsEntry(JwtClaimNames.IAT, Instant.ofEpochSecond(1529942448L));\n\t}\n\n\t@Test\n\tpublic void decodeWhenExpiredThenFail() {\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t.isThrownBy(() -> this.decoder.decode(this.expired).block());\n\t}\n\n\t@Test\n\tpublic void decodeWhenNoPeriodThenFail() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.decoder.decode(\"\").block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenInvalidJwkSetUrlThenFail() {\n\t\tthis.decoder = new NimbusReactiveJwtDecoder(\"http://localhost:1280/certs\");\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> this.decoder.decode(this.messageReadToken).block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenInvalidSignatureThenFail() {\n\t\tassertThatExceptionOfType(BadJwtException.class).isThrownBy(\n\t\t\t\t() -> this.decoder.decode(this.messageReadToken.substring(0, this.messageReadToken.length() - 2))\n\t\t\t\t\t.block());\n\t}\n\n\t@Test\n\tpublic void decodeWhenAlgNoneThenFail() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.decoder\n\t\t\t\t\t\t.decode(\"ew0KICAiYWxnIjogIm5vbmUiLA0KICAidHlwIjogIkpXVCINCn0.ew0KICAic3ViIjogIjEyMzQ1Njc4OTAiLA0KICAibmFtZSI6ICJKb2huIERvZSIsDQogICJpYXQiOiAxNTE2MjM5MDIyDQp9.\")\n\t\t\t\t\t\t.block()\n\t\t\t\t)\n\t\t\t\t.withMessage(\"Unsupported algorithm of none\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenInvalidAlgMismatchThenFail() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.decoder\n\t\t\t\t\t\t.decode(\"ew0KICAiYWxnIjogIkVTMjU2IiwNCiAgInR5cCI6ICJKV1QiDQp9.ew0KICAic3ViIjogIjEyMzQ1Njc4OTAiLA0KICAibmFtZSI6ICJKb2huIERvZSIsDQogICJpYXQiOiAxNTE2MjM5MDIyDQp9.\")\n\t\t\t\t\t\t.block()\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUnsignedTokenThenMessageDoesNotMentionClass() {\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.decoder.decode(this.unsignedToken).block())\n\t\t\t\t.withMessage(\"Unsupported algorithm of none\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingCustomValidatorThenValidatorIsInvoked() {\n\t\tOAuth2TokenValidator jwtValidator = mock(OAuth2TokenValidator.class);\n\t\tthis.decoder.setJwtValidator(jwtValidator);\n\t\tOAuth2Error error = new OAuth2Error(\"mock-error\", \"mock-description\", \"mock-uri\");\n\t\tOAuth2TokenValidatorResult result = OAuth2TokenValidatorResult.failure(error);\n\t\tgiven(jwtValidator.validate(any(Jwt.class))).willReturn(result);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> this.decoder.decode(this.messageReadToken).block())\n\t\t\t\t.withMessageContaining(\"mock-description\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenReadingErrorPickTheFirstErrorMessage() {\n\t\tOAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class);\n\t\tthis.decoder.setJwtValidator(jwtValidator);\n\t\tOAuth2Error errorEmpty = new OAuth2Error(\"mock-error\", \"\", \"mock-uri\");\n\t\tOAuth2Error error = new OAuth2Error(\"mock-error\", \"mock-description\", \"mock-uri\");\n\t\tOAuth2Error error2 = new OAuth2Error(\"mock-error-second\", \"mock-description-second\", \"mock-uri-second\");\n\t\tOAuth2TokenValidatorResult result = OAuth2TokenValidatorResult.failure(errorEmpty, error, error2);\n\t\tgiven(jwtValidator.validate(any(Jwt.class))).willReturn(result);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> this.decoder.decode(this.messageReadToken).block())\n\t\t\t\t.withMessageContaining(\"mock-description\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingSignedJwtThenReturnsClaimsGivenByClaimSetConverter() {\n\t\tConverter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class);\n\t\tthis.decoder.setClaimSetConverter(claimSetConverter);\n\t\tgiven(claimSetConverter.convert(any(Map.class))).willReturn(Collections.singletonMap(\"custom\", \"value\"));\n\t\tJwt jwt = this.decoder.decode(this.messageReadToken).block();\n\t\tassertThat(jwt.getClaims()).hasSize(1);\n\t\tassertThat(jwt.getClaims()).containsEntry(\"custom\", \"value\");\n\t\tverify(claimSetConverter).convert(any(Map.class));\n\t}\n\n\t// gh-7885\n\t@Test\n\tpublic void decodeWhenClaimSetConverterFailsThenBadJwtException() {\n\t\tConverter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class);\n\t\tthis.decoder.setClaimSetConverter(claimSetConverter);\n\t\tgiven(claimSetConverter.convert(any(Map.class))).willThrow(new IllegalArgumentException(\"bad conversion\"));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.decoder.decode(this.messageReadToken).block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setJwtValidatorWhenGivenNullThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.decoder.setJwtValidator(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setClaimSetConverterWhenNullThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.decoder.setClaimSetConverter(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withJwkSetUriWhenNullOrEmptyThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> NimbusReactiveJwtDecoder.withJwkSetUri(null));\n\t}\n\n\t@Test\n\tpublic void jwsAlgorithmWhenNullThenThrowsException() {\n\t\tNimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder builder = NimbusReactiveJwtDecoder\n\t\t\t.withJwkSetUri(this.jwkSetUri);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> builder.jwsAlgorithm(null));\n\t}\n\n\t@Test\n\tpublic void withJwkSetUriWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder\n\t\t\t\t\t\t.withJwkSetUri(this.jwkSetUri)\n\t\t\t\t\t\t.jwtProcessorCustomizer((Consumer<ConfigurableJWTProcessor<JWKSecurityContext>>) null)\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.withMessage(\"jwtProcessorCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void restOperationsWhenNullThenThrowsException() {\n\t\tNimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder builder = NimbusReactiveJwtDecoder\n\t\t\t.withJwkSetUri(this.jwkSetUri);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> builder.webClient(null));\n\t\t// @formatter:on\n\t}\n\n\t// gh-5603\n\t@Test\n\tpublic void decodeWhenSignedThenOk() {\n\t\tWebClient webClient = mockJwkSetResponse(this.jwkSet);\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)\n\t\t\t\t.webClient(webClient)\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(this.messageReadToken).block())\n\t\t\t\t.extracting(Jwt::getExpiresAt)\n\t\t\t\t.isNotNull();\n\t\t// @formatter:on\n\t\tverify(webClient).get();\n\t}\n\n\t// gh-8730\n\t@Test\n\tpublic void withJwkSetUriWhenUsingCustomTypeHeaderThenRefuseOmittedType() {\n\t\tWebClient webClient = mockJwkSetResponse(this.jwkSet);\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)\n\t\t\t\t.webClient(webClient)\n\t\t\t\t.jwtProcessorCustomizer((p) -> p\n\t\t\t\t\t\t.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType(\"JWS\")))\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(this.messageReadToken).block())\n\t\t\t\t.havingRootCause().withMessage(\"Required JOSE header typ (type) parameter is missing\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withJwkSetUriWhenJwtProcessorCustomizerSetsJWSKeySelectorThenUseCustomizedJWSKeySelector()\n\t\t\tthrows InvalidKeySpecException {\n\t\tWebClient webClient = mockJwkSetResponse(new JWKSet(new RSAKey.Builder(key()).build()).toString());\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)\n\t\t\t\t.jwsAlgorithm(SignatureAlgorithm.ES256).webClient(webClient)\n\t\t\t\t.jwtProcessorCustomizer((p) -> p\n\t\t\t\t\t\t.setJWSKeySelector(new JWSVerificationKeySelector<>(JWSAlgorithm.RS512, new JWKSecurityContextJWKSet())))\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(this.rsa512).block()).extracting(Jwt::getSubject).isEqualTo(\"test-subject\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withPublicKeyWhenNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder.withPublicKey(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenSignatureAlgorithmMismatchesKeyTypeThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder.withPublicKey(key())\n\t\t\t\t\t.signatureAlgorithm(SignatureAlgorithm.ES256)\n\t\t\t\t\t.build()\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void buildWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder.withPublicKey(key())\n\t\t\t\t\t\t.jwtProcessorCustomizer(null)\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.withMessage(\"jwtProcessorCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingPublicKeyThenSuccessfullyDecodes() throws Exception {\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withPublicKey(key())\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(this.rsa256).block())\n\t\t\t\t.extracting(Jwt::getSubject)\n\t\t\t\t.isEqualTo(\"test-subject\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenUsingPublicKeyWithRs512ThenSuccessfullyDecodes() throws Exception {\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withPublicKey(key())\n\t\t\t\t.signatureAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(this.rsa512).block())\n\t\t\t\t.extracting(Jwt::getSubject)\n\t\t\t\t.isEqualTo(\"test-subject\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenSignatureMismatchesAlgorithmThenThrowsException() throws Exception {\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withPublicKey(key())\n\t\t\t\t.signatureAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> decoder\n\t\t\t\t\t\t.decode(this.rsa256)\n\t\t\t\t\t\t.block()\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t// gh-8730\n\t@Test\n\tpublic void withPublicKeyWhenUsingCustomTypeHeaderThenRefuseOmittedType() throws Exception {\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withPublicKey(key())\n\t\t\t\t.jwtProcessorCustomizer((p) -> p\n\t\t\t\t\t\t.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType(\"JWS\")))\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(this.rsa256).block())\n\t\t\t\t.havingRootCause().withMessage(\"Required JOSE header typ (type) parameter is missing\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withJwkSourceWhenNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder.withJwkSource(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withJwkSourceWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder.withJwkSource((jwt) -> Flux.empty())\n\t\t\t\t.jwtProcessorCustomizer(null)\n\t\t\t\t.build())\n\t\t\t.withMessage(\"jwtProcessorCustomizer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenCustomJwkSourceResolutionThenDecodes() {\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder\n\t\t\t\t.withJwkSource((jwt) -> Flux.fromIterable(parseJWKSet(this.jwkSet).getKeys()))\n\t\t\t\t.build();\n\t\tassertThat(decoder.decode(this.messageReadToken).block())\n\t\t\t\t.extracting(Jwt::getExpiresAt)\n\t\t\t\t.isNotNull();\n\t\t// @formatter:on\n\t}\n\n\t// gh-8730\n\t@Test\n\tpublic void withJwkSourceWhenUsingCustomTypeHeaderThenRefuseOmittedType() {\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder\n\t\t\t\t.withJwkSource((jwt) -> Flux.empty())\n\t\t\t\t.jwtProcessorCustomizer((p) -> p\n\t\t\t\t\t\t.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType(\"JWS\")))\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(this.messageReadToken).block())\n\t\t\t\t.havingRootCause()\n\t\t\t\t.withMessage(\"Required JOSE header typ (type) parameter is missing\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withSecretKeyWhenSecretKeyNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder.withSecretKey(null))\n\t\t\t\t.withMessage(\"secretKey cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withSecretKeyWhenJwtProcessorCustomizerNullThenThrowsIllegalArgumentException() {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder\n\t\t\t\t\t\t.withSecretKey(secretKey)\n\t\t\t\t\t\t.jwtProcessorCustomizer(null)\n\t\t\t\t\t\t.build()\n\t\t\t\t)\n\t\t\t\t.withMessage(\"jwtProcessorCustomizer cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withSecretKeyWhenMacAlgorithmNullThenThrowsIllegalArgumentException() {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> NimbusReactiveJwtDecoder\n\t\t\t\t\t\t.withSecretKey(secretKey)\n\t\t\t\t\t\t.macAlgorithm(null)\n\t\t\t\t)\n\t\t\t\t.withMessage(\"macAlgorithm cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenSecretKeyThenSuccess() throws Exception {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\tMacAlgorithm macAlgorithm = MacAlgorithm.HS256;\n\t\t// @formatter:off\n\t\tJWTClaimsSet claimsSet = new JWTClaimsSet.Builder()\n\t\t\t\t.subject(\"test-subject\")\n\t\t\t\t.expirationTime(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSignedJWT signedJWT = signedJwt(secretKey, macAlgorithm, claimsSet);\n\t\t// @formatter:off\n\t\tthis.decoder = NimbusReactiveJwtDecoder.withSecretKey(secretKey)\n\t\t\t\t.macAlgorithm(macAlgorithm)\n\t\t\t\t.build();\n\t\tJwt jwt = this.decoder.decode(signedJWT.serialize())\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t\tassertThat(jwt.getSubject()).isEqualTo(\"test-subject\");\n\t}\n\n\t// gh-8730\n\t@Test\n\tpublic void withSecretKeyWhenUsingCustomTypeHeaderThenRefuseOmittedType() {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\t// @formatter:off\n\t\tNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder.withSecretKey(secretKey)\n\t\t\t\t.jwtProcessorCustomizer((p) -> p\n\t\t\t\t\t\t.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType(\"JWS\")))\n\t\t\t\t)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(this.messageReadToken).block())\n\t\t\t\t.havingRootCause().withMessage(\"Required JOSE header typ (type) parameter is missing\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenSecretKeyAndAlgorithmMismatchThenThrowsJwtException() throws Exception {\n\t\tSecretKey secretKey = TestKeys.DEFAULT_SECRET_KEY;\n\t\tMacAlgorithm macAlgorithm = MacAlgorithm.HS256;\n\t\tJWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(\"test-subject\")\n\t\t\t.expirationTime(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t.build();\n\t\tSignedJWT signedJWT = signedJwt(secretKey, macAlgorithm, claimsSet);\n\t\t// @formatter:off\n\t\tthis.decoder = NimbusReactiveJwtDecoder.withSecretKey(secretKey)\n\t\t\t\t.macAlgorithm(MacAlgorithm.HS512)\n\t\t\t\t.build();\n\t\tassertThatExceptionOfType(BadJwtException.class)\n\t\t\t\t.isThrownBy(() -> this.decoder.decode(signedJWT.serialize()).block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void decodeWhenIssuerLocationThenOk() {\n\t\tString issuer = \"https://example.org/issuer\";\n\t\tWebClient real = WebClient.builder().build();\n\t\tWebClient.RequestHeadersUriSpec spec = spy(real.get());\n\t\tWebClient webClient = spy(WebClient.class);\n\t\tgiven(webClient.get()).willReturn(spec);\n\t\tWebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class);\n\t\tgiven(responseSpec.bodyToMono(String.class)).willReturn(Mono.just(this.jwkSet));\n\t\tgiven(responseSpec.bodyToMono(any(ParameterizedTypeReference.class)))\n\t\t\t.willReturn(Mono.just(Map.of(\"issuer\", issuer, \"jwks_uri\", issuer + \"/jwks\")));\n\t\tgiven(spec.retrieve()).willReturn(responseSpec);\n\t\tReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer)\n\t\t\t.webClient(webClient)\n\t\t\t.build();\n\t\tJwt jwt = jwtDecoder.decode(this.messageReadToken).block();\n\t\tassertThat(jwt.hasClaim(JwtClaimNames.EXP)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void jwsKeySelectorWhenNoAlgorithmThenReturnsRS256Selector() {\n\t\tReactiveRemoteJWKSource jwkSource = mock(ReactiveRemoteJWKSource.class);\n\t\tJWSKeySelector<JWKSecurityContext> jwsKeySelector = NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)\n\t\t\t.jwsKeySelector(jwkSource)\n\t\t\t.block();\n\t\tassertThat(jwsKeySelector).isInstanceOf(JWSVerificationKeySelector.class);\n\t\tJWSVerificationKeySelector<JWKSecurityContext> jwsVerificationKeySelector = (JWSVerificationKeySelector<JWKSecurityContext>) jwsKeySelector;\n\t\tassertThat(jwsVerificationKeySelector.isAllowed(JWSAlgorithm.RS256)).isTrue();\n\t}\n\n\t@Test\n\tpublic void jwsKeySelectorWhenOneAlgorithmThenReturnsSingleSelector() {\n\t\tReactiveRemoteJWKSource jwkSource = mock(ReactiveRemoteJWKSource.class);\n\t\tJWSKeySelector<JWKSecurityContext> jwsKeySelector = NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)\n\t\t\t.jwsAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t.jwsKeySelector(jwkSource)\n\t\t\t.block();\n\t\tassertThat(jwsKeySelector).isInstanceOf(JWSVerificationKeySelector.class);\n\t\tJWSVerificationKeySelector<JWKSecurityContext> jwsVerificationKeySelector = (JWSVerificationKeySelector<JWKSecurityContext>) jwsKeySelector;\n\t\tassertThat(jwsVerificationKeySelector.isAllowed(JWSAlgorithm.RS512)).isTrue();\n\t}\n\n\t@Test\n\tpublic void jwsKeySelectorWhenMultipleAlgorithmThenReturnsCompositeSelector() {\n\t\tReactiveRemoteJWKSource jwkSource = mock(ReactiveRemoteJWKSource.class);\n\t\t// @formatter:off\n\t\tJWSKeySelector<JWKSecurityContext> jwsKeySelector = NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)\n\t\t\t\t.jwsAlgorithm(SignatureAlgorithm.RS256)\n\t\t\t\t.jwsAlgorithm(SignatureAlgorithm.RS512)\n\t\t\t\t.jwsKeySelector(jwkSource).block();\n\t\t// @formatter:on\n\t\tassertThat(jwsKeySelector).isInstanceOf(JWSVerificationKeySelector.class);\n\t\tJWSVerificationKeySelector<?> jwsAlgorithmMapKeySelector = (JWSVerificationKeySelector<?>) jwsKeySelector;\n\t\tassertThat(jwsAlgorithmMapKeySelector.isAllowed(JWSAlgorithm.RS256)).isTrue();\n\t\tassertThat(jwsAlgorithmMapKeySelector.isAllowed(JWSAlgorithm.RS512)).isTrue();\n\t}\n\n\t@Test\n\tpublic void decodeWhenPublicKeyValidateTypeFalseThenSkipsNimbusTypeValidation() throws Exception {\n\t\tNimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withPublicKey(TestKeys.DEFAULT_PUBLIC_KEY)\n\t\t\t.validateType(false)\n\t\t\t.build();\n\t\tjwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());\n\t\tRSAPrivateKey privateKey = TestKeys.DEFAULT_PRIVATE_KEY;\n\t\tSignedJWT jwt = signedJwt(privateKey,\n\t\t\t\tnew JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JOSE).build(),\n\t\t\t\tnew JWTClaimsSet.Builder().subject(\"subject\").build());\n\t\tjwtDecoder.decode(jwt.serialize()).block();\n\t}\n\n\t@Test\n\tpublic void decodeWhenSecretKeyValidateTypeFalseThenSkipsNimbusTypeValidation() throws Exception {\n\t\tNimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withSecretKey(TestKeys.DEFAULT_SECRET_KEY)\n\t\t\t.validateType(false)\n\t\t\t.build();\n\t\tjwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());\n\t\tSignedJWT jwt = signedJwt(TestKeys.DEFAULT_SECRET_KEY,\n\t\t\t\tnew JWSHeader.Builder(JWSAlgorithm.HS256).type(JOSEObjectType.JOSE).build(),\n\t\t\t\tnew JWTClaimsSet.Builder().subject(\"subject\").build());\n\t\tjwtDecoder.decode(jwt.serialize()).block();\n\t}\n\n\t@Test\n\tpublic void decodeWhenJwkSourceValidateTypeFalseThenSkipsNimbusTypeValidation() throws Exception {\n\t\tJWK jwk = new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).privateKey(TestKeys.DEFAULT_PRIVATE_KEY)\n\t\t\t.algorithm(JWSAlgorithm.RS256)\n\t\t\t.build();\n\t\tNimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSource((jwt) -> Flux.just(jwk))\n\t\t\t.validateType(false)\n\t\t\t.build();\n\t\tjwtDecoder.setJwtValidator((jwt) -> OAuth2TokenValidatorResult.success());\n\t\tSignedJWT jwt = signedJwt(TestKeys.DEFAULT_PRIVATE_KEY,\n\t\t\t\tnew JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JOSE).build(),\n\t\t\t\tnew JWTClaimsSet.Builder().subject(\"subject\").build());\n\t\tjwtDecoder.decode(jwt.serialize()).block();\n\t}\n\n\tprivate SignedJWT signedJwt(SecretKey secretKey, MacAlgorithm jwsAlgorithm, JWTClaimsSet claimsSet)\n\t\t\tthrows Exception {\n\t\treturn signedJwt(secretKey, new JWSHeader(JWSAlgorithm.parse(jwsAlgorithm.getName())), claimsSet);\n\t}\n\n\tprivate SignedJWT signedJwt(SecretKey secretKey, JWSHeader header, JWTClaimsSet claimsSet) throws Exception {\n\t\tJWSSigner signer = new MACSigner(secretKey);\n\t\treturn signedJwt(signer, header, claimsSet);\n\t}\n\n\tprivate SignedJWT signedJwt(PrivateKey privateKey, JWSHeader header, JWTClaimsSet claimsSet) throws Exception {\n\t\tJWSSigner signer = new RSASSASigner(privateKey);\n\t\treturn signedJwt(signer, header, claimsSet);\n\t}\n\n\tprivate SignedJWT signedJwt(JWSSigner signer, JWSHeader header, JWTClaimsSet claimsSet) throws Exception {\n\t\tSignedJWT signedJWT = new SignedJWT(header, claimsSet);\n\t\tsignedJWT.sign(signer);\n\t\treturn signedJWT;\n\t}\n\n\tprivate JWKSet parseJWKSet(String jwkSet) {\n\t\ttry {\n\t\t\treturn JWKSet.parse(jwkSet);\n\t\t}\n\t\tcatch (ParseException ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate RSAPublicKey key() throws InvalidKeySpecException {\n\t\tbyte[] decoded = Base64.getDecoder().decode(this.publicKey.getBytes());\n\t\tEncodedKeySpec spec = new X509EncodedKeySpec(decoded);\n\t\treturn (RSAPublicKey) kf.generatePublic(spec);\n\t}\n\n\tprivate static WebClient mockJwkSetResponse(String response) {\n\t\tWebClient real = WebClient.builder().build();\n\t\tWebClient.RequestHeadersUriSpec spec = spy(real.get());\n\t\tWebClient webClient = spy(WebClient.class);\n\t\tgiven(webClient.get()).willReturn(spec);\n\t\tWebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class);\n\t\tgiven(responseSpec.bodyToMono(String.class)).willReturn(Mono.just(response));\n\t\tgiven(spec.retrieve()).willReturn(responseSpec);\n\t\treturn webClient;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport okhttp3.HttpUrl;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * Tests for {@link ReactiveJwtDecoderProviderConfigurationUtils}\n *\n * @author Josh Cummings\n */\npublic class ReactiveJwtDecoderProviderConfigurationUtilsTests {\n\n\t/**\n\t * Contains those parameters required to construct a ReactiveJwtDecoder as well as any\n\t * required parameters\n\t */\n\t// @formatter:off\n\tprivate static final String DEFAULT_RESPONSE_TEMPLATE = \"{\\n\"\n\t\t\t+ \"    \\\"authorization_endpoint\\\": \\\"https://example.com/o/oauth2/v2/auth\\\", \\n\"\n\t\t\t+ \"    \\\"id_token_signing_alg_values_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"RS256\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"issuer\\\": \\\"%s\\\", \\n\"\n\t\t\t+ \"    \\\"jwks_uri\\\": \\\"%s/.well-known/jwks.json\\\", \\n\"\n\t\t\t+ \"    \\\"response_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"code\\\", \\n\"\n\t\t\t+ \"        \\\"token\\\", \\n\"\n\t\t\t+ \"        \\\"id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token\\\", \\n\"\n\t\t\t+ \"        \\\"code id_token\\\", \\n\"\n\t\t\t+ \"        \\\"token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"none\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"subject_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"public\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"token_endpoint\\\": \\\"https://example.com/oauth2/v4/token\\\"\\n\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate static final String JWK_SET = \"{\\\"keys\\\":[{\\\"kty\\\":\\\"RSA\\\",\\\"e\\\":\\\"AQAB\\\",\\\"use\\\":\\\"sig\\\",\\\"kid\\\":\\\"one\\\",\\\"n\\\":\\\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\\\"}]}\";\n\n\tprivate static final String OIDC_METADATA_PATH = \"/.well-known/openid-configuration\";\n\n\tprivate static final String OAUTH_METADATA_PATH = \"/.well-known/oauth-authorization-server\";\n\n\tprivate final WebClient web = WebClient.builder().build();\n\n\tprivate MockWebServer server;\n\n\tprivate String issuer;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.issuer = createIssuerFromServer();\n\t\tthis.issuer += \"path\";\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void issuerWhenResponseIsTypicalThenReturnedConfigurationContainsJwksUri() {\n\t\tprepareConfigurationResponse();\n\t\tMap<String, Object> configuration = ReactiveJwtDecoderProviderConfigurationUtils\n\t\t\t.getConfigurationForIssuerLocation(this.issuer, this.web)\n\t\t\t.block();\n\t\tassertThat(configuration).containsKey(\"jwks_uri\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsTypicalThenReturnedConfigurationContainsJwksUri() {\n\t\tprepareConfigurationResponseOidc();\n\t\tMap<String, Object> configuration = ReactiveJwtDecoderProviderConfigurationUtils\n\t\t\t.getConfigurationForIssuerLocation(this.issuer, this.web)\n\t\t\t.block();\n\t\tassertThat(configuration).containsKey(\"jwks_uri\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsTypicalThenReturnedConfigurationContainsJwksUri() {\n\t\tprepareConfigurationResponseOAuth2();\n\t\tMap<String, Object> configuration = ReactiveJwtDecoderProviderConfigurationUtils\n\t\t\t.getConfigurationForIssuerLocation(this.issuer, this.web)\n\t\t\t.block();\n\t\tassertThat(configuration).containsKey(\"jwks_uri\");\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsNonCompliantThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOidc(\"{ \\\"missing_required_keys\\\" : \\\"and_values\\\" }\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(this.issuer, this.web).block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsNonCompliantThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOAuth2(\"{ \\\"missing_required_keys\\\" : \\\"and_values\\\" }\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(this.issuer, this.web).block());\n\t\t// @formatter:on\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {\n\t\tprepareConfigurationResponseOidc(this.buildResponseWithMissingJwksUri());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(this.issuer, this.web).block())\n\t\t\t\t.withMessage(\"The public JWK set URI must not be null\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {\n\t\tprepareConfigurationResponseOAuth2(this.buildResponseWithMissingJwksUri());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(this.issuer, this.web).block())\n\t\t\t\t.withMessage(\"The public JWK set URI must not be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsMalformedThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOidc(\"malformed\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(this.issuer, this.web).block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsMalformedThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOAuth2(\"malformed\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(this.issuer, this.web).block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackRespondingIssuerMismatchesRequestedIssuerThenThrowsIllegalStateException() {\n\t\tprepareConfigurationResponseOidc(String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer + \"/wrong\", this.issuer));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> {\n\t\t\t\t\tMap<String, Object> configuration = ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(this.issuer, this.web).block();\n\t\t\t\t\tJwtDecoderProviderConfigurationUtils.validateIssuer(configuration, this.issuer);\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2RespondingIssuerMismatchesRequestedIssuerThenThrowsIllegalStateException() {\n\t\tprepareConfigurationResponseOAuth2(\n\t\t\t\tString.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer + \"/wrong\", this.issuer));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> {\n\t\t\t\t\tMap<String, Object> configuration = ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(this.issuer, this.web).block();\n\t\t\t\t\tJwtDecoderProviderConfigurationUtils.validateIssuer(configuration, this.issuer);\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackRequestedIssuerIsUnresponsiveThenThrowsIllegalArgumentException()\n\t\t\tthrows Exception {\n\t\tthis.server.shutdown();\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(\"https://issuer\", this.web).block());\n\t\t// @formatter:on\n\t}\n\n\t// gh-15852\n\t@Test\n\tpublic void oidcWhenHostContainsUnderscoreThenRetains() {\n\t\tUriComponents oidc = ReactiveJwtDecoderProviderConfigurationUtils.oidc(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oidc.getHost()).isEqualTo(\"elated_sutherland\");\n\t\tUriComponents oauth = ReactiveJwtDecoderProviderConfigurationUtils.oauth(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oauth.getHost()).isEqualTo(\"elated_sutherland\");\n\t\tUriComponents oidcRfc8414 = ReactiveJwtDecoderProviderConfigurationUtils\n\t\t\t.oidcRfc8414(\"https://elated_sutherland:8080/path\");\n\t\tassertThat(oidcRfc8414.getHost()).isEqualTo(\"elated_sutherland\");\n\t}\n\n\tprivate void prepareConfigurationResponse() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponse(body);\n\t}\n\n\tprivate void prepareConfigurationResponse(String body) {\n\t\tthis.server.enqueue(response(body));\n\t\tthis.server.enqueue(response(JWK_SET));\n\t}\n\n\tprivate void prepareConfigurationResponseOidc() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponseOidc(body);\n\t}\n\n\tprivate void prepareConfigurationResponseOidc(String body) {\n\t\tMap<String, MockResponse> responses = new HashMap<>();\n\t\tresponses.put(oidc(), response(body));\n\t\tresponses.put(jwks(), response(JWK_SET));\n\t\tprepareConfigurationResponses(responses);\n\t}\n\n\tprivate void prepareConfigurationResponseOAuth2() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponseOAuth2(body);\n\t}\n\n\tprivate void prepareConfigurationResponseOAuth2(String body) {\n\t\tMap<String, MockResponse> responses = new HashMap<>();\n\t\tresponses.put(oauth(), response(body));\n\t\tresponses.put(jwks(), response(JWK_SET));\n\t\tprepareConfigurationResponses(responses);\n\t}\n\n\tprivate void prepareConfigurationResponses(Map<String, MockResponse> responses) {\n\t\tDispatcher dispatcher = new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\t// @formatter:off\n\t\t\t\treturn Optional.of(request)\n\t\t\t\t\t\t.map(RecordedRequest::getRequestUrl)\n\t\t\t\t\t\t.map(HttpUrl::toString)\n\t\t\t\t\t\t.map(responses::get)\n\t\t\t\t\t\t.orElse(new MockResponse().setResponseCode(404));\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t};\n\t\tthis.server.setDispatcher(dispatcher);\n\t}\n\n\tprivate String createIssuerFromServer() {\n\t\treturn this.server.url(\"\").toString();\n\t}\n\n\tprivate String oidc() {\n\t\tURI uri = URI.create(this.issuer);\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUri(uri)\n\t\t\t\t.replacePath(uri.getPath() + OIDC_METADATA_PATH)\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate String oauth() {\n\t\tURI uri = URI.create(this.issuer);\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUri(uri)\n\t\t\t\t.replacePath(OAUTH_METADATA_PATH + uri.getPath())\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate String jwks() {\n\t\treturn this.issuer + \"/.well-known/jwks.json\";\n\t}\n\n\tprivate MockResponse response(String body) {\n\t\t// @formatter:off\n\t\treturn new MockResponse().setBody(body)\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\t// @formatter:on\n\t}\n\n\tpublic String buildResponseWithMissingJwksUri() {\n\t\tJsonMapper mapper = new JsonMapper();\n\t\tMap<String, Object> response = mapper.readValue(DEFAULT_RESPONSE_TEMPLATE,\n\t\t\t\tnew TypeReference<Map<String, Object>>() {\n\t\t\t\t});\n\t\tresponse.remove(\"jwks_uri\");\n\t\treturn mapper.writeValueAsString(response);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecodersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport okhttp3.HttpUrl;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * Tests for {@link ReactiveJwtDecoders}\n *\n * @author Josh Cummings\n * @author Rafiullah Hamedy\n */\npublic class ReactiveJwtDecodersTests {\n\n\t/**\n\t * Contains those parameters required to construct a ReactiveJwtDecoder as well as any\n\t * required parameters\n\t */\n\t// @formatter:off\n\tprivate static final String DEFAULT_RESPONSE_TEMPLATE = \"{\\n\"\n\t\t\t+ \"    \\\"authorization_endpoint\\\": \\\"https://example.com/o/oauth2/v2/auth\\\", \\n\"\n\t\t\t+ \"    \\\"id_token_signing_alg_values_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"RS256\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"issuer\\\": \\\"%s\\\", \\n\"\n\t\t\t+ \"    \\\"jwks_uri\\\": \\\"%s/.well-known/jwks.json\\\", \\n\"\n\t\t\t+ \"    \\\"response_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"code\\\", \\n\"\n\t\t\t+ \"        \\\"token\\\", \\n\"\n\t\t\t+ \"        \\\"id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token\\\", \\n\"\n\t\t\t+ \"        \\\"code id_token\\\", \\n\"\n\t\t\t+ \"        \\\"token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"code token id_token\\\", \\n\"\n\t\t\t+ \"        \\\"none\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"subject_types_supported\\\": [\\n\"\n\t\t\t+ \"        \\\"public\\\"\\n\"\n\t\t\t+ \"    ], \\n\"\n\t\t\t+ \"    \\\"token_endpoint\\\": \\\"https://example.com/oauth2/v4/token\\\"\\n\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate static final String JWK_SET = \"{\\\"keys\\\":[{\\\"kty\\\":\\\"RSA\\\",\\\"e\\\":\\\"AQAB\\\",\\\"use\\\":\\\"sig\\\",\\\"kid\\\":\\\"one\\\",\\\"n\\\":\\\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\\\"}]}\";\n\n\tprivate static final String ISSUER_MISMATCH = \"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvd3Jvbmdpc3N1ZXIiLCJleHAiOjQ2ODcyNTYwNDl9.Ax8LMI6rhB9Pv_CE3kFi1JPuLj9gZycifWrLeDpkObWEEVAsIls9zAhNFyJlG-Oo7up6_mDhZgeRfyKnpSF5GhKJtXJDCzwg0ZDVUE6rS0QadSxsMMGbl7c4y0lG_7TfLX2iWeNJukJj_oSW9KzW4FsBp1BoocWjrreesqQU3fZHbikH-c_Fs2TsAIpHnxflyEzfOFWpJ8D4DtzHXqfvieMwpy42xsPZK3LR84zlasf0Ne1tC_hLHvyHRdAXwn0CMoKxc7-8j0r9Mq8kAzUsPn9If7bMLqGkxUcTPdk5x7opAUajDZx95SXHLmtztNtBa2S6EfPJXuPKG6tM5Wq5Ug\";\n\n\tprivate static final String OIDC_METADATA_PATH = \"/.well-known/openid-configuration\";\n\n\tprivate static final String OAUTH_METADATA_PATH = \"/.well-known/oauth-authorization-server\";\n\n\tprivate MockWebServer server;\n\n\tprivate String issuer;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.server = new MockWebServer();\n\t\tthis.server.start();\n\t\tthis.issuer = createIssuerFromServer();\n\t\tthis.issuer += \"path\";\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() throws Exception {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tpublic void issuerWhenResponseIsTypicalThenReturnedDecoderValidatesIssuer() {\n\t\tprepareConfigurationResponse();\n\t\tReactiveJwtDecoder decoder = ReactiveJwtDecoders.fromOidcIssuerLocation(this.issuer);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(ISSUER_MISMATCH).block())\n\t\t\t\t.withMessageContaining(\"The iss claim is not valid\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsTypicalThenReturnedDecoderValidatesIssuer() {\n\t\tprepareConfigurationResponseOidc();\n\t\tReactiveJwtDecoder decoder = ReactiveJwtDecoders.fromIssuerLocation(this.issuer);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(ISSUER_MISMATCH).block())\n\t\t\t\t.withMessageContaining(\"The iss claim is not valid\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsTypicalThenReturnedDecoderValidatesIssuer() {\n\t\tprepareConfigurationResponseOAuth2();\n\t\tReactiveJwtDecoder decoder = ReactiveJwtDecoders.fromIssuerLocation(this.issuer);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(JwtValidationException.class)\n\t\t\t\t.isThrownBy(() -> decoder.decode(ISSUER_MISMATCH).block())\n\t\t\t\t.withMessageContaining(\"The iss claim is not valid\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenResponseIsNonCompliantThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponse(\"{ \\\"missing_required_keys\\\" : \\\"and_values\\\" }\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromOidcIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsNonCompliantThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOidc(\"{ \\\"missing_required_keys\\\" : \\\"and_values\\\" }\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsNonCompliantThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOAuth2(\"{ \\\"missing_required_keys\\\" : \\\"and_values\\\" }\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {\n\t\tprepareConfigurationResponse(this.buildResponseWithMissingJwksUri());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromOidcIssuerLocation(this.issuer))\n\t\t\t\t.withMessage(\"The public JWK set URI must not be null\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {\n\t\tprepareConfigurationResponseOidc(this.buildResponseWithMissingJwksUri());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(this.issuer))\n\t\t\t\t.withMessage(\"The public JWK set URI must not be null\");\n\t\t// @formatter:on\n\t}\n\n\t// gh-7512\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {\n\t\tprepareConfigurationResponseOAuth2(this.buildResponseWithMissingJwksUri());\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(this.issuer))\n\t\t\t\t.withMessage(\"The public JWK set URI must not be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenResponseIsMalformedThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponse(\"malformed\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromOidcIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackResponseIsMalformedThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOidc(\"malformed\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2ResponseIsMalformedThenThrowsRuntimeException() {\n\t\tprepareConfigurationResponseOAuth2(\"malformed\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenRespondingIssuerMismatchesRequestedIssuerThenThrowsIllegalStateException() {\n\t\tprepareConfigurationResponse(String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer + \"/wrong\", this.issuer));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromOidcIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackRespondingIssuerMismatchesRequestedIssuerThenThrowsIllegalStateException() {\n\t\tprepareConfigurationResponseOidc(String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer + \"/wrong\", this.issuer));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOAuth2RespondingIssuerMismatchesRequestedIssuerThenThrowsIllegalStateException() {\n\t\tprepareConfigurationResponseOAuth2(\n\t\t\t\tString.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer + \"/wrong\", this.issuer));\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(this.issuer));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenRequestedIssuerIsUnresponsiveThenThrowsIllegalArgumentException() throws Exception {\n\t\tthis.server.shutdown();\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromOidcIssuerLocation(\"https://issuer\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void issuerWhenOidcFallbackRequestedIssuerIsUnresponsiveThenThrowsIllegalArgumentException()\n\t\t\tthrows Exception {\n\t\tthis.server.shutdown();\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> ReactiveJwtDecoders.fromIssuerLocation(\"https://issuer\"));\n\t\t// @formatter:on\n\t}\n\n\tprivate void prepareConfigurationResponse() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponse(body);\n\t}\n\n\tprivate void prepareConfigurationResponse(String body) {\n\t\tthis.server.enqueue(response(body));\n\t\tthis.server.enqueue(response(JWK_SET));\n\t}\n\n\tprivate void prepareConfigurationResponseOidc() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponseOidc(body);\n\t}\n\n\tprivate void prepareConfigurationResponseOidc(String body) {\n\t\tMap<String, MockResponse> responses = new HashMap<>();\n\t\tresponses.put(oidc(), response(body));\n\t\tresponses.put(jwks(), response(JWK_SET));\n\t\tprepareConfigurationResponses(responses);\n\t}\n\n\tprivate void prepareConfigurationResponseOAuth2() {\n\t\tString body = String.format(DEFAULT_RESPONSE_TEMPLATE, this.issuer, this.issuer);\n\t\tprepareConfigurationResponseOAuth2(body);\n\t}\n\n\tprivate void prepareConfigurationResponseOAuth2(String body) {\n\t\tMap<String, MockResponse> responses = new HashMap<>();\n\t\tresponses.put(oauth(), response(body));\n\t\tresponses.put(jwks(), response(JWK_SET));\n\t\tprepareConfigurationResponses(responses);\n\t}\n\n\tprivate void prepareConfigurationResponses(Map<String, MockResponse> responses) {\n\t\tDispatcher dispatcher = new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\t// @formatter:off\n\t\t\t\treturn Optional.of(request)\n\t\t\t\t\t\t.map(RecordedRequest::getRequestUrl)\n\t\t\t\t\t\t.map(HttpUrl::toString)\n\t\t\t\t\t\t.map(responses::get)\n\t\t\t\t\t\t.orElse(new MockResponse().setResponseCode(404));\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t};\n\t\tthis.server.setDispatcher(dispatcher);\n\t}\n\n\tprivate String createIssuerFromServer() {\n\t\treturn this.server.url(\"\").toString();\n\t}\n\n\tprivate String oidc() {\n\t\tURI uri = URI.create(this.issuer);\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUri(uri)\n\t\t\t\t.replacePath(uri.getPath() + OIDC_METADATA_PATH)\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate String oauth() {\n\t\tURI uri = URI.create(this.issuer);\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUri(uri)\n\t\t\t\t.replacePath(OAUTH_METADATA_PATH + uri.getPath())\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate String jwks() {\n\t\treturn this.issuer + \"/.well-known/jwks.json\";\n\t}\n\n\tprivate MockResponse response(String body) {\n\t\t// @formatter:off\n\t\treturn new MockResponse().setBody(body)\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\t// @formatter:on\n\t}\n\n\tpublic String buildResponseWithMissingJwksUri() {\n\t\tJsonMapper mapper = new JsonMapper();\n\t\tMap<String, Object> response = mapper.readValue(DEFAULT_RESPONSE_TEMPLATE,\n\t\t\t\tnew TypeReference<Map<String, Object>>() {\n\t\t\t\t});\n\t\tresponse.remove(\"jwks_uri\");\n\t\treturn mapper.writeValueAsString(response);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/ReactiveRemoteJWKSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKMatcher;\nimport com.nimbusds.jose.jwk.JWKSelector;\nimport com.nimbusds.jose.jwk.KeyType;\nimport com.nimbusds.jose.jwk.KeyUse;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willReturn;\nimport static org.mockito.BDDMockito.willThrow;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class ReactiveRemoteJWKSourceTests {\n\n\t@Mock\n\tprivate JWKMatcher matcher;\n\n\tprivate ReactiveRemoteJWKSource source;\n\n\tprivate JWKSelector selector;\n\n\tprivate MockWebServer server;\n\n\t@Mock\n\tprivate Supplier<String> mockStringSupplier;\n\n\t// @formatter:off\n\tprivate String keys = \"{\\n\"\n\t\t\t+ \"    \\\"keys\\\": [\\n\"\n\t\t\t+ \"        {\\n\"\n\t\t\t+ \"            \\\"alg\\\": \\\"RS256\\\", \\n\"\n\t\t\t+ \"            \\\"e\\\": \\\"AQAB\\\", \\n\"\n\t\t\t+ \"            \\\"kid\\\": \\\"1923397381d9574bb873202a90c32b7ceeaed027\\\", \\n\"\n\t\t\t+ \"            \\\"kty\\\": \\\"RSA\\\", \\n\"\n\t\t\t+ \"            \\\"n\\\": \\\"m4I5Dk5GnbzzUtqaljDVbpMONi1JLNJ8ZuXE8VvjCAVebDg5vTYhQ33jUwGgbn1wFmytUMgMmvK8A8Gpshl0sO2GBIZoh6_pwLrk657ZEtv-hx9fYKnzwyrfHqxtSswMAyr7XtKl8Ha1I03uFMSaYaaBTwVXCHByhzr4PVXfKAYJNbbcteUZfE8ODlBQkjQLI0IB78Nu8XIRrdzTF_5LCuM6rLUNtX6_KdzPpeX9KEtB7OBAfkdZEtBzGI-aYNLtIaL4qO6cVxBeVDLMoj9kVsRPylrwhEFQcGOjtJhwJwXFzTMZVhkiLFCHxZkkjoMrK5osSRlhduuGI9ot8XTUKQ\\\", \\n\"\n\t\t\t+ \"            \\\"use\\\": \\\"sig\\\"\\n\"\n\t\t\t+ \"        }, \\n\"\n\t\t\t+ \"        {\\n\"\n\t\t\t+ \"            \\\"alg\\\": \\\"RS256\\\", \\n\"\n\t\t\t+ \"            \\\"e\\\": \\\"AQAB\\\", \\n\"\n\t\t\t+ \"            \\\"kid\\\": \\\"7ddf54d3032d1f0d48c3618892ca74c1ac30ad77\\\", \\n\"\n\t\t\t+ \"            \\\"kty\\\": \\\"RSA\\\", \\n\"\n\t\t\t+ \"            \\\"n\\\": \\\"yLlYyux949b7qS-DdqTNjdZb4NtqiNH-Jt7DtRxmfW9XZLOQ6Q2NYgmPe9hyy5GHG7W3zsd6Q-rzq5eGRNEUx1767K1dS5PtkVWPiPG_M7rDqCu3HsLmKQKhRjHYaCWl5NuiMB5mXoPhSwrHd2yeGE7QHIV7_CiQFc1xQsXeiC-nTeJohJO3HI97w0GXE8pHspLYq9oG87f5IHxFr89abmwRug-D7QWQyW5b4doe4ZL-52J-8WHd52kGrGfu4QyV83oAad3I_9Q-yiWOXUr_0GIrzz4_-u5HgqYexnodFhZZSaKuRSg_b5qCnPhW8gBDLAHkmQzQMaWsN14L0pokbQ\\\", \\n\"\n\t\t\t+ \"            \\\"use\\\": \\\"sig\\\"\\n\"\n\t\t\t+ \"        }\\n\"\n\t\t\t+ \"    ]\\n\"\n\t\t\t+ \"}\\n\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate String keys2 = \"{\\n\"\n\t\t\t+ \"    \\\"keys\\\": [\\n\"\n\t\t\t+ \"        {\\n\"\n\t\t\t+ \"            \\\"alg\\\": \\\"RS256\\\", \\n\"\n\t\t\t+ \"            \\\"e\\\": \\\"AQAB\\\", \\n\"\n\t\t\t+ \"            \\\"kid\\\": \\\"rotated\\\", \\n\"\n\t\t\t+ \"            \\\"kty\\\": \\\"RSA\\\", \\n\"\n\t\t\t+ \"            \\\"n\\\": \\\"m4I5Dk5GnbzzUtqaljDVbpMONi1JLNJ8ZuXE8VvjCAVebDg5vTYhQ33jUwGgbn1wFmytUMgMmvK8A8Gpshl0sO2GBIZoh6_pwLrk657ZEtv-hx9fYKnzwyrfHqxtSswMAyr7XtKl8Ha1I03uFMSaYaaBTwVXCHByhzr4PVXfKAYJNbbcteUZfE8ODlBQkjQLI0IB78Nu8XIRrdzTF_5LCuM6rLUNtX6_KdzPpeX9KEtB7OBAfkdZEtBzGI-aYNLtIaL4qO6cVxBeVDLMoj9kVsRPylrwhEFQcGOjtJhwJwXFzTMZVhkiLFCHxZkkjoMrK5osSRlhduuGI9ot8XTUKQ\\\", \\n\"\n\t\t\t+ \"            \\\"use\\\": \\\"sig\\\"\\n\"\n\t\t\t+ \"        }\\n\"\n\t\t\t+ \"    ]\\n\"\n\t\t\t+ \"}\\n\";\n\t// @formatter:on\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.server = new MockWebServer();\n\t\tthis.source = new ReactiveRemoteJWKSource(this.server.url(\"/\").toString());\n\t\tthis.server.enqueue(new MockResponse().setBody(this.keys));\n\t\tthis.selector = new JWKSelector(this.matcher);\n\t}\n\n\t@Test\n\tpublic void getWhenMultipleRequestThenCached() {\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tthis.source.get(this.selector).block();\n\t\tthis.source.get(this.selector).block();\n\t\tassertThat(this.server.getRequestCount()).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void getWhenMatchThenCreatesKeys() {\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tList<JWK> keys = this.source.get(this.selector).block();\n\t\tassertThat(keys).hasSize(2);\n\t\tJWK key1 = keys.get(0);\n\t\tassertThat(key1.getKeyID()).isEqualTo(\"1923397381d9574bb873202a90c32b7ceeaed027\");\n\t\tassertThat(key1.getAlgorithm().getName()).isEqualTo(\"RS256\");\n\t\tassertThat(key1.getKeyType()).isEqualTo(KeyType.RSA);\n\t\tassertThat(key1.getKeyUse()).isEqualTo(KeyUse.SIGNATURE);\n\t\tJWK key2 = keys.get(1);\n\t\tassertThat(key2.getKeyID()).isEqualTo(\"7ddf54d3032d1f0d48c3618892ca74c1ac30ad77\");\n\t\tassertThat(key2.getAlgorithm().getName()).isEqualTo(\"RS256\");\n\t\tassertThat(key2.getKeyType()).isEqualTo(KeyType.RSA);\n\t\tassertThat(key2.getKeyUse()).isEqualTo(KeyUse.SIGNATURE);\n\t}\n\n\t@Test\n\tpublic void getWhenNoMatchAndNoKeyIdThenEmpty() {\n\t\tgiven(this.matcher.matches(any())).willReturn(false);\n\t\tgiven(this.matcher.getKeyIDs()).willReturn(Collections.emptySet());\n\t\tassertThat(this.source.get(this.selector).block()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getWhenNoMatchAndKeyIdNotMatchThenRefreshAndFoundThenFound() {\n\t\tthis.server.enqueue(new MockResponse().setBody(this.keys2));\n\t\tgiven(this.matcher.matches(any())).willReturn(false, false, true);\n\t\tgiven(this.matcher.getKeyIDs()).willReturn(Collections.singleton(\"rotated\"));\n\t\tList<JWK> keys = this.source.get(this.selector).block();\n\t\tassertThat(keys).hasSize(1);\n\t\tassertThat(keys.get(0).getKeyID()).isEqualTo(\"rotated\");\n\t}\n\n\t@Test\n\tpublic void getWhenNoMatchAndKeyIdNotMatchThenRefreshAndNotFoundThenEmpty() {\n\t\tthis.server.enqueue(new MockResponse().setBody(this.keys2));\n\t\tgiven(this.matcher.matches(any())).willReturn(false, false, false);\n\t\tgiven(this.matcher.getKeyIDs()).willReturn(Collections.singleton(\"rotated\"));\n\t\tList<JWK> keys = this.source.get(this.selector).block();\n\t\tassertThat(keys).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getWhenNoMatchAndKeyIdMatchThenEmpty() {\n\t\tgiven(this.matcher.matches(any())).willReturn(false);\n\t\tgiven(this.matcher.getKeyIDs()).willReturn(Collections.singleton(\"7ddf54d3032d1f0d48c3618892ca74c1ac30ad77\"));\n\t\tassertThat(this.source.get(this.selector).block()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getShouldRecoverAndReturnKeysAfterErrorCase() {\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tthis.source = new ReactiveRemoteJWKSource(Mono.fromSupplier(this.mockStringSupplier));\n\t\twillThrow(WebClientResponseException.ServiceUnavailable.class).given(this.mockStringSupplier).get();\n\t\t// first case: id provider has error state\n\t\tassertThatExceptionOfType(WebClientResponseException.ServiceUnavailable.class)\n\t\t\t.isThrownBy(() -> this.source.get(this.selector).block());\n\t\t// second case: id provider is healthy again\n\t\twillReturn(this.server.url(\"/\").toString()).given(this.mockStringSupplier).get();\n\t\tList<JWK> actual = this.source.get(this.selector).block();\n\t\tassertThat(actual).isNotEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/SupplierJwtDecoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link SupplierJwtDecoder}\n *\n * @author Josh Cummings\n */\npublic class SupplierJwtDecoderTests {\n\n\t@Test\n\tpublic void decodeWhenUninitializedThenSupplierInitializes() {\n\t\tJwtDecoder jwtDecoder = mock(JwtDecoder.class);\n\t\tSupplierJwtDecoder supplierJwtDecoder = new SupplierJwtDecoder(() -> jwtDecoder);\n\t\tsupplierJwtDecoder.decode(\"token\");\n\t\tverify(jwtDecoder).decode(\"token\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenInitializationFailsThenInitializationException() {\n\t\tSupplier<JwtDecoder> broken = mock(Supplier.class);\n\t\tgiven(broken.get()).willThrow(RuntimeException.class);\n\t\tJwtDecoder jwtDecoder = new SupplierJwtDecoder(broken);\n\t\tassertThatExceptionOfType(JwtDecoderInitializationException.class).isThrownBy(() -> jwtDecoder.decode(\"token\"));\n\t\tverify(broken).get();\n\t}\n\n\t@Test\n\tpublic void decodeWhenInitializedThenCaches() {\n\t\tJwtDecoder jwtDecoder = mock(JwtDecoder.class);\n\t\tSupplier<JwtDecoder> supplier = mock(Supplier.class);\n\t\tgiven(supplier.get()).willReturn(jwtDecoder);\n\t\tJwtDecoder supplierJwtDecoder = new SupplierJwtDecoder(supplier);\n\t\tsupplierJwtDecoder.decode(\"token\");\n\t\tsupplierJwtDecoder.decode(\"token\");\n\t\tverify(supplier, times(1)).get();\n\t\tverify(jwtDecoder, times(2)).decode(\"token\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenInitializationInitiallyFailsThenRecoverable() {\n\t\tJwtDecoder jwtDecoder = mock(JwtDecoder.class);\n\t\tSupplier<JwtDecoder> broken = mock(Supplier.class);\n\t\tgiven(broken.get()).willThrow(RuntimeException.class);\n\t\tJwtDecoder supplierJwtDecoder = new SupplierJwtDecoder(broken);\n\t\tassertThatExceptionOfType(JwtDecoderInitializationException.class)\n\t\t\t.isThrownBy(() -> supplierJwtDecoder.decode(\"token\"));\n\t\treset(broken);\n\t\tgiven(broken.get()).willReturn(jwtDecoder);\n\t\tsupplierJwtDecoder.decode(\"token\");\n\t\tverify(jwtDecoder).decode(\"token\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/SupplierReactiveJwtDecoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link SupplierReactiveJwtDecoder}\n */\npublic class SupplierReactiveJwtDecoderTests {\n\n\t@Test\n\tpublic void decodeWhenUninitializedThenSupplierInitializes() {\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tgiven(jwtDecoder.decode(\"token\")).willReturn(Mono.empty());\n\t\tSupplierReactiveJwtDecoder supplierReactiveJwtDecoder = new SupplierReactiveJwtDecoder(() -> jwtDecoder);\n\t\tsupplierReactiveJwtDecoder.decode(\"token\").block();\n\t\tverify(jwtDecoder).decode(\"token\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenInitializationFailsThenInitializationException() {\n\t\tSupplier<ReactiveJwtDecoder> broken = mock(Supplier.class);\n\t\tgiven(broken.get()).willThrow(RuntimeException.class);\n\t\tReactiveJwtDecoder jwtDecoder = new SupplierReactiveJwtDecoder(broken);\n\t\tassertThatExceptionOfType(JwtDecoderInitializationException.class)\n\t\t\t.isThrownBy(() -> jwtDecoder.decode(\"token\").block());\n\t\tverify(broken).get();\n\t}\n\n\t@Test\n\tpublic void decodeWhenInitializedThenCaches() {\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tSupplier<ReactiveJwtDecoder> supplier = mock(Supplier.class);\n\t\tgiven(supplier.get()).willReturn(jwtDecoder);\n\t\tgiven(jwtDecoder.decode(\"token\")).willReturn(Mono.empty());\n\t\tReactiveJwtDecoder supplierReactiveJwtDecoder = new SupplierReactiveJwtDecoder(supplier);\n\t\tsupplierReactiveJwtDecoder.decode(\"token\").block();\n\t\tsupplierReactiveJwtDecoder.decode(\"token\").block();\n\t\tverify(supplier, times(1)).get();\n\t\tverify(jwtDecoder, times(2)).decode(\"token\");\n\t}\n\n\t@Test\n\tpublic void decodeWhenInitializationInitiallyFailsThenRecoverable() {\n\t\tReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);\n\t\tSupplier<ReactiveJwtDecoder> broken = mock(Supplier.class);\n\t\tgiven(broken.get()).willThrow(RuntimeException.class);\n\t\tgiven(jwtDecoder.decode(\"token\")).willReturn(Mono.empty());\n\t\tReactiveJwtDecoder supplierReactiveJwtDecoder = new SupplierReactiveJwtDecoder(broken);\n\t\tassertThatExceptionOfType(JwtDecoderInitializationException.class)\n\t\t\t.isThrownBy(() -> supplierReactiveJwtDecoder.decode(\"token\").block());\n\t\treset(broken);\n\t\tgiven(broken.get()).willReturn(jwtDecoder);\n\t\tsupplierReactiveJwtDecoder.decode(\"token\").block();\n\t\tverify(jwtDecoder).decode(\"token\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/TestJwsHeaders.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\n\n/**\n * @author Joe Grandja\n */\npublic final class TestJwsHeaders {\n\n\tprivate TestJwsHeaders() {\n\t}\n\n\tpublic static JwsHeader.Builder jwsHeader() {\n\t\treturn jwsHeader(SignatureAlgorithm.RS256);\n\t}\n\n\tpublic static JwsHeader.Builder jwsHeader(SignatureAlgorithm signatureAlgorithm) {\n\t\t// @formatter:off\n\t\treturn JwsHeader.with(signatureAlgorithm)\n\t\t\t\t.jwkSetUrl(\"https://provider.com/oauth2/jwks\")\n\t\t\t\t.jwk(rsaJwk())\n\t\t\t\t.keyId(\"keyId\")\n\t\t\t\t.x509Url(\"https://provider.com/oauth2/x509\")\n\t\t\t\t.x509CertificateChain(Arrays.asList(\"x509Cert1\", \"x509Cert2\"))\n\t\t\t\t.x509SHA1Thumbprint(\"x509SHA1Thumbprint\")\n\t\t\t\t.x509SHA256Thumbprint(\"x509SHA256Thumbprint\")\n\t\t\t\t.type(\"JWT\")\n\t\t\t\t.contentType(\"jwt-content-type\")\n\t\t\t\t.header(\"custom-header-name\", \"custom-header-value\");\n\t\t// @formatter:on\n\t}\n\n\tprivate static Map<String, Object> rsaJwk() {\n\t\tMap<String, Object> rsaJwk = new HashMap<>();\n\t\trsaJwk.put(\"kty\", \"RSA\");\n\t\trsaJwk.put(\"n\", \"modulus\");\n\t\trsaJwk.put(\"e\", \"exponent\");\n\t\treturn rsaJwk;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/TestJwtClaimsSets.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\n\n/**\n * @author Joe Grandja\n */\npublic final class TestJwtClaimsSets {\n\n\tprivate TestJwtClaimsSets() {\n\t}\n\n\tpublic static JwtClaimsSet.Builder jwtClaimsSet() {\n\t\tString issuer = \"https://provider.com\";\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);\n\n\t\t// @formatter:off\n\t\treturn JwtClaimsSet.builder()\n\t\t\t\t.issuer(issuer)\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.audience(Collections.singletonList(\"client-1\"))\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.notBefore(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.id(\"jti\")\n\t\t\t\t.claim(\"custom-claim-name\", \"custom-claim-value\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/TestJwts.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.time.Instant;\nimport java.util.Arrays;\n\npublic final class TestJwts {\n\n\tprivate TestJwts() {\n\t}\n\n\tpublic static Jwt.Builder jwt() {\n\t\t// @formatter:off\n\t\treturn Jwt.withTokenValue(\"token\")\n\t\t\t\t.header(\"alg\", \"none\")\n\t\t\t\t.audience(Arrays.asList(\"https://audience.example.org\"))\n\t\t\t\t.expiresAt(Instant.MAX)\n\t\t\t\t.issuedAt(Instant.MIN)\n\t\t\t\t.issuer(\"https://issuer.example.org\")\n\t\t\t\t.jti(\"jti\")\n\t\t\t\t.notBefore(Instant.MIN)\n\t\t\t\t.subject(\"mock-test-subject\");\n\t\t// @formatter:on\n\t}\n\n\tpublic static Jwt user() {\n\t\t// @formatter:off\n\t\treturn jwt()\n\t\t\t\t.claim(\"sub\", \"mock-test-subject\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/X509CertificateThumbprintValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.jwt;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.jose.TestX509Certificates;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Joe Grandja\n * @since 6.3\n */\nclass X509CertificateThumbprintValidatorTests {\n\n\tprivate final X509CertificateThumbprintValidator validator = new X509CertificateThumbprintValidator(\n\t\t\tX509CertificateThumbprintValidator.DEFAULT_X509_CERTIFICATE_SUPPLIER);\n\n\t@AfterEach\n\tvoid cleanup() {\n\t\tRequestContextHolder.resetRequestAttributes();\n\t}\n\n\t@Test\n\tvoid constructorWhenX509CertificateSupplierNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new X509CertificateThumbprintValidator(null)).withMessage(\"x509CertificateSupplier cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid validateWhenCnfClaimNotAvailableThenSuccess() {\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tassertThat(this.validator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tvoid validateWhenX5tClaimNotAvailableThenSuccess() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"cnf\", Collections.emptyMap())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tassertThat(this.validator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tvoid validateWhenX509CertificateMissingThenHasErrors() throws Exception {\n\t\tString sha256Thumbprint = X509CertificateThumbprintValidator\n\t\t\t.computeSHA256Thumbprint(TestX509Certificates.DEFAULT_PKI_CERTIFICATE[0]);\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"cnf\", Collections.singletonMap(\"x5t#S256\", sha256Thumbprint))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tassertThat(this.validator.validate(jwt).getErrors())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.first()\n\t\t\t\t.satisfies((error) -> {\n\t\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\t\tassertThat(error.getDescription()).isEqualTo(\"Unable to obtain X509Certificate from current request.\");\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid validateWhenX509CertificateThumbprintInvalidThenHasErrors() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"jakarta.servlet.request.X509Certificate\",\n\t\t\t\tTestX509Certificates.DEFAULT_SELF_SIGNED_CERTIFICATE);\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, null));\n\n\t\tString sha256Thumbprint = X509CertificateThumbprintValidator\n\t\t\t.computeSHA256Thumbprint(TestX509Certificates.DEFAULT_PKI_CERTIFICATE[0]);\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"cnf\", Collections.singletonMap(\"x5t#S256\", sha256Thumbprint))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tassertThat(this.validator.validate(jwt).getErrors())\n\t\t\t\t.hasSize(1)\n\t\t\t\t.first()\n\t\t\t\t.satisfies((error) -> {\n\t\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\t\tassertThat(error.getDescription()).isEqualTo(\"Invalid SHA-256 Thumbprint for X509Certificate.\");\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid validateWhenX509CertificateThumbprintValidThenSuccess() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"jakarta.servlet.request.X509Certificate\", TestX509Certificates.DEFAULT_PKI_CERTIFICATE);\n\t\tRequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, null));\n\n\t\tString sha256Thumbprint = X509CertificateThumbprintValidator\n\t\t\t.computeSHA256Thumbprint(TestX509Certificates.DEFAULT_PKI_CERTIFICATE[0]);\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"cnf\", Collections.singletonMap(\"x5t#S256\", sha256Thumbprint))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertThat(this.validator.validate(jwt).hasErrors()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/spring-security-oauth2-resource-server.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi project(':spring-security-oauth2-core')\n\tapi project(':spring-security-web')\n\tapi 'org.springframework:spring-core'\n\n\toptional project(':spring-security-oauth2-jose')\n\toptional 'com.nimbusds:oauth2-oidc-sdk'\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'org.springframework:spring-webflux'\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(path : ':spring-security-core', configuration : 'tests')\n\ttestImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests')\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\ttestImplementation 'tools.jackson.core:jackson-databind'\n\ttestImplementation 'io.projectreactor.netty:reactor-netty'\n\ttestImplementation 'io.projectreactor:reactor-test'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/BearerTokenError.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.util.Assert;\n\n/**\n * A representation of a\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-3.1\" target=\"_blank\">Bearer Token\n * Error</a>.\n *\n * @author Vedran Pavic\n * @author Josh Cummings\n * @since 5.1\n * @see BearerTokenErrorCodes\n * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-3\" target=\"_blank\">RFC 6750\n * Section 3: The WWW-Authenticate Response Header Field</a>\n */\npublic final class BearerTokenError extends OAuth2Error {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 4521118368930341766L;\n\n\tprivate final HttpStatus httpStatus;\n\n\tprivate final @Nullable String scope;\n\n\t/**\n\t * Create a {@code BearerTokenError} using the provided parameters\n\t * @param errorCode the error code\n\t * @param httpStatus the HTTP status\n\t */\n\tpublic BearerTokenError(String errorCode, HttpStatus httpStatus, @Nullable String description,\n\t\t\t@Nullable String errorUri) {\n\t\tthis(errorCode, httpStatus, description, errorUri, null);\n\t}\n\n\t/**\n\t * Create a {@code BearerTokenError} using the provided parameters\n\t * @param errorCode the error code\n\t * @param httpStatus the HTTP status\n\t * @param description the description\n\t * @param errorUri the URI\n\t * @param scope the scope\n\t */\n\tpublic BearerTokenError(String errorCode, HttpStatus httpStatus, @Nullable String description,\n\t\t\t@Nullable String errorUri, @Nullable String scope) {\n\t\tsuper(errorCode, description, errorUri);\n\t\tAssert.notNull(httpStatus, \"httpStatus cannot be null\");\n\t\tAssert.isTrue(isDescriptionValid(description),\n\t\t\t\t\"description contains invalid ASCII characters, it must conform to RFC 6750\");\n\t\tAssert.isTrue(isErrorCodeValid(errorCode),\n\t\t\t\t\"errorCode contains invalid ASCII characters, it must conform to RFC 6750\");\n\t\tAssert.isTrue(isErrorUriValid(errorUri),\n\t\t\t\t\"errorUri contains invalid ASCII characters, it must conform to RFC 6750\");\n\t\tAssert.isTrue(isScopeValid(scope), \"scope contains invalid ASCII characters, it must conform to RFC 6750\");\n\t\tthis.httpStatus = httpStatus;\n\t\tthis.scope = scope;\n\t}\n\n\t/**\n\t * Return the HTTP status.\n\t * @return the HTTP status\n\t */\n\tpublic HttpStatus getHttpStatus() {\n\t\treturn this.httpStatus;\n\t}\n\n\t/**\n\t * Return the scope.\n\t * @return the scope, or {@code null} if not set\n\t */\n\tpublic @Nullable String getScope() {\n\t\treturn this.scope;\n\t}\n\n\tprivate static boolean isDescriptionValid(@Nullable String description) {\n\t\t// @formatter:off\n\t\treturn description == null || description.chars().allMatch((c) ->\n\t\t\t\twithinTheRangeOf(c, 0x20, 0x21) ||\n\t\t\t\twithinTheRangeOf(c, 0x23, 0x5B) ||\n\t\t\t\twithinTheRangeOf(c, 0x5D, 0x7E));\n\t\t// @formatter:on\n\t}\n\n\tprivate static boolean isErrorCodeValid(String errorCode) {\n\t\t// @formatter:off\n\t\treturn errorCode.chars().allMatch((c) ->\n\t\t\t\twithinTheRangeOf(c, 0x20, 0x21) ||\n\t\t\t\twithinTheRangeOf(c, 0x23, 0x5B) ||\n\t\t\t\twithinTheRangeOf(c, 0x5D, 0x7E));\n\t\t// @formatter:on\n\t}\n\n\tprivate static boolean isErrorUriValid(@Nullable String errorUri) {\n\t\treturn errorUri == null || errorUri.chars()\n\t\t\t.allMatch((c) -> c == 0x21 || withinTheRangeOf(c, 0x23, 0x5B) || withinTheRangeOf(c, 0x5D, 0x7E));\n\t}\n\n\tprivate static boolean isScopeValid(@Nullable String scope) {\n\t\t// @formatter:off\n\t\treturn scope == null || scope.chars().allMatch((c) ->\n\t\t\t\twithinTheRangeOf(c, 0x20, 0x21) ||\n\t\t\t\twithinTheRangeOf(c, 0x23, 0x5B) ||\n\t\t\t\twithinTheRangeOf(c, 0x5D, 0x7E));\n\t\t// @formatter:on\n\t}\n\n\tprivate static boolean withinTheRangeOf(int c, int min, int max) {\n\t\treturn c >= min && c <= max;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/BearerTokenErrorCodes.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\n/**\n * Standard error codes defined by the OAuth 2.0 Authorization Framework: Bearer Token\n * Usage.\n *\n * @author Vedran Pavic\n * @since 5.1\n * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-3.1\" target=\"_blank\">RFC 6750\n * Section 3.1: Error Codes</a>\n */\npublic final class BearerTokenErrorCodes {\n\n\t/**\n\t * {@code invalid_request} - The request is missing a required parameter, includes an\n\t * unsupported parameter or parameter value, repeats the same parameter, uses more\n\t * than one method for including an access token, or is otherwise malformed.\n\t */\n\tpublic static final String INVALID_REQUEST = \"invalid_request\";\n\n\t/**\n\t * {@code invalid_token} - The access token provided is expired, revoked, malformed,\n\t * or invalid for other reasons.\n\t */\n\tpublic static final String INVALID_TOKEN = \"invalid_token\";\n\n\t/**\n\t * {@code insufficient_scope} - The request requires higher privileges than provided\n\t * by the access token.\n\t */\n\tpublic static final String INSUFFICIENT_SCOPE = \"insufficient_scope\";\n\n\tprivate BearerTokenErrorCodes() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/BearerTokenErrors.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpStatus;\n\n/**\n * A factory for creating {@link BearerTokenError} instances that correspond to the\n * registered <a href=\"https://tools.ietf.org/html/rfc6750#section-3.1\">Bearer Token Error\n * Codes</a>.\n *\n * @author Josh Cummings\n * @since 5.3\n */\npublic final class BearerTokenErrors {\n\n\tprivate static final BearerTokenError DEFAULT_INVALID_REQUEST = invalidRequest(\"Invalid request\");\n\n\tprivate static final BearerTokenError DEFAULT_INVALID_TOKEN = invalidToken(\"Invalid token\");\n\n\tprivate static final BearerTokenError DEFAULT_INSUFFICIENT_SCOPE = insufficientScope(\"Insufficient scope\", null);\n\n\tprivate static final String DEFAULT_URI = \"https://tools.ietf.org/html/rfc6750#section-3.1\";\n\n\tprivate BearerTokenErrors() {\n\t}\n\n\t/**\n\t * Create a {@link BearerTokenError} caused by an invalid request\n\t * @param message a description of the error\n\t * @return a {@link BearerTokenError}\n\t */\n\tpublic static BearerTokenError invalidRequest(String message) {\n\t\ttry {\n\t\t\treturn new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, message,\n\t\t\t\t\tDEFAULT_URI);\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\t// some third-party library error messages are not suitable for RFC 6750's\n\t\t\t// error message charset\n\t\t\treturn DEFAULT_INVALID_REQUEST;\n\t\t}\n\t}\n\n\t/**\n\t * Create a {@link BearerTokenError} caused by an invalid token\n\t * @param message a description of the error\n\t * @return a {@link BearerTokenError}\n\t */\n\tpublic static BearerTokenError invalidToken(String message) {\n\t\ttry {\n\t\t\treturn new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED, message,\n\t\t\t\t\tDEFAULT_URI);\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\t// some third-party library error messages are not suitable for RFC 6750's\n\t\t\t// error message charset\n\t\t\treturn DEFAULT_INVALID_TOKEN;\n\t\t}\n\t}\n\n\t/**\n\t * Create a {@link BearerTokenError} caused by an invalid token\n\t * @param scope the scope attribute to use in the error\n\t * @return a {@link BearerTokenError}\n\t */\n\tpublic static BearerTokenError insufficientScope(String message, @Nullable String scope) {\n\t\ttry {\n\t\t\treturn new BearerTokenError(BearerTokenErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN, message,\n\t\t\t\t\tDEFAULT_URI, scope);\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\t// some third-party library error messages are not suitable for RFC 6750's\n\t\t\t// error message charset\n\t\t\treturn DEFAULT_INSUFFICIENT_SCOPE;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/InvalidBearerTokenException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\n\n/**\n * An {@link OAuth2AuthenticationException} that indicates an invalid bearer token.\n *\n * @author Josh Cummings\n * @since 5.3\n */\npublic class InvalidBearerTokenException extends OAuth2AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 6904689954809100280L;\n\n\t/**\n\t * Construct an instance of {@link InvalidBearerTokenException} given the provided\n\t * description.\n\t *\n\t * The description will be wrapped into an\n\t * {@link org.springframework.security.oauth2.core.OAuth2Error} instance as the\n\t * {@code error_description}.\n\t * @param description the description\n\t */\n\tpublic InvalidBearerTokenException(String description) {\n\t\tsuper(BearerTokenErrors.invalidToken(description));\n\t}\n\n\t/**\n\t * Construct an instance of {@link InvalidBearerTokenException} given the provided\n\t * description and cause\n\t *\n\t * The description will be wrapped into an\n\t * {@link org.springframework.security.oauth2.core.OAuth2Error} instance as the\n\t * {@code error_description}.\n\t * @param description the description\n\t * @param cause the causing exception, or {@code null}\n\t */\n\tpublic InvalidBearerTokenException(String description, @Nullable Throwable cause) {\n\t\tsuper(BearerTokenErrors.invalidToken(description));\n\t\tif (cause != null) {\n\t\t\tinitCause(cause);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/OAuth2ProtectedResourceMetadata.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A representation of an OAuth 2.0 Protected Resource Metadata response, which is\n * returned from an OAuth 2.0 Resource Server's Metadata Endpoint, and contains a set of\n * claims about the Resource Server's configuration. The claims are defined by the OAuth\n * 2.0 Protected Resource Metadata specification (RFC 9728).\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2ProtectedResourceMetadataClaimAccessor\n * @see <a target=\"_blank\" href=\"https://www.rfc-editor.org/rfc/rfc9728.html#section-2\">2.\n * Protected Resource Metadata</a>\n */\npublic final class OAuth2ProtectedResourceMetadata\n\t\timplements OAuth2ProtectedResourceMetadataClaimAccessor, Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -18589911827039000L;\n\n\tprivate final Map<String, Object> claims;\n\n\tprivate OAuth2ProtectedResourceMetadata(Map<String, Object> claims) {\n\t\tAssert.notEmpty(claims, \"claims cannot be empty\");\n\t\tthis.claims = Collections.unmodifiableMap(new LinkedHashMap<>(claims));\n\t}\n\n\t/**\n\t * Returns the metadata as claims.\n\t * @return a {@code Map} of the metadata as claims\n\t */\n\tpublic Map<String, Object> getClaims() {\n\t\treturn this.claims;\n\t}\n\n\t/**\n\t * Constructs a new {@link Builder} with empty claims.\n\t * @return the {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Helps configure an {@link OAuth2ProtectedResourceMetadata}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final Map<String, Object> claims = new LinkedHashMap<>();\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the resource identifier for the protected resource, REQUIRED.\n\t\t * @param resource the resource identifier {@code URL} for the protected resource\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder resource(String resource) {\n\t\t\treturn claim(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE, resource);\n\t\t}\n\n\t\t/**\n\t\t * Add the issuer identifier for an authorization server, OPTIONAL.\n\t\t * @param authorizationServer the issuer identifier {@code URL} for an\n\t\t * authorization server\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationServer(String authorizationServer) {\n\t\t\taddClaimToClaimList(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS, authorizationServer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the issuer identifier values for the authorization\n\t\t * servers, allowing the ability to add, replace, or remove, OPTIONAL.\n\t\t * @param authorizationServersConsumer a {@code Consumer} of the issuer identifier\n\t\t * values for the authorization servers\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder authorizationServers(Consumer<List<String>> authorizationServersConsumer) {\n\t\t\tacceptClaimValues(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS,\n\t\t\t\t\tauthorizationServersConsumer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add a {@code scope} supported in authorization requests to the protected\n\t\t * resource, RECOMMENDED.\n\t\t * @param scope a {@code scope} supported in authorization requests to the\n\t\t * protected resource\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder scope(String scope) {\n\t\t\taddClaimToClaimList(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED, scope);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the {@code scope} values supported in authorization\n\t\t * requests to the protected resource, allowing the ability to add, replace, or\n\t\t * remove, RECOMMENDED.\n\t\t * @param scopesConsumer a {@code Consumer} of the {@code scope} values supported\n\t\t * in authorization requests to the protected resource\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder scopes(Consumer<List<String>> scopesConsumer) {\n\t\t\tacceptClaimValues(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED, scopesConsumer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add a supported method for sending an OAuth 2.0 bearer token to the protected\n\t\t * resource, OPTIONAL. Defined values are \"header\", \"body\" and \"query\".\n\t\t * @param bearerMethod a supported method for sending an OAuth 2.0 bearer token to\n\t\t * the protected resource\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder bearerMethod(String bearerMethod) {\n\t\t\taddClaimToClaimList(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED, bearerMethod);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * A {@code Consumer} of the supported methods for sending an OAuth 2.0 bearer\n\t\t * token to the protected resource, allowing the ability to add, replace, or\n\t\t * remove, OPTIONAL.\n\t\t * @param bearerMethodsConsumer a {@code Consumer} of the supported methods for\n\t\t * sending an OAuth 2.0 bearer token to the protected resource\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder bearerMethods(Consumer<List<String>> bearerMethodsConsumer) {\n\t\t\tacceptClaimValues(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED,\n\t\t\t\t\tbearerMethodsConsumer);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the name of the protected resource intended for display to the end user,\n\t\t * RECOMMENDED.\n\t\t * @param resourceName the name of the protected resource intended for display to\n\t\t * the end user\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder resourceName(String resourceName) {\n\t\t\treturn claim(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE_NAME, resourceName);\n\t\t}\n\n\t\t/**\n\t\t * Set to {@code true} to indicate protected resource support for mutual-TLS\n\t\t * client certificate-bound access tokens, OPTIONAL.\n\t\t * @param tlsClientCertificateBoundAccessTokens {@code true} to indicate protected\n\t\t * resource support for mutual-TLS client certificate-bound access tokens\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder tlsClientCertificateBoundAccessTokens(boolean tlsClientCertificateBoundAccessTokens) {\n\t\t\treturn claim(OAuth2ProtectedResourceMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS,\n\t\t\t\t\ttlsClientCertificateBoundAccessTokens);\n\t\t}\n\n\t\t/**\n\t\t * Sets the claim.\n\t\t * @param name the claim name\n\t\t * @param value the claim value\n\t\t * @return the {@link Builder} for further configuration\n\t\t */\n\t\tpublic Builder claim(String name, Object value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides access to every {@link #claim(String, Object)} declared so far\n\t\t * allowing the ability to add, replace, or remove.\n\t\t * @param claimsConsumer a {@code Consumer} of the claims\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder claims(Consumer<Map<String, Object>> claimsConsumer) {\n\t\t\tclaimsConsumer.accept(this.claims);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Validate the claims and build the {@link OAuth2ProtectedResourceMetadata}.\n\t\t * @return the {@link OAuth2ProtectedResourceMetadata}\n\t\t */\n\t\tpublic OAuth2ProtectedResourceMetadata build() {\n\t\t\tvalidate();\n\t\t\treturn new OAuth2ProtectedResourceMetadata(this.claims);\n\t\t}\n\n\t\tprivate void validate() {\n\t\t\tAssert.notNull(this.claims.get(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE),\n\t\t\t\t\t\"resource cannot be null\");\n\t\t\tvalidateURL(this.claims.get(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE),\n\t\t\t\t\t\"resource must be a valid URL\");\n\t\t\tif (this.claims.get(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tthis.claims.get(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS),\n\t\t\t\t\t\t\"authorization_servers must be of type List\");\n\t\t\t\tAssert.notEmpty(\n\t\t\t\t\t\t(List<?>) this.claims.get(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS),\n\t\t\t\t\t\t\"authorization_servers cannot be empty\");\n\t\t\t\tList<?> authorizationServers = (List<?>) this.claims\n\t\t\t\t\t.get(OAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS);\n\t\t\t\tauthorizationServers.forEach((authorizationServer) -> validateURL(authorizationServer,\n\t\t\t\t\t\t\"authorization_server must be a valid URL\"));\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tthis.claims.get(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED),\n\t\t\t\t\t\t\"scopes must be of type List\");\n\t\t\t\tAssert.notEmpty((List<?>) this.claims.get(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED),\n\t\t\t\t\t\t\"scopes cannot be empty\");\n\t\t\t}\n\t\t\tif (this.claims.get(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED) != null) {\n\t\t\t\tAssert.isInstanceOf(List.class,\n\t\t\t\t\t\tthis.claims.get(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"bearer methods must be of type List\");\n\t\t\t\tAssert.notEmpty(\n\t\t\t\t\t\t(List<?>) this.claims.get(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED),\n\t\t\t\t\t\t\"bearer methods cannot be empty\");\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void addClaimToClaimList(String name, String value) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.claims.computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\t((List<String>) this.claims.get(name)).add(value);\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate void acceptClaimValues(String name, Consumer<List<String>> valuesConsumer) {\n\t\t\tAssert.hasText(name, \"name cannot be empty\");\n\t\t\tAssert.notNull(valuesConsumer, \"valuesConsumer cannot be null\");\n\t\t\tthis.claims.computeIfAbsent(name, (k) -> new LinkedList<String>());\n\t\t\tList<String> values = (List<String>) this.claims.get(name);\n\t\t\tvaluesConsumer.accept(values);\n\t\t}\n\n\t\tprivate static void validateURL(@Nullable Object url, String errorMessage) {\n\t\t\tif (url == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (URL.class.isAssignableFrom(url.getClass())) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnew URI(url.toString()).toURL();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(errorMessage, ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/OAuth2ProtectedResourceMetadataClaimAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ClaimAccessor} for the claims a Resource Server describes about its\n * configuration, used in OAuth 2.0 Protected Resource Metadata.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see ClaimAccessor\n * @see OAuth2ProtectedResourceMetadataClaimNames\n * @see <a target=\"_blank\" href=\"https://www.rfc-editor.org/rfc/rfc9728.html#section-2\">2.\n * Protected Resource Metadata</a>\n */\npublic interface OAuth2ProtectedResourceMetadataClaimAccessor extends ClaimAccessor {\n\n\t/**\n\t * Returns the {@code URL} the protected resource asserts as its resource identifier\n\t * {@code (resource)}.\n\t * @return the {@code URL} the protected resource asserts as its resource identifier\n\t */\n\tdefault URL getResource() {\n\t\tURL resource = getClaimAsURL(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE);\n\t\tAssert.notNull(resource, \"resource cannot be null\");\n\t\treturn resource;\n\t}\n\n\t/**\n\t * Returns a list of {@code issuer} identifier {@code URL}'s, for authorization\n\t * servers that can be used with this protected resource\n\t * {@code (authorization_servers)}.\n\t * @return a list of {@code issuer} identifier {@code URL}'s, for authorization\n\t * servers that can be used with this protected resource, or an empty list if not set\n\t */\n\tdefault List<URL> getAuthorizationServers() {\n\t\tList<String> authorizationServers = getClaimAsStringList(\n\t\t\t\tOAuth2ProtectedResourceMetadataClaimNames.AUTHORIZATION_SERVERS);\n\t\tif (authorizationServers == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tList<URL> urls = new ArrayList<>();\n\t\tauthorizationServers.forEach((authorizationServer) -> {\n\t\t\ttry {\n\t\t\t\turls.add(new URI(authorizationServer).toURL());\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalArgumentException(\"Failed to convert authorization_server to URL\", ex);\n\t\t\t}\n\t\t});\n\t\treturn urls;\n\t}\n\n\t/**\n\t * Returns a list of {@code scope} values supported, that are used in authorization\n\t * requests to request access to this protected resource {@code (scopes_supported)}.\n\t * @return a list of {@code scope} values supported, or an empty list if not set\n\t */\n\tdefault List<String> getScopes() {\n\t\tList<String> scopes = getClaimAsStringList(OAuth2ProtectedResourceMetadataClaimNames.SCOPES_SUPPORTED);\n\t\treturn (scopes != null) ? scopes : Collections.emptyList();\n\t}\n\n\t/**\n\t * Returns a list of the supported methods for sending an OAuth 2.0 bearer token to\n\t * the protected resource. Defined values are \"header\", \"body\" and \"query\".\n\t * {@code (bearer_methods_supported)}.\n\t * @return a list of the supported methods for sending an OAuth 2.0 bearer token to\n\t * the protected resource, or an empty list if not set\n\t */\n\tdefault List<String> getBearerMethodsSupported() {\n\t\tList<String> methods = getClaimAsStringList(OAuth2ProtectedResourceMetadataClaimNames.BEARER_METHODS_SUPPORTED);\n\t\treturn (methods != null) ? methods : Collections.emptyList();\n\t}\n\n\t/**\n\t * Returns the name of the protected resource intended for display to the end user\n\t * {@code (resource_name)}.\n\t * @return the name of the protected resource intended for display to the end user, or\n\t * {@code null} if not set\n\t */\n\tdefault @Nullable String getResourceName() {\n\t\treturn getClaimAsString(OAuth2ProtectedResourceMetadataClaimNames.RESOURCE_NAME);\n\t}\n\n\t/**\n\t * Returns {@code true} to indicate protected resource support for mutual-TLS client\n\t * certificate-bound access tokens\n\t * {@code (tls_client_certificate_bound_access_tokens)}.\n\t * @return {@code true} to indicate protected resource support for mutual-TLS client\n\t * certificate-bound access tokens\n\t */\n\tdefault boolean isTlsClientCertificateBoundAccessTokens() {\n\t\treturn Boolean.TRUE.equals(getClaimAsBoolean(\n\t\t\t\tOAuth2ProtectedResourceMetadataClaimNames.TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/OAuth2ProtectedResourceMetadataClaimNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\n/**\n * The names of the claims a Resource Server describes about its configuration, used in\n * OAuth 2.0 Protected Resource Metadata.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see <a target=\"_blank\" href=\"https://www.rfc-editor.org/rfc/rfc9728.html#section-2\">2.\n * Protected Resource Metadata</a>\n */\npublic final class OAuth2ProtectedResourceMetadataClaimNames {\n\n\t/**\n\t * {@code resource} - the {@code URL} the protected resource asserts as its resource\n\t * identifier\n\t */\n\tpublic static final String RESOURCE = \"resource\";\n\n\t/**\n\t * {@code authorization_servers} - a list of {@code issuer} identifier {@code URL}'s,\n\t * for authorization servers that can be used with this protected resource\n\t */\n\tpublic static final String AUTHORIZATION_SERVERS = \"authorization_servers\";\n\n\t/**\n\t * {@code scopes_supported} - a list of {@code scope} values supported, that are used\n\t * in authorization requests to request access to this protected resource\n\t */\n\tpublic static final String SCOPES_SUPPORTED = \"scopes_supported\";\n\n\t/**\n\t * {@code bearer_methods_supported} - a list of the supported methods for sending an\n\t * OAuth 2.0 bearer token to the protected resource. Defined values are \"header\",\n\t * \"body\" and \"query\".\n\t */\n\tpublic static final String BEARER_METHODS_SUPPORTED = \"bearer_methods_supported\";\n\n\t/**\n\t * {@code resource_name} - the name of the protected resource intended for display to\n\t * the end user\n\t */\n\tpublic static final String RESOURCE_NAME = \"resource_name\";\n\n\t/**\n\t * {@code tls_client_certificate_bound_access_tokens} - {@code true} to indicate\n\t * protected resource support for mutual-TLS client certificate-bound access tokens\n\t */\n\tpublic static final String TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS = \"tls_client_certificate_bound_access_tokens\";\n\n\tprivate OAuth2ProtectedResourceMetadataClaimNames() {\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/AbstractOAuth2TokenAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * Base class for {@link AbstractAuthenticationToken} implementations that expose common\n * attributes between different OAuth 2.0 Access Token Formats.\n *\n * <p>\n * For example, a {@link Jwt} could expose its {@link Jwt#getClaims() claims} via\n * {@link #getTokenAttributes()} or an &quot;Introspected&quot; OAuth 2.0 Access Token\n * could expose the attributes of the Introspection Response via\n * {@link #getTokenAttributes()}.\n *\n * @author Joe Grandja\n * @since 5.1\n * @see OAuth2AccessToken\n * @see Jwt\n * @see <a target=\"_blank\" href=\"https://tools.ietf.org/search/rfc7662#section-2.2\">2.2\n * Introspection Response</a>\n */\npublic abstract class AbstractOAuth2TokenAuthenticationToken<T extends OAuth2Token>\n\t\textends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate Object principal;\n\n\tprivate Object credentials;\n\n\tprivate T token;\n\n\t/**\n\t * Sub-class constructor.\n\t */\n\tprotected AbstractOAuth2TokenAuthenticationToken(T token) {\n\t\tthis(token, null);\n\t}\n\n\t/**\n\t * Sub-class constructor.\n\t * @param authorities the authorities assigned to the Access Token, or {@code null}\n\t */\n\tprotected AbstractOAuth2TokenAuthenticationToken(T token,\n\t\t\t@Nullable Collection<? extends GrantedAuthority> authorities) {\n\t\tthis(token, token, token, authorities);\n\t}\n\n\tprotected AbstractOAuth2TokenAuthenticationToken(T token, Object principal, Object credentials,\n\t\t\t@Nullable Collection<? extends GrantedAuthority> authorities) {\n\n\t\tsuper(authorities);\n\t\tAssert.notNull(token, \"token cannot be null\");\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tthis.principal = principal;\n\t\tthis.credentials = credentials;\n\t\tthis.token = token;\n\t}\n\n\tprotected AbstractOAuth2TokenAuthenticationToken(AbstractOAuth2TokenAuthenticationBuilder<T, ?> builder) {\n\t\tsuper(builder);\n\t\tAssert.notNull(builder.credentials, \"token cannot be null\");\n\t\tAssert.notNull(builder.principal, \"principal cannot be null\");\n\t\tthis.principal = builder.principal;\n\t\tthis.credentials = builder.credentials;\n\t\tthis.token = builder.token;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\t/**\n\t * Get the token bound to this {@link Authentication}.\n\t */\n\tpublic final T getToken() {\n\t\treturn this.token;\n\t}\n\n\t/**\n\t * Returns the attributes of the access token.\n\t * @return a {@code Map} of the attributes in the access token.\n\t */\n\tpublic abstract Map<String, Object> getTokenAttributes();\n\n\t/**\n\t * A builder for {@link AbstractOAuth2TokenAuthenticationToken} implementations\n\t *\n\t * @param <B>\n\t * @since 7.0\n\t */\n\tpublic abstract static class AbstractOAuth2TokenAuthenticationBuilder<T extends OAuth2Token, B extends AbstractOAuth2TokenAuthenticationBuilder<T, B>>\n\t\t\textends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate Object principal;\n\n\t\tprivate Object credentials;\n\n\t\tprivate T token;\n\n\t\tprotected AbstractOAuth2TokenAuthenticationBuilder(AbstractOAuth2TokenAuthenticationToken<T> token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.getPrincipal();\n\t\t\tthis.credentials = token.getCredentials();\n\t\t\tthis.token = token.getToken();\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object credentials) {\n\t\t\tAssert.notNull(credentials, \"credentials cannot be null\");\n\t\t\tthis.credentials = credentials;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * The OAuth 2.0 Token to use\n\t\t * @param token the token to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic B token(T token) {\n\t\t\tAssert.notNull(token, \"token cannot be null\");\n\t\t\tthis.token = token;\n\t\t\treturn (B) this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.Transient;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link org.springframework.security.core.Authentication} token that represents a\n * successful authentication as obtained through a bearer token.\n *\n * @author Josh Cummings\n * @since 5.2\n */\n@Transient\npublic class BearerTokenAuthentication extends AbstractOAuth2TokenAuthenticationToken<OAuth2AccessToken> {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final Map<String, Object> attributes;\n\n\t/**\n\t * Constructs a {@link BearerTokenAuthentication} with the provided arguments\n\t * @param principal The OAuth 2.0 attributes\n\t * @param credentials The verified token\n\t * @param authorities The authorities associated with the given token\n\t */\n\tpublic BearerTokenAuthentication(OAuth2AuthenticatedPrincipal principal, OAuth2AccessToken credentials,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(credentials, principal, credentials, authorities);\n\t\tAssert.isTrue(credentials.getTokenType() == OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"credentials must be a bearer token\");\n\t\tthis.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(principal.getAttributes()));\n\t\tsetAuthenticated(true);\n\t}\n\n\tprotected BearerTokenAuthentication(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(builder.attributes));\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getTokenAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder preserving the concrete {@link Authentication} type\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>>\n\t\t\textends AbstractOAuth2TokenAuthenticationBuilder<OAuth2AccessToken, B> {\n\n\t\tprivate Map<String, Object> attributes;\n\n\t\tprotected Builder(BearerTokenAuthentication token) {\n\t\t\tsuper(token);\n\t\t\tthis.attributes = token.getTokenAttributes();\n\t\t}\n\n\t\t/**\n\t\t * Use this principal. Must be of type {@link OAuth2AuthenticatedPrincipal}\n\t\t * @param principal the principal to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.isInstanceOf(OAuth2AuthenticatedPrincipal.class, principal,\n\t\t\t\t\t\"principal must be of type OAuth2AuthenticatedPrincipal\");\n\t\t\tthis.attributes = ((OAuth2AuthenticatedPrincipal) principal).getAttributes();\n\t\t\treturn super.principal(principal);\n\t\t}\n\n\t\t/**\n\t\t * A synonym for {@link #token(OAuth2AccessToken)}\n\t\t * @param token the token to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object token) {\n\t\t\tAssert.isInstanceOf(OAuth2AccessToken.class, token, \"token must be of type OAuth2AccessToken\");\n\t\t\treturn token((OAuth2AccessToken) token);\n\t\t}\n\n\t\t/**\n\t\t * Use this token. Must have a {@link OAuth2AccessToken#getTokenType()} as\n\t\t * {@link OAuth2AccessToken.TokenType#BEARER}.\n\t\t * @param token the token to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\t@Override\n\t\tpublic B token(OAuth2AccessToken token) {\n\t\t\tAssert.isTrue(token.getTokenType() == OAuth2AccessToken.TokenType.BEARER, \"token must be a bearer token\");\n\t\t\tsuper.credentials(token);\n\t\t\treturn super.token(token);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic BearerTokenAuthentication build() {\n\t\t\treturn new BearerTokenAuthentication(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collections;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} that contains a\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>.\n *\n * Used by {@link BearerTokenAuthenticationFilter} to prepare an authentication attempt\n * and supported by {@link JwtAuthenticationProvider}.\n *\n * @author Josh Cummings\n * @since 5.1\n */\npublic class BearerTokenAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String token;\n\n\t/**\n\t * Create a {@code BearerTokenAuthenticationToken} using the provided parameter(s)\n\t * @param token - the bearer token\n\t */\n\tpublic BearerTokenAuthenticationToken(String token) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(token, \"token cannot be empty\");\n\t\tthis.token = token;\n\t}\n\n\t/**\n\t * Get the\n\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n\t * Token</a>\n\t * @return the token that proves the caller's authority to perform the\n\t * {@link jakarta.servlet.http.HttpServletRequest}\n\t */\n\tpublic String getToken() {\n\t\treturn this.token;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn this.getToken();\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.getToken();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidator;\nimport org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;\nimport org.springframework.security.oauth2.jwt.DPoPProofContext;\nimport org.springframework.security.oauth2.jwt.DPoPProofJwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoderFactory;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * An {@link AuthenticationProvider} implementation that is responsible for authenticating\n * a DPoP-bound access token for a protected resource request.\n *\n * @author Joe Grandja\n * @since 6.5\n * @see DPoPAuthenticationToken\n * @see DPoPProofJwtDecoderFactory\n * @see <a target=\"_blank\" href=\"https://datatracker.ietf.org/doc/html/rfc9449\">RFC 9449\n * OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>\n */\npublic final class DPoPAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate final AuthenticationManager tokenAuthenticationManager;\n\n\tprivate JwtDecoderFactory<DPoPProofContext> dPoPProofVerifierFactory;\n\n\t/**\n\t * Constructs a {@code DPoPAuthenticationProvider} using the provided parameters.\n\t * @param tokenAuthenticationManager the {@link AuthenticationManager} used to\n\t * authenticate the DPoP-bound access token\n\t */\n\tpublic DPoPAuthenticationProvider(AuthenticationManager tokenAuthenticationManager) {\n\t\tAssert.notNull(tokenAuthenticationManager, \"tokenAuthenticationManager cannot be null\");\n\t\tthis.tokenAuthenticationManager = tokenAuthenticationManager;\n\t\tFunction<DPoPProofContext, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = (context) -> {\n\t\t\tOAuth2AccessTokenClaims accessToken = context.getAccessToken();\n\t\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\t\treturn new DelegatingOAuth2TokenValidator<>(\n\t\t\t\t\t// Use default validators\n\t\t\t\t\tDPoPProofJwtDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY.apply(context),\n\t\t\t\t\t// Add custom validators\n\t\t\t\t\tnew AthClaimValidator(accessToken), new JwkThumbprintValidator(accessToken));\n\t\t};\n\t\tDPoPProofJwtDecoderFactory dPoPProofJwtDecoderFactory = new DPoPProofJwtDecoderFactory();\n\t\tdPoPProofJwtDecoderFactory.setJwtValidatorFactory(jwtValidatorFactory);\n\t\tthis.dPoPProofVerifierFactory = dPoPProofJwtDecoderFactory;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tDPoPAuthenticationToken dPoPAuthenticationToken = (DPoPAuthenticationToken) authentication;\n\n\t\tBearerTokenAuthenticationToken accessTokenAuthenticationRequest = new BearerTokenAuthenticationToken(\n\t\t\t\tdPoPAuthenticationToken.getAccessToken());\n\t\tAuthentication accessTokenAuthenticationResult = this.tokenAuthenticationManager\n\t\t\t.authenticate(accessTokenAuthenticationRequest);\n\n\t\tAbstractOAuth2TokenAuthenticationToken<OAuth2Token> accessTokenAuthentication = null;\n\t\tif (accessTokenAuthenticationResult instanceof AbstractOAuth2TokenAuthenticationToken) {\n\t\t\taccessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken) accessTokenAuthenticationResult;\n\t\t}\n\t\tif (accessTokenAuthentication == null) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN,\n\t\t\t\t\t\"Unable to authenticate the DPoP-bound access token.\", null);\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\tOAuth2AccessTokenClaims accessToken = new OAuth2AccessTokenClaims(accessTokenAuthentication.getToken(),\n\t\t\t\taccessTokenAuthentication.getTokenAttributes());\n\n\t\tDPoPProofContext dPoPProofContext = DPoPProofContext.withDPoPProof(dPoPAuthenticationToken.getDPoPProof())\n\t\t\t.accessToken(accessToken)\n\t\t\t.method(dPoPAuthenticationToken.getMethod())\n\t\t\t.targetUri(dPoPAuthenticationToken.getResourceUri())\n\t\t\t.build();\n\t\tJwtDecoder dPoPProofVerifier = this.dPoPProofVerifierFactory.createDecoder(dPoPProofContext);\n\n\t\ttry {\n\t\t\tdPoPProofVerifier.decode(dPoPProofContext.getDPoPProof());\n\t\t}\n\t\tcatch (JwtException ex) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF);\n\t\t\tthrow new OAuth2AuthenticationException(error, ex);\n\t\t}\n\n\t\treturn accessTokenAuthenticationResult;\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn DPoPAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Sets the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the\n\t * specified {@link DPoPProofContext} and is used for authenticating a DPoP Proof\n\t * {@link Jwt}. The default factory is {@link DPoPProofJwtDecoderFactory}.\n\t * @param dPoPProofVerifierFactory the {@link JwtDecoderFactory} that provides a\n\t * {@link JwtDecoder} for the specified {@link DPoPProofContext}\n\t */\n\tpublic void setDPoPProofVerifierFactory(JwtDecoderFactory<DPoPProofContext> dPoPProofVerifierFactory) {\n\t\tAssert.notNull(dPoPProofVerifierFactory, \"dPoPProofVerifierFactory cannot be null\");\n\t\tthis.dPoPProofVerifierFactory = dPoPProofVerifierFactory;\n\t}\n\n\tprivate static final class AthClaimValidator implements OAuth2TokenValidator<Jwt> {\n\n\t\tprivate final OAuth2AccessTokenClaims accessToken;\n\n\t\tprivate AthClaimValidator(OAuth2AccessTokenClaims accessToken) {\n\t\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\t\tthis.accessToken = accessToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\t\tAssert.notNull(jwt, \"DPoP proof jwt cannot be null\");\n\t\t\tString accessTokenHashClaim = jwt.getClaimAsString(\"ath\");\n\t\t\tif (!StringUtils.hasText(accessTokenHashClaim)) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"ath claim is required.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\n\t\t\tString accessTokenHash;\n\t\t\ttry {\n\t\t\t\taccessTokenHash = computeSHA256(this.accessToken.getTokenValue());\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"Failed to compute SHA-256 Thumbprint for access token.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\t\t\tif (!accessTokenHashClaim.equals(accessTokenHash)) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"ath claim is invalid.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t}\n\n\t\tprivate static OAuth2Error createOAuth2Error(String reason) {\n\t\t\treturn new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF, reason, null);\n\t\t}\n\n\t\tprivate static String computeSHA256(String value) throws Exception {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.UTF_8));\n\t\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t\t}\n\n\t}\n\n\tprivate static final class JwkThumbprintValidator implements OAuth2TokenValidator<Jwt> {\n\n\t\tprivate final OAuth2AccessTokenClaims accessToken;\n\n\t\tprivate JwkThumbprintValidator(OAuth2AccessTokenClaims accessToken) {\n\t\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\t\tthis.accessToken = accessToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\t\tAssert.notNull(jwt, \"DPoP proof jwt cannot be null\");\n\t\t\tString jwkThumbprintClaim = null;\n\t\t\tMap<String, Object> confirmationMethodClaim = this.accessToken.getClaimAsMap(\"cnf\");\n\t\t\tif (!CollectionUtils.isEmpty(confirmationMethodClaim) && confirmationMethodClaim.containsKey(\"jkt\")) {\n\t\t\t\tjwkThumbprintClaim = (String) confirmationMethodClaim.get(\"jkt\");\n\t\t\t}\n\t\t\tif (jwkThumbprintClaim == null) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"jkt claim is required.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\n\t\t\tJWK jwk = null;\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tMap<String, Object> jwkJson = (Map<String, Object>) jwt.getHeaders().get(\"jwk\");\n\t\t\ttry {\n\t\t\t\tjwk = JWK.parse(jwkJson);\n\t\t\t}\n\t\t\tcatch (Exception ignored) {\n\t\t\t}\n\t\t\tif (jwk == null) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"jwk header is missing or invalid.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\n\t\t\tString jwkThumbprint;\n\t\t\ttry {\n\t\t\t\tjwkThumbprint = jwk.computeThumbprint().toString();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"Failed to compute SHA-256 Thumbprint for jwk.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\n\t\t\tif (!jwkThumbprintClaim.equals(jwkThumbprint)) {\n\t\t\t\tOAuth2Error error = createOAuth2Error(\"jkt claim is invalid.\");\n\t\t\t\treturn OAuth2TokenValidatorResult.failure(error);\n\t\t\t}\n\t\t\treturn OAuth2TokenValidatorResult.success();\n\t\t}\n\n\t\tprivate static OAuth2Error createOAuth2Error(String reason) {\n\t\t\treturn new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF, reason, null);\n\t\t}\n\n\t}\n\n\tprivate static final class OAuth2AccessTokenClaims implements OAuth2Token, ClaimAccessor {\n\n\t\tprivate final OAuth2Token accessToken;\n\n\t\tprivate final Map<String, Object> claims;\n\n\t\tprivate OAuth2AccessTokenClaims(OAuth2Token accessToken, Map<String, Object> claims) {\n\t\t\tthis.accessToken = accessToken;\n\t\t\tthis.claims = claims;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getTokenValue() {\n\t\t\treturn this.accessToken.getTokenValue();\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Instant getIssuedAt() {\n\t\t\treturn this.accessToken.getIssuedAt();\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Instant getExpiresAt() {\n\t\t\treturn this.accessToken.getExpiresAt();\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, Object> getClaims() {\n\t\t\treturn this.claims;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.io.Serial;\nimport java.util.Collections;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link Authentication} representing a protected resource request with a DPoP-bound\n * access token.\n *\n * @author Joe Grandja\n * @since 6.5\n * @see DPoPAuthenticationProvider\n */\npublic class DPoPAuthenticationToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 5481690438914686216L;\n\n\tprivate final String accessToken;\n\n\tprivate final String dPoPProof;\n\n\tprivate final String method;\n\n\tprivate final String resourceUri;\n\n\t/**\n\t * Constructs a {@code DPoPAuthenticationToken} using the provided parameters.\n\t * @param accessToken the DPoP-bound access token\n\t * @param dPoPProof the DPoP Proof {@link Jwt}\n\t * @param method the value of the HTTP method of the request\n\t * @param resourceUri the value of the HTTP resource URI of the request, without query\n\t * and fragment parts\n\t */\n\tpublic DPoPAuthenticationToken(String accessToken, String dPoPProof, String method, String resourceUri) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.hasText(accessToken, \"accessToken cannot be empty\");\n\t\tAssert.hasText(dPoPProof, \"dPoPProof cannot be empty\");\n\t\tAssert.hasText(method, \"method cannot be empty\");\n\t\tAssert.hasText(resourceUri, \"resourceUri cannot be empty\");\n\t\tthis.accessToken = accessToken;\n\t\tthis.dPoPProof = dPoPProof;\n\t\tthis.method = method;\n\t\tthis.resourceUri = resourceUri;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn getAccessToken();\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn getAccessToken();\n\t}\n\n\t/**\n\t * Returns the DPoP-bound access token.\n\t * @return the DPoP-bound access token\n\t */\n\tpublic String getAccessToken() {\n\t\treturn this.accessToken;\n\t}\n\n\t/**\n\t * Returns the DPoP Proof {@link Jwt}.\n\t * @return the DPoP Proof {@link Jwt}\n\t */\n\tpublic String getDPoPProof() {\n\t\treturn this.dPoPProof;\n\t}\n\n\t/**\n\t * Returns the value of the HTTP method of the request.\n\t * @return the value of the HTTP method of the request\n\t */\n\tpublic String getMethod() {\n\t\treturn this.method;\n\t}\n\n\t/**\n\t * Returns the value of the HTTP resource URI of the request, without query and\n\t * fragment parts.\n\t * @return the value of the HTTP resource URI of the request\n\t */\n\tpublic String getResourceUri() {\n\t\treturn this.resourceUri;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/DelegatingJwtGrantedAuthoritiesConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Jwt} to {@link GrantedAuthority} {@link Converter} that is a composite of\n * converters.\n *\n * @author Laszlo Stahorszki\n * @author Josh Cummings\n * @since 5.5\n * @see org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter\n */\npublic class DelegatingJwtGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {\n\n\tprivate final Collection<Converter<Jwt, Collection<GrantedAuthority>>> authoritiesConverters;\n\n\t/**\n\t * Constructs a {@link DelegatingJwtGrantedAuthoritiesConverter} using the provided\n\t * {@link Collection} of {@link Converter}s\n\t * @param authoritiesConverters the {@link Collection} of {@link Converter}s to use\n\t */\n\tpublic DelegatingJwtGrantedAuthoritiesConverter(\n\t\t\tCollection<Converter<Jwt, Collection<GrantedAuthority>>> authoritiesConverters) {\n\t\tAssert.notNull(authoritiesConverters, \"authoritiesConverters cannot be null\");\n\t\tthis.authoritiesConverters = new ArrayList<>(authoritiesConverters);\n\t}\n\n\t/**\n\t * Constructs a {@link DelegatingJwtGrantedAuthoritiesConverter} using the provided\n\t * array of {@link Converter}s\n\t * @param authoritiesConverters the array of {@link Converter}s to use\n\t */\n\t@SafeVarargs\n\tpublic DelegatingJwtGrantedAuthoritiesConverter(\n\t\t\tConverter<Jwt, Collection<GrantedAuthority>>... authoritiesConverters) {\n\t\tthis(Arrays.asList(authoritiesConverters));\n\t}\n\n\t/**\n\t * Extract {@link GrantedAuthority}s from the given {@link Jwt}.\n\t * <p>\n\t * The authorities are extracted from each delegated {@link Converter} one at a time.\n\t * For each converter, its authorities are added in order, with duplicates removed.\n\t * @param jwt The {@link Jwt} token\n\t * @return The {@link GrantedAuthority authorities} read from the token scopes\n\t */\n\t@Override\n\tpublic Collection<GrantedAuthority> convert(Jwt jwt) {\n\t\tCollection<GrantedAuthority> result = new LinkedHashSet<>();\n\n\t\tfor (Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter : this.authoritiesConverters) {\n\t\t\tCollection<GrantedAuthority> authorities = authoritiesConverter.convert(jwt);\n\t\t\tif (authorities != null) {\n\t\t\t\tresult.addAll(authorities);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/ExpressionJwtGrantedAuthoritiesConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * Uses an expression for extracting the token claim value to use for mapping\n * {@link GrantedAuthority authorities}.\n *\n * Note this can be used in combination with a\n * {@link DelegatingJwtGrantedAuthoritiesConverter}.\n *\n * @author Thomas Darimont\n * @since 6.4\n */\npublic final class ExpressionJwtGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate String authorityPrefix = \"SCOPE_\";\n\n\tprivate final Expression authoritiesClaimExpression;\n\n\t/**\n\t * Constructs a {@link ExpressionJwtGrantedAuthoritiesConverter} using the provided\n\t * {@code authoritiesClaimExpression}.\n\t * @param authoritiesClaimExpression The token claim SpEL Expression to map\n\t * authorities from.\n\t */\n\tpublic ExpressionJwtGrantedAuthoritiesConverter(Expression authoritiesClaimExpression) {\n\t\tAssert.notNull(authoritiesClaimExpression, \"authoritiesClaimExpression must not be null\");\n\t\tthis.authoritiesClaimExpression = authoritiesClaimExpression;\n\t}\n\n\t/**\n\t * Sets the prefix to use for {@link GrantedAuthority authorities} mapped by this\n\t * converter. Defaults to {@code \"SCOPE_\"}.\n\t * @param authorityPrefix The authority prefix\n\t */\n\tpublic void setAuthorityPrefix(String authorityPrefix) {\n\t\tAssert.notNull(authorityPrefix, \"authorityPrefix cannot be null\");\n\t\tthis.authorityPrefix = authorityPrefix;\n\t}\n\n\t/**\n\t * Extract {@link GrantedAuthority}s from the given {@link Jwt}.\n\t * @param jwt The {@link Jwt} token\n\t * @return The {@link GrantedAuthority authorities} read from the token scopes\n\t */\n\t@Override\n\tpublic Collection<GrantedAuthority> convert(Jwt jwt) {\n\t\tCollection<GrantedAuthority> grantedAuthorities = new ArrayList<>();\n\t\tfor (String authority : getAuthorities(jwt)) {\n\t\t\tgrantedAuthorities.add(new SimpleGrantedAuthority(this.authorityPrefix + authority));\n\t\t}\n\t\treturn grantedAuthorities;\n\t}\n\n\tprivate Collection<String> getAuthorities(Jwt jwt) {\n\t\tObject authorities;\n\t\ttry {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Looking for authorities with expression. expression=%s\",\n\t\t\t\t\t\tthis.authoritiesClaimExpression.getExpressionString()));\n\t\t\t}\n\t\t\tauthorities = this.authoritiesClaimExpression.getValue(jwt.getClaims(), Collection.class);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Found authorities with expression. authorities=%s\", authorities));\n\t\t\t}\n\t\t}\n\t\tcatch (ExpressionException ee) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Failed to evaluate expression. error=%s\", ee.getMessage()));\n\t\t\t}\n\t\t\tauthorities = Collections.emptyList();\n\t\t}\n\n\t\tif (authorities != null) {\n\t\t\treturn castAuthoritiesToCollection(authorities);\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Collection<String> castAuthoritiesToCollection(Object authorities) {\n\t\treturn (Collection<String>) authorities;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.util.Assert;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n * @author Evgeniy Cheban\n * @author Olivier Antoine\n * @author Andrey Litvitski\n * @since 5.1\n */\npublic class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.BEARER_AUTHORITY;\n\n\tprivate Converter<Jwt, OAuth2AuthenticatedPrincipal> jwtPrincipalConverter = JwtAuthenticatedPrincipal::new;\n\n\tprivate Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\n\t@Override\n\tpublic final AbstractAuthenticationToken convert(Jwt jwt) {\n\t\tCollection<GrantedAuthority> authorities = new HashSet<>(this.jwtGrantedAuthoritiesConverter.convert(jwt));\n\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\tOAuth2AuthenticatedPrincipal principal = this.jwtPrincipalConverter.convert(jwt);\n\t\tauthorities.addAll(principal.getAuthorities());\n\t\treturn new JwtAuthenticationToken(jwt, principal, authorities);\n\t}\n\n\t/**\n\t * Sets the {@link Converter Converter&lt;Jwt, OAuth2AuthenticatedPrincipal&gt;} to\n\t * use.\n\t * @param jwtPrincipalConverter The converter\n\t * @since 7.1\n\t */\n\tpublic void setJwtPrincipalConverter(Converter<Jwt, OAuth2AuthenticatedPrincipal> jwtPrincipalConverter) {\n\t\tAssert.notNull(jwtPrincipalConverter, \"jwtPrincipalConverter cannot be null\");\n\t\tthis.jwtPrincipalConverter = jwtPrincipalConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter Converter&lt;Jwt, Collection&lt;GrantedAuthority&gt;&gt;}\n\t * to use. Defaults to {@link JwtGrantedAuthoritiesConverter}.\n\t * @param jwtGrantedAuthoritiesConverter The converter\n\t * @since 5.2\n\t * @see JwtGrantedAuthoritiesConverter\n\t */\n\tpublic void setJwtGrantedAuthoritiesConverter(\n\t\t\tConverter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter) {\n\t\tAssert.notNull(jwtGrantedAuthoritiesConverter, \"jwtGrantedAuthoritiesConverter cannot be null\");\n\t\tthis.jwtGrantedAuthoritiesConverter = jwtGrantedAuthoritiesConverter;\n\t}\n\n\t/**\n\t * Sets the principal claim name. Defaults to {@link JwtClaimNames#SUB}.\n\t * @param principalClaimName The principal claim name\n\t * @since 5.4\n\t */\n\tpublic void setPrincipalClaimName(String principalClaimName) {\n\t\tAssert.hasText(principalClaimName, \"principalClaimName cannot be empty\");\n\t\tthis.jwtPrincipalConverter = (jwt) -> new JwtAuthenticatedPrincipal(jwt, principalClaimName);\n\t}\n\n\tprivate static final class JwtAuthenticatedPrincipal extends Jwt implements OAuth2AuthenticatedPrincipal {\n\n\t\tprivate final String principalClaimName;\n\n\t\tJwtAuthenticatedPrincipal(Jwt jwt) {\n\t\t\tthis(jwt, JwtClaimNames.SUB);\n\t\t}\n\n\t\tJwtAuthenticatedPrincipal(Jwt jwt, String principalClaimName) {\n\t\t\tsuper(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getHeaders(), jwt.getClaims());\n\t\t\tthis.principalClaimName = principalClaimName;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, Object> getAttributes() {\n\t\t\treturn getClaims();\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\t\treturn List.of();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\tString name = this.getClaimAsString(this.principalClaimName);\n\t\t\treturn (name != null) ? name : \"\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation of the {@link Jwt}-encoded\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>s for protecting OAuth 2.0 Resource Servers.\n * <p>\n * <p>\n * This {@link AuthenticationProvider} is responsible for decoding and verifying a\n * {@link Jwt}-encoded access token, returning its claims set as part of the\n * {@link Authentication} statement.\n * <p>\n * <p>\n * Scopes are translated into {@link GrantedAuthority}s according to the following\n * algorithm:\n *\n * 1. If there is a \"scope\" or \"scp\" attribute, then if a {@link String}, then split by\n * spaces and return, or if a {@link Collection}, then simply return 2. Take the resulting\n * {@link Collection} of {@link String}s and prepend the \"SCOPE_\" keyword, adding as\n * {@link GrantedAuthority}s.\n *\n * @author Josh Cummings\n * @author Joe Grandja\n * @author Jerome Wacongne ch4mp&#64;c4-soft.com\n * @since 5.1\n * @see AuthenticationProvider\n * @see JwtDecoder\n */\npublic final class JwtAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final JwtDecoder jwtDecoder;\n\n\tprivate Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter = new JwtAuthenticationConverter();\n\n\tpublic JwtAuthenticationProvider(JwtDecoder jwtDecoder) {\n\t\tAssert.notNull(jwtDecoder, \"jwtDecoder cannot be null\");\n\t\tthis.jwtDecoder = jwtDecoder;\n\t}\n\n\t/**\n\t * Decode and validate the\n\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n\t * Token</a>.\n\t * @param authentication the authentication request object.\n\t * @return A successful authentication\n\t * @throws AuthenticationException if authentication failed for some reason\n\t */\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tBearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;\n\t\tJwt jwt = getJwt(bearer);\n\t\tAbstractAuthenticationToken token = this.jwtAuthenticationConverter.convert(jwt);\n\t\tAssert.notNull(token, \"token cannot be null\");\n\t\tif (token.getDetails() == null) {\n\t\t\ttoken.setDetails(bearer.getDetails());\n\t\t}\n\t\tthis.logger.debug(\"Authenticated token\");\n\t\treturn token;\n\t}\n\n\tprivate Jwt getJwt(BearerTokenAuthenticationToken bearer) {\n\t\ttry {\n\t\t\treturn this.jwtDecoder.decode(bearer.getToken());\n\t\t}\n\t\tcatch (BadJwtException failed) {\n\t\t\tthis.logger.debug(\"Failed to authenticate since the JWT was invalid\");\n\t\t\tthrow new InvalidBearerTokenException((failed.getMessage() != null) ? failed.getMessage() : \"Invalid token\",\n\t\t\t\t\tfailed);\n\t\t}\n\t\tcatch (JwtException failed) {\n\t\t\tthrow new AuthenticationServiceException(\n\t\t\t\t\t(failed.getMessage() != null) ? failed.getMessage() : \"Invalid token\", failed);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn BearerTokenAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tpublic void setJwtAuthenticationConverter(\n\t\t\tConverter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter) {\n\t\tAssert.notNull(jwtAuthenticationConverter, \"jwtAuthenticationConverter cannot be null\");\n\t\tthis.jwtAuthenticationConverter = jwtAuthenticationConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.Transient;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AbstractOAuth2TokenAuthenticationToken} representing a\n * {@link Jwt} {@code Authentication}.\n *\n * @author Joe Grandja\n * @author Andrey Litvitski\n * @since 5.1\n * @see AbstractOAuth2TokenAuthenticationToken\n * @see Jwt\n */\n@Transient\npublic class JwtAuthenticationToken extends AbstractOAuth2TokenAuthenticationToken<Jwt> {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final @Nullable String name;\n\n\t/**\n\t * Constructs a {@code JwtAuthenticationToken} using the provided parameters.\n\t * @param jwt the JWT\n\t */\n\tpublic JwtAuthenticationToken(Jwt jwt) {\n\t\tsuper(jwt);\n\t\tthis.name = jwt.getSubject();\n\t}\n\n\t/**\n\t * Constructs a {@code JwtAuthenticationToken} using the provided parameters.\n\t * @param jwt the JWT\n\t * @param authorities the authorities assigned to the JWT\n\t */\n\tpublic JwtAuthenticationToken(Jwt jwt, Collection<? extends GrantedAuthority> authorities) {\n\t\tsuper(jwt, authorities);\n\t\tthis.setAuthenticated(true);\n\t\tthis.name = jwt.getSubject();\n\t}\n\n\t/**\n\t * Constructs a {@code JwtAuthenticationToken} using the provided parameters.\n\t * @param jwt the JWT\n\t * @param authorities the authorities assigned to the JWT\n\t * @param name the principal name\n\t */\n\tpublic JwtAuthenticationToken(Jwt jwt, Collection<? extends GrantedAuthority> authorities, String name) {\n\t\tsuper(jwt, authorities);\n\t\tthis.setAuthenticated(true);\n\t\tthis.name = name;\n\t}\n\n\tprotected JwtAuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.name = builder.name;\n\t}\n\n\t/**\n\t * Constructs a {@code JwtAuthenticationToken} using the provided parameters.\n\t * @param jwt the JWT\n\t * @param principal the principal\n\t * @param authorities the authorities assigned to the JWT\n\t * @since 7.1\n\t */\n\tpublic JwtAuthenticationToken(Jwt jwt, Object principal, Collection<? extends GrantedAuthority> authorities) {\n\t\tsuper(jwt, principal, jwt, authorities);\n\t\tthis.setAuthenticated(true);\n\t\tif (principal instanceof AuthenticatedPrincipal authenticatedPrincipal) {\n\t\t\tthis.name = authenticatedPrincipal.getName();\n\t\t}\n\t\telse {\n\t\t\tthis.name = jwt.getSubject();\n\t\t}\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getTokenAttributes() {\n\t\treturn this.getToken().getClaims();\n\t}\n\n\t/**\n\t * The principal name which is, by default, the {@link Jwt}'s subject. Returns empty\n\t * string if the subject claim is absent.\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn (this.name != null) ? this.name : \"\";\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder for {@link JwtAuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t * @see Authentication.Builder\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractOAuth2TokenAuthenticationBuilder<Jwt, B> {\n\n\t\tprivate @Nullable String name;\n\n\t\tprotected Builder(JwtAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.name = token.getName();\n\t\t}\n\n\t\t/**\n\t\t * A synonym for {@link #token(Jwt)}\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.isInstanceOf(Jwt.class, principal, \"principal must be of type Jwt\");\n\t\t\treturn token((Jwt) principal);\n\t\t}\n\n\t\t/**\n\t\t * A synonym for {@link #token(Jwt)}\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object credentials) {\n\t\t\tAssert.isInstanceOf(Jwt.class, credentials, \"credentials must be of type Jwt\");\n\t\t\treturn token((Jwt) credentials);\n\t\t}\n\n\t\t/**\n\t\t * Use this {@code token} as the token, principal, and credentials. Also sets the\n\t\t * {@code name} to {@link Jwt#getSubject}.\n\t\t * @param token the token to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\t@Override\n\t\tpublic B token(Jwt token) {\n\t\t\tsuper.principal(token);\n\t\t\tsuper.credentials(token);\n\t\t\treturn super.token(token).name(token.getSubject());\n\t\t}\n\n\t\t/**\n\t\t * The name to use.\n\t\t * @param name the name to use, or {@code null} if the principal has no name\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic B name(@Nullable String name) {\n\t\t\tthis.name = name;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic JwtAuthenticationToken build() {\n\t\t\treturn new JwtAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtBearerTokenAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Converter} that takes a {@link Jwt} and converts it into a\n * {@link BearerTokenAuthentication}.\n *\n * In the process, it will attempt to parse either the \"scope\" or \"scp\" attribute,\n * whichever it finds first.\n *\n * It's not intended that this implementation be configured since it is simply an adapter.\n * If you are using, for example, a custom {@link JwtGrantedAuthoritiesConverter}, then\n * it's recommended that you simply create your own {@link Converter} that delegates to\n * your custom {@link JwtGrantedAuthoritiesConverter} and instantiates the appropriate\n * {@link BearerTokenAuthentication}.\n *\n * @author Josh Cummings\n * @author Andrey Litvitski\n * @since 5.2\n */\npublic final class JwtBearerTokenAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {\n\n\tprivate Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\n\tprivate Converter<Jwt, OAuth2AuthenticatedPrincipal> jwtPrincipalConverter = (\n\t\t\tjwt) -> new DefaultOAuth2AuthenticatedPrincipal(jwt.getClaims(),\n\t\t\t\t\tthis.jwtGrantedAuthoritiesConverter.convert(jwt));\n\n\t@Override\n\tpublic AbstractAuthenticationToken convert(Jwt jwt) {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(),\n\t\t\t\tjwt.getIssuedAt(), jwt.getExpiresAt());\n\t\tCollection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tOAuth2AuthenticatedPrincipal principal = this.jwtPrincipalConverter.convert(jwt);\n\t\treturn new BearerTokenAuthentication(principal, accessToken, authorities);\n\t}\n\n\t/**\n\t * Sets the {@link Converter Converter&lt;Jwt, OAuth2AuthenticatedPrincipal&gt;} to\n\t * use.\n\t * <p>\n\t * By default, constructs a {@link DefaultOAuth2AuthenticatedPrincipal} based on the\n\t * claims and authorities derived from the {@link Jwt}.\n\t * @param jwtPrincipalConverter The converter\n\t * @since 7.1\n\t */\n\tpublic void setJwtPrincipalConverter(Converter<Jwt, OAuth2AuthenticatedPrincipal> jwtPrincipalConverter) {\n\t\tAssert.notNull(jwtPrincipalConverter, \"jwtPrincipalConverter cannot be null\");\n\t\tthis.jwtPrincipalConverter = jwtPrincipalConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Extracts the {@link GrantedAuthority}s from scope attributes typically found in a\n * {@link Jwt}.\n *\n * @author Eric Deandrea\n * @since 5.2\n */\npublic final class JwtGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final String DEFAULT_AUTHORITY_PREFIX = \"SCOPE_\";\n\n\tprivate static final String DEFAULT_AUTHORITIES_CLAIM_DELIMITER = \" \";\n\n\tprivate static final Collection<String> WELL_KNOWN_AUTHORITIES_CLAIM_NAMES = Arrays.asList(\"scope\", \"scp\");\n\n\tprivate String authorityPrefix = DEFAULT_AUTHORITY_PREFIX;\n\n\tprivate String authoritiesClaimDelimiter = DEFAULT_AUTHORITIES_CLAIM_DELIMITER;\n\n\tprivate Collection<String> authoritiesClaimNames = WELL_KNOWN_AUTHORITIES_CLAIM_NAMES;\n\n\t/**\n\t * Extract {@link GrantedAuthority}s from the given {@link Jwt}.\n\t * @param jwt The {@link Jwt} token\n\t * @return The {@link GrantedAuthority authorities} read from the token scopes\n\t */\n\t@Override\n\tpublic Collection<GrantedAuthority> convert(Jwt jwt) {\n\t\tCollection<GrantedAuthority> grantedAuthorities = new ArrayList<>();\n\t\tfor (String authority : getAuthorities(jwt)) {\n\t\t\tgrantedAuthorities.add(new SimpleGrantedAuthority(this.authorityPrefix + authority));\n\t\t}\n\t\treturn grantedAuthorities;\n\t}\n\n\t/**\n\t * Sets the prefix to use for {@link GrantedAuthority authorities} mapped by this\n\t * converter. Defaults to\n\t * {@link JwtGrantedAuthoritiesConverter#DEFAULT_AUTHORITY_PREFIX}.\n\t * @param authorityPrefix The authority prefix\n\t * @since 5.2\n\t */\n\tpublic void setAuthorityPrefix(String authorityPrefix) {\n\t\tAssert.notNull(authorityPrefix, \"authorityPrefix cannot be null\");\n\t\tthis.authorityPrefix = authorityPrefix;\n\t}\n\n\t/**\n\t * Sets the regex to use for splitting the value of the authorities claim into\n\t * {@link GrantedAuthority authorities}. Defaults to\n\t * {@link JwtGrantedAuthoritiesConverter#DEFAULT_AUTHORITIES_CLAIM_DELIMITER}.\n\t * @param authoritiesClaimDelimiter The regex used to split the authorities\n\t * @since 6.1\n\t */\n\tpublic void setAuthoritiesClaimDelimiter(String authoritiesClaimDelimiter) {\n\t\tAssert.notNull(authoritiesClaimDelimiter, \"authoritiesClaimDelimiter cannot be null\");\n\t\tthis.authoritiesClaimDelimiter = authoritiesClaimDelimiter;\n\t}\n\n\t/**\n\t * Sets the name of token claim to use for mapping {@link GrantedAuthority\n\t * authorities} by this converter. Defaults to\n\t * {@link JwtGrantedAuthoritiesConverter#WELL_KNOWN_AUTHORITIES_CLAIM_NAMES}.\n\t * @param authoritiesClaimName The token claim name to map authorities\n\t * @since 5.2\n\t */\n\tpublic void setAuthoritiesClaimName(String authoritiesClaimName) {\n\t\tAssert.hasText(authoritiesClaimName, \"authoritiesClaimName cannot be empty\");\n\t\tthis.authoritiesClaimNames = Collections.singletonList(authoritiesClaimName);\n\t}\n\n\tprivate @Nullable String getAuthoritiesClaimName(Jwt jwt) {\n\t\tfor (String claimName : this.authoritiesClaimNames) {\n\t\t\tif (jwt.hasClaim(claimName)) {\n\t\t\t\treturn claimName;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate Collection<String> getAuthorities(Jwt jwt) {\n\t\tString claimName = getAuthoritiesClaimName(jwt);\n\t\tif (claimName == null) {\n\t\t\tthis.logger.trace(\"Returning no authorities since could not find any claims that might contain scopes\");\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(LogMessage.format(\"Looking for scopes in claim %s\", claimName));\n\t\t}\n\t\tObject authorities = jwt.getClaim(claimName);\n\t\tif (authorities instanceof String) {\n\t\t\tif (StringUtils.hasText((String) authorities)) {\n\t\t\t\treturn Arrays.asList(((String) authorities).split(this.authoritiesClaimDelimiter));\n\t\t\t}\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tif (authorities instanceof Collection) {\n\t\t\treturn castAuthoritiesToCollection(authorities);\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Collection<String> castAuthoritiesToCollection(Object authorities) {\n\t\treturn (Collection<String>) authorities;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Predicate;\n\nimport com.nimbusds.jwt.JWTParser;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoders;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of {@link AuthenticationManagerResolver} that resolves a JWT-based\n * {@link AuthenticationManager} based on the <a href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a> in\n * a signed JWT (JWS).\n *\n * To use, this class must be able to determine whether the `iss` claim is trusted. Recall\n * that anyone can stand up an authorization server and issue valid tokens to a resource\n * server. The simplest way to achieve this is to supply a set of trusted issuers in the\n * constructor.\n *\n * This class derives the Issuer from the `iss` claim found in the\n * {@link HttpServletRequest}'s\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>.\n *\n * @author Josh Cummings\n * @since 5.3\n */\npublic final class JwtIssuerAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {\n\n\tprivate final AuthenticationManager authenticationManager;\n\n\t/**\n\t * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided\n\t * parameters\n\t * @param trustedIssuers an array of trusted issuers\n\t * @since 6.2\n\t */\n\tpublic static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(String... trustedIssuers) {\n\t\treturn fromTrustedIssuers(Set.of(trustedIssuers));\n\t}\n\n\t/**\n\t * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided\n\t * parameters\n\t * @param trustedIssuers a collection of trusted issuers\n\t * @since 6.2\n\t */\n\tpublic static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Collection<String> trustedIssuers) {\n\t\tAssert.notEmpty(trustedIssuers, \"trustedIssuers cannot be empty\");\n\t\treturn fromTrustedIssuers(Set.copyOf(trustedIssuers)::contains);\n\t}\n\n\t/**\n\t * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided\n\t * parameters\n\t * @param trustedIssuers a predicate to validate issuers\n\t * @since 6.2\n\t */\n\tpublic static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Predicate<String> trustedIssuers) {\n\t\tAssert.notNull(trustedIssuers, \"trustedIssuers cannot be null\");\n\t\treturn new JwtIssuerAuthenticationManagerResolver(\n\t\t\t\tnew TrustedIssuerJwtAuthenticationManagerResolver(trustedIssuers));\n\t}\n\n\t/**\n\t * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided\n\t * parameters\n\t *\n\t * Note that the {@link AuthenticationManagerResolver} provided in this constructor\n\t * will need to verify that the issuer is trusted. This should be done via an allowed\n\t * set of issuers.\n\t *\n\t * One way to achieve this is with a {@link Map} where the keys are the known issuers:\n\t * <pre>\n\t *     Map&lt;String, AuthenticationManager&gt; authenticationManagers = new HashMap&lt;&gt;();\n\t *     authenticationManagers.put(\"https://issuerOne.example.org\", managerOne);\n\t *     authenticationManagers.put(\"https://issuerTwo.example.org\", managerTwo);\n\t *     JwtIssuerAuthenticationManagerResolver resolver = new JwtIssuerAuthenticationManagerResolver(authenticationManagers::get);\n\t * </pre>\n\t *\n\t * The keys in the {@link Map} are the allowed issuers.\n\t * @param issuerAuthenticationManagerResolver a strategy for resolving the\n\t * {@link AuthenticationManager} by the issuer\n\t */\n\tpublic JwtIssuerAuthenticationManagerResolver(\n\t\t\tAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {\n\t\tAssert.notNull(issuerAuthenticationManagerResolver, \"issuerAuthenticationManagerResolver cannot be null\");\n\t\tthis.authenticationManager = new ResolvingAuthenticationManager(issuerAuthenticationManagerResolver);\n\t}\n\n\t/**\n\t * Return an {@link AuthenticationManager} based off of the `iss` claim found in the\n\t * request's bearer token\n\t * @throws OAuth2AuthenticationException if the bearer token is malformed or an\n\t * {@link AuthenticationManager} can't be derived from the issuer\n\t */\n\t@Override\n\tpublic AuthenticationManager resolve(HttpServletRequest request) {\n\t\treturn this.authenticationManager;\n\t}\n\n\tprivate static class ResolvingAuthenticationManager implements AuthenticationManager {\n\n\t\tprivate final Converter<BearerTokenAuthenticationToken, String> issuerConverter = new JwtClaimIssuerConverter();\n\n\t\tprivate final AuthenticationManagerResolver<String> issuerAuthenticationManagerResolver;\n\n\t\tResolvingAuthenticationManager(AuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {\n\t\t\tthis.issuerAuthenticationManagerResolver = issuerAuthenticationManagerResolver;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\t\tAssert.isTrue(authentication instanceof BearerTokenAuthenticationToken,\n\t\t\t\t\t\"Authentication must be of type BearerTokenAuthenticationToken\");\n\t\t\tBearerTokenAuthenticationToken token = (BearerTokenAuthenticationToken) authentication;\n\t\t\tString issuer = this.issuerConverter.convert(token);\n\t\t\tAuthenticationManager authenticationManager = this.issuerAuthenticationManagerResolver.resolve(issuer);\n\t\t\tif (authenticationManager == null) {\n\t\t\t\tAuthenticationException ex = new InvalidBearerTokenException(\"Invalid issuer\");\n\t\t\t\tex.setAuthenticationRequest(authentication);\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn authenticationManager.authenticate(authentication);\n\t\t\t}\n\t\t\tcatch (AuthenticationException ex) {\n\t\t\t\tex.setAuthenticationRequest(authentication);\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate static class JwtClaimIssuerConverter implements Converter<BearerTokenAuthenticationToken, String> {\n\n\t\t@Override\n\t\tpublic String convert(BearerTokenAuthenticationToken authentication) {\n\t\t\tString token = authentication.getToken();\n\t\t\ttry {\n\t\t\t\tString issuer = JWTParser.parse(token).getJWTClaimsSet().getIssuer();\n\t\t\t\tif (issuer != null) {\n\t\t\t\t\treturn issuer;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception cause) {\n\t\t\t\tAuthenticationException ex = new InvalidBearerTokenException(\n\t\t\t\t\t\t(cause.getMessage() != null) ? cause.getMessage() : \"Invalid token\", cause);\n\t\t\t\tex.setAuthenticationRequest(authentication);\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tAuthenticationException ex = new InvalidBearerTokenException(\"Missing issuer\");\n\t\t\tex.setAuthenticationRequest(authentication);\n\t\t\tthrow ex;\n\t\t}\n\n\t}\n\n\tstatic class TrustedIssuerJwtAuthenticationManagerResolver implements AuthenticationManagerResolver<String> {\n\n\t\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t\tprivate final Map<String, AuthenticationManager> authenticationManagers = new ConcurrentHashMap<>();\n\n\t\tprivate final Predicate<String> trustedIssuer;\n\n\t\tTrustedIssuerJwtAuthenticationManagerResolver(Predicate<String> trustedIssuer) {\n\t\t\tthis.trustedIssuer = trustedIssuer;\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"NullAway\") // Interface does not declare @Nullable; this\n\t\t\t\t\t\t\t\t\t\t// implementation returns null when issuer not\n\t\t\t\t\t\t\t\t\t\t// trusted\n\t\tpublic AuthenticationManager resolve(String issuer) {\n\t\t\tif (this.trustedIssuer.test(issuer)) {\n\t\t\t\tAuthenticationManager authenticationManager = this.authenticationManagers.computeIfAbsent(issuer,\n\t\t\t\t\t\t(k) -> {\n\t\t\t\t\t\t\tthis.logger.debug(\"Constructing AuthenticationManager\");\n\t\t\t\t\t\t\tJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuer);\n\t\t\t\t\t\t\treturn new JwtAuthenticationProvider(jwtDecoder)::authenticate;\n\t\t\t\t\t\t});\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Resolved AuthenticationManager for issuer '%s'\", issuer));\n\t\t\t\treturn authenticationManager;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.logger.debug(\"Did not resolve AuthenticationManager since issuer is not trusted\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.time.Duration;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Predicate;\n\nimport com.nimbusds.jwt.JWTParser;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of {@link ReactiveAuthenticationManagerResolver} that resolves a\n * JWT-based {@link ReactiveAuthenticationManager} based on the <a href=\n * \"https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier\">Issuer</a> in\n * a signed JWT (JWS).\n *\n * To use, this class must be able to determine whether the `iss` claim is trusted. Recall\n * that anyone can stand up an authorization server and issue valid tokens to a resource\n * server. The simplest way to achieve this is to supply a set of trusted issuers in the\n * constructor.\n *\n * This class derives the Issuer from the `iss` claim found in the\n * {@link ServerWebExchange}'s\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>.\n *\n * @author Josh Cummings\n * @author Roman Matiushchenko\n * @since 5.3\n */\npublic final class JwtIssuerReactiveAuthenticationManagerResolver\n\t\timplements ReactiveAuthenticationManagerResolver<ServerWebExchange> {\n\n\tprivate final ReactiveAuthenticationManager authenticationManager;\n\n\t/**\n\t * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the\n\t * provided parameters\n\t * @param trustedIssuers an array of trusted issuers\n\t * @since 6.2\n\t */\n\tpublic static JwtIssuerReactiveAuthenticationManagerResolver fromTrustedIssuers(String... trustedIssuers) {\n\t\treturn fromTrustedIssuers(Set.of(trustedIssuers));\n\t}\n\n\t/**\n\t * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the\n\t * provided parameters\n\t * @param trustedIssuers a collection of trusted issuers\n\t * @since 6.2\n\t */\n\tpublic static JwtIssuerReactiveAuthenticationManagerResolver fromTrustedIssuers(Collection<String> trustedIssuers) {\n\t\tAssert.notEmpty(trustedIssuers, \"trustedIssuers cannot be empty\");\n\t\treturn fromTrustedIssuers(Set.copyOf(trustedIssuers)::contains);\n\t}\n\n\t/**\n\t * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the\n\t * provided parameters\n\t * @param trustedIssuers a predicate to validate issuers\n\t * @since 6.2\n\t */\n\tpublic static JwtIssuerReactiveAuthenticationManagerResolver fromTrustedIssuers(Predicate<String> trustedIssuers) {\n\t\tAssert.notNull(trustedIssuers, \"trustedIssuers cannot be null\");\n\t\treturn new JwtIssuerReactiveAuthenticationManagerResolver(\n\t\t\t\tnew TrustedIssuerJwtAuthenticationManagerResolver(trustedIssuers));\n\t}\n\n\t/**\n\t * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the\n\t * provided parameters\n\t *\n\t * Note that the {@link ReactiveAuthenticationManagerResolver} provided in this\n\t * constructor will need to verify that the issuer is trusted. This should be done via\n\t * an allowed set of issuers.\n\t *\n\t * One way to achieve this is with a {@link Map} where the keys are the known issuers:\n\t * <pre>\n\t *     Map&lt;String, ReactiveAuthenticationManager&gt; authenticationManagers = new HashMap&lt;&gt;();\n\t *     authenticationManagers.put(\"https://issuerOne.example.org\", managerOne);\n\t *     authenticationManagers.put(\"https://issuerTwo.example.org\", managerTwo);\n\t *     JwtIssuerReactiveAuthenticationManagerResolver resolver = new JwtIssuerReactiveAuthenticationManagerResolver\n\t *     \t((issuer) -&gt; Mono.justOrEmpty(authenticationManagers.get(issuer));\n\t * </pre>\n\t *\n\t * The keys in the {@link Map} are the trusted issuers.\n\t * @param issuerAuthenticationManagerResolver a strategy for resolving the\n\t * {@link ReactiveAuthenticationManager} by the issuer\n\t */\n\tpublic JwtIssuerReactiveAuthenticationManagerResolver(\n\t\t\tReactiveAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {\n\t\tAssert.notNull(issuerAuthenticationManagerResolver, \"issuerAuthenticationManagerResolver cannot be null\");\n\t\tthis.authenticationManager = new ResolvingAuthenticationManager(issuerAuthenticationManagerResolver);\n\t}\n\n\t/**\n\t * Return an {@link AuthenticationManager} based off of the `iss` claim found in the\n\t * request's bearer token\n\t * @throws OAuth2AuthenticationException if the bearer token is malformed or an\n\t * {@link ReactiveAuthenticationManager} can't be derived from the issuer\n\t */\n\t@Override\n\tpublic Mono<ReactiveAuthenticationManager> resolve(ServerWebExchange exchange) {\n\t\treturn Mono.just(this.authenticationManager);\n\t}\n\n\tprivate static class ResolvingAuthenticationManager implements ReactiveAuthenticationManager {\n\n\t\tprivate final Converter<BearerTokenAuthenticationToken, Mono<String>> issuerConverter = new JwtClaimIssuerConverter();\n\n\t\tprivate final ReactiveAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver;\n\n\t\tResolvingAuthenticationManager(\n\t\t\t\tReactiveAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {\n\n\t\t\tthis.issuerAuthenticationManagerResolver = issuerAuthenticationManagerResolver;\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\t\tAssert.isTrue(authentication instanceof BearerTokenAuthenticationToken,\n\t\t\t\t\t\"Authentication must be of type BearerTokenAuthenticationToken\");\n\t\t\tBearerTokenAuthenticationToken token = (BearerTokenAuthenticationToken) authentication;\n\t\t\treturn this.issuerConverter.convert(token)\n\t\t\t\t.flatMap((issuer) -> this.issuerAuthenticationManagerResolver.resolve(issuer)\n\t\t\t\t\t.switchIfEmpty(Mono.error(() -> {\n\t\t\t\t\t\tAuthenticationException ex = new InvalidBearerTokenException(\"Invalid issuer \" + issuer);\n\t\t\t\t\t\tex.setAuthenticationRequest(authentication);\n\t\t\t\t\t\treturn ex;\n\t\t\t\t\t})))\n\t\t\t\t.flatMap((manager) -> manager.authenticate(authentication))\n\t\t\t\t.doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication));\n\t\t}\n\n\t}\n\n\tprivate static class JwtClaimIssuerConverter implements Converter<BearerTokenAuthenticationToken, Mono<String>> {\n\n\t\t@Override\n\t\tpublic Mono<String> convert(BearerTokenAuthenticationToken token) {\n\t\t\ttry {\n\t\t\t\tString issuer = JWTParser.parse(token.getToken()).getJWTClaimsSet().getIssuer();\n\t\t\t\tif (issuer == null) {\n\t\t\t\t\tAuthenticationException ex = new InvalidBearerTokenException(\"Missing issuer\");\n\t\t\t\t\tex.setAuthenticationRequest(token);\n\t\t\t\t\tthrow ex;\n\t\t\t\t}\n\t\t\t\treturn Mono.just(issuer);\n\t\t\t}\n\t\t\tcatch (Exception cause) {\n\t\t\t\treturn Mono.error(() -> {\n\t\t\t\t\tAuthenticationException ex = new InvalidBearerTokenException(\n\t\t\t\t\t\t\t(cause.getMessage() != null) ? cause.getMessage() : \"Invalid token\", cause);\n\t\t\t\t\tex.setAuthenticationRequest(token);\n\t\t\t\t\treturn ex;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class TrustedIssuerJwtAuthenticationManagerResolver\n\t\t\timplements ReactiveAuthenticationManagerResolver<String> {\n\n\t\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t\tprivate final Map<String, Mono<ReactiveAuthenticationManager>> authenticationManagers = new ConcurrentHashMap<>();\n\n\t\tprivate final Predicate<String> trustedIssuer;\n\n\t\tTrustedIssuerJwtAuthenticationManagerResolver(Predicate<String> trustedIssuer) {\n\t\t\tthis.trustedIssuer = trustedIssuer;\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<ReactiveAuthenticationManager> resolve(String issuer) {\n\t\t\tif (!this.trustedIssuer.test(issuer)) {\n\t\t\t\tthis.logger.debug(\"Did not resolve AuthenticationManager since issuer is not trusted\");\n\t\t\t\treturn Mono.empty();\n\t\t\t}\n\t\t\t// @formatter:off\n\t\t\treturn this.authenticationManagers.computeIfAbsent(issuer,\n\t\t\t\t\t(k) -> Mono.<ReactiveAuthenticationManager>fromCallable(() -> new JwtReactiveAuthenticationManager(ReactiveJwtDecoders.fromIssuerLocation(k)))\n\t\t\t\t\t\t\t.doOnNext((manager) -> this.logger.debug(LogMessage.format(\"Resolved AuthenticationManager for issuer '%s'\", issuer)))\n\t\t\t\t\t\t\t.subscribeOn(Schedulers.boundedElastic())\n\t\t\t\t\t\t\t.cache((manager) -> Duration.ofMillis(Long.MAX_VALUE), (ex) -> Duration.ZERO, () -> Duration.ZERO)\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthenticationManager} for Jwt tokens.\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic final class JwtReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate final ReactiveJwtDecoder jwtDecoder;\n\n\tprivate Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverterAdapter(\n\t\t\tnew JwtAuthenticationConverter());\n\n\tpublic JwtReactiveAuthenticationManager(ReactiveJwtDecoder jwtDecoder) {\n\t\tAssert.notNull(jwtDecoder, \"jwtDecoder cannot be null\");\n\t\tthis.jwtDecoder = jwtDecoder;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\t// @formatter:off\n\t\treturn Mono.justOrEmpty(authentication)\n\t\t\t\t.filter((a) -> a instanceof BearerTokenAuthenticationToken)\n\t\t\t\t.cast(BearerTokenAuthenticationToken.class)\n\t\t\t\t.map(BearerTokenAuthenticationToken::getToken)\n\t\t\t\t.flatMap(this.jwtDecoder::decode)\n\t\t\t\t.flatMap(this.jwtAuthenticationConverter::convert)\n\t\t\t\t.cast(Authentication.class)\n\t\t\t\t.onErrorMap(JwtException.class, this::onError);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Use the given {@link Converter} for converting a {@link Jwt} into an\n\t * {@link AbstractAuthenticationToken}.\n\t * @param jwtAuthenticationConverter the {@link Converter} to use\n\t */\n\tpublic void setJwtAuthenticationConverter(\n\t\t\tConverter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter) {\n\t\tAssert.notNull(jwtAuthenticationConverter, \"jwtAuthenticationConverter cannot be null\");\n\t\tthis.jwtAuthenticationConverter = jwtAuthenticationConverter;\n\t}\n\n\tprivate AuthenticationException onError(JwtException ex) {\n\t\tif (ex instanceof BadJwtException) {\n\t\t\treturn new InvalidBearerTokenException((ex.getMessage() != null) ? ex.getMessage() : \"Invalid token\", ex);\n\t\t}\n\t\treturn new AuthenticationServiceException(ex.getMessage(), ex);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.HashSet;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.security.oauth2.server.resource.introspection.BadOpaqueTokenException;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} implementation for opaque\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>s, using an\n * <a href=\"https://tools.ietf.org/html/rfc7662\" target=\"_blank\">OAuth 2.0 Introspection\n * Endpoint</a> to check the token's validity and reveal its attributes.\n * <p>\n * This {@link AuthenticationProvider} is responsible for introspecting and verifying an\n * opaque access token, returning its attributes set as part of the {@link Authentication}\n * statement.\n * <p>\n * Scopes are translated into {@link GrantedAuthority}s according to the following\n * algorithm:\n * <ol>\n * <li>If there is a \"scope\" attribute, then convert to a {@link Collection} of\n * {@link String}s.\n * <li>Take the resulting {@link Collection} and prepend the \"SCOPE_\" keyword to each\n * element, adding as {@link GrantedAuthority}s.\n * </ol>\n * <p>\n * An {@link OpaqueTokenIntrospector} is responsible for retrieving token attributes from\n * an authorization server.\n * <p>\n * An {@link OpaqueTokenAuthenticationConverter} is responsible for turning a successful\n * introspection result into an {@link Authentication} instance (which may include mapping\n * {@link GrantedAuthority}s from token attributes or retrieving from another source).\n *\n * @author Josh Cummings\n * @author Jerome Wacongne &lt;ch4mp@c4-soft.com&gt;\n * @since 5.2\n * @see AuthenticationProvider\n */\npublic final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.BEARER_AUTHORITY;\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OpaqueTokenIntrospector introspector;\n\n\tprivate OpaqueTokenAuthenticationConverter authenticationConverter = OpaqueTokenAuthenticationProvider::convert;\n\n\t/**\n\t * Creates a {@code OpaqueTokenAuthenticationProvider} with the provided parameters\n\t * @param introspector The {@link OpaqueTokenIntrospector} to use\n\t */\n\tpublic OpaqueTokenAuthenticationProvider(OpaqueTokenIntrospector introspector) {\n\t\tAssert.notNull(introspector, \"introspector cannot be null\");\n\t\tthis.introspector = introspector;\n\t}\n\n\t/**\n\t * Introspect and validate the opaque\n\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n\t * Token</a> and then delegates {@link Authentication} instantiation to\n\t * {@link OpaqueTokenAuthenticationConverter}.\n\t * <p>\n\t * If created Authentication is instance of {@link AbstractAuthenticationToken} and\n\t * details are null, then introspection result details are used.\n\t * @param authentication the authentication request object.\n\t * @return A successful authentication\n\t * @throws AuthenticationException if authentication failed for some reason\n\t */\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (!(authentication instanceof BearerTokenAuthenticationToken bearer)) {\n\t\t\treturn null;\n\t\t}\n\t\tOAuth2AuthenticatedPrincipal principal = getOAuth2AuthenticatedPrincipal(bearer);\n\t\tAuthentication result = this.authenticationConverter.convert(bearer.getToken(), principal);\n\t\tif (result == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (AbstractAuthenticationToken.class.isAssignableFrom(result.getClass())) {\n\t\t\tfinal AbstractAuthenticationToken auth = (AbstractAuthenticationToken) result;\n\t\t\tif (auth.getDetails() == null) {\n\t\t\t\tauth.setDetails(bearer.getDetails());\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(\"Authenticated token\");\n\t\treturn result;\n\t}\n\n\tprivate OAuth2AuthenticatedPrincipal getOAuth2AuthenticatedPrincipal(BearerTokenAuthenticationToken bearer) {\n\t\ttry {\n\t\t\treturn this.introspector.introspect(bearer.getToken());\n\t\t}\n\t\tcatch (BadOpaqueTokenException failed) {\n\t\t\tthis.logger.debug(\"Failed to authenticate since token was invalid\");\n\t\t\tthrow new InvalidBearerTokenException((failed.getMessage() != null) ? failed.getMessage() : \"Invalid token\",\n\t\t\t\t\tfailed);\n\t\t}\n\t\tcatch (OAuth2IntrospectionException failed) {\n\t\t\tthrow new AuthenticationServiceException(failed.getMessage(), failed);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn BearerTokenAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Default {@link OpaqueTokenAuthenticationConverter}.\n\t * @param introspectedToken the bearer string that was successfully introspected\n\t * @param authenticatedPrincipal the successful introspection output\n\t * @return a {@link BearerTokenAuthentication}\n\t */\n\tstatic BearerTokenAuthentication convert(String introspectedToken,\n\t\t\tOAuth2AuthenticatedPrincipal authenticatedPrincipal) {\n\t\tInstant iat = authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.IAT);\n\t\tInstant exp = authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP);\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, introspectedToken,\n\t\t\t\tiat, exp);\n\t\tCollection<GrantedAuthority> authorities = new HashSet<>(authenticatedPrincipal.getAuthorities());\n\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\treturn new BearerTokenAuthentication(authenticatedPrincipal, accessToken, authorities);\n\t}\n\n\t/**\n\t * Provide with a custom bean to turn successful introspection result into an\n\t * {@link Authentication} instance of your choice. By default,\n\t * {@link BearerTokenAuthentication} will be built.\n\t * @param authenticationConverter the converter to use\n\t * @since 5.8\n\t */\n\tpublic void setAuthenticationConverter(OpaqueTokenAuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.security.oauth2.server.resource.introspection.BadOpaqueTokenException;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link ReactiveAuthenticationManager} implementation for opaque\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>s, using an\n * <a href=\"https://tools.ietf.org/html/rfc7662\" target=\"_blank\">OAuth 2.0 Introspection\n * Endpoint</a> to check the token's validity and reveal its attributes.\n * <p>\n * This {@link ReactiveAuthenticationManager} is responsible for introspecting and\n * verifying an opaque access token, returning its attributes set as part of the\n * {@link Authentication} statement.\n * <p>\n * A {@link ReactiveOpaqueTokenIntrospector} is responsible for retrieving token\n * attributes from an authorization server.\n * <p>\n * A {@link ReactiveOpaqueTokenAuthenticationConverter} is responsible for turning a\n * successful introspection result into an {@link Authentication} instance (which may\n * include mapping {@link GrantedAuthority}s from token attributes or retrieving from\n * another source).\n *\n * @author Josh Cummings\n * @author Jerome Wacongne &lt;ch4mp@c4-soft.com&gt;\n * @since 5.2\n * @see ReactiveAuthenticationManager\n */\npublic class OpaqueTokenReactiveAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate final ReactiveOpaqueTokenIntrospector introspector;\n\n\tprivate ReactiveOpaqueTokenAuthenticationConverter authenticationConverter = OpaqueTokenReactiveAuthenticationManager::convert;\n\n\t/**\n\t * Creates a {@code OpaqueTokenReactiveAuthenticationManager} with the provided\n\t * parameters\n\t * @param introspector The {@link ReactiveOpaqueTokenIntrospector} to use\n\t */\n\tpublic OpaqueTokenReactiveAuthenticationManager(ReactiveOpaqueTokenIntrospector introspector) {\n\t\tAssert.notNull(introspector, \"introspector cannot be null\");\n\t\tthis.introspector = introspector;\n\t}\n\n\t/**\n\t * Introspect and validate the opaque\n\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n\t * Token</a> and then delegates {@link Authentication} instantiation to\n\t * {@link ReactiveOpaqueTokenAuthenticationConverter}.\n\t * <p>\n\t * If created Authentication is instance of {@link AbstractAuthenticationToken} and\n\t * details are null, then introspection result details are used.\n\t * @param authentication the authentication request object.\n\t * @return A successful authentication\n\t */\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\t// @formatter:off\n\t\treturn Mono.justOrEmpty(authentication)\n\t\t\t\t.filter(BearerTokenAuthenticationToken.class::isInstance)\n\t\t\t\t.cast(BearerTokenAuthenticationToken.class)\n\t\t\t\t.map(BearerTokenAuthenticationToken::getToken)\n\t\t\t\t.flatMap(this::authenticate);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Authentication> authenticate(String token) {\n\t\t// @formatter:off\n\t\treturn this.introspector.introspect(token)\n\t\t\t\t.flatMap((principal) -> this.authenticationConverter.convert(token, principal))\n\t\t\t\t.onErrorMap(OAuth2IntrospectionException.class, this::onError);\n\t\t// @formatter:on\n\t}\n\n\tprivate AuthenticationException onError(OAuth2IntrospectionException ex) {\n\t\tif (ex instanceof BadOpaqueTokenException) {\n\t\t\treturn new InvalidBearerTokenException((ex.getMessage() != null) ? ex.getMessage() : \"Invalid token\", ex);\n\t\t}\n\t\treturn new AuthenticationServiceException(ex.getMessage(), ex);\n\t}\n\n\t/**\n\t * Default {@link ReactiveOpaqueTokenAuthenticationConverter}.\n\t * @param introspectedToken the bearer string that was successfully introspected\n\t * @param authenticatedPrincipal the successful introspection output\n\t * @return an async wrapper of default {@link OpaqueTokenAuthenticationConverter}\n\t * result\n\t */\n\tstatic Mono<Authentication> convert(String introspectedToken, OAuth2AuthenticatedPrincipal authenticatedPrincipal) {\n\t\treturn Mono.just(OpaqueTokenAuthenticationProvider.convert(introspectedToken, authenticatedPrincipal));\n\t}\n\n\t/**\n\t * Provide with a custom bean to turn successful introspection result into an\n\t * {@link Authentication} instance of your choice. By default,\n\t * {@link BearerTokenAuthentication} will be built.\n\t * @param authenticationConverter the converter to use\n\t * @since 5.8\n\t */\n\tpublic void setAuthenticationConverter(ReactiveOpaqueTokenAuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/ReactiveJwtAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.util.Assert;\n\n/**\n * Reactive version of {@link JwtAuthenticationConverter} for converting a {@link Jwt} to\n * a {@link AbstractAuthenticationToken Mono&lt;AbstractAuthenticationToken&gt;}.\n *\n * @author Eric Deandrea\n * @author Marcus Kainth\n * @since 5.2\n */\npublic final class ReactiveJwtAuthenticationConverter implements Converter<Jwt, Mono<AbstractAuthenticationToken>> {\n\n\tprivate Converter<Jwt, Flux<GrantedAuthority>> jwtGrantedAuthoritiesConverter = new ReactiveJwtGrantedAuthoritiesConverterAdapter(\n\t\t\tnew JwtGrantedAuthoritiesConverter());\n\n\tprivate String principalClaimName = JwtClaimNames.SUB;\n\n\t@Override\n\tpublic Mono<AbstractAuthenticationToken> convert(Jwt jwt) {\n\t\t// @formatter:off\n\t\treturn this.jwtGrantedAuthoritiesConverter.convert(jwt)\n\t\t\t\t.collectList()\n\t\t\t\t.map((authorities) -> {\n\t\t\t\t\tString principalName = jwt.getClaimAsString(this.principalClaimName);\n\t\t\t\t\treturn new JwtAuthenticationToken(jwt, authorities,\n\t\t\t\t\t\t\t(principalName != null) ? principalName : \"\");\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Sets the {@link Converter Converter&lt;Jwt, Flux&lt;GrantedAuthority&gt;&gt;} to\n\t * use. Defaults to a reactive {@link JwtGrantedAuthoritiesConverter}.\n\t * @param jwtGrantedAuthoritiesConverter The converter\n\t * @see JwtGrantedAuthoritiesConverter\n\t */\n\tpublic void setJwtGrantedAuthoritiesConverter(\n\t\t\tConverter<Jwt, Flux<GrantedAuthority>> jwtGrantedAuthoritiesConverter) {\n\t\tAssert.notNull(jwtGrantedAuthoritiesConverter, \"jwtGrantedAuthoritiesConverter cannot be null\");\n\t\tthis.jwtGrantedAuthoritiesConverter = jwtGrantedAuthoritiesConverter;\n\t}\n\n\t/**\n\t * Sets the principal claim name. Defaults to {@link JwtClaimNames#SUB}.\n\t * @param principalClaimName The principal claim name\n\t * @since 6.1\n\t */\n\tpublic void setPrincipalClaimName(String principalClaimName) {\n\t\tAssert.hasText(principalClaimName, \"principalClaimName cannot be empty\");\n\t\tthis.principalClaimName = principalClaimName;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/ReactiveJwtAuthenticationConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * A reactive {@link Converter} for adapting a non-blocking imperative {@link Converter}\n *\n * @author Josh Cummings\n * @since 5.1.1\n */\npublic class ReactiveJwtAuthenticationConverterAdapter implements Converter<Jwt, Mono<AbstractAuthenticationToken>> {\n\n\tprivate final Converter<Jwt, AbstractAuthenticationToken> delegate;\n\n\tpublic ReactiveJwtAuthenticationConverterAdapter(Converter<Jwt, AbstractAuthenticationToken> delegate) {\n\t\tAssert.notNull(delegate, \"delegate cannot be null\");\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic final Mono<AbstractAuthenticationToken> convert(Jwt jwt) {\n\t\treturn Mono.just(jwt).map(this.delegate::convert);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/ReactiveJwtGrantedAuthoritiesConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\n\nimport reactor.core.publisher.Flux;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.util.Assert;\n\n/**\n * Adapts a {@link Converter Converter&lt;Jwt, Collection&lt;GrantedAuthority&gt;&gt;} to\n * a {@link Converter Converter&lt;Jwt, Flux&lt;GrantedAuthority&gt;&gt;}.\n * <p>\n * Make sure the {@link Converter Converter&lt;Jwt,\n * Collection&lt;GrantedAuthority&gt;&gt;} being adapted is non-blocking.\n * </p>\n *\n * @author Eric Deandrea\n * @since 5.2\n * @see JwtGrantedAuthoritiesConverter\n */\npublic final class ReactiveJwtGrantedAuthoritiesConverterAdapter implements Converter<Jwt, Flux<GrantedAuthority>> {\n\n\tprivate final Converter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter;\n\n\tpublic ReactiveJwtGrantedAuthoritiesConverterAdapter(\n\t\t\tConverter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter) {\n\t\tAssert.notNull(grantedAuthoritiesConverter, \"grantedAuthoritiesConverter cannot be null\");\n\t\tthis.grantedAuthoritiesConverter = grantedAuthoritiesConverter;\n\t}\n\n\t@Override\n\tpublic Flux<GrantedAuthority> convert(Jwt jwt) {\n\t\treturn Flux.fromIterable(this.grantedAuthoritiesConverter.convert(jwt));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server {@code Authentication}s and supporting classes and\n * interfaces.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/BadOpaqueTokenException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An exception similar to\n * {@link org.springframework.security.authentication.BadCredentialsException} that\n * indicates an opaque token that is invalid in some way.\n *\n * @author Josh Cummings\n * @since 5.3\n */\npublic class BadOpaqueTokenException extends OAuth2IntrospectionException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -6937847463454551076L;\n\n\tpublic BadOpaqueTokenException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic BadOpaqueTokenException(String message, @Nullable Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/OAuth2IntrospectionAuthenticatedPrincipal.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;\n\n/**\n * A domain object that wraps the attributes of OAuth 2.0 Token Introspection.\n *\n * @author David Kovac\n * @since 5.4\n * @see <a target=\"_blank\" href=\n * \"https://tools.ietf.org/html/rfc7662#section-2.2\">Introspection Response</a>\n */\npublic final class OAuth2IntrospectionAuthenticatedPrincipal\n\t\timplements OAuth2TokenIntrospectionClaimAccessor, OAuth2AuthenticatedPrincipal, Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 382069143804098909L;\n\n\tprivate final OAuth2AuthenticatedPrincipal delegate;\n\n\t/**\n\t * Constructs an {@code OAuth2IntrospectionAuthenticatedPrincipal} using the provided\n\t * parameters.\n\t * @param attributes the attributes of the OAuth 2.0 Token Introspection\n\t * @param authorities the authorities of the OAuth 2.0 Token Introspection\n\t */\n\tpublic OAuth2IntrospectionAuthenticatedPrincipal(Map<String, Object> attributes,\n\t\t\tCollection<GrantedAuthority> authorities) {\n\t\tthis.delegate = new DefaultOAuth2AuthenticatedPrincipal(attributes, authorities);\n\t}\n\n\t/**\n\t * Constructs an {@code OAuth2IntrospectionAuthenticatedPrincipal} using the provided\n\t * parameters.\n\t * @param name the name attached to the OAuth 2.0 Token Introspection\n\t * @param attributes the attributes of the OAuth 2.0 Token Introspection\n\t * @param authorities the authorities of the OAuth 2.0 Token Introspection\n\t */\n\tpublic OAuth2IntrospectionAuthenticatedPrincipal(String name, Map<String, Object> attributes,\n\t\t\tCollection<GrantedAuthority> authorities) {\n\t\tthis.delegate = new DefaultOAuth2AuthenticatedPrincipal(name, attributes, authorities);\n\t}\n\n\t/**\n\t * Gets the attributes of the OAuth 2.0 Token Introspection in map form.\n\t * @return a {@link Map} of the attribute's objects keyed by the attribute's names\n\t */\n\t@Override\n\tpublic Map<String, Object> getAttributes() {\n\t\treturn this.delegate.getAttributes();\n\t}\n\n\t/**\n\t * Get the {@link Collection} of {@link GrantedAuthority}s associated with this OAuth\n\t * 2.0 Token Introspection\n\t * @return the OAuth 2.0 Token Introspection authorities\n\t */\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\treturn this.delegate.getAuthorities();\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn this.delegate.getName();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getClaims() {\n\t\treturn getAttributes();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/OAuth2IntrospectionException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Base exception for all OAuth 2.0 Introspection related errors\n *\n * @author Josh Cummings\n * @since 5.2\n */\npublic class OAuth2IntrospectionException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7327790383594166793L;\n\n\tpublic OAuth2IntrospectionException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic OAuth2IntrospectionException(String message, @Nullable Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/OpaqueTokenAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\n\n/**\n * Convert a successful introspection result into an authentication result.\n *\n * @author Jerome Wacongne &lt;ch4mp@c4-soft.com&gt;\n * @since 5.8\n */\n@FunctionalInterface\npublic interface OpaqueTokenAuthenticationConverter {\n\n\t/**\n\t * Converts a successful introspection result into an authentication result.\n\t * @param introspectedToken the bearer token used to perform token introspection\n\t * @param authenticatedPrincipal the result of token introspection\n\t * @return an {@link Authentication} instance\n\t */\n\tAuthentication convert(String introspectedToken, OAuth2AuthenticatedPrincipal authenticatedPrincipal);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/OpaqueTokenIntrospector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\n\n/**\n * A contract for introspecting and verifying an OAuth 2.0 token.\n *\n * A typical implementation of this interface will make a request to an\n * <a href=\"https://tools.ietf.org/html/rfc7662\" target=\"_blank\">OAuth 2.0 Introspection\n * Endpoint</a> to verify the token and return its attributes, indicating a successful\n * verification.\n *\n * Another sensible implementation of this interface would be to query a backing store of\n * tokens, for example a distributed cache.\n *\n * @author Josh Cummings\n * @since 5.2\n */\n@FunctionalInterface\npublic interface OpaqueTokenIntrospector {\n\n\t/**\n\t * Introspect and verify the given token, returning its attributes.\n\t *\n\t * Returning a {@link OAuth2AuthenticatedPrincipal} is indicative that the token is\n\t * valid.\n\t * @param token the token to introspect\n\t * @return the token's attributes\n\t */\n\tOAuth2AuthenticatedPrincipal introspect(String token);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/ReactiveOpaqueTokenAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\n\n/**\n * Convert a successful introspection result into an authentication result.\n *\n * @author Jerome Wacongne &lt;ch4mp@c4-soft.com&gt;\n * @since 5.8\n */\n@FunctionalInterface\npublic interface ReactiveOpaqueTokenAuthenticationConverter {\n\n\t/**\n\t * Converts a successful introspection result into an authentication result.\n\t * @param introspectedToken the bearer token used to perform token introspection\n\t * @param authenticatedPrincipal the result of token introspection\n\t * @return an {@link Authentication} instance\n\t */\n\tMono<Authentication> convert(String introspectedToken, OAuth2AuthenticatedPrincipal authenticatedPrincipal);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/ReactiveOpaqueTokenIntrospector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.util.Map;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\n\n/**\n * A contract for introspecting and verifying an OAuth 2.0 token.\n *\n * A typical implementation of this interface will make a request to an\n * <a href=\"https://tools.ietf.org/html/rfc7662\" target=\"_blank\">OAuth 2.0 Introspection\n * Endpoint</a> to verify the token and return its attributes, indicating a successful\n * verification.\n *\n * Another sensible implementation of this interface would be to query a backing store of\n * tokens, for example a distributed cache.\n *\n * @author Josh Cummings\n * @since 5.2\n */\n@FunctionalInterface\npublic interface ReactiveOpaqueTokenIntrospector {\n\n\t/**\n\t * Introspect and verify the given token, returning its attributes.\n\t *\n\t * Returning a {@link Map} is indicative that the token is valid.\n\t * @param token the token to introspect\n\t * @return the token's attributes\n\t */\n\tMono<OAuth2AuthenticatedPrincipal> introspect(String token);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/RestClientOpaqueTokenIntrospector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.Serial;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.RestClient;\n\n/**\n * A Spring implementation of {@link OpaqueTokenIntrospector} that verifies and\n * introspects a token using the configured\n * <a href=\"https://tools.ietf.org/html/rfc7662\" target=\"_blank\">OAuth 2.0 Introspection\n * Endpoint</a>, using {@link RestClient} for HTTP communication.\n *\n * @author Andrey Litvitski\n * @since 7.1\n */\npublic final class RestClientOpaqueTokenIntrospector implements OpaqueTokenIntrospector {\n\n\tprivate static final String AUTHORITY_PREFIX = \"SCOPE_\";\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RestClient restClient;\n\n\tprivate final String introspectionUri;\n\n\tprivate Converter<OAuth2TokenIntrospectionClaimAccessor, ? extends OAuth2AuthenticatedPrincipal> authenticationConverter = this::defaultAuthenticationConverter;\n\n\t/**\n\t * Creates a {@code OpaqueTokenAuthenticationProvider} with the provided parameters\n\t * The given {@link RestClient} should perform its own client authentication against\n\t * the introspection endpoint.\n\t * @param introspectionUri The introspection endpoint uri\n\t * @param restClient The client for performing the introspection request\n\t */\n\tpublic RestClientOpaqueTokenIntrospector(String introspectionUri, RestClient restClient) {\n\t\tAssert.notNull(introspectionUri, \"introspectionUri cannot be null\");\n\t\tAssert.notNull(restClient, \"restClient cannot be null\");\n\t\tthis.introspectionUri = introspectionUri;\n\t\tthis.restClient = restClient;\n\t}\n\n\tprivate MultiValueMap<String, String> requestBody(String token) {\n\t\tMultiValueMap<String, String> body = new LinkedMultiValueMap<>();\n\t\tbody.add(\"token\", token);\n\t\treturn body;\n\t}\n\n\t@Override\n\tpublic OAuth2AuthenticatedPrincipal introspect(String token) {\n\t\tResponseEntity<Map<String, Object>> responseEntity = makeRequest(token);\n\t\tMap<String, Object> claims = adaptToNimbusResponse(responseEntity);\n\t\tOAuth2TokenIntrospectionClaimAccessor accessor = convertClaimsSet(claims);\n\t\treturn this.authenticationConverter.convert(accessor);\n\t}\n\n\tprivate ResponseEntity<Map<String, Object>> makeRequest(String token) {\n\t\ttry {\n\t\t\tRestClient.RequestBodySpec spec = this.restClient.post()\n\t\t\t\t.uri(this.introspectionUri)\n\t\t\t\t.headers((h) -> h.setAccept(List.of(MediaType.APPLICATION_JSON)))\n\t\t\t\t.body(requestBody(token));\n\t\t\treturn spec.retrieve().toEntity(STRING_OBJECT_MAP);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new OAuth2IntrospectionException((ex.getMessage() != null) ? ex.getMessage() : \"Invalid token\", ex);\n\t\t}\n\t}\n\n\tprivate Map<String, Object> adaptToNimbusResponse(ResponseEntity<Map<String, Object>> responseEntity) {\n\t\tif (responseEntity.getStatusCode() != HttpStatus.OK) {\n\t\t\tthrow new OAuth2IntrospectionException(\n\t\t\t\t\t\"Introspection endpoint responded with \" + responseEntity.getStatusCode());\n\t\t}\n\t\tMap<String, Object> claims = responseEntity.getBody();\n\t\t// relying solely on the authorization server to validate this token (not checking\n\t\t// 'exp', for example)\n\t\tif (claims == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\n\t\tboolean active = (boolean) claims.compute(OAuth2TokenIntrospectionClaimNames.ACTIVE, (k, v) -> {\n\t\t\tif (v instanceof String) {\n\t\t\t\treturn Boolean.parseBoolean((String) v);\n\t\t\t}\n\t\t\tif (v instanceof Boolean) {\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\tif (!active) {\n\t\t\tthis.logger.trace(\"Did not validate token since it is inactive\");\n\t\t\tthrow new BadOpaqueTokenException(\"Provided token isn't active\");\n\t\t}\n\t\treturn claims;\n\t}\n\n\tprivate ArrayListFromStringClaimAccessor convertClaimsSet(Map<String, Object> claims) {\n\t\tMap<String, Object> converted = new LinkedHashMap<>(claims);\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.AUD, (k, v) -> {\n\t\t\tif (v instanceof String) {\n\t\t\t\treturn Collections.singletonList(v);\n\t\t\t}\n\t\t\treturn v;\n\t\t});\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, (k, v) -> v.toString());\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.EXP,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.IAT,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\t// RFC-7662 page 7 directs users to RFC-7519 for defining the values of these\n\t\t// issuer fields.\n\t\t// https://datatracker.ietf.org/doc/html/rfc7662#page-7\n\t\t//\n\t\t// RFC-7519 page 9 defines issuer fields as being 'case-sensitive' strings\n\t\t// containing\n\t\t// a 'StringOrURI', which is defined on page 5 as being any string, but strings\n\t\t// containing ':'\n\t\t// should be treated as valid URIs.\n\t\t// https://datatracker.ietf.org/doc/html/rfc7519#section-2\n\t\t//\n\t\t// It is not defined however as to whether-or-not normalized URIs should be\n\t\t// treated as the same literal\n\t\t// value. It only defines validation itself, so to avoid potential ambiguity or\n\t\t// unwanted side effects that\n\t\t// may be awkward to debug, we do not want to manipulate this value. Previous\n\t\t// versions of Spring Security\n\t\t// would *only* allow valid URLs, which is not what we wish to achieve here.\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.ISS, (k, v) -> v.toString());\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.NBF,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.SCOPE,\n\t\t\t\t(k, v) -> (v instanceof String s) ? new ArrayListFromString(s.split(\" \")) : v);\n\t\treturn () -> converted;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link Converter Converter&lt;OAuth2TokenIntrospectionClaimAccessor,\n\t * OAuth2AuthenticatedPrincipal&gt;} to use. Defaults to\n\t * {@link RestClientOpaqueTokenIntrospector#defaultAuthenticationConverter}.\n\t * </p>\n\t * <p>\n\t * Use if you need a custom mapping of OAuth 2.0 token claims to the authenticated\n\t * principal.\n\t * </p>\n\t * @param authenticationConverter the converter\n\t */\n\tpublic void setAuthenticationConverter(\n\t\t\tConverter<OAuth2TokenIntrospectionClaimAccessor, ? extends OAuth2AuthenticatedPrincipal> authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"converter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * If {@link RestClientOpaqueTokenIntrospector#authenticationConverter} is not\n\t * explicitly set, this default converter will be used. transforms an\n\t * {@link OAuth2TokenIntrospectionClaimAccessor} into an\n\t * {@link OAuth2AuthenticatedPrincipal} by extracting claims, mapping scopes to\n\t * authorities, and creating a principal.\n\t * @return {@link Converter Converter&lt;OAuth2TokenIntrospectionClaimAccessor,\n\t * OAuth2AuthenticatedPrincipal&gt;}\n\t */\n\tprivate OAuth2IntrospectionAuthenticatedPrincipal defaultAuthenticationConverter(\n\t\t\tOAuth2TokenIntrospectionClaimAccessor accessor) {\n\t\tList<String> scopes = accessor.getScopes();\n\t\tCollection<GrantedAuthority> authorities = authorities((scopes != null) ? scopes : Collections.emptyList());\n\t\treturn new OAuth2IntrospectionAuthenticatedPrincipal(accessor.getClaims(), authorities);\n\t}\n\n\tprivate Collection<GrantedAuthority> authorities(List<String> scopes) {\n\t\tif (!(scopes instanceof ArrayListFromString)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tCollection<GrantedAuthority> authorities = new ArrayList<>();\n\t\tfor (String scope : scopes) {\n\t\t\tauthorities.add(new SimpleGrantedAuthority(AUTHORITY_PREFIX + scope));\n\t\t}\n\t\treturn authorities;\n\t}\n\n\t/**\n\t * Creates a {@code RestClientOpaqueTokenIntrospector.Builder} with the given\n\t * introspection endpoint uri\n\t * @param introspectionUri The introspection endpoint uri\n\t * @return the {@link RestClientOpaqueTokenIntrospector.Builder}\n\t */\n\tpublic static RestClientOpaqueTokenIntrospector.Builder withIntrospectionUri(String introspectionUri) {\n\t\tAssert.notNull(introspectionUri, \"introspectionUri cannot be null\");\n\t\treturn new RestClientOpaqueTokenIntrospector.Builder(introspectionUri);\n\t}\n\n\t// gh-7563\n\tprivate static final class ArrayListFromString extends ArrayList<String> {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = -1804103555781637109L;\n\n\t\tArrayListFromString(String... elements) {\n\t\t\tsuper(Arrays.asList(elements));\n\t\t}\n\n\t}\n\n\t// gh-15165\n\tprivate interface ArrayListFromStringClaimAccessor extends OAuth2TokenIntrospectionClaimAccessor {\n\n\t\t@Override\n\t\tdefault @Nullable List<String> getScopes() {\n\t\t\tObject value = getClaims().get(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t\tif (value instanceof ArrayListFromString list) {\n\t\t\t\treturn list;\n\t\t\t}\n\t\t\treturn OAuth2TokenIntrospectionClaimAccessor.super.getScopes();\n\t\t}\n\n\t}\n\n\t/**\n\t * Used to build {@link RestClientOpaqueTokenIntrospector}.\n\t *\n\t * @author Andrey Litvitski\n\t * @since 7.1\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final String introspectionUri;\n\n\t\tprivate @Nullable String clientId;\n\n\t\tprivate @Nullable String clientSecret;\n\n\t\tprivate final List<Consumer<RestClientOpaqueTokenIntrospector>> postProcessors = new ArrayList<>();\n\n\t\tprivate Builder(String introspectionUri) {\n\t\t\tthis.introspectionUri = introspectionUri;\n\t\t}\n\n\t\t/**\n\t\t * The builder will {@link URLEncoder encode} the client id that you provide, so\n\t\t * please give the unencoded value.\n\t\t * @param clientId The unencoded client id\n\t\t * @return the {@link RestClientOpaqueTokenIntrospector.Builder}\n\t\t */\n\t\tpublic RestClientOpaqueTokenIntrospector.Builder clientId(String clientId) {\n\t\t\tAssert.notNull(clientId, \"clientId cannot be null\");\n\t\t\tthis.clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The builder will {@link URLEncoder encode} the client secret that you provide,\n\t\t * so please give the unencoded value.\n\t\t * @param clientSecret The unencoded client secret\n\t\t * @return the {@link RestClientOpaqueTokenIntrospector.Builder}\n\t\t */\n\t\tpublic RestClientOpaqueTokenIntrospector.Builder clientSecret(String clientSecret) {\n\t\t\tAssert.notNull(clientSecret, \"clientSecret cannot be null\");\n\t\t\tthis.clientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds a {@link Consumer} to customize the\n\t\t * {@link RestClientOpaqueTokenIntrospector} after it is built. This allows for\n\t\t * additional configuration that cannot be expressed through the builder methods.\n\t\t * @param postProcessor the {@link Consumer} to customize the introspector\n\t\t * @return the {@link RestClientOpaqueTokenIntrospector.Builder}\n\t\t */\n\t\tpublic Builder postProcessor(Consumer<RestClientOpaqueTokenIntrospector> postProcessor) {\n\t\t\tAssert.notNull(postProcessor, \"postProcessor cannot be null\");\n\t\t\tthis.postProcessors.add(postProcessor);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Creates a {@code RestClientOpaqueTokenIntrospector}\n\t\t * @return the {@link RestClientOpaqueTokenIntrospector}\n\t\t */\n\t\tpublic RestClientOpaqueTokenIntrospector build() {\n\t\t\tRestClient.Builder builder = RestClient.builder();\n\t\t\tif (this.clientId != null && this.clientSecret != null) {\n\t\t\t\tString clientId = this.clientId;\n\t\t\t\tString clientSecret = this.clientSecret;\n\t\t\t\tbuilder.defaultHeaders((headers) -> headers.setBasicAuth(clientId, clientSecret));\n\t\t\t}\n\t\t\tRestClient restClient = builder.build();\n\t\t\tRestClientOpaqueTokenIntrospector introspector = new RestClientOpaqueTokenIntrospector(\n\t\t\t\t\tthis.introspectionUri, restClient);\n\t\t\tthis.postProcessors.forEach((postProcessor) -> postProcessor.accept(introspector));\n\t\t\treturn introspector;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/SpringOpaqueTokenIntrospector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.Serial;\nimport java.net.URI;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.client.support.BasicAuthenticationInterceptor;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.RestOperations;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * A Spring implementation of {@link OpaqueTokenIntrospector} that verifies and\n * introspects a token using the configured\n * <a href=\"https://tools.ietf.org/html/rfc7662\" target=\"_blank\">OAuth 2.0 Introspection\n * Endpoint</a>.\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic class SpringOpaqueTokenIntrospector implements OpaqueTokenIntrospector {\n\n\tprivate static final String AUTHORITY_PREFIX = \"SCOPE_\";\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RestOperations restOperations;\n\n\tprivate Converter<String, RequestEntity<?>> requestEntityConverter;\n\n\tprivate Converter<OAuth2TokenIntrospectionClaimAccessor, ? extends OAuth2AuthenticatedPrincipal> authenticationConverter = this::defaultAuthenticationConverter;\n\n\t/**\n\t * Creates a {@code OpaqueTokenAuthenticationProvider} with the provided parameters\n\t * @param introspectionUri The introspection endpoint uri\n\t * @param clientId The URL-encoded client id authorized to introspect\n\t * @param clientSecret The URL-encoded client secret authorized to introspect\n\t * @deprecated Please use {@link SpringOpaqueTokenIntrospector.Builder}\n\t */\n\t@Deprecated(since = \"6.5\", forRemoval = true)\n\tpublic SpringOpaqueTokenIntrospector(String introspectionUri, String clientId, String clientSecret) {\n\t\tAssert.notNull(introspectionUri, \"introspectionUri cannot be null\");\n\t\tAssert.notNull(clientId, \"clientId cannot be null\");\n\t\tAssert.notNull(clientSecret, \"clientSecret cannot be null\");\n\t\tthis.requestEntityConverter = this.defaultRequestEntityConverter(URI.create(introspectionUri));\n\t\tRestTemplate restTemplate = new RestTemplate();\n\t\trestTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(clientId, clientSecret));\n\t\tthis.restOperations = restTemplate;\n\t}\n\n\t/**\n\t * Creates a {@code OpaqueTokenAuthenticationProvider} with the provided parameters\n\t * The given {@link RestOperations} should perform its own client authentication\n\t * against the introspection endpoint.\n\t * @param introspectionUri The introspection endpoint uri\n\t * @param restOperations The client for performing the introspection request\n\t */\n\tpublic SpringOpaqueTokenIntrospector(String introspectionUri, RestOperations restOperations) {\n\t\tAssert.notNull(introspectionUri, \"introspectionUri cannot be null\");\n\t\tAssert.notNull(restOperations, \"restOperations cannot be null\");\n\t\tthis.requestEntityConverter = this.defaultRequestEntityConverter(URI.create(introspectionUri));\n\t\tthis.restOperations = restOperations;\n\t}\n\n\tprivate Converter<String, RequestEntity<?>> defaultRequestEntityConverter(URI introspectionUri) {\n\t\treturn (token) -> {\n\t\t\tHttpHeaders headers = requestHeaders();\n\t\t\tMultiValueMap<String, String> body = requestBody(token);\n\t\t\treturn new RequestEntity<>(body, headers, HttpMethod.POST, introspectionUri);\n\t\t};\n\t}\n\n\tprivate HttpHeaders requestHeaders() {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));\n\t\treturn headers;\n\t}\n\n\tprivate MultiValueMap<String, String> requestBody(String token) {\n\t\tMultiValueMap<String, String> body = new LinkedMultiValueMap<>();\n\t\tbody.add(\"token\", token);\n\t\treturn body;\n\t}\n\n\t@Override\n\tpublic OAuth2AuthenticatedPrincipal introspect(String token) {\n\t\tRequestEntity<?> requestEntity = this.requestEntityConverter.convert(token);\n\t\tif (requestEntity == null) {\n\t\t\tthrow new OAuth2IntrospectionException(\"requestEntityConverter returned a null entity\");\n\t\t}\n\t\tResponseEntity<Map<String, Object>> responseEntity = makeRequest(requestEntity);\n\t\tMap<String, Object> claims = adaptToNimbusResponse(responseEntity);\n\t\tOAuth2TokenIntrospectionClaimAccessor accessor = convertClaimsSet(claims);\n\t\treturn this.authenticationConverter.convert(accessor);\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the OAuth 2.0 access token to a\n\t * {@link RequestEntity} representation of the OAuth 2.0 token introspection request.\n\t * @param requestEntityConverter the {@link Converter} used for converting to a\n\t * {@link RequestEntity} representation of the token introspection request\n\t */\n\tpublic void setRequestEntityConverter(Converter<String, RequestEntity<?>> requestEntityConverter) {\n\t\tAssert.notNull(requestEntityConverter, \"requestEntityConverter cannot be null\");\n\t\tthis.requestEntityConverter = requestEntityConverter;\n\t}\n\n\tprivate ResponseEntity<Map<String, Object>> makeRequest(RequestEntity<?> requestEntity) {\n\t\ttry {\n\t\t\treturn this.restOperations.exchange(requestEntity, STRING_OBJECT_MAP);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new OAuth2IntrospectionException((ex.getMessage() != null) ? ex.getMessage() : \"Invalid token\", ex);\n\t\t}\n\t}\n\n\tprivate Map<String, Object> adaptToNimbusResponse(ResponseEntity<Map<String, Object>> responseEntity) {\n\t\tif (responseEntity.getStatusCode() != HttpStatus.OK) {\n\t\t\tthrow new OAuth2IntrospectionException(\n\t\t\t\t\t\"Introspection endpoint responded with \" + responseEntity.getStatusCode());\n\t\t}\n\t\tMap<String, Object> claims = responseEntity.getBody();\n\t\t// relying solely on the authorization server to validate this token (not checking\n\t\t// 'exp', for example)\n\t\tif (claims == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\n\t\tboolean active = (boolean) claims.compute(OAuth2TokenIntrospectionClaimNames.ACTIVE, (k, v) -> {\n\t\t\tif (v instanceof String) {\n\t\t\t\treturn Boolean.parseBoolean((String) v);\n\t\t\t}\n\t\t\tif (v instanceof Boolean) {\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\tif (!active) {\n\t\t\tthis.logger.trace(\"Did not validate token since it is inactive\");\n\t\t\tthrow new BadOpaqueTokenException(\"Provided token isn't active\");\n\t\t}\n\t\treturn claims;\n\t}\n\n\tprivate ArrayListFromStringClaimAccessor convertClaimsSet(Map<String, Object> claims) {\n\t\tMap<String, Object> converted = new LinkedHashMap<>(claims);\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.AUD, (k, v) -> {\n\t\t\tif (v instanceof String) {\n\t\t\t\treturn Collections.singletonList(v);\n\t\t\t}\n\t\t\treturn v;\n\t\t});\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, (k, v) -> v.toString());\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.EXP,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.IAT,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\t// RFC-7662 page 7 directs users to RFC-7519 for defining the values of these\n\t\t// issuer fields.\n\t\t// https://datatracker.ietf.org/doc/html/rfc7662#page-7\n\t\t//\n\t\t// RFC-7519 page 9 defines issuer fields as being 'case-sensitive' strings\n\t\t// containing\n\t\t// a 'StringOrURI', which is defined on page 5 as being any string, but strings\n\t\t// containing ':'\n\t\t// should be treated as valid URIs.\n\t\t// https://datatracker.ietf.org/doc/html/rfc7519#section-2\n\t\t//\n\t\t// It is not defined however as to whether-or-not normalized URIs should be\n\t\t// treated as the same literal\n\t\t// value. It only defines validation itself, so to avoid potential ambiguity or\n\t\t// unwanted side effects that\n\t\t// may be awkward to debug, we do not want to manipulate this value. Previous\n\t\t// versions of Spring Security\n\t\t// would *only* allow valid URLs, which is not what we wish to achieve here.\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.ISS, (k, v) -> v.toString());\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.NBF,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.SCOPE,\n\t\t\t\t(k, v) -> (v instanceof String s) ? new ArrayListFromString(s.split(\" \")) : v);\n\t\treturn () -> converted;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link Converter Converter&lt;OAuth2TokenIntrospectionClaimAccessor,\n\t * OAuth2AuthenticatedPrincipal&gt;} to use. Defaults to\n\t * {@link SpringOpaqueTokenIntrospector#defaultAuthenticationConverter}.\n\t * </p>\n\t * <p>\n\t * Use if you need a custom mapping of OAuth 2.0 token claims to the authenticated\n\t * principal.\n\t * </p>\n\t * @param authenticationConverter the converter\n\t * @since 6.3\n\t */\n\tpublic void setAuthenticationConverter(\n\t\t\tConverter<OAuth2TokenIntrospectionClaimAccessor, ? extends OAuth2AuthenticatedPrincipal> authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"converter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * If {@link SpringOpaqueTokenIntrospector#authenticationConverter} is not explicitly\n\t * set, this default converter will be used. transforms an\n\t * {@link OAuth2TokenIntrospectionClaimAccessor} into an\n\t * {@link OAuth2AuthenticatedPrincipal} by extracting claims, mapping scopes to\n\t * authorities, and creating a principal.\n\t * @return {@link Converter Converter&lt;OAuth2TokenIntrospectionClaimAccessor,\n\t * OAuth2AuthenticatedPrincipal&gt;}\n\t * @since 6.3\n\t */\n\tprivate OAuth2IntrospectionAuthenticatedPrincipal defaultAuthenticationConverter(\n\t\t\tOAuth2TokenIntrospectionClaimAccessor accessor) {\n\t\tList<String> scopes = accessor.getScopes();\n\t\tCollection<GrantedAuthority> authorities = authorities((scopes != null) ? scopes : Collections.emptyList());\n\t\treturn new OAuth2IntrospectionAuthenticatedPrincipal(accessor.getClaims(), authorities);\n\t}\n\n\tprivate Collection<GrantedAuthority> authorities(List<String> scopes) {\n\t\tif (!(scopes instanceof ArrayListFromString)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tCollection<GrantedAuthority> authorities = new ArrayList<>();\n\t\tfor (String scope : scopes) {\n\t\t\tauthorities.add(new SimpleGrantedAuthority(AUTHORITY_PREFIX + scope));\n\t\t}\n\t\treturn authorities;\n\t}\n\n\t/**\n\t * Creates a {@code SpringOpaqueTokenIntrospector.Builder} with the given\n\t * introspection endpoint uri\n\t * @param introspectionUri The introspection endpoint uri\n\t * @return the {@link SpringOpaqueTokenIntrospector.Builder}\n\t * @since 6.5\n\t */\n\tpublic static Builder withIntrospectionUri(String introspectionUri) {\n\t\tAssert.notNull(introspectionUri, \"introspectionUri cannot be null\");\n\t\treturn new Builder(introspectionUri);\n\t}\n\n\t// gh-7563\n\tprivate static final class ArrayListFromString extends ArrayList<String> {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = -1804103555781637109L;\n\n\t\tArrayListFromString(String... elements) {\n\t\t\tsuper(Arrays.asList(elements));\n\t\t}\n\n\t}\n\n\t// gh-15165\n\tprivate interface ArrayListFromStringClaimAccessor extends OAuth2TokenIntrospectionClaimAccessor {\n\n\t\t@Override\n\t\tdefault @Nullable List<String> getScopes() {\n\t\t\tObject value = getClaims().get(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t\tif (value instanceof ArrayListFromString list) {\n\t\t\t\treturn list;\n\t\t\t}\n\t\t\treturn OAuth2TokenIntrospectionClaimAccessor.super.getScopes();\n\t\t}\n\n\t}\n\n\t/**\n\t * Used to build {@link SpringOpaqueTokenIntrospector}.\n\t *\n\t * @author Ngoc Nhan\n\t * @since 6.5\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final String introspectionUri;\n\n\t\tprivate @Nullable String clientId;\n\n\t\tprivate @Nullable String clientSecret;\n\n\t\tprivate final List<Consumer<SpringOpaqueTokenIntrospector>> postProcessors = new ArrayList<>();\n\n\t\tprivate Builder(String introspectionUri) {\n\t\t\tthis.introspectionUri = introspectionUri;\n\t\t}\n\n\t\t/**\n\t\t * The builder will {@link URLEncoder encode} the client id that you provide, so\n\t\t * please give the unencoded value.\n\t\t * @param clientId The unencoded client id\n\t\t * @return the {@link SpringOpaqueTokenIntrospector.Builder}\n\t\t * @since 6.5\n\t\t */\n\t\tpublic Builder clientId(String clientId) {\n\t\t\tAssert.notNull(clientId, \"clientId cannot be null\");\n\t\t\tthis.clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The builder will {@link URLEncoder encode} the client secret that you provide,\n\t\t * so please give the unencoded value.\n\t\t * @param clientSecret The unencoded client secret\n\t\t * @return the {@link SpringOpaqueTokenIntrospector.Builder}\n\t\t * @since 6.5\n\t\t */\n\t\tpublic Builder clientSecret(String clientSecret) {\n\t\t\tAssert.notNull(clientSecret, \"clientSecret cannot be null\");\n\t\t\tthis.clientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds a {@link Consumer} to customize the {@link SpringOpaqueTokenIntrospector}\n\t\t * after it is built. This allows for additional configuration that cannot be\n\t\t * expressed through the builder methods.\n\t\t * @param postProcessor the {@link Consumer} to customize the introspector\n\t\t * @return the {@link SpringOpaqueTokenIntrospector.Builder}\n\t\t * @since 7.1.0\n\t\t */\n\t\tpublic Builder postProcessor(Consumer<SpringOpaqueTokenIntrospector> postProcessor) {\n\t\t\tAssert.notNull(postProcessor, \"postProcessor cannot be null\");\n\t\t\tthis.postProcessors.add(postProcessor);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Creates a {@code SpringOpaqueTokenIntrospector}\n\t\t * @return the {@link SpringOpaqueTokenIntrospector}\n\t\t * @since 6.5\n\t\t */\n\t\tpublic SpringOpaqueTokenIntrospector build() {\n\t\t\tRestTemplate restTemplate = new RestTemplate();\n\t\t\tif (this.clientId != null && this.clientSecret != null) {\n\t\t\t\trestTemplate.getInterceptors()\n\t\t\t\t\t.add(new BasicAuthenticationInterceptor(this.clientId, this.clientSecret));\n\t\t\t}\n\t\t\tSpringOpaqueTokenIntrospector introspector = new SpringOpaqueTokenIntrospector(this.introspectionUri,\n\t\t\t\t\trestTemplate);\n\t\t\tthis.postProcessors.forEach((postProcessor) -> postProcessor.accept(introspector));\n\t\t\treturn introspector;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/SpringReactiveOpaqueTokenIntrospector.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.Serial;\nimport java.net.URI;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferUtils;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.util.Assert;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.WebClient;\n\n/**\n * A Spring implementation of {@link ReactiveOpaqueTokenIntrospector} that verifies and\n * introspects a token using the configured\n * <a href=\"https://tools.ietf.org/html/rfc7662\" target=\"_blank\">OAuth 2.0 Introspection\n * Endpoint</a>.\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic class SpringReactiveOpaqueTokenIntrospector implements ReactiveOpaqueTokenIntrospector {\n\n\tprivate static final String AUTHORITY_PREFIX = \"SCOPE_\";\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate final URI introspectionUri;\n\n\tprivate final WebClient webClient;\n\n\tprivate Converter<OAuth2TokenIntrospectionClaimAccessor, Mono<? extends OAuth2AuthenticatedPrincipal>> authenticationConverter = this::defaultAuthenticationConverter;\n\n\t/**\n\t * Creates a {@code OpaqueTokenReactiveAuthenticationManager} with the provided\n\t * parameters\n\t * @param introspectionUri The introspection endpoint uri\n\t * @param clientId The URL-encoded client id authorized to introspect\n\t * @param clientSecret The URL-encoded client secret authorized to introspect\n\t * @deprecated Please use {@link SpringReactiveOpaqueTokenIntrospector.Builder}\n\t */\n\t@Deprecated(since = \"6.5\", forRemoval = true)\n\tpublic SpringReactiveOpaqueTokenIntrospector(String introspectionUri, String clientId, String clientSecret) {\n\t\tAssert.hasText(introspectionUri, \"introspectionUri cannot be empty\");\n\t\tAssert.hasText(clientId, \"clientId cannot be empty\");\n\t\tAssert.notNull(clientSecret, \"clientSecret cannot be null\");\n\t\tthis.introspectionUri = URI.create(introspectionUri);\n\t\tthis.webClient = WebClient.builder().defaultHeaders((h) -> h.setBasicAuth(clientId, clientSecret)).build();\n\t}\n\n\t/**\n\t * Creates a {@code OpaqueTokenReactiveAuthenticationManager} with the provided\n\t * parameters\n\t * @param introspectionUri The introspection endpoint uri\n\t * @param webClient The client for performing the introspection request\n\t */\n\tpublic SpringReactiveOpaqueTokenIntrospector(String introspectionUri, WebClient webClient) {\n\t\tAssert.hasText(introspectionUri, \"introspectionUri cannot be null\");\n\t\tAssert.notNull(webClient, \"webClient cannot be null\");\n\t\tthis.introspectionUri = URI.create(introspectionUri);\n\t\tthis.webClient = webClient;\n\t}\n\n\t@Override\n\tpublic Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {\n\t\t// @formatter:off\n\t\treturn Mono.just(token)\n\t\t\t\t.flatMap(this::makeRequest)\n\t\t\t\t.map(this::convertClaimsSet)\n\t\t\t\t.flatMap(this.authenticationConverter::convert)\n\t\t\t\t.cast(OAuth2AuthenticatedPrincipal.class)\n\t\t\t\t.onErrorMap((e) -> !(e instanceof OAuth2IntrospectionException), this::onError);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Map<String, Object>> makeRequest(String token) {\n\t\t// @formatter:off\n\t\treturn this.webClient.post()\n\t\t\t\t.uri(this.introspectionUri)\n\t\t\t\t.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t\t.body(BodyInserters.fromFormData(\"token\", token))\n\t\t\t\t.exchangeToMono(this::adaptToNimbusResponse);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Map<String, Object>> adaptToNimbusResponse(ClientResponse responseEntity) {\n\t\tif (responseEntity.statusCode() != HttpStatus.OK) {\n\t\t\t// @formatter:off\n\t\t\treturn responseEntity.bodyToFlux(DataBuffer.class)\n\t\t\t\t\t.map(DataBufferUtils::release)\n\t\t\t\t\t.then(Mono.error(new OAuth2IntrospectionException(\n\t\t\t\t\t\t\t\"Introspection endpoint responded with \" + responseEntity.statusCode()))\n\t\t\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\t\t// relying solely on the authorization server to validate this token (not checking\n\t\t// 'exp', for example)\n\t\treturn responseEntity.bodyToMono(STRING_OBJECT_MAP)\n\t\t\t.filter((body) -> (boolean) body.compute(OAuth2TokenIntrospectionClaimNames.ACTIVE, (k, v) -> {\n\t\t\t\tif (v instanceof String) {\n\t\t\t\t\treturn Boolean.parseBoolean((String) v);\n\t\t\t\t}\n\t\t\t\tif (v instanceof Boolean) {\n\t\t\t\t\treturn v;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}))\n\t\t\t.switchIfEmpty(Mono.error(() -> new BadOpaqueTokenException(\"Provided token isn't active\")));\n\t}\n\n\tprivate ArrayListFromStringClaimAccessor convertClaimsSet(Map<String, Object> claims) {\n\t\tMap<String, Object> converted = new LinkedHashMap<>(claims);\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.AUD, (k, v) -> {\n\t\t\tif (v instanceof String) {\n\t\t\t\treturn Collections.singletonList(v);\n\t\t\t}\n\t\t\treturn v;\n\t\t});\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, (k, v) -> v.toString());\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.EXP,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.IAT,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\t// RFC-7662 page 7 directs users to RFC-7519 for defining the values of these\n\t\t// issuer fields.\n\t\t// https://datatracker.ietf.org/doc/html/rfc7662#page-7\n\t\t//\n\t\t// RFC-7519 page 9 defines issuer fields as being 'case-sensitive' strings\n\t\t// containing\n\t\t// a 'StringOrURI', which is defined on page 5 as being any string, but strings\n\t\t// containing ':'\n\t\t// should be treated as valid URIs.\n\t\t// https://datatracker.ietf.org/doc/html/rfc7519#section-2\n\t\t//\n\t\t// It is not defined however as to whether-or-not normalized URIs should be\n\t\t// treated as the same literal\n\t\t// value. It only defines validation itself, so to avoid potential ambiguity or\n\t\t// unwanted side effects that\n\t\t// may be awkward to debug, we do not want to manipulate this value. Previous\n\t\t// versions of Spring Security\n\t\t// would *only* allow valid URLs, which is not what we wish to achieve here.\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.ISS, (k, v) -> v.toString());\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.NBF,\n\t\t\t\t(k, v) -> Instant.ofEpochSecond(((Number) v).longValue()));\n\t\tconverted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.SCOPE,\n\t\t\t\t(k, v) -> (v instanceof String s) ? new ArrayListFromString(s.split(\" \")) : v);\n\t\treturn () -> converted;\n\t}\n\n\tprivate OAuth2IntrospectionException onError(Throwable ex) {\n\t\treturn new OAuth2IntrospectionException((ex.getMessage() != null) ? ex.getMessage() : \"Invalid token\", ex);\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link Converter Converter&lt;OAuth2TokenIntrospectionClaimAccessor,\n\t * OAuth2AuthenticatedPrincipal&gt;} to use. Defaults to\n\t * {@link SpringReactiveOpaqueTokenIntrospector#defaultAuthenticationConverter}.\n\t * </p>\n\t * <p>\n\t * Use if you need a custom mapping of OAuth 2.0 token claims to the authenticated\n\t * principal.\n\t * </p>\n\t * @param authenticationConverter the converter\n\t * @since 6.3\n\t */\n\tpublic void setAuthenticationConverter(\n\t\t\tConverter<OAuth2TokenIntrospectionClaimAccessor, Mono<? extends OAuth2AuthenticatedPrincipal>> authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\tprivate Mono<OAuth2IntrospectionAuthenticatedPrincipal> defaultAuthenticationConverter(\n\t\t\tOAuth2TokenIntrospectionClaimAccessor accessor) {\n\t\tList<String> scopes = accessor.getScopes();\n\t\tCollection<GrantedAuthority> authorities = authorities((scopes != null) ? scopes : Collections.emptyList());\n\t\treturn Mono.just(new OAuth2IntrospectionAuthenticatedPrincipal(accessor.getClaims(), authorities));\n\t}\n\n\tprivate Collection<GrantedAuthority> authorities(List<String> scopes) {\n\t\tif (!(scopes instanceof ArrayListFromString)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tCollection<GrantedAuthority> authorities = new ArrayList<>();\n\t\tfor (String scope : scopes) {\n\t\t\tauthorities.add(new SimpleGrantedAuthority(AUTHORITY_PREFIX + scope));\n\t\t}\n\t\treturn authorities;\n\t}\n\n\t/**\n\t * Creates a {@code SpringReactiveOpaqueTokenIntrospector.Builder} with the given\n\t * introspection endpoint uri\n\t * @param introspectionUri The introspection endpoint uri\n\t * @return the {@link SpringReactiveOpaqueTokenIntrospector.Builder}\n\t * @since 6.5\n\t */\n\tpublic static Builder withIntrospectionUri(String introspectionUri) {\n\n\t\treturn new Builder(introspectionUri);\n\t}\n\n\t// gh-7563\n\tprivate static final class ArrayListFromString extends ArrayList<String> {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 9182779930765511117L;\n\n\t\tArrayListFromString(String... elements) {\n\t\t\tsuper(Arrays.asList(elements));\n\t\t}\n\n\t}\n\n\t// gh-15165\n\tprivate interface ArrayListFromStringClaimAccessor extends OAuth2TokenIntrospectionClaimAccessor {\n\n\t\t@Override\n\t\tdefault @Nullable List<String> getScopes() {\n\t\t\tObject value = getClaims().get(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t\tif (value instanceof ArrayListFromString list) {\n\t\t\t\treturn list;\n\t\t\t}\n\t\t\treturn OAuth2TokenIntrospectionClaimAccessor.super.getScopes();\n\t\t}\n\n\t}\n\n\t/**\n\t * Used to build {@link SpringReactiveOpaqueTokenIntrospector}.\n\t *\n\t * @author Ngoc Nhan\n\t * @since 6.5\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final String introspectionUri;\n\n\t\tprivate @Nullable String clientId;\n\n\t\tprivate @Nullable String clientSecret;\n\n\t\tprivate final List<Consumer<SpringReactiveOpaqueTokenIntrospector>> postProcessors = new ArrayList<>();\n\n\t\tprivate Builder(String introspectionUri) {\n\t\t\tthis.introspectionUri = introspectionUri;\n\t\t}\n\n\t\t/**\n\t\t * The builder will {@link URLEncoder encode} the client id that you provide, so\n\t\t * please give the unencoded value.\n\t\t * @param clientId The unencoded client id\n\t\t * @return the {@link SpringReactiveOpaqueTokenIntrospector.Builder}\n\t\t * @since 6.5\n\t\t */\n\t\tpublic Builder clientId(String clientId) {\n\t\t\tAssert.notNull(clientId, \"clientId cannot be null\");\n\t\t\tthis.clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The builder will {@link URLEncoder encode} the client secret that you provide,\n\t\t * so please give the unencoded value.\n\t\t * @param clientSecret The unencoded client secret\n\t\t * @return the {@link SpringReactiveOpaqueTokenIntrospector.Builder}\n\t\t * @since 6.5\n\t\t */\n\t\tpublic Builder clientSecret(String clientSecret) {\n\t\t\tAssert.notNull(clientSecret, \"clientSecret cannot be null\");\n\t\t\tthis.clientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds a {@link Consumer} to customize the\n\t\t * {@link SpringReactiveOpaqueTokenIntrospector} after it is built. This allows\n\t\t * for additional configuration that cannot be expressed through the builder\n\t\t * methods.\n\t\t * @param postProcessor the {@link Consumer} to customize the introspector\n\t\t * @return the {@link SpringReactiveOpaqueTokenIntrospector.Builder}\n\t\t * @since 7.1.0\n\t\t */\n\t\tpublic Builder postProcessor(Consumer<SpringReactiveOpaqueTokenIntrospector> postProcessor) {\n\t\t\tAssert.notNull(postProcessor, \"postProcessor cannot be null\");\n\t\t\tthis.postProcessors.add(postProcessor);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Creates a {@code SpringReactiveOpaqueTokenIntrospector}\n\t\t * @return the {@link SpringReactiveOpaqueTokenIntrospector}\n\t\t * @since 6.5\n\t\t */\n\t\tpublic SpringReactiveOpaqueTokenIntrospector build() {\n\t\t\tWebClient.Builder builder = WebClient.builder();\n\t\t\tif (this.clientId != null && this.clientSecret != null) {\n\t\t\t\tString clientId = this.clientId;\n\t\t\t\tString clientSecret = this.clientSecret;\n\t\t\t\tbuilder.defaultHeaders((h) -> h.setBasicAuth(clientId, clientSecret));\n\t\t\t}\n\t\t\tWebClient webClient = builder.build();\n\t\t\tSpringReactiveOpaqueTokenIntrospector introspector = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\t\tthis.introspectionUri, webClient);\n\t\t\tthis.postProcessors.forEach((postProcessor) -> postProcessor.accept(introspector));\n\t\t\treturn introspector;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Introspection supporting classes and interfaces.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server core classes and interfaces providing support.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * An {@link AuthenticationEntryPoint} implementation used to commence authentication of\n * protected resource requests using {@link BearerTokenAuthenticationFilter}.\n * <p>\n * Uses information provided by {@link BearerTokenError} to set HTTP response status code\n * and populate {@code WWW-Authenticate} HTTP header.\n *\n * @author Vedran Pavic\n * @since 5.1\n * @see BearerTokenError\n * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-3\" target=\"_blank\">RFC 6750\n * Section 3: The WWW-Authenticate Response Header Field</a>\n */\npublic final class BearerTokenAuthenticationEntryPoint implements AuthenticationEntryPoint {\n\n\tprivate @Nullable String realmName;\n\n\tprivate Function<HttpServletRequest, String> resourceMetadataParameterResolver = BearerTokenAuthenticationEntryPoint::getResourceMetadataParameter;\n\n\t/**\n\t * Collect error details from the provided parameters and format according to RFC\n\t * 6750, specifically {@code error}, {@code error_description}, {@code error_uri}, and\n\t * {@code scope}.\n\t * @param request that resulted in an <code>AuthenticationException</code>\n\t * @param response so that the user agent can begin authentication\n\t * @param authException that caused the invocation\n\t */\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authException) {\n\t\tHttpStatus status = HttpStatus.UNAUTHORIZED;\n\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\tif (this.realmName != null) {\n\t\t\tparameters.put(\"realm\", this.realmName);\n\t\t}\n\t\tif (authException instanceof OAuth2AuthenticationException oAuth2AuthenticationException) {\n\t\t\tOAuth2Error error = oAuth2AuthenticationException.getError();\n\t\t\tparameters.put(\"error\", error.getErrorCode());\n\t\t\tif (StringUtils.hasText(error.getDescription())) {\n\t\t\t\tparameters.put(\"error_description\", error.getDescription());\n\t\t\t}\n\t\t\tif (StringUtils.hasText(error.getUri())) {\n\t\t\t\tparameters.put(\"error_uri\", error.getUri());\n\t\t\t}\n\t\t\tif (error instanceof BearerTokenError bearerTokenError) {\n\t\t\t\tif (StringUtils.hasText(bearerTokenError.getScope())) {\n\t\t\t\t\tparameters.put(\"scope\", bearerTokenError.getScope());\n\t\t\t\t}\n\t\t\t\tstatus = bearerTokenError.getHttpStatus();\n\t\t\t}\n\t\t}\n\t\tparameters.put(\"resource_metadata\", this.resourceMetadataParameterResolver.apply(request));\n\t\tString wwwAuthenticate = computeWWWAuthenticateHeaderValue(parameters);\n\t\tresponse.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);\n\t\tresponse.setStatus(status.value());\n\t}\n\n\t/**\n\t * Set the default realm name to use in the bearer token error response\n\t * @param realmName the realm name, or {@code null}\n\t */\n\tpublic void setRealmName(@Nullable String realmName) {\n\t\tthis.realmName = realmName;\n\t}\n\n\t/**\n\t * Set the resolver to compute the {@code resource_metadata} parameter from the\n\t * request.\n\t * @param resourceMetadataParameterResolver\n\t * @since 7.1\n\t */\n\tpublic void setResourceMetadataParameterResolver(\n\t\t\tFunction<HttpServletRequest, String> resourceMetadataParameterResolver) {\n\t\tAssert.notNull(resourceMetadataParameterResolver, \"resourceMetadataParameterResolver cannot be null\");\n\t\tthis.resourceMetadataParameterResolver = resourceMetadataParameterResolver;\n\t}\n\n\tprivate static String getResourceMetadataParameter(HttpServletRequest request) {\n\t\tString path = request.getContextPath()\n\t\t\t\t+ OAuth2ProtectedResourceMetadataFilter.DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI;\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t\t.replacePath(path)\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.fragment(null)\n\t\t\t\t.build()\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate static String computeWWWAuthenticateHeaderValue(Map<String, String> parameters) {\n\t\tStringBuilder wwwAuthenticate = new StringBuilder();\n\t\twwwAuthenticate.append(\"Bearer\");\n\t\tif (!parameters.isEmpty()) {\n\t\t\twwwAuthenticate.append(\" \");\n\t\t\tint i = 0;\n\t\t\tfor (Map.Entry<String, String> entry : parameters.entrySet()) {\n\t\t\t\twwwAuthenticate.append(entry.getKey()).append(\"=\\\"\").append(entry.getValue()).append(\"\\\"\");\n\t\t\t\tif (i != parameters.size() - 1) {\n\t\t\t\t\twwwAuthenticate.append(\", \");\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\treturn wwwAuthenticate.toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\n\n/**\n * A strategy for resolving\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>s from the {@link HttpServletRequest}.\n *\n * @author Vedran Pavic\n * @since 5.1\n * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-2\" target=\"_blank\">RFC 6750\n * Section 2: Authenticated Requests</a>\n */\n@FunctionalInterface\npublic interface BearerTokenResolver {\n\n\t/**\n\t * Resolve any\n\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n\t * Token</a> value from the request.\n\t * @param request the request\n\t * @return the Bearer Token value or {@code null} if none found\n\t * @throws OAuth2AuthenticationException if the found token is invalid\n\t */\n\t@Nullable String resolve(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrors;\nimport org.springframework.util.StringUtils;\n\n/**\n * The default {@link BearerTokenResolver} implementation based on RFC 6750.\n *\n * @author Vedran Pavic\n * @since 5.1\n * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-2\" target=\"_blank\">RFC 6750\n * Section 2: Authenticated Requests</a>\n */\npublic final class DefaultBearerTokenResolver implements BearerTokenResolver {\n\n\tprivate static final String ACCESS_TOKEN_PARAMETER_NAME = \"access_token\";\n\n\tprivate static final Pattern authorizationPattern = Pattern.compile(\"^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$\",\n\t\t\tPattern.CASE_INSENSITIVE);\n\n\tprivate boolean allowFormEncodedBodyParameter = false;\n\n\tprivate boolean allowUriQueryParameter = false;\n\n\tprivate String bearerTokenHeaderName = HttpHeaders.AUTHORIZATION;\n\n\t@Override\n\tpublic @Nullable String resolve(final HttpServletRequest request) {\n\t\t// @formatter:off\n\t\treturn resolveToken(\n\t\t\tresolveFromAuthorizationHeader(request),\n\t\t\tresolveAccessTokenFromQueryString(request),\n\t\t\tresolveAccessTokenFromBody(request)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate static @Nullable String resolveToken(@Nullable String... accessTokens) {\n\t\tif (accessTokens == null || accessTokens.length == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString accessToken = null;\n\t\tfor (String token : accessTokens) {\n\t\t\tif (accessToken == null) {\n\t\t\t\taccessToken = token;\n\t\t\t}\n\t\t\telse if (token != null) {\n\t\t\t\tBearerTokenError error = BearerTokenErrors\n\t\t\t\t\t.invalidRequest(\"Found multiple bearer tokens in the request\");\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\t\t}\n\n\t\tif (accessToken != null && accessToken.isBlank()) {\n\t\t\tBearerTokenError error = BearerTokenErrors\n\t\t\t\t.invalidRequest(\"The requested token parameter is an empty string\");\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\treturn accessToken;\n\t}\n\n\tprivate @Nullable String resolveFromAuthorizationHeader(HttpServletRequest request) {\n\t\tString authorization = request.getHeader(this.bearerTokenHeaderName);\n\t\tif (!StringUtils.startsWithIgnoreCase(authorization, \"bearer\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tMatcher matcher = authorizationPattern.matcher(authorization);\n\t\tif (!matcher.matches()) {\n\t\t\tBearerTokenError error = BearerTokenErrors.invalidToken(\"Bearer token is malformed\");\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\treturn matcher.group(\"token\");\n\t}\n\n\tprivate @Nullable String resolveAccessTokenFromQueryString(HttpServletRequest request) {\n\t\tif (!this.allowUriQueryParameter || !HttpMethod.GET.name().equals(request.getMethod())) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn resolveToken(request.getParameterValues(ACCESS_TOKEN_PARAMETER_NAME));\n\t}\n\n\tprivate @Nullable String resolveAccessTokenFromBody(HttpServletRequest request) {\n\t\tif (!this.allowFormEncodedBodyParameter\n\t\t\t\t|| !MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType())\n\t\t\t\t|| HttpMethod.GET.name().equals(request.getMethod())) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString queryString = request.getQueryString();\n\t\tif (queryString != null && queryString.contains(ACCESS_TOKEN_PARAMETER_NAME)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn resolveToken(request.getParameterValues(ACCESS_TOKEN_PARAMETER_NAME));\n\t}\n\n\t/**\n\t * Set if transport of access token using form-encoded body parameter is supported.\n\t * Defaults to {@code false}.\n\t * @param allowFormEncodedBodyParameter if the form-encoded body parameter is\n\t * supported\n\t */\n\tpublic void setAllowFormEncodedBodyParameter(boolean allowFormEncodedBodyParameter) {\n\t\tthis.allowFormEncodedBodyParameter = allowFormEncodedBodyParameter;\n\t}\n\n\t/**\n\t * Set if transport of access token using URI query parameter is supported. Defaults\n\t * to {@code false}.\n\t *\n\t * The spec recommends against using this mechanism for sending bearer tokens, and\n\t * even goes as far as stating that it was only included for completeness.\n\t * @param allowUriQueryParameter if the URI query parameter is supported\n\t */\n\tpublic void setAllowUriQueryParameter(boolean allowUriQueryParameter) {\n\t\tthis.allowUriQueryParameter = allowUriQueryParameter;\n\t}\n\n\t/**\n\t * Set this value to configure what header is checked when resolving a Bearer Token.\n\t * This value is defaulted to {@link HttpHeaders#AUTHORIZATION}.\n\t *\n\t * This allows other headers to be used as the Bearer Token source such as\n\t * {@link HttpHeaders#PROXY_AUTHORIZATION}\n\t * @param bearerTokenHeaderName the header to check when retrieving the Bearer Token.\n\t * @since 5.4\n\t */\n\tpublic void setBearerTokenHeaderName(String bearerTokenHeaderName) {\n\t\tthis.bearerTokenHeaderName = bearerTokenHeaderName;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/GenericHttpMessageConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\n\n/**\n * {@link GenericHttpMessageConverter} implementation that delegates to a\n * {@link SmartHttpMessageConverter}.\n *\n * @param <T> the converted object type\n * @author Sebastien Deleuze\n * @since 7.0\n */\nfinal class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {\n\n\tprivate final SmartHttpMessageConverter<T> smartConverter;\n\n\tGenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {\n\t\tthis.smartConverter = smartConverter;\n\t}\n\n\t@Override\n\tpublic boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forType(type), mediaType);\n\t}\n\n\t@Override\n\tpublic T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\treturn this.smartConverter.getSupportedMediaTypes();\n\t}\n\n\t@Override\n\tpublic T read(Class<? extends T> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(clazz, inputMessage);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, contentType, outputMessage);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/HeaderBearerTokenResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.util.Assert;\n\n/**\n * Generic resolver extracting pre-authenticated JWT identity from a custom header.\n *\n * @author Elena Felder\n * @since 5.2\n */\npublic class HeaderBearerTokenResolver implements BearerTokenResolver {\n\n\tprivate String header;\n\n\tpublic HeaderBearerTokenResolver(String header) {\n\t\tAssert.hasText(header, \"header cannot be empty\");\n\t\tthis.header = header;\n\t}\n\n\t@Override\n\tpublic String resolve(HttpServletRequest request) {\n\t\treturn request.getHeader(this.header);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/OAuth2ProtectedResourceMetadataFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.json.GsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JsonbHttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.oauth2.server.resource.OAuth2ProtectedResourceMetadata;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A {@code Filter} that processes OAuth 2.0 Protected Resource Metadata Requests.\n *\n * @author Joe Grandja\n * @since 7.0\n * @see OAuth2ProtectedResourceMetadata\n * @see <a target=\"_blank\" href=\n * \"https://www.rfc-editor.org/rfc/rfc9728.html#section-3.1\">3.1. Protected Resource\n * Metadata Request</a>\n */\npublic final class OAuth2ProtectedResourceMetadataFilter extends OncePerRequestFilter {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() {\n\t};\n\n\tprivate static final @Nullable GenericHttpMessageConverter<Object> JSON_MESSAGE_CONVERTER = HttpMessageConverters\n\t\t.getJsonMessageConverter();\n\n\t/**\n\t * The default endpoint {@code URI} for OAuth 2.0 Protected Resource Metadata\n\t * requests.\n\t */\n\tstatic final String DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI = \"/.well-known/oauth-protected-resource\";\n\n\tprivate final RequestMatcher requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.GET, DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI.concat(\"/**\"));\n\n\tprivate Consumer<OAuth2ProtectedResourceMetadata.Builder> protectedResourceMetadataCustomizer = (\n\t\t\tprotectedResourceMetadata) -> {\n\t};\n\n\t/**\n\t * Sets the {@code Consumer} providing access to the\n\t * {@link OAuth2ProtectedResourceMetadata.Builder} allowing the ability to customize\n\t * the claims of the Resource Server's configuration.\n\t * @param protectedResourceMetadataCustomizer the {@code Consumer} providing access to\n\t * the {@link OAuth2ProtectedResourceMetadata.Builder}\n\t */\n\tpublic void setProtectedResourceMetadataCustomizer(\n\t\t\tConsumer<OAuth2ProtectedResourceMetadata.Builder> protectedResourceMetadataCustomizer) {\n\t\tAssert.notNull(protectedResourceMetadataCustomizer, \"protectedResourceMetadataCustomizer cannot be null\");\n\t\tthis.protectedResourceMetadataCustomizer = protectedResourceMetadataCustomizer;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tOAuth2ProtectedResourceMetadata.Builder builder = OAuth2ProtectedResourceMetadata.builder()\n\t\t\t.resource(resolveResourceIdentifier(request))\n\t\t\t.bearerMethod(\"header\")\n\t\t\t.tlsClientCertificateBoundAccessTokens(true);\n\n\t\tthis.protectedResourceMetadataCustomizer.accept(builder);\n\n\t\tOAuth2ProtectedResourceMetadata protectedResourceMetadata = builder.build();\n\n\t\ttry {\n\t\t\tGenericHttpMessageConverter<Object> converter = JSON_MESSAGE_CONVERTER;\n\t\t\tAssert.notNull(converter, \"No JSON message converter available\");\n\t\t\tServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);\n\t\t\tconverter.write(protectedResourceMetadata.getClaims(), STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, httpResponse);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the OAuth 2.0 Protected Resource Metadata: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\tprivate static String resolveResourceIdentifier(HttpServletRequest request) {\n\t\t// Resolve Resource Identifier dynamically from request\n\t\tString path = request.getRequestURI();\n\t\tif (!StringUtils.hasText(path)) {\n\t\t\tpath = \"\";\n\t\t}\n\t\telse {\n\t\t\tpath = path.replace(DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI, \"\");\n\t\t}\n\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t\t.replacePath(path)\n\t\t\t\t.replaceQuery(null)\n\t\t\t\t.fragment(null)\n\t\t\t\t.build()\n\t\t\t\t.toUriString();\n\t\t// @formatter:on\n\t}\n\n\tprivate static final class HttpMessageConverters {\n\n\t\tprivate static final boolean jacksonPresent;\n\n\t\tprivate static final boolean jackson2Present;\n\n\t\tprivate static final boolean gsonPresent;\n\n\t\tprivate static final boolean jsonbPresent;\n\n\t\tstatic {\n\t\t\tClassLoader classLoader = HttpMessageConverters.class.getClassLoader();\n\t\t\tjacksonPresent = ClassUtils.isPresent(\"tools.jackson.databind.json.JsonMapper\", classLoader);\n\t\t\tjackson2Present = ClassUtils.isPresent(\"com.fasterxml.jackson.databind.ObjectMapper\", classLoader)\n\t\t\t\t\t&& ClassUtils.isPresent(\"com.fasterxml.jackson.core.JsonGenerator\", classLoader);\n\t\t\tgsonPresent = ClassUtils.isPresent(\"com.google.gson.Gson\", classLoader);\n\t\t\tjsonbPresent = ClassUtils.isPresent(\"jakarta.json.bind.Jsonb\", classLoader);\n\t\t}\n\n\t\tprivate HttpMessageConverters() {\n\t\t}\n\n\t\t@SuppressWarnings(\"removal\")\n\t\tprivate static @Nullable GenericHttpMessageConverter<Object> getJsonMessageConverter() {\n\t\t\tif (jacksonPresent) {\n\t\t\t\treturn new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());\n\t\t\t}\n\t\t\tif (jackson2Present) {\n\t\t\t\treturn new MappingJackson2HttpMessageConverter();\n\t\t\t}\n\t\t\tif (gsonPresent) {\n\t\t\t\treturn new GsonHttpMessageConverter();\n\t\t\t}\n\t\t\tif (jsonbPresent) {\n\t\t\t\treturn new JsonbHttpMessageConverter();\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/BearerTokenAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.access;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.security.web.access.AccessDeniedHandler;\n\n/**\n * Translates any {@link AccessDeniedException} into an HTTP response in accordance with\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-3\" target=\"_blank\">RFC 6750\n * Section 3: The WWW-Authenticate</a>.\n * <p>\n * So long as the class can prove that the request has a valid OAuth 2.0\n * {@link Authentication}, then will return an\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-3.1\" target=\"_blank\">insufficient\n * scope error</a>; otherwise, it will simply indicate the scheme (Bearer) and any\n * configured realm.\n *\n * @author Josh Cummings\n * @since 5.1\n */\npublic final class BearerTokenAccessDeniedHandler implements AccessDeniedHandler {\n\n\tprivate @Nullable String realmName;\n\n\t/**\n\t * Collect error details from the provided parameters and format according to RFC\n\t * 6750, specifically {@code error}, {@code error_description}, {@code error_uri}, and\n\t * {@code scope}.\n\t * @param request that resulted in an <code>AccessDeniedException</code>\n\t * @param response so that the user agent can be advised of the failure\n\t * @param accessDeniedException that caused the invocation\n\t */\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAccessDeniedException accessDeniedException) {\n\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\tif (this.realmName != null) {\n\t\t\tparameters.put(\"realm\", this.realmName);\n\t\t}\n\t\tif (request.getUserPrincipal() instanceof AbstractOAuth2TokenAuthenticationToken) {\n\t\t\tparameters.put(\"error\", BearerTokenErrorCodes.INSUFFICIENT_SCOPE);\n\t\t\tparameters.put(\"error_description\",\n\t\t\t\t\t\"The request requires higher privileges than provided by the access token.\");\n\t\t\tparameters.put(\"error_uri\", \"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t}\n\t\tString wwwAuthenticate = computeWWWAuthenticateHeaderValue(parameters);\n\t\tresponse.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);\n\t\tresponse.setStatus(HttpStatus.FORBIDDEN.value());\n\t}\n\n\t/**\n\t * Set the default realm name to use in the bearer token error response\n\t * @param realmName\n\t */\n\tpublic void setRealmName(@Nullable String realmName) {\n\t\tthis.realmName = realmName;\n\t}\n\n\tprivate static String computeWWWAuthenticateHeaderValue(Map<String, String> parameters) {\n\t\tStringBuilder wwwAuthenticate = new StringBuilder();\n\t\twwwAuthenticate.append(\"Bearer\");\n\t\tif (!parameters.isEmpty()) {\n\t\t\twwwAuthenticate.append(\" \");\n\t\t\tint i = 0;\n\t\t\tfor (Map.Entry<String, String> entry : parameters.entrySet()) {\n\t\t\t\twwwAuthenticate.append(entry.getKey()).append(\"=\\\"\").append(entry.getValue()).append(\"\\\"\");\n\t\t\t\tif (i != parameters.size() - 1) {\n\t\t\t\t\twwwAuthenticate.append(\", \");\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\treturn wwwAuthenticate.toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server access denial classes and interfaces.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.web.access;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/server/BearerTokenServerAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.access.server;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Translates any {@link AccessDeniedException} into an HTTP response in accordance with\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-3\" target=\"_blank\">RFC 6750\n * Section 3: The WWW-Authenticate</a>.\n *\n * So long as the class can prove that the request has a valid OAuth 2.0\n * {@link Authentication}, then will return an\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-3.1\" target=\"_blank\">insufficient\n * scope error</a>; otherwise, it will simply indicate the scheme (Bearer) and any\n * configured realm.\n *\n * @author Josh Cummings\n * @since 5.1\n *\n */\npublic class BearerTokenServerAccessDeniedHandler implements ServerAccessDeniedHandler {\n\n\tprivate static final Collection<String> WELL_KNOWN_SCOPE_ATTRIBUTE_NAMES = Arrays.asList(\"scope\", \"scp\");\n\n\tprivate @Nullable String realmName;\n\n\t@Override\n\tpublic Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {\n\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\tif (this.realmName != null) {\n\t\t\tparameters.put(\"realm\", this.realmName);\n\t\t}\n\t\t// @formatter:off\n\t\treturn exchange.getPrincipal()\n\t\t\t\t.filter(AbstractOAuth2TokenAuthenticationToken.class::isInstance)\n\t\t\t\t.map((token) -> errorMessageParameters(parameters))\n\t\t\t\t.switchIfEmpty(Mono.just(parameters))\n\t\t\t\t.flatMap((params) -> respond(exchange, params));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Set the default realm name to use in the bearer token error response\n\t * @param realmName\n\t */\n\tpublic final void setRealmName(@Nullable String realmName) {\n\t\tthis.realmName = realmName;\n\t}\n\n\tprivate static Map<String, String> errorMessageParameters(Map<String, String> parameters) {\n\t\tparameters.put(\"error\", BearerTokenErrorCodes.INSUFFICIENT_SCOPE);\n\t\tparameters.put(\"error_description\",\n\t\t\t\t\"The request requires higher privileges than provided by the access token.\");\n\t\tparameters.put(\"error_uri\", \"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\treturn parameters;\n\t}\n\n\tprivate static Mono<Void> respond(ServerWebExchange exchange, Map<String, String> parameters) {\n\t\tString wwwAuthenticate = computeWWWAuthenticateHeaderValue(parameters);\n\t\texchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);\n\t\texchange.getResponse().getHeaders().set(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);\n\t\treturn exchange.getResponse().setComplete();\n\t}\n\n\tprivate static String computeWWWAuthenticateHeaderValue(Map<String, String> parameters) {\n\t\tStringBuilder wwwAuthenticate = new StringBuilder();\n\t\twwwAuthenticate.append(\"Bearer\");\n\t\tif (!parameters.isEmpty()) {\n\t\t\twwwAuthenticate.append(\" \");\n\t\t\tint i = 0;\n\t\t\tfor (Map.Entry<String, String> entry : parameters.entrySet()) {\n\t\t\t\twwwAuthenticate.append(entry.getKey()).append(\"=\\\"\").append(entry.getValue()).append(\"\\\"\");\n\t\t\t\tif (i != parameters.size() - 1) {\n\t\t\t\t\twwwAuthenticate.append(\", \");\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\treturn wwwAuthenticate.toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/access/server/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server WebFlux access denied handlers.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.web.access.server;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;\nimport org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Implementation of {@link AuthenticationConverter}, that converts request to\n * {@link BearerTokenAuthenticationToken}\n *\n * @author Max Batischev\n * @author Josh Cummings\n * @since 7.0\n */\npublic final class BearerTokenAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();\n\n\t@Override\n\tpublic @Nullable Authentication convert(HttpServletRequest request) {\n\t\tString token = this.bearerTokenResolver.resolve(request);\n\t\tif (StringUtils.hasText(token)) {\n\t\t\tBearerTokenAuthenticationToken authenticationToken = new BearerTokenAuthenticationToken(token);\n\t\t\tauthenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\treturn authenticationToken;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) {\n\t\tAssert.notNull(bearerTokenResolver, \"bearerTokenResolver cannot be null\");\n\t\tthis.bearerTokenResolver = bearerTokenResolver;\n\t}\n\n\t/**\n\t * Set the {@link AuthenticationDetailsSource} to use. Defaults to\n\t * {@link WebAuthenticationDetailsSource}.\n\t * @param authenticationDetailsSource the {@code AuthenticationDetailsSource} to use\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"authenticationDetailsSource cannot be null\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.authentication;\n\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.oauth2.core.ClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrors;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;\nimport org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;\nimport org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Authenticates requests that contain an OAuth 2.0\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>.\n *\n * This filter should be wired with an {@link AuthenticationManager} that can authenticate\n * a {@link BearerTokenAuthenticationToken}.\n *\n * @author Josh Cummings\n * @author Vedran Pavic\n * @author Joe Grandja\n * @author Jeongjin Kim\n * @since 5.1\n * @see <a href=\"https://tools.ietf.org/html/rfc6750\" target=\"_blank\">The OAuth 2.0\n * Authorization Framework: Bearer Token Usage</a>\n * @see JwtAuthenticationProvider\n */\npublic class BearerTokenAuthenticationFilter extends OncePerRequestFilter {\n\n\tprivate final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;\n\n\tprivate final AuthenticationConverter authenticationConverter;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();\n\n\tprivate AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(\n\t\t\t(request, response, exception) -> this.authenticationEntryPoint.commence(request, response, exception));\n\n\tprivate SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();\n\n\t/**\n\t * Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)\n\t * @param authenticationManagerResolver\n\t */\n\tpublic BearerTokenAuthenticationFilter(\n\t\t\tAuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {\n\t\tthis(authenticationManagerResolver, new BearerTokenAuthenticationConverter());\n\t}\n\n\t/**\n\t * Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)\n\t * @param authenticationManager\n\t */\n\tpublic BearerTokenAuthenticationFilter(AuthenticationManager authenticationManager) {\n\t\tthis(authenticationManager, new BearerTokenAuthenticationConverter());\n\t}\n\n\t/**\n\t * Construct this filter using the provided parameters\n\t * @param authenticationManager the {@link AuthenticationManager} to use\n\t * @param authenticationConverter the {@link AuthenticationConverter} to use\n\t * @since 7.0\n\t * @see JwtAuthenticationProvider\n\t * @see OpaqueTokenAuthenticationProvider\n\t * @see BearerTokenAuthenticationConverter\n\t */\n\tpublic BearerTokenAuthenticationFilter(AuthenticationManager authenticationManager,\n\t\t\tAuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationManagerResolver = (authentication) -> authenticationManager;\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Construct this filter using the provided parameters\n\t * @param authenticationManagerResolver the {@link AuthenticationManagerResolver} to\n\t * use\n\t * @param authenticationConverter the {@link AuthenticationConverter} to use\n\t * @since 7.0\n\t * @see JwtAuthenticationProvider\n\t * @see OpaqueTokenAuthenticationProvider\n\t * @see BearerTokenAuthenticationConverter\n\t */\n\tpublic BearerTokenAuthenticationFilter(\n\t\t\tAuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver,\n\t\t\tAuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationManagerResolver, \"authenticationManagerResolver cannot be null\");\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationManagerResolver = authenticationManagerResolver;\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Extract any\n\t * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n\t * Token</a> from the request and attempt an authentication.\n\t * @param request\n\t * @param response\n\t * @param filterChain\n\t * @throws ServletException\n\t * @throws IOException\n\t */\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tAuthentication authenticationRequest;\n\t\ttry {\n\t\t\tauthenticationRequest = this.authenticationConverter.convert(request);\n\t\t}\n\t\tcatch (OAuth2AuthenticationException invalid) {\n\t\t\tthis.logger.trace(\"Sending to authentication entry point since failed to resolve bearer token\", invalid);\n\t\t\tthis.authenticationEntryPoint.commence(request, response, invalid);\n\t\t\treturn;\n\t\t}\n\n\t\tif (authenticationRequest == null) {\n\t\t\tthis.logger.trace(\"Did not process request since did not find bearer token\");\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tAuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);\n\t\t\tAuthentication authenticationResult = authenticationManager.authenticate(authenticationRequest);\n\t\t\tif (isDPoPBoundAccessToken(authenticationResult)) {\n\t\t\t\t// Prevent downgraded usage of DPoP-bound access tokens,\n\t\t\t\t// by rejecting a DPoP-bound access token received as a bearer token.\n\t\t\t\tBearerTokenError error = BearerTokenErrors.invalidToken(\"Invalid bearer token\");\n\t\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t\t}\n\t\t\tAuthentication current = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (current != null && current.isAuthenticated() && declaresToBuilder(authenticationResult)) {\n\t\t\t\tauthenticationResult = authenticationResult.toBuilder().authorities((a) -> {\n\t\t\t\t\tSet<String> newAuthorities = a.stream()\n\t\t\t\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t\t\t\t.collect(Collectors.toUnmodifiableSet());\n\t\t\t\t\tfor (GrantedAuthority currentAuthority : current.getAuthorities()) {\n\t\t\t\t\t\tif (!newAuthorities.contains(currentAuthority.getAuthority())) {\n\t\t\t\t\t\t\ta.add(currentAuthority);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}).build();\n\t\t\t}\n\t\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\tcontext.setAuthentication(authenticationResult);\n\t\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Set SecurityContextHolder to %s\", authenticationResult));\n\t\t\t}\n\t\t\tfilterChain.doFilter(request, response);\n\t\t}\n\t\tcatch (AuthenticationException failed) {\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\tthis.logger.trace(\"Failed to process authentication request\", failed);\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);\n\t\t}\n\t}\n\n\tprivate static boolean declaresToBuilder(Authentication authentication) {\n\t\tfor (Method method : authentication.getClass().getDeclaredMethods()) {\n\t\t\tif (method.getName().equals(\"toBuilder\") && method.getParameterTypes().length == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * authentication success. The default action is not to save the\n\t * {@link SecurityContext}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Set the {@link BearerTokenResolver} to use. Defaults to\n\t * {@link DefaultBearerTokenResolver}.\n\t * @param bearerTokenResolver the {@code BearerTokenResolver} to use\n\t * @deprecated Please provide an {@link AuthenticationConverter} in the constructor\n\t * instead\n\t * @see BearerTokenAuthenticationConverter\n\t */\n\t@Deprecated\n\tpublic void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) {\n\t\tAssert.notNull(bearerTokenResolver, \"bearerTokenResolver cannot be null\");\n\t\tif (this.authenticationConverter instanceof BearerTokenAuthenticationConverter converter) {\n\t\t\tconverter.setBearerTokenResolver(bearerTokenResolver);\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"You cannot both specify an AuthenticationConverter and a BearerTokenResolver.\");\n\t\t}\n\t}\n\n\t/**\n\t * Set the {@link AuthenticationEntryPoint} to use. Defaults to\n\t * {@link BearerTokenAuthenticationEntryPoint}.\n\t * @param authenticationEntryPoint the {@code AuthenticationEntryPoint} to use\n\t */\n\tpublic void setAuthenticationEntryPoint(final AuthenticationEntryPoint authenticationEntryPoint) {\n\t\tAssert.notNull(authenticationEntryPoint, \"authenticationEntryPoint cannot be null\");\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t}\n\n\t/**\n\t * Set the {@link AuthenticationFailureHandler} to use. Default implementation invokes\n\t * {@link AuthenticationEntryPoint}.\n\t * @param authenticationFailureHandler the {@code AuthenticationFailureHandler} to use\n\t * @since 5.2\n\t */\n\tpublic void setAuthenticationFailureHandler(final AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\t/**\n\t * Set the {@link AuthenticationDetailsSource} to use. Defaults to\n\t * {@link WebAuthenticationDetailsSource}.\n\t * @param authenticationDetailsSource the {@code AuthenticationDetailsSource} to use\n\t * @since 5.5\n\t * @deprecated Please provide an {@link AuthenticationConverter} in the constructor\n\t * and set the {@link AuthenticationDetailsSource} there instead. For example, you can\n\t * use {@link BearerTokenAuthenticationConverter#setAuthenticationDetailsSource}\n\t * @see BearerTokenAuthenticationConverter\n\t */\n\t@Deprecated\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"authenticationDetailsSource cannot be null\");\n\t\tif (this.authenticationConverter instanceof BearerTokenAuthenticationConverter converter) {\n\t\t\tconverter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"You cannot specify both an AuthenticationConverter and an AuthenticationDetailsSource\");\n\t\t}\n\t}\n\n\tprivate static boolean isDPoPBoundAccessToken(Authentication authentication) {\n\t\tif (!(authentication instanceof AbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication)) {\n\t\t\treturn false;\n\t\t}\n\t\tClaimAccessor accessTokenClaims = accessTokenAuthentication::getTokenAttributes;\n\t\tString jwkThumbprintClaim = null;\n\t\tMap<String, Object> confirmationMethodClaim = accessTokenClaims.getClaimAsMap(\"cnf\");\n\t\tif (!CollectionUtils.isEmpty(confirmationMethodClaim) && confirmationMethodClaim.containsKey(\"jkt\")) {\n\t\t\tjwkThumbprintClaim = (String) confirmationMethodClaim.get(\"jkt\");\n\t\t}\n\t\treturn StringUtils.hasText(jwkThumbprintClaim);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server web authentication converters and filters.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.web.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server {@code Filter}'s and supporting classes and interfaces.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServerBearerExchangeFilterFunction.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.reactive.function.client;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.util.Assert;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\n\n/**\n * An {@link ExchangeFilterFunction} that adds the\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a> from an existing {@link OAuth2Token} tied to the current\n * {@link Authentication}.\n *\n * Suitable for Reactive applications, applying it to a typical\n * {@link org.springframework.web.reactive.function.client.WebClient} configuration:\n *\n * <pre>\n\n *  &#64;Bean\n *  WebClient webClient() {\n *      ServerBearerExchangeFilterFunction bearer = new ServerBearerExchangeFilterFunction();\n *      return WebClient.builder()\n *              .filter(bearer).build();\n *  }\n * </pre>\n *\n * @author Josh Cummings\n * @since 5.2\n */\npublic final class ServerBearerExchangeFilterFunction implements ExchangeFilterFunction {\n\n\t@Override\n\tpublic Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {\n\t\t// @formatter:off\n\t\treturn oauth2Token().map((token) -> bearer(request, token))\n\t\t\t\t.defaultIfEmpty(request)\n\t\t\t\t.flatMap(next::exchange);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<OAuth2Token> oauth2Token() {\n\t\t// @formatter:off\n\t\treturn currentAuthentication()\n\t\t\t\t.filter((authentication) -> authentication.getCredentials() != null\n\t\t\t\t\t\t&& authentication.getCredentials() instanceof OAuth2Token)\n\t\t\t\t.map((authentication) -> {\n\t\t\t\t\tObject credentials = authentication.getCredentials();\n\t\t\t\t\tAssert.notNull(credentials, \"credentials cannot be null\");\n\t\t\t\t\treturn (OAuth2Token) credentials;\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Authentication> currentAuthentication() {\n\t\treturn ReactiveSecurityContextHolder.getContext().flatMap((ctx) -> Mono.justOrEmpty(ctx.getAuthentication()));\n\t}\n\n\tprivate ClientRequest bearer(ClientRequest request, OAuth2Token token) {\n\t\t// @formatter:off\n\t\treturn ClientRequest.from(request)\n\t\t\t\t.headers((headers) -> headers.setBearerAuth(token.getTokenValue()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunction.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.reactive.function.client;\n\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2Token;\nimport org.springframework.util.Assert;\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFilterFunction;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\n\n/**\n * An {@link ExchangeFilterFunction} that adds the\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a> from an existing {@link OAuth2Token} tied to the current\n * {@link Authentication}.\n *\n * Suitable for Servlet applications, applying it to a typical\n * {@link org.springframework.web.reactive.function.client.WebClient} configuration:\n *\n * <pre>\n\n *  &#64;Bean\n *  WebClient webClient() {\n *      ServletBearerExchangeFilterFunction bearer = new ServletBearerExchangeFilterFunction();\n *      return WebClient.builder()\n *              .filter(bearer).build();\n *  }\n * </pre>\n *\n * To locate the bearer token, this looks in the Reactor {@link Context} for a key of type\n * {@link Authentication}.\n *\n * @author Josh Cummings\n * @since 5.2\n */\npublic final class ServletBearerExchangeFilterFunction implements ExchangeFilterFunction {\n\n\tstatic final String SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY = \"org.springframework.security.SECURITY_CONTEXT_ATTRIBUTES\";\n\n\t@Override\n\tpublic Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {\n\t\t// @formatter:off\n\t\treturn oauth2Token().map((token) -> bearer(request, token))\n\t\t\t\t.defaultIfEmpty(request)\n\t\t\t\t.flatMap(next::exchange);\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<OAuth2Token> oauth2Token() {\n\t\t// @formatter:off\n\t\treturn Mono.deferContextual(Mono::just)\n\t\t\t\t.cast(Context.class)\n\t\t\t\t.flatMap(this::currentAuthentication)\n\t\t\t\t.filter((authentication) -> authentication.getCredentials() != null\n\t\t\t\t\t\t&& authentication.getCredentials() instanceof OAuth2Token)\n\t\t\t\t.map((authentication) -> {\n\t\t\t\t\tObject credentials = authentication.getCredentials();\n\t\t\t\t\tAssert.notNull(credentials, \"credentials cannot be null\");\n\t\t\t\t\treturn (OAuth2Token) credentials;\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate Mono<Authentication> currentAuthentication(Context ctx) {\n\t\treturn Mono.justOrEmpty(getAttribute(ctx, Authentication.class));\n\t}\n\n\tprivate <T> @Nullable T getAttribute(Context ctx, Class<T> clazz) {\n\t\t// NOTE: SecurityReactorContextConfiguration.SecurityReactorContextSubscriber adds\n\t\t// this key\n\t\tif (!ctx.hasKey(SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY)) {\n\t\t\treturn null;\n\t\t}\n\t\tMap<Class<T>, T> attributes = ctx.get(SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY);\n\t\treturn attributes.get(clazz);\n\t}\n\n\tprivate ClientRequest bearer(ClientRequest request, OAuth2Token token) {\n\t\t// @formatter:off\n\t\treturn ClientRequest.from(request)\n\t\t\t\t.headers((headers) -> headers.setBearerAuth(token.getTokenValue()))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/reactive/function/client/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server WebClient exchange filter functions.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.web.reactive.function.client;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.server;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An {@link AuthenticationEntryPoint} implementation used to commence authentication of\n * protected resource requests using {@link BearerTokenAuthenticationFilter}.\n * <p>\n * Uses information provided by {@link BearerTokenError} to set HTTP response status code\n * and populate {@code WWW-Authenticate} HTTP header.\n *\n * @author Rob Winch\n * @since 5.1\n * @see BearerTokenError\n * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-3\" target=\"_blank\">RFC 6750\n * Section 3: The WWW-Authenticate Response Header Field</a>\n */\npublic final class BearerTokenServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {\n\n\tprivate @Nullable String realmName;\n\n\tpublic void setRealmName(@Nullable String realmName) {\n\t\tthis.realmName = realmName;\n\t}\n\n\t@Override\n\tpublic Mono<Void> commence(ServerWebExchange exchange, AuthenticationException authException) {\n\t\treturn Mono.defer(() -> {\n\t\t\tHttpStatus status = getStatus(authException);\n\t\t\tMap<String, String> parameters = createParameters(authException);\n\t\t\tString wwwAuthenticate = computeWWWAuthenticateHeaderValue(parameters);\n\t\t\tServerHttpResponse response = exchange.getResponse();\n\t\t\tresponse.getHeaders().set(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);\n\t\t\tresponse.setStatusCode(status);\n\t\t\treturn response.setComplete();\n\t\t});\n\t}\n\n\tprivate Map<String, String> createParameters(AuthenticationException authException) {\n\t\tMap<String, String> parameters = new LinkedHashMap<>();\n\t\tif (this.realmName != null) {\n\t\t\tparameters.put(\"realm\", this.realmName);\n\t\t}\n\t\tif (authException instanceof OAuth2AuthenticationException oAuth2AuthenticationException) {\n\t\t\tOAuth2Error error = oAuth2AuthenticationException.getError();\n\t\t\tparameters.put(\"error\", error.getErrorCode());\n\t\t\tif (StringUtils.hasText(error.getDescription())) {\n\t\t\t\tparameters.put(\"error_description\", error.getDescription());\n\t\t\t}\n\t\t\tif (StringUtils.hasText(error.getUri())) {\n\t\t\t\tparameters.put(\"error_uri\", error.getUri());\n\t\t\t}\n\t\t\tif (error instanceof BearerTokenError bearerTokenError\n\t\t\t\t\t&& StringUtils.hasText(bearerTokenError.getScope())) {\n\t\t\t\tparameters.put(\"scope\", bearerTokenError.getScope());\n\t\t\t}\n\t\t}\n\t\treturn parameters;\n\t}\n\n\tprivate HttpStatus getStatus(AuthenticationException authException) {\n\t\tif (authException instanceof OAuth2AuthenticationException oAuth2AuthenticationException) {\n\t\t\tOAuth2Error error = oAuth2AuthenticationException.getError();\n\t\t\tif (error instanceof BearerTokenError bearerTokenError) {\n\t\t\t\treturn bearerTokenError.getHttpStatus();\n\t\t\t}\n\t\t}\n\t\treturn HttpStatus.UNAUTHORIZED;\n\t}\n\n\tprivate static String computeWWWAuthenticateHeaderValue(Map<String, String> parameters) {\n\t\tStringBuilder wwwAuthenticate = new StringBuilder();\n\t\twwwAuthenticate.append(\"Bearer\");\n\t\tif (!parameters.isEmpty()) {\n\t\t\twwwAuthenticate.append(\" \");\n\t\t\tint i = 0;\n\t\t\tfor (Map.Entry<String, String> entry : parameters.entrySet()) {\n\t\t\t\twwwAuthenticate.append(entry.getKey()).append(\"=\\\"\").append(entry.getValue()).append(\"\\\"\");\n\t\t\t\tif (i != parameters.size() - 1) {\n\t\t\t\t\twwwAuthenticate.append(\", \");\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\treturn wwwAuthenticate.toString();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.server.authentication;\n\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrors;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A strategy for resolving\n * <a href=\"https://tools.ietf.org/html/rfc6750#section-1.2\" target=\"_blank\">Bearer\n * Token</a>s from the {@link ServerWebExchange}.\n *\n * @author Rob Winch\n * @since 5.1\n * @see <a href=\"https://tools.ietf.org/html/rfc6750#section-2\" target=\"_blank\">RFC 6750\n * Section 2: Authenticated Requests</a>\n */\npublic class ServerBearerTokenAuthenticationConverter implements ServerAuthenticationConverter {\n\n\tprivate static final String ACCESS_TOKEN_PARAMETER_NAME = \"access_token\";\n\n\tprivate static final Pattern authorizationPattern = Pattern.compile(\"^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$\",\n\t\t\tPattern.CASE_INSENSITIVE);\n\n\tprivate boolean allowFormEncodedBodyParameter = false;\n\n\tprivate boolean allowUriQueryParameter = false;\n\n\tprivate String bearerTokenHeaderName = HttpHeaders.AUTHORIZATION;\n\n\t@Override\n\tpublic Mono<Authentication> convert(ServerWebExchange exchange) {\n\t\treturn Mono.defer(() -> {\n\t\t\tServerHttpRequest request = exchange.getRequest();\n\t\t\t// @formatter:off\n\t\t\treturn Flux.merge(resolveFromAuthorizationHeader(request.getHeaders()),\n\t\t\t\t\t\tresolveAccessTokenFromQueryString(request),\n\t\t\t\t\t\tresolveAccessTokenFromBody(exchange))\n\t\t\t\t.collectList()\n\t\t\t\t.flatMap(ServerBearerTokenAuthenticationConverter::resolveToken)\n\t\t\t\t.map(BearerTokenAuthenticationToken::new);\n\t\t\t// @formatter:on\n\t\t});\n\t}\n\n\tprivate static Mono<String> resolveToken(List<String> accessTokens) {\n\t\tif (CollectionUtils.isEmpty(accessTokens)) {\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\tif (accessTokens.size() > 1) {\n\t\t\tBearerTokenError error = BearerTokenErrors.invalidRequest(\"Found multiple bearer tokens in the request\");\n\t\t\treturn Mono.error(new OAuth2AuthenticationException(error));\n\t\t}\n\n\t\tString accessToken = accessTokens.get(0);\n\t\tif (!StringUtils.hasText(accessToken)) {\n\t\t\tBearerTokenError error = BearerTokenErrors\n\t\t\t\t.invalidRequest(\"The requested token parameter is an empty string\");\n\t\t\treturn Mono.error(new OAuth2AuthenticationException(error));\n\t\t}\n\n\t\treturn Mono.just(accessToken);\n\t}\n\n\tprivate Mono<String> resolveFromAuthorizationHeader(HttpHeaders headers) {\n\t\tString authorization = headers.getFirst(this.bearerTokenHeaderName);\n\t\tif (!StringUtils.startsWithIgnoreCase(authorization, \"bearer\")) {\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t\tMatcher matcher = authorizationPattern.matcher(authorization);\n\t\tif (!matcher.matches()) {\n\t\t\tBearerTokenError error = BearerTokenErrors.invalidToken(\"Bearer token is malformed\");\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\n\t\treturn Mono.just(matcher.group(\"token\"));\n\t}\n\n\tprivate Flux<String> resolveAccessTokenFromQueryString(ServerHttpRequest request) {\n\t\tif (!this.allowUriQueryParameter || !HttpMethod.GET.equals(request.getMethod())) {\n\t\t\treturn Flux.empty();\n\t\t}\n\n\t\treturn resolveTokens(request.getQueryParams());\n\t}\n\n\tprivate Flux<String> resolveAccessTokenFromBody(ServerWebExchange exchange) {\n\t\tServerHttpRequest request = exchange.getRequest();\n\t\tif (!this.allowFormEncodedBodyParameter\n\t\t\t\t|| !MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())\n\t\t\t\t|| !HttpMethod.POST.equals(request.getMethod())) {\n\t\t\treturn Flux.empty();\n\t\t}\n\n\t\treturn exchange.getFormData().flatMapMany(ServerBearerTokenAuthenticationConverter::resolveTokens);\n\t}\n\n\tprivate static Flux<String> resolveTokens(MultiValueMap<String, String> parameters) {\n\t\tList<String> accessTokens = parameters.get(ACCESS_TOKEN_PARAMETER_NAME);\n\t\treturn CollectionUtils.isEmpty(accessTokens) ? Flux.empty() : Flux.fromIterable(accessTokens);\n\t}\n\n\t/**\n\t * Set if transport of access token using URI query parameter is supported. Defaults\n\t * to {@code false}.\n\t *\n\t * The spec recommends against using this mechanism for sending bearer tokens, and\n\t * even goes as far as stating that it was only included for completeness.\n\t * @param allowUriQueryParameter if the URI query parameter is supported\n\t */\n\tpublic void setAllowUriQueryParameter(boolean allowUriQueryParameter) {\n\t\tthis.allowUriQueryParameter = allowUriQueryParameter;\n\t}\n\n\t/**\n\t * Set this value to configure what header is checked when resolving a Bearer Token.\n\t * This value is defaulted to {@link HttpHeaders#AUTHORIZATION}.\n\t *\n\t * This allows other headers to be used as the Bearer Token source such as\n\t * {@link HttpHeaders#PROXY_AUTHORIZATION}\n\t * @param bearerTokenHeaderName the header to check when retrieving the Bearer Token.\n\t * @since 5.4\n\t */\n\tpublic void setBearerTokenHeaderName(String bearerTokenHeaderName) {\n\t\tthis.bearerTokenHeaderName = bearerTokenHeaderName;\n\t}\n\n\t/**\n\t * Set if transport of access token using form-encoded body parameter is supported.\n\t * Defaults to {@code false}.\n\t * @param allowFormEncodedBodyParameter if the form-encoded body parameter is\n\t * supported\n\t * @since 6.5\n\t */\n\tpublic void setAllowFormEncodedBodyParameter(boolean allowFormEncodedBodyParameter) {\n\t\tthis.allowFormEncodedBodyParameter = allowFormEncodedBodyParameter;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server WebFlux authentication converters.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.web.server.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * OAuth 2.0 Resource Server WebFlux support classes.\n */\n@NullMarked\npackage org.springframework.security.oauth2.server.resource.web.server;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/core/TestOAuth2AuthenticatedPrincipals.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.core;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;\n\n/**\n * Test values of {@link OAuth2AuthenticatedPrincipal}s\n *\n * @author Josh Cummings\n */\npublic final class TestOAuth2AuthenticatedPrincipals {\n\n\tprivate TestOAuth2AuthenticatedPrincipals() {\n\t}\n\n\tpublic static OAuth2AuthenticatedPrincipal active() {\n\t\treturn active((attributes) -> {\n\t\t});\n\t}\n\n\tpublic static OAuth2AuthenticatedPrincipal active(Consumer<Map<String, Object>> attributesConsumer) {\n\t\tMap<String, Object> attributes = new HashMap<>();\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, true);\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList(\"https://protected.example.net/resource\"));\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, \"l238j323ds-23ij4\");\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238));\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L));\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.ISS, url(\"https://server.example.com/\"));\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList(\"read\", \"write\", \"dolphin\"));\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\");\n\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.USERNAME, \"jdoe\");\n\t\tattributesConsumer.accept(attributes);\n\t\tCollection<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority(\"SCOPE_read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_write\"), new SimpleGrantedAuthority(\"SCOPE_dolphin\"));\n\t\treturn new OAuth2IntrospectionAuthenticatedPrincipal(attributes, authorities);\n\t}\n\n\tprivate static URL url(String url) {\n\t\ttry {\n\t\t\treturn new URL(url);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new UncheckedIOException(ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/BearerTokenErrorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link BearerTokenError}\n *\n * @author Vedran Pavic\n * @author Josh Cummings\n */\npublic class BearerTokenErrorTests {\n\n\tprivate static final String TEST_ERROR_CODE = \"test-code\";\n\n\tprivate static final HttpStatus TEST_HTTP_STATUS = HttpStatus.UNAUTHORIZED;\n\n\tprivate static final String TEST_DESCRIPTION = \"test-description\";\n\n\tprivate static final String TEST_URI = \"https://example.com\";\n\n\tprivate static final String TEST_SCOPE = \"test-scope\";\n\n\t@Test\n\tpublic void constructorWithErrorCodeWhenErrorCodeIsValidThenCreated() {\n\t\tBearerTokenError error = new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS, null, null);\n\t\tassertThat(error.getErrorCode()).isEqualTo(TEST_ERROR_CODE);\n\t\tassertThat(error.getHttpStatus()).isEqualTo(TEST_HTTP_STATUS);\n\t\tassertThat(error.getDescription()).isNull();\n\t\tassertThat(error.getUri()).isNull();\n\t\tassertThat(error.getScope()).isNull();\n\t}\n\n\t@Test\n\tpublic void constructorWithErrorCodeAndHttpStatusWhenErrorCodeIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(null, TEST_HTTP_STATUS, null, null))\n\t\t\t\t.withMessage(\"errorCode cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithErrorCodeAndHttpStatusWhenErrorCodeIsEmptyThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(\"\", TEST_HTTP_STATUS, null, null))\n\t\t\t\t.withMessage(\"errorCode cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithErrorCodeAndHttpStatusWhenHttpStatusIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, null, null, null))\n\t\t\t\t.withMessage(\"httpStatus cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithAllParametersWhenAllParametersAreValidThenCreated() {\n\t\tBearerTokenError error = new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI,\n\t\t\t\tTEST_SCOPE);\n\t\tassertThat(error.getErrorCode()).isEqualTo(TEST_ERROR_CODE);\n\t\tassertThat(error.getHttpStatus()).isEqualTo(TEST_HTTP_STATUS);\n\t\tassertThat(error.getDescription()).isEqualTo(TEST_DESCRIPTION);\n\t\tassertThat(error.getUri()).isEqualTo(TEST_URI);\n\t\tassertThat(error.getScope()).isEqualTo(TEST_SCOPE);\n\t}\n\n\t@Test\n\tpublic void constructorWithAllParametersWhenErrorCodeIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(null, TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))\n\t\t\t\t.withMessage(\"errorCode cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithAllParametersWhenErrorCodeIsEmptyThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(\"\", TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))\n\t\t\t\t.withMessage(\"errorCode cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithAllParametersWhenHttpStatusIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, null, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))\n\t\t\t\t.withMessage(\"httpStatus cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithAllParametersWhenErrorCodeIsInvalidThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE + \"\\\"\",\n\t\t\t\t\tTEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE)\n\t\t\t\t)\n\t\t\t\t.withMessageContaining(\"errorCode\")\n\t\t\t\t.withMessageContaining(\"RFC 6750\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithAllParametersWhenDescriptionIsInvalidThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS,\n\t\t\t\t\tTEST_DESCRIPTION + \"\\\"\", TEST_URI, TEST_SCOPE)\n\t\t\t\t)\n\t\t\t\t.withMessageContaining(\"description\")\n\t\t\t\t.withMessageContaining(\"RFC 6750\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithAllParametersWhenErrorUriIsInvalidThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS, TEST_DESCRIPTION,\n\t\t\t\t\t\tTEST_URI + \"\\\"\", TEST_SCOPE)\n\t\t\t\t)\n\t\t\t\t.withMessageContaining(\"errorUri\")\n\t\t\t\t.withMessageContaining(\"RFC 6750\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWithAllParametersWhenScopeIsInvalidThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS,\n\t\t\t\t\tTEST_DESCRIPTION, TEST_URI, TEST_SCOPE + \"\\\"\")\n\t\t\t\t)\n\t\t\t\t.withMessageContaining(\"scope\")\n\t\t\t\t.withMessageContaining(\"RFC 6750\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/BearerTokenErrorsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class BearerTokenErrorsTests {\n\n\t@Test\n\tpublic void invalidRequestWhenMessageGivenThenBearerTokenErrorReturned() {\n\t\tString message = \"message\";\n\t\tBearerTokenError error = BearerTokenErrors.invalidRequest(message);\n\t\tassertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INVALID_REQUEST);\n\t\tassertThat(error.getDescription()).isSameAs(message);\n\t\tassertThat(error.getHttpStatus()).isSameAs(HttpStatus.BAD_REQUEST);\n\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t}\n\n\t@Test\n\tpublic void invalidRequestWhenInvalidMessageGivenThenDefaultBearerTokenErrorReturned() {\n\t\tString message = \"has \\\"invalid\\\" chars\";\n\t\tBearerTokenError error = BearerTokenErrors.invalidRequest(message);\n\t\tassertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INVALID_REQUEST);\n\t\tassertThat(error.getDescription()).isEqualTo(\"Invalid request\");\n\t\tassertThat(error.getHttpStatus()).isSameAs(HttpStatus.BAD_REQUEST);\n\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t}\n\n\t@Test\n\tpublic void invalidTokenWhenMessageGivenThenBearerTokenErrorReturned() {\n\t\tString message = \"message\";\n\t\tBearerTokenError error = BearerTokenErrors.invalidToken(message);\n\t\tassertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INVALID_TOKEN);\n\t\tassertThat(error.getDescription()).isSameAs(message);\n\t\tassertThat(error.getHttpStatus()).isSameAs(HttpStatus.UNAUTHORIZED);\n\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t}\n\n\t@Test\n\tpublic void invalidTokenWhenInvalidMessageGivenThenDefaultBearerTokenErrorReturned() {\n\t\tString message = \"has \\\"invalid\\\" chars\";\n\t\tBearerTokenError error = BearerTokenErrors.invalidToken(message);\n\t\tassertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INVALID_TOKEN);\n\t\tassertThat(error.getDescription()).isEqualTo(\"Invalid token\");\n\t\tassertThat(error.getHttpStatus()).isSameAs(HttpStatus.UNAUTHORIZED);\n\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t}\n\n\t@Test\n\tpublic void insufficientScopeWhenMessageGivenThenBearerTokenErrorReturned() {\n\t\tString message = \"message\";\n\t\tString scope = \"scope\";\n\t\tBearerTokenError error = BearerTokenErrors.insufficientScope(message, scope);\n\t\tassertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INSUFFICIENT_SCOPE);\n\t\tassertThat(error.getDescription()).isSameAs(message);\n\t\tassertThat(error.getHttpStatus()).isSameAs(HttpStatus.FORBIDDEN);\n\t\tassertThat(error.getScope()).isSameAs(scope);\n\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t}\n\n\t@Test\n\tpublic void insufficientScopeWhenInvalidMessageGivenThenDefaultBearerTokenErrorReturned() {\n\t\tString message = \"has \\\"invalid\\\" chars\";\n\t\tBearerTokenError error = BearerTokenErrors.insufficientScope(message, \"scope\");\n\t\tassertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INSUFFICIENT_SCOPE);\n\t\tassertThat(error.getDescription()).isSameAs(\"Insufficient scope\");\n\t\tassertThat(error.getHttpStatus()).isSameAs(HttpStatus.FORBIDDEN);\n\t\tassertThat(error.getScope()).isNull();\n\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/DefaultAuthenticationEventPublisherBearerTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.authentication.DefaultAuthenticationEventPublisher;\nimport org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\n\nimport static org.mockito.ArgumentMatchers.isA;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link DefaultAuthenticationEventPublisher}'s bearer token use cases\n *\n * {@see DefaultAuthenticationEventPublisher}\n */\npublic class DefaultAuthenticationEventPublisherBearerTokenTests {\n\n\tDefaultAuthenticationEventPublisher publisher;\n\n\t@Test\n\tpublic void publishAuthenticationFailureWhenInvalidBearerTokenExceptionThenMaps() {\n\t\tApplicationEventPublisher appPublisher = mock(ApplicationEventPublisher.class);\n\t\tAuthentication authentication = new JwtAuthenticationToken(TestJwts.jwt().build());\n\t\tException cause = new Exception();\n\t\tthis.publisher = new DefaultAuthenticationEventPublisher(appPublisher);\n\t\tthis.publisher.publishAuthenticationFailure(new InvalidBearerTokenException(\"invalid\"), authentication);\n\t\tthis.publisher.publishAuthenticationFailure(new InvalidBearerTokenException(\"invalid\", cause), authentication);\n\t\tverify(appPublisher, times(2)).publishEvent(isA(AuthenticationFailureBadCredentialsEvent.class));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport net.minidev.json.JSONObject;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link BearerTokenAuthentication}\n *\n * @author Josh Cummings\n */\npublic class BearerTokenAuthenticationTests {\n\n\tprivate final OAuth2AccessToken token = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\",\n\t\t\tInstant.now(), Instant.now().plusSeconds(3600));\n\n\tprivate final String name = \"sub\";\n\n\tprivate Map<String, Object> attributesMap = new HashMap<>();\n\n\tprivate DefaultOAuth2AuthenticatedPrincipal principal;\n\n\tprivate final Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"USER\");\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.attributesMap.put(OAuth2TokenIntrospectionClaimNames.SUB, this.name);\n\t\tthis.attributesMap.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, \"client_id\");\n\t\tthis.attributesMap.put(OAuth2TokenIntrospectionClaimNames.USERNAME, \"username\");\n\t\tthis.principal = new DefaultOAuth2AuthenticatedPrincipal(this.attributesMap, null);\n\t}\n\n\t@Test\n\tpublic void getNameWhenConfiguredInConstructorThenReturnsName() {\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(this.name, this.attributesMap,\n\t\t\t\tthis.authorities);\n\t\tBearerTokenAuthentication authenticated = new BearerTokenAuthentication(principal, this.token,\n\t\t\t\tthis.authorities);\n\t\tassertThat(authenticated.getName()).isEqualTo(this.name);\n\t}\n\n\t@Test\n\tpublic void getNameWhenHasNoSubjectThenReturnsEmptyString() {\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(\n\t\t\t\tCollections.singletonMap(\"claim\", \"value\"), null);\n\t\tBearerTokenAuthentication authenticated = new BearerTokenAuthentication(principal, this.token, null);\n\t\tassertThat(authenticated.getName()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getNameWhenTokenHasUsernameThenReturnsUsernameAttribute() {\n\t\tBearerTokenAuthentication authenticated = new BearerTokenAuthentication(this.principal, this.token, null);\n\t\t// @formatter:off\n\t\tassertThat(authenticated.getName())\n\t\t\t\t.isEqualTo(this.principal.getAttribute(OAuth2TokenIntrospectionClaimNames.SUB));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenIsNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenAuthentication(this.principal, null, null))\n\t\t\t\t.withMessageContaining(\"token cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenCredentialIsNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenAuthentication(null, this.token, null))\n\t\t\t\t.withMessageContaining(\"principal cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenPassingAllAttributesThenTokenIsAuthenticated() {\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(\"harris\",\n\t\t\t\tCollections.singletonMap(\"claim\", \"value\"), null);\n\t\tBearerTokenAuthentication authenticated = new BearerTokenAuthentication(principal, this.token, null);\n\t\tassertThat(authenticated.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void getTokenAttributesWhenHasTokenThenReturnsThem() {\n\t\tBearerTokenAuthentication authenticated = new BearerTokenAuthentication(this.principal, this.token,\n\t\t\t\tCollections.emptyList());\n\t\tassertThat(authenticated.getTokenAttributes()).isEqualTo(this.principal.getAttributes());\n\t}\n\n\t@Test\n\tpublic void getAuthoritiesWhenHasAuthoritiesThenReturnsThem() {\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"USER\");\n\t\tBearerTokenAuthentication authenticated = new BearerTokenAuthentication(this.principal, this.token,\n\t\t\t\tauthorities);\n\t\tassertThat(authenticated.getAuthorities()).isEqualTo(authorities);\n\t}\n\n\t// gh-6843\n\t@Test\n\tpublic void constructorWhenDefaultParametersThenSetsPrincipalToAttributesCopy() {\n\t\tJSONObject attributes = new JSONObject();\n\t\tattributes.put(\"active\", true);\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(attributes, null);\n\t\tBearerTokenAuthentication token = new BearerTokenAuthentication(principal, this.token, null);\n\t\tassertThat(token.getPrincipal()).isNotSameAs(attributes);\n\t\tassertThat(token.getTokenAttributes()).isNotSameAs(attributes);\n\t}\n\n\t// gh-6843\n\t@Test\n\tpublic void toStringWhenAttributesContainsURLThenDoesNotFail() throws Exception {\n\t\tJSONObject attributes = new JSONObject(Collections.singletonMap(\"iss\", new URL(\"https://idp.example.com\")));\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(attributes, null);\n\t\tBearerTokenAuthentication token = new BearerTokenAuthentication(principal, this.token, null);\n\t\ttoken.toString();\n\t}\n\n\t@Test\n\tpublic void toBuilderWhenApplyThenCopies() {\n\t\tBearerTokenAuthentication factorOne = new BearerTokenAuthentication(TestOAuth2AuthenticatedPrincipals.active(),\n\t\t\t\tthis.token, AuthorityUtils.createAuthorityList(\"FACTOR_ONE\"));\n\t\tBearerTokenAuthentication factorTwo = new BearerTokenAuthentication(\n\t\t\t\tTestOAuth2AuthenticatedPrincipals.active((m) -> m.put(\"k\", \"v\")),\n\t\t\t\tnew OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"nekot\", Instant.now(),\n\t\t\t\t\t\tInstant.now().plusSeconds(3600)),\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"));\n\t\tBearerTokenAuthentication authentication = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.token(factorTwo.getToken())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());\n\t\tassertThat(authentication.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(authentication.getToken()).isSameAs(factorTwo.getToken());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link BearerTokenAuthenticationToken}\n *\n * @author Josh Cummings\n */\npublic class BearerTokenAuthenticationTokenTests {\n\n\t@Test\n\tpublic void constructorWhenTokenIsNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenAuthenticationToken(null))\n\t\t\t\t.withMessageContaining(\"token cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenIsEmptyThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenAuthenticationToken(\"\"))\n\t\t\t\t.withMessageContaining(\"token cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenHasValueThenConstructedCorrectly() {\n\t\tBearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken(\"token\");\n\t\tassertThat(token.getToken()).isEqualTo(\"token\");\n\t\tassertThat(token.getPrincipal()).isEqualTo(\"token\");\n\t\tassertThat(token.getCredentials()).isEqualTo(\"token\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport com.nimbusds.jose.jwk.JWK;\nimport com.nimbusds.jose.jwk.JWKSet;\nimport com.nimbusds.jose.jwk.source.JWKSource;\nimport com.nimbusds.jose.proc.SecurityContext;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.jose.TestJwks;\nimport org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;\nimport org.springframework.security.oauth2.jwt.JwsHeader;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimsSet;\nimport org.springframework.security.oauth2.jwt.JwtEncoderParameters;\nimport org.springframework.security.oauth2.jwt.NimbusJwtEncoder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link DPoPAuthenticationProvider}.\n *\n * @author Joe Grandja\n */\npublic class DPoPAuthenticationProviderTests {\n\n\tprivate NimbusJwtEncoder accessTokenJwtEncoder;\n\n\tprivate NimbusJwtEncoder dPoPProofJwtEncoder;\n\n\tprivate AuthenticationManager tokenAuthenticationManager;\n\n\tprivate DPoPAuthenticationProvider authenticationProvider;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tJWKSource<SecurityContext> jwkSource = (jwkSelector, securityContext) -> jwkSelector\n\t\t\t.select(new JWKSet(TestJwks.DEFAULT_EC_JWK));\n\t\tthis.accessTokenJwtEncoder = new NimbusJwtEncoder(jwkSource);\n\t\tjwkSource = (jwkSelector, securityContext) -> jwkSelector.select(new JWKSet(TestJwks.DEFAULT_RSA_JWK));\n\t\tthis.dPoPProofJwtEncoder = new NimbusJwtEncoder(jwkSource);\n\t\tthis.tokenAuthenticationManager = mock(AuthenticationManager.class);\n\t\tthis.authenticationProvider = new DPoPAuthenticationProvider(this.tokenAuthenticationManager);\n\t}\n\n\t@Test\n\tpublic void constructorWhenTokenAuthenticationManagerNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DPoPAuthenticationProvider(null))\n\t\t\t.withMessage(\"tokenAuthenticationManager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void supportsWhenDPoPAuthenticationTokenThenReturnsTrue() {\n\t\tassertThat(this.authenticationProvider.supports(DPoPAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setDPoPProofVerifierFactoryWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.authenticationProvider.setDPoPProofVerifierFactory(null))\n\t\t\t.withMessage(\"dPoPProofVerifierFactory cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnableToAuthenticateAccessTokenThenThrowOAuth2AuthenticationException() {\n\t\tDPoPAuthenticationToken dPoPAuthenticationToken = new DPoPAuthenticationToken(\"access-token\", \"dpop-proof\",\n\t\t\t\t\"GET\", \"https://resource1\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(dPoPAuthenticationToken))\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);\n\t\t\t\tassertThat(ex.getError().getDescription())\n\t\t\t\t\t.isEqualTo(\"Unable to authenticate the DPoP-bound access token.\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAthMissingThenThrowOAuth2AuthenticationException() {\n\t\tJwt accessToken = generateAccessToken();\n\t\tJwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(accessToken);\n\t\tgiven(this.tokenAuthenticationManager.authenticate(any())).willReturn(jwtAuthenticationToken);\n\n\t\tString method = \"GET\";\n\t\tString resourceUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_RSA_JWK.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", resourceUri)\n//\t\t\t\t.claim(\"ath\", computeSHA256(accessToken.getTokenValue()))\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\tDPoPAuthenticationToken dPoPAuthenticationToken = new DPoPAuthenticationToken(accessToken.getTokenValue(),\n\t\t\t\tdPoPProof.getTokenValue(), method, resourceUri);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(dPoPAuthenticationToken))\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_DPOP_PROOF);\n\t\t\t\tassertThat(ex.getMessage()).contains(\"ath claim is required\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAthDoesNotMatchThenThrowOAuth2AuthenticationException() throws Exception {\n\t\tJwt accessToken = generateAccessToken();\n\t\tJwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(accessToken);\n\t\tgiven(this.tokenAuthenticationManager.authenticate(any())).willReturn(jwtAuthenticationToken);\n\n\t\tString method = \"GET\";\n\t\tString resourceUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_RSA_JWK.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", resourceUri)\n\t\t\t\t.claim(\"ath\", computeSHA256(accessToken.getTokenValue()) + \"-mismatch\")\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\tDPoPAuthenticationToken dPoPAuthenticationToken = new DPoPAuthenticationToken(accessToken.getTokenValue(),\n\t\t\t\tdPoPProof.getTokenValue(), method, resourceUri);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(dPoPAuthenticationToken))\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_DPOP_PROOF);\n\t\t\t\tassertThat(ex.getMessage()).contains(\"ath claim is invalid\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenJktMissingThenThrowOAuth2AuthenticationException() throws Exception {\n\t\tJwt accessToken = generateAccessToken(null); // jkt claim is not added\n\t\tJwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(accessToken);\n\t\tgiven(this.tokenAuthenticationManager.authenticate(any())).willReturn(jwtAuthenticationToken);\n\n\t\tString method = \"GET\";\n\t\tString resourceUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_RSA_JWK.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", resourceUri)\n\t\t\t\t.claim(\"ath\", computeSHA256(accessToken.getTokenValue()))\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\tDPoPAuthenticationToken dPoPAuthenticationToken = new DPoPAuthenticationToken(accessToken.getTokenValue(),\n\t\t\t\tdPoPProof.getTokenValue(), method, resourceUri);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(dPoPAuthenticationToken))\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_DPOP_PROOF);\n\t\t\t\tassertThat(ex.getMessage()).contains(\"jkt claim is required\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenJktDoesNotMatchThenThrowOAuth2AuthenticationException() throws Exception {\n\t\t// Use different jwk to make it not match\n\t\tJwt accessToken = generateAccessToken(TestJwks.DEFAULT_EC_JWK);\n\t\tJwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(accessToken);\n\t\tgiven(this.tokenAuthenticationManager.authenticate(any())).willReturn(jwtAuthenticationToken);\n\n\t\tString method = \"GET\";\n\t\tString resourceUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_RSA_JWK.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", resourceUri)\n\t\t\t\t.claim(\"ath\", computeSHA256(accessToken.getTokenValue()))\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\tDPoPAuthenticationToken dPoPAuthenticationToken = new DPoPAuthenticationToken(accessToken.getTokenValue(),\n\t\t\t\tdPoPProof.getTokenValue(), method, resourceUri);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.authenticationProvider.authenticate(dPoPAuthenticationToken))\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tassertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_DPOP_PROOF);\n\t\t\t\tassertThat(ex.getMessage()).contains(\"jkt claim is invalid\");\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDPoPProofValidThenSuccess() throws Exception {\n\t\tJwt accessToken = generateAccessToken();\n\t\tJwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(accessToken);\n\t\tgiven(this.tokenAuthenticationManager.authenticate(any())).willReturn(jwtAuthenticationToken);\n\n\t\tString method = \"GET\";\n\t\tString resourceUri = \"https://resource1\";\n\n\t\t// @formatter:off\n\t\tMap<String, Object> publicJwk = TestJwks.DEFAULT_RSA_JWK.toPublicJWK().toJSONObject();\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256)\n\t\t\t\t.type(\"dpop+jwt\")\n\t\t\t\t.jwk(publicJwk)\n\t\t\t\t.build();\n\t\tJwtClaimsSet claims = JwtClaimsSet.builder()\n\t\t\t\t.issuedAt(Instant.now())\n\t\t\t\t.claim(\"htm\", method)\n\t\t\t\t.claim(\"htu\", resourceUri)\n\t\t\t\t.claim(\"ath\", computeSHA256(accessToken.getTokenValue()))\n\t\t\t\t.id(UUID.randomUUID().toString())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tJwt dPoPProof = this.dPoPProofJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims));\n\n\t\tDPoPAuthenticationToken dPoPAuthenticationToken = new DPoPAuthenticationToken(accessToken.getTokenValue(),\n\t\t\t\tdPoPProof.getTokenValue(), method, resourceUri);\n\t\tassertThat(this.authenticationProvider.authenticate(dPoPAuthenticationToken)).isSameAs(jwtAuthenticationToken);\n\t}\n\n\tprivate Jwt generateAccessToken() {\n\t\treturn generateAccessToken(TestJwks.DEFAULT_RSA_JWK);\n\t}\n\n\tprivate Jwt generateAccessToken(JWK clientJwk) {\n\t\tMap<String, Object> jktClaim = null;\n\t\tif (clientJwk != null) {\n\t\t\ttry {\n\t\t\t\tString sha256Thumbprint = clientJwk.toPublicJWK().computeThumbprint().toString();\n\t\t\t\tjktClaim = new HashMap<>();\n\t\t\t\tjktClaim.put(\"jkt\", sha256Thumbprint);\n\t\t\t}\n\t\t\tcatch (Exception ignored) {\n\t\t\t}\n\t\t}\n\t\tJwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.ES256).build();\n\t\tInstant issuedAt = Instant.now();\n\t\tInstant expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);\n\t\t// @formatter:off\n\t\tJwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder()\n\t\t\t\t.issuer(\"https://provider.com\")\n\t\t\t\t.subject(\"subject\")\n\t\t\t\t.issuedAt(issuedAt)\n\t\t\t\t.expiresAt(expiresAt)\n\t\t\t\t.id(UUID.randomUUID().toString());\n\t\tif (jktClaim != null) {\n\t\t\tclaimsBuilder.claim(\"cnf\", jktClaim);\t// Bind client public key\n\t\t}\n\t\t// @formatter:on\n\t\treturn this.accessTokenJwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claimsBuilder.build()));\n\t}\n\n\tprivate static String computeSHA256(String value) throws Exception {\n\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\tbyte[] digest = md.digest(value.getBytes(StandardCharsets.UTF_8));\n\t\treturn Base64.getUrlEncoder().withoutPadding().encodeToString(digest);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/DelegatingJwtGrantedAuthoritiesConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for verifying {@link DelegatingJwtGrantedAuthoritiesConverter}\n *\n * @author Laszlo Stahorszki\n * @author Josh Cummings\n */\npublic class DelegatingJwtGrantedAuthoritiesConverterTests {\n\n\t@Test\n\tpublic void convertWhenNoConvertersThenNoAuthorities() {\n\t\tDelegatingJwtGrantedAuthoritiesConverter converter = new DelegatingJwtGrantedAuthoritiesConverter();\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tassertThat(converter.convert(jwt)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenConverterThenAuthorities() {\n\t\tDelegatingJwtGrantedAuthoritiesConverter converter = new DelegatingJwtGrantedAuthoritiesConverter(\n\t\t\t\t((jwt) -> AuthorityUtils.createAuthorityList(\"one\")));\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tCollection<GrantedAuthority> authorities = converter.convert(jwt);\n\t\tassertThat(authorityListToOrderedSet(authorities)).containsExactly(\"one\");\n\t}\n\n\t@Test\n\tpublic void convertWhenMultipleConvertersThenDuplicatesRemoved() {\n\t\tConverter<Jwt, Collection<GrantedAuthority>> one = (jwt) -> AuthorityUtils.createAuthorityList(\"one\", \"two\");\n\t\tConverter<Jwt, Collection<GrantedAuthority>> two = (jwt) -> AuthorityUtils.createAuthorityList(\"one\", \"three\");\n\t\tDelegatingJwtGrantedAuthoritiesConverter composite = new DelegatingJwtGrantedAuthoritiesConverter(one, two);\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tCollection<GrantedAuthority> authorities = composite.convert(jwt);\n\t\tassertThat(authorityListToOrderedSet(authorities)).containsExactly(\"one\", \"two\", \"three\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesConverterIsNullThenIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new DelegatingJwtGrantedAuthoritiesConverter(\n\t\t\t\t\t(Collection<Converter<Jwt, Collection<GrantedAuthority>>>) null));\n\t}\n\n\tprivate Collection<String> authorityListToOrderedSet(Collection<GrantedAuthority> grantedAuthorities) {\n\t\tCollection<String> authorities = new LinkedHashSet<>(grantedAuthorities.size());\n\t\tfor (GrantedAuthority authority : grantedAuthorities) {\n\t\t\tauthorities.add(authority.getAuthority());\n\t\t}\n\t\treturn authorities;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/ExpressionJwtGrantedAuthoritiesConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.spel.standard.SpelExpression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ExpressionJwtGrantedAuthoritiesConverter}\n *\n * @author Thomas Darimont\n * @since 6.4\n */\npublic class ExpressionJwtGrantedAuthoritiesConverterTests {\n\n\t@Test\n\tpublic void convertWhenTokenHasCustomClaimNameExpressionThenCustomClaimNameAttributeIsTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"nested\", Collections.singletonMap(\"roles\", Arrays.asList(\"role1\", \"role2\")))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSpelExpression expression = new SpelExpressionParser().parseRaw(\"[nested][roles]\");\n\t\tExpressionJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new ExpressionJwtGrantedAuthoritiesConverter(\n\t\t\t\texpression);\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"SCOPE_role1\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_role2\"));\n\t}\n\n\t@Test\n\tpublic void convertToEmptyListWhenTokenClaimExpressionYieldsNull() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"nested\", Collections.singletonMap(\"roles\", null))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSpelExpression expression = new SpelExpressionParser().parseRaw(\"[nested][roles]\");\n\t\tExpressionJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new ExpressionJwtGrantedAuthoritiesConverter(\n\t\t\t\texpression);\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasCustomClaimNameExpressionThenCustomClaimNameAttributeIsTranslatedToAuthoritiesWithPrefix() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"nested\", Collections.singletonMap(\"roles\", Arrays.asList(\"role1\", \"role2\")))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSpelExpression expression = new SpelExpressionParser().parseRaw(\"[nested][roles]\");\n\t\tExpressionJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new ExpressionJwtGrantedAuthoritiesConverter(\n\t\t\t\texpression);\n\t\tjwtGrantedAuthoritiesConverter.setAuthorityPrefix(\"CUSTOM_\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"CUSTOM_role1\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"CUSTOM_role2\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasCustomInvalidClaimNameExpressionThenCustomClaimNameAttributeIsTranslatedToEmptyAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"other\", Collections.singletonMap(\"roles\", Arrays.asList(\"role1\", \"role2\")))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tSpelExpression expression = new SpelExpressionParser().parseRaw(\"[nested][roles]\");\n\t\tExpressionJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new ExpressionJwtGrantedAuthoritiesConverter(\n\t\t\t\texpression);\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link JwtAuthenticationConverter}\n *\n * @author Josh Cummings\n * @author Evgeniy Cheban\n * @author Olivier Antoine\n */\npublic class JwtAuthenticationConverterTests {\n\n\tJwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();\n\n\t@Test\n\tpublic void convertWhenDefaultGrantedAuthoritiesConverterSet() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scope\", \"message:read message:write\").build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"SCOPE_message:read\", \"SCOPE_message:write\");\n\t}\n\n\t@Test\n\tpublic void whenSettingNullGrantedAuthoritiesConverter() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(null))\n\t\t\t.withMessage(\"jwtGrantedAuthoritiesConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void convertWithOverriddenGrantedAuthoritiesConverter() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scope\", \"message:read message:write\").build();\n\t\tConverter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter = (token) -> Arrays\n\t\t\t.asList(new SimpleGrantedAuthority(\"blah\"));\n\t\tthis.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);\n\t\tSecurityAssertions.assertThat(authentication).hasAuthority(\"blah\");\n\t}\n\n\t@Test\n\tpublic void whenSettingNullPrincipalClaimName() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(null))\n\t\t\t\t.withMessage(\"principalClaimName cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void whenSettingEmptyPrincipalClaimName() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(\"\"))\n\t\t\t\t.withMessage(\"principalClaimName cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void whenSettingBlankPrincipalClaimName() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(\" \"))\n\t\t\t\t.withMessage(\"principalClaimName cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenPrincipalClaimNameSet() {\n\t\tthis.jwtAuthenticationConverter.setPrincipalClaimName(\"user_id\");\n\t\tJwt jwt = TestJwts.jwt().claim(\"user_id\", \"100\").build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);\n\t\tassertThat(authentication.getName()).isEqualTo(\"100\");\n\t}\n\n\t@Test\n\tpublic void convertWhenPrincipalClaimNameSetAndClaimValueIsNotString() {\n\t\tthis.jwtAuthenticationConverter.setPrincipalClaimName(\"user_id\");\n\t\tJwt jwt = TestJwts.jwt().claim(\"user_id\", 100).build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);\n\t\tassertThat(authentication.getName()).isEqualTo(\"100\");\n\t}\n\n\t@Test\n\tpublic void convertWhenDefaultsThenIssuesFactor() {\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tAuthentication result = this.jwtAuthenticationConverter.convert(jwt);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.BEARER_AUTHORITY);\n\t}\n\n\t@Test\n\tpublic void whenSettingNullJwtPrincipalConverter() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setJwtPrincipalConverter(null))\n\t\t\t.withMessage(\"jwtPrincipalConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void convertWhenJwtPrincipalConverterSetThenCustomPrincipalUsed() {\n\t\tOAuth2AuthenticatedPrincipal customPrincipal = new DefaultOAuth2AuthenticatedPrincipal(\"custom-name\",\n\t\t\t\tMap.of(\"sub\", \"custom-name\"), List.of());\n\t\tthis.jwtAuthenticationConverter.setJwtPrincipalConverter((jwt) -> customPrincipal);\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);\n\t\tassertThat(authentication.getName()).isEqualTo(\"custom-name\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link JwtAuthenticationProvider}\n *\n * @author Josh Cummings\n * @author Jerome Wacongne ch4mp&#64;c4-soft.com\n */\n@ExtendWith(MockitoExtension.class)\npublic class JwtAuthenticationProviderTests {\n\n\t@Mock\n\tConverter<Jwt, AbstractAuthenticationToken> jwtAuthenticationConverter;\n\n\t@Mock\n\tJwtDecoder jwtDecoder;\n\n\tJwtAuthenticationProvider provider;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.provider = new JwtAuthenticationProvider(this.jwtDecoder);\n\t\tthis.provider.setJwtAuthenticationConverter(this.jwtAuthenticationConverter);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenJwtDecodesThenAuthenticationHasAttributesContainedInJwt() {\n\t\tBearerTokenAuthenticationToken token = this.authentication();\n\t\tJwt jwt = TestJwts.jwt().claim(\"name\", \"value\").build();\n\t\tgiven(this.jwtDecoder.decode(\"token\")).willReturn(jwt);\n\t\tgiven(this.jwtAuthenticationConverter.convert(jwt)).willReturn(new JwtAuthenticationToken(jwt));\n\t\tJwtAuthenticationToken authentication = (JwtAuthenticationToken) this.provider.authenticate(token);\n\t\tassertThat(authentication.getTokenAttributes()).containsEntry(\"name\", \"value\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenJwtDecodeFailsThenRespondsWithInvalidToken() {\n\t\tBearerTokenAuthenticationToken token = this.authentication();\n\t\tgiven(this.jwtDecoder.decode(\"token\")).willThrow(BadJwtException.class);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t\t.matches(errorCode(BearerTokenErrorCodes.INVALID_TOKEN));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDecoderThrowsIncompatibleErrorMessageThenWrapsWithGenericOne() {\n\t\tBearerTokenAuthenticationToken token = this.authentication();\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willThrow(new BadJwtException(\"with \\\"invalid\\\" chars\"));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t\t.satisfies((ex) -> assertThat(ex).hasFieldOrPropertyWithValue(\"error.description\", \"Invalid token\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-7785\n\t@Test\n\tpublic void authenticateWhenDecoderFailsGenericallyThenThrowsGenericException() {\n\t\tBearerTokenAuthenticationToken token = this.authentication();\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willThrow(new JwtException(\"no jwk set\"));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t\t.isNotInstanceOf(OAuth2AuthenticationException.class);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenConverterReturnsAuthenticationThenProviderPropagatesIt() {\n\t\tBearerTokenAuthenticationToken token = this.authentication();\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt);\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willReturn(jwt);\n\t\tgiven(this.jwtAuthenticationConverter.convert(jwt)).willReturn(authentication);\n\n\t\tassertThat(this.provider.authenticate(token)).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenConverterDoesNotSetAuthenticationDetailsThenProviderSetsItWithTokenDetails() {\n\t\tBearerTokenAuthenticationToken token = this.authentication();\n\t\tObject details = mock(Object.class);\n\t\ttoken.setDetails(details);\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt);\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willReturn(jwt);\n\t\tgiven(this.jwtAuthenticationConverter.convert(jwt)).willReturn(authentication);\n\t\t// @formatter:off\n\t\tassertThat(this.provider.authenticate(token))\n\t\t\t\t.isEqualTo(authentication).hasFieldOrPropertyWithValue(\"details\",\n\t\t\t\t\t\tdetails);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenConverterSetsAuthenticationDetailsThenProviderDoesNotOverwriteIt() {\n\t\tBearerTokenAuthenticationToken token = this.authentication();\n\t\tObject details = mock(Object.class);\n\t\ttoken.setDetails(details);\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tJwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt);\n\t\tObject expectedDetails = \"To be kept as is\";\n\t\tauthentication.setDetails(expectedDetails);\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willReturn(jwt);\n\t\tgiven(this.jwtAuthenticationConverter.convert(jwt)).willReturn(authentication);\n\t\t// @formatter:off\n\t\tassertThat(this.provider.authenticate(token))\n\t\t\t\t.isEqualTo(authentication).hasFieldOrPropertyWithValue(\"details\",\n\t\t\t\t\t\texpectedDetails);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenConverterReturnsNullThenThrowException() {\n\t\tBearerTokenAuthenticationToken token = this.authentication();\n\t\tJwt jwt = TestJwts.jwt().build();\n\t\tgiven(this.jwtDecoder.decode(\"token\")).willReturn(jwt);\n\t\tgiven(this.jwtAuthenticationConverter.convert(jwt)).willReturn(null);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t\t.withMessageContaining(\"token cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void supportsWhenBearerTokenAuthenticationTokenThenReturnsTrue() {\n\t\tassertThat(this.provider.supports(BearerTokenAuthenticationToken.class)).isTrue();\n\t}\n\n\tprivate BearerTokenAuthenticationToken authentication() {\n\t\treturn new BearerTokenAuthenticationToken(\"token\");\n\t}\n\n\tprivate Predicate<? super Throwable> errorCode(String errorCode) {\n\t\treturn (failed) -> ((OAuth2AuthenticationException) failed).getError().getErrorCode() == errorCode;\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.jose.jws.JwsAlgorithms;\nimport org.springframework.security.oauth2.jwt.Jwt;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link JwtAuthenticationToken}\n *\n * @author Josh Cummings\n */\n@ExtendWith(MockitoExtension.class)\npublic class JwtAuthenticationTokenTests {\n\n\t@Test\n\tpublic void getNameWhenJwtHasSubjectThenReturnsSubject() {\n\t\tJwt jwt = builder().subject(\"Carl\").build();\n\t\tJwtAuthenticationToken token = new JwtAuthenticationToken(jwt);\n\t\tassertThat(token.getName()).isEqualTo(\"Carl\");\n\t}\n\n\t@Test\n\tpublic void getNameWhenJwtHasNoSubjectThenReturnsEmptyString() {\n\t\tJwt jwt = builder().claim(\"claim\", \"value\").build();\n\t\tJwtAuthenticationToken token = new JwtAuthenticationToken(jwt);\n\t\tassertThat(token.getName()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwtIsNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new JwtAuthenticationToken((Jwt) null))\n\t\t\t.withMessageContaining(\"token cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenUsingCorrectParametersThenConstructedCorrectly() {\n\t\tCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"test\");\n\t\tJwt jwt = builder().claim(\"claim\", \"value\").build();\n\t\tJwtAuthenticationToken token = new JwtAuthenticationToken(jwt, authorities);\n\t\tassertThat(token.getAuthorities()).isEqualTo(authorities);\n\t\tassertThat(token.getPrincipal()).isEqualTo(jwt);\n\t\tassertThat(token.getCredentials()).isEqualTo(jwt);\n\t\tassertThat(token.getToken()).isEqualTo(jwt);\n\t\tassertThat(token.getTokenAttributes()).isEqualTo(jwt.getClaims());\n\t\tassertThat(token.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tpublic void constructorWhenUsingOnlyJwtThenConstructedCorrectly() {\n\t\tJwt jwt = builder().claim(\"claim\", \"value\").build();\n\t\tJwtAuthenticationToken token = new JwtAuthenticationToken(jwt);\n\t\tassertThat(token.getAuthorities()).isEmpty();\n\t\tassertThat(token.getPrincipal()).isEqualTo(jwt);\n\t\tassertThat(token.getCredentials()).isEqualTo(jwt);\n\t\tassertThat(token.getToken()).isEqualTo(jwt);\n\t\tassertThat(token.getTokenAttributes()).isEqualTo(jwt.getClaims());\n\t\tassertThat(token.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void getNameWhenConstructedWithJwtThenReturnsSubject() {\n\t\tJwt jwt = builder().subject(\"Hayden\").build();\n\t\tJwtAuthenticationToken token = new JwtAuthenticationToken(jwt);\n\t\tassertThat(token.getName()).isEqualTo(\"Hayden\");\n\t}\n\n\t@Test\n\tpublic void getNameWhenConstructedWithJwtAndAuthoritiesThenReturnsSubject() {\n\t\tCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"test\");\n\t\tJwt jwt = builder().subject(\"Hayden\").build();\n\t\tJwtAuthenticationToken token = new JwtAuthenticationToken(jwt, authorities);\n\t\tassertThat(token.getName()).isEqualTo(\"Hayden\");\n\t}\n\n\t@Test\n\tpublic void getNameWhenConstructedWithNameThenReturnsProvidedName() {\n\t\tCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"test\");\n\t\tJwt jwt = builder().claim(\"claim\", \"value\").build();\n\t\tJwtAuthenticationToken token = new JwtAuthenticationToken(jwt, authorities, \"Hayden\");\n\t\tassertThat(token.getName()).isEqualTo(\"Hayden\");\n\t}\n\n\t@Test\n\tpublic void getNameWhenConstructedWithNoSubjectThenReturnsEmptyString() {\n\t\tCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"test\");\n\t\tJwt jwt = builder().claim(\"claim\", \"value\").build();\n\t\tassertThat(new JwtAuthenticationToken(jwt, authorities, (String) null).getName()).isEmpty();\n\t\tassertThat(new JwtAuthenticationToken(jwt, authorities).getName()).isEmpty();\n\t\tassertThat(new JwtAuthenticationToken(jwt).getName()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void toBuilderWhenApplyThenCopies() {\n\t\tJwtAuthenticationToken factorOne = new JwtAuthenticationToken(builder().claim(\"c\", \"v\").build(),\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"), \"alice\");\n\t\tJwtAuthenticationToken factorTwo = new JwtAuthenticationToken(builder().claim(\"d\", \"w\").build(),\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"), \"bob\");\n\t\tJwtAuthenticationToken result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.name(factorTwo.getName())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(result.getName()).isSameAs(factorTwo.getName());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n\tprivate Jwt.Builder builder() {\n\t\treturn Jwt.withTokenValue(\"token\").header(\"alg\", JwsAlgorithms.RS256);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtBearerTokenAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.jwt.Jwt;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link JwtBearerTokenAuthenticationConverter}\n *\n * @author Josh Cummings\n */\npublic class JwtBearerTokenAuthenticationConverterTests {\n\n\tprivate final JwtBearerTokenAuthenticationConverter converter = new JwtBearerTokenAuthenticationConverter();\n\n\t@Test\n\tpublic void convertWhenJwtThenBearerTokenAuthentication() {\n\t\t// @formatter:off\n\t\tJwt jwt = Jwt.withTokenValue(\"token-value\")\n\t\t\t\t.claim(\"claim\", \"value\")\n\t\t\t\t.header(\"header\", \"value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAbstractAuthenticationToken token = this.converter.convert(jwt);\n\t\tassertThat(token).isInstanceOf(BearerTokenAuthentication.class);\n\t\tBearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;\n\t\tassertThat(bearerToken.getToken().getTokenValue()).isEqualTo(\"token-value\");\n\t\tassertThat(bearerToken.getTokenAttributes()).containsOnlyKeys(\"claim\");\n\t\tassertThat(bearerToken.getAuthorities()).noneMatch(isScope());\n\t}\n\n\t@Test\n\tpublic void convertWhenJwtWithScopeAttributeThenBearerTokenAuthentication() {\n\t\t// @formatter:off\n\t\tJwt jwt = Jwt.withTokenValue(\"token-value\")\n\t\t\t\t.claim(\"scope\", \"message:read message:write\")\n\t\t\t\t.header(\"header\", \"value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAbstractAuthenticationToken token = this.converter.convert(jwt);\n\t\tassertThat(token).isInstanceOf(BearerTokenAuthentication.class);\n\t\tBearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;\n\t\tSecurityAssertions.assertThat(bearerToken).hasAuthorities(\"SCOPE_message:read\", \"SCOPE_message:write\");\n\t}\n\n\t@Test\n\tpublic void convertWhenJwtWithScpAttributeThenBearerTokenAuthentication() {\n\t\t// @formatter:off\n\t\tJwt jwt = Jwt.withTokenValue(\"token-value\")\n\t\t\t\t.claim(\"scp\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t\t.header(\"header\", \"value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAbstractAuthenticationToken token = this.converter.convert(jwt);\n\t\tassertThat(token).isInstanceOf(BearerTokenAuthentication.class);\n\t\tBearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;\n\t\tSecurityAssertions.assertThat(bearerToken).hasAuthorities(\"SCOPE_message:read\", \"SCOPE_message:write\");\n\t}\n\n\t@Test\n\tpublic void convertWhenJwtPrincipalConverterSetThenCustomPrincipalUsed() {\n\t\tOAuth2AuthenticatedPrincipal customPrincipal = new DefaultOAuth2AuthenticatedPrincipal(\"custom-name\",\n\t\t\t\tMap.of(\"claim\", \"value\"), List.of());\n\t\tthis.converter.setJwtPrincipalConverter((jwt) -> customPrincipal);\n\t\t// @formatter:off\n\t\tJwt jwt = Jwt.withTokenValue(\"token-value\")\n\t\t\t\t.claim(\"claim\", \"value\")\n\t\t\t\t.header(\"header\", \"value\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAbstractAuthenticationToken token = this.converter.convert(jwt);\n\t\tassertThat(token).isInstanceOf(BearerTokenAuthentication.class);\n\t\tBearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;\n\t\tassertThat(bearerToken.getName()).isEqualTo(\"custom-name\");\n\t}\n\n\tstatic Predicate<GrantedAuthority> isScope() {\n\t\treturn (a) -> a.getAuthority().startsWith(\"SCOPE_\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link JwtGrantedAuthoritiesConverter}\n *\n * @author Eric Deandrea\n * @since 5.2\n */\npublic class JwtGrantedAuthoritiesConverterTests {\n\n\t@Test\n\tpublic void setAuthorityPrefixWithNullThenException() {\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> jwtGrantedAuthoritiesConverter.setAuthorityPrefix(null));\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scope\", \"message:read message:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"SCOPE_message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void convertWithCustomAuthorityPrefixWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scope\", \"message:read message:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tjwtGrantedAuthoritiesConverter.setAuthorityPrefix(\"ROLE_\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"ROLE_message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void convertWithBlankAsCustomAuthorityPrefixWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scope\", \"message:read message:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tjwtGrantedAuthoritiesConverter.setAuthorityPrefix(\"\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"message:write\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scope\", \"\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scp\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"SCOPE_message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void convertWithCustomAuthorityPrefixWhenTokenHasScpAttributeThenTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scp\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tjwtGrantedAuthoritiesConverter.setAuthorityPrefix(\"ROLE_\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"ROLE_message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"ROLE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void convertWithBlankAsCustomAuthorityPrefixWhenTokenHasScpAttributeThenTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scp\", \"message:read message:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tjwtGrantedAuthoritiesConverter.setAuthorityPrefix(\"\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"message:write\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scp\", Collections.emptyList())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasBothScopeAndScpThenScopeAttributeIsTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scp\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t\t.claim(\"scope\", \"missive:read missive:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"SCOPE_missive:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_missive:write\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasEmptyScopeAndNonEmptyScpThenScopeAttributeIsTranslatedToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scp\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t\t.claim(\"scope\", \"\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasEmptyScopeAndEmptyScpAttributeThenTranslatesToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scp\", Collections.emptyList())\n\t\t\t\t.claim(\"scope\", Collections.emptyList())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasNoScopeAndNoScpAttributeThenTranslatesToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"roles\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasUnsupportedTypeForScopeThenTranslatesToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scope\", new String[] { \"message:read\", \"message:write\" })\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"roles\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t\t.claim(\"scope\", \"missive:read missive:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tjwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(\"roles\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"SCOPE_message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasEmptyCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"roles\", Collections.emptyList())\n\t\t\t\t.claim(\"scope\", \"missive:read missive:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tjwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(\"roles\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasNoCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scope\", \"missive:read missive:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tjwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(\"roles\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void convertWithCustomAuthoritiesSplitRegexWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scope\", \"message:read,message:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tJwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\t\tjwtGrantedAuthoritiesConverter.setAuthoritiesClaimDelimiter(\",\");\n\t\tCollection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"SCOPE_message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.JWSHeader;\nimport com.nimbusds.jose.JWSObject;\nimport com.nimbusds.jose.Payload;\nimport com.nimbusds.jose.crypto.RSASSASigner;\nimport com.nimbusds.jwt.JWTClaimsSet;\nimport com.nimbusds.jwt.PlainJWT;\nimport net.minidev.json.JSONObject;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.MockedStatic;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.jwt.JwtDecoder;\nimport org.springframework.security.oauth2.jwt.JwtDecoders;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.mockito.BDDMockito.verify;\nimport static org.mockito.Mockito.mockStatic;\n\n/**\n * Tests for {@link JwtIssuerAuthenticationManagerResolver}\n */\npublic class JwtIssuerAuthenticationManagerResolverTests {\n\n\tprivate String jwt = jwt(\"iss\", \"trusted\");\n\n\tprivate String evil = jwt(\"iss\", \"\\\"\");\n\n\tprivate String noIssuer = jwt(\"sub\", \"sub\");\n\n\t@Test\n\tpublic void resolveWhenUsingFromTrustedIssuersThenReturnsAuthenticationManager() throws Exception {\n\t\tString issuer = \"https://idp.example\";\n\n\t\t// @formatter:on\n\t\tJWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),\n\t\t\t\tnew Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));\n\t\tjws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));\n\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(issuer);\n\t\tAuthentication token = withBearerToken(jws.serialize());\n\t\tAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);\n\t\tassertThat(authenticationManager).isNotNull();\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\t\tJwt jwt = TestJwts.user();\n\t\tgiven(decoder.decode(token.getName())).willReturn(jwt);\n\t\ttry (MockedStatic<JwtDecoders> jwtDecoders = mockStatic(JwtDecoders.class)) {\n\t\t\tgiven(JwtDecoders.fromIssuerLocation(issuer)).willReturn(decoder);\n\t\t\tAuthentication authentication = authenticationManager.authenticate(token);\n\t\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingFromTrustedIssuersPredicateThenReturnsAuthenticationManager() throws Exception {\n\t\tString issuer = \"https://idp.example\";\n\n\t\t// @formatter:on\n\t\tJWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),\n\t\t\t\tnew Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));\n\t\tjws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));\n\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(issuer::equals);\n\t\tAuthentication token = withBearerToken(jws.serialize());\n\t\tJwtDecoder decoder = mock(JwtDecoder.class);\n\t\tJwt jwt = TestJwts.user();\n\t\tgiven(decoder.decode(token.getName())).willReturn(jwt);\n\t\ttry (MockedStatic<JwtDecoders> jwtDecoders = mockStatic(JwtDecoders.class)) {\n\t\t\tgiven(JwtDecoders.fromIssuerLocation(issuer)).willReturn(decoder);\n\t\t\tAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);\n\t\t\tassertThat(authenticationManager).isNotNull();\n\t\t\tAuthentication authentication = authenticationManager.authenticate(token);\n\t\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingUntrustedIssuerThenException() {\n\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(\"other\", \"issuers\");\n\t\tAuthentication token = withBearerToken(this.jwt);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))\n\t\t\t\t.withMessageContaining(\"Invalid issuer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingCustomIssuerAuthenticationManagerResolverThenUses() {\n\t\tAuthenticationManager authenticationManager = mock(AuthenticationManager.class);\n\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(\n\t\t\t\t(issuer) -> authenticationManager);\n\t\tAuthentication token = withBearerToken(this.jwt);\n\t\tauthenticationManagerResolver.resolve(null).authenticate(token);\n\t\tverify(authenticationManager).authenticate(token);\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingExternalSourceThenRespondsToChanges() {\n\t\tAuthentication token = withBearerToken(this.jwt);\n\t\tMap<String, AuthenticationManager> authenticationManagers = new HashMap<>();\n\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(\n\t\t\t\tauthenticationManagers::get);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))\n\t\t\t\t.withMessageContaining(\"Invalid issuer\");\n\t\t// @formatter:on\n\t\tAuthenticationManager authenticationManager = mock(AuthenticationManager.class);\n\t\tauthenticationManagers.put(\"trusted\", authenticationManager);\n\t\tauthenticationManagerResolver.resolve(null).authenticate(token);\n\t\tverify(authenticationManager).authenticate(token);\n\t\tauthenticationManagers.clear();\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))\n\t\t\t\t.withMessageContaining(\"Invalid issuer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenBearerTokenMalformedThenException() {\n\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(\"trusted\");\n\t\tAuthentication token = withBearerToken(\"jwt\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))\n\t\t\t\t.withMessageNotContaining(\"Invalid issuer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenBearerTokenNoIssuerThenException() {\n\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(\"trusted\");\n\t\tAuthentication token = withBearerToken(this.noIssuer);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))\n\t\t\t\t.withMessageContaining(\"Missing issuer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenBearerTokenEvilThenGenericException() {\n\t\tJwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(\"trusted\");\n\t\tAuthentication token = withBearerToken(this.evil);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver\n\t\t\t\t\t\t.resolve(null).authenticate(token)\n\t\t\t\t)\n\t\t\t\t.withMessage(\"Invalid issuer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthenticationExceptionThenAuthenticationRequestIsIncluded() {\n\t\tAuthentication authentication = new BearerTokenAuthenticationToken(this.jwt);\n\t\tAuthenticationException ex = new InvalidBearerTokenException(\"\");\n\t\tAuthenticationManager manager = mock(AuthenticationManager.class);\n\t\tgiven(manager.authenticate(any())).willThrow(ex);\n\t\tJwtIssuerAuthenticationManagerResolver resolver = new JwtIssuerAuthenticationManagerResolver(\n\t\t\t\t(issuer) -> manager);\n\t\tassertThatExceptionOfType(InvalidBearerTokenException.class)\n\t\t\t.isThrownBy(() -> resolver.resolve(null).authenticate(authentication));\n\t\tassertThat(ex.getAuthenticationRequest()).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void factoryWhenNullOrEmptyIssuersThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> JwtIssuerAuthenticationManagerResolver.fromTrustedIssuers((Predicate<String>) null));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> JwtIssuerAuthenticationManagerResolver.fromTrustedIssuers((Collection<String>) null));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> JwtIssuerAuthenticationManagerResolver.fromTrustedIssuers(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullAuthenticationManagerResolverThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new JwtIssuerAuthenticationManagerResolver((AuthenticationManagerResolver) null));\n\t}\n\n\tprivate Authentication withBearerToken(String token) {\n\t\treturn new BearerTokenAuthenticationToken(token);\n\t}\n\n\tprivate String jwt(String claim, String value) {\n\t\tPlainJWT jwt = new PlainJWT(new JWTClaimsSet.Builder().claim(claim, value).build());\n\t\treturn jwt.serialize();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport com.nimbusds.jose.JWSAlgorithm;\nimport com.nimbusds.jose.JWSHeader;\nimport com.nimbusds.jose.JWSObject;\nimport com.nimbusds.jose.Payload;\nimport com.nimbusds.jose.crypto.RSASSASigner;\nimport com.nimbusds.jwt.JWTClaimsSet;\nimport com.nimbusds.jwt.PlainJWT;\nimport net.minidev.json.JSONObject;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.jose.TestKeys;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtIssuerReactiveAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.mockito.BDDMockito.verify;\n\n/**\n * Tests for {@link JwtIssuerReactiveAuthenticationManagerResolver}\n */\npublic class JwtIssuerReactiveAuthenticationManagerResolverTests {\n\n\t// @formatter:off\n\tprivate static final String DEFAULT_RESPONSE_TEMPLATE = \"{\\n\"\n\t\t\t+ \"    \\\"issuer\\\": \\\"%s\\\", \\n\"\n\t\t\t+ \"    \\\"jwks_uri\\\": \\\"%s/.well-known/jwks.json\\\" \\n\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tprivate static final String JWK_SET = \"{\\\"keys\\\":[{\\\"kty\\\":\\\"RSA\\\",\\\"e\\\":\\\"AQAB\\\",\\\"use\\\":\\\"sig\\\",\\\"kid\\\":\\\"one\\\",\\\"n\\\":\\\"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\\\"}]}\";\n\n\tprivate String jwt = jwt(\"iss\", \"trusted\");\n\n\tprivate String evil = jwt(\"iss\", \"\\\"\");\n\n\tprivate String noIssuer = jwt(\"sub\", \"sub\");\n\n\t@Test\n\tpublic void resolveWhenUsingFromTrustedIssuersThenReturnsAuthenticationManager() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString issuer = server.url(\"\").toString();\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t\t.setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t\t.setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t.setBody(JWK_SET));\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t\t.setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t.setBody(JWK_SET));\n\t\t\tJWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),\n\t\t\t\t\tnew Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));\n\t\t\tjws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));\n\t\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n\t\t\t\t.fromTrustedIssuers(issuer);\n\t\t\tReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();\n\t\t\tassertThat(authenticationManager).isNotNull();\n\t\t\tBearerTokenAuthenticationToken token = withBearerToken(jws.serialize());\n\t\t\tAuthentication authentication = authenticationManager.authenticate(token).block();\n\t\t\tassertThat(authentication).isNotNull();\n\t\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingFromTrustedIssuersPredicateThenReturnsAuthenticationManager() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString issuer = server.url(\"\").toString();\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t\t.setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t\t.setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t.setBody(JWK_SET));\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t\t.setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t.setBody(JWK_SET));\n\t\t\tJWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),\n\t\t\t\t\tnew Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));\n\t\t\tjws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));\n\t\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n\t\t\t\t.fromTrustedIssuers(issuer::equals);\n\t\t\tReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();\n\t\t\tassertThat(authenticationManager).isNotNull();\n\t\t\tBearerTokenAuthenticationToken token = withBearerToken(jws.serialize());\n\t\t\tAuthentication authentication = authenticationManager.authenticate(token).block();\n\t\t\tassertThat(authentication).isNotNull();\n\t\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\t}\n\t}\n\n\t// gh-10444\n\t@Test\n\tpublic void resolveWhednUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString issuer = server.url(\"\").toString();\n\t\t\t// @formatter:off\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(500).setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t\t.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200).setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t\t.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200).setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t\t.setBody(JWK_SET));\n\t\t\t// @formatter:on\n\t\t\tJWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),\n\t\t\t\t\tnew Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));\n\t\t\tjws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));\n\t\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n\t\t\t\t.fromTrustedIssuers(issuer);\n\t\t\tReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();\n\t\t\tassertThat(authenticationManager).isNotNull();\n\t\t\tAuthentication token = withBearerToken(jws.serialize());\n\t\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManager.authenticate(token).block());\n\t\t\tAuthentication authentication = authenticationManager.authenticate(token).block();\n\t\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingSameIssuerThenReturnsSameAuthenticationManager() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString issuer = server.url(\"\").toString();\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t\t.setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));\n\t\t\tserver.enqueue(new MockResponse().setResponseCode(200)\n\t\t\t\t.setHeader(\"Content-Type\", \"application/json\")\n\t\t\t\t.setBody(JWK_SET));\n\t\t\tTrustedIssuerJwtAuthenticationManagerResolver resolver = new TrustedIssuerJwtAuthenticationManagerResolver(\n\t\t\t\t\t(iss) -> iss.equals(issuer));\n\t\t\tReactiveAuthenticationManager authenticationManager = resolver.resolve(issuer).block();\n\t\t\tReactiveAuthenticationManager cachedAuthenticationManager = resolver.resolve(issuer).block();\n\t\t\tassertThat(authenticationManager).isSameAs(cachedAuthenticationManager);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingUntrustedIssuerThenException() {\n\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(\"other\", \"issuers\");\n\t\tAuthentication token = withBearerToken(this.jwt);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null)\n\t\t\t\t\t\t.flatMap((authenticationManager) -> authenticationManager.authenticate(token))\n\t\t\t\t\t\t.block())\n\t\t\t\t.withMessageContaining(\"Invalid issuer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingCustomIssuerAuthenticationManagerResolverThenUses() {\n\t\tAuthentication token = withBearerToken(this.jwt);\n\t\tReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tgiven(authenticationManager.authenticate(token)).willReturn(Mono.empty());\n\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(\n\t\t\t\t(issuer) -> Mono.just(authenticationManager));\n\t\tauthenticationManagerResolver.resolve(null).flatMap((manager) -> manager.authenticate(token)).block();\n\t\tverify(authenticationManager).authenticate(any());\n\t}\n\n\t@Test\n\tpublic void resolveWhenUsingExternalSourceThenRespondsToChanges() {\n\t\tAuthentication token = withBearerToken(this.jwt);\n\t\tMap<String, ReactiveAuthenticationManager> authenticationManagers = new HashMap<>();\n\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(\n\t\t\t\t(issuer) -> Mono.justOrEmpty(authenticationManagers.get(issuer)));\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null)\n\t\t\t\t.flatMap((manager) -> manager.authenticate(token))\n\t\t\t\t.block())\n\t\t\t.withMessageContaining(\"Invalid issuer\");\n\t\tReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);\n\t\tgiven(authenticationManager.authenticate(token)).willReturn(Mono.empty());\n\t\tauthenticationManagers.put(\"trusted\", authenticationManager);\n\t\tauthenticationManagerResolver.resolve(null).flatMap((manager) -> manager.authenticate(token)).block();\n\t\tverify(authenticationManager).authenticate(token);\n\t\tauthenticationManagers.clear();\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null)\n\t\t\t\t\t\t.flatMap((manager) -> manager.authenticate(token))\n\t\t\t\t\t\t.block())\n\t\t\t\t.withMessageContaining(\"Invalid issuer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenBearerTokenMalformedThenException() {\n\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(\"trusted\");\n\t\tAuthentication token = withBearerToken(\"jwt\");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null)\n\t\t\t\t\t\t.flatMap((manager) -> manager.authenticate(token))\n\t\t\t\t\t\t.block())\n\t\t\t\t.withMessageNotContaining(\"Invalid issuer\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenBearerTokenNoIssuerThenException() {\n\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(\"trusted\");\n\t\tAuthentication token = withBearerToken(this.noIssuer);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null)\n\t\t\t\t.flatMap((manager) -> manager.authenticate(token))\n\t\t\t\t.block())\n\t\t\t.withMessageContaining(\"Missing issuer\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenBearerTokenEvilThenGenericException() {\n\t\tJwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver\n\t\t\t.fromTrustedIssuers(\"trusted\");\n\t\tAuthentication token = withBearerToken(this.evil);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> authenticationManagerResolver.resolve(null)\n\t\t\t\t\t\t.flatMap((manager) -> manager.authenticate(token))\n\t\t\t\t\t\t.block())\n\t\t\t\t.withMessage(\"Invalid token\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenAuthenticationExceptionThenAuthenticationRequestIsIncluded() {\n\t\tAuthentication authentication = new BearerTokenAuthenticationToken(this.jwt);\n\t\tAuthenticationException ex = new InvalidBearerTokenException(\"\");\n\t\tReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);\n\t\tgiven(manager.authenticate(any())).willReturn(Mono.error(ex));\n\t\tJwtIssuerReactiveAuthenticationManagerResolver resolver = new JwtIssuerReactiveAuthenticationManagerResolver(\n\t\t\t\t(issuer) -> Mono.just(manager));\n\t\tStepVerifier.create(resolver.resolve(null).block().authenticate(authentication))\n\t\t\t.expectError(InvalidBearerTokenException.class)\n\t\t\t.verify();\n\t\tassertThat(ex.getAuthenticationRequest()).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void factoryWhenNullOrEmptyIssuersThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> JwtIssuerReactiveAuthenticationManagerResolver.fromTrustedIssuers((Predicate<String>) null));\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> JwtIssuerReactiveAuthenticationManagerResolver.fromTrustedIssuers((Collection<String>) null));\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> JwtIssuerReactiveAuthenticationManagerResolver.fromTrustedIssuers(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullAuthenticationManagerResolverThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new JwtIssuerReactiveAuthenticationManagerResolver((ReactiveAuthenticationManagerResolver) null));\n\t}\n\n\tprivate String jwt(String claim, String value) {\n\t\tPlainJWT jwt = new PlainJWT(new JWTClaimsSet.Builder().claim(claim, value).build());\n\t\treturn jwt.serialize();\n\t}\n\n\tprivate BearerTokenAuthenticationToken withBearerToken(String token) {\n\t\treturn new BearerTokenAuthenticationToken(token);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.jwt.BadJwtException;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtException;\nimport org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class JwtReactiveAuthenticationManagerTests {\n\n\t@Mock\n\tprivate ReactiveJwtDecoder jwtDecoder;\n\n\tprivate JwtReactiveAuthenticationManager manager;\n\n\tprivate Jwt jwt;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.manager = new JwtReactiveAuthenticationManager(this.jwtDecoder);\n\t\t// @formatter:off\n\t\tthis.jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scope\", \"message:read message:write\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenJwtDecoderNullThenIllegalArgumentException() {\n\t\tthis.jwtDecoder = null;\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new JwtReactiveAuthenticationManager(this.jwtDecoder));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenWrongTypeThenEmpty() {\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"foo\", \"bar\");\n\t\tassertThat(this.manager.authenticate(token).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenEmptyJwtThenEmpty() {\n\t\tBearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken(\"token-1\");\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willReturn(Mono.empty());\n\t\tassertThat(this.manager.authenticate(token).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenJwtExceptionThenOAuth2AuthenticationException() {\n\t\tBearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken(\"token-1\");\n\t\tgiven(this.jwtDecoder.decode(any())).willReturn(Mono.error(new BadJwtException(\"Oops\")));\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(token).block());\n\t}\n\n\t// gh-7549\n\t@Test\n\tpublic void authenticateWhenDecoderThrowsIncompatibleErrorMessageThenWrapsWithGenericOne() {\n\t\tBearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken(\"token-1\");\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willThrow(new BadJwtException(\"with \\\"invalid\\\" chars\"));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.manager.authenticate(token).block())\n\t\t\t\t.satisfies((ex) -> assertThat(ex)\n\t\t\t\t\t\t.hasFieldOrPropertyWithValue(\"error.description\", \"Invalid token\")\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t// gh-7785\n\t@Test\n\tpublic void authenticateWhenDecoderFailsGenericallyThenThrowsGenericException() {\n\t\tBearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken(\"token-1\");\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willThrow(new JwtException(\"no jwk set\"));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> this.manager.authenticate(token).block())\n\t\t\t\t.isNotInstanceOf(OAuth2AuthenticationException.class);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNotJwtExceptionThenPropagates() {\n\t\tBearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken(\"token-1\");\n\t\tgiven(this.jwtDecoder.decode(any())).willReturn(Mono.error(new RuntimeException(\"Oops\")));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t\t.isThrownBy(() -> this.manager.authenticate(token).block());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenJwtThenSuccess() {\n\t\tBearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken(\"token-1\");\n\t\tgiven(this.jwtDecoder.decode(token.getToken())).willReturn(Mono.just(this.jwt));\n\t\tAuthentication authentication = this.manager.authenticate(token).block();\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"SCOPE_message:read\", \"SCOPE_message:write\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link OpaqueTokenAuthenticationProvider}\n *\n * @author Josh Cummings\n */\npublic class OpaqueTokenAuthenticationProviderTests {\n\n\t@Test\n\tpublic void authenticateWhenActiveTokenThenOk() throws Exception {\n\t\tOAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals\n\t\t\t.active((attributes) -> attributes.put(\"extension_field\", \"twenty-seven\"));\n\t\tOpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);\n\t\tgiven(introspector.introspect(any())).willReturn(principal);\n\t\tOpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);\n\t\tAuthentication result = provider.authenticate(new BearerTokenAuthenticationToken(\"token\"));\n\t\tassertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);\n\t\tMap<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();\n\t\t// @formatter:off\n\t\tassertThat(attributes)\n\t\t\t\t.isNotNull()\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,\n\t\t\t\t\t\tArrays.asList(\"https://protected.example.net/resource\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, \"l238j323ds-23ij4\")\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, new URL(\"https://server.example.com/\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList(\"read\", \"write\", \"dolphin\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\")\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, \"jdoe\")\n\t\t\t\t.containsEntry(\"extension_field\", \"twenty-seven\");\n\t\tSecurityAssertions.assertThat(result).hasAuthorities(\"SCOPE_read\", \"SCOPE_write\", \"SCOPE_dolphin\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingScopeAttributeThenNoAuthorities() {\n\t\tOAuth2AuthenticatedPrincipal principal = new OAuth2IntrospectionAuthenticatedPrincipal(\n\t\t\t\tCollections.singletonMap(\"claim\", \"value\"), null);\n\t\tOpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);\n\t\tgiven(introspector.introspect(any())).willReturn(principal);\n\t\tOpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);\n\t\tAuthentication result = provider.authenticate(new BearerTokenAuthenticationToken(\"token\"));\n\t\tassertThat(result.getPrincipal()).isInstanceOf(OAuth2AuthenticatedPrincipal.class);\n\t\tMap<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();\n\t\t// @formatter:off\n\t\tassertThat(attributes)\n\t\t\t\t.isNotNull()\n\t\t\t\t.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t// @formatter:on\n\t\tSecurityAssertions.assertThat(result).authorities().noneMatch(isScope());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() {\n\t\tOpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);\n\t\tgiven(introspector.introspect(any())).willThrow(new OAuth2IntrospectionException(\"with \\\"invalid\\\" chars\"));\n\t\tOpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> provider.authenticate(new BearerTokenAuthenticationToken(\"token\")));\n\t}\n\n\t@Test\n\tpublic void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OpaqueTokenAuthenticationProvider(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowsIllegalArgumentException() {\n\t\tOpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);\n\t\tOpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> provider.setAuthenticationConverter(null))\n\t\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationConverterThenUses() {\n\t\tOpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);\n\t\tOAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals.active();\n\t\tgiven(introspector.introspect(any())).willReturn(principal);\n\t\tOpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);\n\t\tOpaqueTokenAuthenticationConverter authenticationConverter = mock(OpaqueTokenAuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any(), any(OAuth2AuthenticatedPrincipal.class)))\n\t\t\t.willReturn(new TestingAuthenticationToken(principal, null, Collections.emptyList()));\n\t\tprovider.setAuthenticationConverter(authenticationConverter);\n\n\t\tAuthentication result = provider.authenticate(new BearerTokenAuthenticationToken(\"token\"));\n\t\tassertThat(result).isNotNull();\n\t\tverify(introspector).introspect(\"token\");\n\t\tverify(authenticationConverter).convert(\"token\", principal);\n\t\tverifyNoMoreInteractions(introspector, authenticationConverter);\n\t}\n\n\t@Test\n\tvoid authenticateWhenSuccessThenIssuesFactor() {\n\t\tOAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals.active();\n\t\tOpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);\n\t\tgiven(introspector.introspect(any())).willReturn(principal);\n\t\tOpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);\n\t\tAuthentication request = new BearerTokenAuthenticationToken(\"token\");\n\t\tAuthentication result = provider.authenticate(request);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.BEARER_AUTHORITY);\n\t}\n\n\tstatic Predicate<GrantedAuthority> isScope() {\n\t\treturn (a) -> a.getAuthority().startsWith(\"SCOPE_\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.net.URL;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link OpaqueTokenReactiveAuthenticationManager}\n *\n * @author Josh Cummings\n */\npublic class OpaqueTokenReactiveAuthenticationManagerTests {\n\n\t@Test\n\tpublic void authenticateWhenActiveTokenThenOk() throws Exception {\n\t\tOAuth2AuthenticatedPrincipal authority = TestOAuth2AuthenticatedPrincipals\n\t\t\t.active((attributes) -> attributes.put(\"extension_field\", \"twenty-seven\"));\n\t\tReactiveOpaqueTokenIntrospector introspector = mock(ReactiveOpaqueTokenIntrospector.class);\n\t\tgiven(introspector.introspect(any())).willReturn(Mono.just(authority));\n\t\tOpaqueTokenReactiveAuthenticationManager provider = new OpaqueTokenReactiveAuthenticationManager(introspector);\n\t\tAuthentication result = provider.authenticate(new BearerTokenAuthenticationToken(\"token\")).block();\n\t\tassertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);\n\t\tMap<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();\n\t\t// @formatter:off\n\t\tassertThat(attributes)\n\t\t\t\t.isNotNull()\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,\n\t\t\t\t\t\tArrays.asList(\"https://protected.example.net/resource\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, \"l238j323ds-23ij4\")\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, new URL(\"https://server.example.com/\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList(\"read\", \"write\", \"dolphin\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\")\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, \"jdoe\")\n\t\t\t\t.containsEntry(\"extension_field\", \"twenty-seven\");\n\t\tSecurityAssertions.assertThat(result).hasAuthorities(\"SCOPE_read\", \"SCOPE_write\", \"SCOPE_dolphin\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingScopeAttributeThenNoAuthorities() {\n\t\tOAuth2AuthenticatedPrincipal authority = new OAuth2IntrospectionAuthenticatedPrincipal(\n\t\t\t\tCollections.singletonMap(\"claim\", \"value\"), null);\n\t\tReactiveOpaqueTokenIntrospector introspector = mock(ReactiveOpaqueTokenIntrospector.class);\n\t\tgiven(introspector.introspect(any())).willReturn(Mono.just(authority));\n\t\tOpaqueTokenReactiveAuthenticationManager provider = new OpaqueTokenReactiveAuthenticationManager(introspector);\n\t\tAuthentication result = provider.authenticate(new BearerTokenAuthenticationToken(\"token\")).block();\n\t\tassertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);\n\t\tMap<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();\n\t\tassertThat(attributes).isNotNull().doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\tSecurityAssertions.assertThat(result).authorities().noneMatch(isScope());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() {\n\t\tReactiveOpaqueTokenIntrospector introspector = mock(ReactiveOpaqueTokenIntrospector.class);\n\t\tgiven(introspector.introspect(any()))\n\t\t\t.willReturn(Mono.error(new OAuth2IntrospectionException(\"with \\\"invalid\\\" chars\")));\n\t\tOpaqueTokenReactiveAuthenticationManager provider = new OpaqueTokenReactiveAuthenticationManager(introspector);\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> provider.authenticate(new BearerTokenAuthenticationToken(\"token\")).block());\n\t}\n\n\t@Test\n\tpublic void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new OpaqueTokenReactiveAuthenticationManager(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowsIllegalArgumentException() {\n\t\tReactiveOpaqueTokenIntrospector introspector = mock(ReactiveOpaqueTokenIntrospector.class);\n\t\tOpaqueTokenReactiveAuthenticationManager provider = new OpaqueTokenReactiveAuthenticationManager(introspector);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> provider.setAuthenticationConverter(null))\n\t\t\t\t.withMessage(\"authenticationConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAuthenticationConverterThenUses() {\n\t\tReactiveOpaqueTokenIntrospector introspector = mock(ReactiveOpaqueTokenIntrospector.class);\n\t\tOAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals.active();\n\t\tgiven(introspector.introspect(any())).willReturn(Mono.just(principal));\n\t\tOpaqueTokenReactiveAuthenticationManager provider = new OpaqueTokenReactiveAuthenticationManager(introspector);\n\t\tReactiveOpaqueTokenAuthenticationConverter authenticationConverter = mock(\n\t\t\t\tReactiveOpaqueTokenAuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(any(), any(OAuth2AuthenticatedPrincipal.class)))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(principal, null, Collections.emptyList())));\n\t\tprovider.setAuthenticationConverter(authenticationConverter);\n\n\t\tAuthentication result = provider.authenticate(new BearerTokenAuthenticationToken(\"token\")).block();\n\t\tassertThat(result).isNotNull();\n\t\tverify(introspector).introspect(\"token\");\n\t\tverify(authenticationConverter).convert(\"token\", principal);\n\t\tverifyNoMoreInteractions(introspector, authenticationConverter);\n\t}\n\n\tstatic Predicate<GrantedAuthority> isScope() {\n\t\treturn (a) -> a.getAuthority().startsWith(\"SCOPE_\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/ReactiveJwtAuthenticationConverterAdapterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Arrays;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\n/**\n * Tests for {@link ReactiveJwtAuthenticationConverterAdapter}\n *\n * @author Josh Cummings\n */\npublic class ReactiveJwtAuthenticationConverterAdapterTests {\n\n\tConverter<Jwt, AbstractAuthenticationToken> converter = new JwtAuthenticationConverter();\n\n\tReactiveJwtAuthenticationConverterAdapter jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverterAdapter(\n\t\t\tthis.converter);\n\n\t@Test\n\tpublic void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scope\", \"message:read message:write\").build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"SCOPE_message:read\", \"SCOPE_message:write\");\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scope\", \"\").build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tSecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scp\", Arrays.asList(\"message:read\", \"message:write\")).build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"SCOPE_message:read\", \"SCOPE_message:write\");\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scp\", Arrays.asList()).build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tSecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasBothScopeAndScpThenScopeAttributeIsTranslatedToAuthorities() {\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t.claim(\"scp\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t.claim(\"scope\", \"missive:read missive:write\")\n\t\t\t.build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tSecurityAssertions.assertThat(authentication).hasAuthorities(\"SCOPE_missive:read\", \"SCOPE_missive:write\");\n\t}\n\n\t@Test\n\tpublic void convertWhenTokenHasEmptyScopeAndNonEmptyScpThenScopeAttributeIsTranslatedToNoAuthorities() {\n\t\t// @formatter:off\n\t\tJwt jwt = TestJwts.jwt()\n\t\t\t\t.claim(\"scp\", Arrays.asList(\"message:read\", \"message:write\"))\n\t\t\t\t.claim(\"scope\", \"\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tSecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());\n\t}\n\n\tstatic Predicate<GrantedAuthority> isScope() {\n\t\treturn (a) -> a.getAuthority().startsWith(\"SCOPE_\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/ReactiveJwtAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Flux;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ReactiveJwtAuthenticationConverter}\n *\n * @author Eric Deandrea\n * @author Marcus Kainth\n * @since 5.2\n */\npublic class ReactiveJwtAuthenticationConverterTests {\n\n\tReactiveJwtAuthenticationConverter jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();\n\n\t@Test\n\tpublic void convertWhenDefaultGrantedAuthoritiesConverterSet() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scope\", \"message:read message:write\").build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tCollection<GrantedAuthority> authorities = authentication.getAuthorities();\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"SCOPE_message:read\"),\n\t\t\t\tnew SimpleGrantedAuthority(\"SCOPE_message:write\"));\n\t}\n\n\t@Test\n\tpublic void whenSettingNullGrantedAuthoritiesConverter() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(null))\n\t\t\t.withMessage(\"jwtGrantedAuthoritiesConverter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void convertWithOverriddenGrantedAuthoritiesConverter() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scope\", \"message:read message:write\").build();\n\t\tConverter<Jwt, Flux<GrantedAuthority>> grantedAuthoritiesConverter = (token) -> Flux\n\t\t\t.just(new SimpleGrantedAuthority(\"blah\"));\n\t\tthis.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tCollection<GrantedAuthority> authorities = authentication.getAuthorities();\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"blah\"));\n\t}\n\n\t@Test\n\tpublic void whenSettingNullPrincipalClaimName() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(null))\n\t\t\t\t.withMessage(\"principalClaimName cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void whenSettingEmptyPrincipalClaimName() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(\"\"))\n\t\t\t\t.withMessage(\"principalClaimName cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void whenSettingBlankPrincipalClaimName() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(\" \"))\n\t\t\t\t.withMessage(\"principalClaimName cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void convertWhenPrincipalClaimNameSet() {\n\t\tthis.jwtAuthenticationConverter.setPrincipalClaimName(\"user_id\");\n\t\tJwt jwt = TestJwts.jwt().claim(\"user_id\", \"100\").build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tassertThat(authentication.getName()).isEqualTo(\"100\");\n\t}\n\n\t@Test\n\tpublic void convertWhenPrincipalClaimNameSetAndClaimValueIsNotString() {\n\t\tthis.jwtAuthenticationConverter.setPrincipalClaimName(\"user_id\");\n\t\tJwt jwt = TestJwts.jwt().claim(\"user_id\", 100).build();\n\t\tAbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();\n\t\tassertThat(authentication.getName()).isEqualTo(\"100\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/ReactiveJwtGrantedAuthoritiesConverterAdapterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ReactiveJwtGrantedAuthoritiesConverterAdapter}\n *\n * @author Eric Deandrea\n * @since 5.2\n */\npublic class ReactiveJwtGrantedAuthoritiesConverterAdapterTests {\n\n\t@Test\n\tpublic void convertWithGrantedAuthoritiesConverter() {\n\t\tJwt jwt = TestJwts.jwt().claim(\"scope\", \"message:read message:write\").build();\n\t\tConverter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter = (token) -> Arrays\n\t\t\t.asList(new SimpleGrantedAuthority(\"blah\"));\n\t\tCollection<GrantedAuthority> authorities = new ReactiveJwtGrantedAuthoritiesConverterAdapter(\n\t\t\t\tgrantedAuthoritiesConverter)\n\t\t\t.convert(jwt)\n\t\t\t.toStream()\n\t\t\t.collect(Collectors.toList());\n\t\tassertThat(authorities).containsExactly(new SimpleGrantedAuthority(\"blah\"));\n\t}\n\n\t@Test\n\tpublic void whenConstructingWithInvalidConverter() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new ReactiveJwtGrantedAuthoritiesConverterAdapter(null))\n\t\t\t\t.withMessage(\"grantedAuthoritiesConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/TestBearerTokenAuthentications.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.authentication;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\n\n/**\n * Test instances of {@link BearerTokenAuthentication}\n *\n * @author Josh Cummings\n */\npublic final class TestBearerTokenAuthentications {\n\n\tprivate TestBearerTokenAuthentications() {\n\t}\n\n\tpublic static BearerTokenAuthentication bearer() {\n\t\tCollection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"SCOPE_USER\");\n\t\tOAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(\n\t\t\t\tCollections.singletonMap(\"sub\", \"user\"), authorities);\n\t\tOAuth2AccessToken token = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\", Instant.now(),\n\t\t\t\tInstant.now().plusSeconds(86400), new HashSet<>(Arrays.asList(\"USER\")));\n\t\treturn new BearerTokenAuthentication(principal, token, authorities);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/OAuth2IntrospectionAuthenticatedPrincipalTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OAuth2IntrospectionAuthenticatedPrincipal}\n *\n * @author David Kovac\n */\npublic class OAuth2IntrospectionAuthenticatedPrincipalTests {\n\n\tprivate static final String AUTHORITY = \"SCOPE_read\";\n\n\tprivate static final Collection<GrantedAuthority> AUTHORITIES = AuthorityUtils.createAuthorityList(AUTHORITY);\n\n\tprivate static final String SUBJECT = \"test-subject\";\n\n\tprivate static final String ACTIVE_CLAIM = \"active\";\n\n\tprivate static final String CLIENT_ID_CLAIM = \"client_id\";\n\n\tprivate static final String USERNAME_CLAIM = \"username\";\n\n\tprivate static final String TOKEN_TYPE_CLAIM = \"token_type\";\n\n\tprivate static final String EXP_CLAIM = \"exp\";\n\n\tprivate static final String IAT_CLAIM = \"iat\";\n\n\tprivate static final String NBF_CLAIM = \"nbf\";\n\n\tprivate static final String SUB_CLAIM = \"sub\";\n\n\tprivate static final String AUD_CLAIM = \"aud\";\n\n\tprivate static final String ISS_CLAIM = \"iss\";\n\n\tprivate static final String JTI_CLAIM = \"jti\";\n\n\tprivate static final boolean ACTIVE_VALUE = true;\n\n\tprivate static final String CLIENT_ID_VALUE = \"client-id-1\";\n\n\tprivate static final String USERNAME_VALUE = \"username-1\";\n\n\tprivate static final String TOKEN_TYPE_VALUE = \"token-type-1\";\n\n\tprivate static final long EXP_VALUE = Instant.now().plusSeconds(60).getEpochSecond();\n\n\tprivate static final long IAT_VALUE = Instant.now().getEpochSecond();\n\n\tprivate static final long NBF_VALUE = Instant.now().plusSeconds(5).getEpochSecond();\n\n\tprivate static final String SUB_VALUE = \"subject1\";\n\n\tprivate static final List<String> AUD_VALUE = Arrays.asList(\"aud1\", \"aud2\");\n\n\tprivate static final String ISS_VALUE = \"https://provider.com\";\n\n\tprivate static final String JTI_VALUE = \"jwt-id-1\";\n\n\tprivate static final Map<String, Object> CLAIMS;\n\tstatic {\n\t\tCLAIMS = new HashMap<>();\n\t\tCLAIMS.put(ACTIVE_CLAIM, ACTIVE_VALUE);\n\t\tCLAIMS.put(CLIENT_ID_CLAIM, CLIENT_ID_VALUE);\n\t\tCLAIMS.put(USERNAME_CLAIM, USERNAME_VALUE);\n\t\tCLAIMS.put(TOKEN_TYPE_CLAIM, TOKEN_TYPE_VALUE);\n\t\tCLAIMS.put(EXP_CLAIM, EXP_VALUE);\n\t\tCLAIMS.put(IAT_CLAIM, IAT_VALUE);\n\t\tCLAIMS.put(NBF_CLAIM, NBF_VALUE);\n\t\tCLAIMS.put(SUB_CLAIM, SUB_VALUE);\n\t\tCLAIMS.put(AUD_CLAIM, AUD_VALUE);\n\t\tCLAIMS.put(ISS_CLAIM, ISS_VALUE);\n\t\tCLAIMS.put(JTI_CLAIM, JTI_VALUE);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAttributesIsNullOrEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2IntrospectionAuthenticatedPrincipal(null, AUTHORITIES));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OAuth2IntrospectionAuthenticatedPrincipal(Collections.emptyMap(), AUTHORITIES));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAuthoritiesIsNullOrEmptyThenNoAuthorities() {\n\t\tCollection<? extends GrantedAuthority> authorities = new OAuth2IntrospectionAuthenticatedPrincipal(CLAIMS, null)\n\t\t\t.getAuthorities();\n\t\tassertThat(authorities).isEmpty();\n\t\tauthorities = new OAuth2IntrospectionAuthenticatedPrincipal(CLAIMS, Collections.emptyList()).getAuthorities();\n\t\tassertThat(authorities).isEmpty();\n\t}\n\n\t@Test\n\tpublic void constructorWhenNameIsNullThenFallsbackToSubAttribute() {\n\t\tOAuth2AuthenticatedPrincipal principal = new OAuth2IntrospectionAuthenticatedPrincipal(null, CLAIMS,\n\t\t\t\tAUTHORITIES);\n\t\tassertThat(principal.getName()).isEqualTo(CLAIMS.get(SUB_CLAIM));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAttributesAuthoritiesProvidedThenCreated() {\n\t\tOAuth2IntrospectionAuthenticatedPrincipal principal = new OAuth2IntrospectionAuthenticatedPrincipal(CLAIMS,\n\t\t\t\tAUTHORITIES);\n\t\tassertThat(principal.getName()).isEqualTo(CLAIMS.get(SUB_CLAIM));\n\t\tassertThat(principal.getAttributes()).isEqualTo(CLAIMS);\n\t\tassertThat(principal.getClaims()).isEqualTo(CLAIMS);\n\t\tassertThat(principal.isActive()).isEqualTo(ACTIVE_VALUE);\n\t\tassertThat(principal.getClientId()).isEqualTo(CLIENT_ID_VALUE);\n\t\tassertThat(principal.getUsername()).isEqualTo(USERNAME_VALUE);\n\t\tassertThat(principal.getTokenType()).isEqualTo(TOKEN_TYPE_VALUE);\n\t\tassertThat(principal.getExpiresAt().getEpochSecond()).isEqualTo(EXP_VALUE);\n\t\tassertThat(principal.getIssuedAt().getEpochSecond()).isEqualTo(IAT_VALUE);\n\t\tassertThat(principal.getNotBefore().getEpochSecond()).isEqualTo(NBF_VALUE);\n\t\tassertThat(principal.getSubject()).isEqualTo(SUB_VALUE);\n\t\tassertThat(principal.getAudience()).isEqualTo(AUD_VALUE);\n\t\tassertThat(principal.getIssuer().toString()).isEqualTo(ISS_VALUE);\n\t\tassertThat(principal.getId()).isEqualTo(JTI_VALUE);\n\t\tassertThat(principal.getAuthorities()).hasSize(1);\n\t\tassertThat(principal.getAuthorities().iterator().next().getAuthority()).isEqualTo(AUTHORITY);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAllParametersProvidedAndValidThenCreated() {\n\t\tOAuth2IntrospectionAuthenticatedPrincipal principal = new OAuth2IntrospectionAuthenticatedPrincipal(SUBJECT,\n\t\t\t\tCLAIMS, AUTHORITIES);\n\t\tassertThat(principal.getName()).isEqualTo(SUBJECT);\n\t\tassertThat(principal.getAttributes()).isEqualTo(CLAIMS);\n\t\tassertThat(principal.getClaims()).isEqualTo(CLAIMS);\n\t\tassertThat(principal.isActive()).isEqualTo(ACTIVE_VALUE);\n\t\tassertThat(principal.getClientId()).isEqualTo(CLIENT_ID_VALUE);\n\t\tassertThat(principal.getUsername()).isEqualTo(USERNAME_VALUE);\n\t\tassertThat(principal.getTokenType()).isEqualTo(TOKEN_TYPE_VALUE);\n\t\tassertThat(principal.getExpiresAt().getEpochSecond()).isEqualTo(EXP_VALUE);\n\t\tassertThat(principal.getIssuedAt().getEpochSecond()).isEqualTo(IAT_VALUE);\n\t\tassertThat(principal.getNotBefore().getEpochSecond()).isEqualTo(NBF_VALUE);\n\t\tassertThat(principal.getSubject()).isEqualTo(SUB_VALUE);\n\t\tassertThat(principal.getAudience()).isEqualTo(AUD_VALUE);\n\t\tassertThat(principal.getIssuer().toString()).isEqualTo(ISS_VALUE);\n\t\tassertThat(principal.getId()).isEqualTo(JTI_VALUE);\n\t\tassertThat(principal.getAuthorities()).hasSize(1);\n\t\tassertThat(principal.getAuthorities().iterator().next().getAuthority()).isEqualTo(AUTHORITY);\n\t}\n\n\t@Test\n\tpublic void getNameWhenInConstructorThenReturns() {\n\t\tOAuth2AuthenticatedPrincipal principal = new OAuth2IntrospectionAuthenticatedPrincipal(SUB_VALUE, CLAIMS,\n\t\t\t\tAUTHORITIES);\n\t\tassertThat(principal.getName()).isEqualTo(SUB_VALUE);\n\t}\n\n\t@Test\n\tpublic void getAttributeWhenGivenKeyThenReturnsValue() {\n\t\tOAuth2AuthenticatedPrincipal principal = new OAuth2IntrospectionAuthenticatedPrincipal(CLAIMS, AUTHORITIES);\n\t\tassertHasEqualAttribute(principal, ACTIVE_CLAIM, ACTIVE_VALUE);\n\t\tassertHasEqualAttribute(principal, CLIENT_ID_CLAIM, CLIENT_ID_VALUE);\n\t\tassertHasEqualAttribute(principal, USERNAME_CLAIM, USERNAME_VALUE);\n\t\tassertHasEqualAttribute(principal, TOKEN_TYPE_CLAIM, TOKEN_TYPE_VALUE);\n\t\tassertHasEqualAttribute(principal, EXP_CLAIM, EXP_VALUE);\n\t\tassertHasEqualAttribute(principal, IAT_CLAIM, IAT_VALUE);\n\t\tassertHasEqualAttribute(principal, NBF_CLAIM, NBF_VALUE);\n\t\tassertHasEqualAttribute(principal, SUB_CLAIM, SUB_VALUE);\n\t\tassertHasEqualAttribute(principal, AUD_CLAIM, AUD_VALUE);\n\t\tassertHasEqualAttribute(principal, ISS_CLAIM, ISS_VALUE);\n\t\tassertHasEqualAttribute(principal, JTI_CLAIM, JTI_VALUE);\n\t}\n\n\tprivate void assertHasEqualAttribute(OAuth2AuthenticatedPrincipal principal, String name, Object expected) {\n\t\tObject value = principal.getAttribute(name);\n\t\tassertThat(value).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/RestClientOpaqueTokenIntrospectorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport com.nimbusds.jose.util.JSONObjectUtils;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link RestClientOpaqueTokenIntrospector}\n *\n * @author Andrey Litvitski\n */\npublic class RestClientOpaqueTokenIntrospectorTests {\n\n\tprivate static final String INTROSPECTION_URL = \"https://server.example.com\";\n\n\tprivate static final String CLIENT_ID = \"client\";\n\n\tprivate static final String CLIENT_SECRET = \"secret\";\n\n\t// @formatter:off\n\tprivate static final String ACTIVE_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": true,\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": \\\"read write dolphin\\\",\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String INACTIVE_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": false\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String INVALID_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": \\\"read write dolphin\\\",\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String MALFORMED_SCOPE_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": true,\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": [ \\\"read\\\", \\\"write\\\", \\\"dolphin\\\" ],\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t@Test\n\tpublic void introspectWhenActiveTokenThenOk() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\");\n\t\t\t// @formatter:off\n\t\t\tassertThat(authority.getAttributes())\n\t\t\t\t\t.isNotNull()\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,\n\t\t\t\t\t\t\tArrays.asList(\"https://protected.example.net/resource\"))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, \"l238j323ds-23ij4\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, \"https://server.example.com/\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList(\"read\", \"write\", \"dolphin\"))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, \"jdoe\")\n\t\t\t\t\t.containsEntry(\"extension_field\", \"twenty-seven\");\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWhenBadClientCredentialsThenError() throws IOException {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(\"wrong\")\n\t\t\t\t.build();\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWhenInactiveTokenThenInvalidToken() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, INACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"))\n\t\t\t\t.withMessage(\"Provided token isn't active\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWhenActiveTokenThenParsesValuesInResponse() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString response = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t  \"active\": true,\n\t\t\t\t\t  \"aud\": [\"aud\"],\n\t\t\t\t\t  \"nbf\": 29348723984\n\t\t\t\t\t}\n\t\t\t\t\t\"\"\";\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, response));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\");\n\t\t\tassertThat(authority.getAttributes()).isNotNull()\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList(\"aud\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))\n\t\t\t\t.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.CLIENT_ID)\n\t\t\t\t.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tserver.start();\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tserver.shutdown();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWhenIntrospectionEndpointReturnsMalformedResponseThenInvalidToken() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, \"{}\"));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWhenIntrospectionTokenReturnsInvalidResponseThenInvalidToken() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, INVALID_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t\t}\n\t}\n\n\t// gh-7563\n\t@Test\n\tpublic void introspectWhenIntrospectionTokenReturnsMalformedScopeThenEmptyAuthorities() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, MALFORMED_SCOPE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal principal = introspectionClient.introspect(\"token\");\n\t\t\tassertThat(principal.getAuthorities()).isEmpty();\n\t\t\tCollection<String> scope = principal.getAttribute(\"scope\");\n\t\t\tassertThat(scope).containsExactly(\"read\", \"write\", \"dolphin\");\n\t\t}\n\t}\n\n\t// gh-15165\n\t@Test\n\tpublic void introspectWhenActiveThenMapsAuthorities() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = RestClientOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal principal = introspectionClient.introspect(\"token\");\n\t\t\tassertThat(principal.getAuthorities()).isNotEmpty();\n\t\t\tCollection<String> scope = principal.getAttribute(\"scope\");\n\t\t\tassertThat(scope).containsExactly(\"read\", \"write\", \"dolphin\");\n\t\t\tCollection<String> authorities = AuthorityUtils.authorityListToSet(principal.getAuthorities());\n\t\t\tassertThat(authorities).containsExactly(\"SCOPE_read\", \"SCOPE_write\", \"SCOPE_dolphin\");\n\t\t}\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenConverterIsNullThenExceptionIsThrown() {\n\t\tRestClient restClient = mock(RestClient.class);\n\t\tRestClientOpaqueTokenIntrospector introspectionClient = new RestClientOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestClient);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> introspectionClient.setAuthenticationConverter(null));\n\t}\n\n\t@Test\n\tpublic void introspectWithoutEncodeClientCredentialsThenExceptionIsThrown() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString response = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"active\": true,\n\t\t\t\t\t\t\"username\": \"client%&1\"\n\t\t\t\t\t}\n\t\t\t\t\t\"\"\";\n\t\t\tserver.setDispatcher(requiresAuth(\"client%25%261\", \"secret%40%242\", response));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tRestClient restClient = RestClient.builder()\n\t\t\t\t.defaultHeaders((h) -> h.setBasicAuth(\"client%&1\", \"secret@$2\"))\n\t\t\t\t.build();\n\t\t\tOpaqueTokenIntrospector introspectionClient = new RestClientOpaqueTokenIntrospector(introspectUri,\n\t\t\t\t\trestClient);\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWithEncodeClientCredentialsThenOk() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString response = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"active\": true,\n\t\t\t\t\t\t\"username\": \"client&1\"\n\t\t\t\t\t}\n\t\t\t\t\t\"\"\";\n\t\t\tserver.setDispatcher(requiresAuth(\"client%261\", \"secret%40%242\", response));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = SpringOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(\"client&1\")\n\t\t\t\t.clientSecret(\"secret@$2\")\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\");\n\t\t\t// @formatter:off\n\t\t\tassertThat(authority.getAttributes())\n\t\t\t\t\t.isNotNull()\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, \"client&1\");\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\tprivate static ResponseEntity<Map<String, Object>> response(String content) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\ttry {\n\t\t\treturn new ResponseEntity<>(JSONObjectUtils.parse(content), headers, HttpStatus.OK);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static ResponseEntity<Map<String, Object>> response(Map<String, Object> content) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\ttry {\n\t\t\treturn new ResponseEntity<>(content, headers, HttpStatus.OK);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static Dispatcher requiresAuth(String username, String password, String response) {\n\t\treturn new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\tString authorization = request.getHeader(HttpHeaders.AUTHORIZATION);\n\t\t\t\t// @formatter:off\n\t\t\t\treturn Optional.ofNullable(authorization)\n\t\t\t\t\t\t.filter((a) -> isAuthorized(authorization, username, password))\n\t\t\t\t\t\t.map((a) -> ok(response))\n\t\t\t\t\t\t.orElse(unauthorized());\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static boolean isAuthorized(String authorization, String username, String password) {\n\t\tString[] values = new String(Base64.getDecoder().decode(authorization.substring(6))).split(\":\");\n\t\treturn username.equals(values[0]) && password.equals(values[1]);\n\t}\n\n\tprivate static MockResponse ok(String response) {\n\t\t// @formatter:off\n\t\treturn new MockResponse().setBody(response)\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\t// @formatter:on\n\t}\n\n\tprivate static MockResponse unauthorized() {\n\t\treturn new MockResponse().setResponseCode(401);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/SpringOpaqueTokenIntrospectorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport com.nimbusds.jose.util.JSONObjectUtils;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.RequestEntity;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.web.client.RestOperations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link SpringOpaqueTokenIntrospector}\n */\npublic class SpringOpaqueTokenIntrospectorTests {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<Map<String, Object>>() {\n\t};\n\n\tprivate static final String INTROSPECTION_URL = \"https://server.example.com\";\n\n\tprivate static final String CLIENT_ID = \"client\";\n\n\tprivate static final String CLIENT_SECRET = \"secret\";\n\n\t// @formatter:off\n\tprivate static final String ACTIVE_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": true,\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": \\\"read write dolphin\\\",\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String INACTIVE_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": false\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String INVALID_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": \\\"read write dolphin\\\",\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String MALFORMED_SCOPE_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": true,\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": [ \\\"read\\\", \\\"write\\\", \\\"dolphin\\\" ],\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\tprivate static final ResponseEntity<Map<String, Object>> ACTIVE = response(ACTIVE_RESPONSE);\n\n\tprivate static final ResponseEntity<Map<String, Object>> INACTIVE = response(INACTIVE_RESPONSE);\n\n\tprivate static final ResponseEntity<Map<String, Object>> INVALID = response(INVALID_RESPONSE);\n\n\tprivate static final ResponseEntity<Map<String, Object>> MALFORMED_SCOPE = response(MALFORMED_SCOPE_RESPONSE);\n\n\t@Test\n\tpublic void introspectWhenActiveTokenThenOk() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(introspectUri, CLIENT_ID,\n\t\t\t\t\tCLIENT_SECRET);\n\t\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\");\n\t\t\t// @formatter:off\n\t\t\tassertThat(authority.getAttributes())\n\t\t\t\t\t.isNotNull()\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,\n\t\t\t\t\t\t\tArrays.asList(\"https://protected.example.net/resource\"))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, \"l238j323ds-23ij4\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, \"https://server.example.com/\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList(\"read\", \"write\", \"dolphin\"))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, \"jdoe\")\n\t\t\t\t\t.containsEntry(\"extension_field\", \"twenty-seven\");\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWhenBadClientCredentialsThenError() throws IOException {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(introspectUri, CLIENT_ID,\n\t\t\t\t\t\"wrong\");\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWhenInactiveTokenThenInvalidToken() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(STRING_OBJECT_MAP))).willReturn(INACTIVE);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"))\n\t\t\t\t.withMessage(\"Provided token isn't active\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void introspectWhenActiveTokenThenParsesValuesInResponse() {\n\t\tMap<String, Object> introspectedValues = new HashMap<>();\n\t\tintrospectedValues.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, true);\n\t\tintrospectedValues.put(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList(\"aud\"));\n\t\tintrospectedValues.put(OAuth2TokenIntrospectionClaimNames.NBF, 29348723984L);\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(STRING_OBJECT_MAP)))\n\t\t\t.willReturn(response(introspectedValues));\n\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\");\n\t\t// @formatter:off\n\t\tassertThat(authority.getAttributes())\n\t\t\t\t.isNotNull()\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList(\"aud\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))\n\t\t\t\t.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.CLIENT_ID)\n\t\t\t\t.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void introspectWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(STRING_OBJECT_MAP)))\n\t\t\t.willThrow(new IllegalStateException(\"server was unresponsive\"));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"))\n\t\t\t\t.withMessage(\"server was unresponsive\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void introspectWhenIntrospectionEndpointReturnsMalformedResponseThenInvalidToken() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(STRING_OBJECT_MAP))).willReturn(response(\"{}\"));\n\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t}\n\n\t@Test\n\tpublic void introspectWhenIntrospectionTokenReturnsInvalidResponseThenInvalidToken() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(STRING_OBJECT_MAP))).willReturn(INVALID);\n\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t}\n\n\t// gh-7563\n\t@Test\n\tpublic void introspectWhenIntrospectionTokenReturnsMalformedScopeThenEmptyAuthorities() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(STRING_OBJECT_MAP))).willReturn(MALFORMED_SCOPE);\n\t\tOAuth2AuthenticatedPrincipal principal = introspectionClient.introspect(\"token\");\n\t\tassertThat(principal.getAuthorities()).isEmpty();\n\t\tCollection<String> scope = principal.getAttribute(\"scope\");\n\t\tassertThat(scope).containsExactly(\"read\", \"write\", \"dolphin\");\n\t}\n\n\t// gh-15165\n\t@Test\n\tpublic void introspectWhenActiveThenMapsAuthorities() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tgiven(restOperations.exchange(any(RequestEntity.class), eq(STRING_OBJECT_MAP))).willReturn(ACTIVE);\n\t\tOAuth2AuthenticatedPrincipal principal = introspectionClient.introspect(\"token\");\n\t\tassertThat(principal.getAuthorities()).isNotEmpty();\n\t\tCollection<String> scope = principal.getAttribute(\"scope\");\n\t\tassertThat(scope).containsExactly(\"read\", \"write\", \"dolphin\");\n\t\tCollection<String> authorities = AuthorityUtils.authorityListToSet(principal.getAuthorities());\n\t\tassertThat(authorities).containsExactly(\"SCOPE_read\", \"SCOPE_write\", \"SCOPE_dolphin\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenIntrospectionUriIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SpringOpaqueTokenIntrospector(null, CLIENT_ID, CLIENT_SECRET));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientIdIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SpringOpaqueTokenIntrospector(INTROSPECTION_URL, null, CLIENT_SECRET));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientSecretIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SpringOpaqueTokenIntrospector(INTROSPECTION_URL, CLIENT_ID, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRestOperationsIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SpringOpaqueTokenIntrospector(INTROSPECTION_URL, null));\n\t}\n\n\t@Test\n\tpublic void setRequestEntityConverterWhenConverterIsNullThenExceptionIsThrown() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tSpringOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> introspectionClient.setRequestEntityConverter(null));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Test\n\tpublic void setRequestEntityConverterWhenNonNullConverterGivenThenConverterUsed() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tConverter<String, RequestEntity<?>> requestEntityConverter = mock(Converter.class);\n\t\tRequestEntity requestEntity = mock(RequestEntity.class);\n\t\tString tokenToIntrospect = \"some token\";\n\t\tgiven(requestEntityConverter.convert(tokenToIntrospect)).willReturn(requestEntity);\n\t\tgiven(restOperations.exchange(requestEntity, STRING_OBJECT_MAP)).willReturn(ACTIVE);\n\t\tSpringOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tintrospectionClient.setRequestEntityConverter(requestEntityConverter);\n\t\tintrospectionClient.introspect(tokenToIntrospect);\n\t\tverify(requestEntityConverter).convert(tokenToIntrospect);\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenConverterIsNullThenExceptionIsThrown() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tSpringOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> introspectionClient.setAuthenticationConverter(null));\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNonNullConverterGivenThenConverterUsed() {\n\t\tRestOperations restOperations = mock(RestOperations.class);\n\t\tConverter<String, RequestEntity<?>> requestEntityConverter = mock(Converter.class);\n\t\tRequestEntity requestEntity = mock(RequestEntity.class);\n\t\tConverter<OAuth2TokenIntrospectionClaimAccessor, OAuth2AuthenticatedPrincipal> authenticationConverter = mock(\n\t\t\t\tConverter.class);\n\t\tOAuth2AuthenticatedPrincipal oAuth2AuthenticatedPrincipal = mock(OAuth2AuthenticatedPrincipal.class);\n\t\tString tokenToIntrospect = \"some token\";\n\t\tgiven(requestEntityConverter.convert(tokenToIntrospect)).willReturn(requestEntity);\n\t\tgiven(restOperations.exchange(requestEntity, STRING_OBJECT_MAP)).willReturn(response(ACTIVE_RESPONSE));\n\t\tgiven(authenticationConverter.convert(any())).willReturn(oAuth2AuthenticatedPrincipal);\n\t\tSpringOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,\n\t\t\t\trestOperations);\n\t\tintrospectionClient.setRequestEntityConverter(requestEntityConverter);\n\t\tintrospectionClient.setAuthenticationConverter(authenticationConverter);\n\t\tintrospectionClient.introspect(tokenToIntrospect);\n\t\tverify(authenticationConverter).convert(any());\n\t}\n\n\t@Test\n\tpublic void introspectWithoutEncodeClientCredentialsThenExceptionIsThrown() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString response = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"active\": true,\n\t\t\t\t\t\t\"username\": \"client%&1\"\n\t\t\t\t\t}\n\t\t\t\t\t\"\"\";\n\t\t\tserver.setDispatcher(requiresAuth(\"client%25%261\", \"secret%40%242\", response));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(introspectUri, \"client%&1\",\n\t\t\t\t\t\"secret@$2\");\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\"));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWithEncodeClientCredentialsThenOk() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString response = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"active\": true,\n\t\t\t\t\t\t\"username\": \"client&1\"\n\t\t\t\t\t}\n\t\t\t\t\t\"\"\";\n\t\t\tserver.setDispatcher(requiresAuth(\"client%261\", \"secret%40%242\", response));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tOpaqueTokenIntrospector introspectionClient = SpringOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(\"client&1\")\n\t\t\t\t.clientSecret(\"secret@$2\")\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\");\n\t\t\t// @formatter:off\n\t\t\tassertThat(authority.getAttributes())\n\t\t\t\t\t.isNotNull()\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, \"client&1\");\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void builderWhenPostProcessorSetThenApplied() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tConverter<OAuth2TokenIntrospectionClaimAccessor, OAuth2AuthenticatedPrincipal> authenticationConverter = mock(\n\t\t\t\t\tConverter.class);\n\t\t\tOAuth2AuthenticatedPrincipal principal = mock(OAuth2AuthenticatedPrincipal.class);\n\t\t\tgiven(authenticationConverter.convert(any())).willReturn(principal);\n\t\t\tOpaqueTokenIntrospector introspector = SpringOpaqueTokenIntrospector.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.postProcessor((i) -> i.setAuthenticationConverter(authenticationConverter))\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal result = introspector.introspect(\"token\");\n\t\t\tassertThat(result).isSameAs(principal);\n\t\t}\n\t}\n\n\tprivate static ResponseEntity<Map<String, Object>> response(String content) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\ttry {\n\t\t\treturn new ResponseEntity<>(JSONObjectUtils.parse(content), headers, HttpStatus.OK);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static ResponseEntity<Map<String, Object>> response(Map<String, Object> content) {\n\t\tHttpHeaders headers = new HttpHeaders();\n\t\theaders.setContentType(MediaType.APPLICATION_JSON);\n\t\ttry {\n\t\t\treturn new ResponseEntity<>(content, headers, HttpStatus.OK);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate static Dispatcher requiresAuth(String username, String password, String response) {\n\t\treturn new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\tString authorization = request.getHeader(HttpHeaders.AUTHORIZATION);\n\t\t\t\t// @formatter:off\n\t\t\t\treturn Optional.ofNullable(authorization)\n\t\t\t\t\t\t.filter((a) -> isAuthorized(authorization, username, password))\n\t\t\t\t\t\t.map((a) -> ok(response))\n\t\t\t\t\t\t.orElse(unauthorized());\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static boolean isAuthorized(String authorization, String username, String password) {\n\t\tString[] values = new String(Base64.getDecoder().decode(authorization.substring(6))).split(\":\");\n\t\treturn username.equals(values[0]) && password.equals(values[1]);\n\t}\n\n\tprivate static MockResponse ok(String response) {\n\t\t// @formatter:off\n\t\treturn new MockResponse().setBody(response)\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\t// @formatter:on\n\t}\n\n\tprivate static MockResponse unauthorized() {\n\t\treturn new MockResponse().setResponseCode(401);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/SpringReactiveOpaqueTokenIntrospectorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.introspection;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link SpringReactiveOpaqueTokenIntrospector}\n */\npublic class SpringReactiveOpaqueTokenIntrospectorTests {\n\n\tprivate static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<Map<String, Object>>() {\n\t};\n\n\tprivate static final String INTROSPECTION_URL = \"https://server.example.com\";\n\n\tprivate static final String CLIENT_ID = \"client\";\n\n\tprivate static final String CLIENT_SECRET = \"secret\";\n\n\t// @formatter:off\n\tprivate static final String ACTIVE_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": true,\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": \\\"read write dolphin\\\",\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String INACTIVE_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"active\\\": false\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String INVALID_RESPONSE = \"{\\n\"\n\t\t\t+ \"      \\\"client_id\\\": \\\"l238j323ds-23ij4\\\",\\n\"\n\t\t\t+ \"      \\\"username\\\": \\\"jdoe\\\",\\n\"\n\t\t\t+ \"      \\\"scope\\\": \\\"read write dolphin\\\",\\n\"\n\t\t\t+ \"      \\\"sub\\\": \\\"Z5O3upPC88QrAjx00dis\\\",\\n\"\n\t\t\t+ \"      \\\"aud\\\": \\\"https://protected.example.net/resource\\\",\\n\"\n\t\t\t+ \"      \\\"iss\\\": \\\"https://server.example.com/\\\",\\n\"\n\t\t\t+ \"      \\\"exp\\\": 1419356238,\\n\"\n\t\t\t+ \"      \\\"iat\\\": 1419350238,\\n\"\n\t\t\t+ \"      \\\"extension_field\\\": \\\"twenty-seven\\\"\\n\"\n\t\t\t+ \"     }\";\n\t// @formatter:on\n\n\tprivate final JsonMapper mapper = new JsonMapper();\n\n\t@Test\n\tpublic void authenticateWhenActiveTokenThenOk() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\t\tintrospectUri, CLIENT_ID, CLIENT_SECRET);\n\t\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\").block();\n\t\t\tassertThat(authority).isNotNull();\n\t\t\t// @formatter:off\n\t\t\tassertThat(authority.getAttributes())\n\t\t\t\t\t.isNotNull()\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD,\n\t\t\t\t\t\t\tArrays.asList(\"https://protected.example.net/resource\"))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, \"l238j323ds-23ij4\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.EXP, Instant.ofEpochSecond(1419356238))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ISS, \"https://server.example.com/\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SCOPE, Arrays.asList(\"read\", \"write\", \"dolphin\"))\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"Z5O3upPC88QrAjx00dis\")\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, \"jdoe\")\n\t\t\t\t\t.containsEntry(\"extension_field\", \"twenty-seven\");\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void authenticateWhenBadClientCredentialsThenAuthenticationException() throws IOException {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\t\tintrospectUri, CLIENT_ID, \"wrong\");\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\").block());\n\n\t\t}\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInactiveTokenThenInvalidToken() {\n\t\tWebClient webClient = mockResponse(INACTIVE_RESPONSE);\n\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\tINTROSPECTION_URL, webClient);\n\t\tassertThatExceptionOfType(BadOpaqueTokenException.class)\n\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\").block())\n\t\t\t.withMessage(\"Provided token isn't active\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenActiveTokenThenParsesValuesInResponse() {\n\t\tMap<String, Object> introspectedValues = new HashMap<>();\n\t\tintrospectedValues.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, true);\n\t\tintrospectedValues.put(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList(\"aud\"));\n\t\tintrospectedValues.put(OAuth2TokenIntrospectionClaimNames.NBF, 29348723984L);\n\t\tWebClient webClient = mockResponse(introspectedValues);\n\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\tINTROSPECTION_URL, webClient);\n\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\").block();\n\t\tassertThat(authority).isNotNull();\n\t\t// @formatter:off\n\t\tassertThat(authority.getAttributes())\n\t\t\t\t.isNotNull()\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.AUD, Arrays.asList(\"aud\"))\n\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.NBF, Instant.ofEpochSecond(29348723984L))\n\t\t\t\t.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.CLIENT_ID)\n\t\t\t\t.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() {\n\t\tWebClient webClient = mockResponse(new IllegalStateException(\"server was unresponsive\"));\n\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\tINTROSPECTION_URL, webClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\").block())\n\t\t\t\t.withMessage(\"server was unresponsive\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenIntrospectionTokenReturnsInvalidResponseThenInvalidToken() {\n\t\tWebClient webClient = mockResponse(INVALID_RESPONSE);\n\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\tINTROSPECTION_URL, webClient);\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\").block());\n\t\t// @formatter:on\n\t}\n\n\t// gh-15165\n\t@Test\n\tpublic void introspectWhenActiveThenMapsAuthorities() {\n\t\tWebClient webClient = mockResponse(ACTIVE_RESPONSE);\n\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\tINTROSPECTION_URL, webClient);\n\t\tOAuth2AuthenticatedPrincipal principal = introspectionClient.introspect(\"token\").block();\n\t\tassertThat(principal.getAuthorities()).isNotEmpty();\n\t\tCollection<String> scope = principal.getAttribute(\"scope\");\n\t\tassertThat(scope).containsExactly(\"read\", \"write\", \"dolphin\");\n\t\tCollection<String> authorities = AuthorityUtils.authorityListToSet(principal.getAuthorities());\n\t\tassertThat(authorities).containsExactly(\"SCOPE_read\", \"SCOPE_write\", \"SCOPE_dolphin\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenConverterIsNullThenExceptionIsThrown() {\n\t\tWebClient web = mock(WebClient.class);\n\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\tINTROSPECTION_URL, web);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> introspectionClient.setAuthenticationConverter(null));\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNonNullConverterGivenThenConverterUsed() {\n\t\tWebClient web = mockResponse(ACTIVE_RESPONSE);\n\t\tConverter<OAuth2TokenIntrospectionClaimAccessor, Mono<? extends OAuth2AuthenticatedPrincipal>> authenticationConverter = mock(\n\t\t\t\tConverter.class);\n\t\tOAuth2AuthenticatedPrincipal oAuth2AuthenticatedPrincipal = mock(OAuth2AuthenticatedPrincipal.class);\n\t\tString tokenToIntrospect = \"some token\";\n\t\tgiven(authenticationConverter.convert(any())).willReturn((Mono) Mono.just(oAuth2AuthenticatedPrincipal));\n\t\tSpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\tINTROSPECTION_URL, web);\n\t\tintrospectionClient.setAuthenticationConverter(authenticationConverter);\n\t\tintrospectionClient.introspect(tokenToIntrospect).block();\n\t\tverify(authenticationConverter).convert(any());\n\t}\n\n\t@Test\n\tpublic void constructorWhenIntrospectionUriIsEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SpringReactiveOpaqueTokenIntrospector(\"\", CLIENT_ID, CLIENT_SECRET));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientIdIsEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SpringReactiveOpaqueTokenIntrospector(INTROSPECTION_URL, \"\", CLIENT_SECRET));\n\t}\n\n\t@Test\n\tpublic void constructorWhenClientSecretIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SpringReactiveOpaqueTokenIntrospector(INTROSPECTION_URL, CLIENT_ID, null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRestOperationsIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new SpringReactiveOpaqueTokenIntrospector(INTROSPECTION_URL, null));\n\t}\n\n\t@Test\n\tpublic void introspectWithoutEncodeClientCredentialsThenExceptionIsThrown() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString response = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"active\": true,\n\t\t\t\t\t\t\"username\": \"client%&1\"\n\t\t\t\t\t}\n\t\t\t\t\t\"\"\";\n\t\t\tserver.setDispatcher(requiresAuth(\"client%25%261\", \"secret%40%242\", response));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(\n\t\t\t\t\tintrospectUri, \"client%&1\", \"secret@$2\");\n\t\t\t// @formatter:off\n\t\t\tassertThatExceptionOfType(OAuth2IntrospectionException.class)\n\t\t\t\t\t.isThrownBy(() -> introspectionClient.introspect(\"token\").block());\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void introspectWithEncodeClientCredentialsThenOk() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString response = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"active\": true,\n\t\t\t\t\t\t\"username\": \"client&1\"\n\t\t\t\t\t}\n\t\t\t\t\t\"\"\";\n\t\t\tserver.setDispatcher(requiresAuth(\"client%261\", \"secret%40%242\", response));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tReactiveOpaqueTokenIntrospector introspectionClient = SpringReactiveOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(\"client&1\")\n\t\t\t\t.clientSecret(\"secret@$2\")\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal authority = introspectionClient.introspect(\"token\").block();\n\t\t\t// @formatter:off\n\t\t\tassertThat(authority.getAttributes())\n\t\t\t\t\t.isNotNull()\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.ACTIVE, true)\n\t\t\t\t\t.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, \"client&1\");\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tpublic void builderWhenPostProcessorSetThenApplied() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.setDispatcher(requiresAuth(CLIENT_ID, CLIENT_SECRET, ACTIVE_RESPONSE));\n\t\t\tString introspectUri = server.url(\"/introspect\").toString();\n\t\t\tConverter<OAuth2TokenIntrospectionClaimAccessor, Mono<? extends OAuth2AuthenticatedPrincipal>> authenticationConverter = mock(\n\t\t\t\t\tConverter.class);\n\t\t\tOAuth2AuthenticatedPrincipal principal = mock(OAuth2AuthenticatedPrincipal.class);\n\t\t\tgiven(authenticationConverter.convert(any())).willReturn((Mono) Mono.just(principal));\n\t\t\tReactiveOpaqueTokenIntrospector introspector = SpringReactiveOpaqueTokenIntrospector\n\t\t\t\t.withIntrospectionUri(introspectUri)\n\t\t\t\t.clientId(CLIENT_ID)\n\t\t\t\t.clientSecret(CLIENT_SECRET)\n\t\t\t\t.postProcessor((i) -> i.setAuthenticationConverter(authenticationConverter))\n\t\t\t\t.build();\n\t\t\tOAuth2AuthenticatedPrincipal result = introspector.introspect(\"token\").block();\n\t\t\tassertThat(result).isSameAs(principal);\n\t\t}\n\t}\n\n\tprivate WebClient mockResponse(String response) {\n\t\treturn mockResponse(toMap(response));\n\t}\n\n\tprivate WebClient mockResponse(Map<String, Object> response) {\n\t\tWebClient real = WebClient.builder().build();\n\t\tWebClient.RequestBodyUriSpec spec = spy(real.post());\n\t\tWebClient webClient = spy(WebClient.class);\n\t\tgiven(webClient.post()).willReturn(spec);\n\t\tClientResponse clientResponse = mock(ClientResponse.class);\n\t\tgiven(clientResponse.statusCode()).willReturn(HttpStatus.OK);\n\t\tgiven(clientResponse.bodyToMono(STRING_OBJECT_MAP)).willReturn(Mono.just(response));\n\t\tClientResponse.Headers headers = mock(ClientResponse.Headers.class);\n\t\tgiven(headers.contentType()).willReturn(Optional.of(MediaType.APPLICATION_JSON));\n\t\tgiven(clientResponse.headers()).willReturn(headers);\n\t\tgiven(spec.exchangeToMono(any())).willAnswer((invocation) -> {\n\t\t\tFunction<ClientResponse, Mono<ClientResponse>> fun = invocation.getArgument(0);\n\t\t\treturn fun.apply(clientResponse);\n\t\t});\n\t\treturn webClient;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Map<String, Object> toMap(String string) {\n\t\ttry {\n\t\t\treturn this.mapper.readValue(string, Map.class);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate WebClient mockResponse(Throwable ex) {\n\t\tWebClient real = WebClient.builder().build();\n\t\tWebClient.RequestBodyUriSpec spec = spy(real.post());\n\t\tWebClient webClient = spy(WebClient.class);\n\t\tgiven(webClient.post()).willReturn(spec);\n\t\tgiven(spec.exchangeToMono(any())).willThrow(ex);\n\t\treturn webClient;\n\t}\n\n\tprivate static Dispatcher requiresAuth(String username, String password, String response) {\n\t\treturn new Dispatcher() {\n\t\t\t@Override\n\t\t\tpublic MockResponse dispatch(RecordedRequest request) {\n\t\t\t\tString authorization = request.getHeader(HttpHeaders.AUTHORIZATION);\n\t\t\t\t// @formatter:off\n\t\t\t\treturn Optional.ofNullable(authorization)\n\t\t\t\t\t\t.filter((a) -> isAuthorized(authorization, username, password))\n\t\t\t\t\t\t.map((a) -> ok(response))\n\t\t\t\t\t\t.orElse(unauthorized());\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate static boolean isAuthorized(String authorization, String username, String password) {\n\t\tString[] values = new String(Base64.getDecoder().decode(authorization.substring(6))).split(\":\");\n\t\treturn username.equals(values[0]) && password.equals(values[1]);\n\t}\n\n\tprivate static MockResponse ok(String response) {\n\t\t// @formatter:off\n\t\treturn new MockResponse().setBody(response)\n\t\t\t\t.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\t// @formatter:on\n\t}\n\n\tprivate static MockResponse unauthorized() {\n\t\treturn new MockResponse().setResponseCode(401);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link BearerTokenAuthenticationEntryPoint}.\n *\n * @author Vedran Pavic\n * @author Josh Cummings\n */\npublic class BearerTokenAuthenticationEntryPointTests {\n\n\tprivate BearerTokenAuthenticationEntryPoint authenticationEntryPoint;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();\n\t}\n\n\t@Test\n\tpublic void commenceWhenNoBearerTokenErrorThenStatus401AndAuthHeader() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.authenticationEntryPoint.commence(request, response, new BadCredentialsException(\"test\"));\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\"))\n\t\t\t.isEqualTo(\"Bearer resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenNoBearerTokenErrorAndRealmSetThenStatus401AndAuthHeaderWithRealm() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.authenticationEntryPoint.setRealmName(\"test\");\n\t\tthis.authenticationEntryPoint.commence(request, response, new BadCredentialsException(\"test\"));\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\n\t\t\t\t\"Bearer realm=\\\"test\\\", resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenNoBearerTokenErrorAndContextPathSetThenStatus401AndAuthHeaderWithContextPath() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"/ctx\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.authenticationEntryPoint.commence(request, response, new BadCredentialsException(\"test\"));\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\"))\n\t\t\t.isEqualTo(\"Bearer resource_metadata=\\\"http://localhost/ctx/.well-known/oauth-protected-resource\\\"\");\n\n\t}\n\n\t@Test\n\tpublic void commenceWhenNoBearerTokenErrorAndResourceMetadataResolverSetThenStatus401AndAuthHeaderWithResolvedResourceMetadata() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"resource_id\", \"https://example.com/resource-from-request\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.authenticationEntryPoint\n\t\t\t.setResourceMetadataParameterResolver((req) -> req.getAttribute(\"resource_id\").toString());\n\t\tthis.authenticationEntryPoint.commence(request, response, new BadCredentialsException(\"test\"));\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\"))\n\t\t\t.isEqualTo(\"Bearer resource_metadata=\\\"https://example.com/resource-from-request\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenInvalidRequestErrorThenStatus400AndHeaderWithError() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST,\n\t\t\t\tnull, null);\n\t\tthis.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error));\n\t\tassertThat(response.getStatus()).isEqualTo(400);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\n\t\t\t\t\"Bearer error=\\\"invalid_request\\\", resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenInvalidRequestErrorThenStatus400AndHeaderWithErrorDetails() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST,\n\t\t\t\t\"The access token expired\", null, null);\n\t\tthis.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error));\n\t\tassertThat(response.getStatus()).isEqualTo(400);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\n\t\t\t\t\"Bearer error=\\\"invalid_request\\\", error_description=\\\"The access token expired\\\", resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenInvalidRequestErrorThenStatus400AndHeaderWithErrorUri() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST,\n\t\t\t\tnull, \"https://example.com\", null);\n\t\tthis.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error));\n\t\tassertThat(response.getStatus()).isEqualTo(400);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\n\t\t\t\t\"Bearer error=\\\"invalid_request\\\", error_uri=\\\"https://example.com\\\", resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenInvalidTokenErrorThenStatus401AndHeaderWithError() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED,\n\t\t\t\tnull, null);\n\t\tthis.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error));\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\n\t\t\t\t\"Bearer error=\\\"invalid_token\\\", resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenInsufficientScopeErrorThenStatus403AndHeaderWithError() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN,\n\t\t\t\tnull, null);\n\t\tthis.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error));\n\t\tassertThat(response.getStatus()).isEqualTo(403);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\n\t\t\t\t\"Bearer error=\\\"insufficient_scope\\\", resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenInsufficientScopeErrorThenStatus403AndHeaderWithErrorAndScope() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN,\n\t\t\t\tnull, null, \"test.read test.write\");\n\t\tthis.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error));\n\t\tassertThat(response.getStatus()).isEqualTo(403);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\n\t\t\t\t\"Bearer error=\\\"insufficient_scope\\\", scope=\\\"test.read test.write\\\", resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenInsufficientScopeAndRealmSetThenStatus403AndHeaderWithErrorAndAllDetails()\n\t\t\tthrows Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN,\n\t\t\t\t\"Insufficient scope\", \"https://example.com\", \"test.read test.write\");\n\t\tthis.authenticationEntryPoint.setRealmName(\"test\");\n\t\tthis.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error));\n\t\tassertThat(response.getStatus()).isEqualTo(403);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\"))\n\t\t\t.isEqualTo(\"Bearer realm=\\\"test\\\", error=\\\"insufficient_scope\\\", error_description=\\\"Insufficient scope\\\", \"\n\t\t\t\t\t+ \"error_uri=\\\"https://example.com\\\", scope=\\\"test.read test.write\\\", resource_metadata=\\\"http://localhost/.well-known/oauth-protected-resource\\\"\");\n\t}\n\n\t@Test\n\tpublic void setRealmNameWhenNullRealmNameThenNoExceptionThrown() {\n\t\tthis.authenticationEntryPoint.setRealmName(null);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/DefaultBearerTokenResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link DefaultBearerTokenResolver}.\n *\n * @author Vedran Pavic\n */\npublic class DefaultBearerTokenResolverTests {\n\n\tprivate static final String CUSTOM_HEADER = \"custom-header\";\n\n\tprivate static final String TEST_TOKEN = \"test-token\";\n\n\tprivate DefaultBearerTokenResolver resolver;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.resolver = new DefaultBearerTokenResolver();\n\t}\n\n\t@Test\n\tpublic void resolveWhenValidHeaderIsPresentThenTokenIsResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Bearer \" + TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t// gh-8502\n\t@Test\n\tpublic void resolveWhenHeaderEndsWithPaddingIndicatorThenTokenIsResolved() {\n\t\tString token = TEST_TOKEN + \"==\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Bearer \" + token);\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(token);\n\t}\n\n\t@Test\n\tpublic void resolveWhenCustomDefinedHeaderIsValidAndPresentThenTokenIsResolved() {\n\t\tthis.resolver.setBearerTokenHeaderName(CUSTOM_HEADER);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(CUSTOM_HEADER, \"Bearer \" + TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenLowercaseHeaderIsPresentThenTokenIsResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"authorization\", \"bearer \" + TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenNoHeaderIsPresentThenTokenIsNotResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenHeaderWithWrongSchemeIsPresentThenTokenIsNotResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString(\"test:test\".getBytes()));\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenHeaderWithMissingTokenIsPresentThenAuthenticationExceptionIsThrown() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Bearer \");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t.withMessageContaining((\"Bearer token is malformed\"));\n\t}\n\n\t@Test\n\tpublic void resolveWhenHeaderWithInvalidCharactersIsPresentThenAuthenticationExceptionIsThrown() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Bearer an\\\"invalid\\\"token\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t.withMessageContaining((\"Bearer token is malformed\"));\n\t}\n\n\t@Test\n\tpublic void resolveWhenValidHeaderIsPresentTogetherWithFormParameterThenAuthenticationExceptionIsThrown() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Bearer \" + TEST_TOKEN);\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenValidHeaderIsPresentTogetherWithQueryParameterThenAuthenticationExceptionIsThrown() {\n\t\tthis.resolver.setAllowUriQueryParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Bearer \" + TEST_TOKEN);\n\t\trequest.setMethod(\"GET\");\n\t\trequest.setQueryString(\"access_token=\" + TEST_TOKEN);\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t// gh-10326\n\t@Test\n\tpublic void resolveWhenRequestContainsTwoAccessTokenQueryParametersThenAuthenticationExceptionIsThrown() {\n\t\tthis.resolver.setAllowUriQueryParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\trequest.addParameter(\"access_token\", \"token1\", \"token2\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t// gh-10326\n\t@Test\n\tpublic void resolveWhenRequestContainsTwoAccessTokenFormParametersThenAuthenticationExceptionIsThrown() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", \"token1\", \"token2\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t// gh-10326\n\t@Test\n\tpublic void resolveWhenParameterIsPresentInMultipartRequestAndFormParameterSupportedThenTokenIsNotResolved() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"multipart/form-data\");\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenPostAndFormParameterIsPresentAndSupportedThenTokenIsResolved() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenPutAndFormParameterIsPresentAndSupportedThenTokenIsResolved() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"PUT\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenPatchAndFormParameterIsPresentAndSupportedThenTokenIsResolved() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"PATCH\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenDeleteAndFormParameterIsPresentAndSupportedThenTokenIsResolved() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"DELETE\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenGetAndFormParameterIsPresentAndSupportedThenTokenIsNotResolved() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenPostAndFormParameterIsSupportedAndQueryParameterIsPresentThenTokenIsNotResolved() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.setQueryString(\"access_token=\" + TEST_TOKEN);\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenPostAndQueryParameterIsSupportedAndFormParameterIsPresentThenTokenIsNotResolved() {\n\t\tthis.resolver.setAllowUriQueryParameter(true);\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.setQueryString(\"access_token=\" + TEST_TOKEN);\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenFormParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenQueryParameterIsPresentAndSupportedThenTokenIsResolved() {\n\t\tthis.resolver.setAllowUriQueryParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\trequest.setQueryString(\"access_token=\" + TEST_TOKEN);\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\trequest.setQueryString(\"access_token=\" + TEST_TOKEN);\n\t\trequest.addParameter(\"access_token\", TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t// gh-16038\n\t@Test\n\tpublic void resolveWhenRequestContainsTwoAccessTokenFormParametersAndSupportIsDisabledThenTokenIsNotResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", \"token1\", \"token2\");\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t// gh-16038\n\t@Test\n\tpublic void resolveWhenRequestContainsTwoAccessTokenQueryParametersAndSupportIsDisabledThenTokenIsNotResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\trequest.addParameter(\"access_token\", \"token1\", \"token2\");\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenQueryParameterIsPresentAndEmptyStringThenTokenIsNotResolved() {\n\t\tthis.resolver.setAllowUriQueryParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\trequest.addParameter(\"access_token\", \"\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t.withMessageContaining(\"The requested token parameter is an empty string\")\n\t\t\t.satisfies((e) -> {\n\t\t\t\tBearerTokenError error = (BearerTokenError) e.getError();\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_REQUEST);\n\t\t\t\tassertThat(error.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void resolveWhenFormParameterIsPresentAndEmptyStringThenTokenIsNotResolved() {\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\trequest.setContentType(\"application/x-www-form-urlencoded\");\n\t\trequest.addParameter(\"access_token\", \"\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.resolver.resolve(request))\n\t\t\t.withMessageContaining(\"The requested token parameter is an empty string\")\n\t\t\t.satisfies((e) -> {\n\t\t\t\tBearerTokenError error = (BearerTokenError) e.getError();\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_REQUEST);\n\t\t\t\tassertThat(error.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST);\n\t\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/HeaderBearerTokenResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link HeaderBearerTokenResolver}\n *\n * @author Elena Felder\n */\npublic class HeaderBearerTokenResolverTests {\n\n\tprivate static final String TEST_TOKEN = \"test-token\";\n\n\tprivate static final String CORRECT_HEADER = \"jwt-assertion\";\n\n\tprivate HeaderBearerTokenResolver resolver = new HeaderBearerTokenResolver(CORRECT_HEADER);\n\n\t@Test\n\tpublic void constructorWhenHeaderNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new HeaderBearerTokenResolver(null))\n\t\t\t\t.withMessage(\"header cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenHeaderEmptyThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new HeaderBearerTokenResolver(\"\"))\n\t\t\t\t.withMessage(\"header cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenTokenPresentThenTokenIsResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(CORRECT_HEADER, TEST_TOKEN);\n\t\tassertThat(this.resolver.resolve(request)).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenTokenNotPresentThenTokenIsNotResolved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThat(this.resolver.resolve(request)).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/MockExchangeFunction.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.reactive.function.client.ClientRequest;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport org.springframework.web.reactive.function.client.ExchangeFunction;\n\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class MockExchangeFunction implements ExchangeFunction {\n\n\tprivate List<ClientRequest> requests = new ArrayList<>();\n\n\tprivate ClientResponse response = mock(ClientResponse.class);\n\n\tpublic ClientRequest getRequest() {\n\t\treturn this.requests.get(this.requests.size() - 1);\n\t}\n\n\tpublic List<ClientRequest> getRequests() {\n\t\treturn this.requests;\n\t}\n\n\tpublic ClientResponse getResponse() {\n\t\treturn this.response;\n\t}\n\n\t@Override\n\tpublic Mono<ClientResponse> exchange(ClientRequest request) {\n\t\treturn Mono.defer(() -> {\n\t\t\tthis.requests.add(request);\n\t\t\treturn Mono.just(this.response);\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/OAuth2ProtectedResourceMetadataFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OAuth2ProtectedResourceMetadataFilter}.\n *\n * @author Joe Grandja\n */\npublic class OAuth2ProtectedResourceMetadataFilterTests {\n\n\tprivate static final String DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI = \"/.well-known/oauth-protected-resource\";\n\n\tprivate final OAuth2ProtectedResourceMetadataFilter filter = new OAuth2ProtectedResourceMetadataFilter();\n\n\t@Test\n\tpublic void setProtectedResourceMetadataCustomizerWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setProtectedResourceMetadataCustomizer(null))\n\t\t\t.withMessage(\"protectedResourceMetadataCustomizer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNotProtectedResourceMetadataRequestThenNotProcessed() throws Exception {\n\t\tString requestUri = \"/path\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenProtectedResourceMetadataRequestPostThenNotProcessed() throws Exception {\n\t\tString requestUri = DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenProtectedResourceMetadataRequestThenMetadataResponse() throws Exception {\n\t\tthis.filter.setProtectedResourceMetadataCustomizer(\n\t\t\t\t(protectedResourceMetadata) -> protectedResourceMetadata.authorizationServer(\"https://provider1.com\")\n\t\t\t\t\t.authorizationServer(\"https://provider2.com\")\n\t\t\t\t\t.scope(\"scope1\")\n\t\t\t\t\t.scope(\"scope2\")\n\t\t\t\t\t.resourceName(\"resourceName\"));\n\n\t\tString requestUri = DEFAULT_OAUTH2_PROTECTED_RESOURCE_METADATA_ENDPOINT_URI;\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", requestUri);\n\t\trequest.setServletPath(requestUri);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tthis.filter.doFilter(request, response, filterChain);\n\n\t\tverifyNoInteractions(filterChain);\n\n\t\tassertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);\n\t\tString protectedResourceMetadataResponse = response.getContentAsString();\n\t\tassertThat(protectedResourceMetadataResponse).contains(\"\\\"resource\\\":\\\"http://localhost\\\"\");\n\t\tassertThat(protectedResourceMetadataResponse)\n\t\t\t.contains(\"\\\"authorization_servers\\\":[\\\"https://provider1.com\\\",\\\"https://provider2.com\\\"]\");\n\t\tassertThat(protectedResourceMetadataResponse).contains(\"\\\"scopes_supported\\\":[\\\"scope1\\\",\\\"scope2\\\"]\");\n\t\tassertThat(protectedResourceMetadataResponse).contains(\"\\\"bearer_methods_supported\\\":[\\\"header\\\"]\");\n\t\tassertThat(protectedResourceMetadataResponse).contains(\"\\\"resource_name\\\":\\\"resourceName\\\"\");\n\t\tassertThat(protectedResourceMetadataResponse).contains(\"\\\"tls_client_certificate_bound_access_tokens\\\":true\");\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/access/BearerTokenAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.access;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AbstractOAuth2Token;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link BearerTokenAccessDeniedHandlerTests}\n *\n * @author Josh Cummings\n */\npublic class BearerTokenAccessDeniedHandlerTests {\n\n\tprivate BearerTokenAccessDeniedHandler accessDeniedHandler;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.accessDeniedHandler = new BearerTokenAccessDeniedHandler();\n\t}\n\n\t@Test\n\tpublic void handleWhenNotOAuth2AuthenticatedThenStatus403() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\trequest.setUserPrincipal(authentication);\n\t\tthis.accessDeniedHandler.handle(request, response, null);\n\t\tassertThat(response.getStatus()).isEqualTo(403);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\"Bearer\");\n\t}\n\n\t@Test\n\tpublic void handleWhenNotOAuth2AuthenticatedAndRealmSetThenStatus403AndAuthHeaderWithRealm() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\trequest.setUserPrincipal(authentication);\n\t\tthis.accessDeniedHandler.setRealmName(\"test\");\n\t\tthis.accessDeniedHandler.handle(request, response, null);\n\t\tassertThat(response.getStatus()).isEqualTo(403);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\"Bearer realm=\\\"test\\\"\");\n\t}\n\n\t@Test\n\tpublic void handleWhenOAuth2AuthenticatedThenStatus403AndAuthHeaderWithInsufficientScopeErrorAttribute()\n\t\t\tthrows Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication token = new TestingOAuth2TokenAuthenticationToken(Collections.emptyMap());\n\t\trequest.setUserPrincipal(token);\n\t\tthis.accessDeniedHandler.handle(request, response, null);\n\t\tassertThat(response.getStatus()).isEqualTo(403);\n\t\t// @formatter:off\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\"))\n\t\t\t\t.isEqualTo(\"Bearer error=\\\"insufficient_scope\\\", \"\n\t\t\t\t\t\t+ \"error_description=\\\"The request requires higher privileges than provided by the access token.\\\", \"\n\t\t\t\t\t\t+ \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setRealmNameWhenNullRealmNameThenNoExceptionThrown() {\n\t\tthis.accessDeniedHandler.setRealmName(null);\n\t}\n\n\tstatic class TestingOAuth2TokenAuthenticationToken\n\t\t\textends AbstractOAuth2TokenAuthenticationToken<TestingOAuth2TokenAuthenticationToken.TestingOAuth2Token> {\n\n\t\tprivate Map<String, Object> attributes;\n\n\t\tprotected TestingOAuth2TokenAuthenticationToken(Map<String, Object> attributes) {\n\t\t\tsuper(new TestingOAuth2Token(\"token\"));\n\t\t\tthis.attributes = attributes;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, Object> getTokenAttributes() {\n\t\t\treturn this.attributes;\n\t\t}\n\n\t\tstatic class TestingOAuth2Token extends AbstractOAuth2Token {\n\n\t\t\tTestingOAuth2Token(String tokenValue) {\n\t\t\t\tsuper(tokenValue);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/access/server/BearerTokenServerAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.access.server;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.AbstractOAuth2Token;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\npublic class BearerTokenServerAccessDeniedHandlerTests {\n\n\tprivate BearerTokenServerAccessDeniedHandler accessDeniedHandler;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();\n\t}\n\n\t@Test\n\tpublic void handleWhenNotOAuth2AuthenticatedThenStatus403() {\n\t\tAuthentication token = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\tServerWebExchange exchange = mock(ServerWebExchange.class);\n\t\tgiven(exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tgiven(exchange.getResponse()).willReturn(new MockServerHttpResponse());\n\t\tthis.accessDeniedHandler.handle(exchange, null).block();\n\t\tassertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t\tassertThat(exchange.getResponse().getHeaders().get(\"WWW-Authenticate\")).isEqualTo(Arrays.asList(\"Bearer\"));\n\t}\n\n\t@Test\n\tpublic void handleWhenNotOAuth2AuthenticatedAndRealmSetThenStatus403AndAuthHeaderWithRealm() {\n\t\tAuthentication token = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\tServerWebExchange exchange = mock(ServerWebExchange.class);\n\t\tgiven(exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tgiven(exchange.getResponse()).willReturn(new MockServerHttpResponse());\n\t\tthis.accessDeniedHandler.setRealmName(\"test\");\n\t\tthis.accessDeniedHandler.handle(exchange, null).block();\n\t\tassertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t\tassertThat(exchange.getResponse().getHeaders().get(\"WWW-Authenticate\"))\n\t\t\t.isEqualTo(Arrays.asList(\"Bearer realm=\\\"test\\\"\"));\n\t}\n\n\t@Test\n\tpublic void handleWhenOAuth2AuthenticatedThenStatus403AndAuthHeaderWithInsufficientScopeErrorAttribute() {\n\t\tAuthentication token = new TestingOAuth2TokenAuthenticationToken(Collections.emptyMap());\n\t\tServerWebExchange exchange = mock(ServerWebExchange.class);\n\t\tgiven(exchange.getPrincipal()).willReturn(Mono.just(token));\n\t\tgiven(exchange.getResponse()).willReturn(new MockServerHttpResponse());\n\t\tthis.accessDeniedHandler.handle(exchange, null).block();\n\t\tassertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t\t// @formatter:off\n\t\tassertThat(exchange.getResponse().getHeaders().get(\"WWW-Authenticate\"))\n\t\t\t\t.isEqualTo(Arrays.asList(\"Bearer error=\\\"insufficient_scope\\\", \"\n\t\t\t\t\t\t+ \"error_description=\\\"The request requires higher privileges than provided by the access token.\\\", \"\n\t\t\t\t\t\t+ \"error_uri=\\\"https://tools.ietf.org/html/rfc6750#section-3.1\\\"\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setRealmNameWhenNullRealmNameThenNoExceptionThrown() {\n\t\tthis.accessDeniedHandler.setRealmName(null);\n\t}\n\n\tstatic class TestingOAuth2TokenAuthenticationToken\n\t\t\textends AbstractOAuth2TokenAuthenticationToken<TestingOAuth2TokenAuthenticationToken.TestingOAuth2Token> {\n\n\t\tprivate Map<String, Object> attributes;\n\n\t\tprotected TestingOAuth2TokenAuthenticationToken(Map<String, Object> attributes) {\n\t\t\tsuper(new TestingOAuth2TokenAuthenticationToken.TestingOAuth2Token(\"token\"));\n\t\t\tthis.attributes = attributes;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, Object> getTokenAttributes() {\n\t\t\treturn this.attributes;\n\t\t}\n\n\t\tstatic class TestingOAuth2Token extends AbstractOAuth2Token {\n\n\t\t\tTestingOAuth2Token(String tokenValue) {\n\t\t\t\tsuper(tokenValue);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mockito;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link BearerTokenAuthenticationConverter}\n *\n * @author Max Batischev\n */\npublic class BearerTokenAuthenticationConverterTests {\n\n\tprivate static final String X_AUTH_TOKEN_HEADER = \"X-Auth-Token\";\n\n\tprivate static final String TEST_X_AUTH_TOKEN = \"test-x-auth-token\";\n\n\tprivate static final String BEARER_TOKEN = \"test_bearer_token\";\n\n\tprivate final DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();\n\n\tprivate final BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();\n\n\t{\n\t\tthis.converter.setBearerTokenResolver(this.resolver);\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderIsPresentThenTokenIsConverted() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Bearer \" + BEARER_TOKEN);\n\n\t\tAuthentication authentication = this.converter.convert(request);\n\n\t\tassertThat(authentication).isNotNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenQueryParameterIsPresentThenTokenIsConverted() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.GET.name());\n\t\trequest.addParameter(\"access_token\", BEARER_TOKEN);\n\n\t\tthis.resolver.setAllowUriQueryParameter(true);\n\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderNotIsPresentThenTokenIsNotConverted() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\n\t\tAuthentication authentication = this.converter.convert(request);\n\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenAuthorizationHeaderIsPresentTogetherWithQueryParameterThenAuthenticationExceptionIsThrown() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(\"access_token\", BEARER_TOKEN);\n\t\trequest.setMethod(HttpMethod.GET.name());\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Bearer \" + BEARER_TOKEN);\n\n\t\tthis.resolver.setAllowUriQueryParameter(true);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t@Test\n\tpublic void convertWhenXAuthTokenHeaderIsPresentAndBearerTokenHeaderNameSetThenTokenIsConverted() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(X_AUTH_TOKEN_HEADER, \"Bearer \" + TEST_X_AUTH_TOKEN);\n\n\t\tthis.resolver.setBearerTokenHeaderName(X_AUTH_TOKEN_HEADER);\n\n\t\tAuthentication authentication = this.converter.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenHeaderWithMissingTokenIsPresentThenAuthenticationExceptionIsThrown() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Bearer \");\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.withMessageContaining((\"Bearer token is malformed\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenHeaderWithInvalidCharactersIsPresentThenAuthenticationExceptionIsThrown() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Bearer an\\\"invalid\\\"token\");\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))\n\t\t\t.withMessageContaining((\"Bearer token is malformed\"));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void convertWhenCustomAuthenticationDetailsSourceSetThenTokenIsConverted() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Bearer \" + BEARER_TOKEN);\n\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = Mockito\n\t\t\t.mock(AuthenticationDetailsSource.class);\n\t\tthis.converter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\n\t\tAuthentication authentication = this.converter.convert(request);\n\n\t\tverify(authenticationDetailsSource).buildDetails(any());\n\t\tassertThat(authentication).isNotNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenFormParameterIsPresentAndAllowFormEncodedBodyParameterThenConverted() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(HttpMethod.POST.name());\n\t\trequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t\trequest.addParameter(\"access_token\", BEARER_TOKEN);\n\t\tthis.resolver.setAllowFormEncodedBodyParameter(true);\n\n\t\tassertThat(this.converter.convert(request)).isNotNull();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.authentication;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.NonBuildableAuthenticationToken;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;\nimport org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link BearerTokenAuthenticationFilterTests}\n *\n * @author Josh Cummings\n */\n@ExtendWith(MockitoExtension.class)\npublic class BearerTokenAuthenticationFilterTests {\n\n\t@Mock\n\tAuthenticationEntryPoint authenticationEntryPoint;\n\n\t@Mock\n\tAuthenticationFailureHandler authenticationFailureHandler;\n\n\t@Mock\n\tAuthenticationManager authenticationManager;\n\n\t@Mock\n\tAuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;\n\n\t@Mock\n\tBearerTokenResolver bearerTokenResolver;\n\n\t@Mock\n\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tMockFilterChain filterChain;\n\n\t@BeforeEach\n\tpublic void httpMocks() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.filterChain = new MockFilterChain();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenBearerTokenPresentThenAuthenticates() throws ServletException, IOException {\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tArgumentCaptor<BearerTokenAuthenticationToken> captor = ArgumentCaptor\n\t\t\t.forClass(BearerTokenAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(captor.capture());\n\t\tassertThat(captor.getValue().getPrincipal()).isEqualTo(\"token\");\n\t\tassertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSecurityContextRepositoryThenSaves() throws ServletException, IOException {\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tString token = \"token\";\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(token);\n\t\tTestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken(\"test\", \"password\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(expectedAuthentication);\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.setSecurityContextRepository(securityContextRepository);\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tArgumentCaptor<BearerTokenAuthenticationToken> captor = ArgumentCaptor\n\t\t\t.forClass(BearerTokenAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(captor.capture());\n\t\tassertThat(captor.getValue().getPrincipal()).isEqualTo(token);\n\t\tArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(securityContextRepository).saveContext(contextArg.capture(), eq(this.request), eq(this.response));\n\t\tassertThat(contextArg.getValue().getAuthentication().getName()).isEqualTo(expectedAuthentication.getName());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUsingAuthenticationManagerResolverThenAuthenticates() throws Exception {\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManagerResolver));\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tgiven(this.authenticationManagerResolver.resolve(any())).willReturn(this.authenticationManager);\n\t\tTestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken(\"test\", \"password\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(expectedAuthentication);\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tArgumentCaptor<BearerTokenAuthenticationToken> captor = ArgumentCaptor\n\t\t\t.forClass(BearerTokenAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(captor.capture());\n\t\tassertThat(captor.getValue().getPrincipal()).isEqualTo(\"token\");\n\t\tassertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoBearerTokenPresentThenDoesNotAuthenticate() throws ServletException, IOException {\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(null);\n\t\tdontAuthenticate();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMalformedBearerTokenThenPropagatesError() throws ServletException, IOException {\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST,\n\t\t\t\t\"description\", \"uri\");\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(error);\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willThrow(exception);\n\t\tdontAuthenticate();\n\t\tverify(this.authenticationEntryPoint).commence(this.request, this.response, exception);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationFailsWithDefaultHandlerThenPropagatesError()\n\t\t\tthrows ServletException, IOException {\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED,\n\t\t\t\t\"description\", \"uri\");\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(error);\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tgiven(this.authenticationManager.authenticate(any(BearerTokenAuthenticationToken.class))).willThrow(exception);\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tverify(this.authenticationEntryPoint).commence(this.request, this.response, exception);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationFailsWithCustomHandlerThenPropagatesError()\n\t\t\tthrows ServletException, IOException {\n\t\tBearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED,\n\t\t\t\t\"description\", \"uri\");\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(error);\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tgiven(this.authenticationManager.authenticate(any(BearerTokenAuthenticationToken.class))).willThrow(exception);\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.setAuthenticationFailureHandler(this.authenticationFailureHandler);\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tverify(this.authenticationFailureHandler).onAuthenticationFailure(this.request, this.response, exception);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticationServiceExceptionThenRethrows() {\n\t\tAuthenticationServiceException exception = new AuthenticationServiceException(\"message\");\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(exception);\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(this.request, this.response, this.filterChain));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomEntryPointAndAuthenticationErrorThenUses() throws ServletException, IOException {\n\t\tAuthenticationException exception = new InvalidBearerTokenException(\"message\");\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(exception);\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tAuthenticationEntryPoint entrypoint = mock(AuthenticationEntryPoint.class);\n\t\tfilter.setAuthenticationEntryPoint(entrypoint);\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tverify(entrypoint).commence(any(), any(), any(InvalidBearerTokenException.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationDetailsSourceThenUses() throws ServletException, IOException {\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tverify(this.authenticationDetailsSource).buildDetails(this.request);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomSecurityContextHolderStrategyThenUses() throws ServletException, IOException {\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.createEmptyContext()).willReturn(new SecurityContextImpl());\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl());\n\t\tfilter.setSecurityContextHolderStrategy(strategy);\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tverify(strategy).setContext(any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDPoPBoundTokenDowngradedThenPropagatesError() throws ServletException, IOException {\n\t\tJwt jwt = TestJwts.jwt().claim(\"cnf\", Collections.singletonMap(\"jkt\", \"jwk-thumbprint\")).build();\n\t\tJwtAuthenticationToken authenticationResult = new JwtAuthenticationToken(jwt);\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tgiven(this.authenticationManager.authenticate(any(BearerTokenAuthenticationToken.class)))\n\t\t\t.willReturn(authenticationResult);\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.setAuthenticationFailureHandler(this.authenticationFailureHandler);\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tArgumentCaptor<OAuth2AuthenticationException> exceptionCaptor = ArgumentCaptor\n\t\t\t.forClass(OAuth2AuthenticationException.class);\n\t\tverify(this.authenticationFailureHandler).onAuthenticationFailure(any(), any(), exceptionCaptor.capture());\n\t\tOAuth2Error error = exceptionCaptor.getValue().getError();\n\t\tassertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);\n\t\tassertThat(error.getDescription()).isEqualTo(\"Invalid bearer token\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSetAuthenticationConverterAndAuthenticationDetailsSourceThenIllegalArgument(\n\t\t\t@Mock AuthenticationConverter authenticationConverter) {\n\t\tBearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager,\n\t\t\t\tauthenticationConverter);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> filter.setAuthenticationDetailsSource(this.authenticationDetailsSource));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSetBearerTokenResolverAndAuthenticationConverterThenIllegalArgument(\n\t\t\t@Mock AuthenticationConverter authenticationConverter) {\n\t\tBearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager,\n\t\t\t\tauthenticationConverter);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> filter.setBearerTokenResolver(this.bearerTokenResolver));\n\t}\n\n\t/**\n\t * This is critical to avoid adding duplicate GrantedAuthority instances with the same\n\t * authority when the issuedAt is too old and a new instance is requested.\n\t * @throws Exception\n\t */\n\t@Test\n\tvoid doFilterWhenDefaultEqualsGrantedAuthorityThenNoDuplicates() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tnew DefaultEqualsGrantedAuthority());\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(new TestingAuthenticationToken(\"username\", \"password\", new DefaultEqualsGrantedAuthority()));\n\t\tgiven(this.bearerTokenResolver.resolve(any())).willReturn(\"token\");\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t// @formatter:off\n\t\tSecurityAssertions.assertThat(authentication).authorities()\n\t\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t\t.containsExactly(DefaultEqualsGrantedAuthority.AUTHORITY);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid doFilterWhenNonBuildableAuthenticationSubclassThenSkipsToBuilder() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\", \"FACTORONE\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(new NonBuildableAuthenticationToken(\"username\", \"password\", \"FACTORTWO\"));\n\t\tgiven(this.bearerTokenResolver.resolve(any())).willReturn(\"token\");\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t// @formatter:off\n\t\tSecurityAssertions.assertThat(authentication).authorities()\n\t\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t\t.containsExactly(\"FACTORTWO\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationEntryPointWhenNullThenThrowsException() {\n\t\tBearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> filter.setAuthenticationEntryPoint(null))\n\t\t\t\t.withMessageContaining(\"authenticationEntryPoint cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setBearerTokenResolverWhenNullThenThrowsException() {\n\t\tBearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> filter.setBearerTokenResolver(null))\n\t\t\t\t.withMessageContaining(\"bearerTokenResolver cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tBearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> filter.setAuthenticationDetailsSource(null))\n\t\t\t\t.withMessageContaining(\"authenticationDetailsSource cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setConverterWhenNullThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenAuthenticationFilter(this.authenticationManager, null))\n\t\t\t\t.withMessageContaining(\"authenticationConverter cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullAuthenticationManagerThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenAuthenticationFilter((AuthenticationManager) null))\n\t\t\t\t.withMessageContaining(\"authenticationManager cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullAuthenticationManagerResolverThenThrowsException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new BearerTokenAuthenticationFilter((AuthenticationManagerResolver<HttpServletRequest>) null))\n\t\t\t\t.withMessageContaining(\"authenticationManagerResolver cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid authenticateWhenPreviousAuthenticationThenApplies() throws Exception {\n\t\tAuthentication first = new TestingAuthenticationToken(\"user\", \"pass\", \"FACTOR_ONE\");\n\t\tAuthentication second = new TestingAuthenticationToken(\"user\", \"pass\", \"FACTOR_TWO\");\n\t\tFilter filter = addMocks(new BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tgiven(this.bearerTokenResolver.resolve(this.request)).willReturn(\"token\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(second);\n\n\t\tSecurityContextHolder.getContext().setAuthentication(first);\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tAuthentication result = SecurityContextHolder.getContext().getAuthentication();\n\t\tSecurityContextHolder.clearContext();\n\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n\tprivate BearerTokenAuthenticationFilter addMocks(BearerTokenAuthenticationFilter filter) {\n\t\tfilter.setAuthenticationEntryPoint(this.authenticationEntryPoint);\n\t\tfilter.setBearerTokenResolver(this.bearerTokenResolver);\n\t\tfilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);\n\t\treturn filter;\n\t}\n\n\tprivate void dontAuthenticate() throws ServletException, IOException {\n\t\tBearerTokenAuthenticationFilter filter = addMocks(\n\t\t\t\tnew BearerTokenAuthenticationFilter(this.authenticationManager));\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tverifyNoMoreInteractions(this.authenticationManager);\n\t}\n\n\tstatic final class DefaultEqualsGrantedAuthority implements GrantedAuthority {\n\n\t\tpublic static final String AUTHORITY = \"CUSTOM_AUTHORITY\";\n\n\t\t@Override\n\t\tpublic String getAuthority() {\n\t\t\treturn AUTHORITY;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServerBearerExchangeFilterFunctionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.reactive.function.client;\n\nimport java.net.URI;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.web.MockExchangeFunction;\nimport org.springframework.web.reactive.function.client.ClientRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ServerBearerExchangeFilterFunction}\n *\n * @author Josh Cummings\n */\npublic class ServerBearerExchangeFilterFunctionTests {\n\n\tprivate ServerBearerExchangeFilterFunction function = new ServerBearerExchangeFilterFunction();\n\n\tprivate MockExchangeFunction exchange = new MockExchangeFunction();\n\n\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token-0\",\n\t\t\tInstant.now(), Instant.now().plus(Duration.ofDays(1)));\n\n\tprivate Authentication authentication = new AbstractOAuth2TokenAuthenticationToken<OAuth2AccessToken>(\n\t\t\tthis.accessToken) {\n\t\t@Override\n\t\tpublic Map<String, Object> getTokenAttributes() {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t};\n\n\t@Test\n\tpublic void filterWhenUnauthenticatedThenAuthorizationHeaderNull() {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION)).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticatedThenAuthorizationHeaderNull() throws Exception {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication))\n\t\t\t.block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t// gh-7353\n\t@Test\n\tpublic void filterWhenAuthenticatedWithOtherTokenThenAuthorizationHeaderNull() throws Exception {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(token))\n\t\t\t.block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION)).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenExistingAuthorizationThenSingleAuthorizationHeader() {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Existing\")\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication))\n\t\t\t.block();\n\t\tHttpHeaders headers = this.exchange.getRequest().headers();\n\t\tassertThat(headers.get(HttpHeaders.AUTHORIZATION)).containsOnly(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunctionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.reactive.function.client;\n\nimport java.net.URI;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.util.context.Context;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.web.MockExchangeFunction;\nimport org.springframework.web.reactive.function.client.ClientRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ServletBearerExchangeFilterFunction}\n *\n * @author Josh Cummings\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServletBearerExchangeFilterFunctionTests {\n\n\tprivate ServletBearerExchangeFilterFunction function = new ServletBearerExchangeFilterFunction();\n\n\tprivate MockExchangeFunction exchange = new MockExchangeFunction();\n\n\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token-0\",\n\t\t\tInstant.now(), Instant.now().plus(Duration.ofDays(1)));\n\n\tprivate Authentication authentication = new AbstractOAuth2TokenAuthenticationToken<OAuth2AccessToken>(\n\t\t\tthis.accessToken) {\n\t\t@Override\n\t\tpublic Map<String, Object> getTokenAttributes() {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t};\n\n\t@Test\n\tpublic void filterWhenUnauthenticatedThenAuthorizationHeaderNull() {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tthis.function.filter(request, this.exchange).block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION)).isNull();\n\t}\n\n\t// gh-7353\n\t@Test\n\tpublic void filterWhenAuthenticatedWithOtherTokenThenAuthorizationHeaderNull() {\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tthis.function.filter(request, this.exchange).contextWrite(context(token)).block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION)).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticatedThenAuthorizationHeader() {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\")).build();\n\t\tthis.function.filter(request, this.exchange).contextWrite(context(this.authentication)).block();\n\t\tassertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION))\n\t\t\t.isEqualTo(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\t@Test\n\tpublic void filterWhenExistingAuthorizationThenSingleAuthorizationHeader() {\n\t\tClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create(\"https://example.com\"))\n\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Existing\")\n\t\t\t.build();\n\t\tthis.function.filter(request, this.exchange).contextWrite(context(this.authentication)).block();\n\t\tHttpHeaders headers = this.exchange.getRequest().headers();\n\t\tassertThat(headers.get(HttpHeaders.AUTHORIZATION)).containsOnly(\"Bearer \" + this.accessToken.getTokenValue());\n\t}\n\n\tprivate Context context(Authentication authentication) {\n\t\tMap<Class<?>, Object> contextAttributes = new HashMap<>();\n\t\tcontextAttributes.put(Authentication.class, authentication);\n\t\treturn Context.of(ServletBearerExchangeFilterFunction.SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY,\n\t\t\t\tcontextAttributes);\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.server;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpResponse;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.core.OAuth2Error;\nimport org.springframework.security.oauth2.core.OAuth2ErrorCodes;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class BearerTokenServerAuthenticationEntryPointTests {\n\n\tprivate BearerTokenServerAuthenticationEntryPoint entryPoint = new BearerTokenServerAuthenticationEntryPoint();\n\n\tprivate MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\n\t@Test\n\tpublic void commenceWhenNotOAuth2AuthenticationExceptionThenBearer() {\n\t\tthis.entryPoint.commence(this.exchange, new BadCredentialsException(\"\")).block();\n\t\tassertThat(getResponse().getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo(\"Bearer\");\n\t\tassertThat(getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void commenceWhenRealmNameThenHasRealmName() {\n\t\tthis.entryPoint.setRealmName(\"Realm\");\n\t\tthis.entryPoint.commence(this.exchange, new BadCredentialsException(\"\")).block();\n\t\tassertThat(getResponse().getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE))\n\t\t\t.isEqualTo(\"Bearer realm=\\\"Realm\\\"\");\n\t\tassertThat(getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void commenceWhenOAuth2AuthenticationExceptionThenContainsErrorInformation() {\n\t\tOAuth2Error oauthError = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(oauthError);\n\t\tthis.entryPoint.commence(this.exchange, exception).block();\n\t\tassertThat(getResponse().getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE))\n\t\t\t.isEqualTo(\"Bearer error=\\\"invalid_request\\\"\");\n\t\tassertThat(getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void commenceWhenOAuth2ErrorCompleteThenContainsErrorInformation() {\n\t\tOAuth2Error oauthError = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, \"Oops\", \"https://example.com\");\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(oauthError);\n\t\tthis.entryPoint.commence(this.exchange, exception).block();\n\t\tassertThat(getResponse().getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo(\n\t\t\t\t\"Bearer error=\\\"invalid_request\\\", error_description=\\\"Oops\\\", error_uri=\\\"https://example.com\\\"\");\n\t\tassertThat(getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void commenceWhenBearerTokenThenErrorInformation() {\n\t\tOAuth2Error oauthError = new BearerTokenError(OAuth2ErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, \"Oops\",\n\t\t\t\t\"https://example.com\");\n\t\tOAuth2AuthenticationException exception = new OAuth2AuthenticationException(oauthError);\n\t\tthis.entryPoint.commence(this.exchange, exception).block();\n\t\tassertThat(getResponse().getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo(\n\t\t\t\t\"Bearer error=\\\"invalid_request\\\", error_description=\\\"Oops\\\", error_uri=\\\"https://example.com\\\"\");\n\t\tassertThat(getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);\n\t}\n\n\t@Test\n\tpublic void commenceWhenNoSubscriberThenNothingHappens() {\n\t\tthis.entryPoint.commence(this.exchange, new BadCredentialsException(\"\"));\n\t\tassertThat(getResponse().getHeaders().headerNames()).isEmpty();\n\t\tassertThat(getResponse().getStatusCode()).isNull();\n\t}\n\n\tprivate MockServerHttpResponse getResponse() {\n\t\treturn this.exchange.getResponse();\n\t}\n\n}\n"
  },
  {
    "path": "oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/server/authentication/ServerBearerTokenAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.oauth2.server.resource.web.server.authentication;\n\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticationException;\nimport org.springframework.security.oauth2.server.resource.BearerTokenError;\nimport org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class ServerBearerTokenAuthenticationConverterTests {\n\n\tprivate static final String CUSTOM_HEADER = \"custom-header\";\n\n\tprivate static final String TEST_TOKEN = \"test-token\";\n\n\tprivate ServerBearerTokenAuthenticationConverter converter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.converter = new ServerBearerTokenAuthenticationConverter();\n\t}\n\n\t@Test\n\tpublic void resolveWhenValidHeaderIsPresentThenTokenIsResolved() {\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + TEST_TOKEN);\n\t\t// @formatter:on\n\t\tassertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);\n\t}\n\n\t// gh-8502\n\t@Test\n\tpublic void resolveWhenHeaderEndsWithPaddingIndicatorThenTokenIsResolved() {\n\t\tString token = TEST_TOKEN + \"==\";\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + token);\n\t\t// @formatter:on\n\t\tassertThat(convertToToken(request).getToken()).isEqualTo(token);\n\t}\n\n\t@Test\n\tpublic void resolveWhenCustomDefinedHeaderIsValidAndPresentThenTokenIsResolved() {\n\t\tthis.converter.setBearerTokenHeaderName(CUSTOM_HEADER);\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.header(CUSTOM_HEADER, \"Bearer \" + TEST_TOKEN);\n\t\t// @formatter:on\n\t\tassertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);\n\t}\n\n\t// gh-7011\n\t@Test\n\tpublic void resolveWhenValidHeaderIsEmptyStringThenTokenIsResolved() {\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \");\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> convertToToken(request))\n\t\t\t\t.satisfies((ex) -> {\n\t\t\t\t\tBearerTokenError error = (BearerTokenError) ex.getError();\n\t\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);\n\t\t\t\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t\t\t\tassertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenLowercaseHeaderIsPresentThenTokenIsResolved() {\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"bearer \" + TEST_TOKEN);\n\t\t// @formatter:on\n\t\tassertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenNoHeaderIsPresentThenTokenIsNotResolved() {\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\");\n\t\tassertThat(convertToToken(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenHeaderWithWrongSchemeIsPresentThenTokenIsNotResolved() {\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION,\n\t\t\t\t\t\t\"Basic \" + Base64.getEncoder().encodeToString(\"test:test\".getBytes()));\n\t\t// @formatter:on\n\t\tassertThat(convertToToken(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenHeaderWithMissingTokenIsPresentThenAuthenticationExceptionIsThrown() {\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> convertToToken(request))\n\t\t\t\t.withMessageContaining((\"Bearer token is malformed\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenHeaderWithInvalidCharactersIsPresentThenAuthenticationExceptionIsThrown() {\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer an\\\"invalid\\\"token\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> convertToToken(request))\n\t\t\t\t.withMessageContaining((\"Bearer token is malformed\"));\n\t\t// @formatter:on\n\t}\n\n\t// gh-8865\n\t@Test\n\tpublic void resolveWhenHeaderWithInvalidCharactersIsPresentAndNotSubscribedThenNoneExceptionIsThrown() {\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer an\\\"invalid\\\"token\");\n\t\t// @formatter:on\n\t\tthis.converter.convert(MockServerWebExchange.from(request));\n\t}\n\n\t@Test\n\tpublic void resolveWhenValidHeaderIsPresentTogetherWithQueryParameterThenAuthenticationExceptionIsThrown() {\n\t\tthis.converter.setAllowUriQueryParameter(true);\n\t\t// @formatter:off\n\t\tthis.converter.setAllowUriQueryParameter(true);\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.queryParam(\"access_token\", TEST_TOKEN)\n\t\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + TEST_TOKEN);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> convertToToken(request))\n\t\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenValidHeaderIsPresentTogetherWithBodyParameterThenAuthenticationExceptionIsThrown() {\n\t\tthis.converter.setAllowFormEncodedBodyParameter(true);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/\")\n\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + TEST_TOKEN)\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"access_token=\" + TEST_TOKEN);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenValidHeaderIsPresentTogetherWithBodyParameterAndQueryParameterThenAuthenticationExceptionIsThrown() {\n\t\tthis.converter.setAllowUriQueryParameter(true);\n\t\tthis.converter.setAllowFormEncodedBodyParameter(true);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/\")\n\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Bearer \" + TEST_TOKEN)\n\t\t\t.queryParam(\"access_token\", TEST_TOKEN)\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"access_token=\" + TEST_TOKEN);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenRequestContainsTwoAccessTokenQueryParametersThenAuthenticationExceptionIsThrown() {\n\t\tthis.converter.setAllowUriQueryParameter(true);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/\")\n\t\t\t.queryParam(\"access_token\", \"token1\", \"token2\")\n\t\t\t.build();\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenRequestContainsTwoAccessTokenBodyParametersThenAuthenticationExceptionIsThrown() {\n\t\tthis.converter.setAllowFormEncodedBodyParameter(true);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"access_token=\" + TEST_TOKEN + \"&access_token=\" + TEST_TOKEN);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))\n\t\t\t.withMessageContaining(\"Found multiple bearer tokens in the request\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenQueryParameterIsPresentAndSupportedThenTokenIsResolved() {\n\t\tthis.converter.setAllowUriQueryParameter(true);\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.queryParam(\"access_token\", TEST_TOKEN);\n\t\t// @formatter:on\n\t\tassertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);\n\t}\n\n\t// gh-7011\n\t@Test\n\tpublic void resolveWhenQueryParameterIsEmptyAndSupportedThenOAuth2AuthenticationException() {\n\t\tthis.converter.setAllowUriQueryParameter(true);\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.queryParam(\"access_token\", \"\");\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> convertToToken(request))\n\t\t\t\t.satisfies((ex) -> {\n\t\t\t\t\tBearerTokenError error = (BearerTokenError) ex.getError();\n\t\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_REQUEST);\n\t\t\t\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t\t\t\tassertThat(error.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST);\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {\n\t\t// @formatter:off\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t\t.queryParam(\"access_token\", TEST_TOKEN);\n\t\t// @formatter:on\n\t\tassertThat(convertToToken(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenQueryParameterHasMultipleAccessTokensThenOAuth2AuthenticationException() {\n\t\tthis.converter.setAllowUriQueryParameter(true);\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t.queryParam(\"access_token\", TEST_TOKEN, TEST_TOKEN);\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tBearerTokenError error = (BearerTokenError) ex.getError();\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_REQUEST);\n\t\t\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t\t\tassertThat(error.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST);\n\t\t\t});\n\n\t}\n\n\t@Test\n\tpublic void resolveWhenBodyParameterIsPresentThenTokenIsResolved() {\n\t\tthis.converter.setAllowFormEncodedBodyParameter(true);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"access_token=\" + TEST_TOKEN);\n\n\t\tassertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenBodyParameterIsPresentButNotAllowedThenTokenIsNotResolved() {\n\t\tthis.converter.setAllowFormEncodedBodyParameter(false);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"access_token=\" + TEST_TOKEN);\n\n\t\tassertThat(convertToToken(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenBodyParameterHasMultipleAccessTokensThenOAuth2AuthenticationException() {\n\t\tthis.converter.setAllowFormEncodedBodyParameter(true);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"access_token=\" + TEST_TOKEN + \"&access_token=\" + TEST_TOKEN);\n\n\t\tassertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))\n\t\t\t.satisfies((ex) -> {\n\t\t\t\tBearerTokenError error = (BearerTokenError) ex.getError();\n\t\t\t\tassertThat(error.getDescription()).isEqualTo(\"Found multiple bearer tokens in the request\");\n\t\t\t\tassertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_REQUEST);\n\t\t\t\tassertThat(error.getUri()).isEqualTo(\"https://tools.ietf.org/html/rfc6750#section-3.1\");\n\t\t\t\tassertThat(error.getHttpStatus()).isEqualTo(HttpStatus.BAD_REQUEST);\n\t\t\t});\n\t}\n\n\t@Test\n\tpublic void resolveBodyContainsOtherParameterAsWellThenTokenIsResolved() {\n\t\tthis.converter.setAllowFormEncodedBodyParameter(true);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"access_token=\" + TEST_TOKEN + \"&other_param=value\");\n\n\t\tassertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);\n\t}\n\n\t@Test\n\tpublic void resolveWhenNoBodyParameterThenTokenIsNotResolved() {\n\t\tthis.converter.setAllowFormEncodedBodyParameter(true);\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.post(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED);\n\n\t\tassertThat(convertToToken(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenWrongBodyParameterThenTokenIsNotResolved() {\n\t\tthis.converter.setAllowFormEncodedBodyParameter(true);\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"other_param=value\");\n\n\t\tassertThat(convertToToken(request)).isNull();\n\t}\n\n\t// gh-16038\n\t@Test\n\tpublic void resolveWhenRequestContainsTwoAccessTokenQueryParametersAndSupportIsDisabledThenTokenIsNotResolved() {\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\")\n\t\t\t.queryParam(\"access_token\", TEST_TOKEN, TEST_TOKEN);\n\t\tassertThat(convertToToken(request)).isNull();\n\t}\n\n\tprivate BearerTokenAuthenticationToken convertToToken(MockServerHttpRequest.BaseBuilder<?> request) {\n\t\treturn convertToToken(request.build());\n\t}\n\n\tprivate BearerTokenAuthenticationToken convertToToken(MockServerHttpRequest request) {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(request);\n\t\t// @formatter:off\n\t\treturn this.converter.convert(exchange)\n\t\t\t\t.cast(BearerTokenAuthenticationToken.class)\n\t\t\t\t.block();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/spring-security-rsocket.gradle",
    "content": "plugins {\n\tid 'compile-warnings-error'\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi 'io.rsocket:rsocket-core'\n\tcompileOnly 'com.google.code.findbugs:jsr305:3.0.2'\n\toptional project(':spring-security-oauth2-resource-server')\n\toptional 'org.springframework:spring-messaging'\n\ttestImplementation 'io.projectreactor:reactor-test'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/api/PayloadExchange.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.api;\n\nimport io.rsocket.Payload;\n\nimport org.springframework.util.MimeType;\n\n/**\n * Contract for a Payload interaction.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic interface PayloadExchange {\n\n\tPayloadExchangeType getType();\n\n\tPayload getPayload();\n\n\tMimeType getDataMimeType();\n\n\tMimeType getMetadataMimeType();\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/api/PayloadExchangeType.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.api;\n\n/**\n * The {@link PayloadExchange} type\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic enum PayloadExchangeType {\n\n\t/**\n\t * The <a href=\"https://rsocket.io/docs/Protocol#setup-frame-0x01\">Setup</a>. Can be\n\t * used to determine if a Payload is part of the connection\n\t */\n\tSETUP(false),\n\n\t/**\n\t * A <a href=\"https://rsocket.io/docs/Protocol#frame-fnf\">Fire and Forget</a>\n\t * exchange.\n\t */\n\tFIRE_AND_FORGET(true),\n\n\t/**\n\t * A <a href=\"https://rsocket.io/docs/Protocol#frame-request-response\">Request\n\t * Response</a> exchange.\n\t */\n\tREQUEST_RESPONSE(true),\n\n\t/**\n\t * A <a href=\"https://rsocket.io/docs/Protocol#request-stream-frame\">Request\n\t * Stream</a> exchange. This is only represents the request portion. The\n\t * {@link #PAYLOAD} type represents the data that submitted.\n\t */\n\tREQUEST_STREAM(true),\n\n\t/**\n\t * A <a href=\"https://rsocket.io/docs/Protocol#request-channel-frame\">Request\n\t * Channel</a> exchange.\n\t */\n\tREQUEST_CHANNEL(true),\n\n\t/**\n\t * A <a href=\"https://rsocket.io/docs/Protocol#payload-frame\">Payload</a> exchange.\n\t */\n\tPAYLOAD(false),\n\n\t/**\n\t * A <a href=\"https://rsocket.io/docs/Protocol#frame-metadata-push\">Metadata Push</a>\n\t * exchange.\n\t */\n\tMETADATA_PUSH(true);\n\n\tprivate final boolean isRequest;\n\n\tPayloadExchangeType(boolean isRequest) {\n\t\tthis.isRequest = isRequest;\n\t}\n\n\t/**\n\t * Determines if this exchange is a type of request (i.e. the initial frame).\n\t * @return true if it is a request, else false\n\t */\n\tpublic boolean isRequest() {\n\t\treturn this.isRequest;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/api/PayloadInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.api;\n\nimport reactor.core.publisher.Mono;\n\n/**\n * Contract for interception-style, chained processing of Payloads that may be used to\n * implement cross-cutting, application-agnostic requirements such as security, timeouts,\n * and others.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic interface PayloadInterceptor {\n\n\t/**\n\t * Process the Web request and (optionally) delegate to the next\n\t * {@code PayloadInterceptor} through the given {@link PayloadInterceptorChain}.\n\t * @param exchange the current payload exchange\n\t * @param chain provides a way to delegate to the next interceptor\n\t * @return {@code Mono<Void>} to indicate when payload processing is complete\n\t */\n\tMono<Void> intercept(PayloadExchange exchange, PayloadInterceptorChain chain);\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/api/PayloadInterceptorChain.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.api;\n\nimport reactor.core.publisher.Mono;\n\n/**\n * Contract to allow a {@link PayloadInterceptor} to delegate to the next in the chain. *\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic interface PayloadInterceptorChain {\n\n\t/**\n\t * Process the payload exchange.\n\t * @param exchange the current server exchange\n\t * @return {@code Mono<Void>} to indicate when request processing is complete\n\t */\n\tMono<Void> next(PayloadExchange exchange);\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/api/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security RSocket APIs.\n */\n@NullMarked\npackage org.springframework.security.rsocket.api;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authentication/AnonymousPayloadInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport java.util.List;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.Ordered;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.security.rsocket.api.PayloadInterceptorChain;\nimport org.springframework.util.Assert;\n\n/**\n * If {@link ReactiveSecurityContextHolder} is empty populates an\n * {@code AnonymousAuthenticationToken}\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic class AnonymousPayloadInterceptor implements PayloadInterceptor, Ordered {\n\n\tprivate final String key;\n\n\tprivate final Object principal;\n\n\tprivate final List<GrantedAuthority> authorities;\n\n\tprivate int order;\n\n\t/**\n\t * Creates a filter with a principal named \"anonymousUser\" and the single authority\n\t * \"ROLE_ANONYMOUS\".\n\t * @param key the key to identify tokens created by this filter\n\t */\n\tpublic AnonymousPayloadInterceptor(String key) {\n\t\tthis(key, \"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t}\n\n\t/**\n\t * @param key key the key to identify tokens created by this filter\n\t * @param principal the principal which will be used to represent anonymous users\n\t * @param authorities the authority list for anonymous users\n\t */\n\tpublic AnonymousPayloadInterceptor(String key, Object principal, List<GrantedAuthority> authorities) {\n\t\tAssert.hasLength(key, \"key cannot be null or empty\");\n\t\tAssert.notNull(principal, \"Anonymous authentication principal must be set\");\n\t\tAssert.notNull(authorities, \"Anonymous authorities must be set\");\n\t\tthis.key = key;\n\t\tthis.principal = principal;\n\t\tthis.authorities = authorities;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t@Override\n\tpublic Mono<Void> intercept(PayloadExchange exchange, PayloadInterceptorChain chain) {\n\t\treturn ReactiveSecurityContextHolder.getContext().switchIfEmpty(Mono.defer(() -> {\n\t\t\tAnonymousAuthenticationToken authentication = new AnonymousAuthenticationToken(this.key, this.principal,\n\t\t\t\t\tthis.authorities);\n\t\t\treturn chain.next(exchange)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))\n\t\t\t\t.then(Mono.empty());\n\t\t})).flatMap((securityContext) -> chain.next(exchange));\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadExchangeConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.ByteBufAllocator;\nimport io.rsocket.metadata.AuthMetadataCodec;\nimport io.rsocket.metadata.WellKnownAuthType;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.codec.ByteArrayDecoder;\nimport org.springframework.messaging.rsocket.DefaultMetadataExtractor;\nimport org.springframework.messaging.rsocket.MetadataExtractor;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\n/**\n * Converts from the {@link PayloadExchange} for <a href=\n * \"https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Authentication.md\">Authentication\n * Extension</a>. For <a href=\n * \"https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Simple.md\">Simple</a>\n * a {@link UsernamePasswordAuthenticationToken} is returned. For <a href=\n * \"https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Bearer.md\">Bearer</a>\n * a {@link BearerTokenAuthenticationToken} is returned.\n *\n * @author Rob Winch\n * @since 5.3\n */\npublic class AuthenticationPayloadExchangeConverter implements PayloadExchangeAuthenticationConverter {\n\n\tprivate static final MimeType COMPOSITE_METADATA_MIME_TYPE = MimeTypeUtils\n\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\n\tprivate static final MimeType AUTHENTICATION_MIME_TYPE = MimeTypeUtils\n\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());\n\n\tprivate final MetadataExtractor metadataExtractor = createDefaultExtractor();\n\n\t@Override\n\tpublic Mono<Authentication> convert(PayloadExchange exchange) {\n\t\treturn Mono\n\t\t\t.fromCallable(() -> this.metadataExtractor.extract(exchange.getPayload(),\n\t\t\t\t\tAuthenticationPayloadExchangeConverter.COMPOSITE_METADATA_MIME_TYPE))\n\t\t\t.flatMap((metadata) -> Mono.justOrEmpty(authentication(metadata)));\n\t}\n\n\tprivate @Nullable Authentication authentication(Map<String, Object> metadata) {\n\t\tbyte[] authenticationMetadata = (byte[]) metadata.get(\"authentication\");\n\t\tif (authenticationMetadata == null) {\n\t\t\treturn null;\n\t\t}\n\t\tByteBuf rawAuthentication = ByteBufAllocator.DEFAULT.buffer();\n\t\ttry {\n\t\t\trawAuthentication.writeBytes(authenticationMetadata);\n\t\t\tif (!AuthMetadataCodec.isWellKnownAuthType(rawAuthentication)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tWellKnownAuthType wellKnownAuthType = AuthMetadataCodec.readWellKnownAuthType(rawAuthentication);\n\t\t\tif (WellKnownAuthType.SIMPLE.equals(wellKnownAuthType)) {\n\t\t\t\treturn simple(rawAuthentication);\n\t\t\t}\n\t\t\tif (WellKnownAuthType.BEARER.equals(wellKnownAuthType)) {\n\t\t\t\treturn bearer(rawAuthentication);\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"Unknown Mime Type \" + wellKnownAuthType);\n\t\t}\n\t\tfinally {\n\t\t\trawAuthentication.release();\n\t\t}\n\t}\n\n\tprivate Authentication simple(ByteBuf rawAuthentication) {\n\t\tByteBuf rawUsername = AuthMetadataCodec.readUsername(rawAuthentication);\n\t\tString username = rawUsername.toString(StandardCharsets.UTF_8);\n\t\tByteBuf rawPassword = AuthMetadataCodec.readPassword(rawAuthentication);\n\t\tString password = rawPassword.toString(StandardCharsets.UTF_8);\n\t\treturn UsernamePasswordAuthenticationToken.unauthenticated(username, password);\n\t}\n\n\tprivate Authentication bearer(ByteBuf rawAuthentication) {\n\t\tchar[] rawToken = AuthMetadataCodec.readBearerTokenAsCharArray(rawAuthentication);\n\t\tString token = new String(rawToken);\n\t\treturn new BearerTokenAuthenticationToken(token);\n\t}\n\n\tprivate static MetadataExtractor createDefaultExtractor() {\n\t\tDefaultMetadataExtractor result = new DefaultMetadataExtractor(new ByteArrayDecoder());\n\t\tresult.metadataToExtract(AUTHENTICATION_MIME_TYPE, byte[].class, \"authentication\");\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.Ordered;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.security.rsocket.api.PayloadInterceptorChain;\nimport org.springframework.util.Assert;\n\n/**\n * Uses the provided {@code ReactiveAuthenticationManager} to authenticate a Payload. If\n * authentication is successful, then the result is added to\n * {@link ReactiveSecurityContextHolder}.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic class AuthenticationPayloadInterceptor implements PayloadInterceptor, Ordered {\n\n\tprivate final ReactiveAuthenticationManager authenticationManager;\n\n\tprivate int order;\n\n\tprivate PayloadExchangeAuthenticationConverter authenticationConverter = new BasicAuthenticationPayloadExchangeConverter();\n\n\t/**\n\t * Creates a new instance\n\t * @param authenticationManager the manager to use. Cannot be null\n\t */\n\tpublic AuthenticationPayloadInterceptor(ReactiveAuthenticationManager authenticationManager) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t/**\n\t * Sets the convert to be used\n\t * @param authenticationConverter\n\t */\n\tpublic void setAuthenticationConverter(PayloadExchangeAuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t@Override\n\tpublic Mono<Void> intercept(PayloadExchange exchange, PayloadInterceptorChain chain) {\n\t\treturn this.authenticationConverter.convert(exchange)\n\t\t\t.switchIfEmpty(chain.next(exchange).then(Mono.empty()))\n\t\t\t.flatMap((a) -> this.authenticationManager.authenticate(a))\n\t\t\t.flatMap((a) -> onAuthenticationSuccess(chain.next(exchange), a));\n\t}\n\n\tprivate Mono<Void> onAuthenticationSuccess(Mono<Void> payload, Authentication authentication) {\n\t\treturn payload.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authentication/BasicAuthenticationPayloadExchangeConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport io.rsocket.metadata.WellKnownMimeType;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.messaging.rsocket.DefaultMetadataExtractor;\nimport org.springframework.messaging.rsocket.MetadataExtractor;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.metadata.BasicAuthenticationDecoder;\nimport org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\n/**\n * Converts from the {@link PayloadExchange} to a\n * {@link UsernamePasswordAuthenticationToken} by extracting\n * {@link UsernamePasswordMetadata#BASIC_AUTHENTICATION_MIME_TYPE} from the metadata.\n *\n * @author Rob Winch\n * @since 5.2\n * @deprecated please use {@link AuthenticationPayloadExchangeConverter} instead\n */\n@Deprecated\npublic class BasicAuthenticationPayloadExchangeConverter implements PayloadExchangeAuthenticationConverter {\n\n\tprivate MimeType metadataMimetype = MimeTypeUtils\n\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\n\tprivate MetadataExtractor metadataExtractor = createDefaultExtractor();\n\n\t@Override\n\tpublic Mono<Authentication> convert(PayloadExchange exchange) {\n\t\treturn Mono.fromCallable(() -> this.metadataExtractor.extract(exchange.getPayload(), this.metadataMimetype))\n\t\t\t.flatMap((metadata) -> Mono\n\t\t\t\t.justOrEmpty(metadata.get(UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE.toString())))\n\t\t\t.cast(UsernamePasswordMetadata.class)\n\t\t\t.map((credentials) -> UsernamePasswordAuthenticationToken.unauthenticated(credentials.getUsername(),\n\t\t\t\t\tcredentials.getPassword()));\n\t}\n\n\tprivate static MetadataExtractor createDefaultExtractor() {\n\t\tDefaultMetadataExtractor result = new DefaultMetadataExtractor(new BasicAuthenticationDecoder());\n\t\tresult.metadataToExtract(UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE,\n\t\t\t\tUsernamePasswordMetadata.class, (String) null);\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authentication/BearerPayloadExchangeConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport java.nio.charset.StandardCharsets;\n\nimport io.netty.buffer.ByteBuf;\nimport io.rsocket.metadata.CompositeMetadata;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.metadata.BearerTokenMetadata;\n\n/**\n * Converts from the {@link PayloadExchange} to a {@link BearerTokenAuthenticationToken}\n * by extracting {@link BearerTokenMetadata#BEARER_AUTHENTICATION_MIME_TYPE} from the\n * metadata.\n *\n * @author Rob Winch\n * @since 5.2\n * @deprecated please use {@link AuthenticationPayloadExchangeConverter} instead\n */\n@Deprecated\npublic class BearerPayloadExchangeConverter implements PayloadExchangeAuthenticationConverter {\n\n\tprivate static final String BEARER_MIME_TYPE_VALUE = BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE.toString();\n\n\t@Override\n\tpublic Mono<Authentication> convert(PayloadExchange exchange) {\n\t\tByteBuf metadata = exchange.getPayload().metadata();\n\t\tCompositeMetadata compositeMetadata = new CompositeMetadata(metadata, false);\n\t\tfor (CompositeMetadata.Entry entry : compositeMetadata) {\n\t\t\tif (BEARER_MIME_TYPE_VALUE.equals(entry.getMimeType())) {\n\t\t\t\tByteBuf content = entry.getContent();\n\t\t\t\tString token = content.toString(StandardCharsets.UTF_8);\n\t\t\t\treturn Mono.just(new BearerTokenAuthenticationToken(token));\n\t\t\t}\n\t\t}\n\t\treturn Mono.empty();\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authentication/PayloadExchangeAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.rsocket.api.PayloadExchange;\n\n/**\n * Converts from a {@link PayloadExchange} to an {@link Authentication}\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic interface PayloadExchangeAuthenticationConverter {\n\n\tMono<Authentication> convert(PayloadExchange exchange);\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security RSocket Authentication integration.\n */\n@NullMarked\npackage org.springframework.security.rsocket.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authorization/AuthorizationPayloadInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authorization;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.Ordered;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.security.rsocket.api.PayloadInterceptorChain;\nimport org.springframework.util.Assert;\n\n/**\n * Provides authorization of the {@link PayloadExchange}.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic class AuthorizationPayloadInterceptor implements PayloadInterceptor, Ordered {\n\n\tprivate final ReactiveAuthorizationManager<PayloadExchange> authorizationManager;\n\n\tprivate int order;\n\n\tpublic AuthorizationPayloadInterceptor(ReactiveAuthorizationManager<PayloadExchange> authorizationManager) {\n\t\tAssert.notNull(authorizationManager, \"authorizationManager cannot be null\");\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<Void> intercept(PayloadExchange exchange, PayloadInterceptorChain chain) {\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t.mapNotNull(SecurityContext::getAuthentication)\n\t\t\t.switchIfEmpty(Mono.error(() -> new AuthenticationCredentialsNotFoundException(\n\t\t\t\t\t\"An Authentication (possibly AnonymousAuthenticationToken) is required.\")))\n\t\t\t.as((authentication) -> this.authorizationManager.verify(authentication, exchange))\n\t\t\t.then(chain.next(exchange));\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcher;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcher.MatchResult;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcherEntry;\nimport org.springframework.util.Assert;\n\n/**\n * Maps a @{code List} of {@link PayloadExchangeMatcher} instances to\n *\n * @{code ReactiveAuthorizationManager} instances.\n * @author Rob Winch\n * @since 5.2\n */\npublic final class PayloadExchangeMatcherReactiveAuthorizationManager\n\t\timplements ReactiveAuthorizationManager<PayloadExchange> {\n\n\tprivate final List<PayloadExchangeMatcherEntry<ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext>>> mappings;\n\n\tprivate PayloadExchangeMatcherReactiveAuthorizationManager(\n\t\t\tList<PayloadExchangeMatcherEntry<ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext>>> mappings) {\n\t\tAssert.notEmpty(mappings, \"mappings cannot be null\");\n\t\tthis.mappings = mappings;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, PayloadExchange exchange) {\n\t\treturn Flux.fromIterable(this.mappings)\n\t\t\t.concatMap((mapping) -> mapping.getMatcher()\n\t\t\t\t.matches(exchange)\n\t\t\t\t.filter(PayloadExchangeMatcher.MatchResult::isMatch)\n\t\t\t\t.mapNotNull(MatchResult::getVariables)\n\t\t\t\t.flatMap((variables) -> mapping.getEntry()\n\t\t\t\t\t.authorize(authentication, new PayloadExchangeAuthorizationContext(exchange, variables))))\n\t\t\t.next()\n\t\t\t.switchIfEmpty(Mono.fromCallable(() -> new AuthorizationDecision(false)));\n\t}\n\n\tpublic static PayloadExchangeMatcherReactiveAuthorizationManager.Builder builder() {\n\t\treturn new PayloadExchangeMatcherReactiveAuthorizationManager.Builder();\n\t}\n\n\tpublic static final class Builder {\n\n\t\tprivate final List<PayloadExchangeMatcherEntry<ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext>>> mappings = new ArrayList<>();\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\tpublic PayloadExchangeMatcherReactiveAuthorizationManager.Builder add(\n\t\t\t\tPayloadExchangeMatcherEntry<ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext>> entry) {\n\t\t\tthis.mappings.add(entry);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic PayloadExchangeMatcherReactiveAuthorizationManager build() {\n\t\t\treturn new PayloadExchangeMatcherReactiveAuthorizationManager(this.mappings);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/authorization/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security RSocket authorization integration.\n */\n@NullMarked\npackage org.springframework.security.rsocket.authorization;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/core/ContextPayloadInterceptorChain.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport java.util.List;\nimport java.util.ListIterator;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.security.rsocket.api.PayloadInterceptorChain;\n\n/**\n * A {@link PayloadInterceptorChain} which exposes the Reactor {@link Context} via a\n * member variable. This class is not Thread safe, so a new instance must be created for\n * each Thread.\n *\n * Internally {@code ContextPayloadInterceptorChain} is used to ensure that the Reactor\n * {@code Context} is captured so it can be transferred to subscribers outside of this\n * {@code Context} in {@code PayloadSocketAcceptor}.\n *\n * @author Rob Winch\n * @since 5.2\n * @see PayloadSocketAcceptor\n */\nclass ContextPayloadInterceptorChain implements PayloadInterceptorChain {\n\n\tprivate final @Nullable PayloadInterceptor currentInterceptor;\n\n\tprivate final @Nullable ContextPayloadInterceptorChain next;\n\n\tprivate @Nullable Context context;\n\n\tContextPayloadInterceptorChain(List<PayloadInterceptor> interceptors) {\n\t\tif (interceptors == null) {\n\t\t\tthrow new IllegalArgumentException(\"interceptors cannot be null\");\n\t\t}\n\t\tif (interceptors.isEmpty()) {\n\t\t\tthrow new IllegalArgumentException(\"interceptors cannot be empty\");\n\t\t}\n\t\tContextPayloadInterceptorChain interceptor = init(interceptors);\n\t\tthis.currentInterceptor = interceptor.currentInterceptor;\n\t\tthis.next = interceptor.next;\n\t}\n\n\tprivate static ContextPayloadInterceptorChain init(List<PayloadInterceptor> interceptors) {\n\t\tContextPayloadInterceptorChain interceptor = new ContextPayloadInterceptorChain(null, null);\n\t\tListIterator<? extends PayloadInterceptor> iterator = interceptors.listIterator(interceptors.size());\n\t\twhile (iterator.hasPrevious()) {\n\t\t\tinterceptor = new ContextPayloadInterceptorChain(iterator.previous(), interceptor);\n\t\t}\n\t\treturn interceptor;\n\t}\n\n\tprivate ContextPayloadInterceptorChain(@Nullable PayloadInterceptor currentInterceptor,\n\t\t\t@Nullable ContextPayloadInterceptorChain next) {\n\t\tthis.currentInterceptor = currentInterceptor;\n\t\tthis.next = next;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tpublic Mono<Void> next(PayloadExchange exchange) {\n\t\treturn Mono.defer(() -> shouldIntercept() ? this.currentInterceptor.intercept(exchange, this.next)\n\t\t\t\t: Mono.deferContextual(Mono::just).cast(Context.class).doOnNext((c) -> this.context = c).then());\n\t}\n\n\t@Nullable Context getContext() {\n\t\tif (this.next == null) {\n\t\t\treturn this.context;\n\t\t}\n\t\treturn this.next.getContext();\n\t}\n\n\tprivate boolean shouldIntercept() {\n\t\treturn this.currentInterceptor != null && this.next != null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \"[currentInterceptor=\" + this.currentInterceptor + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/core/DefaultPayloadExchange.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport io.rsocket.Payload;\n\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadExchangeType;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MimeType;\n\n/**\n * Default implementation of {@link PayloadExchange}\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic class DefaultPayloadExchange implements PayloadExchange {\n\n\tprivate final PayloadExchangeType type;\n\n\tprivate final Payload payload;\n\n\tprivate final MimeType metadataMimeType;\n\n\tprivate final MimeType dataMimeType;\n\n\tpublic DefaultPayloadExchange(PayloadExchangeType type, Payload payload, MimeType metadataMimeType,\n\t\t\tMimeType dataMimeType) {\n\t\tAssert.notNull(type, \"type cannot be null\");\n\t\tAssert.notNull(payload, \"payload cannot be null\");\n\t\tAssert.notNull(metadataMimeType, \"metadataMimeType cannot be null\");\n\t\tAssert.notNull(dataMimeType, \"dataMimeType cannot be null\");\n\t\tthis.type = type;\n\t\tthis.payload = payload;\n\t\tthis.metadataMimeType = metadataMimeType;\n\t\tthis.dataMimeType = dataMimeType;\n\t}\n\n\t@Override\n\tpublic PayloadExchangeType getType() {\n\t\treturn this.type;\n\t}\n\n\t@Override\n\tpublic Payload getPayload() {\n\t\treturn this.payload;\n\t}\n\n\t@Override\n\tpublic MimeType getMetadataMimeType() {\n\t\treturn this.metadataMimeType;\n\t}\n\n\t@Override\n\tpublic MimeType getDataMimeType() {\n\t\treturn this.dataMimeType;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocket.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport java.util.List;\n\nimport io.rsocket.Payload;\nimport io.rsocket.RSocket;\nimport io.rsocket.util.RSocketProxy;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.security.rsocket.api.PayloadExchangeType;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MimeType;\n\n/**\n * Combines the {@link PayloadInterceptor} with an {@link RSocketProxy}\n *\n * @author Rob Winch\n * @since 5.2\n */\nclass PayloadInterceptorRSocket extends RSocketProxy {\n\n\tprivate final List<PayloadInterceptor> interceptors;\n\n\tprivate final MimeType metadataMimeType;\n\n\tprivate final MimeType dataMimeType;\n\n\tprivate final Context context;\n\n\tPayloadInterceptorRSocket(RSocket delegate, List<PayloadInterceptor> interceptors, MimeType metadataMimeType,\n\t\t\tMimeType dataMimeType) {\n\t\tthis(delegate, interceptors, metadataMimeType, dataMimeType, Context.empty());\n\t}\n\n\tPayloadInterceptorRSocket(RSocket delegate, List<PayloadInterceptor> interceptors, MimeType metadataMimeType,\n\t\t\tMimeType dataMimeType, Context context) {\n\t\tsuper(delegate);\n\t\tthis.metadataMimeType = metadataMimeType;\n\t\tthis.dataMimeType = dataMimeType;\n\t\tif (delegate == null) {\n\t\t\tthrow new IllegalArgumentException(\"delegate cannot be null\");\n\t\t}\n\t\tif (interceptors == null) {\n\t\t\tthrow new IllegalArgumentException(\"interceptors cannot be null\");\n\t\t}\n\t\tif (interceptors.isEmpty()) {\n\t\t\tthrow new IllegalArgumentException(\"interceptors cannot be empty\");\n\t\t}\n\t\tthis.interceptors = interceptors;\n\t\tthis.context = context;\n\t}\n\n\t@Override\n\tpublic Mono<Void> fireAndForget(Payload payload) {\n\t\treturn intercept(PayloadExchangeType.FIRE_AND_FORGET, payload)\n\t\t\t.flatMap((context) -> this.source.fireAndForget(payload).contextWrite(context));\n\t}\n\n\t@Override\n\tpublic Mono<Payload> requestResponse(Payload payload) {\n\t\treturn intercept(PayloadExchangeType.REQUEST_RESPONSE, payload)\n\t\t\t.flatMap((context) -> this.source.requestResponse(payload).contextWrite(context));\n\t}\n\n\t@Override\n\tpublic Flux<Payload> requestStream(Payload payload) {\n\t\treturn intercept(PayloadExchangeType.REQUEST_STREAM, payload)\n\t\t\t.flatMapMany((context) -> this.source.requestStream(payload).contextWrite(context));\n\t}\n\n\t@Override\n\tpublic Flux<Payload> requestChannel(Publisher<Payload> payloads) {\n\t\treturn Flux.from(payloads).switchOnFirst((signal, innerFlux) -> {\n\t\t\tPayload firstPayload = signal.get();\n\t\t\tAssert.notNull(firstPayload, \"payload cannot be null\");\n\t\t\treturn intercept(PayloadExchangeType.REQUEST_CHANNEL, firstPayload)\n\t\t\t\t.flatMapMany((context) -> innerFlux.index()\n\t\t\t\t\t.concatMap((tuple) -> justOrIntercept(tuple.getT1(), tuple.getT2()))\n\t\t\t\t\t.transform(this.source::requestChannel)\n\t\t\t\t\t.contextWrite(context));\n\t\t});\n\t}\n\n\tprivate Mono<Payload> justOrIntercept(Long index, Payload payload) {\n\t\treturn (index == 0) ? Mono.just(payload) : intercept(PayloadExchangeType.PAYLOAD, payload).thenReturn(payload);\n\t}\n\n\t@Override\n\tpublic Mono<Void> metadataPush(Payload payload) {\n\t\treturn intercept(PayloadExchangeType.METADATA_PUSH, payload)\n\t\t\t.flatMap((c) -> this.source.metadataPush(payload).contextWrite(c));\n\t}\n\n\tprivate Mono<Context> intercept(PayloadExchangeType type, Payload payload) {\n\t\treturn Mono.defer(() -> {\n\t\t\tContextPayloadInterceptorChain chain = new ContextPayloadInterceptorChain(this.interceptors);\n\t\t\tDefaultPayloadExchange exchange = new DefaultPayloadExchange(type, payload, this.metadataMimeType,\n\t\t\t\t\tthis.dataMimeType);\n\t\t\treturn chain.next(exchange)\n\t\t\t\t.then(Mono.fromCallable(chain::getContext))\n\t\t\t\t.defaultIfEmpty(Context.empty())\n\t\t\t\t.contextWrite(this.context);\n\t\t});\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \"[source=\" + this.source + \",interceptors=\" + this.interceptors + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport java.util.List;\n\nimport io.rsocket.ConnectionSetupPayload;\nimport io.rsocket.Payload;\nimport io.rsocket.RSocket;\nimport io.rsocket.SocketAcceptor;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.security.rsocket.api.PayloadExchangeType;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * @author Rob Winch\n * @since 5.2\n */\nclass PayloadSocketAcceptor implements SocketAcceptor {\n\n\tprivate final SocketAcceptor delegate;\n\n\tprivate final List<PayloadInterceptor> interceptors;\n\n\tprivate @Nullable MimeType defaultDataMimeType;\n\n\tprivate MimeType defaultMetadataMimeType = MimeTypeUtils\n\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\n\tPayloadSocketAcceptor(SocketAcceptor delegate, List<PayloadInterceptor> interceptors) {\n\t\tAssert.notNull(delegate, \"delegate cannot be null\");\n\t\tif (interceptors == null) {\n\t\t\tthrow new IllegalArgumentException(\"interceptors cannot be null\");\n\t\t}\n\t\tif (interceptors.isEmpty()) {\n\t\t\tthrow new IllegalArgumentException(\"interceptors cannot be empty\");\n\t\t}\n\t\tthis.delegate = delegate;\n\t\tthis.interceptors = interceptors;\n\t}\n\n\t@Override\n\tpublic Mono<RSocket> accept(ConnectionSetupPayload setup, RSocket sendingSocket) {\n\t\tMimeType dataMimeType = parseMimeType(setup.dataMimeType(), this.defaultDataMimeType);\n\t\tAssert.notNull(dataMimeType, \"No `dataMimeType` in ConnectionSetupPayload and no default value\");\n\t\tMimeType metadataMimeType = parseMimeType(setup.metadataMimeType(), this.defaultMetadataMimeType);\n\t\tAssert.notNull(metadataMimeType, \"No `metadataMimeType` in ConnectionSetupPayload and no default value\");\n\t\t// FIXME do we want to make the sendingSocket available in the PayloadExchange\n\t\treturn intercept(setup, dataMimeType, metadataMimeType)\n\t\t\t.flatMap((ctx) -> this.delegate.accept(setup, sendingSocket)\n\t\t\t\t.map((acceptingSocket) -> new PayloadInterceptorRSocket(acceptingSocket, this.interceptors,\n\t\t\t\t\t\tmetadataMimeType, dataMimeType, ctx))\n\t\t\t\t.contextWrite(ctx));\n\t}\n\n\tprivate Mono<Context> intercept(Payload payload, MimeType dataMimeType, MimeType metadataMimeType) {\n\t\treturn Mono.defer(() -> {\n\t\t\tContextPayloadInterceptorChain chain = new ContextPayloadInterceptorChain(this.interceptors);\n\t\t\tDefaultPayloadExchange exchange = new DefaultPayloadExchange(PayloadExchangeType.SETUP, payload,\n\t\t\t\t\tmetadataMimeType, dataMimeType);\n\t\t\treturn chain.next(exchange).then(Mono.fromCallable(chain::getContext)).defaultIfEmpty(Context.empty());\n\t\t});\n\t}\n\n\tprivate @Nullable MimeType parseMimeType(String str, @Nullable MimeType defaultMimeType) {\n\t\treturn StringUtils.hasText(str) ? MimeTypeUtils.parseMimeType(str) : defaultMimeType;\n\t}\n\n\tvoid setDefaultDataMimeType(@Nullable MimeType defaultDataMimeType) {\n\t\tthis.defaultDataMimeType = defaultDataMimeType;\n\t}\n\n\tvoid setDefaultMetadataMimeType(MimeType defaultMetadataMimeType) {\n\t\tAssert.notNull(defaultMetadataMimeType, \"defaultMetadataMimeType cannot be null\");\n\t\tthis.defaultMetadataMimeType = defaultMetadataMimeType;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptorInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport java.util.List;\n\nimport io.rsocket.SocketAcceptor;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport io.rsocket.plugins.SocketAcceptorInterceptor;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\n/**\n * A {@link SocketAcceptorInterceptor} that applies the {@link PayloadInterceptor}s\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic class PayloadSocketAcceptorInterceptor implements SocketAcceptorInterceptor {\n\n\tprivate final List<PayloadInterceptor> interceptors;\n\n\tprivate @Nullable MimeType defaultDataMimeType;\n\n\tprivate MimeType defaultMetadataMimeType = MimeTypeUtils\n\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\n\tpublic PayloadSocketAcceptorInterceptor(List<PayloadInterceptor> interceptors) {\n\t\tthis.interceptors = interceptors;\n\t}\n\n\t@Override\n\tpublic SocketAcceptor apply(SocketAcceptor socketAcceptor) {\n\t\tPayloadSocketAcceptor acceptor = new PayloadSocketAcceptor(socketAcceptor, this.interceptors);\n\t\tacceptor.setDefaultDataMimeType(this.defaultDataMimeType);\n\t\tacceptor.setDefaultMetadataMimeType(this.defaultMetadataMimeType);\n\t\treturn acceptor;\n\t}\n\n\tpublic void setDefaultDataMimeType(@Nullable MimeType defaultDataMimeType) {\n\t\tthis.defaultDataMimeType = defaultDataMimeType;\n\t}\n\n\tpublic void setDefaultMetadataMimeType(MimeType defaultMetadataMimeType) {\n\t\tAssert.notNull(defaultMetadataMimeType, \"defaultMetadataMimeType cannot be null\");\n\t\tthis.defaultMetadataMimeType = defaultMetadataMimeType;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/core/SecuritySocketAcceptorInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport io.rsocket.SocketAcceptor;\nimport io.rsocket.plugins.SocketAcceptorInterceptor;\n\nimport org.springframework.util.Assert;\n\n/**\n * A SocketAcceptorInterceptor that applies Security through a delegate\n * {@link SocketAcceptorInterceptor}. This allows security to be applied lazily to an\n * application.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic class SecuritySocketAcceptorInterceptor implements SocketAcceptorInterceptor {\n\n\tprivate final SocketAcceptorInterceptor acceptorInterceptor;\n\n\tpublic SecuritySocketAcceptorInterceptor(SocketAcceptorInterceptor acceptorInterceptor) {\n\t\tAssert.notNull(acceptorInterceptor, \"acceptorInterceptor cannot be null\");\n\t\tthis.acceptorInterceptor = acceptorInterceptor;\n\t}\n\n\t@Override\n\tpublic SocketAcceptor apply(SocketAcceptor socketAcceptor) {\n\t\treturn this.acceptorInterceptor.apply(socketAcceptor);\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/core/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security RSocket core integration.\n */\n@NullMarked\npackage org.springframework.security.rsocket.core;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.metadata;\n\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.codec.AbstractDecoder;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.util.MimeType;\n\n/**\n * Decodes {@link UsernamePasswordMetadata#BASIC_AUTHENTICATION_MIME_TYPE}\n *\n * @author Rob Winch\n * @since 5.2\n * @deprecated Basic Authentication did not evolve into a standard. Use Simple\n * Authentication instead.\n */\n@Deprecated\npublic class BasicAuthenticationDecoder extends AbstractDecoder<UsernamePasswordMetadata> {\n\n\tpublic BasicAuthenticationDecoder() {\n\t\tsuper(UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE);\n\t}\n\n\t@Override\n\tpublic Flux<UsernamePasswordMetadata> decode(Publisher<DataBuffer> input, ResolvableType elementType,\n\t\t\t@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {\n\t\treturn Flux.from(input).map(DataBuffer::asByteBuffer).map((byteBuffer) -> {\n\t\t\tbyte[] sizeBytes = new byte[4];\n\t\t\tbyteBuffer.get(sizeBytes);\n\t\t\tint usernameSize = 4;\n\t\t\tbyte[] usernameBytes = new byte[usernameSize];\n\t\t\tbyteBuffer.get(usernameBytes);\n\t\t\tbyte[] passwordBytes = new byte[byteBuffer.remaining()];\n\t\t\tbyteBuffer.get(passwordBytes);\n\t\t\tString username = new String(usernameBytes);\n\t\t\tString password = new String(passwordBytes);\n\t\t\treturn new UsernamePasswordMetadata(username, password);\n\t\t});\n\t}\n\n\t@Override\n\tpublic Mono<UsernamePasswordMetadata> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,\n\t\t\t@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {\n\t\treturn Mono.from(input).map(DataBuffer::asByteBuffer).map((byteBuffer) -> {\n\t\t\tint usernameSize = byteBuffer.getInt();\n\t\t\tbyte[] usernameBytes = new byte[usernameSize];\n\t\t\tbyteBuffer.get(usernameBytes);\n\t\t\tbyte[] passwordBytes = new byte[byteBuffer.remaining()];\n\t\t\tbyteBuffer.get(passwordBytes);\n\t\t\tString username = new String(usernameBytes);\n\t\t\tString password = new String(passwordBytes);\n\t\t\treturn new UsernamePasswordMetadata(username, password);\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/metadata/BasicAuthenticationEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.metadata;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.codec.AbstractEncoder;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.core.io.buffer.DataBufferUtils;\nimport org.springframework.util.MimeType;\n\n/**\n * Encodes {@link UsernamePasswordMetadata#BASIC_AUTHENTICATION_MIME_TYPE}\n *\n * @author Rob Winch\n * @since 5.2\n * @deprecated Basic Authentication did not evolve into a standard. use\n * {@link SimpleAuthenticationEncoder}\n */\n@Deprecated\npublic class BasicAuthenticationEncoder extends AbstractEncoder<UsernamePasswordMetadata> {\n\n\tpublic BasicAuthenticationEncoder() {\n\t\tsuper(UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE);\n\t}\n\n\t@Override\n\tpublic Flux<DataBuffer> encode(Publisher<? extends UsernamePasswordMetadata> inputStream,\n\t\t\tDataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType,\n\t\t\t@Nullable Map<String, Object> hints) {\n\t\treturn Flux.from(inputStream)\n\t\t\t.map((credentials) -> encodeValue(credentials, bufferFactory, elementType, mimeType, hints));\n\t}\n\n\t@Override\n\tpublic DataBuffer encodeValue(UsernamePasswordMetadata credentials, DataBufferFactory bufferFactory,\n\t\t\tResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {\n\t\tString username = credentials.getUsername();\n\t\tString password = credentials.getPassword();\n\t\tbyte[] usernameBytes = username.getBytes(StandardCharsets.UTF_8);\n\t\tbyte[] usernameBytesLengthBytes = ByteBuffer.allocate(4).putInt(usernameBytes.length).array();\n\t\tDataBuffer metadata = bufferFactory.allocateBuffer();\n\t\tboolean release = true;\n\t\ttry {\n\t\t\tmetadata.write(usernameBytesLengthBytes);\n\t\t\tmetadata.write(usernameBytes);\n\t\t\tmetadata.write(password.getBytes(StandardCharsets.UTF_8));\n\t\t\trelease = false;\n\t\t\treturn metadata;\n\t\t}\n\t\tfinally {\n\t\t\tif (release) {\n\t\t\t\tDataBufferUtils.release(metadata);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/metadata/BearerTokenAuthenticationEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.metadata;\n\nimport java.util.Map;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.ByteBufAllocator;\nimport io.rsocket.metadata.AuthMetadataCodec;\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.codec.AbstractEncoder;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.core.io.buffer.NettyDataBufferFactory;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\n/**\n * Encodes <a href=\n * \"https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Bearer.md\">Bearer\n * Authentication</a>.\n *\n * @author Rob Winch\n * @since 5.3\n */\npublic class BearerTokenAuthenticationEncoder extends AbstractEncoder<BearerTokenMetadata> {\n\n\tprivate static final MimeType AUTHENTICATION_MIME_TYPE = MimeTypeUtils\n\t\t.parseMimeType(\"message/x.rsocket.authentication.v0\");\n\n\tprivate NettyDataBufferFactory defaultBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);\n\n\tpublic BearerTokenAuthenticationEncoder() {\n\t\tsuper(AUTHENTICATION_MIME_TYPE);\n\t}\n\n\t@Override\n\tpublic Flux<DataBuffer> encode(Publisher<? extends BearerTokenMetadata> inputStream,\n\t\t\tDataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType,\n\t\t\t@Nullable Map<String, Object> hints) {\n\t\treturn Flux.from(inputStream)\n\t\t\t.map((credentials) -> encodeValue(credentials, bufferFactory, elementType, mimeType, hints));\n\t}\n\n\t@Override\n\tpublic DataBuffer encodeValue(BearerTokenMetadata credentials, DataBufferFactory bufferFactory,\n\t\t\tResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {\n\t\tString token = credentials.getToken();\n\t\tNettyDataBufferFactory factory = nettyFactory(bufferFactory);\n\t\tByteBufAllocator allocator = factory.getByteBufAllocator();\n\t\tByteBuf simpleAuthentication = AuthMetadataCodec.encodeBearerMetadata(allocator, token.toCharArray());\n\t\treturn factory.wrap(simpleAuthentication);\n\t}\n\n\tprivate NettyDataBufferFactory nettyFactory(DataBufferFactory bufferFactory) {\n\t\tif (bufferFactory instanceof NettyDataBufferFactory) {\n\t\t\treturn (NettyDataBufferFactory) bufferFactory;\n\t\t}\n\t\treturn this.defaultBufferFactory;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/metadata/BearerTokenMetadata.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.metadata;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.util.MimeType;\n\n/**\n * Represents a bearer token that has been encoded into a\n * {@link io.rsocket.Payload#metadata() Payload#metadata()}.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic class BearerTokenMetadata {\n\n\t/**\n\t * Represents a bearer token which is encoded as a String.\n\t *\n\t * See <a href=\"https://github.com/rsocket/rsocket/issues/272\">rsocket/rsocket#272</a>\n\t * @deprecated Basic did not evolve into the standard. Instead use Simple\n\t * Authentication\n\t * MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString())\n\t */\n\t@Deprecated\n\tpublic static final MimeType BEARER_AUTHENTICATION_MIME_TYPE = new MediaType(\"message\",\n\t\t\t\"x.rsocket.authentication.bearer.v0\");\n\n\tprivate final String token;\n\n\tpublic BearerTokenMetadata(String token) {\n\t\tthis.token = token;\n\t}\n\n\tpublic String getToken() {\n\t\treturn this.token;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/metadata/SimpleAuthenticationEncoder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.metadata;\n\nimport java.util.Map;\n\nimport io.netty.buffer.ByteBuf;\nimport io.netty.buffer.ByteBufAllocator;\nimport io.rsocket.metadata.AuthMetadataCodec;\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.codec.AbstractEncoder;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.core.io.buffer.NettyDataBufferFactory;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\n/**\n * Encodes <a href=\n * \"https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Simple.md\">Simple</a>\n * Authentication.\n *\n * @author Rob Winch\n * @since 5.3\n */\npublic class SimpleAuthenticationEncoder extends AbstractEncoder<UsernamePasswordMetadata> {\n\n\tprivate static final MimeType AUTHENTICATION_MIME_TYPE = MimeTypeUtils\n\t\t.parseMimeType(\"message/x.rsocket.authentication.v0\");\n\n\tprivate NettyDataBufferFactory defaultBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);\n\n\tpublic SimpleAuthenticationEncoder() {\n\t\tsuper(AUTHENTICATION_MIME_TYPE);\n\t}\n\n\t@Override\n\tpublic Flux<DataBuffer> encode(Publisher<? extends UsernamePasswordMetadata> inputStream,\n\t\t\tDataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType,\n\t\t\t@Nullable Map<String, Object> hints) {\n\t\treturn Flux.from(inputStream)\n\t\t\t.map((credentials) -> encodeValue(credentials, bufferFactory, elementType, mimeType, hints));\n\t}\n\n\t@Override\n\tpublic DataBuffer encodeValue(UsernamePasswordMetadata credentials, DataBufferFactory bufferFactory,\n\t\t\tResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {\n\t\tString username = credentials.getUsername();\n\t\tString password = credentials.getPassword();\n\t\tNettyDataBufferFactory factory = nettyFactory(bufferFactory);\n\t\tByteBufAllocator allocator = factory.getByteBufAllocator();\n\t\tByteBuf simpleAuthentication = AuthMetadataCodec.encodeSimpleMetadata(allocator, username.toCharArray(),\n\t\t\t\tpassword.toCharArray());\n\t\treturn factory.wrap(simpleAuthentication);\n\t}\n\n\tprivate NettyDataBufferFactory nettyFactory(DataBufferFactory bufferFactory) {\n\t\tif (bufferFactory instanceof NettyDataBufferFactory) {\n\t\t\treturn (NettyDataBufferFactory) bufferFactory;\n\t\t}\n\t\treturn this.defaultBufferFactory;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/metadata/UsernamePasswordMetadata.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.metadata;\n\nimport io.rsocket.Payload;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.util.MimeType;\n\n/**\n * Represents a username and password that have been encoded into a\n * {@link Payload#metadata()}.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic final class UsernamePasswordMetadata {\n\n\t/**\n\t * Represents a username password which is encoded as\n\t * {@code ${username-bytes-length}${username-bytes}${password-bytes}}.\n\t *\n\t * See <a href=\"https://github.com/rsocket/rsocket/issues/272\">rsocket/rsocket#272</a>\n\t * @deprecated Basic did not evolve into the standard. Instead use Simple\n\t * Authentication\n\t * MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString())\n\t */\n\t@Deprecated\n\tpublic static final MimeType BASIC_AUTHENTICATION_MIME_TYPE = new MediaType(\"message\",\n\t\t\t\"x.rsocket.authentication.basic.v0\");\n\n\tprivate final String username;\n\n\tprivate final String password;\n\n\tpublic UsernamePasswordMetadata(String username, String password) {\n\t\tthis.username = username;\n\t\tthis.password = password;\n\t}\n\n\tpublic String getUsername() {\n\t\treturn this.username;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn this.password;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/metadata/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security RSocket metadata integration.\n */\n@NullMarked\npackage org.springframework.security.rsocket.metadata;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeAuthorizationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.util.matcher;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.springframework.security.rsocket.api.PayloadExchange;\n\n/**\n * @author Rob Winch\n * @since 5.2\n */\npublic class PayloadExchangeAuthorizationContext {\n\n\tprivate final PayloadExchange exchange;\n\n\tprivate final Map<String, Object> variables;\n\n\tpublic PayloadExchangeAuthorizationContext(PayloadExchange exchange) {\n\t\tthis(exchange, Collections.emptyMap());\n\t}\n\n\tpublic PayloadExchangeAuthorizationContext(PayloadExchange exchange, Map<String, Object> variables) {\n\t\tthis.exchange = exchange;\n\t\tthis.variables = variables;\n\t}\n\n\tpublic PayloadExchange getExchange() {\n\t\treturn this.exchange;\n\t}\n\n\tpublic Map<String, Object> getVariables() {\n\t\treturn Collections.unmodifiableMap(this.variables);\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.util.matcher;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.rsocket.api.PayloadExchange;\n\n/**\n * An interface for determining if a {@link PayloadExchangeMatcher} matches.\n *\n * @author Rob Winch\n * @since 5.2\n */\npublic interface PayloadExchangeMatcher {\n\n\t/**\n\t * Determines if a request matches or not\n\t * @param exchange\n\t * @return\n\t */\n\tMono<MatchResult> matches(PayloadExchange exchange);\n\n\t/**\n\t * The result of matching\n\t */\n\tclass MatchResult {\n\n\t\tprivate final boolean match;\n\n\t\tprivate final @Nullable Map<String, Object> variables;\n\n\t\tprivate MatchResult(boolean match, @Nullable Map<String, Object> variables) {\n\t\t\tthis.match = match;\n\t\t\tthis.variables = variables;\n\t\t}\n\n\t\tpublic boolean isMatch() {\n\t\t\treturn this.match;\n\t\t}\n\n\t\t/**\n\t\t * Gets potential variables and their values\n\t\t * @return\n\t\t */\n\t\tpublic @Nullable Map<String, Object> getVariables() {\n\t\t\treturn this.variables;\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is a match with no variables\n\t\t * @return\n\t\t */\n\t\tpublic static Mono<MatchResult> match() {\n\t\t\treturn match(Collections.emptyMap());\n\t\t}\n\n\t\t/**\n\t\t *\n\t\t * Creates an instance of {@link MatchResult} that is a match with the specified\n\t\t * variables\n\t\t * @param variables\n\t\t * @return\n\t\t */\n\t\tpublic static Mono<MatchResult> match(Map<String, ? extends Object> variables) {\n\t\t\tMatchResult result = new MatchResult(true, (variables != null) ? new HashMap<>(variables) : null);\n\t\t\treturn Mono.just(result);\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is not a match.\n\t\t * @return\n\t\t */\n\t\tpublic static Mono<MatchResult> notMatch() {\n\t\t\treturn Mono.just(new MatchResult(false, Collections.emptyMap()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatcherEntry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.util.matcher;\n\n/**\n * @author Rob Winch\n */\npublic class PayloadExchangeMatcherEntry<T> {\n\n\tprivate final PayloadExchangeMatcher matcher;\n\n\tprivate final T entry;\n\n\tpublic PayloadExchangeMatcherEntry(PayloadExchangeMatcher matcher, T entry) {\n\t\tthis.matcher = matcher;\n\t\tthis.entry = entry;\n\t}\n\n\tpublic PayloadExchangeMatcher getMatcher() {\n\t\treturn this.matcher;\n\t}\n\n\tpublic T getEntry() {\n\t\treturn this.entry;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatchers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.util.matcher;\n\nimport org.springframework.security.rsocket.api.PayloadExchangeType;\n\n/**\n * @author Rob Winch\n */\npublic final class PayloadExchangeMatchers {\n\n\tprivate PayloadExchangeMatchers() {\n\t}\n\n\tpublic static PayloadExchangeMatcher setup() {\n\t\treturn (exchange) -> PayloadExchangeType.SETUP.equals(exchange.getType())\n\t\t\t\t? PayloadExchangeMatcher.MatchResult.match() : PayloadExchangeMatcher.MatchResult.notMatch();\n\t}\n\n\tpublic static PayloadExchangeMatcher anyRequest() {\n\t\treturn (exchange) -> exchange.getType().isRequest() ? PayloadExchangeMatcher.MatchResult.match()\n\t\t\t\t: PayloadExchangeMatcher.MatchResult.notMatch();\n\t}\n\n\tpublic static PayloadExchangeMatcher anyExchange() {\n\t\treturn (exchange) -> PayloadExchangeMatcher.MatchResult.match();\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/RoutePayloadExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.util.matcher;\n\nimport java.util.Map;\nimport java.util.Optional;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.messaging.rsocket.MetadataExtractor;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.util.Assert;\nimport org.springframework.util.RouteMatcher;\n\n// FIXME: Pay attention to the package this goes into. It requires spring-messaging for the MetadataExtractor.\n\n/**\n * @author Rob Winch\n * @since 5.2\n */\npublic class RoutePayloadExchangeMatcher implements PayloadExchangeMatcher {\n\n\tprivate final String pattern;\n\n\tprivate final MetadataExtractor metadataExtractor;\n\n\tprivate final RouteMatcher routeMatcher;\n\n\tpublic RoutePayloadExchangeMatcher(MetadataExtractor metadataExtractor, RouteMatcher routeMatcher, String pattern) {\n\t\tAssert.notNull(pattern, \"pattern cannot be null\");\n\t\tthis.metadataExtractor = metadataExtractor;\n\t\tthis.routeMatcher = routeMatcher;\n\t\tthis.pattern = pattern;\n\t}\n\n\t@Override\n\tpublic Mono<MatchResult> matches(PayloadExchange exchange) {\n\t\tMap<String, Object> metadata = this.metadataExtractor.extract(exchange.getPayload(),\n\t\t\t\texchange.getMetadataMimeType());\n\t\treturn Optional.ofNullable((String) metadata.get(MetadataExtractor.ROUTE_KEY))\n\t\t\t.map(this.routeMatcher::parseRoute)\n\t\t\t.map((route) -> this.routeMatcher.matchAndExtract(this.pattern, route))\n\t\t\t.map(MatchResult::match)\n\t\t\t.orElse(MatchResult.notMatch());\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security RSocket matching APIs.\n */\n@NullMarked\npackage org.springframework.security.rsocket.util.matcher;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/authentication/AnonymousPayloadInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.rsocket.api.PayloadExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class AnonymousPayloadInterceptorTests {\n\n\t@Mock\n\tprivate PayloadExchange exchange;\n\n\tprivate AnonymousPayloadInterceptor interceptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.interceptor = new AnonymousPayloadInterceptor(\"key\");\n\t}\n\n\t@Test\n\tpublic void constructorKeyWhenKeyNullThenException() {\n\t\tString key = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousPayloadInterceptor(key));\n\t}\n\n\t@Test\n\tpublic void constructorKeyPrincipalAuthoritiesWhenKeyNullThenException() {\n\t\tString key = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousPayloadInterceptor(key, \"principal\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\")));\n\t}\n\n\t@Test\n\tpublic void constructorKeyPrincipalAuthoritiesWhenPrincipalNullThenException() {\n\t\tObject principal = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousPayloadInterceptor(\"key\", principal,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\")));\n\t}\n\n\t@Test\n\tpublic void constructorKeyPrincipalAuthoritiesWhenAuthoritiesNullThenException() {\n\t\tList<GrantedAuthority> authorities = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AnonymousPayloadInterceptor(\"key\", \"principal\", authorities));\n\t}\n\n\t@Test\n\tpublic void interceptWhenNoAuthenticationThenAnonymousAuthentication() {\n\t\tAuthenticationPayloadInterceptorChain chain = new AuthenticationPayloadInterceptorChain();\n\t\tthis.interceptor.intercept(this.exchange, chain).block();\n\t\tAuthentication authentication = chain.getAuthentication();\n\t\tassertThat(authentication).isInstanceOf(AnonymousAuthenticationToken.class);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAuthenticationThenOriginalAuthentication() {\n\t\tAuthenticationPayloadInterceptorChain chain = new AuthenticationPayloadInterceptorChain();\n\t\tTestingAuthenticationToken expected = new TestingAuthenticationToken(\"test\", \"password\");\n\t\tthis.interceptor.intercept(this.exchange, chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(expected))\n\t\t\t.block();\n\t\tAuthentication authentication = chain.getAuthentication();\n\t\tassertThat(authentication).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadInterceptorChain.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadInterceptorChain;\n\n/**\n * @author Rob Winch\n */\nclass AuthenticationPayloadInterceptorChain implements PayloadInterceptorChain {\n\n\tprivate Authentication authentication;\n\n\t@Override\n\tpublic Mono<Void> next(PayloadExchange exchange) {\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t.doOnNext((a) -> this.setAuthentication(a))\n\t\t\t.then();\n\t}\n\n\tAuthentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n\tvoid setAuthentication(Authentication authentication) {\n\t\tthis.authentication = authentication;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authentication;\n\nimport java.util.Map;\n\nimport io.netty.buffer.ByteBufAllocator;\nimport io.netty.buffer.CompositeByteBuf;\nimport io.rsocket.Payload;\nimport io.rsocket.metadata.CompositeMetadataCodec;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport io.rsocket.util.DefaultPayload;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DefaultDataBufferFactory;\nimport org.springframework.core.io.buffer.NettyDataBufferFactory;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadExchangeType;\nimport org.springframework.security.rsocket.api.PayloadInterceptorChain;\nimport org.springframework.security.rsocket.core.DefaultPayloadExchange;\nimport org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder;\nimport org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthenticationPayloadInterceptorTests {\n\n\tstatic final MimeType COMPOSITE_METADATA = MimeTypeUtils\n\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\n\t@Mock\n\tReactiveAuthenticationManager authenticationManager;\n\n\t@Captor\n\tArgumentCaptor<Authentication> authenticationArg;\n\n\t@Test\n\tpublic void constructorWhenAuthenticationManagerNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AuthenticationPayloadInterceptor(null));\n\t}\n\n\t@Test\n\tpublic void interceptWhenBasicCredentialsThenAuthenticates() {\n\t\tAuthenticationPayloadInterceptor interceptor = new AuthenticationPayloadInterceptor(this.authenticationManager);\n\t\tPayloadExchange exchange = createExchange();\n\t\tTestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(Mono.just(expectedAuthentication));\n\t\tAuthenticationPayloadInterceptorChain authenticationPayloadChain = new AuthenticationPayloadInterceptorChain();\n\t\tinterceptor.intercept(exchange, authenticationPayloadChain).block();\n\t\tAuthentication authentication = authenticationPayloadChain.getAuthentication();\n\t\tverify(this.authenticationManager).authenticate(this.authenticationArg.capture());\n\t\tassertThat(this.authenticationArg.getValue()).usingRecursiveComparison()\n\t\t\t.isEqualTo(UsernamePasswordAuthenticationToken.unauthenticated(\"user\", \"password\"));\n\t\tassertThat(authentication).isEqualTo(expectedAuthentication);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAuthenticationSuccessThenChainSubscribedOnce() {\n\t\tAuthenticationPayloadInterceptor interceptor = new AuthenticationPayloadInterceptor(this.authenticationManager);\n\t\tPayloadExchange exchange = createExchange();\n\t\tTestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(Mono.just(expectedAuthentication));\n\t\tPublisherProbe<Void> voidResult = PublisherProbe.empty();\n\t\tPayloadInterceptorChain chain = mock(PayloadInterceptorChain.class);\n\t\tgiven(chain.next(any())).willReturn(voidResult.mono());\n\t\tStepVerifier.create(interceptor.intercept(exchange, chain))\n\t\t\t.then(() -> assertThat(voidResult.subscribeCount()).isEqualTo(1))\n\t\t\t.verifyComplete();\n\t}\n\n\tprivate Payload createRequestPayload() {\n\t\tUsernamePasswordMetadata credentials = new UsernamePasswordMetadata(\"user\", \"password\");\n\t\tBasicAuthenticationEncoder encoder = new BasicAuthenticationEncoder();\n\t\tDefaultDataBufferFactory factory = new DefaultDataBufferFactory();\n\t\tResolvableType elementType = ResolvableType.forClass(UsernamePasswordMetadata.class);\n\t\tMimeType mimeType = UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE;\n\t\tMap<String, Object> hints = null;\n\t\tDataBuffer dataBuffer = encoder.encodeValue(credentials, factory, elementType, mimeType, hints);\n\t\tByteBufAllocator allocator = ByteBufAllocator.DEFAULT;\n\t\tCompositeByteBuf metadata = allocator.compositeBuffer();\n\t\tCompositeMetadataCodec.encodeAndAddMetadata(metadata, allocator, mimeType.toString(),\n\t\t\t\tNettyDataBufferFactory.toByteBuf(dataBuffer));\n\t\treturn DefaultPayload.create(allocator.buffer(), metadata);\n\t}\n\n\tprivate PayloadExchange createExchange() {\n\t\treturn new DefaultPayloadExchange(PayloadExchangeType.REQUEST_RESPONSE, createRequestPayload(),\n\t\t\t\tCOMPOSITE_METADATA, MediaType.APPLICATION_JSON);\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/authorization/AuthorizationPayloadInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authorization;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.PublisherProbe;\nimport reactor.util.context.Context;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityReactiveAuthorizationManager;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadInterceptorChain;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthorizationPayloadInterceptorTests {\n\n\t@Mock\n\tprivate ReactiveAuthorizationManager<PayloadExchange> authorizationManager;\n\n\t@Mock\n\tprivate PayloadExchange exchange;\n\n\t@Mock\n\tprivate PayloadInterceptorChain chain;\n\n\tprivate PublisherProbe<Void> managerResult = PublisherProbe.empty();\n\n\tprivate PublisherProbe<Void> chainResult = PublisherProbe.empty();\n\n\t@Test\n\tpublic void interceptWhenAuthenticationEmptyAndSubscribedThenException() {\n\t\tgiven(this.chain.next(any())).willReturn(this.chainResult.mono());\n\t\tAuthorizationPayloadInterceptor interceptor = new AuthorizationPayloadInterceptor(\n\t\t\t\tAuthenticatedReactiveAuthorizationManager.authenticated());\n\t\tStepVerifier.create(interceptor.intercept(this.exchange, this.chain))\n\t\t\t.then(() -> this.chainResult.assertWasNotSubscribed())\n\t\t\t.verifyError(AuthenticationCredentialsNotFoundException.class);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAuthenticationNotSubscribedAndEmptyThenCompletes() {\n\t\tgiven(this.chain.next(any())).willReturn(this.chainResult.mono());\n\t\tgiven(this.authorizationManager.verify(any(), any())).willReturn(this.managerResult.mono());\n\t\tAuthorizationPayloadInterceptor interceptor = new AuthorizationPayloadInterceptor(this.authorizationManager);\n\t\tStepVerifier.create(interceptor.intercept(this.exchange, this.chain))\n\t\t\t.then(() -> this.chainResult.assertWasSubscribed())\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void interceptWhenNotAuthorizedThenException() {\n\t\tgiven(this.chain.next(any())).willReturn(this.chainResult.mono());\n\t\tAuthorizationPayloadInterceptor interceptor = new AuthorizationPayloadInterceptor(\n\t\t\t\tAuthorityReactiveAuthorizationManager.hasRole(\"USER\"));\n\t\tContext userContext = ReactiveSecurityContextHolder\n\t\t\t.withAuthentication(new TestingAuthenticationToken(\"user\", \"password\"));\n\t\tMono<Void> intercept = interceptor.intercept(this.exchange, this.chain).contextWrite(userContext);\n\t\tStepVerifier.create(intercept)\n\t\t\t.then(() -> this.chainResult.assertWasNotSubscribed())\n\t\t\t.verifyError(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void interceptWhenAuthorizedThenContinues() {\n\t\tgiven(this.chain.next(any())).willReturn(this.chainResult.mono());\n\t\tAuthorizationPayloadInterceptor interceptor = new AuthorizationPayloadInterceptor(\n\t\t\t\tAuthenticatedReactiveAuthorizationManager.authenticated());\n\t\tContext userContext = ReactiveSecurityContextHolder\n\t\t\t.withAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tMono<Void> intercept = interceptor.intercept(this.exchange, this.chain).contextWrite(userContext);\n\t\tStepVerifier.create(intercept).then(() -> this.chainResult.assertWasSubscribed()).verifyComplete();\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.authorization;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcher;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcherEntry;\nimport org.springframework.security.rsocket.util.matcher.PayloadExchangeMatchers;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class PayloadExchangeMatcherReactiveAuthorizationManagerTests {\n\n\t@Mock\n\tprivate ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> authz;\n\n\t@Mock\n\tprivate ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> authz2;\n\n\t@Mock\n\tprivate PayloadExchange exchange;\n\n\t@Test\n\tpublic void checkWhenGrantedThenGranted() {\n\t\tAuthorizationDecision expected = new AuthorizationDecision(true);\n\t\tgiven(this.authz.authorize(any(), any())).willReturn(Mono.just(expected));\n\t\tPayloadExchangeMatcherReactiveAuthorizationManager manager = PayloadExchangeMatcherReactiveAuthorizationManager\n\t\t\t.builder()\n\t\t\t.add(new PayloadExchangeMatcherEntry<>(PayloadExchangeMatchers.anyExchange(), this.authz))\n\t\t\t.build();\n\t\tassertThat(manager.authorize(Mono.empty(), this.exchange).block()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void checkWhenDeniedThenDenied() {\n\t\tAuthorizationDecision expected = new AuthorizationDecision(false);\n\t\tgiven(this.authz.authorize(any(), any())).willReturn(Mono.just(expected));\n\t\tPayloadExchangeMatcherReactiveAuthorizationManager manager = PayloadExchangeMatcherReactiveAuthorizationManager\n\t\t\t.builder()\n\t\t\t.add(new PayloadExchangeMatcherEntry<>(PayloadExchangeMatchers.anyExchange(), this.authz))\n\t\t\t.build();\n\t\tassertThat(manager.authorize(Mono.empty(), this.exchange).block()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void checkWhenFirstMatchThenSecondUsed() {\n\t\tAuthorizationDecision expected = new AuthorizationDecision(true);\n\t\tgiven(this.authz.authorize(any(), any())).willReturn(Mono.just(expected));\n\t\tPayloadExchangeMatcherReactiveAuthorizationManager manager = PayloadExchangeMatcherReactiveAuthorizationManager\n\t\t\t.builder()\n\t\t\t.add(new PayloadExchangeMatcherEntry<>(PayloadExchangeMatchers.anyExchange(), this.authz))\n\t\t\t.add(new PayloadExchangeMatcherEntry<>((e) -> PayloadExchangeMatcher.MatchResult.notMatch(), this.authz2))\n\t\t\t.build();\n\t\tassertThat(manager.authorize(Mono.empty(), this.exchange).block()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void checkWhenSecondMatchThenSecondUsed() {\n\t\tAuthorizationDecision expected = new AuthorizationDecision(true);\n\t\tgiven(this.authz2.authorize(any(), any())).willReturn(Mono.just(expected));\n\t\tPayloadExchangeMatcherReactiveAuthorizationManager manager = PayloadExchangeMatcherReactiveAuthorizationManager\n\t\t\t.builder()\n\t\t\t.add(new PayloadExchangeMatcherEntry<>((e) -> PayloadExchangeMatcher.MatchResult.notMatch(), this.authz))\n\t\t\t.add(new PayloadExchangeMatcherEntry<>(PayloadExchangeMatchers.anyExchange(), this.authz2))\n\t\t\t.build();\n\t\tassertThat(manager.authorize(Mono.empty(), this.exchange).block()).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/core/CaptureSecurityContextSocketAcceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport io.rsocket.ConnectionSetupPayload;\nimport io.rsocket.RSocket;\nimport io.rsocket.SocketAcceptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\n\n/**\n * A {@link SocketAcceptor} that captures the {@link SecurityContext} and then continues\n * with the {@link RSocket}\n *\n * @author Rob Winch\n */\nclass CaptureSecurityContextSocketAcceptor implements SocketAcceptor {\n\n\tprivate final RSocket accept;\n\n\tprivate SecurityContext securityContext;\n\n\tCaptureSecurityContextSocketAcceptor(RSocket accept) {\n\t\tthis.accept = accept;\n\t}\n\n\t@Override\n\tpublic Mono<RSocket> accept(ConnectionSetupPayload setup, RSocket sendingSocket) {\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t.doOnNext((securityContext) -> this.securityContext = securityContext)\n\t\t\t.thenReturn(this.accept);\n\t}\n\n\tSecurityContext getSecurityContext() {\n\t\treturn this.securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocketTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport io.rsocket.Payload;\nimport io.rsocket.RSocket;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport io.rsocket.util.ByteBufPayload;\nimport io.rsocket.util.DefaultPayload;\nimport io.rsocket.util.RSocketProxy;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.stubbing.Answer;\nimport org.reactivestreams.Publisher;\nimport org.reactivestreams.Subscription;\nimport reactor.core.CoreSubscriber;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.PublisherProbe;\nimport reactor.test.publisher.TestPublisher;\nimport reactor.util.context.Context;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadExchangeType;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\nimport org.springframework.security.rsocket.api.PayloadInterceptorChain;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class PayloadInterceptorRSocketTests {\n\n\tstatic final MimeType COMPOSITE_METADATA = MimeTypeUtils\n\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\n\t@Mock\n\tRSocket delegate;\n\n\t@Mock\n\tPayloadInterceptor interceptor;\n\n\t@Mock\n\tPayloadInterceptor interceptor2;\n\n\t@Mock\n\tPayload payload;\n\n\t@Captor\n\tprivate ArgumentCaptor<PayloadExchange> exchange;\n\n\tPublisherProbe<Void> voidResult = PublisherProbe.empty();\n\n\tTestPublisher<Payload> payloadResult = TestPublisher.createCold();\n\n\tprivate MimeType metadataMimeType = COMPOSITE_METADATA;\n\n\tprivate MimeType dataMimeType = MediaType.APPLICATION_JSON;\n\n\t@Test\n\tpublic void constructorWhenNullDelegateThenException() {\n\t\tthis.delegate = null;\n\t\tList<PayloadInterceptor> interceptors = Arrays.asList(this.interceptor);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PayloadInterceptorRSocket(this.delegate, interceptors,\n\t\t\t\tthis.metadataMimeType, this.dataMimeType));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullInterceptorsThenException() {\n\t\tList<PayloadInterceptor> interceptors = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PayloadInterceptorRSocket(this.delegate, interceptors,\n\t\t\t\tthis.metadataMimeType, this.dataMimeType));\n\t}\n\n\t@Test\n\tpublic void constructorWhenEmptyInterceptorsThenException() {\n\t\tList<PayloadInterceptor> interceptors = Collections.emptyList();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PayloadInterceptorRSocket(this.delegate, interceptors,\n\t\t\t\tthis.metadataMimeType, this.dataMimeType));\n\t}\n\n\t// single interceptor\n\t@Test\n\tpublic void fireAndForgetWhenInterceptorCompletesThenDelegateSubscribed() {\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withChainNext());\n\t\tgiven(this.delegate.fireAndForget(any())).willReturn(this.voidResult.mono());\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.fireAndForget(this.payload))\n\t\t\t.then(() -> this.voidResult.assertWasSubscribed())\n\t\t\t.verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t}\n\n\t@Test\n\tpublic void fireAndForgetWhenInterceptorErrorsThenDelegateNotSubscribed() {\n\t\tRuntimeException expected = new RuntimeException(\"Oops\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.error(expected));\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.fireAndForget(this.payload))\n\t\t\t.then(() -> this.voidResult.assertWasNotSubscribed())\n\t\t\t.verifyErrorSatisfies((e) -> assertThat(e).isEqualTo(expected));\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t}\n\n\t@Test\n\tpublic void fireAndForgetWhenSecurityContextThenDelegateContext() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withAuthenticated(authentication));\n\t\tgiven(this.delegate.fireAndForget(any())).willReturn(Mono.empty());\n\t\tRSocket assertAuthentication = new RSocketProxy(this.delegate) {\n\t\t\t@Override\n\t\t\tpublic Mono<Void> fireAndForget(Payload payload) {\n\t\t\t\treturn assertAuthentication(authentication).flatMap((a) -> super.fireAndForget(payload));\n\t\t\t}\n\t\t};\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(assertAuthentication,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tinterceptor.fireAndForget(this.payload).block();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.delegate).fireAndForget(this.payload);\n\t}\n\n\t@Test\n\tpublic void requestResponseWhenInterceptorCompletesThenDelegateSubscribed() {\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.delegate.requestResponse(any())).willReturn(this.payloadResult.mono());\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.requestResponse(this.payload))\n\t\t\t.then(() -> this.payloadResult.assertSubscribers())\n\t\t\t.then(() -> this.payloadResult.emit(this.payload))\n\t\t\t.expectNext(this.payload)\n\t\t\t.verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.delegate).requestResponse(this.payload);\n\t}\n\n\t@Test\n\tpublic void requestResponseWhenInterceptorErrorsThenDelegateNotInvoked() {\n\t\tRuntimeException expected = new RuntimeException(\"Oops\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.error(expected));\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t.isThrownBy(() -> interceptor.requestResponse(this.payload).block())\n\t\t\t.isEqualTo(expected);\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverifyNoMoreInteractions(this.delegate);\n\t}\n\n\t@Test\n\tpublic void requestResponseWhenSecurityContextThenDelegateContext() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withAuthenticated(authentication));\n\t\tgiven(this.delegate.requestResponse(any())).willReturn(this.payloadResult.mono());\n\t\tRSocket assertAuthentication = new RSocketProxy(this.delegate) {\n\t\t\t@Override\n\t\t\tpublic Mono<Payload> requestResponse(Payload payload) {\n\t\t\t\treturn assertAuthentication(authentication).flatMap((a) -> super.requestResponse(payload));\n\t\t\t}\n\t\t};\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(assertAuthentication,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.requestResponse(this.payload))\n\t\t\t.then(() -> this.payloadResult.assertSubscribers())\n\t\t\t.then(() -> this.payloadResult.emit(this.payload))\n\t\t\t.expectNext(this.payload)\n\t\t\t.verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.delegate).requestResponse(this.payload);\n\t}\n\n\t@Test\n\tpublic void requestStreamWhenInterceptorCompletesThenDelegateSubscribed() {\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.delegate.requestStream(any())).willReturn(this.payloadResult.flux());\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.requestStream(this.payload))\n\t\t\t.then(() -> this.payloadResult.assertSubscribers())\n\t\t\t.then(() -> this.payloadResult.emit(this.payload))\n\t\t\t.expectNext(this.payload)\n\t\t\t.verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t}\n\n\t@Test\n\tpublic void requestStreamWhenInterceptorErrorsThenDelegateNotSubscribed() {\n\t\tRuntimeException expected = new RuntimeException(\"Oops\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.error(expected));\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.requestStream(this.payload))\n\t\t\t.then(() -> this.payloadResult.assertNoSubscribers())\n\t\t\t.verifyErrorSatisfies((e) -> assertThat(e).isEqualTo(expected));\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t}\n\n\t@Test\n\tpublic void requestStreamWhenSecurityContextThenDelegateContext() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withAuthenticated(authentication));\n\t\tgiven(this.delegate.requestStream(any())).willReturn(this.payloadResult.flux());\n\t\tRSocket assertAuthentication = new RSocketProxy(this.delegate) {\n\t\t\t@Override\n\t\t\tpublic Flux<Payload> requestStream(Payload payload) {\n\t\t\t\treturn assertAuthentication(authentication).flatMapMany((a) -> super.requestStream(payload));\n\t\t\t}\n\t\t};\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(assertAuthentication,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.requestStream(this.payload))\n\t\t\t.then(() -> this.payloadResult.assertSubscribers())\n\t\t\t.then(() -> this.payloadResult.emit(this.payload))\n\t\t\t.expectNext(this.payload)\n\t\t\t.verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.delegate).requestStream(this.payload);\n\t}\n\n\t@Test\n\tpublic void requestChannelWhenInterceptorCompletesThenDelegateSubscribed() {\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.delegate.requestChannel(any())).willReturn(this.payloadResult.flux());\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.requestChannel(Flux.just(this.payload)))\n\t\t\t.then(() -> this.payloadResult.assertSubscribers())\n\t\t\t.then(() -> this.payloadResult.emit(this.payload))\n\t\t\t.expectNext(this.payload)\n\t\t\t.verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.delegate).requestChannel(any());\n\t}\n\n\t// gh-9345\n\t@Test\n\tpublic void requestChannelWhenInterceptorCompletesThenAllPayloadsRetained() {\n\t\tExecutorService executors = Executors.newSingleThreadExecutor();\n\t\tPayload payload = ByteBufPayload.create(\"data\");\n\t\tPayload payloadTwo = ByteBufPayload.create(\"moredata\");\n\t\tPayload payloadThree = ByteBufPayload.create(\"stillmoredata\");\n\t\tContext ctx = Context.empty();\n\t\tFlux<Payload> payloads = this.payloadResult.flux();\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.empty())\n\t\t\t.willReturn(Mono.error(() -> new AccessDeniedException(\"Access Denied\")));\n\t\tgiven(this.delegate.requestChannel(any())).willAnswer((invocation) -> {\n\t\t\tFlux<Payload> input = invocation.getArgument(0);\n\t\t\treturn Flux.from(input)\n\t\t\t\t.switchOnFirst((signal, innerFlux) -> innerFlux.map(Payload::getDataUtf8)\n\t\t\t\t\t.transform((data) -> Flux.<String>create((emitter) -> {\n\t\t\t\t\t\tRunnable run = () -> data.subscribe(new CoreSubscriber<String>() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void onSubscribe(Subscription s) {\n\t\t\t\t\t\t\t\ts.request(3);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void onNext(String s) {\n\t\t\t\t\t\t\t\temitter.next(s);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void onError(Throwable t) {\n\t\t\t\t\t\t\t\temitter.error(t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void onComplete() {\n\t\t\t\t\t\t\t\temitter.complete();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\texecutors.execute(run);\n\t\t\t\t\t}))\n\t\t\t\t\t.map(DefaultPayload::create));\n\t\t});\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType, ctx);\n\t\tStepVerifier.create(interceptor.requestChannel(payloads).doOnDiscard(Payload.class, Payload::release))\n\t\t\t.then(() -> this.payloadResult.assertSubscribers())\n\t\t\t.then(() -> this.payloadResult.emit(payload, payloadTwo, payloadThree))\n\t\t\t.assertNext((next) -> assertThat(next.getDataUtf8()).isEqualTo(payload.getDataUtf8()))\n\t\t\t.verifyError(AccessDeniedException.class);\n\t\tverify(this.interceptor, times(2)).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(payloadTwo);\n\t\tverify(this.delegate).requestChannel(any());\n\t}\n\n\t@Test\n\tpublic void requestChannelWhenInterceptorErrorsThenDelegateNotSubscribed() {\n\t\tRuntimeException expected = new RuntimeException(\"Oops\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.error(expected));\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.requestChannel(Flux.just(this.payload)))\n\t\t\t.then(() -> this.payloadResult.assertNoSubscribers())\n\t\t\t.verifyErrorSatisfies((e) -> assertThat(e).isEqualTo(expected));\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t}\n\n\t@Test\n\tpublic void requestChannelWhenSecurityContextThenDelegateContext() {\n\t\tMono<Payload> payload = Mono.just(this.payload);\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withAuthenticated(authentication));\n\t\tgiven(this.delegate.requestChannel(any())).willReturn(this.payloadResult.flux());\n\t\tRSocket assertAuthentication = new RSocketProxy(this.delegate) {\n\t\t\t@Override\n\t\t\tpublic Flux<Payload> requestChannel(Publisher<Payload> payload) {\n\t\t\t\treturn assertAuthentication(authentication).flatMapMany((a) -> super.requestChannel(payload));\n\t\t\t}\n\t\t};\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(assertAuthentication,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.requestChannel(payload))\n\t\t\t.then(() -> this.payloadResult.assertSubscribers())\n\t\t\t.then(() -> this.payloadResult.emit(this.payload))\n\t\t\t.expectNext(this.payload)\n\t\t\t.verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.delegate).requestChannel(any());\n\t}\n\n\t@Test\n\tpublic void metadataPushWhenInterceptorCompletesThenDelegateSubscribed() {\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.delegate.metadataPush(any())).willReturn(this.voidResult.mono());\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.metadataPush(this.payload))\n\t\t\t.then(() -> this.voidResult.assertWasSubscribed())\n\t\t\t.verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t}\n\n\t@Test\n\tpublic void metadataPushWhenInterceptorErrorsThenDelegateNotSubscribed() {\n\t\tRuntimeException expected = new RuntimeException(\"Oops\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.error(expected));\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.metadataPush(this.payload))\n\t\t\t.then(() -> this.voidResult.assertWasNotSubscribed())\n\t\t\t.verifyErrorSatisfies((e) -> assertThat(e).isEqualTo(expected));\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t}\n\n\t@Test\n\tpublic void metadataPushWhenSecurityContextThenDelegateContext() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withAuthenticated(authentication));\n\t\tgiven(this.delegate.metadataPush(any())).willReturn(this.voidResult.mono());\n\t\tRSocket assertAuthentication = new RSocketProxy(this.delegate) {\n\t\t\t@Override\n\t\t\tpublic Mono<Void> metadataPush(Payload payload) {\n\t\t\t\treturn assertAuthentication(authentication).flatMap((a) -> super.metadataPush(payload));\n\t\t\t}\n\t\t};\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(assertAuthentication,\n\t\t\t\tArrays.asList(this.interceptor), this.metadataMimeType, this.dataMimeType);\n\t\tStepVerifier.create(interceptor.metadataPush(this.payload)).verifyComplete();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.delegate).metadataPush(this.payload);\n\t\tthis.voidResult.assertWasSubscribed();\n\t}\n\n\t// multiple interceptors\n\t@Test\n\tpublic void fireAndForgetWhenInterceptorsCompleteThenDelegateInvoked() {\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withChainNext());\n\t\tgiven(this.interceptor2.intercept(any(), any())).willAnswer(withChainNext());\n\t\tgiven(this.delegate.fireAndForget(any())).willReturn(this.voidResult.mono());\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor, this.interceptor2), this.metadataMimeType, this.dataMimeType);\n\t\tinterceptor.fireAndForget(this.payload).block();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tthis.voidResult.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void fireAndForgetWhenInterceptorsMutatesPayloadThenDelegateInvoked() {\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withChainNext());\n\t\tgiven(this.interceptor2.intercept(any(), any())).willAnswer(withChainNext());\n\t\tgiven(this.delegate.fireAndForget(any())).willReturn(this.voidResult.mono());\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor, this.interceptor2), this.metadataMimeType, this.dataMimeType);\n\t\tinterceptor.fireAndForget(this.payload).block();\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.interceptor2).intercept(any(), any());\n\t\tverify(this.delegate).fireAndForget(eq(this.payload));\n\t\tthis.voidResult.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void fireAndForgetWhenInterceptor1ErrorsThenInterceptor2AndDelegateNotInvoked() {\n\t\tRuntimeException expected = new RuntimeException(\"Oops\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.error(expected));\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor, this.interceptor2), this.metadataMimeType, this.dataMimeType);\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t.isThrownBy(() -> interceptor.fireAndForget(this.payload).block())\n\t\t\t.isEqualTo(expected);\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverifyNoMoreInteractions(this.interceptor2);\n\t\tthis.voidResult.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void fireAndForgetWhenInterceptor2ErrorsThenInterceptor2AndDelegateNotInvoked() {\n\t\tRuntimeException expected = new RuntimeException(\"Oops\");\n\t\tgiven(this.interceptor.intercept(any(), any())).willAnswer(withChainNext());\n\t\tgiven(this.interceptor2.intercept(any(), any())).willReturn(Mono.error(expected));\n\t\tPayloadInterceptorRSocket interceptor = new PayloadInterceptorRSocket(this.delegate,\n\t\t\t\tArrays.asList(this.interceptor, this.interceptor2), this.metadataMimeType, this.dataMimeType);\n\t\tassertThatExceptionOfType(RuntimeException.class)\n\t\t\t.isThrownBy(() -> interceptor.fireAndForget(this.payload).block())\n\t\t\t.isEqualTo(expected);\n\t\tverify(this.interceptor).intercept(this.exchange.capture(), any());\n\t\tassertThat(this.exchange.getValue().getPayload()).isEqualTo(this.payload);\n\t\tverify(this.interceptor2).intercept(any(), any());\n\t\tthis.voidResult.assertWasNotSubscribed();\n\t}\n\n\tprivate Mono<Authentication> assertAuthentication(Authentication authentication) {\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t.doOnNext((a) -> assertThat(a).isEqualTo(authentication));\n\t}\n\n\tprivate Answer<Object> withAuthenticated(Authentication authentication) {\n\t\treturn (invocation) -> {\n\t\t\tPayloadInterceptorChain c = (PayloadInterceptorChain) invocation.getArguments()[1];\n\t\t\treturn c\n\t\t\t\t.next(new DefaultPayloadExchange(PayloadExchangeType.REQUEST_CHANNEL, this.payload,\n\t\t\t\t\t\tthis.metadataMimeType, this.dataMimeType))\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));\n\t\t};\n\t}\n\n\tprivate static Answer<Mono<Void>> withChainNext() {\n\t\treturn (invocation) -> {\n\t\t\tPayloadExchange exchange = (PayloadExchange) invocation.getArguments()[0];\n\t\t\tPayloadInterceptorChain chain = (PayloadInterceptorChain) invocation.getArguments()[1];\n\t\t\treturn chain.next(exchange);\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/core/PayloadSocketAcceptorInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport io.rsocket.ConnectionSetupPayload;\nimport io.rsocket.Payload;\nimport io.rsocket.RSocket;\nimport io.rsocket.SocketAcceptor;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class PayloadSocketAcceptorInterceptorTests {\n\n\t@Mock\n\tprivate PayloadInterceptor interceptor;\n\n\t@Mock\n\tprivate SocketAcceptor socketAcceptor;\n\n\t@Mock\n\tprivate ConnectionSetupPayload setupPayload;\n\n\t@Mock\n\tprivate RSocket rSocket;\n\n\t@Mock\n\tprivate Payload payload;\n\n\tprivate List<PayloadInterceptor> interceptors;\n\n\tprivate PayloadSocketAcceptorInterceptor acceptorInterceptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.interceptors = Arrays.asList(this.interceptor);\n\t\tthis.acceptorInterceptor = new PayloadSocketAcceptorInterceptor(this.interceptors);\n\t}\n\n\t@Test\n\tpublic void applyWhenDefaultMetadataMimeTypeThenDefaulted() {\n\t\tgiven(this.setupPayload.dataMimeType()).willReturn(MediaType.APPLICATION_JSON_VALUE);\n\t\tPayloadExchange exchange = captureExchange();\n\t\tassertThat(exchange.getMetadataMimeType().toString())\n\t\t\t.isEqualTo(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\t\tassertThat(exchange.getDataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t}\n\n\t@Test\n\tpublic void acceptWhenDefaultMetadataMimeTypeOverrideThenDefaulted() {\n\t\tthis.acceptorInterceptor.setDefaultMetadataMimeType(MediaType.APPLICATION_JSON);\n\t\tgiven(this.setupPayload.dataMimeType()).willReturn(MediaType.APPLICATION_JSON_VALUE);\n\t\tPayloadExchange exchange = captureExchange();\n\t\tassertThat(exchange.getMetadataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t\tassertThat(exchange.getDataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t}\n\n\t@Test\n\tpublic void acceptWhenDefaultDataMimeTypeThenDefaulted() {\n\t\tthis.acceptorInterceptor.setDefaultDataMimeType(MediaType.APPLICATION_JSON);\n\t\tPayloadExchange exchange = captureExchange();\n\t\tassertThat(exchange.getMetadataMimeType().toString())\n\t\t\t.isEqualTo(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\t\tassertThat(exchange.getDataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t}\n\n\tprivate PayloadExchange captureExchange() {\n\t\tgiven(this.socketAcceptor.accept(any(), any())).willReturn(Mono.just(this.rSocket));\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.empty());\n\t\tSocketAcceptor wrappedAcceptor = this.acceptorInterceptor.apply(this.socketAcceptor);\n\t\tRSocket result = wrappedAcceptor.accept(this.setupPayload, this.rSocket).block();\n\t\tassertThat(result).isInstanceOf(PayloadInterceptorRSocket.class);\n\t\tgiven(this.rSocket.fireAndForget(any())).willReturn(Mono.empty());\n\t\tresult.fireAndForget(this.payload).block();\n\t\tArgumentCaptor<PayloadExchange> exchangeArg = ArgumentCaptor.forClass(PayloadExchange.class);\n\t\tverify(this.interceptor, times(2)).intercept(exchangeArg.capture(), any());\n\t\treturn exchangeArg.getValue();\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/core/PayloadSocketAcceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.core;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport io.rsocket.ConnectionSetupPayload;\nimport io.rsocket.Payload;\nimport io.rsocket.RSocket;\nimport io.rsocket.SocketAcceptor;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadInterceptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class PayloadSocketAcceptorTests {\n\n\tprivate PayloadSocketAcceptor acceptor;\n\n\tprivate List<PayloadInterceptor> interceptors;\n\n\t@Mock\n\tprivate SocketAcceptor delegate;\n\n\t@Mock\n\tprivate PayloadInterceptor interceptor;\n\n\t@Mock\n\tprivate ConnectionSetupPayload setupPayload;\n\n\t@Mock\n\tprivate RSocket rSocket;\n\n\t@Mock\n\tprivate Payload payload;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.interceptors = Arrays.asList(this.interceptor);\n\t\tthis.acceptor = new PayloadSocketAcceptor(this.delegate, this.interceptors);\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullDelegateThenException() {\n\t\tthis.delegate = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new PayloadSocketAcceptor(this.delegate, this.interceptors));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullInterceptorsThenException() {\n\t\tthis.interceptors = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new PayloadSocketAcceptor(this.delegate, this.interceptors));\n\t}\n\n\t@Test\n\tpublic void constructorWhenEmptyInterceptorsThenException() {\n\t\tthis.interceptors = Collections.emptyList();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new PayloadSocketAcceptor(this.delegate, this.interceptors));\n\t}\n\n\t@Test\n\tpublic void acceptWhenDataMimeTypeNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.acceptor.accept(this.setupPayload, this.rSocket).block());\n\t}\n\n\t@Test\n\tpublic void acceptWhenDefaultMetadataMimeTypeThenDefaulted() {\n\t\tgiven(this.setupPayload.dataMimeType()).willReturn(MediaType.APPLICATION_JSON_VALUE);\n\t\tPayloadExchange exchange = captureExchange();\n\t\tassertThat(exchange.getMetadataMimeType().toString())\n\t\t\t.isEqualTo(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\t\tassertThat(exchange.getDataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t}\n\n\t@Test\n\tpublic void acceptWhenDefaultMetadataMimeTypeOverrideThenDefaulted() {\n\t\tthis.acceptor.setDefaultMetadataMimeType(MediaType.APPLICATION_JSON);\n\t\tgiven(this.setupPayload.dataMimeType()).willReturn(MediaType.APPLICATION_JSON_VALUE);\n\t\tPayloadExchange exchange = captureExchange();\n\t\tassertThat(exchange.getMetadataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t\tassertThat(exchange.getDataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t}\n\n\t@Test\n\tpublic void acceptWhenDefaultDataMimeTypeThenDefaulted() {\n\t\tthis.acceptor.setDefaultDataMimeType(MediaType.APPLICATION_JSON);\n\t\tPayloadExchange exchange = captureExchange();\n\t\tassertThat(exchange.getMetadataMimeType().toString())\n\t\t\t.isEqualTo(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\t\tassertThat(exchange.getDataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t}\n\n\t@Test\n\tpublic void acceptWhenExplicitMimeTypeThenThenOverrideDefault() {\n\t\tgiven(this.setupPayload.metadataMimeType()).willReturn(MediaType.TEXT_PLAIN_VALUE);\n\t\tgiven(this.setupPayload.dataMimeType()).willReturn(MediaType.APPLICATION_JSON_VALUE);\n\t\tPayloadExchange exchange = captureExchange();\n\t\tassertThat(exchange.getMetadataMimeType()).isEqualTo(MediaType.TEXT_PLAIN);\n\t\tassertThat(exchange.getDataMimeType()).isEqualTo(MediaType.APPLICATION_JSON);\n\t}\n\n\t@Test\n\t// gh-8654\n\tpublic void acceptWhenDelegateAcceptRequiresReactiveSecurityContext() {\n\t\tgiven(this.setupPayload.metadataMimeType()).willReturn(MediaType.TEXT_PLAIN_VALUE);\n\t\tgiven(this.setupPayload.dataMimeType()).willReturn(MediaType.APPLICATION_JSON_VALUE);\n\t\tSecurityContext expectedSecurityContext = new SecurityContextImpl(\n\t\t\t\tnew TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tCaptureSecurityContextSocketAcceptor captureSecurityContext = new CaptureSecurityContextSocketAcceptor(\n\t\t\t\tthis.rSocket);\n\t\tPayloadInterceptor authenticateInterceptor = (exchange, chain) -> {\n\t\t\tContext withSecurityContext = ReactiveSecurityContextHolder\n\t\t\t\t.withSecurityContext(Mono.just(expectedSecurityContext));\n\t\t\treturn chain.next(exchange).contextWrite(withSecurityContext);\n\t\t};\n\t\tList<PayloadInterceptor> interceptors = Arrays.asList(authenticateInterceptor);\n\t\tthis.acceptor = new PayloadSocketAcceptor(captureSecurityContext, interceptors);\n\t\tthis.acceptor.accept(this.setupPayload, this.rSocket).block();\n\t\tassertThat(captureSecurityContext.getSecurityContext()).isEqualTo(expectedSecurityContext);\n\t}\n\n\tprivate PayloadExchange captureExchange() {\n\t\tgiven(this.delegate.accept(any(), any())).willReturn(Mono.just(this.rSocket));\n\t\tgiven(this.interceptor.intercept(any(), any())).willReturn(Mono.empty());\n\t\tRSocket result = this.acceptor.accept(this.setupPayload, this.rSocket).block();\n\t\tassertThat(result).isInstanceOf(PayloadInterceptorRSocket.class);\n\t\tgiven(this.rSocket.fireAndForget(any())).willReturn(Mono.empty());\n\t\tresult.fireAndForget(this.payload).block();\n\t\tArgumentCaptor<PayloadExchange> exchangeArg = ArgumentCaptor.forClass(PayloadExchange.class);\n\t\tverify(this.interceptor, times(2)).intercept(exchangeArg.capture(), any());\n\t\treturn exchangeArg.getValue();\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.metadata;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DefaultDataBufferFactory;\nimport org.springframework.util.MimeType;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n */\npublic class BasicAuthenticationDecoderTests {\n\n\t@Test\n\tpublic void basicAuthenticationWhenEncodedThenDecodes() {\n\t\tBasicAuthenticationEncoder encoder = new BasicAuthenticationEncoder();\n\t\tBasicAuthenticationDecoder decoder = new BasicAuthenticationDecoder();\n\t\tUsernamePasswordMetadata expectedCredentials = new UsernamePasswordMetadata(\"rob\", \"password\");\n\t\tDefaultDataBufferFactory factory = new DefaultDataBufferFactory();\n\t\tResolvableType elementType = ResolvableType.forClass(UsernamePasswordMetadata.class);\n\t\tMimeType mimeType = UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE;\n\t\tMap<String, Object> hints = null;\n\t\tDataBuffer dataBuffer = encoder.encodeValue(expectedCredentials, factory, elementType, mimeType, hints);\n\t\tUsernamePasswordMetadata actualCredentials = decoder\n\t\t\t.decodeToMono(Mono.just(dataBuffer), elementType, mimeType, hints)\n\t\t\t.block();\n\t\tassertThat(actualCredentials).usingRecursiveComparison().isEqualTo(expectedCredentials);\n\t}\n\n}\n"
  },
  {
    "path": "rsocket/src/test/java/org/springframework/security/rsocket/util/matcher/RoutePayloadExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.rsocket.util.matcher;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport io.rsocket.Payload;\nimport io.rsocket.metadata.WellKnownMimeType;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.messaging.rsocket.MetadataExtractor;\nimport org.springframework.security.rsocket.api.PayloadExchange;\nimport org.springframework.security.rsocket.api.PayloadExchangeType;\nimport org.springframework.security.rsocket.core.DefaultPayloadExchange;\nimport org.springframework.util.MimeType;\nimport org.springframework.util.MimeTypeUtils;\nimport org.springframework.util.RouteMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class RoutePayloadExchangeMatcherTests {\n\n\tstatic final MimeType COMPOSITE_METADATA = MimeTypeUtils\n\t\t.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString());\n\n\t@Mock\n\tprivate MetadataExtractor metadataExtractor;\n\n\t@Mock\n\tprivate RouteMatcher routeMatcher;\n\n\tprivate PayloadExchange exchange;\n\n\t@Mock\n\tprivate Payload payload;\n\n\t@Mock\n\tprivate RouteMatcher.Route route;\n\n\tprivate String pattern;\n\n\tprivate RoutePayloadExchangeMatcher matcher;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.pattern = \"a.b\";\n\t\tthis.matcher = new RoutePayloadExchangeMatcher(this.metadataExtractor, this.routeMatcher, this.pattern);\n\t\tthis.exchange = new DefaultPayloadExchange(PayloadExchangeType.REQUEST_CHANNEL, this.payload,\n\t\t\t\tCOMPOSITE_METADATA, MediaType.APPLICATION_JSON);\n\t}\n\n\t@Test\n\tpublic void matchesWhenNoRouteThenNotMatch() {\n\t\tgiven(this.metadataExtractor.extract(any(), any())).willReturn(Collections.emptyMap());\n\t\tPayloadExchangeMatcher.MatchResult result = this.matcher.matches(this.exchange).block();\n\t\tassertThat(result.isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenNotMatchThenNotMatch() {\n\t\tString route = \"route\";\n\t\tgiven(this.metadataExtractor.extract(any(), any()))\n\t\t\t.willReturn(Collections.singletonMap(MetadataExtractor.ROUTE_KEY, route));\n\t\tPayloadExchangeMatcher.MatchResult result = this.matcher.matches(this.exchange).block();\n\t\tassertThat(result.isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenMatchAndNoVariablesThenMatch() {\n\t\tString route = \"route\";\n\t\tgiven(this.metadataExtractor.extract(any(), any()))\n\t\t\t.willReturn(Collections.singletonMap(MetadataExtractor.ROUTE_KEY, route));\n\t\tgiven(this.routeMatcher.parseRoute(any())).willReturn(this.route);\n\t\tgiven(this.routeMatcher.matchAndExtract(any(), any())).willReturn(Collections.emptyMap());\n\t\tPayloadExchangeMatcher.MatchResult result = this.matcher.matches(this.exchange).block();\n\t\tassertThat(result.isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenMatchAndVariablesThenMatchAndVariables() {\n\t\tString route = \"route\";\n\t\tMap<String, String> variables = Collections.singletonMap(\"a\", \"b\");\n\t\tgiven(this.metadataExtractor.extract(any(), any()))\n\t\t\t.willReturn(Collections.singletonMap(MetadataExtractor.ROUTE_KEY, route));\n\t\tgiven(this.routeMatcher.parseRoute(any())).willReturn(this.route);\n\t\tgiven(this.routeMatcher.matchAndExtract(any(), any())).willReturn(variables);\n\t\tPayloadExchangeMatcher.MatchResult result = this.matcher.matches(this.exchange).block();\n\t\tassertThat(result.isMatch()).isTrue();\n\t\tassertThat(result.getVariables()).containsAllEntriesOf(variables);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle",
    "content": "plugins {\n\tid 'compile-warnings-error'\n\tid 'javadoc-warnings-error'\n\tid 'security-nullability'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\nconfigurations {\n\topensamlFiveMain { extendsFrom(optional, provided) }\n\topensamlFiveTest { extendsFrom(opensamlFiveMain, testImplementation, testRuntimeOnly) }\n}\n\nsourceSets {\n\topensaml5Main {\n\t\tjava {\n\t\t\tcompileClasspath = main.output + configurations.opensamlFiveMain\n\t\t\tsrcDir 'src/opensaml5Main/java'\n\t\t}\n\t}\n\topensaml5Test {\n\t\tjava {\n\t\t\tcompileClasspath = main.output + test.output + opensaml5Main.output + configurations.opensamlFiveTest\n\t\t\truntimeClasspath = main.output + test.output + opensaml5Main.output + configurations.opensamlFiveTest\n\t\t\tsrcDir 'src/opensaml5Test/java'\n\t\t}\n\t}\n}\n\nsourceSets.configureEach { set ->\n\tif (!set.name.containsIgnoreCase(\"main\")) {\n\t\treturn\n\t}\n\tdef from = copySpec {\n\t\tfrom(\"$projectDir/src/$set.name/java/org/springframework/security/saml2/internal\")\n\t}\n\n\tcopy {\n\t\tinto \"$projectDir/src/$set.name/java/org/springframework/security/saml2/provider/service/authentication/logout\"\n\t\tfilter { line -> line.replaceAll(\".saml2.internal\", \".saml2.provider.service.authentication.logout\") }\n\t\twith from\n\t}\n\n\tcopy {\n\t\tinto \"$projectDir/src/$set.name/java/org/springframework/security/saml2/provider/service/authentication\"\n\t\tfilter { line -> line.replaceAll(\".saml2.internal\", \".saml2.provider.service.authentication\") }\n\t\twith from\n\t}\n\n\tcopy {\n\t\tinto \"$projectDir/src/$set.name/java/org/springframework/security/saml2/provider/service/metadata\"\n\t\tfilter { line -> line.replaceAll(\".saml2.internal\", \".saml2.provider.service.metadata\") }\n\t\twith from\n\t}\n\n\tcopy {\n\t\tinto \"$projectDir/src/$set.name/java/org/springframework/security/saml2/provider/service/web/authentication/logout\"\n\t\tfilter { line -> line.replaceAll(\".saml2.internal\", \".saml2.provider.service.web.authentication.logout\") }\n\t\twith from\n\t}\n\n\tcopy {\n\t\tinto \"$projectDir/src/$set.name/java/org/springframework/security/saml2/provider/service/web/authentication\"\n\t\tfilter { line -> line.replaceAll(\".saml2.internal\", \".saml2.provider.service.web.authentication\") }\n\t\twith from\n\t}\n\n\tcopy {\n\t\tinto \"$projectDir/src/$set.name/java/org/springframework/security/saml2/provider/service/web\"\n\t\tfilter { line -> line.replaceAll(\".saml2.internal\", \".saml2.provider.service.web\") }\n\t\twith from\n\t}\n\n\tcopy {\n\t\tinto \"$projectDir/src/$set.name/java/org/springframework/security/saml2/provider/service/registration\"\n\t\tfilter { line -> line.replaceAll(\".saml2.internal\", \".saml2.provider.service.registration\") }\n\t\twith from\n\t}\n}\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-web')\n\n\tapi ('org.opensaml:opensaml-saml-api')  {\n\t\texclude group: 'commons-logging', module: 'commons-logging'\n\t}\n\tapi ('org.opensaml:opensaml-saml-impl')  {\n\t\texclude group: 'commons-logging', module: 'commons-logging'\n\t}\n\n\topensamlFiveMain (libs.org.opensaml.opensaml5.saml.api)  {\n\t\texclude group: 'commons-logging', module: 'commons-logging'\n\t}\n\topensamlFiveMain (libs.org.opensaml.opensaml5.saml.api)  {\n\t\texclude group: 'commons-logging', module: 'commons-logging'\n\t}\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\toptional 'org.springframework:spring-jdbc'\n\toptional 'tools.jackson.core:jackson-databind'\n\n\ttestImplementation project(path: ':spring-security-web', configuration: 'tests')\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.skyscreamer:jsonassert\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\topensamlFiveMain \"org.apiguardian:apiguardian-api:1.1.2\"\n}\n\njar {\n\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n\tfrom sourceSets.opensaml5Main.output\n}\n\nsourcesJar {\n\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n\tfrom sourceSets.opensaml5Main.allJava\n}\n\ntestJar {\n\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n\tfrom sourceSets.opensaml5Test.output\n}\n\njavadoc {\n\tclasspath += configurations.opensamlFiveMain\n\tsource = sourceSets.main.allJava + sourceSets.opensaml5Main.allJava\n}\n\ntasks.named(\"compileOpensaml5MainJava\") {\n\toptions.nullability.checking = \"main\"\n}\n\ntasks.register(\"opensaml5Test\", Test) {\n\tuseJUnitPlatform()\n\ttestClassesDirs = sourceSets.opensaml5Test.output.classesDirs\n\tclasspath = sourceSets.opensaml5Test.output + sourceSets.opensaml5Test.runtimeClasspath\n}\n\ntasks.named(\"test\") {\n\tdependsOn opensaml5Test\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/Saml2Exception.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2;\n\nimport java.io.Serial;\n\n/**\n * @since 5.2\n */\npublic class Saml2Exception extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 6076252564189633016L;\n\n\tpublic Saml2Exception(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic Saml2Exception(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic Saml2Exception(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/aot/hint/Saml2RuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.aot.hint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.util.ClassUtils;\n\n/**\n * {@link RuntimeHintsRegistrar} for SAML2 Service Provider classes.\n *\n * @author Josh Long\n */\nclass Saml2RuntimeHints implements RuntimeHintsRegistrar {\n\n\tprivate static final boolean jackson2Present;\n\n\tprivate static final boolean jackson3Present;\n\n\tstatic {\n\t\tClassLoader classLoader = ClassUtils.getDefaultClassLoader();\n\t\tjackson2Present = ClassUtils.isPresent(\"com.fasterxml.jackson.databind.ObjectMapper\", classLoader)\n\t\t\t\t&& ClassUtils.isPresent(\"com.fasterxml.jackson.core.JsonGenerator\", classLoader);\n\t\tjackson3Present = ClassUtils.isPresent(\"tools.jackson.databind.json.JsonMapper\", classLoader);\n\t}\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\tregisterAuthenticationHints(hints);\n\t\tregisterJacksonHints(hints);\n\t\tregisterJdbcSchemaHints(hints);\n\t}\n\n\tprivate void registerAuthenticationHints(RuntimeHints hints) {\n\t\thints.reflection()\n\t\t\t.registerTypes(\n\t\t\t\t\tjava.util.List.of(TypeReference.of(Saml2Authentication.class),\n\t\t\t\t\t\t\tTypeReference.of(Saml2AssertionAuthentication.class),\n\t\t\t\t\t\t\tTypeReference.of(DefaultSaml2AuthenticatedPrincipal.class),\n\t\t\t\t\t\t\tTypeReference.of(Saml2PostAuthenticationRequest.class),\n\t\t\t\t\t\t\tTypeReference.of(Saml2RedirectAuthenticationRequest.class),\n\t\t\t\t\t\t\tTypeReference.of(Saml2ResponseAssertion.class), TypeReference.of(Saml2LogoutRequest.class),\n\t\t\t\t\t\t\tTypeReference.of(Saml2Error.class), TypeReference.of(Saml2AuthenticationException.class)),\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));\n\t}\n\n\tprivate void registerJacksonHints(RuntimeHints hints) {\n\t\t// Jackson 2 Module\n\t\tif (jackson2Present) {\n\t\t\t// Register mixins for Jackson 2\n\t\t\tregisterJackson2Mixins(hints);\n\t\t}\n\n\t\t// Jackson 3 Module\n\t\tif (jackson3Present) {\n\t\t\t// Register mixins for Jackson 3\n\t\t\tregisterJackson3Mixins(hints);\n\t\t}\n\t}\n\n\tprivate void registerJackson2Mixins(RuntimeHints hints) {\n\t\tString[] mixinClasses = { \"org.springframework.security.saml2.jackson2.Saml2AuthenticationMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.Saml2JacksonModule\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.Saml2AssertionAuthenticationMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.SimpleSaml2ResponseAssertionAccessorMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.DefaultSaml2AuthenticatedPrincipalMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.Saml2LogoutRequestMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.Saml2RedirectAuthenticationRequestMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.Saml2PostAuthenticationRequestMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.Saml2ErrorMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson2.Saml2AuthenticationExceptionMixin\" };\n\n\t\tfor (String mixinClass : mixinClasses) {\n\t\t\thints.reflection()\n\t\t\t\t.registerType(TypeReference.of(mixinClass),\n\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));\n\t\t}\n\t}\n\n\tprivate void registerJackson3Mixins(RuntimeHints hints) {\n\t\tString[] mixinClasses = { \"org.springframework.security.saml2.jackson.Saml2AuthenticationMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.Saml2AssertionAuthenticationMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.Saml2JacksonModule\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.SimpleSaml2ResponseAssertionAccessorMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.DefaultSaml2AuthenticatedPrincipalMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.Saml2LogoutRequestMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.Saml2RedirectAuthenticationRequestMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.Saml2PostAuthenticationRequestMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.Saml2ErrorMixin\",\n\t\t\t\t\"org.springframework.security.saml2.jackson.Saml2AuthenticationExceptionMixin\" };\n\n\t\tfor (String mixinClass : mixinClasses) {\n\t\t\thints.reflection()\n\t\t\t\t.registerType(TypeReference.of(mixinClass),\n\t\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));\n\t\t}\n\t}\n\n\tprivate void registerJdbcSchemaHints(RuntimeHints hints) {\n\t\thints.resources()\n\t\t\t.registerPattern(\"org/springframework/security/saml2/saml2-asserting-party-metadata-schema.sql\")\n\t\t\t.registerPattern(\"org/springframework/security/saml2/saml2-asserting-party-metadata-schema-postgres.sql\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * AOT and native image hint support for SAML2.\n */\n@NullMarked\npackage org.springframework.security.saml2.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/OpenSamlInitializationService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.opensaml.core.config.ConfigurationService;\nimport org.opensaml.core.config.InitializationService;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistry;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * An initialization service for initializing OpenSAML. Each Spring Security\n * OpenSAML-based component invokes the {@link #initialize()} method at static\n * initialization time.\n *\n * {@link #initialize()} is idempotent and may be safely called in custom classes that\n * need OpenSAML to be initialized in order to function correctly. It's recommended that\n * you call this {@link #initialize()} method when using Spring Security and OpenSAML\n * instead of OpenSAML's {@link InitializationService#initialize()}.\n *\n * The primary purpose of {@link #initialize()} is to prepare OpenSAML's\n * {@link XMLObjectProviderRegistry} with some reasonable defaults. Any changes that\n * Spring Security makes to the registry happen in this method.\n *\n * To override those defaults, call {@link #requireInitialize(Consumer)} and change the\n * registry:\n *\n * <pre>\n * \tstatic {\n *  \tOpenSamlInitializationService.requireInitialize((registry) -&gt; {\n *  \t \tregistry.setParserPool(...);\n *  \t\tregistry.getBuilderFactory().registerBuilder(...);\n *  \t});\n *  }\n * </pre>\n *\n * {@link #requireInitialize(Consumer)} may only be called once per application.\n *\n * If the application already initialized OpenSAML before\n * {@link #requireInitialize(Consumer)} was called, then the configuration changes will\n * not be applied and an exception will be thrown. The reason for this is to alert you to\n * the fact that there are likely some initialization ordering problems in your\n * application that would otherwise lead to an unpredictable state.\n *\n * If you must change the registry's configuration in multiple places in your application,\n * you are expected to handle the initialization ordering issues yourself instead of\n * trying to call {@link #requireInitialize(Consumer)} multiple times.\n *\n * @author Josh Cummings\n * @since 5.4\n */\npublic final class OpenSamlInitializationService {\n\n\tprivate static final Log log = LogFactory.getLog(OpenSamlInitializationService.class);\n\n\tprivate static final AtomicBoolean initialized = new AtomicBoolean(false);\n\n\tprivate OpenSamlInitializationService() {\n\t}\n\n\t/**\n\t * Ready OpenSAML for use and configure it with reasonable defaults.\n\t *\n\t * Initialization is guaranteed to happen only once per application. This method will\n\t * passively return {@code false} if initialization already took place earlier in the\n\t * application.\n\t * @return whether or not initialization was performed. The first thread to initialize\n\t * OpenSAML will return {@code true} while the rest will return {@code false}.\n\t * @throws Saml2Exception if OpenSAML failed to initialize\n\t */\n\tpublic static boolean initialize() {\n\t\treturn initialize((registry) -> {\n\t\t});\n\t}\n\n\t/**\n\t * Ready OpenSAML for use, configure it with reasonable defaults, and modify the\n\t * {@link XMLObjectProviderRegistry} using the provided {@link Consumer}.\n\t *\n\t * Initialization is guaranteed to happen only once per application. This method will\n\t * throw an exception if initialization already took place earlier in the application.\n\t * @param registryConsumer the {@link Consumer} to further configure the\n\t * {@link XMLObjectProviderRegistry}\n\t * @throws Saml2Exception if initialization already happened previously or if OpenSAML\n\t * failed to initialize\n\t */\n\tpublic static void requireInitialize(Consumer<XMLObjectProviderRegistry> registryConsumer) {\n\t\tif (!initialize(registryConsumer)) {\n\t\t\tthrow new Saml2Exception(\"OpenSAML was already initialized previously\");\n\t\t}\n\t}\n\n\tprivate static boolean initialize(Consumer<XMLObjectProviderRegistry> registryConsumer) {\n\t\tif (initialized.compareAndSet(false, true)) {\n\t\t\tlog.trace(\"Initializing OpenSAML\");\n\t\t\ttry {\n\t\t\t\tInitializationService.initialize();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\tregistryConsumer.accept(ConfigurationService.get(XMLObjectProviderRegistry.class));\n\t\t\tlog.debug(\"Initialized OpenSAML\");\n\t\t\treturn true;\n\t\t}\n\t\tlog.debug(\"Refused to re-initialize OpenSAML\");\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2Error.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A representation of an SAML 2.0 Error.\n *\n * <p>\n * At a minimum, an error response will contain an error code. The commonly used error\n * code are defined in this class or a new codes can be defined in the future as arbitrary\n * strings.\n * </p>\n *\n * @since 5.2\n */\npublic class Saml2Error implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String errorCode;\n\n\tprivate final @Nullable String description;\n\n\t/**\n\t * Constructs a {@code Saml2Error} using the provided parameters.\n\t * @param errorCode the error code\n\t * @param description the error description\n\t */\n\tpublic Saml2Error(String errorCode, @Nullable String description) {\n\t\tAssert.hasText(errorCode, \"errorCode cannot be empty\");\n\t\tthis.errorCode = errorCode;\n\t\tthis.description = description;\n\t}\n\n\t/**\n\t * Construct an {@link Saml2ErrorCodes#INVALID_RESPONSE} error\n\t * @param description the error description\n\t * @return the resulting {@link Saml2Error}\n\t * @since 7.0\n\t */\n\tpublic static Saml2Error invalidResponse(@Nullable String description) {\n\t\treturn new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, description);\n\t}\n\n\t/**\n\t * Construct an {@link Saml2ErrorCodes#INTERNAL_VALIDATION_ERROR} error\n\t * @param description the error description\n\t * @return the resulting {@link Saml2Error}\n\t * @since 7.0\n\t */\n\tpublic static Saml2Error internalValidationError(@Nullable String description) {\n\t\treturn new Saml2Error(Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR, description);\n\t}\n\n\t/**\n\t * Construct an {@link Saml2ErrorCodes#MALFORMED_RESPONSE_DATA} error\n\t * @param description the error description\n\t * @return the resulting {@link Saml2Error}\n\t * @since 7.0\n\t */\n\tpublic static Saml2Error malformedResponseData(@Nullable String description) {\n\t\treturn new Saml2Error(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, description);\n\t}\n\n\t/**\n\t * Construct an {@link Saml2ErrorCodes#DECRYPTION_ERROR} error\n\t * @param description the error description\n\t * @return the resulting {@link Saml2Error}\n\t * @since 7.0\n\t */\n\tpublic static Saml2Error decryptionError(@Nullable String description) {\n\t\treturn new Saml2Error(Saml2ErrorCodes.DECRYPTION_ERROR, description);\n\t}\n\n\t/**\n\t * Construct an {@link Saml2ErrorCodes#RELYING_PARTY_REGISTRATION_NOT_FOUND} error\n\t * @param description the error description\n\t * @return the resulting {@link Saml2Error}\n\t * @since 7.0\n\t */\n\tpublic static Saml2Error relyingPartyRegistrationNotFound(@Nullable String description) {\n\t\treturn new Saml2Error(Saml2ErrorCodes.RELYING_PARTY_REGISTRATION_NOT_FOUND, description);\n\t}\n\n\t/**\n\t * Construct an {@link Saml2ErrorCodes#SUBJECT_NOT_FOUND} error\n\t * @param description the error description\n\t * @return the resulting {@link Saml2Error}\n\t * @since 7.0\n\t */\n\tpublic static Saml2Error subjectNotFound(@Nullable String description) {\n\t\treturn new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND, description);\n\t}\n\n\t/**\n\t * Returns the error code.\n\t * @return the error code\n\t */\n\tpublic final String getErrorCode() {\n\t\treturn this.errorCode;\n\t}\n\n\t/**\n\t * Returns the error description.\n\t * @return the error description\n\t */\n\tpublic final @Nullable String getDescription() {\n\t\treturn this.description;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"[\" + this.getErrorCode() + \"] \" + ((this.getDescription() != null) ? this.getDescription() : \"\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2ErrorCodes.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\n/**\n * A list of SAML known 2 error codes used during SAML authentication.\n *\n * @since 5.2\n */\npublic final class Saml2ErrorCodes {\n\n\t/**\n\t * SAML Data does not represent a SAML 2 Response object. A valid XML object was\n\t * received, but that object was not a SAML 2 Response object of type\n\t * {@code ResponseType} per specification\n\t * https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=46\n\t */\n\tpublic static final String UNKNOWN_RESPONSE_CLASS = \"unknown_response_class\";\n\n\t/**\n\t * The serialized AuthNRequest could not be deserialized correctly.\n\t *\n\t * @since 5.7\n\t */\n\tpublic static final String MALFORMED_REQUEST_DATA = \"malformed_request_data\";\n\n\t/**\n\t * The response data is malformed or incomplete. An invalid XML object was received,\n\t * and XML unmarshalling failed.\n\t */\n\tpublic static final String MALFORMED_RESPONSE_DATA = \"malformed_response_data\";\n\n\t/**\n\t * Request is invalid in a general way.\n\t *\n\t * @since 5.6\n\t */\n\tpublic static final String INVALID_REQUEST = \"invalid_request\";\n\n\t/**\n\t * Response is invalid in a general way.\n\t *\n\t * @since 5.5\n\t */\n\tpublic static final String INVALID_RESPONSE = \"invalid_response\";\n\n\t/**\n\t * Response destination does not match the request URL. A SAML 2 response object was\n\t * received at a URL that did not match the URL stored in the {code Destination}\n\t * attribute in the Response object.\n\t * https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38\n\t */\n\tpublic static final String INVALID_DESTINATION = \"invalid_destination\";\n\n\t/**\n\t * The assertion was not valid. The assertion used for authentication failed\n\t * validation. Details around the failure will be present in the error description.\n\t */\n\tpublic static final String INVALID_ASSERTION = \"invalid_assertion\";\n\n\t/**\n\t * The signature of response or assertion was invalid. Either the response or the\n\t * assertion was missing a signature or the signature could not be verified using the\n\t * system's configured credentials. Most commonly the IDP's X509 certificate.\n\t */\n\tpublic static final String INVALID_SIGNATURE = \"invalid_signature\";\n\n\t/**\n\t * The assertion did not contain a subject element. The subject element, type\n\t * SubjectType, contains a {@code NameID} or an {@code EncryptedID} that is used to\n\t * assign the authenticated principal an identifier, typically a username.\n\t *\n\t * https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=18\n\t */\n\tpublic static final String SUBJECT_NOT_FOUND = \"subject_not_found\";\n\n\t/**\n\t * The subject did not contain a user identifier The assertion contained a subject\n\t * element, but the subject element did not have a {@code NameID} or\n\t * {@code EncryptedID} element\n\t *\n\t * https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=18\n\t */\n\tpublic static final String USERNAME_NOT_FOUND = \"username_not_found\";\n\n\t/**\n\t * The system failed to decrypt an assertion or a name identifier. This error code\n\t * will be thrown if the decryption of either a {@code EncryptedAssertion} or\n\t * {@code EncryptedID} fails.\n\t * https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=17\n\t */\n\tpublic static final String DECRYPTION_ERROR = \"decryption_error\";\n\n\t/**\n\t * An Issuer element contained a value that didn't\n\t * https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=15\n\t */\n\tpublic static final String INVALID_ISSUER = \"invalid_issuer\";\n\n\t/**\n\t * An error happened during validation. Used when internal, non classified, errors are\n\t * caught during the authentication process.\n\t */\n\tpublic static final String INTERNAL_VALIDATION_ERROR = \"internal_validation_error\";\n\n\t/**\n\t * The relying party registration was not found. The registration ID did not\n\t * correspond to any relying party registration.\n\t */\n\tpublic static final String RELYING_PARTY_REGISTRATION_NOT_FOUND = \"relying_party_registration_not_found\";\n\n\t/**\n\t * The InResponseTo content of the response does not match the ID of the AuthNRequest.\n\t *\n\t * @since 5.7\n\t */\n\tpublic static final String INVALID_IN_RESPONSE_TO = \"invalid_in_response_to\";\n\n\tprivate Saml2ErrorCodes() {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2ParameterNames.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\n/**\n * Standard parameter names defined in the SAML 2.0 Specification and used by the\n * Authentication Request, Assertion Consumer Response, Logout Request, and Logout\n * Response endpoints.\n *\n * @author Josh Cummings\n * @since 5.6\n * @see <a target=\"_blank\" href=\n * \"https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf\">SAML 2.0\n * Bindings</a>\n */\npublic final class Saml2ParameterNames {\n\n\t/**\n\t * {@code SAMLRequest} - used to request authentication or request logout\n\t */\n\tpublic static final String SAML_REQUEST = \"SAMLRequest\";\n\n\t/**\n\t * {@code SAMLResponse} - used to respond to an authentication or logout request\n\t */\n\tpublic static final String SAML_RESPONSE = \"SAMLResponse\";\n\n\t/**\n\t * {@code RelayState} - used to communicate shared state between the relying and\n\t * asserting party\n\t * @see <a target=\"_blank\" href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf#page=8\">3.1.1\n\t * Use of RelayState</a>\n\t */\n\tpublic static final String RELAY_STATE = \"RelayState\";\n\n\t/**\n\t * {@code SigAlg} - used to communicate which signature algorithm to use to verify\n\t * signature\n\t */\n\tpublic static final String SIG_ALG = \"SigAlg\";\n\n\t/**\n\t * {@code Signature} - used to supply cryptographic signature on any SAML 2.0 payload\n\t */\n\tpublic static final String SIGNATURE = \"Signature\";\n\n\tprivate Saml2ParameterNames() {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResult.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.springframework.util.Assert;\n\n/**\n * A result emitted from a SAML 2.0 Response validation attempt\n *\n * @author Josh Cummings\n * @since 5.4\n */\npublic final class Saml2ResponseValidatorResult {\n\n\tstatic final Saml2ResponseValidatorResult NO_ERRORS = new Saml2ResponseValidatorResult(Collections.emptyList());\n\n\tprivate final Collection<Saml2Error> errors;\n\n\tprivate Saml2ResponseValidatorResult(Collection<Saml2Error> errors) {\n\t\tAssert.notNull(errors, \"errors cannot be null\");\n\t\tthis.errors = new ArrayList<>(errors);\n\t}\n\n\t/**\n\t * Say whether this result indicates success\n\t * @return whether this result has errors\n\t */\n\tpublic boolean hasErrors() {\n\t\treturn !this.errors.isEmpty();\n\t}\n\n\t/**\n\t * Return error details regarding the validation attempt\n\t * @return the collection of results in this result, if any; returns an empty list\n\t * otherwise\n\t */\n\tpublic Collection<Saml2Error> getErrors() {\n\t\treturn Collections.unmodifiableCollection(this.errors);\n\t}\n\n\t/**\n\t * Return a new {@link Saml2ResponseValidatorResult} that contains both the given\n\t * {@link Saml2Error} and the errors from the result\n\t * @param error the {@link Saml2Error} to append\n\t * @return a new {@link Saml2ResponseValidatorResult} for further reporting\n\t */\n\tpublic Saml2ResponseValidatorResult concat(Saml2Error error) {\n\t\tAssert.notNull(error, \"error cannot be null\");\n\t\tCollection<Saml2Error> errors = new ArrayList<>(this.errors);\n\t\terrors.add(error);\n\t\treturn failure(errors);\n\t}\n\n\t/**\n\t * Return a new {@link Saml2ResponseValidatorResult} that contains the errors from the\n\t * given {@link Saml2ResponseValidatorResult} as well as this result.\n\t * @param result the {@link Saml2ResponseValidatorResult} to merge with this one\n\t * @return a new {@link Saml2ResponseValidatorResult} for further reporting\n\t */\n\tpublic Saml2ResponseValidatorResult concat(Saml2ResponseValidatorResult result) {\n\t\tAssert.notNull(result, \"result cannot be null\");\n\t\tCollection<Saml2Error> errors = new ArrayList<>(this.errors);\n\t\terrors.addAll(result.getErrors());\n\t\treturn failure(errors);\n\t}\n\n\t/**\n\t * Construct a successful {@link Saml2ResponseValidatorResult}\n\t * @return an {@link Saml2ResponseValidatorResult} with no errors\n\t */\n\tpublic static Saml2ResponseValidatorResult success() {\n\t\treturn NO_ERRORS;\n\t}\n\n\t/**\n\t * Construct a failure {@link Saml2ResponseValidatorResult} with the provided detail\n\t * @param errors the list of errors\n\t * @return an {@link Saml2ResponseValidatorResult} with the errors specified\n\t */\n\tpublic static Saml2ResponseValidatorResult failure(Saml2Error... errors) {\n\t\treturn failure(Arrays.asList(errors));\n\t}\n\n\t/**\n\t * Construct a failure {@link Saml2ResponseValidatorResult} with the provided detail\n\t * @param errors the list of errors\n\t * @return an {@link Saml2ResponseValidatorResult} with the errors specified\n\t */\n\tpublic static Saml2ResponseValidatorResult failure(Collection<Saml2Error> errors) {\n\t\tif (errors.isEmpty()) {\n\t\t\treturn NO_ERRORS;\n\t\t}\n\n\t\treturn new Saml2ResponseValidatorResult(errors);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2X509Credential.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport java.io.Serializable;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.Arrays;\nimport java.util.LinkedHashSet;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * An object for holding a public certificate, any associated private key, and its\n * intended <a href=\n * \"https://www.oasis-open.org/committees/download.php/8958/sstc-saml-implementation-guidelines-draft-01.pdf\">\n * usages </a> (Line 584, Section 4.3 Credentials).\n *\n * @author Filip Hanik\n * @author Josh Cummings\n * @since 5.4\n */\npublic final class Saml2X509Credential implements Serializable {\n\n\tprivate static final long serialVersionUID = -1015853414272603517L;\n\n\tprivate final @Nullable PrivateKey privateKey;\n\n\tprivate final X509Certificate certificate;\n\n\tprivate final Set<Saml2X509CredentialType> credentialTypes;\n\n\t/**\n\t * Creates a {@link Saml2X509Credential} using the provided parameters\n\t * @param certificate the credential's public certificate\n\t * @param types the credential's intended usages, must be one of\n\t * {@link Saml2X509CredentialType#VERIFICATION} or\n\t * {@link Saml2X509CredentialType#ENCRYPTION} or both.\n\t */\n\tpublic Saml2X509Credential(X509Certificate certificate, Saml2X509CredentialType... types) {\n\t\tthis(null, false, certificate, types);\n\t\tvalidateUsages(types, Saml2X509CredentialType.VERIFICATION, Saml2X509CredentialType.ENCRYPTION);\n\t}\n\n\t/**\n\t * Creates a {@link Saml2X509Credential} using the provided parameters\n\t * @param privateKey the credential's private key\n\t * @param certificate the credential's public certificate\n\t * @param types the credential's intended usages, must be one of\n\t * {@link Saml2X509CredentialType#SIGNING} or\n\t * {@link Saml2X509CredentialType#DECRYPTION} or both.\n\t */\n\tpublic Saml2X509Credential(PrivateKey privateKey, X509Certificate certificate, Saml2X509CredentialType... types) {\n\t\tthis(privateKey, true, certificate, types);\n\t\tvalidateUsages(types, Saml2X509CredentialType.SIGNING, Saml2X509CredentialType.DECRYPTION);\n\t}\n\n\t/**\n\t * Creates a {@link Saml2X509Credential} using the provided parameters\n\t * @param privateKey the credential's private key\n\t * @param certificate the credential's public certificate\n\t * @param types the credential's intended usages\n\t */\n\tpublic Saml2X509Credential(@Nullable PrivateKey privateKey, X509Certificate certificate,\n\t\t\tSet<Saml2X509CredentialType> types) {\n\t\tAssert.notNull(certificate, \"certificate cannot be null\");\n\t\tAssert.notNull(types, \"credentialTypes cannot be null\");\n\t\tthis.privateKey = privateKey;\n\t\tthis.certificate = certificate;\n\t\tthis.credentialTypes = types;\n\t}\n\n\t/**\n\t * Create a {@link Saml2X509Credential} that can be used for encryption.\n\t * @param certificate the certificate to use for encryption\n\t * @return an encrypting {@link Saml2X509Credential}\n\t */\n\tpublic static Saml2X509Credential encryption(X509Certificate certificate) {\n\t\treturn new Saml2X509Credential(certificate, Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION);\n\t}\n\n\t/**\n\t * Create a {@link Saml2X509Credential} that can be used for verification.\n\t * @param certificate the certificate to use for verification\n\t * @return a verifying {@link Saml2X509Credential}\n\t */\n\tpublic static Saml2X509Credential verification(X509Certificate certificate) {\n\t\treturn new Saml2X509Credential(certificate, Saml2X509Credential.Saml2X509CredentialType.VERIFICATION);\n\t}\n\n\t/**\n\t * Create a {@link Saml2X509Credential} that can be used for decryption.\n\t * @param privateKey the private key to use for decryption\n\t * @param certificate the certificate to use for decryption\n\t * @return an decrypting {@link Saml2X509Credential}\n\t */\n\tpublic static Saml2X509Credential decryption(PrivateKey privateKey, X509Certificate certificate) {\n\t\treturn new Saml2X509Credential(privateKey, certificate, Saml2X509Credential.Saml2X509CredentialType.DECRYPTION);\n\t}\n\n\t/**\n\t * Create a {@link Saml2X509Credential} that can be used for signing.\n\t * @param privateKey the private key to use for signing\n\t * @param certificate the certificate to use for signing\n\t * @return a signing {@link Saml2X509Credential}\n\t */\n\tpublic static Saml2X509Credential signing(PrivateKey privateKey, X509Certificate certificate) {\n\t\treturn new Saml2X509Credential(privateKey, certificate, Saml2X509Credential.Saml2X509CredentialType.SIGNING);\n\t}\n\n\tprivate Saml2X509Credential(@Nullable PrivateKey privateKey, boolean keyRequired, X509Certificate certificate,\n\t\t\tSaml2X509CredentialType... types) {\n\t\tAssert.notNull(certificate, \"certificate cannot be null\");\n\t\tAssert.notEmpty(types, \"credentials types cannot be empty\");\n\t\tif (keyRequired) {\n\t\t\tAssert.notNull(privateKey, \"privateKey cannot be null\");\n\t\t}\n\t\tthis.privateKey = privateKey;\n\t\tthis.certificate = certificate;\n\t\tthis.credentialTypes = new LinkedHashSet<>(Arrays.asList(types));\n\t}\n\n\t/**\n\t * Get the private key for this credential\n\t * @return the private key, may be null\n\t * @see #Saml2X509Credential(PrivateKey, X509Certificate, Saml2X509CredentialType...)\n\t */\n\tpublic @Nullable PrivateKey getPrivateKey() {\n\t\treturn this.privateKey;\n\t}\n\n\t/**\n\t * Get the public certificate for this credential\n\t * @return the public certificate\n\t */\n\tpublic X509Certificate getCertificate() {\n\t\treturn this.certificate;\n\t}\n\n\t/**\n\t * Indicate whether this credential can be used for signing\n\t * @return true if the credential has a {@link Saml2X509CredentialType#SIGNING} type\n\t */\n\tpublic boolean isSigningCredential() {\n\t\treturn getCredentialTypes().contains(Saml2X509CredentialType.SIGNING);\n\t}\n\n\t/**\n\t * Indicate whether this credential can be used for decryption\n\t * @return true if the credential has a {@link Saml2X509CredentialType#DECRYPTION}\n\t * type\n\t */\n\tpublic boolean isDecryptionCredential() {\n\t\treturn getCredentialTypes().contains(Saml2X509CredentialType.DECRYPTION);\n\t}\n\n\t/**\n\t * Indicate whether this credential can be used for verification\n\t * @return true if the credential has a {@link Saml2X509CredentialType#VERIFICATION}\n\t * type\n\t */\n\tpublic boolean isVerificationCredential() {\n\t\treturn getCredentialTypes().contains(Saml2X509CredentialType.VERIFICATION);\n\t}\n\n\t/**\n\t * Indicate whether this credential can be used for encryption\n\t * @return true if the credential has a {@link Saml2X509CredentialType#ENCRYPTION}\n\t * type\n\t */\n\tpublic boolean isEncryptionCredential() {\n\t\treturn getCredentialTypes().contains(Saml2X509CredentialType.ENCRYPTION);\n\t}\n\n\t/**\n\t * List all this credential's intended usages\n\t * @return the set of this credential's intended usages\n\t */\n\tpublic Set<Saml2X509CredentialType> getCredentialTypes() {\n\t\treturn this.credentialTypes;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tSaml2X509Credential that = (Saml2X509Credential) o;\n\t\treturn Objects.equals(this.privateKey, that.privateKey) && this.certificate.equals(that.certificate)\n\t\t\t\t&& this.credentialTypes.equals(that.credentialTypes);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.privateKey, this.certificate, this.credentialTypes);\n\t}\n\n\tprivate void validateUsages(Saml2X509CredentialType[] usages, Saml2X509CredentialType... validUsages) {\n\t\tfor (Saml2X509CredentialType usage : usages) {\n\t\t\tboolean valid = false;\n\t\t\tfor (Saml2X509CredentialType validUsage : validUsages) {\n\t\t\t\tif (usage == validUsage) {\n\t\t\t\t\tvalid = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tAssert.state(valid, () -> usage + \" is not a valid usage for this credential\");\n\t\t}\n\t}\n\n\tpublic enum Saml2X509CredentialType {\n\n\t\tVERIFICATION,\n\n\t\tENCRYPTION,\n\n\t\tSIGNING,\n\n\t\tDECRYPTION,\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core SAML2 types and utilities.\n */\n@NullMarked\npackage org.springframework.security.saml2.core;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/internal/OpenSamlOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.internal;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\ninterface OpenSamlOperations {\n\n\t<T extends XMLObject> T build(QName elementName);\n\n\t<T extends XMLObject> T deserialize(String serialized);\n\n\t<T extends XMLObject> T deserialize(InputStream serialized);\n\n\tSerializationConfigurer<?> serialize(XMLObject object);\n\n\tSerializationConfigurer<?> serialize(Element element);\n\n\tSignatureConfigurer<?> withSigningKeys(Collection<Saml2X509Credential> credentials);\n\n\tVerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials);\n\n\tDecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials);\n\n\tinterface SerializationConfigurer<B extends SerializationConfigurer<B>> {\n\n\t\tB prettyPrint(boolean pretty);\n\n\t\tString serialize();\n\n\t}\n\n\tinterface SignatureConfigurer<B extends SignatureConfigurer<B>> {\n\n\t\tB algorithms(List<String> algs);\n\n\t\t<O extends SignableXMLObject> O sign(O object);\n\n\t\tMap<String, String> sign(Map<String, String> params);\n\n\t}\n\n\tinterface VerificationConfigurer {\n\n\t\tVerificationConfigurer entityId(String entityId);\n\n\t\tCollection<Saml2Error> verify(SignableXMLObject signable);\n\n\t\tCollection<Saml2Error> verify(VerificationConfigurer.RedirectParameters parameters);\n\n\t\tfinal class RedirectParameters {\n\n\t\t\tprivate final String id;\n\n\t\t\tprivate final Issuer issuer;\n\n\t\t\tprivate final String algorithm;\n\n\t\t\tprivate final byte @Nullable [] signature;\n\n\t\t\tprivate final byte[] content;\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"SAML request's ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"SAML request's Issuer cannot be null\");\n\t\t\t\tthis.id = request.getID();\n\t\t\t\tthis.issuer = request.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_REQUEST, relayState, queryParams);\n\t\t\t}\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"SAML response's ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"SAML response's Issuer cannot be null\");\n\t\t\t\tthis.id = response.getID();\n\t\t\t\tthis.issuer = response.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_RESPONSE, relayState, queryParams);\n\t\t\t}\n\n\t\t\tstatic byte[] getContent(String samlObject, @Nullable String relayState,\n\t\t\t\t\tfinal Map<String, String> queryParams) {\n\t\t\t\tif (Objects.nonNull(relayState)) {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s&%s=%s\", samlObject, queryParams.get(samlObject),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.RELAY_STATE, queryParams.get(Saml2ParameterNames.RELAY_STATE),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.SIG_ALG, queryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s\", samlObject, queryParams.get(samlObject), Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\t\t\tqueryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getId() {\n\t\t\t\treturn this.id;\n\t\t\t}\n\n\t\t\tIssuer getIssuer() {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\tbyte[] getContent() {\n\t\t\t\treturn this.content;\n\t\t\t}\n\n\t\t\tString getAlgorithm() {\n\t\t\t\treturn this.algorithm;\n\t\t\t}\n\n\t\t\tbyte @Nullable [] getSignature() {\n\t\t\t\treturn this.signature;\n\t\t\t}\n\n\t\t\tboolean hasSignature() {\n\t\t\t\treturn this.signature != null;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface DecryptionConfigurer {\n\n\t\tvoid decrypt(XMLObject object);\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/internal/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.internal;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/internal/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal utilities for SAML2 support (not for public use).\n */\n@NullMarked\npackage org.springframework.security.saml2.internal;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/DefaultSaml2AuthenticatedPrincipalMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link DefaultSaml2AuthenticatedPrincipal}.\n *\n * @author Sebastien Deleuze\n * @author Ulrich Grave\n * @since 7.0\n * @see Saml2JacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@NullUnmarked\nclass DefaultSaml2AuthenticatedPrincipalMixin {\n\n\t@JsonProperty(\"registrationId\")\n\tString registrationId;\n\n\tDefaultSaml2AuthenticatedPrincipalMixin(@JsonProperty(\"name\") String name,\n\t\t\t@JsonProperty(\"attributes\") Map<String, List<Object>> attributes,\n\t\t\t@JsonProperty(\"sessionIndexes\") List<String> sessionIndexes) {\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/Saml2AssertionAuthenticationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link Saml2AssertionAuthentication}.\n *\n * @author Sebastien Deleuze\n * @author Josh Cummings\n * @since 7.0\n * @see Saml2JacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@NullUnmarked\nclass Saml2AssertionAuthenticationMixin {\n\n\t@JsonCreator\n\tSaml2AssertionAuthenticationMixin(@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"assertion\") Saml2ResponseAssertionAccessor assertion,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"relyingPartyRegistrationId\") String relyingPartyRegistrationId) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/Saml2AuthenticationExceptionMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\n\n/**\n * This mixin class is used to serialize/deserialize {@link Saml2AuthenticationException}.\n *\n * @author Sebastien Deleuze\n * @author Ulrich Grave\n * @since 7.0\n * @see Saml2AuthenticationException\n * @see Saml2JacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties({ \"cause\", \"stackTrace\", \"suppressedExceptions\" })\n@NullUnmarked\nabstract class Saml2AuthenticationExceptionMixin {\n\n\t@JsonProperty(\"error\")\n\tabstract Saml2Error getSaml2Error();\n\n\t@JsonProperty(\"detailMessage\")\n\tabstract String getMessage();\n\n\t@JsonCreator\n\tSaml2AuthenticationExceptionMixin(@JsonProperty(\"error\") Saml2Error error,\n\t\t\t@JsonProperty(\"detailMessage\") String message) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/Saml2AuthenticationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize {@link Saml2Authentication}.\n *\n * @author Sebastien Deleuze\n * @since 7.0\n * @see Saml2JacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties({ \"authenticated\" })\n@NullUnmarked\nclass Saml2AuthenticationMixin {\n\n\t@JsonCreator\n\tSaml2AuthenticationMixin(@JsonProperty(\"principal\") AuthenticatedPrincipal principal,\n\t\t\t@JsonProperty(\"saml2Response\") String saml2Response,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/Saml2ErrorMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.saml2.core.Saml2Error;\n\n/**\n * This mixin class is used to serialize/deserialize {@link Saml2Error}.\n *\n * @author Sebastien Deleuze\n * @author Ulrich Grave\n * @since 7.0\n * @see Saml2Error\n * @see Saml2JacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@NullUnmarked\nclass Saml2ErrorMixin {\n\n\t@JsonCreator\n\tSaml2ErrorMixin(@JsonProperty(\"errorCode\") String errorCode, @JsonProperty(\"description\") String description) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/Saml2JacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\n\n/**\n * Jackson module for saml2-service-provider. This module register\n * {@link Saml2AuthenticationMixin}, {@link Saml2AssertionAuthenticationMixin},\n * {@link SimpleSaml2ResponseAssertionAccessorMixin},\n * {@link DefaultSaml2AuthenticatedPrincipalMixin}, {@link Saml2LogoutRequestMixin},\n * {@link Saml2RedirectAuthenticationRequestMixin},\n * {@link Saml2PostAuthenticationRequestMixin}, {@link Saml2ErrorMixin} and\n * {@link Saml2AuthenticationExceptionMixin}.\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @since 7.0\n * @see SecurityJacksonModules\n */\n@SuppressWarnings(\"serial\")\npublic class Saml2JacksonModule extends SecurityJacksonModule {\n\n\tpublic Saml2JacksonModule() {\n\t\tsuper(Saml2JacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(Saml2ResponseAssertion.class)\n\t\t\t.allowIfSubType(DefaultSaml2AuthenticatedPrincipal.class)\n\t\t\t.allowIfSubType(Saml2PostAuthenticationRequest.class)\n\t\t\t.allowIfSubType(Saml2LogoutRequest.class)\n\t\t\t.allowIfSubType(Saml2RedirectAuthenticationRequest.class)\n\t\t\t.allowIfSubType(Saml2AuthenticationException.class)\n\t\t\t.allowIfSubType(Saml2Error.class)\n\t\t\t.allowIfSubType(Saml2AssertionAuthentication.class)\n\t\t\t.allowIfSubType(Saml2Authentication.class);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(Saml2Authentication.class, Saml2AuthenticationMixin.class);\n\t\tcontext.setMixIn(Saml2AssertionAuthentication.class, Saml2AssertionAuthenticationMixin.class);\n\t\tcontext.setMixIn(Saml2ResponseAssertion.class, SimpleSaml2ResponseAssertionAccessorMixin.class);\n\t\tcontext.setMixIn(DefaultSaml2AuthenticatedPrincipal.class, DefaultSaml2AuthenticatedPrincipalMixin.class);\n\t\tcontext.setMixIn(Saml2LogoutRequest.class, Saml2LogoutRequestMixin.class);\n\t\tcontext.setMixIn(Saml2RedirectAuthenticationRequest.class, Saml2RedirectAuthenticationRequestMixin.class);\n\t\tcontext.setMixIn(Saml2PostAuthenticationRequest.class, Saml2PostAuthenticationRequestMixin.class);\n\t\tcontext.setMixIn(Saml2Error.class, Saml2ErrorMixin.class);\n\t\tcontext.setMixIn(Saml2AuthenticationException.class, Saml2AuthenticationExceptionMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/Saml2LogoutRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize {@link Saml2LogoutRequest}.\n *\n * @author Sebastien Deleuze\n * @author Ulrich Grave\n * @since 7.0\n * @see Saml2JacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@NullUnmarked\nclass Saml2LogoutRequestMixin {\n\n\t@JsonIgnore\n\tFunction<Map<String, String>, String> encoder;\n\n\t@JsonCreator\n\tSaml2LogoutRequestMixin(@JsonProperty(\"location\") String location,\n\t\t\t@JsonProperty(\"binding\") Saml2MessageBinding binding,\n\t\t\t@JsonProperty(\"parameters\") Map<String, String> parameters, @JsonProperty(\"id\") String id,\n\t\t\t@JsonProperty(\"relyingPartyRegistrationId\") String relyingPartyRegistrationId) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/Saml2PostAuthenticationRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link Saml2PostAuthenticationRequest}.\n *\n * @author Sebastien Deleuze\n * @author Ulrich Grave\n * @since 7.0\n * @see Saml2JacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@NullUnmarked\nclass Saml2PostAuthenticationRequestMixin {\n\n\t@JsonCreator\n\tSaml2PostAuthenticationRequestMixin(@JsonProperty(\"samlRequest\") String samlRequest,\n\t\t\t@JsonProperty(\"relayState\") String relayState,\n\t\t\t@JsonProperty(\"authenticationRequestUri\") String authenticationRequestUri,\n\t\t\t@JsonProperty(\"relyingPartyRegistrationId\") String relyingPartyRegistrationId,\n\t\t\t@JsonProperty(\"id\") String id) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/Saml2RedirectAuthenticationRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link Saml2RedirectAuthenticationRequest}.\n *\n * @author Sebastien Deleuze\n * @author Ulrich Grave\n * @since 7.0\n * @see Saml2JacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@NullUnmarked\nclass Saml2RedirectAuthenticationRequestMixin {\n\n\t@JsonCreator\n\tSaml2RedirectAuthenticationRequestMixin(@JsonProperty(\"samlRequest\") String samlRequest,\n\t\t\t@JsonProperty(\"sigAlg\") String sigAlg, @JsonProperty(\"signature\") String signature,\n\t\t\t@JsonProperty(\"relayState\") String relayState,\n\t\t\t@JsonProperty(\"authenticationRequestUri\") String authenticationRequestUri,\n\t\t\t@JsonProperty(\"relyingPartyRegistrationId\") String relyingPartyRegistrationId,\n\t\t\t@JsonProperty(\"id\") String id) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/SimpleSaml2ResponseAssertionAccessorMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize {@link Saml2ResponseAssertion}.\n *\n * @author Sebastien Deleuze\n * @author Josh Cummings\n * @since 7.0\n * @see Saml2JacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties({ \"authenticated\" })\n@NullUnmarked\nclass SimpleSaml2ResponseAssertionAccessorMixin {\n\n\t@JsonCreator\n\tSimpleSaml2ResponseAssertionAccessorMixin(@JsonProperty(\"responseValue\") String responseValue,\n\t\t\t@JsonProperty(\"nameId\") String nameId, @JsonProperty(\"sessionIndexes\") List<String> sessionIndexes,\n\t\t\t@JsonProperty(\"attributes\") Map<String, List<Object>> attributes) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 3+ serialization support for SAML2.\n */\n@NullMarked\npackage org.springframework.security.saml2.jackson;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link DefaultSaml2AuthenticatedPrincipal}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new Saml2Jackson2Module());\n * </pre>\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see Saml2Jackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.DefaultSaml2AuthenticatedPrincipalMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@NullUnmarked\nclass DefaultSaml2AuthenticatedPrincipalMixin {\n\n\t@JsonProperty(\"registrationId\")\n\tString registrationId;\n\n\tDefaultSaml2AuthenticatedPrincipalMixin(@JsonProperty(\"name\") String name,\n\t\t\t@JsonProperty(\"attributes\") Map<String, List<Object>> attributes,\n\t\t\t@JsonProperty(\"sessionIndexes\") List<String> sessionIndexes) {\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AssertionAuthenticationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link Saml2AssertionAuthentication}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new Saml2Jackson2Module());\n * </pre>\n *\n * @author Josh Cummings\n * @since 7.0\n * @see Saml2Jackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.Saml2AssertionAuthenticationMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(value = { \"authenticated\" }, ignoreUnknown = true)\n@NullUnmarked\nclass Saml2AssertionAuthenticationMixin {\n\n\t@JsonCreator\n\tSaml2AssertionAuthenticationMixin(@JsonProperty(\"principal\") Object principal,\n\t\t\t@JsonProperty(\"assertion\") Saml2ResponseAssertionAccessor assertion,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities,\n\t\t\t@JsonProperty(\"relyingPartyRegistrationId\") String relyingPartyRegistrationId) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationExceptionMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\n\n/**\n * This mixin class is used to serialize/deserialize {@link Saml2AuthenticationException}.\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see Saml2AuthenticationException\n * @see Saml2Jackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.Saml2AuthenticationExceptionMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true, value = { \"cause\", \"stackTrace\", \"suppressedExceptions\" })\n@NullUnmarked\nabstract class Saml2AuthenticationExceptionMixin {\n\n\t@JsonProperty(\"error\")\n\tabstract Saml2Error getSaml2Error();\n\n\t@JsonProperty(\"detailMessage\")\n\tabstract String getMessage();\n\n\t@JsonCreator\n\tSaml2AuthenticationExceptionMixin(@JsonProperty(\"error\") Saml2Error error,\n\t\t\t@JsonProperty(\"detailMessage\") String message) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize {@link Saml2Authentication}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new Saml2Jackson2Module());\n * </pre>\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see Saml2Jackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.Saml2AuthenticationMixin} based on\n * Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(value = { \"authenticated\" }, ignoreUnknown = true)\n@NullUnmarked\nclass Saml2AuthenticationMixin {\n\n\t@JsonCreator\n\tSaml2AuthenticationMixin(@JsonProperty(\"principal\") AuthenticatedPrincipal principal,\n\t\t\t@JsonProperty(\"saml2Response\") String saml2Response,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2ErrorMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.saml2.core.Saml2Error;\n\n/**\n * This mixin class is used to serialize/deserialize {@link Saml2Error}.\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see Saml2Error\n * @see Saml2Jackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.Saml2ErrorMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@NullUnmarked\nclass Saml2ErrorMixin {\n\n\t@JsonCreator\n\tSaml2ErrorMixin(@JsonProperty(\"errorCode\") String errorCode, @JsonProperty(\"description\") String description) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2Jackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\n\n/**\n * Jackson module for saml2-service-provider. This module register\n * {@link Saml2AuthenticationMixin}, {@link DefaultSaml2AuthenticatedPrincipalMixin},\n * {@link Saml2LogoutRequestMixin}, {@link Saml2RedirectAuthenticationRequestMixin},\n * {@link Saml2PostAuthenticationRequestMixin}, {@link Saml2ErrorMixin} and\n * {@link Saml2AuthenticationExceptionMixin}.\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.saml2.jackson.Saml2JacksonModule} based on Jackson\n * 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings({ \"serial\", \"removal\" })\npublic class Saml2Jackson2Module extends SimpleModule {\n\n\tpublic Saml2Jackson2Module() {\n\t\tsuper(Saml2Jackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\t// TODO Is it expected that default typing in not configured here?\n\t\tcontext.setMixInAnnotations(Saml2Authentication.class, Saml2AuthenticationMixin.class);\n\t\tcontext.setMixInAnnotations(Saml2AssertionAuthentication.class, Saml2AssertionAuthenticationMixin.class);\n\t\tcontext.setMixInAnnotations(Saml2ResponseAssertion.class, SimpleSaml2ResponseAssertionAccessorMixin.class);\n\t\tcontext.setMixInAnnotations(DefaultSaml2AuthenticatedPrincipal.class,\n\t\t\t\tDefaultSaml2AuthenticatedPrincipalMixin.class);\n\t\tcontext.setMixInAnnotations(Saml2LogoutRequest.class, Saml2LogoutRequestMixin.class);\n\t\tcontext.setMixInAnnotations(Saml2RedirectAuthenticationRequest.class,\n\t\t\t\tSaml2RedirectAuthenticationRequestMixin.class);\n\t\tcontext.setMixInAnnotations(Saml2PostAuthenticationRequest.class, Saml2PostAuthenticationRequestMixin.class);\n\t\tcontext.setMixInAnnotations(Saml2Error.class, Saml2ErrorMixin.class);\n\t\tcontext.setMixInAnnotations(Saml2AuthenticationException.class, Saml2AuthenticationExceptionMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2LogoutRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize {@link Saml2LogoutRequest}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new Saml2Jackson2Module());\n * </pre>\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see Saml2Jackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.Saml2LogoutRequestMixin} based on\n * Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@NullUnmarked\nclass Saml2LogoutRequestMixin {\n\n\t@JsonIgnore\n\tFunction<Map<String, String>, String> encoder;\n\n\t@JsonCreator\n\tSaml2LogoutRequestMixin(@JsonProperty(\"location\") String location,\n\t\t\t@JsonProperty(\"binding\") Saml2MessageBinding binding,\n\t\t\t@JsonProperty(\"parameters\") Map<String, String> parameters, @JsonProperty(\"id\") String id,\n\t\t\t@JsonProperty(\"relyingPartyRegistrationId\") String relyingPartyRegistrationId) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2PostAuthenticationRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link Saml2PostAuthenticationRequest}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new Saml2Jackson2Module());\n * </pre>\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see Saml2Jackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.Saml2PostAuthenticationRequestMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@NullUnmarked\nclass Saml2PostAuthenticationRequestMixin {\n\n\t@JsonCreator\n\tSaml2PostAuthenticationRequestMixin(@JsonProperty(\"samlRequest\") String samlRequest,\n\t\t\t@JsonProperty(\"relayState\") String relayState,\n\t\t\t@JsonProperty(\"authenticationRequestUri\") String authenticationRequestUri,\n\t\t\t@JsonProperty(\"relyingPartyRegistrationId\") String relyingPartyRegistrationId,\n\t\t\t@JsonProperty(\"id\") String id) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2RedirectAuthenticationRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize\n * {@link Saml2RedirectAuthenticationRequest}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new Saml2Jackson2Module());\n * </pre>\n *\n * @author Ulrich Grave\n * @since 5.7\n * @see Saml2Jackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.Saml2RedirectAuthenticationRequestMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@NullUnmarked\nclass Saml2RedirectAuthenticationRequestMixin {\n\n\t@JsonCreator\n\tSaml2RedirectAuthenticationRequestMixin(@JsonProperty(\"samlRequest\") String samlRequest,\n\t\t\t@JsonProperty(\"sigAlg\") String sigAlg, @JsonProperty(\"signature\") String signature,\n\t\t\t@JsonProperty(\"relayState\") String relayState,\n\t\t\t@JsonProperty(\"authenticationRequestUri\") String authenticationRequestUri,\n\t\t\t@JsonProperty(\"relyingPartyRegistrationId\") String relyingPartyRegistrationId,\n\t\t\t@JsonProperty(\"id\") String id) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/SimpleSaml2ResponseAssertionAccessorMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;\n\n/**\n * Jackson Mixin class helps in serialize/deserialize {@link Saml2ResponseAssertion}.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new Saml2Jackson2Module());\n * </pre>\n *\n * @author Josh Cummings\n * @since 7.0\n * @see Saml2Jackson2Module\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.saml2.jackson.SimpleSaml2ResponseAssertionAccessorMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(value = { \"authenticated\" }, ignoreUnknown = true)\n@NullUnmarked\nclass SimpleSaml2ResponseAssertionAccessorMixin {\n\n\t@JsonCreator\n\tSimpleSaml2ResponseAssertionAccessorMixin(@JsonProperty(\"responseValue\") String responseValue,\n\t\t\t@JsonProperty(\"nameId\") String nameId, @JsonProperty(\"sessionIndexes\") List<String> sessionIndexes,\n\t\t\t@JsonProperty(\"attributes\") Map<String, List<Object>> attributes) {\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 2 serialization support for SAML2.\n */\n@NullMarked\npackage org.springframework.security.saml2.jackson2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Core SAML2 support for Spring Security.\n */\n@NullMarked\npackage org.springframework.security.saml2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/AbstractSaml2AuthenticationRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serializable;\nimport java.nio.charset.Charset;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.util.Assert;\n\n/**\n * Data holder for {@code AuthNRequest} parameters to be sent using either the\n * {@link Saml2MessageBinding#POST} or {@link Saml2MessageBinding#REDIRECT} binding. Data\n * will be encoded and possibly deflated, but will not be escaped for transport, ie URL\n * encoded, {@link org.springframework.web.util.UriUtils#encode(String, Charset)} or HTML\n * encoded, {@link org.springframework.web.util.HtmlUtils#htmlEscape(String)}.\n * https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf\n * (line 2031)\n *\n * @since 5.3\n * @see Saml2PostAuthenticationRequest\n * @see Saml2RedirectAuthenticationRequest\n */\npublic abstract class AbstractSaml2AuthenticationRequest implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String samlRequest;\n\n\tprivate final @Nullable String relayState;\n\n\tprivate final String authenticationRequestUri;\n\n\tprivate final @Nullable String relyingPartyRegistrationId;\n\n\tprivate final @Nullable String id;\n\n\t/**\n\t * Mandatory constructor for the {@link AbstractSaml2AuthenticationRequest}\n\t * @param samlRequest - the SAMLRequest XML data, SAML encoded, cannot be empty or\n\t * null\n\t * @param relayState - RelayState value that accompanies the request, may be null\n\t * @param authenticationRequestUri - The authenticationRequestUri, a URL, where to\n\t * send the XML message, cannot be empty or null\n\t * @param relyingPartyRegistrationId the registration id of the relying party, may be\n\t * null\n\t * @param id This is the unique id used in the {@link #samlRequest}, cannot be empty\n\t * or null\n\t */\n\tAbstractSaml2AuthenticationRequest(String samlRequest, @Nullable String relayState, String authenticationRequestUri,\n\t\t\t@Nullable String relyingPartyRegistrationId, @Nullable String id) {\n\t\tAssert.hasText(samlRequest, \"samlRequest cannot be null or empty\");\n\t\tAssert.hasText(authenticationRequestUri, \"authenticationRequestUri cannot be null or empty\");\n\t\tthis.authenticationRequestUri = authenticationRequestUri;\n\t\tthis.samlRequest = samlRequest;\n\t\tthis.relayState = relayState;\n\t\tthis.relyingPartyRegistrationId = relyingPartyRegistrationId;\n\t\tthis.id = id;\n\t}\n\n\t/**\n\t * Returns the AuthNRequest XML value to be sent. This value is already encoded for\n\t * transport. If {@link #getBinding()} is {@link Saml2MessageBinding#REDIRECT} the\n\t * value is deflated and SAML encoded. If {@link #getBinding()} is\n\t * {@link Saml2MessageBinding#POST} the value is SAML encoded.\n\t * @return the SAMLRequest parameter value\n\t */\n\tpublic String getSamlRequest() {\n\t\treturn this.samlRequest;\n\t}\n\n\t/**\n\t * Returns the RelayState value, if present in the parameters\n\t * @return the RelayState value, or null if not available\n\t */\n\tpublic @Nullable String getRelayState() {\n\t\treturn this.relayState;\n\t}\n\n\t/**\n\t * Returns the URI endpoint that this AuthNRequest should be sent to.\n\t * @return the URI endpoint for this message\n\t */\n\tpublic String getAuthenticationRequestUri() {\n\t\treturn this.authenticationRequestUri;\n\t}\n\n\t/**\n\t * The identifier for the {@link RelyingPartyRegistration} associated with this\n\t * request\n\t * @return the {@link RelyingPartyRegistration} id\n\t * @since 5.8\n\t */\n\tpublic @Nullable String getRelyingPartyRegistrationId() {\n\t\treturn this.relyingPartyRegistrationId;\n\t}\n\n\t/**\n\t * The unique identifier for this Authentication Request\n\t * @return the Authentication Request identifier\n\t * @since 5.8\n\t */\n\tpublic @Nullable String getId() {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * Returns the binding this AuthNRequest will be sent and encoded with. If\n\t * {@link Saml2MessageBinding#REDIRECT} is used, the DEFLATE encoding will be\n\t * automatically applied.\n\t * @return the binding this message will be sent with.\n\t */\n\tpublic abstract Saml2MessageBinding getBinding();\n\n\t/**\n\t * A builder for {@link AbstractSaml2AuthenticationRequest} and its subclasses.\n\t */\n\tpublic static class Builder<T extends Builder<T>> {\n\n\t\t@Nullable String authenticationRequestUri;\n\n\t\t@Nullable String samlRequest;\n\n\t\t@Nullable String relayState;\n\n\t\t@Nullable String relyingPartyRegistrationId;\n\n\t\t@Nullable String id;\n\n\t\t/**\n\t\t * @deprecated Use {@link #Builder(RelyingPartyRegistration)} instead\n\t\t */\n\t\t@Deprecated\n\t\tprotected Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Creates a new Builder with relying party registration\n\t\t * @param registration the registration of the relying party.\n\t\t * @since 5.8\n\t\t */\n\t\tprotected Builder(RelyingPartyRegistration registration) {\n\t\t\tthis.relyingPartyRegistrationId = registration.getRegistrationId();\n\t\t}\n\n\t\t/**\n\t\t * Casting the return as the generic subtype, when returning itself\n\t\t * @return this object\n\t\t */\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprotected final T _this() {\n\t\t\treturn (T) this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code RelayState} parameter that will accompany this AuthNRequest\n\t\t * @param relayState the relay state value, unencoded. if null or empty, the\n\t\t * parameter will be removed from the map.\n\t\t * @return this object\n\t\t */\n\t\tpublic T relayState(@Nullable String relayState) {\n\t\t\tthis.relayState = relayState;\n\t\t\treturn _this();\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code SAMLRequest} parameter that will accompany this AuthNRequest\n\t\t * @param samlRequest the SAMLRequest parameter.\n\t\t * @return this object\n\t\t */\n\t\tpublic T samlRequest(String samlRequest) {\n\t\t\tthis.samlRequest = samlRequest;\n\t\t\treturn _this();\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code authenticationRequestUri}, a URL that will receive the\n\t\t * AuthNRequest message\n\t\t * @param authenticationRequestUri the relay state value, unencoded.\n\t\t * @return this object\n\t\t */\n\t\tpublic T authenticationRequestUri(String authenticationRequestUri) {\n\t\t\tthis.authenticationRequestUri = authenticationRequestUri;\n\t\t\treturn _this();\n\t\t}\n\n\t\t/**\n\t\t * This is the unique id used in the {@link #samlRequest}\n\t\t * @param id the SAML2 request id\n\t\t * @return the {@link AbstractSaml2AuthenticationRequest.Builder} for further\n\t\t * configurations\n\t\t * @since 5.8\n\t\t */\n\t\tpublic T id(String id) {\n\t\t\tAssert.notNull(id, \"id cannot be null\");\n\t\t\tthis.id = id;\n\t\t\treturn _this();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/BaseOpenSamlAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport javax.xml.namespace.QName;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.schema.XSAny;\nimport org.opensaml.core.xml.schema.XSBoolean;\nimport org.opensaml.core.xml.schema.XSBooleanValue;\nimport org.opensaml.core.xml.schema.XSDateTime;\nimport org.opensaml.core.xml.schema.XSInteger;\nimport org.opensaml.core.xml.schema.XSString;\nimport org.opensaml.core.xml.schema.XSURI;\nimport org.opensaml.saml.common.assertion.AssertionValidationException;\nimport org.opensaml.saml.common.assertion.ValidationContext;\nimport org.opensaml.saml.common.assertion.ValidationResult;\nimport org.opensaml.saml.saml2.assertion.ConditionValidator;\nimport org.opensaml.saml.saml2.assertion.SAML20AssertionValidator;\nimport org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters;\nimport org.opensaml.saml.saml2.assertion.StatementValidator;\nimport org.opensaml.saml.saml2.assertion.SubjectConfirmationValidator;\nimport org.opensaml.saml.saml2.assertion.impl.AudienceRestrictionConditionValidator;\nimport org.opensaml.saml.saml2.assertion.impl.BearerSubjectConfirmationValidator;\nimport org.opensaml.saml.saml2.assertion.impl.DelegationRestrictionConditionValidator;\nimport org.opensaml.saml.saml2.assertion.impl.ProxyRestrictionConditionValidator;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.AuthnStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.OneTimeUse;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.Status;\nimport org.opensaml.saml.saml2.core.StatusCode;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.core.SubjectConfirmationData;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ResponseValidatorResult;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\n\nclass BaseOpenSamlAuthenticationProvider implements AuthenticationProvider {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate final OpenSamlOperations saml;\n\n\tprivate final Converter<ResponseToken, Saml2ResponseValidatorResult> responseSignatureValidator = createDefaultResponseSignatureValidator();\n\n\tprivate Consumer<ResponseToken> responseElementsDecrypter = createDefaultResponseElementsDecrypter();\n\n\tprivate Converter<ResponseToken, Saml2ResponseValidatorResult> responseValidator = createDefaultResponseValidator();\n\n\tprivate final Converter<AssertionToken, Saml2ResponseValidatorResult> assertionSignatureValidator = createDefaultAssertionSignatureValidator();\n\n\tprivate Consumer<AssertionToken> assertionElementsDecrypter = createDefaultAssertionElementsDecrypter();\n\n\tprivate Converter<AssertionToken, Saml2ResponseValidatorResult> assertionValidator = createDefaultAssertionValidator();\n\n\tprivate Converter<ResponseToken, ? extends AbstractAuthenticationToken> responseAuthenticationConverter = createDefaultResponseAuthenticationConverter();\n\n\tprivate boolean validateResponseAfterAssertions = false;\n\n\tprivate static final Set<String> includeChildStatusCodes = new HashSet<>(\n\t\t\tArrays.asList(StatusCode.REQUESTER, StatusCode.RESPONDER, StatusCode.VERSION_MISMATCH));\n\n\tBaseOpenSamlAuthenticationProvider(OpenSamlOperations saml) {\n\t\tthis.saml = saml;\n\t}\n\n\tvoid setResponseElementsDecrypter(Consumer<ResponseToken> responseElementsDecrypter) {\n\t\tAssert.notNull(responseElementsDecrypter, \"responseElementsDecrypter cannot be null\");\n\t\tthis.responseElementsDecrypter = responseElementsDecrypter;\n\t}\n\n\tvoid setResponseValidator(Converter<ResponseToken, Saml2ResponseValidatorResult> responseValidator) {\n\t\tAssert.notNull(responseValidator, \"responseValidator cannot be null\");\n\t\tthis.responseValidator = responseValidator;\n\t}\n\n\tvoid setAssertionValidator(Converter<AssertionToken, Saml2ResponseValidatorResult> assertionValidator) {\n\t\tAssert.notNull(assertionValidator, \"assertionValidator cannot be null\");\n\t\tthis.assertionValidator = assertionValidator;\n\t}\n\n\tvoid setAssertionElementsDecrypter(Consumer<AssertionToken> assertionDecrypter) {\n\t\tAssert.notNull(assertionDecrypter, \"assertionDecrypter cannot be null\");\n\t\tthis.assertionElementsDecrypter = assertionDecrypter;\n\t}\n\n\tvoid setResponseAuthenticationConverter(\n\t\t\tConverter<ResponseToken, ? extends AbstractAuthenticationToken> responseAuthenticationConverter) {\n\t\tAssert.notNull(responseAuthenticationConverter, \"responseAuthenticationConverter cannot be null\");\n\t\tthis.responseAuthenticationConverter = responseAuthenticationConverter;\n\t}\n\n\tvoid setValidateResponseAfterAssertions(boolean validateResponseAfterAssertions) {\n\t\tthis.validateResponseAfterAssertions = validateResponseAfterAssertions;\n\t}\n\n\tstatic Converter<ResponseToken, Saml2ResponseValidatorResult> createDefaultResponseValidator() {\n\t\treturn (responseToken) -> {\n\t\t\tResponse response = responseToken.getResponse();\n\t\t\tSaml2AuthenticationToken token = responseToken.getToken();\n\t\t\tSaml2ResponseValidatorResult result = Saml2ResponseValidatorResult.success();\n\t\t\tList<String> statusCodes = getStatusCodes(response);\n\t\t\tif (!isSuccess(statusCodes)) {\n\t\t\t\tfor (String statusCode : statusCodes) {\n\t\t\t\t\tString message = String.format(\"Invalid status [%s] for SAML response [%s]\", statusCode,\n\t\t\t\t\t\t\tresponse.getID());\n\t\t\t\t\tresult = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, message));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString inResponseTo = response.getInResponseTo();\n\t\t\tresult = result.concat(validateInResponseTo(token.getAuthenticationRequest(), inResponseTo));\n\n\t\t\tString issuer = issuer(response);\n\t\t\tString destination = response.getDestination();\n\t\t\tString location = token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation();\n\t\t\tif (StringUtils.hasText(destination) && !destination.equals(location)) {\n\t\t\t\tString message = \"Invalid destination [\" + destination + \"] for SAML response [\" + response.getID()\n\t\t\t\t\t\t+ \"]\";\n\t\t\t\tresult = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, message));\n\t\t\t}\n\t\t\tString assertingPartyEntityId = token.getRelyingPartyRegistration()\n\t\t\t\t.getAssertingPartyMetadata()\n\t\t\t\t.getEntityId();\n\t\t\tif (!StringUtils.hasText(issuer) || !issuer.equals(assertingPartyEntityId)) {\n\t\t\t\tString message = String.format(\"Invalid issuer [%s] for SAML response [%s]\", issuer, response.getID());\n\t\t\t\tresult = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, message));\n\t\t\t}\n\t\t\tif (response.getAssertions().isEmpty()) {\n\t\t\t\tresult = result.concat(\n\t\t\t\t\t\tnew Saml2Error(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, \"No assertions found in response.\"));\n\t\t\t}\n\t\t\treturn result;\n\t\t};\n\t}\n\n\tprivate static @Nullable String issuer(Response response) {\n\t\tif (response.getIssuer() == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn response.getIssuer().getValue();\n\t}\n\n\tstatic List<String> getStatusCodes(Response response) {\n\t\tStatus status = response.getStatus();\n\t\tif (status == null) {\n\t\t\treturn List.of(StatusCode.SUCCESS);\n\t\t}\n\t\tStatusCode statusCode = status.getStatusCode();\n\t\tif (statusCode == null) {\n\t\t\treturn List.of(StatusCode.SUCCESS);\n\t\t}\n\t\tString parentStatusCodeValue = statusCode.getValue();\n\t\tAssert.notNull(parentStatusCodeValue, \"Response#Status#StatusCode has not value\");\n\t\tif (!includeChildStatusCodes.contains(parentStatusCodeValue)) {\n\t\t\treturn List.of(parentStatusCodeValue);\n\t\t}\n\t\tStatusCode childStatusCode = statusCode.getStatusCode();\n\t\tif (childStatusCode == null) {\n\t\t\treturn List.of(parentStatusCodeValue);\n\t\t}\n\t\tString childStatusCodeValue = childStatusCode.getValue();\n\t\tif (childStatusCodeValue == null) {\n\t\t\treturn List.of(parentStatusCodeValue);\n\t\t}\n\t\treturn List.of(parentStatusCodeValue, childStatusCodeValue);\n\t}\n\n\tstatic boolean isSuccess(List<String> statusCodes) {\n\t\tif (statusCodes.size() != 1) {\n\t\t\treturn false;\n\t\t}\n\n\t\tString statusCode = statusCodes.get(0);\n\t\treturn StatusCode.SUCCESS.equals(statusCode);\n\t}\n\n\tstatic Saml2ResponseValidatorResult validateInResponseTo(@Nullable AbstractSaml2AuthenticationRequest storedRequest,\n\t\t\t@Nullable String inResponseTo) {\n\t\tif (!StringUtils.hasText(inResponseTo)) {\n\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t}\n\t\tif (storedRequest == null) {\n\t\t\tString message = \"The response contained an InResponseTo attribute [\" + inResponseTo + \"]\"\n\t\t\t\t\t+ \" but no saved authentication request was found\";\n\t\t\treturn Saml2ResponseValidatorResult\n\t\t\t\t.failure(new Saml2Error(Saml2ErrorCodes.INVALID_IN_RESPONSE_TO, message));\n\t\t}\n\t\tif (!inResponseTo.equals(storedRequest.getId())) {\n\t\t\tString message = \"The InResponseTo attribute [\" + inResponseTo + \"] does not match the ID of the \"\n\t\t\t\t\t+ \"authentication request [\" + storedRequest.getId() + \"]\";\n\t\t\treturn Saml2ResponseValidatorResult\n\t\t\t\t.failure(new Saml2Error(Saml2ErrorCodes.INVALID_IN_RESPONSE_TO, message));\n\t\t}\n\t\treturn Saml2ResponseValidatorResult.success();\n\t}\n\n\tstatic Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionValidator() {\n\t\treturn createDefaultAssertionValidatorWithParameters(\n\t\t\t\t(params) -> params.put(SAML2AssertionValidationParameters.CLOCK_SKEW, Duration.ofMinutes(5)));\n\t}\n\n\tstatic Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionValidatorWithParameters(\n\t\t\tConsumer<Map<String, Object>> validationContextParameters) {\n\t\treturn createAssertionValidator(Saml2ErrorCodes.INVALID_ASSERTION,\n\t\t\t\t(assertionToken) -> SAML20AssertionValidators.attributeValidator,\n\t\t\t\t(assertionToken) -> createValidationContext(assertionToken, validationContextParameters));\n\t}\n\n\tstatic Converter<ResponseToken, Saml2Authentication> createDefaultResponseAuthenticationConverter() {\n\t\treturn (responseToken) -> {\n\t\t\tResponse response = responseToken.response;\n\t\t\tSaml2AuthenticationToken token = responseToken.token;\n\t\t\tAssertion assertion = CollectionUtils.firstElement(response.getAssertions());\n\t\t\tAssert.notNull(assertion, \"response must have at least one assertion\");\n\t\t\tSubject subject = assertion.getSubject();\n\t\t\tAssert.notNull(subject, \"response assertion must have a subject\");\n\t\t\tNameID nameId = subject.getNameID();\n\t\t\tAssert.notNull(nameId, \"response assertion subject must have a nameId\");\n\t\t\tString username = nameId.getValue();\n\t\t\tAssert.notNull(username, \"required elements must have a value\");\n\t\t\tMap<String, List<Object>> attributes = getAssertionAttributes(assertion);\n\t\t\tList<String> sessionIndexes = getSessionIndexes(assertion);\n\t\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes,\n\t\t\t\t\tsessionIndexes);\n\t\t\tString registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId();\n\t\t\tprincipal.setRelyingPartyRegistrationId(registrationId);\n\t\t\treturn new Saml2Authentication(principal, token.getSaml2Response(),\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\t};\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\ttry {\n\t\t\tSaml2AuthenticationToken token = (Saml2AuthenticationToken) authentication;\n\t\t\tString serializedResponse = token.getSaml2Response();\n\t\t\tResponse response = parseResponse(serializedResponse);\n\t\t\tprocess(token, response);\n\t\t\tAbstractAuthenticationToken authenticationResponse = this.responseAuthenticationConverter\n\t\t\t\t.convert(new ResponseToken(response, token));\n\t\t\tif (authenticationResponse != null) {\n\t\t\t\tauthenticationResponse.setDetails(authentication.getDetails());\n\t\t\t}\n\t\t\treturn authenticationResponse;\n\t\t}\n\t\tcatch (Saml2AuthenticationException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2AuthenticationException(Saml2Error.internalValidationError(ex.getMessage()), ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn Saml2AuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\tprivate Response parseResponse(String response) throws Saml2Exception, Saml2AuthenticationException {\n\t\ttry {\n\t\t\treturn this.saml.deserialize(response);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2AuthenticationException(Saml2Error.malformedResponseData(ex.getMessage()), ex);\n\t\t}\n\t}\n\n\tprivate void process(Saml2AuthenticationToken token, Response response) {\n\t\tString issuer = issuer(response);\n\t\tthis.logger.debug(LogMessage.format(\"Processing SAML response from %s\", issuer));\n\t\tboolean responseSigned = response.isSigned();\n\n\t\tResponseToken responseToken = new ResponseToken(response, token);\n\t\tSaml2ResponseValidatorResult result = this.responseSignatureValidator.convert(responseToken);\n\t\tif (responseSigned) {\n\t\t\tthis.responseElementsDecrypter.accept(responseToken);\n\t\t}\n\t\telse if (!response.getEncryptedAssertions().isEmpty()) {\n\t\t\tresult = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\"Did not decrypt response [\" + response.getID() + \"] since it is not signed\"));\n\t\t}\n\t\tif (!this.validateResponseAfterAssertions) {\n\t\t\tresult = result.concat(this.responseValidator.convert(responseToken));\n\t\t}\n\t\tboolean allAssertionsSigned = true;\n\t\tfor (Assertion assertion : response.getAssertions()) {\n\t\t\tAssertionToken assertionToken = new AssertionToken(assertion, token);\n\t\t\tresult = result.concat(this.assertionSignatureValidator.convert(assertionToken));\n\t\t\tallAssertionsSigned = allAssertionsSigned && assertion.isSigned();\n\t\t\tif (responseSigned || assertion.isSigned()) {\n\t\t\t\tthis.assertionElementsDecrypter.accept(new AssertionToken(assertion, token));\n\t\t\t}\n\t\t\tresult = result.concat(this.assertionValidator.convert(assertionToken));\n\t\t}\n\t\tif (!responseSigned && !allAssertionsSigned) {\n\t\t\tString description = \"Either the response or one of the assertions is unsigned. \"\n\t\t\t\t\t+ \"Please either sign the response or all of the assertions.\";\n\t\t\tresult = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, description));\n\t\t}\n\t\tif (this.validateResponseAfterAssertions) {\n\t\t\tresult = result.concat(this.responseValidator.convert(responseToken));\n\t\t}\n\t\telse {\n\t\t\tAssertion firstAssertion = CollectionUtils.firstElement(response.getAssertions());\n\t\t\tif (firstAssertion != null && !hasName(firstAssertion)) {\n\t\t\t\tSaml2Error error = new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND,\n\t\t\t\t\t\t\"Assertion [\" + firstAssertion.getID() + \"] is missing a subject\");\n\t\t\t\tresult = result.concat(error);\n\t\t\t}\n\t\t}\n\n\t\tif (result.hasErrors()) {\n\t\t\tCollection<Saml2Error> errors = result.getErrors();\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Found \" + errors.size() + \" validation errors in SAML response [\" + response.getID()\n\t\t\t\t\t\t+ \"]: \" + errors);\n\t\t\t}\n\t\t\telse if (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger\n\t\t\t\t\t.debug(\"Found \" + errors.size() + \" validation errors in SAML response [\" + response.getID() + \"]\");\n\t\t\t}\n\t\t\tSaml2Error first = errors.iterator().next();\n\t\t\tthrow new Saml2AuthenticationException(first);\n\t\t}\n\t\telse {\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(\"Successfully processed SAML Response [\" + response.getID() + \"]\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Converter<ResponseToken, Saml2ResponseValidatorResult> createDefaultResponseSignatureValidator() {\n\t\treturn (responseToken) -> {\n\t\t\tResponse response = responseToken.getResponse();\n\t\t\tRelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration();\n\t\t\tif (response.isSigned()) {\n\t\t\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\t\t\tCollection<Saml2X509Credential> credentials = details.getVerificationX509Credentials();\n\t\t\t\tCollection<Saml2Error> errors = this.saml.withVerificationKeys(credentials)\n\t\t\t\t\t.entityId(details.getEntityId())\n\t\t\t\t\t.verify(response);\n\t\t\t\treturn Saml2ResponseValidatorResult.failure(errors);\n\t\t\t}\n\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t};\n\t}\n\n\tprivate Consumer<ResponseToken> createDefaultResponseElementsDecrypter() {\n\t\treturn (responseToken) -> {\n\t\t\tResponse response = responseToken.getResponse();\n\t\t\tRelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration();\n\t\t\ttry {\n\t\t\t\tthis.saml.withDecryptionKeys(registration.getDecryptionX509Credentials()).decrypt(response);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2AuthenticationException(Saml2Error.decryptionError(ex.getMessage()), ex);\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator() {\n\t\treturn (assertionToken) -> {\n\t\t\tRelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration();\n\t\t\tAssertion assertion = assertionToken.getAssertion();\n\t\t\tif (assertion.isSigned()) {\n\t\t\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\t\t\tCollection<Saml2X509Credential> credentials = details.getVerificationX509Credentials();\n\t\t\t\tCollection<Saml2Error> errors = this.saml.withVerificationKeys(credentials)\n\t\t\t\t\t.entityId(details.getEntityId())\n\t\t\t\t\t.verify(assertion);\n\t\t\t\treturn Saml2ResponseValidatorResult.failure(errors);\n\t\t\t}\n\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t};\n\t}\n\n\tprivate Consumer<AssertionToken> createDefaultAssertionElementsDecrypter() {\n\t\treturn (assertionToken) -> {\n\t\t\tAssertion assertion = assertionToken.getAssertion();\n\t\t\tRelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration();\n\t\t\ttry {\n\t\t\t\tthis.saml.withDecryptionKeys(registration.getDecryptionX509Credentials()).decrypt(assertion);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2AuthenticationException(Saml2Error.decryptionError(ex.getMessage()), ex);\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic boolean hasName(@Nullable Assertion assertion) {\n\t\tif (assertion == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (assertion.getSubject() == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (assertion.getSubject().getNameID() == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn assertion.getSubject().getNameID().getValue() != null;\n\t}\n\n\tstatic Map<String, List<Object>> getAssertionAttributes(Assertion assertion) {\n\t\tMultiValueMap<String, Object> attributeMap = new LinkedMultiValueMap<>();\n\t\tfor (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {\n\t\t\tfor (Attribute attribute : attributeStatement.getAttributes()) {\n\t\t\t\tList<Object> attributeValues = new ArrayList<>();\n\t\t\t\tfor (XMLObject xmlObject : attribute.getAttributeValues()) {\n\t\t\t\t\tObject attributeValue = getXmlObjectValue(xmlObject);\n\t\t\t\t\tif (attributeValue != null) {\n\t\t\t\t\t\tattributeValues.add(attributeValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tString name = attribute.getName();\n\t\t\t\tAssert.notNull(name, \"all attributes must have a name\");\n\t\t\t\tattributeMap.addAll(name, attributeValues);\n\t\t\t}\n\t\t}\n\t\treturn new LinkedHashMap<>(attributeMap); // gh-11785\n\t}\n\n\tstatic List<String> getSessionIndexes(Assertion assertion) {\n\t\tList<String> sessionIndexes = new ArrayList<>();\n\t\tfor (AuthnStatement statement : assertion.getAuthnStatements()) {\n\t\t\tString sessionIndex = statement.getSessionIndex();\n\t\t\tif (sessionIndex != null) {\n\t\t\t\tsessionIndexes.add(sessionIndex);\n\t\t\t}\n\t\t}\n\t\treturn sessionIndexes;\n\t}\n\n\tprivate static @Nullable Object getXmlObjectValue(XMLObject xmlObject) {\n\t\tif (xmlObject instanceof XSAny) {\n\t\t\treturn ((XSAny) xmlObject).getTextContent();\n\t\t}\n\t\tif (xmlObject instanceof XSString) {\n\t\t\treturn ((XSString) xmlObject).getValue();\n\t\t}\n\t\tif (xmlObject instanceof XSInteger) {\n\t\t\treturn ((XSInteger) xmlObject).getValue();\n\t\t}\n\t\tif (xmlObject instanceof XSURI) {\n\t\t\treturn ((XSURI) xmlObject).getURI();\n\t\t}\n\t\tif (xmlObject instanceof XSBoolean) {\n\t\t\tXSBooleanValue xsBooleanValue = ((XSBoolean) xmlObject).getValue();\n\t\t\treturn (xsBooleanValue != null) ? xsBooleanValue.getValue() : null;\n\t\t}\n\t\tif (xmlObject instanceof XSDateTime) {\n\t\t\treturn ((XSDateTime) xmlObject).getValue();\n\t\t}\n\t\treturn xmlObject;\n\t}\n\n\tprivate static Converter<AssertionToken, Saml2ResponseValidatorResult> createAssertionValidator(String errorCode,\n\t\t\tConverter<AssertionToken, SAML20AssertionValidator> validatorConverter,\n\t\t\tConverter<AssertionToken, ValidationContext> contextConverter) {\n\n\t\treturn (assertionToken) -> {\n\t\t\tAssertion assertion = assertionToken.assertion;\n\t\t\tSAML20AssertionValidator validator = validatorConverter.convert(assertionToken);\n\t\t\tValidationContext context = contextConverter.convert(assertionToken);\n\t\t\tResponse response = (Response) Objects.requireNonNull(assertion.getParent());\n\t\t\ttry {\n\t\t\t\tValidationResult result = validator.validate(assertion, context);\n\t\t\t\tif (result == ValidationResult.VALID) {\n\t\t\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tString message = String.format(\"Invalid assertion [%s] for SAML response [%s]: %s\", assertion.getID(),\n\t\t\t\t\t\tresponse.getID(), ex.getMessage());\n\t\t\t\treturn Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message));\n\t\t\t}\n\t\t\tString message = String.format(\"Invalid assertion [%s] for SAML response [%s]: %s\", assertion.getID(),\n\t\t\t\t\tresponse.getID(), context.getValidationFailureMessages());\n\t\t\treturn Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message));\n\t\t};\n\t}\n\n\tprivate static ValidationContext createValidationContext(AssertionToken assertionToken,\n\t\t\tConsumer<Map<String, Object>> paramsConsumer) {\n\t\tSaml2AuthenticationToken token = assertionToken.token;\n\t\tRelyingPartyRegistration relyingPartyRegistration = token.getRelyingPartyRegistration();\n\t\tString audience = relyingPartyRegistration.getEntityId();\n\t\tString recipient = relyingPartyRegistration.getAssertionConsumerServiceLocation();\n\t\tString assertingPartyEntityId = relyingPartyRegistration.getAssertingPartyMetadata().getEntityId();\n\t\tMap<String, Object> params = new HashMap<>();\n\t\tAssertion assertion = assertionToken.getAssertion();\n\t\tif (assertionContainsInResponseTo(assertion)) {\n\t\t\tString requestId = getAuthnRequestId(token.getAuthenticationRequest());\n\t\t\tparams.put(SAML2AssertionValidationParameters.SC_VALID_IN_RESPONSE_TO, requestId);\n\t\t}\n\t\tparams.put(SAML2AssertionValidationParameters.COND_VALID_AUDIENCES, Collections.singleton(audience));\n\t\tparams.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton(recipient));\n\t\tparams.put(SAML2AssertionValidationParameters.VALID_ISSUERS, Collections.singleton(assertingPartyEntityId));\n\t\tparamsConsumer.accept(params);\n\t\treturn new ValidationContext(params);\n\t}\n\n\tprivate static boolean assertionContainsInResponseTo(Assertion assertion) {\n\t\tif (assertion.getSubject() == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (SubjectConfirmation confirmation : assertion.getSubject().getSubjectConfirmations()) {\n\t\t\tSubjectConfirmationData confirmationData = confirmation.getSubjectConfirmationData();\n\t\t\tif (confirmationData == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (StringUtils.hasText(confirmationData.getInResponseTo())) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static @Nullable String getAuthnRequestId(@Nullable AbstractSaml2AuthenticationRequest serialized) {\n\t\treturn (serialized != null) ? serialized.getId() : null;\n\t}\n\n\tstatic class SAML20AssertionValidators {\n\n\t\tprivate static final Collection<ConditionValidator> conditions = new ArrayList<>();\n\n\t\tprivate static final Collection<SubjectConfirmationValidator> subjects = new ArrayList<>();\n\n\t\tprivate static final Collection<StatementValidator> statements = new ArrayList<>();\n\n\t\tstatic {\n\t\t\tconditions.add(new AudienceRestrictionConditionValidator());\n\t\t\tconditions.add(new DelegationRestrictionConditionValidator());\n\t\t\tconditions.add(new ConditionValidator() {\n\t\t\t\t@Override\n\t\t\t\tpublic QName getServicedCondition() {\n\t\t\t\t\treturn OneTimeUse.DEFAULT_ELEMENT_NAME;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic ValidationResult validate(Condition condition, Assertion assertion, ValidationContext context) {\n\t\t\t\t\t// applications should validate their own OneTimeUse conditions\n\t\t\t\t\treturn ValidationResult.VALID;\n\t\t\t\t}\n\t\t\t});\n\t\t\tconditions.add(new ProxyRestrictionConditionValidator());\n\t\t\tsubjects.add(new BearerSubjectConfirmationValidator() {\n\t\t\t\tprotected ValidationResult validateAddress(SubjectConfirmation confirmation, Assertion assertion,\n\t\t\t\t\t\tValidationContext context, boolean required) throws AssertionValidationException {\n\t\t\t\t\treturn ValidationResult.VALID;\n\t\t\t\t}\n\n\t\t\t\tprotected ValidationResult validateAddress(SubjectConfirmationData confirmationData,\n\t\t\t\t\t\tAssertion assertion, ValidationContext context, boolean required)\n\t\t\t\t\t\tthrows AssertionValidationException {\n\t\t\t\t\t// applications should validate their own addresses - gh-7514\n\t\t\t\t\treturn ValidationResult.VALID;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tstatic final SAML20AssertionValidator attributeValidator = new SAML20AssertionValidator(conditions, subjects,\n\t\t\t\tstatements, null, null, null) {\n\t\t\t@Override\n\t\t\tprotected ValidationResult validateSignature(Assertion token, ValidationContext context) {\n\t\t\t\treturn ValidationResult.VALID;\n\t\t\t}\n\t\t};\n\n\t}\n\n\t/**\n\t * A tuple containing an OpenSAML {@link Response} and its associated authentication\n\t * token.\n\t *\n\t * @since 5.4\n\t */\n\tstatic class ResponseToken {\n\n\t\tprivate final Saml2AuthenticationToken token;\n\n\t\tprivate final Response response;\n\n\t\tResponseToken(Response response, Saml2AuthenticationToken token) {\n\t\t\tthis.token = token;\n\t\t\tthis.response = response;\n\t\t}\n\n\t\tResponse getResponse() {\n\t\t\treturn this.response;\n\t\t}\n\n\t\tSaml2AuthenticationToken getToken() {\n\t\t\treturn this.token;\n\t\t}\n\n\t}\n\n\t/**\n\t * A tuple containing an OpenSAML {@link Assertion} and its associated authentication\n\t * token.\n\t *\n\t * @since 5.4\n\t */\n\tstatic class AssertionToken {\n\n\t\tprivate final Saml2AuthenticationToken token;\n\n\t\tprivate final Assertion assertion;\n\n\t\tAssertionToken(Assertion assertion, Saml2AuthenticationToken token) {\n\t\t\tthis.token = token;\n\t\t\tthis.assertion = assertion;\n\t\t}\n\n\t\tAssertion getAssertion() {\n\t\t\treturn this.assertion;\n\t\t}\n\n\t\tSaml2AuthenticationToken getToken() {\n\t\t\treturn this.token;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipal.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.util.Assert;\n\n/**\n * Default implementation of a {@link Saml2AuthenticatedPrincipal}.\n *\n * @author Clement Stoquart\n * @since 5.4\n * @deprecated Please use {@link Saml2ResponseAssertionAccessor}\n */\n@Deprecated\n@NullUnmarked\npublic class DefaultSaml2AuthenticatedPrincipal implements Saml2AuthenticatedPrincipal, Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7601324133433139825L;\n\n\tprivate final String name;\n\n\tprivate final Map<String, List<Object>> attributes;\n\n\tprivate final List<String> sessionIndexes;\n\n\tprivate String registrationId;\n\n\tpublic DefaultSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes) {\n\t\tthis(name, attributes, Collections.emptyList());\n\t}\n\n\tpublic DefaultSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes,\n\t\t\tList<String> sessionIndexes) {\n\t\tAssert.notNull(name, \"name cannot be null\");\n\t\tAssert.notNull(attributes, \"attributes cannot be null\");\n\t\tAssert.notNull(sessionIndexes, \"sessionIndexes cannot be null\");\n\t\tthis.name = name;\n\t\tthis.attributes = attributes;\n\t\tthis.sessionIndexes = sessionIndexes;\n\t}\n\n\tpublic DefaultSaml2AuthenticatedPrincipal(String name, Saml2ResponseAssertionAccessor assertion) {\n\t\tthis.name = name;\n\t\tthis.attributes = assertion.getAttributes();\n\t\tthis.sessionIndexes = assertion.getSessionIndexes();\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\t@Override\n\tpublic Map<String, List<Object>> getAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\t@Override\n\tpublic List<String> getSessionIndexes() {\n\t\treturn this.sessionIndexes;\n\t}\n\n\t@Override\n\tpublic String getRelyingPartyRegistrationId() {\n\t\treturn this.registrationId;\n\t}\n\n\tpublic void setRelyingPartyRegistrationId(String registrationId) {\n\t\tAssert.notNull(registrationId, \"relyingPartyRegistrationId cannot be null\");\n\t\tthis.registrationId = registrationId;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object object) {\n\t\tif (this == object) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(object instanceof DefaultSaml2AuthenticatedPrincipal that)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(this.name, that.name) && Objects.equals(this.registrationId, that.registrationId);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.name, this.registrationId);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\ninterface OpenSamlOperations {\n\n\t<T extends XMLObject> T build(QName elementName);\n\n\t<T extends XMLObject> T deserialize(String serialized);\n\n\t<T extends XMLObject> T deserialize(InputStream serialized);\n\n\tSerializationConfigurer<?> serialize(XMLObject object);\n\n\tSerializationConfigurer<?> serialize(Element element);\n\n\tSignatureConfigurer<?> withSigningKeys(Collection<Saml2X509Credential> credentials);\n\n\tVerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials);\n\n\tDecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials);\n\n\tinterface SerializationConfigurer<B extends SerializationConfigurer<B>> {\n\n\t\tB prettyPrint(boolean pretty);\n\n\t\tString serialize();\n\n\t}\n\n\tinterface SignatureConfigurer<B extends SignatureConfigurer<B>> {\n\n\t\tB algorithms(List<String> algs);\n\n\t\t<O extends SignableXMLObject> O sign(O object);\n\n\t\tMap<String, String> sign(Map<String, String> params);\n\n\t}\n\n\tinterface VerificationConfigurer {\n\n\t\tVerificationConfigurer entityId(String entityId);\n\n\t\tCollection<Saml2Error> verify(SignableXMLObject signable);\n\n\t\tCollection<Saml2Error> verify(VerificationConfigurer.RedirectParameters parameters);\n\n\t\tfinal class RedirectParameters {\n\n\t\t\tprivate final String id;\n\n\t\t\tprivate final Issuer issuer;\n\n\t\t\tprivate final String algorithm;\n\n\t\t\tprivate final byte @Nullable [] signature;\n\n\t\t\tprivate final byte[] content;\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"SAML request's ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"SAML request's Issuer cannot be null\");\n\t\t\t\tthis.id = request.getID();\n\t\t\t\tthis.issuer = request.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_REQUEST, relayState, queryParams);\n\t\t\t}\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"SAML response's ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"SAML response's Issuer cannot be null\");\n\t\t\t\tthis.id = response.getID();\n\t\t\t\tthis.issuer = response.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_RESPONSE, relayState, queryParams);\n\t\t\t}\n\n\t\t\tstatic byte[] getContent(String samlObject, @Nullable String relayState,\n\t\t\t\t\tfinal Map<String, String> queryParams) {\n\t\t\t\tif (Objects.nonNull(relayState)) {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s&%s=%s\", samlObject, queryParams.get(samlObject),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.RELAY_STATE, queryParams.get(Saml2ParameterNames.RELAY_STATE),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.SIG_ALG, queryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s\", samlObject, queryParams.get(samlObject), Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\t\t\tqueryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getId() {\n\t\t\t\treturn this.id;\n\t\t\t}\n\n\t\t\tIssuer getIssuer() {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\tbyte[] getContent() {\n\t\t\t\treturn this.content;\n\t\t\t}\n\n\t\t\tString getAlgorithm() {\n\t\t\t\treturn this.algorithm;\n\t\t\t}\n\n\t\t\tbyte @Nullable [] getSignature() {\n\t\t\t\treturn this.signature;\n\t\t\t}\n\n\t\t\tboolean hasSignature() {\n\t\t\t\treturn this.signature != null;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface DecryptionConfigurer {\n\n\t\tvoid decrypt(XMLObject object);\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AssertionAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serial;\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.util.Assert;\n\n/**\n * An authentication based off of a SAML 2.0 Assertion\n *\n * @author Josh Cummings\n * @since 7.0\n * @see Saml2ResponseAssertionAccessor\n * @see Saml2ResponseAssertion\n */\npublic class Saml2AssertionAuthentication extends Saml2Authentication {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -4194323643788693205L;\n\n\tprivate final Saml2ResponseAssertionAccessor assertion;\n\n\tprivate final String relyingPartyRegistrationId;\n\n\tpublic Saml2AssertionAuthentication(Saml2ResponseAssertionAccessor assertion,\n\t\t\tCollection<? extends GrantedAuthority> authorities, String relyingPartyRegistrationId) {\n\t\tsuper(assertion, assertion.getResponseValue(), authorities);\n\t\tthis.assertion = assertion;\n\t\tthis.relyingPartyRegistrationId = relyingPartyRegistrationId;\n\t}\n\n\tpublic Saml2AssertionAuthentication(Object principal, Saml2ResponseAssertionAccessor assertion,\n\t\t\tCollection<? extends GrantedAuthority> authorities, String relyingPartyRegistrationId) {\n\t\tsuper(principal, assertion.getResponseValue(), authorities);\n\t\tthis.assertion = assertion;\n\t\tthis.relyingPartyRegistrationId = relyingPartyRegistrationId;\n\t\tsetAuthenticated(true);\n\t}\n\n\tprotected Saml2AssertionAuthentication(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.assertion = builder.assertion;\n\t\tthis.relyingPartyRegistrationId = builder.relyingPartyRegistrationId;\n\t}\n\n\t@Override\n\tpublic Saml2ResponseAssertionAccessor getCredentials() {\n\t\treturn this.assertion;\n\t}\n\n\tpublic String getRelyingPartyRegistrationId() {\n\t\treturn this.relyingPartyRegistrationId;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link Saml2AssertionAuthentication} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends Saml2Authentication.Builder<B> {\n\n\t\tprivate Saml2ResponseAssertionAccessor assertion;\n\n\t\tprivate String relyingPartyRegistrationId;\n\n\t\tprotected Builder(Saml2AssertionAuthentication token) {\n\t\t\tsuper(token);\n\t\t\tthis.assertion = token.assertion;\n\t\t\tthis.relyingPartyRegistrationId = token.relyingPartyRegistrationId;\n\t\t}\n\n\t\t/**\n\t\t * Use these credentials. They must be of type\n\t\t * {@link Saml2ResponseAssertionAccessor}.\n\t\t * @param credentials the credentials to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object credentials) {\n\t\t\tAssert.isInstanceOf(Saml2ResponseAssertionAccessor.class, credentials,\n\t\t\t\t\t\"credentials must be of type Saml2ResponseAssertionAccessor\");\n\t\t\tsaml2Response(((Saml2ResponseAssertionAccessor) credentials).getResponseValue());\n\t\t\tthis.assertion = (Saml2ResponseAssertionAccessor) credentials;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t/**\n\t\t * Use this registration id\n\t\t * @param relyingPartyRegistrationId the\n\t\t * {@link RelyingPartyRegistration#getRegistrationId} to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic B relyingPartyRegistrationId(String relyingPartyRegistrationId) {\n\t\t\tthis.relyingPartyRegistrationId = relyingPartyRegistrationId;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Saml2AssertionAuthentication build() {\n\t\t\treturn new Saml2AssertionAuthentication(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticatedPrincipal.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Saml2 representation of an {@link AuthenticatedPrincipal}.\n *\n * @author Clement Stoquart\n * @since 5.2.2\n * @deprecated Please use\n * {@link Saml2AssertionAuthentication#getRelyingPartyRegistrationId()} and\n * {@link Saml2ResponseAssertionAccessor} instead\n */\n@Deprecated\n@NullUnmarked\npublic interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {\n\n\t/**\n\t * Get the first value of Saml2 token attribute by name\n\t * @param name the name of the attribute\n\t * @param <A> the type of the attribute\n\t * @return the first attribute value or {@code null} otherwise\n\t * @since 5.4\n\t */\n\tdefault <A> A getFirstAttribute(String name) {\n\t\tList<A> values = getAttribute(name);\n\t\treturn CollectionUtils.firstElement(values);\n\t}\n\n\t/**\n\t * Get the Saml2 token attribute by name\n\t * @param name the name of the attribute\n\t * @param <A> the type of the attribute\n\t * @return the attribute or {@code null} otherwise\n\t * @since 5.4\n\t */\n\tdefault <A> List<A> getAttribute(String name) {\n\t\treturn (List<A>) getAttributes().get(name);\n\t}\n\n\t/**\n\t * Get the Saml2 token attributes\n\t * @return the Saml2 token attributes\n\t * @since 5.4\n\t */\n\tdefault Map<String, List<Object>> getAttributes() {\n\t\treturn Collections.emptyMap();\n\t}\n\n\t/**\n\t * Get the {@link RelyingPartyRegistration} identifier\n\t * @return the {@link RelyingPartyRegistration} identifier\n\t * @since 5.6\n\t */\n\tdefault String getRelyingPartyRegistrationId() {\n\t\treturn null;\n\t}\n\n\tdefault List<String> getSessionIndexes() {\n\t\treturn Collections.emptyList();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Authentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serial;\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link AbstractAuthenticationToken} that represents an\n * authenticated SAML 2.0 {@link Authentication}.\n * <p>\n * The {@link Authentication} associates valid SAML assertion data with a Spring Security\n * authentication object The complete assertion is contained in the object in String\n * format, {@link Saml2Authentication#getSaml2Response()}\n *\n * @since 5.2\n * @see AbstractAuthenticationToken\n */\npublic class Saml2Authentication extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 405897702378720477L;\n\n\tprivate final Object principal;\n\n\tprivate final String saml2Response;\n\n\t/**\n\t * Construct a {@link Saml2Authentication} using the provided parameters\n\t * @param principal the logged in user\n\t * @param saml2Response the SAML 2.0 response used to authenticate the user\n\t * @param authorities the authorities for the logged in user\n\t */\n\tpublic Saml2Authentication(AuthenticatedPrincipal principal, String saml2Response,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\tAssert.hasText(saml2Response, \"saml2Response cannot be null\");\n\t\tthis.principal = principal;\n\t\tthis.saml2Response = saml2Response;\n\t\tsetAuthenticated(true);\n\t}\n\n\tpublic Saml2Authentication(Object principal, String saml2Response,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tthis.principal = principal;\n\t\tthis.saml2Response = saml2Response;\n\t\tsetAuthenticated(true);\n\t}\n\n\tSaml2Authentication(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.principal = builder.principal;\n\t\tthis.saml2Response = builder.saml2Response;\n\t}\n\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t/**\n\t * Returns the SAML response object, as decoded XML. May contain encrypted elements\n\t * @return string representation of the SAML Response XML object\n\t */\n\tpublic String getSaml2Response() {\n\t\treturn this.saml2Response;\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn getSaml2Response();\n\t}\n\n\tabstract static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate Object principal;\n\n\t\tString saml2Response;\n\n\t\tBuilder(Saml2Authentication token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.principal;\n\t\t\tthis.saml2Response = token.saml2Response;\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\tvoid saml2Response(String saml2Response) {\n\t\t\tthis.saml2Response = saml2Response;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.util.Assert;\n\n/**\n * This exception is thrown for all SAML 2.0 related {@link Authentication} errors.\n *\n * <p>\n * There are a number of scenarios where an error may occur, for example:\n * <ul>\n * <li>The response or assertion request is missing or malformed</li>\n * <li>Missing or invalid subject</li>\n * <li>Missing or invalid signatures</li>\n * <li>The time period validation for the assertion fails</li>\n * <li>One of the assertion conditions was not met</li>\n * <li>Decryption failed</li>\n * <li>Unable to locate a subject identifier, commonly known as username</li>\n * </ul>\n *\n * @since 5.2\n */\npublic class Saml2AuthenticationException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2996886630890949105L;\n\n\tprivate final Saml2Error error;\n\n\t/**\n\t * Constructs a {@code Saml2AuthenticationException} using the provided parameters.\n\t * @param error the {@link Saml2Error SAML 2.0 Error}\n\t */\n\tpublic Saml2AuthenticationException(Saml2Error error) {\n\t\tthis(error, defaultMessage(error.getDescription(), error.getErrorCode()));\n\t}\n\n\t/**\n\t * Constructs a {@code Saml2AuthenticationException} using the provided parameters.\n\t * @param error the {@link Saml2Error SAML 2.0 Error}\n\t * @param cause the root cause\n\t */\n\tpublic Saml2AuthenticationException(Saml2Error error, Throwable cause) {\n\t\tthis(error, defaultMessage((cause != null) ? cause.getMessage() : error.getDescription(), error.getErrorCode()),\n\t\t\t\tcause);\n\t}\n\n\t/**\n\t * Constructs a {@code Saml2AuthenticationException} using the provided parameters.\n\t * @param error the {@link Saml2Error SAML 2.0 Error}\n\t * @param message the detail message\n\t */\n\tpublic Saml2AuthenticationException(Saml2Error error, @Nullable String message) {\n\t\tsuper(defaultMessage(message, error.getErrorCode()));\n\t\tthis.error = error;\n\t}\n\n\t/**\n\t * Constructs a {@code Saml2AuthenticationException} using the provided parameters.\n\t * @param error the {@link Saml2Error SAML 2.0 Error}\n\t * @param message the detail message\n\t * @param cause the root cause\n\t */\n\tpublic Saml2AuthenticationException(Saml2Error error, @Nullable String message, Throwable cause) {\n\t\tsuper(defaultMessage(message, error.getErrorCode()), cause);\n\t\tAssert.notNull(error, \"error cannot be null\");\n\t\tthis.error = error;\n\t}\n\n\tprivate static String defaultMessage(@Nullable String message, String errorCode) {\n\t\treturn (message != null) ? message : errorCode;\n\t}\n\n\t/**\n\t * Get the associated {@link Saml2Error}\n\t * @return the associated {@link Saml2Error}\n\t */\n\tpublic Saml2Error getSaml2Error() {\n\t\treturn this.error;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuffer sb = new StringBuffer(\"Saml2AuthenticationException{\");\n\t\tsb.append(\"error=\").append(this.error);\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.util.Collections;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;\nimport org.springframework.util.Assert;\n\n/**\n * Represents an incoming SAML 2.0 response containing an assertion that has not been\n * validated. {@link Saml2AuthenticationToken#isAuthenticated()} will always return false.\n *\n * @author Filip Hanik\n * @author Josh Cummings\n * @since 5.2\n */\npublic class Saml2AuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate final RelyingPartyRegistration relyingPartyRegistration;\n\n\tprivate final String saml2Response;\n\n\tprivate final @Nullable AbstractSaml2AuthenticationRequest authenticationRequest;\n\n\t/**\n\t * Creates a {@link Saml2AuthenticationToken} with the provided parameters.\n\t *\n\t * Note that the given {@link RelyingPartyRegistration} should have all its templates\n\t * resolved at this point. See {@link Saml2WebSsoAuthenticationFilter} for an example\n\t * of performing that resolution.\n\t * @param relyingPartyRegistration the resolved {@link RelyingPartyRegistration} to\n\t * use\n\t * @param saml2Response the SAML 2.0 response to authenticate\n\t * @param authenticationRequest the {@code AuthNRequest} sent to the asserting party\n\t *\n\t * @since 5.6\n\t */\n\tpublic Saml2AuthenticationToken(RelyingPartyRegistration relyingPartyRegistration, String saml2Response,\n\t\t\t@Nullable AbstractSaml2AuthenticationRequest authenticationRequest) {\n\t\tsuper(Collections.emptyList());\n\t\tAssert.notNull(relyingPartyRegistration, \"relyingPartyRegistration cannot be null\");\n\t\tAssert.notNull(saml2Response, \"saml2Response cannot be null\");\n\t\tthis.relyingPartyRegistration = relyingPartyRegistration;\n\t\tthis.saml2Response = saml2Response;\n\t\tthis.authenticationRequest = authenticationRequest;\n\t}\n\n\t/**\n\t * Creates a {@link Saml2AuthenticationToken} with the provided parameters\n\t *\n\t * Note that the given {@link RelyingPartyRegistration} should have all its templates\n\t * resolved at this point. See {@link Saml2WebSsoAuthenticationFilter} for an example\n\t * of performing that resolution.\n\t * @param relyingPartyRegistration the resolved {@link RelyingPartyRegistration} to\n\t * use\n\t * @param saml2Response the SAML 2.0 response to authenticate\n\t *\n\t * @since 5.4\n\t */\n\tpublic Saml2AuthenticationToken(RelyingPartyRegistration relyingPartyRegistration, String saml2Response) {\n\t\tthis(relyingPartyRegistration, saml2Response, null);\n\t}\n\n\t/**\n\t * Returns the decoded and inflated SAML 2.0 Response XML object as a string\n\t * @return decoded and inflated XML data as a {@link String}\n\t */\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn getSaml2Response();\n\t}\n\n\t/**\n\t * Always returns null.\n\t * @return null\n\t */\n\t@Override\n\tpublic @Nullable Object getPrincipal() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get the resolved {@link RelyingPartyRegistration} associated with the request\n\t * @return the resolved {@link RelyingPartyRegistration}\n\t * @since 5.4\n\t */\n\tpublic RelyingPartyRegistration getRelyingPartyRegistration() {\n\t\treturn this.relyingPartyRegistration;\n\t}\n\n\t/**\n\t * Returns inflated and decoded XML representation of the SAML 2 Response\n\t * @return inflated and decoded XML representation of the SAML 2 Response\n\t */\n\tpublic String getSaml2Response() {\n\t\treturn this.saml2Response;\n\t}\n\n\t/**\n\t * @return false\n\t */\n\t@Override\n\tpublic boolean isAuthenticated() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * The state of this object cannot be changed. Will always throw an exception\n\t * @param authenticated ignored\n\t */\n\t@Override\n\tpublic void setAuthenticated(boolean authenticated) {\n\t\tthrow new IllegalArgumentException();\n\t}\n\n\t/**\n\t * Returns the authentication request sent to the assertion party or {@code null} if\n\t * no authentication request is present\n\t * @return the authentication request sent to the assertion party\n\t * @since 5.6\n\t */\n\tpublic @Nullable AbstractSaml2AuthenticationRequest getAuthenticationRequest() {\n\t\treturn this.authenticationRequest;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.util.Assert;\n\n/**\n * Data holder for information required to send an {@code AuthNRequest} over a POST\n * binding from the service provider to the identity provider\n * https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf\n * (line 2031)\n *\n * @since 5.3\n * @see org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver\n */\npublic class Saml2PostAuthenticationRequest extends AbstractSaml2AuthenticationRequest {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -6412064305715642123L;\n\n\tSaml2PostAuthenticationRequest(String samlRequest, @Nullable String relayState, String authenticationRequestUri,\n\t\t\t@Nullable String relyingPartyRegistrationId, @Nullable String id) {\n\t\tsuper(samlRequest, relayState, authenticationRequestUri, relyingPartyRegistrationId, id);\n\t}\n\n\t/**\n\t * @return {@link Saml2MessageBinding#POST}\n\t */\n\t@Override\n\tpublic Saml2MessageBinding getBinding() {\n\t\treturn Saml2MessageBinding.POST;\n\t}\n\n\t/**\n\t * Constructs a {@link Builder} from a {@link RelyingPartyRegistration} object.\n\t * @param registration a relying party registration\n\t * @return a modifiable builder object\n\t * @since 5.7\n\t */\n\tpublic static Builder withRelyingPartyRegistration(RelyingPartyRegistration registration) {\n\t\tString location = registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation();\n\t\treturn new Builder(registration).authenticationRequestUri(location);\n\t}\n\n\t/**\n\t * Builder class for a {@link Saml2PostAuthenticationRequest} object.\n\t */\n\tpublic static final class Builder extends AbstractSaml2AuthenticationRequest.Builder<Builder> {\n\n\t\tprivate Builder(RelyingPartyRegistration registration) {\n\t\t\tsuper(registration);\n\t\t}\n\n\t\t/**\n\t\t * Constructs an immutable {@link Saml2PostAuthenticationRequest} object.\n\t\t * @return an immutable {@link Saml2PostAuthenticationRequest} object.\n\t\t */\n\t\tpublic Saml2PostAuthenticationRequest build() {\n\t\t\tAssert.notNull(this.samlRequest, \"samlRequest cannot be null\");\n\t\t\tAssert.notNull(this.authenticationRequestUri, \"authenticationRequestUri cannot be null\");\n\t\t\treturn new Saml2PostAuthenticationRequest(this.samlRequest, this.relayState, this.authenticationRequestUri,\n\t\t\t\t\tthis.relyingPartyRegistrationId, this.id);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.util.Assert;\n\n/**\n * Data holder for information required to send an {@code AuthNRequest} over a REDIRECT\n * binding from the service provider to the identity provider\n * https://www.oasis-open.org/committees/download.php/35711/sstc-saml-core-errata-2.0-wd-06-diff.pdf\n * (line 2031)\n *\n * @since 5.3\n * @see org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver\n */\npublic final class Saml2RedirectAuthenticationRequest extends AbstractSaml2AuthenticationRequest {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 6476874109764554798L;\n\n\tprivate final @Nullable String sigAlg;\n\n\tprivate final @Nullable String signature;\n\n\tprivate Saml2RedirectAuthenticationRequest(String samlRequest, @Nullable String sigAlg, @Nullable String signature,\n\t\t\t@Nullable String relayState, String authenticationRequestUri, String relyingPartyRegistrationId,\n\t\t\t@Nullable String id) {\n\t\tsuper(samlRequest, relayState, authenticationRequestUri, relyingPartyRegistrationId, id);\n\t\tthis.sigAlg = sigAlg;\n\t\tthis.signature = signature;\n\t}\n\n\t/**\n\t * Returns the SigAlg value for {@link Saml2MessageBinding#REDIRECT} requests\n\t * @return the SigAlg value\n\t */\n\tpublic @Nullable String getSigAlg() {\n\t\treturn this.sigAlg;\n\t}\n\n\t/**\n\t * Returns the Signature value for {@link Saml2MessageBinding#REDIRECT} requests\n\t * @return the Signature value\n\t */\n\tpublic @Nullable String getSignature() {\n\t\treturn this.signature;\n\t}\n\n\t/**\n\t * @return {@link Saml2MessageBinding#REDIRECT}\n\t */\n\t@Override\n\tpublic Saml2MessageBinding getBinding() {\n\t\treturn Saml2MessageBinding.REDIRECT;\n\t}\n\n\t/**\n\t * Constructs a {@link Saml2PostAuthenticationRequest.Builder} from a\n\t * {@link RelyingPartyRegistration} object.\n\t * @param registration a relying party registration\n\t * @return a modifiable builder object\n\t * @since 5.7\n\t */\n\tpublic static Builder withRelyingPartyRegistration(RelyingPartyRegistration registration) {\n\t\tString location = registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation();\n\t\treturn new Builder(registration).authenticationRequestUri(location);\n\t}\n\n\t/**\n\t * Builder class for a {@link Saml2RedirectAuthenticationRequest} object.\n\t */\n\tpublic static final class Builder extends AbstractSaml2AuthenticationRequest.Builder<Builder> {\n\n\t\tprivate @Nullable String sigAlg;\n\n\t\tprivate @Nullable String signature;\n\n\t\tprivate Builder(RelyingPartyRegistration registration) {\n\t\t\tsuper(registration);\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code SigAlg} parameter that will accompany this AuthNRequest\n\t\t * @param sigAlg the SigAlg parameter value.\n\t\t * @return this object\n\t\t */\n\t\tpublic Builder sigAlg(@Nullable String sigAlg) {\n\t\t\tthis.sigAlg = sigAlg;\n\t\t\treturn _this();\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code Signature} parameter that will accompany this AuthNRequest\n\t\t * @param signature the Signature parameter value.\n\t\t * @return this object\n\t\t */\n\t\tpublic Builder signature(@Nullable String signature) {\n\t\t\tthis.signature = signature;\n\t\t\treturn _this();\n\t\t}\n\n\t\t/**\n\t\t * Constructs an immutable {@link Saml2RedirectAuthenticationRequest} object.\n\t\t * @return an immutable {@link Saml2RedirectAuthenticationRequest} object.\n\t\t */\n\t\tpublic Saml2RedirectAuthenticationRequest build() {\n\t\t\tAssert.notNull(this.samlRequest, \"samlRequest cannot be null\");\n\t\t\tAssert.notNull(this.authenticationRequestUri, \"authenticationRequestUri cannot be null\");\n\t\t\tAssert.notNull(this.relyingPartyRegistrationId, \"relyingPartyRegistrationId cannot be null\");\n\t\t\treturn new Saml2RedirectAuthenticationRequest(this.samlRequest, this.sigAlg, this.signature,\n\t\t\t\t\tthis.relayState, this.authenticationRequestUri, this.relyingPartyRegistrationId, this.id);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2ResponseAssertion.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serial;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * An OpenSAML-based implementation of {@link Saml2ResponseAssertionAccessor}\n *\n * @author Josh Cummings\n * @since 7.0\n */\npublic class Saml2ResponseAssertion implements Saml2ResponseAssertionAccessor {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7505233045395024212L;\n\n\tprivate final String responseValue;\n\n\tprivate final String nameId;\n\n\tprivate final List<String> sessionIndexes;\n\n\tprivate final Map<String, List<Object>> attributes;\n\n\tSaml2ResponseAssertion(String responseValue, String nameId, List<String> sessionIndexes,\n\t\t\tMap<String, List<Object>> attributes) {\n\t\tAssert.notNull(responseValue, \"response value cannot be null\");\n\t\tAssert.notNull(nameId, \"nameId cannot be null\");\n\t\tAssert.notNull(sessionIndexes, \"sessionIndexes cannot be null\");\n\t\tAssert.notNull(attributes, \"attributes cannot be null\");\n\t\tthis.responseValue = responseValue;\n\t\tthis.nameId = nameId;\n\t\tthis.sessionIndexes = sessionIndexes;\n\t\tthis.attributes = attributes;\n\t}\n\n\tpublic static Builder withResponseValue(String responseValue) {\n\t\treturn new Builder(responseValue);\n\t}\n\n\t@Override\n\tpublic String getNameId() {\n\t\treturn this.nameId;\n\t}\n\n\t@Override\n\tpublic List<String> getSessionIndexes() {\n\t\treturn this.sessionIndexes;\n\t}\n\n\t@Override\n\tpublic Map<String, List<Object>> getAttributes() {\n\t\treturn this.attributes;\n\t}\n\n\t@Override\n\tpublic String getResponseValue() {\n\t\treturn this.responseValue;\n\t}\n\n\tpublic static final class Builder {\n\n\t\tprivate final String responseValue;\n\n\t\tprivate @Nullable String nameId;\n\n\t\tprivate List<String> sessionIndexes = List.of();\n\n\t\tprivate Map<String, List<Object>> attributes = Map.of();\n\n\t\tBuilder(String responseValue) {\n\t\t\tthis.responseValue = responseValue;\n\t\t}\n\n\t\tpublic Builder nameId(String nameId) {\n\t\t\tthis.nameId = nameId;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder sessionIndexes(List<String> sessionIndexes) {\n\t\t\tthis.sessionIndexes = sessionIndexes;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder attributes(Map<String, List<Object>> attributes) {\n\t\t\tthis.attributes = attributes;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Saml2ResponseAssertion build() {\n\t\t\tAssert.notNull(this.nameId, \"nameId cannot be null\");\n\t\t\treturn new Saml2ResponseAssertion(this.responseValue, this.nameId, this.sessionIndexes, this.attributes);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2ResponseAssertionAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.CollectionUtils;\n\n/**\n * An interface that represents key details from a SAML 2.0 Assertion\n *\n * @author Josh Cummings\n * @since 7.0\n * @see Saml2ResponseAssertion\n */\npublic interface Saml2ResponseAssertionAccessor extends Serializable {\n\n\tString getNameId();\n\n\tList<String> getSessionIndexes();\n\n\t/**\n\t * Get the first value of Saml2 token attribute by name\n\t * @param name the name of the attribute\n\t * @param <A> the type of the attribute\n\t * @return the first attribute value or {@code null} otherwise\n\t */\n\t@Nullable default <A> A getFirstAttribute(String name) {\n\t\tList<A> values = getAttribute(name);\n\t\treturn CollectionUtils.firstElement(values);\n\t}\n\n\t/**\n\t * Get the Saml2 token attribute by name\n\t * @param name the name of the attribute\n\t * @param <A> the type of the attribute\n\t * @return the attribute or {@code null} otherwise\n\t */\n\t@Nullable default <A> List<A> getAttribute(String name) {\n\t\treturn (List<A>) getAttributes().get(name);\n\t}\n\n\tMap<String, List<Object>> getAttributes();\n\n\tString getResponseValue();\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/BaseOpenSamlLogoutRequestValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.util.Collection;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlOperations.VerificationConfigurer;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlOperations.VerificationConfigurer.RedirectParameters;\nimport org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.util.Assert;\n\nclass BaseOpenSamlLogoutRequestValidator implements Saml2LogoutRequestValidator {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final OpenSamlOperations saml;\n\n\tBaseOpenSamlLogoutRequestValidator(OpenSamlOperations saml) {\n\t\tthis.saml = saml;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Saml2LogoutValidatorResult validate(Saml2LogoutRequestValidatorParameters parameters) {\n\t\tSaml2LogoutRequest request = parameters.getLogoutRequest();\n\t\tRelyingPartyRegistration registration = parameters.getRelyingPartyRegistration();\n\t\tAuthentication authentication = parameters.getAuthentication();\n\t\tLogoutRequest logoutRequest = this.saml.deserialize(Saml2Utils.withEncoded(request.getSamlRequest())\n\t\t\t.inflate(request.getBinding() == Saml2MessageBinding.REDIRECT)\n\t\t\t.decode());\n\t\treturn Saml2LogoutValidatorResult.withErrors()\n\t\t\t.errors(verifySignature(request, logoutRequest, registration))\n\t\t\t.errors(validateRequest(logoutRequest, registration, authentication))\n\t\t\t.build();\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> verifySignature(Saml2LogoutRequest request, LogoutRequest logoutRequest,\n\t\t\tRelyingPartyRegistration registration) {\n\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\tCollection<Saml2X509Credential> credentials = details.getVerificationX509Credentials();\n\t\tVerificationConfigurer verify = this.saml.withVerificationKeys(credentials).entityId(details.getEntityId());\n\t\treturn (errors) -> {\n\t\t\tif (logoutRequest.isSigned()) {\n\t\t\t\terrors.addAll(verify.verify(logoutRequest));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tString parametersQuery = request.getParametersQuery();\n\t\t\t\tAssert.notNull(parametersQuery, \"parametersQuery cannot be null for redirect binding\");\n\t\t\t\tRedirectParameters params = new RedirectParameters(request.getParameters(), parametersQuery,\n\t\t\t\t\t\tlogoutRequest);\n\t\t\t\terrors.addAll(verify.verify(params));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateRequest(LogoutRequest request,\n\t\t\tRelyingPartyRegistration registration, @Nullable Authentication authentication) {\n\t\treturn (errors) -> {\n\t\t\tvalidateIssuer(request, registration).accept(errors);\n\t\t\tvalidateDestination(request, registration).accept(errors);\n\t\t\tvalidateSubject(request, registration, authentication).accept(errors);\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateIssuer(LogoutRequest request,\n\t\t\tRelyingPartyRegistration registration) {\n\t\treturn (errors) -> {\n\t\t\tif (request.getIssuer() == null) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, \"Failed to find issuer in LogoutRequest\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString issuer = request.getIssuer().getValue();\n\t\t\tif (!registration.getAssertingPartyMetadata().getEntityId().equals(issuer)) {\n\t\t\t\terrors\n\t\t\t\t\t.add(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, \"Failed to match issuer to configured issuer\"));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateDestination(LogoutRequest request,\n\t\t\tRelyingPartyRegistration registration) {\n\t\treturn (errors) -> {\n\t\t\tif (request.getDestination() == null) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION,\n\t\t\t\t\t\t\"Failed to find destination in LogoutRequest\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString destination = request.getDestination();\n\t\t\tif (!destination.equals(registration.getSingleLogoutServiceLocation())) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION,\n\t\t\t\t\t\t\"Failed to match destination to configured destination\"));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateSubject(LogoutRequest request,\n\t\t\tRelyingPartyRegistration registration, @Nullable Authentication authentication) {\n\t\treturn (errors) -> {\n\t\t\tif (authentication == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tNameID nameId = getNameId(request, registration);\n\t\t\tif (nameId == null) {\n\t\t\t\terrors\n\t\t\t\t\t.add(new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND, \"Failed to find subject in LogoutRequest\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvalidateNameId(nameId, authentication, errors);\n\t\t};\n\t}\n\n\tprivate @Nullable NameID getNameId(LogoutRequest request, RelyingPartyRegistration registration) {\n\t\tthis.saml.withDecryptionKeys(registration.getDecryptionX509Credentials()).decrypt(request);\n\t\treturn request.getNameID();\n\t}\n\n\tprivate void validateNameId(NameID nameId, Authentication authentication, Collection<Saml2Error> errors) {\n\t\tString name = (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor assertion)\n\t\t\t\t? assertion.getNameId() : authentication.getName();\n\t\tif (!name.equals(nameId.getValue())) {\n\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\"Failed to match subject in LogoutRequest with currently logged in user\"));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/BaseOpenSamlLogoutResponseValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.util.Collection;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.saml.saml2.core.LogoutResponse;\nimport org.opensaml.saml.saml2.core.StatusCode;\n\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlOperations.VerificationConfigurer;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlOperations.VerificationConfigurer.RedirectParameters;\nimport org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.util.Assert;\n\nclass BaseOpenSamlLogoutResponseValidator implements Saml2LogoutResponseValidator {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final OpenSamlOperations saml;\n\n\tBaseOpenSamlLogoutResponseValidator(OpenSamlOperations saml) {\n\t\tthis.saml = saml;\n\t}\n\n\t@Override\n\tpublic Saml2LogoutValidatorResult validate(Saml2LogoutResponseValidatorParameters parameters) {\n\t\tSaml2LogoutResponse response = parameters.getLogoutResponse();\n\t\tSaml2LogoutRequest request = parameters.getLogoutRequest();\n\t\tRelyingPartyRegistration registration = parameters.getRelyingPartyRegistration();\n\t\tLogoutResponse logoutResponse = this.saml.deserialize(Saml2Utils.withEncoded(response.getSamlResponse())\n\t\t\t.inflate(response.getBinding() == Saml2MessageBinding.REDIRECT)\n\t\t\t.decode());\n\t\treturn Saml2LogoutValidatorResult.withErrors()\n\t\t\t.errors(verifySignature(response, logoutResponse, registration))\n\t\t\t.errors(validateRequest(logoutResponse, registration))\n\t\t\t.errors(validateLogoutRequest(logoutResponse, request.getId()))\n\t\t\t.build();\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> verifySignature(Saml2LogoutResponse response,\n\t\t\tLogoutResponse logoutResponse, RelyingPartyRegistration registration) {\n\t\treturn (errors) -> {\n\t\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\t\tCollection<Saml2X509Credential> credentials = details.getVerificationX509Credentials();\n\t\t\tVerificationConfigurer verify = this.saml.withVerificationKeys(credentials)\n\t\t\t\t.entityId(details.getEntityId())\n\t\t\t\t.entityId(details.getEntityId());\n\t\t\tif (logoutResponse.isSigned()) {\n\t\t\t\terrors.addAll(verify.verify(logoutResponse));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tString parametersQuery = response.getParametersQuery();\n\t\t\t\tAssert.notNull(parametersQuery, \"parametersQuery cannot be null for redirect binding\");\n\t\t\t\tRedirectParameters params = new RedirectParameters(response.getParameters(), parametersQuery,\n\t\t\t\t\t\tlogoutResponse);\n\t\t\t\terrors.addAll(verify.verify(params));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateRequest(LogoutResponse response,\n\t\t\tRelyingPartyRegistration registration) {\n\t\treturn (errors) -> {\n\t\t\tvalidateIssuer(response, registration).accept(errors);\n\t\t\tvalidateDestination(response, registration).accept(errors);\n\t\t\tvalidateStatus(response).accept(errors);\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateIssuer(LogoutResponse response,\n\t\t\tRelyingPartyRegistration registration) {\n\t\treturn (errors) -> {\n\t\t\tif (response.getIssuer() == null) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, \"Failed to find issuer in LogoutResponse\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString issuer = response.getIssuer().getValue();\n\t\t\tif (!registration.getAssertingPartyMetadata().getEntityId().equals(issuer)) {\n\t\t\t\terrors\n\t\t\t\t\t.add(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, \"Failed to match issuer to configured issuer\"));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateDestination(LogoutResponse response,\n\t\t\tRelyingPartyRegistration registration) {\n\t\treturn (errors) -> {\n\t\t\tif (response.getDestination() == null) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION,\n\t\t\t\t\t\t\"Failed to find destination in LogoutResponse\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString destination = response.getDestination();\n\t\t\tif (!destination.equals(registration.getSingleLogoutServiceResponseLocation())) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION,\n\t\t\t\t\t\t\"Failed to match destination to configured destination\"));\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateStatus(LogoutResponse response) {\n\t\treturn (errors) -> {\n\t\t\tif (response.getStatus() == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (response.getStatus().getStatusCode() == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (StatusCode.SUCCESS.equals(response.getStatus().getStatusCode().getValue())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (StatusCode.PARTIAL_LOGOUT.equals(response.getStatus().getStatusCode().getValue())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, \"Response indicated logout failed\"));\n\t\t};\n\t}\n\n\tprivate Consumer<Collection<Saml2Error>> validateLogoutRequest(LogoutResponse response, @Nullable String id) {\n\t\treturn (errors) -> {\n\t\t\tif (response.getInResponseTo() == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (response.getInResponseTo().equals(id)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE,\n\t\t\t\t\t\"LogoutResponse InResponseTo doesn't match ID of associated LogoutRequest\"));\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\ninterface OpenSamlOperations {\n\n\t<T extends XMLObject> T build(QName elementName);\n\n\t<T extends XMLObject> T deserialize(String serialized);\n\n\t<T extends XMLObject> T deserialize(InputStream serialized);\n\n\tSerializationConfigurer<?> serialize(XMLObject object);\n\n\tSerializationConfigurer<?> serialize(Element element);\n\n\tSignatureConfigurer<?> withSigningKeys(Collection<Saml2X509Credential> credentials);\n\n\tVerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials);\n\n\tDecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials);\n\n\tinterface SerializationConfigurer<B extends SerializationConfigurer<B>> {\n\n\t\tB prettyPrint(boolean pretty);\n\n\t\tString serialize();\n\n\t}\n\n\tinterface SignatureConfigurer<B extends SignatureConfigurer<B>> {\n\n\t\tB algorithms(List<String> algs);\n\n\t\t<O extends SignableXMLObject> O sign(O object);\n\n\t\tMap<String, String> sign(Map<String, String> params);\n\n\t}\n\n\tinterface VerificationConfigurer {\n\n\t\tVerificationConfigurer entityId(String entityId);\n\n\t\tCollection<Saml2Error> verify(SignableXMLObject signable);\n\n\t\tCollection<Saml2Error> verify(VerificationConfigurer.RedirectParameters parameters);\n\n\t\tfinal class RedirectParameters {\n\n\t\t\tprivate final String id;\n\n\t\t\tprivate final Issuer issuer;\n\n\t\t\tprivate final String algorithm;\n\n\t\t\tprivate final byte @Nullable [] signature;\n\n\t\t\tprivate final byte[] content;\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"SAML request's ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"SAML request's Issuer cannot be null\");\n\t\t\t\tthis.id = request.getID();\n\t\t\t\tthis.issuer = request.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_REQUEST, relayState, queryParams);\n\t\t\t}\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"SAML response's ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"SAML response's Issuer cannot be null\");\n\t\t\t\tthis.id = response.getID();\n\t\t\t\tthis.issuer = response.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_RESPONSE, relayState, queryParams);\n\t\t\t}\n\n\t\t\tstatic byte[] getContent(String samlObject, @Nullable String relayState,\n\t\t\t\t\tfinal Map<String, String> queryParams) {\n\t\t\t\tif (Objects.nonNull(relayState)) {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s&%s=%s\", samlObject, queryParams.get(samlObject),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.RELAY_STATE, queryParams.get(Saml2ParameterNames.RELAY_STATE),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.SIG_ALG, queryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s\", samlObject, queryParams.get(samlObject), Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\t\t\tqueryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getId() {\n\t\t\t\treturn this.id;\n\t\t\t}\n\n\t\t\tIssuer getIssuer() {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\tbyte[] getContent() {\n\t\t\t\treturn this.content;\n\t\t\t}\n\n\t\t\tString getAlgorithm() {\n\t\t\t\treturn this.algorithm;\n\t\t\t}\n\n\t\t\tbyte @Nullable [] getSignature() {\n\t\t\t\treturn this.signature;\n\t\t\t}\n\n\t\t\tboolean hasSignature() {\n\t\t\t\treturn this.signature != null;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface DecryptionConfigurer {\n\n\t\tvoid decrypt(XMLObject object);\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * A class that represents a signed and serialized SAML 2.0 Logout Request\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class Saml2LogoutRequest implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3588981995674761337L;\n\n\tprivate static final Function<Map<String, String>, @Nullable String> DEFAULT_ENCODER = (params) -> {\n\t\tif (params.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\tfor (Map.Entry<String, String> component : params.entrySet()) {\n\t\t\tbuilder.queryParam(component.getKey(), UriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t}\n\t\treturn builder.build(true).toString().substring(1);\n\t};\n\n\tprivate final String location;\n\n\tprivate final Saml2MessageBinding binding;\n\n\tprivate final Map<String, String> parameters;\n\n\tprivate final @Nullable String id;\n\n\tprivate final String relyingPartyRegistrationId;\n\n\tprivate transient Function<Map<String, String>, @Nullable String> encoder;\n\n\tprivate Saml2LogoutRequest(String location, Saml2MessageBinding binding, Map<String, String> parameters, String id,\n\t\t\tString relyingPartyRegistrationId) {\n\t\tthis(location, binding, parameters, id, relyingPartyRegistrationId, DEFAULT_ENCODER);\n\t}\n\n\tprivate Saml2LogoutRequest(String location, Saml2MessageBinding binding, Map<String, String> parameters,\n\t\t\t@Nullable String id, String relyingPartyRegistrationId,\n\t\t\tFunction<Map<String, String>, @Nullable String> encoder) {\n\t\tthis.location = location;\n\t\tthis.binding = binding;\n\t\tthis.parameters = Collections.unmodifiableMap(new LinkedHashMap<>(parameters));\n\t\tthis.id = id;\n\t\tthis.relyingPartyRegistrationId = relyingPartyRegistrationId;\n\t\tthis.encoder = encoder;\n\t}\n\n\t/**\n\t * The unique identifier for this Logout Request\n\t * @return the Logout Request identifier\n\t */\n\tpublic @Nullable String getId() {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * Get the location of the asserting party's <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService</a>\n\t * @return the SingleLogoutService location\n\t */\n\tpublic String getLocation() {\n\t\treturn this.location;\n\t}\n\n\t/**\n\t * Get the binding for the asserting party's <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService</a>\n\t * @return the SingleLogoutService binding\n\t */\n\tpublic Saml2MessageBinding getBinding() {\n\t\treturn this.binding;\n\t}\n\n\t/**\n\t * Get the signed and serialized &lt;saml2:LogoutRequest&gt; payload\n\t * @return the signed and serialized &lt;saml2:LogoutRequest&gt; payload\n\t */\n\tpublic String getSamlRequest() {\n\t\tString samlRequest = this.parameters.get(Saml2ParameterNames.SAML_REQUEST);\n\t\tAssert.notNull(samlRequest, \"samlRequest cannot be null\");\n\t\treturn samlRequest;\n\t}\n\n\t/**\n\t * The relay state associated with this Logout Request\n\t * @return the relay state\n\t */\n\tpublic @Nullable String getRelayState() {\n\t\treturn this.parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t}\n\n\t/**\n\t * Get the {@code name} parameters, a short-hand for <code>\n\t * getParameters().get(name)\n\t * </code>\n\t *\n\t * Useful when specifying additional query parameters for the Logout Request\n\t * @param name the parameter's name\n\t * @return the parameter's value\n\t */\n\tpublic @Nullable String getParameter(String name) {\n\t\treturn this.parameters.get(name);\n\t}\n\n\t/**\n\t * Get all parameters\n\t *\n\t * Useful when specifying additional query parameters for the Logout Request\n\t * @return the Logout Request query parameters\n\t */\n\tpublic Map<String, String> getParameters() {\n\t\treturn this.parameters;\n\t}\n\n\t/**\n\t * Get an encoded query string of all parameters. Resulting query does not contain a\n\t * leading question mark.\n\t * @return an encoded string of all parameters\n\t * @since 5.8\n\t */\n\tpublic @Nullable String getParametersQuery() {\n\t\treturn this.encoder.apply(this.parameters);\n\t}\n\n\t/**\n\t * The identifier for the {@link RelyingPartyRegistration} associated with this Logout\n\t * Request\n\t * @return the {@link RelyingPartyRegistration} id\n\t */\n\tpublic String getRelyingPartyRegistrationId() {\n\t\treturn this.relyingPartyRegistrationId;\n\t}\n\n\t/**\n\t * Create a {@link Builder} instance from this {@link RelyingPartyRegistration}\n\t *\n\t * Specifically, this will pull the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService</a>\n\t * location and binding from the {@link RelyingPartyRegistration}\n\t * @param registration the {@link RelyingPartyRegistration} to use\n\t * @return the {@link Builder} for further configurations\n\t */\n\tpublic static Builder withRelyingPartyRegistration(RelyingPartyRegistration registration) {\n\t\treturn new Builder(registration);\n\t}\n\n\tpublic static final class Builder {\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tprivate @Nullable String location;\n\n\t\tprivate Saml2MessageBinding binding;\n\n\t\tprivate Map<String, String> parameters = new LinkedHashMap<>();\n\n\t\tprivate Function<Map<String, String>, @Nullable String> encoder = DEFAULT_ENCODER;\n\n\t\tprivate @Nullable String id;\n\n\t\tprivate Builder(RelyingPartyRegistration registration) {\n\t\t\tthis.registration = registration;\n\t\t\tthis.location = registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation();\n\t\t\tthis.binding = registration.getAssertingPartyMetadata().getSingleLogoutServiceBinding();\n\t\t}\n\n\t\t/**\n\t\t * Use this signed and serialized and Base64-encoded &lt;saml2:LogoutRequest&gt;\n\t\t *\n\t\t * Note that if using the Redirect binding, the value should be\n\t\t * {@link java.util.zip.DeflaterOutputStream deflated} and then Base64-encoded.\n\t\t *\n\t\t * It should not be URL-encoded as this will be done when the request is sent\n\t\t * @param samlRequest the &lt;saml2:LogoutRequest&gt; to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t * @see Saml2LogoutRequestResolver\n\t\t */\n\t\tpublic Builder samlRequest(String samlRequest) {\n\t\t\tthis.parameters.put(Saml2ParameterNames.SAML_REQUEST, samlRequest);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this SAML 2.0 Message Binding\n\t\t *\n\t\t * By default, the asserting party's configured binding is used\n\t\t * @param binding the SAML 2.0 Message Binding to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder binding(Saml2MessageBinding binding) {\n\t\t\tthis.binding = binding;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this location for the SAML 2.0 logout endpoint\n\t\t *\n\t\t * By default, the asserting party's endpoint is used\n\t\t * @param location the SAML 2.0 location to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder location(String location) {\n\t\t\tthis.location = location;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this value for the relay state when sending the Logout Request to the\n\t\t * asserting party\n\t\t *\n\t\t * It should not be URL-encoded as this will be done when the request is sent\n\t\t * @param relayState the relay state\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder relayState(String relayState) {\n\t\t\tthis.parameters.put(Saml2ParameterNames.RELAY_STATE, relayState);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * This is the unique id used in the {@link #samlRequest}\n\t\t * @param id the Logout Request id\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder id(String id) {\n\t\t\tthis.id = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link Consumer} to modify the set of query parameters\n\t\t *\n\t\t * No parameter should be URL-encoded as this will be done when the request is\n\t\t * sent\n\t\t * @param parametersConsumer the {@link Consumer}\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder parameters(Consumer<Map<String, String>> parametersConsumer) {\n\t\t\tparametersConsumer.accept(this.parameters);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this strategy for converting parameters into an encoded query string. The\n\t\t * resulting query does not contain a leading question mark.\n\t\t *\n\t\t * In the event that you already have an encoded version that you want to use, you\n\t\t * can call this by doing {@code parameterEncoder((params) -> encodedValue)}.\n\t\t * @param encoder the strategy to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t * @since 5.8\n\t\t */\n\t\tpublic Builder parametersQuery(Function<Map<String, String>, @Nullable String> encoder) {\n\t\t\tthis.encoder = encoder;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the {@link Saml2LogoutRequest}\n\t\t * @return a constructed {@link Saml2LogoutRequest}\n\t\t */\n\t\tpublic Saml2LogoutRequest build() {\n\t\t\tAssert.notNull(this.location, \"singleLocationServiceLocation cannot be null\");\n\t\t\tAssert.notNull(this.parameters.get(Saml2ParameterNames.SAML_REQUEST), \"samlRequest cannot be null\");\n\t\t\treturn new Saml2LogoutRequest(this.location, this.binding, this.parameters, this.id,\n\t\t\t\t\tthis.registration.getRegistrationId(), this.encoder);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequestValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\n/**\n * Validates SAML 2.0 Logout Requests\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic interface Saml2LogoutRequestValidator {\n\n\t/**\n\t * Authenticates the SAML 2.0 Logout Request received from the SAML 2.0 Asserting\n\t * Party.\n\t *\n\t * By default, verifies the signature, validates the issuer, destination, and user\n\t * identifier.\n\t * @param parameters the {@link Saml2LogoutRequestValidatorParameters} needed\n\t * @return the authentication result\n\t */\n\tSaml2LogoutValidatorResult validate(Saml2LogoutRequestValidatorParameters parameters);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequestValidatorParameters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\n\n/**\n * A holder of the parameters needed to invoke {@link Saml2LogoutRequestValidator}\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic class Saml2LogoutRequestValidatorParameters {\n\n\tprivate final Saml2LogoutRequest request;\n\n\tprivate final RelyingPartyRegistration registration;\n\n\tprivate final @Nullable Authentication authentication;\n\n\t/**\n\t * Construct a {@link Saml2LogoutRequestValidatorParameters}\n\t * @param request the SAML 2.0 Logout Request received from the asserting party\n\t * @param registration the associated {@link RelyingPartyRegistration}\n\t * @param authentication the current user\n\t */\n\tpublic Saml2LogoutRequestValidatorParameters(Saml2LogoutRequest request, RelyingPartyRegistration registration,\n\t\t\t@Nullable Authentication authentication) {\n\t\tthis.request = request;\n\t\tthis.registration = registration;\n\t\tthis.authentication = authentication;\n\t}\n\n\t/**\n\t * The SAML 2.0 Logout Request sent by the asserting party\n\t * @return the logout request\n\t */\n\tpublic Saml2LogoutRequest getLogoutRequest() {\n\t\treturn this.request;\n\t}\n\n\t/**\n\t * The {@link RelyingPartyRegistration} representing this relying party\n\t * @return the relying party\n\t */\n\tpublic RelyingPartyRegistration getRelyingPartyRegistration() {\n\t\treturn this.registration;\n\t}\n\n\t/**\n\t * The current {@link Authentication}\n\t * @return the authenticated user\n\t */\n\tpublic @Nullable Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * A class that represents a signed and serialized SAML 2.0 Logout Response\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class Saml2LogoutResponse {\n\n\tprivate static final Function<Map<String, String>, @Nullable String> DEFAULT_ENCODER = (params) -> {\n\t\tif (params.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\tfor (Map.Entry<String, String> component : params.entrySet()) {\n\t\t\tbuilder.queryParam(component.getKey(), UriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t}\n\t\treturn builder.build(true).toString().substring(1);\n\t};\n\n\tprivate final String location;\n\n\tprivate final Saml2MessageBinding binding;\n\n\tprivate final Map<String, String> parameters;\n\n\tprivate final Function<Map<String, String>, @Nullable String> encoder;\n\n\tprivate Saml2LogoutResponse(String location, Saml2MessageBinding binding, Map<String, String> parameters,\n\t\t\tFunction<Map<String, String>, @Nullable String> encoder) {\n\t\tthis.location = location;\n\t\tthis.binding = binding;\n\t\tthis.parameters = Collections.unmodifiableMap(new LinkedHashMap<>(parameters));\n\t\tthis.encoder = encoder;\n\t}\n\n\t/**\n\t * Get the response location of the asserting party's <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService</a>\n\t * @return the SingleLogoutService response location\n\t */\n\tpublic String getResponseLocation() {\n\t\treturn this.location;\n\t}\n\n\t/**\n\t * Get the binding for the asserting party's <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService</a>\n\t * @return the SingleLogoutService binding\n\t */\n\tpublic Saml2MessageBinding getBinding() {\n\t\treturn this.binding;\n\t}\n\n\t/**\n\t * Get the signed and serialized &lt;saml2:LogoutResponse&gt; payload\n\t * @return the signed and serialized &lt;saml2:LogoutResponse&gt; payload\n\t */\n\tpublic String getSamlResponse() {\n\t\tString samlResponse = this.parameters.get(Saml2ParameterNames.SAML_RESPONSE);\n\t\tAssert.notNull(samlResponse, \"samlResponse cannot be null\");\n\t\treturn samlResponse;\n\t}\n\n\t/**\n\t * The relay state associated with this Logout Request\n\t * @return the relay state\n\t */\n\tpublic @Nullable String getRelayState() {\n\t\treturn this.parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t}\n\n\t/**\n\t * Get the {@code name} parameter, a short-hand for <code>\n\t *\tgetParameters().get(name)\n\t * </code>\n\t *\n\t * Useful when specifying additional query parameters for the Logout Response\n\t * @param name the parameter's name\n\t * @return the parameter's value\n\t */\n\tpublic @Nullable String getParameter(String name) {\n\t\treturn this.parameters.get(name);\n\t}\n\n\t/**\n\t * Get all parameters\n\t *\n\t * Useful when specifying additional query parameters for the Logout Response\n\t * @return the Logout Response query parameters\n\t */\n\tpublic Map<String, String> getParameters() {\n\t\treturn this.parameters;\n\t}\n\n\t/**\n\t * Get an encoded query string of all parameters. Resulting query does not contain a\n\t * leading question mark.\n\t * @return an encoded string of all parameters\n\t * @since 5.8\n\t */\n\tpublic @Nullable String getParametersQuery() {\n\t\treturn this.encoder.apply(this.parameters);\n\t}\n\n\t/**\n\t * Create a {@link Builder} instance from this {@link RelyingPartyRegistration}\n\t *\n\t * Specifically, this will pull the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService</a>\n\t * response location and binding from the {@link RelyingPartyRegistration}\n\t * @param registration the {@link RelyingPartyRegistration} to use\n\t * @return the {@link Builder} for further configurations\n\t */\n\tpublic static Builder withRelyingPartyRegistration(RelyingPartyRegistration registration) {\n\t\treturn new Builder(registration);\n\t}\n\n\tpublic static final class Builder {\n\n\t\tprivate @Nullable String location;\n\n\t\tprivate Saml2MessageBinding binding;\n\n\t\tprivate Map<String, String> parameters = new LinkedHashMap<>();\n\n\t\tprivate Function<Map<String, String>, @Nullable String> encoder = DEFAULT_ENCODER;\n\n\t\tprivate Builder(RelyingPartyRegistration registration) {\n\t\t\tthis.location = registration.getAssertingPartyMetadata().getSingleLogoutServiceResponseLocation();\n\t\t\tthis.binding = registration.getAssertingPartyMetadata().getSingleLogoutServiceBinding();\n\t\t}\n\n\t\t/**\n\t\t * Use this signed and serialized and Base64-encoded &lt;saml2:LogoutResponse&gt;\n\t\t *\n\t\t * Note that if using the Redirect binding, the value should be\n\t\t * {@link java.util.zip.DeflaterOutputStream deflated} and then Base64-encoded.\n\t\t *\n\t\t * It should not be URL-encoded as this will be done when the response is sent\n\t\t * @param samlResponse the &lt;saml2:LogoutResponse&gt; to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t * @see Saml2LogoutResponseResolver\n\t\t */\n\t\tpublic Builder samlResponse(String samlResponse) {\n\t\t\tthis.parameters.put(Saml2ParameterNames.SAML_RESPONSE, samlResponse);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this SAML 2.0 Message Binding\n\t\t *\n\t\t * By default, the asserting party's configured binding is used\n\t\t * @param binding the SAML 2.0 Message Binding to use\n\t\t * @return the {@link Saml2LogoutRequest.Builder} for further configurations\n\t\t */\n\t\tpublic Builder binding(Saml2MessageBinding binding) {\n\t\t\tthis.binding = binding;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this location for the SAML 2.0 logout endpoint\n\t\t *\n\t\t * By default, the asserting party's endpoint is used\n\t\t * @param location the SAML 2.0 location to use\n\t\t * @return the {@link Saml2LogoutRequest.Builder} for further configurations\n\t\t */\n\t\tpublic Builder location(String location) {\n\t\t\tthis.location = location;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this value for the relay state when sending the Logout Request to the\n\t\t * asserting party\n\t\t *\n\t\t * It should not be URL-encoded as this will be done when the response is sent\n\t\t * @param relayState the relay state\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder relayState(String relayState) {\n\t\t\tthis.parameters.put(Saml2ParameterNames.RELAY_STATE, relayState);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link Consumer} to modify the set of query parameters\n\t\t *\n\t\t * No parameter should be URL-encoded as this will be done when the response is\n\t\t * sent, though any signature specified should be Base64-encoded\n\t\t * @param parametersConsumer the {@link Consumer}\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder parameters(Consumer<Map<String, String>> parametersConsumer) {\n\t\t\tparametersConsumer.accept(this.parameters);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this strategy for converting parameters into an encoded query string. The\n\t\t * resulting query does not contain a leading question mark.\n\t\t *\n\t\t * In the event that you already have an encoded version that you want to use, you\n\t\t * can call this by doing {@code parameterEncoder((params) -> encodedValue)}.\n\t\t * @param encoder the strategy to use\n\t\t * @return the {@link Saml2LogoutRequest.Builder} for further configurations\n\t\t * @since 5.8\n\t\t */\n\t\tpublic Builder parametersQuery(Function<Map<String, String>, @Nullable String> encoder) {\n\t\t\tthis.encoder = encoder;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the {@link Saml2LogoutResponse}\n\t\t * @return a constructed {@link Saml2LogoutResponse}\n\t\t */\n\t\tpublic Saml2LogoutResponse build() {\n\t\t\tAssert.notNull(this.location, \"singleLogoutResponseLocation cannot be null\");\n\t\t\tAssert.notNull(this.parameters.get(Saml2ParameterNames.SAML_RESPONSE), \"samlResponse cannot be null\");\n\t\t\treturn new Saml2LogoutResponse(this.location, this.binding, this.parameters, this.encoder);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponseValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\n/**\n * Validates SAML 2.0 Logout Responses\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic interface Saml2LogoutResponseValidator {\n\n\t/**\n\t * Authenticates the SAML 2.0 Logout Response received from the SAML 2.0 Asserting\n\t * Party.\n\t *\n\t * By default, verifies the signature, validates the issuer, destination, and status.\n\t * It also ensures that it aligns with the given logout request.\n\t * @param parameters the {@link Saml2LogoutResponseValidatorParameters} needed\n\t * @return the authentication result\n\t */\n\tSaml2LogoutValidatorResult validate(Saml2LogoutResponseValidatorParameters parameters);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponseValidatorParameters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\n\n/**\n * A holder of the parameters needed to invoke {@link Saml2LogoutResponseValidator}\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic class Saml2LogoutResponseValidatorParameters {\n\n\tprivate final Saml2LogoutResponse response;\n\n\tprivate final Saml2LogoutRequest request;\n\n\tprivate final RelyingPartyRegistration registration;\n\n\t/**\n\t * Construct a {@link Saml2LogoutRequestValidatorParameters}\n\t * @param response the SAML 2.0 Logout Response received from the asserting party\n\t * @param request the SAML 2.0 Logout Request send by this application\n\t * @param registration the associated {@link RelyingPartyRegistration}\n\t */\n\tpublic Saml2LogoutResponseValidatorParameters(Saml2LogoutResponse response, Saml2LogoutRequest request,\n\t\t\tRelyingPartyRegistration registration) {\n\t\tthis.response = response;\n\t\tthis.request = request;\n\t\tthis.registration = registration;\n\t}\n\n\t/**\n\t * The SAML 2.0 Logout Response received from the asserting party\n\t * @return the logout response\n\t */\n\tpublic Saml2LogoutResponse getLogoutResponse() {\n\t\treturn this.response;\n\t}\n\n\t/**\n\t * The SAML 2.0 Logout Request sent by this application\n\t * @return the logout request\n\t */\n\tpublic Saml2LogoutRequest getLogoutRequest() {\n\t\treturn this.request;\n\t}\n\n\t/**\n\t * The {@link RelyingPartyRegistration} representing this relying party\n\t * @return the relying party\n\t */\n\tpublic RelyingPartyRegistration getRelyingPartyRegistration() {\n\t\treturn this.registration;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutValidatorResult.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.function.Consumer;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.util.Assert;\n\n/**\n * A result emitted from a SAML 2.0 Logout validation attempt\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class Saml2LogoutValidatorResult {\n\n\tstatic final Saml2LogoutValidatorResult NO_ERRORS = new Saml2LogoutValidatorResult(Collections.emptyList());\n\n\tprivate final Collection<Saml2Error> errors;\n\n\tprivate Saml2LogoutValidatorResult(Collection<Saml2Error> errors) {\n\t\tAssert.notNull(errors, \"errors cannot be null\");\n\t\tthis.errors = new ArrayList<>(errors);\n\t}\n\n\t/**\n\t * Say whether this result indicates success\n\t * @return whether this result has errors\n\t */\n\tpublic boolean hasErrors() {\n\t\treturn !this.errors.isEmpty();\n\t}\n\n\t/**\n\t * Return error details regarding the validation attempt\n\t * @return the collection of results in this result, if any; returns an empty list\n\t * otherwise\n\t */\n\tpublic Collection<Saml2Error> getErrors() {\n\t\treturn Collections.unmodifiableCollection(this.errors);\n\t}\n\n\t/**\n\t * Construct a successful {@link Saml2LogoutValidatorResult}\n\t * @return an {@link Saml2LogoutValidatorResult} with no errors\n\t */\n\tpublic static Saml2LogoutValidatorResult success() {\n\t\treturn NO_ERRORS;\n\t}\n\n\t/**\n\t * Construct a {@link Saml2LogoutValidatorResult.Builder}, starting with the given\n\t * {@code errors}.\n\t *\n\t * Note that a result with no errors is considered a success.\n\t * @param errors\n\t * @return\n\t */\n\tpublic static Saml2LogoutValidatorResult.Builder withErrors(Saml2Error... errors) {\n\t\treturn new Builder(errors);\n\t}\n\n\tpublic static final class Builder {\n\n\t\tprivate final Collection<Saml2Error> errors;\n\n\t\tprivate Builder(Saml2Error... errors) {\n\t\t\tthis(Arrays.asList(errors));\n\t\t}\n\n\t\tprivate Builder(Collection<Saml2Error> errors) {\n\t\t\tAssert.noNullElements(errors, \"errors cannot have null elements\");\n\t\t\tthis.errors = new ArrayList<>(errors);\n\t\t}\n\n\t\tpublic Builder errors(Consumer<Collection<Saml2Error>> errorsConsumer) {\n\t\t\terrorsConsumer.accept(this.errors);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Saml2LogoutValidatorResult build() {\n\t\t\treturn new Saml2LogoutValidatorResult(this.errors);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal utilities for SAML2 support (not for public use).\n */\n@NullMarked\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal utilities for SAML2 support (not for public use).\n */\n@NullMarked\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/BaseOpenSamlMetadataResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport java.security.cert.CertificateEncodingException;\nimport java.util.ArrayList;\nimport java.util.Base64;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.saml2.metadata.AssertionConsumerService;\nimport org.opensaml.saml.saml2.metadata.EntitiesDescriptor;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\nimport org.opensaml.saml.saml2.metadata.KeyDescriptor;\nimport org.opensaml.saml.saml2.metadata.NameIDFormat;\nimport org.opensaml.saml.saml2.metadata.SPSSODescriptor;\nimport org.opensaml.saml.saml2.metadata.SingleLogoutService;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.xmlsec.signature.KeyInfo;\nimport org.opensaml.xmlsec.signature.X509Certificate;\nimport org.opensaml.xmlsec.signature.X509Data;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.util.Assert;\n\n/**\n * Resolves the SAML 2.0 Relying Party Metadata for a given\n * {@link RelyingPartyRegistration} using the OpenSAML API.\n *\n * @author Jakub Kubrynski\n * @author Josh Cummings\n * @since 5.4\n */\nfinal class BaseOpenSamlMetadataResolver implements Saml2MetadataResolver {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate OpenSamlOperations saml;\n\n\tprivate Consumer<EntityDescriptorParameters> entityDescriptorCustomizer = (parameters) -> {\n\t};\n\n\tprivate boolean usePrettyPrint = true;\n\n\tprivate boolean signMetadata = false;\n\n\tBaseOpenSamlMetadataResolver(OpenSamlOperations saml) {\n\t\tthis.saml = saml;\n\t}\n\n\t@Override\n\tpublic String resolve(RelyingPartyRegistration relyingPartyRegistration) {\n\t\tEntityDescriptor entityDescriptor = entityDescriptor(relyingPartyRegistration);\n\t\treturn serialize(entityDescriptor);\n\t}\n\n\t@Override\n\tpublic String resolve(Iterable<RelyingPartyRegistration> relyingPartyRegistrations) {\n\t\tCollection<EntityDescriptor> entityDescriptors = new ArrayList<>();\n\t\tfor (RelyingPartyRegistration registration : relyingPartyRegistrations) {\n\t\t\tEntityDescriptor entityDescriptor = entityDescriptor(registration);\n\t\t\tentityDescriptors.add(entityDescriptor);\n\t\t}\n\t\tif (entityDescriptors.size() == 1) {\n\t\t\treturn serialize(entityDescriptors.iterator().next());\n\t\t}\n\t\tEntitiesDescriptor entities = this.saml.build(EntitiesDescriptor.DEFAULT_ELEMENT_NAME);\n\t\tentities.getEntityDescriptors().addAll(entityDescriptors);\n\t\treturn serialize(entities);\n\t}\n\n\tprivate EntityDescriptor entityDescriptor(RelyingPartyRegistration registration) {\n\t\tEntityDescriptor entityDescriptor = this.saml.build(EntityDescriptor.DEFAULT_ELEMENT_NAME);\n\t\tentityDescriptor.setEntityID(registration.getEntityId());\n\t\tSPSSODescriptor spSsoDescriptor = buildSpSsoDescriptor(registration);\n\t\tentityDescriptor.getRoleDescriptors(SPSSODescriptor.DEFAULT_ELEMENT_NAME).add(spSsoDescriptor);\n\t\tthis.entityDescriptorCustomizer.accept(new EntityDescriptorParameters(entityDescriptor, registration));\n\t\tif (this.signMetadata) {\n\t\t\treturn this.saml.withSigningKeys(registration.getSigningX509Credentials())\n\t\t\t\t.algorithms(registration.getAssertingPartyMetadata().getSigningAlgorithms())\n\t\t\t\t.sign(entityDescriptor);\n\t\t}\n\t\telse {\n\t\t\tthis.logger.trace(\"Did not sign metadata since `signMetadata` is `false`\");\n\t\t}\n\t\treturn entityDescriptor;\n\t}\n\n\t/**\n\t * Set a {@link Consumer} for modifying the OpenSAML {@link EntityDescriptor}\n\t * @param entityDescriptorCustomizer a consumer that accepts an\n\t * {@link EntityDescriptorParameters}\n\t * @since 5.7\n\t */\n\tvoid setEntityDescriptorCustomizer(Consumer<EntityDescriptorParameters> entityDescriptorCustomizer) {\n\t\tAssert.notNull(entityDescriptorCustomizer, \"entityDescriptorCustomizer cannot be null\");\n\t\tthis.entityDescriptorCustomizer = entityDescriptorCustomizer;\n\t}\n\n\t/**\n\t * Configure whether to pretty-print the metadata XML. This can be helpful when\n\t * signing the metadata payload.\n\t *\n\t * @since 6.2\n\t **/\n\tvoid setUsePrettyPrint(boolean usePrettyPrint) {\n\t\tthis.usePrettyPrint = usePrettyPrint;\n\t}\n\n\tprivate SPSSODescriptor buildSpSsoDescriptor(RelyingPartyRegistration registration) {\n\t\tSPSSODescriptor spSsoDescriptor = this.saml.build(SPSSODescriptor.DEFAULT_ELEMENT_NAME);\n\t\tspSsoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);\n\t\tspSsoDescriptor.getKeyDescriptors()\n\t\t\t.addAll(buildKeys(registration.getSigningX509Credentials(), UsageType.SIGNING));\n\t\tspSsoDescriptor.getKeyDescriptors()\n\t\t\t.addAll(buildKeys(registration.getDecryptionX509Credentials(), UsageType.ENCRYPTION));\n\t\tspSsoDescriptor.getAssertionConsumerServices().add(buildAssertionConsumerService(registration));\n\t\tif (registration.getSingleLogoutServiceLocation() != null) {\n\t\t\tfor (Saml2MessageBinding binding : registration.getSingleLogoutServiceBindings()) {\n\t\t\t\tspSsoDescriptor.getSingleLogoutServices().add(buildSingleLogoutService(registration, binding));\n\t\t\t}\n\t\t}\n\t\tif (registration.getNameIdFormat() != null) {\n\t\t\tspSsoDescriptor.getNameIDFormats().add(buildNameIDFormat(registration));\n\t\t}\n\t\treturn spSsoDescriptor;\n\t}\n\n\tprivate List<KeyDescriptor> buildKeys(Collection<Saml2X509Credential> credentials, UsageType usageType) {\n\t\tList<KeyDescriptor> list = new ArrayList<>();\n\t\tfor (Saml2X509Credential credential : credentials) {\n\t\t\tKeyDescriptor keyDescriptor = buildKeyDescriptor(usageType, credential.getCertificate());\n\t\t\tlist.add(keyDescriptor);\n\t\t}\n\t\treturn list;\n\t}\n\n\tprivate KeyDescriptor buildKeyDescriptor(UsageType usageType, java.security.cert.X509Certificate certificate) {\n\t\tKeyDescriptor keyDescriptor = this.saml.build(KeyDescriptor.DEFAULT_ELEMENT_NAME);\n\t\tKeyInfo keyInfo = this.saml.build(KeyInfo.DEFAULT_ELEMENT_NAME);\n\t\tX509Certificate x509Certificate = this.saml.build(X509Certificate.DEFAULT_ELEMENT_NAME);\n\t\tX509Data x509Data = this.saml.build(X509Data.DEFAULT_ELEMENT_NAME);\n\t\ttry {\n\t\t\tx509Certificate.setValue(new String(Base64.getEncoder().encode(certificate.getEncoded())));\n\t\t}\n\t\tcatch (CertificateEncodingException ex) {\n\t\t\tthrow new Saml2Exception(\"Cannot encode certificate \" + certificate.toString());\n\t\t}\n\t\tx509Data.getX509Certificates().add(x509Certificate);\n\t\tkeyInfo.getX509Datas().add(x509Data);\n\t\tkeyDescriptor.setUse(usageType);\n\t\tkeyDescriptor.setKeyInfo(keyInfo);\n\t\treturn keyDescriptor;\n\t}\n\n\tprivate AssertionConsumerService buildAssertionConsumerService(RelyingPartyRegistration registration) {\n\t\tAssertionConsumerService assertionConsumerService = this.saml\n\t\t\t.build(AssertionConsumerService.DEFAULT_ELEMENT_NAME);\n\t\tassertionConsumerService.setLocation(registration.getAssertionConsumerServiceLocation());\n\t\tassertionConsumerService.setBinding(registration.getAssertionConsumerServiceBinding().getUrn());\n\t\tassertionConsumerService.setIndex(1);\n\t\treturn assertionConsumerService;\n\t}\n\n\tprivate SingleLogoutService buildSingleLogoutService(RelyingPartyRegistration registration,\n\t\t\tSaml2MessageBinding binding) {\n\t\tSingleLogoutService singleLogoutService = this.saml.build(SingleLogoutService.DEFAULT_ELEMENT_NAME);\n\t\tsingleLogoutService.setLocation(registration.getSingleLogoutServiceLocation());\n\t\tsingleLogoutService.setResponseLocation(registration.getSingleLogoutServiceResponseLocation());\n\t\tsingleLogoutService.setBinding(binding.getUrn());\n\t\treturn singleLogoutService;\n\t}\n\n\tprivate NameIDFormat buildNameIDFormat(RelyingPartyRegistration registration) {\n\t\tNameIDFormat nameIdFormat = this.saml.build(NameIDFormat.DEFAULT_ELEMENT_NAME);\n\t\tnameIdFormat.setURI(registration.getNameIdFormat());\n\t\treturn nameIdFormat;\n\t}\n\n\tprivate String serialize(EntityDescriptor entityDescriptor) {\n\t\treturn this.saml.serialize(entityDescriptor).prettyPrint(this.usePrettyPrint).serialize();\n\t}\n\n\tprivate String serialize(EntitiesDescriptor entities) {\n\t\treturn this.saml.serialize(entities).prettyPrint(this.usePrettyPrint).serialize();\n\t}\n\n\t/**\n\t * Configure whether to sign the metadata, defaults to {@code false}.\n\t *\n\t * @since 6.4\n\t */\n\tvoid setSignMetadata(boolean signMetadata) {\n\t\tthis.signMetadata = signMetadata;\n\t}\n\n\t/**\n\t * A tuple containing an OpenSAML {@link EntityDescriptor} and its associated\n\t * {@link RelyingPartyRegistration}\n\t *\n\t * @since 5.7\n\t */\n\tstatic final class EntityDescriptorParameters {\n\n\t\tprivate final EntityDescriptor entityDescriptor;\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tEntityDescriptorParameters(EntityDescriptor entityDescriptor, RelyingPartyRegistration registration) {\n\t\t\tthis.entityDescriptor = entityDescriptor;\n\t\t\tthis.registration = registration;\n\t\t}\n\n\t\tEntityDescriptor getEntityDescriptor() {\n\t\t\treturn this.entityDescriptor;\n\t\t}\n\n\t\tRelyingPartyRegistration getRelyingPartyRegistration() {\n\t\t\treturn this.registration;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\ninterface OpenSamlOperations {\n\n\t<T extends XMLObject> T build(QName elementName);\n\n\t<T extends XMLObject> T deserialize(String serialized);\n\n\t<T extends XMLObject> T deserialize(InputStream serialized);\n\n\tSerializationConfigurer<?> serialize(XMLObject object);\n\n\tSerializationConfigurer<?> serialize(Element element);\n\n\tSignatureConfigurer<?> withSigningKeys(Collection<Saml2X509Credential> credentials);\n\n\tVerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials);\n\n\tDecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials);\n\n\tinterface SerializationConfigurer<B extends SerializationConfigurer<B>> {\n\n\t\tB prettyPrint(boolean pretty);\n\n\t\tString serialize();\n\n\t}\n\n\tinterface SignatureConfigurer<B extends SignatureConfigurer<B>> {\n\n\t\tB algorithms(List<String> algs);\n\n\t\t<O extends SignableXMLObject> O sign(O object);\n\n\t\tMap<String, String> sign(Map<String, String> params);\n\n\t}\n\n\tinterface VerificationConfigurer {\n\n\t\tVerificationConfigurer entityId(String entityId);\n\n\t\tCollection<Saml2Error> verify(SignableXMLObject signable);\n\n\t\tCollection<Saml2Error> verify(VerificationConfigurer.RedirectParameters parameters);\n\n\t\tfinal class RedirectParameters {\n\n\t\t\tprivate final String id;\n\n\t\t\tprivate final Issuer issuer;\n\n\t\t\tprivate final String algorithm;\n\n\t\t\tprivate final byte @Nullable [] signature;\n\n\t\t\tprivate final byte[] content;\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"SAML request's ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"SAML request's Issuer cannot be null\");\n\t\t\t\tthis.id = request.getID();\n\t\t\t\tthis.issuer = request.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_REQUEST, relayState, queryParams);\n\t\t\t}\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"SAML response's ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"SAML response's Issuer cannot be null\");\n\t\t\t\tthis.id = response.getID();\n\t\t\t\tthis.issuer = response.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_RESPONSE, relayState, queryParams);\n\t\t\t}\n\n\t\t\tstatic byte[] getContent(String samlObject, @Nullable String relayState,\n\t\t\t\t\tfinal Map<String, String> queryParams) {\n\t\t\t\tif (Objects.nonNull(relayState)) {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s&%s=%s\", samlObject, queryParams.get(samlObject),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.RELAY_STATE, queryParams.get(Saml2ParameterNames.RELAY_STATE),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.SIG_ALG, queryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s\", samlObject, queryParams.get(samlObject), Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\t\t\tqueryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getId() {\n\t\t\t\treturn this.id;\n\t\t\t}\n\n\t\t\tIssuer getIssuer() {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\tbyte[] getContent() {\n\t\t\t\treturn this.content;\n\t\t\t}\n\n\t\t\tString getAlgorithm() {\n\t\t\t\treturn this.algorithm;\n\t\t\t}\n\n\t\t\tbyte @Nullable [] getSignature() {\n\t\t\t\treturn this.signature;\n\t\t\t}\n\n\t\t\tboolean hasSignature() {\n\t\t\t\treturn this.signature != null;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface DecryptionConfigurer {\n\n\t\tvoid decrypt(XMLObject object);\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/RequestMatcherMetadataResponseResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport org.jspecify.annotations.NullUnmarked;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\n/**\n * An implementation of {@link Saml2MetadataResponseResolver} that identifies which\n * {@link RelyingPartyRegistration}s to use with a {@link RequestMatcher}\n *\n * @author Josh Cummings\n * @since 6.1\n * @deprecated Please use\n * {@link org.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver}\n */\n@Deprecated\n@NullUnmarked\npublic final class RequestMatcherMetadataResponseResolver extends\n\t\torg.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver {\n\n\t/**\n\t * Construct a\n\t * {@link org.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver}\n\t * @param registrations the source for relying party metadata\n\t * @param metadata the strategy for converting {@link RelyingPartyRegistration}s into\n\t * metadata\n\t */\n\tpublic RequestMatcherMetadataResponseResolver(RelyingPartyRegistrationRepository registrations,\n\t\t\tSaml2MetadataResolver metadata) {\n\t\tsuper(registrations, metadata);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2MetadataResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\n\n/**\n * Resolves the SAML 2.0 Relying Party Metadata for a given\n * {@link RelyingPartyRegistration}\n *\n * @author Jakub Kubrynski\n * @author Josh Cummings\n * @since 5.4\n */\npublic interface Saml2MetadataResolver {\n\n\t/**\n\t * Resolve the given relying party's metadata\n\t * @param relyingPartyRegistration the relying party\n\t * @return the relying party's metadata\n\t */\n\tString resolve(RelyingPartyRegistration relyingPartyRegistration);\n\n\tdefault String resolve(Iterable<RelyingPartyRegistration> relyingPartyRegistrations) {\n\t\treturn resolve(relyingPartyRegistrations.iterator().next());\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2MetadataResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\npublic class Saml2MetadataResponse {\n\n\tprivate final String metadata;\n\n\tprivate final String fileName;\n\n\tpublic Saml2MetadataResponse(String metadata, String fileName) {\n\t\tthis.metadata = metadata;\n\t\tthis.fileName = fileName;\n\t}\n\n\tpublic String getMetadata() {\n\t\treturn this.metadata;\n\t}\n\n\tpublic String getFileName() {\n\t\treturn this.fileName;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2MetadataResponseResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Resolves Relying Party SAML 2.0 Metadata given details from the\n * {@link HttpServletRequest}.\n *\n * @author Josh Cummings\n * @since 6.1\n */\npublic interface Saml2MetadataResponseResolver {\n\n\t/**\n\t * Construct and serialize a relying party's SAML 2.0 metadata based on the given\n\t * {@link HttpServletRequest}\n\t * @param request the HTTP request\n\t * @return a {@link Saml2MetadataResponse} instance\n\t */\n\t@Nullable Saml2MetadataResponse resolve(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal utilities for SAML2 support (not for public use).\n */\n@NullMarked\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/AssertingPartyMetadata.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.core.Saml2X509Credential;\n\n/**\n * An interface representing SAML 2.0 Asserting Party metadata\n *\n * @author Josh Cummings\n * @since 6.4\n */\npublic interface AssertingPartyMetadata extends Serializable {\n\n\t/**\n\t * Get the asserting party's <a href=\n\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor\">EntityID</a>.\n\t *\n\t * <p>\n\t * Equivalent to the value found in the asserting party's &lt;EntityDescriptor\n\t * EntityID=\"...\"/&gt;\n\t *\n\t * <p>\n\t * This value may contain a number of placeholders, which need to be resolved before\n\t * use. They are {@code baseUrl}, {@code registrationId}, {@code baseScheme},\n\t * {@code baseHost}, and {@code basePort}.\n\t * @return the asserting party's EntityID\n\t */\n\tString getEntityId();\n\n\t/**\n\t * Get the WantAuthnRequestsSigned setting, indicating the asserting party's\n\t * preference that relying parties should sign the AuthnRequest before sending.\n\t * @return the WantAuthnRequestsSigned value\n\t */\n\tboolean getWantAuthnRequestsSigned();\n\n\t/**\n\t * Get the list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this\n\t * asserting party, in preference order.\n\t *\n\t * <p>\n\t * Equivalent to the values found in &lt;SigningMethod Algorithm=\"...\"/&gt; in the\n\t * asserting party's &lt;IDPSSODescriptor&gt;.\n\t * @return the list of SigningMethod Algorithms\n\t * @since 5.5\n\t */\n\tList<String> getSigningAlgorithms();\n\n\t/**\n\t * Get all verification {@link Saml2X509Credential}s associated with this asserting\n\t * party\n\t * @return all verification {@link Saml2X509Credential}s associated with this\n\t * asserting party\n\t * @since 5.4\n\t */\n\tCollection<Saml2X509Credential> getVerificationX509Credentials();\n\n\t/**\n\t * Get all encryption {@link Saml2X509Credential}s associated with this asserting\n\t * party\n\t * @return all encryption {@link Saml2X509Credential}s associated with this asserting\n\t * party\n\t * @since 5.4\n\t */\n\tCollection<Saml2X509Credential> getEncryptionX509Credentials();\n\n\t/**\n\t * Get the <a href=\n\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a>\n\t * Location.\n\t *\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleSignOnService Location=\"...\"/&gt; in the\n\t * asserting party's &lt;IDPSSODescriptor&gt;.\n\t * @return the SingleSignOnService Location\n\t */\n\tString getSingleSignOnServiceLocation();\n\n\t/**\n\t * Get the <a href=\n\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a>\n\t * Binding.\n\t *\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleSignOnService Binding=\"...\"/&gt; in the\n\t * asserting party's &lt;IDPSSODescriptor&gt;.\n\t * @return the SingleSignOnService Location\n\t */\n\tSaml2MessageBinding getSingleSignOnServiceBinding();\n\n\t/**\n\t * Get the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t * Location</a>\n\t *\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in the\n\t * asserting party's &lt;IDPSSODescriptor&gt;.\n\t * @return the SingleLogoutService Location\n\t * @since 5.6\n\t */\n\t@Nullable String getSingleLogoutServiceLocation();\n\n\t/**\n\t * Get the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t * Response Location</a>\n\t *\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in the\n\t * asserting party's &lt;IDPSSODescriptor&gt;.\n\t * @return the SingleLogoutService Response Location\n\t * @since 5.6\n\t */\n\t@Nullable String getSingleLogoutServiceResponseLocation();\n\n\t/**\n\t * Get the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t * Binding</a>\n\t *\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in the\n\t * asserting party's &lt;IDPSSODescriptor&gt;.\n\t * @return the SingleLogoutService Binding\n\t * @since 5.6\n\t */\n\tSaml2MessageBinding getSingleLogoutServiceBinding();\n\n\tBuilder<?> mutate();\n\n\tinterface Builder<B extends Builder<B>> {\n\n\t\t/**\n\t\t * Set the asserting party's <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor\">EntityID</a>.\n\t\t * Equivalent to the value found in the asserting party's &lt;EntityDescriptor\n\t\t * EntityID=\"...\"/&gt;\n\t\t * @param entityId the asserting party's EntityID\n\t\t * @return the {@link B} for further configuration\n\t\t */\n\t\tB entityId(String entityId);\n\n\t\t/**\n\t\t * Set the WantAuthnRequestsSigned setting, indicating the asserting party's\n\t\t * preference that relying parties should sign the AuthnRequest before sending.\n\t\t * @param wantAuthnRequestsSigned the WantAuthnRequestsSigned setting\n\t\t * @return the {@link B} for further configuration\n\t\t */\n\t\tB wantAuthnRequestsSigned(boolean wantAuthnRequestsSigned);\n\n\t\t/**\n\t\t * Apply this {@link Consumer} to the list of SigningMethod Algorithms\n\t\t * @param signingMethodAlgorithmsConsumer a {@link Consumer} of the list of\n\t\t * SigningMethod Algorithms\n\t\t * @return this {@link B} for further configuration\n\t\t * @since 5.5\n\t\t */\n\t\tB signingAlgorithms(Consumer<List<String>> signingMethodAlgorithmsConsumer);\n\n\t\t/**\n\t\t * Apply this {@link Consumer} to the list of {@link Saml2X509Credential}s\n\t\t * @param credentialsConsumer a {@link Consumer} of the {@link List} of\n\t\t * {@link Saml2X509Credential}s\n\t\t * @return the {@link RelyingPartyRegistration.Builder} for further configuration\n\t\t * @since 5.4\n\t\t */\n\t\tB verificationX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer);\n\n\t\t/**\n\t\t * Apply this {@link Consumer} to the list of {@link Saml2X509Credential}s\n\t\t * @param credentialsConsumer a {@link Consumer} of the {@link List} of\n\t\t * {@link Saml2X509Credential}s\n\t\t * @return the {@link RelyingPartyRegistration.Builder} for further configuration\n\t\t * @since 5.4\n\t\t */\n\t\tB encryptionX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer);\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a>\n\t\t * Location.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleSignOnService Location=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @param singleSignOnServiceLocation the SingleSignOnService Location\n\t\t * @return the {@link B} for further configuration\n\t\t */\n\t\tB singleSignOnServiceLocation(String singleSignOnServiceLocation);\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a>\n\t\t * Binding.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleSignOnService Binding=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @param singleSignOnServiceBinding the SingleSignOnService Binding\n\t\t * @return the {@link B} for further configuration\n\t\t */\n\t\tB singleSignOnServiceBinding(Saml2MessageBinding singleSignOnServiceBinding);\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Location</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @param singleLogoutServiceLocation the SingleLogoutService Location\n\t\t * @return the {@link B} for further configuration\n\t\t * @since 5.6\n\t\t */\n\t\tB singleLogoutServiceLocation(String singleLogoutServiceLocation);\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Response Location</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService\n\t\t * ResponseLocation=\"...\"/&gt; in the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @param singleLogoutServiceResponseLocation the SingleLogoutService Response\n\t\t * Location\n\t\t * @return the {@link B} for further configuration\n\t\t * @since 5.6\n\t\t */\n\t\tB singleLogoutServiceResponseLocation(String singleLogoutServiceResponseLocation);\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Binding</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @param singleLogoutServiceBinding the SingleLogoutService Binding\n\t\t * @return the {@link B} for further configuration\n\t\t * @since 5.6\n\t\t */\n\t\tB singleLogoutServiceBinding(Saml2MessageBinding singleLogoutServiceBinding);\n\n\t\t/**\n\t\t * Creates an immutable ProviderDetails object representing the configuration for\n\t\t * an Identity Provider, IDP\n\t\t * @return immutable ProviderDetails object\n\t\t */\n\t\tAssertingPartyMetadata build();\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/AssertingPartyMetadataRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A repository for retrieving SAML 2.0 Asserting Party Metadata\n *\n * @author Josh Cummings\n * @since 6.4\n * @see BaseOpenSamlAssertingPartyMetadataRepository\n * @see org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations\n */\npublic interface AssertingPartyMetadataRepository extends Iterable<AssertingPartyMetadata> {\n\n\t/**\n\t * Retrieve an {@link AssertingPartyMetadata} by its <a href=\n\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor\">EntityID</a>.\n\t * @param entityId the EntityID to lookup\n\t * @return the found {@link AssertingPartyMetadata}, or {@code null} otherwise\n\t */\n\tdefault @Nullable AssertingPartyMetadata findByEntityId(String entityId) {\n\t\tfor (AssertingPartyMetadata metadata : this) {\n\t\t\tif (metadata.getEntityId().equals(entityId)) {\n\t\t\t\treturn metadata;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/BaseOpenSamlAssertingPartyMetadataRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.util.Iterator;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.saml.criterion.EntityRoleCriterion;\nimport org.opensaml.saml.metadata.IterableMetadataSource;\nimport org.opensaml.saml.metadata.resolver.MetadataResolver;\nimport org.opensaml.saml.metadata.resolver.impl.AbstractBatchMetadataResolver;\nimport org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver;\nimport org.opensaml.saml.metadata.resolver.index.MetadataIndex;\nimport org.opensaml.saml.metadata.resolver.index.impl.RoleMetadataIndex;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\nimport org.opensaml.saml.saml2.metadata.IDPSSODescriptor;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.util.Assert;\n\nclass BaseOpenSamlAssertingPartyMetadataRepository implements AssertingPartyMetadataRepository {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final MetadataResolverAdapter metadataResolver;\n\n\tprivate final Supplier<Iterator<EntityDescriptor>> descriptors;\n\n\t/**\n\t * Construct an {@link BaseOpenSamlAssertingPartyMetadataRepository} using the\n\t * provided {@link MetadataResolver}.\n\t *\n\t * <p>\n\t * The {@link MetadataResolver} should either be of type\n\t * {@link IterableMetadataSource} or it should have a {@link RoleMetadataIndex}\n\t * configured.\n\t * @param metadataResolver the {@link MetadataResolver} to use\n\t */\n\tBaseOpenSamlAssertingPartyMetadataRepository(MetadataResolverAdapter metadataResolver) {\n\t\tAssert.notNull(metadataResolver, \"metadataResolver cannot be null\");\n\t\tif (isRoleIndexed(metadataResolver.metadataResolver)) {\n\t\t\tthis.descriptors = this::allIndexedEntities;\n\t\t}\n\t\telse if (metadataResolver.metadataResolver instanceof IterableMetadataSource source) {\n\t\t\tthis.descriptors = source::iterator;\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"metadataResolver must be an IterableMetadataSource or have a RoleMetadataIndex\");\n\t\t}\n\t\tthis.metadataResolver = metadataResolver;\n\t}\n\n\tprivate static boolean isRoleIndexed(MetadataResolver resolver) {\n\t\tif (!(resolver instanceof AbstractBatchMetadataResolver batch)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (MetadataIndex index : batch.getIndexes()) {\n\t\t\tif (index instanceof RoleMetadataIndex) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate Iterator<EntityDescriptor> allIndexedEntities() {\n\t\tEntityRoleCriterion idps = new EntityRoleCriterion(IDPSSODescriptor.DEFAULT_ELEMENT_NAME);\n\t\ttry {\n\t\t\treturn this.metadataResolver.resolve(idps).iterator();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Iterator<AssertingPartyMetadata> iterator() {\n\t\tIterator<EntityDescriptor> descriptors = this.descriptors.get();\n\t\treturn new Iterator<>() {\n\t\t\t@Override\n\t\t\tpublic boolean hasNext() {\n\t\t\t\treturn descriptors.hasNext();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic AssertingPartyMetadata next() {\n\t\t\t\treturn OpenSamlAssertingPartyDetails.withEntityDescriptor(descriptors.next()).build();\n\t\t\t}\n\t\t};\n\t}\n\n\t@Override\n\tpublic @Nullable AssertingPartyMetadata findByEntityId(String entityId) {\n\t\tEntityDescriptor descriptor = resolveSingle(new EntityIdCriterion(entityId));\n\t\tif (descriptor == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn OpenSamlAssertingPartyDetails.withEntityDescriptor(descriptor).build();\n\t}\n\n\tprivate @Nullable EntityDescriptor resolveSingle(EntityIdCriterion criterion) {\n\t\ttry {\n\t\t\treturn this.metadataResolver.resolveSingle(criterion);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\tstatic MetadataResolver initialize(ResourceBackedMetadataResolver metadataResolver) {\n\t\ttry {\n\t\t\tmetadataResolver.setId(BaseOpenSamlAssertingPartyMetadataRepository.class.getName() + \".metadataResolver\");\n\t\t\tmetadataResolver.setIndexes(Set.of(new RoleMetadataIndex()));\n\t\t\tmetadataResolver.initialize();\n\t\t\treturn metadataResolver;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\tabstract static class MetadataResolverAdapter {\n\n\t\tfinal MetadataResolver metadataResolver;\n\n\t\tMetadataResolverAdapter(MetadataResolver metadataResolver) {\n\t\t\tthis.metadataResolver = metadataResolver;\n\t\t}\n\n\t\tabstract @Nullable EntityDescriptor resolveSingle(EntityIdCriterion entityId) throws Exception;\n\n\t\tabstract Iterable<EntityDescriptor> resolve(EntityRoleCriterion role) throws Exception;\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/CachingRelyingPartyRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.util.Iterator;\nimport java.util.Spliterator;\nimport java.util.concurrent.Callable;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.concurrent.ConcurrentMapCache;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link IterableRelyingPartyRegistrationRepository} that lazily queries and caches\n * metadata from a backing {@link IterableRelyingPartyRegistrationRepository}. Delegates\n * caching policies to Spring Cache.\n *\n * @author Josh Cummings\n * @since 6.4\n */\npublic final class CachingRelyingPartyRegistrationRepository implements IterableRelyingPartyRegistrationRepository {\n\n\tprivate final Callable<IterableRelyingPartyRegistrationRepository> registrationLoader;\n\n\tprivate Cache cache = new ConcurrentMapCache(\"registrations\");\n\n\tpublic CachingRelyingPartyRegistrationRepository(Callable<IterableRelyingPartyRegistrationRepository> loader) {\n\t\tthis.registrationLoader = loader;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Iterator<RelyingPartyRegistration> iterator() {\n\t\treturn registrations().iterator();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable RelyingPartyRegistration findByRegistrationId(String registrationId) {\n\t\treturn registrations().findByRegistrationId(registrationId);\n\t}\n\n\t@Override\n\tpublic @Nullable RelyingPartyRegistration findUniqueByAssertingPartyEntityId(String entityId) {\n\t\treturn registrations().findUniqueByAssertingPartyEntityId(entityId);\n\t}\n\n\t@Override\n\tpublic void forEach(Consumer<? super RelyingPartyRegistration> action) {\n\t\tregistrations().forEach(action);\n\t}\n\n\t@Override\n\tpublic Spliterator<RelyingPartyRegistration> spliterator() {\n\t\treturn registrations().spliterator();\n\t}\n\n\tprivate IterableRelyingPartyRegistrationRepository registrations() {\n\t\tIterableRelyingPartyRegistrationRepository registrations = this.cache.get(\"registrations\",\n\t\t\t\tthis.registrationLoader);\n\t\tAssert.notNull(registrations, \"cache loader failed to return a repostory instance\");\n\t\treturn registrations;\n\t}\n\n\t/**\n\t * Use this cache for the completed {@link RelyingPartyRegistration} instances.\n\t *\n\t * <p>\n\t * Defaults to {@link ConcurrentMapCache}, meaning that the registrations are cached\n\t * without expiry. To turn off the cache, use\n\t * {@link org.springframework.cache.support.NoOpCache}.\n\t * @param cache the {@link Cache} to use\n\t */\n\tpublic void setCache(Cache cache) {\n\t\tAssert.notNull(cache, \"cache cannot be null\");\n\t\tthis.cache = cache;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\n\n/**\n * An in-memory implementation of {@link RelyingPartyRegistrationRepository}. Also\n * implements {@link Iterable} to simplify the default login page.\n *\n * @author Filip Hanik\n * @author Josh Cummings\n * @since 5.2\n */\npublic class InMemoryRelyingPartyRegistrationRepository implements IterableRelyingPartyRegistrationRepository {\n\n\tprivate final Map<String, RelyingPartyRegistration> byRegistrationId;\n\n\tprivate final Map<String, List<RelyingPartyRegistration>> byAssertingPartyEntityId;\n\n\tpublic InMemoryRelyingPartyRegistrationRepository(RelyingPartyRegistration... registrations) {\n\t\tthis(Arrays.asList(registrations));\n\t}\n\n\tpublic InMemoryRelyingPartyRegistrationRepository(Collection<RelyingPartyRegistration> registrations) {\n\t\tAssert.notEmpty(registrations, \"registrations cannot be empty\");\n\t\tthis.byRegistrationId = createMappingToIdentityProvider(registrations);\n\t\tthis.byAssertingPartyEntityId = createMappingByAssertingPartyEntityId(registrations);\n\t}\n\n\tprivate static Map<String, RelyingPartyRegistration> createMappingToIdentityProvider(\n\t\t\tCollection<RelyingPartyRegistration> rps) {\n\t\tLinkedHashMap<String, RelyingPartyRegistration> result = new LinkedHashMap<>();\n\t\tfor (RelyingPartyRegistration rp : rps) {\n\t\t\tAssert.notNull(rp, \"relying party collection cannot contain null values\");\n\t\t\tString key = rp.getRegistrationId();\n\t\t\tAssert.notNull(key, \"relying party identifier cannot be null\");\n\t\t\tAssert.isNull(result.get(key), () -> \"relying party duplicate identifier '\" + key + \"' detected.\");\n\t\t\tresult.put(key, rp);\n\t\t}\n\t\treturn Collections.unmodifiableMap(result);\n\t}\n\n\tprivate static Map<String, List<RelyingPartyRegistration>> createMappingByAssertingPartyEntityId(\n\t\t\tCollection<RelyingPartyRegistration> rps) {\n\t\tMultiValueMap<String, RelyingPartyRegistration> result = new LinkedMultiValueMap<>();\n\t\tfor (RelyingPartyRegistration rp : rps) {\n\t\t\tresult.add(rp.getAssertingPartyMetadata().getEntityId(), rp);\n\t\t}\n\t\treturn Collections.unmodifiableMap(result);\n\t}\n\n\t@Override\n\tpublic @Nullable RelyingPartyRegistration findByRegistrationId(String id) {\n\t\treturn this.byRegistrationId.get(id);\n\t}\n\n\t@Override\n\tpublic @Nullable RelyingPartyRegistration findUniqueByAssertingPartyEntityId(String entityId) {\n\t\tCollection<RelyingPartyRegistration> registrations = this.byAssertingPartyEntityId.get(entityId);\n\t\tif (registrations == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (registrations.size() > 1) {\n\t\t\treturn null;\n\t\t}\n\t\treturn registrations.iterator().next();\n\t}\n\n\t@Override\n\tpublic Iterator<RelyingPartyRegistration> iterator() {\n\t\treturn this.byRegistrationId.values().iterator();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/IterableRelyingPartyRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\n/**\n * An interface that simplifies APIs which require the\n * {@link RelyingPartyRegistrationRepository} to also be {@link Iterable}\n *\n * @author Josh Cummings\n * @since 6.4\n * @see InMemoryRelyingPartyRegistrationRepository\n * @see CachingRelyingPartyRegistrationRepository\n */\npublic interface IterableRelyingPartyRegistrationRepository\n\t\textends RelyingPartyRegistrationRepository, Iterable<RelyingPartyRegistration> {\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/JdbcAssertingPartyMetadataRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.serializer.DefaultDeserializer;\nimport org.springframework.core.serializer.DefaultSerializer;\nimport org.springframework.core.serializer.Deserializer;\nimport org.springframework.core.serializer.Serializer;\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails;\nimport org.springframework.util.Assert;\nimport org.springframework.util.function.ThrowingFunction;\n\n/**\n * A JDBC implementation of {@link AssertingPartyMetadataRepository}.\n *\n * @author Cathy Wang\n * @since 7.0\n */\npublic final class JdbcAssertingPartyMetadataRepository implements AssertingPartyMetadataRepository {\n\n\tprivate final JdbcOperations jdbcOperations;\n\n\tprivate final RowMapper<AssertingPartyMetadata> assertingPartyMetadataRowMapper = new AssertingPartyMetadataRowMapper();\n\n\tprivate final AssertingPartyMetadataParametersMapper assertingPartyMetadataParametersMapper = new AssertingPartyMetadataParametersMapper();\n\n\t// @formatter:off\n\tstatic final String[] COLUMN_NAMES = { \"entity_id\",\n\t\t\t\"single_sign_on_service_location\",\n\t\t\t\"single_sign_on_service_binding\",\n\t\t\t\"want_authn_requests_signed\",\n\t\t\t\"signing_algorithms\",\n\t\t\t\"verification_credentials\",\n\t\t\t\"encryption_credentials\",\n\t\t\t\"single_logout_service_location\",\n\t\t\t\"single_logout_service_response_location\",\n\t\t\t\"single_logout_service_binding\" };\n\n\t// @formatter:on\n\n\tprivate static final String TABLE_NAME = \"saml2_asserting_party_metadata\";\n\n\tprivate static final String ENTITY_ID_FILTER = \"entity_id = ?\";\n\n\t// @formatter:off\n\tprivate static final String LOAD_BY_ID_SQL = \"SELECT \" + String.join(\",\", COLUMN_NAMES)\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + ENTITY_ID_FILTER;\n\n\tprivate static final String LOAD_ALL_SQL = \"SELECT \" + String.join(\",\", COLUMN_NAMES)\n\t\t\t+ \" FROM \" + TABLE_NAME;\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String SAVE_CREDENTIAL_RECORD_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t+ \" (\" + String.join(\",\", COLUMN_NAMES) + \") VALUES (\" + String.join(\",\", Collections.nCopies(COLUMN_NAMES.length, \"?\")) + \")\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String UPDATE_CREDENTIAL_RECORD_SQL = \"UPDATE \" + TABLE_NAME\n\t\t\t+ \" SET \" + String.join(\" = ?,\", Arrays.copyOfRange(COLUMN_NAMES, 1, COLUMN_NAMES.length))\n\t\t\t+ \" = ?\"\n\t\t\t+ \" WHERE \" + ENTITY_ID_FILTER;\n\t// @formatter:on\n\n\t/**\n\t * Constructs a {@code JdbcRelyingPartyRegistrationRepository} using the provided\n\t * parameters.\n\t * @param jdbcOperations the JDBC operations\n\t */\n\tpublic JdbcAssertingPartyMetadataRepository(JdbcOperations jdbcOperations) {\n\t\tAssert.notNull(jdbcOperations, \"jdbcOperations cannot be null\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t}\n\n\t@Override\n\tpublic @Nullable AssertingPartyMetadata findByEntityId(String entityId) {\n\t\tAssert.hasText(entityId, \"entityId cannot be empty\");\n\t\tSqlParameterValue[] parameters = new SqlParameterValue[] { new SqlParameterValue(Types.VARCHAR, entityId) };\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\tList<AssertingPartyMetadata> result = this.jdbcOperations.query(LOAD_BY_ID_SQL, pss,\n\t\t\t\tthis.assertingPartyMetadataRowMapper);\n\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t}\n\n\t@Override\n\tpublic Iterator<AssertingPartyMetadata> iterator() {\n\t\tList<AssertingPartyMetadata> result = this.jdbcOperations.query(LOAD_ALL_SQL,\n\t\t\t\tthis.assertingPartyMetadataRowMapper);\n\t\treturn result.iterator();\n\t}\n\n\t/**\n\t * Persist this {@link AssertingPartyMetadata}\n\t * @param metadata the metadata to persist\n\t */\n\tpublic void save(AssertingPartyMetadata metadata) {\n\t\tAssert.notNull(metadata, \"metadata cannot be null\");\n\t\tint rows = updateCredentialRecord(metadata);\n\t\tif (rows == 0) {\n\t\t\tinsertCredentialRecord(metadata);\n\t\t}\n\t}\n\n\tprivate void insertCredentialRecord(AssertingPartyMetadata metadata) {\n\t\tList<SqlParameterValue> parameters = this.assertingPartyMetadataParametersMapper.apply(metadata);\n\t\tthis.jdbcOperations.update(SAVE_CREDENTIAL_RECORD_SQL, parameters.toArray());\n\t}\n\n\tprivate int updateCredentialRecord(AssertingPartyMetadata metadata) {\n\t\tList<SqlParameterValue> parameters = this.assertingPartyMetadataParametersMapper.apply(metadata);\n\t\tSqlParameterValue credentialId = parameters.remove(0);\n\t\tparameters.add(credentialId);\n\t\treturn this.jdbcOperations.update(UPDATE_CREDENTIAL_RECORD_SQL, parameters.toArray());\n\t}\n\n\t/**\n\t * The default {@link RowMapper} that maps the current row in\n\t * {@code java.sql.ResultSet} to {@link AssertingPartyMetadata}.\n\t */\n\tprivate static final class AssertingPartyMetadataRowMapper implements RowMapper<AssertingPartyMetadata> {\n\n\t\tprivate final Deserializer<Object> deserializer = new DefaultDeserializer();\n\n\t\t@Override\n\t\tpublic AssertingPartyMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\tString entityId = rs.getString(COLUMN_NAMES[0]);\n\t\t\tString singleSignOnUrl = rs.getString(COLUMN_NAMES[1]);\n\t\t\tSaml2MessageBinding singleSignOnBinding = Saml2MessageBinding.from(rs.getString(COLUMN_NAMES[2]));\n\t\t\tAssert.notNull(singleSignOnBinding, \"retrieved an unsupported binding \" + rs.getString(COLUMN_NAMES[2]));\n\t\t\tboolean singleSignOnSignRequest = rs.getBoolean(COLUMN_NAMES[3]);\n\t\t\tList<String> algorithms = List.of(rs.getString(COLUMN_NAMES[4]).split(\",\"));\n\t\t\tbyte[] verificationCredentialsBytes = rs.getBytes(COLUMN_NAMES[5]);\n\t\t\tbyte[] encryptionCredentialsBytes = rs.getBytes(COLUMN_NAMES[6]);\n\t\t\tThrowingFunction<byte[], Collection<Saml2X509Credential>> credentials = (\n\t\t\t\t\tbytes) -> (Collection<Saml2X509Credential>) this.deserializer.deserializeFromByteArray(bytes);\n\t\t\tAssertingPartyMetadata.Builder<?> builder = new AssertingPartyDetails.Builder();\n\t\t\tCollection<Saml2X509Credential> verificationCredentials = credentials.apply(verificationCredentialsBytes);\n\t\t\tCollection<Saml2X509Credential> encryptionCredentials = (encryptionCredentialsBytes != null)\n\t\t\t\t\t? credentials.apply(encryptionCredentialsBytes) : List.of();\n\t\t\tString singleLogoutUrl = rs.getString(COLUMN_NAMES[7]);\n\t\t\tString singleLogoutResponseUrl = rs.getString(COLUMN_NAMES[8]);\n\t\t\tSaml2MessageBinding singleLogoutBinding = Saml2MessageBinding.from(rs.getString(COLUMN_NAMES[9]));\n\t\t\tAssert.notNull(singleLogoutBinding, \"retrieved an unsupported binding \" + rs.getString(COLUMN_NAMES[9]));\n\n\t\t\tbuilder.entityId(entityId)\n\t\t\t\t.wantAuthnRequestsSigned(singleSignOnSignRequest)\n\t\t\t\t.singleSignOnServiceLocation(singleSignOnUrl)\n\t\t\t\t.singleSignOnServiceBinding(singleSignOnBinding)\n\t\t\t\t.singleLogoutServiceLocation(singleLogoutUrl)\n\t\t\t\t.singleLogoutServiceBinding(singleLogoutBinding)\n\t\t\t\t.singleLogoutServiceResponseLocation(singleLogoutResponseUrl)\n\t\t\t\t.signingAlgorithms((a) -> a.addAll(algorithms))\n\t\t\t\t.verificationX509Credentials((c) -> c.addAll(verificationCredentials))\n\t\t\t\t.encryptionX509Credentials((c) -> c.addAll(encryptionCredentials));\n\t\t\treturn builder.build();\n\t\t}\n\n\t}\n\n\tprivate static class AssertingPartyMetadataParametersMapper\n\t\t\timplements Function<AssertingPartyMetadata, List<SqlParameterValue>> {\n\n\t\tprivate final Serializer<Object> serializer = new DefaultSerializer();\n\n\t\t@Override\n\t\tpublic List<SqlParameterValue> apply(AssertingPartyMetadata record) {\n\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getEntityId()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getSingleSignOnServiceLocation()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getSingleSignOnServiceBinding().getUrn()));\n\t\t\tparameters.add(new SqlParameterValue(Types.BOOLEAN, record.getWantAuthnRequestsSigned()));\n\t\t\tparameters.add(new SqlParameterValue(Types.BLOB, String.join(\",\", record.getSigningAlgorithms())));\n\t\t\tThrowingFunction<Collection<Saml2X509Credential>, byte[]> credentials = this.serializer::serializeToByteArray;\n\t\t\tparameters\n\t\t\t\t.add(new SqlParameterValue(Types.BLOB, credentials.apply(record.getVerificationX509Credentials())));\n\t\t\tparameters.add(new SqlParameterValue(Types.BLOB, credentials.apply(record.getEncryptionX509Credentials())));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getSingleLogoutServiceLocation()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getSingleLogoutServiceResponseLocation()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getSingleLogoutServiceBinding().getUrn()));\n\n\t\t\treturn parameters;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyDetails.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.Serial;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.ext.saml2alg.SigningMethod;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\nimport org.opensaml.saml.saml2.metadata.Extensions;\nimport org.opensaml.saml.saml2.metadata.IDPSSODescriptor;\nimport org.opensaml.saml.saml2.metadata.KeyDescriptor;\nimport org.opensaml.saml.saml2.metadata.SingleLogoutService;\nimport org.opensaml.saml.saml2.metadata.SingleSignOnService;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoSupport;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link RelyingPartyRegistration.AssertingPartyDetails} that contains\n * OpenSAML-specific members\n *\n * @author Josh Cummings\n * @since 5.7\n */\npublic final class OpenSamlAssertingPartyDetails extends RelyingPartyRegistration.AssertingPartyDetails {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2412785556799182734L;\n\n\tprivate final transient EntityDescriptor descriptor;\n\n\tOpenSamlAssertingPartyDetails(RelyingPartyRegistration.AssertingPartyDetails details, EntityDescriptor descriptor) {\n\t\tsuper(details.getEntityId(), details.getWantAuthnRequestsSigned(), details.getSigningAlgorithms(),\n\t\t\t\tdetails.getVerificationX509Credentials(), details.getEncryptionX509Credentials(),\n\t\t\t\tdetails.getSingleSignOnServiceLocation(), details.getSingleSignOnServiceBinding(),\n\t\t\t\tdetails.getSingleLogoutServiceLocation(), details.getSingleLogoutServiceResponseLocation(),\n\t\t\t\tdetails.getSingleLogoutServiceBinding());\n\t\tthis.descriptor = descriptor;\n\t}\n\n\t/**\n\t * Get the {@link EntityDescriptor} that underlies this\n\t * {@link org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails}\n\t * @return the {@link EntityDescriptor}\n\t */\n\tpublic EntityDescriptor getEntityDescriptor() {\n\t\treturn this.descriptor;\n\t}\n\n\t/**\n\t * Use this {@link EntityDescriptor} to begin building an\n\t * {@link org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails}\n\t * @param entity the {@link EntityDescriptor} to use\n\t * @return the\n\t * {@link org.springframework.security.saml2.provider.service.registration.OpenSamlAssertingPartyDetails.Builder}\n\t * for further configurations\n\t */\n\tpublic static OpenSamlAssertingPartyDetails.Builder withEntityDescriptor(EntityDescriptor entity) {\n\t\tIDPSSODescriptor idpssoDescriptor = entity.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);\n\t\tif (idpssoDescriptor == null) {\n\t\t\tthrow new Saml2Exception(\"Metadata response is missing the necessary IDPSSODescriptor element\");\n\t\t}\n\t\tList<Saml2X509Credential> verification = new ArrayList<>();\n\t\tList<Saml2X509Credential> encryption = new ArrayList<>();\n\t\tfor (KeyDescriptor keyDescriptor : idpssoDescriptor.getKeyDescriptors()) {\n\t\t\tif (UsageType.SIGNING.equals(keyDescriptor.getUse())) {\n\t\t\t\tList<X509Certificate> certificates = certificates(keyDescriptor);\n\t\t\t\tfor (X509Certificate certificate : certificates) {\n\t\t\t\t\tverification.add(Saml2X509Credential.verification(certificate));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (UsageType.ENCRYPTION.equals(keyDescriptor.getUse())) {\n\t\t\t\tList<X509Certificate> certificates = certificates(keyDescriptor);\n\t\t\t\tfor (X509Certificate certificate : certificates) {\n\t\t\t\t\tencryption.add(Saml2X509Credential.encryption(certificate));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (UsageType.UNSPECIFIED.equals(keyDescriptor.getUse())) {\n\t\t\t\tList<X509Certificate> certificates = certificates(keyDescriptor);\n\t\t\t\tfor (X509Certificate certificate : certificates) {\n\t\t\t\t\tverification.add(Saml2X509Credential.verification(certificate));\n\t\t\t\t\tencryption.add(Saml2X509Credential.encryption(certificate));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (verification.isEmpty()) {\n\t\t\tthrow new Saml2Exception(\n\t\t\t\t\t\"Metadata response is missing verification certificates, necessary for verifying SAML assertions\");\n\t\t}\n\t\tString entityId = entity.getEntityID();\n\t\tAssert.notNull(entityId, \"EntityDescriptor#EntityID cannot be null\");\n\t\tOpenSamlAssertingPartyDetails.Builder builder = new OpenSamlAssertingPartyDetails.Builder(entity)\n\t\t\t.entityId(entityId)\n\t\t\t.wantAuthnRequestsSigned(Boolean.TRUE.equals(idpssoDescriptor.getWantAuthnRequestsSigned()))\n\t\t\t.verificationX509Credentials((c) -> c.addAll(verification))\n\t\t\t.encryptionX509Credentials((c) -> c.addAll(encryption));\n\n\t\tList<SigningMethod> signingMethods = signingMethods(idpssoDescriptor);\n\t\tfor (SigningMethod method : signingMethods) {\n\t\t\tAssert.notNull(method.getAlgorithm(), \"EntityDescriptor declares a SigningMethod with no value\");\n\t\t\tbuilder.signingAlgorithms((algorithms) -> algorithms.add(method.getAlgorithm()));\n\t\t}\n\t\tif (idpssoDescriptor.getSingleSignOnServices().isEmpty()) {\n\t\t\tthrow new Saml2Exception(\n\t\t\t\t\t\"Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests\");\n\t\t}\n\t\tfor (SingleSignOnService singleSignOnService : idpssoDescriptor.getSingleSignOnServices()) {\n\t\t\tSaml2MessageBinding binding;\n\t\t\tif (Saml2MessageBinding.POST.getUrn().equals(singleSignOnService.getBinding())) {\n\t\t\t\tbinding = Saml2MessageBinding.POST;\n\t\t\t}\n\t\t\telse if (Saml2MessageBinding.REDIRECT.getUrn().equals(singleSignOnService.getBinding())) {\n\t\t\t\tbinding = Saml2MessageBinding.REDIRECT;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString location = singleSignOnService.getLocation();\n\t\t\tAssert.notNull(location, \"EntityDescriptor has a SingleSignOnService declaration, but no Location\");\n\t\t\tbuilder.singleSignOnServiceLocation(location).singleSignOnServiceBinding(binding);\n\t\t\tbreak;\n\t\t}\n\t\tfor (SingleLogoutService singleLogoutService : idpssoDescriptor.getSingleLogoutServices()) {\n\t\t\tSaml2MessageBinding binding;\n\t\t\tif (Saml2MessageBinding.POST.getUrn().equals(singleLogoutService.getBinding())) {\n\t\t\t\tbinding = Saml2MessageBinding.POST;\n\t\t\t}\n\t\t\telse if (Saml2MessageBinding.REDIRECT.getUrn().equals(singleLogoutService.getBinding())) {\n\t\t\t\tbinding = Saml2MessageBinding.REDIRECT;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tString location = singleLogoutService.getLocation();\n\t\t\tAssert.notNull(location, \"EntityDescriptor has a SingleLogoutService declaration, but no Location\");\n\t\t\tString responseLocation = (singleLogoutService.getResponseLocation() == null) ? location\n\t\t\t\t\t: singleLogoutService.getResponseLocation();\n\t\t\tbuilder.singleLogoutServiceLocation(location)\n\t\t\t\t.singleLogoutServiceResponseLocation(responseLocation)\n\t\t\t\t.singleLogoutServiceBinding(binding);\n\t\t\tbreak;\n\t\t}\n\t\treturn builder;\n\t}\n\n\tprivate static List<X509Certificate> certificates(KeyDescriptor keyDescriptor) {\n\t\ttry {\n\t\t\treturn KeyInfoSupport.getCertificates(keyDescriptor.getKeyInfo());\n\t\t}\n\t\tcatch (CertificateException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\tprivate static List<SigningMethod> signingMethods(IDPSSODescriptor idpssoDescriptor) {\n\t\tExtensions extensions = idpssoDescriptor.getExtensions();\n\t\tList<SigningMethod> result = signingMethods(extensions);\n\t\tif (!result.isEmpty()) {\n\t\t\treturn result;\n\t\t}\n\t\tEntityDescriptor descriptor = (EntityDescriptor) Objects.requireNonNull(idpssoDescriptor.getParent());\n\t\textensions = descriptor.getExtensions();\n\t\treturn signingMethods(extensions);\n\t}\n\n\tprivate static <T> List<T> signingMethods(@Nullable Extensions extensions) {\n\t\tif (extensions != null) {\n\t\t\treturn (List<T>) extensions.getUnknownXMLObjects(SigningMethod.DEFAULT_ELEMENT_NAME);\n\t\t}\n\t\treturn new ArrayList<>();\n\t}\n\n\t@Override\n\tpublic OpenSamlAssertingPartyDetails.Builder mutate() {\n\t\treturn new OpenSamlAssertingPartyDetails.Builder(this.descriptor).entityId(getEntityId())\n\t\t\t.wantAuthnRequestsSigned(getWantAuthnRequestsSigned())\n\t\t\t.signingAlgorithms((algorithms) -> algorithms.addAll(getSigningAlgorithms()))\n\t\t\t.verificationX509Credentials((c) -> c.addAll(getVerificationX509Credentials()))\n\t\t\t.encryptionX509Credentials((c) -> c.addAll(getEncryptionX509Credentials()))\n\t\t\t.singleSignOnServiceLocation(getSingleSignOnServiceLocation())\n\t\t\t.singleSignOnServiceBinding(getSingleSignOnServiceBinding())\n\t\t\t.singleLogoutServiceLocation(getSingleLogoutServiceLocation())\n\t\t\t.singleLogoutServiceResponseLocation(getSingleLogoutServiceResponseLocation())\n\t\t\t.singleLogoutServiceBinding(getSingleLogoutServiceBinding());\n\t}\n\n\t/**\n\t * An OpenSAML version of\n\t * {@link org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails.Builder}\n\t * that contains the underlying {@link EntityDescriptor}\n\t */\n\tpublic static final class Builder extends RelyingPartyRegistration.AssertingPartyDetails.Builder {\n\n\t\tprivate EntityDescriptor descriptor;\n\n\t\tprivate Builder(EntityDescriptor descriptor) {\n\t\t\tthis.descriptor = descriptor;\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder entityId(String entityId) {\n\t\t\treturn (Builder) super.entityId(entityId);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder wantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) {\n\t\t\treturn (Builder) super.wantAuthnRequestsSigned(wantAuthnRequestsSigned);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder signingAlgorithms(Consumer<List<String>> signingMethodAlgorithmsConsumer) {\n\t\t\treturn (Builder) super.signingAlgorithms(signingMethodAlgorithmsConsumer);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder verificationX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {\n\t\t\treturn (Builder) super.verificationX509Credentials(credentialsConsumer);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder encryptionX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {\n\t\t\treturn (Builder) super.encryptionX509Credentials(credentialsConsumer);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder singleSignOnServiceLocation(String singleSignOnServiceLocation) {\n\t\t\treturn (Builder) super.singleSignOnServiceLocation(singleSignOnServiceLocation);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder singleSignOnServiceBinding(Saml2MessageBinding singleSignOnServiceBinding) {\n\t\t\treturn (Builder) super.singleSignOnServiceBinding(singleSignOnServiceBinding);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder singleLogoutServiceLocation(@Nullable String singleLogoutServiceLocation) {\n\t\t\treturn (Builder) super.singleLogoutServiceLocation(singleLogoutServiceLocation);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder singleLogoutServiceResponseLocation(@Nullable String singleLogoutServiceResponseLocation) {\n\t\t\treturn (Builder) super.singleLogoutServiceResponseLocation(singleLogoutServiceResponseLocation);\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic Builder singleLogoutServiceBinding(Saml2MessageBinding singleLogoutServiceBinding) {\n\t\t\treturn (Builder) super.singleLogoutServiceBinding(singleLogoutServiceBinding);\n\t\t}\n\n\t\t/**\n\t\t * Build an\n\t\t * {@link org.springframework.security.saml2.provider.service.registration.OpenSamlAssertingPartyDetails}\n\t\t * @return\n\t\t */\n\t\t@Override\n\t\tpublic OpenSamlAssertingPartyDetails build() {\n\t\t\treturn new OpenSamlAssertingPartyDetails(super.build(), this.descriptor);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlMetadataUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.InputStream;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport net.shibboleth.shared.xml.ParserPool;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.saml.saml2.metadata.EntitiesDescriptor;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.util.Assert;\n\nfinal class OpenSamlMetadataUtils {\n\n\tprivate static final OpenSamlDeserializer saml;\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t\tsaml = resolveDeserializer();\n\t}\n\n\tstatic OpenSamlDeserializer resolveDeserializer() {\n\t\treturn new OpenSaml5Deserializer();\n\t}\n\n\tprivate OpenSamlMetadataUtils() {\n\n\t}\n\n\tstatic Collection<EntityDescriptor> descriptors(InputStream metadata) {\n\t\tXMLObject object = saml.deserialize(metadata);\n\t\tif (object instanceof EntityDescriptor descriptor) {\n\t\t\treturn Collections.singleton(descriptor);\n\t\t}\n\t\tif (object instanceof EntitiesDescriptor descriptors) {\n\t\t\treturn descriptors.getEntityDescriptors();\n\t\t}\n\t\tthrow new Saml2Exception(\"Unsupported element type: \" + object.getClass().getName());\n\t}\n\n\tprivate interface OpenSamlDeserializer {\n\n\t\tXMLObject deserialize(InputStream serialized);\n\n\t}\n\n\tprivate static class OpenSaml5Deserializer implements OpenSamlDeserializer {\n\n\t\t@Override\n\t\tpublic XMLObject deserialize(InputStream serialized) {\n\t\t\ttry {\n\t\t\t\tParserPool parserPool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\t\tAssert.notNull(parserPool, \"A ParserPool must be configured\");\n\t\t\t\tDocument document = parserPool.parse(serialized);\n\t\t\t\tElement element = document.getDocumentElement();\n\t\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\t\tif (unmarshaller == null) {\n\t\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t\t}\n\t\t\t\treturn unmarshaller.unmarshall(element);\n\t\t\t}\n\t\t\tcatch (Saml2Exception ex) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\ninterface OpenSamlOperations {\n\n\t<T extends XMLObject> T build(QName elementName);\n\n\t<T extends XMLObject> T deserialize(String serialized);\n\n\t<T extends XMLObject> T deserialize(InputStream serialized);\n\n\tSerializationConfigurer<?> serialize(XMLObject object);\n\n\tSerializationConfigurer<?> serialize(Element element);\n\n\tSignatureConfigurer<?> withSigningKeys(Collection<Saml2X509Credential> credentials);\n\n\tVerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials);\n\n\tDecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials);\n\n\tinterface SerializationConfigurer<B extends SerializationConfigurer<B>> {\n\n\t\tB prettyPrint(boolean pretty);\n\n\t\tString serialize();\n\n\t}\n\n\tinterface SignatureConfigurer<B extends SignatureConfigurer<B>> {\n\n\t\tB algorithms(List<String> algs);\n\n\t\t<O extends SignableXMLObject> O sign(O object);\n\n\t\tMap<String, String> sign(Map<String, String> params);\n\n\t}\n\n\tinterface VerificationConfigurer {\n\n\t\tVerificationConfigurer entityId(String entityId);\n\n\t\tCollection<Saml2Error> verify(SignableXMLObject signable);\n\n\t\tCollection<Saml2Error> verify(VerificationConfigurer.RedirectParameters parameters);\n\n\t\tfinal class RedirectParameters {\n\n\t\t\tprivate final String id;\n\n\t\t\tprivate final Issuer issuer;\n\n\t\t\tprivate final String algorithm;\n\n\t\t\tprivate final byte @Nullable [] signature;\n\n\t\t\tprivate final byte[] content;\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"SAML request's ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"SAML request's Issuer cannot be null\");\n\t\t\t\tthis.id = request.getID();\n\t\t\t\tthis.issuer = request.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_REQUEST, relayState, queryParams);\n\t\t\t}\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"SAML response's ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"SAML response's Issuer cannot be null\");\n\t\t\t\tthis.id = response.getID();\n\t\t\t\tthis.issuer = response.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_RESPONSE, relayState, queryParams);\n\t\t\t}\n\n\t\t\tstatic byte[] getContent(String samlObject, @Nullable String relayState,\n\t\t\t\t\tfinal Map<String, String> queryParams) {\n\t\t\t\tif (Objects.nonNull(relayState)) {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s&%s=%s\", samlObject, queryParams.get(samlObject),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.RELAY_STATE, queryParams.get(Saml2ParameterNames.RELAY_STATE),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.SIG_ALG, queryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s\", samlObject, queryParams.get(samlObject), Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\t\t\tqueryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getId() {\n\t\t\t\treturn this.id;\n\t\t\t}\n\n\t\t\tIssuer getIssuer() {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\tbyte[] getContent() {\n\t\t\t\treturn this.content;\n\t\t\t}\n\n\t\t\tString getAlgorithm() {\n\t\t\t\treturn this.algorithm;\n\t\t\t}\n\n\t\t\tbyte @Nullable [] getSignature() {\n\t\t\t\treturn this.signature;\n\t\t\t}\n\n\t\t\tboolean hasSignature() {\n\t\t\t\treturn this.signature != null;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface DecryptionConfigurer {\n\n\t\tvoid decrypt(XMLObject object);\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\n\n/**\n * An {@link HttpMessageConverter} that takes an {@code IDPSSODescriptor} in an HTTP\n * response and converts it into a {@link RelyingPartyRegistration.Builder}.\n *\n * The primary use case for this is constructing a {@link RelyingPartyRegistration} for\n * inclusion in a {@link RelyingPartyRegistrationRepository}. To do so, you can include an\n * instance of this converter in a {@link org.springframework.web.client.RestOperations}\n * like so:\n *\n * <pre>\n * \t\tRestOperations rest = new RestTemplate(Collections.singletonList(\n *     \t\t\tnew RelyingPartyRegistrationsBuilderHttpMessageConverter()));\n * \t\tRelyingPartyRegistration.Builder builder = rest.getForObject\n * \t\t\t\t(\"https://idp.example.org/metadata\", RelyingPartyRegistration.Builder.class);\n * \t\tRelyingPartyRegistration registration = builder.registrationId(\"registration-id\").build();\n * </pre>\n *\n * Note that this will only configure the asserting party (IDP) half of the\n * {@link RelyingPartyRegistration}, meaning where and how to send AuthnRequests, how to\n * verify Assertions, etc.\n *\n * To further configure the {@link RelyingPartyRegistration} with relying party (SP)\n * information, you may invoke the appropriate methods on the builder.\n *\n * @author Josh Cummings\n * @since 5.4\n */\npublic class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter\n\t\timplements HttpMessageConverter<RelyingPartyRegistration.Builder> {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\t@Override\n\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn RelyingPartyRegistration.Builder.class.isAssignableFrom(clazz);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\treturn Arrays.asList(MediaType.APPLICATION_XML, MediaType.TEXT_XML);\n\t}\n\n\t@Override\n\tpublic RelyingPartyRegistration.Builder read(Class<? extends RelyingPartyRegistration.Builder> clazz,\n\t\t\tHttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {\n\t\treturn RelyingPartyRegistrations.fromMetadata(inputMessage.getBody());\n\t}\n\n\t@Override\n\tpublic void write(RelyingPartyRegistration.Builder builder, @Nullable MediaType contentType,\n\t\t\tHttpOutputMessage outputMessage) throws HttpMessageNotWritableException {\n\t\tthrow new HttpMessageNotWritableException(\"This converter cannot write a RelyingPartyRegistration.Builder\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Represents a configured relying party (aka Service Provider) and asserting party (aka\n * Identity Provider) pair.\n *\n * <p>\n * Each RP/AP pair is uniquely identified using a {@code registrationId}, an arbitrary\n * string.\n *\n * <p>\n * A fully configured registration may look like:\n *\n * <pre>\n *\tString registrationId = \"simplesamlphp\";\n *\n * \tString relyingPartyEntityId = \"{baseUrl}/saml2/service-provider-metadata/{registrationId}\";\n *\tString assertingConsumerServiceLocation = \"{baseUrl}/login/saml2/sso/{registrationId}\";\n *\tSaml2X509Credential relyingPartySigningCredential = ...;\n *\n *\tString assertingPartyEntityId = \"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\";\n *\tString singleSignOnServiceLocation = \"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\";\n * \tSaml2X509Credential assertingPartyVerificationCredential = ...;\n *\n *\n *\tRelyingPartyRegistration rp = RelyingPartyRegistration.withRegistrationId(registrationId)\n * \t\t\t.entityId(relyingPartyEntityId)\n * \t\t\t.assertionConsumerServiceLocation(assertingConsumerServiceLocation)\n * \t\t \t.signingX509Credentials((c) -&gt; c.add(relyingPartySigningCredential))\n * \t\t\t.assertingPartyMetadata((metadata) -&gt; metadata\n * \t\t\t\t.entityId(assertingPartyEntityId));\n * \t\t\t\t.singleSignOnServiceLocation(singleSignOnServiceLocation))\n * \t\t\t\t.verifyingX509Credentials((c) -&gt; c.add(assertingPartyVerificationCredential))\n * \t\t\t.build();\n * </pre>\n *\n * @author Filip Hanik\n * @author Josh Cummings\n * @since 5.2\n */\npublic class RelyingPartyRegistration implements Serializable {\n\n\tprivate static final long serialVersionUID = -2718908121120942813L;\n\n\tprivate final String registrationId;\n\n\tprivate final String entityId;\n\n\tprivate final String assertionConsumerServiceLocation;\n\n\tprivate final Saml2MessageBinding assertionConsumerServiceBinding;\n\n\tprivate final @Nullable String singleLogoutServiceLocation;\n\n\tprivate final @Nullable String singleLogoutServiceResponseLocation;\n\n\tprivate final Collection<Saml2MessageBinding> singleLogoutServiceBindings;\n\n\tprivate final @Nullable String nameIdFormat;\n\n\tprivate final boolean authnRequestsSigned;\n\n\tprivate final AssertingPartyMetadata assertingPartyMetadata;\n\n\tprivate final Collection<Saml2X509Credential> decryptionX509Credentials;\n\n\tprivate final Collection<Saml2X509Credential> signingX509Credentials;\n\n\tprotected RelyingPartyRegistration(String registrationId, String entityId, String assertionConsumerServiceLocation,\n\t\t\tSaml2MessageBinding assertionConsumerServiceBinding, @Nullable String singleLogoutServiceLocation,\n\t\t\t@Nullable String singleLogoutServiceResponseLocation,\n\t\t\tCollection<Saml2MessageBinding> singleLogoutServiceBindings, AssertingPartyDetails assertingPartyDetails,\n\t\t\t@Nullable String nameIdFormat, boolean authnRequestsSigned,\n\t\t\tCollection<Saml2X509Credential> decryptionX509Credentials,\n\t\t\tCollection<Saml2X509Credential> signingX509Credentials) {\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\tAssert.hasText(entityId, \"entityId cannot be empty\");\n\t\tAssert.hasText(assertionConsumerServiceLocation, \"assertionConsumerServiceLocation cannot be empty\");\n\t\tAssert.notNull(assertionConsumerServiceBinding, \"assertionConsumerServiceBinding cannot be null\");\n\t\tAssert.isTrue(singleLogoutServiceLocation == null || !CollectionUtils.isEmpty(singleLogoutServiceBindings),\n\t\t\t\t\"singleLogoutServiceBindings cannot be null or empty when singleLogoutServiceLocation is set\");\n\t\tAssert.notNull(assertingPartyDetails, \"assertingPartyDetails cannot be null\");\n\t\tAssert.notNull(decryptionX509Credentials, \"decryptionX509Credentials cannot be null\");\n\t\tfor (Saml2X509Credential c : decryptionX509Credentials) {\n\t\t\tAssert.notNull(c, \"decryptionX509Credentials cannot contain null elements\");\n\t\t\tAssert.isTrue(c.isDecryptionCredential(),\n\t\t\t\t\t\"All decryptionX509Credentials must have a usage of DECRYPTION set\");\n\t\t}\n\t\tAssert.notNull(signingX509Credentials, \"signingX509Credentials cannot be null\");\n\t\tfor (Saml2X509Credential c : signingX509Credentials) {\n\t\t\tAssert.notNull(c, \"signingX509Credentials cannot contain null elements\");\n\t\t\tAssert.isTrue(c.isSigningCredential(), \"All signingX509Credentials must have a usage of SIGNING set\");\n\t\t}\n\t\tthis.registrationId = registrationId;\n\t\tthis.entityId = entityId;\n\t\tthis.assertionConsumerServiceLocation = assertionConsumerServiceLocation;\n\t\tthis.assertionConsumerServiceBinding = assertionConsumerServiceBinding;\n\t\tthis.singleLogoutServiceLocation = singleLogoutServiceLocation;\n\t\tthis.singleLogoutServiceResponseLocation = singleLogoutServiceResponseLocation;\n\t\tthis.singleLogoutServiceBindings = Collections.unmodifiableList(new LinkedList<>(singleLogoutServiceBindings));\n\t\tthis.nameIdFormat = nameIdFormat;\n\t\tthis.authnRequestsSigned = authnRequestsSigned;\n\t\tthis.assertingPartyMetadata = assertingPartyDetails;\n\t\tthis.decryptionX509Credentials = Collections.unmodifiableList(new LinkedList<>(decryptionX509Credentials));\n\t\tthis.signingX509Credentials = Collections.unmodifiableList(new LinkedList<>(signingX509Credentials));\n\t}\n\n\tprivate RelyingPartyRegistration(String registrationId, String entityId, String assertionConsumerServiceLocation,\n\t\t\tSaml2MessageBinding assertionConsumerServiceBinding, @Nullable String singleLogoutServiceLocation,\n\t\t\t@Nullable String singleLogoutServiceResponseLocation,\n\t\t\tCollection<Saml2MessageBinding> singleLogoutServiceBindings, AssertingPartyMetadata assertingPartyMetadata,\n\t\t\t@Nullable String nameIdFormat, boolean authnRequestsSigned,\n\t\t\tCollection<Saml2X509Credential> decryptionX509Credentials,\n\t\t\tCollection<Saml2X509Credential> signingX509Credentials) {\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\tAssert.hasText(entityId, \"entityId cannot be empty\");\n\t\tAssert.hasText(assertionConsumerServiceLocation, \"assertionConsumerServiceLocation cannot be empty\");\n\t\tAssert.notNull(assertionConsumerServiceBinding, \"assertionConsumerServiceBinding cannot be null\");\n\t\tAssert.isTrue(singleLogoutServiceLocation == null || !CollectionUtils.isEmpty(singleLogoutServiceBindings),\n\t\t\t\t\"singleLogoutServiceBindings cannot be null or empty when singleLogoutServiceLocation is set\");\n\t\tAssert.notNull(assertingPartyMetadata, \"assertingPartyMetadata cannot be null\");\n\t\tAssert.notNull(decryptionX509Credentials, \"decryptionX509Credentials cannot be null\");\n\t\tfor (Saml2X509Credential c : decryptionX509Credentials) {\n\t\t\tAssert.notNull(c, \"decryptionX509Credentials cannot contain null elements\");\n\t\t\tAssert.isTrue(c.isDecryptionCredential(),\n\t\t\t\t\t\"All decryptionX509Credentials must have a usage of DECRYPTION set\");\n\t\t}\n\t\tAssert.notNull(signingX509Credentials, \"signingX509Credentials cannot be null\");\n\t\tfor (Saml2X509Credential c : signingX509Credentials) {\n\t\t\tAssert.notNull(c, \"signingX509Credentials cannot contain null elements\");\n\t\t\tAssert.isTrue(c.isSigningCredential(), \"All signingX509Credentials must have a usage of SIGNING set\");\n\t\t}\n\t\tthis.registrationId = registrationId;\n\t\tthis.entityId = entityId;\n\t\tthis.assertionConsumerServiceLocation = assertionConsumerServiceLocation;\n\t\tthis.assertionConsumerServiceBinding = assertionConsumerServiceBinding;\n\t\tthis.singleLogoutServiceLocation = singleLogoutServiceLocation;\n\t\tthis.singleLogoutServiceResponseLocation = singleLogoutServiceResponseLocation;\n\t\tthis.singleLogoutServiceBindings = Collections.unmodifiableList(new LinkedList<>(singleLogoutServiceBindings));\n\t\tthis.nameIdFormat = nameIdFormat;\n\t\tthis.authnRequestsSigned = authnRequestsSigned;\n\t\tthis.assertingPartyMetadata = assertingPartyMetadata;\n\t\tthis.decryptionX509Credentials = Collections.unmodifiableList(new LinkedList<>(decryptionX509Credentials));\n\t\tthis.signingX509Credentials = Collections.unmodifiableList(new LinkedList<>(signingX509Credentials));\n\t}\n\n\t/**\n\t * Copy the properties in this {@link RelyingPartyRegistration} into a {@link Builder}\n\t * @return a {@link Builder} based off of the properties in this\n\t * {@link RelyingPartyRegistration}\n\t * @since 6.1\n\t */\n\tpublic Builder mutate() {\n\t\treturn new Builder(this.registrationId, this.assertingPartyMetadata.mutate()).entityId(this.entityId)\n\t\t\t.signingX509Credentials((c) -> c.addAll(this.signingX509Credentials))\n\t\t\t.decryptionX509Credentials((c) -> c.addAll(this.decryptionX509Credentials))\n\t\t\t.assertionConsumerServiceLocation(this.assertionConsumerServiceLocation)\n\t\t\t.assertionConsumerServiceBinding(this.assertionConsumerServiceBinding)\n\t\t\t.singleLogoutServiceLocation(this.singleLogoutServiceLocation)\n\t\t\t.singleLogoutServiceResponseLocation(this.singleLogoutServiceResponseLocation)\n\t\t\t.singleLogoutServiceBindings((c) -> c.addAll(this.singleLogoutServiceBindings))\n\t\t\t.nameIdFormat(this.nameIdFormat)\n\t\t\t.authnRequestsSigned(this.authnRequestsSigned);\n\t}\n\n\t/**\n\t * Get the unique registration id for this RP/AP pair\n\t * @return the unique registration id for this RP/AP pair\n\t */\n\tpublic String getRegistrationId() {\n\t\treturn this.registrationId;\n\t}\n\n\t/**\n\t * Get the relying party's <a href=\n\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor\">EntityID</a>.\n\t *\n\t * <p>\n\t * Equivalent to the value found in the relying party's &lt;EntityDescriptor\n\t * EntityID=\"...\"/&gt;\n\t *\n\t * <p>\n\t * This value may contain a number of placeholders, which need to be resolved before\n\t * use. They are {@code baseUrl}, {@code registrationId}, {@code baseScheme},\n\t * {@code baseHost}, and {@code basePort}.\n\t * @return the relying party's EntityID\n\t * @since 5.4\n\t */\n\tpublic String getEntityId() {\n\t\treturn this.entityId;\n\t}\n\n\t/**\n\t * Get the AssertionConsumerService Location. Equivalent to the value found in\n\t * &lt;AssertionConsumerService Location=\"...\"/&gt; in the relying party's\n\t * &lt;SPSSODescriptor&gt;.\n\t *\n\t * This value may contain a number of placeholders, which need to be resolved before\n\t * use. They are {@code baseUrl}, {@code registrationId}, {@code baseScheme},\n\t * {@code baseHost}, and {@code basePort}.\n\t * @return the AssertionConsumerService Location\n\t * @since 5.4\n\t */\n\tpublic String getAssertionConsumerServiceLocation() {\n\t\treturn this.assertionConsumerServiceLocation;\n\t}\n\n\t/**\n\t * Get the AssertionConsumerService Binding. Equivalent to the value found in\n\t * &lt;AssertionConsumerService Binding=\"...\"/&gt; in the relying party's\n\t * &lt;SPSSODescriptor&gt;.\n\t * @return the AssertionConsumerService Binding\n\t * @since 5.4\n\t */\n\tpublic Saml2MessageBinding getAssertionConsumerServiceBinding() {\n\t\treturn this.assertionConsumerServiceBinding;\n\t}\n\n\t/**\n\t * Get the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t * Binding</a>\n\t *\n\t *\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in the\n\t * relying party's &lt;SPSSODescriptor&gt;.\n\t * @return the SingleLogoutService Binding\n\t * @since 5.6\n\t */\n\tpublic Saml2MessageBinding getSingleLogoutServiceBinding() {\n\t\tAssert.state(this.singleLogoutServiceBindings.size() == 1, \"Method does not support multiple bindings.\");\n\t\treturn this.singleLogoutServiceBindings.iterator().next();\n\t}\n\n\t/**\n\t * Get the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t * Binding</a>\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in the\n\t * relying party's &lt;SPSSODescriptor&gt;.\n\t * @return the SingleLogoutService Binding\n\t * @since 5.8\n\t */\n\tpublic Collection<Saml2MessageBinding> getSingleLogoutServiceBindings() {\n\t\treturn this.singleLogoutServiceBindings;\n\t}\n\n\t/**\n\t * Get the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t * Location</a>\n\t *\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in the\n\t * relying party's &lt;SPSSODescriptor&gt;.\n\t * @return the SingleLogoutService Location\n\t * @since 5.6\n\t */\n\tpublic @Nullable String getSingleLogoutServiceLocation() {\n\t\treturn this.singleLogoutServiceLocation;\n\t}\n\n\t/**\n\t * Get the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t * Response Location</a>\n\t *\n\t * <p>\n\t * Equivalent to the value found in &lt;SingleLogoutService\n\t * ResponseLocation=\"...\"/&gt; in the relying party's &lt;SPSSODescriptor&gt;.\n\t * @return the SingleLogoutService Response Location\n\t * @since 5.6\n\t */\n\tpublic @Nullable String getSingleLogoutServiceResponseLocation() {\n\t\treturn this.singleLogoutServiceResponseLocation;\n\t}\n\n\t/**\n\t * Get the NameID format.\n\t * @return the NameID format\n\t * @since 5.7\n\t */\n\tpublic @Nullable String getNameIdFormat() {\n\t\treturn this.nameIdFormat;\n\t}\n\n\t/**\n\t * Get the <a href=\n\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=18\">\n\t * AuthnRequestsSigned</a> setting. If {@code true}, the relying party will sign all\n\t * AuthnRequests, regardless of asserting party preference.\n\t *\n\t * <p>\n\t * Note that Spring Security will sign the request if either\n\t * {@link #isAuthnRequestsSigned()} is {@code true} or\n\t * {@link AssertingPartyDetails#getWantAuthnRequestsSigned()} is {@code true}.\n\t * @return the relying-party preference\n\t * @since 6.1\n\t */\n\tpublic boolean isAuthnRequestsSigned() {\n\t\treturn this.authnRequestsSigned;\n\t}\n\n\t/**\n\t * Get the {@link Collection} of decryption {@link Saml2X509Credential}s associated\n\t * with this relying party\n\t * @return the {@link Collection} of decryption {@link Saml2X509Credential}s\n\t * associated with this relying party\n\t * @since 5.4\n\t */\n\tpublic Collection<Saml2X509Credential> getDecryptionX509Credentials() {\n\t\treturn this.decryptionX509Credentials;\n\t}\n\n\t/**\n\t * Get the {@link Collection} of signing {@link Saml2X509Credential}s associated with\n\t * this relying party\n\t * @return the {@link Collection} of signing {@link Saml2X509Credential}s associated\n\t * with this relying party\n\t * @since 5.4\n\t */\n\tpublic Collection<Saml2X509Credential> getSigningX509Credentials() {\n\t\treturn this.signingX509Credentials;\n\t}\n\n\t/**\n\t * Get the metadata for the Asserting Party\n\t * @return the {@link AssertingPartyMetadata}\n\t * @since 6.4\n\t */\n\tpublic AssertingPartyMetadata getAssertingPartyMetadata() {\n\t\treturn this.assertingPartyMetadata;\n\t}\n\n\t/**\n\t * Creates a {@code RelyingPartyRegistration} {@link Builder} with a known\n\t * {@code registrationId}\n\t * @param registrationId a string identifier for the {@code RelyingPartyRegistration}\n\t * @return {@code Builder} to create a {@code RelyingPartyRegistration} object\n\t */\n\tpublic static Builder withRegistrationId(String registrationId) {\n\t\tAssert.hasText(registrationId, \"registrationId cannot be empty\");\n\t\treturn new Builder(registrationId, new AssertingPartyDetails.Builder());\n\t}\n\n\t/**\n\t * Creates a {@code RelyingPartyRegistration} {@link Builder} with a\n\t * {@code registrationId} equivalent to the asserting party entity id. Also\n\t * initializes to the contents of the given {@link AssertingPartyMetadata}.\n\t * @param metadata the metadata used to initialize the\n\t * {@link RelyingPartyRegistration} {@link Builder}\n\t * @return {@link Builder} to create a {@link RelyingPartyRegistration} object\n\t * @since 6.4\n\t */\n\tpublic static Builder withAssertingPartyMetadata(AssertingPartyMetadata metadata) {\n\t\tAssert.notNull(metadata, \"assertingPartyMetadata cannot be null\");\n\t\treturn new Builder(metadata.getEntityId(), metadata.mutate());\n\t}\n\n\t/**\n\t * The configuration metadata of the Asserting party\n\t *\n\t * @since 5.4\n\t */\n\tpublic static class AssertingPartyDetails implements AssertingPartyMetadata {\n\n\t\tprivate static final long serialVersionUID = 8728930758311995475L;\n\n\t\tprivate final String entityId;\n\n\t\tprivate final boolean wantAuthnRequestsSigned;\n\n\t\tprivate List<String> signingAlgorithms;\n\n\t\tprivate final Collection<Saml2X509Credential> verificationX509Credentials;\n\n\t\tprivate final Collection<Saml2X509Credential> encryptionX509Credentials;\n\n\t\tprivate final String singleSignOnServiceLocation;\n\n\t\tprivate final Saml2MessageBinding singleSignOnServiceBinding;\n\n\t\tprivate final @Nullable String singleLogoutServiceLocation;\n\n\t\tprivate final @Nullable String singleLogoutServiceResponseLocation;\n\n\t\tprivate final Saml2MessageBinding singleLogoutServiceBinding;\n\n\t\tAssertingPartyDetails(String entityId, boolean wantAuthnRequestsSigned, List<String> signingAlgorithms,\n\t\t\t\tCollection<Saml2X509Credential> verificationX509Credentials,\n\t\t\t\tCollection<Saml2X509Credential> encryptionX509Credentials, String singleSignOnServiceLocation,\n\t\t\t\tSaml2MessageBinding singleSignOnServiceBinding, @Nullable String singleLogoutServiceLocation,\n\t\t\t\t@Nullable String singleLogoutServiceResponseLocation, Saml2MessageBinding singleLogoutServiceBinding) {\n\t\t\tAssert.hasText(entityId, \"entityId cannot be null or empty\");\n\t\t\tAssert.notEmpty(signingAlgorithms, \"signingAlgorithms cannot be empty\");\n\t\t\tAssert.notNull(verificationX509Credentials, \"verificationX509Credentials cannot be null\");\n\t\t\tfor (Saml2X509Credential credential : verificationX509Credentials) {\n\t\t\t\tAssert.notNull(credential, \"verificationX509Credentials cannot have null values\");\n\t\t\t\tAssert.isTrue(credential.isVerificationCredential(),\n\t\t\t\t\t\t\"All verificationX509Credentials must have a usage of VERIFICATION set\");\n\t\t\t}\n\t\t\tAssert.notNull(encryptionX509Credentials, \"encryptionX509Credentials cannot be null\");\n\t\t\tfor (Saml2X509Credential credential : encryptionX509Credentials) {\n\t\t\t\tAssert.notNull(credential, \"encryptionX509Credentials cannot have null values\");\n\t\t\t\tAssert.isTrue(credential.isEncryptionCredential(),\n\t\t\t\t\t\t\"All encryptionX509Credentials must have a usage of ENCRYPTION set\");\n\t\t\t}\n\t\t\tAssert.notNull(singleSignOnServiceLocation, \"singleSignOnServiceLocation cannot be null\");\n\t\t\tAssert.notNull(singleSignOnServiceBinding, \"singleSignOnServiceBinding cannot be null\");\n\t\t\tthis.entityId = entityId;\n\t\t\tthis.wantAuthnRequestsSigned = wantAuthnRequestsSigned;\n\t\t\tthis.signingAlgorithms = signingAlgorithms;\n\t\t\tthis.verificationX509Credentials = verificationX509Credentials;\n\t\t\tthis.encryptionX509Credentials = encryptionX509Credentials;\n\t\t\tthis.singleSignOnServiceLocation = singleSignOnServiceLocation;\n\t\t\tthis.singleSignOnServiceBinding = singleSignOnServiceBinding;\n\t\t\tthis.singleLogoutServiceLocation = singleLogoutServiceLocation;\n\t\t\tthis.singleLogoutServiceResponseLocation = singleLogoutServiceResponseLocation;\n\t\t\tthis.singleLogoutServiceBinding = singleLogoutServiceBinding;\n\t\t}\n\n\t\t/**\n\t\t * Get the asserting party's <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor\">EntityID</a>.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in the asserting party's &lt;EntityDescriptor\n\t\t * EntityID=\"...\"/&gt;\n\t\t *\n\t\t * <p>\n\t\t * This value may contain a number of placeholders, which need to be resolved\n\t\t * before use. They are {@code baseUrl}, {@code registrationId},\n\t\t * {@code baseScheme}, {@code baseHost}, and {@code basePort}.\n\t\t * @return the asserting party's EntityID\n\t\t */\n\t\tpublic String getEntityId() {\n\t\t\treturn this.entityId;\n\t\t}\n\n\t\t/**\n\t\t * Get the WantAuthnRequestsSigned setting, indicating the asserting party's\n\t\t * preference that relying parties should sign the AuthnRequest before sending.\n\t\t * @return the WantAuthnRequestsSigned value\n\t\t */\n\t\tpublic boolean getWantAuthnRequestsSigned() {\n\t\t\treturn this.wantAuthnRequestsSigned;\n\t\t}\n\n\t\t/**\n\t\t * Get the list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for\n\t\t * this asserting party, in preference order.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the values found in &lt;SigningMethod Algorithm=\"...\"/&gt; in the\n\t\t * asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @return the list of SigningMethod Algorithms\n\t\t * @since 5.5\n\t\t */\n\t\tpublic List<String> getSigningAlgorithms() {\n\t\t\treturn this.signingAlgorithms;\n\t\t}\n\n\t\t/**\n\t\t * Get all verification {@link Saml2X509Credential}s associated with this\n\t\t * asserting party\n\t\t * @return all verification {@link Saml2X509Credential}s associated with this\n\t\t * asserting party\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Collection<Saml2X509Credential> getVerificationX509Credentials() {\n\t\t\treturn this.verificationX509Credentials;\n\t\t}\n\n\t\t/**\n\t\t * Get all encryption {@link Saml2X509Credential}s associated with this asserting\n\t\t * party\n\t\t * @return all encryption {@link Saml2X509Credential}s associated with this\n\t\t * asserting party\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Collection<Saml2X509Credential> getEncryptionX509Credentials() {\n\t\t\treturn this.encryptionX509Credentials;\n\t\t}\n\n\t\t/**\n\t\t * Get the <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a>\n\t\t * Location.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleSignOnService Location=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @return the SingleSignOnService Location\n\t\t */\n\t\tpublic String getSingleSignOnServiceLocation() {\n\t\t\treturn this.singleSignOnServiceLocation;\n\t\t}\n\n\t\t/**\n\t\t * Get the <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a>\n\t\t * Binding.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleSignOnService Binding=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @return the SingleSignOnService Location\n\t\t */\n\t\tpublic Saml2MessageBinding getSingleSignOnServiceBinding() {\n\t\t\treturn this.singleSignOnServiceBinding;\n\t\t}\n\n\t\t/**\n\t\t * Get the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Location</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @return the SingleLogoutService Location\n\t\t * @since 5.6\n\t\t */\n\t\tpublic @Nullable String getSingleLogoutServiceLocation() {\n\t\t\treturn this.singleLogoutServiceLocation;\n\t\t}\n\n\t\t/**\n\t\t * Get the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Response Location</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @return the SingleLogoutService Response Location\n\t\t * @since 5.6\n\t\t */\n\t\tpublic @Nullable String getSingleLogoutServiceResponseLocation() {\n\t\t\treturn this.singleLogoutServiceResponseLocation;\n\t\t}\n\n\t\t/**\n\t\t * Get the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Binding</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in\n\t\t * the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t * @return the SingleLogoutService Binding\n\t\t * @since 5.6\n\t\t */\n\t\tpublic Saml2MessageBinding getSingleLogoutServiceBinding() {\n\t\t\treturn this.singleLogoutServiceBinding;\n\t\t}\n\n\t\tpublic AssertingPartyDetails.Builder mutate() {\n\t\t\treturn new AssertingPartyDetails.Builder().entityId(this.entityId)\n\t\t\t\t.wantAuthnRequestsSigned(this.wantAuthnRequestsSigned)\n\t\t\t\t.signingAlgorithms((algorithms) -> algorithms.addAll(this.signingAlgorithms))\n\t\t\t\t.verificationX509Credentials((c) -> c.addAll(this.verificationX509Credentials))\n\t\t\t\t.encryptionX509Credentials((c) -> c.addAll(this.encryptionX509Credentials))\n\t\t\t\t.singleSignOnServiceLocation(this.singleSignOnServiceLocation)\n\t\t\t\t.singleSignOnServiceBinding(this.singleSignOnServiceBinding)\n\t\t\t\t.singleLogoutServiceLocation(this.singleLogoutServiceLocation)\n\t\t\t\t.singleLogoutServiceResponseLocation(this.singleLogoutServiceResponseLocation)\n\t\t\t\t.singleLogoutServiceBinding(this.singleLogoutServiceBinding);\n\t\t}\n\n\t\tpublic static class Builder implements AssertingPartyMetadata.Builder<Builder> {\n\n\t\t\tprivate @Nullable String entityId;\n\n\t\t\tprivate boolean wantAuthnRequestsSigned = true;\n\n\t\t\tprivate List<String> signingAlgorithms = new ArrayList<>();\n\n\t\t\tprivate Collection<Saml2X509Credential> verificationX509Credentials = new LinkedHashSet<>();\n\n\t\t\tprivate Collection<Saml2X509Credential> encryptionX509Credentials = new LinkedHashSet<>();\n\n\t\t\tprivate @Nullable String singleSignOnServiceLocation;\n\n\t\t\tprivate Saml2MessageBinding singleSignOnServiceBinding = Saml2MessageBinding.REDIRECT;\n\n\t\t\tprivate @Nullable String singleLogoutServiceLocation;\n\n\t\t\tprivate @Nullable String singleLogoutServiceResponseLocation;\n\n\t\t\tprivate Saml2MessageBinding singleLogoutServiceBinding = Saml2MessageBinding.REDIRECT;\n\n\t\t\t/**\n\t\t\t * Set the asserting party's <a href=\n\t\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor\">EntityID</a>.\n\t\t\t * Equivalent to the value found in the asserting party's &lt;EntityDescriptor\n\t\t\t * EntityID=\"...\"/&gt;\n\t\t\t * @param entityId the asserting party's EntityID\n\t\t\t * @return the {@link AssertingPartyDetails.Builder} for further configuration\n\t\t\t */\n\t\t\tpublic Builder entityId(String entityId) {\n\t\t\t\tthis.entityId = entityId;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Set the WantAuthnRequestsSigned setting, indicating the asserting party's\n\t\t\t * preference that relying parties should sign the AuthnRequest before\n\t\t\t * sending.\n\t\t\t * @param wantAuthnRequestsSigned the WantAuthnRequestsSigned setting\n\t\t\t * @return the {@link AssertingPartyDetails.Builder} for further configuration\n\t\t\t */\n\t\t\tpublic Builder wantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) {\n\t\t\t\tthis.wantAuthnRequestsSigned = wantAuthnRequestsSigned;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Apply this {@link Consumer} to the list of SigningMethod Algorithms\n\t\t\t * @param signingMethodAlgorithmsConsumer a {@link Consumer} of the list of\n\t\t\t * SigningMethod Algorithms\n\t\t\t * @return this {@link AssertingPartyDetails.Builder} for further\n\t\t\t * configuration\n\t\t\t * @since 5.5\n\t\t\t */\n\t\t\tpublic Builder signingAlgorithms(Consumer<List<String>> signingMethodAlgorithmsConsumer) {\n\t\t\t\tsigningMethodAlgorithmsConsumer.accept(this.signingAlgorithms);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Apply this {@link Consumer} to the list of {@link Saml2X509Credential}s\n\t\t\t * @param credentialsConsumer a {@link Consumer} of the {@link List} of\n\t\t\t * {@link Saml2X509Credential}s\n\t\t\t * @return the {@link RelyingPartyRegistration.Builder} for further\n\t\t\t * configuration\n\t\t\t * @since 5.4\n\t\t\t */\n\t\t\tpublic Builder verificationX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {\n\t\t\t\tcredentialsConsumer.accept(this.verificationX509Credentials);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Apply this {@link Consumer} to the list of {@link Saml2X509Credential}s\n\t\t\t * @param credentialsConsumer a {@link Consumer} of the {@link List} of\n\t\t\t * {@link Saml2X509Credential}s\n\t\t\t * @return the {@link RelyingPartyRegistration.Builder} for further\n\t\t\t * configuration\n\t\t\t * @since 5.4\n\t\t\t */\n\t\t\tpublic Builder encryptionX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {\n\t\t\t\tcredentialsConsumer.accept(this.encryptionX509Credentials);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Set the <a href=\n\t\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a>\n\t\t\t * Location.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * Equivalent to the value found in &lt;SingleSignOnService\n\t\t\t * Location=\"...\"/&gt; in the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t\t * @param singleSignOnServiceLocation the SingleSignOnService Location\n\t\t\t * @return the {@link AssertingPartyDetails.Builder} for further configuration\n\t\t\t */\n\t\t\tpublic Builder singleSignOnServiceLocation(String singleSignOnServiceLocation) {\n\t\t\t\tthis.singleSignOnServiceLocation = singleSignOnServiceLocation;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Set the <a href=\n\t\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint\">SingleSignOnService</a>\n\t\t\t * Binding.\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * Equivalent to the value found in &lt;SingleSignOnService Binding=\"...\"/&gt;\n\t\t\t * in the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t\t * @param singleSignOnServiceBinding the SingleSignOnService Binding\n\t\t\t * @return the {@link AssertingPartyDetails.Builder} for further configuration\n\t\t\t */\n\t\t\tpublic Builder singleSignOnServiceBinding(Saml2MessageBinding singleSignOnServiceBinding) {\n\t\t\t\tthis.singleSignOnServiceBinding = singleSignOnServiceBinding;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Set the <a href=\n\t\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t\t * Location</a>\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * Equivalent to the value found in &lt;SingleLogoutService\n\t\t\t * Location=\"...\"/&gt; in the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t\t * @param singleLogoutServiceLocation the SingleLogoutService Location\n\t\t\t * @return the {@link AssertingPartyDetails.Builder} for further configuration\n\t\t\t * @since 5.6\n\t\t\t */\n\t\t\tpublic Builder singleLogoutServiceLocation(@Nullable String singleLogoutServiceLocation) {\n\t\t\t\tthis.singleLogoutServiceLocation = singleLogoutServiceLocation;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Set the <a href=\n\t\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t\t * Response Location</a>\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * Equivalent to the value found in &lt;SingleLogoutService\n\t\t\t * ResponseLocation=\"...\"/&gt; in the asserting party's\n\t\t\t * &lt;IDPSSODescriptor&gt;.\n\t\t\t * @param singleLogoutServiceResponseLocation the SingleLogoutService Response\n\t\t\t * Location\n\t\t\t * @return the {@link AssertingPartyDetails.Builder} for further configuration\n\t\t\t * @since 5.6\n\t\t\t */\n\t\t\tpublic Builder singleLogoutServiceResponseLocation(@Nullable String singleLogoutServiceResponseLocation) {\n\t\t\t\tthis.singleLogoutServiceResponseLocation = singleLogoutServiceResponseLocation;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Set the <a href=\n\t\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t\t * Binding</a>\n\t\t\t *\n\t\t\t * <p>\n\t\t\t * Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt;\n\t\t\t * in the asserting party's &lt;IDPSSODescriptor&gt;.\n\t\t\t * @param singleLogoutServiceBinding the SingleLogoutService Binding\n\t\t\t * @return the {@link AssertingPartyDetails.Builder} for further configuration\n\t\t\t * @since 5.6\n\t\t\t */\n\t\t\tpublic Builder singleLogoutServiceBinding(Saml2MessageBinding singleLogoutServiceBinding) {\n\t\t\t\tthis.singleLogoutServiceBinding = singleLogoutServiceBinding;\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Creates an immutable ProviderDetails object representing the configuration\n\t\t\t * for an Identity Provider, IDP\n\t\t\t * @return immutable ProviderDetails object\n\t\t\t */\n\t\t\tpublic AssertingPartyDetails build() {\n\t\t\t\tList<String> signingAlgorithms = this.signingAlgorithms.isEmpty()\n\t\t\t\t\t\t? Collections.singletonList(\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\")\n\t\t\t\t\t\t: Collections.unmodifiableList(this.signingAlgorithms);\n\t\t\t\tAssert.notNull(this.entityId, \"entityId cannot be null\");\n\t\t\t\tAssert.notNull(this.singleSignOnServiceLocation, \"singleSignOnServiceLocation cannot be null\");\n\t\t\t\treturn new AssertingPartyDetails(this.entityId, this.wantAuthnRequestsSigned, signingAlgorithms,\n\t\t\t\t\t\tthis.verificationX509Credentials, this.encryptionX509Credentials,\n\t\t\t\t\t\tthis.singleSignOnServiceLocation, this.singleSignOnServiceBinding,\n\t\t\t\t\t\tthis.singleLogoutServiceLocation, this.singleLogoutServiceResponseLocation,\n\t\t\t\t\t\tthis.singleLogoutServiceBinding);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tpublic static class Builder {\n\n\t\tprivate String registrationId;\n\n\t\tprivate String entityId = \"{baseUrl}/saml2/service-provider-metadata/{registrationId}\";\n\n\t\tprivate Collection<Saml2X509Credential> signingX509Credentials = new LinkedHashSet<>();\n\n\t\tprivate Collection<Saml2X509Credential> decryptionX509Credentials = new LinkedHashSet<>();\n\n\t\tprivate String assertionConsumerServiceLocation = \"{baseUrl}/login/saml2/sso/{registrationId}\";\n\n\t\tprivate Saml2MessageBinding assertionConsumerServiceBinding = Saml2MessageBinding.POST;\n\n\t\tprivate @Nullable String singleLogoutServiceLocation;\n\n\t\tprivate @Nullable String singleLogoutServiceResponseLocation;\n\n\t\tprivate Collection<Saml2MessageBinding> singleLogoutServiceBindings = new LinkedHashSet<>();\n\n\t\tprivate @Nullable String nameIdFormat = null;\n\n\t\tprivate boolean authnRequestsSigned = false;\n\n\t\tprivate AssertingPartyMetadata.Builder<?> assertingPartyMetadataBuilder;\n\n\t\tprotected Builder(String registrationId, AssertingPartyMetadata.Builder<?> assertingPartyMetadataBuilder) {\n\t\t\tthis.registrationId = registrationId;\n\t\t\tthis.assertingPartyMetadataBuilder = assertingPartyMetadataBuilder;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@code registrationId} template. Often be used in URL paths\n\t\t * @param id registrationId for this object, should be unique\n\t\t * @return this object\n\t\t */\n\t\tpublic Builder registrationId(String id) {\n\t\t\tthis.registrationId = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the relying party's <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor\">EntityID</a>.\n\t\t * Equivalent to the value found in the relying party's &lt;EntityDescriptor\n\t\t * EntityID=\"...\"/&gt;\n\t\t *\n\t\t * This value may contain a number of placeholders. They are {@code baseUrl},\n\t\t * {@code registrationId}, {@code baseScheme}, {@code baseHost}, and\n\t\t * {@code basePort}.\n\t\t * @param entityId the relying party's EntityID\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Builder entityId(String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Apply this {@link Consumer} to the {@link Collection} of\n\t\t * {@link Saml2X509Credential}s for the purposes of modifying the\n\t\t * {@link Collection}\n\t\t * @param credentialsConsumer - the {@link Consumer} for modifying the\n\t\t * {@link Collection}\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Builder signingX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {\n\t\t\tcredentialsConsumer.accept(this.signingX509Credentials);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Apply this {@link Consumer} to the {@link Collection} of\n\t\t * {@link Saml2X509Credential}s for the purposes of modifying the\n\t\t * {@link Collection}\n\t\t * @param credentialsConsumer - the {@link Consumer} for modifying the\n\t\t * {@link Collection}\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Builder decryptionX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {\n\t\t\tcredentialsConsumer.accept(this.decryptionX509Credentials);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.3%20AttributeConsumingService\">\n\t\t * AssertionConsumerService</a> Location.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;AssertionConsumerService\n\t\t * Location=\"...\"/&gt; in the relying party's &lt;SPSSODescriptor&gt;\n\t\t *\n\t\t * <p>\n\t\t * This value may contain a number of placeholders. They are {@code baseUrl},\n\t\t * {@code registrationId}, {@code baseScheme}, {@code baseHost}, and\n\t\t * {@code basePort}.\n\t\t * @param assertionConsumerServiceLocation the AssertionConsumerService location\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Builder assertionConsumerServiceLocation(String assertionConsumerServiceLocation) {\n\t\t\tthis.assertionConsumerServiceLocation = assertionConsumerServiceLocation;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.3%20AttributeConsumingService\">\n\t\t * AssertionConsumerService</a> Binding.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;AssertionConsumerService\n\t\t * Binding=\"...\"/&gt; in the relying party's &lt;SPSSODescriptor&gt;\n\t\t * @param assertionConsumerServiceBinding the AssertionConsumerService binding\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.4\n\t\t */\n\t\tpublic Builder assertionConsumerServiceBinding(Saml2MessageBinding assertionConsumerServiceBinding) {\n\t\t\tthis.assertionConsumerServiceBinding = assertionConsumerServiceBinding;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Binding</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in\n\t\t * the relying party's &lt;SPSSODescriptor&gt;.\n\t\t * @param singleLogoutServiceBinding the SingleLogoutService Binding\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.6\n\t\t */\n\t\tpublic Builder singleLogoutServiceBinding(Saml2MessageBinding singleLogoutServiceBinding) {\n\t\t\treturn this.singleLogoutServiceBindings((saml2MessageBindings) -> {\n\t\t\t\tsaml2MessageBindings.clear();\n\t\t\t\tsaml2MessageBindings.add(singleLogoutServiceBinding);\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Apply this {@link Consumer} to the {@link Collection} of\n\t\t * {@link Saml2MessageBinding}s for the purposes of modifying the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Binding</a> {@link Collection}.\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService Binding=\"...\"/&gt; in\n\t\t * the relying party's &lt;SPSSODescriptor&gt;.\n\t\t * @param bindingsConsumer - the {@link Consumer} for modifying the\n\t\t * {@link Collection}\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.8\n\t\t */\n\t\tpublic Builder singleLogoutServiceBindings(Consumer<Collection<Saml2MessageBinding>> bindingsConsumer) {\n\t\t\tbindingsConsumer.accept(this.singleLogoutServiceBindings);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Location</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService Location=\"...\"/&gt; in\n\t\t * the relying party's &lt;SPSSODescriptor&gt;.\n\t\t * @param singleLogoutServiceLocation the SingleLogoutService Location\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.6\n\t\t */\n\t\tpublic Builder singleLogoutServiceLocation(@Nullable String singleLogoutServiceLocation) {\n\t\t\tthis.singleLogoutServiceLocation = singleLogoutServiceLocation;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7\">SingleLogoutService\n\t\t * Response Location</a>\n\t\t *\n\t\t * <p>\n\t\t * Equivalent to the value found in &lt;SingleLogoutService\n\t\t * ResponseLocation=\"...\"/&gt; in the relying party's &lt;SPSSODescriptor&gt;.\n\t\t * @param singleLogoutServiceResponseLocation the SingleLogoutService Response\n\t\t * Location\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.6\n\t\t */\n\t\tpublic Builder singleLogoutServiceResponseLocation(@Nullable String singleLogoutServiceResponseLocation) {\n\t\t\tthis.singleLogoutServiceResponseLocation = singleLogoutServiceResponseLocation;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the NameID format\n\t\t * @param nameIdFormat the given NameID format\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 5.7\n\t\t */\n\t\tpublic Builder nameIdFormat(@Nullable String nameIdFormat) {\n\t\t\tthis.nameIdFormat = nameIdFormat;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the <a href=\n\t\t * \"https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=18\">\n\t\t * AuthnRequestsSigned</a> setting. If {@code true}, the relying party will sign\n\t\t * all AuthnRequests, 301 asserting party preference.\n\t\t *\n\t\t * <p>\n\t\t * Note that Spring Security will sign the request if either\n\t\t * {@link #isAuthnRequestsSigned()} is {@code true} or\n\t\t * {@link AssertingPartyDetails#getWantAuthnRequestsSigned()} is {@code true}.\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 6.1\n\t\t */\n\t\tpublic Builder authnRequestsSigned(Boolean authnRequestsSigned) {\n\t\t\tthis.authnRequestsSigned = authnRequestsSigned;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Apply this {@link Consumer} to further configure the Asserting Party metadata\n\t\t * @param assertingPartyMetadata The {@link Consumer} to apply\n\t\t * @return the {@link Builder} for further configuration\n\t\t * @since 6.4\n\t\t */\n\t\tpublic Builder assertingPartyMetadata(Consumer<AssertingPartyMetadata.Builder<?>> assertingPartyMetadata) {\n\t\t\tassertingPartyMetadata.accept(this.assertingPartyMetadataBuilder);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Constructs a RelyingPartyRegistration object based on the builder\n\t\t * configurations\n\t\t * @return a RelyingPartyRegistration instance\n\t\t */\n\t\tpublic RelyingPartyRegistration build() {\n\t\t\tif (this.singleLogoutServiceResponseLocation == null) {\n\t\t\t\tthis.singleLogoutServiceResponseLocation = this.singleLogoutServiceLocation;\n\t\t\t}\n\n\t\t\tif (this.singleLogoutServiceBindings.isEmpty()) {\n\t\t\t\tthis.singleLogoutServiceBindings.add(Saml2MessageBinding.POST);\n\t\t\t}\n\n\t\t\tAssertingPartyMetadata party = this.assertingPartyMetadataBuilder.build();\n\t\t\treturn new RelyingPartyRegistration(this.registrationId, this.entityId,\n\t\t\t\t\tthis.assertionConsumerServiceLocation, this.assertionConsumerServiceBinding,\n\t\t\t\t\tthis.singleLogoutServiceLocation, this.singleLogoutServiceResponseLocation,\n\t\t\t\t\tthis.singleLogoutServiceBindings, party, this.nameIdFormat, this.authnRequestsSigned,\n\t\t\t\t\tthis.decryptionX509Credentials, this.signingX509Credentials);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A repository for {@link RelyingPartyRegistration}s\n *\n * @author Filip Hanik\n * @author Josh Cummings\n * @since 5.2\n */\npublic interface RelyingPartyRegistrationRepository {\n\n\t/**\n\t * Returns the relying party registration identified by the provided\n\t * {@code registrationId}, or {@code null} if not found.\n\t * @param registrationId the registration identifier\n\t * @return the {@link RelyingPartyRegistration} if found, otherwise {@code null}\n\t */\n\t@Nullable RelyingPartyRegistration findByRegistrationId(String registrationId);\n\n\t/**\n\t * Returns the unique relying party registration associated with the asserting party's\n\t * {@code entityId} or {@code null} if there is no unique match.\n\t * @param entityId the asserting party's entity id\n\t * @return the unique {@link RelyingPartyRegistration} associated the given asserting\n\t * party; {@code null} of there is no unique match asserting party\n\t * @since 6.1\n\t */\n\tdefault @Nullable RelyingPartyRegistration findUniqueByAssertingPartyEntityId(String entityId) {\n\t\treturn findByRegistrationId(entityId);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\n\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\n\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * A utility class for constructing instances of {@link RelyingPartyRegistration}\n *\n * @author Josh Cummings\n * @author Ryan Cassar\n * @author Marcus da Coregio\n * @since 5.4\n */\npublic final class RelyingPartyRegistrations {\n\n\tprivate static final ResourceLoader resourceLoader = new DefaultResourceLoader();\n\n\tprivate RelyingPartyRegistrations() {\n\t}\n\n\t/**\n\t * Return a {@link RelyingPartyRegistration.Builder} based off of the given SAML 2.0\n\t * Asserting Party (IDP) metadata location.\n\t *\n\t * Valid locations can be classpath- or file-based or they can be HTTPS endpoints.\n\t * Some valid endpoints might include:\n\t *\n\t * <pre>\n\t *   metadataLocation = \"classpath:asserting-party-metadata.xml\";\n\t *   metadataLocation = \"file:asserting-party-metadata.xml\";\n\t *   metadataLocation = \"https://ap.example.org/metadata\";\n\t * </pre>\n\t *\n\t * Note that by default the registrationId is set to be the given metadata location,\n\t * but this will most often not be sufficient. To complete the configuration, most\n\t * applications will also need to provide a registrationId, like so:\n\t *\n\t * <pre>\n\t *\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t * \t\t.fromMetadataLocation(metadataLocation)\n\t * \t\t.registrationId(\"registration-id\")\n\t * \t\t.build();\n\t * </pre>\n\t *\n\t * Also note that an {@code IDPSSODescriptor} typically only contains information\n\t * about the asserting party. Thus, you will need to remember to still populate\n\t * anything about the relying party, like any private keys the relying party will use\n\t * for signing AuthnRequests.\n\t * @param metadataLocation The classpath- or file-based locations or HTTPS endpoints\n\t * of the asserting party metadata file\n\t * @return the {@link RelyingPartyRegistration.Builder} for further configuration\n\t */\n\tpublic static RelyingPartyRegistration.Builder fromMetadataLocation(String metadataLocation) {\n\t\ttry (InputStream source = resourceLoader.getResource(metadataLocation).getInputStream()) {\n\t\t\treturn fromMetadata(source);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tif (ex.getCause() instanceof Saml2Exception) {\n\t\t\t\tthrow (Saml2Exception) ex.getCause();\n\t\t\t}\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Return a {@link RelyingPartyRegistration.Builder} based off of the given SAML 2.0\n\t * Asserting Party (IDP) metadata.\n\t *\n\t * <p>\n\t * This method is intended for scenarios when the metadata is looked up by a separate\n\t * mechanism. One such example is when the metadata is stored in a database.\n\t * </p>\n\t *\n\t * <p>\n\t * <strong>The callers of this method are accountable for closing the\n\t * {@code InputStream} source.</strong>\n\t * </p>\n\t *\n\t * Note that by default the registrationId is set to be the given metadata location,\n\t * but this will most often not be sufficient. To complete the configuration, most\n\t * applications will also need to provide a registrationId, like so:\n\t *\n\t * <pre>\n\t *\tString xml = fromDatabase();\n\t *\ttry (InputStream source = new ByteArrayInputStream(xml.getBytes())) {\n\t *\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t * \t\t\t.fromMetadata(source)\n\t * \t\t\t.registrationId(\"registration-id\")\n\t * \t\t\t.build();\n\t * \t}\n\t * </pre>\n\t *\n\t * Also note that an {@code IDPSSODescriptor} typically only contains information\n\t * about the asserting party. Thus, you will need to remember to still populate\n\t * anything about the relying party, like any private keys the relying party will use\n\t * for signing AuthnRequests.\n\t * @param source the {@link InputStream} source containing the asserting party\n\t * metadata\n\t * @return the {@link RelyingPartyRegistration.Builder} for further configuration\n\t * @since 5.6\n\t */\n\tpublic static RelyingPartyRegistration.Builder fromMetadata(InputStream source) {\n\t\treturn collectionFromMetadata(source).iterator().next();\n\t}\n\n\t/**\n\t * Return a {@link Collection} of {@link RelyingPartyRegistration.Builder}s based off\n\t * of the given SAML 2.0 Asserting Party (IDP) metadata location.\n\t *\n\t * Valid locations can be classpath- or file-based or they can be HTTPS endpoints.\n\t * Some valid endpoints might include:\n\t *\n\t * <pre>\n\t *   metadataLocation = \"classpath:asserting-party-metadata.xml\";\n\t *   metadataLocation = \"file:asserting-party-metadata.xml\";\n\t *   metadataLocation = \"https://ap.example.org/metadata\";\n\t * </pre>\n\t *\n\t * Note that by default the registrationId is set to be the given metadata location,\n\t * but this will most often not be sufficient. To complete the configuration, most\n\t * applications will also need to provide a registrationId, like so:\n\t *\n\t * <pre>\n\t *\tIterable&lt;RelyingPartyRegistration&gt; registrations = RelyingPartyRegistrations\n\t * \t\t\t.collectionFromMetadataLocation(location).iterator();\n\t * \tRelyingPartyRegistration one = registrations.next().registrationId(\"one\").build();\n\t * \tRelyingPartyRegistration two = registrations.next().registrationId(\"two\").build();\n\t * \treturn new InMemoryRelyingPartyRegistrationRepository(one, two);\n\t * </pre>\n\t *\n\t * Also note that an {@code IDPSSODescriptor} typically only contains information\n\t * about the asserting party. Thus, you will need to remember to still populate\n\t * anything about the relying party, like any private keys the relying party will use\n\t * for signing AuthnRequests.\n\t * @param location The classpath- or file-based locations or HTTPS endpoints of the\n\t * asserting party metadata file\n\t * @return the {@link Collection} of {@link RelyingPartyRegistration.Builder}s for\n\t * further configuration\n\t * @since 5.7\n\t */\n\tpublic static Collection<RelyingPartyRegistration.Builder> collectionFromMetadataLocation(String location) {\n\t\ttry (InputStream source = resourceLoader.getResource(location).getInputStream()) {\n\t\t\treturn collectionFromMetadata(source);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tif (ex.getCause() instanceof Saml2Exception) {\n\t\t\t\tthrow (Saml2Exception) ex.getCause();\n\t\t\t}\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Return a {@link Collection} of {@link RelyingPartyRegistration.Builder}s based off\n\t * of the given SAML 2.0 Asserting Party (IDP) metadata.\n\t *\n\t * <p>\n\t * This method is intended for scenarios when the metadata is looked up by a separate\n\t * mechanism. One such example is when the metadata is stored in a database.\n\t * </p>\n\t *\n\t * <p>\n\t * <strong>The callers of this method are accountable for closing the\n\t * {@code InputStream} source.</strong>\n\t * </p>\n\t *\n\t * Note that by default the registrationId is set to be the given metadata location,\n\t * but this will most often not be sufficient. To complete the configuration, most\n\t * applications will also need to provide a registrationId, like so:\n\t *\n\t * <pre>\n\t *\tString xml = fromDatabase();\n\t *\ttry (InputStream source = new ByteArrayInputStream(xml.getBytes())) {\n\t *\t\tIterator&lt;RelyingPartyRegistration&gt; registrations = RelyingPartyRegistrations\n\t * \t\t\t\t.collectionFromMetadata(source).iterator();\n\t * \t\tRelyingPartyRegistration one = registrations.next().registrationId(\"one\").build();\n\t * \t\tRelyingPartyRegistration two = registrations.next().registrationId(\"two\").build();\n\t * \t\treturn new InMemoryRelyingPartyRegistrationRepository(one, two);\n\t * \t}\n\t * </pre>\n\t *\n\t * Also note that an {@code IDPSSODescriptor} typically only contains information\n\t * about the asserting party. Thus, you will need to remember to still populate\n\t * anything about the relying party, like any private keys the relying party will use\n\t * for signing AuthnRequests.\n\t * @param source the {@link InputStream} source containing the asserting party\n\t * metadata\n\t * @return the {@link Collection} of {@link RelyingPartyRegistration.Builder}s for\n\t * further configuration\n\t * @since 5.7\n\t */\n\tpublic static Collection<RelyingPartyRegistration.Builder> collectionFromMetadata(InputStream source) {\n\t\tCollection<RelyingPartyRegistration.Builder> builders = new ArrayList<>();\n\t\tfor (EntityDescriptor descriptor : OpenSamlMetadataUtils.descriptors(source)) {\n\t\t\tif (descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) != null) {\n\t\t\t\tOpenSamlAssertingPartyDetails assertingParty = OpenSamlAssertingPartyDetails\n\t\t\t\t\t.withEntityDescriptor(descriptor)\n\t\t\t\t\t.build();\n\t\t\t\tbuilders.add(RelyingPartyRegistration.withAssertingPartyMetadata(assertingParty));\n\t\t\t}\n\t\t}\n\t\tif (builders.isEmpty()) {\n\t\t\tthrow new Saml2Exception(\"Metadata response is missing the necessary IDPSSODescriptor element\");\n\t\t}\n\t\treturn builders;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2MessageBinding.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * The type of bindings that messages are exchanged using Supported bindings are\n * {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST} and\n * {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect}. In addition there is\n * support for {@code urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect} with an XML\n * signature in the message rather than query parameters.\n *\n * @since 5.3\n */\npublic enum Saml2MessageBinding {\n\n\tPOST(\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"),\n\tREDIRECT(\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\");\n\n\tprivate final String urn;\n\n\tSaml2MessageBinding(String s) {\n\t\tthis.urn = s;\n\t}\n\n\t/**\n\t * Returns the URN value from the SAML 2 specification for this binding.\n\t * @return URN value representing this binding\n\t */\n\tpublic String getUrn() {\n\t\treturn this.urn;\n\t}\n\n\t/**\n\t * Attempt to resolve the provided algorithm name to a {@code Saml2MessageBinding}.\n\t * @param name the algorithm name\n\t * @return the resolved {@code Saml2MessageBinding}, or {@code null} if not found\n\t * @since 5.5\n\t */\n\tpublic static @Nullable Saml2MessageBinding from(String name) {\n\t\tfor (Saml2MessageBinding value : values()) {\n\t\t\tif (value.getUrn().equals(name)) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal utilities for SAML2 support (not for public use).\n */\n@NullMarked\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/BaseOpenSamlAuthenticationTokenConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.schema.XSString;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.Response;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\nfinal class BaseOpenSamlAuthenticationTokenConverter implements AuthenticationConverter {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final OpenSamlOperations saml;\n\n\tprivate final RelyingPartyRegistrationRepository registrations;\n\n\tprivate RequestMatcher requestMatcher = new OrRequestMatcher(pathPattern(\"/login/saml2/sso/{registrationId}\"),\n\t\t\tpathPattern(\"/login/saml2/sso\"));\n\n\tprivate Saml2AuthenticationRequestRepository<?> authenticationRequests = new HttpSessionSaml2AuthenticationRequestRepository();\n\n\tprivate boolean shouldConvertGetRequests = true;\n\n\t/**\n\t * Constructs a {@link BaseOpenSamlAuthenticationTokenConverter} given a repository\n\t * for {@link RelyingPartyRegistration}s\n\t * @param registrations the repository for {@link RelyingPartyRegistration}s\n\t * {@link RelyingPartyRegistration}s\n\t */\n\tBaseOpenSamlAuthenticationTokenConverter(RelyingPartyRegistrationRepository registrations,\n\t\t\tOpenSamlOperations saml) {\n\t\tAssert.notNull(registrations, \"relyingPartyRegistrationRepository cannot be null\");\n\t\tthis.registrations = registrations;\n\t\tthis.saml = saml;\n\t}\n\n\t/**\n\t * Resolve an authentication request from the given {@link HttpServletRequest}.\n\t *\n\t * <p>\n\t * First uses the configured {@link RequestMatcher} to deduce whether an\n\t * authentication request is being made and optionally for which\n\t * {@code registrationId}.\n\t *\n\t * <p>\n\t * If there is an associated {@code <saml2:AuthnRequest>}, then the\n\t * {@code registrationId} is looked up and used.\n\t *\n\t * <p>\n\t * If a {@code registrationId} is found in the request, then it is looked up and used.\n\t * In that case, if none is found a {@link Saml2AuthenticationException} is thrown.\n\t *\n\t * <p>\n\t * Finally, if no {@code registrationId} is found in the request, then the code\n\t * attempts to resolve the {@link RelyingPartyRegistration} from the SAML Response's\n\t * Issuer.\n\t * @param request the HTTP request\n\t * @return the {@link Saml2AuthenticationToken} authentication request\n\t * @throws Saml2AuthenticationException if the {@link RequestMatcher} specifies a\n\t * non-existent {@code registrationId}\n\t */\n\t@Override\n\tpublic @Nullable Saml2AuthenticationToken convert(HttpServletRequest request) {\n\t\tString serialized = request.getParameter(Saml2ParameterNames.SAML_RESPONSE);\n\t\tif (serialized == null) {\n\t\t\treturn null;\n\t\t}\n\t\tRequestMatcher.MatchResult result = this.requestMatcher.matcher(request);\n\t\tif (!result.isMatch()) {\n\t\t\treturn null;\n\t\t}\n\t\tSaml2AuthenticationToken token = tokenByAuthenticationRequest(request);\n\t\tif (token == null) {\n\t\t\ttoken = tokenByRegistrationId(request, result);\n\t\t}\n\t\tif (token == null) {\n\t\t\ttoken = tokenByEntityId(request);\n\t\t}\n\t\treturn token;\n\t}\n\n\tprivate @Nullable Saml2AuthenticationToken tokenByAuthenticationRequest(HttpServletRequest request) {\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequests\n\t\t\t.loadAuthenticationRequest(request);\n\t\tif (authenticationRequest == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString registrationId = authenticationRequest.getRelyingPartyRegistrationId();\n\t\tif (registrationId == null) {\n\t\t\treturn null;\n\t\t}\n\t\tRelyingPartyRegistration registration = this.registrations.findByRegistrationId(registrationId);\n\t\treturn tokenByRegistration(request, registration, authenticationRequest);\n\t}\n\n\tprivate @Nullable Saml2AuthenticationToken tokenByRegistrationId(HttpServletRequest request,\n\t\t\tRequestMatcher.MatchResult result) {\n\t\tString registrationId = result.getVariables().get(\"registrationId\");\n\t\tif (registrationId == null) {\n\t\t\treturn null;\n\t\t}\n\t\tRelyingPartyRegistration registration = this.registrations.findByRegistrationId(registrationId);\n\t\treturn tokenByRegistration(request, registration, null);\n\t}\n\n\tprivate @Nullable Saml2AuthenticationToken tokenByEntityId(HttpServletRequest request) {\n\t\tString decoded = decode(request);\n\t\tif (decoded == null) {\n\t\t\treturn null;\n\t\t}\n\t\tResponse response = this.saml.deserialize(decoded);\n\t\tIssuer issuer = response.getIssuer();\n\t\tAssert.notNull(issuer, \"Response#Issuer cannot be null\");\n\t\tRelyingPartyRegistration registration = this.registrations.findUniqueByAssertingPartyEntityId(getValue(issuer));\n\t\treturn tokenByRegistration(request, registration, null);\n\t}\n\n\tprivate @Nullable Saml2AuthenticationToken tokenByRegistration(HttpServletRequest request,\n\t\t\t@Nullable RelyingPartyRegistration registration,\n\t\t\t@Nullable AbstractSaml2AuthenticationRequest authenticationRequest) {\n\t\tif (registration == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString decoded = decode(request);\n\t\tif (decoded == null) {\n\t\t\treturn null;\n\t\t}\n\t\tUriResolver resolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration);\n\t\tString entityId = resolver.resolve(registration.getEntityId());\n\t\tentityId = Objects.requireNonNull(entityId);\n\t\tString assertionConsumerServiceLocation = resolver.resolve(registration.getAssertionConsumerServiceLocation());\n\t\tassertionConsumerServiceLocation = Objects.requireNonNull(assertionConsumerServiceLocation);\n\t\tregistration = registration.mutate()\n\t\t\t.entityId(entityId)\n\t\t\t.assertionConsumerServiceLocation(assertionConsumerServiceLocation)\n\t\t\t.build();\n\t\treturn new Saml2AuthenticationToken(registration, decoded, authenticationRequest);\n\t}\n\n\tprivate String getValue(XSString object) {\n\t\tString value = object.getValue();\n\t\tAssert.notNull(value, \"required elements must have a value\");\n\t\treturn value;\n\t}\n\n\t/**\n\t * Use the given {@link Saml2AuthenticationRequestRepository} to load authentication\n\t * request.\n\t * @param authenticationRequestRepository the\n\t * {@link Saml2AuthenticationRequestRepository} to use\n\t */\n\tvoid setAuthenticationRequestRepository(\n\t\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {\n\t\tAssert.notNull(authenticationRequestRepository, \"authenticationRequestRepository cannot be null\");\n\t\tthis.authenticationRequests = authenticationRequestRepository;\n\t}\n\n\t/**\n\t * Use the given {@link RequestMatcher} to match the request.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tvoid setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\tvoid setShouldConvertGetRequests(boolean shouldConvertGetRequests) {\n\t\tthis.shouldConvertGetRequests = shouldConvertGetRequests;\n\t}\n\n\tprivate @Nullable String decode(HttpServletRequest request) {\n\t\tString encoded = request.getParameter(Saml2ParameterNames.SAML_RESPONSE);\n\t\tboolean isGet = HttpMethod.GET.matches(request.getMethod());\n\t\tif (!this.shouldConvertGetRequests && isGet) {\n\t\t\treturn null;\n\t\t}\n\t\tSaml2Utils.DecodingConfigurer decoding = Saml2Utils.withEncoded(encoded).requireBase64(true).inflate(isGet);\n\t\ttry {\n\t\t\treturn decoding.decode();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2AuthenticationException(Saml2Error.invalidResponse(ex.getMessage()), ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/CacheSaml2AuthenticationRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.concurrent.ConcurrentMapCache;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.util.Assert;\n\n/**\n * A cache-based {@link Saml2AuthenticationRequestRepository}. This can be handy when you\n * are dropping requests due to using SameSite=Strict and the previous session is lost.\n *\n * <p>\n * On the other hand, this presents a tradeoff where the application can only tell that\n * the given authentication request was created by this application, but cannot guarantee\n * that it was for the user trying to log in. Please see the reference for details.\n *\n * @author Josh Cummings\n * @since 6.5\n */\npublic final class CacheSaml2AuthenticationRequestRepository\n\t\timplements Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {\n\n\tprivate Cache cache = new ConcurrentMapCache(\"authentication-requests\");\n\n\t@Override\n\tpublic @Nullable AbstractSaml2AuthenticationRequest loadAuthenticationRequest(HttpServletRequest request) {\n\t\tString relayState = request.getParameter(Saml2ParameterNames.RELAY_STATE);\n\t\tAssert.notNull(relayState, \"relayState must not be null\");\n\t\treturn this.cache.get(relayState, AbstractSaml2AuthenticationRequest.class);\n\t}\n\n\t@Override\n\tpublic void saveAuthenticationRequest(AbstractSaml2AuthenticationRequest authenticationRequest,\n\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\tAssert.notNull(authenticationRequest, \"authenticationRequest must not be null\");\n\t\tString relayState = request.getParameter(Saml2ParameterNames.RELAY_STATE);\n\t\tAssert.notNull(relayState, \"relayState must not be null\");\n\t\tthis.cache.put(relayState, authenticationRequest);\n\t}\n\n\t@Override\n\tpublic @Nullable AbstractSaml2AuthenticationRequest removeAuthenticationRequest(HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tString relayState = request.getParameter(Saml2ParameterNames.RELAY_STATE);\n\t\tAssert.notNull(relayState, \"relayState must not be null\");\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.cache.get(relayState,\n\t\t\t\tAbstractSaml2AuthenticationRequest.class);\n\t\tif (authenticationRequest == null) {\n\t\t\treturn null;\n\t\t}\n\t\tthis.cache.evict(relayState);\n\t\treturn authenticationRequest;\n\t}\n\n\t/**\n\t * Use this {@link Cache} instance. The default is an in-memory cache, which means it\n\t * won't work in a clustered environment. Instead, replace it here with a distributed\n\t * cache.\n\t * @param cache the {@link Cache} instance to use\n\t */\n\tpublic void setCache(Cache cache) {\n\t\tthis.cache = cache;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.util.Map;\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.server.PathContainer;\nimport org.springframework.http.server.RequestPath;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Converter} that resolves a {@link RelyingPartyRegistration} by extracting the\n * registration id from the request, querying a\n * {@link RelyingPartyRegistrationRepository}, and resolving any template values.\n *\n * @author Josh Cummings\n * @since 5.4\n */\npublic final class DefaultRelyingPartyRegistrationResolver\n\t\timplements Converter<HttpServletRequest, @Nullable RelyingPartyRegistration>, RelyingPartyRegistrationResolver {\n\n\tprivate Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;\n\n\tprivate final RequestMatcher registrationRequestMatcher = new RequestMatcher() {\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn matcher(request).isMatch();\n\t\t}\n\n\t\t@Override\n\t\tpublic MatchResult matcher(HttpServletRequest request) {\n\t\t\tRequestPath path = RequestPath.parse(request.getRequestURI(), request.getContextPath());\n\t\t\tPathContainer contextPath = path.contextPath();\n\t\t\tPathContainer relativePath = path.subPath(contextPath.elements().size());\n\t\t\tint size = relativePath.elements().size();\n\t\t\tif (size > 0) {\n\t\t\t\treturn RequestMatcher.MatchResult\n\t\t\t\t\t.match(Map.of(\"registrationId\", relativePath.elements().get(size - 1).value()));\n\t\t\t}\n\t\t\treturn RequestMatcher.MatchResult.notMatch();\n\t\t}\n\t};\n\n\tpublic DefaultRelyingPartyRegistrationResolver(\n\t\t\tRelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {\n\t\tAssert.notNull(relyingPartyRegistrationRepository, \"relyingPartyRegistrationRepository cannot be null\");\n\t\tthis.relyingPartyRegistrationRepository = relyingPartyRegistrationRepository;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable RelyingPartyRegistration convert(HttpServletRequest request) {\n\t\treturn resolve(request, null);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable RelyingPartyRegistration resolve(HttpServletRequest request,\n\t\t\t@Nullable String relyingPartyRegistrationId) {\n\t\tif (relyingPartyRegistrationId == null) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Attempting to resolve from \" + this.registrationRequestMatcher\n\t\t\t\t\t\t+ \" since registrationId is null\");\n\t\t\t}\n\t\t\trelyingPartyRegistrationId = this.registrationRequestMatcher.matcher(request)\n\t\t\t\t.getVariables()\n\t\t\t\t.get(\"registrationId\");\n\t\t}\n\t\tif (relyingPartyRegistrationId == null) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Returning null registration since registrationId is null\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tRelyingPartyRegistration registration = this.relyingPartyRegistrationRepository\n\t\t\t.findByRegistrationId(relyingPartyRegistrationId);\n\t\tif (registration == null) {\n\t\t\treturn null;\n\t\t}\n\t\tUriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration);\n\t\tString entityId = uriResolver.resolve(registration.getEntityId());\n\t\tentityId = Objects.requireNonNull(entityId);\n\t\tString assertionConsumerServiceLocation = uriResolver\n\t\t\t.resolve(registration.getAssertionConsumerServiceLocation());\n\t\tassertionConsumerServiceLocation = Objects.requireNonNull(assertionConsumerServiceLocation);\n\t\treturn registration.mutate()\n\t\t\t.entityId(entityId)\n\t\t\t.assertionConsumerServiceLocation(assertionConsumerServiceLocation)\n\t\t\t.singleLogoutServiceLocation(uriResolver.resolve(registration.getSingleLogoutServiceLocation()))\n\t\t\t.singleLogoutServiceResponseLocation(\n\t\t\t\t\turiResolver.resolve(registration.getSingleLogoutServiceResponseLocation()))\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/HttpSessionSaml2AuthenticationRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\n\n/**\n * A {@link Saml2AuthenticationRequestRepository} implementation that uses\n * {@link HttpSession} to store and retrieve the\n * {@link AbstractSaml2AuthenticationRequest}\n *\n * @author Marcus Da Coregio\n * @since 5.6\n */\npublic class HttpSessionSaml2AuthenticationRequestRepository\n\t\timplements Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {\n\n\tprivate static final String DEFAULT_SAML2_AUTHN_REQUEST_ATTR_NAME = HttpSessionSaml2AuthenticationRequestRepository.class\n\t\t.getName()\n\t\t.concat(\".SAML2_AUTHN_REQUEST\");\n\n\tprivate String saml2AuthnRequestAttributeName = DEFAULT_SAML2_AUTHN_REQUEST_ATTR_NAME;\n\n\t@Override\n\tpublic @Nullable AbstractSaml2AuthenticationRequest loadAuthenticationRequest(HttpServletRequest request) {\n\t\tHttpSession httpSession = request.getSession(false);\n\t\tif (httpSession == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (AbstractSaml2AuthenticationRequest) httpSession.getAttribute(this.saml2AuthnRequestAttributeName);\n\t}\n\n\t@Override\n\tpublic void saveAuthenticationRequest(AbstractSaml2AuthenticationRequest authenticationRequest,\n\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\tif (authenticationRequest == null) {\n\t\t\tremoveAuthenticationRequest(request, response);\n\t\t\treturn;\n\t\t}\n\t\tHttpSession httpSession = request.getSession();\n\t\thttpSession.setAttribute(this.saml2AuthnRequestAttributeName, authenticationRequest);\n\t}\n\n\t@Override\n\tpublic @Nullable AbstractSaml2AuthenticationRequest removeAuthenticationRequest(HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = loadAuthenticationRequest(request);\n\t\tif (authenticationRequest == null) {\n\t\t\treturn null;\n\t\t}\n\t\tHttpSession httpSession = request.getSession();\n\t\thttpSession.removeAttribute(this.saml2AuthnRequestAttributeName);\n\t\treturn authenticationRequest;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/OpenSamlOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\ninterface OpenSamlOperations {\n\n\t<T extends XMLObject> T build(QName elementName);\n\n\t<T extends XMLObject> T deserialize(String serialized);\n\n\t<T extends XMLObject> T deserialize(InputStream serialized);\n\n\tSerializationConfigurer<?> serialize(XMLObject object);\n\n\tSerializationConfigurer<?> serialize(Element element);\n\n\tSignatureConfigurer<?> withSigningKeys(Collection<Saml2X509Credential> credentials);\n\n\tVerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials);\n\n\tDecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials);\n\n\tinterface SerializationConfigurer<B extends SerializationConfigurer<B>> {\n\n\t\tB prettyPrint(boolean pretty);\n\n\t\tString serialize();\n\n\t}\n\n\tinterface SignatureConfigurer<B extends SignatureConfigurer<B>> {\n\n\t\tB algorithms(List<String> algs);\n\n\t\t<O extends SignableXMLObject> O sign(O object);\n\n\t\tMap<String, String> sign(Map<String, String> params);\n\n\t}\n\n\tinterface VerificationConfigurer {\n\n\t\tVerificationConfigurer entityId(String entityId);\n\n\t\tCollection<Saml2Error> verify(SignableXMLObject signable);\n\n\t\tCollection<Saml2Error> verify(VerificationConfigurer.RedirectParameters parameters);\n\n\t\tfinal class RedirectParameters {\n\n\t\t\tprivate final String id;\n\n\t\t\tprivate final Issuer issuer;\n\n\t\t\tprivate final String algorithm;\n\n\t\t\tprivate final byte @Nullable [] signature;\n\n\t\t\tprivate final byte[] content;\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"SAML request's ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"SAML request's Issuer cannot be null\");\n\t\t\t\tthis.id = request.getID();\n\t\t\t\tthis.issuer = request.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_REQUEST, relayState, queryParams);\n\t\t\t}\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"SAML response's ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"SAML response's Issuer cannot be null\");\n\t\t\t\tthis.id = response.getID();\n\t\t\t\tthis.issuer = response.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_RESPONSE, relayState, queryParams);\n\t\t\t}\n\n\t\t\tstatic byte[] getContent(String samlObject, @Nullable String relayState,\n\t\t\t\t\tfinal Map<String, String> queryParams) {\n\t\t\t\tif (Objects.nonNull(relayState)) {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s&%s=%s\", samlObject, queryParams.get(samlObject),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.RELAY_STATE, queryParams.get(Saml2ParameterNames.RELAY_STATE),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.SIG_ALG, queryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s\", samlObject, queryParams.get(samlObject), Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\t\t\tqueryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getId() {\n\t\t\t\treturn this.id;\n\t\t\t}\n\n\t\t\tIssuer getIssuer() {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\tbyte[] getContent() {\n\t\t\t\treturn this.content;\n\t\t\t}\n\n\t\t\tString getAlgorithm() {\n\t\t\t\treturn this.algorithm;\n\t\t\t}\n\n\t\t\tbyte @Nullable [] getSignature() {\n\t\t\t\treturn this.signature;\n\t\t\t}\n\n\t\t\tboolean hasSignature() {\n\t\t\t\treturn this.signature != null;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface DecryptionConfigurer {\n\n\t\tvoid decrypt(XMLObject object);\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/RelyingPartyRegistrationPlaceholderResolvers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A factory for creating placeholder resolvers for {@link RelyingPartyRegistration}\n * templates. Supports {@code baseUrl}, {@code baseScheme}, {@code baseHost},\n * {@code basePort}, {@code basePath}, {@code registrationId},\n * {@code relyingPartyEntityId}, and {@code assertingPartyEntityId}\n *\n * @author Josh Cummings\n * @since 6.1\n */\npublic final class RelyingPartyRegistrationPlaceholderResolvers {\n\n\tprivate static final char PATH_DELIMITER = '/';\n\n\tprivate RelyingPartyRegistrationPlaceholderResolvers() {\n\n\t}\n\n\t/**\n\t * Create a resolver based on the given {@link HttpServletRequest}. Given the request,\n\t * placeholders {@code baseUrl}, {@code baseScheme}, {@code baseHost},\n\t * {@code basePort}, and {@code basePath} are resolved.\n\t * @param request the HTTP request\n\t * @return a resolver that can resolve {@code baseUrl}, {@code baseScheme},\n\t * {@code baseHost}, {@code basePort}, and {@code basePath} placeholders\n\t */\n\tpublic static UriResolver uriResolver(HttpServletRequest request) {\n\t\treturn new UriResolver(uriVariables(request));\n\t}\n\n\t/**\n\t * Create a resolver based on the given {@link HttpServletRequest}. Given the request,\n\t * placeholders {@code baseUrl}, {@code baseScheme}, {@code baseHost},\n\t * {@code basePort}, {@code basePath}, {@code registrationId},\n\t * {@code assertingPartyEntityId}, and {@code relyingPartyEntityId} are resolved.\n\t * @param request the HTTP request\n\t * @return a resolver that can resolve {@code baseUrl}, {@code baseScheme},\n\t * {@code baseHost}, {@code basePort}, {@code basePath}, {@code registrationId},\n\t * {@code relyingPartyEntityId}, and {@code assertingPartyEntityId} placeholders\n\t */\n\tpublic static UriResolver uriResolver(HttpServletRequest request, RelyingPartyRegistration registration) {\n\t\tString relyingPartyEntityId = registration.getEntityId();\n\t\tString assertingPartyEntityId = registration.getAssertingPartyMetadata().getEntityId();\n\t\tString registrationId = registration.getRegistrationId();\n\t\tMap<String, String> uriVariables = uriVariables(request);\n\t\turiVariables.put(\"relyingPartyEntityId\", StringUtils.hasText(relyingPartyEntityId) ? relyingPartyEntityId : \"\");\n\t\turiVariables.put(\"assertingPartyEntityId\",\n\t\t\t\tStringUtils.hasText(assertingPartyEntityId) ? assertingPartyEntityId : \"\");\n\t\turiVariables.put(\"entityId\", StringUtils.hasText(assertingPartyEntityId) ? assertingPartyEntityId : \"\");\n\t\turiVariables.put(\"registrationId\", StringUtils.hasText(registrationId) ? registrationId : \"\");\n\t\treturn new UriResolver(uriVariables);\n\t}\n\n\tprivate static Map<String, String> uriVariables(HttpServletRequest request) {\n\t\tString baseUrl = getApplicationUri(request);\n\t\tMap<String, String> uriVariables = new HashMap<>();\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUriString(baseUrl)\n\t\t\t.replaceQuery(null)\n\t\t\t.fragment(null)\n\t\t\t.build();\n\t\tString scheme = uriComponents.getScheme();\n\t\turiVariables.put(\"baseScheme\", (scheme != null) ? scheme : \"\");\n\t\tString host = uriComponents.getHost();\n\t\turiVariables.put(\"baseHost\", (host != null) ? host : \"\");\n\t\t// following logic is based on HierarchicalUriComponents#toUriString()\n\t\tint port = uriComponents.getPort();\n\t\turiVariables.put(\"basePort\", (port == -1) ? \"\" : \":\" + port);\n\t\tString path = uriComponents.getPath();\n\t\tif (StringUtils.hasLength(path) && path.charAt(0) != PATH_DELIMITER) {\n\t\t\tpath = PATH_DELIMITER + path;\n\t\t}\n\t\turiVariables.put(\"basePath\", (path != null) ? path : \"\");\n\t\turiVariables.put(\"baseUrl\", uriComponents.toUriString());\n\t\treturn uriVariables;\n\t}\n\n\tprivate static String getApplicationUri(HttpServletRequest request) {\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))\n\t\t\t.replacePath(request.getContextPath())\n\t\t\t.replaceQuery(null)\n\t\t\t.fragment(null)\n\t\t\t.build();\n\t\treturn uriComponents.toUriString();\n\t}\n\n\t/**\n\t * A class for resolving {@link RelyingPartyRegistration} URIs\n\t */\n\tpublic static final class UriResolver {\n\n\t\tprivate final Map<String, String> uriVariables;\n\n\t\tprivate UriResolver(Map<String, String> uriVariables) {\n\t\t\tthis.uriVariables = uriVariables;\n\t\t}\n\n\t\tpublic @Nullable String resolve(@Nullable String uri) {\n\t\t\tif (uri == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn UriComponentsBuilder.fromUriString(uri).buildAndExpand(this.uriVariables).toUriString();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/RelyingPartyRegistrationResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\n\n/**\n * A contract for resolving a {@link RelyingPartyRegistration} from the HTTP request\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic interface RelyingPartyRegistrationResolver {\n\n\t/**\n\t * Resolve a {@link RelyingPartyRegistration} from the HTTP request, using the\n\t * {@code relyingPartyRegistrationId}, if it is provided\n\t * @param request the HTTP request\n\t * @param relyingPartyRegistrationId the {@link RelyingPartyRegistration} identifier;\n\t * when {@code null}, may attempt to resolve from the request\n\t * @return the resolved {@link RelyingPartyRegistration}\n\t */\n\t@Nullable RelyingPartyRegistration resolve(HttpServletRequest request, @Nullable String relyingPartyRegistrationId);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\n\n/**\n * A repository for {@link AbstractSaml2AuthenticationRequest}\n *\n * @param <T> the type of SAML 2.0 Authentication Request\n * @author Marcus Da Coregio\n * @since 5.6\n */\npublic interface Saml2AuthenticationRequestRepository<T extends AbstractSaml2AuthenticationRequest> {\n\n\t/**\n\t * Loads the {@link AbstractSaml2AuthenticationRequest} from the request\n\t * @param request the current request\n\t * @return the {@link AbstractSaml2AuthenticationRequest} or {@code null} if it is not\n\t * present\n\t */\n\t@Nullable T loadAuthenticationRequest(HttpServletRequest request);\n\n\t/**\n\t * Saves the current authentication request using the {@link HttpServletRequest} and\n\t * {@link HttpServletResponse}\n\t * @param authenticationRequest the {@link AbstractSaml2AuthenticationRequest}\n\t * @param request the current request\n\t * @param response the current response\n\t */\n\tvoid saveAuthenticationRequest(T authenticationRequest, HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Removes the authentication request using the {@link HttpServletRequest} and\n\t * {@link HttpServletResponse}\n\t * @param request the current request\n\t * @param response the current response\n\t * @return the removed {@link AbstractSaml2AuthenticationRequest} or {@code null} if\n\t * it is not present\n\t */\n\t@Nullable T removeAuthenticationRequest(HttpServletRequest request, HttpServletResponse response);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationConverter} that generates a {@link Saml2AuthenticationToken}\n * appropriate for authenticated a SAML 2.0 Assertion against an\n * {@link org.springframework.security.authentication.AuthenticationManager}.\n *\n * @author Josh Cummings\n * @since 5.4\n */\npublic final class Saml2AuthenticationTokenConverter implements AuthenticationConverter {\n\n\tprivate final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;\n\n\tprivate Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository;\n\n\tprivate boolean shouldConvertGetRequests = true;\n\n\t/**\n\t * Constructs a {@link Saml2AuthenticationTokenConverter} given a strategy for\n\t * resolving {@link RelyingPartyRegistration}s\n\t * @param relyingPartyRegistrationResolver the strategy for resolving\n\t * {@link RelyingPartyRegistration}s\n\t */\n\tpublic Saml2AuthenticationTokenConverter(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {\n\t\tAssert.notNull(relyingPartyRegistrationResolver, \"relyingPartyRegistrationResolver cannot be null\");\n\t\tthis.relyingPartyRegistrationResolver = relyingPartyRegistrationResolver;\n\t\tthis.authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();\n\t}\n\n\t@Override\n\tpublic @Nullable Saml2AuthenticationToken convert(HttpServletRequest request) {\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.loadAuthenticationRequest(request);\n\t\tString relyingPartyRegistrationId = (authenticationRequest != null)\n\t\t\t\t? authenticationRequest.getRelyingPartyRegistrationId() : null;\n\t\tRelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationResolver.resolve(request,\n\t\t\t\trelyingPartyRegistrationId);\n\t\tif (relyingPartyRegistration == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString saml2Response = decode(request);\n\t\tif (saml2Response == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new Saml2AuthenticationToken(relyingPartyRegistration, saml2Response, authenticationRequest);\n\t}\n\n\t/**\n\t * Use the given {@link Saml2AuthenticationRequestRepository} to load authentication\n\t * request.\n\t * @param authenticationRequestRepository the\n\t * {@link Saml2AuthenticationRequestRepository} to use\n\t * @since 5.6\n\t */\n\tpublic void setAuthenticationRequestRepository(\n\t\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {\n\t\tAssert.notNull(authenticationRequestRepository, \"authenticationRequestRepository cannot be null\");\n\t\tthis.authenticationRequestRepository = authenticationRequestRepository;\n\t}\n\n\t/**\n\t * Use the given {@code shouldConvertGetRequests} to convert {@code GET} requests.\n\t * Default is {@code true}.\n\t * @param shouldConvertGetRequests the {@code shouldConvertGetRequests} to use\n\t * @since 7.0\n\t */\n\tpublic void setShouldConvertGetRequests(boolean shouldConvertGetRequests) {\n\t\tthis.shouldConvertGetRequests = shouldConvertGetRequests;\n\t}\n\n\tprivate @Nullable String decode(HttpServletRequest request) {\n\t\tString encoded = request.getParameter(Saml2ParameterNames.SAML_RESPONSE);\n\t\tif (encoded == null) {\n\t\t\treturn null;\n\t\t}\n\t\tboolean isGet = HttpMethod.GET.matches(request.getMethod());\n\t\tif (!this.shouldConvertGetRequests && isGet) {\n\t\t\treturn null;\n\t\t}\n\t\tSaml2Utils.DecodingConfigurer decoding = Saml2Utils.withEncoded(encoded).requireBase64(true).inflate(isGet);\n\t\ttry {\n\t\t\treturn decoding.decode();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2AuthenticationException(Saml2Error.invalidResponse(ex.getMessage()), ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResolver;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@link jakarta.servlet.Filter} that returns the metadata for a Relying Party\n *\n * @author Jakub Kubrynski\n * @author Josh Cummings\n * @since 5.4\n */\npublic final class Saml2MetadataFilter extends OncePerRequestFilter {\n\n\tpublic static final String DEFAULT_METADATA_FILE_NAME = \"saml-{registrationId}-metadata.xml\";\n\n\tprivate final Saml2MetadataResponseResolver metadataResolver;\n\n\tpublic Saml2MetadataFilter(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver,\n\t\t\tSaml2MetadataResolver saml2MetadataResolver) {\n\t\tAssert.notNull(relyingPartyRegistrationResolver, \"relyingPartyRegistrationResolver cannot be null\");\n\t\tAssert.notNull(saml2MetadataResolver, \"saml2MetadataResolver cannot be null\");\n\t\tthis.metadataResolver = new Saml2MetadataResponseResolverAdapter(relyingPartyRegistrationResolver,\n\t\t\t\tsaml2MetadataResolver);\n\t}\n\n\t/**\n\t * Constructs an instance of {@link Saml2MetadataFilter} using the provided\n\t * parameters. The {@link #metadataResolver} field will be initialized with a\n\t * {@link DefaultRelyingPartyRegistrationResolver} instance using the provided\n\t * {@link RelyingPartyRegistrationRepository}\n\t * @param relyingPartyRegistrationRepository the\n\t * {@link RelyingPartyRegistrationRepository} to use\n\t * @param saml2MetadataResolver the {@link Saml2MetadataResolver} to use\n\t * @since 6.1\n\t */\n\tpublic Saml2MetadataFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository,\n\t\t\tSaml2MetadataResolver saml2MetadataResolver) {\n\t\tthis(new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository), saml2MetadataResolver);\n\t}\n\n\t/**\n\t * Constructs an instance of {@link Saml2MetadataFilter}\n\t * @param metadataResponseResolver the strategy for producing metadata\n\t * @since 6.1\n\t */\n\tpublic Saml2MetadataFilter(Saml2MetadataResponseResolver metadataResponseResolver) {\n\t\tthis.metadataResolver = metadataResponseResolver;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\t\tSaml2MetadataResponse metadata;\n\t\ttry {\n\t\t\tmetadata = this.metadataResolver.resolve(request);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tresponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n\t\t\treturn;\n\t\t}\n\t\tif (metadata == null) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\twriteMetadataToResponse(response, metadata);\n\t}\n\n\tprivate void writeMetadataToResponse(HttpServletResponse response, Saml2MetadataResponse metadata)\n\t\t\tthrows IOException {\n\t\tresponse.setContentType(\"application/samlmetadata+xml\");\n\t\tString format = \"attachment; filename=\\\"%s\\\"; filename*=UTF-8''%s\";\n\t\tString fileName = metadata.getFileName();\n\t\tString encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);\n\t\tresponse.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format(format, fileName, encodedFileName));\n\t\tresponse.setContentLength(metadata.getMetadata().getBytes(StandardCharsets.UTF_8).length);\n\t\tresponse.setCharacterEncoding(StandardCharsets.UTF_8.name());\n\t\tresponse.getWriter().write(metadata.getMetadata());\n\t}\n\n\t/**\n\t * Set the {@link RequestMatcher} that determines whether this filter should handle\n\t * the incoming {@link HttpServletRequest}\n\t * @param requestMatcher the {@link RequestMatcher} to identify requests for metadata\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tAssert.isInstanceOf(Saml2MetadataResponseResolverAdapter.class, this.metadataResolver,\n\t\t\t\t\"a Saml2MetadataResponseResolver and RequestMatcher cannot be both set on this filter. Please set the request matcher on the Saml2MetadataResponseResolver itself.\");\n\t\t((Saml2MetadataResponseResolverAdapter) this.metadataResolver).setRequestMatcher(requestMatcher);\n\t}\n\n\t/**\n\t * Sets the metadata filename template containing the {@code {registrationId}}\n\t * template variable.\n\t *\n\t * <p>\n\t * The default value is {@code saml-{registrationId}-metadata.xml}\n\t * @param metadataFilename metadata filename, must contain a {registrationId}\n\t * @since 5.5\n\t */\n\tpublic void setMetadataFilename(String metadataFilename) {\n\t\tAssert.hasText(metadataFilename, \"metadataFilename cannot be empty\");\n\t\tAssert.isTrue(metadataFilename.contains(\"{registrationId}\"),\n\t\t\t\t\"metadataFilename must contain a {registrationId} match variable\");\n\t\tAssert.isInstanceOf(Saml2MetadataResponseResolverAdapter.class, this.metadataResolver,\n\t\t\t\t\"a Saml2MetadataResponseResolver and file name cannot be both set on this filter. Please set the file name on the Saml2MetadataResponseResolver itself.\");\n\t\t((Saml2MetadataResponseResolverAdapter) this.metadataResolver).setMetadataFilename(metadataFilename);\n\t}\n\n\tprivate static final class Saml2MetadataResponseResolverAdapter implements Saml2MetadataResponseResolver {\n\n\t\tprivate final RelyingPartyRegistrationResolver registrations;\n\n\t\tprivate RequestMatcher requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(\"/saml2/service-provider-metadata/{registrationId}\");\n\n\t\tprivate final Saml2MetadataResolver metadataResolver;\n\n\t\tprivate String metadataFilename = DEFAULT_METADATA_FILE_NAME;\n\n\t\tSaml2MetadataResponseResolverAdapter(RelyingPartyRegistrationResolver registrations,\n\t\t\t\tSaml2MetadataResolver metadataResolver) {\n\t\t\tthis.registrations = registrations;\n\t\t\tthis.metadataResolver = metadataResolver;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Saml2MetadataResponse resolve(HttpServletRequest request) {\n\t\t\tRequestMatcher.MatchResult matcher = this.requestMatcher.matcher(request);\n\t\t\tif (!matcher.isMatch()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString registrationId = matcher.getVariables().get(\"registrationId\");\n\t\t\tRelyingPartyRegistration relyingPartyRegistration = this.registrations.resolve(request, registrationId);\n\t\t\tif (relyingPartyRegistration == null) {\n\t\t\t\tthrow new Saml2Exception(\"registration not found\");\n\t\t\t}\n\t\t\tregistrationId = relyingPartyRegistration.getRegistrationId();\n\t\t\tString metadata = this.metadataResolver.resolve(relyingPartyRegistration);\n\t\t\tString fileName = this.metadataFilename.replace(\"{registrationId}\", registrationId);\n\t\t\treturn new Saml2MetadataResponse(metadata, fileName);\n\t\t}\n\n\t\tvoid setRequestMatcher(RequestMatcher requestMatcher) {\n\t\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\t\tthis.requestMatcher = requestMatcher;\n\t\t}\n\n\t\tvoid setMetadataFilename(String metadataFilename) {\n\t\t\tAssert.hasText(metadataFilename, \"metadataFilename cannot be empty\");\n\t\t\tAssert.isTrue(metadataFilename.contains(\"{registrationId}\"),\n\t\t\t\t\t\"metadataFilename must contain a {registrationId} match variable\");\n\t\t\tthis.metadataFilename = metadataFilename;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2WebSsoAuthenticationRequestFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.HtmlUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * This {@code Filter} formulates a\n * <a href=\"https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf\">SAML 2.0\n * AuthnRequest</a> (line 1968) and redirects to a configured asserting party.\n *\n * <p>\n * It supports the <a href=\n * \"https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf\">HTTP-Redirect</a>\n * (line 520) and <a href=\n * \"https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf\">HTTP-POST</a>\n * (line 753) bindings.\n *\n * <p>\n * By default, this {@code Filter} responds to authentication requests at the {@code URI}\n * {@code /saml2/authenticate/{registrationId}}. The {@code URI} template variable\n * {@code {registrationId}} represents the\n * {@link RelyingPartyRegistration#getRegistrationId() registration identifier} of the\n * relying party that is used for initiating the authentication request.\n *\n * @author Filip Hanik\n * @author Josh Cummings\n * @since 5.2\n */\npublic class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter {\n\n\tprivate final Saml2AuthenticationRequestResolver authenticationRequestResolver;\n\n\tprivate Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();\n\n\t/**\n\t * Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the strategy for\n\t * resolving the {@code AuthnRequest}\n\t * @param authenticationRequestResolver the strategy for resolving the\n\t * {@code AuthnRequest}\n\t * @since 5.7\n\t */\n\tpublic Saml2WebSsoAuthenticationRequestFilter(Saml2AuthenticationRequestResolver authenticationRequestResolver) {\n\t\tAssert.notNull(authenticationRequestResolver, \"authenticationRequestResolver cannot be null\");\n\t\tthis.authenticationRequestResolver = authenticationRequestResolver;\n\t}\n\n\t/**\n\t * Use the given {@link Saml2AuthenticationRequestRepository} to save the\n\t * authentication request\n\t * @param authenticationRequestRepository the\n\t * {@link Saml2AuthenticationRequestRepository} to use\n\t * @since 5.6\n\t */\n\tpublic void setAuthenticationRequestRepository(\n\t\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {\n\t\tAssert.notNull(authenticationRequestRepository, \"authenticationRequestRepository cannot be null\");\n\t\tthis.authenticationRequestRepository = authenticationRequestRepository;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestResolver.resolve(request);\n\t\tif (authenticationRequest == null) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tif (authenticationRequest instanceof Saml2RedirectAuthenticationRequest) {\n\t\t\tsendRedirect(request, response, (Saml2RedirectAuthenticationRequest) authenticationRequest);\n\t\t}\n\t\telse {\n\t\t\tsendPost(request, response, (Saml2PostAuthenticationRequest) authenticationRequest);\n\t\t}\n\t}\n\n\tprivate void sendRedirect(HttpServletRequest request, HttpServletResponse response,\n\t\t\tSaml2RedirectAuthenticationRequest authenticationRequest) throws IOException {\n\t\tthis.authenticationRequestRepository.saveAuthenticationRequest(authenticationRequest, request, response);\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder\n\t\t\t.fromUriString(authenticationRequest.getAuthenticationRequestUri());\n\t\taddParameter(Saml2ParameterNames.SAML_REQUEST, authenticationRequest.getSamlRequest(), uriBuilder);\n\t\taddParameter(Saml2ParameterNames.RELAY_STATE, authenticationRequest.getRelayState(), uriBuilder);\n\t\taddParameter(Saml2ParameterNames.SIG_ALG, authenticationRequest.getSigAlg(), uriBuilder);\n\t\taddParameter(Saml2ParameterNames.SIGNATURE, authenticationRequest.getSignature(), uriBuilder);\n\t\tString redirectUrl = uriBuilder.build(true).toUriString();\n\t\tresponse.sendRedirect(redirectUrl);\n\t}\n\n\tprivate void addParameter(String name, @Nullable String value, UriComponentsBuilder builder) {\n\t\tAssert.hasText(name, \"name cannot be empty or null\");\n\t\tif (StringUtils.hasText(value)) {\n\t\t\tbuilder.queryParam(UriUtils.encode(name, StandardCharsets.ISO_8859_1),\n\t\t\t\t\tUriUtils.encode(value, StandardCharsets.ISO_8859_1));\n\t\t}\n\t}\n\n\tprivate void sendPost(HttpServletRequest request, HttpServletResponse response,\n\t\t\tSaml2PostAuthenticationRequest authenticationRequest) throws IOException {\n\t\tthis.authenticationRequestRepository.saveAuthenticationRequest(authenticationRequest, request, response);\n\t\tString html = createSamlPostRequestFormData(authenticationRequest);\n\t\tresponse.setContentType(MediaType.TEXT_HTML_VALUE);\n\t\tresponse.getWriter().write(html);\n\t}\n\n\tprivate String createSamlPostRequestFormData(Saml2PostAuthenticationRequest authenticationRequest) {\n\t\tString authenticationRequestUri = authenticationRequest.getAuthenticationRequestUri();\n\t\tString relayState = authenticationRequest.getRelayState();\n\t\tString samlRequest = authenticationRequest.getSamlRequest();\n\t\tStringBuilder html = new StringBuilder();\n\t\thtml.append(\"<!DOCTYPE html>\\n\");\n\t\thtml.append(\"<html>\\n\").append(\"    <head>\\n\");\n\t\thtml.append(\"        <meta http-equiv=\\\"Content-Security-Policy\\\" \")\n\t\t\t.append(\"content=\\\"script-src 'sha256-oZhLbc2kO8b8oaYLrUc7uye1MgVKMyLtPqWR4WtKF+c='\\\">\\n\");\n\t\thtml.append(\"        <meta charset=\\\"utf-8\\\" />\\n\");\n\t\thtml.append(\"    </head>\\n\");\n\t\thtml.append(\"    <body>\\n\");\n\t\thtml.append(\"        <noscript>\\n\");\n\t\thtml.append(\"            <p>\\n\");\n\t\thtml.append(\"                <strong>Note:</strong> Since your browser does not support JavaScript,\\n\");\n\t\thtml.append(\"                you must press the Continue button once to proceed.\\n\");\n\t\thtml.append(\"            </p>\\n\");\n\t\thtml.append(\"        </noscript>\\n\");\n\t\thtml.append(\"        \\n\");\n\t\thtml.append(\"        <form action=\\\"\");\n\t\thtml.append(authenticationRequestUri);\n\t\thtml.append(\"\\\" method=\\\"post\\\">\\n\");\n\t\thtml.append(\"            <div>\\n\");\n\t\thtml.append(\"                <input type=\\\"hidden\\\" name=\\\"SAMLRequest\\\" value=\\\"\");\n\t\thtml.append(HtmlUtils.htmlEscape(samlRequest));\n\t\thtml.append(\"\\\"/>\\n\");\n\t\tif (StringUtils.hasText(relayState)) {\n\t\t\thtml.append(\"                <input type=\\\"hidden\\\" name=\\\"RelayState\\\" value=\\\"\");\n\t\t\thtml.append(HtmlUtils.htmlEscape(relayState));\n\t\t\thtml.append(\"\\\"/>\\n\");\n\t\t}\n\t\thtml.append(\"            </div>\\n\");\n\t\thtml.append(\"            <noscript>\\n\");\n\t\thtml.append(\"                <div>\\n\");\n\t\thtml.append(\"                    <input type=\\\"submit\\\" value=\\\"Continue\\\"/>\\n\");\n\t\thtml.append(\"                </div>\\n\");\n\t\thtml.append(\"            </noscript>\\n\");\n\t\thtml.append(\"        </form>\\n\");\n\t\thtml.append(\"        \\n\");\n\t\thtml.append(\"        <script>window.onload = function() { document.forms[0].submit(); }</script>\\n\");\n\t\thtml.append(\"    </body>\\n\");\n\t\thtml.append(\"</html>\");\n\t\treturn html.toString();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/BaseOpenSamlAuthenticationRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.config.ConfigurationService;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.XMLObjectBuilderFactory;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistry;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.saml.common.AbstractSAMLObjectBuilder;\nimport org.opensaml.saml.common.SAMLObject;\nimport org.opensaml.saml.saml2.core.AuthnRequest;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.NameIDPolicy;\nimport org.opensaml.saml.saml2.core.impl.AuthnRequestBuilder;\nimport org.opensaml.saml.saml2.core.impl.IssuerBuilder;\nimport org.opensaml.saml.saml2.core.impl.NameIDBuilder;\nimport org.opensaml.saml.saml2.core.impl.NameIDPolicyBuilder;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.AndRequestMatcher;\nimport org.springframework.security.web.util.matcher.ParameterRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatchers;\nimport org.springframework.util.Assert;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * For internal use only. Intended for consolidating common behavior related to minting a\n * SAML 2.0 Authn Request.\n */\nclass BaseOpenSamlAuthenticationRequestResolver implements Saml2AuthenticationRequestResolver {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final OpenSamlOperations saml;\n\n\tprivate final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;\n\n\tprivate final AuthnRequestBuilder authnRequestBuilder;\n\n\tprivate final IssuerBuilder issuerBuilder;\n\n\tprivate final NameIDBuilder nameIdBuilder;\n\n\tprivate final NameIDPolicyBuilder nameIdPolicyBuilder;\n\n\tprivate RequestMatcher requestMatcher = RequestMatchers.anyOf(\n\t\t\tPathPatternRequestMatcher.withDefaults()\n\t\t\t\t.matcher(Saml2AuthenticationRequestResolver.DEFAULT_AUTHENTICATION_REQUEST_URI),\n\t\t\tnew PathPatternQueryRequestMatcher(\"/saml2/authenticate\", \"registrationId={registrationId}\"));\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\tprivate Converter<HttpServletRequest, @Nullable String> relayStateResolver = (request) -> UUID.randomUUID()\n\t\t.toString();\n\n\tprivate Consumer<AuthnRequestParameters> parametersConsumer = (parameters) -> {\n\t};\n\n\t/**\n\t * Construct a {@link BaseOpenSamlAuthenticationRequestResolver} using the provided\n\t * parameters\n\t * @param relyingPartyRegistrationResolver a strategy for resolving the\n\t * {@link RelyingPartyRegistration} from the {@link HttpServletRequest}\n\t */\n\tBaseOpenSamlAuthenticationRequestResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver,\n\t\t\tOpenSamlOperations saml) {\n\t\tthis.saml = saml;\n\t\tAssert.notNull(relyingPartyRegistrationResolver, \"relyingPartyRegistrationResolver cannot be null\");\n\t\tthis.relyingPartyRegistrationResolver = relyingPartyRegistrationResolver;\n\t\tXMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class);\n\t\tAssert.notNull(registry, \"XMLObjectProviderRegistry must be configured\");\n\t\tXMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();\n\t\tAssert.notNull(builderFactory, \"XMLObjectBuilderFactory must be configured\");\n\t\tthis.authnRequestBuilder = builder(builderFactory.ensureBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME));\n\t\tthis.issuerBuilder = builder(builderFactory.ensureBuilder(Issuer.DEFAULT_ELEMENT_NAME));\n\t\tthis.nameIdBuilder = builder(builderFactory.ensureBuilder(NameID.DEFAULT_ELEMENT_NAME));\n\t\tthis.nameIdPolicyBuilder = builder(builderFactory.ensureBuilder(NameIDPolicy.DEFAULT_ELEMENT_NAME));\n\t}\n\n\tprivate static <T extends SAMLObject, B extends AbstractSAMLObjectBuilder<T>> B builder(\n\t\t\tXMLObjectBuilder<T> builder) {\n\t\treturn (B) builder;\n\t}\n\n\tvoid setClock(Clock clock) {\n\t\tthis.clock = clock;\n\t}\n\n\tvoid setRelayStateResolver(Converter<HttpServletRequest, @Nullable String> relayStateResolver) {\n\t\tthis.relayStateResolver = relayStateResolver;\n\t}\n\n\tvoid setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\tvoid setParametersConsumer(Consumer<AuthnRequestParameters> parametersConsumer) {\n\t\tthis.parametersConsumer = parametersConsumer;\n\t}\n\n\t@Override\n\tpublic <T extends AbstractSaml2AuthenticationRequest> @Nullable T resolve(HttpServletRequest request) {\n\t\tRequestMatcher.MatchResult result = this.requestMatcher.matcher(request);\n\t\tif (!result.isMatch()) {\n\t\t\treturn null;\n\t\t}\n\t\tString registrationId = result.getVariables().get(\"registrationId\");\n\t\tRelyingPartyRegistration registration = this.relyingPartyRegistrationResolver.resolve(request, registrationId);\n\t\tif (registration == null) {\n\t\t\treturn null;\n\t\t}\n\t\tUriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration);\n\t\tString entityId = uriResolver.resolve(registration.getEntityId());\n\t\tString assertionConsumerServiceLocation = uriResolver\n\t\t\t.resolve(registration.getAssertionConsumerServiceLocation());\n\t\tAuthnRequest authnRequest = this.authnRequestBuilder.buildObject();\n\t\tauthnRequest.setForceAuthn(Boolean.FALSE);\n\t\tauthnRequest.setIsPassive(Boolean.FALSE);\n\t\tauthnRequest.setProtocolBinding(registration.getAssertionConsumerServiceBinding().getUrn());\n\t\tIssuer iss = this.issuerBuilder.buildObject();\n\t\tiss.setValue(entityId);\n\t\tauthnRequest.setIssuer(iss);\n\t\tauthnRequest.setDestination(registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation());\n\t\tauthnRequest.setAssertionConsumerServiceURL(assertionConsumerServiceLocation);\n\t\tif (registration.getNameIdFormat() != null) {\n\t\t\tNameIDPolicy nameIdPolicy = this.nameIdPolicyBuilder.buildObject();\n\t\t\tnameIdPolicy.setFormat(registration.getNameIdFormat());\n\t\t\tauthnRequest.setNameIDPolicy(nameIdPolicy);\n\t\t}\n\t\tauthnRequest.setIssueInstant(Instant.now(this.clock));\n\t\tthis.parametersConsumer.accept(new AuthnRequestParameters(request, registration, authnRequest));\n\t\tif (authnRequest.getID() == null) {\n\t\t\tauthnRequest.setID(\"ARQ\" + UUID.randomUUID().toString().substring(1));\n\t\t}\n\t\tString relayState = this.relayStateResolver.convert(request);\n\t\tSaml2MessageBinding binding = registration.getAssertingPartyMetadata().getSingleSignOnServiceBinding();\n\t\tif (binding == Saml2MessageBinding.POST) {\n\t\t\tif (registration.getAssertingPartyMetadata().getWantAuthnRequestsSigned()\n\t\t\t\t\t|| registration.isAuthnRequestsSigned()) {\n\t\t\t\tthis.saml.withSigningKeys(registration.getSigningX509Credentials())\n\t\t\t\t\t.algorithms(registration.getAssertingPartyMetadata().getSigningAlgorithms())\n\t\t\t\t\t.sign(authnRequest);\n\t\t\t}\n\t\t\tString xml = serialize(authnRequest);\n\t\t\tString encoded = Saml2Utils.withDecoded(xml).encode();\n\t\t\treturn (T) Saml2PostAuthenticationRequest.withRelyingPartyRegistration(registration)\n\t\t\t\t.samlRequest(encoded)\n\t\t\t\t.relayState(relayState)\n\t\t\t\t.id(Objects.requireNonNull(authnRequest.getID()))\n\t\t\t\t.build();\n\t\t}\n\t\telse {\n\t\t\tString xml = serialize(authnRequest);\n\t\t\tString deflatedAndEncoded = Saml2Utils.withDecoded(xml).deflate(true).encode();\n\t\t\tSaml2RedirectAuthenticationRequest.Builder builder = Saml2RedirectAuthenticationRequest\n\t\t\t\t.withRelyingPartyRegistration(registration)\n\t\t\t\t.samlRequest(deflatedAndEncoded)\n\t\t\t\t.relayState(relayState)\n\t\t\t\t.id(Objects.requireNonNull(authnRequest.getID()));\n\t\t\tif (registration.getAssertingPartyMetadata().getWantAuthnRequestsSigned()\n\t\t\t\t\t|| registration.isAuthnRequestsSigned()) {\n\t\t\t\tMap<String, String> signingParameters = new HashMap<>();\n\t\t\t\tsigningParameters.put(Saml2ParameterNames.SAML_REQUEST, deflatedAndEncoded);\n\t\t\t\tif (relayState != null) {\n\t\t\t\t\tsigningParameters.put(Saml2ParameterNames.RELAY_STATE, relayState);\n\t\t\t\t}\n\t\t\t\tMap<String, String> query = this.saml.withSigningKeys(registration.getSigningX509Credentials())\n\t\t\t\t\t.algorithms(registration.getAssertingPartyMetadata().getSigningAlgorithms())\n\t\t\t\t\t.sign(signingParameters);\n\t\t\t\tbuilder.sigAlg(query.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t.signature(query.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t}\n\t\t\treturn (T) builder.build();\n\t\t}\n\t}\n\n\tprivate String serialize(AuthnRequest authnRequest) {\n\t\treturn this.saml.serialize(authnRequest).serialize();\n\t}\n\n\tprivate static final class PathPatternQueryRequestMatcher implements RequestMatcher {\n\n\t\tprivate final RequestMatcher matcher;\n\n\t\tPathPatternQueryRequestMatcher(String path, String... params) {\n\t\t\tList<RequestMatcher> matchers = new ArrayList<>();\n\t\t\tmatchers.add(pathPattern(path));\n\t\t\tfor (String param : params) {\n\t\t\t\tString[] parts = param.split(\"=\");\n\t\t\t\tif (parts.length == 1) {\n\t\t\t\t\tmatchers.add(new ParameterRequestMatcher(parts[0]));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tmatchers.add(new ParameterRequestMatcher(parts[0], parts[1]));\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.matcher = new AndRequestMatcher(matchers);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn matcher(request).isMatch();\n\t\t}\n\n\t\t@Override\n\t\tpublic MatchResult matcher(HttpServletRequest request) {\n\t\t\treturn this.matcher.matcher(request);\n\t\t}\n\n\t}\n\n\tstatic final class AuthnRequestParameters {\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tprivate final AuthnRequest authnRequest;\n\n\t\tAuthnRequestParameters(HttpServletRequest request, RelyingPartyRegistration registration,\n\t\t\t\tAuthnRequest authnRequest) {\n\t\t\tthis.request = request;\n\t\t\tthis.registration = registration;\n\t\t\tthis.authnRequest = authnRequest;\n\t\t}\n\n\t\tHttpServletRequest getRequest() {\n\t\t\treturn this.request;\n\t\t}\n\n\t\tRelyingPartyRegistration getRelyingPartyRegistration() {\n\t\t\treturn this.registration;\n\t\t}\n\n\t\tAuthnRequest getAuthnRequest() {\n\t\t\treturn this.authnRequest;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\ninterface OpenSamlOperations {\n\n\t<T extends XMLObject> T build(QName elementName);\n\n\t<T extends XMLObject> T deserialize(String serialized);\n\n\t<T extends XMLObject> T deserialize(InputStream serialized);\n\n\tSerializationConfigurer<?> serialize(XMLObject object);\n\n\tSerializationConfigurer<?> serialize(Element element);\n\n\tSignatureConfigurer<?> withSigningKeys(Collection<Saml2X509Credential> credentials);\n\n\tVerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials);\n\n\tDecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials);\n\n\tinterface SerializationConfigurer<B extends SerializationConfigurer<B>> {\n\n\t\tB prettyPrint(boolean pretty);\n\n\t\tString serialize();\n\n\t}\n\n\tinterface SignatureConfigurer<B extends SignatureConfigurer<B>> {\n\n\t\tB algorithms(List<String> algs);\n\n\t\t<O extends SignableXMLObject> O sign(O object);\n\n\t\tMap<String, String> sign(Map<String, String> params);\n\n\t}\n\n\tinterface VerificationConfigurer {\n\n\t\tVerificationConfigurer entityId(String entityId);\n\n\t\tCollection<Saml2Error> verify(SignableXMLObject signable);\n\n\t\tCollection<Saml2Error> verify(VerificationConfigurer.RedirectParameters parameters);\n\n\t\tfinal class RedirectParameters {\n\n\t\t\tprivate final String id;\n\n\t\t\tprivate final Issuer issuer;\n\n\t\t\tprivate final String algorithm;\n\n\t\t\tprivate final byte @Nullable [] signature;\n\n\t\t\tprivate final byte[] content;\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"SAML request's ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"SAML request's Issuer cannot be null\");\n\t\t\t\tthis.id = request.getID();\n\t\t\t\tthis.issuer = request.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_REQUEST, relayState, queryParams);\n\t\t\t}\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"SAML response's ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"SAML response's Issuer cannot be null\");\n\t\t\t\tthis.id = response.getID();\n\t\t\t\tthis.issuer = response.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_RESPONSE, relayState, queryParams);\n\t\t\t}\n\n\t\t\tstatic byte[] getContent(String samlObject, @Nullable String relayState,\n\t\t\t\t\tfinal Map<String, String> queryParams) {\n\t\t\t\tif (Objects.nonNull(relayState)) {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s&%s=%s\", samlObject, queryParams.get(samlObject),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.RELAY_STATE, queryParams.get(Saml2ParameterNames.RELAY_STATE),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.SIG_ALG, queryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s\", samlObject, queryParams.get(samlObject), Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\t\t\tqueryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getId() {\n\t\t\t\treturn this.id;\n\t\t\t}\n\n\t\t\tIssuer getIssuer() {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\tbyte[] getContent() {\n\t\t\t\treturn this.content;\n\t\t\t}\n\n\t\t\tString getAlgorithm() {\n\t\t\t\treturn this.algorithm;\n\t\t\t}\n\n\t\t\tbyte @Nullable [] getSignature() {\n\t\t\t\treturn this.signature;\n\t\t\t}\n\n\t\t\tboolean hasSignature() {\n\t\t\t\treturn this.signature != null;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface DecryptionConfigurer {\n\n\t\tvoid decrypt(XMLObject object);\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2AuthenticationRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\n\n/**\n * A strategy for resolving a SAML 2.0 Authentication Request from the\n * {@link HttpServletRequest}.\n *\n * @author Josh Cummings\n * @since 5.7\n */\npublic interface Saml2AuthenticationRequestResolver {\n\n\tString DEFAULT_AUTHENTICATION_REQUEST_URI = \"/saml2/authenticate/{registrationId}\";\n\n\t<T extends AbstractSaml2AuthenticationRequest> @Nullable T resolve(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;\nimport org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * @since 5.2\n */\npublic class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n\tpublic static final String DEFAULT_FILTER_PROCESSES_URI = \"/login/saml2/sso/{registrationId}\";\n\n\tprivate static final RequestMatcher DEFAULT_REQUEST_MATCHER = new OrRequestMatcher(\n\t\t\tpathPattern(DEFAULT_FILTER_PROCESSES_URI), pathPattern(\"/login/saml2/sso\"));\n\n\tprivate final AuthenticationConverter authenticationConverter;\n\n\tprivate Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();\n\n\tprivate boolean continueChainWhenNoRelyingPartyRegistrationFound = false;\n\n\t/**\n\t * Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter that is\n\t * configured to use the {@link #DEFAULT_FILTER_PROCESSES_URI} processing URL\n\t * @param relyingPartyRegistrationRepository - repository of configured SAML 2\n\t * entities. Required.\n\t */\n\tpublic Saml2WebSsoAuthenticationFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {\n\t\tthis(relyingPartyRegistrationRepository, DEFAULT_FILTER_PROCESSES_URI);\n\t\tRequestMatcher processUri = pathPattern(DEFAULT_FILTER_PROCESSES_URI);\n\t\tsetRequiresAuthenticationRequestMatcher(processUri);\n\t}\n\n\t/**\n\t * Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter\n\t * @param relyingPartyRegistrationRepository - repository of configured SAML 2\n\t * entities. Required.\n\t * @param filterProcessesUrl the processing URL, must contain a {registrationId}\n\t * variable. Required.\n\t */\n\tpublic Saml2WebSsoAuthenticationFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository,\n\t\t\tString filterProcessesUrl) {\n\t\tthis(new Saml2AuthenticationTokenConverter(\n\t\t\t\tnew DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository)), filterProcessesUrl);\n\t\tAssert.isTrue(filterProcessesUrl.contains(\"{registrationId}\"),\n\t\t\t\t\"filterProcessesUrl must contain a {registrationId} match variable\");\n\t}\n\n\t/**\n\t * Creates a {@link Saml2WebSsoAuthenticationFilter} that is configured to use the\n\t * {@link #DEFAULT_FILTER_PROCESSES_URI} processing URL\n\t * @param authenticationConverter the strategy for converting an\n\t * {@link HttpServletRequest} into an {@link Authentication}\n\t * @since 6.2\n\t */\n\tpublic Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter) {\n\t\tsuper(DEFAULT_REQUEST_MATCHER);\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\tsetAllowSessionCreation(true);\n\t\tsetSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());\n\t\tsetAuthenticationConverter(authenticationConverter);\n\t}\n\n\t/**\n\t * Creates a {@link Saml2WebSsoAuthenticationFilter} given the provided parameters\n\t * @param authenticationConverter the strategy for converting an\n\t * {@link HttpServletRequest} into an {@link Authentication}\n\t * @param filterProcessesUrl the processing URL\n\t * @since 5.4\n\t */\n\tpublic Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter, String filterProcessesUrl) {\n\t\tsuper(filterProcessesUrl);\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tAssert.hasText(filterProcessesUrl, \"filterProcessesUrl must contain a URL pattern\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\tsetAllowSessionCreation(true);\n\t\tsetSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());\n\t\tsetAuthenticationConverter(authenticationConverter);\n\t}\n\n\t@Override\n\tprotected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {\n\t\treturn super.requiresAuthentication(request, response);\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows AuthenticationException {\n\t\tAuthentication authentication = this.authenticationConverter.convert(request);\n\t\tif (authentication == null) {\n\t\t\tif (this.continueChainWhenNoRelyingPartyRegistrationFound) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tSaml2Error saml2Error = new Saml2Error(Saml2ErrorCodes.RELYING_PARTY_REGISTRATION_NOT_FOUND,\n\t\t\t\t\t\"No relying party registration found\");\n\t\t\tthrow new Saml2AuthenticationException(saml2Error);\n\t\t}\n\t\tsetDetails(request, authentication);\n\t\tthis.authenticationRequestRepository.removeAuthenticationRequest(request, response);\n\t\treturn getAuthenticationManager().authenticate(authentication);\n\t}\n\n\t/**\n\t * Use the given {@link Saml2AuthenticationRequestRepository} to remove the saved\n\t * authentication request. If the {@link #authenticationConverter} is of the type\n\t * {@link Saml2AuthenticationTokenConverter}, the\n\t * {@link Saml2AuthenticationRequestRepository} will also be set into the\n\t * {@link #authenticationConverter}.\n\t * @param authenticationRequestRepository the\n\t * {@link Saml2AuthenticationRequestRepository} to use\n\t * @since 5.6\n\t */\n\tpublic void setAuthenticationRequestRepository(\n\t\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {\n\t\tAssert.notNull(authenticationRequestRepository, \"authenticationRequestRepository cannot be null\");\n\t\tthis.authenticationRequestRepository = authenticationRequestRepository;\n\t\tsetAuthenticationRequestRepositoryIntoAuthenticationConverter(authenticationRequestRepository);\n\t}\n\n\tprivate void setAuthenticationRequestRepositoryIntoAuthenticationConverter(\n\t\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {\n\t\tif (this.authenticationConverter instanceof Saml2AuthenticationTokenConverter authenticationTokenConverter) {\n\t\t\tauthenticationTokenConverter.setAuthenticationRequestRepository(authenticationRequestRepository);\n\t\t}\n\t}\n\n\tprivate void setDetails(HttpServletRequest request, Authentication authentication) {\n\t\tif (authentication.getDetails() != null) {\n\t\t\treturn;\n\t\t}\n\t\tif (authentication instanceof AbstractAuthenticationToken token) {\n\t\t\tObject details = this.authenticationDetailsSource.buildDetails(request);\n\t\t\ttoken.setDetails(details);\n\t\t}\n\t}\n\n\t/**\n\t * Indicate whether to continue with the rest of the filter chain in the event that no\n\t * relying party registration is found. This is {@code false} by default, meaning that\n\t * it will throw an exception.\n\t * @param continueChain whether to continue\n\t * @since 6.5\n\t */\n\tpublic void setContinueChainWhenNoRelyingPartyRegistrationFound(boolean continueChain) {\n\t\tthis.continueChainWhenNoRelyingPartyRegistrationFound = continueChain;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.config.ConfigurationService;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.XMLObjectBuilderFactory;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistry;\nimport org.opensaml.saml.common.AbstractSAMLObjectBuilder;\nimport org.opensaml.saml.common.SAMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.SessionIndex;\nimport org.opensaml.saml.saml2.core.impl.IssuerBuilder;\nimport org.opensaml.saml.saml2.core.impl.LogoutRequestBuilder;\nimport org.opensaml.saml.saml2.core.impl.NameIDBuilder;\nimport org.opensaml.saml.saml2.core.impl.SessionIndexBuilder;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.util.Assert;\n\n/**\n * For internal use only. Intended for consolidating common behavior related to minting a\n * SAML 2.0 Logout Request.\n */\nfinal class BaseOpenSamlLogoutRequestResolver implements Saml2LogoutRequestResolver {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final OpenSamlOperations saml;\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\tprivate final IssuerBuilder issuerBuilder;\n\n\tprivate final NameIDBuilder nameIdBuilder;\n\n\tprivate final SessionIndexBuilder sessionIndexBuilder;\n\n\tprivate final LogoutRequestBuilder logoutRequestBuilder;\n\n\tprivate final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;\n\n\tprivate Converter<HttpServletRequest, String> relayStateResolver = (request) -> UUID.randomUUID().toString();\n\n\tprivate Consumer<LogoutRequestParameters> parametersConsumer = (parameters) -> {\n\t};\n\n\t/**\n\t * Construct a {@link BaseOpenSamlLogoutRequestResolver}\n\t */\n\tBaseOpenSamlLogoutRequestResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver,\n\t\t\tOpenSamlOperations saml) {\n\t\tthis.relyingPartyRegistrationResolver = relyingPartyRegistrationResolver;\n\t\tthis.saml = saml;\n\t\tXMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class);\n\t\tAssert.notNull(registry, \"XMLObjectProviderRegistry must be configured\");\n\t\tXMLObjectBuilderFactory builderFactory = registry.getBuilderFactory();\n\t\tthis.logoutRequestBuilder = builder(builderFactory.ensureBuilder(LogoutRequest.DEFAULT_ELEMENT_NAME));\n\t\tthis.issuerBuilder = builder(builderFactory.ensureBuilder(Issuer.DEFAULT_ELEMENT_NAME));\n\t\tthis.nameIdBuilder = builder(builderFactory.ensureBuilder(NameID.DEFAULT_ELEMENT_NAME));\n\t\tthis.sessionIndexBuilder = builder(builderFactory.ensureBuilder(SessionIndex.DEFAULT_ELEMENT_NAME));\n\t}\n\n\tprivate static <T extends SAMLObject, B extends AbstractSAMLObjectBuilder<T>> B builder(\n\t\t\tXMLObjectBuilder<T> builder) {\n\t\treturn (B) builder;\n\t}\n\n\tvoid setClock(Clock clock) {\n\t\tthis.clock = clock;\n\t}\n\n\tvoid setRelayStateResolver(Converter<HttpServletRequest, String> relayStateResolver) {\n\t\tthis.relayStateResolver = relayStateResolver;\n\t}\n\n\tvoid setParametersConsumer(Consumer<LogoutRequestParameters> parametersConsumer) {\n\t\tthis.parametersConsumer = parametersConsumer;\n\t}\n\n\t/**\n\t * Prepare to create, sign, and serialize a SAML 2.0 Logout Request.\n\t *\n\t * By default, includes a {@code NameID} based on the {@link Authentication} instance\n\t * as well as the {@code Destination} and {@code Issuer} based on the\n\t * {@link RelyingPartyRegistration} derived from the {@link Authentication}.\n\t * @param request the HTTP request\n\t * @param authentication the current user\n\t * @return a signed and serialized SAML 2.0 Logout Request\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutRequest resolve(HttpServletRequest request, Authentication authentication) {\n\t\tString registrationId = getRegistrationId(authentication);\n\t\tRelyingPartyRegistration registration = this.relyingPartyRegistrationResolver.resolve(request, registrationId);\n\t\tif (registration == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation() == null) {\n\t\t\treturn null;\n\t\t}\n\t\tUriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration);\n\t\tString entityId = uriResolver.resolve(registration.getEntityId());\n\t\tLogoutRequest logoutRequest = this.logoutRequestBuilder.buildObject();\n\t\tlogoutRequest.setDestination(registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation());\n\t\tIssuer issuer = this.issuerBuilder.buildObject();\n\t\tissuer.setValue(entityId);\n\t\tlogoutRequest.setIssuer(issuer);\n\t\tNameID nameId = this.nameIdBuilder.buildObject();\n\t\tlogoutRequest.setNameID(nameId);\n\t\tif (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor info) {\n\t\t\tnameId.setValue(info.getNameId());\n\t\t}\n\t\telse {\n\t\t\tnameId.setValue(authentication.getName());\n\t\t}\n\t\tif (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor info) {\n\t\t\tfor (String index : info.getSessionIndexes()) {\n\t\t\t\tSessionIndex sessionIndex = this.sessionIndexBuilder.buildObject();\n\t\t\t\tsessionIndex.setValue(index);\n\t\t\t\tlogoutRequest.getSessionIndexes().add(sessionIndex);\n\t\t\t}\n\t\t}\n\t\telse if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal info) {\n\t\t\tfor (String index : info.getSessionIndexes()) {\n\t\t\t\tSessionIndex sessionIndex = this.sessionIndexBuilder.buildObject();\n\t\t\t\tsessionIndex.setValue(index);\n\t\t\t\tlogoutRequest.getSessionIndexes().add(sessionIndex);\n\t\t\t}\n\t\t}\n\t\tlogoutRequest.setIssueInstant(Instant.now(this.clock));\n\t\tthis.parametersConsumer\n\t\t\t.accept(new LogoutRequestParameters(request, registration, authentication, logoutRequest));\n\t\tif (logoutRequest.getID() == null) {\n\t\t\tlogoutRequest.setID(\"LR\" + UUID.randomUUID());\n\t\t}\n\t\tString relayState = this.relayStateResolver.convert(request);\n\t\tSaml2LogoutRequest.Builder result = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.id(Objects.requireNonNull(logoutRequest.getID()));\n\t\tif (registration.getAssertingPartyMetadata().getSingleLogoutServiceBinding() == Saml2MessageBinding.POST) {\n\t\t\tString xml = serialize(this.saml.withSigningKeys(registration.getSigningX509Credentials())\n\t\t\t\t.algorithms(registration.getAssertingPartyMetadata().getSigningAlgorithms())\n\t\t\t\t.sign(logoutRequest));\n\t\t\tString samlRequest = Saml2Utils.withDecoded(xml).encode();\n\t\t\treturn result.samlRequest(samlRequest).relayState(relayState).build();\n\t\t}\n\t\telse {\n\t\t\tString xml = serialize(logoutRequest);\n\t\t\tString deflatedAndEncoded = Saml2Utils.withDecoded(xml).deflate(true).encode();\n\t\t\tresult.samlRequest(deflatedAndEncoded);\n\t\t\tMap<String, String> signingParameters = new HashMap<>();\n\t\t\tsigningParameters.put(Saml2ParameterNames.SAML_REQUEST, deflatedAndEncoded);\n\t\t\tsigningParameters.put(Saml2ParameterNames.RELAY_STATE, relayState);\n\t\t\tMap<String, String> query = this.saml.withSigningKeys(registration.getSigningX509Credentials())\n\t\t\t\t.algorithms(registration.getAssertingPartyMetadata().getSigningAlgorithms())\n\t\t\t\t.sign(signingParameters);\n\t\t\treturn result.parameters((params) -> params.putAll(query)).build();\n\t\t}\n\t}\n\n\tprivate @Nullable String getRegistrationId(Authentication authentication) {\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Attempting to resolve registrationId from \" + authentication);\n\t\t}\n\t\tif (authentication instanceof Saml2AssertionAuthentication response) {\n\t\t\treturn response.getRelyingPartyRegistrationId();\n\t\t}\n\t\tif (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {\n\t\t\treturn principal.getRelyingPartyRegistrationId();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String serialize(LogoutRequest logoutRequest) {\n\t\treturn this.saml.serialize(logoutRequest).serialize();\n\t}\n\n\tstatic final class LogoutRequestParameters {\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tprivate final Authentication authentication;\n\n\t\tprivate final LogoutRequest logoutRequest;\n\n\t\tLogoutRequestParameters(HttpServletRequest request, RelyingPartyRegistration registration,\n\t\t\t\tAuthentication authentication, LogoutRequest logoutRequest) {\n\t\t\tthis.request = request;\n\t\t\tthis.registration = registration;\n\t\t\tthis.authentication = authentication;\n\t\t\tthis.logoutRequest = logoutRequest;\n\t\t}\n\n\t\tHttpServletRequest getRequest() {\n\t\t\treturn this.request;\n\t\t}\n\n\t\tRelyingPartyRegistration getRelyingPartyRegistration() {\n\t\t\treturn this.registration;\n\t\t}\n\n\t\tAuthentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\tLogoutRequest getLogoutRequest() {\n\t\t\treturn this.logoutRequest;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutRequestValidatorParametersResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.schema.XSString;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * An OpenSAML-based implementation of\n * {@link Saml2LogoutRequestValidatorParametersResolver}\n */\nfinal class BaseOpenSamlLogoutRequestValidatorParametersResolver\n\t\timplements Saml2LogoutRequestValidatorParametersResolver {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final OpenSamlOperations saml;\n\n\tprivate final RelyingPartyRegistrationRepository registrations;\n\n\tprivate RequestMatcher requestMatcher = new OrRequestMatcher(pathPattern(\"/logout/saml2/slo/{registrationId}\"),\n\t\t\tpathPattern(\"/logout/saml2/slo\"));\n\n\t/**\n\t * Constructs a {@link BaseOpenSamlLogoutRequestValidatorParametersResolver}\n\t */\n\tBaseOpenSamlLogoutRequestValidatorParametersResolver(OpenSamlOperations saml,\n\t\t\tRelyingPartyRegistrationRepository registrations) {\n\t\tAssert.notNull(registrations, \"relyingPartyRegistrationRepository cannot be null\");\n\t\tthis.saml = saml;\n\t\tthis.registrations = registrations;\n\t}\n\n\t/**\n\t * Construct the parameters necessary for validating an asserting party's\n\t * {@code <saml2:LogoutRequest>} based on the given {@link HttpServletRequest}\n\t *\n\t * <p>\n\t * Uses the configured {@link RequestMatcher} to identify the processing request,\n\t * including looking for any indicated {@code registrationId}.\n\t *\n\t * <p>\n\t * If a {@code registrationId} is found in the request, it will attempt to use that,\n\t * erroring if no {@link RelyingPartyRegistration} is found.\n\t *\n\t * <p>\n\t * If no {@code registrationId} is found in the request, it will look for a currently\n\t * logged-in user and use the associated {@code registrationId}.\n\t *\n\t * <p>\n\t * In the event that neither the URL nor any logged in user could determine a\n\t * {@code registrationId}, this code then will try and derive a\n\t * {@link RelyingPartyRegistration} given the {@code <saml2:LogoutRequest>}'s\n\t * {@code Issuer} value.\n\t * @param request the HTTP request\n\t * @return a {@link Saml2LogoutRequestValidatorParameters} instance, or {@code null}\n\t * if one could not be resolved\n\t * @throws Saml2AuthenticationException if the {@link RequestMatcher} specifies a\n\t * non-existent {@code registrationId}\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutRequestValidatorParameters resolve(HttpServletRequest request,\n\t\t\t@Nullable Authentication authentication) {\n\t\tif (request.getParameter(Saml2ParameterNames.SAML_REQUEST) == null) {\n\t\t\treturn null;\n\t\t}\n\t\tRequestMatcher.MatchResult result = this.requestMatcher.matcher(request);\n\t\tif (!result.isMatch()) {\n\t\t\treturn null;\n\t\t}\n\t\tString registrationId = getRegistrationId(result, authentication);\n\t\tif (registrationId == null) {\n\t\t\treturn logoutRequestByEntityId(request, authentication);\n\t\t}\n\t\treturn logoutRequestById(request, authentication, registrationId);\n\t}\n\n\t/**\n\t * The request matcher to use to identify a request to process a\n\t * {@code <saml2:LogoutRequest>}. By default, checks for {@code /logout/saml2/slo} and\n\t * {@code /logout/saml2/slo/{registrationId}}.\n\t *\n\t * <p>\n\t * Generally speaking, the URL does not need to have a {@code registrationId} in it\n\t * since either it can be looked up from the active logged in user or it can be\n\t * derived through the {@code Issuer} in the {@code <saml2:LogoutRequest>}.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tvoid setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\tprivate @Nullable String getRegistrationId(RequestMatcher.MatchResult result,\n\t\t\t@Nullable Authentication authentication) {\n\t\tString registrationId = result.getVariables().get(\"registrationId\");\n\t\tif (registrationId != null) {\n\t\t\treturn registrationId;\n\t\t}\n\t\tif (authentication == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (authentication instanceof Saml2AssertionAuthentication saml2) {\n\t\t\treturn saml2.getRelyingPartyRegistrationId();\n\t\t}\n\t\tif (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal saml2) {\n\t\t\treturn saml2.getRelyingPartyRegistrationId();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate @Nullable Saml2LogoutRequestValidatorParameters logoutRequestById(HttpServletRequest request,\n\t\t\t@Nullable Authentication authentication, String registrationId) {\n\t\tRelyingPartyRegistration registration = this.registrations.findByRegistrationId(registrationId);\n\t\tif (registration == null) {\n\t\t\tthrow new Saml2AuthenticationException(\n\t\t\t\t\tSaml2Error.relyingPartyRegistrationNotFound(\"registration not found\"));\n\t\t}\n\t\treturn logoutRequestByRegistration(request, registration, authentication);\n\t}\n\n\tprivate @Nullable Saml2LogoutRequestValidatorParameters logoutRequestByEntityId(HttpServletRequest request,\n\t\t\t@Nullable Authentication authentication) {\n\t\tString serialized = request.getParameter(Saml2ParameterNames.SAML_REQUEST);\n\t\tLogoutRequest logoutRequest = this.saml.deserialize(\n\t\t\t\tSaml2Utils.withEncoded(serialized).inflate(HttpMethod.GET.matches(request.getMethod())).decode());\n\t\tIssuer issuer = logoutRequest.getIssuer();\n\t\tAssert.notNull(issuer, \"LogoutRequest#Issuer cannot be null\");\n\t\tRelyingPartyRegistration registration = this.registrations.findUniqueByAssertingPartyEntityId(getValue(issuer));\n\t\treturn logoutRequestByRegistration(request, registration, authentication);\n\t}\n\n\tprivate @Nullable Saml2LogoutRequestValidatorParameters logoutRequestByRegistration(HttpServletRequest request,\n\t\t\t@Nullable RelyingPartyRegistration registration, @Nullable Authentication authentication) {\n\t\tif (registration == null) {\n\t\t\treturn null;\n\t\t}\n\t\tSaml2MessageBinding saml2MessageBinding = Saml2MessageBindingUtils.resolveBinding(request);\n\t\tregistration = fromRequest(request, registration);\n\t\tString serialized = request.getParameter(Saml2ParameterNames.SAML_REQUEST);\n\t\tString location = registration.getSingleLogoutServiceLocation();\n\t\tAssert.notNull(location, \"logoutServiceLocation must be configured\");\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(serialized)\n\t\t\t.relayState(request.getParameter(Saml2ParameterNames.RELAY_STATE))\n\t\t\t.binding(saml2MessageBinding)\n\t\t\t.location(location)\n\t\t\t.parameters((params) -> params.put(Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\trequest.getParameter(Saml2ParameterNames.SIG_ALG)))\n\t\t\t.parameters((params) -> params.put(Saml2ParameterNames.SIGNATURE,\n\t\t\t\t\trequest.getParameter(Saml2ParameterNames.SIGNATURE)))\n\t\t\t.parametersQuery((params) -> request.getQueryString())\n\t\t\t.build();\n\t\treturn new Saml2LogoutRequestValidatorParameters(logoutRequest, registration, authentication);\n\t}\n\n\tprivate RelyingPartyRegistration fromRequest(HttpServletRequest request, RelyingPartyRegistration registration) {\n\t\tRelyingPartyRegistrationPlaceholderResolvers.UriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers\n\t\t\t.uriResolver(request, registration);\n\t\tString entityId = Objects.requireNonNull(uriResolver.resolve(registration.getEntityId()));\n\t\tString logoutLocation = uriResolver.resolve(registration.getSingleLogoutServiceLocation());\n\t\tString logoutResponseLocation = uriResolver.resolve(registration.getSingleLogoutServiceResponseLocation());\n\t\treturn registration.mutate()\n\t\t\t.entityId(entityId)\n\t\t\t.singleLogoutServiceLocation(logoutLocation)\n\t\t\t.singleLogoutServiceResponseLocation(logoutResponseLocation)\n\t\t\t.build();\n\t}\n\n\tprivate String getValue(XSString element) {\n\t\tString value = element.getValue();\n\t\tAssert.notNull(value, \"required elements must have a value\");\n\t\treturn value;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutResponseResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.config.ConfigurationService;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.XMLObjectBuilderFactory;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistry;\nimport org.opensaml.core.xml.schema.XSString;\nimport org.opensaml.saml.common.AbstractSAMLObjectBuilder;\nimport org.opensaml.saml.common.SAMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.LogoutResponse;\nimport org.opensaml.saml.saml2.core.Status;\nimport org.opensaml.saml.saml2.core.StatusCode;\nimport org.opensaml.saml.saml2.core.impl.IssuerBuilder;\nimport org.opensaml.saml.saml2.core.impl.LogoutResponseBuilder;\nimport org.opensaml.saml.saml2.core.impl.StatusBuilder;\nimport org.opensaml.saml.saml2.core.impl.StatusCodeBuilder;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.util.Assert;\n\n/**\n * For internal use only. Intended for consolidating common behavior related to minting a\n * SAML 2.0 Logout Response.\n */\nfinal class BaseOpenSamlLogoutResponseResolver implements Saml2LogoutResponseResolver {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final LogoutResponseBuilder logoutResponseBuilder;\n\n\tprivate final IssuerBuilder issuerBuilder;\n\n\tprivate final StatusBuilder statusBuilder;\n\n\tprivate final StatusCodeBuilder statusCodeBuilder;\n\n\tprivate final OpenSamlOperations saml;\n\n\tprivate final @Nullable RelyingPartyRegistrationRepository registrations;\n\n\tprivate final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;\n\n\tprivate Clock clock = Clock.systemUTC();\n\n\tprivate Consumer<LogoutResponseParameters> parametersConsumer = (parameters) -> {\n\t};\n\n\t/**\n\t * Construct a {@link BaseOpenSamlLogoutResponseResolver}\n\t */\n\tBaseOpenSamlLogoutResponseResolver(@Nullable RelyingPartyRegistrationRepository registrations,\n\t\t\tRelyingPartyRegistrationResolver relyingPartyRegistrationResolver, OpenSamlOperations saml) {\n\t\tthis.saml = saml;\n\t\tthis.registrations = registrations;\n\t\tthis.relyingPartyRegistrationResolver = relyingPartyRegistrationResolver;\n\t\tXMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class);\n\t\tAssert.notNull(registry, \"XMLObjectProviderRegistry cannot be null\");\n\t\tXMLObjectBuilderFactory builderFactory = registry.getBuilderFactory();\n\t\tthis.logoutResponseBuilder = builder(builderFactory.ensureBuilder(LogoutResponse.DEFAULT_ELEMENT_NAME));\n\t\tthis.issuerBuilder = builder(builderFactory.ensureBuilder(Issuer.DEFAULT_ELEMENT_NAME));\n\t\tthis.statusBuilder = builder(builderFactory.ensureBuilder(Status.DEFAULT_ELEMENT_NAME));\n\t\tthis.statusCodeBuilder = builder(builderFactory.ensureBuilder(StatusCode.DEFAULT_ELEMENT_NAME));\n\t}\n\n\tprivate static <T extends SAMLObject, B extends AbstractSAMLObjectBuilder<T>> B builder(\n\t\t\tXMLObjectBuilder<T> builder) {\n\t\treturn (B) builder;\n\t}\n\n\t/**\n\t * Prepare to create, sign, and serialize a SAML 2.0 Logout Response.\n\t *\n\t * By default, includes a {@code RelayState} based on the {@link HttpServletRequest}\n\t * as well as the {@code Destination} and {@code Issuer} based on the\n\t * {@link RelyingPartyRegistration} derived from the {@link Authentication}. The\n\t * logout response is also marked as {@code SUCCESS}.\n\t * @param request the HTTP request\n\t * @param authentication the current user\n\t * @return a signed and serialized SAML 2.0 Logout Response\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication) {\n\t\treturn resolve(request, authentication, StatusCode.SUCCESS);\n\t}\n\n\t@Override\n\tpublic @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication,\n\t\t\tSaml2AuthenticationException authenticationException) {\n\t\treturn resolve(request, authentication, getSamlStatus(authenticationException));\n\t}\n\n\tprivate @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication,\n\t\t\tString statusCode) {\n\t\tLogoutRequest logoutRequest = this.saml.deserialize(extractSamlRequest(request));\n\t\tString registrationId = getRegistrationId(authentication);\n\t\tRelyingPartyRegistration registration = this.relyingPartyRegistrationResolver.resolve(request, registrationId);\n\t\tif (registration == null && this.registrations != null) {\n\t\t\tIssuer issuer = logoutRequest.getIssuer();\n\t\t\tAssert.notNull(issuer, \"LogoutRequest#Issuer cannot be null\");\n\t\t\tregistration = this.registrations.findUniqueByAssertingPartyEntityId(getValue(issuer));\n\t\t}\n\t\tif (registration == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (registration.getAssertingPartyMetadata().getSingleLogoutServiceResponseLocation() == null) {\n\t\t\treturn null;\n\t\t}\n\t\tUriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration);\n\t\tString entityId = uriResolver.resolve(registration.getEntityId());\n\t\tLogoutResponse logoutResponse = this.logoutResponseBuilder.buildObject();\n\t\tlogoutResponse\n\t\t\t.setDestination(registration.getAssertingPartyMetadata().getSingleLogoutServiceResponseLocation());\n\t\tIssuer issuer = this.issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);\n\t\tissuer.setValue(entityId);\n\t\tlogoutResponse.setIssuer(issuer);\n\t\tStatusCode code = this.statusCodeBuilder.buildObject(StatusCode.DEFAULT_ELEMENT_NAME);\n\t\tcode.setValue(statusCode);\n\t\tStatus status = this.statusBuilder.buildObject(Status.DEFAULT_ELEMENT_NAME);\n\t\tstatus.setStatusCode(code);\n\t\tlogoutResponse.setStatus(status);\n\t\tlogoutResponse.setInResponseTo(logoutRequest.getID());\n\t\tif (logoutResponse.getID() == null) {\n\t\t\tlogoutResponse.setID(\"LR\" + UUID.randomUUID());\n\t\t}\n\t\tlogoutResponse.setIssueInstant(Instant.now(this.clock));\n\t\tthis.parametersConsumer\n\t\t\t.accept(new LogoutResponseParameters(request, registration, authentication, logoutRequest));\n\t\tString relayState = request.getParameter(Saml2ParameterNames.RELAY_STATE);\n\t\tSaml2LogoutResponse.Builder result = Saml2LogoutResponse.withRelyingPartyRegistration(registration);\n\t\tif (registration.getAssertingPartyMetadata().getSingleLogoutServiceBinding() == Saml2MessageBinding.POST) {\n\t\t\tString xml = serialize(this.saml.withSigningKeys(registration.getSigningX509Credentials())\n\t\t\t\t.algorithms(registration.getAssertingPartyMetadata().getSigningAlgorithms())\n\t\t\t\t.sign(logoutResponse));\n\t\t\tString samlResponse = Saml2Utils.withDecoded(xml).encode();\n\t\t\tresult.samlResponse(samlResponse);\n\t\t\tif (relayState != null) {\n\t\t\t\tresult.relayState(relayState);\n\t\t\t}\n\t\t\treturn result.build();\n\t\t}\n\t\telse {\n\t\t\tString xml = serialize(logoutResponse);\n\t\t\tString deflatedAndEncoded = Saml2Utils.withDecoded(xml).deflate(true).encode();\n\t\t\tresult.samlResponse(deflatedAndEncoded);\n\t\t\tMap<String, String> signingParameters = new HashMap<>();\n\t\t\tsigningParameters.put(Saml2ParameterNames.SAML_RESPONSE, deflatedAndEncoded);\n\t\t\tif (relayState != null) {\n\t\t\t\tsigningParameters.put(Saml2ParameterNames.RELAY_STATE, relayState);\n\t\t\t}\n\t\t\tMap<String, String> parameters = this.saml.withSigningKeys(registration.getSigningX509Credentials())\n\t\t\t\t.algorithms(registration.getAssertingPartyMetadata().getSigningAlgorithms())\n\t\t\t\t.sign(signingParameters);\n\t\t\treturn result.parameters((params) -> params.putAll(parameters)).build();\n\t\t}\n\t}\n\n\tString getValue(XSString object) {\n\t\tString value = object.getValue();\n\t\tAssert.notNull(value, \"required elements must have a value\");\n\t\treturn value;\n\t}\n\n\tvoid setClock(Clock clock) {\n\t\tthis.clock = clock;\n\t}\n\n\tvoid setParametersConsumer(Consumer<LogoutResponseParameters> parametersConsumer) {\n\t\tthis.parametersConsumer = parametersConsumer;\n\t}\n\n\tprivate @Nullable String getRegistrationId(@Nullable Authentication authentication) {\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Attempting to resolve registrationId from \" + authentication);\n\t\t}\n\t\tif (authentication == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (authentication instanceof Saml2AssertionAuthentication saml2) {\n\t\t\treturn saml2.getRelyingPartyRegistrationId();\n\t\t}\n\t\tif (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal saml2) {\n\t\t\treturn saml2.getRelyingPartyRegistrationId();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate String extractSamlRequest(HttpServletRequest request) {\n\t\treturn Saml2Utils.withEncoded(request.getParameter(Saml2ParameterNames.SAML_REQUEST))\n\t\t\t.inflate(Saml2MessageBindingUtils.isHttpRedirectBinding(request))\n\t\t\t.decode();\n\t}\n\n\tprivate String serialize(LogoutResponse logoutResponse) {\n\t\treturn this.saml.serialize(logoutResponse).serialize();\n\t}\n\n\tprivate String getSamlStatus(Saml2AuthenticationException exception) {\n\t\tSaml2Error saml2Error = exception.getSaml2Error();\n\t\treturn switch (saml2Error.getErrorCode()) {\n\t\t\tcase Saml2ErrorCodes.INVALID_DESTINATION -> StatusCode.REQUEST_DENIED;\n\t\t\tcase Saml2ErrorCodes.INVALID_REQUEST -> StatusCode.REQUESTER;\n\t\t\tdefault -> StatusCode.RESPONDER;\n\t\t};\n\t}\n\n\tstatic final class LogoutResponseParameters {\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tprivate final @Nullable Authentication authentication;\n\n\t\tprivate final LogoutRequest logoutRequest;\n\n\t\tLogoutResponseParameters(HttpServletRequest request, RelyingPartyRegistration registration,\n\t\t\t\t@Nullable Authentication authentication, LogoutRequest logoutRequest) {\n\t\t\tthis.request = request;\n\t\t\tthis.registration = registration;\n\t\t\tthis.authentication = authentication;\n\t\t\tthis.logoutRequest = logoutRequest;\n\t\t}\n\n\t\tHttpServletRequest getRequest() {\n\t\t\treturn this.request;\n\t\t}\n\n\t\tRelyingPartyRegistration getRelyingPartyRegistration() {\n\t\t\treturn this.registration;\n\t\t}\n\n\t\t@Nullable Authentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\tLogoutRequest getLogoutRequest() {\n\t\t\treturn this.logoutRequest;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/HttpSessionLogoutRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.security.MessageDigest;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of an {@link Saml2LogoutRequestRepository} that stores\n * {@link Saml2LogoutRequest} in the {@code HttpSession}.\n *\n * @author Josh Cummings\n * @since 5.6\n * @see Saml2LogoutRequestRepository\n * @see Saml2LogoutRequest\n */\npublic final class HttpSessionLogoutRequestRepository implements Saml2LogoutRequestRepository {\n\n\tprivate static final String DEFAULT_LOGOUT_REQUEST_ATTR_NAME = HttpSessionLogoutRequestRepository.class.getName()\n\t\t\t+ \".LOGOUT_REQUEST\";\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutRequest loadLogoutRequest(HttpServletRequest request) {\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session == null) {\n\t\t\treturn null;\n\t\t}\n\t\tSaml2LogoutRequest logoutRequest = (Saml2LogoutRequest) session.getAttribute(DEFAULT_LOGOUT_REQUEST_ATTR_NAME);\n\t\tif (stateParameterEquals(request, logoutRequest)) {\n\t\t\treturn logoutRequest;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void saveLogoutRequest(@Nullable Saml2LogoutRequest logoutRequest, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tAssert.notNull(response, \"response cannot be null\");\n\t\tif (logoutRequest == null) {\n\t\t\trequest.getSession().removeAttribute(DEFAULT_LOGOUT_REQUEST_ATTR_NAME);\n\t\t\treturn;\n\t\t}\n\t\tString state = logoutRequest.getRelayState();\n\t\tAssert.hasText(state, \"logoutRequest.state cannot be empty\");\n\t\trequest.getSession().setAttribute(DEFAULT_LOGOUT_REQUEST_ATTR_NAME, logoutRequest);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutRequest removeLogoutRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tAssert.notNull(response, \"response cannot be null\");\n\t\tSaml2LogoutRequest logoutRequest = loadLogoutRequest(request);\n\t\tif (logoutRequest == null) {\n\t\t\treturn null;\n\t\t}\n\t\trequest.getSession().removeAttribute(DEFAULT_LOGOUT_REQUEST_ATTR_NAME);\n\t\treturn logoutRequest;\n\t}\n\n\tprivate @Nullable String getStateParameter(HttpServletRequest request) {\n\t\treturn request.getParameter(Saml2ParameterNames.RELAY_STATE);\n\t}\n\n\tprivate boolean stateParameterEquals(HttpServletRequest request, @Nullable Saml2LogoutRequest logoutRequest) {\n\t\tString stateParameter = getStateParameter(request);\n\t\tif (stateParameter == null || logoutRequest == null) {\n\t\t\treturn false;\n\t\t}\n\t\tString relayState = logoutRequest.getRelayState();\n\t\tif (relayState == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn MessageDigest.isEqual(Utf8.encode(stateParameter), Utf8.encode(relayState));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\n\ninterface OpenSamlOperations {\n\n\t<T extends XMLObject> T build(QName elementName);\n\n\t<T extends XMLObject> T deserialize(String serialized);\n\n\t<T extends XMLObject> T deserialize(InputStream serialized);\n\n\tSerializationConfigurer<?> serialize(XMLObject object);\n\n\tSerializationConfigurer<?> serialize(Element element);\n\n\tSignatureConfigurer<?> withSigningKeys(Collection<Saml2X509Credential> credentials);\n\n\tVerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials);\n\n\tDecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials);\n\n\tinterface SerializationConfigurer<B extends SerializationConfigurer<B>> {\n\n\t\tB prettyPrint(boolean pretty);\n\n\t\tString serialize();\n\n\t}\n\n\tinterface SignatureConfigurer<B extends SignatureConfigurer<B>> {\n\n\t\tB algorithms(List<String> algs);\n\n\t\t<O extends SignableXMLObject> O sign(O object);\n\n\t\tMap<String, String> sign(Map<String, String> params);\n\n\t}\n\n\tinterface VerificationConfigurer {\n\n\t\tVerificationConfigurer entityId(String entityId);\n\n\t\tCollection<Saml2Error> verify(SignableXMLObject signable);\n\n\t\tCollection<Saml2Error> verify(VerificationConfigurer.RedirectParameters parameters);\n\n\t\tfinal class RedirectParameters {\n\n\t\t\tprivate final String id;\n\n\t\t\tprivate final Issuer issuer;\n\n\t\t\tprivate final String algorithm;\n\n\t\t\tprivate final byte @Nullable [] signature;\n\n\t\t\tprivate final byte[] content;\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"SAML request's ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"SAML request's Issuer cannot be null\");\n\t\t\t\tthis.id = request.getID();\n\t\t\t\tthis.issuer = request.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_REQUEST, relayState, queryParams);\n\t\t\t}\n\n\t\t\tRedirectParameters(Map<String, String> parameters, String parametersQuery, StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"SAML response's ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"SAML response's Issuer cannot be null\");\n\t\t\t\tthis.id = response.getID();\n\t\t\t\tthis.issuer = response.getIssuer();\n\t\t\t\tthis.algorithm = Objects.requireNonNull(parameters.get(Saml2ParameterNames.SIG_ALG),\n\t\t\t\t\t\t\"sigAlg parameter cannot be null\");\n\t\t\t\tif (parameters.get(Saml2ParameterNames.SIGNATURE) != null) {\n\t\t\t\t\tthis.signature = Saml2Utils.samlDecode(parameters.get(Saml2ParameterNames.SIGNATURE));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.signature = null;\n\t\t\t\t}\n\t\t\t\tMap<String, String> queryParams = UriComponentsBuilder.newInstance()\n\t\t\t\t\t.query(parametersQuery)\n\t\t\t\t\t.build(true)\n\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t.toSingleValueMap();\n\t\t\t\tString relayState = parameters.get(Saml2ParameterNames.RELAY_STATE);\n\t\t\t\tthis.content = getContent(Saml2ParameterNames.SAML_RESPONSE, relayState, queryParams);\n\t\t\t}\n\n\t\t\tstatic byte[] getContent(String samlObject, @Nullable String relayState,\n\t\t\t\t\tfinal Map<String, String> queryParams) {\n\t\t\t\tif (Objects.nonNull(relayState)) {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s&%s=%s\", samlObject, queryParams.get(samlObject),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.RELAY_STATE, queryParams.get(Saml2ParameterNames.RELAY_STATE),\n\t\t\t\t\t\t\t\tSaml2ParameterNames.SIG_ALG, queryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn String\n\t\t\t\t\t\t.format(\"%s=%s&%s=%s\", samlObject, queryParams.get(samlObject), Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\t\t\tqueryParams.get(Saml2ParameterNames.SIG_ALG))\n\t\t\t\t\t\t.getBytes(StandardCharsets.UTF_8);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tString getId() {\n\t\t\t\treturn this.id;\n\t\t\t}\n\n\t\t\tIssuer getIssuer() {\n\t\t\t\treturn this.issuer;\n\t\t\t}\n\n\t\t\tbyte[] getContent() {\n\t\t\t\treturn this.content;\n\t\t\t}\n\n\t\t\tString getAlgorithm() {\n\t\t\t\treturn this.algorithm;\n\t\t\t}\n\n\t\t\tbyte @Nullable [] getSignature() {\n\t\t\t\treturn this.signature;\n\t\t\t}\n\n\t\t\tboolean hasSignature() {\n\t\t\t\treturn this.signature != null;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface DecryptionConfigurer {\n\n\t\tvoid decrypt(XMLObject object);\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutValidatorResult;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.logout.CompositeLogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.HtmlUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A filter for handling logout requests in the form of a &lt;saml2:LogoutRequest&gt; sent\n * from the asserting party.\n *\n * @author Josh Cummings\n * @since 5.6\n * @see Saml2LogoutRequestValidator\n */\npublic final class Saml2LogoutRequestFilter extends OncePerRequestFilter {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final Saml2LogoutRequestValidatorParametersResolver logoutRequestResolver;\n\n\tprivate final Saml2LogoutRequestValidator logoutRequestValidator;\n\n\tprivate final Saml2LogoutResponseResolver logoutResponseResolver;\n\n\tprivate final LogoutHandler handler;\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tpublic Saml2LogoutRequestFilter(Saml2LogoutRequestValidatorParametersResolver logoutRequestResolver,\n\t\t\tSaml2LogoutRequestValidator logoutRequestValidator, Saml2LogoutResponseResolver logoutResponseResolver,\n\t\t\tLogoutHandler... handlers) {\n\t\tthis.logoutRequestResolver = logoutRequestResolver;\n\t\tthis.logoutRequestValidator = logoutRequestValidator;\n\t\tthis.logoutResponseResolver = logoutResponseResolver;\n\t\tthis.handler = new CompositeLogoutHandler(handlers);\n\t}\n\n\t/**\n\t * Constructs a {@link Saml2LogoutResponseFilter} for accepting SAML 2.0 Logout\n\t * Requests from the asserting party\n\t * @param relyingPartyRegistrationResolver the strategy for resolving a\n\t * {@link RelyingPartyRegistration}\n\t * @param logoutRequestValidator the SAML 2.0 Logout Request authenticator\n\t * @param logoutResponseResolver the strategy for creating a SAML 2.0 Logout Response\n\t * @param handlers the actions that perform logout\n\t */\n\tpublic Saml2LogoutRequestFilter(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver,\n\t\t\tSaml2LogoutRequestValidator logoutRequestValidator, Saml2LogoutResponseResolver logoutResponseResolver,\n\t\t\tLogoutHandler... handlers) {\n\t\tthis.logoutRequestResolver = new Saml2AssertingPartyLogoutRequestResolver(relyingPartyRegistrationResolver);\n\t\tthis.logoutRequestValidator = logoutRequestValidator;\n\t\tthis.logoutResponseResolver = logoutResponseResolver;\n\t\tthis.handler = new CompositeLogoutHandler(handlers);\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tSaml2LogoutRequestValidatorParameters parameters;\n\t\ttry {\n\t\t\tparameters = this.logoutRequestResolver.resolve(request, authentication);\n\t\t}\n\t\tcatch (Saml2AuthenticationException ex) {\n\t\t\tthis.logger.trace(\"Did not process logout request since failed to find requested RelyingPartyRegistration\");\n\t\t\tresponse.sendError(HttpServletResponse.SC_BAD_REQUEST);\n\t\t\treturn;\n\t\t}\n\t\tif (parameters == null) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tvalidateLogoutRequest(request, parameters);\n\t\t}\n\t\tcatch (Saml2AuthenticationException ex) {\n\t\t\tSaml2LogoutResponse errorLogoutResponse = this.logoutResponseResolver.resolve(request, authentication, ex);\n\t\t\tif (errorLogoutResponse == null) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\n\t\t\t\t\t\t\"Returning error since no error logout response could be generated: %s\", ex.getSaml2Error()));\n\t\t\t\tresponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsendLogoutResponse(request, response, errorLogoutResponse);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.handler.logout(request, response, authentication);\n\t\tSaml2LogoutResponse logoutResponse = this.logoutResponseResolver.resolve(request, authentication);\n\t\tif (logoutResponse == null) {\n\t\t\tthis.logger.trace(\"Returning error since no logout response generated\");\n\t\t\tresponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\n\t\t\treturn;\n\t\t}\n\t\tsendLogoutResponse(request, response, logoutResponse);\n\t}\n\n\tpublic void setLogoutRequestMatcher(RequestMatcher logoutRequestMatcher) {\n\t\tAssert.notNull(logoutRequestMatcher, \"logoutRequestMatcher cannot be null\");\n\t\tAssert.isInstanceOf(Saml2AssertingPartyLogoutRequestResolver.class, this.logoutRequestResolver,\n\t\t\t\t\"saml2LogoutRequestResolver and logoutRequestMatcher cannot both be set. Please set the request matcher in the saml2LogoutRequestResolver itself.\");\n\t\t((Saml2AssertingPartyLogoutRequestResolver) this.logoutRequestResolver)\n\t\t\t.setLogoutRequestMatcher(logoutRequestMatcher);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tprivate void validateLogoutRequest(HttpServletRequest request, Saml2LogoutRequestValidatorParameters parameters) {\n\t\tRelyingPartyRegistration registration = parameters.getRelyingPartyRegistration();\n\t\tif (registration.getSingleLogoutServiceLocation() == null) {\n\t\t\tthis.logger.trace(\n\t\t\t\t\t\"Did not process logout request since RelyingPartyRegistration has not been configured with a logout request endpoint\");\n\t\t\tthrow new Saml2AuthenticationException(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION,\n\t\t\t\t\t\"RelyingPartyRegistration has not been configured with a logout request endpoint\"));\n\t\t}\n\n\t\tSaml2MessageBinding saml2MessageBinding = Saml2MessageBindingUtils.resolveBinding(request);\n\t\tif (!registration.getSingleLogoutServiceBindings().contains(saml2MessageBinding)) {\n\t\t\tthis.logger.trace(\"Did not process logout request since used incorrect binding\");\n\t\t\tthrow new Saml2AuthenticationException(\n\t\t\t\t\tnew Saml2Error(Saml2ErrorCodes.INVALID_REQUEST, \"Logout request used invalid binding\"));\n\t\t}\n\n\t\tSaml2LogoutValidatorResult result = this.logoutRequestValidator.validate(parameters);\n\t\tif (result.hasErrors()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Failed to validate LogoutRequest: %s\", result.getErrors()));\n\t\t\tthrow new Saml2AuthenticationException(\n\t\t\t\t\tnew Saml2Error(Saml2ErrorCodes.INVALID_REQUEST, \"Failed to validate the logout request\"));\n\t\t}\n\t}\n\n\tprivate void sendLogoutResponse(HttpServletRequest request, HttpServletResponse response,\n\t\t\tSaml2LogoutResponse logoutResponse) throws IOException {\n\t\tif (logoutResponse.getBinding() == Saml2MessageBinding.REDIRECT) {\n\t\t\tdoRedirect(request, response, logoutResponse);\n\t\t}\n\t\telse {\n\t\t\tdoPost(response, logoutResponse);\n\t\t}\n\t}\n\n\tprivate void doRedirect(HttpServletRequest request, HttpServletResponse response,\n\t\t\tSaml2LogoutResponse logoutResponse) throws IOException {\n\t\tString location = logoutResponse.getResponseLocation();\n\t\tString query = logoutResponse.getParametersQuery();\n\t\tAssert.notNull(query, \"logout response must have a parameters query when using redirect binding\");\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location).query(query);\n\t\tthis.redirectStrategy.sendRedirect(request, response, uriBuilder.build(true).toUriString());\n\t}\n\n\tprivate void doPost(HttpServletResponse response, Saml2LogoutResponse logoutResponse) throws IOException {\n\t\tString location = logoutResponse.getResponseLocation();\n\t\tString saml = logoutResponse.getSamlResponse();\n\t\tString relayState = logoutResponse.getRelayState();\n\t\tString html = createSamlPostRequestFormData(location, saml, relayState);\n\t\tresponse.setContentType(MediaType.TEXT_HTML_VALUE);\n\t\tresponse.getWriter().write(html);\n\t}\n\n\tprivate String createSamlPostRequestFormData(String location, String saml, @Nullable String relayState) {\n\t\tStringBuilder html = new StringBuilder();\n\t\thtml.append(\"<!DOCTYPE html>\\n\");\n\t\thtml.append(\"<html>\\n\").append(\"    <head>\\n\");\n\t\thtml.append(\"        <meta http-equiv=\\\"Content-Security-Policy\\\" \")\n\t\t\t.append(\"content=\\\"script-src 'sha256-oZhLbc2kO8b8oaYLrUc7uye1MgVKMyLtPqWR4WtKF+c='\\\">\\n\");\n\t\thtml.append(\"        <meta charset=\\\"utf-8\\\" />\\n\");\n\t\thtml.append(\"    </head>\\n\");\n\t\thtml.append(\"    <body>\\n\");\n\t\thtml.append(\"        <noscript>\\n\");\n\t\thtml.append(\"            <p>\\n\");\n\t\thtml.append(\"                <strong>Note:</strong> Since your browser does not support JavaScript,\\n\");\n\t\thtml.append(\"                you must press the Continue button once to proceed.\\n\");\n\t\thtml.append(\"            </p>\\n\");\n\t\thtml.append(\"        </noscript>\\n\");\n\t\thtml.append(\"        \\n\");\n\t\thtml.append(\"        <form action=\\\"\");\n\t\thtml.append(location);\n\t\thtml.append(\"\\\" method=\\\"post\\\">\\n\");\n\t\thtml.append(\"            <div>\\n\");\n\t\thtml.append(\"                <input type=\\\"hidden\\\" name=\\\"SAMLResponse\\\" value=\\\"\");\n\t\thtml.append(HtmlUtils.htmlEscape(saml));\n\t\thtml.append(\"\\\"/>\\n\");\n\t\tif (StringUtils.hasText(relayState)) {\n\t\t\thtml.append(\"                <input type=\\\"hidden\\\" name=\\\"RelayState\\\" value=\\\"\");\n\t\t\thtml.append(HtmlUtils.htmlEscape(relayState));\n\t\t\thtml.append(\"\\\"/>\\n\");\n\t\t}\n\t\thtml.append(\"            </div>\\n\");\n\t\thtml.append(\"            <noscript>\\n\");\n\t\thtml.append(\"                <div>\\n\");\n\t\thtml.append(\"                    <input type=\\\"submit\\\" value=\\\"Continue\\\"/>\\n\");\n\t\thtml.append(\"                </div>\\n\");\n\t\thtml.append(\"            </noscript>\\n\");\n\t\thtml.append(\"        </form>\\n\");\n\t\thtml.append(\"        \\n\");\n\t\thtml.append(\"        <script>window.onload = function() { document.forms[0].submit(); }</script>\\n\");\n\t\thtml.append(\"    </body>\\n\");\n\t\thtml.append(\"</html>\");\n\t\treturn html.toString();\n\t}\n\n\tprivate static class Saml2AssertingPartyLogoutRequestResolver\n\t\t\timplements Saml2LogoutRequestValidatorParametersResolver {\n\n\t\tprivate final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;\n\n\t\tprivate RequestMatcher logoutRequestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t\t.matcher(\"/logout/saml2/slo\");\n\n\t\tSaml2AssertingPartyLogoutRequestResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {\n\t\t\tthis.relyingPartyRegistrationResolver = relyingPartyRegistrationResolver;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Saml2LogoutRequestValidatorParameters resolve(HttpServletRequest request,\n\t\t\t\t@Nullable Authentication authentication) {\n\t\t\tString serialized = request.getParameter(Saml2ParameterNames.SAML_REQUEST);\n\t\t\tif (serialized == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tRequestMatcher.MatchResult result = this.logoutRequestMatcher.matcher(request);\n\t\t\tif (!result.isMatch()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString registrationId = getRegistrationId(result, authentication);\n\t\t\tRelyingPartyRegistration registration = this.relyingPartyRegistrationResolver.resolve(request,\n\t\t\t\t\tregistrationId);\n\t\t\tif (registration == null) {\n\t\t\t\tthrow new Saml2AuthenticationException(\n\t\t\t\t\t\tSaml2Error.relyingPartyRegistrationNotFound(\"registration not found\"));\n\t\t\t}\n\t\t\tUriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration);\n\t\t\tString entityId = uriResolver.resolve(registration.getEntityId());\n\t\t\tentityId = Objects.requireNonNull(entityId);\n\t\t\tString logoutLocation = uriResolver.resolve(registration.getSingleLogoutServiceLocation());\n\t\t\tString logoutResponseLocation = uriResolver.resolve(registration.getSingleLogoutServiceResponseLocation());\n\t\t\tregistration = registration.mutate()\n\t\t\t\t.entityId(entityId)\n\t\t\t\t.singleLogoutServiceLocation(logoutLocation)\n\t\t\t\t.singleLogoutServiceResponseLocation(logoutResponseLocation)\n\t\t\t\t.build();\n\t\t\tSaml2MessageBinding saml2MessageBinding = Saml2MessageBindingUtils.resolveBinding(request);\n\t\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t\t.samlRequest(serialized)\n\t\t\t\t.relayState(request.getParameter(Saml2ParameterNames.RELAY_STATE))\n\t\t\t\t.binding(saml2MessageBinding)\n\t\t\t\t.parameters((params) -> params.put(Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\t\trequest.getParameter(Saml2ParameterNames.SIG_ALG)))\n\t\t\t\t.parameters((params) -> params.put(Saml2ParameterNames.SIGNATURE,\n\t\t\t\t\t\trequest.getParameter(Saml2ParameterNames.SIGNATURE)))\n\t\t\t\t.parametersQuery((params) -> request.getQueryString())\n\t\t\t\t.build();\n\t\t\treturn new Saml2LogoutRequestValidatorParameters(logoutRequest, registration, authentication);\n\t\t}\n\n\t\tvoid setLogoutRequestMatcher(RequestMatcher logoutRequestMatcher) {\n\t\t\tAssert.notNull(logoutRequestMatcher, \"logoutRequestMatcher cannot be null\");\n\t\t\tthis.logoutRequestMatcher = logoutRequestMatcher;\n\t\t}\n\n\t\tprivate @Nullable String getRegistrationId(RequestMatcher.MatchResult result,\n\t\t\t\t@Nullable Authentication authentication) {\n\t\t\tString registrationId = result.getVariables().get(\"registrationId\");\n\t\t\tif (registrationId != null) {\n\t\t\t\treturn registrationId;\n\t\t\t}\n\t\t\tif (authentication == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (authentication instanceof Saml2AssertionAuthentication saml2) {\n\t\t\t\treturn saml2.getRelyingPartyRegistrationId();\n\t\t\t}\n\t\t\tif (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal saml2) {\n\t\t\t\treturn saml2.getRelyingPartyRegistrationId();\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\n\n/**\n * Implementations of this interface are responsible for the persistence of\n * {@link Saml2LogoutRequest} between requests.\n *\n * <p>\n * Used by the {@link Saml2RelyingPartyInitiatedLogoutSuccessHandler} for persisting the\n * Logout Request before it initiates the SAML 2.0 SLO flow. As well, used by\n * {@code OpenSamlLogoutResponseHandler} for resolving the Logout Request associated with\n * that Logout Response.\n *\n * @author Josh Cummings\n * @since 5.6\n * @see Saml2LogoutRequest\n * @see HttpSessionLogoutRequestRepository\n */\npublic interface Saml2LogoutRequestRepository {\n\n\t/**\n\t * Returns the {@link Saml2LogoutRequest} associated to the provided\n\t * {@code HttpServletRequest} or {@code null} if not available.\n\t * @param request the {@code HttpServletRequest}\n\t * @return the {@link Saml2LogoutRequest} or {@code null} if not available\n\t */\n\t@Nullable Saml2LogoutRequest loadLogoutRequest(HttpServletRequest request);\n\n\t/**\n\t * Persists the {@link Saml2LogoutRequest} associating it to the provided\n\t * {@code HttpServletRequest} and/or {@code HttpServletResponse}.\n\t * @param logoutRequest the {@link Saml2LogoutRequest}, if {@code null}, then remove\n\t * logout request\n\t * @param request the {@code HttpServletRequest}\n\t * @param response the {@code HttpServletResponse}\n\t */\n\tvoid saveLogoutRequest(@Nullable Saml2LogoutRequest logoutRequest, HttpServletRequest request,\n\t\t\tHttpServletResponse response);\n\n\t/**\n\t * Removes and returns the {@link Saml2LogoutRequest} associated to the provided\n\t * {@code HttpServletRequest} and {@code HttpServletResponse} or if not available\n\t * returns {@code null}.\n\t * @param request the {@code HttpServletRequest}\n\t * @param response the {@code HttpServletResponse}\n\t * @return the {@link Saml2LogoutRequest} or {@code null} if not available\n\t */\n\t@Nullable Saml2LogoutRequest removeLogoutRequest(HttpServletRequest request, HttpServletResponse response);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\n\n/**\n * Creates a signed SAML 2.0 Logout Request based on information from the\n * {@link HttpServletRequest} and current {@link Authentication}.\n *\n * The returned logout request is suitable for sending to the asserting party based on,\n * for example, the location and binding specified in\n * {@link RelyingPartyRegistration#getAssertingPartyMetadata()}.\n *\n * @author Josh Cummings\n * @since 5.6\n * @see RelyingPartyRegistration\n */\npublic interface Saml2LogoutRequestResolver {\n\n\t/**\n\t * Prepare to create, sign, and serialize a SAML 2.0 Logout Request.\n\t *\n\t * By default, includes a {@code NameID} based on the {@link Authentication} instance.\n\t * @param request the HTTP request\n\t * @param authentication the current user\n\t * @return a signed and serialized SAML 2.0 Logout Request\n\t */\n\t@Nullable Saml2LogoutRequest resolve(HttpServletRequest request, Authentication authentication);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestValidatorParametersResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;\n\n/**\n * Resolved a SAML 2.0 Logout Request and associated validation parameters from the given\n * {@link HttpServletRequest} and current {@link Authentication}.\n *\n * The returned logout request is suitable for validating, logging out the logged-in user,\n * and initiating the construction of a {@code LogoutResponse}.\n *\n * @author Josh Cummings\n * @since 6.1\n */\npublic interface Saml2LogoutRequestValidatorParametersResolver {\n\n\t/**\n\t * Resolve any SAML 2.0 Logout Request and associated\n\t * {@link org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration}\n\t * @param request the HTTP request\n\t * @param authentication the current user, if any; may be null\n\t * @return a SAML 2.0 Logout Request, if any; may be null\n\t */\n\t@Nullable Saml2LogoutRequestValidatorParameters resolve(HttpServletRequest request, @Nullable Authentication authentication);\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidatorParameters;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutValidatorResult;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * A filter for handling a &lt;saml2:LogoutResponse&gt; sent from the asserting party. A\n * &lt;saml2:LogoutResponse&gt; is sent in response to a &lt;saml2:LogoutRequest&gt;\n * already sent by the relying party.\n *\n * Note that before a &lt;saml2:LogoutRequest&gt; is sent, the user is logged out. Given\n * that, this implementation should not use any {@link LogoutSuccessHandler} that relies\n * on the user being logged in.\n *\n * @author Josh Cummings\n * @since 5.6\n * @see Saml2LogoutRequestRepository\n * @see Saml2LogoutResponseValidator\n */\npublic final class Saml2LogoutResponseFilter extends OncePerRequestFilter {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;\n\n\tprivate final Saml2LogoutResponseValidator logoutResponseValidator;\n\n\tprivate final LogoutSuccessHandler logoutSuccessHandler;\n\n\tprivate Saml2LogoutRequestRepository logoutRequestRepository = new HttpSessionLogoutRequestRepository();\n\n\tprivate RequestMatcher logoutRequestMatcher = pathPattern(\"/logout/saml2/slo\");\n\n\tpublic Saml2LogoutResponseFilter(RelyingPartyRegistrationRepository registrations,\n\t\t\tSaml2LogoutResponseValidator logoutResponseValidator, LogoutSuccessHandler logoutSuccessHandler) {\n\t\tthis.relyingPartyRegistrationResolver = (request, id) -> {\n\t\t\tif (id == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn registrations.findByRegistrationId(id);\n\t\t};\n\t\tthis.logoutResponseValidator = logoutResponseValidator;\n\t\tthis.logoutSuccessHandler = logoutSuccessHandler;\n\t}\n\n\t/**\n\t * Constructs a {@link Saml2LogoutResponseFilter} for accepting SAML 2.0 Logout\n\t * Responses from the asserting party\n\t * @param relyingPartyRegistrationResolver the strategy for resolving a\n\t * {@link RelyingPartyRegistration}\n\t * @param logoutResponseValidator authenticates the SAML 2.0 Logout Response\n\t * @param logoutSuccessHandler the action to perform now that logout has succeeded\n\t */\n\tpublic Saml2LogoutResponseFilter(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver,\n\t\t\tSaml2LogoutResponseValidator logoutResponseValidator, LogoutSuccessHandler logoutSuccessHandler) {\n\t\tthis.relyingPartyRegistrationResolver = relyingPartyRegistrationResolver;\n\t\tthis.logoutResponseValidator = logoutResponseValidator;\n\t\tthis.logoutSuccessHandler = logoutSuccessHandler;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (!this.logoutRequestMatcher.matches(request)) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tif (request.getParameter(Saml2ParameterNames.SAML_RESPONSE) == null) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tSaml2LogoutRequest logoutRequest = this.logoutRequestRepository.removeLogoutRequest(request, response);\n\t\tif (logoutRequest == null) {\n\t\t\tthis.logger.trace(\"Did not process logout response since could not find associated LogoutRequest\");\n\t\t\tresponse.sendError(HttpServletResponse.SC_BAD_REQUEST, \"Failed to find associated LogoutRequest\");\n\t\t\treturn;\n\t\t}\n\t\tRelyingPartyRegistration registration = this.relyingPartyRegistrationResolver.resolve(request,\n\t\t\t\tlogoutRequest.getRelyingPartyRegistrationId());\n\t\tif (registration == null) {\n\t\t\tthis.logger\n\t\t\t\t.trace(\"Did not process logout response since failed to find associated RelyingPartyRegistration\");\n\t\t\tSaml2Error error = new Saml2Error(Saml2ErrorCodes.RELYING_PARTY_REGISTRATION_NOT_FOUND,\n\t\t\t\t\t\"Failed to find associated RelyingPartyRegistration\");\n\t\t\tresponse.sendError(HttpServletResponse.SC_BAD_REQUEST, error.toString());\n\t\t\treturn;\n\t\t}\n\t\tString responseLocation = registration.getSingleLogoutServiceResponseLocation();\n\t\tif (responseLocation == null) {\n\t\t\tthis.logger.trace(\n\t\t\t\t\t\"Did not process logout response since RelyingPartyRegistration has not been configured with a logout response endpoint\");\n\t\t\tresponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\n\t\t\treturn;\n\t\t}\n\t\tUriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration);\n\t\tString entityId = Objects.requireNonNull(uriResolver.resolve(registration.getEntityId()));\n\t\tString logoutLocation = uriResolver.resolve(registration.getSingleLogoutServiceLocation());\n\t\tString logoutResponseLocation = Objects.requireNonNull(uriResolver.resolve(responseLocation));\n\t\tregistration = registration.mutate()\n\t\t\t.entityId(entityId)\n\t\t\t.singleLogoutServiceLocation(logoutLocation)\n\t\t\t.singleLogoutServiceResponseLocation(logoutResponseLocation)\n\t\t\t.build();\n\t\tSaml2MessageBinding saml2MessageBinding = Saml2MessageBindingUtils.resolveBinding(request);\n\t\tif (!registration.getSingleLogoutServiceBindings().contains(saml2MessageBinding)) {\n\t\t\tthis.logger.trace(\"Did not process logout response since used incorrect binding\");\n\t\t\tresponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\n\t\t\treturn;\n\t\t}\n\n\t\tString serialized = request.getParameter(Saml2ParameterNames.SAML_RESPONSE);\n\t\tSaml2LogoutResponse logoutResponse = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(serialized)\n\t\t\t.relayState(request.getParameter(Saml2ParameterNames.RELAY_STATE))\n\t\t\t.binding(saml2MessageBinding)\n\t\t\t.location(logoutResponseLocation)\n\t\t\t.parameters((params) -> params.put(Saml2ParameterNames.SIG_ALG,\n\t\t\t\t\trequest.getParameter(Saml2ParameterNames.SIG_ALG)))\n\t\t\t.parameters((params) -> params.put(Saml2ParameterNames.SIGNATURE,\n\t\t\t\t\trequest.getParameter(Saml2ParameterNames.SIGNATURE)))\n\t\t\t.parametersQuery((params) -> request.getQueryString())\n\t\t\t.build();\n\t\tSaml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(logoutResponse,\n\t\t\t\tlogoutRequest, registration);\n\t\tSaml2LogoutValidatorResult result = this.logoutResponseValidator.validate(parameters);\n\t\tif (result.hasErrors()) {\n\t\t\tresponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, result.getErrors().iterator().next().toString());\n\t\t\tthis.logger.debug(LogMessage.format(\"Failed to validate LogoutResponse: %s\", result.getErrors()));\n\t\t\treturn;\n\t\t}\n\t\tthis.logoutSuccessHandler.onLogoutSuccess(request, response, null);\n\t}\n\n\tpublic void setLogoutRequestMatcher(RequestMatcher logoutRequestMatcher) {\n\t\tAssert.notNull(logoutRequestMatcher, \"logoutRequestMatcher cannot be null\");\n\t\tthis.logoutRequestMatcher = logoutRequestMatcher;\n\t}\n\n\t/**\n\t * Use this {@link Saml2LogoutRequestRepository} for retrieving the SAML 2.0 Logout\n\t * Request associated with the request's {@code RelayState}\n\t * @param logoutRequestRepository the {@link Saml2LogoutRequestRepository} to use\n\t */\n\tpublic void setLogoutRequestRepository(Saml2LogoutRequestRepository logoutRequestRepository) {\n\t\tAssert.notNull(logoutRequestRepository, \"logoutRequestRepository cannot be null\");\n\t\tthis.logoutRequestRepository = logoutRequestRepository;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\n\n/**\n * Creates a signed SAML 2.0 Logout Response based on information from the\n * {@link HttpServletRequest} and current {@link Authentication}.\n *\n * The returned logout response is suitable for sending to the asserting party based on,\n * for example, the location and binding specified in\n * {@link RelyingPartyRegistration#getAssertingPartyMetadata()}.\n *\n * @author Josh Cummings\n * @since 5.6\n * @see RelyingPartyRegistration\n */\npublic interface Saml2LogoutResponseResolver {\n\n\t/**\n\t * Prepare to create, sign, and serialize a SAML 2.0 Logout Response.\n\t * @param request the HTTP request\n\t * @param authentication the current user\n\t * @return a signed and serialized SAML 2.0 Logout Response\n\t */\n\t@Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication);\n\n\t/**\n\t * Prepare to create, sign, and serialize a SAML 2.0 Error Logout Response.\n\t * @param request the HTTP request\n\t * @param authentication the current user\n\t * @param authenticationException the thrown exception when the logout request was\n\t * processed\n\t * @return a signed and serialized SAML 2.0 Logout Response, or {@code null} if it\n\t * cannot generate a SAML 2.0 Error Logout Response\n\t * @since 7.0\n\t */\n\tdefault @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication,\n\t\t\tSaml2AuthenticationException authenticationException) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2MessageBindingUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\n\n/**\n * Utility methods for working with {@link Saml2MessageBinding}\n *\n * For internal use only.\n *\n * @since 5.8\n */\nfinal class Saml2MessageBindingUtils {\n\n\tprivate Saml2MessageBindingUtils() {\n\t}\n\n\tstatic Saml2MessageBinding resolveBinding(HttpServletRequest request) {\n\t\tif (isHttpPostBinding(request)) {\n\t\t\treturn Saml2MessageBinding.POST;\n\t\t}\n\t\telse if (isHttpRedirectBinding(request)) {\n\t\t\treturn Saml2MessageBinding.REDIRECT;\n\t\t}\n\t\tthrow new Saml2Exception(\"Unable to determine message binding from request.\");\n\t}\n\n\tprivate static boolean isSamlRequestResponse(HttpServletRequest request) {\n\t\treturn (request.getParameter(Saml2ParameterNames.SAML_REQUEST) != null\n\t\t\t\t|| request.getParameter(Saml2ParameterNames.SAML_RESPONSE) != null);\n\t}\n\n\tstatic boolean isHttpRedirectBinding(HttpServletRequest request) {\n\t\treturn \"GET\".equalsIgnoreCase(request.getMethod()) && isSamlRequestResponse(request);\n\t}\n\n\tstatic boolean isHttpPostBinding(HttpServletRequest request) {\n\t\treturn \"POST\".equalsIgnoreCase(request.getMethod()) && isSamlRequestResponse(request);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.HtmlUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * A success handler for issuing a SAML 2.0 Logout Request to the SAML 2.0 Asserting Party\n *\n * @author Josh Cummings\n * @since 5.6\n */\npublic final class Saml2RelyingPartyInitiatedLogoutSuccessHandler implements LogoutSuccessHandler {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final Saml2LogoutRequestResolver logoutRequestResolver;\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate Saml2LogoutRequestRepository logoutRequestRepository = new HttpSessionLogoutRequestRepository();\n\n\t/**\n\t * Constructs a {@link Saml2RelyingPartyInitiatedLogoutSuccessHandler} using the\n\t * provided parameters\n\t * @param logoutRequestResolver the {@link Saml2LogoutRequestResolver} to use\n\t */\n\tpublic Saml2RelyingPartyInitiatedLogoutSuccessHandler(Saml2LogoutRequestResolver logoutRequestResolver) {\n\t\tthis.logoutRequestResolver = logoutRequestResolver;\n\t}\n\n\t/**\n\t * Produce and send a SAML 2.0 Logout Response based on the SAML 2.0 Logout Request\n\t * received from the asserting party\n\t * @param request the HTTP request\n\t * @param response the HTTP response\n\t * @param authentication the current principal details\n\t * @throws IOException when failing to write to the response\n\t */\n\t@Override\n\tpublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) throws IOException {\n\t\tif (authentication == null) {\n\t\t\tthis.logger.trace(\"Returning 401 since no logout request generated\");\n\t\t\tresponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n\t\t\treturn;\n\t\t}\n\t\tSaml2LogoutRequest logoutRequest = this.logoutRequestResolver.resolve(request, authentication);\n\t\tif (logoutRequest == null) {\n\t\t\tthis.logger.trace(\"Returning 401 since no logout request generated\");\n\t\t\tresponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n\t\t\treturn;\n\t\t}\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, request, response);\n\t\tif (logoutRequest.getBinding() == Saml2MessageBinding.REDIRECT) {\n\t\t\tdoRedirect(request, response, logoutRequest);\n\t\t}\n\t\telse {\n\t\t\tdoPost(response, logoutRequest);\n\t\t}\n\t}\n\n\t/**\n\t * Use this {@link Saml2LogoutRequestRepository} for saving the SAML 2.0 Logout\n\t * Request\n\t * @param logoutRequestRepository the {@link Saml2LogoutRequestRepository} to use\n\t */\n\tpublic void setLogoutRequestRepository(Saml2LogoutRequestRepository logoutRequestRepository) {\n\t\tAssert.notNull(logoutRequestRepository, \"logoutRequestRepository cannot be null\");\n\t\tthis.logoutRequestRepository = logoutRequestRepository;\n\t}\n\n\tprivate void doRedirect(HttpServletRequest request, HttpServletResponse response, Saml2LogoutRequest logoutRequest)\n\t\t\tthrows IOException {\n\t\tString location = logoutRequest.getLocation();\n\t\tString query = logoutRequest.getParametersQuery();\n\t\tAssert.notNull(query, \"logout request must have a parameters query when using redirect binding\");\n\t\tUriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location).query(query);\n\t\tthis.redirectStrategy.sendRedirect(request, response, uriBuilder.build(true).toUriString());\n\t}\n\n\tprivate void doPost(HttpServletResponse response, Saml2LogoutRequest logoutRequest) throws IOException {\n\t\tString location = logoutRequest.getLocation();\n\t\tString saml = logoutRequest.getSamlRequest();\n\t\tString relayState = logoutRequest.getRelayState();\n\t\tString html = createSamlPostRequestFormData(location, saml, relayState);\n\t\tresponse.setContentType(MediaType.TEXT_HTML_VALUE);\n\t\tresponse.getWriter().write(html);\n\t}\n\n\tprivate String createSamlPostRequestFormData(String location, String saml, @Nullable String relayState) {\n\t\tStringBuilder html = new StringBuilder();\n\t\thtml.append(\"<!DOCTYPE html>\\n\");\n\t\thtml.append(\"<html>\\n\").append(\"    <head>\\n\");\n\t\thtml.append(\"        <meta http-equiv=\\\"Content-Security-Policy\\\" \")\n\t\t\t.append(\"content=\\\"script-src 'sha256-oZhLbc2kO8b8oaYLrUc7uye1MgVKMyLtPqWR4WtKF+c='\\\">\\n\");\n\t\thtml.append(\"        <meta charset=\\\"utf-8\\\" />\\n\");\n\t\thtml.append(\"    </head>\\n\");\n\t\thtml.append(\"    <body>\\n\");\n\t\thtml.append(\"        <noscript>\\n\");\n\t\thtml.append(\"            <p>\\n\");\n\t\thtml.append(\"                <strong>Note:</strong> Since your browser does not support JavaScript,\\n\");\n\t\thtml.append(\"                you must press the Continue button once to proceed.\\n\");\n\t\thtml.append(\"            </p>\\n\");\n\t\thtml.append(\"        </noscript>\\n\");\n\t\thtml.append(\"        \\n\");\n\t\thtml.append(\"        <form action=\\\"\");\n\t\thtml.append(location);\n\t\thtml.append(\"\\\" method=\\\"post\\\">\\n\");\n\t\thtml.append(\"            <div>\\n\");\n\t\thtml.append(\"                <input type=\\\"hidden\\\" name=\\\"SAMLRequest\\\" value=\\\"\");\n\t\thtml.append(HtmlUtils.htmlEscape(saml));\n\t\thtml.append(\"\\\"/>\\n\");\n\t\tif (StringUtils.hasText(relayState)) {\n\t\t\thtml.append(\"                <input type=\\\"hidden\\\" name=\\\"RelayState\\\" value=\\\"\");\n\t\t\thtml.append(HtmlUtils.htmlEscape(relayState));\n\t\t\thtml.append(\"\\\"/>\\n\");\n\t\t}\n\t\thtml.append(\"            </div>\\n\");\n\t\thtml.append(\"            <noscript>\\n\");\n\t\thtml.append(\"                <div>\\n\");\n\t\thtml.append(\"                    <input type=\\\"submit\\\" value=\\\"Continue\\\"/>\\n\");\n\t\thtml.append(\"                </div>\\n\");\n\t\thtml.append(\"            </noscript>\\n\");\n\t\thtml.append(\"        </form>\\n\");\n\t\thtml.append(\"        \\n\");\n\t\thtml.append(\"        <script>window.onload = function() { document.forms[0].submit(); }</script>\\n\");\n\t\thtml.append(\"    </body>\\n\");\n\t\thtml.append(\"</html>\");\n\t\treturn html.toString();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal utilities for SAML2 support (not for public use).\n */\n@NullMarked\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal utilities for SAML2 support (not for public use).\n */\n@NullMarked\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/metadata/RequestMatcherMetadataResponseResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.metadata;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.UUID;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResolver;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;\nimport org.springframework.security.saml2.provider.service.registration.IterableRelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers;\nimport org.springframework.security.web.util.matcher.OrRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * An implementation of {@link Saml2MetadataResponseResolver} that identifies which\n * {@link RelyingPartyRegistration}s to use with a {@link RequestMatcher}\n *\n * @author Josh Cummings\n * @since 6.1\n */\npublic class RequestMatcherMetadataResponseResolver implements Saml2MetadataResponseResolver {\n\n\tprivate static final String DEFAULT_METADATA_FILENAME = \"saml-{registrationId}-metadata.xml\";\n\n\tprivate RequestMatcher matcher = new OrRequestMatcher(\n\t\t\tpathPattern(\"/saml2/service-provider-metadata/{registrationId}\"),\n\t\t\tpathPattern(\"/saml2/metadata/{registrationId}\"), pathPattern(\"/saml2/metadata\"));\n\n\tprivate String filename = DEFAULT_METADATA_FILENAME;\n\n\tprivate final RelyingPartyRegistrationRepository registrations;\n\n\tprivate final Saml2MetadataResolver metadata;\n\n\t/**\n\t * Construct a {@link RequestMatcherMetadataResponseResolver}\n\t * @param registrations the source for relying party metadata\n\t * @param metadata the strategy for converting {@link RelyingPartyRegistration}s into\n\t * metadata\n\t */\n\tpublic RequestMatcherMetadataResponseResolver(RelyingPartyRegistrationRepository registrations,\n\t\t\tSaml2MetadataResolver metadata) {\n\t\tAssert.notNull(registrations, \"relyingPartyRegistrationRepository cannot be null\");\n\t\tAssert.notNull(metadata, \"saml2MetadataResolver cannot be null\");\n\t\tthis.registrations = registrations;\n\t\tthis.metadata = metadata;\n\t}\n\n\t/**\n\t * Construct and serialize a relying party's SAML 2.0 metadata based on the given\n\t * {@link HttpServletRequest}. Uses the configured {@link RequestMatcher} to identify\n\t * the metadata request, including looking for any indicated {@code registrationId}.\n\t *\n\t * <p>\n\t * If a {@code registrationId} is found in the request, it will attempt to use that,\n\t * erroring if no {@link RelyingPartyRegistration} is found.\n\t *\n\t * <p>\n\t * If no {@code registrationId} is found in the request, it will attempt to show all\n\t * {@link RelyingPartyRegistration}s in an {@code <md:EntitiesDescriptor>}. To\n\t * exercise this functionality, the provided\n\t * {@link RelyingPartyRegistrationRepository} needs to implement {@link Iterable}.\n\t * @param request the HTTP request\n\t * @return a {@link Saml2MetadataResponse} instance\n\t * @throws Saml2Exception if the {@link RequestMatcher} specifies a non-existent\n\t * {@code registrationId}\n\t */\n\t@Override\n\tpublic @Nullable Saml2MetadataResponse resolve(HttpServletRequest request) {\n\t\tRequestMatcher.MatchResult result = this.matcher.matcher(request);\n\t\tif (!result.isMatch()) {\n\t\t\treturn null;\n\t\t}\n\t\tString registrationId = result.getVariables().get(\"registrationId\");\n\t\tSaml2MetadataResponse response = responseByRegistrationId(request, registrationId);\n\t\tif (response != null) {\n\t\t\treturn response;\n\t\t}\n\t\tif (this.registrations instanceof IterableRelyingPartyRegistrationRepository iterable) {\n\t\t\treturn responseByIterable(request, iterable);\n\t\t}\n\t\tif (this.registrations instanceof Iterable<?>) {\n\t\t\tIterable<RelyingPartyRegistration> registrations = (Iterable<RelyingPartyRegistration>) this.registrations;\n\t\t\treturn responseByIterable(request, registrations);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate @Nullable Saml2MetadataResponse responseByRegistrationId(HttpServletRequest request,\n\t\t\t@Nullable String registrationId) {\n\t\tif (registrationId == null) {\n\t\t\treturn null;\n\t\t}\n\t\tRelyingPartyRegistration registration = this.registrations.findByRegistrationId(registrationId);\n\t\tif (registration == null) {\n\t\t\tthrow new Saml2Exception(\"registration not found\");\n\t\t}\n\t\treturn responseByIterable(request, Collections.singleton(registration));\n\t}\n\n\tprivate Saml2MetadataResponse responseByIterable(HttpServletRequest request,\n\t\t\tIterable<RelyingPartyRegistration> registrations) {\n\t\tMap<String, RelyingPartyRegistration> results = new LinkedHashMap<>();\n\t\tfor (RelyingPartyRegistration registration : registrations) {\n\t\t\tRelyingPartyRegistrationPlaceholderResolvers.UriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers\n\t\t\t\t.uriResolver(request, registration);\n\t\t\tString entityId = Objects.requireNonNull(uriResolver.resolve(registration.getEntityId()));\n\t\t\tresults.computeIfAbsent(entityId, (e) -> {\n\t\t\t\tString ssoLocation = uriResolver.resolve(registration.getAssertionConsumerServiceLocation());\n\t\t\t\tssoLocation = Objects.requireNonNull(ssoLocation);\n\t\t\t\tString sloLocation = uriResolver.resolve(registration.getSingleLogoutServiceLocation());\n\t\t\t\tString sloResponseLocation = uriResolver.resolve(registration.getSingleLogoutServiceResponseLocation());\n\t\t\t\treturn registration.mutate()\n\t\t\t\t\t.entityId(entityId)\n\t\t\t\t\t.assertionConsumerServiceLocation(ssoLocation)\n\t\t\t\t\t.singleLogoutServiceLocation(sloLocation)\n\t\t\t\t\t.singleLogoutServiceResponseLocation(sloResponseLocation)\n\t\t\t\t\t.build();\n\t\t\t});\n\t\t}\n\t\tString metadata = this.metadata.resolve(results.values());\n\t\tString value = (results.size() == 1) ? results.values().iterator().next().getRegistrationId()\n\t\t\t\t: UUID.randomUUID().toString();\n\t\tString fileName = this.filename.replace(\"{registrationId}\", value);\n\t\ttry {\n\t\t\tString encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());\n\t\t\treturn new Saml2MetadataResponse(metadata, encodedFileName);\n\t\t}\n\t\tcatch (UnsupportedEncodingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Use this {@link RequestMatcher} to identity which requests to generate metadata\n\t * for. By default, matches {@code /saml2/metadata},\n\t * {@code /saml2/metadata/{registrationId}}, {@code /saml2/service-provider-metadata},\n\t * and {@code /saml2/service-provider-metadata/{registrationId}}\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be empty\");\n\t\tthis.matcher = requestMatcher;\n\t}\n\n\t/**\n\t * Sets the metadata filename template. If it contains the {@code {registrationId}}\n\t * placeholder, it will be resolved as a random UUID if there are multiple\n\t * {@link RelyingPartyRegistration}s. Otherwise, it will be replaced by the\n\t * {@link RelyingPartyRegistration}'s id.\n\t *\n\t * <p>\n\t * The default value is {@code saml-{registrationId}-metadata.xml}\n\t * @param metadataFilename metadata filename, must contain a {registrationId}\n\t */\n\tpublic void setMetadataFilename(String metadataFilename) {\n\t\tAssert.hasText(metadataFilename, \"metadataFilename cannot be empty\");\n\t\tthis.filename = metadataFilename;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/metadata/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Web metadata endpoint support for SAML2 relying party.\n */\n@NullMarked\npackage org.springframework.security.saml2.provider.service.web.metadata;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internal utilities for SAML2 support (not for public use).\n */\n@NullMarked\npackage org.springframework.security.saml2.provider.service.web;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.saml2.aot.hint.Saml2RuntimeHints\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/resources/org/springframework/security/saml2/saml2-asserting-party-metadata-schema-postgres.sql",
    "content": "CREATE TABLE saml2_asserting_party_metadata\n(\n    entity_id                               VARCHAR(1000) NOT NULL,\n    single_sign_on_service_location         VARCHAR(1000) NOT NULL,\n    single_sign_on_service_binding          VARCHAR(100),\n    want_authn_requests_signed              boolean,\n    signing_algorithms                      BYTEA,\n    verification_credentials                BYTEA NOT NULL,\n    encryption_credentials                  BYTEA,\n    single_logout_service_location          VARCHAR(1000),\n    single_logout_service_response_location VARCHAR(1000),\n    single_logout_service_binding           VARCHAR(100),\n    PRIMARY KEY (entity_id)\n);\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/main/resources/org/springframework/security/saml2/saml2-asserting-party-metadata-schema.sql",
    "content": "CREATE TABLE saml2_asserting_party_metadata\n(\n    entity_id                               VARCHAR(1000) NOT NULL,\n    single_sign_on_service_location         VARCHAR(1000) NOT NULL,\n    single_sign_on_service_binding          VARCHAR(100),\n    want_authn_requests_signed              boolean,\n    signing_algorithms                      VARCHAR(256) NOT NULL,\n    verification_credentials                blob NOT NULL,\n    encryption_credentials                  blob,\n    single_logout_service_location          VARCHAR(1000),\n    single_logout_service_response_location VARCHAR(1000),\n    single_logout_service_binding           VARCHAR(100),\n    PRIMARY KEY (entity_id)\n);\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/internal/OpenSaml5Template.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.internal;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.core.xml.util.XMLObjectSupport;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.criterion.ProtocolCriterion;\nimport org.opensaml.saml.ext.saml2delrestrict.Delegate;\nimport org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;\nimport org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;\nimport org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;\nimport org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialResolver;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;\nimport org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.security.criteria.UsageCriterion;\nimport org.opensaml.security.x509.BasicX509Credential;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.SignatureSigningParametersResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;\nimport org.opensaml.xmlsec.crypto.XMLSigningUtil;\nimport org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.DecryptionException;\nimport org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;\nimport org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.opensaml.xmlsec.signature.Signature;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * For internal use only. Subject to breaking changes at any time.\n */\n@NullMarked\nfinal class OpenSaml5Template implements OpenSamlOperations {\n\n\tprivate static final Log logger = LogFactory.getLog(OpenSaml5Template.class);\n\n\t@Override\n\tpublic <T extends XMLObject> T build(QName elementName) {\n\t\tXMLObjectBuilder<?> builder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(elementName);\n\t\tif (builder == null) {\n\t\t\tthrow new Saml2Exception(\"Unable to resolve Builder for \" + elementName);\n\t\t}\n\t\treturn (T) builder.buildObject(elementName);\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(String serialized) {\n\t\treturn deserialize(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(InputStream serialized) {\n\t\ttry {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tDocument document = pool.parse(serialized);\n\t\t\tElement element = document.getDocumentElement();\n\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\tif (unmarshaller == null) {\n\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t}\n\t\t\treturn (T) unmarshaller.unmarshall(element);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(XMLObject object) {\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\tAssert.notNull(marshaller, \"Marshaller for \" + object.getElementQName() + \" must be configured\");\n\t\ttry {\n\t\t\treturn serialize(marshaller.marshall(object));\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(Element element) {\n\t\treturn new OpenSaml5SerializationConfigurer(element);\n\t}\n\n\t@Override\n\tpublic OpenSaml5SignatureConfigurer withSigningKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5SignatureConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5VerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5VerificationConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5DecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5DecryptionConfigurer(credentials);\n\t}\n\n\tOpenSaml5Template() {\n\n\t}\n\n\tstatic final class OpenSaml5SerializationConfigurer\n\t\t\timplements SerializationConfigurer<OpenSaml5SerializationConfigurer> {\n\n\t\tprivate final Element element;\n\n\t\tboolean pretty;\n\n\t\tOpenSaml5SerializationConfigurer(Element element) {\n\t\t\tthis.element = element;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SerializationConfigurer prettyPrint(boolean pretty) {\n\t\t\tthis.pretty = pretty;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String serialize() {\n\t\t\tif (this.pretty) {\n\t\t\t\treturn SerializeSupport.prettyPrintXML(this.element);\n\t\t\t}\n\t\t\treturn SerializeSupport.nodeToString(this.element);\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5SignatureConfigurer implements SignatureConfigurer<OpenSaml5SignatureConfigurer> {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate final Map<String, String> components = new LinkedHashMap<>();\n\n\t\tprivate List<String> algs = List.of(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\n\t\tOpenSaml5SignatureConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SignatureConfigurer algorithms(List<String> algs) {\n\t\t\tthis.algs = algs;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic <O extends SignableXMLObject> O sign(O object) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\ttry {\n\t\t\t\tSignatureSupport.signObject(object, parameters);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> sign(Map<String, String> params) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\tthis.components.putAll(params);\n\t\t\tCredential credential = parameters.getSigningCredential();\n\t\t\tAssert.notNull(credential, \"credential cannot be null when signing a SAML payload\");\n\t\t\tString algorithmUri = parameters.getSignatureAlgorithm();\n\t\t\tAssert.notNull(algorithmUri, \"algorithmUri cannot be null when signing a SAML payload\");\n\t\t\tthis.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String> component : this.components.entrySet()) {\n\t\t\t\tbuilder.queryParam(component.getKey(),\n\t\t\t\t\t\tUriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = builder.build(true).toString().substring(1);\n\t\t\ttry {\n\t\t\t\tbyte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,\n\t\t\t\t\t\tqueryString.getBytes(StandardCharsets.UTF_8));\n\t\t\t\tString b64Signature = Saml2Utils.samlEncode(rawSignature);\n\t\t\t\tthis.components.put(Saml2ParameterNames.SIGNATURE, b64Signature);\n\t\t\t}\n\t\t\tcatch (SecurityException ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn this.components;\n\t\t}\n\n\t\tprivate SignatureSigningParameters resolveSigningParameters() {\n\t\t\tList<Credential> credentials = resolveSigningCredentials();\n\t\t\tList<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\t\tString canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;\n\t\t\tSignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();\n\t\t\tBasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();\n\t\t\tsigningConfiguration.setSigningCredentials(credentials);\n\t\t\tsigningConfiguration.setSignatureAlgorithms(this.algs);\n\t\t\tsigningConfiguration.setSignatureReferenceDigestMethods(digests);\n\t\t\tsigningConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);\n\t\t\tsigningConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());\n\t\t\tCriteriaSet criteria = new CriteriaSet(new SignatureSigningConfigurationCriterion(signingConfiguration));\n\t\t\ttry {\n\t\t\t\tSignatureSigningParameters parameters = resolver.resolveSingle(criteria);\n\t\t\t\tAssert.notNull(parameters, \"Failed to resolve any signing credential\");\n\t\t\t\treturn parameters;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {\n\t\t\tfinal NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();\n\n\t\t\tnamedManager.setUseDefaultManager(true);\n\t\t\tfinal KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();\n\n\t\t\t// Generator for X509Credentials\n\t\t\tfinal X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();\n\t\t\tx509Factory.setEmitEntityCertificate(true);\n\t\t\tx509Factory.setEmitEntityCertificateChain(true);\n\n\t\t\tdefaultManager.registerFactory(x509Factory);\n\n\t\t\treturn namedManager;\n\t\t}\n\n\t\tprivate List<Credential> resolveSigningCredentials() {\n\t\t\tList<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential x509Credential : this.credentials) {\n\t\t\t\tX509Certificate certificate = x509Credential.getCertificate();\n\t\t\t\tPrivateKey privateKey = x509Credential.getPrivateKey();\n\t\t\t\tBasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey);\n\t\t\t\tcredential.setUsageType(UsageType.SIGNING);\n\t\t\t\tcredentials.add(credential);\n\t\t\t}\n\t\t\treturn credentials;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5VerificationConfigurer implements VerificationConfigurer {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate @Nullable String entityId;\n\n\t\tOpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic VerificationConfigurer entityId(@Nullable String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate SignatureTrustEngine trustEngine(Collection<Saml2X509Credential> keys) {\n\t\t\tSet<Credential> credentials = new HashSet<>();\n\t\t\tfor (Saml2X509Credential key : keys) {\n\t\t\t\tBasicX509Credential cred = new BasicX509Credential(key.getCertificate());\n\t\t\t\tcred.setUsageType(UsageType.SIGNING);\n\t\t\t\tcred.setEntityId(this.entityId);\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tCredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);\n\t\t\treturn new ExplicitKeySignatureTrustEngine(credentialsResolver,\n\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t}\n\n\t\tprivate CriteriaSet verificationCriteria(Issuer issuer) {\n\t\t\tAssert.notNull(issuer.getValue(), \"required elements must have a value\");\n\t\t\treturn new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),\n\t\t\t\t\tnew EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),\n\t\t\t\t\tnew EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(SignableXMLObject signable) {\n\t\t\tif (signable instanceof StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"Response#ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"Response#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(response.getSignature(), \"Response#Signature cannot be null\");\n\t\t\t\treturn verifySignature(response.getID(), response.getIssuer(), response.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"Request#ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"Request#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(request.getSignature(), \"Request#Signature cannot be null\");\n\t\t\t\treturn verifySignature(request.getID(), request.getIssuer(), request.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof Assertion assertion) {\n\t\t\t\tAssert.notNull(assertion.getID(), \"Assertion#ID cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getIssuer(), \"Assertion#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getSignature(), \"Assertion#Signature cannot be null\");\n\t\t\t\treturn verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());\n\t\t\t}\n\t\t\tthrow new Saml2Exception(\"Unsupported object of type: \" + signable.getClass().getName());\n\t\t}\n\n\t\tprivate Collection<Saml2Error> verifySignature(String id, Issuer issuer, Signature signature) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(issuer);\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tSAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();\n\t\t\ttry {\n\t\t\t\tprofileValidator.validate(signature);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, criteria)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\treturn errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(RedirectParameters parameters) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(parameters.getIssuer());\n\t\t\tif (parameters.getAlgorithm() == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature algorithm for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tbyte[] signature = parameters.getSignature();\n\t\t\tif (signature == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tString algorithmUri = parameters.getAlgorithm();\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]: \"));\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5DecryptionConfigurer implements DecryptionConfigurer {\n\n\t\tprivate static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(\n\t\t\t\tArrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),\n\t\t\t\t\t\tnew SimpleRetrievalMethodEncryptedKeyResolver()));\n\n\t\tprivate final Decrypter decrypter;\n\n\t\tOpenSaml5DecryptionConfigurer(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tthis.decrypter = decrypter(decryptionCredentials);\n\t\t}\n\n\t\tprivate static Decrypter decrypter(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tCollection<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential key : decryptionCredentials) {\n\t\t\t\tCredential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey());\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tKeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);\n\t\t\tDecrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);\n\t\t\tdecrypter.setRootInNewDocument(true);\n\t\t\treturn decrypter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decrypt(XMLObject object) {\n\t\t\tif (object instanceof Response response) {\n\t\t\t\tdecryptResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (object instanceof Assertion assertion) {\n\t\t\t\tdecryptAssertion(assertion);\n\t\t\t}\n\t\t\tif (object instanceof LogoutRequest request) {\n\t\t\t\tdecryptLogoutRequest(request);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * The methods that follow are adapted from OpenSAML's {@link DecryptAssertions},\n\t\t * {@link DecryptNameIDs}, and {@link DecryptAttributes}.\n\t\t *\n\t\t * <p>The reason that these OpenSAML classes are not used directly is because they\n\t\t * reference {@link javax.servlet.http.HttpServletRequest} which is a lower\n\t\t * Servlet API version than what Spring Security SAML uses.\n\t\t *\n\t\t * If OpenSAML 5 updates to {@link jakarta.servlet.http.HttpServletRequest}, then\n\t\t * this arrangement can be revisited.\n\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());\n\t\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\t\td.setNameID(decrypted);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAttributes(AttributeStatement statement) {\n\t\t\tCollection<Attribute> decrypteds = new ArrayList<>();\n\t\t\tfor (EncryptedAttribute encrypted : statement.getEncryptedAttributes()) {\n\t\t\t\ttry {\n\t\t\t\t\tAttribute decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatement.getAttributes().addAll(decrypteds);\n\t\t}\n\n\t\tprivate void decryptSubject(@Nullable Subject subject) {\n\t\t\tif (subject != null) {\n\t\t\t\tif (subject.getEncryptedID() != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(subject.getEncryptedID());\n\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\tsubject.setNameID(decrypted);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (final SubjectConfirmation sc : subject.getSubjectConfirmations()) {\n\t\t\t\t\tif (sc.getEncryptedID() != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(sc.getEncryptedID());\n\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\tsc.setNameID(decrypted);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptLogoutRequest(LogoutRequest request) {\n\t\t\tif (request.getEncryptedID() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(request.getEncryptedID());\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\trequest.setNameID(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport javax.xml.namespace.QName;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.saml.common.assertion.AssertionValidationException;\nimport org.opensaml.saml.common.assertion.ValidationContext;\nimport org.opensaml.saml.common.assertion.ValidationResult;\nimport org.opensaml.saml.saml2.assertion.ConditionValidator;\nimport org.opensaml.saml.saml2.assertion.SAML20AssertionValidator;\nimport org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters;\nimport org.opensaml.saml.saml2.assertion.StatementValidator;\nimport org.opensaml.saml.saml2.assertion.SubjectConfirmationValidator;\nimport org.opensaml.saml.saml2.assertion.impl.AudienceRestrictionConditionValidator;\nimport org.opensaml.saml.saml2.assertion.impl.BearerSubjectConfirmationValidator;\nimport org.opensaml.saml.saml2.assertion.impl.DelegationRestrictionConditionValidator;\nimport org.opensaml.saml.saml2.assertion.impl.ProxyRestrictionConditionValidator;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.OneTimeUse;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.core.SubjectConfirmationData;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.xmlsec.signature.support.SignaturePrevalidator;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ResponseValidatorResult;\nimport org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * Implementation of {@link AuthenticationProvider} for SAML authentications when\n * receiving a {@code Response} object containing an {@code Assertion}. This\n * implementation uses the {@code OpenSAML 5} library.\n *\n * <p>\n * The {@link OpenSaml5AuthenticationProvider} supports {@link Saml2AuthenticationToken}\n * objects that contain a SAML response in its decoded XML format\n * {@link Saml2AuthenticationToken#getSaml2Response()} along with the information about\n * the asserting party, the identity provider (IDP), as well as the relying party, the\n * service provider (SP, this application).\n * <p>\n * The {@link Saml2AuthenticationToken} will be processed into a SAML Response object. The\n * SAML response object can be signed. If the Response is signed, a signature will not be\n * required on the assertion.\n * <p>\n * While a response object can contain a list of assertion, this provider will only\n * leverage the first valid assertion for the purpose of authentication. Assertions that\n * do not pass validation will be ignored. If no valid assertions are found a\n * {@link Saml2AuthenticationException} is thrown.\n * <p>\n * This provider supports two types of encrypted SAML elements\n * <ul>\n * <li><a href=\n * \"https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=17\">EncryptedAssertion</a></li>\n * <li><a href=\n * \"https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=14\">EncryptedID</a></li>\n * </ul>\n * If the assertion is encrypted, then signature validation on the assertion is no longer\n * required.\n * <p>\n * This provider does not perform an X509 certificate validation on the configured\n * asserting party, IDP, verification certificates.\n *\n * @author Josh Cummings\n * @since 5.5\n * @see <a href=\n * \"https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38\">SAML 2\n * StatusResponse</a>\n * @see <a href=\"https://shibboleth.atlassian.net/wiki/spaces/OSAML/overview\">OpenSAML</a>\n */\n@NullMarked\npublic final class OpenSaml5AuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.SAML_RESPONSE_AUTHORITY;\n\n\tprivate final BaseOpenSamlAuthenticationProvider delegate;\n\n\t/**\n\t * Creates an {@link OpenSaml5AuthenticationProvider}\n\t */\n\tpublic OpenSaml5AuthenticationProvider() {\n\t\tthis.delegate = new BaseOpenSamlAuthenticationProvider(new OpenSaml5Template());\n\t\tsetResponseValidator(ResponseValidator.withDefaults());\n\t\tsetAssertionValidator(AssertionValidator.withDefaults());\n\t\tsetResponseAuthenticationConverter(new ResponseAuthenticationConverter());\n\t}\n\n\t/**\n\t * Set the {@link Consumer} strategy to use for decrypting elements of a validated\n\t * {@link Response}. The default strategy decrypts all {@link EncryptedAssertion}s\n\t * using OpenSAML's {@link Decrypter}, adding the results to\n\t * {@link Response#getAssertions()}.\n\t *\n\t * You can use this method to configure the {@link Decrypter} instance like so:\n\t *\n\t * <pre>\n\t *\tOpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();\n\t *\tprovider.setResponseElementsDecrypter((responseToken) -&gt; {\n\t *\t    DecrypterParameters parameters = new DecrypterParameters();\n\t *\t    // ... set parameters as needed\n\t *\t    Decrypter decrypter = new Decrypter(parameters);\n\t *\t\tResponse response = responseToken.getResponse();\n\t *  \tEncryptedAssertion encrypted = response.getEncryptedAssertions().get(0);\n\t *  \ttry {\n\t *  \t\tAssertion assertion = decrypter.decrypt(encrypted);\n\t *  \t\tresponse.getAssertions().add(assertion);\n\t *  \t} catch (Exception e) {\n\t *  \t \tthrow new Saml2AuthenticationException(...);\n\t *  \t}\n\t *\t});\n\t * </pre>\n\t *\n\t * Or, in the event that you have your own custom decryption interface, the same\n\t * pattern applies:\n\t *\n\t * <pre>\n\t *\tOpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();\n\t *\tConverter&lt;EncryptedAssertion, Assertion&gt; myService = ...\n\t *\tprovider.setResponseDecrypter((responseToken) -&gt; {\n\t *\t   Response response = responseToken.getResponse();\n\t *\t   response.getEncryptedAssertions().stream()\n\t *\t   \t\t.map(service::decrypt).forEach(response.getAssertions()::add);\n\t *\t});\n\t * </pre>\n\t *\n\t * This is valuable when using an external service to perform the decryption.\n\t * @param responseElementsDecrypter the {@link Consumer} for decrypting response\n\t * elements\n\t * @since 5.5\n\t */\n\tpublic void setResponseElementsDecrypter(Consumer<ResponseToken> responseElementsDecrypter) {\n\t\tAssert.notNull(responseElementsDecrypter, \"responseElementsDecrypter cannot be null\");\n\t\tthis.delegate\n\t\t\t.setResponseElementsDecrypter((token) -> responseElementsDecrypter.accept(new ResponseToken(token)));\n\t}\n\n\t/**\n\t * Set the {@link Converter} to use for validating the SAML 2.0 Response.\n\t *\n\t * You can still invoke the default validator by delegating to\n\t * {@link #createDefaultResponseValidator()}, like so:\n\t *\n\t * <pre>\n\t * OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t * provider.setResponseValidator(responseToken -&gt; {\n\t * \t\tSaml2ResponseValidatorResult result = createDefaultResponseValidator()\n\t * \t\t\t.convert(responseToken)\n\t * \t\treturn result.concat(myCustomValidator.convert(responseToken));\n\t * });\n\t * </pre>\n\t * @param responseValidator the {@link Converter} to use\n\t * @since 5.6\n\t */\n\tpublic void setResponseValidator(Converter<ResponseToken, Saml2ResponseValidatorResult> responseValidator) {\n\t\tAssert.notNull(responseValidator, \"responseValidator cannot be null\");\n\t\tthis.delegate.setResponseValidator((token) -> responseValidator.convert(new ResponseToken(token)));\n\t}\n\n\t/**\n\t * Set the {@link Converter} to use for validating each {@link Assertion} in the SAML\n\t * 2.0 Response.\n\t *\n\t * You can still invoke the default validator by calling\n\t * {@link AssertionValidator#withDefaults()}, like so:\n\t *\n\t * <pre>\n\t *\tOpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();\n\t *  AssertionValidator validator = AssertionValidator.withDefaults();\n\t *  provider.setAssertionValidator(assertionToken -&gt; {\n\t *\t\tSaml2ResponseValidatorResult result = validator.validate(assertionToken);\n\t *\t\treturn result.concat(myCustomValidator.convert(assertionToken));\n\t *  });\n\t * </pre>\n\t *\n\t * You can also use this method to configure the provider to use a different\n\t * {@link ValidationContext} from the default, like so:\n\t *\n\t * <pre>\n\t *\tOpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();\n\t *  AssertionValidator validator = AssertionValidator.builder().clockSkew(Duration.ofMinutes(2)).build();\n\t *\tprovider.setAssertionValidator(validator);\n\t * </pre>\n\t *\n\t * Consider taking a look at {@link AssertionValidator#createValidationContext} to see\n\t * how it constructs a {@link ValidationContext}.\n\t *\n\t * It is not necessary to delegate to the default validator. You can safely replace it\n\t * entirely with your own. Note that signature verification is performed as a separate\n\t * step from this validator.\n\t * @param assertionValidator the validator to use\n\t * @since 5.4\n\t */\n\tpublic void setAssertionValidator(Converter<AssertionToken, Saml2ResponseValidatorResult> assertionValidator) {\n\t\tAssert.notNull(assertionValidator, \"assertionValidator cannot be null\");\n\t\tthis.delegate.setAssertionValidator((token) -> assertionValidator.convert(new AssertionToken(token)));\n\t}\n\n\t/**\n\t * Set the {@link Consumer} strategy to use for decrypting elements of a validated\n\t * {@link Assertion}.\n\t *\n\t * You can use this method to configure the {@link Decrypter} used like so:\n\t *\n\t * <pre>\n\t *\tOpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();\n\t *\tprovider.setResponseDecrypter((assertionToken) -&gt; {\n\t *\t    DecrypterParameters parameters = new DecrypterParameters();\n\t *\t    // ... set parameters as needed\n\t *\t    Decrypter decrypter = new Decrypter(parameters);\n\t *\t\tAssertion assertion = assertionToken.getAssertion();\n\t *  \tEncryptedID encrypted = assertion.getSubject().getEncryptedID();\n\t *  \ttry {\n\t *  \t\tNameID name = decrypter.decrypt(encrypted);\n\t *  \t\tassertion.getSubject().setNameID(name);\n\t *  \t} catch (Exception e) {\n\t *  \t \tthrow new Saml2AuthenticationException(...);\n\t *  \t}\n\t *\t});\n\t * </pre>\n\t *\n\t * Or, in the event that you have your own custom interface, the same pattern applies:\n\t *\n\t * <pre>\n\t *\tOpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();\n\t *\tMyDecryptionService myService = ...\n\t *\tprovider.setResponseDecrypter((responseToken) -&gt; {\n\t *\t   \tAssertion assertion = assertionToken.getAssertion();\n\t *\t   \tEncryptedID encrypted = assertion.getSubject().getEncryptedID();\n\t *\t\tNameID name = myService.decrypt(encrypted);\n\t *\t\tassertion.getSubject().setNameID(name);\n\t *\t});\n\t * </pre>\n\t * @param assertionDecrypter the {@link Consumer} for decrypting assertion elements\n\t * @since 5.5\n\t */\n\tpublic void setAssertionElementsDecrypter(Consumer<AssertionToken> assertionDecrypter) {\n\t\tAssert.notNull(assertionDecrypter, \"assertionDecrypter cannot be null\");\n\t\tthis.delegate.setAssertionElementsDecrypter((token) -> assertionDecrypter.accept(new AssertionToken(token)));\n\t}\n\n\t/**\n\t * Set the {@link Converter} to use for converting a validated {@link Response} into\n\t * an {@link AbstractAuthenticationToken}.\n\t *\n\t * You can delegate to the default behavior by calling\n\t * {@link #createDefaultResponseAuthenticationConverter()} like so:\n\t *\n\t * <pre>\n\t *\tOpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();\n\t * \tConverter&lt;ResponseToken, Saml2Authentication&gt; authenticationConverter =\n\t * \t\t\tcreateDefaultResponseAuthenticationConverter();\n\t *\tprovider.setResponseAuthenticationConverter(responseToken -&gt; {\n\t *\t\tSaml2Authentication authentication = authenticationConverter.convert(responseToken);\n\t *\t\tUser user = myUserRepository.findByUsername(authentication.getName());\n\t *\t\treturn new MyAuthentication(authentication, user);\n\t *\t});\n\t * </pre>\n\t * @param responseAuthenticationConverter the {@link Converter} to use\n\t * @since 5.4\n\t */\n\tpublic void setResponseAuthenticationConverter(\n\t\t\tConverter<ResponseToken, ? extends AbstractAuthenticationToken> responseAuthenticationConverter) {\n\t\tAssert.notNull(responseAuthenticationConverter, \"responseAuthenticationConverter cannot be null\");\n\t\tthis.delegate.setResponseAuthenticationConverter(\n\t\t\t\t(token) -> responseAuthenticationConverter.convert(new ResponseToken(token)));\n\t}\n\n\t/**\n\t * Indicate when to validate response attributes, like {@code Destination} and\n\t * {@code Issuer}. By default, this value is set to false, meaning that response\n\t * attributes are validated first. Setting this value to {@code true} allows you to\n\t * use a response authentication converter that doesn't rely on the {@code NameID}\n\t * element in the {@link Response}'s assertion.\n\t * @param validateResponseAfterAssertions when to validate response attributes\n\t * @since 6.5\n\t * @see #setResponseAuthenticationConverter\n\t * @see ResponseAuthenticationConverter\n\t */\n\tpublic void setValidateResponseAfterAssertions(boolean validateResponseAfterAssertions) {\n\t\tthis.delegate.setValidateResponseAfterAssertions(validateResponseAfterAssertions);\n\t}\n\n\t/**\n\t * Construct a default strategy for validating the SAML 2.0 Response\n\t * @return the default response validator strategy\n\t * @since 5.6\n\t * @deprecated please use {@link ResponseValidator#withDefaults()} instead\n\t */\n\t@Deprecated\n\tpublic static Converter<ResponseToken, Saml2ResponseValidatorResult> createDefaultResponseValidator() {\n\t\treturn ResponseValidator.withDefaults();\n\t}\n\n\t/**\n\t * Construct a default strategy for validating each SAML 2.0 Assertion and associated\n\t * {@link Authentication} token\n\t * @return the default assertion validator strategy\n\t * @deprecated please use {@link AssertionValidator#withDefaults()} instead\n\t */\n\t@Deprecated\n\tpublic static Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionValidator() {\n\t\treturn AssertionValidator.withDefaults();\n\t}\n\n\t/**\n\t * Construct a default strategy for validating each SAML 2.0 Assertion and associated\n\t * {@link Authentication} token\n\t * @param contextConverter the conversion strategy to use to generate a\n\t * {@link ValidationContext} for each assertion being validated\n\t * @return the default assertion validator strategy\n\t * @deprecated Use {@link #createDefaultAssertionValidatorWithParameters} instead\n\t */\n\t@Deprecated\n\tpublic static Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionValidator(\n\t\t\tConverter<AssertionToken, ValidationContext> contextConverter) {\n\t\treturn (assertionToken) -> {\n\t\t\tAssertion assertion = assertionToken.getAssertion();\n\t\t\tSAML20AssertionValidator validator = BaseOpenSamlAuthenticationProvider.SAML20AssertionValidators.attributeValidator;\n\t\t\tValidationContext context = contextConverter.convert(assertionToken);\n\t\t\ttry {\n\t\t\t\tValidationResult result = validator.validate(assertion, context);\n\t\t\t\tif (result == ValidationResult.VALID) {\n\t\t\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tString message = String.format(\"Invalid assertion [%s] for SAML response [%s]: %s\", assertion.getID(),\n\t\t\t\t\t\t((Response) Objects.requireNonNull(assertion.getParent())).getID(), ex.getMessage());\n\t\t\t\treturn Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ASSERTION, message));\n\t\t\t}\n\t\t\tString message = String.format(\"Invalid assertion [%s] for SAML response [%s]: %s\", assertion.getID(),\n\t\t\t\t\t((Response) Objects.requireNonNull(assertion.getParent())).getID(),\n\t\t\t\t\tcontext.getValidationFailureMessages());\n\t\t\treturn Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ASSERTION, message));\n\t\t};\n\t}\n\n\t/**\n\t * Construct a default strategy for validating each SAML 2.0 Assertion and associated\n\t * {@link Authentication} token\n\t * @param validationContextParameters a consumer for editing the values passed to the\n\t * {@link ValidationContext} for each assertion being validated\n\t * @return the default assertion validator strategy\n\t * @since 5.8\n\t * @deprecated please use {@link AssertionValidator#withDefaults()} instead\n\t */\n\t@Deprecated\n\tpublic static Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionValidatorWithParameters(\n\t\t\tConsumer<Map<String, Object>> validationContextParameters) {\n\t\treturn AssertionValidator.builder().validationContextParameters(validationContextParameters).build();\n\t}\n\n\t/**\n\t * Construct a default strategy for converting a SAML 2.0 Response and\n\t * {@link Authentication} token into a {@link Saml2Authentication}\n\t * @return the default response authentication converter strategy\n\t * @deprecated please use {@link ResponseAuthenticationConverter} instead\n\t */\n\t@Deprecated\n\tpublic static Converter<ResponseToken, Saml2Authentication> createDefaultResponseAuthenticationConverter() {\n\t\treturn new ResponseAuthenticationConverter();\n\t}\n\n\t/**\n\t * @param authentication the authentication request object, must be of type\n\t * {@link Saml2AuthenticationToken}\n\t * @return {@link Saml2Authentication} if the assertion is valid\n\t * @throws AuthenticationException if a validation exception occurs\n\t */\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\treturn this.delegate.authenticate(authentication);\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn authentication != null && Saml2AuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * A tuple containing an OpenSAML {@link Response} and its associated authentication\n\t * token.\n\t *\n\t * @since 5.4\n\t */\n\tpublic static class ResponseToken {\n\n\t\tprivate final Saml2AuthenticationToken token;\n\n\t\tprivate final Response response;\n\n\t\tResponseToken(Response response, Saml2AuthenticationToken token) {\n\t\t\tthis.token = token;\n\t\t\tthis.response = response;\n\t\t}\n\n\t\tResponseToken(BaseOpenSamlAuthenticationProvider.ResponseToken token) {\n\t\t\tthis.token = token.getToken();\n\t\t\tthis.response = token.getResponse();\n\t\t}\n\n\t\tpublic Response getResponse() {\n\t\t\treturn this.response;\n\t\t}\n\n\t\tpublic Saml2AuthenticationToken getToken() {\n\t\t\treturn this.token;\n\t\t}\n\n\t}\n\n\t/**\n\t * A tuple containing an OpenSAML {@link Assertion} and its associated authentication\n\t * token.\n\t *\n\t * @since 5.4\n\t */\n\tpublic static class AssertionToken {\n\n\t\tprivate final Saml2AuthenticationToken token;\n\n\t\tprivate final Assertion assertion;\n\n\t\tAssertionToken(Assertion assertion, Saml2AuthenticationToken token) {\n\t\t\tthis.token = token;\n\t\t\tthis.assertion = assertion;\n\t\t}\n\n\t\tAssertionToken(BaseOpenSamlAuthenticationProvider.AssertionToken token) {\n\t\t\tthis.token = token.getToken();\n\t\t\tthis.assertion = token.getAssertion();\n\t\t}\n\n\t\tpublic Assertion getAssertion() {\n\t\t\treturn this.assertion;\n\t\t}\n\n\t\tpublic Saml2AuthenticationToken getToken() {\n\t\t\treturn this.token;\n\t\t}\n\n\t}\n\n\t/**\n\t * A response validator that checks the {@code InResponseTo} value against the\n\t * correlating {@link AbstractSaml2AuthenticationRequest}\n\t *\n\t * @since 6.5\n\t */\n\tpublic static final class InResponseToValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {\n\n\t\t@Override\n\t\tpublic Saml2ResponseValidatorResult convert(ResponseToken responseToken) {\n\t\t\tAbstractSaml2AuthenticationRequest request = responseToken.getToken().getAuthenticationRequest();\n\t\t\tResponse response = responseToken.getResponse();\n\t\t\tString inResponseTo = response.getInResponseTo();\n\t\t\treturn BaseOpenSamlAuthenticationProvider.validateInResponseTo(request, inResponseTo);\n\t\t}\n\n\t}\n\n\t/**\n\t * A response validator that compares the {@code Destination} value to the configured\n\t * {@link RelyingPartyRegistration#getAssertionConsumerServiceLocation()}\n\t *\n\t * @since 6.5\n\t */\n\tpublic static final class DestinationValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {\n\n\t\t@Override\n\t\tpublic Saml2ResponseValidatorResult convert(ResponseToken responseToken) {\n\t\t\tResponse response = responseToken.getResponse();\n\t\t\tSaml2AuthenticationToken token = responseToken.getToken();\n\t\t\tString destination = response.getDestination();\n\t\t\tString location = token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation();\n\t\t\tif (StringUtils.hasText(destination) && !destination.equals(location)) {\n\t\t\t\tString message = \"Invalid destination [\" + destination + \"] for SAML response [\" + response.getID()\n\t\t\t\t\t\t+ \"]\";\n\t\t\t\treturn Saml2ResponseValidatorResult\n\t\t\t\t\t.failure(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, message));\n\t\t\t}\n\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t}\n\n\t}\n\n\t/**\n\t * A response validator that compares the {@code Issuer} value to the configured\n\t * {@link AssertingPartyMetadata#getEntityId()}\n\t *\n\t * @since 6.5\n\t */\n\tpublic static final class IssuerValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {\n\n\t\t@Override\n\t\tpublic Saml2ResponseValidatorResult convert(ResponseToken responseToken) {\n\t\t\tResponse response = responseToken.getResponse();\n\t\t\tSaml2AuthenticationToken token = responseToken.getToken();\n\t\t\tIssuer issuer = response.getIssuer();\n\t\t\tAssert.notNull(issuer, \"Response#Issuer cannot be null\");\n\t\t\tString assertingPartyEntityId = token.getRelyingPartyRegistration()\n\t\t\t\t.getAssertingPartyMetadata()\n\t\t\t\t.getEntityId();\n\t\t\tif (!StringUtils.hasText(issuer.getValue()) || !assertingPartyEntityId.equals(issuer.getValue())) {\n\t\t\t\tString message = String.format(\"Invalid issuer [%s] for SAML response [%s]\", issuer, response.getID());\n\t\t\t\treturn Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, message));\n\t\t\t}\n\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t}\n\n\t}\n\n\t/**\n\t * A composite response validator that confirms a {@code SUCCESS} status, that there\n\t * is at least one assertion, and any other configured converters\n\t *\n\t * @since 6.5\n\t * @see InResponseToValidator\n\t * @see DestinationValidator\n\t * @see IssuerValidator\n\t */\n\tpublic static final class ResponseValidator implements Converter<ResponseToken, Saml2ResponseValidatorResult> {\n\n\t\tprivate static final List<Converter<ResponseToken, Saml2ResponseValidatorResult>> DEFAULTS = List\n\t\t\t.of(new InResponseToValidator(), new DestinationValidator(), new IssuerValidator());\n\n\t\tprivate final List<Converter<ResponseToken, Saml2ResponseValidatorResult>> validators;\n\n\t\t@SafeVarargs\n\t\tpublic ResponseValidator(Converter<ResponseToken, Saml2ResponseValidatorResult>... validators) {\n\t\t\tthis.validators = List.of(validators);\n\t\t\tAssert.notEmpty(this.validators, \"validators cannot be empty\");\n\t\t}\n\n\t\tpublic static ResponseValidator withDefaults() {\n\t\t\treturn new ResponseValidator(new InResponseToValidator(), new DestinationValidator(),\n\t\t\t\t\tnew IssuerValidator());\n\t\t}\n\n\t\t@SafeVarargs\n\t\tpublic static ResponseValidator withDefaults(\n\t\t\t\tConverter<ResponseToken, Saml2ResponseValidatorResult>... validators) {\n\t\t\tList<Converter<ResponseToken, Saml2ResponseValidatorResult>> defaults = new ArrayList<>(DEFAULTS);\n\t\t\tdefaults.addAll(List.of(validators));\n\t\t\treturn new ResponseValidator(defaults.toArray(Converter[]::new));\n\t\t}\n\n\t\t@Override\n\t\tpublic Saml2ResponseValidatorResult convert(ResponseToken responseToken) {\n\t\t\tResponse response = responseToken.getResponse();\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tList<String> statusCodes = BaseOpenSamlAuthenticationProvider.getStatusCodes(response);\n\t\t\tif (!BaseOpenSamlAuthenticationProvider.isSuccess(statusCodes)) {\n\t\t\t\tfor (String statusCode : statusCodes) {\n\t\t\t\t\tString message = String.format(\"Invalid status [%s] for SAML response [%s]\", statusCode,\n\t\t\t\t\t\t\tresponse.getID());\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, message));\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (Converter<ResponseToken, Saml2ResponseValidatorResult> validator : this.validators) {\n\t\t\t\terrors.addAll(validator.convert(responseToken).getErrors());\n\t\t\t}\n\t\t\tif (response.getAssertions().isEmpty()) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, \"No assertions found in response.\"));\n\t\t\t}\n\t\t\treturn Saml2ResponseValidatorResult.failure(errors);\n\t\t}\n\n\t}\n\n\t/**\n\t * A default implementation of {@link OpenSaml5AuthenticationProvider}'s assertion\n\t * validator. This does not check the signature as signature verification is performed\n\t * by a different component\n\t *\n\t * @author Josh Cummings\n\t * @since 6.5\n\t */\n\tpublic static final class AssertionValidator implements Converter<AssertionToken, Saml2ResponseValidatorResult> {\n\n\t\tprivate final SAML20AssertionValidator assertionValidator;\n\n\t\tprivate Consumer<Map<String, Object>> paramsConsumer = (map) -> {\n\t\t};\n\n\t\tpublic AssertionValidator(SAML20AssertionValidator assertionValidator) {\n\t\t\tthis.assertionValidator = assertionValidator;\n\t\t}\n\n\t\t@Override\n\t\tpublic Saml2ResponseValidatorResult convert(AssertionToken source) {\n\t\t\tAssertion assertion = source.getAssertion();\n\t\t\tValidationContext validationContext = createValidationContext(source);\n\t\t\ttry {\n\t\t\t\tValidationResult result = this.assertionValidator.validate(assertion, validationContext);\n\t\t\t\tif (result == ValidationResult.VALID) {\n\t\t\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tString message = String.format(\"Invalid assertion [%s] for SAML response [%s]: %s\", assertion.getID(),\n\t\t\t\t\t\t((Response) Objects.requireNonNull(assertion.getParent())).getID(), ex.getMessage());\n\t\t\t\treturn Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ASSERTION, message));\n\t\t\t}\n\t\t\tString message = String.format(\"Invalid assertion [%s] for SAML response [%s]: %s\", assertion.getID(),\n\t\t\t\t\t((Response) Objects.requireNonNull(assertion.getParent())).getID(),\n\t\t\t\t\tvalidationContext.getValidationFailureMessages());\n\t\t\treturn Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_ASSERTION, message));\n\t\t}\n\n\t\t/**\n\t\t * Validate this assertion\n\t\t * @param token the assertion to validate\n\t\t * @return the validation result\n\t\t */\n\t\tpublic Saml2ResponseValidatorResult validate(AssertionToken token) {\n\t\t\treturn convert(token);\n\t\t}\n\n\t\t/**\n\t\t * Mutate the map of OpenSAML {@link ValidationContext} parameters using the given\n\t\t * {@code paramsConsumer}\n\t\t * @param paramsConsumer the context parameters mutator\n\t\t */\n\t\tpublic void setValidationContextParameters(Consumer<Map<String, Object>> paramsConsumer) {\n\t\t\tthis.paramsConsumer = paramsConsumer;\n\t\t}\n\n\t\tprivate ValidationContext createValidationContext(AssertionToken assertionToken) {\n\t\t\tSaml2AuthenticationToken token = assertionToken.getToken();\n\t\t\tRelyingPartyRegistration relyingPartyRegistration = token.getRelyingPartyRegistration();\n\t\t\tString audience = relyingPartyRegistration.getEntityId();\n\t\t\tString recipient = relyingPartyRegistration.getAssertionConsumerServiceLocation();\n\t\t\tString assertingPartyEntityId = relyingPartyRegistration.getAssertingPartyMetadata().getEntityId();\n\t\t\tMap<String, Object> params = new HashMap<>();\n\t\t\tAssertion assertion = assertionToken.getAssertion();\n\t\t\tif (assertionContainsInResponseTo(assertion)) {\n\t\t\t\tString requestId = getAuthnRequestId(token.getAuthenticationRequest());\n\t\t\t\tparams.put(SAML2AssertionValidationParameters.SC_VALID_IN_RESPONSE_TO, requestId);\n\t\t\t}\n\t\t\tparams.put(SAML2AssertionValidationParameters.COND_VALID_AUDIENCES, Collections.singleton(audience));\n\t\t\tparams.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton(recipient));\n\t\t\tparams.put(SAML2AssertionValidationParameters.VALID_ISSUERS, Collections.singleton(assertingPartyEntityId));\n\t\t\tparams.put(SAML2AssertionValidationParameters.SC_CHECK_ADDRESS, false);\n\t\t\tthis.paramsConsumer.accept(params);\n\t\t\treturn new ValidationContext(params);\n\t\t}\n\n\t\tprivate static boolean assertionContainsInResponseTo(Assertion assertion) {\n\t\t\tif (assertion.getSubject() == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (SubjectConfirmation confirmation : assertion.getSubject().getSubjectConfirmations()) {\n\t\t\t\tSubjectConfirmationData confirmationData = confirmation.getSubjectConfirmationData();\n\t\t\t\tif (confirmationData == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (StringUtils.hasText(confirmationData.getInResponseTo())) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate static @Nullable String getAuthnRequestId(@Nullable AbstractSaml2AuthenticationRequest serialized) {\n\t\t\treturn (serialized != null) ? serialized.getId() : null;\n\t\t}\n\n\t\t/**\n\t\t * Create the default assertion validator\n\t\t * @return the default assertion validator\n\t\t */\n\t\tpublic static AssertionValidator withDefaults() {\n\t\t\treturn new Builder().build();\n\t\t}\n\n\t\t/**\n\t\t * Use a builder to configure aspects of the validator\n\t\t * @return the {@link Builder} for configuration {@link AssertionValidator}\n\t\t */\n\t\tpublic static Builder builder() {\n\t\t\treturn new Builder();\n\t\t}\n\n\t\tpublic static final class Builder {\n\n\t\t\tprivate final List<ConditionValidator> conditions = new ArrayList<>();\n\n\t\t\tprivate final List<SubjectConfirmationValidator> subjects = new ArrayList<>();\n\n\t\t\tprivate final Map<String, Object> validationParameters = new HashMap<>();\n\n\t\t\tprivate Builder() {\n\t\t\t\tthis.conditions.add(new AudienceRestrictionConditionValidator());\n\t\t\t\tthis.conditions.add(new DelegationRestrictionConditionValidator());\n\t\t\t\tthis.conditions.add(new ValidConditionValidator(OneTimeUse.DEFAULT_ELEMENT_NAME));\n\t\t\t\tthis.conditions.add(new ProxyRestrictionConditionValidator());\n\t\t\t\tthis.subjects.add(new BearerSubjectConfirmationValidator());\n\t\t\t\tthis.validationParameters.put(SAML2AssertionValidationParameters.CLOCK_SKEW, Duration.ofMinutes(5));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Use this clock skew for validating assertion timestamps. The default is 5\n\t\t\t * minutes.\n\t\t\t * @param duration the duration to use\n\t\t\t * @return the {@link Builder} for further configuration\n\t\t\t */\n\t\t\tpublic Builder clockSkew(Duration duration) {\n\t\t\t\tthis.validationParameters.put(SAML2AssertionValidationParameters.CLOCK_SKEW, duration);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Mutate the map of {@link ValidationContext} static parameters. By default,\n\t\t\t * these include:\n\t\t\t * <ul>\n\t\t\t * <li>{@link SAML2AssertionValidationParameters#SC_VALID_IN_RESPONSE_TO}</li>>\n\t\t\t * <li>{@link SAML2AssertionValidationParameters#COND_VALID_AUDIENCES}</li>>\n\t\t\t * <li>{@link SAML2AssertionValidationParameters#SC_VALID_RECIPIENTS}</li>>\n\t\t\t * <li>{@link SAML2AssertionValidationParameters#VALID_ISSUERS}</li>>\n\t\t\t * <li>{@link SAML2AssertionValidationParameters#SC_CHECK_ADDRESS}</li>>\n\t\t\t * <li>{@link SAML2AssertionValidationParameters#CLOCK_SKEW}</li>>\n\t\t\t * </ul>\n\t\t\t *\n\t\t\t * Note that several of these are required by various validation steps, for\n\t\t\t * example {@code COND_VALID_AUDIENCES} is needed by\n\t\t\t * {@link BearerSubjectConfirmationValidator}. If you do not want these, the\n\t\t\t * best way to remove them is to remove the {@link #conditionValidators} or\n\t\t\t * {@link #subjectValidators} themselves\n\t\t\t * @param parameters the mutator to change the set of parameters\n\t\t\t * @return\n\t\t\t */\n\t\t\tpublic Builder validationContextParameters(Consumer<Map<String, Object>> parameters) {\n\t\t\t\tparameters.accept(this.validationParameters);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Mutate the list of {@link ConditionValidator}s. By default, these include:\n\t\t\t * <ul>\n\t\t\t * <li>{@link AudienceRestrictionConditionValidator}</li>\n\t\t\t * <li>{@link DelegationRestrictionConditionValidator}</li>\n\t\t\t * <li>{@link ProxyRestrictionConditionValidator}</li>\n\t\t\t * </ul>\n\t\t\t * Note that it also adds a validator that skips the {@code saml2:OneTimeUse}\n\t\t\t * element since this validator does not have caching facilities. However, you\n\t\t\t * can construct your own instance of\n\t\t\t * {@link org.opensaml.saml.saml2.assertion.impl.OneTimeUseConditionValidator}\n\t\t\t * and supply it here.\n\t\t\t * @param conditions the mutator for changing the list of conditions to use\n\t\t\t * @return the {@link Builder} for further configuration\n\t\t\t */\n\t\t\tpublic Builder conditionValidators(Consumer<List<ConditionValidator>> conditions) {\n\t\t\t\tconditions.accept(this.conditions);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Mutate the list of {@link ConditionValidator}s.\n\t\t\t * <p>\n\t\t\t * By default it only has {@link BearerSubjectConfirmationValidator} for which\n\t\t\t * address validation is skipped.\n\t\t\t *\n\t\t\t * To turn address validation on, use\n\t\t\t * {@link #validationContextParameters(Consumer)} to set the\n\t\t\t * {@link SAML2AssertionValidationParameters#SC_CHECK_ADDRESS} value.\n\t\t\t * @param subjects the mutator for changing the list of conditions to use\n\t\t\t * @return the {@link Builder} for further configuration\n\t\t\t */\n\t\t\tpublic Builder subjectValidators(Consumer<List<SubjectConfirmationValidator>> subjects) {\n\t\t\t\tsubjects.accept(this.subjects);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Build the {@link AssertionValidator}\n\t\t\t * @return the {@link AssertionValidator}\n\t\t\t */\n\t\t\tpublic AssertionValidator build() {\n\t\t\t\tAssertionValidator validator = new AssertionValidator(new ValidSignatureAssertionValidator(\n\t\t\t\t\t\tthis.conditions, this.subjects, List.of(), null, null, null));\n\t\t\t\tvalidator.setValidationContextParameters((params) -> params.putAll(this.validationParameters));\n\t\t\t\treturn validator;\n\t\t\t}\n\n\t\t}\n\n\t\tprivate static final class ValidConditionValidator implements ConditionValidator {\n\n\t\t\tprivate final QName name;\n\n\t\t\tprivate ValidConditionValidator(QName name) {\n\t\t\t\tthis.name = name;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic QName getServicedCondition() {\n\t\t\t\treturn this.name;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic ValidationResult validate(Condition condition, Assertion assertion, ValidationContext context) {\n\t\t\t\treturn ValidationResult.VALID;\n\t\t\t}\n\n\t\t}\n\n\t\tprivate static final class ValidSignatureAssertionValidator extends SAML20AssertionValidator {\n\n\t\t\tprivate ValidSignatureAssertionValidator(@Nullable Collection<ConditionValidator> newConditionValidators,\n\t\t\t\t\t@Nullable Collection<SubjectConfirmationValidator> newConfirmationValidators,\n\t\t\t\t\t@Nullable Collection<StatementValidator> newStatementValidators,\n\t\t\t\t\torg.opensaml.saml.saml2.assertion.@Nullable AssertionValidator newAssertionValidator,\n\t\t\t\t\t@Nullable SignatureTrustEngine newTrustEngine,\n\t\t\t\t\t@Nullable SignaturePrevalidator newSignaturePrevalidator) {\n\t\t\t\tsuper(newConditionValidators, newConfirmationValidators, newStatementValidators, newAssertionValidator,\n\t\t\t\t\t\tnewTrustEngine, newSignaturePrevalidator);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected ValidationResult validateSignature(Assertion token, ValidationContext context)\n\t\t\t\t\tthrows AssertionValidationException {\n\t\t\t\treturn ValidationResult.VALID;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * A default implementation of {@link OpenSaml5AuthenticationProvider}'s response\n\t * authentication converter. It will take the principal name from the\n\t * {@link org.opensaml.saml.saml2.core.NameID} element. It will also extract the\n\t * assertion attributes and session indexes. You can either configure the principal\n\t * name converter and granted authorities converter in this class or you can\n\t * post-process this class's result through delegation.\n\t *\n\t * @author Josh Cummings\n\t * @since 6.5\n\t */\n\tpublic static final class ResponseAuthenticationConverter implements Converter<ResponseToken, Saml2Authentication> {\n\n\t\tprivate Converter<Assertion, String> principalNameConverter = ResponseAuthenticationConverter::authenticatedPrincipal;\n\n\t\tprivate Converter<Assertion, Collection<GrantedAuthority>> grantedAuthoritiesConverter = ResponseAuthenticationConverter::grantedAuthorities;\n\n\t\t@Override\n\t\tpublic Saml2Authentication convert(ResponseToken responseToken) {\n\t\t\tResponse response = responseToken.response;\n\t\t\tSaml2AuthenticationToken token = responseToken.token;\n\t\t\tAssertion assertion = CollectionUtils.firstElement(response.getAssertions());\n\t\t\tAssert.notNull(assertion, \"samlResponse must have at least one assertion\");\n\t\t\tString username = this.principalNameConverter.convert(assertion);\n\t\t\tString registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId();\n\t\t\tSaml2ResponseAssertionAccessor accessor = Saml2ResponseAssertion.withResponseValue(token.getSaml2Response())\n\t\t\t\t.nameId(authenticatedPrincipal(assertion))\n\t\t\t\t.sessionIndexes(BaseOpenSamlAuthenticationProvider.getSessionIndexes(assertion))\n\t\t\t\t.attributes(BaseOpenSamlAuthenticationProvider.getAssertionAttributes(assertion))\n\t\t\t\t.build();\n\t\t\tSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, accessor);\n\t\t\tCollection<GrantedAuthority> authorities = new HashSet<>(\n\t\t\t\t\tthis.grantedAuthoritiesConverter.convert(assertion));\n\t\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\t\treturn new Saml2AssertionAuthentication(principal, accessor, authorities, registrationId);\n\t\t}\n\n\t\t/**\n\t\t * Use this strategy to extract the principal name from the {@link Assertion}. By\n\t\t * default, this will retrieve it from the\n\t\t * {@link org.opensaml.saml.saml2.core.Subject}'s\n\t\t * {@link org.opensaml.saml.saml2.core.NameID} value.\n\t\t *\n\t\t * <p>\n\t\t * Note that because of this, if there is no\n\t\t * {@link org.opensaml.saml.saml2.core.NameID} present, then the default throws an\n\t\t * exception.\n\t\t * </p>\n\t\t * @param principalNameConverter the conversion strategy to use\n\t\t */\n\t\tpublic void setPrincipalNameConverter(Converter<Assertion, String> principalNameConverter) {\n\t\t\tAssert.notNull(principalNameConverter, \"principalNameConverter cannot be null\");\n\t\t\tthis.principalNameConverter = principalNameConverter;\n\t\t}\n\n\t\t/**\n\t\t * Use this strategy to grant authorities to a principal given the first\n\t\t * {@link Assertion} in the response. By default, this will grant\n\t\t * {@code ROLE_USER}.\n\t\t * @param grantedAuthoritiesConverter the conversion strategy to use\n\t\t */\n\t\tpublic void setGrantedAuthoritiesConverter(\n\t\t\t\tConverter<Assertion, Collection<GrantedAuthority>> grantedAuthoritiesConverter) {\n\t\t\tAssert.notNull(grantedAuthoritiesConverter, \"grantedAuthoritiesConverter cannot be null\");\n\t\t\tthis.grantedAuthoritiesConverter = grantedAuthoritiesConverter;\n\t\t}\n\n\t\tprivate static String authenticatedPrincipal(Assertion assertion) {\n\t\t\tif (!BaseOpenSamlAuthenticationProvider.hasName(assertion)) {\n\t\t\t\tthrow new Saml2AuthenticationException(\n\t\t\t\t\t\tSaml2Error.subjectNotFound(\"Assertion [\" + assertion.getID() + \"] is missing a subject\"));\n\t\t\t}\n\t\t\tSubject subject = assertion.getSubject();\n\t\t\tAssert.notNull(subject, \"Assertion#Subject cannot be null\");\n\t\t\tNameID nameId = subject.getNameID();\n\t\t\tAssert.notNull(nameId, \"Assertion#Subject#NameID cannot be null\");\n\t\t\treturn Objects.requireNonNull(nameId.getValue());\n\t\t}\n\n\t\tprivate static Collection<GrantedAuthority> grantedAuthorities(Assertion assertion) {\n\t\t\treturn AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5Template.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.core.xml.util.XMLObjectSupport;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.criterion.ProtocolCriterion;\nimport org.opensaml.saml.ext.saml2delrestrict.Delegate;\nimport org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;\nimport org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;\nimport org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;\nimport org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialResolver;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;\nimport org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.security.criteria.UsageCriterion;\nimport org.opensaml.security.x509.BasicX509Credential;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.SignatureSigningParametersResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;\nimport org.opensaml.xmlsec.crypto.XMLSigningUtil;\nimport org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.DecryptionException;\nimport org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;\nimport org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.opensaml.xmlsec.signature.Signature;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * For internal use only. Subject to breaking changes at any time.\n */\n@NullMarked\nfinal class OpenSaml5Template implements OpenSamlOperations {\n\n\tprivate static final Log logger = LogFactory.getLog(OpenSaml5Template.class);\n\n\t@Override\n\tpublic <T extends XMLObject> T build(QName elementName) {\n\t\tXMLObjectBuilder<?> builder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(elementName);\n\t\tif (builder == null) {\n\t\t\tthrow new Saml2Exception(\"Unable to resolve Builder for \" + elementName);\n\t\t}\n\t\treturn (T) builder.buildObject(elementName);\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(String serialized) {\n\t\treturn deserialize(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(InputStream serialized) {\n\t\ttry {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tDocument document = pool.parse(serialized);\n\t\t\tElement element = document.getDocumentElement();\n\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\tif (unmarshaller == null) {\n\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t}\n\t\t\treturn (T) unmarshaller.unmarshall(element);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(XMLObject object) {\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\tAssert.notNull(marshaller, \"Marshaller for \" + object.getElementQName() + \" must be configured\");\n\t\ttry {\n\t\t\treturn serialize(marshaller.marshall(object));\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(Element element) {\n\t\treturn new OpenSaml5SerializationConfigurer(element);\n\t}\n\n\t@Override\n\tpublic OpenSaml5SignatureConfigurer withSigningKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5SignatureConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5VerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5VerificationConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5DecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5DecryptionConfigurer(credentials);\n\t}\n\n\tOpenSaml5Template() {\n\n\t}\n\n\tstatic final class OpenSaml5SerializationConfigurer\n\t\t\timplements SerializationConfigurer<OpenSaml5SerializationConfigurer> {\n\n\t\tprivate final Element element;\n\n\t\tboolean pretty;\n\n\t\tOpenSaml5SerializationConfigurer(Element element) {\n\t\t\tthis.element = element;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SerializationConfigurer prettyPrint(boolean pretty) {\n\t\t\tthis.pretty = pretty;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String serialize() {\n\t\t\tif (this.pretty) {\n\t\t\t\treturn SerializeSupport.prettyPrintXML(this.element);\n\t\t\t}\n\t\t\treturn SerializeSupport.nodeToString(this.element);\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5SignatureConfigurer implements SignatureConfigurer<OpenSaml5SignatureConfigurer> {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate final Map<String, String> components = new LinkedHashMap<>();\n\n\t\tprivate List<String> algs = List.of(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\n\t\tOpenSaml5SignatureConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SignatureConfigurer algorithms(List<String> algs) {\n\t\t\tthis.algs = algs;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic <O extends SignableXMLObject> O sign(O object) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\ttry {\n\t\t\t\tSignatureSupport.signObject(object, parameters);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> sign(Map<String, String> params) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\tthis.components.putAll(params);\n\t\t\tCredential credential = parameters.getSigningCredential();\n\t\t\tAssert.notNull(credential, \"credential cannot be null when signing a SAML payload\");\n\t\t\tString algorithmUri = parameters.getSignatureAlgorithm();\n\t\t\tAssert.notNull(algorithmUri, \"algorithmUri cannot be null when signing a SAML payload\");\n\t\t\tthis.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String> component : this.components.entrySet()) {\n\t\t\t\tbuilder.queryParam(component.getKey(),\n\t\t\t\t\t\tUriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = builder.build(true).toString().substring(1);\n\t\t\ttry {\n\t\t\t\tbyte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,\n\t\t\t\t\t\tqueryString.getBytes(StandardCharsets.UTF_8));\n\t\t\t\tString b64Signature = Saml2Utils.samlEncode(rawSignature);\n\t\t\t\tthis.components.put(Saml2ParameterNames.SIGNATURE, b64Signature);\n\t\t\t}\n\t\t\tcatch (SecurityException ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn this.components;\n\t\t}\n\n\t\tprivate SignatureSigningParameters resolveSigningParameters() {\n\t\t\tList<Credential> credentials = resolveSigningCredentials();\n\t\t\tList<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\t\tString canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;\n\t\t\tSignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();\n\t\t\tBasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();\n\t\t\tsigningConfiguration.setSigningCredentials(credentials);\n\t\t\tsigningConfiguration.setSignatureAlgorithms(this.algs);\n\t\t\tsigningConfiguration.setSignatureReferenceDigestMethods(digests);\n\t\t\tsigningConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);\n\t\t\tsigningConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());\n\t\t\tCriteriaSet criteria = new CriteriaSet(new SignatureSigningConfigurationCriterion(signingConfiguration));\n\t\t\ttry {\n\t\t\t\tSignatureSigningParameters parameters = resolver.resolveSingle(criteria);\n\t\t\t\tAssert.notNull(parameters, \"Failed to resolve any signing credential\");\n\t\t\t\treturn parameters;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {\n\t\t\tfinal NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();\n\n\t\t\tnamedManager.setUseDefaultManager(true);\n\t\t\tfinal KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();\n\n\t\t\t// Generator for X509Credentials\n\t\t\tfinal X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();\n\t\t\tx509Factory.setEmitEntityCertificate(true);\n\t\t\tx509Factory.setEmitEntityCertificateChain(true);\n\n\t\t\tdefaultManager.registerFactory(x509Factory);\n\n\t\t\treturn namedManager;\n\t\t}\n\n\t\tprivate List<Credential> resolveSigningCredentials() {\n\t\t\tList<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential x509Credential : this.credentials) {\n\t\t\t\tX509Certificate certificate = x509Credential.getCertificate();\n\t\t\t\tPrivateKey privateKey = x509Credential.getPrivateKey();\n\t\t\t\tBasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey);\n\t\t\t\tcredential.setUsageType(UsageType.SIGNING);\n\t\t\t\tcredentials.add(credential);\n\t\t\t}\n\t\t\treturn credentials;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5VerificationConfigurer implements VerificationConfigurer {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate @Nullable String entityId;\n\n\t\tOpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic VerificationConfigurer entityId(@Nullable String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate SignatureTrustEngine trustEngine(Collection<Saml2X509Credential> keys) {\n\t\t\tSet<Credential> credentials = new HashSet<>();\n\t\t\tfor (Saml2X509Credential key : keys) {\n\t\t\t\tBasicX509Credential cred = new BasicX509Credential(key.getCertificate());\n\t\t\t\tcred.setUsageType(UsageType.SIGNING);\n\t\t\t\tcred.setEntityId(this.entityId);\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tCredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);\n\t\t\treturn new ExplicitKeySignatureTrustEngine(credentialsResolver,\n\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t}\n\n\t\tprivate CriteriaSet verificationCriteria(Issuer issuer) {\n\t\t\tAssert.notNull(issuer.getValue(), \"required elements must have a value\");\n\t\t\treturn new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),\n\t\t\t\t\tnew EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),\n\t\t\t\t\tnew EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(SignableXMLObject signable) {\n\t\t\tif (signable instanceof StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"Response#ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"Response#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(response.getSignature(), \"Response#Signature cannot be null\");\n\t\t\t\treturn verifySignature(response.getID(), response.getIssuer(), response.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"Request#ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"Request#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(request.getSignature(), \"Request#Signature cannot be null\");\n\t\t\t\treturn verifySignature(request.getID(), request.getIssuer(), request.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof Assertion assertion) {\n\t\t\t\tAssert.notNull(assertion.getID(), \"Assertion#ID cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getIssuer(), \"Assertion#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getSignature(), \"Assertion#Signature cannot be null\");\n\t\t\t\treturn verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());\n\t\t\t}\n\t\t\tthrow new Saml2Exception(\"Unsupported object of type: \" + signable.getClass().getName());\n\t\t}\n\n\t\tprivate Collection<Saml2Error> verifySignature(String id, Issuer issuer, Signature signature) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(issuer);\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tSAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();\n\t\t\ttry {\n\t\t\t\tprofileValidator.validate(signature);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, criteria)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\treturn errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(RedirectParameters parameters) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(parameters.getIssuer());\n\t\t\tif (parameters.getAlgorithm() == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature algorithm for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tbyte[] signature = parameters.getSignature();\n\t\t\tif (signature == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tString algorithmUri = parameters.getAlgorithm();\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]: \"));\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5DecryptionConfigurer implements DecryptionConfigurer {\n\n\t\tprivate static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(\n\t\t\t\tArrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),\n\t\t\t\t\t\tnew SimpleRetrievalMethodEncryptedKeyResolver()));\n\n\t\tprivate final Decrypter decrypter;\n\n\t\tOpenSaml5DecryptionConfigurer(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tthis.decrypter = decrypter(decryptionCredentials);\n\t\t}\n\n\t\tprivate static Decrypter decrypter(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tCollection<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential key : decryptionCredentials) {\n\t\t\t\tCredential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey());\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tKeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);\n\t\t\tDecrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);\n\t\t\tdecrypter.setRootInNewDocument(true);\n\t\t\treturn decrypter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decrypt(XMLObject object) {\n\t\t\tif (object instanceof Response response) {\n\t\t\t\tdecryptResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (object instanceof Assertion assertion) {\n\t\t\t\tdecryptAssertion(assertion);\n\t\t\t}\n\t\t\tif (object instanceof LogoutRequest request) {\n\t\t\t\tdecryptLogoutRequest(request);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * The methods that follow are adapted from OpenSAML's {@link DecryptAssertions},\n\t\t * {@link DecryptNameIDs}, and {@link DecryptAttributes}.\n\t\t *\n\t\t * <p>The reason that these OpenSAML classes are not used directly is because they\n\t\t * reference {@link javax.servlet.http.HttpServletRequest} which is a lower\n\t\t * Servlet API version than what Spring Security SAML uses.\n\t\t *\n\t\t * If OpenSAML 5 updates to {@link jakarta.servlet.http.HttpServletRequest}, then\n\t\t * this arrangement can be revisited.\n\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());\n\t\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\t\td.setNameID(decrypted);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAttributes(AttributeStatement statement) {\n\t\t\tCollection<Attribute> decrypteds = new ArrayList<>();\n\t\t\tfor (EncryptedAttribute encrypted : statement.getEncryptedAttributes()) {\n\t\t\t\ttry {\n\t\t\t\t\tAttribute decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatement.getAttributes().addAll(decrypteds);\n\t\t}\n\n\t\tprivate void decryptSubject(@Nullable Subject subject) {\n\t\t\tif (subject != null) {\n\t\t\t\tif (subject.getEncryptedID() != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(subject.getEncryptedID());\n\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\tsubject.setNameID(decrypted);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (final SubjectConfirmation sc : subject.getSubjectConfirmations()) {\n\t\t\t\t\tif (sc.getEncryptedID() != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(sc.getEncryptedID());\n\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\tsc.setNameID(decrypted);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptLogoutRequest(LogoutRequest request) {\n\t\t\tif (request.getEncryptedID() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(request.getEncryptedID());\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\trequest.setNameID(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSaml5LogoutRequestValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport org.jspecify.annotations.NullMarked;\n\n/**\n * An OpenSAML 5.x compatible implementation of {@link Saml2LogoutResponseValidator}\n *\n * @author Josh Cummings\n * @since 5.6\n */\n@NullMarked\npublic final class OpenSaml5LogoutRequestValidator implements Saml2LogoutRequestValidator {\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate final Saml2LogoutRequestValidator delegate = new BaseOpenSamlLogoutRequestValidator(\n\t\t\tnew OpenSaml5Template());\n\n\t@Override\n\tpublic Saml2LogoutValidatorResult validate(Saml2LogoutRequestValidatorParameters parameters) {\n\t\treturn this.delegate.validate(parameters);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSaml5LogoutResponseValidator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport org.jspecify.annotations.NullMarked;\n\n/**\n * An OpenSAML 5.x compatible implementation of {@link Saml2LogoutResponseValidator}\n *\n * @author Josh Cummings\n * @since 5.6\n */\n@NullMarked\npublic final class OpenSaml5LogoutResponseValidator implements Saml2LogoutResponseValidator {\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate final Saml2LogoutResponseValidator delegate = new BaseOpenSamlLogoutResponseValidator(\n\t\t\tnew OpenSaml5Template());\n\n\t@Override\n\tpublic Saml2LogoutValidatorResult validate(Saml2LogoutResponseValidatorParameters parameters) {\n\t\treturn this.delegate.validate(parameters);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSaml5Template.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.core.xml.util.XMLObjectSupport;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.criterion.ProtocolCriterion;\nimport org.opensaml.saml.ext.saml2delrestrict.Delegate;\nimport org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;\nimport org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;\nimport org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;\nimport org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialResolver;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;\nimport org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.security.criteria.UsageCriterion;\nimport org.opensaml.security.x509.BasicX509Credential;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.SignatureSigningParametersResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;\nimport org.opensaml.xmlsec.crypto.XMLSigningUtil;\nimport org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.DecryptionException;\nimport org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;\nimport org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.opensaml.xmlsec.signature.Signature;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * For internal use only. Subject to breaking changes at any time.\n */\n@NullMarked\nfinal class OpenSaml5Template implements OpenSamlOperations {\n\n\tprivate static final Log logger = LogFactory.getLog(OpenSaml5Template.class);\n\n\t@Override\n\tpublic <T extends XMLObject> T build(QName elementName) {\n\t\tXMLObjectBuilder<?> builder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(elementName);\n\t\tif (builder == null) {\n\t\t\tthrow new Saml2Exception(\"Unable to resolve Builder for \" + elementName);\n\t\t}\n\t\treturn (T) builder.buildObject(elementName);\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(String serialized) {\n\t\treturn deserialize(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(InputStream serialized) {\n\t\ttry {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tDocument document = pool.parse(serialized);\n\t\t\tElement element = document.getDocumentElement();\n\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\tif (unmarshaller == null) {\n\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t}\n\t\t\treturn (T) unmarshaller.unmarshall(element);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(XMLObject object) {\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\tAssert.notNull(marshaller, \"Marshaller for \" + object.getElementQName() + \" must be configured\");\n\t\ttry {\n\t\t\treturn serialize(marshaller.marshall(object));\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(Element element) {\n\t\treturn new OpenSaml5SerializationConfigurer(element);\n\t}\n\n\t@Override\n\tpublic OpenSaml5SignatureConfigurer withSigningKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5SignatureConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5VerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5VerificationConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5DecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5DecryptionConfigurer(credentials);\n\t}\n\n\tOpenSaml5Template() {\n\n\t}\n\n\tstatic final class OpenSaml5SerializationConfigurer\n\t\t\timplements SerializationConfigurer<OpenSaml5SerializationConfigurer> {\n\n\t\tprivate final Element element;\n\n\t\tboolean pretty;\n\n\t\tOpenSaml5SerializationConfigurer(Element element) {\n\t\t\tthis.element = element;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SerializationConfigurer prettyPrint(boolean pretty) {\n\t\t\tthis.pretty = pretty;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String serialize() {\n\t\t\tif (this.pretty) {\n\t\t\t\treturn SerializeSupport.prettyPrintXML(this.element);\n\t\t\t}\n\t\t\treturn SerializeSupport.nodeToString(this.element);\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5SignatureConfigurer implements SignatureConfigurer<OpenSaml5SignatureConfigurer> {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate final Map<String, String> components = new LinkedHashMap<>();\n\n\t\tprivate List<String> algs = List.of(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\n\t\tOpenSaml5SignatureConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SignatureConfigurer algorithms(List<String> algs) {\n\t\t\tthis.algs = algs;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic <O extends SignableXMLObject> O sign(O object) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\ttry {\n\t\t\t\tSignatureSupport.signObject(object, parameters);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> sign(Map<String, String> params) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\tthis.components.putAll(params);\n\t\t\tCredential credential = parameters.getSigningCredential();\n\t\t\tAssert.notNull(credential, \"credential cannot be null when signing a SAML payload\");\n\t\t\tString algorithmUri = parameters.getSignatureAlgorithm();\n\t\t\tAssert.notNull(algorithmUri, \"algorithmUri cannot be null when signing a SAML payload\");\n\t\t\tthis.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String> component : this.components.entrySet()) {\n\t\t\t\tbuilder.queryParam(component.getKey(),\n\t\t\t\t\t\tUriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = builder.build(true).toString().substring(1);\n\t\t\ttry {\n\t\t\t\tbyte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,\n\t\t\t\t\t\tqueryString.getBytes(StandardCharsets.UTF_8));\n\t\t\t\tString b64Signature = Saml2Utils.samlEncode(rawSignature);\n\t\t\t\tthis.components.put(Saml2ParameterNames.SIGNATURE, b64Signature);\n\t\t\t}\n\t\t\tcatch (SecurityException ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn this.components;\n\t\t}\n\n\t\tprivate SignatureSigningParameters resolveSigningParameters() {\n\t\t\tList<Credential> credentials = resolveSigningCredentials();\n\t\t\tList<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\t\tString canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;\n\t\t\tSignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();\n\t\t\tBasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();\n\t\t\tsigningConfiguration.setSigningCredentials(credentials);\n\t\t\tsigningConfiguration.setSignatureAlgorithms(this.algs);\n\t\t\tsigningConfiguration.setSignatureReferenceDigestMethods(digests);\n\t\t\tsigningConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);\n\t\t\tsigningConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());\n\t\t\tCriteriaSet criteria = new CriteriaSet(new SignatureSigningConfigurationCriterion(signingConfiguration));\n\t\t\ttry {\n\t\t\t\tSignatureSigningParameters parameters = resolver.resolveSingle(criteria);\n\t\t\t\tAssert.notNull(parameters, \"Failed to resolve any signing credential\");\n\t\t\t\treturn parameters;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {\n\t\t\tfinal NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();\n\n\t\t\tnamedManager.setUseDefaultManager(true);\n\t\t\tfinal KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();\n\n\t\t\t// Generator for X509Credentials\n\t\t\tfinal X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();\n\t\t\tx509Factory.setEmitEntityCertificate(true);\n\t\t\tx509Factory.setEmitEntityCertificateChain(true);\n\n\t\t\tdefaultManager.registerFactory(x509Factory);\n\n\t\t\treturn namedManager;\n\t\t}\n\n\t\tprivate List<Credential> resolveSigningCredentials() {\n\t\t\tList<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential x509Credential : this.credentials) {\n\t\t\t\tX509Certificate certificate = x509Credential.getCertificate();\n\t\t\t\tPrivateKey privateKey = x509Credential.getPrivateKey();\n\t\t\t\tBasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey);\n\t\t\t\tcredential.setUsageType(UsageType.SIGNING);\n\t\t\t\tcredentials.add(credential);\n\t\t\t}\n\t\t\treturn credentials;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5VerificationConfigurer implements VerificationConfigurer {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate @Nullable String entityId;\n\n\t\tOpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic VerificationConfigurer entityId(@Nullable String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate SignatureTrustEngine trustEngine(Collection<Saml2X509Credential> keys) {\n\t\t\tSet<Credential> credentials = new HashSet<>();\n\t\t\tfor (Saml2X509Credential key : keys) {\n\t\t\t\tBasicX509Credential cred = new BasicX509Credential(key.getCertificate());\n\t\t\t\tcred.setUsageType(UsageType.SIGNING);\n\t\t\t\tcred.setEntityId(this.entityId);\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tCredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);\n\t\t\treturn new ExplicitKeySignatureTrustEngine(credentialsResolver,\n\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t}\n\n\t\tprivate CriteriaSet verificationCriteria(Issuer issuer) {\n\t\t\tAssert.notNull(issuer.getValue(), \"required elements must have a value\");\n\t\t\treturn new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),\n\t\t\t\t\tnew EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),\n\t\t\t\t\tnew EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(SignableXMLObject signable) {\n\t\t\tif (signable instanceof StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"Response#ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"Response#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(response.getSignature(), \"Response#Signature cannot be null\");\n\t\t\t\treturn verifySignature(response.getID(), response.getIssuer(), response.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"Request#ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"Request#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(request.getSignature(), \"Request#Signature cannot be null\");\n\t\t\t\treturn verifySignature(request.getID(), request.getIssuer(), request.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof Assertion assertion) {\n\t\t\t\tAssert.notNull(assertion.getID(), \"Assertion#ID cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getIssuer(), \"Assertion#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getSignature(), \"Assertion#Signature cannot be null\");\n\t\t\t\treturn verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());\n\t\t\t}\n\t\t\tthrow new Saml2Exception(\"Unsupported object of type: \" + signable.getClass().getName());\n\t\t}\n\n\t\tprivate Collection<Saml2Error> verifySignature(String id, Issuer issuer, Signature signature) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(issuer);\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tSAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();\n\t\t\ttry {\n\t\t\t\tprofileValidator.validate(signature);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, criteria)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\treturn errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(RedirectParameters parameters) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(parameters.getIssuer());\n\t\t\tif (parameters.getAlgorithm() == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature algorithm for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tbyte[] signature = parameters.getSignature();\n\t\t\tif (signature == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tString algorithmUri = parameters.getAlgorithm();\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]: \"));\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5DecryptionConfigurer implements DecryptionConfigurer {\n\n\t\tprivate static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(\n\t\t\t\tArrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),\n\t\t\t\t\t\tnew SimpleRetrievalMethodEncryptedKeyResolver()));\n\n\t\tprivate final Decrypter decrypter;\n\n\t\tOpenSaml5DecryptionConfigurer(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tthis.decrypter = decrypter(decryptionCredentials);\n\t\t}\n\n\t\tprivate static Decrypter decrypter(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tCollection<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential key : decryptionCredentials) {\n\t\t\t\tCredential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey());\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tKeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);\n\t\t\tDecrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);\n\t\t\tdecrypter.setRootInNewDocument(true);\n\t\t\treturn decrypter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decrypt(XMLObject object) {\n\t\t\tif (object instanceof Response response) {\n\t\t\t\tdecryptResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (object instanceof Assertion assertion) {\n\t\t\t\tdecryptAssertion(assertion);\n\t\t\t}\n\t\t\tif (object instanceof LogoutRequest request) {\n\t\t\t\tdecryptLogoutRequest(request);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * The methods that follow are adapted from OpenSAML's {@link DecryptAssertions},\n\t\t * {@link DecryptNameIDs}, and {@link DecryptAttributes}.\n\t\t *\n\t\t * <p>The reason that these OpenSAML classes are not used directly is because they\n\t\t * reference {@link javax.servlet.http.HttpServletRequest} which is a lower\n\t\t * Servlet API version than what Spring Security SAML uses.\n\t\t *\n\t\t * If OpenSAML 5 updates to {@link jakarta.servlet.http.HttpServletRequest}, then\n\t\t * this arrangement can be revisited.\n\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());\n\t\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\t\td.setNameID(decrypted);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAttributes(AttributeStatement statement) {\n\t\t\tCollection<Attribute> decrypteds = new ArrayList<>();\n\t\t\tfor (EncryptedAttribute encrypted : statement.getEncryptedAttributes()) {\n\t\t\t\ttry {\n\t\t\t\t\tAttribute decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatement.getAttributes().addAll(decrypteds);\n\t\t}\n\n\t\tprivate void decryptSubject(@Nullable Subject subject) {\n\t\t\tif (subject != null) {\n\t\t\t\tif (subject.getEncryptedID() != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(subject.getEncryptedID());\n\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\tsubject.setNameID(decrypted);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (final SubjectConfirmation sc : subject.getSubjectConfirmations()) {\n\t\t\t\t\tif (sc.getEncryptedID() != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(sc.getEncryptedID());\n\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\tsc.setNameID(decrypted);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptLogoutRequest(LogoutRequest request) {\n\t\t\tif (request.getEncryptedID() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(request.getEncryptedID());\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\trequest.setNameID(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.jspecify.annotations.NullMarked;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\n/**\n * Utility methods for working with serialized SAML messages.\n *\n * For internal use only.\n *\n * @author Josh Cummings\n */\n@NullMarked\nfinal class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tstatic String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tstatic byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tstatic byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream b = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflater = new DeflaterOutputStream(b, new Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {\n\t\t\tthis.decoded = decoded;\n\t\t}\n\n\t\tEncodingConfigurer deflate(boolean deflate) {\n\t\t\tthis.deflate = deflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tString encode() {\n\t\t\tbyte[] bytes = (this.deflate) ? Saml2Utils.samlDeflate(this.decoded)\n\t\t\t\t\t: this.decoded.getBytes(StandardCharsets.UTF_8);\n\t\t\treturn Saml2Utils.samlEncode(bytes);\n\t\t}\n\n\t}\n\n\tstatic final class DecodingConfigurer {\n\n\t\tprivate static final Base64Checker BASE_64_CHECKER = new Base64Checker();\n\n\t\tprivate final String encoded;\n\n\t\tprivate boolean inflate;\n\n\t\tprivate boolean requireBase64;\n\n\t\tprivate DecodingConfigurer(String encoded) {\n\t\t\tthis.encoded = encoded;\n\t\t}\n\n\t\tDecodingConfigurer inflate(boolean inflate) {\n\t\t\tthis.inflate = inflate;\n\t\t\treturn this;\n\t\t}\n\n\t\tDecodingConfigurer requireBase64(boolean requireBase64) {\n\t\t\tthis.requireBase64 = requireBase64;\n\t\t\treturn this;\n\t\t}\n\n\t\tString decode() {\n\t\t\tif (this.requireBase64) {\n\t\t\t\tBASE_64_CHECKER.checkAcceptable(this.encoded);\n\t\t\t}\n\t\t\tbyte[] bytes = Saml2Utils.samlDecode(this.encoded);\n\t\t\treturn (this.inflate) ? Saml2Utils.samlInflate(bytes) : new String(bytes, StandardCharsets.UTF_8);\n\t\t}\n\n\t\tstatic class Base64Checker {\n\n\t\t\tprivate static final int[] values = genValueMapping();\n\n\t\t\tBase64Checker() {\n\n\t\t\t}\n\n\t\t\tprivate static int[] genValueMapping() {\n\t\t\t\tbyte[] alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n\t\t\t\t\t.getBytes(StandardCharsets.ISO_8859_1);\n\n\t\t\t\tint[] values = new int[256];\n\t\t\t\tArrays.fill(values, -1);\n\t\t\t\tfor (int i = 0; i < alphabet.length; i++) {\n\t\t\t\t\tvalues[alphabet[i] & 0xff] = i;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\n\t\t\tboolean isAcceptable(String s) {\n\t\t\t\tint goodChars = 0;\n\t\t\t\tint lastGoodCharVal = -1;\n\n\t\t\t\t// count number of characters from Base64 alphabet\n\t\t\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\t\t\tint val = values[0xff & s.charAt(i)];\n\t\t\t\t\tif (val != -1) {\n\t\t\t\t\t\tlastGoodCharVal = val;\n\t\t\t\t\t\tgoodChars++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/metadata/OpenSaml5MetadataResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\n\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\n\n/**\n * Resolves the SAML 2.0 Relying Party Metadata for a given\n * {@link RelyingPartyRegistration} using the OpenSAML API.\n *\n * @author Jakub Kubrynski\n * @author Josh Cummings\n * @since 5.4\n */\n@NullMarked\npublic final class OpenSaml5MetadataResolver implements Saml2MetadataResolver {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final BaseOpenSamlMetadataResolver delegate;\n\n\tpublic OpenSaml5MetadataResolver() {\n\t\tthis.delegate = new BaseOpenSamlMetadataResolver(new OpenSaml5Template());\n\t}\n\n\t@Override\n\tpublic String resolve(RelyingPartyRegistration relyingPartyRegistration) {\n\t\treturn this.delegate.resolve(relyingPartyRegistration);\n\t}\n\n\tpublic String resolve(Iterable<RelyingPartyRegistration> relyingPartyRegistrations) {\n\t\treturn this.delegate.resolve(relyingPartyRegistrations);\n\t}\n\n\t/**\n\t * Set a {@link Consumer} for modifying the OpenSAML {@link EntityDescriptor}\n\t * @param entityDescriptorCustomizer a consumer that accepts an\n\t * {@link EntityDescriptorParameters}\n\t * @since 5.7\n\t */\n\tpublic void setEntityDescriptorCustomizer(Consumer<EntityDescriptorParameters> entityDescriptorCustomizer) {\n\t\tthis.delegate.setEntityDescriptorCustomizer(\n\t\t\t\t(parameters) -> entityDescriptorCustomizer.accept(new EntityDescriptorParameters(parameters)));\n\t}\n\n\t/**\n\t * Configure whether to pretty-print the metadata XML. This can be helpful when\n\t * signing the metadata payload.\n\t *\n\t * @since 6.2\n\t **/\n\tpublic void setUsePrettyPrint(boolean usePrettyPrint) {\n\t\tthis.delegate.setUsePrettyPrint(usePrettyPrint);\n\t}\n\n\t/**\n\t * Configure whether to sign the metadata, defaults to {@code false}.\n\t *\n\t * @since 6.4\n\t */\n\tpublic void setSignMetadata(boolean signMetadata) {\n\t\tthis.delegate.setSignMetadata(signMetadata);\n\t}\n\n\t/**\n\t * A tuple containing an OpenSAML {@link EntityDescriptor} and its associated\n\t * {@link RelyingPartyRegistration}\n\t *\n\t * @since 5.7\n\t */\n\tpublic static final class EntityDescriptorParameters {\n\n\t\tprivate final EntityDescriptor entityDescriptor;\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tpublic EntityDescriptorParameters(EntityDescriptor entityDescriptor, RelyingPartyRegistration registration) {\n\t\t\tthis.entityDescriptor = entityDescriptor;\n\t\t\tthis.registration = registration;\n\t\t}\n\n\t\tEntityDescriptorParameters(BaseOpenSamlMetadataResolver.EntityDescriptorParameters parameters) {\n\t\t\tthis.entityDescriptor = parameters.getEntityDescriptor();\n\t\t\tthis.registration = parameters.getRelyingPartyRegistration();\n\t\t}\n\n\t\tpublic EntityDescriptor getEntityDescriptor() {\n\t\t\treturn this.entityDescriptor;\n\t\t}\n\n\t\tpublic RelyingPartyRegistration getRelyingPartyRegistration() {\n\t\t\treturn this.registration;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/metadata/OpenSaml5Template.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.core.xml.util.XMLObjectSupport;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.criterion.ProtocolCriterion;\nimport org.opensaml.saml.ext.saml2delrestrict.Delegate;\nimport org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;\nimport org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;\nimport org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;\nimport org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialResolver;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;\nimport org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.security.criteria.UsageCriterion;\nimport org.opensaml.security.x509.BasicX509Credential;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.SignatureSigningParametersResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;\nimport org.opensaml.xmlsec.crypto.XMLSigningUtil;\nimport org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.DecryptionException;\nimport org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;\nimport org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.opensaml.xmlsec.signature.Signature;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * For internal use only. Subject to breaking changes at any time.\n */\n@NullMarked\nfinal class OpenSaml5Template implements OpenSamlOperations {\n\n\tprivate static final Log logger = LogFactory.getLog(OpenSaml5Template.class);\n\n\t@Override\n\tpublic <T extends XMLObject> T build(QName elementName) {\n\t\tXMLObjectBuilder<?> builder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(elementName);\n\t\tif (builder == null) {\n\t\t\tthrow new Saml2Exception(\"Unable to resolve Builder for \" + elementName);\n\t\t}\n\t\treturn (T) builder.buildObject(elementName);\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(String serialized) {\n\t\treturn deserialize(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(InputStream serialized) {\n\t\ttry {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tDocument document = pool.parse(serialized);\n\t\t\tElement element = document.getDocumentElement();\n\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\tif (unmarshaller == null) {\n\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t}\n\t\t\treturn (T) unmarshaller.unmarshall(element);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(XMLObject object) {\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\tAssert.notNull(marshaller, \"Marshaller for \" + object.getElementQName() + \" must be configured\");\n\t\ttry {\n\t\t\treturn serialize(marshaller.marshall(object));\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(Element element) {\n\t\treturn new OpenSaml5SerializationConfigurer(element);\n\t}\n\n\t@Override\n\tpublic OpenSaml5SignatureConfigurer withSigningKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5SignatureConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5VerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5VerificationConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5DecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5DecryptionConfigurer(credentials);\n\t}\n\n\tOpenSaml5Template() {\n\n\t}\n\n\tstatic final class OpenSaml5SerializationConfigurer\n\t\t\timplements SerializationConfigurer<OpenSaml5SerializationConfigurer> {\n\n\t\tprivate final Element element;\n\n\t\tboolean pretty;\n\n\t\tOpenSaml5SerializationConfigurer(Element element) {\n\t\t\tthis.element = element;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SerializationConfigurer prettyPrint(boolean pretty) {\n\t\t\tthis.pretty = pretty;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String serialize() {\n\t\t\tif (this.pretty) {\n\t\t\t\treturn SerializeSupport.prettyPrintXML(this.element);\n\t\t\t}\n\t\t\treturn SerializeSupport.nodeToString(this.element);\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5SignatureConfigurer implements SignatureConfigurer<OpenSaml5SignatureConfigurer> {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate final Map<String, String> components = new LinkedHashMap<>();\n\n\t\tprivate List<String> algs = List.of(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\n\t\tOpenSaml5SignatureConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SignatureConfigurer algorithms(List<String> algs) {\n\t\t\tthis.algs = algs;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic <O extends SignableXMLObject> O sign(O object) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\ttry {\n\t\t\t\tSignatureSupport.signObject(object, parameters);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> sign(Map<String, String> params) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\tthis.components.putAll(params);\n\t\t\tCredential credential = parameters.getSigningCredential();\n\t\t\tAssert.notNull(credential, \"credential cannot be null when signing a SAML payload\");\n\t\t\tString algorithmUri = parameters.getSignatureAlgorithm();\n\t\t\tAssert.notNull(algorithmUri, \"algorithmUri cannot be null when signing a SAML payload\");\n\t\t\tthis.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String> component : this.components.entrySet()) {\n\t\t\t\tbuilder.queryParam(component.getKey(),\n\t\t\t\t\t\tUriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = builder.build(true).toString().substring(1);\n\t\t\ttry {\n\t\t\t\tbyte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,\n\t\t\t\t\t\tqueryString.getBytes(StandardCharsets.UTF_8));\n\t\t\t\tString b64Signature = Saml2Utils.samlEncode(rawSignature);\n\t\t\t\tthis.components.put(Saml2ParameterNames.SIGNATURE, b64Signature);\n\t\t\t}\n\t\t\tcatch (SecurityException ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn this.components;\n\t\t}\n\n\t\tprivate SignatureSigningParameters resolveSigningParameters() {\n\t\t\tList<Credential> credentials = resolveSigningCredentials();\n\t\t\tList<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\t\tString canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;\n\t\t\tSignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();\n\t\t\tBasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();\n\t\t\tsigningConfiguration.setSigningCredentials(credentials);\n\t\t\tsigningConfiguration.setSignatureAlgorithms(this.algs);\n\t\t\tsigningConfiguration.setSignatureReferenceDigestMethods(digests);\n\t\t\tsigningConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);\n\t\t\tsigningConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());\n\t\t\tCriteriaSet criteria = new CriteriaSet(new SignatureSigningConfigurationCriterion(signingConfiguration));\n\t\t\ttry {\n\t\t\t\tSignatureSigningParameters parameters = resolver.resolveSingle(criteria);\n\t\t\t\tAssert.notNull(parameters, \"Failed to resolve any signing credential\");\n\t\t\t\treturn parameters;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {\n\t\t\tfinal NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();\n\n\t\t\tnamedManager.setUseDefaultManager(true);\n\t\t\tfinal KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();\n\n\t\t\t// Generator for X509Credentials\n\t\t\tfinal X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();\n\t\t\tx509Factory.setEmitEntityCertificate(true);\n\t\t\tx509Factory.setEmitEntityCertificateChain(true);\n\n\t\t\tdefaultManager.registerFactory(x509Factory);\n\n\t\t\treturn namedManager;\n\t\t}\n\n\t\tprivate List<Credential> resolveSigningCredentials() {\n\t\t\tList<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential x509Credential : this.credentials) {\n\t\t\t\tX509Certificate certificate = x509Credential.getCertificate();\n\t\t\t\tPrivateKey privateKey = x509Credential.getPrivateKey();\n\t\t\t\tBasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey);\n\t\t\t\tcredential.setUsageType(UsageType.SIGNING);\n\t\t\t\tcredentials.add(credential);\n\t\t\t}\n\t\t\treturn credentials;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5VerificationConfigurer implements VerificationConfigurer {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate @Nullable String entityId;\n\n\t\tOpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic VerificationConfigurer entityId(@Nullable String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate SignatureTrustEngine trustEngine(Collection<Saml2X509Credential> keys) {\n\t\t\tSet<Credential> credentials = new HashSet<>();\n\t\t\tfor (Saml2X509Credential key : keys) {\n\t\t\t\tBasicX509Credential cred = new BasicX509Credential(key.getCertificate());\n\t\t\t\tcred.setUsageType(UsageType.SIGNING);\n\t\t\t\tcred.setEntityId(this.entityId);\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tCredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);\n\t\t\treturn new ExplicitKeySignatureTrustEngine(credentialsResolver,\n\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t}\n\n\t\tprivate CriteriaSet verificationCriteria(Issuer issuer) {\n\t\t\tAssert.notNull(issuer.getValue(), \"required elements must have a value\");\n\t\t\treturn new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),\n\t\t\t\t\tnew EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),\n\t\t\t\t\tnew EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(SignableXMLObject signable) {\n\t\t\tif (signable instanceof StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"Response#ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"Response#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(response.getSignature(), \"Response#Signature cannot be null\");\n\t\t\t\treturn verifySignature(response.getID(), response.getIssuer(), response.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"Request#ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"Request#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(request.getSignature(), \"Request#Signature cannot be null\");\n\t\t\t\treturn verifySignature(request.getID(), request.getIssuer(), request.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof Assertion assertion) {\n\t\t\t\tAssert.notNull(assertion.getID(), \"Assertion#ID cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getIssuer(), \"Assertion#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getSignature(), \"Assertion#Signature cannot be null\");\n\t\t\t\treturn verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());\n\t\t\t}\n\t\t\tthrow new Saml2Exception(\"Unsupported object of type: \" + signable.getClass().getName());\n\t\t}\n\n\t\tprivate Collection<Saml2Error> verifySignature(String id, Issuer issuer, Signature signature) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(issuer);\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tSAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();\n\t\t\ttry {\n\t\t\t\tprofileValidator.validate(signature);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, criteria)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\treturn errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(RedirectParameters parameters) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(parameters.getIssuer());\n\t\t\tif (parameters.getAlgorithm() == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature algorithm for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tbyte[] signature = parameters.getSignature();\n\t\t\tif (signature == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tString algorithmUri = parameters.getAlgorithm();\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]: \"));\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5DecryptionConfigurer implements DecryptionConfigurer {\n\n\t\tprivate static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(\n\t\t\t\tArrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),\n\t\t\t\t\t\tnew SimpleRetrievalMethodEncryptedKeyResolver()));\n\n\t\tprivate final Decrypter decrypter;\n\n\t\tOpenSaml5DecryptionConfigurer(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tthis.decrypter = decrypter(decryptionCredentials);\n\t\t}\n\n\t\tprivate static Decrypter decrypter(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tCollection<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential key : decryptionCredentials) {\n\t\t\t\tCredential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey());\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tKeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);\n\t\t\tDecrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);\n\t\t\tdecrypter.setRootInNewDocument(true);\n\t\t\treturn decrypter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decrypt(XMLObject object) {\n\t\t\tif (object instanceof Response response) {\n\t\t\t\tdecryptResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (object instanceof Assertion assertion) {\n\t\t\t\tdecryptAssertion(assertion);\n\t\t\t}\n\t\t\tif (object instanceof LogoutRequest request) {\n\t\t\t\tdecryptLogoutRequest(request);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * The methods that follow are adapted from OpenSAML's {@link DecryptAssertions},\n\t\t * {@link DecryptNameIDs}, and {@link DecryptAttributes}.\n\t\t *\n\t\t * <p>The reason that these OpenSAML classes are not used directly is because they\n\t\t * reference {@link javax.servlet.http.HttpServletRequest} which is a lower\n\t\t * Servlet API version than what Spring Security SAML uses.\n\t\t *\n\t\t * If OpenSAML 5 updates to {@link jakarta.servlet.http.HttpServletRequest}, then\n\t\t * this arrangement can be revisited.\n\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());\n\t\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\t\td.setNameID(decrypted);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAttributes(AttributeStatement statement) {\n\t\t\tCollection<Attribute> decrypteds = new ArrayList<>();\n\t\t\tfor (EncryptedAttribute encrypted : statement.getEncryptedAttributes()) {\n\t\t\t\ttry {\n\t\t\t\t\tAttribute decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatement.getAttributes().addAll(decrypteds);\n\t\t}\n\n\t\tprivate void decryptSubject(@Nullable Subject subject) {\n\t\t\tif (subject != null) {\n\t\t\t\tif (subject.getEncryptedID() != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(subject.getEncryptedID());\n\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\tsubject.setNameID(decrypted);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (final SubjectConfirmation sc : subject.getSubjectConfirmations()) {\n\t\t\t\t\tif (sc.getEncryptedID() != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(sc.getEncryptedID());\n\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\tsc.setNameID(decrypted);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptLogoutRequest(LogoutRequest request) {\n\t\t\tif (request.getEncryptedID() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(request.getEncryptedID());\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\trequest.setNameID(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/registration/OpenSaml5AssertingPartyMetadataRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.function.Consumer;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.saml.criterion.EntityRoleCriterion;\nimport org.opensaml.saml.metadata.IterableMetadataSource;\nimport org.opensaml.saml.metadata.resolver.MetadataResolver;\nimport org.opensaml.saml.metadata.resolver.filter.impl.SignatureValidationFilter;\nimport org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver;\nimport org.opensaml.saml.metadata.resolver.index.impl.RoleMetadataIndex;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\n\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.provider.service.registration.BaseOpenSamlAssertingPartyMetadataRepository.MetadataResolverAdapter;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of {@link AssertingPartyMetadataRepository} that uses a\n * {@link MetadataResolver} to retrieve {@link AssertingPartyMetadata} instances.\n *\n * <p>\n * The {@link MetadataResolver} constructed in {@link #withTrustedMetadataLocation}\n * provides expiry-aware refreshing.\n *\n * @author Josh Cummings\n * @since 6.4\n * @see AssertingPartyMetadataRepository\n * @see RelyingPartyRegistrations\n */\n@NullMarked\npublic final class OpenSaml5AssertingPartyMetadataRepository implements AssertingPartyMetadataRepository {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final BaseOpenSamlAssertingPartyMetadataRepository delegate;\n\n\t/**\n\t * Construct an {@link OpenSaml5AssertingPartyMetadataRepository} using the provided\n\t * {@link MetadataResolver}.\n\t *\n\t * <p>\n\t * The {@link MetadataResolver} should either be of type\n\t * {@link IterableMetadataSource} or it should have a {@link RoleMetadataIndex}\n\t * configured.\n\t * @param metadataResolver the {@link MetadataResolver} to use\n\t */\n\tpublic OpenSaml5AssertingPartyMetadataRepository(MetadataResolver metadataResolver) {\n\t\tAssert.notNull(metadataResolver, \"metadataResolver cannot be null\");\n\t\tthis.delegate = new BaseOpenSamlAssertingPartyMetadataRepository(\n\t\t\t\tnew CriteriaSetResolverWrapper(metadataResolver));\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Iterator<AssertingPartyMetadata> iterator() {\n\t\treturn this.delegate.iterator();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable AssertingPartyMetadata findByEntityId(String entityId) {\n\t\treturn this.delegate.findByEntityId(entityId);\n\t}\n\n\t/**\n\t * Use this trusted {@code metadataLocation} to retrieve refreshable, expiry-aware\n\t * SAML 2.0 Asserting Party (IDP) metadata.\n\t *\n\t * <p>\n\t * Valid locations can be classpath- or file-based or they can be HTTPS endpoints.\n\t * Some valid endpoints might include:\n\t *\n\t * <pre>\n\t *   metadataLocation = \"classpath:asserting-party-metadata.xml\";\n\t *   metadataLocation = \"file:asserting-party-metadata.xml\";\n\t *   metadataLocation = \"https://ap.example.org/metadata\";\n\t * </pre>\n\t *\n\t * <p>\n\t * Resolution of location is attempted immediately. To defer, wrap in\n\t * {@link CachingRelyingPartyRegistrationRepository}.\n\t * @param metadataLocation the classpath- or file-based locations or HTTPS endpoints\n\t * of the asserting party metadata file\n\t * @return the {@link MetadataLocationRepositoryBuilder} for further configuration\n\t */\n\tpublic static MetadataLocationRepositoryBuilder withTrustedMetadataLocation(String metadataLocation) {\n\t\treturn new MetadataLocationRepositoryBuilder(metadataLocation, true);\n\t}\n\n\t/**\n\t * Use this {@code metadataLocation} to retrieve refreshable, expiry-aware SAML 2.0\n\t * Asserting Party (IDP) metadata. Verification credentials are required.\n\t *\n\t * <p>\n\t * Valid locations can be classpath- or file-based or they can be remote endpoints.\n\t * Some valid endpoints might include:\n\t *\n\t * <pre>\n\t *   metadataLocation = \"classpath:asserting-party-metadata.xml\";\n\t *   metadataLocation = \"file:asserting-party-metadata.xml\";\n\t *   metadataLocation = \"https://ap.example.org/metadata\";\n\t * </pre>\n\t *\n\t * <p>\n\t * Resolution of location is attempted immediately. To defer, wrap in\n\t * {@link CachingRelyingPartyRegistrationRepository}.\n\t * @param metadataLocation the classpath- or file-based locations or remote endpoints\n\t * of the asserting party metadata file\n\t * @return the {@link MetadataLocationRepositoryBuilder} for further configuration\n\t */\n\tpublic static MetadataLocationRepositoryBuilder withMetadataLocation(String metadataLocation) {\n\t\treturn new MetadataLocationRepositoryBuilder(metadataLocation, false);\n\t}\n\n\t/**\n\t * A builder class for configuring {@link OpenSaml5AssertingPartyMetadataRepository}\n\t * for a specific metadata location.\n\t *\n\t * @author Josh Cummings\n\t */\n\tpublic static final class MetadataLocationRepositoryBuilder {\n\n\t\tprivate final String metadataLocation;\n\n\t\tprivate final boolean requireVerificationCredentials;\n\n\t\tprivate final Collection<Credential> verificationCredentials = new ArrayList<>();\n\n\t\tprivate ResourceLoader resourceLoader = new DefaultResourceLoader();\n\n\t\tMetadataLocationRepositoryBuilder(String metadataLocation, boolean trusted) {\n\t\t\tthis.metadataLocation = metadataLocation;\n\t\t\tthis.requireVerificationCredentials = !trusted;\n\t\t}\n\n\t\tpublic MetadataLocationRepositoryBuilder verificationCredentials(Consumer<Collection<Credential>> credentials) {\n\t\t\tcredentials.accept(this.verificationCredentials);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic MetadataLocationRepositoryBuilder resourceLoader(ResourceLoader resourceLoader) {\n\t\t\tthis.resourceLoader = resourceLoader;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic OpenSaml5AssertingPartyMetadataRepository build() {\n\t\t\treturn new OpenSaml5AssertingPartyMetadataRepository(metadataResolver());\n\t\t}\n\n\t\tprivate MetadataResolver metadataResolver() {\n\t\t\tResourceBackedMetadataResolver metadataResolver = resourceBackedMetadataResolver();\n\t\t\tboolean missingCredentials = this.requireVerificationCredentials && this.verificationCredentials.isEmpty();\n\t\t\tAssert.isTrue(!missingCredentials, \"Verification credentials are required\");\n\t\t\treturn initialize(metadataResolver);\n\t\t}\n\n\t\tprivate ResourceBackedMetadataResolver resourceBackedMetadataResolver() {\n\t\t\tResource resource = this.resourceLoader.getResource(this.metadataLocation);\n\t\t\ttry {\n\t\t\t\tResourceBackedMetadataResolver metadataResolver = new ResourceBackedMetadataResolver(\n\t\t\t\t\t\tnew SpringResource(resource));\n\t\t\t\tif (this.verificationCredentials.isEmpty()) {\n\t\t\t\t\treturn metadataResolver;\n\t\t\t\t}\n\t\t\t\tSignatureTrustEngine engine = new ExplicitKeySignatureTrustEngine(\n\t\t\t\t\t\tnew CollectionCredentialResolver(this.verificationCredentials),\n\t\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t\t\tSignatureValidationFilter filter = new SignatureValidationFilter(engine);\n\t\t\t\tfilter.setRequireSignedRoot(true);\n\t\t\t\tmetadataResolver.setMetadataFilter(filter);\n\t\t\t\tfilter.initialize();\n\t\t\t\treturn metadataResolver;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate MetadataResolver initialize(ResourceBackedMetadataResolver metadataResolver) {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tmetadataResolver.setParserPool(pool);\n\t\t\treturn BaseOpenSamlAssertingPartyMetadataRepository.initialize(metadataResolver);\n\t\t}\n\n\t\tprivate static final class SpringResource implements net.shibboleth.shared.resource.Resource {\n\n\t\t\tprivate final Resource resource;\n\n\t\t\tSpringResource(Resource resource) {\n\t\t\t\tthis.resource = resource;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean exists() {\n\t\t\t\treturn this.resource.exists();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isReadable() {\n\t\t\t\treturn this.resource.isReadable();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isOpen() {\n\t\t\t\treturn this.resource.isOpen();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic URL getURL() throws IOException {\n\t\t\t\treturn this.resource.getURL();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic URI getURI() throws IOException {\n\t\t\t\treturn this.resource.getURI();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic File getFile() throws IOException {\n\t\t\t\treturn this.resource.getFile();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic InputStream getInputStream() throws IOException {\n\t\t\t\treturn this.resource.getInputStream();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long contentLength() throws IOException {\n\t\t\t\treturn this.resource.contentLength();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long lastModified() throws IOException {\n\t\t\t\treturn this.resource.lastModified();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic net.shibboleth.shared.resource.Resource createRelativeResource(String relativePath)\n\t\t\t\t\tthrows IOException {\n\t\t\t\treturn new SpringResource(this.resource.createRelative(relativePath));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable String getFilename() {\n\t\t\t\treturn this.resource.getFilename();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String getDescription() {\n\t\t\t\treturn this.resource.getDescription();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tprivate static final class CriteriaSetResolverWrapper extends MetadataResolverAdapter {\n\n\t\tCriteriaSetResolverWrapper(MetadataResolver metadataResolver) {\n\t\t\tsuper(metadataResolver);\n\t\t}\n\n\t\t@Override\n\t\t@Nullable EntityDescriptor resolveSingle(EntityIdCriterion entityId) throws Exception {\n\t\t\treturn super.metadataResolver.resolveSingle(new CriteriaSet(entityId));\n\t\t}\n\n\t\t@Override\n\t\tIterable<EntityDescriptor> resolve(EntityRoleCriterion role) throws Exception {\n\t\t\treturn super.metadataResolver.resolve(new CriteriaSet(role));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/registration/OpenSaml5Template.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.core.xml.util.XMLObjectSupport;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.criterion.ProtocolCriterion;\nimport org.opensaml.saml.ext.saml2delrestrict.Delegate;\nimport org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;\nimport org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;\nimport org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;\nimport org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialResolver;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;\nimport org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.security.criteria.UsageCriterion;\nimport org.opensaml.security.x509.BasicX509Credential;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.SignatureSigningParametersResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;\nimport org.opensaml.xmlsec.crypto.XMLSigningUtil;\nimport org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.DecryptionException;\nimport org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;\nimport org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.opensaml.xmlsec.signature.Signature;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * For internal use only. Subject to breaking changes at any time.\n */\n@NullMarked\nfinal class OpenSaml5Template implements OpenSamlOperations {\n\n\tprivate static final Log logger = LogFactory.getLog(OpenSaml5Template.class);\n\n\t@Override\n\tpublic <T extends XMLObject> T build(QName elementName) {\n\t\tXMLObjectBuilder<?> builder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(elementName);\n\t\tif (builder == null) {\n\t\t\tthrow new Saml2Exception(\"Unable to resolve Builder for \" + elementName);\n\t\t}\n\t\treturn (T) builder.buildObject(elementName);\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(String serialized) {\n\t\treturn deserialize(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(InputStream serialized) {\n\t\ttry {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tDocument document = pool.parse(serialized);\n\t\t\tElement element = document.getDocumentElement();\n\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\tif (unmarshaller == null) {\n\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t}\n\t\t\treturn (T) unmarshaller.unmarshall(element);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(XMLObject object) {\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\tAssert.notNull(marshaller, \"Marshaller for \" + object.getElementQName() + \" must be configured\");\n\t\ttry {\n\t\t\treturn serialize(marshaller.marshall(object));\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(Element element) {\n\t\treturn new OpenSaml5SerializationConfigurer(element);\n\t}\n\n\t@Override\n\tpublic OpenSaml5SignatureConfigurer withSigningKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5SignatureConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5VerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5VerificationConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5DecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5DecryptionConfigurer(credentials);\n\t}\n\n\tOpenSaml5Template() {\n\n\t}\n\n\tstatic final class OpenSaml5SerializationConfigurer\n\t\t\timplements SerializationConfigurer<OpenSaml5SerializationConfigurer> {\n\n\t\tprivate final Element element;\n\n\t\tboolean pretty;\n\n\t\tOpenSaml5SerializationConfigurer(Element element) {\n\t\t\tthis.element = element;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SerializationConfigurer prettyPrint(boolean pretty) {\n\t\t\tthis.pretty = pretty;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String serialize() {\n\t\t\tif (this.pretty) {\n\t\t\t\treturn SerializeSupport.prettyPrintXML(this.element);\n\t\t\t}\n\t\t\treturn SerializeSupport.nodeToString(this.element);\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5SignatureConfigurer implements SignatureConfigurer<OpenSaml5SignatureConfigurer> {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate final Map<String, String> components = new LinkedHashMap<>();\n\n\t\tprivate List<String> algs = List.of(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\n\t\tOpenSaml5SignatureConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SignatureConfigurer algorithms(List<String> algs) {\n\t\t\tthis.algs = algs;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic <O extends SignableXMLObject> O sign(O object) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\ttry {\n\t\t\t\tSignatureSupport.signObject(object, parameters);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> sign(Map<String, String> params) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\tthis.components.putAll(params);\n\t\t\tCredential credential = parameters.getSigningCredential();\n\t\t\tAssert.notNull(credential, \"credential cannot be null when signing a SAML payload\");\n\t\t\tString algorithmUri = parameters.getSignatureAlgorithm();\n\t\t\tAssert.notNull(algorithmUri, \"algorithmUri cannot be null when signing a SAML payload\");\n\t\t\tthis.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String> component : this.components.entrySet()) {\n\t\t\t\tbuilder.queryParam(component.getKey(),\n\t\t\t\t\t\tUriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = builder.build(true).toString().substring(1);\n\t\t\ttry {\n\t\t\t\tbyte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,\n\t\t\t\t\t\tqueryString.getBytes(StandardCharsets.UTF_8));\n\t\t\t\tString b64Signature = Saml2Utils.samlEncode(rawSignature);\n\t\t\t\tthis.components.put(Saml2ParameterNames.SIGNATURE, b64Signature);\n\t\t\t}\n\t\t\tcatch (SecurityException ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn this.components;\n\t\t}\n\n\t\tprivate SignatureSigningParameters resolveSigningParameters() {\n\t\t\tList<Credential> credentials = resolveSigningCredentials();\n\t\t\tList<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\t\tString canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;\n\t\t\tSignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();\n\t\t\tBasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();\n\t\t\tsigningConfiguration.setSigningCredentials(credentials);\n\t\t\tsigningConfiguration.setSignatureAlgorithms(this.algs);\n\t\t\tsigningConfiguration.setSignatureReferenceDigestMethods(digests);\n\t\t\tsigningConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);\n\t\t\tsigningConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());\n\t\t\tCriteriaSet criteria = new CriteriaSet(new SignatureSigningConfigurationCriterion(signingConfiguration));\n\t\t\ttry {\n\t\t\t\tSignatureSigningParameters parameters = resolver.resolveSingle(criteria);\n\t\t\t\tAssert.notNull(parameters, \"Failed to resolve any signing credential\");\n\t\t\t\treturn parameters;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {\n\t\t\tfinal NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();\n\n\t\t\tnamedManager.setUseDefaultManager(true);\n\t\t\tfinal KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();\n\n\t\t\t// Generator for X509Credentials\n\t\t\tfinal X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();\n\t\t\tx509Factory.setEmitEntityCertificate(true);\n\t\t\tx509Factory.setEmitEntityCertificateChain(true);\n\n\t\t\tdefaultManager.registerFactory(x509Factory);\n\n\t\t\treturn namedManager;\n\t\t}\n\n\t\tprivate List<Credential> resolveSigningCredentials() {\n\t\t\tList<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential x509Credential : this.credentials) {\n\t\t\t\tX509Certificate certificate = x509Credential.getCertificate();\n\t\t\t\tPrivateKey privateKey = x509Credential.getPrivateKey();\n\t\t\t\tBasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey);\n\t\t\t\tcredential.setUsageType(UsageType.SIGNING);\n\t\t\t\tcredentials.add(credential);\n\t\t\t}\n\t\t\treturn credentials;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5VerificationConfigurer implements VerificationConfigurer {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate @Nullable String entityId;\n\n\t\tOpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic VerificationConfigurer entityId(@Nullable String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate SignatureTrustEngine trustEngine(Collection<Saml2X509Credential> keys) {\n\t\t\tSet<Credential> credentials = new HashSet<>();\n\t\t\tfor (Saml2X509Credential key : keys) {\n\t\t\t\tBasicX509Credential cred = new BasicX509Credential(key.getCertificate());\n\t\t\t\tcred.setUsageType(UsageType.SIGNING);\n\t\t\t\tcred.setEntityId(this.entityId);\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tCredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);\n\t\t\treturn new ExplicitKeySignatureTrustEngine(credentialsResolver,\n\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t}\n\n\t\tprivate CriteriaSet verificationCriteria(Issuer issuer) {\n\t\t\tAssert.notNull(issuer.getValue(), \"required elements must have a value\");\n\t\t\treturn new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),\n\t\t\t\t\tnew EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),\n\t\t\t\t\tnew EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(SignableXMLObject signable) {\n\t\t\tif (signable instanceof StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"Response#ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"Response#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(response.getSignature(), \"Response#Signature cannot be null\");\n\t\t\t\treturn verifySignature(response.getID(), response.getIssuer(), response.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"Request#ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"Request#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(request.getSignature(), \"Request#Signature cannot be null\");\n\t\t\t\treturn verifySignature(request.getID(), request.getIssuer(), request.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof Assertion assertion) {\n\t\t\t\tAssert.notNull(assertion.getID(), \"Assertion#ID cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getIssuer(), \"Assertion#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getSignature(), \"Assertion#Signature cannot be null\");\n\t\t\t\treturn verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());\n\t\t\t}\n\t\t\tthrow new Saml2Exception(\"Unsupported object of type: \" + signable.getClass().getName());\n\t\t}\n\n\t\tprivate Collection<Saml2Error> verifySignature(String id, Issuer issuer, Signature signature) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(issuer);\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tSAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();\n\t\t\ttry {\n\t\t\t\tprofileValidator.validate(signature);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, criteria)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\treturn errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(RedirectParameters parameters) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(parameters.getIssuer());\n\t\t\tif (parameters.getAlgorithm() == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature algorithm for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tbyte[] signature = parameters.getSignature();\n\t\t\tif (signature == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tString algorithmUri = parameters.getAlgorithm();\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]: \"));\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5DecryptionConfigurer implements DecryptionConfigurer {\n\n\t\tprivate static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(\n\t\t\t\tArrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),\n\t\t\t\t\t\tnew SimpleRetrievalMethodEncryptedKeyResolver()));\n\n\t\tprivate final Decrypter decrypter;\n\n\t\tOpenSaml5DecryptionConfigurer(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tthis.decrypter = decrypter(decryptionCredentials);\n\t\t}\n\n\t\tprivate static Decrypter decrypter(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tCollection<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential key : decryptionCredentials) {\n\t\t\t\tCredential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey());\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tKeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);\n\t\t\tDecrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);\n\t\t\tdecrypter.setRootInNewDocument(true);\n\t\t\treturn decrypter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decrypt(XMLObject object) {\n\t\t\tif (object instanceof Response response) {\n\t\t\t\tdecryptResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (object instanceof Assertion assertion) {\n\t\t\t\tdecryptAssertion(assertion);\n\t\t\t}\n\t\t\tif (object instanceof LogoutRequest request) {\n\t\t\t\tdecryptLogoutRequest(request);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * The methods that follow are adapted from OpenSAML's {@link DecryptAssertions},\n\t\t * {@link DecryptNameIDs}, and {@link DecryptAttributes}.\n\t\t *\n\t\t * <p>The reason that these OpenSAML classes are not used directly is because they\n\t\t * reference {@link javax.servlet.http.HttpServletRequest} which is a lower\n\t\t * Servlet API version than what Spring Security SAML uses.\n\t\t *\n\t\t * If OpenSAML 5 updates to {@link jakarta.servlet.http.HttpServletRequest}, then\n\t\t * this arrangement can be revisited.\n\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());\n\t\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\t\td.setNameID(decrypted);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAttributes(AttributeStatement statement) {\n\t\t\tCollection<Attribute> decrypteds = new ArrayList<>();\n\t\t\tfor (EncryptedAttribute encrypted : statement.getEncryptedAttributes()) {\n\t\t\t\ttry {\n\t\t\t\t\tAttribute decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatement.getAttributes().addAll(decrypteds);\n\t\t}\n\n\t\tprivate void decryptSubject(@Nullable Subject subject) {\n\t\t\tif (subject != null) {\n\t\t\t\tif (subject.getEncryptedID() != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(subject.getEncryptedID());\n\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\tsubject.setNameID(decrypted);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (final SubjectConfirmation sc : subject.getSubjectConfirmations()) {\n\t\t\t\t\tif (sc.getEncryptedID() != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(sc.getEncryptedID());\n\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\tsc.setNameID(decrypted);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptLogoutRequest(LogoutRequest request) {\n\t\t\tif (request.getEncryptedID() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(request.getEncryptedID());\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\trequest.setNameID(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/OpenSaml5AuthenticationTokenConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationConverter} that generates a {@link Saml2AuthenticationToken}\n * appropriate for authenticated a SAML 2.0 Assertion against an\n * {@link org.springframework.security.authentication.AuthenticationManager}.\n *\n * @author Josh Cummings\n * @since 6.1\n */\n@NullMarked\npublic final class OpenSaml5AuthenticationTokenConverter implements AuthenticationConverter {\n\n\tprivate final BaseOpenSamlAuthenticationTokenConverter delegate;\n\n\t/**\n\t * Constructs a {@link OpenSaml5AuthenticationTokenConverter} given a repository for\n\t * {@link RelyingPartyRegistration}s\n\t * @param registrations the repository for {@link RelyingPartyRegistration}s\n\t * {@link RelyingPartyRegistration}s\n\t */\n\tpublic OpenSaml5AuthenticationTokenConverter(RelyingPartyRegistrationRepository registrations) {\n\t\tAssert.notNull(registrations, \"relyingPartyRegistrationRepository cannot be null\");\n\t\tthis.delegate = new BaseOpenSamlAuthenticationTokenConverter(registrations, new OpenSaml5Template());\n\t}\n\n\t/**\n\t * Resolve an authentication request from the given {@link HttpServletRequest}.\n\t *\n\t * <p>\n\t * First uses the configured {@link RequestMatcher} to deduce whether an\n\t * authentication request is being made and optionally for which\n\t * {@code registrationId}.\n\t *\n\t * <p>\n\t * If there is an associated {@code <saml2:AuthnRequest>}, then the\n\t * {@code registrationId} is looked up and used.\n\t *\n\t * <p>\n\t * If a {@code registrationId} is found in the request, then it is looked up and used.\n\t * In that case, if none is found a {@link Saml2AuthenticationException} is thrown.\n\t *\n\t * <p>\n\t * Finally, if no {@code registrationId} is found in the request, then the code\n\t * attempts to resolve the {@link RelyingPartyRegistration} from the SAML Response's\n\t * Issuer.\n\t * @param request the HTTP request\n\t * @return the {@link Saml2AuthenticationToken} authentication request\n\t * @throws Saml2AuthenticationException if the {@link RequestMatcher} specifies a\n\t * non-existent {@code registrationId}\n\t */\n\t@Override\n\tpublic @Nullable Saml2AuthenticationToken convert(HttpServletRequest request) {\n\t\treturn this.delegate.convert(request);\n\t}\n\n\t/**\n\t * Use the given {@link Saml2AuthenticationRequestRepository} to load authentication\n\t * request.\n\t * @param authenticationRequestRepository the\n\t * {@link Saml2AuthenticationRequestRepository} to use\n\t */\n\tpublic void setAuthenticationRequestRepository(\n\t\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {\n\t\tAssert.notNull(authenticationRequestRepository, \"authenticationRequestRepository cannot be null\");\n\t\tthis.delegate.setAuthenticationRequestRepository(authenticationRequestRepository);\n\t}\n\n\t/**\n\t * Use the given {@link RequestMatcher} to match the request.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.delegate.setRequestMatcher(requestMatcher);\n\t}\n\n\t/**\n\t * Use the given {@code shouldConvertGetRequests} to convert {@code GET} requests.\n\t * Default is {@code true}.\n\t * @param shouldConvertGetRequests the {@code shouldConvertGetRequests} to use\n\t * @since 7.0\n\t */\n\tpublic void setShouldConvertGetRequests(boolean shouldConvertGetRequests) {\n\t\tthis.delegate.setShouldConvertGetRequests(shouldConvertGetRequests);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/OpenSaml5Template.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.core.xml.util.XMLObjectSupport;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.criterion.ProtocolCriterion;\nimport org.opensaml.saml.ext.saml2delrestrict.Delegate;\nimport org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;\nimport org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;\nimport org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;\nimport org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialResolver;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;\nimport org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.security.criteria.UsageCriterion;\nimport org.opensaml.security.x509.BasicX509Credential;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.SignatureSigningParametersResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;\nimport org.opensaml.xmlsec.crypto.XMLSigningUtil;\nimport org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.DecryptionException;\nimport org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;\nimport org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.opensaml.xmlsec.signature.Signature;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * For internal use only. Subject to breaking changes at any time.\n */\n@NullMarked\nfinal class OpenSaml5Template implements OpenSamlOperations {\n\n\tprivate static final Log logger = LogFactory.getLog(OpenSaml5Template.class);\n\n\t@Override\n\tpublic <T extends XMLObject> T build(QName elementName) {\n\t\tXMLObjectBuilder<?> builder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(elementName);\n\t\tif (builder == null) {\n\t\t\tthrow new Saml2Exception(\"Unable to resolve Builder for \" + elementName);\n\t\t}\n\t\treturn (T) builder.buildObject(elementName);\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(String serialized) {\n\t\treturn deserialize(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(InputStream serialized) {\n\t\ttry {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tDocument document = pool.parse(serialized);\n\t\t\tElement element = document.getDocumentElement();\n\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\tif (unmarshaller == null) {\n\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t}\n\t\t\treturn (T) unmarshaller.unmarshall(element);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(XMLObject object) {\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\tAssert.notNull(marshaller, \"Marshaller for \" + object.getElementQName() + \" must be configured\");\n\t\ttry {\n\t\t\treturn serialize(marshaller.marshall(object));\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(Element element) {\n\t\treturn new OpenSaml5SerializationConfigurer(element);\n\t}\n\n\t@Override\n\tpublic OpenSaml5SignatureConfigurer withSigningKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5SignatureConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5VerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5VerificationConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5DecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5DecryptionConfigurer(credentials);\n\t}\n\n\tOpenSaml5Template() {\n\n\t}\n\n\tstatic final class OpenSaml5SerializationConfigurer\n\t\t\timplements SerializationConfigurer<OpenSaml5SerializationConfigurer> {\n\n\t\tprivate final Element element;\n\n\t\tboolean pretty;\n\n\t\tOpenSaml5SerializationConfigurer(Element element) {\n\t\t\tthis.element = element;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SerializationConfigurer prettyPrint(boolean pretty) {\n\t\t\tthis.pretty = pretty;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String serialize() {\n\t\t\tif (this.pretty) {\n\t\t\t\treturn SerializeSupport.prettyPrintXML(this.element);\n\t\t\t}\n\t\t\treturn SerializeSupport.nodeToString(this.element);\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5SignatureConfigurer implements SignatureConfigurer<OpenSaml5SignatureConfigurer> {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate final Map<String, String> components = new LinkedHashMap<>();\n\n\t\tprivate List<String> algs = List.of(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\n\t\tOpenSaml5SignatureConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SignatureConfigurer algorithms(List<String> algs) {\n\t\t\tthis.algs = algs;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic <O extends SignableXMLObject> O sign(O object) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\ttry {\n\t\t\t\tSignatureSupport.signObject(object, parameters);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> sign(Map<String, String> params) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\tthis.components.putAll(params);\n\t\t\tCredential credential = parameters.getSigningCredential();\n\t\t\tAssert.notNull(credential, \"credential cannot be null when signing a SAML payload\");\n\t\t\tString algorithmUri = parameters.getSignatureAlgorithm();\n\t\t\tAssert.notNull(algorithmUri, \"algorithmUri cannot be null when signing a SAML payload\");\n\t\t\tthis.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String> component : this.components.entrySet()) {\n\t\t\t\tbuilder.queryParam(component.getKey(),\n\t\t\t\t\t\tUriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = builder.build(true).toString().substring(1);\n\t\t\ttry {\n\t\t\t\tbyte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,\n\t\t\t\t\t\tqueryString.getBytes(StandardCharsets.UTF_8));\n\t\t\t\tString b64Signature = Saml2Utils.samlEncode(rawSignature);\n\t\t\t\tthis.components.put(Saml2ParameterNames.SIGNATURE, b64Signature);\n\t\t\t}\n\t\t\tcatch (SecurityException ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn this.components;\n\t\t}\n\n\t\tprivate SignatureSigningParameters resolveSigningParameters() {\n\t\t\tList<Credential> credentials = resolveSigningCredentials();\n\t\t\tList<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\t\tString canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;\n\t\t\tSignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();\n\t\t\tBasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();\n\t\t\tsigningConfiguration.setSigningCredentials(credentials);\n\t\t\tsigningConfiguration.setSignatureAlgorithms(this.algs);\n\t\t\tsigningConfiguration.setSignatureReferenceDigestMethods(digests);\n\t\t\tsigningConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);\n\t\t\tsigningConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());\n\t\t\tCriteriaSet criteria = new CriteriaSet(new SignatureSigningConfigurationCriterion(signingConfiguration));\n\t\t\ttry {\n\t\t\t\tSignatureSigningParameters parameters = resolver.resolveSingle(criteria);\n\t\t\t\tAssert.notNull(parameters, \"Failed to resolve any signing credential\");\n\t\t\t\treturn parameters;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {\n\t\t\tfinal NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();\n\n\t\t\tnamedManager.setUseDefaultManager(true);\n\t\t\tfinal KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();\n\n\t\t\t// Generator for X509Credentials\n\t\t\tfinal X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();\n\t\t\tx509Factory.setEmitEntityCertificate(true);\n\t\t\tx509Factory.setEmitEntityCertificateChain(true);\n\n\t\t\tdefaultManager.registerFactory(x509Factory);\n\n\t\t\treturn namedManager;\n\t\t}\n\n\t\tprivate List<Credential> resolveSigningCredentials() {\n\t\t\tList<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential x509Credential : this.credentials) {\n\t\t\t\tX509Certificate certificate = x509Credential.getCertificate();\n\t\t\t\tPrivateKey privateKey = x509Credential.getPrivateKey();\n\t\t\t\tBasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey);\n\t\t\t\tcredential.setUsageType(UsageType.SIGNING);\n\t\t\t\tcredentials.add(credential);\n\t\t\t}\n\t\t\treturn credentials;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5VerificationConfigurer implements VerificationConfigurer {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate @Nullable String entityId;\n\n\t\tOpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic VerificationConfigurer entityId(@Nullable String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate SignatureTrustEngine trustEngine(Collection<Saml2X509Credential> keys) {\n\t\t\tSet<Credential> credentials = new HashSet<>();\n\t\t\tfor (Saml2X509Credential key : keys) {\n\t\t\t\tBasicX509Credential cred = new BasicX509Credential(key.getCertificate());\n\t\t\t\tcred.setUsageType(UsageType.SIGNING);\n\t\t\t\tcred.setEntityId(this.entityId);\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tCredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);\n\t\t\treturn new ExplicitKeySignatureTrustEngine(credentialsResolver,\n\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t}\n\n\t\tprivate CriteriaSet verificationCriteria(Issuer issuer) {\n\t\t\tAssert.notNull(issuer.getValue(), \"required elements must have a value\");\n\t\t\treturn new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),\n\t\t\t\t\tnew EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),\n\t\t\t\t\tnew EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(SignableXMLObject signable) {\n\t\t\tif (signable instanceof StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"Response#ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"Response#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(response.getSignature(), \"Response#Signature cannot be null\");\n\t\t\t\treturn verifySignature(response.getID(), response.getIssuer(), response.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"Request#ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"Request#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(request.getSignature(), \"Request#Signature cannot be null\");\n\t\t\t\treturn verifySignature(request.getID(), request.getIssuer(), request.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof Assertion assertion) {\n\t\t\t\tAssert.notNull(assertion.getID(), \"Assertion#ID cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getIssuer(), \"Assertion#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getSignature(), \"Assertion#Signature cannot be null\");\n\t\t\t\treturn verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());\n\t\t\t}\n\t\t\tthrow new Saml2Exception(\"Unsupported object of type: \" + signable.getClass().getName());\n\t\t}\n\n\t\tprivate Collection<Saml2Error> verifySignature(String id, Issuer issuer, Signature signature) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(issuer);\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tSAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();\n\t\t\ttry {\n\t\t\t\tprofileValidator.validate(signature);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, criteria)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\treturn errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(RedirectParameters parameters) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(parameters.getIssuer());\n\t\t\tif (parameters.getAlgorithm() == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature algorithm for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tbyte[] signature = parameters.getSignature();\n\t\t\tif (signature == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tString algorithmUri = parameters.getAlgorithm();\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]: \"));\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5DecryptionConfigurer implements DecryptionConfigurer {\n\n\t\tprivate static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(\n\t\t\t\tArrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),\n\t\t\t\t\t\tnew SimpleRetrievalMethodEncryptedKeyResolver()));\n\n\t\tprivate final Decrypter decrypter;\n\n\t\tOpenSaml5DecryptionConfigurer(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tthis.decrypter = decrypter(decryptionCredentials);\n\t\t}\n\n\t\tprivate static Decrypter decrypter(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tCollection<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential key : decryptionCredentials) {\n\t\t\t\tCredential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey());\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tKeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);\n\t\t\tDecrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);\n\t\t\tdecrypter.setRootInNewDocument(true);\n\t\t\treturn decrypter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decrypt(XMLObject object) {\n\t\t\tif (object instanceof Response response) {\n\t\t\t\tdecryptResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (object instanceof Assertion assertion) {\n\t\t\t\tdecryptAssertion(assertion);\n\t\t\t}\n\t\t\tif (object instanceof LogoutRequest request) {\n\t\t\t\tdecryptLogoutRequest(request);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * The methods that follow are adapted from OpenSAML's {@link DecryptAssertions},\n\t\t * {@link DecryptNameIDs}, and {@link DecryptAttributes}.\n\t\t *\n\t\t * <p>The reason that these OpenSAML classes are not used directly is because they\n\t\t * reference {@link javax.servlet.http.HttpServletRequest} which is a lower\n\t\t * Servlet API version than what Spring Security SAML uses.\n\t\t *\n\t\t * If OpenSAML 5 updates to {@link jakarta.servlet.http.HttpServletRequest}, then\n\t\t * this arrangement can be revisited.\n\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());\n\t\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\t\td.setNameID(decrypted);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAttributes(AttributeStatement statement) {\n\t\t\tCollection<Attribute> decrypteds = new ArrayList<>();\n\t\t\tfor (EncryptedAttribute encrypted : statement.getEncryptedAttributes()) {\n\t\t\t\ttry {\n\t\t\t\t\tAttribute decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatement.getAttributes().addAll(decrypteds);\n\t\t}\n\n\t\tprivate void decryptSubject(@Nullable Subject subject) {\n\t\t\tif (subject != null) {\n\t\t\t\tif (subject.getEncryptedID() != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(subject.getEncryptedID());\n\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\tsubject.setNameID(decrypted);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (final SubjectConfirmation sc : subject.getSubjectConfirmations()) {\n\t\t\t\t\tif (sc.getEncryptedID() != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(sc.getEncryptedID());\n\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\tsc.setNameID(decrypted);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptLogoutRequest(LogoutRequest request) {\n\t\t\tif (request.getEncryptedID() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(request.getEncryptedID());\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\trequest.setNameID(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml5AuthenticationRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.saml.saml2.core.AuthnRequest;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * A strategy for resolving a SAML 2.0 Authentication Request from the\n * {@link HttpServletRequest} using OpenSAML.\n *\n * @author Josh Cummings\n * @since 5.7\n */\n@NullMarked\npublic final class OpenSaml5AuthenticationRequestResolver implements Saml2AuthenticationRequestResolver {\n\n\tprivate final BaseOpenSamlAuthenticationRequestResolver delegate;\n\n\t/**\n\t * Construct an {@link OpenSaml5AuthenticationRequestResolver}\n\t * @param registrations a repository for relying and asserting party configuration\n\t * @since 6.1\n\t */\n\tpublic OpenSaml5AuthenticationRequestResolver(RelyingPartyRegistrationRepository registrations) {\n\t\tthis.delegate = new BaseOpenSamlAuthenticationRequestResolver((request, id) -> {\n\t\t\tif (id == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn registrations.findByRegistrationId(id);\n\t\t}, new OpenSaml5Template());\n\t}\n\n\t/**\n\t * Construct a {@link OpenSaml5AuthenticationRequestResolver}\n\t */\n\tpublic OpenSaml5AuthenticationRequestResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {\n\t\tthis.delegate = new BaseOpenSamlAuthenticationRequestResolver(relyingPartyRegistrationResolver,\n\t\t\t\tnew OpenSaml5Template());\n\t}\n\n\t@Override\n\tpublic <T extends AbstractSaml2AuthenticationRequest> @Nullable T resolve(HttpServletRequest request) {\n\t\treturn this.delegate.resolve(request);\n\t}\n\n\t/**\n\t * Set a {@link Consumer} for modifying the OpenSAML {@link AuthnRequest}\n\t * @param contextConsumer a consumer that accepts an {@link AuthnRequestContext}\n\t */\n\tpublic void setAuthnRequestCustomizer(Consumer<AuthnRequestContext> contextConsumer) {\n\t\tAssert.notNull(contextConsumer, \"contextConsumer cannot be null\");\n\t\tthis.delegate.setParametersConsumer(\n\t\t\t\t(parameters) -> contextConsumer.accept(new AuthnRequestContext(parameters.getRequest(),\n\t\t\t\t\t\tparameters.getRelyingPartyRegistration(), parameters.getAuthnRequest())));\n\t}\n\n\t/**\n\t * Set the {@link RequestMatcher} to use for setting the\n\t * {@link BaseOpenSamlAuthenticationRequestResolver#setRequestMatcher(RequestMatcher)}\n\t * (RequestMatcher)}\n\t * @param requestMatcher the {@link RequestMatcher} to identify authentication\n\t * requests.\n\t * @since 5.8\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.delegate.setRequestMatcher(requestMatcher);\n\t}\n\n\t/**\n\t * Use this {@link Clock} for generating the issued {@link Instant}\n\t * @param clock the {@link Clock} to use\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock must not be null\");\n\t\tthis.delegate.setClock(clock);\n\t}\n\n\t/**\n\t * Use this {@link Converter} to compute the RelayState\n\t * @param relayStateResolver the {@link Converter} to use\n\t * @since 5.8\n\t */\n\tpublic void setRelayStateResolver(Converter<HttpServletRequest, @Nullable String> relayStateResolver) {\n\t\tAssert.notNull(relayStateResolver, \"relayStateResolver cannot be null\");\n\t\tthis.delegate.setRelayStateResolver(relayStateResolver);\n\t}\n\n\tpublic static final class AuthnRequestContext {\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tprivate final AuthnRequest authnRequest;\n\n\t\tpublic AuthnRequestContext(HttpServletRequest request, RelyingPartyRegistration registration,\n\t\t\t\tAuthnRequest authnRequest) {\n\t\t\tthis.request = request;\n\t\t\tthis.registration = registration;\n\t\t\tthis.authnRequest = authnRequest;\n\t\t}\n\n\t\tpublic HttpServletRequest getRequest() {\n\t\t\treturn this.request;\n\t\t}\n\n\t\tpublic RelyingPartyRegistration getRelyingPartyRegistration() {\n\t\t\treturn this.registration;\n\t\t}\n\n\t\tpublic AuthnRequest getAuthnRequest() {\n\t\t\treturn this.authnRequest;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml5Template.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.core.xml.util.XMLObjectSupport;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.criterion.ProtocolCriterion;\nimport org.opensaml.saml.ext.saml2delrestrict.Delegate;\nimport org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;\nimport org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;\nimport org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;\nimport org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialResolver;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;\nimport org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.security.criteria.UsageCriterion;\nimport org.opensaml.security.x509.BasicX509Credential;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.SignatureSigningParametersResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;\nimport org.opensaml.xmlsec.crypto.XMLSigningUtil;\nimport org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.DecryptionException;\nimport org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;\nimport org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.opensaml.xmlsec.signature.Signature;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * For internal use only. Subject to breaking changes at any time.\n */\n@NullMarked\nfinal class OpenSaml5Template implements OpenSamlOperations {\n\n\tprivate static final Log logger = LogFactory.getLog(OpenSaml5Template.class);\n\n\t@Override\n\tpublic <T extends XMLObject> T build(QName elementName) {\n\t\tXMLObjectBuilder<?> builder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(elementName);\n\t\tif (builder == null) {\n\t\t\tthrow new Saml2Exception(\"Unable to resolve Builder for \" + elementName);\n\t\t}\n\t\treturn (T) builder.buildObject(elementName);\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(String serialized) {\n\t\treturn deserialize(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(InputStream serialized) {\n\t\ttry {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tDocument document = pool.parse(serialized);\n\t\t\tElement element = document.getDocumentElement();\n\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\tif (unmarshaller == null) {\n\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t}\n\t\t\treturn (T) unmarshaller.unmarshall(element);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(XMLObject object) {\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\tAssert.notNull(marshaller, \"Marshaller for \" + object.getElementQName() + \" must be configured\");\n\t\ttry {\n\t\t\treturn serialize(marshaller.marshall(object));\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(Element element) {\n\t\treturn new OpenSaml5SerializationConfigurer(element);\n\t}\n\n\t@Override\n\tpublic OpenSaml5SignatureConfigurer withSigningKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5SignatureConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5VerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5VerificationConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5DecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5DecryptionConfigurer(credentials);\n\t}\n\n\tOpenSaml5Template() {\n\n\t}\n\n\tstatic final class OpenSaml5SerializationConfigurer\n\t\t\timplements SerializationConfigurer<OpenSaml5SerializationConfigurer> {\n\n\t\tprivate final Element element;\n\n\t\tboolean pretty;\n\n\t\tOpenSaml5SerializationConfigurer(Element element) {\n\t\t\tthis.element = element;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SerializationConfigurer prettyPrint(boolean pretty) {\n\t\t\tthis.pretty = pretty;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String serialize() {\n\t\t\tif (this.pretty) {\n\t\t\t\treturn SerializeSupport.prettyPrintXML(this.element);\n\t\t\t}\n\t\t\treturn SerializeSupport.nodeToString(this.element);\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5SignatureConfigurer implements SignatureConfigurer<OpenSaml5SignatureConfigurer> {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate final Map<String, String> components = new LinkedHashMap<>();\n\n\t\tprivate List<String> algs = List.of(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\n\t\tOpenSaml5SignatureConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SignatureConfigurer algorithms(List<String> algs) {\n\t\t\tthis.algs = algs;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic <O extends SignableXMLObject> O sign(O object) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\ttry {\n\t\t\t\tSignatureSupport.signObject(object, parameters);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> sign(Map<String, String> params) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\tthis.components.putAll(params);\n\t\t\tCredential credential = parameters.getSigningCredential();\n\t\t\tAssert.notNull(credential, \"credential cannot be null when signing a SAML payload\");\n\t\t\tString algorithmUri = parameters.getSignatureAlgorithm();\n\t\t\tAssert.notNull(algorithmUri, \"algorithmUri cannot be null when signing a SAML payload\");\n\t\t\tthis.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String> component : this.components.entrySet()) {\n\t\t\t\tbuilder.queryParam(component.getKey(),\n\t\t\t\t\t\tUriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = builder.build(true).toString().substring(1);\n\t\t\ttry {\n\t\t\t\tbyte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,\n\t\t\t\t\t\tqueryString.getBytes(StandardCharsets.UTF_8));\n\t\t\t\tString b64Signature = Saml2Utils.samlEncode(rawSignature);\n\t\t\t\tthis.components.put(Saml2ParameterNames.SIGNATURE, b64Signature);\n\t\t\t}\n\t\t\tcatch (SecurityException ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn this.components;\n\t\t}\n\n\t\tprivate SignatureSigningParameters resolveSigningParameters() {\n\t\t\tList<Credential> credentials = resolveSigningCredentials();\n\t\t\tList<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\t\tString canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;\n\t\t\tSignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();\n\t\t\tBasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();\n\t\t\tsigningConfiguration.setSigningCredentials(credentials);\n\t\t\tsigningConfiguration.setSignatureAlgorithms(this.algs);\n\t\t\tsigningConfiguration.setSignatureReferenceDigestMethods(digests);\n\t\t\tsigningConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);\n\t\t\tsigningConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());\n\t\t\tCriteriaSet criteria = new CriteriaSet(new SignatureSigningConfigurationCriterion(signingConfiguration));\n\t\t\ttry {\n\t\t\t\tSignatureSigningParameters parameters = resolver.resolveSingle(criteria);\n\t\t\t\tAssert.notNull(parameters, \"Failed to resolve any signing credential\");\n\t\t\t\treturn parameters;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {\n\t\t\tfinal NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();\n\n\t\t\tnamedManager.setUseDefaultManager(true);\n\t\t\tfinal KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();\n\n\t\t\t// Generator for X509Credentials\n\t\t\tfinal X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();\n\t\t\tx509Factory.setEmitEntityCertificate(true);\n\t\t\tx509Factory.setEmitEntityCertificateChain(true);\n\n\t\t\tdefaultManager.registerFactory(x509Factory);\n\n\t\t\treturn namedManager;\n\t\t}\n\n\t\tprivate List<Credential> resolveSigningCredentials() {\n\t\t\tList<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential x509Credential : this.credentials) {\n\t\t\t\tX509Certificate certificate = x509Credential.getCertificate();\n\t\t\t\tPrivateKey privateKey = x509Credential.getPrivateKey();\n\t\t\t\tBasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey);\n\t\t\t\tcredential.setUsageType(UsageType.SIGNING);\n\t\t\t\tcredentials.add(credential);\n\t\t\t}\n\t\t\treturn credentials;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5VerificationConfigurer implements VerificationConfigurer {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate @Nullable String entityId;\n\n\t\tOpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic VerificationConfigurer entityId(@Nullable String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate SignatureTrustEngine trustEngine(Collection<Saml2X509Credential> keys) {\n\t\t\tSet<Credential> credentials = new HashSet<>();\n\t\t\tfor (Saml2X509Credential key : keys) {\n\t\t\t\tBasicX509Credential cred = new BasicX509Credential(key.getCertificate());\n\t\t\t\tcred.setUsageType(UsageType.SIGNING);\n\t\t\t\tcred.setEntityId(this.entityId);\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tCredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);\n\t\t\treturn new ExplicitKeySignatureTrustEngine(credentialsResolver,\n\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t}\n\n\t\tprivate CriteriaSet verificationCriteria(Issuer issuer) {\n\t\t\tAssert.notNull(issuer.getValue(), \"required elements must have a value\");\n\t\t\treturn new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),\n\t\t\t\t\tnew EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),\n\t\t\t\t\tnew EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(SignableXMLObject signable) {\n\t\t\tif (signable instanceof StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"Response#ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"Response#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(response.getSignature(), \"Response#Signature cannot be null\");\n\t\t\t\treturn verifySignature(response.getID(), response.getIssuer(), response.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"Request#ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"Request#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(request.getSignature(), \"Request#Signature cannot be null\");\n\t\t\t\treturn verifySignature(request.getID(), request.getIssuer(), request.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof Assertion assertion) {\n\t\t\t\tAssert.notNull(assertion.getID(), \"Assertion#ID cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getIssuer(), \"Assertion#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getSignature(), \"Assertion#Signature cannot be null\");\n\t\t\t\treturn verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());\n\t\t\t}\n\t\t\tthrow new Saml2Exception(\"Unsupported object of type: \" + signable.getClass().getName());\n\t\t}\n\n\t\tprivate Collection<Saml2Error> verifySignature(String id, Issuer issuer, Signature signature) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(issuer);\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tSAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();\n\t\t\ttry {\n\t\t\t\tprofileValidator.validate(signature);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, criteria)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\treturn errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(RedirectParameters parameters) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(parameters.getIssuer());\n\t\t\tif (parameters.getAlgorithm() == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature algorithm for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tbyte[] signature = parameters.getSignature();\n\t\t\tif (signature == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tString algorithmUri = parameters.getAlgorithm();\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]: \"));\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5DecryptionConfigurer implements DecryptionConfigurer {\n\n\t\tprivate static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(\n\t\t\t\tArrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),\n\t\t\t\t\t\tnew SimpleRetrievalMethodEncryptedKeyResolver()));\n\n\t\tprivate final Decrypter decrypter;\n\n\t\tOpenSaml5DecryptionConfigurer(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tthis.decrypter = decrypter(decryptionCredentials);\n\t\t}\n\n\t\tprivate static Decrypter decrypter(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tCollection<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential key : decryptionCredentials) {\n\t\t\t\tCredential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey());\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tKeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);\n\t\t\tDecrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);\n\t\t\tdecrypter.setRootInNewDocument(true);\n\t\t\treturn decrypter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decrypt(XMLObject object) {\n\t\t\tif (object instanceof Response response) {\n\t\t\t\tdecryptResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (object instanceof Assertion assertion) {\n\t\t\t\tdecryptAssertion(assertion);\n\t\t\t}\n\t\t\tif (object instanceof LogoutRequest request) {\n\t\t\t\tdecryptLogoutRequest(request);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * The methods that follow are adapted from OpenSAML's {@link DecryptAssertions},\n\t\t * {@link DecryptNameIDs}, and {@link DecryptAttributes}.\n\t\t *\n\t\t * <p>The reason that these OpenSAML classes are not used directly is because they\n\t\t * reference {@link javax.servlet.http.HttpServletRequest} which is a lower\n\t\t * Servlet API version than what Spring Security SAML uses.\n\t\t *\n\t\t * If OpenSAML 5 updates to {@link jakarta.servlet.http.HttpServletRequest}, then\n\t\t * this arrangement can be revisited.\n\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());\n\t\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\t\td.setNameID(decrypted);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAttributes(AttributeStatement statement) {\n\t\t\tCollection<Attribute> decrypteds = new ArrayList<>();\n\t\t\tfor (EncryptedAttribute encrypted : statement.getEncryptedAttributes()) {\n\t\t\t\ttry {\n\t\t\t\t\tAttribute decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatement.getAttributes().addAll(decrypteds);\n\t\t}\n\n\t\tprivate void decryptSubject(@Nullable Subject subject) {\n\t\t\tif (subject != null) {\n\t\t\t\tif (subject.getEncryptedID() != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(subject.getEncryptedID());\n\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\tsubject.setNameID(decrypted);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (final SubjectConfirmation sc : subject.getSubjectConfirmations()) {\n\t\t\t\t\tif (sc.getEncryptedID() != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(sc.getEncryptedID());\n\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\tsc.setNameID(decrypted);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptLogoutRequest(LogoutRequest request) {\n\t\t\tif (request.getEncryptedID() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(request.getEncryptedID());\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\trequest.setNameID(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Saml2LogoutRequestResolver} for resolving SAML 2.0 Logout Requests with\n * OpenSAML 5\n *\n * @author Josh Cummings\n * @author Gerhard Haege\n * @since 5.6\n */\n@NullMarked\npublic final class OpenSaml5LogoutRequestResolver implements Saml2LogoutRequestResolver {\n\n\tprivate final BaseOpenSamlLogoutRequestResolver delegate;\n\n\tpublic OpenSaml5LogoutRequestResolver(RelyingPartyRegistrationRepository registrations) {\n\t\tthis((request, id) -> {\n\t\t\tif (id == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn registrations.findByRegistrationId(id);\n\t\t});\n\t}\n\n\t/**\n\t * Construct a {@link OpenSaml5LogoutRequestResolver}\n\t */\n\tpublic OpenSaml5LogoutRequestResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {\n\t\tthis.delegate = new BaseOpenSamlLogoutRequestResolver(relyingPartyRegistrationResolver,\n\t\t\t\tnew OpenSaml5Template());\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutRequest resolve(HttpServletRequest request, Authentication authentication) {\n\t\treturn this.delegate.resolve(request, authentication);\n\t}\n\n\t/**\n\t * Set a {@link Consumer} for modifying the OpenSAML {@link LogoutRequest}\n\t * @param parametersConsumer a consumer that accepts an\n\t * {@link LogoutRequestParameters}\n\t */\n\tpublic void setParametersConsumer(Consumer<LogoutRequestParameters> parametersConsumer) {\n\t\tAssert.notNull(parametersConsumer, \"parametersConsumer cannot be null\");\n\t\tthis.delegate\n\t\t\t.setParametersConsumer((parameters) -> parametersConsumer.accept(new LogoutRequestParameters(parameters)));\n\t}\n\n\t/**\n\t * Use this {@link Clock} for determining the issued {@link Instant}\n\t * @param clock the {@link Clock} to use\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock must not be null\");\n\t\tthis.delegate.setClock(clock);\n\t}\n\n\t/**\n\t * Use this {@link Converter} to compute the RelayState\n\t * @param relayStateResolver the {@link Converter} to use\n\t * @since 6.1\n\t */\n\tpublic void setRelayStateResolver(Converter<HttpServletRequest, String> relayStateResolver) {\n\t\tAssert.notNull(relayStateResolver, \"relayStateResolver cannot be null\");\n\t\tthis.delegate.setRelayStateResolver(relayStateResolver);\n\t}\n\n\tpublic static final class LogoutRequestParameters {\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tprivate final Authentication authentication;\n\n\t\tprivate final LogoutRequest logoutRequest;\n\n\t\tpublic LogoutRequestParameters(HttpServletRequest request, RelyingPartyRegistration registration,\n\t\t\t\tAuthentication authentication, LogoutRequest logoutRequest) {\n\t\t\tthis.request = request;\n\t\t\tthis.registration = registration;\n\t\t\tthis.authentication = authentication;\n\t\t\tthis.logoutRequest = logoutRequest;\n\t\t}\n\n\t\tLogoutRequestParameters(BaseOpenSamlLogoutRequestResolver.LogoutRequestParameters parameters) {\n\t\t\tthis(parameters.getRequest(), parameters.getRelyingPartyRegistration(), parameters.getAuthentication(),\n\t\t\t\t\tparameters.getLogoutRequest());\n\t\t}\n\n\t\tpublic HttpServletRequest getRequest() {\n\t\t\treturn this.request;\n\t\t}\n\n\t\tpublic RelyingPartyRegistration getRelyingPartyRegistration() {\n\t\t\treturn this.registration;\n\t\t}\n\n\t\tpublic Authentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\tpublic LogoutRequest getLogoutRequest() {\n\t\t\treturn this.logoutRequest;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutRequestValidatorParametersResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * An OpenSAML-based implementation of\n * {@link Saml2LogoutRequestValidatorParametersResolver}\n */\n@NullMarked\npublic final class OpenSaml5LogoutRequestValidatorParametersResolver\n\t\timplements Saml2LogoutRequestValidatorParametersResolver {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final BaseOpenSamlLogoutRequestValidatorParametersResolver delegate;\n\n\t/**\n\t * Constructs a {@link OpenSaml5LogoutRequestValidatorParametersResolver}\n\t */\n\tpublic OpenSaml5LogoutRequestValidatorParametersResolver(RelyingPartyRegistrationRepository registrations) {\n\t\tAssert.notNull(registrations, \"relyingPartyRegistrationRepository cannot be null\");\n\t\tthis.delegate = new BaseOpenSamlLogoutRequestValidatorParametersResolver(new OpenSaml5Template(),\n\t\t\t\tregistrations);\n\t}\n\n\t/**\n\t * Construct the parameters necessary for validating an asserting party's\n\t * {@code <saml2:LogoutRequest>} based on the given {@link HttpServletRequest}\n\t *\n\t * <p>\n\t * Uses the configured {@link RequestMatcher} to identify the processing request,\n\t * including looking for any indicated {@code registrationId}.\n\t *\n\t * <p>\n\t * If a {@code registrationId} is found in the request, it will attempt to use that,\n\t * erroring if no {@link RelyingPartyRegistration} is found.\n\t *\n\t * <p>\n\t * If no {@code registrationId} is found in the request, it will look for a currently\n\t * logged-in user and use the associated {@code registrationId}.\n\t *\n\t * <p>\n\t * In the event that neither the URL nor any logged in user could determine a\n\t * {@code registrationId}, this code then will try and derive a\n\t * {@link RelyingPartyRegistration} given the {@code <saml2:LogoutRequest>}'s\n\t * {@code Issuer} value.\n\t * @param request the HTTP request\n\t * @return a {@link Saml2LogoutRequestValidatorParameters} instance, or {@code null}\n\t * if one could not be resolved\n\t * @throws Saml2AuthenticationException if the {@link RequestMatcher} specifies a\n\t * non-existent {@code registrationId}\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutRequestValidatorParameters resolve(HttpServletRequest request,\n\t\t\t@Nullable Authentication authentication) {\n\t\treturn this.delegate.resolve(request, authentication);\n\t}\n\n\t/**\n\t * The request matcher to use to identify a request to process a\n\t * {@code <saml2:LogoutRequest>}. By default, checks for {@code /logout/saml2/slo} and\n\t * {@code /logout/saml2/slo/{registrationId}}.\n\t *\n\t * <p>\n\t * Generally speaking, the URL does not need to have a {@code registrationId} in it\n\t * since either it can be looked up from the active logged in user or it can be\n\t * derived through the {@code Issuer} in the {@code <saml2:LogoutRequest>}.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.delegate.setRequestMatcher(requestMatcher);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutResponseResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.time.Clock;\nimport java.time.Instant;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Saml2LogoutResponseResolver} for resolving SAML 2.0 Logout Responses with\n * OpenSAML 5\n *\n * @author Josh Cummings\n * @since 5.6\n */\n@NullMarked\npublic final class OpenSaml5LogoutResponseResolver implements Saml2LogoutResponseResolver {\n\n\tprivate final BaseOpenSamlLogoutResponseResolver delegate;\n\n\tpublic OpenSaml5LogoutResponseResolver(RelyingPartyRegistrationRepository registrations) {\n\t\tthis.delegate = new BaseOpenSamlLogoutResponseResolver(registrations, (request, id) -> {\n\t\t\tif (id == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn registrations.findByRegistrationId(id);\n\t\t}, new OpenSaml5Template());\n\t}\n\n\t/**\n\t * Construct a {@link OpenSaml5LogoutResponseResolver}\n\t */\n\tpublic OpenSaml5LogoutResponseResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {\n\t\tthis.delegate = new BaseOpenSamlLogoutResponseResolver(null, relyingPartyRegistrationResolver,\n\t\t\t\tnew OpenSaml5Template());\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication) {\n\t\treturn this.delegate.resolve(request, authentication);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic @Nullable Saml2LogoutResponse resolve(HttpServletRequest request, @Nullable Authentication authentication,\n\t\t\tSaml2AuthenticationException exception) {\n\t\treturn this.delegate.resolve(request, authentication, exception);\n\t}\n\n\t/**\n\t * Set a {@link Consumer} for modifying the OpenSAML {@link LogoutRequest}\n\t * @param parametersConsumer a consumer that accepts an\n\t * {@link OpenSaml5LogoutRequestResolver.LogoutRequestParameters}\n\t */\n\tpublic void setParametersConsumer(Consumer<LogoutResponseParameters> parametersConsumer) {\n\t\tAssert.notNull(parametersConsumer, \"parametersConsumer cannot be null\");\n\t\tthis.delegate\n\t\t\t.setParametersConsumer((parameters) -> parametersConsumer.accept(new LogoutResponseParameters(parameters)));\n\t}\n\n\t/**\n\t * Use this {@link Clock} for determining the issued {@link Instant}\n\t * @param clock the {@link Clock} to use\n\t */\n\tpublic void setClock(Clock clock) {\n\t\tAssert.notNull(clock, \"clock must not be null\");\n\t\tthis.delegate.setClock(clock);\n\t}\n\n\tpublic static final class LogoutResponseParameters {\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tprivate final RelyingPartyRegistration registration;\n\n\t\tprivate final @Nullable Authentication authentication;\n\n\t\tprivate final LogoutRequest logoutRequest;\n\n\t\tpublic LogoutResponseParameters(HttpServletRequest request, RelyingPartyRegistration registration,\n\t\t\t\t@Nullable Authentication authentication, LogoutRequest logoutRequest) {\n\t\t\tthis.request = request;\n\t\t\tthis.registration = registration;\n\t\t\tthis.authentication = authentication;\n\t\t\tthis.logoutRequest = logoutRequest;\n\t\t}\n\n\t\tLogoutResponseParameters(BaseOpenSamlLogoutResponseResolver.LogoutResponseParameters parameters) {\n\t\t\tthis(parameters.getRequest(), parameters.getRelyingPartyRegistration(), parameters.getAuthentication(),\n\t\t\t\t\tparameters.getLogoutRequest());\n\t\t}\n\n\t\tpublic HttpServletRequest getRequest() {\n\t\t\treturn this.request;\n\t\t}\n\n\t\tpublic RelyingPartyRegistration getRelyingPartyRegistration() {\n\t\t\treturn this.registration;\n\t\t}\n\n\t\tpublic @Nullable Authentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\tpublic LogoutRequest getLogoutRequest() {\n\t\t\treturn this.logoutRequest;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5Template.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.resolver.CriteriaSet;\nimport net.shibboleth.shared.xml.ParserPool;\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.criterion.EntityIdCriterion;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.XMLObjectBuilder;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.io.Unmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallerFactory;\nimport org.opensaml.core.xml.util.XMLObjectSupport;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.criterion.ProtocolCriterion;\nimport org.opensaml.saml.ext.saml2delrestrict.Delegate;\nimport org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;\nimport org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.Condition;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.RequestAbstractType;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.StatusResponseType;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.encryption.Decrypter;\nimport org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;\nimport org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;\nimport org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialResolver;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;\nimport org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;\nimport org.opensaml.security.credential.impl.CollectionCredentialResolver;\nimport org.opensaml.security.criteria.UsageCriterion;\nimport org.opensaml.security.x509.BasicX509Credential;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.SignatureSigningParametersResolver;\nimport org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;\nimport org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;\nimport org.opensaml.xmlsec.crypto.XMLSigningUtil;\nimport org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.DecryptionException;\nimport org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;\nimport org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;\nimport org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;\nimport org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;\nimport org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;\nimport org.opensaml.xmlsec.signature.SignableXMLObject;\nimport org.opensaml.xmlsec.signature.Signature;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\nimport org.opensaml.xmlsec.signature.support.SignatureTrustEngine;\nimport org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.UriUtils;\n\n/**\n * For internal use only. Subject to breaking changes at any time.\n */\n@NullMarked\nfinal class OpenSaml5Template implements OpenSamlOperations {\n\n\tprivate static final Log logger = LogFactory.getLog(OpenSaml5Template.class);\n\n\t@Override\n\tpublic <T extends XMLObject> T build(QName elementName) {\n\t\tXMLObjectBuilder<?> builder = XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(elementName);\n\t\tif (builder == null) {\n\t\t\tthrow new Saml2Exception(\"Unable to resolve Builder for \" + elementName);\n\t\t}\n\t\treturn (T) builder.buildObject(elementName);\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(String serialized) {\n\t\treturn deserialize(new ByteArrayInputStream(serialized.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Override\n\tpublic <T extends XMLObject> T deserialize(InputStream serialized) {\n\t\ttry {\n\t\t\tParserPool pool = XMLObjectProviderRegistrySupport.getParserPool();\n\t\t\tAssert.notNull(pool, \"ParserPool must be configured\");\n\t\t\tDocument document = pool.parse(serialized);\n\t\t\tElement element = document.getDocumentElement();\n\t\t\tUnmarshallerFactory factory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();\n\t\t\tUnmarshaller unmarshaller = factory.getUnmarshaller(element);\n\t\t\tif (unmarshaller == null) {\n\t\t\t\tthrow new Saml2Exception(\"Unsupported element of type \" + element.getTagName());\n\t\t\t}\n\t\t\treturn (T) unmarshaller.unmarshall(element);\n\t\t}\n\t\tcatch (Saml2Exception ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(\"Failed to deserialize payload\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(XMLObject object) {\n\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\tAssert.notNull(marshaller, \"Marshaller for \" + object.getElementQName() + \" must be configured\");\n\t\ttry {\n\t\t\treturn serialize(marshaller.marshall(object));\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic OpenSaml5SerializationConfigurer serialize(Element element) {\n\t\treturn new OpenSaml5SerializationConfigurer(element);\n\t}\n\n\t@Override\n\tpublic OpenSaml5SignatureConfigurer withSigningKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5SignatureConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5VerificationConfigurer withVerificationKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5VerificationConfigurer(credentials);\n\t}\n\n\t@Override\n\tpublic OpenSaml5DecryptionConfigurer withDecryptionKeys(Collection<Saml2X509Credential> credentials) {\n\t\treturn new OpenSaml5DecryptionConfigurer(credentials);\n\t}\n\n\tOpenSaml5Template() {\n\n\t}\n\n\tstatic final class OpenSaml5SerializationConfigurer\n\t\t\timplements SerializationConfigurer<OpenSaml5SerializationConfigurer> {\n\n\t\tprivate final Element element;\n\n\t\tboolean pretty;\n\n\t\tOpenSaml5SerializationConfigurer(Element element) {\n\t\t\tthis.element = element;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SerializationConfigurer prettyPrint(boolean pretty) {\n\t\t\tthis.pretty = pretty;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic String serialize() {\n\t\t\tif (this.pretty) {\n\t\t\t\treturn SerializeSupport.prettyPrintXML(this.element);\n\t\t\t}\n\t\t\treturn SerializeSupport.nodeToString(this.element);\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5SignatureConfigurer implements SignatureConfigurer<OpenSaml5SignatureConfigurer> {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate final Map<String, String> components = new LinkedHashMap<>();\n\n\t\tprivate List<String> algs = List.of(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\n\t\tOpenSaml5SignatureConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic OpenSaml5SignatureConfigurer algorithms(List<String> algs) {\n\t\t\tthis.algs = algs;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic <O extends SignableXMLObject> O sign(O object) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\ttry {\n\t\t\t\tSignatureSupport.signObject(object, parameters);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> sign(Map<String, String> params) {\n\t\t\tSignatureSigningParameters parameters = resolveSigningParameters();\n\t\t\tthis.components.putAll(params);\n\t\t\tCredential credential = parameters.getSigningCredential();\n\t\t\tAssert.notNull(credential, \"credential cannot be null when signing a SAML payload\");\n\t\t\tString algorithmUri = parameters.getSignatureAlgorithm();\n\t\t\tAssert.notNull(algorithmUri, \"algorithmUri cannot be null when signing a SAML payload\");\n\t\t\tthis.components.put(Saml2ParameterNames.SIG_ALG, algorithmUri);\n\t\t\tUriComponentsBuilder builder = UriComponentsBuilder.newInstance();\n\t\t\tfor (Map.Entry<String, String> component : this.components.entrySet()) {\n\t\t\t\tbuilder.queryParam(component.getKey(),\n\t\t\t\t\t\tUriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));\n\t\t\t}\n\t\t\tString queryString = builder.build(true).toString().substring(1);\n\t\t\ttry {\n\t\t\t\tbyte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri,\n\t\t\t\t\t\tqueryString.getBytes(StandardCharsets.UTF_8));\n\t\t\t\tString b64Signature = Saml2Utils.samlEncode(rawSignature);\n\t\t\t\tthis.components.put(Saml2ParameterNames.SIGNATURE, b64Signature);\n\t\t\t}\n\t\t\tcatch (SecurityException ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t\treturn this.components;\n\t\t}\n\n\t\tprivate SignatureSigningParameters resolveSigningParameters() {\n\t\t\tList<Credential> credentials = resolveSigningCredentials();\n\t\t\tList<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\t\tString canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;\n\t\t\tSignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();\n\t\t\tBasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration();\n\t\t\tsigningConfiguration.setSigningCredentials(credentials);\n\t\t\tsigningConfiguration.setSignatureAlgorithms(this.algs);\n\t\t\tsigningConfiguration.setSignatureReferenceDigestMethods(digests);\n\t\t\tsigningConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);\n\t\t\tsigningConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());\n\t\t\tCriteriaSet criteria = new CriteriaSet(new SignatureSigningConfigurationCriterion(signingConfiguration));\n\t\t\ttry {\n\t\t\t\tSignatureSigningParameters parameters = resolver.resolveSingle(criteria);\n\t\t\t\tAssert.notNull(parameters, \"Failed to resolve any signing credential\");\n\t\t\t\treturn parameters;\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {\n\t\t\tfinal NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();\n\n\t\t\tnamedManager.setUseDefaultManager(true);\n\t\t\tfinal KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();\n\n\t\t\t// Generator for X509Credentials\n\t\t\tfinal X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();\n\t\t\tx509Factory.setEmitEntityCertificate(true);\n\t\t\tx509Factory.setEmitEntityCertificateChain(true);\n\n\t\t\tdefaultManager.registerFactory(x509Factory);\n\n\t\t\treturn namedManager;\n\t\t}\n\n\t\tprivate List<Credential> resolveSigningCredentials() {\n\t\t\tList<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential x509Credential : this.credentials) {\n\t\t\t\tX509Certificate certificate = x509Credential.getCertificate();\n\t\t\t\tPrivateKey privateKey = x509Credential.getPrivateKey();\n\t\t\t\tBasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey);\n\t\t\t\tcredential.setUsageType(UsageType.SIGNING);\n\t\t\t\tcredentials.add(credential);\n\t\t\t}\n\t\t\treturn credentials;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5VerificationConfigurer implements VerificationConfigurer {\n\n\t\tprivate final Collection<Saml2X509Credential> credentials;\n\n\t\tprivate @Nullable String entityId;\n\n\t\tOpenSaml5VerificationConfigurer(Collection<Saml2X509Credential> credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic VerificationConfigurer entityId(@Nullable String entityId) {\n\t\t\tthis.entityId = entityId;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate SignatureTrustEngine trustEngine(Collection<Saml2X509Credential> keys) {\n\t\t\tSet<Credential> credentials = new HashSet<>();\n\t\t\tfor (Saml2X509Credential key : keys) {\n\t\t\t\tBasicX509Credential cred = new BasicX509Credential(key.getCertificate());\n\t\t\t\tcred.setUsageType(UsageType.SIGNING);\n\t\t\t\tcred.setEntityId(this.entityId);\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tCredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);\n\t\t\treturn new ExplicitKeySignatureTrustEngine(credentialsResolver,\n\t\t\t\t\tDefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());\n\t\t}\n\n\t\tprivate CriteriaSet verificationCriteria(Issuer issuer) {\n\t\t\tAssert.notNull(issuer.getValue(), \"required elements must have a value\");\n\t\t\treturn new CriteriaSet(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue())),\n\t\t\t\t\tnew EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS)),\n\t\t\t\t\tnew EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(SignableXMLObject signable) {\n\t\t\tif (signable instanceof StatusResponseType response) {\n\t\t\t\tAssert.notNull(response.getID(), \"Response#ID cannot be null\");\n\t\t\t\tAssert.notNull(response.getIssuer(), \"Response#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(response.getSignature(), \"Response#Signature cannot be null\");\n\t\t\t\treturn verifySignature(response.getID(), response.getIssuer(), response.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof RequestAbstractType request) {\n\t\t\t\tAssert.notNull(request.getID(), \"Request#ID cannot be null\");\n\t\t\t\tAssert.notNull(request.getIssuer(), \"Request#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(request.getSignature(), \"Request#Signature cannot be null\");\n\t\t\t\treturn verifySignature(request.getID(), request.getIssuer(), request.getSignature());\n\t\t\t}\n\t\t\tif (signable instanceof Assertion assertion) {\n\t\t\t\tAssert.notNull(assertion.getID(), \"Assertion#ID cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getIssuer(), \"Assertion#Issuer cannot be null\");\n\t\t\t\tAssert.notNull(assertion.getSignature(), \"Assertion#Signature cannot be null\");\n\t\t\t\treturn verifySignature(assertion.getID(), assertion.getIssuer(), assertion.getSignature());\n\t\t\t}\n\t\t\tthrow new Saml2Exception(\"Unsupported object of type: \" + signable.getClass().getName());\n\t\t}\n\n\t\tprivate Collection<Saml2Error> verifySignature(String id, Issuer issuer, Signature signature) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(issuer);\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tSAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();\n\t\t\ttry {\n\t\t\t\tprofileValidator.validate(signature);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, criteria)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + id + \"]: \"));\n\t\t\t}\n\n\t\t\treturn errors;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2Error> verify(RedirectParameters parameters) {\n\t\t\tSignatureTrustEngine trustEngine = trustEngine(this.credentials);\n\t\t\tCriteriaSet criteria = verificationCriteria(parameters.getIssuer());\n\t\t\tif (parameters.getAlgorithm() == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature algorithm for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tbyte[] signature = parameters.getSignature();\n\t\t\tif (signature == null) {\n\t\t\t\treturn Collections.singletonList(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Missing signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t}\n\t\t\tCollection<Saml2Error> errors = new ArrayList<>();\n\t\t\tString algorithmUri = parameters.getAlgorithm();\n\t\t\ttry {\n\t\t\t\tif (!trustEngine.validate(signature, parameters.getContent(), algorithmUri, criteria, null)) {\n\t\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\terrors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE,\n\t\t\t\t\t\t\"Invalid signature for object [\" + parameters.getId() + \"]: \"));\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\n\t}\n\n\tstatic final class OpenSaml5DecryptionConfigurer implements DecryptionConfigurer {\n\n\t\tprivate static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(\n\t\t\t\tArrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),\n\t\t\t\t\t\tnew SimpleRetrievalMethodEncryptedKeyResolver()));\n\n\t\tprivate final Decrypter decrypter;\n\n\t\tOpenSaml5DecryptionConfigurer(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tthis.decrypter = decrypter(decryptionCredentials);\n\t\t}\n\n\t\tprivate static Decrypter decrypter(Collection<Saml2X509Credential> decryptionCredentials) {\n\t\t\tCollection<Credential> credentials = new ArrayList<>();\n\t\t\tfor (Saml2X509Credential key : decryptionCredentials) {\n\t\t\t\tCredential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey());\n\t\t\t\tcredentials.add(cred);\n\t\t\t}\n\t\t\tKeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials);\n\t\t\tDecrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver);\n\t\t\tdecrypter.setRootInNewDocument(true);\n\t\t\treturn decrypter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void decrypt(XMLObject object) {\n\t\t\tif (object instanceof Response response) {\n\t\t\t\tdecryptResponse(response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (object instanceof Assertion assertion) {\n\t\t\t\tdecryptAssertion(assertion);\n\t\t\t}\n\t\t\tif (object instanceof LogoutRequest request) {\n\t\t\t\tdecryptLogoutRequest(request);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * The methods that follow are adapted from OpenSAML's {@link DecryptAssertions},\n\t\t * {@link DecryptNameIDs}, and {@link DecryptAttributes}.\n\t\t *\n\t\t * <p>The reason that these OpenSAML classes are not used directly is because they\n\t\t * reference {@link javax.servlet.http.HttpServletRequest} which is a lower\n\t\t * Servlet API version than what Spring Security SAML uses.\n\t\t *\n\t\t * If OpenSAML 5 updates to {@link jakarta.servlet.http.HttpServletRequest}, then\n\t\t * this arrangement can be revisited.\n\t\t */\n\n\t\tprivate void decryptResponse(Response response) {\n\t\t\tCollection<Assertion> decrypteds = new ArrayList<>();\n\n\t\t\tint count = 0;\n\t\t\tint size = response.getEncryptedAssertions().size();\n\t\t\tfor (EncryptedAssertion encrypted : response.getEncryptedAssertions()) {\n\t\t\t\tlogger.trace(String.format(\"Decrypting EncryptedAssertion (%d/%d) in Response [%s]\", count, size,\n\t\t\t\t\t\tresponse.getID()));\n\t\t\t\ttry {\n\t\t\t\t\tAssertion decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresponse.getAssertions().addAll(decrypteds);\n\n\t\t\t// Re-marshall the response so that any ID attributes within the decrypted\n\t\t\t// Assertions\n\t\t\t// will have their ID-ness re-established at the DOM level.\n\t\t\tif (!decrypteds.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tXMLObjectSupport.marshall(response);\n\t\t\t\t}\n\t\t\t\tcatch (final MarshallingException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAssertion(Assertion assertion) {\n\t\t\tfor (AttributeStatement statement : assertion.getAttributeStatements()) {\n\t\t\t\tdecryptAttributes(statement);\n\t\t\t}\n\t\t\tdecryptSubject(assertion.getSubject());\n\t\t\tif (assertion.getConditions() != null) {\n\t\t\t\tfor (Condition c : assertion.getConditions().getConditions()) {\n\t\t\t\t\tif (!(c instanceof DelegationRestrictionType delegation)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfor (Delegate d : delegation.getDelegates()) {\n\t\t\t\t\t\tif (d.getEncryptedID() != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(d.getEncryptedID());\n\t\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\t\td.setNameID(decrypted);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptAttributes(AttributeStatement statement) {\n\t\t\tCollection<Attribute> decrypteds = new ArrayList<>();\n\t\t\tfor (EncryptedAttribute encrypted : statement.getEncryptedAttributes()) {\n\t\t\t\ttry {\n\t\t\t\t\tAttribute decrypted = this.decrypter.decrypt(encrypted);\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\tdecrypteds.add(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatement.getAttributes().addAll(decrypteds);\n\t\t}\n\n\t\tprivate void decryptSubject(@Nullable Subject subject) {\n\t\t\tif (subject != null) {\n\t\t\t\tif (subject.getEncryptedID() != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(subject.getEncryptedID());\n\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\tsubject.setNameID(decrypted);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (final SubjectConfirmation sc : subject.getSubjectConfirmations()) {\n\t\t\t\t\tif (sc.getEncryptedID() != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(sc.getEncryptedID());\n\t\t\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\t\t\tsc.setNameID(decrypted);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final DecryptionException ex) {\n\t\t\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void decryptLogoutRequest(LogoutRequest request) {\n\t\t\tif (request.getEncryptedID() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tNameID decrypted = (NameID) this.decrypter.decrypt(request.getEncryptedID());\n\t\t\t\t\tif (decrypted != null) {\n\t\t\t\t\t\trequest.setNameID(decrypted);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (DecryptionException ex) {\n\t\t\t\t\tthrow new Saml2Exception(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport javax.xml.namespace.QName;\n\nimport org.junit.jupiter.api.Test;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.schema.XSDateTime;\nimport org.opensaml.core.xml.schema.impl.XSDateTimeBuilder;\nimport org.opensaml.saml.common.SignableSAMLObject;\nimport org.opensaml.saml.common.assertion.ValidationContext;\nimport org.opensaml.saml.common.assertion.ValidationResult;\nimport org.opensaml.saml.saml2.assertion.ConditionValidator;\nimport org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.AttributeValue;\nimport org.opensaml.saml.saml2.core.AudienceRestriction;\nimport org.opensaml.saml.saml2.core.Conditions;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.EncryptedID;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.OneTimeUse;\nimport org.opensaml.saml.saml2.core.ProxyRestriction;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.Status;\nimport org.opensaml.saml.saml2.core.StatusCode;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.core.SubjectConfirmationData;\nimport org.opensaml.saml.saml2.core.impl.AttributeBuilder;\nimport org.opensaml.saml.saml2.core.impl.EncryptedAssertionBuilder;\nimport org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder;\nimport org.opensaml.saml.saml2.core.impl.NameIDBuilder;\nimport org.opensaml.saml.saml2.core.impl.ProxyRestrictionBuilder;\nimport org.opensaml.saml.saml2.core.impl.StatusBuilder;\nimport org.opensaml.saml.saml2.core.impl.StatusCodeBuilder;\nimport org.opensaml.xmlsec.encryption.impl.EncryptedDataBuilder;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ResponseValidatorResult;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider.AssertionValidator;\nimport org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider.ResponseAuthenticationConverter;\nimport org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider.ResponseToken;\nimport org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider.ResponseValidator;\nimport org.springframework.security.saml2.provider.service.authentication.TestCustomOpenSaml5Objects.CustomOpenSamlObject;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link OpenSaml5AuthenticationProvider}\n *\n * @author Filip Hanik\n * @author Josh Cummings\n */\npublic class OpenSaml5AuthenticationProviderTests {\n\n\tprivate static String DESTINATION = \"https://localhost/login/saml2/sso/idp-alias\";\n\n\tprivate static String RELYING_PARTY_ENTITY_ID = \"https://localhost/saml2/service-provider-metadata/idp-alias\";\n\n\tprivate static String ASSERTING_PARTY_ENTITY_ID = \"https://some.idp.test/saml2/idp\";\n\n\tprivate final OpenSamlOperations saml = new OpenSaml5Template();\n\n\tprivate OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\n\tprivate Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"name\",\n\t\t\tCollections.emptyMap());\n\n\tprivate Saml2Authentication authentication = new Saml2Authentication(this.principal, \"response\",\n\t\t\tCollections.emptyList());\n\n\t@Test\n\tpublic void supportsWhenSaml2AuthenticationTokenThenReturnTrue() {\n\t\tassertThat(this.provider.supports(Saml2AuthenticationToken.class))\n\t\t\t.withFailMessage(OpenSaml5AuthenticationProvider.class + \"should support \" + Saml2AuthenticationToken.class)\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsWhenNotSaml2AuthenticationTokenThenReturnFalse() {\n\t\tassertThat(!this.provider.supports(Authentication.class))\n\t\t\t.withFailMessage(OpenSaml5AuthenticationProvider.class + \"should not support \" + Authentication.class)\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUnknownDataClassThenThrowAuthenticationException() {\n\t\tAssertion assertion = (Assertion) XMLObjectProviderRegistrySupport.getBuilderFactory()\n\t\t\t.getBuilder(Assertion.DEFAULT_ELEMENT_NAME)\n\t\t\t.buildObject(Assertion.DEFAULT_ELEMENT_NAME);\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider\n\t\t\t\t.authenticate(new Saml2AuthenticationToken(verifying(registration()).build(), serialize(assertion))))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenXmlErrorThenThrowAuthenticationException() {\n\t\tSaml2AuthenticationToken token = new Saml2AuthenticationToken(verifying(registration()).build(), \"invalid xml\");\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidDestinationThenThrowAuthenticationException() {\n\t\tResponse response = response(DESTINATION + \"invalid\", ASSERTING_PARTY_ENTITY_ID);\n\t\tresponse.getAssertions().add(assertion());\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.INVALID_DESTINATION));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenNoAssertionsPresentThenThrowAuthenticationException() {\n\t\tSaml2AuthenticationToken token = token();\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, \"No assertions found in response.\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenInvalidSignatureOnAssertionThenThrowAuthenticationException() {\n\t\tResponse response = response();\n\t\tresponse.getAssertions().add(assertion());\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.INVALID_SIGNATURE));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenOpenSAMLValidationErrorThenThrowAuthenticationException() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tassertion.getSubject()\n\t\t\t.getSubjectConfirmations()\n\t\t\t.get(0)\n\t\t\t.getSubjectConfirmationData()\n\t\t\t.setNotOnOrAfter(Instant.now().minus(Duration.ofDays(3)));\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.INVALID_ASSERTION));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenMissingSubjectThenThrowAuthenticationException() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tassertion.setSubject(null);\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.SUBJECT_NOT_FOUND));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenUsernameMissingThenThrowAuthenticationException() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tassertion.getSubject().getNameID().setValue(null);\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.SUBJECT_NOT_FOUND));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAssertionContainsValidationAddressThenItSucceeds() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tassertion.getSubject()\n\t\t\t.getSubjectConfirmations()\n\t\t\t.forEach((sc) -> sc.getSubjectConfirmationData().setAddress(\"10.10.10.10\"));\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tthis.provider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void evaluateInResponseToSucceedsWhenInResponseToInResponseAndAssertionsMatchRequestID() {\n\t\tResponse response = response();\n\t\tresponse.setInResponseTo(\"SAML2\");\n\t\tresponse.getAssertions().add(signed(assertion(\"SAML2\")));\n\t\tresponse.getAssertions().add(signed(assertion(\"SAML2\")));\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mockedStoredAuthenticationRequest(\"SAML2\");\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()), mockAuthenticationRequest);\n\t\tthis.provider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void evaluateInResponseToSucceedsWhenInResponseToInAssertionOnlyMatchRequestID() {\n\t\tResponse response = response();\n\t\tresponse.getAssertions().add(signed(assertion()));\n\t\tresponse.getAssertions().add(signed(assertion(\"SAML2\")));\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mockedStoredAuthenticationRequest(\"SAML2\");\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()), mockAuthenticationRequest);\n\t\tthis.provider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void evaluateInResponseToFailsWhenInResponseToInAssertionMismatchWithRequestID() {\n\t\tResponse response = response();\n\t\tresponse.setInResponseTo(\"SAML2\");\n\t\tresponse.getAssertions().add(signed(assertion(\"SAML2\")));\n\t\tresponse.getAssertions().add(signed(assertion(\"BAD\")));\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mockedStoredAuthenticationRequest(\"SAML2\");\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()), mockAuthenticationRequest);\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.withStackTraceContaining(\"invalid_assertion\");\n\t}\n\n\t@Test\n\tpublic void evaluateInResponseToFailsWhenInResponseToInAssertionOnlyAndMismatchWithRequestID() {\n\t\tResponse response = response();\n\t\tresponse.getAssertions().add(signed(assertion()));\n\t\tresponse.getAssertions().add(signed(assertion(\"BAD\")));\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mockedStoredAuthenticationRequest(\"SAML2\");\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()), mockAuthenticationRequest);\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.withStackTraceContaining(\"invalid_assertion\");\n\t}\n\n\t@Test\n\tpublic void evaluateInResponseToFailsWhenInResponseInToResponseMismatchWithRequestID() {\n\t\tResponse response = response();\n\t\tresponse.setInResponseTo(\"BAD\");\n\t\tresponse.getAssertions().add(signed(assertion(\"SAML2\")));\n\t\tresponse.getAssertions().add(signed(assertion(\"SAML2\")));\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mockedStoredAuthenticationRequest(\"SAML2\");\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()), mockAuthenticationRequest);\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.withStackTraceContaining(\"invalid_in_response_to\");\n\t}\n\n\t@Test\n\tpublic void evaluateInResponseToFailsWhenInResponseToInResponseButNoSavedRequest() {\n\t\tResponse response = response();\n\t\tresponse.setInResponseTo(\"BAD\");\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.withStackTraceContaining(\"invalid_in_response_to\");\n\t}\n\n\t@Test\n\tpublic void evaluateInResponseToSucceedsWhenNoInResponseToInResponseOrAssertions() {\n\t\tResponse response = response();\n\t\tresponse.getAssertions().add(signed(assertion()));\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mockedStoredAuthenticationRequest(\"SAML2\");\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()), mockAuthenticationRequest);\n\t\tthis.provider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAssertionContainsAttributesThenItSucceeds() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tList<AttributeStatement> attributes = attributeStatements();\n\t\tassertion.getAttributeStatements().addAll(attributes);\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\t\tSaml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal();\n\t\tMap<String, Object> expected = new LinkedHashMap<>();\n\t\texpected.put(\"email\", Arrays.asList(\"john.doe@example.com\", \"doe.john@example.com\"));\n\t\texpected.put(\"name\", Collections.singletonList(\"John Doe\"));\n\t\texpected.put(\"age\", Collections.singletonList(21));\n\t\texpected.put(\"website\", Collections.singletonList(\"https://johndoe.com/\"));\n\t\texpected.put(\"registered\", Collections.singletonList(true));\n\t\tInstant registeredDate = Instant.parse(\"1970-01-01T00:00:00Z\");\n\t\texpected.put(\"registeredDate\", Collections.singletonList(registeredDate));\n\t\texpected.put(\"role\", Arrays.asList(\"RoleOne\", \"RoleTwo\")); // gh-11042\n\t\tassertThat((String) principal.getFirstAttribute(\"name\")).isEqualTo(\"John Doe\");\n\t\tassertThat(principal.getAttributes()).isEqualTo(expected);\n\t\tassertThat(principal.getSessionIndexes()).contains(\"session-index\");\n\t}\n\n\t// gh-11785\n\t@Test\n\tpublic void deserializeWhenAssertionContainsAttributesThenWorks() throws Exception {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tJsonMapper mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tList<AttributeStatement> attributes = TestOpenSamlObjects.attributeStatements();\n\t\tassertion.getAttributeStatements().addAll(attributes);\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\t\tString result = mapper.writeValueAsString(authentication);\n\t\tmapper.readValue(result, Authentication.class);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAssertionContainsCustomAttributesThenItSucceeds() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tAttributeStatement attribute = TestOpenSamlObjects.customAttributeStatement(\"Address\",\n\t\t\t\tTestCustomOpenSaml5Objects.instance());\n\t\tassertion.getAttributeStatements().add(attribute);\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\t\tSaml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal();\n\t\tCustomOpenSamlObject address = (CustomOpenSamlObject) principal.getAttribute(\"Address\").get(0);\n\t\tassertThat(address.getStreet()).isEqualTo(\"Test Street\");\n\t\tassertThat(address.getStreetNumber()).isEqualTo(\"1\");\n\t\tassertThat(address.getZIP()).isEqualTo(\"11111\");\n\t\tassertThat(address.getCity()).isEqualTo(\"Test City\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenEncryptedAssertionWithoutSignatureThenItFails() {\n\t\tResponse response = response();\n\t\tEncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(),\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tresponse.getEncryptedAssertions().add(encryptedAssertion);\n\t\tSaml2AuthenticationToken token = token(response, decrypting(verifying(registration())));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.INVALID_SIGNATURE, \"Did not decrypt response\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenEncryptedAssertionWithSignatureThenItSucceeds() {\n\t\tResponse response = response();\n\t\tAssertion assertion = TestOpenSamlObjects.signed(assertion(),\n\t\t\t\tTestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);\n\t\tEncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion,\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tresponse.getEncryptedAssertions().add(encryptedAssertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), decrypting(verifying(registration())));\n\t\tthis.provider.authenticate(token);\n\t}\n\n\t// gh-16367\n\t@Test\n\tpublic void authenticateWhenEncryptedAssertionWithSignatureThenEncryptedAssertionStillAvailable() {\n\t\tResponse response = response();\n\t\tAssertion assertion = TestOpenSamlObjects.signed(assertion(),\n\t\t\t\tTestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);\n\t\tEncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion,\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tresponse.getEncryptedAssertions().add(encryptedAssertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), decrypting(verifying(registration())));\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setResponseValidator((t) -> {\n\t\t\tassertThat(t.getResponse().getEncryptedAssertions()).isNotEmpty();\n\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t});\n\t\tprovider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenEncryptedAssertionWithResponseSignatureThenItSucceeds() {\n\t\tResponse response = response();\n\t\tEncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(),\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tresponse.getEncryptedAssertions().add(encryptedAssertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), decrypting(verifying(registration())));\n\t\tthis.provider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenEncryptedNameIdWithSignatureThenItSucceeds() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tNameID nameId = assertion.getSubject().getNameID();\n\t\tEncryptedID encryptedID = TestOpenSamlObjects.encrypted(nameId,\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tassertion.getSubject().setNameID(null);\n\t\tassertion.getSubject().setEncryptedID(encryptedID);\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, decrypting(verifying(registration())));\n\t\tthis.provider.authenticate(token);\n\t}\n\n\t// gh-16367\n\t@Test\n\tpublic void authenticateWhenEncryptedNameIdWithSignatureThenEncryptedNameIdStillAvailable() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tNameID nameId = assertion.getSubject().getNameID();\n\t\tEncryptedID encryptedID = TestOpenSamlObjects.encrypted(nameId,\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tassertion.getSubject().setNameID(null);\n\t\tassertion.getSubject().setEncryptedID(encryptedID);\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, decrypting(verifying(registration())));\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setAssertionValidator((t) -> {\n\t\t\tassertThat(t.getAssertion().getSubject().getEncryptedID()).isNotNull();\n\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t});\n\t\tprovider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenEncryptedAttributeThenDecrypts() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tEncryptedAttribute attribute = TestOpenSamlObjects.encrypted(\"name\", \"value\",\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tAttributeStatement statement = build(AttributeStatement.DEFAULT_ELEMENT_NAME);\n\t\tstatement.getEncryptedAttributes().add(attribute);\n\t\tassertion.getAttributeStatements().add(statement);\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), decrypting(verifying(registration())));\n\t\tSaml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(token);\n\t\tSaml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal();\n\t\tassertThat(principal.getAttribute(\"name\")).containsExactly(\"value\");\n\t}\n\n\t// gh-16367\n\t@Test\n\tpublic void authenticateWhenEncryptedAttributeThenEncryptedAttributesStillAvailable() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tEncryptedAttribute attribute = TestOpenSamlObjects.encrypted(\"name\", \"value\",\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tAttributeStatement statement = build(AttributeStatement.DEFAULT_ELEMENT_NAME);\n\t\tstatement.getEncryptedAttributes().add(attribute);\n\t\tassertion.getAttributeStatements().add(statement);\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), decrypting(verifying(registration())));\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setAssertionValidator((t) -> {\n\t\t\tassertThat(t.getAssertion().getAttributeStatements().get(0).getEncryptedAttributes()).isNotEmpty();\n\t\t\treturn Saml2ResponseValidatorResult.success();\n\t\t});\n\t\tprovider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDecryptionKeysAreMissingThenThrowAuthenticationException() {\n\t\tResponse response = response();\n\t\tEncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(),\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tresponse.getEncryptedAssertions().add(encryptedAssertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.DECRYPTION_ERROR, \"Failed to decrypt EncryptedData\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDecryptionKeysAreWrongThenThrowAuthenticationException() {\n\t\tResponse response = response();\n\t\tEncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(),\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tresponse.getEncryptedAssertions().add(encryptedAssertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), registration()\n\t\t\t.decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartyPrivateCredential())));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.DECRYPTION_ERROR, \"Failed to decrypt EncryptedData\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAuthenticationHasDetailsThenSucceeds() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tassertion.getSubject()\n\t\t\t.getSubjectConfirmations()\n\t\t\t.forEach((sc) -> sc.getSubjectConfirmationData().setAddress(\"10.10.10.10\"));\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\ttoken.setDetails(\"some-details\");\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\t\tassertThat(authentication.getDetails()).isEqualTo(\"some-details\");\n\t}\n\n\t@Test\n\tpublic void writeObjectWhenTypeIsSaml2AuthenticationThenNoException() throws IOException {\n\t\tResponse response = response();\n\t\tAssertion assertion = TestOpenSamlObjects.signed(assertion(),\n\t\t\t\tTestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);\n\t\tEncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion,\n\t\t\t\tTestSaml2X509Credentials.assertingPartyEncryptingCredential());\n\t\tresponse.getEncryptedAssertions().add(encryptedAssertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), decrypting(verifying(registration())));\n\t\tSaml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(token);\n\t\t// the following code will throw an exception if authentication isn't serializable\n\t\tByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024);\n\t\tObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream);\n\t\tobjectOutputStream.writeObject(authentication);\n\t\tobjectOutputStream.flush();\n\t}\n\n\t@Test\n\tpublic void createDefaultAssertionValidatorWhenAssertionThenValidates() {\n\t\tResponse response = TestOpenSamlObjects.signedResponseWithOneAssertion();\n\t\tAssertion assertion = response.getAssertions().get(0);\n\t\tOpenSaml5AuthenticationProvider.AssertionToken assertionToken = new OpenSaml5AuthenticationProvider.AssertionToken(\n\t\t\t\tassertion, token());\n\t\tassertThat(\n\t\t\t\tOpenSaml5AuthenticationProvider.createDefaultAssertionValidator().convert(assertionToken).hasErrors())\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDelegatingToDefaultAssertionValidatorThenUses() {\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\t// @formatter:off\n\t\tprovider.setAssertionValidator((assertionToken) -> OpenSaml5AuthenticationProvider\n\t\t\t\t.createDefaultAssertionValidator((token) -> new ValidationContext())\n\t\t\t\t.convert(assertionToken)\n\t\t\t\t.concat(new Saml2Error(\"wrong error\", \"wrong error\"))\n\t\t);\n\t\t// @formatter:on\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tOneTimeUse oneTimeUse = build(OneTimeUse.DEFAULT_ELEMENT_NAME);\n\t\tassertion.getConditions().getConditions().add(oneTimeUse);\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> provider.authenticate(token)).isInstanceOf(Saml2AuthenticationException.class)\n\t\t\t\t.satisfies((error) -> assertThat(error.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_ASSERTION));\n\t\t// @formatter:on\n\t}\n\n\t// gh-11675\n\t@Test\n\tpublic void authenticateWhenUsingCustomAssertionValidatorThenUses() {\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tConsumer<Map<String, Object>> validationParameters = mock(Consumer.class);\n\t\t// @formatter:off\n\t\tprovider.setAssertionValidator(OpenSaml5AuthenticationProvider\n\t\t\t\t.createDefaultAssertionValidatorWithParameters(validationParameters));\n\t\t// @formatter:on\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tOneTimeUse oneTimeUse = build(OneTimeUse.DEFAULT_ELEMENT_NAME);\n\t\tassertion.getConditions().getConditions().add(oneTimeUse);\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\tprovider.authenticate(token);\n\t\tverify(validationParameters).accept(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAssertionValidatorThenUses() {\n\t\tConverter<OpenSaml5AuthenticationProvider.AssertionToken, Saml2ResponseValidatorResult> validator = mock(\n\t\t\t\tConverter.class);\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\t// @formatter:off\n\t\tprovider.setAssertionValidator((assertionToken) -> OpenSaml5AuthenticationProvider.createDefaultAssertionValidator()\n\t\t\t\t.convert(assertionToken)\n\t\t\t\t.concat(validator.convert(assertionToken))\n\t\t);\n\t\t// @formatter:on\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\tgiven(validator.convert(any(OpenSaml5AuthenticationProvider.AssertionToken.class)))\n\t\t\t.willReturn(Saml2ResponseValidatorResult.success());\n\t\tprovider.authenticate(token);\n\t\tverify(validator).convert(any(OpenSaml5AuthenticationProvider.AssertionToken.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAssertionValidatorListThenUses() throws Exception {\n\t\tConditionValidator custom = mock(ConditionValidator.class);\n\t\tgiven(custom.getServicedCondition()).willReturn(AudienceRestriction.DEFAULT_ELEMENT_NAME);\n\t\tgiven(custom.validate(any(), any(), any())).willReturn(ValidationResult.INVALID);\n\t\tAssertionValidator validator = AssertionValidator.builder().conditionValidators((c) -> c.add(custom)).build();\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setAssertionValidator(validator);\n\t\tResponse response = TestOpenSamlObjects.signedResponseWithOneAssertion((r) -> r.getAssertions()\n\t\t\t.get(0)\n\t\t\t.getConditions()\n\t\t\t.getConditions()\n\t\t\t.add(build(AudienceRestriction.DEFAULT_ELEMENT_NAME)));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> provider.authenticate(token))\n\t\t\t.withMessageContaining(\"AudienceRestriction\");\n\t\tverify(custom).validate(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenDefaultConditionValidatorNotUsedThenSignatureStillChecked() {\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setAssertionValidator((assertionToken) -> Saml2ResponseValidatorResult.success());\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tTestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.relyingPartyDecryptingCredential(),\n\t\t\t\tRELYING_PARTY_ENTITY_ID); // broken\n\t\t// signature\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> provider.authenticate(token))\n\t\t\t\t.satisfies((error) -> assertThat(error.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_SIGNATURE));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidationContextCustomizedThenUsers() {\n\t\tMap<String, Object> parameters = new HashMap<>();\n\t\tparameters.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton(\"blah\"));\n\t\tValidationContext context = mock(ValidationContext.class);\n\t\tgiven(context.getStaticParameters()).willReturn(parameters);\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setAssertionValidator(\n\t\t\t\tOpenSaml5AuthenticationProvider.createDefaultAssertionValidator((assertionToken) -> context));\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\t// @formatter:off\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t\t.isThrownBy(() -> provider.authenticate(token)).isInstanceOf(Saml2AuthenticationException.class)\n\t\t\t\t.satisfies((error) -> assertThat(error).hasMessageContaining(\"Invalid assertion\"));\n\t\t// @formatter:on\n\t\tverify(context, atLeastOnce()).getStaticParameters();\n\t}\n\n\t@Test\n\tpublic void authenticateWithSHA1SignatureThenItSucceeds() throws Exception {\n\t\tResponse response = response();\n\t\tAssertion assertion = TestOpenSamlObjects.signed(assertion(),\n\t\t\t\tTestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID,\n\t\t\t\tSignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tthis.provider.authenticate(token);\n\t}\n\n\t@Test\n\tpublic void setAssertionValidatorWhenNullThenIllegalArgument() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.provider.setAssertionValidator(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void createDefaultResponseAuthenticationConverterWhenResponseThenConverts() {\n\t\tResponse response = TestOpenSamlObjects.signedResponseWithOneAssertion();\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tResponseToken responseToken = new ResponseToken(response, token);\n\t\tSaml2Authentication authentication = OpenSaml5AuthenticationProvider\n\t\t\t.createDefaultResponseAuthenticationConverter()\n\t\t\t.convert(responseToken);\n\t\tassertThat(authentication.getName()).isEqualTo(\"test@saml.user\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenResponseAuthenticationConverterConfiguredThenUses() {\n\t\tConverter<ResponseToken, Saml2Authentication> authenticationConverter = mock(Converter.class);\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setResponseAuthenticationConverter(authenticationConverter);\n\t\tResponse response = TestOpenSamlObjects.signedResponseWithOneAssertion();\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tprovider.authenticate(token);\n\t\tverify(authenticationConverter).convert(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenResponseAuthenticationConverterComponentConfiguredThenUses() {\n\t\tConverter<Assertion, Collection<GrantedAuthority>> grantedAuthoritiesConverter = mock(Converter.class);\n\t\tgiven(grantedAuthoritiesConverter.convert(any())).willReturn(AuthorityUtils.createAuthorityList(\"CUSTOM\"));\n\t\tResponseAuthenticationConverter authenticationConverter = new ResponseAuthenticationConverter();\n\t\tauthenticationConverter.setGrantedAuthoritiesConverter(grantedAuthoritiesConverter);\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setResponseAuthenticationConverter(authenticationConverter);\n\t\tResponse response = TestOpenSamlObjects.signedResponseWithOneAssertion();\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tAuthentication authentication = provider.authenticate(token);\n\t\tSecurityAssertions.assertThat(authentication).hasAuthority(\"CUSTOM\");\n\t\tverify(grantedAuthoritiesConverter).convert(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidateResponseAfterAssertionsThenCanHaveResponseAuthenticationConverterThatDoesntNeedANameID() {\n\t\tConverter<ResponseToken, Saml2Authentication> responseAuthenticationConverter = mock(Converter.class);\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setValidateResponseAfterAssertions(true);\n\t\tprovider.setResponseAuthenticationConverter(responseAuthenticationConverter);\n\t\tResponse response = TestOpenSamlObjects\n\t\t\t.signedResponseWithOneAssertion((r) -> r.getAssertions().get(0).setSubject(null));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tprovider.authenticate(token);\n\t\tverify(responseAuthenticationConverter).convert(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenValidateResponseBeforeAssertionsThenMustHaveNameID() {\n\t\tConverter<ResponseToken, Saml2Authentication> responseAuthenticationConverter = mock(Converter.class);\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setValidateResponseAfterAssertions(false);\n\t\tprovider.setResponseAuthenticationConverter(responseAuthenticationConverter);\n\t\tResponse response = TestOpenSamlObjects\n\t\t\t.signedResponseWithOneAssertion((r) -> r.getAssertions().get(0).setSubject(null));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> provider.authenticate(token));\n\t\tverifyNoInteractions(responseAuthenticationConverter);\n\t}\n\n\t@Test\n\tpublic void setResponseAuthenticationConverterWhenNullThenIllegalArgument() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.provider.setResponseAuthenticationConverter(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setResponseElementsDecrypterWhenNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.provider.setResponseElementsDecrypter(null));\n\t}\n\n\t@Test\n\tpublic void setAssertionElementsDecrypterWhenNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.provider.setAssertionElementsDecrypter(null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomResponseElementsDecrypterThenDecryptsResponse() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tresponse.getEncryptedAssertions().add(new EncryptedAssertionBuilder().buildObject());\n\t\tTestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(),\n\t\t\t\tRELYING_PARTY_ENTITY_ID);\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tthis.provider\n\t\t\t.setResponseElementsDecrypter((tuple) -> tuple.getResponse().getAssertions().add(signed(assertion)));\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\t\tassertThat(authentication.getName()).isEqualTo(\"test@saml.user\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomAssertionElementsDecrypterThenDecryptsAssertion() {\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tEncryptedID id = new EncryptedIDBuilder().buildObject();\n\t\tid.setEncryptedData(new EncryptedDataBuilder().buildObject());\n\t\tassertion.getSubject().setEncryptedID(id);\n\t\tresponse.getAssertions().add(signed(assertion));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tthis.provider.setAssertionElementsDecrypter((tuple) -> {\n\t\t\tNameID name = new NameIDBuilder().buildObject();\n\t\t\tname.setValue(\"decrypted name\");\n\t\t\ttuple.getAssertion().getSubject().setNameID(name);\n\t\t});\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\t\tassertThat(authentication.getName()).isEqualTo(\"decrypted name\");\n\t}\n\n\t@Test\n\tpublic void authenticateWhenResponseStatusIsNotSuccessThenFails() {\n\t\tResponse response = TestOpenSamlObjects\n\t\t\t.signedResponseWithOneAssertion((r) -> r.setStatus(TestOpenSamlObjects.status(StatusCode.AUTHN_FAILED)));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.provider.authenticate(token))\n\t\t\t.satisfies(errorOf(Saml2ErrorCodes.INVALID_RESPONSE, \"Invalid status\"));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenResponseStatusIsSuccessThenSucceeds() {\n\t\tResponse response = TestOpenSamlObjects\n\t\t\t.signedResponseWithOneAssertion((r) -> r.setStatus(TestOpenSamlObjects.successStatus()));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tAuthentication authentication = this.provider.authenticate(token);\n\t\tassertThat(authentication.getName()).isEqualTo(\"test@saml.user\");\n\t}\n\n\t@Test\n\tpublic void setResponseValidatorWhenNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.provider.setResponseValidator(null));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomResponseValidatorThenUses() {\n\t\tConverter<OpenSaml5AuthenticationProvider.ResponseToken, Saml2ResponseValidatorResult> validator = mock(\n\t\t\t\tConverter.class);\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\t// @formatter:off\n\t\tprovider.setResponseValidator((responseToken) -> OpenSaml5AuthenticationProvider.createDefaultResponseValidator()\n\t\t\t\t.convert(responseToken)\n\t\t\t\t.concat(validator.convert(responseToken))\n\t\t);\n\t\t// @formatter:on\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\tgiven(validator.convert(any(OpenSaml5AuthenticationProvider.ResponseToken.class)))\n\t\t\t.willReturn(Saml2ResponseValidatorResult.success());\n\t\tprovider.authenticate(token);\n\t\tverify(validator).convert(any(OpenSaml5AuthenticationProvider.ResponseToken.class));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenCustomSetOfResponseValidatorsThenUses() {\n\t\tConverter<OpenSaml5AuthenticationProvider.ResponseToken, Saml2ResponseValidatorResult> validator = mock(\n\t\t\t\tConverter.class);\n\t\tgiven(validator.convert(any()))\n\t\t\t.willReturn(Saml2ResponseValidatorResult.failure(new Saml2Error(\"error\", \"description\")));\n\t\tResponseValidator responseValidator = new ResponseValidator(validator);\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setResponseValidator(responseValidator);\n\t\tResponse response = TestOpenSamlObjects.signedResponseWithOneAssertion();\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> provider.authenticate(token))\n\t\t\t.withMessageContaining(\"description\");\n\t\tverify(validator).convert(any());\n\t}\n\n\t@Test\n\tpublic void authenticateWhenResponseStatusIsNotSuccessThenOnlyReturnParentStatusCodes() {\n\t\tSaml2AuthenticationToken token = TestSaml2AuthenticationTokens.token();\n\n\t\tStatus parentStatus = new StatusBuilder().buildObject();\n\t\tStatusCode parentStatusCode = new StatusCodeBuilder().buildObject();\n\t\tparentStatusCode.setValue(StatusCode.AUTHN_FAILED);\n\t\tStatusCode childStatusCode = new StatusCodeBuilder().buildObject();\n\t\tchildStatusCode.setValue(StatusCode.NO_PASSIVE);\n\t\tparentStatusCode.setStatusCode(childStatusCode);\n\t\tparentStatus.setStatusCode(parentStatusCode);\n\n\t\tResponse response = TestOpenSamlObjects.response();\n\t\tresponse.setStatus(parentStatus);\n\t\tresponse.setIssuer(TestOpenSamlObjects.issuer(\"mockedIssuer\"));\n\n\t\tConverter<ResponseToken, Saml2ResponseValidatorResult> validator = OpenSaml5AuthenticationProvider\n\t\t\t.createDefaultResponseValidator();\n\t\tSaml2ResponseValidatorResult result = validator.convert(new ResponseToken(response, token));\n\n\t\tString expectedErrorMessage = String.format(\"Invalid status [%s] for SAML response\",\n\t\t\t\tparentStatusCode.getValue());\n\t\tassertThat(\n\t\t\t\tresult.getErrors().stream().anyMatch((error) -> error.getDescription().contains(expectedErrorMessage)))\n\t\t\t.isTrue();\n\t\tassertThat(result.getErrors()\n\t\t\t.stream()\n\t\t\t.noneMatch((error) -> error.getDescription().contains(childStatusCode.getValue()))).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenResponseStatusIsNotSuccessThenReturnParentAndChildStatusCode() {\n\t\tSaml2AuthenticationToken token = TestSaml2AuthenticationTokens.token();\n\t\tStatus parentStatus = new StatusBuilder().buildObject();\n\t\tStatusCode parentStatusCode = new StatusCodeBuilder().buildObject();\n\t\tparentStatusCode.setValue(StatusCode.REQUESTER);\n\t\tStatusCode childStatusCode = new StatusCodeBuilder().buildObject();\n\t\tchildStatusCode.setValue(StatusCode.NO_PASSIVE);\n\t\tparentStatusCode.setStatusCode(childStatusCode);\n\t\tparentStatus.setStatusCode(parentStatusCode);\n\n\t\tResponse response = TestOpenSamlObjects.response();\n\t\tresponse.setStatus(parentStatus);\n\t\tresponse.setIssuer(TestOpenSamlObjects.issuer(\"mockedIssuer\"));\n\n\t\tConverter<ResponseToken, Saml2ResponseValidatorResult> validator = OpenSaml5AuthenticationProvider\n\t\t\t.createDefaultResponseValidator();\n\t\tSaml2ResponseValidatorResult result = validator.convert(new ResponseToken(response, token));\n\n\t\tString expectedParentErrorMessage = String.format(\"Invalid status [%s] for SAML response\",\n\t\t\t\tparentStatusCode.getValue());\n\t\tString expectedChildErrorMessage = String.format(\"Invalid status [%s] for SAML response\",\n\t\t\t\tchildStatusCode.getValue());\n\t\tassertThat(result.getErrors()\n\t\t\t.stream()\n\t\t\t.anyMatch((error) -> error.getDescription().contains(expectedParentErrorMessage))).isTrue();\n\t\tassertThat(result.getErrors()\n\t\t\t.stream()\n\t\t\t.anyMatch((error) -> error.getDescription().contains(expectedChildErrorMessage))).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticateWhenAssertionIssuerNotValidThenFailsWithInvalidIssuer() {\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tassertion.setIssuer(TestOpenSamlObjects.issuer(\"https://invalid.idp.test/saml2/idp\"));\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> provider.authenticate(token))\n\t\t\t.withMessageContaining(\"did not match any valid issuers\");\n\t}\n\n\t// gh-14931\n\t@Test\n\tpublic void authenticateWhenAssertionHasProxyRestrictionThenParses() {\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tProxyRestriction condition = new ProxyRestrictionBuilder().buildObject();\n\t\tassertion.getConditions().getConditions().add(condition);\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\tprovider.authenticate(token);\n\t}\n\n\t// gh-15022\n\t@Test\n\tpublic void authenticateWhenClockSkewThenVerifiesSignature() {\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tprovider.setAssertionValidator(OpenSaml5AuthenticationProvider.createDefaultAssertionValidatorWithParameters(\n\t\t\t\t(params) -> params.put(SAML2AssertionValidationParameters.CLOCK_SKEW, Duration.ofMinutes(10))));\n\t\tResponse response = response();\n\t\tAssertion assertion = assertion();\n\t\tassertion.setIssueInstant(Instant.now().plus(Duration.ofMinutes(9)));\n\t\tresponse.getAssertions().add(assertion);\n\t\tSaml2AuthenticationToken token = token(signed(response), verifying(registration()));\n\t\tprovider.authenticate(token);\n\t}\n\n\t// gh-16989\n\t@Test\n\tpublic void authenticateWhenNullIssuerThenNoNullPointer() {\n\t\tOpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n\t\tResponse response = TestOpenSamlObjects.signedResponseWithOneAssertion((r) -> r.setIssuer(null));\n\t\tSaml2AuthenticationToken token = token(response, verifying(registration()));\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> provider.authenticate(token));\n\t}\n\n\t@Test\n\tpublic void authenticateWhenSuccessThenIssuesFactor() {\n\t\tResponse response = TestOpenSamlObjects.signedResponseWithOneAssertion();\n\t\tAuthentication request = token(response, verifying(registration()));\n\t\tAuthentication result = this.provider.authenticate(request);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.SAML_RESPONSE_AUTHORITY);\n\t}\n\n\tprivate <T extends XMLObject> T build(QName qName) {\n\t\treturn (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);\n\t}\n\n\tprivate String serialize(XMLObject object) {\n\t\treturn this.saml.serialize(object).serialize();\n\t}\n\n\tprivate Consumer<Saml2AuthenticationException> errorOf(String errorCode) {\n\t\treturn errorOf(errorCode, null);\n\t}\n\n\tprivate Consumer<Saml2AuthenticationException> errorOf(String errorCode, String description) {\n\t\treturn (ex) -> {\n\t\t\tassertThat(ex.getSaml2Error().getErrorCode()).isEqualTo(errorCode);\n\t\t\tif (StringUtils.hasText(description)) {\n\t\t\t\tassertThat(ex.getSaml2Error().getDescription()).contains(description);\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate Response response() {\n\t\tResponse response = TestOpenSamlObjects.response();\n\t\tresponse.setIssueInstant(Instant.now());\n\t\treturn response;\n\t}\n\n\tprivate Response response(String destination, String issuerEntityId) {\n\t\tResponse response = TestOpenSamlObjects.response(destination, issuerEntityId);\n\t\tresponse.setIssueInstant(Instant.now());\n\t\treturn response;\n\t}\n\n\tprivate Assertion assertion(String inResponseTo) {\n\t\tAssertion assertion = TestOpenSamlObjects.assertion();\n\t\tassertion.setIssueInstant(Instant.now());\n\t\tfor (SubjectConfirmation confirmation : assertion.getSubject().getSubjectConfirmations()) {\n\t\t\tSubjectConfirmationData data = confirmation.getSubjectConfirmationData();\n\t\t\tdata.setNotBefore(Instant.now().minus(Duration.ofMillis(5 * 60 * 1000)));\n\t\t\tdata.setNotOnOrAfter(Instant.now().plus(Duration.ofMillis(5 * 60 * 1000)));\n\t\t\tif (StringUtils.hasText(inResponseTo)) {\n\t\t\t\tdata.setInResponseTo(inResponseTo);\n\t\t\t}\n\t\t}\n\t\tConditions conditions = assertion.getConditions();\n\t\tconditions.setNotBefore(Instant.now().minus(Duration.ofMillis(5 * 60 * 1000)));\n\t\tconditions.setNotOnOrAfter(Instant.now().plus(Duration.ofMillis(5 * 60 * 1000)));\n\t\treturn assertion;\n\t}\n\n\tprivate Assertion assertion() {\n\t\treturn assertion(null);\n\t}\n\n\tprivate <T extends SignableSAMLObject> T signed(T toSign) {\n\t\tTestOpenSamlObjects.signed(toSign, TestSaml2X509Credentials.assertingPartySigningCredential(),\n\t\t\t\tRELYING_PARTY_ENTITY_ID);\n\t\treturn toSign;\n\t}\n\n\tprivate List<AttributeStatement> attributeStatements() {\n\t\tList<AttributeStatement> attributeStatements = TestOpenSamlObjects.attributeStatements();\n\t\tAttributeBuilder attributeBuilder = new AttributeBuilder();\n\t\tAttribute registeredDateAttr = attributeBuilder.buildObject();\n\t\tregisteredDateAttr.setName(\"registeredDate\");\n\t\tXSDateTime registeredDate = new XSDateTimeBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME,\n\t\t\t\tXSDateTime.TYPE_NAME);\n\t\tregisteredDate.setValue(Instant.parse(\"1970-01-01T00:00:00Z\"));\n\t\tregisteredDateAttr.getAttributeValues().add(registeredDate);\n\t\tattributeStatements.iterator().next().getAttributes().add(registeredDateAttr);\n\t\treturn attributeStatements;\n\t}\n\n\tprivate Saml2AuthenticationToken token() {\n\t\tResponse response = response();\n\t\tRelyingPartyRegistration registration = verifying(registration()).build();\n\t\treturn new Saml2AuthenticationToken(registration, serialize(response));\n\t}\n\n\tprivate Saml2AuthenticationToken token(Response response, RelyingPartyRegistration.Builder registration) {\n\t\treturn new Saml2AuthenticationToken(registration.build(), serialize(response));\n\t}\n\n\tprivate Saml2AuthenticationToken token(Response response, RelyingPartyRegistration.Builder registration,\n\t\t\tAbstractSaml2AuthenticationRequest authenticationRequest) {\n\t\treturn new Saml2AuthenticationToken(registration.build(), serialize(response), authenticationRequest);\n\t}\n\n\tprivate AbstractSaml2AuthenticationRequest mockedStoredAuthenticationRequest(String requestId) {\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mock(AbstractSaml2AuthenticationRequest.class);\n\t\tgiven(mockAuthenticationRequest.getId()).willReturn(requestId);\n\t\treturn mockAuthenticationRequest;\n\t}\n\n\tprivate RelyingPartyRegistration.Builder registration() {\n\t\treturn TestRelyingPartyRegistrations.noCredentials()\n\t\t\t.entityId(RELYING_PARTY_ENTITY_ID)\n\t\t\t.assertionConsumerServiceLocation(DESTINATION)\n\t\t\t.assertingPartyMetadata((party) -> party.entityId(ASSERTING_PARTY_ENTITY_ID));\n\t}\n\n\tprivate RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) {\n\t\treturn builder.assertingPartyMetadata((party) -> party\n\t\t\t.verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())));\n\t}\n\n\tprivate RelyingPartyRegistration.Builder decrypting(RelyingPartyRegistration.Builder builder) {\n\t\treturn builder\n\t\t\t.decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyDecryptingCredential()));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/TestCustomOpenSaml5Objects.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport javax.xml.namespace.QName;\n\nimport net.shibboleth.shared.xml.ElementSupport;\nimport org.jspecify.annotations.Nullable;\nimport org.opensaml.core.xml.AbstractXMLObject;\nimport org.opensaml.core.xml.AbstractXMLObjectBuilder;\nimport org.opensaml.core.xml.ElementExtensibleXMLObject;\nimport org.opensaml.core.xml.Namespace;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.AbstractXMLObjectMarshaller;\nimport org.opensaml.core.xml.io.AbstractXMLObjectUnmarshaller;\nimport org.opensaml.core.xml.io.UnmarshallingException;\nimport org.opensaml.core.xml.schema.XSAny;\nimport org.opensaml.core.xml.schema.impl.XSAnyBuilder;\nimport org.opensaml.core.xml.util.IndexedXMLObjectChildrenList;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.saml2.core.AttributeValue;\nimport org.w3c.dom.Element;\n\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\n\npublic final class TestCustomOpenSaml5Objects {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t\tXMLObjectProviderRegistrySupport.getMarshallerFactory()\n\t\t\t.registerMarshaller(CustomOpenSamlObject.TYPE_NAME,\n\t\t\t\t\tnew TestCustomOpenSaml5Objects.CustomSamlObjectMarshaller());\n\t\tXMLObjectProviderRegistrySupport.getUnmarshallerFactory()\n\t\t\t.registerUnmarshaller(CustomOpenSamlObject.TYPE_NAME,\n\t\t\t\t\tnew TestCustomOpenSaml5Objects.CustomSamlObjectUnmarshaller());\n\t}\n\n\tpublic static CustomOpenSamlObject instance() {\n\t\tCustomOpenSamlObject samlObject = new TestCustomOpenSaml5Objects.CustomSamlObjectBuilder()\n\t\t\t.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, CustomOpenSamlObject.TYPE_NAME);\n\t\tXSAny street = new XSAnyBuilder().buildObject(CustomOpenSamlObject.CUSTOM_NS, \"Street\",\n\t\t\t\tCustomOpenSamlObject.TYPE_CUSTOM_PREFIX);\n\t\tstreet.setTextContent(\"Test Street\");\n\t\tsamlObject.getUnknownXMLObjects().add(street);\n\t\tXSAny streetNumber = new XSAnyBuilder().buildObject(CustomOpenSamlObject.CUSTOM_NS, \"Number\",\n\t\t\t\tCustomOpenSamlObject.TYPE_CUSTOM_PREFIX);\n\t\tstreetNumber.setTextContent(\"1\");\n\t\tsamlObject.getUnknownXMLObjects().add(streetNumber);\n\t\tXSAny zip = new XSAnyBuilder().buildObject(CustomOpenSamlObject.CUSTOM_NS, \"ZIP\",\n\t\t\t\tCustomOpenSamlObject.TYPE_CUSTOM_PREFIX);\n\t\tzip.setTextContent(\"11111\");\n\t\tsamlObject.getUnknownXMLObjects().add(zip);\n\t\tXSAny city = new XSAnyBuilder().buildObject(CustomOpenSamlObject.CUSTOM_NS, \"City\",\n\t\t\t\tCustomOpenSamlObject.TYPE_CUSTOM_PREFIX);\n\t\tcity.setTextContent(\"Test City\");\n\t\tsamlObject.getUnknownXMLObjects().add(city);\n\t\treturn samlObject;\n\t}\n\n\tprivate TestCustomOpenSaml5Objects() {\n\n\t}\n\n\tpublic interface CustomOpenSamlObject extends ElementExtensibleXMLObject {\n\n\t\tString TYPE_LOCAL_NAME = \"CustomType\";\n\n\t\tString TYPE_CUSTOM_PREFIX = \"custom\";\n\n\t\tString CUSTOM_NS = \"https://custom.com/schema/custom\";\n\n\t\t/** QName of the CustomType type. */\n\t\tQName TYPE_NAME = new QName(CUSTOM_NS, TYPE_LOCAL_NAME, TYPE_CUSTOM_PREFIX);\n\n\t\tString getStreet();\n\n\t\tString getStreetNumber();\n\n\t\tString getZIP();\n\n\t\tString getCity();\n\n\t}\n\n\tpublic static class CustomOpenSamlObjectImpl extends AbstractXMLObject implements CustomOpenSamlObject {\n\n\t\tprivate IndexedXMLObjectChildrenList<XMLObject> unknownXMLObjects;\n\n\t\t/**\n\t\t * Constructor.\n\t\t * @param namespaceURI the namespace the element is in\n\t\t * @param elementLocalName the local name of the XML element this Object\n\t\t * represents\n\t\t * @param namespacePrefix the prefix for the given namespace\n\t\t */\n\t\tprotected CustomOpenSamlObjectImpl(@Nullable String namespaceURI, String elementLocalName,\n\t\t\t\t@Nullable String namespacePrefix) {\n\t\t\tsuper(namespaceURI, elementLocalName, namespacePrefix);\n\t\t\tsuper.getNamespaceManager().registerNamespaceDeclaration(new Namespace(CUSTOM_NS, TYPE_CUSTOM_PREFIX));\n\t\t\tthis.unknownXMLObjects = new IndexedXMLObjectChildrenList<>(this);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<XMLObject> getUnknownXMLObjects() {\n\t\t\treturn this.unknownXMLObjects;\n\t\t}\n\n\t\t@Override\n\t\tpublic List<XMLObject> getUnknownXMLObjects(QName typeOrName) {\n\t\t\treturn (List<XMLObject>) this.unknownXMLObjects.subList(typeOrName);\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable List<XMLObject> getOrderedChildren() {\n\t\t\treturn Collections.unmodifiableList(this.unknownXMLObjects);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getStreet() {\n\t\t\treturn ((XSAny) getOrderedChildren().get(0)).getTextContent();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getStreetNumber() {\n\t\t\treturn ((XSAny) getOrderedChildren().get(1)).getTextContent();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getZIP() {\n\t\t\treturn ((XSAny) getOrderedChildren().get(2)).getTextContent();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getCity() {\n\t\t\treturn ((XSAny) getOrderedChildren().get(3)).getTextContent();\n\t\t}\n\n\t}\n\n\tpublic static class CustomSamlObjectBuilder extends AbstractXMLObjectBuilder<CustomOpenSamlObject> {\n\n\t\t@Override\n\t\tpublic CustomOpenSamlObject buildObject(@Nullable String namespaceURI, String localName,\n\t\t\t\t@Nullable String namespacePrefix) {\n\t\t\treturn new CustomOpenSamlObjectImpl(namespaceURI, localName, namespacePrefix);\n\t\t}\n\n\t}\n\n\tpublic static class CustomSamlObjectMarshaller extends AbstractXMLObjectMarshaller {\n\n\t\tpublic CustomSamlObjectMarshaller() {\n\t\t\tsuper();\n\t\t}\n\n\t\t@Override\n\t\tprotected void marshallElementContent(XMLObject xmlObject, Element domElement) {\n\t\t\tfinal CustomOpenSamlObject customSamlObject = (CustomOpenSamlObject) xmlObject;\n\n\t\t\tfor (XMLObject object : customSamlObject.getOrderedChildren()) {\n\t\t\t\tElementSupport.appendChildElement(domElement, object.getDOM());\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic static class CustomSamlObjectUnmarshaller extends AbstractXMLObjectUnmarshaller {\n\n\t\tpublic CustomSamlObjectUnmarshaller() {\n\t\t\tsuper();\n\t\t}\n\n\t\t@Override\n\t\tprotected void processChildElement(XMLObject parentXMLObject, XMLObject childXMLObject)\n\t\t\t\tthrows UnmarshallingException {\n\t\t\tfinal CustomOpenSamlObject customSamlObject = (CustomOpenSamlObject) parentXMLObject;\n\t\t\tcustomSamlObject.getUnknownXMLObjects().add(childXMLObject);\n\t\t}\n\n\t\t@Override\n\t\tprotected XMLObject buildXMLObject(Element domElement) {\n\t\t\treturn new CustomOpenSamlObjectImpl(SAMLConstants.SAML20_NS, AttributeValue.DEFAULT_ELEMENT_LOCAL_NAME,\n\t\t\t\t\tCustomOpenSamlObject.TYPE_CUSTOM_PREFIX);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSaml5LogoutRequestValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlOperations.SignatureConfigurer;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OpenSaml5LogoutRequestValidator}\n *\n * @author Josh Cummings\n */\npublic class OpenSaml5LogoutRequestValidatorTests {\n\n\tprivate final OpenSamlOperations saml = new OpenSaml5Template();\n\n\tprivate final OpenSaml5LogoutRequestValidator validator = new OpenSaml5LogoutRequestValidator();\n\n\t@Test\n\tpublic void handleWhenPostBindingThenValidates() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tsign(logoutRequest, registration);\n\t\tSaml2LogoutRequest request = post(logoutRequest, registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,\n\t\t\t\tregistration, authentication(registration));\n\t\tSaml2LogoutValidatorResult result = this.validator.validate(parameters);\n\t\tassertThat(result.hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void handleWhenNameIdIsEncryptedIdPostThenValidates() {\n\n\t\tRelyingPartyRegistration registration = decrypting(encrypting(registration())).build();\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequestNameIdInEncryptedId(registration);\n\t\tsign(logoutRequest, registration);\n\t\tSaml2LogoutRequest request = post(logoutRequest, registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,\n\t\t\t\tregistration, authentication(registration));\n\t\tSaml2LogoutValidatorResult result = this.validator.validate(parameters);\n\t\tassertThat(result.hasErrors()).withFailMessage(() -> result.getErrors().toString()).isFalse();\n\n\t}\n\n\t@Test\n\tpublic void handleWhenRedirectBindingThenValidatesSignatureParameter() {\n\t\tRelyingPartyRegistration registration = registration()\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.REDIRECT))\n\t\t\t.build();\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tSaml2LogoutRequest request = redirect(logoutRequest, registration,\n\t\t\t\tthis.saml.withSigningKeys(registration.getSigningX509Credentials()));\n\t\tSaml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,\n\t\t\t\tregistration, authentication(registration));\n\t\tSaml2LogoutValidatorResult result = this.validator.validate(parameters);\n\t\tassertThat(result.hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void handleWhenInvalidIssuerThenInvalidSignatureError() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tlogoutRequest.getIssuer().setValue(\"wrong\");\n\t\tsign(logoutRequest, registration);\n\t\tSaml2LogoutRequest request = post(logoutRequest, registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,\n\t\t\t\tregistration, authentication(registration));\n\t\tSaml2LogoutValidatorResult result = this.validator.validate(parameters);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_SIGNATURE);\n\t}\n\n\t@Test\n\tpublic void handleWhenMismatchedUserThenInvalidRequestError() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tlogoutRequest.getNameID().setValue(\"wrong\");\n\t\tsign(logoutRequest, registration);\n\t\tSaml2LogoutRequest request = post(logoutRequest, registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,\n\t\t\t\tregistration, authentication(registration));\n\t\tSaml2LogoutValidatorResult result = this.validator.validate(parameters);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_REQUEST);\n\t}\n\n\t@Test\n\tpublic void handleWhenMissingUserThenSubjectNotFoundError() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tlogoutRequest.setNameID(null);\n\t\tsign(logoutRequest, registration);\n\t\tSaml2LogoutRequest request = post(logoutRequest, registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,\n\t\t\t\tregistration, authentication(registration));\n\t\tSaml2LogoutValidatorResult result = this.validator.validate(parameters);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.SUBJECT_NOT_FOUND);\n\t}\n\n\t@Test\n\tpublic void handleWhenMismatchedDestinationThenInvalidDestinationError() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tlogoutRequest.setDestination(\"wrong\");\n\t\tsign(logoutRequest, registration);\n\t\tSaml2LogoutRequest request = post(logoutRequest, registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,\n\t\t\t\tregistration, authentication(registration));\n\t\tSaml2LogoutValidatorResult result = this.validator.validate(parameters);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_DESTINATION);\n\t}\n\n\t// gh-10923\n\t@Test\n\tpublic void handleWhenLogoutResponseHasLineBreaksThenHandles() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\tsign(logoutRequest, registration);\n\t\tString encoded = new StringBuffer(\n\t\t\t\tSaml2Utils.samlEncode(serialize(logoutRequest).getBytes(StandardCharsets.UTF_8)))\n\t\t\t.insert(10, \"\\r\\n\")\n\t\t\t.toString();\n\t\tSaml2LogoutRequest request = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(encoded)\n\t\t\t.build();\n\t\tSaml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(request,\n\t\t\t\tregistration, authentication(registration));\n\t\tSaml2LogoutValidatorResult result = this.validator.validate(parameters);\n\t\tassertThat(result.hasErrors()).isFalse();\n\t}\n\n\tprivate RelyingPartyRegistration.Builder registration() {\n\t\treturn signing(verifying(TestRelyingPartyRegistrations.noCredentials()))\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST));\n\t}\n\n\tprivate RelyingPartyRegistration.Builder decrypting(RelyingPartyRegistration.Builder builder) {\n\t\treturn builder\n\t\t\t.decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyDecryptingCredential()));\n\t}\n\n\tprivate RelyingPartyRegistration.Builder encrypting(RelyingPartyRegistration.Builder builder) {\n\t\treturn builder.assertingPartyMetadata((party) -> party\n\t\t\t.encryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartyEncryptingCredential())));\n\t}\n\n\tprivate RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) {\n\t\treturn builder.assertingPartyMetadata((party) -> party\n\t\t\t.verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())));\n\t}\n\n\tprivate RelyingPartyRegistration.Builder signing(RelyingPartyRegistration.Builder builder) {\n\t\treturn builder.signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartySigningCredential()));\n\t}\n\n\tprivate Authentication authentication(RelyingPartyRegistration registration) {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\", new HashMap<>());\n\t\tprincipal.setRelyingPartyRegistrationId(registration.getRegistrationId());\n\t\treturn new Saml2Authentication(principal, \"response\", new ArrayList<>());\n\t}\n\n\tprivate Saml2LogoutRequest post(LogoutRequest logoutRequest, RelyingPartyRegistration registration) {\n\t\treturn Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(Saml2Utils.samlEncode(serialize(logoutRequest).getBytes(StandardCharsets.UTF_8)))\n\t\t\t.build();\n\t}\n\n\tprivate Saml2LogoutRequest redirect(LogoutRequest logoutRequest, RelyingPartyRegistration registration,\n\t\t\tSignatureConfigurer configurer) {\n\t\tString serialized = Saml2Utils.samlEncode(Saml2Utils.samlDeflate(serialize(logoutRequest)));\n\t\tMap<String, String> parameters = configurer.sign(Map.of(Saml2ParameterNames.SAML_REQUEST, serialized));\n\t\treturn Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(serialized)\n\t\t\t.parameters((params) -> params.putAll(parameters))\n\t\t\t.build();\n\t}\n\n\tprivate void sign(LogoutRequest logoutRequest, RelyingPartyRegistration registration) {\n\t\tTestOpenSamlObjects.signed(logoutRequest, registration.getSigningX509Credentials().iterator().next(),\n\t\t\t\tregistration.getAssertingPartyMetadata().getEntityId());\n\t}\n\n\tprivate String serialize(XMLObject object) {\n\t\treturn this.saml.serialize(object).serialize();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSaml5LogoutResponseValidatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication.logout;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.saml2.core.LogoutResponse;\nimport org.opensaml.saml.saml2.core.StatusCode;\n\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlOperations.SignatureConfigurer;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OpenSaml5LogoutResponseValidator}\n *\n * @author Josh Cummings\n */\npublic class OpenSaml5LogoutResponseValidatorTests {\n\n\tprivate final OpenSamlOperations saml = new OpenSaml5Template();\n\n\tprivate final OpenSaml5LogoutResponseValidator manager = new OpenSaml5LogoutResponseValidator();\n\n\t@Test\n\tpublic void handleWhenAuthenticatedThenHandles() {\n\t\tRelyingPartyRegistration registration = signing(verifying(registration())).build();\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.id(\"id\")\n\t\t\t.samlRequest(\"logout_request\")\n\t\t\t.build();\n\t\tLogoutResponse logoutResponse = TestOpenSamlObjects.assertingPartyLogoutResponse(registration);\n\t\tsign(logoutResponse, registration);\n\t\tSaml2LogoutResponse response = post(logoutResponse, registration);\n\t\tSaml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(response,\n\t\t\t\tlogoutRequest, registration);\n\t\tthis.manager.validate(parameters);\n\t}\n\n\t@Test\n\tpublic void handleWhenRedirectBindingThenValidatesSignatureParameter() {\n\t\tRelyingPartyRegistration registration = signing(verifying(registration()))\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.REDIRECT))\n\t\t\t.build();\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.id(\"id\")\n\t\t\t.samlRequest(\"logout_request\")\n\t\t\t.build();\n\t\tLogoutResponse logoutResponse = TestOpenSamlObjects.assertingPartyLogoutResponse(registration);\n\t\tSaml2LogoutResponse response = redirect(logoutResponse, registration,\n\t\t\t\tthis.saml.withSigningKeys(registration.getSigningX509Credentials()));\n\t\tSaml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(response,\n\t\t\t\tlogoutRequest, registration);\n\t\tthis.manager.validate(parameters);\n\t}\n\n\t@Test\n\tpublic void handleWhenInvalidIssuerThenInvalidSignatureError() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.id(\"id\")\n\t\t\t.samlRequest(\"logout_request\")\n\t\t\t.build();\n\t\tLogoutResponse logoutResponse = TestOpenSamlObjects.assertingPartyLogoutResponse(registration);\n\t\tlogoutResponse.getIssuer().setValue(\"wrong\");\n\t\tsign(logoutResponse, registration);\n\t\tSaml2LogoutResponse response = post(logoutResponse, registration);\n\t\tSaml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(response,\n\t\t\t\tlogoutRequest, registration);\n\t\tSaml2LogoutValidatorResult result = this.manager.validate(parameters);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_SIGNATURE);\n\t}\n\n\t@Test\n\tpublic void handleWhenMismatchedDestinationThenInvalidDestinationError() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.id(\"id\")\n\t\t\t.samlRequest(\"logout_request\")\n\t\t\t.build();\n\t\tLogoutResponse logoutResponse = TestOpenSamlObjects.assertingPartyLogoutResponse(registration);\n\t\tlogoutResponse.setDestination(\"wrong\");\n\t\tsign(logoutResponse, registration);\n\t\tSaml2LogoutResponse response = post(logoutResponse, registration);\n\t\tSaml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(response,\n\t\t\t\tlogoutRequest, registration);\n\t\tSaml2LogoutValidatorResult result = this.manager.validate(parameters);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_DESTINATION);\n\t}\n\n\t@Test\n\tpublic void handleWhenStatusNotSuccessThenInvalidResponseError() {\n\t\tRelyingPartyRegistration registration = registration().build();\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.id(\"id\")\n\t\t\t.samlRequest(\"logout_request\")\n\t\t\t.build();\n\t\tLogoutResponse logoutResponse = TestOpenSamlObjects.assertingPartyLogoutResponse(registration);\n\t\tlogoutResponse.getStatus().getStatusCode().setValue(StatusCode.UNKNOWN_PRINCIPAL);\n\t\tsign(logoutResponse, registration);\n\t\tSaml2LogoutResponse response = post(logoutResponse, registration);\n\t\tSaml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(response,\n\t\t\t\tlogoutRequest, registration);\n\t\tSaml2LogoutValidatorResult result = this.manager.validate(parameters);\n\t\tassertThat(result.hasErrors()).isTrue();\n\t\tassertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE);\n\t}\n\n\t// gh-10923\n\t@Test\n\tpublic void handleWhenLogoutResponseHasLineBreaksThenHandles() {\n\t\tRelyingPartyRegistration registration = signing(verifying(registration())).build();\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.id(\"id\")\n\t\t\t.samlRequest(\"logout_request\")\n\t\t\t.build();\n\t\tLogoutResponse logoutResponse = TestOpenSamlObjects.assertingPartyLogoutResponse(registration);\n\t\tsign(logoutResponse, registration);\n\t\tString encoded = new StringBuilder(\n\t\t\t\tSaml2Utils.samlEncode(serialize(logoutResponse).getBytes(StandardCharsets.UTF_8)))\n\t\t\t.insert(10, \"\\r\\n\")\n\t\t\t.toString();\n\t\tSaml2LogoutResponse response = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(encoded)\n\t\t\t.build();\n\t\tSaml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(response,\n\t\t\t\tlogoutRequest, registration);\n\t\tthis.manager.validate(parameters);\n\t}\n\n\tprivate RelyingPartyRegistration.Builder registration() {\n\t\treturn signing(verifying(TestRelyingPartyRegistrations.noCredentials()))\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST));\n\t}\n\n\tprivate RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) {\n\t\treturn builder.assertingPartyMetadata((party) -> party\n\t\t\t.verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())));\n\t}\n\n\tprivate RelyingPartyRegistration.Builder signing(RelyingPartyRegistration.Builder builder) {\n\t\treturn builder.signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartySigningCredential()));\n\t}\n\n\tprivate Saml2LogoutResponse post(LogoutResponse logoutResponse, RelyingPartyRegistration registration) {\n\t\treturn Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(Saml2Utils.samlEncode(serialize(logoutResponse).getBytes(StandardCharsets.UTF_8)))\n\t\t\t.build();\n\t}\n\n\tprivate Saml2LogoutResponse redirect(LogoutResponse logoutResponse, RelyingPartyRegistration registration,\n\t\t\tSignatureConfigurer<?> configurer) {\n\t\tString serialized = Saml2Utils.samlEncode(Saml2Utils.samlDeflate(serialize(logoutResponse)));\n\t\tMap<String, String> parameters = configurer.sign(Map.of(Saml2ParameterNames.SAML_RESPONSE, serialized));\n\t\treturn Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(serialized)\n\t\t\t.parameters((params) -> params.putAll(parameters))\n\t\t\t.build();\n\t}\n\n\tprivate void sign(LogoutResponse logoutResponse, RelyingPartyRegistration registration) {\n\t\tTestOpenSamlObjects.signed(logoutResponse, registration.getSigningX509Credentials().iterator().next(),\n\t\t\t\tregistration.getAssertingPartyMetadata().getEntityId());\n\t}\n\n\tprivate String serialize(XMLObject object) {\n\t\treturn this.saml.serialize(object).serialize();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/metadata/OpenSaml5MetadataResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OpenSaml5MetadataResolver}\n */\npublic class OpenSaml5MetadataResolverTests {\n\n\t@Test\n\tpublic void resolveWhenRelyingPartyThenMetadataMatches() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tOpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver();\n\t\tString metadata = metadataResolver.resolve(relyingPartyRegistration);\n\t\tassertThat(metadata).contains(\"<md:EntityDescriptor\")\n\t\t\t.contains(\"entityID=\\\"rp-entity-id\\\"\")\n\t\t\t.contains(\"<md:KeyDescriptor use=\\\"signing\\\">\")\n\t\t\t.contains(\"<md:KeyDescriptor use=\\\"encryption\\\">\")\n\t\t\t.contains(\"<ds:X509Certificate>MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBh\")\n\t\t\t.contains(\"Binding=\\\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\\\"\")\n\t\t\t.contains(\"Location=\\\"https://rp.example.org/acs\\\" index=\\\"1\\\"\")\n\t\t\t.contains(\"ResponseLocation=\\\"https://rp.example.org/logout/saml2/response\\\"\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenRelyingPartyAndSignMetadataSetThenMetadataMatches() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tOpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver();\n\t\tmetadataResolver.setSignMetadata(true);\n\t\tString metadata = metadataResolver.resolve(relyingPartyRegistration);\n\t\tassertThat(metadata).contains(\"<md:EntityDescriptor\")\n\t\t\t.contains(\"entityID=\\\"rp-entity-id\\\"\")\n\t\t\t.contains(\"<md:KeyDescriptor use=\\\"signing\\\">\")\n\t\t\t.contains(\"<md:KeyDescriptor use=\\\"encryption\\\">\")\n\t\t\t.contains(\"<ds:X509Certificate>MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBh\")\n\t\t\t.contains(\"Binding=\\\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\\\"\")\n\t\t\t.contains(\"Location=\\\"https://rp.example.org/acs\\\" index=\\\"1\\\"\")\n\t\t\t.contains(\"ResponseLocation=\\\"https://rp.example.org/logout/saml2/response\\\"\")\n\t\t\t.contains(\"Signature xmlns:ds=\\\"http://www.w3.org/2000/09/xmldsig#\\\"\")\n\t\t\t.contains(\"CanonicalizationMethod Algorithm=\\\"http://www.w3.org/2001/10/xml-exc-c14n#\")\n\t\t\t.contains(\"SignatureMethod Algorithm=\\\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\")\n\t\t\t.contains(\"Reference URI=\\\"\\\"\")\n\t\t\t.contains(\"Transform Algorithm=\\\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\")\n\t\t\t.contains(\"Transform Algorithm=\\\"http://www.w3.org/2001/10/xml-exc-c14n#\\\"\")\n\t\t\t.contains(\"DigestMethod Algorithm=\\\"http://www.w3.org/2001/04/xmlenc#sha256\\\"\")\n\t\t\t.contains(\"DigestValue\")\n\t\t\t.contains(\"SignatureValue\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenRelyingPartyNoCredentialsThenMetadataMatches() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()\n\t\t\t.assertingPartyMetadata((party) -> party\n\t\t\t\t.verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))\n\t\t\t.build();\n\t\tOpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver();\n\t\tString metadata = metadataResolver.resolve(relyingPartyRegistration);\n\t\tassertThat(metadata).contains(\"<md:EntityDescriptor\")\n\t\t\t.contains(\"entityID=\\\"rp-entity-id\\\"\")\n\t\t\t.doesNotContain(\"<md:KeyDescriptor use=\\\"signing\\\">\")\n\t\t\t.doesNotContain(\"<md:KeyDescriptor use=\\\"encryption\\\">\")\n\t\t\t.contains(\"Binding=\\\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\\\"\")\n\t\t\t.contains(\"Location=\\\"https://rp.example.org/acs\\\" index=\\\"1\\\"\")\n\t\t\t.contains(\"ResponseLocation=\\\"https://rp.example.org/logout/saml2/response\\\"\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenRelyingPartyNameIDFormatThenMetadataMatches() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.full()\n\t\t\t.nameIdFormat(\"format\")\n\t\t\t.build();\n\t\tOpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver();\n\t\tString metadata = metadataResolver.resolve(relyingPartyRegistration);\n\t\tassertThat(metadata).contains(\"<md:NameIDFormat>format</md:NameIDFormat>\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenRelyingPartyNoLogoutThenMetadataMatches() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.full()\n\t\t\t.singleLogoutServiceLocation(null)\n\t\t\t.nameIdFormat(\"format\")\n\t\t\t.build();\n\t\tOpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver();\n\t\tString metadata = metadataResolver.resolve(relyingPartyRegistration);\n\t\tassertThat(metadata).doesNotContain(\"ResponseLocation\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenEntityDescriptorCustomizerThenUses() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.full()\n\t\t\t.entityId(\"originalEntityId\")\n\t\t\t.build();\n\t\tOpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver();\n\t\tmetadataResolver.setEntityDescriptorCustomizer(\n\t\t\t\t(parameters) -> parameters.getEntityDescriptor().setEntityID(\"overriddenEntityId\"));\n\t\tString metadata = metadataResolver.resolve(relyingPartyRegistration);\n\t\tassertThat(metadata).contains(\"<md:EntityDescriptor\").contains(\"entityID=\\\"overriddenEntityId\\\"\");\n\t}\n\n\t@Test\n\tpublic void resolveIterableWhenRelyingPartiesThenMetadataMatches() {\n\t\tRelyingPartyRegistration one = TestRelyingPartyRegistrations.full()\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tRelyingPartyRegistration two = TestRelyingPartyRegistrations.full()\n\t\t\t.entityId(\"two\")\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tOpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver();\n\t\tString metadata = metadataResolver.resolve(List.of(one, two));\n\t\tassertThat(metadata).contains(\"<md:EntitiesDescriptor\")\n\t\t\t.contains(\"<md:EntityDescriptor\")\n\t\t\t.contains(\"entityID=\\\"rp-entity-id\\\"\")\n\t\t\t.contains(\"entityID=\\\"two\\\"\")\n\t\t\t.contains(\"<md:KeyDescriptor use=\\\"signing\\\">\")\n\t\t\t.contains(\"<md:KeyDescriptor use=\\\"encryption\\\">\")\n\t\t\t.contains(\"<ds:X509Certificate>MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBh\")\n\t\t\t.contains(\"Binding=\\\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\\\"\")\n\t\t\t.contains(\"Location=\\\"https://rp.example.org/acs\\\" index=\\\"1\\\"\")\n\t\t\t.contains(\"ResponseLocation=\\\"https://rp.example.org/logout/saml2/response\\\"\");\n\t}\n\n\t@Test\n\tpublic void resolveIterableWhenRelyingPartiesAndSignMetadataSetThenMetadataMatches() {\n\t\tRelyingPartyRegistration one = TestRelyingPartyRegistrations.full()\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tRelyingPartyRegistration two = TestRelyingPartyRegistrations.full()\n\t\t\t.entityId(\"two\")\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tOpenSaml5MetadataResolver metadataResolver = new OpenSaml5MetadataResolver();\n\t\tmetadataResolver.setSignMetadata(true);\n\t\tString metadata = metadataResolver.resolve(List.of(one, two));\n\t\tassertThat(metadata).contains(\"<md:EntitiesDescriptor\")\n\t\t\t.contains(\"<md:EntityDescriptor\")\n\t\t\t.contains(\"entityID=\\\"rp-entity-id\\\"\")\n\t\t\t.contains(\"entityID=\\\"two\\\"\")\n\t\t\t.contains(\"<md:KeyDescriptor use=\\\"signing\\\">\")\n\t\t\t.contains(\"<md:KeyDescriptor use=\\\"encryption\\\">\")\n\t\t\t.contains(\"<ds:X509Certificate>MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBh\")\n\t\t\t.contains(\"Binding=\\\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\\\"\")\n\t\t\t.contains(\"Location=\\\"https://rp.example.org/acs\\\" index=\\\"1\\\"\")\n\t\t\t.contains(\"ResponseLocation=\\\"https://rp.example.org/logout/saml2/response\\\"\")\n\t\t\t.contains(\"Signature xmlns:ds=\\\"http://www.w3.org/2000/09/xmldsig#\\\"\")\n\t\t\t.contains(\"CanonicalizationMethod Algorithm=\\\"http://www.w3.org/2001/10/xml-exc-c14n#\")\n\t\t\t.contains(\"SignatureMethod Algorithm=\\\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\")\n\t\t\t.contains(\"Reference URI=\\\"\\\"\")\n\t\t\t.contains(\"Transform Algorithm=\\\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\")\n\t\t\t.contains(\"Transform Algorithm=\\\"http://www.w3.org/2001/10/xml-exc-c14n#\\\"\")\n\t\t\t.contains(\"DigestMethod Algorithm=\\\"http://www.w3.org/2001/04/xmlenc#sha256\\\"\")\n\t\t\t.contains(\"DigestValue\")\n\t\t\t.contains(\"SignatureValue\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/registration/OpenSaml5AssertingPartyMetadataRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.UncheckedIOException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\nimport net.shibboleth.shared.xml.SerializeSupport;\nimport okhttp3.mockwebserver.Dispatcher;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport okhttp3.mockwebserver.RecordedRequest;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.Marshaller;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.saml.metadata.IterableMetadataSource;\nimport org.opensaml.saml.metadata.resolver.MetadataResolver;\nimport org.opensaml.saml.metadata.resolver.impl.FilesystemMetadataResolver;\nimport org.opensaml.saml.metadata.resolver.index.impl.RoleMetadataIndex;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\nimport org.opensaml.security.credential.Credential;\nimport org.w3c.dom.Element;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.withSettings;\n\n/**\n * Tests for {@link BaseOpenSamlAssertingPartyMetadataRepository}\n */\npublic class OpenSaml5AssertingPartyMetadataRepositoryTests {\n\n\tprivate static MetadataDispatcher dispatcher = new MetadataDispatcher()\n\t\t.addResponse(\"/entity.xml\", readFile(\"test-metadata.xml\"))\n\t\t.addResponse(\"/entities.xml\", readFile(\"test-entitiesdescriptor.xml\"));\n\n\tprivate static MockWebServer web = new MockWebServer();\n\n\tprivate static String readFile(String fileName) {\n\t\ttry {\n\t\t\tClassPathResource resource = new ClassPathResource(fileName);\n\t\t\ttry (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {\n\t\t\t\treturn reader.lines().collect(Collectors.joining());\n\t\t\t}\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new UncheckedIOException(ex);\n\t\t}\n\t}\n\n\t@BeforeAll\n\tpublic static void start() throws Exception {\n\t\tweb.setDispatcher(dispatcher);\n\t\tweb.start();\n\t}\n\n\t@AfterAll\n\tpublic static void shutdown() throws Exception {\n\t\tweb.shutdown();\n\t}\n\n\t@Test\n\tpublic void withMetadataUrlLocationWhenResolvableThenFindByEntityIdReturns() throws Exception {\n\t\tAssertingPartyMetadataRepository parties = OpenSaml5AssertingPartyMetadataRepository\n\t\t\t.withTrustedMetadataLocation(web.url(\"/entity.xml\").toString())\n\t\t\t.build();\n\t\tAssertingPartyMetadata party = parties.findByEntityId(\"https://idp.example.com/idp/shibboleth\");\n\t\tassertThat(party.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\tassertThat(party.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\tassertThat(party.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(party.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(party.getEncryptionX509Credentials()).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void withMetadataUrlLocationnWhenResolvableThenIteratorReturns() throws Exception {\n\t\tList<AssertingPartyMetadata> parties = new ArrayList<>();\n\t\tOpenSaml5AssertingPartyMetadataRepository.withTrustedMetadataLocation(web.url(\"/entities.xml\").toString())\n\t\t\t.build()\n\t\t\t.iterator()\n\t\t\t.forEachRemaining(parties::add);\n\t\tassertThat(parties).hasSize(2);\n\t\tassertThat(parties).extracting(AssertingPartyMetadata::getEntityId)\n\t\t\t.contains(\"https://ap.example.org/idp/shibboleth\", \"https://idp.example.com/idp/shibboleth\");\n\t}\n\n\t@Test\n\tpublic void withMetadataUrlLocationWhenUnresolvableThenThrowsSaml2Exception() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tString url = server.url(\"/\").toString();\n\t\t\tserver.shutdown();\n\t\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t\t.isThrownBy(() -> OpenSaml5AssertingPartyMetadataRepository.withTrustedMetadataLocation(url).build());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void withMetadataUrlLocationWhenMalformedResponseThenSaml2Exception() throws Exception {\n\t\tdispatcher.addResponse(\"/malformed\", \"malformed\");\n\t\tString url = web.url(\"/malformed\").toString();\n\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t.isThrownBy(() -> OpenSaml5AssertingPartyMetadataRepository.withTrustedMetadataLocation(url).build());\n\t}\n\n\t@Test\n\tpublic void fromMetadataFileLocationWhenResolvableThenFindByEntityIdReturns() {\n\t\tFile file = new File(\"src/test/resources/test-metadata.xml\");\n\t\tAssertingPartyMetadata party = OpenSaml5AssertingPartyMetadataRepository\n\t\t\t.withTrustedMetadataLocation(\"file:\" + file.getAbsolutePath())\n\t\t\t.build()\n\t\t\t.findByEntityId(\"https://idp.example.com/idp/shibboleth\");\n\t\tassertThat(party.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\tassertThat(party.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\tassertThat(party.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(party.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(party.getEncryptionX509Credentials()).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void fromMetadataFileLocationWhenResolvableThenIteratorReturns() {\n\t\tFile file = new File(\"src/test/resources/test-entitiesdescriptor.xml\");\n\t\tCollection<AssertingPartyMetadata> parties = new ArrayList<>();\n\t\tOpenSaml5AssertingPartyMetadataRepository.withTrustedMetadataLocation(\"file:\" + file.getAbsolutePath())\n\t\t\t.build()\n\t\t\t.iterator()\n\t\t\t.forEachRemaining(parties::add);\n\t\tassertThat(parties).hasSize(2);\n\t\tassertThat(parties).extracting(AssertingPartyMetadata::getEntityId)\n\t\t\t.contains(\"https://idp.example.com/idp/shibboleth\", \"https://ap.example.org/idp/shibboleth\");\n\t}\n\n\t@Test\n\tpublic void withMetadataFileLocationWhenNotFoundThenSaml2Exception() {\n\t\tassertThatExceptionOfType(Saml2Exception.class).isThrownBy(\n\t\t\t\t() -> OpenSaml5AssertingPartyMetadataRepository.withTrustedMetadataLocation(\"file:path\").build());\n\t}\n\n\t@Test\n\tpublic void fromMetadataClasspathLocationWhenResolvableThenFindByEntityIdReturns() {\n\t\tAssertingPartyMetadata party = OpenSaml5AssertingPartyMetadataRepository\n\t\t\t.withTrustedMetadataLocation(\"classpath:test-entitiesdescriptor.xml\")\n\t\t\t.build()\n\t\t\t.findByEntityId(\"https://ap.example.org/idp/shibboleth\");\n\t\tassertThat(party.getEntityId()).isEqualTo(\"https://ap.example.org/idp/shibboleth\");\n\t\tassertThat(party.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://ap.example.org/idp/profile/SAML2/POST/SSO\");\n\t\tassertThat(party.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(party.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(party.getEncryptionX509Credentials()).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void fromMetadataClasspathLocationWhenResolvableThenIteratorReturns() {\n\t\tCollection<AssertingPartyMetadata> parties = new ArrayList<>();\n\t\tOpenSaml5AssertingPartyMetadataRepository.withTrustedMetadataLocation(\"classpath:test-entitiesdescriptor.xml\")\n\t\t\t.build()\n\t\t\t.iterator()\n\t\t\t.forEachRemaining(parties::add);\n\t\tassertThat(parties).hasSize(2);\n\t\tassertThat(parties).extracting(AssertingPartyMetadata::getEntityId)\n\t\t\t.contains(\"https://idp.example.com/idp/shibboleth\", \"https://ap.example.org/idp/shibboleth\");\n\t}\n\n\t@Test\n\tpublic void withMetadataClasspathLocationWhenNotFoundThenSaml2Exception() {\n\t\tassertThatExceptionOfType(Saml2Exception.class).isThrownBy(\n\t\t\t\t() -> OpenSaml5AssertingPartyMetadataRepository.withTrustedMetadataLocation(\"classpath:path\").build());\n\t}\n\n\t@Test\n\tpublic void withTrustedMetadataLocationWhenMatchingCredentialsThenVerifiesSignature() throws IOException {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tEntityDescriptor descriptor = TestOpenSamlObjects.entityDescriptor(registration);\n\t\tTestOpenSamlObjects.signed(descriptor, TestSaml2X509Credentials.assertingPartySigningCredential(),\n\t\t\t\tdescriptor.getEntityID());\n\t\tString serialized = serialize(descriptor);\n\t\tCredential credential = TestOpenSamlObjects\n\t\t\t.getSigningCredential(TestSaml2X509Credentials.relyingPartyVerifyingCredential(), descriptor.getEntityID());\n\t\tString endpoint = \"/\" + UUID.randomUUID().toString();\n\t\tdispatcher.addResponse(endpoint, serialized);\n\t\tAssertingPartyMetadataRepository parties = OpenSaml5AssertingPartyMetadataRepository\n\t\t\t.withTrustedMetadataLocation(web.url(endpoint).toString())\n\t\t\t.verificationCredentials((c) -> c.add(credential))\n\t\t\t.build();\n\t\tassertThat(parties.findByEntityId(registration.getAssertingPartyMetadata().getEntityId())).isNotNull();\n\t}\n\n\t@Test\n\tpublic void withTrustedMetadataLocationWhenMismatchingCredentialsThenSaml2Exception() throws IOException {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tEntityDescriptor descriptor = TestOpenSamlObjects.entityDescriptor(registration);\n\t\tTestOpenSamlObjects.signed(descriptor, TestSaml2X509Credentials.relyingPartySigningCredential(),\n\t\t\t\tdescriptor.getEntityID());\n\t\tString serialized = serialize(descriptor);\n\t\tCredential credential = TestOpenSamlObjects\n\t\t\t.getSigningCredential(TestSaml2X509Credentials.relyingPartyVerifyingCredential(), descriptor.getEntityID());\n\t\tString endpoint = \"/\" + UUID.randomUUID().toString();\n\t\tdispatcher.addResponse(endpoint, serialized);\n\t\tassertThatExceptionOfType(Saml2Exception.class).isThrownBy(() -> OpenSaml5AssertingPartyMetadataRepository\n\t\t\t.withTrustedMetadataLocation(web.url(endpoint).toString())\n\t\t\t.verificationCredentials((c) -> c.add(credential))\n\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void withTrustedMetadataLocationWhenNoCredentialsThenSkipsVerifySignature() throws IOException {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tEntityDescriptor descriptor = TestOpenSamlObjects.entityDescriptor(registration);\n\t\tTestOpenSamlObjects.signed(descriptor, TestSaml2X509Credentials.assertingPartySigningCredential(),\n\t\t\t\tdescriptor.getEntityID());\n\t\tString serialized = serialize(descriptor);\n\t\tString endpoint = \"/\" + UUID.randomUUID().toString();\n\t\tdispatcher.addResponse(endpoint, serialized);\n\t\tAssertingPartyMetadataRepository parties = OpenSaml5AssertingPartyMetadataRepository\n\t\t\t.withTrustedMetadataLocation(web.url(endpoint).toString())\n\t\t\t.build();\n\t\tassertThat(parties.findByEntityId(registration.getAssertingPartyMetadata().getEntityId())).isNotNull();\n\t}\n\n\t@Test\n\tpublic void withTrustedMetadataLocationWhenCustomResourceLoaderThenUses() {\n\t\tResourceLoader resourceLoader = mock(ResourceLoader.class);\n\t\tgiven(resourceLoader.getResource(any())).willReturn(new ClassPathResource(\"test-metadata.xml\"));\n\t\tAssertingPartyMetadata party = OpenSaml5AssertingPartyMetadataRepository\n\t\t\t.withTrustedMetadataLocation(\"classpath:wrong\")\n\t\t\t.resourceLoader(resourceLoader)\n\t\t\t.build()\n\t\t\t.iterator()\n\t\t\t.next();\n\t\tassertThat(party.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\tassertThat(party.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\tassertThat(party.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(party.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(party.getEncryptionX509Credentials()).hasSize(1);\n\t\tverify(resourceLoader).getResource(any());\n\t}\n\n\t@Test\n\tpublic void constructorWhenNoIndexAndNoIteratorThenException() {\n\t\tMetadataResolver resolver = mock(MetadataResolver.class);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new OpenSaml5AssertingPartyMetadataRepository(resolver));\n\t}\n\n\t@Test\n\tpublic void constructorWhenIterableResolverThenUses() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tEntityDescriptor descriptor = TestOpenSamlObjects.entityDescriptor(registration);\n\t\tMetadataResolver resolver = mock(MetadataResolver.class,\n\t\t\t\twithSettings().extraInterfaces(IterableMetadataSource.class));\n\t\tgiven(((IterableMetadataSource) resolver).iterator()).willReturn(List.of(descriptor).iterator());\n\t\tAssertingPartyMetadataRepository parties = new OpenSaml5AssertingPartyMetadataRepository(resolver);\n\t\tparties.iterator()\n\t\t\t.forEachRemaining((p) -> assertThat(p.getEntityId())\n\t\t\t\t.isEqualTo(registration.getAssertingPartyMetadata().getEntityId()));\n\t\tverify(((IterableMetadataSource) resolver)).iterator();\n\t}\n\n\t@Test\n\tpublic void constructorWhenIndexedResolverThenUses() throws Exception {\n\t\tFilesystemMetadataResolver resolver = new FilesystemMetadataResolver(\n\t\t\t\tnew ClassPathResource(\"test-metadata.xml\").getFile());\n\t\tresolver.setIndexes(Set.of(new RoleMetadataIndex()));\n\t\tresolver.setId(\"id\");\n\t\tresolver.setParserPool(XMLObjectProviderRegistrySupport.getParserPool());\n\t\tresolver.initialize();\n\t\tMetadataResolver spied = spy(resolver);\n\t\tAssertingPartyMetadataRepository parties = new OpenSaml5AssertingPartyMetadataRepository(spied);\n\t\tparties.iterator()\n\t\t\t.forEachRemaining((p) -> assertThat(p.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\"));\n\t\tverify(spied).resolve(any());\n\t}\n\n\t@Test\n\tpublic void withMetadataLocationWhenNoCredentialsThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(\n\t\t\t\t() -> OpenSaml5AssertingPartyMetadataRepository.withMetadataLocation(\"classpath:test-metadata.xml\")\n\t\t\t\t\t.build());\n\t}\n\n\t@Test\n\tpublic void withMetadataLocationWhenMatchingCredentialsThenVerifiesSignature() throws IOException {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tEntityDescriptor descriptor = TestOpenSamlObjects.entityDescriptor(registration);\n\t\tTestOpenSamlObjects.signed(descriptor, TestSaml2X509Credentials.assertingPartySigningCredential(),\n\t\t\t\tdescriptor.getEntityID());\n\t\tString serialized = serialize(descriptor);\n\t\tCredential credential = TestOpenSamlObjects\n\t\t\t.getSigningCredential(TestSaml2X509Credentials.relyingPartyVerifyingCredential(), descriptor.getEntityID());\n\t\tString endpoint = \"/\" + UUID.randomUUID().toString();\n\t\tdispatcher.addResponse(endpoint, serialized);\n\t\tAssertingPartyMetadataRepository parties = OpenSaml5AssertingPartyMetadataRepository\n\t\t\t.withMetadataLocation(web.url(endpoint).toString())\n\t\t\t.verificationCredentials((c) -> c.add(credential))\n\t\t\t.build();\n\t\tassertThat(parties.findByEntityId(registration.getAssertingPartyMetadata().getEntityId())).isNotNull();\n\t}\n\n\tprivate static String serialize(XMLObject object) {\n\t\ttry {\n\t\t\tMarshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object);\n\t\t\tElement element = marshaller.marshall(object);\n\t\t\treturn SerializeSupport.nodeToString(element);\n\t\t}\n\t\tcatch (MarshallingException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\tprivate static final class MetadataDispatcher extends Dispatcher {\n\n\t\tprivate final MockResponse head = new MockResponse();\n\n\t\tprivate final Map<String, MockResponse> responses = new ConcurrentHashMap<>();\n\n\t\tprivate MetadataDispatcher() {\n\t\t}\n\n\t\t@Override\n\t\tpublic MockResponse dispatch(RecordedRequest request) throws InterruptedException {\n\t\t\tif (\"HEAD\".equals(request.getMethod())) {\n\t\t\t\treturn this.head;\n\t\t\t}\n\t\t\treturn this.responses.get(request.getPath());\n\t\t}\n\n\t\tprivate MetadataDispatcher addResponse(String path, String body) {\n\t\t\tthis.responses.put(path, new MockResponse().setBody(body).setResponseCode(200));\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/web/OpenSaml5AuthenticationTokenConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Instant;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.saml.common.SignableSAMLObject;\nimport org.opensaml.saml.saml2.core.Response;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2Utils;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\nimport org.springframework.util.StreamUtils;\nimport org.springframework.web.util.UriUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link OpenSaml5AuthenticationTokenConverter}\n */\n@ExtendWith(MockitoExtension.class)\npublic final class OpenSaml5AuthenticationTokenConverterTests {\n\n\t@Mock\n\tRelyingPartyRegistrationRepository registrations;\n\n\tprivate final OpenSamlOperations saml = new OpenSaml5Template();\n\n\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\n\t@Test\n\tpublic void convertWhenSamlResponseThenToken() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tgiven(this.registrations.findByRegistrationId(any())).willReturn(this.registration);\n\t\tMockHttpServletRequest request = post(\"/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE,\n\t\t\t\tSaml2Utils.samlEncode(\"response\".getBytes(StandardCharsets.UTF_8)));\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(\"response\");\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.registration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void convertWhenSamlResponseInvalidBase64ThenSaml2AuthenticationException() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tgiven(this.registrations.findByRegistrationId(any())).willReturn(this.registration);\n\t\tMockHttpServletRequest request = post(\"/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, \"invalid\");\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> converter.convert(request))\n\t\t\t.withCauseInstanceOf(IllegalArgumentException.class)\n\t\t\t.satisfies(\n\t\t\t\t\t(ex) -> assertThat(ex.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE))\n\t\t\t.satisfies(\n\t\t\t\t\t(ex) -> assertThat(ex.getSaml2Error().getDescription()).isEqualTo(\"Failed to decode SAMLResponse\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenNoSamlResponseThenNull() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tMockHttpServletRequest request = post(\"/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\tassertThat(converter.convert(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenNoMatchingRequestThenNull() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, \"ignored\");\n\t\tassertThat(converter.convert(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenNoRelyingPartyRegistrationThenNull() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tMockHttpServletRequest request = post(\"/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\tString response = Saml2Utils.samlEncode(serialize(signed(response())).getBytes(StandardCharsets.UTF_8));\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, response);\n\t\tassertThat(converter.convert(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenGetRequestThenInflates() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tgiven(this.registrations.findByRegistrationId(any())).willReturn(this.registration);\n\t\tMockHttpServletRequest request = get(\"/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\tbyte[] deflated = Saml2Utils.samlDeflate(\"response\");\n\t\tString encoded = Saml2Utils.samlEncode(deflated);\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, encoded);\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(\"response\");\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.registration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void convertWhenGetRequestInvalidDeflatedThenSaml2AuthenticationException() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tgiven(this.registrations.findByRegistrationId(any())).willReturn(this.registration);\n\t\tMockHttpServletRequest request = get(\"/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\tbyte[] invalidDeflated = \"invalid\".getBytes();\n\t\tString encoded = Saml2Utils.samlEncode(invalidDeflated);\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, encoded);\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> converter.convert(request))\n\t\t\t.withRootCauseInstanceOf(IOException.class)\n\t\t\t.satisfies(\n\t\t\t\t\t(ex) -> assertThat(ex.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE))\n\t\t\t.satisfies((ex) -> assertThat(ex.getSaml2Error().getDescription()).isEqualTo(\"Unable to inflate string\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingSamlUtilsBase64ThenXmlIsValid() throws Exception {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tgiven(this.registrations.findByRegistrationId(any())).willReturn(this.registration);\n\t\tMockHttpServletRequest request = post(\"/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, getSsoCircleEncodedXml());\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tvalidateSsoCircleXml(token.getSaml2Response());\n\t}\n\n\t@Test\n\tpublic void convertWhenSavedAuthenticationRequestThenToken() {\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = mock(\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = mock(AbstractSaml2AuthenticationRequest.class);\n\t\tgiven(authenticationRequest.getRelyingPartyRegistrationId()).willReturn(this.registration.getRegistrationId());\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tconverter.setAuthenticationRequestRepository(authenticationRequestRepository);\n\t\tgiven(this.registrations.findByRegistrationId(any())).willReturn(this.registration);\n\t\tgiven(authenticationRequestRepository.loadAuthenticationRequest(any(HttpServletRequest.class)))\n\t\t\t.willReturn(authenticationRequest);\n\t\tMockHttpServletRequest request = post(\"/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE,\n\t\t\t\tSaml2Utils.samlEncode(\"response\".getBytes(StandardCharsets.UTF_8)));\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(\"response\");\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.registration.getRegistrationId());\n\t\tassertThat(token.getAuthenticationRequest()).isEqualTo(authenticationRequest);\n\t}\n\n\t@Test\n\tpublic void convertWhenMatchingNoRegistrationIdThenLooksUpByAssertingEntityId() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tString response = serialize(signed(response()));\n\t\tString encoded = Saml2Utils.samlEncode(response.getBytes(StandardCharsets.UTF_8));\n\t\tgiven(this.registrations.findUniqueByAssertingPartyEntityId(TestOpenSamlObjects.ASSERTING_PARTY_ENTITY_ID))\n\t\t\t.willReturn(this.registration);\n\t\tMockHttpServletRequest request = post(\"/login/saml2/sso\");\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, encoded);\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(response);\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.registration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void constructorWhenResolverIsNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Saml2AuthenticationTokenConverter(null));\n\t}\n\n\t@Test\n\tpublic void setAuthenticationRequestRepositoryWhenNullThenIllegalArgument() {\n\t\tOpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(this.registrations);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> converter.setAuthenticationRequestRepository(null));\n\t}\n\n\tprivate void validateSsoCircleXml(String xml) {\n\t\tassertThat(xml).contains(\"InResponseTo=\\\"ARQ9a73ead-7dcf-45a8-89eb-26f3c9900c36\\\"\")\n\t\t\t.contains(\" ID=\\\"s246d157446618e90e43fb79bdd4d9e9e19cf2c7c4\\\"\")\n\t\t\t.contains(\"<saml:Issuer>https://idp.ssocircle.com</saml:Issuer>\");\n\t}\n\n\tprivate String getSsoCircleEncodedXml() throws IOException {\n\t\tClassPathResource resource = new ClassPathResource(\"saml2-response-sso-circle.encoded\");\n\t\tString response = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);\n\t\treturn UriUtils.decode(response, StandardCharsets.UTF_8);\n\t}\n\n\tprivate MockHttpServletRequest post(String uri) {\n\t\treturn TestMockHttpServletRequests.post(uri).build();\n\t}\n\n\tprivate MockHttpServletRequest get(String uri) {\n\t\treturn TestMockHttpServletRequests.get(uri).build();\n\t}\n\n\tprivate <T extends SignableSAMLObject> T signed(T toSign) {\n\t\tTestOpenSamlObjects.signed(toSign, TestSaml2X509Credentials.assertingPartySigningCredential(),\n\t\t\t\tTestOpenSamlObjects.RELYING_PARTY_ENTITY_ID);\n\t\treturn toSign;\n\t}\n\n\tprivate Response response() {\n\t\tResponse response = TestOpenSamlObjects.response();\n\t\tresponse.setIssueInstant(Instant.now());\n\t\treturn response;\n\t}\n\n\tprivate String serialize(XMLObject object) {\n\t\treturn this.saml.serialize(object).serialize();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml5AuthenticationRequestResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\npublic class OpenSaml5AuthenticationRequestResolverTests {\n\n\tMockHttpServletRequest request;\n\n\tRelyingPartyRegistration registration;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.request = givenRequest(\"/saml2/authenticate/registration-id\");\n\t\tthis.registration = TestRelyingPartyRegistrations.full().build();\n\t}\n\n\t@Test\n\tvoid resolveWhenRedirectThenSaml2RedirectAuthenticationRequest() {\n\t\tRelyingPartyRegistrationResolver relyingParties = mock(RelyingPartyRegistrationResolver.class);\n\t\tgiven(relyingParties.resolve(any(), any())).willReturn(this.registration);\n\t\tOpenSaml5AuthenticationRequestResolver resolver = new OpenSaml5AuthenticationRequestResolver(relyingParties);\n\t\tSaml2RedirectAuthenticationRequest authnRequest = resolver.resolve(this.request);\n\t\tassertThat(authnRequest.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(authnRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(this.registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation());\n\t}\n\n\t@Test\n\tvoid resolveWhenPostThenSaml2PostAuthenticationRequest() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertingPartyMetadata((party) -> party.singleSignOnServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.build();\n\t\tRelyingPartyRegistrationResolver relyingParties = mock(RelyingPartyRegistrationResolver.class);\n\t\tgiven(relyingParties.resolve(any(), any())).willReturn(registration);\n\t\tOpenSaml5AuthenticationRequestResolver resolver = new OpenSaml5AuthenticationRequestResolver(relyingParties);\n\t\tSaml2PostAuthenticationRequest authnRequest = resolver.resolve(this.request);\n\t\tassertThat(authnRequest.getBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(authnRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(this.registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation());\n\t}\n\n\t@Test\n\tvoid resolveWhenCustomRelayStateThenUses() {\n\t\tRelyingPartyRegistrationResolver relyingParties = mock(RelyingPartyRegistrationResolver.class);\n\t\tgiven(relyingParties.resolve(any(), any())).willReturn(this.registration);\n\t\tConverter<HttpServletRequest, String> relayState = mock(Converter.class);\n\t\tgiven(relayState.convert(any())).willReturn(\"state\");\n\t\tOpenSaml5AuthenticationRequestResolver resolver = new OpenSaml5AuthenticationRequestResolver(relyingParties);\n\t\tresolver.setRelayStateResolver(relayState);\n\t\tSaml2RedirectAuthenticationRequest authnRequest = resolver.resolve(this.request);\n\t\tassertThat(authnRequest.getRelayState()).isEqualTo(\"state\");\n\t\tverify(relayState).convert(any());\n\t}\n\n\t@Test\n\tvoid resolveWhenCustomAuthenticationUrlTHenUses() {\n\t\tRelyingPartyRegistrationResolver relyingParties = mock(RelyingPartyRegistrationResolver.class);\n\t\tgiven(relyingParties.resolve(any(), any())).willReturn(this.registration);\n\t\tOpenSaml5AuthenticationRequestResolver resolver = new OpenSaml5AuthenticationRequestResolver(relyingParties);\n\t\tresolver.setRequestMatcher(pathPattern(\"/custom/authentication/{registrationId}\"));\n\t\tSaml2RedirectAuthenticationRequest authnRequest = resolver\n\t\t\t.resolve(givenRequest(\"/custom/authentication/registration-id\"));\n\n\t\tassertThat(authnRequest.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(authnRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(this.registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation());\n\n\t}\n\n\tprivate MockHttpServletRequest givenRequest(String path) {\n\t\treturn TestMockHttpServletRequests.get(path).build();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml5SigningUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport java.util.UUID;\n\nimport javax.xml.namespace.QName;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.saml.common.SAMLVersion;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.xmlsec.signature.Signature;\n\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Test open SAML signatures\n */\npublic class OpenSaml5SigningUtilsTests {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\n\tprivate final OpenSamlOperations saml = new OpenSaml5Template();\n\n\tprivate RelyingPartyRegistration registration;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.registration = RelyingPartyRegistration.withRegistrationId(\"saml-idp\")\n\t\t\t.entityId(\"https://some.idp.example.com/entity-id\")\n\t\t\t.signingX509Credentials((c) -> {\n\t\t\t\tc.add(TestSaml2X509Credentials.relyingPartySigningCredential());\n\t\t\t\tc.add(TestSaml2X509Credentials.assertingPartySigningCredential());\n\t\t\t})\n\t\t\t.assertingPartyMetadata((c) -> c.entityId(\"https://some.idp.example.com/entity-id\")\n\t\t\t\t.singleSignOnServiceLocation(\"https://some.idp.example.com/service-location\"))\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void whenSigningAnObjectThenKeyInfoIsPartOfTheSignature() {\n\t\tResponse response = response(\"destination\", \"issuer\");\n\t\tthis.saml.withSigningKeys(this.registration.getSigningX509Credentials()).sign(response);\n\t\tSignature signature = response.getSignature();\n\t\tassertThat(signature).isNotNull();\n\t\tassertThat(signature.getKeyInfo()).isNotNull();\n\t}\n\n\tResponse response(String destination, String issuerEntityId) {\n\t\tResponse response = build(Response.DEFAULT_ELEMENT_NAME);\n\t\tresponse.setID(\"R\" + UUID.randomUUID());\n\t\tresponse.setVersion(SAMLVersion.VERSION_20);\n\t\tresponse.setID(\"_\" + UUID.randomUUID());\n\t\tresponse.setDestination(destination);\n\t\tresponse.setIssuer(issuer(issuerEntityId));\n\t\treturn response;\n\t}\n\n\tIssuer issuer(String entityId) {\n\t\tIssuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME);\n\t\tissuer.setValue(entityId);\n\t\treturn issuer;\n\t}\n\n\t<T extends XMLObject> T build(QName qName) {\n\t\treturn (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutRequestResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OpenSaml5LogoutRequestResolver}\n */\npublic class OpenSaml5LogoutRequestResolverTests {\n\n\tRelyingPartyRegistration registration;\n\n\tRelyingPartyRegistrationResolver registrationResolver;\n\n\tOpenSaml5LogoutRequestResolver logoutRequestResolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.registration = TestRelyingPartyRegistrations.full().build();\n\t\tthis.registrationResolver = mock(RelyingPartyRegistrationResolver.class);\n\t\tthis.logoutRequestResolver = new OpenSaml5LogoutRequestResolver(this.registrationResolver);\n\t}\n\n\t@Test\n\tpublic void resolveWhenCustomParametersConsumerThenUses() {\n\t\tthis.logoutRequestResolver.setParametersConsumer((parameters) -> parameters.getLogoutRequest().setID(\"myid\"));\n\t\tgiven(this.registrationResolver.resolve(any(), any())).willReturn(this.registration);\n\n\t\tSaml2LogoutRequest logoutRequest = this.logoutRequestResolver.resolve(givenRequest(), givenAuthentication());\n\n\t\tassertThat(logoutRequest.getId()).isEqualTo(\"myid\");\n\t}\n\n\t@Test\n\tpublic void setParametersConsumerWhenNullThenIllegalArgument() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.logoutRequestResolver.setParametersConsumer(null));\n\t}\n\n\t@Test\n\tpublic void resolveWhenCustomRelayStateThenUses() {\n\t\tgiven(this.registrationResolver.resolve(any(), any())).willReturn(this.registration);\n\t\tConverter<HttpServletRequest, String> relayState = mock(Converter.class);\n\t\tgiven(relayState.convert(any())).willReturn(\"any-state\");\n\t\tthis.logoutRequestResolver.setRelayStateResolver(relayState);\n\n\t\tSaml2LogoutRequest logoutRequest = this.logoutRequestResolver.resolve(givenRequest(), givenAuthentication());\n\n\t\tassertThat(logoutRequest.getRelayState()).isEqualTo(\"any-state\");\n\t\tverify(relayState).convert(any());\n\t}\n\n\tprivate static Authentication givenAuthentication() {\n\t\treturn new TestingAuthenticationToken(\"user\", \"password\");\n\t}\n\n\tprivate MockHttpServletRequest givenRequest() {\n\t\treturn new MockHttpServletRequest();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutRequestValidatorParametersResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.nio.charset.StandardCharsets;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.opensaml.core.xml.XMLObject;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.authentication.TestSaml2Authentications;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\n\n@ExtendWith(MockitoExtension.class)\npublic final class OpenSaml5LogoutRequestValidatorParametersResolverTests {\n\n\t@Mock\n\tRelyingPartyRegistrationRepository registrations;\n\n\tprivate final OpenSamlOperations saml = new OpenSaml5Template();\n\n\tprivate RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\n\tprivate OpenSaml5LogoutRequestValidatorParametersResolver resolver;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.resolver = new OpenSaml5LogoutRequestValidatorParametersResolver(this.registrations);\n\t}\n\n\t@Test\n\tvoid saml2LogoutRegistrationIdResolveWhenMatchesThenParameters() {\n\t\tString registrationId = this.registration.getRegistrationId();\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo/\" + registrationId);\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"pass\");\n\t\trequest.setParameter(Saml2ParameterNames.SAML_REQUEST, \"request\");\n\t\tgiven(this.registrations.findByRegistrationId(registrationId)).willReturn(this.registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = this.resolver.resolve(request, authentication);\n\t\tassertThat(parameters.getAuthentication()).isEqualTo(authentication);\n\t\tassertThat(parameters.getRelyingPartyRegistration().getRegistrationId()).isEqualTo(registrationId);\n\t\tassertThat(parameters.getLogoutRequest().getSamlRequest()).isEqualTo(\"request\");\n\t}\n\n\t@Test\n\tvoid saml2LogoutRegistrationIdWhenUnauthenticatedThenParameters() {\n\t\tString registrationId = this.registration.getRegistrationId();\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo/\" + registrationId);\n\t\trequest.setParameter(Saml2ParameterNames.SAML_REQUEST, \"request\");\n\t\tgiven(this.registrations.findByRegistrationId(registrationId)).willReturn(this.registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = this.resolver.resolve(request, null);\n\t\tassertThat(parameters.getAuthentication()).isNull();\n\t\tassertThat(parameters.getRelyingPartyRegistration().getRegistrationId()).isEqualTo(registrationId);\n\t\tassertThat(parameters.getLogoutRequest().getSamlRequest()).isEqualTo(\"request\");\n\t}\n\n\t@Test\n\tvoid saml2LogoutResolveWhenAuthenticatedThenParameters() {\n\t\tString registrationId = this.registration.getRegistrationId();\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\");\n\t\tAuthentication authentication = TestSaml2Authentications.authentication();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_REQUEST, \"request\");\n\t\tgiven(this.registrations.findByRegistrationId(registrationId)).willReturn(this.registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = this.resolver.resolve(request, authentication);\n\t\tassertThat(parameters.getAuthentication()).isEqualTo(authentication);\n\t\tassertThat(parameters.getRelyingPartyRegistration().getRegistrationId()).isEqualTo(registrationId);\n\t\tassertThat(parameters.getLogoutRequest().getSamlRequest()).isEqualTo(\"request\");\n\t}\n\n\t@Test\n\tvoid saml2LogoutResolveWhenUnauthenticatedThenParameters() {\n\t\tString registrationId = this.registration.getRegistrationId();\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\");\n\t\tString logoutRequest = serialize(TestOpenSamlObjects.logoutRequest());\n\t\tString encoded = Saml2Utils.samlEncode(logoutRequest.getBytes(StandardCharsets.UTF_8));\n\t\trequest.setParameter(Saml2ParameterNames.SAML_REQUEST, encoded);\n\t\tgiven(this.registrations.findUniqueByAssertingPartyEntityId(TestOpenSamlObjects.ASSERTING_PARTY_ENTITY_ID))\n\t\t\t.willReturn(this.registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = this.resolver.resolve(request, null);\n\t\tassertThat(parameters.getAuthentication()).isNull();\n\t\tassertThat(parameters.getRelyingPartyRegistration().getRegistrationId()).isEqualTo(registrationId);\n\t\tassertThat(parameters.getLogoutRequest().getSamlRequest()).isEqualTo(encoded);\n\t}\n\n\t@Test\n\tvoid saml2LogoutResolveWhenUnauthenticatedGetRequestThenInflates() {\n\t\tString registrationId = this.registration.getRegistrationId();\n\t\tMockHttpServletRequest request = get(\"/logout/saml2/slo\");\n\t\tString logoutRequest = serialize(TestOpenSamlObjects.logoutRequest());\n\t\tString encoded = Saml2Utils.samlEncode(Saml2Utils.samlDeflate(logoutRequest));\n\t\trequest.setParameter(Saml2ParameterNames.SAML_REQUEST, encoded);\n\t\tgiven(this.registrations.findUniqueByAssertingPartyEntityId(TestOpenSamlObjects.ASSERTING_PARTY_ENTITY_ID))\n\t\t\t.willReturn(this.registration);\n\t\tSaml2LogoutRequestValidatorParameters parameters = this.resolver.resolve(request, null);\n\t\tassertThat(parameters.getAuthentication()).isNull();\n\t\tassertThat(parameters.getRelyingPartyRegistration().getRegistrationId()).isEqualTo(registrationId);\n\t\tassertThat(parameters.getLogoutRequest().getSamlRequest()).isEqualTo(encoded);\n\t}\n\n\t@Test\n\tvoid saml2LogoutRegistrationIdResolveWhenNoMatchingRegistrationIdThenSaml2Exception() {\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo/id\");\n\t\trequest.setParameter(Saml2ParameterNames.SAML_REQUEST, \"request\");\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.resolver.resolve(request, null));\n\t}\n\n\tprivate MockHttpServletRequest post(String uri) {\n\t\treturn TestMockHttpServletRequests.post(uri).build();\n\t}\n\n\tprivate MockHttpServletRequest get(String uri) {\n\t\treturn TestMockHttpServletRequests.get(uri).build();\n\t}\n\n\tprivate String serialize(XMLObject object) {\n\t\treturn this.saml.serialize(object).serialize();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml5LogoutResponseResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.Test;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutResponseResolver.LogoutResponseParameters;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link OpenSaml5LogoutResponseResolver}\n */\npublic class OpenSaml5LogoutResponseResolverTests {\n\n\tprivate final OpenSamlOperations saml = new OpenSaml5Template();\n\n\tRelyingPartyRegistrationResolver relyingPartyRegistrationResolver = mock(RelyingPartyRegistrationResolver.class);\n\n\t@Test\n\tpublic void resolveWhenCustomParametersConsumerThenUses() {\n\t\tOpenSaml5LogoutResponseResolver logoutResponseResolver = new OpenSaml5LogoutResponseResolver(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tConsumer<LogoutResponseParameters> parametersConsumer = mock(Consumer.class);\n\t\tlogoutResponseResolver.setParametersConsumer(parametersConsumer);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.assertingPartyMetadata(\n\t\t\t\t\t(party) -> party.singleLogoutServiceResponseLocation(\"https://ap.example.com/logout\"))\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tLogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration);\n\t\trequest.setParameter(Saml2ParameterNames.SAML_REQUEST,\n\t\t\t\tSaml2Utils.samlEncode(this.saml.serialize(logoutRequest).serialize().getBytes()));\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration);\n\t\tSaml2LogoutResponse logoutResponse = logoutResponseResolver.resolve(request, authentication);\n\t\tassertThat(logoutResponse).isNotNull();\n\t\tverify(parametersConsumer).accept(any());\n\t}\n\n\t@Test\n\tpublic void setParametersConsumerWhenNullThenIllegalArgument() {\n\t\tOpenSaml5LogoutRequestResolver logoutRequestResolver = new OpenSaml5LogoutRequestResolver(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> logoutRequestResolver.setParametersConsumer(null));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/OpenSamlInitializationServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport org.junit.jupiter.api.Test;\nimport org.opensaml.core.config.ConfigurationService;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistry;\nimport org.opensaml.saml.saml2.core.AuthnRequest;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link OpenSamlInitializationService}\n *\n * @author Josh Cummings\n */\npublic class OpenSamlInitializationServiceTests {\n\n\t@Test\n\tpublic void initializeWhenInvokedMultipleTimesThenInitializesOnce() {\n\t\tOpenSamlInitializationService.initialize();\n\t\tXMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class);\n\t\tassertThat(registry.getBuilderFactory().getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME)).isNotNull();\n\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t.isThrownBy(() -> OpenSamlInitializationService.requireInitialize((r) -> {\n\t\t\t}))\n\t\t\t.withMessageContaining(\"OpenSAML was already initialized previously\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResultTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for verifying {@link Saml2ResponseValidatorResult}\n *\n * @author Josh Cummings\n */\npublic class Saml2ResponseValidatorResultTests {\n\n\tprivate static final Saml2Error DETAIL = new Saml2Error(\"error\", \"description\");\n\n\t@Test\n\tpublic void successWhenInvokedThenReturnsSuccessfulResult() {\n\t\tSaml2ResponseValidatorResult success = Saml2ResponseValidatorResult.success();\n\t\tassertThat(success.hasErrors()).isFalse();\n\t}\n\n\t@Test\n\tpublic void failureWhenInvokedWithDetailReturnsFailureResultIncludingDetail() {\n\t\tSaml2ResponseValidatorResult failure = Saml2ResponseValidatorResult.failure(DETAIL);\n\n\t\tassertThat(failure.hasErrors()).isTrue();\n\t\tassertThat(failure.getErrors()).containsExactly(DETAIL);\n\t}\n\n\t@Test\n\tpublic void failureWhenInvokedWithMultipleDetailsReturnsFailureResultIncludingAll() {\n\t\tSaml2ResponseValidatorResult failure = Saml2ResponseValidatorResult.failure(DETAIL, DETAIL);\n\n\t\tassertThat(failure.hasErrors()).isTrue();\n\t\tassertThat(failure.getErrors()).containsExactly(DETAIL, DETAIL);\n\t}\n\n\t@Test\n\tpublic void concatErrorWhenInvokedThenReturnsCopyContainingAll() {\n\t\tSaml2ResponseValidatorResult failure = Saml2ResponseValidatorResult.failure(DETAIL);\n\t\tSaml2ResponseValidatorResult added = failure.concat(DETAIL);\n\n\t\tassertThat(added.hasErrors()).isTrue();\n\t\tassertThat(added.getErrors()).containsExactly(DETAIL, DETAIL);\n\t\tassertThat(failure).isNotSameAs(added);\n\t}\n\n\t@Test\n\tpublic void concatResultWhenInvokedThenReturnsCopyContainingAll() {\n\t\tSaml2ResponseValidatorResult failure = Saml2ResponseValidatorResult.failure(DETAIL);\n\t\tSaml2ResponseValidatorResult merged = failure.concat(failure).concat(failure);\n\n\t\tassertThat(merged.hasErrors()).isTrue();\n\t\tassertThat(merged.getErrors()).containsExactly(DETAIL, DETAIL, DETAIL);\n\t\tassertThat(failure).isNotSameAs(merged);\n\t}\n\n\t@Test\n\tpublic void concatErrorWhenNullThenIllegalArgument() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> Saml2ResponseValidatorResult.failure(DETAIL)\n\t\t\t\t\t\t.concat((Saml2Error) null)\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void concatResultWhenNullThenIllegalArgument() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> Saml2ResponseValidatorResult.failure(DETAIL)\n\t\t\t\t\t\t.concat((Saml2ResponseValidatorResult) null)\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2Utils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.zip.Deflater;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.Inflater;\nimport java.util.zip.InflaterOutputStream;\n\nimport org.springframework.security.saml2.Saml2Exception;\n\npublic final class Saml2Utils {\n\n\tprivate Saml2Utils() {\n\t}\n\n\tpublic static String samlEncode(byte[] b) {\n\t\treturn Base64.getEncoder().encodeToString(b);\n\t}\n\n\tpublic static byte[] samlDecode(String s) {\n\t\treturn Base64.getMimeDecoder().decode(s);\n\t}\n\n\tpublic static byte[] samlDeflate(String s) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tDeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(out,\n\t\t\t\t\tnew Deflater(Deflater.DEFLATED, true));\n\t\t\tdeflaterOutputStream.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflaterOutputStream.finish();\n\t\t\treturn out.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tpublic static String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream inflaterOutputStream = new InflaterOutputStream(out, new Inflater(true));\n\t\t\tinflaterOutputStream.write(b);\n\t\t\tinflaterOutputStream.finish();\n\t\t\treturn out.toString(StandardCharsets.UTF_8.name());\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2X509CredentialTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport java.io.ByteArrayInputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.converter.RsaKeyConverters;\nimport org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\npublic class Saml2X509CredentialTests {\n\n\tprivate PrivateKey key;\n\n\tprivate X509Certificate certificate;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tString keyData = \"-----BEGIN PRIVATE KEY-----\\n\"\n\t\t\t\t+ \"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\\n\"\n\t\t\t\t+ \"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\\n\"\n\t\t\t\t+ \"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\\n\"\n\t\t\t\t+ \"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\\n\"\n\t\t\t\t+ \"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\\n\"\n\t\t\t\t+ \"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\\n\"\n\t\t\t\t+ \"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\\n\"\n\t\t\t\t+ \"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\\n\"\n\t\t\t\t+ \"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\\n\"\n\t\t\t\t+ \"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\\n\"\n\t\t\t\t+ \"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\\n\"\n\t\t\t\t+ \"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\\n\"\n\t\t\t\t+ \"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\\n\" + \"INrtuLp4YHbgk1mi\\n\"\n\t\t\t\t+ \"-----END PRIVATE KEY-----\";\n\t\tthis.key = RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(keyData.getBytes(StandardCharsets.UTF_8)));\n\t\tfinal CertificateFactory factory = CertificateFactory.getInstance(\"X.509\");\n\t\tString certificateData = \"-----BEGIN CERTIFICATE-----\\n\"\n\t\t\t\t+ \"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\\n\"\n\t\t\t\t+ \"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\\n\"\n\t\t\t\t+ \"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\\n\"\n\t\t\t\t+ \"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\\n\"\n\t\t\t\t+ \"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\\n\"\n\t\t\t\t+ \"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\\n\"\n\t\t\t\t+ \"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\\n\"\n\t\t\t\t+ \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\\n\"\n\t\t\t\t+ \"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\\n\"\n\t\t\t\t+ \"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\\n\"\n\t\t\t\t+ \"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\\n\"\n\t\t\t\t+ \"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\\n\"\n\t\t\t\t+ \"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\\n\"\n\t\t\t\t+ \"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\\n\" + \"-----END CERTIFICATE-----\";\n\t\tthis.certificate = (X509Certificate) factory\n\t\t\t.generateCertificate(new ByteArrayInputStream(certificateData.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithCredentialsThenItSucceeds() {\n\t\tnew Saml2X509Credential(this.key, this.certificate, Saml2X509CredentialType.SIGNING);\n\t\tnew Saml2X509Credential(this.key, this.certificate, Saml2X509CredentialType.SIGNING,\n\t\t\t\tSaml2X509CredentialType.DECRYPTION);\n\t\tnew Saml2X509Credential(this.key, this.certificate, Saml2X509CredentialType.DECRYPTION);\n\t\tSaml2X509Credential.signing(this.key, this.certificate);\n\t\tSaml2X509Credential.decryption(this.key, this.certificate);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAssertingPartyWithCredentialsThenItSucceeds() {\n\t\tnew Saml2X509Credential(this.certificate, Saml2X509CredentialType.VERIFICATION);\n\t\tnew Saml2X509Credential(this.certificate, Saml2X509CredentialType.VERIFICATION,\n\t\t\t\tSaml2X509CredentialType.ENCRYPTION);\n\t\tnew Saml2X509Credential(this.certificate, Saml2X509CredentialType.ENCRYPTION);\n\t\tSaml2X509Credential.verification(this.certificate);\n\t\tSaml2X509Credential.encryption(this.certificate);\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithoutCredentialsThenItFails() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new Saml2X509Credential(null, (X509Certificate) null, Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithoutPrivateKeyThenItFails() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new Saml2X509Credential(null, this.certificate, Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithoutCertificateThenItFails() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new Saml2X509Credential(this.key, null, Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAssertingPartyWithoutCertificateThenItFails() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new Saml2X509Credential(null, Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithEncryptionUsageThenItFails() {\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> new Saml2X509Credential(this.key, this.certificate, Saml2X509CredentialType.ENCRYPTION));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithVerificationUsageThenItFails() {\n\t\tassertThatIllegalStateException().isThrownBy(\n\t\t\t\t() -> new Saml2X509Credential(this.key, this.certificate, Saml2X509CredentialType.VERIFICATION));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAssertingPartyWithSigningUsageThenItFails() {\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> new Saml2X509Credential(this.certificate, Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAssertingPartyWithDecryptionUsageThenItFails() {\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> new Saml2X509Credential(this.certificate, Saml2X509CredentialType.DECRYPTION));\n\t}\n\n\t@Test\n\tpublic void factoryWhenRelyingPartyForSigningWithoutCredentialsThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Saml2X509Credential.signing(null, null));\n\t}\n\n\t@Test\n\tpublic void factoryWhenRelyingPartyForSigningWithoutPrivateKeyThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Saml2X509Credential.signing(null, this.certificate));\n\t}\n\n\t@Test\n\tpublic void factoryWhenRelyingPartyForSigningWithoutCertificateThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Saml2X509Credential.signing(this.key, null));\n\t}\n\n\t@Test\n\tpublic void factoryWhenRelyingPartyForDecryptionWithoutCredentialsThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Saml2X509Credential.decryption(null, null));\n\t}\n\n\t@Test\n\tpublic void factoryWhenRelyingPartyForDecryptionWithoutPrivateKeyThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Saml2X509Credential.decryption(null, this.certificate));\n\t}\n\n\t@Test\n\tpublic void factoryWhenRelyingPartyForDecryptionWithoutCertificateThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Saml2X509Credential.decryption(this.key, null));\n\t}\n\n\t@Test\n\tpublic void factoryWhenAssertingPartyForVerificationWithoutCertificateThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Saml2X509Credential.verification(null));\n\t}\n\n\t@Test\n\tpublic void factoryWhenAssertingPartyForEncryptionWithoutCertificateThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> Saml2X509Credential.encryption(null));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/TestSaml2X509Credentials.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.core;\n\nimport java.io.ByteArrayInputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport org.springframework.security.converter.RsaKeyConverters;\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;\n\npublic final class TestSaml2X509Credentials {\n\n\tprivate TestSaml2X509Credentials() {\n\t}\n\n\tpublic static Saml2X509Credential assertingPartySigningCredential() {\n\t\treturn new Saml2X509Credential(idpPrivateKey(), idpCertificate(), Saml2X509CredentialType.SIGNING);\n\t}\n\n\tpublic static Saml2X509Credential assertingPartyEncryptingCredential() {\n\t\treturn new Saml2X509Credential(spCertificate(), Saml2X509CredentialType.ENCRYPTION);\n\t}\n\n\tpublic static Saml2X509Credential assertingPartyPrivateCredential() {\n\t\treturn new Saml2X509Credential(idpPrivateKey(), idpCertificate(), Saml2X509CredentialType.SIGNING,\n\t\t\t\tSaml2X509CredentialType.DECRYPTION);\n\t}\n\n\tpublic static Saml2X509Credential relyingPartyVerifyingCredential() {\n\t\treturn new Saml2X509Credential(idpCertificate(), Saml2X509CredentialType.VERIFICATION);\n\t}\n\n\tpublic static Saml2X509Credential relyingPartyEncryptingCredential() {\n\t\treturn new Saml2X509Credential(idpCertificate(), Saml2X509CredentialType.ENCRYPTION);\n\t}\n\n\tpublic static Saml2X509Credential relyingPartySigningCredential() {\n\t\treturn new Saml2X509Credential(spPrivateKey(), spCertificate(), Saml2X509CredentialType.SIGNING);\n\t}\n\n\tpublic static Saml2X509Credential relyingPartyDecryptingCredential() {\n\t\treturn new Saml2X509Credential(spPrivateKey(), spCertificate(), Saml2X509CredentialType.DECRYPTION);\n\t}\n\n\tpublic static Saml2X509Credential altPublicCredential() {\n\t\treturn new Saml2X509Credential(altCertificate(), Saml2X509CredentialType.VERIFICATION,\n\t\t\t\tSaml2X509CredentialType.ENCRYPTION);\n\t}\n\n\tpublic static Saml2X509Credential altPrivateCredential() {\n\t\treturn new Saml2X509Credential(altPrivateKey(), altCertificate(), Saml2X509CredentialType.SIGNING,\n\t\t\t\tSaml2X509CredentialType.DECRYPTION);\n\t}\n\n\tprivate static X509Certificate certificate(String cert) {\n\t\tByteArrayInputStream certBytes = new ByteArrayInputStream(cert.getBytes());\n\t\ttry {\n\t\t\treturn (X509Certificate) CertificateFactory.getInstance(\"X.509\").generateCertificate(certBytes);\n\t\t}\n\t\tcatch (CertificateException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\tprivate static PrivateKey privateKey(String key) {\n\t\treturn RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(key.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\tprivate static X509Certificate idpCertificate() {\n\t\treturn certificate(\n\t\t\t\t\"-----BEGIN CERTIFICATE-----\\n\" + \"MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\\n\"\n\t\t\t\t\t\t+ \"VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\\n\"\n\t\t\t\t\t\t+ \"VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\\n\"\n\t\t\t\t\t\t+ \"c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\\n\"\n\t\t\t\t\t\t+ \"aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\\n\"\n\t\t\t\t\t\t+ \"BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\\n\"\n\t\t\t\t\t\t+ \"BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\\n\"\n\t\t\t\t\t\t+ \"DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\\n\"\n\t\t\t\t\t\t+ \"QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\\n\"\n\t\t\t\t\t\t+ \"E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\\n\"\n\t\t\t\t\t\t+ \"2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\\n\"\n\t\t\t\t\t\t+ \"RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\\n\"\n\t\t\t\t\t\t+ \"nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\\n\"\n\t\t\t\t\t\t+ \"cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\\n\"\n\t\t\t\t\t\t+ \"iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\\n\"\n\t\t\t\t\t\t+ \"ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\\n\"\n\t\t\t\t\t\t+ \"AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\\n\"\n\t\t\t\t\t\t+ \"nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\\n\"\n\t\t\t\t\t\t+ \"ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\\n\"\n\t\t\t\t\t\t+ \"xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\\n\"\n\t\t\t\t\t\t+ \"V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\\n\"\n\t\t\t\t\t\t+ \"lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\\n\" + \"-----END CERTIFICATE-----\\n\");\n\t}\n\n\tprivate static PrivateKey idpPrivateKey() {\n\t\treturn privateKey(\n\t\t\t\t\"-----BEGIN PRIVATE KEY-----\\n\" + \"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4cn62E1xLqpN3\\n\"\n\t\t\t\t\t\t+ \"4PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZX\\n\"\n\t\t\t\t\t\t+ \"W+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHE\\n\"\n\t\t\t\t\t\t+ \"fDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7h\\n\"\n\t\t\t\t\t\t+ \"Z6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/T\\n\"\n\t\t\t\t\t\t+ \"Xy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7\\n\"\n\t\t\t\t\t\t+ \"I+J5lS8VAgMBAAECggEBAKyxBlIS7mcp3chvq0RF7B3PHFJMMzkwE+t3pLJcs4cZ\\n\"\n\t\t\t\t\t\t+ \"nezh/KbREfP70QjXzk/llnZCvxeIs5vRu24vbdBm79qLHqBuHp8XfHHtuo2AfoAQ\\n\"\n\t\t\t\t\t\t+ \"l4h047Xc/+TKMivnPQ0jX9qqndKDLqZDf5wnbslDmlskvF0a/MjsLU0TxtOfo+dB\\n\"\n\t\t\t\t\t\t+ \"t55FW11cGqxZwhS5Gnr+cbw3OkHz23b9gEOt9qfwPVepeysbmm9FjU+k4yVa7rAN\\n\"\n\t\t\t\t\t\t+ \"xcbzVb6Y7GCITe2tgvvEHmjB9BLmWrH3mZ3Af17YU/iN6TrpPd6Sj3QoS+2wGtAe\\n\"\n\t\t\t\t\t\t+ \"HbUs3CKJu7bIHcj4poal6Kh8519S+erJTtqQ8M0ZiEECgYEA43hLYAPaUueFkdfh\\n\"\n\t\t\t\t\t\t+ \"9K/7ClH6436CUH3VdizwUXi26fdhhV/I/ot6zLfU2mgEHU22LBECWQGtAFm8kv0P\\n\"\n\t\t\t\t\t\t+ \"zPn+qjaR3e62l5PIlSYbnkIidzoDZ2ztu4jF5LgStlTJQPteFEGgZVl5o9DaSZOq\\n\"\n\t\t\t\t\t\t+ \"Yd7G3XqXuQ1VGMW58G5FYJPtA1cCgYEAz5TPUtK+R2KXHMjUwlGY9AefQYRYmyX2\\n\"\n\t\t\t\t\t\t+ \"Tn/OFgKvY8lpAkMrhPKONq7SMYc8E9v9G7A0dIOXvW7QOYSapNhKU+np3lUafR5F\\n\"\n\t\t\t\t\t\t+ \"4ZN0bxZ9qjHbn3AMYeraKjeutHvlLtbHdIc1j3sxe/EzltRsYmiqLdEBW0p6hwWg\\n\"\n\t\t\t\t\t\t+ \"tyGhYWVyaXMCgYAfDOKtHpmEy5nOCLwNXKBWDk7DExfSyPqEgSnk1SeS1HP5ctPK\\n\"\n\t\t\t\t\t\t+ \"+1st6sIhdiVpopwFc+TwJWxqKdW18tlfT5jVv1E2DEnccw3kXilS9xAhWkfwrEvf\\n\"\n\t\t\t\t\t\t+ \"V5I74GydewFl32o+NZ8hdo9GL1I8zO1rIq/et8dSOWGuWf9BtKu/vTGTTQKBgFxU\\n\"\n\t\t\t\t\t\t+ \"VjsCnbvmsEwPUAL2hE/WrBFaKocnxXx5AFNt8lEyHtDwy4Sg1nygGcIJ4sD6koQk\\n\"\n\t\t\t\t\t\t+ \"RdClT3LkvR04TAiSY80bN/i6ZcPNGUwSaDGZEWAIOSWbkwZijZNFnSGOEgxZX/IG\\n\"\n\t\t\t\t\t\t+ \"yd39766vREEMTwEeiMNEOZQ/dmxkJm4OOVe25cLdAoGACOtPnq1Fxay80UYBf4rQ\\n\"\n\t\t\t\t\t\t+ \"+bJ9yX1ulB8WIree1hD7OHSB2lRHxrVYWrglrTvkh63Lgx+EcsTV788OsvAVfPPz\\n\"\n\t\t\t\t\t\t+ \"BZrn8SdDlQqalMxUBYEFwnsYD3cQ8yOUnijFVC4xNcdDv8OIqVgSk4KKxU5AshaA\\n\"\n\t\t\t\t\t\t+ \"xk6Mox+u8Cc2eAK12H13i+8=\\n\" + \"-----END PRIVATE KEY-----\\n\");\n\t}\n\n\tprivate static X509Certificate spCertificate() {\n\t\treturn certificate(\n\t\t\t\t\"-----BEGIN CERTIFICATE-----\\n\" + \"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\\n\"\n\t\t\t\t\t\t+ \"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\\n\"\n\t\t\t\t\t\t+ \"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\\n\"\n\t\t\t\t\t\t+ \"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\\n\"\n\t\t\t\t\t\t+ \"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\\n\"\n\t\t\t\t\t\t+ \"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\\n\"\n\t\t\t\t\t\t+ \"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\\n\"\n\t\t\t\t\t\t+ \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\\n\"\n\t\t\t\t\t\t+ \"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\\n\"\n\t\t\t\t\t\t+ \"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\\n\"\n\t\t\t\t\t\t+ \"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\\n\"\n\t\t\t\t\t\t+ \"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\\n\"\n\t\t\t\t\t\t+ \"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\\n\"\n\t\t\t\t\t\t+ \"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\\n\" + \"-----END CERTIFICATE-----\");\n\t}\n\n\tprivate static PrivateKey spPrivateKey() {\n\t\treturn privateKey(\n\t\t\t\t\"-----BEGIN PRIVATE KEY-----\\n\" + \"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\\n\"\n\t\t\t\t\t\t+ \"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\\n\"\n\t\t\t\t\t\t+ \"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\\n\"\n\t\t\t\t\t\t+ \"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\\n\"\n\t\t\t\t\t\t+ \"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\\n\"\n\t\t\t\t\t\t+ \"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\\n\"\n\t\t\t\t\t\t+ \"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\\n\"\n\t\t\t\t\t\t+ \"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\\n\"\n\t\t\t\t\t\t+ \"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\\n\"\n\t\t\t\t\t\t+ \"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\\n\"\n\t\t\t\t\t\t+ \"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\\n\"\n\t\t\t\t\t\t+ \"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\\n\"\n\t\t\t\t\t\t+ \"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\\n\" + \"INrtuLp4YHbgk1mi\\n\"\n\t\t\t\t\t\t+ \"-----END PRIVATE KEY-----\");\n\t}\n\n\tprivate static X509Certificate altCertificate() {\n\t\treturn certificate(\n\t\t\t\t\"-----BEGIN CERTIFICATE-----\\n\" + \"MIICkDCCAfkCFEstVfmWSFQp/j88GaMUwqVK72adMA0GCSqGSIb3DQEBCwUAMIGG\\n\"\n\t\t\t\t\t\t+ \"MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjESMBAGA1UEBwwJVmFu\\n\"\n\t\t\t\t\t\t+ \"Y291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FNTDEMMAoGA1UECwwD\\n\"\n\t\t\t\t\t\t+ \"YWx0MSEwHwYDVQQDDBhhbHQuc3ByaW5nLnNlY3VyaXR5LnNhbWwwHhcNMjIwMjEw\\n\"\n\t\t\t\t\t\t+ \"MTY1ODA4WhcNMzIwMjEwMTY1ODA4WjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgM\\n\"\n\t\t\t\t\t\t+ \"Cldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsGA1UECgwUU3ByaW5n\\n\"\n\t\t\t\t\t\t+ \"IFNlY3VyaXR5IFNBTUwxDDAKBgNVBAsMA2FsdDEhMB8GA1UEAwwYYWx0LnNwcmlu\\n\"\n\t\t\t\t\t\t+ \"Zy5zZWN1cml0eS5zYW1sMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9ZGWj\\n\"\n\t\t\t\t\t\t+ \"TPDsymQCJL044py4xLsBI/S9RvzNeR9oD/tHyoxCE+YZzjf0PyBtwqKzkKWqCPf4\\n\"\n\t\t\t\t\t\t+ \"XGUYHfEpkM5kJYwCW8TsOx5fnwLIQweiPqjYrBr/O0IjHMqYG9HlR/ros7iBt4ab\\n\"\n\t\t\t\t\t\t+ \"EGUu/B9yYg1YRYPxKQ6TNP3AD+9tBT8TsFFyjwIDAQABMA0GCSqGSIb3DQEBCwUA\\n\"\n\t\t\t\t\t\t+ \"A4GBAKJf2VHLjkCHRxlbWn63jGiquq3ENYgd1JS0DZ3ggFmuc6zQiqxzRGtArIDZ\\n\"\n\t\t\t\t\t\t+ \"0jH5nrG0jcvO0fqDqBQh0iT8thfUnkViAQvACZ9a+0x0NzUicJ+Ra51c8Z2enqbg\\n\"\n\t\t\t\t\t\t+ \"pXy+ga67HcAXrDekm1MCGCgiEb/Cgl41lsideqhC8Efl7PRN\\n\" + \"-----END CERTIFICATE-----\");\n\t}\n\n\tprivate static PrivateKey altPrivateKey() {\n\t\treturn privateKey(\n\t\t\t\t\"-----BEGIN PRIVATE KEY-----\\n\" + \"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAL1kZaNM8OzKZAIk\\n\"\n\t\t\t\t\t\t+ \"vTjinLjEuwEj9L1G/M15H2gP+0fKjEIT5hnON/Q/IG3CorOQpaoI9/hcZRgd8SmQ\\n\"\n\t\t\t\t\t\t+ \"zmQljAJbxOw7Hl+fAshDB6I+qNisGv87QiMcypgb0eVH+uizuIG3hpsQZS78H3Ji\\n\"\n\t\t\t\t\t\t+ \"DVhFg/EpDpM0/cAP720FPxOwUXKPAgMBAAECgYEApYKslAZ0cer5dSoYNzNLFOnQ\\n\"\n\t\t\t\t\t\t+ \"J1H92r/Dw+k6+h0lUvr+keyD5T9jhM76DxHOUDBzpmIKGoDcVDQugk2rILfzXsQA\\n\"\n\t\t\t\t\t\t+ \"JtwvDRJk32Z02Vt0jb7t/WUOOQhjKCjQuv9/tOx90GCl0VxYG69UOjaMRWrlg/i9\\n\"\n\t\t\t\t\t\t+ \"6/zcTRIahIn5XxF0psECQQD7ivJCpDbOLJGsc8gNJR4cvjZ1q0mHIOrbKqJC0y1n\\n\"\n\t\t\t\t\t\t+ \"5DrzGEflPeyCUwnOKNp9HJQP8gmZzXfj0JM9KsjpiUChAkEAwL+FmhDoTiqStIrH\\n\"\n\t\t\t\t\t\t+ \"h9Kdnsev//imMmRHxjwDhntYvqavUsISRmY3imd8inoYq5dzWQMzBtoTyMRmqeLT\\n\"\n\t\t\t\t\t\t+ \"DHV1LwJAW4xaV37Eo4z9B7Kr4Hzd1MA1ueW5QQDt+Q4vN/r7z4/1FHyFzh0Xcucd\\n\"\n\t\t\t\t\t\t+ \"7nZX7qj0CkmgzOVG+Rb0P5LOxJA7gQJBAK1KQ2qNct375qPM9bEGSVGchH6k5X7+\\n\"\n\t\t\t\t\t\t+ \"q4ztHdpFgTb/EzdbZiTG935GpjC1rwJuinTnrHOnkwv4j7iDRm24GF8CQQDqPvrQ\\n\"\n\t\t\t\t\t\t+ \"GcItR6UUy0q/B8UxLzlE6t+HiznfiJKfyGgCHU56Y4/ZhzSQz2MZHz9SK4DsUL9s\\n\" + \"bOYrWq8VY2fyjV1t\\n\"\n\t\t\t\t\t\t+ \"-----END PRIVATE KEY-----\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/credentials/Saml2X509CredentialTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.credentials;\n\nimport java.io.ByteArrayInputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.converter.RsaKeyConverters;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\npublic class Saml2X509CredentialTests {\n\n\tprivate Saml2X509Credential credential;\n\n\tprivate PrivateKey key;\n\n\tprivate X509Certificate certificate;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tString keyData = \"-----BEGIN PRIVATE KEY-----\\n\"\n\t\t\t\t+ \"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\\n\"\n\t\t\t\t+ \"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\\n\"\n\t\t\t\t+ \"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\\n\"\n\t\t\t\t+ \"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\\n\"\n\t\t\t\t+ \"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\\n\"\n\t\t\t\t+ \"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\\n\"\n\t\t\t\t+ \"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\\n\"\n\t\t\t\t+ \"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\\n\"\n\t\t\t\t+ \"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\\n\"\n\t\t\t\t+ \"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\\n\"\n\t\t\t\t+ \"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\\n\"\n\t\t\t\t+ \"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\\n\"\n\t\t\t\t+ \"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\\n\" + \"INrtuLp4YHbgk1mi\\n\"\n\t\t\t\t+ \"-----END PRIVATE KEY-----\";\n\t\tthis.key = RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(keyData.getBytes(StandardCharsets.UTF_8)));\n\t\tfinal CertificateFactory factory = CertificateFactory.getInstance(\"X.509\");\n\t\tString certificateData = \"-----BEGIN CERTIFICATE-----\\n\"\n\t\t\t\t+ \"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\\n\"\n\t\t\t\t+ \"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\\n\"\n\t\t\t\t+ \"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\\n\"\n\t\t\t\t+ \"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\\n\"\n\t\t\t\t+ \"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\\n\"\n\t\t\t\t+ \"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\\n\"\n\t\t\t\t+ \"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\\n\"\n\t\t\t\t+ \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\\n\"\n\t\t\t\t+ \"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\\n\"\n\t\t\t\t+ \"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\\n\"\n\t\t\t\t+ \"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\\n\"\n\t\t\t\t+ \"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\\n\"\n\t\t\t\t+ \"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\\n\"\n\t\t\t\t+ \"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\\n\" + \"-----END CERTIFICATE-----\";\n\t\tthis.certificate = (X509Certificate) factory\n\t\t\t.generateCertificate(new ByteArrayInputStream(certificateData.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithCredentialsThenItSucceeds() {\n\t\tnew Saml2X509Credential(this.key, this.certificate, Saml2X509Credential.Saml2X509CredentialType.SIGNING);\n\t\tnew Saml2X509Credential(this.key, this.certificate, Saml2X509Credential.Saml2X509CredentialType.SIGNING,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.DECRYPTION);\n\t\tnew Saml2X509Credential(this.key, this.certificate, Saml2X509Credential.Saml2X509CredentialType.DECRYPTION);\n\t}\n\n\t@Test\n\tpublic void constructorWhenAssertingPartyWithCredentialsThenItSucceeds() {\n\t\tnew Saml2X509Credential(this.certificate, Saml2X509Credential.Saml2X509CredentialType.VERIFICATION);\n\t\tnew Saml2X509Credential(this.certificate, Saml2X509Credential.Saml2X509CredentialType.VERIFICATION,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.ENCRYPTION);\n\t\tnew Saml2X509Credential(this.certificate, Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION);\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithoutCredentialsThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Saml2X509Credential(null, (X509Certificate) null,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithoutPrivateKeyThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Saml2X509Credential(null, this.certificate,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithoutCertificateThenItFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new Saml2X509Credential(this.key, null, Saml2X509Credential.Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAssertingPartyWithoutCertificateThenItFails() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new Saml2X509Credential(null, Saml2X509Credential.Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithEncryptionUsageThenItFails() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> new Saml2X509Credential(this.key, this.certificate,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.ENCRYPTION));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyWithVerificationUsageThenItFails() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> new Saml2X509Credential(this.key, this.certificate,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.VERIFICATION));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAssertingPartyWithSigningUsageThenItFails() {\n\t\tassertThatIllegalStateException().isThrownBy(\n\t\t\t\t() -> new Saml2X509Credential(this.certificate, Saml2X509Credential.Saml2X509CredentialType.SIGNING));\n\t}\n\n\t@Test\n\tpublic void constructorWhenAssertingPartyWithDecryptionUsageThenItFails() {\n\t\tassertThatIllegalStateException().isThrownBy(() -> new Saml2X509Credential(this.certificate,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.DECRYPTION));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/credentials/TestSaml2X509Credentials.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.credentials;\n\nimport java.io.ByteArrayInputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.PrivateKey;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\nimport org.springframework.security.converter.RsaKeyConverters;\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\n\npublic final class TestSaml2X509Credentials {\n\n\tprivate TestSaml2X509Credentials() {\n\t}\n\n\tpublic static Saml2X509Credential assertingPartySigningCredential() {\n\t\treturn new Saml2X509Credential(idpPrivateKey(), idpCertificate(),\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.SIGNING);\n\t}\n\n\tpublic static Saml2X509Credential assertingPartyEncryptingCredential() {\n\t\treturn new Saml2X509Credential(spCertificate(), Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION);\n\t}\n\n\tpublic static Saml2X509Credential assertingPartyPrivateCredential() {\n\t\treturn new Saml2X509Credential(idpPrivateKey(), idpCertificate(),\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.SIGNING,\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.DECRYPTION);\n\t}\n\n\tpublic static Saml2X509Credential relyingPartyVerifyingCredential() {\n\t\treturn new Saml2X509Credential(idpCertificate(), Saml2X509Credential.Saml2X509CredentialType.VERIFICATION);\n\t}\n\n\tpublic static Saml2X509Credential relyingPartySigningCredential() {\n\t\treturn new Saml2X509Credential(spPrivateKey(), spCertificate(),\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.SIGNING);\n\t}\n\n\tpublic static Saml2X509Credential relyingPartyDecryptingCredential() {\n\t\treturn new Saml2X509Credential(spPrivateKey(), spCertificate(),\n\t\t\t\tSaml2X509Credential.Saml2X509CredentialType.DECRYPTION);\n\t}\n\n\tprivate static X509Certificate certificate(String cert) {\n\t\tByteArrayInputStream certBytes = new ByteArrayInputStream(cert.getBytes());\n\t\ttry {\n\t\t\treturn (X509Certificate) CertificateFactory.getInstance(\"X.509\").generateCertificate(certBytes);\n\t\t}\n\t\tcatch (CertificateException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\tprivate static PrivateKey privateKey(String key) {\n\t\treturn RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(key.getBytes(StandardCharsets.UTF_8)));\n\t}\n\n\tprivate static X509Certificate idpCertificate() {\n\t\treturn certificate(\n\t\t\t\t\"-----BEGIN CERTIFICATE-----\\n\" + \"MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\\n\"\n\t\t\t\t\t\t+ \"VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\\n\"\n\t\t\t\t\t\t+ \"VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\\n\"\n\t\t\t\t\t\t+ \"c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\\n\"\n\t\t\t\t\t\t+ \"aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\\n\"\n\t\t\t\t\t\t+ \"BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\\n\"\n\t\t\t\t\t\t+ \"BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\\n\"\n\t\t\t\t\t\t+ \"DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\\n\"\n\t\t\t\t\t\t+ \"QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\\n\"\n\t\t\t\t\t\t+ \"E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\\n\"\n\t\t\t\t\t\t+ \"2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\\n\"\n\t\t\t\t\t\t+ \"RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\\n\"\n\t\t\t\t\t\t+ \"nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\\n\"\n\t\t\t\t\t\t+ \"cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\\n\"\n\t\t\t\t\t\t+ \"iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\\n\"\n\t\t\t\t\t\t+ \"ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\\n\"\n\t\t\t\t\t\t+ \"AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\\n\"\n\t\t\t\t\t\t+ \"nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\\n\"\n\t\t\t\t\t\t+ \"ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\\n\"\n\t\t\t\t\t\t+ \"xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\\n\"\n\t\t\t\t\t\t+ \"V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\\n\"\n\t\t\t\t\t\t+ \"lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\\n\" + \"-----END CERTIFICATE-----\\n\");\n\t}\n\n\tprivate static PrivateKey idpPrivateKey() {\n\t\treturn privateKey(\n\t\t\t\t\"-----BEGIN PRIVATE KEY-----\\n\" + \"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4cn62E1xLqpN3\\n\"\n\t\t\t\t\t\t+ \"4PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZX\\n\"\n\t\t\t\t\t\t+ \"W+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHE\\n\"\n\t\t\t\t\t\t+ \"fDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7h\\n\"\n\t\t\t\t\t\t+ \"Z6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/T\\n\"\n\t\t\t\t\t\t+ \"Xy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7\\n\"\n\t\t\t\t\t\t+ \"I+J5lS8VAgMBAAECggEBAKyxBlIS7mcp3chvq0RF7B3PHFJMMzkwE+t3pLJcs4cZ\\n\"\n\t\t\t\t\t\t+ \"nezh/KbREfP70QjXzk/llnZCvxeIs5vRu24vbdBm79qLHqBuHp8XfHHtuo2AfoAQ\\n\"\n\t\t\t\t\t\t+ \"l4h047Xc/+TKMivnPQ0jX9qqndKDLqZDf5wnbslDmlskvF0a/MjsLU0TxtOfo+dB\\n\"\n\t\t\t\t\t\t+ \"t55FW11cGqxZwhS5Gnr+cbw3OkHz23b9gEOt9qfwPVepeysbmm9FjU+k4yVa7rAN\\n\"\n\t\t\t\t\t\t+ \"xcbzVb6Y7GCITe2tgvvEHmjB9BLmWrH3mZ3Af17YU/iN6TrpPd6Sj3QoS+2wGtAe\\n\"\n\t\t\t\t\t\t+ \"HbUs3CKJu7bIHcj4poal6Kh8519S+erJTtqQ8M0ZiEECgYEA43hLYAPaUueFkdfh\\n\"\n\t\t\t\t\t\t+ \"9K/7ClH6436CUH3VdizwUXi26fdhhV/I/ot6zLfU2mgEHU22LBECWQGtAFm8kv0P\\n\"\n\t\t\t\t\t\t+ \"zPn+qjaR3e62l5PIlSYbnkIidzoDZ2ztu4jF5LgStlTJQPteFEGgZVl5o9DaSZOq\\n\"\n\t\t\t\t\t\t+ \"Yd7G3XqXuQ1VGMW58G5FYJPtA1cCgYEAz5TPUtK+R2KXHMjUwlGY9AefQYRYmyX2\\n\"\n\t\t\t\t\t\t+ \"Tn/OFgKvY8lpAkMrhPKONq7SMYc8E9v9G7A0dIOXvW7QOYSapNhKU+np3lUafR5F\\n\"\n\t\t\t\t\t\t+ \"4ZN0bxZ9qjHbn3AMYeraKjeutHvlLtbHdIc1j3sxe/EzltRsYmiqLdEBW0p6hwWg\\n\"\n\t\t\t\t\t\t+ \"tyGhYWVyaXMCgYAfDOKtHpmEy5nOCLwNXKBWDk7DExfSyPqEgSnk1SeS1HP5ctPK\\n\"\n\t\t\t\t\t\t+ \"+1st6sIhdiVpopwFc+TwJWxqKdW18tlfT5jVv1E2DEnccw3kXilS9xAhWkfwrEvf\\n\"\n\t\t\t\t\t\t+ \"V5I74GydewFl32o+NZ8hdo9GL1I8zO1rIq/et8dSOWGuWf9BtKu/vTGTTQKBgFxU\\n\"\n\t\t\t\t\t\t+ \"VjsCnbvmsEwPUAL2hE/WrBFaKocnxXx5AFNt8lEyHtDwy4Sg1nygGcIJ4sD6koQk\\n\"\n\t\t\t\t\t\t+ \"RdClT3LkvR04TAiSY80bN/i6ZcPNGUwSaDGZEWAIOSWbkwZijZNFnSGOEgxZX/IG\\n\"\n\t\t\t\t\t\t+ \"yd39766vREEMTwEeiMNEOZQ/dmxkJm4OOVe25cLdAoGACOtPnq1Fxay80UYBf4rQ\\n\"\n\t\t\t\t\t\t+ \"+bJ9yX1ulB8WIree1hD7OHSB2lRHxrVYWrglrTvkh63Lgx+EcsTV788OsvAVfPPz\\n\"\n\t\t\t\t\t\t+ \"BZrn8SdDlQqalMxUBYEFwnsYD3cQ8yOUnijFVC4xNcdDv8OIqVgSk4KKxU5AshaA\\n\"\n\t\t\t\t\t\t+ \"xk6Mox+u8Cc2eAK12H13i+8=\\n\" + \"-----END PRIVATE KEY-----\\n\");\n\t}\n\n\tprivate static X509Certificate spCertificate() {\n\t\treturn certificate(\n\t\t\t\t\"-----BEGIN CERTIFICATE-----\\n\" + \"MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC\\n\"\n\t\t\t\t\t\t+ \"VVMxEzARBgNVBAgMCldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsG\\n\"\n\t\t\t\t\t\t+ \"A1UECgwUU3ByaW5nIFNlY3VyaXR5IFNBTUwxCzAJBgNVBAsMAnNwMSAwHgYDVQQD\\n\"\n\t\t\t\t\t\t+ \"DBdzcC5zcHJpbmcuc2VjdXJpdHkuc2FtbDAeFw0xODA1MTQxNDMwNDRaFw0yODA1\\n\"\n\t\t\t\t\t\t+ \"MTExNDMwNDRaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjES\\n\"\n\t\t\t\t\t\t+ \"MBAGA1UEBwwJVmFuY291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FN\\n\"\n\t\t\t\t\t\t+ \"TDELMAkGA1UECwwCc3AxIDAeBgNVBAMMF3NwLnNwcmluZy5zZWN1cml0eS5zYW1s\\n\"\n\t\t\t\t\t\t+ \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRu7/EI0BlNzMEBFVAcbx+lLos\\n\"\n\t\t\t\t\t\t+ \"vzIWU+01dGTY8gBdhMQNYKZ92lMceo2CuVJ66cUURPym3i7nGGzoSnAxAre+0YIM\\n\"\n\t\t\t\t\t\t+ \"+U0razrWtAUE735bkcqELZkOTZLelaoOztmWqRbe5OuEmpewH7cx+kNgcVjdctOG\\n\"\n\t\t\t\t\t\t+ \"y3Q6x+I4qakY/9qhBQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAAeViTvHOyQopWEi\\n\"\n\t\t\t\t\t\t+ \"XOfI2Z9eukwrSknDwq/zscR0YxwwqDBMt/QdAODfSwAfnciiYLkmEjlozWRtOeN+\\n\"\n\t\t\t\t\t\t+ \"qK7UFgP1bRl5qksrYX5S0z2iGJh0GvonLUt3e20Ssfl5tTEDDnAEUMLfBkyaxEHD\\n\"\n\t\t\t\t\t\t+ \"RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B\\n\" + \"-----END CERTIFICATE-----\");\n\t}\n\n\tprivate static PrivateKey spPrivateKey() {\n\t\treturn privateKey(\n\t\t\t\t\"-----BEGIN PRIVATE KEY-----\\n\" + \"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE\\n\"\n\t\t\t\t\t\t+ \"VUBxvH6Uuiy/MhZT7TV0ZNjyAF2ExA1gpn3aUxx6jYK5UnrpxRRE/KbeLucYbOhK\\n\"\n\t\t\t\t\t\t+ \"cDECt77Rggz5TStrOta0BQTvfluRyoQtmQ5Nkt6Vqg7O2ZapFt7k64Sal7AftzH6\\n\"\n\t\t\t\t\t\t+ \"Q2BxWN1y04bLdDrH4jipqRj/2qEFAgMBAAECgYEAj4ExY1jjdN3iEDuOwXuRB+Nn\\n\"\n\t\t\t\t\t\t+ \"x7pC4TgntE2huzdKvLJdGvIouTArce8A6JM5NlTBvm69mMepvAHgcsiMH1zGr5J5\\n\"\n\t\t\t\t\t\t+ \"wJz23mGOyhM1veON41/DJTVG+cxq4soUZhdYy3bpOuXGMAaJ8QLMbQQoivllNihd\\n\"\n\t\t\t\t\t\t+ \"vwH0rNSK8LTYWWPZYIECQQDxct+TFX1VsQ1eo41K0T4fu2rWUaxlvjUGhK6HxTmY\\n\"\n\t\t\t\t\t\t+ \"8OMJptunGRJL1CUjIb45Uz7SP8TPz5FwhXWsLfS182kRAkEA3l+Qd9C9gdpUh1uX\\n\"\n\t\t\t\t\t\t+ \"oPSNIxn5hFUrSTW1EwP9QH9vhwb5Vr8Jrd5ei678WYDLjUcx648RjkjhU9jSMzIx\\n\"\n\t\t\t\t\t\t+ \"EGvYtQJBAMm/i9NR7IVyyNIgZUpz5q4LI21rl1r4gUQuD8vA36zM81i4ROeuCly0\\n\"\n\t\t\t\t\t\t+ \"KkfdxR4PUfnKcQCX11YnHjk9uTFj75ECQEFY/gBnxDjzqyF35hAzrYIiMPQVfznt\\n\"\n\t\t\t\t\t\t+ \"YX/sDTE2AdVBVGaMj1Cb51bPHnNC6Q5kXKQnj/YrLqRQND09Q7ParX0CQQC5NxZr\\n\"\n\t\t\t\t\t\t+ \"9jKqhHj8yQD6PlXTsY4Occ7DH6/IoDenfdEVD5qlet0zmd50HatN2Jiqm5ubN7CM\\n\" + \"INrtuLp4YHbgk1mi\\n\"\n\t\t\t\t\t\t+ \"-----END PRIVATE KEY-----\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson/DefaultSaml2AuthenticatedPrincipalMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass DefaultSaml2AuthenticatedPrincipalMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = TestSaml2JsonPayloads.createDefaultPrincipal();\n\n\t\tString principalJson = this.mapper.writeValueAsString(principal);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON, principalJson, true);\n\t}\n\n\t@Test\n\tvoid shouldSerializeWithoutRegistrationId() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\n\t\t\t\tTestSaml2JsonPayloads.PRINCIPAL_NAME, TestSaml2JsonPayloads.ATTRIBUTES,\n\t\t\t\tTestSaml2JsonPayloads.SESSION_INDEXES);\n\n\t\tString principalJson = this.mapper.writeValueAsString(principal);\n\n\t\tJSONAssert.assertEquals(principalWithoutRegId(), principalJson, true);\n\t}\n\n\t@Test\n\tvoid shouldSerializeWithoutIndices() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\n\t\t\t\tTestSaml2JsonPayloads.PRINCIPAL_NAME, TestSaml2JsonPayloads.ATTRIBUTES);\n\t\tprincipal.setRelyingPartyRegistrationId(TestSaml2JsonPayloads.REG_ID);\n\n\t\tString principalJson = this.mapper.writeValueAsString(principal);\n\n\t\tJSONAssert.assertEquals(principalWithoutIndices(), principalJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = this.mapper.readValue(\n\t\t\t\tTestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON, DefaultSaml2AuthenticatedPrincipal.class);\n\n\t\tassertThat(principal).isNotNull();\n\t\tassertThat(principal.getName()).isEqualTo(TestSaml2JsonPayloads.PRINCIPAL_NAME);\n\t\tassertThat(principal.getRelyingPartyRegistrationId()).isEqualTo(TestSaml2JsonPayloads.REG_ID);\n\t\tassertThat(principal.getAttributes()).isEqualTo(TestSaml2JsonPayloads.ATTRIBUTES);\n\t\tassertThat(principal.getSessionIndexes()).isEqualTo(TestSaml2JsonPayloads.SESSION_INDEXES);\n\t}\n\n\t@Test\n\tvoid shouldDeserializeWithoutRegistrationId() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = this.mapper.readValue(principalWithoutRegId(),\n\t\t\t\tDefaultSaml2AuthenticatedPrincipal.class);\n\n\t\tassertThat(principal).isNotNull();\n\t\tassertThat(principal.getName()).isEqualTo(TestSaml2JsonPayloads.PRINCIPAL_NAME);\n\t\tassertThat(principal.getRelyingPartyRegistrationId()).isNull();\n\t\tassertThat(principal.getAttributes()).isEqualTo(TestSaml2JsonPayloads.ATTRIBUTES);\n\t\tassertThat(principal.getSessionIndexes()).isEqualTo(TestSaml2JsonPayloads.SESSION_INDEXES);\n\t}\n\n\tprivate static String principalWithoutRegId() {\n\t\treturn TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON.replace(TestSaml2JsonPayloads.REG_ID_JSON,\n\t\t\t\t\"null\");\n\t}\n\n\tprivate static String principalWithoutIndices() {\n\t\treturn TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON\n\t\t\t.replace(TestSaml2JsonPayloads.SESSION_INDEXES_JSON, \"[\\\"java.util.Collections$EmptyList\\\", []]\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson/Saml2AuthenticationExceptionMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2AuthenticationExceptionMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2AuthenticationException exception = TestSaml2JsonPayloads.createDefaultSaml2AuthenticationException();\n\n\t\tString exceptionJson = this.mapper.writeValueAsString(exception);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_SAML_AUTH_EXCEPTION_JSON, exceptionJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tSaml2AuthenticationException exception = this.mapper\n\t\t\t.readValue(TestSaml2JsonPayloads.DEFAULT_SAML_AUTH_EXCEPTION_JSON, Saml2AuthenticationException.class);\n\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getMessage()).isEqualTo(\"exceptionMessage\");\n\t\tassertThat(exception.getSaml2Error()).extracting(Saml2Error::getErrorCode, Saml2Error::getDescription)\n\t\t\t.contains(\"errorCode\", \"errorDescription\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson/Saml2AuthenticationMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2AuthenticationMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2Authentication authentication = TestSaml2JsonPayloads.createDefaultAuthentication();\n\n\t\tString authenticationJson = this.mapper.writeValueAsString(authentication);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_SAML2AUTHENTICATION_JSON, authenticationJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tSaml2Authentication authentication = this.mapper\n\t\t\t.readValue(TestSaml2JsonPayloads.DEFAULT_SAML2AUTHENTICATION_JSON, Saml2Authentication.class);\n\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getDetails()).isEqualTo(TestSaml2JsonPayloads.DETAILS);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(TestSaml2JsonPayloads.SAML_RESPONSE);\n\t\tassertThat(authentication.getSaml2Response()).isEqualTo(TestSaml2JsonPayloads.SAML_RESPONSE);\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(TestSaml2JsonPayloads.AUTHORITIES);\n\t\tassertThat(authentication.getPrincipal()).usingRecursiveComparison()\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.createDefaultPrincipal());\n\t\tassertThat(authentication.getDetails()).usingRecursiveComparison().isEqualTo(TestSaml2JsonPayloads.DETAILS);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson/Saml2LogoutRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.databind.DeserializationFeature;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2LogoutRequestMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2LogoutRequest request = TestSaml2JsonPayloads.createDefaultSaml2LogoutRequest();\n\n\t\tString requestJson = this.mapper.writeValueAsString(request);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_LOGOUT_REQUEST_JSON, requestJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() {\n\t\tdeserializeAndAssertRequest();\n\t}\n\n\t// gh-12539\n\t@Test\n\tvoid shouldDeserializeWhenFailOnMissingCreatorPropertiesEnabled() {\n\t\t// Jackson will use reflection to initialize the binding property if this is not\n\t\t// enabled\n\t\tJsonMapper customizedMapper = this.mapper.rebuild()\n\t\t\t.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)\n\t\t\t.build();\n\t\tdeserializeAndAssertRequest();\n\t}\n\n\tprivate void deserializeAndAssertRequest() throws JacksonException {\n\t\tSaml2LogoutRequest logoutRequest = this.mapper.readValue(TestSaml2JsonPayloads.DEFAULT_LOGOUT_REQUEST_JSON,\n\t\t\t\tSaml2LogoutRequest.class);\n\n\t\tassertThat(logoutRequest).isNotNull();\n\t\tassertThat(logoutRequest.getId()).isEqualTo(TestSaml2JsonPayloads.ID);\n\t\tassertThat(logoutRequest.getRelyingPartyRegistrationId())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID);\n\t\tassertThat(logoutRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(logoutRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(logoutRequest.getLocation()).isEqualTo(TestSaml2JsonPayloads.LOCATION);\n\t\tassertThat(logoutRequest.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tMap<String, String> expectedParams = new HashMap<>();\n\t\texpectedParams.put(\"SAMLRequest\", TestSaml2JsonPayloads.SAML_REQUEST);\n\t\texpectedParams.put(\"RelayState\", TestSaml2JsonPayloads.RELAY_STATE);\n\t\texpectedParams.put(\"AdditionalParam\", TestSaml2JsonPayloads.ADDITIONAL_PARAM);\n\t\tassertThat(logoutRequest.getParameters()).containsAllEntriesOf(expectedParams);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson/Saml2PostAuthenticationRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2PostAuthenticationRequestMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2PostAuthenticationRequest request = TestSaml2JsonPayloads.createDefaultSaml2PostAuthenticationRequest();\n\n\t\tString requestJson = this.mapper.writeValueAsString(request);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON, requestJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() {\n\t\tSaml2PostAuthenticationRequest authRequest = this.mapper\n\t\t\t.readValue(TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON, Saml2PostAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID);\n\t\tassertThat(authRequest.getId()).isEqualTo(TestSaml2JsonPayloads.ID);\n\t}\n\n\t@Test\n\tvoid shouldDeserializeWithNoRegistrationId() {\n\t\tString json = TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON.replace(\n\t\t\t\t\"\\\"relyingPartyRegistrationId\\\": \\\"\" + TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID + \"\\\",\", \"\");\n\n\t\tSaml2PostAuthenticationRequest authRequest = this.mapper.readValue(json, Saml2PostAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId()).isNull();\n\t\tassertThat(authRequest.getId()).isEqualTo(TestSaml2JsonPayloads.ID);\n\t}\n\n\t@Test\n\tvoid shouldDeserializeWithNoId() {\n\t\tString json = TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON\n\t\t\t.replace(\", \\\"id\\\": \\\"\" + TestSaml2JsonPayloads.ID + \"\\\"\", \"\");\n\n\t\tSaml2PostAuthenticationRequest authRequest = this.mapper.readValue(json, Saml2PostAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID);\n\t\tassertThat(authRequest.getId()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson/Saml2RedirectAuthenticationRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2RedirectAuthenticationRequestMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2RedirectAuthenticationRequest request = TestSaml2JsonPayloads\n\t\t\t.createDefaultSaml2RedirectAuthenticationRequest();\n\n\t\tString requestJson = this.mapper.writeValueAsString(request);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_REDIRECT_AUTH_REQUEST_JSON, requestJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tSaml2RedirectAuthenticationRequest authRequest = this.mapper.readValue(\n\t\t\t\tTestSaml2JsonPayloads.DEFAULT_REDIRECT_AUTH_REQUEST_JSON, Saml2RedirectAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getSigAlg()).isEqualTo(TestSaml2JsonPayloads.SIG_ALG);\n\t\tassertThat(authRequest.getSignature()).isEqualTo(TestSaml2JsonPayloads.SIGNATURE);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID);\n\t}\n\n\t@Test\n\tvoid shouldDeserializeWithNoRegistrationId() throws Exception {\n\t\tString json = TestSaml2JsonPayloads.DEFAULT_REDIRECT_AUTH_REQUEST_JSON.replace(\n\t\t\t\t\"\\\"relyingPartyRegistrationId\\\": \\\"\" + TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID + \"\\\",\", \"\");\n\n\t\tSaml2RedirectAuthenticationRequest authRequest = this.mapper.readValue(json,\n\t\t\t\tSaml2RedirectAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getSigAlg()).isEqualTo(TestSaml2JsonPayloads.SIG_ALG);\n\t\tassertThat(authRequest.getSignature()).isEqualTo(TestSaml2JsonPayloads.SIGNATURE);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson/TestSaml2JsonPayloads.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\n@SuppressWarnings(\"removal\")\nfinal class TestSaml2JsonPayloads {\n\n\tprivate TestSaml2JsonPayloads() {\n\t}\n\n\tstatic final Map<String, List<Object>> ATTRIBUTES;\n\n\tstatic {\n\t\tMap<String, List<Object>> tmpAttributes = new HashMap<>();\n\t\ttmpAttributes.put(\"name\", Collections.singletonList(\"attr_name\"));\n\t\ttmpAttributes.put(\"email\", Collections.singletonList(\"attr_email\"));\n\t\ttmpAttributes.put(\"listOf\", Collections.unmodifiableList(Arrays.asList(\"Element1\", \"Element2\", 4, true)));\n\t\tATTRIBUTES = Collections.unmodifiableMap(tmpAttributes);\n\t}\n\n\tstatic final String REG_ID = \"REG_ID_TEST\";\n\tstatic final String REG_ID_JSON = \"\\\"\" + REG_ID + \"\\\"\";\n\n\tstatic final String SESSION_INDEXES_JSON = \"[\" + \"  \\\"java.util.Collections$UnmodifiableRandomAccessList\\\",\"\n\t\t\t+ \"  [ \\\"Index 1\\\", \\\"Index 2\\\" ]\" + \"]\";\n\tstatic final List<String> SESSION_INDEXES = Collections.unmodifiableList(Arrays.asList(\"Index 1\", \"Index 2\"));\n\n\tstatic final String PRINCIPAL_NAME = \"principalName\";\n\n\t// @formatter:off\n\tstatic final String DEFAULT_AUTHENTICATED_PRINCIPAL_JSON = \"{\"\n\t\t\t+ \"  \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal\\\",\"\n\t\t\t+ \"  \\\"name\\\": \\\"\" + PRINCIPAL_NAME + \"\\\",\"\n\t\t\t+ \"  \\\"attributes\\\": {\"\n\t\t\t+ \"    \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\"\n\t\t\t+ \"    \\\"listOf\\\": [\"\n\t\t\t+ \"      \\\"java.util.Collections$UnmodifiableRandomAccessList\\\",\"\n\t\t\t+ \"      [ \\\"Element1\\\", \\\"Element2\\\", 4, true ]\"\n\t\t\t+ \"    ],\"\n\t\t\t+ \"    \\\"email\\\": [\"\n\t\t\t+ \"      \\\"java.util.Collections$SingletonList\\\",\"\n\t\t\t+ \"      [ \\\"attr_email\\\" ]\"\n\t\t\t+ \"    ],\"\n\t\t\t+ \"    \\\"name\\\": [\"\n\t\t\t+ \"      \\\"java.util.Collections$SingletonList\\\",\"\n\t\t\t+ \"      [ \\\"attr_name\\\" ]\"\n\t\t\t+ \"    ]\"\n\t\t\t+ \"  },\"\n\t\t\t+ \"  \\\"sessionIndexes\\\": \" + SESSION_INDEXES_JSON + \",\"\n\t\t\t+ \"  \\\"registrationId\\\": \" + REG_ID_JSON + \"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic DefaultSaml2AuthenticatedPrincipal createDefaultPrincipal() {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(PRINCIPAL_NAME,\n\t\t\t\tATTRIBUTES, SESSION_INDEXES);\n\t\tprincipal.setRelyingPartyRegistrationId(REG_ID);\n\t\treturn principal;\n\t}\n\n\tstatic final String SAML_REQUEST = \"samlRequestValue\";\n\tstatic final String RELAY_STATE = \"relayStateValue\";\n\tstatic final String AUTHENTICATION_REQUEST_URI = \"authenticationRequestUriValue\";\n\tstatic final String RELYINGPARTY_REGISTRATION_ID = \"registrationIdValue\";\n\tstatic final String SIG_ALG = \"sigAlgValue\";\n\tstatic final String SIGNATURE = \"signatureValue\";\n\tstatic final String ID = \"idValue\";\n\n\t// @formatter:off\n\tstatic final String DEFAULT_REDIRECT_AUTH_REQUEST_JSON = \"{\"\n\t\t\t+ \" \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest\\\",\"\n\t\t\t+ \" \\\"samlRequest\\\": \\\"\" + SAML_REQUEST + \"\\\",\"\n\t\t\t+ \" \\\"relayState\\\": \\\"\" + RELAY_STATE + \"\\\",\"\n\t\t\t+ \" \\\"authenticationRequestUri\\\": \\\"\" + AUTHENTICATION_REQUEST_URI + \"\\\",\"\n\t\t\t+ \" \\\"relyingPartyRegistrationId\\\": \\\"\" + RELYINGPARTY_REGISTRATION_ID + \"\\\",\"\n\t\t\t+ \" \\\"sigAlg\\\": \\\"\" + SIG_ALG + \"\\\",\"\n\t\t\t+ \" \\\"signature\\\": \\\"\" + SIGNATURE + \"\\\",\"\n\t\t\t+ \" \\\"id\\\": \\\"\" + ID + \"\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tstatic final String DEFAULT_POST_AUTH_REQUEST_JSON = \"{\"\n\t\t\t+ \" \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest\\\",\"\n\t\t\t+ \" \\\"samlRequest\\\": \\\"\" + SAML_REQUEST + \"\\\",\"\n\t\t\t+ \" \\\"relayState\\\": \\\"\" + RELAY_STATE + \"\\\",\"\n\t\t\t+ \" \\\"relyingPartyRegistrationId\\\": \\\"\" + RELYINGPARTY_REGISTRATION_ID + \"\\\",\"\n\t\t\t+ \" \\\"authenticationRequestUri\\\": \\\"\" + AUTHENTICATION_REQUEST_URI + \"\\\",\"\n\t\t\t+ \" \\\"id\\\": \\\"\" + ID + \"\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic final String LOCATION = \"locationValue\";\n\tstatic final String BINDNG = \"REDIRECT\";\n\tstatic final String ADDITIONAL_PARAM = \"additionalParamValue\";\n\n\t// @formatter:off\n\tstatic final String DEFAULT_LOGOUT_REQUEST_JSON = \"{\"\n\t\t\t+ \"  \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest\\\",\"\n\t\t\t+ \"  \\\"id\\\": \\\"\" + ID + \"\\\",\"\n\t\t\t+ \"  \\\"location\\\": \\\"\" + LOCATION + \"\\\",\"\n\t\t\t+ \"  \\\"binding\\\": \\\"\" + BINDNG + \"\\\",\"\n\t\t\t+ \"  \\\"relyingPartyRegistrationId\\\": \\\"\" + RELYINGPARTY_REGISTRATION_ID + \"\\\",\"\n\t\t\t+ \"  \\\"parameters\\\": { \"\n\t\t\t+ \"     \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\"\n\t\t\t+ \"     \\\"SAMLRequest\\\": \\\"\" + SAML_REQUEST + \"\\\",\"\n\t\t\t+ \"     \\\"RelayState\\\": \\\"\" + RELAY_STATE + \"\\\",\"\n\t\t\t+ \"     \\\"AdditionalParam\\\": \\\"\" + ADDITIONAL_PARAM + \"\\\"\"\n\t\t\t+ \"  }\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic Saml2PostAuthenticationRequest createDefaultSaml2PostAuthenticationRequest() {\n\t\treturn Saml2PostAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.full()\n\t\t\t\t.registrationId(RELYINGPARTY_REGISTRATION_ID)\n\t\t\t\t.assertingPartyMetadata((party) -> party.singleSignOnServiceLocation(AUTHENTICATION_REQUEST_URI))\n\t\t\t\t.build())\n\t\t\t.samlRequest(SAML_REQUEST)\n\t\t\t.relayState(RELAY_STATE)\n\t\t\t.id(ID)\n\t\t\t.build();\n\t}\n\n\tstatic Saml2RedirectAuthenticationRequest createDefaultSaml2RedirectAuthenticationRequest() {\n\t\treturn Saml2RedirectAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.full()\n\t\t\t\t.registrationId(RELYINGPARTY_REGISTRATION_ID)\n\t\t\t\t.assertingPartyMetadata((party) -> party.singleSignOnServiceLocation(AUTHENTICATION_REQUEST_URI))\n\t\t\t\t.build())\n\t\t\t.samlRequest(SAML_REQUEST)\n\t\t\t.relayState(RELAY_STATE)\n\t\t\t.sigAlg(SIG_ALG)\n\t\t\t.signature(SIGNATURE)\n\t\t\t.id(ID)\n\t\t\t.build();\n\t}\n\n\tstatic Saml2LogoutRequest createDefaultSaml2LogoutRequest() {\n\t\treturn Saml2LogoutRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.full()\n\t\t\t\t.registrationId(RELYINGPARTY_REGISTRATION_ID)\n\t\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceLocation(LOCATION)\n\t\t\t\t\t.singleLogoutServiceBinding(Saml2MessageBinding.REDIRECT))\n\t\t\t\t.build())\n\t\t\t.id(ID)\n\t\t\t.samlRequest(SAML_REQUEST)\n\t\t\t.relayState(RELAY_STATE)\n\t\t\t.parameters((params) -> params.put(\"AdditionalParam\", ADDITIONAL_PARAM))\n\t\t\t.build();\n\t}\n\n\tstatic final Collection<GrantedAuthority> AUTHORITIES = Collections\n\t\t.unmodifiableList(Arrays.asList(new SimpleGrantedAuthority(\"Role1\"), new SimpleGrantedAuthority(\"Role2\")));\n\n\tstatic final Object DETAILS = User.withUsername(\"username\").password(\"empty\").authorities(\"A\", \"B\").build();\n\tstatic final String SAML_RESPONSE = \"samlResponseValue\";\n\n\t// @formatter:off\n\tstatic final String DEFAULT_SAML2AUTHENTICATION_JSON = \"{\"\n\t\t\t+ \"\t\\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.Saml2Authentication\\\",\"\n\t\t\t+ \"\t\\\"authorities\\\": [\"\n\t\t\t+ \"\t\t\\\"java.util.Collections$UnmodifiableRandomAccessList\\\",\"\n\t\t\t+ \"\t\t[\"\n\t\t\t+ \"\t\t\t{\"\n\t\t\t+ \"\t\t\t\t\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\"\n\t\t\t+ \"\t\t\t\t\\\"authority\\\": \\\"Role1\\\"\"\n\t\t\t+ \"\t\t\t},\"\n\t\t\t+ \"\t\t\t{\"\n\t\t\t+ \"\t\t\t\t\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\"\n\t\t\t+ \"\t\t\t\t\\\"authority\\\": \\\"Role2\\\"\"\n\t\t\t+ \"\t\t\t}\"\n\t\t\t+ \"\t\t]\"\n\t\t\t+ \" ],\"\n\t\t\t+ \"\t\\\"details\\\": {\"\n\t\t\t+ \"\t\t\\\"@class\\\": \\\"org.springframework.security.core.userdetails.User\\\",\"\n\t\t\t+ \"\t\t\\\"password\\\": \\\"empty\\\",\"\n\t\t\t+ \"\t\t\\\"username\\\": \\\"username\\\",\"\n\t\t\t+ \"\t\t\\\"authorities\\\": [\"\n\t\t\t+ \"\t\t\t\\\"java.util.Collections$UnmodifiableSet\\\", [\"\n\t\t\t+ \"\t\t\t\t{\"\n\t\t\t+ \"\t\t\t\t\t\\\"@class\\\":\\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\"\n\t\t\t+ \"\t\t\t\t\t\\\"authority\\\":\\\"A\\\"\"\n\t\t\t+ \"\t\t\t\t},\"\n\t\t\t+ \"\t\t\t\t{\"\n\t\t\t+ \"\t\t\t\t\t\\\"@class\\\":\\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\"\n\t\t\t+ \"\t\t\t\t\t\\\"authority\\\":\\\"B\\\"\"\n\t\t\t+ \"\t\t\t\t}\"\n\t\t\t+ \"\t\t]],\"\n\t\t\t+ \"\t\t\\\"accountNonExpired\\\": true,\"\n\t\t\t+ \"\t\t\\\"accountNonLocked\\\": true,\"\n\t\t\t+ \"\t\t\\\"credentialsNonExpired\\\": true,\"\n\t\t\t+ \"\t\t\\\"enabled\\\": true\"\n\t\t\t+ \"\t},\"\n\t\t\t+ \"\t\\\"principal\\\": \" + DEFAULT_AUTHENTICATED_PRINCIPAL_JSON + \",\"\n\t\t\t+ \"\t\\\"saml2Response\\\": \\\"\" + SAML_RESPONSE + \"\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic Saml2Authentication createDefaultAuthentication() {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = createDefaultPrincipal();\n\t\tSaml2Authentication authentication = new Saml2Authentication(principal, SAML_RESPONSE, AUTHORITIES);\n\t\tauthentication.setDetails(DETAILS);\n\t\treturn authentication;\n\t}\n\n\t// @formatter:off\n\tstatic final String DEFAULT_SAML_AUTH_EXCEPTION_JSON = \"{\"\n\t\t\t+ \"  \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException\\\",\"\n\t\t\t+ \"  \\\"detailMessage\\\": \\\"exceptionMessage\\\",\"\n\t\t\t+ \"  \\\"error\\\": {\"\n\t\t\t+ \"    \\\"@class\\\": \\\"org.springframework.security.saml2.core.Saml2Error\\\",\"\n\t\t\t+ \"    \\\"errorCode\\\": \\\"errorCode\\\",\"\n\t\t\t+ \"    \\\"description\\\": \\\"errorDescription\\\"\"\n\t\t\t+ \"  }\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic Saml2AuthenticationException createDefaultSaml2AuthenticationException() {\n\t\treturn new Saml2AuthenticationException(new Saml2Error(\"errorCode\", \"errorDescription\"), \"exceptionMessage\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/DefaultSaml2AuthenticatedPrincipalMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass DefaultSaml2AuthenticatedPrincipalMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = TestSaml2JsonPayloads.createDefaultPrincipal();\n\n\t\tString principalJson = this.mapper.writeValueAsString(principal);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON, principalJson, true);\n\t}\n\n\t@Test\n\tvoid shouldSerializeWithoutRegistrationId() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\n\t\t\t\tTestSaml2JsonPayloads.PRINCIPAL_NAME, TestSaml2JsonPayloads.ATTRIBUTES,\n\t\t\t\tTestSaml2JsonPayloads.SESSION_INDEXES);\n\n\t\tString principalJson = this.mapper.writeValueAsString(principal);\n\n\t\tJSONAssert.assertEquals(principalWithoutRegId(), principalJson, true);\n\t}\n\n\t@Test\n\tvoid shouldSerializeWithoutIndices() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\n\t\t\t\tTestSaml2JsonPayloads.PRINCIPAL_NAME, TestSaml2JsonPayloads.ATTRIBUTES);\n\t\tprincipal.setRelyingPartyRegistrationId(TestSaml2JsonPayloads.REG_ID);\n\n\t\tString principalJson = this.mapper.writeValueAsString(principal);\n\n\t\tJSONAssert.assertEquals(principalWithoutIndices(), principalJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = this.mapper.readValue(\n\t\t\t\tTestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON, DefaultSaml2AuthenticatedPrincipal.class);\n\n\t\tassertThat(principal).isNotNull();\n\t\tassertThat(principal.getName()).isEqualTo(TestSaml2JsonPayloads.PRINCIPAL_NAME);\n\t\tassertThat(principal.getRelyingPartyRegistrationId()).isEqualTo(TestSaml2JsonPayloads.REG_ID);\n\t\tassertThat(principal.getAttributes()).isEqualTo(TestSaml2JsonPayloads.ATTRIBUTES);\n\t\tassertThat(principal.getSessionIndexes()).isEqualTo(TestSaml2JsonPayloads.SESSION_INDEXES);\n\t}\n\n\t@Test\n\tvoid shouldDeserializeWithoutRegistrationId() throws Exception {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = this.mapper.readValue(principalWithoutRegId(),\n\t\t\t\tDefaultSaml2AuthenticatedPrincipal.class);\n\n\t\tassertThat(principal).isNotNull();\n\t\tassertThat(principal.getName()).isEqualTo(TestSaml2JsonPayloads.PRINCIPAL_NAME);\n\t\tassertThat(principal.getRelyingPartyRegistrationId()).isNull();\n\t\tassertThat(principal.getAttributes()).isEqualTo(TestSaml2JsonPayloads.ATTRIBUTES);\n\t\tassertThat(principal.getSessionIndexes()).isEqualTo(TestSaml2JsonPayloads.SESSION_INDEXES);\n\t}\n\n\tprivate static String principalWithoutRegId() {\n\t\treturn TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON.replace(TestSaml2JsonPayloads.REG_ID_JSON,\n\t\t\t\t\"null\");\n\t}\n\n\tprivate static String principalWithoutIndices() {\n\t\treturn TestSaml2JsonPayloads.DEFAULT_AUTHENTICATED_PRINCIPAL_JSON\n\t\t\t.replace(TestSaml2JsonPayloads.SESSION_INDEXES_JSON, \"[\\\"java.util.Collections$EmptyList\\\", []]\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationExceptionMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2AuthenticationExceptionMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2AuthenticationException exception = TestSaml2JsonPayloads.createDefaultSaml2AuthenticationException();\n\n\t\tString exceptionJson = this.mapper.writeValueAsString(exception);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_SAML_AUTH_EXCEPTION_JSON, exceptionJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tSaml2AuthenticationException exception = this.mapper\n\t\t\t.readValue(TestSaml2JsonPayloads.DEFAULT_SAML_AUTH_EXCEPTION_JSON, Saml2AuthenticationException.class);\n\n\t\tassertThat(exception).isNotNull();\n\t\tassertThat(exception.getMessage()).isEqualTo(\"exceptionMessage\");\n\t\tassertThat(exception.getSaml2Error()).extracting(Saml2Error::getErrorCode, Saml2Error::getDescription)\n\t\t\t.contains(\"errorCode\", \"errorDescription\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/Saml2AuthenticationMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2AuthenticationMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2Authentication authentication = TestSaml2JsonPayloads.createDefaultAuthentication();\n\n\t\tString authenticationJson = this.mapper.writeValueAsString(authentication);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_SAML2AUTHENTICATION_JSON, authenticationJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tSaml2Authentication authentication = this.mapper\n\t\t\t.readValue(TestSaml2JsonPayloads.DEFAULT_SAML2AUTHENTICATION_JSON, Saml2Authentication.class);\n\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getDetails()).isEqualTo(TestSaml2JsonPayloads.DETAILS);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(TestSaml2JsonPayloads.SAML_RESPONSE);\n\t\tassertThat(authentication.getSaml2Response()).isEqualTo(TestSaml2JsonPayloads.SAML_RESPONSE);\n\t\tassertThat(authentication.getAuthorities()).isEqualTo(TestSaml2JsonPayloads.AUTHORITIES);\n\t\tassertThat(authentication.getPrincipal()).usingRecursiveComparison()\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.createDefaultPrincipal());\n\t\tassertThat(authentication.getDetails()).usingRecursiveComparison().isEqualTo(TestSaml2JsonPayloads.DETAILS);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/Saml2LogoutRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2LogoutRequestMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2LogoutRequest request = TestSaml2JsonPayloads.createDefaultSaml2LogoutRequest();\n\n\t\tString requestJson = this.mapper.writeValueAsString(request);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_LOGOUT_REQUEST_JSON, requestJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tdeserializeAndAssertRequest();\n\t}\n\n\t// gh-12539\n\t@Test\n\tvoid shouldDeserializeWhenFailOnMissingCreatorPropertiesEnabled() throws Exception {\n\t\t// Jackson will use reflection to initialize the binding property if this is not\n\t\t// enabled\n\t\tthis.mapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true);\n\t\tdeserializeAndAssertRequest();\n\t}\n\n\tprivate void deserializeAndAssertRequest() throws JsonProcessingException {\n\t\tSaml2LogoutRequest logoutRequest = this.mapper.readValue(TestSaml2JsonPayloads.DEFAULT_LOGOUT_REQUEST_JSON,\n\t\t\t\tSaml2LogoutRequest.class);\n\n\t\tassertThat(logoutRequest).isNotNull();\n\t\tassertThat(logoutRequest.getId()).isEqualTo(TestSaml2JsonPayloads.ID);\n\t\tassertThat(logoutRequest.getRelyingPartyRegistrationId())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID);\n\t\tassertThat(logoutRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(logoutRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(logoutRequest.getLocation()).isEqualTo(TestSaml2JsonPayloads.LOCATION);\n\t\tassertThat(logoutRequest.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tMap<String, String> expectedParams = new HashMap<>();\n\t\texpectedParams.put(\"SAMLRequest\", TestSaml2JsonPayloads.SAML_REQUEST);\n\t\texpectedParams.put(\"RelayState\", TestSaml2JsonPayloads.RELAY_STATE);\n\t\texpectedParams.put(\"AdditionalParam\", TestSaml2JsonPayloads.ADDITIONAL_PARAM);\n\t\tassertThat(logoutRequest.getParameters()).containsAllEntriesOf(expectedParams);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/Saml2PostAuthenticationRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2PostAuthenticationRequestMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2PostAuthenticationRequest request = TestSaml2JsonPayloads.createDefaultSaml2PostAuthenticationRequest();\n\n\t\tString requestJson = this.mapper.writeValueAsString(request);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON, requestJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tSaml2PostAuthenticationRequest authRequest = this.mapper\n\t\t\t.readValue(TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON, Saml2PostAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID);\n\t\tassertThat(authRequest.getId()).isEqualTo(TestSaml2JsonPayloads.ID);\n\t}\n\n\t@Test\n\tvoid shouldDeserializeWithNoRegistrationId() throws Exception {\n\t\tString json = TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON.replace(\n\t\t\t\t\"\\\"relyingPartyRegistrationId\\\": \\\"\" + TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID + \"\\\",\", \"\");\n\n\t\tSaml2PostAuthenticationRequest authRequest = this.mapper.readValue(json, Saml2PostAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId()).isNull();\n\t\tassertThat(authRequest.getId()).isEqualTo(TestSaml2JsonPayloads.ID);\n\t}\n\n\t@Test\n\tvoid shouldDeserializeWithNoId() throws Exception {\n\t\tString json = TestSaml2JsonPayloads.DEFAULT_POST_AUTH_REQUEST_JSON\n\t\t\t.replace(\", \\\"id\\\": \\\"\" + TestSaml2JsonPayloads.ID + \"\\\"\", \"\");\n\n\t\tSaml2PostAuthenticationRequest authRequest = this.mapper.readValue(json, Saml2PostAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID);\n\t\tassertThat(authRequest.getId()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/Saml2RedirectAuthenticationRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Saml2RedirectAuthenticationRequestMixinTests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n\t@Test\n\tvoid shouldSerialize() throws Exception {\n\t\tSaml2RedirectAuthenticationRequest request = TestSaml2JsonPayloads\n\t\t\t.createDefaultSaml2RedirectAuthenticationRequest();\n\n\t\tString requestJson = this.mapper.writeValueAsString(request);\n\n\t\tJSONAssert.assertEquals(TestSaml2JsonPayloads.DEFAULT_REDIRECT_AUTH_REQUEST_JSON, requestJson, true);\n\t}\n\n\t@Test\n\tvoid shouldDeserialize() throws Exception {\n\t\tSaml2RedirectAuthenticationRequest authRequest = this.mapper.readValue(\n\t\t\t\tTestSaml2JsonPayloads.DEFAULT_REDIRECT_AUTH_REQUEST_JSON, Saml2RedirectAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getSigAlg()).isEqualTo(TestSaml2JsonPayloads.SIG_ALG);\n\t\tassertThat(authRequest.getSignature()).isEqualTo(TestSaml2JsonPayloads.SIGNATURE);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID);\n\t}\n\n\t@Test\n\tvoid shouldDeserializeWithNoRegistrationId() throws Exception {\n\t\tString json = TestSaml2JsonPayloads.DEFAULT_REDIRECT_AUTH_REQUEST_JSON.replace(\n\t\t\t\t\"\\\"relyingPartyRegistrationId\\\": \\\"\" + TestSaml2JsonPayloads.RELYINGPARTY_REGISTRATION_ID + \"\\\",\", \"\");\n\n\t\tSaml2RedirectAuthenticationRequest authRequest = this.mapper.readValue(json,\n\t\t\t\tSaml2RedirectAuthenticationRequest.class);\n\n\t\tassertThat(authRequest).isNotNull();\n\t\tassertThat(authRequest.getSamlRequest()).isEqualTo(TestSaml2JsonPayloads.SAML_REQUEST);\n\t\tassertThat(authRequest.getRelayState()).isEqualTo(TestSaml2JsonPayloads.RELAY_STATE);\n\t\tassertThat(authRequest.getAuthenticationRequestUri())\n\t\t\t.isEqualTo(TestSaml2JsonPayloads.AUTHENTICATION_REQUEST_URI);\n\t\tassertThat(authRequest.getSigAlg()).isEqualTo(TestSaml2JsonPayloads.SIG_ALG);\n\t\tassertThat(authRequest.getSignature()).isEqualTo(TestSaml2JsonPayloads.SIGNATURE);\n\t\tassertThat(authRequest.getRelyingPartyRegistrationId()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/jackson2/TestSaml2JsonPayloads.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.jackson2;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\nfinal class TestSaml2JsonPayloads {\n\n\tprivate TestSaml2JsonPayloads() {\n\t}\n\n\tstatic final Map<String, List<Object>> ATTRIBUTES;\n\n\tstatic {\n\t\tMap<String, List<Object>> tmpAttributes = new HashMap<>();\n\t\ttmpAttributes.put(\"name\", Collections.singletonList(\"attr_name\"));\n\t\ttmpAttributes.put(\"email\", Collections.singletonList(\"attr_email\"));\n\t\ttmpAttributes.put(\"listOf\", Collections.unmodifiableList(Arrays.asList(\"Element1\", \"Element2\", 4, true)));\n\t\tATTRIBUTES = Collections.unmodifiableMap(tmpAttributes);\n\t}\n\n\tstatic final String REG_ID = \"REG_ID_TEST\";\n\tstatic final String REG_ID_JSON = \"\\\"\" + REG_ID + \"\\\"\";\n\n\tstatic final String SESSION_INDEXES_JSON = \"[\" + \"  \\\"java.util.Collections$UnmodifiableRandomAccessList\\\",\"\n\t\t\t+ \"  [ \\\"Index 1\\\", \\\"Index 2\\\" ]\" + \"]\";\n\tstatic final List<String> SESSION_INDEXES = Collections.unmodifiableList(Arrays.asList(\"Index 1\", \"Index 2\"));\n\n\tstatic final String PRINCIPAL_NAME = \"principalName\";\n\n\t// @formatter:off\n\tstatic final String DEFAULT_AUTHENTICATED_PRINCIPAL_JSON = \"{\"\n\t\t\t+ \"  \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal\\\",\"\n\t\t\t+ \"  \\\"name\\\": \\\"\" + PRINCIPAL_NAME + \"\\\",\"\n\t\t\t+ \"  \\\"attributes\\\": {\"\n\t\t\t+ \"    \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\"\n\t\t\t+ \"    \\\"listOf\\\": [\"\n\t\t\t+ \"      \\\"java.util.Collections$UnmodifiableRandomAccessList\\\",\"\n\t\t\t+ \"      [ \\\"Element1\\\", \\\"Element2\\\", 4, true ]\"\n\t\t\t+ \"    ],\"\n\t\t\t+ \"    \\\"email\\\": [\"\n\t\t\t+ \"      \\\"java.util.Collections$SingletonList\\\",\"\n\t\t\t+ \"      [ \\\"attr_email\\\" ]\"\n\t\t\t+ \"    ],\"\n\t\t\t+ \"    \\\"name\\\": [\"\n\t\t\t+ \"      \\\"java.util.Collections$SingletonList\\\",\"\n\t\t\t+ \"      [ \\\"attr_name\\\" ]\"\n\t\t\t+ \"    ]\"\n\t\t\t+ \"  },\"\n\t\t\t+ \"  \\\"sessionIndexes\\\": \" + SESSION_INDEXES_JSON + \",\"\n\t\t\t+ \"  \\\"registrationId\\\": \" + REG_ID_JSON + \"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic DefaultSaml2AuthenticatedPrincipal createDefaultPrincipal() {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(PRINCIPAL_NAME,\n\t\t\t\tATTRIBUTES, SESSION_INDEXES);\n\t\tprincipal.setRelyingPartyRegistrationId(REG_ID);\n\t\treturn principal;\n\t}\n\n\tstatic final String SAML_REQUEST = \"samlRequestValue\";\n\tstatic final String RELAY_STATE = \"relayStateValue\";\n\tstatic final String AUTHENTICATION_REQUEST_URI = \"authenticationRequestUriValue\";\n\tstatic final String RELYINGPARTY_REGISTRATION_ID = \"registrationIdValue\";\n\tstatic final String SIG_ALG = \"sigAlgValue\";\n\tstatic final String SIGNATURE = \"signatureValue\";\n\tstatic final String ID = \"idValue\";\n\n\t// @formatter:off\n\tstatic final String DEFAULT_REDIRECT_AUTH_REQUEST_JSON = \"{\"\n\t\t\t+ \" \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest\\\",\"\n\t\t\t+ \" \\\"samlRequest\\\": \\\"\" + SAML_REQUEST + \"\\\",\"\n\t\t\t+ \" \\\"relayState\\\": \\\"\" + RELAY_STATE + \"\\\",\"\n\t\t\t+ \" \\\"authenticationRequestUri\\\": \\\"\" + AUTHENTICATION_REQUEST_URI + \"\\\",\"\n\t\t\t+ \" \\\"relyingPartyRegistrationId\\\": \\\"\" + RELYINGPARTY_REGISTRATION_ID + \"\\\",\"\n\t\t\t+ \" \\\"sigAlg\\\": \\\"\" + SIG_ALG + \"\\\",\"\n\t\t\t+ \" \\\"signature\\\": \\\"\" + SIGNATURE + \"\\\",\"\n\t\t\t+ \" \\\"id\\\": \\\"\" + ID + \"\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tstatic final String DEFAULT_POST_AUTH_REQUEST_JSON = \"{\"\n\t\t\t+ \" \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest\\\",\"\n\t\t\t+ \" \\\"samlRequest\\\": \\\"\" + SAML_REQUEST + \"\\\",\"\n\t\t\t+ \" \\\"relayState\\\": \\\"\" + RELAY_STATE + \"\\\",\"\n\t\t\t+ \" \\\"relyingPartyRegistrationId\\\": \\\"\" + RELYINGPARTY_REGISTRATION_ID + \"\\\",\"\n\t\t\t+ \" \\\"authenticationRequestUri\\\": \\\"\" + AUTHENTICATION_REQUEST_URI + \"\\\",\"\n\t\t\t+ \" \\\"id\\\": \\\"\" + ID + \"\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic final String LOCATION = \"locationValue\";\n\tstatic final String BINDNG = \"REDIRECT\";\n\tstatic final String ADDITIONAL_PARAM = \"additionalParamValue\";\n\n\t// @formatter:off\n\tstatic final String DEFAULT_LOGOUT_REQUEST_JSON = \"{\"\n\t\t\t+ \"  \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest\\\",\"\n\t\t\t+ \"  \\\"id\\\": \\\"\" + ID + \"\\\",\"\n\t\t\t+ \"  \\\"location\\\": \\\"\" + LOCATION + \"\\\",\"\n\t\t\t+ \"  \\\"binding\\\": \\\"\" + BINDNG + \"\\\",\"\n\t\t\t+ \"  \\\"relyingPartyRegistrationId\\\": \\\"\" + RELYINGPARTY_REGISTRATION_ID + \"\\\",\"\n\t\t\t+ \"  \\\"parameters\\\": { \"\n\t\t\t+ \"     \\\"@class\\\": \\\"java.util.Collections$UnmodifiableMap\\\",\"\n\t\t\t+ \"     \\\"SAMLRequest\\\": \\\"\" + SAML_REQUEST + \"\\\",\"\n\t\t\t+ \"     \\\"RelayState\\\": \\\"\" + RELAY_STATE + \"\\\",\"\n\t\t\t+ \"     \\\"AdditionalParam\\\": \\\"\" + ADDITIONAL_PARAM + \"\\\"\"\n\t\t\t+ \"  }\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic Saml2PostAuthenticationRequest createDefaultSaml2PostAuthenticationRequest() {\n\t\treturn Saml2PostAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.full()\n\t\t\t\t.registrationId(RELYINGPARTY_REGISTRATION_ID)\n\t\t\t\t.assertingPartyMetadata((party) -> party.singleSignOnServiceLocation(AUTHENTICATION_REQUEST_URI))\n\t\t\t\t.build())\n\t\t\t.samlRequest(SAML_REQUEST)\n\t\t\t.relayState(RELAY_STATE)\n\t\t\t.id(ID)\n\t\t\t.build();\n\t}\n\n\tstatic Saml2RedirectAuthenticationRequest createDefaultSaml2RedirectAuthenticationRequest() {\n\t\treturn Saml2RedirectAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.full()\n\t\t\t\t.registrationId(RELYINGPARTY_REGISTRATION_ID)\n\t\t\t\t.assertingPartyMetadata((party) -> party.singleSignOnServiceLocation(AUTHENTICATION_REQUEST_URI))\n\t\t\t\t.build())\n\t\t\t.samlRequest(SAML_REQUEST)\n\t\t\t.relayState(RELAY_STATE)\n\t\t\t.sigAlg(SIG_ALG)\n\t\t\t.signature(SIGNATURE)\n\t\t\t.id(ID)\n\t\t\t.build();\n\t}\n\n\tstatic Saml2LogoutRequest createDefaultSaml2LogoutRequest() {\n\t\treturn Saml2LogoutRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.full()\n\t\t\t\t.registrationId(RELYINGPARTY_REGISTRATION_ID)\n\t\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceLocation(LOCATION)\n\t\t\t\t\t.singleLogoutServiceBinding(Saml2MessageBinding.REDIRECT))\n\t\t\t\t.build())\n\t\t\t.id(ID)\n\t\t\t.samlRequest(SAML_REQUEST)\n\t\t\t.relayState(RELAY_STATE)\n\t\t\t.parameters((params) -> params.put(\"AdditionalParam\", ADDITIONAL_PARAM))\n\t\t\t.build();\n\t}\n\n\tstatic final Collection<GrantedAuthority> AUTHORITIES = Collections\n\t\t.unmodifiableList(Arrays.asList(new SimpleGrantedAuthority(\"Role1\"), new SimpleGrantedAuthority(\"Role2\")));\n\n\tstatic final Object DETAILS = User.withUsername(\"username\").password(\"empty\").authorities(\"A\", \"B\").build();\n\tstatic final String SAML_RESPONSE = \"samlResponseValue\";\n\n\t// @formatter:off\n\tstatic final String DEFAULT_SAML2AUTHENTICATION_JSON = \"{\"\n\t\t\t+ \"\t\\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.Saml2Authentication\\\",\"\n\t\t\t+ \"\t\\\"authorities\\\": [\"\n\t\t\t+ \"\t\t\\\"java.util.Collections$UnmodifiableRandomAccessList\\\",\"\n\t\t\t+ \"\t\t[\"\n\t\t\t+ \"\t\t\t{\"\n\t\t\t+ \"\t\t\t\t\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\"\n\t\t\t+ \"\t\t\t\t\\\"authority\\\": \\\"Role1\\\"\"\n\t\t\t+ \"\t\t\t},\"\n\t\t\t+ \"\t\t\t{\"\n\t\t\t+ \"\t\t\t\t\\\"@class\\\": \\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\"\n\t\t\t+ \"\t\t\t\t\\\"authority\\\": \\\"Role2\\\"\"\n\t\t\t+ \"\t\t\t}\"\n\t\t\t+ \"\t\t]\"\n\t\t\t+ \" ],\"\n\t\t\t+ \"\t\\\"details\\\": {\"\n\t\t\t+ \"\t\t\\\"@class\\\": \\\"org.springframework.security.core.userdetails.User\\\",\"\n\t\t\t+ \"\t\t\\\"password\\\": \\\"empty\\\",\"\n\t\t\t+ \"\t\t\\\"username\\\": \\\"username\\\",\"\n\t\t\t+ \"\t\t\\\"authorities\\\": [\"\n\t\t\t+ \"\t\t\t\\\"java.util.Collections$UnmodifiableSet\\\", [\"\n\t\t\t+ \"\t\t\t\t{\"\n\t\t\t+ \"\t\t\t\t\t\\\"@class\\\":\\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\"\n\t\t\t+ \"\t\t\t\t\t\\\"authority\\\":\\\"A\\\"\"\n\t\t\t+ \"\t\t\t\t},\"\n\t\t\t+ \"\t\t\t\t{\"\n\t\t\t+ \"\t\t\t\t\t\\\"@class\\\":\\\"org.springframework.security.core.authority.SimpleGrantedAuthority\\\",\"\n\t\t\t+ \"\t\t\t\t\t\\\"authority\\\":\\\"B\\\"\"\n\t\t\t+ \"\t\t\t\t}\"\n\t\t\t+ \"\t\t]],\"\n\t\t\t+ \"\t\t\\\"accountNonExpired\\\": true,\"\n\t\t\t+ \"\t\t\\\"accountNonLocked\\\": true,\"\n\t\t\t+ \"\t\t\\\"credentialsNonExpired\\\": true,\"\n\t\t\t+ \"\t\t\\\"enabled\\\": true\"\n\t\t\t+ \"\t},\"\n\t\t\t+ \"\t\\\"principal\\\": \" + DEFAULT_AUTHENTICATED_PRINCIPAL_JSON + \",\"\n\t\t\t+ \"\t\\\"saml2Response\\\": \\\"\" + SAML_RESPONSE + \"\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic Saml2Authentication createDefaultAuthentication() {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = createDefaultPrincipal();\n\t\tSaml2Authentication authentication = new Saml2Authentication(principal, SAML_RESPONSE, AUTHORITIES);\n\t\tauthentication.setDetails(DETAILS);\n\t\treturn authentication;\n\t}\n\n\t// @formatter:off\n\tstatic final String DEFAULT_SAML_AUTH_EXCEPTION_JSON = \"{\"\n\t\t\t+ \"  \\\"@class\\\": \\\"org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException\\\",\"\n\t\t\t+ \"  \\\"detailMessage\\\": \\\"exceptionMessage\\\",\"\n\t\t\t+ \"  \\\"error\\\": {\"\n\t\t\t+ \"    \\\"@class\\\": \\\"org.springframework.security.saml2.core.Saml2Error\\\",\"\n\t\t\t+ \"    \\\"errorCode\\\": \\\"errorCode\\\",\"\n\t\t\t+ \"    \\\"description\\\": \\\"errorDescription\\\"\"\n\t\t\t+ \"  }\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\n\tstatic Saml2AuthenticationException createDefaultSaml2AuthenticationException() {\n\t\treturn new Saml2AuthenticationException(new Saml2Error(\"errorCode\", \"errorDescription\"), \"exceptionMessage\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\npublic class DefaultSaml2AuthenticatedPrincipalTests {\n\n\t@Test\n\tpublic void createDefaultSaml2AuthenticatedPrincipal() {\n\t\tMap<String, List<Object>> attributes = new LinkedHashMap<>();\n\t\tattributes.put(\"email\", Arrays.asList(\"john.doe@example.com\", \"doe.john@example.com\"));\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\", attributes);\n\t\tassertThat(principal.getName()).isEqualTo(\"user\");\n\t\tassertThat(principal.getAttributes()).isEqualTo(attributes);\n\t}\n\n\t@Test\n\tpublic void createDefaultSaml2AuthenticatedPrincipalWhenNameNullThenException() {\n\t\tMap<String, List<Object>> attributes = new LinkedHashMap<>();\n\t\tattributes.put(\"email\", Arrays.asList(\"john.doe@example.com\", \"doe.john@example.com\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DefaultSaml2AuthenticatedPrincipal(null, attributes))\n\t\t\t.withMessageContaining(\"name cannot be null\");\n\t}\n\n\t@Test\n\tpublic void createDefaultSaml2AuthenticatedPrincipalWhenAttributesNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultSaml2AuthenticatedPrincipal(\"user\", (Map<String, List<Object>>) null))\n\t\t\t.withMessageContaining(\"attributes cannot be null\");\n\t}\n\n\t@Test\n\tpublic void getFirstAttributeWhenStringValueThenReturnsValue() {\n\t\tMap<String, List<Object>> attributes = new LinkedHashMap<>();\n\t\tattributes.put(\"email\", Arrays.asList(\"john.doe@example.com\", \"doe.john@example.com\"));\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\", attributes);\n\t\tassertThat(principal.<String>getFirstAttribute(\"email\")).isEqualTo(attributes.get(\"email\").get(0));\n\t}\n\n\t@Test\n\tpublic void getAttributeWhenStringValuesThenReturnsValues() {\n\t\tMap<String, List<Object>> attributes = new LinkedHashMap<>();\n\t\tattributes.put(\"email\", Arrays.asList(\"john.doe@example.com\", \"doe.john@example.com\"));\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\", attributes);\n\t\tassertThat(principal.<String>getAttribute(\"email\")).isEqualTo(attributes.get(\"email\"));\n\t}\n\n\t@Test\n\tpublic void getAttributeWhenDistinctValuesThenReturnsValues() {\n\t\tfinal Boolean registered = true;\n\t\tfinal Instant registeredDate = Instant.parse(\"1970-01-01T00:00:00Z\");\n\t\tMap<String, List<Object>> attributes = new LinkedHashMap<>();\n\t\tattributes.put(\"registration\", Arrays.asList(registered, registeredDate));\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\", attributes);\n\t\tList<Object> registrationInfo = principal.getAttribute(\"registration\");\n\t\tassertThat(registrationInfo).isNotNull();\n\t\tassertThat((Boolean) registrationInfo.get(0)).isEqualTo(registered);\n\t\tassertThat((Instant) registrationInfo.get(1)).isEqualTo(registeredDate);\n\t}\n\n\t// gh-15346\n\t@Test\n\tpublic void whenUsedAsKeyInMapThenRetrievableAcrossSerialization() {\n\t\tMap<Saml2AuthenticatedPrincipal, Integer> valuesByPrincipal = new LinkedHashMap<>();\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\", Map.of());\n\t\tvaluesByPrincipal.put(principal, 1);\n\t\tprincipal = new DefaultSaml2AuthenticatedPrincipal(\"user\", Map.of());\n\t\tassertThat(valuesByPrincipal.get(principal)).isEqualTo(1);\n\t\tprincipal = new DefaultSaml2AuthenticatedPrincipal(\"user\", Map.of());\n\t\tprincipal.setRelyingPartyRegistrationId(\"id\");\n\t\tassertThat(valuesByPrincipal.get(principal)).isNull();\n\t\tvaluesByPrincipal.put(principal, 2);\n\t\tprincipal = new DefaultSaml2AuthenticatedPrincipal(\"user\", Map.of());\n\t\tprincipal.setRelyingPartyRegistrationId(\"id\");\n\t\tassertThat(valuesByPrincipal.get(principal)).isEqualTo(2);\n\t\tprincipal = new DefaultSaml2AuthenticatedPrincipal(\"USER\", Map.of());\n\t\tprincipal.setRelyingPartyRegistrationId(\"id\");\n\t\tassertThat(valuesByPrincipal.get(principal)).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/Saml2AssertionAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass Saml2AssertionAuthenticationTests {\n\n\t@Test\n\tvoid toBuilderWhenApplyThenCopies() {\n\t\tSaml2ResponseAssertion.Builder prototype = Saml2ResponseAssertion.withResponseValue(\"response\");\n\t\tSaml2AssertionAuthentication factorOne = new Saml2AssertionAuthentication(\"alice\",\n\t\t\t\tprototype.nameId(\"alice\").build(), AuthorityUtils.createAuthorityList(\"FACTOR_ONE\"), \"alice\");\n\t\tSaml2AssertionAuthentication factorTwo = new Saml2AssertionAuthentication(\"bob\",\n\t\t\t\tprototype.nameId(\"bob\").build(), AuthorityUtils.createAuthorityList(\"FACTOR_TWO\"), \"bob\");\n\t\tSaml2AssertionAuthentication result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.credentials(factorTwo.getCredentials())\n\t\t\t.relyingPartyRegistrationId(factorTwo.getRelyingPartyRegistrationId())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(result.getCredentials()).isSameAs(factorTwo.getCredentials());\n\t\tassertThat(result.getRelyingPartyRegistrationId()).isSameAs(factorTwo.getRelyingPartyRegistrationId());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.util.SerializationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass Saml2PostAuthenticationRequestTests {\n\n\tprivate static final String IDP_SSO_URL = \"https://sso-url.example.com/IDP/SSO\";\n\n\t@Test\n\tvoid serializeWhenDeserializeThenSameFields() {\n\t\tSaml2PostAuthenticationRequest authenticationRequest = getAuthenticationRequestBuilder().build();\n\t\tbyte[] bytes = SerializationUtils.serialize(authenticationRequest);\n\t\tSaml2PostAuthenticationRequest deserializedAuthenticationRequest = (Saml2PostAuthenticationRequest) SerializationUtils\n\t\t\t.deserialize(bytes);\n\t\tassertThat(deserializedAuthenticationRequest).usingRecursiveComparison().isEqualTo(authenticationRequest);\n\t}\n\n\t@Test\n\tvoid serializeWhenDeserializeAndCompareToOtherThenNotSame() {\n\t\tSaml2PostAuthenticationRequest authenticationRequest = getAuthenticationRequestBuilder().build();\n\t\tSaml2PostAuthenticationRequest otherAuthenticationRequest = getAuthenticationRequestBuilder()\n\t\t\t.relayState(\"relay\")\n\t\t\t.build();\n\t\tbyte[] bytes = SerializationUtils.serialize(otherAuthenticationRequest);\n\t\tSaml2PostAuthenticationRequest deserializedAuthenticationRequest = (Saml2PostAuthenticationRequest) SerializationUtils\n\t\t\t.deserialize(bytes);\n\t\tassertThat(deserializedAuthenticationRequest).usingRecursiveComparison().isNotEqualTo(authenticationRequest);\n\t}\n\n\tprivate Saml2PostAuthenticationRequest.Builder getAuthenticationRequestBuilder() {\n\t\treturn Saml2PostAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.relyingPartyRegistration().build())\n\t\t\t.samlRequest(\"request\")\n\t\t\t.authenticationRequestUri(IDP_SSO_URL);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.util.SerializationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass Saml2RedirectAuthenticationRequestTests {\n\n\tprivate static final String IDP_SSO_URL = \"https://sso-url.example.com/IDP/SSO\";\n\n\t@Test\n\tvoid serializeWhenDeserializeThenSameFields() {\n\t\tSaml2RedirectAuthenticationRequest authenticationRequest = getAuthenticationRequestBuilder().build();\n\t\tbyte[] bytes = SerializationUtils.serialize(authenticationRequest);\n\t\tSaml2RedirectAuthenticationRequest deserializedAuthenticationRequest = (Saml2RedirectAuthenticationRequest) SerializationUtils\n\t\t\t.deserialize(bytes);\n\t\tassertThat(deserializedAuthenticationRequest).usingRecursiveComparison().isEqualTo(authenticationRequest);\n\t}\n\n\t@Test\n\tvoid serializeWhenDeserializeAndCompareToOtherThenNotSame() {\n\t\tSaml2RedirectAuthenticationRequest authenticationRequest = getAuthenticationRequestBuilder().build();\n\t\tSaml2RedirectAuthenticationRequest otherAuthenticationRequest = getAuthenticationRequestBuilder()\n\t\t\t.relayState(\"relay\")\n\t\t\t.build();\n\t\tbyte[] bytes = SerializationUtils.serialize(otherAuthenticationRequest);\n\t\tSaml2RedirectAuthenticationRequest deserializedAuthenticationRequest = (Saml2RedirectAuthenticationRequest) SerializationUtils\n\t\t\t.deserialize(bytes);\n\t\tassertThat(deserializedAuthenticationRequest).usingRecursiveComparison().isNotEqualTo(authenticationRequest);\n\t}\n\n\tprivate Saml2RedirectAuthenticationRequest.Builder getAuthenticationRequestBuilder() {\n\t\treturn Saml2RedirectAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.relyingPartyRegistration().build())\n\t\t\t.samlRequest(\"request\")\n\t\t\t.authenticationRequestUri(IDP_SSO_URL);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.security.cert.X509Certificate;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\nimport javax.xml.namespace.QName;\n\nimport org.apache.xml.security.encryption.XMLCipherParameters;\nimport org.opensaml.core.xml.XMLObject;\nimport org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;\nimport org.opensaml.core.xml.io.MarshallingException;\nimport org.opensaml.core.xml.schema.XSAny;\nimport org.opensaml.core.xml.schema.XSBoolean;\nimport org.opensaml.core.xml.schema.XSBooleanValue;\nimport org.opensaml.core.xml.schema.XSInteger;\nimport org.opensaml.core.xml.schema.XSString;\nimport org.opensaml.core.xml.schema.XSURI;\nimport org.opensaml.core.xml.schema.impl.XSAnyBuilder;\nimport org.opensaml.core.xml.schema.impl.XSBooleanBuilder;\nimport org.opensaml.core.xml.schema.impl.XSIntegerBuilder;\nimport org.opensaml.core.xml.schema.impl.XSStringBuilder;\nimport org.opensaml.core.xml.schema.impl.XSURIBuilder;\nimport org.opensaml.saml.common.SAMLVersion;\nimport org.opensaml.saml.common.SignableSAMLObject;\nimport org.opensaml.saml.common.xml.SAMLConstants;\nimport org.opensaml.saml.saml2.core.Assertion;\nimport org.opensaml.saml.saml2.core.Attribute;\nimport org.opensaml.saml.saml2.core.AttributeStatement;\nimport org.opensaml.saml.saml2.core.AttributeValue;\nimport org.opensaml.saml.saml2.core.AuthnRequest;\nimport org.opensaml.saml.saml2.core.AuthnStatement;\nimport org.opensaml.saml.saml2.core.Conditions;\nimport org.opensaml.saml.saml2.core.EncryptedAssertion;\nimport org.opensaml.saml.saml2.core.EncryptedAttribute;\nimport org.opensaml.saml.saml2.core.EncryptedID;\nimport org.opensaml.saml.saml2.core.Issuer;\nimport org.opensaml.saml.saml2.core.LogoutRequest;\nimport org.opensaml.saml.saml2.core.LogoutResponse;\nimport org.opensaml.saml.saml2.core.NameID;\nimport org.opensaml.saml.saml2.core.Response;\nimport org.opensaml.saml.saml2.core.Status;\nimport org.opensaml.saml.saml2.core.StatusCode;\nimport org.opensaml.saml.saml2.core.Subject;\nimport org.opensaml.saml.saml2.core.SubjectConfirmation;\nimport org.opensaml.saml.saml2.core.SubjectConfirmationData;\nimport org.opensaml.saml.saml2.core.impl.AttributeBuilder;\nimport org.opensaml.saml.saml2.core.impl.AttributeStatementBuilder;\nimport org.opensaml.saml.saml2.core.impl.IssuerBuilder;\nimport org.opensaml.saml.saml2.core.impl.LogoutRequestBuilder;\nimport org.opensaml.saml.saml2.core.impl.LogoutResponseBuilder;\nimport org.opensaml.saml.saml2.core.impl.NameIDBuilder;\nimport org.opensaml.saml.saml2.core.impl.StatusBuilder;\nimport org.opensaml.saml.saml2.core.impl.StatusCodeBuilder;\nimport org.opensaml.saml.saml2.encryption.Encrypter;\nimport org.opensaml.saml.saml2.metadata.EntityDescriptor;\nimport org.opensaml.saml.saml2.metadata.IDPSSODescriptor;\nimport org.opensaml.saml.saml2.metadata.KeyDescriptor;\nimport org.opensaml.saml.saml2.metadata.SingleSignOnService;\nimport org.opensaml.saml.saml2.metadata.impl.EntityDescriptorBuilder;\nimport org.opensaml.saml.saml2.metadata.impl.IDPSSODescriptorBuilder;\nimport org.opensaml.saml.saml2.metadata.impl.KeyDescriptorBuilder;\nimport org.opensaml.saml.saml2.metadata.impl.SingleSignOnServiceBuilder;\nimport org.opensaml.security.SecurityException;\nimport org.opensaml.security.credential.BasicCredential;\nimport org.opensaml.security.credential.Credential;\nimport org.opensaml.security.credential.CredentialSupport;\nimport org.opensaml.security.credential.UsageType;\nimport org.opensaml.xmlsec.SignatureSigningParameters;\nimport org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;\nimport org.opensaml.xmlsec.encryption.support.EncryptionException;\nimport org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters;\nimport org.opensaml.xmlsec.keyinfo.KeyInfoSupport;\nimport org.opensaml.xmlsec.signature.KeyInfo;\nimport org.opensaml.xmlsec.signature.impl.KeyInfoBuilder;\nimport org.opensaml.xmlsec.signature.support.SignatureConstants;\nimport org.opensaml.xmlsec.signature.support.SignatureException;\nimport org.opensaml.xmlsec.signature.support.SignatureSupport;\n\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.core.OpenSamlInitializationService;\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\n\npublic final class TestOpenSamlObjects {\n\n\tstatic {\n\t\tOpenSamlInitializationService.initialize();\n\t}\n\tprivate static String USERNAME = \"test@saml.user\";\n\n\tprivate static String DESTINATION = \"https://localhost/login/saml2/sso/idp-alias\";\n\n\tprivate static String LOGOUT_DESTINATION = \"http://localhost/logout/saml2/slo\";\n\n\tpublic static String RELYING_PARTY_ENTITY_ID = \"https://localhost/saml2/service-provider-metadata/idp-alias\";\n\n\tpublic static String ASSERTING_PARTY_ENTITY_ID = \"https://some.idp.test/saml2/idp\";\n\n\tprivate static SecretKey SECRET_KEY = new SecretKeySpec(\n\t\t\tBase64.getDecoder().decode(\"shOnwNMoCv88HKMEa91+FlYoD5RNvzMTAL5LGxZKIFk=\"), \"AES\");\n\n\tprivate TestOpenSamlObjects() {\n\t}\n\n\tpublic static Response response() {\n\t\treturn response(DESTINATION, ASSERTING_PARTY_ENTITY_ID);\n\t}\n\n\tpublic static Response response(String destination, String issuerEntityId) {\n\t\tResponse response = build(Response.DEFAULT_ELEMENT_NAME);\n\t\tresponse.setID(\"R\" + UUID.randomUUID().toString());\n\t\tresponse.setVersion(SAMLVersion.VERSION_20);\n\t\tresponse.setID(\"_\" + UUID.randomUUID().toString());\n\t\tresponse.setDestination(destination);\n\t\tresponse.setIssuer(issuer(issuerEntityId));\n\t\treturn response;\n\t}\n\n\tstatic Response signedResponseWithOneAssertion() {\n\t\treturn signedResponseWithOneAssertion((response) -> {\n\t\t});\n\t}\n\n\tstatic Response signedResponseWithOneAssertion(Consumer<Response> responseConsumer) {\n\t\tResponse response = response();\n\t\tresponse.getAssertions().add(assertion());\n\t\tresponseConsumer.accept(response);\n\t\treturn signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);\n\t}\n\n\tstatic Assertion assertion() {\n\t\treturn assertion(USERNAME, ASSERTING_PARTY_ENTITY_ID, RELYING_PARTY_ENTITY_ID, DESTINATION);\n\t}\n\n\tpublic static Assertion assertion(String username, String issuerEntityId, String recipientEntityId,\n\t\t\tString recipientUri) {\n\t\tAssertion assertion = build(Assertion.DEFAULT_ELEMENT_NAME);\n\t\tassertion.setID(\"A\" + UUID.randomUUID().toString());\n\t\tassertion.setVersion(SAMLVersion.VERSION_20);\n\t\tassertion.setIssuer(issuer(issuerEntityId));\n\t\tassertion.setSubject(subject(username));\n\t\tassertion.setConditions(conditions());\n\t\tassertion.setIssueInstant(Instant.now());\n\t\tSubjectConfirmation subjectConfirmation = subjectConfirmation();\n\t\tsubjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER);\n\t\tSubjectConfirmationData confirmationData = subjectConfirmationData(recipientEntityId);\n\t\tconfirmationData.setRecipient(recipientUri);\n\t\tsubjectConfirmation.setSubjectConfirmationData(confirmationData);\n\t\tassertion.getSubject().getSubjectConfirmations().add(subjectConfirmation);\n\t\tAuthnStatement statement = build(AuthnStatement.DEFAULT_ELEMENT_NAME);\n\t\tstatement.setSessionIndex(\"session-index\");\n\t\tassertion.getAuthnStatements().add(statement);\n\t\treturn assertion;\n\t}\n\n\tstatic Issuer issuer(String entityId) {\n\t\tIssuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME);\n\t\tissuer.setValue(entityId);\n\t\treturn issuer;\n\t}\n\n\tstatic Subject subject(String principalName) {\n\t\tSubject subject = build(Subject.DEFAULT_ELEMENT_NAME);\n\t\tif (principalName != null) {\n\t\t\tsubject.setNameID(nameId(principalName));\n\t\t}\n\t\treturn subject;\n\t}\n\n\tstatic NameID nameId(String principalName) {\n\t\tNameID nameId = build(NameID.DEFAULT_ELEMENT_NAME);\n\t\tnameId.setValue(principalName);\n\t\treturn nameId;\n\t}\n\n\tstatic SubjectConfirmation subjectConfirmation() {\n\t\treturn build(SubjectConfirmation.DEFAULT_ELEMENT_NAME);\n\t}\n\n\tstatic SubjectConfirmationData subjectConfirmationData(String recipient) {\n\t\tSubjectConfirmationData subject = build(SubjectConfirmationData.DEFAULT_ELEMENT_NAME);\n\t\tsubject.setRecipient(recipient);\n\t\treturn subject;\n\t}\n\n\tstatic Conditions conditions() {\n\t\treturn build(Conditions.DEFAULT_ELEMENT_NAME);\n\t}\n\n\tpublic static AuthnRequest authnRequest() {\n\t\tIssuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME);\n\t\tissuer.setValue(ASSERTING_PARTY_ENTITY_ID);\n\t\tAuthnRequest authnRequest = build(AuthnRequest.DEFAULT_ELEMENT_NAME);\n\t\tauthnRequest.setIssuer(issuer);\n\t\tauthnRequest.setDestination(ASSERTING_PARTY_ENTITY_ID + \"/SSO.saml2\");\n\t\tauthnRequest.setAssertionConsumerServiceURL(DESTINATION);\n\t\treturn authnRequest;\n\t}\n\n\tpublic static LogoutRequest logoutRequest() {\n\t\tIssuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME);\n\t\tissuer.setValue(ASSERTING_PARTY_ENTITY_ID);\n\t\tNameID nameId = build(NameID.DEFAULT_ELEMENT_NAME);\n\t\tnameId.setValue(\"user\");\n\t\tLogoutRequest logoutRequest = build(LogoutRequest.DEFAULT_ELEMENT_NAME);\n\t\tlogoutRequest.setIssuer(issuer);\n\t\tlogoutRequest.setDestination(LOGOUT_DESTINATION);\n\t\tlogoutRequest.setNameID(nameId);\n\t\treturn logoutRequest;\n\t}\n\n\tpublic static Credential getSigningCredential(Saml2X509Credential credential, String entityId) {\n\t\tBasicCredential cred = getBasicCredential(credential);\n\t\tcred.setEntityId(entityId);\n\t\tcred.setUsageType(UsageType.SIGNING);\n\t\treturn cred;\n\t}\n\n\tstatic BasicCredential getBasicCredential(Saml2X509Credential credential) {\n\t\treturn CredentialSupport.getSimpleCredential(credential.getCertificate(), credential.getPrivateKey());\n\t}\n\n\tstatic <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId,\n\t\t\tString signAlgorithmUri) {\n\t\tSignatureSigningParameters parameters = new SignatureSigningParameters();\n\t\tCredential signingCredential = getSigningCredential(credential, entityId);\n\t\tparameters.setSigningCredential(signingCredential);\n\t\tparameters.setSignatureAlgorithm(signAlgorithmUri);\n\t\tparameters.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);\n\t\tparameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);\n\t\ttry {\n\t\t\tSignatureSupport.signObject(signable, parameters);\n\t\t}\n\t\tcatch (MarshallingException | SignatureException | SecurityException ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t\treturn signable;\n\t}\n\n\tpublic static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId) {\n\t\treturn signed(signable, credential, entityId, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);\n\t}\n\n\tstatic EncryptedAssertion encrypted(Assertion assertion, Saml2X509Credential credential) {\n\t\tX509Certificate certificate = credential.getCertificate();\n\t\tEncrypter encrypter = getEncrypter(certificate);\n\t\ttry {\n\t\t\treturn encrypter.encrypt(assertion);\n\t\t}\n\t\tcatch (EncryptionException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to encrypt assertion.\", ex);\n\t\t}\n\t}\n\n\tstatic EncryptedID encrypted(NameID nameId, Saml2X509Credential credential) {\n\t\tX509Certificate certificate = credential.getCertificate();\n\t\tEncrypter encrypter = getEncrypter(certificate);\n\t\ttry {\n\t\t\treturn encrypter.encrypt(nameId);\n\t\t}\n\t\tcatch (EncryptionException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to encrypt nameID.\", ex);\n\t\t}\n\t}\n\n\tstatic EncryptedAttribute encrypted(String name, String value, Saml2X509Credential credential) {\n\t\tAttribute attribute = attribute(name, value);\n\t\tX509Certificate certificate = credential.getCertificate();\n\t\tEncrypter encrypter = getEncrypter(certificate);\n\t\ttry {\n\t\t\treturn encrypter.encrypt(attribute);\n\t\t}\n\t\tcatch (EncryptionException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to encrypt nameID.\", ex);\n\t\t}\n\t}\n\n\tprivate static Encrypter getEncrypter(X509Certificate certificate) {\n\t\tString dataAlgorithm = XMLCipherParameters.AES_256;\n\t\tString keyAlgorithm = XMLCipherParameters.RSA_1_5;\n\t\tBasicCredential dataCredential = new BasicCredential(SECRET_KEY);\n\t\tDataEncryptionParameters dataEncryptionParameters = new DataEncryptionParameters();\n\t\tdataEncryptionParameters.setEncryptionCredential(dataCredential);\n\t\tdataEncryptionParameters.setAlgorithm(dataAlgorithm);\n\t\tCredential credential = CredentialSupport.getSimpleCredential(certificate, null);\n\t\tKeyEncryptionParameters keyEncryptionParameters = new KeyEncryptionParameters();\n\t\tkeyEncryptionParameters.setEncryptionCredential(credential);\n\t\tkeyEncryptionParameters.setAlgorithm(keyAlgorithm);\n\t\tEncrypter encrypter = new Encrypter(dataEncryptionParameters, keyEncryptionParameters);\n\t\tEncrypter.KeyPlacement keyPlacement = Encrypter.KeyPlacement.valueOf(\"PEER\");\n\t\tencrypter.setKeyPlacement(keyPlacement);\n\t\treturn encrypter;\n\t}\n\n\tstatic Attribute attribute(String name, String value) {\n\t\tAttribute attribute = build(Attribute.DEFAULT_ELEMENT_NAME);\n\t\tattribute.setName(name);\n\t\tXSString xsValue = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);\n\t\txsValue.setValue(value);\n\t\tattribute.getAttributeValues().add(xsValue);\n\t\treturn attribute;\n\t}\n\n\tstatic AttributeStatement customAttributeStatement(String attributeName, XMLObject customAttributeValue) {\n\t\tAttributeStatementBuilder attributeStatementBuilder = new AttributeStatementBuilder();\n\t\tAttributeBuilder attributeBuilder = new AttributeBuilder();\n\t\tAttribute attribute = attributeBuilder.buildObject();\n\t\tattribute.setName(attributeName);\n\t\tattribute.getAttributeValues().add(customAttributeValue);\n\t\tAttributeStatement attributeStatement = attributeStatementBuilder.buildObject();\n\t\tattributeStatement.getAttributes().add(attribute);\n\t\treturn attributeStatement;\n\t}\n\n\tstatic List<AttributeStatement> attributeStatements() {\n\t\tList<AttributeStatement> attributeStatements = new ArrayList<>();\n\t\tAttributeStatementBuilder attributeStatementBuilder = new AttributeStatementBuilder();\n\t\tAttributeBuilder attributeBuilder = new AttributeBuilder();\n\t\tAttributeStatement attrStmt1 = attributeStatementBuilder.buildObject();\n\t\tAttribute emailAttr = attributeBuilder.buildObject();\n\t\temailAttr.setName(\"email\");\n\t\tXSAny email1 = new XSAnyBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSAny.TYPE_NAME); // gh-8864\n\t\temail1.setTextContent(\"john.doe@example.com\");\n\t\temailAttr.getAttributeValues().add(email1);\n\t\tXSAny email2 = new XSAnyBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME);\n\t\temail2.setTextContent(\"doe.john@example.com\");\n\t\temailAttr.getAttributeValues().add(email2);\n\t\tattrStmt1.getAttributes().add(emailAttr);\n\t\tAttribute nameAttr = attributeBuilder.buildObject();\n\t\tnameAttr.setName(\"name\");\n\t\tXSString name = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);\n\t\tname.setValue(\"John Doe\");\n\t\tnameAttr.getAttributeValues().add(name);\n\t\tattrStmt1.getAttributes().add(nameAttr);\n\t\tAttribute roleOneAttr = attributeBuilder.buildObject(); // gh-11042\n\t\troleOneAttr.setName(\"role\");\n\t\tXSString roleOne = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);\n\t\troleOne.setValue(\"RoleOne\");\n\t\troleOneAttr.getAttributeValues().add(roleOne);\n\t\tattrStmt1.getAttributes().add(roleOneAttr);\n\t\tAttribute roleTwoAttr = attributeBuilder.buildObject(); // gh-11042\n\t\troleTwoAttr.setName(\"role\");\n\t\tXSString roleTwo = new XSStringBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);\n\t\troleTwo.setValue(\"RoleTwo\");\n\t\troleTwoAttr.getAttributeValues().add(roleTwo);\n\t\tattrStmt1.getAttributes().add(roleTwoAttr);\n\t\tAttribute ageAttr = attributeBuilder.buildObject();\n\t\tageAttr.setName(\"age\");\n\t\tXSInteger age = new XSIntegerBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSInteger.TYPE_NAME);\n\t\tage.setValue(21);\n\t\tageAttr.getAttributeValues().add(age);\n\t\tattrStmt1.getAttributes().add(ageAttr);\n\t\tattributeStatements.add(attrStmt1);\n\t\tAttributeStatement attrStmt2 = attributeStatementBuilder.buildObject();\n\t\tAttribute websiteAttr = attributeBuilder.buildObject();\n\t\twebsiteAttr.setName(\"website\");\n\t\tXSURI uri = new XSURIBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSURI.TYPE_NAME);\n\t\turi.setURI(\"https://johndoe.com/\");\n\t\twebsiteAttr.getAttributeValues().add(uri);\n\t\tattrStmt2.getAttributes().add(websiteAttr);\n\t\tAttribute registeredAttr = attributeBuilder.buildObject();\n\t\tregisteredAttr.setName(\"registered\");\n\t\tXSBoolean registered = new XSBooleanBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME,\n\t\t\t\tXSBoolean.TYPE_NAME);\n\t\tregistered.setValue(new XSBooleanValue(true, false));\n\t\tregisteredAttr.getAttributeValues().add(registered);\n\t\tattrStmt2.getAttributes().add(registeredAttr);\n\t\tattributeStatements.add(attrStmt2);\n\t\treturn attributeStatements;\n\t}\n\n\tstatic Status successStatus() {\n\t\treturn status(StatusCode.SUCCESS);\n\t}\n\n\tstatic Status status(String code) {\n\t\tStatus status = new StatusBuilder().buildObject();\n\t\tStatusCode statusCode = new StatusCodeBuilder().buildObject();\n\t\tstatusCode.setValue(code);\n\t\tstatus.setStatusCode(statusCode);\n\t\treturn status;\n\t}\n\n\tpublic static LogoutRequest assertingPartyLogoutRequest(RelyingPartyRegistration registration) {\n\t\tLogoutRequestBuilder logoutRequestBuilder = new LogoutRequestBuilder();\n\t\tLogoutRequest logoutRequest = logoutRequestBuilder.buildObject();\n\t\tlogoutRequest.setID(\"id\");\n\t\tNameIDBuilder nameIdBuilder = new NameIDBuilder();\n\t\tNameID nameId = nameIdBuilder.buildObject();\n\t\tnameId.setValue(\"user\");\n\t\tlogoutRequest.setNameID(nameId);\n\t\tIssuerBuilder issuerBuilder = new IssuerBuilder();\n\t\tIssuer issuer = issuerBuilder.buildObject();\n\t\tissuer.setValue(registration.getAssertingPartyMetadata().getEntityId());\n\t\tlogoutRequest.setIssuer(issuer);\n\t\tlogoutRequest.setDestination(registration.getSingleLogoutServiceLocation());\n\t\treturn logoutRequest;\n\t}\n\n\tpublic static LogoutRequest assertingPartyLogoutRequestNameIdInEncryptedId(RelyingPartyRegistration registration) {\n\t\tLogoutRequestBuilder logoutRequestBuilder = new LogoutRequestBuilder();\n\t\tLogoutRequest logoutRequest = logoutRequestBuilder.buildObject();\n\t\tlogoutRequest.setID(\"id\");\n\t\tNameIDBuilder nameIdBuilder = new NameIDBuilder();\n\t\tNameID nameId = nameIdBuilder.buildObject();\n\t\tnameId.setValue(\"user\");\n\t\tlogoutRequest.setNameID(null);\n\t\tSaml2X509Credential credential = registration.getAssertingPartyMetadata()\n\t\t\t.getEncryptionX509Credentials()\n\t\t\t.iterator()\n\t\t\t.next();\n\t\tEncryptedID encrypted = encrypted(nameId, credential);\n\t\tlogoutRequest.setEncryptedID(encrypted);\n\t\tIssuerBuilder issuerBuilder = new IssuerBuilder();\n\t\tIssuer issuer = issuerBuilder.buildObject();\n\t\tissuer.setValue(registration.getAssertingPartyMetadata().getEntityId());\n\t\tlogoutRequest.setIssuer(issuer);\n\t\tlogoutRequest.setDestination(registration.getSingleLogoutServiceLocation());\n\t\treturn logoutRequest;\n\t}\n\n\tpublic static LogoutResponse assertingPartyLogoutResponse(RelyingPartyRegistration registration) {\n\t\tLogoutResponseBuilder logoutResponseBuilder = new LogoutResponseBuilder();\n\t\tLogoutResponse logoutResponse = logoutResponseBuilder.buildObject();\n\t\tlogoutResponse.setID(\"id\");\n\t\tStatusBuilder statusBuilder = new StatusBuilder();\n\t\tStatusCodeBuilder statusCodeBuilder = new StatusCodeBuilder();\n\t\tStatusCode code = statusCodeBuilder.buildObject();\n\t\tcode.setValue(StatusCode.SUCCESS);\n\t\tStatus status = statusBuilder.buildObject();\n\t\tstatus.setStatusCode(code);\n\t\tlogoutResponse.setStatus(status);\n\t\tIssuerBuilder issuerBuilder = new IssuerBuilder();\n\t\tIssuer issuer = issuerBuilder.buildObject();\n\t\tissuer.setValue(registration.getAssertingPartyMetadata().getEntityId());\n\t\tlogoutResponse.setIssuer(issuer);\n\t\tlogoutResponse.setDestination(registration.getSingleLogoutServiceResponseLocation());\n\t\treturn logoutResponse;\n\t}\n\n\tpublic static EntityDescriptor entityDescriptor(RelyingPartyRegistration registration) {\n\t\tEntityDescriptorBuilder entityDescriptorBuilder = new EntityDescriptorBuilder();\n\t\tEntityDescriptor entityDescriptor = entityDescriptorBuilder.buildObject();\n\t\tentityDescriptor.setEntityID(registration.getAssertingPartyMetadata().getEntityId());\n\t\tIDPSSODescriptorBuilder idpssoDescriptorBuilder = new IDPSSODescriptorBuilder();\n\t\tIDPSSODescriptor idpssoDescriptor = idpssoDescriptorBuilder.buildObject();\n\t\tidpssoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);\n\t\tSingleSignOnServiceBuilder singleSignOnServiceBuilder = new SingleSignOnServiceBuilder();\n\t\tSingleSignOnService singleSignOnService = singleSignOnServiceBuilder.buildObject();\n\t\tsingleSignOnService.setBinding(Saml2MessageBinding.POST.getUrn());\n\t\tsingleSignOnService.setLocation(registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation());\n\t\tidpssoDescriptor.getSingleSignOnServices().add(singleSignOnService);\n\t\tKeyDescriptorBuilder keyDescriptorBuilder = new KeyDescriptorBuilder();\n\t\tKeyDescriptor keyDescriptor = keyDescriptorBuilder.buildObject();\n\t\tkeyDescriptor.setUse(UsageType.SIGNING);\n\t\tKeyInfoBuilder keyInfoBuilder = new KeyInfoBuilder();\n\t\tKeyInfo keyInfo = keyInfoBuilder.buildObject();\n\t\taddCertificate(keyInfo, registration.getSigningX509Credentials().iterator().next().getCertificate());\n\t\tkeyDescriptor.setKeyInfo(keyInfo);\n\t\tidpssoDescriptor.getKeyDescriptors().add(keyDescriptor);\n\t\tentityDescriptor.getRoleDescriptors(IDPSSODescriptor.DEFAULT_ELEMENT_NAME).add(idpssoDescriptor);\n\t\treturn entityDescriptor;\n\t}\n\n\tstatic void addCertificate(KeyInfo info, X509Certificate certificate) {\n\t\ttry {\n\t\t\tKeyInfoSupport.addCertificate(info, certificate);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2Exception(ex);\n\t\t}\n\t}\n\n\tstatic <T extends XMLObject> T build(QName qName) {\n\t\treturn (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2AuthenticationTokens.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\n/**\n * Tests instances for {@link Saml2AuthenticationToken}\n *\n * @author Marcus Da Coregio\n */\npublic final class TestSaml2AuthenticationTokens {\n\n\tprivate TestSaml2AuthenticationTokens() {\n\t}\n\n\tpublic static Saml2AuthenticationToken token() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.build();\n\t\treturn new Saml2AuthenticationToken(relyingPartyRegistration, \"saml2-xml-response-object\");\n\t}\n\n\tpublic static Saml2AuthenticationToken tokenRequested() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.build();\n\t\treturn new Saml2AuthenticationToken(relyingPartyRegistration, \"saml2-xml-response-object\",\n\t\t\t\tTestSaml2PostAuthenticationRequests.create());\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2Authentications.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport java.util.Collections;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\n\n/**\n * Tests instances for {@link Saml2Authentication}\n *\n * @author Josh Cummings\n */\npublic final class TestSaml2Authentications {\n\n\tprivate TestSaml2Authentications() {\n\t}\n\n\tpublic static Saml2Authentication authentication() {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\",\n\t\t\t\tCollections.emptyMap());\n\t\tprincipal.setRelyingPartyRegistrationId(\"simplesamlphp\");\n\t\treturn new Saml2Authentication(principal, \"response\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2LogoutRequests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\npublic final class TestSaml2LogoutRequests {\n\n\tprivate TestSaml2LogoutRequests() {\n\n\t}\n\n\tpublic static Saml2LogoutRequest create() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\treturn Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"samlRequest\")\n\t\t\t.binding(Saml2MessageBinding.POST)\n\t\t\t.location(\"location\")\n\t\t\t.relayState(\"relayState\")\n\t\t\t.id(\"id\")\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2PostAuthenticationRequests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\npublic final class TestSaml2PostAuthenticationRequests {\n\n\tprivate TestSaml2PostAuthenticationRequests() {\n\t}\n\n\tpublic static Saml2PostAuthenticationRequest create() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\treturn Saml2PostAuthenticationRequest.withRelyingPartyRegistration(registration)\n\t\t\t.authenticationRequestUri(\"uri\")\n\t\t\t.samlRequest(\"samlRequest\")\n\t\t\t.id(\"id\")\n\t\t\t.relayState(\"relayState\")\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2RedirectAuthenticationRequests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.authentication;\n\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\npublic final class TestSaml2RedirectAuthenticationRequests {\n\n\tprivate TestSaml2RedirectAuthenticationRequests() {\n\t}\n\n\tpublic static Saml2RedirectAuthenticationRequest create() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\treturn Saml2RedirectAuthenticationRequest.withRelyingPartyRegistration(registration)\n\t\t\t.authenticationRequestUri(\"uri\")\n\t\t\t.samlRequest(\"samlRequest\")\n\t\t\t.id(\"id\")\n\t\t\t.relayState(\"relayState\")\n\t\t\t.sigAlg(\"sigAlg\")\n\t\t\t.signature(\"signature\")\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/metadata/RequestMatcherMetadataResponseResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.metadata;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.saml2.Saml2Exception;\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic final class RequestMatcherMetadataResponseResolverTests {\n\n\t@Mock\n\tSaml2MetadataResolver metadataFactory;\n\n\t@Test\n\tvoid saml2MetadataRegistrationIdResolveWhenMatchesThenResolves() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\tRelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(registration);\n\t\tRequestMatcherMetadataResponseResolver resolver = new RequestMatcherMetadataResponseResolver(registrations,\n\t\t\t\tthis.metadataFactory);\n\t\tString registrationId = registration.getRegistrationId();\n\t\tgiven(this.metadataFactory.resolve(any(Collection.class))).willReturn(\"metadata\");\n\t\tMockHttpServletRequest request = get(\"/saml2/metadata/\" + registrationId);\n\t\tSaml2MetadataResponse response = resolver.resolve(request);\n\t\tassertThat(response.getMetadata()).isEqualTo(\"metadata\");\n\t\tassertThat(response.getFileName()).isEqualTo(\"saml-\" + registrationId + \"-metadata.xml\");\n\t\tverify(this.metadataFactory).resolve(any(Collection.class));\n\t}\n\n\t@Test\n\tvoid saml2MetadataResolveWhenNoMatchingRegistrationThenNull() {\n\t\tRelyingPartyRegistrationRepository registrations = mock(RelyingPartyRegistrationRepository.class);\n\t\tRequestMatcherMetadataResponseResolver resolver = new RequestMatcherMetadataResponseResolver(registrations,\n\t\t\t\tthis.metadataFactory);\n\t\tMockHttpServletRequest request = get(\"/saml2/metadata\");\n\t\tSaml2MetadataResponse response = resolver.resolve(request);\n\t\tassertThat(response).isNull();\n\t}\n\n\t@Test\n\tvoid saml2MetadataRegistrationIdResolveWhenNoMatchingRegistrationThenException() {\n\t\tRelyingPartyRegistrationRepository registrations = mock(RelyingPartyRegistrationRepository.class);\n\t\tRequestMatcherMetadataResponseResolver resolver = new RequestMatcherMetadataResponseResolver(registrations,\n\t\t\t\tthis.metadataFactory);\n\t\tMockHttpServletRequest request = get(\"/saml2/metadata/id\");\n\t\tassertThatExceptionOfType(Saml2Exception.class).isThrownBy(() -> resolver.resolve(request));\n\t}\n\n\t@Test\n\tvoid resolveWhenNoRegistrationIdThenResolvesAll() {\n\t\tRelyingPartyRegistration one = withEntityId(\"one\");\n\t\tRelyingPartyRegistration two = withEntityId(\"two\");\n\t\tRelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(one, two);\n\t\tRequestMatcherMetadataResponseResolver resolver = new RequestMatcherMetadataResponseResolver(registrations,\n\t\t\t\tthis.metadataFactory);\n\t\tgiven(this.metadataFactory.resolve(any(Collection.class))).willReturn(\"metadata\");\n\t\tMockHttpServletRequest request = get(\"/saml2/metadata\");\n\t\tSaml2MetadataResponse response = resolver.resolve(request);\n\t\tassertThat(response.getMetadata()).isEqualTo(\"metadata\");\n\t\tassertThat(response.getFileName()).doesNotContain(one.getRegistrationId())\n\t\t\t.contains(\"saml\")\n\t\t\t.contains(\"metadata.xml\");\n\t\tverify(this.metadataFactory).resolve(any(Collection.class));\n\t}\n\n\t@Test\n\tvoid resolveWhenRequestDoesNotMatchThenNull() {\n\t\tRelyingPartyRegistrationRepository registrations = mock(RelyingPartyRegistrationRepository.class);\n\t\tRequestMatcherMetadataResponseResolver resolver = new RequestMatcherMetadataResponseResolver(registrations,\n\t\t\t\tthis.metadataFactory);\n\t\tassertThat(resolver.resolve(new MockHttpServletRequest())).isNull();\n\t}\n\n\t// gh-13700\n\t@Test\n\tvoid resolveWhenNoRegistrationIdThenResolvesEntityIds() {\n\t\tRelyingPartyRegistration one = withEntityId(\"one\");\n\t\tRelyingPartyRegistration two = withEntityId(\"two\");\n\t\tRelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(one, two);\n\t\tRequestMatcherMetadataResponseResolver resolver = new RequestMatcherMetadataResponseResolver(registrations,\n\t\t\t\tthis.metadataFactory);\n\t\tgiven(this.metadataFactory.resolve(any(Collection.class))).willReturn(\"metadata\");\n\t\tresolver.resolve(get(\"/saml2/metadata\"));\n\t\tArgumentCaptor<Collection<RelyingPartyRegistration>> captor = ArgumentCaptor.forClass(Collection.class);\n\t\tverify(this.metadataFactory).resolve(captor.capture());\n\t\tCollection<RelyingPartyRegistration> resolved = captor.getValue();\n\t\tassertThat(resolved).hasSize(2);\n\t\tassertThat(resolved.iterator().next().getEntityId()).isEqualTo(\"one\");\n\t}\n\n\tprivate MockHttpServletRequest get(String uri) {\n\t\treturn TestMockHttpServletRequests.get(uri).build();\n\t}\n\n\tprivate RelyingPartyRegistration withEntityId(String entityId) {\n\t\treturn TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.registrationId(entityId)\n\t\t\t.entityId(\"{registrationId}\")\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/CachingRelyingPartyRegistrationRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.util.concurrent.Callable;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.cache.Cache;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link CachingRelyingPartyRegistrationRepository}\n */\n@ExtendWith(MockitoExtension.class)\npublic class CachingRelyingPartyRegistrationRepositoryTests {\n\n\t@Mock\n\tCallable<Iterable<RelyingPartyRegistration>> callable;\n\n\t@InjectMocks\n\tCachingRelyingPartyRegistrationRepository registrations;\n\n\t@Test\n\tpublic void iteratorWhenResolvableThenPopulatesCache() throws Exception {\n\t\tgiven(this.callable.call()).willReturn(mock(IterableRelyingPartyRegistrationRepository.class));\n\t\tthis.registrations.iterator();\n\t\tverify(this.callable).call();\n\t\tthis.registrations.iterator();\n\t\tverifyNoMoreInteractions(this.callable);\n\t}\n\n\t@Test\n\tpublic void iteratorWhenExceptionThenPropagates() throws Exception {\n\t\tgiven(this.callable.call()).willThrow(IllegalStateException.class);\n\t\tassertThatExceptionOfType(Cache.ValueRetrievalException.class).isThrownBy(this.registrations::iterator)\n\t\t\t.withCauseInstanceOf(IllegalStateException.class);\n\t}\n\n\t@Test\n\tpublic void findByRegistrationIdWhenResolvableThenPopulatesCache() throws Exception {\n\t\tgiven(this.callable.call()).willReturn(mock(IterableRelyingPartyRegistrationRepository.class));\n\t\tthis.registrations.findByRegistrationId(\"id\");\n\t\tverify(this.callable).call();\n\t\tthis.registrations.findByRegistrationId(\"id\");\n\t\tverifyNoMoreInteractions(this.callable);\n\t}\n\n\t@Test\n\tpublic void findUniqueByAssertingPartyEntityIdWhenResolvableThenPopulatesCache() throws Exception {\n\t\tgiven(this.callable.call()).willReturn(mock(IterableRelyingPartyRegistrationRepository.class));\n\t\tthis.registrations.findUniqueByAssertingPartyEntityId(\"id\");\n\t\tverify(this.callable).call();\n\t\tthis.registrations.findUniqueByAssertingPartyEntityId(\"id\");\n\t\tverifyNoMoreInteractions(this.callable);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link InMemoryRelyingPartyRegistrationRepository}\n */\npublic class InMemoryRelyingPartyRegistrationRepositoryTests {\n\n\t@Test\n\tvoid findByRegistrationIdWhenGivenIdThenReturnsMatchingRegistration() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\tInMemoryRelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(\n\t\t\t\tregistration);\n\t\tassertThat(registrations.findByRegistrationId(registration.getRegistrationId())).isSameAs(registration);\n\t}\n\n\t@Test\n\tvoid findByRegistrationIdWhenGivenWrongIdThenReturnsNull() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\tInMemoryRelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(\n\t\t\t\tregistration);\n\t\tassertThat(registrations.findByRegistrationId(registration.getRegistrationId() + \"wrong\")).isNull();\n\t\tassertThat(registrations.findByRegistrationId(null)).isNull();\n\t}\n\n\t@Test\n\tvoid findByAssertingPartyEntityIdWhenGivenEntityIdThenReturnsMatchingRegistrations() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\tInMemoryRelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(\n\t\t\t\tregistration);\n\t\tString assertingPartyEntityId = registration.getAssertingPartyMetadata().getEntityId();\n\t\tassertThat(registrations.findUniqueByAssertingPartyEntityId(assertingPartyEntityId)).isEqualTo(registration);\n\t}\n\n\t@Test\n\tvoid findByAssertingPartyEntityIdWhenGivenWrongEntityIdThenReturnsEmpty() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\tInMemoryRelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(\n\t\t\t\tregistration);\n\t\tString assertingPartyEntityId = registration.getAssertingPartyMetadata().getEntityId();\n\t\tassertThat(registrations.findUniqueByAssertingPartyEntityId(assertingPartyEntityId + \"wrong\")).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/JdbcAssertingPartyMetadataRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.util.Iterator;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link JdbcAssertingPartyMetadataRepository}\n */\nclass JdbcAssertingPartyMetadataRepositoryTests {\n\n\tprivate static final String SCHEMA_SQL_RESOURCE = \"org/springframework/security/saml2/saml2-asserting-party-metadata-schema.sql\";\n\n\tprivate EmbeddedDatabase db;\n\n\tprivate JdbcAssertingPartyMetadataRepository repository;\n\n\tprivate JdbcOperations jdbcOperations;\n\n\tprivate final AssertingPartyMetadata metadata = TestRelyingPartyRegistrations.full()\n\t\t.build()\n\t\t.getAssertingPartyMetadata();\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.db = createDb();\n\t\tthis.jdbcOperations = new JdbcTemplate(this.db);\n\t\tthis.repository = new JdbcAssertingPartyMetadataRepository(this.jdbcOperations);\n\t}\n\n\t@AfterEach\n\tvoid tearDown() {\n\t\tthis.db.shutdown();\n\t}\n\n\t@Test\n\tvoid constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new JdbcAssertingPartyMetadataRepository(null))\n\t\t\t\t.withMessage(\"jdbcOperations cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid findByEntityIdWhenEntityIdIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.repository.findByEntityId(null))\n\t\t\t\t.withMessage(\"entityId cannot be empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid findByEntityIdWhenEntityPresentThenReturns() {\n\t\tthis.repository.save(this.metadata);\n\n\t\tAssertingPartyMetadata found = this.repository.findByEntityId(this.metadata.getEntityId());\n\n\t\tassertAssertingPartyEquals(found, this.metadata);\n\t}\n\n\t@Test\n\tvoid findByEntityIdWhenNotExistsThenNull() {\n\t\tAssertingPartyMetadata found = this.repository.findByEntityId(\"non-existent-entity-id\");\n\t\tassertThat(found).isNull();\n\t}\n\n\t@Test\n\tvoid iteratorWhenEnitiesExistThenContains() {\n\t\tAssertingPartyMetadata second = this.metadata.mutate().entityId(\"https://example.org/idp\").build();\n\t\tthis.repository.save(this.metadata);\n\t\tthis.repository.save(second);\n\n\t\tIterator<AssertingPartyMetadata> iterator = this.repository.iterator();\n\n\t\tassertAssertingPartyEquals(iterator.next(), this.metadata);\n\t\tassertAssertingPartyEquals(iterator.next(), second);\n\t\tassertThat(iterator.hasNext()).isFalse();\n\t}\n\n\t@Test\n\tvoid saveWhenExistingThenUpdates() {\n\t\tthis.repository.save(this.metadata);\n\t\tboolean existing = this.metadata.getWantAuthnRequestsSigned();\n\t\tthis.repository.save(this.metadata.mutate().wantAuthnRequestsSigned(!existing).build());\n\t\tboolean updated = this.repository.findByEntityId(this.metadata.getEntityId()).getWantAuthnRequestsSigned();\n\t\tassertThat(existing).isNotEqualTo(updated);\n\t}\n\n\tprivate static EmbeddedDatabase createDb() {\n\t\treturn createDb(SCHEMA_SQL_RESOURCE);\n\t}\n\n\tprivate static EmbeddedDatabase createDb(String schema) {\n\t\t// @formatter:off\n\t\treturn new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(schema)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate void assertAssertingPartyEquals(AssertingPartyMetadata found, AssertingPartyMetadata expected) {\n\t\tassertThat(found).isNotNull();\n\t\tassertThat(found.getEntityId()).isEqualTo(expected.getEntityId());\n\t\tassertThat(found.getSingleSignOnServiceLocation()).isEqualTo(expected.getSingleSignOnServiceLocation());\n\t\tassertThat(found.getSingleSignOnServiceBinding()).isEqualTo(expected.getSingleSignOnServiceBinding());\n\t\tassertThat(found.getWantAuthnRequestsSigned()).isEqualTo(expected.getWantAuthnRequestsSigned());\n\t\tassertThat(found.getSingleLogoutServiceLocation()).isEqualTo(expected.getSingleLogoutServiceLocation());\n\t\tassertThat(found.getSingleLogoutServiceResponseLocation())\n\t\t\t.isEqualTo(expected.getSingleLogoutServiceResponseLocation());\n\t\tassertThat(found.getSingleLogoutServiceBinding()).isEqualTo(expected.getSingleLogoutServiceBinding());\n\t\tassertThat(found.getSigningAlgorithms()).containsAll(expected.getSigningAlgorithms());\n\t\tassertThat(found.getVerificationX509Credentials()).containsAll(expected.getVerificationX509Credentials());\n\t\tassertThat(found.getEncryptionX509Credentials()).containsAll(expected.getEncryptionX509Credentials());\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.client.MockClientHttpResponse;\nimport org.springframework.security.saml2.Saml2Exception;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\npublic class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests {\n\n\tprivate static final String CERTIFICATE = \"MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk\";\n\n\tprivate static final String ENTITIES_DESCRIPTOR_TEMPLATE = \"<md:EntitiesDescriptor xmlns:md=\\\"urn:oasis:names:tc:SAML:2.0:metadata\\\">\\n%s</md:EntitiesDescriptor>\";\n\n\tprivate static final String ENTITY_DESCRIPTOR_TEMPLATE = \"<md:EntityDescriptor xmlns:md=\\\"urn:oasis:names:tc:SAML:2.0:metadata\\\" \"\n\t\t\t+ \"entityID=\\\"entity-id\\\" \"\n\t\t\t+ \"ID=\\\"_bf133aac099b99b3d81286e1a341f2d34188043a77fe15bf4bf1487dae9b2ea3\\\">\\n%s\"\n\t\t\t+ \"</md:EntityDescriptor>\";\n\n\tprivate static final String IDP_SSO_DESCRIPTOR_TEMPLATE = \"<md:IDPSSODescriptor protocolSupportEnumeration=\\\"urn:oasis:names:tc:SAML:2.0:protocol\\\">\\n\"\n\t\t\t+ \"%s\\n\" + \"</md:IDPSSODescriptor>\";\n\n\tprivate static final String KEY_DESCRIPTOR_TEMPLATE = \"<md:KeyDescriptor %s>\\n\"\n\t\t\t+ \"<ds:KeyInfo xmlns:ds=\\\"http://www.w3.org/2000/09/xmldsig#\\\">\\n\" + \"<ds:X509Data>\\n\"\n\t\t\t+ \"<ds:X509Certificate>\" + CERTIFICATE + \"</ds:X509Certificate>\\n\" + \"</ds:X509Data>\\n\" + \"</ds:KeyInfo>\\n\"\n\t\t\t+ \"</md:KeyDescriptor>\";\n\n\tprivate static final String SINGLE_SIGN_ON_SERVICE_TEMPLATE = \"<md:SingleSignOnService Binding=\\\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\\\" \"\n\t\t\t+ \"Location=\\\"sso-location\\\"/>\";\n\n\tprivate OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter converter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.converter = new OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter();\n\t}\n\n\t@Test\n\tpublic void readWhenMissingIDPSSODescriptorThenException() {\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(\n\t\t\t\t(String.format(ENTITY_DESCRIPTOR_TEMPLATE, \"\")).getBytes(), HttpStatus.OK);\n\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t.isThrownBy(() -> this.converter.read(RelyingPartyRegistration.Builder.class, response))\n\t\t\t.withMessageContaining(\"Metadata response is missing the necessary IDPSSODescriptor element\");\n\t}\n\n\t@Test\n\tpublic void readWhenMissingVerificationKeyThenException() {\n\t\tString payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE, String.format(IDP_SSO_DESCRIPTOR_TEMPLATE, \"\"));\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), HttpStatus.OK);\n\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t.isThrownBy(() -> this.converter.read(RelyingPartyRegistration.Builder.class, response))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"Metadata response is missing verification certificates, necessary for verifying SAML assertions\");\n\t}\n\n\t@Test\n\tpublic void readWhenMissingSingleSignOnServiceThenException() {\n\t\tString payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE,\n\t\t\t\tString.format(IDP_SSO_DESCRIPTOR_TEMPLATE, String.format(KEY_DESCRIPTOR_TEMPLATE, \"use=\\\"signing\\\"\")));\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), HttpStatus.OK);\n\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t.isThrownBy(() -> this.converter.read(RelyingPartyRegistration.Builder.class, response))\n\t\t\t.withMessageContaining(\n\t\t\t\t\t\"Metadata response is missing a SingleSignOnService, necessary for sending AuthnRequests\");\n\t}\n\n\t@Test\n\tpublic void readWhenDescriptorFullySpecifiedThenConfigures() throws Exception {\n\t\tString payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE,\n\t\t\t\tString.format(IDP_SSO_DESCRIPTOR_TEMPLATE,\n\t\t\t\t\t\tString.format(KEY_DESCRIPTOR_TEMPLATE, \"use=\\\"signing\\\"\")\n\t\t\t\t\t\t\t\t+ String.format(KEY_DESCRIPTOR_TEMPLATE, \"use=\\\"encryption\\\"\")\n\t\t\t\t\t\t\t\t+ String.format(SINGLE_SIGN_ON_SERVICE_TEMPLATE)));\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), HttpStatus.OK);\n\t\tRelyingPartyRegistration registration = this.converter.read(RelyingPartyRegistration.Builder.class, response)\n\t\t\t.registrationId(\"one\")\n\t\t\t.build();\n\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\tassertThat(details.getWantAuthnRequestsSigned()).isFalse();\n\t\tassertThat(details.getSingleSignOnServiceLocation()).isEqualTo(\"sso-location\");\n\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(details.getEntityId()).isEqualTo(\"entity-id\");\n\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(details.getVerificationX509Credentials().iterator().next().getCertificate())\n\t\t\t.isEqualTo(x509Certificate(CERTIFICATE));\n\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t\tassertThat(details.getEncryptionX509Credentials().iterator().next().getCertificate())\n\t\t\t.isEqualTo(x509Certificate(CERTIFICATE));\n\t}\n\n\t// gh-9051\n\t@Test\n\tpublic void readWhenEntitiesDescriptorThenConfigures() throws Exception {\n\t\tString payload = String.format(ENTITIES_DESCRIPTOR_TEMPLATE,\n\t\t\t\tString.format(ENTITY_DESCRIPTOR_TEMPLATE,\n\t\t\t\t\t\tString.format(IDP_SSO_DESCRIPTOR_TEMPLATE,\n\t\t\t\t\t\t\t\tString.format(KEY_DESCRIPTOR_TEMPLATE, \"use=\\\"signing\\\"\")\n\t\t\t\t\t\t\t\t\t\t+ String.format(KEY_DESCRIPTOR_TEMPLATE, \"use=\\\"encryption\\\"\")\n\t\t\t\t\t\t\t\t\t\t+ String.format(SINGLE_SIGN_ON_SERVICE_TEMPLATE))));\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), HttpStatus.OK);\n\t\tRelyingPartyRegistration registration = this.converter.read(RelyingPartyRegistration.Builder.class, response)\n\t\t\t.registrationId(\"one\")\n\t\t\t.build();\n\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\tassertThat(details.getWantAuthnRequestsSigned()).isFalse();\n\t\tassertThat(details.getSingleSignOnServiceLocation()).isEqualTo(\"sso-location\");\n\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);\n\t\tassertThat(details.getEntityId()).isEqualTo(\"entity-id\");\n\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(details.getVerificationX509Credentials().iterator().next().getCertificate())\n\t\t\t.isEqualTo(x509Certificate(CERTIFICATE));\n\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t\tassertThat(details.getEncryptionX509Credentials().iterator().next().getCertificate())\n\t\t\t.isEqualTo(x509Certificate(CERTIFICATE));\n\t}\n\n\t@Test\n\tpublic void readWhenKeyDescriptorHasNoUseThenConfiguresBothKeyTypes() throws Exception {\n\t\tString payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE, String.format(IDP_SSO_DESCRIPTOR_TEMPLATE,\n\t\t\t\tString.format(KEY_DESCRIPTOR_TEMPLATE, \"\") + String.format(SINGLE_SIGN_ON_SERVICE_TEMPLATE)));\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), HttpStatus.OK);\n\t\tRelyingPartyRegistration registration = this.converter.read(RelyingPartyRegistration.Builder.class, response)\n\t\t\t.registrationId(\"one\")\n\t\t\t.build();\n\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\tassertThat(details.getVerificationX509Credentials().iterator().next().getCertificate())\n\t\t\t.isEqualTo(x509Certificate(CERTIFICATE));\n\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t\tassertThat(details.getEncryptionX509Credentials().iterator().next().getCertificate())\n\t\t\t.isEqualTo(x509Certificate(CERTIFICATE));\n\t}\n\n\tX509Certificate x509Certificate(String data) {\n\t\ttry {\n\t\t\tInputStream certificate = new ByteArrayInputStream(Base64.getDecoder().decode(data.getBytes()));\n\t\t\treturn (X509Certificate) CertificateFactory.getInstance(\"X.509\").generateCertificate(certificate);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\t// gh-9051\n\t@Test\n\tpublic void readWhenUnsupportedElementThenSaml2Exception() {\n\t\tString payload = \"<saml2:Assertion xmlns:saml2=\\\"https://some.endpoint\\\"/>\";\n\t\tMockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), HttpStatus.OK);\n\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t.isThrownBy(() -> this.converter.read(RelyingPartyRegistration.Builder.class, response))\n\t\t\t.withMessage(\"Unsupported element of type saml2:Assertion\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\npublic class RelyingPartyRegistrationTests {\n\n\t@Test\n\tpublic void withRelyingPartyRegistrationWorks() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.nameIdFormat(\"format\")\n\t\t\t.authnRequestsSigned(true)\n\t\t\t.assertingPartyMetadata((a) -> a.singleSignOnServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.assertingPartyMetadata((a) -> a.wantAuthnRequestsSigned(false))\n\t\t\t.assertingPartyMetadata((a) -> a.signingAlgorithms((algs) -> algs.add(\"alg\")))\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tRelyingPartyRegistration copy = registration.mutate().build();\n\t\tcompareRegistrations(registration, copy);\n\t}\n\n\t@Test\n\tvoid mutateWhenInvokedThenCreatesCopy() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.nameIdFormat(\"format\")\n\t\t\t.assertingPartyMetadata((a) -> a.singleSignOnServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.assertingPartyMetadata((a) -> a.wantAuthnRequestsSigned(false))\n\t\t\t.assertingPartyMetadata((a) -> a.signingAlgorithms((algs) -> algs.add(\"alg\")))\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tRelyingPartyRegistration copy = registration.mutate().build();\n\t\tcompareRegistrations(registration, copy);\n\t}\n\n\tprivate void compareRegistrations(RelyingPartyRegistration registration, RelyingPartyRegistration copy) {\n\t\tassertThat(copy.getRegistrationId()).isEqualTo(registration.getRegistrationId()).isEqualTo(\"simplesamlphp\");\n\t\tassertThat(copy.getAssertingPartyMetadata().getEntityId())\n\t\t\t.isEqualTo(registration.getAssertingPartyMetadata().getEntityId())\n\t\t\t.isEqualTo(\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\");\n\t\tassertThat(copy.getAssertionConsumerServiceLocation())\n\t\t\t.isEqualTo(registration.getAssertionConsumerServiceLocation())\n\t\t\t.isEqualTo(\"{baseUrl}\" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI);\n\t\tassertThat(copy.getSigningX509Credentials()).containsAll(registration.getSigningX509Credentials());\n\t\tassertThat(copy.getDecryptionX509Credentials()).containsAll(registration.getDecryptionX509Credentials());\n\t\tassertThat(copy.getEntityId()).isEqualTo(registration.getEntityId())\n\t\t\t.isEqualTo(copy.getEntityId())\n\t\t\t.isEqualTo(registration.getEntityId())\n\t\t\t.isEqualTo(\"{baseUrl}/saml2/service-provider-metadata/{registrationId}\");\n\t\tassertThat(copy.getAssertingPartyMetadata().getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(registration.getAssertingPartyMetadata().getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\");\n\t\tassertThat(copy.getAssertingPartyMetadata().getSingleSignOnServiceBinding())\n\t\t\t.isEqualTo(registration.getAssertingPartyMetadata().getSingleSignOnServiceBinding())\n\t\t\t.isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(copy.getAssertingPartyMetadata().getWantAuthnRequestsSigned())\n\t\t\t.isEqualTo(registration.getAssertingPartyMetadata().getWantAuthnRequestsSigned())\n\t\t\t.isFalse();\n\t\tassertThat(copy.getAssertionConsumerServiceBinding())\n\t\t\t.isEqualTo(registration.getAssertionConsumerServiceBinding());\n\t\tassertThat(copy.getDecryptionX509Credentials()).isEqualTo(registration.getDecryptionX509Credentials());\n\t\tassertThat(copy.getSigningX509Credentials()).isEqualTo(registration.getSigningX509Credentials());\n\t\tassertThat(copy.getAssertingPartyMetadata().getEncryptionX509Credentials())\n\t\t\t.isEqualTo(registration.getAssertingPartyMetadata().getEncryptionX509Credentials());\n\t\tassertThat(copy.getAssertingPartyMetadata().getVerificationX509Credentials())\n\t\t\t.isEqualTo(registration.getAssertingPartyMetadata().getVerificationX509Credentials());\n\t\tassertThat(copy.getAssertingPartyMetadata().getSigningAlgorithms())\n\t\t\t.isEqualTo(registration.getAssertingPartyMetadata().getSigningAlgorithms());\n\t\tassertThat(copy.getNameIdFormat()).isEqualTo(registration.getNameIdFormat());\n\t\tassertThat(copy.isAuthnRequestsSigned()).isEqualTo(registration.isAuthnRequestsSigned());\n\t}\n\n\t@Test\n\tpublic void buildWhenUsingDefaultsThenAssertionConsumerServiceBindingDefaultsToPost() {\n\t\tRelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId(\"id\")\n\t\t\t.entityId(\"entity-id\")\n\t\t\t.assertionConsumerServiceLocation(\"location\")\n\t\t\t.assertingPartyMetadata((assertingParty) -> assertingParty.entityId(\"entity-id\")\n\t\t\t\t.singleSignOnServiceLocation(\"location\")\n\t\t\t\t.verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))\n\t\t\t.build();\n\t\tassertThat(relyingPartyRegistration.getAssertionConsumerServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t}\n\n\t@Test\n\tpublic void buildPreservesCredentialsOrder() {\n\t\tSaml2X509Credential altRpCredential = TestSaml2X509Credentials.altPrivateCredential();\n\t\tSaml2X509Credential altApCredential = TestSaml2X509Credentials.altPublicCredential();\n\t\tSaml2X509Credential verifyingCredential = TestSaml2X509Credentials.relyingPartyVerifyingCredential();\n\t\tSaml2X509Credential encryptingCredential = TestSaml2X509Credentials.relyingPartyEncryptingCredential();\n\t\tSaml2X509Credential signingCredential = TestSaml2X509Credentials.relyingPartySigningCredential();\n\t\tSaml2X509Credential decryptionCredential = TestSaml2X509Credentials.relyingPartyDecryptingCredential();\n\n\t\t// Test with the alt credentials first\n\t\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()\n\t\t\t.assertingPartyMetadata((assertingParty) -> assertingParty.verificationX509Credentials((c) -> {\n\t\t\t\tc.add(altApCredential);\n\t\t\t\tc.add(verifyingCredential);\n\t\t\t}).encryptionX509Credentials((c) -> {\n\t\t\t\tc.add(altApCredential);\n\t\t\t\tc.add(encryptingCredential);\n\t\t\t}))\n\t\t\t.signingX509Credentials((c) -> {\n\t\t\t\tc.add(altRpCredential);\n\t\t\t\tc.add(signingCredential);\n\t\t\t})\n\t\t\t.decryptionX509Credentials((c) -> {\n\t\t\t\tc.add(altRpCredential);\n\t\t\t\tc.add(decryptionCredential);\n\t\t\t})\n\t\t\t.build();\n\t\tassertThat(relyingPartyRegistration.getSigningX509Credentials()).containsExactly(altRpCredential,\n\t\t\t\tsigningCredential);\n\t\tassertThat(relyingPartyRegistration.getDecryptionX509Credentials()).containsExactly(altRpCredential,\n\t\t\t\tdecryptionCredential);\n\t\tassertThat(relyingPartyRegistration.getAssertingPartyMetadata().getVerificationX509Credentials())\n\t\t\t.containsExactly(altApCredential, verifyingCredential);\n\t\tassertThat(relyingPartyRegistration.getAssertingPartyMetadata().getEncryptionX509Credentials())\n\t\t\t.containsExactly(altApCredential, encryptingCredential);\n\n\t\t// Test with the alt credentials last\n\t\trelyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()\n\t\t\t.assertingPartyMetadata((assertingParty) -> assertingParty.verificationX509Credentials((c) -> {\n\t\t\t\tc.add(verifyingCredential);\n\t\t\t\tc.add(altApCredential);\n\t\t\t}).encryptionX509Credentials((c) -> {\n\t\t\t\tc.add(encryptingCredential);\n\t\t\t\tc.add(altApCredential);\n\t\t\t}))\n\t\t\t.signingX509Credentials((c) -> {\n\t\t\t\tc.add(signingCredential);\n\t\t\t\tc.add(altRpCredential);\n\t\t\t})\n\t\t\t.decryptionX509Credentials((c) -> {\n\t\t\t\tc.add(decryptionCredential);\n\t\t\t\tc.add(altRpCredential);\n\t\t\t})\n\t\t\t.build();\n\t\tassertThat(relyingPartyRegistration.getSigningX509Credentials()).containsExactly(signingCredential,\n\t\t\t\taltRpCredential);\n\t\tassertThat(relyingPartyRegistration.getDecryptionX509Credentials()).containsExactly(decryptionCredential,\n\t\t\t\taltRpCredential);\n\t\tassertThat(relyingPartyRegistration.getAssertingPartyMetadata().getVerificationX509Credentials())\n\t\t\t.containsExactly(verifyingCredential, altApCredential);\n\t\tassertThat(relyingPartyRegistration.getAssertingPartyMetadata().getEncryptionX509Credentials())\n\t\t\t.containsExactly(encryptingCredential, altApCredential);\n\t}\n\n\t@Test\n\tvoid withAssertingPartyMetadataWhenMetadataThenBuilderCopies() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.nameIdFormat(\"format\")\n\t\t\t.assertingPartyMetadata((a) -> a.singleSignOnServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.assertingPartyMetadata((a) -> a.wantAuthnRequestsSigned(false))\n\t\t\t.assertingPartyMetadata((a) -> a.signingAlgorithms((algs) -> algs.add(\"alg\")))\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tRelyingPartyRegistration copied = RelyingPartyRegistration\n\t\t\t.withAssertingPartyMetadata(registration.getAssertingPartyMetadata())\n\t\t\t.registrationId(registration.getRegistrationId())\n\t\t\t.entityId(registration.getEntityId())\n\t\t\t.signingX509Credentials((c) -> c.addAll(registration.getSigningX509Credentials()))\n\t\t\t.decryptionX509Credentials((c) -> c.addAll(registration.getDecryptionX509Credentials()))\n\t\t\t.assertionConsumerServiceLocation(registration.getAssertionConsumerServiceLocation())\n\t\t\t.assertionConsumerServiceBinding(registration.getAssertionConsumerServiceBinding())\n\t\t\t.singleLogoutServiceLocation(registration.getSingleLogoutServiceLocation())\n\t\t\t.singleLogoutServiceResponseLocation(registration.getSingleLogoutServiceResponseLocation())\n\t\t\t.singleLogoutServiceBindings((c) -> c.addAll(registration.getSingleLogoutServiceBindings()))\n\t\t\t.nameIdFormat(registration.getNameIdFormat())\n\t\t\t.authnRequestsSigned(registration.isAuthnRequestsSigned())\n\t\t\t.build();\n\t\tcompareRegistrations(registration, copied);\n\t}\n\n\t@Test\n\tvoid withAssertingPartyMetadataWhenMetadataThenDisallowsDetails() {\n\t\tAssertingPartyMetadata metadata = new CustomAssertingPartyMetadata();\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> RelyingPartyRegistration.withAssertingPartyMetadata(metadata)\n\t\t\t\t.assertingPartyMetadata((a) -> a.entityId(\"entity-id\"))\n\t\t\t\t.build());\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> RelyingPartyRegistration.withAssertingPartyMetadata(metadata)\n\t\t\t\t.build()\n\t\t\t\t.getAssertingPartyMetadata());\n\t}\n\n\t@Test\n\tvoid withAssertingPartyMetadataWhenDetailsThenBuilderCopies() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.nameIdFormat(\"format\")\n\t\t\t.assertingPartyMetadata((a) -> a.singleSignOnServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.assertingPartyMetadata((a) -> a.wantAuthnRequestsSigned(false))\n\t\t\t.assertingPartyMetadata((a) -> a.signingAlgorithms((algs) -> algs.add(\"alg\")))\n\t\t\t.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\tRelyingPartyRegistration copied = RelyingPartyRegistration.withAssertingPartyMetadata(details)\n\t\t\t.assertingPartyMetadata((a) -> a.entityId(details.getEntityId()))\n\t\t\t.registrationId(registration.getRegistrationId())\n\t\t\t.entityId(registration.getEntityId())\n\t\t\t.signingX509Credentials((c) -> c.addAll(registration.getSigningX509Credentials()))\n\t\t\t.decryptionX509Credentials((c) -> c.addAll(registration.getDecryptionX509Credentials()))\n\t\t\t.assertionConsumerServiceLocation(registration.getAssertionConsumerServiceLocation())\n\t\t\t.assertionConsumerServiceBinding(registration.getAssertionConsumerServiceBinding())\n\t\t\t.singleLogoutServiceLocation(registration.getSingleLogoutServiceLocation())\n\t\t\t.singleLogoutServiceResponseLocation(registration.getSingleLogoutServiceResponseLocation())\n\t\t\t.singleLogoutServiceBindings((c) -> c.addAll(registration.getSingleLogoutServiceBindings()))\n\t\t\t.nameIdFormat(registration.getNameIdFormat())\n\t\t\t.authnRequestsSigned(registration.isAuthnRequestsSigned())\n\t\t\t.build();\n\t\tcompareRegistrations(registration, copied);\n\t}\n\n\tprivate static class CustomAssertingPartyMetadata implements AssertingPartyMetadata {\n\n\t\t@Override\n\t\tpublic String getEntityId() {\n\t\t\treturn \"\";\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean getWantAuthnRequestsSigned() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic List<String> getSigningAlgorithms() {\n\t\t\treturn List.of();\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2X509Credential> getVerificationX509Credentials() {\n\t\t\treturn List.of();\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<Saml2X509Credential> getEncryptionX509Credentials() {\n\t\t\treturn List.of();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getSingleSignOnServiceLocation() {\n\t\t\treturn \"\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Saml2MessageBinding getSingleSignOnServiceBinding() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getSingleLogoutServiceLocation() {\n\t\t\treturn \"\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String getSingleLogoutServiceResponseLocation() {\n\t\t\treturn \"\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Saml2MessageBinding getSingleLogoutServiceBinding() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder mutate() {\n\t\t\treturn new Builder();\n\t\t}\n\n\t\tprivate static class Builder implements AssertingPartyMetadata.Builder<Builder> {\n\n\t\t\t@Override\n\t\t\tpublic Builder entityId(String entityId) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder wantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder signingAlgorithms(Consumer<List<String>> signingMethodAlgorithmsConsumer) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder verificationX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder encryptionX509Credentials(Consumer<Collection<Saml2X509Credential>> credentialsConsumer) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder singleSignOnServiceLocation(String singleSignOnServiceLocation) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder singleSignOnServiceBinding(Saml2MessageBinding singleSignOnServiceBinding) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder singleLogoutServiceLocation(String singleLogoutServiceLocation) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder singleLogoutServiceResponseLocation(String singleLogoutServiceResponseLocation) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder singleLogoutServiceBinding(Saml2MessageBinding singleLogoutServiceBinding) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic AssertingPartyMetadata build() {\n\t\t\t\treturn new CustomAssertingPartyMetadata();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.security.saml2.Saml2Exception;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link RelyingPartyRegistration}\n */\npublic class RelyingPartyRegistrationsTests {\n\n\tprivate String metadata;\n\n\tprivate String entitiesDescriptor;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tClassPathResource resource = new ClassPathResource(\"test-metadata.xml\");\n\t\ttry (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {\n\t\t\tthis.metadata = reader.lines().collect(Collectors.joining());\n\t\t}\n\t\tresource = new ClassPathResource(\"test-entitiesdescriptor.xml\");\n\t\ttry (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {\n\t\t\tthis.entitiesDescriptor = reader.lines().collect(Collectors.joining());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void fromMetadataUrlLocationWhenResolvableThenPopulatesBuilder() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.enqueue(new MockResponse().setBody(this.metadata).setResponseCode(200));\n\t\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t\t\t\t.fromMetadataLocation(server.url(\"/\").toString())\n\t\t\t\t.entityId(\"rp\")\n\t\t\t\t.build();\n\t\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\t\tassertThat(details.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\t\tassertThat(details.getSingleSignOnServiceLocation())\n\t\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void fromMetadataUrlLocationWhenUnresolvableThenSaml2Exception() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.enqueue(new MockResponse().setBody(this.metadata).setResponseCode(200));\n\t\t\tString url = server.url(\"/\").toString();\n\t\t\tserver.shutdown();\n\t\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t\t.isThrownBy(() -> RelyingPartyRegistrations.fromMetadataLocation(url));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void fromMetadataUrlLocationWhenMalformedResponseThenSaml2Exception() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.enqueue(new MockResponse().setBody(\"malformed\").setResponseCode(200));\n\t\t\tString url = server.url(\"/\").toString();\n\t\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t\t.isThrownBy(() -> RelyingPartyRegistrations.fromMetadataLocation(url));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void fromMetadataFileLocationWhenResolvableThenPopulatesBuilder() {\n\t\tFile file = new File(\"src/test/resources/test-metadata.xml\");\n\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t\t\t.fromMetadataLocation(\"file:\" + file.getAbsolutePath())\n\t\t\t.entityId(\"rp\")\n\t\t\t.build();\n\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\tassertThat(details.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\tassertThat(details.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void fromMetadataFileLocationWhenNotFoundThenSaml2Exception() {\n\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t.isThrownBy(() -> RelyingPartyRegistrations.fromMetadataLocation(\"filePath\"));\n\t}\n\n\t@Test\n\tpublic void fromMetadataInputStreamWhenResolvableThenPopulatesBuilder() throws Exception {\n\t\ttry (InputStream source = new ByteArrayInputStream(this.metadata.getBytes())) {\n\t\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations.fromMetadata(source)\n\t\t\t\t.entityId(\"rp\")\n\t\t\t\t.build();\n\t\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\t\tassertThat(details.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\t\tassertThat(details.getSingleSignOnServiceLocation())\n\t\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void fromMetadataInputStreamWhenEmptyThenSaml2Exception() throws Exception {\n\t\ttry (InputStream source = new ByteArrayInputStream(\"\".getBytes())) {\n\t\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t\t.isThrownBy(() -> RelyingPartyRegistrations.fromMetadata(source));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataLocationWhenResolvableThenPopulatesBuilder() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.enqueue(new MockResponse().setBody(this.entitiesDescriptor).setResponseCode(200));\n\t\t\tList<RelyingPartyRegistration> registrations = RelyingPartyRegistrations\n\t\t\t\t.collectionFromMetadataLocation(server.url(\"/\").toString())\n\t\t\t\t.stream()\n\t\t\t\t.map((r) -> r.entityId(\"rp\").build())\n\t\t\t\t.collect(Collectors.toList());\n\t\t\tassertThat(registrations).hasSize(2);\n\t\t\tRelyingPartyRegistration first = registrations.get(0);\n\t\t\tAssertingPartyMetadata details = first.getAssertingPartyMetadata();\n\t\t\tassertThat(details.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\t\tassertThat(details.getSingleSignOnServiceLocation())\n\t\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t\t\tRelyingPartyRegistration second = registrations.get(1);\n\t\t\tdetails = second.getAssertingPartyMetadata();\n\t\t\tassertThat(details.getEntityId()).isEqualTo(\"https://ap.example.org/idp/shibboleth\");\n\t\t\tassertThat(details.getSingleSignOnServiceLocation())\n\t\t\t\t.isEqualTo(\"https://ap.example.org/idp/profile/SAML2/POST/SSO\");\n\t\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataLocationWhenUnresolvableThenSaml2Exception() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.enqueue(new MockResponse().setBody(this.metadata).setResponseCode(200));\n\t\t\tString url = server.url(\"/\").toString();\n\t\t\tserver.shutdown();\n\t\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t\t.isThrownBy(() -> RelyingPartyRegistrations.collectionFromMetadataLocation(url));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataLocationWhenMalformedResponseThenSaml2Exception() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.enqueue(new MockResponse().setBody(\"malformed\").setResponseCode(200));\n\t\t\tString url = server.url(\"/\").toString();\n\t\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t\t.isThrownBy(() -> RelyingPartyRegistrations.collectionFromMetadataLocation(url));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataFileWhenResolvableThenPopulatesBuilder() {\n\t\tFile file = new File(\"src/test/resources/test-entitiesdescriptor.xml\");\n\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t\t\t.collectionFromMetadataLocation(\"file:\" + file.getAbsolutePath())\n\t\t\t.stream()\n\t\t\t.map((r) -> r.entityId(\"rp\").build())\n\t\t\t.findFirst()\n\t\t\t.get();\n\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\tassertThat(details.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\tassertThat(details.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataFileWhenContainsOnlyEntityDescriptorThenPopulatesBuilder() {\n\t\tFile file = new File(\"src/test/resources/test-metadata.xml\");\n\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t\t\t.collectionFromMetadataLocation(\"file:\" + file.getAbsolutePath())\n\t\t\t.stream()\n\t\t\t.map((r) -> r.entityId(\"rp\").build())\n\t\t\t.findFirst()\n\t\t\t.get();\n\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\tassertThat(details.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\tassertThat(details.getSingleSignOnServiceLocation())\n\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataFileWhenNotFoundThenSaml2Exception() {\n\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t.isThrownBy(() -> RelyingPartyRegistrations.collectionFromMetadataLocation(\"filePath\"));\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataInputStreamWhenResolvableThenPopulatesBuilder() throws Exception {\n\t\ttry (InputStream source = new ByteArrayInputStream(this.entitiesDescriptor.getBytes())) {\n\t\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations.collectionFromMetadata(source)\n\t\t\t\t.stream()\n\t\t\t\t.map((r) -> r.entityId(\"rp\").build())\n\t\t\t\t.findFirst()\n\t\t\t\t.get();\n\t\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\t\tassertThat(details.getEntityId()).isEqualTo(\"https://idp.example.com/idp/shibboleth\");\n\t\t\tassertThat(details.getSingleSignOnServiceLocation())\n\t\t\t\t.isEqualTo(\"https://idp.example.com/idp/profile/SAML2/POST/SSO\");\n\t\t\tassertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.POST);\n\t\t\tassertThat(details.getVerificationX509Credentials()).hasSize(1);\n\t\t\tassertThat(details.getEncryptionX509Credentials()).hasSize(1);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void fromMetadataLocationWhenResolvableThenUsesEntityIdAndOpenSamlAssertingPartyDetails() throws Exception {\n\t\ttry (MockWebServer server = new MockWebServer()) {\n\t\t\tserver.enqueue(new MockResponse().setBody(this.metadata).setResponseCode(200));\n\t\t\tRelyingPartyRegistration registration = RelyingPartyRegistrations\n\t\t\t\t.fromMetadataLocation(server.url(\"/\").toString())\n\t\t\t\t.entityId(\"rp\")\n\t\t\t\t.build();\n\t\t\tAssertingPartyMetadata details = registration.getAssertingPartyMetadata();\n\t\t\tassertThat(registration.getRegistrationId()).isEqualTo(details.getEntityId());\n\t\t\tassertThat(details).isInstanceOf(OpenSamlAssertingPartyDetails.class);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataInputStreamWhenEmptyThenSaml2Exception() throws Exception {\n\t\ttry (InputStream source = new ByteArrayInputStream(\"\".getBytes())) {\n\t\t\tassertThatExceptionOfType(Saml2Exception.class)\n\t\t\t\t.isThrownBy(() -> RelyingPartyRegistrations.collectionFromMetadata(source));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataLocationCanHandleFederationMetadata() {\n\t\tCollection<RelyingPartyRegistration.Builder> federationMetadataWithSkippedSPEntries = RelyingPartyRegistrations\n\t\t\t.collectionFromMetadataLocation(\"classpath:test-federated-metadata.xml\");\n\t\tassertThat(federationMetadataWithSkippedSPEntries).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void collectionFromMetadataLocationWithoutIdpThenSaml2Exception() {\n\t\tassertThatExceptionOfType(Saml2Exception.class).isThrownBy(() -> RelyingPartyRegistrations\n\t\t\t.collectionFromMetadataLocation(\"classpath:test-metadata-without-idp.xml\"));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/TestRelyingPartyRegistrations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.registration;\n\nimport org.springframework.security.saml2.core.Saml2X509Credential;\nimport org.springframework.security.saml2.credentials.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;\n\n/**\n * Preconfigured test data for {@link RelyingPartyRegistration} objects\n */\npublic final class TestRelyingPartyRegistrations {\n\n\tprivate TestRelyingPartyRegistrations() {\n\t}\n\n\tpublic static RelyingPartyRegistration.Builder relyingPartyRegistration() {\n\t\tString registrationId = \"simplesamlphp\";\n\t\tString rpEntityId = \"{baseUrl}/saml2/service-provider-metadata/{registrationId}\";\n\t\tSaml2X509Credential signingCredential = TestSaml2X509Credentials.relyingPartySigningCredential();\n\t\tString assertionConsumerServiceLocation = \"{baseUrl}\"\n\t\t\t\t+ Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;\n\t\tString apEntityId = \"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php\";\n\t\tSaml2X509Credential verificationCertificate = TestSaml2X509Credentials.relyingPartyVerifyingCredential();\n\t\tString singleSignOnServiceLocation = \"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php\";\n\t\tString singleLogoutServiceLocation = \"{baseUrl}/logout/saml2/slo\";\n\t\treturn RelyingPartyRegistration.withRegistrationId(registrationId)\n\t\t\t.entityId(rpEntityId)\n\t\t\t.nameIdFormat(\"format\")\n\t\t\t.assertionConsumerServiceLocation(assertionConsumerServiceLocation)\n\t\t\t.singleLogoutServiceLocation(singleLogoutServiceLocation)\n\t\t\t.signingX509Credentials((c) -> c.add(signingCredential))\n\t\t\t.assertingPartyMetadata((a) -> a.entityId(apEntityId)\n\t\t\t\t.singleSignOnServiceLocation(singleSignOnServiceLocation)\n\t\t\t\t.verificationX509Credentials((c) -> c.add(verificationCertificate)));\n\t}\n\n\tpublic static RelyingPartyRegistration.Builder noCredentials() {\n\t\treturn RelyingPartyRegistration.withRegistrationId(\"registration-id\")\n\t\t\t.entityId(\"rp-entity-id\")\n\t\t\t.singleLogoutServiceLocation(\"https://rp.example.org/logout/saml2/request\")\n\t\t\t.singleLogoutServiceResponseLocation(\"https://rp.example.org/logout/saml2/response\")\n\t\t\t.assertionConsumerServiceLocation(\"https://rp.example.org/acs\")\n\t\t\t.assertingPartyMetadata((party) -> party.entityId(\"ap-entity-id\")\n\t\t\t\t.singleSignOnServiceLocation(\"https://ap.example.org/sso\")\n\t\t\t\t.singleLogoutServiceLocation(\"https://ap.example.org/logout/saml2/request\")\n\t\t\t\t.singleLogoutServiceResponseLocation(\"https://ap.example.org/logout/saml2/response\"));\n\t}\n\n\tpublic static RelyingPartyRegistration.Builder full() {\n\t\treturn noCredentials()\n\t\t\t.signingX509Credentials((c) -> c\n\t\t\t\t.add(org.springframework.security.saml2.core.TestSaml2X509Credentials.relyingPartySigningCredential()))\n\t\t\t.decryptionX509Credentials((c) -> c.add(org.springframework.security.saml2.core.TestSaml2X509Credentials\n\t\t\t\t.relyingPartyDecryptingCredential()))\n\t\t\t.assertingPartyMetadata((party) -> party.verificationX509Credentials(\n\t\t\t\t\t(c) -> c.add(org.springframework.security.saml2.core.TestSaml2X509Credentials\n\t\t\t\t\t\t.relyingPartyVerifyingCredential())));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/HttpSessionSaml2AuthenticationRequestRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.servlet;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Marcus Da Coregio\n */\npublic class HttpSessionSaml2AuthenticationRequestRepositoryTests {\n\n\tprivate static final String IDP_SSO_URL = \"https://sso-url.example.com/IDP/SSO\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate HttpSessionSaml2AuthenticationRequestRepository authenticationRequestRepository;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();\n\t}\n\n\t@Test\n\tpublic void loadAuthenticationRequestWhenInvalidSessionThenNull() {\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.loadAuthenticationRequest(this.request);\n\t\tassertThat(authenticationRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthenticationRequestWhenNoAttributeInSessionThenNull() {\n\t\tthis.request.getSession();\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.loadAuthenticationRequest(this.request);\n\t\tassertThat(authenticationRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void loadAuthenticationRequestWhenAttributeInSessionThenReturnsAuthenticationRequest() {\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mock(AbstractSaml2AuthenticationRequest.class);\n\t\tgiven(mockAuthenticationRequest.getAuthenticationRequestUri()).willReturn(IDP_SSO_URL);\n\t\tthis.request.getSession();\n\t\tthis.authenticationRequestRepository.saveAuthenticationRequest(mockAuthenticationRequest, this.request,\n\t\t\t\tthis.response);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.loadAuthenticationRequest(this.request);\n\t\tassertThat(authenticationRequest.getAuthenticationRequestUri()).isEqualTo(IDP_SSO_URL);\n\t}\n\n\t@Test\n\tpublic void saveAuthenticationRequestWhenSessionDontExistsThenCreateAndSave() {\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mock(AbstractSaml2AuthenticationRequest.class);\n\t\tthis.authenticationRequestRepository.saveAuthenticationRequest(mockAuthenticationRequest, this.request,\n\t\t\t\tthis.response);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.loadAuthenticationRequest(this.request);\n\t\tassertThat(authenticationRequest).isNotNull();\n\t}\n\n\t@Test\n\tpublic void saveAuthenticationRequestWhenSessionExistsThenSave() {\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mock(AbstractSaml2AuthenticationRequest.class);\n\t\tthis.request.getSession();\n\t\tthis.authenticationRequestRepository.saveAuthenticationRequest(mockAuthenticationRequest, this.request,\n\t\t\t\tthis.response);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.loadAuthenticationRequest(this.request);\n\t\tassertThat(authenticationRequest).isNotNull();\n\t}\n\n\t@Test\n\tpublic void saveAuthenticationRequestWhenNullAuthenticationRequestThenDontSave() {\n\t\tthis.request.getSession();\n\t\tthis.authenticationRequestRepository.saveAuthenticationRequest(null, this.request, this.response);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.loadAuthenticationRequest(this.request);\n\t\tassertThat(authenticationRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthenticationRequestWhenInvalidSessionThenReturnNull() {\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.removeAuthenticationRequest(this.request, this.response);\n\t\tassertThat(authenticationRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthenticationRequestWhenAttributeInSessionThenRemoveAuthenticationRequest() {\n\t\tAbstractSaml2AuthenticationRequest mockAuthenticationRequest = mock(AbstractSaml2AuthenticationRequest.class);\n\t\tgiven(mockAuthenticationRequest.getAuthenticationRequestUri()).willReturn(IDP_SSO_URL);\n\t\tthis.request.getSession();\n\t\tthis.authenticationRequestRepository.saveAuthenticationRequest(mockAuthenticationRequest, this.request,\n\t\t\t\tthis.response);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.removeAuthenticationRequest(this.request, this.response);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequestAfterRemove = this.authenticationRequestRepository\n\t\t\t.loadAuthenticationRequest(this.request);\n\t\tassertThat(authenticationRequest.getAuthenticationRequestUri()).isEqualTo(IDP_SSO_URL);\n\t\tassertThat(authenticationRequestAfterRemove).isNull();\n\t}\n\n\t@Test\n\tpublic void removeAuthenticationRequestWhenValidSessionNoAttributeThenReturnsNull() {\n\t\tMockHttpSession session = mock(MockHttpSession.class);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(session);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestRepository\n\t\t\t.removeAuthenticationRequest(request, this.response);\n\t\tverify(session).getAttribute(anyString());\n\t\tassertThat(authenticationRequest).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/CacheSaml2AuthenticationRequestRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.cache.Cache;\nimport org.springframework.cache.concurrent.ConcurrentMapCache;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.TestSaml2PostAuthenticationRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link CacheSaml2AuthenticationRequestRepository}\n */\nclass CacheSaml2AuthenticationRequestRepositoryTests {\n\n\tCacheSaml2AuthenticationRequestRepository repository = new CacheSaml2AuthenticationRequestRepository();\n\n\t@Test\n\tvoid loadAuthenticationRequestWhenCachedThenReturns() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.RELAY_STATE, \"test\");\n\t\tSaml2PostAuthenticationRequest authenticationRequest = TestSaml2PostAuthenticationRequests.create();\n\t\tthis.repository.saveAuthenticationRequest(authenticationRequest, request, null);\n\t\tassertThat(this.repository.loadAuthenticationRequest(request)).isEqualTo(authenticationRequest);\n\t\tthis.repository.removeAuthenticationRequest(request, null);\n\t\tassertThat(this.repository.loadAuthenticationRequest(request)).isNull();\n\t}\n\n\t@Test\n\tvoid loadAuthenticationRequestWhenNoRelayStateThenException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.repository.loadAuthenticationRequest(request));\n\t}\n\n\t@Test\n\tvoid saveAuthenticationRequestWhenNoRelayStateThenException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.repository.saveAuthenticationRequest(null, request, null));\n\t}\n\n\t@Test\n\tvoid removeAuthenticationRequestWhenNoRelayStateThenException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.repository.removeAuthenticationRequest(request, null));\n\t}\n\n\t@Test\n\tvoid repositoryWhenCustomCacheThenUses() {\n\t\tCacheSaml2AuthenticationRequestRepository repository = new CacheSaml2AuthenticationRequestRepository();\n\t\tCache cache = spy(new ConcurrentMapCache(\"requests\"));\n\t\trepository.setCache(cache);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.RELAY_STATE, \"test\");\n\t\tSaml2PostAuthenticationRequest authenticationRequest = TestSaml2PostAuthenticationRequests.create();\n\t\trepository.saveAuthenticationRequest(authenticationRequest, request, null);\n\t\tverify(cache).put(eq(\"test\"), any());\n\t\trepository.loadAuthenticationRequest(request);\n\t\tverify(cache).get(\"test\", AbstractSaml2AuthenticationRequest.class);\n\t\trepository.removeAuthenticationRequest(request, null);\n\t\tverify(cache).evict(\"test\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link DefaultRelyingPartyRegistrationResolver}\n */\npublic class DefaultRelyingPartyRegistrationResolverTests {\n\n\tprivate final RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t.build();\n\n\tprivate final RelyingPartyRegistrationRepository repository = new InMemoryRelyingPartyRegistrationRepository(\n\t\t\tthis.registration);\n\n\tprivate final DefaultRelyingPartyRegistrationResolver resolver = new DefaultRelyingPartyRegistrationResolver(\n\t\t\tthis.repository);\n\n\t@Test\n\tpublic void resolveWhenRequestContainsRegistrationIdThenResolves() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\",\n\t\t\t\t\"/some/path/\" + this.registration.getRegistrationId());\n\t\trequest.setPathInfo(\"/some/path/\" + this.registration.getRegistrationId());\n\t\tRelyingPartyRegistration registration = this.resolver.convert(request);\n\t\tassertThat(registration).isNotNull();\n\t\tassertThat(registration.getRegistrationId()).isEqualTo(this.registration.getRegistrationId());\n\t\tassertThat(registration.getEntityId())\n\t\t\t.isEqualTo(\"http://localhost/saml2/service-provider-metadata/\" + this.registration.getRegistrationId());\n\t\tassertThat(registration.getAssertionConsumerServiceLocation())\n\t\t\t.isEqualTo(\"http://localhost/login/saml2/sso/\" + this.registration.getRegistrationId());\n\t\tassertThat(registration.getSingleLogoutServiceLocation()).isEqualTo(\"http://localhost/logout/saml2/slo\");\n\t\tassertThat(registration.getSingleLogoutServiceResponseLocation())\n\t\t\t.isEqualTo(\"http://localhost/logout/saml2/slo\");\n\t}\n\n\t@Test\n\tpublic void resolveWhenRequestContainsInvalidRegistrationIdThenNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setPathInfo(\"/some/path/not-\" + this.registration.getRegistrationId());\n\t\tRelyingPartyRegistration registration = this.resolver.convert(request);\n\t\tassertThat(registration).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveWhenRequestIsMissingRegistrationIdThenNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tRelyingPartyRegistration registration = this.resolver.convert(request);\n\t\tassertThat(registration).isNull();\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullRelyingPartyRegistrationThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DefaultRelyingPartyRegistrationResolver(null));\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/RelyingPartyRegistrationPlaceholderResolversTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationPlaceholderResolvers.UriResolver;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link RelyingPartyRegistrationPlaceholderResolvers}\n */\npublic class RelyingPartyRegistrationPlaceholderResolversTests {\n\n\t@Test\n\tvoid uriResolverGivenRequestCreatesResolver() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tUriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request);\n\t\tString resolved = uriResolver.resolve(\"{baseUrl}/extension\");\n\t\tassertThat(resolved).isEqualTo(\"http://localhost/extension\");\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> uriResolver.resolve(\"{baseUrl}/extension/{registrationId}\"));\n\t}\n\n\t@Test\n\tvoid uriResolverGivenRequestAndRegistrationCreatesResolver() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t\t.entityId(\"http://sp.example.org\")\n\t\t\t.build();\n\t\tUriResolver uriResolver = RelyingPartyRegistrationPlaceholderResolvers.uriResolver(request, registration);\n\t\tString resolved = uriResolver.resolve(\"{baseUrl}/extension/{registrationId}\");\n\t\tassertThat(resolved).isEqualTo(\"http://localhost/extension/simplesamlphp\");\n\t\tresolved = uriResolver.resolve(\"{relyingPartyEntityId}/extension\");\n\t\tassertThat(resolved).isEqualTo(\"http://sp.example.org/extension\");\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.core.Saml2Utils;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.util.StreamUtils;\nimport org.springframework.web.util.UriUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isNull;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class Saml2AuthenticationTokenConverterTests {\n\n\t@Mock\n\tRelyingPartyRegistrationResolver relyingPartyRegistrationResolver;\n\n\tRelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.relyingPartyRegistration()\n\t\t.build();\n\n\t@Test\n\tpublic void convertWhenSamlResponseThenToken() {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))\n\t\t\t.willReturn(this.relyingPartyRegistration);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE,\n\t\t\t\tSaml2Utils.samlEncode(\"response\".getBytes(StandardCharsets.UTF_8)));\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(\"response\");\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.relyingPartyRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void convertWhenSamlResponseWithRelyingPartyRegistrationResolver(\n\t\t\t@Mock RelyingPartyRegistrationResolver resolver) {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(resolver);\n\t\tgiven(resolver.resolve(any(HttpServletRequest.class), any())).willReturn(this.relyingPartyRegistration);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE,\n\t\t\t\tSaml2Utils.samlEncode(\"response\".getBytes(StandardCharsets.UTF_8)));\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(\"response\");\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.relyingPartyRegistration.getRegistrationId());\n\t\tverify(resolver).resolve(any(), isNull());\n\t}\n\n\t@Test\n\tpublic void convertWhenSamlResponseInvalidBase64ThenSaml2AuthenticationException() {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))\n\t\t\t.willReturn(this.relyingPartyRegistration);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, \"invalid\");\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> converter.convert(request))\n\t\t\t.withCauseInstanceOf(IllegalArgumentException.class)\n\t\t\t.satisfies(\n\t\t\t\t\t(ex) -> assertThat(ex.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE))\n\t\t\t.satisfies(\n\t\t\t\t\t(ex) -> assertThat(ex.getSaml2Error().getDescription()).isEqualTo(\"Failed to decode SAMLResponse\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenNoSamlResponseThenNull() {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))\n\t\t\t.willReturn(this.relyingPartyRegistration);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThat(converter.convert(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenNoRelyingPartyRegistrationThenNull() {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any())).willReturn(null);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThat(converter.convert(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void convertWhenGetRequestThenInflates() {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))\n\t\t\t.willReturn(this.relyingPartyRegistration);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\tbyte[] deflated = Saml2Utils.samlDeflate(\"response\");\n\t\tString encoded = Saml2Utils.samlEncode(deflated);\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, encoded);\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(\"response\");\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.relyingPartyRegistration.getRegistrationId());\n\t}\n\n\t@Test\n\tpublic void convertWhenGetRequestInvalidDeflatedThenSaml2AuthenticationException() {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))\n\t\t\t.willReturn(this.relyingPartyRegistration);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\tbyte[] invalidDeflated = \"invalid\".getBytes();\n\t\tString encoded = Saml2Utils.samlEncode(invalidDeflated);\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, encoded);\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> converter.convert(request))\n\t\t\t.withRootCauseInstanceOf(IOException.class)\n\t\t\t.satisfies(\n\t\t\t\t\t(ex) -> assertThat(ex.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE))\n\t\t\t.satisfies((ex) -> assertThat(ex.getSaml2Error().getDescription()).isEqualTo(\"Unable to inflate string\"));\n\t}\n\n\t@Test\n\tpublic void convertWhenUsingSamlUtilsBase64ThenXmlIsValid() throws Exception {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))\n\t\t\t.willReturn(this.relyingPartyRegistration);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, getSsoCircleEncodedXml());\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tvalidateSsoCircleXml(token.getSaml2Response());\n\t}\n\n\t@Test\n\tpublic void convertWhenSavedAuthenticationRequestThenToken() {\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = mock(\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = mock(AbstractSaml2AuthenticationRequest.class);\n\t\tgiven(authenticationRequest.getRelyingPartyRegistrationId())\n\t\t\t.willReturn(this.relyingPartyRegistration.getRegistrationId());\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tconverter.setAuthenticationRequestRepository(authenticationRequestRepository);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))\n\t\t\t.willReturn(this.relyingPartyRegistration);\n\t\tgiven(authenticationRequestRepository.loadAuthenticationRequest(any(HttpServletRequest.class)))\n\t\t\t.willReturn(authenticationRequest);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE,\n\t\t\t\tSaml2Utils.samlEncode(\"response\".getBytes(StandardCharsets.UTF_8)));\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(\"response\");\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.relyingPartyRegistration.getRegistrationId());\n\t\tassertThat(token.getAuthenticationRequest()).isEqualTo(authenticationRequest);\n\t}\n\n\t@Test\n\tpublic void convertWhenSavedAuthenticationRequestThenTokenWithRelyingPartyRegistrationResolver(\n\t\t\t@Mock RelyingPartyRegistrationResolver resolver) {\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = mock(\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\t\tAbstractSaml2AuthenticationRequest authenticationRequest = mock(AbstractSaml2AuthenticationRequest.class);\n\t\tgiven(authenticationRequest.getRelyingPartyRegistrationId())\n\t\t\t.willReturn(this.relyingPartyRegistration.getRegistrationId());\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(resolver);\n\t\tconverter.setAuthenticationRequestRepository(authenticationRequestRepository);\n\t\tgiven(resolver.resolve(any(HttpServletRequest.class), any())).willReturn(this.relyingPartyRegistration);\n\t\tgiven(authenticationRequestRepository.loadAuthenticationRequest(any(HttpServletRequest.class)))\n\t\t\t.willReturn(authenticationRequest);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE,\n\t\t\t\tSaml2Utils.samlEncode(\"response\".getBytes(StandardCharsets.UTF_8)));\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token.getSaml2Response()).isEqualTo(\"response\");\n\t\tassertThat(token.getRelyingPartyRegistration().getRegistrationId())\n\t\t\t.isEqualTo(this.relyingPartyRegistration.getRegistrationId());\n\t\tassertThat(token.getAuthenticationRequest()).isEqualTo(authenticationRequest);\n\t\tverify(resolver).resolve(any(), eq(this.relyingPartyRegistration.getRegistrationId()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenResolverIsNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Saml2AuthenticationTokenConverter(null));\n\t}\n\n\t@Test\n\tpublic void setAuthenticationRequestRepositoryWhenNullThenIllegalArgument() {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> converter.setAuthenticationRequestRepository(null));\n\t}\n\n\t@Test\n\tpublic void shouldNotConvertGetRequests() {\n\t\tSaml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter(\n\t\t\t\tthis.relyingPartyRegistrationResolver);\n\t\tconverter.setShouldConvertGetRequests(false);\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(HttpServletRequest.class), any()))\n\t\t\t.willReturn(this.relyingPartyRegistration);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"GET\");\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE,\n\t\t\t\tSaml2Utils.samlEncode(\"response\".getBytes(StandardCharsets.UTF_8)));\n\t\tSaml2AuthenticationToken token = converter.convert(request);\n\t\tassertThat(token).isNull();\n\t}\n\n\tprivate void validateSsoCircleXml(String xml) {\n\t\tassertThat(xml).contains(\"InResponseTo=\\\"ARQ9a73ead-7dcf-45a8-89eb-26f3c9900c36\\\"\")\n\t\t\t.contains(\" ID=\\\"s246d157446618e90e43fb79bdd4d9e9e19cf2c7c4\\\"\")\n\t\t\t.contains(\"<saml:Issuer>https://idp.ssocircle.com</saml:Issuer>\");\n\t}\n\n\tprivate String getSsoCircleEncodedXml() throws IOException {\n\t\tClassPathResource resource = new ClassPathResource(\"saml2-response-sso-circle.encoded\");\n\t\tString response = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);\n\t\treturn UriUtils.decode(response, StandardCharsets.UTF_8);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.saml2.core.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResolver;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse;\nimport org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests for {@link Saml2MetadataFilter}\n */\npublic class Saml2MetadataFilterTests {\n\n\tRelyingPartyRegistrationRepository repository;\n\n\tSaml2MetadataResolver resolver;\n\n\tSaml2MetadataFilter filter;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tFilterChain chain;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.repository = mock(RelyingPartyRegistrationRepository.class);\n\t\tthis.resolver = mock(Saml2MetadataResolver.class);\n\t\tthis.filter = new Saml2MetadataFilter(this.repository, this.resolver);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = mock(FilterChain.class);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMatcherSucceedsThenResolverInvoked() throws Exception {\n\t\tMockHttpServletRequest request = uri(\"/saml2/service-provider-metadata/registration-id\");\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tverifyNoInteractions(this.chain);\n\t\tverify(this.repository).findByRegistrationId(\"registration-id\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMatcherFailsThenProcessesFilterChain() throws Exception {\n\t\tMockHttpServletRequest request = uri(\"/saml2/authenticate/registration-id\");\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tverify(this.chain).doFilter(request, this.response);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoRelyingPartyRegistrationThenUnauthorized() throws Exception {\n\t\tMockHttpServletRequest request = uri(\"/saml2/service-provider-metadata/invalidRegistration\");\n\t\tgiven(this.repository.findByRegistrationId(\"invalidRegistration\")).willReturn(null);\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tverifyNoInteractions(this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRelyingPartyRegistrationFoundThenInvokesMetadataResolver() throws Exception {\n\t\tMockHttpServletRequest request = uri(\"/saml2/service-provider-metadata/validRegistration\");\n\t\tRelyingPartyRegistration validRegistration = TestRelyingPartyRegistrations.noCredentials()\n\t\t\t.assertingPartyMetadata((party) -> party\n\t\t\t\t.verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))\n\t\t\t.build();\n\t\tString generatedMetadata = \"<xml>test</xml>\";\n\t\tgiven(this.resolver.resolve(validRegistration)).willReturn(generatedMetadata);\n\t\tthis.filter = new Saml2MetadataFilter((r, registrationId) -> validRegistration, this.resolver);\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tverifyNoInteractions(this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(200);\n\t\tassertThat(this.response.getContentAsString()).isEqualTo(generatedMetadata);\n\t\tverify(this.resolver).resolve(validRegistration);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMatchesThenRespondsWithMetadata() throws Exception {\n\t\tSaml2MetadataResponse metadata = new Saml2MetadataResponse(\"<xml/>\", \"metadata.xml\");\n\t\tSaml2MetadataResponseResolver resolver = mock(Saml2MetadataResponseResolver.class);\n\t\tgiven(resolver.resolve(this.request)).willReturn(metadata);\n\t\tSaml2MetadataFilter filter = new Saml2MetadataFilter(resolver);\n\t\tfilter.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.response.getContentType()).isEqualTo(\"application/samlmetadata+xml;charset=UTF-8\");\n\t\tassertThat(this.response.getContentAsString()).isEqualTo(\"<xml/>\");\n\t\tassertThat(this.response.getHeaderValue(HttpHeaders.CONTENT_DISPOSITION)).asString()\n\t\t\t.isEqualTo(\"attachment; filename=\\\"metadata.xml\\\"; filename*=UTF-8''metadata.xml\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomRequestMatcherThenUses() throws Exception {\n\t\tMockHttpServletRequest request = uri(\"/path\");\n\t\tthis.filter.setRequestMatcher(pathPattern(\"/path\"));\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tverifyNoInteractions(this.chain);\n\t\tverify(this.repository).findByRegistrationId(\"path\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSetMetadataFilenameThenUses() throws Exception {\n\t\tRelyingPartyRegistration validRegistration = TestRelyingPartyRegistrations.full().build();\n\t\tString testMetadataFilename = \"test-{registrationId}-metadata.xml\";\n\t\tString fileName = testMetadataFilename.replace(\"{registrationId}\", validRegistration.getRegistrationId());\n\t\tString encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());\n\t\tString generatedMetadata = \"<xml>test</xml>\";\n\t\tMockHttpServletRequest request = uri(\"/saml2/service-provider-metadata/registration-id\");\n\t\tgiven(this.resolver.resolve(validRegistration)).willReturn(generatedMetadata);\n\t\tthis.filter = new Saml2MetadataFilter((r, registrationId) -> validRegistration, this.resolver);\n\t\tthis.filter.setMetadataFilename(testMetadataFilename);\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getHeaderValue(HttpHeaders.CONTENT_DISPOSITION)).asString()\n\t\t\t.isEqualTo(\"attachment; filename=\\\"%s\\\"; filename*=UTF-8''%s\", fileName, encodedFileName);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenResolverConstructorAndPathStartsWithRegistrationIdThenServesMetadata() throws Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tgiven(this.repository.findByRegistrationId(\"registration-id\")).willReturn(registration);\n\t\tgiven(this.resolver.resolve(any(RelyingPartyRegistration.class))).willReturn(\"metadata\");\n\t\tRelyingPartyRegistrationResolver resolver = new DefaultRelyingPartyRegistrationResolver(\n\t\t\t\t(id) -> this.repository.findByRegistrationId(\"registration-id\"));\n\t\tthis.filter = new Saml2MetadataFilter(resolver, this.resolver);\n\t\tthis.filter.setRequestMatcher(pathPattern(\"/metadata\"));\n\t\tMockHttpServletRequest request = uri(\"/metadata\");\n\t\tthis.filter.doFilter(request, this.response, new MockFilterChain());\n\t\tverify(this.repository).findByRegistrationId(\"registration-id\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRelyingPartyRegistrationRepositoryConstructorAndPathStartsWithRegistrationIdThenServesMetadata()\n\t\t\tthrows Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tgiven(this.repository.findByRegistrationId(\"registration-id\")).willReturn(registration);\n\t\tgiven(this.resolver.resolve(any(RelyingPartyRegistration.class))).willReturn(\"metadata\");\n\t\tthis.filter = new Saml2MetadataFilter((id) -> this.repository.findByRegistrationId(\"registration-id\"),\n\t\t\t\tthis.resolver);\n\t\tthis.filter.setRequestMatcher(pathPattern(\"/metadata\"));\n\t\tMockHttpServletRequest request = uri(\"/metadata\");\n\t\tthis.filter.doFilter(request, this.response, new MockFilterChain());\n\t\tverify(this.repository).findByRegistrationId(\"registration-id\");\n\t}\n\n\t// gh-12026\n\t@Test\n\tpublic void doFilterWhenCharacterEncodingThenEncodeSpecialCharactersCorrectly() throws Exception {\n\t\tRelyingPartyRegistration validRegistration = TestRelyingPartyRegistrations.full().build();\n\t\tString testMetadataFilename = \"test-{registrationId}-metadata.xml\";\n\t\tString generatedMetadata = \"<xml>testäöü</xml>\";\n\t\tMockHttpServletRequest request = uri(\"/saml2/service-provider-metadata/registration-id\");\n\t\tgiven(this.resolver.resolve(validRegistration)).willReturn(generatedMetadata);\n\t\tthis.filter = new Saml2MetadataFilter((req, id) -> validRegistration, this.resolver);\n\t\tthis.filter.setMetadataFilename(testMetadataFilename);\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getCharacterEncoding()).isEqualTo(StandardCharsets.UTF_8.name());\n\t\tassertThat(this.response.getContentAsString(StandardCharsets.UTF_8)).isEqualTo(generatedMetadata);\n\t\tassertThat(this.response.getContentLength())\n\t\t\t.isEqualTo(generatedMetadata.getBytes(StandardCharsets.UTF_8).length);\n\t}\n\n\t@Test\n\tpublic void setRequestMatcherWhenNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestMatcher(null));\n\t}\n\n\t@Test\n\tpublic void setMetadataFilenameWhenEmptyThenThrowsException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.filter.setMetadataFilename(\" \"))\n\t\t\t.withMessage(\"metadataFilename cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void setMetadataFilenameWhenMissingRegistrationIdVariableThenThrowsException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.filter.setMetadataFilename(\"metadata-filename.xml\"))\n\t\t\t.withMessage(\"metadataFilename must contain a {registrationId} match variable\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenRelyingPartyRegistrationRepositoryThenUses() throws Exception {\n\t\tRelyingPartyRegistrationRepository repository = mock(RelyingPartyRegistrationRepository.class);\n\t\tthis.filter = new Saml2MetadataFilter(repository, this.resolver);\n\t\tMockHttpServletRequest request = uri(\"/saml2/service-provider-metadata/one\");\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tverify(repository).findByRegistrationId(\"one\");\n\t}\n\n\tprivate MockHttpServletRequest uri(String uri) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", uri);\n\t\trequest.setPathInfo(uri);\n\t\treturn request;\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2WebSsoAuthenticationRequestFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.saml2.credentials.TestSaml2X509Credentials;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;\nimport org.springframework.web.util.HtmlUtils;\nimport org.springframework.web.util.UriUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\npublic class Saml2WebSsoAuthenticationRequestFilterTests {\n\n\tprivate static final String IDP_SSO_URL = \"https://sso-url.example.com/IDP/SSO\";\n\n\tprivate Saml2WebSsoAuthenticationRequestFilter filter;\n\n\tprivate RelyingPartyRegistrationRepository repository = mock(RelyingPartyRegistrationRepository.class);\n\n\tprivate Saml2AuthenticationRequestResolver authenticationRequestResolver = mock(\n\t\t\tSaml2AuthenticationRequestResolver.class);\n\n\tprivate Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = mock(\n\t\t\tSaml2AuthenticationRequestRepository.class);\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate MockFilterChain filterChain;\n\n\tprivate RelyingPartyRegistration.Builder rpBuilder;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.filter = new Saml2WebSsoAuthenticationRequestFilter(this.authenticationRequestResolver);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.request.setPathInfo(\"/saml2/authenticate/registration-id\");\n\t\tthis.filterChain = new MockFilterChain() {\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response) {\n\t\t\t\t((HttpServletResponse) response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);\n\t\t\t}\n\t\t};\n\t\tthis.rpBuilder = RelyingPartyRegistration.withRegistrationId(\"registration-id\")\n\t\t\t.assertingPartyMetadata((c) -> c.entityId(\"idp-entity-id\"))\n\t\t\t.assertingPartyMetadata((c) -> c.singleSignOnServiceLocation(IDP_SSO_URL))\n\t\t\t.assertionConsumerServiceLocation(\"template\")\n\t\t\t.signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartyPrivateCredential()))\n\t\t\t.decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartyPrivateCredential()));\n\t\tthis.filter.setAuthenticationRequestRepository(this.authenticationRequestRepository);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoRelayStateThenRedirectDoesNotContainParameter() throws ServletException, IOException {\n\t\tSaml2RedirectAuthenticationRequest request = redirectAuthenticationRequest().build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(request);\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getHeader(\"Location\")).doesNotContain(\"RelayState=\").startsWith(IDP_SSO_URL);\n\t}\n\n\tprivate static Saml2RedirectAuthenticationRequest.Builder redirectAuthenticationRequest() {\n\t\treturn Saml2RedirectAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.relyingPartyRegistration().build())\n\t\t\t.samlRequest(\"request\")\n\t\t\t.authenticationRequestUri(IDP_SSO_URL);\n\t}\n\n\tprivate static Saml2RedirectAuthenticationRequest.Builder redirectAuthenticationRequest(\n\t\t\tRelyingPartyRegistration registration) {\n\t\treturn Saml2RedirectAuthenticationRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.authenticationRequestUri(IDP_SSO_URL);\n\t}\n\n\tprivate static Saml2PostAuthenticationRequest.Builder postAuthenticationRequest() {\n\t\treturn Saml2PostAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(TestRelyingPartyRegistrations.relyingPartyRegistration().build())\n\t\t\t.samlRequest(\"request\")\n\t\t\t.authenticationRequestUri(IDP_SSO_URL);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRelayStateThenRedirectDoesContainParameter() throws ServletException, IOException {\n\t\tSaml2RedirectAuthenticationRequest request = redirectAuthenticationRequest().relayState(\"relayState\").build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(request);\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getHeader(\"Location\")).contains(\"RelayState=relayState\").startsWith(IDP_SSO_URL);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRelayStateThatRequiresEncodingThenRedirectDoesContainsEncodedParameter() throws Exception {\n\t\tString relayStateValue = \"https://my-relay-state.example.com?with=param&other=param\";\n\t\tString relayStateEncoded = UriUtils.encode(relayStateValue, StandardCharsets.ISO_8859_1);\n\t\tSaml2RedirectAuthenticationRequest request = redirectAuthenticationRequest().relayState(relayStateValue)\n\t\t\t.build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(request);\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getHeader(\"Location\")).contains(\"RelayState=\" + relayStateEncoded)\n\t\t\t.startsWith(IDP_SSO_URL);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSimpleSignatureSpecifiedThenSignatureParametersAreInTheRedirectURL() throws Exception {\n\t\tSaml2RedirectAuthenticationRequest request = redirectAuthenticationRequest().sigAlg(\"sigalg\")\n\t\t\t.signature(\"signature\")\n\t\t\t.build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(request);\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getHeader(\"Location\")).contains(\"SigAlg=\")\n\t\t\t.contains(\"Signature=\")\n\t\t\t.startsWith(IDP_SSO_URL);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSignatureIsDisabledThenSignatureParametersAreNotInTheRedirectURL() throws Exception {\n\t\tSaml2RedirectAuthenticationRequest request = redirectAuthenticationRequest().build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(request);\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getHeader(\"Location\")).doesNotContain(\"SigAlg=\")\n\t\t\t.doesNotContain(\"Signature=\")\n\t\t\t.startsWith(IDP_SSO_URL);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPostFormDataIsPresent() throws Exception {\n\t\tString relayStateValue = \"https://my-relay-state.example.com?with=param&other=param&javascript{alert('1');}\";\n\t\tString relayStateEncoded = HtmlUtils.htmlEscape(relayStateValue);\n\t\tRelyingPartyRegistration registration = this.rpBuilder\n\t\t\t.assertingPartyMetadata((asserting) -> asserting.singleSignOnServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.build();\n\t\tSaml2PostAuthenticationRequest request = Saml2PostAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.relayState(relayStateValue)\n\t\t\t.build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(request);\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getHeader(\"Location\")).isNull();\n\t\tassertThat(this.response.getContentAsString()).contains(\n\t\t\t\t\"<meta http-equiv=\\\"Content-Security-Policy\\\" content=\\\"script-src 'sha256-oZhLbc2kO8b8oaYLrUc7uye1MgVKMyLtPqWR4WtKF+c='\\\">\")\n\t\t\t.contains(\"<script>window.onload = function() { document.forms[0].submit(); }</script>\")\n\t\t\t.contains(\"<form action=\\\"https://sso-url.example.com/IDP/SSO\\\" method=\\\"post\\\">\")\n\t\t\t.contains(\"<input type=\\\"hidden\\\" name=\\\"SAMLRequest\\\"\")\n\t\t\t.contains(\"value=\\\"\" + relayStateEncoded + \"\\\"\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRelyingPartyRegistrationNotFoundThenUnauthorized() throws Exception {\n\t\tSaml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(\n\t\t\t\tthis.authenticationRequestResolver);\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void setAuthenticationRequestRepositoryWhenNullThenException() {\n\t\tSaml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(\n\t\t\t\tthis.authenticationRequestResolver);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setAuthenticationRequestRepository(null));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRedirectThenSaveRedirectRequest() throws ServletException, IOException {\n\t\tSaml2RedirectAuthenticationRequest request = redirectAuthenticationRequest().build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(request);\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tverify(this.authenticationRequestRepository).saveAuthenticationRequest(\n\t\t\t\tany(Saml2RedirectAuthenticationRequest.class), eq(this.request), eq(this.response));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPostThenSaveRedirectRequest() throws ServletException, IOException {\n\t\tRelyingPartyRegistration registration = this.rpBuilder\n\t\t\t.assertingPartyMetadata((asserting) -> asserting.singleSignOnServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.build();\n\t\tSaml2PostAuthenticationRequest request = Saml2PostAuthenticationRequest\n\t\t\t.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.build();\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(request);\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tverify(this.authenticationRequestRepository)\n\t\t\t.saveAuthenticationRequest(any(Saml2PostAuthenticationRequest.class), eq(this.request), eq(this.response));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationRequestResolverThenUses() throws Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();\n\t\tSaml2RedirectAuthenticationRequest authenticationRequest = redirectAuthenticationRequest(registration).build();\n\t\tSaml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(\n\t\t\t\tthis.authenticationRequestResolver);\n\t\tgiven(this.authenticationRequestResolver.resolve(any())).willReturn(authenticationRequest);\n\t\tfilter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tverify(this.authenticationRequestResolver).resolve(any());\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;\nimport org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationTokens;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;\nimport org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\npublic class Saml2WebSsoAuthenticationFilterTests {\n\n\tprivate Saml2WebSsoAuthenticationFilter filter;\n\n\tprivate RelyingPartyRegistrationRepository repository = mock(RelyingPartyRegistrationRepository.class);\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate HttpServletResponse response = new MockHttpServletResponse();\n\n\tprivate AuthenticationManager authenticationManager = mock(AuthenticationManager.class);\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(this.repository);\n\t\tthis.request.setRequestURI(\"/login/saml2/sso/idp-registration-id\");\n\t\tthis.request.setPathInfo(\"/login/saml2/sso/idp-registration-id\");\n\t\tthis.request.setParameter(Saml2ParameterNames.SAML_RESPONSE, \"xml-data-goes-here\");\n\t}\n\n\t@Test\n\tpublic void constructingFilterWithMissingRegistrationIdVariableThenThrowsException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> this.filter = new Saml2WebSsoAuthenticationFilter(this.repository, \"/url/missing/variable\"))\n\t\t\t.withMessage(\"filterProcessesUrl must contain a {registrationId} match variable\");\n\t}\n\n\t@Test\n\tpublic void constructingFilterWithValidRegistrationIdVariableThenSucceeds() {\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(this.repository, \"/url/variable/is/present/{registrationId}\");\n\t}\n\n\t@Test\n\tpublic void constructingFilterWithMissingRegistrationIdVariableAndCustomAuthenticationConverterThenSucceeds() {\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, \"/url/missing/variable\");\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationWhenHappyPathThenReturnsTrue() {\n\t\tRequiresAuthenticationExposingFilter filter = new RequiresAuthenticationExposingFilter(this.repository);\n\t\tassertThat(filter.requiresAuthentication(this.request, this.response)).isTrue();\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationWhenCustomProcessingUrlThenReturnsTrue() {\n\t\tRequiresAuthenticationExposingFilter filter = new RequiresAuthenticationExposingFilter(this.repository,\n\t\t\t\t\"/some/other/path/{registrationId}\");\n\t\tthis.request.setRequestURI(\"/some/other/path/idp-registration-id\");\n\t\tthis.request.setPathInfo(\"/some/other/path/idp-registration-id\");\n\t\tthis.request.setParameter(Saml2ParameterNames.SAML_RESPONSE, \"xml-data-goes-here\");\n\t\tassertThat(filter.requiresAuthentication(this.request, this.response)).isTrue();\n\t}\n\n\t@Test\n\tpublic void attemptAuthenticationWhenRegistrationIdDoesNotExistThenThrowsException() {\n\t\tgiven(this.repository.findByRegistrationId(\"non-existent-id\")).willReturn(null);\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(this.repository, \"/some/other/path/{registrationId}\");\n\t\tthis.request.setRequestURI(\"/some/other/path/non-existent-id\");\n\t\tthis.request.setPathInfo(\"/some/other/path/non-existent-id\");\n\t\tthis.request.setParameter(Saml2ParameterNames.SAML_RESPONSE, \"response\");\n\t\tassertThatExceptionOfType(Saml2AuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.filter.attemptAuthentication(this.request, this.response))\n\t\t\t.withMessage(\"No relying party registration found\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenContinueChainRegistrationIdDoesNotExistThenContinues() throws Exception {\n\t\tgiven(this.repository.findByRegistrationId(\"non-existent-id\")).willReturn(null);\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(this.repository, \"/some/other/path/{registrationId}\");\n\t\tthis.filter.setContinueChainWhenNoRelyingPartyRegistrationFound(true);\n\t\tthis.request.setRequestURI(\"/some/other/path/non-existent-id\");\n\t\tthis.request.setPathInfo(\"/some/other/path/non-existent-id\");\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(this.request, this.response, chain);\n\t\tverify(chain).doFilter(this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenContinueChainNoSamlResponseThenContinues() throws Exception {\n\t\tgiven(this.repository.findByRegistrationId(\"id\")).willReturn(TestRelyingPartyRegistrations.full().build());\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(this.repository, \"/some/other/path/{registrationId}\");\n\t\tthis.filter.setContinueChainWhenNoRelyingPartyRegistrationFound(true);\n\t\tthis.request.setRequestURI(\"/some/other/path/id\");\n\t\tthis.request.setPathInfo(\"/some/other/path/id\");\n\t\tthis.request.removeParameter(Saml2ParameterNames.SAML_RESPONSE);\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(this.request, this.response, chain);\n\t\tverify(chain).doFilter(this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void attemptAuthenticationWhenSavedAuthnRequestThenRemovesAuthnRequest() {\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = mock(\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tgiven(authenticationConverter.convert(this.request)).willReturn(TestSaml2AuthenticationTokens.token());\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, \"/some/other/path/{registrationId}\");\n\t\tthis.filter.setAuthenticationManager((authentication) -> null);\n\t\tthis.request.setRequestURI(\"/some/other/path/idp-registration-id\");\n\t\tthis.request.setPathInfo(\"/some/other/path/idp-registration-id\");\n\t\tthis.filter.setAuthenticationRequestRepository(authenticationRequestRepository);\n\t\tthis.filter.attemptAuthentication(this.request, this.response);\n\t\tverify(authenticationRequestRepository).removeAuthenticationRequest(this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void attemptAuthenticationAddsDetails() {\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tfinal Saml2AuthenticationToken token = TestSaml2AuthenticationTokens.token();\n\t\tgiven(authenticationConverter.convert(this.request)).willReturn(token);\n\t\tfinal AuthenticationDetailsSource authenticationDetailsSource = mock(AuthenticationDetailsSource.class);\n\t\tfinal WebAuthenticationDetails details = mock(WebAuthenticationDetails.class);\n\t\tgiven(authenticationDetailsSource.buildDetails(this.request)).willReturn(details);\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, \"/some/other/path/{registrationId}\");\n\t\tthis.filter.setAuthenticationManager((authentication) -> null);\n\t\tthis.filter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\t\tthis.request.setPathInfo(\"/some/other/path/idp-registration-id\");\n\t\tthis.filter.attemptAuthentication(this.request, this.response);\n\t\tassertThat(token.getDetails()).isEqualTo(details);\n\t}\n\n\t@Test\n\tpublic void attemptAuthenticationWhenAuthenticationNotAbstractAuthenticationTokenDoesNotAddDetails() {\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tfinal Authentication authenticationWithoutDetails = mock(Authentication.class);\n\t\tgiven(authenticationConverter.convert(this.request)).willReturn(authenticationWithoutDetails);\n\t\tfinal AuthenticationDetailsSource authenticationDetailsSource = mock(AuthenticationDetailsSource.class);\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, \"/some/other/path/{registrationId}\");\n\t\tthis.filter.setAuthenticationManager((authentication) -> null);\n\t\tthis.filter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\t\tthis.request.setPathInfo(\"/some/other/path/idp-registration-id\");\n\t\tassertThatNoException().isThrownBy(() -> this.filter.attemptAuthentication(this.request, this.response));\n\t\tverifyNoInteractions(authenticationDetailsSource);\n\t}\n\n\t@Test\n\tpublic void setAuthenticationRequestRepositoryWhenNullThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationRequestRepository(null))\n\t\t\t.withMessage(\"authenticationRequestRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationRequestRepositoryWhenExpectedAuthenticationConverterTypeThenSetLoaderIntoConverter() {\n\t\tSaml2AuthenticationTokenConverter authenticationConverter = mock(Saml2AuthenticationTokenConverter.class);\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = mock(\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, \"/some/other/path/{registrationId}\");\n\t\tthis.filter.setAuthenticationRequestRepository(authenticationRequestRepository);\n\t\tverify(authenticationConverter).setAuthenticationRequestRepository(authenticationRequestRepository);\n\t}\n\n\t@Test\n\tpublic void setAuthenticationRequestRepositoryWhenNotExpectedAuthenticationConverterTypeThenDoNotSet() {\n\t\tAuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);\n\t\tSaml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = mock(\n\t\t\t\tSaml2AuthenticationRequestRepository.class);\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, \"/some/other/path/{registrationId}\");\n\t\tthis.filter.setAuthenticationRequestRepository(authenticationRequestRepository);\n\t\tverifyNoInteractions(authenticationConverter);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenPathStartsWithRegistrationIdThenAuthenticates() throws Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.repository.findByRegistrationId(\"registration-id\")).willReturn(registration);\n\t\tgiven(this.authenticationManager.authenticate(authentication)).willReturn(authentication);\n\t\tString loginProcessingUrl = \"/{registrationId}/login/saml2/sso\";\n\t\tRequestMatcher matcher = pathPattern(loginProcessingUrl);\n\t\tDefaultRelyingPartyRegistrationResolver delegate = new DefaultRelyingPartyRegistrationResolver(this.repository);\n\t\tRelyingPartyRegistrationResolver resolver = (request, id) -> {\n\t\t\tString registrationId = matcher.matcher(request).getVariables().get(\"registrationId\");\n\t\t\treturn delegate.resolve(request, registrationId);\n\t\t};\n\t\tSaml2AuthenticationTokenConverter authenticationConverter = new Saml2AuthenticationTokenConverter(resolver);\n\t\tthis.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, loginProcessingUrl);\n\t\tthis.filter.setAuthenticationManager(this.authenticationManager);\n\t\tthis.request.setRequestURI(\"/registration-id/login/saml2/sso\");\n\t\tthis.request.setPathInfo(\"/registration-id/login/saml2/sso\");\n\t\tthis.request.setParameter(Saml2ParameterNames.SAML_RESPONSE, \"response\");\n\t\tthis.filter.doFilter(this.request, this.response, new MockFilterChain());\n\t\tverify(this.repository).findByRegistrationId(\"registration-id\");\n\t}\n\n\tstatic final class RequiresAuthenticationExposingFilter extends Saml2WebSsoAuthenticationFilter {\n\n\t\tRequiresAuthenticationExposingFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {\n\t\t\tsuper(relyingPartyRegistrationRepository);\n\t\t}\n\n\t\tRequiresAuthenticationExposingFilter(RelyingPartyRegistrationRepository registrations, String url) {\n\t\t\tsuper(registrations, url);\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {\n\t\t\treturn super.requiresAuthentication(request, response);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/HttpSessionLogoutRequestRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link HttpSessionLogoutRequestRepository}\n */\npublic class HttpSessionLogoutRequestRepositoryTests {\n\n\tHttpSessionLogoutRequestRepository logoutRequestRepository = new HttpSessionLogoutRequestRepository();\n\n\t@Test\n\tpublic void loadLogoutRequestWhenHttpServletRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.logoutRequestRepository.loadLogoutRequest(null));\n\t}\n\n\t@Test\n\tpublic void loadLogoutRequestWhenNotSavedThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, \"state-1234\");\n\t\tSaml2LogoutRequest logoutRequest = this.logoutRequestRepository.loadLogoutRequest(request);\n\t\tassertThat(logoutRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void loadLogoutRequestWhenSavedThenReturnLogoutRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, request, response);\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest.getRelayState());\n\t\tSaml2LogoutRequest loadedLogoutRequest = this.logoutRequestRepository.loadLogoutRequest(request);\n\t\tassertThat(loadedLogoutRequest).isEqualTo(logoutRequest);\n\t}\n\n\t@Test\n\tpublic void loadLogoutRequestWhenMultipleSavedThenReplacesLogoutRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSaml2LogoutRequest one = createLogoutRequest().relayState(\"state-1122\").build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(one, request, response);\n\t\tSaml2LogoutRequest two = createLogoutRequest().relayState(\"state-3344\").build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(two, request, response);\n\t\trequest.setParameter(Saml2ParameterNames.RELAY_STATE, one.getRelayState());\n\t\tassertThat(this.logoutRequestRepository.loadLogoutRequest(request)).isNull();\n\t\trequest.setParameter(Saml2ParameterNames.RELAY_STATE, two.getRelayState());\n\t\tassertThat(this.logoutRequestRepository.loadLogoutRequest(request)).isEqualTo(two);\n\t}\n\n\t@Test\n\tvoid serializeAndDeserializeSaml2LogoutRequest() throws IOException, ClassNotFoundException {\n\t\tSaml2LogoutRequest requestToSerialize = createLogoutRequest().relayState(\"state-serialized\").build();\n\t\tbyte[] data;\n\t\ttry (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n\t\t\t\tObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {\n\t\t\tobjectOutputStream.writeObject(requestToSerialize);\n\t\t\tdata = outputStream.toByteArray();\n\t\t}\n\n\t\ttry (ByteArrayInputStream inputStream = new ByteArrayInputStream(data);\n\t\t\t\tObjectInputStream objectInputStream = new ObjectInputStream(inputStream)) {\n\t\t\tSaml2LogoutRequest deserializedRequest = (Saml2LogoutRequest) objectInputStream.readObject();\n\t\t\tassertThat(requestToSerialize.getRelayState()).isEqualTo(deserializedRequest.getRelayState());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void loadLogoutRequestWhenSavedAndStateParameterNullThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, request, new MockHttpServletResponse());\n\t\tassertThat(this.logoutRequestRepository.loadLogoutRequest(request)).isNull();\n\t}\n\n\t@Test\n\tpublic void saveLogoutRequestWhenHttpServletRequestIsNullThenThrowIllegalArgumentException() {\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.logoutRequestRepository\n\t\t\t.saveLogoutRequest(logoutRequest, null, new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void saveLogoutRequestWhenHttpServletResponseIsNullThenThrowIllegalArgumentException() {\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.logoutRequestRepository\n\t\t\t.saveLogoutRequest(logoutRequest, new MockHttpServletRequest(), null));\n\t}\n\n\t@Test\n\tpublic void saveLogoutRequestWhenStateNullThenThrowIllegalArgumentException() {\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().relayState(null).build();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.logoutRequestRepository\n\t\t\t.saveLogoutRequest(logoutRequest, new MockHttpServletRequest(), new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void saveLogoutRequestWhenNotNullThenSaved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, request, new MockHttpServletResponse());\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest.getRelayState());\n\t\tSaml2LogoutRequest loadedLogoutRequest = this.logoutRequestRepository.loadLogoutRequest(request);\n\t\tassertThat(loadedLogoutRequest).isEqualTo(logoutRequest);\n\t}\n\n\t@Test\n\tpublic void saveLogoutRequestWhenNoExistingSessionAndDistributedSessionThenSaved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(new MockDistributedHttpSession());\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, request, new MockHttpServletResponse());\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest.getRelayState());\n\t\tSaml2LogoutRequest loadedLogoutRequest = this.logoutRequestRepository.loadLogoutRequest(request);\n\t\tassertThat(loadedLogoutRequest).isEqualTo(logoutRequest);\n\t}\n\n\t@Test\n\tpublic void saveLogoutRequestWhenExistingSessionAndDistributedSessionThenSaved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(new MockDistributedHttpSession());\n\t\tSaml2LogoutRequest logoutRequest1 = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest1, request, new MockHttpServletResponse());\n\t\tSaml2LogoutRequest logoutRequest2 = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest2, request, new MockHttpServletResponse());\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest2.getRelayState());\n\t\tSaml2LogoutRequest loadedLogoutRequest = this.logoutRequestRepository.loadLogoutRequest(request);\n\t\tassertThat(loadedLogoutRequest).isEqualTo(logoutRequest2);\n\t}\n\n\t@Test\n\tpublic void saveLogoutRequestWhenNullThenRemoved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, request, response);\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest.getRelayState());\n\t\tthis.logoutRequestRepository.saveLogoutRequest(null, request, response);\n\t\tSaml2LogoutRequest loadedLogoutRequest = this.logoutRequestRepository.loadLogoutRequest(request);\n\t\tassertThat(loadedLogoutRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void removeLogoutRequestWhenHttpServletRequestIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.logoutRequestRepository.removeLogoutRequest(null, new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void removeLogoutRequestWhenHttpServletResponseIsNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.logoutRequestRepository.removeLogoutRequest(new MockHttpServletRequest(), null));\n\t}\n\n\t@Test\n\tpublic void removeLogoutRequestWhenSavedThenRemoved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, request, response);\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest.getRelayState());\n\t\tSaml2LogoutRequest removedLogoutRequest = this.logoutRequestRepository.removeLogoutRequest(request, response);\n\t\tSaml2LogoutRequest loadedLogoutRequest = this.logoutRequestRepository.loadLogoutRequest(request);\n\t\tassertThat(removedLogoutRequest).isNotNull();\n\t\tassertThat(loadedLogoutRequest).isNull();\n\t}\n\n\t// gh-5263\n\t@Test\n\tpublic void removeLogoutRequestWhenSavedThenRemovedFromSession() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSaml2LogoutRequest logoutRequest = createLogoutRequest().build();\n\t\tthis.logoutRequestRepository.saveLogoutRequest(logoutRequest, request, response);\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest.getRelayState());\n\t\tSaml2LogoutRequest removedLogoutRequest = this.logoutRequestRepository.removeLogoutRequest(request, response);\n\t\tString sessionAttributeName = HttpSessionLogoutRequestRepository.class.getName() + \".AUTHORIZATION_REQUEST\";\n\t\tassertThat(removedLogoutRequest).isNotNull();\n\t\tassertThat(request.getSession().getAttribute(sessionAttributeName)).isNull();\n\t}\n\n\t@Test\n\tpublic void removeLogoutRequestWhenNotSavedThenNotRemoved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(Saml2ParameterNames.RELAY_STATE, \"state-1234\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSaml2LogoutRequest removedLogoutRequest = this.logoutRequestRepository.removeLogoutRequest(request, response);\n\t\tassertThat(removedLogoutRequest).isNull();\n\t}\n\n\tprivate Saml2LogoutRequest.Builder createLogoutRequest() {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\treturn Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.id(\"id\")\n\t\t\t.parameters((params) -> params.put(Saml2ParameterNames.RELAY_STATE, \"state-1234\"));\n\t}\n\n\tstatic class MockDistributedHttpSession extends MockHttpSession {\n\n\t\t@Override\n\t\tpublic Object getAttribute(String name) {\n\t\t\treturn wrap(super.getAttribute(name));\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAttribute(String name, Object value) {\n\t\t\tsuper.setAttribute(name, wrap(value));\n\t\t}\n\n\t\tprivate Object wrap(Object object) {\n\t\t\tif (object instanceof Map) {\n\t\t\t\tobject = new HashMap<>((Map<Object, Object>) object);\n\t\t\t}\n\t\t\treturn object;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ErrorCodes;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutValidatorResult;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * Tests for {@link Saml2LogoutRequestFilter}\n */\npublic class Saml2LogoutRequestFilterTests {\n\n\tSecurityContextHolderStrategy securityContextHolderStrategy = mock(SecurityContextHolderStrategy.class);\n\n\tRelyingPartyRegistrationResolver relyingPartyRegistrationResolver = mock(RelyingPartyRegistrationResolver.class);\n\n\tSaml2LogoutRequestValidator logoutRequestValidator = mock(Saml2LogoutRequestValidator.class);\n\n\tLogoutHandler logoutHandler = mock(LogoutHandler.class);\n\n\tSaml2LogoutResponseResolver logoutResponseResolver = mock(Saml2LogoutResponseResolver.class);\n\n\tSaml2LogoutRequestFilter logoutRequestProcessingFilter = new Saml2LogoutRequestFilter(\n\t\t\tthis.relyingPartyRegistrationResolver, this.logoutRequestValidator, this.logoutResponseResolver,\n\t\t\tthis.logoutHandler);\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSamlRequestThenRedirects() throws Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").param(Saml2ParameterNames.SAML_REQUEST, \"request\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration);\n\t\tgiven(this.logoutRequestValidator.validate(any())).willReturn(Saml2LogoutValidatorResult.success());\n\t\tSaml2LogoutResponse logoutResponse = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(\"response\")\n\t\t\t.build();\n\t\tgiven(this.logoutResponseResolver.resolve(any(), any())).willReturn(logoutResponse);\n\t\tthis.logoutRequestProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverify(this.logoutRequestValidator).validate(any());\n\t\tverify(this.logoutHandler).logout(any(), any(), any());\n\t\tverify(this.logoutResponseResolver).resolve(any(), any());\n\t\tString content = response.getHeader(\"Location\");\n\t\tassertThat(content).contains(Saml2ParameterNames.SAML_RESPONSE);\n\t\tassertThat(content)\n\t\t\t.startsWith(registration.getAssertingPartyMetadata().getSingleLogoutServiceResponseLocation());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSamlRequestThenPosts() throws Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tgiven(this.securityContextHolderStrategy.getContext()).willReturn(new SecurityContextImpl(authentication));\n\t\tthis.logoutRequestProcessingFilter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").param(Saml2ParameterNames.SAML_REQUEST, \"request\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration);\n\t\tgiven(this.logoutRequestValidator.validate(any())).willReturn(Saml2LogoutValidatorResult.success());\n\t\tSaml2LogoutResponse logoutResponse = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(\"response\")\n\t\t\t.build();\n\t\tgiven(this.logoutResponseResolver.resolve(any(), any())).willReturn(logoutResponse);\n\t\tthis.logoutRequestProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverify(this.logoutRequestValidator).validate(any());\n\t\tverify(this.logoutHandler).logout(any(), any(), any());\n\t\tverify(this.logoutResponseResolver).resolve(any(), any());\n\t\tcheckResponse(response.getContentAsString(), registration);\n\t\tverify(this.securityContextHolderStrategy).getContext();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestMismatchesThenNoLogout() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout\").param(Saml2ParameterNames.SAML_RESPONSE, \"response\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.logoutRequestProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverifyNoInteractions(this.logoutRequestValidator, this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoSamlRequestOrResponseThenNoLogout() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.logoutRequestProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverifyNoInteractions(this.logoutRequestValidator, this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenValidationFailsErrorLogoutResponseIsPosted() throws Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.build();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").param(Saml2ParameterNames.SAML_REQUEST, \"request\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSaml2LogoutResponse logoutResponse = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(\"response\")\n\t\t\t.build();\n\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(request, null)).willReturn(registration);\n\t\tgiven(this.logoutRequestValidator.validate(any()))\n\t\t\t.willReturn(Saml2LogoutValidatorResult.withErrors(new Saml2Error(\"error\", \"description\")).build());\n\t\tgiven(this.logoutResponseResolver.resolve(any(), any(),\n\t\t\t\targThat((ex) -> ex.getSaml2Error().getErrorCode().equals(Saml2ErrorCodes.INVALID_REQUEST))))\n\t\t\t.willReturn(logoutResponse);\n\n\t\tthis.logoutRequestProcessingFilter.doFilter(request, response, new MockFilterChain());\n\n\t\tcheckResponse(response.getContentAsString(), registration);\n\t\tverify(this.logoutRequestValidator).validate(any());\n\t\tverify(this.logoutResponseResolver).resolve(any(), any(),\n\t\t\t\targThat((ex) -> ex.getSaml2Error().getErrorCode().equals(Saml2ErrorCodes.INVALID_REQUEST)));\n\t\tverifyNoInteractions(this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoRelyingErrorLogoutResponseIsPosted() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/logout/saml2/slo\");\n\t\trequest.setParameter(Saml2ParameterNames.SAML_REQUEST, \"request\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.singleLogoutServiceLocation(null)\n\t\t\t.build();\n\t\tSaml2LogoutResponse logoutResponse = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(\"response\")\n\t\t\t.build();\n\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration);\n\t\tgiven(this.logoutResponseResolver.resolve(any(), any(),\n\t\t\t\targThat((ex) -> ex.getSaml2Error().getErrorCode().equals(Saml2ErrorCodes.INVALID_DESTINATION))))\n\t\t\t.willReturn(logoutResponse);\n\n\t\tthis.logoutRequestProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\n\t\tcheckResponse(response.getContentAsString(), registration);\n\t\tverify(this.logoutResponseResolver).resolve(any(), any(),\n\t\t\t\targThat((ex) -> ex.getSaml2Error().getErrorCode().equals(Saml2ErrorCodes.INVALID_DESTINATION)));\n\t\tverifyNoInteractions(this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenInvalidBindingErrorLogoutResponseIsPosted() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").param(Saml2ParameterNames.SAML_REQUEST, \"request\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.singleLogoutServiceBindings((bindings) -> {\n\t\t\t\tbindings.clear();\n\t\t\t\tbindings.add(Saml2MessageBinding.REDIRECT);\n\t\t\t})\n\t\t\t.build();\n\t\tSaml2LogoutResponse logoutResponse = Saml2LogoutResponse.withRelyingPartyRegistration(registration)\n\t\t\t.samlResponse(\"response\")\n\t\t\t.build();\n\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration);\n\t\tgiven(this.logoutResponseResolver.resolve(any(), any(),\n\t\t\t\targThat((ex) -> ex.getSaml2Error().getErrorCode().equals(Saml2ErrorCodes.INVALID_REQUEST))))\n\t\t\t.willReturn(logoutResponse);\n\n\t\tthis.logoutRequestProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\n\t\tcheckResponse(response.getContentAsString(), registration);\n\t\tverify(this.logoutResponseResolver).resolve(any(), any(),\n\t\t\t\targThat((ex) -> ex.getSaml2Error().getErrorCode().equals(Saml2ErrorCodes.INVALID_REQUEST)));\n\t\tverifyNoInteractions(this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoErrorResponseCanBeGeneratedThen401() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").param(Saml2ParameterNames.SAML_REQUEST, \"request\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.singleLogoutServiceBindings((bindings) -> {\n\t\t\t\tbindings.clear();\n\t\t\t\tbindings.add(Saml2MessageBinding.REDIRECT);\n\t\t\t})\n\t\t\t.build();\n\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration);\n\t\tgiven(this.logoutResponseResolver.resolve(any(), any(),\n\t\t\t\targThat((ex) -> ex.getSaml2Error().getErrorCode().equals(Saml2ErrorCodes.INVALID_REQUEST))))\n\t\t\t.willReturn(null);\n\n\t\tthis.logoutRequestProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tverify(this.logoutResponseResolver).resolve(any(), any(),\n\t\t\t\targThat((ex) -> ex.getSaml2Error().getErrorCode().equals(Saml2ErrorCodes.INVALID_REQUEST)));\n\t\tverifyNoInteractions(this.logoutHandler);\n\t}\n\n\tprivate void checkResponse(String responseContent, RelyingPartyRegistration registration) {\n\t\tassertThat(responseContent).contains(Saml2ParameterNames.SAML_RESPONSE);\n\t\tassertThat(responseContent)\n\t\t\t.contains(registration.getAssertingPartyMetadata().getSingleLogoutServiceResponseLocation());\n\t\tassertThat(responseContent).contains(\n\t\t\t\t\"<meta http-equiv=\\\"Content-Security-Policy\\\" content=\\\"script-src 'sha256-oZhLbc2kO8b8oaYLrUc7uye1MgVKMyLtPqWR4WtKF+c='\\\">\");\n\t\tassertThat(responseContent)\n\t\t\t.contains(\"<script>window.onload = function() { document.forms[0].submit(); }</script>\");\n\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.saml2.core.Saml2Error;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutValidatorResult;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\nimport org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.mockito.BDDMockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * Tests for {@link Saml2LogoutResponseFilter}\n */\npublic class Saml2LogoutResponseFilterTests {\n\n\tRelyingPartyRegistrationResolver relyingPartyRegistrationResolver = mock(RelyingPartyRegistrationResolver.class);\n\n\tSaml2LogoutRequestRepository logoutRequestRepository = mock(Saml2LogoutRequestRepository.class);\n\n\tSaml2LogoutResponseValidator logoutResponseValidator = mock(Saml2LogoutResponseValidator.class);\n\n\tLogoutSuccessHandler logoutSuccessHandler = mock(LogoutSuccessHandler.class);\n\n\tSaml2LogoutResponseFilter logoutResponseProcessingFilter = new Saml2LogoutResponseFilter(\n\t\t\tthis.relyingPartyRegistrationResolver, this.logoutResponseValidator, this.logoutSuccessHandler);\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.logoutResponseProcessingFilter.setLogoutRequestRepository(this.logoutRequestRepository);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSamlResponsePostThenLogout() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").param(Saml2ParameterNames.SAML_RESPONSE, \"response\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(request, \"registration-id\")).willReturn(registration);\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.build();\n\t\tgiven(this.logoutRequestRepository.removeLogoutRequest(request, response)).willReturn(logoutRequest);\n\t\tgiven(this.logoutResponseValidator.validate(any())).willReturn(Saml2LogoutValidatorResult.success());\n\t\tthis.logoutResponseProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverify(this.logoutResponseValidator).validate(any());\n\t\tverify(this.logoutSuccessHandler).onLogoutSuccess(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSamlResponseRedirectThenLogout() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = get(\"/logout/saml2/slo\").build();\n\t\trequest.setParameter(Saml2ParameterNames.SAML_RESPONSE, \"response\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.singleLogoutServiceBinding(Saml2MessageBinding.REDIRECT)\n\t\t\t.build();\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(request, \"registration-id\")).willReturn(registration);\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.build();\n\t\tgiven(this.logoutRequestRepository.removeLogoutRequest(request, response)).willReturn(logoutRequest);\n\t\tgiven(this.logoutResponseValidator.validate(any())).willReturn(Saml2LogoutValidatorResult.success());\n\t\tthis.logoutResponseProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverify(this.logoutResponseValidator).validate(any());\n\t\tverify(this.logoutSuccessHandler).onLogoutSuccess(any(), any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestMismatchesThenNoLogout() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout\").param(Saml2ParameterNames.SAML_REQUEST, \"request\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.logoutResponseProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverifyNoInteractions(this.logoutResponseValidator, this.logoutSuccessHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoSamlRequestOrResponseThenNoLogout() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.logoutResponseProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverifyNoInteractions(this.logoutResponseValidator, this.logoutSuccessHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenValidatorFailsThenStops() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").param(Saml2ParameterNames.SAML_RESPONSE, \"response\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(request, \"registration-id\")).willReturn(registration);\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.build();\n\t\tgiven(this.logoutRequestRepository.removeLogoutRequest(request, response)).willReturn(logoutRequest);\n\t\tgiven(this.logoutResponseValidator.validate(any()))\n\t\t\t.willReturn(Saml2LogoutValidatorResult.withErrors(new Saml2Error(\"error\", \"description\")).build());\n\t\tthis.logoutResponseProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tverify(this.logoutResponseValidator).validate(any());\n\t\tverifyNoInteractions(this.logoutSuccessHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoRelyingPartyLogoutThen401() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tMockHttpServletRequest request = post(\"/logout/saml2/slo\").param(Saml2ParameterNames.SAML_RESPONSE, \"response\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.singleLogoutServiceLocation(null)\n\t\t\t.singleLogoutServiceResponseLocation(null)\n\t\t\t.build();\n\t\tgiven(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration);\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.build();\n\t\tgiven(this.logoutRequestRepository.removeLogoutRequest(request, response)).willReturn(logoutRequest);\n\t\tthis.logoutResponseProcessingFilter.doFilterInternal(request, response, new MockFilterChain());\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tverifyNoInteractions(this.logoutSuccessHandler);\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.saml2.provider.service.web.authentication.logout;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.saml2.core.Saml2ParameterNames;\nimport org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;\nimport org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;\nimport org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;\nimport org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;\nimport org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;\nimport org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * Tests for {@link Saml2RelyingPartyInitiatedLogoutSuccessHandler}\n *\n * @author Josh Cummings\n */\npublic class Saml2RelyingPartyInitiatedLogoutSuccessHandlerTests {\n\n\tSaml2LogoutRequestResolver logoutRequestResolver = mock(Saml2LogoutRequestResolver.class);\n\n\tSaml2LogoutRequestRepository logoutRequestRepository = mock(Saml2LogoutRequestRepository.class);\n\n\tSaml2RelyingPartyInitiatedLogoutSuccessHandler logoutRequestSuccessHandler = new Saml2RelyingPartyInitiatedLogoutSuccessHandler(\n\t\t\tthis.logoutRequestResolver);\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.logoutRequestSuccessHandler.setLogoutRequestRepository(this.logoutRequestRepository);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void onLogoutSuccessWhenRedirectThenRedirectsToAssertingParty() throws Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full().build();\n\t\tAuthentication authentication = authentication(registration);\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.build();\n\t\tMockHttpServletRequest request = post(\"/saml2/logout\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tgiven(this.logoutRequestResolver.resolve(any(), any())).willReturn(logoutRequest);\n\t\tthis.logoutRequestSuccessHandler.onLogoutSuccess(request, response, authentication);\n\t\tString content = response.getHeader(\"Location\");\n\t\tassertThat(content).contains(Saml2ParameterNames.SAML_REQUEST);\n\t\tassertThat(content).startsWith(registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation());\n\t}\n\n\t@Test\n\tpublic void onLogoutSuccessWhenPostThenPostsToAssertingParty() throws Exception {\n\t\tRelyingPartyRegistration registration = TestRelyingPartyRegistrations.full()\n\t\t\t.assertingPartyMetadata((party) -> party.singleLogoutServiceBinding(Saml2MessageBinding.POST))\n\t\t\t.build();\n\t\tAuthentication authentication = authentication(registration);\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tSaml2LogoutRequest logoutRequest = Saml2LogoutRequest.withRelyingPartyRegistration(registration)\n\t\t\t.samlRequest(\"request\")\n\t\t\t.build();\n\t\tMockHttpServletRequest request = post(\"/saml2/logout\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tgiven(this.logoutRequestResolver.resolve(any(), any())).willReturn(logoutRequest);\n\t\tthis.logoutRequestSuccessHandler.onLogoutSuccess(request, response, authentication);\n\t\tString content = response.getContentAsString();\n\t\tassertThat(content).contains(Saml2ParameterNames.SAML_REQUEST);\n\t\tassertThat(content).contains(registration.getAssertingPartyMetadata().getSingleLogoutServiceLocation());\n\t\tassertThat(content).contains(\n\t\t\t\t\"<meta http-equiv=\\\"Content-Security-Policy\\\" content=\\\"script-src 'sha256-oZhLbc2kO8b8oaYLrUc7uye1MgVKMyLtPqWR4WtKF+c='\\\">\");\n\t\tassertThat(content).contains(\"<script>window.onload = function() { document.forms[0].submit(); }</script>\");\n\t}\n\n\tprivate Saml2Authentication authentication(RelyingPartyRegistration registration) {\n\t\tDefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(\"user\", new HashMap<>());\n\t\tprincipal.setRelyingPartyRegistrationId(registration.getRegistrationId());\n\t\treturn new Saml2Authentication(principal, \"response\", new ArrayList<>());\n\t}\n\n}\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t\t<encoder>\n\t\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\t<root level=\"${root.level:-WARN}\">\n\t\t<appender-ref ref=\"STDOUT\"/>\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/resources/rsa.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIID1zCCAr+gAwIBAgIUCzQeKBMTO0iHVW3iKmZC41haqCowDQYJKoZIhvcNAQEL\nBQAwezELMAkGA1UEBhMCWFgxEjAQBgNVBAgMCVN0YXRlTmFtZTERMA8GA1UEBwwI\nQ2l0eU5hbWUxFDASBgNVBAoMC0NvbXBhbnlOYW1lMRswGQYDVQQLDBJDb21wYW55\nU2VjdGlvbk5hbWUxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzA5MjAwODI5MDNa\nFw0zMzA5MTcwODI5MDNaMHsxCzAJBgNVBAYTAlhYMRIwEAYDVQQIDAlTdGF0ZU5h\nbWUxETAPBgNVBAcMCENpdHlOYW1lMRQwEgYDVQQKDAtDb21wYW55TmFtZTEbMBkG\nA1UECwwSQ29tcGFueVNlY3Rpb25OYW1lMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEi\nMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUfi4aaCotJZX6OSDjv6fxCCfc\nihSs91Z/mmN+yc1fsxVSs53SIbqUuo+Wzhv34kp8I/r03P9LWVTkFPbeDxAl75Oa\nPGggxK55US0Zfy9Hj1BwWIKV3330N61emID1GDEtFKL4yJbJdreQXnIXTBL2o76V\nnuV/tYozyZnb07IQ1WhUm5WDxgzM0yFudMynTczCBeZHfvharDtB8PFFhCZXW2/9\nTZVVfW4oOML8EAX3hvnvYBlFl/foxXekZSwq/odOkmWCZavT2+0sburHUlOnPGUh\nQj4tHwpMRczp7VX4ptV1D2UrxsK/2B+s9FK2QSLKQ9JzAYJ6WxQjHcvET9jvAgMB\nAAGjUzBRMB0GA1UdDgQWBBQjDr/1E/01pfLPD8uWF7gbaYL0TTAfBgNVHSMEGDAW\ngBQjDr/1E/01pfLPD8uWF7gbaYL0TTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3\nDQEBCwUAA4IBAQAGjUuec0+0XNMCRDKZslbImdCAVsKsEWk6NpnUViDFAxL+KQuC\nNW131UeHb9SCzMqRwrY4QI3nAwJQCmilL/hFM3ss4acn3WHu1yci/iKPUKeL1ec5\nkCFUmqX1NpTiVaytZ/9TKEr69SMVqNfQiuW5U1bIIYTqK8xo46WpM6YNNHO3eJK6\nNH0MW79Wx5ryi4i4C6afqYbVbx7tqcmy8CFeNxgZ0bFQ87SiwYXIj77b6sVYbu32\ndoykBQgSHLcagWASPQ73m73CWUgo+7+EqSKIQqORbgmTLPmOUh99gFIx7jmjTyHm\nNBszx1ZVWuIv3mWmp626Kncyc+LLM9tvgymx\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/resources/saml2-response-sso-circle.encoded",
    "content": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6%0D%0AcHJvdG9jb2wiIElEPSJzMjQ2ZDE1NzQ0NjYxOGU5MGU0M2ZiNzliZGQ0ZDllOWUxOWNmMmM3YzQi%0D%0AIEluUmVzcG9uc2VUbz0iQVJROWE3M2VhZC03ZGNmLTQ1YTgtODllYi0yNmYzYzk5MDBjMzYiIFZl%0D%0AcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE5LTEyLTE5VDE3OjMxOjI0WiIgRGVzdGluYXRp%0D%0Ab249Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9sb2dpbi9zYW1sMi9zc28vc3NvY2lyY2xlIj48c2Ft%0D%0AbDpJc3N1ZXIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlv%0D%0AbiI%2BaHR0cHM6Ly9pZHAuc3NvY2lyY2xlLmNvbTwvc2FtbDpJc3N1ZXI%2BPHNhbWxwOlN0YXR1cyB4%0D%0AbWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj4KPHNhbWxw%0D%0AOlN0YXR1c0NvZGUgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90%0D%0Ab2NvbCIgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyI%2B%0D%0ACjwvc2FtbHA6U3RhdHVzQ29kZT4KPC9zYW1scDpTdGF0dXM%2BPHNhbWw6QXNzZXJ0aW9uIHhtbG5z%0D%0AOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJzMjg4MjBl%0D%0AZWQyY2QyNWQ0Y2I3ZTAwOTg1OTM4OGMzMWU1ZjFlZWZhOTciIElzc3VlSW5zdGFudD0iMjAxOS0x%0D%0AMi0xOVQxNzozMToyNFoiIFZlcnNpb249IjIuMCI%2BCjxzYW1sOklzc3Vlcj5odHRwczovL2lkcC5z%0D%0Ac29jaXJjbGUuY29tPC9zYW1sOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8v%0D%0Ad3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8%2BCjxkczpDYW5vbmlj%0D%0AYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwt%0D%0AZXhjLWMxNG4jIi8%2BCjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3Lncz%0D%0ALm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KPGRzOlJlZmVyZW5jZSBVUkk9IiNzMjg4%0D%0AMjBlZWQyY2QyNWQ0Y2I3ZTAwOTg1OTM4OGMzMWU1ZjFlZWZhOTciPgo8ZHM6VHJhbnNmb3Jtcz4K%0D%0APGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNp%0D%0AZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8%2BCjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8v%0D%0Ad3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KPC9kczpUcmFuc2Zvcm1zPgo8ZHM6%0D%0ARGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2ln%0D%0AI3NoYTEiLz4KPGRzOkRpZ2VzdFZhbHVlPnc2SkVMMGRNNGVuT0EwWmQ5Q0tpbXNwTHpEUT08L2Rz%0D%0AOkRpZ2VzdFZhbHVlPgo8L2RzOlJlZmVyZW5jZT4KPC9kczpTaWduZWRJbmZvPgo8ZHM6U2lnbmF0%0D%0AdXJlVmFsdWU%2BCkg1TGh5VUZITnhzUUdaaWZjbmpkVUV1cnVCSTJ5UkpoYXJJQ1hoUkU5c1dBYkg3%0D%0ASWxGUWx1S2NmV3hGbjNpNEdIdzZRZDg3cDVkL3EKUE5ZTmptTVpUemU2bENIS2dmenZmRExObjNU%0D%0AM0ZhWXFtYkxvTkJUU1YxMTk1Wmgxa1FhdFIwY1NHZXF3NHJtbGR0enZvRzZrNk92YwovRUk5U05i%0D%0ARFJJYURBOW1lbXdEOG0wczVoSUxMb09uRGNQb2lxTHNKR3laYmR2YVZ3RVlnYS9XMitmSmNpTWNB%0D%0ARDViMGdpOWpab05oCko2dnhIcVpwaEg1dm9MZTNFdGVPQ083TTFhMTVmNk9jWU9MeXFYbFFUdytu%0D%0AeElsYnBEeG03ZTQ4VVljL1htYmRwMUsvNlMwOFdmZWkKNFZLL3ZSZjVaUnZ4ckZ4V2ZrRlRGYlN2%0D%0AVkVlOFpURWM3NlEzWkE9PQo8L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz4KPGRzOlg1%0D%0AMDlEYXRhPgo8ZHM6WDUwOUNlcnRpZmljYXRlPgpNSUlFWXpDQ0FrdWdBd0lCQWdJRElBWm1NQTBH%0D%0AQ1NxR1NJYjNEUUVCQ3dVQU1DNHhDekFKQmdOVkJBWVRBa1JGTVJJd0VBWURWUVFLCkRBbFRVMDlE%0D%0AYVhKamJHVXhDekFKQmdOVkJBTU1Ba05CTUI0WERURTJNRGd3TXpFMU1ETXlNMW9YRFRJMk1ETXdO%0D%0AREUxTURNeU0xb3cKUFRFTE1Ba0dBMVVFQmhNQ1JFVXhFakFRQmdOVkJBb1RDVk5UVDBOcGNtTnNa%0D%0AVEVhTUJnR0ExVUVBeE1SYVdSd0xuTnpiMk5wY21OcwpaUzVqYjIwd2dnRWlNQTBHQ1NxR1NJYjNE%0D%0AUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUNBd1dKeU9ZaFltV1pGMlRKdm0xVnlaY2NzCjNaSjBU%0D%0Ac05jb2F6cjJwVFdjWThXVFJiSVY5ZDA2ellqbmd2V2lieWl5bGV3R1hjWU9OQjEwNlpOVWROZ3Jt%0D%0ARmQ1MTk0V3N5eDZiUHYKbmpaRUVSbnk5TE9mdXdRYXFEWWVLaEk2Yyt2ZVhBcG5PZnNZMjZ1OUxx%0D%0AYjlzZ2E5Sm5Da1VHUmFvVnJBVk0zeWZnaHYvQ2cvUUVnKwpJNlNWRVM3NXRLZGNMRFR0L0Z3bUFZ%0D%0AREVCVjhsNTJiY01ETkYrSld0QXVldEk5L2RXQ0JlOVZUQ2FzQXIyRnh3MVpZVEFpcUdJOXNXCjRr%0D%0AV1MyQXBlZGJxc2dIM3FxTWxQQTd0ZzlpS3k4WXcvZGVFbjBxUUl4OEdsVm5RRnBEZ3pHOWsrandC%0D%0Ab2ViQVlmR3ZNY08vQkRYRDIKcGJXVE4rRHZiVVJsQWdNQkFBR2plekI1TUFrR0ExVWRFd1FDTUFB%0D%0Ad0xBWUpZSVpJQVliNFFnRU5CQjhXSFU5d1pXNVRVMHdnUjJWdQpaWEpoZEdWa0lFTmxjblJwWm1s%0D%0AallYUmxNQjBHQTFVZERnUVdCQlFoQW1DZXdFN2FvbkF2eUpmakltQ1JaRHRjY1RBZkJnTlZIU01F%0D%0ACkdEQVdnQlRBMW5FQSswemE2cHBMSXRrT1g1eUVwOGNRYVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9D%0D%0AQWdFQUFoQzUvV3NGOXp0SkhnbysKeDlLVjlicVZTME1tc2dwRzI2eU9BcUZZd09TUG1VdVltSm1I%0D%0AZ21LR2pLcmoxZmRDSU50emNCSEZGQkMxbWFHSjMzbE1rMmJNMlRIeAoyMi9POTNmNFJGbkZhYjd0%0D%0AMjNqUkZjRjBhbVFVT3NEdmx0Zkp3N1hDYWw4SmRnUFVnNlROQzRGeTlYWXYwT0FIYzNvRHAzdmwx%0D%0AWWo4Ci8xcUJnNlJjMzlrZWhtRDV2OFNLWW1wRTd5Rkt4REYxb2w5REtERy9MdkNsU3ZudVZQMGI0%0D%0AQldkQkFBOWFKU0Z0ZE5HZ0V2cEVVcUcKa0oxb3NMVnFDTXZTWXNVdEhtYXBhWDNoaU05UmJYMzhq%0D%0Ac1Nnc2w0NFJhcjVJb2M3S1hPT1pGR2ZFS3l5VXF1Y1lwaldDT1hKRUxBVgpBenA3WFR2QTJxNTV1%0D%0AMzFoTzB3OFl4NHVFUUtsbXhEdVpteHBNejRFV0FSeWpIU0F1REtFVzFSSnZVcjYrNXVBOXFlT0t4%0D%0ATGlLTjFqCm82ZVdBY2w2V3I5TXJlWFI5a0ZwUzZrSGxsZmRWU3JKRVM0U1QwdWgxSnA0RVlnbWl5%0D%0ATW1GQ2JVcEtYaWZwc05XQ0xEZW5FM2hsbEYKMCtxM3dJZHUrNFA4MlJJTTcxbjdxVmduRG5LMjl3%0D%0AbkxoSERhdDlya0M2MkNJYm9ucGtWWW1uUmVYMGp6ZSs3dHdSYW5KT01DSitsRgpnMTZCRHZCY0c4%0D%0AdTBuL3dJRGtISGl0Qkk3YlUxazZjNkR5ZExRKzY5aDhTQ282c085WXVEKy8zeEFHS2FkNEltWjZ2%0D%0AVHdsQjR6RENwCnU2WWdRV29jV1JYRStWa09iK1JCZnZQNzU1UFVhTGZMNjNBRlZscE9uRXBJaW81%0D%0AKytVak5KUnVQdUFBPQo8L2RzOlg1MDlDZXJ0aWZpY2F0ZT4KPC9kczpYNTA5RGF0YT4KPC9kczpL%0D%0AZXlJbmZvPgo8L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0Pgo8c2FtbDpOYW1lSUQgRm9ybWF0%0D%0APSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDp1bnNwZWNpZmllZCIg%0D%0ATmFtZVF1YWxpZmllcj0iaHR0cHM6Ly9pZHAuc3NvY2lyY2xlLmNvbSI%2BZmhhbmlrcGl2b3RhbDwv%0D%0Ac2FtbDpOYW1lSUQ%2BPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpu%0D%0AYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPgo8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0%0D%0AYSBJblJlc3BvbnNlVG89IkFSUTlhNzNlYWQtN2RjZi00NWE4LTg5ZWItMjZmM2M5OTAwYzM2IiBO%0D%0Ab3RPbk9yQWZ0ZXI9IjIwMTktMTItMTlUMTc6NDE6MjRaIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2Nh%0D%0AbGhvc3Q6ODA4MC9sb2dpbi9zYW1sMi9zc28vc3NvY2lyY2xlIi8%2BPC9zYW1sOlN1YmplY3RDb25m%0D%0AaXJtYXRpb24%2BCjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE5%0D%0ALTEyLTE5VDE3OjIxOjI0WiIgTm90T25PckFmdGVyPSIyMDE5LTEyLTE5VDE3OjQxOjI0WiI%2BCjxz%0D%0AYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24%2BCjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9sb2NhbGhvc3Q6%0D%0AODA4MC9zYW1sMi9zZXJ2aWNlLXByb3ZpZGVyLW1ldGFkYXRhL3Nzb2NpcmNsZTwvc2FtbDpBdWRp%0D%0AZW5jZT4KPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24%2BCjwvc2FtbDpDb25kaXRpb25zPgo8c2Ft%0D%0AbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTktMTItMTlUMTY6Mzg6NTFaIiBTZXNz%0D%0AaW9uSW5kZXg9InMyN2NkMjI4ZTIzMjNlZDM5MzM3YjM1NDcyODk5MmQ5ODRiMDNiYzEwMSI%2BPHNh%0D%0AbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1l%0D%0Aczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1s%0D%0AOkF1dGhuQ29udGV4dENsYXNzUmVmPjwvc2FtbDpBdXRobkNvbnRleHQ%2BPC9zYW1sOkF1dGhuU3Rh%0D%0AdGVtZW50PjxzYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iRW1h%0D%0AaWxBZGRyZXNzIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5v%0D%0AcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxT%0D%0AY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmZoYW5pa0BwaXZvdGFsLmlvPC9z%0D%0AYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU%2BPHNhbWw6QXR0cmlidXRlIE5hbWU9%0D%0AIlVzZXJJRCI%2BPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3Jn%0D%0ALzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2No%0D%0AZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5maGFuaWtwaXZvdGFsPC9zYW1sOkF0%0D%0AdHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU%2BPHNhbWw6QXR0cmlidXRlIE5hbWU9IkZpcnN0%0D%0ATmFtZSI%2BPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIw%0D%0AMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1h%0D%0ALWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5GaWxpcDwvc2FtbDpBdHRyaWJ1dGVWYWx1%0D%0AZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJMYXN0TmFtZSI%2BPHNhbWw6%0D%0AQXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1h%0D%0AIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4%0D%0Ac2k6dHlwZT0ieHM6c3RyaW5nIj5IYW5pazwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0%0D%0AcmlidXRlPjwvc2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ%2BPC9zYW1sOkFzc2VydGlvbj48L3NhbWxw%0D%0AOlJlc3BvbnNlPg%3D%3D\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/resources/test-entitiesdescriptor.xml",
    "content": "<md:EntitiesDescriptor\txmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\n\t\t\t\t\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t\t\t\txmlns:shibmd=\"urn:mace:shibboleth:metadata:1.0\"\n\t\t\t\t\t\txmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n\t\t\t\t\t\txmlns:mdui=\"urn:oasis:names:tc:SAML:metadata:ui\">\n\t<md:EntityDescriptor entityID=\"https://idp.example.com/idp/shibboleth\">\n\n\t\t<md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n\t\t\t<md:Extensions>\n\t\t\t\t<shibmd:Scope regexp=\"false\">example.com</shibmd:Scope>\n\n\t\t\t\t<mdui:UIInfo>\n\t\t\t\t\t<mdui:DisplayName xml:lang=\"en\">\n\t\t\t\t\t\tConsortium GARR IdP\n\t\t\t\t\t</mdui:DisplayName>\n\t\t\t\t\t<mdui:DisplayName xml:lang=\"it\">\n\t\t\t\t\t\tConsortium GARR IdP\n\t\t\t\t\t</mdui:DisplayName>\n\n\t\t\t\t\t<mdui:Description xml:lang=\"en\">\n\t\t\t\t\t\tThis Identity Provider gives support for the Consortium GARR's user community\n\t\t\t\t\t</mdui:Description>\n\t\t\t\t\t<mdui:Description xml:lang=\"it\">\n\t\t\t\t\t\tQuesto Identity Provider di test fornisce supporto alla comunita' utenti GARR\n\t\t\t\t\t</mdui:Description>\n\t\t\t\t</mdui:UIInfo>\n\t\t\t</md:Extensions>\n\n\t\t\t<md:KeyDescriptor>\n\t\t\t\t<ds:KeyInfo>\n\t\t\t\t\t<ds:X509Data>\n\t\t\t\t\t\t<ds:X509Certificate>\n\t\t\t\t\t\t\tMIIDZjCCAk6gAwIBAgIVAL9O+PA7SXtlwZZY8MVSE9On1cVWMA0GCSqGSIb3DQEB\n\t\t\t\t\t\t\tBQUAMCkxJzAlBgNVBAMTHmlkZW0tcHVwYWdlbnQuZG16LWludC51bmltby5pdDAe\n\t\t\t\t\t\t\tFw0xMzA3MjQwMDQ0MTRaFw0zMzA3MjQwMDQ0MTRaMCkxJzAlBgNVBAMTHmlkZW0t\n\t\t\t\t\t\t\tcHVwYWdlbnQuZG16LWludC51bmltby5pdDCCASIwDQYJKoZIhvcNAMIIDQADggEP\n\t\t\t\t\t\t\tADCCAQoCggEBAIAcp/VyzZGXUF99kwj4NvL/Rwv4YvBgLWzpCuoxqHZ/hmBwJtqS\n\t\t\t\t\t\t\tv0y9METBPFbgsF3hCISnxbcmNVxf/D0MoeKtw1YPbsUmow/bFe+r72hZ+IVAcejN\n\t\t\t\t\t\t\tiDJ7t5oTjsRN1t1SqvVVk6Ryk5AZhpFW+W9pE9N6c7kJ16Rp2/mbtax9OCzxpece\n\t\t\t\t\t\t\tbyi1eiLfIBmkcRawL/vCc2v6VLI18i6HsNVO3l2yGosKCbuSoGDx2fCdAOk/rgdz\n\t\t\t\t\t\t\tcWOvFsIZSKuD+FVbSS/J9GVs7yotsS4PRl4iX9UMnfDnOMfO7bcBgbXtDl4SCU1v\n\t\t\t\t\t\t\tdJrRw7IL/pLz34Rv9a8nYitrzrxtLOp3nYUCAwEAAaOBhDCBgTBgBgMIIDEEWTBX\n\t\t\t\t\t\t\tgh5pZGVtLXB1cGFnZW50LmRtei1pbnQudW5pbW8uaXSGNWh0dHBzOi8vaWRlbS1w\n\t\t\t\t\t\t\tdXBhZ2VudC5kbXotaW50LnVuaW1vLml0L2lkcC9zaGliYm9sZXRoMB0GA1UdDgQW\n\t\t\t\t\t\t\tBBT8PANzz+adGnTRe8ldcyxAwe4VnzANBgkqhkiG9w0BAQUFAAOCAQEAOEnO8Clu\n\t\t\t\t\t\t\t9z/Lf/8XOOsTdxJbV29DIF3G8KoQsB3dBsLwPZVEAQIP6ceS32Xaxrl6FMTDDNkL\n\t\t\t\t\t\t\tqUvvInUisw0+I5zZwYHybJQCletUWTnz58SC4C9G7FpuXHFZnOGtRcgGD1NOX4UU\n\t\t\t\t\t\t\tduus/4nVcGSLhDjszZ70Xtj0gw2Sn46oQPHTJ81QZ3Y9ih+Aj1c9OtUSBwtWZFkU\n\t\t\t\t\t\t\tyooAKoR8li68Yb21zN2N65AqV+ndL98M8xUYMKLONuAXStDeoVCipH6PJ09Z5U2p\n\t\t\t\t\t\t\tV5p4IQRV6QBsNw9CISJFuHzkVYTH5ZxzN80Ru46vh4y2M0Nu8GQ9I085KoZkrf5e\n\t\t\t\t\t\t\tCq53OZt9ISjHEw==\n\t\t\t\t\t\t</ds:X509Certificate>\n\t\t\t\t\t</ds:X509Data>\n\t\t\t\t</ds:KeyInfo>\n\t\t\t</md:KeyDescriptor>\n\n\t\t\t<md:SingleSignOnService\n\t\t\t\t\tBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n\t\t\t\t\tLocation=\"https://idp.example.com/idp/profile/SAML2/POST/SSO\"/>\n\t\t</md:IDPSSODescriptor>\n\n\t\t<md:Organization>\n\t\t\t<md:OrganizationName xml:lang=\"en\">\n\t\t\t\tConsortium GARR\n\t\t\t</md:OrganizationName>\n\t\t\t<md:OrganizationName xml:lang=\"it\">\n\t\t\t\tConsortium GARR\n\t\t\t</md:OrganizationName>\n\n\t\t\t<md:OrganizationDisplayName xml:lang=\"en\">\n\t\t\t\tConsortium GARR\n\t\t\t</md:OrganizationDisplayName>\n\t\t\t<md:OrganizationDisplayName xml:lang=\"it\">\n\t\t\t\tConsortium GARR\n\t\t\t</md:OrganizationDisplayName>\n\n\t\t\t<md:OrganizationURL xml:lang=\"it\">\n\t\t\t\thttps://example.org\n\t\t\t</md:OrganizationURL>\n\t\t</md:Organization>\n\n\t\t<md:ContactPerson contactType=\"technical\">\n\t\t\t<md:EmailAddress>mailto:technical.contact@example.com</md:EmailAddress>\n\t\t</md:ContactPerson>\n\n\t</md:EntityDescriptor>\n\t<md:EntityDescriptor entityID=\"https://ap.example.org/idp/shibboleth\">\n\n\t\t<md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n\t\t\t<md:Extensions>\n\t\t\t\t<shibmd:Scope regexp=\"false\">example.org</shibmd:Scope>\n\n\t\t\t\t<mdui:UIInfo>\n\t\t\t\t\t<mdui:DisplayName xml:lang=\"en\">\n\t\t\t\t\t\tConsortium GARR IdP\n\t\t\t\t\t</mdui:DisplayName>\n\t\t\t\t\t<mdui:DisplayName xml:lang=\"it\">\n\t\t\t\t\t\tConsortium GARR IdP\n\t\t\t\t\t</mdui:DisplayName>\n\n\t\t\t\t\t<mdui:Description xml:lang=\"en\">\n\t\t\t\t\t\tThis Identity Provider gives support for the Consortium GARR's user community\n\t\t\t\t\t</mdui:Description>\n\t\t\t\t\t<mdui:Description xml:lang=\"it\">\n\t\t\t\t\t\tQuesto Identity Provider di test fornisce supporto alla comunita' utenti GARR\n\t\t\t\t\t</mdui:Description>\n\t\t\t\t</mdui:UIInfo>\n\t\t\t</md:Extensions>\n\n\t\t\t<md:KeyDescriptor>\n\t\t\t\t<ds:KeyInfo>\n\t\t\t\t\t<ds:X509Data>\n\t\t\t\t\t\t<ds:X509Certificate>\n\t\t\t\t\t\t\tMIIDZjCCAk6gAwIBAgIVAL9O+PA7SXtlwZZY8MVSE9On1cVWMA0GCSqGSIb3DQEB\n\t\t\t\t\t\t\tBQUAMCkxJzAlBgNVBAMTHmlkZW0tcHVwYWdlbnQuZG16LWludC51bmltby5pdDAe\n\t\t\t\t\t\t\tFw0xMzA3MjQwMDQ0MTRaFw0zMzA3MjQwMDQ0MTRaMCkxJzAlBgNVBAMTHmlkZW0t\n\t\t\t\t\t\t\tcHVwYWdlbnQuZG16LWludC51bmltby5pdDCCASIwDQYJKoZIhvcNAMIIDQADggEP\n\t\t\t\t\t\t\tADCCAQoCggEBAIAcp/VyzZGXUF99kwj4NvL/Rwv4YvBgLWzpCuoxqHZ/hmBwJtqS\n\t\t\t\t\t\t\tv0y9METBPFbgsF3hCISnxbcmNVxf/D0MoeKtw1YPbsUmow/bFe+r72hZ+IVAcejN\n\t\t\t\t\t\t\tiDJ7t5oTjsRN1t1SqvVVk6Ryk5AZhpFW+W9pE9N6c7kJ16Rp2/mbtax9OCzxpece\n\t\t\t\t\t\t\tbyi1eiLfIBmkcRawL/vCc2v6VLI18i6HsNVO3l2yGosKCbuSoGDx2fCdAOk/rgdz\n\t\t\t\t\t\t\tcWOvFsIZSKuD+FVbSS/J9GVs7yotsS4PRl4iX9UMnfDnOMfO7bcBgbXtDl4SCU1v\n\t\t\t\t\t\t\tdJrRw7IL/pLz34Rv9a8nYitrzrxtLOp3nYUCAwEAAaOBhDCBgTBgBgMIIDEEWTBX\n\t\t\t\t\t\t\tgh5pZGVtLXB1cGFnZW50LmRtei1pbnQudW5pbW8uaXSGNWh0dHBzOi8vaWRlbS1w\n\t\t\t\t\t\t\tdXBhZ2VudC5kbXotaW50LnVuaW1vLml0L2lkcC9zaGliYm9sZXRoMB0GA1UdDgQW\n\t\t\t\t\t\t\tBBT8PANzz+adGnTRe8ldcyxAwe4VnzANBgkqhkiG9w0BAQUFAAOCAQEAOEnO8Clu\n\t\t\t\t\t\t\t9z/Lf/8XOOsTdxJbV29DIF3G8KoQsB3dBsLwPZVEAQIP6ceS32Xaxrl6FMTDDNkL\n\t\t\t\t\t\t\tqUvvInUisw0+I5zZwYHybJQCletUWTnz58SC4C9G7FpuXHFZnOGtRcgGD1NOX4UU\n\t\t\t\t\t\t\tduus/4nVcGSLhDjszZ70Xtj0gw2Sn46oQPHTJ81QZ3Y9ih+Aj1c9OtUSBwtWZFkU\n\t\t\t\t\t\t\tyooAKoR8li68Yb21zN2N65AqV+ndL98M8xUYMKLONuAXStDeoVCipH6PJ09Z5U2p\n\t\t\t\t\t\t\tV5p4IQRV6QBsNw9CISJFuHzkVYTH5ZxzN80Ru46vh4y2M0Nu8GQ9I085KoZkrf5e\n\t\t\t\t\t\t\tCq53OZt9ISjHEw==\n\t\t\t\t\t\t</ds:X509Certificate>\n\t\t\t\t\t</ds:X509Data>\n\t\t\t\t</ds:KeyInfo>\n\t\t\t</md:KeyDescriptor>\n\n\t\t\t<md:SingleSignOnService\n\t\t\t\t\tBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n\t\t\t\t\tLocation=\"https://ap.example.org/idp/profile/SAML2/POST/SSO\"/>\n\t\t</md:IDPSSODescriptor>\n\n\t\t<md:Organization>\n\t\t\t<md:OrganizationName xml:lang=\"en\">\n\t\t\t\tConsortium GARR\n\t\t\t</md:OrganizationName>\n\t\t\t<md:OrganizationName xml:lang=\"it\">\n\t\t\t\tConsortium GARR\n\t\t\t</md:OrganizationName>\n\n\t\t\t<md:OrganizationDisplayName xml:lang=\"en\">\n\t\t\t\tConsortium GARR\n\t\t\t</md:OrganizationDisplayName>\n\t\t\t<md:OrganizationDisplayName xml:lang=\"it\">\n\t\t\t\tConsortium GARR\n\t\t\t</md:OrganizationDisplayName>\n\n\t\t\t<md:OrganizationURL xml:lang=\"it\">\n\t\t\t\thttps://example.org\n\t\t\t</md:OrganizationURL>\n\t\t</md:Organization>\n\n\t\t<md:ContactPerson contactType=\"technical\">\n\t\t\t<md:EmailAddress>mailto:technical.contact@example.org</md:EmailAddress>\n\t\t</md:ContactPerson>\n\n\t</md:EntityDescriptor>\n</md:EntitiesDescriptor>\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/resources/test-federated-metadata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<md:EntitiesDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" ID=\"federation_root\"\n\t\t\t\t\t   cacheDuration=\"P0Y0M0DT0H15M0.000S\" validUntil=\"2099-03-04T20:18:29.383Z\">\n\t<md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\n\t\t\t\t\t\t entityID=\"https://localhost/simplesaml/saml2/idp/metadata.php\">\n\t\t<md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n\t\t\t<md:KeyDescriptor use=\"signing\">\n\t\t\t\t<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n\t\t\t\t\t<ds:X509Data>\n\t\t\t\t\t\t<ds:X509Certificate>\n\t\t\t\t\t\t\tMIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ==\n\t\t\t\t\t\t</ds:X509Certificate>\n\t\t\t\t\t</ds:X509Data>\n\t\t\t\t</ds:KeyInfo>\n\t\t\t</md:KeyDescriptor>\n\t\t\t<md:KeyDescriptor use=\"encryption\">\n\t\t\t\t<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n\t\t\t\t\t<ds:X509Data>\n\t\t\t\t\t\t<ds:X509Certificate>\n\t\t\t\t\t\t\tMIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ==\n\t\t\t\t\t\t</ds:X509Certificate>\n\t\t\t\t\t</ds:X509Data>\n\t\t\t\t</ds:KeyInfo>\n\t\t\t</md:KeyDescriptor>\n\t\t\t<md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n\t\t\t\t\t\t\t\t\tLocation=\"https://localhost/simplesaml/saml2/idp/SingleLogoutService.php\"/>\n\t\t\t<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>\n\t\t\t<md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n\t\t\t\t\t\t\t\t\tLocation=\"https://localhost/simplesaml/saml2/idp/SSOService.php\"/>\n\t\t</md:IDPSSODescriptor>\n\t</md:EntityDescriptor>\n\t<md:EntityDescriptor entityID=\"https://service.provider.org\">\n\t\t<md:SPSSODescriptor AuthnRequestsSigned=\"true\" WantAssertionsSigned=\"true\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n\t\t\t<md:Extensions>\n\t\t\t\t<idpdisco:DiscoveryResponse xmlns:idpdisco=\"urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol\" Binding=\"urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol\" Location=\"https://nomp.se/saml/login?disco=true\" index=\"0\"/>\n\t\t\t</md:Extensions>\n\t\t\t<md:KeyDescriptor use=\"signing\">\n\t\t\t\t<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n\t\t\t\t\t<ds:X509Data>\n\t\t\t\t\t\t<ds:X509Certificate>MIIDhzCCAm+gAwIBAgIEQ4NWOjANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJTRTESMBAGA1UE CBMJU3RvY2tob2xtMRMwEQYDVQQHEwpTdW5kYnliZXJnMRQwEgYDVQQKEwtTZWxlc3NpYSBBQjEN MAsGA1UECxMETk9NUDEXMBUGA1UEAxMOU3RlZmFuIE5vcmJlcmcwHhcNMTgwNzAxMTEzODUwWhcN MzgwNjI2MTEzODUwWjB0MQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMRMwEQYDVQQH EwpTdW5kYnliZXJnMRQwEgYDVQQKEwtTZWxlc3NpYSBBQjENMAsGA1UECxMETk9NUDEXMBUGA1UE AxMOU3RlZmFuIE5vcmJlcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxGtC9ZwND QipHu5MslBANi/+k9CQPK4uHrfmVl8porr8pUWDlpVIGnfbJSc/glZQXCy/xbi79RfF/sFsTrmlb acMSSwwA0TYjJPBsx/MUBKdYQaei91b2IhP2yLSCWug+/A4fF3l/kUcqtX3SPhXpAESjbapyrKzp n1KWjDl7anV/kelOYdFGDATQWhUnslMml1hSeOgaaKQIbFzUH5yOw4RQ52zQkYP8wXF3h8BSP3LD tlSjP1Owme+UDjD+517zCaYHqV0RexDMU7h30m5a6YQeDdhJU02Ene86WhFfssqC+4HpL5g8KcbF T8vYY7Phe/7NqxUYXCaQlxTYHWWdAgMBAAGjITAfMB0GA1UdDgQWBBTv2MiZukGzYLRO/UsRUjvW AreSATANBgkqhkiG9w0BAQsFAAOCAQEACPkF8vkFWNEJDYsuNINKo3qUD9351gjHXo8ZNBbPzi23 xvMWHObYtkZb8+CGxEzI41hhZDnUSIu3CrpwVkf26hnKC6TyrdPsURN1CkdBwcUzjFdo3ZkZo4Uu RJtDBcn/DdZ86mMkEArojWzgleZCe37+7hEm5K/sRuxdT9wfqzprw9tOp/b7Y8423yGwW3+E+aef pKxbZyLCkabo1CT54PoCuypfNcQsSRDF0rmA0mQwfcmgVVkiNPkvQFO6VuNJsQjesxMN3QXSJf7v yqB3Y0IzGVC669FHsEF178Re0WJn4GwIR2UronR38dVdGEEMesyMPgwbww7U77qUkQLdug==</ds:X509Certificate>\n\t\t\t\t\t</ds:X509Data>\n\t\t\t\t</ds:KeyInfo>\n\t\t\t</md:KeyDescriptor>\n\t\t\t<md:KeyDescriptor use=\"encryption\">\n\t\t\t\t<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n\t\t\t\t\t<ds:X509Data>\n\t\t\t\t\t\t<ds:X509Certificate>MIIDhzCCAm+gAwIBAgIEQ4NWOjANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJTRTESMBAGA1UE CBMJU3RvY2tob2xtMRMwEQYDVQQHEwpTdW5kYnliZXJnMRQwEgYDVQQKEwtTZWxlc3NpYSBBQjEN MAsGA1UECxMETk9NUDEXMBUGA1UEAxMOU3RlZmFuIE5vcmJlcmcwHhcNMTgwNzAxMTEzODUwWhcN MzgwNjI2MTEzODUwWjB0MQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMRMwEQYDVQQH EwpTdW5kYnliZXJnMRQwEgYDVQQKEwtTZWxlc3NpYSBBQjENMAsGA1UECxMETk9NUDEXMBUGA1UE AxMOU3RlZmFuIE5vcmJlcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxGtC9ZwND QipHu5MslBANi/+k9CQPK4uHrfmVl8porr8pUWDlpVIGnfbJSc/glZQXCy/xbi79RfF/sFsTrmlb acMSSwwA0TYjJPBsx/MUBKdYQaei91b2IhP2yLSCWug+/A4fF3l/kUcqtX3SPhXpAESjbapyrKzp n1KWjDl7anV/kelOYdFGDATQWhUnslMml1hSeOgaaKQIbFzUH5yOw4RQ52zQkYP8wXF3h8BSP3LD tlSjP1Owme+UDjD+517zCaYHqV0RexDMU7h30m5a6YQeDdhJU02Ene86WhFfssqC+4HpL5g8KcbF T8vYY7Phe/7NqxUYXCaQlxTYHWWdAgMBAAGjITAfMB0GA1UdDgQWBBTv2MiZukGzYLRO/UsRUjvW AreSATANBgkqhkiG9w0BAQsFAAOCAQEACPkF8vkFWNEJDYsuNINKo3qUD9351gjHXo8ZNBbPzi23 xvMWHObYtkZb8+CGxEzI41hhZDnUSIu3CrpwVkf26hnKC6TyrdPsURN1CkdBwcUzjFdo3ZkZo4Uu RJtDBcn/DdZ86mMkEArojWzgleZCe37+7hEm5K/sRuxdT9wfqzprw9tOp/b7Y8423yGwW3+E+aef pKxbZyLCkabo1CT54PoCuypfNcQsSRDF0rmA0mQwfcmgVVkiNPkvQFO6VuNJsQjesxMN3QXSJf7v yqB3Y0IzGVC669FHsEF178Re0WJn4GwIR2UronR38dVdGEEMesyMPgwbww7U77qUkQLdug==</ds:X509Certificate>\n\t\t\t\t\t</ds:X509Data>\n\t\t\t\t</ds:KeyInfo>\n\t\t\t</md:KeyDescriptor>\n\t\t\t<md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://sp.provider.org/saml/SingleLogout\"/>\n\t\t\t<md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://sp.provider.org/saml/SingleLogout\"/>\n\t\t\t<md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://sp.provider.org/saml/SSO\" index=\"0\" isDefault=\"true\"/>\n\t\t\t<md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\" Location=\"https://sp.provider.org/saml/SSO\" index=\"1\"/>\n\t\t\t<md:AttributeConsumingService index=\"0\">\n\t\t\t\t<md:ServiceName xml:lang=\"en\">The SP</md:ServiceName>\n\t\t\t\t<md:RequestedAttribute FriendlyName=\"mail\" Name=\"urn:oid:0.9.2342.19200300.100.1.3\" isRequired=\"true\"/>\n\t\t\t\t<md:RequestedAttribute FriendlyName=\"eduPersonPrincipalName\" Name=\"urn:oid:1.3.6.1.4.1.5923.1.1.1.6\" isRequired=\"true\"/>\n\t\t\t\t<md:RequestedAttribute FriendlyName=\"givenName\" Name=\"urn:oid:2.5.4.42\"/>\n\t\t\t\t<md:RequestedAttribute FriendlyName=\"surName\" Name=\"urn:oid:2.5.4.4\"/>\n\t\t\t</md:AttributeConsumingService>\n\t\t</md:SPSSODescriptor>\n\t\t<md:Organization>\n\t\t\t<md:OrganizationName xml:lang=\"en\">Service Provider</md:OrganizationName>\n\t\t\t<md:OrganizationDisplayName xml:lang=\"en\">Service Provider</md:OrganizationDisplayName>\n\t\t</md:Organization>\n\t</md:EntityDescriptor>\n</md:EntitiesDescriptor>\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/resources/test-metadata-without-idp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<md:EntitiesDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" ID=\"federation_root\"\n\t\t\t\t\t   cacheDuration=\"P0Y0M0DT0H15M0.000S\" validUntil=\"2099-03-04T20:18:29.383Z\">\n\t<md:EntityDescriptor entityID=\"https://service.provider.org\">\n\t\t<md:SPSSODescriptor AuthnRequestsSigned=\"true\" WantAssertionsSigned=\"true\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n\t\t\t<md:Extensions>\n\t\t\t\t<idpdisco:DiscoveryResponse xmlns:idpdisco=\"urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol\" Binding=\"urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol\" Location=\"https://nomp.se/saml/login?disco=true\" index=\"0\"/>\n\t\t\t</md:Extensions>\n\t\t\t<md:KeyDescriptor use=\"signing\">\n\t\t\t\t<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n\t\t\t\t\t<ds:X509Data>\n\t\t\t\t\t\t<ds:X509Certificate>MIIDhzCCAm+gAwIBAgIEQ4NWOjANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJTRTESMBAGA1UE CBMJU3RvY2tob2xtMRMwEQYDVQQHEwpTdW5kYnliZXJnMRQwEgYDVQQKEwtTZWxlc3NpYSBBQjEN MAsGA1UECxMETk9NUDEXMBUGA1UEAxMOU3RlZmFuIE5vcmJlcmcwHhcNMTgwNzAxMTEzODUwWhcN MzgwNjI2MTEzODUwWjB0MQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMRMwEQYDVQQH EwpTdW5kYnliZXJnMRQwEgYDVQQKEwtTZWxlc3NpYSBBQjENMAsGA1UECxMETk9NUDEXMBUGA1UE AxMOU3RlZmFuIE5vcmJlcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxGtC9ZwND QipHu5MslBANi/+k9CQPK4uHrfmVl8porr8pUWDlpVIGnfbJSc/glZQXCy/xbi79RfF/sFsTrmlb acMSSwwA0TYjJPBsx/MUBKdYQaei91b2IhP2yLSCWug+/A4fF3l/kUcqtX3SPhXpAESjbapyrKzp n1KWjDl7anV/kelOYdFGDATQWhUnslMml1hSeOgaaKQIbFzUH5yOw4RQ52zQkYP8wXF3h8BSP3LD tlSjP1Owme+UDjD+517zCaYHqV0RexDMU7h30m5a6YQeDdhJU02Ene86WhFfssqC+4HpL5g8KcbF T8vYY7Phe/7NqxUYXCaQlxTYHWWdAgMBAAGjITAfMB0GA1UdDgQWBBTv2MiZukGzYLRO/UsRUjvW AreSATANBgkqhkiG9w0BAQsFAAOCAQEACPkF8vkFWNEJDYsuNINKo3qUD9351gjHXo8ZNBbPzi23 xvMWHObYtkZb8+CGxEzI41hhZDnUSIu3CrpwVkf26hnKC6TyrdPsURN1CkdBwcUzjFdo3ZkZo4Uu RJtDBcn/DdZ86mMkEArojWzgleZCe37+7hEm5K/sRuxdT9wfqzprw9tOp/b7Y8423yGwW3+E+aef pKxbZyLCkabo1CT54PoCuypfNcQsSRDF0rmA0mQwfcmgVVkiNPkvQFO6VuNJsQjesxMN3QXSJf7v yqB3Y0IzGVC669FHsEF178Re0WJn4GwIR2UronR38dVdGEEMesyMPgwbww7U77qUkQLdug==</ds:X509Certificate>\n\t\t\t\t\t</ds:X509Data>\n\t\t\t\t</ds:KeyInfo>\n\t\t\t</md:KeyDescriptor>\n\t\t\t<md:KeyDescriptor use=\"encryption\">\n\t\t\t\t<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n\t\t\t\t\t<ds:X509Data>\n\t\t\t\t\t\t<ds:X509Certificate>MIIDhzCCAm+gAwIBAgIEQ4NWOjANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJTRTESMBAGA1UE CBMJU3RvY2tob2xtMRMwEQYDVQQHEwpTdW5kYnliZXJnMRQwEgYDVQQKEwtTZWxlc3NpYSBBQjEN MAsGA1UECxMETk9NUDEXMBUGA1UEAxMOU3RlZmFuIE5vcmJlcmcwHhcNMTgwNzAxMTEzODUwWhcN MzgwNjI2MTEzODUwWjB0MQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMRMwEQYDVQQH EwpTdW5kYnliZXJnMRQwEgYDVQQKEwtTZWxlc3NpYSBBQjENMAsGA1UECxMETk9NUDEXMBUGA1UE AxMOU3RlZmFuIE5vcmJlcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxGtC9ZwND QipHu5MslBANi/+k9CQPK4uHrfmVl8porr8pUWDlpVIGnfbJSc/glZQXCy/xbi79RfF/sFsTrmlb acMSSwwA0TYjJPBsx/MUBKdYQaei91b2IhP2yLSCWug+/A4fF3l/kUcqtX3SPhXpAESjbapyrKzp n1KWjDl7anV/kelOYdFGDATQWhUnslMml1hSeOgaaKQIbFzUH5yOw4RQ52zQkYP8wXF3h8BSP3LD tlSjP1Owme+UDjD+517zCaYHqV0RexDMU7h30m5a6YQeDdhJU02Ene86WhFfssqC+4HpL5g8KcbF T8vYY7Phe/7NqxUYXCaQlxTYHWWdAgMBAAGjITAfMB0GA1UdDgQWBBTv2MiZukGzYLRO/UsRUjvW AreSATANBgkqhkiG9w0BAQsFAAOCAQEACPkF8vkFWNEJDYsuNINKo3qUD9351gjHXo8ZNBbPzi23 xvMWHObYtkZb8+CGxEzI41hhZDnUSIu3CrpwVkf26hnKC6TyrdPsURN1CkdBwcUzjFdo3ZkZo4Uu RJtDBcn/DdZ86mMkEArojWzgleZCe37+7hEm5K/sRuxdT9wfqzprw9tOp/b7Y8423yGwW3+E+aef pKxbZyLCkabo1CT54PoCuypfNcQsSRDF0rmA0mQwfcmgVVkiNPkvQFO6VuNJsQjesxMN3QXSJf7v yqB3Y0IzGVC669FHsEF178Re0WJn4GwIR2UronR38dVdGEEMesyMPgwbww7U77qUkQLdug==</ds:X509Certificate>\n\t\t\t\t\t</ds:X509Data>\n\t\t\t\t</ds:KeyInfo>\n\t\t\t</md:KeyDescriptor>\n\t\t\t<md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://sp.provider.org/saml/SingleLogout\"/>\n\t\t\t<md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://sp.provider.org/saml/SingleLogout\"/>\n\t\t\t<md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://sp.provider.org/saml/SSO\" index=\"0\" isDefault=\"true\"/>\n\t\t\t<md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\" Location=\"https://sp.provider.org/saml/SSO\" index=\"1\"/>\n\t\t\t<md:AttributeConsumingService index=\"0\">\n\t\t\t\t<md:ServiceName xml:lang=\"en\">The SP</md:ServiceName>\n\t\t\t\t<md:RequestedAttribute FriendlyName=\"mail\" Name=\"urn:oid:0.9.2342.19200300.100.1.3\" isRequired=\"true\"/>\n\t\t\t\t<md:RequestedAttribute FriendlyName=\"eduPersonPrincipalName\" Name=\"urn:oid:1.3.6.1.4.1.5923.1.1.1.6\" isRequired=\"true\"/>\n\t\t\t\t<md:RequestedAttribute FriendlyName=\"givenName\" Name=\"urn:oid:2.5.4.42\"/>\n\t\t\t\t<md:RequestedAttribute FriendlyName=\"surName\" Name=\"urn:oid:2.5.4.4\"/>\n\t\t\t</md:AttributeConsumingService>\n\t\t</md:SPSSODescriptor>\n\t\t<md:Organization>\n\t\t\t<md:OrganizationName xml:lang=\"en\">Service Provider</md:OrganizationName>\n\t\t\t<md:OrganizationDisplayName xml:lang=\"en\">Service Provider</md:OrganizationDisplayName>\n\t\t</md:Organization>\n\t</md:EntityDescriptor>\n</md:EntitiesDescriptor>\n"
  },
  {
    "path": "saml2/saml2-service-provider/src/test/resources/test-metadata.xml",
    "content": "<md:EntityDescriptor entityID=\"https://idp.example.com/idp/shibboleth\"\n\t\t\t\t\t xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\n\t\t\t\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t\t\t xmlns:shibmd=\"urn:mace:shibboleth:metadata:1.0\"\n\t\t\t\t\t xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n\t\t\t\t\t xmlns:mdui=\"urn:oasis:names:tc:SAML:metadata:ui\">\n\n\t<md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n\t\t<md:Extensions>\n\t\t\t<shibmd:Scope regexp=\"false\">example.com</shibmd:Scope>\n\n\t\t\t<mdui:UIInfo>\n\t\t\t\t<mdui:DisplayName xml:lang=\"en\">\n\t\t\t\t\tConsortium GARR IdP\n\t\t\t\t</mdui:DisplayName>\n\t\t\t\t<mdui:DisplayName xml:lang=\"it\">\n\t\t\t\t\tConsortium GARR IdP\n\t\t\t\t</mdui:DisplayName>\n\n\t\t\t\t<mdui:Description xml:lang=\"en\">\n\t\t\t\t\tThis Identity Provider gives support for the Consortium GARR's user community\n\t\t\t\t</mdui:Description>\n\t\t\t\t<mdui:Description xml:lang=\"it\">\n\t\t\t\t\tQuesto Identity Provider di test fornisce supporto alla comunita' utenti GARR\n\t\t\t\t</mdui:Description>\n\t\t\t</mdui:UIInfo>\n\t\t</md:Extensions>\n\n\t\t<md:KeyDescriptor>\n\t\t\t<ds:KeyInfo>\n\t\t\t\t<ds:X509Data>\n\t\t\t\t\t<ds:X509Certificate>\n\t\t\t\t\t\tMIIDZjCCAk6gAwIBAgIVAL9O+PA7SXtlwZZY8MVSE9On1cVWMA0GCSqGSIb3DQEB\n\t\t\t\t\t\tBQUAMCkxJzAlBgNVBAMTHmlkZW0tcHVwYWdlbnQuZG16LWludC51bmltby5pdDAe\n\t\t\t\t\t\tFw0xMzA3MjQwMDQ0MTRaFw0zMzA3MjQwMDQ0MTRaMCkxJzAlBgNVBAMTHmlkZW0t\n\t\t\t\t\t\tcHVwYWdlbnQuZG16LWludC51bmltby5pdDCCASIwDQYJKoZIhvcNAMIIDQADggEP\n\t\t\t\t\t\tADCCAQoCggEBAIAcp/VyzZGXUF99kwj4NvL/Rwv4YvBgLWzpCuoxqHZ/hmBwJtqS\n\t\t\t\t\t\tv0y9METBPFbgsF3hCISnxbcmNVxf/D0MoeKtw1YPbsUmow/bFe+r72hZ+IVAcejN\n\t\t\t\t\t\tiDJ7t5oTjsRN1t1SqvVVk6Ryk5AZhpFW+W9pE9N6c7kJ16Rp2/mbtax9OCzxpece\n\t\t\t\t\t\tbyi1eiLfIBmkcRawL/vCc2v6VLI18i6HsNVO3l2yGosKCbuSoGDx2fCdAOk/rgdz\n\t\t\t\t\t\tcWOvFsIZSKuD+FVbSS/J9GVs7yotsS4PRl4iX9UMnfDnOMfO7bcBgbXtDl4SCU1v\n\t\t\t\t\t\tdJrRw7IL/pLz34Rv9a8nYitrzrxtLOp3nYUCAwEAAaOBhDCBgTBgBgMIIDEEWTBX\n\t\t\t\t\t\tgh5pZGVtLXB1cGFnZW50LmRtei1pbnQudW5pbW8uaXSGNWh0dHBzOi8vaWRlbS1w\n\t\t\t\t\t\tdXBhZ2VudC5kbXotaW50LnVuaW1vLml0L2lkcC9zaGliYm9sZXRoMB0GA1UdDgQW\n\t\t\t\t\t\tBBT8PANzz+adGnTRe8ldcyxAwe4VnzANBgkqhkiG9w0BAQUFAAOCAQEAOEnO8Clu\n\t\t\t\t\t\t9z/Lf/8XOOsTdxJbV29DIF3G8KoQsB3dBsLwPZVEAQIP6ceS32Xaxrl6FMTDDNkL\n\t\t\t\t\t\tqUvvInUisw0+I5zZwYHybJQCletUWTnz58SC4C9G7FpuXHFZnOGtRcgGD1NOX4UU\n\t\t\t\t\t\tduus/4nVcGSLhDjszZ70Xtj0gw2Sn46oQPHTJ81QZ3Y9ih+Aj1c9OtUSBwtWZFkU\n\t\t\t\t\t\tyooAKoR8li68Yb21zN2N65AqV+ndL98M8xUYMKLONuAXStDeoVCipH6PJ09Z5U2p\n\t\t\t\t\t\tV5p4IQRV6QBsNw9CISJFuHzkVYTH5ZxzN80Ru46vh4y2M0Nu8GQ9I085KoZkrf5e\n\t\t\t\t\t\tCq53OZt9ISjHEw==\n\t\t\t\t\t</ds:X509Certificate>\n\t\t\t\t</ds:X509Data>\n\t\t\t</ds:KeyInfo>\n\t\t</md:KeyDescriptor>\n\n\t\t<md:SingleSignOnService\n\t\t\t\tBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n\t\t\t\tLocation=\"https://idp.example.com/idp/profile/SAML2/POST/SSO\"/>\n\t</md:IDPSSODescriptor>\n\n\t<md:Organization>\n\t\t<md:OrganizationName xml:lang=\"en\">\n\t\t\tConsortium GARR\n\t\t</md:OrganizationName>\n\t\t<md:OrganizationName xml:lang=\"it\">\n\t\t\tConsortium GARR\n\t\t</md:OrganizationName>\n\n\t\t<md:OrganizationDisplayName xml:lang=\"en\">\n\t\t\tConsortium GARR\n\t\t</md:OrganizationDisplayName>\n\t\t<md:OrganizationDisplayName xml:lang=\"it\">\n\t\t\tConsortium GARR\n\t\t</md:OrganizationDisplayName>\n\n\t\t<md:OrganizationURL xml:lang=\"it\">\n\t\t\thttps://example.org\n\t\t</md:OrganizationURL>\n\t</md:Organization>\n\n\t<md:ContactPerson contactType=\"technical\">\n\t\t<md:EmailAddress>mailto:technical.contact@example.com</md:EmailAddress>\n\t</md:ContactPerson>\n\n</md:EntityDescriptor>\n"
  },
  {
    "path": "scripts/release/release-notes-sections.yml",
    "content": "changelog:\n  repository: spring-projects/spring-security\n  sections:\n    - title: \":rewind: Breaking Changes\"\n      labels: [\"type: breaks-passivity\"]\n      sort: \"title\"\n    - title: \":star: New Features\"\n      labels: [\"type: enhancement\"]\n      sort: \"title\"\n    - title: \":beetle: Bug Fixes\"\n      labels: [\"type: bug\"]\n      sort: \"title\"\n    - title: \":hammer: Dependency Upgrades\"\n      labels: [\"type: dependency-upgrade\"]\n      sort: \"title\"\n    - title: \":nut_and_bolt: Build Updates\"\n      labels: [\"type: task\", \"in: build\"]\n      sort: \"title\"\n  issues:\n    exclude:\n      labels: [\"status: duplicate\"]\n"
  },
  {
    "path": "scripts/release/wait-for-done.sh",
    "content": "#!/bin/bash\n\nVERSION=$1\nuntil http -h --check-status --ignore-stdin https://repo1.maven.org/maven2/org/springframework/security/spring-security-core/$VERSION/; do sleep 10; clear; done; spd-say \"It is now uploaded\"\n"
  },
  {
    "path": "scripts/s101.sh",
    "content": "#!/bin/bash\n\n./gradlew jar\nmkdir -p build/s101 && cd $_\nrm *.jar\nfind ../../ -name '*-SNAPSHOT.jar' | grep -v samples | grep -v itest | xargs -I{} cp {} .\n"
  },
  {
    "path": "scripts/update-dependencies.sh",
    "content": "#!/bin/bash\nrm -f build/updates.txt\n./gradlew dependencyUpdate -Drevision=release\nfind . -name report.txt | xargs cat > build/updates.txt\necho \"Updates....\"\ncat build/updates.txt | fgrep ' ->' | sort | uniq\n"
  },
  {
    "path": "settings.gradle",
    "content": "pluginManagement {\n\trepositories {\n\t\tgradlePluginPortal()\n\t}\n}\n\nplugins {\n\tid \"io.spring.develocity.conventions\" version \"0.0.25\"\n}\n\ndependencyResolutionManagement {\n\trepositories {\n\t\tmavenCentral()\n\t\tmaven { url = \"https://repo.spring.io/snapshot\" }\n\t}\n}\n\nrootProject.name = 'spring-security'\n\nFileTree buildFiles = fileTree(rootDir) {\n\tList excludes = gradle.startParameter.projectProperties.get(\"excludeProjects\")?.split(\",\")\n\tinclude '**/*.gradle', '**/*.gradle.kts'\n\texclude 'build', '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*', 'out'\n\texclude '**/grails3'\n\tif(excludes) {\n\t\texclude excludes\n\t}\n}\n\nString rootDirPath = rootDir.absolutePath + File.separator\nbuildFiles.each { File buildFile ->\n\n\tboolean isDefaultName = 'build.gradle'.equals(buildFile.name)\n\tboolean isKotlin = buildFile.name.endsWith(\".kts\")\n\tif(isDefaultName) {\n\t\tString buildFilePath = buildFile.parentFile.absolutePath\n\t\tString projectName = buildFilePath.tokenize(File.separator)[-1]\n\t\tconfigureProject(':' + projectName, projectName, buildFile)\n\t} else {\n\t\tString projectName\n\t\tif (isKotlin) {\n\t\t\tprojectName = buildFile.name.replace('.gradle.kts', '')\n\t\t} else {\n\t\t \tprojectName = buildFile.name.replace('.gradle', '')\n\t\t}\n\t\tconfigureProject(':' + projectName, projectName, buildFile)\n\t}\n}\n\ndef configureProject(String projectPath, String projectName, File buildFile) {\n    include(projectPath)\n\n    def project = findProject(projectPath)\n    project.name = projectName\n    project.projectDir = buildFile.parentFile\n    project.buildFileName = buildFile.name\n}\n"
  },
  {
    "path": "taglibs/spring-security-taglibs.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'compile-warnings-error'\n\tid 'javadoc-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-acl')\n\tapi project(':spring-security-core')\n\tapi project(':spring-security-web')\n\tapi 'org.springframework:spring-aop'\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-expression'\n\tapi 'org.springframework:spring-web'\n\n\tprovided 'jakarta.servlet.jsp:jakarta.servlet.jsp-api'\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestRuntimeOnly 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api'\n\n\ttestImplementation project(':spring-security-access')\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n\nconfigure(project.tasks.withType(Test)) {\n\tsystemProperties['springSecurityVersion'] = version\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/TagLibConfig.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs;\n\nimport jakarta.servlet.jsp.tagext.Tag;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\n/**\n * internal configuration class for taglibs.\n *\n * Not for public use.\n *\n * @author Luke Taylor\n */\npublic final class TagLibConfig {\n\n\tstatic Log logger = LogFactory.getLog(\"spring-security-taglibs\");\n\n\tstatic final boolean DISABLE_UI_SECURITY;\n\n\tstatic final String SECURED_UI_PREFIX;\n\n\tstatic final String SECURED_UI_SUFFIX;\n\n\tstatic {\n\t\tString db = System.getProperty(\"spring.security.disableUISecurity\");\n\t\tString prefix = System.getProperty(\"spring.security.securedUIPrefix\");\n\t\tString suffix = System.getProperty(\"spring.security.securedUISuffix\");\n\t\tSECURED_UI_PREFIX = (prefix != null) ? prefix : \"<span class=\\\"securityHiddenUI\\\">\";\n\t\tSECURED_UI_SUFFIX = (suffix != null) ? suffix : \"</span>\";\n\t\tDISABLE_UI_SECURITY = \"true\".equals(db);\n\t\tif (DISABLE_UI_SECURITY) {\n\t\t\tlogger.warn(\"***** UI security is disabled. All unauthorized content will be displayed *****\");\n\t\t}\n\t}\n\n\tprivate TagLibConfig() {\n\t}\n\n\t/**\n\t * Returns EVAL_BODY_INCLUDE if the authorized flag is true or UI security has been\n\t * disabled. Otherwise returns SKIP_BODY.\n\t * @param authorized whether the user is authorized to see the content or not\n\t */\n\tpublic static int evalOrSkip(boolean authorized) {\n\t\treturn (authorized || DISABLE_UI_SECURITY) ? Tag.EVAL_BODY_INCLUDE : Tag.SKIP_BODY;\n\t}\n\n\tpublic static boolean isUiSecurityDisabled() {\n\t\treturn DISABLE_UI_SECURITY;\n\t}\n\n\tpublic static String getSecuredUiPrefix() {\n\t\treturn SECURED_UI_PREFIX;\n\t}\n\n\tpublic static String getSecuredUiSuffix() {\n\t\treturn SECURED_UI_SUFFIX;\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.authz;\n\nimport java.io.IOException;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.GenericTypeResolver;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ParseException;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.context.support.SecurityWebApplicationContextUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A base class for an &lt;authorize&gt; tag that is independent of the tag rendering\n * technology (JSP, Facelets). It treats tag attributes as simple strings rather than\n * strings that may contain expressions with the exception of the \"access\" attribute,\n * which is always expected to contain a Spring EL expression.\n * <p>\n * Subclasses are expected to extract tag attribute values from the specific rendering\n * technology, evaluate them as expressions if necessary, and set the String-based\n * attributes of this class.\n *\n * @author Francois Beausoleil\n * @author Luke Taylor\n * @author Rossen Stoyanchev\n * @author Rob Winch\n * @since 3.1.0\n */\npublic abstract class AbstractAuthorizeTag {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable String access;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable String url;\n\n\tprivate @Nullable String method = \"GET\";\n\n\t/**\n\t * This method allows subclasses to provide a way to access the ServletRequest\n\t * according to the rendering technology.\n\t */\n\tprotected abstract ServletRequest getRequest();\n\n\t/**\n\t * This method allows subclasses to provide a way to access the ServletResponse\n\t * according to the rendering technology.\n\t */\n\tprotected abstract ServletResponse getResponse();\n\n\t/**\n\t * This method allows subclasses to provide a way to access the ServletContext\n\t * according to the rendering technology.\n\t */\n\tprotected abstract ServletContext getServletContext();\n\n\t/**\n\t * Make an authorization decision by considering all &lt;authorize&gt; tag attributes.\n\t * The following are valid combinations of attributes:\n\t * <ul>\n\t * <li>access</li>\n\t * <li>url, method</li>\n\t * </ul>\n\t * The above combinations are mutually exclusive and evaluated in the given order.\n\t * @return the result of the authorization decision\n\t * @throws IOException\n\t */\n\tpublic boolean authorize() throws IOException {\n\t\tif (StringUtils.hasText(getAccess())) {\n\t\t\treturn authorizeUsingAccessExpression();\n\t\t}\n\t\tif (StringUtils.hasText(getUrl())) {\n\t\t\treturn authorizeUsingUrlCheck();\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Make an authorization decision based on a Spring EL expression. See the\n\t * \"Expression-Based Access Control\" chapter in Spring Security for details on what\n\t * expressions can be used.\n\t * @return the result of the authorization decision\n\t * @throws IOException\n\t */\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tpublic boolean authorizeUsingAccessExpression() throws IOException {\n\t\tif (getContext().getAuthentication() == null) {\n\t\t\treturn false;\n\t\t}\n\t\tString access = getAccess();\n\t\tAssert.notNull(access, \"access cannot be null\");\n\t\tSecurityExpressionHandler<FilterInvocation> handler = getExpressionHandler();\n\t\tExpression accessExpression;\n\t\ttry {\n\t\t\taccessExpression = handler.getExpressionParser().parseExpression(access);\n\t\t}\n\t\tcatch (ParseException ex) {\n\t\t\tthrow new IOException(ex);\n\t\t}\n\t\treturn ExpressionUtils.evaluateAsBoolean(accessExpression, createExpressionEvaluationContext(handler));\n\t}\n\n\t/**\n\t * Allows the {@code EvaluationContext} to be customized for variable lookup etc.\n\t */\n\tprotected EvaluationContext createExpressionEvaluationContext(SecurityExpressionHandler<FilterInvocation> handler) {\n\t\tFilterInvocation f = new FilterInvocation(getRequest(), getResponse(), (request, response) -> {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t});\n\t\treturn handler.createEvaluationContext(getContext().getAuthentication(), f);\n\t}\n\n\t/**\n\t * Make an authorization decision based on the URL and HTTP method attributes. True is\n\t * returned if the user is allowed to access the given URL as defined.\n\t * @return the result of the authorization decision\n\t * @throws IOException\n\t */\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tpublic boolean authorizeUsingUrlCheck() throws IOException {\n\t\tString url = getUrl();\n\t\tAssert.notNull(url, \"url cannot be null\");\n\t\tString contextPath = ((HttpServletRequest) getRequest()).getContextPath();\n\t\tAuthentication currentUser = getContext().getAuthentication();\n\t\treturn getPrivilegeEvaluator().isAllowed(contextPath, url, getMethod(), currentUser);\n\t}\n\n\tpublic @Nullable String getAccess() {\n\t\treturn this.access;\n\t}\n\n\tpublic void setAccess(String access) {\n\t\tthis.access = access;\n\t}\n\n\tpublic @Nullable String getUrl() {\n\t\treturn this.url;\n\t}\n\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\n\tpublic @Nullable String getMethod() {\n\t\treturn this.method;\n\t}\n\n\tpublic void setMethod(String method) {\n\t\tthis.method = (method != null) ? method.toUpperCase(Locale.ENGLISH) : null;\n\t}\n\n\tprivate SecurityContext getContext() {\n\t\tApplicationContext appContext = getApplicationContext();\n\t\tString[] names = appContext.getBeanNamesForType(SecurityContextHolderStrategy.class);\n\t\tif (names.length == 1) {\n\t\t\tSecurityContextHolderStrategy strategy = appContext.getBean(SecurityContextHolderStrategy.class);\n\t\t\treturn strategy.getContext();\n\t\t}\n\t\treturn SecurityContextHolder.getContext();\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprivate SecurityExpressionHandler<FilterInvocation> getExpressionHandler() throws IOException {\n\t\tApplicationContext appContext = getApplicationContext();\n\t\tMap<String, SecurityExpressionHandler> handlers = appContext.getBeansOfType(SecurityExpressionHandler.class);\n\t\tfor (SecurityExpressionHandler handler : handlers.values()) {\n\t\t\tif (FilterInvocation.class\n\t\t\t\t.equals(GenericTypeResolver.resolveTypeArgument(handler.getClass(), SecurityExpressionHandler.class))) {\n\t\t\t\treturn handler;\n\t\t\t}\n\t\t}\n\t\tthrow new IOException(\"No visible WebSecurityExpressionHandler instance could be found in the application \"\n\t\t\t\t+ \"context. There must be at least one in order to support expressions in JSP 'authorize' tags.\");\n\t}\n\n\tprivate WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() throws IOException {\n\t\tWebInvocationPrivilegeEvaluator privEvaluatorFromRequest = (WebInvocationPrivilegeEvaluator) getRequest()\n\t\t\t.getAttribute(WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE);\n\t\tif (privEvaluatorFromRequest != null) {\n\t\t\treturn privEvaluatorFromRequest;\n\t\t}\n\t\tApplicationContext ctx = getApplicationContext();\n\t\tMap<String, WebInvocationPrivilegeEvaluator> wipes = ctx.getBeansOfType(WebInvocationPrivilegeEvaluator.class);\n\t\tif (wipes.isEmpty()) {\n\t\t\tthrow new IOException(\n\t\t\t\t\t\"No visible WebInvocationPrivilegeEvaluator instance could be found in the application \"\n\t\t\t\t\t\t\t+ \"context. There must be at least one in order to support the use of URL access checks in 'authorize' tags.\");\n\t\t}\n\t\treturn (WebInvocationPrivilegeEvaluator) wipes.values().toArray()[0];\n\t}\n\n\tprivate ApplicationContext getApplicationContext() {\n\t\tObject value = getRequest().getAttribute(WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE);\n\t\tif (value == null) {\n\t\t\treturn SecurityWebApplicationContextUtils.findRequiredWebApplicationContext(getServletContext());\n\t\t}\n\t\tif (value instanceof ApplicationContext context) {\n\t\t\treturn context;\n\t\t}\n\t\tthrow new IllegalArgumentException(\"WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE value must be of type \"\n\t\t\t\t+ \"ApplicationContext, found type \" + value.getClass());\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/authz/AccessControlListTag.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.authz;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.jsp.JspException;\nimport jakarta.servlet.jsp.PageContext;\nimport jakarta.servlet.jsp.tagext.Tag;\nimport jakarta.servlet.jsp.tagext.TagSupport;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.taglibs.TagLibConfig;\nimport org.springframework.security.web.context.support.SecurityWebApplicationContextUtils;\n\n/**\n * An implementation of {@link Tag} that allows its body through if all authorizations are\n * granted to the request's principal.\n * <p>\n * One or more comma separate numeric are specified via the {@code hasPermission}\n * attribute. The tag delegates to the configured {@link PermissionEvaluator} which it\n * obtains from the {@code ApplicationContext}.\n * <p>\n * For this class to operate it must be able to access the application context via the\n * {@code WebApplicationContextUtils} and attempt to locate the\n * {@code PermissionEvaluator} instance. There cannot be more than one of these present\n * for the tag to function.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @author Rob Winch\n */\npublic class AccessControlListTag extends TagSupport {\n\n\tprotected static final Log logger = LogFactory.getLog(AccessControlListTag.class);\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate ApplicationContext applicationContext;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate Object domainObject;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate PermissionEvaluator permissionEvaluator;\n\n\tprivate String hasPermission = \"\";\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate String var;\n\n\t@Override\n\tpublic int doStartTag() throws JspException {\n\t\tif ((null == this.hasPermission) || \"\".equals(this.hasPermission)) {\n\t\t\treturn skipBody();\n\t\t}\n\t\tinitializeIfRequired();\n\t\tif (this.domainObject == null) {\n\t\t\tlogger.debug(\"domainObject resolved to null, so including tag body\");\n\t\t\t// Of course they have access to a null object!\n\t\t\treturn evalBody();\n\t\t}\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tlogger.debug(\"SecurityContextHolder did not return a non-null Authentication object, so skipping tag body\");\n\t\t\treturn skipBody();\n\t\t}\n\t\tList<Object> requiredPermissions = parseHasPermission(this.hasPermission);\n\t\tfor (Object requiredPermission : requiredPermissions) {\n\t\t\tif (!this.permissionEvaluator.hasPermission(authentication, this.domainObject, requiredPermission)) {\n\t\t\t\treturn skipBody();\n\t\t\t}\n\t\t}\n\t\treturn evalBody();\n\t}\n\n\tprivate List<Object> parseHasPermission(String hasPermission) {\n\t\tString[] requiredPermissions = hasPermission.split(\",\");\n\t\tList<Object> parsedPermissions = new ArrayList<>(requiredPermissions.length);\n\t\tfor (String permissionToParse : requiredPermissions) {\n\t\t\tObject parsedPermission = permissionToParse;\n\t\t\ttry {\n\t\t\t\tparsedPermission = Integer.parseInt(permissionToParse);\n\t\t\t}\n\t\t\tcatch (NumberFormatException ex) {\n\t\t\t}\n\t\t\tparsedPermissions.add(parsedPermission);\n\t\t}\n\t\treturn parsedPermissions;\n\t}\n\n\tprivate int skipBody() {\n\t\tif (this.var != null) {\n\t\t\tthis.pageContext.setAttribute(this.var, Boolean.FALSE, PageContext.PAGE_SCOPE);\n\t\t}\n\t\treturn TagLibConfig.evalOrSkip(false);\n\t}\n\n\tprivate int evalBody() {\n\t\tif (this.var != null) {\n\t\t\tthis.pageContext.setAttribute(this.var, Boolean.TRUE, PageContext.PAGE_SCOPE);\n\t\t}\n\t\treturn TagLibConfig.evalOrSkip(true);\n\t}\n\n\t/**\n\t * Allows test cases to override where application context obtained from.\n\t * @param pageContext so the <code>ServletContext</code> can be accessed as required\n\t * by Spring's <code>WebApplicationContextUtils</code>\n\t * @return the Spring application context (never <code>null</code>)\n\t */\n\tprotected ApplicationContext getContext(PageContext pageContext) {\n\t\tServletContext servletContext = pageContext.getServletContext();\n\t\treturn SecurityWebApplicationContextUtils.findRequiredWebApplicationContext(servletContext);\n\t}\n\n\tpublic Object getDomainObject() {\n\t\treturn this.domainObject;\n\t}\n\n\tpublic String getHasPermission() {\n\t\treturn this.hasPermission;\n\t}\n\n\tprivate void initializeIfRequired() throws JspException {\n\t\tif (this.applicationContext != null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.applicationContext = getContext(this.pageContext);\n\t\tthis.permissionEvaluator = Objects.requireNonNull(getBeanOfType(PermissionEvaluator.class),\n\t\t\t\t\"PermissionEvaluator Bean is required\");\n\t\tString[] names = this.applicationContext.getBeanNamesForType(SecurityContextHolderStrategy.class);\n\t\tif (names.length == 1) {\n\t\t\tSecurityContextHolderStrategy strategy = this.applicationContext\n\t\t\t\t.getBean(SecurityContextHolderStrategy.class);\n\t\t\tthis.securityContextHolderStrategy = strategy;\n\t\t}\n\t}\n\n\tprivate <T> @Nullable T getBeanOfType(Class<T> type) throws JspException {\n\t\tMap<String, T> map = this.applicationContext.getBeansOfType(type);\n\t\tfor (ApplicationContext context = this.applicationContext.getParent(); context != null; context = context\n\t\t\t.getParent()) {\n\t\t\tmap.putAll(context.getBeansOfType(type));\n\t\t}\n\t\tif (map.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (map.size() == 1) {\n\t\t\treturn map.values().iterator().next();\n\t\t}\n\t\tthrow new JspException(\"Found incorrect number of \" + type.getSimpleName() + \" instances in \"\n\t\t\t\t+ \"application context - you must have only have one!\");\n\t}\n\n\tpublic void setDomainObject(Object domainObject) {\n\t\tthis.domainObject = domainObject;\n\t}\n\n\tpublic void setHasPermission(String hasPermission) {\n\t\tthis.hasPermission = hasPermission;\n\t}\n\n\tpublic void setVar(String var) {\n\t\tthis.var = var;\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.authz;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.jsp.JspException;\nimport jakarta.servlet.jsp.PageContext;\nimport jakarta.servlet.jsp.tagext.Tag;\nimport jakarta.servlet.jsp.tagext.TagSupport;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeanWrapperImpl;\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.context.support.SecurityWebApplicationContextUtils;\nimport org.springframework.security.web.util.TextEscapeUtils;\nimport org.springframework.web.util.TagUtils;\n\n/**\n * An {@link jakarta.servlet.jsp.tagext.Tag} implementation that allows convenient access\n * to the current <code>Authentication</code> object.\n * <p>\n * Whilst JSPs can access the <code>SecurityContext</code> directly, this tag avoids\n * handling <code>null</code> conditions.\n *\n * @author Thomas Champagne\n */\npublic class AuthenticationTag extends TagSupport {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable String var;\n\n\tprivate @Nullable String property;\n\n\tprivate int scope;\n\n\tprivate boolean scopeSpecified;\n\n\tprivate boolean htmlEscape = true;\n\n\tpublic AuthenticationTag() {\n\t\tinit();\n\t}\n\n\t// resets local state\n\tprivate void init() {\n\t\tthis.var = null;\n\t\tthis.scopeSpecified = false;\n\t\tthis.scope = PageContext.PAGE_SCOPE;\n\t}\n\n\tpublic void setVar(String var) {\n\t\tthis.var = var;\n\t}\n\n\tpublic void setProperty(String operation) {\n\t\tthis.property = operation;\n\t}\n\n\tpublic void setScope(String scope) {\n\t\tthis.scope = TagUtils.getScope(scope);\n\t\tthis.scopeSpecified = true;\n\t}\n\n\tpublic void setPageContext(PageContext pageContext) {\n\t\tsuper.setPageContext(pageContext);\n\t\tServletContext servletContext = pageContext.getServletContext();\n\t\tApplicationContext context = SecurityWebApplicationContextUtils\n\t\t\t.findRequiredWebApplicationContext(servletContext);\n\t\tString[] names = context.getBeanNamesForType(SecurityContextHolderStrategy.class);\n\t\tif (names.length == 1) {\n\t\t\tSecurityContextHolderStrategy strategy = context.getBean(SecurityContextHolderStrategy.class);\n\t\t\tthis.securityContextHolderStrategy = strategy;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int doStartTag() throws JspException {\n\t\treturn super.doStartTag();\n\t}\n\n\t@Override\n\tpublic int doEndTag() throws JspException {\n\t\tObject result = null;\n\t\t// determine the value by...\n\t\tif (this.property != null) {\n\t\t\tSecurityContext context = this.securityContextHolderStrategy.getContext();\n\t\t\tif ((context == null) || !(context instanceof SecurityContext) || (context.getAuthentication() == null)) {\n\t\t\t\treturn Tag.EVAL_PAGE;\n\t\t\t}\n\t\t\tAuthentication auth = context.getAuthentication();\n\t\t\tif (auth.getPrincipal() == null) {\n\t\t\t\treturn Tag.EVAL_PAGE;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tBeanWrapperImpl wrapper = new BeanWrapperImpl(auth);\n\t\t\t\tresult = wrapper.getPropertyValue(this.property);\n\t\t\t}\n\t\t\tcatch (BeansException ex) {\n\t\t\t\tthrow new JspException(ex);\n\t\t\t}\n\t\t}\n\t\tif (this.var != null) {\n\t\t\t/*\n\t\t\t * Store the result, letting an IllegalArgumentException propagate back if the\n\t\t\t * scope is invalid (e.g., if an attempt is made to store something in the\n\t\t\t * session without any HttpSession existing).\n\t\t\t */\n\t\t\tif (result != null) {\n\t\t\t\tthis.pageContext.setAttribute(this.var, result, this.scope);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (this.scopeSpecified) {\n\t\t\t\t\tthis.pageContext.removeAttribute(this.var, this.scope);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.pageContext.removeAttribute(this.var);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (this.htmlEscape) {\n\t\t\t\twriteMessage(TextEscapeUtils.escapeEntities(String.valueOf(result)));\n\t\t\t}\n\t\t\telse {\n\t\t\t\twriteMessage(String.valueOf(result));\n\t\t\t}\n\t\t}\n\t\treturn EVAL_PAGE;\n\t}\n\n\tprotected void writeMessage(String msg) throws JspException {\n\t\ttry {\n\t\t\tthis.pageContext.getOut().write(String.valueOf(msg));\n\t\t}\n\t\tcatch (IOException ioe) {\n\t\t\tthrow new JspException(ioe);\n\t\t}\n\t}\n\n\t/**\n\t * Set HTML escaping for this tag, as boolean value.\n\t */\n\tpublic void setHtmlEscape(String htmlEscape) {\n\t\tthis.htmlEscape = Boolean.parseBoolean(htmlEscape);\n\t}\n\n\t/**\n\t * Return the HTML escaping setting for this tag, or the default setting if not\n\t * overridden.\n\t */\n\tprotected boolean isHtmlEscape() {\n\t\treturn this.htmlEscape;\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/authz/JspAuthorizeTag.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.authz;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.jsp.JspException;\nimport jakarta.servlet.jsp.PageContext;\nimport jakarta.servlet.jsp.tagext.Tag;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.ConstructorResolver;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.MethodResolver;\nimport org.springframework.expression.OperatorOverloader;\nimport org.springframework.expression.PropertyAccessor;\nimport org.springframework.expression.TypeComparator;\nimport org.springframework.expression.TypeConverter;\nimport org.springframework.expression.TypeLocator;\nimport org.springframework.expression.TypedValue;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.taglibs.TagLibConfig;\nimport org.springframework.security.web.FilterInvocation;\n\n/**\n * A JSP {@link Tag} implementation of {@link AbstractAuthorizeTag}.\n *\n * @author Rossen Stoyanchev\n * @since 3.1.0\n * @see AbstractAuthorizeTag\n */\npublic class JspAuthorizeTag extends AbstractAuthorizeTag implements Tag {\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable Tag parent;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprotected PageContext pageContext;\n\n\tprotected @Nullable String id;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate @Nullable String var;\n\n\tprivate boolean authorized;\n\n\t/**\n\t * Invokes the base class {@link AbstractAuthorizeTag#authorize()} method to decide if\n\t * the body of the tag should be skipped or not.\n\t * @return {@link Tag#SKIP_BODY} or {@link Tag#EVAL_BODY_INCLUDE}\n\t */\n\t@Override\n\tpublic int doStartTag() throws JspException {\n\t\ttry {\n\t\t\tthis.authorized = super.authorize();\n\t\t\tif (!this.authorized && TagLibConfig.isUiSecurityDisabled()) {\n\t\t\t\tthis.pageContext.getOut().write(TagLibConfig.getSecuredUiPrefix());\n\t\t\t}\n\t\t\tif (this.var != null) {\n\t\t\t\tthis.pageContext.setAttribute(this.var, this.authorized, PageContext.PAGE_SCOPE);\n\t\t\t}\n\t\t\treturn TagLibConfig.evalOrSkip(this.authorized);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new JspException(ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected EvaluationContext createExpressionEvaluationContext(SecurityExpressionHandler<FilterInvocation> handler) {\n\t\treturn new PageContextVariableLookupEvaluationContext(super.createExpressionEvaluationContext(handler));\n\t}\n\n\t/**\n\t * Default processing of the end tag returning EVAL_PAGE.\n\t * @return EVAL_PAGE\n\t * @see Tag#doEndTag()\n\t */\n\t@Override\n\tpublic int doEndTag() throws JspException {\n\t\ttry {\n\t\t\tif (!this.authorized && TagLibConfig.isUiSecurityDisabled()) {\n\t\t\t\tthis.pageContext.getOut().write(TagLibConfig.getSecuredUiSuffix());\n\t\t\t}\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new JspException(ex);\n\t\t}\n\t\treturn EVAL_PAGE;\n\t}\n\n\tpublic @Nullable String getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic void setId(String id) {\n\t\tthis.id = id;\n\t}\n\n\t@Override\n\tpublic @Nullable Tag getParent() {\n\t\treturn this.parent;\n\t}\n\n\t@Override\n\tpublic void setParent(Tag parent) {\n\t\tthis.parent = parent;\n\t}\n\n\tpublic @Nullable String getVar() {\n\t\treturn this.var;\n\t}\n\n\tpublic void setVar(String var) {\n\t\tthis.var = var;\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\tthis.parent = null;\n\t\tthis.id = null;\n\t}\n\n\t@Override\n\tpublic void setPageContext(PageContext pageContext) {\n\t\tthis.pageContext = pageContext;\n\t}\n\n\t@Override\n\tprotected ServletRequest getRequest() {\n\t\treturn this.pageContext.getRequest();\n\t}\n\n\t@Override\n\tprotected ServletResponse getResponse() {\n\t\treturn this.pageContext.getResponse();\n\t}\n\n\t@Override\n\tprotected ServletContext getServletContext() {\n\t\treturn this.pageContext.getServletContext();\n\t}\n\n\tprivate final class PageContextVariableLookupEvaluationContext implements EvaluationContext {\n\n\t\tprivate EvaluationContext delegate;\n\n\t\tprivate PageContextVariableLookupEvaluationContext(EvaluationContext delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic TypedValue getRootObject() {\n\t\t\treturn this.delegate.getRootObject();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<ConstructorResolver> getConstructorResolvers() {\n\t\t\treturn this.delegate.getConstructorResolvers();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<MethodResolver> getMethodResolvers() {\n\t\t\treturn this.delegate.getMethodResolvers();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<PropertyAccessor> getPropertyAccessors() {\n\t\t\treturn this.delegate.getPropertyAccessors();\n\t\t}\n\n\t\t@Override\n\t\tpublic TypeLocator getTypeLocator() {\n\t\t\treturn this.delegate.getTypeLocator();\n\t\t}\n\n\t\t@Override\n\t\tpublic TypeConverter getTypeConverter() {\n\t\t\treturn this.delegate.getTypeConverter();\n\t\t}\n\n\t\t@Override\n\t\tpublic TypeComparator getTypeComparator() {\n\t\t\treturn this.delegate.getTypeComparator();\n\t\t}\n\n\t\t@Override\n\t\tpublic OperatorOverloader getOperatorOverloader() {\n\t\t\treturn this.delegate.getOperatorOverloader();\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable BeanResolver getBeanResolver() {\n\t\t\treturn this.delegate.getBeanResolver();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setVariable(String name, @Nullable Object value) {\n\t\t\tthis.delegate.setVariable(name, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object lookupVariable(String name) {\n\t\t\tObject result = this.delegate.lookupVariable(name);\n\t\t\tif (result == null) {\n\t\t\t\tresult = JspAuthorizeTag.this.pageContext.findAttribute(name);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/authz/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * JSP Security tag library implementation.\n */\n@NullMarked\npackage org.springframework.security.taglibs.authz;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/csrf/AbstractCsrfTag.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.csrf;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.jsp.JspException;\nimport jakarta.servlet.jsp.tagext.TagSupport;\n\nimport org.springframework.security.web.csrf.CsrfToken;\n\n/**\n * An abstract tag for handling CSRF operations.\n *\n * @author Nick Williams\n * @since 3.2.2\n */\nabstract class AbstractCsrfTag extends TagSupport {\n\n\t@Override\n\tpublic int doEndTag() throws JspException {\n\t\tCsrfToken token = (CsrfToken) this.pageContext.getRequest().getAttribute(CsrfToken.class.getName());\n\t\tif (token != null) {\n\t\t\ttry {\n\t\t\t\tthis.pageContext.getOut().write(this.handleToken(token));\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tthrow new JspException(ex);\n\t\t\t}\n\t\t}\n\t\treturn EVAL_PAGE;\n\t}\n\n\tprotected abstract String handleToken(CsrfToken token);\n\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/csrf/CsrfInputTag.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.csrf;\n\nimport org.springframework.security.web.csrf.CsrfToken;\n\n/**\n * A JSP tag that prints out a hidden form field for the CSRF token. See the JSP Tab\n * Library documentation for more information.\n *\n * @author Nick Williams\n * @since 3.2.2\n */\npublic class CsrfInputTag extends AbstractCsrfTag {\n\n\t@Override\n\tpublic String handleToken(CsrfToken token) {\n\t\treturn \"<input type=\\\"hidden\\\" name=\\\"\" + token.getParameterName() + \"\\\" value=\\\"\" + token.getToken() + \"\\\" />\";\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/csrf/CsrfMetaTagsTag.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.csrf;\n\nimport org.springframework.security.web.csrf.CsrfToken;\n\n/**\n * A JSP tag that prints out a meta tags holding the CSRF form field name and token value\n * for use in JavaScrip code. See the JSP Tab Library documentation for more information.\n *\n * @author Nick Williams\n * @since 3.2.2\n */\npublic class CsrfMetaTagsTag extends AbstractCsrfTag {\n\n\t@Override\n\tpublic String handleToken(CsrfToken token) {\n\t\treturn \"<meta name=\\\"_csrf_parameter\\\" content=\\\"\" + token.getParameterName() + \"\\\" />\"\n\t\t\t\t+ \"<meta name=\\\"_csrf_header\\\" content=\\\"\" + token.getHeaderName() + \"\\\" />\"\n\t\t\t\t+ \"<meta name=\\\"_csrf\\\" content=\\\"\" + token.getToken() + \"\\\" />\";\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/csrf/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * JSP Security tag library integration with CSRF protection.\n */\n@NullMarked\npackage org.springframework.security.taglibs.csrf;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "taglibs/src/main/java/org/springframework/security/taglibs/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Security related tag libraries that can be used in JSPs and templates.\n */\n@NullMarked\npackage org.springframework.security.taglibs;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "taglibs/src/main/resources/META-INF/security.tld",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!--\n  ~ Copyright 2004-present the original author or authors.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<taglib xmlns=\"http://java.sun.com/xml/ns/j2ee\"\n        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n        xsi:schemaLocation=\"http://java.sun.com/xml/ns/j2ee https://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd\"\n        version=\"2.0\">\n    <description>Spring Security Authorization Tag Library</description>\n\n    <tlib-version>7.1</tlib-version>\n    <short-name>security</short-name>\n    <uri>http://www.springframework.org/security/tags</uri>\n\n    <tag>\n        <description>\n            A tag which outputs the body of the tag if the configured access expression\n            evaluates to true for the currently authenticated principal.\n        </description>\n        <name>authorize</name>\n        <tag-class>org.springframework.security.taglibs.authz.JspAuthorizeTag</tag-class>\n        <body-content>JSP</body-content>\n\n        <attribute>\n            <description>\n                A Spring-EL expression which is supported by the WebSecurityExpressionHandler\n                in the application context. The latter will be used to evaluate the expression.\n            </description>\n            <name>access</name>\n            <required>false</required>\n            <rtexprvalue>true</rtexprvalue>\n        </attribute>\n\n        <attribute>\n            <description>\n                A URL within the application. If the user has access to this URL (as determined by\n                the AccessDecisionManager), the tag body will be evaluated. If not, it will\n                be skipped.\n            </description>\n            <name>url</name>\n            <required>false</required>\n            <rtexprvalue>true</rtexprvalue>\n        </attribute>\n\n        <attribute>\n            <description>\n                Can be used to specify the HTTP method (typically GET or POST) which is used in combination\n                with the URL when consulting the AccessDecisionManager. Only has any meaning when used in combination\n                with the \"url\" attribute. Defaults to GET.\n            </description>\n            <name>method</name>\n            <required>false</required>\n            <rtexprvalue>false</rtexprvalue>\n        </attribute>\n\n        <attribute>\n            <description>\n                A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the\n                same condition to be reused subsequently in the page without re-evaluation.\n            </description>\n            <name>var</name>\n            <required>false</required>\n            <rtexprvalue>false</rtexprvalue>\n        </attribute>\n    </tag>\n\n    <tag>\n        <description>\n            Allows access to the current Authentication object.\n        </description>\n        <name>authentication</name>\n        <tag-class>org.springframework.security.taglibs.authz.AuthenticationTag</tag-class>\n        <body-content>empty</body-content>\n\n        <attribute>\n            <description>\n                Property of the Authentication object which should be output. Supports nested\n                properties. For example if the principal object is an instance of UserDetails,\n                the property \"principal.username\" will return the username. Alternatively, using\n                \"name\" will call getName method on the Authentication object directly.\n            </description>\n            <name>property</name>\n            <required>true</required>\n            <rtexprvalue>true</rtexprvalue>\n        </attribute>\n        <attribute>\n            <description>\n                Name of the exported scoped variable which will contain the\n                evaluated property of the Authentication object.\n            </description>\n            <name>var</name>\n            <required>false</required>\n            <rtexprvalue>false</rtexprvalue>\n        </attribute>\n        <attribute>\n            <description>\n                Set HTML escaping for this tag, as a boolean value.\n            </description>\n            <name>htmlEscape</name>\n            <required>false</required>\n            <rtexprvalue>true</rtexprvalue>\n        </attribute>\n        <attribute>\n            <description>\n                Scope for var.\n            </description>\n            <name>scope</name>\n            <required>false</required>\n            <rtexprvalue>false</rtexprvalue>\n        </attribute>\n    </tag>\n\n    <tag>\n        <description>\n            Allows inclusion of a tag body if the current Authentication\n            has one of the specified permissions to the presented\n            domain object instance.\n        </description>\n        <name>accesscontrollist</name>\n        <tag-class>org.springframework.security.taglibs.authz.AccessControlListTag</tag-class>\n        <body-content>JSP</body-content>\n\n        <attribute>\n            <description>\n                A comma separated list of permissions, which will be converted to\n                Permission instances by the configured PermissionFactory.\n            </description>\n            <name>hasPermission</name>\n            <required>true</required>\n            <rtexprvalue>true</rtexprvalue>\n        </attribute>\n        <attribute>\n            <description>\n                The actual domain object instance for which permissions\n                are being evaluated.\n            </description>\n            <name>domainObject</name>\n            <required>true</required>\n            <rtexprvalue>true</rtexprvalue>\n        </attribute>\n        <attribute>\n            <description>\n                A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the\n                same condition to be reused subsequently in the page without re-evaluation.\n            </description>\n            <name>var</name>\n            <required>false</required>\n            <rtexprvalue>false</rtexprvalue>\n        </attribute>\n    </tag>\n\n    <tag>\n        <description><![CDATA[\n            If CSRF protection is enabled, this tag inserts a hidden form field with the correct name and value for the\n            CSRF protection token. If CSRF protection is not enabled, this tag outputs nothing. Normally Spring Security\n            automatically inserts this form field for any <form:form> tags, but if for some reason you cannot use\n            <form:form> this tag is a handy replacement. You should place this tag within an HTML <form></form> block,\n            where you would normally place other <input>s. Do NOT place this tag within a Spring <form:form></form:form>\n            block—Spring Security handles Spring forms automatically.\n        ]]></description>\n        <name>csrfInput</name>\n        <tag-class>org.springframework.security.taglibs.csrf.CsrfInputTag</tag-class>\n        <body-content>empty</body-content>\n    </tag>\n\n    <tag>\n        <description><![CDATA[\n            If CSRF protection is enabled, this tag inserts meta tags containing the CSRF protection token form\n            field and header names and CSRF protection token value. These tags are useful for employing CSRF protection\n            within JavaScript in your applications. You should place this tag within an HTML <head></head> block, where\n            you would normally place other meta tags. Once you use this tag, you can access the form field name using\n            the JQuery $(\"meta[name='_csrf_parameter']\").attr(\"content\") and the header name using\n            $(\"meta[name='_csrf_header']\").attr(\"content\"). Likewise, you can access the token value with\n            $(\"meta[name='_csrf']\").attr(\"content\"). You should use a form field when creating and submitting forms from\n            JavaScript, and you should use a header when sending AJAX requests. If CSRF protection is not enabled, this\n            tag outputs nothing.\n        ]]></description>\n        <name>csrfMetaTags</name>\n        <tag-class>org.springframework.security.taglibs.csrf.CsrfMetaTagsTag</tag-class>\n        <body-content>empty</body-content>\n    </tag>\n\n</taglib>\n"
  },
  {
    "path": "taglibs/src/test/java/org/springframework/security/taglibs/TldTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs;\n\nimport java.io.File;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.w3c.dom.Document;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class TldTests {\n\n\t// SEC-2324\n\t@Test\n\tpublic void testTldVersionIsCorrect() throws Exception {\n\t\tString SPRING_SECURITY_VERSION = \"springSecurityVersion\";\n\t\tString version = System.getProperty(SPRING_SECURITY_VERSION);\n\t\tFile securityTld = new File(\"src/main/resources/META-INF/security.tld\");\n\t\tDocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();\n\t\tDocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();\n\t\tDocument document = documentBuilder.parse(securityTld);\n\t\tString tlibVersion = document.getElementsByTagName(\"tlib-version\").item(0).getTextContent();\n\t\tassertThat(version).startsWith(tlibVersion);\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/test/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTagTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.authz;\n\nimport java.io.IOException;\nimport java.util.Collections;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.GenericWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n *\n */\npublic class AbstractAuthorizeTagTests {\n\n\tprivate AbstractAuthorizeTag tag;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate MockServletContext servletContext;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.tag = new AuthzTag();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.servletContext = new MockServletContext();\n\t}\n\n\t@AfterEach\n\tpublic void teardown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void privilegeEvaluatorFromRequest() throws IOException {\n\t\tWebApplicationContext wac = mock(WebApplicationContext.class);\n\t\tthis.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);\n\t\tgiven(wac.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);\n\t\tString uri = \"/something\";\n\t\tWebInvocationPrivilegeEvaluator expected = mock(WebInvocationPrivilegeEvaluator.class);\n\t\tthis.tag.setUrl(uri);\n\t\tthis.request.setAttribute(WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE, expected);\n\t\tthis.tag.authorizeUsingUrlCheck();\n\t\tverify(expected).isAllowed(eq(\"\"), eq(uri), eq(\"GET\"), any());\n\t}\n\n\t@Test\n\tpublic void privilegeEvaluatorFromRequestUsesSecurityContextHolderStrategy() throws IOException {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(\n\t\t\t\tnew TestingAuthenticationToken(\"user\", \"password\", AuthorityUtils.NO_AUTHORITIES)));\n\t\tGenericWebApplicationContext wac = new GenericWebApplicationContext();\n\t\twac.registerBean(SecurityContextHolderStrategy.class, () -> strategy);\n\t\twac.refresh();\n\t\tthis.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);\n\t\tString uri = \"/something\";\n\t\tWebInvocationPrivilegeEvaluator expected = mock(WebInvocationPrivilegeEvaluator.class);\n\t\tthis.tag.setUrl(uri);\n\t\tthis.request.setAttribute(WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE, expected);\n\t\tthis.tag.authorizeUsingUrlCheck();\n\t\tverify(expected).isAllowed(eq(\"\"), eq(uri), eq(\"GET\"), any());\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void privilegeEvaluatorFromChildContext() throws IOException {\n\t\tString uri = \"/something\";\n\t\tWebInvocationPrivilegeEvaluator expected = mock(WebInvocationPrivilegeEvaluator.class);\n\t\tthis.tag.setUrl(uri);\n\t\tWebApplicationContext wac = mock(WebApplicationContext.class);\n\t\tgiven(wac.getBeansOfType(WebInvocationPrivilegeEvaluator.class))\n\t\t\t.willReturn(Collections.singletonMap(\"wipe\", expected));\n\t\tgiven(wac.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);\n\t\tthis.servletContext.setAttribute(\"org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher\", wac);\n\t\tthis.tag.authorizeUsingUrlCheck();\n\t\tverify(expected).isAllowed(eq(\"\"), eq(uri), eq(\"GET\"), any());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic void expressionFromChildContext() throws IOException {\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"USER\"));\n\t\tDefaultWebSecurityExpressionHandler expected = new DefaultWebSecurityExpressionHandler();\n\t\tthis.tag.setAccess(\"permitAll\");\n\t\tWebApplicationContext wac = mock(WebApplicationContext.class);\n\t\tgiven(wac.getBeansOfType(SecurityExpressionHandler.class))\n\t\t\t.willReturn(Collections.<String, SecurityExpressionHandler>singletonMap(\"wipe\", expected));\n\t\tgiven(wac.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);\n\t\tthis.servletContext.setAttribute(\"org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher\", wac);\n\t\tassertThat(this.tag.authorize()).isTrue();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic void expressionWhenApplicationContextAttributeIsSetThenUsed() throws IOException {\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"USER\"));\n\t\tDefaultWebSecurityExpressionHandler expected = new DefaultWebSecurityExpressionHandler();\n\t\tWebApplicationContext context = mock(WebApplicationContext.class);\n\t\tgiven(context.getBeansOfType(SecurityExpressionHandler.class))\n\t\t\t.willReturn(Collections.<String, SecurityExpressionHandler>singletonMap(\"wipe\", expected));\n\t\tgiven(context.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);\n\t\tthis.request.setAttribute(WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE, context);\n\t\tthis.tag.setAccess(\"permitAll\");\n\t\tassertThat(this.tag.authorize()).isTrue();\n\t\tverify(context).getBeansOfType(SecurityExpressionHandler.class);\n\t}\n\n\t@Test\n\tpublic void expressionWhenApplicationContextAttributeIsWrongTypeThenIllegalArgumentException() {\n\t\tthis.request.setAttribute(WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE, \"notAnApplicationContext\");\n\t\tthis.tag.setAccess(\"permitAll\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.tag.authorize());\n\t}\n\n\tprivate class AuthzTag extends AbstractAuthorizeTag {\n\n\t\t@Override\n\t\tprotected ServletRequest getRequest() {\n\t\t\treturn AbstractAuthorizeTagTests.this.request;\n\t\t}\n\n\t\t@Override\n\t\tprotected ServletResponse getResponse() {\n\t\t\treturn AbstractAuthorizeTagTests.this.response;\n\t\t}\n\n\t\t@Override\n\t\tprotected ServletContext getServletContext() {\n\t\t\treturn AbstractAuthorizeTagTests.this.servletContext;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/test/java/org/springframework/security/taglibs/authz/AccessControlListTagTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.authz;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.jsp.tagext.Tag;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockPageContext;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.GenericWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.0\n */\n@SuppressWarnings(\"unchecked\")\npublic class AccessControlListTagTests {\n\n\tAccessControlListTag tag;\n\n\tPermissionEvaluator pe;\n\n\tMockPageContext pageContext;\n\n\tAuthentication bob = new TestingAuthenticationToken(\"bob\", \"bobspass\", \"A\");\n\n\t@BeforeEach\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic void setup() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.bob);\n\t\tthis.tag = new AccessControlListTag();\n\t\tWebApplicationContext ctx = mock(WebApplicationContext.class);\n\t\tthis.pe = mock(PermissionEvaluator.class);\n\t\tMap beanMap = new HashMap();\n\t\tbeanMap.put(\"pe\", this.pe);\n\t\tgiven(ctx.getBeansOfType(PermissionEvaluator.class)).willReturn(beanMap);\n\t\tgiven(ctx.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);\n\t\tMockServletContext servletCtx = new MockServletContext();\n\t\tservletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx);\n\t\tthis.pageContext = new MockPageContext(servletCtx, new MockHttpServletRequest(), new MockHttpServletResponse());\n\t\tthis.tag.setPageContext(this.pageContext);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void bodyIsEvaluatedIfAclGrantsAccess() throws Exception {\n\t\tObject domainObject = new Object();\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, \"READ\")).willReturn(true);\n\t\tthis.tag.setDomainObject(domainObject);\n\t\tthis.tag.setHasPermission(\"READ\");\n\t\tthis.tag.setVar(\"allowed\");\n\t\tassertThat(this.tag.getDomainObject()).isSameAs(domainObject);\n\t\tassertThat(this.tag.getHasPermission()).isEqualTo(\"READ\");\n\t\tassertThat(this.tag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t\tassertThat((Boolean) this.pageContext.getAttribute(\"allowed\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void securityContextHolderStrategyIsUsedIfConfigured() throws Exception {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(this.bob));\n\t\tGenericWebApplicationContext context = new GenericWebApplicationContext();\n\t\tcontext.registerBean(SecurityContextHolderStrategy.class, () -> strategy);\n\t\tcontext.registerBean(PermissionEvaluator.class, () -> this.pe);\n\t\tcontext.refresh();\n\t\tMockServletContext servletCtx = new MockServletContext();\n\t\tservletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);\n\t\tthis.pageContext = new MockPageContext(servletCtx, new MockHttpServletRequest(), new MockHttpServletResponse());\n\t\tthis.tag.setPageContext(this.pageContext);\n\t\tObject domainObject = new Object();\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, \"READ\")).willReturn(true);\n\t\tthis.tag.setDomainObject(domainObject);\n\t\tthis.tag.setHasPermission(\"READ\");\n\t\tthis.tag.setVar(\"allowed\");\n\t\tassertThat(this.tag.getDomainObject()).isSameAs(domainObject);\n\t\tassertThat(this.tag.getHasPermission()).isEqualTo(\"READ\");\n\t\tassertThat(this.tag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t\tassertThat((Boolean) this.pageContext.getAttribute(\"allowed\")).isTrue();\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void childContext() throws Exception {\n\t\tServletContext servletContext = this.pageContext.getServletContext();\n\t\tWebApplicationContext wac = (WebApplicationContext) servletContext\n\t\t\t.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);\n\t\tservletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);\n\t\tservletContext.setAttribute(\"org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher\", wac);\n\t\tObject domainObject = new Object();\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, \"READ\")).willReturn(true);\n\t\tthis.tag.setDomainObject(domainObject);\n\t\tthis.tag.setHasPermission(\"READ\");\n\t\tthis.tag.setVar(\"allowed\");\n\t\tassertThat(this.tag.getDomainObject()).isSameAs(domainObject);\n\t\tassertThat(this.tag.getHasPermission()).isEqualTo(\"READ\");\n\t\tassertThat(this.tag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t\tassertThat((Boolean) this.pageContext.getAttribute(\"allowed\")).isTrue();\n\t}\n\n\t// SEC-2022\n\t@Test\n\tpublic void multiHasPermissionsAreSplit() throws Exception {\n\t\tObject domainObject = new Object();\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, \"READ\")).willReturn(true);\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, \"WRITE\")).willReturn(true);\n\t\tthis.tag.setDomainObject(domainObject);\n\t\tthis.tag.setHasPermission(\"READ,WRITE\");\n\t\tthis.tag.setVar(\"allowed\");\n\t\tassertThat(this.tag.getDomainObject()).isSameAs(domainObject);\n\t\tassertThat(this.tag.getHasPermission()).isEqualTo(\"READ,WRITE\");\n\t\tassertThat(this.tag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t\tassertThat((Boolean) this.pageContext.getAttribute(\"allowed\")).isTrue();\n\t\tverify(this.pe).hasPermission(this.bob, domainObject, \"READ\");\n\t\tverify(this.pe).hasPermission(this.bob, domainObject, \"WRITE\");\n\t\tverifyNoMoreInteractions(this.pe);\n\t}\n\n\t// SEC-2023\n\t@Test\n\tpublic void hasPermissionsBitMaskSupported() throws Exception {\n\t\tObject domainObject = new Object();\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, 1)).willReturn(true);\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, 2)).willReturn(true);\n\t\tthis.tag.setDomainObject(domainObject);\n\t\tthis.tag.setHasPermission(\"1,2\");\n\t\tthis.tag.setVar(\"allowed\");\n\t\tassertThat(this.tag.getDomainObject()).isSameAs(domainObject);\n\t\tassertThat(this.tag.getHasPermission()).isEqualTo(\"1,2\");\n\t\tassertThat(this.tag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t\tassertThat((Boolean) this.pageContext.getAttribute(\"allowed\")).isTrue();\n\t\tverify(this.pe).hasPermission(this.bob, domainObject, 1);\n\t\tverify(this.pe).hasPermission(this.bob, domainObject, 2);\n\t\tverifyNoMoreInteractions(this.pe);\n\t}\n\n\t@Test\n\tpublic void hasPermissionsMixedBitMaskSupported() throws Exception {\n\t\tObject domainObject = new Object();\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, 1)).willReturn(true);\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, \"WRITE\")).willReturn(true);\n\t\tthis.tag.setDomainObject(domainObject);\n\t\tthis.tag.setHasPermission(\"1,WRITE\");\n\t\tthis.tag.setVar(\"allowed\");\n\t\tassertThat(this.tag.getDomainObject()).isSameAs(domainObject);\n\t\tassertThat(this.tag.getHasPermission()).isEqualTo(\"1,WRITE\");\n\t\tassertThat(this.tag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t\tassertThat((Boolean) this.pageContext.getAttribute(\"allowed\")).isTrue();\n\t\tverify(this.pe).hasPermission(this.bob, domainObject, 1);\n\t\tverify(this.pe).hasPermission(this.bob, domainObject, \"WRITE\");\n\t\tverifyNoMoreInteractions(this.pe);\n\t}\n\n\t@Test\n\tpublic void bodyIsSkippedIfAclDeniesAccess() throws Exception {\n\t\tObject domainObject = new Object();\n\t\tgiven(this.pe.hasPermission(this.bob, domainObject, \"READ\")).willReturn(false);\n\t\tthis.tag.setDomainObject(domainObject);\n\t\tthis.tag.setHasPermission(\"READ\");\n\t\tthis.tag.setVar(\"allowed\");\n\t\tassertThat(this.tag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t\tassertThat((Boolean) this.pageContext.getAttribute(\"allowed\")).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/test/java/org/springframework/security/taglibs/authz/AuthenticationTagTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.authz;\n\nimport jakarta.servlet.jsp.JspException;\nimport jakarta.servlet.jsp.tagext.Tag;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockPageContext;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.GenericWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link AuthenticationTag}.\n *\n * @author Ben Alex\n */\npublic class AuthenticationTagTests {\n\n\tprivate final MyAuthenticationTag authenticationTag = new MyAuthenticationTag();\n\n\tprivate final Authentication auth = new TestingAuthenticationToken(\n\t\t\tnew User(\"rodUserDetails\", \"koala\", true, true, true, true, AuthorityUtils.NO_AUTHORITIES), \"koala\",\n\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testOperationWhenPrincipalIsAUserDetailsInstance() throws JspException {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tthis.authenticationTag.setProperty(\"name\");\n\t\tassertThat(this.authenticationTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t\tassertThat(this.authenticationTag.doEndTag()).isEqualTo(Tag.EVAL_PAGE);\n\t\tassertThat(this.authenticationTag.getLastMessage()).isEqualTo(\"rodUserDetails\");\n\t}\n\n\t@Test\n\tpublic void testOperationWhenPrincipalIsAString() throws JspException {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"rodAsString\", \"koala\", AuthorityUtils.NO_AUTHORITIES));\n\t\tthis.authenticationTag.setProperty(\"principal\");\n\t\tassertThat(this.authenticationTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t\tassertThat(this.authenticationTag.doEndTag()).isEqualTo(Tag.EVAL_PAGE);\n\t\tassertThat(this.authenticationTag.getLastMessage()).isEqualTo(\"rodAsString\");\n\t}\n\n\t@Test\n\tpublic void testNestedPropertyIsReadCorrectly() throws JspException {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tthis.authenticationTag.setProperty(\"principal.username\");\n\t\tassertThat(this.authenticationTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t\tassertThat(this.authenticationTag.doEndTag()).isEqualTo(Tag.EVAL_PAGE);\n\t\tassertThat(this.authenticationTag.getLastMessage()).isEqualTo(\"rodUserDetails\");\n\t}\n\n\t@Test\n\tpublic void testOperationWhenPrincipalIsNull() throws JspException {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(null, \"koala\", AuthorityUtils.NO_AUTHORITIES));\n\t\tthis.authenticationTag.setProperty(\"principal\");\n\t\tassertThat(this.authenticationTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t\tassertThat(this.authenticationTag.doEndTag()).isEqualTo(Tag.EVAL_PAGE);\n\t}\n\n\t@Test\n\tpublic void testOperationWhenSecurityContextIsNull() throws Exception {\n\t\tSecurityContextHolder.getContext().setAuthentication(null);\n\t\tthis.authenticationTag.setProperty(\"principal\");\n\t\tassertThat(this.authenticationTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t\tassertThat(this.authenticationTag.doEndTag()).isEqualTo(Tag.EVAL_PAGE);\n\t\tassertThat(this.authenticationTag.getLastMessage()).isNull();\n\t}\n\n\t@Test\n\tpublic void testSkipsBodyIfNullOrEmptyOperation() throws Exception {\n\t\tthis.authenticationTag.setProperty(\"\");\n\t\tassertThat(this.authenticationTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t\tassertThat(this.authenticationTag.doEndTag()).isEqualTo(Tag.EVAL_PAGE);\n\t}\n\n\t@Test\n\tpublic void testThrowsExceptionForUnrecognisedProperty() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.auth);\n\t\tthis.authenticationTag.setProperty(\"qsq\");\n\t\tassertThatExceptionOfType(JspException.class).isThrownBy(() -> {\n\t\t\tthis.authenticationTag.doStartTag();\n\t\t\tthis.authenticationTag.doEndTag();\n\t\t});\n\t}\n\n\t@Test\n\tpublic void htmlEscapingIsUsedByDefault() throws Exception {\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(\"<>& \", \"\"));\n\t\tthis.authenticationTag.setProperty(\"name\");\n\t\tthis.authenticationTag.doStartTag();\n\t\tthis.authenticationTag.doEndTag();\n\t\tassertThat(this.authenticationTag.getLastMessage()).isEqualTo(\"&lt;&gt;&amp;&#32;\");\n\t}\n\n\t@Test\n\tpublic void settingHtmlEscapeToFalsePreventsEscaping() throws Exception {\n\t\tSecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(\"<>& \", \"\"));\n\t\tthis.authenticationTag.setProperty(\"name\");\n\t\tthis.authenticationTag.setHtmlEscape(\"false\");\n\t\tthis.authenticationTag.doStartTag();\n\t\tthis.authenticationTag.doEndTag();\n\t\tassertThat(this.authenticationTag.getLastMessage()).isEqualTo(\"<>& \");\n\t}\n\n\t@Test\n\tpublic void setSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(\n\t\t\t\tnew TestingAuthenticationToken(\"rodAsString\", \"koala\", AuthorityUtils.NO_AUTHORITIES)));\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tGenericWebApplicationContext applicationContext = new GenericWebApplicationContext();\n\t\tapplicationContext.registerBean(SecurityContextHolderStrategy.class, () -> strategy);\n\t\tapplicationContext.refresh();\n\t\tservletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);\n\t\tthis.authenticationTag.setPageContext(new MockPageContext(servletContext));\n\t\tthis.authenticationTag.setProperty(\"principal\");\n\t\tassertThat(this.authenticationTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t\tassertThat(this.authenticationTag.doEndTag()).isEqualTo(Tag.EVAL_PAGE);\n\t\tassertThat(this.authenticationTag.getLastMessage()).isEqualTo(\"rodAsString\");\n\t\tverify(strategy).getContext();\n\t}\n\n\tprivate class MyAuthenticationTag extends AuthenticationTag {\n\n\t\tString lastMessage = null;\n\n\t\tString getLastMessage() {\n\t\t\treturn this.lastMessage;\n\t\t}\n\n\t\t@Override\n\t\tprotected void writeMessage(String msg) {\n\t\t\tthis.lastMessage = msg;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/test/java/org/springframework/security/taglibs/authz/AuthorizeTagTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.authz;\n\nimport jakarta.servlet.jsp.JspException;\nimport jakarta.servlet.jsp.tagext.Tag;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockPageContext;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.access.PermissionEvaluator;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;\nimport org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.StaticWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Francois Beausoleil\n * @author Luke Taylor\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthorizeTagTests {\n\n\t@Mock\n\tprivate PermissionEvaluator permissionEvaluator;\n\n\tprivate JspAuthorizeTag authorizeTag;\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate final TestingAuthenticationToken currentUser = new TestingAuthenticationToken(\"abc\", \"123\",\n\t\t\t\"ROLE SUPERVISOR\", \"ROLE_TELLER\");\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.currentUser);\n\t\tStaticWebApplicationContext ctx = new StaticWebApplicationContext();\n\t\tBeanDefinitionBuilder webExpressionHandler = BeanDefinitionBuilder\n\t\t\t.rootBeanDefinition(DefaultWebSecurityExpressionHandler.class);\n\t\twebExpressionHandler.addPropertyValue(\"permissionEvaluator\", this.permissionEvaluator);\n\t\tctx.registerBeanDefinition(\"expressionHandler\", webExpressionHandler.getBeanDefinition());\n\t\tctx.registerSingleton(\"wipe\", MockWebInvocationPrivilegeEvaluator.class);\n\t\tMockServletContext servletCtx = new MockServletContext();\n\t\tservletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx);\n\t\tthis.authorizeTag = new JspAuthorizeTag();\n\t\tthis.authorizeTag.setPageContext(new MockPageContext(servletCtx, this.request, new MockHttpServletResponse()));\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t// access attribute tests\n\t@Test\n\tpublic void taglibsDocumentationHasPermissionOr() throws Exception {\n\t\tObject domain = new Object();\n\t\tthis.request.setAttribute(\"domain\", domain);\n\t\tthis.authorizeTag.setAccess(\"hasPermission(#domain,'read') or hasPermission(#domain,'write')\");\n\t\tgiven(this.permissionEvaluator.hasPermission(eq(this.currentUser), eq(domain), anyString())).willReturn(true);\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t}\n\n\t@Test\n\tpublic void skipsBodyIfNoAuthenticationPresent() throws Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.authorizeTag.setAccess(\"permitAll\");\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t}\n\n\t@Test\n\tpublic void skipsBodyIfAccessExpressionDeniesAccess() throws Exception {\n\t\tthis.authorizeTag.setAccess(\"denyAll\");\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t}\n\n\t@Test\n\tpublic void showsBodyIfAccessExpressionAllowsAccess() throws Exception {\n\t\tthis.authorizeTag.setAccess(\"permitAll\");\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t}\n\n\t@Test\n\tpublic void requestAttributeIsResolvedAsElVariable() throws JspException {\n\t\tthis.request.setAttribute(\"blah\", \"blah\");\n\t\tthis.authorizeTag.setAccess(\"#blah == 'blah'\");\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t}\n\n\t// url attribute tests\n\t@Test\n\tpublic void skipsBodyWithUrlSetIfNoAuthenticationPresent() throws Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tthis.authorizeTag.setUrl(\"/something\");\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t}\n\n\t@Test\n\tpublic void skipsBodyIfUrlIsNotAllowed() throws Exception {\n\t\tthis.authorizeTag.setUrl(\"/notallowed\");\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t}\n\n\t@Test\n\tpublic void evaluatesBodyIfUrlIsAllowed() throws Exception {\n\t\tthis.authorizeTag.setUrl(\"/allowed\");\n\t\tthis.authorizeTag.setMethod(\"GET\");\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);\n\t}\n\n\t@Test\n\tpublic void skipsBodyIfMethodIsNotAllowed() throws Exception {\n\t\tthis.authorizeTag.setUrl(\"/allowed\");\n\t\tthis.authorizeTag.setMethod(\"POST\");\n\t\tassertThat(this.authorizeTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);\n\t}\n\n\tpublic static class MockWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {\n\n\t\t@Override\n\t\tpublic boolean isAllowed(String uri, Authentication authentication) {\n\t\t\treturn \"/allowed\".equals(uri);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {\n\t\t\treturn \"/allowed\".equals(uri) && (method == null || \"GET\".equals(method));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/test/java/org/springframework/security/taglibs/csrf/AbstractCsrfTagTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.csrf;\n\nimport java.io.UnsupportedEncodingException;\n\nimport jakarta.servlet.jsp.JspException;\nimport jakarta.servlet.jsp.tagext.Tag;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockPageContext;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Nick Williams\n */\npublic class AbstractCsrfTagTests {\n\n\tpublic MockTag tag;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tthis.request = new MockHttpServletRequest(servletContext);\n\t\tthis.response = new MockHttpServletResponse();\n\t\tMockPageContext pageContext = new MockPageContext(servletContext, this.request, this.response);\n\t\tthis.tag = new MockTag();\n\t\tthis.tag.setPageContext(pageContext);\n\t}\n\n\t@Test\n\tpublic void noCsrfDoesNotRender() throws JspException, UnsupportedEncodingException {\n\t\tthis.tag.handleReturn = \"shouldNotBeRendered\";\n\t\tint returned = this.tag.doEndTag();\n\t\tassertThat(returned).as(\"The returned value is not correct.\").isEqualTo(Tag.EVAL_PAGE);\n\t\tassertThat(this.response.getContentAsString()).withFailMessage(\"The output value is not correct.\")\n\t\t\t.isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void hasCsrfRendersReturnedValue() throws JspException, UnsupportedEncodingException {\n\t\tCsrfToken token = new DefaultCsrfToken(\"X-Csrf-Token\", \"_csrf\", \"abc123def456ghi789\");\n\t\tthis.request.setAttribute(CsrfToken.class.getName(), token);\n\t\tthis.tag.handleReturn = \"fooBarBazQux\";\n\t\tint returned = this.tag.doEndTag();\n\t\tassertThat(returned).as(\"The returned value is not correct.\").isEqualTo(Tag.EVAL_PAGE);\n\t\tassertThat(this.response.getContentAsString()).withFailMessage(\"The output value is not correct.\")\n\t\t\t.isEqualTo(\"fooBarBazQux\");\n\t\tassertThat(this.tag.token).as(\"The token is not correct.\").isSameAs(token);\n\t}\n\n\t@Test\n\tpublic void hasCsrfRendersDifferentValue() throws JspException, UnsupportedEncodingException {\n\t\tCsrfToken token = new DefaultCsrfToken(\"X-Csrf-Token\", \"_csrf\", \"abc123def456ghi789\");\n\t\tthis.request.setAttribute(CsrfToken.class.getName(), token);\n\t\tthis.tag.handleReturn = \"<input type=\\\"hidden\\\" />\";\n\t\tint returned = this.tag.doEndTag();\n\t\tassertThat(returned).as(\"The returned value is not correct.\").isEqualTo(Tag.EVAL_PAGE);\n\t\tassertThat(this.response.getContentAsString()).withFailMessage(\"The output value is not correct.\")\n\t\t\t.isEqualTo(\"<input type=\\\"hidden\\\" />\");\n\t\tassertThat(this.tag.token).as(\"The token is not correct.\").isSameAs(token);\n\t}\n\n\tprivate static class MockTag extends AbstractCsrfTag {\n\n\t\tprivate CsrfToken token;\n\n\t\tprivate String handleReturn;\n\n\t\t@Override\n\t\tprotected String handleToken(CsrfToken token) {\n\t\t\tthis.token = token;\n\t\t\treturn this.handleReturn;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/test/java/org/springframework/security/taglibs/csrf/CsrfInputTagTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Nick Williams\n */\npublic class CsrfInputTagTests {\n\n\tpublic CsrfInputTag tag;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.tag = new CsrfInputTag();\n\t}\n\n\t@Test\n\tpublic void handleTokenReturnsHiddenInput() {\n\t\tCsrfToken token = new DefaultCsrfToken(\"X-Csrf-Token\", \"_csrf\", \"abc123def456ghi789\");\n\t\tString value = this.tag.handleToken(token);\n\t\tassertThat(value).as(\"The returned value should not be null.\").isNotNull();\n\t\tassertThat(value).withFailMessage(\"The output is not correct.\")\n\t\t\t.isEqualTo(\"<input type=\\\"hidden\\\" name=\\\"_csrf\\\" value=\\\"abc123def456ghi789\\\" />\");\n\t}\n\n\t@Test\n\tpublic void handleTokenReturnsHiddenInputDifferentTokenValue() {\n\t\tCsrfToken token = new DefaultCsrfToken(\"X-Csrf-Token\", \"csrfParameter\", \"fooBarBazQux\");\n\t\tString value = this.tag.handleToken(token);\n\t\tassertThat(value).as(\"The returned value should not be null.\").isNotNull();\n\t\tassertThat(value).withFailMessage(\"The output is not correct.\")\n\t\t\t.isEqualTo(\"<input type=\\\"hidden\\\" name=\\\"csrfParameter\\\" value=\\\"fooBarBazQux\\\" />\");\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/test/java/org/springframework/security/taglibs/csrf/CsrfMetaTagsTagTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.taglibs.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Nick Williams\n */\npublic class CsrfMetaTagsTagTests {\n\n\tpublic CsrfMetaTagsTag tag;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.tag = new CsrfMetaTagsTag();\n\t}\n\n\t@Test\n\tpublic void handleTokenRendersTags() {\n\t\tCsrfToken token = new DefaultCsrfToken(\"X-Csrf-Token\", \"_csrf\", \"abc123def456ghi789\");\n\t\tString value = this.tag.handleToken(token);\n\t\tassertThat(value).as(\"The returned value should not be null.\").isNotNull();\n\t\tassertThat(value).withFailMessage(\"The output is not correct.\")\n\t\t\t.isEqualTo(\"<meta name=\\\"_csrf_parameter\\\" content=\\\"_csrf\\\" />\"\n\t\t\t\t\t+ \"<meta name=\\\"_csrf_header\\\" content=\\\"X-Csrf-Token\\\" />\"\n\t\t\t\t\t+ \"<meta name=\\\"_csrf\\\" content=\\\"abc123def456ghi789\\\" />\");\n\t}\n\n\t@Test\n\tpublic void handleTokenRendersTagsDifferentToken() {\n\t\tCsrfToken token = new DefaultCsrfToken(\"csrfHeader\", \"csrfParameter\", \"fooBarBazQux\");\n\t\tString value = this.tag.handleToken(token);\n\t\tassertThat(value).as(\"The returned value should not be null.\").isNotNull();\n\t\tassertThat(value).withFailMessage(\"The output is not correct.\")\n\t\t\t.isEqualTo(\"<meta name=\\\"_csrf_parameter\\\" content=\\\"csrfParameter\\\" />\"\n\t\t\t\t\t+ \"<meta name=\\\"_csrf_header\\\" content=\\\"csrfHeader\\\" />\"\n\t\t\t\t\t+ \"<meta name=\\\"_csrf\\\" content=\\\"fooBarBazQux\\\" />\");\n\t}\n\n}\n"
  },
  {
    "path": "taglibs/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "test/spring-security-test.gradle",
    "content": "plugins {\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n\tid 'compile-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi project(':spring-security-web')\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-test'\n\n\toptional project(':spring-security-config')\n\toptional project(':spring-security-oauth2-client')\n\toptional project(':spring-security-oauth2-jose')\n\toptional project(':spring-security-oauth2-resource-server')\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'org.springframework:spring-webmvc'\n\toptional 'org.springframework:spring-webflux'\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(path : ':spring-security-config', configuration : 'tests')\n\ttestImplementation 'com.fasterxml.jackson.core:jackson-databind'\n\ttestImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'\n\ttestImplementation 'io.projectreactor:reactor-test'\n\ttestImplementation 'jakarta.xml.bind:jakarta.xml.bind-api'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation 'org.skyscreamer:jsonassert'\n\ttestImplementation 'org.springframework:spring-webmvc'\n\ttestImplementation 'org.springframework:spring-tx'\n\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/aot/hint/WebTestUtilsRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.aot.hint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.util.ClassUtils;\n\n/**\n * {@link RuntimeHintsRegistrar} implementation that register runtime hints for\n * {@link org.springframework.security.test.web.support.WebTestUtils}.\n *\n * @author Marcus da Coregio\n * @since 6.0\n */\nclass WebTestUtilsRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\tif (!ClassUtils.isPresent(\"jakarta.servlet.Filter\", classLoader)) {\n\t\t\treturn;\n\t\t}\n\t\tregisterFilterChainProxyHints(hints);\n\t\tregisterSecurityContextRepositoryHints(hints);\n\t\tregisterCsrfTokenRepositoryHints(hints);\n\t}\n\n\tprivate void registerFilterChainProxyHints(RuntimeHints hints) {\n\t\thints.reflection().registerType(FilterChainProxy.class, MemberCategory.INVOKE_DECLARED_METHODS);\n\t\thints.reflection()\n\t\t\t.registerType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy\"),\n\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS);\n\t\thints.reflection()\n\t\t\t.registerType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy\"),\n\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS);\n\t}\n\n\tprivate void registerCsrfTokenRepositoryHints(RuntimeHints hints) {\n\t\thints.reflection().registerType(CsrfFilter.class, MemberCategory.ACCESS_DECLARED_FIELDS);\n\t}\n\n\tprivate void registerSecurityContextRepositoryHints(RuntimeHints hints) {\n\t\thints.reflection().registerType(SecurityContextPersistenceFilter.class, MemberCategory.ACCESS_DECLARED_FIELDS);\n\t\thints.reflection().registerType(SecurityContextHolderFilter.class, MemberCategory.ACCESS_DECLARED_FIELDS);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/aot/hint/WithSecurityContextTestRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.aot.hint;\n\nimport java.util.Arrays;\nimport java.util.stream.Stream;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.core.annotation.MergedAnnotation;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.security.test.context.support.WithSecurityContext;\nimport org.springframework.test.context.aot.TestRuntimeHintsRegistrar;\n\nimport static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.SUPERCLASS;\n\n/**\n * {@link TestRuntimeHintsRegistrar} implementation that register runtime hints for\n * {@link WithSecurityContext#factory()} classes.\n *\n * @author Marcus da Coregio\n * @since 6.0\n */\nclass WithSecurityContextTestRuntimeHints implements TestRuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, Class<?> testClass, ClassLoader classLoader) {\n\t\tStream.concat(getClassAnnotations(testClass), getMethodAnnotations(testClass))\n\t\t\t.filter(MergedAnnotation::isPresent)\n\t\t\t.map((withSecurityContext) -> withSecurityContext.getClass(\"factory\"))\n\t\t\t.forEach((factory) -> registerDeclaredConstructors(hints, factory));\n\t}\n\n\tprivate Stream<MergedAnnotation<WithSecurityContext>> getClassAnnotations(Class<?> testClass) {\n\t\treturn MergedAnnotations.search(SUPERCLASS).from(testClass).stream(WithSecurityContext.class);\n\t}\n\n\tprivate Stream<MergedAnnotation<WithSecurityContext>> getMethodAnnotations(Class<?> testClass) {\n\t\treturn Arrays.stream(testClass.getDeclaredMethods())\n\t\t\t.map((method) -> MergedAnnotations.from(method, SUPERCLASS).get(WithSecurityContext.class));\n\t}\n\n\tprivate void registerDeclaredConstructors(RuntimeHints hints, Class<?> factory) {\n\t\thints.reflection().registerType(factory, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * AOT Support for Spring Security test.\n */\n@NullMarked\npackage org.springframework.security.test.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/TestSecurityContextHolder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context;\n\nimport jakarta.servlet.FilterChain;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.util.Assert;\n\n/**\n * The {@link TestSecurityContextHolder} is very similar to {@link SecurityContextHolder},\n * but is necessary for testing. For example, we cannot populate the desired\n * {@link SecurityContext} in {@link SecurityContextHolder} for web based testing. In a\n * web request, the {@link SecurityContextPersistenceFilter} will override the\n * {@link SecurityContextHolder} with the value returned by the\n * {@link SecurityContextRepository}. At the end of the {@link FilterChain} the\n * {@link SecurityContextPersistenceFilter} will clear out the\n * {@link SecurityContextHolder}. This means if we make multiple web requests, we will not\n * know which {@link SecurityContext} to use on subsequent requests.\n *\n * Typical usage is as follows:\n *\n * <ul>\n * <li>Before a test is executed, the {@link TestSecurityContextHolder} is populated.\n * Typically this is done using the\n * {@link org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener}\n * </li>\n * <li>The test is ran. When used with {@link MockMvc} it is typically used with\n * {@link SecurityMockMvcRequestPostProcessors#testSecurityContext()}. Which ensures the\n * {@link SecurityContext} from {@link TestSecurityContextHolder} is properly\n * populated.</li>\n * <li>After the test is executed, the {@link TestSecurityContextHolder} and the\n * {@link SecurityContextHolder} are cleared out</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Tadaya Tsuyukubo\n * @since 4.0\n */\npublic final class TestSecurityContextHolder {\n\n\tprivate static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();\n\n\tprivate TestSecurityContextHolder() {\n\t}\n\n\t/**\n\t * Clears the {@link SecurityContext} from {@link TestSecurityContextHolder} and\n\t * {@link SecurityContextHolder}.\n\t */\n\tpublic static void clearContext() {\n\t\tcontextHolder.remove();\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t/**\n\t * Gets the {@link SecurityContext} from {@link TestSecurityContextHolder}.\n\t * @return the {@link SecurityContext} from {@link TestSecurityContextHolder}.\n\t */\n\tpublic static SecurityContext getContext() {\n\t\tSecurityContext ctx = contextHolder.get();\n\t\tif (ctx == null) {\n\t\t\tctx = getDefaultContext();\n\t\t\tcontextHolder.set(ctx);\n\t\t}\n\t\treturn ctx;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContext} on {@link TestSecurityContextHolder} and\n\t * {@link SecurityContextHolder}.\n\t * @param context the {@link SecurityContext} to use\n\t */\n\tpublic static void setContext(SecurityContext context) {\n\t\tAssert.notNull(context, \"Only non-null SecurityContext instances are permitted\");\n\t\tcontextHolder.set(context);\n\t\tSecurityContextHolder.setContext(context);\n\t}\n\n\t/**\n\t * Creates a new {@link SecurityContext} with the given {@link Authentication}. The\n\t * {@link SecurityContext} is set on {@link TestSecurityContextHolder} and\n\t * {@link SecurityContextHolder}.\n\t * @param authentication the {@link Authentication} to use\n\t * @since 5.1.1\n\t */\n\tpublic static void setAuthentication(Authentication authentication) {\n\t\tAssert.notNull(authentication, \"Only non-null Authentication instances are permitted\");\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\tsetContext(context);\n\t}\n\n\t/**\n\t * Gets the default {@link SecurityContext} by delegating to the\n\t * {@link SecurityContextHolder}\n\t * @return the default {@link SecurityContext}\n\t */\n\tprivate static SecurityContext getDefaultContext() {\n\t\treturn SecurityContextHolder.getContext();\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/TestSecurityContextHolderStrategyAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\npublic final class TestSecurityContextHolderStrategyAdapter implements SecurityContextHolderStrategy {\n\n\t@Override\n\tpublic void clearContext() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Override\n\tpublic SecurityContext getContext() {\n\t\treturn TestSecurityContextHolder.getContext();\n\t}\n\n\t@Override\n\tpublic void setContext(SecurityContext context) {\n\t\tTestSecurityContextHolder.setContext(context);\n\t}\n\n\t@Override\n\tpublic SecurityContext createEmptyContext() {\n\t\treturn SecurityContextHolder.createEmptyContext();\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/annotation/SecurityTestExecutionListeners.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.security.test.context.support.ReactorContextTestExecutionListener;\nimport org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener;\nimport org.springframework.test.context.TestExecutionListeners;\n\n/**\n * There are many times a user may want to use Spring Security's test support (i.e.\n * WithMockUser) but have no need for any other {@link TestExecutionListeners} (i.e. no\n * need to setup an {@link ApplicationContext}). This annotation is a meta annotation that\n * only enables Spring Security's {@link TestExecutionListeners}.\n *\n * @author Rob Winch\n * @since 4.0.2\n * @see WithSecurityContextTestExecutionListener\n * @see ReactorContextTestExecutionListener\n */\n@Documented\n@Inherited\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@TestExecutionListeners(inheritListeners = false,\n\t\tlisteners = { WithSecurityContextTestExecutionListener.class, ReactorContextTestExecutionListener.class })\npublic @interface SecurityTestExecutionListeners {\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for Framework's Test annotations.\n */\n@NullMarked\npackage org.springframework.security.test.context.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security support managing the\n * {@link org.springframework.security.core.context.SecurityContext}.\n */\n@NullMarked\npackage org.springframework.security.test.context;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/DelegatingTestExecutionListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.springframework.test.context.TestContext;\nimport org.springframework.test.context.TestExecutionListener;\nimport org.springframework.test.context.support.AbstractTestExecutionListener;\nimport org.springframework.util.Assert;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\nclass DelegatingTestExecutionListener extends AbstractTestExecutionListener {\n\n\tprivate final TestExecutionListener delegate;\n\n\tDelegatingTestExecutionListener(TestExecutionListener delegate) {\n\t\tAssert.notNull(delegate, \"delegate cannot be null\");\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic void beforeTestClass(TestContext testContext) throws Exception {\n\t\tthis.delegate.beforeTestClass(testContext);\n\t}\n\n\t@Override\n\tpublic void prepareTestInstance(TestContext testContext) throws Exception {\n\t\tthis.delegate.prepareTestInstance(testContext);\n\t}\n\n\t@Override\n\tpublic void beforeTestMethod(TestContext testContext) throws Exception {\n\t\tthis.delegate.beforeTestMethod(testContext);\n\t}\n\n\t@Override\n\tpublic void beforeTestExecution(TestContext testContext) throws Exception {\n\t\tthis.delegate.beforeTestExecution(testContext);\n\t}\n\n\t@Override\n\tpublic void afterTestExecution(TestContext testContext) throws Exception {\n\t\tthis.delegate.afterTestExecution(testContext);\n\t}\n\n\t@Override\n\tpublic void afterTestMethod(TestContext testContext) throws Exception {\n\t\tthis.delegate.afterTestMethod(testContext);\n\t}\n\n\t@Override\n\tpublic void afterTestClass(TestContext testContext) throws Exception {\n\t\tthis.delegate.afterTestClass(testContext);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/ReactorContextTestExecutionListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.reactivestreams.Subscription;\nimport reactor.core.CoreSubscriber;\nimport reactor.core.publisher.Hooks;\nimport reactor.core.publisher.Mono;\nimport reactor.core.publisher.Operators;\nimport reactor.util.context.Context;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.test.context.TestContext;\nimport org.springframework.test.context.TestExecutionListener;\nimport org.springframework.test.context.support.AbstractTestExecutionListener;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Sets up the Reactor Context with the Authentication from the TestSecurityContextHolder\n * and then clears the Reactor Context at the end of the tests.\n *\n * @author Rob Winch\n * @since 5.0\n * @see WithSecurityContextTestExecutionListener\n * @see org.springframework.security.test.context.annotation.SecurityTestExecutionListeners\n */\npublic class ReactorContextTestExecutionListener extends DelegatingTestExecutionListener {\n\n\tprivate static final String HOOKS_CLASS_NAME = \"reactor.core.publisher.Hooks\";\n\n\tprivate static final String CONTEXT_OPERATOR_KEY = SecurityContext.class.getName();\n\n\tpublic ReactorContextTestExecutionListener() {\n\t\tsuper(createDelegate());\n\t}\n\n\tprivate static TestExecutionListener createDelegate() {\n\t\tif (!ClassUtils.isPresent(HOOKS_CLASS_NAME, ReactorContextTestExecutionListener.class.getClassLoader())) {\n\t\t\treturn new AbstractTestExecutionListener() {\n\t\t\t};\n\t\t}\n\t\treturn new DelegateTestExecutionListener();\n\t}\n\n\t/**\n\t * Returns {@code 11000}.\n\t */\n\t@Override\n\tpublic int getOrder() {\n\t\treturn 11000;\n\t}\n\n\tprivate static class DelegateTestExecutionListener extends AbstractTestExecutionListener {\n\n\t\t@Override\n\t\tpublic void beforeTestMethod(TestContext testContext) {\n\t\t\tSecurityContext securityContext = TestSecurityContextHolder.getContext();\n\t\t\tHooks.onLastOperator(CONTEXT_OPERATOR_KEY,\n\t\t\t\t\tOperators.lift((s, sub) -> new SecuritySubContext<>(sub, securityContext)));\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterTestMethod(TestContext testContext) {\n\t\t\tHooks.resetOnLastOperator(CONTEXT_OPERATOR_KEY);\n\t\t}\n\n\t\tprivate static class SecuritySubContext<T> implements CoreSubscriber<T> {\n\n\t\t\tprivate static String CONTEXT_DEFAULTED_ATTR_NAME = SecuritySubContext.class.getName()\n\t\t\t\t.concat(\".CONTEXT_DEFAULTED_ATTR_NAME\");\n\n\t\t\tprivate final CoreSubscriber<T> delegate;\n\n\t\t\tprivate final SecurityContext securityContext;\n\n\t\t\tSecuritySubContext(CoreSubscriber<T> delegate, SecurityContext securityContext) {\n\t\t\t\tthis.delegate = delegate;\n\t\t\t\tthis.securityContext = securityContext;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Context currentContext() {\n\t\t\t\tContext context = this.delegate.currentContext();\n\t\t\t\tif (context.hasKey(CONTEXT_DEFAULTED_ATTR_NAME)) {\n\t\t\t\t\treturn context;\n\t\t\t\t}\n\t\t\t\tcontext = context.put(CONTEXT_DEFAULTED_ATTR_NAME, Boolean.TRUE);\n\t\t\t\tAuthentication authentication = this.securityContext.getAuthentication();\n\t\t\t\tif (authentication == null) {\n\t\t\t\t\treturn context;\n\t\t\t\t}\n\t\t\t\tContext toMerge = ReactiveSecurityContextHolder.withSecurityContext(Mono.just(this.securityContext));\n\t\t\t\treturn toMerge.putAll(context.readOnly());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onSubscribe(Subscription s) {\n\t\t\t\tthis.delegate.onSubscribe(s);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onNext(T t) {\n\t\t\t\tthis.delegate.onNext(t);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onError(Throwable ex) {\n\t\t\t\tthis.delegate.onError(ex);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onComplete() {\n\t\t\t\tthis.delegate.onComplete();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/TestExecutionEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.springframework.test.context.TestContext;\n\n/**\n * Represents the events on the methods of\n * {@link org.springframework.test.context.TestExecutionListener}\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic enum TestExecutionEvent {\n\n\t/**\n\t * Associated to\n\t * {@link org.springframework.test.context.TestExecutionListener#beforeTestMethod(TestContext)}\n\t * event.\n\t */\n\tTEST_METHOD,\n\n\t/**\n\t * Associated to\n\t * {@link org.springframework.test.context.TestExecutionListener#beforeTestExecution(TestContext)}\n\t * event.\n\t */\n\tTEST_EXECUTION\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithAnonymousUser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.test.context.TestContext;\n\n/**\n * When used with {@link WithSecurityContextTestExecutionListener} this annotation can be\n * added to a test method to emulate running with an anonymous user. The\n * {@link SecurityContext} that is used will contain an\n * {@link AnonymousAuthenticationToken}. This is useful when a user wants to run a\n * majority of tests as a specific user and wishes to override a few methods to be\n * anonymous. For example:\n *\n * <pre>\n * <code>\n * &#064;WithMockUser\n * public class SecurityTests {\n *     &#064;Test\n *     &#064;WithAnonymousUser\n *     public void runAsAnonymous() {\n *         // ... run as an anonymous user ...\n *     }\n *\n *     // ... lots of tests ran with a default user ...\n * }\n * </code> </pre>\n *\n * @author Rob Winch\n * @since 4.1\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\n@WithSecurityContext(factory = WithAnonymousUserSecurityContextFactory.class)\npublic @interface WithAnonymousUser {\n\n\t/**\n\t * Determines when the {@link SecurityContext} is setup. The default is before\n\t * {@link TestExecutionEvent#TEST_METHOD} which occurs during\n\t * {@link org.springframework.test.context.TestExecutionListener#beforeTestMethod(TestContext)}\n\t * @return the {@link TestExecutionEvent} to initialize before\n\t * @since 5.1\n\t */\n\t@AliasFor(annotation = WithSecurityContext.class)\n\tTestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithAnonymousUserSecurityContextFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.util.List;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\n/**\n * A {@link WithSecurityContextFactory} that runs with an\n * {@link AnonymousAuthenticationToken}. .\n *\n * @author Rob Winch\n * @since 4.1\n * @see WithUserDetails\n */\nfinal class WithAnonymousUserSecurityContextFactory implements WithSecurityContextFactory<WithAnonymousUser> {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t@Override\n\tpublic SecurityContext createSecurityContext(WithAnonymousUser withUser) {\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\");\n\t\tAuthentication authentication = new AnonymousAuthenticationToken(\"key\", \"anonymous\", authorities);\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\treturn context;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithMockUser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.test.context.TestContext;\nimport org.springframework.test.web.servlet.MockMvc;\n\n/**\n * When used with {@link WithSecurityContextTestExecutionListener} this annotation can be\n * added to a test method to emulate running with a mocked user. In order to work with\n * {@link MockMvc} The {@link SecurityContext} that is used will have the following\n * properties:\n *\n * <ul>\n * <li>The {@link SecurityContext} created with be that of\n * {@link SecurityContextHolder#createEmptyContext()}</li>\n * <li>It will be populated with an {@link UsernamePasswordAuthenticationToken} that uses\n * the username of either {@link #value()} or {@link #username()},\n * {@link GrantedAuthority} that are specified by {@link #roles()}, and a password\n * specified by {@link #password()}.\n * </ul>\n *\n * @see WithUserDetails\n * @author Rob Winch\n * @since 4.0\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\n@WithSecurityContext(factory = WithMockUserSecurityContextFactory.class)\npublic @interface WithMockUser {\n\n\t/**\n\t * Convenience mechanism for specifying the username. The default is \"user\". If\n\t * {@link #username()} is specified it will be used instead of {@link #value()}\n\t * @return\n\t */\n\tString value() default \"user\";\n\n\t/**\n\t * The username to be used. Note that {@link #value()} is a synonym for\n\t * {@link #username()}, but if {@link #username()} is specified it will take\n\t * precedence.\n\t * @return\n\t */\n\tString username() default \"\";\n\n\t/**\n\t * <p>\n\t * The roles to use. The default is \"USER\". A {@link GrantedAuthority} will be created\n\t * for each value within roles. Each value in roles will automatically be prefixed\n\t * with \"ROLE_\". For example, the default will result in \"ROLE_USER\" being used.\n\t * </p>\n\t * <p>\n\t * If {@link #authorities()} is specified this property cannot be changed from the\n\t * default.\n\t * </p>\n\t * @return\n\t */\n\tString[] roles() default { \"USER\" };\n\n\t/**\n\t * <p>\n\t * The authorities to use. A {@link GrantedAuthority} will be created for each value.\n\t * </p>\n\t *\n\t * <p>\n\t * If this property is specified then {@link #roles()} is not used. This differs from\n\t * {@link #roles()} in that it does not prefix the values passed in automatically.\n\t * </p>\n\t * @return\n\t */\n\tString[] authorities() default {};\n\n\t/**\n\t * The password to be used. The default is \"password\".\n\t * @return\n\t */\n\tString password() default \"password\";\n\n\t/**\n\t * Determines when the {@link SecurityContext} is setup. The default is before\n\t * {@link TestExecutionEvent#TEST_METHOD} which occurs during\n\t * {@link org.springframework.test.context.TestExecutionListener#beforeTestMethod(TestContext)}\n\t * @return the {@link TestExecutionEvent} to initialize before\n\t * @since 5.1\n\t */\n\t@AliasFor(annotation = WithSecurityContext.class)\n\tTestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link WithSecurityContextFactory} that works with {@link WithMockUser}.\n *\n * @author Rob Winch\n * @since 4.0\n * @see WithMockUser\n */\nfinal class WithMockUserSecurityContextFactory implements WithSecurityContextFactory<WithMockUser> {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t@Override\n\tpublic SecurityContext createSecurityContext(WithMockUser withUser) {\n\t\tString username = StringUtils.hasLength(withUser.username()) ? withUser.username() : withUser.value();\n\t\tAssert.notNull(username, () -> withUser + \" cannot have null username on both username and value properties\");\n\t\tList<GrantedAuthority> grantedAuthorities = new ArrayList<>();\n\t\tfor (String authority : withUser.authorities()) {\n\t\t\tgrantedAuthorities.add(new SimpleGrantedAuthority(authority));\n\t\t}\n\t\tif (grantedAuthorities.isEmpty()) {\n\t\t\tfor (String role : withUser.roles()) {\n\t\t\t\tAssert.isTrue(!role.startsWith(\"ROLE_\"), () -> \"roles cannot start with ROLE_ Got \" + role);\n\t\t\t\tgrantedAuthorities.add(new SimpleGrantedAuthority(\"ROLE_\" + role));\n\t\t\t}\n\t\t}\n\t\telse if (!(withUser.roles().length == 1 && \"USER\".equals(withUser.roles()[0]))) {\n\t\t\tthrow new IllegalStateException(\"You cannot define roles attribute \" + Arrays.asList(withUser.roles())\n\t\t\t\t\t+ \" with authorities attribute \" + Arrays.asList(withUser.authorities()));\n\t\t}\n\t\tUser principal = new User(username, withUser.password(), true, true, true, true, grantedAuthorities);\n\t\tAuthentication authentication = UsernamePasswordAuthenticationToken.authenticated(principal,\n\t\t\t\tprincipal.getPassword(), principal.getAuthorities());\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\treturn context;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithSecurityContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.test.context.TestContext;\n\n/**\n * <p>\n * An annotation to determine what {@link SecurityContext} to use. The {@link #factory()}\n * attribute must be provided with an instance of\n * {@link WithUserDetailsSecurityContextFactory}.\n * </p>\n *\n * <p>\n * Typically this annotation will be used as an meta-annotation as done with\n * {@link WithMockUser} and {@link WithUserDetails}.\n * </p>\n *\n * <p>\n * If you would like to create your own implementation of\n * {@link WithSecurityContextFactory} you can do so by implementing the interface. You can\n * also use {@link Autowired} and other Spring semantics on the\n * {@link WithSecurityContextFactory} implementation.\n * </p>\n *\n * @author Rob Winch\n * @since 4.0\n */\n@Target({ ElementType.ANNOTATION_TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface WithSecurityContext {\n\n\t/**\n\t * The {@link WithUserDetailsSecurityContextFactory} to use to create the\n\t * {@link SecurityContext}. It can contain {@link Autowired} and other Spring\n\t * annotations.\n\t * @return\n\t */\n\tClass<? extends WithSecurityContextFactory<? extends Annotation>> factory();\n\n\t/**\n\t * Determines when the {@link SecurityContext} is setup. The default is before\n\t * {@link TestExecutionEvent#TEST_METHOD} which occurs during\n\t * {@link org.springframework.test.context.TestExecutionListener#beforeTestMethod(TestContext)}\n\t * @return the {@link TestExecutionEvent} to initialize before\n\t * @since 5.1\n\t */\n\tTestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.lang.annotation.Annotation;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\n\n/**\n * An API that works with WithUserTestExcecutionListener for creating a\n * {@link SecurityContext} that is populated in the {@link TestSecurityContextHolder}.\n *\n * @param <A>\n * @author Rob Winch\n * @since 4.0\n * @see WithSecurityContext\n * @see WithMockUser\n * @see WithUserDetails\n */\npublic interface WithSecurityContextFactory<A extends Annotation> {\n\n\t/**\n\t * Create a {@link SecurityContext} given an Annotation.\n\t * @param annotation the {@link Annotation} to create the {@link SecurityContext}\n\t * from. Cannot be null.\n\t * @return the {@link SecurityContext} to use. Cannot be null.\n\t */\n\tSecurityContext createSecurityContext(A annotation);\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextTestExecutionListener.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.GenericTypeResolver;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.MergedAnnotation;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.context.TestSecurityContextHolderStrategyAdapter;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;\nimport org.springframework.test.context.TestContext;\nimport org.springframework.test.context.TestContextAnnotationUtils;\nimport org.springframework.test.context.TestExecutionListener;\nimport org.springframework.test.context.support.AbstractTestExecutionListener;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link TestExecutionListener} that will find annotations that are annotated with\n * {@link WithSecurityContext} on a test method or at the class level. If found, the\n * {@link WithSecurityContext#factory()} is used to create a {@link SecurityContext} that\n * will be used with this test. If using with {@link MockMvc} the\n * {@link SecurityMockMvcRequestPostProcessors#testSecurityContext()} needs to be used\n * too.\n *\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 4.0\n * @see ReactorContextTestExecutionListener\n * @see org.springframework.security.test.context.annotation.SecurityTestExecutionListeners\n */\npublic class WithSecurityContextTestExecutionListener extends AbstractTestExecutionListener {\n\n\tstatic final String SECURITY_CONTEXT_ATTR_NAME = WithSecurityContextTestExecutionListener.class.getName()\n\t\t.concat(\".SECURITY_CONTEXT\");\n\n\tstatic final SecurityContextHolderStrategy DEFAULT_SECURITY_CONTEXT_HOLDER_STRATEGY = new TestSecurityContextHolderStrategyAdapter();\n\n\tConverter<TestContext, SecurityContextHolderStrategy> securityContextHolderStrategyConverter = (testContext) -> {\n\t\tif (!testContext.hasApplicationContext()) {\n\t\t\treturn DEFAULT_SECURITY_CONTEXT_HOLDER_STRATEGY;\n\t\t}\n\t\tApplicationContext context = testContext.getApplicationContext();\n\t\tif (context.getBeanNamesForType(SecurityContextHolderStrategy.class).length == 0) {\n\t\t\treturn DEFAULT_SECURITY_CONTEXT_HOLDER_STRATEGY;\n\t\t}\n\t\treturn context.getBean(SecurityContextHolderStrategy.class);\n\t};\n\n\t/**\n\t * Sets up the {@link SecurityContext} for each test method. First the specific method\n\t * is inspected for a {@link WithSecurityContext} or {@link Annotation} that has\n\t * {@link WithSecurityContext} on it. If that is not found, the class is inspected. If\n\t * still not found, then no {@link SecurityContext} is populated.\n\t */\n\t@Override\n\tpublic void beforeTestMethod(TestContext testContext) {\n\t\tTestSecurityContext testSecurityContext = createTestSecurityContext(testContext.getTestMethod(), testContext);\n\t\tif (testSecurityContext == null) {\n\t\t\ttestSecurityContext = createTestSecurityContext(testContext.getTestClass(), testContext);\n\t\t}\n\t\tif (testSecurityContext == null) {\n\t\t\treturn;\n\t\t}\n\t\tSupplier<SecurityContext> supplier = testSecurityContext.getSecurityContextSupplier();\n\t\tif (testSecurityContext.getTestExecutionEvent() == TestExecutionEvent.TEST_METHOD) {\n\t\t\tthis.securityContextHolderStrategyConverter.convert(testContext).setContext(supplier.get());\n\t\t}\n\t\telse {\n\t\t\ttestContext.setAttribute(SECURITY_CONTEXT_ATTR_NAME, supplier);\n\t\t}\n\t}\n\n\t/**\n\t * If configured before test execution sets the SecurityContext\n\t * @since 5.1\n\t */\n\t@Override\n\tpublic void beforeTestExecution(TestContext testContext) {\n\t\tSupplier<SecurityContext> supplier = (Supplier<SecurityContext>) testContext\n\t\t\t.removeAttribute(SECURITY_CONTEXT_ATTR_NAME);\n\t\tif (supplier != null) {\n\t\t\tthis.securityContextHolderStrategyConverter.convert(testContext).setContext(supplier.get());\n\t\t}\n\t}\n\n\tprivate @Nullable TestSecurityContext createTestSecurityContext(AnnotatedElement annotated, TestContext context) {\n\t\tWithSecurityContext withSecurityContext = AnnotatedElementUtils.findMergedAnnotation(annotated,\n\t\t\t\tWithSecurityContext.class);\n\t\treturn createTestSecurityContext(annotated, withSecurityContext, context);\n\t}\n\n\tprivate @Nullable TestSecurityContext createTestSecurityContext(Class<?> annotated, TestContext context) {\n\t\tTestContextAnnotationUtils.AnnotationDescriptor<WithSecurityContext> withSecurityContextDescriptor = TestContextAnnotationUtils\n\t\t\t.findAnnotationDescriptor(annotated, WithSecurityContext.class);\n\t\tif (withSecurityContextDescriptor == null) {\n\t\t\treturn null;\n\t\t}\n\t\tWithSecurityContext withSecurityContext = withSecurityContextDescriptor.getAnnotation();\n\t\tClass<?> rootDeclaringClass = withSecurityContextDescriptor.getRootDeclaringClass();\n\t\treturn createTestSecurityContext(rootDeclaringClass, withSecurityContext, context);\n\t}\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tprivate @Nullable TestSecurityContext createTestSecurityContext(AnnotatedElement annotated,\n\t\t\t@Nullable WithSecurityContext withSecurityContext, TestContext context) {\n\t\tif (withSecurityContext == null) {\n\t\t\treturn null;\n\t\t}\n\t\twithSecurityContext = AnnotationUtils.synthesizeAnnotation(withSecurityContext, annotated);\n\t\tWithSecurityContextFactory factory = createFactory(withSecurityContext, context);\n\t\tClass<? extends Annotation> type = (Class<? extends Annotation>) GenericTypeResolver\n\t\t\t.resolveTypeArgument(factory.getClass(), WithSecurityContextFactory.class);\n\t\tAssert.isTrue(type != null, factory.getClass() + \" must specify a Type argument\");\n\t\tAnnotation annotation = findAnnotation(annotated, type);\n\t\tAssert.isTrue(annotation != null, \"No annotation found for \" + type + \" on \" + annotated);\n\t\tSupplier<SecurityContext> supplier = () -> {\n\t\t\ttry {\n\t\t\t\treturn factory.createSecurityContext(annotation);\n\t\t\t}\n\t\t\tcatch (RuntimeException ex) {\n\t\t\t\tthrow new IllegalStateException(\"Unable to create SecurityContext using \" + annotation, ex);\n\t\t\t}\n\t\t};\n\t\tTestExecutionEvent initialize = withSecurityContext.setupBefore();\n\t\treturn new TestSecurityContext(supplier, initialize);\n\t}\n\n\tprivate @Nullable Annotation findAnnotation(AnnotatedElement annotated, Class<? extends Annotation> type) {\n\t\tAnnotation findAnnotation = AnnotatedElementUtils.findMergedAnnotation(annotated, type);\n\t\tif (findAnnotation != null) {\n\t\t\treturn findAnnotation;\n\t\t}\n\t\tMergedAnnotations allAnnotations = MergedAnnotations.from(annotated);\n\t\t// @formatter:off\n\t\treturn allAnnotations.stream()\n\t\t\t.filter((annotationToTest) -> {\n\t\t\t\tWithSecurityContext withSecurityContext = AnnotationUtils.findAnnotation(annotationToTest.getType(),\n\t\t\t\t\t\tWithSecurityContext.class);\n\t\t\t\treturn withSecurityContext != null;\n\t\t\t})\n\t\t\t.map(MergedAnnotation::synthesize)\n\t\t\t.findFirst()\n\t\t\t.orElse(null);\n\t\t// @formatter:on\n\t}\n\n\tprivate WithSecurityContextFactory<? extends Annotation> createFactory(WithSecurityContext withSecurityContext,\n\t\t\tTestContext testContext) {\n\t\tClass<? extends WithSecurityContextFactory<? extends Annotation>> clazz = withSecurityContext.factory();\n\t\ttry {\n\t\t\treturn testContext.getApplicationContext().getAutowireCapableBeanFactory().createBean(clazz);\n\t\t}\n\t\tcatch (IllegalStateException ex) {\n\t\t\treturn BeanUtils.instantiateClass(clazz);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t/**\n\t * Clears out the {@link TestSecurityContextHolder} and the\n\t * {@link SecurityContextHolder} after each test method.\n\t */\n\t@Override\n\tpublic void afterTestMethod(TestContext testContext) {\n\t\tthis.securityContextHolderStrategyConverter.convert(testContext).clearContext();\n\t}\n\n\t/**\n\t * Returns {@code 10000}.\n\t */\n\t@Override\n\tpublic int getOrder() {\n\t\treturn 10000;\n\t}\n\n\tstatic class TestSecurityContext {\n\n\t\tprivate final Supplier<SecurityContext> securityContextSupplier;\n\n\t\tprivate final TestExecutionEvent testExecutionEvent;\n\n\t\tTestSecurityContext(Supplier<SecurityContext> securityContextSupplier, TestExecutionEvent testExecutionEvent) {\n\t\t\tthis.securityContextSupplier = securityContextSupplier;\n\t\t\tthis.testExecutionEvent = testExecutionEvent;\n\t\t}\n\n\t\tSupplier<SecurityContext> getSecurityContextSupplier() {\n\t\t\treturn this.securityContextSupplier;\n\t\t}\n\n\t\tTestExecutionEvent getTestExecutionEvent() {\n\t\t\treturn this.testExecutionEvent;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithUserDetails.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.test.context.TestContext;\nimport org.springframework.test.web.servlet.MockMvc;\n\n/**\n * When used with {@link WithSecurityContextTestExecutionListener} this annotation can be\n * added to a test method to emulate running with a {@link UserDetails} returned from the\n * {@link UserDetailsService}. In order to work with {@link MockMvc} The\n * {@link SecurityContext} that is used will have the following properties:\n *\n * <ul>\n * <li>The {@link SecurityContext} created with be that of\n * {@link SecurityContextHolder#createEmptyContext()}</li>\n * <li>It will be populated with an {@link UsernamePasswordAuthenticationToken} that uses\n * the username of {@link #value()}.\n * </ul>\n *\n * @see WithMockUser\n * @author Rob Winch\n * @since 4.0\n */\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\n@WithSecurityContext(factory = WithUserDetailsSecurityContextFactory.class)\npublic @interface WithUserDetails {\n\n\t/**\n\t * The username to look up in the {@link UserDetailsService}\n\t * @return\n\t */\n\tString value() default \"user\";\n\n\t/**\n\t * The bean name for the {@link UserDetailsService} to use. If this is not provided,\n\t * then the lookup is done by type and expects only a single\n\t * {@link UserDetailsService} bean to be exposed.\n\t * @return the bean name for the {@link UserDetailsService} to use.\n\t * @since 4.1\n\t */\n\tString userDetailsServiceBeanName() default \"\";\n\n\t/**\n\t * Determines when the {@link SecurityContext} is setup. The default is before\n\t * {@link TestExecutionEvent#TEST_METHOD} which occurs during\n\t * {@link org.springframework.test.context.TestExecutionListener#beforeTestMethod(TestContext)}\n\t * @return the {@link TestExecutionEvent} to initialize before\n\t * @since 5.1\n\t */\n\t@AliasFor(annotation = WithSecurityContext.class)\n\tTestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanNotOfRequiredTypeException;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link WithSecurityContextFactory} that works with {@link WithUserDetails} .\n *\n * @author Rob Winch\n * @since 4.0\n * @see WithUserDetails\n */\nfinal class WithUserDetailsSecurityContextFactory implements WithSecurityContextFactory<WithUserDetails> {\n\n\tprivate static final boolean reactorPresent;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate BeanFactory beans;\n\n\tstatic {\n\t\treactorPresent = ClassUtils.isPresent(\"reactor.core.publisher.Mono\",\n\t\t\t\tWithUserDetailsSecurityContextFactory.class.getClassLoader());\n\t}\n\n\t@Autowired\n\tWithUserDetailsSecurityContextFactory(BeanFactory beans) {\n\t\tthis.beans = beans;\n\t}\n\n\t@Override\n\tpublic SecurityContext createSecurityContext(WithUserDetails withUser) {\n\t\tString beanName = withUser.userDetailsServiceBeanName();\n\t\tUserDetailsService userDetailsService = findUserDetailsService(beanName);\n\t\tString username = withUser.value();\n\t\tAssert.hasLength(username, \"value() must be non empty String\");\n\t\tUserDetails principal = userDetailsService.loadUserByUsername(username);\n\t\tAuthentication authentication = UsernamePasswordAuthenticationToken.authenticated(principal,\n\t\t\t\tprincipal.getPassword(), principal.getAuthorities());\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\treturn context;\n\t}\n\n\t@Autowired(required = false)\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tprivate UserDetailsService findUserDetailsService(String beanName) {\n\t\tif (reactorPresent) {\n\t\t\tUserDetailsService reactive = findAndAdaptReactiveUserDetailsService(beanName);\n\t\t\tif (reactive != null) {\n\t\t\t\treturn reactive;\n\t\t\t}\n\t\t}\n\t\treturn StringUtils.hasLength(beanName) ? this.beans.getBean(beanName, UserDetailsService.class)\n\t\t\t\t: this.beans.getBean(UserDetailsService.class);\n\t}\n\n\t@Nullable UserDetailsService findAndAdaptReactiveUserDetailsService(String beanName) {\n\t\ttry {\n\t\t\tReactiveUserDetailsService reactiveUserDetailsService = StringUtils.hasLength(beanName)\n\t\t\t\t\t? this.beans.getBean(beanName, ReactiveUserDetailsService.class)\n\t\t\t\t\t: this.beans.getBean(ReactiveUserDetailsService.class);\n\t\t\treturn new ReactiveUserDetailsServiceAdapter(reactiveUserDetailsService);\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException | BeanNotOfRequiredTypeException ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate final class ReactiveUserDetailsServiceAdapter implements UserDetailsService {\n\n\t\tprivate final ReactiveUserDetailsService userDetailsService;\n\n\t\tprivate ReactiveUserDetailsServiceAdapter(ReactiveUserDetailsService userDetailsService) {\n\t\t\tthis.userDetailsService = userDetailsService;\n\t\t}\n\n\t\t@Override\n\t\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\t\tpublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n\t\t\treturn this.userDetailsService.findByUsername(username).block();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/context/support/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security support classes for the Spring TestContext Framework.\n */\n@NullMarked\npackage org.springframework.security.test.context.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.http.client.reactive.ClientHttpConnector;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.reactive.result.method.annotation.OAuth2AuthorizedClientArgumentResolver;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.server.WebSessionServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;\nimport org.springframework.security.web.server.csrf.CsrfWebFilter;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.reactive.server.MockServerConfigurer;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.test.web.reactive.server.WebTestClientConfigurer;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;\nimport org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;\nimport org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.adapter.WebHttpHandlerBuilder;\n\n/**\n * Test utilities for working with Spring Security and\n * {@link org.springframework.test.web.reactive.server.WebTestClient.Builder#apply(WebTestClientConfigurer)}.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic final class SecurityMockServerConfigurers {\n\n\tprivate SecurityMockServerConfigurers() {\n\t}\n\n\t/**\n\t * Sets up Spring Security's {@link WebTestClient} test support\n\t * @return the MockServerConfigurer to use\n\t */\n\tpublic static MockServerConfigurer springSecurity() {\n\t\treturn new MockServerConfigurer() {\n\n\t\t\t@Override\n\t\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\t\t\t\tbuilder.filters((filters) -> filters.add(0, new MutatorFilter()));\n\t\t\t}\n\n\t\t};\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to use the provided Authentication as the Principal\n\t * @param authentication the Authentication to use.\n\t * @return the configurer to use\n\t */\n\tpublic static <T extends WebTestClientConfigurer & MockServerConfigurer> T mockAuthentication(\n\t\t\tAuthentication authentication) {\n\t\treturn (T) new MutatorWebTestClientConfigurer(() -> Mono.just(authentication).map(SecurityContextImpl::new));\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to use the provided UserDetails to create a\n\t * UsernamePasswordAuthenticationToken as the Principal\n\t * @param userDetails the UserDetails to use.\n\t * @return the configurer to use\n\t */\n\tpublic static <T extends WebTestClientConfigurer & MockServerConfigurer> T mockUser(UserDetails userDetails) {\n\t\treturn mockAuthentication(UsernamePasswordAuthenticationToken.authenticated(userDetails,\n\t\t\t\tuserDetails.getPassword(), userDetails.getAuthorities()));\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to use a UserDetails to create a\n\t * UsernamePasswordAuthenticationToken as the Principal. This uses a default username\n\t * of \"user\", password of \"password\", and granted authorities of \"ROLE_USER\".\n\t * @return the {@link UserExchangeMutator} to use\n\t */\n\tpublic static UserExchangeMutator mockUser() {\n\t\treturn mockUser(\"user\");\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to use a UserDetails to create a\n\t * UsernamePasswordAuthenticationToken as the Principal. This uses a default password\n\t * of \"password\" and granted authorities of \"ROLE_USER\".\n\t * @return the {@link WebTestClientConfigurer} to use\n\t */\n\tpublic static UserExchangeMutator mockUser(String username) {\n\t\treturn new UserExchangeMutator(username);\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to establish a {@link SecurityContext} that has a\n\t * {@link JwtAuthenticationToken} for the {@link Authentication} and a {@link Jwt} for\n\t * the {@link Authentication#getPrincipal()}. All details are declarative and do not\n\t * require the JWT to be valid.\n\t * @return the {@link JwtMutator} to further configure or use\n\t * @since 5.2\n\t */\n\tpublic static JwtMutator mockJwt() {\n\t\treturn new JwtMutator();\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to establish a {@link SecurityContext} that has a\n\t * {@link BearerTokenAuthentication} for the {@link Authentication} and an\n\t * {@link OAuth2AuthenticatedPrincipal} for the {@link Authentication#getPrincipal()}.\n\t * All details are declarative and do not require the token to be valid.\n\t * @return the {@link OpaqueTokenMutator} to further configure or use\n\t * @since 5.3\n\t */\n\tpublic static OpaqueTokenMutator mockOpaqueToken() {\n\t\treturn new OpaqueTokenMutator();\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to establish a {@link SecurityContext} that has a\n\t * {@link OAuth2AuthenticationToken} for the {@link Authentication}. All details are\n\t * declarative and do not require the corresponding OAuth 2.0 tokens to be valid.\n\t * @return the {@link OAuth2LoginMutator} to further configure or use\n\t * @since 5.3\n\t */\n\tpublic static OAuth2LoginMutator mockOAuth2Login() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\", null,\n\t\t\t\tnull, Collections.singleton(\"read\"));\n\t\treturn new OAuth2LoginMutator(accessToken);\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to establish a {@link SecurityContext} that has a\n\t * {@link OAuth2AuthenticationToken} for the {@link Authentication}. All details are\n\t * declarative and do not require the corresponding OAuth 2.0 tokens to be valid.\n\t * @return the {@link OidcLoginMutator} to further configure or use\n\t * @since 5.3\n\t */\n\tpublic static OidcLoginMutator mockOidcLogin() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\", null,\n\t\t\t\tnull, Collections.singleton(\"read\"));\n\t\treturn new OidcLoginMutator(accessToken);\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to establish a {@link OAuth2AuthorizedClient} in the\n\t * session. All details are declarative and do not require the corresponding OAuth 2.0\n\t * tokens to be valid.\n\t *\n\t * <p>\n\t * The support works by associating the authorized client to the ServerWebExchange\n\t * using a {@link ServerOAuth2AuthorizedClientRepository}\n\t * </p>\n\t * @return the {@link OAuth2ClientMutator} to further configure or use\n\t * @since 5.3\n\t */\n\tpublic static OAuth2ClientMutator mockOAuth2Client() {\n\t\treturn new OAuth2ClientMutator();\n\t}\n\n\t/**\n\t * Updates the ServerWebExchange to establish a {@link OAuth2AuthorizedClient} in the\n\t * session. All details are declarative and do not require the corresponding OAuth 2.0\n\t * tokens to be valid.\n\t *\n\t * <p>\n\t * The support works by associating the authorized client to the ServerWebExchange\n\t * using a {@link ServerOAuth2AuthorizedClientRepository}\n\t * </p>\n\t * @param registrationId The registration id associated with the\n\t * {@link OAuth2AuthorizedClient}\n\t * @return the {@link OAuth2ClientMutator} to further configure or use\n\t * @since 5.3\n\t */\n\tpublic static OAuth2ClientMutator mockOAuth2Client(String registrationId) {\n\t\treturn new OAuth2ClientMutator(registrationId);\n\t}\n\n\tpublic static CsrfMutator csrf() {\n\t\treturn new CsrfMutator();\n\t}\n\n\tpublic static final class CsrfMutator implements WebTestClientConfigurer, MockServerConfigurer {\n\n\t\tprivate CsrfMutator() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder,\n\t\t\t\t@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {\n\t\t\tCsrfWebFilter filter = new CsrfWebFilter();\n\t\t\tfilter.setRequireCsrfProtectionMatcher((e) -> ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\t\tif (httpHandlerBuilder == null) {\n\t\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\t\"Cannot apply Csrf because WebHttpHandlerBuilder is null. You must use mock clients.\");\n\t\t\t}\n\t\t\thttpHandlerBuilder.filters((filters) -> filters.add(0, filter));\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the WebServerExchange using {@code {@link\n\t * SecurityMockServerConfigurers#mockUser(UserDetails)}}. Defaults to use a password\n\t * of \"password\" and granted authorities of \"ROLE_USER\".\n\t */\n\tpublic static final class UserExchangeMutator implements WebTestClientConfigurer, MockServerConfigurer {\n\n\t\tprivate final User.UserBuilder userBuilder;\n\n\t\tprivate UserExchangeMutator(String username) {\n\t\t\tthis.userBuilder = User.withUsername(username);\n\t\t\tpassword(\"password\");\n\t\t\troles(\"USER\");\n\t\t}\n\n\t\t/**\n\t\t * Specifies the password to use. Default is \"password\".\n\t\t * @param password the password to use\n\t\t * @return the UserExchangeMutator\n\t\t */\n\t\tpublic UserExchangeMutator password(String password) {\n\t\t\tthis.userBuilder.password(password);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the roles to use. Default is \"USER\". This is similar to authorities\n\t\t * except each role is automatically prefixed with \"ROLE_USER\".\n\t\t * @param roles the roles to use.\n\t\t * @return the UserExchangeMutator\n\t\t */\n\t\tpublic UserExchangeMutator roles(String... roles) {\n\t\t\tthis.userBuilder.roles(roles);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the {@code GrantedAuthority}s to use. Default is \"ROLE_USER\".\n\t\t * @param authorities the authorities to use.\n\t\t * @return the UserExchangeMutator\n\t\t */\n\t\tpublic UserExchangeMutator authorities(GrantedAuthority... authorities) {\n\t\t\tthis.userBuilder.authorities(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the {@code GrantedAuthority}s to use. Default is \"ROLE_USER\".\n\t\t * @param authorities the authorities to use.\n\t\t * @return the UserExchangeMutator\n\t\t */\n\t\tpublic UserExchangeMutator authorities(Collection<? extends GrantedAuthority> authorities) {\n\t\t\tthis.userBuilder.authorities(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the {@code GrantedAuthority}s to use. Default is \"ROLE_USER\".\n\t\t * @param authorities the authorities to use.\n\t\t * @return the UserExchangeMutator\n\t\t */\n\t\tpublic UserExchangeMutator authorities(String... authorities) {\n\t\t\tthis.userBuilder.authorities(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic UserExchangeMutator accountExpired(boolean accountExpired) {\n\t\t\tthis.userBuilder.accountExpired(accountExpired);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic UserExchangeMutator accountLocked(boolean accountLocked) {\n\t\t\tthis.userBuilder.accountLocked(accountLocked);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic UserExchangeMutator credentialsExpired(boolean credentialsExpired) {\n\t\t\tthis.userBuilder.credentialsExpired(credentialsExpired);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic UserExchangeMutator disabled(boolean disabled) {\n\t\t\tthis.userBuilder.disabled(disabled);\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\t\t\tconfigurer().beforeServerCreated(builder);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {\n\t\t\tconfigurer().afterConfigureAdded(serverSpec);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder,\n\t\t\t\t@Nullable WebHttpHandlerBuilder webHttpHandlerBuilder,\n\t\t\t\t@Nullable ClientHttpConnector clientHttpConnector) {\n\t\t\tconfigurer().afterConfigurerAdded(builder, webHttpHandlerBuilder, clientHttpConnector);\n\t\t}\n\n\t\tprivate <T extends WebTestClientConfigurer & MockServerConfigurer> T configurer() {\n\t\t\treturn mockUser(this.userBuilder.build());\n\t\t}\n\n\t}\n\n\tprivate static final class MutatorWebTestClientConfigurer implements WebTestClientConfigurer, MockServerConfigurer {\n\n\t\tprivate final Supplier<Mono<SecurityContext>> context;\n\n\t\tprivate MutatorWebTestClientConfigurer(Supplier<Mono<SecurityContext>> context) {\n\t\t\tthis.context = context;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\t\t\tbuilder.filters(addSetupMutatorFilter());\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder,\n\t\t\t\t@Nullable WebHttpHandlerBuilder webHttpHandlerBuilder,\n\t\t\t\t@Nullable ClientHttpConnector clientHttpConnector) {\n\t\t\tif (webHttpHandlerBuilder == null) {\n\t\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\t\"Cannot apply Spring Security Test Support to null WebHttpHandlerBuilder. This happens when trying to integrate with non-mock based clients.\");\n\t\t\t}\n\t\t\twebHttpHandlerBuilder.filters(addSetupMutatorFilter());\n\t\t}\n\n\t\tprivate Consumer<List<WebFilter>> addSetupMutatorFilter() {\n\t\t\treturn (filters) -> filters.add(0, new SetupMutatorFilter(this.context));\n\t\t}\n\n\t}\n\n\tprivate static final class SetupMutatorFilter implements WebFilter {\n\n\t\tprivate final Supplier<Mono<SecurityContext>> context;\n\n\t\tprivate SetupMutatorFilter(Supplier<Mono<SecurityContext>> context) {\n\t\t\tthis.context = context;\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain webFilterChain) {\n\t\t\texchange.getAttributes().computeIfAbsent(MutatorFilter.ATTRIBUTE_NAME, (key) -> this.context);\n\t\t\treturn webFilterChain.filter(exchange);\n\t\t}\n\n\t}\n\n\tprivate static class MutatorFilter implements WebFilter {\n\n\t\tpublic static final String ATTRIBUTE_NAME = \"context\";\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain webFilterChain) {\n\t\t\tSupplier<Mono<SecurityContext>> context = exchange.getAttribute(ATTRIBUTE_NAME);\n\t\t\tif (context != null) {\n\t\t\t\texchange.getAttributes().remove(ATTRIBUTE_NAME);\n\t\t\t\treturn webFilterChain.filter(exchange)\n\t\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(context.get()));\n\t\t\t}\n\t\t\treturn webFilterChain.filter(exchange);\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the WebServerExchange using {@code {@link\n\t * SecurityMockServerConfigurers#mockAuthentication(Authentication)}}.\n\t *\n\t * @author Jérôme Wacongne &lt;ch4mp&#64;c4-soft.com&gt;\n\t * @author Josh Cummings\n\t * @since 5.2\n\t */\n\tpublic static final class JwtMutator implements WebTestClientConfigurer, MockServerConfigurer {\n\n\t\tprivate Jwt jwt;\n\n\t\tprivate Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\n\t\tprivate JwtMutator() {\n\t\t\tjwt((jwt) -> {\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Jwt.Builder} {@link Consumer} to configure the underlying\n\t\t * {@link Jwt}\n\t\t *\n\t\t * This method first creates a default {@link Jwt.Builder} instance with default\n\t\t * values for the {@code alg}, {@code sub}, and {@code scope} claims. The\n\t\t * {@link Consumer} can then modify these or provide additional configuration.\n\t\t *\n\t\t * Calling {@link SecurityMockServerConfigurers#mockJwt()} is the equivalent of\n\t\t * calling {@code SecurityMockMvcRequestPostProcessors.mockJwt().jwt(() -> {})}.\n\t\t * @param jwtBuilderConsumer For configuring the underlying {@link Jwt}\n\t\t * @return the {@link JwtMutator} for further configuration\n\t\t */\n\t\tpublic JwtMutator jwt(Consumer<Jwt.Builder> jwtBuilderConsumer) {\n\t\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.header(\"alg\", \"none\")\n\t\t\t\t.claim(JwtClaimNames.SUB, \"user\")\n\t\t\t\t.claim(\"scope\", \"read\");\n\t\t\tjwtBuilderConsumer.accept(jwtBuilder);\n\t\t\tthis.jwt = jwtBuilder.build();\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Jwt}\n\t\t * @param jwt The {@link Jwt} to use\n\t\t * @return the {@link JwtMutator} for further configuration\n\t\t */\n\t\tpublic JwtMutator jwt(Jwt jwt) {\n\t\t\tthis.jwt = jwt;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the token\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link JwtMutator} for further configuration\n\t\t */\n\t\tpublic JwtMutator authorities(Collection<GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authoritiesConverter = (jwt) -> authorities;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the token\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link JwtMutator} for further configuration\n\t\t */\n\t\tpublic JwtMutator authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authoritiesConverter = (jwt) -> Arrays.asList(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides the configured {@link Jwt} so that custom authorities can be derived\n\t\t * from it\n\t\t * @param authoritiesConverter the conversion strategy from {@link Jwt} to a\n\t\t * {@link Collection} of {@link GrantedAuthority}s\n\t\t * @return the {@link JwtMutator} for further configuration\n\t\t */\n\t\tpublic JwtMutator authorities(Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter) {\n\t\t\tAssert.notNull(authoritiesConverter, \"authoritiesConverter cannot be null\");\n\t\t\tthis.authoritiesConverter = authoritiesConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\t\t\tconfigurer().beforeServerCreated(builder);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {\n\t\t\tconfigurer().afterConfigureAdded(serverSpec);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder,\n\t\t\t\t@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {\n\t\t\tif (httpHandlerBuilder == null) {\n\t\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\t\"Cannot apply Spring Security Test Support to null WebHttpHandlerBuilder. This happens when trying to integrate with non-mock based clients.\");\n\t\t\t}\n\t\t\thttpHandlerBuilder.filter((exchange, chain) -> {\n\t\t\t\tCsrfWebFilter.skipExchange(exchange);\n\t\t\t\treturn chain.filter(exchange);\n\t\t\t});\n\t\t\tconfigurer().afterConfigurerAdded(builder, httpHandlerBuilder, connector);\n\t\t}\n\n\t\tprivate <T extends WebTestClientConfigurer & MockServerConfigurer> T configurer() {\n\t\t\treturn mockAuthentication(\n\t\t\t\t\tnew JwtAuthenticationToken(this.jwt, this.authoritiesConverter.convert(this.jwt)));\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class OpaqueTokenMutator implements WebTestClientConfigurer, MockServerConfigurer {\n\n\t\tprivate Supplier<Map<String, Object>> attributes = this::defaultAttributes;\n\n\t\tprivate Supplier<Collection<GrantedAuthority>> authorities = this::defaultAuthorities;\n\n\t\tprivate Supplier<OAuth2AuthenticatedPrincipal> principal = this::defaultPrincipal;\n\n\t\tprivate OpaqueTokenMutator() {\n\t\t}\n\n\t\t/**\n\t\t * Mutate the attributes using the given {@link Consumer}\n\t\t * @param attributesConsumer The {@link Consumer} for mutating the {@code Map} of\n\t\t * attributes\n\t\t * @return the {@link OpaqueTokenMutator} for further configuration\n\t\t */\n\t\tpublic OpaqueTokenMutator attributes(Consumer<Map<String, Object>> attributesConsumer) {\n\t\t\tAssert.notNull(attributesConsumer, \"attributesConsumer cannot be null\");\n\t\t\tthis.attributes = () -> {\n\t\t\t\tMap<String, Object> attributes = defaultAttributes();\n\t\t\t\tattributesConsumer.accept(attributes);\n\t\t\t\treturn attributes;\n\t\t\t};\n\t\t\tthis.principal = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the resulting principal\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OpaqueTokenMutator} for further configuration\n\t\t */\n\t\tpublic OpaqueTokenMutator authorities(Collection<GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = () -> authorities;\n\t\t\tthis.principal = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the resulting principal\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OpaqueTokenMutator} for further configuration\n\t\t */\n\t\tpublic OpaqueTokenMutator authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = () -> Arrays.asList(authorities);\n\t\t\tthis.principal = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided principal\n\t\t * @param principal the principal to use\n\t\t * @return the {@link OpaqueTokenMutator} for further configuration\n\t\t */\n\t\tpublic OpaqueTokenMutator principal(OAuth2AuthenticatedPrincipal principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = () -> principal;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\t\t\tconfigurer().beforeServerCreated(builder);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {\n\t\t\tconfigurer().afterConfigureAdded(serverSpec);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder,\n\t\t\t\t@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {\n\t\t\tif (httpHandlerBuilder == null) {\n\t\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\t\"Cannot apply Spring Security Test Support to null WebHttpHandlerBuilder. This happens when trying to integrate with non-mock based clients.\");\n\t\t\t}\n\t\t\thttpHandlerBuilder.filter((exchange, chain) -> {\n\t\t\t\tCsrfWebFilter.skipExchange(exchange);\n\t\t\t\treturn chain.filter(exchange);\n\t\t\t});\n\t\t\tconfigurer().afterConfigurerAdded(builder, httpHandlerBuilder, connector);\n\t\t}\n\n\t\tprivate <T extends WebTestClientConfigurer & MockServerConfigurer> T configurer() {\n\t\t\tOAuth2AuthenticatedPrincipal principal = this.principal.get();\n\t\t\tOAuth2AccessToken accessToken = getOAuth2AccessToken(principal);\n\t\t\tBearerTokenAuthentication token = new BearerTokenAuthentication(principal, accessToken,\n\t\t\t\t\tprincipal.getAuthorities());\n\t\t\treturn mockAuthentication(token);\n\t\t}\n\n\t\tprivate Map<String, Object> defaultAttributes() {\n\t\t\tMap<String, Object> attributes = new HashMap<>();\n\t\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.SUB, \"user\");\n\t\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.SCOPE, \"read\");\n\t\t\treturn attributes;\n\t\t}\n\n\t\tprivate Collection<GrantedAuthority> defaultAuthorities() {\n\t\t\tMap<String, Object> attributes = this.attributes.get();\n\t\t\tObject scope = attributes.get(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t\tif (scope == null) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\tif (scope instanceof Collection) {\n\t\t\t\treturn getAuthorities((Collection) scope);\n\t\t\t}\n\t\t\tString scopes = scope.toString();\n\t\t\tif (!StringUtils.hasText(scopes)) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\treturn getAuthorities(Arrays.asList(scopes.split(\" \")));\n\t\t}\n\n\t\tprivate OAuth2AuthenticatedPrincipal defaultPrincipal() {\n\t\t\treturn new OAuth2IntrospectionAuthenticatedPrincipal(this.attributes.get(), this.authorities.get());\n\t\t}\n\n\t\tprivate Collection<GrantedAuthority> getAuthorities(Collection<?> scopes) {\n\t\t\treturn scopes.stream()\n\t\t\t\t.map((scope) -> new SimpleGrantedAuthority(\"SCOPE_\" + scope))\n\t\t\t\t.collect(Collectors.toList());\n\t\t}\n\n\t\tprivate OAuth2AccessToken getOAuth2AccessToken(OAuth2AuthenticatedPrincipal principal) {\n\t\t\tInstant expiresAt = getInstant(principal.getAttributes(), \"exp\");\n\t\t\tInstant issuedAt = getInstant(principal.getAttributes(), \"iat\");\n\t\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\", issuedAt, expiresAt);\n\t\t}\n\n\t\tprivate @Nullable Instant getInstant(Map<String, Object> attributes, String name) {\n\t\t\tObject value = attributes.get(name);\n\t\t\tif (value == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (value instanceof Instant) {\n\t\t\t\treturn (Instant) value;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(name + \" attribute must be of type Instant\");\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class OAuth2LoginMutator implements WebTestClientConfigurer, MockServerConfigurer {\n\n\t\tprivate final String nameAttributeKey = \"sub\";\n\n\t\tprivate ClientRegistration clientRegistration;\n\n\t\tprivate OAuth2AccessToken accessToken;\n\n\t\tprivate Supplier<Collection<GrantedAuthority>> authorities = this::defaultAuthorities;\n\n\t\tprivate Supplier<Map<String, Object>> attributes = this::defaultAttributes;\n\n\t\tprivate Supplier<OAuth2User> oauth2User = this::defaultPrincipal;\n\n\t\tprivate OAuth2LoginMutator(OAuth2AccessToken accessToken) {\n\t\t\tthis.accessToken = accessToken;\n\t\t\tthis.clientRegistration = clientRegistrationBuilder().build();\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the {@link Authentication}\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OAuth2LoginMutator} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginMutator authorities(Collection<GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = () -> authorities;\n\t\t\tthis.oauth2User = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the {@link Authentication}\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OAuth2LoginMutator} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginMutator authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = () -> Arrays.asList(authorities);\n\t\t\tthis.oauth2User = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Mutate the attributes using the given {@link Consumer}\n\t\t * @param attributesConsumer The {@link Consumer} for mutating the {@code Map} of\n\t\t * attributes\n\t\t * @return the {@link OAuth2LoginMutator} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginMutator attributes(Consumer<Map<String, Object>> attributesConsumer) {\n\t\t\tAssert.notNull(attributesConsumer, \"attributesConsumer cannot be null\");\n\t\t\tthis.attributes = () -> {\n\t\t\t\tMap<String, Object> attributes = defaultAttributes();\n\t\t\t\tattributesConsumer.accept(attributes);\n\t\t\t\treturn attributes;\n\t\t\t};\n\t\t\tthis.oauth2User = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link OAuth2User} as the authenticated user.\n\t\t * @param oauth2User the {@link OAuth2User} to use\n\t\t * @return the {@link OAuth2LoginMutator} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginMutator oauth2User(OAuth2User oauth2User) {\n\t\t\tthis.oauth2User = () -> oauth2User;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link ClientRegistration} as the client to authorize.\n\t\t * <p>\n\t\t * The supplied {@link ClientRegistration} will be registered into a\n\t\t * {@link ServerOAuth2AuthorizedClientRepository}.\n\t\t * @param clientRegistration the {@link ClientRegistration} to use\n\t\t * @return the {@link OAuth2LoginMutator} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginMutator clientRegistration(ClientRegistration clientRegistration) {\n\t\t\tthis.clientRegistration = clientRegistration;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\t\t\tOAuth2AuthenticationToken token = getToken();\n\t\t\tmockOAuth2Client().accessToken(this.accessToken)\n\t\t\t\t.clientRegistration(this.clientRegistration)\n\t\t\t\t.principalName(token.getPrincipal().getName())\n\t\t\t\t.beforeServerCreated(builder);\n\t\t\tmockAuthentication(token).beforeServerCreated(builder);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {\n\t\t\tOAuth2AuthenticationToken token = getToken();\n\t\t\tmockOAuth2Client().accessToken(this.accessToken)\n\t\t\t\t.clientRegistration(this.clientRegistration)\n\t\t\t\t.principalName(token.getPrincipal().getName())\n\t\t\t\t.afterConfigureAdded(serverSpec);\n\t\t\tmockAuthentication(token).afterConfigureAdded(serverSpec);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder,\n\t\t\t\t@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {\n\t\t\tOAuth2AuthenticationToken token = getToken();\n\t\t\tmockOAuth2Client().accessToken(this.accessToken)\n\t\t\t\t.clientRegistration(this.clientRegistration)\n\t\t\t\t.principalName(token.getPrincipal().getName())\n\t\t\t\t.afterConfigurerAdded(builder, httpHandlerBuilder, connector);\n\t\t\tmockAuthentication(token).afterConfigurerAdded(builder, httpHandlerBuilder, connector);\n\t\t}\n\n\t\tprivate OAuth2AuthenticationToken getToken() {\n\t\t\tOAuth2User oauth2User = this.oauth2User.get();\n\t\t\treturn new OAuth2AuthenticationToken(oauth2User, oauth2User.getAuthorities(),\n\t\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\t}\n\n\t\tprivate ClientRegistration.Builder clientRegistrationBuilder() {\n\t\t\treturn ClientRegistration.withRegistrationId(\"test\")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.clientId(\"test-client\")\n\t\t\t\t.authorizationUri(\"https://authorize-uri.example.org\")\n\t\t\t\t.tokenUri(\"https://token-uri.example.org\");\n\t\t}\n\n\t\tprivate Collection<GrantedAuthority> defaultAuthorities() {\n\t\t\tSet<GrantedAuthority> authorities = new LinkedHashSet<>();\n\t\t\tauthorities.add(new OAuth2UserAuthority(this.attributes.get(), this.nameAttributeKey));\n\t\t\tfor (String authority : this.accessToken.getScopes()) {\n\t\t\t\tauthorities.add(new SimpleGrantedAuthority(\"SCOPE_\" + authority));\n\t\t\t}\n\t\t\treturn authorities;\n\t\t}\n\n\t\tprivate Map<String, Object> defaultAttributes() {\n\t\t\tMap<String, Object> attributes = new HashMap<>();\n\t\t\tattributes.put(this.nameAttributeKey, \"user\");\n\t\t\treturn attributes;\n\t\t}\n\n\t\tprivate OAuth2User defaultPrincipal() {\n\t\t\treturn new DefaultOAuth2User(this.authorities.get(), this.attributes.get(), this.nameAttributeKey);\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class OidcLoginMutator implements WebTestClientConfigurer, MockServerConfigurer {\n\n\t\tprivate ClientRegistration clientRegistration;\n\n\t\tprivate OAuth2AccessToken accessToken;\n\n\t\tprivate @Nullable OidcIdToken idToken;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate OidcUserInfo userInfo;\n\n\t\tprivate Supplier<OidcUser> oidcUser = this::defaultPrincipal;\n\n\t\tprivate @Nullable Collection<GrantedAuthority> authorities;\n\n\t\tprivate OidcLoginMutator(OAuth2AccessToken accessToken) {\n\t\t\tthis.accessToken = accessToken;\n\t\t\tthis.clientRegistration = clientRegistrationBuilder().build();\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the {@link Authentication}\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OidcLoginMutator} for further configuration\n\t\t */\n\t\tpublic OidcLoginMutator authorities(Collection<GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = authorities;\n\t\t\tthis.oidcUser = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the {@link Authentication}\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OidcLoginMutator} for further configuration\n\t\t */\n\t\tpublic OidcLoginMutator authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = Arrays.asList(authorities);\n\t\t\tthis.oidcUser = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link OidcIdToken} when constructing the authenticated user\n\t\t * @param idTokenBuilderConsumer a {@link Consumer} of a\n\t\t * {@link OidcIdToken.Builder}\n\t\t * @return the {@link OidcLoginMutator} for further configuration\n\t\t */\n\t\tpublic OidcLoginMutator idToken(Consumer<OidcIdToken.Builder> idTokenBuilderConsumer) {\n\t\t\tOidcIdToken.Builder builder = OidcIdToken.withTokenValue(\"id-token\");\n\t\t\tbuilder.subject(\"user\");\n\t\t\tidTokenBuilderConsumer.accept(builder);\n\t\t\tthis.idToken = builder.build();\n\t\t\tthis.oidcUser = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link OidcUserInfo} when constructing the authenticated user\n\t\t * @param userInfoBuilderConsumer a {@link Consumer} of a\n\t\t * {@link OidcUserInfo.Builder}\n\t\t * @return the {@link OidcLoginMutator} for further configuration\n\t\t */\n\t\tpublic OidcLoginMutator userInfoToken(Consumer<OidcUserInfo.Builder> userInfoBuilderConsumer) {\n\t\t\tOidcUserInfo.Builder builder = OidcUserInfo.builder();\n\t\t\tuserInfoBuilderConsumer.accept(builder);\n\t\t\tthis.userInfo = builder.build();\n\t\t\tthis.oidcUser = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link OidcUser} as the authenticated user.\n\t\t * <p>\n\t\t * Supplying an {@link OidcUser} will take precedence over {@link #idToken},\n\t\t * {@link #userInfo}, and list of {@link GrantedAuthority}s to use.\n\t\t * @param oidcUser the {@link OidcUser} to use\n\t\t * @return the {@link OidcLoginMutator} for further configuration\n\t\t */\n\t\tpublic OidcLoginMutator oidcUser(OidcUser oidcUser) {\n\t\t\tthis.oidcUser = () -> oidcUser;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link ClientRegistration} as the client to authorize.\n\t\t * <p>\n\t\t * The supplied {@link ClientRegistration} will be registered into a\n\t\t * {@link ServerOAuth2AuthorizedClientRepository}.\n\t\t * @param clientRegistration the {@link ClientRegistration} to use\n\t\t * @return the {@link OidcLoginMutator} for further configuration\n\t\t */\n\t\tpublic OidcLoginMutator clientRegistration(ClientRegistration clientRegistration) {\n\t\t\tthis.clientRegistration = clientRegistration;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\t\t\tOAuth2AuthenticationToken token = getToken();\n\t\t\tmockOAuth2Client().accessToken(this.accessToken)\n\t\t\t\t.principalName(token.getPrincipal().getName())\n\t\t\t\t.clientRegistration(this.clientRegistration)\n\t\t\t\t.beforeServerCreated(builder);\n\t\t\tmockAuthentication(token).beforeServerCreated(builder);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {\n\t\t\tOAuth2AuthenticationToken token = getToken();\n\t\t\tmockOAuth2Client().accessToken(this.accessToken)\n\t\t\t\t.principalName(token.getPrincipal().getName())\n\t\t\t\t.clientRegistration(this.clientRegistration)\n\t\t\t\t.afterConfigureAdded(serverSpec);\n\t\t\tmockAuthentication(token).afterConfigureAdded(serverSpec);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder,\n\t\t\t\t@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {\n\t\t\tOAuth2AuthenticationToken token = getToken();\n\t\t\tmockOAuth2Client().accessToken(this.accessToken)\n\t\t\t\t.principalName(token.getPrincipal().getName())\n\t\t\t\t.clientRegistration(this.clientRegistration)\n\t\t\t\t.afterConfigurerAdded(builder, httpHandlerBuilder, connector);\n\t\t\tmockAuthentication(token).afterConfigurerAdded(builder, httpHandlerBuilder, connector);\n\t\t}\n\n\t\tprivate ClientRegistration.Builder clientRegistrationBuilder() {\n\t\t\treturn ClientRegistration.withRegistrationId(\"test\")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.clientId(\"test-client\")\n\t\t\t\t.authorizationUri(\"https://authorize-uri.example.org\")\n\t\t\t\t.tokenUri(\"https://token-uri.example.org\");\n\t\t}\n\n\t\tprivate OAuth2AuthenticationToken getToken() {\n\t\t\tOidcUser oidcUser = this.oidcUser.get();\n\t\t\treturn new OAuth2AuthenticationToken(oidcUser, oidcUser.getAuthorities(),\n\t\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\t}\n\n\t\tprivate Collection<GrantedAuthority> getAuthorities() {\n\t\t\tif (this.authorities != null) {\n\t\t\t\treturn this.authorities;\n\t\t\t}\n\t\t\tSet<GrantedAuthority> authorities = new LinkedHashSet<>();\n\t\t\tauthorities.add(new OidcUserAuthority(getOidcIdToken(), getOidcUserInfo()));\n\t\t\tfor (String authority : this.accessToken.getScopes()) {\n\t\t\t\tauthorities.add(new SimpleGrantedAuthority(\"SCOPE_\" + authority));\n\t\t\t}\n\t\t\treturn authorities;\n\t\t}\n\n\t\tprivate OidcIdToken getOidcIdToken() {\n\t\t\tif (this.idToken != null) {\n\t\t\t\treturn this.idToken;\n\t\t\t}\n\t\t\treturn new OidcIdToken(\"id-token\", null, null, Collections.singletonMap(IdTokenClaimNames.SUB, \"user\"));\n\t\t}\n\n\t\tprivate OidcUserInfo getOidcUserInfo() {\n\t\t\treturn this.userInfo;\n\t\t}\n\n\t\tprivate OidcUser defaultPrincipal() {\n\t\t\treturn new DefaultOidcUser(getAuthorities(), getOidcIdToken(), this.userInfo);\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class OAuth2ClientMutator implements WebTestClientConfigurer, MockServerConfigurer {\n\n\t\tprivate String registrationId = \"test\";\n\n\t\tprivate @Nullable ClientRegistration clientRegistration;\n\n\t\tprivate String principalName = \"user\";\n\n\t\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token\", null, null, Collections.singleton(\"read\"));\n\n\t\tprivate OAuth2ClientMutator() {\n\t\t}\n\n\t\tprivate OAuth2ClientMutator(String registrationId) {\n\t\t\tthis.registrationId = registrationId;\n\t\t\tclientRegistration((c) -> {\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link ClientRegistration}\n\t\t * @param clientRegistration\n\t\t * @return the\n\t\t * {@link SecurityMockMvcRequestPostProcessors.OAuth2ClientRequestPostProcessor}\n\t\t * for further configuration\n\t\t */\n\t\tpublic OAuth2ClientMutator clientRegistration(ClientRegistration clientRegistration) {\n\t\t\tthis.clientRegistration = clientRegistration;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link Consumer} to configure a {@link ClientRegistration}\n\t\t * @param clientRegistrationConfigurer the {@link ClientRegistration} configurer\n\t\t * @return the\n\t\t * {@link SecurityMockMvcRequestPostProcessors.OAuth2ClientRequestPostProcessor}\n\t\t * for further configuration\n\t\t */\n\t\tpublic OAuth2ClientMutator clientRegistration(\n\t\t\t\tConsumer<ClientRegistration.Builder> clientRegistrationConfigurer) {\n\t\t\tClientRegistration.Builder builder = clientRegistrationBuilder();\n\t\t\tclientRegistrationConfigurer.accept(builder);\n\t\t\tthis.clientRegistration = builder.build();\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this as the resource owner's principal name\n\t\t * @param principalName the resource owner's principal name\n\t\t * @return the {@link OAuth2ClientMutator} for further configuration\n\t\t */\n\t\tpublic OAuth2ClientMutator principalName(String principalName) {\n\t\t\tAssert.notNull(principalName, \"principalName cannot be null\");\n\t\t\tthis.principalName = principalName;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link OAuth2AccessToken}\n\t\t * @param accessToken the {@link OAuth2AccessToken} to use\n\t\t * @return the\n\t\t * {@link SecurityMockMvcRequestPostProcessors.OAuth2ClientRequestPostProcessor}\n\t\t * for further configuration\n\t\t */\n\t\tpublic OAuth2ClientMutator accessToken(OAuth2AccessToken accessToken) {\n\t\t\tthis.accessToken = accessToken;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeServerCreated(WebHttpHandlerBuilder builder) {\n\t\t\tbuilder.filters(addAuthorizedClientFilter());\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterConfigurerAdded(WebTestClient.Builder builder,\n\t\t\t\t@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {\n\t\t\tif (httpHandlerBuilder == null) {\n\t\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\t\"Cannot apply Spring Security Test Support to null WebHttpHandlerBuilder. This happens when trying to integrate with non-mock based clients.\");\n\t\t\t}\n\t\t\thttpHandlerBuilder.filters(addAuthorizedClientFilter());\n\t\t}\n\n\t\tprivate Consumer<List<WebFilter>> addAuthorizedClientFilter() {\n\t\t\tOAuth2AuthorizedClient client = getClient();\n\t\t\treturn (filters) -> filters.add(0, (exchange, chain) -> {\n\t\t\t\tServerOAuth2AuthorizedClientRepository authorizedClientRepository = OAuth2ClientServerTestUtils\n\t\t\t\t\t.getAuthorizedClientRepository(exchange);\n\t\t\t\tif (!(authorizedClientRepository instanceof TestOAuth2AuthorizedClientRepository)) {\n\t\t\t\t\tAssert.isTrue(authorizedClientRepository != null,\n\t\t\t\t\t\t\t\"ServerOAuth2AuthorizedClientRepository cannot be null\");\n\t\t\t\t\tauthorizedClientRepository = new TestOAuth2AuthorizedClientRepository(authorizedClientRepository);\n\t\t\t\t\tOAuth2ClientServerTestUtils.setAuthorizedClientRepository(exchange, authorizedClientRepository);\n\t\t\t\t}\n\t\t\t\tTestOAuth2AuthorizedClientRepository.enable(exchange);\n\t\t\t\tAuthentication anonymousPrincipal = new AnonymousAuthenticationToken(\"anonymous\", \"anonymousUser\",\n\t\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\t\t\treturn authorizedClientRepository.saveAuthorizedClient(client, anonymousPrincipal, exchange)\n\t\t\t\t\t.then(chain.filter(exchange));\n\t\t\t});\n\t\t}\n\n\t\tprivate OAuth2AuthorizedClient getClient() {\n\t\t\tAssert.notNull(this.clientRegistration,\n\t\t\t\t\t\"Please specify a ClientRegistration via one of the clientRegistration methods\");\n\t\t\treturn new OAuth2AuthorizedClient(this.clientRegistration, this.principalName, this.accessToken);\n\t\t}\n\n\t\tprivate ClientRegistration.Builder clientRegistrationBuilder() {\n\t\t\treturn ClientRegistration.withRegistrationId(this.registrationId)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.clientId(\"test-client\")\n\t\t\t\t.clientSecret(\"test-secret\")\n\t\t\t\t.authorizationUri(\"https://idp.example.org/oauth/authorize\")\n\t\t\t\t.tokenUri(\"https://idp.example.org/oauth/token\");\n\t\t}\n\n\t\t/**\n\t\t * Used to wrap the {@link OAuth2AuthorizedClientRepository} to provide support\n\t\t * for testing when the request is wrapped\n\t\t */\n\t\tprivate static final class TestOAuth2AuthorizedClientManager implements ReactiveOAuth2AuthorizedClientManager {\n\n\t\t\tstatic final String ENABLED_ATTR_NAME = TestOAuth2AuthorizedClientManager.class.getName()\n\t\t\t\t.concat(\".ENABLED\");\n\n\t\t\tprivate final ReactiveOAuth2AuthorizedClientManager delegate;\n\n\t\t\tprivate @Nullable ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t\t\tTestOAuth2AuthorizedClientManager(ReactiveOAuth2AuthorizedClientManager delegate) {\n\t\t\t\tthis.delegate = delegate;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {\n\t\t\t\tServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());\n\t\t\t\tif (exchange != null && isEnabled(exchange) && this.authorizedClientRepository != null) {\n\t\t\t\t\treturn this.authorizedClientRepository.loadAuthorizedClient(\n\t\t\t\t\t\t\tauthorizeRequest.getClientRegistrationId(), authorizeRequest.getPrincipal(), exchange);\n\t\t\t\t}\n\t\t\t\treturn this.delegate.authorize(authorizeRequest);\n\t\t\t}\n\n\t\t\tstatic void enable(ServerWebExchange exchange) {\n\t\t\t\texchange.getAttributes().put(ENABLED_ATTR_NAME, Boolean.TRUE);\n\t\t\t}\n\n\t\t\tboolean isEnabled(@Nullable ServerWebExchange exchange) {\n\t\t\t\treturn exchange != null && Boolean.TRUE.equals(exchange.getAttribute(ENABLED_ATTR_NAME));\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Used to wrap the {@link OAuth2AuthorizedClientRepository} to provide support\n\t\t * for testing when the request is wrapped\n\t\t */\n\t\tstatic final class TestOAuth2AuthorizedClientRepository implements ServerOAuth2AuthorizedClientRepository {\n\n\t\t\tstatic final String TOKEN_ATTR_NAME = TestOAuth2AuthorizedClientRepository.class.getName().concat(\".TOKEN\");\n\n\t\t\tstatic final String ENABLED_ATTR_NAME = TestOAuth2AuthorizedClientRepository.class.getName()\n\t\t\t\t.concat(\".ENABLED\");\n\n\t\t\tprivate final ServerOAuth2AuthorizedClientRepository delegate;\n\n\t\t\tTestOAuth2AuthorizedClientRepository(ServerOAuth2AuthorizedClientRepository delegate) {\n\t\t\t\tthis.delegate = delegate;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic <T extends OAuth2AuthorizedClient> Mono<T> loadAuthorizedClient(String clientRegistrationId,\n\t\t\t\t\tAuthentication principal, ServerWebExchange exchange) {\n\t\t\t\tif (isEnabled(exchange)) {\n\t\t\t\t\tT attr = exchange.getAttribute(TOKEN_ATTR_NAME);\n\t\t\t\t\treturn Mono.justOrEmpty(attr);\n\t\t\t\t}\n\t\t\t\treturn this.delegate.loadAuthorizedClient(clientRegistrationId, principal, exchange);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<Void> saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\t\t\tServerWebExchange exchange) {\n\t\t\t\tif (isEnabled(exchange)) {\n\t\t\t\t\texchange.getAttributes().put(TOKEN_ATTR_NAME, authorizedClient);\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t}\n\t\t\t\treturn this.delegate.saveAuthorizedClient(authorizedClient, principal, exchange);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Mono<Void> removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\t\t\tServerWebExchange exchange) {\n\t\t\t\tif (isEnabled(exchange)) {\n\t\t\t\t\texchange.getAttributes().remove(TOKEN_ATTR_NAME);\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t}\n\t\t\t\treturn this.delegate.removeAuthorizedClient(clientRegistrationId, principal, exchange);\n\t\t\t}\n\n\t\t\tstatic void enable(ServerWebExchange exchange) {\n\t\t\t\texchange.getAttributes().put(ENABLED_ATTR_NAME, Boolean.TRUE);\n\t\t\t}\n\n\t\t\tboolean isEnabled(ServerWebExchange exchange) {\n\t\t\t\treturn Boolean.TRUE.equals(exchange.getAttribute(ENABLED_ATTR_NAME));\n\t\t\t}\n\n\t\t}\n\n\t\tprivate static final class OAuth2ClientServerTestUtils {\n\n\t\t\tprivate static final ServerOAuth2AuthorizedClientRepository DEFAULT_CLIENT_REPO = new WebSessionServerOAuth2AuthorizedClientRepository();\n\n\t\t\tprivate OAuth2ClientServerTestUtils() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Gets the {@link ServerOAuth2AuthorizedClientRepository} for the specified\n\t\t\t * {@link ServerWebExchange}. If one is not found, one based off of\n\t\t\t * {@link WebSessionServerOAuth2AuthorizedClientRepository} is used.\n\t\t\t * @param exchange the {@link ServerWebExchange} to obtain the\n\t\t\t * {@link ReactiveOAuth2AuthorizedClientManager}\n\t\t\t * @return the {@link ReactiveOAuth2AuthorizedClientManager} for the specified\n\t\t\t * {@link ServerWebExchange}\n\t\t\t */\n\t\t\tstatic @Nullable ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository(\n\t\t\t\t\tServerWebExchange exchange) {\n\t\t\t\tReactiveOAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(exchange);\n\t\t\t\tif (manager == null) {\n\t\t\t\t\treturn DEFAULT_CLIENT_REPO;\n\t\t\t\t}\n\t\t\t\tif (manager instanceof DefaultReactiveOAuth2AuthorizedClientManager) {\n\t\t\t\t\treturn (ServerOAuth2AuthorizedClientRepository) ReflectionTestUtils.getField(manager,\n\t\t\t\t\t\t\t\"authorizedClientRepository\");\n\t\t\t\t}\n\t\t\t\tif (manager instanceof TestOAuth2AuthorizedClientManager) {\n\t\t\t\t\treturn ((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository;\n\t\t\t\t}\n\t\t\t\treturn DEFAULT_CLIENT_REPO;\n\t\t\t}\n\n\t\t\tstatic void setAuthorizedClientRepository(ServerWebExchange exchange,\n\t\t\t\t\tServerOAuth2AuthorizedClientRepository repository) {\n\t\t\t\tReactiveOAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(exchange);\n\t\t\t\tif (manager == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (manager instanceof DefaultReactiveOAuth2AuthorizedClientManager) {\n\t\t\t\t\tReflectionTestUtils.setField(manager, \"authorizedClientRepository\", repository);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!(manager instanceof TestOAuth2AuthorizedClientManager)) {\n\t\t\t\t\tmanager = new TestOAuth2AuthorizedClientManager(manager);\n\t\t\t\t\tsetOAuth2AuthorizedClientManager(exchange, manager);\n\t\t\t\t}\n\t\t\t\tTestOAuth2AuthorizedClientManager.enable(exchange);\n\t\t\t\t((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository = repository;\n\t\t\t}\n\n\t\t\tstatic @Nullable ReactiveOAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(\n\t\t\t\t\tServerWebExchange exchange) {\n\t\t\t\tOAuth2AuthorizedClientArgumentResolver resolver = findResolver(exchange,\n\t\t\t\t\t\tOAuth2AuthorizedClientArgumentResolver.class);\n\t\t\t\tif (resolver == null) {\n\t\t\t\t\treturn (authorizeRequest) -> DEFAULT_CLIENT_REPO.loadAuthorizedClient(\n\t\t\t\t\t\t\tauthorizeRequest.getClientRegistrationId(), authorizeRequest.getPrincipal(), exchange);\n\t\t\t\t}\n\t\t\t\treturn (ReactiveOAuth2AuthorizedClientManager) ReflectionTestUtils.getField(resolver,\n\t\t\t\t\t\t\"authorizedClientManager\");\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the {@link ReactiveOAuth2AuthorizedClientManager} for the specified\n\t\t\t * {@link ServerWebExchange}.\n\t\t\t * @param exchange the {@link ServerWebExchange} to obtain the\n\t\t\t * {@link ReactiveOAuth2AuthorizedClientManager}\n\t\t\t * @param manager the {@link ReactiveOAuth2AuthorizedClientManager} to set\n\t\t\t */\n\t\t\tstatic void setOAuth2AuthorizedClientManager(ServerWebExchange exchange,\n\t\t\t\t\tReactiveOAuth2AuthorizedClientManager manager) {\n\t\t\t\tOAuth2AuthorizedClientArgumentResolver resolver = findResolver(exchange,\n\t\t\t\t\t\tOAuth2AuthorizedClientArgumentResolver.class);\n\t\t\t\tif (resolver == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tReflectionTestUtils.setField(resolver, \"authorizedClientManager\", manager);\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tstatic <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(ServerWebExchange exchange,\n\t\t\t\t\tClass<T> resolverClass) {\n\t\t\t\tif (!ClassUtils.isPresent(\n\t\t\t\t\t\t\"org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter\",\n\t\t\t\t\t\tnull)) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn WebFluxClasspathGuard.findResolver(exchange, resolverClass);\n\t\t\t}\n\n\t\t\tprivate static class WebFluxClasspathGuard {\n\n\t\t\t\tstatic <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(ServerWebExchange exchange,\n\t\t\t\t\t\tClass<T> resolverClass) {\n\t\t\t\t\tRequestMappingHandlerAdapter handlerAdapter = getRequestMappingHandlerAdapter(exchange);\n\t\t\t\t\tif (handlerAdapter == null) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tArgumentResolverConfigurer configurer = handlerAdapter.getArgumentResolverConfigurer();\n\t\t\t\t\tif (configurer == null) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tList<HandlerMethodArgumentResolver> resolvers = (List<HandlerMethodArgumentResolver>) ReflectionTestUtils\n\t\t\t\t\t\t.invokeGetterMethod(configurer, \"customResolvers\");\n\t\t\t\t\tif (resolvers == null) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tfor (HandlerMethodArgumentResolver resolver : resolvers) {\n\t\t\t\t\t\tif (resolverClass.isAssignableFrom(resolver.getClass())) {\n\t\t\t\t\t\t\treturn (T) resolver;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tprivate static @Nullable RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(\n\t\t\t\t\t\tServerWebExchange exchange) {\n\t\t\t\t\tApplicationContext context = exchange.getApplicationContext();\n\t\t\t\t\tif (context != null) {\n\t\t\t\t\t\tString[] names = context.getBeanNamesForType(RequestMappingHandlerAdapter.class);\n\t\t\t\t\t\tif (names.length > 0) {\n\t\t\t\t\t\t\treturn (RequestMappingHandlerAdapter) context.getBean(names[0]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/reactive/server/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security upport for testing Spring WebFlux server endpoints via WebTestClient.\n */\n@NullMarked\npackage org.springframework.security.test.web.reactive.server;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuilders.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport jakarta.servlet.ServletContext;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.Mergeable;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.RequestBuilder;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\n\n/**\n * Contains Spring Security related {@link MockMvc} {@link RequestBuilder}s.\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic final class SecurityMockMvcRequestBuilders {\n\n\tprivate SecurityMockMvcRequestBuilders() {\n\t}\n\n\t/**\n\t * Creates a request (including any necessary {@link CsrfToken}) that will submit a\n\t * form based login to POST \"/login\".\n\t * @return the FormLoginRequestBuilder for further customizations\n\t */\n\tpublic static FormLoginRequestBuilder formLogin() {\n\t\treturn new FormLoginRequestBuilder();\n\t}\n\n\t/**\n\t * Creates a request (including any necessary {@link CsrfToken}) that will submit a\n\t * form based login to POST {@code loginProcessingUrl}.\n\t * @param loginProcessingUrl the URL to POST to\n\t * @return the FormLoginRequestBuilder for further customizations\n\t */\n\tpublic static FormLoginRequestBuilder formLogin(String loginProcessingUrl) {\n\t\treturn formLogin().loginProcessingUrl(loginProcessingUrl);\n\t}\n\n\t/**\n\t * Creates a logout request.\n\t * @return the LogoutRequestBuilder for additional customizations\n\t */\n\tpublic static LogoutRequestBuilder logout() {\n\t\treturn new LogoutRequestBuilder();\n\t}\n\n\t/**\n\t * Creates a logout request (including any necessary {@link CsrfToken}) to the\n\t * specified {@code logoutUrl}\n\t * @param logoutUrl the logout request URL\n\t * @return the LogoutRequestBuilder for additional customizations\n\t */\n\tpublic static LogoutRequestBuilder logout(String logoutUrl) {\n\t\treturn new LogoutRequestBuilder().logoutUrl(logoutUrl);\n\t}\n\n\t/**\n\t * Creates a logout request (including any necessary {@link CsrfToken})\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tpublic static final class LogoutRequestBuilder implements RequestBuilder, Mergeable {\n\n\t\tprivate String logoutUrl = \"/logout\";\n\n\t\tprivate RequestPostProcessor postProcessor = csrf();\n\n\t\tprivate @Nullable Mergeable parent;\n\n\t\tprivate LogoutRequestBuilder() {\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest buildRequest(ServletContext servletContext) {\n\t\t\tMockHttpServletRequestBuilder logoutRequest = post(this.logoutUrl).accept(MediaType.TEXT_HTML,\n\t\t\t\t\tMediaType.ALL);\n\t\t\tif (this.parent != null) {\n\t\t\t\tlogoutRequest = (MockHttpServletRequestBuilder) logoutRequest.merge(this.parent);\n\t\t\t}\n\t\t\tMockHttpServletRequest request = logoutRequest.buildRequest(servletContext);\n\t\t\tlogoutRequest.postProcessRequest(request);\n\t\t\treturn this.postProcessor.postProcessRequest(request);\n\t\t}\n\n\t\t/**\n\t\t * Specifies the logout URL to POST to. Defaults to \"/logout\".\n\t\t * @param logoutUrl the logout URL to POST to. Defaults to \"/logout\".\n\t\t * @return the {@link LogoutRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic LogoutRequestBuilder logoutUrl(String logoutUrl) {\n\t\t\tthis.logoutUrl = logoutUrl;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the logout URL to POST to.\n\t\t * @param logoutUrl the logout URL to POST to.\n\t\t * @param uriVars the URI variables\n\t\t * @return the {@link LogoutRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic LogoutRequestBuilder logoutUrl(String logoutUrl, Object... uriVars) {\n\t\t\tthis.logoutUrl = UriComponentsBuilder.fromPath(logoutUrl).buildAndExpand(uriVars).encode().toString();\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isMergeEnabled() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object merge(@Nullable Object parent) {\n\t\t\tif (parent == null) {\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\tif (parent instanceof Mergeable) {\n\t\t\t\tthis.parent = (Mergeable) parent;\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"Cannot merge with [\" + parent.getClass().getName() + \"]\");\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates a form based login request including any necessary {@link CsrfToken}.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tpublic static final class FormLoginRequestBuilder implements RequestBuilder, Mergeable {\n\n\t\tprivate String usernameParam = \"username\";\n\n\t\tprivate String passwordParam = \"password\";\n\n\t\tprivate String username = \"user\";\n\n\t\tprivate String password = \"password\";\n\n\t\tprivate String loginProcessingUrl = \"/login\";\n\n\t\tprivate MediaType acceptMediaType = MediaType.APPLICATION_FORM_URLENCODED;\n\n\t\tprivate @Nullable Mergeable parent;\n\n\t\tprivate RequestPostProcessor postProcessor = csrf();\n\n\t\tprivate FormLoginRequestBuilder() {\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest buildRequest(ServletContext servletContext) {\n\t\t\tMockHttpServletRequestBuilder loginRequest = post(this.loginProcessingUrl).accept(this.acceptMediaType)\n\t\t\t\t.param(this.usernameParam, this.username)\n\t\t\t\t.param(this.passwordParam, this.password);\n\t\t\tif (this.parent != null) {\n\t\t\t\tloginRequest = (MockHttpServletRequestBuilder) loginRequest.merge(this.parent);\n\t\t\t}\n\t\t\tMockHttpServletRequest request = loginRequest.buildRequest(servletContext);\n\t\t\tloginRequest.postProcessRequest(request);\n\t\t\treturn this.postProcessor.postProcessRequest(request);\n\t\t}\n\n\t\t/**\n\t\t * Specifies the URL to POST to. Default is \"/login\"\n\t\t * @param loginProcessingUrl the URL to POST to. Default is \"/login\"\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder loginProcessingUrl(String loginProcessingUrl) {\n\t\t\tthis.loginProcessingUrl = loginProcessingUrl;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the URL to POST to.\n\t\t * @param loginProcessingUrl the URL to POST to\n\t\t * @param uriVars the URI variables\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder loginProcessingUrl(String loginProcessingUrl, Object... uriVars) {\n\t\t\tthis.loginProcessingUrl = UriComponentsBuilder.fromPath(loginProcessingUrl)\n\t\t\t\t.buildAndExpand(uriVars)\n\t\t\t\t.encode()\n\t\t\t\t.toString();\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The HTTP parameter to place the username. Default is \"username\".\n\t\t * @param usernameParameter the HTTP parameter to place the username. Default is\n\t\t * \"username\".\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder userParameter(String usernameParameter) {\n\t\t\tthis.usernameParam = usernameParameter;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The HTTP parameter to place the password. Default is \"password\".\n\t\t * @param passwordParameter the HTTP parameter to place the password. Default is\n\t\t * \"password\".\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder passwordParam(String passwordParameter) {\n\t\t\tthis.passwordParam = passwordParameter;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The value of the password parameter. Default is \"password\".\n\t\t * @param password the value of the password parameter. Default is \"password\".\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder password(String password) {\n\t\t\tthis.password = password;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * The value of the username parameter. Default is \"user\".\n\t\t * @param username the value of the username parameter. Default is \"user\".\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder user(String username) {\n\t\t\tthis.username = username;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specify both the password parameter name and the password.\n\t\t * @param passwordParameter the HTTP parameter to place the password. Default is\n\t\t * \"password\".\n\t\t * @param password the value of the password parameter. Default is \"password\".\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder password(String passwordParameter, String password) {\n\t\t\tpasswordParam(passwordParameter);\n\t\t\tthis.password = password;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specify both the password parameter name and the password.\n\t\t * @param usernameParameter the HTTP parameter to place the username. Default is\n\t\t * \"username\".\n\t\t * @param username the value of the username parameter. Default is \"user\".\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder user(String usernameParameter, String username) {\n\t\t\tuserParameter(usernameParameter);\n\t\t\tthis.username = username;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specify a media type to set as the Accept header in the request.\n\t\t * @param acceptMediaType the {@link MediaType} to set the Accept header to.\n\t\t * Default is: MediaType.APPLICATION_FORM_URLENCODED\n\t\t * @return the {@link FormLoginRequestBuilder} for additional customizations\n\t\t */\n\t\tpublic FormLoginRequestBuilder acceptMediaType(MediaType acceptMediaType) {\n\t\t\tthis.acceptMediaType = acceptMediaType;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isMergeEnabled() {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object merge(@Nullable Object parent) {\n\t\t\tif (parent == null) {\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\tif (parent instanceof Mergeable) {\n\t\t\t\tthis.parent = (Mergeable) parent;\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"Cannot merge with [\" + parent.getClass().getName() + \"]\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.convert.converter.Converter;\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.client.web.method.annotation.OAuth2AuthorizedClientArgumentResolver;\nimport org.springframework.security.oauth2.core.AuthorizationGrantType;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.OidcUserInfo;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2UserAuthority;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.JwtClaimNames;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;\nimport org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.context.TestSecurityContextHolderStrategyAdapter;\nimport org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.context.HttpRequestResponseHolder;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.DigestUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.WebApplicationContextUtils;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;\n\n/**\n * Contains {@link MockMvc} {@link RequestPostProcessor} implementations for Spring\n * Security.\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic final class SecurityMockMvcRequestPostProcessors {\n\n\tprivate static final SecurityContextHolderStrategy DEFAULT_SECURITY_CONTEXT_HOLDER_STRATEGY = new TestSecurityContextHolderStrategyAdapter();\n\n\tprivate SecurityMockMvcRequestPostProcessors() {\n\t}\n\n\t/**\n\t * Creates a DigestRequestPostProcessor that enables easily adding digest based\n\t * authentication to a request.\n\t * @return the DigestRequestPostProcessor to use\n\t */\n\tpublic static DigestRequestPostProcessor digest() {\n\t\treturn new DigestRequestPostProcessor();\n\t}\n\n\t/**\n\t * Creates a DigestRequestPostProcessor that enables easily adding digest based\n\t * authentication to a request.\n\t * @param username the username to use\n\t * @return the DigestRequestPostProcessor to use\n\t */\n\tpublic static DigestRequestPostProcessor digest(String username) {\n\t\treturn digest().username(username);\n\t}\n\n\t/**\n\t * Populates the provided X509Certificate instances on the request.\n\t * @param certificates the X509Certificate instances to populate\n\t * @return the\n\t * {@link org.springframework.test.web.servlet.request.RequestPostProcessor} to use.\n\t */\n\tpublic static RequestPostProcessor x509(X509Certificate... certificates) {\n\t\treturn new X509RequestPostProcessor(certificates);\n\t}\n\n\t/**\n\t * Finds an X509Certificate using a resourceName and populates it on the request.\n\t * @param resourceName the name of the X509Certificate resource\n\t * @return the\n\t * {@link org.springframework.test.web.servlet.request.RequestPostProcessor} to use.\n\t * @throws IOException\n\t * @throws CertificateException\n\t */\n\tpublic static RequestPostProcessor x509(String resourceName) throws IOException, CertificateException {\n\t\tResourceLoader loader = new DefaultResourceLoader();\n\t\tResource resource = loader.getResource(resourceName);\n\t\tInputStream inputStream = resource.getInputStream();\n\t\tCertificateFactory certFactory = CertificateFactory.getInstance(\"X.509\");\n\t\tX509Certificate certificate = (X509Certificate) certFactory.generateCertificate(inputStream);\n\t\treturn x509(certificate);\n\t}\n\n\t/**\n\t * Creates a {@link RequestPostProcessor} that will automatically populate a valid\n\t * {@link CsrfToken} in the request.\n\t * @return the {@link CsrfRequestPostProcessor} for further customizations.\n\t */\n\tpublic static CsrfRequestPostProcessor csrf() {\n\t\treturn new CsrfRequestPostProcessor();\n\t}\n\n\t/**\n\t * Creates a {@link RequestPostProcessor} that can be used to ensure that the\n\t * resulting request is ran with the user in the {@link TestSecurityContextHolder}.\n\t * @return the {@link RequestPostProcessor} to use\n\t */\n\tpublic static RequestPostProcessor testSecurityContext() {\n\t\treturn new TestSecurityContextHolderPostProcessor();\n\t}\n\n\t/**\n\t * Establish a {@link SecurityContext} that has a\n\t * {@link UsernamePasswordAuthenticationToken} for the\n\t * {@link Authentication#getPrincipal()} and a {@link User} for the\n\t * {@link UsernamePasswordAuthenticationToken#getPrincipal()}. All details are\n\t * declarative and do not require that the user actually exists.\n\t *\n\t * <p>\n\t * The support works by associating the user to the HttpServletRequest. To associate\n\t * the request to the SecurityContextHolder you need to ensure that the\n\t * SecurityContextPersistenceFilter is associated with the MockMvc instance. A few\n\t * ways to do this are:\n\t * </p>\n\t *\n\t * <ul>\n\t * <li>Invoking apply {@link SecurityMockMvcConfigurers#springSecurity()}</li>\n\t * <li>Adding Spring Security's FilterChainProxy to MockMvc</li>\n\t * <li>Manually adding {@link SecurityContextPersistenceFilter} to the MockMvc\n\t * instance may make sense when using MockMvcBuilders standaloneSetup</li>\n\t * </ul>\n\t * @param username the username to populate\n\t * @return the {@link UserRequestPostProcessor} for additional customization\n\t */\n\tpublic static UserRequestPostProcessor user(String username) {\n\t\treturn new UserRequestPostProcessor(username);\n\t}\n\n\t/**\n\t * Establish a {@link SecurityContext} that has a\n\t * {@link UsernamePasswordAuthenticationToken} for the\n\t * {@link Authentication#getPrincipal()} and a custom {@link UserDetails} for the\n\t * {@link UsernamePasswordAuthenticationToken#getPrincipal()}. All details are\n\t * declarative and do not require that the user actually exists.\n\t *\n\t * <p>\n\t * The support works by associating the user to the HttpServletRequest. To associate\n\t * the request to the SecurityContextHolder you need to ensure that the\n\t * SecurityContextPersistenceFilter is associated with the MockMvc instance. A few\n\t * ways to do this are:\n\t * </p>\n\t *\n\t * <ul>\n\t * <li>Invoking apply {@link SecurityMockMvcConfigurers#springSecurity()}</li>\n\t * <li>Adding Spring Security's FilterChainProxy to MockMvc</li>\n\t * <li>Manually adding {@link SecurityContextPersistenceFilter} to the MockMvc\n\t * instance may make sense when using MockMvcBuilders standaloneSetup</li>\n\t * </ul>\n\t * @param user the UserDetails to populate\n\t * @return the {@link RequestPostProcessor} to use\n\t */\n\tpublic static RequestPostProcessor user(UserDetails user) {\n\t\treturn new UserDetailsRequestPostProcessor(user);\n\t}\n\n\t/**\n\t * Establish a {@link SecurityContext} that has a {@link JwtAuthenticationToken} for\n\t * the {@link Authentication} and a {@link Jwt} for the\n\t * {@link Authentication#getPrincipal()}. All details are declarative and do not\n\t * require the JWT to be valid.\n\t *\n\t * <p>\n\t * The support works by associating the authentication to the HttpServletRequest. To\n\t * associate the request to the SecurityContextHolder you need to ensure that the\n\t * SecurityContextPersistenceFilter is associated with the MockMvc instance. A few\n\t * ways to do this are:\n\t * </p>\n\t *\n\t * <ul>\n\t * <li>Invoking apply {@link SecurityMockMvcConfigurers#springSecurity()}</li>\n\t * <li>Adding Spring Security's FilterChainProxy to MockMvc</li>\n\t * <li>Manually adding {@link SecurityContextPersistenceFilter} to the MockMvc\n\t * instance may make sense when using MockMvcBuilders standaloneSetup</li>\n\t * </ul>\n\t * @return the {@link JwtRequestPostProcessor} for additional customization\n\t */\n\tpublic static JwtRequestPostProcessor jwt() {\n\t\treturn new JwtRequestPostProcessor();\n\t}\n\n\t/**\n\t * Establish a {@link SecurityContext} that has a {@link BearerTokenAuthentication}\n\t * for the {@link Authentication} and a {@link OAuth2AuthenticatedPrincipal} for the\n\t * {@link Authentication#getPrincipal()}. All details are declarative and do not\n\t * require the token to be valid\n\t *\n\t * <p>\n\t * The support works by associating the authentication to the HttpServletRequest. To\n\t * associate the request to the SecurityContextHolder you need to ensure that the\n\t * SecurityContextPersistenceFilter is associated with the MockMvc instance. A few\n\t * ways to do this are:\n\t * </p>\n\t *\n\t * <ul>\n\t * <li>Invoking apply {@link SecurityMockMvcConfigurers#springSecurity()}</li>\n\t * <li>Adding Spring Security's FilterChainProxy to MockMvc</li>\n\t * <li>Manually adding {@link SecurityContextPersistenceFilter} to the MockMvc\n\t * instance may make sense when using MockMvcBuilders standaloneSetup</li>\n\t * </ul>\n\t * @return the {@link OpaqueTokenRequestPostProcessor} for additional customization\n\t * @since 5.3\n\t */\n\tpublic static OpaqueTokenRequestPostProcessor opaqueToken() {\n\t\treturn new OpaqueTokenRequestPostProcessor();\n\t}\n\n\t/**\n\t * Establish a {@link SecurityContext} that uses the specified {@link Authentication}\n\t * for the {@link Authentication#getPrincipal()} and a custom {@link UserDetails}. All\n\t * details are declarative and do not require that the user actually exists.\n\t *\n\t * <p>\n\t * The support works by associating the user to the HttpServletRequest. To associate\n\t * the request to the SecurityContextHolder you need to ensure that the\n\t * SecurityContextPersistenceFilter is associated with the MockMvc instance. A few\n\t * ways to do this are:\n\t * </p>\n\t *\n\t * <ul>\n\t * <li>Invoking apply {@link SecurityMockMvcConfigurers#springSecurity()}</li>\n\t * <li>Adding Spring Security's FilterChainProxy to MockMvc</li>\n\t * <li>Manually adding {@link SecurityContextPersistenceFilter} to the MockMvc\n\t * instance may make sense when using MockMvcBuilders standaloneSetup</li>\n\t * </ul>\n\t * @param authentication the Authentication to populate\n\t * @return the {@link RequestPostProcessor} to use\n\t */\n\tpublic static RequestPostProcessor authentication(Authentication authentication) {\n\t\treturn new AuthenticationRequestPostProcessor(authentication);\n\t}\n\n\t/**\n\t * Establish a {@link SecurityContext} that uses an\n\t * {@link AnonymousAuthenticationToken}. This is useful when a user wants to run a\n\t * majority of tests as a specific user and wishes to override a few methods to be\n\t * anonymous. For example:\n\t *\n\t * <pre>\n\t * <code>\n\t * public class SecurityTests {\n\t *     &#064;Before\n\t *     public void setup() {\n\t *         mockMvc = MockMvcBuilders\n\t *             .webAppContextSetup(context)\n\t *             .defaultRequest(get(\"/\").with(user(\"user\")))\n\t *             .build();\n\t *     }\n\t *\n\t *     &#064;Test\n\t *     public void anonymous() {\n\t *         mockMvc.perform(get(\"anonymous\").with(anonymous()));\n\t *     }\n\t *     // ... lots of tests ran with a default user ...\n\t * }\n\t * </code> </pre>\n\t * @return the {@link RequestPostProcessor} to use\n\t */\n\tpublic static RequestPostProcessor anonymous() {\n\t\treturn new AnonymousRequestPostProcessor();\n\t}\n\n\t/**\n\t * Establish the specified {@link SecurityContext} to be used.\n\t *\n\t * <p>\n\t * This works by associating the user to the {@link HttpServletRequest}. To associate\n\t * the request to the {@link SecurityContextHolder} you need to ensure that the\n\t * {@link SecurityContextPersistenceFilter} (i.e. Spring Security's FilterChainProxy\n\t * will typically do this) is associated with the {@link MockMvc} instance.\n\t * </p>\n\t */\n\tpublic static RequestPostProcessor securityContext(SecurityContext securityContext) {\n\t\treturn new SecurityContextRequestPostProcessor(securityContext);\n\t}\n\n\t/**\n\t * Convenience mechanism for setting the Authorization header to use HTTP Basic with\n\t * the given username and password. This method will automatically perform the\n\t * necessary Base64 encoding.\n\t * @param username the username to include in the Authorization header.\n\t * @param password the password to include in the Authorization header.\n\t * @return the {@link RequestPostProcessor} to use\n\t */\n\tpublic static RequestPostProcessor httpBasic(String username, String password) {\n\t\treturn new HttpBasicRequestPostProcessor(username, password);\n\t}\n\n\t/**\n\t * Establish a {@link SecurityContext} that has a {@link OAuth2AuthenticationToken}\n\t * for the {@link Authentication}, a {@link OAuth2User} as the principal, and a\n\t * {@link OAuth2AuthorizedClient} in the session. All details are declarative and do\n\t * not require associated tokens to be valid.\n\t *\n\t * <p>\n\t * The support works by associating the authentication to the HttpServletRequest. To\n\t * associate the request to the SecurityContextHolder you need to ensure that the\n\t * SecurityContextPersistenceFilter is associated with the MockMvc instance. A few\n\t * ways to do this are:\n\t * </p>\n\t *\n\t * <ul>\n\t * <li>Invoking apply {@link SecurityMockMvcConfigurers#springSecurity()}</li>\n\t * <li>Adding Spring Security's FilterChainProxy to MockMvc</li>\n\t * <li>Manually adding {@link SecurityContextPersistenceFilter} to the MockMvc\n\t * instance may make sense when using MockMvcBuilders standaloneSetup</li>\n\t * </ul>\n\t * @return the {@link OidcLoginRequestPostProcessor} for additional customization\n\t * @since 5.3\n\t */\n\tpublic static OAuth2LoginRequestPostProcessor oauth2Login() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\", null,\n\t\t\t\tnull, Collections.singleton(\"read\"));\n\t\treturn new OAuth2LoginRequestPostProcessor(accessToken);\n\t}\n\n\t/**\n\t * Establish a {@link SecurityContext} that has a {@link OAuth2AuthenticationToken}\n\t * for the {@link Authentication}, a {@link OidcUser} as the principal, and a\n\t * {@link OAuth2AuthorizedClient} in the session. All details are declarative and do\n\t * not require associated tokens to be valid.\n\t *\n\t * <p>\n\t * The support works by associating the authentication to the HttpServletRequest. To\n\t * associate the request to the SecurityContextHolder you need to ensure that the\n\t * SecurityContextPersistenceFilter is associated with the MockMvc instance. A few\n\t * ways to do this are:\n\t * </p>\n\t *\n\t * <ul>\n\t * <li>Invoking apply {@link SecurityMockMvcConfigurers#springSecurity()}</li>\n\t * <li>Adding Spring Security's FilterChainProxy to MockMvc</li>\n\t * <li>Manually adding {@link SecurityContextPersistenceFilter} to the MockMvc\n\t * instance may make sense when using MockMvcBuilders standaloneSetup</li>\n\t * </ul>\n\t * @return the {@link OidcLoginRequestPostProcessor} for additional customization\n\t * @since 5.3\n\t */\n\tpublic static OidcLoginRequestPostProcessor oidcLogin() {\n\t\tOAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"access-token\", null,\n\t\t\t\tnull, Collections.singleton(\"read\"));\n\t\treturn new OidcLoginRequestPostProcessor(accessToken);\n\t}\n\n\t/**\n\t * Establish an {@link OAuth2AuthorizedClient} in the session. All details are\n\t * declarative and do not require associated tokens to be valid.\n\t *\n\t * <p>\n\t * The support works by associating the authorized client to the HttpServletRequest\n\t * using an {@link OAuth2AuthorizedClientRepository}\n\t * </p>\n\t * @return the {@link OAuth2ClientRequestPostProcessor} for additional customization\n\t * @since 5.3\n\t */\n\tpublic static OAuth2ClientRequestPostProcessor oauth2Client() {\n\t\treturn new OAuth2ClientRequestPostProcessor();\n\t}\n\n\t/**\n\t * Establish an {@link OAuth2AuthorizedClient} in the session. All details are\n\t * declarative and do not require associated tokens to be valid.\n\t *\n\t * <p>\n\t * The support works by associating the authorized client to the HttpServletRequest\n\t * using an {@link OAuth2AuthorizedClientRepository}\n\t * </p>\n\t * @param registrationId The registration id for the {@link OAuth2AuthorizedClient}\n\t * @return the {@link OAuth2ClientRequestPostProcessor} for additional customization\n\t * @since 5.3\n\t */\n\tpublic static OAuth2ClientRequestPostProcessor oauth2Client(String registrationId) {\n\t\treturn new OAuth2ClientRequestPostProcessor(registrationId);\n\t}\n\n\tprivate static SecurityContextHolderStrategy getSecurityContextHolderStrategy(HttpServletRequest request) {\n\t\tWebApplicationContext context = WebApplicationContextUtils\n\t\t\t.findWebApplicationContext(request.getServletContext());\n\t\tif (context == null) {\n\t\t\treturn DEFAULT_SECURITY_CONTEXT_HOLDER_STRATEGY;\n\t\t}\n\t\tif (context.getBeanNamesForType(SecurityContextHolderStrategy.class).length == 0) {\n\t\t\treturn DEFAULT_SECURITY_CONTEXT_HOLDER_STRATEGY;\n\t\t}\n\t\treturn context.getBean(SecurityContextHolderStrategy.class);\n\t}\n\n\t/**\n\t * Populates the X509Certificate instances onto the request\n\t */\n\tprivate static final class X509RequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate final X509Certificate[] certificates;\n\n\t\tprivate X509RequestPostProcessor(X509Certificate... certificates) {\n\t\t\tAssert.notNull(certificates, \"X509Certificate cannot be null\");\n\t\t\tthis.certificates = certificates;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\trequest.setAttribute(\"jakarta.servlet.request.X509Certificate\", this.certificates);\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n\t/**\n\t * Populates a valid {@link CsrfToken} into the request.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tpublic static final class CsrfRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate static final byte[] INVALID_TOKEN_BYTES = new byte[] { 1, 1, 1, 96, 99, 98 };\n\n\t\tprivate static final String INVALID_TOKEN_VALUE = Base64.getEncoder().encodeToString(INVALID_TOKEN_BYTES);\n\n\t\tprivate boolean asHeader;\n\n\t\tprivate boolean useInvalidToken;\n\n\t\tprivate CsrfRequestPostProcessor() {\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tCsrfTokenRepository repository = WebTestUtils.getCsrfTokenRepository(request);\n\t\t\tCsrfTokenRequestHandler handler = WebTestUtils.getCsrfTokenRequestHandler(request);\n\t\t\tAssert.isTrue(handler != null, \"No CsrfTokenRequestHandler found\");\n\t\t\tif (!(repository instanceof TestCsrfTokenRepository)) {\n\t\t\t\trepository = new TestCsrfTokenRepository(new HttpSessionCsrfTokenRepository());\n\t\t\t\tWebTestUtils.setCsrfTokenRepository(request, repository);\n\t\t\t}\n\t\t\tTestCsrfTokenRepository.enable(request);\n\t\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t\tDeferredCsrfToken deferredCsrfToken = repository.loadDeferredToken(request, response);\n\t\t\thandler.handle(request, response, deferredCsrfToken);\n\t\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\t\tif (token == null) {\n\t\t\t\treturn request;\n\t\t\t}\n\t\t\tString tokenValue = this.useInvalidToken ? INVALID_TOKEN_VALUE : token.getToken();\n\t\t\tif (this.asHeader) {\n\t\t\t\trequest.addHeader(token.getHeaderName(), tokenValue);\n\t\t\t}\n\t\t\telse {\n\t\t\t\trequest.setParameter(token.getParameterName(), tokenValue);\n\t\t\t}\n\t\t\treturn request;\n\t\t}\n\n\t\t/**\n\t\t * Instead of using the {@link CsrfToken} as a request parameter (default) will\n\t\t * populate the {@link CsrfToken} as a header.\n\t\t * @return the {@link CsrfRequestPostProcessor} for additional customizations\n\t\t */\n\t\tpublic CsrfRequestPostProcessor asHeader() {\n\t\t\tthis.asHeader = true;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates an invalid token value on the request.\n\t\t * @return the {@link CsrfRequestPostProcessor} for additional customizations\n\t\t */\n\t\tpublic CsrfRequestPostProcessor useInvalidToken() {\n\t\t\tthis.useInvalidToken = true;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Used to wrap the CsrfTokenRepository to provide support for testing when the\n\t\t * request is wrapped (i.e. Spring Session is in use).\n\t\t */\n\t\tstatic class TestCsrfTokenRepository implements CsrfTokenRepository {\n\n\t\t\tstatic final String TOKEN_ATTR_NAME = TestCsrfTokenRepository.class.getName().concat(\".TOKEN\");\n\n\t\t\tstatic final String ENABLED_ATTR_NAME = TestCsrfTokenRepository.class.getName().concat(\".ENABLED\");\n\n\t\t\tprivate final CsrfTokenRepository delegate;\n\n\t\t\tTestCsrfTokenRepository(CsrfTokenRepository delegate) {\n\t\t\t\tthis.delegate = delegate;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic CsrfToken generateToken(HttpServletRequest request) {\n\t\t\t\treturn this.delegate.generateToken(request);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void saveToken(@Nullable CsrfToken token, HttpServletRequest request, HttpServletResponse response) {\n\t\t\t\tif (isEnabled(request)) {\n\t\t\t\t\trequest.setAttribute(TOKEN_ATTR_NAME, token);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.delegate.saveToken(token, request, response);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable CsrfToken loadToken(HttpServletRequest request) {\n\t\t\t\tif (isEnabled(request)) {\n\t\t\t\t\treturn (CsrfToken) request.getAttribute(TOKEN_ATTR_NAME);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn this.delegate.loadToken(request);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tstatic void enable(HttpServletRequest request) {\n\t\t\t\trequest.setAttribute(ENABLED_ATTR_NAME, Boolean.TRUE);\n\t\t\t}\n\n\t\t\tboolean isEnabled(HttpServletRequest request) {\n\t\t\t\treturn Boolean.TRUE.equals(request.getAttribute(ENABLED_ATTR_NAME));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tpublic static class DigestRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate String username = \"user\";\n\n\t\tprivate String password = \"password\";\n\n\t\tprivate String realm = \"Spring Security\";\n\n\t\tprivate String nonce = generateNonce(60);\n\n\t\tprivate String qop = \"auth\";\n\n\t\tprivate String nc = \"00000001\";\n\n\t\tprivate String cnonce = \"c822c727a648aba7\";\n\n\t\t/**\n\t\t * Configures the username to use\n\t\t * @param username the username to use\n\t\t * @return the DigestRequestPostProcessor for further customization\n\t\t */\n\t\tprivate DigestRequestPostProcessor username(String username) {\n\t\t\tAssert.notNull(username, \"username cannot be null\");\n\t\t\tthis.username = username;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the password to use\n\t\t * @param password the password to use\n\t\t * @return the DigestRequestPostProcessor for further customization\n\t\t */\n\t\tpublic DigestRequestPostProcessor password(String password) {\n\t\t\tAssert.notNull(password, \"password cannot be null\");\n\t\t\tthis.password = password;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the realm to use\n\t\t * @param realm the realm to use\n\t\t * @return the DigestRequestPostProcessor for further customization\n\t\t */\n\t\tpublic DigestRequestPostProcessor realm(String realm) {\n\t\t\tAssert.notNull(realm, \"realm cannot be null\");\n\t\t\tthis.realm = realm;\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate static String generateNonce(int validitySeconds) {\n\t\t\tlong expiryTime = System.currentTimeMillis() + (validitySeconds * 1000);\n\t\t\tString toDigest = expiryTime + \":\" + \"key\";\n\t\t\tString signatureValue = md5Hex(toDigest);\n\t\t\tString nonceValue = expiryTime + \":\" + signatureValue;\n\t\t\treturn new String(Base64.getEncoder().encode(nonceValue.getBytes()));\n\t\t}\n\n\t\tprivate String createAuthorizationHeader(MockHttpServletRequest request) {\n\t\t\tString uri = request.getRequestURI();\n\t\t\tString responseDigest = generateDigest(this.username, this.realm, this.password, request.getMethod(), uri,\n\t\t\t\t\tthis.qop, this.nonce, this.nc, this.cnonce);\n\t\t\treturn \"Digest username=\\\"\" + this.username + \"\\\", realm=\\\"\" + this.realm + \"\\\", nonce=\\\"\" + this.nonce\n\t\t\t\t\t+ \"\\\", uri=\\\"\" + uri + \"\\\", response=\\\"\" + responseDigest + \"\\\", qop=\" + this.qop + \", nc=\"\n\t\t\t\t\t+ this.nc + \", cnonce=\\\"\" + this.cnonce + \"\\\"\";\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\trequest.addHeader(\"Authorization\", createAuthorizationHeader(request));\n\t\t\treturn request;\n\t\t}\n\n\t\t/**\n\t\t * Computes the <code>response</code> portion of a Digest authentication header.\n\t\t * Both the server and user agent should compute the <code>response</code>\n\t\t * independently. Provided as a static method to simplify the coding of user\n\t\t * agents.\n\t\t * @param username the user's login name.\n\t\t * @param realm the name of the realm.\n\t\t * @param password the user's password in plaintext or ready-encoded.\n\t\t * @param httpMethod the HTTP request method (GET, POST etc.)\n\t\t * @param uri the request URI.\n\t\t * @param qop the qop directive, or null if not set.\n\t\t * @param nonce the nonce supplied by the server\n\t\t * @param nc the \"nonce-count\" as defined in RFC 2617.\n\t\t * @param cnonce opaque string supplied by the client when qop is set.\n\t\t * @return the MD5 of the digest authentication response, encoded in hex\n\t\t * @throws IllegalArgumentException if the supplied qop value is unsupported.\n\t\t */\n\t\tprivate static String generateDigest(String username, String realm, String password,\n\t\t\t\t@Nullable String httpMethod, @Nullable String uri, String qop, String nonce, String nc, String cnonce)\n\t\t\t\tthrows IllegalArgumentException {\n\t\t\tString a1Md5 = encodePasswordInA1Format(username, realm, password);\n\t\t\tString a2 = httpMethod + \":\" + uri;\n\t\t\tString a2Md5 = md5Hex(a2);\n\t\t\tif (qop == null) {\n\t\t\t\t// as per RFC 2069 compliant clients (also reaffirmed by RFC 2617)\n\t\t\t\treturn md5Hex(a1Md5 + \":\" + nonce + \":\" + a2Md5);\n\t\t\t}\n\t\t\tif (\"auth\".equals(qop)) {\n\t\t\t\t// As per RFC 2617 compliant clients\n\t\t\t\treturn md5Hex(a1Md5 + \":\" + nonce + \":\" + nc + \":\" + cnonce + \":\" + qop + \":\" + a2Md5);\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"This method does not support a qop: '\" + qop + \"'\");\n\t\t}\n\n\t\tstatic String encodePasswordInA1Format(String username, String realm, String password) {\n\t\t\treturn md5Hex(username + \":\" + realm + \":\" + password);\n\t\t}\n\n\t\tprivate static String md5Hex(String a2) {\n\t\t\treturn DigestUtils.md5DigestAsHex(a2.getBytes(StandardCharsets.UTF_8));\n\t\t}\n\n\t}\n\n\t/**\n\t * Support class for {@link RequestPostProcessor}'s that establish a Spring Security\n\t * context\n\t */\n\tprivate abstract static class SecurityContextRequestPostProcessorSupport {\n\n\t\t/**\n\t\t * Saves the specified {@link Authentication} into an empty\n\t\t * {@link SecurityContext} using the {@link SecurityContextRepository}.\n\t\t * @param authentication the {@link Authentication} to save\n\t\t * @param request the {@link HttpServletRequest} to use\n\t\t */\n\t\tfinal void save(Authentication authentication, HttpServletRequest request) {\n\t\t\tSecurityContext securityContext = getSecurityContextHolderStrategy(request).createEmptyContext();\n\t\t\tsecurityContext.setAuthentication(authentication);\n\t\t\tsave(securityContext, request);\n\t\t}\n\n\t\t/**\n\t\t * Saves the {@link SecurityContext} using the {@link SecurityContextRepository}\n\t\t * @param securityContext the {@link SecurityContext} to save\n\t\t * @param request the {@link HttpServletRequest} to use\n\t\t */\n\t\tfinal void save(SecurityContext securityContext, HttpServletRequest request) {\n\t\t\tSecurityContextRepository securityContextRepository = WebTestUtils.getSecurityContextRepository(request);\n\t\t\tboolean isTestRepository = securityContextRepository instanceof TestSecurityContextRepository;\n\t\t\tif (!isTestRepository) {\n\t\t\t\tsecurityContextRepository = new TestSecurityContextRepository(securityContextRepository);\n\t\t\t\tWebTestUtils.setSecurityContextRepository(request, securityContextRepository);\n\t\t\t}\n\t\t\tHttpServletResponse response = new MockHttpServletResponse();\n\t\t\tHttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response);\n\t\t\tsecurityContextRepository.loadContext(requestResponseHolder);\n\t\t\trequest = requestResponseHolder.getRequest();\n\t\t\tresponse = requestResponseHolder.getResponse();\n\t\t\tsecurityContextRepository.saveContext(securityContext, request, response);\n\t\t}\n\n\t\t/**\n\t\t * Used to wrap the SecurityContextRepository to provide support for testing in\n\t\t * stateless mode\n\t\t */\n\t\tstatic final class TestSecurityContextRepository implements SecurityContextRepository {\n\n\t\t\tprivate static final String ATTR_NAME = TestSecurityContextRepository.class.getName().concat(\".REPO\");\n\n\t\t\tprivate final SecurityContextRepository delegate;\n\n\t\t\tprivate TestSecurityContextRepository(SecurityContextRepository delegate) {\n\t\t\t\tthis.delegate = delegate;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {\n\t\t\t\tSecurityContext result = getContext(requestResponseHolder.getRequest());\n\t\t\t\t// always load from the delegate to ensure the request/response in the\n\t\t\t\t// holder are updated\n\t\t\t\t// remember the SecurityContextRepository is used in many different\n\t\t\t\t// locations\n\t\t\t\tSecurityContext delegateResult = this.delegate.loadContext(requestResponseHolder);\n\t\t\t\treturn (result != null) ? result : delegateResult;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {\n\t\t\t\trequest.setAttribute(ATTR_NAME, context);\n\t\t\t\tthis.delegate.saveContext(context, request, response);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean containsContext(HttpServletRequest request) {\n\t\t\t\treturn getContext(request) != null || this.delegate.containsContext(request);\n\t\t\t}\n\n\t\t\tprivate static SecurityContext getContext(HttpServletRequest request) {\n\t\t\t\treturn (SecurityContext) request.getAttribute(ATTR_NAME);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Associates the {@link SecurityContext} found in\n\t * {@link TestSecurityContextHolder#getContext()} with the\n\t * {@link MockHttpServletRequest}.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tprivate static final class TestSecurityContextHolderPostProcessor extends SecurityContextRequestPostProcessorSupport\n\t\t\timplements RequestPostProcessor {\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\t// TestSecurityContextHolder is only a default value\n\t\t\tSecurityContext existingContext = TestSecurityContextRepository.getContext(request);\n\t\t\tif (existingContext != null) {\n\t\t\t\treturn request;\n\t\t\t}\n\t\t\tSecurityContextHolderStrategy strategy = getSecurityContextHolderStrategy(request);\n\t\t\tSecurityContext empty = strategy.createEmptyContext();\n\t\t\tSecurityContext context = strategy.getContext();\n\t\t\tif (!empty.equals(context)) {\n\t\t\t\tsave(context, request);\n\t\t\t}\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n\t/**\n\t * Associates the specified {@link SecurityContext} with the\n\t * {@link MockHttpServletRequest}.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tprivate static final class SecurityContextRequestPostProcessor extends SecurityContextRequestPostProcessorSupport\n\t\t\timplements RequestPostProcessor {\n\n\t\tprivate final SecurityContext securityContext;\n\n\t\tprivate SecurityContextRequestPostProcessor(SecurityContext securityContext) {\n\t\t\tthis.securityContext = securityContext;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tsave(this.securityContext, request);\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n\t/**\n\t * Sets the specified {@link Authentication} on an empty {@link SecurityContext} and\n\t * associates it to the {@link MockHttpServletRequest}\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t *\n\t */\n\tprivate static final class AuthenticationRequestPostProcessor extends SecurityContextRequestPostProcessorSupport\n\t\t\timplements RequestPostProcessor {\n\n\t\tprivate final Authentication authentication;\n\n\t\tprivate AuthenticationRequestPostProcessor(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tSecurityContext context = getSecurityContextHolderStrategy(request).createEmptyContext();\n\t\t\tcontext.setAuthentication(this.authentication);\n\t\t\tsave(this.authentication, request);\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates a {@link UsernamePasswordAuthenticationToken} and sets the\n\t * {@link UserDetails} as the principal and associates it to the\n\t * {@link MockHttpServletRequest}.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tprivate static final class UserDetailsRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate final AuthenticationRequestPostProcessor delegate;\n\n\t\tUserDetailsRequestPostProcessor(UserDetails user) {\n\t\t\tAuthentication token = UsernamePasswordAuthenticationToken.authenticated(user, user.getPassword(),\n\t\t\t\t\tuser.getAuthorities());\n\t\t\tthis.delegate = new AuthenticationRequestPostProcessor(token);\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\treturn this.delegate.postProcessRequest(request);\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates a {@link UsernamePasswordAuthenticationToken} and sets the principal to be\n\t * a {@link User} and associates it to the {@link MockHttpServletRequest}.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tpublic static final class UserRequestPostProcessor extends SecurityContextRequestPostProcessorSupport\n\t\t\timplements RequestPostProcessor {\n\n\t\tprivate String username;\n\n\t\tprivate String password = \"password\";\n\n\t\tprivate static final String ROLE_PREFIX = \"ROLE_\";\n\n\t\tprivate Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\n\t\tprivate boolean enabled = true;\n\n\t\tprivate boolean accountNonExpired = true;\n\n\t\tprivate boolean credentialsNonExpired = true;\n\n\t\tprivate boolean accountNonLocked = true;\n\n\t\t/**\n\t\t * Creates a new instance with the given username\n\t\t * @param username the username to use\n\t\t */\n\t\tprivate UserRequestPostProcessor(String username) {\n\t\t\tAssert.notNull(username, \"username cannot be null\");\n\t\t\tthis.username = username;\n\t\t}\n\n\t\t/**\n\t\t * Specify the roles of the user to authenticate as. This method is similar to\n\t\t * {@link #authorities(GrantedAuthority...)}, but just not as flexible.\n\t\t * @param roles The roles to populate. Note that if the role does not start with\n\t\t * {@link #ROLE_PREFIX} it will automatically be prepended. This means by default\n\t\t * {@code roles(\"ROLE_USER\")} and {@code roles(\"USER\")} are equivalent.\n\t\t * @return the UserRequestPostProcessor for further customizations\n\t\t * @see #authorities(GrantedAuthority...)\n\t\t * @see #ROLE_PREFIX\n\t\t */\n\t\tpublic UserRequestPostProcessor roles(String... roles) {\n\t\t\tList<GrantedAuthority> authorities = new ArrayList<>(roles.length);\n\t\t\tfor (String role : roles) {\n\t\t\t\tAssert.isTrue(!role.startsWith(ROLE_PREFIX), () -> \"Role should not start with \" + ROLE_PREFIX\n\t\t\t\t\t\t+ \" since this method automatically prefixes with this value. Got \" + role);\n\t\t\t\tauthorities.add(new SimpleGrantedAuthority(ROLE_PREFIX + role));\n\t\t\t}\n\t\t\tthis.authorities = authorities;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the user's {@link GrantedAuthority}'s. The default is ROLE_USER.\n\t\t * @param authorities\n\t\t * @return the UserRequestPostProcessor for further customizations\n\t\t * @see #roles(String...)\n\t\t */\n\t\tpublic UserRequestPostProcessor authorities(GrantedAuthority... authorities) {\n\t\t\treturn authorities(Arrays.asList(authorities));\n\t\t}\n\n\t\t/**\n\t\t * Populates the user's {@link GrantedAuthority}'s. The default is ROLE_USER.\n\t\t * @param authorities\n\t\t * @return the UserRequestPostProcessor for further customizations\n\t\t * @see #roles(String...)\n\t\t */\n\t\tpublic UserRequestPostProcessor authorities(Collection<? extends GrantedAuthority> authorities) {\n\t\t\tthis.authorities = authorities;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Populates the user's password. The default is \"password\"\n\t\t * @param password the user's password\n\t\t * @return the UserRequestPostProcessor for further customizations\n\t\t */\n\t\tpublic UserRequestPostProcessor password(String password) {\n\t\t\tthis.password = password;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tUserDetailsRequestPostProcessor delegate = new UserDetailsRequestPostProcessor(createUser());\n\t\t\treturn delegate.postProcessRequest(request);\n\t\t}\n\n\t\t/**\n\t\t * Creates a new {@link User}\n\t\t * @return the {@link User} for the principal\n\t\t */\n\t\tprivate User createUser() {\n\t\t\treturn new User(this.username, this.password, this.enabled, this.accountNonExpired,\n\t\t\t\t\tthis.credentialsNonExpired, this.accountNonLocked, this.authorities);\n\t\t}\n\n\t}\n\n\tprivate static class AnonymousRequestPostProcessor extends SecurityContextRequestPostProcessorSupport\n\t\t\timplements RequestPostProcessor {\n\n\t\tprivate AuthenticationRequestPostProcessor delegate = new AuthenticationRequestPostProcessor(\n\t\t\t\tnew AnonymousAuthenticationToken(\"key\", \"anonymous\",\n\t\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\")));\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\treturn this.delegate.postProcessRequest(request);\n\t\t}\n\n\t}\n\n\tprivate static final class HttpBasicRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate String headerValue;\n\n\t\tprivate HttpBasicRequestPostProcessor(String username, String password) {\n\t\t\tbyte[] toEncode = (username + \":\" + password).getBytes(StandardCharsets.UTF_8);\n\t\t\tthis.headerValue = \"Basic \" + new String(Base64.getEncoder().encode(toEncode));\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\trequest.addHeader(\"Authorization\", this.headerValue);\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Jérôme Wacongne &lt;ch4mp&#64;c4-soft.com&gt;\n\t * @author Josh Cummings\n\t * @since 5.2\n\t */\n\tpublic static final class JwtRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate Jwt jwt;\n\n\t\tprivate Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter = new JwtGrantedAuthoritiesConverter();\n\n\t\tprivate JwtRequestPostProcessor() {\n\t\t\tthis.jwt((jwt) -> {\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Jwt.Builder} {@link Consumer} to configure the underlying\n\t\t * {@link Jwt}\n\t\t *\n\t\t * This method first creates a default {@link Jwt.Builder} instance with default\n\t\t * values for the {@code alg}, {@code sub}, and {@code scope} claims. The\n\t\t * {@link Consumer} can then modify these or provide additional configuration.\n\t\t *\n\t\t * Calling {@link SecurityMockMvcRequestPostProcessors#jwt()} is the equivalent of\n\t\t * calling {@code SecurityMockMvcRequestPostProcessors.jwt().jwt(() -> {})}.\n\t\t * @param jwtBuilderConsumer For configuring the underlying {@link Jwt}\n\t\t * @return the {@link JwtRequestPostProcessor} for additional customization\n\t\t */\n\t\tpublic JwtRequestPostProcessor jwt(Consumer<Jwt.Builder> jwtBuilderConsumer) {\n\t\t\tJwt.Builder jwtBuilder = Jwt.withTokenValue(\"token\")\n\t\t\t\t.header(\"alg\", \"none\")\n\t\t\t\t.claim(JwtClaimNames.SUB, \"user\")\n\t\t\t\t.claim(\"scope\", \"read\");\n\t\t\tjwtBuilderConsumer.accept(jwtBuilder);\n\t\t\tthis.jwt = jwtBuilder.build();\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the given {@link Jwt}\n\t\t * @param jwt The {@link Jwt} to use\n\t\t * @return the {@link JwtRequestPostProcessor} for additional customization\n\t\t */\n\t\tpublic JwtRequestPostProcessor jwt(Jwt jwt) {\n\t\t\tthis.jwt = jwt;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the token\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link JwtRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic JwtRequestPostProcessor authorities(Collection<GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authoritiesConverter = (jwt) -> authorities;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the token\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link JwtRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic JwtRequestPostProcessor authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authoritiesConverter = (jwt) -> Arrays.asList(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Provides the configured {@link Jwt} so that custom authorities can be derived\n\t\t * from it\n\t\t * @param authoritiesConverter the conversion strategy from {@link Jwt} to a\n\t\t * {@link Collection} of {@link GrantedAuthority}s\n\t\t * @return the {@link JwtRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic JwtRequestPostProcessor authorities(Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter) {\n\t\t\tAssert.notNull(authoritiesConverter, \"authoritiesConverter cannot be null\");\n\t\t\tthis.authoritiesConverter = authoritiesConverter;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tCsrfFilter.skipRequest(request);\n\t\t\tJwtAuthenticationToken token = new JwtAuthenticationToken(this.jwt,\n\t\t\t\t\tthis.authoritiesConverter.convert(this.jwt));\n\t\t\treturn new AuthenticationRequestPostProcessor(token).postProcessRequest(request);\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class OpaqueTokenRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate Supplier<Map<String, Object>> attributes = this::defaultAttributes;\n\n\t\tprivate Supplier<Collection<GrantedAuthority>> authorities = this::defaultAuthorities;\n\n\t\tprivate Supplier<OAuth2AuthenticatedPrincipal> principal = this::defaultPrincipal;\n\n\t\tprivate OpaqueTokenRequestPostProcessor() {\n\t\t}\n\n\t\t/**\n\t\t * Mutate the attributes using the given {@link Consumer}\n\t\t * @param attributesConsumer The {@link Consumer} for mutating the {@code Map} of\n\t\t * attributes\n\t\t * @return the {@link OpaqueTokenRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OpaqueTokenRequestPostProcessor attributes(Consumer<Map<String, Object>> attributesConsumer) {\n\t\t\tAssert.notNull(attributesConsumer, \"attributesConsumer cannot be null\");\n\t\t\tthis.attributes = () -> {\n\t\t\t\tMap<String, Object> attributes = defaultAttributes();\n\t\t\t\tattributesConsumer.accept(attributes);\n\t\t\t\treturn attributes;\n\t\t\t};\n\t\t\tthis.principal = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the resulting principal\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OpaqueTokenRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OpaqueTokenRequestPostProcessor authorities(Collection<GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = () -> authorities;\n\t\t\tthis.principal = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the resulting principal\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OpaqueTokenRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OpaqueTokenRequestPostProcessor authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = () -> Arrays.asList(authorities);\n\t\t\tthis.principal = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided principal\n\t\t * @param principal the principal to use\n\t\t * @return the {@link OpaqueTokenRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OpaqueTokenRequestPostProcessor principal(OAuth2AuthenticatedPrincipal principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = () -> principal;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tCsrfFilter.skipRequest(request);\n\t\t\tOAuth2AuthenticatedPrincipal principal = this.principal.get();\n\t\t\tOAuth2AccessToken accessToken = getOAuth2AccessToken(principal);\n\t\t\tBearerTokenAuthentication token = new BearerTokenAuthentication(principal, accessToken,\n\t\t\t\t\tprincipal.getAuthorities());\n\t\t\treturn new AuthenticationRequestPostProcessor(token).postProcessRequest(request);\n\t\t}\n\n\t\tprivate Map<String, Object> defaultAttributes() {\n\t\t\tMap<String, Object> attributes = new HashMap<>();\n\t\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.SUB, \"user\");\n\t\t\tattributes.put(OAuth2TokenIntrospectionClaimNames.SCOPE, \"read\");\n\t\t\treturn attributes;\n\t\t}\n\n\t\tprivate Collection<GrantedAuthority> defaultAuthorities() {\n\t\t\tMap<String, Object> attributes = this.attributes.get();\n\t\t\tObject scope = attributes.get(OAuth2TokenIntrospectionClaimNames.SCOPE);\n\t\t\tif (scope == null) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\tif (scope instanceof Collection) {\n\t\t\t\treturn getAuthorities((Collection) scope);\n\t\t\t}\n\t\t\tString scopes = scope.toString();\n\t\t\tif (!StringUtils.hasText(scopes)) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\treturn getAuthorities(Arrays.asList(scopes.split(\" \")));\n\t\t}\n\n\t\tprivate OAuth2AuthenticatedPrincipal defaultPrincipal() {\n\t\t\treturn new OAuth2IntrospectionAuthenticatedPrincipal(this.attributes.get(), this.authorities.get());\n\t\t}\n\n\t\tprivate Collection<GrantedAuthority> getAuthorities(Collection<?> scopes) {\n\t\t\treturn scopes.stream()\n\t\t\t\t.map((scope) -> new SimpleGrantedAuthority(\"SCOPE_\" + scope))\n\t\t\t\t.collect(Collectors.toList());\n\t\t}\n\n\t\tprivate OAuth2AccessToken getOAuth2AccessToken(OAuth2AuthenticatedPrincipal principal) {\n\t\t\tInstant expiresAt = getInstant(principal.getAttributes(), \"exp\");\n\t\t\tInstant issuedAt = getInstant(principal.getAttributes(), \"iat\");\n\t\t\treturn new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, \"token\", issuedAt, expiresAt);\n\t\t}\n\n\t\tprivate @Nullable Instant getInstant(Map<String, Object> attributes, String name) {\n\t\t\tObject value = attributes.get(name);\n\t\t\tif (value == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (value instanceof Instant) {\n\t\t\t\treturn (Instant) value;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(name + \" attribute must be of type Instant\");\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class OAuth2LoginRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate final String nameAttributeKey = \"sub\";\n\n\t\tprivate ClientRegistration clientRegistration;\n\n\t\tprivate OAuth2AccessToken accessToken;\n\n\t\tprivate Supplier<Collection<GrantedAuthority>> authorities = this::defaultAuthorities;\n\n\t\tprivate Supplier<Map<String, Object>> attributes = this::defaultAttributes;\n\n\t\tprivate Supplier<OAuth2User> oauth2User = this::defaultPrincipal;\n\n\t\tprivate OAuth2LoginRequestPostProcessor(OAuth2AccessToken accessToken) {\n\t\t\tthis.accessToken = accessToken;\n\t\t\tthis.clientRegistration = clientRegistrationBuilder().build();\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the {@link Authentication}\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OAuth2LoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginRequestPostProcessor authorities(Collection<GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = () -> authorities;\n\t\t\tthis.oauth2User = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the {@link Authentication}\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OAuth2LoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginRequestPostProcessor authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = () -> Arrays.asList(authorities);\n\t\t\tthis.oauth2User = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Mutate the attributes using the given {@link Consumer}\n\t\t * @param attributesConsumer The {@link Consumer} for mutating the {@code Map} of\n\t\t * attributes\n\t\t * @return the {@link OAuth2LoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginRequestPostProcessor attributes(Consumer<Map<String, Object>> attributesConsumer) {\n\t\t\tAssert.notNull(attributesConsumer, \"attributesConsumer cannot be null\");\n\t\t\tthis.attributes = () -> {\n\t\t\t\tMap<String, Object> attributes = defaultAttributes();\n\t\t\t\tattributesConsumer.accept(attributes);\n\t\t\t\treturn attributes;\n\t\t\t};\n\t\t\tthis.oauth2User = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link OAuth2User} as the authenticated user.\n\t\t * @param oauth2User the {@link OAuth2User} to use\n\t\t * @return the {@link OAuth2LoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginRequestPostProcessor oauth2User(OAuth2User oauth2User) {\n\t\t\tthis.oauth2User = () -> oauth2User;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link ClientRegistration} as the client to authorize.\n\t\t *\n\t\t * The supplied {@link ClientRegistration} will be registered into an\n\t\t * {@link OAuth2AuthorizedClientRepository}.\n\t\t * @param clientRegistration the {@link ClientRegistration} to use\n\t\t * @return the {@link OAuth2LoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2LoginRequestPostProcessor clientRegistration(ClientRegistration clientRegistration) {\n\t\t\tthis.clientRegistration = clientRegistration;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tOAuth2User oauth2User = this.oauth2User.get();\n\t\t\tOAuth2AuthenticationToken token = new OAuth2AuthenticationToken(oauth2User, oauth2User.getAuthorities(),\n\t\t\t\t\tthis.clientRegistration.getRegistrationId());\n\t\t\trequest = new AuthenticationRequestPostProcessor(token).postProcessRequest(request);\n\t\t\treturn new OAuth2ClientRequestPostProcessor().clientRegistration(this.clientRegistration)\n\t\t\t\t.principalName(oauth2User.getName())\n\t\t\t\t.accessToken(this.accessToken)\n\t\t\t\t.postProcessRequest(request);\n\t\t}\n\n\t\tprivate ClientRegistration.Builder clientRegistrationBuilder() {\n\t\t\treturn ClientRegistration.withRegistrationId(\"test\")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.clientId(\"test-client\")\n\t\t\t\t.authorizationUri(\"https://authorize-uri.example.org\")\n\t\t\t\t.tokenUri(\"https://token-uri.example.org\");\n\t\t}\n\n\t\tprivate Collection<GrantedAuthority> defaultAuthorities() {\n\t\t\tSet<GrantedAuthority> authorities = new LinkedHashSet<>();\n\t\t\tauthorities.add(new OAuth2UserAuthority(this.attributes.get(), this.nameAttributeKey));\n\t\t\tfor (String authority : this.accessToken.getScopes()) {\n\t\t\t\tauthorities.add(new SimpleGrantedAuthority(\"SCOPE_\" + authority));\n\t\t\t}\n\t\t\treturn authorities;\n\t\t}\n\n\t\tprivate Map<String, Object> defaultAttributes() {\n\t\t\tMap<String, Object> attributes = new HashMap<>();\n\t\t\tattributes.put(this.nameAttributeKey, \"user\");\n\t\t\treturn attributes;\n\t\t}\n\n\t\tprivate OAuth2User defaultPrincipal() {\n\t\t\treturn new DefaultOAuth2User(this.authorities.get(), this.attributes.get(), this.nameAttributeKey);\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class OidcLoginRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate ClientRegistration clientRegistration;\n\n\t\tprivate OAuth2AccessToken accessToken;\n\n\t\tprivate @Nullable OidcIdToken idToken;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate OidcUserInfo userInfo;\n\n\t\tprivate Supplier<OidcUser> oidcUser = this::defaultPrincipal;\n\n\t\tprivate @Nullable Collection<GrantedAuthority> authorities;\n\n\t\tprivate OidcLoginRequestPostProcessor(OAuth2AccessToken accessToken) {\n\t\t\tthis.accessToken = accessToken;\n\t\t\tthis.clientRegistration = clientRegistrationBuilder().build();\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the {@link Authentication}\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OidcLoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OidcLoginRequestPostProcessor authorities(Collection<GrantedAuthority> authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = authorities;\n\t\t\tthis.oidcUser = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided authorities in the {@link Authentication}\n\t\t * @param authorities the authorities to use\n\t\t * @return the {@link OidcLoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OidcLoginRequestPostProcessor authorities(GrantedAuthority... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.authorities = Arrays.asList(authorities);\n\t\t\tthis.oidcUser = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link OidcIdToken} when constructing the authenticated user\n\t\t * @param idTokenBuilderConsumer a {@link Consumer} of a\n\t\t * {@link OidcIdToken.Builder}\n\t\t * @return the {@link OidcLoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OidcLoginRequestPostProcessor idToken(Consumer<OidcIdToken.Builder> idTokenBuilderConsumer) {\n\t\t\tOidcIdToken.Builder builder = OidcIdToken.withTokenValue(\"id-token\");\n\t\t\tbuilder.subject(\"user\");\n\t\t\tidTokenBuilderConsumer.accept(builder);\n\t\t\tthis.idToken = builder.build();\n\t\t\tthis.oidcUser = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link OidcUserInfo} when constructing the authenticated user\n\t\t * @param userInfoBuilderConsumer a {@link Consumer} of a\n\t\t * {@link OidcUserInfo.Builder}\n\t\t * @return the {@link OidcLoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OidcLoginRequestPostProcessor userInfoToken(Consumer<OidcUserInfo.Builder> userInfoBuilderConsumer) {\n\t\t\tOidcUserInfo.Builder builder = OidcUserInfo.builder();\n\t\t\tuserInfoBuilderConsumer.accept(builder);\n\t\t\tthis.userInfo = builder.build();\n\t\t\tthis.oidcUser = this::defaultPrincipal;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link OidcUser} as the authenticated user.\n\t\t * @param oidcUser the {@link OidcUser} to use\n\t\t * @return the {@link OidcLoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OidcLoginRequestPostProcessor oidcUser(OidcUser oidcUser) {\n\t\t\tthis.oidcUser = () -> oidcUser;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use the provided {@link ClientRegistration} as the client to authorize.\n\t\t *\n\t\t * The supplied {@link ClientRegistration} will be registered into an\n\t\t * {@link HttpSessionOAuth2AuthorizedClientRepository}.\n\t\t * @param clientRegistration the {@link ClientRegistration} to use\n\t\t * @return the {@link OidcLoginRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OidcLoginRequestPostProcessor clientRegistration(ClientRegistration clientRegistration) {\n\t\t\tthis.clientRegistration = clientRegistration;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tOidcUser oidcUser = this.oidcUser.get();\n\t\t\treturn new OAuth2LoginRequestPostProcessor(this.accessToken).oauth2User(oidcUser)\n\t\t\t\t.clientRegistration(this.clientRegistration)\n\t\t\t\t.postProcessRequest(request);\n\t\t}\n\n\t\tprivate ClientRegistration.Builder clientRegistrationBuilder() {\n\t\t\treturn ClientRegistration.withRegistrationId(\"test\")\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.clientId(\"test-client\")\n\t\t\t\t.authorizationUri(\"https://authorize-uri.example.org\")\n\t\t\t\t.tokenUri(\"https://token-uri.example.org\");\n\t\t}\n\n\t\tprivate Collection<GrantedAuthority> getAuthorities() {\n\t\t\tif (this.authorities != null) {\n\t\t\t\treturn this.authorities;\n\t\t\t}\n\t\t\tSet<GrantedAuthority> authorities = new LinkedHashSet<>();\n\t\t\tauthorities.add(new OidcUserAuthority(getOidcIdToken(), getOidcUserInfo()));\n\t\t\tfor (String authority : this.accessToken.getScopes()) {\n\t\t\t\tauthorities.add(new SimpleGrantedAuthority(\"SCOPE_\" + authority));\n\t\t\t}\n\t\t\treturn authorities;\n\t\t}\n\n\t\tprivate OidcIdToken getOidcIdToken() {\n\t\t\tif (this.idToken != null) {\n\t\t\t\treturn this.idToken;\n\t\t\t}\n\t\t\treturn new OidcIdToken(\"id-token\", null, null, Collections.singletonMap(IdTokenClaimNames.SUB, \"user\"));\n\t\t}\n\n\t\tprivate OidcUserInfo getOidcUserInfo() {\n\t\t\treturn this.userInfo;\n\t\t}\n\n\t\tprivate OidcUser defaultPrincipal() {\n\t\t\treturn new DefaultOidcUser(getAuthorities(), getOidcIdToken(), this.userInfo);\n\t\t}\n\n\t}\n\n\t/**\n\t * @author Josh Cummings\n\t * @since 5.3\n\t */\n\tpublic static final class OAuth2ClientRequestPostProcessor implements RequestPostProcessor {\n\n\t\tprivate String registrationId = \"test\";\n\n\t\tprivate @Nullable ClientRegistration clientRegistration;\n\n\t\tprivate String principalName = \"user\";\n\n\t\tprivate OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,\n\t\t\t\t\"access-token\", null, null, Collections.singleton(\"read\"));\n\n\t\tprivate OAuth2ClientRequestPostProcessor() {\n\t\t}\n\n\t\tprivate OAuth2ClientRequestPostProcessor(String registrationId) {\n\t\t\tthis.registrationId = registrationId;\n\t\t\tclientRegistration((c) -> {\n\t\t\t});\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link ClientRegistration}\n\t\t * @param clientRegistration\n\t\t * @return the {@link OAuth2ClientRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2ClientRequestPostProcessor clientRegistration(ClientRegistration clientRegistration) {\n\t\t\tthis.clientRegistration = clientRegistration;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link Consumer} to configure a {@link ClientRegistration}\n\t\t * @param clientRegistrationConfigurer the {@link ClientRegistration} configurer\n\t\t * @return the {@link OAuth2ClientRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2ClientRequestPostProcessor clientRegistration(\n\t\t\t\tConsumer<ClientRegistration.Builder> clientRegistrationConfigurer) {\n\t\t\tClientRegistration.Builder builder = clientRegistrationBuilder();\n\t\t\tclientRegistrationConfigurer.accept(builder);\n\t\t\tthis.clientRegistration = builder.build();\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this as the resource owner's principal name\n\t\t * @param principalName the resource owner's principal name\n\t\t * @return the {@link OAuth2ClientRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2ClientRequestPostProcessor principalName(String principalName) {\n\t\t\tAssert.notNull(principalName, \"principalName cannot be null\");\n\t\t\tthis.principalName = principalName;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link OAuth2AccessToken}\n\t\t * @param accessToken the {@link OAuth2AccessToken} to use\n\t\t * @return the {@link OAuth2ClientRequestPostProcessor} for further configuration\n\t\t */\n\t\tpublic OAuth2ClientRequestPostProcessor accessToken(OAuth2AccessToken accessToken) {\n\t\t\tthis.accessToken = accessToken;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {\n\t\t\tif (this.clientRegistration == null) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Please specify a ClientRegistration via one \" + \"of the clientRegistration methods\");\n\t\t\t}\n\t\t\tOAuth2AuthorizedClient client = new OAuth2AuthorizedClient(this.clientRegistration, this.principalName,\n\t\t\t\t\tthis.accessToken);\n\t\t\tOAuth2AuthorizedClientRepository authorizedClientRepository = OAuth2ClientServletTestUtils\n\t\t\t\t.getAuthorizedClientRepository(request);\n\t\t\tAssert.isTrue(authorizedClientRepository != null,\n\t\t\t\t\t\"Could not find OAuth2AuthorizedClientRepository on the request\");\n\t\t\tif (!(authorizedClientRepository instanceof TestOAuth2AuthorizedClientRepository)) {\n\t\t\t\tauthorizedClientRepository = new TestOAuth2AuthorizedClientRepository(authorizedClientRepository);\n\t\t\t\tOAuth2ClientServletTestUtils.setAuthorizedClientRepository(request, authorizedClientRepository);\n\t\t\t}\n\t\t\tTestOAuth2AuthorizedClientRepository.enable(request);\n\t\t\tAuthentication anonymousPrincipal = new AnonymousAuthenticationToken(\"anonymous\", \"anonymousUser\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\t\tauthorizedClientRepository.saveAuthorizedClient(client, anonymousPrincipal, request,\n\t\t\t\t\tnew MockHttpServletResponse());\n\t\t\treturn request;\n\t\t}\n\n\t\tprivate ClientRegistration.Builder clientRegistrationBuilder() {\n\t\t\treturn ClientRegistration.withRegistrationId(this.registrationId)\n\t\t\t\t.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)\n\t\t\t\t.redirectUri(\"https://client.example.com\")\n\t\t\t\t.clientId(\"test-client\")\n\t\t\t\t.clientSecret(\"test-secret\")\n\t\t\t\t.authorizationUri(\"https://idp.example.org/oauth/authorize\")\n\t\t\t\t.tokenUri(\"https://idp.example.org/oauth/token\");\n\t\t}\n\n\t\t/**\n\t\t * Used to wrap the {@link OAuth2AuthorizedClientRepository} to provide support\n\t\t * for testing when the request is wrapped\n\t\t */\n\t\tprivate static final class TestOAuth2AuthorizedClientManager implements OAuth2AuthorizedClientManager {\n\n\t\t\tstatic final String ENABLED_ATTR_NAME = TestOAuth2AuthorizedClientManager.class.getName()\n\t\t\t\t.concat(\".ENABLED\");\n\n\t\t\tprivate final OAuth2AuthorizedClientManager delegate;\n\n\t\t\tprivate @Nullable OAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\t\t\tTestOAuth2AuthorizedClientManager(OAuth2AuthorizedClientManager delegate) {\n\t\t\t\tthis.delegate = delegate;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {\n\t\t\t\tHttpServletRequest request = authorizeRequest.getAttribute(HttpServletRequest.class.getName());\n\t\t\t\tif (request != null && isEnabled(request) && this.authorizedClientRepository != null) {\n\t\t\t\t\treturn this.authorizedClientRepository.loadAuthorizedClient(\n\t\t\t\t\t\t\tauthorizeRequest.getClientRegistrationId(), authorizeRequest.getPrincipal(), request);\n\t\t\t\t}\n\t\t\t\treturn this.delegate.authorize(authorizeRequest);\n\t\t\t}\n\n\t\t\tstatic void enable(HttpServletRequest request) {\n\t\t\t\trequest.setAttribute(ENABLED_ATTR_NAME, Boolean.TRUE);\n\t\t\t}\n\n\t\t\tboolean isEnabled(@Nullable HttpServletRequest request) {\n\t\t\t\treturn request != null && Boolean.TRUE.equals(request.getAttribute(ENABLED_ATTR_NAME));\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Used to wrap the {@link OAuth2AuthorizedClientRepository} to provide support\n\t\t * for testing when the request is wrapped\n\t\t */\n\t\tstatic final class TestOAuth2AuthorizedClientRepository implements OAuth2AuthorizedClientRepository {\n\n\t\t\tstatic final String TOKEN_ATTR_NAME = TestOAuth2AuthorizedClientRepository.class.getName().concat(\".TOKEN\");\n\n\t\t\tstatic final String ENABLED_ATTR_NAME = TestOAuth2AuthorizedClientRepository.class.getName()\n\t\t\t\t.concat(\".ENABLED\");\n\n\t\t\tprivate final OAuth2AuthorizedClientRepository delegate;\n\n\t\t\tTestOAuth2AuthorizedClientRepository(OAuth2AuthorizedClientRepository delegate) {\n\t\t\t\tthis.delegate = delegate;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic <T extends OAuth2AuthorizedClient> @Nullable T loadAuthorizedClient(String clientRegistrationId,\n\t\t\t\t\tAuthentication principal, HttpServletRequest request) {\n\t\t\t\tif (isEnabled(request)) {\n\t\t\t\t\treturn (T) request.getAttribute(TOKEN_ATTR_NAME);\n\t\t\t\t}\n\t\t\t\treturn this.delegate.loadAuthorizedClient(clientRegistrationId, principal, request);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,\n\t\t\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\t\t\tif (isEnabled(request)) {\n\t\t\t\t\trequest.setAttribute(TOKEN_ATTR_NAME, authorizedClient);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.delegate.saveAuthorizedClient(authorizedClient, principal, request, response);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void removeAuthorizedClient(String clientRegistrationId, Authentication principal,\n\t\t\t\t\tHttpServletRequest request, HttpServletResponse response) {\n\t\t\t\tif (isEnabled(request)) {\n\t\t\t\t\trequest.removeAttribute(TOKEN_ATTR_NAME);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.delegate.removeAuthorizedClient(clientRegistrationId, principal, request, response);\n\t\t\t}\n\n\t\t\tstatic void enable(HttpServletRequest request) {\n\t\t\t\trequest.setAttribute(ENABLED_ATTR_NAME, Boolean.TRUE);\n\t\t\t}\n\n\t\t\tboolean isEnabled(HttpServletRequest request) {\n\t\t\t\treturn Boolean.TRUE.equals(request.getAttribute(ENABLED_ATTR_NAME));\n\t\t\t}\n\n\t\t}\n\n\t\tprivate static final class OAuth2ClientServletTestUtils {\n\n\t\t\tprivate static final OAuth2AuthorizedClientRepository DEFAULT_CLIENT_REPO = new HttpSessionOAuth2AuthorizedClientRepository();\n\n\t\t\tprivate OAuth2ClientServletTestUtils() {\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Gets the {@link OAuth2AuthorizedClientRepository} for the specified\n\t\t\t * {@link HttpServletRequest}. If one is not found, one based off of\n\t\t\t * {@link HttpSessionOAuth2AuthorizedClientRepository} is used.\n\t\t\t * @param request the {@link HttpServletRequest} to obtain the\n\t\t\t * {@link OAuth2AuthorizedClientManager}\n\t\t\t * @return the {@link OAuth2AuthorizedClientManager} for the specified\n\t\t\t * {@link HttpServletRequest}\n\t\t\t */\n\t\t\tstatic @Nullable OAuth2AuthorizedClientRepository getAuthorizedClientRepository(\n\t\t\t\t\tHttpServletRequest request) {\n\t\t\t\tOAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(request);\n\t\t\t\tif (manager == null) {\n\t\t\t\t\treturn DEFAULT_CLIENT_REPO;\n\t\t\t\t}\n\t\t\t\tif (manager instanceof DefaultOAuth2AuthorizedClientManager) {\n\t\t\t\t\treturn (OAuth2AuthorizedClientRepository) ReflectionTestUtils.getField(manager,\n\t\t\t\t\t\t\t\"authorizedClientRepository\");\n\t\t\t\t}\n\t\t\t\tif (manager instanceof TestOAuth2AuthorizedClientManager) {\n\t\t\t\t\treturn ((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository;\n\t\t\t\t}\n\t\t\t\treturn DEFAULT_CLIENT_REPO;\n\t\t\t}\n\n\t\t\tstatic void setAuthorizedClientRepository(HttpServletRequest request,\n\t\t\t\t\tOAuth2AuthorizedClientRepository repository) {\n\t\t\t\tOAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(request);\n\t\t\t\tif (manager == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (manager instanceof DefaultOAuth2AuthorizedClientManager) {\n\t\t\t\t\tReflectionTestUtils.setField(manager, \"authorizedClientRepository\", repository);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!(manager instanceof TestOAuth2AuthorizedClientManager)) {\n\t\t\t\t\tmanager = new TestOAuth2AuthorizedClientManager(manager);\n\t\t\t\t\tsetOAuth2AuthorizedClientManager(request, manager);\n\t\t\t\t}\n\t\t\t\tTestOAuth2AuthorizedClientManager.enable(request);\n\t\t\t\t((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository = repository;\n\t\t\t}\n\n\t\t\tstatic @Nullable OAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(\n\t\t\t\t\tHttpServletRequest request) {\n\t\t\t\tOAuth2AuthorizedClientArgumentResolver resolver = findResolver(request,\n\t\t\t\t\t\tOAuth2AuthorizedClientArgumentResolver.class);\n\t\t\t\tif (resolver == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn (OAuth2AuthorizedClientManager) ReflectionTestUtils.getField(resolver,\n\t\t\t\t\t\t\"authorizedClientManager\");\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Sets the {@link OAuth2AuthorizedClientManager} for the specified\n\t\t\t * {@link HttpServletRequest}.\n\t\t\t * @param request the {@link HttpServletRequest} to obtain the\n\t\t\t * {@link OAuth2AuthorizedClientManager}\n\t\t\t * @param manager the {@link OAuth2AuthorizedClientManager} to set\n\t\t\t */\n\t\t\tstatic void setOAuth2AuthorizedClientManager(HttpServletRequest request,\n\t\t\t\t\tOAuth2AuthorizedClientManager manager) {\n\t\t\t\tOAuth2AuthorizedClientArgumentResolver resolver = findResolver(request,\n\t\t\t\t\t\tOAuth2AuthorizedClientArgumentResolver.class);\n\t\t\t\tif (resolver == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tReflectionTestUtils.setField(resolver, \"authorizedClientManager\", manager);\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tstatic <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(HttpServletRequest request,\n\t\t\t\t\tClass<T> resolverClass) {\n\t\t\t\tif (!ClassUtils.isPresent(\n\t\t\t\t\t\t\"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter\", null)) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn WebMvcClasspathGuard.findResolver(request, resolverClass);\n\t\t\t}\n\n\t\t\tprivate static class WebMvcClasspathGuard {\n\n\t\t\t\tstatic <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(HttpServletRequest request,\n\t\t\t\t\t\tClass<T> resolverClass) {\n\t\t\t\t\tServletContext servletContext = request.getServletContext();\n\t\t\t\t\tRequestMappingHandlerAdapter mapping = getRequestMappingHandlerAdapter(servletContext);\n\t\t\t\t\tif (mapping == null) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tList<HandlerMethodArgumentResolver> resolvers = mapping.getCustomArgumentResolvers();\n\t\t\t\t\tif (resolvers == null) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tfor (HandlerMethodArgumentResolver resolver : resolvers) {\n\t\t\t\t\t\tif (resolverClass.isAssignableFrom(resolver.getClass())) {\n\t\t\t\t\t\t\treturn (T) resolver;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tprivate static @Nullable RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(\n\t\t\t\t\t\tServletContext servletContext) {\n\t\t\t\t\tWebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);\n\t\t\t\t\tif (context != null) {\n\t\t\t\t\t\tString[] names = context.getBeanNamesForType(RequestMappingHandlerAdapter.class);\n\t\t\t\t\t\tif (names.length > 0) {\n\t\t\t\t\t\t\treturn (RequestMappingHandlerAdapter) context.getBean(names[0]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/request/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security built-in org.springframework.test.web.servlet.RequestBuilder\n * implementations.\n */\n@NullMarked\npackage org.springframework.security.test.web.servlet.request;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultHandlers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.response;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultHandler;\n\n/**\n * Security related {@link MockMvc} {@link ResultHandler}s\n *\n * @author Marcus da Coregio\n * @since 5.6\n */\npublic final class SecurityMockMvcResultHandlers {\n\n\tprivate SecurityMockMvcResultHandlers() {\n\t}\n\n\t/**\n\t * Exports the {@link SecurityContext} from {@link TestSecurityContextHolder} to\n\t * {@link SecurityContextHolder}\n\t */\n\tpublic static ResultHandler exportTestSecurityContext() {\n\t\treturn new ExportTestSecurityContextHandler();\n\t}\n\n\t/**\n\t * A {@link ResultHandler} that copies the {@link SecurityContext} from\n\t * {@link TestSecurityContextHolder} to {@link SecurityContextHolder}\n\t *\n\t * @author Marcus da Coregio\n\t * @since 5.6\n\t */\n\tprivate static class ExportTestSecurityContextHandler implements ResultHandler {\n\n\t\t@Override\n\t\tpublic void handle(MvcResult result) {\n\t\t\tSecurityContextHolder.setContext(TestSecurityContextHolder.getContext());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.response;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.context.HttpRequestResponseHolder;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.test.util.AssertionErrors;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Security related {@link MockMvc} {@link ResultMatcher}s.\n *\n * @author Rob Winch\n * @author Eddú Meléndez\n * @since 4.0\n */\npublic final class SecurityMockMvcResultMatchers {\n\n\tprivate SecurityMockMvcResultMatchers() {\n\t}\n\n\t/**\n\t * {@link ResultMatcher} that verifies that a specified user is authenticated.\n\t * @return the {@link AuthenticatedMatcher} to use\n\t */\n\tpublic static AuthenticatedMatcher authenticated() {\n\t\treturn new AuthenticatedMatcher();\n\t}\n\n\t/**\n\t * {@link ResultMatcher} that verifies that no user is authenticated.\n\t * @return the {@link AuthenticatedMatcher} to use\n\t */\n\tpublic static ResultMatcher unauthenticated() {\n\t\treturn new UnAuthenticatedMatcher();\n\t}\n\n\tprivate abstract static class AuthenticationMatcher<T extends AuthenticationMatcher<T>> implements ResultMatcher {\n\n\t\tprotected SecurityContext load(MvcResult result) {\n\t\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(result.getRequest(), result.getResponse());\n\t\t\tSecurityContextRepository repository = WebTestUtils.getSecurityContextRepository(result.getRequest());\n\t\t\treturn repository.loadContext(holder);\n\t\t}\n\n\t}\n\n\t/**\n\t * A {@link MockMvc} {@link ResultMatcher} that verifies a specific user is associated\n\t * to the {@link MvcResult}.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tpublic static final class AuthenticatedMatcher extends AuthenticationMatcher<AuthenticatedMatcher> {\n\n\t\tprivate @Nullable SecurityContext expectedContext;\n\n\t\tprivate @Nullable Authentication expectedAuthentication;\n\n\t\tprivate @Nullable Object expectedAuthenticationPrincipal;\n\n\t\tprivate @Nullable String expectedAuthenticationName;\n\n\t\tprivate @Nullable Collection<? extends GrantedAuthority> expectedGrantedAuthorities;\n\n\t\tprivate @Nullable Collection<String> expectedAuthorities;\n\n\t\tprivate Predicate<GrantedAuthority> ignoreAuthorities = (authority) -> false;\n\n\t\tprivate @Nullable Consumer<Authentication> assertAuthentication;\n\n\t\tAuthenticatedMatcher() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void match(MvcResult result) {\n\t\t\tSecurityContext context = load(result);\n\t\t\tAuthentication auth = context.getAuthentication();\n\t\t\tAssertionErrors.assertTrue(\"Authentication should not be null\", auth != null);\n\t\t\tif (this.assertAuthentication != null) {\n\t\t\t\tthis.assertAuthentication.accept(auth);\n\t\t\t}\n\t\t\tif (this.expectedContext != null) {\n\t\t\t\tAssertionErrors.assertEquals(this.expectedContext + \" does not equal \" + context, this.expectedContext,\n\t\t\t\t\t\tcontext);\n\t\t\t}\n\t\t\tif (this.expectedAuthentication != null) {\n\t\t\t\tAssertionErrors.assertEquals(\n\t\t\t\t\t\tthis.expectedAuthentication + \" does not equal \" + context.getAuthentication(),\n\t\t\t\t\t\tthis.expectedAuthentication, context.getAuthentication());\n\t\t\t}\n\t\t\tif (this.expectedAuthenticationPrincipal != null) {\n\t\t\t\tAssertionErrors.assertTrue(\"Authentication cannot be null\", context.getAuthentication() != null);\n\t\t\t\tAssertionErrors.assertEquals(\n\t\t\t\t\t\tthis.expectedAuthenticationPrincipal + \" does not equal \"\n\t\t\t\t\t\t\t\t+ context.getAuthentication().getPrincipal(),\n\t\t\t\t\t\tthis.expectedAuthenticationPrincipal, context.getAuthentication().getPrincipal());\n\t\t\t}\n\t\t\tif (this.expectedAuthenticationName != null) {\n\t\t\t\tAssertionErrors.assertTrue(\"Authentication cannot be null\", auth != null);\n\t\t\t\tString name = auth.getName();\n\t\t\t\tAssertionErrors.assertEquals(this.expectedAuthenticationName + \" does not equal \" + name,\n\t\t\t\t\t\tthis.expectedAuthenticationName, name);\n\t\t\t}\n\t\t\tif (this.expectedGrantedAuthorities != null) {\n\t\t\t\tAssertionErrors.assertTrue(\"Authentication cannot be null\", auth != null);\n\t\t\t\tCollection<? extends GrantedAuthority> authorities = new ArrayList<>(auth.getAuthorities());\n\t\t\t\tauthorities.removeIf(this.ignoreAuthorities);\n\t\t\t\tAssertionErrors.assertTrue(\n\t\t\t\t\t\tauthorities + \" does not contain the same authorities as \" + this.expectedGrantedAuthorities,\n\t\t\t\t\t\tauthorities.containsAll(this.expectedGrantedAuthorities));\n\t\t\t\tAssertionErrors.assertTrue(\n\t\t\t\t\t\tthis.expectedGrantedAuthorities + \" does not contain the same authorities as \" + authorities,\n\t\t\t\t\t\tthis.expectedGrantedAuthorities.containsAll(authorities));\n\t\t\t}\n\t\t\tif (this.expectedAuthorities != null) {\n\t\t\t\tAssertionErrors.assertTrue(\"Authentication cannot be null\", auth != null);\n\t\t\t\tList<String> authorities = auth.getAuthorities()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(Predicate.not(this.ignoreAuthorities))\n\t\t\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t\t\t.toList();\n\t\t\t\tAssertionErrors.assertTrue(\n\t\t\t\t\t\tauthorities + \" does not contain the same authorities as \" + this.expectedAuthorities,\n\t\t\t\t\t\tthis.expectedAuthorities.containsAll(authorities));\n\t\t\t\tAssertionErrors.assertTrue(\n\t\t\t\t\t\tthis.expectedAuthorities + \" does not contain the same authorities as \" + authorities,\n\t\t\t\t\t\tauthorities.containsAll(this.expectedAuthorities));\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Allows for any validating the authentication with arbitrary assertions\n\t\t * @param assertAuthentication the Consumer which validates the authentication\n\t\t * @return the AuthenticatedMatcher to perform additional assertions\n\t\t */\n\t\tpublic AuthenticatedMatcher withAuthentication(Consumer<Authentication> assertAuthentication) {\n\t\t\tthis.assertAuthentication = assertAuthentication;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the expected username\n\t\t * @param expected the expected username\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t */\n\t\tpublic AuthenticatedMatcher withUsername(String expected) {\n\t\t\treturn withAuthenticationName(expected);\n\t\t}\n\n\t\t/**\n\t\t * Specifies the expected {@link SecurityContext}\n\t\t * @param expected the expected {@link SecurityContext}\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t */\n\t\tpublic AuthenticatedMatcher withSecurityContext(SecurityContext expected) {\n\t\t\tthis.expectedContext = expected;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the expected {@link Authentication}\n\t\t * @param expected the expected {@link Authentication}\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t */\n\t\tpublic AuthenticatedMatcher withAuthentication(Authentication expected) {\n\t\t\tthis.expectedAuthentication = expected;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the expected principal\n\t\t * @param expected the expected principal\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t */\n\t\tpublic AuthenticatedMatcher withAuthenticationPrincipal(Object expected) {\n\t\t\tthis.expectedAuthenticationPrincipal = expected;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the expected {@link Authentication#getName()}\n\t\t * @param expected the expected {@link Authentication#getName()}\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t */\n\t\tpublic AuthenticatedMatcher withAuthenticationName(String expected) {\n\t\t\tthis.expectedAuthenticationName = expected;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the {@link GrantedAuthority#getAuthority()}\n\t\t * @param authorities the authorityNames\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t */\n\t\tpublic AuthenticatedMatcher withAuthorities(String... authorities) {\n\t\t\tAssert.notNull(authorities, \"authorities cannot be null\");\n\t\t\tthis.expectedAuthorities = Arrays.asList(authorities);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the {@link Authentication#getAuthorities()}\n\t\t * @param expected the {@link Authentication#getAuthorities()}\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t */\n\t\tpublic AuthenticatedMatcher withAuthorities(Collection<? extends GrantedAuthority> expected) {\n\t\t\tthis.expectedGrantedAuthorities = expected;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Specifies the expected roles.\n\t\t * <p>\n\t\t * Since a set of authorities can contain more than just roles, this method\n\t\t * differs from {@link #withAuthorities} in that it only verifies the authorities\n\t\t * prefixed by {@code ROLE_}. Other authorities are ignored.\n\t\t * <p>\n\t\t * If you want to validate more than just roles, please use\n\t\t * {@link #withAuthorities}.\n\t\t * @param roles the roles. Each value is automatically prefixed with \"ROLE_\"\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t */\n\t\tpublic AuthenticatedMatcher withRoles(String... roles) {\n\t\t\treturn withRoles(\"ROLE_\", roles);\n\t\t}\n\n\t\t/**\n\t\t * Specifies the expected roles.\n\t\t * <p>\n\t\t * Since a set of authorities can contain more than just roles, this method\n\t\t * differs from {@link #withAuthorities} in that it only verifies the authorities\n\t\t * prefixed by {@code ROLE_}. Other authorities are ignored.\n\t\t * <p>\n\t\t * If you want to validate more than just roles, please use\n\t\t * {@link #withAuthorities}.\n\t\t * @param rolePrefix the role prefix\n\t\t * @param roles the roles. Each value is automatically prefixed with the\n\t\t * {@code rolePrefix}\n\t\t * @return the {@link AuthenticatedMatcher} for further customization\n\t\t * @since 7.0\n\t\t */\n\t\tpublic AuthenticatedMatcher withRoles(String rolePrefix, String[] roles) {\n\t\t\tList<GrantedAuthority> withPrefix = new ArrayList<>();\n\t\t\tfor (String role : roles) {\n\t\t\t\twithPrefix.add(new SimpleGrantedAuthority(rolePrefix + role));\n\t\t\t}\n\t\t\tthis.ignoreAuthorities = (authority) -> (authority.getAuthority() != null\n\t\t\t\t\t&& !authority.getAuthority().startsWith(rolePrefix));\n\t\t\treturn withAuthorities(withPrefix);\n\t\t}\n\n\t}\n\n\t/**\n\t * A {@link MockMvc} {@link ResultMatcher} that verifies no {@link Authentication} is\n\t * associated with the {@link MvcResult}.\n\t *\n\t * @author Rob Winch\n\t * @since 4.0\n\t */\n\tprivate static final class UnAuthenticatedMatcher extends AuthenticationMatcher<UnAuthenticatedMatcher> {\n\n\t\tprivate AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\t\tprivate UnAuthenticatedMatcher() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void match(MvcResult result) {\n\t\t\tSecurityContext context = load(result);\n\n\t\t\tAuthentication authentication = context.getAuthentication();\n\t\t\tAssertionErrors.assertTrue(\"Expected anonymous Authentication got \" + context,\n\t\t\t\t\tauthentication == null || this.trustResolver.isAnonymous(authentication));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/response/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security server-side support for testing Spring MVC applications.\n */\n@NullMarked\npackage org.springframework.security.test.web.servlet.response;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.setup;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.FilterConfig;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\n\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.WebApplicationContext;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.testSecurityContext;\n\n/**\n * Configures Spring Security by adding the springSecurityFilterChain and adding the\n * {@link org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors#testSecurityContext()}\n * .\n *\n * @author Rob Winch\n * @since 4.0\n */\nfinal class SecurityMockMvcConfigurer extends MockMvcConfigurerAdapter {\n\n\tprivate final DelegateFilter delegateFilter;\n\n\t/**\n\t * Creates a new instance\n\t */\n\tSecurityMockMvcConfigurer() {\n\t\tthis.delegateFilter = new DelegateFilter();\n\t}\n\n\t/**\n\t * Creates a new instance with the provided {@link jakarta.servlet.Filter}\n\t * @param springSecurityFilterChain the {@link jakarta.servlet.Filter} to use\n\t */\n\tSecurityMockMvcConfigurer(Filter springSecurityFilterChain) {\n\t\tthis.delegateFilter = new DelegateFilter(springSecurityFilterChain);\n\t}\n\n\t@Override\n\tpublic void afterConfigurerAdded(ConfigurableMockMvcBuilder<?> builder) {\n\t\tbuilder.addFilters(this.delegateFilter);\n\t}\n\n\t@Override\n\tpublic RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder,\n\t\t\tWebApplicationContext context) {\n\t\tString securityBeanId = BeanIds.SPRING_SECURITY_FILTER_CHAIN;\n\t\tif (getSpringSecurityFilterChain() == null && context.containsBean(securityBeanId)) {\n\t\t\tsetSpringSecurityFilterChain(context.getBean(securityBeanId, Filter.class));\n\t\t}\n\t\tAssert.state(getSpringSecurityFilterChain() != null,\n\t\t\t\t() -> \"springSecurityFilterChain cannot be null. Ensure a Bean with the name \" + securityBeanId\n\t\t\t\t\t\t+ \" implementing Filter is present or inject the Filter to be used.\");\n\t\t// This is used by other test support to obtain the FilterChainProxy\n\t\tServletContext servletContext = context.getServletContext();\n\t\tAssert.notNull(servletContext, \"ServletContext must not be null\");\n\t\tservletContext.setAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN, getSpringSecurityFilterChain());\n\t\treturn testSecurityContext();\n\t}\n\n\tprivate void setSpringSecurityFilterChain(Filter filter) {\n\t\tthis.delegateFilter.setDelegate(filter);\n\t}\n\n\tprivate Filter getSpringSecurityFilterChain() {\n\t\treturn this.delegateFilter.delegate;\n\t}\n\n\t/**\n\t * Allows adding in {@link #afterConfigurerAdded(ConfigurableMockMvcBuilder)} to\n\t * preserve Filter order and then lazily set the delegate in\n\t * {@link #beforeMockMvcCreated(ConfigurableMockMvcBuilder, WebApplicationContext)}.\n\t *\n\t * {@link org.springframework.web.filter.DelegatingFilterProxy} is not used because it\n\t * is not easy to lazily set the delegate or get the delegate which is necessary for\n\t * the test infrastructure.\n\t */\n\tstatic class DelegateFilter implements Filter {\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Filter delegate;\n\n\t\tDelegateFilter() {\n\t\t}\n\n\t\tDelegateFilter(Filter delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\tvoid setDelegate(Filter delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\tFilter getDelegate() {\n\t\t\tFilter result = this.delegate;\n\t\t\tAssert.state(result != null,\n\t\t\t\t\t() -> \"delegate cannot be null. Ensure a Bean with the name \" + BeanIds.SPRING_SECURITY_FILTER_CHAIN\n\t\t\t\t\t\t\t+ \" implementing Filter is present or inject the Filter to be used.\");\n\t\t\treturn result;\n\t\t}\n\n\t\t@Override\n\t\tpublic void init(FilterConfig filterConfig) throws ServletException {\n\t\t\tgetDelegate().init(filterConfig);\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tgetDelegate().doFilter(request, response, chain);\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroy() {\n\t\t\tgetDelegate().destroy();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn getDelegate().equals(obj);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn getDelegate().hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn getDelegate().toString();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.setup;\n\nimport jakarta.servlet.Filter;\n\nimport org.springframework.test.web.servlet.setup.MockMvcConfigurer;\nimport org.springframework.util.Assert;\n\n/**\n * Provides Security related\n * {@link org.springframework.test.web.servlet.setup.MockMvcConfigurer} implementations.\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic final class SecurityMockMvcConfigurers {\n\n\tprivate SecurityMockMvcConfigurers() {\n\t}\n\n\t/**\n\t * Configures the MockMvcBuilder for use with Spring Security. Specifically the\n\t * configurer adds the Spring Bean named \"springSecurityFilterChain\" as a Filter. It\n\t * will also ensure that the TestSecurityContextHolder is leveraged for each request\n\t * by applying\n\t * {@link org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors#testSecurityContext()}\n\t * .\n\t * @return the {@link org.springframework.test.web.servlet.setup.MockMvcConfigurer} to\n\t * use\n\t */\n\tpublic static MockMvcConfigurer springSecurity() {\n\t\treturn new SecurityMockMvcConfigurer();\n\t}\n\n\t/**\n\t * Configures the MockMvcBuilder for use with Spring Security. Specifically the\n\t * configurer adds the provided Filter. It will also ensure that the\n\t * TestSecurityContextHolder is leveraged for each request by applying\n\t * {@link org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors#testSecurityContext()}\n\t * .\n\t * @param springSecurityFilterChain the Filter to be added\n\t * @return the {@link org.springframework.test.web.servlet.setup.MockMvcConfigurer} to\n\t * use\n\t */\n\tpublic static MockMvcConfigurer springSecurity(Filter springSecurityFilterChain) {\n\t\tAssert.notNull(springSecurityFilterChain, \"springSecurityFilterChain cannot be null\");\n\t\treturn new SecurityMockMvcConfigurer(springSecurityFilterChain);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/servlet/setup/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security built-in MockMvcBuilder implementations.\n */\n@NullMarked\npackage org.springframework.security.test.web.servlet.setup;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/support/WebTestUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.support;\n\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.WebApplicationContextUtils;\n\n/**\n * A utility class for testing spring security\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic abstract class WebTestUtils {\n\n\tprivate static final SecurityContextRepository DEFAULT_CONTEXT_REPO = new HttpSessionSecurityContextRepository();\n\n\tprivate static final CsrfTokenRepository DEFAULT_TOKEN_REPO = new HttpSessionCsrfTokenRepository();\n\n\tprivate static final CsrfTokenRequestHandler DEFAULT_CSRF_HANDLER = new XorCsrfTokenRequestAttributeHandler();\n\n\tprivate WebTestUtils() {\n\t}\n\n\t/**\n\t * Gets the {@link SecurityContextRepository} for the specified\n\t * {@link HttpServletRequest}. If one is not found, a default\n\t * {@link HttpSessionSecurityContextRepository} is used.\n\t * @param request the {@link HttpServletRequest} to obtain the\n\t * {@link SecurityContextRepository}\n\t * @return the {@link SecurityContextRepository} for the specified\n\t * {@link HttpServletRequest}\n\t */\n\tpublic static SecurityContextRepository getSecurityContextRepository(HttpServletRequest request) {\n\t\tSecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class);\n\t\tif (filter != null) {\n\t\t\tSecurityContextRepository repo = (SecurityContextRepository) ReflectionTestUtils.getField(filter, \"repo\");\n\t\t\tAssert.notNull(repo, \"SecurityContextRepository must not be null\");\n\t\t\treturn repo;\n\t\t}\n\t\tSecurityContextHolderFilter holderFilter = findFilter(request, SecurityContextHolderFilter.class);\n\t\tif (holderFilter != null) {\n\t\t\tSecurityContextRepository securityContextRepository = (SecurityContextRepository) ReflectionTestUtils\n\t\t\t\t.getField(holderFilter, \"securityContextRepository\");\n\t\t\tAssert.notNull(securityContextRepository, \"SecurityContextRepository must not be null\");\n\t\t\treturn securityContextRepository;\n\t\t}\n\t\treturn DEFAULT_CONTEXT_REPO;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} for the specified\n\t * {@link HttpServletRequest}.\n\t * @param request the {@link HttpServletRequest} to obtain the\n\t * {@link SecurityContextRepository}\n\t * @param securityContextRepository the {@link SecurityContextRepository} to set\n\t */\n\tpublic static void setSecurityContextRepository(HttpServletRequest request,\n\t\t\tSecurityContextRepository securityContextRepository) {\n\t\tSecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class);\n\t\tif (filter != null) {\n\t\t\tReflectionTestUtils.setField(filter, \"repo\", securityContextRepository);\n\t\t}\n\t\tSecurityContextHolderFilter holderFilter = findFilter(request, SecurityContextHolderFilter.class);\n\t\tif (holderFilter != null) {\n\t\t\tReflectionTestUtils.setField(holderFilter, \"securityContextRepository\", securityContextRepository);\n\t\t}\n\t}\n\n\t/**\n\t * Gets the {@link CsrfTokenRepository} for the specified {@link HttpServletRequest}.\n\t * If one is not found, the default {@link HttpSessionCsrfTokenRepository} is used.\n\t * @param request the {@link HttpServletRequest} to obtain the\n\t * {@link CsrfTokenRepository}\n\t * @return the {@link CsrfTokenRepository} for the specified\n\t * {@link HttpServletRequest}\n\t */\n\tpublic static @Nullable CsrfTokenRepository getCsrfTokenRepository(HttpServletRequest request) {\n\t\tCsrfFilter filter = findFilter(request, CsrfFilter.class);\n\t\tif (filter == null) {\n\t\t\treturn DEFAULT_TOKEN_REPO;\n\t\t}\n\t\treturn (CsrfTokenRepository) ReflectionTestUtils.getField(filter, \"tokenRepository\");\n\t}\n\n\t/**\n\t * Gets the {@link CsrfTokenRequestHandler} for the specified\n\t * {@link HttpServletRequest}. If one is not found, the default\n\t * {@link XorCsrfTokenRequestAttributeHandler} is used.\n\t * @param request the {@link HttpServletRequest} to obtain the\n\t * {@link CsrfTokenRequestHandler}\n\t * @return the {@link CsrfTokenRequestHandler} for the specified\n\t * {@link HttpServletRequest}\n\t */\n\tpublic static @Nullable CsrfTokenRequestHandler getCsrfTokenRequestHandler(HttpServletRequest request) {\n\t\tCsrfFilter filter = findFilter(request, CsrfFilter.class);\n\t\tif (filter == null) {\n\t\t\treturn DEFAULT_CSRF_HANDLER;\n\t\t}\n\t\treturn (CsrfTokenRequestHandler) ReflectionTestUtils.getField(filter, \"requestHandler\");\n\t}\n\n\t/**\n\t * Sets the {@link CsrfTokenRepository} for the specified {@link HttpServletRequest}.\n\t * @param request the {@link HttpServletRequest} to obtain the\n\t * {@link CsrfTokenRepository}\n\t * @param repository the {@link CsrfTokenRepository} to set\n\t */\n\tpublic static void setCsrfTokenRepository(HttpServletRequest request, CsrfTokenRepository repository) {\n\t\tCsrfFilter filter = findFilter(request, CsrfFilter.class);\n\t\tif (filter != null) {\n\t\t\tReflectionTestUtils.setField(filter, \"tokenRepository\", repository);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tstatic <T extends Filter> @Nullable T findFilter(HttpServletRequest request, Class<T> filterClass) {\n\t\tServletContext servletContext = request.getServletContext();\n\t\tFilter springSecurityFilterChain = getSpringSecurityFilterChain(servletContext);\n\t\tif (springSecurityFilterChain == null) {\n\t\t\treturn null;\n\t\t}\n\t\tList<Filter> filters = ReflectionTestUtils.invokeMethod(springSecurityFilterChain, \"getFilters\", request);\n\t\tif (filters == null) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (Filter filter : filters) {\n\t\t\tif (filterClass.isAssignableFrom(filter.getClass())) {\n\t\t\t\treturn (T) filter;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static @Nullable Filter getSpringSecurityFilterChain(ServletContext servletContext) {\n\t\tFilter result = (Filter) servletContext.getAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\tWebApplicationContext webApplicationContext = WebApplicationContextUtils\n\t\t\t.getWebApplicationContext(servletContext);\n\t\tif (webApplicationContext == null) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tString beanName = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;\n\t\t\treturn webApplicationContext.getBean(beanName, Filter.class);\n\t\t}\n\t\tcatch (NoSuchBeanDefinitionException ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "test/src/main/java/org/springframework/security/test/web/support/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security supporting the org.springframework.web.context package, such as\n * WebApplicationContext implementations and various utility classes.\n */\n@NullMarked\npackage org.springframework.security.test.web.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "test/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.test.context.aot.TestRuntimeHintsRegistrar=\\\norg.springframework.security.test.aot.hint.WithSecurityContextTestRuntimeHints\n\norg.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.test.aot.hint.WebTestUtilsRuntimeHints\n"
  },
  {
    "path": "test/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.test.context.TestExecutionListener = \\\n\torg.springframework.security.test.context.support.WithSecurityContextTestExecutionListener,\\\n\torg.springframework.security.test.context.support.ReactorContextTestExecutionListener\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/aot/hint/WebTestUtilsRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.aot.hint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link WebTestUtilsRuntimeHints}.\n *\n * @author Marcus da Coregio\n */\nclass WebTestUtilsRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@Test\n\tvoid filterChainProxyHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(FilterChainProxy.class)\n\t\t\t.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid compositeFilterChainProxyHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy\"))\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints);\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy\"))\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid csrfFilterHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(CsrfFilter.class)\n\t\t\t.withMemberCategories(MemberCategory.ACCESS_DECLARED_FIELDS)).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid securityContextPersistenceFilterHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(SecurityContextPersistenceFilter.class)\n\t\t\t.withMemberCategories(MemberCategory.ACCESS_DECLARED_FIELDS)).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid securityContextHolderFilterHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(SecurityContextHolderFilter.class)\n\t\t\t.withMemberCategories(MemberCategory.ACCESS_DECLARED_FIELDS)).accepts(this.hints);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/aot/hint/WithSecurityContextTestRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.aot.hint;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.showcase.WithMockCustomUser;\nimport org.springframework.security.test.context.showcase.WithMockCustomUserSecurityContextFactory;\nimport org.springframework.security.test.context.support.WithAnonymousUser;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.context.support.WithSecurityContext;\nimport org.springframework.security.test.context.support.WithSecurityContextFactory;\nimport org.springframework.security.test.context.support.WithUserDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link WithSecurityContextTestRuntimeHints}.\n */\n@WithMockCustomUser\nclass WithSecurityContextTestRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\tprivate final WithSecurityContextTestRuntimeHints registrar = new WithSecurityContextTestRuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.registrar.registerHints(this.hints, WithSecurityContextTestRuntimeHintsTests.class,\n\t\t\t\tWithSecurityContextTestRuntimeHintsTests.class.getClassLoader());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tvoid withMockUserHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.test.context.support.WithMockUserSecurityContextFactory\"))\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n\t@Test\n\t@WithAnonymousUser\n\tvoid withAnonymousUserHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.test.context.support.WithAnonymousUserSecurityContextFactory\"))\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n\t@Test\n\t@WithUserDetails\n\tvoid withUserDetailsHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.test.context.support.WithUserDetailsSecurityContextFactory\"))\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n\t@Test\n\t@WithMockTestUser\n\tvoid withMockTestUserHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(WithMockTestUserSecurityContextFactory.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid withMockCustomUserOnClassHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(WithMockCustomUserSecurityContextFactory.class)\n\t\t\t.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@WithSecurityContext(factory = WithMockTestUserSecurityContextFactory.class)\n\t@interface WithMockTestUser {\n\n\t}\n\n\tstatic class WithMockTestUserSecurityContextFactory implements WithSecurityContextFactory<WithMockTestUser> {\n\n\t\t@Override\n\t\tpublic SecurityContext createSecurityContext(WithMockTestUser annotation) {\n\t\t\treturn SecurityContextHolder.createEmptyContext();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/TestSecurityContextHolderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\npublic class TestSecurityContextHolderTests {\n\n\tprivate SecurityContext context;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.context = SecurityContextHolder.createEmptyContext();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void clearContextClearsBoth() {\n\t\tSecurityContextHolder.setContext(this.context);\n\t\tTestSecurityContextHolder.setContext(this.context);\n\t\tTestSecurityContextHolder.clearContext();\n\t\tassertThat(SecurityContextHolder.getContext()).isNotSameAs(this.context);\n\t\tassertThat(TestSecurityContextHolder.getContext()).isNotSameAs(this.context);\n\t}\n\n\t@Test\n\tpublic void getContextDefaultsNonNull() {\n\t\tassertThat(TestSecurityContextHolder.getContext()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void setContextSetsBoth() {\n\t\tTestSecurityContextHolder.setContext(this.context);\n\t\tassertThat(TestSecurityContextHolder.getContext()).isSameAs(this.context);\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(this.context);\n\t}\n\n\t@Test\n\tpublic void setContextWithAuthentication() {\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tTestSecurityContextHolder.setAuthentication(authentication);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication()).isSameAs(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/annotation/SecurityTestExecutionListenerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.annotation;\n\nimport java.security.Principal;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(SpringExtension.class)\n@SecurityTestExecutionListeners\npublic class SecurityTestExecutionListenerTests {\n\n\t@WithMockUser\n\t@Test\n\tpublic void withSecurityContextTestExecutionListenerIsRegistered() {\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"user\");\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void reactorContextTestSecurityContextHolderExecutionListenerTestIsRegistered() {\n\t\tMono<String> name = ReactiveSecurityContextHolder.getContext()\n\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t.map(Principal::getName);\n\t\tStepVerifier.create(name).expectNext(\"user\").verifyComplete();\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/CustomUserDetails.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase;\n\nimport java.util.Collection;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * @author Rob Winch\n */\npublic class CustomUserDetails implements UserDetails {\n\n\tprivate final String name;\n\n\tprivate final String username;\n\n\tprivate final Collection<? extends GrantedAuthority> authorities;\n\n\tpublic CustomUserDetails(String name, String username) {\n\t\tthis.name = name;\n\t\tthis.username = username;\n\t\tthis.authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t}\n\n\t@Override\n\tpublic Collection<? extends GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic String getPassword() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getUsername() {\n\t\treturn this.username;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CustomUserDetails{\" + \"username='\" + this.username + '\\'' + '}';\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/WithMockCustomUser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.springframework.security.test.context.support.WithSecurityContext;\n\n/**\n * @author Rob Winch\n */\n@Retention(RetentionPolicy.RUNTIME)\n@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)\npublic @interface WithMockCustomUser {\n\n\t/**\n\t * The username to be used. The default is rob\n\t * @return\n\t */\n\tString username() default \"rob\";\n\n\t/**\n\t * The roles to use. The default is \"USER\". A\n\t * {@link org.springframework.security.core.GrantedAuthority} will be created for each\n\t * value within roles. Each value in roles will automatically be prefixed with\n\t * \"ROLE_\". For example, the default will result in \"ROLE_USER\" being used.\n\t * @return\n\t */\n\tString[] roles() default { \"USER\" };\n\n\t/**\n\t * The name of the user\n\t * @return\n\t */\n\tString name() default \"Rob Winch\";\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/WithMockCustomUserSecurityContextFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.support.WithSecurityContextFactory;\n\n/**\n * @author Rob Winch\n */\npublic class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {\n\n\t@Override\n\tpublic SecurityContext createSecurityContext(WithMockCustomUser customUser) {\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tCustomUserDetails principal = new CustomUserDetails(customUser.name(), customUser.username());\n\t\tAuthentication auth = UsernamePasswordAuthenticationToken.authenticated(principal, \"password\",\n\t\t\t\tprincipal.getAuthorities());\n\t\tcontext.setAuthentication(auth);\n\t\treturn context;\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/WithMockUserParent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase;\n\nimport org.springframework.security.test.context.support.WithMockUser;\n\n/**\n * @author Eddú Meléndez\n */\n@WithMockUser\npublic class WithMockUserParent {\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/WithMockUserParentTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.test.context.showcase.service.HelloMessageService;\nimport org.springframework.security.test.context.showcase.service.MessageService;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Eddú Meléndez\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = WithMockUserParentTests.Config.class)\npublic class WithMockUserParentTests extends WithMockUserParent {\n\n\t@Autowired\n\tprivate MessageService messageService;\n\n\t@Test\n\tpublic void getMessageWithMockUser() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"user\");\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\t@EnableWebSecurity\n\t@ComponentScan(basePackageClasses = HelloMessageService.class)\n\tstatic class Config {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/WithMockUserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.test.context.showcase.service.HelloMessageService;\nimport org.springframework.security.test.context.showcase.service.MessageService;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = WithMockUserTests.Config.class)\npublic class WithMockUserTests {\n\n\t@Autowired\n\tprivate MessageService messageService;\n\n\t@Test\n\tpublic void getMessageUnauthenticated() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.messageService.getMessage())\n\t\t\t.withRootCauseInstanceOf(AuthenticationCredentialsNotFoundException.class);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void getMessageWithMockUser() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"user\");\n\t}\n\n\t@Test\n\t@WithMockUser(\"customUsername\")\n\tpublic void getMessageWithMockUserCustomUsername() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"customUsername\");\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"admin\", roles = { \"USER\", \"ADMIN\" })\n\tpublic void getMessageWithMockUserCustomUser() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"admin\").contains(\"ROLE_USER\").contains(\"ROLE_ADMIN\");\n\t}\n\n\t@Test\n\t@WithMockUser(username = \"admin\", authorities = { \"ADMIN\", \"USER\" })\n\tpublic void getMessageWithMockUserCustomAuthorities() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"admin\").contains(\"ADMIN\").contains(\"USER\").doesNotContain(\"ROLE_\");\n\t}\n\n\t@Configuration\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.METHOD)\n\t@Inherited\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic @interface WithAdminUser {\n\n\t\t@AliasFor(annotation = WithMockUser.class, attribute = \"value\")\n\t\tString value();\n\n\t}\n\n\t@Test\n\t@WithAdminUser(\"admin\")\n\tpublic void getMessageWithMetaAnnotationAdminUser() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"admin\").contains(\"ADMIN\").contains(\"ROLE_ADMIN\");\n\t}\n\n\t@EnableMethodSecurity\n\t@EnableWebSecurity\n\t@ComponentScan(basePackageClasses = HelloMessageService.class)\n\tstatic class Config {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/WithUserDetailsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.test.context.showcase.service.HelloMessageService;\nimport org.springframework.security.test.context.showcase.service.MessageService;\nimport org.springframework.security.test.context.support.WithUserDetails;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = WithUserDetailsTests.Config.class)\npublic class WithUserDetailsTests {\n\n\t@Autowired\n\tprivate MessageService messageService;\n\n\t@Test\n\tpublic void getMessageUnauthenticated() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.messageService.getMessage())\n\t\t\t.withRootCauseInstanceOf(AuthenticationCredentialsNotFoundException.class);\n\t}\n\n\t@Test\n\t@WithUserDetails\n\tpublic void getMessageWithUserDetails() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"user\");\n\t\tassertThat(getPrincipal()).isInstanceOf(CustomUserDetails.class);\n\t}\n\n\t@Test\n\t@WithUserDetails(\"customUsername\")\n\tpublic void getMessageWithUserDetailsCustomUsername() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"customUsername\");\n\t\tassertThat(getPrincipal()).isInstanceOf(CustomUserDetails.class);\n\t}\n\n\t@Test\n\t@WithUserDetails(value = \"customUsername\", userDetailsServiceBeanName = \"myUserDetailsService\")\n\tpublic void getMessageWithUserDetailsServiceBeanName() {\n\t\tString message = this.messageService.getMessage();\n\t\tassertThat(message).contains(\"customUsername\");\n\t\tassertThat(getPrincipal()).isInstanceOf(CustomUserDetails.class);\n\t}\n\n\tprivate Object getPrincipal() {\n\t\treturn SecurityContextHolder.getContext().getAuthentication().getPrincipal();\n\t}\n\n\t@Configuration\n\t@EnableMethodSecurity\n\t@EnableWebSecurity\n\t@ComponentScan(basePackageClasses = HelloMessageService.class)\n\tstatic class Config {\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tauth.userDetailsService(myUserDetailsService());\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService myUserDetailsService() {\n\t\t\treturn new CustomUserDetailsService();\n\t\t}\n\n\t}\n\n\tstatic class CustomUserDetailsService implements UserDetailsService {\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {\n\t\t\treturn new CustomUserDetails(\"name\", username);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/service/HelloMessageService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase.service;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author Rob Winch\n */\n@Component\npublic class HelloMessageService implements MessageService {\n\n\t@Override\n\t@PreAuthorize(\"authenticated\")\n\tpublic String getMessage() {\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\treturn \"Hello \" + authentication;\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/showcase/service/MessageService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.showcase.service;\n\n/**\n * @author Rob Winch\n */\npublic interface MessageService {\n\n\tString getMessage();\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/support/ReactorContextTestExecutionListenerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\nimport java.util.concurrent.ForkJoinPool;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Hooks;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.core.OrderComparator;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.test.context.TestContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(MockitoExtension.class)\npublic class ReactorContextTestExecutionListenerTests {\n\n\t@Mock\n\tprivate TestContext testContext;\n\n\tprivate ReactorContextTestExecutionListener listener = new ReactorContextTestExecutionListener();\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t\tHooks.resetOnLastOperator();\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenSecurityContextEmptyThenReactorContextNull() throws Exception {\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tMono<?> result = ReactiveSecurityContextHolder.getContext();\n\t\tStepVerifier.create(result).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenNullAuthenticationThenReactorContextNull() throws Exception {\n\t\tTestSecurityContextHolder.setContext(new SecurityContextImpl());\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tMono<?> result = ReactiveSecurityContextHolder.getContext();\n\t\tStepVerifier.create(result).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenAuthenticationThenReactorContextHasAuthentication() throws Exception {\n\t\tTestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_USER\");\n\t\tTestSecurityContextHolder.setAuthentication(expectedAuthentication);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertAuthentication(expectedAuthentication);\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenCustomContext() throws Exception {\n\t\tTestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_USER\");\n\t\tSecurityContext context = new CustomContext(expectedAuthentication);\n\t\tTestSecurityContextHolder.setContext(context);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertSecurityContext(context);\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenExistingAuthenticationThenReactorContextHasOriginalAuthentication()\n\t\t\tthrows Exception {\n\t\tTestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_USER\");\n\t\tTestingAuthenticationToken contextHolder = new TestingAuthenticationToken(\"contextHolder\", \"password\",\n\t\t\t\t\"ROLE_USER\");\n\t\tTestSecurityContextHolder.setAuthentication(contextHolder);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tMono<Authentication> authentication = Mono.just(\"any\")\n\t\t\t.flatMap((s) -> ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(expectedAuthentication));\n\t\tStepVerifier.create(authentication).expectNext(expectedAuthentication).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenClearThenReactorContextDoesNotOverride() throws Exception {\n\t\tTestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken(\"user\", \"password\",\n\t\t\t\t\"ROLE_USER\");\n\t\tTestingAuthenticationToken contextHolder = new TestingAuthenticationToken(\"contextHolder\", \"password\",\n\t\t\t\t\"ROLE_USER\");\n\t\tTestSecurityContextHolder.setAuthentication(contextHolder);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tMono<Authentication> authentication = Mono.just(\"any\")\n\t\t\t.flatMap((s) -> ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.clearContext());\n\t\tStepVerifier.create(authentication).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void afterTestMethodWhenSecurityContextEmptyThenNoError() throws Exception {\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tthis.listener.afterTestMethod(this.testContext);\n\t}\n\n\t@Test\n\tpublic void afterTestMethodWhenSetupThenReactorContextNull() throws Exception {\n\t\tbeforeTestMethodWhenAuthenticationThenReactorContextHasAuthentication();\n\t\tthis.listener.afterTestMethod(this.testContext);\n\t\tassertThat(Mono.deferContextual(Mono::just).block().isEmpty()).isTrue();\n\t}\n\n\t@Test\n\tpublic void afterTestMethodWhenDifferentHookIsRegistered() throws Exception {\n\t\tObject obj = new Object();\n\t\tHooks.onLastOperator(\"CUSTOM_HOOK\", (p) -> Mono.just(obj));\n\t\tthis.listener.afterTestMethod(this.testContext);\n\t\tObject result = Mono.deferContextual(Mono::just).block();\n\t\tassertThat(result).isEqualTo(obj);\n\t}\n\n\t@Test\n\tpublic void orderWhenComparedToWithSecurityContextTestExecutionListenerIsAfter() {\n\t\tOrderComparator comparator = new OrderComparator();\n\t\tWithSecurityContextTestExecutionListener withSecurity = new WithSecurityContextTestExecutionListener();\n\t\tReactorContextTestExecutionListener reactorContext = new ReactorContextTestExecutionListener();\n\t\tassertThat(comparator.compare(withSecurity, reactorContext)).isLessThan(0);\n\t}\n\n\t@Test\n\tpublic void checkSecurityContextResolutionWhenSubscribedContextCalledOnTheDifferentThreadThanWithSecurityContextTestExecutionListener()\n\t\t\tthrows Exception {\n\t\tTestingAuthenticationToken contextHolder = new TestingAuthenticationToken(\"contextHolder\", \"password\",\n\t\t\t\t\"ROLE_USER\");\n\t\tTestSecurityContextHolder.setAuthentication(contextHolder);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tForkJoinPool.commonPool().submit(() -> assertAuthentication(contextHolder)).join();\n\t}\n\n\tpublic void assertAuthentication(Authentication expected) {\n\t\tMono<Authentication> authentication = ReactiveSecurityContextHolder.getContext()\n\t\t\t.map(SecurityContext::getAuthentication);\n\t\tStepVerifier.create(authentication).expectNext(expected).verifyComplete();\n\t}\n\n\tprivate void assertSecurityContext(SecurityContext expected) {\n\t\tMono<SecurityContext> securityContext = ReactiveSecurityContextHolder.getContext();\n\t\tStepVerifier.create(securityContext).expectNext(expected).verifyComplete();\n\t}\n\n\tstatic class CustomContext implements SecurityContext {\n\n\t\tprivate Authentication authentication;\n\n\t\tCustomContext(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAuthentication(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/support/WithAnonymousUserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.annotation.AnnotatedElementUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class WithAnonymousUserTests {\n\n\t@Test\n\tpublic void defaults() {\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(Annotated.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_METHOD);\n\t}\n\n\t@Test\n\tpublic void findMergedAnnotationWhenSetupExplicitThenOverridden() {\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(SetupExplicit.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_METHOD);\n\t}\n\n\t@Test\n\tpublic void findMergedAnnotationWhenSetupOverriddenThenOverridden() {\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(SetupOverridden.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_EXECUTION);\n\t}\n\n\t@WithAnonymousUser\n\tprivate class Annotated {\n\n\t}\n\n\t@WithAnonymousUser(setupBefore = TestExecutionEvent.TEST_METHOD)\n\tprivate class SetupExplicit {\n\n\t}\n\n\t@WithAnonymousUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n\tprivate class SetupOverridden {\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.BDDMockito.given;\n\n@ExtendWith(MockitoExtension.class)\npublic class WithMockUserSecurityContextFactoryTests {\n\n\t@Mock\n\tprivate WithMockUser withUser;\n\n\tprivate WithMockUserSecurityContextFactory factory;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.factory = new WithMockUserSecurityContextFactory();\n\t}\n\n\t@Test\n\tpublic void usernameNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.factory.createSecurityContext(this.withUser));\n\t}\n\n\t@Test\n\tpublic void valueDefaultsUsername() {\n\t\tgiven(this.withUser.value()).willReturn(\"valueUser\");\n\t\tgiven(this.withUser.password()).willReturn(\"password\");\n\t\tgiven(this.withUser.roles()).willReturn(new String[] { \"USER\" });\n\t\tgiven(this.withUser.authorities()).willReturn(new String[] {});\n\t\tassertThat(this.factory.createSecurityContext(this.withUser).getAuthentication().getName())\n\t\t\t.isEqualTo(this.withUser.value());\n\t}\n\n\t@Test\n\tpublic void usernamePrioritizedOverValue() {\n\t\tgiven(this.withUser.username()).willReturn(\"customUser\");\n\t\tgiven(this.withUser.password()).willReturn(\"password\");\n\t\tgiven(this.withUser.roles()).willReturn(new String[] { \"USER\" });\n\t\tgiven(this.withUser.authorities()).willReturn(new String[] {});\n\t\tassertThat(this.factory.createSecurityContext(this.withUser).getAuthentication().getName())\n\t\t\t.isEqualTo(this.withUser.username());\n\t}\n\n\t@Test\n\tpublic void rolesWorks() {\n\t\tgiven(this.withUser.value()).willReturn(\"valueUser\");\n\t\tgiven(this.withUser.password()).willReturn(\"password\");\n\t\tgiven(this.withUser.roles()).willReturn(new String[] { \"USER\", \"CUSTOM\" });\n\t\tgiven(this.withUser.authorities()).willReturn(new String[] {});\n\t\tassertThat(this.factory.createSecurityContext(this.withUser).getAuthentication().getAuthorities())\n\t\t\t.extracting(\"authority\")\n\t\t\t.containsOnly(\"ROLE_USER\", \"ROLE_CUSTOM\");\n\t}\n\n\t@Test\n\tpublic void authoritiesWorks() {\n\t\tgiven(this.withUser.value()).willReturn(\"valueUser\");\n\t\tgiven(this.withUser.password()).willReturn(\"password\");\n\t\tgiven(this.withUser.roles()).willReturn(new String[] { \"USER\" });\n\t\tgiven(this.withUser.authorities()).willReturn(new String[] { \"USER\", \"CUSTOM\" });\n\t\tassertThat(this.factory.createSecurityContext(this.withUser).getAuthentication().getAuthorities())\n\t\t\t.extracting(\"authority\")\n\t\t\t.containsOnly(\"USER\", \"CUSTOM\");\n\t}\n\n\t@Test\n\tpublic void authoritiesAndRolesInvalid() {\n\t\tgiven(this.withUser.value()).willReturn(\"valueUser\");\n\t\tgiven(this.withUser.roles()).willReturn(new String[] { \"CUSTOM\" });\n\t\tgiven(this.withUser.authorities()).willReturn(new String[] { \"USER\", \"CUSTOM\" });\n\t\tassertThatIllegalStateException().isThrownBy(() -> this.factory.createSecurityContext(this.withUser));\n\t}\n\n\t@Test\n\tpublic void rolesWithRolePrefixFails() {\n\t\tgiven(this.withUser.value()).willReturn(\"valueUser\");\n\t\tgiven(this.withUser.roles()).willReturn(new String[] { \"ROLE_FAIL\" });\n\t\tgiven(this.withUser.authorities()).willReturn(new String[] {});\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.factory.createSecurityContext(this.withUser));\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/support/WithMockUserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.annotation.AnnotatedElementUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class WithMockUserTests {\n\n\t@Test\n\tpublic void defaults() {\n\t\tWithMockUser mockUser = AnnotatedElementUtils.findMergedAnnotation(Annotated.class, WithMockUser.class);\n\t\tassertThat(mockUser.value()).isEqualTo(\"user\");\n\t\tassertThat(mockUser.username()).isEmpty();\n\t\tassertThat(mockUser.password()).isEqualTo(\"password\");\n\t\tassertThat(mockUser.roles()).containsOnly(\"USER\");\n\t\tassertThat(mockUser.setupBefore()).isEqualByComparingTo(TestExecutionEvent.TEST_METHOD);\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(Annotated.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_METHOD);\n\t}\n\n\t@Test\n\tpublic void findMergedAnnotationWhenSetupExplicitThenOverridden() {\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(SetupExplicit.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_METHOD);\n\t}\n\n\t@Test\n\tpublic void findMergedAnnotationWhenSetupOverriddenThenOverridden() {\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(SetupOverridden.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_EXECUTION);\n\t}\n\n\t@WithMockUser\n\tprivate class Annotated {\n\n\t}\n\n\t@WithMockUser(setupBefore = TestExecutionEvent.TEST_METHOD)\n\tprivate class SetupExplicit {\n\n\t}\n\n\t@WithMockUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n\tprivate class SetupOverridden {\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/support/WithSecurityContextTestExcecutionListenerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.AnnotationAwareOrderComparator;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.test.context.NestedTestConfiguration;\nimport org.springframework.test.context.TestContext;\nimport org.springframework.test.context.TestExecutionListener;\nimport org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener;\nimport org.springframework.test.context.support.AbstractTestExecutionListener;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n@ExtendWith(MockitoExtension.class)\npublic class WithSecurityContextTestExcecutionListenerTests {\n\n\tprivate ConfigurableApplicationContext context;\n\n\t@Mock\n\tprivate TestContext testContext;\n\n\tprivate WithSecurityContextTestExecutionListener listener;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.listener = new WithSecurityContextTestExecutionListener();\n\t\tthis.context = new AnnotationConfigApplicationContext(Config.class);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic void beforeTestMethodNullSecurityContextNoError() throws Exception {\n\t\tClass testClass = FakeTest.class;\n\t\tgiven(this.testContext.getTestClass()).willReturn(testClass);\n\t\tgiven(this.testContext.getTestMethod()).willReturn(ReflectionUtils.findMethod(testClass, \"testNoAnnotation\"));\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic void beforeTestMethodNoApplicationContext() throws Exception {\n\t\tClass testClass = FakeTest.class;\n\t\tgiven(this.testContext.getApplicationContext()).willThrow(new IllegalStateException());\n\t\tgiven(this.testContext.getTestMethod()).willReturn(ReflectionUtils.findMethod(testClass, \"testWithMockUser\"));\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic void beforeTestMethodInnerClass() throws Exception {\n\t\tClass testClass = OuterClass.InnerClass.class;\n\t\tMethod testNoAnnotation = ReflectionUtils.findMethod(testClass, \"testNoAnnotation\");\n\t\tgiven(this.testContext.getTestClass()).willReturn(testClass);\n\t\tgiven(this.testContext.getTestMethod()).willReturn(testNoAnnotation);\n\t\tgiven(this.testContext.getApplicationContext()).willThrow(new IllegalStateException(\"\"));\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic void beforeTestMethodInnerInnerClass() throws Exception {\n\t\tClass testClass = OuterClass.InnerClass.InnerInnerClass.class;\n\t\tMethod testNoAnnotation = ReflectionUtils.findMethod(testClass, \"testNoAnnotation\");\n\t\tgiven(this.testContext.getTestClass()).willReturn(testClass);\n\t\tgiven(this.testContext.getTestMethod()).willReturn(testNoAnnotation);\n\t\tgiven(this.testContext.getApplicationContext()).willThrow(new IllegalStateException(\"\"));\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tpublic void beforeTestMethodInnerClassWhenOverride() throws Exception {\n\t\tClass testClass = OverrideOuterClass.InnerClass.class;\n\t\tMethod testNoAnnotation = ReflectionUtils.findMethod(testClass, \"testNoAnnotation\");\n\t\tgiven(this.testContext.getTestClass()).willReturn(testClass);\n\t\tgiven(this.testContext.getTestMethod()).willReturn(testNoAnnotation);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t// gh-3962\n\t@Test\n\tpublic void withSecurityContextAfterSqlScripts() {\n\t\tSqlScriptsTestExecutionListener sql = new SqlScriptsTestExecutionListener();\n\t\tWithSecurityContextTestExecutionListener security = new WithSecurityContextTestExecutionListener();\n\t\tList<TestExecutionListener> listeners = Arrays.asList(security, sql);\n\t\tAnnotationAwareOrderComparator.sort(listeners);\n\t\tassertThat(listeners).containsExactly(sql, security);\n\t}\n\n\t// SEC-2709\n\t@Test\n\tpublic void orderOverridden() {\n\t\tAbstractTestExecutionListener otherListener = new AbstractTestExecutionListener() {\n\t\t};\n\t\tList<TestExecutionListener> listeners = new ArrayList<>();\n\t\tlisteners.add(otherListener);\n\t\tlisteners.add(this.listener);\n\t\tAnnotationAwareOrderComparator.sort(listeners);\n\t\tassertThat(listeners).containsSequence(this.listener, otherListener);\n\t}\n\n\t@Test\n\t// gh-3837\n\tpublic void handlesGenericAnnotation() throws Exception {\n\t\tMethod method = ReflectionUtils.findMethod(WithSecurityContextTestExcecutionListenerTests.class,\n\t\t\t\t\"handlesGenericAnnotationTestMethod\");\n\t\tTestContext testContext = mock(TestContext.class);\n\t\tgiven(testContext.getTestMethod()).willReturn(method);\n\t\tgiven(testContext.getApplicationContext()).willThrow(new IllegalStateException(\"\"));\n\t\tthis.listener.beforeTestMethod(testContext);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal())\n\t\t\t.isInstanceOf(WithSuperClassWithSecurityContext.class);\n\t}\n\n\t@WithSuperClassWithSecurityContext\n\tpublic void handlesGenericAnnotationTestMethod() {\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@WithSecurityContext(factory = SuperClassWithSecurityContextFactory.class)\n\t@interface WithSuperClassWithSecurityContext {\n\n\t\tString username() default \"WithSuperClassWithSecurityContext\";\n\n\t}\n\n\tstatic class SuperClassWithSecurityContextFactory implements WithSecurityContextFactory<Annotation> {\n\n\t\t@Override\n\t\tpublic SecurityContext createSecurityContext(Annotation annotation) {\n\t\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\t\tcontext.setAuthentication(new TestingAuthenticationToken(annotation, \"NA\"));\n\t\t\treturn context;\n\t\t}\n\n\t}\n\n\tstatic class FakeTest {\n\n\t\tvoid testNoAnnotation() {\n\t\t}\n\n\t\t@WithMockUser\n\t\tvoid testWithMockUser() {\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t}\n\n\t@WithMockUser\n\tclass OuterClass {\n\n\t\tclass InnerClass {\n\n\t\t\tvoid testNoAnnotation() {\n\t\t\t}\n\n\t\t\tclass InnerInnerClass {\n\n\t\t\t\tvoid testNoAnnotation() {\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@WithMockUser\n\t@NestedTestConfiguration(NestedTestConfiguration.EnclosingConfiguration.OVERRIDE)\n\tclass OverrideOuterClass {\n\n\t\tclass InnerClass {\n\n\t\t\tvoid testNoAnnotation() {\n\t\t\t}\n\n\t\t\tclass InnerInnerClass {\n\n\t\t\t\tvoid testNoAnnotation() {\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/support/WithSecurityContextTestExecutionListenerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport java.lang.reflect.Method;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.ArgumentMatchers;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.TestContext;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith({ MockitoExtension.class, SpringExtension.class })\n@ContextConfiguration(classes = WithSecurityContextTestExecutionListenerTests.NoOpConfiguration.class)\npublic class WithSecurityContextTestExecutionListenerTests {\n\n\t@Autowired\n\tprivate ApplicationContext applicationContext;\n\n\tprivate TestContext testContext;\n\n\tprivate WithSecurityContextTestExecutionListener listener = new WithSecurityContextTestExecutionListener();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.testContext = mock(TestContext.class);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenWithMockUserTestExecutionDefaultThenSecurityContextSet() throws Exception {\n\t\tMethod testMethod = TheTest.class.getMethod(\"withMockUserDefault\");\n\t\tgiven(this.testContext.getApplicationContext()).willReturn(this.applicationContext);\n\t\tgiven(this.testContext.getTestMethod()).willReturn(testMethod);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tverify(this.testContext, never()).setAttribute(\n\t\t\t\teq(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME), any(SecurityContext.class));\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenWithMockUserTestMethodThenSecurityContextSet() throws Exception {\n\t\tMethod testMethod = TheTest.class.getMethod(\"withMockUserTestMethod\");\n\t\tgiven(this.testContext.getApplicationContext()).willReturn(this.applicationContext);\n\t\tgiven(this.testContext.getTestMethod()).willReturn(testMethod);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tverify(this.testContext, never()).setAttribute(\n\t\t\t\teq(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME), any(SecurityContext.class));\n\t}\n\n\t@Test\n\tpublic void beforeTestMethodWhenWithMockUserTestExecutionThenTestContextSet() throws Exception {\n\t\tMethod testMethod = TheTest.class.getMethod(\"withMockUserTestExecution\");\n\t\tgiven(this.testContext.getApplicationContext()).willReturn(this.applicationContext);\n\t\tgiven(this.testContext.getTestMethod()).willReturn(testMethod);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tverify(this.testContext).setAttribute(eq(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME),\n\t\t\t\tArgumentMatchers.<Supplier<SecurityContext>>any());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void beforeTestMethodWhenWithMockUserTestExecutionThenTestContextSupplierOk() throws Exception {\n\t\tMethod testMethod = TheTest.class.getMethod(\"withMockUserTestExecution\");\n\t\tgiven(this.testContext.getApplicationContext()).willReturn(this.applicationContext);\n\t\tgiven(this.testContext.getTestMethod()).willReturn(testMethod);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\tArgumentCaptor<Supplier<SecurityContext>> supplierCaptor = ArgumentCaptor.forClass(Supplier.class);\n\t\tverify(this.testContext).setAttribute(eq(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME),\n\t\t\t\tsupplierCaptor.capture());\n\t\tassertThat(supplierCaptor.getValue().get().getAuthentication()).isNotNull();\n\t}\n\n\t@Test\n\t// gh-6591\n\tpublic void beforeTestMethodWhenTestExecutionThenDelayFactoryCreate() throws Exception {\n\t\tMethod testMethod = TheTest.class.getMethod(\"withUserDetails\");\n\t\tgiven(this.testContext.getApplicationContext()).willReturn(this.applicationContext);\n\t\t// do not set a UserDetailsService Bean so it would fail if looked up\n\t\tgiven(this.testContext.getTestMethod()).willReturn(testMethod);\n\t\tthis.listener.beforeTestMethod(this.testContext);\n\t\t// bean lookup of UserDetailsService would fail if it has already been looked up\n\t}\n\n\t@Test\n\tpublic void beforeTestExecutionWhenTestContextNullThenSecurityContextNotSet() {\n\t\tthis.listener.beforeTestExecution(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void beforeTestExecutionWhenTestContextNotNullThenSecurityContextSet() {\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"passsword\", \"ROLE_USER\"));\n\t\tSupplier<SecurityContext> supplier = () -> securityContext;\n\t\tgiven(this.testContext.removeAttribute(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME))\n\t\t\t.willReturn(supplier);\n\t\tthis.listener.beforeTestExecution(this.testContext);\n\t\tassertThat(TestSecurityContextHolder.getContext().getAuthentication())\n\t\t\t.isEqualTo(securityContext.getAuthentication());\n\t}\n\n\t@Configuration\n\tstatic class NoOpConfiguration {\n\n\t}\n\n\tstatic class TheTest {\n\n\t\t@WithMockUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n\t\tpublic void withMockUserTestExecution() {\n\t\t}\n\n\t\t@WithMockUser(setupBefore = TestExecutionEvent.TEST_METHOD)\n\t\tpublic void withMockUserTestMethod() {\n\t\t}\n\n\t\t@WithMockUser\n\t\tpublic void withMockUserDefault() {\n\t\t}\n\n\t\t@WithUserDetails(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n\t\tpublic void withUserDetails() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanNotOfRequiredTypeException;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class WithUserDetailsSecurityContextFactoryTests {\n\n\t@Mock\n\tprivate ReactiveUserDetailsService reactiveUserDetailsService;\n\n\t@Mock\n\tprivate UserDetailsService userDetailsService;\n\n\t@Mock\n\tprivate UserDetails userDetails;\n\n\t@Mock\n\tprivate BeanFactory beans;\n\n\t@Mock\n\tprivate WithUserDetails withUserDetails;\n\n\tprivate WithUserDetailsSecurityContextFactory factory;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.factory = new WithUserDetailsSecurityContextFactory(this.beans);\n\t}\n\n\t@Test\n\tpublic void createSecurityContextNullValue() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.factory.createSecurityContext(this.withUserDetails));\n\t}\n\n\t@Test\n\tpublic void createSecurityContextEmptyValue() {\n\t\tgiven(this.withUserDetails.value()).willReturn(\"\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.factory.createSecurityContext(this.withUserDetails));\n\t}\n\n\t@Test\n\tpublic void createSecurityContextWithExistingUser() {\n\t\tString username = \"user\";\n\t\tgiven(this.beans.getBean(ReactiveUserDetailsService.class)).willThrow(new NoSuchBeanDefinitionException(\"\"));\n\t\tgiven(this.beans.getBean(UserDetailsService.class)).willReturn(this.userDetailsService);\n\t\tgiven(this.withUserDetails.value()).willReturn(username);\n\t\tgiven(this.userDetailsService.loadUserByUsername(username)).willReturn(this.userDetails);\n\t\tSecurityContext context = this.factory.createSecurityContext(this.withUserDetails);\n\t\tassertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getPrincipal()).isEqualTo(this.userDetails);\n\t\tverify(this.beans).getBean(UserDetailsService.class);\n\t}\n\n\t// gh-3346\n\t@Test\n\tpublic void createSecurityContextWithUserDetailsServiceName() {\n\t\tString beanName = \"secondUserDetailsServiceBean\";\n\t\tString username = \"user\";\n\t\tgiven(this.beans.getBean(beanName, ReactiveUserDetailsService.class)).willThrow(\n\t\t\t\tnew BeanNotOfRequiredTypeException(\"\", ReactiveUserDetailsService.class, UserDetailsService.class));\n\t\tgiven(this.withUserDetails.value()).willReturn(username);\n\t\tgiven(this.withUserDetails.userDetailsServiceBeanName()).willReturn(beanName);\n\t\tgiven(this.userDetailsService.loadUserByUsername(username)).willReturn(this.userDetails);\n\t\tgiven(this.beans.getBean(beanName, UserDetailsService.class)).willReturn(this.userDetailsService);\n\t\tSecurityContext context = this.factory.createSecurityContext(this.withUserDetails);\n\t\tassertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getPrincipal()).isEqualTo(this.userDetails);\n\t\tverify(this.beans).getBean(beanName, UserDetailsService.class);\n\t}\n\n\t@Test\n\tpublic void createSecurityContextWithReactiveUserDetailsService() {\n\t\tString username = \"user\";\n\t\tgiven(this.withUserDetails.value()).willReturn(username);\n\t\tgiven(this.beans.getBean(ReactiveUserDetailsService.class)).willReturn(this.reactiveUserDetailsService);\n\t\tgiven(this.reactiveUserDetailsService.findByUsername(username)).willReturn(Mono.just(this.userDetails));\n\t\tSecurityContext context = this.factory.createSecurityContext(this.withUserDetails);\n\t\tassertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getPrincipal()).isEqualTo(this.userDetails);\n\t\tverify(this.beans).getBean(ReactiveUserDetailsService.class);\n\t}\n\n\t@Test\n\tpublic void createSecurityContextWithReactiveUserDetailsServiceAndBeanName() {\n\t\tString beanName = \"secondUserDetailsServiceBean\";\n\t\tString username = \"user\";\n\t\tgiven(this.withUserDetails.value()).willReturn(username);\n\t\tgiven(this.withUserDetails.userDetailsServiceBeanName()).willReturn(beanName);\n\t\tgiven(this.beans.getBean(beanName, ReactiveUserDetailsService.class))\n\t\t\t.willReturn(this.reactiveUserDetailsService);\n\t\tgiven(this.reactiveUserDetailsService.findByUsername(username)).willReturn(Mono.just(this.userDetails));\n\t\tSecurityContext context = this.factory.createSecurityContext(this.withUserDetails);\n\t\tassertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getPrincipal()).isEqualTo(this.userDetails);\n\t\tverify(this.beans).getBean(beanName, ReactiveUserDetailsService.class);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.context.support;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.core.annotation.AnnotationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class WithUserDetailsTests {\n\n\t@Test\n\tpublic void defaults() {\n\t\tWithUserDetails userDetails = AnnotationUtils.findAnnotation(Annotated.class, WithUserDetails.class);\n\t\tassertThat(userDetails.value()).isEqualTo(\"user\");\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(Annotated.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_METHOD);\n\t}\n\n\t@Test\n\tpublic void findMergedAnnotationWhenSetupExplicitThenOverridden() {\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(SetupExplicit.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_METHOD);\n\t}\n\n\t@Test\n\tpublic void findMergedAnnotationWhenSetupOverriddenThenOverridden() {\n\t\tWithSecurityContext context = AnnotatedElementUtils.findMergedAnnotation(SetupOverridden.class,\n\t\t\t\tWithSecurityContext.class);\n\t\tassertThat(context.setupBefore()).isEqualTo(TestExecutionEvent.TEST_EXECUTION);\n\t}\n\n\t@WithUserDetails\n\tprivate static class Annotated {\n\n\t}\n\n\t@WithUserDetails(setupBefore = TestExecutionEvent.TEST_METHOD)\n\tprivate class SetupExplicit {\n\n\t}\n\n\t@WithUserDetails(setupBefore = TestExecutionEvent.TEST_EXECUTION)\n\tprivate class SetupOverridden {\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/AbstractMockServerConfigurersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.security.Principal;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n * @since 5.0\n */\nabstract class AbstractMockServerConfigurersTests {\n\n\tprotected PrincipalController controller = new PrincipalController();\n\n\tprotected SecurityContextController securityContextController = new SecurityContextController();\n\n\tprotected User.UserBuilder userBuilder = User.withUsername(\"user\").password(\"password\").roles(\"USER\");\n\n\tprotected void assertPrincipalCreatedFromUserDetails(Principal principal, UserDetails originalUserDetails) {\n\t\tassertThat(principal).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tUsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) principal;\n\t\tassertThat(authentication.getCredentials()).isEqualTo(originalUserDetails.getPassword());\n\t\tassertThat(authentication.getAuthorities()).containsOnlyElementsOf(originalUserDetails.getAuthorities());\n\t\tUserDetails userDetails = (UserDetails) authentication.getPrincipal();\n\t\tassertThat(userDetails.getPassword()).isEqualTo(authentication.getCredentials());\n\t\tassertThat(authentication.getAuthorities()).containsOnlyElementsOf(userDetails.getAuthorities());\n\t}\n\n\t@RestController\n\tprotected static class PrincipalController {\n\n\t\tvolatile Principal principal;\n\n\t\t@RequestMapping(\"/**\")\n\t\tpublic Principal get(Principal principal) {\n\t\t\tthis.principal = principal;\n\t\t\treturn principal;\n\t\t}\n\n\t\tpublic Principal removePrincipal() {\n\t\t\tPrincipal result = this.principal;\n\t\t\tthis.principal = null;\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic void assertPrincipalIsEqualTo(Principal expected) {\n\t\t\tassertThat(this.principal).isEqualTo(expected);\n\t\t\tthis.principal = null;\n\t\t}\n\n\t}\n\n\t@RestController\n\tprotected static class SecurityContextController {\n\n\t\tvolatile SecurityContext securityContext;\n\n\t\t@RequestMapping(\"/**\")\n\t\tpublic SecurityContext get(@CurrentSecurityContext SecurityContext securityContext) {\n\t\t\tthis.securityContext = securityContext;\n\t\t\treturn securityContext;\n\t\t}\n\n\t\tpublic SecurityContext removeSecurityContext() {\n\t\t\tSecurityContext result = this.securityContext;\n\t\t\tthis.securityContext = null;\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurerOpaqueTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;\nimport org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;\nimport org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;\nimport org.springframework.security.web.reactive.result.method.annotation.CurrentSecurityContextArgumentResolver;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Josh Cummings\n * @since 5.3\n */\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockServerConfigurerOpaqueTokenTests extends AbstractMockServerConfigurersTests {\n\n\tprivate GrantedAuthority authority1 = new SimpleGrantedAuthority(\"one\");\n\n\tprivate GrantedAuthority authority2 = new SimpleGrantedAuthority(\"two\");\n\n\tprivate WebTestClient client = WebTestClient.bindToController(this.securityContextController)\n\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t.argumentResolvers((resolvers) -> resolvers\n\t\t\t.addCustomResolver(new CurrentSecurityContextArgumentResolver(new ReactiveAdapterRegistry())))\n\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t.configureClient()\n\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t.build();\n\n\t@Test\n\tpublic void mockOpaqueTokenWhenUsingDefaultsThenBearerTokenAuthentication() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken()).get().exchange().expectStatus().isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(BearerTokenAuthentication.class);\n\t\tBearerTokenAuthentication token = (BearerTokenAuthentication) context.getAuthentication();\n\t\tassertThat(token.getAuthorities()).isNotEmpty();\n\t\tassertThat(token.getToken()).isNotNull();\n\t\tassertThat(token.getTokenAttributes()).containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, \"user\");\n\t}\n\n\t@Test\n\tpublic void mockOpaqueTokenWhenAuthoritiesThenBearerTokenAuthentication() {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken().authorities(this.authority1, this.authority2))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities()).containsOnly(this.authority1,\n\t\t\t\tthis.authority2);\n\t}\n\n\t@Test\n\tpublic void mockOpaqueTokenWhenAttributesThenBearerTokenAuthentication() {\n\t\tString sub = new String(\"my-subject\");\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken()\n\t\t\t\t.attributes((attributes) -> attributes.put(OAuth2TokenIntrospectionClaimNames.SUB, sub)))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(BearerTokenAuthentication.class);\n\t\tBearerTokenAuthentication token = (BearerTokenAuthentication) context.getAuthentication();\n\t\tassertThat(token.getTokenAttributes().get(OAuth2TokenIntrospectionClaimNames.SUB)).isSameAs(sub);\n\t}\n\n\t@Test\n\tpublic void mockOpaqueTokenWhenPrincipalThenBearerTokenAuthentication() {\n\t\tOAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals.active();\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken().principal(principal))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(BearerTokenAuthentication.class);\n\t\tBearerTokenAuthentication token = (BearerTokenAuthentication) context.getAuthentication();\n\t\tassertThat(token.getPrincipal()).isSameAs(principal);\n\t}\n\n\t@Test\n\tpublic void mockOpaqueTokenWhenPrincipalSpecifiedThenLastCalledTakesPrecedence() {\n\t\tOAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals\n\t\t\t.active((a) -> a.put(\"scope\", \"user\"));\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken()\n\t\t\t\t.attributes((a) -> a.put(OAuth2TokenIntrospectionClaimNames.SUB, \"foo\"))\n\t\t\t\t.principal(principal))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(BearerTokenAuthentication.class);\n\t\tBearerTokenAuthentication token = (BearerTokenAuthentication) context.getAuthentication();\n\t\tassertThat((String) ((OAuth2AuthenticatedPrincipal) token.getPrincipal())\n\t\t\t.getAttribute(OAuth2TokenIntrospectionClaimNames.SUB))\n\t\t\t.isEqualTo(principal.getAttribute(OAuth2TokenIntrospectionClaimNames.SUB));\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOpaqueToken()\n\t\t\t\t.principal(principal)\n\t\t\t\t.attributes((a) -> a.put(OAuth2TokenIntrospectionClaimNames.SUB, \"bar\")))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tcontext = this.securityContextController.removeSecurityContext();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(BearerTokenAuthentication.class);\n\t\ttoken = (BearerTokenAuthentication) context.getAuthentication();\n\t\tassertThat((String) ((OAuth2AuthenticatedPrincipal) token.getPrincipal())\n\t\t\t.getAttribute(OAuth2TokenIntrospectionClaimNames.SUB)).isEqualTo(\"bar\");\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersAnnotatedTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.util.concurrent.ForkJoinPool;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(SpringExtension.class)\n@SecurityTestExecutionListeners\npublic class SecurityMockServerConfigurersAnnotatedTests extends AbstractMockServerConfigurersTests {\n\n\tWebTestClient client = WebTestClient.bindToController(this.controller)\n\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t.configureClient()\n\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t.build();\n\n\t@Test\n\t@WithMockUser\n\tpublic void withMockUserWhenOnMethodThenSuccess() {\n\t\tthis.client.get().exchange().expectStatus().isOk();\n\t\tAuthentication authentication = TestSecurityContextHolder.getContext().getAuthentication();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void withMockUserWhenGlobalMockPrincipalThenOverridesAnnotation() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"authentication\", \"secret\",\n\t\t\t\t\"ROLE_USER\");\n\t\tthis.client = WebTestClient.bindToController(this.controller)\n\t\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t\t.apply(SecurityMockServerConfigurers.mockAuthentication(authentication))\n\t\t\t.configureClient()\n\t\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.build();\n\t\tthis.client.get().exchange().expectStatus().isOk();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void withMockUserWhenMutateWithMockPrincipalThenOverridesAnnotation() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"authentication\", \"secret\",\n\t\t\t\t\"ROLE_USER\");\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockAuthentication(authentication))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void withMockUserWhenMutateWithMockPrincipalAndNoMutateThenOverridesAnnotationAndUsesAnnotation() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"authentication\", \"secret\",\n\t\t\t\t\"ROLE_USER\");\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockAuthentication(authentication))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t\tthis.client.get().exchange().expectStatus().isOk();\n\t\tassertPrincipalCreatedFromUserDetails(this.controller.removePrincipal(), this.userBuilder.build());\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void withMockUserWhenOnMethodAndRequestIsExecutedOnDifferentThreadThenSuccess() {\n\t\tAuthentication authentication = TestSecurityContextHolder.getContext().getAuthentication();\n\t\tForkJoinPool.commonPool().submit(() -> this.client.get().exchange().expectStatus().isOk()).join();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void withMockUserAndWithCallOnSeparateThreadWhenMutateWithMockPrincipalAndNoMutateThenOverridesAnnotationAndUsesAnnotation() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"authentication\", \"secret\",\n\t\t\t\t\"ROLE_USER\");\n\t\tForkJoinPool.commonPool()\n\t\t\t.submit(() -> this.client.mutateWith(SecurityMockServerConfigurers.mockAuthentication(authentication))\n\t\t\t\t.get()\n\t\t\t\t.exchange()\n\t\t\t\t.expectStatus()\n\t\t\t\t.isOk())\n\t\t\t.join();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t\tForkJoinPool.commonPool().submit(() -> this.client.get().exchange().expectStatus().isOk()).join();\n\t\tassertPrincipalCreatedFromUserDetails(this.controller.removePrincipal(), this.userBuilder.build());\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersClassAnnotatedTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.security.Principal;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@WithMockUser\n@ExtendWith(SpringExtension.class)\n@SecurityTestExecutionListeners\npublic class SecurityMockServerConfigurersClassAnnotatedTests extends AbstractMockServerConfigurersTests {\n\n\tWebTestClient client = WebTestClient.bindToController(this.controller)\n\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t.configureClient()\n\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t.build();\n\n\t@Test\n\tpublic void wheMockUserWhenClassAnnotatedThenSuccess() {\n\t\tthis.client.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((response) -> assertThat(response.getResponseBody()).contains(\"\\\"username\\\":\\\"user\\\"\"));\n\t\tAuthentication authentication = TestSecurityContextHolder.getContext().getAuthentication();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t}\n\n\t@Test\n\t@WithMockUser(\"method-user\")\n\tpublic void withMockUserWhenClassAndMethodAnnotationThenMethodOverrides() {\n\t\tthis.client.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((response) -> assertThat(response.getResponseBody()).contains(\"\\\"username\\\":\\\"method-user\\\"\"));\n\t\tAuthentication authentication = TestSecurityContextHolder.getContext().getAuthentication();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void withMockUserWhenMutateWithThenMustateWithOverrides() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockUser(\"mutateWith-mockUser\"))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((response) -> assertThat(response.getResponseBody())\n\t\t\t\t.contains(\"\\\"username\\\":\\\"mutateWith-mockUser\\\"\"));\n\t\tPrincipal principal = this.controller.removePrincipal();\n\t\tassertPrincipalCreatedFromUserDetails(principal, this.userBuilder.username(\"mutateWith-mockUser\").build());\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersJwtTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.web.reactive.result.method.annotation.CurrentSecurityContextArgumentResolver;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jérôme Wacongne &lt;ch4mp&#64;c4-soft.com&gt;\n * @author Josh Cummings\n * @since 5.2\n */\npublic class SecurityMockServerConfigurersJwtTests extends AbstractMockServerConfigurersTests {\n\n\tGrantedAuthority authority1 = new SimpleGrantedAuthority(\"AUTHORITY1\");\n\n\tGrantedAuthority authority2 = new SimpleGrantedAuthority(\"AUTHORITY2\");\n\n\tWebTestClient client = WebTestClient.bindToController(this.securityContextController)\n\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t.argumentResolvers((resolvers) -> resolvers\n\t\t\t.addCustomResolver(new CurrentSecurityContextArgumentResolver(new ReactiveAdapterRegistry())))\n\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t.configureClient()\n\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t.build();\n\n\t@Test\n\tpublic void mockJwtWhenUsingDefaultsTheCreatesJwtAuthentication() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockJwt()).get().exchange().expectStatus().isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(JwtAuthenticationToken.class);\n\t\tJwtAuthenticationToken token = (JwtAuthenticationToken) context.getAuthentication();\n\t\tassertThat(token.getAuthorities()).isNotEmpty();\n\t\tassertThat(token.getToken()).isNotNull();\n\t\tassertThat(token.getToken().getSubject()).isEqualTo(\"user\");\n\t\tassertThat(token.getToken().getHeaders()).containsEntry(\"alg\", \"none\");\n\t}\n\n\t@Test\n\tpublic void mockJwtWhenProvidingBuilderConsumerThenProducesJwtAuthentication() {\n\t\tString name = new String(\"user\");\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockJwt().jwt((jwt) -> jwt.subject(name)))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(JwtAuthenticationToken.class);\n\t\tJwtAuthenticationToken token = (JwtAuthenticationToken) context.getAuthentication();\n\t\tassertThat(token.getToken().getSubject()).isSameAs(name);\n\t}\n\n\t@Test\n\tpublic void mockJwtWhenProvidingCustomAuthoritiesThenProducesJwtAuthentication() {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockJwt()\n\t\t\t\t.jwt((jwt) -> jwt.claim(\"scope\", \"ignored authorities\"))\n\t\t\t\t.authorities(this.authority1, this.authority2))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities()).containsOnly(this.authority1,\n\t\t\t\tthis.authority2);\n\t}\n\n\t@Test\n\tpublic void mockJwtWhenProvidingScopedAuthoritiesThenProducesJwtAuthentication() {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockJwt().jwt((jwt) -> jwt.claim(\"scope\", \"scoped authorities\")))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities())\n\t\t\t.containsOnly(new SimpleGrantedAuthority(\"SCOPE_scoped\"), new SimpleGrantedAuthority(\"SCOPE_authorities\"));\n\t}\n\n\t@Test\n\tpublic void mockJwtWhenProvidingGrantedAuthoritiesThenProducesJwtAuthentication() {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockJwt()\n\t\t\t\t.jwt((jwt) -> jwt.claim(\"scope\", \"ignored authorities\"))\n\t\t\t\t.authorities((jwt) -> Arrays.asList(this.authority1)))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities()).containsOnly(this.authority1);\n\t}\n\n\t@Test\n\tpublic void mockJwtWhenProvidingPreparedJwtThenProducesJwtAuthentication() {\n\t\tJwt originalToken = TestJwts.jwt().header(\"header1\", \"value1\").subject(\"some_user\").build();\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockJwt().jwt(originalToken))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tSecurityContext context = this.securityContextController.removeSecurityContext();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(JwtAuthenticationToken.class);\n\t\tJwtAuthenticationToken retrievedToken = (JwtAuthenticationToken) context.getAuthentication();\n\t\tassertThat(retrievedToken.getToken().getSubject()).isEqualTo(\"some_user\");\n\t\tassertThat(retrievedToken.getToken().getTokenValue()).isEqualTo(\"token\");\n\t\tassertThat(retrievedToken.getToken().getHeaders()).containsEntry(\"header1\", \"value1\");\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersOAuth2ClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.reactive.result.method.annotation.OAuth2AuthorizedClientArgumentResolver;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.OAuth2ClientMutator.TestOAuth2AuthorizedClientRepository;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.DispatcherHandler;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.adapter.WebHttpHandlerBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockServerConfigurersOAuth2ClientTests extends AbstractMockServerConfigurersTests {\n\n\tprivate OAuth2LoginController controller = new OAuth2LoginController();\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Mock\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate ReactiveOAuth2AuthorizedClientManager authorizedClientManager;\n\n\tprivate WebTestClient client;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(\n\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository);\n\t\tthis.client = WebTestClient.bindToController(this.controller)\n\t\t\t.argumentResolvers((c) -> c\n\t\t\t\t.addCustomResolver(new OAuth2AuthorizedClientArgumentResolver(this.authorizedClientManager)))\n\t\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t\t.configureClient()\n\t\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.build();\n\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenUsingDefaultsThenException() throws Exception {\n\t\tWebHttpHandlerBuilder builder = WebHttpHandlerBuilder.webHandler(new DispatcherHandler());\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> SecurityMockServerConfigurers.mockOAuth2Client().beforeServerCreated(builder))\n\t\t\t.withMessageContaining(\"ClientRegistration\");\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenUsingRegistrationIdThenProducesAuthorizedClient() throws Exception {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Client(\"registration-id\"))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client).isNotNull();\n\t\tassertThat(client.getClientRegistration().getRegistrationId()).isEqualTo(\"registration-id\");\n\t\tassertThat(client.getAccessToken().getTokenValue()).isEqualTo(\"access-token\");\n\t\tassertThat(client.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenClientRegistrationThenUses() throws Exception {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.registrationId(\"registration-id\")\n\t\t\t.clientId(\"client-id\")\n\t\t\t.build();\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Client().clientRegistration(clientRegistration))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client).isNotNull();\n\t\tassertThat(client.getClientRegistration().getRegistrationId()).isEqualTo(\"registration-id\");\n\t\tassertThat(client.getAccessToken().getTokenValue()).isEqualTo(\"access-token\");\n\t\tassertThat(client.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenClientRegistrationConsumerThenUses() throws Exception {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOAuth2Client(\"registration-id\")\n\t\t\t\t.clientRegistration((c) -> c.clientId(\"client-id\")))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client).isNotNull();\n\t\tassertThat(client.getClientRegistration().getRegistrationId()).isEqualTo(\"registration-id\");\n\t\tassertThat(client.getClientRegistration().getClientId()).isEqualTo(\"client-id\");\n\t\tassertThat(client.getAccessToken().getTokenValue()).isEqualTo(\"access-token\");\n\t\tassertThat(client.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenPrincipalNameThenUses() throws Exception {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOAuth2Client(\"registration-id\").principalName(\"test-subject\"))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.isEqualTo(\"test-subject\");\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenAccessTokenThenUses() throws Exception {\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOAuth2Client(\"registration-id\").accessToken(accessToken))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client).isNotNull();\n\t\tassertThat(client.getClientRegistration().getRegistrationId()).isEqualTo(\"registration-id\");\n\t\tassertThat(client.getAccessToken().getTokenValue()).isEqualTo(\"no-scopes\");\n\t\tassertThat(client.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenUsedOnceThenDoesNotAffectRemainingTests() throws Exception {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Client(\"registration-id\"))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client).isNotNull();\n\t\tassertThat(client.getClientRegistration().getClientId()).isEqualTo(\"test-client\");\n\t\tclient = new OAuth2AuthorizedClient(TestClientRegistrations.clientRegistration().build(), \"sub\",\n\t\t\t\tTestOAuth2AccessTokens.noScopes());\n\t\tgiven(this.authorizedClientRepository.loadAuthorizedClient(eq(\"registration-id\"), any(Authentication.class),\n\t\t\t\tany(ServerWebExchange.class)))\n\t\t\t.willReturn(Mono.just(client));\n\t\tthis.client.get().uri(\"/client\").exchange().expectStatus().isOk();\n\t\tclient = this.controller.authorizedClient;\n\t\tassertThat(client).isNotNull();\n\t\tassertThat(client.getClientRegistration().getClientId()).isEqualTo(\"client-id\");\n\t\tverify(this.authorizedClientRepository).loadAuthorizedClient(eq(\"registration-id\"), any(Authentication.class),\n\t\t\t\tany(ServerWebExchange.class));\n\t}\n\n\t// gh-13113\n\t@Test\n\tpublic void oauth2ClientWhenUsedThenSetsClientToRepository() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Client(\"registration-id\"))\n\t\t\t.mutateWith((clientBuilder, httpBuilder, connector) -> httpBuilder\n\t\t\t\t.filters((filters) -> filters.add((exchange, chain) -> {\n\t\t\t\t\tServerOAuth2AuthorizedClientRepository repository = (ServerOAuth2AuthorizedClientRepository) ReflectionTestUtils\n\t\t\t\t\t\t.getField(this.authorizedClientManager, \"authorizedClientRepository\");\n\t\t\t\t\tassertThat(repository).isInstanceOf(TestOAuth2AuthorizedClientRepository.class);\n\t\t\t\t\treturn repository.loadAuthorizedClient(\"registration-id\", null, exchange)\n\t\t\t\t\t\t.switchIfEmpty(Mono.error(new AssertionError(\"no authorized client found\")))\n\t\t\t\t\t\t.then(chain.filter(exchange));\n\t\t\t\t})))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t}\n\n\t@RestController\n\tstatic class OAuth2LoginController {\n\n\t\tvolatile OAuth2AuthorizedClient authorizedClient;\n\n\t\t@GetMapping(\"/client\")\n\t\tString authorizedClient(\n\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"registration-id\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\tthis.authorizedClient = authorizedClient;\n\t\t\treturn authorizedClient.getPrincipalName();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersOAuth2LoginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.reactive.result.method.annotation.OAuth2AuthorizedClientArgumentResolver;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockServerConfigurersOAuth2LoginTests extends AbstractMockServerConfigurersTests {\n\n\tprivate OAuth2LoginController controller = new OAuth2LoginController();\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Mock\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate WebTestClient client;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.client = WebTestClient.bindToController(this.controller)\n\t\t\t.argumentResolvers((c) -> c.addCustomResolver(new OAuth2AuthorizedClientArgumentResolver(\n\t\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository)))\n\t\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t\t.configureClient()\n\t\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenUsingDefaultsThenProducesDefaultAuthentication() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login())\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getAuthorizedClientRegistrationId()).isEqualTo(\"test\");\n\t\tassertThat(token.getPrincipal()).isInstanceOf(OAuth2User.class);\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"sub\", \"user\");\n\t\tassertThat((Collection<GrantedAuthority>) token.getPrincipal().getAuthorities())\n\t\t\t.contains(new SimpleGrantedAuthority(\"SCOPE_read\"));\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenUsingDefaultsThenProducesDefaultAuthorizedClient() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login())\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client).isNotNull();\n\t\tassertThat(client.getClientRegistration().getRegistrationId()).isEqualTo(\"test\");\n\t\tassertThat(client.getAccessToken().getTokenValue()).isEqualTo(\"access-token\");\n\t\tassertThat(client.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenAuthoritiesSpecifiedThenGrantsAccess() {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login()\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"SCOPE_admin\")))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat((Collection<GrantedAuthority>) token.getPrincipal().getAuthorities())\n\t\t\t.contains(new SimpleGrantedAuthority(\"SCOPE_admin\"));\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenAttributeSpecifiedThenUserHasAttribute() {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login()\n\t\t\t\t.attributes((a) -> a.put(\"iss\", \"https://idp.example.org\")))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"iss\", \"https://idp.example.org\");\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenNameSpecifiedThenUserHasName() throws Exception {\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.commaSeparatedStringToAuthorityList(\"SCOPE_read\"),\n\t\t\t\tCollections.singletonMap(\"custom-attribute\", \"test-subject\"), \"custom-attribute\");\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login().oauth2User(oauth2User))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token.getPrincipal().getName()).isEqualTo(\"test-subject\");\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login().oauth2User(oauth2User))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client.getPrincipalName()).isEqualTo(\"test-subject\");\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenOAuth2UserSpecifiedThenLastCalledTakesPrecedence() throws Exception {\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"SCOPE_read\"),\n\t\t\t\tCollections.singletonMap(\"sub\", \"subject\"), \"sub\");\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login()\n\t\t\t\t.attributes((a) -> a.put(\"subject\", \"foo\"))\n\t\t\t\t.oauth2User(oauth2User))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"sub\", \"subject\");\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login()\n\t\t\t\t.oauth2User(oauth2User)\n\t\t\t\t.attributes((a) -> a.put(\"sub\", \"bar\")))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\ttoken = this.controller.token;\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"sub\", \"bar\");\n\t}\n\n\t@RestController\n\tstatic class OAuth2LoginController {\n\n\t\tvolatile OAuth2AuthenticationToken token;\n\n\t\tvolatile OAuth2AuthorizedClient authorizedClient;\n\n\t\t@GetMapping(\"/token\")\n\t\tOAuth2AuthenticationToken token(OAuth2AuthenticationToken token) {\n\t\t\tthis.token = token;\n\t\t\treturn token;\n\t\t}\n\n\t\t@GetMapping(\"/client\")\n\t\tString authorizedClient(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {\n\t\t\tthis.authorizedClient = authorizedClient;\n\t\t\treturn authorizedClient.getPrincipalName();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersOidcLoginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.util.Collection;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;\nimport org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.reactive.result.method.annotation.OAuth2AuthorizedClientArgumentResolver;\nimport org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockServerConfigurersOidcLoginTests extends AbstractMockServerConfigurersTests {\n\n\tprivate OAuth2LoginController controller = new OAuth2LoginController();\n\n\t@Mock\n\tprivate ReactiveClientRegistrationRepository clientRegistrationRepository;\n\n\t@Mock\n\tprivate ServerOAuth2AuthorizedClientRepository authorizedClientRepository;\n\n\tprivate WebTestClient client;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.client = WebTestClient.bindToController(this.controller)\n\t\t\t.argumentResolvers((c) -> c.addCustomResolver(new OAuth2AuthorizedClientArgumentResolver(\n\t\t\t\t\tthis.clientRegistrationRepository, this.authorizedClientRepository)))\n\t\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t\t.configureClient()\n\t\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenUsingDefaultsThenProducesDefaultAuthentication() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOidcLogin())\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getAuthorizedClientRegistrationId()).isEqualTo(\"test\");\n\t\tassertThat(token.getPrincipal()).isInstanceOf(OidcUser.class);\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"sub\", \"user\");\n\t\tassertThat((Collection<GrantedAuthority>) token.getPrincipal().getAuthorities())\n\t\t\t.contains(new SimpleGrantedAuthority(\"SCOPE_read\"));\n\t\tassertThat(((OidcUser) token.getPrincipal()).getIdToken().getTokenValue()).isEqualTo(\"id-token\");\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenUsingDefaultsThenProducesDefaultAuthorizedClient() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOidcLogin())\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client).isNotNull();\n\t\tassertThat(client.getClientRegistration().getRegistrationId()).isEqualTo(\"test\");\n\t\tassertThat(client.getAccessToken().getTokenValue()).isEqualTo(\"access-token\");\n\t\tassertThat(client.getRefreshToken()).isNull();\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenAuthoritiesSpecifiedThenGrantsAccess() {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOidcLogin()\n\t\t\t\t.authorities(new SimpleGrantedAuthority(\"SCOPE_admin\")))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat((Collection<GrantedAuthority>) token.getPrincipal().getAuthorities())\n\t\t\t.contains(new SimpleGrantedAuthority(\"SCOPE_admin\"));\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenIdTokenSpecifiedThenUserHasClaims() {\n\t\tthis.client\n\t\t\t.mutateWith(\n\t\t\t\t\tSecurityMockServerConfigurers.mockOidcLogin().idToken((i) -> i.issuer(\"https://idp.example.org\")))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"iss\", \"https://idp.example.org\");\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenUserInfoSpecifiedThenUserHasClaims() throws Exception {\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockOidcLogin().userInfoToken((u) -> u.email(\"email@email\")))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"email\", \"email@email\");\n\t}\n\n\t@Test\n\tpublic void oidcUserWhenNameSpecifiedThenUserHasName() throws Exception {\n\t\tOidcUser oidcUser = new DefaultOidcUser(AuthorityUtils.commaSeparatedStringToAuthorityList(\"SCOPE_read\"),\n\t\t\t\tOidcIdToken.withTokenValue(\"id-token\").claim(\"custom-attribute\", \"test-subject\").build(),\n\t\t\t\t\"custom-attribute\");\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login().oauth2User(oidcUser))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token.getPrincipal().getName()).isEqualTo(\"test-subject\");\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockOAuth2Login().oauth2User(oidcUser))\n\t\t\t.get()\n\t\t\t.uri(\"/client\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthorizedClient client = this.controller.authorizedClient;\n\t\tassertThat(client.getPrincipalName()).isEqualTo(\"test-subject\");\n\t}\n\n\t// gh-7794\n\t@Test\n\tpublic void oidcLoginWhenOidcUserSpecifiedThenLastCalledTakesPrecedence() throws Exception {\n\t\tOidcUser oidcUser = new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"SCOPE_read\"),\n\t\t\t\tTestOidcIdTokens.idToken().build());\n\t\tthis.client\n\t\t\t.mutateWith(\n\t\t\t\t\tSecurityMockServerConfigurers.mockOidcLogin().idToken((i) -> i.subject(\"foo\")).oidcUser(oidcUser))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tOAuth2AuthenticationToken token = this.controller.token;\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"sub\", \"subject\");\n\t\tthis.client\n\t\t\t.mutateWith(\n\t\t\t\t\tSecurityMockServerConfigurers.mockOidcLogin().oidcUser(oidcUser).idToken((i) -> i.subject(\"bar\")))\n\t\t\t.get()\n\t\t\t.uri(\"/token\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\ttoken = this.controller.token;\n\t\tassertThat(token.getPrincipal().getAttributes()).containsEntry(\"sub\", \"bar\");\n\t}\n\n\t@RestController\n\tstatic class OAuth2LoginController {\n\n\t\tvolatile OAuth2AuthenticationToken token;\n\n\t\tvolatile OAuth2AuthorizedClient authorizedClient;\n\n\t\t@GetMapping(\"/token\")\n\t\tOAuth2AuthenticationToken token(OAuth2AuthenticationToken token) {\n\t\t\tthis.token = token;\n\t\t\treturn token;\n\t\t}\n\n\t\t@GetMapping(\"/client\")\n\t\tString authorizedClient(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {\n\t\t\tthis.authorizedClient = authorizedClient;\n\t\t\treturn authorizedClient.getPrincipalName();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.security.Principal;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;\nimport org.springframework.security.web.server.csrf.CsrfWebFilter;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class SecurityMockServerConfigurersTests extends AbstractMockServerConfigurersTests {\n\n\tWebTestClient client = WebTestClient.bindToController(this.controller)\n\t\t.webFilter(new CsrfWebFilter(), new SecurityContextServerWebExchangeWebFilter())\n\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t.configureClient()\n\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t.build();\n\n\t@Test\n\tpublic void mockAuthenticationWhenLocalThenSuccess() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"authentication\", \"secret\",\n\t\t\t\t\"ROLE_USER\");\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockAuthentication(authentication))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void mockAuthenticationWhenGlobalThenSuccess() {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"authentication\", \"secret\",\n\t\t\t\t\"ROLE_USER\");\n\t\tthis.client = WebTestClient.bindToController(this.controller)\n\t\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t\t.apply(SecurityMockServerConfigurers.mockAuthentication(authentication))\n\t\t\t.configureClient()\n\t\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.build();\n\t\tthis.client.get().exchange().expectStatus().isOk();\n\t\tthis.controller.assertPrincipalIsEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void mockUserWhenDefaultsThenSuccess() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockUser()).get().exchange().expectStatus().isOk();\n\t\tPrincipal actual = this.controller.removePrincipal();\n\t\tassertPrincipalCreatedFromUserDetails(actual, this.userBuilder.build());\n\t}\n\n\t@Test\n\tpublic void mockUserWhenGlobalThenSuccess() {\n\t\tthis.client = WebTestClient.bindToController(this.controller)\n\t\t\t.webFilter(new SecurityContextServerWebExchangeWebFilter())\n\t\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t\t.apply(SecurityMockServerConfigurers.mockUser())\n\t\t\t.configureClient()\n\t\t\t.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)\n\t\t\t.build();\n\t\tthis.client.get().exchange().expectStatus().isOk();\n\t\tPrincipal actual = this.controller.removePrincipal();\n\t\tassertPrincipalCreatedFromUserDetails(actual, this.userBuilder.build());\n\t}\n\n\t@Test\n\tpublic void mockUserStringWhenLocalThenSuccess() {\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockUser(this.userBuilder.build().getUsername()))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tPrincipal actual = this.controller.removePrincipal();\n\t\tassertPrincipalCreatedFromUserDetails(actual, this.userBuilder.build());\n\t}\n\n\t@Test\n\tpublic void mockUserStringWhenCustomThenSuccess() {\n\t\tthis.userBuilder = User.withUsername(\"admin\").password(\"secret\").roles(\"USER\", \"ADMIN\");\n\t\tthis.client\n\t\t\t.mutateWith(SecurityMockServerConfigurers.mockUser(\"admin\").password(\"secret\").roles(\"USER\", \"ADMIN\"))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tPrincipal actual = this.controller.removePrincipal();\n\t\tassertPrincipalCreatedFromUserDetails(actual, this.userBuilder.build());\n\t}\n\n\t@Test\n\tpublic void mockUserUserDetailsLocalThenSuccess() {\n\t\tUserDetails userDetails = this.userBuilder.build();\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.mockUser(userDetails))\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tPrincipal actual = this.controller.removePrincipal();\n\t\tassertPrincipalCreatedFromUserDetails(actual, this.userBuilder.build());\n\t}\n\n\t@Test\n\tpublic void csrfWhenMutateWithThenDisablesCsrf() {\n\t\tthis.client.post()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isEqualTo(HttpStatus.FORBIDDEN)\n\t\t\t.expectBody()\n\t\t\t.consumeWith((b) -> assertThat(new String(b.getResponseBody())).contains(\"Access Denied\"));\n\t\tthis.client.mutateWith(SecurityMockServerConfigurers.csrf()).post().exchange().expectStatus().isOk();\n\t}\n\n\t@Test\n\tpublic void csrfWhenGlobalThenDisablesCsrf() {\n\t\tthis.client = WebTestClient.bindToController(this.controller)\n\t\t\t.webFilter(new CsrfWebFilter())\n\t\t\t.apply(SecurityMockServerConfigurers.springSecurity())\n\t\t\t.apply(SecurityMockServerConfigurers.csrf())\n\t\t\t.configureClient()\n\t\t\t.build();\n\t\tthis.client.get().exchange().expectStatus().isOk();\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/Sec2935Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class Sec2935Tests {\n\n\t@Autowired\n\tWebApplicationContext context;\n\n\tMockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t// SEC-2935\n\t@Test\n\tpublic void postProcessorUserNoUser() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin/abc\").with(user(\"user\").roles(\"ADMIN\", \"USER\")))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t\tthis.mvc.perform(get(\"/admin/abc\")).andExpect(status().isUnauthorized()).andExpect(unauthenticated());\n\t}\n\n\t@Test\n\tpublic void postProcessorUserOtherUser() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin/abc\").with(user(\"user1\").roles(\"ADMIN\", \"USER\")))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andExpect(authenticated().withUsername(\"user1\"));\n\t\tthis.mvc.perform(get(\"/admin/abc\").with(user(\"user2\").roles(\"USER\")))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withUsername(\"user2\"));\n\t}\n\n\t@WithMockUser\n\t@Test\n\tpublic void postProcessorUserWithMockUser() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin/abc\").with(user(\"user1\").roles(\"ADMIN\", \"USER\")))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andExpect(authenticated().withUsername(\"user1\"));\n\t\tthis.mvc.perform(get(\"/admin/abc\"))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t// SEC-2941\n\t@Test\n\tpublic void defaultRequest() throws Exception {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.defaultRequest(get(\"/\").with(user(\"default\")))\n\t\t\t.build();\n\t\tthis.mvc.perform(get(\"/admin/abc\").with(user(\"user1\").roles(\"ADMIN\", \"USER\")))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andExpect(authenticated().withUsername(\"user1\"));\n\t\tthis.mvc.perform(get(\"/admin/abc\"))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withUsername(\"default\"));\n\t}\n\n\t@Disabled\n\t@WithMockUser\n\t@Test\n\tpublic void defaultRequestOverridesWithMockUser() throws Exception {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.defaultRequest(get(\"/\").with(user(\"default\")))\n\t\t\t.build();\n\t\tthis.mvc.perform(get(\"/admin/abc\").with(user(\"user1\").roles(\"ADMIN\", \"USER\")))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andExpect(authenticated().withUsername(\"user1\"));\n\t\tthis.mvc.perform(get(\"/admin/abc\"))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(authenticated().withUsername(\"default\"));\n\t}\n\n\t@EnableWebSecurity\n\t@Configuration\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tauth.inMemoryAuthentication();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLoginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\n\npublic class SecurityMockMvcRequestBuildersFormLoginTests {\n\n\tprivate MockServletContext servletContext;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.servletContext = new MockServletContext();\n\t}\n\n\t@Test\n\tpublic void defaults() {\n\t\tMockHttpServletRequest request = formLogin().buildRequest(this.servletContext);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(request.getParameter(\"username\")).isEqualTo(\"user\");\n\t\tassertThat(request.getParameter(\"password\")).isEqualTo(\"password\");\n\t\tassertThat(request.getMethod()).isEqualTo(\"POST\");\n\t\tassertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken());\n\t\tassertThat(request.getRequestURI()).isEqualTo(\"/login\");\n\t\tassertThat(request.getParameter(\"_csrf\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void custom() {\n\t\tMockHttpServletRequest request = formLogin(\"/login\").user(\"username\", \"admin\")\n\t\t\t.password(\"password\", \"secret\")\n\t\t\t.buildRequest(this.servletContext);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(request.getParameter(\"username\")).isEqualTo(\"admin\");\n\t\tassertThat(request.getParameter(\"password\")).isEqualTo(\"secret\");\n\t\tassertThat(request.getMethod()).isEqualTo(\"POST\");\n\t\tassertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken());\n\t\tassertThat(request.getRequestURI()).isEqualTo(\"/login\");\n\t}\n\n\t@Test\n\tpublic void customWithUriVars() {\n\t\tMockHttpServletRequest request = formLogin().loginProcessingUrl(\"/uri-login/{var1}/{var2}\", \"val1\", \"val2\")\n\t\t\t.user(\"username\", \"admin\")\n\t\t\t.password(\"password\", \"secret\")\n\t\t\t.buildRequest(this.servletContext);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(request.getParameter(\"username\")).isEqualTo(\"admin\");\n\t\tassertThat(request.getParameter(\"password\")).isEqualTo(\"secret\");\n\t\tassertThat(request.getMethod()).isEqualTo(\"POST\");\n\t\tassertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken());\n\t\tassertThat(request.getRequestURI()).isEqualTo(\"/uri-login/val1/val2\");\n\t}\n\n\t/**\n\t * spring-restdocs uses postprocessors to do its trick. It will work only if these are\n\t * merged together with our request builders. (gh-7572)\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void postProcessorsAreMergedDuringMockMvcPerform() throws Exception {\n\t\tRequestPostProcessor postProcessor = mock(RequestPostProcessor.class);\n\t\tgiven(postProcessor.postProcessRequest(any())).willAnswer((i) -> i.getArgument(0));\n\t\tMockMvc mockMvc = MockMvcBuilders.standaloneSetup(new Object())\n\t\t\t.defaultRequest(MockMvcRequestBuilders.get(\"/\").with(postProcessor))\n\t\t\t.build();\n\t\tMvcResult mvcResult = mockMvc.perform(formLogin()).andReturn();\n\t\tassertThat(mvcResult.getRequest().getMethod()).isEqualTo(HttpMethod.POST.name());\n\t\tassertThat(mvcResult.getRequest().getHeader(\"Accept\"))\n\t\t\t.isEqualTo(MediaType.toString(Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED)));\n\t\tassertThat(mvcResult.getRequest().getParameter(\"username\")).isEqualTo(\"user\");\n\t\tassertThat(mvcResult.getRequest().getParameter(\"password\")).isEqualTo(\"password\");\n\t\tassertThat(mvcResult.getRequest().getRequestURI()).isEqualTo(\"/login\");\n\t\tassertThat(mvcResult.getRequest().getParameter(\"_csrf\")).isNotEmpty();\n\t\tverify(postProcessor).postProcessRequest(any());\n\t}\n\n\t// gh-3920\n\t@Test\n\tpublic void usesAcceptMediaForContentNegotiation() {\n\t\tMockHttpServletRequest request = formLogin(\"/login\").user(\"username\", \"admin\")\n\t\t\t.password(\"password\", \"secret\")\n\t\t\t.buildRequest(this.servletContext);\n\t\tassertThat(request.getHeader(\"Accept\")).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestBuildersFormLogoutTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.request.RequestPostProcessor;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;\n\npublic class SecurityMockMvcRequestBuildersFormLogoutTests {\n\n\tprivate MockServletContext servletContext;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.servletContext = new MockServletContext();\n\t}\n\n\t@Test\n\tpublic void defaults() {\n\t\tMockHttpServletRequest request = logout().buildRequest(this.servletContext);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(request.getMethod()).isEqualTo(\"POST\");\n\t\tassertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken());\n\t\tassertThat(request.getRequestURI()).isEqualTo(\"/logout\");\n\t}\n\n\t@Test\n\tpublic void custom() {\n\t\tMockHttpServletRequest request = logout(\"/admin/logout\").buildRequest(this.servletContext);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(request.getMethod()).isEqualTo(\"POST\");\n\t\tassertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken());\n\t\tassertThat(request.getRequestURI()).isEqualTo(\"/admin/logout\");\n\t}\n\n\t@Test\n\tpublic void customWithUriVars() {\n\t\tMockHttpServletRequest request = logout().logoutUrl(\"/uri-logout/{var1}/{var2}\", \"val1\", \"val2\")\n\t\t\t.buildRequest(this.servletContext);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(request.getMethod()).isEqualTo(\"POST\");\n\t\tassertThat(request.getParameter(token.getParameterName())).isEqualTo(token.getToken());\n\t\tassertThat(request.getRequestURI()).isEqualTo(\"/uri-logout/val1/val2\");\n\t}\n\n\t/**\n\t * spring-restdocs uses postprocessors to do its trick. It will work only if these are\n\t * merged together with our request builders. (gh-7572)\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void postProcessorsAreMergedDuringMockMvcPerform() throws Exception {\n\t\tRequestPostProcessor postProcessor = mock(RequestPostProcessor.class);\n\t\tgiven(postProcessor.postProcessRequest(any())).willAnswer((i) -> i.getArgument(0));\n\t\tMockMvc mockMvc = MockMvcBuilders.standaloneSetup(new Object())\n\t\t\t.defaultRequest(MockMvcRequestBuilders.get(\"/\").with(postProcessor))\n\t\t\t.build();\n\t\tMvcResult mvcResult = mockMvc.perform(logout()).andReturn();\n\t\tassertThat(mvcResult.getRequest().getMethod()).isEqualTo(HttpMethod.POST.name());\n\t\tassertThat(mvcResult.getRequest().getHeader(\"Accept\"))\n\t\t\t.isEqualTo(MediaType.toString(Arrays.asList(MediaType.TEXT_HTML, MediaType.ALL)));\n\t\tassertThat(mvcResult.getRequest().getRequestURI()).isEqualTo(\"/logout\");\n\t\tassertThat(mvcResult.getRequest().getParameter(\"_csrf\")).isNotEmpty();\n\t\tverify(postProcessor).postProcessRequest(any());\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationStatelessTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SecurityMockMvcRequestPostProcessorsAuthenticationStatelessTests.Config.class)\n@WebAppConfiguration\npublic class SecurityMockMvcRequestPostProcessorsAuthenticationStatelessTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t// SEC-2593\n\t@Test\n\tpublic void userRequestPostProcessorWorksWithStateless() throws Exception {\n\t\tthis.mvc.perform(get(\"/\").with(user(\"user\"))).andExpect(status().is2xxSuccessful());\n\t}\n\n\t// SEC-2593\n\t@WithMockUser\n\t@Test\n\tpublic void withMockUserWorksWithStateless() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().is2xxSuccessful());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.STATELESS));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class Controller {\n\n\t\t\t@RequestMapping(\"/\")\n\t\t\tString hello() {\n\t\t\t\treturn \"Hello\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockMvcRequestPostProcessorsAuthenticationTests {\n\n\t@Captor\n\tprivate ArgumentCaptor<SecurityContext> contextCaptor;\n\n\t@Mock\n\tprivate SecurityContextRepository repository;\n\n\tprivate MockHttpServletRequest request;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\t@Mock\n\tprivate MockedStatic<WebTestUtils> webTestUtils;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.webTestUtils.when(() -> WebTestUtils.getSecurityContextRepository(this.request))\n\t\t\t.thenReturn(this.repository);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void userDetails() {\n\t\tauthentication(this.authentication).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat(context.getAuthentication()).isSameAs(this.authentication);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsCertificateTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.security.cert.X509Certificate;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockMvcRequestPostProcessorsCertificateTests {\n\n\t@Mock\n\tprivate X509Certificate certificate;\n\n\tprivate MockHttpServletRequest request;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t}\n\n\t@Test\n\tpublic void x509SingleCertificate() {\n\t\tMockHttpServletRequest postProcessedRequest = x509(this.certificate).postProcessRequest(this.request);\n\t\tX509Certificate[] certificates = (X509Certificate[]) postProcessedRequest\n\t\t\t.getAttribute(\"jakarta.servlet.request.X509Certificate\");\n\t\tassertThat(certificates).containsOnly(this.certificate);\n\t}\n\n\t@Test\n\tpublic void x509ResourceName() throws Exception {\n\t\tMockHttpServletRequest postProcessedRequest = x509(\"rod.cer\").postProcessRequest(this.request);\n\t\tX509Certificate[] certificates = (X509Certificate[]) postProcessedRequest\n\t\t\t.getAttribute(\"jakarta.servlet.request.X509Certificate\");\n\t\tassertThat(certificates).hasSize(1);\n\t\tassertThat(certificates[0].getSubjectDN().getName())\n\t\t\t.isEqualTo(\"CN=rod, OU=Spring Security, O=Spring Framework\");\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsCsrfDebugFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.csrf.CookieCsrfTokenRepository;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.web.context.WebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class SecurityMockMvcRequestPostProcessorsCsrfDebugFilterTests {\n\n\t@Autowired\n\tprivate WebApplicationContext wac;\n\n\t// SEC-3836\n\t@Test\n\tpublic void findCookieCsrfTokenRepository() {\n\t\tMockHttpServletRequest request = post(\"/\").buildRequest(this.wac.getServletContext());\n\t\tCsrfTokenRepository csrfTokenRepository = WebTestUtils.getCsrfTokenRepository(request);\n\t\tassertThat(csrfTokenRepository).isNotNull();\n\t\tassertThat(csrfTokenRepository).isEqualTo(Config.cookieCsrfTokenRepository);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class Config {\n\n\t\tstatic CsrfTokenRepository cookieCsrfTokenRepository = new CookieCsrfTokenRepository();\n\n\t\t@Bean\n\t\tSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.csrf((csrf) -> csrf.csrfTokenRepository(cookieCsrfTokenRepository));\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tWebSecurityCustomizer webSecurityCustomizer() {\n\t\t\t// Enable the DebugFilter\n\t\t\treturn (web) -> web.debug(true);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsCsrfTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessorsCsrfTests.Config.TheController;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.CsrfTokenRequestHandler;\nimport org.springframework.security.web.csrf.DeferredCsrfToken;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultMatcher;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class SecurityMockMvcRequestPostProcessorsCsrfTests {\n\n\t@Autowired\n\tWebApplicationContext wac;\n\n\t@Autowired\n\tTheController controller;\n\n\t@Autowired\n\tFilterChainProxy springSecurityFilterChain;\n\n\tMockMvc mockMvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.mockMvc = MockMvcBuilders\n\t\t\t.webAppContextSetup(this.wac)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t// gh-3881\n\t@Test\n\tpublic void csrfWithStandalone() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc = MockMvcBuilders\n\t\t\t\t.standaloneSetup(this.controller)\n\t\t\t\t.apply(springSecurity(this.springSecurityFilterChain))\n\t\t\t\t.build();\n\t\tthis.mockMvc.perform(post(\"/\").with(csrf()))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(csrfAsParam());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void csrfWithParam() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(post(\"/\").with(csrf()))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(csrfAsParam());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void csrfWithHeader() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(post(\"/\").with(csrf().asHeader()))\n\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t.andExpect(csrfAsHeader());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void csrfWithInvalidParam() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(post(\"/\").with(csrf().useInvalidToken()))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(csrfAsParam());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void csrfWithInvalidHeader() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(post(\"/\").with(csrf().asHeader().useInvalidToken()))\n\t\t\t.andExpect(status().isForbidden())\n\t\t\t.andExpect(csrfAsHeader());\n\t\t// @formatter:on\n\t}\n\n\t// SEC-3097\n\t@Test\n\tpublic void csrfWithWrappedRequest() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc = MockMvcBuilders\n\t\t\t\t.webAppContextSetup(this.wac)\n\t\t\t\t.addFilter(new SessionRepositoryFilter())\n\t\t\t\t.apply(springSecurity())\n\t\t\t\t.build();\n\t\tthis.mockMvc.perform(post(\"/\").with(csrf()))\n\t\t\t\t.andExpect(status().is2xxSuccessful())\n\t\t\t\t.andExpect(csrfAsParam());\n\t\t// @formatter:on\n\t}\n\n\t// gh-4016\n\t@Test\n\tpublic void csrfWhenUsedThenDoesNotImpactOriginalRepository() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc.perform(post(\"/\").with(csrf()));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpSessionCsrfTokenRepository repo = new HttpSessionCsrfTokenRepository();\n\t\tCsrfTokenRequestHandler handler = new XorCsrfTokenRequestAttributeHandler();\n\t\tDeferredCsrfToken deferredCsrfToken = repo.loadDeferredToken(request, response);\n\t\thandler.handle(request, response, deferredCsrfToken);\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tMockHttpServletRequestBuilder requestWithCsrf = post(\"/\")\n\t\t\t.param(token.getParameterName(), token.getToken())\n\t\t\t.session((MockHttpSession) request.getSession());\n\t\tthis.mockMvc.perform(requestWithCsrf)\n\t\t\t.andExpect(status().isOk());\n\t\t// @formatter:on\n\t}\n\n\tpublic static ResultMatcher csrfAsParam() {\n\t\treturn new CsrfParamResultMatcher();\n\t}\n\n\tpublic static ResultMatcher csrfAsHeader() {\n\t\treturn new CsrfHeaderResultMatcher();\n\t}\n\n\tstatic class CsrfParamResultMatcher implements ResultMatcher {\n\n\t\t@Override\n\t\tpublic void match(MvcResult result) {\n\t\t\tMockHttpServletRequest request = result.getRequest();\n\t\t\tassertThat(request.getParameter(\"_csrf\")).isNotNull();\n\t\t\tassertThat(request.getHeader(\"X-CSRF-TOKEN\")).isNull();\n\t\t}\n\n\t}\n\n\tstatic class CsrfHeaderResultMatcher implements ResultMatcher {\n\n\t\t@Override\n\t\tpublic void match(MvcResult result) {\n\t\t\tMockHttpServletRequest request = result.getRequest();\n\t\t\tassertThat(request.getParameter(\"_csrf\")).isNull();\n\t\t\tassertThat(request.getHeader(\"X-CSRF-TOKEN\")).isNotNull();\n\t\t}\n\n\t}\n\n\tstatic class SessionRepositoryFilter extends OncePerRequestFilter {\n\n\t\t@Override\n\t\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tFilterChain filterChain) throws ServletException, IOException {\n\t\t\tfilterChain.doFilter(new SessionRequestWrapper(request), response);\n\t\t}\n\n\t\tstatic class SessionRequestWrapper extends HttpServletRequestWrapper {\n\n\t\t\tHttpSession session = new MockHttpSession();\n\n\t\t\tSessionRequestWrapper(HttpServletRequest request) {\n\t\t\t\tsuper(request);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic HttpSession getSession(boolean create) {\n\t\t\t\treturn this.session;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic HttpSession getSession() {\n\t\t\t\treturn this.session;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class TheController {\n\n\t\t\t@RequestMapping(\"/\")\n\t\t\tString index() {\n\t\t\t\treturn \"Hi\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsDigestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.www.DigestAuthenticationFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.digest;\n\npublic class SecurityMockMvcRequestPostProcessorsDigestTests {\n\n\tprivate DigestAuthenticationFilter filter;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate String username;\n\n\tprivate String password;\n\n\tprivate DigestAuthenticationEntryPoint entryPoint;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.password = \"password\";\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.entryPoint = new DigestAuthenticationEntryPoint();\n\t\tthis.entryPoint.setKey(\"key\");\n\t\tthis.entryPoint.setRealmName(\"Spring Security\");\n\t\tthis.filter = new DigestAuthenticationFilter();\n\t\tthis.filter.setUserDetailsService(\n\t\t\t\t(username) -> new User(username, this.password, AuthorityUtils.createAuthorityList(\"ROLE_USER\")));\n\t\tthis.filter.setAuthenticationEntryPoint(this.entryPoint);\n\t\tthis.filter.afterPropertiesSet();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void digestWithFilter() throws Exception {\n\t\tMockHttpServletRequest postProcessedRequest = digest().postProcessRequest(this.request);\n\t\tassertThat(extractUser()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\tpublic void digestWithFilterCustomUsername() throws Exception {\n\t\tString username = \"admin\";\n\t\tMockHttpServletRequest postProcessedRequest = digest(username).postProcessRequest(this.request);\n\t\tassertThat(extractUser()).isEqualTo(username);\n\t}\n\n\t@Test\n\tpublic void digestWithFilterCustomPassword() throws Exception {\n\t\tString username = \"custom\";\n\t\tthis.password = \"secret\";\n\t\tMockHttpServletRequest postProcessedRequest = digest(username).password(this.password)\n\t\t\t.postProcessRequest(this.request);\n\t\tassertThat(extractUser()).isEqualTo(username);\n\t}\n\n\t@Test\n\tpublic void digestWithFilterCustomRealm() throws Exception {\n\t\tString username = \"admin\";\n\t\tthis.entryPoint.setRealmName(\"Custom\");\n\t\tMockHttpServletRequest postProcessedRequest = digest(username).realm(this.entryPoint.getRealmName())\n\t\t\t.postProcessRequest(this.request);\n\t\tassertThat(extractUser()).isEqualTo(username);\n\t}\n\n\t@Test\n\tpublic void digestWithFilterFails() throws Exception {\n\t\tString username = \"admin\";\n\t\tMockHttpServletRequest postProcessedRequest = digest(username).realm(\"Invalid\")\n\t\t\t.postProcessRequest(this.request);\n\t\tassertThat(extractUser()).isNull();\n\t}\n\n\tprivate String extractUser() throws IOException, ServletException {\n\t\tthis.filter.doFilter(this.request, new MockHttpServletResponse(), new MockFilterChain() {\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response) {\n\t\t\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t\t\tSecurityMockMvcRequestPostProcessorsDigestTests.this.username = (authentication != null)\n\t\t\t\t\t\t? authentication.getName() : null;\n\t\t\t}\n\t\t});\n\t\treturn this.username;\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsJwtTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.oauth2.jwt.Jwt;\nimport org.springframework.security.oauth2.jwt.TestJwts;\nimport org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;\n\n/**\n * Tests for {@link SecurityMockMvcRequestPostProcessors#jwt}\n *\n * @author Jérôme Wacongne &lt;ch4mp&#64;c4-soft.com&gt;\n * @author Josh Cummings\n * @since 5.2\n */\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockMvcRequestPostProcessorsJwtTests {\n\n\t@Captor\n\tprivate ArgumentCaptor<SecurityContext> contextCaptor;\n\n\t@Mock\n\tprivate SecurityContextRepository repository;\n\n\tprivate MockHttpServletRequest request;\n\n\t@Mock\n\tprivate GrantedAuthority authority1;\n\n\t@Mock\n\tprivate GrantedAuthority authority2;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tSecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter(this.repository);\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tservletContext.setAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN,\n\t\t\t\tnew FilterChainProxy(new DefaultSecurityFilterChain(AnyRequestMatcher.INSTANCE, filter)));\n\t\tthis.request = new MockHttpServletRequest(servletContext);\n\t\tWebTestUtils.setSecurityContextRepository(this.request, this.repository);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void jwtWhenUsingDefaultsThenProducesDefaultJwtAuthentication() {\n\t\tjwt().postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(JwtAuthenticationToken.class);\n\t\tJwtAuthenticationToken token = (JwtAuthenticationToken) context.getAuthentication();\n\t\tassertThat(token.getAuthorities()).isNotEmpty();\n\t\tassertThat(token.getToken()).isNotNull();\n\t\tassertThat(token.getToken().getSubject()).isEqualTo(\"user\");\n\t\tassertThat(token.getToken().getHeaders()).containsEntry(\"alg\", \"none\");\n\t}\n\n\t@Test\n\tpublic void jwtWhenProvidingBuilderConsumerThenProducesJwtAuthentication() {\n\t\tString name = new String(\"user\");\n\t\tjwt().jwt((jwt) -> jwt.subject(name)).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(JwtAuthenticationToken.class);\n\t\tJwtAuthenticationToken token = (JwtAuthenticationToken) context.getAuthentication();\n\t\tassertThat(token.getToken().getSubject()).isSameAs(name);\n\t}\n\n\t@Test\n\tpublic void jwtWhenProvidingCustomAuthoritiesThenProducesJwtAuthentication() {\n\t\tjwt().jwt((jwt) -> jwt.claim(\"scope\", \"ignored authorities\"))\n\t\t\t.authorities(this.authority1, this.authority2)\n\t\t\t.postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities()).containsOnly(this.authority1,\n\t\t\t\tthis.authority2);\n\t}\n\n\t@Test\n\tpublic void jwtWhenProvidingScopedAuthoritiesThenProducesJwtAuthentication() {\n\t\tjwt().jwt((jwt) -> jwt.claim(\"scope\", \"scoped authorities\")).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities())\n\t\t\t.containsOnly(new SimpleGrantedAuthority(\"SCOPE_scoped\"), new SimpleGrantedAuthority(\"SCOPE_authorities\"));\n\t}\n\n\t@Test\n\tpublic void jwtWhenProvidingGrantedAuthoritiesThenProducesJwtAuthentication() {\n\t\tjwt().jwt((jwt) -> jwt.claim(\"scope\", \"ignored authorities\"))\n\t\t\t.authorities((jwt) -> Arrays.asList(this.authority1))\n\t\t\t.postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities()).containsOnly(this.authority1);\n\t}\n\n\t@Test\n\tpublic void jwtWhenProvidingPreparedJwtThenUsesItForAuthentication() {\n\t\tJwt originalToken = TestJwts.jwt().header(\"header1\", \"value1\").subject(\"some_user\").build();\n\t\tjwt().jwt(originalToken).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tJwtAuthenticationToken retrievedToken = (JwtAuthenticationToken) context.getAuthentication();\n\t\tassertThat(retrievedToken.getToken().getSubject()).isEqualTo(\"some_user\");\n\t\tassertThat(retrievedToken.getToken().getTokenValue()).isEqualTo(\"token\");\n\t\tassertThat(retrievedToken.getToken().getHeaders()).containsEntry(\"header1\", \"value1\");\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsOAuth2ClientTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistration;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.OAuth2AccessToken;\nimport org.springframework.security.oauth2.core.TestOAuth2AccessTokens;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.OAuth2ClientRequestPostProcessor.TestOAuth2AuthorizedClientRepository;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Client;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\n\n/**\n * Tests for {@link SecurityMockMvcRequestPostProcessors#oidcLogin()}\n *\n * @author Josh Cummings\n * @since 5.3\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class SecurityMockMvcRequestPostProcessorsOAuth2ClientTests {\n\n\t@Autowired\n\tWebApplicationContext context;\n\n\tMockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.mvc = MockMvcBuilders\n\t\t\t.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenUsingDefaultsThenException() throws Exception {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> oauth2Client().postProcessRequest(new MockHttpServletRequest()))\n\t\t\t.withMessageContaining(\"ClientRegistration\");\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenUsingDefaultsThenProducesDefaultAuthorizedClient() throws Exception {\n\t\tthis.mvc.perform(get(\"/access-token\").with(oauth2Client(\"registration-id\")))\n\t\t\t.andExpect(content().string(\"access-token\"));\n\t\tthis.mvc.perform(get(\"/client-id\").with(oauth2Client(\"registration-id\")))\n\t\t\t.andExpect(content().string(\"test-client\"));\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenClientRegistrationThenUses() throws Exception {\n\t\tClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()\n\t\t\t.registrationId(\"registration-id\")\n\t\t\t.clientId(\"client-id\")\n\t\t\t.build();\n\t\tthis.mvc.perform(get(\"/client-id\").with(oauth2Client().clientRegistration(clientRegistration)))\n\t\t\t.andExpect(content().string(\"client-id\"));\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenClientRegistrationConsumerThenUses() throws Exception {\n\t\tthis.mvc\n\t\t\t.perform(get(\"/client-id\")\n\t\t\t\t.with(oauth2Client(\"registration-id\").clientRegistration((c) -> c.clientId(\"client-id\"))))\n\t\t\t.andExpect(content().string(\"client-id\"));\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenPrincipalNameThenUses() throws Exception {\n\t\tthis.mvc.perform(get(\"/principal-name\").with(oauth2Client(\"registration-id\").principalName(\"test-subject\")))\n\t\t\t.andExpect(content().string(\"test-subject\"));\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenAccessTokenThenUses() throws Exception {\n\t\tOAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();\n\t\tthis.mvc.perform(get(\"/access-token\").with(oauth2Client(\"registration-id\").accessToken(accessToken)))\n\t\t\t.andExpect(content().string(\"no-scopes\"));\n\t}\n\n\t@Test\n\tpublic void oauth2ClientWhenUsedOnceThenDoesNotAffectRemainingTests() throws Exception {\n\t\tthis.mvc.perform(get(\"/client-id\").with(oauth2Client(\"registration-id\")))\n\t\t\t.andExpect(content().string(\"test-client\"));\n\t\tOAuth2AuthorizedClient client = new OAuth2AuthorizedClient(TestClientRegistrations.clientRegistration().build(),\n\t\t\t\t\"sub\", TestOAuth2AccessTokens.noScopes());\n\t\tOAuth2AuthorizedClientRepository repository = this.context.getBean(OAuth2AuthorizedClientRepository.class);\n\t\tgiven(repository.loadAuthorizedClient(eq(\"registration-id\"), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class)))\n\t\t\t.willReturn(client);\n\t\tthis.mvc.perform(get(\"/client-id\")).andExpect(content().string(\"client-id\"));\n\t\tverify(repository).loadAuthorizedClient(eq(\"registration-id\"), any(Authentication.class),\n\t\t\t\tany(HttpServletRequest.class));\n\t}\n\n\t// gh-13113\n\t@Test\n\tpublic void oauth2ClientWhenUsedThenSetsClientToRepository() throws Exception {\n\t\tHttpServletRequest request = this.mvc.perform(get(\"/client-id\").with(oauth2Client(\"registration-id\")))\n\t\t\t.andExpect(content().string(\"test-client\"))\n\t\t\t.andReturn()\n\t\t\t.getRequest();\n\t\tOAuth2AuthorizedClientManager manager = this.context.getBean(OAuth2AuthorizedClientManager.class);\n\t\tOAuth2AuthorizedClientRepository repository = (OAuth2AuthorizedClientRepository) ReflectionTestUtils\n\t\t\t.getField(manager, \"authorizedClientRepository\");\n\t\tassertThat(repository).isInstanceOf(TestOAuth2AuthorizedClientRepository.class);\n\t\tassertThat((OAuth2AuthorizedClient) repository.loadAuthorizedClient(\"id\", null, request)).isNotNull();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class OAuth2ClientConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authz) -> authz\n\t\t\t\t\t\t.anyRequest().permitAll()\n\t\t\t\t)\n\t\t\t\t.oauth2Client(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clients,\n\t\t\t\tOAuth2AuthorizedClientRepository authorizedClients) {\n\t\t\treturn new DefaultOAuth2AuthorizedClientManager(clients, authorizedClients);\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn mock(ClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository authorizedClientRepository() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PrincipalController {\n\n\t\t\t@GetMapping(\"/access-token\")\n\t\t\tString accessToken(\n\t\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"registration-id\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn authorizedClient.getAccessToken().getTokenValue();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/principal-name\")\n\t\t\tString principalName(\n\t\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"registration-id\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn authorizedClient.getPrincipalName();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/client-id\")\n\t\t\tString clientId(\n\t\t\t\t\t@RegisteredOAuth2AuthorizedClient(\"registration-id\") OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn authorizedClient.getClientRegistration().getClientId();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsOAuth2LoginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.registration.TestClientRegistrations;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.user.DefaultOAuth2User;\nimport org.springframework.security.oauth2.core.user.OAuth2User;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link SecurityMockMvcRequestPostProcessors#oauth2Login()}\n *\n * @author Josh Cummings\n * @since 5.3\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class SecurityMockMvcRequestPostProcessorsOAuth2LoginTests {\n\n\t@Autowired\n\tWebApplicationContext context;\n\n\tMockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.mvc = MockMvcBuilders\n\t\t\t.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenUsingDefaultsThenProducesDefaultAuthentication() throws Exception {\n\t\tthis.mvc.perform(get(\"/name\").with(oauth2Login())).andExpect(content().string(\"user\"));\n\t\tthis.mvc.perform(get(\"/admin/id-token/name\").with(oauth2Login())).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenUsingDefaultsThenProducesDefaultAuthorizedClient() throws Exception {\n\t\tthis.mvc.perform(get(\"/client-id\").with(oauth2Login())).andExpect(content().string(\"test-client\"));\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenAuthoritiesSpecifiedThenGrantsAccess() throws Exception {\n\t\tthis.mvc\n\t\t\t.perform(get(\"/admin/scopes\").with(oauth2Login().authorities(new SimpleGrantedAuthority(\"SCOPE_admin\"))))\n\t\t\t.andExpect(content().string(\"[\\\"SCOPE_admin\\\"]\"));\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenAttributeSpecifiedThenUserHasAttribute() throws Exception {\n\t\tthis.mvc\n\t\t\t.perform(get(\"/attributes/iss\")\n\t\t\t\t.with(oauth2Login().attributes((a) -> a.put(\"iss\", \"https://idp.example.org\"))))\n\t\t\t.andExpect(content().string(\"https://idp.example.org\"));\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenNameSpecifiedThenUserHasName() throws Exception {\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.commaSeparatedStringToAuthorityList(\"SCOPE_read\"),\n\t\t\t\tCollections.singletonMap(\"custom-attribute\", \"test-subject\"), \"custom-attribute\");\n\t\tthis.mvc.perform(get(\"/attributes/custom-attribute\").with(oauth2Login().oauth2User(oauth2User)))\n\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\tthis.mvc.perform(get(\"/name\").with(oauth2Login().oauth2User(oauth2User)))\n\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\tthis.mvc.perform(get(\"/client-name\").with(oauth2Login().oauth2User(oauth2User)))\n\t\t\t.andExpect(content().string(\"test-subject\"));\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenClientRegistrationSpecifiedThenUses() throws Exception {\n\t\tthis.mvc\n\t\t\t.perform(get(\"/client-id\")\n\t\t\t\t.with(oauth2Login().clientRegistration(TestClientRegistrations.clientRegistration().build())))\n\t\t\t.andExpect(content().string(\"client-id\"));\n\t}\n\n\t@Test\n\tpublic void oauth2LoginWhenOAuth2UserSpecifiedThenLastCalledTakesPrecedence() throws Exception {\n\t\tOAuth2User oauth2User = new DefaultOAuth2User(AuthorityUtils.createAuthorityList(\"SCOPE_read\"),\n\t\t\t\tCollections.singletonMap(\"username\", \"user\"), \"username\");\n\t\tthis.mvc\n\t\t\t.perform(get(\"/attributes/sub\")\n\t\t\t\t.with(oauth2Login().attributes((a) -> a.put(\"sub\", \"bar\")).oauth2User(oauth2User)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"no-attribute\"));\n\t\tthis.mvc\n\t\t\t.perform(get(\"/attributes/sub\")\n\t\t\t\t.with(oauth2Login().oauth2User(oauth2User).attributes((a) -> a.put(\"sub\", \"bar\"))))\n\t\t\t.andExpect(content().string(\"bar\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class OAuth2LoginConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t\t.requestMatchers(\"/admin/**\").hasAuthority(\"SCOPE_admin\")\n\t\t\t\t\t\t.anyRequest().hasAuthority(\"SCOPE_read\")\n\t\t\t\t).oauth2Login(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn mock(ClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PrincipalController {\n\n\t\t\t@GetMapping(\"/name\")\n\t\t\tString name(@AuthenticationPrincipal OAuth2User oauth2User) {\n\t\t\t\treturn oauth2User.getName();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/client-id\")\n\t\t\tString authorizedClient(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn authorizedClient.getClientRegistration().getClientId();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/client-name\")\n\t\t\tString clientName(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn authorizedClient.getPrincipalName();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/attributes/{attribute}\")\n\t\t\tString attributes(@AuthenticationPrincipal OAuth2User oauth2User,\n\t\t\t\t\t@PathVariable(\"attribute\") String attribute) {\n\t\t\t\treturn Optional.ofNullable((String) oauth2User.getAttribute(attribute)).orElse(\"no-attribute\");\n\t\t\t}\n\n\t\t\t@GetMapping(\"/admin/scopes\")\n\t\t\tList<String> scopes(\n\t\t\t\t\t@AuthenticationPrincipal(expression = \"authorities\") Collection<GrantedAuthority> authorities) {\n\t\t\t\treturn authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsOidcLoginTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.client.OAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;\nimport org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;\nimport org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;\nimport org.springframework.security.oauth2.core.oidc.OidcIdToken;\nimport org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;\nimport org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;\nimport org.springframework.security.oauth2.core.oidc.user.OidcUser;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oidcLogin;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link SecurityMockMvcRequestPostProcessors#oidcLogin()}\n *\n * @author Josh Cummings\n * @since 5.3\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class SecurityMockMvcRequestPostProcessorsOidcLoginTests {\n\n\t@Autowired\n\tWebApplicationContext context;\n\n\tMockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.mvc = MockMvcBuilders\n\t\t\t.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenUsingDefaultsThenProducesDefaultAuthentication() throws Exception {\n\t\tthis.mvc.perform(get(\"/name\").with(oidcLogin())).andExpect(content().string(\"user\"));\n\t\tthis.mvc.perform(get(\"/admin/id-token/name\").with(oidcLogin())).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenUsingDefaultsThenProducesDefaultAuthorizedClient() throws Exception {\n\t\tthis.mvc.perform(get(\"/access-token\").with(oidcLogin())).andExpect(content().string(\"access-token\"));\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenAuthoritiesSpecifiedThenGrantsAccess() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin/scopes\").with(oidcLogin().authorities(new SimpleGrantedAuthority(\"SCOPE_admin\"))))\n\t\t\t.andExpect(content().string(\"[\\\"SCOPE_admin\\\"]\"));\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenIdTokenSpecifiedThenUserHasClaims() throws Exception {\n\t\tthis.mvc.perform(get(\"/id-token/iss\").with(oidcLogin().idToken((i) -> i.issuer(\"https://idp.example.org\"))))\n\t\t\t.andExpect(content().string(\"https://idp.example.org\"));\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenUserInfoSpecifiedThenUserHasClaims() throws Exception {\n\t\tthis.mvc.perform(get(\"/user-info/email\").with(oidcLogin().userInfoToken((u) -> u.email(\"email@email\"))))\n\t\t\t.andExpect(content().string(\"email@email\"));\n\t}\n\n\t@Test\n\tpublic void oidcLoginWhenNameSpecifiedThenUserHasName() throws Exception {\n\t\tOidcUser oidcUser = new DefaultOidcUser(AuthorityUtils.commaSeparatedStringToAuthorityList(\"SCOPE_read\"),\n\t\t\t\tOidcIdToken.withTokenValue(\"id-token\").claim(\"custom-attribute\", \"test-subject\").build(),\n\t\t\t\t\"custom-attribute\");\n\t\tthis.mvc.perform(get(\"/id-token/custom-attribute\").with(oidcLogin().oidcUser(oidcUser)))\n\t\t\t.andExpect(content().string(\"test-subject\"));\n\t\tthis.mvc.perform(get(\"/name\").with(oidcLogin().oidcUser(oidcUser))).andExpect(content().string(\"test-subject\"));\n\t\tthis.mvc.perform(get(\"/client-name\").with(oidcLogin().oidcUser(oidcUser)))\n\t\t\t.andExpect(content().string(\"test-subject\"));\n\t}\n\n\t// gh-7794\n\t@Test\n\tpublic void oidcLoginWhenOidcUserSpecifiedThenLastCalledTakesPrecedence() throws Exception {\n\t\tOidcUser oidcUser = new DefaultOidcUser(AuthorityUtils.createAuthorityList(\"SCOPE_read\"),\n\t\t\t\tTestOidcIdTokens.idToken().build());\n\t\tthis.mvc.perform(get(\"/id-token/sub\").with(oidcLogin().idToken((i) -> i.subject(\"foo\")).oidcUser(oidcUser)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string(\"subject\"));\n\t\tthis.mvc.perform(get(\"/id-token/sub\").with(oidcLogin().oidcUser(oidcUser).idToken((i) -> i.subject(\"bar\"))))\n\t\t\t.andExpect(content().string(\"bar\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class OAuth2LoginConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasAuthority(\"SCOPE_admin\")\n\t\t\t\t\t.anyRequest().hasAuthority(\"SCOPE_read\"))\n\t\t\t\t.oauth2Login(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tClientRegistrationRepository clientRegistrationRepository() {\n\t\t\treturn mock(ClientRegistrationRepository.class);\n\t\t}\n\n\t\t@Bean\n\t\tOAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository() {\n\t\t\treturn mock(OAuth2AuthorizedClientRepository.class);\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PrincipalController {\n\n\t\t\t@GetMapping(\"/name\")\n\t\t\tString name(@AuthenticationPrincipal OidcUser oidcUser) {\n\t\t\t\treturn oidcUser.getName();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/client-name\")\n\t\t\tString clientName(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn authorizedClient.getPrincipalName();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/access-token\")\n\t\t\tString authorizedClient(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {\n\t\t\t\treturn authorizedClient.getAccessToken().getTokenValue();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/id-token/{claim}\")\n\t\t\tString idTokenClaim(@AuthenticationPrincipal OidcUser oidcUser, @PathVariable(\"claim\") String claim) {\n\t\t\t\treturn oidcUser.getIdToken().getClaim(claim);\n\t\t\t}\n\n\t\t\t@GetMapping(\"/user-info/{claim}\")\n\t\t\tString userInfoClaim(@AuthenticationPrincipal OidcUser oidcUser, @PathVariable(\"claim\") String claim) {\n\t\t\t\treturn oidcUser.getUserInfo().getClaim(claim);\n\t\t\t}\n\n\t\t\t@GetMapping(\"/admin/scopes\")\n\t\t\tList<String> scopes(\n\t\t\t\t\t@AuthenticationPrincipal(expression = \"authorities\") Collection<GrantedAuthority> authorities) {\n\t\t\t\treturn authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsOpaqueTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;\nimport org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;\nimport org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.opaqueToken;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link SecurityMockMvcRequestPostProcessors#opaqueToken()}\n *\n * @author Josh Cummings\n * @since 5.3\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class SecurityMockMvcRequestPostProcessorsOpaqueTokenTests {\n\n\t@Autowired\n\tWebApplicationContext context;\n\n\tMockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.mvc = MockMvcBuilders\n\t\t\t.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void opaqueTokenWhenUsingDefaultsThenProducesDefaultAuthentication() throws Exception {\n\t\tthis.mvc.perform(get(\"/name\").with(opaqueToken())).andExpect(content().string(\"user\"));\n\t\tthis.mvc.perform(get(\"/admin/scopes\").with(opaqueToken())).andExpect(status().isForbidden());\n\t}\n\n\t@Test\n\tpublic void opaqueTokenWhenAttributeSpecifiedThenUserHasAttribute() throws Exception {\n\t\tthis.mvc\n\t\t\t.perform(get(\"/opaque-token/iss\")\n\t\t\t\t.with(opaqueToken().attributes((a) -> a.put(\"iss\", \"https://idp.example.org\"))))\n\t\t\t.andExpect(content().string(\"https://idp.example.org\"));\n\t}\n\n\t@Test\n\tpublic void opaqueTokenWhenPrincipalSpecifiedThenAuthenticationHasPrincipal() throws Exception {\n\t\tCollection authorities = Collections.singleton(new SimpleGrantedAuthority(\"SCOPE_read\"));\n\t\tOAuth2AuthenticatedPrincipal principal = mock(OAuth2AuthenticatedPrincipal.class);\n\t\tgiven(principal.getName()).willReturn(\"ben\");\n\t\tgiven(principal.getAuthorities()).willReturn(authorities);\n\t\tthis.mvc.perform(get(\"/name\").with(opaqueToken().principal(principal))).andExpect(content().string(\"ben\"));\n\t}\n\n\t// gh-7800\n\t@Test\n\tpublic void opaqueTokenWhenPrincipalSpecifiedThenLastCalledTakesPrecedence() throws Exception {\n\t\tOAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals\n\t\t\t.active((a) -> a.put(\"scope\", \"user\"));\n\t\tthis.mvc\n\t\t\t.perform(get(\"/opaque-token/sub\")\n\t\t\t\t.with(opaqueToken().attributes((a) -> a.put(\"sub\", \"foo\")).principal(principal)))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().string((String) principal.getAttribute(\"sub\")));\n\t\tthis.mvc\n\t\t\t.perform(get(\"/opaque-token/sub\")\n\t\t\t\t.with(opaqueToken().principal(principal).attributes((a) -> a.put(\"sub\", \"bar\"))))\n\t\t\t.andExpect(content().string(\"bar\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class OAuth2LoginConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasAuthority(\"SCOPE_admin\")\n\t\t\t\t\t.anyRequest().hasAuthority(\"SCOPE_read\"))\n\t\t\t\t.oauth2ResourceServer((server) -> server\n\t\t\t\t\t.opaqueToken((opaqueToken) -> opaqueToken\n\t\t\t\t\t\t.introspector(mock(OpaqueTokenIntrospector.class))));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@RestController\n\t\tstatic class PrincipalController {\n\n\t\t\t@GetMapping(\"/name\")\n\t\t\tString name(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal) {\n\t\t\t\treturn principal.getName();\n\t\t\t}\n\n\t\t\t@GetMapping(\"/opaque-token/{attribute}\")\n\t\t\tString tokenAttribute(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal,\n\t\t\t\t\t@PathVariable(\"attribute\") String attribute) {\n\t\t\t\treturn principal.getAttribute(attribute);\n\t\t\t}\n\n\t\t\t@GetMapping(\"/admin/scopes\")\n\t\t\tList<String> scopes(\n\t\t\t\t\t@AuthenticationPrincipal(expression = \"authorities\") Collection<GrantedAuthority> authorities) {\n\t\t\t\treturn authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsSecurityContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.securityContext;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockMvcRequestPostProcessorsSecurityContextTests {\n\n\t@Captor\n\tprivate ArgumentCaptor<SecurityContext> contextCaptor;\n\n\t@Mock\n\tprivate SecurityContextRepository repository;\n\n\tprivate MockHttpServletRequest request;\n\n\t@Mock\n\tprivate SecurityContext expectedContext;\n\n\t@Mock\n\tprivate MockedStatic<WebTestUtils> webTestUtils;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.webTestUtils.when(() -> WebTestUtils.getSecurityContextRepository(this.request))\n\t\t\t.thenReturn(this.repository);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void userDetails() {\n\t\tsecurityContext(this.expectedContext).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat(context).isSameAs(this.expectedContext);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextStatelessTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport jakarta.servlet.Filter;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.testSecurityContext;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SecurityMockMvcRequestPostProcessorsTestSecurityContextStatelessTests.Config.class)\n@WebAppConfiguration\npublic class SecurityMockMvcRequestPostProcessorsTestSecurityContextStatelessTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\t@Autowired\n\tprivate Filter springSecurityFilterChain;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.addFilters(this.springSecurityFilterChain)\n\t\t\t.defaultRequest(get(\"/\").with(testSecurityContext()))\n\t\t\t.build();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void testSecurityContextWithMockUserWorksWithStateless() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().is2xxSuccessful());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.sessionManagement((management) -> management\n\t\t\t\t\t.sessionCreationPolicy(SessionCreationPolicy.STATELESS));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\tauth.inMemoryAuthentication();\n\t\t}\n\n\t\t@RestController\n\t\tstatic class Controller {\n\n\t\t\t@RequestMapping(\"/\")\n\t\t\tString hello() {\n\t\t\t\treturn \"Hello\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsTestSecurityContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.testSecurityContext;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockMvcRequestPostProcessorsTestSecurityContextTests {\n\n\t@Mock\n\tprivate SecurityContext context;\n\n\t@Mock\n\tprivate SecurityContextRepository repository;\n\n\t@Mock\n\tprivate MockedStatic<WebTestUtils> webTestUtils;\n\n\tprivate MockHttpServletRequest request;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.webTestUtils.when(() -> WebTestUtils.getSecurityContextRepository(this.request))\n\t\t\t.thenReturn(this.repository);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testSecurityContextSaves() {\n\t\tTestSecurityContextHolder.setContext(this.context);\n\t\ttestSecurityContext().postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(eq(this.context), eq(this.request), any(HttpServletResponse.class));\n\t}\n\n\t// Ensure it does not fail if TestSecurityContextHolder is not initialized\n\t@Test\n\tpublic void testSecurityContextNoContext() {\n\t\ttestSecurityContext().postProcessRequest(this.request);\n\t\tverify(this.repository, never()).saveContext(any(SecurityContext.class), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserDetailsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockMvcRequestPostProcessorsUserDetailsTests {\n\n\t@Captor\n\tprivate ArgumentCaptor<SecurityContext> contextCaptor;\n\n\t@Mock\n\tprivate SecurityContextRepository repository;\n\n\tprivate MockHttpServletRequest request;\n\n\t@Mock\n\tprivate UserDetails userDetails;\n\n\t@Mock\n\tprivate MockedStatic<WebTestUtils> webTestUtils;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.webTestUtils.when(() -> WebTestUtils.getSecurityContextRepository(this.request))\n\t\t\t.thenReturn(this.repository);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void userDetails() {\n\t\tuser(this.userDetails).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getPrincipal()).isSameAs(this.userDetails);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsUserTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.request;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.MockedStatic;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.context.TestSecurityContextHolder;\nimport org.springframework.security.test.web.support.WebTestUtils;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockMvcRequestPostProcessorsUserTests {\n\n\t@Captor\n\tprivate ArgumentCaptor<SecurityContext> contextCaptor;\n\n\t@Mock\n\tprivate SecurityContextRepository repository;\n\n\tprivate MockHttpServletRequest request;\n\n\t@Mock\n\tprivate GrantedAuthority authority1;\n\n\t@Mock\n\tprivate GrantedAuthority authority2;\n\n\t@Mock\n\tprivate MockedStatic<WebTestUtils> webTestUtils;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.webTestUtils.when(() -> WebTestUtils.getSecurityContextRepository(this.request))\n\t\t\t.thenReturn(this.repository);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tTestSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void userWithDefaults() {\n\t\tString username = \"userabc\";\n\t\tuser(username).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getName()).isEqualTo(username);\n\t\tassertThat(context.getAuthentication().getCredentials()).isEqualTo(\"password\");\n\t\tassertThat(context.getAuthentication().getAuthorities()).extracting(\"authority\").containsOnly(\"ROLE_USER\");\n\t}\n\n\t@Test\n\tpublic void userWithCustom() {\n\t\tString username = \"customuser\";\n\t\tuser(username).roles(\"CUSTOM\", \"ADMIN\").password(\"newpass\").postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat(context.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(context.getAuthentication().getName()).isEqualTo(username);\n\t\tassertThat(context.getAuthentication().getCredentials()).isEqualTo(\"newpass\");\n\t\tassertThat(context.getAuthentication().getAuthorities()).extracting(\"authority\")\n\t\t\t.containsOnly(\"ROLE_CUSTOM\", \"ROLE_ADMIN\");\n\t}\n\n\t@Test\n\tpublic void userCustomAuthoritiesVarargs() {\n\t\tString username = \"customuser\";\n\t\tuser(username).authorities(this.authority1, this.authority2).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities()).containsOnly(this.authority1,\n\t\t\t\tthis.authority2);\n\t}\n\n\t@Test\n\tpublic void userRolesWithRolePrefixErrors() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> user(\"user\").roles(\"ROLE_INVALID\").postProcessRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void userCustomAuthoritiesList() {\n\t\tString username = \"customuser\";\n\t\tuser(username).authorities(Arrays.asList(this.authority1, this.authority2)).postProcessRequest(this.request);\n\t\tverify(this.repository).saveContext(this.contextCaptor.capture(), eq(this.request),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tSecurityContext context = this.contextCaptor.getValue();\n\t\tassertThat((List<GrantedAuthority>) context.getAuthentication().getAuthorities()).containsOnly(this.authority1,\n\t\t\t\tthis.authority2);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/response/Gh3409Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.response;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.securityContext;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n * @author Rob Winch\n * @since 4.1\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration\n@WebAppConfiguration\npublic class Gh3409Tests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mockMvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.mockMvc = MockMvcBuilders\n\t\t\t.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t// gh-3409\n\t@Test\n\tpublic void unauthenticatedAnonymousUser() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc\n\t\t\t.perform(get(\"/public/\")\n\t\t\t.with(securityContext(new SecurityContextImpl())));\n\t\tthis.mockMvc\n\t\t\t.perform(get(\"/public/\"))\n\t\t\t.andExpect(unauthenticated());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void unauthenticatedNullAuthentication() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc\n\t\t\t.perform(get(\"/\")\n\t\t\t.with(securityContext(new SecurityContextImpl())));\n\t\tthis.mockMvc\n\t\t\t.perform(get(\"/\"))\n\t\t\t.andExpect(unauthenticated());\n\t\t// @formatter:on\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/public/**\").permitAll()\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultHandlersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.response;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHandlers.exportTestSecurityContext;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SecurityMockMvcResultHandlersTests.Config.class)\n@WebAppConfiguration\npublic class SecurityMockMvcResultHandlersTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mockMvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.mockMvc = MockMvcBuilders\n\t\t\t\t.webAppContextSetup(this.context)\n\t\t\t\t.apply(springSecurity())\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void withTestSecurityContextCopiedToSecurityContextHolder() throws Exception {\n\t\tthis.mockMvc.perform(get(\"/\")).andDo(exportTestSecurityContext());\n\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\n\t\tassertThat(authentication.getName()).isEqualTo(\"user\");\n\t\tassertThat(authentication.getAuthorities()).hasSize(1).first().hasToString(\"ROLE_USER\");\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void withTestSecurityContextNotCopiedToSecurityContextHolder() throws Exception {\n\t\tthis.mockMvc.perform(get(\"/\"));\n\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@RestController\n\t\tstatic class Controller {\n\n\t\t\t@RequestMapping(\"/\")\n\t\t\tString ok() {\n\t\t\t\treturn \"ok\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.response;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SecurityMockMvcResultMatchersTests.Config.class)\n@WebAppConfiguration\npublic class SecurityMockMvcResultMatchersTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mockMvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\t// @formatter:off\n\t\tthis.mockMvc = MockMvcBuilders\n\t\t\t.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withAuthenticationWhenMatchesThenSuccess() throws Exception {\n\t\tthis.mockMvc.perform(formLogin())\n\t\t\t.andExpect(authenticated().withAuthentication(\n\t\t\t\t\t(auth) -> assertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken.class)));\n\t}\n\n\t@Test\n\tpublic void withAuthenticationWhenNotMatchesThenFails() throws Exception {\n\t\tassertThatExceptionOfType(AssertionError.class).isThrownBy(() -> this.mockMvc.perform(formLogin())\n\t\t\t.andExpect(authenticated().withAuthentication((auth) -> assertThat(auth.getName()).isEqualTo(\"notmatch\"))));\n\t}\n\n\t// SEC-2719\n\t@Test\n\tpublic void withRolesNotOrderSensitive() throws Exception {\n\t\t// @formatter:off\n\t\tthis.mockMvc\n\t\t\t.perform(formLogin())\n\t\t\t.andExpect(authenticated().withRoles(\"USER\", \"SELLER\"))\n\t\t\t.andExpect(authenticated().withRoles(\"SELLER\", \"USER\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void withRolesFailsIfNotAllRoles() throws Exception {\n\t\tassertThatExceptionOfType(AssertionError.class).isThrownBy(() ->\n\t\t// @formatter:off\n\t\t\tthis.mockMvc\n\t\t\t\t.perform(formLogin())\n\t\t\t\t.andExpect(authenticated().withRoles(\"USER\"))\n\t\t// @formatter:on\n\t\t);\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder().username(\"user\").password(\"password\").roles(\"USER\", \"SELLER\").build();\n\t\t\t// @formatter:on\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\n\t\t@RestController\n\t\tstatic class Controller {\n\n\t\t\t@RequestMapping(\"/\")\n\t\t\tString ok() {\n\t\t\t\treturn \"ok\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/response/SecurityMockWithAuthoritiesMvcResultMatchersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.response;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SecurityMockWithAuthoritiesMvcResultMatchersTests.Config.class)\n@WebAppConfiguration\npublic class SecurityMockWithAuthoritiesMvcResultMatchersTests {\n\n\tprivate static final String ROLE_CUSTOM = \"ROLE_CUSTOM\";\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mockMvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t@Test\n\tpublic void withAuthoritiesStringAllowsAnyOrderAndPermitsAnyImpl() throws Exception {\n\t\tthis.mockMvc.perform(formLogin())\n\t\t\t.andExpect(authenticated().withAuthorities(\"ROLE_ADMIN\", \"ROLE_SELLER\",\n\t\t\t\t\tFactorGrantedAuthority.PASSWORD_AUTHORITY));\n\t}\n\n\t@Test\n\tpublic void withAuthoritiesFailsIfNotAllRoles() throws Exception {\n\t\tList<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>();\n\t\tgrantedAuthorities.add(new SimpleGrantedAuthority(\"ROLE_ADMIN\"));\n\t\tassertThatExceptionOfType(AssertionError.class).isThrownBy(\n\t\t\t\t() -> this.mockMvc.perform(formLogin()).andExpect(authenticated().withAuthorities(grantedAuthorities)));\n\t}\n\n\t@Test\n\tpublic void withAuthoritiesStringSupportsCustomAuthority() throws Exception {\n\t\tthis.mockMvc.perform(formLogin().user(\"custom\"))\n\t\t\t.andExpect(authenticated().withAuthorities(ROLE_CUSTOM, FactorGrantedAuthority.PASSWORD_AUTHORITY));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder().username(\"user\").password(\"password\").roles(\"ADMIN\", \"SELLER\").build();\n\t\t\tUserDetails customAuthorityUser = User.withDefaultPasswordEncoder().username(\"custom\").password(\"password\").authorities(new CustomAuthority(ROLE_CUSTOM)).build();\n\t\t\treturn new InMemoryUserDetailsManager(user, customAuthorityUser);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@RestController\n\t\tstatic class Controller {\n\n\t\t\t@RequestMapping(\"/\")\n\t\t\tString ok() {\n\t\t\t\treturn \"ok\";\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * A custom {@link GrantedAuthority} for testing.\n\t *\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tstatic class CustomAuthority implements GrantedAuthority {\n\n\t\tprivate final String authority;\n\n\t\tCustomAuthority(String authority) {\n\t\t\tthis.authority = authority;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getAuthority() {\n\t\t\treturn this.authority;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.setup;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletContext;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;\nimport org.springframework.web.context.WebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class SecurityMockMvcConfigurerTests {\n\n\t@Mock\n\tprivate Filter filter;\n\n\t@Mock\n\tprivate Filter beanFilter;\n\n\t@Mock\n\tprivate ConfigurableMockMvcBuilder<?> builder;\n\n\t@Mock\n\tprivate WebApplicationContext context;\n\n\t@Mock\n\tprivate ServletContext servletContext;\n\n\t@Test\n\tpublic void beforeMockMvcCreatedOverrideBean() throws Exception {\n\t\tgiven(this.context.getServletContext()).willReturn(this.servletContext);\n\t\tSecurityMockMvcConfigurer configurer = new SecurityMockMvcConfigurer(this.filter);\n\t\tconfigurer.afterConfigurerAdded(this.builder);\n\t\tconfigurer.beforeMockMvcCreated(this.builder, this.context);\n\t\tassertFilterAdded(this.filter);\n\t\tverify(this.servletContext).setAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN, this.filter);\n\t}\n\n\t@Test\n\tpublic void beforeMockMvcCreatedBean() throws Exception {\n\t\tgiven(this.context.getServletContext()).willReturn(this.servletContext);\n\t\treturnFilterBean();\n\t\tSecurityMockMvcConfigurer configurer = new SecurityMockMvcConfigurer();\n\t\tconfigurer.afterConfigurerAdded(this.builder);\n\t\tconfigurer.beforeMockMvcCreated(this.builder, this.context);\n\t\tassertFilterAdded(this.beanFilter);\n\t}\n\n\t@Test\n\tpublic void beforeMockMvcCreatedNoBean() throws Exception {\n\t\tgiven(this.context.getServletContext()).willReturn(this.servletContext);\n\t\tSecurityMockMvcConfigurer configurer = new SecurityMockMvcConfigurer(this.filter);\n\t\tconfigurer.afterConfigurerAdded(this.builder);\n\t\tconfigurer.beforeMockMvcCreated(this.builder, this.context);\n\t\tassertFilterAdded(this.filter);\n\t}\n\n\t@Test\n\tpublic void beforeMockMvcCreatedNoFilter() {\n\t\tSecurityMockMvcConfigurer configurer = new SecurityMockMvcConfigurer();\n\t\tconfigurer.afterConfigurerAdded(this.builder);\n\t\tassertThatIllegalStateException().isThrownBy(() -> configurer.beforeMockMvcCreated(this.builder, this.context));\n\t}\n\n\tprivate void assertFilterAdded(Filter filter) {\n\t\tArgumentCaptor<SecurityMockMvcConfigurer.DelegateFilter> filterArg = ArgumentCaptor\n\t\t\t.forClass(SecurityMockMvcConfigurer.DelegateFilter.class);\n\t\tverify(this.builder).addFilters(filterArg.capture());\n\t\tassertThat(filterArg.getValue().getDelegate()).isEqualTo(filter);\n\t}\n\n\tprivate void returnFilterBean() {\n\t\tgiven(this.context.containsBean(anyString())).willReturn(true);\n\t\tgiven(this.context.getBean(anyString(), eq(Filter.class))).willReturn(this.beanFilter);\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/setup/SecurityMockMvcConfigurersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.setup;\n\nimport jakarta.servlet.Filter;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.users.AuthenticationTestConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(SpringExtension.class)\n@WebAppConfiguration\npublic class SecurityMockMvcConfigurersTests {\n\n\t@Autowired\n\tWebApplicationContext wac;\n\n\tFilter noOpFilter = mock(Filter.class);\n\n\t/**\n\t * Since noOpFilter is first does not continue the chain, security will not be invoked\n\t * and the status should be OK\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void applySpringSecurityWhenAddFilterFirstThenFilterFirst() throws Exception {\n\t\tMockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)\n\t\t\t.addFilters(this.noOpFilter)\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t\tmockMvc.perform(get(\"/\")).andExpect(status().isOk());\n\t}\n\n\t/**\n\t * Since noOpFilter is second security will be invoked and the status will be not OK.\n\t * We know this because if noOpFilter were first security would not be invoked sincet\n\t * noOpFilter does not continue the FilterChain\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void applySpringSecurityWhenAddFilterSecondThenSecurityFirst() throws Exception {\n\t\tMockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)\n\t\t\t.apply(springSecurity())\n\t\t\t.addFilters(this.noOpFilter)\n\t\t\t.build();\n\t\tmockMvc.perform(get(\"/\")).andExpect(status().is4xxClientError());\n\t}\n\n\t@Configuration\n\t@EnableWebMvc\n\t@EnableWebSecurity\n\t@Import(AuthenticationTestConfiguration.class)\n\tstatic class Config {\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CsrfShowcaseTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = CsrfShowcaseTests.Config.class)\n@WebAppConfiguration\npublic class CsrfShowcaseTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t@Test\n\tpublic void postWithCsrfWorks() throws Exception {\n\t\tthis.mvc.perform(post(\"/\").with(csrf())).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void postWithCsrfWorksWithPut() throws Exception {\n\t\tthis.mvc.perform(put(\"/\").with(csrf())).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void postWithNoCsrfForbidden() throws Exception {\n\t\tthis.mvc.perform(post(\"/\")).andExpect(status().isForbidden());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/CustomCsrfShowcaseTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = CustomCsrfShowcaseTests.Config.class)\n@WebAppConfiguration\npublic class CustomCsrfShowcaseTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\t@Autowired\n\tprivate CsrfTokenRepository repository;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.defaultRequest(get(\"/\").with(csrf()))\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void postWithCsrfWorks() throws Exception {\n\t\tthis.mvc.perform(post(\"/\").with(csrf())).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void postWithCsrfWorksWithPut() throws Exception {\n\t\tthis.mvc.perform(put(\"/\").with(csrf())).andExpect(status().isNotFound());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.csrfTokenRepository(repo()));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Bean\n\t\tCsrfTokenRepository repo() {\n\t\t\tHttpSessionCsrfTokenRepository repo = new HttpSessionCsrfTokenRepository();\n\t\t\trepo.setParameterName(\"custom_csrf\");\n\t\t\treturn repo;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/csrf/DefaultCsrfShowcaseTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = DefaultCsrfShowcaseTests.Config.class)\n@WebAppConfiguration\npublic class DefaultCsrfShowcaseTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.defaultRequest(get(\"/\").with(csrf()))\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void postWithCsrfWorks() throws Exception {\n\t\tthis.mvc.perform(post(\"/\")).andExpect(status().isNotFound());\n\t}\n\n\t@Test\n\tpublic void postWithCsrfWorksWithPut() throws Exception {\n\t\tthis.mvc.perform(put(\"/\")).andExpect(status().isNotFound());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/AuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.login;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = AuthenticationTests.Config.class)\n@WebAppConfiguration\npublic class AuthenticationTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.apply(springSecurity())\n\t\t\t.defaultRequest(get(\"/\").accept(MediaType.TEXT_HTML))\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void requiresAuthentication() throws Exception {\n\t\tthis.mvc.perform(get(\"/\")).andExpect(status().isFound());\n\t}\n\n\t@Test\n\tpublic void httpBasicAuthenticationSuccess() throws Exception {\n\t\tthis.mvc.perform(get(\"/secured/butnotfound\").with(httpBasic(\"user\", \"password\")))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void authenticationSuccess() throws Exception {\n\t\tthis.mvc.perform(formLogin())\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void authenticationFailed() throws Exception {\n\t\tthis.mvc.perform(formLogin().user(\"user\").password(\"invalid\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/login?error\"))\n\t\t\t.andExpect(unauthenticated());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tDefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((authorize) -> authorize\n\t\t\t\t\t.anyRequest().authenticated()\n\t\t\t\t)\n\t\t\t\t.sessionManagement((sessions) -> sessions\n\t\t\t\t\t.requireExplicitAuthenticationStrategy(false)\n\t\t\t\t)\n\t\t\t\t.httpBasic(withDefaults())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\t// @formatter:off\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder().username(\"user\").password(\"password\").roles(\"USER\").build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomConfigAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.login;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = CustomConfigAuthenticationTests.Config.class)\n@WebAppConfiguration\npublic class CustomConfigAuthenticationTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\t@Autowired\n\tprivate SecurityContextRepository securityContextRepository;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t@Test\n\tpublic void authenticationSuccess() throws Exception {\n\t\tthis.mvc.perform(formLogin(\"/authenticate\").user(\"user\", \"user\").password(\"pass\", \"password\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void withUserSuccess() throws Exception {\n\t\tthis.mvc.perform(get(\"/\").with(user(\"user\")))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void authenticationFailed() throws Exception {\n\t\tthis.mvc.perform(formLogin(\"/authenticate\").user(\"user\", \"notfound\").password(\"pass\", \"invalid\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/authenticate?error\"))\n\t\t\t.andExpect(unauthenticated());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.securityContext((context) -> context\n\t\t\t\t\t.securityContextRepository(securityContextRepository()))\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.usernameParameter(\"user\")\n\t\t\t\t\t.passwordParameter(\"pass\")\n\t\t\t\t\t.loginPage(\"/authenticate\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder().username(\"user\").password(\"password\").roles(\"USER\").build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\t\t// @formatter:on\n\t\t@Bean\n\t\tSecurityContextRepository securityContextRepository() {\n\t\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\t\trepo.setSpringSecurityContextKey(\"CUSTOM\");\n\t\t\treturn repo;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/login/CustomLoginRequestBuilderAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.login;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.FormLoginRequestBuilder;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = CustomLoginRequestBuilderAuthenticationTests.Config.class)\n@WebAppConfiguration\npublic class CustomLoginRequestBuilderAuthenticationTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t@Test\n\tpublic void authenticationSuccess() throws Exception {\n\t\tthis.mvc.perform(login())\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/\"))\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void authenticationFailed() throws Exception {\n\t\tthis.mvc.perform(login().user(\"notfound\").password(\"invalid\"))\n\t\t\t.andExpect(status().isFound())\n\t\t\t.andExpect(redirectedUrl(\"/authenticate?error\"))\n\t\t\t.andExpect(unauthenticated());\n\t}\n\n\tstatic FormLoginRequestBuilder login() {\n\t\treturn formLogin(\"/authenticate\").userParameter(\"user\").passwordParam(\"pass\");\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin((login) -> login\n\t\t\t\t\t.usernameParameter(\"user\")\n\t\t\t\t\t.passwordParameter(\"pass\")\n\t\t\t\t\t.loginPage(\"/authenticate\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t// @formatter:off\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\tUserDetails user = User.withDefaultPasswordEncoder().username(\"user\").password(\"password\").roles(\"USER\").build();\n\t\t\treturn new InMemoryUserDetailsManager(user);\n\t\t}\n\t\t// @formatter:on\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/DefaultfSecurityRequestsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.secured;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.anonymous;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = DefaultfSecurityRequestsTests.Config.class)\n@WebAppConfiguration\npublic class DefaultfSecurityRequestsTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.defaultRequest(get(\"/\").with(user(\"user\").roles(\"ADMIN\")))\n\t\t\t.apply(springSecurity())\n\t\t\t.build();\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithUser() throws Exception {\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithAdmin() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithAnonymous() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin\").with(anonymous()))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(unauthenticated());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/SecurityRequestsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.secured;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;\nimport static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = SecurityRequestsTests.Config.class)\n@WebAppConfiguration\npublic class SecurityRequestsTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\t@Autowired\n\tprivate UserDetailsService userDetailsService;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithUser() throws Exception {\n\t\tthis.mvc.perform(get(\"/\").with(user(\"user\")))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithAdmin() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin\").with(user(\"admin\").roles(\"ADMIN\")))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with admin\n\t\t\t.andExpect(authenticated().withUsername(\"admin\"));\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithUserDetails() throws Exception {\n\t\tUserDetails user = this.userDetailsService.loadUserByUsername(\"user\");\n\t\tthis.mvc.perform(get(\"/\").with(user(user)))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withAuthenticationPrincipal(user));\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithAuthentication() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"notused\", \"ROLE_USER\");\n\t\tthis.mvc.perform(get(\"/\").with(authentication(authentication)))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withAuthentication(authentication));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithAdminRob.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.secured;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.security.test.context.support.WithMockUser;\n\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\n@WithMockUser(value = \"rob\", roles = \"ADMIN\")\npublic @interface WithAdminRob {\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.secured;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = WithUserAuthenticationTests.Config.class)\n@WebAppConfiguration\npublic class WithUserAuthenticationTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context)\n\t\t\t.apply(SecurityMockMvcConfigurers.springSecurity())\n\t\t\t.build();\n\t}\n\n\t@Test\n\t@WithMockUser\n\tpublic void requestProtectedUrlWithUser() throws Exception {\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\t@WithAdminRob\n\tpublic void requestProtectedUrlWithAdminRob() throws Exception {\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"rob\").withRoles(\"ADMIN\"));\n\t}\n\n\t@Test\n\t@WithMockUser(roles = \"ADMIN\")\n\tpublic void requestProtectedUrlWithAdmin() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"user\").withRoles(\"ADMIN\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserClassLevelAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.secured;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.test.context.support.WithAnonymousUser;\nimport org.springframework.security.test.context.support.WithMockUser;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = WithUserClassLevelAuthenticationTests.Config.class)\n@WebAppConfiguration\n@WithMockUser(roles = \"ADMIN\")\npublic class WithUserClassLevelAuthenticationTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithUser() throws Exception {\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithAdmin() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"user\").withRoles(\"ADMIN\"));\n\t}\n\n\t@Test\n\t@WithAnonymousUser\n\tpublic void requestProtectedUrlWithAnonymous() throws Exception {\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t// Ensure did not get past security\n\t\t\t.andExpect(status().isUnauthorized())\n\t\t\t// Ensure not authenticated\n\t\t\t.andExpect(unauthenticated());\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.httpBasic(withDefaults());\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Autowired\n\t\tvoid configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n\t\t\t// @formatter:off\n\t\t\tauth\n\t\t\t\t.inMemoryAuthentication()\n\t\t\t\t\t.withUser(\"user\").password(\"password\").roles(\"USER\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.secured;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.context.support.WithUserDetails;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = WithUserDetailsAuthenticationTests.Config.class)\n@WebAppConfiguration\npublic class WithUserDetailsAuthenticationTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t@Test\n\t@WithUserDetails\n\tpublic void requestProtectedUrlWithUser() throws Exception {\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"user\"));\n\t}\n\n\t@Test\n\t@WithUserDetails(\"admin\")\n\tpublic void requestProtectedUrlWithAdmin() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"admin\").withRoles(\"ADMIN\", \"USER\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsClassLevelAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.servlet.showcase.secured;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.provisioning.InMemoryUserDetailsManager;\nimport org.springframework.security.test.context.support.WithUserDetails;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.context.web.WebAppConfiguration;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.servlet.config.annotation.EnableWebMvc;\n\nimport static org.springframework.security.config.Customizer.withDefaults;\nimport static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;\nimport static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = WithUserDetailsClassLevelAuthenticationTests.Config.class)\n@WebAppConfiguration\n@WithUserDetails(\"admin\")\npublic class WithUserDetailsClassLevelAuthenticationTests {\n\n\t@Autowired\n\tprivate WebApplicationContext context;\n\n\tprivate MockMvc mvc;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build();\n\t}\n\n\t@Test\n\tpublic void requestRootUrlWithAdmin() throws Exception {\n\t\tthis.mvc.perform(get(\"/\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"admin\").withRoles(\"ADMIN\", \"USER\"));\n\t}\n\n\t@Test\n\tpublic void requestProtectedUrlWithAdmin() throws Exception {\n\t\tthis.mvc.perform(get(\"/admin\"))\n\t\t\t// Ensure we got past Security\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t// Ensure it appears we are authenticated with user\n\t\t\t.andExpect(authenticated().withUsername(\"admin\").withRoles(\"ADMIN\", \"USER\"));\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\t@EnableWebMvc\n\tstatic class Config {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.authorizeHttpRequests((requests) -> requests\n\t\t\t\t\t.requestMatchers(\"/admin/**\").hasRole(\"ADMIN\")\n\t\t\t\t\t.anyRequest().authenticated())\n\t\t\t\t.formLogin(withDefaults());\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t\t@Bean\n\t\tUserDetailsService userDetailsService() {\n\t\t\treturn new InMemoryUserDetailsManager(PasswordEncodedUser.user(), PasswordEncodedUser.admin());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/java/org/springframework/security/test/web/support/WebTestUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.support;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.context.DelegatingSecurityContextRepository;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextHolderFilter;\nimport org.springframework.security.web.context.SecurityContextPersistenceFilter;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.csrf.CsrfFilter;\nimport org.springframework.security.web.csrf.CsrfTokenRepository;\nimport org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n@ExtendWith(MockitoExtension.class)\npublic class WebTestUtilsTests {\n\n\t@Mock\n\tprivate SecurityContextRepository contextRepo;\n\n\t@Mock\n\tprivate CsrfTokenRepository csrfRepo;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate ConfigurableApplicationContext context;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tif (this.context != null) {\n\t\t\tthis.context.close();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getCsrfTokenRepositorytNoWac() {\n\t\tassertThat(WebTestUtils.getCsrfTokenRepository(this.request))\n\t\t\t.isInstanceOf(HttpSessionCsrfTokenRepository.class);\n\t}\n\n\t@Test\n\tpublic void getCsrfTokenRepositorytNoSecurity() {\n\t\tloadConfig(Config.class);\n\t\tassertThat(WebTestUtils.getCsrfTokenRepository(this.request))\n\t\t\t.isInstanceOf(HttpSessionCsrfTokenRepository.class);\n\t}\n\n\t@Test\n\tpublic void getCsrfTokenRepositorytSecurityNoCsrf() {\n\t\tloadConfig(SecurityNoCsrfConfig.class);\n\t\tassertThat(WebTestUtils.getCsrfTokenRepository(this.request))\n\t\t\t.isInstanceOf(HttpSessionCsrfTokenRepository.class);\n\t}\n\n\t@Test\n\tpublic void getCsrfTokenRepositorytSecurityCustomRepo() {\n\t\tCustomSecurityConfig.CONTEXT_REPO = this.contextRepo;\n\t\tCustomSecurityConfig.CSRF_REPO = this.csrfRepo;\n\t\tloadConfig(CustomSecurityConfig.class);\n\t\tassertThat(WebTestUtils.getCsrfTokenRepository(this.request)).isSameAs(this.csrfRepo);\n\t}\n\n\t// getSecurityContextRepository\n\t@Test\n\tpublic void getSecurityContextRepositoryNoWac() {\n\t\tassertThat(WebTestUtils.getSecurityContextRepository(this.request))\n\t\t\t.isInstanceOf(HttpSessionSecurityContextRepository.class);\n\t}\n\n\t@Test\n\tpublic void getSecurityContextRepositoryNoSecurity() {\n\t\tloadConfig(Config.class);\n\t\tassertThat(WebTestUtils.getSecurityContextRepository(this.request))\n\t\t\t.isInstanceOf(HttpSessionSecurityContextRepository.class);\n\t}\n\n\t@Test\n\tpublic void getSecurityContextRepositorySecurityNoCsrf() {\n\t\tloadConfig(SecurityNoCsrfConfig.class);\n\t\tassertThat(WebTestUtils.getSecurityContextRepository(this.request))\n\t\t\t.isInstanceOf(DelegatingSecurityContextRepository.class);\n\t}\n\n\t@Test\n\tpublic void getSecurityContextRepositorySecurityCustomRepo() {\n\t\tCustomSecurityConfig.CONTEXT_REPO = this.contextRepo;\n\t\tCustomSecurityConfig.CSRF_REPO = this.csrfRepo;\n\t\tloadConfig(CustomSecurityConfig.class);\n\t\tassertThat(WebTestUtils.getSecurityContextRepository(this.request)).isSameAs(this.contextRepo);\n\t}\n\n\t@Test\n\tpublic void setSecurityContextRepositoryWhenSecurityContextHolderFilter() {\n\t\tSecurityContextRepository expectedRepository = mock(SecurityContextRepository.class);\n\t\tloadConfig(SecurityContextHolderFilterConfig.class);\n\t\t// verify our configuration sets up to have SecurityContextHolderFilter and not\n\t\t// SecurityContextPersistenceFilter\n\t\tassertThat(WebTestUtils.findFilter(this.request, SecurityContextPersistenceFilter.class)).isNull();\n\t\tassertThat(WebTestUtils.findFilter(this.request, SecurityContextHolderFilter.class)).isNotNull();\n\n\t\tWebTestUtils.setSecurityContextRepository(this.request, expectedRepository);\n\t\tassertThat(WebTestUtils.getSecurityContextRepository(this.request)).isSameAs(expectedRepository);\n\t}\n\n\t// gh-3343\n\t@Test\n\tpublic void findFilterNoMatchingFilters() {\n\t\tloadConfig(PartialSecurityConfig.class);\n\t\tassertThat(WebTestUtils.findFilter(this.request, SecurityContextPersistenceFilter.class)).isNull();\n\t}\n\n\t@Test\n\tpublic void findFilterNoSpringSecurityFilterChainInContext() {\n\t\tloadConfig(NoSecurityConfig.class);\n\t\tCsrfFilter toFind = new CsrfFilter(new HttpSessionCsrfTokenRepository());\n\t\tFilterChainProxy springSecurityFilterChain = new FilterChainProxy(\n\t\t\t\tnew DefaultSecurityFilterChain(AnyRequestMatcher.INSTANCE, toFind));\n\t\tthis.request.getServletContext().setAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN, springSecurityFilterChain);\n\t\tassertThat(WebTestUtils.findFilter(this.request, toFind.getClass())).isEqualTo(toFind);\n\t}\n\n\t@Test\n\tpublic void findFilterExplicitWithSecurityFilterInContext() {\n\t\tloadConfig(SecurityConfigWithDefaults.class);\n\t\tCsrfFilter toFind = new CsrfFilter(new HttpSessionCsrfTokenRepository());\n\t\tFilterChainProxy springSecurityFilterChain = new FilterChainProxy(\n\t\t\t\tnew DefaultSecurityFilterChain(AnyRequestMatcher.INSTANCE, toFind));\n\t\tthis.request.getServletContext().setAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN, springSecurityFilterChain);\n\t\tassertThat(WebTestUtils.findFilter(this.request, toFind.getClass())).isSameAs(toFind);\n\t}\n\n\tprivate void loadConfig(Class<?> config) {\n\t\tAnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();\n\t\tcontext.register(config);\n\t\tcontext.refresh();\n\t\tthis.context = context;\n\t\tthis.request.getServletContext()\n\t\t\t.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);\n\t}\n\n\t@Configuration\n\tstatic class Config {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityNoCsrfConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\thttp.csrf((csrf) -> csrf.disable());\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class CustomSecurityConfig {\n\n\t\tstatic CsrfTokenRepository CSRF_REPO;\n\t\tstatic SecurityContextRepository CONTEXT_REPO;\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.csrf((csrf) -> csrf\n\t\t\t\t\t.csrfTokenRepository(CSRF_REPO))\n\t\t\t\t.securityContext((context) -> context\n\t\t\t\t\t.securityContextRepository(CONTEXT_REPO));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class PartialSecurityConfig {\n\n\t\t@Bean\n\t\tSecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityMatcher(pathPattern(\"/willnotmatchthis\"));\n\t\t\treturn http.build();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Configuration\n\tstatic class NoSecurityConfig {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityConfigWithDefaults {\n\n\t}\n\n\t@Configuration\n\t@EnableWebSecurity\n\tstatic class SecurityContextHolderFilterConfig {\n\n\t\t@Bean\n\t\tDefaultSecurityFilterChain springSecurityFilter(HttpSecurity http) throws Exception {\n\t\t\t// @formatter:off\n\t\t\thttp\n\t\t\t\t.securityContext((securityContext) -> securityContext.requireExplicitSave(true));\n\t\t\t// @formatter:on\n\t\t\treturn http.build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "test/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "test/template.mf",
    "content": "Implementation-Title: org.springframework.security.test\nImplementation-Version: ${version}\nBundle-SymbolicName: org.springframework.security.test\nBundle-Name: Spring Security Test\nBundle-Vendor: SpringSource\nBundle-Version: ${version}\nBundle-ManifestVersion: 2\nIgnored-Existing-Headers:\n Import-Package,\n Export-Package\nImport-Template:\n org.apache.commons.logging.*;version=\"${cloggingRange}\",\n org.springframework.security.core.*;version=\"${secRange}\",\n org.springframework.security.authentication.*;version=\"${secRange}\",\n org.springframework.security.web.*;version=\"${secRange}\",\n org.springframework.beans.factory;version=\"${springRange}\",\n org.springframework.util;version=\"${springRange}\",\n javax.servlet.*;version=\"0\"\n"
  },
  {
    "path": "web/spring-security-web.gradle",
    "content": "plugins {\n\tid 'io.spring.convention.spring-module'\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n\tid 'test-compile-target-jdk25'\n}\n\nconfigurations {\n\tjavascript {\n\t\tcanBeConsumed = false\n\t}\n}\n\ndef syncJavascript = tasks.register('syncJavascript', Sync) {\n\tgroup = 'Build'\n\tdescription = 'Syncs the Javascript from the javascript configuration'\n\tinto project.layout.buildDirectory.dir('spring-security-javascript')\n\tfrom(configurations.javascript) {\n\t\tinto 'org/springframework/security'\n\t}\n}\n\n\nsourceSets {\n\tmain {\n\t\tresources {\n\t\t\tsrcDirs(syncJavascript)\n\t\t}\n\t}\n}\n\ndependencies {\n\tjavascript project(path: ':spring-security-javascript', configuration: 'javascript')\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-aop'\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-context'\n\tapi 'org.springframework:spring-expression'\n\tapi 'org.springframework:spring-web'\n\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\toptional 'io.micrometer:context-propagation'\n\toptional 'io.projectreactor:reactor-core'\n\toptional 'org.springframework:spring-jdbc'\n\toptional 'org.springframework:spring-tx'\n\toptional 'org.springframework:spring-webflux'\n\toptional 'org.springframework:spring-webmvc'\n\toptional 'tools.jackson.core:jackson-databind'\n\toptional libs.webauthn4j.core\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(path: ':spring-security-core', configuration: 'tests')\n\ttestImplementation 'io.projectreactor:reactor-test'\n\ttestImplementation 'jakarta.xml.bind:jakarta.xml.bind-api'\n\ttestImplementation 'jakarta.websocket:jakarta.websocket-api'\n\ttestImplementation 'jakarta.websocket:jakarta.websocket-client-api'\n\ttestImplementation 'org.hamcrest:hamcrest'\n\ttestImplementation 'org.mockito:mockito-core'\n\ttestImplementation 'org.skyscreamer:jsonassert'\n\ttestImplementation 'org.springframework:spring-webflux'\n\ttestImplementation 'org.synchronoss.cloud:nio-multipart-parser'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/AuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\n\n/**\n * Used by {@link ExceptionTranslationFilter} to commence an authentication scheme.\n *\n * @author Ben Alex\n */\npublic interface AuthenticationEntryPoint {\n\n\t/**\n\t * Commences an authentication scheme.\n\t * <p>\n\t * <code>ExceptionTranslationFilter</code> will populate the <code>HttpSession</code>\n\t * attribute named\n\t * <code>AbstractAuthenticationProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY</code>\n\t * with the requested target URL before calling this method.\n\t * <p>\n\t * Implementations should modify the headers on the <code>ServletResponse</code> as\n\t * necessary to commence the authentication process.\n\t * @param request that resulted in an <code>AuthenticationException</code>\n\t * @param response so that the user agent can begin authentication\n\t * @param authException that caused the invocation\n\t */\n\tvoid commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)\n\t\t\tthrows IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * Simple implementation of <tt>RedirectStrategy</tt> which is the default used throughout\n * the framework.\n *\n * @author Luke Taylor\n * @author Mark Chesney\n * @since 3.0\n */\npublic class DefaultRedirectStrategy implements RedirectStrategy {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate boolean contextRelative;\n\n\tprivate HttpStatus statusCode = HttpStatus.FOUND;\n\n\t/**\n\t * Redirects the response to the supplied URL.\n\t * <p>\n\t * If <tt>contextRelative</tt> is set, the redirect value will be the value after the\n\t * request context path. Note that this will result in the loss of protocol\n\t * information (HTTP or HTTPS), so will cause problems if a redirect is being\n\t * performed to change to HTTPS, for example.\n\t */\n\t@Override\n\tpublic void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {\n\t\tString redirectUrl = calculateRedirectUrl(request.getContextPath(), url);\n\t\tredirectUrl = response.encodeRedirectURL(redirectUrl);\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Redirecting to %s\", redirectUrl));\n\t\t}\n\t\tif (this.statusCode == HttpStatus.FOUND) {\n\t\t\tresponse.sendRedirect(redirectUrl);\n\t\t}\n\t\telse {\n\t\t\tresponse.setHeader(HttpHeaders.LOCATION, redirectUrl);\n\t\t\tresponse.setStatus(this.statusCode.value());\n\t\t\tresponse.getWriter().flush();\n\t\t}\n\t}\n\n\tprotected String calculateRedirectUrl(String contextPath, String url) {\n\t\tif (!UrlUtils.isAbsoluteUrl(url)) {\n\t\t\tif (isContextRelative()) {\n\t\t\t\treturn url;\n\t\t\t}\n\t\t\treturn contextPath + url;\n\t\t}\n\t\t// Full URL, including http(s)://\n\t\tif (!isContextRelative()) {\n\t\t\treturn url;\n\t\t}\n\t\tAssert.isTrue(url.contains(contextPath), \"The fully qualified URL does not include context path.\");\n\t\t// Calculate the relative URL from the fully qualified URL, minus the last\n\t\t// occurrence of the scheme and base context.\n\t\turl = url.substring(url.lastIndexOf(\"://\") + 3);\n\t\turl = url.substring(url.indexOf(contextPath) + contextPath.length());\n\t\tif (url.length() > 1 && url.charAt(0) == '/') {\n\t\t\turl = url.substring(1);\n\t\t}\n\t\treturn url;\n\t}\n\n\t/**\n\t * If <tt>true</tt>, causes any redirection URLs to be calculated minus the protocol\n\t * and context path (defaults to <tt>false</tt>).\n\t */\n\tpublic void setContextRelative(boolean useRelativeContext) {\n\t\tthis.contextRelative = useRelativeContext;\n\t}\n\n\t/**\n\t * Returns <tt>true</tt>, if the redirection URL should be calculated minus the\n\t * protocol and context path (defaults to <tt>false</tt>).\n\t */\n\tprotected boolean isContextRelative() {\n\t\treturn this.contextRelative;\n\t}\n\n\t/**\n\t * Sets the HTTP status code to use. The default is {@link HttpStatus#FOUND}.\n\t * <p>\n\t * Note that according to RFC 7231, with {@link HttpStatus#FOUND}, a user agent MAY\n\t * change the request method from POST to GET for the subsequent request. If this\n\t * behavior is undesired, {@link HttpStatus#TEMPORARY_REDIRECT} can be used instead.\n\t * @param statusCode the HTTP status code to use.\n\t * @since 6.2\n\t */\n\tpublic void setStatusCode(HttpStatus statusCode) {\n\t\tAssert.notNull(statusCode, \"statusCode cannot be null\");\n\t\tthis.statusCode = statusCode;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/DefaultSecurityFilterChain.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.BeanNameAware;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.StringUtils;\n\n/**\n * Standard implementation of {@code SecurityFilterChain}.\n *\n * @author Luke Taylor\n * @author Jinwoo Bae\n * @since 3.1\n */\npublic final class DefaultSecurityFilterChain implements SecurityFilterChain, BeanNameAware, BeanFactoryAware {\n\n\tprivate static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);\n\n\tprivate final RequestMatcher requestMatcher;\n\n\tprivate final List<Filter> filters;\n\n\tprivate @Nullable String beanName;\n\n\tprivate @Nullable ConfigurableListableBeanFactory beanFactory;\n\n\tpublic DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {\n\t\tthis(requestMatcher, Arrays.asList(filters));\n\t}\n\n\tpublic DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {\n\t\tif (filters.isEmpty()) {\n\t\t\tlogger.debug(LogMessage.format(\"Will not secure %s\", requestMatcher));\n\t\t}\n\t\telse {\n\t\t\tList<String> filterNames = new ArrayList<>();\n\t\t\tfor (Filter filter : filters) {\n\t\t\t\tfilterNames.add(filter.getClass().getSimpleName());\n\t\t\t}\n\t\t\tString names = StringUtils.collectionToDelimitedString(filterNames, \", \");\n\t\t\tlogger.debug(LogMessage.format(\"Will secure %s with filters: %s\", requestMatcher, names));\n\t\t}\n\t\tthis.requestMatcher = requestMatcher;\n\t\tthis.filters = new ArrayList<>(filters);\n\t}\n\n\tpublic RequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\t@Override\n\tpublic List<Filter> getFilters() {\n\t\treturn this.filters;\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\treturn this.requestMatcher.matches(request);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tList<String> filterNames = new ArrayList<>();\n\t\tfor (Filter filter : this.filters) {\n\t\t\tString name = filter.getClass().getSimpleName();\n\t\t\tif (name.endsWith(\"Filter\")) {\n\t\t\t\tname = name.substring(0, name.length() - \"Filter\".length());\n\t\t\t}\n\t\t\tfilterNames.add(name);\n\t\t}\n\t\tString declaration = this.getClass().getSimpleName();\n\t\tif (this.beanName != null) {\n\t\t\tdeclaration += \" defined as '\" + this.beanName + \"'\";\n\t\t\tif (this.beanFactory != null) {\n\t\t\t\tBeanDefinition bd = this.beanFactory.getBeanDefinition(this.beanName);\n\t\t\t\tString description = bd.getResourceDescription();\n\t\t\t\tif (description != null) {\n\t\t\t\t\tdeclaration += \" in [\" + description + \"]\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn declaration + \" matching [\" + this.requestMatcher + \"] and having filters \" + filterNames;\n\t}\n\n\t@Override\n\tpublic void setBeanName(@NonNull String name) {\n\t\tthis.beanName = name;\n\t}\n\n\t@Override\n\tpublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n\t\tif (beanFactory instanceof ConfigurableListableBeanFactory listable) {\n\t\t\tthis.beanFactory = listable;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/FilterChainProxy.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.access.PathPatternRequestTransformer;\nimport org.springframework.security.web.firewall.FirewalledRequest;\nimport org.springframework.security.web.firewall.HttpFirewall;\nimport org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler;\nimport org.springframework.security.web.firewall.RequestRejectedException;\nimport org.springframework.security.web.firewall.RequestRejectedHandler;\nimport org.springframework.security.web.firewall.StrictHttpFirewall;\nimport org.springframework.security.web.util.ThrowableAnalyzer;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.DelegatingFilterProxy;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Delegates {@code Filter} requests to a list of Spring-managed filter beans. As of\n * version 2.0, you shouldn't need to explicitly configure a {@code FilterChainProxy} bean\n * in your application context unless you need very fine control over the filter chain\n * contents. Most cases should be adequately covered by the default\n * {@code <security:http />} namespace configuration options.\n * <p>\n * The {@code FilterChainProxy} is linked into the servlet container filter chain by\n * adding a standard Spring {@link DelegatingFilterProxy} declaration in the application\n * {@code web.xml} file.\n *\n * <h2>Configuration</h2>\n * <p>\n * As of version 3.1, {@code FilterChainProxy} is configured using a list of\n * {@link SecurityFilterChain} instances, each of which contains a {@link RequestMatcher}\n * and a list of filters which should be applied to matching requests. Most applications\n * will only contain a single filter chain, and if you are using the namespace, you don't\n * have to set the chains explicitly. If you require finer-grained control, you can make\n * use of the {@code <filter-chain>} namespace element. This defines a URI pattern and the\n * list of filters (as comma-separated bean names) which should be applied to requests\n * which match the pattern. An example configuration might look like this:\n *\n * <pre>\n *  &lt;bean id=\"myfilterChainProxy\" class=\"org.springframework.security.web.FilterChainProxy\"&gt;\n *      &lt;constructor-arg&gt;\n *          &lt;util:list&gt;\n *              &lt;security:filter-chain pattern=\"/do/not/filter*\" filters=\"none\"/&gt;\n *              &lt;security:filter-chain pattern=\"/**\" filters=\"filter1,filter2,filter3\"/&gt;\n *          &lt;/util:list&gt;\n *      &lt;/constructor-arg&gt;\n *  &lt;/bean&gt;\n * </pre>\n *\n * The names \"filter1\", \"filter2\", \"filter3\" should be the bean names of {@code Filter}\n * instances defined in the application context. The order of the names defines the order\n * in which the filters will be applied. As shown above, use of the value \"none\" for the\n * \"filters\" can be used to exclude a request pattern from the security filter chain\n * entirely. Please consult the security namespace schema file for a full list of\n * available configuration options.\n *\n * <h2>Request Handling</h2>\n * <p>\n * Each possible pattern that the {@code FilterChainProxy} should service must be entered.\n * The first match for a given request will be used to define all of the {@code Filter}s\n * that apply to that request. This means you must put most specific matches at the top of\n * the list, and ensure all {@code Filter}s that should apply for a given matcher are\n * entered against the respective entry. The {@code FilterChainProxy} will not iterate\n * through the remainder of the map entries to locate additional {@code Filter}s.\n * <p>\n * {@code FilterChainProxy} respects normal handling of {@code Filter}s that elect not to\n * call\n * {@link jakarta.servlet.Filter#doFilter(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse, jakarta.servlet.FilterChain)}\n * , in that the remainder of the original or {@code FilterChainProxy}-declared filter\n * chain will not be called.\n *\n * <h3>Request Firewalling</h3>\n *\n * An {@link HttpFirewall} instance is used to validate incoming requests and create a\n * wrapped request which provides consistent path values for matching against. See\n * {@link StrictHttpFirewall}, for more information on the type of attacks which the\n * default implementation protects against. A custom implementation can be injected to\n * provide stricter control over the request contents or if an application needs to\n * support certain types of request which are rejected by default.\n * <p>\n * Note that this means that you must use the Spring Security filters in combination with\n * a {@code FilterChainProxy} if you want this protection. Don't define them explicitly in\n * your {@code web.xml} file.\n * <p>\n * {@code FilterChainProxy} will use the firewall instance to obtain both request and\n * response objects which will be fed down the filter chain, so it is also possible to use\n * this functionality to control the functionality of the response. When the request has\n * passed through the security filter chain, the {@code reset} method will be called. With\n * the default implementation this means that the original values of {@code servletPath}\n * and {@code pathInfo} will be returned thereafter, instead of the modified ones used for\n * security pattern matching.\n * <p>\n * Since this additional wrapping functionality is performed by the\n * {@code FilterChainProxy}, we don't recommend that you use multiple instances in the\n * same filter chain. It shouldn't be considered purely as a utility for wrapping filter\n * beans in a single {@code Filter} instance.\n *\n * <h2>Filter Lifecycle</h2>\n * <p>\n * Note the {@code Filter} lifecycle mismatch between the servlet container and IoC\n * container. As described in the {@link DelegatingFilterProxy} Javadocs, we recommend you\n * allow the IoC container to manage the lifecycle instead of the servlet container.\n * {@code FilterChainProxy} does not invoke the standard filter lifecycle methods on any\n * filter beans that you add to the application context.\n *\n * @author Carlos Sanchez\n * @author Ben Alex\n * @author Luke Taylor\n * @author Rob Winch\n */\npublic class FilterChainProxy extends GenericFilterBean {\n\n\tprivate static final Log logger = LogFactory.getLog(FilterChainProxy.class);\n\n\tprivate static final String FILTER_APPLIED = FilterChainProxy.class.getName().concat(\".APPLIED\");\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate List<SecurityFilterChain> filterChains;\n\n\tprivate FilterChainValidator filterChainValidator = new NullFilterChainValidator();\n\n\tprivate HttpFirewall firewall = new StrictHttpFirewall();\n\n\tprivate RequestRejectedHandler requestRejectedHandler = new HttpStatusRequestRejectedHandler();\n\n\tprivate ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();\n\n\tprivate FilterChainDecorator filterChainDecorator = new VirtualFilterChainDecorator();\n\n\tpublic FilterChainProxy() {\n\t\tthis(Collections.emptyList());\n\t}\n\n\tpublic FilterChainProxy(SecurityFilterChain chain) {\n\t\tthis(Arrays.asList(chain));\n\t}\n\n\tpublic FilterChainProxy(List<SecurityFilterChain> filterChains) {\n\t\tAssert.notNull(filterChains, \"filterChains cannot be null\");\n\t\tthis.filterChains = filterChains;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tthis.filterChainValidator.validate(this);\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tboolean clearContext = request.getAttribute(FILTER_APPLIED) == null;\n\t\tif (!clearContext) {\n\t\t\tdoFilterInternal(request, response, chain);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\trequest.setAttribute(FILTER_APPLIED, Boolean.TRUE);\n\t\t\tdoFilterInternal(request, response, chain);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tThrowable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);\n\t\t\tThrowable requestRejectedException = this.throwableAnalyzer\n\t\t\t\t.getFirstThrowableOfType(RequestRejectedException.class, causeChain);\n\t\t\tif (!(requestRejectedException instanceof RequestRejectedException)) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tthis.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response,\n\t\t\t\t\t(RequestRejectedException) requestRejectedException);\n\t\t}\n\t\tfinally {\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\trequest.removeAttribute(FILTER_APPLIED);\n\t\t}\n\t}\n\n\tprivate void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tFirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);\n\t\tHttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);\n\t\tList<Filter> filters = getFilters(firewallRequest);\n\t\tif (filters == null || filters.isEmpty()) {\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(LogMessage.of(() -> \"No security for \" + requestLine(firewallRequest)));\n\t\t\t}\n\t\t\tfirewallRequest.reset();\n\t\t\tthis.filterChainDecorator.decorate(chain).doFilter(firewallRequest, firewallResponse);\n\t\t\treturn;\n\t\t}\n\t\tif (logger.isDebugEnabled()) {\n\t\t\tlogger.debug(LogMessage.of(() -> \"Securing \" + requestLine(firewallRequest)));\n\t\t}\n\t\tFilterChain reset = (req, res) -> {\n\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\tlogger.debug(LogMessage.of(() -> \"Secured \" + requestLine(firewallRequest)));\n\t\t\t}\n\t\t\t// Deactivate path stripping as we exit the security filter chain\n\t\t\tfirewallRequest.reset();\n\t\t\tchain.doFilter(req, res);\n\t\t};\n\t\tthis.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse);\n\t}\n\n\t/**\n\t * Returns the first filter chain matching the supplied URL.\n\t * @param request the request to match\n\t * @return an ordered array of Filters defining the filter chain\n\t */\n\tprivate @Nullable List<Filter> getFilters(HttpServletRequest request) {\n\t\tint count = 0;\n\t\tfor (SecurityFilterChain chain : this.filterChains) {\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(LogMessage.format(\"Trying to match request against %s (%d/%d)\", chain, ++count,\n\t\t\t\t\t\tthis.filterChains.size()));\n\t\t\t}\n\t\t\tif (chain.matches(request)) {\n\t\t\t\treturn chain.getFilters();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Convenience method, mainly for testing.\n\t * @param url the URL\n\t * @return matching filter list\n\t */\n\tpublic @Nullable List<Filter> getFilters(String url) {\n\t\tPathPatternRequestTransformer requestTransformer = new PathPatternRequestTransformer();\n\t\tHttpServletRequest transformed = requestTransformer.transform(new FilterInvocation(url, \"GET\").getRequest());\n\t\treturn getFilters(this.firewall.getFirewalledRequest(transformed));\n\t}\n\n\t/**\n\t * @return the list of {@code SecurityFilterChain}s which will be matched against and\n\t * applied to incoming requests.\n\t */\n\tpublic List<SecurityFilterChain> getFilterChains() {\n\t\treturn Collections.unmodifiableList(this.filterChains);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Used (internally) to specify a validation strategy for the filters in each\n\t * configured chain.\n\t * @param filterChainValidator the validator instance which will be invoked on during\n\t * initialization to check the {@code FilterChainProxy} instance.\n\t */\n\tpublic void setFilterChainValidator(FilterChainValidator filterChainValidator) {\n\t\tthis.filterChainValidator = filterChainValidator;\n\t}\n\n\t/**\n\t * Used to decorate the original {@link FilterChain} for each request\n\t *\n\t * <p>\n\t * By default, this decorates the filter chain with a {@link VirtualFilterChain} that\n\t * iterates through security filters and then delegates to the original chain\n\t * @param filterChainDecorator the strategy for constructing the filter chain\n\t * @since 6.0\n\t */\n\tpublic void setFilterChainDecorator(FilterChainDecorator filterChainDecorator) {\n\t\tAssert.notNull(filterChainDecorator, \"filterChainDecorator cannot be null\");\n\t\tthis.filterChainDecorator = filterChainDecorator;\n\t}\n\n\t/**\n\t * Sets the \"firewall\" implementation which will be used to validate and wrap (or\n\t * potentially reject) the incoming requests. The default implementation should be\n\t * satisfactory for most requirements.\n\t * @param firewall\n\t */\n\tpublic void setFirewall(HttpFirewall firewall) {\n\t\tthis.firewall = firewall;\n\t}\n\n\t/**\n\t * Sets the {@link RequestRejectedHandler} to be used for requests rejected by the\n\t * firewall.\n\t * @param requestRejectedHandler the {@link RequestRejectedHandler}\n\t * @since 5.2\n\t */\n\tpublic void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {\n\t\tAssert.notNull(requestRejectedHandler, \"requestRejectedHandler may not be null\");\n\t\tthis.requestRejectedHandler = requestRejectedHandler;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"FilterChainProxy[\");\n\t\tsb.append(\"Filter Chains: \");\n\t\tsb.append(this.filterChains);\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n\tprivate static String requestLine(HttpServletRequest request) {\n\t\treturn request.getMethod() + \" \" + UrlUtils.buildRequestUrl(request);\n\t}\n\n\t/**\n\t * Internal {@code FilterChain} implementation that is used to pass a request through\n\t * the additional internal list of filters which match the request.\n\t */\n\tprivate static final class VirtualFilterChain implements FilterChain {\n\n\t\tprivate final FilterChain originalChain;\n\n\t\tprivate final List<Filter> additionalFilters;\n\n\t\tprivate final int size;\n\n\t\tprivate int currentPosition = 0;\n\n\t\tprivate VirtualFilterChain(FilterChain chain, List<Filter> additionalFilters) {\n\t\t\tthis.originalChain = chain;\n\t\t\tthis.additionalFilters = additionalFilters;\n\t\t\tthis.size = additionalFilters.size();\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {\n\t\t\tif (this.currentPosition == this.size) {\n\t\t\t\tthis.originalChain.doFilter(request, response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.currentPosition++;\n\t\t\tFilter nextFilter = this.additionalFilters.get(this.currentPosition - 1);\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tString name = nextFilter.getClass().getSimpleName();\n\t\t\t\tlogger.trace(LogMessage.format(\"Invoking %s (%d/%d)\", name, this.currentPosition, this.size));\n\t\t\t}\n\t\t\tnextFilter.doFilter(request, response, this);\n\t\t}\n\n\t}\n\n\tpublic interface FilterChainValidator {\n\n\t\tvoid validate(FilterChainProxy filterChainProxy);\n\n\t}\n\n\tprivate static class NullFilterChainValidator implements FilterChainValidator {\n\n\t\t@Override\n\t\tpublic void validate(FilterChainProxy filterChainProxy) {\n\t\t}\n\n\t}\n\n\t/**\n\t * A strategy for decorating the provided filter chain with one that accounts for the\n\t * {@link SecurityFilterChain} for a given request.\n\t *\n\t * @author Josh Cummings\n\t * @since 6.0\n\t */\n\tpublic interface FilterChainDecorator {\n\n\t\t/**\n\t\t * Provide a new {@link FilterChain} that accounts for needed security\n\t\t * considerations when there are no security filters.\n\t\t * @param original the original {@link FilterChain}\n\t\t * @return a security-enabled {@link FilterChain}\n\t\t */\n\t\tdefault FilterChain decorate(FilterChain original) {\n\t\t\treturn decorate(original, Collections.emptyList());\n\t\t}\n\n\t\t/**\n\t\t * Provide a new {@link FilterChain} that accounts for the provided filters as\n\t\t * well as the original filter chain.\n\t\t * @param original the original {@link FilterChain}\n\t\t * @param filters the security filters\n\t\t * @return a security-enabled {@link FilterChain} that includes the provided\n\t\t * filters\n\t\t */\n\t\tFilterChain decorate(FilterChain original, List<Filter> filters);\n\n\t}\n\n\t/**\n\t * A {@link FilterChainDecorator} that uses the {@link VirtualFilterChain}\n\t *\n\t * @author Josh Cummings\n\t * @since 6.0\n\t */\n\tpublic static final class VirtualFilterChainDecorator implements FilterChainDecorator {\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic FilterChain decorate(FilterChain original) {\n\t\t\treturn original;\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic FilterChain decorate(FilterChain original, List<Filter> filters) {\n\t\t\treturn new VirtualFilterChain(original, filters);\n\t\t}\n\n\t}\n\n\tprivate static final class FirewallFilter implements Filter {\n\n\t\tprivate final HttpFirewall firewall;\n\n\t\tprivate FirewallFilter(HttpFirewall firewall) {\n\t\t\tthis.firewall = firewall;\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tHttpServletRequest request = (HttpServletRequest) servletRequest;\n\t\t\tHttpServletResponse response = (HttpServletResponse) servletResponse;\n\t\t\tfilterChain.doFilter(this.firewall.getFirewalledRequest(request),\n\t\t\t\t\tthis.firewall.getFirewalledResponse(response));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/FilterInvocation.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodHandles.Lookup;\nimport java.lang.invoke.MethodType;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Holds objects associated with a HTTP filter.\n * <P>\n * Guarantees the request and response are instances of <code>HttpServletRequest</code>\n * and <code>HttpServletResponse</code>, and that there are no <code>null</code> objects.\n * <p>\n * Required so that security system classes can obtain access to the filter environment,\n * as well as the request and response.\n *\n * @author Ben Alex\n * @author colin sampaleanu\n * @author Luke Taylor\n * @author Rob Winch\n */\npublic class FilterInvocation {\n\n\tstatic final FilterChain DUMMY_CHAIN = (req, res) -> {\n\t\tthrow new UnsupportedOperationException(\"Dummy filter chain\");\n\t};\n\n\tprivate final FilterChain chain;\n\n\tprivate HttpServletRequest request;\n\n\tprivate @Nullable HttpServletResponse response;\n\n\tpublic FilterInvocation(ServletRequest request, ServletResponse response, FilterChain chain) {\n\t\tAssert.isTrue(request != null && response != null && chain != null, \"Cannot pass null values to constructor\");\n\t\tthis.request = (HttpServletRequest) request;\n\t\tthis.response = (HttpServletResponse) response;\n\t\tthis.chain = chain;\n\t}\n\n\tpublic FilterInvocation(String servletPath, String method) {\n\t\tthis(null, servletPath, method);\n\t}\n\n\tpublic FilterInvocation(@Nullable String contextPath, String servletPath, String method) {\n\t\tthis(contextPath, servletPath, method, null);\n\t}\n\n\tpublic FilterInvocation(@Nullable String contextPath, String servletPath, @Nullable String method,\n\t\t\t@Nullable ServletContext servletContext) {\n\t\tthis(contextPath, servletPath, null, null, method, servletContext);\n\t}\n\n\tpublic FilterInvocation(String contextPath, String servletPath, String pathInfo, String query, String method) {\n\t\tthis(contextPath, servletPath, pathInfo, query, method, null);\n\t}\n\n\tpublic FilterInvocation(@Nullable String contextPath, String servletPath, @Nullable String pathInfo,\n\t\t\t@Nullable String query, @Nullable String method, @Nullable ServletContext servletContext) {\n\t\tDummyRequest request = new DummyRequest();\n\t\tcontextPath = (contextPath != null) ? contextPath : \"/cp\";\n\t\trequest.setContextPath(contextPath);\n\t\trequest.setServletPath(servletPath);\n\t\trequest.setRequestURI(contextPath + servletPath + ((pathInfo != null) ? pathInfo : \"\"));\n\t\trequest.setPathInfo(pathInfo);\n\t\trequest.setQueryString(query);\n\t\trequest.setMethod(method);\n\t\trequest.setServletContext(servletContext);\n\t\tthis.request = request;\n\t\tthis.chain = DUMMY_CHAIN;\n\t}\n\n\tpublic FilterChain getChain() {\n\t\treturn this.chain;\n\t}\n\n\t/**\n\t * Indicates the URL that the user agent used for this request.\n\t * <p>\n\t * @return the full URL of this request\n\t */\n\tpublic String getFullRequestUrl() {\n\t\treturn UrlUtils.buildFullRequestUrl(this.request);\n\t}\n\n\tpublic HttpServletRequest getHttpRequest() {\n\t\treturn this.request;\n\t}\n\n\tpublic @Nullable HttpServletResponse getHttpResponse() {\n\t\treturn this.response;\n\t}\n\n\t/**\n\t * Obtains the web application-specific fragment of the URL.\n\t * @return the URL, excluding any server name, context path or servlet path\n\t */\n\tpublic String getRequestUrl() {\n\t\treturn UrlUtils.buildRequestUrl(this.request);\n\t}\n\n\tpublic HttpServletRequest getRequest() {\n\t\treturn getHttpRequest();\n\t}\n\n\tpublic @Nullable HttpServletResponse getResponse() {\n\t\treturn getHttpResponse();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (!StringUtils.hasLength(this.request.getMethod())) {\n\t\t\treturn \"filter invocation [\" + getRequestUrl() + \"]\";\n\t\t}\n\t\telse {\n\t\t\treturn \"filter invocation [\" + this.request.getMethod() + \" \" + getRequestUrl() + \"]\";\n\t\t}\n\t}\n\n\tstatic class DummyRequest extends HttpServletRequestWrapper {\n\n\t\tprivate static final HttpServletRequest UNSUPPORTED_REQUEST = (HttpServletRequest) Proxy.newProxyInstance(\n\t\t\t\tDummyRequest.class.getClassLoader(), new Class[] { HttpServletRequest.class },\n\t\t\t\tnew UnsupportedOperationExceptionInvocationHandler());\n\n\t\tprivate @Nullable String requestURI;\n\n\t\tprivate String contextPath = \"\";\n\n\t\tprivate @Nullable String servletPath;\n\n\t\tprivate @Nullable String pathInfo;\n\n\t\tprivate @Nullable String queryString;\n\n\t\tprivate @Nullable String method;\n\n\t\tprivate @Nullable ServletContext servletContext;\n\n\t\tprivate final HttpHeaders headers = new HttpHeaders();\n\n\t\tprivate final Map<String, String[]> parameters = new LinkedHashMap<>();\n\n\t\tDummyRequest() {\n\t\t\tsuper(UNSUPPORTED_REQUEST);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getCharacterEncoding() {\n\t\t\treturn \"UTF-8\";\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object getAttribute(String attributeName) {\n\t\t\treturn null;\n\t\t}\n\n\t\tvoid setRequestURI(String requestURI) {\n\t\t\tthis.requestURI = requestURI;\n\t\t}\n\n\t\tvoid setPathInfo(@Nullable String pathInfo) {\n\t\t\tthis.pathInfo = pathInfo;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String getRequestURI() {\n\t\t\treturn this.requestURI;\n\t\t}\n\n\t\tvoid setContextPath(String contextPath) {\n\t\t\tthis.contextPath = contextPath;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getContextPath() {\n\t\t\treturn this.contextPath;\n\t\t}\n\n\t\tvoid setServletPath(String servletPath) {\n\t\t\tthis.servletPath = servletPath;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String getServletPath() {\n\t\t\treturn this.servletPath;\n\t\t}\n\n\t\tvoid setMethod(@Nullable String method) {\n\t\t\tthis.method = method;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String getMethod() {\n\t\t\treturn this.method;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String getPathInfo() {\n\t\t\treturn this.pathInfo;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String getQueryString() {\n\t\t\treturn this.queryString;\n\t\t}\n\n\t\tvoid setQueryString(@Nullable String queryString) {\n\t\t\tthis.queryString = queryString;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String getServerName() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String getHeader(String name) {\n\t\t\treturn this.headers.getFirst(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic Enumeration<String> getHeaders(String name) {\n\t\t\tList<String> headerList = this.headers.get(name);\n\t\t\tif (headerList == null) {\n\t\t\t\treturn Collections.emptyEnumeration();\n\t\t\t}\n\t\t\treturn Collections.enumeration(headerList);\n\t\t}\n\n\t\t@Override\n\t\tpublic Enumeration<String> getHeaderNames() {\n\t\t\treturn Collections.enumeration(this.headers.headerNames());\n\t\t}\n\n\t\t@Override\n\t\tpublic int getIntHeader(String name) {\n\t\t\tString value = this.headers.getFirst(name);\n\t\t\tif (value == null) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\treturn Integer.parseInt(value);\n\t\t}\n\n\t\tvoid addHeader(String name, String value) {\n\t\t\tthis.headers.add(name, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable String getParameter(String name) {\n\t\t\tString[] array = this.parameters.get(name);\n\t\t\treturn (array != null && array.length > 0) ? array[0] : null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String[]> getParameterMap() {\n\t\t\treturn Collections.unmodifiableMap(this.parameters);\n\t\t}\n\n\t\t@Override\n\t\tpublic Enumeration<String> getParameterNames() {\n\t\t\treturn Collections.enumeration(this.parameters.keySet());\n\t\t}\n\n\t\t@Override\n\t\tpublic String @Nullable [] getParameterValues(String name) {\n\t\t\treturn this.parameters.get(name);\n\t\t}\n\n\t\tvoid setParameter(String name, String... values) {\n\t\t\tthis.parameters.put(name, values);\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable ServletContext getServletContext() {\n\t\t\treturn this.servletContext;\n\t\t}\n\n\t\tvoid setServletContext(@Nullable ServletContext servletContext) {\n\t\t\tthis.servletContext = servletContext;\n\t\t}\n\n\t}\n\n\tstatic final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {\n\n\t\tprivate static final float JAVA_VERSION = Float.parseFloat(System.getProperty(\"java.class.version\", \"52\"));\n\n\t\t@Override\n\t\tpublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\t\t\tif (method.isDefault()) {\n\t\t\t\treturn invokeDefaultMethod(proxy, method, args);\n\t\t\t}\n\t\t\tthrow new UnsupportedOperationException(method + \" is not supported\");\n\t\t}\n\n\t\tprivate Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {\n\t\t\tif (isJdk8OrEarlier()) {\n\t\t\t\treturn invokeDefaultMethodForJdk8(proxy, method, args);\n\t\t\t}\n\t\t\treturn MethodHandles.lookup()\n\t\t\t\t.findSpecial(method.getDeclaringClass(), method.getName(),\n\t\t\t\t\t\tMethodType.methodType(method.getReturnType(), new Class[0]), method.getDeclaringClass())\n\t\t\t\t.bindTo(proxy)\n\t\t\t\t.invokeWithArguments(args);\n\t\t}\n\n\t\tprivate Object invokeDefaultMethodForJdk8(Object proxy, Method method, Object[] args) throws Throwable {\n\t\t\tConstructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class);\n\t\t\tconstructor.setAccessible(true);\n\t\t\tClass<?> clazz = method.getDeclaringClass();\n\t\t\treturn constructor.newInstance(clazz)\n\t\t\t\t.in(clazz)\n\t\t\t\t.unreflectSpecial(method, clazz)\n\t\t\t\t.bindTo(proxy)\n\t\t\t\t.invokeWithArguments(args);\n\t\t}\n\n\t\tprivate boolean isJdk8OrEarlier() {\n\t\t\treturn JAVA_VERSION <= 52;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/FormPostRedirectStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.Map.Entry;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.web.util.HtmlUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Redirect using an auto-submitting HTML form using the POST method. All query params\n * provided in the URL are changed to inputs in the form so they are submitted as POST\n * data instead of query string data.\n *\n * @author Craig Andrews\n * @author Steve Riesenberg\n * @since 6.5\n */\npublic final class FormPostRedirectStrategy implements RedirectStrategy {\n\n\tprivate static final String CONTENT_SECURITY_POLICY_HEADER = \"Content-Security-Policy\";\n\n\tprivate static final String REDIRECT_PAGE_TEMPLATE = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <meta charset=\"utf-8\">\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t    <title>Redirect</title>\n\t\t\t  </head>\n\t\t\t  <body>\n\t\t\t    <form id=\"redirect-form\" method=\"POST\" action=\"{{action}}\">\n\t\t\t      {{params}}\n\t\t\t      <noscript>\n\t\t\t        <p>JavaScript is not enabled for this page.</p>\n\t\t\t        <button type=\"submit\">Click to continue</button>\n\t\t\t      </noscript>\n\t\t\t    </form>\n\t\t\t    <script nonce=\"{{nonce}}\">\n\t\t\t      document.getElementById(\"redirect-form\").submit();\n\t\t\t    </script>\n\t\t\t  </body>\n\t\t\t</html>\n\t\t\t\"\"\";\n\n\tprivate static final String HIDDEN_INPUT_TEMPLATE = \"\"\"\n\t\t\t<input name=\"{{name}}\" type=\"hidden\" value=\"{{value}}\" />\n\t\t\t\"\"\";\n\n\tprivate static final StringKeyGenerator DEFAULT_NONCE_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\t@Override\n\tpublic void sendRedirect(final HttpServletRequest request, final HttpServletResponse response, final String url)\n\t\t\tthrows IOException {\n\t\tfinal UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(url);\n\n\t\tfinal StringBuilder hiddenInputsHtmlBuilder = new StringBuilder();\n\t\tfor (final Entry<String, List<String>> entry : uriComponentsBuilder.build().getQueryParams().entrySet()) {\n\t\t\tfinal String name = entry.getKey();\n\t\t\tfor (final String value : entry.getValue()) {\n\t\t\t\t// @formatter:off\n\t\t\t\tfinal String hiddenInput = HIDDEN_INPUT_TEMPLATE\n\t\t\t\t\t.replace(\"{{name}}\", HtmlUtils.htmlEscape(name))\n\t\t\t\t\t.replace(\"{{value}}\", HtmlUtils.htmlEscape(value));\n\t\t\t\t// @formatter:on\n\t\t\t\thiddenInputsHtmlBuilder.append(hiddenInput.trim());\n\t\t\t}\n\t\t}\n\n\t\t// Create the script-src policy directive for the Content-Security-Policy header\n\t\tfinal String nonce = DEFAULT_NONCE_GENERATOR.generateKey();\n\t\tfinal String policyDirective = \"script-src 'nonce-%s'\".formatted(nonce);\n\n\t\t// @formatter:off\n\t\tfinal String html = REDIRECT_PAGE_TEMPLATE\n\t\t\t// Clear the query string as we don't want that to be part of the form action URL\n\t\t\t.replace(\"{{action}}\", HtmlUtils.htmlEscape(uriComponentsBuilder.replaceQuery(null).build().toUriString()))\n\t\t\t.replace(\"{{params}}\", hiddenInputsHtmlBuilder.toString())\n\t\t\t.replace(\"{{nonce}}\", HtmlUtils.htmlEscape(nonce));\n\t\t// @formatter:on\n\n\t\tresponse.setStatus(HttpStatus.OK.value());\n\t\tresponse.setContentType(MediaType.TEXT_HTML_VALUE);\n\t\tresponse.setHeader(CONTENT_SECURITY_POLICY_HEADER, policyDirective);\n\t\tresponse.getWriter().write(html);\n\t\tresponse.getWriter().flush();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/ObservationFilterChainDecorator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport io.micrometer.common.KeyValue;\nimport io.micrometer.common.KeyValues;\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationConvention;\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.StringUtils;\n\n/**\n * A {@link org.springframework.security.web.FilterChainProxy.FilterChainDecorator} that\n * wraps the chain in before and after observations\n *\n * @author Josh Cummings\n * @author Nikita Konev\n * @since 6.0\n */\npublic final class ObservationFilterChainDecorator implements FilterChainProxy.FilterChainDecorator {\n\n\tprivate static final Log logger = LogFactory.getLog(FilterChainProxy.class);\n\n\tstatic final String ATTRIBUTE = ObservationFilterChainDecorator.class + \".observation\";\n\n\tstatic final String UNSECURED_OBSERVATION_NAME = \"spring.security.http.unsecured.requests\";\n\n\tstatic final String SECURED_OBSERVATION_NAME = \"spring.security.http.secured.requests\";\n\n\tprivate final ObservationRegistry registry;\n\n\tpublic ObservationFilterChainDecorator(ObservationRegistry registry) {\n\t\tthis.registry = registry;\n\t}\n\n\t@Override\n\tpublic FilterChain decorate(FilterChain original) {\n\t\treturn wrapUnsecured(original);\n\t}\n\n\t@Override\n\tpublic FilterChain decorate(FilterChain original, List<Filter> filters) {\n\t\treturn new VirtualFilterChain(wrapSecured(original), wrap(filters));\n\t}\n\n\tprivate FilterChain wrapSecured(FilterChain original) {\n\t\treturn (req, res) -> {\n\t\t\tAroundFilterObservation parent = observation((HttpServletRequest) req);\n\t\t\tObservation observation = Observation.createNotStarted(SECURED_OBSERVATION_NAME, this.registry)\n\t\t\t\t.contextualName(\"secured request\");\n\t\t\tparent.wrap(FilterObservation.create(observation).wrap(original)).doFilter(req, res);\n\t\t};\n\t}\n\n\tprivate FilterChain wrapUnsecured(FilterChain original) {\n\t\treturn (req, res) -> {\n\t\t\tObservation observation = Observation.createNotStarted(UNSECURED_OBSERVATION_NAME, this.registry)\n\t\t\t\t.contextualName(\"unsecured request\");\n\t\t\tFilterObservation.create(observation).wrap(original).doFilter(req, res);\n\t\t};\n\t}\n\n\tprivate List<ObservationFilter> wrap(List<Filter> filters) {\n\t\tint size = filters.size();\n\t\tList<ObservationFilter> observableFilters = new ArrayList<>();\n\t\tint position = 1;\n\t\tfor (Filter filter : filters) {\n\t\t\tobservableFilters.add(new ObservationFilter(this.registry, filter, position, size));\n\t\t\tposition++;\n\t\t}\n\t\treturn observableFilters;\n\t}\n\n\tstatic AroundFilterObservation observation(HttpServletRequest request) {\n\t\tAroundFilterObservation observation = (AroundFilterObservation) request.getAttribute(ATTRIBUTE);\n\t\treturn (observation != null) ? observation : AroundFilterObservation.NOOP;\n\t}\n\n\tprivate static final class VirtualFilterChain implements FilterChain {\n\n\t\tprivate final FilterChain originalChain;\n\n\t\tprivate final List<ObservationFilter> additionalFilters;\n\n\t\tprivate final int size;\n\n\t\tprivate int currentPosition = 0;\n\n\t\tprivate VirtualFilterChain(FilterChain chain, List<ObservationFilter> additionalFilters) {\n\t\t\tthis.originalChain = chain;\n\t\t\tthis.additionalFilters = additionalFilters;\n\t\t\tthis.size = additionalFilters.size();\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {\n\t\t\tif (this.currentPosition == this.size) {\n\t\t\t\tthis.originalChain.doFilter(request, response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.currentPosition++;\n\t\t\tObservationFilter nextFilter = this.additionalFilters.get(this.currentPosition - 1);\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tString name = nextFilter.getName();\n\t\t\t\tlogger.trace(LogMessage.format(\"Invoking %s (%d/%d)\", name, this.currentPosition, this.size));\n\t\t\t}\n\t\t\tnextFilter.doFilter(request, response, this);\n\t\t}\n\n\t}\n\n\tstatic final class ObservationFilter implements Filter {\n\n\t\tstatic final Map<String, String> OBSERVATION_NAMES = new HashMap<>();\n\n\t\tstatic {\n\t\t\tOBSERVATION_NAMES.put(\"DisableEncodeUrlFilter\", \"session.urlencoding\");\n\t\t\tOBSERVATION_NAMES.put(\"ForceEagerSessionCreationFilter\", \"session.eagercreate\");\n\t\t\tOBSERVATION_NAMES.put(\"ChannelProcessingFilter\", \"access.channel\");\n\t\t\tOBSERVATION_NAMES.put(\"WebAsyncManagerIntegrationFilter\", \"context.async\");\n\t\t\tOBSERVATION_NAMES.put(\"SecurityContextHolderFilter\", \"context.holder\");\n\t\t\tOBSERVATION_NAMES.put(\"SecurityContextPersistenceFilter\", \"context.management\");\n\t\t\tOBSERVATION_NAMES.put(\"HeaderWriterFilter\", \"header\");\n\t\t\tOBSERVATION_NAMES.put(\"CorsFilter\", \"cors\");\n\t\t\tOBSERVATION_NAMES.put(\"CsrfFilter\", \"csrf\");\n\t\t\tOBSERVATION_NAMES.put(\"LogoutFilter\", \"logout\");\n\t\t\tOBSERVATION_NAMES.put(\"OAuth2AuthorizationRequestRedirectFilter\", \"oauth2.authnrequest\");\n\t\t\tOBSERVATION_NAMES.put(\"Saml2WebSsoAuthenticationRequestFilter\", \"saml2.authnrequest\");\n\t\t\tOBSERVATION_NAMES.put(\"X509AuthenticationFilter\", \"authentication.x509\");\n\t\t\tOBSERVATION_NAMES.put(\"J2eePreAuthenticatedProcessingFilter\", \"preauthentication.j2ee\");\n\t\t\tOBSERVATION_NAMES.put(\"RequestHeaderAuthenticationFilter\", \"preauthentication.header\");\n\t\t\tOBSERVATION_NAMES.put(\"RequestAttributeAuthenticationFilter\", \"preauthentication.attribute\");\n\t\t\tOBSERVATION_NAMES.put(\"WebSpherePreAuthenticatedProcessingFilter\", \"preauthentication.websphere\");\n\t\t\tOBSERVATION_NAMES.put(\"CasAuthenticationFilter\", \"cas.authentication\");\n\t\t\tOBSERVATION_NAMES.put(\"OAuth2LoginAuthenticationFilter\", \"oauth2.authentication\");\n\t\t\tOBSERVATION_NAMES.put(\"Saml2WebSsoAuthenticationFilter\", \"saml2.authentication\");\n\t\t\tOBSERVATION_NAMES.put(\"UsernamePasswordAuthenticationFilter\", \"authentication.form\");\n\t\t\tOBSERVATION_NAMES.put(\"DefaultLoginPageGeneratingFilter\", \"page.login\");\n\t\t\tOBSERVATION_NAMES.put(\"DefaultLogoutPageGeneratingFilter\", \"page.logout\");\n\t\t\tOBSERVATION_NAMES.put(\"ConcurrentSessionFilter\", \"session.concurrent\");\n\t\t\tOBSERVATION_NAMES.put(\"DigestAuthenticationFilter\", \"authentication.digest\");\n\t\t\tOBSERVATION_NAMES.put(\"BearerTokenAuthenticationFilter\", \"authentication.bearer\");\n\t\t\tOBSERVATION_NAMES.put(\"BasicAuthenticationFilter\", \"authentication.basic\");\n\t\t\tOBSERVATION_NAMES.put(\"RequestCacheAwareFilter\", \"requestcache\");\n\t\t\tOBSERVATION_NAMES.put(\"SecurityContextHolderAwareRequestFilter\", \"context.servlet\");\n\t\t\tOBSERVATION_NAMES.put(\"JaasApiIntegrationFilter\", \"jaas\");\n\t\t\tOBSERVATION_NAMES.put(\"RememberMeAuthenticationFilter\", \"authentication.rememberme\");\n\t\t\tOBSERVATION_NAMES.put(\"AnonymousAuthenticationFilter\", \"authentication.anonymous\");\n\t\t\tOBSERVATION_NAMES.put(\"OAuth2AuthorizationCodeGrantFilter\", \"oauth2.client.code\");\n\t\t\tOBSERVATION_NAMES.put(\"SessionManagementFilter\", \"session.management\");\n\t\t\tOBSERVATION_NAMES.put(\"ExceptionTranslationFilter\", \"access.exceptions\");\n\t\t\tOBSERVATION_NAMES.put(\"FilterSecurityInterceptor\", \"access.request\");\n\t\t\tOBSERVATION_NAMES.put(\"AuthorizationFilter\", \"authorization\");\n\t\t\tOBSERVATION_NAMES.put(\"SwitchUserFilter\", \"authentication.switch\");\n\t\t}\n\n\t\tprivate final ObservationRegistry registry;\n\n\t\tprivate final FilterChainObservationConvention convention = new FilterChainObservationConvention();\n\n\t\tprivate final Filter filter;\n\n\t\tprivate final String name;\n\n\t\tprivate final String eventName;\n\n\t\tprivate final int position;\n\n\t\tprivate final int size;\n\n\t\tObservationFilter(ObservationRegistry registry, Filter filter, int position, int size) {\n\t\t\tthis.registry = registry;\n\t\t\tthis.filter = filter;\n\t\t\tthis.name = filter.getClass().getSimpleName();\n\t\t\tthis.position = position;\n\t\t\tthis.size = size;\n\t\t\tthis.eventName = eventName(this.name);\n\t\t}\n\n\t\tprivate String eventName(String className) {\n\t\t\tString eventName = OBSERVATION_NAMES.get(className);\n\t\t\treturn (eventName != null) ? eventName : className;\n\t\t}\n\n\t\tString getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tif (this.position == 1) {\n\t\t\t\tAroundFilterObservation parent = parent((HttpServletRequest) request);\n\t\t\t\tparent.wrap(this::wrapFilter).doFilter(request, response, chain);\n\t\t\t}\n\t\t\telse {\n\t\t\t\twrapFilter(request, response, chain);\n\t\t\t}\n\t\t}\n\n\t\tprivate void wrapFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tAroundFilterObservation parent = observation((HttpServletRequest) request);\n\t\t\tif (parent.before().getContext() instanceof FilterChainObservationContext parentBefore) {\n\t\t\t\tparentBefore.setChainSize(this.size);\n\t\t\t\tparentBefore.setFilterName(this.name);\n\t\t\t\tparentBefore.setChainPosition(this.position);\n\t\t\t}\n\t\t\tparent.before().event(Observation.Event.of(this.eventName + \".before\", \"before \" + this.name));\n\t\t\tthis.filter.doFilter(request, response, chain);\n\t\t\tparent.start();\n\t\t\tif (parent.after().getContext() instanceof FilterChainObservationContext parentAfter) {\n\t\t\t\tparentAfter.setChainSize(this.size);\n\t\t\t\tparentAfter.setFilterName(this.name);\n\t\t\t\tparentAfter.setChainPosition(this.size - this.position + 1);\n\t\t\t}\n\t\t\tparent.after().event(Observation.Event.of(this.eventName + \".after\", \"after \" + this.name));\n\t\t}\n\n\t\tprivate AroundFilterObservation parent(HttpServletRequest request) {\n\t\t\tFilterChainObservationContext beforeContext = FilterChainObservationContext.before();\n\t\t\tFilterChainObservationContext afterContext = FilterChainObservationContext.after();\n\n\t\t\tAroundFilterObservation existingParentObservation = (AroundFilterObservation) request\n\t\t\t\t.getAttribute(ATTRIBUTE);\n\t\t\tif (existingParentObservation != null) {\n\t\t\t\tbeforeContext\n\t\t\t\t\t.setParentObservation(existingParentObservation.before().getContext().getParentObservation());\n\t\t\t\tafterContext\n\t\t\t\t\t.setParentObservation(existingParentObservation.after().getContext().getParentObservation());\n\t\t\t}\n\n\t\t\tObservation before = Observation.createNotStarted(this.convention, () -> beforeContext, this.registry);\n\t\t\tObservation after = Observation.createNotStarted(this.convention, () -> afterContext, this.registry);\n\t\t\tAroundFilterObservation parent = AroundFilterObservation.create(before, after);\n\t\t\trequest.setAttribute(ATTRIBUTE, parent);\n\t\t\treturn parent;\n\t\t}\n\n\t}\n\n\tinterface AroundFilterObservation extends FilterObservation {\n\n\t\tAroundFilterObservation NOOP = new AroundFilterObservation() {\n\t\t};\n\n\t\tstatic AroundFilterObservation create(Observation before, Observation after) {\n\t\t\tif (before.isNoop() || after.isNoop()) {\n\t\t\t\treturn NOOP;\n\t\t\t}\n\t\t\treturn new SimpleAroundFilterObservation(before, after);\n\t\t}\n\n\t\tdefault Observation before() {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\tdefault Observation after() {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\tclass SimpleAroundFilterObservation implements AroundFilterObservation {\n\n\t\t\tprivate final ObservationReference before;\n\n\t\t\tprivate final ObservationReference after;\n\n\t\t\tprivate final AtomicReference<ObservationReference> reference = new AtomicReference<>(\n\t\t\t\t\tObservationReference.NOOP);\n\n\t\t\tSimpleAroundFilterObservation(Observation before, Observation after) {\n\t\t\t\tthis.before = new ObservationReference(before);\n\t\t\t\tthis.after = new ObservationReference(after);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void start() {\n\t\t\t\tif (this.reference.compareAndSet(ObservationReference.NOOP, this.before)) {\n\t\t\t\t\tthis.before.start();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.reference.compareAndSet(this.before, this.after)) {\n\t\t\t\t\tthis.before.stop();\n\t\t\t\t\tthis.after.start();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void error(Throwable ex) {\n\t\t\t\tthis.reference.get().error(ex);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void stop() {\n\t\t\t\tthis.reference.get().stop();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Filter wrap(Filter filter) {\n\t\t\t\treturn (request, response, chain) -> {\n\t\t\t\t\tstart();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfilter.doFilter(request, response, chain);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Throwable ex) {\n\t\t\t\t\t\terror(ex);\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\tstop();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic FilterChain wrap(FilterChain chain) {\n\t\t\t\treturn (request, response) -> {\n\t\t\t\t\tstop();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tchain.doFilter(request, response);\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\tstart();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation before() {\n\t\t\t\treturn this.before.observation;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation after() {\n\t\t\t\treturn this.after.observation;\n\t\t\t}\n\n\t\t\tprivate static final class ObservationReference {\n\n\t\t\t\tprivate static final ObservationReference NOOP = new ObservationReference(Observation.NOOP);\n\n\t\t\t\tprivate final AtomicInteger state = new AtomicInteger(0);\n\n\t\t\t\tprivate final Observation observation;\n\n\t\t\t\tprivate volatile Observation.Scope scope = Observation.Scope.NOOP;\n\n\t\t\t\tprivate ObservationReference(Observation observation) {\n\t\t\t\t\tthis.observation = observation;\n\t\t\t\t}\n\n\t\t\t\tprivate void start() {\n\t\t\t\t\tif (this.state.compareAndSet(0, 1)) {\n\t\t\t\t\t\tthis.observation.start();\n\t\t\t\t\t\tthis.scope = this.observation.openScope();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tprivate void error(Throwable error) {\n\t\t\t\t\tif (this.state.get() == 1) {\n\t\t\t\t\t\tthis.scope.getCurrentObservation().error(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tprivate void stop() {\n\t\t\t\t\tif (this.state.compareAndSet(1, 2)) {\n\t\t\t\t\t\tthis.scope.close();\n\t\t\t\t\t\tthis.scope.getCurrentObservation().stop();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface FilterObservation {\n\n\t\tFilterObservation NOOP = new FilterObservation() {\n\t\t};\n\n\t\tstatic FilterObservation create(Observation observation) {\n\t\t\tif (observation.isNoop()) {\n\t\t\t\treturn NOOP;\n\t\t\t}\n\t\t\treturn new SimpleFilterObservation(observation);\n\t\t}\n\n\t\tdefault void start() {\n\t\t}\n\n\t\tdefault void error(Throwable ex) {\n\t\t}\n\n\t\tdefault void stop() {\n\t\t}\n\n\t\tdefault Filter wrap(Filter filter) {\n\t\t\treturn filter;\n\t\t}\n\n\t\tdefault FilterChain wrap(FilterChain chain) {\n\t\t\treturn chain;\n\t\t}\n\n\t\tclass SimpleFilterObservation implements FilterObservation {\n\n\t\t\tprivate final Observation observation;\n\n\t\t\tSimpleFilterObservation(Observation observation) {\n\t\t\t\tthis.observation = observation;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void start() {\n\t\t\t\tthis.observation.start();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void error(Throwable ex) {\n\t\t\t\tthis.observation.error(ex);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void stop() {\n\t\t\t\tthis.observation.stop();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Filter wrap(Filter filter) {\n\t\t\t\tif (this.observation.isNoop()) {\n\t\t\t\t\treturn filter;\n\t\t\t\t}\n\t\t\t\treturn (request, response, chain) -> {\n\t\t\t\t\tthis.observation.start();\n\t\t\t\t\ttry (Observation.Scope scope = this.observation.openScope()) {\n\t\t\t\t\t\tfilter.doFilter(request, response, chain);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Throwable ex) {\n\t\t\t\t\t\tthis.observation.error(ex);\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\tthis.observation.stop();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic FilterChain wrap(FilterChain chain) {\n\t\t\t\tif (this.observation.isNoop()) {\n\t\t\t\t\treturn chain;\n\t\t\t\t}\n\t\t\t\treturn (request, response) -> {\n\t\t\t\t\tthis.observation.start();\n\t\t\t\t\ttry (Observation.Scope scope = this.observation.openScope()) {\n\t\t\t\t\t\tchain.doFilter(request, response);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Throwable ex) {\n\t\t\t\t\t\tthis.observation.error(ex);\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\tthis.observation.stop();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic final class FilterChainObservationContext extends Observation.Context {\n\n\t\tprivate final String filterSection;\n\n\t\tprivate @Nullable String filterName;\n\n\t\tprivate int chainPosition;\n\n\t\tprivate int chainSize;\n\n\t\tprivate FilterChainObservationContext(String filterSection) {\n\t\t\tthis.filterSection = filterSection;\n\t\t\tsetContextualName(\"security filterchain \" + filterSection);\n\t\t}\n\n\t\tstatic FilterChainObservationContext before() {\n\t\t\treturn new FilterChainObservationContext(\"before\");\n\t\t}\n\n\t\tstatic FilterChainObservationContext after() {\n\t\t\treturn new FilterChainObservationContext(\"after\");\n\t\t}\n\n\t\tString getFilterSection() {\n\t\t\treturn this.filterSection;\n\t\t}\n\n\t\t@Nullable String getFilterName() {\n\t\t\treturn this.filterName;\n\t\t}\n\n\t\tvoid setFilterName(String filterName) {\n\t\t\tthis.filterName = filterName;\n\t\t}\n\n\t\tint getChainPosition() {\n\t\t\treturn this.chainPosition;\n\t\t}\n\n\t\tvoid setChainPosition(int chainPosition) {\n\t\t\tthis.chainPosition = chainPosition;\n\t\t}\n\n\t\tint getChainSize() {\n\t\t\treturn this.chainSize;\n\t\t}\n\n\t\tvoid setChainSize(int chainSize) {\n\t\t\tthis.chainSize = chainSize;\n\t\t}\n\n\t}\n\n\tstatic final class FilterChainObservationConvention\n\t\t\timplements ObservationConvention<FilterChainObservationContext> {\n\n\t\tstatic final String CHAIN_OBSERVATION_NAME = \"spring.security.filterchains\";\n\n\t\tprivate static final String CHAIN_POSITION_NAME = \"spring.security.filterchain.position\";\n\n\t\tprivate static final String CHAIN_SIZE_NAME = \"spring.security.filterchain.size\";\n\n\t\tprivate static final String FILTER_SECTION_NAME = \"spring.security.reached.filter.section\";\n\n\t\tprivate static final String FILTER_NAME = \"spring.security.reached.filter.name\";\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn CHAIN_OBSERVATION_NAME;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getContextualName(FilterChainObservationContext context) {\n\t\t\treturn \"security filterchain \" + context.getFilterSection();\n\t\t}\n\n\t\t@Override\n\t\tpublic KeyValues getLowCardinalityKeyValues(FilterChainObservationContext context) {\n\t\t\treturn KeyValues.of(CHAIN_SIZE_NAME, String.valueOf(context.getChainSize()))\n\t\t\t\t.and(CHAIN_POSITION_NAME, String.valueOf(context.getChainPosition()))\n\t\t\t\t.and(FILTER_SECTION_NAME, context.getFilterSection())\n\t\t\t\t.and(FILTER_NAME,\n\t\t\t\t\t\t(StringUtils.hasText(context.getFilterName())) ? context.getFilterName() : KeyValue.NONE_VALUE);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supportsContext(Observation.Context context) {\n\t\t\treturn context instanceof FilterChainObservationContext;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/PortMapper.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.util.Locale;\n\nimport jakarta.servlet.ServletRequest;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * <code>PortMapper</code> implementations provide callers with information about which\n * HTTP ports are associated with which HTTPS ports on the system, and vice versa.\n *\n * @author Ben Alex\n */\npublic interface PortMapper {\n\n\t/**\n\t * Locates the HTTP port associated with the specified HTTPS port.\n\t * <P>\n\t * Returns <code>null</code> if unknown.\n\t * </p>\n\t * @param httpsPort\n\t * @return the HTTP port or <code>null</code> if unknown\n\t */\n\t@Nullable Integer lookupHttpPort(Integer httpsPort);\n\n\t/**\n\t * Locates the HTTPS port associated with the specified HTTP port.\n\t * <P>\n\t * Returns <code>null</code> if unknown.\n\t * </p>\n\t * @param httpPort\n\t * @return the HTTPS port or <code>null</code> if unknown\n\t */\n\t@Nullable Integer lookupHttpsPort(Integer httpPort);\n\n\t/**\n\t * Get server port from request and automatically apply the configured mapping.\n\t * @param request ServletRequest\n\t * @return the mapped port\n\t */\n\tdefault Integer getServerPort(ServletRequest request) {\n\t\tint serverPort = request.getServerPort();\n\t\tString scheme = request.getScheme().toLowerCase(Locale.ENGLISH);\n\t\tInteger mappedPort = null;\n\t\tif (\"http\".equals(scheme)) {\n\t\t\tmappedPort = lookupHttpPort(serverPort);\n\t\t}\n\t\telse if (\"https\".equals(scheme)) {\n\t\t\tmappedPort = lookupHttpsPort(serverPort);\n\t\t}\n\t\treturn (mappedPort != null) ? mappedPort : serverPort;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/PortMapperImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Concrete implementation of {@link PortMapper} that obtains HTTP:HTTPS pairs from the\n * application context.\n * <p>\n * By default the implementation will assume 80:443 and 8080:8443 are HTTP:HTTPS pairs\n * respectively. If different pairs are required, use {@link #setPortMappings(Map)}.\n *\n * @author Ben Alex\n * @author colin sampaleanu\n */\npublic class PortMapperImpl implements PortMapper {\n\n\tprivate final Map<Integer, Integer> httpsPortMappings;\n\n\tpublic PortMapperImpl() {\n\t\tthis.httpsPortMappings = new HashMap<>();\n\t\tthis.httpsPortMappings.put(80, 443);\n\t\tthis.httpsPortMappings.put(8080, 8443);\n\t}\n\n\t/**\n\t * Returns the translated (Integer -&gt; Integer) version of the original port mapping\n\t * specified via setHttpsPortMapping()\n\t */\n\tpublic Map<Integer, Integer> getTranslatedPortMappings() {\n\t\treturn this.httpsPortMappings;\n\t}\n\n\t@Override\n\tpublic @Nullable Integer lookupHttpPort(Integer httpsPort) {\n\t\tfor (Integer httpPort : this.httpsPortMappings.keySet()) {\n\t\t\tif (this.httpsPortMappings.get(httpPort).equals(httpsPort)) {\n\t\t\t\treturn httpPort;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic @Nullable Integer lookupHttpsPort(Integer httpPort) {\n\t\treturn this.httpsPortMappings.get(httpPort);\n\t}\n\n\t/**\n\t * Set to override the default HTTP port to HTTPS port mappings of 80:443, and\n\t * 8080:8443. In a Spring XML ApplicationContext, a definition would look something\n\t * like this:\n\t *\n\t * <pre>\n\t *  &lt;property name=\"portMappings\"&gt;\n\t *      &lt;map&gt;\n\t *          &lt;entry key=\"80\"&gt;&lt;value&gt;443&lt;/value&gt;&lt;/entry&gt;\n\t *          &lt;entry key=\"8080\"&gt;&lt;value&gt;8443&lt;/value&gt;&lt;/entry&gt;\n\t *      &lt;/map&gt;\n\t * &lt;/property&gt;\n\t * </pre>\n\t * @param newMappings A Map consisting of String keys and String values, where for\n\t * each entry the key is the string representation of an integer HTTP port number, and\n\t * the value is the string representation of the corresponding integer HTTPS port\n\t * number.\n\t * @throws IllegalArgumentException if input map does not consist of String keys and\n\t * values, each representing an integer port number in the range 1-65535 for that\n\t * mapping.\n\t */\n\tpublic void setPortMappings(Map<String, String> newMappings) {\n\t\tAssert.notNull(newMappings, \"A valid list of HTTPS port mappings must be provided\");\n\t\tthis.httpsPortMappings.clear();\n\t\tfor (Map.Entry<String, String> entry : newMappings.entrySet()) {\n\t\t\tInteger httpPort = Integer.valueOf(entry.getKey());\n\t\t\tInteger httpsPort = Integer.valueOf(entry.getValue());\n\t\t\tAssert.isTrue(isInPortRange(httpPort) && isInPortRange(httpsPort),\n\t\t\t\t\t() -> \"one or both ports out of legal range: \" + httpPort + \", \" + httpsPort);\n\t\t\tthis.httpsPortMappings.put(httpPort, httpsPort);\n\t\t}\n\t\tAssert.isTrue(!this.httpsPortMappings.isEmpty(), \"must map at least one port\");\n\t}\n\n\tprivate boolean isInPortRange(int port) {\n\t\treturn port >= 1 && port <= 65535;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/RedirectStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * Encapsulates the redirection logic for all classes in the framework which perform\n * redirects.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface RedirectStrategy {\n\n\t/**\n\t * Performs a redirect to the supplied URL\n\t * @param request the current request\n\t * @param response the response to redirect\n\t * @param url the target URL to redirect to, for example \"/login\"\n\t */\n\tvoid sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/RequestMatcherRedirectFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Filter that redirects requests that match {@link RequestMatcher} to the specified URL.\n *\n * @author Evgeniy Cheban\n * @since 5.6\n */\npublic final class RequestMatcherRedirectFilter extends OncePerRequestFilter {\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate final RequestMatcher requestMatcher;\n\n\tprivate final String redirectUrl;\n\n\t/**\n\t * Create and initialize an instance of the filter.\n\t * @param requestMatcher the request matcher\n\t * @param redirectUrl the redirect URL\n\t */\n\tpublic RequestMatcherRedirectFilter(RequestMatcher requestMatcher, String redirectUrl) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tAssert.hasText(redirectUrl, \"redirectUrl cannot be empty\");\n\t\tthis.requestMatcher = requestMatcher;\n\t\tthis.redirectUrl = redirectUrl;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tif (this.requestMatcher.matches(request)) {\n\t\t\tthis.redirectStrategy.sendRedirect(request, response, this.redirectUrl);\n\t\t}\n\t\telse {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/SecurityFilterChain.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.http.HttpServletRequest;\n\n/**\n * Defines a filter chain which is capable of being matched against an\n * {@code HttpServletRequest}. in order to decide whether it applies to that request.\n * <p>\n * Used to configure a {@code FilterChainProxy}.\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic interface SecurityFilterChain {\n\n\tboolean matches(HttpServletRequest request);\n\n\tList<Filter> getFilters();\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/UnreachableFilterChainException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\n/**\n * Thrown if {@link SecurityFilterChain securityFilterChain} is not valid.\n *\n * @author Max Batischev\n * @since 6.5\n */\npublic class UnreachableFilterChainException extends IllegalArgumentException {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final SecurityFilterChain filterChain;\n\n\tprivate final SecurityFilterChain unreachableFilterChain;\n\n\t/**\n\t * Constructs an <code>UnreachableFilterChainException</code> with the specified\n\t * message.\n\t * @param message the detail message\n\t */\n\tpublic UnreachableFilterChainException(String message, SecurityFilterChain filterChain,\n\t\t\tSecurityFilterChain unreachableFilterChain) {\n\t\tsuper(message);\n\t\tthis.filterChain = filterChain;\n\t\tthis.unreachableFilterChain = unreachableFilterChain;\n\t}\n\n\tpublic SecurityFilterChain getFilterChain() {\n\t\treturn this.filterChain;\n\t}\n\n\tpublic SecurityFilterChain getUnreachableFilterChain() {\n\t\treturn this.unreachableFilterChain;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/WebAttributes.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;\n\n/**\n * Well-known keys which are used to store Spring Security information in request or\n * session scope.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.0.3\n */\npublic final class WebAttributes {\n\n\t/**\n\t * Used to cache an {@code AccessDeniedException} in the request for rendering.\n\t *\n\t * @see org.springframework.security.web.access.AccessDeniedHandlerImpl\n\t */\n\tpublic static final String ACCESS_DENIED_403 = \"SPRING_SECURITY_403_EXCEPTION\";\n\n\t/**\n\t * Set as a request attribute to provide an\n\t * {@link org.springframework.context.ApplicationContext} for use by JSP authorize\n\t * tags when resolving security beans.\n\t * <p>\n\t * When set, this attribute is preferred over the root web application context. The\n\t * value must be of type {@link org.springframework.context.ApplicationContext}.\n\t *\n\t * <p>\n\t * Used in {@code org.springframework.security.taglibs.authz.AbstractAuthorizeTag}.\n\t *\n\t * @since 7.1\n\t */\n\tpublic static final String APPLICATION_CONTEXT_ATTRIBUTE = WebAttributes.class.getName()\n\t\t\t+ \".APPLICATION_CONTEXT_ATTRIBUTE\";\n\n\t/**\n\t * Used to cache an authentication-failure exception in the session.\n\t *\n\t * @see org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler\n\t */\n\tpublic static final String AUTHENTICATION_EXCEPTION = \"SPRING_SECURITY_LAST_EXCEPTION\";\n\n\t/**\n\t * Set as a request attribute to override the default\n\t * {@link WebInvocationPrivilegeEvaluator}\n\t *\n\t * @since 3.1.3\n\t * @see WebInvocationPrivilegeEvaluator\n\t */\n\tpublic static final String WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE = WebAttributes.class.getName()\n\t\t\t+ \".WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE\";\n\n\t/**\n\t * Used to set a {@code Collection} of\n\t * {@link org.springframework.security.authorization.RequiredFactorError} instances\n\t * into the {@link HttpServletRequest}.\n\t * <p>\n\t * Represents what authorities are missing to be authorized for the current request\n\t *\n\t * @since 7.0\n\t * @see org.springframework.security.web.access.DelegatingMissingAuthorityAccessDeniedHandler\n\t */\n\tpublic static final String REQUIRED_FACTOR_ERRORS = WebAttributes.class + \".REQUIRED_FACTOR_ERRORS\t\";\n\n\tprivate WebAttributes() {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/AccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.access.AccessDeniedException;\n\n/**\n * Used by {@link ExceptionTranslationFilter} to handle an\n * <code>AccessDeniedException</code>.\n *\n * @author Ben Alex\n */\npublic interface AccessDeniedHandler {\n\n\t/**\n\t * Handles an access denied failure.\n\t * @param request that resulted in an <code>AccessDeniedException</code>\n\t * @param response so that the user agent can be advised of the failure\n\t * @param accessDeniedException that caused the invocation\n\t * @throws IOException in the event of an IOException\n\t * @throws ServletException in the event of a ServletException\n\t */\n\tvoid handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)\n\t\t\tthrows IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/AccessDeniedHandlerImpl.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.util.Assert;\n\n/**\n * Base implementation of {@link AccessDeniedHandler}.\n * <p>\n * This implementation sends a 403 (SC_FORBIDDEN) HTTP error code. In addition, if an\n * {@link #errorPage} is defined, the implementation will perform a request dispatcher\n * \"forward\" to the specified error page view. Being a \"forward\", the\n * <code>SecurityContextHolder</code> will remain populated. This is of benefit if the\n * view (or a tag library or macro) wishes to access the\n * <code>SecurityContextHolder</code>. The request scope will also be populated with the\n * exception itself, available from the key {@link WebAttributes#ACCESS_DENIED_403}.\n *\n * @author Ben Alex\n */\npublic class AccessDeniedHandlerImpl implements AccessDeniedHandler {\n\n\tprotected static final Log logger = LogFactory.getLog(AccessDeniedHandlerImpl.class);\n\n\tprivate @Nullable String errorPage;\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAccessDeniedException accessDeniedException) throws IOException, ServletException {\n\t\tif (response.isCommitted()) {\n\t\t\tlogger.trace(\"Did not write to response since already committed\");\n\t\t\treturn;\n\t\t}\n\t\tif (this.errorPage == null) {\n\t\t\tlogger.debug(\"Responding with 403 status code\");\n\t\t\tresponse.sendError(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase());\n\t\t\treturn;\n\t\t}\n\t\t// Put exception into request scope (perhaps of use to a view)\n\t\trequest.setAttribute(WebAttributes.ACCESS_DENIED_403, accessDeniedException);\n\t\t// Set the 403 status code.\n\t\tresponse.setStatus(HttpStatus.FORBIDDEN.value());\n\t\t// forward to error page.\n\t\tif (logger.isDebugEnabled()) {\n\t\t\tlogger.debug(LogMessage.format(\"Forwarding to %s with status code 403\", this.errorPage));\n\t\t}\n\t\trequest.getRequestDispatcher(this.errorPage).forward(request, response);\n\t}\n\n\t/**\n\t * The error page to use. Must begin with a \"/\" and is interpreted relative to the\n\t * current context root.\n\t * @param errorPage the dispatcher path to display\n\t * @throws IllegalArgumentException if the argument doesn't comply with the above\n\t * limitations\n\t */\n\tpublic void setErrorPage(String errorPage) {\n\t\tAssert.isTrue(errorPage == null || errorPage.startsWith(\"/\"), \"errorPage must begin with '/'\");\n\t\tthis.errorPage = errorPage;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.ServletContextAware;\n\n/**\n * An implementation of {@link WebInvocationPrivilegeEvaluator} which delegates the checks\n * to an instance of {@link AuthorizationManager}\n *\n * @author Marcus Da Coregio\n * @since 5.5.5\n */\npublic final class AuthorizationManagerWebInvocationPrivilegeEvaluator\n\t\timplements WebInvocationPrivilegeEvaluator, ServletContextAware {\n\n\tprivate final AuthorizationManager<HttpServletRequest> authorizationManager;\n\n\tprivate @Nullable ServletContext servletContext;\n\n\tprivate HttpServletRequestTransformer requestTransformer = HttpServletRequestTransformer.IDENTITY;\n\n\tpublic AuthorizationManagerWebInvocationPrivilegeEvaluator(\n\t\t\tAuthorizationManager<HttpServletRequest> authorizationManager) {\n\t\tAssert.notNull(authorizationManager, \"authorizationManager cannot be null\");\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t@Override\n\tpublic boolean isAllowed(String uri, @Nullable Authentication authentication) {\n\t\treturn isAllowed(null, uri, null, authentication);\n\t}\n\n\t@Override\n\tpublic boolean isAllowed(@Nullable String contextPath, String uri, @Nullable String method,\n\t\t\t@Nullable Authentication authentication) {\n\t\tFilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);\n\t\tHttpServletRequest httpRequest = this.requestTransformer.transform(filterInvocation.getHttpRequest());\n\t\tAuthorizationResult result = this.authorizationManager.authorize(() -> authentication, httpRequest);\n\t\treturn result == null || result.isGranted();\n\t}\n\n\t@Override\n\tpublic void setServletContext(ServletContext servletContext) {\n\t\tthis.servletContext = servletContext;\n\t}\n\n\t/**\n\t * Set a {@link HttpServletRequestTransformer} to be used prior to passing to the\n\t * {@link AuthorizationManager}.\n\t * @param requestTransformer the {@link HttpServletRequestTransformer} to use.\n\t */\n\tpublic void setRequestTransformer(HttpServletRequestTransformer requestTransformer) {\n\t\tAssert.notNull(requestTransformer, \"requestTransformer cannot be null\");\n\t\tthis.requestTransformer = requestTransformer;\n\t}\n\n\t/**\n\t * Used to transform the {@link HttpServletRequest} prior to passing it into the\n\t * {@link AuthorizationManager}.\n\t */\n\tpublic interface HttpServletRequestTransformer {\n\n\t\tHttpServletRequestTransformer IDENTITY = (request) -> request;\n\n\t\t/**\n\t\t * Return the {@link HttpServletRequest} that is passed into the\n\t\t * {@link AuthorizationManager}\n\t\t * @param request the {@link HttpServletRequest} created by the\n\t\t * {@link WebInvocationPrivilegeEvaluator}\n\t\t * @return the {@link HttpServletRequest} that is passed into the\n\t\t * {@link AuthorizationManager}\n\t\t */\n\t\tHttpServletRequest transform(HttpServletRequest request);\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/CompositeAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.access.AccessDeniedException;\n\npublic final class CompositeAccessDeniedHandler implements AccessDeniedHandler {\n\n\tprivate Collection<AccessDeniedHandler> handlers;\n\n\tpublic CompositeAccessDeniedHandler(AccessDeniedHandler... handlers) {\n\t\tthis(Arrays.asList(handlers));\n\t}\n\n\tpublic CompositeAccessDeniedHandler(Collection<AccessDeniedHandler> handlers) {\n\t\tthis.handlers = new ArrayList<>(handlers);\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAccessDeniedException accessDeniedException) throws IOException, ServletException {\n\t\tfor (AccessDeniedHandler handler : this.handlers) {\n\t\t\thandler.handle(request, response, accessDeniedException);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/DelegatingAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\nimport java.util.LinkedHashMap;\nimport java.util.Map.Entry;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AccessDeniedHandler} that delegates to other {@link AccessDeniedHandler}\n * instances based upon the type of {@link AccessDeniedException} passed into\n * {@link #handle(HttpServletRequest, HttpServletResponse, AccessDeniedException)}.\n *\n * @author Rob Winch\n * @since 3.2\n *\n */\npublic final class DelegatingAccessDeniedHandler implements AccessDeniedHandler {\n\n\tprivate final LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers;\n\n\tprivate final AccessDeniedHandler defaultHandler;\n\n\t/**\n\t * Creates a new instance\n\t * @param handlers a map of the {@link AccessDeniedException} class to the\n\t * {@link AccessDeniedHandler} that should be used. Each is considered in the order\n\t * they are specified and only the first {@link AccessDeniedHandler} is ued.\n\t * @param defaultHandler the default {@link AccessDeniedHandler} that should be used\n\t * if none of the handlers matches.\n\t */\n\tpublic DelegatingAccessDeniedHandler(\n\t\t\tLinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers,\n\t\t\tAccessDeniedHandler defaultHandler) {\n\t\tAssert.notEmpty(handlers, \"handlers cannot be null or empty\");\n\t\tAssert.notNull(defaultHandler, \"defaultHandler cannot be null\");\n\t\tthis.handlers = handlers;\n\t\tthis.defaultHandler = defaultHandler;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAccessDeniedException accessDeniedException) throws IOException, ServletException {\n\t\tfor (Entry<Class<? extends AccessDeniedException>, AccessDeniedHandler> entry : this.handlers.entrySet()) {\n\t\t\tClass<? extends AccessDeniedException> handlerClass = entry.getKey();\n\t\t\tif (handlerClass.isAssignableFrom(accessDeniedException.getClass())) {\n\t\t\t\tAccessDeniedHandler handler = entry.getValue();\n\t\t\t\thandler.handle(request, response, accessDeniedException);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis.defaultHandler.handle(request, response, accessDeniedException);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.InsufficientAuthenticationException;\nimport org.springframework.security.authorization.AuthorityAuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.FactorAuthorizationDecision;\nimport org.springframework.security.authorization.RequiredFactor;\nimport org.springframework.security.authorization.RequiredFactorError;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\nimport org.springframework.security.web.savedrequest.NullRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.ThrowableAnalyzer;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AccessDeniedHandler} that adapts {@link AuthenticationEntryPoint}s based on\n * missing {@link GrantedAuthority}s. These authorities are specified in an\n * {@link AuthorityAuthorizationDecision} inside an {@link AuthorizationDeniedException}.\n *\n * <p>\n * This is helpful in adaptive authentication scenarios where an\n * {@link org.springframework.security.authorization.AuthorizationManager} indicates\n * additional authorities needed to access a given resource.\n * </p>\n *\n * <p>\n * For example, if an\n * {@link org.springframework.security.authorization.AuthorizationManager} states that to\n * access the home page, the user needs the {@code FACTOR_OTT} authority, then this\n * handler can be configured in the following way to redirect to the one-time-token login\n * page:\n * </p>\n *\n * <code>\n *     AccessDeniedHandler handler = DelegatingMissingAuthorityAccessDeniedHandler.builder()\n *         .addEntryPointFor(new LoginUrlAuthenticationEntryPoint(\"/login\"), GrantedAuthorities.FACTOR_OTT_AUTHORITY)\n *         .addEntryPointFor(new MyCustomEntryPoint(), GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY)\n *         .build();\n * </code>\n *\n * @author Josh Cummings\n * @since 7.0\n * @see AuthorizationDeniedException\n * @see AuthorityAuthorizationDecision\n * <code>org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer</code>\n */\npublic final class DelegatingMissingAuthorityAccessDeniedHandler implements AccessDeniedHandler {\n\n\tprivate final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();\n\n\tprivate final Map<String, AuthenticationEntryPoint> entryPoints;\n\n\tprivate RequestCache requestCache = new NullRequestCache();\n\n\tprivate AccessDeniedHandler defaultAccessDeniedHandler = new AccessDeniedHandlerImpl();\n\n\tprivate DelegatingMissingAuthorityAccessDeniedHandler(Map<String, AuthenticationEntryPoint> entryPoints) {\n\t\tAssert.notEmpty(entryPoints, \"entryPoints cannot be empty\");\n\t\tthis.entryPoints = entryPoints;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException denied)\n\t\t\tthrows IOException, ServletException {\n\t\tList<AuthorityRequiredFactorErrorEntry> errorEntries = authorityErrors(denied);\n\t\tList<RequiredFactorError> errors = errorEntries.stream()\n\t\t\t.map(AuthorityRequiredFactorErrorEntry::getError)\n\t\t\t.filter(Objects::nonNull)\n\t\t\t.toList();\n\t\tfor (AuthorityRequiredFactorErrorEntry authorityError : errorEntries) {\n\t\t\tString requiredAuthority = authorityError.getAuthority();\n\t\t\tAuthenticationEntryPoint entryPoint = this.entryPoints.get(requiredAuthority);\n\t\t\tif (entryPoint == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.requestCache.saveRequest(request, response);\n\t\t\tif (!errors.isEmpty()) {\n\t\t\t\trequest.setAttribute(WebAttributes.REQUIRED_FACTOR_ERRORS, errors);\n\t\t\t}\n\t\t\tString message = String.format(\"Missing Authorities %s\", requiredAuthority);\n\t\t\tAuthenticationException ex = new InsufficientAuthenticationException(message, denied);\n\t\t\tentryPoint.commence(request, response, ex);\n\t\t\treturn;\n\t\t}\n\t\tthis.defaultAccessDeniedHandler.handle(request, response, denied);\n\t}\n\n\t/**\n\t * Use this {@link AccessDeniedHandler} for {@link AccessDeniedException}s that this\n\t * handler doesn't support. By default, this uses {@link AccessDeniedHandlerImpl}.\n\t * @param defaultAccessDeniedHandler the default {@link AccessDeniedHandler} to use\n\t */\n\tpublic void setDefaultAccessDeniedHandler(AccessDeniedHandler defaultAccessDeniedHandler) {\n\t\tAssert.notNull(defaultAccessDeniedHandler, \"defaultAccessDeniedHandler cannot be null\");\n\t\tthis.defaultAccessDeniedHandler = defaultAccessDeniedHandler;\n\t}\n\n\t/**\n\t * Use this {@link RequestCache} to remember the current request.\n\t * <p>\n\t * Uses {@link NullRequestCache} by default\n\t * </p>\n\t * @param requestCache the {@link RequestCache} to use\n\t */\n\tpublic void setRequestCache(RequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCachgrantedaue cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\tprivate List<AuthorityRequiredFactorErrorEntry> authorityErrors(AccessDeniedException ex) {\n\t\tAuthorizationDeniedException denied = findAuthorizationDeniedException(ex);\n\t\tif (denied == null) {\n\t\t\treturn List.of();\n\t\t}\n\t\tAuthorizationResult authorizationResult = denied.getAuthorizationResult();\n\t\tif (authorizationResult instanceof FactorAuthorizationDecision factorDecision) {\n\t\t\t// @formatter:off\n\t\t\treturn factorDecision.getFactorErrors().stream()\n\t\t\t\t.map((error) -> {\n\t\t\t\t\tString authority = error.getRequiredFactor().getAuthority();\n\t\t\t\t\treturn new AuthorityRequiredFactorErrorEntry(authority, error);\n\t\t\t\t})\n\t\t\t\t.collect(Collectors.toList());\n\t\t\t// @formatter:on\n\t\t}\n\t\tif (authorizationResult instanceof AuthorityAuthorizationDecision authorityDecision) {\n\t\t\t// @formatter:off\n\t\t\treturn authorityDecision.getAuthorities().stream()\n\t\t\t\t\t.filter((ga) -> ga.getAuthority() != null)\n\t\t\t\t\t.map((grantedAuthority) -> {\n\t\t\t\t\tString authority = grantedAuthority.getAuthority();\n\t\t\t\t\tif (authority.startsWith(\"FACTOR_\")) {\n\t\t\t\t\t\tRequiredFactor required = RequiredFactor.withAuthority(authority).build();\n\t\t\t\t\t\treturn new AuthorityRequiredFactorErrorEntry(authority, RequiredFactorError.createMissing(required));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\treturn new AuthorityRequiredFactorErrorEntry(authority, null);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.collect(Collectors.toList());\n\t\t\t// @formatter:on\n\t\t}\n\t\treturn List.of();\n\t}\n\n\tprivate @Nullable AuthorizationDeniedException findAuthorizationDeniedException(AccessDeniedException ex) {\n\t\tif (ex instanceof AuthorizationDeniedException denied) {\n\t\t\treturn denied;\n\t\t}\n\t\tThrowable[] chain = this.throwableAnalyzer.determineCauseChain(ex);\n\t\treturn (AuthorizationDeniedException) this.throwableAnalyzer\n\t\t\t.getFirstThrowableOfType(AuthorizationDeniedException.class, chain);\n\t}\n\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * A builder for configuring the set of authority/entry-point pairs\n\t *\n\t * @author Josh Cummings\n\t * @since 7.0\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final Map<String, DelegatingAuthenticationEntryPoint.Builder> entryPointBuilderByAuthority = new LinkedHashMap<>();\n\n\t\tprivate Builder() {\n\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link AuthenticationEntryPoint} when the given\n\t\t * {@code missingAuthority} is missing from the authenticated user\n\t\t * @param entryPoint the {@link AuthenticationEntryPoint} for the given authority\n\t\t * @param missingAuthority the authority\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder addEntryPointFor(AuthenticationEntryPoint entryPoint, String missingAuthority) {\n\t\t\tDelegatingAuthenticationEntryPoint.Builder builder = DelegatingAuthenticationEntryPoint.builder()\n\t\t\t\t.addEntryPointFor(entryPoint, AnyRequestMatcher.INSTANCE);\n\t\t\tthis.entryPointBuilderByAuthority.put(missingAuthority, builder);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Use this {@link AuthenticationEntryPoint} when the given\n\t\t * {@code missingAuthority} is missing from the authenticated user\n\t\t * @param entryPoint a consumer to configure the underlying\n\t\t * {@link DelegatingAuthenticationEntryPoint}\n\t\t * @param missingAuthority the authority\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\tpublic Builder addEntryPointFor(Consumer<DelegatingAuthenticationEntryPoint.Builder> entryPoint,\n\t\t\t\tString missingAuthority) {\n\t\t\tentryPoint.accept(this.entryPointBuilderByAuthority.computeIfAbsent(missingAuthority,\n\t\t\t\t\t(k) -> DelegatingAuthenticationEntryPoint.builder()));\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DelegatingMissingAuthorityAccessDeniedHandler build() {\n\t\t\tMap<String, AuthenticationEntryPoint> entryPointByAuthority = new LinkedHashMap<>();\n\t\t\tthis.entryPointBuilderByAuthority.forEach((key, value) -> entryPointByAuthority.put(key, value.build()));\n\t\t\treturn new DelegatingMissingAuthorityAccessDeniedHandler(entryPointByAuthority);\n\t\t}\n\n\t}\n\n\t/**\n\t * A mapping of a {@link GrantedAuthority#getAuthority()} to a possibly null\n\t * {@link RequiredFactorError}.\n\t *\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tprivate static final class AuthorityRequiredFactorErrorEntry {\n\n\t\tprivate final String authority;\n\n\t\tprivate final @Nullable RequiredFactorError error;\n\n\t\tprivate AuthorityRequiredFactorErrorEntry(String authority, @Nullable RequiredFactorError error) {\n\t\t\tAssert.notNull(authority, \"authority cannot be null\");\n\t\t\tthis.authority = authority;\n\t\t\tthis.error = error;\n\t\t}\n\n\t\tprivate String getAuthority() {\n\t\t\treturn this.authority;\n\t\t}\n\n\t\tprivate @Nullable RequiredFactorError getError() {\n\t\t\treturn this.error;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authentication.InsufficientAuthenticationException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.ThrowableAnalyzer;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Handles any <code>AccessDeniedException</code> and <code>AuthenticationException</code>\n * thrown within the filter chain.\n * <p>\n * This filter is necessary because it provides the bridge between Java exceptions and\n * HTTP responses. It is solely concerned with maintaining the user interface. This filter\n * does not do any actual security enforcement.\n * <p>\n * If an {@link AuthenticationException} is detected, the filter will launch the\n * <code>authenticationEntryPoint</code>. This allows common handling of authentication\n * failures originating from Web or Method Security.\n * <p>\n * If an {@link AccessDeniedException} is detected, the filter will determine whether or\n * not the user is an anonymous user. If they are an anonymous user, the\n * <code>authenticationEntryPoint</code> will be launched. If they are not an anonymous\n * user, the filter will delegate to the\n * {@link org.springframework.security.web.access.AccessDeniedHandler}. By default the\n * filter will use\n * {@link org.springframework.security.web.access.AccessDeniedHandlerImpl}.\n * <p>\n * To use this filter, it is necessary to specify the following properties:\n * <ul>\n * <li><code>authenticationEntryPoint</code> indicates the handler that should commence\n * the authentication process if an <code>AuthenticationException</code> is detected. Note\n * that this may also switch the current protocol from http to https for an SSL\n * login.</li>\n * <li><tt>requestCache</tt> determines the strategy used to save a request during the\n * authentication process in order that it may be retrieved and reused once the user has\n * authenticated. The default implementation is {@link HttpSessionRequestCache}.</li>\n * </ul>\n *\n * @author Ben Alex\n * @author colin sampaleanu\n */\npublic class ExceptionTranslationFilter extends GenericFilterBean implements MessageSourceAware {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();\n\n\tprivate AuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();\n\n\tprivate final RequestCache requestCache;\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tpublic ExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint) {\n\t\tthis(authenticationEntryPoint, new HttpSessionRequestCache());\n\t}\n\n\tpublic ExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint, RequestCache requestCache) {\n\t\tAssert.notNull(authenticationEntryPoint, \"authenticationEntryPoint cannot be null\");\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.authenticationEntryPoint, \"authenticationEntryPoint must be specified\");\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\ttry {\n\t\t\tchain.doFilter(request, response);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\t// Try to extract a SpringSecurityException from the stacktrace\n\t\t\tThrowable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);\n\t\t\tRuntimeException securityException = (AuthenticationException) this.throwableAnalyzer\n\t\t\t\t.getFirstThrowableOfType(AuthenticationException.class, causeChain);\n\t\t\tif (securityException == null) {\n\t\t\t\tsecurityException = (AccessDeniedException) this.throwableAnalyzer\n\t\t\t\t\t.getFirstThrowableOfType(AccessDeniedException.class, causeChain);\n\t\t\t}\n\t\t\tif (securityException == null) {\n\t\t\t\trethrow(ex);\n\t\t\t}\n\t\t\tif (response.isCommitted()) {\n\t\t\t\tthrow new ServletException(\"Unable to handle the Spring Security Exception \"\n\t\t\t\t\t\t+ \"because the response is already committed.\", ex);\n\t\t\t}\n\t\t\thandleSpringSecurityException(request, response, chain, securityException);\n\t\t}\n\t}\n\n\tprivate void rethrow(Exception ex) throws ServletException {\n\t\t// Rethrow ServletExceptions and RuntimeExceptions as-is\n\t\tif (ex instanceof ServletException) {\n\t\t\tthrow (ServletException) ex;\n\t\t}\n\t\tif (ex instanceof RuntimeException) {\n\t\t\tthrow (RuntimeException) ex;\n\t\t}\n\t\t// Wrap other Exceptions. This shouldn't actually happen\n\t\t// as we've already covered all the possibilities for doFilter\n\t\tthrow new RuntimeException(ex);\n\t}\n\n\tpublic AuthenticationEntryPoint getAuthenticationEntryPoint() {\n\t\treturn this.authenticationEntryPoint;\n\t}\n\n\tprotected AuthenticationTrustResolver getAuthenticationTrustResolver() {\n\t\treturn this.authenticationTrustResolver;\n\t}\n\n\tprivate void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,\n\t\t\tFilterChain chain, @Nullable RuntimeException exception) throws IOException, ServletException {\n\t\tif (exception instanceof AuthenticationException) {\n\t\t\thandleAuthenticationException(request, response, chain, (AuthenticationException) exception);\n\t\t}\n\t\telse if (exception instanceof AccessDeniedException) {\n\t\t\thandleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);\n\t\t}\n\t}\n\n\tprivate void handleAuthenticationException(HttpServletRequest request, HttpServletResponse response,\n\t\t\tFilterChain chain, AuthenticationException exception) throws ServletException, IOException {\n\t\tthis.logger.trace(\"Sending to authentication entry point since authentication failed\", exception);\n\t\tsendStartAuthentication(request, response, chain, exception);\n\t}\n\n\tprivate void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,\n\t\t\tFilterChain chain, AccessDeniedException exception) throws ServletException, IOException {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tboolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication);\n\t\tif (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(LogMessage.format(\"Sending %s to authentication entry point since access is denied\",\n\t\t\t\t\t\tauthentication), exception);\n\t\t\t}\n\t\t\tAuthenticationException ex = new InsufficientAuthenticationException(\n\t\t\t\t\tthis.messages.getMessage(\"ExceptionTranslationFilter.insufficientAuthentication\",\n\t\t\t\t\t\t\t\"Full authentication is required to access this resource\"),\n\t\t\t\t\texception);\n\t\t\tex.setAuthenticationRequest(authentication);\n\t\t\tsendStartAuthentication(request, response, chain, ex);\n\t\t}\n\t\telse {\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(\n\t\t\t\t\t\tLogMessage.format(\"Sending %s to access denied handler since access is denied\", authentication),\n\t\t\t\t\t\texception);\n\t\t\t}\n\t\t\tthis.accessDeniedHandler.handle(request, response, exception);\n\t\t}\n\t}\n\n\tprotected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,\n\t\t\tAuthenticationException reason) throws ServletException, IOException {\n\t\t// SEC-112: Clear the SecurityContextHolder's Authentication, as the\n\t\t// existing Authentication is no longer considered valid\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\tthis.requestCache.saveRequest(request, response);\n\t\tthis.authenticationEntryPoint.commence(request, response, reason);\n\t}\n\n\tpublic void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {\n\t\tAssert.notNull(accessDeniedHandler, \"AccessDeniedHandler required\");\n\t\tthis.accessDeniedHandler = accessDeniedHandler;\n\t}\n\n\tpublic void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {\n\t\tAssert.notNull(authenticationTrustResolver, \"authenticationTrustResolver must not be null\");\n\t\tthis.authenticationTrustResolver = authenticationTrustResolver;\n\t}\n\n\tpublic void setThrowableAnalyzer(ThrowableAnalyzer throwableAnalyzer) {\n\t\tAssert.notNull(throwableAnalyzer, \"throwableAnalyzer must not be null\");\n\t\tthis.throwableAnalyzer = throwableAnalyzer;\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Default implementation of <code>ThrowableAnalyzer</code> which is capable of also\n\t * unwrapping <code>ServletException</code>s.\n\t */\n\tprivate static final class DefaultThrowableAnalyzer extends ThrowableAnalyzer {\n\n\t\t/**\n\t\t * @see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()\n\t\t */\n\t\t@Override\n\t\tprotected void initExtractorMap() {\n\t\t\tsuper.initExtractorMap();\n\t\t\tregisterExtractor(ServletException.class, (throwable) -> {\n\t\t\t\tThrowableAnalyzer.verifyThrowableHierarchy(throwable, ServletException.class);\n\t\t\t\treturn ((ServletException) throwable).getRootCause();\n\t\t\t});\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/HttpStatusAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AccessDeniedHandler} that sends an {@link HttpStatus} as a response.\n *\n * @author Sangyoon Jeong\n * @since 6.5\n */\npublic final class HttpStatusAccessDeniedHandler implements AccessDeniedHandler {\n\n\tprivate static final Log logger = LogFactory.getLog(HttpStatusAccessDeniedHandler.class);\n\n\tprivate final HttpStatus httpStatus;\n\n\t/**\n\t * Creates a new instance.\n\t * @param httpStatus the HttpStatus to set\n\t */\n\tpublic HttpStatusAccessDeniedHandler(HttpStatus httpStatus) {\n\t\tAssert.notNull(httpStatus, \"httpStatus cannot be null\");\n\t\tthis.httpStatus = httpStatus;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAccessDeniedException accessDeniedException) throws IOException, ServletException {\n\t\tlogger.debug(LogMessage.format(\"Access denied with status code %d\", this.httpStatus.value()));\n\n\t\tresponse.sendError(this.httpStatus.value(), \"Access Denied\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/IpAddressAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.util.matcher.IpAddressMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link AuthorizationManager}, that determines if the current request contains the\n * specified address or range of addresses\n *\n * @author brunodmartins\n * @since 6.3\n */\npublic final class IpAddressAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {\n\n\tprivate final IpAddressMatcher ipAddressMatcher;\n\n\tIpAddressAuthorizationManager(String ipAddress) {\n\t\tthis.ipAddressMatcher = new IpAddressMatcher(ipAddress);\n\t}\n\n\t/**\n\t * Creates an instance of {@link IpAddressAuthorizationManager} with the provided IP\n\t * address.\n\t * @param ipAddress the address or range of addresses from which the request must\n\t * @return the new instance\n\t */\n\tpublic static IpAddressAuthorizationManager hasIpAddress(String ipAddress) {\n\t\tAssert.notNull(ipAddress, \"ipAddress cannot be null\");\n\t\treturn new IpAddressAuthorizationManager(ipAddress);\n\t}\n\n\t@Override\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tRequestAuthorizationContext requestAuthorizationContext) {\n\t\treturn new AuthorizationDecision(\n\t\t\t\tthis.ipAddressMatcher.matcher(requestAuthorizationContext.getRequest()).isMatch());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/NoOpAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.access.AccessDeniedException;\n\n/**\n * An {@link AccessDeniedHandler} implementation that does nothing.\n *\n * @author Marcus da Coregio\n * @since 6.2\n */\npublic class NoOpAccessDeniedHandler implements AccessDeniedHandler {\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAccessDeniedException accessDeniedException) throws IOException, ServletException {\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/ObservationMarkingAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.access.AccessDeniedException;\n\npublic final class ObservationMarkingAccessDeniedHandler implements AccessDeniedHandler {\n\n\tprivate final ObservationRegistry registry;\n\n\tpublic ObservationMarkingAccessDeniedHandler(ObservationRegistry registry) {\n\t\tthis.registry = registry;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception)\n\t\t\tthrows IOException, ServletException {\n\t\tObservation observation = this.registry.getCurrentObservation();\n\t\tif (observation != null) {\n\t\t\tobservation.error(exception);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/PathPatternRequestTransformer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.web.util.ServletRequestPathUtils;\n\n/**\n * Prepares the privilege evaluator's request for {@link PathPatternRequestMatcher}\n * authorization rules.\n *\n * @author Josh Cummings\n * @since 6.5\n */\npublic final class PathPatternRequestTransformer\n\t\timplements AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer {\n\n\t@Override\n\tpublic HttpServletRequest transform(HttpServletRequest request) {\n\t\tHttpServletRequest wrapped = new AttributesSupportingHttpServletRequest(request);\n\t\tServletRequestPathUtils.parseAndCache(wrapped);\n\t\treturn wrapped;\n\t}\n\n\tprivate static final class AttributesSupportingHttpServletRequest extends HttpServletRequestWrapper {\n\n\t\tprivate final Map<String, Object> attributes = new HashMap<>();\n\n\t\tAttributesSupportingHttpServletRequest(HttpServletRequest request) {\n\t\t\tsuper(request);\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object getAttribute(String name) {\n\t\t\treturn this.attributes.get(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAttribute(String name, Object value) {\n\t\t\tthis.attributes.put(name, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeAttribute(String name) {\n\t\t\tthis.attributes.remove(name);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/RequestMatcherDelegatingAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\nimport java.util.LinkedHashMap;\nimport java.util.Map.Entry;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AccessDeniedHandler} that delegates to other {@link AccessDeniedHandler}\n * instances based upon the type of {@link HttpServletRequest} passed into\n * {@link #handle(HttpServletRequest, HttpServletResponse, AccessDeniedException)}.\n *\n * @author Josh Cummings\n * @since 5.1\n *\n */\npublic final class RequestMatcherDelegatingAccessDeniedHandler implements AccessDeniedHandler {\n\n\tprivate final LinkedHashMap<RequestMatcher, AccessDeniedHandler> handlers;\n\n\tprivate final AccessDeniedHandler defaultHandler;\n\n\t/**\n\t * Creates a new instance\n\t * @param handlers a map of {@link RequestMatcher}s to {@link AccessDeniedHandler}s\n\t * that should be used. Each is considered in the order they are specified and only\n\t * the first {@link AccessDeniedHandler} is used.\n\t * @param defaultHandler the default {@link AccessDeniedHandler} that should be used\n\t * if none of the matchers match.\n\t */\n\tpublic RequestMatcherDelegatingAccessDeniedHandler(LinkedHashMap<RequestMatcher, AccessDeniedHandler> handlers,\n\t\t\tAccessDeniedHandler defaultHandler) {\n\t\tAssert.notEmpty(handlers, \"handlers cannot be null or empty\");\n\t\tAssert.notNull(defaultHandler, \"defaultHandler cannot be null\");\n\t\tthis.handlers = new LinkedHashMap<>(handlers);\n\t\tthis.defaultHandler = defaultHandler;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAccessDeniedException accessDeniedException) throws IOException, ServletException {\n\t\tfor (Entry<RequestMatcher, AccessDeniedHandler> entry : this.handlers.entrySet()) {\n\t\t\tRequestMatcher matcher = entry.getKey();\n\t\t\tif (matcher.matches(request)) {\n\t\t\t\tAccessDeniedHandler handler = entry.getValue();\n\t\t\t\thandler.handle(request, response, accessDeniedException);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis.defaultHandler.handle(request, response, accessDeniedException);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.ServletContextAware;\n\n/**\n * A {@link WebInvocationPrivilegeEvaluator} which delegates to a list of\n * {@link WebInvocationPrivilegeEvaluator} based on a\n * {@link org.springframework.security.web.util.matcher.RequestMatcher} evaluation\n *\n * @author Marcus Da Coregio\n * @since 5.5.5\n * @deprecated please use {@link AuthorizationManagerWebInvocationPrivilegeEvaluator} and\n * adapt any delegate {@link WebInvocationPrivilegeEvaluator}s into\n * {@link AuthorizationManager}s\n */\n@Deprecated\npublic final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator\n\t\timplements WebInvocationPrivilegeEvaluator, ServletContextAware {\n\n\tprivate final List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> delegates;\n\n\tprivate @Nullable ServletContext servletContext;\n\n\tpublic RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(\n\t\t\tList<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries) {\n\t\tAssert.notNull(requestMatcherPrivilegeEvaluatorsEntries, \"requestMatcherPrivilegeEvaluators cannot be null\");\n\t\tfor (RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> entry : requestMatcherPrivilegeEvaluatorsEntries) {\n\t\t\tAssert.notNull(entry.getRequestMatcher(), \"requestMatcher cannot be null\");\n\t\t\tAssert.notNull(entry.getEntry(), \"webInvocationPrivilegeEvaluators cannot be null\");\n\t\t}\n\t\tthis.delegates = requestMatcherPrivilegeEvaluatorsEntries;\n\t}\n\n\t/**\n\t * Determines whether the user represented by the supplied <tt>Authentication</tt>\n\t * object is allowed to invoke the supplied URI.\n\t * <p>\n\t * Uses the provided URI in the\n\t * {@link org.springframework.security.web.util.matcher.RequestMatcher#matches(HttpServletRequest)}\n\t * for every {@code RequestMatcher} configured. If no {@code RequestMatcher} is\n\t * matched, or if there is not an available {@code WebInvocationPrivilegeEvaluator},\n\t * returns {@code true}.\n\t * @param uri the URI excluding the context path (a default context path setting will\n\t * be used)\n\t * @return true if access is allowed, false if denied\n\t */\n\t@Override\n\tpublic boolean isAllowed(String uri, @Nullable Authentication authentication) {\n\t\tList<WebInvocationPrivilegeEvaluator> privilegeEvaluators = getDelegate(null, uri, null);\n\t\tif (privilegeEvaluators.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (WebInvocationPrivilegeEvaluator evaluator : privilegeEvaluators) {\n\t\t\tboolean isAllowed = evaluator.isAllowed(uri, authentication);\n\t\t\tif (!isAllowed) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Determines whether the user represented by the supplied <tt>Authentication</tt>\n\t * object is allowed to invoke the supplied URI.\n\t * <p>\n\t * Uses the provided URI in the\n\t * {@link org.springframework.security.web.util.matcher.RequestMatcher#matches(HttpServletRequest)}\n\t * for every {@code RequestMatcher} configured. If no {@code RequestMatcher} is\n\t * matched, or if there is not an available {@code WebInvocationPrivilegeEvaluator},\n\t * returns {@code true}.\n\t * @param uri the URI excluding the context path (a default context path setting will\n\t * be used)\n\t * @param contextPath the context path (may be null, in which case a default value\n\t * will be used).\n\t * @param method the HTTP method (or null, for any method)\n\t * @param authentication the <tt>Authentication</tt> instance whose authorities should\n\t * be used in evaluation whether access should be granted.\n\t * @return true if access is allowed, false if denied\n\t */\n\t@Override\n\tpublic boolean isAllowed(String contextPath, String uri, @Nullable String method,\n\t\t\t@Nullable Authentication authentication) {\n\t\tList<WebInvocationPrivilegeEvaluator> privilegeEvaluators = getDelegate(contextPath, uri, method);\n\t\tif (privilegeEvaluators.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (WebInvocationPrivilegeEvaluator evaluator : privilegeEvaluators) {\n\t\t\tboolean isAllowed = evaluator.isAllowed(contextPath, uri, method, authentication);\n\t\t\tif (!isAllowed) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate List<WebInvocationPrivilegeEvaluator> getDelegate(@Nullable String contextPath, String uri,\n\t\t\t@Nullable String method) {\n\t\tFilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);\n\t\tHttpServletRequest request = filterInvocation.getHttpRequest();\n\t\tfor (RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate : this.delegates) {\n\t\t\tif (delegate.getRequestMatcher().matches(request)) {\n\t\t\t\treturn delegate.getEntry();\n\t\t\t}\n\t\t}\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic void setServletContext(ServletContext servletContext) {\n\t\tthis.servletContext = servletContext;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/WebInvocationPrivilegeEvaluator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Allows users to determine whether they have privileges for a given web URI.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface WebInvocationPrivilegeEvaluator {\n\n\t/**\n\t * Determines whether the user represented by the supplied <tt>Authentication</tt>\n\t * object is allowed to invoke the supplied URI.\n\t * <p>\n\t * Note this will only match authorization rules that don't require a certain\n\t * {@code HttpMethod}.\n\t * @param uri the URI excluding the context path (a default context path setting will\n\t * be used)\n\t */\n\tboolean isAllowed(String uri, @Nullable Authentication authentication);\n\n\t/**\n\t * Determines whether the user represented by the supplied <tt>Authentication</tt>\n\t * object is allowed to invoke the supplied URI, with the given parameters.\n\t * <p>\n\t * Note:\n\t * <ul>\n\t * <li>The default implementation of <tt>FilterInvocationSecurityMetadataSource</tt>\n\t * disregards the <code>contextPath</code> when evaluating which secure object\n\t * metadata applies to a given request URI, so generally the <code>contextPath</code>\n\t * is unimportant unless you are using a custom\n\t * <code>FilterInvocationSecurityMetadataSource</code>.</li>\n\t * <li>this will only match authorization rules that don't require a certain\n\t * {@code HttpMethod}.</li>\n\t * </ul>\n\t * @param uri the URI excluding the context path\n\t * @param contextPath the context path (may be null).\n\t * @param method the HTTP method (or null, for any method)\n\t * @param authentication the <tt>Authentication</tt> instance whose authorities should\n\t * be used in evaluation whether access should be granted.\n\t * @return true if access is allowed, false if denied\n\t */\n\tboolean isAllowed(String contextPath, String uri, @Nullable String method, @Nullable Authentication authentication);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/expression/AbstractVariableEvaluationContextPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.security.web.FilterInvocation;\n\n/**\n * Exposes URI template variables as variables on the {@link EvaluationContext}. For\n * example, the pattern \"/user/{username}/**\" would expose a variable named username based\n * on the current URI.\n *\n * <p>\n * NOTE: This API is intentionally kept package scope as it may change in the future. It\n * may be nice to allow users to augment expressions and queries\n * </p>\n *\n * @author Rob Winch\n * @since 4.1\n */\nabstract class AbstractVariableEvaluationContextPostProcessor\n\t\timplements EvaluationContextPostProcessor<FilterInvocation> {\n\n\t@Override\n\tpublic final EvaluationContext postProcess(EvaluationContext context, FilterInvocation invocation) {\n\t\treturn new VariableEvaluationContext(context, invocation.getHttpRequest());\n\t}\n\n\tabstract Map<String, String> extractVariables(HttpServletRequest request);\n\n\t/**\n\t * {@link DelegatingEvaluationContext} to expose variable.\n\t */\n\tclass VariableEvaluationContext extends DelegatingEvaluationContext {\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tprivate @Nullable Map<String, String> variables;\n\n\t\tVariableEvaluationContext(EvaluationContext delegate, HttpServletRequest request) {\n\t\t\tsuper(delegate);\n\t\t\tthis.request = request;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object lookupVariable(String name) {\n\t\t\tObject result = super.lookupVariable(name);\n\t\t\tif (result != null) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tif (this.variables == null) {\n\t\t\t\tthis.variables = extractVariables(this.request);\n\t\t\t}\n\t\t\treturn this.variables.get(name);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.access.expression.AbstractSecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.access.expression.SecurityExpressionOperations;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authorization.AuthorizationManagerFactory;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link SecurityExpressionHandler} that uses a {@link RequestAuthorizationContext} to\n * create a {@link WebSecurityExpressionRoot}.\n *\n * @author Evgeniy Cheban\n * @author Steve Riesenberg\n * @since 5.8\n */\npublic class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpressionHandler<RequestAuthorizationContext>\n\t\timplements SecurityExpressionHandler<RequestAuthorizationContext> {\n\n\tprivate static final String DEFAULT_ROLE_PREFIX = \"ROLE_\";\n\n\tprivate String defaultRolePrefix = DEFAULT_ROLE_PREFIX;\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/spring-projects/spring-framework/issues/35371\n\tpublic EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tRequestAuthorizationContext context) {\n\t\tWebSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, context);\n\t\tStandardEvaluationContext ctx = new StandardEvaluationContext(root);\n\t\tctx.setBeanResolver(getBeanResolver());\n\t\tcontext.getVariables().forEach(ctx::setVariable);\n\t\treturn ctx;\n\t}\n\n\t@Override\n\tprotected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,\n\t\t\tRequestAuthorizationContext context) {\n\t\treturn createSecurityExpressionRoot(() -> authentication, context);\n\t}\n\n\tprivate WebSecurityExpressionRoot createSecurityExpressionRoot(\n\t\t\tSupplier<? extends @Nullable Authentication> authentication, RequestAuthorizationContext context) {\n\t\tWebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, context);\n\t\troot.setAuthorizationManagerFactory(getAuthorizationManagerFactory());\n\t\troot.setPermissionEvaluator(getPermissionEvaluator());\n\t\tif (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {\n\t\t\t// Ensure SecurityExpressionRoot can strip the custom role prefix\n\t\t\troot.setDefaultRolePrefix(this.defaultRolePrefix);\n\t\t}\n\t\treturn root;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. The default is\n\t * {@link AuthenticationTrustResolverImpl}.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tgetDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);\n\t}\n\n\t/**\n\t * Sets the default prefix to be added to\n\t * {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasAnyRole(String...)}\n\t * or\n\t * {@link org.springframework.security.access.expression.SecurityExpressionRoot#hasRole(String)}.\n\t * For example, if hasRole(\"ADMIN\") or hasRole(\"ROLE_ADMIN\") is passed in, then the\n\t * role ROLE_ADMIN will be used when the defaultRolePrefix is \"ROLE_\" (default).\n\t * @param defaultRolePrefix the default prefix to add to roles. The default is\n\t * \"ROLE_\".\n\t * @deprecated Use\n\t * {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic void setDefaultRolePrefix(String defaultRolePrefix) {\n\t\tAssert.notNull(defaultRolePrefix, \"defaultRolePrefix cannot be null\");\n\t\tgetDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);\n\t\tthis.defaultRolePrefix = defaultRolePrefix;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/expression/DelegatingEvaluationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.ConstructorResolver;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.MethodResolver;\nimport org.springframework.expression.OperatorOverloader;\nimport org.springframework.expression.PropertyAccessor;\nimport org.springframework.expression.TypeComparator;\nimport org.springframework.expression.TypeConverter;\nimport org.springframework.expression.TypeLocator;\nimport org.springframework.expression.TypedValue;\n\n/**\n * An instance of {@link EvaluationContext} that delegates to another implementation.\n *\n * @author Rob Winch\n * @since 4.1\n */\nclass DelegatingEvaluationContext implements EvaluationContext {\n\n\tprivate final EvaluationContext delegate;\n\n\tDelegatingEvaluationContext(EvaluationContext delegate) {\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic TypedValue getRootObject() {\n\t\treturn this.delegate.getRootObject();\n\t}\n\n\t@Override\n\tpublic List<ConstructorResolver> getConstructorResolvers() {\n\t\treturn this.delegate.getConstructorResolvers();\n\t}\n\n\t@Override\n\tpublic List<MethodResolver> getMethodResolvers() {\n\t\treturn this.delegate.getMethodResolvers();\n\t}\n\n\t@Override\n\tpublic List<PropertyAccessor> getPropertyAccessors() {\n\t\treturn this.delegate.getPropertyAccessors();\n\t}\n\n\t@Override\n\tpublic TypeLocator getTypeLocator() {\n\t\treturn this.delegate.getTypeLocator();\n\t}\n\n\t@Override\n\tpublic TypeConverter getTypeConverter() {\n\t\treturn this.delegate.getTypeConverter();\n\t}\n\n\t@Override\n\tpublic TypeComparator getTypeComparator() {\n\t\treturn this.delegate.getTypeComparator();\n\t}\n\n\t@Override\n\tpublic OperatorOverloader getOperatorOverloader() {\n\t\treturn this.delegate.getOperatorOverloader();\n\t}\n\n\t@Override\n\tpublic @Nullable BeanResolver getBeanResolver() {\n\t\treturn this.delegate.getBeanResolver();\n\t}\n\n\t@Override\n\tpublic void setVariable(String name, @Nullable Object value) {\n\t\tthis.delegate.setVariable(name, value);\n\t}\n\n\t@Override\n\tpublic @Nullable Object lookupVariable(String name) {\n\t\treturn this.delegate.lookupVariable(name);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/expression/EvaluationContextPostProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport org.springframework.expression.EvaluationContext;\n\n/**\n *\n * /** Allows post processing the {@link EvaluationContext}\n *\n * <p>\n * This API is intentionally kept package scope as it may evolve over time.\n * </p>\n *\n * @param <I> the invocation to use for post processing\n * @author Rob Winch\n * @since 4.1\n */\ninterface EvaluationContextPostProcessor<I> {\n\n\t/**\n\t * Allows post processing of the {@link EvaluationContext}. Implementations may return\n\t * a new instance of {@link EvaluationContext} or modify the {@link EvaluationContext}\n\t * that was passed in.\n\t * @param context the original {@link EvaluationContext}\n\t * @param invocation the security invocation object (i.e. FilterInvocation)\n\t * @return the updated context.\n\t */\n\tEvaluationContext postProcess(EvaluationContext context, I invocation);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/expression/FilterInvocationExpressionRoot.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.util.matcher.IpAddressMatcher;\n\n/**\n * @author Steve Riesenberg\n * @since 7.0\n */\nfinal class FilterInvocationExpressionRoot extends SecurityExpressionRoot<FilterInvocation> {\n\n\t/**\n\t * Allows direct access to the request object\n\t */\n\tpublic final HttpServletRequest request;\n\n\t/**\n\t * Creates an instance for the given {@link Supplier} of the {@link Authentication}\n\t * and {@link HttpServletRequest}.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to use\n\t * @param fi the {@link FilterInvocation} to use\n\t */\n\tFilterInvocationExpressionRoot(Supplier<Authentication> authentication, FilterInvocation fi) {\n\t\tsuper(authentication, fi);\n\t\tthis.request = fi.getRequest();\n\t}\n\n\t/**\n\t * Takes a specific IP address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or\n\t * 202.24.0.0/14).\n\t * @param ipAddress the address or range of addresses from which the request must\n\t * come.\n\t * @return true if the IP address of the current request is in the required range.\n\t */\n\tpublic boolean hasIpAddress(String ipAddress) {\n\t\tIpAddressMatcher matcher = new IpAddressMatcher(ipAddress);\n\t\treturn matcher.matches(this.request);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/expression/WebExpressionAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.security.access.expression.ExpressionUtils;\nimport org.springframework.security.access.expression.SecurityExpressionHandler;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ExpressionAuthorizationDecision;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.util.Assert;\n\n/**\n * An expression-based {@link AuthorizationManager} that determines the access by\n * evaluating the provided expression.\n *\n * @author Evgeniy Cheban\n * @since 5.8\n */\npublic final class WebExpressionAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {\n\n\tprivate SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler = new DefaultHttpSecurityExpressionHandler();\n\n\tprivate Expression expression;\n\n\t/**\n\t * Creates an instance.\n\t * @param expressionString the raw expression string to parse\n\t */\n\tpublic WebExpressionAuthorizationManager(String expressionString) {\n\t\tAssert.hasText(expressionString, \"expressionString cannot be empty\");\n\t\tthis.expression = this.expressionHandler.getExpressionParser().parseExpression(expressionString);\n\t}\n\n\tprivate WebExpressionAuthorizationManager(String expressionString,\n\t\t\tSecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) {\n\t\tAssert.hasText(expressionString, \"expressionString cannot be empty\");\n\t\tthis.expressionHandler = expressionHandler;\n\t\tthis.expression = expressionHandler.getExpressionParser().parseExpression(expressionString);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityExpressionHandler} to be used. The default is\n\t * {@link DefaultHttpSecurityExpressionHandler}.\n\t * @param expressionHandler the {@link SecurityExpressionHandler} to use\n\t * @deprecated Please use {@link #withDefaults()} or {@link #withExpressionHandler}\n\t */\n\t@Deprecated\n\tpublic void setExpressionHandler(SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) {\n\t\tAssert.notNull(expressionHandler, \"expressionHandler cannot be null\");\n\t\tthis.expressionHandler = expressionHandler;\n\t\tthis.expression = expressionHandler.getExpressionParser()\n\t\t\t.parseExpression(this.expression.getExpressionString());\n\t}\n\n\t/**\n\t * Determines the access by evaluating the provided expression.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to check\n\t * @param context the {@link RequestAuthorizationContext} to check\n\t * @return an {@link ExpressionAuthorizationDecision} based on the evaluated\n\t * expression\n\t */\n\t@Override\n\tpublic AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tRequestAuthorizationContext context) {\n\t\tEvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, context);\n\t\tboolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx);\n\t\treturn new ExpressionAuthorizationDecision(granted, this.expression);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"WebExpressionAuthorizationManager[expression='\" + this.expression + \"']\";\n\t}\n\n\t/**\n\t * Use a {@link DefaultHttpSecurityExpressionHandler} to create\n\t * {@link WebExpressionAuthorizationManager} instances.\n\t *\n\t * <p>\n\t * Note that publishing the {@link Builder} as a bean will allow the default\n\t * expression handler to be configured with a bean provider so that expressions can\n\t * reference beans\n\t * @return a {@link Builder} for constructing\n\t * {@link WebExpressionAuthorizationManager} instances\n\t * @since 7.0\n\t */\n\tpublic static Builder withDefaults() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Use this {@link SecurityExpressionHandler} to create\n\t * {@link WebExpressionAuthorizationManager} instances\n\t * @param expressionHandler\n\t * @return a {@link Builder} for constructing\n\t * {@link WebExpressionAuthorizationManager} instances\n\t * @since 7.0\n\t */\n\tpublic static Builder withExpressionHandler(\n\t\t\tSecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) {\n\t\treturn new Builder(expressionHandler);\n\t}\n\n\t/**\n\t * A {@link Builder} for constructing {@link WebExpressionAuthorizationManager}\n\t * instances.\n\t *\n\t * <p>\n\t * May be reused to create multiple instances.\n\t *\n\t * @author Josh Cummings\n\t * @since 7.0\n\t */\n\tpublic static final class Builder implements ApplicationContextAware {\n\n\t\tprivate final SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler;\n\n\t\tprivate final boolean defaultExpressionHandler;\n\n\t\tprivate Builder() {\n\t\t\tthis.expressionHandler = new DefaultHttpSecurityExpressionHandler();\n\t\t\tthis.defaultExpressionHandler = true;\n\t\t}\n\n\t\tprivate Builder(SecurityExpressionHandler<RequestAuthorizationContext> expressionHandler) {\n\t\t\tthis.expressionHandler = expressionHandler;\n\t\t\tthis.defaultExpressionHandler = false;\n\t\t}\n\n\t\t/**\n\t\t * Create a {@link WebExpressionAuthorizationManager} using this\n\t\t * {@code expression}\n\t\t * @param expression the expression to evaluate\n\t\t * @return the resulting {@link AuthorizationManager}\n\t\t */\n\t\tpublic WebExpressionAuthorizationManager expression(String expression) {\n\t\t\treturn new WebExpressionAuthorizationManager(expression, this.expressionHandler);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\t\tif (this.defaultExpressionHandler) {\n\t\t\t\t((DefaultHttpSecurityExpressionHandler) this.expressionHandler).setApplicationContext(context);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/expression/WebSecurityExpressionRoot.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.util.matcher.IpAddressMatcher;\n\n/**\n * @author Luke Taylor\n * @author Evgeniy Cheban\n * @author Steve Riesenberg\n * @since 3.0\n */\npublic class WebSecurityExpressionRoot extends SecurityExpressionRoot<RequestAuthorizationContext> {\n\n\t/**\n\t * Allows direct access to the request object\n\t */\n\tpublic final HttpServletRequest request;\n\n\t/**\n\t * @deprecated Use\n\t * {@link #WebSecurityExpressionRoot(Supplier, RequestAuthorizationContext)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\tpublic WebSecurityExpressionRoot(@Nullable Authentication a, FilterInvocation fi) {\n\t\tthis(() -> a, new RequestAuthorizationContext(fi.getRequest()));\n\t}\n\n\t/**\n\t * Creates an instance for the given {@link Supplier} of the {@link Authentication}\n\t * and {@link HttpServletRequest}.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to use\n\t * @param request the {@link HttpServletRequest} to use\n\t * @since 5.8\n\t * @deprecated Use\n\t * {@link #WebSecurityExpressionRoot(Supplier, RequestAuthorizationContext)} instead\n\t */\n\t@Deprecated(since = \"7.0\")\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1246\n\tpublic WebSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tHttpServletRequest request) {\n\t\tsuper(authentication, new RequestAuthorizationContext(request));\n\t\tthis.request = request;\n\t}\n\n\t/**\n\t * Creates an instance for the given {@link Supplier} of the {@link Authentication}\n\t * and {@link HttpServletRequest}.\n\t * @param authentication the {@link Supplier} of the {@link Authentication} to use\n\t * @param context the {@link RequestAuthorizationContext} to use\n\t * @since 7.0\n\t */\n\tpublic WebSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tRequestAuthorizationContext context) {\n\t\tsuper(authentication, context);\n\t\tthis.request = context.getRequest();\n\t}\n\n\t/**\n\t * Takes a specific IP address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or\n\t * 202.24.0.0/14).\n\t * @param ipAddress the address or range of addresses from which the request must\n\t * come.\n\t * @return true if the IP address of the current request is in the required range.\n\t */\n\tpublic boolean hasIpAddress(String ipAddress) {\n\t\tIpAddressMatcher matcher = new IpAddressMatcher(ipAddress);\n\t\treturn matcher.matches(this.request);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/expression/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Implementation of web security expressions.\n */\n@NullMarked\npackage org.springframework.security.web.access.expression;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport java.io.IOException;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.event.AuthorizationDeniedEvent;\nimport org.springframework.security.authorization.event.AuthorizationGrantedEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * An authorization filter that restricts access to the URL using\n * {@link AuthorizationManager}.\n *\n * @author Evgeniy Cheban\n * @since 5.5\n */\npublic class AuthorizationFilter extends GenericFilterBean {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final AuthorizationManager<HttpServletRequest> authorizationManager;\n\n\tprivate AuthorizationEventPublisher eventPublisher = new NoopAuthorizationEventPublisher();\n\n\tprivate boolean observeOncePerRequest = false;\n\n\tprivate boolean filterErrorDispatch = true;\n\n\tprivate boolean filterAsyncDispatch = true;\n\n\t/**\n\t * Creates an instance.\n\t * @param authorizationManager the {@link AuthorizationManager} to use\n\t */\n\tpublic AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {\n\t\tAssert.notNull(authorizationManager, \"authorizationManager cannot be null\");\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tHttpServletRequest request = (HttpServletRequest) servletRequest;\n\t\tHttpServletResponse response = (HttpServletResponse) servletResponse;\n\n\t\tif (this.observeOncePerRequest && isApplied(request)) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tif (skipDispatch(request)) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tString alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();\n\t\trequest.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);\n\t\ttry {\n\t\t\tAuthorizationResult result = this.authorizationManager.authorize(this::getAuthentication, request);\n\t\t\tthis.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, result);\n\t\t\tif (result != null && !result.isGranted()) {\n\t\t\t\tthrow new AuthorizationDeniedException(\"Access Denied\", result);\n\t\t\t}\n\t\t\tchain.doFilter(request, response);\n\t\t}\n\t\tfinally {\n\t\t\trequest.removeAttribute(alreadyFilteredAttributeName);\n\t\t}\n\t}\n\n\tprivate boolean skipDispatch(HttpServletRequest request) {\n\t\tif (DispatcherType.ERROR.equals(request.getDispatcherType()) && !this.filterErrorDispatch) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn DispatcherType.ASYNC.equals(request.getDispatcherType()) && !this.filterAsyncDispatch;\n\t}\n\n\tprivate boolean isApplied(HttpServletRequest request) {\n\t\treturn request.getAttribute(getAlreadyFilteredAttributeName()) != null;\n\t}\n\n\tprivate String getAlreadyFilteredAttributeName() {\n\t\tString name = getFilterName();\n\t\tif (name == null) {\n\t\t\tname = getClass().getName();\n\t\t}\n\t\treturn name + \".APPLIED\";\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tprivate Authentication getAuthentication() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\tthrow new AuthenticationCredentialsNotFoundException(\n\t\t\t\t\t\"An Authentication object was not found in the SecurityContext\");\n\t\t}\n\t\treturn authentication;\n\t}\n\n\t/**\n\t * Use this {@link AuthorizationEventPublisher} to publish\n\t * {@link AuthorizationDeniedEvent}s and {@link AuthorizationGrantedEvent}s.\n\t * @param eventPublisher the {@link ApplicationEventPublisher} to use\n\t * @since 5.7\n\t */\n\tpublic void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {\n\t\tAssert.notNull(eventPublisher, \"eventPublisher cannot be null\");\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\t/**\n\t * Gets the {@link AuthorizationManager} used by this filter\n\t * @return the {@link AuthorizationManager}\n\t */\n\tpublic AuthorizationManager<HttpServletRequest> getAuthorizationManager() {\n\t\treturn this.authorizationManager;\n\t}\n\n\tpublic boolean isObserveOncePerRequest() {\n\t\treturn this.observeOncePerRequest;\n\t}\n\n\t/**\n\t * Sets whether this filter apply only once per request. By default, this is\n\t * <code>false</code>, meaning the filter will execute on every request. Sometimes\n\t * users may wish it to execute more than once per request, such as when JSP forwards\n\t * are being used and filter security is desired on each included fragment of the HTTP\n\t * request.\n\t * @param observeOncePerRequest whether the filter should only be applied once per\n\t * request\n\t */\n\tpublic void setObserveOncePerRequest(boolean observeOncePerRequest) {\n\t\tthis.observeOncePerRequest = observeOncePerRequest;\n\t}\n\n\t/**\n\t * If set to true, the filter will be applied to error dispatcher. Defaults to\n\t * {@code true}.\n\t * @param filterErrorDispatch whether the filter should be applied to error dispatcher\n\t */\n\tpublic void setFilterErrorDispatch(boolean filterErrorDispatch) {\n\t\tthis.filterErrorDispatch = filterErrorDispatch;\n\t}\n\n\t/**\n\t * If set to true, the filter will be applied to the async dispatcher. Defaults to\n\t * {@code true}.\n\t * @param filterAsyncDispatch whether the filter should be applied to async dispatch\n\t */\n\tpublic void setFilterAsyncDispatch(boolean filterAsyncDispatch) {\n\t\tthis.filterAsyncDispatch = filterAsyncDispatch;\n\t}\n\n\tprivate static class NoopAuthorizationEventPublisher implements AuthorizationEventPublisher {\n\n\t\t@Override\n\t\tpublic <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,\n\t\t\t\t@Nullable AuthorizationResult result) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/intercept/RequestAuthorizationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\n/**\n * An {@link HttpServletRequest} authorization context.\n *\n * @author Evgeniy Cheban\n * @since 5.5\n */\npublic final class RequestAuthorizationContext {\n\n\tprivate final HttpServletRequest request;\n\n\tprivate final Map<String, String> variables;\n\n\t/**\n\t * Creates an instance.\n\t * @param request the {@link HttpServletRequest} to use\n\t */\n\tpublic RequestAuthorizationContext(HttpServletRequest request) {\n\t\tthis(request, Collections.emptyMap());\n\t}\n\n\t/**\n\t * Creates an instance.\n\t * @param request the {@link HttpServletRequest} to use\n\t * @param variables a map containing key-value pairs representing extracted variable\n\t * names and variable values\n\t */\n\tpublic RequestAuthorizationContext(HttpServletRequest request, Map<String, String> variables) {\n\t\tthis.request = request;\n\t\tthis.variables = variables;\n\t}\n\n\t/**\n\t * Returns the {@link HttpServletRequest}.\n\t * @return the {@link HttpServletRequest} to use\n\t */\n\tpublic HttpServletRequest getRequest() {\n\t\treturn this.request;\n\t}\n\n\t/**\n\t * Returns the extracted variable values where the key is the variable name and the\n\t * value is the variable value.\n\t * @return a map containing key-value pairs representing extracted variable names and\n\t * variable values\n\t */\n\tpublic Map<String, String> getVariables() {\n\t\treturn this.variables;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/intercept/RequestKey.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * @author Luke Taylor\n * @since 2.0\n */\npublic class RequestKey {\n\n\tprivate final String url;\n\n\tprivate final @Nullable String method;\n\n\tpublic RequestKey(String url) {\n\t\tthis(url, null);\n\t}\n\n\tpublic RequestKey(String url, @Nullable String method) {\n\t\tAssert.notNull(url, \"url cannot be null\");\n\t\tthis.url = url;\n\t\tthis.method = method;\n\t}\n\n\tString getUrl() {\n\t\treturn this.url;\n\t}\n\n\t@Nullable String getMethod() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (!(obj instanceof RequestKey key)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!this.url.equals(key.url)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.method == null) {\n\t\t\treturn key.method == null;\n\t\t}\n\t\treturn this.method.equals(key.method);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.url.hashCode();\n\t\tresult = 31 * result + ((this.method != null) ? this.method.hashCode() : 0);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder(this.url.length() + 7);\n\t\tsb.append(\"[\");\n\t\tif (this.method != null) {\n\t\t\tsb.append(this.method).append(\",\");\n\t\t}\n\t\tsb.append(this.url);\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.SingleResultAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthorizationManager} which delegates to a specific\n * {@link AuthorizationManager} based on a {@link RequestMatcher} evaluation.\n *\n * @author Evgeniy Cheban\n * @author Parikshit Dutta\n * @since 5.5\n */\npublic final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {\n\n\tprivate static final AuthorizationDecision DENY = new AuthorizationDecision(false);\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final List<RequestMatcherEntry<AuthorizationManager<? super RequestAuthorizationContext>>> mappings;\n\n\tprivate RequestMatcherDelegatingAuthorizationManager(\n\t\t\tList<RequestMatcherEntry<AuthorizationManager<? super RequestAuthorizationContext>>> mappings) {\n\t\tAssert.notEmpty(mappings, \"mappings cannot be empty\");\n\t\tthis.mappings = mappings;\n\t}\n\n\t@Override\n\tpublic @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,\n\t\t\tHttpServletRequest request) {\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(LogMessage.format(\"Authorizing %s\", requestLine(request)));\n\t\t}\n\t\tfor (RequestMatcherEntry<AuthorizationManager<? super RequestAuthorizationContext>> mapping : this.mappings) {\n\n\t\t\tRequestMatcher matcher = mapping.getRequestMatcher();\n\t\t\tMatchResult matchResult = matcher.matcher(request);\n\t\t\tif (matchResult.isMatch()) {\n\t\t\t\tAuthorizationManager<? super RequestAuthorizationContext> manager = mapping.getEntry();\n\t\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\t\tthis.logger.trace(\n\t\t\t\t\t\t\tLogMessage.format(\"Checking authorization on %s using %s\", requestLine(request), manager));\n\t\t\t\t}\n\t\t\t\treturn manager.authorize(authentication,\n\t\t\t\t\t\tnew RequestAuthorizationContext(request, matchResult.getVariables()));\n\t\t\t}\n\t\t}\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(LogMessage.of(() -> \"Denying request since did not find matching RequestMatcher\"));\n\t\t}\n\t\treturn DENY;\n\t}\n\n\tprivate static String requestLine(HttpServletRequest request) {\n\t\treturn request.getMethod() + \" \" + UrlUtils.buildRequestUrl(request);\n\t}\n\n\t/**\n\t * Creates a builder for {@link RequestMatcherDelegatingAuthorizationManager}.\n\t * @return the new {@link Builder} instance\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * A builder for {@link RequestMatcherDelegatingAuthorizationManager}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate boolean anyRequestConfigured;\n\n\t\tprivate final List<RequestMatcherEntry<AuthorizationManager<? super RequestAuthorizationContext>>> mappings = new ArrayList<>();\n\n\t\t/**\n\t\t * Maps a {@link RequestMatcher} to an {@link AuthorizationManager}.\n\t\t * @param matcher the {@link RequestMatcher} to use\n\t\t * @param manager the {@link AuthorizationManager} to use\n\t\t * @return the {@link Builder} for further customizations\n\t\t */\n\t\tpublic Builder add(RequestMatcher matcher, AuthorizationManager<? super RequestAuthorizationContext> manager) {\n\t\t\tAssert.state(!this.anyRequestConfigured, \"Can't add mappings after anyRequest\");\n\t\t\tAssert.notNull(matcher, \"matcher cannot be null\");\n\t\t\tAssert.notNull(manager, \"manager cannot be null\");\n\t\t\tthis.mappings.add(new RequestMatcherEntry<>(matcher, manager));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows to configure the {@link RequestMatcher} to {@link AuthorizationManager}\n\t\t * mappings.\n\t\t * @param mappingsConsumer used to configure the {@link RequestMatcher} to\n\t\t * {@link AuthorizationManager} mappings.\n\t\t * @return the {@link Builder} for further customizations\n\t\t * @since 5.7\n\t\t */\n\t\tpublic Builder mappings(\n\t\t\t\tConsumer<List<RequestMatcherEntry<AuthorizationManager<? super RequestAuthorizationContext>>>> mappingsConsumer) {\n\t\t\tAssert.state(!this.anyRequestConfigured, \"Can't configure mappings after anyRequest\");\n\t\t\tAssert.notNull(mappingsConsumer, \"mappingsConsumer cannot be null\");\n\t\t\tmappingsConsumer.accept(this.mappings);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Maps any request.\n\t\t * @return the {@link AuthorizedUrl} for further customizations\n\t\t * @since 6.2\n\t\t */\n\t\tpublic AuthorizedUrl anyRequest() {\n\t\t\tAssert.state(!this.anyRequestConfigured, \"Can't configure anyRequest after itself\");\n\t\t\tthis.anyRequestConfigured = true;\n\t\t\treturn new AuthorizedUrl(AnyRequestMatcher.INSTANCE);\n\t\t}\n\n\t\t/**\n\t\t * Maps {@link RequestMatcher}s to {@link AuthorizationManager}.\n\t\t * @param matchers the {@link RequestMatcher}s to map\n\t\t * @return the {@link AuthorizedUrl} for further customizations\n\t\t * @since 6.2\n\t\t */\n\t\tpublic AuthorizedUrl requestMatchers(RequestMatcher... matchers) {\n\t\t\tAssert.state(!this.anyRequestConfigured, \"Can't configure requestMatchers after anyRequest\");\n\t\t\treturn new AuthorizedUrl(matchers);\n\t\t}\n\n\t\t/**\n\t\t * Creates a {@link RequestMatcherDelegatingAuthorizationManager} instance.\n\t\t * @return the {@link RequestMatcherDelegatingAuthorizationManager} instance\n\t\t */\n\t\tpublic RequestMatcherDelegatingAuthorizationManager build() {\n\t\t\treturn new RequestMatcherDelegatingAuthorizationManager(this.mappings);\n\t\t}\n\n\t\t/**\n\t\t * An object that allows configuring the {@link AuthorizationManager} for\n\t\t * {@link RequestMatcher}s.\n\t\t *\n\t\t * @author Evgeniy Cheban\n\t\t * @since 6.2\n\t\t */\n\t\tpublic final class AuthorizedUrl {\n\n\t\t\tprivate final List<RequestMatcher> matchers;\n\n\t\t\tprivate AuthorizedUrl(RequestMatcher... matchers) {\n\t\t\t\tthis(List.of(matchers));\n\t\t\t}\n\n\t\t\tprivate AuthorizedUrl(List<RequestMatcher> matchers) {\n\t\t\t\tthis.matchers = matchers;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that URLs are allowed by anyone.\n\t\t\t * @return the {@link Builder} for further customizations\n\t\t\t */\n\t\t\tpublic Builder permitAll() {\n\t\t\t\treturn access(SingleResultAuthorizationManager.permitAll());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that URLs are not allowed by anyone.\n\t\t\t * @return the {@link Builder} for further customizations\n\t\t\t */\n\t\t\tpublic Builder denyAll() {\n\t\t\t\treturn access(SingleResultAuthorizationManager.denyAll());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that URLs are allowed by any authenticated user.\n\t\t\t * @return the {@link Builder} for further customizations\n\t\t\t */\n\t\t\tpublic Builder authenticated() {\n\t\t\t\treturn access(AuthenticatedAuthorizationManager.authenticated());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that URLs are allowed by users who have authenticated and were not\n\t\t\t * \"remembered\".\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder fullyAuthenticated() {\n\t\t\t\treturn access(AuthenticatedAuthorizationManager.fullyAuthenticated());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that URLs are allowed by users that have been remembered.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder rememberMe() {\n\t\t\t\treturn access(AuthenticatedAuthorizationManager.rememberMe());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specify that URLs are allowed by anonymous users.\n\t\t\t * @return the {@link Builder} for further customization\n\t\t\t */\n\t\t\tpublic Builder anonymous() {\n\t\t\t\treturn access(AuthenticatedAuthorizationManager.anonymous());\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specifies a user requires a role.\n\t\t\t * @param role the role that should be required which is prepended with ROLE_\n\t\t\t * automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_\n\t\t\t * @return {@link Builder} for further customizations\n\t\t\t */\n\t\t\tpublic Builder hasRole(String role) {\n\t\t\t\treturn access(AuthorityAuthorizationManager.hasRole(role));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specifies that a user requires one of many roles.\n\t\t\t * @param roles the roles that the user should have at least one of (i.e.\n\t\t\t * ADMIN, USER, etc). Each role should not start with ROLE_ since it is\n\t\t\t * automatically prepended already\n\t\t\t * @return the {@link Builder} for further customizations\n\t\t\t */\n\t\t\tpublic Builder hasAnyRole(String... roles) {\n\t\t\t\treturn access(AuthorityAuthorizationManager.hasAnyRole(roles));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specifies a user requires an authority.\n\t\t\t * @param authority the authority that should be required\n\t\t\t * @return the {@link Builder} for further customizations\n\t\t\t */\n\t\t\tpublic Builder hasAuthority(String authority) {\n\t\t\t\treturn access(AuthorityAuthorizationManager.hasAuthority(authority));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Specifies that a user requires one of many authorities.\n\t\t\t * @param authorities the authorities that the user should have at least one\n\t\t\t * of (i.e. ROLE_USER, ROLE_ADMIN, etc)\n\t\t\t * @return the {@link Builder} for further customizations\n\t\t\t */\n\t\t\tpublic Builder hasAnyAuthority(String... authorities) {\n\t\t\t\treturn access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));\n\t\t\t}\n\n\t\t\tprivate Builder access(AuthorizationManager<RequestAuthorizationContext> manager) {\n\t\t\t\tfor (RequestMatcher matcher : this.matchers) {\n\t\t\t\t\tBuilder.this.mappings.add(new RequestMatcherEntry<>(matcher, manager));\n\t\t\t\t}\n\t\t\t\treturn Builder.this;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/intercept/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Enforcement of security for HTTP requests, typically by the URL requested.\n */\n@NullMarked\npackage org.springframework.security.web.access.intercept;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/access/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Access-control related classes and packages.\n */\n@NullMarked\npackage org.springframework.security.web.access;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/aot/hint/WebMvcSecurityRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.aot.hint;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.security.web.access.expression.WebSecurityExpressionRoot;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\n\n/**\n * {@link RuntimeHintsRegistrar} for WebMVC classes\n *\n * @author Marcus Da Coregio\n * @author Daniel Garnier-Moiroux\n * @since 6.0\n */\nclass WebMvcSecurityRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\thints.reflection()\n\t\t\t.registerType(WebSecurityExpressionRoot.class, (builder) -> builder\n\t\t\t\t.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));\n\t\thints.reflection()\n\t\t\t.registerType(\n\t\t\t\t\tTypeReference\n\t\t\t\t\t\t.of(\"org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler$SupplierCsrfToken\"),\n\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS);\n\t\thints.reflection()\n\t\t\t.registerType(PreAuthenticatedAuthenticationToken.class,\n\t\t\t\t\t(builder) -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,\n\t\t\t\t\t\t\tMemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));\n\n\t\tClassPathResource css = new ClassPathResource(\"org/springframework/security/default-ui.css\");\n\t\tif (css.exists()) {\n\t\t\thints.resources().registerResource(css);\n\t\t}\n\n\t\tClassPathResource webauthnJavascript = new ClassPathResource(\n\t\t\t\t\"org/springframework/security/spring-security-webauthn.js\");\n\t\tif (webauthnJavascript.exists()) {\n\t\t\thints.resources().registerResource(webauthnJavascript);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/aot/hint/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Runtime hints for AOT web package.\n */\n@NullMarked\npackage org.springframework.security.web.aot.hint;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.lang.Contract;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Abstract processor of browser-based HTTP-based authentication requests.\n *\n * <h3>Authentication Process</h3>\n *\n * The filter requires that you set the <tt>authenticationManager</tt> property. An\n * <tt>AuthenticationManager</tt> is required to process the authentication request tokens\n * created by implementing classes.\n * <p>\n * This filter will intercept a request and attempt to perform authentication from that\n * request if the request matches the\n * {@link #setRequiresAuthenticationRequestMatcher(RequestMatcher)}.\n * <p>\n * Authentication is performed by the\n * {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse)\n * attemptAuthentication} method, which must be implemented by subclasses.\n *\n * <h4>Authentication Success</h4>\n *\n * If authentication is successful, the resulting {@link Authentication} object will be\n * placed into the <code>SecurityContext</code> for the current thread, which is\n * guaranteed to have already been created by an earlier filter.\n * <p>\n * The configured {@link #setAuthenticationSuccessHandler(AuthenticationSuccessHandler)\n * AuthenticationSuccessHandler} will then be called to take the redirect to the\n * appropriate destination after a successful login. The default behaviour is implemented\n * in a {@link SavedRequestAwareAuthenticationSuccessHandler} which will make use of any\n * <tt>DefaultSavedRequest</tt> set by the <tt>ExceptionTranslationFilter</tt> and\n * redirect the user to the URL contained therein. Otherwise it will redirect to the\n * webapp root \"/\". You can customize this behaviour by injecting a differently configured\n * instance of this class, or by using a different implementation.\n * <p>\n * See the\n * {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, Authentication)}\n * method for more information.\n *\n * <h4>Authentication Failure</h4>\n *\n * If authentication fails, it will delegate to the configured\n * {@link AuthenticationFailureHandler} to allow the failure information to be conveyed to\n * the client. The default implementation is {@link SimpleUrlAuthenticationFailureHandler}\n * , which sends a 401 error code to the client. It may also be configured with a failure\n * URL as an alternative. Again you can inject whatever behaviour you require here.\n *\n * <h4>Event Publication</h4>\n *\n * If authentication is successful, an {@link InteractiveAuthenticationSuccessEvent} will\n * be published via the application context. No events will be published if authentication\n * was unsuccessful, because this would generally be recorded via an\n * {@code AuthenticationManager}-specific application event.\n *\n * <h4>Session Authentication</h4>\n *\n * The class has an optional {@link SessionAuthenticationStrategy} which will be invoked\n * immediately after a successful call to {@code attemptAuthentication()}. Different\n * implementations {@link #setSessionAuthenticationStrategy(SessionAuthenticationStrategy)\n * can be injected} to enable things like session-fixation attack prevention or to control\n * the number of simultaneous sessions a principal may have.\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean\n\t\timplements ApplicationEventPublisherAware, MessageSourceAware {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprotected @Nullable ApplicationEventPublisher eventPublisher;\n\n\tprotected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate AuthenticationConverter authenticationConverter = (request) -> {\n\t\tthrow new AuthenticationCredentialsNotFoundException(\n\t\t\t\t\"Please either configure an AuthenticationConverter or override attemptAuthentication when extending AbstractAuthenticationProcessingFilter\");\n\t};\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate AuthenticationManager authenticationManager;\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate RememberMeServices rememberMeServices = new NullRememberMeServices();\n\n\tprivate RequestMatcher requiresAuthenticationRequestMatcher;\n\n\tprivate boolean continueChainBeforeSuccessfulAuthentication = false;\n\n\tprivate boolean continueChainWhenNoAuthenticationResult;\n\n\tprivate SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();\n\n\tprivate boolean allowSessionCreation = true;\n\n\tprivate AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();\n\n\tprivate AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();\n\n\tprivate SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();\n\n\tprivate boolean mfaEnabled;\n\n\t/**\n\t * @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.\n\t */\n\tprotected AbstractAuthenticationProcessingFilter(String defaultFilterProcessesUrl) {\n\t\tthis(pathPattern(defaultFilterProcessesUrl));\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param requiresAuthenticationRequestMatcher the {@link RequestMatcher} used to\n\t * determine if authentication is required. Cannot be null.\n\t */\n\tprotected AbstractAuthenticationProcessingFilter(RequestMatcher requiresAuthenticationRequestMatcher) {\n\t\tAssert.notNull(requiresAuthenticationRequestMatcher, \"requiresAuthenticationRequestMatcher cannot be null\");\n\t\tthis.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher;\n\t}\n\n\t/**\n\t * Creates a new instance with a default filterProcessesUrl and an\n\t * {@link AuthenticationManager}\n\t * @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.\n\t * @param authenticationManager the {@link AuthenticationManager} used to authenticate\n\t * an {@link Authentication} object. Cannot be null.\n\t */\n\tprotected AbstractAuthenticationProcessingFilter(String defaultFilterProcessesUrl,\n\t\t\tAuthenticationManager authenticationManager) {\n\t\tthis(pathPattern(defaultFilterProcessesUrl));\n\t\tsetAuthenticationManager(authenticationManager);\n\t}\n\n\t/**\n\t * Creates a new instance with a {@link RequestMatcher} and an\n\t * {@link AuthenticationManager}\n\t * @param requiresAuthenticationRequestMatcher the {@link RequestMatcher} used to\n\t * determine if authentication is required. Cannot be null.\n\t * @param authenticationManager the {@link AuthenticationManager} used to authenticate\n\t * an {@link Authentication} object. Cannot be null.\n\t */\n\tprotected AbstractAuthenticationProcessingFilter(RequestMatcher requiresAuthenticationRequestMatcher,\n\t\t\tAuthenticationManager authenticationManager) {\n\t\tsetRequiresAuthenticationRequestMatcher(requiresAuthenticationRequestMatcher);\n\t\tsetAuthenticationManager(authenticationManager);\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.authenticationManager, \"authenticationManager must be specified\");\n\t}\n\n\t/**\n\t * Invokes the {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse)\n\t * requiresAuthentication} method to determine whether the request is for\n\t * authentication and should be handled by this filter. If it is an authentication\n\t * request, the {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse)\n\t * attemptAuthentication} will be invoked to perform the authentication. There are\n\t * then three possible outcomes:\n\t * <ol>\n\t * <li>An <tt>Authentication</tt> object is returned. The configured\n\t * {@link SessionAuthenticationStrategy} will be invoked (to handle any\n\t * session-related behaviour such as creating a new session to protect against\n\t * session-fixation attacks) followed by the invocation of\n\t * {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, Authentication)}\n\t * method</li>\n\t * <li>An <tt>AuthenticationException</tt> occurs during authentication. The\n\t * {@link #unsuccessfulAuthentication(HttpServletRequest, HttpServletResponse, AuthenticationException)\n\t * unsuccessfulAuthentication} method will be invoked</li>\n\t * <li>Null is returned, indicating that the authentication process is incomplete. The\n\t * method will then return immediately, assuming that the subclass has done any\n\t * necessary work (such as redirects) to continue the authentication process. The\n\t * assumption is that a later request will be received by this method where the\n\t * returned <tt>Authentication</tt> object is not null.\n\t * </ol>\n\t */\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tif (!requiresAuthentication(request, response)) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tAuthentication authenticationResult = attemptAuthentication(request, response);\n\t\t\tif (authenticationResult == null) {\n\t\t\t\tif (this.continueChainWhenNoAuthenticationResult) {\n\t\t\t\t\tchain.doFilter(request, response);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// return immediately as subclass has indicated that it hasn't completed\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tAuthentication current = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (shouldPerformMfa(current, authenticationResult)) {\n\t\t\t\tauthenticationResult = authenticationResult.toBuilder()\n\t\t\t\t// @formatter:off\n\t\t\t\t\t.authorities((a) -> {\n\t\t\t\t\t\tSet<String> newAuthorities = a.stream()\n\t\t\t\t\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t\t\t\t\t.collect(Collectors.toUnmodifiableSet());\n\t\t\t\t\t\tfor (GrantedAuthority currentAuthority : current.getAuthorities()) {\n\t\t\t\t\t\t\tif (!newAuthorities.contains(currentAuthority.getAuthority())) {\n\t\t\t\t\t\t\t\ta.add(currentAuthority);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.build();\n\t\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t\tthis.sessionStrategy.onAuthentication(authenticationResult, request, response);\n\t\t\t// Authentication success\n\t\t\tif (this.continueChainBeforeSuccessfulAuthentication) {\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t}\n\t\t\tsuccessfulAuthentication(request, response, chain, authenticationResult);\n\t\t}\n\t\tcatch (InternalAuthenticationServiceException failed) {\n\t\t\tthis.logger.error(\"An internal error occurred while trying to authenticate the user.\", failed);\n\t\t\tunsuccessfulAuthentication(request, response, failed);\n\t\t}\n\t\tcatch (AuthenticationException ex) {\n\t\t\t// Authentication failed\n\t\t\tunsuccessfulAuthentication(request, response, ex);\n\t\t}\n\t}\n\n\t@Contract(\"null, _ -> false\")\n\tprivate boolean shouldPerformMfa(@Nullable Authentication current, Authentication authenticationResult) {\n\t\tif (!this.mfaEnabled) {\n\t\t\treturn false;\n\t\t}\n\t\tif (current == null || !current.isAuthenticated()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!declaresToBuilder(authenticationResult)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn current.getName().equals(authenticationResult.getName());\n\t}\n\n\tprivate static boolean declaresToBuilder(Authentication authentication) {\n\t\tfor (Method method : authentication.getClass().getDeclaredMethods()) {\n\t\t\tif (method.getName().equals(\"toBuilder\") && method.getParameterTypes().length == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Indicates whether this filter should attempt to process a login request for the\n\t * current invocation.\n\t * <p>\n\t * It strips any parameters from the \"path\" section of the request URL (such as the\n\t * jsessionid parameter in <em>https://host/myapp/index.html;jsessionid=blah</em>)\n\t * before matching against the <code>filterProcessesUrl</code> property.\n\t * <p>\n\t * Subclasses may override for special requirements, such as Tapestry integration.\n\t * @return <code>true</code> if the filter should attempt authentication,\n\t * <code>false</code> otherwise.\n\t */\n\tprotected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.requiresAuthenticationRequestMatcher.matches(request)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger\n\t\t\t\t.trace(LogMessage.format(\"Did not match request to %s\", this.requiresAuthenticationRequestMatcher));\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Performs actual authentication.\n\t * <p>\n\t * The implementation should do one of the following:\n\t * <ol>\n\t * <li>Return a populated authentication token for the authenticated user, indicating\n\t * successful authentication</li>\n\t * <li>Return null, indicating that the authentication process is still in progress.\n\t * Before returning, the implementation should perform any additional work required to\n\t * complete the process.</li>\n\t * <li>Throw an <tt>AuthenticationException</tt> if the authentication process\n\t * fails</li>\n\t * </ol>\n\t * @param request from which to extract parameters and perform the authentication\n\t * @param response the response, which may be needed if the implementation has to do a\n\t * redirect as part of a multi-stage authentication process (such as OIDC).\n\t * @return the authenticated user token, or null if authentication is incomplete.\n\t * @throws AuthenticationException if authentication fails.\n\t */\n\tpublic @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows AuthenticationException, IOException, ServletException {\n\t\tAuthentication authentication = this.authenticationConverter.convert(request);\n\t\tif (authentication == null) {\n\t\t\treturn null;\n\t\t}\n\t\tAuthentication result = this.authenticationManager.authenticate(authentication);\n\t\tif (result == null) {\n\t\t\tthrow new ServletException(\"AuthenticationManager should not return null Authentication object.\");\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Default behaviour for successful authentication.\n\t * <ol>\n\t * <li>Sets the successful <tt>Authentication</tt> object on the\n\t * {@link SecurityContextHolder}</li>\n\t * <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li>\n\t * <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured\n\t * <tt>ApplicationEventPublisher</tt></li>\n\t * <li>Delegates additional behaviour to the\n\t * {@link AuthenticationSuccessHandler}.</li>\n\t * </ol>\n\t *\n\t * Subclasses can override this method to continue the {@link FilterChain} after\n\t * successful authentication.\n\t * @param request\n\t * @param response\n\t * @param chain\n\t * @param authResult the object returned from the <tt>attemptAuthentication</tt>\n\t * method.\n\t * @throws IOException\n\t * @throws ServletException\n\t */\n\tprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,\n\t\t\tAuthentication authResult) throws IOException, ServletException {\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authResult);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Set SecurityContextHolder to %s\", authResult));\n\t\t}\n\t\tthis.rememberMeServices.loginSuccess(request, response, authResult);\n\t\tif (this.eventPublisher != null) {\n\t\t\tthis.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));\n\t\t}\n\t\tthis.successHandler.onAuthenticationSuccess(request, response, authResult);\n\t}\n\n\t/**\n\t * Default behaviour for unsuccessful authentication.\n\t * <ol>\n\t * <li>Clears the {@link SecurityContextHolder}</li>\n\t * <li>Stores the exception in the session (if it exists or\n\t * <tt>allowSessionCreation</tt> is set to <tt>true</tt>)</li>\n\t * <li>Informs the configured <tt>RememberMeServices</tt> of the failed login</li>\n\t * <li>Delegates additional behaviour to the\n\t * {@link AuthenticationFailureHandler}.</li>\n\t * </ol>\n\t */\n\tprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException failed) throws IOException, ServletException {\n\t\tthis.securityContextHolderStrategy.clearContext();\n\t\tthis.logger.trace(\"Failed to process authentication request\", failed);\n\t\tthis.logger.trace(\"Cleared SecurityContextHolder\");\n\t\tthis.logger.trace(\"Handling authentication failure\");\n\t\tthis.rememberMeServices.loginFail(request, response);\n\t\tthis.failureHandler.onAuthenticationFailure(request, response, failed);\n\t}\n\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t\tthis.continueChainWhenNoAuthenticationResult = true;\n\t}\n\n\tprotected AuthenticationManager getAuthenticationManager() {\n\t\treturn this.authenticationManager;\n\t}\n\n\tpublic void setAuthenticationManager(AuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t/**\n\t * Sets the URL that determines if authentication is required\n\t * @param filterProcessesUrl\n\t */\n\tpublic void setFilterProcessesUrl(String filterProcessesUrl) {\n\t\tsetRequiresAuthenticationRequestMatcher(pathPattern(filterProcessesUrl));\n\t}\n\n\tpublic final void setRequiresAuthenticationRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null or empty\");\n\t\tthis.requiresAuthenticationRequestMatcher = requestMatcher;\n\t}\n\n\tpublic RememberMeServices getRememberMeServices() {\n\t\treturn this.rememberMeServices;\n\t}\n\n\tpublic void setRememberMeServices(RememberMeServices rememberMeServices) {\n\t\tAssert.notNull(rememberMeServices, \"rememberMeServices cannot be null\");\n\t\tthis.rememberMeServices = rememberMeServices;\n\t}\n\n\t/**\n\t * Indicates if the filter chain should be continued prior to delegation to\n\t * {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, Authentication)}\n\t * , which may be useful in certain environment (such as Tapestry applications).\n\t * Defaults to <code>false</code>.\n\t */\n\tpublic void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {\n\t\tthis.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"AuthenticationDetailsSource required\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\tprotected boolean getAllowSessionCreation() {\n\t\treturn this.allowSessionCreation;\n\t}\n\n\tpublic void setAllowSessionCreation(boolean allowSessionCreation) {\n\t\tthis.allowSessionCreation = allowSessionCreation;\n\t}\n\n\t/**\n\t * Enables Multi-Factor Authentication (MFA) support.\n\t * @param mfaEnabled true to enable MFA support, false to disable it. Default is\n\t * false.\n\t */\n\tpublic void setMfaEnabled(boolean mfaEnabled) {\n\t\tthis.mfaEnabled = mfaEnabled;\n\t}\n\n\t/**\n\t * The session handling strategy which will be invoked immediately after an\n\t * authentication request is successfully processed by the\n\t * <tt>AuthenticationManager</tt>. Used, for example, to handle changing of the\n\t * session identifier to prevent session fixation attacks.\n\t * @param sessionStrategy the implementation to use. If not set a null implementation\n\t * is used.\n\t */\n\tpublic void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {\n\t\tthis.sessionStrategy = sessionStrategy;\n\t}\n\n\t/**\n\t * Sets the strategy used to handle a successful authentication. By default a\n\t * {@link SavedRequestAwareAuthenticationSuccessHandler} is used.\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {\n\t\tAssert.notNull(successHandler, \"successHandler cannot be null\");\n\t\tthis.successHandler = successHandler;\n\t}\n\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {\n\t\tAssert.notNull(failureHandler, \"failureHandler cannot be null\");\n\t\tthis.failureHandler = failureHandler;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * authentication success. The default action is not to save the\n\t * {@link SecurityContext}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tprotected AuthenticationSuccessHandler getSuccessHandler() {\n\t\treturn this.successHandler;\n\t}\n\n\tprotected AuthenticationFailureHandler getFailureHandler() {\n\t\treturn this.failureHandler;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationTargetUrlRequestHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Base class containing the logic used by strategies which handle redirection to a URL\n * and are passed an {@code Authentication} object as part of the contract. See\n * {@link AuthenticationSuccessHandler} and\n * {@link org.springframework.security.web.authentication.logout.LogoutSuccessHandler\n * LogoutSuccessHandler}, for example.\n * <p>\n * Uses the following logic sequence to determine how it should handle the\n * forward/redirect\n * <ul>\n * <li>If the {@code alwaysUseDefaultTargetUrl} property is set to true, the\n * {@code defaultTargetUrl} property will be used for the destination.</li>\n * <li>If a parameter matching the value of {@code targetUrlParameter} has been set on the\n * request, the value will be used as the destination. If you are enabling this\n * functionality, then you should ensure that the parameter cannot be used by an attacker\n * to redirect the user to a malicious site (by clicking on a URL with the parameter\n * included, for example). Typically it would be used when the parameter is included in\n * the login form and submitted with the username and password.</li>\n * <li>If the {@code useReferer} property is set, the \"Referer\" HTTP header value will be\n * used, if present.</li>\n * <li>As a fallback option, the {@code defaultTargetUrl} value will be used.</li>\n * </ul>\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic abstract class AbstractAuthenticationTargetUrlRequestHandler {\n\n\tprotected final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate @Nullable String targetUrlParameter = null;\n\n\tprivate String defaultTargetUrl = \"/\";\n\n\tprivate boolean alwaysUseDefaultTargetUrl = false;\n\n\tprivate boolean useReferer = false;\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprotected AbstractAuthenticationTargetUrlRequestHandler() {\n\t}\n\n\t/**\n\t * Invokes the configured {@code RedirectStrategy} with the URL returned by the\n\t * {@code determineTargetUrl} method.\n\t * <p>\n\t * The redirect will not be performed if the response has already been committed.\n\t */\n\tprotected void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) throws IOException, ServletException {\n\t\tString targetUrl = determineTargetUrl(request, response, authentication);\n\t\tif (response.isCommitted()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Did not redirect to %s since response already committed.\", targetUrl));\n\t\t\treturn;\n\t\t}\n\t\tthis.redirectStrategy.sendRedirect(request, response, targetUrl);\n\t}\n\n\t/**\n\t * Builds the target URL according to the logic defined in the main class Javadoc\n\t * @since 5.2\n\t */\n\tprotected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\treturn determineTargetUrl(request, response);\n\t}\n\n\t/**\n\t * Builds the target URL according to the logic defined in the main class Javadoc.\n\t */\n\tprotected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (isAlwaysUseDefaultTargetUrl()) {\n\t\t\treturn this.defaultTargetUrl;\n\t\t}\n\t\tString targetUrlParameterValue = getTargetUrlParameterValue(request);\n\t\tif (StringUtils.hasText(targetUrlParameterValue)) {\n\t\t\ttrace(\"Using url %s from request parameter %s\", targetUrlParameterValue, this.targetUrlParameter);\n\t\t\treturn targetUrlParameterValue;\n\t\t}\n\t\tif (this.useReferer) {\n\t\t\ttrace(\"Using url %s from Referer header\", request.getHeader(\"Referer\"));\n\t\t\treturn request.getHeader(\"Referer\");\n\t\t}\n\t\treturn this.defaultTargetUrl;\n\t}\n\n\tprivate @Nullable String getTargetUrlParameterValue(HttpServletRequest request) {\n\t\tif (this.targetUrlParameter == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString value = request.getParameter(this.targetUrlParameter);\n\t\tif (value == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (StringUtils.hasText(value)) {\n\t\t\treturn value;\n\t\t}\n\t\treturn this.defaultTargetUrl;\n\t}\n\n\tprivate void trace(String msg, @Nullable Object... msgParts) {\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(String.format(msg, msgParts));\n\t\t}\n\t}\n\n\t/**\n\t * Supplies the default target Url that will be used if no saved request is found or\n\t * the {@code alwaysUseDefaultTargetUrl} property is set to true. If not set, defaults\n\t * to {@code /}.\n\t * @return the defaultTargetUrl property\n\t */\n\tprotected final String getDefaultTargetUrl() {\n\t\treturn this.defaultTargetUrl;\n\t}\n\n\t/**\n\t * Supplies the default target Url that will be used if no saved request is found in\n\t * the session, or the {@code alwaysUseDefaultTargetUrl} property is set to true. If\n\t * not set, defaults to {@code /}. It will be treated as relative to the web-app's\n\t * context path, and should include the leading <code>/</code>. Alternatively,\n\t * inclusion of a scheme name (such as \"http://\" or \"https://\") as the prefix will\n\t * denote a fully-qualified URL and this is also supported.\n\t * @param defaultTargetUrl\n\t */\n\tpublic void setDefaultTargetUrl(String defaultTargetUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl),\n\t\t\t\t\"defaultTarget must start with '/' or with 'http(s)'\");\n\t\tthis.defaultTargetUrl = defaultTargetUrl;\n\t}\n\n\t/**\n\t * If <code>true</code>, will always redirect to the value of {@code defaultTargetUrl}\n\t * (defaults to <code>false</code>).\n\t */\n\tpublic void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {\n\t\tthis.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;\n\t}\n\n\tprotected boolean isAlwaysUseDefaultTargetUrl() {\n\t\treturn this.alwaysUseDefaultTargetUrl;\n\t}\n\n\t/**\n\t * If this property is set, the current request will be checked for this a parameter\n\t * with this name and the value used as the target URL if present.\n\t * @param targetUrlParameter the name of the parameter containing the encoded target\n\t * URL. Defaults to null.\n\t */\n\tpublic void setTargetUrlParameter(String targetUrlParameter) {\n\t\tif (targetUrlParameter != null) {\n\t\t\tAssert.hasText(targetUrlParameter, \"targetUrlParameter cannot be empty\");\n\t\t}\n\t\tthis.targetUrlParameter = targetUrlParameter;\n\t}\n\n\tprotected @Nullable String getTargetUrlParameter() {\n\t\treturn this.targetUrlParameter;\n\t}\n\n\t/**\n\t * Allows overriding of the behaviour when redirecting to a target URL.\n\t * @param redirectStrategy {@link RedirectStrategy} to use\n\t */\n\tpublic void setRedirectStrategy(RedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n\tprotected RedirectStrategy getRedirectStrategy() {\n\t\treturn this.redirectStrategy;\n\t}\n\n\t/**\n\t * If set to {@code true} the {@code Referer} header will be used (if available).\n\t * Defaults to {@code false}.\n\t */\n\tpublic void setUseReferer(boolean useReferer) {\n\t\tthis.useReferer = useReferer;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.util.function.SingletonSupplier;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Detects if there is no {@code Authentication} object in the\n * {@code SecurityContextHolder}, and populates it with one if needed.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @author Evgeniy Cheban\n */\npublic class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate String key;\n\n\tprivate Object principal;\n\n\tprivate List<GrantedAuthority> authorities;\n\n\t/**\n\t * Creates a filter with a principal named \"anonymousUser\" and the single authority\n\t * \"ROLE_ANONYMOUS\".\n\t * @param key the key to identify tokens created by this filter\n\t */\n\tpublic AnonymousAuthenticationFilter(String key) {\n\t\tthis(key, \"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t}\n\n\t/**\n\t * @param key key the key to identify tokens created by this filter\n\t * @param principal the principal which will be used to represent anonymous users\n\t * @param authorities the authority list for anonymous users\n\t */\n\tpublic AnonymousAuthenticationFilter(String key, Object principal, List<GrantedAuthority> authorities) {\n\t\tAssert.hasLength(key, \"key cannot be null or empty\");\n\t\tAssert.notNull(principal, \"Anonymous authentication principal must be set\");\n\t\tAssert.notNull(authorities, \"Anonymous authorities must be set\");\n\t\tthis.key = key;\n\t\tthis.principal = principal;\n\t\tthis.authorities = authorities;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasLength(this.key, \"key must have length\");\n\t\tAssert.notNull(this.principal, \"Anonymous authentication principal must be set\");\n\t\tAssert.notNull(this.authorities, \"Anonymous authorities must be set\");\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tSupplier<SecurityContext> deferredContext = this.securityContextHolderStrategy.getDeferredContext();\n\t\tthis.securityContextHolderStrategy\n\t\t\t.setDeferredContext(defaultWithAnonymous((HttpServletRequest) req, deferredContext));\n\t\tchain.doFilter(req, res);\n\t}\n\n\tprivate Supplier<SecurityContext> defaultWithAnonymous(HttpServletRequest request,\n\t\t\tSupplier<SecurityContext> currentDeferredContext) {\n\t\treturn SingletonSupplier.of(() -> {\n\t\t\tSecurityContext currentContext = currentDeferredContext.get();\n\t\t\treturn defaultWithAnonymous(request, currentContext);\n\t\t});\n\t}\n\n\tprivate SecurityContext defaultWithAnonymous(HttpServletRequest request, SecurityContext currentContext) {\n\t\tAuthentication currentAuthentication = currentContext.getAuthentication();\n\t\tif (currentAuthentication == null) {\n\t\t\tAuthentication anonymous = createAuthentication(request);\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.of(() -> \"Set SecurityContextHolder to \" + anonymous));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.logger.debug(\"Set SecurityContextHolder to anonymous SecurityContext\");\n\t\t\t}\n\t\t\tSecurityContext anonymousContext = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\tanonymousContext.setAuthentication(anonymous);\n\t\t\treturn anonymousContext;\n\t\t}\n\t\telse {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.of(() -> \"Did not set SecurityContextHolder since already authenticated \"\n\t\t\t\t\t\t+ currentAuthentication));\n\t\t\t}\n\t\t}\n\t\treturn currentContext;\n\t}\n\n\tprotected Authentication createAuthentication(HttpServletRequest request) {\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(this.key, this.principal,\n\t\t\t\tthis.authorities);\n\t\ttoken.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\treturn token;\n\t}\n\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"AuthenticationDetailsSource required\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\tpublic List<GrantedAuthority> getAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/AuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * A strategy used for converting from a {@link HttpServletRequest} to an\n * {@link Authentication} of particular type. Used to authenticate with appropriate\n * {@link AuthenticationManager}. If the result is null, then it signals that no\n * authentication attempt should be made. It is also possible to throw\n * {@link AuthenticationException} within the {@link #convert(HttpServletRequest)} if\n * there was invalid Authentication scheme value.\n *\n * @author Sergey Bespalov\n * @since 5.2.0\n */\npublic interface AuthenticationConverter {\n\n\t@Nullable Authentication convert(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/AuthenticationEntryPointFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.util.Assert;\n\n/**\n * Adapts a {@link AuthenticationEntryPoint} into a {@link AuthenticationFailureHandler}\n *\n * @author Sergey Bespalov\n * @since 5.2.0\n */\npublic class AuthenticationEntryPointFailureHandler implements AuthenticationFailureHandler {\n\n\tprivate boolean rethrowAuthenticationServiceException = true;\n\n\tprivate final AuthenticationEntryPoint authenticationEntryPoint;\n\n\tpublic AuthenticationEntryPointFailureHandler(AuthenticationEntryPoint authenticationEntryPoint) {\n\t\tAssert.notNull(authenticationEntryPoint, \"authenticationEntryPoint cannot be null\");\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t}\n\n\t@Override\n\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException, ServletException {\n\t\tif (!this.rethrowAuthenticationServiceException) {\n\t\t\tthis.authenticationEntryPoint.commence(request, response, exception);\n\t\t\treturn;\n\t\t}\n\t\tif (!AuthenticationServiceException.class.isAssignableFrom(exception.getClass())) {\n\t\t\tthis.authenticationEntryPoint.commence(request, response, exception);\n\t\t\treturn;\n\t\t}\n\t\tthrow exception;\n\t}\n\n\t/**\n\t * Set whether to rethrow {@link AuthenticationServiceException}s (defaults to true)\n\t * @param rethrowAuthenticationServiceException whether to rethrow\n\t * {@link AuthenticationServiceException}s\n\t * @since 5.8\n\t */\n\tpublic void setRethrowAuthenticationServiceException(boolean rethrowAuthenticationServiceException) {\n\t\tthis.rethrowAuthenticationServiceException = rethrowAuthenticationServiceException;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/AuthenticationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Strategy used to handle a failed authentication attempt.\n * <p>\n * Typical behaviour might be to redirect the user to the authentication page (in the case\n * of a form login) to allow them to try again. More sophisticated logic might be\n * implemented depending on the type of the exception. For example, a\n * {@link CredentialsExpiredException} might cause a redirect to a web controller which\n * allowed the user to change their password.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface AuthenticationFailureHandler {\n\n\t/**\n\t * Called when an authentication attempt fails.\n\t * @param request the request during which the authentication attempt occurred.\n\t * @param response the response.\n\t * @param exception the exception which was thrown to reject the authentication\n\t * request.\n\t */\n\tvoid onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.lang.Contract;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@link Filter} that performs authentication of a particular request. An outline of\n * the logic:\n *\n * <ul>\n * <li>A request comes in and if it does not match\n * {@link #setRequestMatcher(RequestMatcher)}, then this filter does nothing and the\n * {@link FilterChain} is continued. If it does match then...</li>\n * <li>An attempt to convert the {@link HttpServletRequest} into an {@link Authentication}\n * is made. If the result is empty, then the filter does nothing more and the\n * {@link FilterChain} is continued. If it does create an {@link Authentication}...</li>\n * <li>The {@link AuthenticationManager} specified in\n * {@link #AuthenticationFilter(AuthenticationManager, AuthenticationConverter)} is used\n * to perform authentication.</li>\n * <li>The {@link AuthenticationManagerResolver} specified in\n * {@link #AuthenticationFilter(AuthenticationManagerResolver, AuthenticationConverter)}\n * is used to resolve the appropriate authentication manager from context to perform\n * authentication.</li>\n * <li>If authentication is successful, {@link AuthenticationSuccessHandler} is invoked\n * and the authentication is set on {@link SecurityContextHolder}, else\n * {@link AuthenticationFailureHandler} is invoked</li>\n * </ul>\n *\n * @author Sergey Bespalov\n * @author Andrey Litvitski\n * @since 5.2.0\n */\npublic class AuthenticationFilter extends OncePerRequestFilter {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;\n\n\tprivate AuthenticationConverter authenticationConverter;\n\n\tprivate AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();\n\n\tprivate AuthenticationFailureHandler failureHandler = new AuthenticationEntryPointFailureHandler(\n\t\t\tnew HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));\n\n\tprivate SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();\n\n\tprivate AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;\n\n\tprivate boolean mfaEnabled;\n\n\tpublic AuthenticationFilter(AuthenticationManager authenticationManager,\n\t\t\tAuthenticationConverter authenticationConverter) {\n\t\tthis((AuthenticationManagerResolver<HttpServletRequest>) (r) -> authenticationManager, authenticationConverter);\n\t}\n\n\tpublic AuthenticationFilter(AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver,\n\t\t\tAuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationManagerResolver, \"authenticationManagerResolver cannot be null\");\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationManagerResolver = authenticationManagerResolver;\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\tpublic RequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\tpublic AuthenticationConverter getAuthenticationConverter() {\n\t\treturn this.authenticationConverter;\n\t}\n\n\t/**\n\t * Enables Multi-Factor Authentication (MFA) support.\n\t * @param mfaEnabled true to enable MFA support, false to disable it. Default is\n\t * false.\n\t */\n\tpublic void setMfaEnabled(boolean mfaEnabled) {\n\t\tthis.mfaEnabled = mfaEnabled;\n\t}\n\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\tpublic AuthenticationSuccessHandler getSuccessHandler() {\n\t\treturn this.successHandler;\n\t}\n\n\tpublic void setSuccessHandler(AuthenticationSuccessHandler successHandler) {\n\t\tAssert.notNull(successHandler, \"successHandler cannot be null\");\n\t\tthis.successHandler = successHandler;\n\t}\n\n\tpublic AuthenticationFailureHandler getFailureHandler() {\n\t\treturn this.failureHandler;\n\t}\n\n\tpublic void setFailureHandler(AuthenticationFailureHandler failureHandler) {\n\t\tAssert.notNull(failureHandler, \"failureHandler cannot be null\");\n\t\tthis.failureHandler = failureHandler;\n\t}\n\n\tpublic AuthenticationManagerResolver<HttpServletRequest> getAuthenticationManagerResolver() {\n\t\treturn this.authenticationManagerResolver;\n\t}\n\n\tpublic void setAuthenticationManagerResolver(\n\t\t\tAuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {\n\t\tAssert.notNull(authenticationManagerResolver, \"authenticationManagerResolver cannot be null\");\n\t\tthis.authenticationManagerResolver = authenticationManagerResolver;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * authentication success. The default action is not to save the\n\t * {@link SecurityContext}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(\"Did not match request to \" + this.requestMatcher);\n\t\t\t}\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tAuthentication authenticationResult = attemptAuthentication(request, response);\n\t\t\tif (authenticationResult == null) {\n\t\t\t\tfilterChain.doFilter(request, response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tAuthentication current = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (shouldPerformMfa(current, authenticationResult)) {\n\t\t\t\tauthenticationResult = authenticationResult.toBuilder()\n\t\t\t\t// @formatter:off\n\t\t\t\t\t.authorities((a) -> {\n\t\t\t\t\t\tSet<String> newAuthorities = a.stream()\n\t\t\t\t\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t\t\t\t\t.collect(Collectors.toUnmodifiableSet());\n\t\t\t\t\t\tfor (GrantedAuthority currentAuthority : current.getAuthorities()) {\n\t\t\t\t\t\t\tif (!newAuthorities.contains(currentAuthority.getAuthority())) {\n\t\t\t\t\t\t\t\ta.add(currentAuthority);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.build();\n\t\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t\tHttpSession session = request.getSession(false);\n\t\t\tif (session != null) {\n\t\t\t\trequest.changeSessionId();\n\t\t\t}\n\t\t\tsuccessfulAuthentication(request, response, filterChain, authenticationResult);\n\t\t}\n\t\tcatch (AuthenticationException ex) {\n\t\t\tunsuccessfulAuthentication(request, response, ex);\n\t\t}\n\t}\n\n\t@Contract(\"null, _ -> false\")\n\tprivate boolean shouldPerformMfa(@Nullable Authentication current, Authentication authenticationResult) {\n\t\tif (!this.mfaEnabled) {\n\t\t\treturn false;\n\t\t}\n\t\tif (current == null || !current.isAuthenticated()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!declaresToBuilder(authenticationResult)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn current.getName().equals(authenticationResult.getName());\n\t}\n\n\tprivate static boolean declaresToBuilder(Authentication authentication) {\n\t\tfor (Method method : authentication.getClass().getDeclaredMethods()) {\n\t\t\tif (method.getName().equals(\"toBuilder\") && method.getParameterTypes().length == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected String getAlreadyFilteredAttributeName() {\n\t\tString name = getFilterName();\n\t\tif (name == null) {\n\t\t\tname = getClass().getName().concat(\"-\" + System.identityHashCode(this));\n\t\t}\n\t\treturn name + ALREADY_FILTERED_SUFFIX;\n\t}\n\n\tprivate void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException failed) throws IOException, ServletException {\n\t\tthis.securityContextHolderStrategy.clearContext();\n\t\tthis.failureHandler.onAuthenticationFailure(request, response, failed);\n\t}\n\n\tprivate void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,\n\t\t\tAuthentication authentication) throws IOException, ServletException {\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\tthis.successHandler.onAuthenticationSuccess(request, response, chain, authentication);\n\t}\n\n\tprivate @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows AuthenticationException, ServletException {\n\t\tAuthentication authentication = this.authenticationConverter.convert(request);\n\t\tif (authentication == null) {\n\t\t\treturn null;\n\t\t}\n\t\tAuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);\n\t\tAuthentication authenticationResult = authenticationManager.authenticate(authentication);\n\t\tif (authenticationResult == null) {\n\t\t\tthrow new ServletException(\"AuthenticationManager should not return null Authentication object.\");\n\t\t}\n\t\treturn authenticationResult;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/AuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Strategy used to handle a successful user authentication.\n * <p>\n * Implementations can do whatever they want but typical behaviour would be to control the\n * navigation to the subsequent destination (using a redirect or a forward). For example,\n * after a user has logged in by submitting a login form, the application needs to decide\n * where they should be redirected to afterwards (see\n * {@link AbstractAuthenticationProcessingFilter} and subclasses). Other logic may also be\n * included if required.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface AuthenticationSuccessHandler {\n\n\t/**\n\t * Called when a user has been successfully authenticated.\n\t * @param request the request which caused the successful authentication\n\t * @param response the response\n\t * @param chain the {@link FilterChain} which can be used to proceed other filters in\n\t * the chain\n\t * @param authentication the <tt>Authentication</tt> object which was created during\n\t * the authentication process.\n\t * @since 5.2.0\n\t */\n\tdefault void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain,\n\t\t\tAuthentication authentication) throws IOException, ServletException {\n\t\tonAuthenticationSuccess(request, response, authentication);\n\t\tchain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Called when a user has been successfully authenticated.\n\t * @param request the request which caused the successful authentication\n\t * @param response the response\n\t * @param authentication the <tt>Authentication</tt> object which was created during\n\t * the authentication process.\n\t */\n\tvoid onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link AuthenticationConverter}, that iterates over multiple\n * {@link AuthenticationConverter}. The first non-null {@link Authentication} will be used\n * as a result.\n *\n * @author Max Batischev\n * @since 6.3\n */\npublic final class DelegatingAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate final List<AuthenticationConverter> delegates;\n\n\tpublic DelegatingAuthenticationConverter(List<AuthenticationConverter> delegates) {\n\t\tAssert.notEmpty(delegates, \"delegates cannot be null\");\n\t\tthis.delegates = List.copyOf(delegates);\n\t}\n\n\tpublic DelegatingAuthenticationConverter(AuthenticationConverter... delegates) {\n\t\tAssert.notEmpty(delegates, \"delegates cannot be null\");\n\t\tthis.delegates = List.of(delegates);\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication convert(HttpServletRequest request) {\n\t\tfor (AuthenticationConverter delegate : this.delegates) {\n\t\t\tAuthentication authentication = delegate.convert(request);\n\t\t\tif (authentication != null) {\n\t\t\t\treturn authentication;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.util.matcher.ELRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcherEditor;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\nimport org.springframework.util.Assert;\n\n/**\n * An {@code AuthenticationEntryPoint} which selects a concrete\n * {@code AuthenticationEntryPoint} based on a {@link RequestMatcher} evaluation.\n *\n * <p>\n * A configuration might look like this:\n * </p>\n *\n * <pre>\n * &lt;bean id=&quot;daep&quot; class=&quot;org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint&quot;&gt;\n *     &lt;constructor-arg&gt;\n *         &lt;map&gt;\n *             &lt;entry key=&quot;hasIpAddress('192.168.1.0/24') and hasHeader('User-Agent','Mozilla')&quot; value-ref=&quot;firstAEP&quot; /&gt;\n *             &lt;entry key=&quot;hasHeader('User-Agent','MSIE')&quot; value-ref=&quot;secondAEP&quot; /&gt;\n *         &lt;/map&gt;\n *     &lt;/constructor-arg&gt;\n *     &lt;property name=&quot;defaultEntryPoint&quot; ref=&quot;defaultAEP&quot;/&gt;\n * &lt;/bean&gt;\n * </pre>\n *\n * This example uses the {@link RequestMatcherEditor} which creates a\n * {@link ELRequestMatcher} instances for the map keys.\n *\n * @author Mike Wiesner\n * @since 3.0.2\n */\npublic class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {\n\n\tprivate static final Log logger = LogFactory.getLog(DelegatingAuthenticationEntryPoint.class);\n\n\tprivate final List<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate AuthenticationEntryPoint defaultEntryPoint;\n\n\t/**\n\t * Creates a new instance with the provided mappings.\n\t * @param entryPoints the mapping of {@link RequestMatcher} to\n\t * {@link AuthenticationEntryPoint}. Cannot be null or empty.\n\t * @param defaultEntryPoint the default {@link AuthenticationEntryPoint}. Cannot be\n\t * null.\n\t */\n\tpublic DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint defaultEntryPoint,\n\t\t\tRequestMatcherEntry<AuthenticationEntryPoint>... entryPoints) {\n\t\tAssert.notEmpty(entryPoints, \"entryPoints cannot be empty\");\n\t\tAssert.notNull(defaultEntryPoint, \"defaultEntryPoint cannot be null\");\n\t\tthis.entryPoints = Arrays.asList(entryPoints);\n\t\tthis.defaultEntryPoint = defaultEntryPoint;\n\t}\n\n\t/**\n\t * Creates a new instance with the provided mappings.\n\t * @param defaultEntryPoint the default {@link AuthenticationEntryPoint}. Cannot be\n\t * null.\n\t * @param entryPoints the mapping of {@link RequestMatcher} to\n\t * {@link AuthenticationEntryPoint}. Cannot be null or empty.\n\t */\n\tpublic DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint defaultEntryPoint,\n\t\t\tList<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints) {\n\t\tAssert.notEmpty(entryPoints, \"entryPoints cannot be empty\");\n\t\tAssert.notNull(defaultEntryPoint, \"defaultEntryPoint cannot be null\");\n\t\tthis.entryPoints = entryPoints;\n\t\tthis.defaultEntryPoint = defaultEntryPoint;\n\t}\n\n\t/**\n\t * Creates a new instance.\n\t * @param entryPoints\n\t * @deprecated Use\n\t * {@link #DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint, List)}\n\t */\n\t@Deprecated(forRemoval = true)\n\tpublic DelegatingAuthenticationEntryPoint(LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints) {\n\t\tthis.entryPoints = entryPoints.entrySet()\n\t\t\t.stream()\n\t\t\t.map((e) -> new RequestMatcherEntry<>(e.getKey(), e.getValue()))\n\t\t\t.collect(Collectors.toList());\n\t}\n\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authException) throws IOException, ServletException {\n\t\tfor (RequestMatcherEntry<AuthenticationEntryPoint> entry : this.entryPoints) {\n\t\t\tRequestMatcher requestMatcher = entry.getRequestMatcher();\n\t\t\tlogger.debug(LogMessage.format(\"Trying to match using %s\", requestMatcher));\n\t\t\tif (requestMatcher.matches(request)) {\n\t\t\t\tAuthenticationEntryPoint entryPoint = entry.getEntry();\n\t\t\t\tlogger.debug(LogMessage.format(\"Match found! Executing %s\", entryPoint));\n\t\t\t\tentryPoint.commence(request, response, authException);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tlogger.debug(LogMessage.format(\"No match found. Using default entry point %s\", this.defaultEntryPoint));\n\t\t// No EntryPoint matched, use defaultEntryPoint\n\t\tthis.defaultEntryPoint.commence(request, response, authException);\n\t}\n\n\t/**\n\t * EntryPoint which is used when no RequestMatcher returned true\n\t * @deprecated Use\n\t * {@link #DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint, List)}\n\t */\n\t@Deprecated(forRemoval = true)\n\tpublic void setDefaultEntryPoint(AuthenticationEntryPoint defaultEntryPoint) {\n\t\tthis.defaultEntryPoint = defaultEntryPoint;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notEmpty(this.entryPoints, \"entryPoints must be specified\");\n\t\tAssert.notNull(this.defaultEntryPoint, \"defaultEntryPoint must be specified\");\n\t}\n\n\t/**\n\t * Creates a new {@link Builder}\n\t * @return the new {@link Builder}\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Used to build a new instance of {@link DelegatingAuthenticationEntryPoint}.\n\t *\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate @Nullable AuthenticationEntryPoint defaultEntryPoint;\n\n\t\tprivate List<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints = new ArrayList<RequestMatcherEntry<AuthenticationEntryPoint>>();\n\n\t\t/**\n\t\t * Set the default {@link AuthenticationEntryPoint} if none match. The default is\n\t\t * to use the first {@link AuthenticationEntryPoint} added in\n\t\t * {@link #addEntryPointFor(AuthenticationEntryPoint, RequestMatcher)}.\n\t\t * @param defaultEntryPoint the default {@link AuthenticationEntryPoint} to use.\n\t\t * @return the {@link Builder} for further customization.\n\t\t */\n\t\tpublic Builder defaultEntryPoint(@Nullable AuthenticationEntryPoint defaultEntryPoint) {\n\t\t\tthis.defaultEntryPoint = defaultEntryPoint;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds an {@link AuthenticationEntryPoint} for the provided\n\t\t * {@link RequestMatcher}.\n\t\t * @param entryPoint the {@link AuthenticationEntryPoint} to use. Cannot be null.\n\t\t * @param requestMatcher the {@link RequestMatcher} to use. Cannot be null.\n\t\t * @return the {@link Builder} for further customization.\n\t\t */\n\t\tpublic Builder addEntryPointFor(AuthenticationEntryPoint entryPoint, RequestMatcher requestMatcher) {\n\t\t\tAssert.notNull(entryPoint, \"entryPoint cannot be null\");\n\t\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\t\tthis.entryPoints.add(new RequestMatcherEntry<>(requestMatcher, entryPoint));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link AuthenticationEntryPoint}. If the\n\t\t * {@link #defaultEntryPoint(AuthenticationEntryPoint)} is not set, then the first\n\t\t * {@link #addEntryPointFor(AuthenticationEntryPoint, RequestMatcher)} is used as\n\t\t * the default. If the {@link #defaultEntryPoint(AuthenticationEntryPoint)} is not\n\t\t * set and there is only a single\n\t\t * {@link #addEntryPointFor(AuthenticationEntryPoint, RequestMatcher)}, then the\n\t\t * {@link AuthenticationEntryPoint} is returned rather than wrapping it in\n\t\t * {@link DelegatingAuthenticationEntryPoint}.\n\t\t * @return the {@link AuthenticationEntryPoint} to use.\n\t\t */\n\t\tpublic AuthenticationEntryPoint build() {\n\t\t\tAuthenticationEntryPoint defaultEntryPoint = this.defaultEntryPoint;\n\t\t\tif (defaultEntryPoint == null) {\n\t\t\t\tAssert.state(!this.entryPoints.isEmpty(), \"entryPoints cannot be empty if defaultEntryPoint is null\");\n\t\t\t\tAuthenticationEntryPoint firstAuthenticationEntryPoint = this.entryPoints.get(0).getEntry();\n\t\t\t\tif (this.entryPoints.size() == 1) {\n\t\t\t\t\treturn firstAuthenticationEntryPoint;\n\t\t\t\t}\n\t\t\t\tdefaultEntryPoint = firstAuthenticationEntryPoint;\n\t\t\t}\n\t\t\telse if (this.entryPoints.isEmpty()) {\n\t\t\t\treturn defaultEntryPoint;\n\t\t\t}\n\t\t\treturn new DelegatingAuthenticationEntryPoint(defaultEntryPoint, this.entryPoints);\n\t\t}\n\n\t\tprivate Builder() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationFailureHandler} that delegates to other\n * {@link AuthenticationFailureHandler} instances based upon the type of\n * {@link AuthenticationException} passed into\n * {@link #onAuthenticationFailure(HttpServletRequest, HttpServletResponse, AuthenticationException)}\n * .\n *\n * @author Kazuki Shimizu\n * @since 4.0\n */\npublic class DelegatingAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n\tprivate final LinkedHashMap<Class<? extends AuthenticationException>, AuthenticationFailureHandler> handlers;\n\n\tprivate final AuthenticationFailureHandler defaultHandler;\n\n\t/**\n\t * Creates a new instance\n\t * @param handlers a map of the {@link AuthenticationException} class to the\n\t * {@link AuthenticationFailureHandler} that should be used. Each is considered in the\n\t * order they are specified and only the first {@link AuthenticationFailureHandler} is\n\t * ued. This parameter cannot specify null or empty.\n\t * @param defaultHandler the default {@link AuthenticationFailureHandler} that should\n\t * be used if none of the handlers matches. This parameter cannot specify null.\n\t * @throws IllegalArgumentException if invalid argument is specified\n\t */\n\tpublic DelegatingAuthenticationFailureHandler(\n\t\t\tLinkedHashMap<Class<? extends AuthenticationException>, AuthenticationFailureHandler> handlers,\n\t\t\tAuthenticationFailureHandler defaultHandler) {\n\t\tAssert.notEmpty(handlers, \"handlers cannot be null or empty\");\n\t\tAssert.notNull(defaultHandler, \"defaultHandler cannot be null\");\n\t\tthis.handlers = handlers;\n\t\tthis.defaultHandler = defaultHandler;\n\t}\n\n\t@Override\n\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException, ServletException {\n\t\tfor (Map.Entry<Class<? extends AuthenticationException>, AuthenticationFailureHandler> entry : this.handlers\n\t\t\t.entrySet()) {\n\t\t\tClass<? extends AuthenticationException> handlerMappedExceptionClass = entry.getKey();\n\t\t\tif (handlerMappedExceptionClass.isAssignableFrom(exception.getClass())) {\n\t\t\t\tAuthenticationFailureHandler handler = entry.getValue();\n\t\t\t\thandler.onAuthenticationFailure(request, response, exception);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis.defaultHandler.onAuthenticationFailure(request, response, exception);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ExceptionMappingAuthenticationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * Uses the internal map of exceptions types to URLs to determine the destination on\n * authentication failure. The keys are the full exception class names.\n * <p>\n * If a match isn't found, falls back to the behaviour of the parent class,\n * {@link SimpleUrlAuthenticationFailureHandler}.\n * <p>\n * The map of exception names to URLs should be injected by setting the\n * <tt>exceptionMappings</tt> property.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class ExceptionMappingAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {\n\n\tprivate final Map<String, String> failureUrlMap = new HashMap<>();\n\n\t@Override\n\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException, ServletException {\n\t\tString url = this.failureUrlMap.get(exception.getClass().getName());\n\t\tif (url != null) {\n\t\t\tgetRedirectStrategy().sendRedirect(request, response, url);\n\t\t}\n\t\telse {\n\t\t\tsuper.onAuthenticationFailure(request, response, exception);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the map of exception types (by name) to URLs.\n\t * @param failureUrlMap the map keyed by the fully-qualified name of the exception\n\t * class, with the corresponding failure URL as the value.\n\t * @throws IllegalArgumentException if the entries are not Strings or the URL is not\n\t * valid.\n\t */\n\tpublic void setExceptionMappings(Map<?, ?> failureUrlMap) {\n\t\tthis.failureUrlMap.clear();\n\t\tfor (Map.Entry<?, ?> entry : failureUrlMap.entrySet()) {\n\t\t\tObject exception = entry.getKey();\n\t\t\tObject url = entry.getValue();\n\t\t\tAssert.isInstanceOf(String.class, exception, \"Exception key must be a String (the exception classname).\");\n\t\t\tAssert.isInstanceOf(String.class, url, \"URL must be a String\");\n\t\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl((String) url), () -> \"Not a valid redirect URL: \" + url);\n\t\t\tthis.failureUrlMap.put((String) exception, (String) url);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ForwardAuthenticationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Forward Authentication Failure Handler\n * </p>\n *\n * @author Shazin Sadakath\n * @since 4.1\n */\npublic class ForwardAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n\tprivate final String forwardUrl;\n\n\t/**\n\t * @param forwardUrl\n\t */\n\tpublic ForwardAuthenticationFailureHandler(String forwardUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> \"'\" + forwardUrl + \"' is not a valid forward URL\");\n\t\tthis.forwardUrl = forwardUrl;\n\t}\n\n\t@Override\n\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException, ServletException {\n\t\trequest.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);\n\t\trequest.getRequestDispatcher(this.forwardUrl).forward(request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ForwardAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Forward Authentication Success Handler\n * </p>\n *\n * @author Shazin Sadakath\n * @since 4.1\n *\n */\npublic class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler {\n\n\tprivate final String forwardUrl;\n\n\t/**\n\t * @param forwardUrl\n\t */\n\tpublic ForwardAuthenticationSuccessHandler(String forwardUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> \"'\" + forwardUrl + \"' is not a valid forward URL\");\n\t\tthis.forwardUrl = forwardUrl;\n\t}\n\n\t@Override\n\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException, ServletException {\n\t\trequest.getRequestDispatcher(this.forwardUrl).forward(request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/GenericHttpMessageConverterAdapter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\n\n/**\n * {@link GenericHttpMessageConverter} implementation that delegates to a\n * {@link SmartHttpMessageConverter}.\n *\n * @param <T> the converted object type\n * @author Sebastien Deleuze\n * @since 7.0\n */\nfinal class GenericHttpMessageConverterAdapter<T> implements GenericHttpMessageConverter<T> {\n\n\tprivate final SmartHttpMessageConverter<T> smartConverter;\n\n\tGenericHttpMessageConverterAdapter(SmartHttpMessageConverter<T> smartConverter) {\n\t\tthis.smartConverter = smartConverter;\n\t}\n\n\t@Override\n\tpublic boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forType(type), mediaType);\n\t}\n\n\t@Override\n\tpublic T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(ResolvableType.forType(type), inputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(ResolvableType.forType(type), clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, ResolvableType.forType(type), contentType, outputMessage, null);\n\t}\n\n\t@Override\n\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canRead(ResolvableType.forClass(clazz), mediaType);\n\t}\n\n\t@Override\n\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\treturn this.smartConverter.canWrite(clazz, mediaType);\n\t}\n\n\t@Override\n\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\treturn this.smartConverter.getSupportedMediaTypes();\n\t}\n\n\t@Override\n\tpublic T read(Class<? extends T> clazz, HttpInputMessage inputMessage)\n\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\treturn this.smartConverter.read(clazz, inputMessage);\n\t}\n\n\t@Override\n\tpublic void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)\n\t\t\tthrows IOException, HttpMessageNotWritableException {\n\t\tthis.smartConverter.write(t, contentType, outputMessage);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/Http403ForbiddenEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\n\n/**\n * <p>\n * In the pre-authenticated authentication case (unlike CAS, for example) the user will\n * already have been identified through some external mechanism and a secure context\n * established by the time the security-enforcement filter is invoked.\n * <p>\n * Therefore this class isn't actually responsible for the commencement of authentication,\n * as it is in the case of other providers. It will be called if the user is rejected by\n * the AbstractPreAuthenticatedProcessingFilter, resulting in a null authentication.\n * <p>\n * The <code>commence</code> method will always return an\n * <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).\n *\n * @author Luke Taylor\n * @author Ruud Senden\n * @since 2.0\n * @see org.springframework.security.web.access.ExceptionTranslationFilter\n */\npublic class Http403ForbiddenEntryPoint implements AuthenticationEntryPoint {\n\n\tprivate static final Log logger = LogFactory.getLog(Http403ForbiddenEntryPoint.class);\n\n\t/**\n\t * Always returns a 403 error code to the client.\n\t */\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2)\n\t\t\tthrows IOException {\n\t\tlogger.debug(\"Pre-authenticated entry point called. Rejecting access\");\n\t\tresponse.sendError(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/HttpMessageConverterAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationSuccessHandler} that writes a JSON response with the redirect\n * URL and an authenticated status similar to:\n *\n * <code>\n *     {\n *         \"redirectUrl\": \"/user/profile\",\n *         \"authenticated\": true\n *     }\n * </code>\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class HttpMessageConverterAuthenticationSuccessHandler implements AuthenticationSuccessHandler {\n\n\tprivate HttpMessageConverter<Object> converter = HttpMessageConverters.getJsonMessageConverter();\n\n\tprivate RequestCache requestCache = new HttpSessionRequestCache();\n\n\t/**\n\t * Sets the {@link GenericHttpMessageConverter} to write to the response. The default\n\t * is {@link MappingJackson2HttpMessageConverter}.\n\t * @param converter the {@link GenericHttpMessageConverter} to use. Cannot be null.\n\t */\n\tpublic void setConverter(HttpMessageConverter<Object> converter) {\n\t\tAssert.notNull(converter, \"converter cannot be null\");\n\t\tthis.converter = converter;\n\t}\n\n\t/**\n\t * Sets the {@link RequestCache} to use. The default is\n\t * {@link HttpSessionRequestCache}.\n\t * @param requestCache the {@link RequestCache} to use. Cannot be null\n\t */\n\tpublic void setRequestCache(RequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t@Override\n\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException, ServletException {\n\t\tfinal SavedRequest savedRequest = this.requestCache.getRequest(request, response);\n\t\tfinal String redirectUrl = (savedRequest != null) ? savedRequest.getRedirectUrl()\n\t\t\t\t: request.getContextPath() + \"/\";\n\t\tthis.requestCache.removeRequest(request, response);\n\t\tthis.converter.write(new AuthenticationSuccess(redirectUrl), MediaType.APPLICATION_JSON,\n\t\t\t\tnew ServletServerHttpResponse(response));\n\t}\n\n\t/**\n\t * A response object used to write the JSON response for successful authentication.\n\t *\n\t * NOTE: We should be careful about writing {@link Authentication} or\n\t * {@link Authentication#getPrincipal()} to the response since it contains\n\t * credentials.\n\t */\n\tpublic static final class AuthenticationSuccess {\n\n\t\tprivate final String redirectUrl;\n\n\t\tprivate AuthenticationSuccess(String redirectUrl) {\n\t\t\tthis.redirectUrl = redirectUrl;\n\t\t}\n\n\t\tpublic String getRedirectUrl() {\n\t\t\treturn this.redirectUrl;\n\t\t}\n\n\t\tpublic boolean isAuthenticated() {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/HttpMessageConverters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.GsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.converter.json.JsonbHttpMessageConverter;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.util.ClassUtils;\n\n/**\n * Utility methods for {@link HttpMessageConverter}'s.\n *\n * @author Rob Winch\n * @since 7.0.4\n */\nfinal class HttpMessageConverters {\n\n\tprivate static final boolean jacksonPresent;\n\n\tprivate static final boolean jackson2Present;\n\n\tprivate static final boolean gsonPresent;\n\n\tprivate static final boolean jsonbPresent;\n\n\tprivate static final String JSON_MAPPER = \"tools.jackson.databind.json.JsonMapper\";\n\n\tprivate static final String OBJECT_MAPPER = \"com.fasterxml.jackson.databind.ObjectMapper\";\n\n\tprivate static final String JSON_GENERATOR = \"com.fasterxml.jackson.core.JsonGenerator\";\n\n\tprivate static final String GSON = \"com.google.gson.Gson\";\n\n\tprivate static final String JSONB = \"jakarta.json.bind.Jsonb\";\n\n\tstatic {\n\t\tClassLoader classLoader = HttpMessageConverters.class.getClassLoader();\n\t\tjacksonPresent = ClassUtils.isPresent(JSON_MAPPER, classLoader);\n\t\tjackson2Present = ClassUtils.isPresent(OBJECT_MAPPER, classLoader)\n\t\t\t\t&& ClassUtils.isPresent(JSON_GENERATOR, classLoader);\n\t\tgsonPresent = ClassUtils.isPresent(GSON, classLoader);\n\t\tjsonbPresent = ClassUtils.isPresent(JSONB, classLoader);\n\t}\n\n\tprivate HttpMessageConverters() {\n\t}\n\n\t/**\n\t * Gets the {@link GenericHttpMessageConverterAdapter} to use for JSON.\n\t * @return the {@link GenericHttpMessageConverterAdapter} to use.\n\t */\n\t@SuppressWarnings(\"removal\")\n\tstatic GenericHttpMessageConverter<Object> getJsonMessageConverter() {\n\t\tif (jacksonPresent) {\n\t\t\treturn new GenericHttpMessageConverterAdapter<>(new JacksonJsonHttpMessageConverter());\n\t\t}\n\t\tif (jackson2Present) {\n\t\t\treturn new MappingJackson2HttpMessageConverter();\n\t\t}\n\t\tif (gsonPresent) {\n\t\t\treturn new GsonHttpMessageConverter();\n\t\t}\n\t\tif (jsonbPresent) {\n\t\t\treturn new JsonbHttpMessageConverter();\n\t\t}\n\t\tthrow new IllegalStateException(\n\t\t\t\t\"Cannot find JSON Converter on the classpath. Add one following classes to the classpath \"\n\t\t\t\t\t\t+ String.join(\", \", JSON_MAPPER, OBJECT_MAPPER, JSON_MAPPER, GSON, JSONB));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/HttpStatusEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationEntryPoint} that sends a generic {@link HttpStatus} as a\n * response. Useful for JavaScript clients which cannot use Basic authentication since the\n * browser intercepts the response.\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic final class HttpStatusEntryPoint implements AuthenticationEntryPoint {\n\n\tprivate final HttpStatus httpStatus;\n\n\t/**\n\t * Creates a new instance.\n\t * @param httpStatus the HttpStatus to set\n\t */\n\tpublic HttpStatusEntryPoint(HttpStatus httpStatus) {\n\t\tAssert.notNull(httpStatus, \"httpStatus cannot be null\");\n\t\tthis.httpStatus = httpStatus;\n\t}\n\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authException) {\n\t\tresponse.setStatus(this.httpStatus.value());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Locale;\n\nimport jakarta.servlet.RequestDispatcher;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authorization.RequiredFactorError;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.util.RedirectUrlBuilder;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Used by the {@link ExceptionTranslationFilter} to commence a form login authentication\n * via the {@link UsernamePasswordAuthenticationFilter}.\n * <p>\n * Holds the location of the login form in the {@code loginFormUrl} property, and uses\n * that to build a redirect URL to the login page. Alternatively, an absolute URL can be\n * set in this property and that will be used exclusively.\n * <p>\n * When using a relative URL, you can set the {@code forceHttps} property to true, to\n * force the protocol used for the login form to be {@code HTTPS}, even if the original\n * intercepted request for a resource used the {@code HTTP} protocol. When this happens,\n * after a successful login (via HTTPS), the original resource will still be accessed as\n * HTTP, via the original request URL. For the forced HTTPS feature to work, the\n * {@link PortMapper} is consulted to determine the HTTP:HTTPS pairs. The value of\n * {@code forceHttps} will have no effect if an absolute URL is used.\n *\n * @author Ben Alex\n * @author colin sampaleanu\n * @author Omri Spector\n * @author Luke Taylor\n * @author Michal Okosy\n * @since 3.0\n */\npublic class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {\n\n\tprivate static final Log logger = LogFactory.getLog(LoginUrlAuthenticationEntryPoint.class);\n\n\tprivate static final String FACTOR_PREFIX = \"FACTOR_\";\n\n\tprivate PortMapper portMapper = new PortMapperImpl();\n\n\tprivate String loginFormUrl;\n\n\tprivate boolean forceHttps = false;\n\n\tprivate boolean useForward = false;\n\n\tprivate boolean favorRelativeUris = true;\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\t/**\n\t * @param loginFormUrl URL where the login page can be found. Should either be\n\t * relative to the web-app context path (include a leading {@code /}) or an absolute\n\t * URL.\n\t */\n\tpublic LoginUrlAuthenticationEntryPoint(String loginFormUrl) {\n\t\tAssert.notNull(loginFormUrl, \"loginFormUrl cannot be null\");\n\t\tthis.loginFormUrl = loginFormUrl;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.isTrue(StringUtils.hasText(this.loginFormUrl) && UrlUtils.isValidRedirectUrl(this.loginFormUrl),\n\t\t\t\t\"loginFormUrl must be specified and must be a valid redirect URL\");\n\t\tAssert.isTrue(!this.useForward || !UrlUtils.isAbsoluteUrl(this.loginFormUrl),\n\t\t\t\t\"useForward must be false if using an absolute loginFormURL\");\n\t\tAssert.notNull(this.portMapper, \"portMapper must be specified\");\n\t}\n\n\t/**\n\t * Allows subclasses to modify the login form URL that should be applicable for a\n\t * given request.\n\t * @param request the request\n\t * @param response the response\n\t * @param exception the exception\n\t * @return the URL (cannot be null or empty; defaults to {@link #getLoginFormUrl()})\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprotected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) {\n\t\tCollection<RequiredFactorError> factorErrors = getAttribute(request, WebAttributes.REQUIRED_FACTOR_ERRORS,\n\t\t\t\tCollection.class);\n\t\tif (CollectionUtils.isEmpty(factorErrors)) {\n\t\t\treturn getLoginFormUrl();\n\t\t}\n\t\tList<String> factorTypes = factorErrors.stream()\n\t\t\t.map((factorError) -> factorError.getRequiredFactor().getAuthority())\n\t\t\t.map((a) -> a.substring(FACTOR_PREFIX.length()).toLowerCase(Locale.ROOT))\n\t\t\t.toList();\n\t\tList<String> factorReasons = factorErrors.stream()\n\t\t\t.map((factorError) -> factorError.isExpired() ? \"expired\" : \"missing\")\n\t\t\t.toList();\n\t\treturn UriComponentsBuilder.fromUriString(getLoginFormUrl())\n\t\t\t.queryParam(\"factor.type\", factorTypes)\n\t\t\t.queryParam(\"factor.reason\", factorReasons)\n\t\t\t.toUriString();\n\t}\n\n\tprivate static <T> @Nullable T getAttribute(HttpServletRequest request, String name, Class<T> clazz) {\n\t\tObject value = request.getAttribute(name);\n\t\tif (value == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString message = String.format(\"Found %s in %s, but expecting a %s\", value.getClass(), name, clazz);\n\t\tAssert.isInstanceOf(clazz, value, message);\n\t\treturn (T) value;\n\t}\n\n\t/**\n\t * Performs the redirect (or forward) to the login form URL.\n\t */\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authException) throws IOException, ServletException {\n\t\tif (!this.useForward) {\n\t\t\t// redirect to login page. Use https if forceHttps true\n\t\t\tString redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);\n\t\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUrl);\n\t\t\treturn;\n\t\t}\n\t\tString redirectUrl = null;\n\t\tif (requiresRewrite(request)) {\n\t\t\t// First redirect the current request to HTTPS. When that request is received,\n\t\t\t// the forward to the login page will be used.\n\t\t\tredirectUrl = buildHttpsRedirectUrlForRequest(request);\n\t\t}\n\t\tif (redirectUrl != null) {\n\t\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUrl);\n\t\t\treturn;\n\t\t}\n\t\tString loginForm = determineUrlToUseForThisRequest(request, response, authException);\n\t\tlogger.debug(LogMessage.format(\"Server side forward to: %s\", loginForm));\n\t\tRequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);\n\t\tdispatcher.forward(request, response);\n\t}\n\n\tprotected String buildRedirectUrlToLoginPage(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authException) {\n\t\tString loginForm = determineUrlToUseForThisRequest(request, response, authException);\n\t\tif (UrlUtils.isAbsoluteUrl(loginForm)) {\n\t\t\treturn loginForm;\n\t\t}\n\t\tif (requiresRewrite(request)) {\n\t\t\treturn httpsUri(request, loginForm);\n\t\t}\n\t\treturn this.favorRelativeUris ? loginForm : absoluteUri(request, loginForm).getUrl();\n\t}\n\n\tprivate boolean requiresRewrite(HttpServletRequest request) {\n\t\treturn this.forceHttps && \"http\".equals(request.getScheme());\n\t}\n\n\tprivate String httpsUri(HttpServletRequest request, String path) {\n\t\tint serverPort = getServerPort(request);\n\t\tInteger httpsPort = this.portMapper.lookupHttpsPort(serverPort);\n\t\tif (httpsPort == null) {\n\t\t\tlogger.warn(LogMessage.format(\"Unable to redirect to HTTPS as no port mapping found for HTTP port %s\",\n\t\t\t\t\tserverPort));\n\t\t\treturn this.favorRelativeUris ? path : absoluteUri(request, path).getUrl();\n\t\t}\n\t\tRedirectUrlBuilder builder = absoluteUri(request, path);\n\t\tbuilder.setScheme(\"https\");\n\t\tbuilder.setPort(httpsPort);\n\t\treturn builder.getUrl();\n\t}\n\n\tprivate RedirectUrlBuilder absoluteUri(HttpServletRequest request, String path) {\n\t\tRedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();\n\t\turlBuilder.setScheme(request.getScheme());\n\t\turlBuilder.setServerName(request.getServerName());\n\t\turlBuilder.setPort(getServerPort(request));\n\t\turlBuilder.setContextPath(request.getContextPath());\n\t\turlBuilder.setPathInfo(path);\n\t\treturn urlBuilder;\n\t}\n\n\t/**\n\t * Builds a URL to redirect the supplied request to HTTPS. Used to redirect the\n\t * current request to HTTPS, before doing a forward to the login page.\n\t */\n\tprotected @Nullable String buildHttpsRedirectUrlForRequest(HttpServletRequest request)\n\t\t\tthrows IOException, ServletException {\n\t\tint serverPort = getServerPort(request);\n\t\tInteger httpsPort = this.portMapper.lookupHttpsPort(serverPort);\n\t\tif (httpsPort != null) {\n\t\t\tRedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();\n\t\t\turlBuilder.setScheme(\"https\");\n\t\t\turlBuilder.setServerName(request.getServerName());\n\t\t\turlBuilder.setPort(httpsPort);\n\t\t\turlBuilder.setContextPath(request.getContextPath());\n\t\t\turlBuilder.setServletPath(request.getServletPath());\n\t\t\turlBuilder.setPathInfo(request.getPathInfo());\n\t\t\turlBuilder.setQuery(request.getQueryString());\n\t\t\treturn urlBuilder.getUrl();\n\t\t}\n\t\t// Fall through to server-side forward with warning message\n\t\tlogger.warn(\n\t\t\t\tLogMessage.format(\"Unable to redirect to HTTPS as no port mapping found for HTTP port %s\", serverPort));\n\t\treturn null;\n\t}\n\n\tpublic int getServerPort(ServletRequest request) {\n\t\treturn this.portMapper.getServerPort(request);\n\t}\n\n\t/**\n\t * Set to true to force login form access to be via https. If this value is true (the\n\t * default is false), and the incoming request for the protected resource which\n\t * triggered the interceptor was not already <code>https</code>, then the client will\n\t * first be redirected to an https URL, even if <tt>serverSideRedirect</tt> is set to\n\t * <tt>true</tt>.\n\t */\n\tpublic void setForceHttps(boolean forceHttps) {\n\t\tthis.forceHttps = forceHttps;\n\t}\n\n\tprotected boolean isForceHttps() {\n\t\treturn this.forceHttps;\n\t}\n\n\tpublic String getLoginFormUrl() {\n\t\treturn this.loginFormUrl;\n\t}\n\n\tpublic void setPortMapper(PortMapper portMapper) {\n\t\tAssert.notNull(portMapper, \"portMapper cannot be null\");\n\t\tthis.portMapper = portMapper;\n\t}\n\n\tprotected PortMapper getPortMapper() {\n\t\treturn this.portMapper;\n\t}\n\n\t/**\n\t * Tells if we are to do a forward to the {@code loginFormUrl} using the\n\t * {@code RequestDispatcher}, instead of a 302 redirect.\n\t * @param useForward true if a forward to the login page should be used. Must be false\n\t * (the default) if {@code loginFormUrl} is set to an absolute value.\n\t */\n\tpublic void setUseForward(boolean useForward) {\n\t\tthis.useForward = useForward;\n\t}\n\n\tprotected boolean isUseForward() {\n\t\treturn this.useForward;\n\t}\n\n\t/**\n\t * Favor using relative URIs when formulating a redirect.\n\t *\n\t * <p>\n\t * Note that a relative redirect is not always possible. For example, when redirecting\n\t * from {@code http} to {@code https}, the URL needs to be absolute.\n\t * </p>\n\t * @param favorRelativeUris whether to favor relative URIs or not\n\t * @since 6.5\n\t */\n\tpublic void setFavorRelativeUris(boolean favorRelativeUris) {\n\t\tthis.favorRelativeUris = favorRelativeUris;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/NoOpAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\n\n/**\n * An {@link AuthenticationEntryPoint} implementation that does nothing.\n *\n * @author Marcus da Coregio\n * @since 6.2\n */\npublic class NoOpAuthenticationEntryPoint implements AuthenticationEntryPoint {\n\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authException) throws IOException, ServletException {\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/NullRememberMeServices.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Implementation of {@link NullRememberMeServices} that does nothing.\n * <p>\n * Used as a default by several framework classes.\n * </p>\n *\n * @author Ben Alex\n */\npublic class NullRememberMeServices implements RememberMeServices {\n\n\t@Override\n\tpublic @Nullable Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void loginFail(HttpServletRequest request, HttpServletResponse response) {\n\t}\n\n\t@Override\n\tpublic void loginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication successfulAuthentication) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/RememberMeServices.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Implement by a class that is capable of providing a remember-me service.\n *\n * <p>\n * Spring Security filters (namely\n * {@link org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter\n * AbstractAuthenticationProcessingFilter} and\n * {@link org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter\n * RememberMeAuthenticationFilter} will call the methods provided by an implementation of\n * this interface.\n * <p>\n * Implementations may implement any type of remember-me capability they wish. Rolling\n * cookies (as per <a href=\n * \"https://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice\">\n * https://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice</a>) can\n * be used, as can simple implementations that don't require a persistent store.\n * Implementations also determine the validity period of a remember-me cookie. This\n * interface has been designed to accommodate any of these remember-me models.\n * <p>\n * This interface does not define how remember-me services should offer a \"cancel all\n * remember-me tokens\" type capability, as this will be implementation specific and\n * requires no hooks into Spring Security.\n *\n * @author Ben Alex\n */\npublic interface RememberMeServices {\n\n\t/**\n\t * This method will be called whenever the <code>SecurityContextHolder</code> does not\n\t * contain an <code>Authentication</code> object and Spring Security wishes to provide\n\t * an implementation with an opportunity to authenticate the request using remember-me\n\t * capabilities. Spring Security makes no attempt whatsoever to determine whether the\n\t * browser has requested remember-me services or presented a valid cookie. Such\n\t * determinations are left to the implementation. If a browser has presented an\n\t * unauthorised cookie for whatever reason, it should be silently ignored and\n\t * invalidated using the <code>HttpServletResponse</code> object.\n\t * <p>\n\t * The returned <code>Authentication</code> must be acceptable to\n\t * {@link org.springframework.security.authentication.AuthenticationManager} or\n\t * {@link org.springframework.security.authentication.AuthenticationProvider} defined\n\t * by the web application. It is recommended\n\t * {@link org.springframework.security.authentication.RememberMeAuthenticationToken}\n\t * be used in most cases, as it has a corresponding authentication provider.\n\t * @param request to look for a remember-me token within\n\t * @param response to change, cancel or modify the remember-me token\n\t * @return a valid authentication object, or <code>null</code> if the request should\n\t * not be authenticated\n\t */\n\t@Nullable Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Called whenever an interactive authentication attempt was made, but the credentials\n\t * supplied by the user were missing or otherwise invalid. Implementations should\n\t * invalidate any and all remember-me tokens indicated in the\n\t * <code>HttpServletRequest</code>.\n\t * @param request that contained an invalid authentication request\n\t * @param response to change, cancel or modify the remember-me token\n\t */\n\tvoid loginFail(HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Called whenever an interactive authentication attempt is successful. An\n\t * implementation may automatically set a remember-me token in the\n\t * <code>HttpServletResponse</code>, although this is not recommended. Instead,\n\t * implementations should typically look for a request parameter that indicates the\n\t * browser has presented an explicit request for authentication to be remembered, such\n\t * as the presence of a HTTP POST parameter.\n\t * @param request that contained the valid authentication request\n\t * @param response to change, cancel or modify the remember-me token\n\t * @param successfulAuthentication representing the successfully authenticated\n\t * principal\n\t */\n\tvoid loginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication successfulAuthentication);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/RequestMatcherDelegatingAuthenticationManagerResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationManagerResolver} that returns a {@link AuthenticationManager}\n * instances based upon the type of {@link HttpServletRequest} passed into\n * {@link #resolve(HttpServletRequest)}.\n *\n * @author Josh Cummings\n * @since 5.7\n */\npublic final class RequestMatcherDelegatingAuthenticationManagerResolver\n\t\timplements AuthenticationManagerResolver<HttpServletRequest> {\n\n\tprivate final List<RequestMatcherEntry<AuthenticationManager>> authenticationManagers;\n\n\tprivate AuthenticationManager defaultAuthenticationManager = (authentication) -> {\n\t\tAuthenticationException ex = new AuthenticationServiceException(\"Cannot authenticate \" + authentication);\n\t\tex.setAuthenticationRequest(authentication);\n\t\tthrow ex;\n\t};\n\n\t/**\n\t * Construct an {@link RequestMatcherDelegatingAuthenticationManagerResolver} based on\n\t * the provided parameters\n\t * @param authenticationManagers a {@link Map} of\n\t * {@link RequestMatcher}/{@link AuthenticationManager} pairs\n\t */\n\tRequestMatcherDelegatingAuthenticationManagerResolver(\n\t\t\tRequestMatcherEntry<AuthenticationManager>... authenticationManagers) {\n\t\tAssert.notEmpty(authenticationManagers, \"authenticationManagers cannot be empty\");\n\t\tthis.authenticationManagers = Arrays.asList(authenticationManagers);\n\t}\n\n\t/**\n\t * Construct an {@link RequestMatcherDelegatingAuthenticationManagerResolver} based on\n\t * the provided parameters\n\t * @param authenticationManagers a {@link Map} of\n\t * {@link RequestMatcher}/{@link AuthenticationManager} pairs\n\t */\n\tRequestMatcherDelegatingAuthenticationManagerResolver(\n\t\t\tList<RequestMatcherEntry<AuthenticationManager>> authenticationManagers) {\n\t\tAssert.notEmpty(authenticationManagers, \"authenticationManagers cannot be empty\");\n\t\tthis.authenticationManagers = authenticationManagers;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic AuthenticationManager resolve(HttpServletRequest context) {\n\t\tfor (RequestMatcherEntry<AuthenticationManager> entry : this.authenticationManagers) {\n\t\t\tif (entry.getRequestMatcher().matches(context)) {\n\t\t\t\treturn entry.getEntry();\n\t\t\t}\n\t\t}\n\n\t\treturn this.defaultAuthenticationManager;\n\t}\n\n\t/**\n\t * Set the default {@link AuthenticationManager} to use when a request does not match\n\t * @param defaultAuthenticationManager the default {@link AuthenticationManager} to\n\t * use\n\t */\n\tpublic void setDefaultAuthenticationManager(AuthenticationManager defaultAuthenticationManager) {\n\t\tAssert.notNull(defaultAuthenticationManager, \"defaultAuthenticationManager cannot be null\");\n\t\tthis.defaultAuthenticationManager = defaultAuthenticationManager;\n\t}\n\n\t/**\n\t * Creates a builder for {@link RequestMatcherDelegatingAuthorizationManager}.\n\t * @return the new {@link RequestMatcherDelegatingAuthorizationManager.Builder}\n\t * instance\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * A builder for {@link RequestMatcherDelegatingAuthenticationManagerResolver}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final List<RequestMatcherEntry<AuthenticationManager>> entries = new ArrayList<>();\n\n\t\tprivate Builder() {\n\n\t\t}\n\n\t\t/**\n\t\t * Maps a {@link RequestMatcher} to an {@link AuthorizationManager}.\n\t\t * @param matcher the {@link RequestMatcher} to use\n\t\t * @param manager the {@link AuthenticationManager} to use\n\t\t * @return the {@link Builder} for further\n\t\t * customizationServerWebExchangeDelegatingReactiveAuthenticationManagerResolvers\n\t\t */\n\t\tpublic Builder add(RequestMatcher matcher, AuthenticationManager manager) {\n\t\t\tAssert.notNull(matcher, \"matcher cannot be null\");\n\t\t\tAssert.notNull(manager, \"manager cannot be null\");\n\t\t\tthis.entries.add(new RequestMatcherEntry<>(matcher, manager));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Creates a {@link RequestMatcherDelegatingAuthenticationManagerResolver}\n\t\t * instance.\n\t\t * @return the {@link RequestMatcherDelegatingAuthenticationManagerResolver}\n\t\t * instance\n\t\t */\n\t\tpublic RequestMatcherDelegatingAuthenticationManagerResolver build() {\n\t\t\treturn new RequestMatcherDelegatingAuthenticationManagerResolver(this.entries);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.access.ExceptionTranslationFilter;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.StringUtils;\n\n/**\n * An authentication success strategy which can make use of the\n * {@link org.springframework.security.web.savedrequest.DefaultSavedRequest} which may\n * have been stored in the session by the {@link ExceptionTranslationFilter}. When such a\n * request is intercepted and requires authentication, the request data is stored to\n * record the original destination before the authentication process commenced, and to\n * allow the request to be reconstructed when a redirect to the same URL occurs. This\n * class is responsible for performing the redirect to the original URL if appropriate.\n * <p>\n * Following a successful authentication, it decides on the redirect destination, based on\n * the following scenarios:\n * <ul>\n * <li>If the {@code alwaysUseDefaultTargetUrl} property is set to true, the\n * {@code defaultTargetUrl} will be used for the destination. Any\n * {@code DefaultSavedRequest} stored in the session will be removed.</li>\n * <li>If the {@code targetUrlParameter} has been set on the request, the value will be\n * used as the destination. Any {@code DefaultSavedRequest} will again be removed.</li>\n * <li>If a {@link org.springframework.security.web.savedrequest.SavedRequest} is found in\n * the {@code RequestCache} (as set by the {@link ExceptionTranslationFilter} to record\n * the original destination before the authentication process commenced), a redirect will\n * be performed to the Url of that original destination. The {@code SavedRequest} object\n * will remain cached and be picked up when the redirected request is received (See\n * <a href=\"\n * {@docRoot}/org/springframework/security/web/savedrequest/SavedRequestAwareWrapper.html\">SavedRequestAwareWrapper</a>).\n * </li>\n * <li>If no {@link org.springframework.security.web.savedrequest.SavedRequest} is found,\n * it will delegate to the base class.</li>\n * </ul>\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {\n\n\tprotected final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate RequestCache requestCache = new HttpSessionRequestCache();\n\n\t@Override\n\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws ServletException, IOException {\n\t\tSavedRequest savedRequest = this.requestCache.getRequest(request, response);\n\t\tif (savedRequest == null) {\n\t\t\tsuper.onAuthenticationSuccess(request, response, authentication);\n\t\t\treturn;\n\t\t}\n\t\tString targetUrlParameter = getTargetUrlParameter();\n\t\tif (isAlwaysUseDefaultTargetUrl()\n\t\t\t\t|| (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {\n\t\t\tthis.requestCache.removeRequest(request, response);\n\t\t\tsuper.onAuthenticationSuccess(request, response, authentication);\n\t\t\treturn;\n\t\t}\n\t\tclearAuthenticationAttributes(request);\n\t\t// Use the DefaultSavedRequest URL\n\t\tString targetUrl = savedRequest.getRedirectUrl();\n\t\tgetRedirectStrategy().sendRedirect(request, response, targetUrl);\n\t}\n\n\tpublic void setRequestCache(RequestCache requestCache) {\n\t\tthis.requestCache = requestCache;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * <tt>AuthenticationFailureHandler</tt> which performs a redirect to the value of the\n * {@link #setDefaultFailureUrl defaultFailureUrl} property when the\n * <tt>onAuthenticationFailure</tt> method is called. If the property has not been set it\n * will send a 401 response to the client, with the error message from the\n * <tt>AuthenticationException</tt> which caused the failure.\n * <p>\n * If the {@code useForward} property is set, a {@code RequestDispatcher.forward} call\n * will be made to the destination instead of a redirect.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate @Nullable String defaultFailureUrl;\n\n\tprivate boolean forwardToDestination = false;\n\n\tprivate boolean allowSessionCreation = true;\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tpublic SimpleUrlAuthenticationFailureHandler() {\n\t}\n\n\tpublic SimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) {\n\t\tsetDefaultFailureUrl(defaultFailureUrl);\n\t}\n\n\t/**\n\t * Performs the redirect or forward to the {@code defaultFailureUrl} if set, otherwise\n\t * returns a 401 error code.\n\t * <p>\n\t * If redirecting or forwarding, {@code saveException} will be called to cache the\n\t * exception for use in the target view.\n\t */\n\t@Override\n\tpublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException exception) throws IOException, ServletException {\n\t\tif (this.defaultFailureUrl == null) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Sending 401 Unauthorized error since no failure URL is set\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.logger.debug(\"Sending 401 Unauthorized error\");\n\t\t\t}\n\t\t\tresponse.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());\n\t\t\treturn;\n\t\t}\n\t\tsaveException(request, exception);\n\t\tif (this.forwardToDestination) {\n\t\t\tthis.logger.debug(\"Forwarding to \" + this.defaultFailureUrl);\n\t\t\trequest.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);\n\t\t}\n\t\telse {\n\t\t\tthis.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);\n\t\t}\n\t}\n\n\t/**\n\t * Caches the {@code AuthenticationException} for use in view rendering.\n\t * <p>\n\t * If {@code forwardToDestination} is set to true, request scope will be used,\n\t * otherwise it will attempt to store the exception in the session. If there is no\n\t * session and {@code allowSessionCreation} is {@code true} a session will be created.\n\t * Otherwise the exception will not be stored.\n\t */\n\tprotected final void saveException(HttpServletRequest request, AuthenticationException exception) {\n\t\tif (this.forwardToDestination) {\n\t\t\trequest.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);\n\t\t\treturn;\n\t\t}\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session != null || this.allowSessionCreation) {\n\t\t\trequest.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);\n\t\t}\n\t}\n\n\t/**\n\t * The URL which will be used as the failure destination.\n\t * @param defaultFailureUrl the failure URL, for example \"/loginFailed.jsp\".\n\t */\n\tpublic void setDefaultFailureUrl(String defaultFailureUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl),\n\t\t\t\t() -> \"'\" + defaultFailureUrl + \"' is not a valid redirect URL\");\n\t\tthis.defaultFailureUrl = defaultFailureUrl;\n\t}\n\n\tprotected boolean isUseForward() {\n\t\treturn this.forwardToDestination;\n\t}\n\n\t/**\n\t * If set to <tt>true</tt>, performs a forward to the failure destination URL instead\n\t * of a redirect. Defaults to <tt>false</tt>.\n\t */\n\tpublic void setUseForward(boolean forwardToDestination) {\n\t\tthis.forwardToDestination = forwardToDestination;\n\t}\n\n\t/**\n\t * Allows overriding of the behaviour when redirecting to a target URL.\n\t */\n\tpublic void setRedirectStrategy(RedirectStrategy redirectStrategy) {\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n\tprotected RedirectStrategy getRedirectStrategy() {\n\t\treturn this.redirectStrategy;\n\t}\n\n\tprotected boolean isAllowSessionCreation() {\n\t\treturn this.allowSessionCreation;\n\t}\n\n\tpublic void setAllowSessionCreation(boolean allowSessionCreation) {\n\t\tthis.allowSessionCreation = allowSessionCreation;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.WebAttributes;\n\n/**\n * <tt>AuthenticationSuccessHandler</tt> which can be configured with a default URL which\n * users should be sent to upon successful authentication.\n * <p>\n * The logic used is that of the {@link AbstractAuthenticationTargetUrlRequestHandler\n * parent class}.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class SimpleUrlAuthenticationSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler\n\t\timplements AuthenticationSuccessHandler {\n\n\tpublic SimpleUrlAuthenticationSuccessHandler() {\n\t}\n\n\t/**\n\t * Constructor which sets the <tt>defaultTargetUrl</tt> property of the base class.\n\t * @param defaultTargetUrl the URL to which the user should be redirected on\n\t * successful authentication.\n\t */\n\tpublic SimpleUrlAuthenticationSuccessHandler(String defaultTargetUrl) {\n\t\tsetDefaultTargetUrl(defaultTargetUrl);\n\t}\n\n\t/**\n\t * Calls the parent class {@code handle()} method to forward or redirect to the target\n\t * URL, and then calls {@code clearAuthenticationAttributes()} to remove any leftover\n\t * session data.\n\t */\n\t@Override\n\tpublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authentication) throws IOException, ServletException {\n\t\thandle(request, response, authentication);\n\t\tclearAuthenticationAttributes(request);\n\t}\n\n\t/**\n\t * Removes temporary authentication-related data which may have been stored in the\n\t * session during the authentication process.\n\t */\n\tprotected final void clearAuthenticationAttributes(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session != null) {\n\t\t\tsession.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Processes an authentication form submission. Called\n * {@code AuthenticationProcessingFilter} prior to Spring Security 3.0.\n * <p>\n * Login forms must present two parameters to this filter: a username and password. The\n * default parameter names to use are contained in the static fields\n * {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and\n * {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}. The parameter names can also be changed by\n * setting the {@code usernameParameter} and {@code passwordParameter} properties.\n * <p>\n * This filter by default responds to the URL {@code /login}.\n *\n * @author Ben Alex\n * @author Colin Sampaleanu\n * @author Luke Taylor\n * @since 3.0\n */\npublic class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n\tpublic static final String SPRING_SECURITY_FORM_USERNAME_KEY = \"username\";\n\n\tpublic static final String SPRING_SECURITY_FORM_PASSWORD_KEY = \"password\";\n\n\tprivate static final RequestMatcher DEFAULT_PATH_REQUEST_MATCHER = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.POST, \"/login\");\n\n\tprivate String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;\n\n\tprivate String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;\n\n\tprivate boolean postOnly = true;\n\n\tpublic UsernamePasswordAuthenticationFilter() {\n\t\tsuper(DEFAULT_PATH_REQUEST_MATCHER);\n\t}\n\n\tpublic UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {\n\t\tsuper(DEFAULT_PATH_REQUEST_MATCHER, authenticationManager);\n\t}\n\n\t@Override\n\tpublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows AuthenticationException {\n\t\tif (this.postOnly && !request.getMethod().equals(\"POST\")) {\n\t\t\tthrow new AuthenticationServiceException(\"Authentication method not supported: \" + request.getMethod());\n\t\t}\n\t\tString username = obtainUsername(request);\n\t\tusername = (username != null) ? username.trim() : \"\";\n\t\tString password = obtainPassword(request);\n\t\tpassword = (password != null) ? password : \"\";\n\t\tUsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,\n\t\t\t\tpassword);\n\t\t// Allow subclasses to set the \"details\" property\n\t\tsetDetails(request, authRequest);\n\t\treturn this.getAuthenticationManager().authenticate(authRequest);\n\t}\n\n\t/**\n\t * Enables subclasses to override the composition of the password, such as by\n\t * including additional values and a separator.\n\t * <p>\n\t * This might be used for example if a postcode/zipcode was required in addition to\n\t * the password. A delimiter such as a pipe (|) should be used to separate the\n\t * password and extended value(s). The <code>AuthenticationDao</code> will need to\n\t * generate the expected password in a corresponding manner.\n\t * </p>\n\t * @param request so that request attributes can be retrieved\n\t * @return the password that will be presented in the <code>Authentication</code>\n\t * request token to the <code>AuthenticationManager</code>\n\t */\n\tprotected @Nullable String obtainPassword(HttpServletRequest request) {\n\t\treturn request.getParameter(this.passwordParameter);\n\t}\n\n\t/**\n\t * Enables subclasses to override the composition of the username, such as by\n\t * including additional values and a separator.\n\t * @param request so that request attributes can be retrieved\n\t * @return the username that will be presented in the <code>Authentication</code>\n\t * request token to the <code>AuthenticationManager</code>\n\t */\n\tprotected @Nullable String obtainUsername(HttpServletRequest request) {\n\t\treturn request.getParameter(this.usernameParameter);\n\t}\n\n\t/**\n\t * Provided so that subclasses may configure what is put into the authentication\n\t * request's details property.\n\t * @param request that an authentication request is being created for\n\t * @param authRequest the authentication request object that should have its details\n\t * set\n\t */\n\tprotected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {\n\t\tauthRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t}\n\n\t/**\n\t * Sets the parameter name which will be used to obtain the username from the login\n\t * request.\n\t * @param usernameParameter the parameter name. Defaults to \"username\".\n\t */\n\tpublic void setUsernameParameter(String usernameParameter) {\n\t\tAssert.hasText(usernameParameter, \"Username parameter must not be empty or null\");\n\t\tthis.usernameParameter = usernameParameter;\n\t}\n\n\t/**\n\t * Sets the parameter name which will be used to obtain the password from the login\n\t * request.\n\t * @param passwordParameter the parameter name. Defaults to \"password\".\n\t */\n\tpublic void setPasswordParameter(String passwordParameter) {\n\t\tAssert.hasText(passwordParameter, \"Password parameter must not be empty or null\");\n\t\tthis.passwordParameter = passwordParameter;\n\t}\n\n\t/**\n\t * Defines whether only HTTP POST requests will be allowed by this filter. If set to\n\t * true, and an authentication request is received which is not a POST request, an\n\t * exception will be raised immediately and authentication will not be attempted. The\n\t * <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed\n\t * authentication.\n\t * <p>\n\t * Defaults to <tt>true</tt> but may be overridden by subclasses.\n\t */\n\tpublic void setPostOnly(boolean postOnly) {\n\t\tthis.postOnly = postOnly;\n\t}\n\n\tpublic final String getUsernameParameter() {\n\t\treturn this.usernameParameter;\n\t}\n\n\tpublic final String getPasswordParameter() {\n\t\treturn this.passwordParameter;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetails.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.Serializable;\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A holder of selected HTTP details related to a web authentication request.\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic class WebAuthenticationDetails implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String remoteAddress;\n\n\tprivate final @Nullable String sessionId;\n\n\t/**\n\t * Records the remote address and will also set the session Id if a session already\n\t * exists (it won't create one).\n\t * @param request that the authentication request was received from\n\t */\n\tpublic WebAuthenticationDetails(HttpServletRequest request) {\n\t\tthis(request.getRemoteAddr(), extractSessionId(request));\n\t}\n\n\t/**\n\t * Constructor to add Jackson2 serialize/deserialize support\n\t * @param remoteAddress remote address of current request\n\t * @param sessionId session id\n\t * @since 5.7\n\t */\n\tpublic WebAuthenticationDetails(String remoteAddress, @Nullable String sessionId) {\n\t\tthis.remoteAddress = remoteAddress;\n\t\tthis.sessionId = sessionId;\n\t}\n\n\tprivate static @Nullable String extractSessionId(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\treturn (session != null) ? session.getId() : null;\n\t}\n\n\t/**\n\t * Indicates the TCP/IP address the authentication request was received from.\n\t * @return the address\n\t */\n\tpublic String getRemoteAddress() {\n\t\treturn this.remoteAddress;\n\t}\n\n\t/**\n\t * Indicates the <code>HttpSession</code> id the authentication request was received\n\t * from.\n\t * @return the session ID\n\t */\n\tpublic @Nullable String getSessionId() {\n\t\treturn this.sessionId;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tWebAuthenticationDetails that = (WebAuthenticationDetails) o;\n\t\treturn Objects.equals(this.remoteAddress, that.remoteAddress) && Objects.equals(this.sessionId, that.sessionId);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.remoteAddress, this.sessionId);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(getClass().getSimpleName()).append(\" [\");\n\t\tsb.append(\"RemoteIpAddress=\").append(this.getRemoteAddress()).append(\", \");\n\t\tsb.append(\"SessionId=\").append(this.getSessionId()).append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetailsSource.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\n\n/**\n * Implementation of {@link AuthenticationDetailsSource} which builds the details object\n * from an <tt>HttpServletRequest</tt> object, creating a {@code WebAuthenticationDetails}\n * .\n *\n * @author Ben Alex\n */\npublic class WebAuthenticationDetailsSource\n\t\timplements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {\n\n\t/**\n\t * @param context the {@code HttpServletRequest} object.\n\t * @return the {@code WebAuthenticationDetails} containing information about the\n\t * current request\n\t */\n\t@Override\n\tpublic WebAuthenticationDetails buildDetails(HttpServletRequest context) {\n\t\treturn new WebAuthenticationDetails(context);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/CompositeLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Performs a logout through all the {@link LogoutHandler} implementations. If any\n * exception is thrown by\n * {@link #logout(HttpServletRequest, HttpServletResponse, Authentication)}, no additional\n * LogoutHandler are invoked.\n *\n * @author Eddú Meléndez\n * @since 4.2.0\n */\npublic final class CompositeLogoutHandler implements LogoutHandler {\n\n\tprivate final List<LogoutHandler> logoutHandlers;\n\n\tpublic CompositeLogoutHandler(LogoutHandler... logoutHandlers) {\n\t\tAssert.notEmpty(logoutHandlers, \"LogoutHandlers are required\");\n\t\tthis.logoutHandlers = Arrays.asList(logoutHandlers);\n\t}\n\n\tpublic CompositeLogoutHandler(List<LogoutHandler> logoutHandlers) {\n\t\tAssert.notEmpty(logoutHandlers, \"LogoutHandlers are required\");\n\t\tthis.logoutHandlers = logoutHandlers;\n\t}\n\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tfor (LogoutHandler handler : this.logoutHandlers) {\n\t\t\thandler.logout(request, response, authentication);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * A logout handler which clears either - A defined list of cookie names, using the\n * context path as the cookie path OR - A given list of Cookies\n *\n * @author Luke Taylor\n * @author Onur Kagan Ozcan\n * @since 3.1\n */\npublic final class CookieClearingLogoutHandler implements LogoutHandler {\n\n\tprivate final List<Function<HttpServletRequest, Cookie>> cookiesToClear;\n\n\tpublic CookieClearingLogoutHandler(String... cookiesToClear) {\n\t\tAssert.notNull(cookiesToClear, \"List of cookies cannot be null\");\n\t\tList<Function<HttpServletRequest, Cookie>> cookieList = new ArrayList<>();\n\t\tfor (String cookieName : cookiesToClear) {\n\t\t\tcookieList.add((request) -> {\n\t\t\t\tCookie cookie = new Cookie(cookieName, null);\n\t\t\t\tString contextPath = request.getContextPath();\n\t\t\t\tString cookiePath = StringUtils.hasText(contextPath) ? contextPath : \"/\";\n\t\t\t\tcookie.setPath(cookiePath);\n\t\t\t\tcookie.setMaxAge(0);\n\t\t\t\tcookie.setSecure(request.isSecure());\n\t\t\t\treturn cookie;\n\t\t\t});\n\t\t}\n\t\tthis.cookiesToClear = cookieList;\n\t}\n\n\t/**\n\t * @param cookiesToClear - One or more Cookie objects that must have maxAge of 0\n\t * @since 5.2\n\t */\n\tpublic CookieClearingLogoutHandler(Cookie... cookiesToClear) {\n\t\tAssert.notNull(cookiesToClear, \"List of cookies cannot be null\");\n\t\tList<Function<HttpServletRequest, Cookie>> cookieList = new ArrayList<>();\n\t\tfor (Cookie cookie : cookiesToClear) {\n\t\t\tAssert.isTrue(cookie.getMaxAge() == 0, \"Cookie maxAge must be 0\");\n\t\t\tcookieList.add((request) -> cookie);\n\t\t}\n\t\tthis.cookiesToClear = cookieList;\n\t}\n\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tthis.cookiesToClear.forEach((f) -> response.addCookie(f.apply(request)));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/DelegatingLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.io.IOException;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Delegates to logout handlers based on matched request matchers\n *\n * @author Shazin Sadakath\n * @author Rob Winch\n * @since 4.1\n */\npublic class DelegatingLogoutSuccessHandler implements LogoutSuccessHandler {\n\n\tprivate final LinkedHashMap<RequestMatcher, LogoutSuccessHandler> matcherToHandler;\n\n\tprivate @Nullable LogoutSuccessHandler defaultLogoutSuccessHandler;\n\n\tpublic DelegatingLogoutSuccessHandler(LinkedHashMap<RequestMatcher, LogoutSuccessHandler> matcherToHandler) {\n\t\tAssert.notEmpty(matcherToHandler, \"matcherToHandler cannot be null\");\n\t\tthis.matcherToHandler = matcherToHandler;\n\t}\n\n\t@Override\n\tpublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) throws IOException, ServletException {\n\t\tfor (Map.Entry<RequestMatcher, LogoutSuccessHandler> entry : this.matcherToHandler.entrySet()) {\n\t\t\tRequestMatcher matcher = entry.getKey();\n\t\t\tif (matcher.matches(request)) {\n\t\t\t\tLogoutSuccessHandler handler = entry.getValue();\n\t\t\t\thandler.onLogoutSuccess(request, response, authentication);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (this.defaultLogoutSuccessHandler != null) {\n\t\t\tthis.defaultLogoutSuccessHandler.onLogoutSuccess(request, response, authentication);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the default {@link LogoutSuccessHandler} if no other handlers available\n\t * @param defaultLogoutSuccessHandler the defaultLogoutSuccessHandler to set\n\t */\n\tpublic void setDefaultLogoutSuccessHandler(LogoutSuccessHandler defaultLogoutSuccessHandler) {\n\t\tthis.defaultLogoutSuccessHandler = defaultLogoutSuccessHandler;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/ForwardLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * {@link LogoutSuccessHandler} implementation that will perform a request dispatcher\n * \"forward\" to the specified target URL.\n *\n * @author Vedran Pavic\n * @since 5.0\n */\npublic class ForwardLogoutSuccessHandler implements LogoutSuccessHandler {\n\n\tprivate final String targetUrl;\n\n\t/**\n\t * Construct a new {@link ForwardLogoutSuccessHandler} with the given target URL.\n\t * @param targetUrl the target URL\n\t */\n\tpublic ForwardLogoutSuccessHandler(String targetUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(targetUrl), () -> \"'\" + targetUrl + \"' is not a valid target URL\");\n\t\tthis.targetUrl = targetUrl;\n\t}\n\n\t@Override\n\tpublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) throws IOException, ServletException {\n\t\trequest.getRequestDispatcher(this.targetUrl).forward(request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/HeaderWriterLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * @author Rafiullah Hamedy\n * @since 5.2\n */\npublic final class HeaderWriterLogoutHandler implements LogoutHandler {\n\n\tprivate final HeaderWriter headerWriter;\n\n\t/**\n\t * Constructs a new instance using the passed {@link HeaderWriter} implementation\n\t * @param headerWriter\n\t * @throws IllegalArgumentException if headerWriter is null.\n\t */\n\tpublic HeaderWriterLogoutHandler(HeaderWriter headerWriter) {\n\t\tAssert.notNull(headerWriter, \"headerWriter cannot be null\");\n\t\tthis.headerWriter = headerWriter;\n\t}\n\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tthis.headerWriter.writeHeaders(request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/HttpStatusReturningLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of the {@link LogoutSuccessHandler}. By default returns an HTTP status\n * code of {@code 200}. This is useful in REST-type scenarios where a redirect upon a\n * successful logout is not desired.\n *\n * @author Gunnar Hillert\n * @since 4.0.2\n */\npublic class HttpStatusReturningLogoutSuccessHandler implements LogoutSuccessHandler {\n\n\tprivate final HttpStatus httpStatusToReturn;\n\n\t/**\n\t * Initialize the {@code HttpStatusLogoutSuccessHandler} with a user-defined\n\t * {@link HttpStatus}.\n\t * @param httpStatusToReturn Must not be {@code null}.\n\t */\n\tpublic HttpStatusReturningLogoutSuccessHandler(HttpStatus httpStatusToReturn) {\n\t\tAssert.notNull(httpStatusToReturn, \"The provided HttpStatus must not be null.\");\n\t\tthis.httpStatusToReturn = httpStatusToReturn;\n\t}\n\n\t/**\n\t * Initialize the {@code HttpStatusLogoutSuccessHandler} with the default\n\t * {@link HttpStatus#OK}.\n\t */\n\tpublic HttpStatusReturningLogoutSuccessHandler() {\n\t\tthis.httpStatusToReturn = HttpStatus.OK;\n\t}\n\n\t/**\n\t * Implementation of\n\t * {@link LogoutSuccessHandler#onLogoutSuccess(HttpServletRequest, HttpServletResponse, Authentication)}\n\t * . Sets the status on the {@link HttpServletResponse}.\n\t */\n\t@Override\n\tpublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) throws IOException {\n\t\tresponse.setStatus(this.httpStatusToReturn.value());\n\t\tresponse.getWriter().flush();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/LogoutFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.GenericFilterBean;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Logs a principal out.\n * <p>\n * Polls a series of {@link LogoutHandler}s. The handlers should be specified in the order\n * they are required. Generally you will want to call logout handlers\n * <code>TokenBasedRememberMeServices</code> and <code>SecurityContextLogoutHandler</code>\n * (in that order).\n * <p>\n * After logout, a redirect will be performed to the URL determined by either the\n * configured <tt>LogoutSuccessHandler</tt> or the <tt>logoutSuccessUrl</tt>, depending on\n * which constructor was used.\n *\n * @author Ben Alex\n * @author Eddú Meléndez\n */\npublic class LogoutFilter extends GenericFilterBean {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate RequestMatcher logoutRequestMatcher;\n\n\tprivate final LogoutHandler handler;\n\n\tprivate final LogoutSuccessHandler logoutSuccessHandler;\n\n\t/**\n\t * Constructor which takes a <tt>LogoutSuccessHandler</tt> instance to determine the\n\t * target destination after logging out. The list of <tt>LogoutHandler</tt>s are\n\t * intended to perform the actual logout functionality (such as clearing the security\n\t * context, invalidating the session, etc.).\n\t */\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tpublic LogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) {\n\t\tthis.handler = new CompositeLogoutHandler(handlers);\n\t\tAssert.notNull(logoutSuccessHandler, \"logoutSuccessHandler cannot be null\");\n\t\tthis.logoutSuccessHandler = logoutSuccessHandler;\n\t\tsetFilterProcessesUrl(\"/logout\");\n\t}\n\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tpublic LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {\n\t\tthis.handler = new CompositeLogoutHandler(handlers);\n\t\tAssert.isTrue(!StringUtils.hasLength(logoutSuccessUrl) || UrlUtils.isValidRedirectUrl(logoutSuccessUrl),\n\t\t\t\t() -> logoutSuccessUrl + \" isn't a valid redirect URL\");\n\t\tSimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();\n\t\tif (StringUtils.hasText(logoutSuccessUrl)) {\n\t\t\turlLogoutSuccessHandler.setDefaultTargetUrl(logoutSuccessUrl);\n\t\t}\n\t\tthis.logoutSuccessHandler = urlLogoutSuccessHandler;\n\t\tsetFilterProcessesUrl(\"/logout\");\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tif (requiresLogout(request, response)) {\n\t\t\tAuthentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Logging out [%s]\", auth));\n\t\t\t}\n\t\t\tthis.handler.logout(request, response, auth);\n\t\t\tthis.logoutSuccessHandler.onLogoutSuccess(request, response, auth);\n\t\t\treturn;\n\t\t}\n\t\tchain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Allow subclasses to modify when a logout should take place.\n\t * @param request the request\n\t * @param response the response\n\t * @return <code>true</code> if logout should occur, <code>false</code> otherwise\n\t */\n\tprotected boolean requiresLogout(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.logoutRequestMatcher.matches(request)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(LogMessage.format(\"Did not match request to %s\", this.logoutRequestMatcher));\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tpublic void setLogoutRequestMatcher(RequestMatcher logoutRequestMatcher) {\n\t\tAssert.notNull(logoutRequestMatcher, \"logoutRequestMatcher cannot be null\");\n\t\tthis.logoutRequestMatcher = logoutRequestMatcher;\n\t}\n\n\tpublic void setFilterProcessesUrl(String filterProcessesUrl) {\n\t\tthis.logoutRequestMatcher = pathPattern(filterProcessesUrl);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/LogoutHandler.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Indicates a class that is able to participate in logout handling.\n *\n * <p>\n * Called by {@link LogoutFilter}.\n *\n * @author Ben Alex\n */\npublic interface LogoutHandler {\n\n\t/**\n\t * Causes a logout to be completed. The method must complete successfully.\n\t * @param request the HTTP request\n\t * @param response the HTTP response\n\t * @param authentication the current principal details\n\t */\n\tvoid logout(HttpServletRequest request, HttpServletResponse response, @Nullable Authentication authentication);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/LogoutSuccessEventPublishingLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.security.authentication.event.LogoutSuccessEvent;\nimport org.springframework.security.core.Authentication;\n\n/**\n * A logout handler which publishes {@link LogoutSuccessEvent}\n *\n * @author Onur Kagan Ozcan\n * @since 5.2.0\n */\npublic final class LogoutSuccessEventPublishingLogoutHandler implements LogoutHandler, ApplicationEventPublisherAware {\n\n\tprivate @Nullable ApplicationEventPublisher eventPublisher;\n\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tif (this.eventPublisher == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (authentication == null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.eventPublisher.publishEvent(new LogoutSuccessEvent(authentication));\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tthis.eventPublisher = applicationEventPublisher;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/LogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Strategy that is called after a successful logout by the {@link LogoutFilter}, to\n * handle redirection or forwarding to the appropriate destination.\n * <p>\n * Note that the interface is almost the same as {@link LogoutHandler} but may raise an\n * exception. <tt>LogoutHandler</tt> implementations expect to be invoked to perform\n * necessary cleanup, so should not throw exceptions.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface LogoutSuccessHandler {\n\n\tvoid onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) throws IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\n\n/**\n * Performs a logout by modifying the\n * {@link org.springframework.security.core.context.SecurityContextHolder}.\n * <p>\n * Will also invalidate the {@link HttpSession} if {@link #isInvalidateHttpSession()} is\n * {@code true} and the session is not {@code null}.\n * <p>\n * Will also remove the {@link Authentication} from the current {@link SecurityContext} if\n * {@link #clearAuthentication} is set to true (default).\n *\n * @author Ben Alex\n * @author Rob Winch\n */\npublic class SecurityContextLogoutHandler implements LogoutHandler {\n\n\tprotected final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate boolean invalidateHttpSession = true;\n\n\tprivate boolean clearAuthentication = true;\n\n\tprivate SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();\n\n\t/**\n\t * Requires the request to be passed in.\n\t * @param request from which to obtain a HTTP session (cannot be null)\n\t * @param response the response (cannot be null)\n\t * @param authentication not used (can be <code>null</code>)\n\t */\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tAssert.notNull(request, \"HttpServletRequest required\");\n\t\tif (this.invalidateHttpSession) {\n\t\t\tHttpSession session = request.getSession(false);\n\t\t\tif (session != null) {\n\t\t\t\tsession.invalidate();\n\t\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\t\tthis.logger.debug(LogMessage.format(\"Invalidated session %s\", session.getId()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tSecurityContext context = this.securityContextHolderStrategy.getContext();\n\t\tthis.securityContextHolderStrategy.clearContext();\n\t\tif (this.clearAuthentication) {\n\t\t\tcontext.setAuthentication(null);\n\t\t}\n\t\tSecurityContext emptyContext = this.securityContextHolderStrategy.createEmptyContext();\n\t\tthis.securityContextRepository.saveContext(emptyContext, request, response);\n\t}\n\n\tpublic boolean isInvalidateHttpSession() {\n\t\treturn this.invalidateHttpSession;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Causes the {@link HttpSession} to be invalidated when this {@link LogoutHandler} is\n\t * invoked. Defaults to true.\n\t * @param invalidateHttpSession true if you wish the session to be invalidated\n\t * (default) or false if it should not be.\n\t */\n\tpublic void setInvalidateHttpSession(boolean invalidateHttpSession) {\n\t\tthis.invalidateHttpSession = invalidateHttpSession;\n\t}\n\n\t/**\n\t * If true, removes the {@link Authentication} from the {@link SecurityContext} to\n\t * prevent issues with concurrent requests.\n\t * @param clearAuthentication true if you wish to clear the {@link Authentication}\n\t * from the {@link SecurityContext} (default) or false if the {@link Authentication}\n\t * should not be removed.\n\t */\n\tpublic void setClearAuthentication(boolean clearAuthentication) {\n\t\tthis.clearAuthentication = clearAuthentication;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to use. Default is\n\t * {@link HttpSessionSecurityContextRepository}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/SimpleUrlLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;\n\n/**\n * Handles the navigation on logout by delegating to the\n * {@link AbstractAuthenticationTargetUrlRequestHandler} base class logic.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class SimpleUrlLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler\n\t\timplements LogoutSuccessHandler {\n\n\t@Override\n\tpublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) throws IOException, ServletException {\n\t\tsuper.handle(request, response, authentication);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/logout/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Logout functionality based around a filter which handles a specific logout URL.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.logout;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ott/DefaultGenerateOneTimeTokenRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport java.time.Duration;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Default implementation of {@link GenerateOneTimeTokenRequestResolver}. Resolves\n * {@link GenerateOneTimeTokenRequest} from username parameter.\n *\n * @author Max Batischev\n * @since 6.5\n */\npublic final class DefaultGenerateOneTimeTokenRequestResolver implements GenerateOneTimeTokenRequestResolver {\n\n\tprivate static final Duration DEFAULT_EXPIRES_IN = Duration.ofMinutes(5);\n\n\tprivate Duration expiresIn = DEFAULT_EXPIRES_IN;\n\n\t@Override\n\tpublic @Nullable GenerateOneTimeTokenRequest resolve(HttpServletRequest request) {\n\t\tString username = request.getParameter(\"username\");\n\t\tif (!StringUtils.hasText(username)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new GenerateOneTimeTokenRequest(username, this.expiresIn);\n\t}\n\n\t/**\n\t * Sets one-time token expiration time\n\t * @param expiresIn one-time token expiration time\n\t */\n\tpublic void setExpiresIn(Duration expiresIn) {\n\t\tAssert.notNull(expiresIn, \"expiresIn cannot be null\");\n\t\tthis.expiresIn = expiresIn;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ott/GenerateOneTimeTokenFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.authentication.ott.OneTimeTokenService;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Filter that process a One-Time Token generation request.\n *\n * @author Marcus da Coregio\n * @since 6.4\n * @see OneTimeTokenService\n */\npublic final class GenerateOneTimeTokenFilter extends OncePerRequestFilter {\n\n\tpublic static final String DEFAULT_GENERATE_URL = \"/ott/generate\";\n\n\tprivate final OneTimeTokenService tokenService;\n\n\tprivate final OneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler;\n\n\tprivate RequestMatcher requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.POST, DEFAULT_GENERATE_URL);\n\n\tprivate GenerateOneTimeTokenRequestResolver requestResolver = new DefaultGenerateOneTimeTokenRequestResolver();\n\n\tpublic GenerateOneTimeTokenFilter(OneTimeTokenService tokenService,\n\t\t\tOneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler) {\n\t\tAssert.notNull(tokenService, \"tokenService cannot be null\");\n\t\tAssert.notNull(tokenGenerationSuccessHandler, \"tokenGenerationSuccessHandler cannot be null\");\n\t\tthis.tokenService = tokenService;\n\t\tthis.tokenGenerationSuccessHandler = tokenGenerationSuccessHandler;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tGenerateOneTimeTokenRequest generateRequest = this.requestResolver.resolve(request);\n\t\tif (generateRequest == null) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tOneTimeToken ott = this.tokenService.generate(generateRequest);\n\t\tthis.tokenGenerationSuccessHandler.handle(request, response, ott);\n\t}\n\n\t/**\n\t * Use the given {@link RequestMatcher} to match the request.\n\t * @param requestMatcher\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\t/**\n\t * Use the given {@link GenerateOneTimeTokenRequestResolver} to resolve\n\t * {@link GenerateOneTimeTokenRequest}.\n\t * @param requestResolver {@link GenerateOneTimeTokenRequestResolver}\n\t * @since 6.5\n\t */\n\tpublic void setRequestResolver(GenerateOneTimeTokenRequestResolver requestResolver) {\n\t\tAssert.notNull(requestResolver, \"requestResolver cannot be null\");\n\t\tthis.requestResolver = requestResolver;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ott/GenerateOneTimeTokenRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\n\n/**\n * A strategy for resolving a {@link GenerateOneTimeTokenRequest} from the\n * {@link HttpServletRequest}.\n *\n * @author Max Batischev\n * @since 6.5\n */\npublic interface GenerateOneTimeTokenRequestResolver {\n\n\t/**\n\t * Resolves {@link GenerateOneTimeTokenRequest} from {@link HttpServletRequest}\n\t * @param request {@link HttpServletRequest} to resolve\n\t * @return {@link GenerateOneTimeTokenRequest}\n\t */\n\t@Nullable GenerateOneTimeTokenRequest resolve(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ott/OneTimeTokenAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.util.StringUtils;\n\n/**\n * An implementation of {@link AuthenticationConverter} that detects if the request\n * contains a {@code token} parameter and constructs a\n * {@link OneTimeTokenAuthenticationToken} with it.\n *\n * @author Marcus da Coregio\n * @since 6.4\n * @see GenerateOneTimeTokenFilter\n */\npublic class OneTimeTokenAuthenticationConverter implements AuthenticationConverter {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\t@Override\n\tpublic @Nullable Authentication convert(HttpServletRequest request) {\n\t\tString token = request.getParameter(\"token\");\n\t\tif (!StringUtils.hasText(token)) {\n\t\t\tthis.logger.debug(\"No token found in request\");\n\t\t\treturn null;\n\t\t}\n\t\treturn OneTimeTokenAuthenticationToken.unauthenticated(token);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ott/OneTimeTokenAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Filter that processes a one-time token for log in.\n * <p>\n * By default, it uses {@link OneTimeTokenAuthenticationConverter} to extract the token\n * from the request.\n *\n * @author Daniel Garnier-Moiroux\n * @since 6.5\n */\npublic final class OneTimeTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n\tpublic static final String DEFAULT_LOGIN_PROCESSING_URL = \"/login/ott\";\n\n\tpublic OneTimeTokenAuthenticationFilter() {\n\t\tsuper(pathPattern(HttpMethod.POST, DEFAULT_LOGIN_PROCESSING_URL));\n\t\tsetAuthenticationConverter(new OneTimeTokenAuthenticationConverter());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ott/OneTimeTokenGenerationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.authentication.ott.OneTimeToken;\n\n/**\n * Defines a strategy to handle generated one-time tokens.\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\n@FunctionalInterface\npublic interface OneTimeTokenGenerationSuccessHandler {\n\n\t/**\n\t * Handles generated one-time tokens\n\t */\n\tvoid handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken)\n\t\t\tthrows IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ott/RedirectOneTimeTokenGenerationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link OneTimeTokenGenerationSuccessHandler} that performs a redirect to a specific\n * location\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic final class RedirectOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate final String redirectUrl;\n\n\t/**\n\t * Constructs an instance of this class that redirects to the specified URL.\n\t * @param redirectUrl\n\t */\n\tpublic RedirectOneTimeTokenGenerationSuccessHandler(String redirectUrl) {\n\t\tAssert.hasText(redirectUrl, \"redirectUrl cannot be empty or null\");\n\t\tthis.redirectUrl = redirectUrl;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken)\n\t\t\tthrows IOException {\n\t\tthis.redirectStrategy.sendRedirect(request, response, this.redirectUrl);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ott/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Package for One Time Token usage.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.ott;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Authentication processing mechanisms, which respond to the submission of authentication\n * credentials using various protocols (eg BASIC, CAS, form login etc).\n */\n@NullMarked\npackage org.springframework.security.web.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/password/HaveIBeenPwnedRestApiPasswordChecker.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.password;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.password.CompromisedPasswordChecker;\nimport org.springframework.security.authentication.password.CompromisedPasswordDecision;\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.client.RestClient;\nimport org.springframework.web.client.RestClientException;\n\n/**\n * Checks if the provided password was leaked by relying on\n * <a href=\"https://www.haveibeenpwned.com/API/v3#PwnedPasswords\">Have I Been Pwned REST\n * API</a>. This implementation uses the Search by Range in order to protect the value of\n * the source password being searched for.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic final class HaveIBeenPwnedRestApiPasswordChecker implements CompromisedPasswordChecker {\n\n\tprivate static final String API_URL = \"https://api.pwnedpasswords.com/range/\";\n\n\tprivate static final int PREFIX_LENGTH = 5;\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate RestClient restClient = RestClient.builder().baseUrl(API_URL).build();\n\n\t@Override\n\tpublic CompromisedPasswordDecision check(@Nullable String password) {\n\t\tif (password == null) {\n\t\t\treturn new CompromisedPasswordDecision(false);\n\t\t}\n\t\tbyte[] hash = getSha1Digest().digest(password.getBytes(StandardCharsets.UTF_8));\n\t\tString encoded = new String(Hex.encode(hash)).toUpperCase(Locale.ROOT);\n\t\tString prefix = encoded.substring(0, PREFIX_LENGTH);\n\t\tString suffix = encoded.substring(PREFIX_LENGTH);\n\n\t\tList<String> passwords = getLeakedPasswordsForPrefix(prefix);\n\t\tboolean isLeaked = findLeakedPassword(passwords, suffix);\n\t\treturn new CompromisedPasswordDecision(isLeaked);\n\t}\n\n\t/**\n\t * Sets the {@link RestClient} to use when making requests to Have I Been Pwned REST\n\t * API. By default, a {@link RestClient} with a base URL of {@link #API_URL} is used.\n\t * @param restClient the {@link RestClient} to use\n\t */\n\tpublic void setRestClient(RestClient restClient) {\n\t\tAssert.notNull(restClient, \"restClient cannot be null\");\n\t\tthis.restClient = restClient;\n\t}\n\n\tprivate boolean findLeakedPassword(List<String> passwords, String suffix) {\n\t\tfor (String pw : passwords) {\n\t\t\tif (pw.startsWith(suffix)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate List<String> getLeakedPasswordsForPrefix(String prefix) {\n\t\ttry {\n\t\t\tString response = this.restClient.get().uri(prefix).retrieve().body(String.class);\n\t\t\tif (!StringUtils.hasText(response)) {\n\t\t\t\treturn Collections.emptyList();\n\t\t\t}\n\t\t\treturn response.lines().toList();\n\t\t}\n\t\tcatch (RestClientException ex) {\n\t\t\tthis.logger.error(\"Request for leaked passwords failed\", ex);\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t}\n\n\tprivate static MessageDigest getSha1Digest() {\n\t\ttry {\n\t\t\treturn MessageDigest.getInstance(\"SHA-1\");\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new RuntimeException(ex.getMessage());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/password/HaveIBeenPwnedRestApiReactivePasswordChecker.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.password;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Locale;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\n\nimport org.springframework.security.authentication.password.CompromisedPasswordDecision;\nimport org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport org.springframework.web.reactive.function.client.WebClientResponseException;\n\n/**\n * Checks if the provided password was leaked by relying on\n * <a href=\"https://www.haveibeenpwned.com/API/v3#PwnedPasswords\">Have I Been Pwned REST\n * API</a>. This implementation uses the Search by Range in order to protect the value of\n * the source password being searched for.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic class HaveIBeenPwnedRestApiReactivePasswordChecker implements ReactiveCompromisedPasswordChecker {\n\n\tprivate static final String API_URL = \"https://api.pwnedpasswords.com/range/\";\n\n\tprivate static final int PREFIX_LENGTH = 5;\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate WebClient webClient = WebClient.builder().baseUrl(API_URL).build();\n\n\t@Override\n\tpublic Mono<CompromisedPasswordDecision> check(@Nullable String password) {\n\t\treturn getHash(password).map((hash) -> new String(Hex.encode(hash)))\n\t\t\t.flatMap(this::findLeakedPassword)\n\t\t\t.defaultIfEmpty(Boolean.FALSE)\n\t\t\t.map(CompromisedPasswordDecision::new);\n\t}\n\n\tprivate Mono<Boolean> findLeakedPassword(String encodedPassword) {\n\t\tString prefix = encodedPassword.substring(0, PREFIX_LENGTH).toUpperCase(Locale.ROOT);\n\t\tString suffix = encodedPassword.substring(PREFIX_LENGTH).toUpperCase(Locale.ROOT);\n\t\treturn getLeakedPasswordsForPrefix(prefix).any((leakedPw) -> leakedPw.startsWith(suffix));\n\t}\n\n\tprivate Flux<String> getLeakedPasswordsForPrefix(String prefix) {\n\t\treturn this.webClient.get().uri(prefix).retrieve().bodyToMono(String.class).flatMapMany((body) -> {\n\t\t\tif (StringUtils.hasText(body)) {\n\t\t\t\treturn Flux.fromStream(body.lines());\n\t\t\t}\n\t\t\treturn Flux.empty();\n\t\t})\n\t\t\t.doOnError((ex) -> this.logger.error(\"Request for leaked passwords failed\", ex))\n\t\t\t.onErrorResume(WebClientResponseException.class, (ex) -> Flux.empty());\n\t}\n\n\t/**\n\t * Sets the {@link WebClient} to use when making requests to Have I Been Pwned REST\n\t * API. By default, a {@link WebClient} with a base URL of {@link #API_URL} is used.\n\t * @param webClient the {@link WebClient} to use\n\t */\n\tpublic void setWebClient(WebClient webClient) {\n\t\tAssert.notNull(webClient, \"webClient cannot be null\");\n\t\tthis.webClient = webClient;\n\t}\n\n\tprivate Mono<byte[]> getHash(@Nullable String rawPassword) {\n\t\treturn Mono.justOrEmpty(rawPassword)\n\t\t\t.map((password) -> getSha1Digest().digest(password.getBytes(StandardCharsets.UTF_8)))\n\t\t\t.subscribeOn(Schedulers.boundedElastic())\n\t\t\t.publishOn(Schedulers.parallel());\n\t}\n\n\tprivate static MessageDigest getSha1Digest() {\n\t\ttry {\n\t\t\treturn MessageDigest.getInstance(\"SHA-1\");\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new RuntimeException(ex.getMessage());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/password/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes for Password APIs.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.password;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.lang.Contract;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Base class for processing filters that handle pre-authenticated authentication\n * requests, where it is assumed that the principal has already been authenticated by an\n * external system.\n * <p>\n * The purpose is then only to extract the necessary information on the principal from the\n * incoming request, rather than to authenticate them. External authentication systems may\n * provide this information via request data such as headers or cookies which the\n * pre-authentication system can extract. It is assumed that the external system is\n * responsible for the accuracy of the data and preventing the submission of forged\n * values.\n *\n * Subclasses must implement the {@code getPreAuthenticatedPrincipal()} and\n * {@code getPreAuthenticatedCredentials()} methods. Subclasses of this filter are\n * typically used in combination with a {@code PreAuthenticatedAuthenticationProvider},\n * which is used to load additional data for the user. This provider will reject null\n * credentials, so the {@link #getPreAuthenticatedCredentials} method should not return\n * null for a valid principal.\n * <p>\n * If the security context already contains an {@code Authentication} object (either from\n * a invocation of the filter or because of some other authentication mechanism), the\n * filter will do nothing by default. You can force it to check for a change in the\n * principal by setting the {@link #setCheckForPrincipalChanges(boolean)\n * checkForPrincipalChanges} property.\n * <p>\n * By default, the filter chain will proceed when an authentication attempt fails in order\n * to allow other authentication mechanisms to process the request. To reject the\n * credentials immediately, set the\n * <tt>continueFilterChainOnUnsuccessfulAuthentication</tt> flag to false. The exception\n * raised by the <tt>AuthenticationManager</tt> will the be re-thrown. Note that this will\n * not affect cases where the principal returned by {@link #getPreAuthenticatedPrincipal}\n * is null, when the chain will still proceed as normal.\n * <p>\n * The filter saves the {@link SecurityContext} using the configured\n * {@link SecurityContextRepository}, which can be set via\n * {@link #setSecurityContextRepository}.\n *\n * @author Luke Taylor\n * @author Ruud Senden\n * @author Rob Winch\n * @author Tadaya Tsuyukubo\n * @since 2.0\n */\npublic abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFilterBean\n\t\timplements ApplicationEventPublisherAware {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable ApplicationEventPublisher eventPublisher = null;\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate boolean continueFilterChainOnUnsuccessfulAuthentication = true;\n\n\tprivate boolean checkForPrincipalChanges;\n\n\tprivate boolean invalidateSessionOnPrincipalChange = true;\n\n\tprivate @Nullable AuthenticationSuccessHandler authenticationSuccessHandler = null;\n\n\tprivate @Nullable AuthenticationFailureHandler authenticationFailureHandler = null;\n\n\tprivate RequestMatcher requiresAuthenticationRequestMatcher = new PreAuthenticatedProcessingRequestMatcher();\n\n\tprivate SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();\n\n\tprivate boolean mfaEnabled;\n\n\t/**\n\t * Check whether all required properties have been set.\n\t */\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\ttry {\n\t\t\tsuper.afterPropertiesSet();\n\t\t}\n\t\tcatch (ServletException ex) {\n\t\t\t// convert to RuntimeException for passivity on afterPropertiesSet signature\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t\tAssert.notNull(this.authenticationManager, \"An AuthenticationManager must be set\");\n\t}\n\n\t/**\n\t * Try to authenticate a pre-authenticated user with Spring Security if the user has\n\t * not yet been authenticated.\n\t */\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tif (this.requiresAuthenticationRequestMatcher.matches((HttpServletRequest) request)) {\n\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\tlogger.debug(LogMessage\n\t\t\t\t\t.of(() -> \"Authenticating \" + this.securityContextHolderStrategy.getContext().getAuthentication()));\n\t\t\t}\n\t\t\tdoAuthenticate((HttpServletRequest) request, (HttpServletResponse) response);\n\t\t}\n\t\telse {\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(LogMessage.format(\"Did not authenticate since request did not match [%s]\",\n\t\t\t\t\t\tthis.requiresAuthenticationRequestMatcher));\n\t\t\t}\n\t\t}\n\t\tchain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Determines if the current principal has changed. The default implementation tries\n\t *\n\t * <ul>\n\t * <li>If the {@link #getPreAuthenticatedPrincipal(HttpServletRequest)} is a String,\n\t * the {@link Authentication#getName()} is compared against the pre authenticated\n\t * principal</li>\n\t * <li>Otherwise, the {@link #getPreAuthenticatedPrincipal(HttpServletRequest)} is\n\t * compared against the {@link Authentication#getPrincipal()}\n\t * </ul>\n\t *\n\t * <p>\n\t * Subclasses can override this method to determine when a principal has changed.\n\t * </p>\n\t * @param request\n\t * @param currentAuthentication\n\t * @return true if the principal has changed, else false\n\t */\n\tprotected boolean principalChanged(HttpServletRequest request, Authentication currentAuthentication) {\n\t\tObject principal = getPreAuthenticatedPrincipal(request);\n\t\tif ((principal instanceof String) && currentAuthentication.getName().equals(principal)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (principal != null && principal.equals(currentAuthentication.getPrincipal())) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.logger.debug(LogMessage.format(\"Pre-authenticated principal has changed to %s and will be reauthenticated\",\n\t\t\t\tprincipal));\n\t\treturn true;\n\t}\n\n\t/**\n\t * Do the actual authentication for a pre-authenticated user.\n\t */\n\tprivate void doAuthenticate(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows IOException, ServletException {\n\t\tObject principal = getPreAuthenticatedPrincipal(request);\n\t\tif (principal == null) {\n\t\t\tthis.logger.debug(\"No pre-authenticated principal found in request\");\n\t\t\treturn;\n\t\t}\n\t\tthis.logger.debug(LogMessage.format(\"preAuthenticatedPrincipal = %s, trying to authenticate\", principal));\n\t\tObject credentials = getPreAuthenticatedCredentials(request);\n\t\ttry {\n\t\t\tPreAuthenticatedAuthenticationToken authenticationRequest = new PreAuthenticatedAuthenticationToken(\n\t\t\t\t\tprincipal, credentials);\n\t\t\tauthenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\t\tAuthentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);\n\t\t\tAuthentication current = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (shouldPerformMfa(current, authenticationResult)) {\n\t\t\t\tauthenticationResult = authenticationResult.toBuilder()\n\t\t\t\t// @formatter:off\n\t\t\t\t\t.authorities((a) -> {\n\t\t\t\t\t\tSet<String> newAuthorities = a.stream()\n\t\t\t\t\t\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t\t\t\t\t\t.collect(Collectors.toUnmodifiableSet());\n\t\t\t\t\t\tfor (GrantedAuthority currentAuthority : current.getAuthorities()) {\n\t\t\t\t\t\t\tif (!newAuthorities.contains(currentAuthority.getAuthority())) {\n\t\t\t\t\t\t\t\ta.add(currentAuthority);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.build();\n\t\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t\tsuccessfulAuthentication(request, response, authenticationResult);\n\t\t}\n\t\tcatch (AuthenticationException ex) {\n\t\t\tunsuccessfulAuthentication(request, response, ex);\n\t\t\tif (!this.continueFilterChainOnUnsuccessfulAuthentication) {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Contract(\"null, _ -> false\")\n\tprivate boolean shouldPerformMfa(@Nullable Authentication current, Authentication authenticationResult) {\n\t\tif (!this.mfaEnabled) {\n\t\t\treturn false;\n\t\t}\n\t\tif (current == null || !current.isAuthenticated()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!declaresToBuilder(authenticationResult)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn current.getName().equals(authenticationResult.getName());\n\t}\n\n\tprivate static boolean declaresToBuilder(Authentication authentication) {\n\t\tfor (Method method : authentication.getClass().getDeclaredMethods()) {\n\t\t\tif (method.getName().equals(\"toBuilder\") && method.getParameterTypes().length == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Puts the <code>Authentication</code> instance returned by the authentication\n\t * manager into the secure context.\n\t */\n\tprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authResult) throws IOException, ServletException {\n\t\tthis.logger.debug(LogMessage.format(\"Authentication success: %s\", authResult));\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authResult);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\tif (this.eventPublisher != null) {\n\t\t\tthis.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));\n\t\t}\n\t\tif (this.authenticationSuccessHandler != null) {\n\t\t\tthis.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authResult);\n\t\t}\n\t}\n\n\t/**\n\t * Ensures the authentication object in the secure context is set to null when\n\t * authentication fails.\n\t * <p>\n\t * Caches the failure exception as a request attribute\n\t */\n\tprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException failed) throws IOException, ServletException {\n\t\tthis.securityContextHolderStrategy.clearContext();\n\t\tthis.logger.debug(\"Cleared security context due to exception\", failed);\n\t\trequest.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, failed);\n\t\tif (this.authenticationFailureHandler != null) {\n\t\t\tthis.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);\n\t\t}\n\t}\n\n\t/**\n\t * @param anApplicationEventPublisher The ApplicationEventPublisher to use\n\t */\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher anApplicationEventPublisher) {\n\t\tthis.eventPublisher = anApplicationEventPublisher;\n\t}\n\n\t/**\n\t * Enables Multi-Factor Authentication (MFA) support.\n\t * @param mfaEnabled true to enable MFA support, false to disable it. Default is\n\t * false.\n\t */\n\tpublic void setMfaEnabled(boolean mfaEnabled) {\n\t\tthis.mfaEnabled = mfaEnabled;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * authentication success. The default action is to save the {@link SecurityContext}\n\t * in {@link HttpSession} using {@link HttpSessionSecurityContextRepository}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * @param authenticationDetailsSource The AuthenticationDetailsSource to use\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"AuthenticationDetailsSource required\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\tprotected AuthenticationDetailsSource<HttpServletRequest, ?> getAuthenticationDetailsSource() {\n\t\treturn this.authenticationDetailsSource;\n\t}\n\n\t/**\n\t * @param authenticationManager The AuthenticationManager to use\n\t */\n\tpublic void setAuthenticationManager(AuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t/**\n\t * If set to {@code true} (the default), any {@code AuthenticationException} raised by\n\t * the {@code AuthenticationManager} will be swallowed, and the request will be\n\t * allowed to proceed, potentially using alternative authentication mechanisms. If\n\t * {@code false}, authentication failure will result in an immediate exception.\n\t * @param shouldContinue set to {@code true} to allow the request to proceed after a\n\t * failed authentication.\n\t */\n\tpublic void setContinueFilterChainOnUnsuccessfulAuthentication(boolean shouldContinue) {\n\t\tthis.continueFilterChainOnUnsuccessfulAuthentication = shouldContinue;\n\t}\n\n\t/**\n\t * If set, the pre-authenticated principal will be checked on each request and\n\t * compared against the name of the current <tt>Authentication</tt> object. A check to\n\t * determine if {@link Authentication#getPrincipal()} is equal to the principal will\n\t * also be performed. If a change is detected, the user will be reauthenticated.\n\t * @param checkForPrincipalChanges\n\t */\n\tpublic void setCheckForPrincipalChanges(boolean checkForPrincipalChanges) {\n\t\tthis.checkForPrincipalChanges = checkForPrincipalChanges;\n\t}\n\n\t/**\n\t * If <tt>checkForPrincipalChanges</tt> is set, and a change of principal is detected,\n\t * determines whether any existing session should be invalidated before proceeding to\n\t * authenticate the new principal.\n\t * @param invalidateSessionOnPrincipalChange <tt>false</tt> to retain the existing\n\t * session. Defaults to <tt>true</tt>.\n\t */\n\tpublic void setInvalidateSessionOnPrincipalChange(boolean invalidateSessionOnPrincipalChange) {\n\t\tthis.invalidateSessionOnPrincipalChange = invalidateSessionOnPrincipalChange;\n\t}\n\n\t/**\n\t * Sets the strategy used to handle a successful authentication.\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the strategy used to handle a failed authentication.\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\t/**\n\t * Sets the request matcher to check whether to proceed the request further.\n\t */\n\tpublic void setRequiresAuthenticationRequestMatcher(RequestMatcher requiresAuthenticationRequestMatcher) {\n\t\tAssert.notNull(requiresAuthenticationRequestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Override to extract the principal information from the current request\n\t */\n\tprotected abstract @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest request);\n\n\t/**\n\t * Override to extract the credentials (if applicable) from the current request.\n\t * Should not return null for a valid principal, though some implementations may\n\t * return a dummy value.\n\t */\n\tprotected abstract @Nullable Object getPreAuthenticatedCredentials(HttpServletRequest request);\n\n\t/**\n\t * Request matcher for default auth check logic\n\t */\n\tprivate class PreAuthenticatedProcessingRequestMatcher implements RequestMatcher {\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\tAuthentication currentUser = AbstractPreAuthenticatedProcessingFilter.this.securityContextHolderStrategy\n\t\t\t\t.getContext()\n\t\t\t\t.getAuthentication();\n\t\t\tif (currentUser == null) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (!AbstractPreAuthenticatedProcessingFilter.this.checkForPrincipalChanges) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!principalChanged(request, currentUser)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tAbstractPreAuthenticatedProcessingFilter.this.logger\n\t\t\t\t.debug(\"Pre-authenticated principal has changed and will be reauthenticated\");\n\t\t\tif (AbstractPreAuthenticatedProcessingFilter.this.invalidateSessionOnPrincipalChange) {\n\t\t\t\tAbstractPreAuthenticatedProcessingFilter.this.securityContextHolderStrategy.clearContext();\n\t\t\t\tHttpSession session = request.getSession(false);\n\t\t\t\tif (session != null) {\n\t\t\t\t\tAbstractPreAuthenticatedProcessingFilter.this.logger.debug(\"Invalidating existing session\");\n\t\t\t\t\tsession.invalidate();\n\t\t\t\t\trequest.getSession();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Processes a pre-authenticated authentication request. The request will typically\n * originate from a\n * {@link org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter}\n * subclass.\n *\n * <p>\n * This authentication provider will not perform any checks on authentication requests, as\n * they should already be pre-authenticated. However, the AuthenticationUserDetailsService\n * implementation may still throw a UsernameNotFoundException, for example.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic class PreAuthenticatedAuthenticationProvider implements AuthenticationProvider, InitializingBean, Ordered {\n\n\tprivate static final Log logger = LogFactory.getLog(PreAuthenticatedAuthenticationProvider.class);\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> preAuthenticatedUserDetailsService;\n\n\tprivate UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();\n\n\tprivate Supplier<Collection<GrantedAuthority>> grantedAuthoritySupplier = List::of;\n\n\tprivate boolean throwExceptionWhenTokenRejected;\n\n\tprivate int order = -1; // default: same as non-ordered\n\n\t/**\n\t * Check whether all required properties have been set.\n\t */\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.preAuthenticatedUserDetailsService, \"An AuthenticationUserDetailsService must be set\");\n\t}\n\n\t/**\n\t * Authenticate the given PreAuthenticatedAuthenticationToken.\n\t * <p>\n\t * If the principal contained in the authentication object is null, the request will\n\t * be ignored to allow other providers to authenticate it.\n\t */\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tif (!supports(authentication.getClass())) {\n\t\t\treturn null;\n\t\t}\n\t\tlogger.debug(LogMessage.format(\"PreAuthenticated authentication request: %s\", authentication));\n\t\tif (authentication.getPrincipal() == null) {\n\t\t\tlogger.debug(\"No pre-authenticated principal found in request.\");\n\t\t\tif (this.throwExceptionWhenTokenRejected) {\n\t\t\t\tthrow new BadCredentialsException(\"No pre-authenticated principal found in request.\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif (authentication.getCredentials() == null) {\n\t\t\tlogger.debug(\"No pre-authenticated credentials found in request.\");\n\t\t\tif (this.throwExceptionWhenTokenRejected) {\n\t\t\t\tthrow new BadCredentialsException(\"No pre-authenticated credentials found in request.\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tUserDetails userDetails = this.preAuthenticatedUserDetailsService\n\t\t\t.loadUserDetails((PreAuthenticatedAuthenticationToken) authentication);\n\t\tthis.userDetailsChecker.check(userDetails);\n\t\tCollection<GrantedAuthority> authorities = new LinkedHashSet<>(userDetails.getAuthorities());\n\t\tauthorities.addAll(this.grantedAuthoritySupplier.get());\n\t\tPreAuthenticatedAuthenticationToken result = new PreAuthenticatedAuthenticationToken(userDetails,\n\t\t\t\tauthentication.getCredentials(), authorities);\n\t\tresult.setDetails(authentication.getDetails());\n\t\treturn result;\n\t}\n\n\t/**\n\t * Indicate that this provider only supports PreAuthenticatedAuthenticationToken\n\t * (sub)classes.\n\t */\n\t@Override\n\tpublic final boolean supports(Class<?> authentication) {\n\t\treturn PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication);\n\t}\n\n\t/**\n\t * Set the AuthenticatedUserDetailsService to be used to load the {@code UserDetails}\n\t * for the authenticated user.\n\t * @param uds\n\t */\n\tpublic void setPreAuthenticatedUserDetailsService(\n\t\t\tAuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> uds) {\n\t\tthis.preAuthenticatedUserDetailsService = uds;\n\t}\n\n\t/**\n\t * If true, causes the provider to throw a BadCredentialsException if the presented\n\t * authentication request is invalid (contains a null principal or credentials).\n\t * Otherwise it will just return null. Defaults to false.\n\t */\n\tpublic void setThrowExceptionWhenTokenRejected(boolean throwExceptionWhenTokenRejected) {\n\t\tthis.throwExceptionWhenTokenRejected = throwExceptionWhenTokenRejected;\n\t}\n\n\t/**\n\t * Sets the strategy which will be used to validate the loaded <tt>UserDetails</tt>\n\t * object for the user. Defaults to an {@link AccountStatusUserDetailsChecker}.\n\t * @param userDetailsChecker\n\t */\n\tpublic void setUserDetailsChecker(UserDetailsChecker userDetailsChecker) {\n\t\tAssert.notNull(userDetailsChecker, \"userDetailsChecker cannot be null\");\n\t\tthis.userDetailsChecker = userDetailsChecker;\n\t}\n\n\t/**\n\t * Sets authorities that this provider should grant once authentication completes\n\t * @param grantedAuthoritySupplier the supplier that grants authorities\n\t */\n\tpublic void setGrantedAuthoritySupplier(Supplier<Collection<GrantedAuthority>> grantedAuthoritySupplier) {\n\t\tAssert.notNull(grantedAuthoritySupplier, \"grantedAuthoritySupplier cannot be null\");\n\t\tthis.grantedAuthoritySupplier = grantedAuthoritySupplier;\n\t}\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int i) {\n\t\tthis.order = i;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * {@link org.springframework.security.core.Authentication} implementation for\n * pre-authenticated authentication.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic class PreAuthenticatedAuthenticationToken extends AbstractAuthenticationToken {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final Object principal;\n\n\tprivate final @Nullable Object credentials;\n\n\t/**\n\t * Constructor used for an authentication request. The\n\t * {@link org.springframework.security.core.Authentication#isAuthenticated()} will\n\t * return <code>false</code>.\n\t * @param aPrincipal The pre-authenticated principal\n\t * @param aCredentials The pre-authenticated credentials\n\t */\n\tpublic PreAuthenticatedAuthenticationToken(Object aPrincipal, @Nullable Object aCredentials) {\n\t\tsuper((Collection<? extends GrantedAuthority>) null);\n\t\tthis.principal = aPrincipal;\n\t\tthis.credentials = aCredentials;\n\t}\n\n\t/**\n\t * Constructor used for an authentication response. The\n\t * {@link org.springframework.security.core.Authentication#isAuthenticated()} will\n\t * return <code>true</code>.\n\t * @param aPrincipal The authenticated principal\n\t * @param anAuthorities The granted authorities\n\t */\n\tpublic PreAuthenticatedAuthenticationToken(Object aPrincipal, @Nullable Object aCredentials,\n\t\t\tCollection<? extends GrantedAuthority> anAuthorities) {\n\t\tsuper(anAuthorities);\n\t\tthis.principal = aPrincipal;\n\t\tthis.credentials = aCredentials;\n\t\tsetAuthenticated(true);\n\t}\n\n\tprotected PreAuthenticatedAuthenticationToken(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.principal = builder.principal;\n\t\tthis.credentials = builder.credentials;\n\t}\n\n\t/**\n\t * Get the credentials\n\t */\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn this.credentials;\n\t}\n\n\t/**\n\t * Get the principal\n\t */\n\t@Override\n\tpublic Object getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link PreAuthenticatedAuthenticationToken} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate Object principal;\n\n\t\tprivate @Nullable Object credentials;\n\n\t\tprotected Builder(PreAuthenticatedAuthenticationToken token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.principal;\n\t\t\tthis.credentials = token.credentials;\n\t\t}\n\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.notNull(principal, \"principal cannot be null\");\n\t\t\tthis.principal = principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic B credentials(@Nullable Object credentials) {\n\t\t\tthis.credentials = credentials;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic PreAuthenticatedAuthenticationToken build() {\n\t\t\treturn new PreAuthenticatedAuthenticationToken(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedCredentialsNotFoundException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.AuthenticationException;\n\npublic class PreAuthenticatedCredentialsNotFoundException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2026209817833032728L;\n\n\tpublic PreAuthenticatedCredentialsNotFoundException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * @param message The message for the Exception\n\t * @param cause The Exception that caused this Exception.\n\t */\n\tpublic PreAuthenticatedCredentialsNotFoundException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedGrantedAuthoritiesUserDetailsService.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.Collection;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.GrantedAuthoritiesContainer;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * This AuthenticationUserDetailsService implementation creates a UserDetails object based\n * solely on the information contained in the given PreAuthenticatedAuthenticationToken.\n * The user name is set to the name as returned by\n * PreAuthenticatedAuthenticationToken.getName(), the password is set to a fixed dummy\n * value (it will not be used by the PreAuthenticatedAuthenticationProvider anyway), and\n * the Granted Authorities are retrieved from the details object as returned by\n * PreAuthenticatedAuthenticationToken.getDetails().\n *\n * <p>\n * The details object as returned by PreAuthenticatedAuthenticationToken.getDetails() must\n * implement the {@link GrantedAuthoritiesContainer} interface for this implementation to\n * work.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic class PreAuthenticatedGrantedAuthoritiesUserDetailsService\n\t\timplements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {\n\n\t/**\n\t * Get a UserDetails object based on the user name contained in the given token, and\n\t * the GrantedAuthorities as returned by the GrantedAuthoritiesContainer\n\t * implementation as returned by the token.getDetails() method.\n\t */\n\t@Override\n\tpublic final UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) throws AuthenticationException {\n\t\tAssert.notNull(token.getDetails(), \"token.getDetails() cannot be null\");\n\t\tAssert.isInstanceOf(GrantedAuthoritiesContainer.class, token.getDetails());\n\t\tCollection<? extends GrantedAuthority> authorities = ((GrantedAuthoritiesContainer) token.getDetails())\n\t\t\t.getGrantedAuthorities();\n\t\treturn createUserDetails(token, authorities);\n\t}\n\n\t/**\n\t * Creates the final <tt>UserDetails</tt> object. Can be overridden to customize the\n\t * contents.\n\t * @param token the authentication request token\n\t * @param authorities the pre-authenticated authorities.\n\t */\n\tprotected UserDetails createUserDetails(Authentication token, Collection<? extends GrantedAuthority> authorities) {\n\t\treturn new User(token.getName(), \"N/A\", true, true, true, true, authorities);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.GrantedAuthoritiesContainer;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\n\n/**\n * This WebAuthenticationDetails implementation allows for storing a list of\n * pre-authenticated Granted Authorities.\n *\n * @author Ruud Senden\n * @author Luke Taylor\n * @since 2.0\n */\npublic class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends WebAuthenticationDetails\n\t\timplements GrantedAuthoritiesContainer {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final List<GrantedAuthority> authorities;\n\n\tpublic PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(HttpServletRequest request,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(request);\n\t\tList<GrantedAuthority> temp = new ArrayList<>(authorities.size());\n\t\ttemp.addAll(authorities);\n\t\tthis.authorities = Collections.unmodifiableList(temp);\n\t}\n\n\t@Override\n\tpublic List<GrantedAuthority> getGrantedAuthorities() {\n\t\treturn this.authorities;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(super.toString()).append(\"; \");\n\t\tsb.append(this.authorities);\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/RequestAttributeAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A simple pre-authenticated filter which obtains the username from request attributes,\n * for use with SSO systems such as\n * <a href=\"https://webauth.stanford.edu/manual/mod/mod_webauth.html#java\">Stanford\n * WebAuth</a> or <a href=\n * \"https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPJavaInstall\">Shibboleth</a>.\n * <p>\n * As with most pre-authenticated scenarios, it is essential that the external\n * authentication system is set up correctly as this filter does no authentication\n * whatsoever.\n * <p>\n * The property {@code principalEnvironmentVariable} is the name of the request attribute\n * that contains the username. It defaults to \"REMOTE_USER\" for compatibility with WebAuth\n * and Shibboleth.\n * <p>\n * If the environment variable is missing from the request,\n * {@code getPreAuthenticatedPrincipal} will throw an exception. You can override this\n * behaviour by setting the {@code exceptionIfVariableMissing} property.\n *\n * @author Milan Sevcik\n * @since 4.2\n */\npublic class RequestAttributeAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {\n\n\tprivate String principalEnvironmentVariable = \"REMOTE_USER\";\n\n\tprivate @Nullable String credentialsEnvironmentVariable;\n\n\tprivate boolean exceptionIfVariableMissing = true;\n\n\t/**\n\t * Read and returns the variable named by {@code principalEnvironmentVariable} from\n\t * the request.\n\t * @throws PreAuthenticatedCredentialsNotFoundException if the environment variable is\n\t * missing and {@code exceptionIfVariableMissing} is set to {@code true}.\n\t */\n\t@Override\n\tprotected @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest request) {\n\t\tString principal = (String) request.getAttribute(this.principalEnvironmentVariable);\n\t\tif (principal == null && this.exceptionIfVariableMissing) {\n\t\t\tthrow new PreAuthenticatedCredentialsNotFoundException(\n\t\t\t\t\tthis.principalEnvironmentVariable + \" variable not found in request.\");\n\t\t}\n\t\treturn principal;\n\t}\n\n\t/**\n\t * Credentials aren't usually applicable, but if a\n\t * {@code credentialsEnvironmentVariable} is set, this will be read and used as the\n\t * credentials value. Otherwise a dummy value will be used.\n\t */\n\t@Override\n\tprotected @Nullable Object getPreAuthenticatedCredentials(HttpServletRequest request) {\n\t\tif (this.credentialsEnvironmentVariable != null) {\n\t\t\treturn request.getAttribute(this.credentialsEnvironmentVariable);\n\t\t}\n\t\treturn \"N/A\";\n\t}\n\n\tpublic void setPrincipalEnvironmentVariable(String principalEnvironmentVariable) {\n\t\tAssert.hasText(principalEnvironmentVariable, \"principalEnvironmentVariable must not be empty or null\");\n\t\tthis.principalEnvironmentVariable = principalEnvironmentVariable;\n\t}\n\n\tpublic void setCredentialsEnvironmentVariable(String credentialsEnvironmentVariable) {\n\t\tAssert.hasText(credentialsEnvironmentVariable, \"credentialsEnvironmentVariable must not be empty or null\");\n\t\tthis.credentialsEnvironmentVariable = credentialsEnvironmentVariable;\n\t}\n\n\t/**\n\t * Defines whether an exception should be raised if the principal variable is missing.\n\t * Defaults to {@code true}.\n\t * @param exceptionIfVariableMissing set to {@code false} to override the default\n\t * behaviour and allow the request to proceed if no variable is found.\n\t */\n\tpublic void setExceptionIfVariableMissing(boolean exceptionIfVariableMissing) {\n\t\tthis.exceptionIfVariableMissing = exceptionIfVariableMissing;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/RequestHeaderAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A simple pre-authenticated filter which obtains the username from a request header, for\n * use with systems such as CA Siteminder.\n * <p>\n * As with most pre-authenticated scenarios, it is essential that the external\n * authentication system is set up correctly as this filter does no authentication\n * whatsoever. All the protection is assumed to be provided externally and if this filter\n * is included inappropriately in a configuration, it would be possible to assume the\n * identity of a user merely by setting the correct header name. This also means it should\n * not generally be used in combination with other Spring Security authentication\n * mechanisms such as form login, as this would imply there was a means of bypassing the\n * external system which would be risky.\n * <p>\n * The property {@code principalRequestHeader} is the name of the request header that\n * contains the username. It defaults to \"SM_USER\" for compatibility with Siteminder.\n * <p>\n * If the header is missing from the request, {@code getPreAuthenticatedPrincipal} will\n * throw an exception. You can override this behaviour by setting the\n * {@code exceptionIfHeaderMissing} property.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class RequestHeaderAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {\n\n\tprivate String principalRequestHeader = \"SM_USER\";\n\n\tprivate @Nullable String credentialsRequestHeader;\n\n\tprivate boolean exceptionIfHeaderMissing = true;\n\n\t/**\n\t * Read and returns the header named by {@code principalRequestHeader} from the\n\t * request.\n\t * @throws PreAuthenticatedCredentialsNotFoundException if the header is missing and\n\t * {@code exceptionIfHeaderMissing} is set to {@code true}.\n\t */\n\t@Override\n\tprotected @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest request) {\n\t\tString principal = request.getHeader(this.principalRequestHeader);\n\t\tif (principal == null && this.exceptionIfHeaderMissing) {\n\t\t\tthrow new PreAuthenticatedCredentialsNotFoundException(\n\t\t\t\t\tthis.principalRequestHeader + \" header not found in request.\");\n\t\t}\n\t\treturn principal;\n\t}\n\n\t/**\n\t * Credentials aren't usually applicable, but if a {@code credentialsRequestHeader} is\n\t * set, this will be read and used as the credentials value. Otherwise a dummy value\n\t * will be used.\n\t */\n\t@Override\n\tprotected @Nullable Object getPreAuthenticatedCredentials(HttpServletRequest request) {\n\t\tif (this.credentialsRequestHeader != null) {\n\t\t\treturn request.getHeader(this.credentialsRequestHeader);\n\t\t}\n\t\treturn \"N/A\";\n\t}\n\n\tpublic void setPrincipalRequestHeader(String principalRequestHeader) {\n\t\tAssert.hasText(principalRequestHeader, \"principalRequestHeader must not be empty or null\");\n\t\tthis.principalRequestHeader = principalRequestHeader;\n\t}\n\n\tpublic void setCredentialsRequestHeader(String credentialsRequestHeader) {\n\t\tAssert.hasText(credentialsRequestHeader, \"credentialsRequestHeader must not be empty or null\");\n\t\tthis.credentialsRequestHeader = credentialsRequestHeader;\n\t}\n\n\t/**\n\t * Defines whether an exception should be raised if the principal header is missing.\n\t * Defaults to {@code true}.\n\t * @param exceptionIfHeaderMissing set to {@code false} to override the default\n\t * behaviour and allow the request to proceed if no header is found.\n\t */\n\tpublic void setExceptionIfHeaderMissing(boolean exceptionIfHeaderMissing) {\n\t\tthis.exceptionIfHeaderMissing = exceptionIfHeaderMissing;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.j2ee;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.Attributes2GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.MappableAttributesRetriever;\nimport org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of AuthenticationDetailsSource which converts the user's J2EE roles (as\n * obtained by calling {@link HttpServletRequest#isUserInRole(String)}) into\n * {@code GrantedAuthority}s and stores these in the authentication details object.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource implements\n\t\tAuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails>,\n\t\tInitializingBean {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\t/**\n\t * The role attributes returned by the configured {@code MappableAttributesRetriever}\n\t */\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprotected Set<String> j2eeMappableRoles;\n\n\tprotected Attributes2GrantedAuthoritiesMapper j2eeUserRoles2GrantedAuthoritiesMapper = new SimpleAttributes2GrantedAuthoritiesMapper();\n\n\t/**\n\t * Check that all required properties have been set.\n\t */\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.j2eeMappableRoles, \"No mappable roles available\");\n\t\tAssert.notNull(this.j2eeUserRoles2GrantedAuthoritiesMapper, \"Roles to granted authorities mapper not set\");\n\t}\n\n\t/**\n\t * Obtains the list of user roles based on the current user's JEE roles. The\n\t * {@link jakarta.servlet.http.HttpServletRequest#isUserInRole(String)} method is\n\t * called for each of the values in the {@code j2eeMappableRoles} set to determine if\n\t * that role should be assigned to the user.\n\t * @param request the request which should be used to extract the user's roles.\n\t * @return The subset of {@code j2eeMappableRoles} which applies to the current user\n\t * making the request.\n\t */\n\tprotected Collection<String> getUserRoles(HttpServletRequest request) {\n\t\tArrayList<String> j2eeUserRolesList = new ArrayList<>();\n\t\tfor (String role : this.j2eeMappableRoles) {\n\t\t\tif (request.isUserInRole(role)) {\n\t\t\t\tj2eeUserRolesList.add(role);\n\t\t\t}\n\t\t}\n\t\treturn j2eeUserRolesList;\n\t}\n\n\t/**\n\t * Builds the authentication details object.\n\t *\n\t * @see org.springframework.security.authentication.AuthenticationDetailsSource#buildDetails(Object)\n\t */\n\t@Override\n\tpublic PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails buildDetails(HttpServletRequest context) {\n\t\tCollection<String> j2eeUserRoles = getUserRoles(context);\n\t\tCollection<? extends GrantedAuthority> userGrantedAuthorities = this.j2eeUserRoles2GrantedAuthoritiesMapper\n\t\t\t.getGrantedAuthorities(j2eeUserRoles);\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"J2EE roles [%s] mapped to Granted Authorities: [%s]\", j2eeUserRoles,\n\t\t\t\t\tuserGrantedAuthorities));\n\t\t}\n\t\treturn new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(context, userGrantedAuthorities);\n\t}\n\n\t/**\n\t * @param aJ2eeMappableRolesRetriever The MappableAttributesRetriever to use\n\t */\n\tpublic void setMappableRolesRetriever(MappableAttributesRetriever aJ2eeMappableRolesRetriever) {\n\t\tthis.j2eeMappableRoles = Collections.unmodifiableSet(aJ2eeMappableRolesRetriever.getMappableAttributes());\n\t}\n\n\t/**\n\t * @param mapper The Attributes2GrantedAuthoritiesMapper to use\n\t */\n\tpublic void setUserRoles2GrantedAuthoritiesMapper(Attributes2GrantedAuthoritiesMapper mapper) {\n\t\tthis.j2eeUserRoles2GrantedAuthoritiesMapper = mapper;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.j2ee;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\n\n/**\n * This AbstractPreAuthenticatedProcessingFilter implementation is based on the J2EE\n * container-based authentication mechanism. It will use the J2EE user principal name as\n * the pre-authenticated principal.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic class J2eePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {\n\n\t/**\n\t * Return the J2EE user name.\n\t */\n\t@Override\n\tprotected @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {\n\t\tObject principal = (httpRequest.getUserPrincipal() != null) ? httpRequest.getUserPrincipal().getName() : null;\n\t\tthis.logger.debug(LogMessage.format(\"PreAuthenticated J2EE principal: %s\", principal));\n\t\treturn principal;\n\t}\n\n\t/**\n\t * For J2EE container-based authentication there is no generic way to retrieve the\n\t * credentials, as such this method returns a fixed dummy value.\n\t */\n\t@Override\n\tprotected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {\n\t\treturn \"N/A\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlMappableAttributesRetriever.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.j2ee;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.StringReader;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.FactoryConfigurationError;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.EntityResolver;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ResourceLoaderAware;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.security.core.authority.mapping.MappableAttributesRetriever;\nimport org.springframework.util.Assert;\n\n/**\n * This <tt>MappableAttributesRetriever</tt> implementation reads the list of defined J2EE\n * roles from a <tt>web.xml</tt> file and returns these from {\n * {@link #getMappableAttributes()}.\n *\n * @author Ruud Senden\n * @author Luke Taylor\n * @since 2.0\n */\npublic class WebXmlMappableAttributesRetriever\n\t\timplements ResourceLoaderAware, MappableAttributesRetriever, InitializingBean {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate @Nullable ResourceLoader resourceLoader;\n\n\tprivate Set<String> mappableAttributes = new HashSet<>();\n\n\t@Override\n\tpublic void setResourceLoader(ResourceLoader resourceLoader) {\n\t\tthis.resourceLoader = resourceLoader;\n\t}\n\n\t@Override\n\tpublic Set<String> getMappableAttributes() {\n\t\treturn this.mappableAttributes;\n\t}\n\n\t/**\n\t * Loads the web.xml file using the configured <tt>ResourceLoader</tt> and parses the\n\t * role-name elements from it, using these as the set of <tt>mappableAttributes</tt>.\n\t */\n\n\t@Override\n\tpublic void afterPropertiesSet() throws Exception {\n\t\tAssert.notNull(this.resourceLoader, \"resourceLoader cannot be null\");\n\t\tResource webXml = this.resourceLoader.getResource(\"/WEB-INF/web.xml\");\n\t\tDocument doc = getDocument(webXml.getInputStream());\n\t\tNodeList webApp = doc.getElementsByTagName(\"web-app\");\n\t\tAssert.isTrue(webApp.getLength() == 1, () -> \"Failed to find 'web-app' element in resource\" + webXml);\n\t\tNodeList securityRoles = ((Element) webApp.item(0)).getElementsByTagName(\"security-role\");\n\t\tList<String> roleNames = getRoleNames(webXml, securityRoles);\n\t\tthis.mappableAttributes = Collections.unmodifiableSet(new HashSet<>(roleNames));\n\t}\n\n\tprivate List<String> getRoleNames(Resource webXml, NodeList securityRoles) {\n\t\tArrayList<String> roleNames = new ArrayList<>();\n\t\tfor (int i = 0; i < securityRoles.getLength(); i++) {\n\t\t\tElement securityRoleElement = (Element) securityRoles.item(i);\n\t\t\tNodeList roles = securityRoleElement.getElementsByTagName(\"role-name\");\n\t\t\tif (roles.getLength() > 0) {\n\t\t\t\tString roleName = roles.item(0).getTextContent().trim();\n\t\t\t\troleNames.add(roleName);\n\t\t\t\tthis.logger.info(\"Retrieved role-name '\" + roleName + \"' from web.xml\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.logger.info(\"No security-role elements found in \" + webXml);\n\t\t\t}\n\t\t}\n\t\treturn roleNames;\n\t}\n\n\t/**\n\t * @return Document for the specified InputStream\n\t */\n\tprivate Document getDocument(InputStream aStream) {\n\t\ttry {\n\t\t\tDocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\n\t\t\tfactory.setValidating(false);\n\t\t\tDocumentBuilder builder = factory.newDocumentBuilder();\n\t\t\tbuilder.setEntityResolver(new MyEntityResolver());\n\t\t\treturn builder.parse(aStream);\n\t\t}\n\t\tcatch (FactoryConfigurationError | IOException | SAXException | ParserConfigurationException ex) {\n\t\t\tthrow new RuntimeException(\"Unable to parse document object\", ex);\n\t\t}\n\t\tfinally {\n\t\t\ttry {\n\t\t\t\taStream.close();\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tthis.logger.warn(\"Failed to close input stream for web.xml\", ex);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * We do not need to resolve external entities, so just return an empty String.\n\t */\n\tprivate static final class MyEntityResolver implements EntityResolver {\n\n\t\t@Override\n\t\tpublic InputSource resolveEntity(String publicId, String systemId) {\n\t\t\treturn new InputSource(new StringReader(\"\"));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Pre-authentication support for container-authenticated requests.\n * <p>\n * It is assumed that standard JEE security has been configured and Spring Security hooks\n * into the security methods exposed by {@code HttpServletRequest} to build\n * {@code Authentication} object for the user.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.preauth.j2ee;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for \"pre-authenticated\" scenarios, where Spring Security assumes the incoming\n * request has already been authenticated by some externally configured system.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.preauth;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/DefaultWASUsernameAndGroupsExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.websphere;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport javax.naming.Context;\nimport javax.naming.InitialContext;\nimport javax.naming.NamingException;\nimport javax.security.auth.Subject;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\n\n/**\n * WebSphere Security helper class to allow retrieval of the current username and groups.\n * <p>\n * See Spring Security Jira SEC-477.\n *\n * @author Ruud Senden\n * @author Stephane Manciot\n * @since 2.0\n */\nfinal class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroupsExtractor {\n\n\tprivate static final Log logger = LogFactory.getLog(DefaultWASUsernameAndGroupsExtractor.class);\n\n\tprivate static final String PORTABLE_REMOTE_OBJECT_CLASSNAME = \"javax.rmi.PortableRemoteObject\";\n\n\tprivate static final String USER_REGISTRY = \"UserRegistry\";\n\n\tprivate static @Nullable Method getRunAsSubject = null;\n\n\tprivate static @Nullable Method getGroupsForUser = null;\n\n\tprivate static @Nullable Method getSecurityName = null;\n\n\tprivate static @Nullable Method narrow = null;\n\n\t// SEC-803\n\tprivate static @Nullable Class<?> wsCredentialClass = null;\n\n\t@Override\n\tpublic List<String> getGroupsForCurrentUser() {\n\t\treturn getWebSphereGroups(getRunAsSubject());\n\t}\n\n\t@Override\n\tpublic @Nullable String getCurrentUserName() {\n\t\treturn getSecurityName(getRunAsSubject());\n\t}\n\n\t/**\n\t * Get the security name for the given subject.\n\t * @param subject The subject for which to retrieve the security name\n\t * @return String the security name for the given subject\n\t */\n\tprivate static @Nullable String getSecurityName(final Subject subject) {\n\t\tlogger.debug(LogMessage.format(\"Determining Websphere security name for subject %s\", subject));\n\t\tString userSecurityName = null;\n\t\tif (subject != null) {\n\t\t\t// SEC-803\n\t\t\tObject credential = subject.getPublicCredentials(getWSCredentialClass()).iterator().next();\n\t\t\tif (credential != null) {\n\t\t\t\tuserSecurityName = (String) invokeMethod(getSecurityNameMethod(), credential);\n\t\t\t}\n\t\t}\n\t\tlogger.debug(LogMessage.format(\"Websphere security name is %s for subject %s\", subject, userSecurityName));\n\t\treturn userSecurityName;\n\t}\n\n\t/**\n\t * Get the current RunAs subject.\n\t * @return Subject the current RunAs subject\n\t */\n\tprivate static Subject getRunAsSubject() {\n\t\tlogger.debug(\"Retrieving WebSphere RunAs subject\");\n\t\t// get Subject: WSSubject.getCallerSubject ();\n\t\treturn (Subject) invokeMethod(getRunAsSubjectMethod(), null, new Object[] {});\n\t}\n\n\t/**\n\t * Get the WebSphere group names for the given subject.\n\t * @param subject The subject for which to retrieve the WebSphere group names\n\t * @return the WebSphere group names for the given subject\n\t */\n\tprivate static List<String> getWebSphereGroups(final Subject subject) {\n\t\treturn getWebSphereGroups(getSecurityName(subject));\n\t}\n\n\t/**\n\t * Get the WebSphere group names for the given security name.\n\t * @param securityName The security name for which to retrieve the WebSphere group\n\t * names\n\t * @return the WebSphere group names for the given security name\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static List<String> getWebSphereGroups(final @Nullable String securityName) {\n\t\tContext context = null;\n\t\ttry {\n\t\t\t// TODO: Cache UserRegistry object\n\t\t\tcontext = new InitialContext();\n\t\t\tObject objRef = context.lookup(USER_REGISTRY);\n\t\t\tObject userReg = invokeMethod(getNarrowMethod(), null, objRef,\n\t\t\t\t\tClass.forName(\"com.ibm.websphere.security.UserRegistry\"));\n\t\t\tlogger.debug(LogMessage.format(\"Determining WebSphere groups for user %s using WebSphere UserRegistry %s\",\n\t\t\t\t\tsecurityName, userReg));\n\t\t\tfinal Collection<String> groups = (Collection<String>) invokeMethod(getGroupsForUserMethod(), userReg,\n\t\t\t\t\tnew Object[] { securityName });\n\t\t\tlogger.debug(LogMessage.format(\"Groups for user %s: %s\", securityName, groups));\n\t\t\treturn new ArrayList<>(groups);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.error(\"Exception occurred while looking up groups for user\", ex);\n\t\t\tthrow new RuntimeException(\"Exception occurred while looking up groups for user\", ex);\n\t\t}\n\t\tfinally {\n\t\t\tcloseContext(context);\n\t\t}\n\t}\n\n\tprivate static void closeContext(@Nullable Context context) {\n\t\ttry {\n\t\t\tif (context != null) {\n\t\t\t\tcontext.close();\n\t\t\t}\n\t\t}\n\t\tcatch (NamingException ex) {\n\t\t\tlogger.debug(\"Exception occurred while closing context\", ex);\n\t\t}\n\t}\n\n\tprivate static Object invokeMethod(Method method, @Nullable Object instance, Object... args) {\n\t\ttry {\n\t\t\treturn method.invoke(instance, args);\n\t\t}\n\t\tcatch (IllegalArgumentException | IllegalAccessException | InvocationTargetException ex) {\n\t\t\tString message = \"Error while invoking method \" + method.getClass().getName() + \".\" + method.getName() + \"(\"\n\t\t\t\t\t+ Arrays.asList(args) + \")\";\n\t\t\tlogger.error(message, ex);\n\t\t\tthrow new RuntimeException(message, ex);\n\t\t}\n\t}\n\n\tprivate static Method getMethod(String className, String methodName, String[] parameterTypeNames) {\n\t\ttry {\n\t\t\tClass<?> c = Class.forName(className);\n\t\t\tint len = parameterTypeNames.length;\n\t\t\tClass<?>[] parameterTypes = new Class[len];\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tparameterTypes[i] = Class.forName(parameterTypeNames[i]);\n\t\t\t}\n\t\t\treturn c.getDeclaredMethod(methodName, parameterTypes);\n\t\t}\n\t\tcatch (ClassNotFoundException ex) {\n\t\t\tlogger.error(\"Required class\" + className + \" not found\");\n\t\t\tthrow new RuntimeException(\"Required class\" + className + \" not found\", ex);\n\t\t}\n\t\tcatch (NoSuchMethodException ex) {\n\t\t\tlogger.error(\"Required method \" + methodName + \" with parameter types (\" + Arrays.asList(parameterTypeNames)\n\t\t\t\t\t+ \") not found on class \" + className);\n\t\t\tthrow new RuntimeException(\"Required class\" + className + \" not found\", ex);\n\t\t}\n\t}\n\n\tprivate static Method getRunAsSubjectMethod() {\n\t\tif (getRunAsSubject == null) {\n\t\t\tgetRunAsSubject = getMethod(\"com.ibm.websphere.security.auth.WSSubject\", \"getRunAsSubject\",\n\t\t\t\t\tnew String[] {});\n\t\t}\n\t\treturn getRunAsSubject;\n\t}\n\n\tprivate static Method getGroupsForUserMethod() {\n\t\tif (getGroupsForUser == null) {\n\t\t\tgetGroupsForUser = getMethod(\"com.ibm.websphere.security.UserRegistry\", \"getGroupsForUser\",\n\t\t\t\t\tnew String[] { \"java.lang.String\" });\n\t\t}\n\t\treturn getGroupsForUser;\n\t}\n\n\tprivate static Method getSecurityNameMethod() {\n\t\tif (getSecurityName == null) {\n\t\t\tgetSecurityName = getMethod(\"com.ibm.websphere.security.cred.WSCredential\", \"getSecurityName\",\n\t\t\t\t\tnew String[] {});\n\t\t}\n\t\treturn getSecurityName;\n\t}\n\n\tprivate static Method getNarrowMethod() {\n\t\tif (narrow == null) {\n\t\t\tnarrow = getMethod(PORTABLE_REMOTE_OBJECT_CLASSNAME, \"narrow\",\n\t\t\t\t\tnew String[] { Object.class.getName(), Class.class.getName() });\n\t\t}\n\t\treturn narrow;\n\t}\n\n\t// SEC-803\n\tprivate static Class<?> getWSCredentialClass() {\n\t\tif (wsCredentialClass == null) {\n\t\t\twsCredentialClass = getClass(\"com.ibm.websphere.security.cred.WSCredential\");\n\t\t}\n\t\treturn wsCredentialClass;\n\t}\n\n\tprivate static Class<?> getClass(String className) {\n\t\ttry {\n\t\t\treturn Class.forName(className);\n\t\t}\n\t\tcatch (ClassNotFoundException ex) {\n\t\t\tlogger.error(\"Required class \" + className + \" not found\");\n\t\t\tthrow new RuntimeException(\"Required class \" + className + \" not found\", ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASUsernameAndGroupsExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.websphere;\n\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Provides indirection between classes using websphere and the actual container\n * interaction, allowing for easier unit testing.\n * <p>\n * Only for internal use.\n *\n * @author Luke Taylor\n * @since 3.0.0\n */\ninterface WASUsernameAndGroupsExtractor {\n\n\tList<String> getGroupsForCurrentUser();\n\n\t@Nullable String getCurrentUserName();\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedProcessingFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.websphere;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\n\n/**\n * This AbstractPreAuthenticatedProcessingFilter implementation is based on WebSphere\n * authentication. It will use the WebSphere RunAs user principal name as the\n * pre-authenticated principal.\n *\n * @author Ruud Senden\n * @since 2.0\n */\npublic class WebSpherePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {\n\n\tprivate final WASUsernameAndGroupsExtractor wasHelper;\n\n\t/**\n\t * Public constructor which overrides the default AuthenticationDetails class to be\n\t * used.\n\t */\n\tpublic WebSpherePreAuthenticatedProcessingFilter() {\n\t\tthis(new DefaultWASUsernameAndGroupsExtractor());\n\t}\n\n\tWebSpherePreAuthenticatedProcessingFilter(WASUsernameAndGroupsExtractor wasHelper) {\n\t\tthis.wasHelper = wasHelper;\n\t\tsetAuthenticationDetailsSource(new WebSpherePreAuthenticatedWebAuthenticationDetailsSource());\n\t}\n\n\t/**\n\t * Return the WebSphere user name.\n\t */\n\t@Override\n\tprotected @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {\n\t\tObject principal = this.wasHelper.getCurrentUserName();\n\t\tthis.logger.debug(LogMessage.format(\"PreAuthenticated WebSphere principal: %s\", principal));\n\t\treturn principal;\n\t}\n\n\t/**\n\t * For J2EE container-based authentication there is no generic way to retrieve the\n\t * credentials, as such this method returns a fixed dummy value.\n\t */\n\t@Override\n\tprotected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {\n\t\treturn \"N/A\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedWebAuthenticationDetailsSource.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.websphere;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.Attributes2GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;\n\n/**\n * This AuthenticationDetailsSource implementation will set the pre-authenticated granted\n * authorities based on the WebSphere groups for the current WebSphere user, mapped using\n * the configured Attributes2GrantedAuthoritiesMapper.\n *\n * @author Ruud Senden\n */\npublic class WebSpherePreAuthenticatedWebAuthenticationDetailsSource implements\n\t\tAuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate Attributes2GrantedAuthoritiesMapper webSphereGroups2GrantedAuthoritiesMapper = new SimpleAttributes2GrantedAuthoritiesMapper();\n\n\tprivate final WASUsernameAndGroupsExtractor wasHelper;\n\n\tpublic WebSpherePreAuthenticatedWebAuthenticationDetailsSource() {\n\t\tthis(new DefaultWASUsernameAndGroupsExtractor());\n\t}\n\n\tpublic WebSpherePreAuthenticatedWebAuthenticationDetailsSource(WASUsernameAndGroupsExtractor wasHelper) {\n\t\tthis.wasHelper = wasHelper;\n\t}\n\n\t@Override\n\tpublic PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails buildDetails(HttpServletRequest context) {\n\t\treturn new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(context,\n\t\t\t\tgetWebSphereGroupsBasedGrantedAuthorities());\n\t}\n\n\t/**\n\t * Get a list of Granted Authorities based on the current user's WebSphere groups.\n\t * @return authorities mapped from the user's WebSphere groups.\n\t */\n\tprivate Collection<? extends GrantedAuthority> getWebSphereGroupsBasedGrantedAuthorities() {\n\t\tList<String> webSphereGroups = this.wasHelper.getGroupsForCurrentUser();\n\t\tCollection<? extends GrantedAuthority> userGas = this.webSphereGroups2GrantedAuthoritiesMapper\n\t\t\t.getGrantedAuthorities(webSphereGroups);\n\t\tthis.logger.debug(\n\t\t\t\tLogMessage.format(\"WebSphere groups: %s mapped to Granted Authorities: %s\", webSphereGroups, userGas));\n\t\treturn userGas;\n\t}\n\n\t/**\n\t * @param mapper The Attributes2GrantedAuthoritiesMapper to use for converting the WAS\n\t * groups to authorities\n\t */\n\tpublic void setWebSphereGroups2GrantedAuthoritiesMapper(Attributes2GrantedAuthoritiesMapper mapper) {\n\t\tthis.webSphereGroups2GrantedAuthoritiesMapper = mapper;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Websphere-specific pre-authentication classes.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.preauth.websphere;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.x509;\n\nimport java.security.cert.X509Certificate;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.util.Assert;\n\n/**\n * Obtains the principal from a certificate using a regular expression match against the\n * Subject (as returned by a call to {@link X509Certificate#getSubjectDN()}).\n * <p>\n * The regular expression should contain a single group; for example the default\n * expression \"CN=(.*?)(?:,|$)\" matches the common name field. So \"CN=Jimi Hendrix,\n * OU=...\" will give a user name of \"Jimi Hendrix\".\n * <p>\n * The matches are case insensitive. So \"emailAddress=(.*?),\" will match\n * \"EMAILADDRESS=jimi@hendrix.org, CN=...\" giving a user name \"jimi@hendrix.org\"\n *\n * @author Luke Taylor\n * @deprecated Please use {@link SubjectX500PrincipalExtractor} instead\n */\n@Deprecated\npublic class SubjectDnX509PrincipalExtractor implements X509PrincipalExtractor, MessageSourceAware {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate Pattern subjectDnPattern;\n\n\t@SuppressWarnings(\"NullAway\") // Dataflow analysis limitation\n\tpublic SubjectDnX509PrincipalExtractor() {\n\t\tsetSubjectDnRegex(\"CN=(.*?)(?:,|$)\");\n\t}\n\n\t@Override\n\tpublic Object extractPrincipal(X509Certificate clientCert) {\n\t\t// String subjectDN = clientCert.getSubjectX500Principal().getName();\n\t\tString subjectDN = clientCert.getSubjectDN().getName();\n\t\tthis.logger.debug(LogMessage.format(\"Subject DN is '%s'\", subjectDN));\n\t\tMatcher matcher = this.subjectDnPattern.matcher(subjectDN);\n\t\tif (!matcher.find()) {\n\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"SubjectDnX509PrincipalExtractor.noMatching\",\n\t\t\t\t\tnew Object[] { subjectDN }, \"No matching pattern was found in subject DN: {0}\"));\n\t\t}\n\t\tAssert.isTrue(matcher.groupCount() == 1, \"Regular expression must contain a single group \");\n\t\tString username = matcher.group(1);\n\t\tthis.logger.debug(LogMessage.format(\"Extracted Principal name is '%s'\", username));\n\t\treturn username;\n\t}\n\n\t/**\n\t * Sets the regular expression which will by used to extract the user name from the\n\t * certificate's Subject DN.\n\t * <p>\n\t * It should contain a single group; for example the default expression\n\t * \"CN=(.*?)(?:,|$)\" matches the common name field. So \"CN=Jimi Hendrix, OU=...\" will\n\t * give a user name of \"Jimi Hendrix\".\n\t * <p>\n\t * The matches are case insensitive. So \"emailAddress=(.?),\" will match\n\t * \"EMAILADDRESS=jimi@hendrix.org, CN=...\" giving a user name \"jimi@hendrix.org\"\n\t * @param subjectDnRegex the regular expression to find in the subject\n\t */\n\tpublic void setSubjectDnRegex(String subjectDnRegex) {\n\t\tAssert.hasText(subjectDnRegex, \"Regular expression may not be null or empty\");\n\t\tthis.subjectDnPattern = Pattern.compile(subjectDnRegex, Pattern.CASE_INSENSITIVE);\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/x509/SubjectX500PrincipalExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.x509;\n\nimport java.security.cert.X509Certificate;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.security.auth.x500.X500Principal;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.util.Assert;\n\n/**\n * Extracts the principal from the {@link X500Principal#getName(String)} returned by\n * {@link X509Certificate#getSubjectX500Principal()} passed into\n * {@link #extractPrincipal(X509Certificate)} depending on the value of\n * {@link #setExtractPrincipalNameFromEmail(boolean)}.\n *\n * @author Max Batischev\n * @author Rob Winch\n * @since 7.0\n */\npublic final class SubjectX500PrincipalExtractor implements X509PrincipalExtractor, MessageSourceAware {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate static final Pattern EMAIL_SUBJECT_DN_PATTERN = Pattern.compile(\"OID.1.2.840.113549.1.9.1=(.*?)(?:,|$)\",\n\t\t\tPattern.CASE_INSENSITIVE);\n\n\tprivate static final Pattern CN_SUBJECT_DN_PATTERN = Pattern.compile(\"CN=(.*?)(?:,|$)\", Pattern.CASE_INSENSITIVE);\n\n\tprivate MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate Pattern subjectDnPattern = CN_SUBJECT_DN_PATTERN;\n\n\tprivate String x500PrincipalFormat = X500Principal.RFC2253;\n\n\t@Override\n\tpublic Object extractPrincipal(X509Certificate clientCert) {\n\t\tAssert.notNull(clientCert, \"clientCert cannot be null\");\n\t\tX500Principal principal = clientCert.getSubjectX500Principal();\n\t\tString subjectDN = principal.getName(this.x500PrincipalFormat);\n\t\tthis.logger.debug(LogMessage.format(\"Subject DN is '%s'\", subjectDN));\n\t\tMatcher matcher = this.subjectDnPattern.matcher(subjectDN);\n\t\tif (!matcher.find()) {\n\t\t\tthrow new BadCredentialsException(this.messages.getMessage(\"SubjectX500PrincipalExtractor.noMatching\",\n\t\t\t\t\tnew Object[] { subjectDN }, \"No matching pattern was found in subject DN: {0}\"));\n\t\t}\n\t\tString principalName = matcher.group(1);\n\t\tthis.logger.debug(LogMessage.format(\"Extracted Principal name is '%s'\", principalName));\n\t\treturn principalName;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t/**\n\t * Sets if the principal name should be extracted from the emailAddress or CN\n\t * attribute (default).\n\t *\n\t * By default, the format {@link X500Principal#RFC2253} is passed to\n\t * {@link X500Principal#getName(String)} and the principal is extracted from the CN\n\t * attribute as defined in\n\t * <a href=\"https://datatracker.ietf.org/doc/html/rfc2253#section-2.3\">Converting\n\t * AttributeTypeAndValue of RFC2253</a>.\n\t *\n\t * If {@link #setExtractPrincipalNameFromEmail(boolean)} is {@code true}, then the\n\t * format {@link X500Principal#RFC2253} is passed to\n\t * {@link X500Principal#getName(String)} and the principal is extracted from the\n\t * <a href=\"https://oid-base.com/get/1.2.840.113549.1.9.1\">OID.1.2.840.113549.1.9.1\n\t * (emailAddress)</a> attribute as defined in\n\t * <a href=\"https://datatracker.ietf.org/doc/html/rfc1779#section-2.3\">Section 2.3 of\n\t * RFC1779</a>.\n\t * @param extractPrincipalNameFromEmail whether to extract the principal from the\n\t * emailAddress (default false)\n\t * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc2253\">RFC2253</a>\n\t * @see <a href=\"https://datatracker.ietf.org/doc/html/rfC1779\">RFC1779</a>\n\t */\n\tpublic void setExtractPrincipalNameFromEmail(boolean extractPrincipalNameFromEmail) {\n\t\tif (extractPrincipalNameFromEmail) {\n\t\t\tthis.subjectDnPattern = EMAIL_SUBJECT_DN_PATTERN;\n\t\t\tthis.x500PrincipalFormat = X500Principal.RFC1779;\n\t\t}\n\t\telse {\n\t\t\tthis.subjectDnPattern = CN_SUBJECT_DN_PATTERN;\n\t\t\tthis.x500PrincipalFormat = X500Principal.RFC2253;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/x509/X509AuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.x509;\n\nimport java.security.cert.X509Certificate;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;\n\n/**\n * @author Luke Taylor\n */\npublic class X509AuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {\n\n\tprivate X509PrincipalExtractor principalExtractor = new SubjectX500PrincipalExtractor();\n\n\t@Override\n\tprotected @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest request) {\n\t\tX509Certificate cert = extractClientCertificate(request);\n\t\treturn (cert != null) ? this.principalExtractor.extractPrincipal(cert) : null;\n\t}\n\n\t@Override\n\tprotected @Nullable Object getPreAuthenticatedCredentials(HttpServletRequest request) {\n\t\treturn extractClientCertificate(request);\n\t}\n\n\tprivate @Nullable X509Certificate extractClientCertificate(HttpServletRequest request) {\n\t\tX509Certificate[] certs = (X509Certificate[]) request.getAttribute(\"jakarta.servlet.request.X509Certificate\");\n\t\tif (certs != null && certs.length > 0) {\n\t\t\tthis.logger.debug(LogMessage.format(\"X.509 client authentication certificate:%s\", certs[0]));\n\t\t\treturn certs[0];\n\t\t}\n\t\tthis.logger.debug(\"No client certificate found in request.\");\n\t\treturn null;\n\t}\n\n\tpublic void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) {\n\t\tthis.principalExtractor = principalExtractor;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/x509/X509PrincipalExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.x509;\n\nimport java.security.cert.X509Certificate;\n\n/**\n * Obtains the principal from an X509Certificate for use within the framework.\n *\n * @author Luke Taylor\n */\n@FunctionalInterface\npublic interface X509PrincipalExtractor {\n\n\t/**\n\t * Returns the principal (usually a String) for the given certificate.\n\t */\n\tObject extractPrincipal(X509Certificate cert);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/preauth/x509/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * X.509 client certificate authentication support. Hooks into the certificate exposed by\n * the servlet container through the {@code jakarta.servlet.request.X509Certificate}\n * property.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.preauth.x509;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AccountStatusException;\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Base class for RememberMeServices implementations.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @author Eddú Meléndez\n * @author Onur Kagan Ozcan\n * @author Ngoc Nhan\n * @since 2.0\n */\npublic abstract class AbstractRememberMeServices\n\t\timplements RememberMeServices, InitializingBean, LogoutHandler, MessageSourceAware {\n\n\tpublic static final String SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY = \"remember-me\";\n\n\tpublic static final String DEFAULT_PARAMETER = \"remember-me\";\n\n\tpublic static final int TWO_WEEKS_S = 1209600;\n\n\tprivate static final String DELIMITER = \":\";\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate UserDetailsService userDetailsService;\n\n\tprivate UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate String cookieName = SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY;\n\n\tprivate @Nullable String cookieDomain;\n\n\tprivate String parameter = DEFAULT_PARAMETER;\n\n\tprivate boolean alwaysRemember;\n\n\tprivate String key;\n\n\tprivate int tokenValiditySeconds = TWO_WEEKS_S;\n\n\tprivate @Nullable Boolean useSecureCookie = null;\n\n\tprivate GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();\n\n\tprivate Consumer<Cookie> cookieCustomizer = (cookie) -> {\n\t};\n\n\tprotected AbstractRememberMeServices(String key, UserDetailsService userDetailsService) {\n\t\tAssert.hasLength(key, \"key cannot be empty or null\");\n\t\tAssert.notNull(userDetailsService, \"UserDetailsService cannot be null\");\n\t\tthis.key = key;\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasLength(this.key, \"key cannot be empty or null\");\n\t\tAssert.notNull(this.userDetailsService, \"A UserDetailsService is required\");\n\t}\n\n\t/**\n\t * Template implementation which locates the Spring Security cookie, decodes it into a\n\t * delimited array of tokens and submits it to subclasses for processing via the\n\t * <tt>processAutoLoginCookie</tt> method.\n\t * <p>\n\t * The returned username is then used to load the UserDetails object for the user,\n\t * which in turn is used to create a valid authentication token.\n\t */\n\t@Override\n\tpublic @Nullable Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {\n\t\tString rememberMeCookie = extractRememberMeCookie(request);\n\t\tif (rememberMeCookie == null) {\n\t\t\treturn null;\n\t\t}\n\t\tthis.logger.debug(\"Remember-me cookie detected\");\n\t\tif (rememberMeCookie.isEmpty()) {\n\t\t\tthis.logger.debug(\"Cookie was empty\");\n\t\t\tcancelCookie(request, response);\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tString[] cookieTokens = decodeCookie(rememberMeCookie);\n\t\t\tUserDetails user = processAutoLoginCookie(cookieTokens, request, response);\n\t\t\tthis.userDetailsChecker.check(user);\n\t\t\tthis.logger.debug(\"Remember-me cookie accepted\");\n\t\t\treturn createSuccessfulAuthentication(request, user);\n\t\t}\n\t\tcatch (CookieTheftException ex) {\n\t\t\tcancelCookie(request, response);\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (UsernameNotFoundException ex) {\n\t\t\tthis.logger.debug(\"Remember-me login was valid but corresponding user not found.\", ex);\n\t\t}\n\t\tcatch (InvalidCookieException ex) {\n\t\t\tthis.logger.debug(\"Invalid remember-me cookie: \" + ex.getMessage());\n\t\t}\n\t\tcatch (AccountStatusException ex) {\n\t\t\tthis.logger.debug(\"Invalid UserDetails: \" + ex.getMessage());\n\t\t}\n\t\tcatch (RememberMeAuthenticationException ex) {\n\t\t\tthis.logger.debug(ex.getMessage());\n\t\t}\n\t\tcancelCookie(request, response);\n\t\treturn null;\n\t}\n\n\t/**\n\t * Locates the Spring Security remember me cookie in the request and returns its\n\t * value. The cookie is searched for by name and also by matching the context path to\n\t * the cookie path.\n\t * @param request the submitted request which is to be authenticated\n\t * @return the cookie value (if present), null otherwise.\n\t */\n\tprotected @Nullable String extractRememberMeCookie(HttpServletRequest request) {\n\t\tCookie[] cookies = request.getCookies();\n\t\tif (cookies == null) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (Cookie cookie : cookies) {\n\t\t\tif (this.cookieName.equals(cookie.getName())) {\n\t\t\t\treturn cookie.getValue();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Creates the final <tt>Authentication</tt> object returned from the\n\t * <tt>autoLogin</tt> method.\n\t * <p>\n\t * By default it will create a <tt>RememberMeAuthenticationToken</tt> instance.\n\t * @param request the original request. The configured\n\t * <tt>AuthenticationDetailsSource</tt> will use this to build the details property of\n\t * the returned object.\n\t * @param user the <tt>UserDetails</tt> loaded from the <tt>UserDetailsService</tt>.\n\t * This will be stored as the principal.\n\t * @return the <tt>Authentication</tt> for the remember-me authenticated user\n\t */\n\tprotected Authentication createSuccessfulAuthentication(HttpServletRequest request, UserDetails user) {\n\t\tRememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, user,\n\t\t\t\tthis.authoritiesMapper.mapAuthorities(user.getAuthorities()));\n\t\tauth.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\treturn auth;\n\t}\n\n\t/**\n\t * Decodes the cookie and splits it into a set of token strings using the \":\"\n\t * delimiter.\n\t * @param cookieValue the value obtained from the submitted cookie\n\t * @return the array of tokens.\n\t * @throws InvalidCookieException if the cookie was not base64 encoded.\n\t */\n\tprotected String[] decodeCookie(String cookieValue) throws InvalidCookieException {\n\t\tfor (int j = 0; j < cookieValue.length() % 4; j++) {\n\t\t\tcookieValue = cookieValue + \"=\";\n\t\t}\n\t\tString cookieAsPlainText;\n\t\ttry {\n\t\t\tcookieAsPlainText = new String(Base64.getDecoder().decode(cookieValue.getBytes()));\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\tthrow new InvalidCookieException(\"Cookie token was not Base64 encoded; value was '\" + cookieValue + \"'\");\n\t\t}\n\t\tString[] tokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, DELIMITER);\n\t\tfor (int i = 0; i < tokens.length; i++) {\n\t\t\ttokens[i] = URLDecoder.decode(tokens[i], StandardCharsets.UTF_8);\n\t\t}\n\t\treturn tokens;\n\t}\n\n\t/**\n\t * Inverse operation of decodeCookie.\n\t * @param cookieTokens the tokens to be encoded.\n\t * @return base64 encoding of the tokens concatenated with the \":\" delimiter.\n\t */\n\tprotected String encodeCookie(String[] cookieTokens) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 0; i < cookieTokens.length; i++) {\n\t\t\tsb.append(URLEncoder.encode(cookieTokens[i], StandardCharsets.UTF_8));\n\t\t\tif (i < cookieTokens.length - 1) {\n\t\t\t\tsb.append(DELIMITER);\n\t\t\t}\n\t\t}\n\t\tString value = sb.toString();\n\t\tsb = new StringBuilder(new String(Base64.getEncoder().encode(value.getBytes())));\n\t\twhile (sb.charAt(sb.length() - 1) == '=') {\n\t\t\tsb.deleteCharAt(sb.length() - 1);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic void loginFail(HttpServletRequest request, HttpServletResponse response) {\n\t\tthis.logger.debug(\"Interactive login attempt was unsuccessful.\");\n\t\tcancelCookie(request, response);\n\t\tonLoginFail(request, response);\n\t}\n\n\tprotected void onLoginFail(HttpServletRequest request, HttpServletResponse response) {\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t *\n\t * <p>\n\t * Examines the incoming request and checks for the presence of the configured\n\t * \"remember me\" parameter. If it's present, or if <tt>alwaysRemember</tt> is set to\n\t * true, calls <tt>onLoginSuccess</tt>.\n\t * </p>\n\t */\n\t@Override\n\tpublic void loginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication successfulAuthentication) {\n\t\tif (!rememberMeRequested(request, this.parameter)) {\n\t\t\tthis.logger.debug(\"Remember-me login not requested.\");\n\t\t\treturn;\n\t\t}\n\t\tonLoginSuccess(request, response, successfulAuthentication);\n\t}\n\n\t/**\n\t * Called from loginSuccess when a remember-me login has been requested. Typically\n\t * implemented by subclasses to set a remember-me cookie and potentially store a\n\t * record of it if the implementation requires this.\n\t */\n\tprotected abstract void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication successfulAuthentication);\n\n\t/**\n\t * Allows customization of whether a remember-me login has been requested. The default\n\t * is to return true if <tt>alwaysRemember</tt> is set or the configured parameter\n\t * name has been included in the request and is set to the value \"true\".\n\t * @param request the request submitted from an interactive login, which may include\n\t * additional information indicating that a persistent login is desired.\n\t * @param parameter the configured remember-me parameter name.\n\t * @return true if the request includes information indicating that a persistent login\n\t * has been requested.\n\t */\n\tprotected boolean rememberMeRequested(HttpServletRequest request, String parameter) {\n\t\tif (this.alwaysRemember) {\n\t\t\treturn true;\n\t\t}\n\t\tString paramValue = request.getParameter(parameter);\n\t\tif (paramValue != null) {\n\t\t\tif (paramValue.equalsIgnoreCase(\"true\") || paramValue.equalsIgnoreCase(\"on\")\n\t\t\t\t\t|| paramValue.equalsIgnoreCase(\"yes\") || paramValue.equals(\"1\")) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(\n\t\t\t\tLogMessage.format(\"Did not send remember-me cookie (principal did not set parameter '%s')\", parameter));\n\t\treturn false;\n\t}\n\n\t/**\n\t * Called from autoLogin to process the submitted persistent login cookie. Subclasses\n\t * should validate the cookie and perform any additional management required.\n\t * @param cookieTokens the decoded and tokenized cookie value\n\t * @param request the request\n\t * @param response the response, to allow the cookie to be modified if required.\n\t * @return the UserDetails for the corresponding user account if the cookie was\n\t * validated successfully.\n\t * @throws RememberMeAuthenticationException if the cookie is invalid or the login is\n\t * invalid for some other reason.\n\t * @throws UsernameNotFoundException if the user account corresponding to the login\n\t * cookie couldn't be found (for example if the user has been removed from the\n\t * system).\n\t */\n\tprotected abstract UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,\n\t\t\tHttpServletResponse response) throws RememberMeAuthenticationException, UsernameNotFoundException;\n\n\t/**\n\t * Sets a \"cancel cookie\" (with maxAge = 0) on the response to disable persistent\n\t * logins.\n\t */\n\tprotected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {\n\t\tthis.logger.debug(\"Cancelling cookie\");\n\t\tCookie cookie = new Cookie(this.cookieName, null);\n\t\tcookie.setMaxAge(0);\n\t\tcookie.setPath(getCookiePath(request));\n\t\tif (this.cookieDomain != null) {\n\t\t\tcookie.setDomain(this.cookieDomain);\n\t\t}\n\t\tcookie.setSecure((this.useSecureCookie != null) ? this.useSecureCookie : request.isSecure());\n\t\tresponse.addCookie(cookie);\n\t}\n\n\t/**\n\t * Sets the cookie on the response.\n\t *\n\t * By default a secure cookie will be used if the connection is secure. You can set\n\t * the {@code useSecureCookie} property to {@code false} to override this. If you set\n\t * it to {@code true}, the cookie will always be flagged as secure. By default the\n\t * cookie will be marked as HttpOnly.\n\t * @param tokens the tokens which will be encoded to make the cookie value.\n\t * @param maxAge the value passed to {@link Cookie#setMaxAge(int)}\n\t * @param request the request\n\t * @param response the response to add the cookie to.\n\t */\n\tprotected void setCookie(String[] tokens, int maxAge, HttpServletRequest request, HttpServletResponse response) {\n\t\tString cookieValue = encodeCookie(tokens);\n\t\tCookie cookie = new Cookie(this.cookieName, cookieValue);\n\t\tcookie.setMaxAge(maxAge);\n\t\tcookie.setPath(getCookiePath(request));\n\t\tif (this.cookieDomain != null) {\n\t\t\tcookie.setDomain(this.cookieDomain);\n\t\t}\n\t\tcookie.setSecure((this.useSecureCookie != null) ? this.useSecureCookie : request.isSecure());\n\t\tcookie.setHttpOnly(true);\n\n\t\tthis.cookieCustomizer.accept(cookie);\n\n\t\tresponse.addCookie(cookie);\n\t}\n\n\tprivate String getCookiePath(HttpServletRequest request) {\n\t\tString contextPath = request.getContextPath();\n\t\treturn contextPath.isEmpty() ? \"/\" : contextPath;\n\t}\n\n\t/**\n\t * Implementation of {@code LogoutHandler}. Default behaviour is to call\n\t * {@code cancelCookie()}.\n\t */\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tthis.logger.debug(LogMessage\n\t\t\t.of(() -> \"Logout of user \" + ((authentication != null) ? authentication.getName() : \"Unknown\")));\n\t\tcancelCookie(request, response);\n\t}\n\n\tpublic void setCookieName(String cookieName) {\n\t\tAssert.hasLength(cookieName, \"Cookie name cannot be empty or null\");\n\t\tthis.cookieName = cookieName;\n\t}\n\n\tpublic void setCookieDomain(String cookieDomain) {\n\t\tAssert.hasLength(cookieDomain, \"Cookie domain cannot be empty or null\");\n\t\tthis.cookieDomain = cookieDomain;\n\t}\n\n\tprotected String getCookieName() {\n\t\treturn this.cookieName;\n\t}\n\n\tpublic void setAlwaysRemember(boolean alwaysRemember) {\n\t\tthis.alwaysRemember = alwaysRemember;\n\t}\n\n\t/**\n\t * Sets the name of the parameter which should be checked for to see if a remember-me\n\t * has been requested during a login request. This should be the same name you assign\n\t * to the checkbox in your login form.\n\t * @param parameter the HTTP request parameter\n\t */\n\tpublic void setParameter(String parameter) {\n\t\tAssert.hasText(parameter, \"Parameter name cannot be empty or null\");\n\t\tthis.parameter = parameter;\n\t}\n\n\tpublic String getParameter() {\n\t\treturn this.parameter;\n\t}\n\n\tprotected UserDetailsService getUserDetailsService() {\n\t\treturn this.userDetailsService;\n\t}\n\n\tpublic String getKey() {\n\t\treturn this.key;\n\t}\n\n\tpublic void setTokenValiditySeconds(int tokenValiditySeconds) {\n\t\tthis.tokenValiditySeconds = tokenValiditySeconds;\n\t}\n\n\tprotected int getTokenValiditySeconds() {\n\t\treturn this.tokenValiditySeconds;\n\t}\n\n\t/**\n\t * Whether the cookie should be flagged as secure or not. Secure cookies can only be\n\t * sent over an HTTPS connection and thus cannot be accidentally submitted over HTTP\n\t * where they could be intercepted.\n\t * <p>\n\t * By default the cookie will be secure if the request is secure. If you only want to\n\t * use remember-me over HTTPS (recommended) you should set this property to\n\t * {@code true}.\n\t * @param useSecureCookie set to {@code true} to always user secure cookies,\n\t * {@code false} to disable their use.\n\t */\n\tpublic void setUseSecureCookie(boolean useSecureCookie) {\n\t\tthis.useSecureCookie = useSecureCookie;\n\t}\n\n\tprotected AuthenticationDetailsSource<HttpServletRequest, ?> getAuthenticationDetailsSource() {\n\t\treturn this.authenticationDetailsSource;\n\t}\n\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"AuthenticationDetailsSource cannot be null\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t/**\n\t * Sets the strategy to be used to validate the {@code UserDetails} object obtained\n\t * for the user when processing a remember-me cookie to automatically log in a user.\n\t * @param userDetailsChecker the strategy which will be passed the user object to\n\t * allow it to be rejected if account should not be allowed to authenticate (if it is\n\t * locked, for example). Defaults to a {@code AccountStatusUserDetailsChecker}\n\t * instance.\n\t *\n\t */\n\tpublic void setUserDetailsChecker(UserDetailsChecker userDetailsChecker) {\n\t\tthis.userDetailsChecker = userDetailsChecker;\n\t}\n\n\tpublic void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {\n\t\tthis.authoritiesMapper = authoritiesMapper;\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t/**\n\t * Sets the {@link Consumer}, allowing customization of cookie.\n\t * @param cookieCustomizer customize for cookie\n\t * @since 6.4\n\t */\n\tpublic void setCookieCustomizer(Consumer<Cookie> cookieCustomizer) {\n\t\tAssert.notNull(cookieCustomizer, \"cookieCustomizer cannot be null\");\n\t\tthis.cookieCustomizer = cookieCustomizer;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/CookieTheftException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.io.Serial;\n\n/**\n * @author Luke Taylor\n */\npublic class CookieTheftException extends RememberMeAuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7215039140728554850L;\n\n\tpublic CookieTheftException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/InMemoryTokenRepositoryImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.dao.DataIntegrityViolationException;\n\n/**\n * Simple <tt>PersistentTokenRepository</tt> implementation backed by a Map. Intended for\n * testing only.\n *\n * @author Luke Taylor\n */\npublic class InMemoryTokenRepositoryImpl implements PersistentTokenRepository {\n\n\tprivate final Map<String, PersistentRememberMeToken> seriesTokens = new HashMap<>();\n\n\t@Override\n\tpublic synchronized void createNewToken(PersistentRememberMeToken token) {\n\t\tPersistentRememberMeToken current = this.seriesTokens.get(token.getSeries());\n\t\tif (current != null) {\n\t\t\tthrow new DataIntegrityViolationException(\"Series Id '\" + token.getSeries() + \"' already exists!\");\n\t\t}\n\t\tthis.seriesTokens.put(token.getSeries(), token);\n\t}\n\n\t@Override\n\tpublic synchronized void updateToken(String series, String tokenValue, Date lastUsed) {\n\t\tPersistentRememberMeToken token = getTokenForSeries(series);\n\t\tif (token == null) {\n\t\t\tthrow new IllegalArgumentException(\"Token for series '\" + series + \"' does not exist\");\n\t\t}\n\t\tPersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), series, tokenValue,\n\t\t\t\tnew Date());\n\t\t// Store it, overwriting the existing one.\n\t\tthis.seriesTokens.put(series, newToken);\n\t}\n\n\t@Override\n\tpublic synchronized @Nullable PersistentRememberMeToken getTokenForSeries(String seriesId) {\n\t\treturn this.seriesTokens.get(seriesId);\n\t}\n\n\t@Override\n\tpublic synchronized void removeUserTokens(String username) {\n\t\tIterator<String> series = this.seriesTokens.keySet().iterator();\n\t\twhile (series.hasNext()) {\n\t\t\tString seriesId = series.next();\n\t\t\tPersistentRememberMeToken token = this.seriesTokens.get(seriesId);\n\t\t\tif (token != null && username.equals(token.getUsername())) {\n\t\t\t\tseries.remove();\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/InvalidCookieException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.io.Serial;\n\n/**\n * Exception thrown by a RememberMeServices implementation to indicate that a submitted\n * cookie is of an invalid format or has expired.\n *\n * @author Luke Taylor\n */\npublic class InvalidCookieException extends RememberMeAuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7952247791921087125L;\n\n\tpublic InvalidCookieException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/JdbcTokenRepositoryImpl.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Date;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.dao.EmptyResultDataAccessException;\nimport org.springframework.dao.IncorrectResultSizeDataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.support.JdbcDaoSupport;\n\n/**\n * JDBC based persistent login token repository implementation.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements PersistentTokenRepository {\n\n\t/** Default SQL for creating the database table to store the tokens */\n\tpublic static final String CREATE_TABLE_SQL = \"create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, \"\n\t\t\t+ \"token varchar(64) not null, last_used timestamp not null)\";\n\n\t/** The default SQL used by the <tt>getTokenBySeries</tt> query */\n\tpublic static final String DEF_TOKEN_BY_SERIES_SQL = \"select username,series,token,last_used from persistent_logins where series = ?\";\n\n\t/** The default SQL used by <tt>createNewToken</tt> */\n\tpublic static final String DEF_INSERT_TOKEN_SQL = \"insert into persistent_logins (username, series, token, last_used) values(?,?,?,?)\";\n\n\t/** The default SQL used by <tt>updateToken</tt> */\n\tpublic static final String DEF_UPDATE_TOKEN_SQL = \"update persistent_logins set token = ?, last_used = ? where series = ?\";\n\n\t/** The default SQL used by <tt>removeUserTokens</tt> */\n\tpublic static final String DEF_REMOVE_USER_TOKENS_SQL = \"delete from persistent_logins where username = ?\";\n\n\tprivate String tokensBySeriesSql = DEF_TOKEN_BY_SERIES_SQL;\n\n\tprivate String insertTokenSql = DEF_INSERT_TOKEN_SQL;\n\n\tprivate String updateTokenSql = DEF_UPDATE_TOKEN_SQL;\n\n\tprivate String removeUserTokensSql = DEF_REMOVE_USER_TOKENS_SQL;\n\n\tprivate boolean createTableOnStartup;\n\n\t@Override\n\tprotected void initDao() {\n\t\tif (this.createTableOnStartup) {\n\t\t\tgetTemplate().execute(CREATE_TABLE_SQL);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void createNewToken(PersistentRememberMeToken token) {\n\t\tgetTemplate().update(this.insertTokenSql, token.getUsername(), token.getSeries(), token.getTokenValue(),\n\t\t\t\ttoken.getDate());\n\t}\n\n\t@Override\n\tpublic void updateToken(String series, String tokenValue, Date lastUsed) {\n\t\tgetTemplate().update(this.updateTokenSql, tokenValue, lastUsed, series);\n\t}\n\n\t/**\n\t * Loads the token data for the supplied series identifier.\n\t *\n\t * If an error occurs, it will be reported and null will be returned (since the result\n\t * should just be a failed persistent login).\n\t * @param seriesId\n\t * @return the token matching the series, or null if no match found or an exception\n\t * occurred.\n\t */\n\t@Override\n\tpublic @Nullable PersistentRememberMeToken getTokenForSeries(String seriesId) {\n\t\ttry {\n\t\t\treturn getTemplate().queryForObject(this.tokensBySeriesSql, this::createRememberMeToken, seriesId);\n\t\t}\n\t\tcatch (EmptyResultDataAccessException ex) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Querying token for series '%s' returned no results.\", seriesId), ex);\n\t\t}\n\t\tcatch (IncorrectResultSizeDataAccessException ex) {\n\t\t\tthis.logger.error(LogMessage.format(\n\t\t\t\t\t\"Querying token for series '%s' returned more than one value. Series\" + \" should be unique\",\n\t\t\t\t\tseriesId));\n\t\t}\n\t\tcatch (DataAccessException ex) {\n\t\t\tthis.logger.error(\"Failed to load token for series \" + seriesId, ex);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate PersistentRememberMeToken createRememberMeToken(ResultSet rs, int rowNum) throws SQLException {\n\t\treturn new PersistentRememberMeToken(rs.getString(1), rs.getString(2), rs.getString(3), rs.getTimestamp(4));\n\t}\n\n\t@Override\n\tpublic void removeUserTokens(String username) {\n\t\tgetTemplate().update(this.removeUserTokensSql, username);\n\t}\n\n\t/**\n\t * Intended for convenience in debugging. Will create the persistent_tokens database\n\t * table when the class is initialized during the initDao method.\n\t * @param createTableOnStartup set to true to execute the\n\t */\n\tpublic void setCreateTableOnStartup(boolean createTableOnStartup) {\n\t\tthis.createTableOnStartup = createTableOnStartup;\n\t}\n\n\tprivate JdbcTemplate getTemplate() {\n\t\t@Nullable JdbcTemplate result = super.getJdbcTemplate();\n\t\tif (result == null) {\n\t\t\tthrow new IllegalStateException(\"JdbcTemplate was removed\");\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/PersistentRememberMeToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.util.Date;\n\n/**\n * @author Luke Taylor\n */\npublic class PersistentRememberMeToken {\n\n\tprivate final String username;\n\n\tprivate final String series;\n\n\tprivate final String tokenValue;\n\n\tprivate final Date date;\n\n\tpublic PersistentRememberMeToken(String username, String series, String tokenValue, Date date) {\n\t\tthis.username = username;\n\t\tthis.series = series;\n\t\tthis.tokenValue = tokenValue;\n\t\tthis.date = date;\n\t}\n\n\tpublic String getUsername() {\n\t\treturn this.username;\n\t}\n\n\tpublic String getSeries() {\n\t\treturn this.series;\n\t}\n\n\tpublic String getTokenValue() {\n\t\treturn this.tokenValue;\n\t}\n\n\tpublic Date getDate() {\n\t\treturn this.date;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServices.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Date;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.util.Assert;\n\n/**\n * {@link RememberMeServices} implementation based on Barry Jaspan's <a href=\n * \"https://web.archive.org/web/20180819014446/http://jaspan.com/improved_persistent_login_cookie_best_practice\">Improved\n * Persistent Login Cookie Best Practice</a>.\n *\n * There is a slight modification to the described approach, in that the username is not\n * stored as part of the cookie but obtained from the persistent store via an\n * implementation of {@link PersistentTokenRepository}. The latter should place a unique\n * constraint on the series identifier, so that it is impossible for the same identifier\n * to be allocated to two different users.\n *\n * <p>\n * User management such as changing passwords, removing users and setting user status\n * should be combined with maintenance of the user's persistent tokens.\n * </p>\n *\n * <p>\n * Note that while this class will use the date a token was created to check whether a\n * presented cookie is older than the configured <tt>tokenValiditySeconds</tt> property\n * and deny authentication in this case, it will not delete these tokens from storage. A\n * suitable batch process should be run periodically to remove expired tokens from the\n * database.\n * </p>\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices {\n\n\tprivate PersistentTokenRepository tokenRepository = new InMemoryTokenRepositoryImpl();\n\n\tprivate SecureRandom random;\n\n\tpublic static final int DEFAULT_SERIES_LENGTH = 16;\n\n\tpublic static final int DEFAULT_TOKEN_LENGTH = 16;\n\n\tprivate int seriesLength = DEFAULT_SERIES_LENGTH;\n\n\tprivate int tokenLength = DEFAULT_TOKEN_LENGTH;\n\n\tpublic PersistentTokenBasedRememberMeServices(String key, UserDetailsService userDetailsService,\n\t\t\tPersistentTokenRepository tokenRepository) {\n\t\tsuper(key, userDetailsService);\n\t\tthis.random = new SecureRandom();\n\t\tthis.tokenRepository = tokenRepository;\n\t}\n\n\t/**\n\t * Locates the presented cookie data in the token repository, using the series id. If\n\t * the data compares successfully with that in the persistent store, a new token is\n\t * generated and stored with the same series. The corresponding cookie value is set on\n\t * the response.\n\t * @param cookieTokens the series and token values\n\t * @throws RememberMeAuthenticationException if there is no stored token corresponding\n\t * to the submitted cookie, or if the token in the persistent store has expired.\n\t * @throws InvalidCookieException if the cookie doesn't have two tokens as expected.\n\t * @throws CookieTheftException if a presented series value is found, but the stored\n\t * token is different from the one presented.\n\t */\n\t@Override\n\tprotected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tif (cookieTokens.length != 2) {\n\t\t\tthrow new InvalidCookieException(\"Cookie token did not contain \" + 2 + \" tokens, but contained '\"\n\t\t\t\t\t+ Arrays.asList(cookieTokens) + \"'\");\n\t\t}\n\t\tString presentedSeries = cookieTokens[0];\n\t\tString presentedToken = cookieTokens[1];\n\t\tPersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries);\n\t\tif (token == null) {\n\t\t\t// No series match, so we can't authenticate using this cookie\n\t\t\tthrow new RememberMeAuthenticationException(\"No persistent token found for series id: \" + presentedSeries);\n\t\t}\n\t\t// We have a match for this user/series combination\n\t\tif (!presentedToken.equals(token.getTokenValue())) {\n\t\t\t// Token doesn't match series value. Delete all logins for this user and throw\n\t\t\t// an exception to warn them.\n\t\t\tthis.tokenRepository.removeUserTokens(token.getUsername());\n\t\t\tthrow new CookieTheftException(this.messages.getMessage(\n\t\t\t\t\t\"PersistentTokenBasedRememberMeServices.cookieStolen\",\n\t\t\t\t\t\"Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.\"));\n\t\t}\n\t\tif (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {\n\t\t\tthrow new RememberMeAuthenticationException(\"Remember-me login has expired\");\n\t\t}\n\t\t// Token also matches, so login is valid. Update the token value, keeping the\n\t\t// *same* series number.\n\t\tthis.logger.debug(LogMessage.format(\"Refreshing persistent login token for user '%s', series '%s'\",\n\t\t\t\ttoken.getUsername(), token.getSeries()));\n\t\tPersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(),\n\t\t\t\tgenerateTokenData(), new Date());\n\t\ttry {\n\t\t\tthis.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate());\n\t\t\taddCookie(newToken, request, response);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthis.logger.error(\"Failed to update token: \", ex);\n\t\t\tthrow new RememberMeAuthenticationException(\"Autologin failed due to data access problem\");\n\t\t}\n\t\treturn getUserDetailsService().loadUserByUsername(token.getUsername());\n\t}\n\n\t/**\n\t * Creates a new persistent login token with a new series number, stores the data in\n\t * the persistent token repository and adds the corresponding cookie to the response.\n\t *\n\t */\n\t@Override\n\tprotected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication successfulAuthentication) {\n\t\tString username = successfulAuthentication.getName();\n\t\tthis.logger.debug(LogMessage.format(\"Creating new persistent login for user %s\", username));\n\t\tPersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, generateSeriesData(),\n\t\t\t\tgenerateTokenData(), new Date());\n\t\ttry {\n\t\t\tthis.tokenRepository.createNewToken(persistentToken);\n\t\t\taddCookie(persistentToken, request, response);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthis.logger.error(\"Failed to save persistent token \", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tsuper.logout(request, response, authentication);\n\t\tif (authentication != null) {\n\t\t\tthis.tokenRepository.removeUserTokens(authentication.getName());\n\t\t}\n\t}\n\n\tprotected String generateSeriesData() {\n\t\tbyte[] newSeries = new byte[this.seriesLength];\n\t\tthis.random.nextBytes(newSeries);\n\t\treturn new String(Base64.getEncoder().encode(newSeries));\n\t}\n\n\tprotected String generateTokenData() {\n\t\tbyte[] newToken = new byte[this.tokenLength];\n\t\tthis.random.nextBytes(newToken);\n\t\treturn new String(Base64.getEncoder().encode(newToken));\n\t}\n\n\tprivate void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {\n\t\tsetCookie(new String[] { token.getSeries(), token.getTokenValue() }, getTokenValiditySeconds(), request,\n\t\t\t\tresponse);\n\t}\n\n\tpublic void setSeriesLength(int seriesLength) {\n\t\tthis.seriesLength = seriesLength;\n\t}\n\n\tpublic void setTokenLength(int tokenLength) {\n\t\tthis.tokenLength = tokenLength;\n\t}\n\n\t@Override\n\tpublic void setTokenValiditySeconds(int tokenValiditySeconds) {\n\t\tAssert.isTrue(tokenValiditySeconds > 0, \"tokenValiditySeconds must be positive for this implementation\");\n\t\tsuper.setTokenValiditySeconds(tokenValiditySeconds);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/PersistentTokenRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.util.Date;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * The abstraction used by {@link PersistentTokenBasedRememberMeServices} to store the\n * persistent login tokens for a user.\n *\n * @author Luke Taylor\n * @since 2.0\n * @see JdbcTokenRepositoryImpl\n * @see InMemoryTokenRepositoryImpl\n */\npublic interface PersistentTokenRepository {\n\n\tvoid createNewToken(PersistentRememberMeToken token);\n\n\tvoid updateToken(String series, String tokenValue, Date lastUsed);\n\n\t@Nullable PersistentRememberMeToken getTokenForSeries(String seriesId);\n\n\tvoid removeUserTokens(String username);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/RememberMeAuthenticationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * This exception is thrown when an\n * {@link org.springframework.security.core.Authentication} exception occurs while using\n * the remember-me authentication.\n *\n * @author Luke Taylor\n */\npublic class RememberMeAuthenticationException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 7028526952590057426L;\n\n\t/**\n\t * Constructs a {@code RememberMeAuthenticationException} with the specified message\n\t * and root cause.\n\t * @param msg the detail message\n\t * @param cause the root cause\n\t */\n\tpublic RememberMeAuthenticationException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n\t/**\n\t * Constructs an {@code RememberMeAuthenticationException} with the specified message\n\t * and no root cause.\n\t * @param msg the detail message\n\t */\n\tpublic RememberMeAuthenticationException(String msg) {\n\t\tsuper(msg);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/RememberMeAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Detects if there is no {@code Authentication} object in the {@code SecurityContext},\n * and populates the context with a remember-me authentication token if a\n * {@link RememberMeServices} implementation so requests.\n * <p>\n * Concrete {@code RememberMeServices} implementations will have their\n * {@link RememberMeServices#autoLogin(HttpServletRequest, HttpServletResponse)} method\n * called by this filter. If this method returns a non-null {@code Authentication} object,\n * it will be passed to the {@code AuthenticationManager}, so that any\n * authentication-specific behaviour can be achieved. The resulting {@code Authentication}\n * (if successful) will be placed into the {@code SecurityContext}.\n * <p>\n * If authentication is successful, an {@link InteractiveAuthenticationSuccessEvent} will\n * be published to the application context. No events will be published if authentication\n * was unsuccessful, because this would generally be recorded via an\n * {@code AuthenticationManager}-specific application event.\n * <p>\n * Normally the request will be allowed to proceed regardless of whether authentication\n * succeeds or fails. If some control over the destination for authenticated users is\n * required, an {@link AuthenticationSuccessHandler} can be injected\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic class RememberMeAuthenticationFilter extends GenericFilterBean implements ApplicationEventPublisherAware {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable ApplicationEventPublisher eventPublisher;\n\n\tprivate @Nullable AuthenticationSuccessHandler successHandler;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate RememberMeServices rememberMeServices;\n\n\tprivate SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();\n\n\tprivate SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();\n\n\tpublic RememberMeAuthenticationFilter(AuthenticationManager authenticationManager,\n\t\t\tRememberMeServices rememberMeServices) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.notNull(rememberMeServices, \"rememberMeServices cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.rememberMeServices = rememberMeServices;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.authenticationManager, \"authenticationManager must be specified\");\n\t\tAssert.notNull(this.rememberMeServices, \"rememberMeServices must be specified\");\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tif (this.securityContextHolderStrategy.getContext().getAuthentication() != null) {\n\t\t\tthis.logger.debug(LogMessage\n\t\t\t\t.of(() -> \"SecurityContextHolder not populated with remember-me token, as it already contained: '\"\n\t\t\t\t\t\t+ this.securityContextHolderStrategy.getContext().getAuthentication() + \"'\"));\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tAuthentication remembermeToken = this.rememberMeServices.autoLogin(request, response);\n\t\tif (remembermeToken != null) {\n\t\t\t// Attempt authentication via AuthenticationManager\n\t\t\ttry {\n\t\t\t\tAuthentication rememberMeAuth = this.authenticationManager.authenticate(remembermeToken);\n\t\t\t\tthis.sessionStrategy.onAuthentication(rememberMeAuth, request, response);\n\t\t\t\t// Store to SecurityContextHolder\n\t\t\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\t\tcontext.setAuthentication(rememberMeAuth);\n\t\t\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\t\t\tonSuccessfulAuthentication(request, response, rememberMeAuth);\n\t\t\t\tthis.logger.debug(LogMessage\n\t\t\t\t\t.of(() -> \"SecurityContextHolder populated with remember-me token: '\" + rememberMeAuth + \"'\"));\n\t\t\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\t\t\tif (this.eventPublisher != null) {\n\t\t\t\t\tthis.eventPublisher\n\t\t\t\t\t\t.publishEvent(new InteractiveAuthenticationSuccessEvent(rememberMeAuth, this.getClass()));\n\t\t\t\t}\n\t\t\t\tif (this.successHandler != null) {\n\t\t\t\t\tthis.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (AuthenticationException ex) {\n\t\t\t\tthis.logger.debug(LogMessage\n\t\t\t\t\t.format(\"SecurityContextHolder not populated with remember-me token, as AuthenticationManager \"\n\t\t\t\t\t\t\t+ \"rejected Authentication returned by RememberMeServices: '%s'; \"\n\t\t\t\t\t\t\t+ \"invalidating remember-me token\", remembermeToken),\n\t\t\t\t\t\tex);\n\t\t\t\tthis.rememberMeServices.loginFail(request, response);\n\t\t\t\tonUnsuccessfulAuthentication(request, response, ex);\n\t\t\t}\n\t\t}\n\t\tchain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Called if a remember-me token is presented and successfully authenticated by the\n\t * {@code RememberMeServices} {@code autoLogin} method and the\n\t * {@code AuthenticationManager}.\n\t */\n\tprotected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authResult) {\n\t}\n\n\t/**\n\t * Called if the {@code AuthenticationManager} rejects the authentication object\n\t * returned from the {@code RememberMeServices} {@code autoLogin} method. This method\n\t * will not be called when no remember-me token is present in the request and\n\t * {@code autoLogin} returns {@code null}.\n\t */\n\tprotected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException failed) {\n\t}\n\n\tpublic RememberMeServices getRememberMeServices() {\n\t\treturn this.rememberMeServices;\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\t/**\n\t * Allows control over the destination a remembered user is sent to when they are\n\t * successfully authenticated. By default, the filter will just allow the current\n\t * request to proceed, but if an {@code AuthenticationSuccessHandler} is set, it will\n\t * be invoked and the {@code doFilter()} method will return immediately, thus allowing\n\t * the application to redirect the user to a specific URL, regardless of what the\n\t * original request was for.\n\t * @param successHandler the strategy to invoke immediately before returning from\n\t * {@code doFilter()}.\n\t */\n\tpublic void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {\n\t\tAssert.notNull(successHandler, \"successHandler cannot be null\");\n\t\tthis.successHandler = successHandler;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * authentication success. The default action is not to save the\n\t * {@link SecurityContext}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * The session handling strategy which will be invoked immediately after an\n\t * authentication request is successfully processed by the\n\t * <tt>AuthenticationManager</tt>. Used, for example, to handle changing of the\n\t * session identifier to prevent session fixation attacks.\n\t * @param sessionStrategy the implementation to use. If not set a null implementation\n\t * is used.\n\t * @since 6.4\n\t */\n\tpublic void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {\n\t\tAssert.notNull(sessionStrategy, \"sessionStrategy cannot be null\");\n\t\tthis.sessionStrategy = sessionStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Arrays;\nimport java.util.Date;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Identifies previously remembered users by a Base-64 encoded cookie.\n *\n * <p>\n * This implementation does not rely on an external database, so is attractive for simple\n * applications. The cookie will be valid for a specific period from the date of the last\n * {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}. As per\n * the interface contract, this method will only be called when the principal completes a\n * successful interactive authentication. As such the time period commences from the last\n * authentication attempt where they furnished credentials - not the time period they last\n * logged in via remember-me. The implementation will only send a remember-me token if the\n * parameter defined by {@link #setParameter(String)} is present.\n * <p>\n * An {@link org.springframework.security.core.userdetails.UserDetailsService} is required\n * by this implementation, so that it can construct a valid <code>Authentication</code>\n * from the returned {@link org.springframework.security.core.userdetails.UserDetails}.\n * This is also necessary so that the user's password is available and can be checked as\n * part of the encoded cookie.\n * <p>\n * The cookie encoded by this implementation adopts the following form:\n *\n * <pre>\n * username + &quot;:&quot; + expiryTime + &quot;:&quot; + algorithmName + &quot;:&quot;\n * \t\t+ algorithmHex(username + &quot;:&quot; + expiryTime + &quot;:&quot; + password + &quot;:&quot; + key)\n * </pre>\n *\n * <p>\n * This implementation uses the algorithm configured in {@link #encodingAlgorithm} to\n * encode the signature. It will try to use the algorithm retrieved from the\n * {@code algorithmName} to validate the signature. However, if the {@code algorithmName}\n * is not present in the cookie value, the algorithm configured in\n * {@link #matchingAlgorithm} will be used to validate the signature. This allows users to\n * safely upgrade to a different encoding algorithm while still able to verify old ones if\n * there is no {@code algorithmName} present.\n * </p>\n *\n * <p>\n * As such, if the user changes their password, any remember-me token will be invalidated.\n * Equally, the system administrator may invalidate every remember-me token on issue by\n * changing the key. This provides some reasonable approaches to recovering from a\n * remember-me token being left on a public machine (e.g. kiosk system, Internet cafe\n * etc). Most importantly, at no time is the user's password ever sent to the user agent,\n * providing an important security safeguard. Unfortunately the username is necessary in\n * this implementation (as we do not want to rely on a database for remember-me services).\n * High security applications should be aware of this occasionally undesired disclosure of\n * a valid username.\n * <p>\n * This is a basic remember-me implementation which is suitable for many applications.\n * However, we recommend a database-based implementation if you require a more secure\n * remember-me approach (see {@link PersistentTokenBasedRememberMeServices}).\n * <p>\n * By default the tokens will be valid for 14 days from the last successful authentication\n * attempt. This can be changed using {@link #setTokenValiditySeconds(int)}. If this value\n * is less than zero, the <tt>expiryTime</tt> will remain at 14 days, but the negative\n * value will be used for the <tt>maxAge</tt> property of the cookie, meaning that it will\n * not be stored when the browser is closed.\n *\n * @author Ben Alex\n * @author Marcus Da Coregio\n */\npublic class TokenBasedRememberMeServices extends AbstractRememberMeServices {\n\n\tprivate static final RememberMeTokenAlgorithm DEFAULT_MATCHING_ALGORITHM = RememberMeTokenAlgorithm.SHA256;\n\n\tprivate static final RememberMeTokenAlgorithm DEFAULT_ENCODING_ALGORITHM = RememberMeTokenAlgorithm.SHA256;\n\n\tprivate final RememberMeTokenAlgorithm encodingAlgorithm;\n\n\tprivate RememberMeTokenAlgorithm matchingAlgorithm = DEFAULT_MATCHING_ALGORITHM;\n\n\tpublic TokenBasedRememberMeServices(String key, UserDetailsService userDetailsService) {\n\t\tthis(key, userDetailsService, DEFAULT_ENCODING_ALGORITHM);\n\t}\n\n\t/**\n\t * Construct the instance with the parameters provided\n\t * @param key the signature key\n\t * @param userDetailsService the {@link UserDetailsService}\n\t * @param encodingAlgorithm the {@link RememberMeTokenAlgorithm} used to encode the\n\t * signature\n\t * @since 5.8\n\t */\n\tpublic TokenBasedRememberMeServices(String key, UserDetailsService userDetailsService,\n\t\t\tRememberMeTokenAlgorithm encodingAlgorithm) {\n\t\tsuper(key, userDetailsService);\n\t\tAssert.notNull(encodingAlgorithm, \"encodingAlgorithm cannot be null\");\n\t\tthis.encodingAlgorithm = encodingAlgorithm;\n\t}\n\n\t@Override\n\tprotected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tif (!isValidCookieTokensLength(cookieTokens)) {\n\t\t\tthrow new InvalidCookieException(\n\t\t\t\t\t\"Cookie token did not contain 3 or 4 tokens, but contained '\" + Arrays.asList(cookieTokens) + \"'\");\n\t\t}\n\t\tlong tokenExpiryTime = getTokenExpiryTime(cookieTokens);\n\t\tif (isTokenExpired(tokenExpiryTime)) {\n\t\t\tthrow new InvalidCookieException(\"Cookie token[1] has expired (expired on '\" + new Date(tokenExpiryTime)\n\t\t\t\t\t+ \"'; current time is '\" + new Date() + \"')\");\n\t\t}\n\t\t// Check the user exists. Defer lookup until after expiry time checked, to\n\t\t// possibly avoid expensive database call.\n\t\tUserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);\n\t\tAssert.notNull(userDetails, () -> \"UserDetailsService \" + getUserDetailsService()\n\t\t\t\t+ \" returned null for username \" + cookieTokens[0] + \". \" + \"This is an interface contract violation\");\n\t\t// Check signature of token matches remaining details. Must do this after user\n\t\t// lookup, as we need the DAO-derived password. If efficiency was a major issue,\n\t\t// just add in a UserCache implementation, but recall that this method is usually\n\t\t// only called once per HttpSession - if the token is valid, it will cause\n\t\t// SecurityContextHolder population, whilst if invalid, will cause the cookie to\n\t\t// be cancelled.\n\t\tString actualTokenSignature = cookieTokens[2];\n\t\tRememberMeTokenAlgorithm actualAlgorithm = this.matchingAlgorithm;\n\t\t// If the cookie value contains the algorithm, we use that algorithm to check the\n\t\t// signature\n\t\tif (cookieTokens.length == 4) {\n\t\t\tactualTokenSignature = cookieTokens[3];\n\t\t\tactualAlgorithm = RememberMeTokenAlgorithm.valueOf(cookieTokens[2]);\n\t\t}\n\t\tString expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails.getUsername(),\n\t\t\t\tuserDetails.getPassword(), actualAlgorithm);\n\t\tif (!equals(expectedTokenSignature, actualTokenSignature)) {\n\t\t\tthrow new InvalidCookieException(\"Cookie contained signature '\" + actualTokenSignature + \"' but expected '\"\n\t\t\t\t\t+ expectedTokenSignature + \"'\");\n\t\t}\n\t\treturn userDetails;\n\t}\n\n\tprivate boolean isValidCookieTokensLength(String[] cookieTokens) {\n\t\treturn cookieTokens.length == 3 || cookieTokens.length == 4;\n\t}\n\n\tprivate long getTokenExpiryTime(String[] cookieTokens) {\n\t\ttry {\n\t\t\treturn Long.valueOf(cookieTokens[1]);\n\t\t}\n\t\tcatch (NumberFormatException nfe) {\n\t\t\tthrow new InvalidCookieException(\n\t\t\t\t\t\"Cookie token[1] did not contain a valid number (contained '\" + cookieTokens[1] + \"')\");\n\t\t}\n\t}\n\n\t/**\n\t * Calculates the digital signature to be put in the cookie. Default value is\n\t * {@link #encodingAlgorithm} applied to (\"username:tokenExpiryTime:password:key\")\n\t */\n\tprotected String makeTokenSignature(long tokenExpiryTime, String username, String password) {\n\t\tString data = username + \":\" + tokenExpiryTime + \":\" + password + \":\" + getKey();\n\t\ttry {\n\t\t\tMessageDigest digest = MessageDigest.getInstance(this.encodingAlgorithm.getDigestAlgorithm());\n\t\t\treturn new String(Hex.encode(digest.digest(data.getBytes())));\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(\"No \" + this.encodingAlgorithm.name() + \" algorithm available!\");\n\t\t}\n\t}\n\n\t/**\n\t * Calculates the digital signature to be put in the cookie.\n\t * @since 5.8\n\t */\n\tprotected String makeTokenSignature(long tokenExpiryTime, String username, @Nullable String password,\n\t\t\tRememberMeTokenAlgorithm algorithm) {\n\t\tString data = username + \":\" + tokenExpiryTime + \":\" + password + \":\" + getKey();\n\t\ttry {\n\t\t\tMessageDigest digest = MessageDigest.getInstance(algorithm.getDigestAlgorithm());\n\t\t\treturn new String(Hex.encode(digest.digest(data.getBytes())));\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(\"No \" + algorithm.name() + \" algorithm available!\");\n\t\t}\n\t}\n\n\tprotected boolean isTokenExpired(long tokenExpiryTime) {\n\t\treturn tokenExpiryTime < System.currentTimeMillis();\n\t}\n\n\t@Override\n\tpublic void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication successfulAuthentication) {\n\t\tString username = retrieveUserName(successfulAuthentication);\n\t\tString password = retrievePassword(successfulAuthentication);\n\t\t// If unable to find a username and password, just abort as\n\t\t// TokenBasedRememberMeServices is\n\t\t// unable to construct a valid token in this case.\n\t\tif (!StringUtils.hasLength(username)) {\n\t\t\tthis.logger.debug(\"Unable to retrieve username\");\n\t\t\treturn;\n\t\t}\n\t\tif (!StringUtils.hasLength(password)) {\n\t\t\tUserDetails user = getUserDetailsService().loadUserByUsername(username);\n\t\t\tpassword = user.getPassword();\n\t\t\tif (!StringUtils.hasLength(password)) {\n\t\t\t\tthis.logger.debug(\"Unable to obtain password for user: \" + username);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tint tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);\n\t\tlong expiryTime = System.currentTimeMillis();\n\t\t// SEC-949\n\t\texpiryTime += 1000L * ((tokenLifetime < 0) ? TWO_WEEKS_S : tokenLifetime);\n\t\tString signatureValue = makeTokenSignature(expiryTime, username, password, this.encodingAlgorithm);\n\t\tsetCookie(new String[] { username, Long.toString(expiryTime), this.encodingAlgorithm.name(), signatureValue },\n\t\t\t\ttokenLifetime, request, response);\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger\n\t\t\t\t.debug(\"Added remember-me cookie for user '\" + username + \"', expiry: '\" + new Date(expiryTime) + \"'\");\n\t\t}\n\t}\n\n\t/**\n\t * Sets the algorithm to be used to match the token signature\n\t * @param matchingAlgorithm the matching algorithm\n\t * @since 5.8\n\t */\n\tpublic void setMatchingAlgorithm(RememberMeTokenAlgorithm matchingAlgorithm) {\n\t\tAssert.notNull(matchingAlgorithm, \"matchingAlgorithm cannot be null\");\n\t\tthis.matchingAlgorithm = matchingAlgorithm;\n\t}\n\n\t/**\n\t * Calculates the validity period in seconds for a newly generated remember-me login.\n\t * After this period (from the current time) the remember-me login will be considered\n\t * expired. This method allows customization based on request parameters supplied with\n\t * the login or information in the <tt>Authentication</tt> object. The default value\n\t * is just the token validity period property, <tt>tokenValiditySeconds</tt>.\n\t * <p>\n\t * The returned value will be used to work out the expiry time of the token and will\n\t * also be used to set the <tt>maxAge</tt> property of the cookie.\n\t * <p>\n\t * See SEC-485.\n\t * @param request the request passed to onLoginSuccess\n\t * @param authentication the successful authentication object.\n\t * @return the lifetime in seconds.\n\t */\n\tprotected int calculateLoginLifetime(HttpServletRequest request, Authentication authentication) {\n\t\treturn getTokenValiditySeconds();\n\t}\n\n\tprotected String retrieveUserName(Authentication authentication) {\n\t\tObject principal = authentication.getPrincipal();\n\t\tAssert.notNull(principal, \"Authentication.getPrincipal() cannot be null\");\n\t\tif (principal instanceof UserDetails userDetails) {\n\t\t\treturn userDetails.getUsername();\n\t\t}\n\t\treturn principal.toString();\n\t}\n\n\tprotected @Nullable String retrievePassword(Authentication authentication) {\n\t\tObject principal = authentication.getPrincipal();\n\t\tif (principal instanceof UserDetails userDetails) {\n\t\t\treturn userDetails.getPassword();\n\t\t}\n\t\tif (authentication.getCredentials() != null) {\n\t\t\treturn authentication.getCredentials().toString();\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Constant time comparison to prevent against timing attacks.\n\t */\n\tprivate static boolean equals(String expected, String actual) {\n\t\tbyte[] expectedBytes = bytesUtf8(expected);\n\t\tbyte[] actualBytes = bytesUtf8(actual);\n\t\treturn MessageDigest.isEqual(expectedBytes, actualBytes);\n\t}\n\n\tprivate static byte @Nullable [] bytesUtf8(String s) {\n\t\treturn (s != null) ? Utf8.encode(s) : null;\n\t}\n\n\tpublic enum RememberMeTokenAlgorithm {\n\n\t\tMD5(\"MD5\"), SHA256(\"SHA-256\");\n\n\t\tprivate final String digestAlgorithm;\n\n\t\tRememberMeTokenAlgorithm(String digestAlgorithm) {\n\t\t\tthis.digestAlgorithm = digestAlgorithm;\n\t\t}\n\n\t\tpublic String getDigestAlgorithm() {\n\t\t\treturn this.digestAlgorithm;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/rememberme/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for remembering a user between different web sessions.\n * <p>\n * Comes with two default implementations. See the <a href=\n * \"https://docs.spring.io/spring-security/site/docs/3.0.x/reference/remember-me.html\">Remember-Me\n * Authentication</a> chapter of the reference manual.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.rememberme;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/AbstractSessionFixationProtectionStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.WebUtils;\n\n/**\n * A base class for performing session fixation protection.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic abstract class AbstractSessionFixationProtectionStrategy\n\t\timplements SessionAuthenticationStrategy, ApplicationEventPublisherAware {\n\n\tprotected final Log logger = LogFactory.getLog(this.getClass());\n\n\t/**\n\t * Used for publishing events related to session fixation protection, such as\n\t * {@link SessionFixationProtectionEvent}.\n\t */\n\tprivate ApplicationEventPublisher applicationEventPublisher = new NullEventPublisher();\n\n\t/**\n\t * If set to {@code true}, a session will always be created, even if one didn't exist\n\t * at the start of the request. Defaults to {@code false}.\n\t */\n\tprivate boolean alwaysCreateSession;\n\n\tAbstractSessionFixationProtectionStrategy() {\n\t}\n\n\t/**\n\t * Called when a user is newly authenticated.\n\t * <p>\n\t * If a session already exists, and matches the session Id from the client, a new\n\t * session will be created, and the session attributes copied to it (if\n\t * {@code migrateSessionAttributes} is set). If the client's requested session Id is\n\t * invalid, nothing will be done, since there is no need to change the session Id if\n\t * it doesn't match the current session.\n\t * <p>\n\t * If there is no session, no action is taken unless the {@code alwaysCreateSession}\n\t * property is set, in which case a session will be created if one doesn't already\n\t * exist.\n\t */\n\t@Override\n\tpublic void onAuthentication(Authentication authentication, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tboolean hadSessionAlready = request.getSession(false) != null;\n\t\tif (!hadSessionAlready && !this.alwaysCreateSession) {\n\t\t\t// Session fixation isn't a problem if there's no session\n\t\t\treturn;\n\t\t}\n\t\t// Create new session if necessary\n\t\tHttpSession session = request.getSession();\n\t\tif (hadSessionAlready && request.isRequestedSessionIdValid()) {\n\t\t\tString originalSessionId;\n\t\t\tString newSessionId;\n\t\t\tObject mutex = WebUtils.getSessionMutex(session);\n\t\t\tsynchronized (mutex) {\n\t\t\t\t// We need to migrate to a new session\n\t\t\t\toriginalSessionId = session.getId();\n\t\t\t\tsession = applySessionFixation(request);\n\t\t\t\tnewSessionId = session.getId();\n\t\t\t}\n\t\t\tif (originalSessionId.equals(newSessionId)) {\n\t\t\t\tthis.logger.warn(\"Your servlet container did not change the session ID when a new session \"\n\t\t\t\t\t\t+ \"was created. You will not be adequately protected against session-fixation attacks\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\t\tthis.logger.debug(LogMessage.format(\"Changed session id from %s\", originalSessionId));\n\t\t\t\t}\n\t\t\t}\n\t\t\tonSessionChange(originalSessionId, session, authentication);\n\t\t}\n\t}\n\n\t/**\n\t * Applies session fixation\n\t * @param request the {@link HttpServletRequest} to apply session fixation protection\n\t * for\n\t * @return the new {@link HttpSession} to use. Cannot be null.\n\t */\n\tabstract HttpSession applySessionFixation(HttpServletRequest request);\n\n\t/**\n\t * Called when the session has been changed and the old attributes have been migrated\n\t * to the new session. Only called if a session existed to start with. Allows\n\t * subclasses to plug in additional behaviour. *\n\t * <p>\n\t * The default implementation of this method publishes a\n\t * {@link SessionFixationProtectionEvent} to notify the application that the session\n\t * ID has changed. If you override this method and still wish these events to be\n\t * published, you should call {@code super.onSessionChange()} within your overriding\n\t * method.\n\t * @param originalSessionId the original session identifier\n\t * @param newSession the newly created session\n\t * @param auth the token for the newly authenticated principal\n\t */\n\tprotected void onSessionChange(String originalSessionId, HttpSession newSession, Authentication auth) {\n\t\tthis.applicationEventPublisher\n\t\t\t.publishEvent(new SessionFixationProtectionEvent(auth, originalSessionId, newSession.getId()));\n\t}\n\n\t/**\n\t * Sets the {@link ApplicationEventPublisher} to use for submitting\n\t * {@link SessionFixationProtectionEvent}. The default is to not submit the\n\t * {@link SessionFixationProtectionEvent}.\n\t * @param applicationEventPublisher the {@link ApplicationEventPublisher}. Cannot be\n\t * null.\n\t */\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n\t\tAssert.notNull(applicationEventPublisher, \"applicationEventPublisher cannot be null\");\n\t\tthis.applicationEventPublisher = applicationEventPublisher;\n\t}\n\n\tpublic void setAlwaysCreateSession(boolean alwaysCreateSession) {\n\t\tthis.alwaysCreateSession = alwaysCreateSession;\n\t}\n\n\tprotected static final class NullEventPublisher implements ApplicationEventPublisher {\n\n\t\t@Override\n\t\tpublic void publishEvent(ApplicationEvent event) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void publishEvent(Object event) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/ChangeSessionIdAuthenticationStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpSession;\n\n/**\n * Uses {@code HttpServletRequest.changeSessionId()} to protect against session fixation\n * attacks. This is the default implementation.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class ChangeSessionIdAuthenticationStrategy extends AbstractSessionFixationProtectionStrategy {\n\n\t@Override\n\tHttpSession applySessionFixation(HttpServletRequest request) {\n\t\trequest.changeSessionId();\n\t\treturn request.getSession();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/CompositeSessionAuthenticationStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link SessionAuthenticationStrategy} that accepts multiple\n * {@link SessionAuthenticationStrategy} implementations to delegate to. Each\n * {@link SessionAuthenticationStrategy} is invoked in turn. The invocations are short\n * circuited if any exception, (i.e. SessionAuthenticationException) is thrown.\n *\n * <p>\n * Typical usage would include having the following delegates (in this order)\n * </p>\n *\n * <ul>\n * <li>{@link ConcurrentSessionControlAuthenticationStrategy} - verifies that a user is\n * allowed to authenticate (i.e. they have not already logged into the application.</li>\n * <li>{@link SessionFixationProtectionStrategy} - If session fixation is desired,\n * {@link SessionFixationProtectionStrategy} should be after\n * {@link ConcurrentSessionControlAuthenticationStrategy} to prevent unnecessary\n * {@link HttpSession} creation if the\n * {@link ConcurrentSessionControlAuthenticationStrategy} rejects authentication.</li>\n * <li>{@link RegisterSessionAuthenticationStrategy} - It is important this is after\n * {@link SessionFixationProtectionStrategy} so that the correct session is registered.\n * </li>\n * </ul>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class CompositeSessionAuthenticationStrategy implements SessionAuthenticationStrategy {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final List<SessionAuthenticationStrategy> delegateStrategies;\n\n\tpublic CompositeSessionAuthenticationStrategy(List<SessionAuthenticationStrategy> delegateStrategies) {\n\t\tAssert.notEmpty(delegateStrategies, \"delegateStrategies cannot be null or empty\");\n\t\tfor (SessionAuthenticationStrategy strategy : delegateStrategies) {\n\t\t\tAssert.notNull(strategy, () -> \"delegateStrategies cannot contain null entires. Got \" + delegateStrategies);\n\t\t}\n\t\tthis.delegateStrategies = delegateStrategies;\n\t}\n\n\t@Override\n\tpublic void onAuthentication(Authentication authentication, HttpServletRequest request,\n\t\t\tHttpServletResponse response) throws SessionAuthenticationException {\n\t\tint currentPosition = 0;\n\t\tint size = this.delegateStrategies.size();\n\t\tfor (SessionAuthenticationStrategy delegate : this.delegateStrategies) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Preparing session with %s (%d/%d)\",\n\t\t\t\t\t\tdelegate.getClass().getSimpleName(), ++currentPosition, size));\n\t\t\t}\n\t\t\tdelegate.onAuthentication(authentication, request, response);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getName() + \" [delegateStrategies = \" + this.delegateStrategies + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport java.util.Comparator;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.session.ConcurrentSessionFilter;\nimport org.springframework.security.web.session.SessionManagementFilter;\nimport org.springframework.util.Assert;\n\n/**\n * Strategy which handles concurrent session-control.\n *\n * <p>\n * When invoked following an authentication, it will check whether the user in question\n * should be allowed to proceed, by comparing the number of sessions they already have\n * active with the configured <tt>maximumSessions</tt> value. The {@link SessionRegistry}\n * is used as the source of data on authenticated users and session data.\n * </p>\n * <p>\n * If a user has reached the maximum number of permitted sessions, the behaviour depends\n * on the <tt>exceptionIfMaxExceeded</tt> property. The default behaviour is to expire any\n * sessions that exceed the maximum number of permitted sessions, starting with the least\n * recently used sessions. The expired sessions will be invalidated by the\n * {@link ConcurrentSessionFilter} if accessed again. If <tt>exceptionIfMaxExceeded</tt>\n * is set to <tt>true</tt>, however, the user will be prevented from starting a new\n * authenticated session.\n * </p>\n * <p>\n * This strategy can be injected into both the {@link SessionManagementFilter} and\n * instances of {@link AbstractAuthenticationProcessingFilter} (typically\n * {@link UsernamePasswordAuthenticationFilter}), but is typically combined with\n * {@link RegisterSessionAuthenticationStrategy} using\n * {@link CompositeSessionAuthenticationStrategy}.\n * </p>\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.2\n * @see CompositeSessionAuthenticationStrategy\n */\npublic class ConcurrentSessionControlAuthenticationStrategy\n\t\timplements MessageSourceAware, SessionAuthenticationStrategy {\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate final SessionRegistry sessionRegistry;\n\n\tprivate boolean exceptionIfMaximumExceeded = false;\n\n\tprivate SessionLimit sessionLimit = SessionLimit.of(1);\n\n\t/**\n\t * @param sessionRegistry the session registry which should be updated when the\n\t * authenticated session is changed.\n\t */\n\tpublic ConcurrentSessionControlAuthenticationStrategy(SessionRegistry sessionRegistry) {\n\t\tAssert.notNull(sessionRegistry, \"The sessionRegistry cannot be null\");\n\t\tthis.sessionRegistry = sessionRegistry;\n\t}\n\n\t/**\n\t * In addition to the steps from the superclass, the sessionRegistry will be updated\n\t * with the new session information.\n\t */\n\t@Override\n\tpublic void onAuthentication(Authentication authentication, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tint allowedSessions = getMaximumSessionsForThisUser(authentication);\n\t\tif (allowedSessions == -1) {\n\t\t\t// We permit unlimited logins\n\t\t\treturn;\n\t\t}\n\t\tObject principal = authentication.getPrincipal();\n\t\tAssert.notNull(principal, \"Authentication.getPrincipal() cannot be null\");\n\t\tList<SessionInformation> sessions = this.sessionRegistry.getAllSessions(principal, false);\n\t\tint sessionCount = sessions.size();\n\t\tif (sessionCount < allowedSessions) {\n\t\t\t// They haven't got too many login sessions running at present\n\t\t\treturn;\n\t\t}\n\t\tif (sessionCount == allowedSessions) {\n\t\t\tHttpSession session = request.getSession(false);\n\t\t\tif (session != null) {\n\t\t\t\t// Only permit it though if this request is associated with one of the\n\t\t\t\t// already registered sessions\n\t\t\t\tfor (SessionInformation si : sessions) {\n\t\t\t\t\tif (si.getSessionId().equals(session.getId())) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If the session is null, a new one will be created by the parent class,\n\t\t\t// exceeding the allowed number\n\t\t}\n\t\tallowableSessionsExceeded(sessions, allowedSessions, this.sessionRegistry);\n\t}\n\n\t/**\n\t * Method intended for use by subclasses to override the maximum number of sessions\n\t * that are permitted for a particular authentication. The default implementation\n\t * simply returns the <code>maximumSessions</code> value for the bean.\n\t * @param authentication to determine the maximum sessions for\n\t * @return either -1 meaning unlimited, or a positive integer to limit (never zero)\n\t */\n\tprotected int getMaximumSessionsForThisUser(Authentication authentication) {\n\t\treturn this.sessionLimit.apply(authentication);\n\t}\n\n\t/**\n\t * Allows subclasses to customise behaviour when too many sessions are detected.\n\t * @param sessions either <code>null</code> or all unexpired sessions associated with\n\t * the principal\n\t * @param allowableSessions the number of concurrent sessions the user is allowed to\n\t * have\n\t * @param registry an instance of the <code>SessionRegistry</code> for subclass use\n\t *\n\t */\n\tprotected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,\n\t\t\tSessionRegistry registry) throws SessionAuthenticationException {\n\t\tif (this.exceptionIfMaximumExceeded || (sessions == null)) {\n\t\t\tthrow new SessionAuthenticationException(\n\t\t\t\t\tthis.messages.getMessage(\"ConcurrentSessionControlAuthenticationStrategy.exceededAllowed\",\n\t\t\t\t\t\t\tnew Object[] { allowableSessions }, \"Maximum sessions of {0} for this principal exceeded\"));\n\t\t}\n\t\t// Determine least recently used sessions, and mark them for invalidation\n\t\tsessions.sort(Comparator.comparing(SessionInformation::getLastRequest));\n\t\tint maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;\n\t\tList<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);\n\t\tfor (SessionInformation session : sessionsToBeExpired) {\n\t\t\tsession.expireNow();\n\t\t}\n\t}\n\n\t/**\n\t * Sets the <tt>exceptionIfMaximumExceeded</tt> property, which determines whether the\n\t * user should be prevented from opening more sessions than allowed. If set to\n\t * <tt>true</tt>, a <tt>SessionAuthenticationException</tt> will be raised which means\n\t * the user authenticating will be prevented from authenticating. if set to\n\t * <tt>false</tt>, the user that has already authenticated will be forcibly logged\n\t * out.\n\t * @param exceptionIfMaximumExceeded defaults to <tt>false</tt>.\n\t */\n\tpublic void setExceptionIfMaximumExceeded(boolean exceptionIfMaximumExceeded) {\n\t\tthis.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;\n\t}\n\n\t/**\n\t * Sets the <tt>sessionLimit</tt> property. The default value is 1. Use -1 for\n\t * unlimited sessions.\n\t * @param maximumSessions the maximum number of permitted sessions a user can have\n\t * open simultaneously.\n\t */\n\tpublic void setMaximumSessions(int maximumSessions) {\n\t\tthis.sessionLimit = SessionLimit.of(maximumSessions);\n\t}\n\n\t/**\n\t * Sets the <tt>sessionLimit</tt> property. The default value is 1. Use -1 for\n\t * unlimited sessions.\n\t * @param sessionLimit the session limit strategy\n\t * @since 6.5\n\t */\n\tpublic void setMaximumSessions(SessionLimit sessionLimit) {\n\t\tAssert.notNull(sessionLimit, \"sessionLimit cannot be null\");\n\t\tthis.sessionLimit = sessionLimit;\n\t}\n\n\t/**\n\t * Sets the {@link MessageSource} used for reporting errors back to the user when the\n\t * user has exceeded the maximum number of authentications.\n\t */\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/NullAuthenticatedSessionStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\npublic final class NullAuthenticatedSessionStrategy implements SessionAuthenticationStrategy {\n\n\t@Override\n\tpublic void onAuthentication(@Nullable Authentication authentication, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/RegisterSessionAuthenticationStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.web.session.HttpSessionEventPublisher;\nimport org.springframework.util.Assert;\n\n/**\n * Strategy used to register a user with the {@link SessionRegistry} after successful\n * {@link Authentication}.\n *\n * <p>\n * {@link RegisterSessionAuthenticationStrategy} is typically used in combination with\n * {@link CompositeSessionAuthenticationStrategy} and\n * {@link ConcurrentSessionControlAuthenticationStrategy}, but can be used on its own if\n * tracking of sessions is desired but no need to control concurrency.\n *\n * <p>\n * NOTE: When using a {@link SessionRegistry} it is important that all sessions (including\n * timed out sessions) are removed. This is typically done by adding\n * {@link HttpSessionEventPublisher}.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.2\n * @see CompositeSessionAuthenticationStrategy\n */\npublic class RegisterSessionAuthenticationStrategy implements SessionAuthenticationStrategy {\n\n\tprivate final SessionRegistry sessionRegistry;\n\n\t/**\n\t * @param sessionRegistry the session registry which should be updated when the\n\t * authenticated session is changed.\n\t */\n\tpublic RegisterSessionAuthenticationStrategy(SessionRegistry sessionRegistry) {\n\t\tAssert.notNull(sessionRegistry, \"The sessionRegistry cannot be null\");\n\t\tthis.sessionRegistry = sessionRegistry;\n\t}\n\n\t/**\n\t * In addition to the steps from the superclass, the sessionRegistry will be updated\n\t * with the new session information.\n\t */\n\t@Override\n\tpublic void onAuthentication(Authentication authentication, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tObject principal = authentication.getPrincipal();\n\t\tAssert.notNull(principal, \"The principal cannot be null\");\n\t\tthis.sessionRegistry.registerNewSession(request.getSession().getId(), principal);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/SessionAuthenticationException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Thrown by an {@link SessionAuthenticationStrategy} or\n * {@link SessionAuthenticationStrategy} to indicate that an authentication object is not\n * valid for the current session, typically because the same user has exceeded the number\n * of sessions they are allowed to have concurrently.\n *\n * @author Luke Taylor\n * @since 3.0\n * @see SessionAuthenticationStrategy\n */\npublic class SessionAuthenticationException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2359914603911936474L;\n\n\tpublic SessionAuthenticationException(String msg) {\n\t\tsuper(msg);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/SessionAuthenticationStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Allows pluggable support for HttpSession-related behaviour when an authentication\n * occurs.\n * <p>\n * Typical use would be to make sure a session exists or to change the session Id to guard\n * against session-fixation attacks.\n *\n * @author Luke Taylor\n * @since\n */\npublic interface SessionAuthenticationStrategy {\n\n\t/**\n\t * Performs Http session-related functionality when a new authentication occurs.\n\t * @throws SessionAuthenticationException if it is decided that the authentication is\n\t * not allowed for the session. This will typically be because the user has too many\n\t * sessions open at once.\n\t */\n\tvoid onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows SessionAuthenticationException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/SessionFixationProtectionEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport java.io.Serial;\n\nimport org.springframework.security.authentication.event.AbstractAuthenticationEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Indicates a session ID was changed for the purposes of session fixation protection.\n *\n * @author Nicholas Williams\n * @since 3.2\n * @see SessionFixationProtectionStrategy\n */\npublic class SessionFixationProtectionEvent extends AbstractAuthenticationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2554621992006921150L;\n\n\tprivate final String oldSessionId;\n\n\tprivate final String newSessionId;\n\n\t/**\n\t * Constructs a new session fixation protection event.\n\t * @param authentication The authentication object\n\t * @param oldSessionId The old session ID before it was changed\n\t * @param newSessionId The new session ID after it was changed\n\t */\n\tpublic SessionFixationProtectionEvent(Authentication authentication, String oldSessionId, String newSessionId) {\n\t\tsuper(authentication);\n\t\tAssert.hasLength(oldSessionId, \"oldSessionId must have length\");\n\t\tAssert.hasLength(newSessionId, \"newSessionId must have length\");\n\t\tthis.oldSessionId = oldSessionId;\n\t\tthis.newSessionId = newSessionId;\n\t}\n\n\t/**\n\t * Getter for the session ID before it was changed.\n\t * @return the old session ID.\n\t */\n\tpublic String getOldSessionId() {\n\t\treturn this.oldSessionId;\n\t}\n\n\t/**\n\t * Getter for the session ID after it was changed.\n\t * @return the new session ID.\n\t */\n\tpublic String getNewSessionId() {\n\t\treturn this.newSessionId;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/SessionFixationProtectionStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.core.log.LogMessage;\n\n/**\n * Uses {@code HttpServletRequest.invalidate()} to protect against session fixation\n * attacks.\n * <p>\n * Creates a new session for the newly authenticated user if they already have a session\n * (as a defence against session-fixation protection attacks), and copies their session\n * attributes across to the new session. The copying of the attributes can be disabled by\n * setting {@code migrateSessionAttributes} to {@code false} (note that even in this case,\n * internal Spring Security attributes will still be migrated to the new session).\n * <p>\n * This approach will only be effective if your servlet container always assigns a new\n * session Id when a session is invalidated and a new session created by calling\n * {@link HttpServletRequest#getSession()}.\n * <p>\n * <h3>Issues with {@code HttpSessionBindingListener}</h3>\n * <p>\n * The migration of existing attributes to the newly-created session may cause problems if\n * any of the objects implement the {@code HttpSessionBindingListener} interface in a way\n * which makes assumptions about the life-cycle of the object. An example is the use of\n * Spring session-scoped beans, where the initial removal of the bean from the session\n * will cause the {@code DisposableBean} interface to be invoked, in the assumption that\n * the bean is no longer required.\n * <p>\n * We'd recommend that you take account of this when designing your application and do not\n * store attributes which may not function correctly when they are removed and then placed\n * back in the session. Alternatively, you should customize the\n * {@code SessionAuthenticationStrategy} to deal with the issue in an application-specific\n * way.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class SessionFixationProtectionStrategy extends AbstractSessionFixationProtectionStrategy {\n\n\t/**\n\t * Indicates that the session attributes of an existing session should be migrated to\n\t * the new session. Defaults to <code>true</code>.\n\t */\n\tboolean migrateSessionAttributes = true;\n\n\t/**\n\t * Called to extract the existing attributes from the session, prior to invalidating\n\t * it. If {@code migrateAttributes} is set to {@code false}, only Spring Security\n\t * attributes will be retained. All application attributes will be discarded.\n\t * <p>\n\t * You can override this method to control exactly what is transferred to the new\n\t * session.\n\t * @param session the session from which the attributes should be extracted\n\t * @return the map of session attributes which should be transferred to the new\n\t * session\n\t */\n\tprotected Map<String, Object> extractAttributes(HttpSession session) {\n\t\treturn createMigratedAttributeMap(session);\n\t}\n\n\t@Override\n\tfinal HttpSession applySessionFixation(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession();\n\t\tString originalSessionId = session.getId();\n\t\tthis.logger.debug(LogMessage.of(() -> \"Invalidating session with Id '\" + originalSessionId + \"' \"\n\t\t\t\t+ (this.migrateSessionAttributes ? \"and\" : \"without\") + \" migrating attributes.\"));\n\t\tMap<String, Object> attributesToMigrate = extractAttributes(session);\n\t\tint maxInactiveIntervalToMigrate = session.getMaxInactiveInterval();\n\t\tsession.invalidate();\n\t\tsession = request.getSession(true); // we now have a new session\n\t\tthis.logger.debug(LogMessage.format(\"Started new session: %s\", session.getId()));\n\t\ttransferAttributes(attributesToMigrate, session);\n\t\tif (this.migrateSessionAttributes) {\n\t\t\tsession.setMaxInactiveInterval(maxInactiveIntervalToMigrate);\n\t\t}\n\t\treturn session;\n\t}\n\n\t/**\n\t * @param attributes the attributes which were extracted from the original session by\n\t * {@code extractAttributes}\n\t * @param newSession the newly created session\n\t */\n\tvoid transferAttributes(Map<String, Object> attributes, HttpSession newSession) {\n\t\tif (attributes != null) {\n\t\t\tattributes.forEach(newSession::setAttribute);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate HashMap<String, Object> createMigratedAttributeMap(HttpSession session) {\n\t\tHashMap<String, Object> attributesToMigrate = new HashMap<>();\n\t\tEnumeration<String> enumeration = session.getAttributeNames();\n\t\twhile (enumeration.hasMoreElements()) {\n\t\t\tString key = enumeration.nextElement();\n\t\t\tif (!this.migrateSessionAttributes && !key.startsWith(\"SPRING_SECURITY_\")) {\n\t\t\t\t// Only retain Spring Security attributes\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tattributesToMigrate.put(key, session.getAttribute(key));\n\t\t}\n\t\treturn attributesToMigrate;\n\t}\n\n\t/**\n\t * Defines whether attributes should be migrated to a new session or not. Has no\n\t * effect if you override the {@code extractAttributes} method.\n\t * <p>\n\t * Attributes used by Spring Security (to store cached requests, for example) will\n\t * still be retained by default, even if you set this value to {@code false}.\n\t * @param migrateSessionAttributes whether the attributes from the session should be\n\t * transferred to the new, authenticated session.\n\t */\n\tpublic void setMigrateSessionAttributes(boolean migrateSessionAttributes) {\n\t\tthis.migrateSessionAttributes = migrateSessionAttributes;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/SessionLimit.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport java.util.function.Function;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * Represents the maximum number of sessions allowed. Use {@link #UNLIMITED} to indicate\n * that there is no limit.\n *\n * @author Claudenir Freitas\n * @since 6.5\n */\npublic interface SessionLimit extends Function<Authentication, Integer> {\n\n\t/**\n\t * Represents unlimited sessions.\n\t */\n\tSessionLimit UNLIMITED = (authentication) -> -1;\n\n\t/**\n\t * Creates a {@link SessionLimit} that always returns the given value for any user\n\t * @param maxSessions the maximum number of sessions allowed\n\t * @return a {@link SessionLimit} instance that returns the given value.\n\t */\n\tstatic SessionLimit of(int maxSessions) {\n\t\tAssert.isTrue(maxSessions != 0,\n\t\t\t\t\"MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum\");\n\t\treturn (authentication) -> maxSessions;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/session/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Strategy interface and implementations for handling session-related behaviour for a\n * newly authenticated user.\n * <p>\n * Comes with support for:\n * <ul>\n * <li>Protection against session-fixation attacks</li>\n * <li>Controlling the number of sessions an authenticated user can have open</li>\n * </ul>\n */\n@NullMarked\npackage org.springframework.security.web.authentication.session;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/switchuser/AuthenticationSwitchUserEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.switchuser;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.event.AbstractAuthenticationEvent;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * Application event which indicates that a user context switch.\n *\n * @author Mark St.Godard\n */\npublic class AuthenticationSwitchUserEvent extends AbstractAuthenticationEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 6265996480231793939L;\n\n\tprivate final @Nullable UserDetails targetUser;\n\n\t/**\n\t * Switch user context event constructor\n\t * @param authentication The current <code>Authentication</code> object\n\t * @param targetUser The target user\n\t */\n\tpublic AuthenticationSwitchUserEvent(Authentication authentication, @Nullable UserDetails targetUser) {\n\t\tsuper(authentication);\n\t\tthis.targetUser = targetUser;\n\t}\n\n\tpublic @Nullable UserDetails getTargetUser() {\n\t\treturn this.targetUser;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserAuthorityChanger.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.switchuser;\n\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * Allows subclasses to modify the {@link GrantedAuthority} list that will be assigned to\n * the principal when they assume the identity of a different principal.\n *\n * <p>\n * Configured against the {@link SwitchUserFilter}.\n *\n * @author Ben Alex\n *\n */\npublic interface SwitchUserAuthorityChanger {\n\n\t/**\n\t * Allow subclasses to add or remove authorities that will be granted when in switch\n\t * user mode.\n\t * @param targetUser the UserDetails representing the identity being switched to\n\t * @param currentAuthentication the current Authentication of the principal performing\n\t * the switching\n\t * @param authoritiesToBeGranted all\n\t * {@link org.springframework.security.core.GrantedAuthority} instances to be granted\n\t * to the user, excluding the special \"switch user\" authority that is used internally\n\t * (guaranteed never null)\n\t * @return the modified list of granted authorities.\n\t */\n\tCollection<? extends GrantedAuthority> modifyGrantedAuthorities(UserDetails targetUser,\n\t\t\t@Nullable Authentication currentAuthentication,\n\t\t\tCollection<? extends GrantedAuthority> authoritiesToBeGranted);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.switchuser;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Switch User processing filter responsible for user context switching.\n * <p>\n * This filter is similar to Unix 'su' however for Spring Security-managed web\n * applications. A common use-case for this feature is the ability to allow\n * higher-authority users (e.g. ROLE_ADMIN) to switch to a regular user (e.g. ROLE_USER).\n * <p>\n * This filter assumes that the user performing the switch will be required to be logged\n * in as normal (i.e. as a ROLE_ADMIN user). The user will then access a page/controller\n * that enables the administrator to specify who they wish to become (see\n * <code>switchUserUrl</code>).\n * <p>\n * <b>Note: This URL will be required to have appropriate security constraints configured\n * so that only users of that role can access it (e.g. ROLE_ADMIN).</b>\n * <p>\n * On a successful switch, the user's <code>SecurityContext</code> will be updated to\n * reflect the specified user and will also contain an additional\n * {@link org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority}\n * which contains the original user. Before switching, a check will be made on whether the\n * user is already currently switched, and any current switch will be exited to prevent\n * \"nested\" switches.\n * <p>\n * To 'exit' from a user context, the user needs to access a URL (see\n * <code>exitUserUrl</code>) that will switch back to the original user as identified by\n * the <code>ROLE_PREVIOUS_ADMINISTRATOR</code>.\n * <p>\n * To configure the Switch User Processing Filter, create a bean definition for the Switch\n * User processing filter and add to the filterChainProxy. Note that the filter must come\n * <b>after</b> the <tt>FilterSecurityInterceptor</tt> in the chain, in order to apply the\n * correct constraints to the <tt>switchUserUrl</tt>. Example:\n *\n * <pre>\n * &lt;bean id=\"switchUserProcessingFilter\" class=\"org.springframework.security.web.authentication.switchuser.SwitchUserFilter\"&gt;\n *    &lt;property name=\"userDetailsService\" ref=\"userDetailsService\" /&gt;\n *    &lt;property name=\"switchUserUrl\" value=\"/login/impersonate\" /&gt;\n *    &lt;property name=\"exitUserUrl\" value=\"/logout/impersonate\" /&gt;\n *    &lt;property name=\"targetUrl\" value=\"/index.jsp\" /&gt;\n * &lt;/bean&gt;\n * </pre>\n *\n * @author Mark St.Godard\n * @see SwitchUserGrantedAuthority\n */\npublic class SwitchUserFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {\n\n\tpublic static final String SPRING_SECURITY_SWITCH_USERNAME_KEY = \"username\";\n\n\tpublic static final String ROLE_PREVIOUS_ADMINISTRATOR = \"ROLE_PREVIOUS_ADMINISTRATOR\";\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable ApplicationEventPublisher eventPublisher;\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate RequestMatcher exitUserMatcher = createMatcher(\"/logout/impersonate\");\n\n\tprivate RequestMatcher switchUserMatcher = createMatcher(\"/login/impersonate\");\n\n\tprivate @Nullable String targetUrl;\n\n\tprivate @Nullable String switchFailureUrl;\n\n\tprivate String usernameParameter = SPRING_SECURITY_SWITCH_USERNAME_KEY;\n\n\tprivate String switchAuthorityRole = ROLE_PREVIOUS_ADMINISTRATOR;\n\n\tprivate @Nullable SwitchUserAuthorityChanger switchUserAuthorityChanger;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate UserDetailsService userDetailsService;\n\n\tprivate UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate AuthenticationSuccessHandler successHandler;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate AuthenticationFailureHandler failureHandler;\n\n\tprivate SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.userDetailsService, \"userDetailsService must be specified\");\n\t\tAssert.isTrue(this.successHandler != null || this.targetUrl != null,\n\t\t\t\t\"You must set either a successHandler or the targetUrl\");\n\t\tif (this.targetUrl != null) {\n\t\t\tAssert.isNull(this.successHandler, \"You cannot set both successHandler and targetUrl\");\n\t\t\tthis.successHandler = new SimpleUrlAuthenticationSuccessHandler(this.targetUrl);\n\t\t}\n\t\tif (this.failureHandler == null) {\n\t\t\tthis.failureHandler = (this.switchFailureUrl != null)\n\t\t\t\t\t? new SimpleUrlAuthenticationFailureHandler(this.switchFailureUrl)\n\t\t\t\t\t: new SimpleUrlAuthenticationFailureHandler();\n\t\t}\n\t\telse {\n\t\t\tAssert.isNull(this.switchFailureUrl, \"You cannot set both a switchFailureUrl and a failureHandler\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\t// check for switch or exit request\n\t\tif (requiresSwitchUser(request)) {\n\t\t\t// if set, attempt switch and store original\n\t\t\ttry {\n\t\t\t\tAuthentication targetUser = attemptSwitchUser(request);\n\t\t\t\t// update the current context to the new target user\n\t\t\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\t\tcontext.setAuthentication(targetUser);\n\t\t\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Set SecurityContextHolder to %s\", targetUser));\n\t\t\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\t\t\t// redirect to target url\n\t\t\t\tthis.successHandler.onAuthenticationSuccess(request, response, targetUser);\n\t\t\t}\n\t\t\tcatch (AuthenticationException ex) {\n\t\t\t\tthis.logger.debug(\"Failed to switch user\", ex);\n\t\t\t\tthis.failureHandler.onAuthenticationFailure(request, response, ex);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (requiresExitUser(request)) {\n\t\t\t// get the original authentication object (if exists)\n\t\t\tAuthentication originalUser = attemptExitUser(request);\n\t\t\t// update the current context back to the original user\n\t\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\tcontext.setAuthentication(originalUser);\n\t\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\t\tthis.logger.debug(LogMessage.format(\"Set SecurityContextHolder to %s\", originalUser));\n\t\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\t\t// redirect to target url\n\t\t\tthis.successHandler.onAuthenticationSuccess(request, response, originalUser);\n\t\t\treturn;\n\t\t}\n\t\tthis.logger.trace(LogMessage.format(\"Did not attempt to switch user since request did not match [%s] or [%s]\",\n\t\t\t\tthis.switchUserMatcher, this.exitUserMatcher));\n\t\tchain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Attempt to switch to another user. If the user does not exist or is not active,\n\t * return null.\n\t * @return The new <code>Authentication</code> request if successfully switched to\n\t * another user, <code>null</code> otherwise.\n\t * @throws UsernameNotFoundException If the target user is not found.\n\t * @throws LockedException if the account is locked.\n\t * @throws DisabledException If the target user is disabled.\n\t * @throws AccountExpiredException If the target user account is expired.\n\t * @throws CredentialsExpiredException If the target user credentials are expired.\n\t */\n\tprotected Authentication attemptSwitchUser(HttpServletRequest request) throws AuthenticationException {\n\t\tUsernamePasswordAuthenticationToken targetUserRequest;\n\t\tString username = request.getParameter(this.usernameParameter);\n\t\tusername = (username != null) ? username : \"\";\n\t\tthis.logger.debug(LogMessage.format(\"Attempting to switch to user [%s]\", username));\n\t\tUserDetails targetUser = this.userDetailsService.loadUserByUsername(username);\n\t\tthis.userDetailsChecker.check(targetUser);\n\t\t// OK, create the switch user token\n\t\ttargetUserRequest = createSwitchUserToken(request, targetUser);\n\t\t// publish event\n\t\tif (this.eventPublisher != null) {\n\t\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (authentication != null) {\n\t\t\t\tthis.eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(authentication, targetUser));\n\t\t\t}\n\t\t}\n\t\treturn targetUserRequest;\n\t}\n\n\t/**\n\t * Attempt to exit from an already switched user.\n\t * @param request The http servlet request\n\t * @return The original <code>Authentication</code> object or <code>null</code>\n\t * otherwise.\n\t * @throws AuthenticationCredentialsNotFoundException If no\n\t * <code>Authentication</code> associated with this request.\n\t */\n\tprotected Authentication attemptExitUser(HttpServletRequest request)\n\t\t\tthrows AuthenticationCredentialsNotFoundException {\n\t\t// need to check to see if the current user has a SwitchUserGrantedAuthority\n\t\tAuthentication current = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (current == null) {\n\t\t\tthrow new AuthenticationCredentialsNotFoundException(this.messages\n\t\t\t\t.getMessage(\"SwitchUserFilter.noCurrentUser\", \"No current user associated with this request\"));\n\t\t}\n\t\t// check to see if the current user did actual switch to another user\n\t\t// if so, get the original source user so we can switch back\n\t\tAuthentication original = getSourceAuthentication(current);\n\t\tif (original == null) {\n\t\t\tthis.logger.debug(\"Failed to find original user\");\n\t\t\tthrow new AuthenticationCredentialsNotFoundException(this.messages\n\t\t\t\t.getMessage(\"SwitchUserFilter.noOriginalAuthentication\", \"Failed to find original user\"));\n\t\t}\n\t\t// get the source user details\n\t\tUserDetails originalUser = null;\n\t\tObject obj = original.getPrincipal();\n\t\tif ((obj != null) && obj instanceof UserDetails) {\n\t\t\toriginalUser = (UserDetails) obj;\n\t\t}\n\t\t// publish event\n\t\tif (this.eventPublisher != null) {\n\t\t\tthis.eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(current, originalUser));\n\t\t}\n\t\treturn original;\n\t}\n\n\t/**\n\t * Create a switch user token that contains an additional <tt>GrantedAuthority</tt>\n\t * that contains the original <code>Authentication</code> object.\n\t * @param request The http servlet request.\n\t * @param targetUser The target user\n\t * @return The authentication token\n\t *\n\t * @see SwitchUserGrantedAuthority\n\t */\n\tprivate UsernamePasswordAuthenticationToken createSwitchUserToken(HttpServletRequest request,\n\t\t\tUserDetails targetUser) {\n\t\tUsernamePasswordAuthenticationToken targetUserRequest;\n\t\t// grant an additional authority that contains the original Authentication object\n\t\t// which will be used to 'exit' from the current switched user.\n\t\tAuthentication currentAuthentication = getCurrentAuthentication(request);\n\t\tAssert.notNull(currentAuthentication, \"currentAuthentication cannot be null\");\n\t\tGrantedAuthority switchAuthority = new SwitchUserGrantedAuthority(this.switchAuthorityRole,\n\t\t\t\tcurrentAuthentication);\n\t\t// get the original authorities\n\t\tCollection<? extends GrantedAuthority> orig = targetUser.getAuthorities();\n\t\t// Allow subclasses to change the authorities to be granted\n\t\tif (this.switchUserAuthorityChanger != null) {\n\t\t\torig = this.switchUserAuthorityChanger.modifyGrantedAuthorities(targetUser, currentAuthentication, orig);\n\t\t}\n\t\t// add the new switch user authority\n\t\tList<GrantedAuthority> newAuths = new ArrayList<>(orig);\n\t\tnewAuths.add(switchAuthority);\n\t\t// create the new authentication token\n\t\ttargetUserRequest = UsernamePasswordAuthenticationToken.authenticated(targetUser, targetUser.getPassword(),\n\t\t\t\tnewAuths);\n\t\t// set details\n\t\ttargetUserRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\treturn targetUserRequest;\n\t}\n\n\tprivate @Nullable Authentication getCurrentAuthentication(HttpServletRequest request) {\n\t\ttry {\n\t\t\t// SEC-1763. Check first if we are already switched.\n\t\t\treturn attemptExitUser(request);\n\t\t}\n\t\tcatch (AuthenticationCredentialsNotFoundException ex) {\n\t\t\treturn this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t}\n\t}\n\n\t/**\n\t * Find the original <code>Authentication</code> object from the current user's\n\t * granted authorities. A successfully switched user should have a\n\t * <code>SwitchUserGrantedAuthority</code> that contains the original source user\n\t * <code>Authentication</code> object.\n\t * @param current The current <code>Authentication</code> object\n\t * @return The source user <code>Authentication</code> object or <code>null</code>\n\t * otherwise.\n\t */\n\tprivate @Nullable Authentication getSourceAuthentication(Authentication current) {\n\t\tAuthentication original = null;\n\t\t// iterate over granted authorities and find the 'switch user' authority\n\t\tCollection<? extends GrantedAuthority> authorities = current.getAuthorities();\n\t\tfor (GrantedAuthority auth : authorities) {\n\t\t\t// check for switch user type of authority\n\t\t\tif (auth instanceof SwitchUserGrantedAuthority) {\n\t\t\t\toriginal = ((SwitchUserGrantedAuthority) auth).getSource();\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Found original switch user granted authority [%s]\", original));\n\t\t\t}\n\t\t}\n\t\treturn original;\n\t}\n\n\t/**\n\t * Checks the request URI for the presence of <tt>exitUserUrl</tt>.\n\t * @param request The http servlet request\n\t * @return <code>true</code> if the request requires a exit user, <code>false</code>\n\t * otherwise.\n\t *\n\t * @see SwitchUserFilter#setExitUserUrl(String)\n\t */\n\tprotected boolean requiresExitUser(HttpServletRequest request) {\n\t\treturn this.exitUserMatcher.matches(request);\n\t}\n\n\t/**\n\t * Checks the request URI for the presence of <tt>switchUserUrl</tt>.\n\t * @param request The http servlet request\n\t * @return <code>true</code> if the request requires a switch, <code>false</code>\n\t * otherwise.\n\t *\n\t * @see SwitchUserFilter#setSwitchUserUrl(String)\n\t */\n\tprotected boolean requiresSwitchUser(HttpServletRequest request) {\n\t\treturn this.switchUserMatcher.matches(request);\n\t}\n\n\t@Override\n\tpublic void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) throws BeansException {\n\t\tthis.eventPublisher = eventPublisher;\n\t}\n\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"AuthenticationDetailsSource required\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tAssert.notNull(messageSource, \"messageSource cannot be null\");\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\t/**\n\t * Sets the authentication data access object.\n\t * @param userDetailsService The <tt>UserDetailsService</tt> which will be used to\n\t * load information for the user that is being switched to.\n\t */\n\tpublic void setUserDetailsService(UserDetailsService userDetailsService) {\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t/**\n\t * Set the URL to respond to exit user processing. This is a shortcut for\n\t * {@link #setExitUserMatcher(RequestMatcher)}.\n\t * @param exitUserUrl The exit user URL.\n\t */\n\tpublic void setExitUserUrl(String exitUserUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(exitUserUrl),\n\t\t\t\t\"exitUserUrl cannot be empty and must be a valid redirect URL\");\n\t\tthis.exitUserMatcher = createMatcher(exitUserUrl);\n\t}\n\n\t/**\n\t * Set the matcher to respond to exit user processing.\n\t * @param exitUserMatcher The exit matcher to use.\n\t */\n\tpublic void setExitUserMatcher(RequestMatcher exitUserMatcher) {\n\t\tAssert.notNull(exitUserMatcher, \"exitUserMatcher cannot be null\");\n\t\tthis.exitUserMatcher = exitUserMatcher;\n\t}\n\n\t/**\n\t * Set the URL to respond to switch user processing. This is a shortcut for\n\t * {@link #setSwitchUserMatcher(RequestMatcher)}\n\t * @param switchUserUrl The switch user URL.\n\t */\n\tpublic void setSwitchUserUrl(String switchUserUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(switchUserUrl),\n\t\t\t\t\"switchUserUrl cannot be empty and must be a valid redirect URL\");\n\t\tthis.switchUserMatcher = createMatcher(switchUserUrl);\n\t}\n\n\t/**\n\t * Set the matcher to respond to switch user processing.\n\t * @param switchUserMatcher The switch user matcher.\n\t */\n\tpublic void setSwitchUserMatcher(RequestMatcher switchUserMatcher) {\n\t\tAssert.notNull(switchUserMatcher, \"switchUserMatcher cannot be null\");\n\t\tthis.switchUserMatcher = switchUserMatcher;\n\t}\n\n\t/**\n\t * Sets the URL to go to after a successful switch / exit user request. Use\n\t * {@link #setSuccessHandler(AuthenticationSuccessHandler) setSuccessHandler} instead\n\t * if you need more customized behaviour.\n\t * @param targetUrl The target url.\n\t */\n\tpublic void setTargetUrl(String targetUrl) {\n\t\tthis.targetUrl = targetUrl;\n\t}\n\n\t/**\n\t * Used to define custom behaviour on a successful switch or exit user.\n\t * <p>\n\t * Can be used instead of setting <tt>targetUrl</tt>.\n\t */\n\tpublic void setSuccessHandler(AuthenticationSuccessHandler successHandler) {\n\t\tAssert.notNull(successHandler, \"successHandler cannot be null\");\n\t\tthis.successHandler = successHandler;\n\t}\n\n\t/**\n\t * Sets the URL to which a user should be redirected if the switch fails. For example,\n\t * this might happen because the account they are attempting to switch to is invalid\n\t * (the user doesn't exist, account is locked etc).\n\t * <p>\n\t * If not set, an error message will be written to the response.\n\t * <p>\n\t * Use {@link #setFailureHandler(AuthenticationFailureHandler) failureHandler} instead\n\t * if you need more customized behaviour.\n\t * @param switchFailureUrl the url to redirect to.\n\t */\n\tpublic void setSwitchFailureUrl(String switchFailureUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(switchFailureUrl), \"switchFailureUrl must be a valid redirect URL\");\n\t\tthis.switchFailureUrl = switchFailureUrl;\n\t}\n\n\t/**\n\t * Used to define custom behaviour when a switch fails.\n\t * <p>\n\t * Can be used instead of setting <tt>switchFailureUrl</tt>.\n\t */\n\tpublic void setFailureHandler(AuthenticationFailureHandler failureHandler) {\n\t\tAssert.notNull(failureHandler, \"failureHandler cannot be null\");\n\t\tthis.failureHandler = failureHandler;\n\t}\n\n\t/**\n\t * @param switchUserAuthorityChanger to use to fine-tune the authorities granted to\n\t * subclasses (may be null if SwitchUserFilter should not fine-tune the authorities)\n\t */\n\tpublic void setSwitchUserAuthorityChanger(SwitchUserAuthorityChanger switchUserAuthorityChanger) {\n\t\tthis.switchUserAuthorityChanger = switchUserAuthorityChanger;\n\t}\n\n\t/**\n\t * Sets the {@link UserDetailsChecker} that is called on the target user whenever the\n\t * user is switched.\n\t * @param userDetailsChecker the {@link UserDetailsChecker} that checks the status of\n\t * the user that is being switched to. Defaults to\n\t * {@link AccountStatusUserDetailsChecker}.\n\t */\n\tpublic void setUserDetailsChecker(UserDetailsChecker userDetailsChecker) {\n\t\tthis.userDetailsChecker = userDetailsChecker;\n\t}\n\n\t/**\n\t * Allows the parameter containing the username to be customized.\n\t * @param usernameParameter the parameter name. Defaults to {@code username}\n\t */\n\tpublic void setUsernameParameter(String usernameParameter) {\n\t\tthis.usernameParameter = usernameParameter;\n\t}\n\n\t/**\n\t * Allows the role of the switchAuthority to be customized.\n\t * @param switchAuthorityRole the role name. Defaults to\n\t * {@link #ROLE_PREVIOUS_ADMINISTRATOR}\n\t */\n\tpublic void setSwitchAuthorityRole(String switchAuthorityRole) {\n\t\tAssert.notNull(switchAuthorityRole, \"switchAuthorityRole cannot be null\");\n\t\tthis.switchAuthorityRole = switchAuthorityRole;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * switch user success. The default is\n\t * {@link RequestAttributeSecurityContextRepository}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t * @since 5.7.7\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\tprivate static RequestMatcher createMatcher(String pattern) {\n\t\treturn pathPattern(HttpMethod.POST, pattern);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserGrantedAuthority.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.switchuser;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.util.Assert;\n\n/**\n * Custom {@code GrantedAuthority} used by\n * {@link org.springframework.security.web.authentication.switchuser.SwitchUserFilter}\n * <p>\n * Stores the {@code Authentication} object of the original user to be used later when\n * 'exiting' from a user switch.\n *\n * @author Mark St.Godard\n * @see org.springframework.security.web.authentication.switchuser.SwitchUserFilter\n */\npublic final class SwitchUserGrantedAuthority implements GrantedAuthority {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String role;\n\n\tprivate final Authentication source;\n\n\tpublic SwitchUserGrantedAuthority(String role, Authentication source) {\n\t\tAssert.notNull(role, \"role cannot be null\");\n\t\tAssert.notNull(source, \"source cannot be null\");\n\t\tthis.role = role;\n\t\tthis.source = source;\n\t}\n\n\t/**\n\t * Returns the original user associated with a successful user switch.\n\t * @return The original <code>Authentication</code> object of the switched user.\n\t */\n\tpublic Authentication getSource() {\n\t\treturn this.source;\n\t}\n\n\t@Override\n\tpublic String getAuthority() {\n\t\treturn this.role;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj instanceof SwitchUserGrantedAuthority swa) {\n\t\t\treturn this.role.equals(swa.getAuthority()) && this.source.equals(swa.getSource());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = this.role.hashCode();\n\t\tresult = 31 * result + this.source.hashCode();\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Switch User Authority [\" + this.role + \",\" + this.source + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/switchuser/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Provides HTTP-based \"switch user\" (su) capabilities.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.switchuser;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * For internal use with namespace configuration in the case where a user doesn't\n * configure a login page. The configuration code will insert this filter in the chain\n * instead.\n *\n * Will only work if a redirect is used to the login page.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class DefaultLoginPageGeneratingFilter extends GenericFilterBean {\n\n\tpublic static final String DEFAULT_LOGIN_PAGE_URL = \"/login\";\n\n\tpublic static final String ERROR_PARAMETER_NAME = \"error\";\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable String loginPageUrl;\n\n\tprivate @Nullable String logoutSuccessUrl;\n\n\tprivate @Nullable String failureUrl;\n\n\tprivate boolean formLoginEnabled;\n\n\tprivate boolean oauth2LoginEnabled;\n\n\tprivate boolean saml2LoginEnabled;\n\n\tprivate boolean passkeysEnabled;\n\n\tprivate boolean oneTimeTokenEnabled;\n\n\tprivate @Nullable String authenticationUrl;\n\n\tprivate @Nullable String generateOneTimeTokenUrl;\n\n\tprivate @Nullable String usernameParameter;\n\n\tprivate @Nullable String passwordParameter;\n\n\tprivate @Nullable String rememberMeParameter;\n\n\tprivate final String factorTypeParameter = \"factor.type\";\n\n\tprivate final String factorReasonParameter = \"factor.reason\";\n\n\tprivate final List<String> allowedParameters = List.of(this.factorTypeParameter, this.factorReasonParameter);\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate Map<String, String> oauth2AuthenticationUrlToClientName;\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate Map<String, String> saml2AuthenticationUrlToProviderName;\n\n\tprivate Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = (request) -> Collections.emptyMap();\n\n\tprivate Function<HttpServletRequest, Map<String, String>> resolveHeaders = (request) -> Collections.emptyMap();\n\n\tpublic DefaultLoginPageGeneratingFilter() {\n\t}\n\n\tpublic DefaultLoginPageGeneratingFilter(UsernamePasswordAuthenticationFilter authFilter) {\n\t\tthis.loginPageUrl = DEFAULT_LOGIN_PAGE_URL;\n\t\tthis.logoutSuccessUrl = DEFAULT_LOGIN_PAGE_URL + \"?logout\";\n\t\tthis.failureUrl = DEFAULT_LOGIN_PAGE_URL + \"?\" + ERROR_PARAMETER_NAME;\n\t\tif (authFilter != null) {\n\t\t\tinitAuthFilter(authFilter);\n\t\t}\n\t}\n\n\tprivate void initAuthFilter(UsernamePasswordAuthenticationFilter authFilter) {\n\t\tthis.formLoginEnabled = true;\n\t\tthis.usernameParameter = authFilter.getUsernameParameter();\n\t\tthis.passwordParameter = authFilter.getPasswordParameter();\n\t\tif (authFilter.getRememberMeServices() instanceof AbstractRememberMeServices rememberMeServices) {\n\t\t\tthis.rememberMeParameter = rememberMeServices.getParameter();\n\t\t}\n\t}\n\n\t/**\n\t * Use this {@link SecurityContextHolderStrategy} to retrieve authenticated users.\n\t * <p>\n\t * Uses {@link SecurityContextHolder#getContextHolderStrategy()} by default.\n\t * @param securityContextHolderStrategy the strategy to use\n\t * @since 7.0\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Sets a Function used to resolve a Map of the hidden inputs where the key is the\n\t * name of the input and the value is the value of the input. Typically this is used\n\t * to resolve the CSRF token.\n\t * @param resolveHiddenInputs the function to resolve the inputs\n\t */\n\tpublic void setResolveHiddenInputs(Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs) {\n\t\tAssert.notNull(resolveHiddenInputs, \"resolveHiddenInputs cannot be null\");\n\t\tthis.resolveHiddenInputs = resolveHiddenInputs;\n\t}\n\n\t/**\n\t * Sets a Function used to resolve a Map of the HTTP headers where the key is the name\n\t * of the header and the value is the value of the header. Typically, this is used to\n\t * resolve the CSRF token.\n\t * @param resolveHeaders the function to resolve the headers\n\t */\n\tpublic void setResolveHeaders(Function<HttpServletRequest, Map<String, String>> resolveHeaders) {\n\t\tAssert.notNull(resolveHeaders, \"resolveHeaders cannot be null\");\n\t\tthis.resolveHeaders = resolveHeaders;\n\t}\n\n\tpublic boolean isEnabled() {\n\t\treturn this.formLoginEnabled || this.oauth2LoginEnabled || this.saml2LoginEnabled || this.oneTimeTokenEnabled;\n\t}\n\n\tpublic void setLogoutSuccessUrl(String logoutSuccessUrl) {\n\t\tthis.logoutSuccessUrl = logoutSuccessUrl;\n\t}\n\n\tpublic @Nullable String getLoginPageUrl() {\n\t\treturn this.loginPageUrl;\n\t}\n\n\tpublic void setLoginPageUrl(String loginPageUrl) {\n\t\tthis.loginPageUrl = loginPageUrl;\n\t}\n\n\tpublic void setFailureUrl(String failureUrl) {\n\t\tthis.failureUrl = failureUrl;\n\t}\n\n\tpublic void setFormLoginEnabled(boolean formLoginEnabled) {\n\t\tthis.formLoginEnabled = formLoginEnabled;\n\t}\n\n\tpublic void setOauth2LoginEnabled(boolean oauth2LoginEnabled) {\n\t\tthis.oauth2LoginEnabled = oauth2LoginEnabled;\n\t}\n\n\tpublic void setOneTimeTokenEnabled(boolean oneTimeTokenEnabled) {\n\t\tthis.oneTimeTokenEnabled = oneTimeTokenEnabled;\n\t}\n\n\tpublic void setSaml2LoginEnabled(boolean saml2LoginEnabled) {\n\t\tthis.saml2LoginEnabled = saml2LoginEnabled;\n\t}\n\n\tpublic void setPasskeysEnabled(boolean passkeysEnabled) {\n\t\tthis.passkeysEnabled = passkeysEnabled;\n\t}\n\n\tpublic void setAuthenticationUrl(String authenticationUrl) {\n\t\tthis.authenticationUrl = authenticationUrl;\n\t}\n\n\tpublic void setOneTimeTokenGenerationUrl(String generateOneTimeTokenUrl) {\n\t\tthis.generateOneTimeTokenUrl = generateOneTimeTokenUrl;\n\t}\n\n\tpublic void setUsernameParameter(String usernameParameter) {\n\t\tthis.usernameParameter = usernameParameter;\n\t}\n\n\tpublic void setPasswordParameter(String passwordParameter) {\n\t\tthis.passwordParameter = passwordParameter;\n\t}\n\n\tpublic void setRememberMeParameter(String rememberMeParameter) {\n\t\tthis.rememberMeParameter = rememberMeParameter;\n\t}\n\n\tpublic void setOauth2AuthenticationUrlToClientName(Map<String, String> oauth2AuthenticationUrlToClientName) {\n\t\tthis.oauth2AuthenticationUrlToClientName = oauth2AuthenticationUrlToClientName;\n\t}\n\n\tpublic void setSaml2AuthenticationUrlToProviderName(Map<String, String> saml2AuthenticationUrlToProviderName) {\n\t\tthis.saml2AuthenticationUrlToProviderName = saml2AuthenticationUrlToProviderName;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tboolean loginError = isErrorPage(request);\n\t\tboolean logoutSuccess = isLogoutSuccess(request);\n\t\tif (isLoginUrlRequest(request) || loginError || logoutSuccess) {\n\t\t\tString loginPageHtml = generateLoginPageHtml(request, loginError, logoutSuccess);\n\t\t\tresponse.setContentType(\"text/html;charset=UTF-8\");\n\t\t\tresponse.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);\n\t\t\tresponse.getWriter().write(loginPageHtml);\n\t\t\treturn;\n\t\t}\n\t\tchain.doFilter(request, response);\n\t}\n\n\tprivate String generateLoginPageHtml(HttpServletRequest request, boolean loginError, boolean logoutSuccess) {\n\t\tString errorMsg = \"Invalid credentials\";\n\t\tString contextPath = request.getContextPath();\n\n\t\tHtmlTemplates.Builder builder = HtmlTemplates.fromTemplate(LOGIN_PAGE_TEMPLATE)\n\t\t\t.withRawHtml(\"contextPath\", contextPath)\n\t\t\t.withRawHtml(\"javaScript\", \"\")\n\t\t\t.withRawHtml(\"formLogin\", \"\")\n\t\t\t.withRawHtml(\"oneTimeTokenLogin\", \"\")\n\t\t\t.withRawHtml(\"oauth2Login\", \"\")\n\t\t\t.withRawHtml(\"saml2Login\", \"\")\n\t\t\t.withRawHtml(\"passkeyLogin\", \"\");\n\n\t\tPredicate<String> wantsAuthority = wantsAuthority(request);\n\t\tif (wantsAuthority.test(\"webauthn\")) {\n\t\t\tbuilder.withRawHtml(\"javaScript\", renderJavaScript(request, contextPath))\n\t\t\t\t.withRawHtml(\"passkeyLogin\", renderPasskeyLogin());\n\t\t}\n\t\tif (wantsAuthority.test(\"password\")) {\n\t\t\tbuilder.withRawHtml(\"formLogin\",\n\t\t\t\t\trenderFormLogin(request, loginError, logoutSuccess, contextPath, errorMsg));\n\t\t}\n\t\tif (wantsAuthority.test(\"ott\")) {\n\t\t\tbuilder.withRawHtml(\"oneTimeTokenLogin\",\n\t\t\t\t\trenderOneTimeTokenLogin(request, loginError, logoutSuccess, contextPath, errorMsg));\n\t\t}\n\t\tif (wantsAuthority.test(\"authorization_code\")) {\n\t\t\tbuilder.withRawHtml(\"oauth2Login\", renderOAuth2Login(loginError, logoutSuccess, errorMsg, contextPath));\n\t\t}\n\t\tif (wantsAuthority.test(\"saml_response\")) {\n\t\t\tbuilder.withRawHtml(\"saml2Login\", renderSaml2Login(loginError, logoutSuccess, errorMsg, contextPath));\n\t\t}\n\t\treturn builder.render();\n\t}\n\n\tprivate Predicate<String> wantsAuthority(HttpServletRequest request) {\n\t\tString[] authorities = request.getParameterValues(this.factorTypeParameter);\n\t\tif (authorities == null) {\n\t\t\treturn (authority) -> true;\n\t\t}\n\t\treturn List.of(authorities)::contains;\n\t}\n\n\tprivate String renderJavaScript(HttpServletRequest request, String contextPath) {\n\t\tif (this.passkeysEnabled) {\n\t\t\treturn HtmlTemplates.fromTemplate(PASSKEY_SCRIPT_TEMPLATE)\n\t\t\t\t.withValue(\"loginPageUrl\", this.loginPageUrl)\n\t\t\t\t.withValue(\"contextPath\", contextPath)\n\t\t\t\t.withRawHtml(\"csrfHeaders\", renderHeaders(request))\n\t\t\t\t.render();\n\t\t}\n\t\treturn \"\";\n\t}\n\n\tprivate String renderPasskeyLogin() {\n\t\tif (this.passkeysEnabled) {\n\t\t\treturn PASSKEY_FORM_TEMPLATE;\n\t\t}\n\t\treturn \"\";\n\t}\n\n\tprivate String renderHeaders(HttpServletRequest request) {\n\t\tStringBuffer javascriptHeadersEntries = new StringBuffer();\n\t\tMap<String, String> headers = this.resolveHeaders.apply(request);\n\t\tfor (Map.Entry<String, String> header : headers.entrySet()) {\n\t\t\tjavascriptHeadersEntries.append(HtmlTemplates.fromTemplate(CSRF_HEADERS)\n\t\t\t\t.withValue(\"headerName\", header.getKey())\n\t\t\t\t.withValue(\"headerValue\", header.getValue())\n\t\t\t\t.render());\n\t\t}\n\t\treturn javascriptHeadersEntries.toString();\n\t}\n\n\tprivate String renderFormLogin(HttpServletRequest request, boolean loginError, boolean logoutSuccess,\n\t\t\tString contextPath, String errorMsg) {\n\t\tif (!this.formLoginEnabled) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tString username = getUsername();\n\t\tString usernameInput = ((username != null)\n\t\t\t\t? HtmlTemplates.fromTemplate(FORM_READONLY_USERNAME_INPUT).withValue(\"username\", username)\n\t\t\t\t: HtmlTemplates.fromTemplate(FORM_USERNAME_INPUT))\n\t\t\t.withValue(\"usernameParameter\", this.usernameParameter)\n\t\t\t.render();\n\n\t\tString hiddenInputs = this.resolveHiddenInputs.apply(request)\n\t\t\t.entrySet()\n\t\t\t.stream()\n\t\t\t.map((inputKeyValue) -> renderHiddenInput(inputKeyValue.getKey(), inputKeyValue.getValue()))\n\t\t\t.collect(Collectors.joining(\"\\n\"));\n\n\t\treturn HtmlTemplates.fromTemplate(LOGIN_FORM_TEMPLATE)\n\t\t\t.withValue(\"loginUrl\", contextPath + this.authenticationUrl)\n\t\t\t.withRawHtml(\"errorMessage\", renderError(loginError, errorMsg))\n\t\t\t.withRawHtml(\"logoutMessage\", renderSuccess(logoutSuccess))\n\t\t\t.withRawHtml(\"usernameInput\", usernameInput)\n\t\t\t.withValue(\"passwordParameter\", this.passwordParameter)\n\t\t\t.withRawHtml(\"rememberMeInput\", renderRememberMe(this.rememberMeParameter))\n\t\t\t.withRawHtml(\"hiddenInputs\", hiddenInputs)\n\t\t\t.withRawHtml(\"autocomplete\", this.passkeysEnabled ? \"autocomplete=\\\"password webauthn\\\" \" : \"\")\n\t\t\t.render();\n\t}\n\n\tprivate String renderOneTimeTokenLogin(HttpServletRequest request, boolean loginError, boolean logoutSuccess,\n\t\t\tString contextPath, String errorMsg) {\n\t\tif (!this.oneTimeTokenEnabled) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tString hiddenInputs = this.resolveHiddenInputs.apply(request)\n\t\t\t.entrySet()\n\t\t\t.stream()\n\t\t\t.map((inputKeyValue) -> renderHiddenInput(inputKeyValue.getKey(), inputKeyValue.getValue()))\n\t\t\t.collect(Collectors.joining(\"\\n\"));\n\n\t\tString username = getUsername();\n\t\tString usernameInput = (username != null)\n\t\t\t\t? HtmlTemplates.fromTemplate(ONE_TIME_READONLY_USERNAME_INPUT).withValue(\"username\", username).render()\n\t\t\t\t: ONE_TIME_USERNAME_INPUT;\n\n\t\treturn HtmlTemplates.fromTemplate(ONE_TIME_TEMPLATE)\n\t\t\t.withValue(\"generateOneTimeTokenUrl\", contextPath + this.generateOneTimeTokenUrl)\n\t\t\t.withRawHtml(\"errorMessage\", renderError(loginError, errorMsg))\n\t\t\t.withRawHtml(\"logoutMessage\", renderSuccess(logoutSuccess))\n\t\t\t.withRawHtml(\"hiddenInputs\", hiddenInputs)\n\t\t\t.withRawHtml(\"usernameInput\", usernameInput)\n\t\t\t.render();\n\t}\n\n\tprivate String renderOAuth2Login(boolean loginError, boolean logoutSuccess, String errorMsg, String contextPath) {\n\t\tif (!this.oauth2LoginEnabled) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tString oauth2Rows = this.oauth2AuthenticationUrlToClientName.entrySet()\n\t\t\t.stream()\n\t\t\t.map((urlToName) -> renderOAuth2Row(contextPath, urlToName.getKey(), urlToName.getValue()))\n\t\t\t.collect(Collectors.joining(\"\\n\"));\n\n\t\treturn HtmlTemplates.fromTemplate(OAUTH2_LOGIN_TEMPLATE)\n\t\t\t.withRawHtml(\"errorMessage\", renderError(loginError, errorMsg))\n\t\t\t.withRawHtml(\"logoutMessage\", renderSuccess(logoutSuccess))\n\t\t\t.withRawHtml(\"oauth2Rows\", oauth2Rows)\n\t\t\t.render();\n\t}\n\n\tprivate static String renderOAuth2Row(String contextPath, String url, String clientName) {\n\t\treturn HtmlTemplates.fromTemplate(OAUTH2_ROW_TEMPLATE)\n\t\t\t.withValue(\"url\", contextPath + url)\n\t\t\t.withValue(\"clientName\", clientName)\n\t\t\t.render();\n\t}\n\n\tprivate String renderSaml2Login(boolean loginError, boolean logoutSuccess, String errorMsg, String contextPath) {\n\t\tif (!this.saml2LoginEnabled) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tString samlRows = this.saml2AuthenticationUrlToProviderName.entrySet()\n\t\t\t.stream()\n\t\t\t.map((urlToName) -> renderSaml2Row(contextPath, urlToName.getKey(), urlToName.getValue()))\n\t\t\t.collect(Collectors.joining(\"\\n\"));\n\n\t\treturn HtmlTemplates.fromTemplate(SAML_LOGIN_TEMPLATE)\n\t\t\t.withRawHtml(\"errorMessage\", renderError(loginError, errorMsg))\n\t\t\t.withRawHtml(\"logoutMessage\", renderSuccess(logoutSuccess))\n\t\t\t.withRawHtml(\"samlRows\", samlRows)\n\t\t\t.render();\n\t}\n\n\tprivate static String renderSaml2Row(String contextPath, String url, String clientName) {\n\t\treturn HtmlTemplates.fromTemplate(SAML_ROW_TEMPLATE)\n\t\t\t.withValue(\"url\", contextPath + url)\n\t\t\t.withValue(\"clientName\", clientName)\n\t\t\t.render();\n\t}\n\n\tprivate String renderHiddenInput(String name, String value) {\n\t\treturn HtmlTemplates.fromTemplate(HIDDEN_HTML_INPUT_TEMPLATE)\n\t\t\t.withValue(\"name\", name)\n\t\t\t.withValue(\"value\", value)\n\t\t\t.render();\n\t}\n\n\tprivate String renderRememberMe(@Nullable String paramName) {\n\t\tif (paramName == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn HtmlTemplates\n\t\t\t.fromTemplate(\"<p><input type='checkbox' name='{{paramName}}'/> Remember me on this computer.</p>\")\n\t\t\t.withValue(\"paramName\", paramName)\n\t\t\t.render();\n\t}\n\n\tprivate @Nullable String getUsername() {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication != null && authentication.isAuthenticated()) {\n\t\t\treturn authentication.getName();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate boolean isLogoutSuccess(HttpServletRequest request) {\n\t\treturn this.logoutSuccessUrl != null && matches(request, this.logoutSuccessUrl);\n\t}\n\n\tprivate boolean isLoginUrlRequest(HttpServletRequest request) {\n\t\treturn matches(request, this.loginPageUrl);\n\t}\n\n\tprivate boolean isErrorPage(HttpServletRequest request) {\n\t\treturn matches(request, this.failureUrl);\n\t}\n\n\tprivate String renderError(boolean isError, String message) {\n\t\tif (!isError) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn HtmlTemplates.fromTemplate(ALERT_TEMPLATE).withValue(\"message\", message).render();\n\t}\n\n\tprivate String renderSuccess(boolean isLogoutSuccess) {\n\t\tif (!isLogoutSuccess) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn \"<div class=\\\"alert alert-success\\\" role=\\\"alert\\\">You have been signed out</div>\";\n\t}\n\n\tprivate boolean matches(HttpServletRequest request, @Nullable String url) {\n\t\tif (!\"GET\".equals(request.getMethod()) || url == null) {\n\t\t\treturn false;\n\t\t}\n\t\tString uri = request.getRequestURI();\n\t\tint pathParamIndex = uri.indexOf(';');\n\t\tif (pathParamIndex > 0) {\n\t\t\t// strip everything after the first semi-colon\n\t\t\turi = uri.substring(0, pathParamIndex);\n\t\t}\n\t\tif (request.getQueryString() != null) {\n\t\t\turi += \"?\" + request.getQueryString();\n\t\t}\n\t\tUriComponentsBuilder addAllowed = UriComponentsBuilder.fromUriString(url);\n\t\tfor (String parameter : this.allowedParameters) {\n\t\t\tString[] values = request.getParameterValues(parameter);\n\t\t\tif (values != null) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\taddAllowed.queryParam(parameter, value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (\"\".equals(request.getContextPath())) {\n\t\t\treturn uri.equals(addAllowed.toUriString());\n\t\t}\n\t\treturn uri.equals(request.getContextPath() + addAllowed.toUriString());\n\t}\n\n\tprivate static final String CSRF_HEADERS = \"\"\"\n\t\t\t{\"{{headerName}}\" : \"{{headerValue}}\"}\"\"\";\n\n\tprivate static final String PASSKEY_SCRIPT_TEMPLATE = \"\"\"\n\t\t\t\t<script type=\"text/javascript\" src=\"{{contextPath}}/login/webauthn.js\"></script>\n\t\t\t\t<script type=\"text/javascript\">\n\t\t\t\t<!--\n\t\t\t\t\tdocument.addEventListener(\"DOMContentLoaded\",() => setupLogin({{csrfHeaders}}, \"{{contextPath}}\", document.getElementById('passkey-signin')));\n\n\t\t\t\t//-->\n\t\t\t\t</script>\n\t\t\t\"\"\";\n\n\tprivate static final String PASSKEY_FORM_TEMPLATE = \"\"\"\n\t\t\t<div class=\"login-form\">\n\t\t\t<h2>Login with Passkeys</h2>\n\t\t\t<button id=\"passkey-signin\" type=\"submit\" class=\"primary\">Sign in with a passkey</button>\n\t\t\t</div>\n\t\t\t\"\"\";\n\n\tprivate static final String LOGIN_PAGE_TEMPLATE = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <meta charset=\"utf-8\">\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t    <title>Please sign in</title>\n\t\t\t    <link href=\"{{contextPath}}/default-ui.css\" rel=\"stylesheet\" />{{javaScript}}\n\t\t\t  </head>\n\t\t\t  <body>\n\t\t\t    <div class=\"content\">\n\t\t\t{{formLogin}}\n\t\t\t{{oneTimeTokenLogin}}{{passkeyLogin}}\n\t\t\t{{oauth2Login}}\n\t\t\t{{saml2Login}}\n\t\t\t    </div>\n\t\t\t  </body>\n\t\t\t</html>\"\"\";\n\n\tprivate static final String LOGIN_FORM_TEMPLATE = \"\"\"\n\t\t\t      <form class=\"login-form\" method=\"post\" action=\"{{loginUrl}}\">\n\t\t\t        <h2>Please sign in</h2>\n\t\t\t{{errorMessage}}{{logoutMessage}}\n\t\t\t        <p>\n\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t          {{usernameInput}}\n\t\t\t        </p>\n\t\t\t        <p>\n\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t          <input type=\"password\" id=\"password\" name=\"{{passwordParameter}}\" placeholder=\"Password\" {{autocomplete}}required>\n\t\t\t        </p>\n\t\t\t{{rememberMeInput}}\n\t\t\t{{hiddenInputs}}\n\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t      </form>\"\"\";\n\n\tprivate static final String FORM_READONLY_USERNAME_INPUT = \"\"\"\n\t\t\t<input type=\"text\" id=\"username\" name=\"{{usernameParameter}}\" value=\"{{username}}\" placeholder=\"Username\" required readonly>\n\t\t\t\"\"\";\n\n\tprivate static final String FORM_USERNAME_INPUT = \"\"\"\n\t\t\t<input type=\"text\" id=\"username\" name=\"{{usernameParameter}}\" placeholder=\"Username\" required autofocus>\n\t\t\t\"\"\";\n\n\tprivate static final String HIDDEN_HTML_INPUT_TEMPLATE = \"\"\"\n\t\t\t<input name=\"{{name}}\" type=\"hidden\" value=\"{{value}}\" />\n\t\t\t\"\"\";\n\n\tprivate static final String ALERT_TEMPLATE = \"\"\"\n\t\t\t<div class=\"alert alert-danger\" role=\"alert\">{{message}}</div>\"\"\";\n\n\tprivate static final String OAUTH2_LOGIN_TEMPLATE = \"\"\"\n\t\t\t<h2>Login with OAuth 2.0</h2>\n\t\t\t{{errorMessage}}{{logoutMessage}}\n\t\t\t<table class=\"table table-striped\">\n\t\t\t  {{oauth2Rows}}\n\t\t\t</table>\"\"\";\n\n\tprivate static final String OAUTH2_ROW_TEMPLATE = \"\"\"\n\t\t\t<tr><td><a href=\"{{url}}\">{{clientName}}</a></td></tr>\"\"\";\n\n\tprivate static final String SAML_LOGIN_TEMPLATE = \"\"\"\n\t\t\t<h2>Login with SAML 2.0</h2>\n\t\t\t{{errorMessage}}{{logoutMessage}}\n\t\t\t<table class=\"table table-striped\">\n\t\t\t  {{samlRows}}\n\t\t\t</table>\"\"\";\n\n\tprivate static final String SAML_ROW_TEMPLATE = OAUTH2_ROW_TEMPLATE;\n\n\tprivate static final String ONE_TIME_TEMPLATE = \"\"\"\n\t\t\t      <form id=\"ott-form\" class=\"login-form\" method=\"post\" action=\"{{generateOneTimeTokenUrl}}\">\n\t\t\t        <h2>Request a One-Time Token</h2>\n\t\t\t{{errorMessage}}{{logoutMessage}}\n\t\t\t        <p>\n\t\t\t          <label for=\"ott-username\" class=\"screenreader\">Username</label>\n\t\t\t          {{usernameInput}}\n\t\t\t        </p>\n\t\t\t{{hiddenInputs}}\n\t\t\t        <button class=\"primary\" type=\"submit\" form=\"ott-form\">Send Token</button>\n\t\t\t      </form>\n\t\t\t\"\"\";\n\n\tprivate static final String ONE_TIME_READONLY_USERNAME_INPUT = \"\"\"\n\t\t\t<input type=\"text\" id=\"ott-username\" name=\"username\" value=\"{{username}}\" placeholder=\"Username\" required readonly>\n\t\t\t\"\"\";\n\n\tprivate static final String ONE_TIME_USERNAME_INPUT = \"\"\"\n\t\t\t<input type=\"text\" id=\"ott-username\" name=\"username\" placeholder=\"Username\" required>\n\t\t\t\"\"\";\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Generates a default log out page.\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic class DefaultLogoutPageGeneratingFilter extends OncePerRequestFilter {\n\n\tprivate RequestMatcher matcher = pathPattern(HttpMethod.GET, \"/logout\");\n\n\tprivate Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = (request) -> Collections.emptyMap();\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (this.matcher.matches(request)) {\n\t\t\trenderLogout(request, response);\n\t\t}\n\t\telse {\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(LogMessage.format(\"Did not render default logout page since request did not match [%s]\",\n\t\t\t\t\t\tthis.matcher));\n\t\t\t}\n\t\t\tfilterChain.doFilter(request, response);\n\t\t}\n\t}\n\n\tprivate void renderLogout(HttpServletRequest request, HttpServletResponse response) throws IOException {\n\t\tString renderedPage = HtmlTemplates.fromTemplate(LOGOUT_PAGE_TEMPLATE)\n\t\t\t.withValue(\"contextPath\", request.getContextPath())\n\t\t\t.withRawHtml(\"hiddenInputs\", renderHiddenInputs(request).indent(8))\n\t\t\t.render();\n\t\tresponse.setContentType(\"text/html;charset=UTF-8\");\n\t\tresponse.getWriter().write(renderedPage);\n\t}\n\n\t/**\n\t * Sets a Function used to resolve a Map of the hidden inputs where the key is the\n\t * name of the input and the value is the value of the input. Typically this is used\n\t * to resolve the CSRF token.\n\t * @param resolveHiddenInputs the function to resolve the inputs\n\t */\n\tpublic void setResolveHiddenInputs(Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs) {\n\t\tAssert.notNull(resolveHiddenInputs, \"resolveHiddenInputs cannot be null\");\n\t\tthis.resolveHiddenInputs = resolveHiddenInputs;\n\t}\n\n\tprivate String renderHiddenInputs(HttpServletRequest request) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (Map.Entry<String, String> input : this.resolveHiddenInputs.apply(request).entrySet()) {\n\t\t\tString inputElement = HtmlTemplates.fromTemplate(HIDDEN_HTML_INPUT_TEMPLATE)\n\t\t\t\t.withValue(\"name\", input.getKey())\n\t\t\t\t.withValue(\"value\", input.getValue())\n\t\t\t\t.render();\n\t\t\tsb.append(inputElement);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate static final String LOGOUT_PAGE_TEMPLATE = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <meta charset=\"utf-8\">\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t    <title>Confirm Log Out?</title>\n\t\t\t    <link href=\"{{contextPath}}/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t  </head>\n\t\t\t  <body>\n\t\t\t    <div class=\"content\">\n\t\t\t      <form class=\"logout-form\" method=\"post\" action=\"{{contextPath}}/logout\">\n\t\t\t        <h2>Are you sure you want to log out?</h2>\n\t\t\t{{hiddenInputs}}\n\t\t\t        <button class=\"primary\" type=\"submit\">Log Out</button>\n\t\t\t      </form>\n\t\t\t    </div>\n\t\t\t  </body>\n\t\t\t</html>\"\"\";\n\n\tprivate static final String HIDDEN_HTML_INPUT_TEMPLATE = \"\"\"\n\t\t\t<input name=\"{{name}}\" type=\"hidden\" value=\"{{value}}\" />\n\t\t\t\"\"\";\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ui/DefaultOneTimeTokenSubmitPageGeneratingFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationFilter;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Creates a default one-time token submit page. If the request contains a {@code token}\n * query param the page will automatically fill the form with the token value.\n *\n * @author Marcus da Coregio\n * @since 6.4\n */\npublic final class DefaultOneTimeTokenSubmitPageGeneratingFilter extends OncePerRequestFilter {\n\n\tpublic static final String DEFAULT_SUBMIT_PAGE_URL = \"/login/ott\";\n\n\tprivate RequestMatcher requestMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.GET, DEFAULT_SUBMIT_PAGE_URL);\n\n\tprivate Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = (request) -> Collections.emptyMap();\n\n\tprivate String loginProcessingUrl = OneTimeTokenAuthenticationFilter.DEFAULT_LOGIN_PROCESSING_URL;\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tString html = generateHtml(request);\n\t\tresponse.setContentType(\"text/html;charset=UTF-8\");\n\t\tresponse.setContentLength(html.getBytes(StandardCharsets.UTF_8).length);\n\t\tresponse.getWriter().write(html);\n\t}\n\n\tprivate String generateHtml(HttpServletRequest request) {\n\t\tString contextPath = request.getContextPath();\n\n\t\tString token = request.getParameter(\"token\");\n\t\tString tokenValue = StringUtils.hasText(token) ? token : \"\";\n\n\t\tString hiddenInputs = this.resolveHiddenInputs.apply(request)\n\t\t\t.entrySet()\n\t\t\t.stream()\n\t\t\t.map((inputKeyValue) -> renderHiddenInput(inputKeyValue.getKey(), inputKeyValue.getValue()))\n\t\t\t.collect(Collectors.joining(\"\\n\"));\n\n\t\treturn HtmlTemplates.fromTemplate(ONE_TIME_TOKEN_SUBMIT_PAGE_TEMPLATE)\n\t\t\t.withValue(\"contextPath\", contextPath)\n\t\t\t.withValue(\"tokenValue\", tokenValue)\n\t\t\t.withValue(\"loginProcessingUrl\", contextPath + this.loginProcessingUrl)\n\t\t\t.withRawHtml(\"hiddenInputs\", hiddenInputs)\n\t\t\t.render();\n\t}\n\n\tprivate String renderHiddenInput(String name, String value) {\n\t\treturn HtmlTemplates.fromTemplate(HIDDEN_HTML_INPUT_TEMPLATE)\n\t\t\t.withValue(\"name\", name)\n\t\t\t.withValue(\"value\", value)\n\t\t\t.render();\n\t}\n\n\t/**\n\t * Sets a Function used to resolve a Map of the hidden inputs where the key is the\n\t * name of the input and the value is the value of the input.\n\t * @param resolveHiddenInputs the function to resolve the inputs\n\t */\n\tpublic void setResolveHiddenInputs(Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs) {\n\t\tAssert.notNull(resolveHiddenInputs, \"resolveHiddenInputs cannot be null\");\n\t\tthis.resolveHiddenInputs = resolveHiddenInputs;\n\t}\n\n\t/**\n\t * Use this {@link RequestMatcher} to choose whether this filter will handle the\n\t * request. By default, it handles {@code /login/ott}.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\t/**\n\t * Specifies the URL that the submit form should POST to. Defaults to\n\t * {@code /login/ott}.\n\t * @param loginProcessingUrl\n\t */\n\tpublic void setLoginProcessingUrl(String loginProcessingUrl) {\n\t\tAssert.hasText(loginProcessingUrl, \"loginProcessingUrl cannot be null or empty\");\n\t\tthis.loginProcessingUrl = loginProcessingUrl;\n\t}\n\n\tprivate static final String ONE_TIME_TOKEN_SUBMIT_PAGE_TEMPLATE = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <title>One-Time Token Login</title>\n\t\t\t    <meta charset=\"utf-8\"/>\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"/>\n\t\t\t    <link href=\"{{contextPath}}/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t  </head>\n\t\t\t  <body>\n\t\t\t    <div class=\"container\">\n\t\t\t      <form class=\"login-form\" action=\"{{loginProcessingUrl}}\" method=\"post\">\n\t\t\t        <h2>Please input the token</h2>\n\t\t\t        <p>\n\t\t\t          <label for=\"token\" class=\"screenreader\">Token</label>\n\t\t\t          <input type=\"text\" id=\"token\" name=\"token\" value=\"{{tokenValue}}\" placeholder=\"Token\" required=\"true\" autofocus=\"autofocus\"/>\n\t\t\t        </p>\n\t\t\t        <button class=\"primary\" type=\"submit\">Sign in</button>\n\t\t\t{{hiddenInputs}}\n\t\t\t      </form>\n\t\t\t    </div>\n\t\t\t  </body>\n\t\t\t</html>\n\t\t\t\"\"\";\n\n\tprivate static final String HIDDEN_HTML_INPUT_TEMPLATE = \"\"\"\n\t\t\t<input name=\"{{name}}\" type=\"hidden\" value=\"{{value}}\" />\n\t\t\t\"\"\";\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ui/DefaultResourcesFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Serve common static assets used in default UIs, such as CSS or Javascript files. For\n * internal use only.\n *\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n */\npublic final class DefaultResourcesFilter extends GenericFilterBean {\n\n\tprivate final RequestMatcher matcher;\n\n\tprivate final ClassPathResource resource;\n\n\tprivate final MediaType mediaType;\n\n\tprivate DefaultResourcesFilter(RequestMatcher matcher, ClassPathResource resource, MediaType mediaType) {\n\t\tAssert.isTrue(resource.exists(), \"classpath resource must exist\");\n\t\tthis.matcher = matcher;\n\t\tthis.resource = resource;\n\t\tthis.mediaType = mediaType;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!(request instanceof HttpServletRequest servletRequest)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.matcher.matches(servletRequest)) {\n\t\t\tresponse.setContentType(this.mediaType.toString());\n\t\t\tresponse.getWriter().write(this.resource.getContentAsString(StandardCharsets.UTF_8));\n\t\t\treturn;\n\t\t}\n\n\t\tfilterChain.doFilter(request, response);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s [matcher=%s, resource=%s]\".formatted(getClass().getSimpleName(), this.matcher.toString(),\n\t\t\t\tthis.resource.getPath());\n\t}\n\n\t/**\n\t * Create an instance of {@link DefaultResourcesFilter} serving Spring Security's\n\t * default CSS stylesheet.\n\t * <p>\n\t * The created {@link DefaultResourcesFilter} matches requests\n\t * {@code HTTP GET /default-ui.css}, and returns the default stylesheet at\n\t * {@code org/springframework/security/default-ui.css} with content-type\n\t * {@code text/css;charset=UTF-8}.\n\t * @return -\n\t */\n\tpublic static DefaultResourcesFilter css() {\n\t\treturn new DefaultResourcesFilter(pathPattern(HttpMethod.GET, \"/default-ui.css\"),\n\t\t\t\tnew ClassPathResource(\"org/springframework/security/default-ui.css\"),\n\t\t\t\tnew MediaType(\"text\", \"css\", StandardCharsets.UTF_8));\n\t}\n\n\t/**\n\t * Create an instance of {@link DefaultResourcesFilter} serving Spring Security's\n\t * default webauthn javascript.\n\t * <p>\n\t * The created {@link DefaultResourcesFilter} matches requests\n\t * {@code HTTP GET /login/webauthn.js}, and returns the default webauthn javascript at\n\t * {@code org/springframework/security/spring-security-webauthn.js} with content-type\n\t * {@code text/javascript;charset=UTF-8}. This file is generated in the\n\t * {@code spring-security-javascript} project.\n\t * @return -\n\t */\n\tpublic static DefaultResourcesFilter webauthn() {\n\t\treturn new DefaultResourcesFilter(pathPattern(HttpMethod.GET, \"/login/webauthn.js\"),\n\t\t\t\tnew ClassPathResource(\"org/springframework/security/spring-security-webauthn.js\"),\n\t\t\t\tnew MediaType(\"text\", \"javascript\", StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ui/HtmlTemplates.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.HtmlUtils;\n\n/**\n * Render HTML templates using string substitution. Intended for internal use. Variables\n * can be templated using double curly-braces: {@code {{name}}}.\n *\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n */\nfinal class HtmlTemplates {\n\n\tprivate HtmlTemplates() {\n\t}\n\n\tstatic Builder fromTemplate(String template) {\n\t\treturn new Builder(template);\n\t}\n\n\tstatic final class Builder {\n\n\t\tprivate final String template;\n\n\t\tprivate final Map<String, String> values = new HashMap<>();\n\n\t\tprivate Builder(String template) {\n\t\t\tthis.template = template;\n\t\t}\n\n\t\t/**\n\t\t * HTML-escape, and inject value {@code value} in every {@code {{key}}}\n\t\t * placeholder.\n\t\t * @param key the placeholder name\n\t\t * @param value the value to inject\n\t\t * @return this instance for further templating\n\t\t */\n\t\tBuilder withValue(String key, @Nullable String value) {\n\t\t\tif (value != null) {\n\t\t\t\tthis.values.put(key, HtmlUtils.htmlEscape(value));\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Inject value {@code value} in every {@code {{key}}} placeholder without\n\t\t * HTML-escaping. Useful for injecting \"sub-templates\".\n\t\t * @param key the placeholder name\n\t\t * @param value the value to inject\n\t\t * @return this instance for further templating\n\t\t */\n\t\tBuilder withRawHtml(String key, String value) {\n\t\t\tif (!value.isEmpty() && value.charAt(value.length() - 1) == '\\n') {\n\t\t\t\tvalue = value.substring(0, value.length() - 1);\n\t\t\t}\n\t\t\tthis.values.put(key, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Render the template. All placeholders MUST have a corresponding value. If a\n\t\t * placeholder does not have a corresponding value, throws\n\t\t * {@link IllegalStateException}.\n\t\t * @return the rendered template\n\t\t */\n\t\tString render() {\n\t\t\tString template = this.template;\n\t\t\tfor (String key : this.values.keySet()) {\n\t\t\t\tString pattern = Pattern.quote(\"{{\" + key + \"}}\");\n\t\t\t\ttemplate = template.replaceAll(pattern, this.values.get(key));\n\t\t\t}\n\n\t\t\tString unusedPlaceholders = Pattern.compile(\"\\\\{\\\\{([a-zA-Z0-9]+)}}\")\n\t\t\t\t.matcher(template)\n\t\t\t\t.results()\n\t\t\t\t.map((result) -> result.group(1))\n\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t\tif (StringUtils.hasLength(unusedPlaceholders)) {\n\t\t\t\tthrow new IllegalStateException(\"Unused placeholders in template: [%s]\".formatted(unusedPlaceholders));\n\t\t\t}\n\n\t\t\treturn template;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/ui/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Authentication user-interface rendering code. Used to conveniently create an\n * appropriate login page when using namespace configuration without defining a login page\n * URL.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.ui;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Converts from a HttpServletRequest to {@link UsernamePasswordAuthenticationToken} that\n * can be authenticated. Null authentication possible if there was no Authorization header\n * with Basic authentication scheme.\n *\n * @author Sergey Bespalov\n * @since 5.2.0\n */\npublic class BasicAuthenticationConverter implements AuthenticationConverter {\n\n\tpublic static final String AUTHENTICATION_SCHEME_BASIC = \"Basic\";\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;\n\n\tprivate Charset credentialsCharset = StandardCharsets.UTF_8;\n\n\tpublic BasicAuthenticationConverter() {\n\t\tthis(new WebAuthenticationDetailsSource());\n\t}\n\n\tpublic BasicAuthenticationConverter(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\tpublic Charset getCredentialsCharset() {\n\t\treturn this.credentialsCharset;\n\t}\n\n\tpublic void setCredentialsCharset(Charset credentialsCharset) {\n\t\tthis.credentialsCharset = credentialsCharset;\n\t}\n\n\tpublic AuthenticationDetailsSource<HttpServletRequest, ?> getAuthenticationDetailsSource() {\n\t\treturn this.authenticationDetailsSource;\n\t}\n\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"AuthenticationDetailsSource required\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\t@Override\n\tpublic @Nullable UsernamePasswordAuthenticationToken convert(HttpServletRequest request) {\n\t\tString header = request.getHeader(HttpHeaders.AUTHORIZATION);\n\t\tif (header == null) {\n\t\t\treturn null;\n\t\t}\n\t\theader = header.trim();\n\t\tif (!StringUtils.startsWithIgnoreCase(header, AUTHENTICATION_SCHEME_BASIC)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (header.equalsIgnoreCase(AUTHENTICATION_SCHEME_BASIC)) {\n\t\t\tthrow new BadCredentialsException(\"Empty basic authentication token\");\n\t\t}\n\t\tbyte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);\n\t\tbyte[] decoded = decode(base64Token);\n\t\tString token = new String(decoded, getCredentialsCharset(request));\n\t\tint delim = token.indexOf(\":\");\n\t\tif (delim == -1) {\n\t\t\tthrow new BadCredentialsException(\"Invalid basic authentication token\");\n\t\t}\n\t\tUsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(token.substring(0, delim), token.substring(delim + 1));\n\t\tresult.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\treturn result;\n\t}\n\n\tprivate byte[] decode(byte[] base64Token) {\n\t\ttry {\n\t\t\treturn Base64.getDecoder().decode(base64Token);\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\tthrow new BadCredentialsException(\"Failed to decode basic authentication token\");\n\t\t}\n\t}\n\n\tprotected Charset getCredentialsCharset(HttpServletRequest request) {\n\t\treturn getCredentialsCharset();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.util.Assert;\n\n/**\n * Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the\n * {@link BasicAuthenticationFilter}.\n * <p>\n * Once a user agent is authenticated using BASIC authentication, logout requires that the\n * browser be closed or an unauthorized (401) header be sent. The simplest way of\n * achieving the latter is to call the\n * {@link #commence(HttpServletRequest, HttpServletResponse, AuthenticationException)}\n * method below. This will indicate to the browser its credentials are no longer\n * authorized, causing it to prompt the user to login again.\n *\n * @author Ben Alex\n * @author Andrey Litvitski\n */\npublic class BasicAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {\n\n\tprivate @Nullable String realmName;\n\n\tprivate @Nullable Charset charset = StandardCharsets.UTF_8;\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasText(this.realmName, \"realmName must be specified\");\n\t}\n\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authException) throws IOException {\n\t\tString header = \"Basic realm=\\\"\" + this.realmName + \"\\\"\";\n\t\tif (this.charset != null) {\n\t\t\theader += \", charset=\\\"\" + this.charset.name() + \"\\\"\";\n\t\t}\n\t\tresponse.setHeader(\"WWW-Authenticate\", header);\n\t\tresponse.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());\n\t}\n\n\tpublic @Nullable String getRealmName() {\n\t\treturn this.realmName;\n\t}\n\n\tpublic void setRealmName(String realmName) {\n\t\tthis.realmName = realmName;\n\t}\n\n\t/**\n\t * Sets the charset to include in the {@code WWW-Authenticate} response header. By\n\t * default, it is set to {@link StandardCharsets#UTF_8}. Set to {@code null} to omit\n\t * the charset attribute from the header. As per RFC 7617, only UTF-8 is permitted.\n\t * @param charset the charset to use ({@link StandardCharsets#UTF_8} is the only\n\t * accepted value), or {@code null} to remove the charset attribute\n\t * @since 7.1\n\t */\n\tpublic void setCharset(@Nullable Charset charset) {\n\t\tAssert.isTrue(charset == null || StandardCharsets.UTF_8.equals(charset),\n\t\t\t\t\"RFC 7617 only permits UTF-8 as the charset for Basic authentication\");\n\t\tthis.charset = charset;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.nio.charset.Charset;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.lang.Contract;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.NullRememberMeServices;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Processes a HTTP request's BASIC authorization headers, putting the result into the\n * <code>SecurityContextHolder</code>.\n *\n * <p>\n * For a detailed background on what this filter is designed to process, refer to\n * <a href=\"https://tools.ietf.org/html/rfc1945\">RFC 1945, Section 11.1</a>. Any realm\n * name presented in the HTTP request is ignored.\n *\n * <p>\n * In summary, this filter is responsible for processing any request that has a HTTP\n * request header of <code>Authorization</code> with an authentication scheme of\n * <code>Basic</code> and a Base64-encoded <code>username:password</code> token. For\n * example, to authenticate user \"Aladdin\" with password \"open sesame\" the following\n * header would be presented:\n *\n * <pre>\n *\n * Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n * </pre>\n *\n * <p>\n * This filter can be used to provide BASIC authentication services to both remoting\n * protocol clients (such as Hessian and SOAP) as well as standard user agents (such as\n * Internet Explorer and Netscape).\n * <p>\n * If authentication is successful, the resulting {@link Authentication} object will be\n * placed into the <code>SecurityContextHolder</code>.\n *\n * <p>\n * If authentication fails and <code>ignoreFailure</code> is <code>false</code> (the\n * default), an {@link AuthenticationEntryPoint} implementation is called (unless the\n * <tt>ignoreFailure</tt> property is set to <tt>true</tt>). Usually this should be\n * {@link BasicAuthenticationEntryPoint}, which will prompt the user to authenticate again\n * via BASIC authentication.\n *\n * <p>\n * Basic authentication is an attractive protocol because it is simple and widely\n * deployed. However, it still transmits a password in clear text and as such is\n * undesirable in many situations.\n * <p>\n * Note that if a {@link RememberMeServices} is set, this filter will automatically send\n * back remember-me details to the client. Therefore, subsequent requests will not need to\n * present a BASIC authentication header as they will be authenticated using the\n * remember-me mechanism.\n *\n * @author Ben Alex\n */\npublic class BasicAuthenticationFilter extends OncePerRequestFilter {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate @Nullable AuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate RememberMeServices rememberMeServices = new NullRememberMeServices();\n\n\tprivate boolean ignoreFailure = false;\n\n\tprivate String credentialsCharset = \"UTF-8\";\n\n\tprivate AuthenticationConverter authenticationConverter = new BasicAuthenticationConverter();\n\n\tprivate SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();\n\n\tprivate boolean mfaEnabled;\n\n\t/**\n\t * Creates an instance which will authenticate against the supplied\n\t * {@code AuthenticationManager} and which will ignore failed authentication attempts,\n\t * allowing the request to proceed down the filter chain.\n\t * @param authenticationManager the bean to submit authentication requests to\n\t */\n\tpublic BasicAuthenticationFilter(AuthenticationManager authenticationManager) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.ignoreFailure = true;\n\t}\n\n\t/**\n\t * Creates an instance which will authenticate against the supplied\n\t * {@code AuthenticationManager} and use the supplied {@code AuthenticationEntryPoint}\n\t * to handle authentication failures.\n\t * @param authenticationManager the bean to submit authentication requests to\n\t * @param authenticationEntryPoint will be invoked when authentication fails.\n\t * Typically an instance of {@link BasicAuthenticationEntryPoint}.\n\t */\n\tpublic BasicAuthenticationFilter(AuthenticationManager authenticationManager,\n\t\t\tAuthenticationEntryPoint authenticationEntryPoint) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tAssert.notNull(authenticationEntryPoint, \"authenticationEntryPoint cannot be null\");\n\t\tthis.authenticationManager = authenticationManager;\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * authentication success. The default action is not to save the\n\t * {@link SecurityContext}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Enables Multi-Factor Authentication (MFA) support.\n\t * @param mfaEnabled true to enable MFA support, false to disable it. Default is\n\t * false.\n\t */\n\tpublic void setMfaEnabled(boolean mfaEnabled) {\n\t\tthis.mfaEnabled = mfaEnabled;\n\t}\n\n\t/**\n\t * Sets the\n\t * {@link org.springframework.security.web.authentication.AuthenticationConverter} to\n\t * use. Defaults to {@link BasicAuthenticationConverter}\n\t * @param authenticationConverter the converter to use\n\t * @since 6.2\n\t */\n\tpublic void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.authenticationManager, \"An AuthenticationManager is required\");\n\t\tif (!isIgnoreFailure()) {\n\t\t\tAssert.notNull(this.authenticationEntryPoint, \"An AuthenticationEntryPoint is required\");\n\t\t}\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\ttry {\n\t\t\tAuthentication authRequest = this.authenticationConverter.convert(request);\n\t\t\tif (authRequest == null) {\n\t\t\t\tthis.logger.trace(\"Did not process authentication request since failed to find \"\n\t\t\t\t\t\t+ \"username and password in Basic Authorization header\");\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString username = authRequest.getName();\n\t\t\tthis.logger.trace(LogMessage.format(\"Found username '%s' in Basic Authorization header\", username));\n\t\t\tif (authenticationIsRequired(username)) {\n\t\t\t\tAuthentication authResult = this.authenticationManager.authenticate(authRequest);\n\t\t\t\tAuthentication current = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\t\tif (shouldPerformMfa(current, authResult)) {\n\t\t\t\t\tauthResult = authResult.toBuilder()\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\t\t.authorities((a) -> {\n\t\t\t\t\t\t\tSet<String> newAuthorities = a.stream()\n\t\t\t\t\t\t\t\t.map(GrantedAuthority::getAuthority)\n\t\t\t\t\t\t\t\t.collect(Collectors.toUnmodifiableSet());\n\t\t\t\t\t\t\tfor (GrantedAuthority currentAuthority : current.getAuthorities()) {\n\t\t\t\t\t\t\t\tif (!newAuthorities.contains(currentAuthority.getAuthority())) {\n\t\t\t\t\t\t\t\t\ta.add(currentAuthority);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.build();\n\t\t\t\t\t\t// @formatter:on\n\t\t\t\t}\n\t\t\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\t\t\tcontext.setAuthentication(authResult);\n\t\t\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\t\tthis.logger.debug(LogMessage.format(\"Set SecurityContextHolder to %s\", authResult));\n\t\t\t\t}\n\t\t\t\tthis.rememberMeServices.loginSuccess(request, response, authResult);\n\t\t\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\t\t\tonSuccessfulAuthentication(request, response, authResult);\n\t\t\t}\n\t\t}\n\t\tcatch (AuthenticationException ex) {\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\tthis.logger.debug(\"Failed to process authentication request\", ex);\n\t\t\tthis.rememberMeServices.loginFail(request, response);\n\t\t\tonUnsuccessfulAuthentication(request, response, ex);\n\t\t\tif (this.ignoreFailure || this.authenticationEntryPoint == null) {\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.authenticationEntryPoint.commence(request, response, ex);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tchain.doFilter(request, response);\n\t}\n\n\t@Contract(\"null, _ -> false\")\n\tprivate boolean shouldPerformMfa(@Nullable Authentication current, Authentication authenticationResult) {\n\t\tif (!this.mfaEnabled) {\n\t\t\treturn false;\n\t\t}\n\t\tif (current == null || !current.isAuthenticated()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!declaresToBuilder(authenticationResult)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn current.getName().equals(authenticationResult.getName());\n\t}\n\n\tprivate static boolean declaresToBuilder(Authentication authentication) {\n\t\tfor (Method method : authentication.getClass().getDeclaredMethods()) {\n\t\t\tif (method.getName().equals(\"toBuilder\") && method.getParameterTypes().length == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprotected boolean authenticationIsRequired(String username) {\n\t\t// Only reauthenticate if username doesn't match SecurityContextHolder and user\n\t\t// isn't authenticated (see SEC-53)\n\t\tAuthentication existingAuth = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (existingAuth == null || !existingAuth.getName().equals(username) || !existingAuth.isAuthenticated()) {\n\t\t\treturn true;\n\t\t}\n\t\t// Handle unusual condition where an AnonymousAuthenticationToken is already\n\t\t// present. This shouldn't happen very often, as BasicAuthenticationFilter is\n\t\t// meant to\n\t\t// be earlier in the filter chain than AnonymousAuthenticationFilter.\n\t\t// Nevertheless, presence of both an AnonymousAuthenticationToken together with a\n\t\t// BASIC authentication request header should indicate reauthentication using the\n\t\t// BASIC protocol is desirable. This behaviour is also consistent with that\n\t\t// provided by form and digest, both of which force re-authentication if the\n\t\t// respective header is detected (and in doing so replace/ any existing\n\t\t// AnonymousAuthenticationToken). See SEC-610.\n\t\treturn (existingAuth instanceof AnonymousAuthenticationToken);\n\t}\n\n\tprotected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthentication authResult) throws IOException {\n\t}\n\n\tprotected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException failed) throws IOException {\n\t}\n\n\tprotected @Nullable AuthenticationEntryPoint getAuthenticationEntryPoint() {\n\t\treturn this.authenticationEntryPoint;\n\t}\n\n\tprotected AuthenticationManager getAuthenticationManager() {\n\t\treturn this.authenticationManager;\n\t}\n\n\tprotected boolean isIgnoreFailure() {\n\t\treturn this.ignoreFailure;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationDetailsSource} to use. By default, it is set to use\n\t * the {@link WebAuthenticationDetailsSource}. Note that this configuration applies\n\t * exclusively when the {@link #authenticationConverter} is set to\n\t * {@link BasicAuthenticationConverter}. If you are utilizing a different\n\t * implementation, you will need to manually specify the authentication details on it.\n\t * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} to use.\n\t */\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tif (this.authenticationConverter instanceof BasicAuthenticationConverter basicAuthenticationConverter) {\n\t\t\tbasicAuthenticationConverter.setAuthenticationDetailsSource(authenticationDetailsSource);\n\t\t}\n\t}\n\n\tpublic void setRememberMeServices(RememberMeServices rememberMeServices) {\n\t\tAssert.notNull(rememberMeServices, \"rememberMeServices cannot be null\");\n\t\tthis.rememberMeServices = rememberMeServices;\n\t}\n\n\t/**\n\t * Sets the charset to use when decoding credentials to {@link String}s. By default,\n\t * it is set to {@code UTF-8}. Note that this configuration applies exclusively when\n\t * the {@link #authenticationConverter} is set to\n\t * {@link BasicAuthenticationConverter}. If you are utilizing a different\n\t * implementation, you will need to manually specify the charset on it.\n\t * @param credentialsCharset the charset to use.\n\t */\n\tpublic void setCredentialsCharset(String credentialsCharset) {\n\t\tAssert.hasText(credentialsCharset, \"credentialsCharset cannot be null or empty\");\n\t\tthis.credentialsCharset = credentialsCharset;\n\t\tif (this.authenticationConverter instanceof BasicAuthenticationConverter basicAuthenticationConverter) {\n\t\t\tbasicAuthenticationConverter.setCredentialsCharset(Charset.forName(credentialsCharset));\n\t\t}\n\t}\n\n\tprotected String getCredentialsCharset(HttpServletRequest httpRequest) {\n\t\treturn this.credentialsCharset;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\nfinal class DigestAuthUtils {\n\n\tprivate static final String[] EMPTY_STRING_ARRAY = new String[0];\n\n\tprivate DigestAuthUtils() {\n\t}\n\n\tstatic String encodePasswordInA1Format(@Nullable String username, @Nullable String realm,\n\t\t\t@Nullable String password) {\n\t\tString a1 = username + \":\" + realm + \":\" + password;\n\n\t\treturn md5Hex(a1);\n\t}\n\n\tstatic String @Nullable [] splitIgnoringQuotes(String str, char separatorChar) {\n\t\tif (str == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint len = str.length();\n\t\tif (len == 0) {\n\t\t\treturn EMPTY_STRING_ARRAY;\n\t\t}\n\t\tList<String> list = new ArrayList<>();\n\t\tint i = 0;\n\t\tint start = 0;\n\t\tboolean match = false;\n\t\twhile (i < len) {\n\t\t\tif (str.charAt(i) == '\"') {\n\t\t\t\ti++;\n\t\t\t\twhile (i < len) {\n\t\t\t\t\tif (str.charAt(i) == '\"') {\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\tmatch = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (str.charAt(i) == separatorChar) {\n\t\t\t\tif (match) {\n\t\t\t\t\tlist.add(str.substring(start, i));\n\t\t\t\t\tmatch = false;\n\t\t\t\t}\n\t\t\t\tstart = ++i;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmatch = true;\n\t\t\ti++;\n\t\t}\n\t\tif (match) {\n\t\t\tlist.add(str.substring(start, i));\n\t\t}\n\t\treturn list.toArray(new String[0]);\n\t}\n\n\t/**\n\t * Computes the <code>response</code> portion of a Digest authentication header. Both\n\t * the server and user agent should compute the <code>response</code> independently.\n\t * Provided as a static method to simplify the coding of user agents.\n\t * @param passwordAlreadyEncoded true if the password argument is already encoded in\n\t * the correct format. False if it is plain text.\n\t * @param username the user's login name.\n\t * @param realm the name of the realm.\n\t * @param password the user's password in plaintext or ready-encoded.\n\t * @param httpMethod the HTTP request method (GET, POST etc.)\n\t * @param uri the request URI.\n\t * @param qop the qop directive, or null if not set.\n\t * @param nonce the nonce supplied by the server\n\t * @param nc the \"nonce-count\" as defined in RFC 2617.\n\t * @param cnonce opaque string supplied by the client when qop is set.\n\t * @return the MD5 of the digest authentication response, encoded in hex\n\t * @throws IllegalArgumentException if the supplied qop value is unsupported.\n\t */\n\tstatic String generateDigest(boolean passwordAlreadyEncoded, @Nullable String username, @Nullable String realm,\n\t\t\t@Nullable String password, String httpMethod, @Nullable String uri, @Nullable String qop,\n\t\t\t@Nullable String nonce, @Nullable String nc, @Nullable String cnonce) throws IllegalArgumentException {\n\t\tString a2 = httpMethod + \":\" + uri;\n\t\tString a1Md5 = (!passwordAlreadyEncoded) ? DigestAuthUtils.encodePasswordInA1Format(username, realm, password)\n\t\t\t\t: password;\n\t\tString a2Md5 = md5Hex(a2);\n\t\tif (qop == null) {\n\t\t\t// as per RFC 2069 compliant clients (also reaffirmed by RFC 2617)\n\t\t\treturn md5Hex(a1Md5 + \":\" + nonce + \":\" + a2Md5);\n\t\t}\n\t\tif (\"auth\".equals(qop)) {\n\t\t\t// As per RFC 2617 compliant clients\n\t\t\treturn md5Hex(a1Md5 + \":\" + nonce + \":\" + nc + \":\" + cnonce + \":\" + qop + \":\" + a2Md5);\n\t\t}\n\t\tthrow new IllegalArgumentException(\"This method does not support a qop: '\" + qop + \"'\");\n\t}\n\n\t/**\n\t * Takes an array of <code>String</code>s, and for each element removes any instances\n\t * of <code>removeCharacter</code>, and splits the element based on the\n\t * <code>delimiter</code>. A <code>Map</code> is then generated, with the left of the\n\t * delimiter providing the key, and the right of the delimiter providing the value.\n\t * <p>\n\t * Will trim both the key and value before adding to the <code>Map</code>.\n\t * </p>\n\t * @param array the array to process\n\t * @param delimiter to split each element using (typically the equals symbol)\n\t * @param removeCharacters one or more characters to remove from each element prior to\n\t * attempting the split operation (typically the quotation mark symbol) or\n\t * <code>null</code> if no removal should occur\n\t * @return a <code>Map</code> representing the array contents, or <code>null</code> if\n\t * the array to process was null or empty\n\t */\n\tstatic @Nullable Map<String, String> splitEachArrayElementAndCreateMap(String @Nullable [] array, String delimiter,\n\t\t\tString removeCharacters) {\n\t\tif ((array == null) || (array.length == 0)) {\n\t\t\treturn null;\n\t\t}\n\t\tMap<String, String> map = new HashMap<>();\n\t\tfor (String s : array) {\n\t\t\tString postRemove = (removeCharacters != null) ? StringUtils.replace(s, removeCharacters, \"\") : s;\n\t\t\tString[] splitThisArrayElement = split(postRemove, delimiter);\n\t\t\tif (splitThisArrayElement == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmap.put(splitThisArrayElement[0].trim(), splitThisArrayElement[1].trim());\n\t\t}\n\t\treturn map;\n\t}\n\n\t/**\n\t * Splits a <code>String</code> at the first instance of the delimiter.\n\t * <p>\n\t * Does not include the delimiter in the response.\n\t * </p>\n\t * @param toSplit the string to split\n\t * @param delimiter to split the string up with\n\t * @return a two element array with index 0 being before the delimiter, and index 1\n\t * being after the delimiter (neither element includes the delimiter)\n\t * @throws IllegalArgumentException if an argument was invalid\n\t */\n\tstatic String @Nullable [] split(String toSplit, String delimiter) {\n\t\tAssert.hasLength(toSplit, \"Cannot split a null or empty string\");\n\t\tAssert.hasLength(delimiter, \"Cannot use a null or empty delimiter to split a string\");\n\t\tAssert.isTrue(delimiter.length() == 1, \"Delimiter can only be one character in length\");\n\t\tint offset = toSplit.indexOf(delimiter);\n\t\tif (offset < 0) {\n\t\t\treturn null;\n\t\t}\n\t\tString beforeDelimiter = toSplit.substring(0, offset);\n\t\tString afterDelimiter = toSplit.substring(offset + 1);\n\t\treturn new String[] { beforeDelimiter, afterDelimiter };\n\t}\n\n\tstatic String md5Hex(String data) {\n\t\ttry {\n\t\t\tMessageDigest digest = MessageDigest.getInstance(\"MD5\");\n\t\t\treturn new String(Hex.encode(digest.digest(data.getBytes())));\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(\"No MD5 algorithm available!\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.io.IOException;\nimport java.util.Base64;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.util.Assert;\n\n/**\n * Used by the <code>SecurityEnforcementFilter</code> to commence authentication via the\n * {@link DigestAuthenticationFilter}.\n * <p>\n * The nonce sent back to the user agent will be valid for the period indicated by\n * {@link #setNonceValiditySeconds(int)}. By default this is 300 seconds. Shorter times\n * should be used if replay attacks are a major concern. Larger values can be used if\n * performance is a greater concern. This class correctly presents the\n * <code>stale=true</code> header when the nonce has expired, so properly implemented user\n * agents will automatically renegotiate with a new nonce value (i.e. without presenting a\n * new password dialog box to the user).\n *\n * @author Ben Alex\n */\npublic class DigestAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean, Ordered {\n\n\tprivate static final Log logger = LogFactory.getLog(DigestAuthenticationEntryPoint.class);\n\n\tprivate @Nullable String key;\n\n\tprivate @Nullable String realmName;\n\n\tprivate int nonceValiditySeconds = 300;\n\n\tprivate int order = Integer.MAX_VALUE; // ~ default\n\n\t@Override\n\tpublic int getOrder() {\n\t\treturn this.order;\n\t}\n\n\tpublic void setOrder(int order) {\n\t\tthis.order = order;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.hasLength(this.realmName, \"realmName must be specified\");\n\t\tAssert.hasLength(this.key, \"key must be specified\");\n\t}\n\n\t@Override\n\tpublic void commence(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAuthenticationException authException) throws IOException {\n\t\t// compute a nonce (do not use remote IP address due to proxy farms) format of\n\t\t// nonce is: base64(expirationTime + \":\" + md5Hex(expirationTime + \":\" + key))\n\t\tlong expiryTime = System.currentTimeMillis() + (this.nonceValiditySeconds * 1000);\n\t\tString signatureValue = DigestAuthUtils.md5Hex(expiryTime + \":\" + this.key);\n\t\tString nonceValue = expiryTime + \":\" + signatureValue;\n\t\tString nonceValueBase64 = new String(Base64.getEncoder().encode(nonceValue.getBytes()));\n\t\t// qop is quality of protection, as defined by RFC 2617. We do not use opaque due\n\t\t// to IE violation of RFC 2617 in not representing opaque on subsequent requests\n\t\t// in same session.\n\t\tString authenticateHeader = \"Digest realm=\\\"\" + this.realmName + \"\\\", \" + \"qop=\\\"auth\\\", nonce=\\\"\"\n\t\t\t\t+ nonceValueBase64 + \"\\\"\";\n\t\tif (authException instanceof NonceExpiredException) {\n\t\t\tauthenticateHeader = authenticateHeader + \", stale=\\\"true\\\"\";\n\t\t}\n\t\tlogger.debug(LogMessage.format(\"WWW-Authenticate header sent to user agent: %s\", authenticateHeader));\n\t\tresponse.addHeader(\"WWW-Authenticate\", authenticateHeader);\n\t\tresponse.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());\n\t}\n\n\tpublic @Nullable String getKey() {\n\t\treturn this.key;\n\t}\n\n\tpublic int getNonceValiditySeconds() {\n\t\treturn this.nonceValiditySeconds;\n\t}\n\n\tpublic @Nullable String getRealmName() {\n\t\treturn this.realmName;\n\t}\n\n\tpublic void setKey(String key) {\n\t\tthis.key = key;\n\t}\n\n\tpublic void setNonceValiditySeconds(int nonceValiditySeconds) {\n\t\tthis.nonceValiditySeconds = nonceValiditySeconds;\n\t}\n\n\tpublic void setRealmName(String realmName) {\n\t\tthis.realmName = realmName;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006, 2009 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.io.IOException;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.MessageSourceAware;\nimport org.springframework.context.support.MessageSourceAccessor;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.SpringSecurityMessageSource;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.UserCache;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.core.userdetails.cache.NullUserCache;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Processes a HTTP request's Digest authorization headers, putting the result into the\n * <code>SecurityContextHolder</code>.\n * <p>\n * For a detailed background on what this filter is designed to process, refer to\n * <a href=\"https://www.ietf.org/rfc/rfc2617.txt\">RFC 2617</a> (which superseded RFC 2069,\n * although this filter support clients that implement either RFC 2617 or RFC 2069).\n * <p>\n * This filter can be used to provide Digest authentication services to both remoting\n * protocol clients (such as Hessian and SOAP) as well as standard user agents (such as\n * Internet Explorer and FireFox).\n * <p>\n * This Digest implementation has been designed to avoid needing to store session state\n * between invocations. All session management information is stored in the \"nonce\" that\n * is sent to the client by the {@link DigestAuthenticationEntryPoint}.\n * <p>\n * If authentication is successful, the resulting\n * {@link org.springframework.security.core.Authentication Authentication} object will be\n * placed into the <code>SecurityContextHolder</code>.\n * <p>\n * If authentication fails, an\n * {@link org.springframework.security.web.AuthenticationEntryPoint\n * AuthenticationEntryPoint} implementation is called. This must always be\n * {@link DigestAuthenticationEntryPoint}, which will prompt the user to authenticate\n * again via Digest authentication.\n * <p>\n * Note there are limitations to Digest authentication, although it is a more\n * comprehensive and secure solution than Basic authentication. Please see RFC 2617\n * section 4 for a full discussion on the advantages of Digest authentication over Basic\n * authentication, including commentary on the limitations that it still imposes.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @since 1.0.0\n */\npublic class DigestAuthenticationFilter extends GenericFilterBean implements MessageSourceAware {\n\n\tprivate static final Log logger = LogFactory.getLog(DigestAuthenticationFilter.class);\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate DigestAuthenticationEntryPoint authenticationEntryPoint;\n\n\tprotected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();\n\n\tprivate UserCache userCache = new NullUserCache();\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate UserDetailsService userDetailsService;\n\n\tprivate boolean passwordAlreadyEncoded = false;\n\n\tprivate boolean createAuthenticatedToken = false;\n\n\tprivate SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.userDetailsService, \"A UserDetailsService is required\");\n\t\tAssert.notNull(this.authenticationEntryPoint, \"A DigestAuthenticationEntryPoint is required\");\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tString header = request.getHeader(\"Authorization\");\n\t\tif (header == null || !header.startsWith(\"Digest \")) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tlogger.debug(LogMessage.format(\"Digest Authorization header received from user agent: %s\", header));\n\t\tDigestData digestAuth = new DigestData(header);\n\t\ttry {\n\t\t\tdigestAuth.validateAndDecode(this.authenticationEntryPoint.getKey(),\n\t\t\t\t\tthis.authenticationEntryPoint.getRealmName());\n\t\t}\n\t\tcatch (BadCredentialsException ex) {\n\t\t\tfail(request, response, ex);\n\t\t\treturn;\n\t\t}\n\t\t// Lookup password for presented username. N.B. DAO-provided password MUST be\n\t\t// clear text - not encoded/salted (unless this instance's passwordAlreadyEncoded\n\t\t// property is 'false')\n\t\tboolean cacheWasUsed = true;\n\t\t// username was validated as non-null in digestAuth validateAndDecode\n\t\tString username = Objects.requireNonNull(digestAuth.getUsername());\n\t\tUserDetails user = this.userCache.getUserFromCache(username);\n\t\tString serverDigestMd5;\n\t\ttry {\n\t\t\tif (user == null) {\n\t\t\t\tcacheWasUsed = false;\n\t\t\t\tuser = this.userDetailsService.loadUserByUsername(username);\n\t\t\t\tif (user == null) {\n\t\t\t\t\tthrow new AuthenticationServiceException(\n\t\t\t\t\t\t\t\"AuthenticationDao returned null, which is an interface contract violation\");\n\t\t\t\t}\n\t\t\t\tthis.userCache.putUserInCache(user);\n\t\t\t}\n\t\t\tserverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(), request.getMethod());\n\t\t\t// If digest is incorrect, try refreshing from backend and recomputing\n\t\t\tif (!serverDigestMd5.equals(digestAuth.getResponse()) && cacheWasUsed) {\n\t\t\t\tlogger.debug(\"Digest comparison failure; trying to refresh user from DAO in case password had changed\");\n\t\t\t\tuser = this.userDetailsService.loadUserByUsername(username);\n\t\t\t\tthis.userCache.putUserInCache(user);\n\t\t\t\tserverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(), request.getMethod());\n\t\t\t}\n\t\t}\n\t\tcatch (UsernameNotFoundException ex) {\n\t\t\tString message = this.messages.getMessage(\"DigestAuthenticationFilter.usernameNotFound\",\n\t\t\t\t\tnew Object[] { username }, \"Username {0} not found\");\n\t\t\tfail(request, response, new BadCredentialsException(message));\n\t\t\treturn;\n\t\t}\n\t\t// If digest is still incorrect, definitely reject authentication attempt\n\t\tif (!serverDigestMd5.equals(digestAuth.getResponse())) {\n\t\t\tlogger.debug(LogMessage.format(\n\t\t\t\t\t\"Expected response: '%s' but received: '%s'; is AuthenticationDao returning clear text passwords?\",\n\t\t\t\t\tserverDigestMd5, digestAuth.getResponse()));\n\t\t\tString message = this.messages.getMessage(\"DigestAuthenticationFilter.incorrectResponse\",\n\t\t\t\t\t\"Incorrect response\");\n\t\t\tfail(request, response, new BadCredentialsException(message));\n\t\t\treturn;\n\t\t}\n\t\t// To get this far, the digest must have been valid\n\t\t// Check the nonce has not expired\n\t\t// We do this last so we can direct the user agent its nonce is stale\n\t\t// but the request was otherwise appearing to be valid\n\t\tif (digestAuth.isNonceExpired()) {\n\t\t\tString message = this.messages.getMessage(\"DigestAuthenticationFilter.nonceExpired\",\n\t\t\t\t\t\"Nonce has expired/timed out\");\n\t\t\tfail(request, response, new NonceExpiredException(message));\n\t\t\treturn;\n\t\t}\n\t\tlogger.debug(LogMessage.format(\"Authentication success for user: '%s' with response: '%s'\", username,\n\t\t\t\tdigestAuth.getResponse()));\n\t\tAuthentication authentication = createSuccessfulAuthentication(request, user);\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tcontext.setAuthentication(authentication);\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\tthis.securityContextRepository.saveContext(context, request, response);\n\t\tchain.doFilter(request, response);\n\t}\n\n\tprivate Authentication createSuccessfulAuthentication(HttpServletRequest request, UserDetails user) {\n\t\tUsernamePasswordAuthenticationToken authRequest = getAuthRequest(user);\n\t\tauthRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));\n\t\treturn authRequest;\n\t}\n\n\tprivate UsernamePasswordAuthenticationToken getAuthRequest(UserDetails user) {\n\t\tif (this.createAuthenticatedToken) {\n\t\t\treturn UsernamePasswordAuthenticationToken.authenticated(user, user.getPassword(), user.getAuthorities());\n\t\t}\n\t\treturn UsernamePasswordAuthenticationToken.unauthenticated(user, user.getPassword());\n\t}\n\n\tprivate void fail(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)\n\t\t\tthrows IOException, ServletException {\n\t\tSecurityContext context = this.securityContextHolderStrategy.createEmptyContext();\n\t\tthis.securityContextHolderStrategy.setContext(context);\n\t\tlogger.debug(failed);\n\t\tthis.authenticationEntryPoint.commence(request, response, failed);\n\t}\n\n\tprotected final DigestAuthenticationEntryPoint getAuthenticationEntryPoint() {\n\t\treturn this.authenticationEntryPoint;\n\t}\n\n\tpublic UserCache getUserCache() {\n\t\treturn this.userCache;\n\t}\n\n\tpublic @Nullable UserDetailsService getUserDetailsService() {\n\t\treturn this.userDetailsService;\n\t}\n\n\tpublic void setAuthenticationDetailsSource(\n\t\t\tAuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {\n\t\tAssert.notNull(authenticationDetailsSource, \"AuthenticationDetailsSource required\");\n\t\tthis.authenticationDetailsSource = authenticationDetailsSource;\n\t}\n\n\tpublic void setAuthenticationEntryPoint(DigestAuthenticationEntryPoint authenticationEntryPoint) {\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t}\n\n\t@Override\n\tpublic void setMessageSource(MessageSource messageSource) {\n\t\tthis.messages = new MessageSourceAccessor(messageSource);\n\t}\n\n\tpublic void setPasswordAlreadyEncoded(boolean passwordAlreadyEncoded) {\n\t\tthis.passwordAlreadyEncoded = passwordAlreadyEncoded;\n\t}\n\n\tpublic void setUserCache(UserCache userCache) {\n\t\tthis.userCache = userCache;\n\t}\n\n\tpublic void setUserDetailsService(UserDetailsService userDetailsService) {\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t/**\n\t * If you set this property, the Authentication object, which is created after the\n\t * successful digest authentication will be marked as <b>authenticated</b> and filled\n\t * with the authorities loaded by the UserDetailsService. It therefore will not be\n\t * re-authenticated by your AuthenticationProvider. This means, that only the password\n\t * of the user is checked, but not the flags like isEnabled() or\n\t * isAccountNonExpired(). You will save some time by enabling this flag, as otherwise\n\t * your UserDetailsService will be called twice. A more secure option would be to\n\t * introduce a cache around your UserDetailsService, but if you don't use these flags,\n\t * you can also safely enable this option.\n\t * @param createAuthenticatedToken default is false\n\t */\n\tpublic void setCreateAuthenticatedToken(boolean createAuthenticatedToken) {\n\t\tthis.createAuthenticatedToken = createAuthenticatedToken;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on\n\t * authentication success. The default action is not to save the\n\t * {@link SecurityContext}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * Cannot be null.\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tprivate class DigestData {\n\n\t\tprivate final @Nullable String username;\n\n\t\tprivate final @Nullable String realm;\n\n\t\tprivate final @Nullable String nonce;\n\n\t\tprivate final @Nullable String uri;\n\n\t\tprivate final @Nullable String response;\n\n\t\tprivate final @Nullable String qop;\n\n\t\tprivate final @Nullable String nc;\n\n\t\tprivate final @Nullable String cnonce;\n\n\t\tprivate final String section212response;\n\n\t\tprivate long nonceExpiryTime;\n\n\t\tDigestData(String header) {\n\t\t\tthis.section212response = header.substring(7);\n\t\t\tString[] headerEntries = DigestAuthUtils.splitIgnoringQuotes(this.section212response, ',');\n\t\t\tMap<String, String> headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, \"=\", \"\\\"\");\n\t\t\tif (headerMap == null) {\n\t\t\t\theaderMap = Collections.emptyMap();\n\t\t\t}\n\t\t\tthis.username = headerMap.get(\"username\");\n\t\t\tthis.realm = headerMap.get(\"realm\");\n\t\t\tthis.nonce = headerMap.get(\"nonce\");\n\t\t\tthis.uri = headerMap.get(\"uri\");\n\t\t\tthis.response = headerMap.get(\"response\");\n\t\t\tthis.qop = headerMap.get(\"qop\"); // RFC 2617 extension\n\t\t\tthis.nc = headerMap.get(\"nc\"); // RFC 2617 extension\n\t\t\tthis.cnonce = headerMap.get(\"cnonce\"); // RFC 2617 extension\n\t\t\tlogger.debug(\n\t\t\t\t\tLogMessage.format(\"Extracted username: '%s'; realm: '%s'; nonce: '%s'; uri: '%s'; response: '%s'\",\n\t\t\t\t\t\t\tthis.username, this.realm, this.nonce, this.uri, this.response));\n\t\t}\n\n\t\tvoid validateAndDecode(@Nullable String entryPointKey, @Nullable String expectedRealm)\n\t\t\t\tthrows BadCredentialsException {\n\t\t\t// Check all required parameters were supplied (ie RFC 2069)\n\t\t\tif ((this.username == null) || (this.realm == null) || (this.nonce == null) || (this.uri == null)\n\t\t\t\t\t|| (this.response == null)) {\n\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\"DigestAuthenticationFilter.missingMandatory\", new Object[] { this.section212response },\n\t\t\t\t\t\t\"Missing mandatory digest value; received header {0}\"));\n\t\t\t}\n\t\t\t// Check all required parameters for an \"auth\" qop were supplied (ie RFC 2617)\n\t\t\tif (\"auth\".equals(this.qop)) {\n\t\t\t\tif ((this.nc == null) || (this.cnonce == null)) {\n\t\t\t\t\tlogger.debug(LogMessage.format(\"extracted nc: '%s'; cnonce: '%s'\", this.nc, this.cnonce));\n\t\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\t\"DigestAuthenticationFilter.missingAuth\", new Object[] { this.section212response },\n\t\t\t\t\t\t\t\"Missing mandatory digest value; received header {0}\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Check realm name equals what we expected\n\t\t\tif (!this.realm.equals(expectedRealm)) {\n\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\"DigestAuthenticationFilter.incorrectRealm\", new Object[] { this.realm, expectedRealm },\n\t\t\t\t\t\t\"Response realm name '{0}' does not match system realm name of '{1}'\"));\n\t\t\t}\n\t\t\t// Check nonce was Base64 encoded (as sent by DigestAuthenticationEntryPoint)\n\t\t\tfinal byte[] nonceBytes;\n\t\t\ttry {\n\t\t\t\tnonceBytes = Base64.getDecoder().decode(this.nonce.getBytes());\n\t\t\t}\n\t\t\tcatch (IllegalArgumentException ex) {\n\t\t\t\tthrow new BadCredentialsException(\n\t\t\t\t\t\tDigestAuthenticationFilter.this.messages.getMessage(\"DigestAuthenticationFilter.nonceEncoding\",\n\t\t\t\t\t\t\t\tnew Object[] { this.nonce }, \"Nonce is not encoded in Base64; received nonce {0}\"));\n\t\t\t}\n\t\t\t// Decode nonce from Base64 format of nonce is: base64(expirationTime + \":\" +\n\t\t\t// md5Hex(expirationTime + \":\" + key))\n\t\t\tString nonceAsPlainText = new String(nonceBytes);\n\t\t\tString[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, \":\");\n\t\t\tif (nonceTokens.length != 2) {\n\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\"DigestAuthenticationFilter.nonceNotTwoTokens\", new Object[] { nonceAsPlainText },\n\t\t\t\t\t\t\"Nonce should have yielded two tokens but was {0}\"));\n\t\t\t}\n\t\t\t// Extract expiry time from nonce\n\t\t\ttry {\n\t\t\t\tthis.nonceExpiryTime = Long.valueOf(nonceTokens[0]);\n\t\t\t}\n\t\t\tcatch (NumberFormatException nfe) {\n\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\"DigestAuthenticationFilter.nonceNotNumeric\", new Object[] { nonceAsPlainText },\n\t\t\t\t\t\t\"Nonce token should have yielded a numeric first token, but was {0}\"));\n\t\t\t}\n\t\t\t// Check signature of nonce matches this expiry time\n\t\t\tString expectedNonceSignature = DigestAuthUtils.md5Hex(this.nonceExpiryTime + \":\" + entryPointKey);\n\t\t\tif (!expectedNonceSignature.equals(nonceTokens[1])) {\n\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\"DigestAuthenticationFilter.nonceCompromised\", new Object[] { nonceAsPlainText },\n\t\t\t\t\t\t\"Nonce token compromised {0}\"));\n\t\t\t}\n\t\t}\n\n\t\tString calculateServerDigest(@Nullable String password, String httpMethod) {\n\t\t\t// Compute the expected response-digest (will be in hex form). Don't catch\n\t\t\t// IllegalArgumentException (already checked validity)\n\t\t\treturn DigestAuthUtils.generateDigest(DigestAuthenticationFilter.this.passwordAlreadyEncoded, this.username,\n\t\t\t\t\tthis.realm, password, httpMethod, this.uri, this.qop, this.nonce, this.nc, this.cnonce);\n\t\t}\n\n\t\tboolean isNonceExpired() {\n\t\t\tlong now = System.currentTimeMillis();\n\t\t\treturn this.nonceExpiryTime < now;\n\t\t}\n\n\t\t@Nullable String getUsername() {\n\t\t\treturn this.username;\n\t\t}\n\n\t\t@Nullable String getResponse() {\n\t\t\treturn this.response;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/www/NonceExpiredException.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.io.Serial;\n\nimport org.springframework.security.core.AuthenticationException;\n\n/**\n * Thrown if an authentication request is rejected because the digest nonce has expired.\n *\n * @author Ben Alex\n */\npublic class NonceExpiredException extends AuthenticationException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3487244679050681257L;\n\n\t/**\n\t * Constructs a <code>NonceExpiredException</code> with the specified message.\n\t * @param msg the detail message\n\t */\n\tpublic NonceExpiredException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\t/**\n\t * Constructs a <code>NonceExpiredException</code> with the specified message and root\n\t * cause.\n\t * @param msg the detail message\n\t * @param cause root cause\n\t */\n\tpublic NonceExpiredException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/authentication/www/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WWW-Authenticate based authentication mechanism implementations: Basic and Digest\n * authentication.\n */\n@NullMarked\npackage org.springframework.security.web.authentication.www;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/bind/annotation/AuthenticationPrincipal.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.bind.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Annotation that binds a method parameter or method return value to the\n * {@link Authentication#getPrincipal()}. This is necessary to signal that the argument\n * should be resolved to the current user rather than a user that might be edited on a\n * form.\n *\n * @deprecated Use\n * {@link org.springframework.security.core.annotation.AuthenticationPrincipal} instead.\n * @author Rob Winch\n * @since 3.2\n */\n@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Deprecated\npublic @interface AuthenticationPrincipal {\n\n\t/**\n\t * True if a {@link ClassCastException} should be thrown when the current\n\t * {@link Authentication#getPrincipal()} is the incorrect type. Default is false.\n\t * @return\n\t */\n\tboolean errorOnInvalidType() default false;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/bind/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Annotations for binding web security APIs.\n */\n@NullMarked\npackage org.springframework.security.web.bind.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/bind/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * APIs for binding web security APIs.\n */\n@NullMarked\npackage org.springframework.security.web.bind;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/bind/support/AuthenticationPrincipalArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.bind.support;\n\nimport java.lang.annotation.Annotation;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.bind.annotation.AuthenticationPrincipal;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.support.WebDataBinderFactory;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.method.support.ModelAndViewContainer;\n\n/**\n * Allows resolving the {@link Authentication#getPrincipal()} using the\n * {@link AuthenticationPrincipal} annotation. For example, the following\n * {@link Controller}:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;RequestMapping(\"/user/current/show\")\n *     public String show(@AuthenticationPrincipal CustomUser customUser) {\n *         // do something with CustomUser\n *         return \"view\";\n *     }\n * }\n * </pre>\n *\n * <p>\n * Will resolve the CustomUser argument using {@link Authentication#getPrincipal()} from\n * the {@link SecurityContextHolder}. If the {@link Authentication} or\n * {@link Authentication#getPrincipal()} is null, it will return null. If the types do not\n * match, null will be returned unless\n * {@link AuthenticationPrincipal#errorOnInvalidType()} is true in which case a\n * {@link ClassCastException} will be thrown.\n *\n * <p>\n * Alternatively, users can create a custom meta annotation as shown below:\n *\n * <pre>\n * &#064;Target({ ElementType.PARAMETER })\n * &#064;Retention(RetentionPolicy.RUNTIME)\n * &#064;AuthenticationPrincipal\n * public @interface CurrentUser {\n * }\n * </pre>\n *\n * <p>\n * The custom annotation can then be used instead. For example:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;RequestMapping(\"/user/current/show\")\n *     public String show(@CurrentUser CustomUser customUser) {\n *         // do something with CustomUser\n *         return \"view\";\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @since 3.2\n * @deprecated Use\n * {@link org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver}\n * instead.\n */\n@Deprecated\npublic final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null;\n\t}\n\n\t@Override\n\tpublic @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,\n\t\t\tNativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\treturn null;\n\t\t}\n\t\tObject principal = authentication.getPrincipal();\n\t\tif (principal != null && !parameter.getParameterType().isAssignableFrom(principal.getClass())) {\n\t\t\t@Nullable AuthenticationPrincipal authPrincipal = findMethodAnnotation(AuthenticationPrincipal.class, parameter);\n\t\t\tif (authPrincipal != null && authPrincipal.errorOnInvalidType()) {\n\t\t\t\tthrow new ClassCastException(principal + \" is not assignable to \" + parameter.getParameterType());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn principal;\n\t}\n\n\t/**\n\t * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.\n\t * @param annotationClass the class of the {@link Annotation} to find on the\n\t * {@link MethodParameter}\n\t * @param parameter the {@link MethodParameter} to search for an {@link Annotation}\n\t * @return the {@link Annotation} that was found or null.\n\t */\n\tprivate <T extends Annotation> @Nullable T findMethodAnnotation(Class<T> annotationClass,\n\t\t\tMethodParameter parameter) {\n\t\tT annotation = parameter.getParameterAnnotation(annotationClass);\n\t\tif (annotation != null) {\n\t\t\treturn annotation;\n\t\t}\n\t\tAnnotation[] annotationsToSearch = parameter.getParameterAnnotations();\n\t\tfor (Annotation toSearch : annotationsToSearch) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);\n\t\t\tif (annotation != null) {\n\t\t\t\treturn annotation;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/bind/support/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for binding web security APIs.\n */\n@NullMarked\npackage org.springframework.security.web.bind.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/AbstractSecurityWebApplicationInitializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.Set;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterRegistration.Dynamic;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.SessionTrackingMode;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.Conventions;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.web.session.HttpSessionEventPublisher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.WebApplicationInitializer;\nimport org.springframework.web.context.AbstractContextLoaderInitializer;\nimport org.springframework.web.context.ContextLoaderListener;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\nimport org.springframework.web.filter.DelegatingFilterProxy;\n\n/**\n * Registers the {@link DelegatingFilterProxy} to use the springSecurityFilterChain before\n * any other registered {@link Filter}. When used with\n * {@link #AbstractSecurityWebApplicationInitializer(Class...)}, it will also register a\n * {@link ContextLoaderListener}. When used with\n * {@link #AbstractSecurityWebApplicationInitializer()}, this class is typically used in\n * addition to a subclass of {@link AbstractContextLoaderInitializer}.\n *\n * <p>\n * By default the {@link DelegatingFilterProxy} is registered without support, but can be\n * enabled by overriding {@link #isAsyncSecuritySupported()} and\n * {@link #getSecurityDispatcherTypes()}.\n * </p>\n *\n * <p>\n * Additional configuration before and after the springSecurityFilterChain can be added by\n * overriding {@link #afterSpringSecurityFilterChain(ServletContext)}.\n * </p>\n *\n *\n * <h2>Caveats</h2>\n * <p>\n * Subclasses of AbstractDispatcherServletInitializer will register their filters before\n * any other {@link Filter}. This means that you will typically want to ensure subclasses\n * of AbstractDispatcherServletInitializer are invoked first. This can be done by ensuring\n * the {@link Order} or {@link Ordered} of AbstractDispatcherServletInitializer are sooner\n * than subclasses of {@link AbstractSecurityWebApplicationInitializer}.\n * </p>\n *\n * @author Rob Winch\n * @author Keesun Baik\n */\npublic abstract class AbstractSecurityWebApplicationInitializer implements WebApplicationInitializer {\n\n\tprivate static final String SERVLET_CONTEXT_PREFIX = \"org.springframework.web.servlet.FrameworkServlet.CONTEXT.\";\n\n\tpublic static final String DEFAULT_FILTER_NAME = \"springSecurityFilterChain\";\n\n\tprivate final Class<?> @Nullable [] configurationClasses;\n\n\t/**\n\t * Creates a new instance that assumes the Spring Security configuration is loaded by\n\t * some other means than this class. For example, a user might create a\n\t * {@link ContextLoaderListener} using a subclass of\n\t * {@link AbstractContextLoaderInitializer}.\n\t *\n\t * @see ContextLoaderListener\n\t */\n\tprotected AbstractSecurityWebApplicationInitializer() {\n\t\tthis.configurationClasses = null;\n\t}\n\n\t/**\n\t * Creates a new instance that will instantiate the {@link ContextLoaderListener} with\n\t * the specified classes.\n\t * @param configurationClasses\n\t */\n\tprotected AbstractSecurityWebApplicationInitializer(Class<?>... configurationClasses) {\n\t\tthis.configurationClasses = configurationClasses;\n\t}\n\n\t@Override\n\tpublic final void onStartup(ServletContext servletContext) {\n\t\tbeforeSpringSecurityFilterChain(servletContext);\n\t\tif (this.configurationClasses != null) {\n\t\t\tAnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();\n\t\t\trootAppContext.register(this.configurationClasses);\n\t\t\tservletContext.addListener(new ContextLoaderListener(rootAppContext));\n\t\t}\n\t\tif (enableHttpSessionEventPublisher()) {\n\t\t\tservletContext.addListener(\"org.springframework.security.web.session.HttpSessionEventPublisher\");\n\t\t}\n\t\tservletContext.setSessionTrackingModes(getSessionTrackingModes());\n\t\tinsertSpringSecurityFilterChain(servletContext);\n\t\tafterSpringSecurityFilterChain(servletContext);\n\t}\n\n\t/**\n\t * Override this if {@link HttpSessionEventPublisher} should be added as a listener.\n\t * This should be true, if session management has specified a maximum number of\n\t * sessions.\n\t * @return true to add {@link HttpSessionEventPublisher}, else false\n\t */\n\tprotected boolean enableHttpSessionEventPublisher() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Registers the springSecurityFilterChain\n\t * @param servletContext the {@link ServletContext}\n\t */\n\tprivate void insertSpringSecurityFilterChain(ServletContext servletContext) {\n\t\tString filterName = DEFAULT_FILTER_NAME;\n\t\tDelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(filterName);\n\t\tString contextAttribute = getWebApplicationContextAttribute();\n\t\tif (contextAttribute != null) {\n\t\t\tspringSecurityFilterChain.setContextAttribute(contextAttribute);\n\t\t}\n\t\tregisterFilter(servletContext, true, filterName, springSecurityFilterChain);\n\t}\n\n\t/**\n\t * Inserts the provided {@link Filter}s before existing {@link Filter}s using default\n\t * generated names, {@link #getSecurityDispatcherTypes()}, and\n\t * {@link #isAsyncSecuritySupported()}.\n\t * @param servletContext the {@link ServletContext} to use\n\t * @param filters the {@link Filter}s to register\n\t */\n\tprotected final void insertFilters(ServletContext servletContext, Filter... filters) {\n\t\tregisterFilters(servletContext, true, filters);\n\t}\n\n\t/**\n\t * Inserts the provided {@link Filter}s after existing {@link Filter}s using default\n\t * generated names, {@link #getSecurityDispatcherTypes()}, and\n\t * {@link #isAsyncSecuritySupported()}.\n\t * @param servletContext the {@link ServletContext} to use\n\t * @param filters the {@link Filter}s to register\n\t */\n\tprotected final void appendFilters(ServletContext servletContext, Filter... filters) {\n\t\tregisterFilters(servletContext, false, filters);\n\t}\n\n\t/**\n\t * Registers the provided {@link Filter}s using default generated names,\n\t * {@link #getSecurityDispatcherTypes()}, and {@link #isAsyncSecuritySupported()}.\n\t * @param servletContext the {@link ServletContext} to use\n\t * @param insertBeforeOtherFilters if true, will insert the provided {@link Filter}s\n\t * before other {@link Filter}s. Otherwise, will insert the {@link Filter}s after\n\t * other {@link Filter}s.\n\t * @param filters the {@link Filter}s to register\n\t */\n\tprivate void registerFilters(ServletContext servletContext, boolean insertBeforeOtherFilters, Filter... filters) {\n\t\tAssert.notEmpty(filters, \"filters cannot be null or empty\");\n\t\tfor (Filter filter : filters) {\n\t\t\tAssert.notNull(filter, () -> \"filters cannot contain null values. Got \" + Arrays.asList(filters));\n\t\t\tString filterName = Conventions.getVariableName(filter);\n\t\t\tregisterFilter(servletContext, insertBeforeOtherFilters, filterName, filter);\n\t\t}\n\t}\n\n\t/**\n\t * Registers the provided filter using the {@link #isAsyncSecuritySupported()} and\n\t * {@link #getSecurityDispatcherTypes()}.\n\t * @param servletContext\n\t * @param insertBeforeOtherFilters should this Filter be inserted before or after\n\t * other {@link Filter}\n\t * @param filterName\n\t * @param filter\n\t */\n\tprivate void registerFilter(ServletContext servletContext, boolean insertBeforeOtherFilters, String filterName,\n\t\t\tFilter filter) {\n\t\tDynamic registration = servletContext.addFilter(filterName, filter);\n\t\tAssert.state(registration != null, () -> \"Duplicate Filter registration for '\" + filterName\n\t\t\t\t+ \"'. Check to ensure the Filter is only configured once.\");\n\t\tregistration.setAsyncSupported(isAsyncSecuritySupported());\n\t\tEnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();\n\t\tregistration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters, \"/*\");\n\t}\n\n\t/**\n\t * Returns the {@link DelegatingFilterProxy#getContextAttribute()} or null if the\n\t * parent {@link ApplicationContext} should be used. The default behavior is to use\n\t * the parent {@link ApplicationContext}.\n\t *\n\t * <p>\n\t * If {@link #getDispatcherWebApplicationContextSuffix()} is non-null the\n\t * {@link WebApplicationContext} for the Dispatcher will be used. This means the child\n\t * {@link ApplicationContext} is used to look up the springSecurityFilterChain bean.\n\t * </p>\n\t * @return the {@link DelegatingFilterProxy#getContextAttribute()} or null if the\n\t * parent {@link ApplicationContext} should be used\n\t */\n\tprivate @Nullable String getWebApplicationContextAttribute() {\n\t\tString dispatcherServletName = getDispatcherWebApplicationContextSuffix();\n\t\tif (dispatcherServletName == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn SERVLET_CONTEXT_PREFIX + dispatcherServletName;\n\t}\n\n\t/**\n\t * Determines how a session should be tracked. By default,\n\t * {@link SessionTrackingMode#COOKIE} is used.\n\t *\n\t * <p>\n\t * Note that {@link SessionTrackingMode#URL} is intentionally omitted to help\n\t * protected against <a href=\"https://en.wikipedia.org/wiki/Session_fixation\">session\n\t * fixation attacks</a>. {@link SessionTrackingMode#SSL} is omitted because SSL\n\t * configuration is required for this to work.\n\t * </p>\n\t *\n\t * <p>\n\t * Subclasses can override this method to make customizations.\n\t * </p>\n\t * @return\n\t */\n\tprotected Set<SessionTrackingMode> getSessionTrackingModes() {\n\t\treturn EnumSet.of(SessionTrackingMode.COOKIE);\n\t}\n\n\t/**\n\t * Return the &lt;servlet-name&gt; to use the DispatcherServlet's\n\t * {@link WebApplicationContext} to find the {@link DelegatingFilterProxy} or null to\n\t * use the parent {@link ApplicationContext}.\n\t *\n\t * <p>\n\t * For example, if you are using AbstractDispatcherServletInitializer or\n\t * AbstractAnnotationConfigDispatcherServletInitializer and using the provided Servlet\n\t * name, you can return \"dispatcher\" from this method to use the DispatcherServlet's\n\t * {@link WebApplicationContext}.\n\t * </p>\n\t * @return the &lt;servlet-name&gt; of the DispatcherServlet to use its\n\t * {@link WebApplicationContext} or null (default) to use the parent\n\t * {@link ApplicationContext}.\n\t */\n\tprotected @Nullable String getDispatcherWebApplicationContextSuffix() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Invoked before the springSecurityFilterChain is added.\n\t * @param servletContext the {@link ServletContext}\n\t */\n\tprotected void beforeSpringSecurityFilterChain(ServletContext servletContext) {\n\n\t}\n\n\t/**\n\t * Invoked after the springSecurityFilterChain is added.\n\t * @param servletContext the {@link ServletContext}\n\t */\n\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\n\t}\n\n\t/**\n\t * Get the {@link DispatcherType} for the springSecurityFilterChain.\n\t * @return\n\t */\n\tprotected EnumSet<DispatcherType> getSecurityDispatcherTypes() {\n\t\treturn EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC, DispatcherType.FORWARD,\n\t\t\t\tDispatcherType.INCLUDE);\n\t}\n\n\t/**\n\t * Determine if the springSecurityFilterChain should be marked as supporting async.\n\t * Default is true.\n\t * @return true if springSecurityFilterChain should be marked as supporting async\n\t */\n\tprotected boolean isAsyncSecuritySupported() {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/DelegatingSecurityContextRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.context.DeferredSecurityContext;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.util.Assert;\n\n/**\n * @author Steve Riesenberg\n * @author Josh Cummings\n * @since 5.8\n */\npublic final class DelegatingSecurityContextRepository implements SecurityContextRepository {\n\n\tprivate final List<SecurityContextRepository> delegates;\n\n\tpublic DelegatingSecurityContextRepository(SecurityContextRepository... delegates) {\n\t\tthis(Arrays.asList(delegates));\n\t}\n\n\tpublic DelegatingSecurityContextRepository(List<SecurityContextRepository> delegates) {\n\t\tAssert.notEmpty(delegates, \"delegates cannot be empty\");\n\t\tthis.delegates = delegates;\n\t}\n\n\t/**\n\t * @deprecated\n\t * @see SecurityContextRepository#loadContext\n\t */\n\t@Override\n\t@Deprecated\n\tpublic SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {\n\t\tSecurityContext result = SecurityContextHolder.createEmptyContext();\n\t\tfor (SecurityContextRepository delegate : this.delegates) {\n\t\t\tSecurityContext delegateResult = delegate.loadContext(requestResponseHolder);\n\t\t\tif (result == null || delegate.containsContext(requestResponseHolder.getRequest())) {\n\t\t\t\tresult = delegateResult;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {\n\t\tDeferredSecurityContext deferredSecurityContext = null;\n\t\tfor (SecurityContextRepository delegate : this.delegates) {\n\t\t\tif (deferredSecurityContext == null) {\n\t\t\t\tdeferredSecurityContext = delegate.loadDeferredContext(request);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tDeferredSecurityContext next = delegate.loadDeferredContext(request);\n\t\t\t\tdeferredSecurityContext = new DelegatingDeferredSecurityContext(deferredSecurityContext, next);\n\t\t\t}\n\t\t}\n\t\tif (deferredSecurityContext == null) {\n\t\t\tthrow new IllegalStateException(\"No deferredSecurityContext found\");\n\t\t}\n\t\treturn deferredSecurityContext;\n\t}\n\n\t@Override\n\tpublic void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {\n\t\tfor (SecurityContextRepository delegate : this.delegates) {\n\t\t\tdelegate.saveContext(context, request, response);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean containsContext(HttpServletRequest request) {\n\t\tfor (SecurityContextRepository delegate : this.delegates) {\n\t\t\tif (delegate.containsContext(request)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tstatic final class DelegatingDeferredSecurityContext implements DeferredSecurityContext {\n\n\t\tprivate final DeferredSecurityContext previous;\n\n\t\tprivate final DeferredSecurityContext next;\n\n\t\tDelegatingDeferredSecurityContext(DeferredSecurityContext previous, DeferredSecurityContext next) {\n\t\t\tthis.previous = previous;\n\t\t\tthis.next = next;\n\t\t}\n\n\t\t@Override\n\t\tpublic SecurityContext get() {\n\t\t\tSecurityContext securityContext = this.previous.get();\n\t\t\tif (!this.previous.isGenerated()) {\n\t\t\t\treturn securityContext;\n\t\t\t}\n\t\t\treturn this.next.get();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGenerated() {\n\t\t\treturn this.previous.isGenerated() && this.next.isGenerated();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/HttpRequestResponseHolder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * Used to pass the incoming request to\n * {@link SecurityContextRepository#loadContext(HttpRequestResponseHolder)}, allowing the\n * method to swap the request for a wrapped version, as well as returning the\n * <tt>SecurityContext</tt> value.\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use\n * {@link SecurityContextRepository#loadDeferredContext(HttpServletRequest)}\n */\n@Deprecated\npublic final class HttpRequestResponseHolder {\n\n\tprivate HttpServletRequest request;\n\n\tprivate HttpServletResponse response;\n\n\tpublic HttpRequestResponseHolder(HttpServletRequest request, HttpServletResponse response) {\n\t\tthis.request = request;\n\t\tthis.response = response;\n\t}\n\n\tpublic HttpServletRequest getRequest() {\n\t\treturn this.request;\n\t}\n\n\tpublic void setRequest(HttpServletRequest request) {\n\t\tthis.request = request;\n\t}\n\n\tpublic HttpServletResponse getResponse() {\n\t\treturn this.response;\n\t}\n\n\tpublic void setResponse(HttpServletResponse response) {\n\t\tthis.response = response;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.AsyncContext;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.Transient;\nimport org.springframework.security.core.context.DeferredSecurityContext;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.WebUtils;\n\n/**\n * A {@code SecurityContextRepository} implementation which stores the security context in\n * the {@code HttpSession} between requests.\n * <p>\n * The {@code HttpSession} will be queried to retrieve the {@code SecurityContext} in the\n * <tt>loadContext</tt> method (using the key {@link #SPRING_SECURITY_CONTEXT_KEY} by\n * default). If a valid {@code SecurityContext} cannot be obtained from the\n * {@code HttpSession} for whatever reason, a fresh {@code SecurityContext} will be\n * created by calling by {@link SecurityContextHolder#createEmptyContext()} and this\n * instance will be returned instead.\n * <p>\n * When <tt>saveContext</tt> is called, the context will be stored under the same key,\n * provided\n * <ol>\n * <li>The value has changed</li>\n * <li>The configured <tt>AuthenticationTrustResolver</tt> does not report that the\n * contents represent an anonymous user</li>\n * </ol>\n * <p>\n * With the standard configuration, no {@code HttpSession} will be created during\n * <tt>loadContext</tt> if one does not already exist. When <tt>saveContext</tt> is called\n * at the end of the web request, and no session exists, a new {@code HttpSession} will\n * <b>only</b> be created if the supplied {@code SecurityContext} is not equal to an empty\n * {@code SecurityContext} instance. This avoids needless <code>HttpSession</code>\n * creation, but automates the storage of changes made to the context during the request.\n * Note that if {@link SecurityContextPersistenceFilter} is configured to eagerly create\n * sessions, then the session-minimisation logic applied here will not make any\n * difference. If you are using eager session creation, then you should ensure that the\n * <tt>allowSessionCreation</tt> property of this class is set to <tt>true</tt> (the\n * default).\n * <p>\n * If for whatever reason no {@code HttpSession} should <b>ever</b> be created (for\n * example, if Basic authentication is being used or similar clients that will never\n * present the same {@code jsessionid}), then {@link #setAllowSessionCreation(boolean)\n * allowSessionCreation} should be set to <code>false</code>. Only do this if you really\n * need to conserve server memory and ensure all classes using the\n * {@code SecurityContextHolder} are designed to have no persistence of the\n * {@code SecurityContext} between web requests.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class HttpSessionSecurityContextRepository implements SecurityContextRepository {\n\n\t/**\n\t * The default key under which the security context will be stored in the session.\n\t */\n\tpublic static final String SPRING_SECURITY_CONTEXT_KEY = \"SPRING_SECURITY_CONTEXT\";\n\n\tprotected final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t/**\n\t * SecurityContext instance used to check for equality with default (unauthenticated)\n\t * content\n\t */\n\tprivate Object contextObject = this.securityContextHolderStrategy.createEmptyContext();\n\n\tprivate boolean allowSessionCreation = true;\n\n\tprivate boolean disableUrlRewriting = false;\n\n\tprivate String springSecurityContextKey = SPRING_SECURITY_CONTEXT_KEY;\n\n\tprivate AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\t/**\n\t * Gets the security context for the current request (if available) and returns it.\n\t * <p>\n\t * If the session is null, the context object is null or the context object stored in\n\t * the session is not an instance of {@code SecurityContext}, a new context object\n\t * will be generated and returned.\n\t * @deprecated please see {@link SecurityContextRepository#loadContext}\n\t */\n\t@Deprecated\n\t@Override\n\tpublic SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {\n\t\tHttpServletRequest request = requestResponseHolder.getRequest();\n\t\tHttpServletResponse response = requestResponseHolder.getResponse();\n\t\tHttpSession httpSession = request.getSession(false);\n\t\tSecurityContext context = readSecurityContextFromSession(httpSession);\n\t\tif (context == null) {\n\t\t\tcontext = generateNewContext();\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Created %s\", context));\n\t\t\t}\n\t\t}\n\t\tif (response != null) {\n\t\t\tSaveToSessionResponseWrapper wrappedResponse = new SaveToSessionResponseWrapper(response, request,\n\t\t\t\t\thttpSession != null, context);\n\t\t\twrappedResponse.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\t\trequestResponseHolder.setResponse(wrappedResponse);\n\t\t\trequestResponseHolder.setRequest(new SaveToSessionRequestWrapper(request, wrappedResponse));\n\t\t}\n\t\treturn context;\n\t}\n\n\t@Override\n\tpublic DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {\n\t\tSupplier<SecurityContext> supplier = () -> readSecurityContextFromSession(request.getSession(false));\n\t\treturn new SupplierDeferredSecurityContext(supplier, this.securityContextHolderStrategy);\n\t}\n\n\t@Override\n\tpublic void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {\n\t\tSaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils.getNativeResponse(response,\n\t\t\t\tSaveContextOnUpdateOrErrorResponseWrapper.class);\n\t\tif (responseWrapper == null) {\n\t\t\tsaveContextInHttpSession(context, request);\n\t\t\treturn;\n\t\t}\n\t\tresponseWrapper.saveContext(context);\n\t}\n\n\tprivate void saveContextInHttpSession(SecurityContext context, HttpServletRequest request) {\n\t\tif (isTransient(context) || isTransient(context.getAuthentication())) {\n\t\t\treturn;\n\t\t}\n\t\tSecurityContext emptyContext = generateNewContext();\n\t\tif (emptyContext.equals(context)) {\n\t\t\tHttpSession session = request.getSession(false);\n\t\t\tremoveContextFromSession(context, session);\n\t\t}\n\t\telse {\n\t\t\tboolean createSession = this.allowSessionCreation;\n\t\t\tHttpSession session = request.getSession(createSession);\n\t\t\tsetContextInSession(context, session);\n\t\t}\n\t}\n\n\tprivate void setContextInSession(SecurityContext context, HttpSession session) {\n\t\tif (session != null) {\n\t\t\tsession.setAttribute(this.springSecurityContextKey, context);\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Stored %s to HttpSession [%s]\", context, session));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void removeContextFromSession(SecurityContext context, HttpSession session) {\n\t\tif (session != null) {\n\t\t\tsession.removeAttribute(this.springSecurityContextKey);\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Removed %s from HttpSession [%s]\", context, session));\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean containsContext(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn session.getAttribute(this.springSecurityContextKey) != null;\n\t}\n\n\t/**\n\t * @param httpSession the session obtained from the request.\n\t */\n\tprivate @Nullable SecurityContext readSecurityContextFromSession(HttpSession httpSession) {\n\t\tif (httpSession == null) {\n\t\t\tthis.logger.trace(\"No HttpSession currently exists\");\n\t\t\treturn null;\n\t\t}\n\t\t// Session exists, so try to obtain a context from it.\n\t\tObject contextFromSession = httpSession.getAttribute(this.springSecurityContextKey);\n\t\tif (contextFromSession == null) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Did not find SecurityContext in HttpSession %s \"\n\t\t\t\t\t\t+ \"using the SPRING_SECURITY_CONTEXT session attribute\", httpSession.getId()));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t// We now have the security context object from the session.\n\t\tif (!(contextFromSession instanceof SecurityContext)) {\n\t\t\tthis.logger.warn(LogMessage.format(\n\t\t\t\t\t\"%s did not contain a SecurityContext but contained: '%s'; are you improperly \"\n\t\t\t\t\t\t\t+ \"modifying the HttpSession directly (you should always use SecurityContextHolder) \"\n\t\t\t\t\t\t\t+ \"or using the HttpSession attribute reserved for this class?\",\n\t\t\t\t\tthis.springSecurityContextKey, contextFromSession));\n\t\t\treturn null;\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger\n\t\t\t\t.trace(LogMessage.format(\"Retrieved %s from %s\", contextFromSession, this.springSecurityContextKey));\n\t\t}\n\t\telse if (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Retrieved %s\", contextFromSession));\n\t\t}\n\t\t// Everything OK. The only non-null return from this method.\n\t\treturn (SecurityContext) contextFromSession;\n\t}\n\n\t/**\n\t * By default, calls {@link SecurityContextHolder#createEmptyContext()} to obtain a\n\t * new context (there should be no context present in the holder when this method is\n\t * called). Using this approach the context creation strategy is decided by the\n\t * {@link SecurityContextHolderStrategy} in use. The default implementations will\n\t * return a new <tt>SecurityContextImpl</tt>.\n\t * @return a new SecurityContext instance. Never null.\n\t */\n\tprotected SecurityContext generateNewContext() {\n\t\treturn this.securityContextHolderStrategy.createEmptyContext();\n\t}\n\n\t/**\n\t * If set to true (the default), a session will be created (if required) to store the\n\t * security context if it is determined that its contents are different from the\n\t * default empty context value.\n\t * <p>\n\t * Note that setting this flag to false does not prevent this class from storing the\n\t * security context. If your application (or another filter) creates a session, then\n\t * the security context will still be stored for an authenticated user.\n\t * @param allowSessionCreation\n\t */\n\tpublic void setAllowSessionCreation(boolean allowSessionCreation) {\n\t\tthis.allowSessionCreation = allowSessionCreation;\n\t}\n\n\t/**\n\t * Allows the use of session identifiers in URLs to be disabled. Off by default.\n\t * @param disableUrlRewriting set to <tt>true</tt> to disable URL encoding methods in\n\t * the response wrapper and prevent the use of <tt>jsessionid</tt> parameters.\n\t */\n\tpublic void setDisableUrlRewriting(boolean disableUrlRewriting) {\n\t\tthis.disableUrlRewriting = disableUrlRewriting;\n\t}\n\n\t/**\n\t * Allows the session attribute name to be customized for this repository instance.\n\t * @param springSecurityContextKey the key under which the security context will be\n\t * stored. Defaults to {@link #SPRING_SECURITY_CONTEXT_KEY}.\n\t */\n\tpublic void setSpringSecurityContextKey(String springSecurityContextKey) {\n\t\tAssert.hasText(springSecurityContextKey, \"springSecurityContextKey cannot be empty\");\n\t\tthis.springSecurityContextKey = springSecurityContextKey;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {\n\t\tthis.securityContextHolderStrategy = strategy;\n\t\tthis.contextObject = this.securityContextHolderStrategy.createEmptyContext();\n\t}\n\n\tprivate boolean isTransient(@Nullable Object object) {\n\t\tif (object == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn AnnotationUtils.getAnnotation(object.getClass(), Transient.class) != null;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. The default is\n\t * {@link AuthenticationTrustResolverImpl}.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be\n\t * null.\n\t */\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\tthis.trustResolver = trustResolver;\n\t}\n\n\tprivate static class SaveToSessionRequestWrapper extends HttpServletRequestWrapper {\n\n\t\tprivate final SaveContextOnUpdateOrErrorResponseWrapper response;\n\n\t\tSaveToSessionRequestWrapper(HttpServletRequest request, SaveContextOnUpdateOrErrorResponseWrapper response) {\n\t\t\tsuper(request);\n\t\t\tthis.response = response;\n\t\t}\n\n\t\t@Override\n\t\tpublic AsyncContext startAsync() {\n\t\t\tthis.response.disableSaveOnResponseCommitted();\n\t\t\treturn super.startAsync();\n\t\t}\n\n\t\t@Override\n\t\tpublic AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)\n\t\t\t\tthrows IllegalStateException {\n\t\t\tthis.response.disableSaveOnResponseCommitted();\n\t\t\treturn super.startAsync(servletRequest, servletResponse);\n\t\t}\n\n\t}\n\n\t/**\n\t * Wrapper that is applied to every request/response to update the\n\t * <code>HttpSession</code> with the <code>SecurityContext</code> when a\n\t * <code>sendError()</code> or <code>sendRedirect</code> happens. See SEC-398.\n\t * <p>\n\t * Stores the necessary state from the start of the request in order to make a\n\t * decision about whether the security context has changed before saving it.\n\t */\n\tfinal class SaveToSessionResponseWrapper extends SaveContextOnUpdateOrErrorResponseWrapper {\n\n\t\tprivate final Log logger = HttpSessionSecurityContextRepository.this.logger;\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tprivate final boolean httpSessionExistedAtStartOfRequest;\n\n\t\tprivate final SecurityContext contextBeforeExecution;\n\n\t\tprivate final @Nullable Authentication authBeforeExecution;\n\n\t\tprivate boolean isSaveContextInvoked;\n\n\t\t/**\n\t\t * Takes the parameters required to call <code>saveContext()</code> successfully\n\t\t * in addition to the request and the response object we are wrapping.\n\t\t * @param request the request object (used to obtain the session, if one exists).\n\t\t * @param httpSessionExistedAtStartOfRequest indicates whether there was a session\n\t\t * in place before the filter chain executed. If this is true, and the session is\n\t\t * found to be null, this indicates that it was invalidated during the request and\n\t\t * a new session will now be created.\n\t\t * @param context the context before the filter chain executed. The context will\n\t\t * only be stored if it or its contents changed during the request.\n\t\t */\n\t\tSaveToSessionResponseWrapper(HttpServletResponse response, HttpServletRequest request,\n\t\t\t\tboolean httpSessionExistedAtStartOfRequest, SecurityContext context) {\n\t\t\tsuper(response, HttpSessionSecurityContextRepository.this.disableUrlRewriting);\n\t\t\tthis.request = request;\n\t\t\tthis.httpSessionExistedAtStartOfRequest = httpSessionExistedAtStartOfRequest;\n\t\t\tthis.contextBeforeExecution = context;\n\t\t\tthis.authBeforeExecution = context.getAuthentication();\n\t\t}\n\n\t\t/**\n\t\t * Stores the supplied security context in the session (if available) and if it\n\t\t * has changed since it was set at the start of the request. If the\n\t\t * AuthenticationTrustResolver identifies the current user as anonymous, then the\n\t\t * context will not be stored.\n\t\t * @param context the context object obtained from the SecurityContextHolder after\n\t\t * the request has been processed by the filter chain.\n\t\t * SecurityContextHolder.getContext() cannot be used to obtain the context as it\n\t\t * has already been cleared by the time this method is called.\n\t\t *\n\t\t */\n\t\t@Override\n\t\tprotected void saveContext(SecurityContext context) {\n\t\t\tif (isTransient(context)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfinal Authentication authentication = context.getAuthentication();\n\t\t\tif (isTransient(authentication)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tHttpSession httpSession = this.request.getSession(false);\n\t\t\tString springSecurityContextKey = HttpSessionSecurityContextRepository.this.springSecurityContextKey;\n\t\t\t// See SEC-776\n\t\t\tif (authentication == null\n\t\t\t\t\t|| HttpSessionSecurityContextRepository.this.trustResolver.isAnonymous(authentication)) {\n\t\t\t\tif (httpSession != null && this.authBeforeExecution != null) {\n\t\t\t\t\t// SEC-1587 A non-anonymous context may still be in the session\n\t\t\t\t\t// SEC-1735 remove if the contextBeforeExecution was not anonymous\n\t\t\t\t\thttpSession.removeAttribute(springSecurityContextKey);\n\t\t\t\t\tthis.isSaveContextInvoked = true;\n\t\t\t\t}\n\t\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\t\tif (authentication == null) {\n\t\t\t\t\t\tthis.logger.debug(\"Did not store empty SecurityContext\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.logger.debug(\"Did not store anonymous SecurityContext\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thttpSession = (httpSession != null) ? httpSession : createNewSessionIfAllowed(context);\n\t\t\t// If HttpSession exists, store current SecurityContext but only if it has\n\t\t\t// actually changed in this thread (see SEC-37, SEC-1307, SEC-1528)\n\t\t\tif (httpSession != null) {\n\t\t\t\t// We may have a new session, so check also whether the context attribute\n\t\t\t\t// is set SEC-1561\n\t\t\t\tif (contextChanged(context) || httpSession.getAttribute(springSecurityContextKey) == null) {\n\t\t\t\t\tHttpSessionSecurityContextRepository.this.saveContextInHttpSession(context, this.request);\n\t\t\t\t\tthis.isSaveContextInvoked = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean contextChanged(SecurityContext context) {\n\t\t\treturn this.isSaveContextInvoked || context != this.contextBeforeExecution\n\t\t\t\t\t|| context.getAuthentication() != this.authBeforeExecution;\n\t\t}\n\n\t\tprivate @Nullable HttpSession createNewSessionIfAllowed(SecurityContext context) {\n\t\t\tif (this.httpSessionExistedAtStartOfRequest) {\n\t\t\t\tthis.logger.debug(\"HttpSession is now null, but was not null at start of request; \"\n\t\t\t\t\t\t+ \"session was invalidated, so do not create a new session\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (!HttpSessionSecurityContextRepository.this.allowSessionCreation) {\n\t\t\t\tthis.logger.debug(\"The HttpSession is currently null, and the \"\n\t\t\t\t\t\t+ HttpSessionSecurityContextRepository.class.getSimpleName()\n\t\t\t\t\t\t+ \" is prohibited from creating an HttpSession \"\n\t\t\t\t\t\t+ \"(because the allowSessionCreation property is false) - SecurityContext thus not \"\n\t\t\t\t\t\t+ \"stored for next request\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// Generate a HttpSession only if we need to\n\t\t\tif (HttpSessionSecurityContextRepository.this.contextObject.equals(context)) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\n\t\t\t\t\t\t\"HttpSession is null, but SecurityContext has not changed from \"\n\t\t\t\t\t\t\t\t+ \"default empty context %s so not creating HttpSession or storing SecurityContext\",\n\t\t\t\t\t\tcontext));\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tHttpSession session = this.request.getSession(true);\n\t\t\t\tthis.logger.debug(\"Created HttpSession as SecurityContext is non-default\");\n\t\t\t\treturn session;\n\t\t\t}\n\t\t\tcatch (IllegalStateException ex) {\n\t\t\t\t// Response must already be committed, therefore can't create a new\n\t\t\t\t// session\n\t\t\t\tthis.logger.warn(\"Failed to create a session, as response has been committed. \"\n\t\t\t\t\t\t+ \"Unable to store SecurityContext.\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/NullSecurityContextRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * @author Luke Taylor\n * @since 3.1\n */\npublic final class NullSecurityContextRepository implements SecurityContextRepository {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t@Override\n\tpublic boolean containsContext(HttpServletRequest request) {\n\t\treturn false;\n\t}\n\n\t/**\n\t * @deprecated please see {@link SecurityContextRepository#loadContext}\n\t */\n\t@Override\n\t@Deprecated\n\tpublic SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {\n\t\treturn this.securityContextHolderStrategy.createEmptyContext();\n\t}\n\n\t@Override\n\tpublic void saveContext(SecurityContext context, HttpServletRequest request,\n\t\t\t@Nullable HttpServletResponse response) {\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/RequestAttributeSecurityContextRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.DeferredSecurityContext;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * Stores the {@link SecurityContext} on a\n * {@link jakarta.servlet.ServletRequest#setAttribute(String, Object)} so that it can be\n * restored when different dispatch types occur. It will not be available on subsequent\n * requests.\n *\n * Unlike {@link HttpSessionSecurityContextRepository} this filter has no need to persist\n * the {@link SecurityContext} on the response being committed because the\n * {@link SecurityContext} will not be available for subsequent requests for\n * {@link RequestAttributeSecurityContextRepository}.\n *\n * @author Rob Winch\n * @since 5.7\n */\npublic final class RequestAttributeSecurityContextRepository implements SecurityContextRepository {\n\n\t/**\n\t * The default request attribute name to use.\n\t */\n\tpublic static final String DEFAULT_REQUEST_ATTR_NAME = RequestAttributeSecurityContextRepository.class.getName()\n\t\t.concat(\".SPRING_SECURITY_CONTEXT\");\n\n\tprivate final String requestAttributeName;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t/**\n\t * Creates a new instance using {@link #DEFAULT_REQUEST_ATTR_NAME}.\n\t */\n\tpublic RequestAttributeSecurityContextRepository() {\n\t\tthis(DEFAULT_REQUEST_ATTR_NAME);\n\t}\n\n\t/**\n\t * Creates a new instance with the specified request attribute name.\n\t * @param requestAttributeName the request attribute name to set to the\n\t * {@link SecurityContext}.\n\t */\n\tpublic RequestAttributeSecurityContextRepository(String requestAttributeName) {\n\t\tthis.requestAttributeName = requestAttributeName;\n\t}\n\n\t@Override\n\tpublic boolean containsContext(HttpServletRequest request) {\n\t\treturn getContext(request) != null;\n\t}\n\n\t/**\n\t * @deprecated please see {@link SecurityContextRepository#loadContext}\n\t */\n\t@Override\n\t@Deprecated\n\tpublic SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {\n\t\treturn loadDeferredContext(requestResponseHolder.getRequest()).get();\n\t}\n\n\t@Override\n\tpublic DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {\n\t\tSupplier<SecurityContext> supplier = () -> getContext(request);\n\t\treturn new SupplierDeferredSecurityContext(supplier, this.securityContextHolderStrategy);\n\t}\n\n\tprivate SecurityContext getContext(HttpServletRequest request) {\n\t\treturn (SecurityContext) request.getAttribute(this.requestAttributeName);\n\t}\n\n\t@Override\n\tpublic void saveContext(SecurityContext context, HttpServletRequest request,\n\t\t\t@Nullable HttpServletResponse response) {\n\t\trequest.setAttribute(this.requestAttributeName, context);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/SaveContextOnUpdateOrErrorResponseWrapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.util.OnCommittedResponseWrapper;\nimport org.springframework.util.Assert;\n\n/**\n * Base class for response wrappers which encapsulate the logic for storing a security\n * context and which store the <code>SecurityContext</code> when a\n * <code>sendError()</code>, <code>sendRedirect</code>,\n * <code>getOutputStream().close()</code>, <code>getOutputStream().flush()</code>,\n * <code>getWriter().close()</code>, or <code>getWriter().flush()</code> happens on the\n * same thread that this {@link SaveContextOnUpdateOrErrorResponseWrapper} was created.\n * See issue SEC-398 and SEC-2005.\n * <p>\n * Sub-classes should implement the {@link #saveContext(SecurityContext context)} method.\n * <p>\n * Support is also provided for disabling URL rewriting\n *\n * @author Luke Taylor\n * @author Marten Algesten\n * @author Rob Winch\n * @since 3.0\n * @deprecated Use\n * {@link SecurityContextRepository#loadDeferredContext(HttpServletRequest)} instead.\n */\n@Deprecated\npublic abstract class SaveContextOnUpdateOrErrorResponseWrapper extends OnCommittedResponseWrapper {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate boolean contextSaved = false;\n\n\t// See SEC-1052\n\tprivate final boolean disableUrlRewriting;\n\n\t/**\n\t * @param response the response to be wrapped\n\t * @param disableUrlRewriting turns the URL encoding methods into null operations,\n\t * preventing the use of URL rewriting to add the session identifier as a URL\n\t * parameter.\n\t */\n\tpublic SaveContextOnUpdateOrErrorResponseWrapper(HttpServletResponse response, boolean disableUrlRewriting) {\n\t\tsuper(response);\n\t\tthis.disableUrlRewriting = disableUrlRewriting;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Invoke this method to disable automatic saving of the {@link SecurityContext} when\n\t * the {@link HttpServletResponse} is committed. This can be useful in the event that\n\t * Async Web Requests are made which may no longer contain the {@link SecurityContext}\n\t * on it.\n\t */\n\tpublic void disableSaveOnResponseCommitted() {\n\t\tdisableOnResponseCommitted();\n\t}\n\n\t/**\n\t * Implements the logic for storing the security context.\n\t * @param context the <tt>SecurityContext</tt> instance to store\n\t */\n\tprotected abstract void saveContext(SecurityContext context);\n\n\t/**\n\t * Calls <code>saveContext()</code> with the current contents of the\n\t * <tt>SecurityContextHolder</tt> as long as {@link #disableSaveOnResponseCommitted()\n\t * ()} was not invoked.\n\t */\n\t@Override\n\tprotected void onResponseCommitted() {\n\t\tsaveContext(this.securityContextHolderStrategy.getContext());\n\t\tthis.contextSaved = true;\n\t}\n\n\t@Override\n\tpublic final String encodeRedirectURL(String url) {\n\t\tif (this.disableUrlRewriting) {\n\t\t\treturn url;\n\t\t}\n\t\treturn super.encodeRedirectURL(url);\n\t}\n\n\t@Override\n\tpublic final String encodeURL(String url) {\n\t\tif (this.disableUrlRewriting) {\n\t\t\treturn url;\n\t\t}\n\t\treturn super.encodeURL(url);\n\t}\n\n\t/**\n\t * Tells if the response wrapper has called <code>saveContext()</code> because of this\n\t * wrapper.\n\t */\n\tpublic final boolean isContextSaved() {\n\t\treturn this.contextSaved;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/SecurityContextHolderFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.io.IOException;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * A {@link jakarta.servlet.Filter} that uses the {@link SecurityContextRepository} to\n * obtain the {@link SecurityContext} and set it on the {@link SecurityContextHolder}.\n * This is similar to {@link SecurityContextPersistenceFilter} except that the\n * {@link SecurityContextRepository#saveContext(SecurityContext, HttpServletRequest, HttpServletResponse)}\n * must be explicitly invoked to save the {@link SecurityContext}. This improves the\n * efficiency and provides better flexibility by allowing different authentication\n * mechanisms to choose individually if authentication should be persisted.\n *\n * @author Rob Winch\n * @author Marcus da Coregio\n * @since 5.7\n */\npublic class SecurityContextHolderFilter extends GenericFilterBean {\n\n\tprivate static final String FILTER_APPLIED = SecurityContextHolderFilter.class.getName() + \".APPLIED\";\n\n\tprivate final SecurityContextRepository securityContextRepository;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t/**\n\t * Creates a new instance.\n\t * @param securityContextRepository the repository to use. Cannot be null.\n\t */\n\tpublic SecurityContextHolderFilter(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (request.getAttribute(FILTER_APPLIED) != null) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\trequest.setAttribute(FILTER_APPLIED, Boolean.TRUE);\n\t\tSupplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);\n\t\ttry {\n\t\t\tthis.securityContextHolderStrategy.setDeferredContext(deferredContext);\n\t\t\tchain.doFilter(request, response);\n\t\t}\n\t\tfinally {\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\trequest.removeAttribute(FILTER_APPLIED);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/SecurityContextPersistenceFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Populates the {@link SecurityContextHolder} with information obtained from the\n * configured {@link SecurityContextRepository} prior to the request and stores it back in\n * the repository once the request has completed and clearing the context holder. By\n * default it uses an {@link HttpSessionSecurityContextRepository}. See this class for\n * information <tt>HttpSession</tt> related configuration options.\n * <p>\n * This filter will only execute once per request, to resolve servlet container\n * (specifically Weblogic) incompatibilities.\n * <p>\n * This filter MUST be executed BEFORE any authentication processing mechanisms.\n * Authentication processing mechanisms (e.g. BASIC, CAS processing filters etc) expect\n * the <code>SecurityContextHolder</code> to contain a valid <code>SecurityContext</code>\n * by the time they execute.\n * <p>\n * This is essentially a refactoring of the old\n * <tt>HttpSessionContextIntegrationFilter</tt> to delegate the storage issues to a\n * separate strategy, allowing for more customization in the way the security context is\n * maintained between requests.\n * <p>\n * The <tt>forceEagerSessionCreation</tt> property can be used to ensure that a session is\n * always available before the filter chain executes (the default is <code>false</code>,\n * as this is resource intensive and not recommended).\n *\n * @author Luke Taylor\n * @since 3.0\n * @deprecated Use {@link SecurityContextHolderFilter}\n */\n@Deprecated\npublic class SecurityContextPersistenceFilter extends GenericFilterBean {\n\n\tstatic final String FILTER_APPLIED = \"__spring_security_scpf_applied\";\n\n\tprivate SecurityContextRepository repo;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate boolean forceEagerSessionCreation = false;\n\n\tpublic SecurityContextPersistenceFilter() {\n\t\tthis(new HttpSessionSecurityContextRepository());\n\t}\n\n\tpublic SecurityContextPersistenceFilter(SecurityContextRepository repo) {\n\t\tthis.repo = repo;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\t// ensure that filter is only applied once per request\n\t\tif (request.getAttribute(FILTER_APPLIED) != null) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\trequest.setAttribute(FILTER_APPLIED, Boolean.TRUE);\n\t\tif (this.forceEagerSessionCreation) {\n\t\t\tHttpSession session = request.getSession();\n\t\t\tif (this.logger.isDebugEnabled() && session.isNew()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Created session %s eagerly\", session.getId()));\n\t\t\t}\n\t\t}\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);\n\t\ttry {\n\t\t\tthis.securityContextHolderStrategy.setContext(contextBeforeChainExecution);\n\t\t\tif (contextBeforeChainExecution.getAuthentication() == null) {\n\t\t\t\tlogger.debug(\"Set SecurityContextHolder to empty SecurityContext\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\t\tthis.logger\n\t\t\t\t\t\t.debug(LogMessage.format(\"Set SecurityContextHolder to %s\", contextBeforeChainExecution));\n\t\t\t\t}\n\t\t\t}\n\t\t\tchain.doFilter(holder.getRequest(), holder.getResponse());\n\t\t}\n\t\tfinally {\n\t\t\tSecurityContext contextAfterChainExecution = this.securityContextHolderStrategy.getContext();\n\t\t\t// Crucial removal of SecurityContextHolder contents before anything else.\n\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\tthis.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());\n\t\t\trequest.removeAttribute(FILTER_APPLIED);\n\t\t\tthis.logger.debug(\"Cleared SecurityContextHolder to complete request\");\n\t\t}\n\t}\n\n\tpublic void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {\n\t\tthis.forceEagerSessionCreation = forceEagerSessionCreation;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/SecurityContextRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.context.DeferredSecurityContext;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.util.function.SingletonSupplier;\n\n/**\n * Strategy used for persisting a {@link SecurityContext} between requests.\n * <p>\n * Used by {@link SecurityContextPersistenceFilter} to obtain the context which should be\n * used for the current thread of execution and to store the context once it has been\n * removed from thread-local storage and the request has completed.\n * <p>\n * The persistence mechanism used will depend on the implementation, but most commonly the\n * <tt>HttpSession</tt> will be used to store the context.\n *\n * @author Luke Taylor\n * @since 3.0\n * @see SecurityContextPersistenceFilter\n * @see HttpSessionSecurityContextRepository\n * @see SaveContextOnUpdateOrErrorResponseWrapper\n */\npublic interface SecurityContextRepository {\n\n\t/**\n\t * Obtains the security context for the supplied request. For an unauthenticated user,\n\t * an empty context implementation should be returned. This method should not return\n\t * null.\n\t * <p>\n\t * The use of the <tt>HttpRequestResponseHolder</tt> parameter allows implementations\n\t * to return wrapped versions of the request or response (or both), allowing them to\n\t * access implementation-specific state for the request. The values obtained from the\n\t * holder will be passed on to the filter chain and also to the <tt>saveContext</tt>\n\t * method when it is finally called to allow implicit saves of the\n\t * <tt>SecurityContext</tt>. Implementations may wish to return a subclass of\n\t * {@link SaveContextOnUpdateOrErrorResponseWrapper} as the response object, which\n\t * guarantees that the context is persisted when an error or redirect occurs.\n\t * Implementations may allow passing in the original request response to allow\n\t * explicit saves.\n\t * @param requestResponseHolder holder for the current request and response for which\n\t * the context should be loaded.\n\t * @return The security context which should be used for the current request, never\n\t * null.\n\t * @deprecated Use {@link #loadDeferredContext(HttpServletRequest)} instead.\n\t */\n\t@Deprecated\n\tSecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);\n\n\t/**\n\t * Defers loading the {@link SecurityContext} using the {@link HttpServletRequest}\n\t * until it is needed by the application.\n\t * @param request the {@link HttpServletRequest} to load the {@link SecurityContext}\n\t * from\n\t * @return a {@link DeferredSecurityContext} that returns the {@link SecurityContext}\n\t * which cannot be null\n\t * @since 5.8\n\t */\n\tdefault DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {\n\t\tSupplier<SecurityContext> supplier = () -> {\n\t\t\t@SuppressWarnings(\"NullAway\") // fixed when remove deprecated method\n\t\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, null);\n\t\t\treturn loadContext(holder);\n\t\t};\n\t\treturn new SupplierDeferredSecurityContext(SingletonSupplier.of(supplier),\n\t\t\t\tSecurityContextHolder.getContextHolderStrategy());\n\t}\n\n\t/**\n\t * Stores the security context on completion of a request.\n\t * @param context the non-null context which was obtained from the holder.\n\t * @param request\n\t * @param response\n\t */\n\tvoid saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Allows the repository to be queried as to whether it contains a security context\n\t * for the current request.\n\t * @param request the current request\n\t * @return true if a context is found for the request, false otherwise\n\t */\n\tboolean containsContext(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/SupplierDeferredSecurityContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.function.Supplier;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.context.DeferredSecurityContext;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\n/**\n * @author Steve Riesenberg\n * @since 5.8\n */\nfinal class SupplierDeferredSecurityContext implements DeferredSecurityContext {\n\n\tprivate static final Log logger = LogFactory.getLog(SupplierDeferredSecurityContext.class);\n\n\tprivate final Supplier<SecurityContext> supplier;\n\n\tprivate final SecurityContextHolderStrategy strategy;\n\n\tprivate @Nullable SecurityContext securityContext;\n\n\tprivate boolean missingContext;\n\n\tSupplierDeferredSecurityContext(Supplier<SecurityContext> supplier, SecurityContextHolderStrategy strategy) {\n\t\tthis.supplier = supplier;\n\t\tthis.strategy = strategy;\n\t}\n\n\t@Override\n\tpublic @Nullable SecurityContext get() {\n\t\tinit();\n\t\treturn this.securityContext;\n\t}\n\n\t@Override\n\tpublic boolean isGenerated() {\n\t\tinit();\n\t\treturn this.missingContext;\n\t}\n\n\tprivate void init() {\n\t\tif (this.securityContext != null) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.securityContext = this.supplier.get();\n\t\tthis.missingContext = (this.securityContext == null);\n\t\tif (this.missingContext) {\n\t\t\tthis.securityContext = this.strategy.createEmptyContext();\n\t\t\tif (logger.isTraceEnabled()) {\n\t\t\t\tlogger.trace(LogMessage.format(\"Created %s\", this.securityContext));\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes which are responsible for maintaining the security context between HTTP\n * requests.\n */\n@NullMarked\npackage org.springframework.security.web.context;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/request/async/SecurityContextCallableProcessingInterceptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context.request.async;\n\nimport java.util.concurrent.Callable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.context.request.async.CallableProcessingInterceptor;\n\n/**\n * <p>\n * Allows for integration with Spring MVC's {@link Callable} support.\n * </p>\n * <p>\n * A {@link CallableProcessingInterceptor} that establishes the injected\n * {@link SecurityContext} on the {@link SecurityContextHolder} when\n * {@link #preProcess(NativeWebRequest, Callable)} is invoked. It also clear out the\n * {@link SecurityContextHolder} by invoking {@link SecurityContextHolder#clearContext()}\n * in the {@link #postProcess(NativeWebRequest, Callable, Object)} method.\n * </p>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class SecurityContextCallableProcessingInterceptor implements CallableProcessingInterceptor {\n\n\tprivate @Nullable volatile SecurityContext securityContext;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t/**\n\t * Create a new {@link SecurityContextCallableProcessingInterceptor} that uses the\n\t * {@link SecurityContext} from the {@link SecurityContextHolder} at the time\n\t * {@link #beforeConcurrentHandling(NativeWebRequest, Callable)} is invoked.\n\t */\n\tpublic SecurityContextCallableProcessingInterceptor() {\n\t}\n\n\t/**\n\t * Creates a new {@link SecurityContextCallableProcessingInterceptor} with the\n\t * specified {@link SecurityContext}.\n\t * @param securityContext the {@link SecurityContext} to set on the\n\t * {@link SecurityContextHolder} in {@link #preProcess(NativeWebRequest, Callable)}.\n\t * Cannot be null.\n\t * @throws IllegalArgumentException if {@link SecurityContext} is null.\n\t */\n\tpublic SecurityContextCallableProcessingInterceptor(SecurityContext securityContext) {\n\t\tAssert.notNull(securityContext, \"securityContext cannot be null\");\n\t\tsetSecurityContext(securityContext);\n\t}\n\n\t@Override\n\tpublic <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) {\n\t\tif (this.securityContext == null) {\n\t\t\tsetSecurityContext(this.securityContextHolderStrategy.getContext());\n\t\t}\n\t}\n\n\t@Override\n\tpublic <T> void preProcess(NativeWebRequest request, Callable<T> task) {\n\t\tif (this.securityContext != null) {\n\t\t\tthis.securityContextHolderStrategy.setContext(this.securityContext);\n\t\t}\n\t}\n\n\t@Override\n\tpublic <T> void postProcess(NativeWebRequest request, Callable<T> task, @Nullable Object concurrentResult) {\n\t\tthis.securityContextHolderStrategy.clearContext();\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tprivate void setSecurityContext(SecurityContext securityContext) {\n\t\tthis.securityContext = securityContext;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/request/async/WebAsyncManagerIntegrationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context.request.async;\n\nimport java.io.IOException;\nimport java.util.concurrent.Callable;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.request.async.WebAsyncManager;\nimport org.springframework.web.context.request.async.WebAsyncUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Provides integration between the {@link SecurityContext} and Spring Web's\n * {@link WebAsyncManager} by using the\n * {@link SecurityContextCallableProcessingInterceptor#beforeConcurrentHandling(org.springframework.web.context.request.NativeWebRequest, Callable)}\n * to populate the {@link SecurityContext} on the {@link Callable}.\n *\n * @author Rob Winch\n * @see SecurityContextCallableProcessingInterceptor\n */\npublic final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter {\n\n\tprivate static final Object CALLABLE_INTERCEPTOR_KEY = new Object();\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tWebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);\n\t\tSecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager\n\t\t\t.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);\n\t\tif (securityProcessingInterceptor == null) {\n\t\t\tSecurityContextCallableProcessingInterceptor interceptor = new SecurityContextCallableProcessingInterceptor();\n\t\t\tinterceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\t\tasyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, interceptor);\n\t\t}\n\t\tfilterChain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/request/async/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Async request context APIs.\n */\n@NullMarked\npackage org.springframework.security.web.context.request.async;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/support/SecurityWebApplicationContextUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context.support;\n\nimport jakarta.servlet.ServletContext;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.WebApplicationContextUtils;\n\n/**\n * Spring Security extension to Spring's {@link WebApplicationContextUtils}.\n *\n * @author Rob Winch\n */\npublic abstract class SecurityWebApplicationContextUtils extends WebApplicationContextUtils {\n\n\t/**\n\t * Find a unique {@code WebApplicationContext} for this web app: either the root web\n\t * app context (preferred) or a unique {@code WebApplicationContext} among the\n\t * registered {@code ServletContext} attributes (typically coming from a single\n\t * {@code DispatcherServlet} in the current web application).\n\t * <p>\n\t * Note that {@code DispatcherServlet}'s exposure of its context can be controlled\n\t * through its {@code publishContext} property, which is {@code true} by default but\n\t * can be selectively switched to only publish a single context despite multiple\n\t * {@code DispatcherServlet} registrations in the web app.\n\t * @param servletContext ServletContext to find the web application context for\n\t * @return the desired WebApplicationContext for this web app\n\t * @throws IllegalStateException if no WebApplicationContext can be found\n\t * @see #getWebApplicationContext(ServletContext)\n\t * @see ServletContext#getAttributeNames()\n\t */\n\tpublic static WebApplicationContext findRequiredWebApplicationContext(ServletContext servletContext) {\n\t\tWebApplicationContext webApplicationContext = findWebApplicationContext(servletContext);\n\t\tAssert.state(webApplicationContext != null,\n\t\t\t\t\"No WebApplicationContext found: no ContextLoaderListener registered?\");\n\t\treturn webApplicationContext;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/context/support/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Async support for request context.\n */\n@NullMarked\npackage org.springframework.security.web.context.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.WebUtils;\n\n/**\n * A {@link CsrfTokenRepository} that persists the CSRF token in a cookie named\n * \"XSRF-TOKEN\" and reads from the header \"X-XSRF-TOKEN\" following the conventions of\n * AngularJS. When using with AngularJS be sure to use {@link #withHttpOnlyFalse()}.\n *\n * @author Rob Winch\n * @author Steve Riesenberg\n * @author Alex Montoya\n * @since 4.1\n */\npublic final class CookieCsrfTokenRepository implements CsrfTokenRepository {\n\n\tstatic final String DEFAULT_CSRF_COOKIE_NAME = \"XSRF-TOKEN\";\n\n\tstatic final String DEFAULT_CSRF_PARAMETER_NAME = \"_csrf\";\n\n\tstatic final String DEFAULT_CSRF_HEADER_NAME = \"X-XSRF-TOKEN\";\n\n\tprivate static final String CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME = CookieCsrfTokenRepository.class.getName()\n\t\t.concat(\".REMOVED\");\n\n\tprivate String parameterName = DEFAULT_CSRF_PARAMETER_NAME;\n\n\tprivate String headerName = DEFAULT_CSRF_HEADER_NAME;\n\n\tprivate String cookieName = DEFAULT_CSRF_COOKIE_NAME;\n\n\tprivate boolean cookieHttpOnly = true;\n\n\tprivate @Nullable String cookiePath;\n\n\tprivate @Nullable String cookieDomain;\n\n\tprivate @Nullable Boolean secure;\n\n\tprivate int cookieMaxAge = -1;\n\n\tprivate Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer = (builder) -> {\n\t};\n\n\t/**\n\t * Add a {@link Consumer} for a {@code ResponseCookieBuilder} that will be invoked for\n\t * each cookie being built, just before the call to {@code build()}.\n\t * @param cookieCustomizer consumer for a cookie builder\n\t * @since 6.1\n\t */\n\tpublic void setCookieCustomizer(Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer) {\n\t\tAssert.notNull(cookieCustomizer, \"cookieCustomizer must not be null\");\n\t\tthis.cookieCustomizer = cookieCustomizer;\n\t}\n\n\t@Override\n\tpublic CsrfToken generateToken(HttpServletRequest request) {\n\t\treturn new DefaultCsrfToken(this.headerName, this.parameterName, createNewToken());\n\t}\n\n\t@Override\n\tpublic void saveToken(@Nullable CsrfToken token, HttpServletRequest request, HttpServletResponse response) {\n\t\tString tokenValue = (token != null) ? token.getToken() : \"\";\n\n\t\tResponseCookie.ResponseCookieBuilder cookieBuilder = ResponseCookie.from(this.cookieName, tokenValue)\n\t\t\t.secure((this.secure != null) ? this.secure : request.isSecure())\n\t\t\t.path(StringUtils.hasLength(this.cookiePath) ? this.cookiePath : this.getRequestContext(request))\n\t\t\t.maxAge((token != null) ? this.cookieMaxAge : 0)\n\t\t\t.httpOnly(this.cookieHttpOnly)\n\t\t\t.domain(this.cookieDomain);\n\n\t\tthis.cookieCustomizer.accept(cookieBuilder);\n\n\t\tResponseCookie responseCookie = cookieBuilder.build();\n\t\tif (!StringUtils.hasLength(responseCookie.getSameSite())) {\n\t\t\tCookie cookie = mapToCookie(responseCookie);\n\t\t\tresponse.addCookie(cookie);\n\t\t}\n\t\telse if (request.getServletContext().getMajorVersion() > 5) {\n\t\t\tCookie cookie = mapToCookie(responseCookie);\n\t\t\tresponse.addCookie(cookie);\n\t\t}\n\t\telse {\n\t\t\tresponse.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());\n\t\t}\n\n\t\t// Set request attribute to signal that response has blank cookie value,\n\t\t// which allows loadToken to return null when token has been removed\n\t\tif (!StringUtils.hasLength(tokenValue)) {\n\t\t\trequest.setAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME, Boolean.TRUE);\n\t\t}\n\t\telse {\n\t\t\trequest.removeAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME);\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable CsrfToken loadToken(HttpServletRequest request) {\n\t\t// Return null when token has been removed during the current request\n\t\t// which allows loadDeferredToken to re-generate the token\n\t\tif (Boolean.TRUE.equals(request.getAttribute(CSRF_TOKEN_REMOVED_ATTRIBUTE_NAME))) {\n\t\t\treturn null;\n\t\t}\n\t\tCookie cookie = WebUtils.getCookie(request, this.cookieName);\n\t\tif (cookie == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString token = cookie.getValue();\n\t\tif (!StringUtils.hasLength(token)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new DefaultCsrfToken(this.headerName, this.parameterName, token);\n\t}\n\n\t/**\n\t * Sets the name of the HTTP request parameter that should be used to provide a token.\n\t * @param parameterName the name of the HTTP request parameter that should be used to\n\t * provide a token\n\t */\n\tpublic void setParameterName(String parameterName) {\n\t\tAssert.notNull(parameterName, \"parameterName cannot be null\");\n\t\tthis.parameterName = parameterName;\n\t}\n\n\t/**\n\t * Sets the name of the HTTP header that should be used to provide the token.\n\t * @param headerName the name of the HTTP header that should be used to provide the\n\t * token\n\t */\n\tpublic void setHeaderName(String headerName) {\n\t\tAssert.notNull(headerName, \"headerName cannot be null\");\n\t\tthis.headerName = headerName;\n\t}\n\n\t/**\n\t * Sets the name of the cookie that the expected CSRF token is saved to and read from.\n\t * @param cookieName the name of the cookie that the expected CSRF token is saved to\n\t * and read from\n\t */\n\tpublic void setCookieName(String cookieName) {\n\t\tAssert.notNull(cookieName, \"cookieName cannot be null\");\n\t\tthis.cookieName = cookieName;\n\t}\n\n\tprivate String getRequestContext(HttpServletRequest request) {\n\t\tString contextPath = request.getContextPath();\n\t\treturn (contextPath.length() > 0) ? contextPath : \"/\";\n\t}\n\n\t/**\n\t * Factory method to conveniently create an instance that creates cookies where\n\t * {@link Cookie#isHttpOnly()} is set to false.\n\t * @return an instance of CookieCsrfTokenRepository that creates cookies where\n\t * {@link Cookie#isHttpOnly()} is set to false.\n\t */\n\tpublic static CookieCsrfTokenRepository withHttpOnlyFalse() {\n\t\tCookieCsrfTokenRepository result = new CookieCsrfTokenRepository();\n\t\tresult.cookieHttpOnly = false;\n\t\treturn result;\n\t}\n\n\tprivate String createNewToken() {\n\t\treturn UUID.randomUUID().toString();\n\t}\n\n\tprivate Cookie mapToCookie(ResponseCookie responseCookie) {\n\t\tCookie cookie = new Cookie(responseCookie.getName(), responseCookie.getValue());\n\t\tcookie.setSecure(responseCookie.isSecure());\n\t\tcookie.setPath(responseCookie.getPath());\n\t\tcookie.setMaxAge((int) responseCookie.getMaxAge().getSeconds());\n\t\tcookie.setHttpOnly(responseCookie.isHttpOnly());\n\t\tif (StringUtils.hasLength(responseCookie.getDomain())) {\n\t\t\tcookie.setDomain(responseCookie.getDomain());\n\t\t}\n\t\tif (StringUtils.hasText(responseCookie.getSameSite())) {\n\t\t\tcookie.setAttribute(\"SameSite\", responseCookie.getSameSite());\n\t\t}\n\t\treturn cookie;\n\t}\n\n\t/**\n\t * Set the path that the Cookie will be created with. This will override the default\n\t * functionality which uses the request context as the path.\n\t * @param path the path to use\n\t */\n\tpublic void setCookiePath(String path) {\n\t\tthis.cookiePath = path;\n\t}\n\n\t/**\n\t * Get the path that the CSRF cookie will be set to.\n\t * @return the path to be used.\n\t */\n\tpublic @Nullable String getCookiePath() {\n\t\treturn this.cookiePath;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfAuthenticationStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.util.Assert;\n\n/**\n * {@link CsrfAuthenticationStrategy} is in charge of removing the {@link CsrfToken} upon\n * authenticating. A new {@link CsrfToken} will then be generated by the framework upon\n * the next request.\n *\n * @author Rob Winch\n * @author Steve Riesenberg\n * @since 3.2\n */\npublic final class CsrfAuthenticationStrategy implements SessionAuthenticationStrategy {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final CsrfTokenRepository tokenRepository;\n\n\tprivate CsrfTokenRequestHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();\n\n\t/**\n\t * Creates a new instance\n\t * @param tokenRepository the {@link CsrfTokenRepository} to use\n\t */\n\tpublic CsrfAuthenticationStrategy(CsrfTokenRepository tokenRepository) {\n\t\tAssert.notNull(tokenRepository, \"tokenRepository cannot be null\");\n\t\tthis.tokenRepository = tokenRepository;\n\t}\n\n\t/**\n\t * Specify a {@link CsrfTokenRequestHandler} to use for making the {@code CsrfToken}\n\t * available as a request attribute.\n\t * @param requestHandler the {@link CsrfTokenRequestHandler} to use\n\t */\n\tpublic void setRequestHandler(CsrfTokenRequestHandler requestHandler) {\n\t\tAssert.notNull(requestHandler, \"requestHandler cannot be null\");\n\t\tthis.requestHandler = requestHandler;\n\t}\n\n\t@Override\n\tpublic void onAuthentication(@Nullable Authentication authentication, HttpServletRequest request,\n\t\t\tHttpServletResponse response) throws SessionAuthenticationException {\n\t\tboolean containsToken = this.tokenRepository.loadToken(request) != null;\n\t\tif (containsToken) {\n\t\t\tthis.tokenRepository.saveToken(null, request, response);\n\t\t\tDeferredCsrfToken deferredCsrfToken = this.tokenRepository.loadDeferredToken(request, response);\n\t\t\tthis.requestHandler.handle(request, response, deferredCsrfToken);\n\t\t\tthis.logger.debug(\"Replaced CSRF Token\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.io.Serial;\n\nimport org.springframework.security.access.AccessDeniedException;\n\n/**\n * Thrown when an invalid or missing {@link CsrfToken} is found in the HttpServletRequest\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class CsrfException extends AccessDeniedException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 7802567627837252670L;\n\n\tpublic CsrfException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.io.IOException;\nimport java.security.MessageDigest;\nimport java.util.Arrays;\nimport java.util.HashSet;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.access.AccessDeniedHandlerImpl;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * <p>\n * Applies\n * <a href=\"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)\" >CSRF</a>\n * protection using a synchronizer token pattern. Developers are required to ensure that\n * {@link CsrfFilter} is invoked for any request that allows state to change. Typically\n * this just means that they should ensure their web application follows proper REST\n * semantics (i.e. do not change state with the HTTP methods GET, HEAD, TRACE, OPTIONS).\n * </p>\n *\n * <p>\n * Typically the {@link CsrfTokenRepository} implementation chooses to store the\n * {@link CsrfToken} in {@link HttpSession} with {@link HttpSessionCsrfTokenRepository}.\n * This is preferred to storing the token in a cookie which can be modified by a client\n * application.\n * </p>\n *\n * @author Rob Winch\n * @author Steve Riesenberg\n * @since 3.2\n */\npublic final class CsrfFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The default {@link RequestMatcher} that indicates if CSRF protection is required or\n\t * not. The default is to ignore GET, HEAD, TRACE, OPTIONS and process all other\n\t * requests.\n\t */\n\tpublic static final RequestMatcher DEFAULT_CSRF_MATCHER = new DefaultRequiresCsrfMatcher();\n\n\t/**\n\t * The attribute name to use when marking a given request as one that should not be\n\t * filtered.\n\t * <p>\n\t * To use, set the attribute on your {@link HttpServletRequest}: <pre>\n\t * \tCsrfFilter.skipRequest(request);\n\t * </pre>\n\t */\n\tprivate static final String SHOULD_NOT_FILTER = \"SHOULD_NOT_FILTER\" + CsrfFilter.class.getName();\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final CsrfTokenRepository tokenRepository;\n\n\tprivate RequestMatcher requireCsrfProtectionMatcher = DEFAULT_CSRF_MATCHER;\n\n\tprivate AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();\n\n\tprivate CsrfTokenRequestHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();\n\n\t/**\n\t * Creates a new instance.\n\t * @param tokenRepository the {@link CsrfTokenRepository} to use\n\t */\n\tpublic CsrfFilter(CsrfTokenRepository tokenRepository) {\n\t\tAssert.notNull(tokenRepository, \"tokenRepository cannot be null\");\n\t\tthis.tokenRepository = tokenRepository;\n\t}\n\n\t@Override\n\tprotected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {\n\t\treturn Boolean.TRUE.equals(request.getAttribute(SHOULD_NOT_FILTER));\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tDeferredCsrfToken deferredCsrfToken = this.tokenRepository.loadDeferredToken(request, response);\n\t\trequest.setAttribute(DeferredCsrfToken.class.getName(), deferredCsrfToken);\n\t\tthis.requestHandler.handle(request, response, deferredCsrfToken);\n\t\tif (!this.requireCsrfProtectionMatcher.matches(request)) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(\"Did not protect against CSRF since request did not match \"\n\t\t\t\t\t\t+ this.requireCsrfProtectionMatcher);\n\t\t\t}\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tCsrfToken csrfToken = deferredCsrfToken.get();\n\t\tString actualToken = this.requestHandler.resolveCsrfTokenValue(request, csrfToken);\n\t\tif (actualToken != null && this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(LogMessage.format(\"Found a CSRF token in the request\"));\n\t\t}\n\t\tif (!equalsConstantTime(csrfToken.getToken(), actualToken)) {\n\t\t\tboolean missingToken = deferredCsrfToken.isGenerated();\n\t\t\tthis.logger\n\t\t\t\t.debug(LogMessage.of(() -> \"Invalid CSRF token found for \" + UrlUtils.buildFullRequestUrl(request)));\n\t\t\tAccessDeniedException exception = (!missingToken) ? new InvalidCsrfTokenException(csrfToken, actualToken)\n\t\t\t\t\t: new MissingCsrfTokenException(actualToken);\n\t\t\tthis.accessDeniedHandler.handle(request, response, exception);\n\t\t\treturn;\n\t\t}\n\t\tfilterChain.doFilter(request, response);\n\t}\n\n\tpublic static void skipRequest(HttpServletRequest request) {\n\t\trequest.setAttribute(SHOULD_NOT_FILTER, Boolean.TRUE);\n\t}\n\n\t/**\n\t * Specifies a {@link RequestMatcher} that is used to determine if CSRF protection\n\t * should be applied. If the {@link RequestMatcher} returns true for a given request,\n\t * then CSRF protection is applied.\n\t *\n\t * <p>\n\t * The default is to apply CSRF protection for any HTTP method other than GET, HEAD,\n\t * TRACE, OPTIONS.\n\t * </p>\n\t * @param requireCsrfProtectionMatcher the {@link RequestMatcher} used to determine if\n\t * CSRF protection should be applied.\n\t */\n\tpublic void setRequireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {\n\t\tAssert.notNull(requireCsrfProtectionMatcher, \"requireCsrfProtectionMatcher cannot be null\");\n\t\tthis.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;\n\t}\n\n\t/**\n\t * Specifies a {@link AccessDeniedHandler} that should be used when CSRF protection\n\t * fails.\n\t *\n\t * <p>\n\t * The default is to use AccessDeniedHandlerImpl with no arguments.\n\t * </p>\n\t * @param accessDeniedHandler the {@link AccessDeniedHandler} to use\n\t */\n\tpublic void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {\n\t\tAssert.notNull(accessDeniedHandler, \"accessDeniedHandler cannot be null\");\n\t\tthis.accessDeniedHandler = accessDeniedHandler;\n\t}\n\n\t/**\n\t * Specifies a {@link CsrfTokenRequestHandler} that is used to make the\n\t * {@link CsrfToken} available as a request attribute.\n\t *\n\t * <p>\n\t * The default is {@link XorCsrfTokenRequestAttributeHandler}.\n\t * </p>\n\t * @param requestHandler the {@link CsrfTokenRequestHandler} to use\n\t * @since 5.8\n\t */\n\tpublic void setRequestHandler(CsrfTokenRequestHandler requestHandler) {\n\t\tAssert.notNull(requestHandler, \"requestHandler cannot be null\");\n\t\tthis.requestHandler = requestHandler;\n\t}\n\n\t/**\n\t * Constant time comparison to prevent against timing attacks.\n\t * @param expected\n\t * @param actual\n\t * @return\n\t */\n\tprivate static boolean equalsConstantTime(String expected, @Nullable String actual) {\n\t\tif (expected == actual) {\n\t\t\treturn true;\n\t\t}\n\t\tif (expected == null || actual == null) {\n\t\t\treturn false;\n\t\t}\n\t\t// Encode after ensure that the string is not null\n\t\tbyte[] expectedBytes = Utf8.encode(expected);\n\t\tbyte[] actualBytes = Utf8.encode(actual);\n\t\treturn MessageDigest.isEqual(expectedBytes, actualBytes);\n\t}\n\n\tprivate static final class DefaultRequiresCsrfMatcher implements RequestMatcher {\n\n\t\tprivate final HashSet<String> allowedMethods = new HashSet<>(Arrays.asList(\"GET\", \"HEAD\", \"TRACE\", \"OPTIONS\"));\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn !this.allowedMethods.contains(request.getMethod());\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"IsNotHttpMethod \" + this.allowedMethods;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.util.Assert;\n\n/**\n * {@link CsrfLogoutHandler} is in charge of removing the {@link CsrfToken} upon logout. A\n * new {@link CsrfToken} will then be generated by the framework upon the next request.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class CsrfLogoutHandler implements LogoutHandler {\n\n\tprivate final CsrfTokenRepository csrfTokenRepository;\n\n\t/**\n\t * Creates a new instance\n\t * @param csrfTokenRepository the {@link CsrfTokenRepository} to use\n\t */\n\tpublic CsrfLogoutHandler(CsrfTokenRepository csrfTokenRepository) {\n\t\tAssert.notNull(csrfTokenRepository, \"csrfTokenRepository cannot be null\");\n\t\tthis.csrfTokenRepository = csrfTokenRepository;\n\t}\n\n\t/**\n\t * Clears the {@link CsrfToken}\n\t *\n\t * @see org.springframework.security.web.authentication.logout.LogoutHandler#logout(jakarta.servlet.http.HttpServletRequest,\n\t * jakarta.servlet.http.HttpServletResponse,\n\t * org.springframework.security.core.Authentication)\n\t */\n\t@Override\n\tpublic void logout(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable Authentication authentication) {\n\t\tthis.csrfTokenRepository.saveToken(null, request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.io.Serializable;\n\n/**\n * Provides the information about an expected CSRF token.\n *\n * @author Rob Winch\n * @since 3.2\n * @see DefaultCsrfToken\n */\npublic interface CsrfToken extends Serializable {\n\n\t/**\n\t * Gets the HTTP header that the CSRF is populated on the response and can be placed\n\t * on requests instead of the parameter. Cannot be null.\n\t * @return the HTTP header that the CSRF is populated on the response and can be\n\t * placed on requests instead of the parameter\n\t */\n\tString getHeaderName();\n\n\t/**\n\t * Gets the HTTP parameter name that should contain the token. Cannot be null.\n\t * @return the HTTP parameter name that should contain the token.\n\t */\n\tString getParameterName();\n\n\t/**\n\t * Gets the token value. Cannot be null.\n\t * @return the token value\n\t */\n\tString getToken();\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfTokenRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An API to allow changing the method in which the expected {@link CsrfToken} is\n * associated to the {@link HttpServletRequest}. For example, it may be stored in\n * {@link HttpSession}.\n *\n * @author Rob Winch\n * @author Steve Riesenberg\n * @since 3.2\n * @see HttpSessionCsrfTokenRepository\n */\npublic interface CsrfTokenRepository {\n\n\t/**\n\t * Generates a {@link CsrfToken}\n\t * @param request the {@link HttpServletRequest} to use\n\t * @return the {@link CsrfToken} that was generated. Cannot be null.\n\t */\n\tCsrfToken generateToken(HttpServletRequest request);\n\n\t/**\n\t * Saves the {@link CsrfToken} using the {@link HttpServletRequest} and\n\t * {@link HttpServletResponse}. If the {@link CsrfToken} is null, it is the same as\n\t * deleting it.\n\t * @param token the {@link CsrfToken} to save or null to delete\n\t * @param request the {@link HttpServletRequest} to use\n\t * @param response the {@link HttpServletResponse} to use\n\t */\n\tvoid saveToken(@Nullable CsrfToken token, HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Loads the expected {@link CsrfToken} from the {@link HttpServletRequest}\n\t * @param request the {@link HttpServletRequest} to use\n\t * @return the {@link CsrfToken} or null if none exists\n\t */\n\t@Nullable CsrfToken loadToken(HttpServletRequest request);\n\n\t/**\n\t * Defers loading the {@link CsrfToken} using the {@link HttpServletRequest} and\n\t * {@link HttpServletResponse} until it is needed by the application.\n\t * <p>\n\t * The returned {@link DeferredCsrfToken} is cached to allow subsequent calls to\n\t * {@link DeferredCsrfToken#get()} to return the same {@link CsrfToken} without the\n\t * cost of loading or generating the token again.\n\t * @param request the {@link HttpServletRequest} to use\n\t * @param response the {@link HttpServletResponse} to use\n\t * @return a {@link DeferredCsrfToken} that will load the {@link CsrfToken}\n\t * @since 5.8\n\t */\n\tdefault DeferredCsrfToken loadDeferredToken(HttpServletRequest request, HttpServletResponse response) {\n\t\treturn new RepositoryDeferredCsrfToken(this, request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfTokenRequestAttributeHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of the {@link CsrfTokenRequestHandler} interface that is capable of\n * making the {@link CsrfToken} available as a request attribute and resolving the token\n * value as either a header or parameter value of the request.\n *\n * @author Steve Riesenberg\n * @author Yoobin Yoon\n * @author Andrey Litvitski\n * @since 5.8\n */\npublic class CsrfTokenRequestAttributeHandler implements CsrfTokenRequestHandler {\n\n\tprivate static final Log logger = LogFactory.getLog(CsrfTokenRequestAttributeHandler.class);\n\n\t@Nullable private String csrfRequestAttributeName = \"_csrf\";\n\n\t/**\n\t * The {@link CsrfToken} is available as a request attribute named\n\t * {@code CsrfToken.class.getName()}. By default, an additional request attribute that\n\t * is the same as {@link CsrfToken#getParameterName()} is set. This attribute allows\n\t * overriding the additional attribute.\n\t * @param csrfRequestAttributeName the name of an additional request attribute with\n\t * the value of the CsrfToken. Default is {@link CsrfToken#getParameterName()}\n\t */\n\tpublic final void setCsrfRequestAttributeName(@Nullable String csrfRequestAttributeName) {\n\t\tthis.csrfRequestAttributeName = csrfRequestAttributeName;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tSupplier<CsrfToken> deferredCsrfToken) {\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tAssert.notNull(response, \"response cannot be null\");\n\t\tAssert.notNull(deferredCsrfToken, \"deferredCsrfToken cannot be null\");\n\n\t\tCsrfToken csrfToken = new SupplierCsrfToken(deferredCsrfToken);\n\t\trequest.setAttribute(CsrfToken.class.getName(), csrfToken);\n\t\tString csrfAttrName = (this.csrfRequestAttributeName != null) ? this.csrfRequestAttributeName\n\t\t\t\t: csrfToken.getParameterName();\n\t\trequest.setAttribute(csrfAttrName, csrfToken);\n\n\t\tlogger.trace(LogMessage.format(\"Wrote a CSRF token to the following request attributes: [%s, %s]\", csrfAttrName,\n\t\t\t\tCsrfToken.class.getName()));\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tprivate static final class SupplierCsrfToken implements CsrfToken {\n\n\t\tprivate final Supplier<CsrfToken> csrfTokenSupplier;\n\n\t\tprivate SupplierCsrfToken(Supplier<CsrfToken> csrfTokenSupplier) {\n\t\t\tthis.csrfTokenSupplier = csrfTokenSupplier;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getHeaderName() {\n\t\t\treturn getDelegate().getHeaderName();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getParameterName() {\n\t\t\treturn getDelegate().getParameterName();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getToken() {\n\t\t\treturn getDelegate().getToken();\n\t\t}\n\n\t\tprivate CsrfToken getDelegate() {\n\t\t\tCsrfToken delegate = this.csrfTokenSupplier.get();\n\t\t\tif (delegate == null) {\n\t\t\t\tthrow new IllegalStateException(\"csrfTokenSupplier returned null delegate\");\n\t\t\t}\n\t\t\treturn delegate;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfTokenRequestHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\n\n/**\n * A callback interface that is used to make the {@link CsrfToken} created by the\n * {@link CsrfTokenRepository} available as a request attribute. Implementations of this\n * interface may choose to perform additional tasks or customize how the token is made\n * available to the application through request attributes.\n *\n * @author Steve Riesenberg\n * @author Yoobin Yoon\n * @since 5.8\n * @see CsrfTokenRequestAttributeHandler\n */\n@FunctionalInterface\npublic interface CsrfTokenRequestHandler extends CsrfTokenRequestResolver {\n\n\t/**\n\t * Handles a request using a {@link CsrfToken}.\n\t * @param request the {@code HttpServletRequest} being handled\n\t * @param response the {@code HttpServletResponse} being handled\n\t * @param csrfToken the {@link CsrfToken} created by the {@link CsrfTokenRepository}\n\t */\n\tvoid handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken);\n\n\t@Override\n\tdefault @Nullable String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tAssert.notNull(csrfToken, \"csrfToken cannot be null\");\n\t\tString actualToken = request.getHeader(csrfToken.getHeaderName());\n\t\tif (actualToken != null) {\n\t\t\treturn actualToken;\n\t\t}\n\t\tCsrfTokenRequestHandlerLoggerHolder.logger.trace(\n\t\t\t\tLogMessage.format(\"Did not find a CSRF token in the [%s] request header\", csrfToken.getHeaderName()));\n\n\t\tactualToken = request.getParameter(csrfToken.getParameterName());\n\t\tif (actualToken != null) {\n\t\t\treturn actualToken;\n\t\t}\n\t\tCsrfTokenRequestHandlerLoggerHolder.logger.trace(LogMessage\n\t\t\t.format(\"Did not find a CSRF token in the [%s] request parameter\", csrfToken.getParameterName()));\n\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfTokenRequestHandlerLoggerHolder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\n/**\n * Utility class for holding the logger for {@link CsrfTokenRequestHandler}\n */\nfinal class CsrfTokenRequestHandlerLoggerHolder {\n\n\tstatic final Log logger = LogFactory.getLog(CsrfTokenRequestHandler.class);\n\n\tprivate CsrfTokenRequestHandlerLoggerHolder() {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/CsrfTokenRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Implementations of this interface are capable of resolving the token value of a\n * {@link CsrfToken} from the provided {@code HttpServletRequest}. Used by the\n * {@link CsrfFilter}.\n *\n * @author Steve Riesenberg\n * @since 5.8\n * @see CsrfTokenRequestAttributeHandler\n */\n@FunctionalInterface\npublic interface CsrfTokenRequestResolver {\n\n\t/**\n\t * Returns the token value resolved from the provided {@code HttpServletRequest} and\n\t * {@link CsrfToken} or {@code null} if not available.\n\t * @param request the {@code HttpServletRequest} being processed\n\t * @param csrfToken the {@link CsrfToken} created by the {@link CsrfTokenRepository}\n\t * @return the token value resolved from the request\n\t */\n\t@Nullable String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/DefaultCsrfToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.io.Serial;\n\nimport org.springframework.util.Assert;\n\n/**\n * A CSRF token that is used to protect against CSRF attacks.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class DefaultCsrfToken implements CsrfToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 6552658053267913685L;\n\n\tprivate final String token;\n\n\tprivate final String parameterName;\n\n\tprivate final String headerName;\n\n\t/**\n\t * Creates a new instance\n\t * @param headerName the HTTP header name to use\n\t * @param parameterName the HTTP parameter name to use\n\t * @param token the value of the token (i.e. expected value of the HTTP parameter of\n\t * parametername).\n\t */\n\tpublic DefaultCsrfToken(String headerName, String parameterName, String token) {\n\t\tAssert.hasLength(headerName, \"headerName cannot be null or empty\");\n\t\tAssert.hasLength(parameterName, \"parameterName cannot be null or empty\");\n\t\tAssert.hasLength(token, \"token cannot be null or empty\");\n\t\tthis.headerName = headerName;\n\t\tthis.parameterName = parameterName;\n\t\tthis.token = token;\n\t}\n\n\t@Override\n\tpublic String getHeaderName() {\n\t\treturn this.headerName;\n\t}\n\n\t@Override\n\tpublic String getParameterName() {\n\t\treturn this.parameterName;\n\t}\n\n\t@Override\n\tpublic String getToken() {\n\t\treturn this.token;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/DeferredCsrfToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.util.function.Supplier;\n\n/**\n * An interface that allows delayed access to a {@link CsrfToken} that may be generated.\n *\n * @author Rob Winch\n * @author Steve Riesenberg\n * @author Daeho Kwon\n * @since 5.8\n */\npublic interface DeferredCsrfToken extends Supplier<CsrfToken> {\n\n\t/**\n\t * Gets the {@link CsrfToken}\n\t * @return a non-null {@link CsrfToken}\n\t */\n\tCsrfToken get();\n\n\t/**\n\t * Returns true if {@link #get()} refers to a generated {@link CsrfToken} or false if\n\t * it already existed.\n\t * @return true if {@link #get()} refers to a generated {@link CsrfToken} or false if\n\t * it already existed.\n\t */\n\tboolean isGenerated();\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/HttpSessionCsrfTokenRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.util.UUID;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link CsrfTokenRepository} that stores the {@link CsrfToken} in the\n * {@link HttpSession}.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class HttpSessionCsrfTokenRepository implements CsrfTokenRepository {\n\n\tprivate static final String DEFAULT_CSRF_PARAMETER_NAME = \"_csrf\";\n\n\tprivate static final String DEFAULT_CSRF_HEADER_NAME = \"X-CSRF-TOKEN\";\n\n\tprivate static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class.getName()\n\t\t.concat(\".CSRF_TOKEN\");\n\n\tprivate String parameterName = DEFAULT_CSRF_PARAMETER_NAME;\n\n\tprivate String headerName = DEFAULT_CSRF_HEADER_NAME;\n\n\tprivate String sessionAttributeName = DEFAULT_CSRF_TOKEN_ATTR_NAME;\n\n\t@Override\n\tpublic void saveToken(@Nullable CsrfToken token, HttpServletRequest request, HttpServletResponse response) {\n\t\tif (token == null) {\n\t\t\tHttpSession session = request.getSession(false);\n\t\t\tif (session != null) {\n\t\t\t\tsession.removeAttribute(this.sessionAttributeName);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tHttpSession session = request.getSession();\n\t\t\tsession.setAttribute(this.sessionAttributeName, token);\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable CsrfToken loadToken(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (CsrfToken) session.getAttribute(this.sessionAttributeName);\n\t}\n\n\t@Override\n\tpublic CsrfToken generateToken(HttpServletRequest request) {\n\t\treturn new DefaultCsrfToken(this.headerName, this.parameterName, createNewToken());\n\t}\n\n\t/**\n\t * Sets the {@link HttpServletRequest} parameter name that the {@link CsrfToken} is\n\t * expected to appear on\n\t * @param parameterName the new parameter name to use\n\t */\n\tpublic void setParameterName(String parameterName) {\n\t\tAssert.hasLength(parameterName, \"parameterName cannot be null or empty\");\n\t\tthis.parameterName = parameterName;\n\t}\n\n\t/**\n\t * Sets the header name that the {@link CsrfToken} is expected to appear on and the\n\t * header that the response will contain the {@link CsrfToken}.\n\t * @param headerName the new header name to use\n\t */\n\tpublic void setHeaderName(String headerName) {\n\t\tAssert.hasLength(headerName, \"headerName cannot be null or empty\");\n\t\tthis.headerName = headerName;\n\t}\n\n\t/**\n\t * Sets the {@link HttpSession} attribute name that the {@link CsrfToken} is stored in\n\t * @param sessionAttributeName the new attribute name to use\n\t */\n\tpublic void setSessionAttributeName(String sessionAttributeName) {\n\t\tAssert.hasLength(sessionAttributeName, \"sessionAttributeName cannot be null or empty\");\n\t\tthis.sessionAttributeName = sessionAttributeName;\n\t}\n\n\tprivate String createNewToken() {\n\t\treturn UUID.randomUUID().toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/InvalidCsrfTokenException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.io.Serial;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Thrown when an expected {@link CsrfToken} exists, but it does not match the value\n * present on the {@link HttpServletRequest}\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class InvalidCsrfTokenException extends CsrfException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -7745955098435417418L;\n\n\t/**\n\t * @param expectedAccessToken\n\t * @param actualAccessToken\n\t */\n\tpublic InvalidCsrfTokenException(CsrfToken expectedAccessToken, @Nullable String actualAccessToken) {\n\t\tsuper(\"Invalid CSRF Token '\" + actualAccessToken + \"' was found on the request parameter '\"\n\t\t\t\t+ expectedAccessToken.getParameterName() + \"' or header '\" + expectedAccessToken.getHeaderName()\n\t\t\t\t+ \"'.\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/MissingCsrfTokenException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Thrown when no expected {@link CsrfToken} is found but is required.\n *\n * @author Rob Winch\n * @since 3.2\n */\n@SuppressWarnings(\"serial\")\npublic class MissingCsrfTokenException extends CsrfException {\n\n\tpublic MissingCsrfTokenException(@Nullable String actualToken) {\n\t\tsuper(\"Could not verify the provided CSRF token because no token was found to compare.\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/RepositoryDeferredCsrfToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @author Rob Winch\n * @author Steve Riesenberg\n * @since 5.8\n */\nfinal class RepositoryDeferredCsrfToken implements DeferredCsrfToken {\n\n\tprivate final CsrfTokenRepository csrfTokenRepository;\n\n\tprivate final HttpServletRequest request;\n\n\tprivate final HttpServletResponse response;\n\n\tprivate @Nullable CsrfToken csrfToken;\n\n\tprivate boolean missingToken;\n\n\tRepositoryDeferredCsrfToken(CsrfTokenRepository csrfTokenRepository, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tthis.csrfTokenRepository = csrfTokenRepository;\n\t\tthis.request = request;\n\t\tthis.response = response;\n\t}\n\n\t@Override\n\tpublic CsrfToken get() {\n\t\tinit();\n\t\treturn Objects.requireNonNull(this.csrfToken);\n\t}\n\n\t@Override\n\tpublic boolean isGenerated() {\n\t\tinit();\n\t\treturn this.missingToken;\n\t}\n\n\tprivate void init() {\n\t\tif (this.csrfToken != null) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.csrfToken = this.csrfTokenRepository.loadToken(this.request);\n\t\tthis.missingToken = (this.csrfToken == null);\n\t\tif (this.missingToken) {\n\t\t\tthis.csrfToken = this.csrfTokenRepository.generateToken(this.request);\n\t\t\tthis.csrfTokenRepository.saveToken(this.csrfToken, this.request, this.response);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.security.SecureRandom;\nimport java.util.Base64;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of the {@link CsrfTokenRequestHandler} interface that is capable of\n * masking the value of the {@link CsrfToken} on each request and resolving the raw token\n * value from the masked value as either a header or parameter value of the request.\n *\n * @author Steve Riesenberg\n * @author Yoobin Yoon\n * @since 5.8\n */\npublic final class XorCsrfTokenRequestAttributeHandler extends CsrfTokenRequestAttributeHandler {\n\n\tprivate static final Log logger = LogFactory.getLog(XorCsrfTokenRequestAttributeHandler.class);\n\n\tprivate SecureRandom secureRandom = new SecureRandom();\n\n\t/**\n\t * Specifies the {@code SecureRandom} used to generate random bytes that are used to\n\t * mask the value of the {@link CsrfToken} on each request.\n\t * @param secureRandom the {@code SecureRandom} to use to generate random bytes\n\t */\n\tpublic void setSecureRandom(SecureRandom secureRandom) {\n\t\tAssert.notNull(secureRandom, \"secureRandom cannot be null\");\n\t\tthis.secureRandom = secureRandom;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tSupplier<CsrfToken> deferredCsrfToken) {\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tAssert.notNull(response, \"response cannot be null\");\n\t\tAssert.notNull(deferredCsrfToken, \"deferredCsrfToken cannot be null\");\n\t\tSupplier<CsrfToken> updatedCsrfToken = deferCsrfTokenUpdate(deferredCsrfToken);\n\t\tsuper.handle(request, response, updatedCsrfToken);\n\t}\n\n\tprivate Supplier<CsrfToken> deferCsrfTokenUpdate(Supplier<CsrfToken> csrfTokenSupplier) {\n\t\treturn new CachedCsrfTokenSupplier(() -> {\n\t\t\tCsrfToken csrfToken = csrfTokenSupplier.get();\n\t\t\tAssert.state(csrfToken != null, \"csrfToken supplier returned null\");\n\t\t\tString updatedToken = createXoredCsrfToken(this.secureRandom, csrfToken.getToken());\n\t\t\treturn new DefaultCsrfToken(csrfToken.getHeaderName(), csrfToken.getParameterName(), updatedToken);\n\t\t});\n\t}\n\n\t@Override\n\tpublic @Nullable String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {\n\t\tString actualToken = super.resolveCsrfTokenValue(request, csrfToken);\n\t\tif (actualToken == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn getTokenValue(actualToken, csrfToken.getToken());\n\t}\n\n\tprivate static @Nullable String getTokenValue(String actualToken, String token) {\n\t\tbyte[] actualBytes;\n\t\ttry {\n\t\t\tactualBytes = Base64.getUrlDecoder().decode(actualToken);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.trace(LogMessage.format(\"Not returning the CSRF token since it's not Base64-encoded\"), ex);\n\t\t\treturn null;\n\t\t}\n\n\t\tbyte[] tokenBytes = Utf8.encode(token);\n\t\tint tokenSize = tokenBytes.length;\n\t\tif (actualBytes.length != tokenSize * 2) {\n\t\t\tlogger.trace(LogMessage.format(\n\t\t\t\t\t\"Not returning the CSRF token since its Base64-decoded length (%d) is not equal to (%d)\",\n\t\t\t\t\tactualBytes.length, tokenSize * 2));\n\t\t\treturn null;\n\t\t}\n\n\t\t// extract token and random bytes\n\t\tbyte[] xoredCsrf = new byte[tokenSize];\n\t\tbyte[] randomBytes = new byte[tokenSize];\n\n\t\tSystem.arraycopy(actualBytes, 0, randomBytes, 0, tokenSize);\n\t\tSystem.arraycopy(actualBytes, tokenSize, xoredCsrf, 0, tokenSize);\n\n\t\tbyte[] csrfBytes = xorCsrf(randomBytes, xoredCsrf);\n\t\treturn Utf8.decode(csrfBytes);\n\t}\n\n\tprivate static String createXoredCsrfToken(SecureRandom secureRandom, String token) {\n\t\tbyte[] tokenBytes = Utf8.encode(token);\n\t\tbyte[] randomBytes = new byte[tokenBytes.length];\n\t\tsecureRandom.nextBytes(randomBytes);\n\n\t\tbyte[] xoredBytes = xorCsrf(randomBytes, tokenBytes);\n\t\tbyte[] combinedBytes = new byte[tokenBytes.length + randomBytes.length];\n\t\tSystem.arraycopy(randomBytes, 0, combinedBytes, 0, randomBytes.length);\n\t\tSystem.arraycopy(xoredBytes, 0, combinedBytes, randomBytes.length, xoredBytes.length);\n\n\t\treturn Base64.getUrlEncoder().encodeToString(combinedBytes);\n\t}\n\n\tprivate static byte[] xorCsrf(byte[] randomBytes, byte[] csrfBytes) {\n\t\tAssert.isTrue(randomBytes.length == csrfBytes.length, \"arrays must be equal length\");\n\t\tint len = csrfBytes.length;\n\t\tbyte[] xoredCsrf = new byte[len];\n\t\tSystem.arraycopy(csrfBytes, 0, xoredCsrf, 0, len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\txoredCsrf[i] ^= randomBytes[i];\n\t\t}\n\t\treturn xoredCsrf;\n\t}\n\n\tprivate static final class CachedCsrfTokenSupplier implements Supplier<CsrfToken> {\n\n\t\tprivate final Supplier<CsrfToken> delegate;\n\n\t\tprivate @Nullable CsrfToken csrfToken;\n\n\t\tprivate CachedCsrfTokenSupplier(Supplier<CsrfToken> delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic CsrfToken get() {\n\t\t\tif (this.csrfToken == null) {\n\t\t\t\tthis.csrfToken = this.delegate.get();\n\t\t\t}\n\t\t\treturn this.csrfToken;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/csrf/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * APIs for protection against CSRF attacks.\n */\n@NullMarked\npackage org.springframework.security.web.csrf;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/debug/DebugFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.debug;\n\nimport java.io.IOException;\nimport java.util.Enumeration;\nimport java.util.List;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.FilterConfig;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.util.UrlUtils;\n\n/**\n * Spring Security debugging filter.\n * <p>\n * Logs information (such as session creation) to help the user understand how requests\n * are being handled by Spring Security and provide them with other relevant information\n * (such as when sessions are being created).\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.1\n */\npublic final class DebugFilter implements Filter {\n\n\tstatic final String ALREADY_FILTERED_ATTR_NAME = DebugFilter.class.getName().concat(\".FILTERED\");\n\n\tprivate final FilterChainProxy filterChainProxy;\n\n\tprivate final Logger logger = new Logger();\n\n\tpublic DebugFilter(FilterChainProxy filterChainProxy) {\n\t\tthis.filterChainProxy = filterChainProxy;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {\n\t\t\tthrow new ServletException(\"DebugFilter just supports HTTP requests\");\n\t\t}\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, filterChain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows IOException, ServletException {\n\t\tList<Filter> filters = getFilters(request);\n\t\tthis.logger.info(\"Request received for \" + request.getMethod() + \" '\" + UrlUtils.buildRequestUrl(request)\n\t\t\t\t+ \"':\\n\\n\" + request + \"\\n\\n\" + \"servletPath:\" + request.getServletPath() + \"\\n\" + \"pathInfo:\"\n\t\t\t\t+ request.getPathInfo() + \"\\n\" + \"headers: \\n\" + formatHeaders(request) + \"\\n\\n\"\n\t\t\t\t+ formatFilters(filters));\n\t\tif (request.getAttribute(ALREADY_FILTERED_ATTR_NAME) == null) {\n\t\t\tinvokeWithWrappedRequest(request, response, filterChain);\n\t\t}\n\t\telse {\n\t\t\tthis.filterChainProxy.doFilter(request, response, filterChain);\n\t\t}\n\t}\n\n\tprivate void invokeWithWrappedRequest(HttpServletRequest request, HttpServletResponse response,\n\t\t\tFilterChain filterChain) throws IOException, ServletException {\n\t\trequest.setAttribute(ALREADY_FILTERED_ATTR_NAME, Boolean.TRUE);\n\t\trequest = new DebugRequestWrapper(request);\n\t\ttry {\n\t\t\tthis.filterChainProxy.doFilter(request, response, filterChain);\n\t\t}\n\t\tfinally {\n\t\t\trequest.removeAttribute(ALREADY_FILTERED_ATTR_NAME);\n\t\t}\n\t}\n\n\tString formatHeaders(HttpServletRequest request) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tEnumeration<String> eHeaderNames = request.getHeaderNames();\n\t\twhile (eHeaderNames.hasMoreElements()) {\n\t\t\tString headerName = eHeaderNames.nextElement();\n\t\t\tsb.append(headerName);\n\t\t\tsb.append(\": \");\n\t\t\tEnumeration<String> eHeaderValues = request.getHeaders(headerName);\n\t\t\twhile (eHeaderValues.hasMoreElements()) {\n\t\t\t\tsb.append(eHeaderValues.nextElement());\n\t\t\t\tif (eHeaderValues.hasMoreElements()) {\n\t\t\t\t\tsb.append(\", \");\n\t\t\t\t}\n\t\t\t}\n\t\t\tsb.append(\"\\n\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tString formatFilters(@Nullable List<Filter> filters) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"Security filter chain: \");\n\t\tif (filters == null) {\n\t\t\tsb.append(\"no match\");\n\t\t}\n\t\telse if (filters.isEmpty()) {\n\t\t\tsb.append(\"[] empty (bypassed by security='none') \");\n\t\t}\n\t\telse {\n\t\t\tsb.append(\"[\\n\");\n\t\t\tfor (Filter f : filters) {\n\t\t\t\tsb.append(\"  \").append(f.getClass().getSimpleName()).append(\"\\n\");\n\t\t\t}\n\t\t\tsb.append(\"]\");\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate @Nullable List<Filter> getFilters(HttpServletRequest request) {\n\t\tfor (SecurityFilterChain chain : this.filterChainProxy.getFilterChains()) {\n\t\t\tif (chain.matches(request)) {\n\t\t\t\treturn chain.getFilters();\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void init(FilterConfig filterConfig) {\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t}\n\n\tpublic FilterChainProxy getFilterChainProxy() {\n\t\treturn this.filterChainProxy;\n\t}\n\n\tstatic class DebugRequestWrapper extends HttpServletRequestWrapper {\n\n\t\tprivate static final Logger logger = new Logger();\n\n\t\tDebugRequestWrapper(HttpServletRequest request) {\n\t\t\tsuper(request);\n\t\t}\n\n\t\t@Override\n\t\tpublic HttpSession getSession() {\n\t\t\tboolean sessionExists = super.getSession(false) != null;\n\t\t\tHttpSession session = super.getSession();\n\t\t\tif (!sessionExists) {\n\t\t\t\tDebugRequestWrapper.logger.info(\"New HTTP session created: \" + session.getId(), true);\n\t\t\t}\n\t\t\treturn session;\n\t\t}\n\n\t\t@Override\n\t\tpublic HttpSession getSession(boolean create) {\n\t\t\tif (!create) {\n\t\t\t\treturn super.getSession(create);\n\t\t\t}\n\t\t\treturn getSession();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/debug/Logger.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.debug;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\n/**\n * Controls output for the Spring Security debug feature.\n *\n * @author Luke Taylor\n * @since 3.1\n */\nfinal class Logger {\n\n\tprivate static final Log logger = LogFactory.getLog(\"Spring Security Debugger\");\n\n\tvoid info(String message) {\n\t\tinfo(message, false);\n\t}\n\n\tvoid info(String message, boolean dumpStack) {\n\t\tStringBuilder output = new StringBuilder(256);\n\t\toutput.append(\"\\n\\n************************************************************\\n\\n\");\n\t\toutput.append(message).append(\"\\n\");\n\n\t\tif (dumpStack) {\n\t\t\tStringWriter os = new StringWriter();\n\t\t\tnew Exception().printStackTrace(new PrintWriter(os));\n\t\t\tStringBuffer buffer = os.getBuffer();\n\t\t\t// Remove the exception in case it scares people.\n\t\t\tint start = buffer.indexOf(\"java.lang.Exception\");\n\t\t\tbuffer.replace(start, start + 19, \"\");\n\t\t\toutput.append(\"\\nCall stack: \\n\").append(os.toString());\n\t\t}\n\n\t\toutput.append(\"\\n\\n************************************************************\\n\\n\");\n\n\t\tlogger.info(output.toString());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/debug/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * APIs for debugging web security.\n */\n@NullMarked\npackage org.springframework.security.web.debug;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/CompositeRequestRejectedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link RequestRejectedHandler} that delegates to several other\n * {@link RequestRejectedHandler}s.\n *\n * @author Adam Ostrožlík\n * @since 5.7\n */\npublic final class CompositeRequestRejectedHandler implements RequestRejectedHandler {\n\n\tprivate final List<RequestRejectedHandler> requestRejectedhandlers;\n\n\t/**\n\t * Creates a new instance.\n\t * @param requestRejectedhandlers the {@link RequestRejectedHandler} instances to\n\t * handle {@link org.springframework.security.web.firewall.RequestRejectedException}\n\t */\n\tpublic CompositeRequestRejectedHandler(RequestRejectedHandler... requestRejectedhandlers) {\n\t\tAssert.notEmpty(requestRejectedhandlers, \"requestRejectedhandlers cannot be empty\");\n\t\tthis.requestRejectedhandlers = Arrays.asList(requestRejectedhandlers);\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tRequestRejectedException requestRejectedException) throws IOException, ServletException {\n\t\tfor (RequestRejectedHandler requestRejectedhandler : this.requestRejectedhandlers) {\n\t\t\trequestRejectedhandler.handle(request, response, requestRejectedException);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/DefaultHttpFirewall.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * <p>\n * User's should consider using {@link StrictHttpFirewall} because rather than trying to\n * sanitize a malicious URL it rejects the malicious URL providing better security\n * guarantees.\n * <p>\n * Default implementation which wraps requests in order to provide consistent values of\n * the {@code servletPath} and {@code pathInfo}, which do not contain path parameters (as\n * defined in <a href=\"https://www.ietf.org/rfc/rfc2396.txt\">RFC 2396</a>). Different\n * servlet containers interpret the servlet spec differently as to how path parameters are\n * treated and it is possible they might be added in order to bypass particular security\n * constraints. When using this implementation, they will be removed for all requests as\n * the request passes through the security filter chain. Note that this means that any\n * segments in the decoded path which contain a semi-colon, will have the part following\n * the semi-colon removed for request matching. Your application should not contain any\n * valid paths which contain semi-colons.\n * <p>\n * If any un-normalized paths are found (containing directory-traversal character\n * sequences), the request will be rejected immediately. Most containers normalize the\n * paths before performing the servlet-mapping, but again this is not guaranteed by the\n * servlet spec.\n *\n * @author Luke Taylor\n * @see StrictHttpFirewall\n */\npublic class DefaultHttpFirewall implements HttpFirewall {\n\n\tprivate boolean allowUrlEncodedSlash;\n\n\t@Override\n\tpublic FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {\n\t\tFirewalledRequest firewalledRequest = new RequestWrapper(request);\n\t\tif (!isNormalized(firewalledRequest.getServletPath()) || !isNormalized(firewalledRequest.getPathInfo())) {\n\t\t\tthrow new RequestRejectedException(\n\t\t\t\t\t\"Un-normalized paths are not supported: \" + firewalledRequest.getServletPath()\n\t\t\t\t\t\t\t+ ((firewalledRequest.getPathInfo() != null) ? firewalledRequest.getPathInfo() : \"\"));\n\t\t}\n\t\tString requestURI = firewalledRequest.getRequestURI();\n\t\tif (containsInvalidUrlEncodedSlash(requestURI)) {\n\t\t\tthrow new RequestRejectedException(\"The requestURI cannot contain encoded slash. Got \" + requestURI);\n\t\t}\n\t\treturn firewalledRequest;\n\t}\n\n\t@Override\n\tpublic HttpServletResponse getFirewalledResponse(HttpServletResponse response) {\n\t\treturn new FirewalledResponse(response);\n\t}\n\n\t/**\n\t * <p>\n\t * Sets if the application should allow a URL encoded slash character.\n\t * </p>\n\t * <p>\n\t * If true (default is false), a URL encoded slash will be allowed in the URL.\n\t * Allowing encoded slashes can cause security vulnerabilities in some situations\n\t * depending on how the container constructs the HttpServletRequest.\n\t * </p>\n\t * @param allowUrlEncodedSlash the new value (default false)\n\t */\n\tpublic void setAllowUrlEncodedSlash(boolean allowUrlEncodedSlash) {\n\t\tthis.allowUrlEncodedSlash = allowUrlEncodedSlash;\n\t}\n\n\tprivate boolean containsInvalidUrlEncodedSlash(String uri) {\n\t\tif (this.allowUrlEncodedSlash || uri == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn uri.contains(\"%2f\") || uri.contains(\"%2F\");\n\t}\n\n\t/**\n\t * Checks whether a path is normalized (doesn't contain path traversal sequences like\n\t * \"./\", \"/../\" or \"/.\")\n\t * @param path the path to test\n\t * @return true if the path doesn't contain any path-traversal character sequences.\n\t */\n\tprivate boolean isNormalized(String path) {\n\t\tif (path == null) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (int i = path.length(); i > 0;) {\n\t\t\tint slashIndex = path.lastIndexOf('/', i - 1);\n\t\t\tint gap = i - slashIndex;\n\t\t\tif (gap == 2 && path.charAt(slashIndex + 1) == '.') {\n\t\t\t\t// \".\", \"/./\" or \"/.\"\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (gap == 3 && path.charAt(slashIndex + 1) == '.' && path.charAt(slashIndex + 2) == '.') {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ti = slashIndex;\n\t\t}\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/DefaultRequestRejectedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * Default implementation of {@link RequestRejectedHandler} that simply rethrows the\n * exception.\n *\n * @author Leonard Brünings\n * @since 5.4\n */\npublic class DefaultRequestRejectedHandler implements RequestRejectedHandler {\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tRequestRejectedException requestRejectedException) throws IOException, ServletException {\n\t\tthrow requestRejectedException;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/FirewalledRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\n\n/**\n * Request wrapper which is returned by the {@code HttpFirewall} interface.\n * <p>\n * The only difference is the {@code reset} method which allows some or all of the state\n * to be reset by the {@code FilterChainProxy} when the request leaves the security filter\n * chain.\n *\n * @author Luke Taylor\n */\npublic abstract class FirewalledRequest extends HttpServletRequestWrapper {\n\n\t/**\n\t * Constructs a request object wrapping the given request.\n\t * @throws IllegalArgumentException if the request is null\n\t */\n\tpublic FirewalledRequest(HttpServletRequest request) {\n\t\tsuper(request);\n\t}\n\n\t/**\n\t * This method will be called once the request has passed through the security filter\n\t * chain, when it is about to proceed to the application proper.\n\t * <p>\n\t * An implementation can thus choose to modify the state of the request for the\n\t * security infrastructure, while still maintaining the original\n\t * {@link HttpServletRequest}.\n\t */\n\tpublic abstract void reset();\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"FirewalledRequest[ \" + getRequest() + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/FirewalledResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpServletResponseWrapper;\n\nimport org.springframework.util.Assert;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @author Gabriel Lavoie\n * @author Luke Butters\n */\nclass FirewalledResponse extends HttpServletResponseWrapper {\n\n\tprivate static final String LOCATION_HEADER = \"Location\";\n\n\tprivate static final String SET_COOKIE_HEADER = \"Set-Cookie\";\n\n\tFirewalledResponse(HttpServletResponse response) {\n\t\tsuper(response);\n\t}\n\n\t@Override\n\tpublic void sendRedirect(String location) throws IOException {\n\t\t// TODO: implement pluggable validation, instead of simple blocklist.\n\t\t// SEC-1790. Prevent redirects containing CRLF\n\t\tvalidateCrlf(LOCATION_HEADER, location);\n\t\tsuper.sendRedirect(location);\n\t}\n\n\t@Override\n\tpublic void setHeader(String name, String value) {\n\t\tvalidateCrlf(name, value);\n\t\tsuper.setHeader(name, value);\n\t}\n\n\t@Override\n\tpublic void addHeader(String name, String value) {\n\t\tvalidateCrlf(name, value);\n\t\tsuper.addHeader(name, value);\n\t}\n\n\t@Override\n\tpublic void addCookie(Cookie cookie) {\n\t\tif (cookie != null) {\n\t\t\tvalidateCrlf(SET_COOKIE_HEADER, cookie.getName());\n\t\t\tvalidateCrlf(SET_COOKIE_HEADER, cookie.getValue());\n\t\t\tvalidateCrlf(SET_COOKIE_HEADER, cookie.getPath());\n\t\t\tvalidateCrlf(SET_COOKIE_HEADER, cookie.getDomain());\n\t\t}\n\t\tsuper.addCookie(cookie);\n\t}\n\n\tvoid validateCrlf(String name, String value) {\n\t\tAssert.isTrue(!hasCrlf(name) && !hasCrlf(value), () -> \"Invalid characters (CR/LF) in header \" + name);\n\t}\n\n\tprivate boolean hasCrlf(String value) {\n\t\treturn value != null && (value.indexOf('\\n') != -1 || value.indexOf('\\r') != -1);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/HttpFirewall.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * Interface which can be used to reject potentially dangerous requests and/or wrap them\n * to control their behaviour.\n * <p>\n * The implementation is injected into the {@code FilterChainProxy} and will be invoked\n * before sending any request through the filter chain. It can also provide a response\n * wrapper if the response behaviour should also be restricted.\n *\n * @author Luke Taylor\n */\npublic interface HttpFirewall {\n\n\t/**\n\t * Provides the request object which will be passed through the filter chain.\n\t * @throws RequestRejectedException if the request should be rejected immediately\n\t */\n\tFirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException;\n\n\t/**\n\t * Provides the response which will be passed through the filter chain.\n\t * @param response the original response\n\t * @return either the original response or a replacement/wrapper.\n\t */\n\tHttpServletResponse getFirewalledResponse(HttpServletResponse response);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/HttpStatusRequestRejectedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\n\n/**\n * A simple implementation of {@link RequestRejectedHandler} that sends an error with\n * configurable status code.\n *\n * @author Leonard Brünings\n * @since 5.4\n */\npublic class HttpStatusRequestRejectedHandler implements RequestRejectedHandler {\n\n\tprivate static final Log logger = LogFactory.getLog(HttpStatusRequestRejectedHandler.class);\n\n\tprivate final int httpError;\n\n\t/**\n\t * Constructs an instance which uses {@code 400} as response code.\n\t */\n\tpublic HttpStatusRequestRejectedHandler() {\n\t\tthis.httpError = HttpServletResponse.SC_BAD_REQUEST;\n\t}\n\n\t/**\n\t * Constructs an instance which uses a configurable http code as response.\n\t * @param httpError http status code to use\n\t */\n\tpublic HttpStatusRequestRejectedHandler(int httpError) {\n\t\tthis.httpError = httpError;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tRequestRejectedException requestRejectedException) throws IOException {\n\t\tlogger.debug(LogMessage.format(\"Rejecting request due to: %s\", requestRejectedException.getMessage()),\n\t\t\t\trequestRejectedException);\n\t\tresponse.sendError(this.httpError);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/ObservationMarkingRequestRejectedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.io.IOException;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\npublic final class ObservationMarkingRequestRejectedHandler implements RequestRejectedHandler {\n\n\tprivate final ObservationRegistry registry;\n\n\tpublic ObservationMarkingRequestRejectedHandler(ObservationRegistry registry) {\n\t\tthis.registry = registry;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response, RequestRejectedException exception)\n\t\t\tthrows IOException, ServletException {\n\t\tObservation observation = this.registry.getCurrentObservation();\n\t\tif (observation != null) {\n\t\t\tobservation.error(exception);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/RequestRejectedException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.io.Serial;\n\n/**\n * @author Luke Taylor\n */\npublic class RequestRejectedException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 7226768874760909859L;\n\n\tpublic RequestRejectedException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/RequestRejectedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * Used by {@link org.springframework.security.web.FilterChainProxy} to handle an\n * <code>RequestRejectedException</code>.\n *\n * @author Leonard Brünings\n * @since 5.4\n */\npublic interface RequestRejectedHandler {\n\n\t/**\n\t * Handles an request rejected failure.\n\t * @param request that resulted in an <code>RequestRejectedException</code>\n\t * @param response so that the user agent can be advised of the failure\n\t * @param requestRejectedException that caused the invocation\n\t * @throws IOException in the event of an IOException\n\t * @throws ServletException in the event of a ServletException\n\t */\n\tvoid handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tRequestRejectedException requestRejectedException) throws IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/RequestWrapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.io.IOException;\nimport java.util.StringTokenizer;\n\nimport jakarta.servlet.RequestDispatcher;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.StringUtils;\n\n/**\n * Request wrapper which ensures values of {@code servletPath} and {@code pathInfo} are\n * returned which are suitable for pattern matching against. It strips out path parameters\n * and extra consecutive '/' characters.\n *\n * <h3>Path Parameters</h3> Parameters (as defined in\n * <a href=\"https://www.ietf.org/rfc/rfc2396.txt\">RFC 2396</a>) are stripped from the path\n * segments of the {@code servletPath} and {@code pathInfo} values of the request.\n * <p>\n * The parameter sequence is demarcated by a semi-colon, so each segment is checked for\n * the occurrence of a \";\" character and truncated at that point if it is present.\n * <p>\n * The behaviour differs between servlet containers in how they interpret the servlet\n * spec, which does not clearly state what the behaviour should be. For consistency, we\n * make sure they are always removed, to avoid the risk of URL matching rules being\n * bypassed by the malicious addition of parameters to the path component.\n *\n * @author Luke Taylor\n * @author Ngoc Nhan\n */\nfinal class RequestWrapper extends FirewalledRequest {\n\n\tprivate final @Nullable String strippedServletPath;\n\n\tprivate final @Nullable String strippedPathInfo;\n\n\tprivate boolean stripPaths = true;\n\n\tRequestWrapper(HttpServletRequest request) {\n\t\tsuper(request);\n\t\tthis.strippedServletPath = strip(request.getServletPath());\n\t\tString pathInfo = strip(request.getPathInfo());\n\t\tif (!StringUtils.hasLength(pathInfo)) {\n\t\t\tpathInfo = null;\n\t\t}\n\t\tthis.strippedPathInfo = pathInfo;\n\t}\n\n\t/**\n\t * Removes path parameters from each path segment in the supplied path and truncates\n\t * sequences of multiple '/' characters to a single '/'.\n\t * @param path either the {@code servletPath} and {@code pathInfo} from the original\n\t * request\n\t * @return the supplied value, with path parameters removed and sequences of multiple\n\t * '/' characters truncated, or null if the supplied path was null.\n\t */\n\tprivate @Nullable String strip(String path) {\n\t\tif (path == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint semicolonIndex = path.indexOf(';');\n\t\tif (semicolonIndex < 0) {\n\t\t\tint doubleSlashIndex = path.indexOf(\"//\");\n\t\t\tif (doubleSlashIndex < 0) {\n\t\t\t\t// Most likely case, no parameters in any segment and no '//', so no\n\t\t\t\t// stripping required\n\t\t\t\treturn path;\n\t\t\t}\n\t\t}\n\t\tStringTokenizer tokenizer = new StringTokenizer(path, \"/\");\n\t\tStringBuilder stripped = new StringBuilder(path.length());\n\t\tif (path.charAt(0) == '/') {\n\t\t\tstripped.append('/');\n\t\t}\n\t\twhile (tokenizer.hasMoreTokens()) {\n\t\t\tString segment = tokenizer.nextToken();\n\t\t\tsemicolonIndex = segment.indexOf(';');\n\t\t\tif (semicolonIndex >= 0) {\n\t\t\t\tsegment = segment.substring(0, semicolonIndex);\n\t\t\t}\n\t\t\tstripped.append(segment).append('/');\n\t\t}\n\t\t// Remove the trailing slash if the original path didn't have one\n\t\tif (path.charAt(path.length() - 1) != '/') {\n\t\t\tstripped.deleteCharAt(stripped.length() - 1);\n\t\t}\n\t\treturn stripped.toString();\n\t}\n\n\t@Override\n\tpublic @Nullable String getPathInfo() {\n\t\treturn this.stripPaths ? this.strippedPathInfo : super.getPathInfo();\n\t}\n\n\t@Override\n\tpublic @Nullable String getServletPath() {\n\t\treturn this.stripPaths ? this.strippedServletPath : super.getServletPath();\n\t}\n\n\t@Override\n\tpublic RequestDispatcher getRequestDispatcher(String path) {\n\t\treturn this.stripPaths ? new FirewalledRequestAwareRequestDispatcher(path) : super.getRequestDispatcher(path);\n\t}\n\n\t@Override\n\tpublic void reset() {\n\t\tthis.stripPaths = false;\n\t}\n\n\t/**\n\t * Ensures {@link FirewalledRequest#reset()} is called prior to performing a forward.\n\t * It then delegates work to the {@link RequestDispatcher} from the original\n\t * {@link HttpServletRequest}.\n\t *\n\t * @author Rob Winch\n\t */\n\tprivate class FirewalledRequestAwareRequestDispatcher implements RequestDispatcher {\n\n\t\tprivate final String path;\n\n\t\t/**\n\t\t * @param path the {@code path} that will be used to obtain the delegate\n\t\t * {@link RequestDispatcher} from the original {@link HttpServletRequest}.\n\t\t */\n\t\tFirewalledRequestAwareRequestDispatcher(String path) {\n\t\t\tthis.path = path;\n\t\t}\n\n\t\t@Override\n\t\tpublic void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {\n\t\t\treset();\n\t\t\tgetDelegateDispatcher().forward(request, response);\n\t\t}\n\n\t\t@Override\n\t\tpublic void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {\n\t\t\tgetDelegateDispatcher().include(request, response);\n\t\t}\n\n\t\tprivate RequestDispatcher getDelegateDispatcher() {\n\t\t\treturn RequestWrapper.super.getRequestDispatcher(this.path);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * A strict implementation of {@link HttpFirewall} that rejects any suspicious requests\n * with a {@link RequestRejectedException}.\n * </p>\n * <p>\n * The following rules are applied to the firewall:\n * </p>\n * <ul>\n * <li>Rejects HTTP methods that are not allowed. This specified to block\n * <a href=\"https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)\">HTTP Verb\n * tampering and XST attacks</a>. See {@link #setAllowedHttpMethods(Collection)}</li>\n * <li>Rejects URLs that are not normalized to avoid bypassing security constraints. There\n * is no way to disable this as it is considered extremely risky to disable this\n * constraint. A few options to allow this behavior is to normalize the request prior to\n * the firewall or using {@link DefaultHttpFirewall} instead. Please keep in mind that\n * normalizing the request is fragile and why requests are rejected rather than\n * normalized.</li>\n * <li>Rejects URLs that contain characters that are not printable ASCII characters. There\n * is no way to disable this as it is considered extremely risky to disable this\n * constraint.</li>\n * <li>Rejects URLs that contain semicolons. See {@link #setAllowSemicolon(boolean)}</li>\n * <li>Rejects URLs that contain a URL encoded slash. See\n * {@link #setAllowUrlEncodedSlash(boolean)}</li>\n * <li>Rejects URLs that contain a backslash. See {@link #setAllowBackSlash(boolean)}</li>\n * <li>Rejects URLs that contain a null character. See {@link #setAllowNull(boolean)}</li>\n * <li>Rejects URLs that contain a URL encoded percent. See\n * {@link #setAllowUrlEncodedPercent(boolean)}</li>\n * <li>Rejects hosts that are not allowed. See {@link #setAllowedHostnames(Predicate)}\n * </li>\n * <li>Reject headers names that are not allowed. See\n * {@link #setAllowedHeaderNames(Predicate)}</li>\n * <li>Reject headers values that are not allowed. See\n * {@link #setAllowedHeaderValues(Predicate)}</li>\n * <li>Reject parameter names that are not allowed. See\n * {@link #setAllowedParameterNames(Predicate)}</li>\n * <li>Reject parameter values that are not allowed. See\n * {@link #setAllowedParameterValues(Predicate)}</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Eddú Meléndez\n * @author Jinwoo Bae\n * @since 4.2.4\n * @see DefaultHttpFirewall\n */\npublic class StrictHttpFirewall implements HttpFirewall {\n\n\t/**\n\t * Used to specify to {@link #setAllowedHttpMethods(Collection)} that any HTTP method\n\t * should be allowed.\n\t */\n\tprivate static final Set<String> ALLOW_ANY_HTTP_METHOD = Collections.emptySet();\n\n\tprivate static final String ENCODED_PERCENT = \"%25\";\n\n\tprivate static final String PERCENT = \"%\";\n\n\tprivate static final List<String> FORBIDDEN_ENCODED_PERIOD = Collections\n\t\t.unmodifiableList(Arrays.asList(\"%2e\", \"%2E\"));\n\n\tprivate static final List<String> FORBIDDEN_SEMICOLON = Collections\n\t\t.unmodifiableList(Arrays.asList(\";\", \"%3b\", \"%3B\"));\n\n\tprivate static final List<String> FORBIDDEN_FORWARDSLASH = Collections\n\t\t.unmodifiableList(Arrays.asList(\"%2f\", \"%2F\"));\n\n\tprivate static final List<String> FORBIDDEN_DOUBLE_FORWARDSLASH = Collections\n\t\t.unmodifiableList(Arrays.asList(\"//\", \"%2f%2f\", \"%2f%2F\", \"%2F%2f\", \"%2F%2F\"));\n\n\tprivate static final List<String> FORBIDDEN_BACKSLASH = Collections\n\t\t.unmodifiableList(Arrays.asList(\"\\\\\", \"%5c\", \"%5C\"));\n\n\tprivate static final List<String> FORBIDDEN_NULL = Collections.unmodifiableList(Arrays.asList(\"\\0\", \"%00\"));\n\n\tprivate static final List<String> FORBIDDEN_LF = Collections.unmodifiableList(Arrays.asList(\"\\n\", \"%0a\", \"%0A\"));\n\n\tprivate static final List<String> FORBIDDEN_CR = Collections.unmodifiableList(Arrays.asList(\"\\r\", \"%0d\", \"%0D\"));\n\n\tprivate static final List<String> FORBIDDEN_LINE_SEPARATOR = Collections.unmodifiableList(Arrays.asList(\"\\u2028\"));\n\n\tprivate static final List<String> FORBIDDEN_PARAGRAPH_SEPARATOR = Collections\n\t\t.unmodifiableList(Arrays.asList(\"\\u2029\"));\n\n\tprivate Set<String> encodedUrlBlocklist = new HashSet<>();\n\n\tprivate Set<String> decodedUrlBlocklist = new HashSet<>();\n\n\tprivate Set<String> allowedHttpMethods = createDefaultAllowedHttpMethods();\n\n\tprivate Predicate<String> allowedHostnames = (hostname) -> true;\n\n\tprivate static final Pattern ASSIGNED_AND_NOT_ISO_CONTROL_PATTERN = Pattern\n\t\t.compile(\"[\\\\p{IsAssigned}&&[^\\\\p{IsControl}]]*\");\n\n\tprivate static final Predicate<String> ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE = (\n\t\t\ts) -> ASSIGNED_AND_NOT_ISO_CONTROL_PATTERN.matcher(s).matches();\n\n\tprivate static final Pattern HEADER_VALUE_PATTERN = Pattern.compile(\"[\\\\p{IsAssigned}&&[[^\\\\p{IsControl}]||\\\\t]]*\");\n\n\tprivate static final Predicate<String> HEADER_VALUE_PREDICATE = (s) -> HEADER_VALUE_PATTERN.matcher(s).matches();\n\n\tprivate Predicate<String> allowedHeaderNames = ALLOWED_HEADER_NAMES;\n\n\tpublic static final Predicate<String> ALLOWED_HEADER_NAMES = ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE;\n\n\tprivate Predicate<String> allowedHeaderValues = ALLOWED_HEADER_VALUES;\n\n\tpublic static final Predicate<String> ALLOWED_HEADER_VALUES = HEADER_VALUE_PREDICATE;\n\n\tprivate Predicate<String> allowedParameterNames = ALLOWED_PARAMETER_NAMES;\n\n\tpublic static final Predicate<String> ALLOWED_PARAMETER_NAMES = ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE;\n\n\tprivate Predicate<String> allowedParameterValues = ALLOWED_PARAMETER_VALUES;\n\n\tpublic static final Predicate<String> ALLOWED_PARAMETER_VALUES = (value) -> true;\n\n\tpublic StrictHttpFirewall() {\n\t\turlBlocklistsAddAll(FORBIDDEN_SEMICOLON);\n\t\turlBlocklistsAddAll(FORBIDDEN_FORWARDSLASH);\n\t\turlBlocklistsAddAll(FORBIDDEN_DOUBLE_FORWARDSLASH);\n\t\turlBlocklistsAddAll(FORBIDDEN_BACKSLASH);\n\t\turlBlocklistsAddAll(FORBIDDEN_NULL);\n\t\turlBlocklistsAddAll(FORBIDDEN_LF);\n\t\turlBlocklistsAddAll(FORBIDDEN_CR);\n\n\t\tthis.encodedUrlBlocklist.add(ENCODED_PERCENT);\n\t\tthis.encodedUrlBlocklist.addAll(FORBIDDEN_ENCODED_PERIOD);\n\t\tthis.decodedUrlBlocklist.add(PERCENT);\n\t\tthis.decodedUrlBlocklist.addAll(FORBIDDEN_LINE_SEPARATOR);\n\t\tthis.decodedUrlBlocklist.addAll(FORBIDDEN_PARAGRAPH_SEPARATOR);\n\t}\n\n\t/**\n\t * Sets if any HTTP method is allowed. If this set to true, then no validation on the\n\t * HTTP method will be performed. This can open the application up to\n\t * <a href=\"https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)\"> HTTP\n\t * Verb tampering and XST attacks</a>\n\t * @param unsafeAllowAnyHttpMethod if true, disables HTTP method validation, else\n\t * resets back to the defaults. Default is false.\n\t * @since 5.1\n\t * @see #setAllowedHttpMethods(Collection)\n\t */\n\tpublic void setUnsafeAllowAnyHttpMethod(boolean unsafeAllowAnyHttpMethod) {\n\t\tthis.allowedHttpMethods = unsafeAllowAnyHttpMethod ? ALLOW_ANY_HTTP_METHOD : createDefaultAllowedHttpMethods();\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which HTTP methods should be allowed. The default is to allow \"DELETE\",\n\t * \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", and \"PUT\".\n\t * </p>\n\t * @param allowedHttpMethods the case-sensitive collection of HTTP methods that are\n\t * allowed.\n\t * @since 5.1\n\t * @see #setUnsafeAllowAnyHttpMethod(boolean)\n\t */\n\tpublic void setAllowedHttpMethods(Collection<String> allowedHttpMethods) {\n\t\tAssert.notNull(allowedHttpMethods, \"allowedHttpMethods cannot be null\");\n\t\tthis.allowedHttpMethods = (allowedHttpMethods != ALLOW_ANY_HTTP_METHOD) ? new HashSet<>(allowedHttpMethods)\n\t\t\t\t: ALLOW_ANY_HTTP_METHOD;\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if semicolon is allowed in the URL (i.e. matrix variables). The default\n\t * is to disable this behavior because it is a common way of attempting to perform\n\t * <a href=\"https://www.owasp.org/index.php/Reflected_File_Download\">Reflected File\n\t * Download Attacks</a>. It is also the source of many exploits which bypass URL based\n\t * security.\n\t * </p>\n\t * <p>\n\t * For example, the following CVEs are a subset of the issues related to ambiguities\n\t * in the Servlet Specification on how to treat semicolons that led to CVEs:\n\t * </p>\n\t * <ul>\n\t * <li><a href=\"https://pivotal.io/security/cve-2016-5007\">cve-2016-5007</a></li>\n\t * <li><a href=\"https://pivotal.io/security/cve-2016-9879\">cve-2016-9879</a></li>\n\t * <li><a href=\"https://pivotal.io/security/cve-2018-1199\">cve-2018-1199</a></li>\n\t * </ul>\n\t *\n\t * <p>\n\t * If you are wanting to allow semicolons, please reconsider as it is a very common\n\t * source of security bypasses. A few common reasons users want semicolons and\n\t * alternatives are listed below:\n\t * </p>\n\t * <ul>\n\t * <li>Including the JSESSIONID in the path - You should not include session id (or\n\t * any sensitive information) in a URL as it can lead to leaking. Instead use Cookies.\n\t * </li>\n\t * <li>Matrix Variables - Users wanting to leverage Matrix Variables should consider\n\t * using HTTP parameters instead.</li>\n\t * </ul>\n\t * @param allowSemicolon should semicolons be allowed in the URL. Default is false\n\t */\n\tpublic void setAllowSemicolon(boolean allowSemicolon) {\n\t\tif (allowSemicolon) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_SEMICOLON);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_SEMICOLON);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a slash \"/\" that is URL encoded \"%2F\" should be allowed in the path\n\t * or not. The default is to not allow this behavior because it is a common way to\n\t * bypass URL based security.\n\t * </p>\n\t * <p>\n\t * For example, due to ambiguities in the servlet specification, the value is not\n\t * parsed consistently which results in different values in {@code HttpServletRequest}\n\t * path related values which allow bypassing certain security constraints.\n\t * </p>\n\t * @param allowUrlEncodedSlash should a slash \"/\" that is URL encoded \"%2F\" be allowed\n\t * in the path or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedSlash(boolean allowUrlEncodedSlash) {\n\t\tif (allowUrlEncodedSlash) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_FORWARDSLASH);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_FORWARDSLASH);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if double slash \"//\" that is URL encoded \"%2F%2F\" should be allowed in\n\t * the path or not. The default is to not allow.\n\t * </p>\n\t * @param allowUrlEncodedDoubleSlash should a slash \"//\" that is URL encoded \"%2F%2F\"\n\t * be allowed in the path or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedDoubleSlash(boolean allowUrlEncodedDoubleSlash) {\n\t\tif (allowUrlEncodedDoubleSlash) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_DOUBLE_FORWARDSLASH);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_DOUBLE_FORWARDSLASH);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a period \".\" that is URL encoded \"%2E\" should be allowed in the path\n\t * or not. The default is to not allow this behavior because it is a frequent source\n\t * of security exploits.\n\t * </p>\n\t * <p>\n\t * For example, due to ambiguities in the servlet specification a URL encoded period\n\t * might lead to bypassing security constraints through a directory traversal attack.\n\t * This is because the path is not parsed consistently which results in different\n\t * values in {@code HttpServletRequest} path related values which allow bypassing\n\t * certain security constraints.\n\t * </p>\n\t * @param allowUrlEncodedPeriod should a period \".\" that is URL encoded \"%2E\" be\n\t * allowed in the path or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedPeriod(boolean allowUrlEncodedPeriod) {\n\t\tif (allowUrlEncodedPeriod) {\n\t\t\tthis.encodedUrlBlocklist.removeAll(FORBIDDEN_ENCODED_PERIOD);\n\t\t}\n\t\telse {\n\t\t\tthis.encodedUrlBlocklist.addAll(FORBIDDEN_ENCODED_PERIOD);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a backslash \"\\\" or a URL encoded backslash \"%5C\" should be allowed in\n\t * the path or not. The default is not to allow this behavior because it is a frequent\n\t * source of security exploits.\n\t * </p>\n\t * <p>\n\t * For example, due to ambiguities in the servlet specification a URL encoded period\n\t * might lead to bypassing security constraints through a directory traversal attack.\n\t * This is because the path is not parsed consistently which results in different\n\t * values in {@code HttpServletRequest} path related values which allow bypassing\n\t * certain security constraints.\n\t * </p>\n\t * @param allowBackSlash a backslash \"\\\" or a URL encoded backslash \"%5C\" be allowed\n\t * in the path or not. Default is false\n\t */\n\tpublic void setAllowBackSlash(boolean allowBackSlash) {\n\t\tif (allowBackSlash) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_BACKSLASH);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_BACKSLASH);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a null \"\\0\" or a URL encoded nul \"%00\" should be allowed in the path\n\t * or not. The default is not to allow this behavior because it is a frequent source\n\t * of security exploits.\n\t * </p>\n\t * @param allowNull a null \"\\0\" or a URL encoded null \"%00\" be allowed in the path or\n\t * not. Default is false\n\t * @since 5.4\n\t */\n\tpublic void setAllowNull(boolean allowNull) {\n\t\tif (allowNull) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_NULL);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_NULL);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a percent \"%\" that is URL encoded \"%25\" should be allowed in the path\n\t * or not. The default is not to allow this behavior because it is a frequent source\n\t * of security exploits.\n\t * </p>\n\t * <p>\n\t * For example, this can lead to exploits that involve double URL encoding that lead\n\t * to bypassing security constraints.\n\t * </p>\n\t * @param allowUrlEncodedPercent if a percent \"%\" that is URL encoded \"%25\" should be\n\t * allowed in the path or not. Default is false\n\t */\n\tpublic void setAllowUrlEncodedPercent(boolean allowUrlEncodedPercent) {\n\t\tif (allowUrlEncodedPercent) {\n\t\t\tthis.encodedUrlBlocklist.remove(ENCODED_PERCENT);\n\t\t\tthis.decodedUrlBlocklist.remove(PERCENT);\n\t\t}\n\t\telse {\n\t\t\tthis.encodedUrlBlocklist.add(ENCODED_PERCENT);\n\t\t\tthis.decodedUrlBlocklist.add(PERCENT);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if a URL encoded Carriage Return is allowed in the path or not. The\n\t * default is not to allow this behavior because it is a frequent source of security\n\t * exploits.\n\t * @param allowUrlEncodedCarriageReturn if URL encoded Carriage Return is allowed in\n\t * the URL or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedCarriageReturn(boolean allowUrlEncodedCarriageReturn) {\n\t\tif (allowUrlEncodedCarriageReturn) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_CR);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_CR);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if a URL encoded Line Feed is allowed in the path or not. The default is\n\t * not to allow this behavior because it is a frequent source of security exploits.\n\t * @param allowUrlEncodedLineFeed if URL encoded Line Feed is allowed in the URL or\n\t * not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedLineFeed(boolean allowUrlEncodedLineFeed) {\n\t\tif (allowUrlEncodedLineFeed) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_LF);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_LF);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if a URL encoded paragraph separator is allowed in the path or not. The\n\t * default is not to allow this behavior because it is a frequent source of security\n\t * exploits.\n\t * @param allowUrlEncodedParagraphSeparator if URL encoded paragraph separator is\n\t * allowed in the URL or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedParagraphSeparator(boolean allowUrlEncodedParagraphSeparator) {\n\t\tif (allowUrlEncodedParagraphSeparator) {\n\t\t\tthis.decodedUrlBlocklist.removeAll(FORBIDDEN_PARAGRAPH_SEPARATOR);\n\t\t}\n\t\telse {\n\t\t\tthis.decodedUrlBlocklist.addAll(FORBIDDEN_PARAGRAPH_SEPARATOR);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if a URL encoded line separator is allowed in the path or not. The\n\t * default is not to allow this behavior because it is a frequent source of security\n\t * exploits.\n\t * @param allowUrlEncodedLineSeparator if URL encoded line separator is allowed in the\n\t * URL or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedLineSeparator(boolean allowUrlEncodedLineSeparator) {\n\t\tif (allowUrlEncodedLineSeparator) {\n\t\t\tthis.decodedUrlBlocklist.removeAll(FORBIDDEN_LINE_SEPARATOR);\n\t\t}\n\t\telse {\n\t\t\tthis.decodedUrlBlocklist.addAll(FORBIDDEN_LINE_SEPARATOR);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which header names should be allowed. The default is to reject header\n\t * names that contain ISO control characters and characters that are not defined.\n\t * </p>\n\t * @param allowedHeaderNames the predicate for testing header names\n\t * @since 5.4\n\t * @see Character#isISOControl(int)\n\t * @see Character#isDefined(int)\n\t */\n\tpublic void setAllowedHeaderNames(Predicate<String> allowedHeaderNames) {\n\t\tAssert.notNull(allowedHeaderNames, \"allowedHeaderNames cannot be null\");\n\t\tthis.allowedHeaderNames = allowedHeaderNames;\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which header values should be allowed. The default is to reject header\n\t * values that contain ISO control characters and characters that are not defined.\n\t * </p>\n\t * @param allowedHeaderValues the predicate for testing hostnames\n\t * @since 5.4\n\t * @see Character#isISOControl(int)\n\t * @see Character#isDefined(int)\n\t */\n\tpublic void setAllowedHeaderValues(Predicate<String> allowedHeaderValues) {\n\t\tAssert.notNull(allowedHeaderValues, \"allowedHeaderValues cannot be null\");\n\t\tthis.allowedHeaderValues = allowedHeaderValues;\n\t}\n\n\t/**\n\t * Determines which parameter names should be allowed. The default is to reject header\n\t * names that contain ISO control characters and characters that are not defined.\n\t * @param allowedParameterNames the predicate for testing parameter names\n\t * @since 5.4\n\t * @see Character#isISOControl(int)\n\t * @see Character#isDefined(int)\n\t */\n\tpublic void setAllowedParameterNames(Predicate<String> allowedParameterNames) {\n\t\tAssert.notNull(allowedParameterNames, \"allowedParameterNames cannot be null\");\n\t\tthis.allowedParameterNames = allowedParameterNames;\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which parameter values should be allowed. The default is to allow any\n\t * parameter value.\n\t * </p>\n\t * @param allowedParameterValues the predicate for testing parameter values\n\t * @since 5.4\n\t */\n\tpublic void setAllowedParameterValues(Predicate<String> allowedParameterValues) {\n\t\tAssert.notNull(allowedParameterValues, \"allowedParameterValues cannot be null\");\n\t\tthis.allowedParameterValues = allowedParameterValues;\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which hostnames should be allowed. The default is to allow any hostname.\n\t * </p>\n\t * @param allowedHostnames the predicate for testing hostnames\n\t * @since 5.2\n\t */\n\tpublic void setAllowedHostnames(Predicate<String> allowedHostnames) {\n\t\tAssert.notNull(allowedHostnames, \"allowedHostnames cannot be null\");\n\t\tthis.allowedHostnames = allowedHostnames;\n\t}\n\n\tprivate void urlBlocklistsAddAll(Collection<String> values) {\n\t\tthis.encodedUrlBlocklist.addAll(values);\n\t\tthis.decodedUrlBlocklist.addAll(values);\n\t}\n\n\tprivate void urlBlocklistsRemoveAll(Collection<String> values) {\n\t\tthis.encodedUrlBlocklist.removeAll(values);\n\t\tthis.decodedUrlBlocklist.removeAll(values);\n\t}\n\n\t@Override\n\tpublic FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {\n\t\trejectForbiddenHttpMethod(request);\n\t\trejectedBlocklistedUrls(request);\n\t\trejectedUntrustedHosts(request);\n\t\tif (!isNormalized(request)) {\n\t\t\tthrow new RequestRejectedException(\"The request was rejected because the URL was not normalized.\");\n\t\t}\n\t\trejectNonPrintableAsciiCharactersInFieldName(request.getRequestURI(), \"requestURI\");\n\t\treturn new StrictFirewalledRequest(request);\n\t}\n\n\tprivate void rejectNonPrintableAsciiCharactersInFieldName(String toCheck, String propertyName) {\n\t\tif (!containsOnlyPrintableAsciiCharacters(toCheck)) {\n\t\t\tthrow new RequestRejectedException(String\n\t\t\t\t.format(\"The %s was rejected because it can only contain printable ASCII characters.\", propertyName));\n\t\t}\n\t}\n\n\tprivate void rejectForbiddenHttpMethod(HttpServletRequest request) {\n\t\tif (this.allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {\n\t\t\treturn;\n\t\t}\n\t\tif (!this.allowedHttpMethods.contains(request.getMethod())) {\n\t\t\tthrow new RequestRejectedException(\n\t\t\t\t\t\"The request was rejected because the HTTP method \\\"\" + request.getMethod()\n\t\t\t\t\t\t\t+ \"\\\" was not included within the list of allowed HTTP methods \" + this.allowedHttpMethods);\n\t\t}\n\t}\n\n\tprivate void rejectedBlocklistedUrls(HttpServletRequest request) {\n\t\tfor (String forbidden : this.encodedUrlBlocklist) {\n\t\t\tif (encodedUrlContains(request, forbidden)) {\n\t\t\t\tthrow new RequestRejectedException(\n\t\t\t\t\t\t\"The request was rejected because the URL contained a potentially malicious String \\\"\"\n\t\t\t\t\t\t\t\t+ forbidden + \"\\\"\");\n\t\t\t}\n\t\t}\n\t\tfor (String forbidden : this.decodedUrlBlocklist) {\n\t\t\tif (decodedUrlContains(request, forbidden)) {\n\t\t\t\tthrow new RequestRejectedException(\n\t\t\t\t\t\t\"The request was rejected because the URL contained a potentially malicious String \\\"\"\n\t\t\t\t\t\t\t\t+ forbidden + \"\\\"\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void rejectedUntrustedHosts(HttpServletRequest request) {\n\t\tString serverName = request.getServerName();\n\t\tif (serverName != null && !this.allowedHostnames.test(serverName)) {\n\t\t\tthrow new RequestRejectedException(\n\t\t\t\t\t\"The request was rejected because the domain \" + serverName + \" is untrusted.\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic HttpServletResponse getFirewalledResponse(HttpServletResponse response) {\n\t\treturn new FirewalledResponse(response);\n\t}\n\n\tprivate static Set<String> createDefaultAllowedHttpMethods() {\n\t\tSet<String> result = new HashSet<>();\n\t\tresult.add(HttpMethod.DELETE.name());\n\t\tresult.add(HttpMethod.GET.name());\n\t\tresult.add(HttpMethod.HEAD.name());\n\t\tresult.add(HttpMethod.OPTIONS.name());\n\t\tresult.add(HttpMethod.PATCH.name());\n\t\tresult.add(HttpMethod.POST.name());\n\t\tresult.add(HttpMethod.PUT.name());\n\t\treturn result;\n\t}\n\n\tprivate static boolean isNormalized(HttpServletRequest request) {\n\t\tif (!isNormalized(request.getRequestURI())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!isNormalized(request.getContextPath())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!isNormalized(request.getServletPath())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!isNormalized(request.getPathInfo())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean encodedUrlContains(HttpServletRequest request, String value) {\n\t\tif (valueContains(request.getContextPath(), value)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn valueContains(request.getRequestURI(), value);\n\t}\n\n\tprivate static boolean decodedUrlContains(HttpServletRequest request, String value) {\n\t\tif (valueContains(request.getServletPath(), value)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn valueContains(request.getPathInfo(), value);\n\t}\n\n\tprivate static boolean containsOnlyPrintableAsciiCharacters(String uri) {\n\t\tif (uri == null) {\n\t\t\treturn true;\n\t\t}\n\t\tint length = uri.length();\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tchar ch = uri.charAt(i);\n\t\t\tif (ch < '\\u0020' || ch > '\\u007e') {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean valueContains(String value, String contains) {\n\t\treturn value != null && value.contains(contains);\n\t}\n\n\t/**\n\t * Checks whether a path is normalized (doesn't contain path traversal sequences like\n\t * \"./\", \"/../\" or \"/.\")\n\t * @param path the path to test\n\t * @return true if the path doesn't contain any path-traversal character sequences.\n\t */\n\tprivate static boolean isNormalized(String path) {\n\t\tif (path == null) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (int i = path.length(); i > 0;) {\n\t\t\tint slashIndex = path.lastIndexOf('/', i - 1);\n\t\t\tint gap = i - slashIndex;\n\t\t\tif (gap == 2 && path.charAt(slashIndex + 1) == '.') {\n\t\t\t\treturn false; // \".\", \"/./\" or \"/.\"\n\t\t\t}\n\t\t\tif (gap == 3 && path.charAt(slashIndex + 1) == '.' && path.charAt(slashIndex + 2) == '.') {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ti = slashIndex;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Provides the existing encoded url blocklist which can add/remove entries from\n\t * @return the existing encoded url blocklist, never null\n\t */\n\tpublic Set<String> getEncodedUrlBlocklist() {\n\t\treturn this.encodedUrlBlocklist;\n\t}\n\n\t/**\n\t * Provides the existing decoded url blocklist which can add/remove entries from\n\t * @return the existing decoded url blocklist, never null\n\t */\n\tpublic Set<String> getDecodedUrlBlocklist() {\n\t\treturn this.decodedUrlBlocklist;\n\t}\n\n\t/**\n\t * Provides the existing encoded url blocklist which can add/remove entries from\n\t * @return the existing encoded url blocklist, never null\n\t * @deprecated Use {@link #getEncodedUrlBlocklist()} instead\n\t */\n\t@Deprecated\n\tpublic Set<String> getEncodedUrlBlacklist() {\n\t\treturn getEncodedUrlBlocklist();\n\t}\n\n\t/**\n\t * Provides the existing decoded url blocklist which can add/remove entries from\n\t * @return the existing decoded url blocklist, never null\n\t *\n\t */\n\tpublic Set<String> getDecodedUrlBlacklist() {\n\t\treturn getDecodedUrlBlocklist();\n\t}\n\n\t/**\n\t * Strict {@link FirewalledRequest}.\n\t */\n\tprivate class StrictFirewalledRequest extends FirewalledRequest {\n\n\t\tStrictFirewalledRequest(HttpServletRequest request) {\n\t\t\tsuper(request);\n\t\t}\n\n\t\t@Override\n\t\tpublic long getDateHeader(String name) {\n\t\t\tif (name != null) {\n\t\t\t\tvalidateAllowedHeaderName(name);\n\t\t\t}\n\t\t\treturn super.getDateHeader(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getIntHeader(String name) {\n\t\t\tif (name != null) {\n\t\t\t\tvalidateAllowedHeaderName(name);\n\t\t\t}\n\t\t\treturn super.getIntHeader(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getHeader(String name) {\n\t\t\tif (name != null) {\n\t\t\t\tvalidateAllowedHeaderName(name);\n\t\t\t}\n\t\t\tString value = super.getHeader(name);\n\t\t\tif (value != null) {\n\t\t\t\tvalidateAllowedHeaderValue(name, value);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic Enumeration<String> getHeaders(String name) {\n\t\t\tif (name != null) {\n\t\t\t\tvalidateAllowedHeaderName(name);\n\t\t\t}\n\t\t\tEnumeration<String> headers = super.getHeaders(name);\n\t\t\treturn new Enumeration<>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasMoreElements() {\n\t\t\t\t\treturn headers.hasMoreElements();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String nextElement() {\n\t\t\t\t\tString value = headers.nextElement();\n\t\t\t\t\tvalidateAllowedHeaderValue(name, value);\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic Enumeration<String> getHeaderNames() {\n\t\t\tEnumeration<String> names = super.getHeaderNames();\n\t\t\treturn new Enumeration<>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasMoreElements() {\n\t\t\t\t\treturn names.hasMoreElements();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String nextElement() {\n\t\t\t\t\tString headerNames = names.nextElement();\n\t\t\t\t\tvalidateAllowedHeaderName(headerNames);\n\t\t\t\t\treturn headerNames;\n\t\t\t\t}\n\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic String getParameter(String name) {\n\t\t\tif (name != null) {\n\t\t\t\tvalidateAllowedParameterName(name);\n\t\t\t}\n\t\t\tString value = super.getParameter(name);\n\t\t\tif (value != null) {\n\t\t\t\tvalidateAllowedParameterValue(name, value);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String[]> getParameterMap() {\n\t\t\tMap<String, String[]> parameterMap = super.getParameterMap();\n\t\t\tfor (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {\n\t\t\t\tString name = entry.getKey();\n\t\t\t\tString[] values = entry.getValue();\n\t\t\t\tvalidateAllowedParameterName(name);\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\tvalidateAllowedParameterValue(name, value);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parameterMap;\n\t\t}\n\n\t\t@Override\n\t\tpublic Enumeration<String> getParameterNames() {\n\t\t\tEnumeration<String> parameterNames = super.getParameterNames();\n\t\t\treturn new Enumeration<>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean hasMoreElements() {\n\t\t\t\t\treturn parameterNames.hasMoreElements();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String nextElement() {\n\t\t\t\t\tString name = parameterNames.nextElement();\n\t\t\t\t\tvalidateAllowedParameterName(name);\n\t\t\t\t\treturn name;\n\t\t\t\t}\n\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] getParameterValues(String name) {\n\t\t\tif (name != null) {\n\t\t\t\tvalidateAllowedParameterName(name);\n\t\t\t}\n\t\t\tString[] values = super.getParameterValues(name);\n\t\t\tif (values != null) {\n\t\t\t\tfor (String value : values) {\n\t\t\t\t\tvalidateAllowedParameterValue(name, value);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn values;\n\t\t}\n\n\t\tprivate void validateAllowedHeaderName(String headerNames) {\n\t\t\tif (!StrictHttpFirewall.this.allowedHeaderNames.test(headerNames)) {\n\t\t\t\tthrow new RequestRejectedException(\n\t\t\t\t\t\t\"The request was rejected because the header name \\\"\" + headerNames + \"\\\" is not allowed.\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void validateAllowedHeaderValue(String name, String value) {\n\t\t\tif (!StrictHttpFirewall.this.allowedHeaderValues.test(value)) {\n\t\t\t\tthrow new RequestRejectedException(\"The request was rejected because the header: \\\"\" + name\n\t\t\t\t\t\t+ \" \\\" has a value \\\"\" + value + \"\\\" that is not allowed.\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void validateAllowedParameterName(String name) {\n\t\t\tif (!StrictHttpFirewall.this.allowedParameterNames.test(name)) {\n\t\t\t\tthrow new RequestRejectedException(\n\t\t\t\t\t\t\"The request was rejected because the parameter name \\\"\" + name + \"\\\" is not allowed.\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void validateAllowedParameterValue(String name, String value) {\n\t\t\tif (!StrictHttpFirewall.this.allowedParameterValues.test(value)) {\n\t\t\t\tthrow new RequestRejectedException(\"The request was rejected because the parameter: \\\"\" + name\n\t\t\t\t\t\t+ \" \\\" has a value \\\"\" + value + \"\\\" that is not allowed.\");\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void reset() {\n\t\t}\n\n\t};\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/firewall/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * APIs for web security firewall support.\n */\n@NullMarked\npackage org.springframework.security.web.firewall;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/Header.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.util.Assert;\n\n/**\n * Represents a Header to be added to the {@link HttpServletResponse}\n */\npublic final class Header {\n\n\tprivate final String headerName;\n\n\tprivate final List<String> headerValues;\n\n\t/**\n\t * Creates a new instance\n\t * @param headerName the name of the header\n\t * @param headerValues the values of the header\n\t */\n\tpublic Header(String headerName, String... headerValues) {\n\t\tAssert.hasText(headerName, \"headerName is required\");\n\t\tAssert.notEmpty(headerValues, \"headerValues cannot be null or empty\");\n\t\tAssert.noNullElements(headerValues, \"headerValues cannot contain null values\");\n\t\tthis.headerName = headerName;\n\t\tthis.headerValues = Arrays.asList(headerValues);\n\t}\n\n\t/**\n\t * Gets the name of the header. Cannot be <code>null</code>.\n\t * @return the name of the header.\n\t */\n\tpublic String getName() {\n\t\treturn this.headerName;\n\t}\n\n\t/**\n\t * Gets the values of the header. Cannot be null, empty, or contain null values.\n\t * @return the values of the header\n\t */\n\tpublic List<String> getValues() {\n\t\treturn this.headerValues;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tHeader other = (Header) obj;\n\t\tif (!this.headerName.equals(other.headerName)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.headerValues.equals(other.headerValues);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.headerName.hashCode() + this.headerValues.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Header [name: \" + this.headerName + \", values: \" + this.headerValues + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/HeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * Contract for writing headers to a {@link HttpServletResponse}\n *\n * @author Marten Deinum\n * @author Rob Winch\n * @since 3.2\n * @see HeaderWriterFilter\n */\npublic interface HeaderWriter {\n\n\t/**\n\t * Create a {@code Header} instance.\n\t * @param request the request\n\t * @param response the response\n\t */\n\tvoid writeHeaders(HttpServletRequest request, HttpServletResponse response);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/HeaderWriterFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.RequestDispatcher;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.util.OnCommittedResponseWrapper;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Filter implementation to add headers to the current response. Can be useful to add\n * certain headers which enable browser protection. Like X-Frame-Options, X-XSS-Protection\n * and X-Content-Type-Options.\n *\n * @author Marten Deinum\n * @author Josh Cummings\n * @author Ankur Pathak\n * @since 3.2\n */\npublic class HeaderWriterFilter extends OncePerRequestFilter {\n\n\t/**\n\t * The {@link HeaderWriter} to write headers to the response.\n\t * {@see CompositeHeaderWriter}\n\t */\n\tprivate final List<HeaderWriter> headerWriters;\n\n\t/**\n\t * Indicates whether to write the headers at the beginning of the request.\n\t */\n\tprivate boolean shouldWriteHeadersEagerly = false;\n\n\t/**\n\t * Creates a new instance.\n\t * @param headerWriters the {@link HeaderWriter} instances to write out headers to the\n\t * {@link HttpServletResponse}.\n\t */\n\tpublic HeaderWriterFilter(List<HeaderWriter> headerWriters) {\n\t\tAssert.notEmpty(headerWriters, \"headerWriters cannot be null or empty\");\n\t\tthis.headerWriters = headerWriters;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (this.shouldWriteHeadersEagerly) {\n\t\t\tdoHeadersBefore(request, response, filterChain);\n\t\t}\n\t\telse {\n\t\t\tdoHeadersAfter(request, response, filterChain);\n\t\t}\n\t}\n\n\tprivate void doHeadersBefore(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows IOException, ServletException {\n\t\twriteHeaders(request, response);\n\t\tfilterChain.doFilter(request, response);\n\t}\n\n\tprivate void doHeadersAfter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows IOException, ServletException {\n\t\tHeaderWriterResponse headerWriterResponse = new HeaderWriterResponse(request, response);\n\t\tHeaderWriterRequest headerWriterRequest = new HeaderWriterRequest(request, headerWriterResponse);\n\t\ttry {\n\t\t\tfilterChain.doFilter(headerWriterRequest, headerWriterResponse);\n\t\t}\n\t\tfinally {\n\t\t\theaderWriterResponse.writeHeaders();\n\t\t}\n\t}\n\n\tvoid writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tfor (HeaderWriter writer : this.headerWriters) {\n\t\t\twriter.writeHeaders(request, response);\n\t\t}\n\t}\n\n\t/**\n\t * Allow writing headers at the beginning of the request.\n\t * @param shouldWriteHeadersEagerly boolean to allow writing headers at the beginning\n\t * of the request.\n\t * @since 5.2\n\t */\n\tpublic void setShouldWriteHeadersEagerly(boolean shouldWriteHeadersEagerly) {\n\t\tthis.shouldWriteHeadersEagerly = shouldWriteHeadersEagerly;\n\t}\n\n\tclass HeaderWriterResponse extends OnCommittedResponseWrapper {\n\n\t\tprivate final HttpServletRequest request;\n\n\t\tHeaderWriterResponse(HttpServletRequest request, HttpServletResponse response) {\n\t\t\tsuper(response);\n\t\t\tthis.request = request;\n\t\t}\n\n\t\t@Override\n\t\tprotected void onResponseCommitted() {\n\t\t\twriteHeaders();\n\t\t\tthis.disableOnResponseCommitted();\n\t\t}\n\n\t\tprotected void writeHeaders() {\n\t\t\tif (isDisableOnResponseCommitted()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tHeaderWriterFilter.this.writeHeaders(this.request, getHttpResponse());\n\t\t}\n\n\t\tprivate HttpServletResponse getHttpResponse() {\n\t\t\treturn (HttpServletResponse) getResponse();\n\t\t}\n\n\t}\n\n\tstatic class HeaderWriterRequest extends HttpServletRequestWrapper {\n\n\t\tprivate final HeaderWriterResponse response;\n\n\t\tHeaderWriterRequest(HttpServletRequest request, HeaderWriterResponse response) {\n\t\t\tsuper(request);\n\t\t\tthis.response = response;\n\t\t}\n\n\t\t@Override\n\t\tpublic RequestDispatcher getRequestDispatcher(String path) {\n\t\t\treturn new HeaderWriterRequestDispatcher(super.getRequestDispatcher(path), this.response);\n\t\t}\n\n\t}\n\n\tstatic class HeaderWriterRequestDispatcher implements RequestDispatcher {\n\n\t\tprivate final RequestDispatcher delegate;\n\n\t\tprivate final HeaderWriterResponse response;\n\n\t\tHeaderWriterRequestDispatcher(RequestDispatcher delegate, HeaderWriterResponse response) {\n\t\t\tthis.delegate = delegate;\n\t\t\tthis.response = response;\n\t\t}\n\n\t\t@Override\n\t\tpublic void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {\n\t\t\tthis.delegate.forward(request, response);\n\t\t}\n\n\t\t@Override\n\t\tpublic void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {\n\t\t\tthis.response.onResponseCommitted();\n\t\t\tthis.delegate.include(request, response);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * APIs for writing security HTTP Headers.\n */\n@NullMarked\npackage org.springframework.security.web.header;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/CacheControlHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.header.Header;\nimport org.springframework.security.web.header.HeaderWriter;\n\n/**\n * Inserts headers to prevent caching if no cache control headers have been specified.\n * Specifically it adds the following headers:\n * <ul>\n * <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>\n * <li>Pragma: no-cache</li>\n * <li>Expires: 0</li>\n * </ul>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class CacheControlHeadersWriter implements HeaderWriter {\n\n\tprivate static final String EXPIRES = \"Expires\";\n\n\tprivate static final String PRAGMA = \"Pragma\";\n\n\tprivate static final String CACHE_CONTROL = \"Cache-Control\";\n\n\tprivate final HeaderWriter delegate;\n\n\t/**\n\t * Creates a new instance\n\t */\n\tpublic CacheControlHeadersWriter() {\n\t\tthis.delegate = new StaticHeadersWriter(createHeaders());\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (hasHeader(response, CACHE_CONTROL) || hasHeader(response, EXPIRES) || hasHeader(response, PRAGMA)\n\t\t\t\t|| response.getStatus() == HttpStatus.NOT_MODIFIED.value()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.delegate.writeHeaders(request, response);\n\t}\n\n\tprivate boolean hasHeader(HttpServletResponse response, String headerName) {\n\t\treturn response.getHeader(headerName) != null;\n\t}\n\n\tprivate static List<Header> createHeaders() {\n\t\tList<Header> headers = new ArrayList<>(3);\n\t\theaders.add(new Header(CACHE_CONTROL, \"no-cache, no-store, max-age=0, must-revalidate\"));\n\t\theaders.add(new Header(PRAGMA, \"no-cache\"));\n\t\theaders.add(new Header(EXPIRES, \"0\"));\n\t\treturn headers;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/ClearSiteDataHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Provides support for <a href=\"https://w3c.github.io/webappsec-clear-site-data/\">Clear\n * Site Data</a>.\n *\n * <p>\n * Developers may instruct a user agent to clear various types of relevant data by\n * delivering a Clear-Site-Data HTTP response header in response to a request.\n * <p>\n *\n * <p>\n * Due to <a href=\"https://w3c.github.io/webappsec-clear-site-data/#incomplete\">Incomplete\n * Clearing</a> section the header is only applied if the request is secure.\n * </p>\n *\n * @author Rafiullah Hamedy\n * @since 5.2\n */\npublic final class ClearSiteDataHeaderWriter implements HeaderWriter {\n\n\tprivate static final String CLEAR_SITE_DATA_HEADER = \"Clear-Site-Data\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RequestMatcher requestMatcher;\n\n\tprivate String headerValue;\n\n\t/**\n\t * <p>\n\t * Creates a new instance of {@link ClearSiteDataHeaderWriter} with given sources. The\n\t * constructor also initializes <b>requestMatcher</b> with a new instance of\n\t * <b>SecureRequestMatcher</b> to ensure that header is only applied if and when the\n\t * request is secure as per the <b>Incomplete Clearing</b> section.\n\t * </p>\n\t * @param directives (i.e. \"cache\", \"cookies\", \"storage\", \"executionContexts\" or \"*\")\n\t * @throws IllegalArgumentException if sources is null or empty.\n\t */\n\tpublic ClearSiteDataHeaderWriter(Directive... directives) {\n\t\tAssert.notEmpty(directives, \"directives cannot be empty or null\");\n\t\tthis.requestMatcher = new SecureRequestMatcher();\n\t\tthis.headerValue = transformToHeaderValue(directives);\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.requestMatcher.matches(request)) {\n\t\t\tif (!response.containsHeader(CLEAR_SITE_DATA_HEADER)) {\n\t\t\t\tresponse.setHeader(CLEAR_SITE_DATA_HEADER, this.headerValue);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.logger.debug(\n\t\t\t\tLogMessage.format(\"Not injecting Clear-Site-Data header since it did not match the requestMatcher %s\",\n\t\t\t\t\t\tthis.requestMatcher));\n\t}\n\n\tprivate String transformToHeaderValue(Directive... directives) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 0; i < directives.length - 1; i++) {\n\t\t\tsb.append(directives[i].headerValue).append(\", \");\n\t\t}\n\t\tsb.append(directives[directives.length - 1].headerValue);\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getName() + \" [headerValue=\" + this.headerValue + \"]\";\n\t}\n\n\t/**\n\t * Represents the directive values expected by the {@link ClearSiteDataHeaderWriter}.\n\t */\n\tpublic enum Directive {\n\n\t\tCACHE(\"cache\"),\n\n\t\tCOOKIES(\"cookies\"),\n\n\t\tSTORAGE(\"storage\"),\n\n\t\tEXECUTION_CONTEXTS(\"executionContexts\"),\n\n\t\tALL(\"*\");\n\n\t\tprivate final String headerValue;\n\n\t\tDirective(String headerValue) {\n\t\t\tthis.headerValue = \"\\\"\" + headerValue + \"\\\"\";\n\t\t}\n\n\t\tpublic String getHeaderValue() {\n\t\t\treturn this.headerValue;\n\t\t}\n\n\t}\n\n\tprivate static final class SecureRequestMatcher implements RequestMatcher {\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn request.isSecure();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Is Secure\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/CompositeHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link HeaderWriter} that delegates to several other {@link HeaderWriter}s.\n *\n * @author Ankur Pathak\n * @since 5.2\n */\npublic class CompositeHeaderWriter implements HeaderWriter {\n\n\tprivate final List<HeaderWriter> headerWriters;\n\n\t/**\n\t * Creates a new instance.\n\t * @param headerWriters the {@link HeaderWriter} instances to write out headers to the\n\t * {@link HttpServletResponse}.\n\t */\n\tpublic CompositeHeaderWriter(List<HeaderWriter> headerWriters) {\n\t\tAssert.notEmpty(headerWriters, \"headerWriters cannot be empty\");\n\t\tthis.headerWriters = headerWriters;\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tthis.headerWriters.forEach((headerWriter) -> headerWriter.writeHeaders(request, response));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/ContentSecurityPolicyHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Provides support for <a href=\"https://www.w3.org/TR/CSP2/\">Content Security Policy\n * (CSP) Level 2</a>.\n * </p>\n *\n * <p>\n * CSP provides a mechanism for web applications to mitigate content injection\n * vulnerabilities, such as cross-site scripting (XSS). CSP is a declarative policy that\n * allows web application authors to inform the client (user-agent) about the sources from\n * which the application expects to load resources.\n * </p>\n *\n * <p>\n * For example, a web application can declare that it only expects to load script from\n * specific, trusted sources. This declaration allows the client to detect and block\n * malicious scripts injected into the application by an attacker.\n * </p>\n *\n * <p>\n * A declaration of a security policy contains a set of security policy directives (for\n * example, script-src and object-src), each responsible for declaring the restrictions\n * for a particular resource type. The list of directives defined can be found at\n * <a href=\"https://www.w3.org/TR/CSP2/#directives\">Directives</a>.\n * </p>\n *\n * <p>\n * Each directive has a name and value. For detailed syntax on writing security policies,\n * see <a href=\"https://www.w3.org/TR/CSP2/#syntax-and-algorithms\">Syntax and\n * Algorithms</a>.\n * </p>\n *\n * <p>\n * This implementation of {@link HeaderWriter} writes one of the following headers:\n * </p>\n * <ul>\n * <li>Content-Security-Policy</li>\n * <li>Content-Security-Policy-Report-Only</li>\n * </ul>\n *\n * <p>\n * By default, the Content-Security-Policy header is included in the response. However,\n * calling {@link #setReportOnly(boolean)} with {@code true} will include the\n * Content-Security-Policy-Report-Only header in the response. <strong>NOTE:</strong> The\n * supplied security policy directive(s) will be used for whichever header is enabled\n * (included).\n * </p>\n *\n * <p>\n * <strong> CSP is not intended as a first line of defense against content injection\n * vulnerabilities. Instead, CSP is used to reduce the harm caused by content injection\n * attacks. As a first line of defense against content injection, web application authors\n * should validate their input and encode their output. </strong>\n * </p>\n *\n * @author Joe Grandja\n * @author Ankur Pathak\n * @since 4.1\n */\npublic final class ContentSecurityPolicyHeaderWriter implements HeaderWriter {\n\n\tprivate static final String CONTENT_SECURITY_POLICY_HEADER = \"Content-Security-Policy\";\n\n\tprivate static final String CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER = \"Content-Security-Policy-Report-Only\";\n\n\tprivate static final String DEFAULT_SRC_SELF_POLICY = \"default-src 'self'\";\n\n\tprivate String policyDirectives;\n\n\tprivate boolean reportOnly;\n\n\t/**\n\t * Creates a new instance. Default value: default-src 'self'\n\t */\n\tpublic ContentSecurityPolicyHeaderWriter() {\n\t\tsetPolicyDirectives(DEFAULT_SRC_SELF_POLICY);\n\t\tthis.reportOnly = false;\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param policyDirectives maps to {@link #setPolicyDirectives(String)}\n\t * @throws IllegalArgumentException if policyDirectives is null or empty\n\t */\n\tpublic ContentSecurityPolicyHeaderWriter(String policyDirectives) {\n\t\tsetPolicyDirectives(policyDirectives);\n\t\tthis.reportOnly = false;\n\t}\n\n\t/**\n\t * @see org.springframework.security.web.header.HeaderWriter#writeHeaders(jakarta.servlet.http.HttpServletRequest,\n\t * jakarta.servlet.http.HttpServletResponse)\n\t */\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tString headerName = (!this.reportOnly) ? CONTENT_SECURITY_POLICY_HEADER\n\t\t\t\t: CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER;\n\t\tif (!response.containsHeader(headerName)) {\n\t\t\tresponse.setHeader(headerName, this.policyDirectives);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the security policy directive(s) to be used in the response header.\n\t * @param policyDirectives the security policy directive(s)\n\t * @throws IllegalArgumentException if policyDirectives is null or empty\n\t */\n\tpublic void setPolicyDirectives(String policyDirectives) {\n\t\tAssert.hasLength(policyDirectives, \"policyDirectives cannot be null or empty\");\n\t\tthis.policyDirectives = policyDirectives;\n\t}\n\n\t/**\n\t * If true, includes the Content-Security-Policy-Report-Only header in the response,\n\t * otherwise, defaults to the Content-Security-Policy header.\n\t * @param reportOnly set to true for reporting policy violations only\n\t */\n\tpublic void setReportOnly(boolean reportOnly) {\n\t\tthis.reportOnly = reportOnly;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getName() + \" [policyDirectives=\" + this.policyDirectives + \"; reportOnly=\" + this.reportOnly\n\t\t\t\t+ \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/CrossOriginEmbedderPolicyHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * Inserts Cross-Origin-Embedder-Policy header.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @see <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy\">\n * Cross-Origin-Embedder-Policy</a>\n */\npublic final class CrossOriginEmbedderPolicyHeaderWriter implements HeaderWriter {\n\n\tprivate static final String EMBEDDER_POLICY = \"Cross-Origin-Embedder-Policy\";\n\n\tprivate @Nullable CrossOriginEmbedderPolicy policy;\n\n\t/**\n\t * Sets the {@link CrossOriginEmbedderPolicy} value to be used in the\n\t * {@code Cross-Origin-Embedder-Policy} header\n\t * @param embedderPolicy the {@link CrossOriginEmbedderPolicy} to use\n\t */\n\tpublic void setPolicy(CrossOriginEmbedderPolicy embedderPolicy) {\n\t\tAssert.notNull(embedderPolicy, \"embedderPolicy cannot be null\");\n\t\tthis.policy = embedderPolicy;\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.policy != null && !response.containsHeader(EMBEDDER_POLICY)) {\n\t\t\tresponse.addHeader(EMBEDDER_POLICY, this.policy.getPolicy());\n\t\t}\n\t}\n\n\tpublic enum CrossOriginEmbedderPolicy {\n\n\t\tUNSAFE_NONE(\"unsafe-none\"),\n\n\t\tREQUIRE_CORP(\"require-corp\"),\n\n\t\tCREDENTIALLESS(\"credentialless\");\n\n\t\tprivate final String policy;\n\n\t\tCrossOriginEmbedderPolicy(String policy) {\n\t\t\tthis.policy = policy;\n\t\t}\n\n\t\tpublic String getPolicy() {\n\t\t\treturn this.policy;\n\t\t}\n\n\t\tpublic static @Nullable CrossOriginEmbedderPolicy from(String embedderPolicy) {\n\t\t\tfor (CrossOriginEmbedderPolicy policy : values()) {\n\t\t\t\tif (policy.getPolicy().equals(embedderPolicy)) {\n\t\t\t\t\treturn policy;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/CrossOriginOpenerPolicyHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * Inserts the Cross-Origin-Opener-Policy header\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @see <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy\">\n * Cross-Origin-Opener-Policy</a>\n */\npublic final class CrossOriginOpenerPolicyHeaderWriter implements HeaderWriter {\n\n\tprivate static final String OPENER_POLICY = \"Cross-Origin-Opener-Policy\";\n\n\tprivate @Nullable CrossOriginOpenerPolicy policy;\n\n\t/**\n\t * Sets the {@link CrossOriginOpenerPolicy} value to be used in the\n\t * {@code Cross-Origin-Opener-Policy} header\n\t * @param openerPolicy the {@link CrossOriginOpenerPolicy} to use\n\t */\n\tpublic void setPolicy(CrossOriginOpenerPolicy openerPolicy) {\n\t\tAssert.notNull(openerPolicy, \"openerPolicy cannot be null\");\n\t\tthis.policy = openerPolicy;\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.policy != null && !response.containsHeader(OPENER_POLICY)) {\n\t\t\tresponse.addHeader(OPENER_POLICY, this.policy.getPolicy());\n\t\t}\n\t}\n\n\tpublic enum CrossOriginOpenerPolicy {\n\n\t\tUNSAFE_NONE(\"unsafe-none\"),\n\n\t\tSAME_ORIGIN_ALLOW_POPUPS(\"same-origin-allow-popups\"),\n\n\t\tSAME_ORIGIN(\"same-origin\");\n\n\t\tprivate final String policy;\n\n\t\tCrossOriginOpenerPolicy(String policy) {\n\t\t\tthis.policy = policy;\n\t\t}\n\n\t\tpublic String getPolicy() {\n\t\t\treturn this.policy;\n\t\t}\n\n\t\tpublic static @Nullable CrossOriginOpenerPolicy from(String openerPolicy) {\n\t\t\tfor (CrossOriginOpenerPolicy policy : values()) {\n\t\t\t\tif (policy.getPolicy().equals(openerPolicy)) {\n\t\t\t\t\treturn policy;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/CrossOriginResourcePolicyHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * Inserts Cross-Origin-Resource-Policy header\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @see <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy\">\n * Cross-Origin-Resource-Policy</a>\n */\npublic final class CrossOriginResourcePolicyHeaderWriter implements HeaderWriter {\n\n\tprivate static final String RESOURCE_POLICY = \"Cross-Origin-Resource-Policy\";\n\n\tprivate @Nullable CrossOriginResourcePolicy policy;\n\n\t/**\n\t * Sets the {@link CrossOriginResourcePolicy} value to be used in the\n\t * {@code Cross-Origin-Resource-Policy} header\n\t * @param resourcePolicy the {@link CrossOriginResourcePolicy} to use\n\t */\n\tpublic void setPolicy(CrossOriginResourcePolicy resourcePolicy) {\n\t\tAssert.notNull(resourcePolicy, \"resourcePolicy cannot be null\");\n\t\tthis.policy = resourcePolicy;\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.policy != null && !response.containsHeader(RESOURCE_POLICY)) {\n\t\t\tresponse.addHeader(RESOURCE_POLICY, this.policy.getPolicy());\n\t\t}\n\t}\n\n\tpublic enum CrossOriginResourcePolicy {\n\n\t\tSAME_SITE(\"same-site\"),\n\n\t\tSAME_ORIGIN(\"same-origin\"),\n\n\t\tCROSS_ORIGIN(\"cross-origin\");\n\n\t\tprivate final String policy;\n\n\t\tCrossOriginResourcePolicy(String policy) {\n\t\t\tthis.policy = policy;\n\t\t}\n\n\t\tpublic String getPolicy() {\n\t\t\treturn this.policy;\n\t\t}\n\n\t\tpublic static @Nullable CrossOriginResourcePolicy from(String resourcePolicy) {\n\t\t\tfor (CrossOriginResourcePolicy policy : values()) {\n\t\t\t\tif (policy.getPolicy().equals(resourcePolicy)) {\n\t\t\t\t\treturn policy;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/DelegatingRequestMatcherHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Delegates to the provided {@link HeaderWriter} when\n * {@link RequestMatcher#matches(HttpServletRequest)} returns true.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class DelegatingRequestMatcherHeaderWriter implements HeaderWriter {\n\n\tprivate final RequestMatcher requestMatcher;\n\n\tprivate final HeaderWriter delegateHeaderWriter;\n\n\t/**\n\t * Creates a new instance\n\t * @param requestMatcher the {@link RequestMatcher} to use. If returns true, the\n\t * delegateHeaderWriter will be invoked.\n\t * @param delegateHeaderWriter the {@link HeaderWriter} to invoke if the\n\t * {@link RequestMatcher} returns true.\n\t */\n\tpublic DelegatingRequestMatcherHeaderWriter(RequestMatcher requestMatcher, HeaderWriter delegateHeaderWriter) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tAssert.notNull(delegateHeaderWriter, \"delegateHeaderWriter cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t\tthis.delegateHeaderWriter = delegateHeaderWriter;\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.requestMatcher.matches(request)) {\n\t\t\tthis.delegateHeaderWriter.writeHeaders(request, response);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getName() + \" [requestMatcher=\" + this.requestMatcher + \", delegateHeaderWriter=\"\n\t\t\t\t+ this.delegateHeaderWriter + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/FeaturePolicyHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * Provides support for <a href=\"https://wicg.github.io/feature-policy/\">Feature\n * Policy</a>.\n * <p>\n * Feature Policy allows web developers to selectively enable, disable, and modify the\n * behavior of certain APIs and web features in the browser.\n * <p>\n * A declaration of a feature policy contains a set of security policy directives, each\n * responsible for declaring the restrictions for a particular feature type.\n *\n * @author Vedran Pavic\n * @author Ankur Pathak\n * @since 5.1\n */\npublic final class FeaturePolicyHeaderWriter implements HeaderWriter {\n\n\tprivate static final String FEATURE_POLICY_HEADER = \"Feature-Policy\";\n\n\tprivate String policyDirectives;\n\n\t/**\n\t * Create a new instance of {@link FeaturePolicyHeaderWriter} with supplied security\n\t * policy directive(s).\n\t * @param policyDirectives the security policy directive(s)\n\t * @throws IllegalArgumentException if policyDirectives is {@code null} or empty\n\t */\n\tpublic FeaturePolicyHeaderWriter(String policyDirectives) {\n\t\tsetPolicyDirectives(policyDirectives);\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (!response.containsHeader(FEATURE_POLICY_HEADER)) {\n\t\t\tresponse.setHeader(FEATURE_POLICY_HEADER, this.policyDirectives);\n\t\t}\n\t}\n\n\t/**\n\t * Set the security policy directive(s) to be used in the response header.\n\t * @param policyDirectives the security policy directive(s)\n\t * @throws IllegalArgumentException if policyDirectives is {@code null} or empty\n\t */\n\tpublic void setPolicyDirectives(String policyDirectives) {\n\t\tAssert.hasLength(policyDirectives, \"policyDirectives must not be null or empty\");\n\t\tthis.policyDirectives = policyDirectives;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getName() + \" [policyDirectives=\" + this.policyDirectives + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/HpkpHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Provides support for <a href=\"https://tools.ietf.org/html/rfc7469\">HTTP Public Key\n * Pinning (HPKP)</a>.\n *\n * <p>\n * Since <a href=\"https://tools.ietf.org/html/rfc7469#section-4.1\">Section 4.1</a> states\n * that a value on the order of 60 days (5,184,000 seconds) may be considered a good\n * balance, we use this value as the default. This can be customized using\n * {@link #setMaxAgeInSeconds(long)}.\n * </p>\n *\n * <p>\n * Because <a href=\"https://tools.ietf.org/html/rfc7469#appendix-B\">Appendix B</a>\n * recommends that operators should first deploy public key pinning by using the\n * report-only mode, we opted to use this mode as default. This can be customized using\n * {@link #setReportOnly(boolean)}.\n * </p>\n *\n * <p>\n * Since we need to validate a certificate chain, the \"Public-Key-Pins\" or\n * \"Public-Key-Pins-Report-Only\" header will only be added when\n * {@link HttpServletRequest#isSecure()} returns {@code true}.\n * </p>\n *\n * <p>\n * To set the pins you first need to extract the public key information from your\n * certificate or key file and encode them using Base64. The following commands will help\n * you extract the Base64 encoded information from a key file, a certificate signing\n * request, or a certificate.\n *\n * <pre>\n * openssl rsa -in my-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64\n *\n * openssl req -in my-signing-request.csr -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64\n *\n * openssl x509 -in my-certificate.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64\n * </pre>\n *\n *\n * The following command will extract the Base64 encoded information for a website.\n *\n * <pre>\n * openssl s_client -servername www.example.com -connect www.example.com:443 | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64\n * </pre>\n * </p>\n *\n * <p>\n * Some examples:\n *\n * <pre>\n * Public-Key-Pins: max-age=3000;\n * \t\tpin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";\n * \t\tpin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"\n *\n * Public-Key-Pins: max-age=5184000;\n * \t\tpin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\";\n * \t\tpin-sha256=\"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=\"\n *\n * Public-Key-Pins: max-age=5184000;\n * \t\tpin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\";\n * \t\tpin-sha256=\"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=\";\n * \t\treport-uri=\"https://example.com/pkp-report\"\n *\n * Public-Key-Pins-Report-Only: max-age=5184000;\n * \t\tpin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\";\n * \t\tpin-sha256=\"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=\";\n * \t\treport-uri=\"https://other.example.net/pkp-report\"\n *\n * Public-Key-Pins: max-age=5184000;\n * \t\tpin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";\n * \t\tpin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\";\n * \t\tpin-sha256=\"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=\";\n * \t\tincludeSubDomains\n * </pre>\n * </p>\n *\n * @author Tim Ysewyn\n * @author Ankur Pathak\n * @since 4.1\n * @deprecated see <a href=\n * \"https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning\">Certificate\n * and Public Key Pinning</a> for more context\n */\n@Deprecated\npublic final class HpkpHeaderWriter implements HeaderWriter {\n\n\tprivate static final long DEFAULT_MAX_AGE_SECONDS = 5184000;\n\n\tprivate static final String HPKP_HEADER_NAME = \"Public-Key-Pins\";\n\n\tprivate static final String HPKP_RO_HEADER_NAME = \"Public-Key-Pins-Report-Only\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final RequestMatcher requestMatcher = new SecureRequestMatcher();\n\n\tprivate Map<String, String> pins = new LinkedHashMap<>();\n\n\tprivate long maxAgeInSeconds;\n\n\tprivate boolean includeSubDomains;\n\n\tprivate boolean reportOnly;\n\n\tprivate URI reportUri;\n\n\tprivate String hpkpHeaderValue;\n\n\t/**\n\t * Creates a new instance\n\t * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}\n\t * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}\n\t * @param reportOnly maps to {@link #setReportOnly(boolean)}\n\t */\n\tpublic HpkpHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains, boolean reportOnly) {\n\t\tthis.maxAgeInSeconds = maxAgeInSeconds;\n\t\tthis.includeSubDomains = includeSubDomains;\n\t\tthis.reportOnly = reportOnly;\n\t\tupdateHpkpHeaderValue();\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}\n\t * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}\n\t */\n\tpublic HpkpHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains) {\n\t\tthis(maxAgeInSeconds, includeSubDomains, true);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}\n\t */\n\tpublic HpkpHeaderWriter(long maxAgeInSeconds) {\n\t\tthis(maxAgeInSeconds, false);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t */\n\tpublic HpkpHeaderWriter() {\n\t\tthis(DEFAULT_MAX_AGE_SECONDS);\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tthis.logger.debug(\"Not injecting HPKP header since it wasn't a secure connection\");\n\t\t\treturn;\n\t\t}\n\t\tif (this.pins.isEmpty()) {\n\t\t\tthis.logger.debug(\"Not injecting HPKP header since there aren't any pins\");\n\t\t\treturn;\n\t\t}\n\t\tString headerName = (this.reportOnly) ? HPKP_RO_HEADER_NAME : HPKP_HEADER_NAME;\n\t\tif (!response.containsHeader(headerName)) {\n\t\t\tresponse.setHeader(headerName, this.hpkpHeaderValue);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the value for the pin- directive of the Public-Key-Pins header.\n\t * </p>\n\t *\n\t * <p>\n\t * The pin directive specifies a way for web host operators to indicate a\n\t * cryptographic identity that should be bound to a given web host. See\n\t * <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.1\">Section 2.1.1</a> for\n\t * additional details.\n\t * </p>\n\t *\n\t * <p>\n\t * To get a pin of\n\t *\n\t * Public-Key-Pins: pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";\n\t * pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"\n\t *\n\t * Use\n\t *\n\t * <code>\n\t * Map&lt;String, String&gt; pins = new HashMap&lt;String, String&gt;();\n\t * pins.put(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\");\n\t * pins.put(\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\", \"sha256\");\n\t * </code>\n\t * </p>\n\t * @param pins the map of base64-encoded SPKI fingerprint &amp; cryptographic hash\n\t * algorithm pairs.\n\t * @throws IllegalArgumentException if pins is null\n\t */\n\tpublic void setPins(Map<String, String> pins) {\n\t\tAssert.notNull(pins, \"pins cannot be null\");\n\t\tthis.pins = pins;\n\t\tupdateHpkpHeaderValue();\n\t}\n\n\t/**\n\t * <p>\n\t * Adds a list of SHA256 hashed pins for the pin- directive of the Public-Key-Pins\n\t * header.\n\t * </p>\n\t *\n\t * <p>\n\t * The pin directive specifies a way for web host operators to indicate a\n\t * cryptographic identity that should be bound to a given web host. See\n\t * <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.1\">Section 2.1.1</a> for\n\t * additional details.\n\t * </p>\n\t *\n\t * <p>\n\t * To get a pin of\n\t *\n\t * Public-Key-Pins-Report-Only:\n\t * pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";\n\t * pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"\n\t *\n\t * Use\n\t *\n\t * HpkpHeaderWriter hpkpHeaderWriter = new HpkpHeaderWriter();\n\t * hpkpHeaderWriter.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM\",\n\t * \"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\");\n\t * </p>\n\t * @param pins a list of base64-encoded SPKI fingerprints.\n\t * @throws IllegalArgumentException if a pin is null\n\t */\n\tpublic void addSha256Pins(String... pins) {\n\t\tfor (String pin : pins) {\n\t\t\tAssert.notNull(pin, \"pin cannot be null\");\n\t\t\tthis.pins.put(pin, \"sha256\");\n\t\t}\n\t\tupdateHpkpHeaderValue();\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the value (in seconds) for the max-age directive of the Public-Key-Pins\n\t * header. The default is 60 days.\n\t * </p>\n\t *\n\t * <p>\n\t * This instructs browsers how long they should regard the host (from whom the message\n\t * was received) as a known pinned host. See\n\t * <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.2\">Section 2.1.2</a> for\n\t * additional details.\n\t * </p>\n\t *\n\t * <p>\n\t * To get a header like\n\t *\n\t * Public-Key-Pins-Report-Only: max-age=2592000;\n\t * pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";\n\t * pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"\n\t *\n\t * Use\n\t *\n\t * HpkpHeaderWriter hpkpHeaderWriter = new HpkpHeaderWriter();\n\t * hpkpHeaderWriter.setMaxAgeInSeconds(TimeUnit.DAYS.toSeconds(30));\n\t * </p>\n\t * @param maxAgeInSeconds the maximum amount of time (in seconds) to regard the host\n\t * as a known pinned host. (i.e. TimeUnit.DAYS.toSeconds(30) would set this to 30\n\t * days)\n\t * @throws IllegalArgumentException if maxAgeInSeconds is negative\n\t */\n\tpublic void setMaxAgeInSeconds(long maxAgeInSeconds) {\n\t\tAssert.isTrue(maxAgeInSeconds > 0, () -> \"maxAgeInSeconds must be non-negative. Got \" + maxAgeInSeconds);\n\t\tthis.maxAgeInSeconds = maxAgeInSeconds;\n\t\tupdateHpkpHeaderValue();\n\t}\n\n\t/**\n\t * <p>\n\t * If true, the pinning policy applies to this pinned host as well as any subdomains\n\t * of the host's domain name. The default is false.\n\t * </p>\n\t *\n\t * <p>\n\t * See <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.3\">Section 2.1.3</a>\n\t * for additional details.\n\t * </p>\n\t *\n\t * <p>\n\t * To get a header like\n\t *\n\t * Public-Key-Pins-Report-Only: max-age=5184000;\n\t * pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";\n\t * pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"; includeSubDomains\n\t *\n\t * you should set this to true.\n\t * </p>\n\t * @param includeSubDomains true to include subdomains, else false\n\t */\n\tpublic void setIncludeSubDomains(boolean includeSubDomains) {\n\t\tthis.includeSubDomains = includeSubDomains;\n\t\tupdateHpkpHeaderValue();\n\t}\n\n\t/**\n\t * <p>\n\t * To get a Public-Key-Pins header you should set this to false, otherwise the header\n\t * will be Public-Key-Pins-Report-Only. When in report-only mode, the browser should\n\t * not terminate the connection with the server. By default this is true.\n\t * </p>\n\t *\n\t * <p>\n\t * See <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1\">Section 2.1</a> for\n\t * additional details.\n\t * </p>\n\t *\n\t * <p>\n\t * To get a header like\n\t *\n\t * Public-Key-Pins: max-age=5184000;\n\t * pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";\n\t * pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"\n\t *\n\t * you should the this to false.\n\t * </p>\n\t * @param reportOnly true to report only, else false\n\t */\n\tpublic void setReportOnly(boolean reportOnly) {\n\t\tthis.reportOnly = reportOnly;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the URI to which the browser should report pin validation failures.\n\t * </p>\n\t *\n\t * <p>\n\t * See <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.4\">Section 2.1.4</a>\n\t * for additional details.\n\t * </p>\n\t *\n\t * <p>\n\t * To get a header like\n\t *\n\t * Public-Key-Pins-Report-Only: max-age=5184000;\n\t * pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\";\n\t * pin-sha256=\"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=\";\n\t * report-uri=\"https://other.example.net/pkp-report\"\n\t *\n\t * Use\n\t *\n\t * HpkpHeaderWriter hpkpHeaderWriter = new HpkpHeaderWriter();\n\t * hpkpHeaderWriter.setReportUri(new URI(\"https://other.example.net/pkp-report\"));\n\t * </p>\n\t * @param reportUri the URI where the browser should send the report to.\n\t */\n\tpublic void setReportUri(URI reportUri) {\n\t\tthis.reportUri = reportUri;\n\t\tupdateHpkpHeaderValue();\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the URI to which the browser should report pin validation failures.\n\t * </p>\n\t *\n\t * <p>\n\t * See <a href=\"https://tools.ietf.org/html/rfc7469#section-2.1.4\">Section 2.1.4</a>\n\t * for additional details.\n\t * </p>\n\t *\n\t * <p>\n\t * To get a header like\n\t *\n\t * Public-Key-Pins-Report-Only: max-age=5184000;\n\t * pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\";\n\t * pin-sha256=\"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=\";\n\t * report-uri=\"https://other.example.net/pkp-report\"\n\t *\n\t * Use\n\t *\n\t * HpkpHeaderWriter hpkpHeaderWriter = new HpkpHeaderWriter();\n\t * hpkpHeaderWriter.setReportUri(\"https://other.example.net/pkp-report\");\n\t * </p>\n\t * @param reportUri the URI where the browser should send the report to.\n\t * @throws IllegalArgumentException if the reportUri is not a valid URI\n\t */\n\tpublic void setReportUri(String reportUri) {\n\t\ttry {\n\t\t\tthis.reportUri = new URI(reportUri);\n\t\t\tupdateHpkpHeaderValue();\n\t\t}\n\t\tcatch (URISyntaxException ex) {\n\t\t\tthrow new IllegalArgumentException(ex);\n\t\t}\n\t}\n\n\tprivate void updateHpkpHeaderValue() {\n\t\tString headerValue = \"max-age=\" + this.maxAgeInSeconds;\n\t\tfor (Map.Entry<String, String> pin : this.pins.entrySet()) {\n\t\t\theaderValue += \" ; pin-\" + pin.getValue() + \"=\\\"\" + pin.getKey() + \"\\\"\";\n\t\t}\n\t\tif (this.reportUri != null) {\n\t\t\theaderValue += \" ; report-uri=\\\"\" + this.reportUri.toString() + \"\\\"\";\n\t\t}\n\t\tif (this.includeSubDomains) {\n\t\t\theaderValue += \" ; includeSubDomains\";\n\t\t}\n\t\tthis.hpkpHeaderValue = headerValue;\n\t}\n\n\tprivate static final class SecureRequestMatcher implements RequestMatcher {\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn request.isSecure();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Is Secure\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/HstsHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * Provides support for <a href=\"https://tools.ietf.org/html/rfc6797\">HTTP Strict\n * Transport Security (HSTS)</a>.\n *\n * <p>\n * By default the expiration is one year, subdomains will be included and preload will not\n * be included. This can be customized using {@link #setMaxAgeInSeconds(long)},\n * {@link #setIncludeSubDomains(boolean)} and {@link #setPreload(boolean)} respectively.\n * </p>\n *\n * <p>\n * Since <a href=\"https://tools.ietf.org/html/rfc6797#section-7.2\">section 7.2</a> states\n * that HSTS Host MUST NOT include the STS header in HTTP responses, the default behavior\n * is that the \"Strict-Transport-Security\" will only be added when\n * {@link HttpServletRequest#isSecure()} returns {@code true} . At times this may need to\n * be customized. For example, in some situations where SSL termination is used, something\n * else may be used to determine if SSL was used. For these circumstances,\n * {@link #setRequestMatcher(RequestMatcher)} can be invoked with a custom\n * {@link RequestMatcher}.\n * </p>\n *\n * <p>\n * See <a href=\"https://hstspreload.org/\">Website hstspreload.org</a> for additional\n * details on HSTS preload.\n * </p>\n *\n * @author Rob Winch\n * @author Ankur Pathak\n * @since 3.2\n */\npublic final class HstsHeaderWriter implements HeaderWriter {\n\n\tprivate static final long DEFAULT_MAX_AGE_SECONDS = 31536000;\n\n\tprivate static final String HSTS_HEADER_NAME = \"Strict-Transport-Security\";\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate long maxAgeInSeconds;\n\n\tprivate boolean includeSubDomains;\n\n\tprivate boolean preload;\n\n\tprivate String hstsHeaderValue;\n\n\t/**\n\t * Creates a new instance\n\t * @param requestMatcher maps to {@link #setRequestMatcher(RequestMatcher)}\n\t * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}\n\t * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}\n\t * @param preload maps to {@link #setPreload(boolean)}\n\t * @since 5.2.0\n\t */\n\tpublic HstsHeaderWriter(RequestMatcher requestMatcher, long maxAgeInSeconds, boolean includeSubDomains,\n\t\t\tboolean preload) {\n\t\tthis.requestMatcher = requestMatcher;\n\t\tthis.maxAgeInSeconds = maxAgeInSeconds;\n\t\tthis.includeSubDomains = includeSubDomains;\n\t\tthis.preload = preload;\n\t\tupdateHstsHeaderValue();\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param requestMatcher maps to {@link #setRequestMatcher(RequestMatcher)}\n\t * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}\n\t * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}\n\t */\n\tpublic HstsHeaderWriter(RequestMatcher requestMatcher, long maxAgeInSeconds, boolean includeSubDomains) {\n\t\tthis(requestMatcher, maxAgeInSeconds, includeSubDomains, false);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}\n\t * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}\n\t * @param preload maps to {@link #setPreload(boolean)}\n\t * @since 5.2.0\n\t */\n\tpublic HstsHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains, boolean preload) {\n\t\tthis(new SecureRequestMatcher(), maxAgeInSeconds, includeSubDomains, preload);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}\n\t * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}\n\t */\n\tpublic HstsHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains) {\n\t\tthis(new SecureRequestMatcher(), maxAgeInSeconds, includeSubDomains, false);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)}\n\t */\n\tpublic HstsHeaderWriter(long maxAgeInSeconds) {\n\t\tthis(new SecureRequestMatcher(), maxAgeInSeconds, true, false);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)}\n\t */\n\tpublic HstsHeaderWriter(boolean includeSubDomains) {\n\t\tthis(new SecureRequestMatcher(), DEFAULT_MAX_AGE_SECONDS, includeSubDomains, false);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t */\n\tpublic HstsHeaderWriter() {\n\t\tthis(DEFAULT_MAX_AGE_SECONDS);\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Not injecting HSTS header since it did not match request to [%s]\",\n\t\t\t\t\t\tthis.requestMatcher));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!response.containsHeader(HSTS_HEADER_NAME)) {\n\t\t\tresponse.setHeader(HSTS_HEADER_NAME, this.hstsHeaderValue);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link RequestMatcher} used to determine if the\n\t * \"Strict-Transport-Security\" should be added. If true the header is added, else the\n\t * header is not added. By default the header is added when\n\t * {@link HttpServletRequest#isSecure()} returns true.\n\t * @param requestMatcher the {@link RequestMatcher} to use.\n\t * @throws IllegalArgumentException if {@link RequestMatcher} is null\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the value (in seconds) for the max-age directive of the\n\t * Strict-Transport-Security header. The default is one year.\n\t * </p>\n\t *\n\t * <p>\n\t * This instructs browsers how long to remember to keep this domain as a known HSTS\n\t * Host. See <a href=\"https://tools.ietf.org/html/rfc6797#section-6.1.1\">Section\n\t * 6.1.1</a> for additional details.\n\t * </p>\n\t * @param maxAgeInSeconds the maximum amount of time (in seconds) to consider this\n\t * domain as a known HSTS Host.\n\t * @throws IllegalArgumentException if maxAgeInSeconds is negative\n\t */\n\tpublic void setMaxAgeInSeconds(long maxAgeInSeconds) {\n\t\tAssert.isTrue(maxAgeInSeconds >= 0, () -> \"maxAgeInSeconds must be non-negative. Got \" + maxAgeInSeconds);\n\t\tthis.maxAgeInSeconds = maxAgeInSeconds;\n\t\tupdateHstsHeaderValue();\n\t}\n\n\t/**\n\t * <p>\n\t * If true, subdomains should be considered HSTS Hosts too. The default is true.\n\t * </p>\n\t *\n\t * <p>\n\t * See <a href=\"https://tools.ietf.org/html/rfc6797#section-6.1.2\">Section 6.1.2</a>\n\t * for additional details.\n\t * </p>\n\t * @param includeSubDomains true to include subdomains, else false\n\t */\n\tpublic void setIncludeSubDomains(boolean includeSubDomains) {\n\t\tthis.includeSubDomains = includeSubDomains;\n\t\tupdateHstsHeaderValue();\n\t}\n\n\t/**\n\t * <p>\n\t * If true, preload will be included in HSTS Header. The default is false.\n\t * </p>\n\t *\n\t * <p>\n\t * See <a href=\"https://tools.ietf.org/html/rfc6797#section-6.1.2\">Section 6.1.2</a>\n\t * for additional details.\n\t * </p>\n\t * @param preload true to include preload, else false\n\t * @since 5.2.0\n\t */\n\tpublic void setPreload(boolean preload) {\n\t\tthis.preload = preload;\n\t\tupdateHstsHeaderValue();\n\t}\n\n\tprivate void updateHstsHeaderValue() {\n\t\tString headerValue = \"max-age=\" + this.maxAgeInSeconds;\n\t\tif (this.includeSubDomains) {\n\t\t\theaderValue += \" ; includeSubDomains\";\n\t\t}\n\t\tif (this.preload) {\n\t\t\theaderValue += \" ; preload\";\n\t\t}\n\t\tthis.hstsHeaderValue = headerValue;\n\t}\n\n\tprivate static final class SecureRequestMatcher implements RequestMatcher {\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn request.isSecure();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Is Secure\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/PermissionsPolicyHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * Provides support for\n * <a href=\"https://w3c.github.io/webappsec-permissions-policy//\">Permissions Policy</a>.\n * <p>\n * Permissions Policy allows web developers to selectively enable, disable, and modify the\n * behavior of certain APIs and web features in the browser.\n * <p>\n * A declaration of a permissions policy contains a set of security policies, each\n * responsible for declaring the restrictions for a particular feature type.\n *\n * @author Christophe Gilles\n * @since 5.5\n */\npublic final class PermissionsPolicyHeaderWriter implements HeaderWriter {\n\n\tprivate static final String PERMISSIONS_POLICY_HEADER = \"Permissions-Policy\";\n\n\tprivate @Nullable String policy;\n\n\t/**\n\t * Create a new instance of {@link PermissionsPolicyHeaderWriter}.\n\t */\n\tpublic PermissionsPolicyHeaderWriter() {\n\t}\n\n\t/**\n\t * Create a new instance of {@link PermissionsPolicyHeaderWriter} with supplied\n\t * security policy.\n\t * @param policy the security policy\n\t * @throws IllegalArgumentException if policy is {@code null} or empty\n\t */\n\tpublic PermissionsPolicyHeaderWriter(String policy) {\n\t\tsetPolicy(policy);\n\t}\n\n\t/**\n\t * Sets the policy to be used in the response header.\n\t * @param policy a permissions policy\n\t * @throws IllegalArgumentException if policy is null\n\t */\n\tpublic void setPolicy(String policy) {\n\t\tAssert.hasLength(policy, \"policy can not be null or empty\");\n\t\tthis.policy = policy;\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (!response.containsHeader(PERMISSIONS_POLICY_HEADER)) {\n\t\t\tresponse.setHeader(PERMISSIONS_POLICY_HEADER, this.policy);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getName() + \" [policy=\" + this.policy + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/ReferrerPolicyHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * Provides support for <a href=\"https://www.w3.org/TR/referrer-policy/\">Referrer\n * Policy</a>.\n * </p>\n *\n * <p>\n * The list of policies defined can be found at\n * <a href=\"https://www.w3.org/TR/referrer-policy/#referrer-policies\">Referrer\n * Policies</a>.\n * </p>\n *\n * <p>\n * This implementation of {@link HeaderWriter} writes the following header:\n * </p>\n * <ul>\n * <li>Referrer-Policy</li>\n * </ul>\n *\n * <p>\n * By default, the Referrer-Policy header is not included in the response. Policy\n * <b>no-referrer</b> is used by default if no {@link ReferrerPolicy} is set.\n * </p>\n *\n * @author Eddú Meléndez\n * @author Kazuki Shimizu\n * @author Ankur Pathak\n * @since 4.2\n */\npublic class ReferrerPolicyHeaderWriter implements HeaderWriter {\n\n\tprivate static final String REFERRER_POLICY_HEADER = \"Referrer-Policy\";\n\n\tprivate ReferrerPolicy policy;\n\n\t/**\n\t * Creates a new instance. Default value: no-referrer.\n\t */\n\tpublic ReferrerPolicyHeaderWriter() {\n\t\tthis(ReferrerPolicy.NO_REFERRER);\n\t}\n\n\t/**\n\t * Creates a new instance.\n\t * @param policy a referrer policy\n\t * @throws IllegalArgumentException if policy is null\n\t */\n\tpublic ReferrerPolicyHeaderWriter(ReferrerPolicy policy) {\n\t\tAssert.notNull(policy, \"policy can not be null\");\n\t\tthis.policy = policy;\n\t}\n\n\t/**\n\t * Sets the policy to be used in the response header.\n\t * @param policy a referrer policy\n\t * @throws IllegalArgumentException if policy is null\n\t */\n\tpublic void setPolicy(ReferrerPolicy policy) {\n\t\tAssert.notNull(policy, \"policy can not be null\");\n\t\tthis.policy = policy;\n\t}\n\n\t/**\n\t * @see org.springframework.security.web.header.HeaderWriter#writeHeaders(HttpServletRequest,\n\t * HttpServletResponse)\n\t */\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (!response.containsHeader(REFERRER_POLICY_HEADER)) {\n\t\t\tresponse.setHeader(REFERRER_POLICY_HEADER, this.policy.getPolicy());\n\t\t}\n\t}\n\n\tpublic enum ReferrerPolicy {\n\n\t\tNO_REFERRER(\"no-referrer\"),\n\n\t\tNO_REFERRER_WHEN_DOWNGRADE(\"no-referrer-when-downgrade\"),\n\n\t\tSAME_ORIGIN(\"same-origin\"),\n\n\t\tORIGIN(\"origin\"),\n\n\t\tSTRICT_ORIGIN(\"strict-origin\"),\n\n\t\tORIGIN_WHEN_CROSS_ORIGIN(\"origin-when-cross-origin\"),\n\n\t\tSTRICT_ORIGIN_WHEN_CROSS_ORIGIN(\"strict-origin-when-cross-origin\"),\n\n\t\tUNSAFE_URL(\"unsafe-url\");\n\n\t\tprivate static final Map<String, ReferrerPolicy> REFERRER_POLICIES;\n\n\t\tstatic {\n\t\t\tMap<String, ReferrerPolicy> referrerPolicies = new HashMap<>();\n\t\t\tfor (ReferrerPolicy referrerPolicy : values()) {\n\t\t\t\treferrerPolicies.put(referrerPolicy.getPolicy(), referrerPolicy);\n\t\t\t}\n\t\t\tREFERRER_POLICIES = Collections.unmodifiableMap(referrerPolicies);\n\t\t}\n\n\t\tprivate final String policy;\n\n\t\tReferrerPolicy(String policy) {\n\t\t\tthis.policy = policy;\n\t\t}\n\n\t\tpublic String getPolicy() {\n\t\t\treturn this.policy;\n\t\t}\n\n\t\tpublic static @Nullable ReferrerPolicy get(String referrerPolicy) {\n\t\t\treturn REFERRER_POLICIES.get(referrerPolicy);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/StaticHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.header.Header;\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * {@code HeaderWriter} implementation which writes the same {@code Header} instance.\n *\n * @author Marten Deinum\n * @author Rob Winch\n * @author Ankur Pathak\n * @since 3.2\n */\npublic class StaticHeadersWriter implements HeaderWriter {\n\n\tprivate final List<Header> headers;\n\n\t/**\n\t * Creates a new instance\n\t * @param headers the {@link Header} instances to use\n\t */\n\tpublic StaticHeadersWriter(List<Header> headers) {\n\t\tAssert.notEmpty(headers, \"headers cannot be null or empty\");\n\t\tthis.headers = headers;\n\t}\n\n\t/**\n\t * Creates a new instance with a single header\n\t * @param headerName the name of the header\n\t * @param headerValues the values for the header\n\t */\n\tpublic StaticHeadersWriter(String headerName, String... headerValues) {\n\t\tthis(Collections.singletonList(new Header(headerName, headerValues)));\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tfor (Header header : this.headers) {\n\t\t\tif (!response.containsHeader(header.getName())) {\n\t\t\t\tfor (String value : header.getValues()) {\n\t\t\t\t\tresponse.addHeader(header.getName(), value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getName() + \" [headers=\" + this.headers + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/XContentTypeOptionsHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\n/**\n * A {@link StaticHeadersWriter} that inserts headers to prevent content sniffing.\n * Specifically the following headers are set:\n * <ul>\n * <li>X-Content-Type-Options: nosniff</li>\n * </ul>\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class XContentTypeOptionsHeaderWriter extends StaticHeadersWriter {\n\n\t/**\n\t * Creates a new instance\n\t */\n\tpublic XContentTypeOptionsHeaderWriter() {\n\t\tsuper(\"X-Content-Type-Options\", \"nosniff\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/XXssProtectionHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * Renders the <a href=\n * \"https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx\"\n * >X-XSS-Protection header</a>.\n *\n * @author Rob Winch\n * @author Ankur Pathak\n * @author Daniel Garnier-Moiroux\n * @since 3.2\n */\npublic final class XXssProtectionHeaderWriter implements HeaderWriter {\n\n\tprivate static final String XSS_PROTECTION_HEADER = \"X-XSS-Protection\";\n\n\tprivate HeaderValue headerValue;\n\n\t/**\n\t * Create a new instance\n\t */\n\tpublic XXssProtectionHeaderWriter() {\n\t\tthis.headerValue = HeaderValue.DISABLED;\n\t}\n\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (!response.containsHeader(XSS_PROTECTION_HEADER)) {\n\t\t\tresponse.setHeader(XSS_PROTECTION_HEADER, this.headerValue.toString());\n\t\t}\n\t}\n\n\t/**\n\t * Sets the value of the X-XSS-PROTECTION header.\n\t * <p>\n\t * If {@link HeaderValue#DISABLED}, will specify that X-XSS-Protection is disabled.\n\t * For example:\n\t *\n\t * <pre>\n\t * X-XSS-Protection: 0\n\t * </pre>\n\t * <p>\n\t * If {@link HeaderValue#ENABLED}, will contain a value of 1, but will not specify the\n\t * mode as blocked. In this instance, any content will be attempted to be fixed. For\n\t * example:\n\t *\n\t * <pre>\n\t * X-XSS-Protection: 1\n\t * </pre>\n\t * <p>\n\t * If {@link HeaderValue#ENABLED_MODE_BLOCK}, will contain a value of 1 and will\n\t * specify mode as blocked. The content will be replaced with \"#\". For example:\n\t *\n\t * <pre>\n\t * X-XSS-Protection: 1; mode=block\n\t * </pre>\n\t * @param headerValue the new header value\n\t * @throws IllegalArgumentException when headerValue is null\n\t * @since 5.8\n\t */\n\tpublic void setHeaderValue(HeaderValue headerValue) {\n\t\tAssert.notNull(headerValue, \"headerValue cannot be null\");\n\t\tthis.headerValue = headerValue;\n\t}\n\n\t/**\n\t * The value of the x-xss-protection header. One of: \"0\", \"1\", \"1; mode=block\"\n\t *\n\t * @author Daniel Garnier-Moiroux\n\t * @since 5.8\n\t */\n\tpublic enum HeaderValue {\n\n\t\tDISABLED(\"0\"), ENABLED(\"1\"), ENABLED_MODE_BLOCK(\"1; mode=block\");\n\n\t\tprivate final String value;\n\n\t\tHeaderValue(String value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\tpublic static @Nullable HeaderValue from(String headerValue) {\n\t\t\tfor (HeaderValue value : values()) {\n\t\t\t\tif (value.toString().equals(headerValue)) {\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn this.value;\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getName() + \" [headerValue=\" + this.headerValue + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/frameoptions/AbstractRequestParameterAllowFromStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Base class for AllowFromStrategy implementations which use a request parameter to\n * retrieve the origin. By default the parameter named <code>x-frames-allow-from</code> is\n * read from the request.\n *\n * @author Marten Deinum\n * @since 3.2\n * @deprecated ALLOW-FROM is an obsolete directive that no longer works in modern\n * browsers. Instead use Content-Security-Policy with the <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n * directive.\n */\n@Deprecated\npublic abstract class AbstractRequestParameterAllowFromStrategy implements AllowFromStrategy {\n\n\tprivate static final String DEFAULT_ORIGIN_REQUEST_PARAMETER = \"x-frames-allow-from\";\n\n\tprivate String allowFromParameterName = DEFAULT_ORIGIN_REQUEST_PARAMETER;\n\n\t/** Logger for use by subclasses */\n\tprotected final Log log = LogFactory.getLog(getClass());\n\n\tAbstractRequestParameterAllowFromStrategy() {\n\t}\n\n\t@Override\n\tpublic String getAllowFromValue(HttpServletRequest request) {\n\t\tString allowFromOrigin = request.getParameter(this.allowFromParameterName);\n\t\tthis.log.debug(LogMessage.format(\"Supplied origin '%s'\", allowFromOrigin));\n\t\tif (StringUtils.hasText(allowFromOrigin) && allowed(allowFromOrigin)) {\n\t\t\treturn allowFromOrigin;\n\t\t}\n\t\treturn \"DENY\";\n\t}\n\n\t/**\n\t * Sets the HTTP parameter used to retrieve the value for the origin that is allowed\n\t * from. The value of the parameter should be a valid URL. The default parameter name\n\t * is \"x-frames-allow-from\".\n\t * @param allowFromParameterName the name of the HTTP parameter to\n\t */\n\tpublic void setAllowFromParameterName(String allowFromParameterName) {\n\t\tAssert.notNull(allowFromParameterName, \"allowFromParameterName cannot be null\");\n\t\tthis.allowFromParameterName = allowFromParameterName;\n\t}\n\n\t/**\n\t * Method to be implemented by base classes, used to determine if the supplied origin\n\t * is allowed.\n\t * @param allowFromOrigin the supplied origin\n\t * @return <code>true</code> if the supplied origin is allowed.\n\t */\n\tprotected abstract boolean allowed(String allowFromOrigin);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/frameoptions/AllowFromStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\n/**\n * Strategy interfaces used by the {@code FrameOptionsHeaderWriter} to determine the\n * actual value to use for the X-Frame-Options header when using the ALLOW-FROM directive.\n *\n * @author Marten Deinum\n * @since 3.2\n * @deprecated ALLOW-FROM is an obsolete directive that no longer works in modern\n * browsers. Instead use Content-Security-Policy with the <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n * directive.\n */\n@Deprecated\npublic interface AllowFromStrategy {\n\n\t/**\n\t * Gets the value for ALLOW-FROM excluding the ALLOW-FROM. For example, the result\n\t * might be \"https://example.com/\".\n\t * @param request the {@link HttpServletRequest}\n\t * @return the value for ALLOW-FROM or null if no header should be added for this\n\t * request.\n\t */\n\tString getAllowFromValue(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/frameoptions/RegExpAllowFromStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport java.util.regex.Pattern;\n\nimport org.springframework.util.Assert;\n\n/**\n * Implementation which uses a regular expression to validate the supplied origin. If the\n * value of the HTTP parameter matches the pattern, then the result will be ALLOW-FROM\n * &lt;parameter-value&gt;.\n *\n * @author Marten Deinum\n * @since 3.2\n * @deprecated ALLOW-FROM is an obsolete directive that no longer works in modern\n * browsers. Instead use Content-Security-Policy with the <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n * directive.\n */\n@Deprecated\npublic final class RegExpAllowFromStrategy extends AbstractRequestParameterAllowFromStrategy {\n\n\tprivate final Pattern pattern;\n\n\t/**\n\t * Creates a new instance\n\t * @param pattern the Pattern to compare against the HTTP parameter value. If the\n\t * pattern matches, the domain will be allowed, else denied.\n\t */\n\tpublic RegExpAllowFromStrategy(String pattern) {\n\t\tAssert.hasText(pattern, \"Pattern cannot be empty.\");\n\t\tthis.pattern = Pattern.compile(pattern);\n\t}\n\n\t@Override\n\tprotected boolean allowed(String allowFromOrigin) {\n\t\treturn this.pattern.matcher(allowFromOrigin).matches();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/frameoptions/StaticAllowFromStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport java.net.URI;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\n/**\n * Simple implementation of the {@code AllowFromStrategy}\n *\n * @deprecated ALLOW-FROM is an obsolete directive that no longer works in modern\n * browsers. Instead use Content-Security-Policy with the <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n * directive.\n */\n@Deprecated\npublic final class StaticAllowFromStrategy implements AllowFromStrategy {\n\n\tprivate final URI uri;\n\n\tpublic StaticAllowFromStrategy(URI uri) {\n\t\tthis.uri = uri;\n\t}\n\n\t@Override\n\tpublic String getAllowFromValue(HttpServletRequest request) {\n\t\treturn this.uri.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/frameoptions/WhiteListedAllowFromStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport java.util.Collection;\n\nimport org.springframework.util.Assert;\n\n/**\n * Implementation which checks the supplied origin against a list of allowed origins.\n *\n * @author Marten Deinum\n * @since 3.2\n * @deprecated ALLOW-FROM is an obsolete directive that no longer works in modern\n * browsers. Instead use Content-Security-Policy with the <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n * directive.\n */\n@Deprecated\npublic final class WhiteListedAllowFromStrategy extends AbstractRequestParameterAllowFromStrategy {\n\n\tprivate final Collection<String> allowed;\n\n\t/**\n\t * Creates a new instance\n\t * @param allowed the origins that are allowed.\n\t */\n\tpublic WhiteListedAllowFromStrategy(Collection<String> allowed) {\n\t\tAssert.notEmpty(allowed, \"Allowed origins cannot be empty.\");\n\t\tthis.allowed = allowed;\n\t}\n\n\t@Override\n\tprotected boolean allowed(String allowFromOrigin) {\n\t\treturn this.allowed.contains(allowFromOrigin);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/frameoptions/XFrameOptionsHeaderWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.util.Assert;\n\n/**\n * {@code HeaderWriter} implementation for the X-Frame-Options headers. When using the\n * ALLOW-FROM directive the actual value is determined by a {@code AllowFromStrategy}.\n *\n * @author Marten Deinum\n * @author Rob Winch\n * @author Ankur Pathak\n * @since 3.2\n */\npublic final class XFrameOptionsHeaderWriter implements HeaderWriter {\n\n\tpublic static final String XFRAME_OPTIONS_HEADER = \"X-Frame-Options\";\n\n\tprivate final @Nullable AllowFromStrategy allowFromStrategy;\n\n\tprivate final XFrameOptionsMode frameOptionsMode;\n\n\t/**\n\t * Creates an instance with {@link XFrameOptionsMode#DENY}\n\t */\n\tpublic XFrameOptionsHeaderWriter() {\n\t\tthis(XFrameOptionsMode.DENY);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param frameOptionsMode the {@link XFrameOptionsMode} to use. If using\n\t * {@link XFrameOptionsMode#ALLOW_FROM}, use Content-Security-Policy with the <a href=\n\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t * directive instead.\n\t */\n\tpublic XFrameOptionsHeaderWriter(XFrameOptionsMode frameOptionsMode) {\n\t\tAssert.notNull(frameOptionsMode, \"frameOptionsMode cannot be null\");\n\t\tAssert.isTrue(!XFrameOptionsMode.ALLOW_FROM.equals(frameOptionsMode),\n\t\t\t\t\"ALLOW_FROM requires an AllowFromStrategy. Please use \"\n\t\t\t\t\t\t+ \"FrameOptionsHeaderWriter(AllowFromStrategy allowFromStrategy) instead\");\n\t\tthis.frameOptionsMode = frameOptionsMode;\n\t\tthis.allowFromStrategy = null;\n\t}\n\n\t/**\n\t * Creates a new instance with {@link XFrameOptionsMode#ALLOW_FROM}.\n\t * @param allowFromStrategy the strategy for determining what the value for ALLOW_FROM\n\t * is.\n\t * @deprecated ALLOW-FROM is an obsolete directive that no longer works in modern\n\t * browsers. Instead use Content-Security-Policy with the <a href=\n\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t * directive.\n\t * @see AllowFromStrategy\n\t */\n\t@Deprecated\n\tpublic XFrameOptionsHeaderWriter(AllowFromStrategy allowFromStrategy) {\n\t\tAssert.notNull(allowFromStrategy, \"allowFromStrategy cannot be null\");\n\t\tthis.frameOptionsMode = XFrameOptionsMode.ALLOW_FROM;\n\t\tthis.allowFromStrategy = allowFromStrategy;\n\t}\n\n\t/**\n\t * Writes the X-Frame-Options header value, overwritting any previous value.\n\t * @param request the servlet request\n\t * @param response the servlet response\n\t */\n\t@Override\n\tpublic void writeHeaders(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (XFrameOptionsMode.ALLOW_FROM.equals(this.frameOptionsMode)) {\n\t\t\tAssert.notNull(this.allowFromStrategy,\n\t\t\t\t\t\"AllowFromStrategy cannot be null when frameOptionsMode is ALLOW_FROM\");\n\t\t\tString allowFromValue = this.allowFromStrategy.getAllowFromValue(request);\n\t\t\tif (XFrameOptionsMode.DENY.getMode().equals(allowFromValue)) {\n\t\t\t\tif (!response.containsHeader(XFRAME_OPTIONS_HEADER)) {\n\t\t\t\t\tresponse.setHeader(XFRAME_OPTIONS_HEADER, XFrameOptionsMode.DENY.getMode());\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (allowFromValue != null) {\n\t\t\t\tif (!response.containsHeader(XFRAME_OPTIONS_HEADER)) {\n\t\t\t\t\tresponse.setHeader(XFRAME_OPTIONS_HEADER,\n\t\t\t\t\t\t\tXFrameOptionsMode.ALLOW_FROM.getMode() + \" \" + allowFromValue);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tresponse.setHeader(XFRAME_OPTIONS_HEADER, this.frameOptionsMode.getMode());\n\t\t}\n\t}\n\n\t/**\n\t * The possible values for the X-Frame-Options header.\n\t *\n\t * @author Rob Winch\n\t * @since 3.2\n\t */\n\tpublic enum XFrameOptionsMode {\n\n\t\tDENY(\"DENY\"),\n\n\t\tSAMEORIGIN(\"SAMEORIGIN\"),\n\n\t\t/**\n\t\t * @deprecated ALLOW-FROM is an obsolete directive that no longer works in modern\n\t\t * browsers. Instead use Content-Security-Policy with the <a href=\n\t\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors\">frame-ancestors</a>\n\t\t * directive.\n\t\t */\n\t\t@Deprecated\n\t\tALLOW_FROM(\"ALLOW-FROM\");\n\n\t\tprivate final String mode;\n\n\t\tXFrameOptionsMode(String mode) {\n\t\t\tthis.mode = mode;\n\t\t}\n\n\t\t/**\n\t\t * Gets the mode for the X-Frame-Options header value. For example, DENY,\n\t\t * SAMEORIGIN, ALLOW-FROM. Cannot be null.\n\t\t * @return the mode for the X-Frame-Options header value.\n\t\t */\n\t\tprivate String getMode() {\n\t\t\treturn this.mode;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/frameoptions/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * APIs for writing security HTTP Headers related to frame options.\n */\n@NullMarked\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/header/writers/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * APIs for writing security HTTP Headers.\n */\n@NullMarked\npackage org.springframework.security.web.header.writers;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/http/SecurityHeaders.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.http;\n\nimport java.util.function.Consumer;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.util.Assert;\n\n/**\n * Utilities for interacting with {@link HttpHeaders}\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic final class SecurityHeaders {\n\n\tprivate SecurityHeaders() {\n\t}\n\n\t/**\n\t * Sets the provided value as a Bearer token in a header with the name of\n\t * {@link HttpHeaders#AUTHORIZATION}\n\t * @param bearerTokenValue the bear token value\n\t * @return a {@link Consumer} that sets the header.\n\t */\n\tpublic static Consumer<HttpHeaders> bearerToken(String bearerTokenValue) {\n\t\tAssert.hasText(bearerTokenValue, \"bearerTokenValue cannot be null\");\n\t\treturn (headers) -> headers.set(HttpHeaders.AUTHORIZATION, \"Bearer \" + bearerTokenValue);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/http/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * HTTP based security APIs.\n */\n@NullMarked\npackage org.springframework.security.web.http;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jaasapi/JaasApiIntegrationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jaasapi;\n\nimport java.io.IOException;\nimport java.security.PrivilegedActionException;\nimport java.security.PrivilegedExceptionAction;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.login.LoginContext;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.jaas.JaasAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * <p>\n * A <code>Filter</code> which attempts to obtain a JAAS <code>Subject</code> and continue\n * the <code>FilterChain</code> running as that <code>Subject</code>.\n * </p>\n * <p>\n * By using this <code>Filter</code> in conjunction with Spring's\n * <code>JaasAuthenticationProvider</code> both Spring's <code>SecurityContext</code> and\n * a JAAS <code>Subject</code> can be populated simultaneously. This is useful when\n * integrating with code that requires a JAAS <code>Subject</code> to be populated.\n * </p>\n *\n * @author Rob Winch\n * @see #doFilter(ServletRequest, ServletResponse, FilterChain)\n * @see #obtainSubject(ServletRequest)\n */\npublic class JaasApiIntegrationFilter extends GenericFilterBean {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate boolean createEmptySubject;\n\n\t/**\n\t * <p>\n\t * Attempts to obtain and run as a JAAS <code>Subject</code> using\n\t * {@link #obtainSubject(ServletRequest)}.\n\t * </p>\n\t *\n\t * <p>\n\t * If the <code>Subject</code> is <code>null</code> and <tt>createEmptySubject</tt> is\n\t * <code>true</code>, an empty, writeable <code>Subject</code> is used. This allows\n\t * for the <code>Subject</code> to be populated at the time of login. If the\n\t * <code>Subject</code> is <code>null</code>, the <code>FilterChain</code> continues\n\t * with no additional processing. If the <code>Subject</code> is not <code>null</code>\n\t * , the <code>FilterChain</code> is ran with\n\t * {@link Subject#doAs(Subject, PrivilegedExceptionAction)} in conjunction with the\n\t * <code>Subject</code> obtained.\n\t * </p>\n\t */\n\t@Override\n\tpublic final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\n\t\tSubject subject = obtainSubject(request);\n\t\tif (subject == null && this.createEmptySubject) {\n\t\t\tthis.logger.debug(\"Subject returned was null and createEmptySubject is true; \"\n\t\t\t\t\t+ \"creating new empty subject to run as.\");\n\t\t\tsubject = new Subject();\n\t\t}\n\t\tif (subject == null) {\n\t\t\tthis.logger.debug(\"Subject is null continue running with no Subject.\");\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tthis.logger.debug(LogMessage.format(\"Running as Subject %s\", subject));\n\t\ttry {\n\t\t\tSubject.doAs(subject, (PrivilegedExceptionAction<Object>) () -> {\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t\treturn null;\n\t\t\t});\n\t\t}\n\t\tcatch (PrivilegedActionException ex) {\n\t\t\tthrow new ServletException(ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Obtains the <code>Subject</code> to run as or <code>null</code> if no\n\t * <code>Subject</code> is available.\n\t * </p>\n\t * <p>\n\t * The default implementation attempts to obtain the <code>Subject</code> from the\n\t * <code>SecurityContext</code>'s <code>Authentication</code>. If it is of type\n\t * <code>JaasAuthenticationToken</code> and is authenticated, the <code>Subject</code>\n\t * is returned from it. Otherwise, <code>null</code> is returned.\n\t * </p>\n\t * @param request the current <code>ServletRequest</code>\n\t * @return the Subject to run as or <code>null</code> if no <code>Subject</code> is\n\t * available.\n\t */\n\tprotected @Nullable Subject obtainSubject(ServletRequest request) {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tthis.logger.debug(LogMessage.format(\"Attempting to obtainSubject using authentication : %s\", authentication));\n\t\tif (authentication == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!authentication.isAuthenticated()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!(authentication instanceof JaasAuthenticationToken token)) {\n\t\t\treturn null;\n\t\t}\n\t\tLoginContext loginContext = token.getLoginContext();\n\t\tif (loginContext == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn loginContext.getSubject();\n\t}\n\n\t/**\n\t * Sets <code>createEmptySubject</code>. If the value is <code>true</code>, and\n\t * {@link #obtainSubject(ServletRequest)} returns <code>null</code>, an empty,\n\t * writeable <code>Subject</code> is created instead. Otherwise no\n\t * <code>Subject</code> is used. The default is <code>false</code>.\n\t * @param createEmptySubject the new value\n\t */\n\tpublic final void setCreateEmptySubject(boolean createEmptySubject) {\n\t\tthis.createEmptySubject = createEmptySubject;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jaasapi/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Makes a JAAS Subject available as the current Subject.\n * <p>\n * To use, simply add the {@code JaasApiIntegrationFilter} to the Spring Security filter\n * chain.\n */\n@NullMarked\npackage org.springframework.security.web.jaasapi;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/CookieDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport jakarta.servlet.http.Cookie;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.ValueDeserializer;\nimport tools.jackson.databind.node.MissingNode;\nimport tools.jackson.databind.node.NullNode;\n\n/**\n * Jackson deserializer for {@link Cookie}. This is needed because in most cases we don't\n * set {@link Cookie#getDomain()} property. So when jackson deserialize that json\n * {@link Cookie#setDomain(String)} throws {@link NullPointerException}. This is\n * registered with {@link CookieMixin} but you can also use it with your own mixin.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see CookieMixin\n */\nclass CookieDeserializer extends ValueDeserializer<Cookie> {\n\n\t@Override\n\tpublic Cookie deserialize(JsonParser jp, DeserializationContext ctxt) throws JacksonException {\n\t\tJsonNode jsonNode = ctxt.readTree(jp);\n\t\tCookie cookie = new Cookie(readJsonNode(jsonNode, \"name\").stringValue(),\n\t\t\t\treadJsonNode(jsonNode, \"value\").stringValue());\n\t\tJsonNode domainNode = readJsonNode(jsonNode, \"domain\");\n\t\tcookie.setDomain((domainNode.isMissingNode()) ? null : domainNode.stringValue());\n\t\tcookie.setMaxAge(readJsonNode(jsonNode, \"maxAge\").asInt(-1));\n\t\tcookie.setSecure(readJsonNode(jsonNode, \"secure\").asBoolean());\n\t\tJsonNode pathNode = readJsonNode(jsonNode, \"path\");\n\t\tcookie.setPath((pathNode.isMissingNode()) ? null : pathNode.stringValue());\n\t\tJsonNode attributes = readJsonNode(jsonNode, \"attributes\");\n\t\tcookie.setHttpOnly(readJsonNode(attributes, \"HttpOnly\") != null);\n\t\treturn cookie;\n\t}\n\n\tprivate JsonNode readJsonNode(JsonNode jsonNode, String field) {\n\t\treturn hasNonNullField(jsonNode, field) ? jsonNode.get(field) : MissingNode.getInstance();\n\t}\n\n\tprivate boolean hasNonNullField(JsonNode jsonNode, String field) {\n\t\treturn jsonNode.has(field) && !(jsonNode.get(field) instanceof NullNode);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/CookieMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * Mixin class to serialize/deserialize {@link jakarta.servlet.http.Cookie}\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see WebServletJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(using = CookieDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class CookieMixin {\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/DefaultCsrfTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson mixin class to serialize/deserialize\n * {@link org.springframework.security.web.csrf.DefaultCsrfToken} serialization support.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see WebJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\nclass DefaultCsrfTokenMixin {\n\n\t/**\n\t * JsonCreator constructor needed by Jackson to create\n\t * {@link org.springframework.security.web.csrf.DefaultCsrfToken} object.\n\t * @param headerName the name of the header\n\t * @param parameterName the parameter name\n\t * @param token the CSRF token value\n\t */\n\t@JsonCreator\n\tDefaultCsrfTokenMixin(@JsonProperty(\"headerName\") String headerName,\n\t\t\t@JsonProperty(\"parameterName\") String parameterName, @JsonProperty(\"token\") String token) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/DefaultSavedRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\n\n/**\n * Jackson mixin class to serialize/deserialize {@link DefaultSavedRequest}. This mixin\n * use {@link DefaultSavedRequest.Builder} to deserialized json.In order to use this mixin\n * class you also need to register {@link CookieMixin}.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see WebServletJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonDeserialize(builder = DefaultSavedRequest.Builder.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class DefaultSavedRequestMixin {\n\n\t@JsonInclude(JsonInclude.Include.NON_NULL)\n\t@Nullable String matchingRequestParameterName;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/PreAuthenticatedAuthenticationTokenDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport java.util.List;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.JsonNode;\nimport tools.jackson.databind.ValueDeserializer;\nimport tools.jackson.databind.node.MissingNode;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\n\n/**\n * Custom deserializer for {@link PreAuthenticatedAuthenticationToken}. At the time of\n * deserialization it will invoke suitable constructor depending on the value of\n * <b>authenticated</b> property. It will ensure that the token's state must not change.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see PreAuthenticatedAuthenticationTokenMixin\n */\nclass PreAuthenticatedAuthenticationTokenDeserializer extends ValueDeserializer<PreAuthenticatedAuthenticationToken> {\n\n\tprivate static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<>() {\n\t};\n\n\t/**\n\t * This method construct {@link PreAuthenticatedAuthenticationToken} object from\n\t * serialized json.\n\t * @param jp the JsonParser\n\t * @param ctxt the DeserializationContext\n\t * @return the user\n\t * @throws tools.jackson.core.JacksonException if an error during JSON processing\n\t * occurs\n\t */\n\t@Override\n\tpublic PreAuthenticatedAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tJsonNode jsonNode = ctxt.readTree(jp);\n\t\tboolean authenticated = readJsonNode(jsonNode, \"authenticated\").asBoolean();\n\t\tJsonNode principalNode = readJsonNode(jsonNode, \"principal\");\n\t\tObject principal = (!principalNode.isObject()) ? principalNode.stringValue()\n\t\t\t\t: ctxt.readTreeAsValue(principalNode, Object.class);\n\t\tObject credentials = readJsonNode(jsonNode, \"credentials\").stringValue();\n\t\tJsonNode authoritiesNode = readJsonNode(jsonNode, \"authorities\");\n\t\tList<GrantedAuthority> authorities = ctxt.readTreeAsValue(authoritiesNode,\n\t\t\t\tctxt.getTypeFactory().constructType(GRANTED_AUTHORITY_LIST));\n\t\tPreAuthenticatedAuthenticationToken token = (!authenticated)\n\t\t\t\t? new PreAuthenticatedAuthenticationToken(principal, credentials)\n\t\t\t\t: new PreAuthenticatedAuthenticationToken(principal, credentials, authorities);\n\t\ttoken.setDetails(readJsonNode(jsonNode, \"details\"));\n\t\treturn token;\n\t}\n\n\tprivate JsonNode readJsonNode(JsonNode jsonNode, String field) {\n\t\treturn jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/PreAuthenticatedAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\n\n/**\n * This mixin class is used to serialize / deserialize\n * {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}.\n * This class register a custom deserializer\n * {@link PreAuthenticatedAuthenticationTokenDeserializer}.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see WebJacksonModule\n * @see SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonDeserialize(using = PreAuthenticatedAuthenticationTokenDeserializer.class)\nabstract class PreAuthenticatedAuthenticationTokenMixin {\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/SavedCookieMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson mixin class to serialize/deserialize\n * {@link org.springframework.security.web.savedrequest.SavedCookie} serialization\n * support.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see WebServletJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class SavedCookieMixin {\n\n\t@JsonCreator\n\tSavedCookieMixin(@JsonProperty(\"name\") String name, @JsonProperty(\"value\") String value,\n\t\t\t@JsonProperty(\"domain\") String domain, @JsonProperty(\"maxAge\") int maxAge,\n\t\t\t@JsonProperty(\"path\") String path, @JsonProperty(\"secure\") boolean secure) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/SwitchUserGrantedAuthorityMixIn.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\n\n/**\n * Jackson mixin class to serialize/deserialize {@link SwitchUserGrantedAuthority}.\n *\n * @author Sebastien Deleuze\n * @author Markus Heiden\n * @since 7.0\n * @see WebJacksonModule\n * @see WebServletJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class SwitchUserGrantedAuthorityMixIn {\n\n\t@JsonCreator\n\tSwitchUserGrantedAuthorityMixIn(@JsonProperty(\"role\") String role, @JsonProperty(\"source\") Authentication source) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/WebAuthenticationDetailsMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson mixin class to serialize/deserialize\n * {@link org.springframework.security.web.authentication.WebAuthenticationDetails}.\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see WebServletJacksonModule\n * @see org.springframework.security.jackson.SecurityJacksonModules\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\nclass WebAuthenticationDetailsMixin {\n\n\t@JsonCreator\n\tWebAuthenticationDetailsMixin(@JsonProperty(\"remoteAddress\") String remoteAddress,\n\t\t\t@JsonProperty(\"sessionId\") String sessionId) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/WebJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\nimport org.springframework.security.web.savedrequest.SavedCookie;\n\n/**\n * Jackson module for spring-security-web. This module register\n * {@link DefaultCsrfTokenMixin}, {@link PreAuthenticatedAuthenticationTokenMixin} and\n * {@link SwitchUserGrantedAuthorityMixIn}.\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @author Jitendra Singh\n * @since 7.0\n * @see SecurityJacksonModules\n */\n@SuppressWarnings(\"serial\")\npublic class WebJacksonModule extends SecurityJacksonModule {\n\n\tpublic WebJacksonModule() {\n\t\tsuper(WebJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(DefaultCsrfToken.class)\n\t\t\t.allowIfSubType(SavedCookie.class)\n\t\t\t.allowIfSubType(DefaultSavedRequest.class)\n\t\t\t.allowIfSubType(WebAuthenticationDetails.class)\n\t\t\t.allowIfSubType(PreAuthenticatedAuthenticationToken.class)\n\t\t\t.allowIfSubType(SwitchUserGrantedAuthority.class);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(DefaultCsrfToken.class, DefaultCsrfTokenMixin.class);\n\t\tcontext.setMixIn(PreAuthenticatedAuthenticationToken.class, PreAuthenticatedAuthenticationTokenMixin.class);\n\t\tcontext.setMixIn(SwitchUserGrantedAuthority.class, SwitchUserGrantedAuthorityMixIn.class);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/WebServletJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport jakarta.servlet.http.Cookie;\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\nimport org.springframework.security.web.savedrequest.SavedCookie;\nimport org.springframework.security.web.server.csrf.DefaultCsrfToken;\n\n/**\n * Jackson module for spring-security-web related to servlet. This module registers\n * {@link CookieMixin}, {@link SavedCookieMixin}, {@link DefaultSavedRequestMixin},\n * {@link WebAuthenticationDetailsMixin}, and {@link SwitchUserGrantedAuthorityMixIn}.\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Sebastien Deleuze\n * @author Boris Finkelshteyn\n * @since 7.0\n * @see SecurityJacksonModules\n */\n@SuppressWarnings(\"serial\")\npublic class WebServletJacksonModule extends SecurityJacksonModule {\n\n\tpublic WebServletJacksonModule() {\n\t\tsuper(WebServletJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(Cookie.class).allowIfSubType(DefaultCsrfToken.class);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(Cookie.class, CookieMixin.class);\n\t\tcontext.setMixIn(SavedCookie.class, SavedCookieMixin.class);\n\t\tcontext.setMixIn(DefaultSavedRequest.class, DefaultSavedRequestMixin.class);\n\t\tcontext.setMixIn(WebAuthenticationDetails.class, WebAuthenticationDetailsMixin.class);\n\t\tcontext.setMixIn(SwitchUserGrantedAuthority.class, SwitchUserGrantedAuthorityMixIn.class);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 3+ serialization support for web.\n */\n@NullMarked\npackage org.springframework.security.web.jackson;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/CookieDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.MissingNode;\nimport com.fasterxml.jackson.databind.node.NullNode;\nimport jakarta.servlet.http.Cookie;\n\n/**\n * Jackson deserializer for {@link Cookie}. This is needed because in most cases we don't\n * set {@link Cookie#getDomain()} property. So when jackson deserialize that json\n * {@link Cookie#setDomain(String)} throws {@link NullPointerException}. This is\n * registered with {@link CookieMixin} but you can also use it with your own mixin.\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see CookieMixin\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.CookieDeserializer} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nclass CookieDeserializer extends JsonDeserializer<Cookie> {\n\n\t@Override\n\tpublic Cookie deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {\n\t\tObjectMapper mapper = (ObjectMapper) jp.getCodec();\n\t\tJsonNode jsonNode = mapper.readTree(jp);\n\t\tCookie cookie = new Cookie(readJsonNode(jsonNode, \"name\").asText(), readJsonNode(jsonNode, \"value\").asText());\n\t\tcookie.setDomain(readJsonNode(jsonNode, \"domain\").asText());\n\t\tcookie.setMaxAge(readJsonNode(jsonNode, \"maxAge\").asInt(-1));\n\t\tcookie.setSecure(readJsonNode(jsonNode, \"secure\").asBoolean());\n\t\tcookie.setPath(readJsonNode(jsonNode, \"path\").asText());\n\t\tJsonNode attributes = readJsonNode(jsonNode, \"attributes\");\n\t\tcookie.setHttpOnly(readJsonNode(attributes, \"HttpOnly\") != null);\n\t\treturn cookie;\n\t}\n\n\tprivate JsonNode readJsonNode(JsonNode jsonNode, String field) {\n\t\treturn hasNonNullField(jsonNode, field) ? jsonNode.get(field) : MissingNode.getInstance();\n\t}\n\n\tprivate boolean hasNonNullField(JsonNode jsonNode, String field) {\n\t\treturn jsonNode.has(field) && !(jsonNode.get(field) instanceof NullNode);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/CookieMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\n/**\n * Mixin class to serialize/deserialize {@link jakarta.servlet.http.Cookie}\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new WebServletJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see WebServletJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.CookieMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonDeserialize(using = CookieDeserializer.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class CookieMixin {\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/DefaultCsrfTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson mixin class to serialize/deserialize\n * {@link org.springframework.security.web.csrf.DefaultCsrfToken} serialization support.\n *\n * <pre>\n * \t\tObjectMapper mapper = new ObjectMapper();\n *\t\tmapper.registerModule(new WebJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see WebJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.DefaultCsrfTokenMixin} based on Jackson\n * 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = \"@class\")\n@JsonIgnoreProperties(ignoreUnknown = true)\nclass DefaultCsrfTokenMixin {\n\n\t/**\n\t * JsonCreator constructor needed by Jackson to create\n\t * {@link org.springframework.security.web.csrf.DefaultCsrfToken} object.\n\t * @param headerName the name of the header\n\t * @param parameterName the parameter name\n\t * @param token the CSRF token value\n\t */\n\t@JsonCreator\n\tDefaultCsrfTokenMixin(@JsonProperty(\"headerName\") String headerName,\n\t\t\t@JsonProperty(\"parameterName\") String parameterName, @JsonProperty(\"token\") String token) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/DefaultSavedRequestMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\n\n/**\n * Jackson mixin class to serialize/deserialize {@link DefaultSavedRequest}. This mixin\n * use {@link org.springframework.security.web.savedrequest.DefaultSavedRequest.Builder}\n * to deserialized json.In order to use this mixin class you also need to register\n * {@link CookieMixin}.\n * <p>\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new WebServletJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see WebServletJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.DefaultCsrfTokenMixin} based on Jackson\n * 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonDeserialize(builder = DefaultSavedRequest.Builder.class)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\nabstract class DefaultSavedRequestMixin {\n\n\t@JsonInclude(JsonInclude.Include.NON_NULL)\n\t@Nullable String matchingRequestParameterName;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/PreAuthenticatedAuthenticationTokenDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.MissingNode;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\n\n/**\n * Custom deserializer for {@link PreAuthenticatedAuthenticationToken}. At the time of\n * deserialization it will invoke suitable constructor depending on the value of\n * <b>authenticated</b> property. It will ensure that the token's state must not change.\n * <p>\n * This deserializer is already registered with\n * {@link PreAuthenticatedAuthenticationTokenMixin} but you can also registered it with\n * your own mixin class.\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see PreAuthenticatedAuthenticationTokenMixin\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.PreAuthenticatedAuthenticationTokenDeserializer}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\nclass PreAuthenticatedAuthenticationTokenDeserializer extends JsonDeserializer<PreAuthenticatedAuthenticationToken> {\n\n\tprivate static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<>() {\n\t};\n\n\t/**\n\t * This method construct {@link PreAuthenticatedAuthenticationToken} object from\n\t * serialized json.\n\t * @param jp the JsonParser\n\t * @param ctxt the DeserializationContext\n\t * @return the user\n\t * @throws IOException if a exception during IO occurs\n\t * @throws JsonProcessingException if an error during JSON processing occurs\n\t */\n\t@Override\n\tpublic PreAuthenticatedAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt)\n\t\t\tthrows IOException, JsonProcessingException {\n\t\tObjectMapper mapper = (ObjectMapper) jp.getCodec();\n\t\tJsonNode jsonNode = mapper.readTree(jp);\n\t\tBoolean authenticated = readJsonNode(jsonNode, \"authenticated\").asBoolean();\n\t\tJsonNode principalNode = readJsonNode(jsonNode, \"principal\");\n\t\tObject principal = (!principalNode.isObject()) ? principalNode.asText()\n\t\t\t\t: mapper.readValue(principalNode.traverse(mapper), Object.class);\n\t\tObject credentials = readJsonNode(jsonNode, \"credentials\").asText();\n\t\tList<GrantedAuthority> authorities = mapper.readValue(readJsonNode(jsonNode, \"authorities\").traverse(mapper),\n\t\t\t\tGRANTED_AUTHORITY_LIST);\n\t\tPreAuthenticatedAuthenticationToken token = (!authenticated)\n\t\t\t\t? new PreAuthenticatedAuthenticationToken(principal, credentials)\n\t\t\t\t: new PreAuthenticatedAuthenticationToken(principal, credentials, authorities);\n\t\ttoken.setDetails(readJsonNode(jsonNode, \"details\"));\n\t\treturn token;\n\t}\n\n\tprivate JsonNode readJsonNode(JsonNode jsonNode, String field) {\n\t\treturn jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/PreAuthenticatedAuthenticationTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.jackson2.SimpleGrantedAuthorityMixin;\n\n/**\n * This mixin class is used to serialize / deserialize\n * {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}.\n * This class register a custom deserializer\n * {@link PreAuthenticatedAuthenticationTokenDeserializer}.\n *\n * In order to use this mixin you'll need to add 3 more mixin classes.\n * <ol>\n * <li>{@link SimpleGrantedAuthorityMixin}</li>\n * </ol>\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new CoreJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.PreAuthenticatedAuthenticationTokenMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = \"@class\")\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonDeserialize(using = PreAuthenticatedAuthenticationTokenDeserializer.class)\nabstract class PreAuthenticatedAuthenticationTokenMixin {\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/SavedCookieMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson mixin class to serialize/deserialize\n * {@link org.springframework.security.web.savedrequest.SavedCookie} serialization\n * support.\n *\n * <pre>\n * \t\tObjectMapper mapper = new ObjectMapper();\n *\t\tmapper.registerModule(new WebServletJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see WebServletJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.SavedCookieMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class SavedCookieMixin {\n\n\t@JsonCreator\n\tSavedCookieMixin(@JsonProperty(\"name\") String name, @JsonProperty(\"value\") String value,\n\t\t\t@JsonProperty(\"domain\") String domain, @JsonProperty(\"maxAge\") int maxAge,\n\t\t\t@JsonProperty(\"path\") String path, @JsonProperty(\"secure\") boolean secure) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/SwitchUserGrantedAuthorityMixIn.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\n\n/**\n * Jackson mixin class to serialize/deserialize {@link SwitchUserGrantedAuthority}.\n *\n * @author Markus Heiden\n * @since 6.3\n * @see WebJackson2Module\n * @see WebServletJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.SwitchUserGrantedAuthorityMixIn} based\n * on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class SwitchUserGrantedAuthorityMixIn {\n\n\t@JsonCreator\n\tSwitchUserGrantedAuthorityMixIn(@JsonProperty(\"role\") String role, @JsonProperty(\"source\") Authentication source) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/WebAuthenticationDetailsMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson mixin class to serialize/deserialize\n * {@link org.springframework.security.web.authentication.WebAuthenticationDetails}.\n *\n * <pre>\n * \tObjectMapper mapper = new ObjectMapper();\n *\tmapper.registerModule(new WebServletJackson2Module());\n * </pre>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see WebServletJackson2Module\n * @see org.springframework.security.jackson2.SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.jackson.SwitchUserGrantedAuthorityMixIn} based\n * on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)\n@JsonIgnoreProperties(ignoreUnknown = true)\n@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,\n\t\tisGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)\nclass WebAuthenticationDetailsMixin {\n\n\t@JsonCreator\n\tWebAuthenticationDetailsMixin(@JsonProperty(\"remoteAddress\") String remoteAddress,\n\t\t\t@JsonProperty(\"sessionId\") String sessionId) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/WebJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\n\n/**\n * Jackson module for spring-security-web. This module register\n * {@link DefaultCsrfTokenMixin}, {@link PreAuthenticatedAuthenticationTokenMixin} and\n * {@link SwitchUserGrantedAuthorityMixIn}. If no default typing enabled by default then\n * it'll enable it because typing info is needed to properly serialize/deserialize\n * objects. In order to use this module just add this module into your ObjectMapper\n * configuration.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new WebJackson2Module());\n * </pre>\n *\n * <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list of all\n * security modules.</b>\n *\n * @author Jitendra Singh\n * @since 4.2\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.jackson.WebJacksonModule} based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings({ \"serial\", \"removal\" })\npublic class WebJackson2Module extends SimpleModule {\n\n\tpublic WebJackson2Module() {\n\t\tsuper(WebJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tSecurityJackson2Modules.enableDefaultTyping(context.getOwner());\n\t\tcontext.setMixInAnnotations(DefaultCsrfToken.class, DefaultCsrfTokenMixin.class);\n\t\tcontext.setMixInAnnotations(PreAuthenticatedAuthenticationToken.class,\n\t\t\t\tPreAuthenticatedAuthenticationTokenMixin.class);\n\t\tcontext.setMixInAnnotations(SwitchUserGrantedAuthority.class, SwitchUserGrantedAuthorityMixIn.class);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/WebServletJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport jakarta.servlet.http.Cookie;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\nimport org.springframework.security.web.savedrequest.SavedCookie;\n\n/**\n * Jackson module for spring-security-web related to servlet. This module registers\n * {@link CookieMixin}, {@link SavedCookieMixin}, {@link DefaultSavedRequestMixin},\n * {@link WebAuthenticationDetailsMixin}, and {@link SwitchUserGrantedAuthorityMixIn}. If\n * no default typing is enabled by default then it will be enabled, because typing info is\n * needed to properly serialize/deserialize objects. In order to use this module just add\n * this module into your ObjectMapper configuration.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new WebServletJackson2Module());\n * </pre> <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list\n * of all security modules.</b>\n *\n * @author Boris Finkelshteyn\n * @since 5.1\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.jackson.WebServletJacksonModule} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings({ \"serial\", \"removal\" })\npublic class WebServletJackson2Module extends SimpleModule {\n\n\tpublic WebServletJackson2Module() {\n\t\tsuper(WebServletJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tSecurityJackson2Modules.enableDefaultTyping(context.getOwner());\n\t\tcontext.setMixInAnnotations(Cookie.class, CookieMixin.class);\n\t\tcontext.setMixInAnnotations(SavedCookie.class, SavedCookieMixin.class);\n\t\tcontext.setMixInAnnotations(DefaultSavedRequest.class, DefaultSavedRequestMixin.class);\n\t\tcontext.setMixInAnnotations(WebAuthenticationDetails.class, WebAuthenticationDetailsMixin.class);\n\t\tcontext.setMixInAnnotations(SwitchUserGrantedAuthority.class, SwitchUserGrantedAuthorityMixIn.class);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/jackson2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 2 serialization support for web.\n */\n@NullMarked\npackage org.springframework.security.web.jackson2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.method.annotation;\n\nimport java.lang.annotation.Annotation;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.support.WebDataBinderFactory;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.method.support.ModelAndViewContainer;\n\n/**\n * Allows resolving the {@link Authentication#getPrincipal()} using the\n * {@link AuthenticationPrincipal} annotation. For example, the following\n * {@link Controller}:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;MessageMapping(\"/im\")\n *     public void im(@AuthenticationPrincipal CustomUser customUser) {\n *         // do something with CustomUser\n *     }\n * }\n * </pre>\n *\n * <p>\n * Will resolve the CustomUser argument using {@link Authentication#getPrincipal()} from\n * the {@link SecurityContextHolder}. If the {@link Authentication} or\n * {@link Authentication#getPrincipal()} is null, it will return null. If the types do not\n * match, null will be returned unless\n * {@link AuthenticationPrincipal#errorOnInvalidType()} is true in which case a\n * {@link ClassCastException} will be thrown.\n *\n * <p>\n * Alternatively, users can create a custom meta annotation as shown below:\n *\n * <pre>\n * &#064;Target({ ElementType.PARAMETER })\n * &#064;Retention(RetentionPolicy.RUNTIME)\n * &#064;AuthenticationPrincipal\n * public @interface CurrentUser {\n * }\n * </pre>\n *\n * <p>\n * The custom annotation can then be used instead. For example:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;MessageMapping(\"/im\")\n *     public void im(@CurrentUser CustomUser customUser) {\n *         // do something with CustomUser\n *     }\n * }\n * </pre>\n *\n * @author Rob Winch\n * @author DingHao\n * @since 4.0\n */\npublic final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {\n\n\tprivate final Class<AuthenticationPrincipal> annotationType = AuthenticationPrincipal.class;\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate ExpressionParser parser = new SpelExpressionParser();\n\n\tprivate SecurityAnnotationScanner<AuthenticationPrincipal> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(AuthenticationPrincipal.class);\n\n\tprivate boolean useAnnotationTemplate = false;\n\n\tprivate @Nullable BeanResolver beanResolver;\n\n\t@Override\n\tpublic @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,\n\t\t\tNativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {\n\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\tif (authentication == null) {\n\t\t\treturn null;\n\t\t}\n\t\tObject principal = authentication.getPrincipal();\n\t\tAuthenticationPrincipal annotation = findMethodAnnotation(parameter);\n\t\tAssert.notNull(annotation, \"@AuthenticationPrincipal is required. Call supportsParameter first.\");\n\t\tString expressionToParse = annotation.expression();\n\t\tif (StringUtils.hasLength(expressionToParse)) {\n\t\t\tStandardEvaluationContext context = new StandardEvaluationContext();\n\t\t\tcontext.setRootObject(principal);\n\t\t\tcontext.setVariable(\"this\", principal);\n\t\t\t// https://github.com/spring-projects/spring-framework/issues/35371\n\t\t\tif (this.beanResolver != null) {\n\t\t\t\tcontext.setBeanResolver(this.beanResolver);\n\t\t\t}\n\t\t\tExpression expression = this.parser.parseExpression(expressionToParse);\n\t\t\tprincipal = expression.getValue(context);\n\t\t}\n\t\tif (principal != null && !ClassUtils.isAssignable(parameter.getParameterType(), principal.getClass())) {\n\t\t\tif (annotation.errorOnInvalidType()) {\n\t\t\t\tthrow new ClassCastException(principal + \" is not assignable to \" + parameter.getParameterType());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn principal;\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn findMethodAnnotation(parameter) != null;\n\t}\n\n\t/**\n\t * Sets the {@link BeanResolver} to be used on the expressions\n\t * @param beanResolver the {@link BeanResolver} to use\n\t */\n\tpublic void setBeanResolver(BeanResolver beanResolver) {\n\t\tthis.beanResolver = beanResolver;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Configure AuthenticationPrincipal template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param templateDefaults - whether to resolve AuthenticationPrincipal templates\n\t * parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(AuthenticationPrincipal.class, templateDefaults);\n\t\tthis.useAnnotationTemplate = templateDefaults != null;\n\t}\n\n\t/**\n\t * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.\n\t * {@link MethodParameter}\n\t * @param parameter the {@link MethodParameter} to search for an {@link Annotation}\n\t * @return the {@link Annotation} that was found or null.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate @Nullable AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) {\n\t\tif (this.useAnnotationTemplate) {\n\t\t\treturn this.scanner.scan(parameter.getParameter());\n\t\t}\n\t\tAuthenticationPrincipal annotation = parameter.getParameterAnnotation(this.annotationType);\n\t\tif (annotation != null) {\n\t\t\treturn annotation;\n\t\t}\n\t\tAnnotation[] annotationsToSearch = parameter.getParameterAnnotations();\n\t\tfor (Annotation toSearch : annotationsToSearch) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), this.annotationType);\n\t\t\tif (annotation != null) {\n\t\t\t\treturn MergedAnnotations.from(toSearch).get(this.annotationType).synthesize();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/method/annotation/CsrfTokenArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.method.annotation;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.bind.support.WebDataBinderFactory;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.method.support.ModelAndViewContainer;\n\n/**\n * Allows resolving the current {@link CsrfToken}. For example, the following\n * {@link RestController} will resolve the current {@link CsrfToken}:\n *\n * <pre>\n * <code>\n * &#064;RestController\n * public class MyController {\n *     &#064;MessageMapping(\"/im\")\n *     public CsrfToken csrf(CsrfToken token) {\n *         return token;\n *     }\n * }\n * </code> </pre>\n *\n * @author Rob Winch\n * @since 4.0\n */\npublic final class CsrfTokenArgumentResolver implements HandlerMethodArgumentResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn CsrfToken.class.equals(parameter.getParameterType());\n\t}\n\n\t@Override\n\tpublic @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,\n\t\t\tNativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {\n\t\tCsrfToken token = (CsrfToken) webRequest.getAttribute(CsrfToken.class.getName(),\n\t\t\t\tRequestAttributes.SCOPE_REQUEST);\n\t\treturn token;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.method.annotation;\n\nimport java.lang.annotation.Annotation;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.support.WebDataBinderFactory;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.method.support.ModelAndViewContainer;\n\n/**\n * Allows resolving the {@link SecurityContext} using the {@link CurrentSecurityContext}\n * annotation. For example, the following {@link Controller}:\n *\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;RequestMapping(\"/im\")\n *     public void security(@CurrentSecurityContext SecurityContext context) {\n *         // do something with context\n *     }\n * }\n * </pre>\n *\n * it can also support the spring SPEL expression to get the value from SecurityContext\n * <pre>\n * &#64;Controller\n * public class MyController {\n *     &#64;RequestMapping(\"/im\")\n *     public void security(@CurrentSecurityContext(expression=\"authentication\") Authentication authentication) {\n *         // do something with context\n *     }\n * }\n * </pre>\n *\n * <p>\n * Will resolve the {@link SecurityContext} argument using\n * {@link SecurityContextHolder#getContext()} from the {@link SecurityContextHolder}. If\n * the {@link SecurityContext} is {@code null}, it will return {@code null}. If the types\n * do not match, {@code null} will be returned unless\n * {@link CurrentSecurityContext#errorOnInvalidType()} is {@code true} in which case a\n * {@link ClassCastException} will be thrown.\n * </p>\n *\n * @author Dan Zheng\n * @author DingHao\n * @since 5.2\n */\npublic final class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate ExpressionParser parser = new SpelExpressionParser();\n\n\tprivate final Class<CurrentSecurityContext> annotationType = CurrentSecurityContext.class;\n\n\tprivate SecurityAnnotationScanner<CurrentSecurityContext> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(this.annotationType);\n\n\tprivate boolean useAnnotationTemplate = false;\n\n\tprivate @Nullable BeanResolver beanResolver;\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn SecurityContext.class.isAssignableFrom(parameter.getParameterType())\n\t\t\t\t|| findMethodAnnotation(parameter) != null;\n\t}\n\n\t@Override\n\tpublic @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,\n\t\t\tNativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {\n\t\tSecurityContext securityContext = this.securityContextHolderStrategy.getContext();\n\t\tif (securityContext == null) {\n\t\t\treturn null;\n\t\t}\n\t\tCurrentSecurityContext annotation = findMethodAnnotation(parameter);\n\t\tif (annotation != null) {\n\t\t\treturn resolveSecurityContextFromAnnotation(parameter, annotation, securityContext);\n\t\t}\n\n\t\treturn securityContext;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t/**\n\t * Set the {@link BeanResolver} to be used on the expressions\n\t * @param beanResolver the {@link BeanResolver} to use\n\t */\n\tpublic void setBeanResolver(BeanResolver beanResolver) {\n\t\tAssert.notNull(beanResolver, \"beanResolver cannot be null\");\n\t\tthis.beanResolver = beanResolver;\n\t}\n\n\t/**\n\t * Configure CurrentSecurityContext template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param templateDefaults - whether to resolve CurrentSecurityContext templates\n\t * parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.useAnnotationTemplate = templateDefaults != null;\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(CurrentSecurityContext.class, templateDefaults);\n\t}\n\n\tprivate @Nullable Object resolveSecurityContextFromAnnotation(MethodParameter parameter,\n\t\t\tCurrentSecurityContext annotation, SecurityContext securityContext) {\n\t\tObject securityContextResult = securityContext;\n\t\tString expressionToParse = annotation.expression();\n\t\tif (StringUtils.hasLength(expressionToParse)) {\n\t\t\tStandardEvaluationContext context = new StandardEvaluationContext();\n\t\t\tcontext.setRootObject(securityContext);\n\t\t\tcontext.setVariable(\"this\", securityContext);\n\t\t\t// https://github.com/spring-projects/spring-framework/issues/35371\n\t\t\tif (this.beanResolver != null) {\n\t\t\t\tcontext.setBeanResolver(this.beanResolver);\n\t\t\t}\n\t\t\tExpression expression = this.parser.parseExpression(expressionToParse);\n\t\t\tsecurityContextResult = expression.getValue(context);\n\t\t}\n\t\tif (securityContextResult != null\n\t\t\t\t&& !parameter.getParameterType().isAssignableFrom(securityContextResult.getClass())) {\n\t\t\tif (annotation.errorOnInvalidType()) {\n\t\t\t\tthrow new ClassCastException(\n\t\t\t\t\t\tsecurityContextResult + \" is not assignable to \" + parameter.getParameterType());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn securityContextResult;\n\t}\n\n\t/**\n\t * Obtain the specified {@link Annotation} on the specified {@link MethodParameter}.\n\t * @param parameter the {@link MethodParameter} to search for an {@link Annotation}\n\t * @return the {@link Annotation} that was found or null.\n\t */\n\tprivate @Nullable CurrentSecurityContext findMethodAnnotation(MethodParameter parameter) {\n\t\tif (this.useAnnotationTemplate) {\n\t\t\treturn this.scanner.scan(parameter.getParameter());\n\t\t}\n\t\tCurrentSecurityContext annotation = parameter.getParameterAnnotation(this.annotationType);\n\t\tif (annotation != null) {\n\t\t\treturn annotation;\n\t\t}\n\t\tAnnotation[] annotationsToSearch = parameter.getParameterAnnotations();\n\t\tfor (Annotation toSearch : annotationsToSearch) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), this.annotationType);\n\t\t\tif (annotation != null) {\n\t\t\t\treturn MergedAnnotations.from(toSearch).get(this.annotationType).synthesize();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/method/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for Spring Framework's handler method processing.\n */\n@NullMarked\npackage org.springframework.security.web.method.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security's web security module. Classes which have a dependency on the Servlet\n * API can be found here.\n */\n@NullMarked\npackage org.springframework.security.web;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.reactive.result.method.annotation;\n\nimport java.lang.annotation.Annotation;\n\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.util.ClassUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.BindingContext;\nimport org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Resolves the Authentication\n *\n * @author Rob Winch\n * @author DingHao\n * @since 5.0\n */\npublic class AuthenticationPrincipalArgumentResolver extends HandlerMethodArgumentResolverSupport {\n\n\tprivate ExpressionParser parser = new SpelExpressionParser();\n\n\tprivate final Class<AuthenticationPrincipal> annotationType = AuthenticationPrincipal.class;\n\n\tprivate SecurityAnnotationScanner<AuthenticationPrincipal> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(this.annotationType);\n\n\tprivate boolean useAnnotationTemplate = false;\n\n\tprivate @Nullable BeanResolver beanResolver;\n\n\tpublic AuthenticationPrincipalArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {\n\t\tsuper(adapterRegistry);\n\t}\n\n\t/**\n\t * Sets the {@link BeanResolver} to be used on the expressions\n\t * @param beanResolver the {@link BeanResolver} to use\n\t */\n\tpublic void setBeanResolver(BeanResolver beanResolver) {\n\t\tthis.beanResolver = beanResolver;\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn findMethodAnnotation(parameter) != null;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,\n\t\t\tServerWebExchange exchange) {\n\t\tReactiveAdapter adapter = getAdapterRegistry().getAdapter(parameter.getParameterType());\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t.mapNotNull(SecurityContext::getAuthentication)\n\t\t\t.flatMap((authentication) -> {\n\t\t\t\tMono<Object> principal = Mono.justOrEmpty(resolvePrincipal(parameter, authentication.getPrincipal()));\n\t\t\t\treturn (adapter != null) ? Mono.just(adapter.fromPublisher(principal)) : principal;\n\t\t\t});\n\t}\n\n\t@SuppressWarnings(\"NullAway\") // https://github.com/spring-projects/spring-framework/issues/35371\n\tprivate @Nullable Object resolvePrincipal(MethodParameter parameter, @Nullable Object principal) {\n\t\tAuthenticationPrincipal annotation = findMethodAnnotation(parameter);\n\t\tif (annotation == null) {\n\t\t\t// FIXME: Add test\n\t\t\treturn null;\n\t\t}\n\t\tString expressionToParse = annotation.expression();\n\t\tif (StringUtils.hasLength(expressionToParse)) {\n\t\t\tStandardEvaluationContext context = new StandardEvaluationContext();\n\t\t\tcontext.setRootObject(principal);\n\t\t\tcontext.setVariable(\"this\", principal);\n\t\t\tcontext.setBeanResolver(this.beanResolver);\n\t\t\tExpression expression = this.parser.parseExpression(expressionToParse);\n\t\t\tprincipal = expression.getValue(context);\n\t\t}\n\t\tif (isInvalidType(parameter, principal)) {\n\t\t\tif (annotation.errorOnInvalidType()) {\n\t\t\t\tthrow new ClassCastException(principal + \" is not assignable to \" + parameter.getParameterType());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn principal;\n\t}\n\n\tprivate boolean isInvalidType(MethodParameter parameter, @Nullable Object principal) {\n\t\tif (principal == null) {\n\t\t\treturn false;\n\t\t}\n\t\tClass<?> typeToCheck = parameter.getParameterType();\n\t\tboolean isParameterPublisher = Publisher.class.isAssignableFrom(parameter.getParameterType());\n\t\tif (isParameterPublisher) {\n\t\t\tResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);\n\t\t\tClass<?> genericType = resolvableType.resolveGeneric(0);\n\t\t\tif (genericType == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttypeToCheck = genericType;\n\t\t}\n\t\treturn !ClassUtils.isAssignable(typeToCheck, principal.getClass());\n\t}\n\n\t/**\n\t * Configure AuthenticationPrincipal template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param templateDefaults - whether to resolve AuthenticationPrincipal templates\n\t * parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.useAnnotationTemplate = templateDefaults != null;\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(AuthenticationPrincipal.class, templateDefaults);\n\t}\n\n\t/**\n\t * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.\n\t * {@link MethodParameter}\n\t * @param parameter the {@link MethodParameter} to search for an {@link Annotation}\n\t * @return the {@link Annotation} that was found or null.\n\t */\n\tprivate @Nullable AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) {\n\t\tif (this.useAnnotationTemplate) {\n\t\t\treturn this.scanner.scan(parameter.getParameter());\n\t\t}\n\t\tAuthenticationPrincipal annotation = parameter.getParameterAnnotation(this.annotationType);\n\t\tif (annotation != null) {\n\t\t\treturn annotation;\n\t\t}\n\t\tAnnotation[] annotationsToSearch = parameter.getParameterAnnotations();\n\t\tfor (Annotation toSearch : annotationsToSearch) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), this.annotationType);\n\t\t\tif (annotation != null) {\n\t\t\t\treturn MergedAnnotations.from(toSearch).get(this.annotationType).synthesize();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/CurrentSecurityContextArgumentResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.reactive.result.method.annotation;\n\nimport java.lang.annotation.Annotation;\n\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.MergedAnnotations;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanner;\nimport org.springframework.security.core.annotation.SecurityAnnotationScanners;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.BindingContext;\nimport org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Resolves the {@link SecurityContext}\n *\n * @author Dan Zheng\n * @author DingHao\n * @since 5.2\n */\npublic class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumentResolverSupport {\n\n\tprivate ExpressionParser parser = new SpelExpressionParser();\n\n\tprivate final Class<CurrentSecurityContext> annotationType = CurrentSecurityContext.class;\n\n\tprivate SecurityAnnotationScanner<CurrentSecurityContext> scanner = SecurityAnnotationScanners\n\t\t.requireUnique(this.annotationType);\n\n\tprivate boolean useAnnotationTemplate = false;\n\n\tprivate @Nullable BeanResolver beanResolver;\n\n\tpublic CurrentSecurityContextArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {\n\t\tsuper(adapterRegistry);\n\t}\n\n\t/**\n\t * Sets the {@link BeanResolver} to be used on the expressions\n\t * @param beanResolver the {@link BeanResolver} to use\n\t */\n\tpublic void setBeanResolver(BeanResolver beanResolver) {\n\t\tAssert.notNull(beanResolver, \"beanResolver cannot be null\");\n\t\tthis.beanResolver = beanResolver;\n\t}\n\n\t/**\n\t * Configure CurrentSecurityContext template resolution\n\t * <p>\n\t * By default, this value is <code>null</code>, which indicates that templates should\n\t * not be resolved.\n\t * @param templateDefaults - whether to resolve CurrentSecurityContext templates\n\t * parameters\n\t * @since 6.4\n\t */\n\tpublic void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {\n\t\tthis.useAnnotationTemplate = templateDefaults != null;\n\t\tthis.scanner = SecurityAnnotationScanners.requireUnique(CurrentSecurityContext.class, templateDefaults);\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(MethodParameter parameter) {\n\t\treturn isMonoSecurityContext(parameter) || findMethodAnnotation(parameter) != null;\n\t}\n\n\tprivate boolean isMonoSecurityContext(MethodParameter parameter) {\n\t\tboolean isParameterPublisher = Publisher.class.isAssignableFrom(parameter.getParameterType());\n\t\tif (isParameterPublisher) {\n\t\t\tResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);\n\t\t\tClass<?> genericType = resolvableType.resolveGeneric(0);\n\t\t\tif (genericType == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn SecurityContext.class.isAssignableFrom(genericType);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,\n\t\t\tServerWebExchange exchange) {\n\t\tReactiveAdapter adapter = getAdapterRegistry().getAdapter(parameter.getParameterType());\n\t\tMono<SecurityContext> reactiveSecurityContext = ReactiveSecurityContextHolder.getContext();\n\t\tif (reactiveSecurityContext == null) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\treturn reactiveSecurityContext.flatMap((securityContext) -> {\n\t\t\tMono<Object> resolvedSecurityContext = Mono.justOrEmpty(resolveSecurityContext(parameter, securityContext));\n\t\t\treturn (adapter != null) ? Mono.just(adapter.fromPublisher(resolvedSecurityContext))\n\t\t\t\t\t: resolvedSecurityContext;\n\t\t});\n\n\t}\n\n\t/**\n\t * resolve the expression from {@link CurrentSecurityContext} annotation to get the\n\t * value.\n\t * @param parameter the method parameter.\n\t * @param securityContext the security context.\n\t * @return the resolved object from expression.\n\t */\n\tprivate @Nullable Object resolveSecurityContext(MethodParameter parameter, SecurityContext securityContext) {\n\t\tCurrentSecurityContext annotation = findMethodAnnotation(parameter);\n\t\tif (annotation != null) {\n\t\t\treturn resolveSecurityContextFromAnnotation(annotation, parameter, securityContext);\n\t\t}\n\t\treturn securityContext;\n\t}\n\n\t@SuppressWarnings(\"NullAway\") // https://github.com/spring-projects/spring-framework/issues/35371\n\tprivate @Nullable Object resolveSecurityContextFromAnnotation(CurrentSecurityContext annotation,\n\t\t\tMethodParameter parameter, Object securityContext) {\n\t\tObject securityContextResult = securityContext;\n\t\tString expressionToParse = annotation.expression();\n\t\tif (StringUtils.hasLength(expressionToParse)) {\n\t\t\tStandardEvaluationContext context = new StandardEvaluationContext();\n\t\t\tcontext.setRootObject(securityContext);\n\t\t\tcontext.setVariable(\"this\", securityContext);\n\t\t\tcontext.setBeanResolver(this.beanResolver);\n\t\t\tExpression expression = this.parser.parseExpression(expressionToParse);\n\t\t\tsecurityContextResult = expression.getValue(context);\n\t\t}\n\t\tif (isInvalidType(parameter, securityContextResult)) {\n\t\t\tif (annotation.errorOnInvalidType()) {\n\t\t\t\tthrow new ClassCastException(\n\t\t\t\t\t\tsecurityContextResult + \" is not assignable to \" + parameter.getParameterType());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn securityContextResult;\n\t}\n\n\t/**\n\t * check if the retrieved value match with the parameter type.\n\t * @param parameter the method parameter.\n\t * @param reactiveSecurityContext the security context.\n\t * @return true = is not invalid type.\n\t */\n\tprivate boolean isInvalidType(MethodParameter parameter, @Nullable Object reactiveSecurityContext) {\n\t\tif (reactiveSecurityContext == null) {\n\t\t\treturn false;\n\t\t}\n\t\tClass<?> typeToCheck = parameter.getParameterType();\n\t\tboolean isParameterPublisher = Publisher.class.isAssignableFrom(parameter.getParameterType());\n\t\tif (isParameterPublisher) {\n\t\t\tResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);\n\t\t\tClass<?> genericType = resolvableType.resolveGeneric(0);\n\t\t\tif (genericType == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttypeToCheck = genericType;\n\t\t}\n\t\treturn !typeToCheck.isAssignableFrom(reactiveSecurityContext.getClass());\n\t}\n\n\t/**\n\t * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.\n\t * @param parameter the {@link MethodParameter} to search for an {@link Annotation}\n\t * @return the {@link Annotation} that was found or null.\n\t */\n\tprivate @Nullable CurrentSecurityContext findMethodAnnotation(MethodParameter parameter) {\n\t\tif (this.useAnnotationTemplate) {\n\t\t\treturn this.scanner.scan(parameter.getParameter());\n\t\t}\n\t\tCurrentSecurityContext annotation = parameter.getParameterAnnotation(this.annotationType);\n\t\tif (annotation != null) {\n\t\t\treturn annotation;\n\t\t}\n\t\tAnnotation[] annotationsToSearch = parameter.getParameterAnnotations();\n\t\tfor (Annotation toSearch : annotationsToSearch) {\n\t\t\tannotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), this.annotationType);\n\t\t\tif (annotation != null) {\n\t\t\t\treturn MergedAnnotations.from(toSearch).get(this.annotationType).synthesize();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for Spring Framework's reactive handler method processing.\n */\n@NullMarked\npackage org.springframework.security.web.reactive.result.method.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.reactive.result.view;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.NonNull;\n\nimport org.springframework.security.web.server.csrf.CsrfToken;\nimport org.springframework.web.reactive.result.view.RequestDataValueProcessor;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class CsrfRequestDataValueProcessor implements RequestDataValueProcessor {\n\n\t/**\n\t * The default request attribute to look for a {@link CsrfToken}.\n\t */\n\tpublic static final String DEFAULT_CSRF_ATTR_NAME = \"_csrf\";\n\n\tprivate static final Pattern DISABLE_CSRF_TOKEN_PATTERN = Pattern.compile(\"(?i)^(GET|HEAD|TRACE|OPTIONS)$\");\n\n\tprivate static final String DISABLE_CSRF_TOKEN_ATTR = \"DISABLE_CSRF_TOKEN_ATTR\";\n\n\t@Override\n\tpublic String processAction(ServerWebExchange exchange, String action, String httpMethod) {\n\t\tif (httpMethod != null && DISABLE_CSRF_TOKEN_PATTERN.matcher(httpMethod).matches()) {\n\t\t\texchange.getAttributes().put(DISABLE_CSRF_TOKEN_ATTR, Boolean.TRUE);\n\t\t}\n\t\telse {\n\t\t\texchange.getAttributes().remove(DISABLE_CSRF_TOKEN_ATTR);\n\t\t}\n\t\treturn action;\n\t}\n\n\t@Override\n\tpublic String processFormFieldValue(ServerWebExchange exchange, String name, String value, String type) {\n\t\treturn value;\n\t}\n\n\t@NonNull\n\t@Override\n\tpublic Map<String, String> getExtraHiddenFields(ServerWebExchange exchange) {\n\t\tif (Boolean.TRUE.equals(exchange.getAttribute(DISABLE_CSRF_TOKEN_ATTR))) {\n\t\t\texchange.getAttributes().remove(DISABLE_CSRF_TOKEN_ATTR);\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tCsrfToken token = exchange.getAttribute(DEFAULT_CSRF_ATTR_NAME);\n\t\tif (token == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\treturn Collections.singletonMap(token.getParameterName(), token.getToken());\n\t}\n\n\t@Override\n\tpublic String processUrl(ServerWebExchange exchange, String url) {\n\t\treturn url;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/reactive/result/view/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for Spring Framework's reactive view processing.\n */\n@NullMarked\npackage org.springframework.security.web.reactive.result.view;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/CookieRequestCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\nimport org.springframework.web.util.WebUtils;\n\n/**\n * An Implementation of {@code RequestCache} which saves the original request URI in a\n * cookie.\n *\n * @author Zeeshan Adnan\n * @since 5.4\n */\npublic class CookieRequestCache implements RequestCache {\n\n\tprivate RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;\n\n\tprotected final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate static final String COOKIE_NAME = \"REDIRECT_URI\";\n\n\tprivate static final int COOKIE_MAX_AGE = -1;\n\n\tprivate Consumer<Cookie> cookieCustomizer = (cookie) -> {\n\t};\n\n\t@Override\n\tpublic void saveRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tthis.logger.debug(\"Request not saved as configured RequestMatcher did not match\");\n\t\t\treturn;\n\t\t}\n\t\tString redirectUrl = UrlUtils.buildFullRequestUrl(request);\n\t\tCookie savedCookie = new Cookie(COOKIE_NAME, encodeCookie(redirectUrl));\n\t\tsavedCookie.setMaxAge(COOKIE_MAX_AGE);\n\t\tsavedCookie.setSecure(request.isSecure());\n\t\tsavedCookie.setPath(getCookiePath(request));\n\t\tsavedCookie.setHttpOnly(true);\n\t\tthis.cookieCustomizer.accept(savedCookie);\n\t\tresponse.addCookie(savedCookie);\n\t}\n\n\t@Override\n\tpublic @Nullable SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\tCookie savedRequestCookie = WebUtils.getCookie(request, COOKIE_NAME);\n\t\tif (savedRequestCookie == null) {\n\t\t\treturn null;\n\t\t}\n\t\tString originalURI = decodeCookie(savedRequestCookie.getValue());\n\t\tif (originalURI == null) {\n\t\t\treturn null;\n\t\t}\n\t\tUriComponents uriComponents = UriComponentsBuilder.fromUriString(originalURI).build();\n\t\tDefaultSavedRequest.Builder builder = new DefaultSavedRequest.Builder();\n\t\tint port = getPort(uriComponents);\n\t\treturn builder.setScheme(uriComponents.getScheme())\n\t\t\t.setServerName(uriComponents.getHost())\n\t\t\t.setRequestURI(uriComponents.getPath())\n\t\t\t.setQueryString(uriComponents.getQuery())\n\t\t\t.setServerPort(port)\n\t\t\t.setMethod(request.getMethod())\n\t\t\t.setLocales(Collections.list(request.getLocales()))\n\t\t\t.setParameters(request.getParameterMap())\n\t\t\t.build();\n\t}\n\n\tprivate int getPort(UriComponents uriComponents) {\n\t\tint port = uriComponents.getPort();\n\t\tif (port != -1) {\n\t\t\treturn port;\n\t\t}\n\t\tif (\"https\".equalsIgnoreCase(uriComponents.getScheme())) {\n\t\t\treturn 443;\n\t\t}\n\t\treturn 80;\n\t}\n\n\t@Override\n\tpublic @Nullable HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\t@Nullable SavedRequest saved = this.getRequest(request, response);\n\t\tif (!this.matchesSavedRequest(request, saved)) {\n\t\t\tthis.logger.debug(\"saved request doesn't match\");\n\t\t\treturn null;\n\t\t}\n\t\tthis.removeRequest(request, response);\n\t\t// we know that saved is non-null because matchesSavedRequest = true\n\t\treturn new SavedRequestAwareWrapper(Objects.requireNonNull(saved), request);\n\t}\n\n\t@Override\n\tpublic void removeRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\tCookie removeSavedRequestCookie = new Cookie(COOKIE_NAME, \"\");\n\t\tremoveSavedRequestCookie.setSecure(request.isSecure());\n\t\tremoveSavedRequestCookie.setHttpOnly(true);\n\t\tremoveSavedRequestCookie.setPath(getCookiePath(request));\n\t\tremoveSavedRequestCookie.setMaxAge(0);\n\t\tresponse.addCookie(removeSavedRequestCookie);\n\t}\n\n\tprivate static String encodeCookie(String cookieValue) {\n\t\treturn Base64.getEncoder().encodeToString(cookieValue.getBytes());\n\t}\n\n\tprivate @Nullable String decodeCookie(String encodedCookieValue) {\n\t\ttry {\n\t\t\treturn new String(Base64.getDecoder().decode(encodedCookieValue.getBytes()));\n\t\t}\n\t\tcatch (IllegalArgumentException ex) {\n\t\t\tthis.logger.debug(\"Failed decode cookie value \" + encodedCookieValue);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static String getCookiePath(HttpServletRequest request) {\n\t\tString contextPath = request.getContextPath();\n\t\treturn (StringUtils.hasLength(contextPath)) ? contextPath : \"/\";\n\t}\n\n\tprivate boolean matchesSavedRequest(HttpServletRequest request, @Nullable SavedRequest savedRequest) {\n\t\tif (savedRequest == null) {\n\t\t\treturn false;\n\t\t}\n\t\tString currentUrl = UrlUtils.buildFullRequestUrl(request);\n\t\treturn savedRequest.getRedirectUrl().equals(currentUrl);\n\t}\n\n\t/**\n\t * Allows selective use of saved requests for a subset of requests. By default any\n\t * request will be cached by the {@code saveRequest} method.\n\t * <p>\n\t * If set, only matching requests will be cached.\n\t * @param requestMatcher a request matching strategy which defines which requests\n\t * should be cached.\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher should not be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\t/**\n\t * Sets the {@link Consumer}, allowing customization of cookie.\n\t * @param cookieCustomizer customize for cookie\n\t * @since 6.4\n\t */\n\tpublic void setCookieCustomizer(Consumer<Cookie> cookieCustomizer) {\n\t\tAssert.notNull(cookieCustomizer, \"cookieCustomizer cannot be null\");\n\t\tthis.cookieCustomizer = cookieCustomizer;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/DefaultSavedRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.TreeMap;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Represents central information from a {@code HttpServletRequest}.\n * <p>\n * This class is used by\n * {@link org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter}\n * and {@link org.springframework.security.web.savedrequest.SavedRequestAwareWrapper} to\n * reproduce the request after successful authentication. An instance of this class is\n * stored at the time of an authentication exception by\n * {@link org.springframework.security.web.access.ExceptionTranslationFilter}.\n * <p>\n * <em>IMPLEMENTATION NOTE</em>: It is assumed that this object is accessed only from the\n * context of a single thread, so no synchronization around internal collection classes is\n * performed.\n * <p>\n * This class is based on code in Apache Tomcat.\n *\n * @author Craig McClanahan\n * @author Andrey Grebnev\n * @author Ben Alex\n * @author Luke Taylor\n * @author Ngoc Nhan\n */\npublic class DefaultSavedRequest implements SavedRequest {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprotected static final Log logger = LogFactory.getLog(DefaultSavedRequest.class);\n\n\tprivate static final String HEADER_IF_NONE_MATCH = \"If-None-Match\";\n\n\tprivate static final String HEADER_IF_MODIFIED_SINCE = \"If-Modified-Since\";\n\n\tprivate final ArrayList<SavedCookie> cookies = new ArrayList<>();\n\n\tprivate final ArrayList<Locale> locales = new ArrayList<>();\n\n\tprivate final Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);\n\n\tprivate final Map<String, String[]> parameters = new TreeMap<>();\n\n\tprivate final @Nullable String contextPath;\n\n\tprivate final String method;\n\n\tprivate final @Nullable String pathInfo;\n\n\tprivate final @Nullable String queryString;\n\n\tprivate final String requestURI;\n\n\tprivate final @Nullable String requestURL;\n\n\tprivate final String scheme;\n\n\tprivate final String serverName;\n\n\tprivate final @Nullable String servletPath;\n\n\tprivate final int serverPort;\n\n\tprivate final @Nullable String matchingRequestParameterName;\n\n\tpublic DefaultSavedRequest(HttpServletRequest request) {\n\t\tthis(request, null);\n\t}\n\n\tpublic DefaultSavedRequest(HttpServletRequest request, @Nullable String matchingRequestParameterName) {\n\t\tAssert.notNull(request, \"Request required\");\n\t\t// Cookies\n\t\taddCookies(request.getCookies());\n\t\t// Headers\n\t\tEnumeration<String> names = request.getHeaderNames();\n\t\twhile (names.hasMoreElements()) {\n\t\t\tString name = names.nextElement();\n\t\t\t// Skip If-Modified-Since and If-None-Match header. SEC-1412, SEC-1624.\n\t\t\tif (HEADER_IF_MODIFIED_SINCE.equalsIgnoreCase(name) || HEADER_IF_NONE_MATCH.equalsIgnoreCase(name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tEnumeration<String> values = request.getHeaders(name);\n\t\t\twhile (values.hasMoreElements()) {\n\t\t\t\tthis.addHeader(name, values.nextElement());\n\t\t\t}\n\t\t}\n\t\t// Locales\n\t\taddLocales(request.getLocales());\n\t\t// Parameters\n\t\taddParameters(request.getParameterMap());\n\t\t// Primitives\n\t\tthis.method = request.getMethod();\n\t\tthis.pathInfo = request.getPathInfo();\n\t\tthis.queryString = request.getQueryString();\n\t\tthis.requestURI = request.getRequestURI();\n\t\tthis.serverPort = request.getServerPort();\n\t\tthis.requestURL = request.getRequestURL().toString();\n\t\tthis.scheme = request.getScheme();\n\t\tthis.serverName = request.getServerName();\n\t\tthis.contextPath = request.getContextPath();\n\t\tthis.servletPath = request.getServletPath();\n\t\tthis.matchingRequestParameterName = matchingRequestParameterName;\n\t}\n\n\t/**\n\t * Private constructor invoked through Builder\n\t */\n\tprivate DefaultSavedRequest(Builder builder) {\n\t\tthis.contextPath = builder.contextPath;\n\t\tthis.method = (builder.method != null) ? builder.method : \"GET\";\n\t\tthis.pathInfo = builder.pathInfo;\n\t\tthis.queryString = builder.queryString;\n\t\tthis.requestURI = Objects.requireNonNull(builder.requestURI);\n\t\tthis.requestURL = builder.requestURL;\n\t\tthis.scheme = Objects.requireNonNull(builder.scheme);\n\t\tthis.serverName = Objects.requireNonNull(builder.serverName);\n\t\tthis.servletPath = builder.servletPath;\n\t\tthis.serverPort = builder.serverPort;\n\t\tthis.matchingRequestParameterName = builder.matchingRequestParameterName;\n\t}\n\n\t/**\n\t * @since 4.2\n\t */\n\tprivate void addCookies(Cookie[] cookies) {\n\t\tif (cookies != null) {\n\t\t\tfor (Cookie cookie : cookies) {\n\t\t\t\tthis.addCookie(cookie);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void addCookie(Cookie cookie) {\n\t\tthis.cookies.add(new SavedCookie(cookie));\n\t}\n\n\tprivate void addHeader(String name, String value) {\n\t\tList<String> values = this.headers.computeIfAbsent(name, (key) -> new ArrayList<>());\n\t\tvalues.add(value);\n\t}\n\n\t/**\n\t * @since 4.2\n\t */\n\tprivate void addLocales(Enumeration<Locale> locales) {\n\t\twhile (locales.hasMoreElements()) {\n\t\t\tLocale locale = locales.nextElement();\n\t\t\tthis.addLocale(locale);\n\t\t}\n\t}\n\n\tprivate void addLocale(Locale locale) {\n\t\tthis.locales.add(locale);\n\t}\n\n\t/**\n\t * @since 4.2\n\t */\n\tprivate void addParameters(Map<String, String[]> parameters) {\n\t\tif (ObjectUtils.isEmpty(parameters)) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (Map.Entry<String, String[]> entry : parameters.entrySet()) {\n\t\t\tString name = entry.getKey();\n\t\t\tString[] values = entry.getValue();\n\t\t\tif (values != null) {\n\t\t\t\tthis.parameters.put(name, values);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic @Nullable String getContextPath() {\n\t\treturn this.contextPath;\n\t}\n\n\t@Override\n\tpublic List<Cookie> getCookies() {\n\t\tList<Cookie> cookieList = new ArrayList<>(this.cookies.size());\n\t\tfor (SavedCookie savedCookie : this.cookies) {\n\t\t\tcookieList.add(savedCookie.getCookie());\n\t\t}\n\t\treturn cookieList;\n\t}\n\n\t/**\n\t * Indicates the URL that the user agent used for this request.\n\t * @return the full URL of this request\n\t */\n\t@Override\n\tpublic String getRedirectUrl() {\n\t\tString queryString = createQueryString(this.queryString, this.matchingRequestParameterName);\n\t\treturn UrlUtils.buildFullRequestUrl(this.scheme, this.serverName, this.serverPort, this.requestURI,\n\t\t\t\tqueryString);\n\t}\n\n\t@Override\n\tpublic Collection<String> getHeaderNames() {\n\t\treturn this.headers.keySet();\n\t}\n\n\t@Override\n\tpublic List<String> getHeaderValues(String name) {\n\t\tList<String> values = this.headers.get(name);\n\t\treturn (values != null) ? values : Collections.emptyList();\n\t}\n\n\t@Override\n\tpublic List<Locale> getLocales() {\n\t\treturn this.locales;\n\t}\n\n\t@Override\n\tpublic String getMethod() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic Map<String, String[]> getParameterMap() {\n\t\treturn this.parameters;\n\t}\n\n\tpublic Collection<String> getParameterNames() {\n\t\treturn this.parameters.keySet();\n\t}\n\n\t@Override\n\tpublic String @Nullable [] getParameterValues(String name) {\n\t\treturn this.parameters.get(name);\n\t}\n\n\tpublic @Nullable String getPathInfo() {\n\t\treturn this.pathInfo;\n\t}\n\n\tpublic @Nullable String getQueryString() {\n\t\treturn (this.queryString);\n\t}\n\n\tpublic @Nullable String getRequestURI() {\n\t\treturn (this.requestURI);\n\t}\n\n\tpublic @Nullable String getRequestURL() {\n\t\treturn this.requestURL;\n\t}\n\n\tpublic @Nullable String getScheme() {\n\t\treturn this.scheme;\n\t}\n\n\tpublic @Nullable String getServerName() {\n\t\treturn this.serverName;\n\t}\n\n\tpublic int getServerPort() {\n\t\treturn this.serverPort;\n\t}\n\n\tpublic @Nullable String getServletPath() {\n\t\treturn this.servletPath;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DefaultSavedRequest [\" + getRedirectUrl() + \"]\";\n\t}\n\n\tprivate static @Nullable String createQueryString(@Nullable String queryString,\n\t\t\t@Nullable String matchingRequestParameterName) {\n\t\tif (matchingRequestParameterName == null) {\n\t\t\treturn queryString;\n\t\t}\n\t\tif (!StringUtils.hasLength(queryString)) {\n\t\t\treturn matchingRequestParameterName;\n\t\t}\n\t\treturn UriComponentsBuilder.newInstance()\n\t\t\t.query(queryString)\n\t\t\t.replaceQueryParam(matchingRequestParameterName)\n\t\t\t.queryParam(matchingRequestParameterName)\n\t\t\t.build()\n\t\t\t.getQuery();\n\t}\n\n\t/**\n\t * @since 4.2\n\t */\n\t@JsonIgnoreProperties(ignoreUnknown = true)\n\t@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = \"set\")\n\t@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = \"set\")\n\tpublic static class Builder {\n\n\t\tprivate @Nullable List<SavedCookie> cookies = null;\n\n\t\tprivate @Nullable List<Locale> locales = null;\n\n\t\tprivate Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);\n\n\t\tprivate Map<String, String[]> parameters = new TreeMap<>();\n\n\t\tprivate @Nullable String contextPath;\n\n\t\tprivate @Nullable String method;\n\n\t\tprivate @Nullable String pathInfo;\n\n\t\tprivate @Nullable String queryString;\n\n\t\tprivate @Nullable String requestURI;\n\n\t\tprivate @Nullable String requestURL;\n\n\t\tprivate @Nullable String scheme;\n\n\t\tprivate @Nullable String serverName;\n\n\t\tprivate @Nullable String servletPath;\n\n\t\tprivate int serverPort = 80;\n\n\t\tprivate @Nullable String matchingRequestParameterName;\n\n\t\tpublic Builder setCookies(List<SavedCookie> cookies) {\n\t\t\tthis.cookies = cookies;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setLocales(List<Locale> locales) {\n\t\t\tthis.locales = locales;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setHeaders(Map<String, List<String>> header) {\n\t\t\tthis.headers.putAll(header);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setParameters(Map<String, String[]> parameters) {\n\t\t\tthis.parameters = parameters;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setContextPath(String contextPath) {\n\t\t\tthis.contextPath = contextPath;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setMethod(String method) {\n\t\t\tthis.method = method;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setPathInfo(String pathInfo) {\n\t\t\tthis.pathInfo = pathInfo;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setQueryString(@Nullable String queryString) {\n\t\t\tthis.queryString = queryString;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setRequestURI(@Nullable String requestURI) {\n\t\t\tthis.requestURI = requestURI;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setRequestURL(String requestURL) {\n\t\t\tthis.requestURL = requestURL;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setScheme(@Nullable String scheme) {\n\t\t\tthis.scheme = scheme;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setServerName(@Nullable String serverName) {\n\t\t\tthis.serverName = serverName;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setServletPath(String servletPath) {\n\t\t\tthis.servletPath = servletPath;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setServerPort(int serverPort) {\n\t\t\tthis.serverPort = serverPort;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder setMatchingRequestParameterName(String matchingRequestParameterName) {\n\t\t\tthis.matchingRequestParameterName = matchingRequestParameterName;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DefaultSavedRequest build() {\n\t\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(this);\n\t\t\tif (!ObjectUtils.isEmpty(this.cookies)) {\n\t\t\t\tfor (SavedCookie cookie : this.cookies) {\n\t\t\t\t\tsavedRequest.addCookie(cookie.getCookie());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!ObjectUtils.isEmpty(this.locales)) {\n\t\t\t\tsavedRequest.locales.addAll(this.locales);\n\t\t\t}\n\t\t\tsavedRequest.addParameters(this.parameters);\n\t\t\tthis.headers.remove(HEADER_IF_MODIFIED_SINCE);\n\t\t\tthis.headers.remove(HEADER_IF_NONE_MATCH);\n\t\t\tfor (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {\n\t\t\t\tString headerName = entry.getKey();\n\t\t\t\tList<String> headerValues = entry.getValue();\n\t\t\t\tfor (String headerValue : headerValues) {\n\t\t\t\t\tsavedRequest.addHeader(headerName, headerValue);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn savedRequest;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/Enumerator.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.NoSuchElementException;\n\n/**\n * <p>\n * Adapter that wraps an <code>Enumeration</code> around a Java 2 collection\n * <code>Iterator</code>.\n * </p>\n * <p>\n * Constructors are provided to easily create such wrappers.\n * </p>\n * <p>\n * This class is based on code in Apache Tomcat.\n * </p>\n *\n * @author Craig McClanahan\n * @author Andrey Grebnev\n */\npublic class Enumerator<T> implements Enumeration<T> {\n\n\t/**\n\t * The <code>Iterator</code> over which the <code>Enumeration</code> represented by\n\t * this class actually operates.\n\t */\n\t@SuppressWarnings(\"NullAway\")\n\tprivate Iterator<T> iterator = null;\n\n\t/**\n\t * Return an Enumeration over the values of the specified Collection.\n\t * @param collection Collection whose values should be enumerated\n\t */\n\tpublic Enumerator(Collection<T> collection) {\n\t\tthis(collection.iterator());\n\t}\n\n\t/**\n\t * Return an Enumeration over the values of the specified Collection.\n\t * @param collection Collection whose values should be enumerated\n\t * @param clone true to clone iterator\n\t */\n\tpublic Enumerator(Collection<T> collection, boolean clone) {\n\t\tthis(collection.iterator(), clone);\n\t}\n\n\t/**\n\t * Return an Enumeration over the values returned by the specified Iterator.\n\t * @param iterator Iterator to be wrapped\n\t */\n\tpublic Enumerator(Iterator<T> iterator) {\n\t\tthis.iterator = iterator;\n\t}\n\n\t/**\n\t * Return an Enumeration over the values returned by the specified Iterator.\n\t * @param iterator Iterator to be wrapped\n\t * @param clone true to clone iterator\n\t */\n\tpublic Enumerator(Iterator<T> iterator, boolean clone) {\n\t\tif (!clone) {\n\t\t\tthis.iterator = iterator;\n\t\t}\n\t\telse {\n\t\t\tList<T> list = new ArrayList<>();\n\t\t\twhile (iterator.hasNext()) {\n\t\t\t\tlist.add(iterator.next());\n\t\t\t}\n\t\t\tthis.iterator = list.iterator();\n\t\t}\n\t}\n\n\t/**\n\t * Return an Enumeration over the values of the specified Map.\n\t * @param map Map whose values should be enumerated\n\t */\n\tpublic Enumerator(Map<?, T> map) {\n\t\tthis(map.values().iterator());\n\t}\n\n\t/**\n\t * Return an Enumeration over the values of the specified Map.\n\t * @param map Map whose values should be enumerated\n\t * @param clone true to clone iterator\n\t */\n\tpublic Enumerator(Map<?, T> map, boolean clone) {\n\t\tthis(map.values().iterator(), clone);\n\t}\n\n\t/**\n\t * Tests if this enumeration contains more elements.\n\t * @return <code>true</code> if and only if this enumeration object contains at least\n\t * one more element to provide, <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean hasMoreElements() {\n\t\treturn (this.iterator.hasNext());\n\t}\n\n\t/**\n\t * Returns the next element of this enumeration if this enumeration has at least one\n\t * more element to provide.\n\t * @return the next element of this enumeration\n\t * @exception NoSuchElementException if no more elements exist\n\t */\n\t@Override\n\tpublic T nextElement() throws NoSuchElementException {\n\t\treturn (this.iterator.next());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/FastHttpDateFormat.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.TimeZone;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Utility class to generate HTTP dates.\n * <p>\n * This class is based on code in Apache Tomcat.\n *\n * @author Remy Maucherat\n * @author Andrey Grebnev\n */\npublic final class FastHttpDateFormat {\n\n\t/**\n\t * HTTP date format.\n\t */\n\tprotected static final SimpleDateFormat format = new SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss zzz\", Locale.US);\n\n\t/**\n\t * The set of SimpleDateFormat formats to use in <code>getDateHeader()</code>.\n\t */\n\tprotected static final SimpleDateFormat[] formats = {\n\t\t\tnew SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss zzz\", Locale.US),\n\t\t\tnew SimpleDateFormat(\"EEEEEE, dd-MMM-yy HH:mm:ss zzz\", Locale.US),\n\t\t\tnew SimpleDateFormat(\"EEE MMMM d HH:mm:ss yyyy\", Locale.US) };\n\n\t/**\n\t * GMT time zone - all HTTP dates are on GMT\n\t */\n\tprotected static final TimeZone gmtZone = TimeZone.getTimeZone(\"GMT\");\n\n\tstatic {\n\t\tformat.setTimeZone(gmtZone);\n\t\tformats[0].setTimeZone(gmtZone);\n\t\tformats[1].setTimeZone(gmtZone);\n\t\tformats[2].setTimeZone(gmtZone);\n\t}\n\n\t/**\n\t * Instant on which the currentDate object was generated.\n\t */\n\tprotected static long currentDateGenerated = 0L;\n\n\t/**\n\t * Current formatted date.\n\t */\n\tprotected static @Nullable String currentDate = null;\n\n\t/**\n\t * Formatter cache.\n\t */\n\tprotected static final HashMap<Long, String> formatCache = new HashMap<>();\n\n\t/**\n\t * Parser cache.\n\t */\n\tprotected static final HashMap<String, Long> parseCache = new HashMap<>();\n\n\tprivate FastHttpDateFormat() {\n\t}\n\n\t/**\n\t * Formats a specified date to HTTP format. If local format is not <code>null</code>,\n\t * it's used instead.\n\t * @param value Date value to format\n\t * @param threadLocalformat The format to use (or <code>null</code> -- then HTTP\n\t * format will be used)\n\t * @return Formatted date\n\t */\n\tpublic static String formatDate(long value, DateFormat threadLocalformat) {\n\t\tString cachedDate = null;\n\t\tLong longValue = value;\n\t\ttry {\n\t\t\tcachedDate = formatCache.get(longValue);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t}\n\t\tif (cachedDate != null) {\n\t\t\treturn cachedDate;\n\t\t}\n\t\tString newDate;\n\t\tDate dateValue = new Date(value);\n\t\tif (threadLocalformat != null) {\n\t\t\tnewDate = threadLocalformat.format(dateValue);\n\t\t\tsynchronized (formatCache) {\n\t\t\t\tupdateCache(formatCache, longValue, newDate);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tsynchronized (formatCache) {\n\t\t\t\tnewDate = format.format(dateValue);\n\t\t\t\tupdateCache(formatCache, longValue, newDate);\n\t\t\t}\n\t\t}\n\t\treturn newDate;\n\t}\n\n\t/**\n\t * Gets the current date in HTTP format.\n\t * @return Current date in HTTP format\n\t */\n\tpublic static @Nullable String getCurrentDate() {\n\t\tlong now = System.currentTimeMillis();\n\t\tif ((now - currentDateGenerated) > 1000) {\n\t\t\tsynchronized (format) {\n\t\t\t\tif ((now - currentDateGenerated) > 1000) {\n\t\t\t\t\tcurrentDateGenerated = now;\n\t\t\t\t\tcurrentDate = format.format(new Date(now));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn currentDate;\n\t}\n\n\t/**\n\t * Parses date with given formatters.\n\t * @param value The string to parse\n\t * @param formats Array of formats to use\n\t * @return Parsed date (or <code>null</code> if no formatter matched)\n\t */\n\tprivate static @Nullable Long internalParseDate(String value, DateFormat[] formats) {\n\t\tDate date = null;\n\t\tfor (int i = 0; (date == null) && (i < formats.length); i++) {\n\t\t\ttry {\n\t\t\t\tdate = formats[i].parse(value);\n\t\t\t}\n\t\t\tcatch (ParseException ex) {\n\t\t\t}\n\t\t}\n\t\tif (date == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn date.getTime();\n\t}\n\n\t/**\n\t * Tries to parse the given date as an HTTP date. If local format list is not\n\t * <code>null</code>, it's used instead.\n\t * @param value The string to parse\n\t * @param threadLocalformats Array of formats to use for parsing. If <code>null</code>\n\t * , HTTP formats are used.\n\t * @return Parsed date (or -1 if error occurred)\n\t */\n\tpublic static long parseDate(String value, DateFormat[] threadLocalformats) {\n\t\tLong cachedDate = null;\n\t\ttry {\n\t\t\tcachedDate = parseCache.get(value);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t}\n\t\tif (cachedDate != null) {\n\t\t\treturn cachedDate;\n\t\t}\n\t\tLong date;\n\t\tif (threadLocalformats != null) {\n\t\t\tdate = internalParseDate(value, threadLocalformats);\n\t\t\tsynchronized (parseCache) {\n\t\t\t\tupdateCache(parseCache, value, date);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tsynchronized (parseCache) {\n\t\t\t\tdate = internalParseDate(value, formats);\n\t\t\t\tupdateCache(parseCache, value, date);\n\t\t\t}\n\t\t}\n\t\treturn (date != null) ? date : -1L;\n\t}\n\n\t/**\n\t * Updates cache.\n\t * @param cache Cache to be updated\n\t * @param key Key to be updated\n\t * @param value New value\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static void updateCache(HashMap cache, Object key, @Nullable Object value) {\n\t\tif (value == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (cache.size() > 1000) {\n\t\t\tcache.clear();\n\t\t}\n\t\tcache.put(key, value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/HttpSessionRequestCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * {@code RequestCache} which stores the {@code SavedRequest} in the HttpSession.\n *\n * The {@link DefaultSavedRequest} class is used as the implementation.\n *\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @since 3.0\n */\npublic class HttpSessionRequestCache implements RequestCache {\n\n\tstatic final String SAVED_REQUEST = \"SPRING_SECURITY_SAVED_REQUEST\";\n\n\tprotected final Log logger = LogFactory.getLog(this.getClass());\n\n\tprivate boolean createSessionAllowed = true;\n\n\tprivate RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;\n\n\tprivate String sessionAttrName = SAVED_REQUEST;\n\n\tprivate String matchingRequestParameterName = \"continue\";\n\n\t/**\n\t * Stores the current request, provided the configuration properties allow it.\n\t */\n\t@Override\n\tpublic void saveRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger\n\t\t\t\t\t.trace(LogMessage.format(\"Did not save request since it did not match [%s]\", this.requestMatcher));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.createSessionAllowed || request.getSession(false) != null) {\n\t\t\t// Store the HTTP request itself. Used by\n\t\t\t// AbstractAuthenticationProcessingFilter\n\t\t\t// for redirection after successful authentication (SEC-29)\n\t\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(request, this.matchingRequestParameterName);\n\t\t\trequest.getSession().setAttribute(this.sessionAttrName, savedRequest);\n\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\tthis.logger.debug(LogMessage.format(\"Saved request %s to session\", savedRequest.getRedirectUrl()));\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthis.logger.trace(\"Did not save request since there's no session and createSessionAllowed is false\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable SavedRequest getRequest(HttpServletRequest currentRequest, HttpServletResponse response) {\n\t\tHttpSession session = currentRequest.getSession(false);\n\t\treturn (session != null) ? (SavedRequest) session.getAttribute(this.sessionAttrName) : null;\n\t}\n\n\t@Override\n\tpublic void removeRequest(HttpServletRequest currentRequest, HttpServletResponse response) {\n\t\tHttpSession session = currentRequest.getSession(false);\n\t\tif (session != null) {\n\t\t\tthis.logger.trace(\"Removing DefaultSavedRequest from session if present\");\n\t\t\tsession.removeAttribute(this.sessionAttrName);\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\tif (this.matchingRequestParameterName != null) {\n\t\t\tif (!StringUtils.hasText(request.getQueryString())\n\t\t\t\t\t|| !UriComponentsBuilder.fromUriString(UrlUtils.buildRequestUrl(request))\n\t\t\t\t\t\t.build()\n\t\t\t\t\t\t.getQueryParams()\n\t\t\t\t\t\t.containsKey(this.matchingRequestParameterName)) {\n\t\t\t\tthis.logger.trace(\n\t\t\t\t\t\t\"matchingRequestParameterName is required for getMatchingRequest to lookup a value, but not provided\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tSavedRequest saved = getRequest(request, response);\n\t\tif (saved == null) {\n\t\t\tthis.logger.trace(\"No saved request\");\n\t\t\treturn null;\n\t\t}\n\t\tif (!matchesSavedRequest(request, saved)) {\n\t\t\tif (this.logger.isTraceEnabled()) {\n\t\t\t\tthis.logger.trace(LogMessage.format(\"Did not match request %s to the saved one %s\",\n\t\t\t\t\t\tUrlUtils.buildRequestUrl(request), saved));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tremoveRequest(request, response);\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Loaded matching saved request %s\", saved.getRedirectUrl()));\n\t\t}\n\t\treturn new SavedRequestAwareWrapper(saved, request);\n\t}\n\n\tprivate boolean matchesSavedRequest(HttpServletRequest request, SavedRequest savedRequest) {\n\t\tString currentUrl = UrlUtils.buildFullRequestUrl(request);\n\t\treturn savedRequest.getRedirectUrl().equals(currentUrl);\n\t}\n\n\t/**\n\t * Allows selective use of saved requests for a subset of requests. By default any\n\t * request will be cached by the {@code saveRequest} method.\n\t * <p>\n\t * If set, only matching requests will be cached.\n\t * @param requestMatcher a request matching strategy which defines which requests\n\t * should be cached.\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\t/**\n\t * If <code>true</code>, indicates that it is permitted to store the target URL and\n\t * exception information in a new <code>HttpSession</code> (the default). In\n\t * situations where you do not wish to unnecessarily create <code>HttpSession</code>s\n\t * - because the user agent will know the failed URL, such as with BASIC or Digest\n\t * authentication - you may wish to set this property to <code>false</code>.\n\t */\n\tpublic void setCreateSessionAllowed(boolean createSessionAllowed) {\n\t\tthis.createSessionAllowed = createSessionAllowed;\n\t}\n\n\t/**\n\t * If the {@code sessionAttrName} property is set, the request is stored in the\n\t * session using this attribute name. Default is \"SPRING_SECURITY_SAVED_REQUEST\".\n\t * @param sessionAttrName a new session attribute name.\n\t * @since 4.2.1\n\t */\n\tpublic void setSessionAttrName(String sessionAttrName) {\n\t\tthis.sessionAttrName = sessionAttrName;\n\t}\n\n\t/**\n\t * Specify the name of a query parameter that is added to the URL that specifies the\n\t * request cache should be checked in\n\t * {@link #getMatchingRequest(HttpServletRequest, HttpServletResponse)}\n\t * @param matchingRequestParameterName the parameter name that must be in the request\n\t * for {@link #getMatchingRequest(HttpServletRequest, HttpServletResponse)} to check\n\t * the session. Default is \"continue\".\n\t */\n\tpublic void setMatchingRequestParameterName(String matchingRequestParameterName) {\n\t\tthis.matchingRequestParameterName = matchingRequestParameterName;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/NullRequestCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Null implementation of <tt>RequestCache</tt>. Typically used when creation of a session\n * is not desired.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class NullRequestCache implements RequestCache {\n\n\t@Override\n\tpublic @Nullable SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void removeRequest(HttpServletRequest request, HttpServletResponse response) {\n\n\t}\n\n\t@Override\n\tpublic void saveRequest(HttpServletRequest request, HttpServletResponse response) {\n\t}\n\n\t@Override\n\tpublic @Nullable HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/RequestCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Implements \"saved request\" logic, allowing a single request to be retrieved and\n * restarted after redirecting to an authentication mechanism.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface RequestCache {\n\n\t/**\n\t * Caches the current request for later retrieval, once authentication has taken\n\t * place. Used by <tt>ExceptionTranslationFilter</tt>.\n\t * @param request the request to be stored\n\t */\n\tvoid saveRequest(HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Returns the saved request, leaving it cached.\n\t * @param request the current request\n\t * @return the saved request which was previously cached, or null if there is none.\n\t */\n\t@Nullable SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Returns a wrapper around the saved request, if it matches the current request. The\n\t * saved request should be removed from the cache.\n\t * @param request\n\t * @param response\n\t * @return the wrapped save request, if it matches the original, or null if there is\n\t * no cached request or it doesn't match.\n\t */\n\t@Nullable HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response);\n\n\t/**\n\t * Removes the cached request.\n\t * @param request the current request, allowing access to the cache.\n\t */\n\tvoid removeRequest(HttpServletRequest request, HttpServletResponse response);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/RequestCacheAwareFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Responsible for reconstituting the saved request if one is cached and it matches the\n * current request.\n * <p>\n * It will call\n * {@link RequestCache#getMatchingRequest(HttpServletRequest, HttpServletResponse)\n * getMatchingRequest} on the configured <tt>RequestCache</tt>. If the method returns a\n * value (a wrapper of the saved request), it will pass this to the filter chain's\n * <tt>doFilter</tt> method. If null is returned by the cache, the original request is\n * used and the filter has no effect.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class RequestCacheAwareFilter extends GenericFilterBean {\n\n\tprivate RequestCache requestCache;\n\n\tpublic RequestCacheAwareFilter() {\n\t\tthis(new HttpSessionRequestCache());\n\t}\n\n\tpublic RequestCacheAwareFilter(RequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tHttpServletRequest wrappedSavedRequest = this.requestCache.getMatchingRequest((HttpServletRequest) request,\n\t\t\t\t(HttpServletResponse) response);\n\t\tchain.doFilter((wrappedSavedRequest != null) ? wrappedSavedRequest : request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/SavedCookie.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.io.Serializable;\n\nimport jakarta.servlet.http.Cookie;\n\n/**\n * Stores off the values of a cookie in a serializable holder\n *\n * @author Ray Krueger\n */\npublic class SavedCookie implements Serializable {\n\n\tprivate static final long serialVersionUID = 620L;\n\n\tprivate final String name;\n\n\tprivate final String value;\n\n\tprivate final String domain;\n\n\tprivate final int maxAge;\n\n\tprivate final String path;\n\n\tprivate final boolean secure;\n\n\tpublic SavedCookie(String name, String value, String domain, int maxAge, String path, boolean secure) {\n\t\tthis.name = name;\n\t\tthis.value = value;\n\t\tthis.domain = domain;\n\t\tthis.maxAge = maxAge;\n\t\tthis.path = path;\n\t\tthis.secure = secure;\n\t}\n\n\tpublic SavedCookie(Cookie cookie) {\n\t\tthis(cookie.getName(), cookie.getValue(), cookie.getDomain(), cookie.getMaxAge(), cookie.getPath(),\n\t\t\t\tcookie.getSecure());\n\t}\n\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\tpublic String getDomain() {\n\t\treturn this.domain;\n\t}\n\n\tpublic int getMaxAge() {\n\t\treturn this.maxAge;\n\t}\n\n\tpublic String getPath() {\n\t\treturn this.path;\n\t}\n\n\tpublic boolean isSecure() {\n\t\treturn this.secure;\n\t}\n\n\tpublic Cookie getCookie() {\n\t\tCookie cookie = new Cookie(getName(), getValue());\n\t\tif (getDomain() != null) {\n\t\t\tcookie.setDomain(getDomain());\n\t\t}\n\t\tif (getPath() != null) {\n\t\t\tcookie.setPath(getPath());\n\t\t}\n\t\tcookie.setMaxAge(getMaxAge());\n\t\tcookie.setSecure(isSecure());\n\t\treturn cookie;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/SavedRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport jakarta.servlet.http.Cookie;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Encapsulates the functionality required of a cached request for both an authentication\n * mechanism (typically form-based login) to redirect to the original URL and for a\n * <tt>RequestCache</tt> to build a wrapped request, reproducing the original request\n * data.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic interface SavedRequest extends java.io.Serializable {\n\n\t/**\n\t * @return the URL for the saved request, allowing a redirect to be performed.\n\t */\n\tString getRedirectUrl();\n\n\tList<Cookie> getCookies();\n\n\tString getMethod();\n\n\tList<String> getHeaderValues(String name);\n\n\tCollection<String> getHeaderNames();\n\n\tList<Locale> getLocales();\n\n\tString @Nullable [] getParameterValues(String name);\n\n\tMap<String, String[]> getParameterMap();\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapper.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TimeZone;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpHeaders;\n\n/**\n * Provides request parameters, headers and cookies from either an original request or a\n * saved request.\n *\n * <p>\n * Note that not all request parameters in the original request are emulated by this\n * wrapper. Nevertheless, the important data from the original request is emulated and\n * this should prove adequate for most purposes (in particular standard HTTP GET and POST\n * operations).\n *\n * <p>\n * Added into a request by\n * {@link org.springframework.security.web.savedrequest.RequestCacheAwareFilter}.\n *\n * @author Andrey Grebnev\n * @author Ben Alex\n * @author Luke Taylor\n */\nclass SavedRequestAwareWrapper extends HttpServletRequestWrapper {\n\n\tprotected static final Log logger = LogFactory.getLog(SavedRequestAwareWrapper.class);\n\n\tprotected static final TimeZone GMT_ZONE = TimeZone.getTimeZone(\"GMT\");\n\n\tprotected SavedRequest savedRequest;\n\n\t/**\n\t * The set of SimpleDateFormat formats to use in getDateHeader(). Notice that because\n\t * SimpleDateFormat is not thread-safe, we can't declare formats[] as a static\n\t * variable.\n\t */\n\tprotected final SimpleDateFormat[] formats = new SimpleDateFormat[3];\n\n\tSavedRequestAwareWrapper(SavedRequest saved, HttpServletRequest request) {\n\t\tsuper(request);\n\t\tthis.savedRequest = saved;\n\t\tthis.formats[0] = new SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss zzz\", Locale.US);\n\t\tthis.formats[1] = new SimpleDateFormat(\"EEEEEE, dd-MMM-yy HH:mm:ss zzz\", Locale.US);\n\t\tthis.formats[2] = new SimpleDateFormat(\"EEE MMMM d HH:mm:ss yyyy\", Locale.US);\n\t\tthis.formats[0].setTimeZone(GMT_ZONE);\n\t\tthis.formats[1].setTimeZone(GMT_ZONE);\n\t\tthis.formats[2].setTimeZone(GMT_ZONE);\n\t}\n\n\t@Override\n\tpublic long getDateHeader(String name) {\n\t\tString value = getHeader(name);\n\t\tif (value == null) {\n\t\t\treturn -1L;\n\t\t}\n\t\t// Attempt to convert the date header in a variety of formats\n\t\tlong result = FastHttpDateFormat.parseDate(value, this.formats);\n\t\tif (result != -1L) {\n\t\t\treturn result;\n\t\t}\n\t\tthrow new IllegalArgumentException(value);\n\t}\n\n\t@Override\n\tpublic @Nullable String getHeader(String name) {\n\t\tList<String> values = this.savedRequest.getHeaderValues(name);\n\t\treturn values.isEmpty() ? null : values.get(0);\n\t}\n\n\t@Override\n\tpublic Enumeration<String> getHeaderNames() {\n\t\treturn new Enumerator<>(this.savedRequest.getHeaderNames());\n\t}\n\n\t@Override\n\tpublic Enumeration<String> getHeaders(String name) {\n\t\treturn new Enumerator<>(this.savedRequest.getHeaderValues(name));\n\t}\n\n\t@Override\n\tpublic int getIntHeader(String name) {\n\t\tString value = getHeader(name);\n\t\treturn (value != null) ? Integer.parseInt(value) : -1;\n\t}\n\n\t@Override\n\tpublic Locale getLocale() {\n\t\tList<Locale> locales = this.savedRequest.getLocales();\n\t\treturn locales.isEmpty() ? Locale.getDefault() : locales.get(0);\n\t}\n\n\t@Override\n\tpublic Enumeration<Locale> getLocales() {\n\t\tList<Locale> locales = this.savedRequest.getLocales();\n\t\tif (locales.isEmpty()) {\n\t\t\t// Fall back to default locale\n\t\t\tlocales = new ArrayList<>(1);\n\t\t\tlocales.add(Locale.getDefault());\n\t\t}\n\t\treturn new Enumerator<>(locales);\n\t}\n\n\t@Override\n\tpublic @Nullable String getMethod() {\n\t\treturn this.savedRequest.getMethod();\n\t}\n\n\t@Override\n\tpublic @Nullable String getContentType() {\n\t\treturn getHeader(HttpHeaders.CONTENT_TYPE);\n\t}\n\n\t/**\n\t * If the parameter is available from the wrapped request then the request has been\n\t * forwarded/included to a URL with parameters, either supplementing or overriding the\n\t * saved request values.\n\t * <p>\n\t * In this case, the value from the wrapped request should be used.\n\t * <p>\n\t * If the value from the wrapped request is null, an attempt will be made to retrieve\n\t * the parameter from the saved request.\n\t */\n\t@Override\n\tpublic @Nullable String getParameter(String name) {\n\t\tString value = super.getParameter(name);\n\t\tif (value != null) {\n\t\t\treturn value;\n\t\t}\n\t\tString[] values = this.savedRequest.getParameterValues(name);\n\t\tif (values == null || values.length == 0) {\n\t\t\treturn null;\n\t\t}\n\t\treturn values[0];\n\t}\n\n\t@Override\n\tpublic Map<String, String[]> getParameterMap() {\n\t\tSet<String> names = getCombinedParameterNames();\n\t\tMap<String, String[]> parameterMap = new HashMap<>(names.size());\n\t\tfor (String name : names) {\n\t\t\tparameterMap.put(name, getParameterValues(name));\n\t\t}\n\t\treturn parameterMap;\n\t}\n\n\tprivate Set<String> getCombinedParameterNames() {\n\t\tSet<String> names = new HashSet<>();\n\t\tnames.addAll(super.getParameterMap().keySet());\n\t\tnames.addAll(this.savedRequest.getParameterMap().keySet());\n\t\treturn names;\n\t}\n\n\t@Override\n\tpublic Enumeration<String> getParameterNames() {\n\t\treturn new Enumerator<>(getCombinedParameterNames());\n\t}\n\n\t@Override\n\tpublic String[] getParameterValues(String name) {\n\t\tString[] savedRequestParams = this.savedRequest.getParameterValues(name);\n\t\tString[] wrappedRequestParams = super.getParameterValues(name);\n\t\tif (savedRequestParams == null) {\n\t\t\treturn wrappedRequestParams;\n\t\t}\n\t\tif (wrappedRequestParams == null) {\n\t\t\treturn savedRequestParams;\n\t\t}\n\t\t// We have parameters in both saved and wrapped requests so have to merge them\n\t\tList<String> wrappedParamsList = Arrays.asList(wrappedRequestParams);\n\t\tList<String> combinedParams = new ArrayList<>(wrappedParamsList);\n\t\t// We want to add all parameters of the saved request *apart from* duplicates of\n\t\t// those already added\n\t\tfor (String savedRequestParam : savedRequestParams) {\n\t\t\tif (!wrappedParamsList.contains(savedRequestParam)) {\n\t\t\t\tcombinedParams.add(savedRequestParam);\n\t\t\t}\n\t\t}\n\t\treturn combinedParams.toArray(new String[0]);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/SimpleSavedRequest.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.io.Serial;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport jakarta.servlet.http.Cookie;\n\nimport org.springframework.util.Assert;\n\n/**\n * A Bean implementation of SavedRequest\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic class SimpleSavedRequest implements SavedRequest {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 807650604272166969L;\n\n\tprivate String redirectUrl;\n\n\tprivate List<Cookie> cookies = new ArrayList<>();\n\n\tprivate String method = \"GET\";\n\n\tprivate Map<String, List<String>> headers = new HashMap<>();\n\n\tprivate List<Locale> locales = new ArrayList<>();\n\n\tprivate Map<String, String[]> parameters = new HashMap<>();\n\n\tpublic SimpleSavedRequest() {\n\t\tthis.redirectUrl = \"/\";\n\t\tthis.method = \"GET\";\n\t}\n\n\tpublic SimpleSavedRequest(String redirectUrl) {\n\t\tthis.redirectUrl = redirectUrl;\n\t}\n\n\tpublic SimpleSavedRequest(SavedRequest request) {\n\t\tthis.redirectUrl = request.getRedirectUrl();\n\t\tthis.cookies = request.getCookies();\n\t\tfor (String headerName : request.getHeaderNames()) {\n\t\t\tthis.headers.put(headerName, request.getHeaderValues(headerName));\n\t\t}\n\t\tthis.locales = request.getLocales();\n\t\tthis.parameters = request.getParameterMap();\n\t\tthis.method = request.getMethod();\n\t}\n\n\t@Override\n\tpublic String getRedirectUrl() {\n\t\treturn this.redirectUrl;\n\t}\n\n\t@Override\n\tpublic List<Cookie> getCookies() {\n\t\treturn this.cookies;\n\t}\n\n\t@Override\n\tpublic String getMethod() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic List<String> getHeaderValues(String name) {\n\t\treturn this.headers.getOrDefault(name, new ArrayList<>());\n\t}\n\n\t@Override\n\tpublic Collection<String> getHeaderNames() {\n\t\treturn this.headers.keySet();\n\t}\n\n\t@Override\n\tpublic List<Locale> getLocales() {\n\t\treturn this.locales;\n\t}\n\n\t@Override\n\tpublic String[] getParameterValues(String name) {\n\t\treturn this.parameters.getOrDefault(name, new String[0]);\n\t}\n\n\t@Override\n\tpublic Map<String, String[]> getParameterMap() {\n\t\treturn this.parameters;\n\t}\n\n\tpublic void setRedirectUrl(String redirectUrl) {\n\t\tAssert.notNull(redirectUrl, \"redirectUrl cannot be null\");\n\t\tthis.redirectUrl = redirectUrl;\n\t}\n\n\tpublic void setCookies(List<Cookie> cookies) {\n\t\tAssert.notNull(cookies, \"cookies cannot be null\");\n\t\tthis.cookies = cookies;\n\t}\n\n\tpublic void setMethod(String method) {\n\t\tAssert.notNull(method, \"method cannot be null\");\n\t\tthis.method = method;\n\t}\n\n\tpublic void setHeaders(Map<String, List<String>> headers) {\n\t\tAssert.notNull(headers, \"headers cannot be null\");\n\t\tthis.headers = headers;\n\t}\n\n\tpublic void setLocales(List<Locale> locales) {\n\t\tAssert.notNull(locales, \"locales cannot be null\");\n\t\tthis.locales = locales;\n\t}\n\n\tpublic void setParameters(Map<String, String[]> parameters) {\n\t\tAssert.notNull(parameters, \"parameters cannot be null\");\n\t\tthis.parameters = parameters;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/savedrequest/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Classes related to the caching of an {@code HttpServletRequest} which requires\n * authentication. While the user is logging in, the request is cached (using the\n * RequestCache implementation) by the ExceptionTranslationFilter. Once the user has been\n * authenticated, the original request is restored following a redirect to a matching URL,\n * and the {@code RequestCache} is queried to obtain the original (matching) request.\n */\n@NullMarked\npackage org.springframework.security.web.savedrequest;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/DefaultServerRedirectStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.net.URI;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * The default {@link ServerRedirectStrategy} to use.\n *\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n */\npublic class DefaultServerRedirectStrategy implements ServerRedirectStrategy {\n\n\tprivate static final Log logger = LogFactory.getLog(DefaultServerRedirectStrategy.class);\n\n\tprivate HttpStatus httpStatus = HttpStatus.FOUND;\n\n\tprivate boolean contextRelative = true;\n\n\t@Override\n\tpublic Mono<Void> sendRedirect(ServerWebExchange exchange, URI location) {\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\tAssert.notNull(location, \"location cannot be null\");\n\t\treturn Mono.fromRunnable(() -> {\n\t\t\tServerHttpResponse response = exchange.getResponse();\n\t\t\tresponse.setStatusCode(this.httpStatus);\n\t\t\tURI newLocation = createLocation(exchange, location);\n\t\t\tlogger.debug(LogMessage.format(\"Redirecting to '%s'\", newLocation));\n\t\t\tresponse.getHeaders().setLocation(newLocation);\n\t\t});\n\t}\n\n\tprivate URI createLocation(ServerWebExchange exchange, URI location) {\n\t\tif (!this.contextRelative) {\n\t\t\treturn location;\n\t\t}\n\t\tString url = location.toASCIIString();\n\t\tif (url.startsWith(\"/\")) {\n\t\t\tString context = exchange.getRequest().getPath().contextPath().value();\n\t\t\treturn URI.create(context + url);\n\t\t}\n\t\treturn location;\n\t}\n\n\t/**\n\t * The {@link HttpStatus} to use for the redirect.\n\t * @param httpStatus the status to use. Cannot be null\n\t */\n\tpublic void setHttpStatus(HttpStatus httpStatus) {\n\t\tAssert.notNull(httpStatus, \"httpStatus cannot be null\");\n\t\tthis.httpStatus = httpStatus;\n\t}\n\n\t/**\n\t * Sets if the location is relative to the context.\n\t * @param contextRelative if redirects should be relative to the context. Default is\n\t * true.\n\t */\n\tpublic void setContextRelative(boolean contextRelative) {\n\t\tthis.contextRelative = contextRelative;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/DelegatingServerAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ServerAuthenticationEntryPoint} which delegates to multiple\n * {@link ServerAuthenticationEntryPoint} based on a {@link ServerWebExchangeMatcher}\n *\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n */\npublic class DelegatingServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {\n\n\tprivate static final Log logger = LogFactory.getLog(DelegatingServerAuthenticationEntryPoint.class);\n\n\tprivate final List<DelegateEntry> entryPoints;\n\n\tprivate ServerAuthenticationEntryPoint defaultEntryPoint = (exchange, ex) -> {\n\t\texchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);\n\t\treturn exchange.getResponse().setComplete();\n\t};\n\n\tpublic DelegatingServerAuthenticationEntryPoint(DelegateEntry... entryPoints) {\n\t\tthis(Arrays.asList(entryPoints));\n\t}\n\n\tpublic DelegatingServerAuthenticationEntryPoint(List<DelegateEntry> entryPoints) {\n\t\tAssert.notEmpty(entryPoints, \"entryPoints cannot be null\");\n\t\tthis.entryPoints = entryPoints;\n\t}\n\n\t@Override\n\tpublic Mono<Void> commence(ServerWebExchange exchange, AuthenticationException ex) {\n\t\treturn Flux.fromIterable(this.entryPoints)\n\t\t\t.filterWhen((entry) -> isMatch(exchange, entry))\n\t\t\t.next()\n\t\t\t.map((entry) -> entry.getEntryPoint())\n\t\t\t.doOnNext((entryPoint) -> logger.debug(LogMessage.format(\"Match found! Executing %s\", entryPoint)))\n\t\t\t.switchIfEmpty(Mono.just(this.defaultEntryPoint)\n\t\t\t\t.doOnNext((entryPoint) -> logger\n\t\t\t\t\t.debug(LogMessage.format(\"No match found. Using default entry point %s\", this.defaultEntryPoint))))\n\t\t\t.flatMap((entryPoint) -> entryPoint.commence(exchange, ex));\n\t}\n\n\tprivate Mono<Boolean> isMatch(ServerWebExchange exchange, DelegateEntry entry) {\n\t\tServerWebExchangeMatcher matcher = entry.getMatcher();\n\t\tlogger.debug(LogMessage.format(\"Trying to match using %s\", matcher));\n\t\treturn matcher.matches(exchange).map(MatchResult::isMatch);\n\t}\n\n\t/**\n\t * EntryPoint which is used when no RequestMatcher returned true\n\t */\n\tpublic void setDefaultEntryPoint(ServerAuthenticationEntryPoint defaultEntryPoint) {\n\t\tthis.defaultEntryPoint = defaultEntryPoint;\n\t}\n\n\tpublic static class DelegateEntry {\n\n\t\tprivate final ServerWebExchangeMatcher matcher;\n\n\t\tprivate final ServerAuthenticationEntryPoint entryPoint;\n\n\t\tpublic DelegateEntry(ServerWebExchangeMatcher matcher, ServerAuthenticationEntryPoint entryPoint) {\n\t\t\tthis.matcher = matcher;\n\t\t\tthis.entryPoint = entryPoint;\n\t\t}\n\n\t\tpublic ServerWebExchangeMatcher getMatcher() {\n\t\t\treturn this.matcher;\n\t\t}\n\n\t\tpublic ServerAuthenticationEntryPoint getEntryPoint() {\n\t\t\treturn this.entryPoint;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ExchangeMatcherRedirectWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Web filter that redirects requests that match {@link ServerWebExchangeMatcher} to the\n * specified URL.\n *\n * @author Evgeniy Cheban\n * @since 5.6\n */\npublic final class ExchangeMatcherRedirectWebFilter implements WebFilter {\n\n\tprivate final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();\n\n\tprivate final ServerWebExchangeMatcher exchangeMatcher;\n\n\tprivate final URI redirectUri;\n\n\t/**\n\t * Create and initialize an instance of the web filter.\n\t * @param exchangeMatcher the exchange matcher\n\t * @param redirectUrl the redirect URL\n\t */\n\tpublic ExchangeMatcherRedirectWebFilter(ServerWebExchangeMatcher exchangeMatcher, String redirectUrl) {\n\t\tAssert.notNull(exchangeMatcher, \"exchangeMatcher cannot be null\");\n\t\tAssert.hasText(redirectUrl, \"redirectUrl cannot be empty\");\n\t\tthis.exchangeMatcher = exchangeMatcher;\n\t\tthis.redirectUri = URI.create(redirectUrl);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t// @formatter:off\n\t\treturn this.exchangeMatcher.matches(exchange)\n\t\t\t\t.filter(MatchResult::isMatch)\n\t\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t\t.flatMap((result) -> this.redirectStrategy.sendRedirect(exchange, this.redirectUri));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/FormPostServerRedirectStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.Map;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.core.io.buffer.DataBufferUtils;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.crypto.keygen.Base64StringKeyGenerator;\nimport org.springframework.security.crypto.keygen.StringKeyGenerator;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.util.HtmlUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Redirect using an auto-submitting HTML form using the POST method. All query params\n * provided in the URL are changed to inputs in the form so they are submitted as POST\n * data instead of query string data.\n *\n * @author Max Batischev\n * @author Steve Riesenberg\n * @since 6.5\n */\npublic final class FormPostServerRedirectStrategy implements ServerRedirectStrategy {\n\n\tprivate static final String CONTENT_SECURITY_POLICY_HEADER = \"Content-Security-Policy\";\n\n\tprivate static final String REDIRECT_PAGE_TEMPLATE = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <meta charset=\"utf-8\">\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t    <title>Redirect</title>\n\t\t\t  </head>\n\t\t\t  <body>\n\t\t\t    <form id=\"redirect-form\" method=\"POST\" action=\"{{action}}\">\n\t\t\t      {{params}}\n\t\t\t      <noscript>\n\t\t\t        <p>JavaScript is not enabled for this page.</p>\n\t\t\t        <button type=\"submit\">Click to continue</button>\n\t\t\t      </noscript>\n\t\t\t    </form>\n\t\t\t    <script nonce=\"{{nonce}}\">\n\t\t\t      document.getElementById(\"redirect-form\").submit();\n\t\t\t    </script>\n\t\t\t  </body>\n\t\t\t</html>\n\t\t\t\"\"\";\n\n\tprivate static final String HIDDEN_INPUT_TEMPLATE = \"\"\"\n\t\t\t<input name=\"{{name}}\" type=\"hidden\" value=\"{{value}}\" />\n\t\t\t\"\"\";\n\n\tprivate static final StringKeyGenerator DEFAULT_NONCE_GENERATOR = new Base64StringKeyGenerator(\n\t\t\tBase64.getUrlEncoder().withoutPadding(), 96);\n\n\t@Override\n\tpublic Mono<Void> sendRedirect(ServerWebExchange exchange, URI location) {\n\t\tfinal UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(location);\n\n\t\tfinal StringBuilder hiddenInputsHtmlBuilder = new StringBuilder();\n\t\tfor (final Map.Entry<String, List<String>> entry : uriComponentsBuilder.build().getQueryParams().entrySet()) {\n\t\t\tfinal String name = entry.getKey();\n\t\t\tfor (final String value : entry.getValue()) {\n\t\t\t\t// @formatter:off\n\t\t\t\tfinal String hiddenInput = HIDDEN_INPUT_TEMPLATE\n\t\t\t\t\t.replace(\"{{name}}\", HtmlUtils.htmlEscape(name))\n\t\t\t\t\t.replace(\"{{value}}\", HtmlUtils.htmlEscape(value));\n\t\t\t\t// @formatter:on\n\t\t\t\thiddenInputsHtmlBuilder.append(hiddenInput.trim());\n\t\t\t}\n\t\t}\n\n\t\t// Create the script-src policy directive for the Content-Security-Policy header\n\t\tfinal String nonce = DEFAULT_NONCE_GENERATOR.generateKey();\n\t\tfinal String policyDirective = \"script-src 'nonce-%s'\".formatted(nonce);\n\n\t\t// @formatter:off\n\t\tfinal String html = REDIRECT_PAGE_TEMPLATE\n\t\t\t// Clear the query string as we don't want that to be part of the form action URL\n\t\t\t.replace(\"{{action}}\", HtmlUtils.htmlEscape(uriComponentsBuilder.replaceQuery(null).build().toUriString()))\n\t\t\t.replace(\"{{params}}\", hiddenInputsHtmlBuilder.toString())\n\t\t\t.replace(\"{{nonce}}\", HtmlUtils.htmlEscape(nonce));\n\t\t// @formatter:on\n\n\t\tfinal ServerHttpResponse response = exchange.getResponse();\n\t\tresponse.setStatusCode(HttpStatus.OK);\n\t\tresponse.getHeaders().setContentType(MediaType.TEXT_HTML);\n\t\tresponse.getHeaders().set(CONTENT_SECURITY_POLICY_HEADER, policyDirective);\n\n\t\tfinal DataBufferFactory bufferFactory = response.bufferFactory();\n\t\tfinal DataBuffer buffer = bufferFactory.wrap(html.getBytes(StandardCharsets.UTF_8));\n\t\treturn response.writeWith(Mono.just(buffer)).doOnError((error) -> DataBufferUtils.release(buffer));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/MatcherSecurityWebFilterChain.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\n\n/**\n * A {@link SecurityWebFilterChain} that leverages a {@link ServerWebExchangeMatcher} to\n * determine which {@link WebFilter} to execute.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class MatcherSecurityWebFilterChain implements SecurityWebFilterChain {\n\n\tprivate final ServerWebExchangeMatcher matcher;\n\n\tprivate final List<WebFilter> filters;\n\n\tpublic MatcherSecurityWebFilterChain(ServerWebExchangeMatcher matcher, List<WebFilter> filters) {\n\t\tAssert.notNull(matcher, \"matcher cannot be null\");\n\t\tAssert.notEmpty(filters, () -> \"filters cannot be null or empty. Got \" + filters);\n\t\tthis.matcher = matcher;\n\t\tthis.filters = filters;\n\t}\n\n\t@Override\n\tpublic Mono<Boolean> matches(ServerWebExchange exchange) {\n\t\treturn this.matcher.matches(exchange).map((m) -> m.isMatch());\n\t}\n\n\t@Override\n\tpublic Flux<WebFilter> getWebFilters() {\n\t\treturn Flux.fromIterable(this.filters);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ObservationWebFilterChainDecorator.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ListIterator;\n\nimport io.micrometer.common.KeyValue;\nimport io.micrometer.common.KeyValues;\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationConvention;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.WebHandler;\n\n/**\n * A\n * {@link org.springframework.security.web.server.WebFilterChainProxy.WebFilterChainDecorator}\n * that wraps the chain in before and after observations\n *\n * @author Josh Cummings\n * @since 6.0\n */\npublic final class ObservationWebFilterChainDecorator implements WebFilterChainProxy.WebFilterChainDecorator {\n\n\tprivate static final String ATTRIBUTE = ObservationWebFilterChainDecorator.class + \".observation\";\n\n\tstatic final String UNSECURED_OBSERVATION_NAME = \"spring.security.http.unsecured.requests\";\n\n\tstatic final String SECURED_OBSERVATION_NAME = \"spring.security.http.secured.requests\";\n\n\tprivate final ObservationRegistry registry;\n\n\tpublic ObservationWebFilterChainDecorator(ObservationRegistry registry) {\n\t\tthis.registry = registry;\n\t}\n\n\t@Override\n\tpublic WebFilterChain decorate(WebFilterChain original) {\n\t\treturn wrapUnsecured(original);\n\t}\n\n\t@Override\n\tpublic WebFilterChain decorate(WebFilterChain original, List<WebFilter> filters) {\n\t\treturn new ObservationWebFilterChain(wrapSecured(original)::filter, wrap(filters));\n\t}\n\n\tprivate static @Nullable AroundWebFilterObservation observation(ServerWebExchange exchange) {\n\t\treturn exchange.getAttribute(ATTRIBUTE);\n\t}\n\n\tprivate WebFilterChain wrapSecured(WebFilterChain original) {\n\t\treturn (exchange) -> Mono.deferContextual((contextView) -> {\n\t\t\tAroundWebFilterObservation parent = observation(exchange);\n\t\t\tAssert.notNull(parent, \"AroundWebFilterObservation parent cannot be null\");\n\t\t\tObservation parentObservation = contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null);\n\t\t\tObservation observation = Observation.createNotStarted(SECURED_OBSERVATION_NAME, this.registry)\n\t\t\t\t.contextualName(\"secured request\")\n\t\t\t\t.parentObservation(parentObservation);\n\t\t\treturn parent.wrap(WebFilterObservation.create(observation).wrap(original)).filter(exchange);\n\t\t});\n\t}\n\n\tprivate WebFilterChain wrapUnsecured(WebFilterChain original) {\n\t\treturn (exchange) -> Mono.deferContextual((contextView) -> {\n\t\t\tObservation parentObservation = contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null);\n\t\t\tObservation observation = Observation.createNotStarted(UNSECURED_OBSERVATION_NAME, this.registry)\n\t\t\t\t.contextualName(\"unsecured request\")\n\t\t\t\t.parentObservation(parentObservation);\n\t\t\treturn WebFilterObservation.create(observation).wrap(original).filter(exchange);\n\t\t});\n\t}\n\n\tprivate List<ObservationWebFilter> wrap(List<WebFilter> filters) {\n\t\tint size = filters.size();\n\t\tList<ObservationWebFilter> observableFilters = new ArrayList<>();\n\t\tint position = 1;\n\t\tfor (WebFilter filter : filters) {\n\t\t\tobservableFilters.add(new ObservationWebFilter(this.registry, filter, position, size));\n\t\t\tposition++;\n\t\t}\n\t\treturn observableFilters;\n\t}\n\n\tstatic class ObservationWebFilterChain implements WebFilterChain {\n\n\t\tprivate final WebHandler handler;\n\n\t\tprivate final @Nullable ObservationWebFilter currentFilter;\n\n\t\tprivate final @Nullable ObservationWebFilterChain chain;\n\n\t\t/**\n\t\t * Public constructor with the list of filters and the target handler to use.\n\t\t * @param handler the target handler\n\t\t * @param filters the filters ahead of the handler\n\t\t * @since 5.1\n\t\t */\n\t\tObservationWebFilterChain(WebHandler handler, List<ObservationWebFilter> filters) {\n\t\t\tAssert.notNull(handler, \"WebHandler is required\");\n\t\t\tthis.handler = handler;\n\t\t\tObservationWebFilterChain chain = initChain(filters, handler);\n\t\t\tthis.currentFilter = chain.currentFilter;\n\t\t\tthis.chain = chain.chain;\n\t\t}\n\n\t\tprivate static ObservationWebFilterChain initChain(List<ObservationWebFilter> filters, WebHandler handler) {\n\t\t\tObservationWebFilterChain chain = new ObservationWebFilterChain(handler, null, null);\n\t\t\tListIterator<? extends ObservationWebFilter> iterator = filters.listIterator(filters.size());\n\t\t\twhile (iterator.hasPrevious()) {\n\t\t\t\tchain = new ObservationWebFilterChain(handler, iterator.previous(), chain);\n\t\t\t}\n\t\t\treturn chain;\n\t\t}\n\n\t\t/**\n\t\t * Private constructor to represent one link in the chain.\n\t\t */\n\t\tprivate ObservationWebFilterChain(WebHandler handler, @Nullable ObservationWebFilter currentFilter,\n\t\t\t\t@Nullable ObservationWebFilterChain chain) {\n\t\t\tthis.currentFilter = currentFilter;\n\t\t\tthis.handler = handler;\n\t\t\tthis.chain = chain;\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange) {\n\t\t\treturn Mono.defer(() -> (this.currentFilter != null && this.chain != null)\n\t\t\t\t\t? invokeFilter(this.currentFilter, this.chain, exchange) : this.handler.handle(exchange));\n\t\t}\n\n\t\tprivate Mono<Void> invokeFilter(ObservationWebFilter current, ObservationWebFilterChain chain,\n\t\t\t\tServerWebExchange exchange) {\n\t\t\tString currentName = current.getName();\n\t\t\treturn current.filter(exchange, chain).checkpoint(currentName + \" [DefaultWebFilterChain]\");\n\t\t}\n\n\t}\n\n\tstatic final class ObservationWebFilter implements WebFilter {\n\n\t\tprivate final ObservationRegistry registry;\n\n\t\tprivate final WebFilterChainObservationConvention convention = new WebFilterChainObservationConvention();\n\n\t\tprivate final WebFilter filter;\n\n\t\tprivate final String name;\n\n\t\tprivate final int position;\n\n\t\tprivate final int size;\n\n\t\tObservationWebFilter(ObservationRegistry registry, WebFilter filter, int position, int size) {\n\t\t\tthis.registry = registry;\n\t\t\tthis.filter = filter;\n\t\t\tthis.name = filter.getClass().getSimpleName();\n\t\t\tthis.position = position;\n\t\t\tthis.size = size;\n\t\t}\n\n\t\tString getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\tif (this.position == 1) {\n\t\t\t\treturn Mono.deferContextual((contextView) -> {\n\t\t\t\t\tObservation parentObservation = contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null);\n\t\t\t\t\tAroundWebFilterObservation parent = parent(exchange, parentObservation);\n\t\t\t\t\treturn parent.wrap(this::wrapFilter).filter(exchange, chain);\n\t\t\t\t});\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn wrapFilter(exchange, chain);\n\t\t\t}\n\t\t}\n\n\t\tprivate Mono<Void> wrapFilter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\tAroundWebFilterObservation parent = observation(exchange);\n\t\t\tAssert.notNull(parent, \"ObservationWebFilter parent is required\");\n\t\t\tif (parent.before().getContext() instanceof WebFilterChainObservationContext parentBefore) {\n\t\t\t\tparentBefore.setChainSize(this.size);\n\t\t\t\tparentBefore.setFilterName(this.name);\n\t\t\t\tparentBefore.setChainPosition(this.position);\n\t\t\t}\n\t\t\treturn this.filter.filter(exchange, chain).doOnSuccess((result) -> {\n\t\t\t\tparent.start();\n\t\t\t\tif (parent.after().getContext() instanceof WebFilterChainObservationContext parentAfter) {\n\t\t\t\t\tparentAfter.setChainSize(this.size);\n\t\t\t\t\tparentAfter.setFilterName(this.name);\n\t\t\t\t\tparentAfter.setChainPosition(this.size - this.position + 1);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tprivate AroundWebFilterObservation parent(ServerWebExchange exchange, @Nullable Observation parentObservation) {\n\t\t\tWebFilterChainObservationContext beforeContext = WebFilterChainObservationContext.before();\n\t\t\tWebFilterChainObservationContext afterContext = WebFilterChainObservationContext.after();\n\t\t\tObservation before = Observation.createNotStarted(this.convention, () -> beforeContext, this.registry)\n\t\t\t\t.parentObservation(parentObservation);\n\t\t\tObservation after = Observation.createNotStarted(this.convention, () -> afterContext, this.registry)\n\t\t\t\t.parentObservation(parentObservation);\n\t\t\tAroundWebFilterObservation parent = AroundWebFilterObservation.create(before, after);\n\t\t\texchange.getAttributes().put(ATTRIBUTE, parent);\n\t\t\treturn parent;\n\t\t}\n\n\t}\n\n\tinterface AroundWebFilterObservation extends WebFilterObservation {\n\n\t\tAroundWebFilterObservation NOOP = new AroundWebFilterObservation() {\n\t\t};\n\n\t\tstatic AroundWebFilterObservation create(Observation before, Observation after) {\n\t\t\tif (before.isNoop() || after.isNoop()) {\n\t\t\t\treturn NOOP;\n\t\t\t}\n\t\t\treturn new SimpleAroundWebFilterObservation(before, after);\n\t\t}\n\n\t\tdefault Observation before() {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\tdefault Observation after() {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\tclass SimpleAroundWebFilterObservation implements AroundWebFilterObservation {\n\n\t\t\tprivate final Object lock = new Object();\n\n\t\t\tprivate final PhasedObservation before;\n\n\t\t\tprivate final PhasedObservation after;\n\n\t\t\tprivate volatile PhasedObservation currentObservation = PhasedObservation.NOOP;\n\n\t\t\tSimpleAroundWebFilterObservation(Observation before, Observation after) {\n\t\t\t\tthis.before = new PhasedObservation(before);\n\t\t\t\tthis.after = new PhasedObservation(after);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation start() {\n\t\t\t\tsynchronized (this.lock) {\n\t\t\t\t\tif (this.currentObservation == PhasedObservation.NOOP) {\n\t\t\t\t\t\tthis.before.start();\n\t\t\t\t\t\tthis.currentObservation = this.before;\n\t\t\t\t\t\treturn this.currentObservation;\n\t\t\t\t\t}\n\t\t\t\t\tif (this.currentObservation == this.before) {\n\t\t\t\t\t\tthis.before.stop();\n\t\t\t\t\t\tthis.after.start();\n\t\t\t\t\t\tthis.currentObservation = this.after;\n\t\t\t\t\t\treturn this.currentObservation;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn Observation.NOOP;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation error(Throwable ex) {\n\t\t\t\tthis.currentObservation.error(ex);\n\t\t\t\treturn this.currentObservation.observation;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void stop() {\n\t\t\t\tthis.before.stop();\n\t\t\t\tthis.after.stop();\n\t\t\t}\n\n\t\t\tprivate void close() {\n\t\t\t\tthis.before.close();\n\t\t\t\tthis.after.close();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation contextualName(@Nullable String contextualName) {\n\t\t\t\treturn this.currentObservation.observation.contextualName(contextualName);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation parentObservation(@Nullable Observation parentObservation) {\n\t\t\t\treturn this.currentObservation.observation.parentObservation(parentObservation);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation lowCardinalityKeyValue(KeyValue keyValue) {\n\t\t\t\treturn this.currentObservation.observation.lowCardinalityKeyValue(keyValue);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation highCardinalityKeyValue(KeyValue keyValue) {\n\t\t\t\treturn this.currentObservation.observation.highCardinalityKeyValue(keyValue);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation observationConvention(ObservationConvention<?> observationConvention) {\n\t\t\t\treturn this.currentObservation.observation.observationConvention(observationConvention);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation event(Event event) {\n\t\t\t\treturn this.currentObservation.observation.event(event);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Context getContext() {\n\t\t\t\treturn this.currentObservation.observation.getContext();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Scope openScope() {\n\t\t\t\treturn this.currentObservation.observation.openScope();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic WebFilterChain wrap(WebFilterChain chain) {\n\t\t\t\treturn (exchange) -> {\n\t\t\t\t\tstop();\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\treturn chain.filter(exchange)\n\t\t\t\t\t\t\t.doOnSuccess((v) -> start())\n\t\t\t\t\t\t\t.doOnCancel(this::start)\n\t\t\t\t\t\t\t.doOnError((t) -> {\n\t\t\t\t\t\t\t\terror(t);\n\t\t\t\t\t\t\t\tstart();\n\t\t\t\t\t\t\t});\n\t\t\t\t\t// @formatter:on\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic WebFilter wrap(WebFilter filter) {\n\t\t\t\treturn (exchange, chain) -> {\n\t\t\t\t\tstart();\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\treturn filter.filter(exchange, chain)\n\t\t\t\t\t\t\t.doOnSuccess((v) -> close())\n\t\t\t\t\t\t\t.doOnCancel(this::close)\n\t\t\t\t\t\t\t.doOnError((t) -> {\n\t\t\t\t\t\t\t\terror(t);\n\t\t\t\t\t\t\t\tclose();\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.contextWrite((context) -> context.put(ObservationThreadLocalAccessor.KEY, this));\n\t\t\t\t\t// @formatter:on\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation before() {\n\t\t\t\treturn this.before.observation;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation after() {\n\t\t\t\treturn this.after.observation;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn this.currentObservation.observation.toString();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tinterface WebFilterObservation extends Observation {\n\n\t\tWebFilterObservation NOOP = new WebFilterObservation() {\n\t\t};\n\n\t\tstatic WebFilterObservation create(Observation observation) {\n\t\t\tif (observation.isNoop()) {\n\t\t\t\treturn NOOP;\n\t\t\t}\n\t\t\treturn new SimpleWebFilterObservation(observation);\n\t\t}\n\n\t\t@Override\n\t\tdefault Observation contextualName(@Nullable String contextualName) {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tdefault Observation parentObservation(@Nullable Observation parentObservation) {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tdefault Observation lowCardinalityKeyValue(KeyValue keyValue) {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tdefault Observation highCardinalityKeyValue(KeyValue keyValue) {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tdefault Observation observationConvention(ObservationConvention<?> observationConvention) {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tdefault Observation error(Throwable error) {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tdefault Observation event(Event event) {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tdefault Observation start() {\n\t\t\treturn Observation.NOOP;\n\t\t}\n\n\t\t@Override\n\t\tdefault Context getContext() {\n\t\t\treturn new Observation.Context();\n\t\t}\n\n\t\t@Override\n\t\tdefault void stop() {\n\n\t\t}\n\n\t\t@Override\n\t\tdefault Scope openScope() {\n\t\t\treturn Scope.NOOP;\n\t\t}\n\n\t\tdefault WebFilter wrap(WebFilter filter) {\n\t\t\treturn filter;\n\t\t}\n\n\t\tdefault WebFilterChain wrap(WebFilterChain chain) {\n\t\t\treturn chain;\n\t\t}\n\n\t\tclass SimpleWebFilterObservation implements WebFilterObservation {\n\n\t\t\tprivate final PhasedObservation observation;\n\n\t\t\tSimpleWebFilterObservation(Observation observation) {\n\t\t\t\tthis.observation = new PhasedObservation(observation);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation start() {\n\t\t\t\treturn this.observation.start();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation error(Throwable ex) {\n\t\t\t\treturn this.observation.error(ex);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void stop() {\n\t\t\t\tthis.observation.stop();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation contextualName(@Nullable String contextualName) {\n\t\t\t\treturn this.observation.contextualName(contextualName);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation parentObservation(@Nullable Observation parentObservation) {\n\t\t\t\treturn this.observation.parentObservation(parentObservation);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation lowCardinalityKeyValue(KeyValue keyValue) {\n\t\t\t\treturn this.observation.lowCardinalityKeyValue(keyValue);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation highCardinalityKeyValue(KeyValue keyValue) {\n\t\t\t\treturn this.observation.highCardinalityKeyValue(keyValue);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation observationConvention(ObservationConvention<?> observationConvention) {\n\t\t\t\treturn this.observation.observationConvention(observationConvention);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Observation event(Event event) {\n\t\t\t\treturn this.observation.event(event);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Context getContext() {\n\t\t\t\treturn this.observation.getContext();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Scope openScope() {\n\t\t\t\treturn this.observation.openScope();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic WebFilter wrap(WebFilter filter) {\n\t\t\t\tif (this.observation.isNoop()) {\n\t\t\t\t\treturn filter;\n\t\t\t\t}\n\t\t\t\treturn (exchange, chain) -> {\n\t\t\t\t\tthis.observation.start();\n\t\t\t\t\treturn filter.filter(exchange, chain)\n\t\t\t\t\t\t.doOnSuccess((v) -> this.observation.stop())\n\t\t\t\t\t\t.doOnCancel(this.observation::stop)\n\t\t\t\t\t\t.doOnError((t) -> {\n\t\t\t\t\t\t\tthis.observation.error(t);\n\t\t\t\t\t\t\tthis.observation.stop();\n\t\t\t\t\t\t});\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic WebFilterChain wrap(WebFilterChain chain) {\n\t\t\t\tif (this.observation.isNoop()) {\n\t\t\t\t\treturn chain;\n\t\t\t\t}\n\t\t\t\treturn (exchange) -> {\n\t\t\t\t\tthis.observation.start();\n\t\t\t\t\treturn chain.filter(exchange)\n\t\t\t\t\t\t.doOnSuccess((v) -> this.observation.stop())\n\t\t\t\t\t\t.doOnCancel(this.observation::stop)\n\t\t\t\t\t\t.doOnError((t) -> {\n\t\t\t\t\t\t\tthis.observation.error(t);\n\t\t\t\t\t\t\tthis.observation.stop();\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.contextWrite((context) -> context.put(ObservationThreadLocalAccessor.KEY, this.observation));\n\t\t\t\t};\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic final class WebFilterChainObservationContext extends Observation.Context {\n\n\t\tprivate final String filterSection;\n\n\t\tprivate @Nullable String filterName;\n\n\t\tprivate int chainPosition;\n\n\t\tprivate int chainSize;\n\n\t\tprivate WebFilterChainObservationContext(String filterSection) {\n\t\t\tthis.filterSection = filterSection;\n\t\t}\n\n\t\tstatic WebFilterChainObservationContext before() {\n\t\t\treturn new WebFilterChainObservationContext(\"before\");\n\t\t}\n\n\t\tstatic WebFilterChainObservationContext after() {\n\t\t\treturn new WebFilterChainObservationContext(\"after\");\n\t\t}\n\n\t\tString getFilterSection() {\n\t\t\treturn this.filterSection;\n\t\t}\n\n\t\t@Nullable String getFilterName() {\n\t\t\treturn this.filterName;\n\t\t}\n\n\t\tvoid setFilterName(String filterName) {\n\t\t\tthis.filterName = filterName;\n\t\t}\n\n\t\tint getChainPosition() {\n\t\t\treturn this.chainPosition;\n\t\t}\n\n\t\tvoid setChainPosition(int chainPosition) {\n\t\t\tthis.chainPosition = chainPosition;\n\t\t}\n\n\t\tint getChainSize() {\n\t\t\treturn this.chainSize;\n\t\t}\n\n\t\tvoid setChainSize(int chainSize) {\n\t\t\tthis.chainSize = chainSize;\n\t\t}\n\n\t}\n\n\tstatic final class WebFilterChainObservationConvention\n\t\t\timplements ObservationConvention<WebFilterChainObservationContext> {\n\n\t\tstatic final String CHAIN_OBSERVATION_NAME = \"spring.security.filterchains\";\n\n\t\tprivate static final String CHAIN_POSITION_NAME = \"spring.security.filterchain.position\";\n\n\t\tprivate static final String CHAIN_SIZE_NAME = \"spring.security.filterchain.size\";\n\n\t\tprivate static final String FILTER_SECTION_NAME = \"spring.security.reached.filter.section\";\n\n\t\tprivate static final String FILTER_NAME = \"spring.security.reached.filter.name\";\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn CHAIN_OBSERVATION_NAME;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getContextualName(WebFilterChainObservationContext context) {\n\t\t\treturn \"security filterchain \" + context.getFilterSection();\n\t\t}\n\n\t\t@Override\n\t\tpublic KeyValues getLowCardinalityKeyValues(WebFilterChainObservationContext context) {\n\t\t\treturn KeyValues.of(CHAIN_SIZE_NAME, String.valueOf(context.getChainSize()))\n\t\t\t\t.and(CHAIN_POSITION_NAME, String.valueOf(context.getChainPosition()))\n\t\t\t\t.and(FILTER_SECTION_NAME, context.getFilterSection())\n\t\t\t\t.and(FILTER_NAME,\n\t\t\t\t\t\t(StringUtils.hasText(context.getFilterName())) ? context.getFilterName() : KeyValue.NONE_VALUE);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supportsContext(Observation.Context context) {\n\t\t\treturn context instanceof WebFilterChainObservationContext;\n\t\t}\n\n\t}\n\n\tprivate static final class PhasedObservation implements Observation {\n\n\t\tprivate static final PhasedObservation NOOP = new PhasedObservation(Observation.NOOP);\n\n\t\tprivate final Object lock = new Object();\n\n\t\tprivate volatile int phase = 0;\n\n\t\tprivate final Observation observation;\n\n\t\tprivate PhasedObservation(Observation observation) {\n\t\t\tthis.observation = observation;\n\t\t}\n\n\t\t@Override\n\t\tpublic Observation contextualName(@Nullable String contextualName) {\n\t\t\treturn this.observation.contextualName(contextualName);\n\t\t}\n\n\t\t@Override\n\t\tpublic Observation parentObservation(@Nullable Observation parentObservation) {\n\t\t\treturn this.observation.parentObservation(parentObservation);\n\t\t}\n\n\t\t@Override\n\t\tpublic Observation lowCardinalityKeyValue(KeyValue keyValue) {\n\t\t\treturn this.observation.lowCardinalityKeyValue(keyValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic Observation highCardinalityKeyValue(KeyValue keyValue) {\n\t\t\treturn this.observation.highCardinalityKeyValue(keyValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic Observation observationConvention(ObservationConvention<?> observationConvention) {\n\t\t\treturn this.observation.observationConvention(observationConvention);\n\t\t}\n\n\t\t@Override\n\t\tpublic Observation event(Event event) {\n\t\t\treturn this.observation.event(event);\n\t\t}\n\n\t\t@Override\n\t\tpublic Context getContext() {\n\t\t\treturn this.observation.getContext();\n\t\t}\n\n\t\t@Override\n\t\tpublic Scope openScope() {\n\t\t\treturn this.observation.openScope();\n\t\t}\n\n\t\t@Override\n\t\tpublic PhasedObservation start() {\n\t\t\tsynchronized (this.lock) {\n\t\t\t\tif (this.phase == 0) {\n\t\t\t\t\tthis.observation.start();\n\t\t\t\t\tthis.phase = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic PhasedObservation error(Throwable ex) {\n\t\t\tsynchronized (this.lock) {\n\t\t\t\tif (this.phase == 1) {\n\t\t\t\t\tthis.observation.error(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void stop() {\n\t\t\tsynchronized (this.lock) {\n\t\t\t\tif (this.phase == 1) {\n\t\t\t\t\tthis.observation.stop();\n\t\t\t\t\tthis.phase = 2;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvoid close() {\n\t\t\tsynchronized (this.lock) {\n\t\t\t\tif (this.phase == 1) {\n\t\t\t\t\tthis.observation.stop();\n\t\t\t\t}\n\t\t\t\tthis.phase = 3;\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/SecurityWebFilterChain.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\n\n/**\n * Defines a filter chain which is capable of being matched against a\n * {@link ServerWebExchange} in order to decide whether it applies to that request.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic interface SecurityWebFilterChain {\n\n\t/**\n\t * Determines if this {@link SecurityWebFilterChain} matches the provided\n\t * {@link ServerWebExchange}\n\t * @param exchange the {@link ServerWebExchange}\n\t * @return true if it matches, else false\n\t */\n\tMono<Boolean> matches(ServerWebExchange exchange);\n\n\t/**\n\t * The {@link WebFilter} to use\n\t * @return\n\t */\n\tFlux<WebFilter> getWebFilters();\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ServerAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Used to request authentication\n *\n * @author Rob Winch\n * @since 5.0\n */\n@FunctionalInterface\npublic interface ServerAuthenticationEntryPoint {\n\n\t/**\n\t * Initiates the authentication flow\n\t * @param exchange\n\t * @param ex\n\t * @return {@code Mono<Void>} to indicate when the request for authentication is\n\t * complete\n\t */\n\tMono<Void> commence(ServerWebExchange exchange, AuthenticationException ex);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ServerFormLoginAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Converts a ServerWebExchange into a UsernamePasswordAuthenticationToken from the form\n * data HTTP parameters.\n *\n * @author Rob Winch\n * @since 5.0\n * @deprecated use\n * {@link org.springframework.security.web.server.authentication.ServerFormLoginAuthenticationConverter}\n * instead.\n */\n@Deprecated\npublic class ServerFormLoginAuthenticationConverter implements Function<ServerWebExchange, Mono<Authentication>> {\n\n\tprivate String usernameParameter = \"username\";\n\n\tprivate String passwordParameter = \"password\";\n\n\t@Override\n\t@Deprecated\n\tpublic Mono<Authentication> apply(ServerWebExchange exchange) {\n\t\treturn exchange.getFormData().map(this::createAuthentication);\n\t}\n\n\tprivate UsernamePasswordAuthenticationToken createAuthentication(MultiValueMap<String, String> data) {\n\t\tString username = data.getFirst(this.usernameParameter);\n\t\tString password = data.getFirst(this.passwordParameter);\n\t\treturn UsernamePasswordAuthenticationToken.unauthenticated(username, password);\n\t}\n\n\t/**\n\t * The parameter name of the form data to extract the username\n\t * @param usernameParameter the username HTTP parameter\n\t */\n\tpublic void setUsernameParameter(String usernameParameter) {\n\t\tAssert.notNull(usernameParameter, \"usernameParameter cannot be null\");\n\t\tthis.usernameParameter = usernameParameter;\n\t}\n\n\t/**\n\t * The parameter name of the form data to extract the password\n\t * @param passwordParameter the password HTTP parameter\n\t */\n\tpublic void setPasswordParameter(String passwordParameter) {\n\t\tAssert.notNull(passwordParameter, \"passwordParameter cannot be null\");\n\t\tthis.passwordParameter = passwordParameter;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ServerHttpBasicAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Converts from a {@link ServerWebExchange} to an {@link Authentication} that can be\n * authenticated.\n *\n * @author Rob Winch\n * @since 5.0\n * @deprecated Use\n * {@link org.springframework.security.web.server.authentication.ServerHttpBasicAuthenticationConverter}\n * instead.\n */\n@Deprecated\npublic class ServerHttpBasicAuthenticationConverter implements Function<ServerWebExchange, Mono<Authentication>> {\n\n\tpublic static final String BASIC = \"Basic \";\n\n\tprivate Charset credentialsCharset = StandardCharsets.UTF_8;\n\n\t@Override\n\t@Deprecated\n\tpublic Mono<Authentication> apply(ServerWebExchange exchange) {\n\t\tServerHttpRequest request = exchange.getRequest();\n\t\tString authorization = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);\n\t\tif (!StringUtils.startsWithIgnoreCase(authorization, \"basic \")) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tString credentials = (authorization.length() <= BASIC.length()) ? \"\" : authorization.substring(BASIC.length());\n\t\tString decoded = new String(base64Decode(credentials), this.credentialsCharset);\n\t\tString[] parts = decoded.split(\":\", 2);\n\t\tif (parts.length != 2) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\treturn Mono.just(UsernamePasswordAuthenticationToken.unauthenticated(parts[0], parts[1]));\n\t}\n\n\tprivate byte[] base64Decode(String value) {\n\t\ttry {\n\t\t\treturn Base64.getDecoder().decode(value);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn new byte[0];\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Charset} used to decode the Base64-encoded bytes of the basic\n\t * authentication credentials. The default is <code>UTF_8</code>.\n\t * @param credentialsCharset the {@link Charset} used to decode the Base64-encoded\n\t * bytes of the basic authentication credentials\n\t * @since 5.7\n\t */\n\tpublic final void setCredentialsCharset(Charset credentialsCharset) {\n\t\tAssert.notNull(credentialsCharset, \"credentialsCharset cannot be null\");\n\t\tthis.credentialsCharset = credentialsCharset;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ServerRedirectStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A strategy for performing redirects.\n *\n * @author Rob Winch\n * @since 5.0\n */\n@FunctionalInterface\npublic interface ServerRedirectStrategy {\n\n\t/**\n\t * Performs a redirect based upon the provided {@link ServerWebExchange} and\n\t * {@link URI}\n\t * @param exchange the {@link ServerWebExchange} to use\n\t * @param location the location to redirect to\n\t * @return {@code Mono<Void>} to indicate when redirect is complete\n\t */\n\tMono<Void> sendRedirect(ServerWebExchange exchange, URI location);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ServerWebExchangeThreadLocalAccessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport io.micrometer.context.ThreadLocalAccessor;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ThreadLocalAccessor} for accessing a {@link ServerWebExchange}.\n * <p>\n * This class adapts the existing Reactor Context attribute\n * {@code ServerWebExchange.class} to the {@link ThreadLocalAccessor} contract to allow\n * Micrometer Context Propagation to automatically propagate a {@link ServerWebExchange}\n * in Reactive applications. It is automatically registered with the\n * {@link io.micrometer.context.ContextRegistry} through the\n * {@link java.util.ServiceLoader} mechanism when context-propagation is on the classpath.\n *\n * @author Steve Riesenberg\n * @since 6.5\n * @see io.micrometer.context.ContextRegistry\n */\npublic final class ServerWebExchangeThreadLocalAccessor implements ThreadLocalAccessor<ServerWebExchange> {\n\n\tprivate static final ThreadLocal<ServerWebExchange> threadLocal = new ThreadLocal<>();\n\n\t@Override\n\tpublic Object key() {\n\t\treturn ServerWebExchange.class;\n\t}\n\n\t@Override\n\tpublic ServerWebExchange getValue() {\n\t\treturn threadLocal.get();\n\t}\n\n\t@Override\n\tpublic void setValue(ServerWebExchange exchange) {\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\tthreadLocal.set(exchange);\n\t}\n\n\t@Override\n\tpublic void setValue() {\n\t\tthreadLocal.remove();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/WebFilterChainProxy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.server.firewall.HttpStatusExchangeRejectedHandler;\nimport org.springframework.security.web.server.firewall.ServerExchangeRejectedException;\nimport org.springframework.security.web.server.firewall.ServerExchangeRejectedHandler;\nimport org.springframework.security.web.server.firewall.ServerWebExchangeFirewall;\nimport org.springframework.security.web.server.firewall.StrictServerWebExchangeFirewall;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.handler.DefaultWebFilterChain;\n\n/**\n * Used to delegate to a List of {@link SecurityWebFilterChain} instances.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebFilterChainProxy implements WebFilter {\n\n\tprivate final List<SecurityWebFilterChain> filters;\n\n\tprivate WebFilterChainDecorator filterChainDecorator = new DefaultWebFilterChainDecorator();\n\n\tprivate ServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();\n\n\tprivate ServerExchangeRejectedHandler exchangeRejectedHandler = new HttpStatusExchangeRejectedHandler();\n\n\tpublic WebFilterChainProxy(List<SecurityWebFilterChain> filters) {\n\t\tthis.filters = filters;\n\t}\n\n\tpublic WebFilterChainProxy(SecurityWebFilterChain... filters) {\n\t\tthis.filters = Arrays.asList(filters);\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.firewall.getFirewalledExchange(exchange)\n\t\t\t.flatMap((firewalledExchange) -> filterFirewalledExchange(firewalledExchange, chain))\n\t\t\t.onErrorResume(ServerExchangeRejectedException.class,\n\t\t\t\t\t(rejected) -> this.exchangeRejectedHandler.handle(exchange, rejected).then(Mono.empty()));\n\t}\n\n\tprivate Mono<Void> filterFirewalledExchange(ServerWebExchange firewalledExchange, WebFilterChain chain) {\n\t\treturn Flux.fromIterable(this.filters)\n\t\t\t.filterWhen((securityWebFilterChain) -> securityWebFilterChain.matches(firewalledExchange))\n\t\t\t.next()\n\t\t\t.switchIfEmpty(Mono\n\t\t\t\t.defer(() -> this.filterChainDecorator.decorate(chain).filter(firewalledExchange).then(Mono.empty())))\n\t\t\t.flatMap((securityWebFilterChain) -> securityWebFilterChain.getWebFilters().collectList())\n\t\t\t.map((filters) -> this.filterChainDecorator.decorate(chain, filters))\n\t\t\t.flatMap((securedChain) -> securedChain.filter(firewalledExchange));\n\t}\n\n\t/**\n\t * Protects the application using the provided\n\t * {@link StrictServerWebExchangeFirewall}.\n\t * @param firewall the {@link StrictServerWebExchangeFirewall} to use. Cannot be null.\n\t * @since 6.4\n\t */\n\tpublic void setFirewall(ServerWebExchangeFirewall firewall) {\n\t\tAssert.notNull(firewall, \"firewall cannot be null\");\n\t\tthis.firewall = firewall;\n\t}\n\n\t/**\n\t * Handles {@link ServerExchangeRejectedException} when the\n\t * {@link ServerWebExchangeFirewall} rejects the provided {@link ServerWebExchange}.\n\t * @param exchangeRejectedHandler the {@link ServerExchangeRejectedHandler} to use.\n\t */\n\tpublic void setExchangeRejectedHandler(ServerExchangeRejectedHandler exchangeRejectedHandler) {\n\t\tthis.exchangeRejectedHandler = exchangeRejectedHandler;\n\t}\n\n\t/**\n\t * Used to decorate the original {@link WebFilterChain} for each request\n\t *\n\t * <p>\n\t * By default, this decorates the filter chain with a {@link DefaultWebFilterChain}\n\t * that iterates through security filters and then delegates to the original chain\n\t * @param filterChainDecorator the strategy for constructing the filter chain\n\t * @since 6.0\n\t */\n\tpublic void setFilterChainDecorator(WebFilterChainDecorator filterChainDecorator) {\n\t\tAssert.notNull(filterChainDecorator, \"filterChainDecorator cannot be null\");\n\t\tthis.filterChainDecorator = filterChainDecorator;\n\t}\n\n\t/**\n\t * A strategy for decorating the provided filter chain with one that accounts for the\n\t * {@link SecurityFilterChain} for a given request.\n\t *\n\t * @author Josh Cummings\n\t * @since 6.0\n\t */\n\tpublic interface WebFilterChainDecorator {\n\n\t\t/**\n\t\t * Provide a new {@link WebFilterChain} that accounts for needed security\n\t\t * considerations when there are no security filters.\n\t\t * @param original the original {@link WebFilterChain}\n\t\t * @return a security-enabled {@link WebFilterChain}\n\t\t */\n\t\tdefault WebFilterChain decorate(WebFilterChain original) {\n\t\t\treturn decorate(original, Collections.emptyList());\n\t\t}\n\n\t\t/**\n\t\t * Provide a new {@link WebFilterChain} that accounts for the provided filters as\n\t\t * well as the original filter chain.\n\t\t * @param original the original {@link WebFilterChain}\n\t\t * @param filters the security filters\n\t\t * @return a security-enabled {@link WebFilterChain} that includes the provided\n\t\t * filters\n\t\t */\n\t\tWebFilterChain decorate(WebFilterChain original, List<WebFilter> filters);\n\n\t}\n\n\t/**\n\t * A {@link WebFilterChainDecorator} that uses the {@link DefaultWebFilterChain}\n\t *\n\t * @author Josh Cummings\n\t * @since 6.0\n\t */\n\tpublic static class DefaultWebFilterChainDecorator implements WebFilterChainDecorator {\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic WebFilterChain decorate(WebFilterChain original) {\n\t\t\treturn original;\n\t\t}\n\n\t\t/**\n\t\t * {@inheritDoc}\n\t\t */\n\t\t@Override\n\t\tpublic WebFilterChain decorate(WebFilterChain original, List<WebFilter> filters) {\n\t\t\treturn new DefaultWebFilterChain(original::filter, filters);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/WebFilterExchange.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * A composite of the {@link ServerWebExchange} and the {@link WebFilterChain}. This is\n * typically used as a value object for handling success and failures.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebFilterExchange {\n\n\tprivate final ServerWebExchange exchange;\n\n\tprivate final WebFilterChain chain;\n\n\tpublic WebFilterExchange(ServerWebExchange exchange, WebFilterChain chain) {\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\tAssert.notNull(chain, \"chain cannot be null\");\n\t\tthis.exchange = exchange;\n\t\tthis.chain = chain;\n\t}\n\n\t/**\n\t * Get the exchange\n\t * @return the exchange. Cannot be {@code null}\n\t */\n\tpublic ServerWebExchange getExchange() {\n\t\treturn this.exchange;\n\t}\n\n\t/**\n\t * The filter chain\n\t * @return the filter chain. Cannot be {@code null}\n\t */\n\tpublic WebFilterChain getChain() {\n\t\treturn this.chain;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/AnonymousAuthenticationWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Detects if there is no {@code Authentication} object in the\n * {@code ReactiveSecurityContextHolder}, and populates it with one if needed.\n *\n * @author Ankur Pathak\n * @author Mathieu Ouellet\n * @since 5.2.0\n */\npublic class AnonymousAuthenticationWebFilter implements WebFilter {\n\n\tprivate static final Log logger = LogFactory.getLog(AnonymousAuthenticationWebFilter.class);\n\n\tprivate String key;\n\n\tprivate Object principal;\n\n\tprivate List<GrantedAuthority> authorities;\n\n\t/**\n\t * Creates a filter with a principal named \"anonymousUser\" and the single authority\n\t * \"ROLE_ANONYMOUS\".\n\t * @param key the key to identify tokens created by this filter\n\t */\n\tpublic AnonymousAuthenticationWebFilter(String key) {\n\t\tthis(key, \"anonymousUser\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t}\n\n\t/**\n\t * @param key key the key to identify tokens created by this filter\n\t * @param principal the principal which will be used to represent anonymous users\n\t * @param authorities the authority list for anonymous users\n\t */\n\tpublic AnonymousAuthenticationWebFilter(String key, Object principal, List<GrantedAuthority> authorities) {\n\t\tAssert.hasLength(key, \"key cannot be null or empty\");\n\t\tAssert.notNull(principal, \"Anonymous authentication principal must be set\");\n\t\tAssert.notNull(authorities, \"Anonymous authorities must be set\");\n\t\tthis.key = key;\n\t\tthis.principal = principal;\n\t\tthis.authorities = authorities;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn ReactiveSecurityContextHolder.getContext().switchIfEmpty(Mono.defer(() -> {\n\t\t\tAuthentication authentication = createAuthentication(exchange);\n\t\t\tSecurityContext securityContext = new SecurityContextImpl(authentication);\n\t\t\tlogger.debug(LogMessage.format(\"Populated SecurityContext with anonymous token: '%s'\", authentication));\n\t\t\treturn chain.filter(exchange)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))\n\t\t\t\t.then(Mono.empty());\n\t\t})).flatMap((securityContext) -> {\n\t\t\tlogger.debug(LogMessage.format(\"SecurityContext contains anonymous token: '%s'\",\n\t\t\t\t\tsecurityContext.getAuthentication()));\n\t\t\treturn chain.filter(exchange);\n\t\t});\n\t}\n\n\tprotected Authentication createAuthentication(ServerWebExchange exchange) {\n\t\treturn new AnonymousAuthenticationToken(this.key, this.principal, this.authorities);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationConverterServerWebExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Matches if the {@link ServerAuthenticationConverter} can convert a\n * {@link ServerWebExchange} to an {@link Authentication}.\n *\n * @author David Kovac\n * @since 5.4\n * @see ServerAuthenticationConverter\n */\npublic final class AuthenticationConverterServerWebExchangeMatcher implements ServerWebExchangeMatcher {\n\n\tprivate final ServerAuthenticationConverter serverAuthenticationConverter;\n\n\tpublic AuthenticationConverterServerWebExchangeMatcher(\n\t\t\tServerAuthenticationConverter serverAuthenticationConverter) {\n\t\tAssert.notNull(serverAuthenticationConverter, \"serverAuthenticationConverter cannot be null\");\n\t\tthis.serverAuthenticationConverter = serverAuthenticationConverter;\n\t}\n\n\t@Override\n\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\treturn this.serverAuthenticationConverter.convert(exchange)\n\t\t\t.flatMap((a) -> MatchResult.match())\n\t\t\t.onErrorResume((ex) -> MatchResult.notMatch())\n\t\t\t.switchIfEmpty(MatchResult.notMatch());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.function.Function;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * A {@link WebFilter} that performs authentication of a particular request. An outline of\n * the logic:\n *\n * <ul>\n * <li>A request comes in and if it does not match\n * {@link #setRequiresAuthenticationMatcher(ServerWebExchangeMatcher)}, then this filter\n * does nothing and the {@link WebFilterChain} is continued. If it does match then...</li>\n * <li>An attempt to convert the {@link ServerWebExchange} into an {@link Authentication}\n * is made. If the result is empty, then the filter does nothing more and the\n * {@link WebFilterChain} is continued. If it does create an {@link Authentication}...\n * </li>\n * <li>The {@link ReactiveAuthenticationManager} specified in\n * {@link #AuthenticationWebFilter(ReactiveAuthenticationManager)} is used to perform\n * authentication.</li>\n * <li>The {@link ReactiveAuthenticationManagerResolver} specified in\n * {@link #AuthenticationWebFilter(ReactiveAuthenticationManagerResolver)} is used to\n * resolve the appropriate authentication manager from context to perform authentication.\n * </li>\n * <li>If authentication is successful, {@link ServerAuthenticationSuccessHandler} is\n * invoked and the authentication is set on {@link ReactiveSecurityContextHolder}, else\n * {@link ServerAuthenticationFailureHandler} is invoked</li>\n * </ul>\n *\n * @author Rob Winch\n * @author Rafiullah Hamedy\n * @author Mathieu Ouellet\n * @since 5.0\n */\npublic class AuthenticationWebFilter implements WebFilter {\n\n\tprivate static final Log logger = LogFactory.getLog(AuthenticationWebFilter.class);\n\n\tprivate final ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver;\n\n\tprivate ServerAuthenticationSuccessHandler authenticationSuccessHandler = new WebFilterChainServerAuthenticationSuccessHandler();\n\n\tprivate ServerAuthenticationConverter authenticationConverter = new ServerHttpBasicAuthenticationConverter();\n\n\tprivate ServerAuthenticationFailureHandler authenticationFailureHandler = new ServerAuthenticationEntryPointFailureHandler(\n\t\t\tnew HttpBasicServerAuthenticationEntryPoint());\n\n\tprivate ServerSecurityContextRepository securityContextRepository = NoOpServerSecurityContextRepository\n\t\t.getInstance();\n\n\tprivate ServerWebExchangeMatcher requiresAuthenticationMatcher = ServerWebExchangeMatchers.anyExchange();\n\n\t/**\n\t * Creates an instance\n\t * @param authenticationManager the authentication manager to use\n\t */\n\tpublic AuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager) {\n\t\tAssert.notNull(authenticationManager, \"authenticationManager cannot be null\");\n\t\tthis.authenticationManagerResolver = (request) -> Mono.just(authenticationManager);\n\t}\n\n\t/**\n\t * Creates an instance\n\t * @param authenticationManagerResolver the authentication manager resolver to use\n\t * @since 5.3\n\t */\n\tpublic AuthenticationWebFilter(\n\t\t\tReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver) {\n\t\tAssert.notNull(authenticationManagerResolver, \"authenticationResolverManager cannot be null\");\n\t\tthis.authenticationManagerResolver = authenticationManagerResolver;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.requiresAuthenticationMatcher.matches(exchange)\n\t\t\t.filter((matchResult) -> matchResult.isMatch())\n\t\t\t.flatMap((matchResult) -> this.authenticationConverter.convert(exchange))\n\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t.flatMap((token) -> authenticate(exchange, chain, token))\n\t\t\t.onErrorResume(AuthenticationException.class, (ex) -> this.authenticationFailureHandler\n\t\t\t\t.onAuthenticationFailure(new WebFilterExchange(exchange, chain), ex));\n\t}\n\n\tprivate Mono<Void> authenticate(ServerWebExchange exchange, WebFilterChain chain, Authentication token) {\n\t\treturn this.authenticationManagerResolver.resolve(exchange)\n\t\t\t.flatMap((authenticationManager) -> authenticationManager.authenticate(token))\n\t\t\t.switchIfEmpty(Mono\n\t\t\t\t.defer(() -> Mono.error(new IllegalStateException(\"No provider found for \" + token.getClass()))))\n\t\t\t.flatMap(\n\t\t\t\t\t(authentication) -> onAuthenticationSuccess(authentication, new WebFilterExchange(exchange, chain)))\n\t\t\t.doOnError(AuthenticationException.class,\n\t\t\t\t\t(ex) -> logger.debug(LogMessage.format(\"Authentication failed: %s\", ex.getMessage()), ex));\n\t}\n\n\tprotected Mono<Void> onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) {\n\t\tServerWebExchange exchange = webFilterExchange.getExchange();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(authentication);\n\t\treturn this.securityContextRepository.save(exchange, securityContext)\n\t\t\t.then(this.authenticationSuccessHandler.onAuthenticationSuccess(webFilterExchange, authentication))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)));\n\t}\n\n\t/**\n\t * Sets the repository for persisting the SecurityContext. Default is\n\t * {@link NoOpServerSecurityContextRepository}\n\t * @param securityContextRepository the repository to use\n\t */\n\tpublic void setSecurityContextRepository(ServerSecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Sets the authentication success handler. Default is\n\t * {@link WebFilterChainServerAuthenticationSuccessHandler}\n\t * @param authenticationSuccessHandler the success handler to use\n\t */\n\tpublic void setAuthenticationSuccessHandler(ServerAuthenticationSuccessHandler authenticationSuccessHandler) {\n\t\tAssert.notNull(authenticationSuccessHandler, \"authenticationSuccessHandler cannot be null\");\n\t\tthis.authenticationSuccessHandler = authenticationSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the strategy used for converting from a {@link ServerWebExchange} to an\n\t * {@link Authentication} used for authenticating with the provided\n\t * {@link ReactiveAuthenticationManager}. If the result is empty, then it signals that\n\t * no authentication attempt should be made. The default converter is\n\t * {@link ServerHttpBasicAuthenticationConverter}\n\t * @param authenticationConverter the converter to use\n\t * @deprecated As of 5.1 in favor of\n\t * {@link #setServerAuthenticationConverter(ServerAuthenticationConverter)}\n\t * @see #setServerAuthenticationConverter(ServerAuthenticationConverter)\n\t */\n\t@Deprecated\n\tpublic void setAuthenticationConverter(Function<ServerWebExchange, Mono<Authentication>> authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tsetServerAuthenticationConverter(authenticationConverter::apply);\n\t}\n\n\t/**\n\t * Sets the strategy used for converting from a {@link ServerWebExchange} to an\n\t * {@link Authentication} used for authenticating with the provided\n\t * {@link ReactiveAuthenticationManager}. If the result is empty, then it signals that\n\t * no authentication attempt should be made. The default converter is\n\t * {@link ServerHttpBasicAuthenticationConverter}\n\t * @param authenticationConverter the converter to use\n\t * @since 5.1\n\t */\n\tpublic void setServerAuthenticationConverter(ServerAuthenticationConverter authenticationConverter) {\n\t\tAssert.notNull(authenticationConverter, \"authenticationConverter cannot be null\");\n\t\tthis.authenticationConverter = authenticationConverter;\n\t}\n\n\t/**\n\t * Sets the failure handler used when authentication fails. The default is to prompt\n\t * for basic authentication.\n\t * @param authenticationFailureHandler the handler to use. Cannot be null.\n\t */\n\tpublic void setAuthenticationFailureHandler(ServerAuthenticationFailureHandler authenticationFailureHandler) {\n\t\tAssert.notNull(authenticationFailureHandler, \"authenticationFailureHandler cannot be null\");\n\t\tthis.authenticationFailureHandler = authenticationFailureHandler;\n\t}\n\n\t/**\n\t * Sets the matcher used to determine when creating an {@link Authentication} from\n\t * {@link #setServerAuthenticationConverter(ServerAuthenticationConverter)} to be\n\t * authentication. If the converter returns an empty result, then no authentication is\n\t * attempted. The default is any request\n\t * @param requiresAuthenticationMatcher the matcher to use. Cannot be null.\n\t */\n\tpublic void setRequiresAuthenticationMatcher(ServerWebExchangeMatcher requiresAuthenticationMatcher) {\n\t\tAssert.notNull(requiresAuthenticationMatcher, \"requiresAuthenticationMatcher cannot be null\");\n\t\tthis.requiresAuthenticationMatcher = requiresAuthenticationMatcher;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ConcurrentSessionControlServerAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport reactor.core.publisher.Mono;\nimport reactor.util.function.Tuples;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.ReactiveSessionInformation;\nimport org.springframework.security.core.session.ReactiveSessionRegistry;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.WebSession;\n\n/**\n * Controls the number of sessions a user can have concurrently authenticated in an\n * application. It also allows for customizing behaviour when an authentication attempt is\n * made while the user already has the maximum number of sessions open. By default, it\n * allows a maximum of 1 session per user, if the maximum is exceeded, the user's least\n * recently used session(s) will be expired.\n *\n * @author Marcus da Coregio\n * @since 6.3\n * @see ServerMaximumSessionsExceededHandler\n * @see RegisterSessionServerAuthenticationSuccessHandler\n */\npublic final class ConcurrentSessionControlServerAuthenticationSuccessHandler\n\t\timplements ServerAuthenticationSuccessHandler {\n\n\tprivate final ReactiveSessionRegistry sessionRegistry;\n\n\tprivate final ServerMaximumSessionsExceededHandler maximumSessionsExceededHandler;\n\n\tprivate SessionLimit sessionLimit = SessionLimit.of(1);\n\n\tpublic ConcurrentSessionControlServerAuthenticationSuccessHandler(ReactiveSessionRegistry sessionRegistry,\n\t\t\tServerMaximumSessionsExceededHandler maximumSessionsExceededHandler) {\n\t\tAssert.notNull(sessionRegistry, \"sessionRegistry cannot be null\");\n\t\tAssert.notNull(maximumSessionsExceededHandler, \"maximumSessionsExceededHandler cannot be null\");\n\t\tthis.sessionRegistry = sessionRegistry;\n\t\tthis.maximumSessionsExceededHandler = maximumSessionsExceededHandler;\n\t}\n\n\t@Override\n\tpublic Mono<Void> onAuthenticationSuccess(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn this.sessionLimit.apply(authentication)\n\t\t\t.flatMap((maxSessions) -> handleConcurrency(exchange, authentication, maxSessions));\n\t}\n\n\tprivate Mono<Void> handleConcurrency(WebFilterExchange exchange, Authentication authentication,\n\t\t\tInteger maximumSessions) {\n\t\treturn this.sessionRegistry.getAllSessions(Objects.requireNonNull(authentication.getPrincipal()))\n\t\t\t.collectList()\n\t\t\t.flatMap((registeredSessions) -> exchange.getExchange()\n\t\t\t\t.getSession()\n\t\t\t\t.map((currentSession) -> Tuples.of(currentSession, registeredSessions)))\n\t\t\t.flatMap((sessionTuple) -> {\n\t\t\t\tWebSession currentSession = sessionTuple.getT1();\n\t\t\t\tList<ReactiveSessionInformation> registeredSessions = sessionTuple.getT2();\n\t\t\t\tint registeredSessionsCount = registeredSessions.size();\n\t\t\t\tif (registeredSessionsCount < maximumSessions) {\n\t\t\t\t\treturn Mono.empty();\n\t\t\t\t}\n\t\t\t\tif (registeredSessionsCount == maximumSessions) {\n\t\t\t\t\tfor (ReactiveSessionInformation registeredSession : registeredSessions) {\n\t\t\t\t\t\tif (registeredSession.getSessionId().equals(currentSession.getId())) {\n\t\t\t\t\t\t\treturn Mono.empty();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this.maximumSessionsExceededHandler.handle(new MaximumSessionsContext(authentication,\n\t\t\t\t\t\tregisteredSessions, maximumSessions, currentSession));\n\t\t\t});\n\t}\n\n\t/**\n\t * Sets the strategy used to resolve the maximum number of sessions that are allowed\n\t * for a specific {@link Authentication}. By default, it returns {@code 1} for any\n\t * authentication.\n\t * @param sessionLimit the {@link SessionLimit} to use\n\t */\n\tpublic void setSessionLimit(SessionLimit sessionLimit) {\n\t\tAssert.notNull(sessionLimit, \"sessionLimit cannot be null\");\n\t\tthis.sessionLimit = sessionLimit;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ServerAuthenticationConverter} that delegates to other\n * {@link ServerAuthenticationConverter} instances.\n *\n * @author DingHao\n * @since 6.3\n */\npublic final class DelegatingServerAuthenticationConverter implements ServerAuthenticationConverter {\n\n\tprivate final List<ServerAuthenticationConverter> delegates;\n\n\tprivate boolean continueOnError = false;\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tpublic DelegatingServerAuthenticationConverter(ServerAuthenticationConverter... converters) {\n\t\tthis(List.of(converters));\n\t}\n\n\tpublic DelegatingServerAuthenticationConverter(List<ServerAuthenticationConverter> converters) {\n\t\tAssert.notEmpty(converters, \"converters cannot be null\");\n\t\tthis.delegates = converters;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> convert(ServerWebExchange exchange) {\n\t\tFlux<ServerAuthenticationConverter> result = Flux.fromIterable(this.delegates);\n\t\tFunction<ServerAuthenticationConverter, Mono<Authentication>> logging = (\n\t\t\t\tconverter) -> converter.convert(exchange).doOnError(this.logger::debug);\n\t\treturn ((this.continueOnError) ? result.concatMapDelayError(logging) : result.concatMap(logging)).next();\n\t}\n\n\t/**\n\t * Continue iterating when a delegate errors, defaults to {@code false}\n\t * @param continueOnError whether to continue when a delegate errors\n\t * @since 6.3\n\t */\n\tpublic void setContinueOnError(boolean continueOnError) {\n\t\tthis.continueOnError = continueOnError;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.util.Assert;\n\n/**\n * Delegates to a collection of {@link ServerAuthenticationSuccessHandler}\n * implementations.\n *\n * @author Rob Winch\n * @since 5.1\n */\npublic class DelegatingServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {\n\n\tprivate final List<ServerAuthenticationSuccessHandler> delegates;\n\n\tpublic DelegatingServerAuthenticationSuccessHandler(ServerAuthenticationSuccessHandler... delegates) {\n\t\tAssert.notEmpty(delegates, \"delegates cannot be null or empty\");\n\t\tthis.delegates = Arrays.asList(delegates);\n\t}\n\n\t/**\n\t * Creates a new instance with the provided list of delegates\n\t * @param delegates the {@link List} of {@link ServerAuthenticationSuccessHandler}\n\t * @since 6.3\n\t */\n\tpublic DelegatingServerAuthenticationSuccessHandler(List<ServerAuthenticationSuccessHandler> delegates) {\n\t\tAssert.notEmpty(delegates, \"delegates cannot be null or empty\");\n\t\tthis.delegates = delegates;\n\t}\n\n\t@Override\n\tpublic Mono<Void> onAuthenticationSuccess(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn Flux.fromIterable(this.delegates)\n\t\t\t.concatMap((delegate) -> delegate.onAuthenticationSuccess(exchange, authentication))\n\t\t\t.then();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/HttpBasicServerAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Prompts a user for HTTP Basic authentication.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class HttpBasicServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {\n\n\tprivate static final String WWW_AUTHENTICATE = \"WWW-Authenticate\";\n\n\tprivate static final String DEFAULT_REALM = \"Realm\";\n\n\tprivate static String WWW_AUTHENTICATE_FORMAT = \"Basic realm=\\\"%s\\\"\";\n\n\tprivate String headerValue = createHeaderValue(DEFAULT_REALM);\n\n\t@Override\n\tpublic Mono<Void> commence(ServerWebExchange exchange, AuthenticationException ex) {\n\t\treturn Mono.fromRunnable(() -> {\n\t\t\tServerHttpResponse response = exchange.getResponse();\n\t\t\tresponse.setStatusCode(HttpStatus.UNAUTHORIZED);\n\t\t\tresponse.getHeaders().set(WWW_AUTHENTICATE, this.headerValue);\n\t\t});\n\t}\n\n\t/**\n\t * Sets the realm to be used\n\t * @param realm the realm. Default is \"Realm\"\n\t */\n\tpublic void setRealm(String realm) {\n\t\tthis.headerValue = createHeaderValue(realm);\n\t}\n\n\tprivate static String createHeaderValue(String realm) {\n\t\tAssert.notNull(realm, \"realm cannot be null\");\n\t\treturn String.format(WWW_AUTHENTICATE_FORMAT, realm);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/HttpStatusServerEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ServerAuthenticationEntryPoint} that sends a generic {@link HttpStatus} as a\n * response. Useful for JavaScript clients which cannot use Basic authentication since the\n * browser intercepts the response.\n *\n * @author Eric Deandrea\n * @since 5.1\n */\npublic class HttpStatusServerEntryPoint implements ServerAuthenticationEntryPoint {\n\n\tprivate final HttpStatus httpStatus;\n\n\tpublic HttpStatusServerEntryPoint(HttpStatus httpStatus) {\n\t\tAssert.notNull(httpStatus, \"httpStatus cannot be null\");\n\t\tthis.httpStatus = httpStatus;\n\t}\n\n\t@Override\n\tpublic Mono<Void> commence(ServerWebExchange exchange, AuthenticationException authException) {\n\t\treturn Mono.fromRunnable(() -> exchange.getResponse().setStatusCode(this.httpStatus));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/InvalidateLeastUsedServerMaximumSessionsExceededHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.session.ReactiveSessionInformation;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.session.WebSessionStore;\n\n/**\n * Implementation of {@link ServerMaximumSessionsExceededHandler} that invalidates the\n * least recently used {@link ReactiveSessionInformation} and removes the related sessions\n * from the {@link WebSessionStore}. It only invalidates the amount of sessions that\n * exceed the maximum allowed. For example, if the maximum was exceeded by 1, only the\n * least recently used session will be invalidated.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic final class InvalidateLeastUsedServerMaximumSessionsExceededHandler\n\t\timplements ServerMaximumSessionsExceededHandler {\n\n\tprivate final WebSessionStore webSessionStore;\n\n\tpublic InvalidateLeastUsedServerMaximumSessionsExceededHandler(WebSessionStore webSessionStore) {\n\t\tAssert.notNull(webSessionStore, \"webSessionStore cannot be null\");\n\t\tthis.webSessionStore = webSessionStore;\n\t}\n\n\t@Override\n\tpublic Mono<Void> handle(MaximumSessionsContext context) {\n\t\tList<ReactiveSessionInformation> sessions = new ArrayList<>(context.getSessions());\n\t\tsessions.removeIf((session) -> session.getSessionId().equals(context.getCurrentSession().getId()));\n\t\tsessions.sort(Comparator.comparing(ReactiveSessionInformation::getLastAccessTime));\n\t\tint maximumSessionsExceededBy = sessions.size() - context.getMaximumSessionsAllowed() + 1;\n\t\tList<ReactiveSessionInformation> leastRecentlyUsedSessionsToInvalidate = sessions.subList(0,\n\t\t\t\tmaximumSessionsExceededBy);\n\n\t\treturn Flux.fromIterable(leastRecentlyUsedSessionsToInvalidate)\n\t\t\t.flatMap((toInvalidate) -> toInvalidate.invalidate().thenReturn(toInvalidate))\n\t\t\t.flatMap((toInvalidate) -> this.webSessionStore.removeSession(toInvalidate.getSessionId()))\n\t\t\t.then();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/MaximumSessionsContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.List;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.ReactiveSessionInformation;\nimport org.springframework.web.server.WebSession;\n\npublic final class MaximumSessionsContext {\n\n\tprivate final Authentication authentication;\n\n\tprivate final List<ReactiveSessionInformation> sessions;\n\n\tprivate final int maximumSessionsAllowed;\n\n\tprivate final WebSession currentSession;\n\n\tpublic MaximumSessionsContext(Authentication authentication, List<ReactiveSessionInformation> sessions,\n\t\t\tint maximumSessionsAllowed, WebSession currentSession) {\n\t\tthis.authentication = authentication;\n\t\tthis.sessions = sessions;\n\t\tthis.maximumSessionsAllowed = maximumSessionsAllowed;\n\t\tthis.currentSession = currentSession;\n\t}\n\n\tpublic Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n\tpublic List<ReactiveSessionInformation> getSessions() {\n\t\treturn this.sessions;\n\t}\n\n\tpublic int getMaximumSessionsAllowed() {\n\t\treturn this.maximumSessionsAllowed;\n\t}\n\n\tpublic WebSession getCurrentSession() {\n\t\treturn this.currentSession;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/PreventLoginServerMaximumSessionsExceededHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\n\n/**\n * Returns a {@link Mono} that terminates with {@link SessionAuthenticationException} when\n * the maximum number of sessions for a user has been reached.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic final class PreventLoginServerMaximumSessionsExceededHandler implements ServerMaximumSessionsExceededHandler {\n\n\t@Override\n\tpublic Mono<Void> handle(MaximumSessionsContext context) {\n\t\treturn context.getCurrentSession()\n\t\t\t.invalidate()\n\t\t\t.then(Mono.defer(() -> Mono.error(new SessionAuthenticationException(\"Maximum sessions exceeded\"))));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ReactivePreAuthenticatedAuthenticationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\n\n/**\n * Reactive version of\n * {@link org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider}\n *\n * This manager receives a {@link PreAuthenticatedAuthenticationToken}, checks that\n * associated account is not disabled, expired, or blocked, and returns new authenticated\n * {@link PreAuthenticatedAuthenticationToken}.\n *\n * If no {@link UserDetailsChecker} is provided, a default\n * {@link AccountStatusUserDetailsChecker} will be created.\n *\n * @author Alexey Nesterov\n * @since 5.2\n */\npublic class ReactivePreAuthenticatedAuthenticationManager implements ReactiveAuthenticationManager {\n\n\tprivate final ReactiveUserDetailsService userDetailsService;\n\n\tprivate final UserDetailsChecker userDetailsChecker;\n\n\tpublic ReactivePreAuthenticatedAuthenticationManager(ReactiveUserDetailsService userDetailsService) {\n\t\tthis(userDetailsService, new AccountStatusUserDetailsChecker());\n\t}\n\n\tpublic ReactivePreAuthenticatedAuthenticationManager(ReactiveUserDetailsService userDetailsService,\n\t\t\tUserDetailsChecker userDetailsChecker) {\n\t\tthis.userDetailsService = userDetailsService;\n\t\tthis.userDetailsChecker = userDetailsChecker;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> authenticate(Authentication authentication) {\n\t\treturn Mono.just(authentication)\n\t\t\t.filter(this::supports)\n\t\t\t.map(Authentication::getName)\n\t\t\t.flatMap(this.userDetailsService::findByUsername)\n\t\t\t.switchIfEmpty(Mono.error(() -> UsernameNotFoundException.fromUsername(authentication.getName())))\n\t\t\t.doOnNext(this.userDetailsChecker::check)\n\t\t\t.map((userDetails) -> {\n\t\t\t\tPreAuthenticatedAuthenticationToken result = new PreAuthenticatedAuthenticationToken(userDetails,\n\t\t\t\t\t\tauthentication.getCredentials(), userDetails.getAuthorities());\n\t\t\t\tresult.setDetails(authentication.getDetails());\n\t\t\t\treturn result;\n\t\t\t});\n\t}\n\n\tprivate boolean supports(Authentication authentication) {\n\t\treturn PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication.getClass());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/RedirectServerAuthenticationEntryPoint.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Performs a redirect to a specified location.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class RedirectServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {\n\n\tprivate final URI location;\n\n\tprivate ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();\n\n\tprivate ServerRequestCache requestCache = new WebSessionServerRequestCache();\n\n\t/**\n\t * Creates an instance\n\t * @param location the location to redirect to (i.e. \"/logout-success\")\n\t */\n\tpublic RedirectServerAuthenticationEntryPoint(String location) {\n\t\tAssert.notNull(location, \"location cannot be null\");\n\t\tthis.location = URI.create(location);\n\t}\n\n\t/**\n\t * The request cache to use to save the request before sending a redirect.\n\t * @param requestCache the cache to redirect to.\n\t */\n\tpublic void setRequestCache(ServerRequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t@Override\n\tpublic Mono<Void> commence(ServerWebExchange exchange, AuthenticationException ex) {\n\t\treturn this.requestCache.saveRequest(exchange)\n\t\t\t.then(this.redirectStrategy.sendRedirect(exchange, this.location));\n\t}\n\n\t/**\n\t * Sets the RedirectStrategy to use.\n\t * @param redirectStrategy the strategy to use. Default is DefaultRedirectStrategy.\n\t */\n\tpublic void setRedirectStrategy(ServerRedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/RedirectServerAuthenticationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.util.Assert;\n\n/**\n * Performs a redirect to a specified location.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class RedirectServerAuthenticationFailureHandler implements ServerAuthenticationFailureHandler {\n\n\tprivate final URI location;\n\n\tprivate ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();\n\n\t/**\n\t * Creates an instance\n\t * @param location the location to redirect to (i.e. \"/login?failed\")\n\t */\n\tpublic RedirectServerAuthenticationFailureHandler(String location) {\n\t\tAssert.notNull(location, \"location cannot be null\");\n\t\tthis.location = URI.create(location);\n\t}\n\n\t/**\n\t * Sets the RedirectStrategy to use.\n\t * @param redirectStrategy the strategy to use. Default is DefaultRedirectStrategy.\n\t */\n\tpublic void setRedirectStrategy(ServerRedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n\t@Override\n\tpublic Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {\n\t\treturn this.redirectStrategy.sendRedirect(webFilterExchange.getExchange(), this.location);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/RedirectServerAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.savedrequest.ServerRequestCache;\nimport org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Performs a redirect on authentication success. The default is to redirect to a saved\n * request if present and otherwise \"/\".\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class RedirectServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {\n\n\tprivate URI location = URI.create(\"/\");\n\n\tprivate ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();\n\n\tprivate ServerRequestCache requestCache = new WebSessionServerRequestCache();\n\n\t/**\n\t * Creates a new instance with location of \"/\"\n\t */\n\tpublic RedirectServerAuthenticationSuccessHandler() {\n\t}\n\n\t/**\n\t * Creates a new instance with the specified location\n\t * @param location the location to redirect if the no request is cached in\n\t * {@link #setRequestCache(ServerRequestCache)}\n\t */\n\tpublic RedirectServerAuthenticationSuccessHandler(String location) {\n\t\tthis.location = URI.create(location);\n\t}\n\n\t/**\n\t * Sets the {@link ServerRequestCache} used to redirect to. Default is\n\t * {@link WebSessionServerRequestCache}.\n\t * @param requestCache the cache to use\n\t */\n\tpublic void setRequestCache(ServerRequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n\t@Override\n\tpublic Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {\n\t\tServerWebExchange exchange = webFilterExchange.getExchange();\n\t\treturn this.requestCache.getRedirectUri(exchange)\n\t\t\t.defaultIfEmpty(this.location)\n\t\t\t.flatMap((location) -> this.redirectStrategy.sendRedirect(exchange, location));\n\t}\n\n\t/**\n\t * Where the user is redirected to upon authentication success\n\t * @param location the location to redirect to. The default is \"/\"\n\t */\n\tpublic void setLocation(URI location) {\n\t\tAssert.notNull(location, \"location cannot be null\");\n\t\tthis.location = location;\n\t}\n\n\t/**\n\t * The RedirectStrategy to use.\n\t * @param redirectStrategy the strategy to use. Default is DefaultRedirectStrategy.\n\t */\n\tpublic void setRedirectStrategy(ServerRedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/RegisterSessionServerAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.Objects;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.ReactiveSessionInformation;\nimport org.springframework.security.core.session.ReactiveSessionRegistry;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.util.Assert;\n\n/**\n * An implementation of {@link ServerAuthenticationSuccessHandler} that will register a\n * {@link ReactiveSessionInformation} with the provided {@link ReactiveSessionRegistry}.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic final class RegisterSessionServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {\n\n\tprivate final ReactiveSessionRegistry sessionRegistry;\n\n\tpublic RegisterSessionServerAuthenticationSuccessHandler(ReactiveSessionRegistry sessionRegistry) {\n\t\tAssert.notNull(sessionRegistry, \"sessionRegistry cannot be null\");\n\t\tthis.sessionRegistry = sessionRegistry;\n\t}\n\n\t@Override\n\tpublic Mono<Void> onAuthenticationSuccess(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn exchange.getExchange()\n\t\t\t.getSession()\n\t\t\t.map((session) -> new ReactiveSessionInformation(Objects.requireNonNull(authentication.getPrincipal()),\n\t\t\t\t\tsession.getId(), session.getLastAccessTime()))\n\t\t\t.flatMap(this.sessionRegistry::saveSessionInformation);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A strategy used for converting from a {@link ServerWebExchange} to an\n * {@link Authentication} used for authenticating with a provided\n * {@link org.springframework.security.authentication.ReactiveAuthenticationManager}. If\n * the result is {@link Mono#empty()}, then it signals that no authentication attempt\n * should be made.\n *\n * @author Eric Deandrea\n * @since 5.1\n */\n@FunctionalInterface\npublic interface ServerAuthenticationConverter {\n\n\t/**\n\t * Converts a {@link ServerWebExchange} to an {@link Authentication}\n\t * @param exchange The {@link ServerWebExchange}\n\t * @return A {@link Mono} representing an {@link Authentication}\n\t */\n\tMono<Authentication> convert(ServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerAuthenticationEntryPointFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.util.Assert;\n\n/**\n * Adapts a {@link ServerAuthenticationEntryPoint} into a\n * {@link ServerAuthenticationFailureHandler}\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class ServerAuthenticationEntryPointFailureHandler implements ServerAuthenticationFailureHandler {\n\n\tprivate final ServerAuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate boolean rethrowAuthenticationServiceException = true;\n\n\tpublic ServerAuthenticationEntryPointFailureHandler(ServerAuthenticationEntryPoint authenticationEntryPoint) {\n\t\tAssert.notNull(authenticationEntryPoint, \"authenticationEntryPoint cannot be null\");\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t}\n\n\t@Override\n\tpublic Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {\n\t\tif (!this.rethrowAuthenticationServiceException) {\n\t\t\treturn this.authenticationEntryPoint.commence(webFilterExchange.getExchange(), exception);\n\t\t}\n\t\tif (!AuthenticationServiceException.class.isAssignableFrom(exception.getClass())) {\n\t\t\treturn this.authenticationEntryPoint.commence(webFilterExchange.getExchange(), exception);\n\t\t}\n\t\treturn Mono.error(exception);\n\t}\n\n\t/**\n\t * Set whether to rethrow {@link AuthenticationServiceException}s (defaults to true)\n\t * @param rethrowAuthenticationServiceException whether to rethrow\n\t * {@link AuthenticationServiceException}s\n\t * @since 5.8\n\t */\n\tpublic void setRethrowAuthenticationServiceException(boolean rethrowAuthenticationServiceException) {\n\t\tthis.rethrowAuthenticationServiceException = rethrowAuthenticationServiceException;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerAuthenticationFailureHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.WebFilterExchange;\n\n/**\n * Handles authentication failure\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic interface ServerAuthenticationFailureHandler {\n\n\t/**\n\t * Invoked when authentication attempt fails\n\t * @param webFilterExchange the exchange\n\t * @param exception the reason authentication failed\n\t * @return a completion notification (success or error)\n\t */\n\tMono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\n\n/**\n * Handles authentication success\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic interface ServerAuthenticationSuccessHandler {\n\n\t/**\n\t * Invoked when the application authenticates successfully\n\t * @param webFilterExchange the exchange\n\t * @param authentication the {@link Authentication}\n\t * @return a completion notification (success or error)\n\t */\n\tMono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerFormLoginAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Converts a ServerWebExchange into a UsernamePasswordAuthenticationToken from the form\n * data HTTP parameters.\n *\n * @author Rob Winch\n * @since 5.1\n */\n@SuppressWarnings(\"deprecation\")\npublic class ServerFormLoginAuthenticationConverter\n\t\textends org.springframework.security.web.server.ServerFormLoginAuthenticationConverter\n\t\timplements ServerAuthenticationConverter {\n\n\t@Override\n\tpublic Mono<Authentication> convert(ServerWebExchange exchange) {\n\t\treturn apply(exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerHttpBasicAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Converts from a {@link ServerWebExchange} to an {@link Authentication} that can be\n * authenticated.\n *\n * @author Rob Winch\n * @since 5.1\n */\n@SuppressWarnings(\"deprecation\")\npublic class ServerHttpBasicAuthenticationConverter\n\t\textends org.springframework.security.web.server.ServerHttpBasicAuthenticationConverter\n\t\timplements ServerAuthenticationConverter {\n\n\t@Override\n\tpublic Mono<Authentication> convert(ServerWebExchange exchange) {\n\t\treturn apply(exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerMaximumSessionsExceededHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\n/**\n * Strategy for handling the scenario when the maximum number of sessions for a user has\n * been reached.\n *\n * @author Marcus da Coregio\n * @since 6.3\n */\npublic interface ServerMaximumSessionsExceededHandler {\n\n\t/**\n\t * Handles the scenario when the maximum number of sessions for a user has been\n\t * reached.\n\t * @param context the context with information about the sessions and the user\n\t * @return an empty {@link Mono} that completes when the handling is done\n\t */\n\tMono<Void> handle(MaximumSessionsContext context);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcherEntry;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ReactiveAuthenticationManagerResolver} that returns a\n * {@link ReactiveAuthenticationManager} instances based upon the type of\n * {@link ServerWebExchange} passed into {@link #resolve(ServerWebExchange)}.\n *\n * @author Josh Cummings\n * @since 5.7\n *\n */\npublic final class ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver\n\t\timplements ReactiveAuthenticationManagerResolver<ServerWebExchange> {\n\n\tprivate final List<ServerWebExchangeMatcherEntry<ReactiveAuthenticationManager>> authenticationManagers;\n\n\tprivate ReactiveAuthenticationManager defaultAuthenticationManager = (authentication) -> {\n\t\tAuthenticationException ex = new AuthenticationServiceException(\"Cannot authenticate \" + authentication);\n\t\tex.setAuthenticationRequest(authentication);\n\t\treturn Mono.error(ex);\n\t};\n\n\t/**\n\t * Construct an\n\t * {@link ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver} based on\n\t * the provided parameters\n\t * @param managers a set of {@link ServerWebExchangeMatcherEntry}s\n\t */\n\tServerWebExchangeDelegatingReactiveAuthenticationManagerResolver(\n\t\t\tServerWebExchangeMatcherEntry<ReactiveAuthenticationManager>... managers) {\n\t\tthis(Arrays.asList(managers));\n\t}\n\n\t/**\n\t * Construct an\n\t * {@link ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver} based on\n\t * the provided parameters\n\t * @param managers a {@link List} of {@link ServerWebExchangeMatcherEntry}s\n\t */\n\tServerWebExchangeDelegatingReactiveAuthenticationManagerResolver(\n\t\t\tList<ServerWebExchangeMatcherEntry<ReactiveAuthenticationManager>> managers) {\n\t\tAssert.notNull(managers, \"entries cannot be null\");\n\t\tthis.authenticationManagers = managers;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic Mono<ReactiveAuthenticationManager> resolve(ServerWebExchange exchange) {\n\t\treturn Flux.fromIterable(this.authenticationManagers)\n\t\t\t.filterWhen((entry) -> isMatch(exchange, entry))\n\t\t\t.next()\n\t\t\t.map(ServerWebExchangeMatcherEntry::getEntry)\n\t\t\t.defaultIfEmpty(this.defaultAuthenticationManager);\n\t}\n\n\t/**\n\t * Set the default {@link ReactiveAuthenticationManager} to use when a request does\n\t * not match\n\t * @param defaultAuthenticationManager the default\n\t * {@link ReactiveAuthenticationManager} to use\n\t */\n\tpublic void setDefaultAuthenticationManager(ReactiveAuthenticationManager defaultAuthenticationManager) {\n\t\tAssert.notNull(defaultAuthenticationManager, \"defaultAuthenticationManager cannot be null\");\n\t\tthis.defaultAuthenticationManager = defaultAuthenticationManager;\n\t}\n\n\t/**\n\t * Creates a builder for {@link RequestMatcherDelegatingAuthorizationManager}.\n\t * @return the new {@link RequestMatcherDelegatingAuthorizationManager.Builder}\n\t * instance\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\tprivate Mono<Boolean> isMatch(ServerWebExchange exchange,\n\t\t\tServerWebExchangeMatcherEntry<ReactiveAuthenticationManager> entry) {\n\t\tServerWebExchangeMatcher matcher = entry.getMatcher();\n\t\treturn matcher.matches(exchange).map(ServerWebExchangeMatcher.MatchResult::isMatch);\n\t}\n\n\t/**\n\t * A builder for\n\t * {@link ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver}.\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final List<ServerWebExchangeMatcherEntry<ReactiveAuthenticationManager>> entries = new ArrayList<>();\n\n\t\tprivate Builder() {\n\n\t\t}\n\n\t\t/**\n\t\t * Maps a {@link ServerWebExchangeMatcher} to an\n\t\t * {@link ReactiveAuthenticationManager}.\n\t\t * @param matcher the {@link ServerWebExchangeMatcher} to use\n\t\t * @param manager the {@link ReactiveAuthenticationManager} to use\n\t\t * @return the\n\t\t * {@link ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver.Builder}\n\t\t * for further customizations\n\t\t */\n\t\tpublic ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver.Builder add(\n\t\t\t\tServerWebExchangeMatcher matcher, ReactiveAuthenticationManager manager) {\n\t\t\tAssert.notNull(matcher, \"matcher cannot be null\");\n\t\t\tAssert.notNull(manager, \"manager cannot be null\");\n\t\t\tthis.entries.add(new ServerWebExchangeMatcherEntry<>(matcher, manager));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Creates a\n\t\t * {@link ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver}\n\t\t * instance.\n\t\t * @return the\n\t\t * {@link ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver}\n\t\t * instance\n\t\t */\n\t\tpublic ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver build() {\n\t\t\treturn new ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver(this.entries);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ServerX509AuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.security.cert.X509Certificate;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NonNull;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.server.reactive.SslInfo;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\nimport org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Converts from a {@link SslInfo} provided by a request to an\n * {@link PreAuthenticatedAuthenticationToken} that can be authenticated.\n *\n * @author Alexey Nesterov\n * @since 5.2\n */\npublic class ServerX509AuthenticationConverter implements ServerAuthenticationConverter {\n\n\tprotected final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final X509PrincipalExtractor principalExtractor;\n\n\tpublic ServerX509AuthenticationConverter(@NonNull X509PrincipalExtractor principalExtractor) {\n\t\tthis.principalExtractor = principalExtractor;\n\t}\n\n\t@Override\n\tpublic Mono<Authentication> convert(ServerWebExchange exchange) {\n\t\tSslInfo sslInfo = exchange.getRequest().getSslInfo();\n\t\tif (sslInfo == null) {\n\t\t\tthis.logger.debug(\"No SslInfo provided with a request, skipping x509 authentication\");\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tif (sslInfo.getPeerCertificates() == null || sslInfo.getPeerCertificates().length == 0) {\n\t\t\tthis.logger.debug(\"No peer certificates found in SslInfo, skipping x509 authentication\");\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tX509Certificate clientCertificate = sslInfo.getPeerCertificates()[0];\n\t\tObject principal = this.principalExtractor.extractPrincipal(clientCertificate);\n\t\treturn Mono.just(new PreAuthenticatedAuthenticationToken(principal, clientCertificate));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/SessionLimit.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.function.Function;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\n\n/**\n * Represents the maximum number of sessions allowed. Use {@link #UNLIMITED} to indicate\n * that there is no limit.\n *\n * @author Marcus da Coregio\n * @since 6.3\n * @see ConcurrentSessionControlServerAuthenticationSuccessHandler\n */\npublic interface SessionLimit extends Function<Authentication, Mono<Integer>> {\n\n\t/**\n\t * Represents unlimited sessions. This is just a shortcut to return\n\t * {@link Mono#empty()} for any user.\n\t */\n\tSessionLimit UNLIMITED = (authentication) -> Mono.empty();\n\n\t/**\n\t * Creates a {@link SessionLimit} that always returns the given value for any user\n\t * @param maxSessions the maximum number of sessions allowed\n\t * @return a {@link SessionLimit} instance that returns the given value.\n\t */\n\tstatic SessionLimit of(int maxSessions) {\n\t\treturn (authentication) -> Mono.just(maxSessions);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/SwitchUserWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsChecker;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Switch User processing filter responsible for user context switching. A common use-case\n * for this feature is the ability to allow higher-authority users (e.g. ROLE_ADMIN) to\n * switch to a regular user (e.g. ROLE_USER).\n * <p>\n * This filter assumes that the user performing the switch will be required to be logged\n * in as normal user (i.e. with a ROLE_ADMIN role). The user will then access a\n * page/controller that enables the administrator to specify who they wish to become (see\n * <code>switchUserUrl</code>).\n * <p>\n * <b>Note: This URL will be required to have appropriate security constraints configured\n * so that only users of that role can access it (e.g. ROLE_ADMIN).</b>\n * <p>\n * On a successful switch, the user's <code>SecurityContext</code> will be updated to\n * reflect the specified user and will also contain an additional\n * {@link org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority}\n * which contains the original user. Before switching, a check will be made on whether the\n * user is already currently switched, and any current switch will be exited to prevent\n * \"nested\" switches.\n * <p>\n * To 'exit' from a user context, the user needs to access a URL (see\n * <code>exitUserUrl</code>) that will switch back to the original user as identified by\n * the <code>ROLE_PREVIOUS_ADMINISTRATOR</code>.\n * <p>\n * To configure the Switch User Processing Filter, create a bean definition for the Switch\n * User processing filter and add to the filterChainProxy. Note that the filter must come\n * <b>after</b> the\n * <code>org.springframework.security.config.web.server.SecurityWebFiltersOrder#AUTHORIZATION</code>\n * in the chain, in order to apply the correct constraints to the <tt>switchUserUrl</tt>.\n * Example: <pre>\n * SwitchUserWebFilter filter = new SwitchUserWebFilter(userDetailsService, loginSuccessHandler, failureHandler);\n * http.addFilterAfter(filter, SecurityWebFiltersOrder.AUTHORIZATION);\n * </pre>\n *\n * @author Artur Otrzonsek\n * @since 5.4\n * @see SwitchUserGrantedAuthority\n */\npublic class SwitchUserWebFilter implements WebFilter {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tpublic static final String SPRING_SECURITY_SWITCH_USERNAME_KEY = \"username\";\n\n\tpublic static final String ROLE_PREVIOUS_ADMINISTRATOR = \"ROLE_PREVIOUS_ADMINISTRATOR\";\n\n\tprivate final ServerAuthenticationSuccessHandler successHandler;\n\n\tprivate final @Nullable ServerAuthenticationFailureHandler failureHandler;\n\n\tprivate final ReactiveUserDetailsService userDetailsService;\n\n\tprivate final UserDetailsChecker userDetailsChecker;\n\n\tprivate ServerSecurityContextRepository securityContextRepository;\n\n\tprivate ServerWebExchangeMatcher switchUserMatcher = createMatcher(\"/login/impersonate\");\n\n\tprivate ServerWebExchangeMatcher exitUserMatcher = createMatcher(\"/logout/impersonate\");\n\n\t/**\n\t * Creates a filter for the user context switching\n\t * @param userDetailsService The <tt>UserDetailsService</tt> which will be used to\n\t * load information for the user that is being switched to.\n\t * @param successHandler Used to define custom behaviour on a successful switch or\n\t * exit user.\n\t * @param failureHandler Used to define custom behaviour when a switch fails.\n\t */\n\tpublic SwitchUserWebFilter(ReactiveUserDetailsService userDetailsService,\n\t\t\tServerAuthenticationSuccessHandler successHandler,\n\t\t\t@Nullable ServerAuthenticationFailureHandler failureHandler) {\n\t\tAssert.notNull(userDetailsService, \"userDetailsService must be specified\");\n\t\tAssert.notNull(successHandler, \"successHandler must be specified\");\n\t\tthis.userDetailsService = userDetailsService;\n\t\tthis.successHandler = successHandler;\n\t\tthis.failureHandler = failureHandler;\n\t\tthis.securityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tthis.userDetailsChecker = new AccountStatusUserDetailsChecker();\n\t}\n\n\t/**\n\t * Creates a filter for the user context switching\n\t * @param userDetailsService The <tt>UserDetailsService</tt> which will be used to\n\t * load information for the user that is being switched to.\n\t * @param successTargetUrl Sets the URL to go to after a successful switch / exit user\n\t * request\n\t * @param failureTargetUrl The URL to which a user should be redirected if the switch\n\t * fails\n\t */\n\tpublic SwitchUserWebFilter(ReactiveUserDetailsService userDetailsService, String successTargetUrl,\n\t\t\t@Nullable String failureTargetUrl) {\n\t\tAssert.notNull(userDetailsService, \"userDetailsService must be specified\");\n\t\tAssert.notNull(successTargetUrl, \"successTargetUrl must be specified\");\n\t\tthis.userDetailsService = userDetailsService;\n\t\tthis.successHandler = new RedirectServerAuthenticationSuccessHandler(successTargetUrl);\n\t\tthis.failureHandler = (failureTargetUrl != null)\n\t\t\t\t? new RedirectServerAuthenticationFailureHandler(failureTargetUrl) : null;\n\t\tthis.securityContextRepository = new WebSessionServerSecurityContextRepository();\n\t\tthis.userDetailsChecker = new AccountStatusUserDetailsChecker();\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\tfinal WebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain);\n\t\treturn switchUser(webFilterExchange).switchIfEmpty(Mono.defer(() -> exitSwitchUser(webFilterExchange)))\n\t\t\t.switchIfEmpty(Mono.defer(() -> {\n\t\t\t\tthis.logger\n\t\t\t\t\t.trace(LogMessage.format(\"Did not attempt to switch user since request did not match [%s] or [%s]\",\n\t\t\t\t\t\t\tthis.switchUserMatcher, this.exitUserMatcher));\n\t\t\t\treturn chain.filter(exchange).then(Mono.empty());\n\t\t\t}))\n\t\t\t.flatMap((authentication) -> onAuthenticationSuccess(authentication, webFilterExchange))\n\t\t\t.onErrorResume(SwitchUserAuthenticationException.class, (exception) -> Mono.empty());\n\t}\n\n\t/**\n\t * Attempt to switch to another user.\n\t * @param webFilterExchange The web filter exchange\n\t * @return The new <code>Authentication</code> object if successfully switched to\n\t * another user, <code>Mono.empty()</code> otherwise.\n\t * @throws AuthenticationCredentialsNotFoundException If the target user can not be\n\t * found by username\n\t */\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tprotected Mono<Authentication> switchUser(WebFilterExchange webFilterExchange) {\n\t\treturn this.switchUserMatcher.matches(webFilterExchange.getExchange())\n\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t.flatMap((matchResult) -> ReactiveSecurityContextHolder.getContext())\n\t\t\t.mapNotNull(SecurityContext::getAuthentication)\n\t\t\t.flatMap((currentAuthentication) -> {\n\t\t\t\tString username = getUsername(webFilterExchange.getExchange());\n\t\t\t\treturn attemptSwitchUser(currentAuthentication, username);\n\t\t\t})\n\t\t\t.onErrorResume(AuthenticationException.class, (ex) -> onAuthenticationFailure(ex, webFilterExchange)\n\t\t\t\t.then(Mono.error(new SwitchUserAuthenticationException(ex))));\n\t}\n\n\t/**\n\t * Attempt to exit from an already switched user.\n\t * @param webFilterExchange The web filter exchange\n\t * @return The original <code>Authentication</code> object.\n\t * @throws AuthenticationCredentialsNotFoundException If there is no\n\t * <code>Authentication</code> associated with this request or the user is not\n\t * switched.\n\t */\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tprotected Mono<Authentication> exitSwitchUser(WebFilterExchange webFilterExchange) {\n\t\treturn this.exitUserMatcher.matches(webFilterExchange.getExchange())\n\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t.flatMap((matchResult) -> ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.mapNotNull(SecurityContext::getAuthentication)\n\t\t\t\t.switchIfEmpty(Mono.error(this::noCurrentUserException)))\n\t\t\t.map(this::attemptExitUser);\n\t}\n\n\t/**\n\t * Returns the name of the target user.\n\t * @param exchange The server web exchange\n\t * @return the name of the target user.\n\t */\n\tprotected @Nullable String getUsername(ServerWebExchange exchange) {\n\t\treturn exchange.getRequest().getQueryParams().getFirst(SPRING_SECURITY_SWITCH_USERNAME_KEY);\n\t}\n\n\tprivate @NonNull Mono<Authentication> attemptSwitchUser(Authentication currentAuthentication,\n\t\t\t@Nullable String userName) {\n\t\tAssert.notNull(userName, \"The userName can not be null.\");\n\t\tthis.logger.debug(LogMessage.format(\"Attempting to switch to user [%s]\", userName));\n\t\treturn this.userDetailsService.findByUsername(userName)\n\t\t\t.switchIfEmpty(Mono.error(this::noTargetAuthenticationException))\n\t\t\t.doOnNext(this.userDetailsChecker::check)\n\t\t\t.map((userDetails) -> createSwitchUserToken(userDetails, currentAuthentication));\n\t}\n\n\tprivate @NonNull Authentication attemptExitUser(Authentication currentAuthentication) {\n\t\tOptional<Authentication> sourceAuthentication = extractSourceAuthentication(currentAuthentication);\n\t\tif (sourceAuthentication.isEmpty()) {\n\t\t\tthis.logger.debug(\"Failed to find original user\");\n\t\t\tthrow noOriginalAuthenticationException();\n\t\t}\n\t\treturn sourceAuthentication.get();\n\t}\n\n\tprivate Mono<Void> onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) {\n\t\tServerWebExchange exchange = webFilterExchange.getExchange();\n\t\tSecurityContextImpl securityContext = new SecurityContextImpl(authentication);\n\t\treturn this.securityContextRepository.save(exchange, securityContext)\n\t\t\t.doOnSuccess((v) -> this.logger.debug(LogMessage.format(\"Switched user to %s\", authentication)))\n\t\t\t.then(this.successHandler.onAuthenticationSuccess(webFilterExchange, authentication))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)));\n\t}\n\n\tprivate Mono<Void> onAuthenticationFailure(AuthenticationException exception, WebFilterExchange webFilterExchange) {\n\t\treturn Mono.justOrEmpty(this.failureHandler).switchIfEmpty(Mono.defer(() -> {\n\t\t\tthis.logger.debug(\"Failed to switch user\", exception);\n\t\t\treturn Mono.error(exception);\n\t\t})).flatMap((failureHandler) -> failureHandler.onAuthenticationFailure(webFilterExchange, exception));\n\t}\n\n\tprivate Authentication createSwitchUserToken(UserDetails targetUser, Authentication currentAuthentication) {\n\t\tOptional<Authentication> sourceAuthentication = extractSourceAuthentication(currentAuthentication);\n\t\tif (sourceAuthentication.isPresent()) {\n\t\t\t// SEC-1763. Check first if we are already switched.\n\t\t\tthis.logger.debug(\n\t\t\t\t\tLogMessage.format(\"Found original switch user granted authority [%s]\", sourceAuthentication.get()));\n\t\t\tcurrentAuthentication = sourceAuthentication.get();\n\t\t}\n\t\tGrantedAuthority switchAuthority = new SwitchUserGrantedAuthority(ROLE_PREVIOUS_ADMINISTRATOR,\n\t\t\t\tcurrentAuthentication);\n\t\tCollection<? extends GrantedAuthority> targetUserAuthorities = targetUser.getAuthorities();\n\t\tList<GrantedAuthority> extendedTargetUserAuthorities = new ArrayList<>(targetUserAuthorities);\n\t\textendedTargetUserAuthorities.add(switchAuthority);\n\t\treturn UsernamePasswordAuthenticationToken.authenticated(targetUser, targetUser.getPassword(),\n\t\t\t\textendedTargetUserAuthorities);\n\t}\n\n\t/**\n\t * Find the original <code>Authentication</code> object from the current user's\n\t * granted authorities. A successfully switched user should have a\n\t * <code>SwitchUserGrantedAuthority</code> that contains the original source user\n\t * <code>Authentication</code> object.\n\t * @param currentAuthentication The current <code>Authentication</code> object\n\t * @return The source user <code>Authentication</code> object or\n\t * <code>Optional.empty</code> otherwise.\n\t */\n\tprivate Optional<Authentication> extractSourceAuthentication(Authentication currentAuthentication) {\n\t\t// iterate over granted authorities and find the 'switch user' authority\n\t\tfor (GrantedAuthority authority : currentAuthentication.getAuthorities()) {\n\t\t\tif (authority instanceof SwitchUserGrantedAuthority switchAuthority) {\n\t\t\t\treturn Optional.of(switchAuthority.getSource());\n\t\t\t}\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate static ServerWebExchangeMatcher createMatcher(String pattern) {\n\t\treturn ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, pattern);\n\t}\n\n\tprivate AuthenticationCredentialsNotFoundException noCurrentUserException() {\n\t\treturn new AuthenticationCredentialsNotFoundException(\"No current user associated with this request\");\n\t}\n\n\tprivate AuthenticationCredentialsNotFoundException noOriginalAuthenticationException() {\n\t\treturn new AuthenticationCredentialsNotFoundException(\"Could not find original Authentication object\");\n\t}\n\n\tprivate AuthenticationCredentialsNotFoundException noTargetAuthenticationException() {\n\t\treturn new AuthenticationCredentialsNotFoundException(\"No target user for the given username\");\n\t}\n\n\t/**\n\t * Sets the repository for persisting the SecurityContext. Default is\n\t * {@link WebSessionServerSecurityContextRepository}\n\t * @param securityContextRepository the repository to use\n\t */\n\tpublic void setSecurityContextRepository(ServerSecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Set the URL to respond to exit user processing. This is a shortcut for *\n\t * {@link #setExitUserMatcher(ServerWebExchangeMatcher)}\n\t * @param exitUserUrl The exit user URL.\n\t */\n\tpublic void setExitUserUrl(String exitUserUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(exitUserUrl),\n\t\t\t\t\"exitUserUrl cannot be empty and must be a valid redirect URL\");\n\t\tthis.exitUserMatcher = createMatcher(exitUserUrl);\n\t}\n\n\t/**\n\t * Set the matcher to respond to exit user processing.\n\t * @param exitUserMatcher The exit matcher to use\n\t */\n\tpublic void setExitUserMatcher(ServerWebExchangeMatcher exitUserMatcher) {\n\t\tAssert.notNull(exitUserMatcher, \"exitUserMatcher cannot be null\");\n\t\tthis.exitUserMatcher = exitUserMatcher;\n\t}\n\n\t/**\n\t * Set the URL to respond to switch user processing. This is a shortcut for\n\t * {@link #setSwitchUserMatcher(ServerWebExchangeMatcher)}\n\t * @param switchUserUrl The switch user URL.\n\t */\n\tpublic void setSwitchUserUrl(String switchUserUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(switchUserUrl),\n\t\t\t\t\"switchUserUrl cannot be empty and must be a valid redirect URL\");\n\t\tthis.switchUserMatcher = createMatcher(switchUserUrl);\n\t}\n\n\t/**\n\t * Set the matcher to respond to switch user processing.\n\t * @param switchUserMatcher The switch user matcher.\n\t */\n\tpublic void setSwitchUserMatcher(ServerWebExchangeMatcher switchUserMatcher) {\n\t\tAssert.notNull(switchUserMatcher, \"switchUserMatcher cannot be null\");\n\t\tthis.switchUserMatcher = switchUserMatcher;\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tprivate static class SwitchUserAuthenticationException extends RuntimeException {\n\n\t\tSwitchUserAuthenticationException(AuthenticationException exception) {\n\t\t\tsuper(exception);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/WebFilterChainServerAuthenticationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Success handler that continues the filter chain after authentication success.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebFilterChainServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {\n\n\t@Override\n\tpublic Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {\n\t\tServerWebExchange exchange = webFilterExchange.getExchange();\n\t\treturn webFilterExchange.getChain().filter(exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.util.Assert;\n\n/**\n * Delegates to a collection of {@link ServerLogoutHandler} implementations.\n *\n * @author Eric Deandrea\n * @since 5.1\n */\npublic class DelegatingServerLogoutHandler implements ServerLogoutHandler {\n\n\tprivate final List<ServerLogoutHandler> delegates = new ArrayList<>();\n\n\tpublic DelegatingServerLogoutHandler(ServerLogoutHandler... delegates) {\n\t\tAssert.notEmpty(delegates, \"delegates cannot be null or empty\");\n\t\tthis.delegates.addAll(Arrays.asList(delegates));\n\t}\n\n\tpublic DelegatingServerLogoutHandler(Collection<ServerLogoutHandler> delegates) {\n\t\tAssert.notEmpty(delegates, \"delegates cannot be null or empty\");\n\t\tthis.delegates.addAll(delegates);\n\t}\n\n\t@Override\n\tpublic Mono<Void> logout(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn Flux.fromIterable(this.delegates)\n\t\t\t.concatMap((delegate) -> delegate.logout(exchange, authentication))\n\t\t\t.then();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/HeaderWriterServerLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.header.ServerHttpHeadersWriter;\nimport org.springframework.util.Assert;\n\n/**\n * <p>\n * A {@link ServerLogoutHandler} implementation which writes HTTP headers during logout.\n * </p>\n *\n * @author MD Sayem Ahmed\n * @since 5.2\n */\npublic final class HeaderWriterServerLogoutHandler implements ServerLogoutHandler {\n\n\tprivate final ServerHttpHeadersWriter headersWriter;\n\n\t/**\n\t * <p>\n\t * Constructs a new instance using the {@link ServerHttpHeadersWriter} implementation.\n\t * </p>\n\t * @param headersWriter a {@link ServerHttpHeadersWriter} implementation\n\t * @throws IllegalArgumentException if the argument is null\n\t */\n\tpublic HeaderWriterServerLogoutHandler(ServerHttpHeadersWriter headersWriter) {\n\t\tAssert.notNull(headersWriter, \"headersWriter cannot be null\");\n\t\tthis.headersWriter = headersWriter;\n\t}\n\n\t@Override\n\tpublic Mono<Void> logout(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn this.headersWriter.writeHttpHeaders(exchange.getExchange());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.util.Assert;\n\n/**\n * Implementation of the {@link ServerLogoutSuccessHandler}. By default returns an HTTP\n * status code of {@code 200}. This is useful in REST-type scenarios where a redirect upon\n * a successful logout is not desired.\n *\n * @author Eric Deandrea\n * @since 5.1\n */\npublic class HttpStatusReturningServerLogoutSuccessHandler implements ServerLogoutSuccessHandler {\n\n\tprivate final HttpStatus httpStatusToReturn;\n\n\t/**\n\t * Initialize the {@code HttpStatusReturningServerLogoutSuccessHandler} with a\n\t * user-defined {@link HttpStatus}.\n\t * @param httpStatusToReturn Must not be {@code null}.\n\t */\n\tpublic HttpStatusReturningServerLogoutSuccessHandler(HttpStatus httpStatusToReturn) {\n\t\tAssert.notNull(httpStatusToReturn, \"The provided HttpStatus must not be null.\");\n\t\tthis.httpStatusToReturn = httpStatusToReturn;\n\t}\n\n\t/**\n\t * Initialize the {@code HttpStatusReturningServerLogoutSuccessHandler} with the\n\t * default {@link HttpStatus#OK}.\n\t */\n\tpublic HttpStatusReturningServerLogoutSuccessHandler() {\n\t\tthis.httpStatusToReturn = HttpStatus.OK;\n\t}\n\n\t/**\n\t * Implementation of\n\t * {@link ServerLogoutSuccessHandler#onLogoutSuccess(WebFilterExchange, Authentication)}.\n\t * Sets the status on the {@link WebFilterExchange}.\n\t * @param exchange The exchange\n\t * @param authentication The {@link Authentication}\n\t * @return A completion notification (success or error)\n\t */\n\t@Override\n\tpublic Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn Mono.fromRunnable(() -> exchange.getExchange().getResponse().setStatusCode(this.httpStatusToReturn));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/LogoutWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * If the request matches, logs an authenticated user out by delegating to a\n * {@link ServerLogoutHandler}.\n *\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n */\npublic class LogoutWebFilter implements WebFilter {\n\n\tprivate static final Log logger = LogFactory.getLog(LogoutWebFilter.class);\n\n\tprivate AnonymousAuthenticationToken anonymousAuthenticationToken = new AnonymousAuthenticationToken(\"key\",\n\t\t\t\"anonymous\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\n\tprivate ServerLogoutHandler logoutHandler = new SecurityContextServerLogoutHandler();\n\n\tprivate ServerLogoutSuccessHandler logoutSuccessHandler = new RedirectServerLogoutSuccessHandler();\n\n\tprivate ServerWebExchangeMatcher requiresLogout = ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,\n\t\t\t\"/logout\");\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.requiresLogout.matches(exchange)\n\t\t\t.filter((result) -> result.isMatch())\n\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t.map((result) -> exchange)\n\t\t\t.flatMap(this::flatMapAuthentication)\n\t\t\t.flatMap((authentication) -> {\n\t\t\t\tWebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain);\n\t\t\t\treturn logout(webFilterExchange, authentication);\n\t\t\t});\n\t}\n\n\tprivate Mono<Authentication> flatMapAuthentication(ServerWebExchange exchange) {\n\t\treturn exchange.getPrincipal().cast(Authentication.class).defaultIfEmpty(this.anonymousAuthenticationToken);\n\t}\n\n\tprivate Mono<Void> logout(WebFilterExchange webFilterExchange, Authentication authentication) {\n\t\tlogger.debug(LogMessage.format(\"Logging out user '%s' and transferring to logout destination\", authentication));\n\t\treturn this.logoutHandler.logout(webFilterExchange, authentication)\n\t\t\t.then(this.logoutSuccessHandler.onLogoutSuccess(webFilterExchange, authentication))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.clearContext());\n\t}\n\n\t/**\n\t * Sets the {@link ServerLogoutSuccessHandler}. The default is\n\t * {@link RedirectServerLogoutSuccessHandler}.\n\t * @param logoutSuccessHandler the handler to use\n\t */\n\tpublic void setLogoutSuccessHandler(ServerLogoutSuccessHandler logoutSuccessHandler) {\n\t\tAssert.notNull(logoutSuccessHandler, \"logoutSuccessHandler cannot be null\");\n\t\tthis.logoutSuccessHandler = logoutSuccessHandler;\n\t}\n\n\t/**\n\t * Sets the {@link ServerLogoutHandler}. The default is\n\t * {@link SecurityContextServerLogoutHandler}.\n\t * @param logoutHandler The handler to use\n\t */\n\tpublic void setLogoutHandler(ServerLogoutHandler logoutHandler) {\n\t\tAssert.notNull(logoutHandler, \"logoutHandler must not be null\");\n\t\tthis.logoutHandler = logoutHandler;\n\t}\n\n\tpublic void setRequiresLogoutMatcher(ServerWebExchangeMatcher requiresLogoutMatcher) {\n\t\tAssert.notNull(requiresLogoutMatcher, \"requiresLogoutMatcher must not be null\");\n\t\tthis.requiresLogout = requiresLogoutMatcher;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/RedirectServerLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.util.Assert;\n\n/**\n * Performs a redirect on log out success.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class RedirectServerLogoutSuccessHandler implements ServerLogoutSuccessHandler {\n\n\tpublic static final String DEFAULT_LOGOUT_SUCCESS_URL = \"/login?logout\";\n\n\tprivate URI logoutSuccessUrl = URI.create(DEFAULT_LOGOUT_SUCCESS_URL);\n\n\tprivate ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();\n\n\t@Override\n\tpublic Mono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn this.redirectStrategy.sendRedirect(exchange.getExchange(), this.logoutSuccessUrl);\n\t}\n\n\t/**\n\t * The URL to redirect to after successfully logging out.\n\t * @param logoutSuccessUrl the url to redirect to. Default is \"/login?logout\".\n\t */\n\tpublic void setLogoutSuccessUrl(URI logoutSuccessUrl) {\n\t\tAssert.notNull(logoutSuccessUrl, \"logoutSuccessUrl cannot be null\");\n\t\tthis.logoutSuccessUrl = logoutSuccessUrl;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/SecurityContextServerLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ServerLogoutHandler} which removes the SecurityContext using the provided\n * {@link ServerSecurityContextRepository}\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class SecurityContextServerLogoutHandler implements ServerLogoutHandler {\n\n\tprivate ServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();\n\n\t@Override\n\tpublic Mono<Void> logout(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn this.securityContextRepository.save(exchange.getExchange(), null);\n\t}\n\n\t/**\n\t * Sets the {@link ServerSecurityContextRepository} that should be used for logging\n\t * out. Default is {@link WebSessionServerSecurityContextRepository}\n\t * @param securityContextRepository the {@link ServerSecurityContextRepository} to\n\t * use.\n\t */\n\tpublic void setSecurityContextRepository(ServerSecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/ServerLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\n\n/**\n * Handles log out\n *\n * @author Rob Winch\n * @since 5.0\n * @see ServerLogoutSuccessHandler\n */\npublic interface ServerLogoutHandler {\n\n\t/**\n\t * Invoked when log out is requested\n\t * @param exchange the exchange\n\t * @param authentication the {@link Authentication}\n\t * @return a completion notification (success or error)\n\t */\n\tMono<Void> logout(WebFilterExchange exchange, Authentication authentication);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/ServerLogoutSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\n\n/**\n * Strategy for when log out was successfully performed (typically after\n * {@link ServerLogoutHandler} is invoked).\n *\n * @author Rob Winch\n * @since 5.0\n * @see ServerLogoutHandler\n */\npublic interface ServerLogoutSuccessHandler {\n\n\t/**\n\t * Invoked after log out was successful\n\t * @param exchange the exchange\n\t * @param authentication the {@link Authentication}\n\t * @return a completion notification (success or error)\n\t */\n\tMono<Void> onLogoutSuccess(WebFilterExchange exchange, Authentication authentication);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/WebSessionServerLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.WebSession;\n\n/**\n * A {@link ServerLogoutHandler} which invalidates the active {@link WebSession}.\n *\n * @author Bogdan Ilchyshyn\n * @since 5.6\n */\npublic class WebSessionServerLogoutHandler implements ServerLogoutHandler {\n\n\t@Override\n\tpublic Mono<Void> logout(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn exchange.getExchange().getSession().flatMap(WebSession::invalidate);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/logout/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive logout APIs.\n */\n@NullMarked\npackage org.springframework.security.web.server.authentication.logout;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ott/DefaultServerGenerateOneTimeTokenRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport java.time.Duration;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Default implementation of {@link ServerGenerateOneTimeTokenRequestResolver}. Resolves\n * {@link GenerateOneTimeTokenRequest} from username parameter.\n *\n * @author Max Batischev\n * @since 6.5\n */\npublic final class DefaultServerGenerateOneTimeTokenRequestResolver\n\t\timplements ServerGenerateOneTimeTokenRequestResolver {\n\n\tprivate static final String USERNAME = \"username\";\n\n\tprivate static final Duration DEFAULT_EXPIRES_IN = Duration.ofMinutes(5);\n\n\tprivate Duration expiresIn = DEFAULT_EXPIRES_IN;\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<GenerateOneTimeTokenRequest> resolve(ServerWebExchange exchange) {\n\t\t// @formatter:off\n\t\treturn exchange.getFormData()\n\t\t\t\t.mapNotNull((data) -> data.getFirst(USERNAME))\n\t\t\t\t.switchIfEmpty(Mono.empty())\n\t\t\t\t.map((username) -> new GenerateOneTimeTokenRequest(username, this.expiresIn));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Sets one-time token expiration time\n\t * @param expiresIn one-time token expiration time\n\t */\n\tpublic void setExpiresIn(Duration expiresIn) {\n\t\tAssert.notNull(expiresIn, \"expiresIn cannot be null\");\n\t\tthis.expiresIn = expiresIn;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ott/GenerateOneTimeTokenWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * {@link WebFilter} implementation that process a One-Time Token generation request.\n *\n * @author Max Batischev\n * @since 6.4\n * @see ReactiveOneTimeTokenService\n */\npublic final class GenerateOneTimeTokenWebFilter implements WebFilter {\n\n\tprivate final ReactiveOneTimeTokenService oneTimeTokenService;\n\n\tprivate ServerWebExchangeMatcher matcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, \"/ott/generate\");\n\n\tprivate ServerGenerateOneTimeTokenRequestResolver generateRequestResolver = new DefaultServerGenerateOneTimeTokenRequestResolver();\n\n\tprivate final ServerOneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler;\n\n\tpublic GenerateOneTimeTokenWebFilter(ReactiveOneTimeTokenService oneTimeTokenService,\n\t\t\tServerOneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler) {\n\t\tAssert.notNull(oneTimeTokenGenerationSuccessHandler, \"oneTimeTokenGenerationSuccessHandler cannot be null\");\n\t\tAssert.notNull(oneTimeTokenService, \"oneTimeTokenService cannot be null\");\n\t\tthis.oneTimeTokenGenerationSuccessHandler = oneTimeTokenGenerationSuccessHandler;\n\t\tthis.oneTimeTokenService = oneTimeTokenService;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t// @formatter:off\n\t\treturn this.matcher.matches(exchange)\n\t\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t\t.flatMap((result) -> this.generateRequestResolver.resolve(exchange))\n\t\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t\t.flatMap(this.oneTimeTokenService::generate)\n\t\t\t\t.flatMap((token) -> this.oneTimeTokenGenerationSuccessHandler.handle(exchange, token));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Use the given {@link ServerWebExchangeMatcher} to match the request.\n\t * @param matcher\n\t */\n\tpublic void setRequestMatcher(ServerWebExchangeMatcher matcher) {\n\t\tAssert.notNull(matcher, \"matcher cannot be null\");\n\t\tthis.matcher = matcher;\n\t}\n\n\t/**\n\t * Use the given {@link ServerGenerateOneTimeTokenRequestResolver} to resolve the\n\t * request, defaults to {@link DefaultServerGenerateOneTimeTokenRequestResolver}\n\t * @param requestResolver {@link ServerGenerateOneTimeTokenRequestResolver}\n\t * @since 6.5\n\t */\n\tpublic void setGenerateRequestResolver(ServerGenerateOneTimeTokenRequestResolver requestResolver) {\n\t\tAssert.notNull(requestResolver, \"requestResolver cannot be null\");\n\t\tthis.generateRequestResolver = requestResolver;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ott/ServerGenerateOneTimeTokenRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A strategy for resolving a {@link GenerateOneTimeTokenRequest} from the\n * {@link ServerWebExchange}.\n *\n * @author Max Batischev\n * @since 6.5\n */\npublic interface ServerGenerateOneTimeTokenRequestResolver {\n\n\t/**\n\t * Resolves {@link GenerateOneTimeTokenRequest} from {@link ServerWebExchange}\n\t * @param exchange {@link ServerWebExchange} to resolve\n\t * @return {@link GenerateOneTimeTokenRequest}\n\t */\n\tMono<GenerateOneTimeTokenRequest> resolve(ServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ott/ServerOneTimeTokenAuthenticationConverter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.authentication.ServerAuthenticationConverter;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of {@link ServerAuthenticationConverter} for resolving\n * {@link OneTimeTokenAuthenticationToken} from token parameter.\n *\n * @author Max Batischev\n * @since 6.4\n * @see GenerateOneTimeTokenWebFilter\n */\npublic final class ServerOneTimeTokenAuthenticationConverter implements ServerAuthenticationConverter {\n\n\tprivate static final String TOKEN = \"token\";\n\n\t@Override\n\tpublic Mono<Authentication> convert(ServerWebExchange exchange) {\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\tif (isFormEncodedRequest(exchange.getRequest())) {\n\t\t\treturn exchange.getFormData()\n\t\t\t\t.map((data) -> OneTimeTokenAuthenticationToken.unauthenticated(data.getFirst(TOKEN)));\n\t\t}\n\t\tString token = resolveTokenFromRequest(exchange.getRequest());\n\t\tif (!StringUtils.hasText(token)) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\treturn Mono.just(OneTimeTokenAuthenticationToken.unauthenticated(token));\n\t}\n\n\tprivate @Nullable String resolveTokenFromRequest(ServerHttpRequest request) {\n\t\tList<String> parameterTokens = request.getQueryParams().get(TOKEN);\n\t\tif (CollectionUtils.isEmpty(parameterTokens)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (parameterTokens.size() == 1) {\n\t\t\treturn parameterTokens.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate boolean isFormEncodedRequest(ServerHttpRequest request) {\n\t\treturn HttpMethod.POST.equals(request.getMethod()) && MediaType.APPLICATION_FORM_URLENCODED_VALUE\n\t\t\t.equals(request.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ott/ServerOneTimeTokenGenerationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Defines a reactive strategy to handle generated one-time tokens.\n *\n * @author Max Batischev\n * @since 6.4\n */\n@FunctionalInterface\npublic interface ServerOneTimeTokenGenerationSuccessHandler {\n\n\t/**\n\t * Handles generated one-time tokens\n\t * @param exchange the {@link ServerWebExchange} to use\n\t * @param oneTimeToken the {@link OneTimeToken} to handle\n\t * @return a completion handling (success or error)\n\t */\n\tMono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ott/ServerRedirectOneTimeTokenGenerationSuccessHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.ott.OneTimeToken;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ServerOneTimeTokenGenerationSuccessHandler} that performs a redirect to a\n * specific location\n *\n * @author Max Batischev\n * @since 6.4\n */\npublic final class ServerRedirectOneTimeTokenGenerationSuccessHandler\n\t\timplements ServerOneTimeTokenGenerationSuccessHandler {\n\n\tprivate final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();\n\n\tprivate final URI redirectUri;\n\n\tpublic ServerRedirectOneTimeTokenGenerationSuccessHandler(String redirectUri) {\n\t\tAssert.hasText(redirectUri, \"redirectUri cannot be empty or null\");\n\t\tthis.redirectUri = URI.create(redirectUri);\n\t}\n\n\t@Override\n\tpublic Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {\n\t\treturn this.redirectStrategy.sendRedirect(exchange, this.redirectUri);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/ott/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive OTT APIs.\n */\n@NullMarked\npackage org.springframework.security.web.server.authentication.ott;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive web Authorization APIs.\n */\n@NullMarked\npackage org.springframework.security.web.server.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/AuthorizationContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class AuthorizationContext {\n\n\tprivate final ServerWebExchange exchange;\n\n\tprivate final Map<String, Object> variables;\n\n\tpublic AuthorizationContext(ServerWebExchange exchange) {\n\t\tthis(exchange, Collections.emptyMap());\n\t}\n\n\tpublic AuthorizationContext(ServerWebExchange exchange, Map<String, Object> variables) {\n\t\tthis.exchange = exchange;\n\t\tthis.variables = variables;\n\t}\n\n\tpublic ServerWebExchange getExchange() {\n\t\treturn this.exchange;\n\t}\n\n\tpublic Map<String, Object> getVariables() {\n\t\treturn Collections.unmodifiableMap(this.variables);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/AuthorizationWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n */\npublic class AuthorizationWebFilter implements WebFilter {\n\n\tprivate static final Log logger = LogFactory.getLog(AuthorizationWebFilter.class);\n\n\tprivate ReactiveAuthorizationManager<? super ServerWebExchange> authorizationManager;\n\n\tpublic AuthorizationWebFilter(ReactiveAuthorizationManager<? super ServerWebExchange> authorizationManager) {\n\t\tthis.authorizationManager = authorizationManager;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t.filter((c) -> c.getAuthentication() != null)\n\t\t\t.mapNotNull(SecurityContext::getAuthentication)\n\t\t\t.as((authentication) -> this.authorizationManager.verify(authentication, exchange))\n\t\t\t.doOnSuccess((it) -> logger.debug(\"Authorization successful\"))\n\t\t\t.doOnError(AccessDeniedException.class,\n\t\t\t\t\t(ex) -> logger.debug(LogMessage.format(\"Authorization failed: %s\", ex.getMessage())))\n\t\t\t.switchIfEmpty(chain.filter(exchange));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcherEntry;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @author Evgeniy Cheban\n * @since 5.0\n */\npublic final class DelegatingReactiveAuthorizationManager implements ReactiveAuthorizationManager<ServerWebExchange> {\n\n\tprivate static final Log logger = LogFactory.getLog(DelegatingReactiveAuthorizationManager.class);\n\n\tprivate final List<ServerWebExchangeMatcherEntry<ReactiveAuthorizationManager<AuthorizationContext>>> mappings;\n\n\tprivate DelegatingReactiveAuthorizationManager(\n\t\t\tList<ServerWebExchangeMatcherEntry<ReactiveAuthorizationManager<AuthorizationContext>>> mappings) {\n\t\tthis.mappings = mappings;\n\t}\n\n\t@Override\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, ServerWebExchange exchange) {\n\t\treturn Flux.fromIterable(this.mappings)\n\t\t\t.concatMap((mapping) -> mapping.getMatcher()\n\t\t\t\t.matches(exchange)\n\t\t\t\t.filter(MatchResult::isMatch)\n\t\t\t\t.map(MatchResult::getVariables)\n\t\t\t\t.flatMap((variables) -> {\n\t\t\t\t\tlogger.debug(LogMessage.of(() -> \"Checking authorization on '\"\n\t\t\t\t\t\t\t+ exchange.getRequest().getPath().pathWithinApplication() + \"' using \"\n\t\t\t\t\t\t\t+ mapping.getEntry()));\n\t\t\t\t\treturn mapping.getEntry().authorize(authentication, new AuthorizationContext(exchange, variables));\n\t\t\t\t}))\n\t\t\t.next()\n\t\t\t.switchIfEmpty(Mono.fromCallable(() -> new AuthorizationDecision(false)));\n\t}\n\n\tpublic static DelegatingReactiveAuthorizationManager.Builder builder() {\n\t\treturn new DelegatingReactiveAuthorizationManager.Builder();\n\t}\n\n\tpublic static final class Builder {\n\n\t\tprivate final List<ServerWebExchangeMatcherEntry<ReactiveAuthorizationManager<AuthorizationContext>>> mappings = new ArrayList<>();\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\tpublic DelegatingReactiveAuthorizationManager.Builder add(\n\t\t\t\tServerWebExchangeMatcherEntry<ReactiveAuthorizationManager<AuthorizationContext>> entry) {\n\t\t\tthis.mappings.add(entry);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DelegatingReactiveAuthorizationManager build() {\n\t\t\treturn new DelegatingReactiveAuthorizationManager(this.mappings);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/ExceptionTranslationWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authentication.InsufficientAuthenticationException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * @author Rob Winch\n * @author César Revert\n * @since 5.0\n */\npublic class ExceptionTranslationWebFilter implements WebFilter {\n\n\tprivate ServerAuthenticationEntryPoint authenticationEntryPoint = new HttpBasicServerAuthenticationEntryPoint();\n\n\tprivate ServerAccessDeniedHandler accessDeniedHandler = new HttpStatusServerAccessDeniedHandler(\n\t\t\tHttpStatus.FORBIDDEN);\n\n\tprivate AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn chain.filter(exchange)\n\t\t\t.onErrorResume(AccessDeniedException.class,\n\t\t\t\t\t(denied) -> exchange.getPrincipal()\n\t\t\t\t\t\t.switchIfEmpty(Mono.defer(() -> commenceAuthentication(exchange, null)))\n\t\t\t\t\t\t.flatMap((principal) -> {\n\t\t\t\t\t\t\tif (!(principal instanceof Authentication authentication)) {\n\t\t\t\t\t\t\t\treturn this.accessDeniedHandler.handle(exchange, denied);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (this.authenticationTrustResolver.isAuthenticated(authentication)) {\n\t\t\t\t\t\t\t\treturn this.accessDeniedHandler.handle(exchange, denied);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn commenceAuthentication(exchange, authentication);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then());\n\t}\n\n\t/**\n\t * Sets the access denied handler.\n\t * @param accessDeniedHandler the access denied handler to use. Default is\n\t * HttpStatusAccessDeniedHandler with HttpStatus.FORBIDDEN\n\t */\n\tpublic void setAccessDeniedHandler(ServerAccessDeniedHandler accessDeniedHandler) {\n\t\tAssert.notNull(accessDeniedHandler, \"accessDeniedHandler cannot be null\");\n\t\tthis.accessDeniedHandler = accessDeniedHandler;\n\t}\n\n\t/**\n\t * Sets the authentication entry point used when authentication is required\n\t * @param authenticationEntryPoint the authentication entry point to use. Default is\n\t * {@link HttpBasicServerAuthenticationEntryPoint}\n\t */\n\tpublic void setAuthenticationEntryPoint(ServerAuthenticationEntryPoint authenticationEntryPoint) {\n\t\tAssert.notNull(authenticationEntryPoint, \"authenticationEntryPoint cannot be null\");\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t}\n\n\t/**\n\t * Sets the authentication trust resolver.\n\t * @param authenticationTrustResolver the authentication trust resolver to use.\n\t * Default is {@link AuthenticationTrustResolverImpl}\n\t *\n\t * @since 5.5\n\t */\n\tpublic void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {\n\t\tAssert.notNull(authenticationTrustResolver, \"authenticationTrustResolver must not be null\");\n\t\tthis.authenticationTrustResolver = authenticationTrustResolver;\n\t}\n\n\tprivate <T> Mono<T> commenceAuthentication(ServerWebExchange exchange, @Nullable Authentication authentication) {\n\t\tAuthenticationException cause = new InsufficientAuthenticationException(\n\t\t\t\t\"Full authentication is required to access this resource\");\n\t\tAuthenticationException ex = new AuthenticationCredentialsNotFoundException(\"Not Authenticated\", cause);\n\t\tif (authentication != null) {\n\t\t\tex.setAuthenticationRequest(authentication);\n\t\t}\n\t\treturn this.authenticationEntryPoint.commence(exchange, ex).then(Mono.empty());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/HttpStatusServerAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport java.nio.charset.Charset;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.core.io.buffer.DataBufferUtils;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Sets the provided HTTP Status when access is denied.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class HttpStatusServerAccessDeniedHandler implements ServerAccessDeniedHandler {\n\n\tprivate final HttpStatus httpStatus;\n\n\t/**\n\t * Creates an instance with the provided status\n\t * @param httpStatus the status to use\n\t */\n\tpublic HttpStatusServerAccessDeniedHandler(HttpStatus httpStatus) {\n\t\tAssert.notNull(httpStatus, \"httpStatus cannot be null\");\n\t\tthis.httpStatus = httpStatus;\n\t}\n\n\t@Override\n\tpublic Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException ex) {\n\t\treturn Mono.defer(() -> Mono.just(exchange.getResponse())).flatMap((response) -> {\n\t\t\tresponse.setStatusCode(this.httpStatus);\n\t\t\tresponse.getHeaders().setContentType(MediaType.TEXT_PLAIN);\n\t\t\tDataBufferFactory dataBufferFactory = response.bufferFactory();\n\t\t\tDataBuffer buffer = dataBufferFactory.wrap(\"Access Denied\".getBytes(Charset.defaultCharset()));\n\t\t\treturn response.writeWith(Mono.just(buffer)).doOnError((error) -> DataBufferUtils.release(buffer));\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/IpAddressReactiveAuthorizationManager.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.ReactiveAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.util.matcher.IpAddressServerWebExchangeMatcher;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link ReactiveAuthorizationManager}, that determines if the current request contains\n * the specified address or range of addresses\n *\n * @author Guirong Hu\n * @since 5.7\n */\npublic final class IpAddressReactiveAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {\n\n\tprivate final IpAddressServerWebExchangeMatcher ipAddressExchangeMatcher;\n\n\tIpAddressReactiveAuthorizationManager(String ipAddress) {\n\t\tthis.ipAddressExchangeMatcher = new IpAddressServerWebExchangeMatcher(ipAddress);\n\t}\n\n\t@Override\n\tpublic Mono<AuthorizationResult> authorize(Mono<Authentication> authentication, AuthorizationContext context) {\n\t\treturn Mono.just(context.getExchange())\n\t\t\t.flatMap(this.ipAddressExchangeMatcher::matches)\n\t\t\t.map((matchResult) -> new AuthorizationDecision(matchResult.isMatch()));\n\t}\n\n\t/**\n\t * Creates an instance of {@link IpAddressReactiveAuthorizationManager} with the\n\t * provided IP address.\n\t * @param ipAddress the address or range of addresses from which the request must\n\t * @return the new instance\n\t */\n\tpublic static IpAddressReactiveAuthorizationManager hasIpAddress(String ipAddress) {\n\t\tAssert.notNull(ipAddress, \"This IP address is required; it must not be null\");\n\t\treturn new IpAddressReactiveAuthorizationManager(ipAddress);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/ServerAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic interface ServerAccessDeniedHandler {\n\n\tMono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/ServerWebExchangeDelegatingServerAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ServerAccessDeniedHandler} which delegates to multiple\n * {@link ServerAccessDeniedHandler}s based on a {@link ServerWebExchangeMatcher}\n *\n * @author Josh Cummings\n * @since 5.1\n */\npublic class ServerWebExchangeDelegatingServerAccessDeniedHandler implements ServerAccessDeniedHandler {\n\n\tprivate final List<DelegateEntry> handlers;\n\n\tprivate ServerAccessDeniedHandler defaultHandler = (exchange, ex) -> {\n\t\texchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);\n\t\treturn exchange.getResponse().setComplete();\n\t};\n\n\t/**\n\t * Creates a new instance\n\t * @param handlers a list of {@link ServerWebExchangeMatcher}/\n\t * {@link ServerAccessDeniedHandler} pairs that should be used. Each is considered in\n\t * the order they are specified and only the first {@link ServerAccessDeniedHandler}\n\t * is used. If none match, then the default {@link ServerAccessDeniedHandler} is used.\n\t */\n\tpublic ServerWebExchangeDelegatingServerAccessDeniedHandler(DelegateEntry... handlers) {\n\t\tthis(Arrays.asList(handlers));\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param handlers a list of {@link ServerWebExchangeMatcher}/\n\t * {@link ServerAccessDeniedHandler} pairs that should be used. Each is considered in\n\t * the order they are specified and only the first {@link ServerAccessDeniedHandler}\n\t * is used. If none match, then the default {@link ServerAccessDeniedHandler} is used.\n\t */\n\tpublic ServerWebExchangeDelegatingServerAccessDeniedHandler(List<DelegateEntry> handlers) {\n\t\tAssert.notEmpty(handlers, \"handlers cannot be null\");\n\t\tthis.handlers = handlers;\n\t}\n\n\t@Override\n\tpublic Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {\n\t\treturn Flux.fromIterable(this.handlers)\n\t\t\t.filterWhen((entry) -> isMatch(exchange, entry))\n\t\t\t.next()\n\t\t\t.map(DelegateEntry::getAccessDeniedHandler)\n\t\t\t.defaultIfEmpty(this.defaultHandler)\n\t\t\t.flatMap((handler) -> handler.handle(exchange, denied));\n\t}\n\n\t/**\n\t * Use this {@link ServerAccessDeniedHandler} when no {@link ServerWebExchangeMatcher}\n\t * matches.\n\t * @param accessDeniedHandler - the default {@link ServerAccessDeniedHandler} to use\n\t */\n\tpublic void setDefaultAccessDeniedHandler(ServerAccessDeniedHandler accessDeniedHandler) {\n\t\tAssert.notNull(accessDeniedHandler, \"accessDeniedHandler cannot be null\");\n\t\tthis.defaultHandler = accessDeniedHandler;\n\t}\n\n\tprivate Mono<Boolean> isMatch(ServerWebExchange exchange, DelegateEntry entry) {\n\t\tServerWebExchangeMatcher matcher = entry.getMatcher();\n\t\treturn matcher.matches(exchange).map(ServerWebExchangeMatcher.MatchResult::isMatch);\n\t}\n\n\tpublic static class DelegateEntry {\n\n\t\tprivate final ServerWebExchangeMatcher matcher;\n\n\t\tprivate final ServerAccessDeniedHandler accessDeniedHandler;\n\n\t\tpublic DelegateEntry(ServerWebExchangeMatcher matcher, ServerAccessDeniedHandler accessDeniedHandler) {\n\t\t\tthis.matcher = matcher;\n\t\t\tthis.accessDeniedHandler = accessDeniedHandler;\n\t\t}\n\n\t\tpublic ServerWebExchangeMatcher getMatcher() {\n\t\t\treturn this.matcher;\n\t\t}\n\n\t\tpublic ServerAccessDeniedHandler getAccessDeniedHandler() {\n\t\t\treturn this.accessDeniedHandler;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/authorization/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive web OTT APIs.\n */\n@NullMarked\npackage org.springframework.security.web.server.authorization;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/context/NoOpServerSecurityContextRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A do nothing implementation of {@link ServerSecurityContextRepository}. Used in\n * stateless applications.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic final class NoOpServerSecurityContextRepository implements ServerSecurityContextRepository {\n\n\tprivate static final NoOpServerSecurityContextRepository INSTANCE = new NoOpServerSecurityContextRepository();\n\n\tprivate NoOpServerSecurityContextRepository() {\n\t}\n\n\t@Override\n\tpublic Mono<Void> save(ServerWebExchange exchange, @Nullable SecurityContext context) {\n\t\treturn Mono.empty();\n\t}\n\n\t@Override\n\tpublic Mono<SecurityContext> load(ServerWebExchange exchange) {\n\t\treturn Mono.empty();\n\t}\n\n\tpublic static NoOpServerSecurityContextRepository getInstance() {\n\t\treturn INSTANCE;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/context/ReactorContextWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Uses a {@link ServerSecurityContextRepository} to provide the {@link SecurityContext}\n * to initialize the {@link ReactiveSecurityContextHolder}.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class ReactorContextWebFilter implements WebFilter {\n\n\tprivate final ServerSecurityContextRepository repository;\n\n\tpublic ReactorContextWebFilter(ServerSecurityContextRepository repository) {\n\t\tAssert.notNull(repository, \"repository cannot be null\");\n\t\tthis.repository = repository;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn chain.filter(exchange)\n\t\t\t.contextWrite((context) -> context.hasKey(SecurityContext.class) ? context\n\t\t\t\t\t: withSecurityContext(context, exchange));\n\t}\n\n\tprivate Context withSecurityContext(Context mainContext, ServerWebExchange exchange) {\n\t\treturn mainContext\n\t\t\t.putAll(this.repository.load(exchange).as(ReactiveSecurityContextHolder::withSecurityContext).readOnly());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/context/SecurityContextServerWebExchange.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport java.security.Principal;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.ServerWebExchangeDecorator;\n\n/**\n * Overrides the {@link ServerWebExchange#getPrincipal()} with the provided\n * SecurityContext\n *\n * @author Rob Winch\n * @since 5.0\n * @see SecurityContextServerWebExchangeWebFilter\n */\npublic class SecurityContextServerWebExchange extends ServerWebExchangeDecorator {\n\n\tprivate final Mono<SecurityContext> context;\n\n\tpublic SecurityContextServerWebExchange(ServerWebExchange delegate, Mono<SecurityContext> context) {\n\t\tsuper(delegate);\n\t\tthis.context = context;\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"unchecked\", \"NullAway\" }) // https://github.com/uber/NullAway/issues/1290\n\tpublic <T extends Principal> Mono<T> getPrincipal() {\n\t\treturn this.context.mapNotNull((context) -> (T) context.getAuthentication());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/context/SecurityContextServerWebExchangeWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Override the {@link ServerWebExchange#getPrincipal()} to be looked up using\n * {@link ReactiveSecurityContextHolder}.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class SecurityContextServerWebExchangeWebFilter implements WebFilter {\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\n\t\treturn chain.filter(new SecurityContextServerWebExchange(exchange, ReactiveSecurityContextHolder.getContext()));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/context/ServerSecurityContextRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Strategy used for persisting a {@link SecurityContext} between requests.\n *\n * @author Rob Winch\n * @since 5.0\n * @see ReactorContextWebFilter\n */\npublic interface ServerSecurityContextRepository {\n\n\t/**\n\t * Saves the SecurityContext\n\t * @param exchange the exchange to associate to the SecurityContext\n\t * @param context the SecurityContext to save\n\t * @return a completion notification (success or error)\n\t */\n\tMono<Void> save(ServerWebExchange exchange, @Nullable SecurityContext context);\n\n\t/**\n\t * Loads the SecurityContext associated with the {@link ServerWebExchange}\n\t * @param exchange the exchange to look up the {@link SecurityContext}\n\t * @return the {@link SecurityContext} to lookup or empty if not found. Never null\n\t */\n\tMono<SecurityContext> load(ServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/context/WebSessionServerSecurityContextRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\n/**\n * Stores the {@link SecurityContext} in the\n * {@link org.springframework.web.server.WebSession}. When a {@link SecurityContext} is\n * saved, the session id is changed to prevent session fixation attacks.\n *\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n */\npublic class WebSessionServerSecurityContextRepository implements ServerSecurityContextRepository {\n\n\tprivate static final Log logger = LogFactory.getLog(WebSessionServerSecurityContextRepository.class);\n\n\t/**\n\t * The default session attribute name to save and load the {@link SecurityContext}\n\t */\n\tpublic static final String DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME = \"SPRING_SECURITY_CONTEXT\";\n\n\tprivate String springSecurityContextAttrName = DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME;\n\n\tprivate boolean cacheSecurityContext;\n\n\t/**\n\t * Sets the session attribute name used to save and load the {@link SecurityContext}\n\t * @param springSecurityContextAttrName the session attribute name to use to save and\n\t * load the {@link SecurityContext}\n\t */\n\tpublic void setSpringSecurityContextAttrName(String springSecurityContextAttrName) {\n\t\tAssert.hasText(springSecurityContextAttrName, \"springSecurityContextAttrName cannot be null or empty\");\n\t\tthis.springSecurityContextAttrName = springSecurityContextAttrName;\n\t}\n\n\t/**\n\t * If set to true the result of {@link #load(ServerWebExchange)} will use\n\t * {@link Mono#cache()} to prevent multiple lookups.\n\t * @param cacheSecurityContext true if {@link Mono#cache()} should be used, else\n\t * false.\n\t */\n\tpublic void setCacheSecurityContext(boolean cacheSecurityContext) {\n\t\tthis.cacheSecurityContext = cacheSecurityContext;\n\t}\n\n\t@Override\n\tpublic Mono<Void> save(ServerWebExchange exchange, @Nullable SecurityContext context) {\n\t\treturn exchange.getSession().doOnNext((session) -> {\n\t\t\tif (context == null) {\n\t\t\t\tsession.getAttributes().remove(this.springSecurityContextAttrName);\n\t\t\t\tlogger.debug(LogMessage.format(\"Removed SecurityContext stored in WebSession: '%s'\", session));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsession.getAttributes().put(this.springSecurityContextAttrName, context);\n\t\t\t\tlogger.debug(LogMessage.format(\"Saved SecurityContext '%s' in WebSession: '%s'\", context, session));\n\t\t\t}\n\t\t}).flatMap(WebSession::changeSessionId);\n\t}\n\n\t@Override\n\tpublic Mono<SecurityContext> load(ServerWebExchange exchange) {\n\t\tMono<SecurityContext> result = exchange.getSession().flatMap((session) -> {\n\t\t\tSecurityContext context = (SecurityContext) session.getAttribute(this.springSecurityContextAttrName);\n\t\t\tlogger.debug((context != null)\n\t\t\t\t\t? LogMessage.format(\"Found SecurityContext '%s' in WebSession: '%s'\", context, session)\n\t\t\t\t\t: LogMessage.format(\"No SecurityContext found in WebSession: '%s'\", session));\n\t\t\treturn Mono.justOrEmpty(context);\n\t\t});\n\t\treturn (this.cacheSecurityContext) ? result.cache() : result;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/context/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive web context APIs.\n */\n@NullMarked\npackage org.springframework.security.web.server.context;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\n\nimport org.springframework.http.HttpCookie;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A {@link ServerCsrfTokenRepository} that persists the CSRF token in a cookie named\n * \"XSRF-TOKEN\" and reads from the header \"X-XSRF-TOKEN\" following the conventions of\n * AngularJS. When using with AngularJS be sure to use {@link #withHttpOnlyFalse()} .\n *\n * @author Eric Deandrea\n * @author Thomas Vitale\n * @author Alonso Araya\n * @author Alex Montoya\n * @since 5.1\n */\npublic final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRepository {\n\n\tstatic final String DEFAULT_CSRF_COOKIE_NAME = \"XSRF-TOKEN\";\n\tstatic final String DEFAULT_CSRF_PARAMETER_NAME = \"_csrf\";\n\tstatic final String DEFAULT_CSRF_HEADER_NAME = \"X-XSRF-TOKEN\";\n\n\tprivate String parameterName = DEFAULT_CSRF_PARAMETER_NAME;\n\n\tprivate String headerName = DEFAULT_CSRF_HEADER_NAME;\n\n\tprivate @Nullable String cookiePath;\n\n\tprivate @Nullable String cookieDomain;\n\n\tprivate String cookieName = DEFAULT_CSRF_COOKIE_NAME;\n\n\tprivate boolean cookieHttpOnly = true;\n\n\tprivate @Nullable Boolean secure;\n\n\tprivate int cookieMaxAge = -1;\n\n\tprivate Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer = (builder) -> {\n\t};\n\n\t/**\n\t * Add a {@link Consumer} for a {@code ResponseCookieBuilder} that will be invoked for\n\t * each cookie being built, just before the call to {@code build()}.\n\t * @param cookieCustomizer consumer for a cookie builder\n\t * @since 6.1\n\t */\n\tpublic void setCookieCustomizer(Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer) {\n\t\tAssert.notNull(cookieCustomizer, \"cookieCustomizer must not be null\");\n\t\tthis.cookieCustomizer = cookieCustomizer;\n\t}\n\n\t/**\n\t * Factory method to conveniently create an instance that has creates cookies with\n\t * {@link ResponseCookie#isHttpOnly} set to false.\n\t * @return an instance of CookieCsrfTokenRepository that creates cookies with\n\t * {@link ResponseCookie#isHttpOnly} set to false\n\t */\n\tpublic static CookieServerCsrfTokenRepository withHttpOnlyFalse() {\n\t\tCookieServerCsrfTokenRepository result = new CookieServerCsrfTokenRepository();\n\t\tresult.cookieHttpOnly = false;\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic Mono<CsrfToken> generateToken(ServerWebExchange exchange) {\n\t\treturn Mono.fromCallable(this::createCsrfToken).subscribeOn(Schedulers.boundedElastic());\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveToken(ServerWebExchange exchange, @Nullable CsrfToken token) {\n\t\treturn Mono.fromRunnable(() -> {\n\t\t\tString tokenValue = (token != null) ? token.getToken() : \"\";\n\t\t\t// @formatter:off\n\t\t\tResponseCookie.ResponseCookieBuilder cookieBuilder = ResponseCookie\n\t\t\t\t\t.from(this.cookieName, tokenValue)\n\t\t\t\t\t.domain(this.cookieDomain)\n\t\t\t\t\t.httpOnly(this.cookieHttpOnly)\n\t\t\t\t\t.maxAge(!tokenValue.isEmpty() ? this.cookieMaxAge : 0)\n\t\t\t\t\t.path((this.cookiePath != null) ? this.cookiePath : getRequestContext(exchange.getRequest()))\n\t\t\t\t\t.secure((this.secure != null) ? this.secure : (exchange.getRequest().getSslInfo() != null));\n\n\t\t\tthis.cookieCustomizer.accept(cookieBuilder);\n\n\t\t\t// @formatter:on\n\t\t\texchange.getResponse().addCookie(cookieBuilder.build());\n\t\t});\n\t}\n\n\t@Override\n\tpublic Mono<CsrfToken> loadToken(ServerWebExchange exchange) {\n\t\treturn Mono.fromCallable(() -> {\n\t\t\tHttpCookie csrfCookie = exchange.getRequest().getCookies().getFirst(this.cookieName);\n\t\t\tif ((csrfCookie == null) || !StringUtils.hasText(csrfCookie.getValue())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn createCsrfToken(csrfCookie.getValue());\n\t\t});\n\t}\n\n\t/**\n\t * Sets the cookie name\n\t * @param cookieName The cookie name\n\t */\n\tpublic void setCookieName(String cookieName) {\n\t\tAssert.hasLength(cookieName, \"cookieName can't be null\");\n\t\tthis.cookieName = cookieName;\n\t}\n\n\t/**\n\t * Sets the parameter name\n\t * @param parameterName The parameter name\n\t */\n\tpublic void setParameterName(String parameterName) {\n\t\tAssert.hasLength(parameterName, \"parameterName can't be null\");\n\t\tthis.parameterName = parameterName;\n\t}\n\n\t/**\n\t * Sets the header name\n\t * @param headerName The header name\n\t */\n\tpublic void setHeaderName(String headerName) {\n\t\tAssert.hasLength(headerName, \"headerName can't be null\");\n\t\tthis.headerName = headerName;\n\t}\n\n\t/**\n\t * Sets the cookie path\n\t * @param cookiePath The cookie path\n\t */\n\tpublic void setCookiePath(String cookiePath) {\n\t\tthis.cookiePath = cookiePath;\n\t}\n\n\tprivate CsrfToken createCsrfToken() {\n\t\treturn createCsrfToken(createNewToken());\n\t}\n\n\tprivate CsrfToken createCsrfToken(String tokenValue) {\n\t\treturn new DefaultCsrfToken(this.headerName, this.parameterName, tokenValue);\n\t}\n\n\tprivate String createNewToken() {\n\t\treturn UUID.randomUUID().toString();\n\t}\n\n\tprivate String getRequestContext(ServerHttpRequest request) {\n\t\tString contextPath = request.getPath().contextPath().value();\n\t\treturn StringUtils.hasLength(contextPath) ? contextPath : \"/\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/CsrfException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.io.Serial;\n\nimport org.springframework.security.access.AccessDeniedException;\n\n/**\n * Thrown when an invalid or missing {@link CsrfToken} is found in the ServerWebExchange\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class CsrfException extends AccessDeniedException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -8209680716517631141L;\n\n\tpublic CsrfException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/CsrfServerLogoutHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;\nimport org.springframework.util.Assert;\n\n/**\n * {@link CsrfServerLogoutHandler} is in charge of removing the {@link CsrfToken} upon\n * logout. A new {@link CsrfToken} will then be generated by the framework upon the next\n * request.\n *\n * @author Eric Deandrea\n * @since 5.1\n */\npublic class CsrfServerLogoutHandler implements ServerLogoutHandler {\n\n\tprivate final ServerCsrfTokenRepository csrfTokenRepository;\n\n\t/**\n\t * Creates a new instance\n\t * @param csrfTokenRepository The {@link ServerCsrfTokenRepository} to use\n\t */\n\tpublic CsrfServerLogoutHandler(ServerCsrfTokenRepository csrfTokenRepository) {\n\t\tAssert.notNull(csrfTokenRepository, \"csrfTokenRepository cannot be null\");\n\t\tthis.csrfTokenRepository = csrfTokenRepository;\n\t}\n\n\t/**\n\t * Clears the {@link CsrfToken}\n\t * @param exchange the exchange\n\t * @param authentication the {@link Authentication}\n\t * @return A completion notification (success or error)\n\t */\n\t@Override\n\tpublic Mono<Void> logout(WebFilterExchange exchange, Authentication authentication) {\n\t\treturn this.csrfTokenRepository.saveToken(exchange.getExchange(), null);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/CsrfToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.io.Serializable;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic interface CsrfToken extends Serializable {\n\n\t/**\n\t * Gets the HTTP header that the CSRF is populated on the response and can be placed\n\t * on requests instead of the parameter. Cannot be null.\n\t * @return the HTTP header that the CSRF is populated on the response and can be\n\t * placed on requests instead of the parameter\n\t */\n\tString getHeaderName();\n\n\t/**\n\t * Gets the HTTP parameter name that should contain the token. Cannot be null.\n\t * @return the HTTP parameter name that should contain the token.\n\t */\n\tString getParameterName();\n\n\t/**\n\t * Gets the token value. Cannot be null.\n\t * @return the token value\n\t */\n\tString getToken();\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/CsrfWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.security.MessageDigest;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;\nimport org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * <p>\n * Applies\n * <a href=\"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)\" >CSRF</a>\n * protection using a synchronizer token pattern. Developers are required to ensure that\n * {@link CsrfWebFilter} is invoked for any request that allows state to change. Typically\n * this just means that they should ensure their web application follows proper REST\n * semantics (i.e. do not change state with the HTTP methods GET, HEAD, TRACE, OPTIONS).\n * </p>\n *\n * <p>\n * Typically the {@link ServerCsrfTokenRepository} implementation chooses to store the\n * {@link CsrfToken} in {@link org.springframework.web.server.WebSession} with\n * {@link WebSessionServerCsrfTokenRepository}. This is preferred to storing the token in\n * a cookie which can be modified by a client application.\n * </p>\n * <p>\n * The {@code Mono&lt;CsrfToken&gt;} is exposes as a request attribute with the name of\n * {@code CsrfToken.class.getName()}. If the token is new it will automatically be saved\n * at the time it is subscribed.\n * </p>\n *\n * @author Rob Winch\n * @author Parikshit Dutta\n * @author Steve Riesenberg\n * @since 5.0\n */\npublic class CsrfWebFilter implements WebFilter {\n\n\tpublic static final ServerWebExchangeMatcher DEFAULT_CSRF_MATCHER = new DefaultRequireCsrfProtectionMatcher();\n\n\t/**\n\t * The attribute name to use when marking a given request as one that should not be\n\t * filtered.\n\t *\n\t * To use, set the attribute on your {@link ServerWebExchange}: <pre>\n\t * \tCsrfWebFilter.skipExchange(exchange);\n\t * </pre>\n\t */\n\tprivate static final String SHOULD_NOT_FILTER = \"SHOULD_NOT_FILTER\" + CsrfWebFilter.class.getName();\n\n\tprivate ServerWebExchangeMatcher requireCsrfProtectionMatcher = DEFAULT_CSRF_MATCHER;\n\n\tprivate ServerCsrfTokenRepository csrfTokenRepository = new WebSessionServerCsrfTokenRepository();\n\n\tprivate ServerAccessDeniedHandler accessDeniedHandler = new HttpStatusServerAccessDeniedHandler(\n\t\t\tHttpStatus.FORBIDDEN);\n\n\tprivate ServerCsrfTokenRequestHandler requestHandler = new XorServerCsrfTokenRequestAttributeHandler();\n\n\tpublic void setAccessDeniedHandler(ServerAccessDeniedHandler accessDeniedHandler) {\n\t\tAssert.notNull(accessDeniedHandler, \"accessDeniedHandler\");\n\t\tthis.accessDeniedHandler = accessDeniedHandler;\n\t}\n\n\tpublic void setCsrfTokenRepository(ServerCsrfTokenRepository csrfTokenRepository) {\n\t\tAssert.notNull(csrfTokenRepository, \"csrfTokenRepository cannot be null\");\n\t\tthis.csrfTokenRepository = csrfTokenRepository;\n\t}\n\n\tpublic void setRequireCsrfProtectionMatcher(ServerWebExchangeMatcher requireCsrfProtectionMatcher) {\n\t\tAssert.notNull(requireCsrfProtectionMatcher, \"requireCsrfProtectionMatcher cannot be null\");\n\t\tthis.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;\n\t}\n\n\t/**\n\t * Specifies a {@link ServerCsrfTokenRequestHandler} that is used to make the\n\t * {@code CsrfToken} available as an exchange attribute.\n\t * <p>\n\t * The default is {@link XorServerCsrfTokenRequestAttributeHandler}.\n\t * @param requestHandler the {@link ServerCsrfTokenRequestHandler} to use\n\t * @since 5.8\n\t */\n\tpublic void setRequestHandler(ServerCsrfTokenRequestHandler requestHandler) {\n\t\tAssert.notNull(requestHandler, \"requestHandler cannot be null\");\n\t\tthis.requestHandler = requestHandler;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\tif (Boolean.TRUE.equals(exchange.getAttribute(SHOULD_NOT_FILTER))) {\n\t\t\treturn chain.filter(exchange).then(Mono.empty());\n\t\t}\n\t\treturn this.requireCsrfProtectionMatcher.matches(exchange)\n\t\t\t.filter(MatchResult::isMatch)\n\t\t\t.filter((matchResult) -> !exchange.getAttributes().containsKey(CsrfToken.class.getName()))\n\t\t\t.flatMap((m) -> validateToken(exchange))\n\t\t\t.flatMap((m) -> continueFilterChain(exchange, chain))\n\t\t\t.switchIfEmpty(continueFilterChain(exchange, chain).then(Mono.empty()))\n\t\t\t.onErrorResume(CsrfException.class, (ex) -> this.accessDeniedHandler.handle(exchange, ex));\n\t}\n\n\tpublic static void skipExchange(ServerWebExchange exchange) {\n\t\texchange.getAttributes().put(SHOULD_NOT_FILTER, Boolean.TRUE);\n\t}\n\n\tprivate Mono<Void> validateToken(ServerWebExchange exchange) {\n\t\treturn this.csrfTokenRepository.loadToken(exchange)\n\t\t\t.switchIfEmpty(Mono.defer(() -> Mono.error(new CsrfException(\"An expected CSRF token cannot be found\"))))\n\t\t\t.filterWhen((expected) -> containsValidCsrfToken(exchange, expected))\n\t\t\t.switchIfEmpty(Mono.defer(() -> Mono.error(new CsrfException(\"Invalid CSRF Token\"))))\n\t\t\t.then();\n\t}\n\n\tprivate Mono<Boolean> containsValidCsrfToken(ServerWebExchange exchange, CsrfToken expected) {\n\t\treturn this.requestHandler.resolveCsrfTokenValue(exchange, expected)\n\t\t\t.map((actual) -> equalsConstantTime(actual, expected.getToken()));\n\t}\n\n\tprivate Mono<Void> continueFilterChain(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn Mono.defer(() -> {\n\t\t\tMono<CsrfToken> csrfToken = csrfToken(exchange);\n\t\t\tthis.requestHandler.handle(exchange, csrfToken);\n\t\t\treturn chain.filter(exchange);\n\t\t});\n\t}\n\n\tprivate Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {\n\t\treturn this.csrfTokenRepository.loadToken(exchange).switchIfEmpty(generateToken(exchange));\n\t}\n\n\t/**\n\t * Constant time comparison to prevent against timing attacks.\n\t * @param expected\n\t * @param actual\n\t * @return\n\t */\n\tprivate static boolean equalsConstantTime(String expected, String actual) {\n\t\tif (expected == actual) {\n\t\t\treturn true;\n\t\t}\n\t\tif (expected == null || actual == null) {\n\t\t\treturn false;\n\t\t}\n\t\t// Encode after ensure that the string is not null\n\t\tbyte[] expectedBytes = Utf8.encode(expected);\n\t\tbyte[] actualBytes = Utf8.encode(actual);\n\t\treturn MessageDigest.isEqual(expectedBytes, actualBytes);\n\t}\n\n\tprivate Mono<CsrfToken> generateToken(ServerWebExchange exchange) {\n\t\treturn this.csrfTokenRepository.generateToken(exchange)\n\t\t\t.delayUntil((token) -> this.csrfTokenRepository.saveToken(exchange, token))\n\t\t\t.cache();\n\t}\n\n\tprivate static class DefaultRequireCsrfProtectionMatcher implements ServerWebExchangeMatcher {\n\n\t\tprivate static final Set<HttpMethod> ALLOWED_METHODS = new HashSet<>(\n\t\t\t\tArrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE, HttpMethod.OPTIONS));\n\n\t\t@Override\n\t\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\t\treturn Mono.just(exchange.getRequest())\n\t\t\t\t.flatMap((r) -> Mono.justOrEmpty(r.getMethod()))\n\t\t\t\t.filter(ALLOWED_METHODS::contains)\n\t\t\t\t.flatMap((m) -> MatchResult.notMatch())\n\t\t\t\t.switchIfEmpty(MatchResult.match());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/DefaultCsrfToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.io.Serial;\n\nimport org.springframework.util.Assert;\n\n/**\n * A CSRF token that is used to protect against CSRF attacks.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic final class DefaultCsrfToken implements CsrfToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 308340117851874929L;\n\n\tprivate final String token;\n\n\tprivate final String parameterName;\n\n\tprivate final String headerName;\n\n\t/**\n\t * Creates a new instance\n\t * @param headerName the HTTP header name to use\n\t * @param parameterName the HTTP parameter name to use\n\t * @param token the value of the token (i.e. expected value of the HTTP parameter of\n\t * parametername).\n\t */\n\tpublic DefaultCsrfToken(String headerName, String parameterName, String token) {\n\t\tAssert.hasLength(headerName, \"headerName cannot be null or empty\");\n\t\tAssert.hasLength(parameterName, \"parameterName cannot be null or empty\");\n\t\tAssert.hasLength(token, \"token cannot be null or empty\");\n\t\tthis.headerName = headerName;\n\t\tthis.parameterName = parameterName;\n\t\tthis.token = token;\n\t}\n\n\t@Override\n\tpublic String getHeaderName() {\n\t\treturn this.headerName;\n\t}\n\n\t@Override\n\tpublic String getParameterName() {\n\t\treturn this.parameterName;\n\t}\n\n\t@Override\n\tpublic String getToken() {\n\t\treturn this.token;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || !(obj instanceof CsrfToken)) {\n\t\t\treturn false;\n\t\t}\n\t\tCsrfToken other = (CsrfToken) obj;\n\t\tif (!getToken().equals(other.getToken())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!getParameterName().equals(other.getParameterName())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn getHeaderName().equals(other.getHeaderName());\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = getToken().hashCode();\n\t\tresult = 31 * result + getParameterName().hashCode();\n\t\tresult = 31 * result + getHeaderName().hashCode();\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/ServerCsrfTokenRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An API to allow changing the method in which the expected {@link CsrfToken} is\n * associated to the {@link ServerWebExchange}. For example, it may be stored in\n * {@link org.springframework.web.server.WebSession}.\n *\n * @author Rob Winch\n * @since 5.0\n * @see WebSessionServerCsrfTokenRepository\n *\n */\npublic interface ServerCsrfTokenRepository {\n\n\t/**\n\t * Generates a {@link CsrfToken}\n\t * @param exchange the {@link ServerWebExchange} to use\n\t * @return the {@link CsrfToken} that was generated. Cannot be null.\n\t */\n\tMono<CsrfToken> generateToken(ServerWebExchange exchange);\n\n\t/**\n\t * Saves the {@link CsrfToken} using the {@link ServerWebExchange}. If the\n\t * {@link CsrfToken} is null, it is the same as deleting it.\n\t * @param exchange the {@link ServerWebExchange} to use\n\t * @param token the {@link CsrfToken} to save or null to delete\n\t */\n\tMono<Void> saveToken(ServerWebExchange exchange, @Nullable CsrfToken token);\n\n\t/**\n\t * Loads the expected {@link CsrfToken} from the {@link ServerWebExchange}\n\t * @param exchange the {@link ServerWebExchange} to use\n\t * @return the {@link CsrfToken} or null if none exists\n\t */\n\tMono<CsrfToken> loadToken(ServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/ServerCsrfTokenRequestAttributeHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.codec.multipart.FormFieldPart;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of the {@link ServerCsrfTokenRequestHandler} interface that is\n * capable of making the {@link CsrfToken} available as an exchange attribute and\n * resolving the token value as either a form data value or header of the request.\n *\n * @author Steve Riesenberg\n * @author Yoobin Yoon\n * @since 5.8\n */\npublic class ServerCsrfTokenRequestAttributeHandler implements ServerCsrfTokenRequestHandler {\n\n\tprivate static final Log logger = LogFactory.getLog(ServerCsrfTokenRequestAttributeHandler.class);\n\n\tprivate boolean isTokenFromMultipartDataEnabled;\n\n\t@Override\n\tpublic void handle(ServerWebExchange exchange, Mono<CsrfToken> csrfToken) {\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\tAssert.notNull(csrfToken, \"csrfToken cannot be null\");\n\t\texchange.getAttributes().put(CsrfToken.class.getName(), csrfToken);\n\t\tlogger.trace(LogMessage.format(\"Wrote a CSRF token to the [%s] exchange attribute\", CsrfToken.class.getName()));\n\t}\n\n\t@Override\n\tpublic Mono<String> resolveCsrfTokenValue(ServerWebExchange exchange, CsrfToken csrfToken) {\n\t\treturn ServerCsrfTokenRequestHandler.super.resolveCsrfTokenValue(exchange, csrfToken)\n\t\t\t.switchIfEmpty(tokenFromMultipartData(exchange, csrfToken));\n\t}\n\n\t/**\n\t * Specifies if the {@code ServerCsrfTokenRequestResolver} should try to resolve the\n\t * actual CSRF token from the body of multipart data requests.\n\t * @param tokenFromMultipartDataEnabled true if should read from multipart form body,\n\t * else false. Default is false\n\t */\n\tpublic void setTokenFromMultipartDataEnabled(boolean tokenFromMultipartDataEnabled) {\n\t\tthis.isTokenFromMultipartDataEnabled = tokenFromMultipartDataEnabled;\n\t}\n\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tprivate Mono<String> tokenFromMultipartData(ServerWebExchange exchange, CsrfToken expected) {\n\t\tif (!this.isTokenFromMultipartDataEnabled) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tServerHttpRequest request = exchange.getRequest();\n\t\tHttpHeaders headers = request.getHeaders();\n\t\tMediaType contentType = headers.getContentType();\n\t\tif (!MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\treturn exchange.getMultipartData()\n\t\t\t.mapNotNull((d) -> d.getFirst(expected.getParameterName()))\n\t\t\t.cast(FormFieldPart.class)\n\t\t\t.map(FormFieldPart::value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/ServerCsrfTokenRequestHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A callback interface that is used to make the {@link CsrfToken} created by the\n * {@link ServerCsrfTokenRepository} available as an exchange attribute. Implementations\n * of this interface may choose to perform additional tasks or customize how the token is\n * made available to the application through exchange attributes.\n *\n * @author Steve Riesenberg\n * @since 5.8\n * @see ServerCsrfTokenRequestAttributeHandler\n */\n@FunctionalInterface\npublic interface ServerCsrfTokenRequestHandler extends ServerCsrfTokenRequestResolver {\n\n\t/**\n\t * Handles a request using a {@link CsrfToken}.\n\t * @param exchange the {@code ServerWebExchange} with the request being handled\n\t * @param csrfToken the {@code Mono<CsrfToken>} created by the\n\t * {@link ServerCsrfTokenRepository}\n\t */\n\tvoid handle(ServerWebExchange exchange, Mono<CsrfToken> csrfToken);\n\n\t@Override\n\tdefault Mono<String> resolveCsrfTokenValue(ServerWebExchange exchange, CsrfToken csrfToken) {\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\tAssert.notNull(csrfToken, \"csrfToken cannot be null\");\n\n\t\tString headerName = csrfToken.getHeaderName();\n\t\tString parameterName = csrfToken.getParameterName();\n\n\t\treturn exchange.getFormData().flatMap((data) -> {\n\t\t\tString token = data.getFirst(parameterName);\n\t\t\tif (token != null) {\n\t\t\t\treturn Mono.just(token);\n\t\t\t}\n\t\t\tServerCsrfTokenRequestHandlerLoggerHolder.logger\n\t\t\t\t.trace(LogMessage.format(\"Did not find a CSRF token in the [%s] request parameter\", parameterName));\n\t\t\treturn Mono.empty();\n\t\t}).switchIfEmpty(Mono.defer(() -> {\n\t\t\tString token = exchange.getRequest().getHeaders().getFirst(headerName);\n\t\t\tif (token != null) {\n\t\t\t\treturn Mono.just(token);\n\t\t\t}\n\t\t\tServerCsrfTokenRequestHandlerLoggerHolder.logger\n\t\t\t\t.trace(LogMessage.format(\"Did not find a CSRF token in the [%s] request header\", headerName));\n\t\t\treturn Mono.empty();\n\t\t}));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/ServerCsrfTokenRequestHandlerLoggerHolder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\n/**\n * Utility class for holding the logger for {@link ServerCsrfTokenRequestHandler}\n */\nfinal class ServerCsrfTokenRequestHandlerLoggerHolder {\n\n\tstatic final Log logger = LogFactory.getLog(ServerCsrfTokenRequestHandler.class);\n\n\tprivate ServerCsrfTokenRequestHandlerLoggerHolder() {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/ServerCsrfTokenRequestResolver.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Implementations of this interface are capable of resolving the token value of a\n * {@link CsrfToken} from the provided {@code ServerWebExchange}. Used by the\n * {@link CsrfWebFilter}.\n *\n * @author Steve Riesenberg\n * @since 5.8\n * @see ServerCsrfTokenRequestAttributeHandler\n */\n@FunctionalInterface\npublic interface ServerCsrfTokenRequestResolver {\n\n\t/**\n\t * Returns the token value resolved from the provided {@code ServerWebExchange} and\n\t * {@link CsrfToken} or {@code Mono.empty()} if not available.\n\t * @param exchange the {@code ServerWebExchange} with the request being processed\n\t * @param csrfToken the {@link CsrfToken} created by the\n\t * {@link ServerCsrfTokenRepository}\n\t * @return the token value resolved from the request\n\t */\n\tMono<String> resolveCsrfTokenValue(ServerWebExchange exchange, CsrfToken csrfToken);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/WebSessionServerCsrfTokenRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\n/**\n * A {@link ServerCsrfTokenRepository} that stores the {@link CsrfToken} in the\n * {@link WebSession}.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebSessionServerCsrfTokenRepository implements ServerCsrfTokenRepository {\n\n\tprivate static final String DEFAULT_CSRF_PARAMETER_NAME = \"_csrf\";\n\n\tprivate static final String DEFAULT_CSRF_HEADER_NAME = \"X-CSRF-TOKEN\";\n\n\tprivate static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = WebSessionServerCsrfTokenRepository.class.getName()\n\t\t.concat(\".CSRF_TOKEN\");\n\n\tprivate String parameterName = DEFAULT_CSRF_PARAMETER_NAME;\n\n\tprivate String headerName = DEFAULT_CSRF_HEADER_NAME;\n\n\tprivate String sessionAttributeName = DEFAULT_CSRF_TOKEN_ATTR_NAME;\n\n\t@Override\n\tpublic Mono<CsrfToken> generateToken(ServerWebExchange exchange) {\n\t\treturn Mono.fromCallable(() -> createCsrfToken()).subscribeOn(Schedulers.boundedElastic());\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveToken(ServerWebExchange exchange, @Nullable CsrfToken token) {\n\t\treturn exchange.getSession()\n\t\t\t.doOnNext((session) -> putToken(session.getAttributes(), token))\n\t\t\t.flatMap((session) -> session.changeSessionId());\n\t}\n\n\tprivate void putToken(Map<String, Object> attributes, @Nullable CsrfToken token) {\n\t\tif (token == null) {\n\t\t\tattributes.remove(this.sessionAttributeName);\n\t\t}\n\t\telse {\n\t\t\tattributes.put(this.sessionAttributeName, token);\n\t\t}\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\") // https://github.com/uber/NullAway/issues/1290\n\tpublic Mono<CsrfToken> loadToken(ServerWebExchange exchange) {\n\t\treturn exchange.getSession()\n\t\t\t.filter((session) -> session.getAttributes().containsKey(this.sessionAttributeName))\n\t\t\t.mapNotNull((session) -> session.getAttribute(this.sessionAttributeName));\n\t}\n\n\t/**\n\t * Sets the {@link ServerWebExchange} parameter name that the {@link CsrfToken} is\n\t * expected to appear on\n\t * @param parameterName the new parameter name to use\n\t */\n\tpublic void setParameterName(String parameterName) {\n\t\tAssert.hasLength(parameterName, \"parameterName cannot be null or empty\");\n\t\tthis.parameterName = parameterName;\n\t}\n\n\t/**\n\t * Sets the header name that the {@link CsrfToken} is expected to appear on and the\n\t * header that the response will contain the {@link CsrfToken}.\n\t * @param headerName the new header name to use\n\t */\n\tpublic void setHeaderName(String headerName) {\n\t\tAssert.hasLength(headerName, \"headerName cannot be null or empty\");\n\t\tthis.headerName = headerName;\n\t}\n\n\t/**\n\t * Sets the {@link WebSession} attribute name that the {@link CsrfToken} is stored in\n\t * @param sessionAttributeName the new attribute name to use\n\t */\n\tpublic void setSessionAttributeName(String sessionAttributeName) {\n\t\tAssert.hasLength(sessionAttributeName, \"sessionAttributeName cannot be null or empty\");\n\t\tthis.sessionAttributeName = sessionAttributeName;\n\t}\n\n\tprivate CsrfToken createCsrfToken() {\n\t\treturn new DefaultCsrfToken(this.headerName, this.parameterName, createNewToken());\n\t}\n\n\tprivate String createNewToken() {\n\t\treturn UUID.randomUUID().toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.security.SecureRandom;\nimport java.util.Base64;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of the {@link ServerCsrfTokenRequestAttributeHandler} and\n * {@link ServerCsrfTokenRequestResolver} interfaces that is capable of masking the value\n * of the {@link CsrfToken} on each request and resolving the raw token value from the\n * masked value as either a form data value or header of the request.\n *\n * @author Steve Riesenberg\n * @author Yoobin Yoon\n * @since 5.8\n */\npublic final class XorServerCsrfTokenRequestAttributeHandler extends ServerCsrfTokenRequestAttributeHandler {\n\n\tprivate static final Log logger = LogFactory.getLog(XorServerCsrfTokenRequestAttributeHandler.class);\n\n\tprivate SecureRandom secureRandom = new SecureRandom();\n\n\t/**\n\t * Specifies the {@code SecureRandom} used to generate random bytes that are used to\n\t * mask the value of the {@link CsrfToken} on each request.\n\t * @param secureRandom the {@code SecureRandom} to use to generate random bytes\n\t */\n\tpublic void setSecureRandom(SecureRandom secureRandom) {\n\t\tAssert.notNull(secureRandom, \"secureRandom cannot be null\");\n\t\tthis.secureRandom = secureRandom;\n\t}\n\n\t@Override\n\tpublic void handle(ServerWebExchange exchange, Mono<CsrfToken> csrfToken) {\n\t\tAssert.notNull(exchange, \"exchange cannot be null\");\n\t\tAssert.notNull(csrfToken, \"csrfToken cannot be null\");\n\t\tMono<CsrfToken> updatedCsrfToken = csrfToken\n\t\t\t.map((token) -> new DefaultCsrfToken(token.getHeaderName(), token.getParameterName(),\n\t\t\t\t\tcreateXoredCsrfToken(this.secureRandom, token.getToken())))\n\t\t\t.cast(CsrfToken.class)\n\t\t\t.cache();\n\t\tsuper.handle(exchange, updatedCsrfToken);\n\t}\n\n\t@Override\n\tpublic Mono<String> resolveCsrfTokenValue(ServerWebExchange exchange, CsrfToken csrfToken) {\n\t\treturn super.resolveCsrfTokenValue(exchange, csrfToken)\n\t\t\t.flatMap((actualToken) -> Mono.justOrEmpty(getTokenValue(actualToken, csrfToken.getToken())));\n\t}\n\n\tprivate static @Nullable String getTokenValue(String actualToken, String token) {\n\t\tbyte[] actualBytes;\n\t\ttry {\n\t\t\tactualBytes = Base64.getUrlDecoder().decode(actualToken);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.trace(LogMessage.format(\"Not returning the CSRF token since it's not Base64-encoded\"), ex);\n\t\t\treturn null;\n\t\t}\n\n\t\tbyte[] tokenBytes = Utf8.encode(token);\n\t\tint tokenSize = tokenBytes.length;\n\t\tif (actualBytes.length != tokenSize * 2) {\n\t\t\tlogger.trace(LogMessage.format(\n\t\t\t\t\t\"Not returning the CSRF token since its Base64-decoded length (%d) is not equal to (%d)\",\n\t\t\t\t\tactualBytes.length, tokenSize * 2));\n\t\t\treturn null;\n\t\t}\n\n\t\t// extract token and random bytes\n\t\tbyte[] xoredCsrf = new byte[tokenSize];\n\t\tbyte[] randomBytes = new byte[tokenSize];\n\n\t\tSystem.arraycopy(actualBytes, 0, randomBytes, 0, tokenSize);\n\t\tSystem.arraycopy(actualBytes, tokenSize, xoredCsrf, 0, tokenSize);\n\n\t\tbyte[] csrfBytes = xorCsrf(randomBytes, xoredCsrf);\n\t\treturn (csrfBytes != null) ? Utf8.decode(csrfBytes) : null;\n\t}\n\n\tprivate static String createXoredCsrfToken(SecureRandom secureRandom, String token) {\n\t\tbyte[] tokenBytes = Utf8.encode(token);\n\t\tbyte[] randomBytes = new byte[tokenBytes.length];\n\t\tsecureRandom.nextBytes(randomBytes);\n\n\t\tbyte[] xoredBytes = xorCsrf(randomBytes, tokenBytes);\n\t\tbyte[] combinedBytes = new byte[tokenBytes.length + randomBytes.length];\n\t\tSystem.arraycopy(randomBytes, 0, combinedBytes, 0, randomBytes.length);\n\t\tSystem.arraycopy(xoredBytes, 0, combinedBytes, randomBytes.length, xoredBytes.length);\n\n\t\treturn Base64.getUrlEncoder().encodeToString(combinedBytes);\n\t}\n\n\tprivate static byte[] xorCsrf(byte[] randomBytes, byte[] csrfBytes) {\n\t\tAssert.isTrue(randomBytes.length == csrfBytes.length, \"arrays must be equal length\");\n\t\tint len = csrfBytes.length;\n\t\tbyte[] xoredCsrf = new byte[len];\n\t\tSystem.arraycopy(csrfBytes, 0, xoredCsrf, 0, len);\n\t\tfor (int i = 0; i < len; i++) {\n\t\t\txoredCsrf[i] ^= randomBytes[i];\n\t\t}\n\t\treturn xoredCsrf;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/csrf/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive APIs for protecting against CSRF attacks.\n */\n@NullMarked\npackage org.springframework.security.web.server.csrf;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/firewall/HttpStatusExchangeRejectedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.firewall;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * A simple implementation of {@link ServerExchangeRejectedHandler} that sends an error\n * with configurable status code.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class HttpStatusExchangeRejectedHandler implements ServerExchangeRejectedHandler {\n\n\tprivate static final Log logger = LogFactory.getLog(HttpStatusExchangeRejectedHandler.class);\n\n\tprivate final HttpStatus status;\n\n\t/**\n\t * Constructs an instance which uses {@code 400} as response code.\n\t */\n\tpublic HttpStatusExchangeRejectedHandler() {\n\t\tthis(HttpStatus.BAD_REQUEST);\n\t}\n\n\t/**\n\t * Constructs an instance which uses a configurable http code as response.\n\t * @param status http status code to use\n\t */\n\tpublic HttpStatusExchangeRejectedHandler(HttpStatus status) {\n\t\tthis.status = status;\n\t}\n\n\t@Override\n\tpublic Mono<Void> handle(ServerWebExchange exchange,\n\t\t\tServerExchangeRejectedException serverExchangeRejectedException) {\n\t\treturn Mono.fromRunnable(() -> {\n\t\t\tlogger.debug(\n\t\t\t\t\tLogMessage.format(\"Rejecting request due to: %s\", serverExchangeRejectedException.getMessage()),\n\t\t\t\t\tserverExchangeRejectedException);\n\t\t\texchange.getResponse().setStatusCode(this.status);\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/firewall/ServerExchangeRejectedException.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.firewall;\n\nimport java.io.Serial;\n\n/**\n * Thrown when a {@link org.springframework.web.server.ServerWebExchange} is rejected.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class ServerExchangeRejectedException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 904984955691607748L;\n\n\tpublic ServerExchangeRejectedException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/firewall/ServerExchangeRejectedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.firewall;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Handles {@link ServerExchangeRejectedException} thrown by\n * {@link ServerWebExchangeFirewall}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface ServerExchangeRejectedHandler {\n\n\t/**\n\t * Handles an request rejected failure.\n\t * @param exchange the {@link ServerWebExchange} that was rejected\n\t * @param serverExchangeRejectedException that caused the invocation\n\t */\n\tMono<Void> handle(ServerWebExchange exchange, ServerExchangeRejectedException serverExchangeRejectedException);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/firewall/ServerWebExchangeFirewall.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.firewall;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Interface which can be used to reject potentially dangerous requests and/or wrap them\n * to control their behaviour.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface ServerWebExchangeFirewall {\n\n\t/**\n\t * An implementation of {@link StrictServerWebExchangeFirewall} that does nothing.\n\t * This is considered insecure and not recommended.\n\t */\n\tServerWebExchangeFirewall INSECURE_NOOP = (exchange) -> Mono.just(exchange);\n\n\t/**\n\t * Get a {@link ServerWebExchange} that has firewall rules applied to it.\n\t * @param exchange the {@link ServerWebExchange} to apply firewall rules to.\n\t * @return the {@link ServerWebExchange} that has firewall rules applied to it.\n\t * @throws ServerExchangeRejectedException when a rule is broken.\n\t */\n\tMono<ServerWebExchange> getFirewalledExchange(ServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/firewall/StrictServerWebExchangeFirewall.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.firewall;\n\nimport java.net.InetSocketAddress;\nimport java.net.URI;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.http.server.reactive.ServerHttpRequestDecorator;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.http.server.reactive.SslInfo;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.ServerWebExchangeDecorator;\n\n/**\n * <p>\n * A strict implementation of {@link ServerWebExchangeFirewall} that rejects any\n * suspicious requests with a {@link ServerExchangeRejectedException}.\n * </p>\n * <p>\n * The following rules are applied to the firewall:\n * </p>\n * <ul>\n * <li>Rejects HTTP methods that are not allowed. This specified to block\n * <a href=\"https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)\">HTTP Verb\n * tampering and XST attacks</a>. See {@link #setAllowedHttpMethods(Collection)}</li>\n * <li>Rejects URLs that are not normalized to avoid bypassing security constraints. There\n * is no way to disable this as it is considered extremely risky to disable this\n * constraint. A few options to allow this behavior is to normalize the request prior to\n * the firewall or using\n * {@link org.springframework.security.web.firewall.DefaultHttpFirewall} instead. Please\n * keep in mind that normalizing the request is fragile and why requests are rejected\n * rather than normalized.</li>\n * <li>Rejects URLs that contain characters that are not printable ASCII characters. There\n * is no way to disable this as it is considered extremely risky to disable this\n * constraint.</li>\n * <li>Rejects URLs that contain semicolons. See {@link #setAllowSemicolon(boolean)}</li>\n * <li>Rejects URLs that contain a URL encoded slash. See\n * {@link #setAllowUrlEncodedSlash(boolean)}</li>\n * <li>Rejects URLs that contain a backslash. See {@link #setAllowBackSlash(boolean)}</li>\n * <li>Rejects URLs that contain a null character. See {@link #setAllowNull(boolean)}</li>\n * <li>Rejects URLs that contain a URL encoded percent. See\n * {@link #setAllowUrlEncodedPercent(boolean)}</li>\n * <li>Rejects hosts that are not allowed. See {@link #setAllowedHostnames(Predicate)}\n * </li>\n * <li>Reject headers names that are not allowed. See\n * {@link #setAllowedHeaderNames(Predicate)}</li>\n * <li>Reject headers values that are not allowed. See\n * {@link #setAllowedHeaderValues(Predicate)}</li>\n * <li>Reject parameter names that are not allowed. See\n * {@link #setAllowedParameterNames(Predicate)}</li>\n * <li>Reject parameter values that are not allowed. See\n * {@link #setAllowedParameterValues(Predicate)}</li>\n * </ul>\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class StrictServerWebExchangeFirewall implements ServerWebExchangeFirewall {\n\n\t/**\n\t * Used to specify to {@link #setAllowedHttpMethods(Collection)} that any HTTP method\n\t * should be allowed.\n\t */\n\tprivate static final Set<HttpMethod> ALLOW_ANY_HTTP_METHOD = Collections.emptySet();\n\n\tprivate static final String ENCODED_PERCENT = \"%25\";\n\n\tprivate static final String PERCENT = \"%\";\n\n\tprivate static final List<String> FORBIDDEN_ENCODED_PERIOD = Collections\n\t\t.unmodifiableList(Arrays.asList(\"%2e\", \"%2E\"));\n\n\tprivate static final List<String> FORBIDDEN_SEMICOLON = Collections\n\t\t.unmodifiableList(Arrays.asList(\";\", \"%3b\", \"%3B\"));\n\n\tprivate static final List<String> FORBIDDEN_FORWARDSLASH = Collections\n\t\t.unmodifiableList(Arrays.asList(\"%2f\", \"%2F\"));\n\n\tprivate static final List<String> FORBIDDEN_DOUBLE_FORWARDSLASH = Collections\n\t\t.unmodifiableList(Arrays.asList(\"//\", \"%2f%2f\", \"%2f%2F\", \"%2F%2f\", \"%2F%2F\"));\n\n\tprivate static final List<String> FORBIDDEN_BACKSLASH = Collections\n\t\t.unmodifiableList(Arrays.asList(\"\\\\\", \"%5c\", \"%5C\"));\n\n\tprivate static final List<String> FORBIDDEN_NULL = Collections.unmodifiableList(Arrays.asList(\"\\0\", \"%00\"));\n\n\tprivate static final List<String> FORBIDDEN_LF = Collections.unmodifiableList(Arrays.asList(\"\\n\", \"%0a\", \"%0A\"));\n\n\tprivate static final List<String> FORBIDDEN_CR = Collections.unmodifiableList(Arrays.asList(\"\\r\", \"%0d\", \"%0D\"));\n\n\tprivate static final List<String> FORBIDDEN_LINE_SEPARATOR = Collections.unmodifiableList(Arrays.asList(\"\\u2028\"));\n\n\tprivate static final List<String> FORBIDDEN_PARAGRAPH_SEPARATOR = Collections\n\t\t.unmodifiableList(Arrays.asList(\"\\u2029\"));\n\n\tprivate Set<String> encodedUrlBlocklist = new HashSet<>();\n\n\tprivate Set<String> decodedUrlBlocklist = new HashSet<>();\n\n\tprivate Set<HttpMethod> allowedHttpMethods = createDefaultAllowedHttpMethods();\n\n\tprivate Predicate<String> allowedHostnames = (hostname) -> true;\n\n\tprivate static final Pattern ASSIGNED_AND_NOT_ISO_CONTROL_PATTERN = Pattern\n\t\t.compile(\"[\\\\p{IsAssigned}&&[^\\\\p{IsControl}]]*\");\n\n\tprivate static final Predicate<String> ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE = (s) -> s == null\n\t\t\t|| ASSIGNED_AND_NOT_ISO_CONTROL_PATTERN.matcher(s).matches();\n\n\tprivate static final Pattern HEADER_VALUE_PATTERN = Pattern.compile(\"[\\\\p{IsAssigned}&&[[^\\\\p{IsControl}]||\\\\t]]*\");\n\n\tprivate static final Predicate<String> HEADER_VALUE_PREDICATE = (s) -> s == null\n\t\t\t|| HEADER_VALUE_PATTERN.matcher(s).matches();\n\n\tprivate Predicate<String> allowedHeaderNames = ALLOWED_HEADER_NAMES;\n\n\tpublic static final Predicate<String> ALLOWED_HEADER_NAMES = ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE;\n\n\tprivate Predicate<String> allowedHeaderValues = ALLOWED_HEADER_VALUES;\n\n\tpublic static final Predicate<String> ALLOWED_HEADER_VALUES = HEADER_VALUE_PREDICATE;\n\n\tprivate Predicate<String> allowedParameterNames = ALLOWED_PARAMETER_NAMES;\n\n\tpublic static final Predicate<String> ALLOWED_PARAMETER_NAMES = ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE;\n\n\tprivate Predicate<String> allowedParameterValues = ALLOWED_PARAMETER_VALUES;\n\n\tpublic static final Predicate<String> ALLOWED_PARAMETER_VALUES = (value) -> true;\n\n\tpublic StrictServerWebExchangeFirewall() {\n\t\turlBlocklistsAddAll(FORBIDDEN_SEMICOLON);\n\t\turlBlocklistsAddAll(FORBIDDEN_FORWARDSLASH);\n\t\turlBlocklistsAddAll(FORBIDDEN_DOUBLE_FORWARDSLASH);\n\t\turlBlocklistsAddAll(FORBIDDEN_BACKSLASH);\n\t\turlBlocklistsAddAll(FORBIDDEN_NULL);\n\t\turlBlocklistsAddAll(FORBIDDEN_LF);\n\t\turlBlocklistsAddAll(FORBIDDEN_CR);\n\n\t\tthis.encodedUrlBlocklist.add(ENCODED_PERCENT);\n\t\tthis.encodedUrlBlocklist.addAll(FORBIDDEN_ENCODED_PERIOD);\n\t\tthis.decodedUrlBlocklist.add(PERCENT);\n\t\tthis.decodedUrlBlocklist.addAll(FORBIDDEN_LINE_SEPARATOR);\n\t\tthis.decodedUrlBlocklist.addAll(FORBIDDEN_PARAGRAPH_SEPARATOR);\n\t}\n\n\tpublic Set<String> getEncodedUrlBlocklist() {\n\t\treturn this.encodedUrlBlocklist;\n\t}\n\n\tpublic Set<String> getDecodedUrlBlocklist() {\n\t\treturn this.decodedUrlBlocklist;\n\t}\n\n\t@Override\n\tpublic Mono<ServerWebExchange> getFirewalledExchange(ServerWebExchange exchange) {\n\t\treturn Mono.fromCallable(() -> {\n\t\t\tServerHttpRequest request = exchange.getRequest();\n\t\t\trejectForbiddenHttpMethod(request);\n\t\t\trejectedBlocklistedUrls(request);\n\t\t\trejectedUntrustedHosts(request);\n\t\t\tif (!isNormalized(request)) {\n\t\t\t\tthrow new ServerExchangeRejectedException(\n\t\t\t\t\t\t\"The request was rejected because the URL was not normalized\");\n\t\t\t}\n\n\t\t\texchange.getResponse().beforeCommit(() -> Mono.fromRunnable(() -> {\n\t\t\t\tServerHttpResponse response = exchange.getResponse();\n\t\t\t\tHttpHeaders headers = response.getHeaders();\n\t\t\t\theaders.forEach((headerName, headerValues) -> {\n\t\t\t\t\tfor (String headerValue : headerValues) {\n\t\t\t\t\t\tvalidateCrlf(headerName, headerValue);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}));\n\t\t\treturn new StrictFirewallServerWebExchange(exchange);\n\t\t});\n\t}\n\n\tprivate static void validateCrlf(String name, String value) {\n\t\tAssert.isTrue(!hasCrlf(name) && !hasCrlf(value), () -> \"Invalid characters (CR/LF) in header \" + name);\n\t}\n\n\tprivate static boolean hasCrlf(String value) {\n\t\treturn value != null && (value.indexOf('\\n') != -1 || value.indexOf('\\r') != -1);\n\t}\n\n\t/**\n\t * Sets if any HTTP method is allowed. If this set to true, then no validation on the\n\t * HTTP method will be performed. This can open the application up to\n\t * <a href=\"https://www.owasp.org/index.php/Test_HTTP_Methods_(OTG-CONFIG-006)\"> HTTP\n\t * Verb tampering and XST attacks</a>\n\t * @param unsafeAllowAnyHttpMethod if true, disables HTTP method validation, else\n\t * resets back to the defaults. Default is false.\n\t * @since 5.1\n\t * @see #setAllowedHttpMethods(Collection)\n\t */\n\tpublic void setUnsafeAllowAnyHttpMethod(boolean unsafeAllowAnyHttpMethod) {\n\t\tthis.allowedHttpMethods = unsafeAllowAnyHttpMethod ? ALLOW_ANY_HTTP_METHOD : createDefaultAllowedHttpMethods();\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which HTTP methods should be allowed. The default is to allow \"DELETE\",\n\t * \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", and \"PUT\".\n\t * </p>\n\t * @param allowedHttpMethods the case-sensitive collection of HTTP methods that are\n\t * allowed.\n\t * @since 5.1\n\t * @see #setUnsafeAllowAnyHttpMethod(boolean)\n\t */\n\tpublic void setAllowedHttpMethods(Collection<HttpMethod> allowedHttpMethods) {\n\t\tAssert.notNull(allowedHttpMethods, \"allowedHttpMethods cannot be null\");\n\t\tthis.allowedHttpMethods = (allowedHttpMethods != ALLOW_ANY_HTTP_METHOD) ? new HashSet<>(allowedHttpMethods)\n\t\t\t\t: ALLOW_ANY_HTTP_METHOD;\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if semicolon is allowed in the URL (i.e. matrix variables). The default\n\t * is to disable this behavior because it is a common way of attempting to perform\n\t * <a href=\"https://www.owasp.org/index.php/Reflected_File_Download\">Reflected File\n\t * Download Attacks</a>. It is also the source of many exploits which bypass URL based\n\t * security.\n\t * </p>\n\t * <p>\n\t * For example, the following CVEs are a subset of the issues related to ambiguities\n\t * in the Servlet Specification on how to treat semicolons that led to CVEs:\n\t * </p>\n\t * <ul>\n\t * <li><a href=\"https://pivotal.io/security/cve-2016-5007\">cve-2016-5007</a></li>\n\t * <li><a href=\"https://pivotal.io/security/cve-2016-9879\">cve-2016-9879</a></li>\n\t * <li><a href=\"https://pivotal.io/security/cve-2018-1199\">cve-2018-1199</a></li>\n\t * </ul>\n\t *\n\t * <p>\n\t * If you are wanting to allow semicolons, please reconsider as it is a very common\n\t * source of security bypasses. A few common reasons users want semicolons and\n\t * alternatives are listed below:\n\t * </p>\n\t * <ul>\n\t * <li>Including the JSESSIONID in the path - You should not include session id (or\n\t * any sensitive information) in a URL as it can lead to leaking. Instead use Cookies.\n\t * </li>\n\t * <li>Matrix Variables - Users wanting to leverage Matrix Variables should consider\n\t * using HTTP parameters instead.</li>\n\t * </ul>\n\t * @param allowSemicolon should semicolons be allowed in the URL. Default is false\n\t */\n\tpublic void setAllowSemicolon(boolean allowSemicolon) {\n\t\tif (allowSemicolon) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_SEMICOLON);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_SEMICOLON);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a slash \"/\" that is URL encoded \"%2F\" should be allowed in the path\n\t * or not. The default is to not allow this behavior because it is a common way to\n\t * bypass URL based security.\n\t * </p>\n\t * <p>\n\t * For example, due to ambiguities in the servlet specification, the value is not\n\t * parsed consistently which results in different values in {@code HttpServletRequest}\n\t * path related values which allow bypassing certain security constraints.\n\t * </p>\n\t * @param allowUrlEncodedSlash should a slash \"/\" that is URL encoded \"%2F\" be allowed\n\t * in the path or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedSlash(boolean allowUrlEncodedSlash) {\n\t\tif (allowUrlEncodedSlash) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_FORWARDSLASH);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_FORWARDSLASH);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if double slash \"//\" that is URL encoded \"%2F%2F\" should be allowed in\n\t * the path or not. The default is to not allow.\n\t * </p>\n\t * @param allowUrlEncodedDoubleSlash should a slash \"//\" that is URL encoded \"%2F%2F\"\n\t * be allowed in the path or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedDoubleSlash(boolean allowUrlEncodedDoubleSlash) {\n\t\tif (allowUrlEncodedDoubleSlash) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_DOUBLE_FORWARDSLASH);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_DOUBLE_FORWARDSLASH);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a period \".\" that is URL encoded \"%2E\" should be allowed in the path\n\t * or not. The default is to not allow this behavior because it is a frequent source\n\t * of security exploits.\n\t * </p>\n\t * <p>\n\t * For example, due to ambiguities in the servlet specification a URL encoded period\n\t * might lead to bypassing security constraints through a directory traversal attack.\n\t * This is because the path is not parsed consistently which results in different\n\t * values in {@code HttpServletRequest} path related values which allow bypassing\n\t * certain security constraints.\n\t * </p>\n\t * @param allowUrlEncodedPeriod should a period \".\" that is URL encoded \"%2E\" be\n\t * allowed in the path or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedPeriod(boolean allowUrlEncodedPeriod) {\n\t\tif (allowUrlEncodedPeriod) {\n\t\t\tthis.encodedUrlBlocklist.removeAll(FORBIDDEN_ENCODED_PERIOD);\n\t\t}\n\t\telse {\n\t\t\tthis.encodedUrlBlocklist.addAll(FORBIDDEN_ENCODED_PERIOD);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a backslash \"\\\" or a URL encoded backslash \"%5C\" should be allowed in\n\t * the path or not. The default is not to allow this behavior because it is a frequent\n\t * source of security exploits.\n\t * </p>\n\t * <p>\n\t * For example, due to ambiguities in the servlet specification a URL encoded period\n\t * might lead to bypassing security constraints through a directory traversal attack.\n\t * This is because the path is not parsed consistently which results in different\n\t * values in {@code HttpServletRequest} path related values which allow bypassing\n\t * certain security constraints.\n\t * </p>\n\t * @param allowBackSlash a backslash \"\\\" or a URL encoded backslash \"%5C\" be allowed\n\t * in the path or not. Default is false\n\t */\n\tpublic void setAllowBackSlash(boolean allowBackSlash) {\n\t\tif (allowBackSlash) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_BACKSLASH);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_BACKSLASH);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a null \"\\0\" or a URL encoded nul \"%00\" should be allowed in the path\n\t * or not. The default is not to allow this behavior because it is a frequent source\n\t * of security exploits.\n\t * </p>\n\t * @param allowNull a null \"\\0\" or a URL encoded null \"%00\" be allowed in the path or\n\t * not. Default is false\n\t * @since 5.4\n\t */\n\tpublic void setAllowNull(boolean allowNull) {\n\t\tif (allowNull) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_NULL);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_NULL);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines if a percent \"%\" that is URL encoded \"%25\" should be allowed in the path\n\t * or not. The default is not to allow this behavior because it is a frequent source\n\t * of security exploits.\n\t * </p>\n\t * <p>\n\t * For example, this can lead to exploits that involve double URL encoding that lead\n\t * to bypassing security constraints.\n\t * </p>\n\t * @param allowUrlEncodedPercent if a percent \"%\" that is URL encoded \"%25\" should be\n\t * allowed in the path or not. Default is false\n\t */\n\tpublic void setAllowUrlEncodedPercent(boolean allowUrlEncodedPercent) {\n\t\tif (allowUrlEncodedPercent) {\n\t\t\tthis.encodedUrlBlocklist.remove(ENCODED_PERCENT);\n\t\t\tthis.decodedUrlBlocklist.remove(PERCENT);\n\t\t}\n\t\telse {\n\t\t\tthis.encodedUrlBlocklist.add(ENCODED_PERCENT);\n\t\t\tthis.decodedUrlBlocklist.add(PERCENT);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if a URL encoded Carriage Return is allowed in the path or not. The\n\t * default is not to allow this behavior because it is a frequent source of security\n\t * exploits.\n\t * @param allowUrlEncodedCarriageReturn if URL encoded Carriage Return is allowed in\n\t * the URL or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedCarriageReturn(boolean allowUrlEncodedCarriageReturn) {\n\t\tif (allowUrlEncodedCarriageReturn) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_CR);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_CR);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if a URL encoded Line Feed is allowed in the path or not. The default is\n\t * not to allow this behavior because it is a frequent source of security exploits.\n\t * @param allowUrlEncodedLineFeed if URL encoded Line Feed is allowed in the URL or\n\t * not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedLineFeed(boolean allowUrlEncodedLineFeed) {\n\t\tif (allowUrlEncodedLineFeed) {\n\t\t\turlBlocklistsRemoveAll(FORBIDDEN_LF);\n\t\t}\n\t\telse {\n\t\t\turlBlocklistsAddAll(FORBIDDEN_LF);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if a URL encoded paragraph separator is allowed in the path or not. The\n\t * default is not to allow this behavior because it is a frequent source of security\n\t * exploits.\n\t * @param allowUrlEncodedParagraphSeparator if URL encoded paragraph separator is\n\t * allowed in the URL or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedParagraphSeparator(boolean allowUrlEncodedParagraphSeparator) {\n\t\tif (allowUrlEncodedParagraphSeparator) {\n\t\t\tthis.decodedUrlBlocklist.removeAll(FORBIDDEN_PARAGRAPH_SEPARATOR);\n\t\t}\n\t\telse {\n\t\t\tthis.decodedUrlBlocklist.addAll(FORBIDDEN_PARAGRAPH_SEPARATOR);\n\t\t}\n\t}\n\n\t/**\n\t * Determines if a URL encoded line separator is allowed in the path or not. The\n\t * default is not to allow this behavior because it is a frequent source of security\n\t * exploits.\n\t * @param allowUrlEncodedLineSeparator if URL encoded line separator is allowed in the\n\t * URL or not. Default is false.\n\t */\n\tpublic void setAllowUrlEncodedLineSeparator(boolean allowUrlEncodedLineSeparator) {\n\t\tif (allowUrlEncodedLineSeparator) {\n\t\t\tthis.decodedUrlBlocklist.removeAll(FORBIDDEN_LINE_SEPARATOR);\n\t\t}\n\t\telse {\n\t\t\tthis.decodedUrlBlocklist.addAll(FORBIDDEN_LINE_SEPARATOR);\n\t\t}\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which header names should be allowed. The default is to reject header\n\t * names that contain ISO control characters and characters that are not defined.\n\t * </p>\n\t * @param allowedHeaderNames the predicate for testing header names\n\t * @since 5.4\n\t * @see Character#isISOControl(int)\n\t * @see Character#isDefined(int)\n\t */\n\tpublic void setAllowedHeaderNames(Predicate<String> allowedHeaderNames) {\n\t\tAssert.notNull(allowedHeaderNames, \"allowedHeaderNames cannot be null\");\n\t\tthis.allowedHeaderNames = allowedHeaderNames;\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which header values should be allowed. The default is to reject header\n\t * values that contain ISO control characters and characters that are not defined.\n\t * </p>\n\t * @param allowedHeaderValues the predicate for testing hostnames\n\t * @since 5.4\n\t * @see Character#isISOControl(int)\n\t * @see Character#isDefined(int)\n\t */\n\tpublic void setAllowedHeaderValues(Predicate<String> allowedHeaderValues) {\n\t\tAssert.notNull(allowedHeaderValues, \"allowedHeaderValues cannot be null\");\n\t\tthis.allowedHeaderValues = allowedHeaderValues;\n\t}\n\n\t/**\n\t * Determines which parameter names should be allowed. The default is to reject header\n\t * names that contain ISO control characters and characters that are not defined.\n\t * @param allowedParameterNames the predicate for testing parameter names\n\t * @since 5.4\n\t * @see Character#isISOControl(int)\n\t * @see Character#isDefined(int)\n\t */\n\tpublic void setAllowedParameterNames(Predicate<String> allowedParameterNames) {\n\t\tAssert.notNull(allowedParameterNames, \"allowedParameterNames cannot be null\");\n\t\tthis.allowedParameterNames = allowedParameterNames;\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which parameter values should be allowed. The default is to allow any\n\t * parameter value.\n\t * </p>\n\t * @param allowedParameterValues the predicate for testing parameter values\n\t * @since 5.4\n\t */\n\tpublic void setAllowedParameterValues(Predicate<String> allowedParameterValues) {\n\t\tAssert.notNull(allowedParameterValues, \"allowedParameterValues cannot be null\");\n\t\tthis.allowedParameterValues = allowedParameterValues;\n\t}\n\n\t/**\n\t * <p>\n\t * Determines which hostnames should be allowed. The default is to allow any hostname.\n\t * </p>\n\t * @param allowedHostnames the predicate for testing hostnames\n\t * @since 5.2\n\t */\n\tpublic void setAllowedHostnames(Predicate<String> allowedHostnames) {\n\t\tAssert.notNull(allowedHostnames, \"allowedHostnames cannot be null\");\n\t\tthis.allowedHostnames = allowedHostnames;\n\t}\n\n\tprivate void urlBlocklistsAddAll(Collection<String> values) {\n\t\tthis.encodedUrlBlocklist.addAll(values);\n\t\tthis.decodedUrlBlocklist.addAll(values);\n\t}\n\n\tprivate void urlBlocklistsRemoveAll(Collection<String> values) {\n\t\tthis.encodedUrlBlocklist.removeAll(values);\n\t\tthis.decodedUrlBlocklist.removeAll(values);\n\t}\n\n\tprivate void rejectNonPrintableAsciiCharactersInFieldName(String toCheck, String propertyName) {\n\t\tif (!containsOnlyPrintableAsciiCharacters(toCheck)) {\n\t\t\tthrow new ServerExchangeRejectedException(String\n\t\t\t\t.format(\"The %s was rejected because it can only contain printable ASCII characters.\", propertyName));\n\t\t}\n\t}\n\n\tprivate void rejectForbiddenHttpMethod(ServerHttpRequest request) {\n\t\tif (this.allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {\n\t\t\treturn;\n\t\t}\n\t\tif (!this.allowedHttpMethods.contains(request.getMethod())) {\n\t\t\tthrow new ServerExchangeRejectedException(\n\t\t\t\t\t\"The request was rejected because the HTTP method \\\"\" + request.getMethod()\n\t\t\t\t\t\t\t+ \"\\\" was not included within the list of allowed HTTP methods \" + this.allowedHttpMethods);\n\t\t}\n\t}\n\n\tprivate void rejectedBlocklistedUrls(ServerHttpRequest request) {\n\t\tfor (String forbidden : this.encodedUrlBlocklist) {\n\t\t\tif (encodedUrlContains(request, forbidden)) {\n\t\t\t\tthrow new ServerExchangeRejectedException(\n\t\t\t\t\t\t\"The request was rejected because the URL contained a potentially malicious String \\\"\"\n\t\t\t\t\t\t\t\t+ forbidden + \"\\\"\");\n\t\t\t}\n\t\t}\n\t\tfor (String forbidden : this.decodedUrlBlocklist) {\n\t\t\tif (decodedUrlContains(request, forbidden)) {\n\t\t\t\tthrow new ServerExchangeRejectedException(\n\t\t\t\t\t\t\"The request was rejected because the URL contained a potentially malicious String \\\"\"\n\t\t\t\t\t\t\t\t+ forbidden + \"\\\"\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void rejectedUntrustedHosts(ServerHttpRequest request) {\n\t\tString hostName = request.getURI().getHost();\n\t\tif (hostName != null && !this.allowedHostnames.test(hostName)) {\n\t\t\tthrow new ServerExchangeRejectedException(\n\t\t\t\t\t\"The request was rejected because the domain \" + hostName + \" is untrusted.\");\n\t\t}\n\t}\n\n\tprivate static Set<HttpMethod> createDefaultAllowedHttpMethods() {\n\t\tSet<HttpMethod> result = new HashSet<>();\n\t\tresult.add(HttpMethod.DELETE);\n\t\tresult.add(HttpMethod.GET);\n\t\tresult.add(HttpMethod.HEAD);\n\t\tresult.add(HttpMethod.OPTIONS);\n\t\tresult.add(HttpMethod.PATCH);\n\t\tresult.add(HttpMethod.POST);\n\t\tresult.add(HttpMethod.PUT);\n\t\treturn result;\n\t}\n\n\tprivate boolean isNormalized(ServerHttpRequest request) {\n\t\tif (!isNormalized(request.getPath().value())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!isNormalized(request.getURI().getRawPath())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!isNormalized(request.getURI().getPath())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void validateAllowedHeaderName(String headerNames) {\n\t\tif (!StrictServerWebExchangeFirewall.this.allowedHeaderNames.test(headerNames)) {\n\t\t\tthrow new ServerExchangeRejectedException(\n\t\t\t\t\t\"The request was rejected because the header name \\\"\" + headerNames + \"\\\" is not allowed.\");\n\t\t}\n\t}\n\n\tprivate void validateAllowedHeaderValue(Object key, @Nullable String value) {\n\t\tif (!StrictServerWebExchangeFirewall.this.allowedHeaderValues.test(value)) {\n\t\t\tthrow new ServerExchangeRejectedException(\"The request was rejected because the header: \\\"\" + key\n\t\t\t\t\t+ \" \\\" has a value \\\"\" + value + \"\\\" that is not allowed.\");\n\t\t}\n\t}\n\n\tprivate void validateAllowedParameterName(String name) {\n\t\tif (!StrictServerWebExchangeFirewall.this.allowedParameterNames.test(name)) {\n\t\t\tthrow new ServerExchangeRejectedException(\n\t\t\t\t\t\"The request was rejected because the parameter name \\\"\" + name + \"\\\" is not allowed.\");\n\t\t}\n\t}\n\n\tprivate void validateAllowedParameterValue(String name, String value) {\n\t\tif (!StrictServerWebExchangeFirewall.this.allowedParameterValues.test(value)) {\n\t\t\tthrow new ServerExchangeRejectedException(\"The request was rejected because the parameter: \\\"\" + name\n\t\t\t\t\t+ \" \\\" has a value \\\"\" + value + \"\\\" that is not allowed.\");\n\t\t}\n\t}\n\n\tprivate static boolean encodedUrlContains(ServerHttpRequest request, String value) {\n\t\tif (valueContains(request.getPath().value(), value)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn valueContains(request.getURI().getRawPath(), value);\n\t}\n\n\tprivate static boolean decodedUrlContains(ServerHttpRequest request, String value) {\n\t\treturn valueContains(request.getURI().getPath(), value);\n\t}\n\n\tprivate static boolean containsOnlyPrintableAsciiCharacters(String uri) {\n\t\tif (uri == null) {\n\t\t\treturn true;\n\t\t}\n\t\tint length = uri.length();\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tchar ch = uri.charAt(i);\n\t\t\tif (ch < '\\u0020' || ch > '\\u007e') {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean valueContains(String value, String contains) {\n\t\treturn value != null && value.contains(contains);\n\t}\n\n\t/**\n\t * Checks whether a path is normalized (doesn't contain path traversal sequences like\n\t * \"./\", \"/../\" or \"/.\")\n\t * @param path the path to test\n\t * @return true if the path doesn't contain any path-traversal character sequences.\n\t */\n\tprivate static boolean isNormalized(String path) {\n\t\tif (path == null) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (int i = path.length(); i > 0;) {\n\t\t\tint slashIndex = path.lastIndexOf('/', i - 1);\n\t\t\tint gap = i - slashIndex;\n\t\t\tif (gap == 2 && path.charAt(slashIndex + 1) == '.') {\n\t\t\t\treturn false; // \".\", \"/./\" or \"/.\"\n\t\t\t}\n\t\t\tif (gap == 3 && path.charAt(slashIndex + 1) == '.' && path.charAt(slashIndex + 2) == '.') {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ti = slashIndex;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate final class StrictFirewallServerWebExchange extends ServerWebExchangeDecorator {\n\n\t\tprivate StrictFirewallServerWebExchange(ServerWebExchange delegate) {\n\t\t\tsuper(delegate);\n\t\t}\n\n\t\t@Override\n\t\tpublic ServerHttpRequest getRequest() {\n\t\t\treturn new StrictFirewallHttpRequest(super.getRequest());\n\t\t}\n\n\t\tprivate final class StrictFirewallHttpRequest extends ServerHttpRequestDecorator {\n\n\t\t\tprivate StrictFirewallHttpRequest(ServerHttpRequest delegate) {\n\t\t\t\tsuper(delegate);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic HttpHeaders getHeaders() {\n\t\t\t\treturn new StrictFirewallHttpHeaders(super.getHeaders());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic MultiValueMap<String, String> getQueryParams() {\n\t\t\t\tMultiValueMap<String, String> queryParams = super.getQueryParams();\n\t\t\t\tfor (Map.Entry<String, List<String>> paramEntry : queryParams.entrySet()) {\n\t\t\t\t\tString paramName = paramEntry.getKey();\n\t\t\t\t\tvalidateAllowedParameterName(paramName);\n\t\t\t\t\tfor (String paramValue : paramEntry.getValue()) {\n\t\t\t\t\t\tvalidateAllowedParameterValue(paramName, paramValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn queryParams;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Builder mutate() {\n\t\t\t\treturn new StrictFirewallBuilder(super.mutate());\n\t\t\t}\n\n\t\t\tprivate final class StrictFirewallHttpHeaders extends HttpHeaders {\n\n\t\t\t\tprivate StrictFirewallHttpHeaders(HttpHeaders delegate) {\n\t\t\t\t\tsuper(delegate);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic @Nullable String getFirst(String headerName) {\n\t\t\t\t\tvalidateAllowedHeaderName(headerName);\n\t\t\t\t\tString headerValue = super.getFirst(headerName);\n\t\t\t\t\tvalidateAllowedHeaderValue(headerName, headerValue);\n\t\t\t\t\treturn headerValue;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic @Nullable List<String> get(String headerName) {\n\t\t\t\t\tvalidateAllowedHeaderName(headerName);\n\t\t\t\t\tList<String> headerValues = super.get(headerName);\n\t\t\t\t\tif (headerValues == null) {\n\t\t\t\t\t\treturn headerValues;\n\t\t\t\t\t}\n\t\t\t\t\tfor (String headerValue : headerValues) {\n\t\t\t\t\t\tvalidateAllowedHeaderValue(headerName, headerValue);\n\t\t\t\t\t}\n\t\t\t\t\treturn headerValues;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Set<String> headerNames() {\n\t\t\t\t\tSet<String> headerNames = super.headerNames();\n\t\t\t\t\tfor (String headerName : headerNames) {\n\t\t\t\t\t\tvalidateAllowedHeaderName(headerName);\n\t\t\t\t\t}\n\t\t\t\t\treturn headerNames;\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tprivate final class StrictFirewallBuilder implements Builder {\n\n\t\t\t\tprivate final Builder delegate;\n\n\t\t\t\tprivate StrictFirewallBuilder(Builder delegate) {\n\t\t\t\t\tthis.delegate = delegate;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder method(HttpMethod httpMethod) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.method(httpMethod));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder uri(URI uri) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.uri(uri));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder path(String path) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.path(path));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder contextPath(String contextPath) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.contextPath(contextPath));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder header(String headerName, String... headerValues) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.header(headerName, headerValues));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder headers(Consumer<HttpHeaders> headersConsumer) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.headers(headersConsumer));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder sslInfo(SslInfo sslInfo) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.sslInfo(sslInfo));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder remoteAddress(InetSocketAddress remoteAddress) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.remoteAddress(remoteAddress));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Builder localAddress(InetSocketAddress localAddress) {\n\t\t\t\t\treturn new StrictFirewallBuilder(this.delegate.localAddress(localAddress));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic ServerHttpRequest build() {\n\t\t\t\t\treturn new StrictFirewallHttpRequest(this.delegate.build());\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/firewall/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive HTTP Firewall APIs.\n */\n@NullMarked\npackage org.springframework.security.web.server.firewall;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/CacheControlServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Writes cache control related headers.\n *\n * <pre>\n * Cache-Control: no-cache, no-store, max-age=0, must-revalidate\n * Pragma: no-cache\n * Expires: 0\n * </pre>\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class CacheControlServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\t/**\n\t * The value for expires value\n\t */\n\tpublic static final String EXPIRES_VALUE = \"0\";\n\n\t/**\n\t * The value for pragma value\n\t */\n\tpublic static final String PRAGMA_VALUE = \"no-cache\";\n\n\t/**\n\t * The value for cache control value\n\t */\n\tpublic static final String CACHE_CONTROL_VALUE = \"no-cache, no-store, max-age=0, must-revalidate\";\n\n\t/**\n\t * The value for the cache control value.\n\t * @deprecated Please use {@link #CACHE_CONTROL_VALUE}\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tpublic static final String CACHE_CONTRTOL_VALUE = CACHE_CONTROL_VALUE;\n\n\t/**\n\t * The delegate to write all the cache control related headers\n\t */\n\tprivate static final ServerHttpHeadersWriter CACHE_HEADERS = StaticServerHttpHeadersWriter.builder()\n\t\t.header(HttpHeaders.CACHE_CONTROL, CacheControlServerHttpHeadersWriter.CACHE_CONTROL_VALUE)\n\t\t.header(HttpHeaders.PRAGMA, CacheControlServerHttpHeadersWriter.PRAGMA_VALUE)\n\t\t.header(HttpHeaders.EXPIRES, CacheControlServerHttpHeadersWriter.EXPIRES_VALUE)\n\t\t.build();\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\tif (exchange.getResponse().getStatusCode() == HttpStatus.NOT_MODIFIED) {\n\t\t\treturn Mono.empty();\n\t\t}\n\t\treturn CACHE_HEADERS.writeHttpHeaders(exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/ClearSiteDataServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * <p>\n * Writes the {@code Clear-Site-Data} response header when the request is secure.\n * </p>\n *\n * <p>\n * For further details please consult <a href=\"https://www.w3.org/TR/clear-site-data/\">W3C\n * Documentation</a>.\n * </p>\n *\n * @author MD Sayem Ahmed\n * @since 5.2\n */\npublic final class ClearSiteDataServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String CLEAR_SITE_DATA_HEADER = \"Clear-Site-Data\";\n\n\tprivate final StaticServerHttpHeadersWriter headerWriterDelegate;\n\n\t/**\n\t * <p>\n\t * Constructs a new instance using the given directives.\n\t * </p>\n\t * @param directives directives that will be written as the header value\n\t * @throws IllegalArgumentException if the argument is null or empty\n\t */\n\tpublic ClearSiteDataServerHttpHeadersWriter(Directive... directives) {\n\t\tAssert.notEmpty(directives, \"directives cannot be empty or null\");\n\t\tthis.headerWriterDelegate = StaticServerHttpHeadersWriter.builder()\n\t\t\t.header(CLEAR_SITE_DATA_HEADER, transformToHeaderValue(directives))\n\t\t\t.build();\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\tif (isSecure(exchange)) {\n\t\t\treturn this.headerWriterDelegate.writeHttpHeaders(exchange);\n\t\t}\n\t\treturn Mono.empty();\n\t}\n\n\t/**\n\t * <p>\n\t * Represents the directive values expected by the\n\t * {@link ClearSiteDataServerHttpHeadersWriter}\n\t * </p>\n\t * .\n\t */\n\tpublic enum Directive {\n\n\t\tCACHE(\"cache\"),\n\n\t\tCOOKIES(\"cookies\"),\n\n\t\tSTORAGE(\"storage\"),\n\n\t\tEXECUTION_CONTEXTS(\"executionContexts\"),\n\n\t\tALL(\"*\");\n\n\t\tprivate final String headerValue;\n\n\t\tDirective(String headerValue) {\n\t\t\tthis.headerValue = \"\\\"\" + headerValue + \"\\\"\";\n\t\t}\n\n\t\tpublic String getHeaderValue() {\n\t\t\treturn this.headerValue;\n\t\t}\n\n\t}\n\n\tprivate String transformToHeaderValue(Directive... directives) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 0; i < directives.length - 1; i++) {\n\t\t\tsb.append(directives[i].headerValue).append(\", \");\n\t\t}\n\t\tsb.append(directives[directives.length - 1].headerValue);\n\t\treturn sb.toString();\n\t}\n\n\tprivate boolean isSecure(ServerWebExchange exchange) {\n\t\tString scheme = exchange.getRequest().getURI().getScheme();\n\t\treturn scheme != null && scheme.equalsIgnoreCase(\"https\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Combines multiple {@link ServerHttpHeadersWriter} instances into a single instance.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class CompositeServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tprivate final List<ServerHttpHeadersWriter> writers;\n\n\tpublic CompositeServerHttpHeadersWriter(ServerHttpHeadersWriter... writers) {\n\t\tthis(Arrays.asList(writers));\n\t}\n\n\tpublic CompositeServerHttpHeadersWriter(List<ServerHttpHeadersWriter> writers) {\n\t\tthis.writers = writers;\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn Flux.fromIterable(this.writers).concatMap((w) -> w.writeHttpHeaders(exchange)).then();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/ContentSecurityPolicyServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.header.StaticServerHttpHeadersWriter.Builder;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Writes the {@code Content-Security-Policy} response header with configured policy\n * directives.\n *\n * @author Vedran Pavic\n * @since 5.1\n */\npublic final class ContentSecurityPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String CONTENT_SECURITY_POLICY = \"Content-Security-Policy\";\n\n\tpublic static final String CONTENT_SECURITY_POLICY_REPORT_ONLY = \"Content-Security-Policy-Report-Only\";\n\n\tprivate @Nullable String policyDirectives;\n\n\tprivate boolean reportOnly;\n\n\tprivate @Nullable ServerHttpHeadersWriter delegate;\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();\n\t}\n\n\t/**\n\t * Set the policy directive(s) to be used in the response header.\n\t * @param policyDirectives the policy directive(s)\n\t * @throws IllegalArgumentException if policyDirectives is {@code null} or empty\n\t */\n\tpublic void setPolicyDirectives(String policyDirectives) {\n\t\tAssert.hasLength(policyDirectives, \"policyDirectives must not be null or empty\");\n\t\tthis.policyDirectives = policyDirectives;\n\t\tthis.delegate = createDelegate();\n\t}\n\n\t/**\n\t * Set whether to include the {@code Content-Security-Policy-Report-Only} header in\n\t * the response. Otherwise, defaults to the {@code Content-Security-Policy} header.\n\t * @param reportOnly whether to only report policy violations\n\t */\n\tpublic void setReportOnly(boolean reportOnly) {\n\t\tthis.reportOnly = reportOnly;\n\t\tthis.delegate = createDelegate();\n\t}\n\n\tprivate @Nullable ServerHttpHeadersWriter createDelegate() {\n\t\tif (this.policyDirectives == null) {\n\t\t\treturn null;\n\t\t}\n\t\tBuilder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(resolveHeader(this.reportOnly), this.policyDirectives);\n\t\treturn builder.build();\n\t}\n\n\tprivate static String resolveHeader(boolean reportOnly) {\n\t\treturn reportOnly ? CONTENT_SECURITY_POLICY_REPORT_ONLY : CONTENT_SECURITY_POLICY;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/ContentTypeOptionsServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Adds X-Content-Type-Options: nosniff\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class ContentTypeOptionsServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String X_CONTENT_OPTIONS = \"X-Content-Type-Options\";\n\n\tpublic static final String NOSNIFF = \"nosniff\";\n\n\t/**\n\t * The delegate to write all the cache control related headers\n\t */\n\tprivate static final ServerHttpHeadersWriter CONTENT_TYPE_HEADERS = StaticServerHttpHeadersWriter.builder()\n\t\t.header(X_CONTENT_OPTIONS, NOSNIFF)\n\t\t.build();\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn CONTENT_TYPE_HEADERS.writeHttpHeaders(exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/CrossOriginEmbedderPolicyServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Inserts Cross-Origin-Embedder-Policy headers.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @see <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy\">\n * Cross-Origin-Embedder-Policy</a>\n */\npublic final class CrossOriginEmbedderPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String EMBEDDER_POLICY = \"Cross-Origin-Embedder-Policy\";\n\n\tprivate @Nullable ServerHttpHeadersWriter delegate;\n\n\t/**\n\t * Sets the {@link CrossOriginEmbedderPolicy} value to be used in the\n\t * {@code Cross-Origin-Embedder-Policy} header\n\t * @param embedderPolicy the {@link CrossOriginEmbedderPolicy} to use\n\t */\n\tpublic void setPolicy(CrossOriginEmbedderPolicy embedderPolicy) {\n\t\tAssert.notNull(embedderPolicy, \"embedderPolicy cannot be null\");\n\t\tthis.delegate = createDelegate(embedderPolicy);\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();\n\t}\n\n\tprivate static ServerHttpHeadersWriter createDelegate(CrossOriginEmbedderPolicy embedderPolicy) {\n\t\tStaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(EMBEDDER_POLICY, embedderPolicy.getPolicy());\n\t\treturn builder.build();\n\t}\n\n\tpublic enum CrossOriginEmbedderPolicy {\n\n\t\tUNSAFE_NONE(\"unsafe-none\"),\n\n\t\tREQUIRE_CORP(\"require-corp\"),\n\n\t\tCREDENTIALLESS(\"credentialless\");\n\n\t\tprivate final String policy;\n\n\t\tCrossOriginEmbedderPolicy(String policy) {\n\t\t\tthis.policy = policy;\n\t\t}\n\n\t\tpublic String getPolicy() {\n\t\t\treturn this.policy;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/CrossOriginOpenerPolicyServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Inserts Cross-Origin-Opener-Policy header.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @see <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy\">\n * Cross-Origin-Opener-Policy</a>\n */\npublic final class CrossOriginOpenerPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String OPENER_POLICY = \"Cross-Origin-Opener-Policy\";\n\n\tprivate @Nullable ServerHttpHeadersWriter delegate;\n\n\t/**\n\t * Sets the {@link CrossOriginOpenerPolicy} value to be used in the\n\t * {@code Cross-Origin-Opener-Policy} header\n\t * @param openerPolicy the {@link CrossOriginOpenerPolicy} to use\n\t */\n\tpublic void setPolicy(CrossOriginOpenerPolicy openerPolicy) {\n\t\tAssert.notNull(openerPolicy, \"openerPolicy cannot be null\");\n\t\tthis.delegate = createDelegate(openerPolicy);\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();\n\t}\n\n\tprivate static ServerHttpHeadersWriter createDelegate(CrossOriginOpenerPolicy openerPolicy) {\n\t\tStaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(OPENER_POLICY, openerPolicy.getPolicy());\n\t\treturn builder.build();\n\t}\n\n\tpublic enum CrossOriginOpenerPolicy {\n\n\t\tUNSAFE_NONE(\"unsafe-none\"),\n\n\t\tSAME_ORIGIN_ALLOW_POPUPS(\"same-origin-allow-popups\"),\n\n\t\tSAME_ORIGIN(\"same-origin\");\n\n\t\tprivate final String policy;\n\n\t\tCrossOriginOpenerPolicy(String policy) {\n\t\t\tthis.policy = policy;\n\t\t}\n\n\t\tpublic String getPolicy() {\n\t\t\treturn this.policy;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/CrossOriginResourcePolicyServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Inserts Cross-Origin-Resource-Policy headers.\n *\n * @author Marcus Da Coregio\n * @since 5.7\n * @see <a href=\n * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy\">\n * Cross-Origin-Resource-Policy</a>\n */\npublic final class CrossOriginResourcePolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String RESOURCE_POLICY = \"Cross-Origin-Resource-Policy\";\n\n\tprivate @Nullable ServerHttpHeadersWriter delegate;\n\n\t/**\n\t * Sets the {@link CrossOriginResourcePolicy} value to be used in the\n\t * {@code Cross-Origin-Embedder-Policy} header\n\t * @param resourcePolicy the {@link CrossOriginResourcePolicy} to use\n\t */\n\tpublic void setPolicy(CrossOriginResourcePolicy resourcePolicy) {\n\t\tAssert.notNull(resourcePolicy, \"resourcePolicy cannot be null\");\n\t\tthis.delegate = createDelegate(resourcePolicy);\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();\n\t}\n\n\tprivate static ServerHttpHeadersWriter createDelegate(CrossOriginResourcePolicy resourcePolicy) {\n\t\tStaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(RESOURCE_POLICY, resourcePolicy.getPolicy());\n\t\treturn builder.build();\n\t}\n\n\tpublic enum CrossOriginResourcePolicy {\n\n\t\tSAME_SITE(\"same-site\"),\n\n\t\tSAME_ORIGIN(\"same-origin\"),\n\n\t\tCROSS_ORIGIN(\"cross-origin\");\n\n\t\tprivate final String policy;\n\n\t\tCrossOriginResourcePolicy(String policy) {\n\t\t\tthis.policy = policy;\n\t\t}\n\n\t\tpublic String getPolicy() {\n\t\t\treturn this.policy;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/FeaturePolicyServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.header.StaticServerHttpHeadersWriter.Builder;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Writes the {@code Feature-Policy} response header with configured policy directives.\n *\n * @author Vedran Pavic\n * @since 5.1\n */\npublic final class FeaturePolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String FEATURE_POLICY = \"Feature-Policy\";\n\n\tprivate @Nullable ServerHttpHeadersWriter delegate;\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();\n\t}\n\n\t/**\n\t * Set the policy directive(s) to be used in the response header.\n\t * @param policyDirectives the policy directive(s)\n\t * @throws IllegalArgumentException if policyDirectives is {@code null} or empty\n\t */\n\tpublic void setPolicyDirectives(String policyDirectives) {\n\t\tAssert.hasLength(policyDirectives, \"policyDirectives must not be null or empty\");\n\t\tthis.delegate = createDelegate(policyDirectives);\n\t}\n\n\tprivate static ServerHttpHeadersWriter createDelegate(String policyDirectives) {\n\t\tBuilder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(FEATURE_POLICY, policyDirectives);\n\t\treturn builder.build();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/HttpHeaderWriterWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Invokes a {@link ServerHttpHeadersWriter} on\n * {@link ServerHttpResponse#beforeCommit(java.util.function.Supplier)}.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class HttpHeaderWriterWebFilter implements WebFilter {\n\n\tprivate final ServerHttpHeadersWriter writer;\n\n\tpublic HttpHeaderWriterWebFilter(ServerHttpHeadersWriter writer) {\n\t\tthis.writer = writer;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\texchange.getResponse().beforeCommit(() -> this.writer.writeHttpHeaders(exchange));\n\t\treturn chain.filter(exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/PermissionsPolicyServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.header.StaticServerHttpHeadersWriter.Builder;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Writes the {@code Permissions-Policy} response header with configured policy\n * directives.\n *\n * @author Christophe Gilles\n * @since 5.5\n */\npublic final class PermissionsPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String PERMISSIONS_POLICY = \"Permissions-Policy\";\n\n\tprivate @Nullable ServerHttpHeadersWriter delegate;\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();\n\t}\n\n\tprivate static ServerHttpHeadersWriter createDelegate(String policyDirectives) {\n\t\tBuilder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(PERMISSIONS_POLICY, policyDirectives);\n\t\treturn builder.build();\n\t}\n\n\t/**\n\t * Set the policy to be used in the response header.\n\t * @param policy the policy\n\t * @throws IllegalArgumentException if policy is {@code null}\n\t */\n\tpublic void setPolicy(String policy) {\n\t\tAssert.notNull(policy, \"policy must not be null\");\n\t\tthis.delegate = createDelegate(policy);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/ReferrerPolicyServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.header.StaticServerHttpHeadersWriter.Builder;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Writes the {@code Referrer-Policy} response header.\n *\n * @author Vedran Pavic\n * @since 5.1\n */\npublic final class ReferrerPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String REFERRER_POLICY = \"Referrer-Policy\";\n\n\tprivate ServerHttpHeadersWriter delegate;\n\n\tpublic ReferrerPolicyServerHttpHeadersWriter() {\n\t\tthis.delegate = createDelegate(ReferrerPolicy.NO_REFERRER);\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn this.delegate.writeHttpHeaders(exchange);\n\t}\n\n\t/**\n\t * Set the policy to be used in the response header.\n\t * @param policy the policy\n\t * @throws IllegalArgumentException if policy is {@code null}\n\t */\n\tpublic void setPolicy(ReferrerPolicy policy) {\n\t\tAssert.notNull(policy, \"policy must not be null\");\n\t\tthis.delegate = createDelegate(policy);\n\t}\n\n\tprivate static ServerHttpHeadersWriter createDelegate(ReferrerPolicy policy) {\n\t\tBuilder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(REFERRER_POLICY, policy.getPolicy());\n\t\treturn builder.build();\n\t}\n\n\tpublic enum ReferrerPolicy {\n\n\t\tNO_REFERRER(\"no-referrer\"),\n\n\t\tNO_REFERRER_WHEN_DOWNGRADE(\"no-referrer-when-downgrade\"),\n\n\t\tSAME_ORIGIN(\"same-origin\"),\n\n\t\tORIGIN(\"origin\"),\n\n\t\tSTRICT_ORIGIN(\"strict-origin\"),\n\n\t\tORIGIN_WHEN_CROSS_ORIGIN(\"origin-when-cross-origin\"),\n\n\t\tSTRICT_ORIGIN_WHEN_CROSS_ORIGIN(\"strict-origin-when-cross-origin\"),\n\n\t\tUNSAFE_URL(\"unsafe-url\");\n\n\t\tprivate static final Map<String, ReferrerPolicy> REFERRER_POLICIES;\n\n\t\tstatic {\n\t\t\tMap<String, ReferrerPolicy> referrerPolicies = new HashMap<>();\n\t\t\tfor (ReferrerPolicy referrerPolicy : values()) {\n\t\t\t\treferrerPolicies.put(referrerPolicy.getPolicy(), referrerPolicy);\n\t\t\t}\n\t\t\tREFERRER_POLICIES = Collections.unmodifiableMap(referrerPolicies);\n\t\t}\n\n\t\tprivate final String policy;\n\n\t\tReferrerPolicy(String policy) {\n\t\t\tthis.policy = policy;\n\t\t}\n\n\t\tpublic String getPolicy() {\n\t\t\treturn this.policy;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/ServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.util.function.Supplier;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Interface for writing headers just before the response is committed.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic interface ServerHttpHeadersWriter {\n\n\t/**\n\t * Write the headers to the response.\n\t * @param exchange\n\t * @return A Mono which is returned to the {@link Supplier} of the\n\t * {@link ServerHttpResponse#beforeCommit(Supplier)}.\n\t */\n\tMono<Void> writeHttpHeaders(ServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/ServerWebExchangeDelegatingServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcherEntry;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Delegates to a provided {@link ServerHttpHeadersWriter} if\n * {@link ServerWebExchangeMatcher#matches(ServerWebExchange)} returns a match.\n *\n * @author David Herberth\n * @since 5.8\n */\npublic final class ServerWebExchangeDelegatingServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tprivate final ServerWebExchangeMatcherEntry<ServerHttpHeadersWriter> headersWriter;\n\n\t/**\n\t * Creates a new instance\n\t * @param headersWriter the {@link ServerWebExchangeMatcherEntry} holding a\n\t * {@link ServerWebExchangeMatcher} and the {@link ServerHttpHeadersWriter} to invoke\n\t * if the matcher returns a match.\n\t */\n\tpublic ServerWebExchangeDelegatingServerHttpHeadersWriter(\n\t\t\tServerWebExchangeMatcherEntry<ServerHttpHeadersWriter> headersWriter) {\n\t\tAssert.notNull(headersWriter, \"headersWriter cannot be null\");\n\t\tAssert.notNull(headersWriter.getMatcher(), \"webExchangeMatcher cannot be null\");\n\t\tAssert.notNull(headersWriter.getEntry(), \"delegateHeadersWriter cannot be null\");\n\t\tthis.headersWriter = headersWriter;\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param webExchangeMatcher the {@link ServerWebExchangeMatcher} to use. If it\n\t * returns a match, the delegateHeadersWriter is invoked.\n\t * @param delegateHeadersWriter the {@link ServerHttpHeadersWriter} to invoke if the\n\t * {@link ServerWebExchangeMatcher} returns a match.\n\t */\n\tpublic ServerWebExchangeDelegatingServerHttpHeadersWriter(ServerWebExchangeMatcher webExchangeMatcher,\n\t\t\tServerHttpHeadersWriter delegateHeadersWriter) {\n\t\tthis(new ServerWebExchangeMatcherEntry<>(webExchangeMatcher, delegateHeadersWriter));\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn this.headersWriter.getMatcher()\n\t\t\t.matches(exchange)\n\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t.flatMap((matchResult) -> this.headersWriter.getEntry().writeHttpHeaders(exchange));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/StaticServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.util.Arrays;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Allows specifying {@link HttpHeaders} that should be written to the response.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class StaticServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tprivate final HttpHeaders headersToAdd;\n\n\tpublic StaticServerHttpHeadersWriter(HttpHeaders headersToAdd) {\n\t\tthis.headersToAdd = headersToAdd;\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\tHttpHeaders headers = exchange.getResponse().getHeaders();\n\t\t// Note: We need to ensure that the following algorithm compares headers\n\t\t// case insensitively, which should be true of headers.containsKey().\n\t\tboolean containsNoHeadersToAdd = true;\n\t\tfor (String headerName : this.headersToAdd.headerNames()) {\n\t\t\tif (headers.containsHeader(headerName)) {\n\t\t\t\tcontainsNoHeadersToAdd = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (containsNoHeadersToAdd) {\n\t\t\tthis.headersToAdd.forEach(headers::put);\n\t\t}\n\t\treturn Mono.empty();\n\t}\n\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\tpublic static class Builder {\n\n\t\tprivate HttpHeaders headers = new HttpHeaders();\n\n\t\tpublic Builder header(String headerName, String... values) {\n\t\t\tthis.headers.put(headerName, Arrays.asList(values));\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic StaticServerHttpHeadersWriter build() {\n\t\t\treturn new StaticServerHttpHeadersWriter(this.headers);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/StrictTransportSecurityServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.time.Duration;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.header.StaticServerHttpHeadersWriter.Builder;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Writes the Strict-Transport-Security if the request is secure.\n *\n * @author Rob Winch\n * @author Ankur Pathak\n * @since 5.0\n */\npublic final class StrictTransportSecurityServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String STRICT_TRANSPORT_SECURITY = \"Strict-Transport-Security\";\n\n\tprivate String maxAge;\n\n\tprivate String subdomain;\n\n\tprivate String preload;\n\n\tprivate ServerHttpHeadersWriter delegate;\n\n\tpublic StrictTransportSecurityServerHttpHeadersWriter() {\n\t\tsetIncludeSubDomains(true);\n\t\tsetMaxAge(Duration.ofDays(365L));\n\t\tsetPreload(false);\n\t\tupdateDelegate();\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn isSecure(exchange) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();\n\t}\n\n\t/**\n\t * Sets if subdomains should be included. Default is true\n\t * @param includeSubDomains if subdomains should be included\n\t */\n\tpublic void setIncludeSubDomains(boolean includeSubDomains) {\n\t\tthis.subdomain = includeSubDomains ? \" ; includeSubDomains\" : \"\";\n\t\tupdateDelegate();\n\t}\n\n\t/**\n\t * <p>\n\t * Sets if preload should be included. Default is false\n\t * </p>\n\t *\n\t * <p>\n\t * See <a href=\"https://hstspreload.org/\">Website hstspreload.org</a> for additional\n\t * details.\n\t * </p>\n\t * @param preload if preload should be included\n\t * @since 5.2.0\n\t */\n\tpublic void setPreload(boolean preload) {\n\t\tthis.preload = preload ? \" ; preload\" : \"\";\n\t\tupdateDelegate();\n\t}\n\n\t/**\n\t * Sets the max age of the header. Default is a year.\n\t * @param maxAge the max age of the header\n\t */\n\tpublic void setMaxAge(Duration maxAge) {\n\t\tthis.maxAge = \"max-age=\" + maxAge.getSeconds();\n\t\tupdateDelegate();\n\t}\n\n\tprivate void updateDelegate() {\n\t\tBuilder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(STRICT_TRANSPORT_SECURITY, this.maxAge + this.subdomain + this.preload);\n\t\tthis.delegate = builder.build();\n\t}\n\n\tprivate boolean isSecure(ServerWebExchange exchange) {\n\t\tString scheme = exchange.getRequest().getURI().getScheme();\n\t\tboolean isSecure = scheme != null && scheme.equalsIgnoreCase(\"https\");\n\t\treturn isSecure;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/XContentTypeOptionsServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Adds X-Content-Type-Options: nosniff\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class XContentTypeOptionsServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String X_CONTENT_OPTIONS = \"X-Content-Type-Options\";\n\n\tpublic static final String NOSNIFF = \"nosniff\";\n\n\t/**\n\t * The delegate to write all the cache control related headers\n\t */\n\tprivate static final ServerHttpHeadersWriter CONTENT_TYPE_HEADERS = StaticServerHttpHeadersWriter.builder()\n\t\t.header(X_CONTENT_OPTIONS, NOSNIFF)\n\t\t.build();\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn CONTENT_TYPE_HEADERS.writeHttpHeaders(exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/XFrameOptionsServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.header.StaticServerHttpHeadersWriter.Builder;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * {@code ServerHttpHeadersWriter} implementation for the X-Frame-Options headers.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class XFrameOptionsServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String X_FRAME_OPTIONS = \"X-Frame-Options\";\n\n\tprivate ServerHttpHeadersWriter delegate = createDelegate(Mode.DENY);\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn this.delegate.writeHttpHeaders(exchange);\n\t}\n\n\t/**\n\t * Sets the X-Frame-Options mode. There is no support for ALLOW-FROM because not\n\t * <a href=\n\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options\">all\n\t * browsers support it</a>. Consider using X-Frame-Options with\n\t * Content-Security-Policy <a href=\n\t * \"https://w3c.github.io/webappsec/specs/content-security-policy/#directive-frame-ancestors\">frame-ancestors</a>.\n\t * @param mode\n\t */\n\tpublic void setMode(Mode mode) {\n\t\tthis.delegate = createDelegate(mode);\n\t}\n\n\t/**\n\t * The X-Frame-Options values. There is no support for ALLOW-FROM because not <a href=\n\t * \"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options\">all\n\t * browsers support it</a>. Consider using X-Frame-Options with\n\t * Content-Security-Policy <a href=\n\t * \"https://w3c.github.io/webappsec/specs/content-security-policy/#directive-frame-ancestors\">frame-ancestors</a>.\n\t *\n\t * @author Rob Winch\n\t * @since 5.0\n\t */\n\tpublic enum Mode {\n\n\t\t/**\n\t\t * A browser receiving content with this header field MUST NOT display this\n\t\t * content in any frame.\n\t\t */\n\t\tDENY,\n\n\t\t/**\n\t\t * A browser receiving content with this header field MUST NOT display this\n\t\t * content in any frame from a page of different origin than the content itself.\n\t\t *\n\t\t * If a browser or plugin cannot reliably determine whether or not the origin of\n\t\t * the content and the frame are the same, this MUST be treated as \"DENY\".\n\t\t */\n\t\tSAMEORIGIN\n\n\t}\n\n\tprivate static ServerHttpHeadersWriter createDelegate(Mode mode) {\n\t\tBuilder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(X_FRAME_OPTIONS, mode.name());\n\t\treturn builder.build();\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/XXssProtectionServerHttpHeadersWriter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.header.StaticServerHttpHeadersWriter.Builder;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Add the x-xss-protection header.\n *\n * @author Rob Winch\n * @author Daniel Garnier-Moiroux\n * @since 5.0\n */\npublic class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersWriter {\n\n\tpublic static final String X_XSS_PROTECTION = \"X-XSS-Protection\";\n\n\tprivate ServerHttpHeadersWriter delegate;\n\n\tprivate HeaderValue headerValue;\n\n\t/**\n\t * Creates a new instance\n\t */\n\tpublic XXssProtectionServerHttpHeadersWriter() {\n\t\tthis.headerValue = HeaderValue.DISABLED;\n\t\tupdateDelegate();\n\t}\n\n\t@Override\n\tpublic Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {\n\t\treturn this.delegate.writeHttpHeaders(exchange);\n\t}\n\n\t/**\n\t * Sets the value of the X-XSS-PROTECTION header. Defaults to\n\t * {@link HeaderValue#DISABLED}\n\t * <p>\n\t * If {@link HeaderValue#DISABLED}, will specify that X-XSS-Protection is disabled.\n\t * For example:\n\t *\n\t * <pre>\n\t * X-XSS-Protection: 0\n\t * </pre>\n\t * <p>\n\t * If {@link HeaderValue#ENABLED}, will contain a value of 1, but will not specify the\n\t * mode as blocked. In this instance, any content will be attempted to be fixed. For\n\t * example:\n\t *\n\t * <pre>\n\t * X-XSS-Protection: 1\n\t * </pre>\n\t * <p>\n\t * If {@link HeaderValue#ENABLED_MODE_BLOCK}, will contain a value of 1 and will\n\t * specify mode as blocked. The content will be replaced with \"#\". For example:\n\t *\n\t * <pre>\n\t * X-XSS-Protection: 1; mode=block\n\t * </pre>\n\t * @param headerValue the new headerValue\n\t * @throws IllegalArgumentException if headerValue is null\n\t * @since 5.8\n\t */\n\tpublic void setHeaderValue(HeaderValue headerValue) {\n\t\tAssert.notNull(headerValue, \"headerValue cannot be null\");\n\t\tthis.headerValue = headerValue;\n\t\tupdateDelegate();\n\t}\n\n\t/**\n\t * The value of the x-xss-protection header. One of: \"0\", \"1\", \"1; mode=block\"\n\t *\n\t * @author Daniel Garnier-Moiroux\n\t * @since 5.8\n\t */\n\tpublic enum HeaderValue {\n\n\t\tDISABLED(\"0\"), ENABLED(\"1\"), ENABLED_MODE_BLOCK(\"1; mode=block\");\n\n\t\tprivate final String value;\n\n\t\tHeaderValue(String value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn this.value;\n\t\t}\n\n\t}\n\n\tprivate void updateDelegate() {\n\t\tBuilder builder = StaticServerHttpHeadersWriter.builder();\n\t\tbuilder.header(X_XSS_PROTECTION, this.headerValue.toString());\n\t\tthis.delegate = builder.build();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/header/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive APIs for adding HTTP Header based security.\n */\n@NullMarked\npackage org.springframework.security.web.server.header;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/jackson/DefaultCsrfServerTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson mixin class to serialize/deserialize\n * {@link org.springframework.security.web.server.csrf.DefaultCsrfToken} serialization\n * support.\n *\n * @author Sebastien Deleuze\n * @author Boris Finkelshteyn\n * @since 7.0\n * @see WebServerJacksonModule\n */\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)\nclass DefaultCsrfServerTokenMixin {\n\n\t/**\n\t * JsonCreator constructor needed by Jackson to create\n\t * {@link org.springframework.security.web.server.csrf.DefaultCsrfToken} object.\n\t * @param headerName the name of the header\n\t * @param parameterName the parameter name\n\t * @param token the CSRF token value\n\t */\n\t@JsonCreator\n\tDefaultCsrfServerTokenMixin(@JsonProperty(\"headerName\") String headerName,\n\t\t\t@JsonProperty(\"parameterName\") String parameterName, @JsonProperty(\"token\") String token) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/jackson/WebServerJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.jackson;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.web.server.csrf.DefaultCsrfToken;\n\n/**\n * Jackson module for spring-security-web-flux. This module register\n * {@link DefaultCsrfServerTokenMixin}.\n *\n * <p>\n * The recommended way to configure it is to use {@link SecurityJacksonModules} in order\n * to enable properly automatic inclusion of type information with related validation.\n *\n * <pre>\n *     ClassLoader loader = getClass().getClassLoader();\n *     JsonMapper mapper = JsonMapper.builder()\n * \t\t\t\t.addModules(SecurityJacksonModules.getModules(loader))\n * \t\t\t\t.build();\n * </pre>\n *\n * @author Boris Finkelshteyn\n * @since 5.1\n * @see SecurityJacksonModules\n */\n@SuppressWarnings(\"serial\")\npublic class WebServerJacksonModule extends SecurityJacksonModule {\n\n\tprivate static final String NAME = WebServerJacksonModule.class.getName();\n\n\tprivate static final Version VERSION = new Version(1, 0, 0, null, null, null);\n\n\tpublic WebServerJacksonModule() {\n\t\tsuper(NAME, VERSION);\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(DefaultCsrfToken.class, DefaultCsrfServerTokenMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/jackson/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 3+ serialization support for reactive web server.\n */\n@NullMarked\npackage org.springframework.security.web.server.jackson;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/jackson2/DefaultCsrfServerTokenMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.jackson2;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\n\n/**\n * Jackson mixin class to serialize/deserialize\n * {@link org.springframework.security.web.server.csrf.DefaultCsrfToken} serialization\n * support.\n *\n * <pre>\n * \t\tObjectMapper mapper = new ObjectMapper();\n * \t\tmapper.registerModule(new WebServerJackson2Module());\n * </pre>\n *\n * @author Boris Finkelshteyn\n * @since 5.1\n * @see WebServerJackson2Module\n * @deprecated as of 7.0 in favor of\n * {@code org.springframework.security.web.server.jackson.DefaultCsrfServerTokenMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = \"@class\")\n@JsonIgnoreProperties(ignoreUnknown = true)\nclass DefaultCsrfServerTokenMixin {\n\n\t/**\n\t * JsonCreator constructor needed by Jackson to create\n\t * {@link org.springframework.security.web.server.csrf.DefaultCsrfToken} object.\n\t * @param headerName the name of the header\n\t * @param parameterName the parameter name\n\t * @param token the CSRF token value\n\t */\n\t@JsonCreator\n\tDefaultCsrfServerTokenMixin(@JsonProperty(\"headerName\") String headerName,\n\t\t\t@JsonProperty(\"parameterName\") String parameterName, @JsonProperty(\"token\") String token) {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/jackson2/WebServerJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.jackson2;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.web.server.csrf.DefaultCsrfToken;\n\n/**\n * Jackson module for spring-security-web-flux. This module register\n * {@link DefaultCsrfServerTokenMixin} If no default typing enabled by default then it'll\n * enable it because typing info is needed to properly serialize/deserialize objects. In\n * order to use this module just add this module into your ObjectMapper configuration.\n *\n * <pre>\n *     ObjectMapper mapper = new ObjectMapper();\n *     mapper.registerModule(new WebServerJackson2Module());\n * </pre> <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list\n * of all security modules.</b>\n *\n * @author Boris Finkelshteyn\n * @since 5.1\n * @see SecurityJackson2Modules\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.server.jackson.WebServerJacksonModule} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings({ \"serial\", \"removal\" })\npublic class WebServerJackson2Module extends SimpleModule {\n\n\tprivate static final String NAME = WebServerJackson2Module.class.getName();\n\n\tprivate static final Version VERSION = new Version(1, 0, 0, null, null, null);\n\n\tpublic WebServerJackson2Module() {\n\t\tsuper(NAME, VERSION);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tSecurityJackson2Modules.enableDefaultTyping(context.getOwner());\n\t\tcontext.setMixInAnnotations(DefaultCsrfToken.class, DefaultCsrfServerTokenMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/jackson2/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Jackson 2 serialization support for reactive web server.\n */\n@NullMarked\npackage org.springframework.security.web.server.jackson2;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WebFlux Spring Security support.\n */\n@NullMarked\npackage org.springframework.security.web.server;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/savedrequest/CookieServerRequestCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.savedrequest;\n\nimport java.net.URI;\nimport java.time.Duration;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.function.Consumer;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpCookie;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.MediaTypeServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of {@link ServerRequestCache} that saves the requested URI in a\n * cookie.\n *\n * @author Eleftheria Stein\n * @author Mathieu Ouellet\n * @since 5.4\n */\npublic class CookieServerRequestCache implements ServerRequestCache {\n\n\tprivate static final String REDIRECT_URI_COOKIE_NAME = \"REDIRECT_URI\";\n\n\tprivate static final Duration COOKIE_MAX_AGE = Duration.ofSeconds(-1);\n\n\tprivate static final Log logger = LogFactory.getLog(CookieServerRequestCache.class);\n\n\tprivate ServerWebExchangeMatcher saveRequestMatcher = createDefaultRequestMatcher();\n\n\tprivate Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer = (cookieBuilder) -> {\n\t};\n\n\t/**\n\t * Sets the matcher to determine if the request should be saved. The default is to\n\t * match on any GET request.\n\t * @param saveRequestMatcher the {@link ServerWebExchangeMatcher} that determines if\n\t * the request should be saved\n\t */\n\tpublic void setSaveRequestMatcher(ServerWebExchangeMatcher saveRequestMatcher) {\n\t\tAssert.notNull(saveRequestMatcher, \"saveRequestMatcher cannot be null\");\n\t\tthis.saveRequestMatcher = saveRequestMatcher;\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveRequest(ServerWebExchange exchange) {\n\t\treturn this.saveRequestMatcher.matches(exchange)\n\t\t\t.filter((m) -> m.isMatch())\n\t\t\t.map((m) -> exchange.getResponse())\n\t\t\t.map(ServerHttpResponse::getCookies)\n\t\t\t.doOnNext((cookies) -> {\n\t\t\t\tResponseCookie.ResponseCookieBuilder builder = createRedirectUriCookieBuilder(exchange.getRequest());\n\t\t\t\tthis.cookieCustomizer.accept(builder);\n\t\t\t\tResponseCookie redirectUriCookie = builder.build();\n\t\t\t\tcookies.add(REDIRECT_URI_COOKIE_NAME, redirectUriCookie);\n\t\t\t\tlogger.debug(LogMessage.format(\"Request added to Cookie: %s\", redirectUriCookie));\n\t\t\t})\n\t\t\t.then();\n\t}\n\n\t@Override\n\tpublic Mono<URI> getRedirectUri(ServerWebExchange exchange) {\n\t\tMultiValueMap<String, HttpCookie> cookieMap = exchange.getRequest().getCookies();\n\t\treturn Mono.justOrEmpty(cookieMap.getFirst(REDIRECT_URI_COOKIE_NAME))\n\t\t\t.map(HttpCookie::getValue)\n\t\t\t.map(CookieServerRequestCache::decodeCookie)\n\t\t\t.onErrorResume(IllegalArgumentException.class, (ex) -> Mono.empty())\n\t\t\t.map(URI::create);\n\t}\n\n\t@Override\n\tpublic Mono<ServerHttpRequest> removeMatchingRequest(ServerWebExchange exchange) {\n\t\treturn Mono.just(exchange.getResponse())\n\t\t\t.map(ServerHttpResponse::getCookies)\n\t\t\t.doOnNext((cookies) -> cookies.add(REDIRECT_URI_COOKIE_NAME,\n\t\t\t\t\tinvalidateRedirectUriCookie(exchange.getRequest())))\n\t\t\t.thenReturn(exchange.getRequest());\n\t}\n\n\t/**\n\t * Sets the {@link Consumer}, allowing customization of cookie.\n\t * @param cookieCustomizer customize for cookie\n\t * @since 6.4\n\t */\n\tpublic void setCookieCustomizer(Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer) {\n\t\tAssert.notNull(cookieCustomizer, \"cookieCustomizer cannot be null\");\n\t\tthis.cookieCustomizer = cookieCustomizer;\n\t}\n\n\tprivate static ResponseCookie.ResponseCookieBuilder createRedirectUriCookieBuilder(ServerHttpRequest request) {\n\t\tString path = request.getPath().pathWithinApplication().value();\n\t\tString query = request.getURI().getRawQuery();\n\t\tString redirectUri = path + ((query != null) ? \"?\" + query : \"\");\n\t\treturn createResponseCookieBuilder(request, encodeCookie(redirectUri), COOKIE_MAX_AGE);\n\t}\n\n\tprivate static ResponseCookie invalidateRedirectUriCookie(ServerHttpRequest request) {\n\t\treturn createResponseCookieBuilder(request, null, Duration.ZERO).build();\n\t}\n\n\tprivate static ResponseCookie.ResponseCookieBuilder createResponseCookieBuilder(ServerHttpRequest request,\n\t\t\t@Nullable String cookieValue, Duration age) {\n\t\treturn ResponseCookie.from(REDIRECT_URI_COOKIE_NAME)\n\t\t\t.value(cookieValue)\n\t\t\t.path(request.getPath().contextPath().value() + \"/\")\n\t\t\t.maxAge(age)\n\t\t\t.httpOnly(true)\n\t\t\t.secure(\"https\".equalsIgnoreCase(request.getURI().getScheme()))\n\t\t\t.sameSite(\"Lax\");\n\t}\n\n\tprivate static String encodeCookie(String cookieValue) {\n\t\treturn new String(Base64.getEncoder().encode(cookieValue.getBytes()));\n\t}\n\n\tprivate static String decodeCookie(String encodedCookieValue) {\n\t\treturn new String(Base64.getDecoder().decode(encodedCookieValue.getBytes()));\n\t}\n\n\tprivate static ServerWebExchangeMatcher createDefaultRequestMatcher() {\n\t\tServerWebExchangeMatcher get = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, \"/**\");\n\t\tServerWebExchangeMatcher notFavicon = new NegatedServerWebExchangeMatcher(\n\t\t\t\tServerWebExchangeMatchers.pathMatchers(\"/favicon.*\"));\n\t\tMediaTypeServerWebExchangeMatcher html = new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML);\n\t\thtml.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\treturn new AndServerWebExchangeMatcher(get, notFavicon, html);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/savedrequest/NoOpServerRequestCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.savedrequest;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An implementation of {@link ServerRequestCache} that does nothing. This is used in\n * stateless applications\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic final class NoOpServerRequestCache implements ServerRequestCache {\n\n\tprivate NoOpServerRequestCache() {\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveRequest(ServerWebExchange exchange) {\n\t\treturn Mono.empty();\n\t}\n\n\t@Override\n\tpublic Mono<URI> getRedirectUri(ServerWebExchange exchange) {\n\t\treturn Mono.empty();\n\t}\n\n\t@Override\n\tpublic Mono<ServerHttpRequest> removeMatchingRequest(ServerWebExchange exchange) {\n\t\treturn Mono.empty();\n\t}\n\n\tpublic static NoOpServerRequestCache getInstance() {\n\t\treturn new NoOpServerRequestCache();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/savedrequest/ServerRequestCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.savedrequest;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Saves a {@link ServerHttpRequest} so it can be \"replayed\" later. This is useful for\n * when a page was requested and authentication is necessary.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic interface ServerRequestCache {\n\n\t/**\n\t * Save the {@link ServerHttpRequest}\n\t * @param exchange the exchange to save\n\t * @return Return a {@code Mono<Void>} which only replays complete and error signals\n\t * from this {@link Mono}.\n\t */\n\tMono<Void> saveRequest(ServerWebExchange exchange);\n\n\t/**\n\t * Get the URI that can be redirected to trigger the saved request to be used\n\t * @param exchange the exchange to obtain the saved {@link ServerHttpRequest} from\n\t * @return the URI that can be redirected to trigger the saved request to be used\n\t */\n\tMono<URI> getRedirectUri(ServerWebExchange exchange);\n\n\t/**\n\t * If the provided {@link ServerWebExchange} matches the saved\n\t * {@link ServerHttpRequest} gets the saved {@link ServerHttpRequest}\n\t * @param exchange the exchange to obtain the request from\n\t * @return the {@link ServerHttpRequest}\n\t */\n\tMono<ServerHttpRequest> removeMatchingRequest(ServerWebExchange exchange);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/savedrequest/ServerRequestCacheWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.savedrequest;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * A {@link WebFilter} that replays any matching request in {@link ServerRequestCache}\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class ServerRequestCacheWebFilter implements WebFilter {\n\n\tprivate ServerRequestCache requestCache = new WebSessionServerRequestCache();\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.requestCache.removeMatchingRequest(exchange)\n\t\t\t.map((r) -> exchange.mutate().request(r).build())\n\t\t\t.defaultIfEmpty(exchange)\n\t\t\t.flatMap((e) -> chain.filter(e));\n\t}\n\n\tpublic void setRequestCache(ServerRequestCache requestCache) {\n\t\tAssert.notNull(requestCache, \"requestCache cannot be null\");\n\t\tthis.requestCache = requestCache;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/savedrequest/WebSessionServerRequestCache.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.savedrequest;\n\nimport java.net.URI;\nimport java.util.Collections;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.MediaTypeServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * An implementation of {@link ServerRequestCache} that saves the\n * {@link ServerHttpRequest} in the {@link WebSession}.\n *\n * The current implementation only saves the URL that was requested.\n *\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n */\npublic class WebSessionServerRequestCache implements ServerRequestCache {\n\n\tprivate static final String DEFAULT_SAVED_REQUEST_ATTR = \"SPRING_SECURITY_SAVED_REQUEST\";\n\n\tprivate static final Log logger = LogFactory.getLog(WebSessionServerRequestCache.class);\n\n\tprivate String sessionAttrName = DEFAULT_SAVED_REQUEST_ATTR;\n\n\tprivate ServerWebExchangeMatcher saveRequestMatcher = createDefaultRequestMatcher();\n\n\tprivate @Nullable String matchingRequestParameterName;\n\n\t/**\n\t * Sets the matcher to determine if the request should be saved. The default is to\n\t * match on any GET request.\n\t * @param saveRequestMatcher the {@link ServerWebExchangeMatcher} that determines if\n\t * the request should be saved\n\t */\n\tpublic void setSaveRequestMatcher(ServerWebExchangeMatcher saveRequestMatcher) {\n\t\tAssert.notNull(saveRequestMatcher, \"saveRequestMatcher cannot be null\");\n\t\tthis.saveRequestMatcher = saveRequestMatcher;\n\t}\n\n\t@Override\n\tpublic Mono<Void> saveRequest(ServerWebExchange exchange) {\n\t\treturn this.saveRequestMatcher.matches(exchange)\n\t\t\t.filter(MatchResult::isMatch)\n\t\t\t.flatMap((m) -> exchange.getSession())\n\t\t\t.map(WebSession::getAttributes)\n\t\t\t.doOnNext((attrs) -> {\n\t\t\t\tString requestPath = pathInApplication(exchange.getRequest());\n\t\t\t\tattrs.put(this.sessionAttrName, requestPath);\n\t\t\t\tlogger.debug(LogMessage.format(\"Request added to WebSession: '%s'\", requestPath));\n\t\t\t})\n\t\t\t.then();\n\t}\n\n\t@Override\n\tpublic Mono<URI> getRedirectUri(ServerWebExchange exchange) {\n\t\treturn exchange.getSession()\n\t\t\t.flatMap((session) -> Mono.justOrEmpty(session.<String>getAttribute(this.sessionAttrName)))\n\t\t\t.map(this::createRedirectUri);\n\t}\n\n\t@Override\n\tpublic Mono<ServerHttpRequest> removeMatchingRequest(ServerWebExchange exchange) {\n\t\tMultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();\n\t\tif (this.matchingRequestParameterName != null && !queryParams.containsKey(this.matchingRequestParameterName)) {\n\t\t\tlogger.trace(\n\t\t\t\t\t\"matchingRequestParameterName is required for getMatchingRequest to lookup a value, but not provided\");\n\t\t\treturn Mono.empty();\n\t\t}\n\t\tServerHttpRequest request = stripMatchingRequestParameterName(exchange.getRequest());\n\t\treturn exchange.getSession().map(WebSession::getAttributes).filter((attributes) -> {\n\t\t\tString requestPath = pathInApplication(request);\n\t\t\tboolean removed = attributes.remove(this.sessionAttrName, requestPath);\n\t\t\tif (removed) {\n\t\t\t\tlogger.debug(LogMessage.format(\"Request removed from WebSession: '%s'\", requestPath));\n\t\t\t}\n\t\t\treturn removed;\n\t\t}).map((attributes) -> request);\n\t}\n\n\t/**\n\t * Specify the name of a query parameter that is added to the URL in\n\t * {@link #getRedirectUri(ServerWebExchange)} and is required for\n\t * {@link #removeMatchingRequest(ServerWebExchange)} to look up the\n\t * {@link ServerHttpRequest}.\n\t * @param matchingRequestParameterName the parameter name that must be in the request\n\t * for {@link #removeMatchingRequest(ServerWebExchange)} to check the session.\n\t */\n\tpublic void setMatchingRequestParameterName(String matchingRequestParameterName) {\n\t\tthis.matchingRequestParameterName = matchingRequestParameterName;\n\t}\n\n\tprivate ServerHttpRequest stripMatchingRequestParameterName(ServerHttpRequest request) {\n\t\tif (this.matchingRequestParameterName == null) {\n\t\t\treturn request;\n\t\t}\n\t\t// @formatter:off\n\t\tURI uri = UriComponentsBuilder.fromUri(request.getURI())\n\t\t\t\t.replaceQueryParam(this.matchingRequestParameterName)\n\t\t\t\t.build()\n\t\t\t\t.toUri();\n\t\treturn request.mutate()\n\t\t\t\t.uri(uri)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\tprivate static String pathInApplication(ServerHttpRequest request) {\n\t\tString path = request.getPath().pathWithinApplication().value();\n\t\tString query = request.getURI().getRawQuery();\n\t\treturn path + ((query != null) ? \"?\" + query : \"\");\n\t}\n\n\tprivate URI createRedirectUri(String uri) {\n\t\tif (this.matchingRequestParameterName == null) {\n\t\t\treturn URI.create(uri);\n\t\t}\n\t\t// @formatter:off\n\t\treturn UriComponentsBuilder.fromUriString(uri)\n\t\t\t\t.queryParam(this.matchingRequestParameterName)\n\t\t\t\t.build()\n\t\t\t\t.toUri();\n\t\t// @formatter:on\n\t}\n\n\tprivate static ServerWebExchangeMatcher createDefaultRequestMatcher() {\n\t\tServerWebExchangeMatcher get = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, \"/**\");\n\t\tServerWebExchangeMatcher notFavicon = new NegatedServerWebExchangeMatcher(\n\t\t\t\tServerWebExchangeMatchers.pathMatchers(\"/favicon.*\"));\n\t\tMediaTypeServerWebExchangeMatcher html = new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML);\n\t\thtml.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\treturn new AndServerWebExchangeMatcher(get, notFavicon, html);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/savedrequest/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive support for saving requests (to replay them after interrupted by security\n * workflows like authentication).\n */\n@NullMarked\npackage org.springframework.security.web.server.savedrequest;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/transport/HttpsRedirectWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.transport;\n\nimport java.net.URI;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.security.web.server.DefaultServerRedirectStrategy;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Redirects any non-HTTPS request to its HTTPS equivalent.\n *\n * Can be configured to use a {@link ServerWebExchangeMatcher} to narrow which requests\n * get redirected.\n *\n * Can also be configured for custom ports using {@link PortMapper}.\n *\n * @author Josh Cummings\n * @since 5.1\n */\npublic final class HttpsRedirectWebFilter implements WebFilter {\n\n\tprivate PortMapper portMapper = new PortMapperImpl();\n\n\tprivate ServerWebExchangeMatcher requiresHttpsRedirectMatcher = ServerWebExchangeMatchers.anyExchange();\n\n\tprivate final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn Mono.just(exchange)\n\t\t\t.filter(this::isInsecure)\n\t\t\t.flatMap(this.requiresHttpsRedirectMatcher::matches)\n\t\t\t.filter((matchResult) -> matchResult.isMatch())\n\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t.map((matchResult) -> createRedirectUri(exchange))\n\t\t\t.flatMap((uri) -> this.redirectStrategy.sendRedirect(exchange, uri));\n\t}\n\n\t/**\n\t * Use this {@link PortMapper} for mapping custom ports\n\t * @param portMapper the {@link PortMapper} to use\n\t */\n\tpublic void setPortMapper(PortMapper portMapper) {\n\t\tAssert.notNull(portMapper, \"portMapper cannot be null\");\n\t\tthis.portMapper = portMapper;\n\t}\n\n\t/**\n\t * Use this {@link ServerWebExchangeMatcher} to narrow which requests are redirected\n\t * to HTTPS.\n\t *\n\t * The filter already first checks for HTTPS in the uri scheme, so it is not necessary\n\t * to include that check in this matcher.\n\t * @param requiresHttpsRedirectMatcher the {@link ServerWebExchangeMatcher} to use\n\t */\n\tpublic void setRequiresHttpsRedirectMatcher(ServerWebExchangeMatcher requiresHttpsRedirectMatcher) {\n\t\tAssert.notNull(requiresHttpsRedirectMatcher, \"requiresHttpsRedirectMatcher cannot be null\");\n\t\tthis.requiresHttpsRedirectMatcher = requiresHttpsRedirectMatcher;\n\t}\n\n\tprivate boolean isInsecure(ServerWebExchange exchange) {\n\t\treturn !\"https\".equals(exchange.getRequest().getURI().getScheme());\n\t}\n\n\tprivate URI createRedirectUri(ServerWebExchange exchange) {\n\t\tint port = exchange.getRequest().getURI().getPort();\n\t\tUriComponentsBuilder builder = UriComponentsBuilder.fromUri(exchange.getRequest().getURI());\n\t\tif (port > 0) {\n\t\t\tInteger httpsPort = this.portMapper.lookupHttpsPort(port);\n\t\t\tAssert.state(httpsPort != null, () -> \"HTTP Port '\" + port + \"' does not have a corresponding HTTPS Port\");\n\t\t\tbuilder.port(httpsPort);\n\t\t}\n\t\treturn builder.scheme(\"https\").build().toUri();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/transport/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WebFlux based transport security.\n */\n@NullMarked\npackage org.springframework.security.web.server.transport;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ui/DefaultResourcesWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.codec.ResourceHttpMessageWriter;\nimport org.springframework.security.web.authentication.ui.DefaultResourcesFilter;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Serve common static assets used in default UIs, such as CSS or Javascript files. For\n * internal use only.\n *\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n */\npublic final class DefaultResourcesWebFilter implements WebFilter {\n\n\tprivate final ServerWebExchangeMatcher matcher;\n\n\tprivate final ClassPathResource resource;\n\n\tprivate final MediaType mediaType;\n\n\tprivate DefaultResourcesWebFilter(ServerWebExchangeMatcher matcher, ClassPathResource resource,\n\t\t\tMediaType mediaType) {\n\t\tAssert.isTrue(resource.exists(), \"classpath resource must exist\");\n\t\tthis.matcher = matcher;\n\t\tthis.resource = resource;\n\t\tthis.mediaType = mediaType;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.matcher.matches(exchange)\n\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t.flatMap((matchResult) -> sendContent(exchange));\n\t}\n\n\tprivate Mono<Void> sendContent(ServerWebExchange exchange) {\n\t\texchange.getResponse().setStatusCode(HttpStatus.OK);\n\t\tResourceHttpMessageWriter writer = new ResourceHttpMessageWriter();\n\t\treturn writer.write(Mono.just(this.resource), ResolvableType.forClass(Resource.class),\n\t\t\t\tResolvableType.forClass(Resource.class), this.mediaType, exchange.getRequest(), exchange.getResponse(),\n\t\t\t\tCollections.emptyMap());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s{matcher=%s, resource='%s'}\".formatted(getClass().getSimpleName(), this.matcher,\n\t\t\t\tthis.resource.getPath());\n\t}\n\n\t/**\n\t * Create an instance of {@link DefaultResourcesWebFilter} serving Spring Security's\n\t * default CSS stylesheet.\n\t * <p>\n\t * The created {@link DefaultResourcesFilter} matches requests\n\t * {@code HTTP GET /default-ui.css}, and returns the default stylesheet at\n\t * {@code org/springframework/security/default-ui.css} with content-type\n\t * {@code text/css;charset=UTF-8}.\n\t * @return -\n\t */\n\tpublic static DefaultResourcesWebFilter css() {\n\t\treturn new DefaultResourcesWebFilter(\n\t\t\t\tnew PathPatternParserServerWebExchangeMatcher(\"/default-ui.css\", HttpMethod.GET),\n\t\t\t\tnew ClassPathResource(\"org/springframework/security/default-ui.css\"),\n\t\t\t\tnew MediaType(\"text\", \"css\", StandardCharsets.UTF_8));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ui/HtmlTemplates.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.HtmlUtils;\n\n/**\n * Render HTML templates using string substitution. Intended for internal use. Variables\n * can be templated using double curly-braces: {@code {{name}}}.\n *\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n * @see org.springframework.security.web.authentication.ui.HtmlTemplates\n */\nfinal class HtmlTemplates {\n\n\tprivate HtmlTemplates() {\n\t}\n\n\tstatic Builder fromTemplate(String template) {\n\t\treturn new Builder(template);\n\t}\n\n\tstatic final class Builder {\n\n\t\tprivate final String template;\n\n\t\tprivate final Map<String, String> values = new HashMap<>();\n\n\t\tprivate Builder(String template) {\n\t\t\tthis.template = template;\n\t\t}\n\n\t\t/**\n\t\t * HTML-escape, and inject value {@code value} in every {@code {{key}}}\n\t\t * placeholder.\n\t\t * @param key the placeholder name\n\t\t * @param value the value to inject\n\t\t * @return this instance for further templating\n\t\t */\n\t\tBuilder withValue(String key, String value) {\n\t\t\tthis.values.put(key, HtmlUtils.htmlEscape(value));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Inject value {@code value} in every {@code {{key}}} placeholder without\n\t\t * HTML-escaping. Useful for injecting \"sub-templates\".\n\t\t * @param key the placeholder name\n\t\t * @param value the value to inject\n\t\t * @return this instance for further templating\n\t\t */\n\t\tBuilder withRawHtml(String key, String value) {\n\t\t\tif (!value.isEmpty() && value.charAt(value.length() - 1) == '\\n') {\n\t\t\t\tvalue = value.substring(0, value.length() - 1);\n\t\t\t}\n\t\t\tthis.values.put(key, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Render the template. All placeholders MUST have a corresponding value. If a\n\t\t * placeholder does not have a corresponding value, throws\n\t\t * {@link IllegalStateException}.\n\t\t * @return the rendered template\n\t\t */\n\t\tString render() {\n\t\t\tString template = this.template;\n\t\t\tfor (String key : this.values.keySet()) {\n\t\t\t\tString pattern = Pattern.quote(\"{{\" + key + \"}}\");\n\t\t\t\ttemplate = template.replaceAll(pattern, this.values.get(key));\n\t\t\t}\n\n\t\t\tString unusedPlaceholders = Pattern.compile(\"\\\\{\\\\{([a-zA-Z0-9]+)}}\")\n\t\t\t\t.matcher(template)\n\t\t\t\t.results()\n\t\t\t\t.map((result) -> result.group(1))\n\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t\tif (StringUtils.hasLength(unusedPlaceholders)) {\n\t\t\t\tthrow new IllegalStateException(\"Unused placeholders in template: [%s]\".formatted(unusedPlaceholders));\n\t\t\t}\n\n\t\t\treturn template;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport java.nio.charset.Charset;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.web.server.csrf.CsrfToken;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Generates a default log in page used for authenticating users.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class LoginPageGeneratingWebFilter implements WebFilter {\n\n\tprivate ServerWebExchangeMatcher matcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, \"/login\");\n\n\tprivate Map<String, String> oauth2AuthenticationUrlToClientName = new HashMap<>();\n\n\tprivate boolean formLoginEnabled;\n\n\tprivate boolean oneTimeTokenEnabled = false;\n\n\tprivate @Nullable String generateOneTimeTokenUrl;\n\n\t/**\n\t * Specifies the URL that a One-Time Token generate request will be processed.\n\t * @param generateOneTimeTokenUrl\n\t * @since 6.4\n\t */\n\tpublic void setGenerateOneTimeTokenUrl(String generateOneTimeTokenUrl) {\n\t\tAssert.isTrue(StringUtils.hasText(generateOneTimeTokenUrl), \"generateOneTimeTokenUrl cannot be null or empty\");\n\t\tthis.generateOneTimeTokenUrl = generateOneTimeTokenUrl;\n\t}\n\n\tpublic void setFormLoginEnabled(boolean enabled) {\n\t\tthis.formLoginEnabled = enabled;\n\t}\n\n\t/**\n\t * Set if one-time token login is supported. Defaults to {@code false}.\n\t * @param oneTimeTokenEnabled\n\t */\n\tpublic void setOneTimeTokenEnabled(boolean oneTimeTokenEnabled) {\n\t\tthis.oneTimeTokenEnabled = oneTimeTokenEnabled;\n\t}\n\n\tpublic void setOauth2AuthenticationUrlToClientName(Map<String, String> oauth2AuthenticationUrlToClientName) {\n\t\tAssert.notNull(oauth2AuthenticationUrlToClientName, \"oauth2AuthenticationUrlToClientName cannot be null\");\n\t\tthis.oauth2AuthenticationUrlToClientName = oauth2AuthenticationUrlToClientName;\n\t}\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.matcher.matches(exchange)\n\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t.flatMap((matchResult) -> render(exchange));\n\t}\n\n\tprivate Mono<Void> render(ServerWebExchange exchange) {\n\t\tServerHttpResponse result = exchange.getResponse();\n\t\tresult.setStatusCode(HttpStatus.OK);\n\t\tresult.getHeaders().setContentType(MediaType.TEXT_HTML);\n\t\treturn result.writeWith(createBuffer(exchange));\n\t}\n\n\tprivate Mono<DataBuffer> createBuffer(ServerWebExchange exchange) {\n\t\tMono<CsrfToken> token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());\n\t\treturn token.map(LoginPageGeneratingWebFilter::csrfToken).defaultIfEmpty(\"\").map((csrfTokenHtmlInput) -> {\n\t\t\tbyte[] bytes = createPage(exchange, csrfTokenHtmlInput);\n\t\t\tDataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();\n\t\t\treturn bufferFactory.wrap(bytes);\n\t\t});\n\t}\n\n\tprivate byte[] createPage(ServerWebExchange exchange, String csrfTokenHtmlInput) {\n\t\tMultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();\n\t\tString contextPath = exchange.getRequest().getPath().contextPath().value();\n\n\t\treturn HtmlTemplates.fromTemplate(LOGIN_PAGE_TEMPLATE)\n\t\t\t.withRawHtml(\"contextPath\", contextPath)\n\t\t\t.withRawHtml(\"formLogin\", formLogin(queryParams, contextPath, csrfTokenHtmlInput))\n\t\t\t.withRawHtml(\"oneTimeTokenLogin\", renderOneTimeTokenLogin(queryParams, contextPath, csrfTokenHtmlInput))\n\t\t\t.withRawHtml(\"oauth2Login\", oauth2Login(queryParams, contextPath, this.oauth2AuthenticationUrlToClientName))\n\t\t\t.render()\n\t\t\t.getBytes(Charset.defaultCharset());\n\t}\n\n\tprivate String formLogin(MultiValueMap<String, String> queryParams, String contextPath, String csrfTokenHtmlInput) {\n\t\tif (!this.formLoginEnabled) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tboolean isError = queryParams.containsKey(\"error\");\n\t\tboolean isLogoutSuccess = queryParams.containsKey(\"logout\");\n\n\t\treturn HtmlTemplates.fromTemplate(LOGIN_FORM_TEMPLATE)\n\t\t\t.withValue(\"loginUrl\", contextPath + \"/login\")\n\t\t\t.withRawHtml(\"errorMessage\", createError(isError))\n\t\t\t.withRawHtml(\"logoutMessage\", createLogoutSuccess(isLogoutSuccess))\n\t\t\t.withRawHtml(\"csrf\", csrfTokenHtmlInput)\n\t\t\t.render();\n\t}\n\n\tprivate String renderOneTimeTokenLogin(MultiValueMap<String, String> queryParams, String contextPath,\n\t\t\tString csrfTokenHtmlInput) {\n\t\tif (!this.oneTimeTokenEnabled) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tboolean isError = queryParams.containsKey(\"error\");\n\t\tboolean isLogoutSuccess = queryParams.containsKey(\"logout\");\n\n\t\treturn HtmlTemplates.fromTemplate(ONE_TIME_TEMPLATE)\n\t\t\t.withValue(\"generateOneTimeTokenUrl\", contextPath + this.generateOneTimeTokenUrl)\n\t\t\t.withRawHtml(\"errorMessage\", createError(isError))\n\t\t\t.withRawHtml(\"logoutMessage\", createLogoutSuccess(isLogoutSuccess))\n\t\t\t.withRawHtml(\"csrf\", csrfTokenHtmlInput)\n\t\t\t.render();\n\t}\n\n\tprivate static String oauth2Login(MultiValueMap<String, String> queryParams, String contextPath,\n\t\t\tMap<String, String> oauth2AuthenticationUrlToClientName) {\n\t\tif (oauth2AuthenticationUrlToClientName.isEmpty()) {\n\t\t\treturn \"\";\n\t\t}\n\t\tboolean isError = queryParams.containsKey(\"error\");\n\n\t\tString oauth2Rows = oauth2AuthenticationUrlToClientName.entrySet()\n\t\t\t.stream()\n\t\t\t.map((urlToName) -> oauth2LoginLink(contextPath, urlToName.getKey(), urlToName.getValue()))\n\t\t\t.collect(Collectors.joining(\"\\n\"))\n\t\t\t.indent(2);\n\t\treturn HtmlTemplates.fromTemplate(OAUTH2_LOGIN_TEMPLATE)\n\t\t\t.withRawHtml(\"errorMessage\", createError(isError))\n\t\t\t.withRawHtml(\"oauth2Rows\", oauth2Rows)\n\t\t\t.render();\n\t}\n\n\tprivate static String oauth2LoginLink(String contextPath, String url, String clientName) {\n\t\treturn HtmlTemplates.fromTemplate(OAUTH2_ROW_TEMPLATE)\n\t\t\t.withValue(\"url\", contextPath + url)\n\t\t\t.withValue(\"clientName\", clientName)\n\t\t\t.render();\n\t}\n\n\tprivate static String csrfToken(CsrfToken token) {\n\t\treturn HtmlTemplates.fromTemplate(CSRF_INPUT_TEMPLATE)\n\t\t\t.withValue(\"name\", token.getParameterName())\n\t\t\t.withValue(\"value\", token.getToken())\n\t\t\t.render();\n\t}\n\n\tprivate static String createError(boolean isError) {\n\t\treturn isError ? \"<div class=\\\"alert alert-danger\\\" role=\\\"alert\\\">Invalid credentials</div>\" : \"\";\n\t}\n\n\tprivate static String createLogoutSuccess(boolean isLogoutSuccess) {\n\t\treturn isLogoutSuccess ? \"<div class=\\\"alert alert-success\\\" role=\\\"alert\\\">You have been signed out</div>\"\n\t\t\t\t: \"\";\n\t}\n\n\tprivate static final String LOGIN_PAGE_TEMPLATE = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <meta charset=\"utf-8\">\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t    <title>Please sign in</title>\n\t\t\t    <link href=\"{{contextPath}}/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t  </head>\n\t\t\t  <body>\n\t\t\t    <div class=\"content\">\n\t\t\t{{formLogin}}\n\t\t\t{{oneTimeTokenLogin}}\n\t\t\t{{oauth2Login}}\n\t\t\t    </div>\n\t\t\t  </body>\n\t\t\t</html>\"\"\";\n\n\tprivate static final String LOGIN_FORM_TEMPLATE = \"\"\"\n\t\t\t      <form class=\"login-form\" method=\"post\" action=\"{{loginUrl}}\">\n\t\t\t        <h2>Please sign in</h2>\n\t\t\t{{errorMessage}}{{logoutMessage}}\n\t\t\t        <p>\n\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t          <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n\t\t\t        </p>\n\t\t\t        <p>\n\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t          <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n\t\t\t        </p>\n\t\t\t{{csrf}}\n\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t      </form>\"\"\";\n\n\tprivate static final String CSRF_INPUT_TEMPLATE = \"\"\"\n\t\t\t<input name=\"{{name}}\" type=\"hidden\" value=\"{{value}}\" />\n\t\t\t\"\"\";\n\n\tprivate static final String OAUTH2_LOGIN_TEMPLATE = \"\"\"\n\t\t\t<h2>Login with OAuth 2.0</h2>\n\t\t\t{{errorMessage}}\n\t\t\t<table class=\"table table-striped\">\n\t\t\t{{oauth2Rows}}\n\t\t\t</table>\"\"\";\n\n\tprivate static final String OAUTH2_ROW_TEMPLATE = \"\"\"\n\t\t\t<tr><td><a href=\"{{url}}\">{{clientName}}</a></td></tr>\"\"\";\n\n\tprivate static final String ONE_TIME_TEMPLATE = \"\"\"\n\t\t\t      <form id=\"ott-form\" class=\"login-form\" method=\"post\" action=\"{{generateOneTimeTokenUrl}}\">\n\t\t\t        <h2>Request a One-Time Token</h2>\n\t\t\t      {{errorMessage}}{{logoutMessage}}\n\t\t\t        <p>\n\t\t\t          <label for=\"ott-username\" class=\"screenreader\">Username</label>\n\t\t\t          <input type=\"text\" id=\"ott-username\" name=\"username\" placeholder=\"Username\" required>\n\t\t\t        </p>\n\t\t\t        {{csrf}}\n\t\t\t        <button class=\"primary\" type=\"submit\" form=\"ott-form\">Send Token</button>\n\t\t\t      </form>\n\t\t\t\"\"\";\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ui/LogoutPageGeneratingWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport java.nio.charset.Charset;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.web.server.csrf.CsrfToken;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Generates a default log out page.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class LogoutPageGeneratingWebFilter implements WebFilter {\n\n\tprivate ServerWebExchangeMatcher matcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, \"/logout\");\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.matcher.matches(exchange)\n\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t.flatMap((matchResult) -> render(exchange));\n\t}\n\n\tprivate Mono<Void> render(ServerWebExchange exchange) {\n\t\tServerHttpResponse result = exchange.getResponse();\n\t\tresult.setStatusCode(HttpStatus.OK);\n\t\tresult.getHeaders().setContentType(MediaType.TEXT_HTML);\n\t\treturn result.writeWith(createBuffer(exchange));\n\t}\n\n\tprivate Mono<DataBuffer> createBuffer(ServerWebExchange exchange) {\n\t\tMono<CsrfToken> token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());\n\t\tString contextPath = exchange.getRequest().getPath().contextPath().value();\n\t\treturn token.map(LogoutPageGeneratingWebFilter::csrfToken).defaultIfEmpty(\"\").map((csrfTokenHtmlInput) -> {\n\t\t\tbyte[] bytes = createPage(csrfTokenHtmlInput, contextPath);\n\t\t\tDataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();\n\t\t\treturn bufferFactory.wrap(bytes);\n\t\t});\n\t}\n\n\tprivate static byte[] createPage(String csrfTokenHtmlInput, String contextPath) {\n\t\treturn HtmlTemplates.fromTemplate(LOGOUT_PAGE_TEMPLATE)\n\t\t\t.withValue(\"contextPath\", contextPath)\n\t\t\t.withRawHtml(\"csrf\", csrfTokenHtmlInput.indent(8))\n\t\t\t.render()\n\t\t\t.getBytes(Charset.defaultCharset());\n\t}\n\n\tprivate static String csrfToken(CsrfToken token) {\n\t\treturn HtmlTemplates.fromTemplate(CSRF_INPUT_TEMPLATE)\n\t\t\t.withValue(\"name\", token.getParameterName())\n\t\t\t.withValue(\"value\", token.getToken())\n\t\t\t.render();\n\t}\n\n\tprivate static final String LOGOUT_PAGE_TEMPLATE = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <meta charset=\"utf-8\">\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t    <title>Confirm Log Out?</title>\n\t\t\t    <link href=\"{{contextPath}}/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t  </head>\n\t\t\t  <body>\n\t\t\t    <div class=\"content\">\n\t\t\t      <form class=\"logout-form\" method=\"post\" action=\"{{contextPath}}/logout\">\n\t\t\t        <h2>Are you sure you want to log out?</h2>\n\t\t\t{{csrf}}\n\t\t\t        <button class=\"primary\" type=\"submit\">Log Out</button>\n\t\t\t      </form>\n\t\t\t    </div>\n\t\t\t  </body>\n\t\t\t</html>\"\"\";\n\n\tprivate static final String CSRF_INPUT_TEMPLATE = \"\"\"\n\t\t\t<input name=\"{{name}}\" type=\"hidden\" value=\"{{value}}\" />\n\t\t\t\"\"\";\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ui/OneTimeTokenSubmitPageGeneratingWebFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport java.nio.charset.Charset;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferFactory;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.security.web.server.csrf.CsrfToken;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\n/**\n * Creates a default one-time token submit page. If the request contains a {@code token}\n * query param the page will automatically fill the form with the token value.\n *\n * @author Max Batischev\n * @since 6.4\n */\npublic final class OneTimeTokenSubmitPageGeneratingWebFilter implements WebFilter {\n\n\tprivate ServerWebExchangeMatcher matcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, \"/login/ott\");\n\n\tprivate String loginProcessingUrl = \"/login/ott\";\n\n\t@Override\n\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\treturn this.matcher.matches(exchange)\n\t\t\t.filter(ServerWebExchangeMatcher.MatchResult::isMatch)\n\t\t\t.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))\n\t\t\t.flatMap((matchResult) -> render(exchange));\n\t}\n\n\tprivate Mono<Void> render(ServerWebExchange exchange) {\n\t\tServerHttpResponse result = exchange.getResponse();\n\t\tresult.setStatusCode(HttpStatus.OK);\n\t\tresult.getHeaders().setContentType(MediaType.TEXT_HTML);\n\t\treturn result.writeWith(createBuffer(exchange));\n\t}\n\n\tprivate Mono<DataBuffer> createBuffer(ServerWebExchange exchange) {\n\t\tMono<CsrfToken> token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());\n\t\treturn token.map(OneTimeTokenSubmitPageGeneratingWebFilter::csrfToken)\n\t\t\t.defaultIfEmpty(\"\")\n\t\t\t.map((csrfTokenHtmlInput) -> {\n\t\t\t\tbyte[] bytes = createPage(exchange, csrfTokenHtmlInput);\n\t\t\t\tDataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();\n\t\t\t\treturn bufferFactory.wrap(bytes);\n\t\t\t});\n\t}\n\n\tprivate byte[] createPage(ServerWebExchange exchange, String csrfTokenHtmlInput) {\n\t\tMultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();\n\t\tString token = queryParams.getFirst(\"token\");\n\t\tString tokenValue = StringUtils.hasText(token) ? token : \"\";\n\n\t\tString contextPath = exchange.getRequest().getPath().contextPath().value();\n\n\t\treturn HtmlTemplates.fromTemplate(ONE_TIME_TOKEN_SUBMIT_PAGE_TEMPLATE)\n\t\t\t.withRawHtml(\"contextPath\", contextPath)\n\t\t\t.withValue(\"tokenValue\", tokenValue)\n\t\t\t.withRawHtml(\"csrf\", csrfTokenHtmlInput.indent(8))\n\t\t\t.withValue(\"loginProcessingUrl\", contextPath + this.loginProcessingUrl)\n\t\t\t.render()\n\t\t\t.getBytes(Charset.defaultCharset());\n\t}\n\n\tprivate static String csrfToken(CsrfToken token) {\n\t\treturn HtmlTemplates.fromTemplate(CSRF_INPUT_TEMPLATE)\n\t\t\t.withValue(\"name\", token.getParameterName())\n\t\t\t.withValue(\"value\", token.getToken())\n\t\t\t.render();\n\t}\n\n\t/**\n\t * Use this {@link ServerWebExchangeMatcher} to choose whether this filter will handle\n\t * the request. By default, it handles {@code /login/ott}.\n\t * @param requestMatcher {@link ServerWebExchangeMatcher} to use\n\t */\n\tpublic void setRequestMatcher(ServerWebExchangeMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.matcher = requestMatcher;\n\t}\n\n\t/**\n\t * Specifies the URL that the submit form should POST to. Defaults to\n\t * {@code /login/ott}.\n\t * @param loginProcessingUrl\n\t */\n\tpublic void setLoginProcessingUrl(String loginProcessingUrl) {\n\t\tAssert.hasText(loginProcessingUrl, \"loginProcessingUrl cannot be null or empty\");\n\t\tthis.loginProcessingUrl = loginProcessingUrl;\n\t}\n\n\tprivate static final String ONE_TIME_TOKEN_SUBMIT_PAGE_TEMPLATE = \"\"\"\n\t\t\t<!DOCTYPE html>\n\t\t\t<html lang=\"en\">\n\t\t\t  <head>\n\t\t\t    <title>One-Time Token Login</title>\n\t\t\t    <meta charset=\"utf-8\"/>\n\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"/>\n\t\t\t    <link href=\"{{contextPath}}/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t  </head>\n\t\t\t  <body>\n\t\t\t    <div class=\"container\">\n\t\t\t      <form class=\"login-form\" action=\"{{loginProcessingUrl}}\" method=\"post\">\n\t\t\t        <h2>Please input the token</h2>\n\t\t\t        <p>\n\t\t\t          <label for=\"token\" class=\"screenreader\">Token</label>\n\t\t\t          <input type=\"text\" id=\"token\" name=\"token\" value=\"{{tokenValue}}\" placeholder=\"Token\" required=\"true\" autofocus=\"autofocus\"/>\n\t\t\t        </p>\n\t\t\t{{csrf}}\n\t\t\t        <button class=\"primary\" type=\"submit\">Sign in</button>\n\t\t\t      </form>\n\t\t\t    </div>\n\t\t\t  </body>\n\t\t\t</html>\n\t\t\t\"\"\";\n\n\tprivate static final String CSRF_INPUT_TEMPLATE = \"\"\"\n\t\t\t<input name=\"{{name}}\" type=\"hidden\" value=\"{{value}}\" />\n\t\t\t\"\"\";\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/ui/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Support for rendering UIs (e.g. default log in pages) in WebFlux.\n */\n@NullMarked\npackage org.springframework.security.web.server.ui;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/AndServerWebExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Matches if all the provided {@link ServerWebExchangeMatcher} match\n *\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n * @see OrServerWebExchangeMatcher\n */\npublic class AndServerWebExchangeMatcher implements ServerWebExchangeMatcher {\n\n\tprivate static final Log logger = LogFactory.getLog(AndServerWebExchangeMatcher.class);\n\n\tprivate final List<ServerWebExchangeMatcher> matchers;\n\n\tpublic AndServerWebExchangeMatcher(List<ServerWebExchangeMatcher> matchers) {\n\t\tAssert.notEmpty(matchers, \"matchers cannot be empty\");\n\t\tthis.matchers = matchers;\n\t}\n\n\tpublic AndServerWebExchangeMatcher(ServerWebExchangeMatcher... matchers) {\n\t\tthis(Arrays.asList(matchers));\n\t}\n\n\t@Override\n\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\treturn Mono.defer(() -> {\n\t\t\tMap<String, Object> variables = new HashMap<>();\n\t\t\treturn Flux.fromIterable(this.matchers)\n\t\t\t\t.doOnNext((matcher) -> logger.debug(LogMessage.format(\"Trying to match using %s\", matcher)))\n\t\t\t\t.flatMap((matcher) -> matcher.matches(exchange))\n\t\t\t\t.doOnNext((matchResult) -> variables.putAll(matchResult.getVariables()))\n\t\t\t\t.all(MatchResult::isMatch)\n\t\t\t\t.flatMap((allMatch) -> allMatch ? MatchResult.match(variables) : MatchResult.notMatch())\n\t\t\t\t.doOnNext((matchResult) -> logger\n\t\t\t\t\t.debug(matchResult.isMatch() ? \"All requestMatchers returned true\" : \"Did not match\"));\n\t\t});\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AndServerWebExchangeMatcher{\" + \"matchers=\" + this.matchers + '}';\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/IpAddressServerWebExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.List;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.util.matcher.InetAddressMatcher;\nimport org.springframework.security.web.util.matcher.InetAddressMatchers;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Matches a request based on IP Address or subnet mask matching against the remote\n * address.\n *\n * @author Guirong Hu\n * @since 5.7\n */\npublic final class IpAddressServerWebExchangeMatcher implements ServerWebExchangeMatcher {\n\n\tprivate final InetAddressMatcher ipAddressMatcher;\n\n\t/**\n\t * Takes a specific IP address or a range specified using the IP/Netmask (e.g.\n\t * 192.168.1.0/24 or 202.24.0.0/14).\n\t * @param ipAddress the address or range of addresses from which the request must\n\t * come.\n\t */\n\tpublic IpAddressServerWebExchangeMatcher(String ipAddress) {\n\t\tAssert.hasText(ipAddress, \"IP address cannot be empty\");\n\t\tthis.ipAddressMatcher = InetAddressMatchers.builder().includeAddresses(List.of(ipAddress)).build();\n\t}\n\n\t@Override\n\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\t// @formatter:off\n\t\treturn Mono.justOrEmpty(exchange.getRequest().getRemoteAddress())\n\t\t\t\t.map((remoteAddress) -> remoteAddress.isUnresolved() ? remoteAddress.getHostString() : remoteAddress.getAddress().getHostAddress())\n\t\t\t\t.map(this.ipAddressMatcher::matches)\n\t\t\t\t.flatMap((matches) -> matches ? MatchResult.match() : MatchResult.notMatch())\n\t\t\t\t.switchIfEmpty(MatchResult.notMatch());\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"IpAddressServerWebExchangeMatcher{ipAddressMatcher=\" + this.ipAddressMatcher + '}';\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/MediaTypeServerWebExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.InvalidMediaTypeException;\nimport org.springframework.http.MediaType;\nimport org.springframework.util.Assert;\nimport org.springframework.util.MimeTypeUtils;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.server.NotAcceptableStatusException;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Matches based upon the accept headers.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class MediaTypeServerWebExchangeMatcher implements ServerWebExchangeMatcher {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final Collection<MediaType> matchingMediaTypes;\n\n\tprivate boolean useEquals;\n\n\tprivate Set<MediaType> ignoredMediaTypes = Collections.emptySet();\n\n\t/**\n\t * Creates a new instance\n\t * @param matchingMediaTypes the types to match on\n\t */\n\tpublic MediaTypeServerWebExchangeMatcher(MediaType... matchingMediaTypes) {\n\t\tAssert.notEmpty(matchingMediaTypes, \"matchingMediaTypes cannot be null\");\n\t\tAssert.noNullElements(matchingMediaTypes, \"matchingMediaTypes cannot contain null\");\n\t\tthis.matchingMediaTypes = Arrays.asList(matchingMediaTypes);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param matchingMediaTypes the types to match on\n\t */\n\tpublic MediaTypeServerWebExchangeMatcher(Collection<MediaType> matchingMediaTypes) {\n\t\tAssert.notEmpty(matchingMediaTypes, \"matchingMediaTypes cannot be null\");\n\t\tAssert.noNullElements(matchingMediaTypes,\n\t\t\t\t() -> \"matchingMediaTypes cannot contain null. Got \" + matchingMediaTypes);\n\t\tthis.matchingMediaTypes = matchingMediaTypes;\n\t}\n\n\t@Override\n\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\tList<MediaType> httpRequestMediaTypes;\n\t\ttry {\n\t\t\thttpRequestMediaTypes = resolveMediaTypes(exchange);\n\t\t}\n\t\tcatch (NotAcceptableStatusException ex) {\n\t\t\tthis.logger.debug(\"Failed to parse MediaTypes, returning false\", ex);\n\t\t\treturn MatchResult.notMatch();\n\t\t}\n\t\tthis.logger.debug(LogMessage.format(\"httpRequestMediaTypes=%s\", httpRequestMediaTypes));\n\t\tfor (MediaType httpRequestMediaType : httpRequestMediaTypes) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Processing %s\", httpRequestMediaType));\n\t\t\tif (shouldIgnore(httpRequestMediaType)) {\n\t\t\t\tthis.logger.debug(\"Ignoring\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (this.useEquals) {\n\t\t\t\tboolean isEqualTo = this.matchingMediaTypes.contains(httpRequestMediaType);\n\t\t\t\tthis.logger.debug(\"isEqualTo \" + isEqualTo);\n\t\t\t\treturn isEqualTo ? MatchResult.match() : MatchResult.notMatch();\n\t\t\t}\n\t\t\tfor (MediaType matchingMediaType : this.matchingMediaTypes) {\n\t\t\t\tboolean isCompatibleWith = matchingMediaType.isCompatibleWith(httpRequestMediaType);\n\t\t\t\tthis.logger.debug(LogMessage.format(\"%s .isCompatibleWith %s = %s\", matchingMediaType,\n\t\t\t\t\t\thttpRequestMediaType, isCompatibleWith));\n\t\t\t\tif (isCompatibleWith) {\n\t\t\t\t\treturn MatchResult.match();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(\"Did not match any media types\");\n\t\treturn MatchResult.notMatch();\n\t}\n\n\tprivate boolean shouldIgnore(MediaType httpRequestMediaType) {\n\t\tfor (MediaType ignoredMediaType : this.ignoredMediaTypes) {\n\t\t\tif (httpRequestMediaType.includes(ignoredMediaType)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * If set to true, matches on exact {@link MediaType}, else uses\n\t * {@link MediaType#isCompatibleWith(MediaType)}.\n\t * @param useEquals specify if equals comparison should be used.\n\t */\n\tpublic void setUseEquals(boolean useEquals) {\n\t\tthis.useEquals = useEquals;\n\t}\n\n\t/**\n\t * Set the {@link MediaType} to ignore from the {@link ContentNegotiationStrategy}.\n\t * This is useful if for example, you want to match on\n\t * {@link MediaType#APPLICATION_JSON} but want to ignore {@link MediaType#ALL}.\n\t * @param ignoredMediaTypes the {@link MediaType}'s to ignore from the\n\t * {@link ContentNegotiationStrategy}\n\t */\n\tpublic void setIgnoredMediaTypes(Set<MediaType> ignoredMediaTypes) {\n\t\tthis.ignoredMediaTypes = ignoredMediaTypes;\n\t}\n\n\tprivate List<MediaType> resolveMediaTypes(ServerWebExchange exchange) throws NotAcceptableStatusException {\n\t\ttry {\n\t\t\tList<MediaType> mediaTypes = exchange.getRequest().getHeaders().getAccept();\n\t\t\tMimeTypeUtils.sortBySpecificity(mediaTypes);\n\t\t\treturn mediaTypes;\n\t\t}\n\t\tcatch (InvalidMediaTypeException ex) {\n\t\t\tString value = exchange.getRequest().getHeaders().getFirst(\"Accept\");\n\t\t\tthrow new NotAcceptableStatusException(\n\t\t\t\t\t\"Could not parse 'Accept' header [\" + value + \"]: \" + ex.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"MediaTypeRequestMatcher [matchingMediaTypes=\" + this.matchingMediaTypes + \", useEquals=\"\n\t\t\t\t+ this.useEquals + \", ignoredMediaTypes=\" + this.ignoredMediaTypes + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/NegatedServerWebExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Negates the provided matcher. If the provided matcher returns true, then the result\n * will be false. If the provided matcher returns false, then the result will be true.\n *\n * @author Tao Qian\n * @author Mathieu Ouellet\n * @since 5.1\n */\npublic class NegatedServerWebExchangeMatcher implements ServerWebExchangeMatcher {\n\n\tprivate static final Log logger = LogFactory.getLog(NegatedServerWebExchangeMatcher.class);\n\n\tprivate final ServerWebExchangeMatcher matcher;\n\n\tpublic NegatedServerWebExchangeMatcher(ServerWebExchangeMatcher matcher) {\n\t\tAssert.notNull(matcher, \"matcher cannot be null\");\n\t\tthis.matcher = matcher;\n\t}\n\n\t@Override\n\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\treturn this.matcher.matches(exchange)\n\t\t\t.flatMap(this::negate)\n\t\t\t.doOnNext((matchResult) -> logger.debug(LogMessage.format(\"matches = %s\", matchResult.isMatch())));\n\t}\n\n\tprivate Mono<MatchResult> negate(MatchResult matchResult) {\n\t\treturn matchResult.isMatch() ? MatchResult.notMatch() : MatchResult.match();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"NegatedServerWebExchangeMatcher{\" + \"matcher=\" + this.matcher + '}';\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/OrServerWebExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * Matches if any of the provided {@link ServerWebExchangeMatcher} match\n *\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n * @see AndServerWebExchangeMatcher\n */\npublic class OrServerWebExchangeMatcher implements ServerWebExchangeMatcher {\n\n\tprivate static final Log logger = LogFactory.getLog(OrServerWebExchangeMatcher.class);\n\n\tprivate final List<ServerWebExchangeMatcher> matchers;\n\n\tpublic OrServerWebExchangeMatcher(List<ServerWebExchangeMatcher> matchers) {\n\t\tAssert.notEmpty(matchers, \"matchers cannot be empty\");\n\t\tthis.matchers = matchers;\n\t}\n\n\tpublic OrServerWebExchangeMatcher(ServerWebExchangeMatcher... matchers) {\n\t\tthis(Arrays.asList(matchers));\n\t}\n\n\t@Override\n\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\treturn Flux.fromIterable(this.matchers)\n\t\t\t.doOnNext((matcher) -> logger.debug(LogMessage.format(\"Trying to match using %s\", matcher)))\n\t\t\t.flatMap((matcher) -> matcher.matches(exchange))\n\t\t\t.filter(MatchResult::isMatch)\n\t\t\t.next()\n\t\t\t.switchIfEmpty(MatchResult.notMatch())\n\t\t\t.doOnNext((matchResult) -> logger.debug(matchResult.isMatch() ? \"matched\" : \"No matches found\"));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"OrServerWebExchangeMatcher{matchers=\" + this.matchers + '}';\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/PathPatternParserServerWebExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.server.PathContainer;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.util.Assert;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.util.pattern.PathPattern;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\n/**\n * Matches if the {@link PathPattern} matches the path within the application.\n *\n * @author Rob Winch\n * @author Mathieu Ouellet\n * @since 5.0\n */\npublic final class PathPatternParserServerWebExchangeMatcher implements ServerWebExchangeMatcher {\n\n\tprivate static final Log logger = LogFactory.getLog(PathPatternParserServerWebExchangeMatcher.class);\n\n\tprivate final PathPattern pattern;\n\n\tprivate final @Nullable HttpMethod method;\n\n\tpublic PathPatternParserServerWebExchangeMatcher(PathPattern pattern) {\n\t\tthis(pattern, null);\n\t}\n\n\tpublic PathPatternParserServerWebExchangeMatcher(PathPattern pattern, @Nullable HttpMethod method) {\n\t\tAssert.notNull(pattern, \"pattern cannot be null\");\n\t\tthis.pattern = pattern;\n\t\tthis.method = method;\n\t}\n\n\tpublic PathPatternParserServerWebExchangeMatcher(String pattern, @Nullable HttpMethod method) {\n\t\tAssert.notNull(pattern, \"pattern cannot be null\");\n\t\tthis.pattern = parse(pattern);\n\t\tthis.method = method;\n\t}\n\n\tpublic PathPatternParserServerWebExchangeMatcher(String pattern) {\n\t\tthis(pattern, null);\n\t}\n\n\tprivate PathPattern parse(String pattern) {\n\t\tPathPatternParser parser = PathPatternParser.defaultInstance;\n\t\tpattern = parser.initFullPathPattern(pattern);\n\t\treturn parser.parse(pattern);\n\t}\n\n\t@Override\n\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\tServerHttpRequest request = exchange.getRequest();\n\t\tPathContainer path = request.getPath().pathWithinApplication();\n\t\tif (this.method != null && !this.method.equals(request.getMethod())) {\n\t\t\treturn MatchResult.notMatch().doOnNext((result) -> {\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"Request '\" + request.getMethod() + \" \" + path + \"' doesn't match '\" + this.method\n\t\t\t\t\t\t\t+ \" \" + this.pattern.getPatternString() + \"'\");\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tPathPattern.PathMatchInfo pathMatchInfo = this.pattern.matchAndExtract(path);\n\t\tif (pathMatchInfo == null) {\n\t\t\treturn MatchResult.notMatch().doOnNext((result) -> {\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"Request '\" + request.getMethod() + \" \" + path + \"' doesn't match '\" + this.method\n\t\t\t\t\t\t\t+ \" \" + this.pattern.getPatternString() + \"'\");\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tMap<String, String> pathVariables = pathMatchInfo.getUriVariables();\n\t\tMap<String, Object> variables = new HashMap<>(pathVariables);\n\t\tif (logger.isDebugEnabled()) {\n\t\t\tlogger\n\t\t\t\t.debug(\"Checking match of request : '\" + path + \"'; against '\" + this.pattern.getPatternString() + \"'\");\n\t\t}\n\t\treturn MatchResult.match(variables);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"PathMatcherServerWebExchangeMatcher{\" + \"pattern='\" + this.pattern + '\\'' + \", method=\" + this.method\n\t\t\t\t+ '}';\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * An interface for determining if a {@link ServerWebExchangeMatcher} matches.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic interface ServerWebExchangeMatcher {\n\n\t/**\n\t * Determines if a request matches or not\n\t * @param exchange\n\t * @return\n\t */\n\tMono<MatchResult> matches(ServerWebExchange exchange);\n\n\t/**\n\t * The result of matching\n\t */\n\tclass MatchResult {\n\n\t\tprivate final boolean match;\n\n\t\tprivate final Map<String, Object> variables;\n\n\t\tprivate MatchResult(boolean match, Map<String, Object> variables) {\n\t\t\tthis.match = match;\n\t\t\tthis.variables = variables;\n\t\t}\n\n\t\tpublic boolean isMatch() {\n\t\t\treturn this.match;\n\t\t}\n\n\t\t/**\n\t\t * Gets potential variables and their values\n\t\t * @return\n\t\t */\n\t\tpublic Map<String, Object> getVariables() {\n\t\t\treturn this.variables;\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is a match with no variables\n\t\t * @return\n\t\t */\n\t\tpublic static Mono<MatchResult> match() {\n\t\t\treturn match(Collections.emptyMap());\n\t\t}\n\n\t\t/**\n\t\t *\n\t\t * Creates an instance of {@link MatchResult} that is a match with the specified\n\t\t * variables\n\t\t * @param variables\n\t\t * @return\n\t\t */\n\t\tpublic static Mono<MatchResult> match(Map<String, Object> variables) {\n\t\t\treturn Mono.just(new MatchResult(true, variables));\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is not a match.\n\t\t * @return\n\t\t */\n\t\tpublic static Mono<MatchResult> notMatch() {\n\t\t\treturn Mono.just(new MatchResult(false, Collections.emptyMap()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatcherEntry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\n/**\n * A rich object for associating a {@link ServerWebExchangeMatcher} to another object.\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class ServerWebExchangeMatcherEntry<T> {\n\n\tprivate final ServerWebExchangeMatcher matcher;\n\n\tprivate final T entry;\n\n\tpublic ServerWebExchangeMatcherEntry(ServerWebExchangeMatcher matcher, T entry) {\n\t\tthis.matcher = matcher;\n\t\tthis.entry = entry;\n\t}\n\n\tpublic ServerWebExchangeMatcher getMatcher() {\n\t\treturn this.matcher;\n\t}\n\n\tpublic T getEntry() {\n\t\treturn this.entry;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.util.pattern.PathPattern;\n\n/**\n * Provides factory methods for creating common {@link ServerWebExchangeMatcher}\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic abstract class ServerWebExchangeMatchers {\n\n\tprivate ServerWebExchangeMatchers() {\n\t}\n\n\t/**\n\t * Creates a matcher that matches on the specific method and any of the provided\n\t * patterns.\n\t * @param method the method to match on. If null, any method will be matched\n\t * @param patterns the patterns to match on\n\t * @return the matcher to use\n\t */\n\tpublic static ServerWebExchangeMatcher pathMatchers(@Nullable HttpMethod method, String... patterns) {\n\t\tList<ServerWebExchangeMatcher> matchers = new ArrayList<>(patterns.length);\n\t\tfor (String pattern : patterns) {\n\t\t\tmatchers.add(new PathPatternParserServerWebExchangeMatcher(pattern, method));\n\t\t}\n\t\treturn new OrServerWebExchangeMatcher(matchers);\n\t}\n\n\t/**\n\t * Creates a matcher that matches on any of the provided patterns.\n\t * @param patterns the patterns to match on\n\t * @return the matcher to use\n\t */\n\tpublic static ServerWebExchangeMatcher pathMatchers(String... patterns) {\n\t\treturn pathMatchers(null, patterns);\n\t}\n\n\t/**\n\t * Creates a matcher that matches on any of the provided {@link PathPattern}s.\n\t * @param pathPatterns the {@link PathPattern}s to match on\n\t * @return the matcher to use\n\t */\n\tpublic static ServerWebExchangeMatcher pathMatchers(PathPattern... pathPatterns) {\n\t\treturn pathMatchers(null, pathPatterns);\n\t}\n\n\t/**\n\t * Creates a matcher that matches on the specific method and any of the provided\n\t * {@link PathPattern}s.\n\t * @param method the method to match on. If null, any method will be matched.\n\t * @param pathPatterns the {@link PathPattern}s to match on\n\t * @return the matcher to use\n\t */\n\tpublic static ServerWebExchangeMatcher pathMatchers(@Nullable HttpMethod method, PathPattern... pathPatterns) {\n\t\tList<ServerWebExchangeMatcher> matchers = new ArrayList<>(pathPatterns.length);\n\t\tfor (PathPattern pathPattern : pathPatterns) {\n\t\t\tmatchers.add(new PathPatternParserServerWebExchangeMatcher(pathPattern, method));\n\t\t}\n\t\treturn new OrServerWebExchangeMatcher(matchers);\n\t}\n\n\t/**\n\t * Creates a matcher that will match on any of the provided matchers\n\t * @param matchers the matchers to match on\n\t * @return the matcher to use\n\t */\n\tpublic static ServerWebExchangeMatcher matchers(ServerWebExchangeMatcher... matchers) {\n\t\treturn new OrServerWebExchangeMatcher(matchers);\n\t}\n\n\t/**\n\t * Matches any exchange\n\t * @return the matcher to use\n\t */\n\t@SuppressWarnings(\"Convert2Lambda\")\n\tpublic static ServerWebExchangeMatcher anyExchange() {\n\t\t// we don't use a lambda to ensure a unique equals and hashcode\n\t\t// which otherwise can cause problems with adding multiple entries to an ordered\n\t\t// LinkedHashMap\n\t\treturn new ServerWebExchangeMatcher() {\n\n\t\t\t@Override\n\t\t\tpublic Mono<MatchResult> matches(ServerWebExchange exchange) {\n\t\t\t\treturn ServerWebExchangeMatcher.MatchResult.match();\n\t\t\t}\n\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/server/util/matcher/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Reactive APIs for matching requests which are used for, among other things, mapping\n * authorization rules.\n */\n@NullMarked\npackage org.springframework.security.web.server.util.matcher;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servlet/support/csrf/CsrfRequestDataValueProcessor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servlet.support.csrf;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.web.servlet.support.RequestDataValueProcessor;\n\n/**\n * Integration with Spring Web MVC that automatically adds the {@link CsrfToken} into\n * forms with hidden inputs when using Spring tag libraries.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class CsrfRequestDataValueProcessor implements RequestDataValueProcessor {\n\n\tprivate Pattern DISABLE_CSRF_TOKEN_PATTERN = Pattern.compile(\"(?i)^(GET|HEAD|TRACE|OPTIONS)$\");\n\n\tprivate String DISABLE_CSRF_TOKEN_ATTR = \"DISABLE_CSRF_TOKEN_ATTR\";\n\n\tpublic String processAction(HttpServletRequest request, String action) {\n\t\treturn action;\n\t}\n\n\t@Override\n\tpublic String processAction(HttpServletRequest request, String action, String method) {\n\t\tif (method != null && this.DISABLE_CSRF_TOKEN_PATTERN.matcher(method).matches()) {\n\t\t\trequest.setAttribute(this.DISABLE_CSRF_TOKEN_ATTR, Boolean.TRUE);\n\t\t}\n\t\telse {\n\t\t\trequest.removeAttribute(this.DISABLE_CSRF_TOKEN_ATTR);\n\t\t}\n\t\treturn action;\n\t}\n\n\t@Override\n\tpublic String processFormFieldValue(HttpServletRequest request, @Nullable String name, String value, String type) {\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic Map<String, String> getExtraHiddenFields(HttpServletRequest request) {\n\t\tif (Boolean.TRUE.equals(request.getAttribute(this.DISABLE_CSRF_TOKEN_ATTR))) {\n\t\t\trequest.removeAttribute(this.DISABLE_CSRF_TOKEN_ATTR);\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tCsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tif (token == null) {\n\t\t\treturn Collections.emptyMap();\n\t\t}\n\t\tMap<String, String> hiddenFields = new HashMap<>(1);\n\t\thiddenFields.put(token.getParameterName(), token.getToken());\n\t\treturn hiddenFields;\n\t}\n\n\t@Override\n\tpublic String processUrl(HttpServletRequest request, String url) {\n\t\treturn url;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servlet/support/csrf/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * CSRF support classes for Spring's web MVC framework. Provides easy evaluation of the\n * request context in views, and miscellaneous HandlerInterceptor implementations.\n */\n@NullMarked\npackage org.springframework.security.web.servlet.support.csrf;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servlet/util/matcher/PathPatternRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servlet.util.matcher;\n\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.server.PathContainer;\nimport org.springframework.http.server.RequestPath;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.util.ServletRequestPathUtils;\nimport org.springframework.web.util.pattern.PathPattern;\nimport org.springframework.web.util.pattern.PathPatternParser;\n\n/**\n * A {@link RequestMatcher} that uses {@link PathPattern}s to match against each\n * {@link HttpServletRequest}. The provided path should be relative to the context path\n * (that is, it should exclude any context path).\n *\n * <p>\n * You can provide the servlet path in {@link PathPatternRequestMatcher.Builder#basePath}\n * and reuse for multiple matchers.\n *\n * <p>\n * Note that the {@link org.springframework.web.servlet.HandlerMapping} that contains the\n * related URI patterns must be using {@link PathPatternParser#defaultInstance}. If that\n * is not the case, use {@link PathPatternParser} to parse your path and provide a\n * {@link PathPattern} in the constructor.\n * </p>\n *\n * @author Josh Cummings\n * @author Andrey Litvitski\n * @since 6.5\n */\npublic final class PathPatternRequestMatcher implements RequestMatcher {\n\n\tprivate final PathPattern pattern;\n\n\tprivate final RequestMatcher method;\n\n\t/**\n\t * Creates a {@link PathPatternRequestMatcher} that uses the provided {@code pattern}.\n\t * <p>\n\t * The {@code pattern} should be relative to the context path\n\t * </p>\n\t * @param pattern the pattern used to match\n\t */\n\tprivate PathPatternRequestMatcher(PathPattern pattern, RequestMatcher method) {\n\t\tthis.pattern = pattern;\n\t\tthis.method = method;\n\t}\n\n\t/**\n\t * Construct a {@link PathPatternRequestMatcher} using the {@link PathPatternParser}\n\t * defaults.\n\t * <p>\n\t * If you are configuring a custom {@link PathPatternParser}, please use\n\t * {@link #withPathPatternParser} instead.\n\t * @param pattern the URI pattern to match\n\t * @return a {@link PathPatternRequestMatcher} that matches requests to the given\n\t * {@code pattern}\n\t * @since 7.0\n\t * @see PathPattern\n\t */\n\tpublic static PathPatternRequestMatcher pathPattern(String pattern) {\n\t\treturn pathPattern(null, pattern);\n\t}\n\n\t/**\n\t * Construct a {@link PathPatternRequestMatcher} using the {@link PathPatternParser}\n\t * defaults.\n\t * <p>\n\t * If you are configuring a custom {@link PathPatternParser}, please use\n\t * {@link #withPathPatternParser} instead.\n\t * @param method the HTTP method to match, {@code null} indicates that the method does\n\t * not matter\n\t * @param pattern the URI pattern to match\n\t * @return a {@link PathPatternRequestMatcher} that matches requests to the given\n\t * {@code pattern} and {@code method}\n\t * @since 7.0\n\t * @see PathPattern\n\t */\n\tpublic static PathPatternRequestMatcher pathPattern(@Nullable HttpMethod method, String pattern) {\n\t\treturn withDefaults().matcher(method, pattern);\n\t}\n\n\t/**\n\t * Use {@link PathPatternParser#defaultInstance} to parse path patterns.\n\t * @return a {@link Builder} that treats URIs as relative to the context path, if any\n\t */\n\tpublic static Builder withDefaults() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Use this {@link PathPatternParser} to parse path patterns.\n\t * @param parser the {@link PathPatternParser} to use\n\t * @return a {@link Builder} that treats URIs as relative to the given\n\t * {@code servletPath}\n\t */\n\tpublic static Builder withPathPatternParser(PathPatternParser parser) {\n\t\tAssert.notNull(parser, \"pathPatternParser cannot be null\");\n\t\treturn new Builder(parser);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\treturn matcher(request).isMatch();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic MatchResult matcher(HttpServletRequest request) {\n\t\tif (!this.method.matches(request)) {\n\t\t\treturn MatchResult.notMatch();\n\t\t}\n\t\tPathContainer path = getPathContainer(request);\n\t\tPathPattern.PathMatchInfo info = this.pattern.matchAndExtract(path);\n\t\treturn (info != null) ? MatchResult.match(info.getUriVariables()) : MatchResult.notMatch();\n\t}\n\n\tprivate PathContainer getPathContainer(HttpServletRequest request) {\n\t\tRequestPath path;\n\t\tif (ServletRequestPathUtils.hasParsedRequestPath(request)) {\n\t\t\tpath = ServletRequestPathUtils.getParsedRequestPath(request);\n\t\t}\n\t\telse {\n\t\t\tpath = ServletRequestPathUtils.parseAndCache(request);\n\t\t\tServletRequestPathUtils.clearParsedRequestPath(request);\n\t\t}\n\t\tPathContainer contextPath = path.contextPath();\n\t\treturn path.subPath(contextPath.elements().size());\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (!(o instanceof PathPatternRequestMatcher that)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(this.pattern, that.pattern) && Objects.equals(this.method, that.method);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.pattern, this.method);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder request = new StringBuilder();\n\t\tif (this.method instanceof HttpMethodRequestMatcher m) {\n\t\t\trequest.append(m.method.name()).append(' ');\n\t\t}\n\t\treturn \"PathPattern [\" + request + this.pattern + \"]\";\n\t}\n\n\t/**\n\t * A builder for specifying various elements of a request for the purpose of creating\n\t * a {@link PathPatternRequestMatcher}.\n\t *\n\t * <p>\n\t * To match a request URI like {@code /app/servlet/my/resource/**} where {@code /app}\n\t * is the context path, you can do\n\t * {@code PathPatternRequestMatcher.pathPattern(\"/servlet/my/resource/**\")}\n\t *\n\t * <p>\n\t * If you have many paths that have a common path prefix, you can use\n\t * {@link #basePath} to reduce repetition like so:\n\t *\n\t * <pre>\n\t *     PathPatternRequestMatcher.Builder mvc = withDefaults().basePath(\"/mvc\");\n\t *     http\n\t *         .authorizeHttpRequests((authorize) -> authorize\n\t *              .requestMatchers(mvc.matcher(\"/user/**\")).hasAuthority(\"user\")\n\t *              .requestMatchers(mvc.matcher(\"/admin/**\")).hasAuthority(\"admin\")\n\t *         )\n\t *             ...\n\t * </pre>\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final PathPatternParser parser;\n\n\t\tprivate final String basePath;\n\n\t\tBuilder() {\n\t\t\tthis(PathPatternParser.defaultInstance);\n\t\t}\n\n\t\tBuilder(PathPatternParser parser) {\n\t\t\tthis(parser, \"\");\n\t\t}\n\n\t\tBuilder(PathPatternParser parser, String basePath) {\n\t\t\tthis.parser = parser;\n\t\t\tthis.basePath = basePath;\n\t\t}\n\n\t\t/**\n\t\t * Match requests starting with this {@code basePath}.\n\t\t *\n\t\t * <p>\n\t\t * Prefixes should be of the form {@code /my/prefix}, starting with a slash, not\n\t\t * ending in a slash, and not containing and wildcards The special value\n\t\t * {@code \"/\"} may be used to indicate the root context.\n\t\t * @param basePath the path prefix\n\t\t * @return the {@link Builder} for more configuration\n\t\t */\n\t\tpublic Builder basePath(String basePath) {\n\t\t\tAssert.notNull(basePath, \"basePath cannot be null\");\n\t\t\tAssert.isTrue(basePath.startsWith(\"/\"), \"basePath must start with '/'\");\n\t\t\tAssert.isTrue(\"/\".equals(basePath) || !basePath.endsWith(\"/\"), \"basePath must not end with a slash\");\n\t\t\tAssert.isTrue(!basePath.contains(\"*\"), \"basePath must not contain a star\");\n\t\t\treturn new Builder(this.parser, basePath);\n\t\t}\n\n\t\t/**\n\t\t * Match requests having this path pattern.\n\t\t *\n\t\t * <p>\n\t\t * When the HTTP {@code method} is null, then the matcher does not consider the\n\t\t * HTTP method\n\t\t *\n\t\t * <p>\n\t\t * Path patterns always start with a slash and may contain placeholders. They can\n\t\t * also be followed by {@code /**} to signify all URIs under a given path.\n\t\t *\n\t\t * <p>\n\t\t * These must be specified relative to any context path prefix. A\n\t\t * {@link #basePath} may be specified to reuse a common prefix, for example a\n\t\t * servlet path.\n\t\t *\n\t\t * <p>\n\t\t * The following are valid patterns and their meaning\n\t\t * <ul>\n\t\t * <li>{@code /path} - match exactly and only `/path`</li>\n\t\t * <li>{@code /path/**} - match `/path` and any of its descendants</li>\n\t\t * <li>{@code /path/{value}/**} - match `/path/subdirectory` and any of its\n\t\t * descendants, capturing the value of the subdirectory in\n\t\t * {@link RequestAuthorizationContext#getVariables()}</li>\n\t\t * </ul>\n\t\t *\n\t\t * <p>\n\t\t * A more comprehensive list can be found at {@link PathPattern}.\n\t\t * @param path the path pattern to match\n\t\t * @return the {@link Builder} for more configuration\n\t\t */\n\t\tpublic PathPatternRequestMatcher matcher(String path) {\n\t\t\treturn matcher(null, path);\n\t\t}\n\n\t\t/**\n\t\t * Match requests having this {@link HttpMethod} and path pattern.\n\t\t *\n\t\t * <p>\n\t\t * When the HTTP {@code method} is null, then the matcher does not consider the\n\t\t * HTTP method\n\t\t *\n\t\t * <p>\n\t\t * Path patterns always start with a slash and may contain placeholders. They can\n\t\t * also be followed by {@code /**} to signify all URIs under a given path.\n\t\t *\n\t\t * <p>\n\t\t * These must be specified relative to any context path prefix. A\n\t\t * {@link #basePath} may be specified to reuse a common prefix, for example a\n\t\t * servlet path.\n\t\t *\n\t\t * <p>\n\t\t * The following are valid patterns and their meaning\n\t\t * <ul>\n\t\t * <li>{@code /path} - match exactly and only `/path`</li>\n\t\t * <li>{@code /path/**} - match `/path` and any of its descendants</li>\n\t\t * <li>{@code /path/{value}/**} - match `/path/subdirectory` and any of its\n\t\t * descendants, capturing the value of the subdirectory in\n\t\t * {@link RequestAuthorizationContext#getVariables()}</li>\n\t\t * </ul>\n\t\t *\n\t\t * <p>\n\t\t * A more comprehensive list can be found at {@link PathPattern}.\n\t\t * @param method the {@link HttpMethod} to match, may be null\n\t\t * @param path the path pattern to match\n\t\t * @return the {@link Builder} for more configuration\n\t\t */\n\t\tpublic PathPatternRequestMatcher matcher(@Nullable HttpMethod method, String path) {\n\t\t\tAssert.notNull(path, \"pattern cannot be null\");\n\t\t\tAssert.isTrue(path.startsWith(\"/\"), \"pattern must start with a /\");\n\t\t\tString prefix = (\"/\".equals(this.basePath)) ? \"\" : this.basePath;\n\t\t\tPathPattern pathPattern = this.parser.parse(prefix + path);\n\t\t\treturn new PathPatternRequestMatcher(pathPattern,\n\t\t\t\t\t(method != null) ? new HttpMethodRequestMatcher(method) : AnyRequestMatcher.INSTANCE);\n\t\t}\n\n\t}\n\n\tprivate static final class HttpMethodRequestMatcher implements RequestMatcher {\n\n\t\tprivate final HttpMethod method;\n\n\t\tHttpMethodRequestMatcher(HttpMethod method) {\n\t\t\tthis.method = method;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(HttpServletRequest request) {\n\t\t\treturn this.method.name().equals(request.getMethod());\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"HttpMethod [\" + this.method + \"]\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servlet/util/matcher/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Integration with Spring Framework's support for matching HTTP request paths.\n */\n@NullMarked\npackage org.springframework.security.web.servlet.util.matcher;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servletapi/HttpServlet3RequestFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servletapi;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport jakarta.servlet.AsyncContext;\nimport jakarta.servlet.AsyncListener;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.concurrent.DelegatingSecurityContextRunnable;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * Provides integration with the Servlet 3 APIs. The additional methods that are\n * integrated with can be found below:\n *\n * <ul>\n * <li>{@link HttpServletRequest#authenticate(HttpServletResponse)} - Allows the user to\n * determine if they are authenticated and if not send the user to the login page. See\n * {@link #setAuthenticationEntryPoint(AuthenticationEntryPoint)}.</li>\n * <li>{@link HttpServletRequest#login(String, String)} - Allows the user to authenticate\n * using the {@link AuthenticationManager}. See\n * {@link #setAuthenticationManager(AuthenticationManager)}.</li>\n * <li>{@link HttpServletRequest#logout()} - Allows the user to logout using the\n * {@link LogoutHandler}s configured in Spring Security. See\n * {@link #setLogoutHandlers(List)}.</li>\n * <li>{@link AsyncContext#start(Runnable)} - Automatically copy the\n * {@link SecurityContext} from the {@link SecurityContextHolder} found on the Thread that\n * invoked {@link AsyncContext#start(Runnable)} to the Thread that processes the\n * {@link Runnable}.</li>\n * </ul>\n *\n * @author Rob Winch\n * @see SecurityContextHolderAwareRequestFilter\n * @see Servlet3SecurityContextHolderAwareRequestWrapper\n * @see SecurityContextAsyncContext\n */\nfinal class HttpServlet3RequestFactory implements HttpServletRequestFactory {\n\n\tprivate Log logger = LogFactory.getLog(getClass());\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final String rolePrefix;\n\n\tprivate AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();\n\n\tprivate @Nullable AuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate @Nullable AuthenticationManager authenticationManager;\n\n\tprivate @Nullable List<LogoutHandler> logoutHandlers;\n\n\tprivate SecurityContextRepository securityContextRepository;\n\n\tHttpServlet3RequestFactory(String rolePrefix, SecurityContextRepository securityContextRepository) {\n\t\tthis.rolePrefix = rolePrefix;\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link AuthenticationEntryPoint} used when integrating\n\t * {@link HttpServletRequest} with Servlet 3 APIs. Specifically, it will be used when\n\t * {@link HttpServletRequest#authenticate(HttpServletResponse)} is called and the user\n\t * is not authenticated.\n\t * </p>\n\t * <p>\n\t * If the value is null (default), then the default container behavior will be be\n\t * retained when invoking {@link HttpServletRequest#authenticate(HttpServletResponse)}\n\t * .\n\t * </p>\n\t * @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use when\n\t * invoking {@link HttpServletRequest#authenticate(HttpServletResponse)} if the user\n\t * is not authenticated.\n\t */\n\n\tvoid setAuthenticationEntryPoint(@Nullable AuthenticationEntryPoint authenticationEntryPoint) {\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link AuthenticationManager} used when integrating\n\t * {@link HttpServletRequest} with Servlet 3 APIs. Specifically, it will be used when\n\t * {@link HttpServletRequest#login(String, String)} is invoked to determine if the\n\t * user is authenticated.\n\t * </p>\n\t * <p>\n\t * If the value is null (default), then the default container behavior will be\n\t * retained when invoking {@link HttpServletRequest#login(String, String)}.\n\t * </p>\n\t * @param authenticationManager the {@link AuthenticationManager} to use when invoking\n\t * {@link HttpServletRequest#login(String, String)}\n\t */\n\tvoid setAuthenticationManager(@Nullable AuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link LogoutHandler}s used when integrating with\n\t * {@link HttpServletRequest} with Servlet 3 APIs. Specifically it will be used when\n\t * {@link HttpServletRequest#logout()} is invoked in order to log the user out. So\n\t * long as the {@link LogoutHandler}s do not commit the {@link HttpServletResponse}\n\t * (expected), then the user is in charge of handling the response.\n\t * </p>\n\t * <p>\n\t * If the value is null (default), the default container behavior will be retained\n\t * when invoking {@link HttpServletRequest#logout()}.\n\t * </p>\n\t * @param logoutHandlers the {@code List<LogoutHandler>}s when invoking\n\t * {@link HttpServletRequest#logout()}.\n\t */\n\tvoid setLogoutHandlers(@Nullable List<LogoutHandler> logoutHandlers) {\n\t\tthis.logoutHandlers = logoutHandlers;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. The default is\n\t * {@link AuthenticationTrustResolverImpl}.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be\n\t * null.\n\t */\n\tvoid setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\tthis.trustResolver = trustResolver;\n\t}\n\n\tvoid setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\t@Override\n\tpublic HttpServletRequest create(HttpServletRequest request, HttpServletResponse response) {\n\t\tServlet3SecurityContextHolderAwareRequestWrapper wrapper = new Servlet3SecurityContextHolderAwareRequestWrapper(\n\t\t\t\trequest, this.rolePrefix, response);\n\t\twrapper.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\treturn wrapper;\n\t}\n\n\tprivate class Servlet3SecurityContextHolderAwareRequestWrapper extends SecurityContextHolderAwareRequestWrapper {\n\n\t\tprivate final HttpServletResponse response;\n\n\t\tServlet3SecurityContextHolderAwareRequestWrapper(HttpServletRequest request, String rolePrefix,\n\t\t\t\tHttpServletResponse response) {\n\t\t\tsuper(request, HttpServlet3RequestFactory.this.trustResolver, rolePrefix);\n\t\t\tthis.response = response;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable AsyncContext getAsyncContext() {\n\t\t\tAsyncContext asyncContext = super.getAsyncContext();\n\t\t\tif (asyncContext == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn new SecurityContextAsyncContext(asyncContext);\n\t\t}\n\n\t\t@Override\n\t\tpublic AsyncContext startAsync() {\n\t\t\tAsyncContext startAsync = super.startAsync();\n\t\t\treturn new SecurityContextAsyncContext(startAsync);\n\t\t}\n\n\t\t@Override\n\t\tpublic AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)\n\t\t\t\tthrows IllegalStateException {\n\t\t\tAsyncContext startAsync = super.startAsync(servletRequest, servletResponse);\n\t\t\treturn new SecurityContextAsyncContext(startAsync);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean authenticate(HttpServletResponse response) throws IOException, ServletException {\n\t\t\tAuthenticationEntryPoint entryPoint = HttpServlet3RequestFactory.this.authenticationEntryPoint;\n\t\t\tif (entryPoint == null) {\n\t\t\t\tHttpServlet3RequestFactory.this.logger.debug(\n\t\t\t\t\t\t\"authenticationEntryPoint is null, so allowing original HttpServletRequest to handle authenticate\");\n\t\t\t\treturn super.authenticate(response);\n\t\t\t}\n\t\t\tif (isAuthenticated()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tentryPoint.commence(this, response,\n\t\t\t\t\tnew AuthenticationCredentialsNotFoundException(\"User is not Authenticated\"));\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void login(String username, String password) throws ServletException {\n\t\t\tif (isAuthenticated()) {\n\t\t\t\tthrow new ServletException(\"Cannot perform login for '\" + username + \"' already authenticated as '\"\n\t\t\t\t\t\t+ getRemoteUser() + \"'\");\n\t\t\t}\n\t\t\tAuthenticationManager authManager = HttpServlet3RequestFactory.this.authenticationManager;\n\t\t\tif (authManager == null) {\n\t\t\t\tHttpServlet3RequestFactory.this.logger\n\t\t\t\t\t.debug(\"authenticationManager is null, so allowing original HttpServletRequest to handle login\");\n\t\t\t\tsuper.login(username, password);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tAuthentication authentication = getAuthentication(authManager, username, password);\n\t\t\tSecurityContext context = HttpServlet3RequestFactory.this.securityContextHolderStrategy\n\t\t\t\t.createEmptyContext();\n\t\t\tcontext.setAuthentication(authentication);\n\t\t\tHttpServlet3RequestFactory.this.securityContextHolderStrategy.setContext(context);\n\t\t\tHttpServlet3RequestFactory.this.securityContextRepository.saveContext(context, this, this.response);\n\t\t}\n\n\t\tprivate Authentication getAuthentication(AuthenticationManager authManager, String username, String password)\n\t\t\t\tthrows ServletException {\n\t\t\ttry {\n\t\t\t\tUsernamePasswordAuthenticationToken authentication = UsernamePasswordAuthenticationToken\n\t\t\t\t\t.unauthenticated(username, password);\n\t\t\t\tObject details = HttpServlet3RequestFactory.this.authenticationDetailsSource.buildDetails(this);\n\t\t\t\tauthentication.setDetails(details);\n\t\t\t\treturn authManager.authenticate(authentication);\n\t\t\t}\n\t\t\tcatch (AuthenticationException ex) {\n\t\t\t\tHttpServlet3RequestFactory.this.securityContextHolderStrategy.clearContext();\n\t\t\t\tthrow new ServletException(ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void logout() throws ServletException {\n\t\t\tList<LogoutHandler> handlers = HttpServlet3RequestFactory.this.logoutHandlers;\n\t\t\tif (CollectionUtils.isEmpty(handlers)) {\n\t\t\t\tHttpServlet3RequestFactory.this.logger\n\t\t\t\t\t.debug(\"logoutHandlers is null, so allowing original HttpServletRequest to handle logout\");\n\t\t\t\tsuper.logout();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tAuthentication authentication = HttpServlet3RequestFactory.this.securityContextHolderStrategy.getContext()\n\t\t\t\t.getAuthentication();\n\t\t\tfor (LogoutHandler handler : handlers) {\n\t\t\t\thandler.logout(this, this.response, authentication);\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean isAuthenticated() {\n\t\t\treturn getUserPrincipal() != null;\n\t\t}\n\n\t}\n\n\tprivate static class SecurityContextAsyncContext implements AsyncContext {\n\n\t\tprivate final AsyncContext asyncContext;\n\n\t\tSecurityContextAsyncContext(AsyncContext asyncContext) {\n\t\t\tthis.asyncContext = asyncContext;\n\t\t}\n\n\t\t@Override\n\t\tpublic ServletRequest getRequest() {\n\t\t\treturn this.asyncContext.getRequest();\n\t\t}\n\n\t\t@Override\n\t\tpublic ServletResponse getResponse() {\n\t\t\treturn this.asyncContext.getResponse();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasOriginalRequestAndResponse() {\n\t\t\treturn this.asyncContext.hasOriginalRequestAndResponse();\n\t\t}\n\n\t\t@Override\n\t\tpublic void dispatch() {\n\t\t\tthis.asyncContext.dispatch();\n\t\t}\n\n\t\t@Override\n\t\tpublic void dispatch(String path) {\n\t\t\tthis.asyncContext.dispatch(path);\n\t\t}\n\n\t\t@Override\n\t\tpublic void dispatch(ServletContext context, String path) {\n\t\t\tthis.asyncContext.dispatch(context, path);\n\t\t}\n\n\t\t@Override\n\t\tpublic void complete() {\n\t\t\tthis.asyncContext.complete();\n\t\t}\n\n\t\t@Override\n\t\tpublic void start(Runnable run) {\n\t\t\tthis.asyncContext.start(new DelegatingSecurityContextRunnable(run));\n\t\t}\n\n\t\t@Override\n\t\tpublic void addListener(AsyncListener listener) {\n\t\t\tthis.asyncContext.addListener(listener);\n\t\t}\n\n\t\t@Override\n\t\tpublic void addListener(AsyncListener listener, ServletRequest request, ServletResponse response) {\n\t\t\tthis.asyncContext.addListener(listener, request, response);\n\t\t}\n\n\t\t@Override\n\t\tpublic <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException {\n\t\t\treturn this.asyncContext.createListener(clazz);\n\t\t}\n\n\t\t@Override\n\t\tpublic long getTimeout() {\n\t\t\treturn this.asyncContext.getTimeout();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setTimeout(long timeout) {\n\t\t\tthis.asyncContext.setTimeout(timeout);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servletapi/HttpServletRequestFactory.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servletapi;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * Internal interface for creating a {@link HttpServletRequest}.\n *\n * @author Rob Winch\n * @since 3.2\n * @see HttpServlet3RequestFactory\n */\ninterface HttpServletRequestFactory {\n\n\t/**\n\t * Given a {@link HttpServletRequest} returns a {@link HttpServletRequest} that in\n\t * most cases wraps the original {@link HttpServletRequest}.\n\t * @param request the original {@link HttpServletRequest}. Cannot be null.\n\t * @param response the original {@link HttpServletResponse}. Cannot be null.\n\t * @return a non-null HttpServletRequest\n\t */\n\tHttpServletRequest create(HttpServletRequest request, HttpServletResponse response);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilter.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servletapi;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport jakarta.servlet.AsyncContext;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * A <code>Filter</code> which populates the <code>ServletRequest</code> with a request\n * wrapper which implements the servlet API security methods.\n * <p>\n * {@link SecurityContextHolderAwareRequestWrapper} is extended to provide the following\n * additional methods:\n * </p>\n * <ul>\n * <li>{@link HttpServletRequest#authenticate(HttpServletResponse)} - Allows the user to\n * determine if they are authenticated and if not send the user to the login page. See\n * {@link #setAuthenticationEntryPoint(AuthenticationEntryPoint)}.</li>\n * <li>{@link HttpServletRequest#login(String, String)} - Allows the user to authenticate\n * using the {@link AuthenticationManager}. See\n * {@link #setAuthenticationManager(AuthenticationManager)}.</li>\n * <li>{@link HttpServletRequest#logout()} - Allows the user to logout using the\n * {@link LogoutHandler}s configured in Spring Security. See\n * {@link #setLogoutHandlers(List)}.</li>\n * <li>{@link AsyncContext#start(Runnable)} - Automatically copy the\n * {@link SecurityContext} from the {@link SecurityContextHolder} found on the Thread that\n * invoked {@link AsyncContext#start(Runnable)} to the Thread that processes the\n * {@link Runnable}.</li>\n * </ul>\n *\n * @author Orlando Garcia Carmona\n * @author Ben Alex\n * @author Luke Taylor\n * @author Rob Winch\n * @author Eddú Meléndez\n */\npublic class SecurityContextHolderAwareRequestFilter extends GenericFilterBean {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate String rolePrefix = \"ROLE_\";\n\n\t@SuppressWarnings(\"NullAway.Init\")\n\tprivate HttpServletRequestFactory requestFactory;\n\n\tprivate @Nullable AuthenticationEntryPoint authenticationEntryPoint;\n\n\tprivate @Nullable AuthenticationManager authenticationManager;\n\n\tprivate @Nullable List<LogoutHandler> logoutHandlers;\n\n\tprivate AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();\n\n\t/**\n\t * Sets the {@link SecurityContextRepository} to use. The default is to use\n\t * {@link HttpSessionSecurityContextRepository}.\n\t * @param securityContextRepository the {@link SecurityContextRepository} to use.\n\t * @since 6.0\n\t */\n\tpublic void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {\n\t\tAssert.notNull(securityContextRepository, \"securityContextRepository cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tpublic void setRolePrefix(String rolePrefix) {\n\t\tAssert.notNull(rolePrefix, \"Role prefix must not be null\");\n\t\tthis.rolePrefix = rolePrefix;\n\t\tupdateFactory();\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link AuthenticationEntryPoint} used when integrating\n\t * {@link HttpServletRequest} with Servlet 3 APIs. Specifically, it will be used when\n\t * {@link HttpServletRequest#authenticate(HttpServletResponse)} is called and the user\n\t * is not authenticated.\n\t * </p>\n\t * <p>\n\t * If the value is null (default), then the default container behavior will be be\n\t * retained when invoking {@link HttpServletRequest#authenticate(HttpServletResponse)}\n\t * .\n\t * </p>\n\t * @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use when\n\t * invoking {@link HttpServletRequest#authenticate(HttpServletResponse)} if the user\n\t * is not authenticated.\n\t */\n\tpublic void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {\n\t\tthis.authenticationEntryPoint = authenticationEntryPoint;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link AuthenticationManager} used when integrating\n\t * {@link HttpServletRequest} with Servlet 3 APIs. Specifically, it will be used when\n\t * {@link HttpServletRequest#login(String, String)} is invoked to determine if the\n\t * user is authenticated.\n\t * </p>\n\t * <p>\n\t * If the value is null (default), then the default container behavior will be\n\t * retained when invoking {@link HttpServletRequest#login(String, String)}.\n\t * </p>\n\t * @param authenticationManager the {@link AuthenticationManager} to use when invoking\n\t * {@link HttpServletRequest#login(String, String)}\n\t */\n\tpublic void setAuthenticationManager(AuthenticationManager authenticationManager) {\n\t\tthis.authenticationManager = authenticationManager;\n\t}\n\n\t/**\n\t * <p>\n\t * Sets the {@link LogoutHandler}s used when integrating with\n\t * {@link HttpServletRequest} with Servlet 3 APIs. Specifically it will be used when\n\t * {@link HttpServletRequest#logout()} is invoked in order to log the user out. So\n\t * long as the {@link LogoutHandler}s do not commit the {@link HttpServletResponse}\n\t * (expected), then the user is in charge of handling the response.\n\t * </p>\n\t * <p>\n\t * If the value is null (default), the default container behavior will be retained\n\t * when invoking {@link HttpServletRequest#logout()}.\n\t * </p>\n\t * @param logoutHandlers the {@code List&lt;LogoutHandler&gt;}s when invoking\n\t * {@link HttpServletRequest#logout()}.\n\t */\n\tpublic void setLogoutHandlers(List<LogoutHandler> logoutHandlers) {\n\t\tthis.logoutHandlers = logoutHandlers;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tchain.doFilter(this.requestFactory.create((HttpServletRequest) req, (HttpServletResponse) res), res);\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() throws ServletException {\n\t\tsuper.afterPropertiesSet();\n\t\tupdateFactory();\n\t}\n\n\tprivate void updateFactory() {\n\t\tString rolePrefix = this.rolePrefix;\n\t\tthis.requestFactory = createServlet3Factory(rolePrefix);\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. The default is\n\t * {@link AuthenticationTrustResolverImpl}.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be\n\t * null.\n\t */\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\tthis.trustResolver = trustResolver;\n\t\tupdateFactory();\n\t}\n\n\tprivate HttpServletRequestFactory createServlet3Factory(String rolePrefix) {\n\t\tHttpServlet3RequestFactory factory = new HttpServlet3RequestFactory(rolePrefix, this.securityContextRepository);\n\t\tfactory.setTrustResolver(this.trustResolver);\n\t\tfactory.setAuthenticationEntryPoint(this.authenticationEntryPoint);\n\t\tfactory.setAuthenticationManager(this.authenticationManager);\n\t\tfactory.setLogoutHandlers(this.logoutHandlers);\n\t\tfactory.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);\n\t\treturn factory;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestWrapper.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servletapi;\n\nimport java.security.Principal;\nimport java.util.Collection;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.Assert;\n\n/**\n * A Spring Security-aware <code>HttpServletRequestWrapper</code>, which uses the\n * <code>SecurityContext</code>-defined <code>Authentication</code> object to implement\n * the servlet API security methods:\n *\n * <ul>\n * <li>{@link #getUserPrincipal()}</li>\n * <li>{@link SecurityContextHolderAwareRequestWrapper#isUserInRole(String)}</li>\n * <li>{@link HttpServletRequestWrapper#getRemoteUser()}.</li>\n * </ul>\n *\n * @author Orlando Garcia Carmona\n * @author Ben Alex\n * @author Luke Taylor\n * @author Rob Winch\n * @see SecurityContextHolderAwareRequestFilter\n */\npublic class SecurityContextHolderAwareRequestWrapper extends HttpServletRequestWrapper {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final AuthenticationTrustResolver trustResolver;\n\n\t/**\n\t * The prefix passed by the filter. It will be prepended to any supplied role values\n\t * before comparing it with the roles obtained from the security context.\n\t */\n\tprivate final String rolePrefix;\n\n\t/**\n\t * Creates a new instance with {@link AuthenticationTrustResolverImpl}.\n\t * @param request\n\t * @param rolePrefix\n\t */\n\tpublic SecurityContextHolderAwareRequestWrapper(HttpServletRequest request, String rolePrefix) {\n\t\tthis(request, new AuthenticationTrustResolverImpl(), rolePrefix);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param request the original {@link HttpServletRequest}\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be\n\t * null.\n\t * @param rolePrefix The prefix to be added to {@link #isUserInRole(String)} or null\n\t * if no prefix.\n\t */\n\tpublic SecurityContextHolderAwareRequestWrapper(HttpServletRequest request,\n\t\t\tAuthenticationTrustResolver trustResolver, String rolePrefix) {\n\t\tsuper(request);\n\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\tthis.rolePrefix = rolePrefix;\n\t\tthis.trustResolver = trustResolver;\n\t}\n\n\t/**\n\t * Obtain the current active <code>Authentication</code>\n\t * @return the authentication object or <code>null</code>\n\t */\n\tprivate @Nullable Authentication getAuthentication() {\n\t\tAuthentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\treturn (this.trustResolver.isAuthenticated(auth)) ? auth : null;\n\t}\n\n\t/**\n\t * Returns the principal's name, as obtained from the\n\t * <code>SecurityContextHolder</code>. Properly handles both <code>String</code>-based\n\t * and <code>UserDetails</code>-based principals.\n\t * @return the username or <code>null</code> if unavailable\n\t */\n\t@Override\n\tpublic @Nullable String getRemoteUser() {\n\t\tAuthentication auth = getAuthentication();\n\t\tif ((auth == null) || (auth.getPrincipal() == null)) {\n\t\t\treturn null;\n\t\t}\n\t\tif (auth.getPrincipal() instanceof UserDetails) {\n\t\t\treturn ((UserDetails) auth.getPrincipal()).getUsername();\n\t\t}\n\t\tif (auth instanceof AbstractAuthenticationToken) {\n\t\t\treturn auth.getName();\n\t\t}\n\t\treturn auth.getPrincipal().toString();\n\t}\n\n\t/**\n\t * Returns the <code>Authentication</code> (which is a subclass of\n\t * <code>Principal</code>), or <code>null</code> if unavailable.\n\t * @return the <code>Authentication</code>, or <code>null</code>\n\t */\n\t@Override\n\tpublic @Nullable Principal getUserPrincipal() {\n\t\tAuthentication auth = getAuthentication();\n\t\tif ((auth == null) || (auth.getPrincipal() == null)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn auth;\n\t}\n\n\tprivate boolean isGranted(String role) {\n\t\tAuthentication auth = getAuthentication();\n\t\tif (this.rolePrefix != null && role != null && !role.startsWith(this.rolePrefix)) {\n\t\t\trole = this.rolePrefix + role;\n\t\t}\n\t\tif ((auth == null) || (auth.getPrincipal() == null)) {\n\t\t\treturn false;\n\t\t}\n\t\tCollection<? extends GrantedAuthority> authorities = auth.getAuthorities();\n\t\tif (authorities == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (GrantedAuthority grantedAuthority : authorities) {\n\t\t\tif (role.equals(grantedAuthority.getAuthority())) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Simple searches for an exactly matching\n\t * {@link org.springframework.security.core.GrantedAuthority#getAuthority()}.\n\t * <p>\n\t * Will always return <code>false</code> if the <code>SecurityContextHolder</code>\n\t * contains an <code>Authentication</code> with <code>null</code>\n\t * <code>principal</code> and/or <code>GrantedAuthority[]</code> objects.\n\t * @param role the <code>GrantedAuthority</code><code>String</code> representation to\n\t * check for\n\t * @return <code>true</code> if an <b>exact</b> (case sensitive) matching granted\n\t * authority is located, <code>false</code> otherwise\n\t */\n\t@Override\n\tpublic boolean isUserInRole(String role) {\n\t\treturn isGranted(role);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"SecurityContextHolderAwareRequestWrapper[ \" + getRequest() + \"]\";\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/servletapi/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Populates a Servlet request with a new Spring Security compliant\n * {@code HttpServletRequestWrapper}.\n * <p>\n * To use, simply add the {@code SecurityContextHolderAwareRequestFilter} to the Spring\n * Security filter chain.\n */\n@NullMarked\npackage org.springframework.security.web.servletapi;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/ConcurrentSessionFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.logout.CompositeLogoutHandler;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Filter required by concurrent session handling package.\n * <p>\n * This filter performs two functions. First, it calls\n * {@link org.springframework.security.core.session.SessionRegistry#refreshLastRequest(String)}\n * for each request so that registered sessions always have a correct \"last update\"\n * date/time. Second, it retrieves a\n * {@link org.springframework.security.core.session.SessionInformation} from the\n * <code>SessionRegistry</code> for each request and checks if the session has been marked\n * as expired. If it has been marked as expired, the configured logout handlers will be\n * called (as happens with\n * {@link org.springframework.security.web.authentication.logout.LogoutFilter}), typically\n * to invalidate the session. To handle the expired session a call to the\n * {@link SessionInformationExpiredStrategy} is made. The session invalidation will cause\n * an {@link org.springframework.security.web.session.HttpSessionDestroyedEvent} to be\n * published via the\n * {@link org.springframework.security.web.session.HttpSessionEventPublisher} registered\n * in <code>web.xml</code>.\n * </p>\n *\n * @author Ben Alex\n * @author Eddú Meléndez\n * @author Marten Deinum\n * @author Onur Kagan Ozcan\n */\npublic class ConcurrentSessionFilter extends GenericFilterBean {\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final SessionRegistry sessionRegistry;\n\n\tprivate @Nullable String expiredUrl;\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate LogoutHandler handlers = new CompositeLogoutHandler(new SecurityContextLogoutHandler());\n\n\tprivate SessionInformationExpiredStrategy sessionInformationExpiredStrategy;\n\n\tpublic ConcurrentSessionFilter(SessionRegistry sessionRegistry) {\n\t\tAssert.notNull(sessionRegistry, \"SessionRegistry required\");\n\t\tthis.sessionRegistry = sessionRegistry;\n\t\tthis.sessionInformationExpiredStrategy = new ResponseBodySessionInformationExpiredStrategy();\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param sessionRegistry the SessionRegistry to use\n\t * @param expiredUrl the URL to redirect to\n\t * @deprecated use\n\t * {@link #ConcurrentSessionFilter(SessionRegistry, SessionInformationExpiredStrategy)}\n\t * with {@link SimpleRedirectSessionInformationExpiredStrategy} instead.\n\t */\n\t@Deprecated\n\tpublic ConcurrentSessionFilter(SessionRegistry sessionRegistry, String expiredUrl) {\n\t\tAssert.notNull(sessionRegistry, \"SessionRegistry required\");\n\t\tAssert.isTrue(expiredUrl == null || UrlUtils.isValidRedirectUrl(expiredUrl),\n\t\t\t\t() -> expiredUrl + \" isn't a valid redirect URL\");\n\t\tthis.expiredUrl = expiredUrl;\n\t\tthis.sessionRegistry = sessionRegistry;\n\t\tthis.sessionInformationExpiredStrategy = (event) -> {\n\t\t\tHttpServletRequest request = event.getRequest();\n\t\t\tHttpServletResponse response = event.getResponse();\n\t\t\tSessionInformation info = event.getSessionInformation();\n\t\t\tthis.redirectStrategy.sendRedirect(request, response, determineExpiredUrl(request, info));\n\t\t};\n\t}\n\n\tpublic ConcurrentSessionFilter(SessionRegistry sessionRegistry,\n\t\t\tSessionInformationExpiredStrategy sessionInformationExpiredStrategy) {\n\t\tAssert.notNull(sessionRegistry, \"sessionRegistry required\");\n\t\tAssert.notNull(sessionInformationExpiredStrategy, \"sessionInformationExpiredStrategy cannot be null\");\n\t\tthis.sessionRegistry = sessionRegistry;\n\t\tthis.sessionInformationExpiredStrategy = sessionInformationExpiredStrategy;\n\t}\n\n\t@Override\n\tpublic void afterPropertiesSet() {\n\t\tAssert.notNull(this.sessionRegistry, \"SessionRegistry required\");\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session != null) {\n\t\t\tSessionInformation info = this.sessionRegistry.getSessionInformation(session.getId());\n\t\t\tif (info != null) {\n\t\t\t\tif (info.isExpired()) {\n\t\t\t\t\t// Expired - abort processing\n\t\t\t\t\tthis.logger.debug(LogMessage\n\t\t\t\t\t\t.of(() -> \"Requested session ID \" + request.getRequestedSessionId() + \" has expired.\"));\n\t\t\t\t\tdoLogout(request, response);\n\t\t\t\t\tthis.sessionInformationExpiredStrategy\n\t\t\t\t\t\t.onExpiredSessionDetected(new SessionInformationExpiredEvent(info, request, response, chain));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Non-expired - update last request date/time\n\t\t\t\tthis.sessionRegistry.refreshLastRequest(info.getSessionId());\n\t\t\t}\n\t\t}\n\t\tchain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Determine the URL for expiration\n\t * @param request the HttpServletRequest\n\t * @param info the {@link SessionInformation}\n\t * @return the URL for expiration\n\t * @deprecated Use\n\t * {@link #ConcurrentSessionFilter(SessionRegistry, SessionInformationExpiredStrategy)}\n\t * instead.\n\t */\n\t@Deprecated\n\tprotected String determineExpiredUrl(HttpServletRequest request, SessionInformation info) {\n\t\tAssert.notNull(this.expiredUrl, \"expiredUrl cannot be null\");\n\t\treturn this.expiredUrl;\n\t}\n\n\tprivate void doLogout(HttpServletRequest request, HttpServletResponse response) {\n\t\tAuthentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();\n\n\t\tthis.handlers.logout(request, response, auth);\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n\tpublic void setLogoutHandlers(LogoutHandler[] handlers) {\n\t\tthis.handlers = new CompositeLogoutHandler(handlers);\n\t}\n\n\t/**\n\t * Set list of {@link LogoutHandler}\n\t * @param handlers list of {@link LogoutHandler}\n\t * @since 5.2.0\n\t */\n\tpublic void setLogoutHandlers(List<LogoutHandler> handlers) {\n\t\tthis.handlers = new CompositeLogoutHandler(handlers);\n\t}\n\n\t/**\n\t * Sets the {@link RedirectStrategy} used with\n\t * {@link #ConcurrentSessionFilter(SessionRegistry, String)}\n\t * @param redirectStrategy the {@link RedirectStrategy} to use\n\t * @deprecated use\n\t * {@link #ConcurrentSessionFilter(SessionRegistry, SessionInformationExpiredStrategy)}\n\t * instead.\n\t */\n\t@Deprecated\n\tpublic void setRedirectStrategy(RedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n\t/**\n\t * A {@link SessionInformationExpiredStrategy} that writes an error message to the\n\t * response body.\n\t *\n\t * @author Rob Winch\n\t * @since 4.2\n\t */\n\tprivate static final class ResponseBodySessionInformationExpiredStrategy\n\t\t\timplements SessionInformationExpiredStrategy {\n\n\t\t@Override\n\t\tpublic void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException {\n\t\t\tHttpServletResponse response = event.getResponse();\n\t\t\tresponse.getWriter()\n\t\t\t\t.print(\"This session has been expired (possibly due to multiple concurrent \"\n\t\t\t\t\t\t+ \"logins being attempted as the same user).\");\n\t\t\tresponse.flushBuffer();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/DisableEncodeUrlFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpServletResponseWrapper;\n\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Disables encoding URLs using the {@link HttpServletResponse} to prevent including the\n * session id in URLs which is not considered URL because the session id can be leaked in\n * things like HTTP access logs.\n *\n * @author Rob Winch\n * @since 5.7\n */\npublic class DisableEncodeUrlFilter extends OncePerRequestFilter {\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tfilterChain.doFilter(request, new DisableEncodeUrlResponseWrapper(response));\n\t}\n\n\t/**\n\t * Disables URL rewriting for the {@link HttpServletResponse} to prevent including the\n\t * session id in URLs which is not considered URL because the session id can be leaked\n\t * in things like HTTP access logs.\n\t *\n\t * @author Rob Winch\n\t * @since 5.7\n\t */\n\tprivate static final class DisableEncodeUrlResponseWrapper extends HttpServletResponseWrapper {\n\n\t\t/**\n\t\t * Constructs a response adaptor wrapping the given response.\n\t\t * @param response the {@link HttpServletResponse} to be wrapped.\n\t\t * @throws IllegalArgumentException if the response is null\n\t\t */\n\t\tprivate DisableEncodeUrlResponseWrapper(HttpServletResponse response) {\n\t\t\tsuper(response);\n\t\t}\n\n\t\t@Override\n\t\tpublic String encodeRedirectURL(String url) {\n\t\t\treturn url;\n\t\t}\n\n\t\t@Override\n\t\tpublic String encodeURL(String url) {\n\t\t\treturn url;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/ForceEagerSessionCreationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Eagerly creates {@link HttpSession} if it does not already exist.\n *\n * @author Rob Winch\n * @since 5.7\n */\npublic class ForceEagerSessionCreationFilter extends OncePerRequestFilter {\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tHttpSession session = request.getSession();\n\t\tif (this.logger.isDebugEnabled() && session.isNew()) {\n\t\t\tthis.logger.debug(LogMessage.format(\"Created session eagerly\"));\n\t\t}\n\t\tfilterChain.doFilter(request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/HttpSessionCreatedEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.security.core.session.SessionCreationEvent;\n\n/**\n * Published by the {@link HttpSessionEventPublisher} when an {@code HttpSession} is\n * created by the container\n *\n * @author Ray Krueger\n * @author Luke Taylor\n */\n@SuppressWarnings(\"serial\")\npublic class HttpSessionCreatedEvent extends SessionCreationEvent {\n\n\tpublic HttpSessionCreatedEvent(HttpSession session) {\n\t\tsuper(session);\n\t}\n\n\tpublic HttpSession getSession() {\n\t\treturn (HttpSession) getSource();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/HttpSessionDestroyedEvent.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.session.SessionDestroyedEvent;\n\n/**\n * Published by the {@link HttpSessionEventPublisher} when a HttpSession is removed from\n * the container\n *\n * @author Ray Krueger\n * @author Luke Taylor\n * @author Rob Winch\n */\n@SuppressWarnings(\"serial\")\npublic class HttpSessionDestroyedEvent extends SessionDestroyedEvent {\n\n\tpublic HttpSessionDestroyedEvent(HttpSession session) {\n\t\tsuper(session);\n\t}\n\n\tpublic HttpSession getSession() {\n\t\treturn (HttpSession) getSource();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic List<SecurityContext> getSecurityContexts() {\n\t\tHttpSession session = getSession();\n\t\tEnumeration<String> attributes = session.getAttributeNames();\n\t\tArrayList<SecurityContext> contexts = new ArrayList<>();\n\t\twhile (attributes.hasMoreElements()) {\n\t\t\tString attributeName = attributes.nextElement();\n\t\t\tObject attributeValue = session.getAttribute(attributeName);\n\t\t\tif (attributeValue instanceof SecurityContext) {\n\t\t\t\tcontexts.add((SecurityContext) attributeValue);\n\t\t\t}\n\t\t}\n\t\treturn contexts;\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn getSession().getId();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/HttpSessionEventPublisher.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.http.HttpSession;\nimport jakarta.servlet.http.HttpSessionEvent;\nimport jakarta.servlet.http.HttpSessionIdListener;\nimport jakarta.servlet.http.HttpSessionListener;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.web.context.support.SecurityWebApplicationContextUtils;\n\n/**\n * Declared in web.xml as\n *\n * <pre>\n * &lt;listener&gt;\n *     &lt;listener-class&gt;org.springframework.security.web.session.HttpSessionEventPublisher&lt;/listener-class&gt;\n * &lt;/listener&gt;\n * </pre>\n *\n * Publishes <code>HttpSessionApplicationEvent</code>s to the Spring Root\n * WebApplicationContext. Maps jakarta.servlet.http.HttpSessionListener.sessionCreated()\n * to {@link HttpSessionCreatedEvent}. Maps\n * jakarta.servlet.http.HttpSessionListener.sessionDestroyed() to\n * {@link HttpSessionDestroyedEvent}.\n *\n * @author Ray Krueger\n */\npublic class HttpSessionEventPublisher implements HttpSessionListener, HttpSessionIdListener {\n\n\tprivate static final String LOGGER_NAME = HttpSessionEventPublisher.class.getName();\n\n\tApplicationContext getContext(ServletContext servletContext) {\n\t\treturn SecurityWebApplicationContextUtils.findRequiredWebApplicationContext(servletContext);\n\t}\n\n\t/**\n\t * Handles the HttpSessionEvent by publishing a {@link HttpSessionCreatedEvent} to the\n\t * application appContext.\n\t * @param event HttpSessionEvent passed in by the container\n\t */\n\t@Override\n\tpublic void sessionCreated(HttpSessionEvent event) {\n\t\textracted(event.getSession(), new HttpSessionCreatedEvent(event.getSession()));\n\t}\n\n\t/**\n\t * Handles the HttpSessionEvent by publishing a {@link HttpSessionDestroyedEvent} to\n\t * the application appContext.\n\t * @param event The HttpSessionEvent pass in by the container\n\t */\n\t@Override\n\tpublic void sessionDestroyed(HttpSessionEvent event) {\n\t\textracted(event.getSession(), new HttpSessionDestroyedEvent(event.getSession()));\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\t@Override\n\tpublic void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {\n\t\textracted(event.getSession(), new HttpSessionIdChangedEvent(event.getSession(), oldSessionId));\n\t}\n\n\tprivate void extracted(HttpSession session, ApplicationEvent e) {\n\t\tLog log = LogFactory.getLog(LOGGER_NAME);\n\t\tlog.debug(LogMessage.format(\"Publishing event: %s\", e));\n\t\tgetContext(session.getServletContext()).publishEvent(e);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/HttpSessionIdChangedEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.Serial;\n\nimport jakarta.servlet.http.HttpSession;\n\nimport org.springframework.security.core.session.SessionIdChangedEvent;\n\n/**\n * Published by the {@link HttpSessionEventPublisher} when an {@link HttpSession} ID is\n * changed.\n *\n * @since 5.4\n */\n@SuppressWarnings(\"serial\")\npublic class HttpSessionIdChangedEvent extends SessionIdChangedEvent {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5725731666499807941L;\n\n\tprivate final String oldSessionId;\n\n\tprivate final String newSessionId;\n\n\tpublic HttpSessionIdChangedEvent(HttpSession session, String oldSessionId) {\n\t\tsuper(session);\n\t\tthis.oldSessionId = oldSessionId;\n\t\tthis.newSessionId = session.getId();\n\t}\n\n\t@Override\n\tpublic String getOldSessionId() {\n\t\treturn this.oldSessionId;\n\t}\n\n\t@Override\n\tpublic String getNewSessionId() {\n\t\treturn this.newSessionId;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/InvalidSessionAccessDeniedHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.util.Assert;\n\n/**\n * An adapter of {@link InvalidSessionStrategy} to {@link AccessDeniedHandler}\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class InvalidSessionAccessDeniedHandler implements AccessDeniedHandler {\n\n\tprivate final InvalidSessionStrategy invalidSessionStrategy;\n\n\t/**\n\t * Creates a new instance\n\t * @param invalidSessionStrategy the {@link InvalidSessionStrategy} to delegate to\n\t */\n\tpublic InvalidSessionAccessDeniedHandler(InvalidSessionStrategy invalidSessionStrategy) {\n\t\tAssert.notNull(invalidSessionStrategy, \"invalidSessionStrategy cannot be null\");\n\t\tthis.invalidSessionStrategy = invalidSessionStrategy;\n\t}\n\n\t@Override\n\tpublic void handle(HttpServletRequest request, HttpServletResponse response,\n\t\t\tAccessDeniedException accessDeniedException) throws IOException, ServletException {\n\t\tthis.invalidSessionStrategy.onInvalidSessionDetected(request, response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/InvalidSessionStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\n/**\n * Determines the behaviour of the {@code SessionManagementFilter} when an invalid session\n * Id is submitted and detected in the {@code SessionManagementFilter}.\n *\n * @author Luke Taylor\n */\npublic interface InvalidSessionStrategy {\n\n\tvoid onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/RequestedUrlRedirectInvalidSessionStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.util.Assert;\nimport org.springframework.web.servlet.support.ServletUriComponentsBuilder;\n\n/**\n * Performs a redirect to the original request URL when an invalid requested session is\n * detected by the {@code SessionManagementFilter}.\n *\n * @author Craig Andrews\n * @author Mark Chesney\n */\npublic final class RequestedUrlRedirectInvalidSessionStrategy implements InvalidSessionStrategy {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate boolean createNewSession = true;\n\n\t@Override\n\tpublic void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {\n\t\tString destinationUrl = ServletUriComponentsBuilder.fromRequest(request)\n\t\t\t.host(null)\n\t\t\t.scheme(null)\n\t\t\t.port(null)\n\t\t\t.toUriString();\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(\"Starting new session (if required) and redirecting to '\" + destinationUrl + \"'\");\n\t\t}\n\t\tif (this.createNewSession) {\n\t\t\trequest.getSession();\n\t\t}\n\t\tthis.redirectStrategy.sendRedirect(request, response, destinationUrl);\n\t}\n\n\t/**\n\t * Determines whether a new session should be created before redirecting (to avoid\n\t * possible looping issues where the same session ID is sent with the redirected\n\t * request). Alternatively, ensure that the configured URL does not pass through the\n\t * {@code SessionManagementFilter}.\n\t * @param createNewSession defaults to {@code true}.\n\t */\n\tpublic void setCreateNewSession(boolean createNewSession) {\n\t\tthis.createNewSession = createNewSession;\n\t}\n\n\t/**\n\t * Sets the redirect strategy to use. The default is {@link DefaultRedirectStrategy}.\n\t * @param redirectStrategy the redirect strategy to use.\n\t * @since 6.2\n\t */\n\tpublic void setRedirectStrategy(RedirectStrategy redirectStrategy) {\n\t\tAssert.notNull(redirectStrategy, \"redirectStrategy cannot be null\");\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/SessionInformationExpiredEvent.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.util.Assert;\n\n/**\n * An event for when a {@link SessionInformation} is expired.\n *\n * @author Rob Winch\n * @since 4.2\n */\n@SuppressWarnings(\"serial\")\npublic final class SessionInformationExpiredEvent extends ApplicationEvent {\n\n\tprivate final HttpServletRequest request;\n\n\tprivate final HttpServletResponse response;\n\n\tprivate final @Nullable FilterChain filterChain;\n\n\t/**\n\t * Creates a new instance\n\t * @param sessionInformation the SessionInformation that is expired\n\t * @param request the HttpServletRequest\n\t * @param response the HttpServletResponse\n\t */\n\tpublic SessionInformationExpiredEvent(SessionInformation sessionInformation, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tthis(sessionInformation, request, response, null);\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param sessionInformation the SessionInformation that is expired\n\t * @param request the HttpServletRequest\n\t * @param response the HttpServletResponse\n\t * @param filterChain the FilterChain\n\t * @since 6.4\n\t */\n\tpublic SessionInformationExpiredEvent(SessionInformation sessionInformation, HttpServletRequest request,\n\t\t\tHttpServletResponse response, @Nullable FilterChain filterChain) {\n\t\tsuper(sessionInformation);\n\t\tAssert.notNull(request, \"request cannot be null\");\n\t\tAssert.notNull(response, \"response cannot be null\");\n\t\tthis.request = request;\n\t\tthis.response = response;\n\t\tthis.filterChain = filterChain;\n\t}\n\n\t/**\n\t * @return the request\n\t */\n\tpublic HttpServletRequest getRequest() {\n\t\treturn this.request;\n\t}\n\n\t/**\n\t * @return the response\n\t */\n\tpublic HttpServletResponse getResponse() {\n\t\treturn this.response;\n\t}\n\n\tpublic SessionInformation getSessionInformation() {\n\t\treturn (SessionInformation) getSource();\n\t}\n\n\t/**\n\t * @return the filter chain. Can be {@code null}.\n\t * @since 6.4\n\t */\n\tpublic @Nullable FilterChain getFilterChain() {\n\t\treturn this.filterChain;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/SessionInformationExpiredStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\n\n/**\n * Determines the behaviour of the {@code ConcurrentSessionFilter} when an expired session\n * is detected in the {@code ConcurrentSessionFilter}.\n *\n * @author Marten Deinum\n * @author Rob Winch\n * @since 4.2.0\n */\npublic interface SessionInformationExpiredStrategy {\n\n\tvoid onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException;\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.GenericFilterBean;\n\n/**\n * Detects that a user has been authenticated since the start of the request and, if they\n * have, calls the configured {@link SessionAuthenticationStrategy} to perform any\n * session-related activity such as activating session-fixation protection mechanisms or\n * checking for multiple concurrent logins.\n *\n * @author Martin Algesten\n * @author Luke Taylor\n * @since 2.0\n */\npublic class SessionManagementFilter extends GenericFilterBean {\n\n\tstatic final String FILTER_APPLIED = \"__spring_security_session_mgmt_filter_applied\";\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final SecurityContextRepository securityContextRepository;\n\n\tprivate SessionAuthenticationStrategy sessionAuthenticationStrategy;\n\n\tprivate AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate @Nullable InvalidSessionStrategy invalidSessionStrategy = null;\n\n\tprivate AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();\n\n\tpublic SessionManagementFilter(SecurityContextRepository securityContextRepository) {\n\t\tthis(securityContextRepository, new SessionFixationProtectionStrategy());\n\t}\n\n\tpublic SessionManagementFilter(SecurityContextRepository securityContextRepository,\n\t\t\tSessionAuthenticationStrategy sessionStrategy) {\n\t\tAssert.notNull(securityContextRepository, \"SecurityContextRepository cannot be null\");\n\t\tAssert.notNull(sessionStrategy, \"SessionAuthenticationStrategy cannot be null\");\n\t\tthis.securityContextRepository = securityContextRepository;\n\t\tthis.sessionAuthenticationStrategy = sessionStrategy;\n\t}\n\n\t@Override\n\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tdoFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);\n\t}\n\n\tprivate void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows IOException, ServletException {\n\t\tif (request.getAttribute(FILTER_APPLIED) != null) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\trequest.setAttribute(FILTER_APPLIED, Boolean.TRUE);\n\t\tif (!this.securityContextRepository.containsContext(request)) {\n\t\t\tAuthentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();\n\t\t\tif (this.trustResolver.isAuthenticated(authentication)) {\n\t\t\t\t// The user has been authenticated during the current request, so call the\n\t\t\t\t// session strategy\n\t\t\t\ttry {\n\t\t\t\t\tthis.sessionAuthenticationStrategy.onAuthentication(authentication, request, response);\n\t\t\t\t}\n\t\t\t\tcatch (SessionAuthenticationException ex) {\n\t\t\t\t\t// The session strategy can reject the authentication\n\t\t\t\t\tthis.logger.debug(\"SessionAuthenticationStrategy rejected the authentication object\", ex);\n\t\t\t\t\tthis.securityContextHolderStrategy.clearContext();\n\t\t\t\t\tthis.failureHandler.onAuthenticationFailure(request, response, ex);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Eagerly save the security context to make it available for any possible\n\t\t\t\t// re-entrant requests which may occur before the current request\n\t\t\t\t// completes. SEC-1396.\n\t\t\t\tthis.securityContextRepository.saveContext(this.securityContextHolderStrategy.getContext(), request,\n\t\t\t\t\t\tresponse);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// No security context or authentication present. Check for a session\n\t\t\t\t// timeout\n\t\t\t\tif (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {\n\t\t\t\t\tif (this.logger.isDebugEnabled()) {\n\t\t\t\t\t\tthis.logger.debug(LogMessage.format(\"Request requested invalid session id %s\",\n\t\t\t\t\t\t\t\trequest.getRequestedSessionId()));\n\t\t\t\t\t}\n\t\t\t\t\tif (this.invalidSessionStrategy != null) {\n\t\t\t\t\t\tthis.invalidSessionStrategy.onInvalidSessionDetected(request, response);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tchain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Sets the strategy which will be invoked instead of allowing the filter chain to\n\t * proceed, if the user agent requests an invalid session ID. If the property is not\n\t * set, no action will be taken.\n\t * @param invalidSessionStrategy the strategy to invoke. Typically a\n\t * {@link SimpleRedirectInvalidSessionStrategy}.\n\t */\n\tpublic void setInvalidSessionStrategy(InvalidSessionStrategy invalidSessionStrategy) {\n\t\tthis.invalidSessionStrategy = invalidSessionStrategy;\n\t}\n\n\t/**\n\t * The handler which will be invoked if the <tt>AuthenticatedSessionStrategy</tt>\n\t * raises a <tt>SessionAuthenticationException</tt>, indicating that the user is not\n\t * allowed to be authenticated for this session (typically because they already have\n\t * too many sessions open).\n\t *\n\t */\n\tpublic void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {\n\t\tAssert.notNull(failureHandler, \"failureHandler cannot be null\");\n\t\tthis.failureHandler = failureHandler;\n\t}\n\n\t/**\n\t * Sets the {@link AuthenticationTrustResolver} to be used. The default is\n\t * {@link AuthenticationTrustResolverImpl}.\n\t * @param trustResolver the {@link AuthenticationTrustResolver} to use. Cannot be\n\t * null.\n\t */\n\tpublic void setTrustResolver(AuthenticationTrustResolver trustResolver) {\n\t\tAssert.notNull(trustResolver, \"trustResolver cannot be null\");\n\t\tthis.trustResolver = trustResolver;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t *\n\t * @since 5.8\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/SimpleRedirectInvalidSessionStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * Performs a redirect to a fixed URL when an invalid requested session is detected by the\n * {@code SessionManagementFilter}.\n *\n * @author Luke Taylor\n */\npublic final class SimpleRedirectInvalidSessionStrategy implements InvalidSessionStrategy {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final String destinationUrl;\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\tprivate boolean createNewSession = true;\n\n\tpublic SimpleRedirectInvalidSessionStrategy(String invalidSessionUrl) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), \"url must start with '/' or with 'http(s)'\");\n\t\tthis.destinationUrl = invalidSessionUrl;\n\t}\n\n\t@Override\n\tpublic void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {\n\t\tif (this.logger.isDebugEnabled()) {\n\t\t\tthis.logger.debug(\"Starting new session (if required) and redirecting to '\" + this.destinationUrl + \"'\");\n\t\t}\n\t\tif (this.createNewSession) {\n\t\t\trequest.getSession();\n\t\t}\n\t\tthis.redirectStrategy.sendRedirect(request, response, this.destinationUrl);\n\t}\n\n\t/**\n\t * Determines whether a new session should be created before redirecting (to avoid\n\t * possible looping issues where the same session ID is sent with the redirected\n\t * request). Alternatively, ensure that the configured URL does not pass through the\n\t * {@code SessionManagementFilter}.\n\t * @param createNewSession defaults to {@code true}.\n\t */\n\tpublic void setCreateNewSession(boolean createNewSession) {\n\t\tthis.createNewSession = createNewSession;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/SimpleRedirectSessionInformationExpiredStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.io.IOException;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.util.Assert;\n\n/**\n * Performs a redirect to a fixed URL when an expired session is detected by the\n * {@code ConcurrentSessionFilter}.\n *\n * @author Marten Deinum\n * @since 4.2.0\n */\npublic final class SimpleRedirectSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final String destinationUrl;\n\n\tprivate final RedirectStrategy redirectStrategy;\n\n\tpublic SimpleRedirectSessionInformationExpiredStrategy(String invalidSessionUrl) {\n\t\tthis(invalidSessionUrl, new DefaultRedirectStrategy());\n\t}\n\n\tpublic SimpleRedirectSessionInformationExpiredStrategy(String invalidSessionUrl,\n\t\t\tRedirectStrategy redirectStrategy) {\n\t\tAssert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), \"url must start with '/' or with 'http(s)'\");\n\t\tthis.destinationUrl = invalidSessionUrl;\n\t\tthis.redirectStrategy = redirectStrategy;\n\t}\n\n\t@Override\n\tpublic void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException {\n\t\tthis.logger.debug(\"Redirecting to '\" + this.destinationUrl + \"'\");\n\t\tthis.redirectStrategy.sendRedirect(event.getRequest(), event.getResponse(), this.destinationUrl);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/session/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Session management filters, {@code HttpSession} events and publisher classes.\n */\n@NullMarked\npackage org.springframework.security.web.session;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/transport/HttpsRedirectFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.transport;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.PortMapperImpl;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.util.UrlUtils;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\n/**\n * Redirects any non-HTTPS request to its HTTPS equivalent.\n *\n * <p>\n * Can be configured to use a {@link RequestMatcher} to narrow which requests get\n * redirected.\n *\n * <p>\n * Can also be configured for custom ports using {@link PortMapper}.\n *\n * @author Josh Cummings\n * @since 6.5\n */\npublic final class HttpsRedirectFilter extends OncePerRequestFilter {\n\n\tprivate PortMapper portMapper = new PortMapperImpl();\n\n\tprivate RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;\n\n\tprivate final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!isInsecure(request)) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tif (!this.requestMatcher.matches(request)) {\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\t\tString redirectUri = createRedirectUri(request);\n\t\tthis.redirectStrategy.sendRedirect(request, response, redirectUri);\n\t}\n\n\t/**\n\t * Use this {@link PortMapper} for mapping custom ports\n\t * @param portMapper the {@link PortMapper} to use\n\t */\n\tpublic void setPortMapper(PortMapper portMapper) {\n\t\tAssert.notNull(portMapper, \"portMapper cannot be null\");\n\t\tthis.portMapper = portMapper;\n\t}\n\n\t/**\n\t * Use this {@link RequestMatcher} to narrow which requests are redirected to HTTPS.\n\t *\n\t * The filter already first checks for HTTPS in the uri scheme, so it is not necessary\n\t * to include that check in this matcher.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\tprivate boolean isInsecure(HttpServletRequest request) {\n\t\treturn !\"https\".equals(request.getScheme());\n\t}\n\n\tprivate String createRedirectUri(HttpServletRequest request) {\n\t\tString url = UrlUtils.buildFullRequestUrl(request);\n\t\tUriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);\n\t\tUriComponents components = builder.build();\n\t\tint port = components.getPort();\n\t\tif (port > 0) {\n\t\t\tInteger httpsPort = this.portMapper.lookupHttpsPort(port);\n\t\t\tAssert.state(httpsPort != null, () -> \"HTTP Port '\" + port + \"' does not have a corresponding HTTPS Port\");\n\t\t\tbuilder.port(httpsPort);\n\t\t}\n\t\treturn builder.scheme(\"https\").toUriString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/transport/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Spring Security HTTP transport support.\n */\n@NullMarked\npackage org.springframework.security.web.transport;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/OnCommittedResponseWrapper.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.Locale;\n\nimport jakarta.servlet.ServletOutputStream;\nimport jakarta.servlet.WriteListener;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpServletResponseWrapper;\n\n/**\n * Base class for response wrappers which encapsulate the logic for handling an event when\n * the {@link jakarta.servlet.http.HttpServletResponse} is committed.\n *\n * @author Rob Winch\n * @since 4.0.2\n */\npublic abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {\n\n\tprivate boolean disableOnCommitted;\n\n\t/**\n\t * The Content-Length response header. If this is greater than 0, then once\n\t * {@link #contentWritten} is larger than or equal the response is considered\n\t * committed.\n\t */\n\tprivate long contentLength;\n\n\t/**\n\t * The size of data written to the response body. The field will only be updated when\n\t * {@link #disableOnCommitted} is false.\n\t */\n\tprivate long contentWritten;\n\n\t/**\n\t * @param response the response to be wrapped\n\t */\n\tpublic OnCommittedResponseWrapper(HttpServletResponse response) {\n\t\tsuper(response);\n\t}\n\n\t@Override\n\tpublic void addHeader(String name, String value) {\n\t\tcheckContentLengthHeader(name, value);\n\t\tsuper.addHeader(name, value);\n\t}\n\n\t@Override\n\tpublic void addIntHeader(String name, int value) {\n\t\tcheckContentLengthHeader(name, value);\n\t\tsuper.addIntHeader(name, value);\n\t}\n\n\t@Override\n\tpublic void setHeader(String name, String value) {\n\t\tcheckContentLengthHeader(name, value);\n\t\tsuper.setHeader(name, value);\n\t}\n\n\t@Override\n\tpublic void setIntHeader(String name, int value) {\n\t\tcheckContentLengthHeader(name, value);\n\t\tsuper.setIntHeader(name, value);\n\t}\n\n\tprivate void checkContentLengthHeader(String name, int value) {\n\t\tif (\"Content-Length\".equalsIgnoreCase(name)) {\n\t\t\tsetContentLength(value);\n\t\t}\n\t}\n\n\tprivate void checkContentLengthHeader(String name, String value) {\n\t\tif (\"Content-Length\".equalsIgnoreCase(name)) {\n\t\t\tsetContentLength(Long.parseLong(value));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setContentLength(int len) {\n\t\tsetContentLength((long) len);\n\t\tsuper.setContentLength(len);\n\t}\n\n\t@Override\n\tpublic void setContentLengthLong(long len) {\n\t\tsetContentLength(len);\n\t\tsuper.setContentLengthLong(len);\n\t}\n\n\tprivate void setContentLength(long len) {\n\t\tthis.contentLength = len;\n\t\tcheckContentLength(0);\n\t}\n\n\t/**\n\t * Invoke this method to disable invoking\n\t * {@link OnCommittedResponseWrapper#onResponseCommitted()} when the\n\t * {@link jakarta.servlet.http.HttpServletResponse} is committed. This can be useful\n\t * in the event that Async Web Requests are made.\n\t */\n\tprotected void disableOnResponseCommitted() {\n\t\tthis.disableOnCommitted = true;\n\t}\n\n\t/**\n\t * Returns true if {@link #onResponseCommitted()} will be invoked when the response is\n\t * committed, else false.\n\t * @return if {@link #onResponseCommitted()} is enabled\n\t */\n\tprotected boolean isDisableOnResponseCommitted() {\n\t\treturn this.disableOnCommitted;\n\t}\n\n\t/**\n\t * Implement the logic for handling the\n\t * {@link jakarta.servlet.http.HttpServletResponse} being committed\n\t */\n\tprotected abstract void onResponseCommitted();\n\n\t/**\n\t * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked\n\t * before calling the superclass <code>sendError()</code>\n\t */\n\t@Override\n\tpublic final void sendError(int sc) throws IOException {\n\t\tdoOnResponseCommitted();\n\t\tsuper.sendError(sc);\n\t}\n\n\t/**\n\t * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked\n\t * before calling the superclass <code>sendError()</code>\n\t */\n\t@Override\n\tpublic final void sendError(int sc, String msg) throws IOException {\n\t\tdoOnResponseCommitted();\n\t\tsuper.sendError(sc, msg);\n\t}\n\n\t/**\n\t * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked\n\t * before calling the superclass <code>sendRedirect()</code>\n\t */\n\t@Override\n\tpublic final void sendRedirect(String location) throws IOException {\n\t\tdoOnResponseCommitted();\n\t\tsuper.sendRedirect(location);\n\t}\n\n\t/**\n\t * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked\n\t * before calling the calling <code>getOutputStream().close()</code> or\n\t * <code>getOutputStream().flush()</code>\n\t */\n\t@Override\n\tpublic ServletOutputStream getOutputStream() throws IOException {\n\t\treturn new SaveContextServletOutputStream(super.getOutputStream());\n\t}\n\n\t/**\n\t * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked\n\t * before calling the <code>getWriter().close()</code> or\n\t * <code>getWriter().flush()</code>\n\t */\n\t@Override\n\tpublic PrintWriter getWriter() throws IOException {\n\t\treturn new SaveContextPrintWriter(super.getWriter());\n\t}\n\n\t/**\n\t * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked\n\t * before calling the superclass <code>flushBuffer()</code>\n\t */\n\t@Override\n\tpublic void flushBuffer() throws IOException {\n\t\tdoOnResponseCommitted();\n\t\tsuper.flushBuffer();\n\t}\n\n\tprivate void trackContentLength(boolean content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\tcheckContentLength(content ? 4 : 5); // TODO Localization\n\t\t}\n\t}\n\n\tprivate void trackContentLength(char content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\tcheckContentLength(1);\n\t\t}\n\t}\n\n\tprivate void trackContentLength(Object content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\ttrackContentLength(String.valueOf(content));\n\t\t}\n\t}\n\n\tprivate void trackContentLength(byte[] content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\tcheckContentLength((content != null) ? content.length : 0);\n\t\t}\n\t}\n\n\tprivate void trackContentLength(char[] content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\tcheckContentLength((content != null) ? content.length : 0);\n\t\t}\n\t}\n\n\tprivate void trackContentLength(int content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\ttrackContentLength(String.valueOf(content));\n\t\t}\n\t}\n\n\tprivate void trackContentLength(float content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\ttrackContentLength(String.valueOf(content));\n\t\t}\n\t}\n\n\tprivate void trackContentLength(double content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\ttrackContentLength(String.valueOf(content));\n\t\t}\n\t}\n\n\tprivate void trackContentLengthLn() {\n\t\tif (!this.disableOnCommitted) {\n\t\t\ttrackContentLength(\"\\r\\n\");\n\t\t}\n\t}\n\n\tprivate void trackContentLength(String content) {\n\t\tif (!this.disableOnCommitted) {\n\t\t\tint contentLength = (content != null) ? content.length() : 4;\n\t\t\tcheckContentLength(contentLength);\n\t\t}\n\t}\n\n\t/**\n\t * Adds the contentLengthToWrite to the total contentWritten size and checks to see if\n\t * the response should be written.\n\t * @param contentLengthToWrite the size of the content that is about to be written.\n\t */\n\tprivate void checkContentLength(long contentLengthToWrite) {\n\t\tthis.contentWritten += contentLengthToWrite;\n\t\tboolean isBodyFullyWritten = this.contentLength > 0 && this.contentWritten >= this.contentLength;\n\t\tint bufferSize = getBufferSize();\n\t\tboolean requiresFlush = bufferSize > 0 && this.contentWritten >= bufferSize;\n\t\tif (isBodyFullyWritten || requiresFlush) {\n\t\t\tdoOnResponseCommitted();\n\t\t}\n\t}\n\n\t/**\n\t * Calls <code>onResponseCommitted()</code> with the current contents as long as\n\t * {@link #disableOnResponseCommitted()} was not invoked.\n\t */\n\tprivate void doOnResponseCommitted() {\n\t\tif (!this.disableOnCommitted) {\n\t\t\tonResponseCommitted();\n\t\t\tdisableOnResponseCommitted();\n\t\t}\n\t}\n\n\t/**\n\t * Ensures {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before\n\t * calling the prior to methods that commit the response. We delegate all methods to\n\t * the original {@link java.io.PrintWriter} to ensure that the behavior is as close to\n\t * the original {@link java.io.PrintWriter} as possible. See SEC-2039\n\t *\n\t * @author Rob Winch\n\t */\n\tprivate class SaveContextPrintWriter extends PrintWriter {\n\n\t\tprivate final PrintWriter delegate;\n\n\t\tSaveContextPrintWriter(PrintWriter delegate) {\n\t\t\tsuper(delegate);\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic void flush() {\n\t\t\tdoOnResponseCommitted();\n\t\t\tthis.delegate.flush();\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tdoOnResponseCommitted();\n\t\t\tthis.delegate.close();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn this.delegate.equals(obj);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn this.delegate.hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn getClass().getName() + \"[delegate=\" + this.delegate.toString() + \"]\";\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean checkError() {\n\t\t\treturn this.delegate.checkError();\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(int c) {\n\t\t\ttrackContentLength(c);\n\t\t\tthis.delegate.write(c);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(char[] buf, int off, int len) {\n\t\t\tcheckContentLength(len);\n\t\t\tthis.delegate.write(buf, off, len);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(char[] buf) {\n\t\t\ttrackContentLength(buf);\n\t\t\tthis.delegate.write(buf);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(String s, int off, int len) {\n\t\t\tcheckContentLength(len);\n\t\t\tthis.delegate.write(s, off, len);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(String s) {\n\t\t\ttrackContentLength(s);\n\t\t\tthis.delegate.write(s);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(boolean b) {\n\t\t\ttrackContentLength(b);\n\t\t\tthis.delegate.print(b);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(char c) {\n\t\t\ttrackContentLength(c);\n\t\t\tthis.delegate.print(c);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(int i) {\n\t\t\ttrackContentLength(i);\n\t\t\tthis.delegate.print(i);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(long l) {\n\t\t\ttrackContentLength(l);\n\t\t\tthis.delegate.print(l);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(float f) {\n\t\t\ttrackContentLength(f);\n\t\t\tthis.delegate.print(f);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(double d) {\n\t\t\ttrackContentLength(d);\n\t\t\tthis.delegate.print(d);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(char[] s) {\n\t\t\ttrackContentLength(s);\n\t\t\tthis.delegate.print(s);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(String s) {\n\t\t\ttrackContentLength(s);\n\t\t\tthis.delegate.print(s);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(Object obj) {\n\t\t\ttrackContentLength(obj);\n\t\t\tthis.delegate.print(obj);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println() {\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println();\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(boolean x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(char x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(int x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(long x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(float x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(double x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(char[] x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(String x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(Object x) {\n\t\t\ttrackContentLength(x);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(x);\n\t\t}\n\n\t\t@Override\n\t\tpublic PrintWriter printf(String format, Object... args) {\n\t\t\treturn this.delegate.printf(format, args);\n\t\t}\n\n\t\t@Override\n\t\tpublic PrintWriter printf(Locale l, String format, Object... args) {\n\t\t\treturn this.delegate.printf(l, format, args);\n\t\t}\n\n\t\t@Override\n\t\tpublic PrintWriter format(String format, Object... args) {\n\t\t\treturn this.delegate.format(format, args);\n\t\t}\n\n\t\t@Override\n\t\tpublic PrintWriter format(Locale l, String format, Object... args) {\n\t\t\treturn this.delegate.format(l, format, args);\n\t\t}\n\n\t\t@Override\n\t\tpublic PrintWriter append(CharSequence csq) {\n\t\t\tcheckContentLength(csq.length());\n\t\t\treturn this.delegate.append(csq);\n\t\t}\n\n\t\t@Override\n\t\tpublic PrintWriter append(CharSequence csq, int start, int end) {\n\t\t\tcheckContentLength(end - start);\n\t\t\treturn this.delegate.append(csq, start, end);\n\t\t}\n\n\t\t@Override\n\t\tpublic PrintWriter append(char c) {\n\t\t\ttrackContentLength(c);\n\t\t\treturn this.delegate.append(c);\n\t\t}\n\n\t}\n\n\t/**\n\t * Ensures{@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before\n\t * calling methods that commit the response. We delegate all methods to the original\n\t * {@link jakarta.servlet.ServletOutputStream} to ensure that the behavior is as close\n\t * to the original {@link jakarta.servlet.ServletOutputStream} as possible. See\n\t * SEC-2039\n\t *\n\t * @author Rob Winch\n\t */\n\tprivate class SaveContextServletOutputStream extends ServletOutputStream {\n\n\t\tprivate final ServletOutputStream delegate;\n\n\t\tSaveContextServletOutputStream(ServletOutputStream delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(int b) throws IOException {\n\t\t\ttrackContentLength(b);\n\t\t\tthis.delegate.write(b);\n\t\t}\n\n\t\t@Override\n\t\tpublic void flush() throws IOException {\n\t\t\tdoOnResponseCommitted();\n\t\t\tthis.delegate.flush();\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws IOException {\n\t\t\tdoOnResponseCommitted();\n\t\t\tthis.delegate.close();\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(boolean b) throws IOException {\n\t\t\ttrackContentLength(b);\n\t\t\tthis.delegate.print(b);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(char c) throws IOException {\n\t\t\ttrackContentLength(c);\n\t\t\tthis.delegate.print(c);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(double d) throws IOException {\n\t\t\ttrackContentLength(d);\n\t\t\tthis.delegate.print(d);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(float f) throws IOException {\n\t\t\ttrackContentLength(f);\n\t\t\tthis.delegate.print(f);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(int i) throws IOException {\n\t\t\ttrackContentLength(i);\n\t\t\tthis.delegate.print(i);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(long l) throws IOException {\n\t\t\ttrackContentLength(l);\n\t\t\tthis.delegate.print(l);\n\t\t}\n\n\t\t@Override\n\t\tpublic void print(String s) throws IOException {\n\t\t\ttrackContentLength(s);\n\t\t\tthis.delegate.print(s);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println() throws IOException {\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println();\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(boolean b) throws IOException {\n\t\t\ttrackContentLength(b);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(b);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(char c) throws IOException {\n\t\t\ttrackContentLength(c);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(c);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(double d) throws IOException {\n\t\t\ttrackContentLength(d);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(d);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(float f) throws IOException {\n\t\t\ttrackContentLength(f);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(f);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(int i) throws IOException {\n\t\t\ttrackContentLength(i);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(i);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(long l) throws IOException {\n\t\t\ttrackContentLength(l);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(l);\n\t\t}\n\n\t\t@Override\n\t\tpublic void println(String s) throws IOException {\n\t\t\ttrackContentLength(s);\n\t\t\ttrackContentLengthLn();\n\t\t\tthis.delegate.println(s);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(byte[] b) throws IOException {\n\t\t\ttrackContentLength(b);\n\t\t\tthis.delegate.write(b);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(byte[] b, int off, int len) throws IOException {\n\t\t\tcheckContentLength(len);\n\t\t\tthis.delegate.write(b, off, len);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isReady() {\n\t\t\treturn this.delegate.isReady();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setWriteListener(WriteListener writeListener) {\n\t\t\tthis.delegate.setWriteListener(writeListener);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn this.delegate.equals(obj);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn this.delegate.hashCode();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn getClass().getName() + \"[delegate=\" + this.delegate.toString() + \"]\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/RedirectUrlBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Internal class for building redirect URLs.\n *\n * Could probably make more use of the classes in java.net for this.\n *\n * @author Luke Taylor\n * @since 2.0\n */\npublic class RedirectUrlBuilder {\n\n\tprivate @Nullable String scheme;\n\n\tprivate @Nullable String serverName;\n\n\tprivate int port;\n\n\tprivate @Nullable String contextPath;\n\n\tprivate @Nullable String servletPath;\n\n\tprivate @Nullable String pathInfo;\n\n\tprivate @Nullable String query;\n\n\tpublic void setScheme(String scheme) {\n\t\tAssert.isTrue(\"http\".equals(scheme) || \"https\".equals(scheme), () -> \"Unsupported scheme '\" + scheme + \"'\");\n\t\tthis.scheme = scheme;\n\t}\n\n\tpublic void setServerName(String serverName) {\n\t\tthis.serverName = serverName;\n\t}\n\n\tpublic void setPort(int port) {\n\t\tthis.port = port;\n\t}\n\n\tpublic void setContextPath(String contextPath) {\n\t\tthis.contextPath = contextPath;\n\t}\n\n\tpublic void setServletPath(String servletPath) {\n\t\tthis.servletPath = servletPath;\n\t}\n\n\tpublic void setPathInfo(String pathInfo) {\n\t\tthis.pathInfo = pathInfo;\n\t}\n\n\tpublic void setQuery(String query) {\n\t\tthis.query = query;\n\t}\n\n\tpublic String getUrl() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tAssert.notNull(this.scheme, \"scheme cannot be null\");\n\t\tAssert.notNull(this.serverName, \"serverName cannot be null\");\n\t\tsb.append(this.scheme).append(\"://\").append(this.serverName);\n\t\t// Append the port number if it's not standard for the scheme\n\t\tif (this.port != (this.scheme.equals(\"http\") ? 80 : 443)) {\n\t\t\tsb.append(\":\").append(this.port);\n\t\t}\n\t\tif (this.contextPath != null) {\n\t\t\tsb.append(this.contextPath);\n\t\t}\n\t\tif (this.servletPath != null) {\n\t\t\tsb.append(this.servletPath);\n\t\t}\n\t\tif (this.pathInfo != null) {\n\t\t\tsb.append(this.pathInfo);\n\t\t}\n\t\tif (this.query != null) {\n\t\t\tsb.append(\"?\").append(this.query);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/TextEscapeUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport org.springframework.util.StringUtils;\n\n/**\n * Internal utility for escaping characters in HTML strings.\n *\n * @author Luke Taylor\n *\n */\npublic abstract class TextEscapeUtils {\n\n\tpublic static String escapeEntities(String s) {\n\t\tif (!StringUtils.hasLength(s)) {\n\t\t\treturn s;\n\t\t}\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\tchar ch = s.charAt(i);\n\t\t\tif (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9') {\n\t\t\t\tsb.append(ch);\n\t\t\t}\n\t\t\telse if (ch == '<') {\n\t\t\t\tsb.append(\"&lt;\");\n\t\t\t}\n\t\t\telse if (ch == '>') {\n\t\t\t\tsb.append(\"&gt;\");\n\t\t\t}\n\t\t\telse if (ch == '&') {\n\t\t\t\tsb.append(\"&amp;\");\n\t\t\t}\n\t\t\telse if (Character.isWhitespace(ch)) {\n\t\t\t\tsb.append(\"&#\").append((int) ch).append(\";\");\n\t\t\t}\n\t\t\telse if (Character.isISOControl(ch)) {\n\t\t\t\t// ignore control chars\n\t\t\t}\n\t\t\telse if (Character.isHighSurrogate(ch)) {\n\t\t\t\tif (i + 1 >= s.length()) {\n\t\t\t\t\t// Unexpected end\n\t\t\t\t\tthrow new IllegalArgumentException(\"Missing low surrogate character at end of string\");\n\t\t\t\t}\n\t\t\t\tchar low = s.charAt(i + 1);\n\t\t\t\tif (!Character.isLowSurrogate(low)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Expected low surrogate character but found value = \" + (int) low);\n\t\t\t\t}\n\t\t\t\tint codePoint = Character.toCodePoint(ch, low);\n\t\t\t\tif (Character.isDefined(codePoint)) {\n\t\t\t\t\tsb.append(\"&#\").append(codePoint).append(\";\");\n\t\t\t\t}\n\t\t\t\ti++; // skip the next character as we have already dealt with it\n\t\t\t}\n\t\t\telse if (Character.isLowSurrogate(ch)) {\n\t\t\t\tthrow new IllegalArgumentException(\"Unexpected low surrogate character, value = \" + (int) ch);\n\t\t\t}\n\t\t\telse if (Character.isDefined(ch)) {\n\t\t\t\tsb.append(\"&#\").append((int) ch).append(\";\");\n\t\t\t}\n\t\t\t// Ignore anything else\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/ThrowableAnalyzer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Handler for analyzing {@link Throwable} instances.\n *\n * Can be subclassed to customize its behavior.\n *\n * @author Andreas Senft\n * @since 2.0\n */\npublic class ThrowableAnalyzer {\n\n\t/**\n\t * Default extractor for {@link Throwable} instances.\n\t *\n\t * @see Throwable#getCause()\n\t */\n\tpublic static final ThrowableCauseExtractor DEFAULT_EXTRACTOR = Throwable::getCause;\n\n\t/**\n\t * Default extractor for {@link InvocationTargetException} instances.\n\t *\n\t * @see InvocationTargetException#getTargetException()\n\t */\n\tpublic static final ThrowableCauseExtractor INVOCATIONTARGET_EXTRACTOR = (throwable) -> {\n\t\tverifyThrowableHierarchy(throwable, InvocationTargetException.class);\n\t\treturn ((InvocationTargetException) throwable).getTargetException();\n\t};\n\n\t/**\n\t * Comparator to order classes ascending according to their hierarchy relation. If two\n\t * classes have a hierarchical relation, the \"higher\" class is considered to be\n\t * greater by this comparator.<br>\n\t * For hierarchically unrelated classes their fully qualified name will be compared.\n\t */\n\tprivate static final Comparator<Class<? extends Throwable>> CLASS_HIERARCHY_COMPARATOR = (class1, class2) -> {\n\t\tif (class1.isAssignableFrom(class2)) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (class2.isAssignableFrom(class1)) {\n\t\t\treturn -1;\n\t\t}\n\t\treturn class1.getName().compareTo(class2.getName());\n\t};\n\n\t/**\n\t * Map of registered cause extractors. key: Class&lt;Throwable&gt;; value:\n\t * ThrowableCauseExtractor\n\t */\n\tprivate final Map<Class<? extends Throwable>, ThrowableCauseExtractor> extractorMap;\n\n\t/**\n\t * Creates a new <code>ThrowableAnalyzer</code> instance.\n\t */\n\tpublic ThrowableAnalyzer() {\n\t\tthis.extractorMap = new TreeMap<>(CLASS_HIERARCHY_COMPARATOR);\n\t\tinitExtractorMap();\n\t}\n\n\t/**\n\t * Registers a <code>ThrowableCauseExtractor</code> for the specified type. <i>Can be\n\t * used in subclasses overriding {@link #initExtractorMap()}.</i>\n\t * @param throwableType the type (has to be a subclass of <code>Throwable</code>)\n\t * @param extractor the associated <code>ThrowableCauseExtractor</code> (not\n\t * <code>null</code>)\n\t * @throws IllegalArgumentException if one of the arguments is invalid\n\t */\n\tprotected final void registerExtractor(Class<? extends Throwable> throwableType,\n\t\t\tThrowableCauseExtractor extractor) {\n\t\tAssert.notNull(extractor, \"Invalid extractor: null\");\n\t\tthis.extractorMap.put(throwableType, extractor);\n\t}\n\n\t/**\n\t * Initializes associations between <code>Throwable</code>s and\n\t * <code>ThrowableCauseExtractor</code>s. The default implementation performs the\n\t * following registrations:\n\t * <ul>\n\t * <li>{@link #DEFAULT_EXTRACTOR} for {@link Throwable}</li>\n\t * <li>{@link #INVOCATIONTARGET_EXTRACTOR} for {@link InvocationTargetException}</li>\n\t * </ul>\n\t * <br>\n\t * Subclasses overriding this method are encouraged to invoke the super method to\n\t * perform the default registrations. They can register additional extractors as\n\t * required.\n\t * <p>\n\t * Note: An extractor registered for a specific type is applicable for that type\n\t * <i>and all subtypes thereof</i>. However, extractors registered to more specific\n\t * types are guaranteed to be resolved first. So in the default case\n\t * InvocationTargetExceptions will be handled by {@link #INVOCATIONTARGET_EXTRACTOR}\n\t * while all other throwables are handled by {@link #DEFAULT_EXTRACTOR}.\n\t *\n\t * @see #registerExtractor(Class, ThrowableCauseExtractor)\n\t */\n\tprotected void initExtractorMap() {\n\t\tregisterExtractor(InvocationTargetException.class, INVOCATIONTARGET_EXTRACTOR);\n\t\tregisterExtractor(Throwable.class, DEFAULT_EXTRACTOR);\n\t}\n\n\t/**\n\t * Returns an array containing the classes for which extractors are registered. The\n\t * order of the classes is the order in which comparisons will occur for resolving a\n\t * matching extractor.\n\t * @return the types for which extractors are registered\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tfinal Class<? extends Throwable>[] getRegisteredTypes() {\n\t\tSet<Class<? extends Throwable>> typeList = this.extractorMap.keySet();\n\t\treturn typeList.toArray(new Class[0]);\n\t}\n\n\t/**\n\t * Determines the cause chain of the provided <code>Throwable</code>. The returned\n\t * array contains all throwables extracted from the stacktrace, using the registered\n\t * {@link ThrowableCauseExtractor extractors}. The elements of the array are ordered:\n\t * The first element is the passed in throwable itself. The following elements appear\n\t * in their order downward the stacktrace.\n\t * <p>\n\t * Note: If no {@link ThrowableCauseExtractor} is registered for this instance then\n\t * the returned array will always only contain the passed in throwable.\n\t * @param throwable the <code>Throwable</code> to analyze\n\t * @return an array of all determined throwables from the stacktrace\n\t * @throws IllegalArgumentException if the throwable is <code>null</code>\n\t *\n\t * @see #initExtractorMap()\n\t */\n\tpublic final Throwable[] determineCauseChain(Throwable throwable) {\n\t\tAssert.notNull(throwable, \"Invalid throwable: null\");\n\t\tList<Throwable> chain = new ArrayList<>();\n\t\tThrowable currentThrowable = throwable;\n\t\twhile (currentThrowable != null) {\n\t\t\tchain.add(currentThrowable);\n\t\t\tcurrentThrowable = extractCause(currentThrowable);\n\t\t}\n\t\treturn chain.toArray(new Throwable[0]);\n\t}\n\n\t/**\n\t * Extracts the cause of the given throwable using an appropriate extractor.\n\t * @param throwable the <code>Throwable</code> (not <code>null</code>\n\t * @return the cause, may be <code>null</code> if none could be resolved\n\t */\n\tprivate @Nullable Throwable extractCause(Throwable throwable) {\n\t\tfor (Map.Entry<Class<? extends Throwable>, ThrowableCauseExtractor> entry : this.extractorMap.entrySet()) {\n\t\t\tClass<? extends Throwable> throwableType = entry.getKey();\n\t\t\tif (throwableType.isInstance(throwable)) {\n\t\t\t\tThrowableCauseExtractor extractor = entry.getValue();\n\t\t\t\treturn extractor.extractCause(throwable);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the first throwable from the passed in array that is assignable to the\n\t * provided type. A returned instance is safe to be cast to the specified type.\n\t * <p>\n\t * If the passed in array is null or empty this method returns <code>null</code>.\n\t * @param throwableType the type to look for\n\t * @param chain the array (will be processed in element order)\n\t * @return the found <code>Throwable</code>, <code>null</code> if not found\n\t * @throws IllegalArgumentException if the provided type is <code>null</code> or no\n\t * subclass of <code>Throwable</code>\n\t */\n\tpublic final @Nullable Throwable getFirstThrowableOfType(Class<? extends Throwable> throwableType,\n\t\t\tThrowable[] chain) {\n\t\tif (chain != null) {\n\t\t\tfor (Throwable t : chain) {\n\t\t\t\tif ((t != null) && throwableType.isInstance(t)) {\n\t\t\t\t\treturn t;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Verifies that the provided throwable is a valid subclass of the provided type (or\n\t * of the type itself). If <code>expectedBaseType</code> is <code>null</code>, no\n\t * check will be performed.\n\t * <p>\n\t * Can be used for verification purposes in implementations of\n\t * {@link ThrowableCauseExtractor extractors}.\n\t * @param throwable the <code>Throwable</code> to check\n\t * @param expectedBaseType the type to check against\n\t * @throws IllegalArgumentException if <code>throwable</code> is either\n\t * <code>null</code> or its type is not assignable to <code>expectedBaseType</code>\n\t */\n\tpublic static void verifyThrowableHierarchy(Throwable throwable, Class<? extends Throwable> expectedBaseType) {\n\t\tif (expectedBaseType == null) {\n\t\t\treturn;\n\t\t}\n\t\tAssert.notNull(throwable, \"Invalid throwable: null\");\n\t\tClass<? extends Throwable> throwableType = throwable.getClass();\n\t\tAssert.isTrue(expectedBaseType.isAssignableFrom(throwableType), () -> \"Invalid type: '\"\n\t\t\t\t+ throwableType.getName() + \"'. Has to be a subclass of '\" + expectedBaseType.getName() + \"'\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/ThrowableCauseExtractor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Interface for handlers extracting the cause out of a specific {@link Throwable} type.\n *\n * @author Andreas Senft\n * @since 2.0\n * @see ThrowableAnalyzer\n */\npublic interface ThrowableCauseExtractor {\n\n\t/**\n\t * Extracts the cause from the provided <code>Throwable</code>.\n\t * @param throwable the <code>Throwable</code>\n\t * @return the extracted cause (maybe <code>null</code>)\n\t * @throws IllegalArgumentException if <code>throwable</code> is <code>null</code> or\n\t * otherwise considered invalid for the implementation\n\t */\n\t@Nullable Throwable extractCause(Throwable throwable);\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/UrlUtils.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport java.util.Locale;\nimport java.util.regex.Pattern;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Provides static methods for composing URLs.\n * <p>\n * Placed into a separate class for visibility, so that changes to URL formatting\n * conventions will affect all users.\n *\n * @author Ben Alex\n */\npublic final class UrlUtils {\n\n\tprivate static final Pattern ABSOLUTE_URL = Pattern.compile(\"\\\\A[a-z0-9.+-]+://.*\", Pattern.CASE_INSENSITIVE);\n\n\tprivate UrlUtils() {\n\t}\n\n\tpublic static String buildFullRequestUrl(HttpServletRequest r) {\n\t\treturn buildFullRequestUrl(r.getScheme(), r.getServerName(), r.getServerPort(), r.getRequestURI(),\n\t\t\t\tr.getQueryString());\n\t}\n\n\t/**\n\t * Obtains the full URL the client used to make the request.\n\t * <p>\n\t * Note that the server port will not be shown if it is the default server port for\n\t * HTTP or HTTPS (80 and 443 respectively).\n\t * @return the full URL, suitable for redirects (not decoded).\n\t */\n\tpublic static String buildFullRequestUrl(String scheme, String serverName, int serverPort, String requestURI,\n\t\t\t@Nullable String queryString) {\n\t\tscheme = scheme.toLowerCase(Locale.ENGLISH);\n\t\tStringBuilder url = new StringBuilder();\n\t\turl.append(scheme).append(\"://\").append(serverName);\n\t\t// Only add port if not default\n\t\tif (\"http\".equals(scheme)) {\n\t\t\tif (serverPort != 80) {\n\t\t\t\turl.append(\":\").append(serverPort);\n\t\t\t}\n\t\t}\n\t\telse if (\"https\".equals(scheme)) {\n\t\t\tif (serverPort != 443) {\n\t\t\t\turl.append(\":\").append(serverPort);\n\t\t\t}\n\t\t}\n\t\t// Use the requestURI as it is encoded (RFC 3986) and hence suitable for\n\t\t// redirects.\n\t\turl.append(requestURI);\n\t\tif (queryString != null) {\n\t\t\turl.append(\"?\").append(queryString);\n\t\t}\n\t\treturn url.toString();\n\t}\n\n\t/**\n\t * Obtains the web application-specific fragment of the request URL.\n\t * <p>\n\t * Under normal spec conditions,\n\t *\n\t * <pre>\n\t * requestURI = contextPath + servletPath + pathInfo\n\t * </pre>\n\t *\n\t * But the requestURI is not decoded, whereas the servletPath and pathInfo are\n\t * (SEC-1255). This method is typically used to return a URL for matching against\n\t * secured paths, hence the decoded form is used in preference to the requestURI for\n\t * building the returned value. But this method may also be called using dummy request\n\t * objects which just have the requestURI and contextPath set, for example, so it will\n\t * fall back to using those.\n\t * @return the decoded URL, excluding any server name, context path or servlet path\n\t *\n\t */\n\tpublic static String buildRequestUrl(HttpServletRequest r) {\n\t\treturn buildRequestUrl(r.getServletPath(), r.getRequestURI(), r.getContextPath(), r.getPathInfo(),\n\t\t\t\tr.getQueryString());\n\t}\n\n\t/**\n\t * Obtains the web application-specific fragment of the URL.\n\t */\n\tprivate static String buildRequestUrl(String servletPath, String requestURI, String contextPath, String pathInfo,\n\t\t\tString queryString) {\n\t\tStringBuilder url = new StringBuilder();\n\t\tif (servletPath != null) {\n\t\t\turl.append(servletPath);\n\t\t\tif (pathInfo != null) {\n\t\t\t\turl.append(pathInfo);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\turl.append(requestURI.substring(contextPath.length()));\n\t\t}\n\t\tif (queryString != null) {\n\t\t\turl.append(\"?\").append(queryString);\n\t\t}\n\t\treturn url.toString();\n\t}\n\n\t/**\n\t * Returns true if the supplied URL starts with a \"/\" or is absolute.\n\t */\n\tpublic static boolean isValidRedirectUrl(String url) {\n\t\treturn url != null && (url.startsWith(\"/\") || isAbsoluteUrl(url));\n\t}\n\n\t/**\n\t * Decides if a URL is absolute based on whether it contains a valid scheme name, as\n\t * defined in RFC 1738.\n\t */\n\tpublic static boolean isAbsoluteUrl(String url) {\n\t\treturn (url != null) ? ABSOLUTE_URL.matcher(url).matches() : false;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/AndRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.util.Assert;\n\n/**\n * {@link RequestMatcher} that will return true if all of the passed in\n * {@link RequestMatcher} instances match.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class AndRequestMatcher implements RequestMatcher {\n\n\tprivate final List<RequestMatcher> requestMatchers;\n\n\t/**\n\t * Creates a new instance\n\t * @param requestMatchers the {@link RequestMatcher} instances to try\n\t */\n\tpublic AndRequestMatcher(List<RequestMatcher> requestMatchers) {\n\t\tAssert.notEmpty(requestMatchers, \"requestMatchers must contain a value\");\n\t\tAssert.noNullElements(requestMatchers, \"requestMatchers cannot contain null values\");\n\t\tthis.requestMatchers = requestMatchers;\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param requestMatchers the {@link RequestMatcher} instances to try\n\t */\n\tpublic AndRequestMatcher(RequestMatcher... requestMatchers) {\n\t\tthis(Arrays.asList(requestMatchers));\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\tfor (RequestMatcher matcher : this.requestMatchers) {\n\t\t\tif (!matcher.matches(request)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns a {@link MatchResult} for this {@link HttpServletRequest}. In the case of a\n\t * match, request variables are a composition of the request variables in underlying\n\t * matchers. In the event that two matchers have the same key, the last key is the one\n\t * propagated.\n\t * @param request the HTTP request\n\t * @return a {@link MatchResult} based on the given HTTP request\n\t * @since 6.1\n\t */\n\t@Override\n\tpublic MatchResult matcher(HttpServletRequest request) {\n\t\tMap<String, String> variables = new LinkedHashMap<>();\n\t\tfor (RequestMatcher matcher : this.requestMatchers) {\n\t\t\tMatchResult result = matcher.matcher(request);\n\t\t\tif (!result.isMatch()) {\n\t\t\t\treturn MatchResult.notMatch();\n\t\t\t}\n\t\t\tvariables.putAll(result.getVariables());\n\t\t}\n\t\treturn MatchResult.match(variables);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tAndRequestMatcher that = (AndRequestMatcher) o;\n\t\treturn Objects.equals(this.requestMatchers, that.requestMatchers);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.requestMatchers);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"And \" + this.requestMatchers;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/AnyRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\n/**\n * Matches any supplied request.\n *\n * @author Luke Taylor\n * @since 3.1\n */\npublic final class AnyRequestMatcher implements RequestMatcher {\n\n\tpublic static final RequestMatcher INSTANCE = new AnyRequestMatcher();\n\n\tprivate AnyRequestMatcher() {\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\treturn true;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"deprecation\")\n\tpublic boolean equals(Object obj) {\n\t\treturn obj instanceof AnyRequestMatcher\n\t\t\t\t|| obj instanceof org.springframework.security.web.util.matcher.AnyRequestMatcher;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 1;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"any request\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/DispatcherTypeRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.util.StringUtils;\n\n/**\n * Checks the {@link DispatcherType} to decide whether to match a given request.\n * {@code HttpServletRequest}.\n *\n * Can also be configured to match a specific HTTP method.\n *\n * @author Nick McKinney\n * @since 5.5\n */\npublic class DispatcherTypeRequestMatcher implements RequestMatcher {\n\n\tprivate final DispatcherType dispatcherType;\n\n\tprivate final @Nullable HttpMethod httpMethod;\n\n\t/**\n\t * Creates an instance which matches requests with the provided {@link DispatcherType}\n\t * @param dispatcherType the type to match against\n\t */\n\tpublic DispatcherTypeRequestMatcher(DispatcherType dispatcherType) {\n\t\tthis(dispatcherType, null);\n\t}\n\n\t/**\n\t * Creates an instance which matches requests with the provided {@link DispatcherType}\n\t * and {@link HttpMethod}\n\t * @param dispatcherType the type to match against\n\t * @param httpMethod the HTTP method to match. May be null to match all methods.\n\t */\n\tpublic DispatcherTypeRequestMatcher(DispatcherType dispatcherType, @Nullable HttpMethod httpMethod) {\n\t\tthis.dispatcherType = dispatcherType;\n\t\tthis.httpMethod = httpMethod;\n\t}\n\n\t/**\n\t * Performs the match against the request's method and dispatcher type.\n\t * @param request the request to check for a match\n\t * @return true if the http method and dispatcher type align\n\t */\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\tif (this.httpMethod != null && StringUtils.hasText(request.getMethod())\n\t\t\t\t&& this.httpMethod != HttpMethod.valueOf(request.getMethod())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn this.dispatcherType == request.getDispatcherType();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DispatcherTypeRequestMatcher{\" + \"dispatcherType=\" + this.dispatcherType + \", httpMethod=\"\n\t\t\t\t+ this.httpMethod + '}';\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/ELRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.spel.standard.SpelExpressionParser;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\nimport org.springframework.util.Assert;\n\n/**\n * A RequestMatcher implementation which uses a SpEL expression\n *\n * <p>\n * With the default EvaluationContext ({@link ELRequestMatcherContext}) you can use\n * <code>hasIpAddress()</code> and <code>hasHeader()</code>\n * </p>\n *\n * <p>\n * See {@link DelegatingAuthenticationEntryPoint} for an example configuration.\n * </p>\n *\n * @author Mike Wiesner\n * @since 3.0.2\n */\npublic class ELRequestMatcher implements RequestMatcher {\n\n\tprivate final Expression expression;\n\n\tpublic ELRequestMatcher(String el) {\n\t\tSpelExpressionParser parser = new SpelExpressionParser();\n\t\tthis.expression = parser.parseExpression(el);\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\tEvaluationContext context = createELContext(request);\n\t\tBoolean result = this.expression.getValue(context, Boolean.class);\n\t\tAssert.notNull(result,\n\t\t\t\t\"The expression \" + this.expression.getExpressionString() + \" returned null. Expected true|false\");\n\t\treturn result;\n\t}\n\n\t/**\n\t * Subclasses can override this methode if they want to use a different EL root\n\t * context\n\t * @return EL root context which is used to evaluate the expression\n\t */\n\tpublic EvaluationContext createELContext(HttpServletRequest request) {\n\t\treturn new StandardEvaluationContext(new ELRequestMatcherContext(request));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"EL [el=\\\"\").append(this.expression.getExpressionString()).append(\"\\\"\");\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/ELRequestMatcherContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.util.StringUtils;\n\nclass ELRequestMatcherContext {\n\n\tprivate final HttpServletRequest request;\n\n\tELRequestMatcherContext(HttpServletRequest request) {\n\t\tthis.request = request;\n\t}\n\n\tpublic boolean hasIpAddress(String ipAddress) {\n\t\treturn (new IpAddressMatcher(ipAddress).matches(this.request));\n\t}\n\n\tpublic boolean hasHeader(String headerName, String value) {\n\t\tString header = this.request.getHeader(headerName);\n\t\treturn StringUtils.hasText(header) && header.contains(value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/InetAddressMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.net.InetAddress;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Matches an {@link InetAddress}.\n *\n * @author Rossen Stoyanchev\n * @author Rob Winch\n * @since 7.1\n */\n@FunctionalInterface\npublic interface InetAddressMatcher {\n\n\t/**\n\t * Whether the given address matches.\n\t * @param address the {@link InetAddress} to check (may be {@code null})\n\t * @return {@code true} if the address matches, {@code false} otherwise\n\t */\n\tboolean matches(@Nullable InetAddress address);\n\n\t/**\n\t * Whether the given address string matches.\n\t * @param address the IP address string to check (may be {@code null})\n\t * @return {@code true} if the address matches, {@code false} otherwise\n\t */\n\tdefault boolean matches(@Nullable String address) {\n\t\treturn (address != null) ? matches(InetAddressParser.parseAddress(address)) : false;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/InetAddressMatchers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.net.InetAddress;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * Factory for creating {@link InetAddressMatcher} instances with various matching\n * strategies for IP addresses.\n *\n * @author Rob Winch\n * @since 7.1\n */\npublic final class InetAddressMatchers {\n\n\tprivate InetAddressMatchers() {\n\t}\n\n\t/**\n\t * Creates a new builder for configuring an {@link InetAddressMatcher}.\n\t * @return a new {@link Builder} instance\n\t */\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * Creates a new builder configured to match external (non-private) IP addresses.\n\t * @return a {@link Builder} configured to match external addresses\n\t */\n\tpublic static Builder matchExternal() {\n\t\treturn builder().matchAll(ExternalInetAddressMatcher.getInstance());\n\t}\n\n\t/**\n\t * Creates a new builder configured to match internal (private) IP addresses.\n\t * <p>\n\t * Internal addresses include loopback addresses (127.0.0.0/8 for IPv4, ::1 for IPv6),\n\t * private IPv4 address ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), and IPv6\n\t * Unique Local Addresses (fc00::/7).\n\t * @return a {@link Builder} configured to match internal addresses\n\t */\n\tpublic static Builder matchInternal() {\n\t\treturn builder().matchAll(InternalInetAddressMatcher.getInstance());\n\t}\n\n\t/**\n\t * A builder for constructing {@link InetAddressMatcher} instances with various\n\t * matching rules.\n\t *\n\t * @author Kian Jamali\n\t * @author Gábor Vaspöri\n\t * @author Rossen Stoyanchev\n\t * @author Rob Winch\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final List<InetAddressMatcher> matchers = new ArrayList<>();\n\n\t\tprivate boolean reportOnly;\n\n\t\t/**\n\t\t * Adds an include list matcher that permits only the specified addresses.\n\t\t * @param addresses the list of IP address patterns to include (cannot be null or\n\t\t * empty)\n\t\t * @return this builder for method chaining\n\t\t * @throws IllegalArgumentException if addresses is null or empty\n\t\t */\n\t\tpublic Builder includeAddresses(List<String> addresses) {\n\t\t\tAssert.notEmpty(addresses, \"addresses cannot be empty\");\n\t\t\tList<InetAddressMatcher> matchers = addresses.stream()\n\t\t\t\t.<InetAddressMatcher>map(IpInetAddressMatcher::new)\n\t\t\t\t.toList();\n\t\t\tthis.matchers.add(new IncludeListInetAddressMatcher(matchers));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds an exclude list matcher that blocks the specified addresses.\n\t\t * @param addresses the list of IP address patterns to exclude (cannot be null or\n\t\t * empty)\n\t\t * @return this builder for method chaining\n\t\t * @throws IllegalArgumentException if addresses is null or empty\n\t\t */\n\t\tpublic Builder excludeAddresses(List<String> addresses) {\n\t\t\tAssert.notEmpty(addresses, \"addresses cannot be empty\");\n\t\t\tList<InetAddressMatcher> matchers = addresses.stream()\n\t\t\t\t.<InetAddressMatcher>map(IpInetAddressMatcher::new)\n\t\t\t\t.toList();\n\t\t\tthis.matchers.add(new ExcludeListInetAddressMatcher(matchers));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Adds custom matchers to the matcher chain. All matchers must match for an\n\t\t * address to be permitted.\n\t\t * @param matchers the custom {@link InetAddressMatcher} instances to add (cannot\n\t\t * be null or empty)\n\t\t * @return this builder for method chaining\n\t\t * @throws IllegalArgumentException if matchers is null or empty\n\t\t */\n\t\tpublic Builder matchAll(InetAddressMatcher... matchers) {\n\t\t\tAssert.notEmpty(matchers, \"matchers cannot be empty\");\n\t\t\tfor (InetAddressMatcher matcher : matchers) {\n\t\t\t\tthis.matchers.add(matcher);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configures the matcher to operate in report-only mode. In this mode, matching\n\t\t * logic is evaluated and logged, but all addresses are allowed regardless of\n\t\t * match results.\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder reportOnly() {\n\t\t\tthis.reportOnly = true;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds the configured {@link InetAddressMatcher}.\n\t\t * @return the constructed {@link InetAddressMatcher}\n\t\t */\n\t\tpublic InetAddressMatcher build() {\n\t\t\treturn new CompositeInetAddressMatcher(this.matchers, this.reportOnly);\n\t\t}\n\n\t}\n\n\t/**\n\t * An {@link InetAddressMatcher} that matches addresses against an include list. Only\n\t * addresses that match an entry in the include list are permitted.\n\t *\n\t * @author Rossen Stoyanchev\n\t * @author Rob Winch\n\t */\n\tstatic final class IncludeListInetAddressMatcher implements InetAddressMatcher {\n\n\t\tprivate final List<InetAddressMatcher> includeList;\n\n\t\tIncludeListInetAddressMatcher(List<InetAddressMatcher> includeList) {\n\t\t\tAssert.notEmpty(includeList, \"includeList cannot be null or empty\");\n\t\t\tthis.includeList = new ArrayList<>(includeList);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(@Nullable InetAddress address) {\n\t\t\tfor (InetAddressMatcher matcher : this.includeList) {\n\t\t\t\tif (matcher.matches(address)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"IncludeListInetAddressMatcher[\\\"\" + this.includeList + \"\\\"]\";\n\t\t}\n\n\t}\n\n\t/**\n\t * An {@link InetAddressMatcher} that matches addresses against an exclude list.\n\t * Addresses that match an entry in the exclude list are rejected.\n\t *\n\t * @author Rossen Stoyanchev\n\t * @author Rob Winch\n\t */\n\tstatic final class ExcludeListInetAddressMatcher implements InetAddressMatcher {\n\n\t\tprivate final List<InetAddressMatcher> excludeList;\n\n\t\tExcludeListInetAddressMatcher(List<InetAddressMatcher> excludeList) {\n\t\t\tAssert.notEmpty(excludeList, \"excludeList cannot be null or empty\");\n\t\t\tthis.excludeList = new ArrayList<>(excludeList);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(@Nullable InetAddress address) {\n\t\t\tfor (InetAddressMatcher matcher : this.excludeList) {\n\t\t\t\tif (matcher.matches(address)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"ExcludeListInetAddressMatcher[\\\"\" + this.excludeList + \"\\\"]\";\n\t\t}\n\n\t}\n\n\t/**\n\t * An {@link InetAddressMatcher} that matches internal (private) addresses.\n\t * <p>\n\t * Internal addresses include loopback addresses (127.0.0.0/8 for IPv4, ::1 for IPv6),\n\t * private IPv4 address ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), and IPv6\n\t * Unique Local Addresses (fc00::/7).\n\t *\n\t * @author Gábor Vaspöri\n\t * @author Kian Jamali\n\t * @author Rossen Stoyanchev\n\t * @author Rob Winch\n\t */\n\tstatic final class InternalInetAddressMatcher implements InetAddressMatcher {\n\n\t\tprivate static final InternalInetAddressMatcher INSTANCE = new InternalInetAddressMatcher();\n\n\t\tstatic InternalInetAddressMatcher getInstance() {\n\t\t\treturn INSTANCE;\n\t\t}\n\n\t\tprivate InternalInetAddressMatcher() {\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(@Nullable InetAddress address) {\n\t\t\tif (address == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (address.isLoopbackAddress() || address.isLinkLocalAddress() || address.isSiteLocalAddress()) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tbyte[] rawAddress = address.getAddress();\n\n\t\t\tif (rawAddress.length == 16) {\n\t\t\t\t// Convert signed bytes to unsigned ints for easier matching logic\n\t\t\t\tint[] iAddr = new int[rawAddress.length];\n\t\t\t\tfor (int i = 0; i < rawAddress.length; i++) {\n\t\t\t\t\tiAddr[i] = Byte.toUnsignedInt(rawAddress[i]);\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * IPv6, check for Unique Local Addresses. We cannot rely on\n\t\t\t\t * Inet6Address.isSiteLocalAddress() here because the JVM implementation\n\t\t\t\t * dictates that fec0::/10 is the only site-local IPv6 address space,\n\t\t\t\t * based on the outdated RFC 2373. That RFC was deprecated by the IETF in\n\t\t\t\t * 2004 in favor of fc00::/7 (RFC 4193). To keep our private network\n\t\t\t\t * checking accurate to modern subnets, we maintain manual parsing.\n\t\t\t\t */\n\t\t\t\tif (iAddr[0] == 0xfc || iAddr[0] == 0xfd) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// IPv4/IPv6 translation, 64:ff9b\n\t\t\t\tif (iAddr[0] == 0x00 && iAddr[1] == 0x64 && iAddr[2] == 0xff && iAddr[3] == 0x9b) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tInetAddress ipv4Part = InetAddress.getByAddress(\n\t\t\t\t\t\t\t\tnew byte[] { rawAddress[12], rawAddress[13], rawAddress[14], rawAddress[15] });\n\n\t\t\t\t\t\tif (ipv4Part.isLoopbackAddress() || ipv4Part.isLinkLocalAddress()\n\t\t\t\t\t\t\t\t|| ipv4Part.isSiteLocalAddress()) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (java.net.UnknownHostException ex) {\n\t\t\t\t\t\t// Should not happen for 4-byte array\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"InternalInetAddressMatcher\";\n\t\t}\n\n\t}\n\n\t/**\n\t * An {@link InetAddressMatcher} that matches external (public) addresses.\n\t * <p>\n\t * External addresses are any addresses that are not internal (private) addresses.\n\t * This matcher delegates to {@link InternalInetAddressMatcher} and negates the\n\t * result.\n\t *\n\t * @author Gábor Vaspöri\n\t * @author Kian Jamali\n\t * @author Rossen Stoyanchev\n\t * @author Rob Winch\n\t */\n\tstatic final class ExternalInetAddressMatcher implements InetAddressMatcher {\n\n\t\tprivate static final ExternalInetAddressMatcher INSTANCE = new ExternalInetAddressMatcher();\n\n\t\tstatic ExternalInetAddressMatcher getInstance() {\n\t\t\treturn INSTANCE;\n\t\t}\n\n\t\tprivate final InternalInetAddressMatcher internalMatcher = InternalInetAddressMatcher.getInstance();\n\n\t\tprivate ExternalInetAddressMatcher() {\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(@Nullable InetAddress address) {\n\t\t\treturn !this.internalMatcher.matches(address);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"ExternalInetAddressMatcher\";\n\t\t}\n\n\t}\n\n\t/**\n\t * A composite {@link InetAddressMatcher} that chains multiple matchers together. All\n\t * matchers must match for an address to be allowed. If report-only mode is enabled,\n\t * matching results are logged but all addresses are permitted.\n\t *\n\t * @author Gábor Vaspöri\n\t * @author Kian Jamali\n\t * @author Rossen Stoyanchev\n\t * @author Rob Winch\n\t */\n\tstatic final class CompositeInetAddressMatcher implements InetAddressMatcher {\n\n\t\tprivate static final Log logger = LogFactory.getLog(InetAddressMatcher.class);\n\n\t\tprivate final List<InetAddressMatcher> matchers;\n\n\t\tprivate final boolean reportOnly;\n\n\t\tCompositeInetAddressMatcher(List<InetAddressMatcher> matchers, boolean reportOnly) {\n\t\t\tthis.matchers = new ArrayList<>(matchers);\n\t\t\tthis.reportOnly = reportOnly;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(@Nullable InetAddress address) {\n\t\t\tboolean result = doMatch(address);\n\t\t\treturn (this.reportOnly || result);\n\t\t}\n\n\t\tprivate boolean doMatch(@Nullable InetAddress address) {\n\t\t\tfor (InetAddressMatcher matcher : this.matchers) {\n\t\t\t\tif (!matcher.matches(address)) {\n\t\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\t\tlogger.debug(\"InetAddress \" + address + \" blocked by \" + matcher);\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/InetAddressParser.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.util.regex.Pattern;\n\nimport org.springframework.util.Assert;\n\n/**\n * Utility class for parsing IP addresses.\n *\n * @author Luke Taylor\n * @author Steve Riesenberg\n * @author Andrey Litvitski\n * @author Rob Winch\n * @since 7.1\n */\nfinal class InetAddressParser {\n\n\tprivate static Pattern IPV4 = Pattern.compile(\"^\\\\d{1,3}(?:\\\\.\\\\d{1,3}){0,3}(?:/\\\\d{1,2})?$\");\n\n\t/**\n\t * Parses the given address string into an {@link InetAddress}.\n\t * @param address the IP address string to parse\n\t * @return the parsed {@link InetAddress}\n\t * @throws IllegalArgumentException if the address cannot be parsed or appears to be a\n\t * hostname\n\t */\n\tstatic InetAddress parseAddress(String address) {\n\t\tassertNotHostName(address);\n\t\ttry {\n\t\t\treturn InetAddress.getByName(address);\n\t\t}\n\t\tcatch (UnknownHostException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Failed to parse address '\" + address + \"'\", ex);\n\t\t}\n\t}\n\n\tstatic void assertNotHostName(String ipAddress) {\n\t\tAssert.isTrue(isIpAddress(ipAddress),\n\t\t\t\t() -> String.format(\"ipAddress %s doesn't look like an IP Address. Is it a host name?\", ipAddress));\n\t}\n\n\tprivate static boolean isIpAddress(String ipAddress) {\n\t\tif (!org.springframework.util.StringUtils.hasText(ipAddress)) {\n\t\t\treturn false;\n\t\t}\n\t\t// @formatter:off\n\t\treturn IPV4.matcher(ipAddress).matches()\n\t\t\t|| ipAddress.charAt(0) == '['\n\t\t\t|| ipAddress.charAt(0) == ':'\n\t\t\t|| Character.digit(ipAddress.charAt(0), 16) != -1\n\t\t\t&& ipAddress.indexOf(':') > 0;\n\t\t// @formatter:on\n\t}\n\n\tprivate InetAddressParser() {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/IpAddressMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Matches a request based on IP Address or subnet mask matching against the remote\n * address.\n * <p>\n * Both IPv6 and IPv4 addresses are supported, but a matcher which is configured with an\n * IPv4 address will never match a request which returns an IPv6 address, and vice-versa.\n *\n * @author Luke Taylor\n * @author Steve Riesenberg\n * @author Andrey Litvitski\n * @since 3.0.2\n */\npublic final class IpAddressMatcher implements RequestMatcher {\n\n\tprivate final InetAddressMatcher matcher;\n\n\t/**\n\t * Takes a specific IP address or a range specified using the IP/Netmask (e.g.\n\t * 192.168.1.0/24 or 202.24.0.0/14).\n\t * @param ipAddress the address or range of addresses from which the request must\n\t * come.\n\t */\n\tpublic IpAddressMatcher(String ipAddress) {\n\t\tthis.matcher = new IpInetAddressMatcher(ipAddress);\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\treturn this.matcher.matches(request.getRemoteAddr());\n\t}\n\n\t/**\n\t * Checks if the given IP address string matches the configured address pattern.\n\t * @param ipAddress the IP address string to check (may be {@code null})\n\t * @return {@code true} if the address matches, {@code false} otherwise\n\t */\n\tpublic boolean matches(@Nullable String ipAddress) {\n\t\treturn this.matcher.matches(ipAddress);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.matcher.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/IpInetAddressMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.util.Objects;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Implementation of {@link InetAddressMatcher} that matches IP addresses with support for\n * CIDR notation (e.g., 192.168.1.0/24).\n * <p>\n * Both IPv4 and IPv6 addresses are supported. The matcher can be configured with either a\n * specific IP address or a subnet using CIDR notation.\n * <p>\n * The logic from this class was migrated from {@link IpAddressMatcher} to provide a more\n * general API that did not depend on the servlet APIs (e.g. HttpServletRequest).\n *\n * @author Luke Taylor\n * @author Steve Riesenberg\n * @author Andrey Litvitski\n * @since 7.1\n * @see IpAddressMatcher\n */\nfinal class IpInetAddressMatcher implements InetAddressMatcher {\n\n\tprivate static final Log logger = LogFactory.getLog(IpAddressMatcher.class);\n\n\tprivate final InetAddress requiredAddress;\n\n\tprivate final int nMaskBits;\n\n\tIpInetAddressMatcher(String ipAddress) {\n\t\tAssert.hasText(ipAddress, \"ipAddress cannot be empty\");\n\t\tString requiredAddress;\n\t\tint nMaskBits;\n\t\tif (ipAddress.indexOf('/') > 0) {\n\t\t\tString[] parts = Objects.requireNonNull(StringUtils.split(ipAddress, \"/\"));\n\t\t\trequiredAddress = parts[0];\n\t\t\tnMaskBits = Integer.parseInt(parts[1]);\n\t\t}\n\t\telse {\n\t\t\trequiredAddress = ipAddress;\n\t\t\tnMaskBits = -1;\n\t\t}\n\t\tthis.requiredAddress = InetAddressParser.parseAddress(requiredAddress);\n\t\tthis.nMaskBits = nMaskBits;\n\t\tAssert.isTrue(this.requiredAddress.getAddress().length * 8 >= this.nMaskBits, () -> String\n\t\t\t.format(\"IP address %s is too short for bitmask of length %d\", requiredAddress, this.nMaskBits));\n\t}\n\n\tprivate static InetAddress parse(String address) {\n\t\ttry {\n\t\t\tInetAddress result = InetAddress.getByName(address);\n\t\t\tif (address.matches(\".*[a-zA-Z\\\\-].*$\") && !address.contains(\":\")) {\n\t\t\t\tlogger.warn(\"Hostname '\" + address + \"' resolved to \" + result.toString()\n\t\t\t\t\t\t+ \" will be used on IP address matching\");\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\tcatch (UnknownHostException ex) {\n\t\t\tthrow new IllegalArgumentException(String.format(\"Failed to parse address '%s'\", address), ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean matches(@Nullable InetAddress toCheck) {\n\t\tif (toCheck == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.nMaskBits < 0) {\n\t\t\treturn toCheck.equals(this.requiredAddress);\n\t\t}\n\t\tbyte[] remAddr = toCheck.getAddress();\n\t\tbyte[] reqAddr = this.requiredAddress.getAddress();\n\t\tint nMaskFullBytes = this.nMaskBits / 8;\n\t\tbyte finalByte = (byte) (0xFF00 >> (this.nMaskBits & 0x07));\n\t\tfor (int i = 0; i < nMaskFullBytes; i++) {\n\t\t\tif (remAddr[i] != reqAddr[i]) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif (finalByte != 0) {\n\t\t\treturn (remAddr[nMaskFullBytes] & finalByte) == (reqAddr[nMaskFullBytes] & finalByte);\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tString hostAddress = this.requiredAddress.getHostAddress();\n\t\treturn (this.nMaskBits < 0) ? \"IpAddress [\" + hostAddress + \"]\"\n\t\t\t\t: \"IpAddress [\" + hostAddress + \"/\" + this.nMaskBits + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.util.Assert;\nimport org.springframework.web.HttpMediaTypeNotAcceptableException;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.accept.HeaderContentNegotiationStrategy;\nimport org.springframework.web.context.request.ServletWebRequest;\n\n/**\n * Allows matching {@link HttpServletRequest} based upon the {@link MediaType}'s resolved\n * from a {@link ContentNegotiationStrategy}.\n *\n * By default, the matching process will perform the following:\n *\n * <ul>\n * <li>The {@link ContentNegotiationStrategy} will resolve the {@link MediaType} 's for\n * the current request</li>\n * <li>Each matchingMediaTypes that was passed into the constructor will be compared\n * against the {@link MediaType} instances resolved from the\n * {@link ContentNegotiationStrategy}.</li>\n * <li>If one of the matchingMediaTypes is compatible with one of the resolved\n * {@link MediaType} returned from the {@link ContentNegotiationStrategy}, then it returns\n * true</li>\n * </ul>\n *\n * For example, consider the following example\n *\n * <pre>\n * GET /\n * Accept: application/json\n *\n * ContentNegotiationStrategy negotiationStrategy = new HeaderContentNegotiationStrategy()\n * MediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(negotiationStrategy, MediaType.APPLICATION_JSON);\n * assert matcher.matches(request) == true // returns true\n * </pre>\n *\n * The following will also return true\n *\n * <pre>\n * GET /\n * Accept: *&#47;*\n *\n * ContentNegotiationStrategy negotiationStrategy = new HeaderContentNegotiationStrategy()\n * MediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(negotiationStrategy, MediaType.APPLICATION_JSON);\n * assert matcher.matches(request) == true // returns true\n * </pre>\n *\n * <h3>Ignoring Media Types</h3>\n *\n * Sometimes you may want to ignore certain types of media types. For example, you may\n * want to match on \"application/json\" but ignore \"*&#47;\" sent by a web browser.\n *\n * <pre>\n * GET /\n * Accept: *&#47;*\n *\n * ContentNegotiationStrategy negotiationStrategy = new HeaderContentNegotiationStrategy()\n * MediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(negotiationStrategy, MediaType.APPLICATION_JSON);\n * matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n * assert matcher.matches(request) == false // returns false\n * </pre>\n *\n * <pre>\n * GET /\n * Accept: application/json\n *\n * ContentNegotiationStrategy negotiationStrategy = new HeaderContentNegotiationStrategy()\n * MediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(negotiationStrategy, MediaType.APPLICATION_JSON);\n * matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n * assert matcher.matches(request) == true // returns true\n * </pre>\n *\n * <h3>Exact media type comparison</h3>\n *\n * By default as long as the {@link MediaType} discovered by\n * {@link ContentNegotiationStrategy} returns true for\n * {@link MediaType#isCompatibleWith(MediaType)} on the matchingMediaTypes, the result of\n * the match is true. However, sometimes you may want to perform an exact match. This can\n * be done with the following examples:\n *\n * <pre>\n * GET /\n * Accept: application/json\n *\n * ContentNegotiationStrategy negotiationStrategy = new HeaderContentNegotiationStrategy()\n * MediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(negotiationStrategy, MediaType.APPLICATION_JSON);\n * matcher.setUseEquals(true);\n * assert matcher.matches(request) == true // returns true\n * </pre>\n *\n * <pre>\n * GET /\n * Accept: application/*\n *\n * ContentNegotiationStrategy negotiationStrategy = new HeaderContentNegotiationStrategy()\n * MediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(negotiationStrategy, MediaType.APPLICATION_JSON);\n * matcher.setUseEquals(true);\n * assert matcher.matches(request) == false // returns false\n * </pre>\n *\n * <pre>\n * GET /\n * Accept: *&#47;*\n *\n * ContentNegotiationStrategy negotiationStrategy = new HeaderContentNegotiationStrategy()\n * MediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(negotiationStrategy, MediaType.APPLICATION_JSON);\n * matcher.setUseEquals(true);\n * assert matcher.matches(request) == false // returns false\n * </pre>\n *\n * @author Rob Winch\n * @author Dan Zheng\n * @since 3.2\n */\n\npublic final class MediaTypeRequestMatcher implements RequestMatcher {\n\n\tprivate final Log logger = LogFactory.getLog(getClass());\n\n\tprivate final ContentNegotiationStrategy contentNegotiationStrategy;\n\n\tprivate final Collection<MediaType> matchingMediaTypes;\n\n\tprivate boolean useEquals;\n\n\tprivate Set<MediaType> ignoredMediaTypes = Collections.emptySet();\n\n\t/**\n\t * Creates an instance\n\t * @param matchingMediaTypes the {@link MediaType} that will make the http request.\n\t * @since 5.2\n\t */\n\tpublic MediaTypeRequestMatcher(MediaType... matchingMediaTypes) {\n\t\tthis(new HeaderContentNegotiationStrategy(), Arrays.asList(matchingMediaTypes));\n\t}\n\n\t/**\n\t * Creates an instance\n\t * @param matchingMediaTypes the {@link MediaType} that will make the http request.\n\t * @since 5.2\n\t */\n\tpublic MediaTypeRequestMatcher(Collection<MediaType> matchingMediaTypes) {\n\t\tthis(new HeaderContentNegotiationStrategy(), matchingMediaTypes);\n\t}\n\n\t/**\n\t * Creates an instance\n\t * @param contentNegotiationStrategy the {@link ContentNegotiationStrategy} to use\n\t * @param matchingMediaTypes the {@link MediaType} that will make the\n\t * {@link RequestMatcher} return true\n\t */\n\tpublic MediaTypeRequestMatcher(ContentNegotiationStrategy contentNegotiationStrategy,\n\t\t\tMediaType... matchingMediaTypes) {\n\t\tthis(contentNegotiationStrategy, Arrays.asList(matchingMediaTypes));\n\t}\n\n\t/**\n\t * Creates an instance\n\t * @param contentNegotiationStrategy the {@link ContentNegotiationStrategy} to use\n\t * @param matchingMediaTypes the {@link MediaType} that will make the\n\t * {@link RequestMatcher} return true\n\t */\n\tpublic MediaTypeRequestMatcher(ContentNegotiationStrategy contentNegotiationStrategy,\n\t\t\tCollection<MediaType> matchingMediaTypes) {\n\t\tAssert.notNull(contentNegotiationStrategy, \"ContentNegotiationStrategy cannot be null\");\n\t\tAssert.notEmpty(matchingMediaTypes, \"matchingMediaTypes cannot be null or empty\");\n\t\tthis.contentNegotiationStrategy = contentNegotiationStrategy;\n\t\tthis.matchingMediaTypes = matchingMediaTypes;\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\tList<MediaType> httpRequestMediaTypes;\n\t\ttry {\n\t\t\thttpRequestMediaTypes = this.contentNegotiationStrategy.resolveMediaTypes(new ServletWebRequest(request));\n\t\t}\n\t\tcatch (HttpMediaTypeNotAcceptableException ex) {\n\t\t\tthis.logger.debug(\"Failed to match request since failed to parse MediaTypes\", ex);\n\t\t\treturn false;\n\t\t}\n\t\tfor (MediaType httpRequestMediaType : httpRequestMediaTypes) {\n\t\t\tif (shouldIgnore(httpRequestMediaType)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (this.useEquals) {\n\t\t\t\treturn this.matchingMediaTypes.contains(httpRequestMediaType);\n\t\t\t}\n\t\t\tfor (MediaType matchingMediaType : this.matchingMediaTypes) {\n\t\t\t\tboolean isCompatibleWith = matchingMediaType.isCompatibleWith(httpRequestMediaType);\n\t\t\t\tif (isCompatibleWith) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean shouldIgnore(MediaType httpRequestMediaType) {\n\t\tfor (MediaType ignoredMediaType : this.ignoredMediaTypes) {\n\t\t\tif (httpRequestMediaType.includes(ignoredMediaType)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * If set to true, matches on exact {@link MediaType}, else uses\n\t * {@link MediaType#isCompatibleWith(MediaType)}.\n\t * @param useEquals specify if equals comparison should be used.\n\t */\n\tpublic void setUseEquals(boolean useEquals) {\n\t\tthis.useEquals = useEquals;\n\t}\n\n\t/**\n\t * Set the {@link MediaType} to ignore from the {@link ContentNegotiationStrategy}.\n\t * This is useful if for example, you want to match on\n\t * {@link MediaType#APPLICATION_JSON} but want to ignore {@link MediaType#ALL}.\n\t * @param ignoredMediaTypes the {@link MediaType}'s to ignore from the\n\t * {@link ContentNegotiationStrategy}\n\t */\n\tpublic void setIgnoredMediaTypes(Set<MediaType> ignoredMediaTypes) {\n\t\tthis.ignoredMediaTypes = ignoredMediaTypes;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof MediaTypeRequestMatcher that)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(this.contentNegotiationStrategy.getClass(), that.contentNegotiationStrategy.getClass())\n\t\t\t\t&& Objects.equals(this.useEquals, that.useEquals)\n\t\t\t\t&& Objects.equals(this.matchingMediaTypes, that.matchingMediaTypes)\n\t\t\t\t&& Objects.equals(this.ignoredMediaTypes, that.ignoredMediaTypes);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.contentNegotiationStrategy.getClass(), this.useEquals, this.matchingMediaTypes,\n\t\t\t\tthis.ignoredMediaTypes);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"MediaTypeRequestMatcher [contentNegotiationStrategy=\" + this.contentNegotiationStrategy\n\t\t\t\t+ \", matchingMediaTypes=\" + this.matchingMediaTypes + \", useEquals=\" + this.useEquals\n\t\t\t\t+ \", ignoredMediaTypes=\" + this.ignoredMediaTypes + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/NegatedRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link RequestMatcher} that will negate the {@link RequestMatcher} passed in. For\n * example, if the {@link RequestMatcher} passed in returns true,\n * {@link NegatedRequestMatcher} will return false. If the {@link RequestMatcher} passed\n * in returns false, {@link NegatedRequestMatcher} will return true.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic class NegatedRequestMatcher implements RequestMatcher {\n\n\tprivate final RequestMatcher requestMatcher;\n\n\t/**\n\t * Creates a new instance\n\t * @param requestMatcher the {@link RequestMatcher} that will be negated.\n\t */\n\tpublic NegatedRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\treturn !this.requestMatcher.matches(request);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof NegatedRequestMatcher that)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(this.requestMatcher, that.requestMatcher);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.requestMatcher);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Not [\" + this.requestMatcher + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/OrRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.util.Assert;\n\n/**\n * {@link RequestMatcher} that will return true if any of the passed in\n * {@link RequestMatcher} instances match.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class OrRequestMatcher implements RequestMatcher {\n\n\tprivate final List<RequestMatcher> requestMatchers;\n\n\t/**\n\t * Creates a new instance\n\t * @param requestMatchers the {@link RequestMatcher} instances to try\n\t */\n\tpublic OrRequestMatcher(List<RequestMatcher> requestMatchers) {\n\t\tAssert.notEmpty(requestMatchers, \"requestMatchers must contain a value\");\n\t\tAssert.noNullElements(requestMatchers, \"requestMatchers cannot contain null values\");\n\t\tthis.requestMatchers = requestMatchers;\n\t}\n\n\t/**\n\t * Creates a new instance\n\t * @param requestMatchers the {@link RequestMatcher} instances to try\n\t */\n\tpublic OrRequestMatcher(RequestMatcher... requestMatchers) {\n\t\tthis(Arrays.asList(requestMatchers));\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\tfor (RequestMatcher matcher : this.requestMatchers) {\n\t\t\tif (matcher.matches(request)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns a {@link MatchResult} for this {@link HttpServletRequest}. In the case of a\n\t * match, request variables are any request variables from the first underlying\n\t * matcher.\n\t * @param request the HTTP request\n\t * @return a {@link MatchResult} based on the given HTTP request\n\t * @since 6.1\n\t */\n\t@Override\n\tpublic MatchResult matcher(HttpServletRequest request) {\n\t\tfor (RequestMatcher matcher : this.requestMatchers) {\n\t\t\tMatchResult result = matcher.matcher(request);\n\t\t\tif (result.isMatch()) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn MatchResult.notMatch();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tOrRequestMatcher that = (OrRequestMatcher) o;\n\t\treturn Objects.equals(this.requestMatchers, that.requestMatchers);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.requestMatchers);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Or \" + this.requestMatchers;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/ParameterRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Map;\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\n/**\n * A {@link RequestMatcher} for matching on a request parameter and its value.\n *\n * <p>\n * The value may also be specified as a placeholder in order to match on any value,\n * returning the value as part of the {@link MatchResult}.\n *\n * @author Josh Cummings\n * @since 6.4\n */\npublic final class ParameterRequestMatcher implements RequestMatcher {\n\n\tprivate static final MatchesValueMatcher NON_NULL = Objects::nonNull;\n\n\tprivate final String name;\n\n\tprivate final ValueMatcher matcher;\n\n\tpublic ParameterRequestMatcher(String name) {\n\t\tthis.name = name;\n\t\tthis.matcher = NON_NULL;\n\t}\n\n\tpublic ParameterRequestMatcher(String name, String value) {\n\t\tthis.name = name;\n\t\tMatchesValueMatcher matcher = value::equals;\n\t\tif (value.startsWith(\"{\") && value.endsWith(\"}\")) {\n\t\t\tString key = value.substring(1, value.length() - 1);\n\t\t\tthis.matcher = (v) -> (v != null) ? MatchResult.match(Map.of(key, v)) : MatchResult.notMatch();\n\t\t}\n\t\telse {\n\t\t\tthis.matcher = matcher;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\treturn matcher(request).isMatch();\n\t}\n\n\t@Override\n\tpublic MatchResult matcher(HttpServletRequest request) {\n\t\tString parameterValue = request.getParameter(this.name);\n\t\treturn this.matcher.matcher(parameterValue);\n\t}\n\n\tprivate interface ValueMatcher {\n\n\t\tMatchResult matcher(String value);\n\n\t}\n\n\tprivate interface MatchesValueMatcher extends ValueMatcher {\n\n\t\tdefault MatchResult matcher(String value) {\n\t\t\tif (matches(value)) {\n\t\t\t\treturn MatchResult.match();\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn MatchResult.notMatch();\n\t\t\t}\n\t\t}\n\n\t\tboolean matches(String value);\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/RegexRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.regex.Pattern;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.core.log.LogMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\n/**\n * Uses a regular expression to decide whether a supplied the URL of a supplied\n * {@code HttpServletRequest}.\n *\n * Can also be configured to match a specific HTTP method.\n *\n * The match is performed against the {@code servletPath + pathInfo + queryString} of the\n * request and is case-sensitive by default. Case-insensitive matching can be used by\n * using the constructor which takes the {@code caseInsensitive} argument.\n *\n * @author Luke Taylor\n * @author Rob Winch\n * @since 3.1\n */\npublic final class RegexRequestMatcher implements RequestMatcher {\n\n\tprivate static final int DEFAULT = Pattern.DOTALL;\n\n\tprivate static final int CASE_INSENSITIVE = DEFAULT | Pattern.CASE_INSENSITIVE;\n\n\tprivate static final Log logger = LogFactory.getLog(RegexRequestMatcher.class);\n\n\tprivate final Pattern pattern;\n\n\tprivate final @Nullable HttpMethod httpMethod;\n\n\t/**\n\t * Creates a case-sensitive {@code Pattern} instance to match against the request.\n\t * @param pattern the regular expression to compile into a pattern.\n\t * @since 5.8\n\t */\n\tpublic static RegexRequestMatcher regexMatcher(String pattern) {\n\t\tAssert.hasText(pattern, \"pattern cannot be empty\");\n\t\treturn new RegexRequestMatcher(pattern, null);\n\t}\n\n\t/**\n\t * Creates an instance that matches to all requests with the same {@link HttpMethod}.\n\t * @param method the HTTP method to match. Must not be null.\n\t * @since 5.8\n\t */\n\tpublic static RegexRequestMatcher regexMatcher(HttpMethod method) {\n\t\tAssert.notNull(method, \"method cannot be null\");\n\t\treturn new RegexRequestMatcher(\".*\", method.name());\n\t}\n\n\t/**\n\t * Creates a case-sensitive {@code Pattern} instance to match against the request.\n\t * @param method the HTTP method to match. May be null to match all methods.\n\t * @param pattern the regular expression to compile into a pattern.\n\t * @since 5.8\n\t */\n\tpublic static RegexRequestMatcher regexMatcher(HttpMethod method, String pattern) {\n\t\tAssert.notNull(method, \"method cannot be null\");\n\t\tAssert.hasText(pattern, \"pattern cannot be empty\");\n\t\treturn new RegexRequestMatcher(pattern, method.name());\n\t}\n\n\t/**\n\t * Creates a case-sensitive {@code Pattern} instance to match against the request.\n\t * @param pattern the regular expression to compile into a pattern.\n\t * @param httpMethod the HTTP method to match. May be null to match all methods.\n\t */\n\tpublic RegexRequestMatcher(String pattern, @Nullable String httpMethod) {\n\t\tthis(pattern, httpMethod, false);\n\t}\n\n\t/**\n\t * As above, but allows setting of whether case-insensitive matching should be used.\n\t * @param pattern the regular expression to compile into a pattern.\n\t * @param httpMethod the HTTP method to match. May be null to match all methods.\n\t * @param caseInsensitive if true, the pattern will be compiled with the\n\t * {@link Pattern#CASE_INSENSITIVE} flag set.\n\t */\n\tpublic RegexRequestMatcher(String pattern, @Nullable String httpMethod, boolean caseInsensitive) {\n\t\tthis.pattern = Pattern.compile(pattern, caseInsensitive ? CASE_INSENSITIVE : DEFAULT);\n\t\tthis.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod) : null;\n\t}\n\n\t/**\n\t * Performs the match of the request URL ({@code servletPath + pathInfo + queryString}\n\t * ) against the compiled pattern. If the query string is present, a question mark\n\t * will be prepended.\n\t * @param request the request to match\n\t * @return true if the pattern matches the URL, false otherwise.\n\t */\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\tif (this.httpMethod != null && request.getMethod() != null\n\t\t\t\t&& this.httpMethod != HttpMethod.valueOf(request.getMethod())) {\n\t\t\treturn false;\n\t\t}\n\t\tString url = request.getServletPath();\n\t\tString pathInfo = request.getPathInfo();\n\t\tString query = request.getQueryString();\n\t\tif (pathInfo != null || query != null) {\n\t\t\tStringBuilder sb = new StringBuilder(url);\n\t\t\tif (pathInfo != null) {\n\t\t\t\tsb.append(pathInfo);\n\t\t\t}\n\t\t\tif (query != null) {\n\t\t\t\tsb.append('?').append(query);\n\t\t\t}\n\t\t\turl = sb.toString();\n\t\t}\n\t\tlogger.debug(LogMessage.format(\"Checking match of request : '%s'; against '%s'\", url, this.pattern));\n\t\treturn this.pattern.matcher(url).matches();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"Regex [pattern='\").append(this.pattern).append(\"'\");\n\t\tif (this.httpMethod != null) {\n\t\t\tsb.append(\", \").append(this.httpMethod);\n\t\t}\n\t\tsb.append(\"]\");\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/RequestHeaderRequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Objects;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * A {@link RequestMatcher} that can be used to match request that contain a header with\n * an expected header name and an expected value.\n *\n * <p>\n * For example, the following will match an request that contains a header with the name\n * X-Requested-With no matter what the value is.\n * </p>\n *\n * <pre>\n * RequestMatcher matcher = new RequestHeaderRequestMatcher(&quot;X-Requested-With&quot;);\n * </pre>\n *\n * Alternatively, the RequestHeaderRequestMatcher can be more precise and require a\n * specific value. For example the following will match on requests with the header name\n * of X-Requested-With with the value of \"XMLHttpRequest\", but will not match on header\n * name of \"X-Requested-With\" with the value of \"Other\".\n *\n * <pre>\n * RequestMatcher matcher = new RequestHeaderRequestMatcher(&quot;X-Requested-With&quot;,\n * \t\t&quot;XMLHttpRequest&quot;);\n * </pre>\n *\n * The value used to compare is the first header value, so in the previous example if the\n * header \"X-Requested-With\" contains the values \"Other\" and \"XMLHttpRequest\", then it\n * will not match.\n *\n * @author Rob Winch\n * @since 3.2\n */\npublic final class RequestHeaderRequestMatcher implements RequestMatcher {\n\n\tprivate final String expectedHeaderName;\n\n\tprivate final @Nullable String expectedHeaderValue;\n\n\t/**\n\t * Creates a new instance that will match if a header by the name of\n\t * {@link #expectedHeaderName} is present. In this instance, the value does not\n\t * matter.\n\t * @param expectedHeaderName the name of the expected header that if present the\n\t * request will match. Cannot be null.\n\t */\n\tpublic RequestHeaderRequestMatcher(String expectedHeaderName) {\n\t\tthis(expectedHeaderName, null);\n\t}\n\n\t/**\n\t * Creates a new instance that will match if a header by the name of\n\t * {@link #expectedHeaderName} is present and if the {@link #expectedHeaderValue} is\n\t * non-null the first value is the same.\n\t * @param expectedHeaderName the name of the expected header. Cannot be null\n\t * @param expectedHeaderValue the expected header value or null if the value does not\n\t * matter\n\t */\n\tpublic RequestHeaderRequestMatcher(String expectedHeaderName, @Nullable String expectedHeaderValue) {\n\t\tAssert.notNull(expectedHeaderName, \"headerName cannot be null\");\n\t\tthis.expectedHeaderName = expectedHeaderName;\n\t\tthis.expectedHeaderValue = expectedHeaderValue;\n\t}\n\n\t@Override\n\tpublic boolean matches(HttpServletRequest request) {\n\t\tString actualHeaderValue = request.getHeader(this.expectedHeaderName);\n\t\tif (this.expectedHeaderValue == null) {\n\t\t\treturn actualHeaderValue != null;\n\t\t}\n\t\treturn this.expectedHeaderValue.equals(actualHeaderValue);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!(obj instanceof RequestHeaderRequestMatcher that)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Objects.equals(this.expectedHeaderName, that.expectedHeaderName)\n\t\t\t\t&& Objects.equals(this.expectedHeaderValue, that.expectedHeaderValue);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.expectedHeaderName, this.expectedHeaderValue);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RequestHeaderRequestMatcher [expectedHeaderName=\" + this.expectedHeaderName + \", expectedHeaderValue=\"\n\t\t\t\t+ this.expectedHeaderValue + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/RequestMatcher.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\n/**\n * Simple strategy to match an <tt>HttpServletRequest</tt>.\n *\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @since 3.0.2\n */\n@FunctionalInterface\npublic interface RequestMatcher {\n\n\t/**\n\t * Decides whether the rule implemented by the strategy matches the supplied request.\n\t * @param request the request to check for a match\n\t * @return true if the request matches, false otherwise\n\t */\n\tboolean matches(HttpServletRequest request);\n\n\t/**\n\t * Returns a MatchResult for this RequestMatcher. The default implementation returns\n\t * {@link Collections#emptyMap()} when {@link MatchResult#getVariables()} is invoked.\n\t * @return the MatchResult from comparing this RequestMatcher against the\n\t * HttpServletRequest\n\t * @since 5.2\n\t */\n\tdefault MatchResult matcher(HttpServletRequest request) {\n\t\tboolean match = matches(request);\n\t\treturn new MatchResult(match, Collections.emptyMap());\n\t}\n\n\t/**\n\t * The result of matching against an HttpServletRequest contains the status, true or\n\t * false, of the match and if present, any variables extracted from the match\n\t *\n\t * @since 5.2\n\t */\n\tclass MatchResult {\n\n\t\tprivate final boolean match;\n\n\t\tprivate final Map<String, String> variables;\n\n\t\tMatchResult(boolean match, Map<String, String> variables) {\n\t\t\tthis.match = match;\n\t\t\tthis.variables = variables;\n\t\t}\n\n\t\t/**\n\t\t * @return true if the comparison against the HttpServletRequest produced a\n\t\t * successful match\n\t\t */\n\t\tpublic boolean isMatch() {\n\t\t\treturn this.match;\n\t\t}\n\n\t\t/**\n\t\t * Returns the extracted variable values where the key is the variable name and\n\t\t * the value is the variable value\n\t\t * @return a map containing key-value pairs representing extracted variable names\n\t\t * and variable values\n\t\t */\n\t\tpublic Map<String, String> getVariables() {\n\t\t\treturn this.variables;\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is a match with no variables\n\t\t * @return {@link MatchResult} that is a match with no variables\n\t\t */\n\t\tpublic static MatchResult match() {\n\t\t\treturn new MatchResult(true, Collections.emptyMap());\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is a match with the specified\n\t\t * variables\n\t\t * @param variables the specified variables\n\t\t * @return {@link MatchResult} that is a match with the specified variables\n\t\t */\n\t\tpublic static MatchResult match(Map<String, String> variables) {\n\t\t\treturn new MatchResult(true, variables);\n\t\t}\n\n\t\t/**\n\t\t * Creates an instance of {@link MatchResult} that is not a match.\n\t\t * @return {@link MatchResult} that is not a match\n\t\t */\n\t\tpublic static MatchResult notMatch() {\n\t\t\treturn new MatchResult(false, Collections.emptyMap());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/RequestMatcherEditor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.beans.PropertyEditorSupport;\n\nimport org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;\n\n/**\n * PropertyEditor which creates ELRequestMatcher instances from Strings\n *\n * This allows to use a String in a BeanDefinition instead of an (inner) bean if a\n * RequestMatcher is required, e.g. in {@link DelegatingAuthenticationEntryPoint}\n *\n * @author Mike Wiesner\n * @since 3.0.2\n */\npublic class RequestMatcherEditor extends PropertyEditorSupport {\n\n\t@Override\n\tpublic void setAsText(String text) throws IllegalArgumentException {\n\t\tsetValue(new ELRequestMatcher(text));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/RequestMatcherEntry.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\n/**\n * A rich object for associating a {@link RequestMatcher} to another object.\n *\n * @author Marcus Da Coregio\n * @since 5.5.5\n */\npublic class RequestMatcherEntry<T> {\n\n\tprivate final RequestMatcher requestMatcher;\n\n\tprivate final T entry;\n\n\tpublic RequestMatcherEntry(RequestMatcher requestMatcher, T entry) {\n\t\tthis.requestMatcher = requestMatcher;\n\t\tthis.entry = entry;\n\t}\n\n\tpublic RequestMatcher getRequestMatcher() {\n\t\treturn this.requestMatcher;\n\t}\n\n\tpublic T getEntry() {\n\t\treturn this.entry;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/RequestMatchers.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.List;\n\n/**\n * A factory class to create {@link RequestMatcher} instances.\n *\n * @author Christian Schuster\n * @since 6.1\n */\npublic final class RequestMatchers {\n\n\t/**\n\t * Creates a {@link RequestMatcher} that matches if at least one of the given\n\t * {@link RequestMatcher}s matches, if <code>matchers</code> are empty then the\n\t * returned matcher never matches.\n\t * @param matchers the {@link RequestMatcher}s to use\n\t * @return the any-of composed {@link RequestMatcher}\n\t * @see OrRequestMatcher\n\t */\n\tpublic static RequestMatcher anyOf(RequestMatcher... matchers) {\n\t\treturn (matchers.length > 0) ? new OrRequestMatcher(List.of(matchers)) : (request) -> false;\n\t}\n\n\t/**\n\t * Creates a {@link RequestMatcher} that matches if all the given\n\t * {@link RequestMatcher}s match, if <code>matchers</code> are empty then the returned\n\t * matcher always matches.\n\t * @param matchers the {@link RequestMatcher}s to use\n\t * @return the all-of composed {@link RequestMatcher}\n\t * @see AndRequestMatcher\n\t */\n\tpublic static RequestMatcher allOf(RequestMatcher... matchers) {\n\t\treturn (matchers.length > 0) ? new AndRequestMatcher(List.of(matchers)) : (request) -> true;\n\t}\n\n\t/**\n\t * Creates a {@link RequestMatcher} that matches if the given {@link RequestMatcher}\n\t * does not match.\n\t * @param matcher the {@link RequestMatcher} to use\n\t * @return the inverted {@link RequestMatcher}\n\t */\n\tpublic static RequestMatcher not(RequestMatcher matcher) {\n\t\treturn (request) -> !matcher.matches(request);\n\t}\n\n\tprivate RequestMatchers() {\n\t}\n\n}\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/matcher/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Servlet APIs for matching requests which are used for, among other things, mapping\n * authorization rules.\n */\n@NullMarked\npackage org.springframework.security.web.util.matcher;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/java/org/springframework/security/web/util/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Web utility classes.\n * <p>\n * Should not depend on any other framework classes.\n */\n@NullMarked\npackage org.springframework.security.web.util;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "web/src/main/resources/META-INF/services/io.micrometer.context.ThreadLocalAccessor",
    "content": "org.springframework.security.web.server.ServerWebExchangeThreadLocalAccessor\n"
  },
  {
    "path": "web/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.web.aot.hint.WebMvcSecurityRuntimeHints\n"
  },
  {
    "path": "web/src/main/resources/org/springframework/security/default-ui.css",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* General layout */\nbody {\n    font-family: system-ui, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n    background-color: #eee;\n    padding: 40px 0;\n    margin: 0;\n    line-height: 1.5;\n}\n\nh2 {\n    margin-top: 0;\n    margin-bottom: 0.5rem;\n    font-size: 2rem;\n    font-weight: 500;\n    line-height: 2rem;\n}\n\n.content {\n    margin-right: auto;\n    margin-left: auto;\n    padding-right: 15px;\n    padding-left: 15px;\n    width: 100%;\n    box-sizing: border-box;\n}\n\n@media (min-width: 800px) {\n    .content {\n        max-width: 760px;\n    }\n}\n\n.v-middle {\n    vertical-align: middle;\n}\n\n.center {\n    text-align: center;\n}\n\n.no-margin {\n    margin: 0;\n}\n\n/* Components */\na,\na:visited {\n    text-decoration: none;\n    color: #06f;\n}\n\na:hover {\n    text-decoration: underline;\n    color: #003c97;\n}\n\ninput[type=\"text\"],\ninput[type=\"password\"] {\n    height: auto;\n    width: 100%;\n    font-size: 1rem;\n    padding: 0.5rem;\n    box-sizing: border-box;\n}\n\nbutton {\n    padding: 0.5rem 1rem;\n    font-size: 1.25rem;\n    line-height: 1.5;\n    border: none;\n    border-radius: 0.1rem;\n    width: 100%;\n    cursor: pointer;\n}\n\nbutton.primary {\n    color: #fff;\n    background-color: #06f;\n}\n\nbutton.small {\n    padding: .25rem .5rem;\n    font-size: .875rem;\n    line-height: 1.5;\n}\n\n.alert {\n    padding: 0.75rem 1rem;\n    margin-bottom: 1rem;\n    line-height: 1.5;\n    border-radius: 0.1rem;\n    width: 100%;\n    box-sizing: border-box;\n    border-width: 1px;\n    border-style: solid;\n}\n\n.alert.alert-danger {\n    color: #6b1922;\n    background-color: #f7d5d7;\n    border-color: #eab6bb;\n}\n\n.alert.alert-success {\n    color: #145222;\n    background-color: #d1f0d9;\n    border-color: #c2ebcb;\n}\n\n.screenreader {\n    position: absolute;\n    clip: rect(0 0 0 0);\n    height: 1px;\n    width: 1px;\n    padding: 0;\n    border: 0;\n    overflow: hidden;\n}\n\ntable {\n    width: 100%;\n    max-width: 100%;\n    margin-bottom: 2rem;\n    border-collapse: collapse;\n}\n\n.table-striped th {\n    padding: .75rem;\n}\n\n.table-striped tr:nth-of-type(2n + 1) {\n    background-color: #e1e1e1;\n}\n\n.table-striped > thead > tr:first-child {\n    background-color: inherit;\n}\n\ntd {\n    padding: 0.75rem;\n    vertical-align: top;\n}\n\ntr.v-middle > td {\n    vertical-align: middle;\n}\n\n/* Login / logout layouts */\n.login-form,\n.logout-form,\n.default-form {\n    max-width: 340px;\n    padding: 0 15px 15px 15px;\n    margin: 0 auto 2rem auto;\n    box-sizing: border-box;\n}\n"
  },
  {
    "path": "web/src/main/resources/org/springframework/security/user-credentials-schema-postgres.sql",
    "content": "create table user_credentials\n(\n    credential_id                varchar(1000) not null,\n    user_entity_user_id          varchar(1000) not null,\n    public_key                   bytea         not null,\n    signature_count              bigint,\n    uv_initialized               boolean,\n    backup_eligible              boolean       not null,\n    authenticator_transports     varchar(1000),\n    public_key_credential_type   varchar(100),\n    backup_state                 boolean       not null,\n    attestation_object           bytea,\n    attestation_client_data_json bytea,\n    created                      timestamp,\n    last_used                    timestamp,\n    label                        varchar(1000) not null,\n    primary key (credential_id)\n);\n"
  },
  {
    "path": "web/src/main/resources/org/springframework/security/user-credentials-schema.sql",
    "content": "create table user_credentials\n(\n    credential_id                varchar(1000) not null,\n    user_entity_user_id          varchar(1000) not null,\n    public_key                   blob          not null,\n    signature_count              bigint,\n    uv_initialized               boolean,\n    backup_eligible              boolean       not null,\n    authenticator_transports     varchar(1000),\n    public_key_credential_type   varchar(100),\n    backup_state                 boolean       not null,\n    attestation_object           blob,\n    attestation_client_data_json blob,\n    created                      timestamp,\n    last_used                    timestamp,\n    label                        varchar(1000) not null,\n    primary key (credential_id)\n);\n"
  },
  {
    "path": "web/src/main/resources/org/springframework/security/user-entities-schema.sql",
    "content": "create table user_entities\n(\n    id           varchar(1000) not null,\n    name         varchar(100)  not null,\n    display_name varchar(200),\n    primary key (id)\n);\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/MockFilterConfig.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.FilterConfig;\nimport jakarta.servlet.ServletContext;\n\n/**\n * @author Ben Alex\n */\n@SuppressWarnings(\"unchecked\")\npublic class MockFilterConfig implements FilterConfig {\n\n\tprivate Map map = new HashMap();\n\n\t@Override\n\tpublic String getFilterName() {\n\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t}\n\n\t@Override\n\tpublic String getInitParameter(String arg0) {\n\t\tObject result = this.map.get(arg0);\n\t\tif (result != null) {\n\t\t\treturn (String) result;\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Enumeration getInitParameterNames() {\n\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t}\n\n\t@Override\n\tpublic ServletContext getServletContext() {\n\t\tthrow new UnsupportedOperationException(\"mock method not implemented\");\n\t}\n\n\tpublic void setInitParmeter(String parameter, String value) {\n\t\tthis.map.put(parameter, value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/MockSecurityContextHolderStrategy.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security;\n\nimport java.util.function.Supplier;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\npublic class MockSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n\tprivate Supplier<SecurityContext> mock;\n\n\tpublic MockSecurityContextHolderStrategy() {\n\n\t}\n\n\tpublic MockSecurityContextHolderStrategy(Authentication authentication) {\n\t\tthis(() -> new SecurityContextImpl(authentication));\n\t}\n\n\tpublic MockSecurityContextHolderStrategy(Supplier<SecurityContext> mock) {\n\t\tthis.mock = mock;\n\t}\n\n\t@Override\n\tpublic void clearContext() {\n\t\tthis.mock = () -> null;\n\t}\n\n\t@Override\n\tpublic SecurityContext getContext() {\n\t\treturn this.mock.get();\n\t}\n\n\t@Override\n\tpublic void setContext(SecurityContext context) {\n\t\tthis.mock = () -> context;\n\t}\n\n\t@Override\n\tpublic SecurityContext createEmptyContext() {\n\t\treturn new SecurityContextImpl();\n\t}\n\n\t@Override\n\tpublic Supplier<SecurityContext> getDeferredContext() {\n\t\treturn this.mock;\n\t}\n\n\t@Override\n\tpublic void setDeferredContext(Supplier<SecurityContext> deferredContext) {\n\t\tthis.mock = deferredContext;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/test/web/CodecTestUtils.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\n\nimport org.springframework.security.crypto.codec.Hex;\nimport org.springframework.util.DigestUtils;\n\npublic final class CodecTestUtils {\n\n\tprivate CodecTestUtils() {\n\t}\n\n\tpublic static String encodeBase64(String unencoded) {\n\t\treturn Base64.getEncoder().encodeToString(unencoded.getBytes());\n\t}\n\n\tpublic static String encodeBase64(byte[] unencoded) {\n\t\treturn Base64.getEncoder().encodeToString(unencoded);\n\t}\n\n\tpublic static String decodeBase64(String encoded) {\n\t\treturn new String(Base64.getDecoder().decode(encoded));\n\t}\n\n\tpublic static boolean isBase64(byte[] arrayOctet) {\n\t\ttry {\n\t\t\tBase64.getMimeDecoder().decode(arrayOctet);\n\t\t\treturn true;\n\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static String md5Hex(String data) {\n\t\treturn DigestUtils.md5DigestAsHex(data.getBytes());\n\t}\n\n\tpublic static String algorithmHex(String algorithmName, String data) {\n\t\ttry {\n\t\t\tMessageDigest digest = MessageDigest.getInstance(algorithmName);\n\t\t\treturn new String(Hex.encode(digest.digest(data.getBytes())));\n\t\t}\n\t\tcatch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new IllegalStateException(\"No \" + algorithmName + \" algorithm available!\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/test/web/reactive/server/WebTestClientBuilder.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\nimport org.springframework.security.web.server.WebFilterChainProxy;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.test.web.reactive.server.WebTestClient.Builder;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.server.WebFilter;\n\n/**\n * Provides a convenient mechanism for running {@link WebTestClient} against\n * {@link WebFilter}\n *\n * @author Rob Winch\n * @since 5.0\n *\n */\npublic final class WebTestClientBuilder {\n\n\tprivate WebTestClientBuilder() {\n\t}\n\n\tpublic static Builder bindToWebFilters(WebFilter... webFilters) {\n\t\treturn WebTestClient.bindToController(new Http200RestController()).webFilter(webFilters).configureClient();\n\t}\n\n\tpublic static Builder bindToWebFilters(SecurityWebFilterChain securityWebFilterChain) {\n\t\treturn bindToWebFilters(new WebFilterChainProxy(securityWebFilterChain));\n\t}\n\n\tpublic static Builder bindToControllerAndWebFilters(Class<?> controller, WebFilter... webFilters) {\n\t\treturn WebTestClient.bindToController(controller).webFilter(webFilters).configureClient();\n\t}\n\n\tpublic static Builder bindToControllerAndWebFilters(Class<?> controller,\n\t\t\tSecurityWebFilterChain securityWebFilterChain) {\n\t\treturn bindToControllerAndWebFilters(controller, new WebFilterChainProxy(securityWebFilterChain));\n\t}\n\n\t@RestController\n\tpublic static class Http200RestController {\n\n\t\t@RequestMapping(\"/**\")\n\t\t@ResponseStatus(HttpStatus.OK)\n\t\tpublic String ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/test/web/reactive/server/WebTestHandler.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.test.web.reactive.server;\n\nimport java.util.Arrays;\n\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest.BaseBuilder;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebHandler;\nimport org.springframework.web.server.handler.FilteringWebHandler;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic final class WebTestHandler {\n\n\tprivate final MockWebHandler webHandler = new MockWebHandler();\n\n\tprivate final WebHandler handler;\n\n\tprivate WebTestHandler(WebFilter... filters) {\n\t\tthis.handler = new FilteringWebHandler(this.webHandler, Arrays.asList(filters));\n\t}\n\n\tpublic WebHandlerResult exchange(BaseBuilder<?> baseBuilder) {\n\t\tServerWebExchange exchange = MockServerWebExchange.from(baseBuilder.build());\n\t\treturn exchange(exchange);\n\t}\n\n\tpublic WebHandlerResult exchange(ServerWebExchange exchange) {\n\t\tthis.handler.handle(exchange).block();\n\t\treturn new WebHandlerResult(this.webHandler.exchange);\n\t}\n\n\tpublic static WebTestHandler bindToWebFilters(WebFilter... filters) {\n\t\treturn new WebTestHandler(filters);\n\t}\n\n\tpublic static final class WebHandlerResult {\n\n\t\tprivate final ServerWebExchange exchange;\n\n\t\tprivate WebHandlerResult(ServerWebExchange exchange) {\n\t\t\tthis.exchange = exchange;\n\t\t}\n\n\t\tpublic ServerWebExchange getExchange() {\n\t\t\treturn this.exchange;\n\t\t}\n\n\t}\n\n\tstatic class MockWebHandler implements WebHandler {\n\n\t\tprivate ServerWebExchange exchange;\n\n\t\t@Override\n\t\tpublic Mono<Void> handle(ServerWebExchange exchange) {\n\t\t\tthis.exchange = exchange;\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/DefaultRedirectStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Luke Taylor\n * @author Mark Chesney\n * @since 3.0\n */\npublic class DefaultRedirectStrategyTests {\n\n\t@Test\n\tpublic void contextRelativeUrlWithContextNameInHostnameIsHandledCorrectly() throws Exception {\n\t\tDefaultRedirectStrategy rds = new DefaultRedirectStrategy();\n\t\trds.setContextRelative(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"/context\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trds.sendRedirect(request, response, \"https://context.blah.com/context/remainder\");\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"remainder\");\n\t}\n\n\t// SEC-2177\n\t@Test\n\tpublic void contextRelativeUrlWithMultipleSchemesInHostnameIsHandledCorrectly() throws Exception {\n\t\tDefaultRedirectStrategy rds = new DefaultRedirectStrategy();\n\t\trds.setContextRelative(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"/context\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trds.sendRedirect(request, response, \"https://https://context.blah.com/context/remainder\");\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"remainder\");\n\t}\n\n\t@Test\n\tpublic void contextRelativeShouldThrowExceptionIfURLDoesNotContainContextPath() throws Exception {\n\t\tDefaultRedirectStrategy rds = new DefaultRedirectStrategy();\n\t\trds.setContextRelative(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"/context\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> rds.sendRedirect(request, response, \"https://redirectme.somewhere.else\"));\n\t}\n\n\t@Test\n\tpublic void statusCodeIsHandledCorrectly() throws Exception {\n\t\t// given\n\t\tDefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\t\tredirectStrategy.setStatusCode(HttpStatus.TEMPORARY_REDIRECT);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\n\t\t// when\n\t\tredirectStrategy.sendRedirect(request, response, \"/requested\");\n\n\t\t// then\n\t\tassertThat(response.isCommitted()).isTrue();\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/requested\");\n\t\tassertThat(response.getStatus()).isEqualTo(307);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/FilterChainProxyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.firewall.FirewalledRequest;\nimport org.springframework.security.web.firewall.HttpFirewall;\nimport org.springframework.security.web.firewall.RequestRejectedException;\nimport org.springframework.security.web.firewall.RequestRejectedHandler;\nimport org.springframework.security.web.servlet.TestMockHttpServletMappings;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n */\npublic class FilterChainProxyTests {\n\n\tprivate FilterChainProxy fcp;\n\n\tprivate RequestMatcher matcher;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate FilterChain chain;\n\n\tprivate Filter filter;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.matcher = mock(RequestMatcher.class);\n\t\tthis.filter = mock(Filter.class);\n\t\twillAnswer((Answer<Object>) (inv) -> {\n\t\t\tObject[] args = inv.getArguments();\n\t\t\tFilterChain fc = (FilterChain) args[2];\n\t\t\tHttpServletRequestWrapper extraWrapper = new HttpServletRequestWrapper((HttpServletRequest) args[0]);\n\t\t\tfc.doFilter(extraWrapper, (HttpServletResponse) args[1]);\n\t\t\treturn null;\n\t\t}).given(this.filter).doFilter(any(), any(), any());\n\t\tthis.fcp = new FilterChainProxy(new DefaultSecurityFilterChain(this.matcher, Arrays.asList(this.filter)));\n\t\tthis.fcp.setFilterChainValidator(mock(FilterChainProxy.FilterChainValidator.class));\n\t\tthis.request = get(\"/path\").build();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.chain = mock(FilterChain.class);\n\t}\n\n\t@AfterEach\n\tpublic void teardown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void toStringCallSucceeds() {\n\t\tthis.fcp.afterPropertiesSet();\n\t\tthis.fcp.toString();\n\t}\n\n\t@Test\n\tpublic void securityFilterChainIsNotInvokedIfMatchFails() throws Exception {\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(false);\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(this.fcp.getFilterChains()).hasSize(1);\n\t\tassertThat(this.fcp.getFilterChains().get(0).getFilters().get(0)).isSameAs(this.filter);\n\t\tverifyNoMoreInteractions(this.filter);\n\t\t// The actual filter chain should be invoked though\n\t\tverify(this.chain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void originalChainIsInvokedAfterSecurityChainIfMatchSucceeds() throws Exception {\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(true);\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.filter).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(FilterChain.class));\n\t\tverify(this.chain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void originalFilterChainIsInvokedIfMatchingSecurityChainIsEmpty() throws Exception {\n\t\tList<Filter> noFilters = Collections.emptyList();\n\t\tthis.fcp = new FilterChainProxy(new DefaultSecurityFilterChain(this.matcher, noFilters));\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(true);\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.chain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void requestIsWrappedForMatchingAndFilteringWhenMatchIsFound() throws Exception {\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.matcher).matches(any(FirewalledRequest.class));\n\t\tverify(this.filter).doFilter(any(FirewalledRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(FilterChain.class));\n\t\tverify(this.chain).doFilter(any(), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void requestIsWrappedForMatchingAndFilteringWhenMatchIsNotFound() throws Exception {\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(false);\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.matcher).matches(any(FirewalledRequest.class));\n\t\tverifyNoMoreInteractions(this.filter);\n\t\tverify(this.chain).doFilter(any(FirewalledRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void wrapperIsResetWhenNoMatchingFilters() throws Exception {\n\t\tHttpFirewall fw = mock(HttpFirewall.class);\n\t\tFirewalledRequest fwr = mock(FirewalledRequest.class);\n\t\tgiven(fwr.getRequestURI()).willReturn(\"/\");\n\t\tgiven(fwr.getContextPath()).willReturn(\"\");\n\t\tgiven(fwr.getHttpServletMapping()).willReturn(TestMockHttpServletMappings.defaultMapping());\n\t\tthis.fcp.setFirewall(fw);\n\t\tgiven(fw.getFirewalledRequest(this.request)).willReturn(fwr);\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(false);\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(fwr).reset();\n\t}\n\n\t// SEC-1639\n\t@Test\n\tpublic void bothWrappersAreResetWithNestedFcps() throws Exception {\n\t\tHttpFirewall fw = mock(HttpFirewall.class);\n\t\tFilterChainProxy firstFcp = new FilterChainProxy(new DefaultSecurityFilterChain(this.matcher, this.fcp));\n\t\tfirstFcp.setFirewall(fw);\n\t\tthis.fcp.setFirewall(fw);\n\t\tFirewalledRequest firstFwr = mock(FirewalledRequest.class, \"firstFwr\");\n\t\tgiven(firstFwr.getRequestURI()).willReturn(\"/\");\n\t\tgiven(firstFwr.getContextPath()).willReturn(\"\");\n\t\tgiven(firstFwr.getHttpServletMapping()).willReturn(TestMockHttpServletMappings.defaultMapping());\n\t\tFirewalledRequest fwr = mock(FirewalledRequest.class, \"fwr\");\n\t\tgiven(fwr.getRequestURI()).willReturn(\"/\");\n\t\tgiven(fwr.getContextPath()).willReturn(\"\");\n\t\tgiven(fwr.getHttpServletMapping()).willReturn(TestMockHttpServletMappings.defaultMapping());\n\t\tgiven(fw.getFirewalledRequest(this.request)).willReturn(firstFwr);\n\t\tgiven(fw.getFirewalledRequest(firstFwr)).willReturn(fwr);\n\t\tgiven(fwr.getRequest()).willReturn(firstFwr);\n\t\tgiven(firstFwr.getRequest()).willReturn(this.request);\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tfirstFcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(firstFwr).reset();\n\t\tverify(fwr).reset();\n\t}\n\n\t@Test\n\tpublic void doFilterClearsSecurityContextHolder() throws Exception {\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(true);\n\t\twillAnswer((Answer<Object>) (inv) -> {\n\t\t\tSecurityContextHolder.getContext()\n\t\t\t\t.setAuthentication(new TestingAuthenticationToken(\"username\", \"password\"));\n\t\t\treturn null;\n\t\t}).given(this.filter)\n\t\t\t.doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class), any(FilterChain.class));\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomSecurityContextHolderStrategyClearsSecurityContext() throws Exception {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tthis.fcp.setSecurityContextHolderStrategy(strategy);\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(true);\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(strategy).clearContext();\n\t}\n\n\t@Test\n\tpublic void doFilterClearsSecurityContextHolderWithException() throws Exception {\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(true);\n\t\twillAnswer((Answer<Object>) (inv) -> {\n\t\t\tSecurityContextHolder.getContext()\n\t\t\t\t.setAuthentication(new TestingAuthenticationToken(\"username\", \"password\"));\n\t\t\tthrow new ServletException(\"oops\");\n\t\t}).given(this.filter)\n\t\t\t.doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class), any(FilterChain.class));\n\t\tassertThatExceptionOfType(ServletException.class)\n\t\t\t.isThrownBy(() -> this.fcp.doFilter(this.request, this.response, this.chain));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t// SEC-2027\n\t@Test\n\tpublic void doFilterClearsSecurityContextHolderOnceOnForwards() throws Exception {\n\t\tfinal FilterChain innerChain = mock(FilterChain.class);\n\t\tgiven(this.matcher.matches(any(HttpServletRequest.class))).willReturn(true);\n\t\twillAnswer((Answer<Object>) (inv) -> {\n\t\t\tTestingAuthenticationToken expected = new TestingAuthenticationToken(\"username\", \"password\");\n\t\t\tSecurityContextHolder.getContext().setAuthentication(expected);\n\t\t\twillAnswer((Answer<Object>) (inv1) -> {\n\t\t\t\tinnerChain.doFilter(this.request, this.response);\n\t\t\t\treturn null;\n\t\t\t}).given(this.filter)\n\t\t\t\t.doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class), any(FilterChain.class));\n\t\t\tthis.fcp.doFilter(this.request, this.response, innerChain);\n\t\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(expected);\n\t\t\treturn null;\n\t\t}).given(this.filter)\n\t\t\t.doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class), any(FilterChain.class));\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(innerChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void setRequestRejectedHandlerDoesNotAcceptNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.fcp.setRequestRejectedHandler(null));\n\t}\n\n\t@Test\n\tpublic void requestRejectedHandlerIsCalledIfFirewallThrowsRequestRejectedException() throws Exception {\n\t\tHttpFirewall fw = mock(HttpFirewall.class);\n\t\tRequestRejectedHandler rjh = mock(RequestRejectedHandler.class);\n\t\tthis.fcp.setFirewall(fw);\n\t\tthis.fcp.setRequestRejectedHandler(rjh);\n\t\tRequestRejectedException requestRejectedException = new RequestRejectedException(\"Contains illegal chars\");\n\t\tgiven(fw.getFirewalledRequest(this.request)).willThrow(requestRejectedException);\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(rjh).handle(eq(this.request), eq(this.response), eq((requestRejectedException)));\n\t}\n\n\t@Test\n\tpublic void requestRejectedHandlerIsCalledIfFirewallThrowsWrappedRequestRejectedException() throws Exception {\n\t\tHttpFirewall fw = mock(HttpFirewall.class);\n\t\tRequestRejectedHandler rjh = mock(RequestRejectedHandler.class);\n\t\tthis.fcp.setFirewall(fw);\n\t\tthis.fcp.setRequestRejectedHandler(rjh);\n\t\tRequestRejectedException requestRejectedException = new RequestRejectedException(\"Contains illegal chars\");\n\t\tServletException servletException = new ServletException(requestRejectedException);\n\t\tgiven(fw.getFirewalledRequest(this.request)).willReturn(new MockFirewalledRequest(this.request));\n\t\twillThrow(servletException).given(this.chain).doFilter(any(), any());\n\t\tthis.fcp.doFilter(this.request, this.response, this.chain);\n\t\tverify(rjh).handle(eq(this.request), eq(this.response), eq((requestRejectedException)));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMatchesThenObservationRegistryObserves() throws Exception {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tSecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, Arrays.asList(this.filter));\n\t\tFilterChainProxy fcp = new FilterChainProxy(sec);\n\t\tfcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));\n\t\tFilter filter = ObservationFilterChainDecorator.FilterObservation\n\t\t\t.create(Observation.createNotStarted(\"wrap\", registry))\n\t\t\t.wrap(fcp);\n\t\tfilter.doFilter(this.request, this.response, this.chain);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(4)).onStart(captor.capture());\n\t\tverify(handler, times(4)).onStop(any());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertFilterChainObservation(contexts.next(), \"before\", 1);\n\t\tassertThat(contexts.next().getName()).isEqualTo(ObservationFilterChainDecorator.SECURED_OBSERVATION_NAME);\n\t\tassertFilterChainObservation(contexts.next(), \"after\", 1);\n\t}\n\n\t// gh-12610\n\t@Test\n\tvoid parentObservationIsTakenIntoAccountDuringDispatchError() throws Exception {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tSecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, Arrays.asList(this.filter));\n\t\tFilterChainProxy fcp = new FilterChainProxy(sec);\n\t\tfcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));\n\t\tFilter initialFilter = ObservationFilterChainDecorator.FilterObservation\n\t\t\t.create(Observation.createNotStarted(\"wrap\", registry))\n\t\t\t.wrap(fcp);\n\n\t\tServletRequest initialRequest = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tinitialFilter.doFilter(initialRequest, new MockHttpServletResponse(), this.chain);\n\n\t\t// simulate request attribute copying in case dispatching to ERROR\n\t\tObservationFilterChainDecorator.AroundFilterObservation parentObservation = (ObservationFilterChainDecorator.AroundFilterObservation) initialRequest\n\t\t\t.getAttribute(ObservationFilterChainDecorator.ATTRIBUTE);\n\t\tassertThat(parentObservation).isNotNull();\n\n\t\t// simulate dispatching error-related request\n\t\tFilter errorRelatedFilter = ObservationFilterChainDecorator.FilterObservation\n\t\t\t.create(Observation.createNotStarted(\"wrap\", registry))\n\t\t\t.wrap(fcp);\n\t\tServletRequest errorRelatedRequest = new MockHttpServletRequest(\"GET\", \"/error\");\n\t\terrorRelatedRequest.setAttribute(ObservationFilterChainDecorator.ATTRIBUTE, parentObservation);\n\t\terrorRelatedFilter.doFilter(errorRelatedRequest, new MockHttpServletResponse(), this.chain);\n\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(8)).onStart(captor.capture());\n\t\tverify(handler, times(8)).onStop(any());\n\t\tList<Observation.Context> contexts = captor.getAllValues();\n\n\t\tObservation.Context initialRequestObservationContextBefore = contexts.get(1);\n\t\tObservation.Context initialRequestObservationContextAfter = contexts.get(3);\n\t\tassertFilterChainObservation(initialRequestObservationContextBefore, \"before\", 1);\n\t\tassertFilterChainObservation(initialRequestObservationContextAfter, \"after\", 1);\n\n\t\tassertThat(initialRequestObservationContextBefore.getParentObservation()).isNotNull();\n\t\tassertThat(initialRequestObservationContextBefore.getParentObservation())\n\t\t\t.isSameAs(initialRequestObservationContextAfter.getParentObservation());\n\n\t\tObservation.Context errorRelatedRequestObservationContextBefore = contexts.get(5);\n\t\tObservation.Context errorRelatedRequestObservationContextAfter = contexts.get(7);\n\t\tassertFilterChainObservation(errorRelatedRequestObservationContextBefore, \"before\", 1);\n\t\tassertFilterChainObservation(errorRelatedRequestObservationContextAfter, \"after\", 1);\n\n\t\tassertThat(errorRelatedRequestObservationContextBefore.getParentObservation()).isNotNull();\n\t\tassertThat(errorRelatedRequestObservationContextBefore.getParentObservation())\n\t\t\t.isSameAs(initialRequestObservationContextBefore.getParentObservation());\n\t\tassertThat(errorRelatedRequestObservationContextAfter.getParentObservation()).isNotNull();\n\t\tassertThat(errorRelatedRequestObservationContextAfter.getParentObservation())\n\t\t\t.isSameAs(initialRequestObservationContextBefore.getParentObservation());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMultipleFiltersThenObservationRegistryObserves() throws Exception {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tFilter one = mockFilter();\n\t\tFilter two = mockFilter();\n\t\tFilter three = mockFilter();\n\t\tSecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, one, two, three);\n\t\tFilterChainProxy fcp = new FilterChainProxy(sec);\n\t\tfcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));\n\t\tFilter filter = ObservationFilterChainDecorator.FilterObservation\n\t\t\t.create(Observation.createNotStarted(\"wrap\", registry))\n\t\t\t.wrap(fcp);\n\t\tfilter.doFilter(this.request, this.response, this.chain);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(4)).onStart(captor.capture());\n\t\tverify(handler, times(4)).onStop(any());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertFilterChainObservation(contexts.next(), \"before\", 3);\n\t\tassertThat(contexts.next().getName()).isEqualTo(ObservationFilterChainDecorator.SECURED_OBSERVATION_NAME);\n\t\tassertFilterChainObservation(contexts.next(), \"after\", 3);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMismatchesThenObservationRegistryObserves() throws Exception {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tSecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, Arrays.asList(this.filter));\n\t\tFilterChainProxy fcp = new FilterChainProxy(sec);\n\t\tfcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));\n\t\tFilter filter = ObservationFilterChainDecorator.FilterObservation\n\t\t\t.create(Observation.createNotStarted(\"wrap\", registry))\n\t\t\t.wrap(fcp);\n\t\tfilter.doFilter(this.request, this.response, this.chain);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(2)).onStart(captor.capture());\n\t\tverify(handler, times(2)).onStop(any());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(ObservationFilterChainDecorator.UNSECURED_OBSERVATION_NAME);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenFilterExceptionThenObservationRegistryObserves() throws Exception {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\twillThrow(IllegalStateException.class).given(this.filter).doFilter(any(), any(), any());\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tSecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, Arrays.asList(this.filter));\n\t\tFilterChainProxy fcp = new FilterChainProxy(sec);\n\t\tfcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));\n\t\tFilter filter = ObservationFilterChainDecorator.FilterObservation\n\t\t\t.create(Observation.createNotStarted(\"wrap\", registry))\n\t\t\t.wrap(fcp);\n\t\tassertThatExceptionOfType(IllegalStateException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(this.request, this.response, this.chain));\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(2)).onStart(captor.capture());\n\t\tverify(handler, times(2)).onStop(any());\n\t\tverify(handler, atLeastOnce()).onError(any());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertFilterChainObservation(contexts.next(), \"before\", 1);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenExceptionWithMultipleFiltersThenObservationRegistryObserves() throws Exception {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tFilter one = mockFilter();\n\t\tFilter two = mock(Filter.class);\n\t\twillThrow(IllegalStateException.class).given(two).doFilter(any(), any(), any());\n\t\tFilter three = mockFilter();\n\t\tSecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, one, two, three);\n\t\tFilterChainProxy fcp = new FilterChainProxy(sec);\n\t\tfcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));\n\t\tFilter filter = ObservationFilterChainDecorator.FilterObservation\n\t\t\t.create(Observation.createNotStarted(\"wrap\", registry))\n\t\t\t.wrap(fcp);\n\t\tassertThatExceptionOfType(IllegalStateException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(this.request, this.response, this.chain));\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(2)).onStart(captor.capture());\n\t\tverify(handler, times(2)).onStop(any());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertFilterChainObservation(contexts.next(), \"before\", 2);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenOneFilterDoesNotProceedThenObservationRegistryObserves() throws Exception {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tgiven(this.matcher.matches(any())).willReturn(true);\n\t\tFilter one = mockFilter();\n\t\tFilter two = mock(Filter.class);\n\t\tFilter three = mockFilter();\n\t\tSecurityFilterChain sec = new DefaultSecurityFilterChain(this.matcher, one, two, three);\n\t\tFilterChainProxy fcp = new FilterChainProxy(sec);\n\t\tfcp.setFilterChainDecorator(new ObservationFilterChainDecorator(registry));\n\t\tFilter filter = ObservationFilterChainDecorator.FilterObservation\n\t\t\t.create(Observation.createNotStarted(\"wrap\", registry))\n\t\t\t.wrap(fcp);\n\t\tfilter.doFilter(this.request, this.response, this.chain);\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(3)).onStart(captor.capture());\n\t\tverify(handler, times(3)).onStop(any());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertFilterChainObservation(contexts.next(), \"before\", 2);\n\t\tassertFilterChainObservation(contexts.next(), \"after\", 3);\n\t}\n\n\tstatic void assertFilterChainObservation(Observation.Context context, String filterSection, int chainPosition) {\n\t\tassertThat(context).isInstanceOf(ObservationFilterChainDecorator.FilterChainObservationContext.class);\n\t\tObservationFilterChainDecorator.FilterChainObservationContext filterChainObservationContext = (ObservationFilterChainDecorator.FilterChainObservationContext) context;\n\t\tassertThat(context.getName())\n\t\t\t.isEqualTo(ObservationFilterChainDecorator.FilterChainObservationConvention.CHAIN_OBSERVATION_NAME);\n\t\tassertThat(context.getContextualName()).endsWith(filterSection);\n\t\tassertThat(filterChainObservationContext.getChainPosition()).isEqualTo(chainPosition);\n\t}\n\n\tstatic Filter mockFilter() throws Exception {\n\t\tFilter filter = mock(Filter.class);\n\t\twillAnswer((invocation) -> {\n\t\t\tHttpServletRequest request = invocation.getArgument(0, HttpServletRequest.class);\n\t\t\tHttpServletResponse response = invocation.getArgument(1, HttpServletResponse.class);\n\t\t\tFilterChain chain = invocation.getArgument(2, FilterChain.class);\n\t\t\tchain.doFilter(request, response);\n\t\t\treturn null;\n\t\t}).given(filter).doFilter(any(), any(), any());\n\t\treturn filter;\n\t}\n\n\tprivate static class MockFirewalledRequest extends FirewalledRequest {\n\n\t\tMockFirewalledRequest(HttpServletRequest request) {\n\t\t\tsuper(request);\n\t\t}\n\n\t\t@Override\n\t\tpublic void reset() {\n\n\t\t}\n\n\t}\n\n\tprivate static class MockFilter implements Filter {\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tchain.doFilter(request, response);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/FilterInvocationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.util.Enumeration;\nimport java.util.NoSuchElementException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.web.FilterInvocation.DummyRequest;\nimport org.springframework.security.web.util.UrlUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests {@link FilterInvocation}.\n *\n * @author Ben Alex\n * @author colin sampaleanu\n */\npublic class FilterInvocationTests {\n\n\t@Test\n\tpublic void testGettersAndStringMethods() {\n\t\tMockHttpServletRequest request = get().requestUri(\"/mycontext\", \"/HelloWorld\", \"/some/more/segments.html\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tFilterInvocation fi = new FilterInvocation(request, response, chain);\n\t\tassertThat(fi.getRequest()).isEqualTo(request);\n\t\tassertThat(fi.getHttpRequest()).isEqualTo(request);\n\t\tassertThat(fi.getResponse()).isEqualTo(response);\n\t\tassertThat(fi.getHttpResponse()).isEqualTo(response);\n\t\tassertThat(fi.getChain()).isEqualTo(chain);\n\t\tassertThat(fi.getRequestUrl()).isEqualTo(\"/HelloWorld/some/more/segments.html\");\n\t\tassertThat(fi.toString()).isEqualTo(\"filter invocation [GET /HelloWorld/some/more/segments.html]\");\n\t\tassertThat(fi.getFullRequestUrl()).isEqualTo(\"http://localhost/mycontext/HelloWorld/some/more/segments.html\");\n\t}\n\n\t@Test\n\tpublic void testRejectsNullFilterChain() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(null, null);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new FilterInvocation(request, response, null));\n\t}\n\n\t@Test\n\tpublic void testRejectsNullServletRequest() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new FilterInvocation(null, response, mock(FilterChain.class)));\n\t}\n\n\t@Test\n\tpublic void testRejectsNullServletResponse() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(null, null);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new FilterInvocation(request, null, mock(FilterChain.class)));\n\t}\n\n\t@Test\n\tpublic void testStringMethodsWithAQueryString() {\n\t\tMockHttpServletRequest request = get().requestUri(\"/mycontext\", \"/HelloWorld\", null)\n\t\t\t.queryString(\"foo=bar\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tassertThat(fi.getRequestUrl()).isEqualTo(\"/HelloWorld?foo=bar\");\n\t\tassertThat(fi.toString()).isEqualTo(\"filter invocation [GET /HelloWorld?foo=bar]\");\n\t\tassertThat(fi.getFullRequestUrl()).isEqualTo(\"http://localhost/mycontext/HelloWorld?foo=bar\");\n\t}\n\n\t@Test\n\tpublic void testStringMethodsWithoutAnyQueryString() {\n\t\tMockHttpServletRequest request = get().requestUri(\"/mycontext\", \"/HelloWorld\", null).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterInvocation fi = new FilterInvocation(request, response, mock(FilterChain.class));\n\t\tassertThat(fi.getRequestUrl()).isEqualTo(\"/HelloWorld\");\n\t\tassertThat(fi.toString()).isEqualTo(\"filter invocation [GET /HelloWorld]\");\n\t\tassertThat(fi.getFullRequestUrl()).isEqualTo(\"http://localhost/mycontext/HelloWorld\");\n\t}\n\n\t@Test\n\tpublic void dummyChainRejectsInvocation() throws Exception {\n\t\tassertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> FilterInvocation.DUMMY_CHAIN\n\t\t\t.doFilter(mock(HttpServletRequest.class), mock(HttpServletResponse.class)));\n\t}\n\n\t@Test\n\tpublic void dummyRequestIsSupportedByUrlUtils() {\n\t\tDummyRequest request = new DummyRequest();\n\t\trequest.setContextPath(\"\");\n\t\trequest.setRequestURI(\"/something\");\n\t\tUrlUtils.buildRequestUrl(request);\n\t}\n\n\t@Test\n\tpublic void constructorWhenServletContextProvidedThenSetServletContextInRequest() {\n\t\tString contextPath = \"\";\n\t\tString servletPath = \"/path\";\n\t\tString method = \"\";\n\t\tMockServletContext mockServletContext = new MockServletContext();\n\t\tFilterInvocation filterInvocation = new FilterInvocation(contextPath, servletPath, method, mockServletContext);\n\t\tassertThat(filterInvocation.getRequest().getServletContext()).isSameAs(mockServletContext);\n\t}\n\n\t@Test\n\tpublic void testDummyRequestGetHeaders() {\n\t\tDummyRequest request = new DummyRequest();\n\t\trequest.addHeader(\"known\", \"val\");\n\t\tEnumeration<String> headers = request.getHeaders(\"known\");\n\t\tassertThat(headers.hasMoreElements()).isTrue();\n\t\tassertThat(headers.nextElement()).isEqualTo(\"val\");\n\t\tassertThat(headers.hasMoreElements()).isFalse();\n\t\tassertThatExceptionOfType(NoSuchElementException.class).isThrownBy(headers::nextElement);\n\t}\n\n\t@Test\n\tpublic void testDummyRequestGetHeadersNull() {\n\t\tDummyRequest request = new DummyRequest();\n\t\tEnumeration<String> headers = request.getHeaders(\"unknown\");\n\t\tassertThat(headers.hasMoreElements()).isFalse();\n\t\tassertThatExceptionOfType(NoSuchElementException.class).isThrownBy(headers::nextElement);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/FormPostRedirectStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\n\nimport org.assertj.core.api.ThrowingConsumer;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class FormPostRedirectStrategyTests {\n\n\tprivate static final String POLICY_DIRECTIVE_PATTERN = \"script-src 'nonce-(.+)'\";\n\n\tprivate FormPostRedirectStrategy redirectStrategy;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void beforeEach() {\n\t\tthis.redirectStrategy = new FormPostRedirectStrategy();\n\t\tfinal MockServletContext mockServletContext = new MockServletContext();\n\t\tmockServletContext.setContextPath(\"/contextPath\");\n\t\t// the request URL doesn't matter\n\t\tthis.request = MockMvcRequestBuilders.get(\"https://localhost\").buildRequest(mockServletContext);\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void absoluteUrlNoParametersRedirect() throws IOException {\n\t\tthis.redirectStrategy.sendRedirect(this.request, this.response, \"https://example.com\");\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(this.response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);\n\t\tassertThat(this.response.getContentAsString()).contains(\"action=\\\"https://example.com\\\"\");\n\t\tassertThat(this.response).satisfies(hasScriptSrcNonce());\n\t}\n\n\t@Test\n\tpublic void rootRelativeUrlNoParametersRedirect() throws IOException {\n\t\tthis.redirectStrategy.sendRedirect(this.request, this.response, \"/test\");\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(this.response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);\n\t\tassertThat(this.response.getContentAsString()).contains(\"action=\\\"/test\\\"\");\n\t\tassertThat(this.response).satisfies(hasScriptSrcNonce());\n\t}\n\n\t@Test\n\tpublic void relativeUrlNoParametersRedirect() throws IOException {\n\t\tthis.redirectStrategy.sendRedirect(this.request, this.response, \"test\");\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(this.response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);\n\t\tassertThat(this.response.getContentAsString()).contains(\"action=\\\"test\\\"\");\n\t\tassertThat(this.response).satisfies(hasScriptSrcNonce());\n\t}\n\n\t@Test\n\tpublic void absoluteUrlWithFragmentRedirect() throws IOException {\n\t\tthis.redirectStrategy.sendRedirect(this.request, this.response, \"https://example.com/path#fragment\");\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(this.response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);\n\t\tassertThat(this.response.getContentAsString()).contains(\"action=\\\"https://example.com/path#fragment\\\"\");\n\t\tassertThat(this.response).satisfies(hasScriptSrcNonce());\n\t}\n\n\t@Test\n\tpublic void absoluteUrlWithQueryParamsRedirect() throws IOException {\n\t\tthis.redirectStrategy.sendRedirect(this.request, this.response,\n\t\t\t\t\"https://example.com/path?param1=one&param2=two#fragment\");\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(this.response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);\n\t\tassertThat(this.response.getContentAsString()).contains(\"action=\\\"https://example.com/path#fragment\\\"\");\n\t\tassertThat(this.response.getContentAsString())\n\t\t\t.contains(\"<input name=\\\"param1\\\" type=\\\"hidden\\\" value=\\\"one\\\" />\");\n\t\tassertThat(this.response.getContentAsString())\n\t\t\t.contains(\"<input name=\\\"param2\\\" type=\\\"hidden\\\" value=\\\"two\\\" />\");\n\t\tassertThat(this.response).satisfies(hasScriptSrcNonce());\n\t}\n\n\tprivate ThrowingConsumer<MockHttpServletResponse> hasScriptSrcNonce() {\n\t\treturn (response) -> {\n\t\t\tfinal String policyDirective = response.getHeader(\"Content-Security-Policy\");\n\t\t\tassertThat(policyDirective).isNotEmpty();\n\t\t\tassertThat(policyDirective).matches(POLICY_DIRECTIVE_PATTERN);\n\n\t\t\tfinal String nonce = policyDirective.replaceFirst(POLICY_DIRECTIVE_PATTERN, \"$1\");\n\t\t\tassertThat(response.getContentAsString()).contains(\"<script nonce=\\\"%s\\\">\".formatted(nonce));\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/ObservationFilterChainDecoratorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link ObservationFilterChainDecorator}\n */\npublic class ObservationFilterChainDecoratorTests {\n\n\t@Test\n\tvoid decorateWhenDefaultsThenObserves() throws Exception {\n\t\tObservationHandler<?> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationFilterChainDecorator decorator = new ObservationFilterChainDecorator(registry);\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tFilterChain decorated = decorator.decorate(chain);\n\t\tdecorated.doFilter(new MockHttpServletRequest(\"GET\", \"/\"), new MockHttpServletResponse());\n\t\tverify(handler).onStart(any());\n\t}\n\n\t@Test\n\tvoid decorateWhenNoopThenDoesNotObserve() throws Exception {\n\t\tObservationHandler<?> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.NOOP;\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationFilterChainDecorator decorator = new ObservationFilterChainDecorator(registry);\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tFilterChain decorated = decorator.decorate(chain);\n\t\tdecorated.doFilter(new MockHttpServletRequest(\"GET\", \"/\"), new MockHttpServletResponse());\n\t\tverifyNoInteractions(handler);\n\t}\n\n\t@Test\n\tvoid decorateFiltersWhenDefaultsThenObserves() throws Exception {\n\t\tObservationHandler<?> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationFilterChainDecorator decorator = new ObservationFilterChainDecorator(registry);\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tFilter filter = mock(Filter.class);\n\t\tFilterChain decorated = decorator.decorate(chain, List.of(filter));\n\t\tdecorated.doFilter(new MockHttpServletRequest(\"GET\", \"/\"), new MockHttpServletResponse());\n\t\tverify(handler, times(2)).onStart(any());\n\t\tArgumentCaptor<Observation.Event> event = ArgumentCaptor.forClass(Observation.Event.class);\n\t\tverify(handler, times(2)).onEvent(event.capture(), any());\n\t\tList<Observation.Event> events = event.getAllValues();\n\t\tassertThat(events.get(0).getName()).isEqualTo(filter.getClass().getSimpleName() + \".before\");\n\t\tassertThat(events.get(1).getName()).isEqualTo(filter.getClass().getSimpleName() + \".after\");\n\t}\n\n\t@Test\n\tvoid decorateFiltersWhenDefaultsThenUsesEventName() throws Exception {\n\t\tObservationHandler<?> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationFilterChainDecorator decorator = new ObservationFilterChainDecorator(registry);\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tFilter filter = new BasicAuthenticationFilter();\n\t\tFilterChain decorated = decorator.decorate(chain, List.of(filter));\n\t\tdecorated.doFilter(new MockHttpServletRequest(\"GET\", \"/\"), new MockHttpServletResponse());\n\t\tArgumentCaptor<Observation.Event> event = ArgumentCaptor.forClass(Observation.Event.class);\n\t\tverify(handler, times(2)).onEvent(event.capture(), any());\n\t\tList<Observation.Event> events = event.getAllValues();\n\t\tassertThat(events.get(0).getName()).isEqualTo(\"authentication.basic.before\");\n\t\tassertThat(events.get(1).getName()).isEqualTo(\"authentication.basic.after\");\n\t}\n\n\t// gh-12787\n\t@Test\n\tvoid decorateFiltersWhenErrorsThenClosesObservationOnlyOnce() throws Exception {\n\t\tObservationHandler<?> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationFilterChainDecorator decorator = new ObservationFilterChainDecorator(registry);\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tFilter filter = mock(Filter.class);\n\t\twillThrow(IllegalArgumentException.class).given(filter).doFilter(any(), any(), any());\n\t\tFilterChain decorated = decorator.decorate(chain, List.of(filter));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(\n\t\t\t\t() -> decorated.doFilter(new MockHttpServletRequest(\"GET\", \"/\"), new MockHttpServletResponse()));\n\t\tverify(handler).onScopeClosed(any());\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"decorateFiltersWhenCompletesThenHasSpringSecurityReachedFilterNameTag\")\n\tvoid decorateFiltersWhenCompletesThenHasSpringSecurityReachedFilterNameTag(Filter filter,\n\t\t\tString expectedFilterNameTag) throws Exception {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationFilterChainDecorator decorator = new ObservationFilterChainDecorator(registry);\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tFilterChain decorated = decorator.decorate(chain, List.of(filter));\n\t\tdecorated.doFilter(new MockHttpServletRequest(\"GET\", \"/\"), new MockHttpServletResponse());\n\t\tArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(3)).onScopeClosed(context.capture());\n\t\tassertThat(context.getValue().getLowCardinalityKeyValue(\"spring.security.reached.filter.name\").getValue())\n\t\t\t.isEqualTo(expectedFilterNameTag);\n\t}\n\n\t// gh-13660\n\t@Test\n\tvoid observationNamesDoNotContainDashes() {\n\t\tObservationFilterChainDecorator.ObservationFilter.OBSERVATION_NAMES.values()\n\t\t\t.forEach((name) -> assertThat(name).doesNotContain(\"-\"));\n\t}\n\n\tstatic Stream<Arguments> decorateFiltersWhenCompletesThenHasSpringSecurityReachedFilterNameTag() {\n\t\tFilter filterWithName = new BasicAuthenticationFilter();\n\n\t\t// Anonymous class leads to an empty filter-name\n\t\tFilter filterWithoutName = new Filter() {\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\t\tthrows IOException, ServletException {\n\t\t\t\tchain.doFilter(request, response);\n\t\t\t}\n\t\t};\n\n\t\treturn Stream.of(Arguments.of(filterWithName, \"BasicAuthenticationFilter\"),\n\t\t\t\tArguments.of(filterWithoutName, \"none\"));\n\t}\n\n\tprivate static class BasicAuthenticationFilter implements Filter {\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n\t\t\t\tthrows IOException, ServletException {\n\t\t\tchain.doFilter(request, response);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/PortMapperImplTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link PortMapperImpl}.\n *\n * @author Ben Alex\n */\npublic class PortMapperImplTests {\n\n\t@Test\n\tpublic void testDefaultMappingsAreKnown() {\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tassertThat(portMapper.lookupHttpPort(443)).isEqualTo(Integer.valueOf(80));\n\t\tassertThat(Integer.valueOf(8080)).isEqualTo(portMapper.lookupHttpPort(8443));\n\t\tassertThat(Integer.valueOf(443)).isEqualTo(portMapper.lookupHttpsPort(80));\n\t\tassertThat(Integer.valueOf(8443)).isEqualTo(portMapper.lookupHttpsPort(8080));\n\t}\n\n\t@Test\n\tpublic void testDetectsEmptyMap() {\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> portMapper.setPortMappings(new HashMap<>()));\n\t}\n\n\t@Test\n\tpublic void testDetectsNullMap() {\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> portMapper.setPortMappings(null));\n\t}\n\n\t@Test\n\tpublic void testGetTranslatedPortMappings() {\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tassertThat(portMapper.getTranslatedPortMappings()).hasSize(2);\n\t}\n\n\t@Test\n\tpublic void testRejectsOutOfRangeMappings() {\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tMap<String, String> map = new HashMap<>();\n\t\tmap.put(\"79\", \"80559\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> portMapper.setPortMappings(map));\n\t}\n\n\t@Test\n\tpublic void testReturnsNullIfHttpPortCannotBeFound() {\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tassertThat(portMapper.lookupHttpPort(Integer.valueOf(\"34343\")) == null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testSupportsCustomMappings() {\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tMap<String, String> map = new HashMap<>();\n\t\tmap.put(\"79\", \"442\");\n\t\tportMapper.setPortMappings(map);\n\t\tassertThat(portMapper.lookupHttpPort(442)).isEqualTo(Integer.valueOf(79));\n\t\tassertThat(Integer.valueOf(442)).isEqualTo(portMapper.lookupHttpsPort(79));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/RequestMatcherRedirectFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests for {@link RequestMatcherRedirectFilter}.\n *\n * @author Evgeniy Cheban\n */\npublic class RequestMatcherRedirectFilterTests {\n\n\t@Test\n\tpublic void doFilterWhenRequestMatchThenRedirectToSpecifiedUrl() throws Exception {\n\t\tRequestMatcherRedirectFilter filter = new RequestMatcherRedirectFilter(pathPattern(\"/context\"), \"/test\");\n\n\t\tMockHttpServletRequest request = get(\"/context\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tfilter.doFilter(request, response, filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/test\");\n\n\t\tverifyNoInteractions(filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestNotMatchThenNextFilter() throws Exception {\n\t\tRequestMatcherRedirectFilter filter = new RequestMatcherRedirectFilter(pathPattern(\"/context\"), \"/test\");\n\n\t\tMockHttpServletRequest request = get(\"/test\").build();\n\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\n\t\tfilter.doFilter(request, response, filterChain);\n\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\n\t\tverify(filterChain).doFilter(request, response);\n\t}\n\n\t@Test\n\tpublic void constructWhenRequestMatcherNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RequestMatcherRedirectFilter(null, \"/test\"))\n\t\t\t.withMessage(\"requestMatcher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructWhenRedirectUrlNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new RequestMatcherRedirectFilter(pathPattern(\"/**\"), null))\n\t\t\t.withMessage(\"redirectUrl cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructWhenRedirectUrlEmpty() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RequestMatcherRedirectFilter(pathPattern(\"/**\"), \"\"))\n\t\t\t.withMessage(\"redirectUrl cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructWhenRedirectUrlBlank() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RequestMatcherRedirectFilter(pathPattern(\"/**\"), \" \"))\n\t\t\t.withMessage(\"redirectUrl cannot be empty\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/ServerWebExchangeThreadLocalAccessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web;\n\nimport java.util.concurrent.CountDownLatch;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.task.SimpleAsyncTaskExecutor;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.ServerWebExchangeThreadLocalAccessor;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ServerWebExchangeThreadLocalAccessor}.\n *\n * @author Steve Riesenberg\n */\npublic class ServerWebExchangeThreadLocalAccessorTests {\n\n\tprivate ServerWebExchangeThreadLocalAccessor threadLocalAccessor;\n\n\tprivate ServerWebExchange exchange;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.threadLocalAccessor = new ServerWebExchangeThreadLocalAccessor();\n\t\tthis.exchange = MockServerWebExchange.builder(MockServerHttpRequest.get(\"/\")).build();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tthis.threadLocalAccessor.setValue();\n\t}\n\n\t@Test\n\tpublic void keyAlwaysReturnsServerWebExchangeClass() {\n\t\tassertThat(this.threadLocalAccessor.key()).isEqualTo(ServerWebExchange.class);\n\t}\n\n\t@Test\n\tpublic void getValueWhenThreadLocalNotSetThenReturnsNull() {\n\t\tassertThat(this.threadLocalAccessor.getValue()).isNull();\n\t}\n\n\t@Test\n\tpublic void getValueWhenThreadLocalSetThenReturnsServerWebExchange() {\n\t\tthis.threadLocalAccessor.setValue(this.exchange);\n\t\tassertThat(this.threadLocalAccessor.getValue()).isSameAs(this.exchange);\n\t}\n\n\t@Test\n\tpublic void getValueWhenThreadLocalSetOnAnotherThreadThenReturnsNull() throws InterruptedException {\n\t\tCountDownLatch threadLocalSet = new CountDownLatch(1);\n\t\tCountDownLatch threadLocalRead = new CountDownLatch(1);\n\t\tCountDownLatch threadLocalCleared = new CountDownLatch(1);\n\n\t\tRunnable task = () -> {\n\t\t\tthis.threadLocalAccessor.setValue(this.exchange);\n\t\t\tthreadLocalSet.countDown();\n\t\t\ttry {\n\t\t\t\tthreadLocalRead.await();\n\t\t\t}\n\t\t\tcatch (InterruptedException ignored) {\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tthis.threadLocalAccessor.setValue();\n\t\t\t\tthreadLocalCleared.countDown();\n\t\t\t}\n\t\t};\n\t\ttry (SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor()) {\n\t\t\ttaskExecutor.execute(task);\n\t\t\tthreadLocalSet.await();\n\t\t\tassertThat(this.threadLocalAccessor.getValue()).isNull();\n\t\t\tthreadLocalRead.countDown();\n\t\t\tthreadLocalCleared.await();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void setValueWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.threadLocalAccessor.setValue(null))\n\t\t\t.withMessage(\"exchange cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setValueWhenThreadLocalSetThenClearsThreadLocal() {\n\t\tthis.threadLocalAccessor.setValue(this.exchange);\n\t\tassertThat(this.threadLocalAccessor.getValue()).isSameAs(this.exchange);\n\n\t\tthis.threadLocalAccessor.setValue();\n\t\tassertThat(this.threadLocalAccessor.getValue()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer;\nimport org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n@ExtendWith(MockitoExtension.class)\nclass AuthorizationManagerWebInvocationPrivilegeEvaluatorTests {\n\n\t@InjectMocks\n\tprivate AuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator;\n\n\t@Mock\n\tprivate AuthorizationManager<HttpServletRequest> authorizationManager;\n\n\t@Mock\n\tprivate HttpServletRequestTransformer requestTransformer;\n\n\t@Test\n\tvoid constructorWhenAuthorizationManagerNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthorizationManagerWebInvocationPrivilegeEvaluator(null))\n\t\t\t.withMessage(\"authorizationManager cannot be null\");\n\t}\n\n\t@Test\n\tvoid isAllowedWhenAuthorizationManagerAllowsThenAllowedTrue() {\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(true));\n\t\tboolean allowed = this.privilegeEvaluator.isAllowed(\"/test\", TestAuthentication.authenticatedUser());\n\t\tassertThat(allowed).isTrue();\n\t\tverify(this.authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tvoid isAllowedWhenAuthorizationManagerDeniesAllowedFalse() {\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(new AuthorizationDecision(false));\n\t\tboolean allowed = this.privilegeEvaluator.isAllowed(\"/test\", TestAuthentication.authenticatedUser());\n\t\tassertThat(allowed).isFalse();\n\t}\n\n\t@Test\n\tvoid isAllowedWhenAuthorizationManagerAbstainsThenAllowedTrue() {\n\t\tgiven(this.authorizationManager.authorize(any(), any())).willReturn(null);\n\t\tboolean allowed = this.privilegeEvaluator.isAllowed(\"/test\", TestAuthentication.authenticatedUser());\n\t\tassertThat(allowed).isTrue();\n\t}\n\n\t@Test\n\tvoid isAllowedWhenServletContextExistsThenFilterInvocationHasServletContext() {\n\t\tServletContext servletContext = new MockServletContext();\n\t\tthis.privilegeEvaluator.setServletContext(servletContext);\n\t\tthis.privilegeEvaluator.isAllowed(\"/test\", TestAuthentication.authenticatedUser());\n\t\tArgumentCaptor<HttpServletRequest> captor = ArgumentCaptor.forClass(HttpServletRequest.class);\n\t\tverify(this.authorizationManager).authorize(any(), captor.capture());\n\t\tassertThat(captor.getValue().getServletContext()).isSameAs(servletContext);\n\t}\n\n\t@Test\n\tvoid setRequestTransformerWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.privilegeEvaluator.setRequestTransformer(null));\n\t}\n\n\t@Test\n\tvoid isAllowedWhenRequestTransformerThenUsesRequestTransformerResult() {\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tgiven(this.requestTransformer.transform(any())).willReturn(request);\n\t\tthis.privilegeEvaluator.setRequestTransformer(this.requestTransformer);\n\n\t\tthis.privilegeEvaluator.isAllowed(\"/test\", TestAuthentication.authenticatedUser());\n\n\t\tverify(this.authorizationManager).authorize(any(), eq(request));\n\t}\n\n\t// gh-16771\n\t@Test\n\tvoid isAllowedWhenInvokesDelegateThenCachesRequestPath() {\n\t\tRequestMatcherDelegatingAuthorizationManager authorizationManager = RequestMatcherDelegatingAuthorizationManager\n\t\t\t.builder()\n\t\t\t.add(pathPattern(\"/test/**\"),\n\t\t\t\t\t(authentication, ctx) -> this.authorizationManager.authorize(authentication, ctx.getRequest()))\n\t\t\t.build();\n\t\tAuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(\n\t\t\t\tauthorizationManager);\n\t\tprivilegeEvaluator.setRequestTransformer(new PathPatternRequestTransformer());\n\t\tprivilegeEvaluator.isAllowed(\"/test\", TestAuthentication.authenticatedUser());\n\t\tverify(this.authorizationManager).authorize(any(), any());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/DelegatingAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.util.LinkedHashMap;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.web.csrf.CsrfException;\nimport org.springframework.security.web.csrf.InvalidCsrfTokenException;\nimport org.springframework.security.web.csrf.MissingCsrfTokenException;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingAccessDeniedHandlerTests {\n\n\t@Mock\n\tprivate AccessDeniedHandler handler1;\n\n\t@Mock\n\tprivate AccessDeniedHandler handler2;\n\n\t@Mock\n\tprivate AccessDeniedHandler handler3;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\tprivate LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers;\n\n\tprivate DelegatingAccessDeniedHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.handlers = new LinkedHashMap<>();\n\t}\n\n\t@Test\n\tpublic void moreSpecificDoesNotInvokeLessSpecific() throws Exception {\n\t\tthis.handlers.put(CsrfException.class, this.handler1);\n\t\tthis.handler = new DelegatingAccessDeniedHandler(this.handlers, this.handler3);\n\t\tAccessDeniedException accessDeniedException = new AccessDeniedException(\"\");\n\t\tthis.handler.handle(this.request, this.response, accessDeniedException);\n\t\tverify(this.handler1, never()).handle(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AccessDeniedException.class));\n\t\tverify(this.handler3).handle(this.request, this.response, accessDeniedException);\n\t}\n\n\t@Test\n\tpublic void matchesDoesNotInvokeDefault() throws Exception {\n\t\tthis.handlers.put(InvalidCsrfTokenException.class, this.handler1);\n\t\tthis.handlers.put(MissingCsrfTokenException.class, this.handler2);\n\t\tthis.handler = new DelegatingAccessDeniedHandler(this.handlers, this.handler3);\n\t\tAccessDeniedException accessDeniedException = new MissingCsrfTokenException(\"123\");\n\t\tthis.handler.handle(this.request, this.response, accessDeniedException);\n\t\tverify(this.handler1, never()).handle(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AccessDeniedException.class));\n\t\tverify(this.handler2).handle(this.request, this.response, accessDeniedException);\n\t\tverify(this.handler3, never()).handle(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AccessDeniedException.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authorization.AuthorityAuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.FactorAuthorizationDecision;\nimport org.springframework.security.authorization.RequiredFactor;\nimport org.springframework.security.authorization.RequiredFactorError;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n@ExtendWith(MockitoExtension.class)\nclass DelegatingMissingAuthorityAccessDeniedHandlerTests {\n\n\tDelegatingMissingAuthorityAccessDeniedHandler.Builder builder;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\t@Mock\n\tAuthenticationEntryPoint factorEntryPoint;\n\n\t@Mock\n\tAccessDeniedHandler defaultAccessDeniedHandler;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.builder = DelegatingMissingAuthorityAccessDeniedHandler.builder();\n\t\tthis.builder.addEntryPointFor(this.factorEntryPoint, \"FACTOR\");\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tvoid whenKnownAuthorityThenCommences() throws Exception {\n\t\tAccessDeniedHandler accessDeniedHandler = this.builder.build();\n\t\taccessDeniedHandler.handle(this.request, this.response, missingAuthorities(\"FACTOR\"));\n\t\tverify(this.factorEntryPoint).commence(any(), any(), any());\n\t}\n\n\t@Test\n\tvoid whenUnknownAuthorityThenDefaultCommences() throws Exception {\n\t\tDelegatingMissingAuthorityAccessDeniedHandler accessDeniedHandler = this.builder.build();\n\t\taccessDeniedHandler.setDefaultAccessDeniedHandler(this.defaultAccessDeniedHandler);\n\t\taccessDeniedHandler.handle(this.request, this.response, missingAuthorities(\"ROLE_USER\"));\n\t\tverify(this.defaultAccessDeniedHandler).handle(any(), any(), any());\n\t\tverifyNoInteractions(this.factorEntryPoint);\n\t}\n\n\t@Test\n\tvoid whenNoAuthoritiesFoundThenDefaultCommences() throws Exception {\n\t\tDelegatingMissingAuthorityAccessDeniedHandler accessDeniedHandler = this.builder.build();\n\t\taccessDeniedHandler.setDefaultAccessDeniedHandler(this.defaultAccessDeniedHandler);\n\t\taccessDeniedHandler.handle(this.request, this.response, new AccessDeniedException(\"access denied\"));\n\t\tverify(this.defaultAccessDeniedHandler).handle(any(), any(), any());\n\t}\n\n\t@Test\n\tvoid whenMultipleAuthoritiesThenFirstMatchCommences() throws Exception {\n\t\tAuthenticationEntryPoint passwordEntryPoint = mock(AuthenticationEntryPoint.class);\n\t\tthis.builder.addEntryPointFor(passwordEntryPoint, \"PASSWORD\");\n\t\tAccessDeniedHandler accessDeniedHandler = this.builder.build();\n\t\taccessDeniedHandler.handle(this.request, this.response, missingAuthorities(\"PASSWORD\", \"FACTOR\"));\n\t\tverify(passwordEntryPoint).commence(any(), any(), any());\n\t\taccessDeniedHandler.handle(this.request, this.response, missingAuthorities(\"FACTOR\", \"PASSWORD\"));\n\t\tverify(this.factorEntryPoint).commence(any(), any(), any());\n\t}\n\n\t@Test\n\tvoid whenCustomRequestCacheThenUses() throws Exception {\n\t\tRequestCache requestCache = mock(RequestCache.class);\n\t\tDelegatingMissingAuthorityAccessDeniedHandler accessDeniedHandler = this.builder.build();\n\t\taccessDeniedHandler.setRequestCache(requestCache);\n\t\taccessDeniedHandler.handle(this.request, this.response, missingAuthorities(\"FACTOR\"));\n\t\tverify(requestCache).saveRequest(any(), any());\n\t\tverify(this.factorEntryPoint).commence(any(), any(), any());\n\t}\n\n\t@Test\n\tvoid whenKnownAuthorityButNoRequestMatchThenCommences() throws Exception {\n\t\tAuthenticationEntryPoint passwordEntryPoint = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher xhr = new RequestHeaderRequestMatcher(\"X-Requested-With\");\n\t\tthis.builder.addEntryPointFor((ep) -> ep.addEntryPointFor(passwordEntryPoint, xhr), \"PASSWORD\");\n\t\tAccessDeniedHandler accessDeniedHandler = this.builder.build();\n\t\taccessDeniedHandler.handle(this.request, this.response, missingAuthorities(\"PASSWORD\"));\n\t\tverify(passwordEntryPoint).commence(any(), any(), any());\n\t}\n\n\t@Test\n\tvoid whenMultipleEntryPointsThenFirstRequestMatchCommences() throws Exception {\n\t\tAuthenticationEntryPoint basicPasswordEntryPoint = mock(AuthenticationEntryPoint.class);\n\t\tAuthenticationEntryPoint formPasswordEntryPoint = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher xhr = new RequestHeaderRequestMatcher(\"X-Requested-With\");\n\t\tthis.builder.addEntryPointFor(\n\t\t\t\t(ep) -> ep.addEntryPointFor(basicPasswordEntryPoint, xhr).defaultEntryPoint(formPasswordEntryPoint),\n\t\t\t\t\"PASSWORD\");\n\t\tAccessDeniedHandler accessDeniedHandler = this.builder.build();\n\t\taccessDeniedHandler.handle(this.request, this.response, missingAuthorities(\"PASSWORD\"));\n\t\tverify(formPasswordEntryPoint).commence(any(), any(), any());\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"X-Requested-With\", \"XmlHttpRequest\");\n\t\taccessDeniedHandler.handle(request, this.response, missingAuthorities(\"PASSWORD\"));\n\t\tverify(basicPasswordEntryPoint).commence(any(), any(), any());\n\t}\n\n\t// gh-18000\n\t@Test\n\tvoid whenMultipleFactorErrorsThenPropagatesAll() throws Exception {\n\t\tAccessDeniedHandler accessDeniedHandler = this.builder.build();\n\t\taccessDeniedHandler.handle(this.request, this.response, missingFactors(\"FACTOR\", \"PASSWORD\"));\n\t\tverify(this.factorEntryPoint).commence(any(), any(), any());\n\t\tObject attribute = this.request.getAttribute(WebAttributes.REQUIRED_FACTOR_ERRORS);\n\t\tassertThat(attribute).isInstanceOf(Collection.class);\n\t\tassertThat((Collection<?>) attribute).hasSize(2);\n\t}\n\n\tAuthorizationDeniedException missingAuthorities(String... authorities) {\n\t\tCollection<GrantedAuthority> granted = AuthorityUtils.createAuthorityList(authorities);\n\t\tAuthorityAuthorizationDecision decision = new AuthorityAuthorizationDecision(false, granted);\n\t\treturn new AuthorizationDeniedException(\"access denied\", decision);\n\t}\n\n\tAuthorizationDeniedException missingFactors(String... factors) {\n\t\tList<RequiredFactorError> errors = Stream.of(factors)\n\t\t\t.map((f) -> RequiredFactorError.createMissing(RequiredFactor.withAuthority(f).build()))\n\t\t\t.toList();\n\t\tFactorAuthorizationDecision decision = new FactorAuthorizationDecision(errors);\n\t\treturn new AuthorizationDeniedException(\"access denied\", decision);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/ExceptionTranslationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.i18n.LocaleContextHolder;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.RememberMeAuthenticationToken;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests {@link ExceptionTranslationFilter}.\n *\n * @author Ben Alex\n * @author Gengwu Zhao\n */\npublic class ExceptionTranslationFilterTests {\n\n\t@AfterEach\n\t@BeforeEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\tprivate static String getSavedRequestUrl(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session == null) {\n\t\t\treturn null;\n\t\t}\n\t\tHttpSessionRequestCache rc = new HttpSessionRequestCache();\n\t\tSavedRequest sr = rc.getRequest(request, new MockHttpServletResponse());\n\t\treturn sr.getRedirectUrl();\n\t}\n\n\t@Test\n\tpublic void testAccessDeniedWhenAnonymous() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = get().requestUri(\"/mycontext\", \"/secure/page.html\", null).build();\n\t\t// Setup the FilterChain to thrown an access denied exception\n\t\tFilterChain fc = mockFilterChainWithException(new AccessDeniedException(\"\"));\n\t\t// Setup SecurityContextHolder, as filter needs to check if user is\n\t\t// anonymous\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new AnonymousAuthenticationToken(\"ignored\", \"ignored\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"IGNORED\")));\n\t\t// Test\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint);\n\t\tfilter.setAuthenticationTrustResolver(new AuthenticationTrustResolverImpl());\n\t\tassertThat(filter.getAuthenticationTrustResolver()).isNotNull();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, fc);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/login.jsp\");\n\t}\n\n\t@Test\n\tpublic void testAccessDeniedWhenAnonymousThenIncludesAuthenticationRequest() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tFilterChain fc = mockFilterChainWithException(new AccessDeniedException(\"\"));\n\t\tAnonymousAuthenticationToken token = new AnonymousAuthenticationToken(\"ignored\", \"ignored\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"IGNORED\"));\n\t\tSecurityContextHolder.getContext().setAuthentication(token);\n\t\tAuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class);\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(entryPoint);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, fc);\n\t\tArgumentCaptor<AuthenticationException> ex = ArgumentCaptor.forClass(AuthenticationException.class);\n\t\tverify(entryPoint).commence(any(), any(), ex.capture());\n\t\tassertThat(ex.getValue().getAuthenticationRequest()).isEqualTo(token);\n\t}\n\n\t@Test\n\tpublic void testAccessDeniedWithRememberMe() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = get().requestUri(\"/mycontext\", \"/secure/page.html\", null).build();\n\t\t// Setup the FilterChain to thrown an access denied exception\n\t\tFilterChain fc = mockFilterChainWithException(new AccessDeniedException(\"\"));\n\t\t// Setup SecurityContextHolder, as filter needs to check if user is remembered\n\t\tSecurityContext securityContext = SecurityContextHolder.createEmptyContext();\n\t\tsecurityContext.setAuthentication(\n\t\t\t\tnew RememberMeAuthenticationToken(\"ignored\", \"ignored\", AuthorityUtils.createAuthorityList(\"IGNORED\")));\n\t\tSecurityContextHolder.setContext(securityContext);\n\t\tRequestCache requestCache = new HttpSessionRequestCache();\n\t\t// Test\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint, requestCache);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, fc);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/login.jsp\");\n\t\tassertThat(getSavedRequestUrl(request)).isEqualTo(requestCache.getRequest(request, response).getRedirectUrl());\n\t}\n\n\t@Test\n\tpublic void testAccessDeniedWhenNonAnonymous() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = get(\"/secure/page.html\").build();\n\t\t// Setup the FilterChain to thrown an access denied exception\n\t\tFilterChain fc = mockFilterChainWithException(new AccessDeniedException(\"\"));\n\t\t// Setup SecurityContextHolder, as filter needs to check if user is\n\t\t// anonymous\n\t\tSecurityContextHolder.clearContext();\n\t\t// Setup a new AccessDeniedHandlerImpl that will do a \"forward\"\n\t\tAccessDeniedHandlerImpl adh = new AccessDeniedHandlerImpl();\n\t\tadh.setErrorPage(\"/error.jsp\");\n\t\t// Test\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint);\n\t\tfilter.setAccessDeniedHandler(adh);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, fc);\n\t\tassertThat(response.getStatus()).isEqualTo(403);\n\t\tassertThat(request.getAttribute(WebAttributes.ACCESS_DENIED_403))\n\t\t\t.isExactlyInstanceOf(AccessDeniedException.class);\n\t}\n\n\t@Test\n\tpublic void testLocalizedErrorMessages() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = get(\"/secure/page.html\").build();\n\t\t// Setup the FilterChain to thrown an access denied exception\n\t\tFilterChain fc = mockFilterChainWithException(new AccessDeniedException(\"\"));\n\t\t// Setup SecurityContextHolder, as filter needs to check if user is\n\t\t// anonymous\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new AnonymousAuthenticationToken(\"ignored\", \"ignored\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"IGNORED\")));\n\t\t// Test\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(\n\t\t\t\t(req, res, ae) -> res.sendError(403, ae.getMessage()));\n\t\tfilter.setAuthenticationTrustResolver(new AuthenticationTrustResolverImpl());\n\t\tassertThat(filter.getAuthenticationTrustResolver()).isNotNull();\n\t\tLocaleContextHolder.setDefaultLocale(Locale.GERMAN);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, fc);\n\t\tassertThat(response.getErrorMessage())\n\t\t\t.isEqualTo(\"Vollst\\u00e4ndige Authentifikation wird ben\\u00f6tigt um auf diese Resource zuzugreifen\");\n\t}\n\n\t@Test\n\tpublic void redirectedToLoginFormAndSessionShowsOriginalTargetWhenAuthenticationException() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = get().requestUri(\"/mycontext\", \"/secure/page.html\", null).build();\n\t\t// Setup the FilterChain to thrown an authentication failure exception\n\t\tFilterChain fc = mockFilterChainWithException(new BadCredentialsException(\"\"));\n\t\t// Test\n\t\tRequestCache requestCache = new HttpSessionRequestCache();\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint, requestCache);\n\t\tfilter.afterPropertiesSet();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, fc);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/login.jsp\");\n\t\tassertThat(getSavedRequestUrl(request)).isEqualTo(requestCache.getRequest(request, response).getRedirectUrl());\n\t}\n\n\t@Test\n\tpublic void redirectedToLoginFormAndSessionShowsOriginalTargetWithExoticPortWhenAuthenticationException()\n\t\t\tthrows Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = get(\"http://localhost:8080\")\n\t\t\t.requestUri(\"/mycontext\", \"/secure/page.html\", null)\n\t\t\t.build();\n\t\t// Setup the FilterChain to thrown an authentication failure exception\n\t\tFilterChain fc = mockFilterChainWithException(new BadCredentialsException(\"\"));\n\t\t// Test\n\t\tHttpSessionRequestCache requestCache = new HttpSessionRequestCache();\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint, requestCache);\n\t\tfilter.afterPropertiesSet();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, fc);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/login.jsp\");\n\t}\n\n\t@Test\n\tpublic void startupDetectsMissingAuthenticationEntryPoint() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ExceptionTranslationFilter(null));\n\t}\n\n\t@Test\n\tpublic void startupDetectsMissingRequestCache() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ExceptionTranslationFilter(this.mockEntryPoint, null));\n\t}\n\n\t@Test\n\tpublic void successfulAccessGrant() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = get(\"/secure/page.html\").build();\n\t\t// Test\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint);\n\t\tassertThat(filter.getAuthenticationEntryPoint()).isSameAs(this.mockEntryPoint);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, mock(FilterChain.class));\n\t}\n\n\t@Test\n\tpublic void thrownIOExceptionServletExceptionAndRuntimeExceptionsAreRethrown() throws Exception {\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint);\n\t\tfilter.afterPropertiesSet();\n\t\tException[] exceptions = { new IOException(), new ServletException(), new RuntimeException() };\n\t\tfor (Exception exception : exceptions) {\n\t\t\tFilterChain fc = mockFilterChainWithException(exception);\n\t\t\tassertThatExceptionOfType(Exception.class)\n\t\t\t\t.isThrownBy(() -> filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), fc))\n\t\t\t\t.isSameAs(exception);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void doFilterWhenResponseCommittedThenRethrowsException() {\n\t\tthis.mockEntryPoint = mock(AuthenticationEntryPoint.class);\n\t\tFilterChain chain = (request, response) -> {\n\t\t\tHttpServletResponse httpResponse = (HttpServletResponse) response;\n\t\t\thttpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);\n\t\t\tthrow new AccessDeniedException(\"Denied\");\n\t\t};\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint);\n\t\tassertThatExceptionOfType(ServletException.class).isThrownBy(() -> filter.doFilter(request, response, chain))\n\t\t\t.withCauseInstanceOf(AccessDeniedException.class);\n\t\tverifyNoMoreInteractions(this.mockEntryPoint);\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNullThenThrowsException() {\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setMessageSource(null));\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNotNullThenCanGet() {\n\t\tMessageSource source = mock(MessageSource.class);\n\t\tExceptionTranslationFilter filter = new ExceptionTranslationFilter(this.mockEntryPoint);\n\t\tfilter.setMessageSource(source);\n\t\tString code = \"code\";\n\t\tfilter.messages.getMessage(code);\n\t\tverify(source).getMessage(eq(code), any(), any());\n\t}\n\n\tprivate FilterChain mockFilterChainWithException(Exception exception) throws ServletException, IOException {\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\twillThrow(exception).given(fc).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t\treturn fc;\n\t}\n\n\tprivate AuthenticationEntryPoint mockEntryPoint = (request, response, authException) -> response\n\t\t.sendRedirect(request.getContextPath() + \"/login.jsp\");\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/HttpStatusAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.AccessDeniedException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n@ExtendWith(MockitoExtension.class)\npublic class HttpStatusAccessDeniedHandlerTests {\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\tprivate HttpStatus httpStatus = HttpStatus.FORBIDDEN;\n\n\tprivate HttpStatusAccessDeniedHandler handler = new HttpStatusAccessDeniedHandler(this.httpStatus);\n\n\tprivate AccessDeniedException exception = new AccessDeniedException(\"Forbidden\");\n\n\t@Test\n\tpublic void constructorHttpStatusWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new HttpStatusAccessDeniedHandler(null));\n\t}\n\n\t@Test\n\tpublic void commenceThenStatusSet() throws IOException, ServletException {\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.handler.handle(this.request, this.response, this.exception);\n\t\tassertThat(this.response.getStatus()).isEqualTo(this.httpStatus.value());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/NoOpAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.access.AccessDeniedException;\n\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nclass NoOpAccessDeniedHandlerTests {\n\n\tprivate final NoOpAccessDeniedHandler handler = new NoOpAccessDeniedHandler();\n\n\t@Test\n\tvoid handleWhenInvokedThenDoesNothing() throws Exception {\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tAccessDeniedException exception = mock(AccessDeniedException.class);\n\t\tthis.handler.handle(request, response, exception);\n\t\tverifyNoInteractions(request, response, exception);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/RequestMatcherDelegatingAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.util.LinkedHashMap;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Josh Cummings\n */\npublic class RequestMatcherDelegatingAccessDeniedHandlerTests {\n\n\tprivate RequestMatcherDelegatingAccessDeniedHandler delegator;\n\n\tprivate LinkedHashMap<RequestMatcher, AccessDeniedHandler> deniedHandlers;\n\n\tprivate AccessDeniedHandler accessDeniedHandler;\n\n\tprivate HttpServletRequest request;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.accessDeniedHandler = mock(AccessDeniedHandler.class);\n\t\tthis.deniedHandlers = new LinkedHashMap<>();\n\t\tthis.request = new MockHttpServletRequest();\n\t}\n\n\t@Test\n\tpublic void handleWhenNothingMatchesThenOnlyDefaultHandlerInvoked() throws Exception {\n\t\tAccessDeniedHandler handler = mock(AccessDeniedHandler.class);\n\t\tRequestMatcher matcher = mock(RequestMatcher.class);\n\t\tgiven(matcher.matches(this.request)).willReturn(false);\n\t\tthis.deniedHandlers.put(matcher, handler);\n\t\tthis.delegator = new RequestMatcherDelegatingAccessDeniedHandler(this.deniedHandlers, this.accessDeniedHandler);\n\t\tthis.delegator.handle(this.request, null, null);\n\t\tverify(this.accessDeniedHandler).handle(this.request, null, null);\n\t\tverify(handler, never()).handle(this.request, null, null);\n\t}\n\n\t@Test\n\tpublic void handleWhenFirstMatchesThenOnlyFirstInvoked() throws Exception {\n\t\tAccessDeniedHandler firstHandler = mock(AccessDeniedHandler.class);\n\t\tRequestMatcher firstMatcher = mock(RequestMatcher.class);\n\t\tAccessDeniedHandler secondHandler = mock(AccessDeniedHandler.class);\n\t\tRequestMatcher secondMatcher = mock(RequestMatcher.class);\n\t\tgiven(firstMatcher.matches(this.request)).willReturn(true);\n\t\tthis.deniedHandlers.put(firstMatcher, firstHandler);\n\t\tthis.deniedHandlers.put(secondMatcher, secondHandler);\n\t\tthis.delegator = new RequestMatcherDelegatingAccessDeniedHandler(this.deniedHandlers, this.accessDeniedHandler);\n\t\tthis.delegator.handle(this.request, null, null);\n\t\tverify(firstHandler).handle(this.request, null, null);\n\t\tverify(secondHandler, never()).handle(this.request, null, null);\n\t\tverify(this.accessDeniedHandler, never()).handle(this.request, null, null);\n\t\tverify(secondMatcher, never()).matches(this.request);\n\t}\n\n\t@Test\n\tpublic void handleWhenSecondMatchesThenOnlySecondInvoked() throws Exception {\n\t\tAccessDeniedHandler firstHandler = mock(AccessDeniedHandler.class);\n\t\tRequestMatcher firstMatcher = mock(RequestMatcher.class);\n\t\tAccessDeniedHandler secondHandler = mock(AccessDeniedHandler.class);\n\t\tRequestMatcher secondMatcher = mock(RequestMatcher.class);\n\t\tgiven(firstMatcher.matches(this.request)).willReturn(false);\n\t\tgiven(secondMatcher.matches(this.request)).willReturn(true);\n\t\tthis.deniedHandlers.put(firstMatcher, firstHandler);\n\t\tthis.deniedHandlers.put(secondMatcher, secondHandler);\n\t\tthis.delegator = new RequestMatcherDelegatingAccessDeniedHandler(this.deniedHandlers, this.accessDeniedHandler);\n\t\tthis.delegator.handle(this.request, null, null);\n\t\tverify(secondHandler).handle(this.request, null, null);\n\t\tverify(firstHandler, never()).handle(this.request, null, null);\n\t\tverify(this.accessDeniedHandler, never()).handle(this.request, null, null);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/RequestMatcherDelegatingWebInvocationPrivilegeEvaluatorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link RequestMatcherDelegatingWebInvocationPrivilegeEvaluator}\n *\n * @author Marcus Da Coregio\n */\nclass RequestMatcherDelegatingWebInvocationPrivilegeEvaluatorTests {\n\n\tprivate final RequestMatcher alwaysMatch = mock(RequestMatcher.class);\n\n\tprivate final RequestMatcher alwaysDeny = mock(RequestMatcher.class);\n\n\tprivate final String uri = \"/test\";\n\n\tprivate final Authentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tgiven(this.alwaysMatch.matches(any())).willReturn(true);\n\t\tgiven(this.alwaysDeny.matches(any())).willReturn(false);\n\t}\n\n\t@Test\n\tvoid isAllowedWhenDelegatesEmptyThenAllowed() {\n\t\tWebInvocationPrivilegeEvaluator delegating = evaluator();\n\t\tassertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();\n\t}\n\n\t@Test\n\tvoid isAllowedWhenNotMatchThenAllowed() {\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> notMatch = entry(this.alwaysDeny,\n\t\t\t\tTestWebInvocationPrivilegeEvaluators.alwaysAllow());\n\t\tWebInvocationPrivilegeEvaluator delegating = evaluator(notMatch);\n\t\tassertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();\n\t\tverify(notMatch.getRequestMatcher()).matches(any());\n\t}\n\n\t@Test\n\tvoid isAllowedWhenPrivilegeEvaluatorAllowThenAllowedTrue() {\n\t\tWebInvocationPrivilegeEvaluator delegating = evaluator(allow(this.alwaysMatch));\n\t\tassertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();\n\t}\n\n\t@Test\n\tvoid isAllowedWhenPrivilegeEvaluatorDenyThenAllowedFalse() {\n\t\tWebInvocationPrivilegeEvaluator delegating = evaluator(deny(this.alwaysMatch));\n\t\tassertThat(delegating.isAllowed(this.uri, this.authentication)).isFalse();\n\t}\n\n\t@Test\n\tvoid isAllowedWhenNotMatchThenMatchThenOnlySecondDelegateInvoked() {\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> notMatchDelegate = entry(this.alwaysDeny,\n\t\t\t\tTestWebInvocationPrivilegeEvaluators.alwaysAllow());\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> matchDelegate = entry(this.alwaysMatch,\n\t\t\t\tTestWebInvocationPrivilegeEvaluators.alwaysAllow());\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> spyNotMatchDelegate = spy(notMatchDelegate);\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> spyMatchDelegate = spy(matchDelegate);\n\n\t\tWebInvocationPrivilegeEvaluator delegating = evaluator(notMatchDelegate, spyMatchDelegate);\n\t\tassertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();\n\t\tverify(spyNotMatchDelegate.getRequestMatcher()).matches(any());\n\t\tverify(spyNotMatchDelegate, never()).getEntry();\n\t\tverify(spyMatchDelegate.getRequestMatcher()).matches(any());\n\t\tverify(spyMatchDelegate, times(2)).getEntry(); // 2 times, one for constructor and\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// other one in isAllowed\n\t}\n\n\t@Test\n\tvoid isAllowedWhenDelegatePrivilegeEvaluatorsEmptyThenAllowedTrue() {\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = entry(this.alwaysMatch);\n\t\tWebInvocationPrivilegeEvaluator delegating = evaluator(delegate);\n\t\tassertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();\n\t}\n\n\t@Test\n\tvoid isAllowedWhenFirstDelegateDenyThenDoNotInvokeOthers() {\n\t\tWebInvocationPrivilegeEvaluator deny = TestWebInvocationPrivilegeEvaluators.alwaysDeny();\n\t\tWebInvocationPrivilegeEvaluator allow = TestWebInvocationPrivilegeEvaluators.alwaysAllow();\n\t\tWebInvocationPrivilegeEvaluator spyDeny = spy(deny);\n\t\tWebInvocationPrivilegeEvaluator spyAllow = spy(allow);\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = entry(this.alwaysMatch, spyDeny,\n\t\t\t\tspyAllow);\n\n\t\tWebInvocationPrivilegeEvaluator delegating = evaluator(delegate);\n\n\t\tassertThat(delegating.isAllowed(this.uri, this.authentication)).isFalse();\n\t\tverify(spyDeny).isAllowed(any(), any());\n\t\tverifyNoInteractions(spyAllow);\n\t}\n\n\t@Test\n\tvoid isAllowedWhenDifferentArgumentsThenCallSpecificIsAllowedInDelegate() {\n\t\tWebInvocationPrivilegeEvaluator deny = TestWebInvocationPrivilegeEvaluators.alwaysDeny();\n\t\tWebInvocationPrivilegeEvaluator spyDeny = spy(deny);\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = entry(this.alwaysMatch, spyDeny);\n\n\t\tWebInvocationPrivilegeEvaluator delegating = evaluator(delegate);\n\n\t\tassertThat(delegating.isAllowed(this.uri, this.authentication)).isFalse();\n\t\tassertThat(delegating.isAllowed(\"/cp\", this.uri, \"GET\", this.authentication)).isFalse();\n\t\tverify(spyDeny).isAllowed(any(), any());\n\t\tverify(spyDeny).isAllowed(any(), any(), any(), any());\n\t\tverifyNoMoreInteractions(spyDeny);\n\t}\n\n\t@Test\n\tvoid isAllowedWhenServletContextIsSetThenPassedFilterInvocationHttpServletRequestHasServletContext() {\n\t\tAuthentication token = new TestingAuthenticationToken(\"test\", \"Password\", \"MOCK_INDEX\");\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tArgumentCaptor<HttpServletRequest> argumentCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);\n\t\tRequestMatcher requestMatcher = mock(RequestMatcher.class);\n\t\tWebInvocationPrivilegeEvaluator wipe = mock(WebInvocationPrivilegeEvaluator.class);\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = entry(requestMatcher, wipe);\n\t\tRequestMatcherDelegatingWebInvocationPrivilegeEvaluator requestMatcherWipe = evaluator(delegate);\n\t\trequestMatcherWipe.setServletContext(servletContext);\n\t\trequestMatcherWipe.isAllowed(\"/foo/index.jsp\", token);\n\t\tverify(requestMatcher).matches(argumentCaptor.capture());\n\t\tassertThat(argumentCaptor.getValue().getServletContext()).isNotNull();\n\t}\n\n\t@Test\n\tvoid constructorWhenPrivilegeEvaluatorsNullThenException() {\n\t\tRequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> entry = new RequestMatcherEntry<>(this.alwaysMatch,\n\t\t\t\tnull);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> evaluator(entry))\n\t\t\t.withMessageContaining(\"webInvocationPrivilegeEvaluators cannot be null\");\n\t}\n\n\t@Test\n\tvoid constructorWhenRequestMatcherNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> evaluator(deny(null)))\n\t\t\t.withMessageContaining(\"requestMatcher cannot be null\");\n\t}\n\n\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\tprivate RequestMatcherDelegatingWebInvocationPrivilegeEvaluator evaluator(RequestMatcherEntry... entries) {\n\t\treturn new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(List.of(entries));\n\t}\n\n\tprivate RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> allow(RequestMatcher requestMatcher) {\n\t\treturn entry(requestMatcher, TestWebInvocationPrivilegeEvaluators.alwaysAllow());\n\t}\n\n\tprivate RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> deny(RequestMatcher requestMatcher) {\n\t\treturn entry(requestMatcher, TestWebInvocationPrivilegeEvaluators.alwaysDeny());\n\t}\n\n\tprivate RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> entry(RequestMatcher requestMatcher,\n\t\t\tWebInvocationPrivilegeEvaluator... evaluators) {\n\t\treturn new RequestMatcherEntry<>(requestMatcher, List.of(evaluators));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/TestWebInvocationPrivilegeEvaluators.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access;\n\nimport org.springframework.security.core.Authentication;\n\npublic final class TestWebInvocationPrivilegeEvaluators {\n\n\tprivate static final AlwaysAllowWebInvocationPrivilegeEvaluator ALWAYS_ALLOW = new AlwaysAllowWebInvocationPrivilegeEvaluator();\n\n\tprivate static final AlwaysDenyWebInvocationPrivilegeEvaluator ALWAYS_DENY = new AlwaysDenyWebInvocationPrivilegeEvaluator();\n\n\tprivate TestWebInvocationPrivilegeEvaluators() {\n\t}\n\n\tpublic static WebInvocationPrivilegeEvaluator alwaysAllow() {\n\t\treturn ALWAYS_ALLOW;\n\t}\n\n\tpublic static WebInvocationPrivilegeEvaluator alwaysDeny() {\n\t\treturn ALWAYS_DENY;\n\t}\n\n\tprivate static class AlwaysAllowWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {\n\n\t\t@Override\n\t\tpublic boolean isAllowed(String uri, Authentication authentication) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\tprivate static class AlwaysDenyWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {\n\n\t\t@Override\n\t\tpublic boolean isAllowed(String uri, Authentication authentication) {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/expression/AbstractVariableEvaluationContextPostProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.spel.support.StandardEvaluationContext;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.FilterInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * @author Rob Winch\n *\n */\npublic class AbstractVariableEvaluationContextPostProcessorTests {\n\n\tstatic final String KEY = \"a\";\n\tstatic final String VALUE = \"b\";\n\n\tVariableEvaluationContextPostProcessor processor;\n\n\tFilterInvocation invocation;\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tEvaluationContext context;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.processor = new VariableEvaluationContextPostProcessor();\n\t\tthis.request = get(\"/\").build();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.invocation = new FilterInvocation(this.request, this.response, new MockFilterChain());\n\t\tthis.context = new StandardEvaluationContext();\n\t}\n\n\t@Test\n\tpublic void extractVariables() {\n\t\tthis.context = this.processor.postProcess(this.context, this.invocation);\n\t\tassertThat(this.context.lookupVariable(KEY)).isEqualTo(VALUE);\n\t}\n\n\t@Test\n\tpublic void extractVariablesOnlyUsedOnce() {\n\t\tthis.context = this.processor.postProcess(this.context, this.invocation);\n\t\tassertThat(this.context.lookupVariable(KEY)).isEqualTo(VALUE);\n\t\tthis.processor.results = Collections.emptyMap();\n\t\tassertThat(this.context.lookupVariable(KEY)).isEqualTo(VALUE);\n\t}\n\n\tstatic class VariableEvaluationContextPostProcessor extends AbstractVariableEvaluationContextPostProcessor {\n\n\t\tMap<String, String> results = Collections.singletonMap(KEY, VALUE);\n\n\t\t@Override\n\t\tprotected Map<String, String> extractVariables(HttpServletRequest request) {\n\t\t\treturn this.results;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.function.Supplier;\n\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.context.support.StaticApplicationContext;\nimport org.springframework.expression.EvaluationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.expression.TypedValue;\nimport org.springframework.security.access.expression.SecurityExpressionRoot;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n@ExtendWith(MockitoExtension.class)\npublic class DefaultHttpSecurityExpressionHandlerTests {\n\n\t@Mock\n\tprivate AuthenticationTrustResolver trustResolver;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\t@Mock\n\tprivate RequestAuthorizationContext context;\n\n\tprivate DefaultHttpSecurityExpressionHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.handler = new DefaultHttpSecurityExpressionHandler();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void expressionPropertiesAreResolvedAgainstAppContextBeans() {\n\t\tStaticApplicationContext appContext = new StaticApplicationContext();\n\t\tRootBeanDefinition bean = new RootBeanDefinition(SimpleGrantedAuthority.class);\n\t\tbean.getConstructorArgumentValues().addGenericArgumentValue(\"ROLE_A\");\n\t\tappContext.registerBeanDefinition(\"role\", bean);\n\t\tthis.handler.setApplicationContext(appContext);\n\t\tEvaluationContext ctx = this.handler.createEvaluationContext(mock(Authentication.class),\n\t\t\t\tmock(RequestAuthorizationContext.class));\n\t\tExpressionParser parser = this.handler.getExpressionParser();\n\t\tassertThat(parser.parseExpression(\"@role.getAuthority() == 'ROLE_A'\").getValue(ctx, Boolean.class)).isTrue();\n\t\tassertThat(parser.parseExpression(\"@role.authority == 'ROLE_A'\").getValue(ctx, Boolean.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void setTrustResolverNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setTrustResolver(null));\n\t}\n\n\t@Test\n\tpublic void createEvaluationContextCustomTrustResolver() {\n\t\tthis.handler.setTrustResolver(this.trustResolver);\n\t\tExpression expression = this.handler.getExpressionParser().parseExpression(\"anonymous\");\n\t\tEvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.context);\n\t\tassertThat(expression.getValue(context, Boolean.class)).isFalse();\n\t\tverify(this.trustResolver).isAnonymous(this.authentication);\n\t}\n\n\t@Test\n\tpublic void createEvaluationContextSupplierAuthentication() {\n\t\tSupplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);\n\t\tgiven(mockAuthenticationSupplier.get()).willReturn(this.authentication);\n\t\tEvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier, this.context);\n\t\tverifyNoInteractions(mockAuthenticationSupplier);\n\t\tassertThat(context.getRootObject()).extracting(TypedValue::getValue)\n\t\t\t.asInstanceOf(InstanceOfAssertFactories.type(WebSecurityExpressionRoot.class))\n\t\t\t.extracting(SecurityExpressionRoot::getAuthentication)\n\t\t\t.isEqualTo(this.authentication);\n\t\tverify(mockAuthenticationSupplier).get();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/expression/DelegatingEvaluationContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.ConstructorResolver;\nimport org.springframework.expression.MethodResolver;\nimport org.springframework.expression.OperatorOverloader;\nimport org.springframework.expression.PropertyAccessor;\nimport org.springframework.expression.TypeComparator;\nimport org.springframework.expression.TypeConverter;\nimport org.springframework.expression.TypeLocator;\nimport org.springframework.expression.TypedValue;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingEvaluationContextTests {\n\n\t@Mock\n\tDelegatingEvaluationContext delegate;\n\n\t@InjectMocks\n\tDelegatingEvaluationContext context;\n\n\t@Test\n\tpublic void getRootObject() {\n\t\tTypedValue expected = mock(TypedValue.class);\n\t\tgiven(this.delegate.getRootObject()).willReturn(expected);\n\t\tassertThat(this.context.getRootObject()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void getConstructorResolvers() {\n\t\tList<ConstructorResolver> expected = new ArrayList<>();\n\t\tgiven(this.delegate.getConstructorResolvers()).willReturn(expected);\n\t\tassertThat(this.context.getConstructorResolvers()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void getMethodResolvers() {\n\t\tList<MethodResolver> expected = new ArrayList<>();\n\t\tgiven(this.delegate.getMethodResolvers()).willReturn(expected);\n\t\tassertThat(this.context.getMethodResolvers()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void getPropertyAccessors() {\n\t\tList<PropertyAccessor> expected = new ArrayList<>();\n\t\tgiven(this.delegate.getPropertyAccessors()).willReturn(expected);\n\t\tassertThat(this.context.getPropertyAccessors()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void getTypeLocator() {\n\t\tTypeLocator expected = mock(TypeLocator.class);\n\t\tgiven(this.delegate.getTypeLocator()).willReturn(expected);\n\t\tassertThat(this.context.getTypeLocator()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void getTypeConverter() {\n\t\tTypeConverter expected = mock(TypeConverter.class);\n\t\tgiven(this.delegate.getTypeConverter()).willReturn(expected);\n\t\tassertThat(this.context.getTypeConverter()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void getTypeComparator() {\n\t\tTypeComparator expected = mock(TypeComparator.class);\n\t\tgiven(this.delegate.getTypeComparator()).willReturn(expected);\n\t\tassertThat(this.context.getTypeComparator()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void getOperatorOverloader() {\n\t\tOperatorOverloader expected = mock(OperatorOverloader.class);\n\t\tgiven(this.delegate.getOperatorOverloader()).willReturn(expected);\n\t\tassertThat(this.context.getOperatorOverloader()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void getBeanResolver() {\n\t\tBeanResolver expected = mock(BeanResolver.class);\n\t\tgiven(this.delegate.getBeanResolver()).willReturn(expected);\n\t\tassertThat(this.context.getBeanResolver()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void setVariable() {\n\t\tString name = \"name\";\n\t\tString value = \"value\";\n\t\tthis.context.setVariable(name, value);\n\t\tverify(this.delegate).setVariable(name, value);\n\t}\n\n\t@Test\n\tpublic void lookupVariable() {\n\t\tString name = \"name\";\n\t\tString expected = \"expected\";\n\t\tgiven(this.delegate.lookupVariable(name)).willReturn(expected);\n\t\tassertThat(this.context.lookupVariable(name)).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/expression/WebExpressionAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.support.GenericApplicationContext;\nimport org.springframework.expression.Expression;\nimport org.springframework.expression.ExpressionParser;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.web.access.intercept.RequestAuthorizationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link WebExpressionAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n */\nclass WebExpressionAuthorizationManagerTests {\n\n\t@Test\n\tvoid instantiateWhenExpressionStringNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new WebExpressionAuthorizationManager(null))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionStringEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new WebExpressionAuthorizationManager(\"\"))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionStringBlankThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new WebExpressionAuthorizationManager(\" \"))\n\t\t\t.withMessage(\"expressionString cannot be empty\");\n\t}\n\n\t@Test\n\tvoid instantiateWhenExpressionHandlerNotSetThenDefaultUsed() {\n\t\tWebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tassertThat(manager).extracting(\"expressionHandler\").isInstanceOf(DefaultHttpSecurityExpressionHandler.class);\n\t}\n\n\t@Test\n\tvoid setExpressionHandlerWhenNullThenIllegalArgumentException() {\n\t\tWebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null))\n\t\t\t.withMessage(\"expressionHandler cannot be null\");\n\t}\n\n\t@Test\n\tvoid setExpressionHandlerWhenNotNullThenVerifyExpressionHandler() {\n\t\tString expressionString = \"hasRole('ADMIN')\";\n\t\tWebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager(expressionString);\n\t\tDefaultHttpSecurityExpressionHandler expressionHandler = new DefaultHttpSecurityExpressionHandler();\n\t\tExpressionParser mockExpressionParser = mock(ExpressionParser.class);\n\t\tExpression mockExpression = mock(Expression.class);\n\t\tgiven(mockExpressionParser.parseExpression(expressionString)).willReturn(mockExpression);\n\t\texpressionHandler.setExpressionParser(mockExpressionParser);\n\t\tmanager.setExpressionHandler(expressionHandler);\n\t\tassertThat(manager).extracting(\"expressionHandler\").isEqualTo(expressionHandler);\n\t\tassertThat(manager).extracting(\"expression\").isEqualTo(mockExpression);\n\t\tverify(mockExpressionParser).parseExpression(expressionString);\n\t}\n\n\t@Test\n\tvoid checkWhenExpressionHasRoleAdminConfiguredAndRoleAdminThenGrantedDecision() {\n\t\tWebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin,\n\t\t\t\tnew RequestAuthorizationContext(new MockHttpServletRequest()));\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid checkWhenExpressionHasRoleAdminConfiguredAndRoleUserThenDeniedDecision() {\n\t\tWebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager(\"hasRole('ADMIN')\");\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser,\n\t\t\t\tnew RequestAuthorizationContext(new MockHttpServletRequest()));\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tvoid authorizeWhenDefaultsThenEvaluatesExpressionsReferencingBeans() {\n\t\tGenericApplicationContext context = new GenericApplicationContext();\n\t\tcontext.registerBean(\"bean\", WebExpressionAuthorizationManagerTests.class, () -> this);\n\t\tcontext.refresh();\n\t\tWebExpressionAuthorizationManager.Builder builder = WebExpressionAuthorizationManager.withDefaults();\n\t\tbuilder.setApplicationContext(context);\n\t\tWebExpressionAuthorizationManager manager = builder\n\t\t\t.expression(\"@bean.class.simpleName.startsWith('WebExpression')\");\n\t\tAuthorizationResult result = manager.authorize(TestAuthentication::authenticatedUser,\n\t\t\t\tnew RequestAuthorizationContext(new MockHttpServletRequest()));\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenDefaultsAsBeanThenEvaluatesExpressionsReferencingBeans() {\n\t\tGenericApplicationContext context = new GenericApplicationContext();\n\t\tcontext.registerBean(\"bean\", WebExpressionAuthorizationManagerTests.class, () -> this);\n\t\tcontext.registerBean(\"builder\", WebExpressionAuthorizationManager.Builder.class,\n\t\t\t\tWebExpressionAuthorizationManager::withDefaults);\n\t\tcontext.refresh();\n\t\tWebExpressionAuthorizationManager.Builder builder = context\n\t\t\t.getBean(WebExpressionAuthorizationManager.Builder.class);\n\t\tWebExpressionAuthorizationManager manager = builder\n\t\t\t.expression(\"@bean.class.simpleName.startsWith('WebExpression')\");\n\t\tAuthorizationResult result = manager.authorize(TestAuthentication::authenticatedUser,\n\t\t\t\tnew RequestAuthorizationContext(new MockHttpServletRequest()));\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tvoid authorizeWhenExpressionHandlerHasBeanProviderThenEvaluatesExpressionsReferencingBeans() {\n\t\tGenericApplicationContext context = new GenericApplicationContext();\n\t\tcontext.registerBean(\"bean\", WebExpressionAuthorizationManagerTests.class, () -> this);\n\t\tcontext.refresh();\n\t\tDefaultHttpSecurityExpressionHandler expressionHandler = new DefaultHttpSecurityExpressionHandler();\n\t\texpressionHandler.setApplicationContext(context);\n\t\tWebExpressionAuthorizationManager manager = WebExpressionAuthorizationManager\n\t\t\t.withExpressionHandler(expressionHandler)\n\t\t\t.expression(\"@bean.class.simpleName.startsWith('WebExpression')\");\n\t\tAuthorizationResult result = manager.authorize(TestAuthentication::authenticatedUser,\n\t\t\t\tnew RequestAuthorizationContext(new MockHttpServletRequest()));\n\t\tassertThat(result.isGranted()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/expression/WebSecurityExpressionRootTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.expression;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.FilterInvocation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link WebSecurityExpressionRoot}.\n *\n * @author Luke Taylor\n * @since 3.0\n */\npublic class WebSecurityExpressionRootTests {\n\n\t@Test\n\tpublic void ipAddressMatchesForEqualIpAddresses() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/test\");\n\t\t// IPv4\n\t\trequest.setRemoteAddr(\"192.168.1.1\");\n\t\tWebSecurityExpressionRoot root = new WebSecurityExpressionRoot(mock(Authentication.class),\n\t\t\t\tnew FilterInvocation(request, mock(HttpServletResponse.class), mock(FilterChain.class)));\n\t\tassertThat(root.hasIpAddress(\"192.168.1.1\")).isTrue();\n\t\t// IPv6 Address\n\t\trequest.setRemoteAddr(\"fa:db8:85a3::8a2e:370:7334\");\n\t\tassertThat(root.hasIpAddress(\"fa:db8:85a3::8a2e:370:7334\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void addressesInIpRangeMatch() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/test\");\n\t\tWebSecurityExpressionRoot root = new WebSecurityExpressionRoot(mock(Authentication.class),\n\t\t\t\tnew FilterInvocation(request, mock(HttpServletResponse.class), mock(FilterChain.class)));\n\t\tfor (int i = 0; i < 255; i++) {\n\t\t\trequest.setRemoteAddr(\"192.168.1.\" + i);\n\t\t\tassertThat(root.hasIpAddress(\"192.168.1.0/24\")).isTrue();\n\t\t}\n\t\trequest.setRemoteAddr(\"192.168.1.127\");\n\t\t// 25 = FF FF FF 80\n\t\tassertThat(root.hasIpAddress(\"192.168.1.0/25\")).isTrue();\n\t\t// encroach on the mask\n\t\trequest.setRemoteAddr(\"192.168.1.128\");\n\t\tassertThat(root.hasIpAddress(\"192.168.1.0/25\")).isFalse();\n\t\trequest.setRemoteAddr(\"192.168.1.255\");\n\t\tassertThat(root.hasIpAddress(\"192.168.1.128/25\")).isTrue();\n\t\tassertThat(root.hasIpAddress(\"192.168.1.192/26\")).isTrue();\n\t\tassertThat(root.hasIpAddress(\"192.168.1.224/27\")).isTrue();\n\t\tassertThat(root.hasIpAddress(\"192.168.1.240/27\")).isTrue();\n\t\tassertThat(root.hasIpAddress(\"192.168.1.255/32\")).isTrue();\n\t\trequest.setRemoteAddr(\"202.24.199.127\");\n\t\tassertThat(root.hasIpAddress(\"202.24.0.0/14\")).isTrue();\n\t\trequest.setRemoteAddr(\"202.25.179.135\");\n\t\tassertThat(root.hasIpAddress(\"202.24.0.0/14\")).isTrue();\n\t\trequest.setRemoteAddr(\"202.26.179.135\");\n\t\tassertThat(root.hasIpAddress(\"202.24.0.0/14\")).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport java.io.IOException;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationEventPublisher;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.web.util.WebUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link AuthorizationFilter}.\n *\n * @author Evgeniy Cheban\n */\npublic class AuthorizationFilterTests {\n\n\tprivate static final String ALREADY_FILTERED_ATTRIBUTE_NAME = \"org.springframework.security.web.access.intercept.AuthorizationFilter.APPLIED\";\n\n\tprivate AuthorizationFilter filter;\n\n\tprivate AuthorizationManager<HttpServletRequest> authorizationManager;\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate final MockHttpServletResponse response = new MockHttpServletResponse();\n\n\tprivate final FilterChain chain = new MockFilterChain();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authorizationManager = mock(AuthorizationManager.class);\n\t\tthis.filter = new AuthorizationFilter(this.authorizationManager);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Exception {\n\t\tAuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);\n\t\tgiven(mockAuthorizationManager.authorize(any(Supplier.class), any(HttpServletRequest.class)))\n\t\t\t.willReturn(new AuthorizationDecision(true));\n\t\tAuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);\n\t\tTestingAuthenticationToken authenticationToken = new TestingAuthenticationToken(\"user\", \"password\");\n\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(authenticationToken));\n\t\tfilter.setSecurityContextHolderStrategy(strategy);\n\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest(null, \"/path\");\n\t\tMockHttpServletResponse mockResponse = new MockHttpServletResponse();\n\t\tFilterChain mockFilterChain = mock(FilterChain.class);\n\n\t\tfilter.doFilter(mockRequest, mockResponse, mockFilterChain);\n\n\t\tArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);\n\t\tverify(mockAuthorizationManager).authorize(authenticationCaptor.capture(), eq(mockRequest));\n\t\tSupplier<Authentication> authentication = authenticationCaptor.getValue();\n\t\tassertThat(authentication.get()).isEqualTo(authenticationToken);\n\n\t\tverify(mockFilterChain).doFilter(mockRequest, mockResponse);\n\t\tverify(strategy).getContext();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthorizationManagerVerifyThrowsAccessDeniedExceptionThenStopFilterChain() {\n\t\tAuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);\n\t\tAuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);\n\t\tTestingAuthenticationToken authenticationToken = new TestingAuthenticationToken(\"user\", \"password\");\n\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(authenticationToken);\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest(null, \"/path\");\n\t\tMockHttpServletResponse mockResponse = new MockHttpServletResponse();\n\t\tFilterChain mockFilterChain = mock(FilterChain.class);\n\n\t\twillThrow(new AccessDeniedException(\"Access Denied\")).given(mockAuthorizationManager)\n\t\t\t.authorize(any(), eq(mockRequest));\n\n\t\tassertThatExceptionOfType(AccessDeniedException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(mockRequest, mockResponse, mockFilterChain))\n\t\t\t.withMessage(\"Access Denied\");\n\n\t\tArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);\n\t\tverify(mockAuthorizationManager).authorize(authenticationCaptor.capture(), eq(mockRequest));\n\t\tSupplier<Authentication> authentication = authenticationCaptor.getValue();\n\t\tassertThat(authentication.get()).isEqualTo(authenticationToken);\n\n\t\tverifyNoInteractions(mockFilterChain);\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticationNullThenAuthenticationCredentialsNotFoundException() {\n\t\tAuthorizationFilter filter = new AuthorizationFilter(AuthenticatedAuthorizationManager.authenticated());\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest(null, \"/path\");\n\t\tMockHttpServletResponse mockResponse = new MockHttpServletResponse();\n\t\tFilterChain mockFilterChain = mock(FilterChain.class);\n\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(mockRequest, mockResponse, mockFilterChain))\n\t\t\t.withMessage(\"An Authentication object was not found in the SecurityContext\");\n\n\t\tverifyNoInteractions(mockFilterChain);\n\t}\n\n\t@Test\n\tpublic void getAuthorizationManager() {\n\t\tAuthorizationManager<HttpServletRequest> authorizationManager = mock(AuthorizationManager.class);\n\t\tAuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);\n\t\tassertThat(authorizationFilter.getAuthorizationManager()).isSameAs(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {\n\t\tAuthorizationManager<HttpServletRequest> authorizationManager = mock(AuthorizationManager.class);\n\t\tAuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> authorizationFilter.setAuthorizationEventPublisher(null))\n\t\t\t.withMessage(\"eventPublisher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthorizationEventPublisherThenUses() throws Exception {\n\t\tAuthorizationFilter authorizationFilter = new AuthorizationFilter(\n\t\t\t\tAuthenticatedAuthorizationManager.authenticated());\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest(null, \"/path\");\n\t\tMockHttpServletResponse mockResponse = new MockHttpServletResponse();\n\t\tFilterChain mockFilterChain = mock(FilterChain.class);\n\n\t\tSecurityContext securityContext = new SecurityContextImpl();\n\t\tsecurityContext.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tSecurityContextHolder.setContext(securityContext);\n\n\t\tAuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);\n\t\tauthorizationFilter.setAuthorizationEventPublisher(eventPublisher);\n\t\tauthorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain);\n\t\tverify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class),\n\t\t\t\tany(AuthorizationDecision.class));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenErrorThenDoFilter() throws Exception {\n\t\tAuthorizationManager<HttpServletRequest> authorizationManager = mock(AuthorizationManager.class);\n\t\tAuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest(null, \"/path\");\n\t\tmockRequest.setDispatcherType(DispatcherType.ERROR);\n\t\tmockRequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\");\n\t\tMockHttpServletResponse mockResponse = new MockHttpServletResponse();\n\t\tFilterChain mockFilterChain = mock(FilterChain.class);\n\n\t\tauthorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain);\n\t\tverify(authorizationManager).authorize(any(Supplier.class), eq(mockRequest));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenErrorAndShouldFilterAllDispatcherTypesFalseThenDoNotFilter() throws Exception {\n\t\tAuthorizationManager<HttpServletRequest> authorizationManager = mock(AuthorizationManager.class);\n\t\tAuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);\n\t\tauthorizationFilter.setObserveOncePerRequest(true);\n\t\tauthorizationFilter.setFilterErrorDispatch(false);\n\t\tauthorizationFilter.setFilterAsyncDispatch(false);\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest(null, \"/path\");\n\t\tmockRequest.setDispatcherType(DispatcherType.ERROR);\n\t\tmockRequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\");\n\t\tMockHttpServletResponse mockResponse = new MockHttpServletResponse();\n\t\tFilterChain mockFilterChain = mock(FilterChain.class);\n\n\t\tauthorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain);\n\t\tverifyNoInteractions(authorizationManager);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenObserveOncePerRequestTrueAndIsAppliedThenNotInvoked() throws ServletException, IOException {\n\t\tsetIsAppliedTrue();\n\t\tthis.filter.setObserveOncePerRequest(true);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverifyNoInteractions(this.authorizationManager);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenObserveOncePerRequestTrueAndNotAppliedThenInvoked() throws ServletException, IOException {\n\t\tthis.filter.setObserveOncePerRequest(true);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenObserveOncePerRequestFalseAndIsAppliedThenInvoked() throws ServletException, IOException {\n\t\tsetIsAppliedTrue();\n\t\tthis.filter.setObserveOncePerRequest(false);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenObserveOncePerRequestFalseAndNotAppliedThenInvoked() throws ServletException, IOException {\n\t\tthis.filter.setObserveOncePerRequest(false);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenFilterErrorDispatchFalseAndIsErrorThenNotInvoked() throws ServletException, IOException {\n\t\tthis.request.setDispatcherType(DispatcherType.ERROR);\n\t\tthis.filter.setFilterErrorDispatch(false);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverifyNoInteractions(this.authorizationManager);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenFilterErrorDispatchTrueAndIsErrorThenInvoked() throws ServletException, IOException {\n\t\tthis.request.setDispatcherType(DispatcherType.ERROR);\n\t\tthis.filter.setFilterErrorDispatch(true);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenFilterThenSetAlreadyFilteredAttribute() throws ServletException, IOException {\n\t\tthis.request = mock(MockHttpServletRequest.class);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.request).setAttribute(ALREADY_FILTERED_ATTRIBUTE_NAME, Boolean.TRUE);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenFilterThenRemoveAlreadyFilteredAttribute() throws ServletException, IOException {\n\t\tthis.request = spy(MockHttpServletRequest.class);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.request).setAttribute(ALREADY_FILTERED_ATTRIBUTE_NAME, Boolean.TRUE);\n\t\tassertThat(this.request.getAttribute(ALREADY_FILTERED_ATTRIBUTE_NAME)).isNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenFilterAsyncDispatchTrueAndIsAsyncThenInvoked() throws ServletException, IOException {\n\t\tthis.request.setDispatcherType(DispatcherType.ASYNC);\n\t\tthis.filter.setFilterAsyncDispatch(true);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverify(this.authorizationManager).authorize(any(), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenFilterAsyncDispatchFalseAndIsAsyncThenNotInvoked() throws ServletException, IOException {\n\t\tthis.request.setDispatcherType(DispatcherType.ASYNC);\n\t\tthis.filter.setFilterAsyncDispatch(false);\n\t\tthis.filter.doFilter(this.request, this.response, this.chain);\n\t\tverifyNoInteractions(this.authorizationManager);\n\t}\n\n\t@Test\n\tpublic void filterWhenFilterErrorDispatchDefaultThenTrue() {\n\t\tBoolean filterErrorDispatch = (Boolean) ReflectionTestUtils.getField(this.filter, \"filterErrorDispatch\");\n\t\tassertThat(filterErrorDispatch).isTrue();\n\t}\n\n\t@Test\n\tpublic void filterWhenFilterAsyncDispatchDefaultThenTrue() {\n\t\tBoolean filterAsyncDispatch = (Boolean) ReflectionTestUtils.getField(this.filter, \"filterAsyncDispatch\");\n\t\tassertThat(filterAsyncDispatch).isTrue();\n\t}\n\n\t@Test\n\tpublic void filterWhenObserveOncePerRequestDefaultThenFalse() {\n\t\tassertThat(this.filter.isObserveOncePerRequest()).isFalse();\n\t}\n\n\tprivate void setIsAppliedTrue() {\n\t\tthis.request.setAttribute(ALREADY_FILTERED_ATTRIBUTE_NAME, Boolean.TRUE);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/intercept/RequestKeyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Luke Taylor\n *\n */\npublic class RequestKeyTests {\n\n\t@Test\n\tpublic void equalsWorksWithNullHttpMethod() {\n\t\tRequestKey key1 = new RequestKey(\"/someurl\");\n\t\tRequestKey key2 = new RequestKey(\"/someurl\");\n\t\tassertThat(key2).isEqualTo(key1);\n\t\tkey1 = new RequestKey(\"/someurl\", \"GET\");\n\t\tassertThat(key1.equals(key2)).isFalse();\n\t\tassertThat(key2.equals(key1)).isFalse();\n\t}\n\n\t@Test\n\tpublic void keysWithSameUrlAndHttpMethodAreEqual() {\n\t\tRequestKey key1 = new RequestKey(\"/someurl\", \"GET\");\n\t\tRequestKey key2 = new RequestKey(\"/someurl\", \"GET\");\n\t\tassertThat(key2).isEqualTo(key1);\n\t}\n\n\t@Test\n\tpublic void keysWithSameUrlAndDifferentHttpMethodAreNotEqual() {\n\t\tRequestKey key1 = new RequestKey(\"/someurl\", \"GET\");\n\t\tRequestKey key2 = new RequestKey(\"/someurl\", \"POST\");\n\t\tassertThat(key1.equals(key2)).isFalse();\n\t\tassertThat(key2.equals(key1)).isFalse();\n\t}\n\n\t@Test\n\tpublic void keysWithDifferentUrlsAreNotEquals() {\n\t\tRequestKey key1 = new RequestKey(\"/someurl\", \"GET\");\n\t\tRequestKey key2 = new RequestKey(\"/anotherurl\", \"GET\");\n\t\tassertThat(key1.equals(key2)).isFalse();\n\t\tassertThat(key2.equals(key1)).isFalse();\n\t}\n\n\t/**\n\t */\n\t@Test\n\tpublic void keysWithNullUrlFailsAssertion() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RequestKey(null, null))\n\t\t\t.withMessage(\"url cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.access.intercept;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorityAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.authorization.SingleResultAuthorizationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests for {@link RequestMatcherDelegatingAuthorizationManager}.\n *\n * @author Evgeniy Cheban\n * @author Parikshit Dutta\n */\npublic class RequestMatcherDelegatingAuthorizationManagerTests {\n\n\t@Test\n\tpublic void buildWhenMappingsEmptyThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder().build())\n\t\t\t.withMessage(\"mappings cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void addWhenMatcherNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t\t.add(null, SingleResultAuthorizationManager.permitAll())\n\t\t\t\t.build())\n\t\t\t.withMessage(\"matcher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void addWhenManagerNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> RequestMatcherDelegatingAuthorizationManager.builder().add(pathPattern(\"/grant\"), null).build())\n\t\t\t.withMessage(\"manager cannot be null\");\n\t}\n\n\t@Test\n\tpublic void checkWhenMultipleMappingsConfiguredThenDelegatesMatchingManager() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.add(pathPattern(null, \"/grant\"), SingleResultAuthorizationManager.permitAll())\n\t\t\t.add(pathPattern(null, \"/deny\"), SingleResultAuthorizationManager.denyAll())\n\t\t\t.build();\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\n\t\tAuthorizationResult grant = manager.authorize(authentication, new MockHttpServletRequest(null, \"/grant\"));\n\t\tassertThat(grant).isNotNull();\n\t\tassertThat(grant.isGranted()).isTrue();\n\n\t\tAuthorizationResult deny = manager.authorize(authentication, new MockHttpServletRequest(null, \"/deny\"));\n\t\tassertThat(deny).isNotNull();\n\t\tassertThat(deny.isGranted()).isFalse();\n\n\t\tAuthorizationResult defaultDeny = manager.authorize(authentication,\n\t\t\t\tnew MockHttpServletRequest(null, \"/unmapped\"));\n\t\tassertThat(defaultDeny).isNotNull();\n\t\tassertThat(defaultDeny.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenMultipleMappingsConfiguredWithConsumerThenDelegatesMatchingManager() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.mappings((m) -> {\n\t\t\t\tm.add(new RequestMatcherEntry<>(pathPattern(\"/grant\"), SingleResultAuthorizationManager.permitAll()));\n\t\t\t\tm.add(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE,\n\t\t\t\t\t\tAuthorityAuthorizationManager.hasRole(\"ADMIN\")));\n\t\t\t\tm.add(new RequestMatcherEntry<>(pathPattern(\"/afterAny\"),\n\t\t\t\t\t\tSingleResultAuthorizationManager.permitAll()));\n\t\t\t})\n\t\t\t.build();\n\n\t\tSupplier<Authentication> authentication = () -> new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\n\t\tAuthorizationResult grant = manager.authorize(authentication, new MockHttpServletRequest(null, \"/grant\"));\n\n\t\tassertThat(grant).isNotNull();\n\t\tassertThat(grant.isGranted()).isTrue();\n\n\t\tAuthorizationResult afterAny = manager.authorize(authentication, new MockHttpServletRequest(null, \"/afterAny\"));\n\t\tassertThat(afterAny).isNotNull();\n\t\tassertThat(afterAny.isGranted()).isFalse();\n\n\t\tAuthorizationResult unmapped = manager.authorize(authentication, new MockHttpServletRequest(null, \"/unmapped\"));\n\t\tassertThat(unmapped).isNotNull();\n\t\tassertThat(unmapped.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void addWhenMappingsConsumerNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder().mappings(null).build())\n\t\t\t.withMessage(\"mappingsConsumer cannot be null\");\n\t}\n\n\t@Test\n\tpublic void mappingsWhenConfiguredAfterAnyRequestThenException() {\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t\t.anyRequest()\n\t\t\t\t.authenticated()\n\t\t\t\t.mappings((m) -> m.add(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE,\n\t\t\t\t\t\tAuthenticatedAuthorizationManager.authenticated()))))\n\t\t\t.withMessage(\"Can't configure mappings after anyRequest\");\n\t}\n\n\t@Test\n\tpublic void addWhenConfiguredAfterAnyRequestThenException() {\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t\t.anyRequest()\n\t\t\t\t.authenticated()\n\t\t\t\t.add(AnyRequestMatcher.INSTANCE, AuthenticatedAuthorizationManager.authenticated()))\n\t\t\t.withMessage(\"Can't add mappings after anyRequest\");\n\t}\n\n\t@Test\n\tpublic void requestMatchersWhenConfiguredAfterAnyRequestThenException() {\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t\t.anyRequest()\n\t\t\t\t.authenticated()\n\t\t\t\t.requestMatchers(pathPattern(\"/authenticated\"))\n\t\t\t\t.authenticated()\n\t\t\t\t.build())\n\t\t\t.withMessage(\"Can't configure requestMatchers after anyRequest\");\n\t}\n\n\t@Test\n\tpublic void anyRequestWhenConfiguredAfterAnyRequestThenException() {\n\t\tassertThatIllegalStateException()\n\t\t\t.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t\t.anyRequest()\n\t\t\t\t.authenticated()\n\t\t\t\t.anyRequest()\n\t\t\t\t.authenticated()\n\t\t\t\t.build())\n\t\t\t.withMessage(\"Can't configure anyRequest after itself\");\n\t}\n\n\t@Test\n\tpublic void anyRequestWhenPermitAllThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.permitAll()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::anonymousUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void anyRequestWhenDenyAllThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.denyAll()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void authenticatedWhenAuthenticatedUserThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.authenticated()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void authenticatedWhenAnonymousUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.authenticated()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::anonymousUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedWhenAuthenticatedUserThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.fullyAuthenticated()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedWhenAnonymousUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.fullyAuthenticated()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::anonymousUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void fullyAuthenticatedWhenRememberMeUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.fullyAuthenticated()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::rememberMeUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenRememberMeUserThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.rememberMe()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::rememberMeUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void rememberMeWhenAuthenticatedUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.rememberMe()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void anonymousWhenAnonymousUserThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.anonymous()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::anonymousUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void anonymousWhenAuthenticatedUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.anonymous()\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasRoleAdminWhenAuthenticatedUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasRole(\"ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasRoleAdminWhenAuthenticatedAdminThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasRole(\"ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleUserOrAdminWhenAuthenticatedUserThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasAnyRole(\"USER\", \"ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleUserOrAdminWhenAuthenticatedAdminThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasAnyRole(\"USER\", \"ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyRoleUserOrAdminWhenAnonymousUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasAnyRole(\"USER\", \"ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::anonymousUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAuthorityRoleAdminWhenAuthenticatedUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasAuthority(\"ROLE_ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void hasAuthorityRoleAdminWhenAuthenticatedAdminThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasAuthority(\"ROLE_ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityRoleUserOrAdminWhenAuthenticatedUserThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasAnyAuthority(\"ROLE_USER\", \"ROLE_ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityRoleUserOrAdminWhenAuthenticatedAdminThenGrantedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasAnyAuthority(\"ROLE_USER\", \"ROLE_ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::authenticatedAdmin, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isTrue();\n\t}\n\n\t@Test\n\tpublic void hasAnyAuthorityRoleUserOrAdminWhenAnonymousUserThenDeniedDecision() {\n\t\tRequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()\n\t\t\t.anyRequest()\n\t\t\t.hasAnyRole(\"USER\", \"ADMIN\")\n\t\t\t.build();\n\t\tAuthorizationResult decision = manager.authorize(TestAuthentication::anonymousUser, null);\n\t\tassertThat(decision).isNotNull();\n\t\tassertThat(decision.isGranted()).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/aot/hint/WebMvcSecurityRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.aot.hint;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.aot.hint.MemberCategory;\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.TypeReference;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.security.web.access.expression.WebSecurityExpressionRoot;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link WebMvcSecurityRuntimeHints}\n *\n * @author Marcus Da Coregio\n */\nclass WebMvcSecurityRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@Test\n\tvoid webSecurityExpressionRootHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(WebSecurityExpressionRoot.class)\n\t\t\t.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS))\n\t\t\t.accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid supplierCsrfTokenHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.reflection()\n\t\t\t.onType(TypeReference\n\t\t\t\t.of(\"org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler$SupplierCsrfToken\"))\n\t\t\t.withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid cssHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.resource().forResource(\"org/springframework/security/default-ui.css\"))\n\t\t\t.accepts(this.hints);\n\t}\n\n\t@Test\n\tvoid webauthnJavascriptHasHints() {\n\t\tassertThat(RuntimeHintsPredicates.resource()\n\t\t\t.forResource(\"org/springframework/security/spring-security-webauthn.js\")).accepts(this.hints);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.util.ArrayList;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.apache.commons.logging.Log;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.mock.web.MockFilterConfig;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.NonBuildableAuthenticationToken;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.authentication.rememberme.AbstractRememberMeServicesTests;\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.firewall.DefaultHttpFirewall;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.Builder;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests {@link AbstractAuthenticationProcessingFilter}.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @author Rob Winch\n */\n@SuppressWarnings(\"deprecation\")\npublic class AbstractAuthenticationProcessingFilterTests {\n\n\tSavedRequestAwareAuthenticationSuccessHandler successHandler;\n\n\tSimpleUrlAuthenticationFailureHandler failureHandler;\n\n\tprivate MockHttpServletRequest createMockAuthenticationRequest() {\n\t\treturn withMockAuthenticationRequest().build();\n\t}\n\n\tprivate Builder withMockAuthenticationRequest() {\n\t\treturn get(\"www.example.com\").requestUri(\"/mycontext\", \"/j_mock_post\", null);\n\t}\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.successHandler = new SavedRequestAwareAuthenticationSuccessHandler();\n\t\tthis.successHandler.setDefaultTargetUrl(\"/logged_in.jsp\");\n\t\tthis.failureHandler = new SimpleUrlAuthenticationFailureHandler();\n\t\tthis.failureHandler.setDefaultFailureUrl(\"/failed.jsp\");\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testDefaultProcessesFilterUrlMatchesWithPathParameter() {\n\t\tMockHttpServletRequest request = post(\"/login;jsessionid=I8MIONOSTHOR\").build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter();\n\t\tfilter.setFilterProcessesUrl(\"/login\");\n\t\tDefaultHttpFirewall firewall = new DefaultHttpFirewall();\n\t\t// the firewall ensures that path parameters are ignored\n\t\tHttpServletRequest firewallRequest = firewall.getFirewalledRequest(request);\n\t\tassertThat(filter.requiresAuthentication(firewallRequest, response)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testFilterProcessesUrlVariationsRespected() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = withMockAuthenticationRequest()\n\t\t\t.requestUri(\"/mycontext\", \"/j_OTHER_LOCATION\", null)\n\t\t\t.build();\n\t\t// Setup our filter configuration\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\t// Setup our expectation that the filter chain will not be invoked, as we redirect\n\t\t// to defaultTargetUrl\n\t\tMockFilterChain chain = new MockFilterChain(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Setup our test object, to grant access\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(true);\n\t\tfilter.setFilterProcessesUrl(\"/j_OTHER_LOCATION\");\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/logged_in.jsp\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()).isEqualTo(\"test\");\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tAbstractAuthenticationProcessingFilter filter = new MockAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(mock(AuthenticationManager.class));\n\t\tfilter.setFilterProcessesUrl(\"/p\");\n\t\tfilter.afterPropertiesSet();\n\t\tassertThat(filter.getRememberMeServices()).isNotNull();\n\t\tfilter.setRememberMeServices(\n\t\t\t\tnew TokenBasedRememberMeServices(\"key\", new AbstractRememberMeServicesTests.MockUserDetailsService()));\n\t\tassertThat(filter.getRememberMeServices().getClass()).isEqualTo(TokenBasedRememberMeServices.class);\n\t\tassertThat(filter.getAuthenticationManager() != null).isTrue();\n\t}\n\n\t@Test\n\tpublic void testIgnoresAnyServletPathOtherThanFilterProcessesUrl() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = withMockAuthenticationRequest()\n\t\t\t.requestUri(\"/mycontext\", \"/some.file.html\", null)\n\t\t\t.build();\n\t\t// Setup our filter configuration\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\t// Setup our expectation that the filter chain will be invoked, as our request is\n\t\t// for a page the filter isn't monitoring\n\t\tMockFilterChain chain = new MockFilterChain(true);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Setup our test object, to deny access\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(false);\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWithDefaultFilterProcessesUrl() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tHttpSession sessionPreAuth = request.getSession();\n\t\t// Setup our filter configuration\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\t// Setup our expectation that the filter chain will not be invoked, as we redirect\n\t\t// to defaultTargetUrl\n\t\tMockFilterChain chain = new MockFilterChain(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Setup our test object, to grant access\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(true);\n\t\tfilter.setFilterProcessesUrl(\"/j_mock_post\");\n\t\tfilter.setSessionAuthenticationStrategy(mock(SessionAuthenticationStrategy.class));\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tfilter.setAuthenticationFailureHandler(this.failureHandler);\n\t\tfilter.setAuthenticationManager(mock(AuthenticationManager.class));\n\t\tfilter.afterPropertiesSet();\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/logged_in.jsp\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()).isEqualTo(\"test\");\n\t\tassertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t\t// Should still have the same session\n\t\tassertThat(request.getSession()).isEqualTo(sessionPreAuth);\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWithDefaultFilterProcessesUrlAndAuthenticationManager() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tHttpSession sessionPreAuth = request.getSession();\n\t\t// Setup our filter configuration\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\t// Setup our expectation that the filter chain will not be invoked, as we redirect\n\t\t// to defaultTargetUrl\n\t\tMockFilterChain chain = new MockFilterChain(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Setup our test object, to grant access\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(\"/j_mock_post\",\n\t\t\t\tmock(AuthenticationManager.class));\n\t\tfilter.setSessionAuthenticationStrategy(mock(SessionAuthenticationStrategy.class));\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tfilter.setAuthenticationFailureHandler(this.failureHandler);\n\t\tfilter.afterPropertiesSet();\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/logged_in.jsp\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()).isEqualTo(\"test\");\n\t\tassertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t\t// Should still have the same session\n\t\tassertThat(request.getSession()).isEqualTo(sessionPreAuth);\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWithRequestMatcherAndAuthenticationManager() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = withMockAuthenticationRequest()\n\t\t\t.requestUri(\"/mycontext\", \"/j_eradicate_corona_virus\", null)\n\t\t\t.build();\n\t\tHttpSession sessionPreAuth = request.getSession();\n\t\t// Setup our filter configuration\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\t// Setup our expectation that the filter chain will not be invoked, as we redirect\n\t\t// to defaultTargetUrl\n\t\tMockFilterChain chain = new MockFilterChain(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Setup our test object, to grant access\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(pathPattern(\"/j_eradicate_corona_virus\"),\n\t\t\t\tmock(AuthenticationManager.class));\n\t\tfilter.setSessionAuthenticationStrategy(mock(SessionAuthenticationStrategy.class));\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tfilter.setAuthenticationFailureHandler(this.failureHandler);\n\t\tfilter.afterPropertiesSet();\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/logged_in.jsp\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()).isEqualTo(\"test\");\n\t\tassertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t\t// Should still have the same session\n\t\tassertThat(request.getSession()).isEqualTo(sessionPreAuth);\n\t}\n\n\t@Test\n\tpublic void testStartupDetectsInvalidAuthenticationManager() {\n\t\tAbstractAuthenticationProcessingFilter filter = new MockAuthenticationFilter();\n\t\tfilter.setAuthenticationFailureHandler(this.failureHandler);\n\t\tthis.successHandler.setDefaultTargetUrl(\"/\");\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tfilter.setFilterProcessesUrl(\"/login\");\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet)\n\t\t\t.withMessage(\"authenticationManager must be specified\");\n\t}\n\n\t@Test\n\tpublic void testStartupDetectsInvalidFilterProcessesUrl() {\n\t\tAbstractAuthenticationProcessingFilter filter = new MockAuthenticationFilter();\n\t\tfilter.setAuthenticationFailureHandler(this.failureHandler);\n\t\tfilter.setAuthenticationManager(mock(AuthenticationManager.class));\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setFilterProcessesUrl(null))\n\t\t\t.withMessage(\"pattern cannot be null\");\n\t}\n\n\t@Test\n\tpublic void testSuccessLoginThenFailureLoginResultsInSessionLosingToken() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\t// Setup our filter configuration\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\t// Setup our expectation that the filter chain will not be invoked, as we redirect\n\t\t// to defaultTargetUrl\n\t\tMockFilterChain chain = new MockFilterChain(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Setup our test object, to grant access\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(true);\n\t\tfilter.setFilterProcessesUrl(\"/j_mock_post\");\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mycontext/logged_in.jsp\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()).isEqualTo(\"test\");\n\t\t// Now try again but this time have filter deny access\n\t\t// Setup our HTTP request\n\t\t// Setup our expectation that the filter chain will not be invoked, as we redirect\n\t\t// to authenticationFailureUrl\n\t\tchain = new MockFilterChain(false);\n\t\tresponse = new MockHttpServletResponse();\n\t\t// Setup our test object, to deny access\n\t\tfilter = new MockAuthenticationFilter(false);\n\t\tfilter.setFilterProcessesUrl(\"/j_mock_post\");\n\t\tfilter.setAuthenticationFailureHandler(this.failureHandler);\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void testSuccessfulAuthenticationInvokesSuccessHandlerAndSetsContext() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\t// Setup our filter configuration\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\t// Setup our expectation that the filter chain will be invoked, as we want to go\n\t\t// to the location requested in the session\n\t\tMockFilterChain chain = new MockFilterChain(true);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Setup our test object, to grant access\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(true);\n\t\tfilter.setFilterProcessesUrl(\"/j_mock_post\");\n\t\tAuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);\n\t\tfilter.setAuthenticationSuccessHandler(successHandler);\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(successHandler).onAuthenticationSuccess(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(Authentication.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void testSuccessfulAuthenticationThenDefaultDoesNotCreateSession() throws Exception {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain(false);\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter();\n\n\t\tfilter.successfulAuthentication(request, response, chain, authentication);\n\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void testSuccessfulAuthenticationWhenCustomSecurityContextRepositoryThenAuthenticationSaved()\n\t\t\tthrows Exception {\n\t\tArgumentCaptor<SecurityContext> contextCaptor = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tSecurityContextRepository repository = mock(SecurityContextRepository.class);\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain(false);\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter();\n\t\tfilter.setSecurityContextRepository(repository);\n\n\t\tfilter.successfulAuthentication(request, response, chain, authentication);\n\n\t\tverify(repository).saveContext(contextCaptor.capture(), eq(request), eq(response));\n\t\tassertThat(contextCaptor.getValue().getAuthentication()).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void testFailedAuthenticationInvokesFailureHandler() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\t// Setup our filter configuration\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\t// Setup our expectation that the filter chain will not be invoked, as we redirect\n\t\t// to authenticationFailureUrl\n\t\tMockFilterChain chain = new MockFilterChain(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Setup our test object, to deny access\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(false);\n\t\tAuthenticationFailureHandler failureHandler = mock(AuthenticationFailureHandler.class);\n\t\tfilter.setAuthenticationFailureHandler(failureHandler);\n\t\t// Test\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(failureHandler).onAuthenticationFailure(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AuthenticationException.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t/**\n\t * SEC-571\n\t */\n\t@Test\n\tpublic void testNoSessionIsCreatedIfAllowSessionCreationIsFalse() throws Exception {\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\tMockFilterChain chain = new MockFilterChain(true);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Reject authentication, so exception would normally be stored in session\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(false);\n\t\tthis.failureHandler.setAllowSessionCreation(false);\n\t\tfilter.setAuthenticationFailureHandler(this.failureHandler);\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t/**\n\t * SEC-462\n\t */\n\t@Test\n\tpublic void testLoginErrorWithNoFailureUrlSendsUnauthorizedStatus() throws Exception {\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tMockFilterConfig config = new MockFilterConfig(null, null);\n\t\tMockFilterChain chain = new MockFilterChain(true);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(false);\n\t\tthis.successHandler.setDefaultTargetUrl(\"https://monkeymachine.co.uk/\");\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t}\n\n\t/**\n\t * SEC-1919\n\t */\n\t@Test\n\tpublic void loginErrorWithInternAuthenticationServiceExceptionLogsError() throws Exception {\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tMockFilterChain chain = new MockFilterChain(true);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tLog logger = mock(Log.class);\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(false);\n\t\tReflectionTestUtils.setField(filter, \"logger\", logger);\n\t\tfilter.exceptionToThrow = new InternalAuthenticationServiceException(\"Mock requested to do so\");\n\t\tthis.successHandler.setDefaultTargetUrl(\"https://monkeymachine.co.uk/\");\n\t\tfilter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(logger).error(anyString(), eq(filter.exceptionToThrow));\n\t\tassertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t}\n\n\t@Test\n\tvoid doFilterWhenAuthenticatedThenCombinesAuthorities() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication newAuthn = UsernamePasswordAuthenticationToken.authenticated(existingAuthn.getName(), \"test\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"TEST\"));\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(newAuthn);\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, new MockFilterChain(false));\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication.getAuthorities()).extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactlyInAnyOrder(ROLE_EXISTING, \"TEST\");\n\t}\n\n\t@Test\n\tvoid doFilterWhenDefaultThenMfaDisabled() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication newAuthn = UsernamePasswordAuthenticationToken.authenticated(existingAuthn.getName(), \"test\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"TEST\"));\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(newAuthn);\n\t\tfilter.doFilter(request, response, new MockFilterChain(false));\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isEqualTo(newAuthn);\n\t}\n\n\t// gh-18112\n\t@Test\n\tvoid doFilterWhenDifferentPrincipalThenDoesNotCombine() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication newAuthn = UsernamePasswordAuthenticationToken\n\t\t\t.authenticated(existingAuthn.getName() + \"different\", \"test\", AuthorityUtils.createAuthorityList(\"TEST\"));\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(newAuthn);\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, new MockFilterChain(false));\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isEqualTo(newAuthn);\n\t}\n\n\t/**\n\t * This is critical to avoid adding duplicate GrantedAuthority instances with the\n\t * same' authority when the issuedAt is too old and a new instance is requested.\n\t * @throws Exception\n\t */\n\t@Test\n\tvoid doFilterWhenDefaultEqualsAuthorityThenNoDuplicates() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tnew DefaultEqualsGrantedAuthority());\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(\n\t\t\t\tnew TestingAuthenticationToken(\"username\", \"password\", new DefaultEqualsGrantedAuthority()));\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, new MockFilterChain(false));\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(new ArrayList<GrantedAuthority>(authentication.getAuthorities()))\n\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactly(DefaultEqualsGrantedAuthority.AUTHORITY);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\", \"FACTORONE\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = createMockAuthenticationRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockAuthenticationFilter filter = new MockAuthenticationFilter(\n\t\t\t\tnew NonBuildableAuthenticationToken(\"username\", \"password\", \"FACTORTWO\"));\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, new MockFilterChain(false));\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.authorities()\n\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactly(\"FACTORTWO\");\n\t}\n\n\t/**\n\t * https://github.com/spring-projects/spring-security/pull/3905\n\t */\n\t@Test\n\tpublic void setRememberMeServicesShouldntAllowNulls() {\n\t\tAbstractAuthenticationProcessingFilter filter = new MockAuthenticationFilter();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setRememberMeServices(null));\n\t}\n\n\tprivate class MockAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n\t\tprivate static final String DEFAULT_FILTER_PROCESSING_URL = \"/j_mock_post\";\n\n\t\tprivate AuthenticationException exceptionToThrow;\n\n\t\tprivate final @Nullable Authentication authentication;\n\n\t\tMockAuthenticationFilter(Authentication authentication) {\n\t\t\tsuper(DEFAULT_FILTER_PROCESSING_URL);\n\t\t\tthis.authentication = authentication;\n\t\t\tsetupRememberMeServicesAndAuthenticationException();\n\t\t}\n\n\t\tMockAuthenticationFilter(boolean grantAccess) {\n\t\t\tthis(createDefaultAuthentication(grantAccess));\n\t\t}\n\n\t\tprivate MockAuthenticationFilter() {\n\t\t\tthis(null);\n\t\t}\n\n\t\tprivate MockAuthenticationFilter(String defaultFilterProcessingUrl,\n\t\t\t\tAuthenticationManager authenticationManager) {\n\t\t\tsuper(defaultFilterProcessingUrl, authenticationManager);\n\t\t\tsetupRememberMeServicesAndAuthenticationException();\n\t\t\tthis.authentication = createDefaultAuthentication(true);\n\t\t}\n\n\t\tprivate MockAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher,\n\t\t\t\tAuthenticationManager authenticationManager) {\n\t\t\tsuper(requiresAuthenticationRequestMatcher, authenticationManager);\n\t\t\tsetupRememberMeServicesAndAuthenticationException();\n\t\t\tthis.authentication = createDefaultAuthentication(true);\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)\n\t\t\t\tthrows AuthenticationException {\n\t\t\tif (this.authentication != null) {\n\t\t\t\treturn this.authentication;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow this.exceptionToThrow;\n\t\t\t}\n\t\t}\n\n\t\tprivate void setupRememberMeServicesAndAuthenticationException() {\n\t\t\tsetRememberMeServices(new NullRememberMeServices());\n\t\t\tthis.exceptionToThrow = new BadCredentialsException(\"Mock requested to do so\");\n\t\t}\n\n\t\tprivate static @Nullable Authentication createDefaultAuthentication(boolean grantAccess) {\n\t\t\tif (!grantAccess) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn UsernamePasswordAuthenticationToken.authenticated(\"test\", \"test\",\n\t\t\t\t\tAuthorityUtils.createAuthorityList(\"TEST\"));\n\t\t}\n\n\t}\n\n\tprivate class MockFilterChain implements FilterChain {\n\n\t\tprivate boolean expectToProceed;\n\n\t\tMockFilterChain(boolean expectToProceed) {\n\t\t\tthis.expectToProceed = expectToProceed;\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response) {\n\t\t\tif (!this.expectToProceed) {\n\t\t\t\tfail(\"Did not expect filter chain to proceed\");\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationTargetUrlRequestHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Dayan Kodippily\n */\npublic class AbstractAuthenticationTargetUrlRequestHandlerTests {\n\n\tpublic static final String REQUEST_URI = \"https://example.org\";\n\n\tpublic static final String DEFAULT_TARGET_URL = \"/defaultTarget\";\n\n\tpublic static final String REFERER_URL = \"https://www.springsource.com/\";\n\n\tpublic static final String TARGET_URL = \"https://example.org/target\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate AbstractAuthenticationTargetUrlRequestHandler handler;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.handler = new AbstractAuthenticationTargetUrlRequestHandler() {\n\t\t\t@Override\n\t\t\tprotected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {\n\t\t\t\treturn super.determineTargetUrl(request, response);\n\t\t\t}\n\t\t};\n\t\tthis.handler.setDefaultTargetUrl(DEFAULT_TARGET_URL);\n\t\tthis.request.setRequestURI(REQUEST_URI);\n\t}\n\n\t@Test\n\tvoid returnDefaultTargetUrlIfUseDefaultTargetUrlTrue() {\n\t\tthis.handler.setAlwaysUseDefaultTargetUrl(true);\n\t\tassertThat(this.handler.determineTargetUrl(this.request, this.response)).isEqualTo(DEFAULT_TARGET_URL);\n\t}\n\n\t@Test\n\tvoid returnTargetUrlParamValueIfParamHasValue() {\n\t\tthis.handler.setTargetUrlParameter(\"param\");\n\t\tthis.request.setParameter(\"param\", TARGET_URL);\n\t\tassertThat(this.handler.determineTargetUrl(this.request, this.response)).isEqualTo(TARGET_URL);\n\t}\n\n\t@Test\n\tvoid targetUrlParamValueTakePrecedenceOverRefererIfParamHasValue() {\n\t\tthis.handler.setUseReferer(true);\n\t\tthis.handler.setTargetUrlParameter(\"param\");\n\t\tthis.request.setParameter(\"param\", TARGET_URL);\n\t\tassertThat(this.handler.determineTargetUrl(this.request, this.response)).isEqualTo(TARGET_URL);\n\t}\n\n\t@Test\n\tvoid returnDefaultTargetUrlIfTargetUrlParamHasNoValue() {\n\t\tthis.handler.setTargetUrlParameter(\"param\");\n\t\tthis.request.setParameter(\"param\", \"\");\n\t\tassertThat(this.handler.determineTargetUrl(this.request, this.response)).isEqualTo(DEFAULT_TARGET_URL);\n\t}\n\n\t@Test\n\tvoid returnDefaultTargetUrlIfTargetUrlParamHasNoValueContainsOnlyWhiteSpaces() {\n\t\tthis.handler.setTargetUrlParameter(\"param\");\n\t\tthis.request.setParameter(\"param\", \"   \");\n\t\tassertThat(this.handler.determineTargetUrl(this.request, this.response)).isEqualTo(DEFAULT_TARGET_URL);\n\t}\n\n\t@Test\n\tvoid returnRefererUrlIfUseRefererIsTrue() {\n\t\tthis.handler.setUseReferer(true);\n\t\tthis.request.addHeader(\"Referer\", REFERER_URL);\n\t\tassertThat(this.handler.determineTargetUrl(this.request, this.response)).isEqualTo(REFERER_URL);\n\t}\n\n\t@Test\n\tvoid returnDefaultTargetUrlIfUseRefererIsFalse() {\n\t\tthis.handler.setUseReferer(false);\n\t\tthis.request.addHeader(\"Referer\", REFERER_URL);\n\t\tassertThat(this.handler.determineTargetUrl(this.request, this.response)).isEqualTo(DEFAULT_TARGET_URL);\n\t}\n\n\t@Test\n\tvoid setRedirectStrategyWhenGivenNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setRedirectStrategy(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.FilterConfig;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.MockSecurityContextHolderStrategy;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link AnonymousAuthenticationFilter}.\n *\n * @author Ben Alex\n * @author Eddú Meléndez\n * @author Evgeniy Cheban\n */\npublic class AnonymousAuthenticationFilterTests {\n\n\tprivate void executeFilterInContainerSimulator(FilterConfig filterConfig, Filter filter, ServletRequest request,\n\t\t\tServletResponse response, FilterChain filterChain) throws ServletException, IOException {\n\t\tfilter.doFilter(request, response, filterChain);\n\t}\n\n\t@BeforeEach\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testDetectsMissingKey() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousAuthenticationFilter(null));\n\t}\n\n\t@Test\n\tpublic void testDetectsUserAttribute() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AnonymousAuthenticationFilter(\"qwerty\", null, null));\n\t}\n\n\t@Test\n\tpublic void testOperationWhenAuthenticationExistsInContextHolder() throws Exception {\n\t\t// Put an Authentication object into the SecurityContextHolder\n\t\tAuthentication originalAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_A\");\n\t\tSecurityContext originalContext = new SecurityContextImpl(originalAuth);\n\t\tSecurityContextHolder.setContext(originalContext);\n\t\tAnonymousAuthenticationFilter filter = new AnonymousAuthenticationFilter(\"qwerty\", \"anonymousUsername\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"x\");\n\t\texecuteFilterInContainerSimulator(mock(FilterConfig.class), filter, request, new MockHttpServletResponse(),\n\t\t\t\tnew MockFilterChain(true));\n\t\t// Ensure getDeferredContext still\n\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(originalContext);\n\t}\n\n\t@Test\n\tpublic void testOperationWhenNoAuthenticationInSecurityContextHolder() throws Exception {\n\t\tAnonymousAuthenticationFilter filter = new AnonymousAuthenticationFilter(\"qwerty\", \"anonymousUsername\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tfilter.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"x\");\n\t\texecuteFilterInContainerSimulator(mock(FilterConfig.class), filter, request, new MockHttpServletResponse(),\n\t\t\t\tnew MockFilterChain(true));\n\t\tAuthentication auth = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(auth.getPrincipal()).isEqualTo(\"anonymousUsername\");\n\t\tassertThat(AuthorityUtils.authorityListToSet(auth.getAuthorities())).contains(\"ROLE_ANONYMOUS\");\n\t\tSecurityContextHolder.getContext().setAuthentication(null); // so anonymous fires\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// again\n\t}\n\n\t@Test\n\tpublic void doFilterDoesNotGetContext() throws Exception {\n\t\tSupplier<SecurityContext> originalSupplier = mock(Supplier.class);\n\t\tAuthentication originalAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_A\");\n\t\tSecurityContext originalContext = new SecurityContextImpl(originalAuth);\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getDeferredContext()).willReturn(originalSupplier);\n\t\tgiven(strategy.getContext()).willReturn(originalContext);\n\t\tAnonymousAuthenticationFilter filter = new AnonymousAuthenticationFilter(\"qwerty\", \"anonymousUsername\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tfilter.setSecurityContextHolderStrategy(strategy);\n\t\tfilter.afterPropertiesSet();\n\n\t\texecuteFilterInContainerSimulator(mock(FilterConfig.class), filter, new MockHttpServletRequest(),\n\t\t\t\tnew MockHttpServletResponse(), new MockFilterChain(true));\n\t\tverify(strategy, never()).getContext();\n\t\tverify(originalSupplier, never()).get();\n\t}\n\n\t@Test\n\tpublic void doFilterSetsSingletonSupplier() throws Exception {\n\t\tSupplier<SecurityContext> originalSupplier = mock(Supplier.class);\n\t\tAuthentication originalAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_A\");\n\t\tSecurityContext originalContext = new SecurityContextImpl(originalAuth);\n\t\tSecurityContextHolderStrategy strategy = new MockSecurityContextHolderStrategy(originalSupplier);\n\t\tgiven(originalSupplier.get()).willReturn(originalContext);\n\t\tAnonymousAuthenticationFilter filter = new AnonymousAuthenticationFilter(\"qwerty\", \"anonymousUsername\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tfilter.setSecurityContextHolderStrategy(strategy);\n\t\tfilter.afterPropertiesSet();\n\t\texecuteFilterInContainerSimulator(mock(FilterConfig.class), filter, new MockHttpServletRequest(),\n\t\t\t\tnew MockHttpServletResponse(), new MockFilterChain(true));\n\t\tSupplier<SecurityContext> deferredContext = strategy.getDeferredContext();\n\t\tdeferredContext.get();\n\t\tdeferredContext.get();\n\t\tverify(originalSupplier, times(1)).get();\n\t}\n\n\tprivate class MockFilterChain implements FilterChain {\n\n\t\tprivate boolean expectToProceed;\n\n\t\tMockFilterChain(boolean expectToProceed) {\n\t\t\tthis.expectToProceed = expectToProceed;\n\t\t}\n\n\t\t@Override\n\t\tpublic void doFilter(ServletRequest request, ServletResponse response) {\n\t\t\tif (!this.expectToProceed) {\n\t\t\t\tfail(\"Did not expect filter chain to proceed\");\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/AuthenticationEntryPointFailureHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link AuthenticationEntryPointFailureHandler}\n */\npublic class AuthenticationEntryPointFailureHandlerTests {\n\n\t@Test\n\tvoid onAuthenticationFailureWhenRethrowingThenAuthenticationServiceExceptionSwallowed() throws Exception {\n\t\tAuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class);\n\t\tAuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(entryPoint);\n\t\thandler.setRethrowAuthenticationServiceException(false);\n\t\thandler.onAuthenticationFailure(null, null, new AuthenticationServiceException(\"fail\"));\n\t}\n\n\t@Test\n\tvoid handleWhenDefaultsThenAuthenticationServiceExceptionRethrown() {\n\t\tAuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class);\n\t\tAuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(entryPoint);\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> handler.onAuthenticationFailure(null, null, new AuthenticationServiceException(\"fail\")));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.Servlet;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationManagerResolver;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.NonBuildableAuthenticationToken;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Sergey Bespalov\n * @author Andrey Litvitski\n * @since 5.2.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthenticationFilterTests {\n\n\t@Mock\n\tprivate AuthenticationSuccessHandler successHandler;\n\n\t@Mock\n\tprivate AuthenticationConverter authenticationConverter;\n\n\t@Mock\n\tprivate AuthenticationManager authenticationManager;\n\n\t@Mock\n\tprivate AuthenticationFailureHandler failureHandler;\n\n\t@Mock\n\tprivate AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;\n\n\t@Mock\n\tprivate RequestMatcher requestMatcher;\n\n\tprivate void givenResolveWillReturnAuthenticationManager() {\n\t\tgiven(this.authenticationManagerResolver.resolve(any())).willReturn(this.authenticationManager);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultsAndNoAuthenticationThenContinues() throws Exception {\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(this.authenticationManager);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticationManagerResolverDefaultsAndNoAuthenticationThenContinues() throws Exception {\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManagerResolver,\n\t\t\t\tthis.authenticationConverter);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(this.authenticationManagerResolver);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultsAndAuthenticationSuccessThenContinues() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.createEmptyContext()).willReturn(new SecurityContextImpl());\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl());\n\t\tfilter.setSecurityContextHolderStrategy(strategy);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tverify(strategy).setContext(any());\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticationManagerResolverDefaultsAndAuthenticationSuccessThenContinues()\n\t\t\tthrows Exception {\n\t\tgivenResolveWillReturnAuthenticationManager();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManagerResolver,\n\t\t\t\tthis.authenticationConverter);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(this.authenticationManager).authenticate(any(Authentication.class));\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultsAndAuthenticationFailThenUnauthorized() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(new BadCredentialsException(\"failed\"));\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticationManagerResolverDefaultsAndAuthenticationFailThenUnauthorized()\n\t\t\tthrows Exception {\n\t\tgivenResolveWillReturnAuthenticationManager();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(new BadCredentialsException(\"failed\"));\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManagerResolver,\n\t\t\t\tthis.authenticationConverter);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertEmptyThenOk() throws Exception {\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(null);\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManagerResolver,\n\t\t\t\tthis.authenticationConverter);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), chain);\n\t\tverifyNoMoreInteractions(this.authenticationManagerResolver);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertAndAuthenticationSuccessThenSuccess() throws Exception {\n\t\tgivenResolveWillReturnAuthenticationManager();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManagerResolver,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.setSuccessHandler(this.successHandler);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(this.successHandler).onAuthenticationSuccess(any(), any(), any(), eq(authentication));\n\t\tverifyNoMoreInteractions(this.failureHandler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertAndAuthenticationEmptyThenServerError() throws Exception {\n\t\tgivenResolveWillReturnAuthenticationManager();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(null);\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManagerResolver,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.setSuccessHandler(this.successHandler);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tassertThatExceptionOfType(ServletException.class).isThrownBy(() -> filter.doFilter(request, response, chain));\n\t\tverifyNoMoreInteractions(this.successHandler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenNotMatchAndConvertAndAuthenticationSuccessThenContinues() throws Exception {\n\t\tgiven(this.requestMatcher.matches(any())).willReturn(false);\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManagerResolver,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.setRequestMatcher(this.requestMatcher);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(this.authenticationConverter, this.authenticationManagerResolver, this.successHandler);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t// gh-7446\n\t@Test\n\tpublic void filterWhenSuccessfulAuthenticationThenSessionIdChanges() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tMockHttpSession session = new MockHttpSession();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = new MockFilterChain();\n\t\tString sessionId = session.getId();\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(session.getId()).isNotEqualTo(sessionId);\n\t}\n\n\t@Test\n\tpublic void filterWhenSuccessfulAuthenticationThenNoSessionCreated() throws Exception {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = new MockFilterChain();\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenAuthenticatedThenCombinesAuthorities() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(existingAuthn);\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(new TestingAuthenticationToken(existingAuthn.getName(), \"password\", \"TEST\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = new MockFilterChain();\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, chain);\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication.getAuthorities()).extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactlyInAnyOrder(ROLE_EXISTING, \"TEST\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenDefaultThenMfaDisabled() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(existingAuthn);\n\t\tTestingAuthenticationToken newAuthn = new TestingAuthenticationToken(existingAuthn.getName(), \"password\",\n\t\t\t\t\"TEST\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(newAuthn);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = new MockFilterChain();\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.doFilter(request, response, chain);\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isEqualTo(newAuthn);\n\t}\n\n\t// gh-18112\n\t@Test\n\tpublic void doFilterWhenDifferentPrincipalThenDoesNotCombine() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(existingAuthn);\n\t\tTestingAuthenticationToken expected = new TestingAuthenticationToken(existingAuthn.getName() + \"different\",\n\t\t\t\t\"password\", \"TEST\");\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(expected);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = new MockFilterChain();\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, chain);\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isEqualTo(expected);\n\t}\n\n\t/**\n\t * This is critical to avoid adding duplicate GrantedAuthority instances with the\n\t * same' authority when the issuedAt is too old and a new instance is requested.\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void doFilterWhenDefaultEqualsGrantedAuthorityThenNoDuplicates() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tnew DefaultEqualsGrantedAuthority());\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(existingAuthn);\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(new TestingAuthenticationToken(\"user\", \"password\", new DefaultEqualsGrantedAuthority()));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = new MockFilterChain();\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, chain);\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication.getAuthorities()).extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactlyInAnyOrder(DefaultEqualsGrantedAuthority.AUTHORITY);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\", \"FACTORONE\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(existingAuthn);\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(new NonBuildableAuthenticationToken(\"user\", \"password\", \"FACTORTWO\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = new MockFilterChain();\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, chain);\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.authorities()\n\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactly(\"FACTORTWO\");\n\t}\n\n\t@Test\n\tpublic void filterWhenCustomSecurityContextRepositoryAndSuccessfulAuthenticationRepositoryUsed() throws Exception {\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tArgumentCaptor<SecurityContext> securityContextArg = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\");\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = new MockFilterChain();\n\t\tAuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tfilter.setSecurityContextRepository(securityContextRepository);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(securityContextRepository).saveContext(securityContextArg.capture(), eq(request), eq(response));\n\t\tassertThat(securityContextArg.getValue().getAuthentication()).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void filterWhenMultipleInChainThenAllFiltered() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationFilter filter1 = new AuthenticationFilter(this.authenticationManager,\n\t\t\t\tthis.authenticationConverter);\n\t\tAuthenticationConverter converter2 = mock(AuthenticationConverter.class);\n\t\tAuthenticationFilter filter2 = new AuthenticationFilter(this.authenticationManager, converter2);\n\t\tFilterChain chain = new MockFilterChain(mock(Servlet.class), filter1, filter2);\n\t\tchain.doFilter(request, response);\n\t\tverify(this.authenticationConverter).convert(any());\n\t\tverify(converter2).convert(any());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/DefaultEqualsGrantedAuthority.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.springframework.security.core.GrantedAuthority;\n\n/**\n * Used for testing when a {@link GrantedAuthority} might not be equal, but the\n * {@link #getAuthority()} is.\n *\n * @author Rob Winch\n * @since 7.0\n */\npublic class DefaultEqualsGrantedAuthority implements GrantedAuthority {\n\n\tpublic static final String AUTHORITY = \"CUSTOM_AUTHORITY\";\n\n\t@Override\n\tpublic String getAuthority() {\n\t\treturn AUTHORITY;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/DefaultLoginPageGeneratingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authorization.AuthorizationDeniedException;\nimport org.springframework.security.authorization.FactorAuthorizationDecision;\nimport org.springframework.security.authorization.RequiredFactor;\nimport org.springframework.security.authorization.RequiredFactorError;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.access.DelegatingMissingAuthorityAccessDeniedHandler;\nimport org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;\nimport org.springframework.security.web.servlet.TestMockHttpServletRequests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n * @since 3.0\n */\npublic class DefaultLoginPageGeneratingFilterTests {\n\n\tprivate FilterChain chain = mock(FilterChain.class);\n\n\t@Test\n\tpublic void generatingPageWithAuthenticationProcessingFilterOnlyIsSuccessFul() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tfilter.doFilter(new MockHttpServletRequest(\"GET\", \"/login\"), new MockHttpServletResponse(), this.chain);\n\t\tfilter.doFilter(new MockHttpServletRequest(\"GET\", \"/login;pathparam=unused\"), new MockHttpServletResponse(),\n\t\t\t\tthis.chain);\n\t}\n\n\t@Test\n\tpublic void generatesForGetLogin() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(new MockHttpServletRequest(\"GET\", \"/login\"), response, this.chain);\n\t\tassertThat(response.getContentAsString()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void generatesForPostLogin() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/login\");\n\t\tfilter.doFilter(request, response, this.chain);\n\t\tassertThat(response.getContentAsString()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void generatesForNotEmptyContextLogin() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/context/login\");\n\t\trequest.setContextPath(\"/context\");\n\t\tfilter.doFilter(request, response, this.chain);\n\t\tassertThat(response.getContentAsString()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void generatesForGetApiLogin() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(new MockHttpServletRequest(\"GET\", \"/api/login\"), response, this.chain);\n\t\tassertThat(response.getContentAsString()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void generatesForWithQueryMatch() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/login\");\n\t\trequest.setQueryString(\"error\");\n\t\tfilter.doFilter(request, response, this.chain);\n\t\tassertThat(response.getContentAsString()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void generatesForWithContentLength() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tfilter.setOauth2LoginEnabled(true);\n\t\tfilter.setOauth2AuthenticationUrlToClientName(\n\t\t\t\tCollections.singletonMap(\"XYUU\", \"\\u8109\\u640F\\u7F51\\u5E10\\u6237\\u767B\\u5F55\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/login\");\n\t\tfilter.doFilter(request, response, this.chain);\n\t\tassertThat(response\n\t\t\t.getContentLength() == response.getContentAsString().getBytes(response.getCharacterEncoding()).length)\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void generatesForWithQueryNoMatch() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/login\");\n\t\trequest.setQueryString(\"not\");\n\t\tfilter.doFilter(request, response, this.chain);\n\t\tassertThat(response.getContentAsString()).isEmpty();\n\t}\n\n\t// gh-5394\n\t@Test\n\tpublic void generatesForOAuth2LoginAndEscapesClientName() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter();\n\t\tfilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\tfilter.setOauth2LoginEnabled(true);\n\t\tString clientName = \"Google < > \\\" \\' &\";\n\t\tfilter.setOauth2AuthenticationUrlToClientName(\n\t\t\t\tCollections.singletonMap(\"/oauth2/authorization/google\", clientName));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(new MockHttpServletRequest(\"GET\", \"/login\"), response, this.chain);\n\t\tassertThat(response.getContentAsString())\n\t\t\t.contains(\"<a href=\\\"/oauth2/authorization/google\\\">Google &lt; &gt; &quot; &#39; &amp;</a>\");\n\t}\n\n\t@Test\n\tpublic void generatesForSaml2LoginAndEscapesClientName() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter();\n\t\tfilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\tfilter.setSaml2LoginEnabled(true);\n\t\tString clientName = \"Google < > \\\" \\' &\";\n\t\tfilter.setSaml2AuthenticationUrlToProviderName(Collections.singletonMap(\"/saml/sso/google\", clientName));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(new MockHttpServletRequest(\"GET\", \"/login\"), response, this.chain);\n\t\tassertThat(response.getContentAsString()).contains(\"Login with SAML 2.0\");\n\t\tassertThat(response.getContentAsString())\n\t\t\t.contains(\"<a href=\\\"/saml/sso/google\\\">Google &lt; &gt; &quot; &#39; &amp;</a>\");\n\t}\n\n\t// gh-13768\n\t@Test\n\tpublic void generatesWhenExceptionWithEmptyMessageThenInvalidCredentials() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tfilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/login\");\n\t\trequest.setQueryString(\"error\");\n\t\trequest.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, new BadCredentialsException(null));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, this.chain);\n\t\tassertThat(response.getContentAsString()).contains(\"Invalid credentials\");\n\t}\n\n\t@Test\n\tpublic void generateWhenOneTimeTokenLoginThenOttForm() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter();\n\t\tfilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\tfilter.setOneTimeTokenEnabled(true);\n\t\tfilter.setOneTimeTokenGenerationUrl(\"/ott/authenticate\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(new MockHttpServletRequest(\"GET\", \"/login\"), response, this.chain);\n\t\tassertThat(response.getContentAsString()).contains(\"Request a One-Time Token\");\n\t\tassertThat(response.getContentAsString()).contains(\"\"\"\n\t\t\t\t      <form id=\"ott-form\" class=\"login-form\" method=\"post\" action=\"/ott/authenticate\">\n\t\t\t\t        <h2>Request a One-Time Token</h2>\n\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"ott-username\" class=\"screenreader\">Username</label>\n\t\t\t\t          <input type=\"text\" id=\"ott-username\" name=\"username\" placeholder=\"Username\" required>\n\t\t\t\t        </p>\n\n\t\t\t\t        <button class=\"primary\" type=\"submit\" form=\"ott-form\">Send Token</button>\n\t\t\t\t      </form>\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tpublic void generateWhenOneTimeTokenRequestedThenOttForm() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter();\n\t\tfilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\tfilter.setFormLoginEnabled(true);\n\t\tfilter.setOneTimeTokenEnabled(true);\n\t\tfilter.setOneTimeTokenGenerationUrl(\"/ott/authenticate\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest loginRequest = createLoginRequestFromMissingAuthority(\n\t\t\t\tFactorGrantedAuthority.OTT_AUTHORITY);\n\t\tfilter.doFilter(loginRequest, response, this.chain);\n\t\tassertThat(response.getContentAsString()).contains(\"Request a One-Time Token\");\n\t\tassertThat(response.getContentAsString()).contains(\"\"\"\n\t\t\t\t      <form id=\"ott-form\" class=\"login-form\" method=\"post\" action=\"/ott/authenticate\">\n\t\t\t\t        <h2>Request a One-Time Token</h2>\n\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"ott-username\" class=\"screenreader\">Username</label>\n\t\t\t\t          <input type=\"text\" id=\"ott-username\" name=\"username\" placeholder=\"Username\" required>\n\t\t\t\t        </p>\n\n\t\t\t\t        <button class=\"primary\" type=\"submit\" form=\"ott-form\">Send Token</button>\n\t\t\t\t      </form>\n\t\t\t\t\"\"\");\n\t\tassertThat(response.getContentAsString()).doesNotContain(\"Password\");\n\t}\n\n\t@Test\n\tpublic void generateWhenTwoAuthoritiesRequestedThenBothForms() throws Exception {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter();\n\t\tfilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\tfilter.setFormLoginEnabled(true);\n\t\tfilter.setUsernameParameter(\"username\");\n\t\tfilter.setPasswordParameter(\"password\");\n\t\tfilter.setOneTimeTokenEnabled(true);\n\t\tfilter.setOneTimeTokenGenerationUrl(\"/ott/authenticate\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(TestMockHttpServletRequests\n\t\t\t.get(\"/login?factor.type=ott&factor.type=password&factor.reason=missing&factor.reason=missing\")\n\t\t\t.build(), response, this.chain);\n\t\tassertThat(response.getContentAsString()).contains(\"Request a One-Time Token\");\n\t\tassertThat(response.getContentAsString()).contains(\"\"\"\n\t\t\t\t      <form id=\"ott-form\" class=\"login-form\" method=\"post\" action=\"/ott/authenticate\">\n\t\t\t\t        <h2>Request a One-Time Token</h2>\n\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"ott-username\" class=\"screenreader\">Username</label>\n\t\t\t\t          <input type=\"text\" id=\"ott-username\" name=\"username\" placeholder=\"Username\" required>\n\t\t\t\t        </p>\n\n\t\t\t\t        <button class=\"primary\" type=\"submit\" form=\"ott-form\">Send Token</button>\n\t\t\t\t      </form>\n\t\t\t\t\"\"\");\n\t\tassertThat(response.getContentAsString()).contains(\"Password\");\n\t}\n\n\tprivate MockHttpServletRequest createLoginRequestFromMissingAuthority(String factorAuthority)\n\t\t\tthrows ServletException, IOException {\n\t\tLoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint(\"/login\");\n\t\tList<RequiredFactorError> factorErrors = new ArrayList<>();\n\t\tDelegatingMissingAuthorityAccessDeniedHandler.Builder handlerBldr = DelegatingMissingAuthorityAccessDeniedHandler\n\t\t\t.builder();\n\t\thandlerBldr.addEntryPointFor(entryPoint, factorAuthority);\n\t\tRequiredFactor requiredFactor = RequiredFactor.withAuthority(factorAuthority).build();\n\t\tRequiredFactorError factorError = RequiredFactorError.createMissing(requiredFactor);\n\t\tfactorErrors.add(factorError);\n\t\tDelegatingMissingAuthorityAccessDeniedHandler handler = handlerBldr.build();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFactorAuthorizationDecision decision = new FactorAuthorizationDecision(factorErrors);\n\t\thandler.handle(request, response, new AuthorizationDeniedException(\"\", decision));\n\t\treturn TestMockHttpServletRequests.get(response.getRedirectedUrl()).build();\n\t}\n\n\t@Test\n\tpublic void generateWhenAuthenticatedThenReadOnlyUsername() throws Exception {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter();\n\t\tfilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\tfilter.setFormLoginEnabled(true);\n\t\tfilter.setUsernameParameter(\"username\");\n\t\tfilter.setPasswordParameter(\"password\");\n\t\tfilter.setOneTimeTokenEnabled(true);\n\t\tfilter.setOneTimeTokenGenerationUrl(\"/ott/authenticate\");\n\t\tfilter.setSecurityContextHolderStrategy(strategy);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(TestAuthentication.authenticatedUser()));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(TestMockHttpServletRequests.get(\"/login\").build(), response, this.chain);\n\t\tassertThat(response.getContentAsString()).contains(\"Request a One-Time Token\");\n\t\tassertThat(response.getContentAsString()).contains(\n\t\t\t\t\"\"\"\n\t\t\t\t\t\t<input type=\"text\" id=\"ott-username\" name=\"username\" value=\"user\" placeholder=\"Username\" required readonly>\n\t\t\t\t\t\t\"\"\");\n\t\tassertThat(response.getContentAsString()).contains(\"\"\"\n\t\t\t\t<input type=\"text\" id=\"username\" name=\"username\" value=\"user\" placeholder=\"Username\" required readonly>\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid generatesThenRenders() throws ServletException, IOException {\n\t\tDefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(\n\t\t\t\tnew UsernamePasswordAuthenticationFilter());\n\t\tfilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);\n\t\tfilter.setSaml2LoginEnabled(true);\n\t\tString clientName = \"Google < > \\\" \\' &\";\n\t\tfilter.setSaml2AuthenticationUrlToProviderName(Collections.singletonMap(\"/saml/sso/google\", clientName));\n\t\tfilter.setOauth2LoginEnabled(true);\n\t\tclientName = \"Google < > \\\" \\' &\";\n\t\tfilter.setOauth2AuthenticationUrlToClientName(\n\t\t\t\tCollections.singletonMap(\"/oauth2/authorization/google\", clientName));\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/login\");\n\t\trequest.setQueryString(\"error\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trequest.getSession()\n\t\t\t.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, new BadCredentialsException(\"Bad credentials\"));\n\t\tfilter.doFilter(request, response, this.chain);\n\t\tassertThat(response.getContentAsString()).isEqualTo(\"\"\"\n\t\t\t\t<!DOCTYPE html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t  <head>\n\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t  </head>\n\t\t\t\t  <body>\n\t\t\t\t    <div class=\"content\">\n\t\t\t\t      <form class=\"login-form\" method=\"post\" action=\"null\">\n\t\t\t\t        <h2>Please sign in</h2>\n\t\t\t\t<div class=\"alert alert-danger\" role=\"alert\">Invalid credentials</div>\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t\t          <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n\t\t\t\t        </p>\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t\t          <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n\t\t\t\t        </p>\n\n\n\t\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t\t      </form>\n\n\t\t\t\t<h2>Login with OAuth 2.0</h2>\n\t\t\t\t<div class=\"alert alert-danger\" role=\"alert\">Invalid credentials</div>\n\t\t\t\t<table class=\"table table-striped\">\n\t\t\t\t  <tr><td><a href=\"/oauth2/authorization/google\">Google &lt; &gt; &quot; &#39; &amp;</a></td></tr>\n\t\t\t\t</table>\n\t\t\t\t<h2>Login with SAML 2.0</h2>\n\t\t\t\t<div class=\"alert alert-danger\" role=\"alert\">Invalid credentials</div>\n\t\t\t\t<table class=\"table table-striped\">\n\t\t\t\t  <tr><td><a href=\"/saml/sso/google\">Google &lt; &gt; &quot; &#39; &amp;</a></td></tr>\n\t\t\t\t</table>\n\t\t\t\t    </div>\n\t\t\t\t  </body>\n\t\t\t\t</html>\"\"\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/DelegatingAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.test.web.CodecTestUtils;\nimport org.springframework.security.web.authentication.www.BasicAuthenticationConverter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * Tests for {@link DelegatingAuthenticationConverter}.\n *\n * @author Max Batischev\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingAuthenticationConverterTests {\n\n\tprivate static final String X_AUTH_TOKEN_HEADER = \"X-Auth-Token\";\n\n\tprivate static final String TEST_X_AUTH_TOKEN = \"test-x-auth-token\";\n\n\tprivate static final String TEST_CUSTOM_PRINCIPAL = \"test_custom_principal\";\n\n\tprivate static final String TEST_CUSTOM_CREDENTIALS = \"test_custom_credentials\";\n\n\tprivate static final String TEST_BASIC_CREDENTIALS = \"username:password\";\n\n\tprivate static final String INVALID_BASIC_CREDENTIALS = \"invalid_credentials\";\n\n\tprivate DelegatingAuthenticationConverter converter;\n\n\t@Mock\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;\n\n\t@Test\n\tpublic void requestWhenBasicAuthorizationHeaderIsPresentThenAuthenticates() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + CodecTestUtils.encodeBase64(TEST_BASIC_CREDENTIALS));\n\t\tthis.converter = new DelegatingAuthenticationConverter(\n\t\t\t\tnew BasicAuthenticationConverter(this.authenticationDetailsSource),\n\t\t\t\tnew TestNullableAuthenticationConverter());\n\n\t\tAuthentication authentication = this.converter.convert(request);\n\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getName()).isEqualTo(\"username\");\n\t}\n\n\t@Test\n\tpublic void requestWhenXAuthHeaderIsPresentThenAuthenticates() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(X_AUTH_TOKEN_HEADER, TEST_X_AUTH_TOKEN);\n\t\tthis.converter = new DelegatingAuthenticationConverter(new TestAuthenticationConverter(),\n\t\t\t\tnew TestNullableAuthenticationConverter());\n\n\t\tAuthentication authentication = this.converter.convert(request);\n\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getName()).isEqualTo(TEST_CUSTOM_PRINCIPAL);\n\t}\n\n\t@Test\n\tpublic void requestWhenXAuthHeaderIsPresentThenDoesntAuthenticate() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(X_AUTH_TOKEN_HEADER, TEST_X_AUTH_TOKEN);\n\t\tthis.converter = new DelegatingAuthenticationConverter(new TestNullableAuthenticationConverter());\n\n\t\tAuthentication authentication = this.converter.convert(request);\n\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenInvalidBasicAuthorizationTokenThenError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + CodecTestUtils.encodeBase64(INVALID_BASIC_CREDENTIALS));\n\t\tthis.converter = new DelegatingAuthenticationConverter(\n\t\t\t\tnew BasicAuthenticationConverter(this.authenticationDetailsSource),\n\t\t\t\tnew TestNullableAuthenticationConverter());\n\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.converter.convert(request));\n\t}\n\n\tprivate static class TestAuthenticationConverter implements AuthenticationConverter {\n\n\t\t@Override\n\t\tpublic Authentication convert(HttpServletRequest request) {\n\t\t\tString header = request.getHeader(X_AUTH_TOKEN_HEADER);\n\t\t\tif (header != null) {\n\t\t\t\treturn new TestingAuthenticationToken(TEST_CUSTOM_PRINCIPAL, TEST_CUSTOM_CREDENTIALS);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate static class TestNullableAuthenticationConverter implements AuthenticationConverter {\n\n\t\t@Override\n\t\tpublic Authentication convert(HttpServletRequest request) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointContextTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(\n\t\tlocations = \"classpath:org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTest-context.xml\")\npublic class DelegatingAuthenticationEntryPointContextTests {\n\n\t@Autowired\n\tprivate DelegatingAuthenticationEntryPoint daep;\n\n\t@Autowired\n\t@Qualifier(\"firstAEP\")\n\tprivate AuthenticationEntryPoint firstAEP;\n\n\t@Autowired\n\t@Qualifier(\"defaultAEP\")\n\tprivate AuthenticationEntryPoint defaultAEP;\n\n\t@Test\n\t@DirtiesContext\n\tpublic void testFirstAEP() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRemoteAddr(\"192.168.1.10\");\n\t\trequest.addHeader(\"User-Agent\", \"Mozilla/5.0\");\n\t\tthis.daep.commence(request, null, null);\n\t\tverify(this.firstAEP).commence(request, null, null);\n\t\tverify(this.defaultAEP, never()).commence(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AuthenticationException.class));\n\t}\n\n\t@Test\n\t@DirtiesContext\n\tpublic void testDefaultAEP() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRemoteAddr(\"192.168.1.10\");\n\t\tthis.daep.commence(request, null, null);\n\t\tverify(this.defaultAEP).commence(request, null, null);\n\t\tverify(this.firstAEP, never()).commence(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(AuthenticationException.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.List;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcherEntry;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Test class for {@link DelegatingAuthenticationEntryPoint}\n *\n * @author Mike Wiesner\n * @since 3.0.2\n * @version $Id:$\n */\npublic class DelegatingAuthenticationEntryPointTests {\n\n\tprivate DelegatingAuthenticationEntryPoint daep;\n\n\tprivate LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints;\n\n\tprivate AuthenticationEntryPoint defaultEntryPoint;\n\n\tprivate HttpServletRequest request = new MockHttpServletRequest();\n\n\t@BeforeEach\n\tpublic void before() {\n\t\tthis.defaultEntryPoint = mock(AuthenticationEntryPoint.class);\n\t\tthis.entryPoints = new LinkedHashMap<>();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void testDefaultEntryPoint() throws Exception {\n\t\tAuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher firstRM = mock(RequestMatcher.class);\n\t\tgiven(firstRM.matches(this.request)).willReturn(false);\n\t\tthis.entryPoints.put(firstRM, firstAEP);\n\t\tthis.daep = new DelegatingAuthenticationEntryPoint(this.entryPoints);\n\t\tthis.daep.setDefaultEntryPoint(this.defaultEntryPoint);\n\t\tthis.daep.commence(this.request, null, null);\n\t\tverify(this.defaultEntryPoint).commence(this.request, null, null);\n\t\tverify(firstAEP, never()).commence(this.request, null, null);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void testFirstEntryPoint() throws Exception {\n\t\tAuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher firstRM = mock(RequestMatcher.class);\n\t\tAuthenticationEntryPoint secondAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher secondRM = mock(RequestMatcher.class);\n\t\tgiven(firstRM.matches(this.request)).willReturn(true);\n\t\tthis.entryPoints.put(firstRM, firstAEP);\n\t\tthis.entryPoints.put(secondRM, secondAEP);\n\t\tthis.daep = new DelegatingAuthenticationEntryPoint(this.entryPoints);\n\t\tthis.daep.setDefaultEntryPoint(this.defaultEntryPoint);\n\t\tthis.daep.commence(this.request, null, null);\n\t\tverify(firstAEP).commence(this.request, null, null);\n\t\tverify(secondAEP, never()).commence(this.request, null, null);\n\t\tverify(this.defaultEntryPoint, never()).commence(this.request, null, null);\n\t\tverify(secondRM, never()).matches(this.request);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void testSecondEntryPoint() throws Exception {\n\t\tAuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher firstRM = mock(RequestMatcher.class);\n\t\tAuthenticationEntryPoint secondAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher secondRM = mock(RequestMatcher.class);\n\t\tgiven(firstRM.matches(this.request)).willReturn(false);\n\t\tgiven(secondRM.matches(this.request)).willReturn(true);\n\t\tthis.entryPoints.put(firstRM, firstAEP);\n\t\tthis.entryPoints.put(secondRM, secondAEP);\n\t\tthis.daep = new DelegatingAuthenticationEntryPoint(this.entryPoints);\n\t\tthis.daep.setDefaultEntryPoint(this.defaultEntryPoint);\n\t\tthis.daep.commence(this.request, null, null);\n\t\tverify(secondAEP).commence(this.request, null, null);\n\t\tverify(firstAEP, never()).commence(this.request, null, null);\n\t\tverify(this.defaultEntryPoint, never()).commence(this.request, null, null);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void constructorAepListWhenNullEntryPoints() {\n\t\tList<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new DelegatingAuthenticationEntryPoint(mock(AuthenticationEntryPoint.class), entryPoints));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void constructorAepListWhenEmptyEntryPoints() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingAuthenticationEntryPoint(mock(AuthenticationEntryPoint.class),\n\t\t\t\t\tCollections.emptyList()));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void constructorAepListWhenNullDefaultEntryPoint() {\n\t\tAuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher matcher = mock(RequestMatcher.class);\n\t\tList<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints = List\n\t\t\t.of(new RequestMatcherEntry<>(matcher, entryPoint));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingAuthenticationEntryPoint(null, entryPoints));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void constructorAepVargsWhenNullEntryPoints() {\n\t\tRequestMatcherEntry<AuthenticationEntryPoint>[] entryPoints = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new DelegatingAuthenticationEntryPoint(mock(AuthenticationEntryPoint.class), entryPoints));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void constructorAepVargsWhenEmptyEntryPoints() {\n\t\tRequestMatcherEntry<AuthenticationEntryPoint>[] entryPoints = new RequestMatcherEntry[0];\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new DelegatingAuthenticationEntryPoint(mock(AuthenticationEntryPoint.class), entryPoints));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void constructorAepVargsWhenNullDefaultEntryPoint() {\n\t\tAuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher matcher = mock(RequestMatcher.class);\n\t\tRequestMatcherEntry<AuthenticationEntryPoint>[] entryPoints = new RequestMatcherEntry[] {\n\t\t\t\tnew RequestMatcherEntry<>(matcher, entryPoint) };\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingAuthenticationEntryPoint(null, entryPoints));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void commenceWhenNoMatchThenDefaultEntryPoint() throws Exception {\n\t\tAuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher firstRM = mock(RequestMatcher.class);\n\t\tgiven(firstRM.matches(this.request)).willReturn(false);\n\t\tRequestMatcherEntry<AuthenticationEntryPoint> entry = new RequestMatcherEntry<>(firstRM, firstAEP);\n\t\tthis.daep = new DelegatingAuthenticationEntryPoint(this.defaultEntryPoint, entry);\n\t\tthis.daep.commence(this.request, null, null);\n\t\tverify(this.defaultEntryPoint).commence(this.request, null, null);\n\t\tverify(firstAEP, never()).commence(this.request, null, null);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void commenceWhenMatchFirstEntryPointThenOthersNotInvoked() throws Exception {\n\t\tAuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher firstRM = mock(RequestMatcher.class);\n\t\tgiven(firstRM.matches(this.request)).willReturn(true);\n\t\tRequestMatcherEntry<AuthenticationEntryPoint> firstEntry = new RequestMatcherEntry<>(firstRM, firstAEP);\n\t\tAuthenticationEntryPoint secondAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher secondRM = mock(RequestMatcher.class);\n\t\tgiven(secondRM.matches(this.request)).willReturn(false);\n\t\tRequestMatcherEntry<AuthenticationEntryPoint> secondEntry = new RequestMatcherEntry<>(firstRM, firstAEP);\n\t\tthis.daep = new DelegatingAuthenticationEntryPoint(this.defaultEntryPoint, firstEntry, secondEntry);\n\t\tthis.daep.commence(this.request, null, null);\n\t\tverify(firstAEP).commence(this.request, null, null);\n\t\tverify(secondAEP, never()).commence(this.request, null, null);\n\t\tverify(this.defaultEntryPoint, never()).commence(this.request, null, null);\n\t\tverify(secondRM, never()).matches(this.request);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tpublic void commenceWhenSecondMatchesThenDefaultNotInvoked() throws Exception {\n\t\tAuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher firstRM = mock(RequestMatcher.class);\n\t\tgiven(firstRM.matches(this.request)).willReturn(false);\n\t\tRequestMatcherEntry<AuthenticationEntryPoint> firstEntry = new RequestMatcherEntry<>(firstRM, firstAEP);\n\t\tAuthenticationEntryPoint secondAEP = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher secondRM = mock(RequestMatcher.class);\n\t\tgiven(secondRM.matches(this.request)).willReturn(true);\n\t\tRequestMatcherEntry<AuthenticationEntryPoint> secondEntry = new RequestMatcherEntry<>(secondRM, secondAEP);\n\t\tthis.daep = new DelegatingAuthenticationEntryPoint(this.defaultEntryPoint, firstEntry, secondEntry);\n\t\tthis.daep.commence(this.request, null, null);\n\t\tverify(secondAEP).commence(this.request, null, null);\n\t\tverify(firstAEP, never()).commence(this.request, null, null);\n\t\tverify(this.defaultEntryPoint, never()).commence(this.request, null, null);\n\t}\n\n\t@Test\n\tvoid builderWhenDefaultNullAndSingleEntryPointThenReturnsSingle() {\n\t\tAuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class);\n\n\t\tAuthenticationEntryPoint result = DelegatingAuthenticationEntryPoint.builder()\n\t\t\t.addEntryPointFor(entryPoint, mock(RequestMatcher.class))\n\t\t\t.build();\n\n\t\tassertThat(result).isEqualTo(entryPoint);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid builderWhenDefaultNullThenFirstIsDefault() throws ServletException, IOException {\n\t\tAuthenticationEntryPoint firstEntryPoint = mock(AuthenticationEntryPoint.class);\n\t\tAuthenticationEntryPoint secondEntryPoint = mock(AuthenticationEntryPoint.class);\n\t\tRequestMatcher neverMatch = mock(RequestMatcher.class);\n\t\tgiven(neverMatch.matches(this.request)).willReturn(false);\n\t\tAuthenticationEntryPoint result = DelegatingAuthenticationEntryPoint.builder()\n\t\t\t.addEntryPointFor(firstEntryPoint, neverMatch)\n\t\t\t.addEntryPointFor(secondEntryPoint, neverMatch)\n\t\t\t.build();\n\n\t\tresult.commence(this.request, null, null);\n\n\t\tverify(firstEntryPoint).commence(any(), any(), any());\n\t\tverifyNoInteractions(secondEntryPoint);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid builderWhenDefaultAndEmptyEntryPointsThenReturnsDefault() {\n\t\tAuthenticationEntryPoint defaultEntryPoint = mock(AuthenticationEntryPoint.class);\n\n\t\tAuthenticationEntryPoint result = DelegatingAuthenticationEntryPoint.builder()\n\t\t\t.defaultEntryPoint(defaultEntryPoint)\n\t\t\t.build();\n\n\t\tassertThat(result).isEqualTo(defaultEntryPoint);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid builderWhenNoEntryPointsThenIllegalStateException() {\n\t\tDelegatingAuthenticationEntryPoint.Builder builder = DelegatingAuthenticationEntryPoint.builder();\n\t\tassertThatIllegalStateException().isThrownBy(builder::build);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/DelegatingAuthenticationFailureHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.util.LinkedHashMap;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.AccountStatusException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Test class for\n * {@link org.springframework.security.web.authentication.DelegatingAuthenticationFailureHandler}\n *\n * @author Kazuki shimizu\n * @since 4.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingAuthenticationFailureHandlerTests {\n\n\t@Mock\n\tprivate AuthenticationFailureHandler handler1;\n\n\t@Mock\n\tprivate AuthenticationFailureHandler handler2;\n\n\t@Mock\n\tprivate AuthenticationFailureHandler defaultHandler;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\tprivate LinkedHashMap<Class<? extends AuthenticationException>, AuthenticationFailureHandler> handlers;\n\n\tprivate DelegatingAuthenticationFailureHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.handlers = new LinkedHashMap<>();\n\t}\n\n\t@Test\n\tpublic void handleByDefaultHandler() throws Exception {\n\t\tthis.handlers.put(BadCredentialsException.class, this.handler1);\n\t\tthis.handler = new DelegatingAuthenticationFailureHandler(this.handlers, this.defaultHandler);\n\t\tAuthenticationException exception = new AccountExpiredException(\"\");\n\t\tthis.handler.onAuthenticationFailure(this.request, this.response, exception);\n\t\tverifyNoMoreInteractions(this.handler1, this.handler2);\n\t\tverify(this.defaultHandler).onAuthenticationFailure(this.request, this.response, exception);\n\t}\n\n\t@Test\n\tpublic void handleByMappedHandlerWithSameType() throws Exception {\n\t\tthis.handlers.put(BadCredentialsException.class, this.handler1); // same type\n\t\tthis.handlers.put(AccountStatusException.class, this.handler2);\n\t\tthis.handler = new DelegatingAuthenticationFailureHandler(this.handlers, this.defaultHandler);\n\t\tAuthenticationException exception = new BadCredentialsException(\"\");\n\t\tthis.handler.onAuthenticationFailure(this.request, this.response, exception);\n\t\tverifyNoMoreInteractions(this.handler2, this.defaultHandler);\n\t\tverify(this.handler1).onAuthenticationFailure(this.request, this.response, exception);\n\t}\n\n\t@Test\n\tpublic void handleByMappedHandlerWithSuperType() throws Exception {\n\t\tthis.handlers.put(BadCredentialsException.class, this.handler1);\n\t\tthis.handlers.put(AccountStatusException.class, this.handler2); // super type of\n\t\t// CredentialsExpiredException\n\t\tthis.handler = new DelegatingAuthenticationFailureHandler(this.handlers, this.defaultHandler);\n\t\tAuthenticationException exception = new CredentialsExpiredException(\"\");\n\t\tthis.handler.onAuthenticationFailure(this.request, this.response, exception);\n\t\tverifyNoMoreInteractions(this.handler1, this.defaultHandler);\n\t\tverify(this.handler2).onAuthenticationFailure(this.request, this.response, exception);\n\t}\n\n\t@Test\n\tpublic void handlersIsNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingAuthenticationFailureHandler(null, this.defaultHandler))\n\t\t\t.withMessage(\"handlers cannot be null or empty\");\n\t}\n\n\t@Test\n\tpublic void handlersIsEmpty() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingAuthenticationFailureHandler(this.handlers, this.defaultHandler))\n\t\t\t.withMessage(\"handlers cannot be null or empty\");\n\t}\n\n\t@Test\n\tpublic void defaultHandlerIsNull() {\n\t\tthis.handlers.put(BadCredentialsException.class, this.handler1);\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingAuthenticationFailureHandler(this.handlers, null))\n\t\t\t.withMessage(\"defaultHandler cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ExceptionMappingAuthenticationFailureHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.util.HashMap;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.BadCredentialsException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\npublic class ExceptionMappingAuthenticationFailureHandlerTests {\n\n\t@Test\n\tpublic void defaultTargetUrlIsUsedIfNoMappingExists() throws Exception {\n\t\tExceptionMappingAuthenticationFailureHandler fh = new ExceptionMappingAuthenticationFailureHandler();\n\t\tfh.setDefaultFailureUrl(\"/failed\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfh.onAuthenticationFailure(new MockHttpServletRequest(), response, new BadCredentialsException(\"\"));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/failed\");\n\t}\n\n\t@Test\n\tpublic void exceptionMapIsUsedIfMappingExists() throws Exception {\n\t\tExceptionMappingAuthenticationFailureHandler fh = new ExceptionMappingAuthenticationFailureHandler();\n\t\tHashMap<String, String> mapping = new HashMap<>();\n\t\tmapping.put(\"org.springframework.security.authentication.BadCredentialsException\", \"/badcreds\");\n\t\tfh.setExceptionMappings(mapping);\n\t\tfh.setDefaultFailureUrl(\"/failed\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfh.onAuthenticationFailure(new MockHttpServletRequest(), response, new BadCredentialsException(\"\"));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/badcreds\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ForwardAuthenticaionSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * <p>\n * Forward Authentication Failure Handler Tests\n * </p>\n *\n * @author Shazin Sadakath\n * @since 4.1\n */\npublic class ForwardAuthenticaionSuccessHandlerTests {\n\n\t@Test\n\tpublic void invalidForwardUrl() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ForwardAuthenticationSuccessHandler(\"aaa\"));\n\t}\n\n\t@Test\n\tpublic void emptyForwardUrl() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ForwardAuthenticationSuccessHandler(\"\"));\n\t}\n\n\t@Test\n\tpublic void responseIsForwarded() throws Exception {\n\t\tForwardAuthenticationSuccessHandler fash = new ForwardAuthenticationSuccessHandler(\"/forwardUrl\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\tfash.onAuthenticationSuccess(request, response, authentication);\n\t\tassertThat(response.getForwardedUrl()).isEqualTo(\"/forwardUrl\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ForwardAuthenticationFailureHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.WebAttributes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * <p>\n * Forward Authentication Failure Handler Tests\n * </p>\n *\n * @author Shazin Sadakath @since4.1\n */\npublic class ForwardAuthenticationFailureHandlerTests {\n\n\t@Test\n\tpublic void invalidForwardUrl() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ForwardAuthenticationFailureHandler(\"aaa\"));\n\t}\n\n\t@Test\n\tpublic void emptyForwardUrl() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ForwardAuthenticationFailureHandler(\"\"));\n\t}\n\n\t@Test\n\tpublic void responseIsForwarded() throws Exception {\n\t\tForwardAuthenticationFailureHandler fafh = new ForwardAuthenticationFailureHandler(\"/forwardUrl\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationException e = mock(AuthenticationException.class);\n\t\tfafh.onAuthenticationFailure(request, response, e);\n\t\tassertThat(response.getForwardedUrl()).isEqualTo(\"/forwardUrl\");\n\t\tassertThat(request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).isEqualTo(e);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/HttpMessageConverterAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.BDDMockito;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SimpleSavedRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.verify;\n\n@ExtendWith(MockitoExtension.class)\nclass HttpMessageConverterAuthenticationSuccessHandlerTests {\n\n\t@Mock\n\tprivate HttpMessageConverter converter;\n\n\t@Mock\n\tprivate RequestCache requestCache;\n\n\tprivate HttpMessageConverterAuthenticationSuccessHandler handler = new HttpMessageConverterAuthenticationSuccessHandler();\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate MockHttpServletResponse response = new MockHttpServletResponse();\n\n\tprivate Authentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\n\t@Test\n\tvoid setConverterWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setConverter(null));\n\t}\n\n\t@Test\n\tvoid setRequestCacheWhenNullThenThrowIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setRequestCache(null));\n\t}\n\n\t@Test\n\tvoid onAuthenticationSuccessWhenDefaultsThenContextRoot() throws Exception {\n\t\tthis.handler.onAuthenticationSuccess(this.request, this.response, this.authentication);\n\t\tString body = this.response.getContentAsString();\n\t\tJSONAssert.assertEquals(\"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"redirectUrl\" : \"/\",\n\t\t\t\t\t\"authenticated\": true\n\t\t\t\t}\"\"\", body, false);\n\t}\n\n\t@Test\n\tvoid onAuthenticationSuccessWhenSavedRequestThenInResponse() throws Exception {\n\t\tSimpleSavedRequest savedRequest = new SimpleSavedRequest(\"/redirect\");\n\t\tgiven(this.requestCache.getRequest(this.request, this.response)).willReturn(savedRequest);\n\t\tthis.handler.setRequestCache(this.requestCache);\n\t\tthis.handler.onAuthenticationSuccess(this.request, this.response, this.authentication);\n\t\tverify(this.requestCache).removeRequest(this.request, this.response);\n\t\tString body = this.response.getContentAsString();\n\t\tJSONAssert.assertEquals(\"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"redirectUrl\" : \"/redirect\",\n\t\t\t\t\t\"authenticated\": true\n\t\t\t\t}\"\"\", body, false);\n\t}\n\n\t@Test\n\tvoid onAuthenticationSuccessWhenCustomConverterThenInResponse() throws Exception {\n\t\tSimpleSavedRequest savedRequest = new SimpleSavedRequest(\"/redirect\");\n\t\tgiven(this.requestCache.getRequest(this.request, this.response)).willReturn(savedRequest);\n\t\tString expectedBody = \"Custom!\";\n\t\tBDDMockito.doAnswer((invocation) -> {\n\t\t\tthis.response.getWriter().write(expectedBody);\n\t\t\treturn null;\n\t\t}).when(this.converter).write(any(), any(), any());\n\t\tthis.handler.setRequestCache(this.requestCache);\n\t\tthis.handler.setConverter(this.converter);\n\t\tthis.handler.onAuthenticationSuccess(this.request, this.response, this.authentication);\n\t\tString body = this.response.getContentAsString();\n\t\tassertThat(body).isEqualTo(expectedBody);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/HttpStatusEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @since 4.0\n */\npublic class HttpStatusEntryPointTests {\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response;\n\n\tAuthenticationException authException;\n\n\tHttpStatusEntryPoint entryPoint;\n\n\t@SuppressWarnings(\"serial\")\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.authException = new AuthenticationException(\"\") {\n\t\t};\n\t\tthis.entryPoint = new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void constructorNullStatus() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new HttpStatusEntryPoint(null));\n\t}\n\n\t@Test\n\tpublic void unauthorized() throws Exception {\n\t\tthis.entryPoint.commence(this.request, this.response, this.authException);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.PortMapperImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests {@link LoginUrlAuthenticationEntryPoint}.\n *\n * @author Ben Alex\n * @author colin sampaleanu\n */\npublic class LoginUrlAuthenticationEntryPointTests {\n\n\t@Test\n\tpublic void testDetectsMissingLoginFormUrl() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new LoginUrlAuthenticationEntryPoint(null));\n\t}\n\n\t@Test\n\tpublic void testDetectsMissingPortMapper() {\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/login\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ep.setPortMapper(null));\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tassertThat(ep.getLoginFormUrl()).isEqualTo(\"/hello\");\n\t\tassertThat(ep.getPortMapper() != null).isTrue();\n\t\tep.setForceHttps(false);\n\t\tassertThat(ep.isForceHttps()).isFalse();\n\t\tep.setForceHttps(true);\n\t\tassertThat(ep.isForceHttps()).isTrue();\n\t\tassertThat(ep.isUseForward()).isFalse();\n\t\tep.setUseForward(true);\n\t\tassertThat(ep.isUseForward()).isTrue();\n\t}\n\n\t@Test\n\tpublic void testHttpsOperationFromOriginalHttpUrl() throws Exception {\n\t\tMockHttpServletRequest request = get(\"http://127.0.0.1\").requestUri(\"/bigWebApp\", \"/some_path\", null).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tPortMapperImpl portMapper = new PortMapperImpl();\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setPortMapper(portMapper);\n\t\tep.setForceHttps(true);\n\t\tep.afterPropertiesSet();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://127.0.0.1/bigWebApp/hello\");\n\t\trequest.setServerPort(8080);\n\t\tresponse = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://127.0.0.1:8443/bigWebApp/hello\");\n\t\t// Now test an unusual custom HTTP:HTTPS is handled properly\n\t\trequest.setServerPort(8888);\n\t\tportMapper.getTranslatedPortMappings().put(8888, 8443);\n\t\tresponse = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://127.0.0.1:8443/bigWebApp/hello\");\n\t\tportMapper = new PortMapperImpl();\n\t\tMap<String, String> map = new HashMap<>();\n\t\tmap.put(\"8888\", \"9999\");\n\t\tportMapper.setPortMappings(map);\n\t\tep.setPortMapper(portMapper);\n\t\tresponse = new MockHttpServletResponse();\n\t\tep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.setForceHttps(true);\n\t\tep.setPortMapper(portMapper);\n\t\tep.afterPropertiesSet();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://127.0.0.1:9999/bigWebApp/hello\");\n\t}\n\n\t@Test\n\tpublic void testHttpsOperationFromOriginalHttpsUrl() throws Exception {\n\t\tMockHttpServletRequest request = get(\"https://www.example.com:443\").requestUri(\"/bigWebApp\", \"/some_path\", null)\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setFavorRelativeUris(false);\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.setForceHttps(true);\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.afterPropertiesSet();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://www.example.com/bigWebApp/hello\");\n\t\trequest.setServerPort(8443);\n\t\tresponse = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://www.example.com:8443/bigWebApp/hello\");\n\t\t// access to https via http port\n\t\trequest.setServerPort(8080);\n\t\tresponse = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://www.example.com:8443/bigWebApp/hello\");\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() throws Exception {\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.afterPropertiesSet();\n\t\tMockHttpServletRequest request = get().requestUri(\"/bigWebApp\", \"/some_path\", null).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/bigWebApp/hello\");\n\t}\n\n\t@Test\n\tpublic void testOperationWhenHttpsRequestsButHttpsPortUnknown() throws Exception {\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setFavorRelativeUris(false);\n\t\tep.setForceHttps(true);\n\t\tep.afterPropertiesSet();\n\t\tMockHttpServletRequest request = get(\"http://localhost:8888\").requestUri(\"/bigWebApp\", \"/some_path\", null)\n\t\t\t.build(); // NB: Port we can't resolve\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\t// Response doesn't switch to HTTPS, as we didn't know HTTP port 8888 to HTTP port\n\t\t// mapping\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"http://localhost:8888/bigWebApp/hello\");\n\t}\n\n\t@Test\n\tpublic void testServerSideRedirectWithoutForceHttpsForwardsToLoginPage() throws Exception {\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setUseForward(true);\n\t\tep.afterPropertiesSet();\n\t\tMockHttpServletRequest request = get().requestUri(\"/bigWebApp\", \"/some_path\", null).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getForwardedUrl()).isEqualTo(\"/hello\");\n\t}\n\n\t@Test\n\tpublic void testServerSideRedirectWithForceHttpsRedirectsCurrentRequest() throws Exception {\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setUseForward(true);\n\t\tep.setForceHttps(true);\n\t\tep.afterPropertiesSet();\n\t\tMockHttpServletRequest request = get(\"http://127.0.0.1\").requestUri(\"/bigWebApp\", \"/some_path\", null).build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://127.0.0.1/bigWebApp/some_path\");\n\t}\n\n\t// SEC-1498\n\t@Test\n\tpublic void absoluteLoginFormUrlIsSupported() throws Exception {\n\t\tfinal String loginFormUrl = \"https://somesite.com/login\";\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(loginFormUrl);\n\t\tep.afterPropertiesSet();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.commence(new MockHttpServletRequest(\"GET\", \"/someUrl\"), response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(loginFormUrl);\n\t}\n\n\t@Test\n\tpublic void absoluteLoginFormUrlCantBeUsedWithForwarding() throws Exception {\n\t\tfinal String loginFormUrl = \"https://somesite.com/login\";\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"https://somesite.com/login\");\n\t\tep.setUseForward(true);\n\t\tassertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void commenceWhenFavorRelativeUrisThenHttpsSchemeNotIncluded() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/some_path\");\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"www.example.com\");\n\t\trequest.setContextPath(\"/bigWebApp\");\n\t\trequest.setServerPort(443);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setFavorRelativeUris(true);\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.setForceHttps(true);\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.afterPropertiesSet();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/bigWebApp/hello\");\n\t\trequest.setServerPort(8443);\n\t\tresponse = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/bigWebApp/hello\");\n\t\t// access to https via http port\n\t\trequest.setServerPort(8080);\n\t\tresponse = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/bigWebApp/hello\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenFavorRelativeUrisThenHttpSchemeNotIncluded() throws Exception {\n\t\tLoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint(\"/hello\");\n\t\tep.setFavorRelativeUris(true);\n\t\tep.setPortMapper(new PortMapperImpl());\n\t\tep.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/some_path\");\n\t\trequest.setContextPath(\"/bigWebApp\");\n\t\trequest.setScheme(\"http\");\n\t\trequest.setServerName(\"localhost\");\n\t\trequest.setContextPath(\"/bigWebApp\");\n\t\trequest.setServerPort(80);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.commence(request, response, null);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/bigWebApp/hello\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/NoOpAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\nclass NoOpAuthenticationEntryPointTests {\n\n\tprivate final NoOpAuthenticationEntryPoint authenticationEntryPoint = new NoOpAuthenticationEntryPoint();\n\n\t@Test\n\tvoid commenceWhenInvokedThenDoesNothing() throws Exception {\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tAuthenticationException exception = mock(AuthenticationException.class);\n\t\tthis.authenticationEntryPoint.commence(request, response, exception);\n\t\tverifyNoInteractions(request, response, exception);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/RequestMatcherDelegatingAuthenticationManagerResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests for {@link RequestMatcherDelegatingAuthenticationManagerResolverTests}\n *\n * @author Josh Cummings\n */\npublic class RequestMatcherDelegatingAuthenticationManagerResolverTests {\n\n\tprivate AuthenticationManager one = mock(AuthenticationManager.class);\n\n\tprivate AuthenticationManager two = mock(AuthenticationManager.class);\n\n\t@Test\n\tpublic void resolveWhenMatchesThenReturnsAuthenticationManager() {\n\t\tRequestMatcherDelegatingAuthenticationManagerResolver resolver = RequestMatcherDelegatingAuthenticationManagerResolver\n\t\t\t.builder()\n\t\t\t.add(pathPattern(\"/one/**\"), this.one)\n\t\t\t.add(pathPattern(\"/two/**\"), this.two)\n\t\t\t.build();\n\n\t\tMockHttpServletRequest request = get(\"/one/location\").build();\n\t\tassertThat(resolver.resolve(request)).isEqualTo(this.one);\n\t}\n\n\t@Test\n\tpublic void resolveWhenDoesNotMatchThenReturnsDefaultAuthenticationManager() {\n\t\tRequestMatcherDelegatingAuthenticationManagerResolver resolver = RequestMatcherDelegatingAuthenticationManagerResolver\n\t\t\t.builder()\n\t\t\t.add(pathPattern(\"/one/**\"), this.one)\n\t\t\t.add(pathPattern(\"/two/**\"), this.two)\n\t\t\t.build();\n\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/wrong/location\");\n\t\tAuthenticationManager authenticationManager = resolver.resolve(request);\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"principal\", \"creds\");\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> authenticationManager.authenticate(authentication));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/SavedRequestAwareAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\npublic class SavedRequestAwareAuthenticationSuccessHandlerTests {\n\n\t@Test\n\tpublic void defaultUrlMuststartWithSlashOrHttpScheme() {\n\t\tSavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();\n\t\thandler.setDefaultTargetUrl(\"/acceptableRelativeUrl\");\n\t\thandler.setDefaultTargetUrl(\"https://some.site.org/index.html\");\n\t\thandler.setDefaultTargetUrl(\"https://some.site.org/index.html\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> handler.setDefaultTargetUrl(\"missingSlash\"));\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessHasSavedRequest() throws Exception {\n\t\tString redirectUrl = \"http://localhost/appcontext/page\";\n\t\tRedirectStrategy redirectStrategy = mock(RedirectStrategy.class);\n\t\tRequestCache requestCache = mock(RequestCache.class);\n\t\tSavedRequest savedRequest = mock(SavedRequest.class);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tgiven(savedRequest.getRedirectUrl()).willReturn(redirectUrl);\n\t\tgiven(requestCache.getRequest(request, response)).willReturn(savedRequest);\n\t\tSavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();\n\t\thandler.setRequestCache(requestCache);\n\t\thandler.setRedirectStrategy(redirectStrategy);\n\t\thandler.onAuthenticationSuccess(request, response, mock(Authentication.class));\n\t\tverify(redirectStrategy).sendRedirect(request, response, redirectUrl);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationFailureHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.WebAttributes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class SimpleUrlAuthenticationFailureHandlerTests {\n\n\t@Test\n\tpublic void error401IsReturnedIfNoUrlIsSet() throws Exception {\n\t\tSimpleUrlAuthenticationFailureHandler afh = new SimpleUrlAuthenticationFailureHandler();\n\t\tRedirectStrategy rs = mock(RedirectStrategy.class);\n\t\tafh.setRedirectStrategy(rs);\n\t\tassertThat(afh.getRedirectStrategy()).isSameAs(rs);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tafh.onAuthenticationFailure(request, response, mock(AuthenticationException.class));\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void exceptionIsSavedToSessionOnRedirect() throws Exception {\n\t\tSimpleUrlAuthenticationFailureHandler afh = new SimpleUrlAuthenticationFailureHandler();\n\t\tafh.setDefaultFailureUrl(\"/target\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationException e = mock(AuthenticationException.class);\n\t\tafh.onAuthenticationFailure(request, response, e);\n\t\tassertThat(request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).isSameAs(e);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/target\");\n\t}\n\n\t@Test\n\tpublic void exceptionIsNotSavedIfAllowSessionCreationIsFalse() throws Exception {\n\t\tSimpleUrlAuthenticationFailureHandler afh = new SimpleUrlAuthenticationFailureHandler(\"/target\");\n\t\tafh.setAllowSessionCreation(false);\n\t\tassertThat(afh.isAllowSessionCreation()).isFalse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tafh.onAuthenticationFailure(request, response, mock(AuthenticationException.class));\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t// SEC-462\n\t@Test\n\tpublic void responseIsForwardedIfUseForwardIsTrue() throws Exception {\n\t\tSimpleUrlAuthenticationFailureHandler afh = new SimpleUrlAuthenticationFailureHandler(\"/target\");\n\t\tafh.setUseForward(true);\n\t\tassertThat(afh.isUseForward()).isTrue();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationException e = mock(AuthenticationException.class);\n\t\tafh.onAuthenticationFailure(request, response, e);\n\t\tassertThat(request.getSession(false)).isNull();\n\t\tassertThat(response.getRedirectedUrl()).isNull();\n\t\tassertThat(response.getForwardedUrl()).isEqualTo(\"/target\");\n\t\t// Request scope should be used for forward\n\t\tassertThat(request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).isSameAs(e);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.WebAttributes;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class SimpleUrlAuthenticationSuccessHandlerTests {\n\n\t@Test\n\tpublic void defaultTargetUrlIsUsedIfNoOtherInformationSet() throws Exception {\n\t\tSimpleUrlAuthenticationSuccessHandler ash = new SimpleUrlAuthenticationSuccessHandler();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tash.onAuthenticationSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/\");\n\t}\n\n\t// SEC-1428\n\t@Test\n\tpublic void redirectIsNotPerformedIfResponseIsCommitted() throws Exception {\n\t\tSimpleUrlAuthenticationSuccessHandler ash = new SimpleUrlAuthenticationSuccessHandler(\"/target\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tresponse.setCommitted(true);\n\t\tash.onAuthenticationSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(response.getRedirectedUrl()).isNull();\n\t}\n\n\t/**\n\t * SEC-213\n\t */\n\t@Test\n\tpublic void targetUrlParameterIsUsedIfPresentAndParameterNameIsSet() throws Exception {\n\t\tSimpleUrlAuthenticationSuccessHandler ash = new SimpleUrlAuthenticationSuccessHandler(\"/defaultTarget\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trequest.setParameter(\"targetUrl\", \"/target\");\n\t\tash.onAuthenticationSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/defaultTarget\");\n\t\t// Try with parameter set\n\t\tash.setTargetUrlParameter(\"targetUrl\");\n\t\tresponse = new MockHttpServletResponse();\n\t\tash.onAuthenticationSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/target\");\n\t}\n\n\t@Test\n\tpublic void refererIsUsedIfUseRefererIsSet() throws Exception {\n\t\tSimpleUrlAuthenticationSuccessHandler ash = new SimpleUrlAuthenticationSuccessHandler(\"/defaultTarget\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tash.setUseReferer(true);\n\t\trequest.addHeader(\"Referer\", \"https://www.springsource.com/\");\n\t\tash.onAuthenticationSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://www.springsource.com/\");\n\t}\n\n\t/**\n\t * SEC-297 fix.\n\t */\n\t@Test\n\tpublic void absoluteDefaultTargetUrlDoesNotHaveContextPathPrepended() throws Exception {\n\t\tSimpleUrlAuthenticationSuccessHandler ash = new SimpleUrlAuthenticationSuccessHandler();\n\t\tash.setDefaultTargetUrl(\"https://monkeymachine.co.uk/\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tash.onAuthenticationSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://monkeymachine.co.uk/\");\n\t}\n\n\t@Test\n\tpublic void setTargetUrlParameterNullTargetUrlParameter() {\n\t\tSimpleUrlAuthenticationSuccessHandler ash = new SimpleUrlAuthenticationSuccessHandler();\n\t\tash.setTargetUrlParameter(\"targetUrl\");\n\t\tash.setTargetUrlParameter(null);\n\t\tassertThat(ash.getTargetUrlParameter()).isNull();\n\t}\n\n\t@Test\n\tpublic void setTargetUrlParameterEmptyTargetUrlParameter() {\n\t\tSimpleUrlAuthenticationSuccessHandler ash = new SimpleUrlAuthenticationSuccessHandler();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ash.setTargetUrlParameter(\"\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> ash.setTargetUrlParameter(\"   \"));\n\t}\n\n\t@Test\n\tpublic void shouldRemoveAuthenticationAttributeWhenOnAuthenticationSuccess() throws Exception {\n\t\tSimpleUrlAuthenticationSuccessHandler ash = new SimpleUrlAuthenticationSuccessHandler();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpSession session = request.getSession();\n\t\tassertThat(session).isNotNull();\n\t\tsession.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,\n\t\t\t\tnew BadCredentialsException(\"Invalid credentials\"));\n\t\tassertThat(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).isNotNull();\n\t\tassertThat(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION))\n\t\t\t.isInstanceOf(AuthenticationException.class);\n\t\tash.onAuthenticationSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * Tests {@link UsernamePasswordAuthenticationFilter}.\n *\n * @author Ben Alex\n */\npublic class UsernamePasswordAuthenticationFilterTests {\n\n\t@Test\n\tpublic void testNormalOperation() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/\");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY, \"rod\");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, \"koala\");\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\t// filter.init(null);\n\t\tAuthentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());\n\t\tassertThat(result != null).isTrue();\n\t\tassertThat(((WebAuthenticationDetails) result.getDetails()).getRemoteAddress()).isEqualTo(\"127.0.0.1\");\n\t}\n\n\t@Test\n\tpublic void testConstructorInjectionOfAuthenticationManager() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/\");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY, \"rod\");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, \"dokdo\");\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter(\n\t\t\t\tcreateAuthenticationManager());\n\t\tAuthentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());\n\t\tassertThat(result).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testNullPasswordHandledGracefully() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/\");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY, \"rod\");\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tassertThat(filter.attemptAuthentication(request, new MockHttpServletResponse())).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testNullUsernameHandledGracefully() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/\");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, \"koala\");\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tassertThat(filter.attemptAuthentication(request, new MockHttpServletResponse())).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testUsingDifferentParameterNamesWorksAsExpected() {\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.setUsernameParameter(\"x\");\n\t\tfilter.setPasswordParameter(\"y\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/\");\n\t\trequest.addParameter(\"x\", \"rod\");\n\t\trequest.addParameter(\"y\", \"koala\");\n\t\tAuthentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(((WebAuthenticationDetails) result.getDetails()).getRemoteAddress()).isEqualTo(\"127.0.0.1\");\n\t}\n\n\t@Test\n\tpublic void testSpacesAreTrimmedCorrectlyFromUsername() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/\");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY, \" rod \");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, \"koala\");\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tAuthentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());\n\t\tassertThat(result.getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void testFailedAuthenticationThrowsException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/\");\n\t\trequest.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY, \"rod\");\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class))).willThrow(new BadCredentialsException(\"\"));\n\t\tfilter.setAuthenticationManager(am);\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t.isThrownBy(() -> filter.attemptAuthentication(request, new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void testSecurityContextHolderStrategyUsed() throws Exception {\n\t\tMockHttpServletRequest request = post(\"/login\")\n\t\t\t.param(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY, \"rod\")\n\t\t\t.param(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, \"koala\")\n\t\t\t.build();\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tSecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());\n\t\tfilter.setSecurityContextHolderStrategy(strategy);\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tArgumentCaptor<SecurityContext> captor = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(strategy).setContext(captor.capture());\n\t\tassertThat(captor.getValue().getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t}\n\n\t/**\n\t * SEC-571\n\t */\n\t@Test\n\tpublic void noSessionIsCreatedIfAllowSessionCreationIsFalse() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setMethod(\"POST\");\n\t\tUsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();\n\t\tfilter.setAllowSessionCreation(false);\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.attemptAuthentication(request, new MockHttpServletResponse());\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\tprivate AuthenticationManager createAuthenticationManager() {\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class)))\n\t\t\t.willAnswer((Answer<Authentication>) (invocation) -> (Authentication) invocation.getArguments()[0]);\n\t\treturn am;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/CompositeLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.InOrder;\n\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Eddú Meléndez\n * @author Rob Winch\n * @since 4.2.0\n */\npublic class CompositeLogoutHandlerTests {\n\n\t@Test\n\tpublic void buildEmptyCompositeLogoutHandlerThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CompositeLogoutHandler())\n\t\t\t.withMessage(\"LogoutHandlers are required\");\n\t}\n\n\t@Test\n\tpublic void callLogoutHandlersSuccessfullyWithArray() {\n\t\tLogoutHandler securityContextLogoutHandler = mock(SecurityContextLogoutHandler.class);\n\t\tLogoutHandler csrfLogoutHandler = mock(SecurityContextLogoutHandler.class);\n\t\tLogoutHandler handler = new CompositeLogoutHandler(securityContextLogoutHandler, csrfLogoutHandler);\n\t\thandler.logout(mock(HttpServletRequest.class), mock(HttpServletResponse.class), mock(Authentication.class));\n\t\tverify(securityContextLogoutHandler, times(1)).logout(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(Authentication.class));\n\t\tverify(csrfLogoutHandler, times(1)).logout(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void callLogoutHandlersSuccessfully() {\n\t\tLogoutHandler securityContextLogoutHandler = mock(SecurityContextLogoutHandler.class);\n\t\tLogoutHandler csrfLogoutHandler = mock(SecurityContextLogoutHandler.class);\n\t\tList<LogoutHandler> logoutHandlers = Arrays.asList(securityContextLogoutHandler, csrfLogoutHandler);\n\t\tLogoutHandler handler = new CompositeLogoutHandler(logoutHandlers);\n\t\thandler.logout(mock(HttpServletRequest.class), mock(HttpServletResponse.class), mock(Authentication.class));\n\t\tverify(securityContextLogoutHandler, times(1)).logout(any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class), any(Authentication.class));\n\t\tverify(csrfLogoutHandler, times(1)).logout(any(HttpServletRequest.class), any(HttpServletResponse.class),\n\t\t\t\tany(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void callLogoutHandlersThrowException() {\n\t\tLogoutHandler firstLogoutHandler = mock(LogoutHandler.class);\n\t\tLogoutHandler secondLogoutHandler = mock(LogoutHandler.class);\n\t\twillThrow(new IllegalArgumentException()).given(firstLogoutHandler)\n\t\t\t.logout(any(HttpServletRequest.class), any(HttpServletResponse.class), any(Authentication.class));\n\t\tList<LogoutHandler> logoutHandlers = Arrays.asList(firstLogoutHandler, secondLogoutHandler);\n\t\tLogoutHandler handler = new CompositeLogoutHandler(logoutHandlers);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> handler.logout(mock(HttpServletRequest.class),\n\t\t\t\tmock(HttpServletResponse.class), mock(Authentication.class)));\n\t\tInOrder logoutHandlersInOrder = inOrder(firstLogoutHandler, secondLogoutHandler);\n\t\tlogoutHandlersInOrder.verify(firstLogoutHandler, times(1))\n\t\t\t.logout(any(HttpServletRequest.class), any(HttpServletResponse.class), any(Authentication.class));\n\t\tlogoutHandlersInOrder.verify(secondLogoutHandler, never())\n\t\t\t.logout(any(HttpServletRequest.class), any(HttpServletResponse.class), any(Authentication.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/CookieClearingLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport jakarta.servlet.http.Cookie;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n * @author Onur Kagan Ozcan\n */\npublic class CookieClearingLogoutHandlerTests {\n\n\t// SEC-2036\n\t@Test\n\tpublic void emptyContextRootIsConverted() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"\");\n\t\tCookieClearingLogoutHandler handler = new CookieClearingLogoutHandler(\"my_cookie\");\n\t\thandler.logout(request, response, mock(Authentication.class));\n\t\tassertThat(response.getCookies()).hasSize(1);\n\t\tfor (Cookie c : response.getCookies()) {\n\t\t\tassertThat(c.getPath()).isEqualTo(\"/\");\n\t\t\tassertThat(c.getMaxAge()).isZero();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void configuredCookiesAreCleared() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"/app\");\n\t\tCookieClearingLogoutHandler handler = new CookieClearingLogoutHandler(\"my_cookie\", \"my_cookie_too\");\n\t\thandler.logout(request, response, mock(Authentication.class));\n\t\tassertThat(response.getCookies()).hasSize(2);\n\t\tfor (Cookie c : response.getCookies()) {\n\t\t\tassertThat(c.getPath()).isEqualTo(\"/app\");\n\t\t\tassertThat(c.getMaxAge()).isZero();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void configuredCookieIsSecure() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSecure(true);\n\t\trequest.setContextPath(\"/app\");\n\t\tCookieClearingLogoutHandler handler = new CookieClearingLogoutHandler(\"my_cookie\");\n\t\thandler.logout(request, response, mock(Authentication.class));\n\t\tassertThat(response.getCookies()).hasSize(1);\n\t\tassertThat(response.getCookies()[0].getSecure()).isTrue();\n\t}\n\n\t@Test\n\tpublic void configuredCookieIsNotSecure() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSecure(false);\n\t\trequest.setContextPath(\"/app\");\n\t\tCookieClearingLogoutHandler handler = new CookieClearingLogoutHandler(\"my_cookie\");\n\t\thandler.logout(request, response, mock(Authentication.class));\n\t\tassertThat(response.getCookies()).hasSize(1);\n\t\tassertThat(response.getCookies()[0].getSecure()).isFalse();\n\t}\n\n\t@Test\n\tpublic void passedInCookiesAreCleared() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"/foo/bar\");\n\t\tCookie cookie1 = new Cookie(\"my_cookie\", null);\n\t\tcookie1.setPath(\"/foo\");\n\t\tcookie1.setMaxAge(0);\n\t\tCookie cookie2 = new Cookie(\"my_cookie_too\", null);\n\t\tcookie2.setPath(\"/foo\");\n\t\tcookie2.setMaxAge(0);\n\t\tCookieClearingLogoutHandler handler = new CookieClearingLogoutHandler(cookie1, cookie2);\n\t\thandler.logout(request, response, mock(Authentication.class));\n\t\tassertThat(response.getCookies()).hasSize(2);\n\t\tfor (Cookie c : response.getCookies()) {\n\t\t\tassertThat(c.getPath()).isEqualTo(\"/foo\");\n\t\t\tassertThat(c.getMaxAge()).isZero();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void invalidAge() {\n\t\tCookie cookie1 = new Cookie(\"my_cookie\", null);\n\t\tcookie1.setPath(\"/foo\");\n\t\tcookie1.setMaxAge(100);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CookieClearingLogoutHandler(cookie1));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/DelegatingLogoutSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport java.util.LinkedHashMap;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * DelegatingLogoutSuccessHandlerTests Tests\n *\n * @author Shazin Sadakath\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingLogoutSuccessHandlerTests {\n\n\t@Mock\n\tRequestMatcher matcher;\n\n\t@Mock\n\tRequestMatcher matcher2;\n\n\t@Mock\n\tLogoutSuccessHandler handler;\n\n\t@Mock\n\tLogoutSuccessHandler handler2;\n\n\t@Mock\n\tLogoutSuccessHandler defaultHandler;\n\n\t@Mock\n\tHttpServletRequest request;\n\n\t@Mock\n\tMockHttpServletResponse response;\n\n\t@Mock\n\tAuthentication authentication;\n\n\tDelegatingLogoutSuccessHandler delegatingHandler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tLinkedHashMap<RequestMatcher, LogoutSuccessHandler> matcherToHandler = new LinkedHashMap<>();\n\t\tmatcherToHandler.put(this.matcher, this.handler);\n\t\tmatcherToHandler.put(this.matcher2, this.handler2);\n\t\tthis.delegatingHandler = new DelegatingLogoutSuccessHandler(matcherToHandler);\n\t}\n\n\t@Test\n\tpublic void onLogoutSuccessFirstMatches() throws Exception {\n\t\tthis.delegatingHandler.setDefaultLogoutSuccessHandler(this.defaultHandler);\n\t\tgiven(this.matcher.matches(this.request)).willReturn(true);\n\t\tthis.delegatingHandler.onLogoutSuccess(this.request, this.response, this.authentication);\n\t\tverify(this.handler).onLogoutSuccess(this.request, this.response, this.authentication);\n\t\tverifyNoMoreInteractions(this.matcher2, this.handler2, this.defaultHandler);\n\t}\n\n\t@Test\n\tpublic void onLogoutSuccessSecondMatches() throws Exception {\n\t\tthis.delegatingHandler.setDefaultLogoutSuccessHandler(this.defaultHandler);\n\t\tgiven(this.matcher2.matches(this.request)).willReturn(true);\n\t\tthis.delegatingHandler.onLogoutSuccess(this.request, this.response, this.authentication);\n\t\tverify(this.handler2).onLogoutSuccess(this.request, this.response, this.authentication);\n\t\tverifyNoMoreInteractions(this.handler, this.defaultHandler);\n\t}\n\n\t@Test\n\tpublic void onLogoutSuccessDefault() throws Exception {\n\t\tthis.delegatingHandler.setDefaultLogoutSuccessHandler(this.defaultHandler);\n\t\tthis.delegatingHandler.onLogoutSuccess(this.request, this.response, this.authentication);\n\t\tverify(this.defaultHandler).onLogoutSuccess(this.request, this.response, this.authentication);\n\t\tverifyNoMoreInteractions(this.handler, this.handler2);\n\t}\n\n\t@Test\n\tpublic void onLogoutSuccessNoMatchDefaultNull() throws Exception {\n\t\tthis.delegatingHandler.onLogoutSuccess(this.request, this.response, this.authentication);\n\t\tverifyNoMoreInteractions(this.handler, this.handler2, this.defaultHandler);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/ForwardLogoutSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link ForwardLogoutSuccessHandler}.\n *\n * @author Vedran Pavic\n */\npublic class ForwardLogoutSuccessHandlerTests {\n\n\t@Test\n\tpublic void invalidTargetUrl() {\n\t\tString targetUrl = \"not.valid\";\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ForwardLogoutSuccessHandler(targetUrl))\n\t\t\t.withMessage(\"'\" + targetUrl + \"' is not a valid target URL\");\n\t}\n\n\t@Test\n\tpublic void emptyTargetUrl() {\n\t\tString targetUrl = \" \";\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ForwardLogoutSuccessHandler(targetUrl))\n\t\t\t.withMessage(\"'\" + targetUrl + \"' is not a valid target URL\");\n\t}\n\n\t@Test\n\tpublic void logoutSuccessIsHandled() throws Exception {\n\t\tString targetUrl = \"/login?logout\";\n\t\tForwardLogoutSuccessHandler handler = new ForwardLogoutSuccessHandler(targetUrl);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\thandler.onLogoutSuccess(request, response, authentication);\n\t\tassertThat(response.getForwardedUrl()).isEqualTo(targetUrl);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/HeaderWriterLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.header.HeaderWriter;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rafiullah Hamedy\n * @author Josh Cummings\n * @see HeaderWriterLogoutHandler\n */\npublic class HeaderWriterLogoutHandlerTests {\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate MockHttpServletRequest request;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.request = new MockHttpServletRequest();\n\t}\n\n\t@Test\n\tpublic void constructorWhenHeaderWriterIsNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new HeaderWriterLogoutHandler(null))\n\t\t\t.withMessage(\"headerWriter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void logoutWhenHasHeaderWriterThenInvoked() {\n\t\tHeaderWriter headerWriter = mock(HeaderWriter.class);\n\t\tHeaderWriterLogoutHandler handler = new HeaderWriterLogoutHandler(headerWriter);\n\t\thandler.logout(this.request, this.response, mock(Authentication.class));\n\t\tverify(headerWriter).writeHeaders(this.request, this.response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/HttpStatusReturningLogoutSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Gunnar Hillert\n */\npublic class HttpStatusReturningLogoutSuccessHandlerTests {\n\n\t@Test\n\tpublic void testDefaultHttpStatusBeingReturned() throws Exception {\n\t\tfinal HttpStatusReturningLogoutSuccessHandler lsh = new HttpStatusReturningLogoutSuccessHandler();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tlsh.onLogoutSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(request.getSession(false)).isNull();\n\t\tassertThat(response.getRedirectedUrl()).isNull();\n\t\tassertThat(response.getForwardedUrl()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t}\n\n\t@Test\n\tpublic void testCustomHttpStatusBeingReturned() throws Exception {\n\t\tfinal HttpStatusReturningLogoutSuccessHandler lsh = new HttpStatusReturningLogoutSuccessHandler(\n\t\t\t\tHttpStatus.NO_CONTENT);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tlsh.onLogoutSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(request.getSession(false)).isNull();\n\t\tassertThat(response.getRedirectedUrl()).isNull();\n\t\tassertThat(response.getForwardedUrl()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());\n\t}\n\n\t@Test\n\tpublic void testThatSettNullHttpStatusThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new HttpStatusReturningLogoutSuccessHandler(null))\n\t\t\t.withMessage(\"The provided HttpStatus must not be null.\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/LogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.firewall.DefaultHttpFirewall;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * @author Luke Taylor\n */\npublic class LogoutHandlerTests {\n\n\tLogoutFilter filter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.filter = new LogoutFilter(\"/success\", new SecurityContextLogoutHandler());\n\t}\n\n\t@Test\n\tpublic void testRequiresLogoutUrlWorksWithPathParams() {\n\t\tMockHttpServletRequest request = post().requestUri(\"/context\", \"/logout;someparam=blah\", null)\n\t\t\t.queryString(\"otherparam=blah\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tDefaultHttpFirewall fw = new DefaultHttpFirewall();\n\t\tassertThat(this.filter.requiresLogout(fw.getFirewalledRequest(request), response)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testRequiresLogoutUrlWorksWithQueryParams() {\n\t\tMockHttpServletRequest request = get().requestUri(\"/context\", \"/logout\", null)\n\t\t\t.queryString(\"otherparam=blah\")\n\t\t\t.build();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(this.filter.requiresLogout(request, response)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/LogoutSuccessEventPublishingLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.event.LogoutSuccessEvent;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Onur Kagan Ozcan\n */\npublic class LogoutSuccessEventPublishingLogoutHandlerTests {\n\n\t@Test\n\tpublic void shouldPublishEvent() {\n\t\tLogoutSuccessEventPublishingLogoutHandler handler = new LogoutSuccessEventPublishingLogoutHandler();\n\t\tLogoutAwareEventPublisher eventPublisher = new LogoutAwareEventPublisher();\n\t\thandler.setApplicationEventPublisher(eventPublisher);\n\t\thandler.logout(new MockHttpServletRequest(), new MockHttpServletResponse(), mock(Authentication.class));\n\t\tassertThat(eventPublisher.flag).isTrue();\n\t}\n\n\t@Test\n\tpublic void shouldNotPublishEventWhenAuthenticationIsNull() {\n\t\tLogoutSuccessEventPublishingLogoutHandler handler = new LogoutSuccessEventPublishingLogoutHandler();\n\t\tLogoutAwareEventPublisher eventPublisher = new LogoutAwareEventPublisher();\n\t\thandler.setApplicationEventPublisher(eventPublisher);\n\t\thandler.logout(new MockHttpServletRequest(), new MockHttpServletResponse(), null);\n\t\tassertThat(eventPublisher.flag).isFalse();\n\t}\n\n\tprivate static class LogoutAwareEventPublisher implements ApplicationEventPublisher {\n\n\t\tBoolean flag = false;\n\n\t\t@Override\n\t\tpublic void publishEvent(Object event) {\n\t\t\tif (LogoutSuccessEvent.class.isAssignableFrom(event.getClass())) {\n\t\t\t\tthis.flag = true;\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n */\npublic class SecurityContextLogoutHandlerTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate SecurityContextLogoutHandler handler;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.handler = new SecurityContextLogoutHandler();\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tcontext.setAuthentication(\n\t\t\t\tnew TestingAuthenticationToken(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\")));\n\t\tSecurityContextHolder.setContext(context);\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t// SEC-2025\n\t@Test\n\tpublic void clearsAuthentication() {\n\t\tSecurityContext beforeContext = SecurityContextHolder.getContext();\n\t\tthis.handler.logout(this.request, this.response, SecurityContextHolder.getContext().getAuthentication());\n\t\tassertThat(beforeContext.getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void disableClearsAuthentication() {\n\t\tthis.handler.setClearAuthentication(false);\n\t\tSecurityContext beforeContext = SecurityContextHolder.getContext();\n\t\tAuthentication beforeAuthentication = beforeContext.getAuthentication();\n\t\tthis.handler.logout(this.request, this.response, SecurityContextHolder.getContext().getAuthentication());\n\t\tassertThat(beforeContext.getAuthentication()).isNotNull();\n\t\tassertThat(beforeContext.getAuthentication()).isSameAs(beforeAuthentication);\n\t}\n\n\t@Test\n\tpublic void logoutWhenSecurityContextRepositoryThenSaveEmptyContext() {\n\t\tSecurityContextRepository repository = mock(SecurityContextRepository.class);\n\t\tthis.handler.setSecurityContextRepository(repository);\n\t\tthis.handler.logout(this.request, this.response, SecurityContextHolder.getContext().getAuthentication());\n\t\tverify(repository).saveContext(eq(SecurityContextHolder.createEmptyContext()), any(), any());\n\t}\n\n\t@Test\n\tpublic void logoutWhenClearAuthenticationFalseThenSaveEmptyContext() {\n\t\tSecurityContextRepository repository = mock(SecurityContextRepository.class);\n\t\tthis.handler.setSecurityContextRepository(repository);\n\t\tthis.handler.setClearAuthentication(false);\n\t\tthis.handler.logout(this.request, this.response, SecurityContextHolder.getContext().getAuthentication());\n\t\tverify(repository).saveContext(eq(SecurityContextHolder.createEmptyContext()), any(), any());\n\t}\n\n\t@Test\n\tpublic void constructorWhenDefaultSecurityContextRepositoryThenHttpSessionSecurityContextRepository() {\n\t\tSecurityContextRepository securityContextRepository = (SecurityContextRepository) ReflectionTestUtils\n\t\t\t.getField(this.handler, \"securityContextRepository\");\n\t\tassertThat(securityContextRepository).isInstanceOf(HttpSessionSecurityContextRepository.class);\n\t}\n\n\t@Test\n\tpublic void setSecurityContextRepositoryWhenNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.handler.setSecurityContextRepository(null))\n\t\t\t.withMessage(\"securityContextRepository cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/logout/SimpleUrlLogoutSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.logout;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class SimpleUrlLogoutSuccessHandlerTests {\n\n\t@Test\n\tpublic void doesntRedirectIfResponseIsCommitted() throws Exception {\n\t\tSimpleUrlLogoutSuccessHandler lsh = new SimpleUrlLogoutSuccessHandler();\n\t\tlsh.setDefaultTargetUrl(\"/target\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tresponse.setCommitted(true);\n\t\tlsh.onLogoutSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(request.getSession(false)).isNull();\n\t\tassertThat(response.getRedirectedUrl()).isNull();\n\t\tassertThat(response.getForwardedUrl()).isNull();\n\t}\n\n\t@Test\n\tpublic void absoluteUrlIsSupported() throws Exception {\n\t\tSimpleUrlLogoutSuccessHandler lsh = new SimpleUrlLogoutSuccessHandler();\n\t\tlsh.setDefaultTargetUrl(\"https://someurl.com/\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tlsh.onLogoutSuccess(request, response, mock(Authentication.class));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"https://someurl.com/\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ott/DefaultGenerateOneTimeTokenRequestResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport java.time.Duration;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link DefaultGenerateOneTimeTokenRequestResolver}\n *\n * @author Max Batischev\n */\npublic class DefaultGenerateOneTimeTokenRequestResolverTests {\n\n\tprivate final DefaultGenerateOneTimeTokenRequestResolver requestResolver = new DefaultGenerateOneTimeTokenRequestResolver();\n\n\t@Test\n\tvoid resolveWhenUsernameParameterIsPresentThenResolvesGenerateRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"username\", \"test\");\n\n\t\tGenerateOneTimeTokenRequest generateRequest = this.requestResolver.resolve(request);\n\n\t\tassertThat(generateRequest).isNotNull();\n\t\tassertThat(generateRequest.getUsername()).isEqualTo(\"test\");\n\t\tassertThat(generateRequest.getExpiresIn()).isEqualTo(Duration.ofSeconds(300));\n\t}\n\n\t@Test\n\tvoid resolveWhenUsernameParameterIsNotPresentThenNull() {\n\t\tGenerateOneTimeTokenRequest generateRequest = this.requestResolver.resolve(new MockHttpServletRequest());\n\n\t\tassertThat(generateRequest).isNull();\n\t}\n\n\t@Test\n\tvoid resolveWhenExpiresInSetThenResolvesGenerateRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"username\", \"test\");\n\t\tthis.requestResolver.setExpiresIn(Duration.ofSeconds(600));\n\n\t\tGenerateOneTimeTokenRequest generateRequest = this.requestResolver.resolve(request);\n\n\t\tassertThat(generateRequest.getExpiresIn()).isEqualTo(Duration.ofSeconds(600));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ott/GenerateOneTimeTokenFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport java.io.IOException;\nimport java.time.Instant;\n\nimport jakarta.servlet.ServletException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentMatchers;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.ott.DefaultOneTimeToken;\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.OneTimeTokenService;\nimport org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;\n\n/**\n * Tests for {@link GenerateOneTimeTokenWebFilter}\n *\n * @author Max Batischev\n */\npublic class GenerateOneTimeTokenFilterTests {\n\n\tprivate final OneTimeTokenService oneTimeTokenService = mock(OneTimeTokenService.class);\n\n\tprivate final RedirectOneTimeTokenGenerationSuccessHandler successHandler = new RedirectOneTimeTokenGenerationSuccessHandler(\n\t\t\t\"/login/ott\");\n\n\tprivate static final String TOKEN = \"token\";\n\n\tprivate static final String USERNAME = \"user\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate final MockHttpServletResponse response = new MockHttpServletResponse();\n\n\tprivate final MockFilterChain filterChain = new MockFilterChain();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.request = post(\"/ott/generate\").build();\n\t}\n\n\t@Test\n\tvoid filterWhenUsernameFormParamIsPresentThenSuccess() throws ServletException, IOException {\n\t\tgiven(this.oneTimeTokenService.generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class)))\n\t\t\t.willReturn(new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now()));\n\t\tthis.request.setParameter(\"username\", USERNAME);\n\n\t\tGenerateOneTimeTokenFilter filter = new GenerateOneTimeTokenFilter(this.oneTimeTokenService,\n\t\t\t\tthis.successHandler);\n\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\n\t\tverify(this.oneTimeTokenService).generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class));\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\"/login/ott\");\n\t}\n\n\t@Test\n\tvoid filterWhenUsernameFormParamIsEmptyThenNull() throws ServletException, IOException {\n\t\tgiven(this.oneTimeTokenService.generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class)))\n\t\t\t.willReturn((new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now())));\n\n\t\tGenerateOneTimeTokenFilter filter = new GenerateOneTimeTokenFilter(this.oneTimeTokenService,\n\t\t\t\tthis.successHandler);\n\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\n\t\tverify(this.oneTimeTokenService, never()).generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class));\n\t}\n\n\t@Test\n\tpublic void constructorWhenOneTimeTokenServiceNullThenIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new GenerateOneTimeTokenFilter(null, this.successHandler));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setWhenRequestMatcherNullThenIllegalArgumentException() {\n\t\tGenerateOneTimeTokenFilter filter = new GenerateOneTimeTokenFilter(this.oneTimeTokenService,\n\t\t\t\tthis.successHandler);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> filter.setRequestMatcher(null));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid filterWhenUsernameFormParamIsEmptyButRequestResolverCanResolveThenSuccess()\n\t\t\tthrows ServletException, IOException {\n\t\tGenerateOneTimeTokenRequestResolver requestResolver = mock();\n\t\tgiven(this.oneTimeTokenService.generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class)))\n\t\t\t.willReturn((new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now())));\n\t\tgiven(requestResolver.resolve(this.request)).willReturn(new GenerateOneTimeTokenRequest(USERNAME));\n\n\t\tGenerateOneTimeTokenFilter filter = new GenerateOneTimeTokenFilter(this.oneTimeTokenService,\n\t\t\t\tthis.successHandler);\n\t\tfilter.setRequestResolver(requestResolver);\n\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\n\t\tverify(this.oneTimeTokenService).generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class));\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(\"/login/ott\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ott/OneTimeTokenAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link OneTimeTokenAuthenticationConverter}\n *\n * @author Marcus da Coregio\n */\nclass OneTimeTokenAuthenticationConverterTests {\n\n\tprivate final OneTimeTokenAuthenticationConverter converter = new OneTimeTokenAuthenticationConverter();\n\n\t@Test\n\tvoid convertWhenTokenParameterThenReturnOneTimeTokenAuthenticationToken() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"token\", \"1234\");\n\t\tOneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getTokenValue()).isEqualTo(\"1234\");\n\t\tassertThat(authentication.getPrincipal()).isNull();\n\t}\n\n\t@Test\n\tvoid convertWhenTokenAndUsernameParameterThenReturnOneTimeTokenAuthenticationTokenWithUsername() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"token\", \"1234\");\n\t\tOneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getTokenValue()).isEqualTo(\"1234\");\n\t}\n\n\t@Test\n\tvoid convertWhenOnlyUsernameParameterThenReturnNull() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"username\", \"josh\");\n\t\tOneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter\n\t\t\t.convert(request);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tvoid convertWhenNoTokenParameterThenNull() {\n\t\tAuthentication authentication = this.converter.convert(new MockHttpServletRequest());\n\t\tassertThat(authentication).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ott/OneTimeTokenAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.web.servlet.MockServletContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\n\n/**\n * Tests for {@link OneTimeTokenAuthenticationFilter}.\n *\n * @author Daniel Garnier-Moiroux\n * @since 6.5\n */\n@ExtendWith(MockitoExtension.class)\nclass OneTimeTokenAuthenticationFilterTests {\n\n\t@Mock\n\tprivate FilterChain chain;\n\n\t@Mock\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate final OneTimeTokenAuthenticationFilter filter = new OneTimeTokenAuthenticationFilter();\n\n\tprivate final HttpServletResponse response = new MockHttpServletResponse();\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.filter.setAuthenticationManager(this.authenticationManager);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid setAuthenticationConverterWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationConverter(null));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid doFilterWhenUrlDoesNotMatchThenContinues() throws ServletException, IOException {\n\t\tOneTimeTokenAuthenticationConverter converter = mock(OneTimeTokenAuthenticationConverter.class);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tthis.filter.setAuthenticationConverter(converter);\n\t\tthis.filter.doFilter(post(\"/nomatch\").buildRequest(new MockServletContext()), response, this.chain);\n\t\tverifyNoInteractions(converter, response);\n\t\tverify(this.chain).doFilter(any(), any());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid doFilterWhenMethodDoesNotMatchThenContinues() throws ServletException, IOException {\n\t\tOneTimeTokenAuthenticationConverter converter = mock(OneTimeTokenAuthenticationConverter.class);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tthis.filter.setAuthenticationConverter(converter);\n\t\tthis.filter.doFilter(get(\"/login/ott\").buildRequest(new MockServletContext()), response, this.chain);\n\t\tverifyNoInteractions(converter, response);\n\t\tverify(this.chain).doFilter(any(), any());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid doFilterWhenMissingTokenThenPropagatesRequest() throws ServletException, IOException {\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(post(\"/login/ott\").buildRequest(new MockServletContext()), this.response, chain);\n\t\tverify(chain).doFilter(any(), any());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid doFilterWhenInvalidTokenThenUnauthorized() throws ServletException, IOException {\n\t\tgiven(this.authenticationManager.authenticate(any())).willThrow(new BadCredentialsException(\"invalid token\"));\n\t\tthis.filter.doFilter(\n\t\t\t\tpost(\"/login/ott\").param(\"token\", \"some-token-value\").buildRequest(new MockServletContext()),\n\t\t\t\tthis.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());\n\t\tverifyNoInteractions(this.chain);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid doFilterWhenValidThenRedirectsToSavedRequest() throws ServletException, IOException {\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(OneTimeTokenAuthenticationToken.authenticated(\"username\", AuthorityUtils.NO_AUTHORITIES));\n\t\tthis.filter.doFilter(\n\t\t\t\tpost(\"/login/ott\").param(\"token\", \"some-token-value\").buildRequest(new MockServletContext()),\n\t\t\t\tthis.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.FOUND.value());\n\t\tassertThat(this.response.getHeader(\"location\")).endsWith(\"/\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ott/RedirectOneTimeTokenGenerationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ott;\n\nimport java.io.IOException;\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.ott.DefaultOneTimeToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link RedirectOneTimeTokenGenerationSuccessHandler}\n *\n * @author Marcus da Coregio\n */\nclass RedirectOneTimeTokenGenerationSuccessHandlerTests {\n\n\t@Test\n\tvoid handleThenRedirectToDefaultLocation() throws IOException {\n\t\tRedirectOneTimeTokenGenerationSuccessHandler handler = new RedirectOneTimeTokenGenerationSuccessHandler(\n\t\t\t\t\"/login/ott\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\thandler.handle(new MockHttpServletRequest(), response, new DefaultOneTimeToken(\"token\", \"user\", Instant.now()));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/login/ott\");\n\t}\n\n\t@Test\n\tvoid handleWhenUrlChangedThenRedirectToUrl() throws IOException {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRedirectOneTimeTokenGenerationSuccessHandler handler = new RedirectOneTimeTokenGenerationSuccessHandler(\n\t\t\t\t\"/redirected\");\n\t\thandler.handle(new MockHttpServletRequest(), response, new DefaultOneTimeToken(\"token\", \"user\", Instant.now()));\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/redirected\");\n\t}\n\n\t@Test\n\tvoid setRedirectUrlWhenNullOrEmptyThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RedirectOneTimeTokenGenerationSuccessHandler(null))\n\t\t\t.withMessage(\"redirectUrl cannot be empty or null\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RedirectOneTimeTokenGenerationSuccessHandler(\"\"))\n\t\t\t.withMessage(\"redirectUrl cannot be empty or null\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/password/HaveIBeenPwnedRestApiPasswordCheckerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.password;\n\nimport java.io.IOException;\n\nimport okhttp3.HttpUrl;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.password.CompromisedPasswordDecision;\nimport org.springframework.web.client.RestClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\nclass HaveIBeenPwnedRestApiPasswordCheckerTests {\n\n\tprivate final String pwnedPasswords = \"\"\"\n\t\t\t2CDE4CDCFA5AD7D223BD1800338FBEAA04E:1\n\t\t\t2CF90F92EE1941547BB13DFC7D0E0AFE504:1\n\t\t\t2D10A6654B6D75908AE572559542245CBFA:6\n\t\t\t2D4FCF535FE92B8B950424E16E65EFBFED3:1\n\t\t\t2D6980B9098804E7A83DC5831BFBAF3927F:1\n\t\t\t2D8D1B3FAACCA6A3C6A91617B2FA32E2F57:1\n\t\t\t2DC183F740EE76F27B78EB39C8AD972A757:300185\n\t\t\t2DE4C0087846D223DBBCCF071614590F300:3\n\t\t\t2DEA2B1D02714099E4B7A874B4364D518F6:1\n\t\t\t2E750AE8C4756A20CE040BF3DDF094FA7EC:1\n\t\t\t2E90B7B3C5C1181D16C48E273D9AC7F3C16:5\n\t\t\t2E991A9162F24F01826D8AF73CA20F2B430:1\n\t\t\t2EAE5EA981BFAF29A8869A40BDDADF3879B:2\n\t\t\t2F1AC09E3846595E436BBDDDD2189358AF9:1\n\t\t\t\"\"\";\n\n\tprivate final MockWebServer server = new MockWebServer();\n\n\tprivate final HaveIBeenPwnedRestApiPasswordChecker passwordChecker = new HaveIBeenPwnedRestApiPasswordChecker();\n\n\t@BeforeEach\n\tvoid setup() throws IOException {\n\t\tthis.server.start();\n\t\tHttpUrl url = this.server.url(\"/range/\");\n\t\tthis.passwordChecker.setRestClient(RestClient.builder().baseUrl(url.toString()).build());\n\t}\n\n\t@AfterEach\n\tvoid tearDown() throws IOException {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tvoid checkWhenPasswordIsLeakedThenIsCompromised() throws InterruptedException {\n\t\tthis.server.enqueue(new MockResponse().setBody(this.pwnedPasswords).setResponseCode(200));\n\t\tCompromisedPasswordDecision check = this.passwordChecker.check(\"P@ssw0rd\");\n\t\tassertThat(check.isCompromised()).isTrue();\n\t\tassertThat(this.server.takeRequest().getPath()).isEqualTo(\"/range/21BD1\");\n\t}\n\n\t@Test\n\tvoid checkWhenPasswordNotLeakedThenNotCompromised() {\n\t\tthis.server.enqueue(new MockResponse().setBody(this.pwnedPasswords).setResponseCode(200));\n\t\tCompromisedPasswordDecision check = this.passwordChecker.check(\"My1nCr3d!bL3P@SS0W0RD\");\n\t\tassertThat(check.isCompromised()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkWhenNoPasswordsReturnedFromApiCallThenNotCompromised() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(200));\n\t\tCompromisedPasswordDecision check = this.passwordChecker.check(\"123456\");\n\t\tassertThat(check.isCompromised()).isFalse();\n\t}\n\n\t@Test\n\tvoid checkWhenResponseStatusNot200ThenNotCompromised() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(503));\n\t\tassertThatNoException().isThrownBy(() -> this.passwordChecker.check(\"123456\"));\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(404));\n\t\tassertThatNoException().isThrownBy(() -> this.passwordChecker.check(\"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/password/HaveIBeenPwnedRestApiReactivePasswordCheckerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.password;\n\nimport java.io.IOException;\n\nimport okhttp3.HttpUrl;\nimport okhttp3.mockwebserver.MockResponse;\nimport okhttp3.mockwebserver.MockWebServer;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.web.reactive.function.client.WebClient;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass HaveIBeenPwnedRestApiReactivePasswordCheckerTests {\n\n\tprivate final String pwnedPasswords = \"\"\"\n\t\t\t2CDE4CDCFA5AD7D223BD1800338FBEAA04E:1\n\t\t\t2CF90F92EE1941547BB13DFC7D0E0AFE504:1\n\t\t\t2D10A6654B6D75908AE572559542245CBFA:6\n\t\t\t2D4FCF535FE92B8B950424E16E65EFBFED3:1\n\t\t\t2D6980B9098804E7A83DC5831BFBAF3927F:1\n\t\t\t2D8D1B3FAACCA6A3C6A91617B2FA32E2F57:1\n\t\t\t2DC183F740EE76F27B78EB39C8AD972A757:300185\n\t\t\t2DE4C0087846D223DBBCCF071614590F300:3\n\t\t\t2DEA2B1D02714099E4B7A874B4364D518F6:1\n\t\t\t2E750AE8C4756A20CE040BF3DDF094FA7EC:1\n\t\t\t2E90B7B3C5C1181D16C48E273D9AC7F3C16:5\n\t\t\t2E991A9162F24F01826D8AF73CA20F2B430:1\n\t\t\t2EAE5EA981BFAF29A8869A40BDDADF3879B:2\n\t\t\t2F1AC09E3846595E436BBDDDD2189358AF9:1\n\t\t\t\"\"\";\n\n\tprivate final MockWebServer server = new MockWebServer();\n\n\tprivate final HaveIBeenPwnedRestApiReactivePasswordChecker passwordChecker = new HaveIBeenPwnedRestApiReactivePasswordChecker();\n\n\t@BeforeEach\n\tvoid setup() throws IOException {\n\t\tthis.server.start();\n\t\tHttpUrl url = this.server.url(\"/range/\");\n\t\tthis.passwordChecker.setWebClient(WebClient.builder().baseUrl(url.toString()).build());\n\t}\n\n\t@AfterEach\n\tvoid tearDown() throws IOException {\n\t\tthis.server.shutdown();\n\t}\n\n\t@Test\n\tvoid checkWhenPasswordIsLeakedThenIsCompromised() throws InterruptedException {\n\t\tthis.server.enqueue(new MockResponse().setBody(this.pwnedPasswords).setResponseCode(200));\n\t\tStepVerifier.create(this.passwordChecker.check(\"P@ssw0rd\"))\n\t\t\t.assertNext((check) -> assertThat(check.isCompromised()).isTrue())\n\t\t\t.verifyComplete();\n\t\tassertThat(this.server.takeRequest().getPath()).isEqualTo(\"/range/21BD1\");\n\t}\n\n\t@Test\n\tvoid checkWhenPasswordNotLeakedThenNotCompromised() {\n\t\tthis.server.enqueue(new MockResponse().setBody(this.pwnedPasswords).setResponseCode(200));\n\t\tStepVerifier.create(this.passwordChecker.check(\"My1nCr3d!bL3P@SS0W0RD\"))\n\t\t\t.assertNext((check) -> assertThat(check.isCompromised()).isFalse())\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tvoid checkWhenNoPasswordsReturnedFromApiCallThenNotCompromised() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(200));\n\t\tStepVerifier.create(this.passwordChecker.check(\"P@ssw0rd\"))\n\t\t\t.assertNext((check) -> assertThat(check.isCompromised()).isFalse())\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tvoid checkWhenResponseStatusNot200ThenNotCompromised() {\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(503));\n\t\tStepVerifier.create(this.passwordChecker.check(\"123456\"))\n\t\t\t.assertNext((check) -> assertThat(check.isCompromised()).isFalse())\n\t\t\t.verifyComplete();\n\t\tthis.server.enqueue(new MockResponse().setResponseCode(404));\n\t\tStepVerifier.create(this.passwordChecker.check(\"123456\"))\n\t\t\t.assertNext((check) -> assertThat(check.isCompromised()).isFalse())\n\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tvoid checkWhenNullThenNotCompromised() {\n\t\tStepVerifier.create(this.passwordChecker.check(null))\n\t\t\t.assertNext((check) -> assertThat(check.isCompromised()).isFalse())\n\t\t\t.verifyComplete();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.ArrayList;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.NonBuildableAuthenticationToken;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.web.WebAttributes;\nimport org.springframework.security.web.authentication.DefaultEqualsGrantedAuthority;\nimport org.springframework.security.web.authentication.ForwardAuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * @author Rob Winch\n * @author Tadaya Tsuyukubo\n *\n */\npublic class AbstractPreAuthenticatedProcessingFilterTests {\n\n\tprivate AbstractPreAuthenticatedProcessingFilter filter;\n\n\t@BeforeEach\n\tpublic void createFilter() {\n\t\tthis.filter = new AbstractPreAuthenticatedProcessingFilter() {\n\t\t\t@Override\n\t\t\tprotected Object getPreAuthenticatedCredentials(HttpServletRequest request) {\n\t\t\t\treturn \"n/a\";\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {\n\t\t\t\treturn \"doesntmatter\";\n\t\t\t}\n\t\t};\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void filterChainProceedsOnFailedAuthenticationByDefault() throws Exception {\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class))).willThrow(new BadCredentialsException(\"\"));\n\t\tthis.filter.setAuthenticationManager(am);\n\t\tthis.filter.afterPropertiesSet();\n\t\tthis.filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), mock(FilterChain.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t/* SEC-881 */\n\t@Test\n\tpublic void exceptionIsThrownOnFailedAuthenticationIfContinueFilterChainOnUnsuccessfulAuthenticationSetToFalse()\n\t\t\tthrows Exception {\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class))).willThrow(new BadCredentialsException(\"\"));\n\t\tthis.filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);\n\t\tthis.filter.setAuthenticationManager(am);\n\t\tthis.filter.afterPropertiesSet();\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.filter\n\t\t\t.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), mock(FilterChain.class)));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void testAfterPropertiesSet() {\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);\n\t}\n\n\t// SEC-2045\n\t@Test\n\tpublic void testAfterPropertiesSetInvokesSuper() {\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tassertThat(filter.initFilterBeanInvoked).isTrue();\n\t}\n\n\t@Test\n\tpublic void testDoFilterAuthenticated() throws Exception {\n\t\ttestDoFilter(true);\n\t}\n\n\t@Test\n\tpublic void testDoFilterUnauthenticated() throws Exception {\n\t\ttestDoFilter(false);\n\t}\n\n\t// SEC-1968\n\t@Test\n\tpublic void nullPreAuthenticationClearsPreviousUser() throws Exception {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"oldUser\", \"pass\", \"ROLE_USER\"));\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.principal = null;\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void nullPreAuthenticationPerservesPreviousUserCheckPrincipalChangesFalse() throws Exception {\n\t\tTestingAuthenticationToken authentication = new TestingAuthenticationToken(\"oldUser\", \"pass\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.principal = null;\n\t\tfilter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), new MockFilterChain());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationFalsePrincipalString() throws Exception {\n\t\tObject principal = \"sameprincipal\";\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(principal, \"something\", \"ROLE_USER\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = principal;\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(am);\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationTruePrincipalString() throws Exception {\n\t\tObject currentPrincipal = \"currentUser\";\n\t\tTestingAuthenticationToken authRequest = new TestingAuthenticationToken(currentPrincipal, \"something\",\n\t\t\t\t\"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authRequest);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = \"newUser\";\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(am).authenticate(any(PreAuthenticatedAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void callsAuthenticationSuccessHandlerOnSuccessfulAuthentication() throws Exception {\n\t\tObject currentPrincipal = \"currentUser\";\n\t\tTestingAuthenticationToken authRequest = new TestingAuthenticationToken(currentPrincipal, \"something\",\n\t\t\t\t\"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authRequest);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setAuthenticationSuccessHandler(new ForwardAuthenticationSuccessHandler(\"/forwardUrl\"));\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = \"newUser\";\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(am).authenticate(any(PreAuthenticatedAuthenticationToken.class));\n\t\tassertThat(response.getForwardedUrl()).isEqualTo(\"/forwardUrl\");\n\t}\n\n\t@Test\n\tpublic void securityContextRepository() throws Exception {\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tObject currentPrincipal = \"currentUser\";\n\t\tTestingAuthenticationToken authRequest = new TestingAuthenticationToken(currentPrincipal, \"something\",\n\t\t\t\t\"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authRequest);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setSecurityContextRepository(securityContextRepository);\n\t\tfilter.setAuthenticationSuccessHandler(new ForwardAuthenticationSuccessHandler(\"/forwardUrl\"));\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = \"newUser\";\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any())).willReturn(authRequest);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(securityContextRepository).saveContext(contextArg.capture(), eq(request), eq(response));\n\t\tassertThat(contextArg.getValue().getAuthentication().getPrincipal()).isEqualTo(authRequest.getName());\n\t}\n\n\t@Test\n\tpublic void callsAuthenticationFailureHandlerOnFailedAuthentication() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setAuthenticationFailureHandler(new ForwardAuthenticationFailureHandler(\"/forwardUrl\"));\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(PreAuthenticatedAuthenticationToken.class)))\n\t\t\t.willThrow(new PreAuthenticatedCredentialsNotFoundException(\"invalid\"));\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(am).authenticate(any(PreAuthenticatedAuthenticationToken.class));\n\t\tassertThat(response.getForwardedUrl()).isEqualTo(\"/forwardUrl\");\n\t\tassertThat(request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).isNotNull();\n\t}\n\n\t// SEC-2078\n\t@Test\n\tpublic void requiresAuthenticationFalsePrincipalNotString() throws Exception {\n\t\tObject principal = new Object();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(principal, \"something\", \"ROLE_USER\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = principal;\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(am);\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationFalsePrincipalUser() throws Exception {\n\t\tUser currentPrincipal = new User(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tUsernamePasswordAuthenticationToken currentAuthentication = UsernamePasswordAuthenticationToken\n\t\t\t.authenticated(currentPrincipal, currentPrincipal.getPassword(), currentPrincipal.getAuthorities());\n\t\tSecurityContextHolder.getContext().setAuthentication(currentAuthentication);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = new User(currentPrincipal.getUsername(), currentPrincipal.getPassword(),\n\t\t\t\tAuthorityUtils.NO_AUTHORITIES);\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(am);\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationTruePrincipalNotString() throws Exception {\n\t\tObject currentPrincipal = new Object();\n\t\tTestingAuthenticationToken authRequest = new TestingAuthenticationToken(currentPrincipal, \"something\",\n\t\t\t\t\"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(authRequest);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = new Object();\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(am).authenticate(any(PreAuthenticatedAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationOverridePrincipalChangedTrue() throws Exception {\n\t\tObject principal = new Object();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(principal, \"something\", \"ROLE_USER\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter() {\n\t\t\t@Override\n\t\t\tprotected boolean principalChanged(HttpServletRequest request, Authentication currentAuthentication) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = principal;\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(am).authenticate(any(PreAuthenticatedAuthenticationToken.class));\n\t}\n\n\t@Test\n\tpublic void requiresAuthenticationOverridePrincipalChangedFalse() throws Exception {\n\t\tObject principal = new Object();\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(principal, \"something\", \"ROLE_USER\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter() {\n\t\t\t@Override\n\t\t\tprotected boolean principalChanged(HttpServletRequest request, Authentication currentAuthentication) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\tfilter.principal = principal;\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(am);\n\t}\n\n\t@Test\n\tpublic void requestNotMatchRequestMatcher() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setRequiresAuthenticationRequestMatcher(pathPattern(\"/no-matching\"));\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverifyNoMoreInteractions(am);\n\t}\n\n\t@Test\n\tpublic void requestMatchesRequestMatcher() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setRequiresAuthenticationRequestMatcher(pathPattern(\"/**\"));\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(am).authenticate(any(PreAuthenticatedAuthenticationToken.class));\n\t}\n\n\t@Test\n\tvoid doFilterWhenAuthenticatedThenCombinesAuthorities() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter = createFilterAuthenticatesWith(new TestingAuthenticationToken(\"username\", \"password\", \"TEST\"));\n\t\tthis.filter.setMfaEnabled(true);\n\t\tthis.filter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t// @formatter:off\n\t\tassertThat(authentication.getAuthorities())\n\t\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t\t.containsExactlyInAnyOrder(ROLE_EXISTING, \"TEST\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid doFilterWhenDefaultThenMfaDisabled() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tTestingAuthenticationToken newAuthn = new TestingAuthenticationToken(\"username\", \"password\", \"TEST\");\n\t\tthis.filter = createFilterAuthenticatesWith(newAuthn);\n\t\tthis.filter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isEqualTo(newAuthn);\n\t}\n\n\t// gh-18112\n\t@Test\n\tvoid doFilterWhenDifferentPrincipalThenDoesNotCombine() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tTestingAuthenticationToken newAuthn = new TestingAuthenticationToken(existingAuthn.getName() + \"different\",\n\t\t\t\t\"password\", \"TEST\");\n\t\tthis.filter = createFilterAuthenticatesWith(newAuthn);\n\t\tthis.filter.setMfaEnabled(true);\n\t\tthis.filter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isEqualTo(newAuthn);\n\t}\n\n\t/**\n\t * This is critical to avoid adding duplicate GrantedAuthority instances with the\n\t * same' authority when the issuedAt is too old and a new instance is requested.\n\t * @throws Exception\n\t */\n\t@Test\n\tvoid doFilterWhenDefaultEqualsGrantedAuthorityThenNoDuplicates() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tnew DefaultEqualsGrantedAuthority());\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter = createFilterAuthenticatesWith(\n\t\t\t\tnew TestingAuthenticationToken(\"username\", \"password\", new DefaultEqualsGrantedAuthority()));\n\t\tthis.filter.setMfaEnabled(true);\n\t\tthis.filter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\t// @formatter:off\n\t\tassertThat(new ArrayList<GrantedAuthority>(authentication.getAuthorities()))\n\t\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t\t.containsExactly(DefaultEqualsGrantedAuthority.AUTHORITY);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\", \"FACTORONE\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter = createFilterAuthenticatesWith(\n\t\t\t\tnew NonBuildableAuthenticationToken(\"username\", \"password\", \"FACTORTWO\"));\n\t\tthis.filter.setMfaEnabled(true);\n\t\tthis.filter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.authorities()\n\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactly(\"FACTORTWO\");\n\t}\n\n\tprivate AbstractPreAuthenticatedProcessingFilter createFilterAuthenticatesWith(Authentication authentication) {\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tfilter.setRequiresAuthenticationRequestMatcher(AnyRequestMatcher.INSTANCE);\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any())).willReturn(authentication);\n\t\tfilter.setAuthenticationManager(am);\n\t\treturn filter;\n\t}\n\n\tprivate void testDoFilter(boolean grantAccess) throws Exception {\n\t\tMockHttpServletRequest req = new MockHttpServletRequest();\n\t\tMockHttpServletResponse res = new MockHttpServletResponse();\n\t\tgetFilter(grantAccess).doFilter(req, res, new MockFilterChain());\n\t\tassertThat(null != SecurityContextHolder.getContext().getAuthentication()).isEqualTo(grantAccess);\n\t}\n\n\tprivate static ConcretePreAuthenticatedProcessingFilter getFilter(boolean grantAccess) {\n\t\tConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tif (!grantAccess) {\n\t\t\tgiven(am.authenticate(any(Authentication.class))).willThrow(new BadCredentialsException(\"\"));\n\t\t}\n\t\telse {\n\t\t\tgiven(am.authenticate(any(Authentication.class)))\n\t\t\t\t.willAnswer((Answer<Authentication>) (invocation) -> (Authentication) invocation.getArguments()[0]);\n\t\t}\n\t\tfilter.setAuthenticationManager(am);\n\t\tfilter.afterPropertiesSet();\n\t\treturn filter;\n\t}\n\n\tprivate static class ConcretePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {\n\n\t\tprivate Object principal = \"testPrincipal\";\n\n\t\tprivate boolean initFilterBeanInvoked;\n\n\t\t@Override\n\t\tprotected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {\n\t\t\treturn this.principal;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {\n\t\t\treturn \"testCredentials\";\n\t\t}\n\n\t\t@Override\n\t\tprotected void initFilterBean() throws ServletException {\n\t\t\tsuper.initFilterBean();\n\t\t\tthis.initFilterBeanInvoked = true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/Http403ForbiddenEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class Http403ForbiddenEntryPointTests {\n\n\tpublic void testCommence() throws IOException {\n\t\tMockHttpServletRequest req = new MockHttpServletRequest();\n\t\tMockHttpServletResponse resp = new MockHttpServletResponse();\n\t\tHttp403ForbiddenEntryPoint fep = new Http403ForbiddenEntryPoint();\n\t\tfep.commence(req, resp, new AuthenticationCredentialsNotFoundException(\"test\"));\n\t\tassertThat(resp.getStatus()).withFailMessage(\"Incorrect status\").isEqualTo(HttpServletResponse.SC_FORBIDDEN);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.Collection;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.AuthenticationUserDetailsService;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author TSARDD\n * @since 18-okt-2007\n */\npublic class PreAuthenticatedAuthenticationProviderTests {\n\n\t@Test\n\tpublic final void afterPropertiesSet() {\n\t\tPreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();\n\t\tassertThatIllegalArgumentException().isThrownBy(provider::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic final void authenticateInvalidToken() throws Exception {\n\t\tUserDetails ud = new User(\"dummyUser\", \"dummyPwd\", true, true, true, true, AuthorityUtils.NO_AUTHORITIES);\n\t\tPreAuthenticatedAuthenticationProvider provider = getProvider(ud);\n\t\tAuthentication request = UsernamePasswordAuthenticationToken.unauthenticated(\"dummyUser\", \"dummyPwd\");\n\t\tAuthentication result = provider.authenticate(request);\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic final void nullPrincipalReturnsNullAuthentication() {\n\t\tPreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();\n\t\tAuthentication request = new PreAuthenticatedAuthenticationToken(null, \"dummyPwd\");\n\t\tAuthentication result = provider.authenticate(request);\n\t\tassertThat(result).isNull();\n\t}\n\n\t@Test\n\tpublic final void authenticateKnownUser() throws Exception {\n\t\tUserDetails ud = new User(\"dummyUser\", \"dummyPwd\", true, true, true, true, AuthorityUtils.NO_AUTHORITIES);\n\t\tPreAuthenticatedAuthenticationProvider provider = getProvider(ud);\n\t\tAuthentication request = new PreAuthenticatedAuthenticationToken(\"dummyUser\", \"dummyPwd\");\n\t\tAuthentication result = provider.authenticate(request);\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(ud).isEqualTo(result.getPrincipal());\n\t\t// @TODO: Add more asserts?\n\t}\n\n\t@Test\n\tpublic final void authenticateIgnoreCredentials() throws Exception {\n\t\tUserDetails ud = new User(\"dummyUser1\", \"dummyPwd1\", true, true, true, true, AuthorityUtils.NO_AUTHORITIES);\n\t\tPreAuthenticatedAuthenticationProvider provider = getProvider(ud);\n\t\tAuthentication request = new PreAuthenticatedAuthenticationToken(\"dummyUser1\", \"dummyPwd2\");\n\t\tAuthentication result = provider.authenticate(request);\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(ud).isEqualTo(result.getPrincipal());\n\t\t// @TODO: Add more asserts?\n\t}\n\n\t@Test\n\tpublic final void authenticateUnknownUserThrowsException() throws Exception {\n\t\tUserDetails ud = new User(\"dummyUser1\", \"dummyPwd\", true, true, true, true, AuthorityUtils.NO_AUTHORITIES);\n\t\tPreAuthenticatedAuthenticationProvider provider = getProvider(ud);\n\t\tAuthentication request = new PreAuthenticatedAuthenticationToken(\"dummyUser2\", \"dummyPwd\");\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(request));\n\t}\n\n\t@Test\n\tvoid authenticateWhenSuccessThenIssuesFactor() {\n\t\tUserDetails ud = PasswordEncodedUser.user();\n\t\tPreAuthenticatedAuthenticationProvider provider = getProvider(ud);\n\t\tSupplier<Collection<GrantedAuthority>> authorities = mock(Supplier.class);\n\t\tgiven(authorities.get()).willReturn(AuthorityUtils.createAuthorityList(\"FACTOR\"));\n\t\tprovider.setGrantedAuthoritySupplier(authorities);\n\t\tAuthentication request = new PreAuthenticatedAuthenticationToken(ud.getUsername(), ud.getPassword());\n\t\tAuthentication result = provider.authenticate(request);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(\"FACTOR\");\n\t\tverify(authorities).get();\n\t}\n\n\t@Test\n\tpublic final void supportsArbitraryObject() throws Exception {\n\t\tPreAuthenticatedAuthenticationProvider provider = getProvider(null);\n\t\tassertThat(provider.supports(Authentication.class)).isFalse();\n\t}\n\n\t@Test\n\tpublic final void supportsPreAuthenticatedAuthenticationToken() throws Exception {\n\t\tPreAuthenticatedAuthenticationProvider provider = getProvider(null);\n\t\tassertThat(provider.supports(PreAuthenticatedAuthenticationToken.class)).isTrue();\n\t}\n\n\t@Test\n\tpublic void getSetOrder() throws Exception {\n\t\tPreAuthenticatedAuthenticationProvider provider = getProvider(null);\n\t\tprovider.setOrder(333);\n\t\tassertThat(333).isEqualTo(provider.getOrder());\n\t}\n\n\tprivate PreAuthenticatedAuthenticationProvider getProvider(UserDetails aUserDetails) {\n\t\tPreAuthenticatedAuthenticationProvider result = new PreAuthenticatedAuthenticationProvider();\n\t\tresult.setPreAuthenticatedUserDetailsService(getPreAuthenticatedUserDetailsService(aUserDetails));\n\t\tresult.afterPropertiesSet();\n\t\treturn result;\n\t}\n\n\tprivate AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getPreAuthenticatedUserDetailsService(\n\t\t\tfinal UserDetails aUserDetails) {\n\t\treturn (token) -> {\n\t\t\tif (aUserDetails != null && aUserDetails.getUsername().equals(token.getName())) {\n\t\t\t\treturn aUserDetails;\n\t\t\t}\n\t\t\tthrow new UsernameNotFoundException(\"notfound\");\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author TSARDD\n * @since 18-okt-2007\n */\npublic class PreAuthenticatedAuthenticationTokenTests {\n\n\t@Test\n\tpublic void testPreAuthenticatedAuthenticationTokenRequestWithDetails() {\n\t\tObject principal = \"dummyUser\";\n\t\tObject credentials = \"dummyCredentials\";\n\t\tObject details = \"dummyDetails\";\n\t\tPreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, credentials);\n\t\ttoken.setDetails(details);\n\t\tassertThat(token.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(token.getCredentials()).isEqualTo(credentials);\n\t\tassertThat(token.getDetails()).isEqualTo(details);\n\t\tassertThat(token.getAuthorities()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void testPreAuthenticatedAuthenticationTokenRequestWithoutDetails() {\n\t\tObject principal = \"dummyUser\";\n\t\tObject credentials = \"dummyCredentials\";\n\t\tPreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, credentials);\n\t\tassertThat(token.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(token.getCredentials()).isEqualTo(credentials);\n\t\tassertThat(token.getDetails()).isNull();\n\t\tassertThat(token.getAuthorities()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void testPreAuthenticatedAuthenticationTokenResponse() {\n\t\tObject principal = \"dummyUser\";\n\t\tObject credentials = \"dummyCredentials\";\n\t\tList<GrantedAuthority> gas = AuthorityUtils.createAuthorityList(\"Role1\");\n\t\tPreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(principal, credentials,\n\t\t\t\tgas);\n\t\tassertThat(token.getPrincipal()).isEqualTo(principal);\n\t\tassertThat(token.getCredentials()).isEqualTo(credentials);\n\t\tassertThat(token.getDetails()).isNull();\n\t\tassertThat(token.getAuthorities()).isNotNull();\n\t\tCollection<GrantedAuthority> resultColl = token.getAuthorities();\n\t\tassertThat(gas.containsAll(resultColl) && resultColl.containsAll(gas))\n\t\t\t.withFailMessage(\"GrantedAuthority collections do not match; result: \" + resultColl + \", expected: \" + gas)\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void toBuilderWhenApplyThenCopies() {\n\t\tPreAuthenticatedAuthenticationToken factorOne = new PreAuthenticatedAuthenticationToken(\"alice\", \"pass\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"));\n\t\tPreAuthenticatedAuthenticationToken factorTwo = new PreAuthenticatedAuthenticationToken(\"bob\", \"ssap\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"));\n\t\tPreAuthenticatedAuthenticationToken result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.credentials(factorTwo.getCredentials())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(result.getCredentials()).isSameAs(factorTwo.getCredentials());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedGrantedAuthoritiesUserDetailsServiceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.GrantedAuthoritiesContainer;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author TSARDD\n * @since 18-okt-2007\n */\npublic class PreAuthenticatedGrantedAuthoritiesUserDetailsServiceTests {\n\n\t@Test\n\tpublic void testGetUserDetailsInvalidType() {\n\t\tPreAuthenticatedGrantedAuthoritiesUserDetailsService svc = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();\n\t\tPreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(\"dummy\", \"dummy\");\n\t\ttoken.setDetails(new Object());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> svc.loadUserDetails(token));\n\t}\n\n\t@Test\n\tpublic void testGetUserDetailsNoDetails() {\n\t\tPreAuthenticatedGrantedAuthoritiesUserDetailsService svc = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();\n\t\tPreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(\"dummy\", \"dummy\");\n\t\ttoken.setDetails(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> svc.loadUserDetails(token));\n\t}\n\n\t@Test\n\tpublic void testGetUserDetailsEmptyAuthorities() {\n\t\tfinal String userName = \"dummyUser\";\n\t\ttestGetUserDetails(userName, AuthorityUtils.NO_AUTHORITIES);\n\t}\n\n\t@Test\n\tpublic void testGetUserDetailsWithAuthorities() {\n\t\tfinal String userName = \"dummyUser\";\n\t\ttestGetUserDetails(userName, AuthorityUtils.createAuthorityList(\"Role1\", \"Role2\"));\n\t}\n\n\tprivate void testGetUserDetails(final String userName, final List<GrantedAuthority> gas) {\n\t\tPreAuthenticatedGrantedAuthoritiesUserDetailsService svc = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();\n\t\tPreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(userName, \"dummy\");\n\t\ttoken.setDetails((GrantedAuthoritiesContainer) () -> gas);\n\t\tUserDetails ud = svc.loadUserDetails(token);\n\t\tassertThat(ud.isAccountNonExpired()).isTrue();\n\t\tassertThat(ud.isAccountNonLocked()).isTrue();\n\t\tassertThat(ud.isCredentialsNonExpired()).isTrue();\n\t\tassertThat(ud.isEnabled()).isTrue();\n\t\tassertThat(userName).isEqualTo(ud.getUsername());\n\t\t// Password is not saved by\n\t\t// PreAuthenticatedGrantedAuthoritiesUserDetailsService\n\t\t// assertThat(password).isEqualTo(ud.getPassword());\n\t\tassertThat(gas.containsAll(ud.getAuthorities()) && ud.getAuthorities().containsAll(gas))\n\t\t\t.withFailMessage(\n\t\t\t\t\t\"GrantedAuthority collections do not match; result: \" + ud.getAuthorities() + \", expected: \" + gas)\n\t\t\t.isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author TSARDD\n */\npublic class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests {\n\n\tList<GrantedAuthority> gas = AuthorityUtils.createAuthorityList(\"Role1\", \"Role2\");\n\n\t@Test\n\tpublic void testToString() {\n\t\tPreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(\n\t\t\t\tgetRequest(\"testUser\", new String[] {}), this.gas);\n\t\tString toString = details.toString();\n\t\tassertThat(toString.contains(\"Role1\")).as(\"toString should contain Role1\").isTrue();\n\t\tassertThat(toString.contains(\"Role2\")).as(\"toString should contain Role2\").isTrue();\n\t}\n\n\t@Test\n\tpublic void testGetSetPreAuthenticatedGrantedAuthorities() {\n\t\tPreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(\n\t\t\t\tgetRequest(\"testUser\", new String[] {}), this.gas);\n\t\tList<GrantedAuthority> returnedGas = details.getGrantedAuthorities();\n\t\tassertThat(this.gas.containsAll(returnedGas) && returnedGas.containsAll(this.gas))\n\t\t\t.withFailMessage(\n\t\t\t\t\t\"Collections do not contain same elements; expected: \" + this.gas + \", returned: \" + returnedGas)\n\t\t\t.isTrue();\n\t}\n\n\tprivate HttpServletRequest getRequest(final String userName, final String[] aRoles) {\n\t\tMockHttpServletRequest req = new MockHttpServletRequest() {\n\t\t\tprivate Set<String> roles = new HashSet<>(Arrays.asList(aRoles));\n\n\t\t\t@Override\n\t\t\tpublic boolean isUserInRole(String arg0) {\n\t\t\t\treturn this.roles.contains(arg0);\n\t\t\t}\n\t\t};\n\t\treq.setRemoteUser(userName);\n\t\treturn req;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/RequestAttributeAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Milan Sevcik\n */\npublic class RequestAttributeAuthenticationFilterTests {\n\n\t@AfterEach\n\t@BeforeEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void rejectsMissingHeader() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestAttributeAuthenticationFilter filter = new RequestAttributeAuthenticationFilter();\n\t\tassertThatExceptionOfType(PreAuthenticatedCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(request, response, chain));\n\t}\n\n\t@Test\n\tpublic void defaultsToUsingSiteminderHeader() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"REMOTE_USER\", \"cat\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestAttributeAuthenticationFilter filter = new RequestAttributeAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"cat\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo(\"N/A\");\n\t}\n\n\t@Test\n\tpublic void alternativeHeaderNameIsSupported() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"myUsernameVariable\", \"wolfman\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestAttributeAuthenticationFilter filter = new RequestAttributeAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.setPrincipalEnvironmentVariable(\"myUsernameVariable\");\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"wolfman\");\n\t}\n\n\t@Test\n\tpublic void credentialsAreRetrievedIfHeaderNameIsSet() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestAttributeAuthenticationFilter filter = new RequestAttributeAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.setCredentialsEnvironmentVariable(\"myCredentialsVariable\");\n\t\trequest.setAttribute(\"REMOTE_USER\", \"cat\");\n\t\trequest.setAttribute(\"myCredentialsVariable\", \"catspassword\");\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo(\"catspassword\");\n\t}\n\n\t@Test\n\tpublic void userIsReauthenticatedIfPrincipalChangesAndCheckForPrincipalChangesIsSet() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestAttributeAuthenticationFilter filter = new RequestAttributeAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\trequest.setAttribute(\"REMOTE_USER\", \"cat\");\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\trequest = new MockHttpServletRequest();\n\t\trequest.setAttribute(\"REMOTE_USER\", \"dog\");\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication dog = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(dog).isNotNull();\n\t\tassertThat(dog.getName()).isEqualTo(\"dog\");\n\t\t// Make sure authentication doesn't occur every time (i.e. if the variable\n\t\t// *doesn't*\n\t\t// change)\n\t\tfilter.setAuthenticationManager(mock(AuthenticationManager.class));\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(dog);\n\t}\n\n\t@Test\n\tpublic void missingHeaderCausesException() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestAttributeAuthenticationFilter filter = new RequestAttributeAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tassertThatExceptionOfType(PreAuthenticatedCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(request, response, chain));\n\t}\n\n\t@Test\n\tpublic void missingHeaderIsIgnoredIfExceptionIfHeaderMissingIsFalse() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestAttributeAuthenticationFilter filter = new RequestAttributeAuthenticationFilter();\n\t\tfilter.setExceptionIfVariableMissing(false);\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.doFilter(request, response, chain);\n\t}\n\n\t/**\n\t * Create an authentication manager which returns the passed in object.\n\t */\n\tprivate AuthenticationManager createAuthenticationManager() {\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class)))\n\t\t\t.willAnswer((Answer<Authentication>) (invocation) -> (Authentication) invocation.getArguments()[0]);\n\t\treturn am;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/header/RequestHeaderAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.header;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;\nimport org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class RequestHeaderAuthenticationFilterTests {\n\n\t@AfterEach\n\t@BeforeEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void rejectsMissingHeader() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();\n\t\tassertThatExceptionOfType(PreAuthenticatedCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(request, response, chain));\n\t}\n\n\t@Test\n\tpublic void defaultsToUsingSiteminderHeader() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"SM_USER\", \"cat\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"cat\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo(\"N/A\");\n\t}\n\n\t@Test\n\tpublic void alternativeHeaderNameIsSupported() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"myUsernameHeader\", \"wolfman\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.setPrincipalRequestHeader(\"myUsernameHeader\");\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"wolfman\");\n\t}\n\n\t@Test\n\tpublic void credentialsAreRetrievedIfHeaderNameIsSet() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.setCredentialsRequestHeader(\"myCredentialsHeader\");\n\t\trequest.addHeader(\"SM_USER\", \"cat\");\n\t\trequest.addHeader(\"myCredentialsHeader\", \"catspassword\");\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo(\"catspassword\");\n\t}\n\n\t@Test\n\tpublic void userIsReauthenticatedIfPrincipalChangesAndCheckForPrincipalChangesIsSet() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.setCheckForPrincipalChanges(true);\n\t\trequest.addHeader(\"SM_USER\", \"cat\");\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\trequest = new MockHttpServletRequest();\n\t\trequest.addHeader(\"SM_USER\", \"dog\");\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication dog = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(dog).isNotNull();\n\t\tassertThat(dog.getName()).isEqualTo(\"dog\");\n\t\t// Make sure authentication doesn't occur every time (i.e. if the header *doesn't\n\t\t// change)\n\t\tfilter.setAuthenticationManager(mock(AuthenticationManager.class));\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(dog);\n\t}\n\n\t@Test\n\tpublic void missingHeaderCausesException() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tassertThatExceptionOfType(PreAuthenticatedCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(request, response, chain));\n\t}\n\n\t@Test\n\tpublic void missingHeaderIsIgnoredIfExceptionIfHeaderMissingIsFalse() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tRequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();\n\t\tfilter.setExceptionIfHeaderMissing(false);\n\t\tfilter.setAuthenticationManager(createAuthenticationManager());\n\t\tfilter.doFilter(request, response, chain);\n\t}\n\n\t/**\n\t * Create an authentication manager which returns the passed in object.\n\t */\n\tprivate AuthenticationManager createAuthenticationManager() {\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class)))\n\t\t\t.willAnswer((Answer<Authentication>) (invocation) -> (Authentication) invocation.getArguments()[0]);\n\t\treturn am;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.j2ee;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.mapping.Attributes2GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.MappableAttributesRetriever;\nimport org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper;\nimport org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author TSARDD\n */\npublic class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests {\n\n\t@Test\n\tpublic final void testAfterPropertiesSetException() {\n\t\tJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource t = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();\n\t\tassertThatIllegalArgumentException().isThrownBy(t::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic final void testBuildDetailsHttpServletRequestNoMappedNoUserRoles() {\n\t\tString[] mappedRoles = new String[] {};\n\t\tString[] roles = new String[] {};\n\t\tString[] expectedRoles = new String[] {};\n\t\ttestDetails(mappedRoles, roles, expectedRoles);\n\t}\n\n\t@Test\n\tpublic final void testBuildDetailsHttpServletRequestNoMappedUnmappedUserRoles() {\n\t\tString[] mappedRoles = new String[] {};\n\t\tString[] roles = new String[] { \"Role1\", \"Role2\" };\n\t\tString[] expectedRoles = new String[] {};\n\t\ttestDetails(mappedRoles, roles, expectedRoles);\n\t}\n\n\t@Test\n\tpublic final void testBuildDetailsHttpServletRequestNoUserRoles() {\n\t\tString[] mappedRoles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\" };\n\t\tString[] roles = new String[] {};\n\t\tString[] expectedRoles = new String[] {};\n\t\ttestDetails(mappedRoles, roles, expectedRoles);\n\t}\n\n\t@Test\n\tpublic final void testBuildDetailsHttpServletRequestAllUserRoles() {\n\t\tString[] mappedRoles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\" };\n\t\tString[] roles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\" };\n\t\tString[] expectedRoles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\" };\n\t\ttestDetails(mappedRoles, roles, expectedRoles);\n\t}\n\n\t@Test\n\tpublic final void testBuildDetailsHttpServletRequestUnmappedUserRoles() {\n\t\tString[] mappedRoles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\" };\n\t\tString[] roles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\", \"Role5\" };\n\t\tString[] expectedRoles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\" };\n\t\ttestDetails(mappedRoles, roles, expectedRoles);\n\t}\n\n\t@Test\n\tpublic final void testBuildDetailsHttpServletRequestPartialUserRoles() {\n\t\tString[] mappedRoles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\" };\n\t\tString[] roles = new String[] { \"Role2\", \"Role3\" };\n\t\tString[] expectedRoles = new String[] { \"Role2\", \"Role3\" };\n\t\ttestDetails(mappedRoles, roles, expectedRoles);\n\t}\n\n\t@Test\n\tpublic final void testBuildDetailsHttpServletRequestPartialAndUnmappedUserRoles() {\n\t\tString[] mappedRoles = new String[] { \"Role1\", \"Role2\", \"Role3\", \"Role4\" };\n\t\tString[] roles = new String[] { \"Role2\", \"Role3\", \"Role5\" };\n\t\tString[] expectedRoles = new String[] { \"Role2\", \"Role3\" };\n\t\ttestDetails(mappedRoles, roles, expectedRoles);\n\t}\n\n\tprivate void testDetails(String[] mappedRoles, String[] userRoles, String[] expectedRoles) {\n\t\tJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource src = getJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource(\n\t\t\t\tmappedRoles);\n\t\tObject o = src.buildDetails(getRequest(\"testUser\", userRoles));\n\t\tassertThat(o).isNotNull();\n\t\tassertThat(o instanceof PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails).withFailMessage(\n\t\t\t\t\"Returned object not of type PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails, actual type: \"\n\t\t\t\t\t\t+ o.getClass())\n\t\t\t.isTrue();\n\t\tPreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = (PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails) o;\n\t\tList<GrantedAuthority> gas = details.getGrantedAuthorities();\n\t\tassertThat(gas).as(\"Granted authorities should not be null\").isNotNull();\n\t\tassertThat(gas).hasSize(expectedRoles.length);\n\t\tCollection<String> expectedRolesColl = Arrays.asList(expectedRoles);\n\t\tCollection<String> gasRolesSet = new HashSet<>();\n\t\tfor (GrantedAuthority grantedAuthority : gas) {\n\t\t\tgasRolesSet.add(grantedAuthority.getAuthority());\n\t\t}\n\t\tassertThat(expectedRolesColl.containsAll(gasRolesSet) && gasRolesSet.containsAll(expectedRolesColl))\n\t\t\t.withFailMessage(\"Granted Authorities do not match expected roles\")\n\t\t\t.isTrue();\n\t}\n\n\tprivate J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource getJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource(\n\t\t\tString[] mappedRoles) {\n\t\tJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource result = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();\n\t\tresult.setMappableRolesRetriever(getMappableRolesRetriever(mappedRoles));\n\t\tresult.setUserRoles2GrantedAuthoritiesMapper(getJ2eeUserRoles2GrantedAuthoritiesMapper());\n\t\tresult.afterPropertiesSet();\n\t\treturn result;\n\t}\n\n\tprivate MappableAttributesRetriever getMappableRolesRetriever(String[] mappedRoles) {\n\t\tSimpleMappableAttributesRetriever result = new SimpleMappableAttributesRetriever();\n\t\tresult.setMappableAttributes(new HashSet<>(Arrays.asList(mappedRoles)));\n\t\treturn result;\n\t}\n\n\tprivate Attributes2GrantedAuthoritiesMapper getJ2eeUserRoles2GrantedAuthoritiesMapper() {\n\t\tSimpleAttributes2GrantedAuthoritiesMapper result = new SimpleAttributes2GrantedAuthoritiesMapper();\n\t\tresult.setAddPrefixIfAlreadyExisting(false);\n\t\tresult.setConvertAttributeToLowerCase(false);\n\t\tresult.setConvertAttributeToUpperCase(false);\n\t\tresult.setAttributePrefix(\"\");\n\t\treturn result;\n\t}\n\n\tprivate HttpServletRequest getRequest(final String userName, final String[] aRoles) {\n\t\tMockHttpServletRequest req = new MockHttpServletRequest() {\n\t\t\tprivate Set<String> roles = new HashSet<>(Arrays.asList(aRoles));\n\n\t\t\t@Override\n\t\t\tpublic boolean isUserInRole(String arg0) {\n\t\t\t\treturn this.roles.contains(arg0);\n\t\t\t}\n\t\t};\n\t\treq.setRemoteUser(userName);\n\t\treturn req;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/j2ee/J2eePreAuthenticatedProcessingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.j2ee;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author TSARDD\n * @since 18-okt-2007\n */\npublic class J2eePreAuthenticatedProcessingFilterTests {\n\n\t@Test\n\tpublic final void testGetPreAuthenticatedPrincipal() {\n\t\tString user = \"testUser\";\n\t\tassertThat(user).isEqualTo(new J2eePreAuthenticatedProcessingFilter()\n\t\t\t.getPreAuthenticatedPrincipal(getRequest(user, new String[] {})));\n\t}\n\n\t@Test\n\tpublic final void testGetPreAuthenticatedCredentials() {\n\t\tassertThat(\"N/A\").isEqualTo(new J2eePreAuthenticatedProcessingFilter()\n\t\t\t.getPreAuthenticatedCredentials(getRequest(\"testUser\", new String[] {})));\n\t}\n\n\tprivate HttpServletRequest getRequest(final String aUserName, final String[] aRoles) {\n\t\tMockHttpServletRequest req = new MockHttpServletRequest() {\n\t\t\tprivate Set<String> roles = new HashSet<>(Arrays.asList(aRoles));\n\n\t\t\t@Override\n\t\t\tpublic boolean isUserInRole(String arg0) {\n\t\t\t\treturn this.roles.contains(arg0);\n\t\t\t}\n\t\t};\n\t\treq.setRemoteUser(aUserName);\n\t\treq.setUserPrincipal(() -> aUserName);\n\t\treturn req;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlJ2eeDefinedRolesRetrieverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.j2ee;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class WebXmlJ2eeDefinedRolesRetrieverTests {\n\n\t@Test\n\tpublic void testRole1To4Roles() throws Exception {\n\t\tList<String> ROLE1TO4_EXPECTED_ROLES = Arrays.asList(\"Role1\", \"Role2\", \"Role3\", \"Role4\");\n\t\tfinal Resource webXml = new ClassPathResource(\"webxml/Role1-4.web.xml\");\n\t\tWebXmlMappableAttributesRetriever rolesRetriever = new WebXmlMappableAttributesRetriever();\n\t\trolesRetriever.setResourceLoader(new ResourceLoader() {\n\t\t\t@Override\n\t\t\tpublic ClassLoader getClassLoader() {\n\t\t\t\treturn Thread.currentThread().getContextClassLoader();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Resource getResource(String location) {\n\t\t\t\treturn webXml;\n\t\t\t}\n\t\t});\n\t\trolesRetriever.afterPropertiesSet();\n\t\tSet<String> j2eeRoles = rolesRetriever.getMappableAttributes();\n\t\tassertThat(j2eeRoles).containsAll(ROLE1TO4_EXPECTED_ROLES);\n\t}\n\n\t@Test\n\tpublic void testGetZeroJ2eeRoles() throws Exception {\n\t\tfinal Resource webXml = new ClassPathResource(\"webxml/NoRoles.web.xml\");\n\t\tWebXmlMappableAttributesRetriever rolesRetriever = new WebXmlMappableAttributesRetriever();\n\t\trolesRetriever.setResourceLoader(new ResourceLoader() {\n\t\t\t@Override\n\t\t\tpublic ClassLoader getClassLoader() {\n\t\t\t\treturn Thread.currentThread().getContextClassLoader();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Resource getResource(String location) {\n\t\t\t\treturn webXml;\n\t\t\t}\n\t\t});\n\t\trolesRetriever.afterPropertiesSet();\n\t\tSet<String> j2eeRoles = rolesRetriever.getMappableAttributes();\n\t\tassertThat(j2eeRoles).isEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/websphere/WebSpherePreAuthenticatedProcessingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.websphere;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Luke Taylor\n */\npublic class WebSpherePreAuthenticatedProcessingFilterTests {\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void principalsAndCredentialsAreExtractedCorrectly() throws Exception {\n\t\tnew WebSpherePreAuthenticatedProcessingFilter();\n\t\tWASUsernameAndGroupsExtractor helper = mock(WASUsernameAndGroupsExtractor.class);\n\t\tgiven(helper.getCurrentUserName()).willReturn(\"jerry\");\n\t\tWebSpherePreAuthenticatedProcessingFilter filter = new WebSpherePreAuthenticatedProcessingFilter(helper);\n\t\tassertThat(filter.getPreAuthenticatedPrincipal(new MockHttpServletRequest())).isEqualTo(\"jerry\");\n\t\tassertThat(filter.getPreAuthenticatedCredentials(new MockHttpServletRequest())).isEqualTo(\"N/A\");\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class)))\n\t\t\t.willAnswer((Answer<Authentication>) (invocation) -> (Authentication) invocation.getArguments()[0]);\n\t\tfilter.setAuthenticationManager(am);\n\t\tWebSpherePreAuthenticatedWebAuthenticationDetailsSource ads = new WebSpherePreAuthenticatedWebAuthenticationDetailsSource(\n\t\t\t\thelper);\n\t\tads.setWebSphereGroups2GrantedAuthoritiesMapper(new SimpleAttributes2GrantedAuthoritiesMapper());\n\t\tfilter.setAuthenticationDetailsSource(ads);\n\t\tfilter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), mock(FilterChain.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/x509/SubjectDnX509PrincipalExtractorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.x509;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.security.authentication.BadCredentialsException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n */\npublic class SubjectDnX509PrincipalExtractorTests {\n\n\tSubjectDnX509PrincipalExtractor extractor;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.extractor = new SubjectDnX509PrincipalExtractor();\n\t}\n\n\t@Test\n\tpublic void invalidRegexFails() {\n\t\t// missing closing bracket on group\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.extractor.setSubjectDnRegex(\"CN=(.*?,\"));\n\t}\n\n\t@Test\n\tpublic void defaultCNPatternReturnsExpectedPrincipal() throws Exception {\n\t\tObject principal = this.extractor.extractPrincipal(X509TestUtils.buildTestCertificate());\n\t\tassertThat(principal).isEqualTo(\"Luke Taylor\");\n\t}\n\n\t@Test\n\tpublic void matchOnEmailReturnsExpectedPrincipal() throws Exception {\n\t\tthis.extractor.setSubjectDnRegex(\"emailAddress=(.*?),\");\n\t\tObject principal = this.extractor.extractPrincipal(X509TestUtils.buildTestCertificate());\n\t\tassertThat(principal).isEqualTo(\"luke@monkeymachine\");\n\t}\n\n\t@Test\n\tpublic void matchOnShoeSizeThrowsBadCredentials() throws Exception {\n\t\tthis.extractor.setSubjectDnRegex(\"shoeSize=(.*?),\");\n\t\tassertThatExceptionOfType(BadCredentialsException.class)\n\t\t\t.isThrownBy(() -> this.extractor.extractPrincipal(X509TestUtils.buildTestCertificate()));\n\t}\n\n\t@Test\n\tpublic void defaultCNPatternReturnsPrincipalAtEndOfDNString() throws Exception {\n\t\tObject principal = this.extractor.extractPrincipal(X509TestUtils.buildTestCertificateWithCnAtEnd());\n\t\tassertThat(principal).isEqualTo(\"Duke\");\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.extractor.setMessageSource(null));\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNotNullThenCanGet() {\n\t\tMessageSource source = mock(MessageSource.class);\n\t\tthis.extractor.setMessageSource(source);\n\t\tString code = \"code\";\n\t\tthis.extractor.messages.getMessage(code);\n\t\tverify(source).getMessage(eq(code), any(), any());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/x509/SubjectX500PrincipalExtractorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.x509;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link SubjectX500PrincipalExtractor}.\n *\n * @author Max Batischev\n */\npublic class SubjectX500PrincipalExtractorTests {\n\n\tprivate final SubjectX500PrincipalExtractor extractor = new SubjectX500PrincipalExtractor();\n\n\t@Test\n\tvoid extractWhenCnPatternSetThenExtractsPrincipalName() throws Exception {\n\t\tObject principal = this.extractor.extractPrincipal(X509TestUtils.buildTestCertificate());\n\n\t\tassertThat(principal).isEqualTo(\"Luke Taylor\");\n\t}\n\n\t@Test\n\tvoid extractWhenEmailPatternSetThenExtractsPrincipalName() throws Exception {\n\t\tthis.extractor.setExtractPrincipalNameFromEmail(true);\n\n\t\tObject principal = this.extractor.extractPrincipal(X509TestUtils.buildTestCertificate());\n\n\t\tassertThat(principal).isEqualTo(\"luke@monkeymachine\");\n\t}\n\n\t@Test\n\tvoid extractWhenCnAtEndThenExtractsPrincipalName() throws Exception {\n\t\tObject principal = this.extractor.extractPrincipal(X509TestUtils.buildTestCertificateWithCnAtEnd());\n\n\t\tassertThat(principal).isEqualTo(\"Duke\");\n\t}\n\n\t@Test\n\tvoid setMessageSourceWhenNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.extractor.setMessageSource(null));\n\t}\n\n\t@Test\n\tvoid extractWhenCertificateIsNullThenFails() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.extractor.extractPrincipal(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/preauth/x509/X509TestUtils.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.preauth.x509;\n\nimport java.io.ByteArrayInputStream;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\n/**\n * Certificate creation utility for use in X.509 tests.\n *\n * @author Luke Taylor\n */\npublic final class X509TestUtils {\n\n\tprivate X509TestUtils() {\n\t}\n\n\t/**\n\t * Builds an X.509 certificate. In human-readable form it is:\n\t *\n\t * <pre>\n\t * Certificate:\n\t *  Data:\n\t *    Version: 3 (0x2)\n\t *    Serial Number: 1 (0x1)\n\t *    Signature Algorithm: sha1WithRSAEncryption\n\t *    Issuer: CN=Monkey Machine CA, C=UK, ST=Scotland, L=Glasgow,\n\t *      O=monkeymachine.co.uk/emailAddress=ca@monkeymachine\n\t *    Validity\n\t *      Not Before: Mar  6 23:28:22 2005 GMT\n\t *      Not After : Mar  6 23:28:22 2006 GMT\n\t *    Subject: C=UK, ST=Scotland, L=Glasgow, O=Monkey Machine Ltd,\n\t *      OU=Open Source Development Lab., CN=Luke Taylor/emailAddress=luke@monkeymachine\n\t *    Subject Public Key Info:\n\t *      Public Key Algorithm: rsaEncryption\n\t *      RSA Public Key: (512 bit)\n\t *          [omitted]\n\t *    X509v3 extensions:\n\t *      X509v3 Basic Constraints:\n\t *      CA:FALSE\n\t *      Netscape Cert Type:\n\t *      SSL Client\n\t *      X509v3 Key Usage:\n\t *      Digital Signature, Non Repudiation, Key Encipherment\n\t *      X509v3 Subject Key Identifier:\n\t *      6E:E6:5B:57:33:CF:0E:2F:15:C2:F4:DF:EC:14:BE:FB:CF:54:56:3C\n\t *      X509v3 Authority Key Identifier:\n\t *      keyid:AB:78:EC:AF:10:1B:8A:9B:1F:C7:B1:25:8F:16:28:F2:17:9A:AD:36\n\t *      DirName:/CN=Monkey Machine CA/C=UK/ST=Scotland/L=Glasgow/O=monkeymachine.co.uk/emailAddress=ca@monkeymachine\n\t *      serial:00\n\t *      Netscape CA Revocation Url:\n\t *      https://monkeymachine.co.uk/ca-crl.pem\n\t *  Signature Algorithm: sha1WithRSAEncryption\n\t *             [signature omitted]\n\t * </pre>\n\t */\n\tpublic static X509Certificate buildTestCertificate() throws Exception {\n\t\tString cert = \"-----BEGIN CERTIFICATE-----\\n\"\n\t\t\t\t+ \"MIIEQTCCAymgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBkzEaMBgGA1UEAxMRTW9u\\n\"\n\t\t\t\t+ \"a2V5IE1hY2hpbmUgQ0ExCzAJBgNVBAYTAlVLMREwDwYDVQQIEwhTY290bGFuZDEQ\\n\"\n\t\t\t\t+ \"MA4GA1UEBxMHR2xhc2dvdzEcMBoGA1UEChMTbW9ua2V5bWFjaGluZS5jby51azEl\\n\"\n\t\t\t\t+ \"MCMGCSqGSIb3DQEJARYWY2FAbW9ua2V5bWFjaGluZS5jby51azAeFw0wNTAzMDYy\\n\"\n\t\t\t\t+ \"MzI4MjJaFw0wNjAzMDYyMzI4MjJaMIGvMQswCQYDVQQGEwJVSzERMA8GA1UECBMI\\n\"\n\t\t\t\t+ \"U2NvdGxhbmQxEDAOBgNVBAcTB0dsYXNnb3cxGzAZBgNVBAoTEk1vbmtleSBNYWNo\\n\"\n\t\t\t\t+ \"aW5lIEx0ZDElMCMGA1UECxMcT3BlbiBTb3VyY2UgRGV2ZWxvcG1lbnQgTGFiLjEU\\n\"\n\t\t\t\t+ \"MBIGA1UEAxMLTHVrZSBUYXlsb3IxITAfBgkqhkiG9w0BCQEWEmx1a2VAbW9ua2V5\\n\"\n\t\t\t\t+ \"bWFjaGluZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDItxZr07mm65ttYH7RMaVo\\n\"\n\t\t\t\t+ \"VeMCq4ptfn+GFFEk4+54OkDuh1CHlk87gEc1jx3ZpQPJRTJx31z3YkiAcP+RDzxr\\n\"\n\t\t\t\t+ \"AgMBAAGjggFIMIIBRDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIHgDALBgNV\\n\"\n\t\t\t\t+ \"HQ8EBAMCBeAwHQYDVR0OBBYEFG7mW1czzw4vFcL03+wUvvvPVFY8MIHABgNVHSME\\n\"\n\t\t\t\t+ \"gbgwgbWAFKt47K8QG4qbH8exJY8WKPIXmq02oYGZpIGWMIGTMRowGAYDVQQDExFN\\n\"\n\t\t\t\t+ \"b25rZXkgTWFjaGluZSBDQTELMAkGA1UEBhMCVUsxETAPBgNVBAgTCFNjb3RsYW5k\\n\"\n\t\t\t\t+ \"MRAwDgYDVQQHEwdHbGFzZ293MRwwGgYDVQQKExNtb25rZXltYWNoaW5lLmNvLnVr\\n\"\n\t\t\t\t+ \"MSUwIwYJKoZIhvcNAQkBFhZjYUBtb25rZXltYWNoaW5lLmNvLnVrggEAMDUGCWCG\\n\"\n\t\t\t\t+ \"SAGG+EIBBAQoFiZodHRwczovL21vbmtleW1hY2hpbmUuY28udWsvY2EtY3JsLnBl\\n\"\n\t\t\t\t+ \"bTANBgkqhkiG9w0BAQUFAAOCAQEAZ961bEgm2rOq6QajRLeoljwXDnt0S9BGEWL4\\n\"\n\t\t\t\t+ \"PMU2FXDog9aaPwfmZ5fwKaSebwH4HckTp11xwe/D9uBZJQ74Uf80UL9z2eo0GaSR\\n\"\n\t\t\t\t+ \"nRB3QPZfRvop0I4oPvwViKt3puLsi9XSSJ1w9yswnIf89iONT7ZyssPg48Bojo8q\\n\"\n\t\t\t\t+ \"lcKwXuDRBWciODK/xWhvQbaegGJ1BtXcEHtvNjrUJLwSMDSr+U5oUYdMohG0h1iJ\\n\"\n\t\t\t\t+ \"R+JQc49I33o2cTc77wfEWLtVdXAyYY4GSJR6VfgvV40x85ItaNS3HHfT/aXU1x4m\\n\"\n\t\t\t\t+ \"W9YQkWlA6t0blGlC+ghTOY1JbgWnEfXMmVgg9a9cWaYQ+NQwqA==\\n\" + \"-----END CERTIFICATE-----\";\n\t\tByteArrayInputStream in = new ByteArrayInputStream(cert.getBytes());\n\t\tCertificateFactory cf = CertificateFactory.getInstance(\"X.509\");\n\t\treturn (X509Certificate) cf.generateCertificate(in);\n\t}\n\n\t/**\n\t * Builds an X.509 certificate with a subject DN where the CN field is at the end of\n\t * the line. The actual DN line is:\n\t *\n\t * <pre>\n\t *  L=Cupertino,C=US,ST=CA,OU=Java Software,O=Sun Microsystems\\, Inc,CN=Duke\n\t * </pre>\n\t *\n\t */\n\tpublic static X509Certificate buildTestCertificateWithCnAtEnd() throws Exception {\n\t\tString cert = \"-----BEGIN CERTIFICATE-----\\n\"\n\t\t\t\t+ \"MIIDjTCCAnWgAwIBAgIBATALBgkqhkiG9w0BAQswdTENMAsGA1UEAwwERHVrZTEe\\n\"\n\t\t\t\t+ \"MBwGA1UECgwVU3VuIE1pY3Jvc3lzdGVtcywgSW5jMRYwFAYDVQQLDA1KYXZhIFNv\\n\"\n\t\t\t\t+ \"ZnR3YXJlMQswCQYDVQQIDAJDQTELMAkGA1UEBhMCVVMxEjAQBgNVBAcMCUN1cGVy\\n\"\n\t\t\t\t+ \"dGlubzAeFw0xMjA1MTgxNDQ4MzBaFw0xMzA1MTgxNDQ4MzBaMHUxDTALBgNVBAMM\\n\"\n\t\t\t\t+ \"BER1a2UxHjAcBgNVBAoMFVN1biBNaWNyb3N5c3RlbXMsIEluYzEWMBQGA1UECwwN\\n\"\n\t\t\t\t+ \"SmF2YSBTb2Z0d2FyZTELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVTMRIwEAYDVQQH\\n\"\n\t\t\t\t+ \"DAlDdXBlcnRpbm8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGLaCx\\n\"\n\t\t\t\t+ \"Dy5oRJ/FelcoO/lAEApAhR4wxmUIu0guzN0Tx/cuWfyo4349NOxf5XfRcje37B//\\n\"\n\t\t\t\t+ \"hyMwK1Q/pRhRYtZlK+O+9tNCAupekmSxEw9wNsRXNJ18QTTvQRPReXhG8gOiGmU2\\n\"\n\t\t\t\t+ \"kpTVjpZURo/0WGuEyAWYzH99cQfUM92vIaGKq2fApNfwCULtFnAY9WPDZtwSZYhC\\n\"\n\t\t\t\t+ \"qSAoy6B1I2A3i+G5Ep++eCa9PZKCZIPWJiC5+nMmzwCOnQqcZlorsrQ+M+I4GgE2\\n\"\n\t\t\t\t+ \"Rryb/AeKoSPsrm4t0aWhFhKcuHpk3jfKhJhi5e+5bnY17pCoY9hx5EK3WqfKL/x1\\n\"\n\t\t\t\t+ \"3HKsPpf/MieRWiAdAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8E\\n\"\n\t\t\t\t+ \"DDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAdAtXZYCdb7JKzfwY7vEO\\n\"\n\t\t\t\t+ \"9TOMyxxwxhxs+26urL2wQWqtRgHXopoi/GGSuZG5aPQcHWLoqZ1f7nZoWfKzJMKw\\n\"\n\t\t\t\t+ \"MOvaw6wSSkmEoEvdek3s/bH6Gp0spnykqtb+kunGr/XFxyBhHmfdSroEgzspslFh\\n\"\n\t\t\t\t+ \"Glqe/XfrQmFgPWd13GH8mqzSU1zc+0Ka7s68jcuNfz9ble5rT0IrdjRm5E64mVGk\\n\"\n\t\t\t\t+ \"aJTAO5N87ks5JjkDHDJzcyYRcIpqBGotJtyZTjGpIeAG8xLGlkSsUg88iUOchI7s\\n\"\n\t\t\t\t+ \"dOmse9mpgEjCb4kdZ0PnoxMFjsPR8AoGOz4A5vA19nKqWM8bxK9hqLGKsaiQpQg7\\n\" + \"bA==\\n\"\n\t\t\t\t+ \"-----END CERTIFICATE-----\\n\";\n\t\tByteArrayInputStream in = new ByteArrayInputStream(cert.getBytes());\n\t\tCertificateFactory cf = CertificateFactory.getInstance(\"X.509\");\n\t\treturn (X509Certificate) cf.generateCertificate(in);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mockito;\n\nimport org.springframework.context.MessageSource;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n */\n@SuppressWarnings(\"unchecked\")\npublic class AbstractRememberMeServicesTests {\n\n\tstatic User joe = new User(\"joe\", \"password\", true, true, true, true, AuthorityUtils.createAuthorityList(\"ROLE_A\"));\n\n\tMockUserDetailsService uds;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.uds = new MockUserDetailsService(joe, false);\n\t}\n\n\t@Test\n\tpublic void nonBase64CookieShouldBeDetected() {\n\t\tassertThatExceptionOfType(InvalidCookieException.class)\n\t\t\t.isThrownBy(() -> new MockRememberMeServices(this.uds).decodeCookie(\"nonBase64CookieValue%\"));\n\t}\n\n\t@Test\n\tpublic void setAndGetAreConsistent() throws Exception {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tassertThat(services.getCookieName()).isNotNull();\n\t\tassertThat(services.getParameter()).isNotNull();\n\t\tassertThat(services.getKey()).isEqualTo(\"xxxx\");\n\t\tservices.setParameter(\"rm\");\n\t\tassertThat(services.getParameter()).isEqualTo(\"rm\");\n\t\tservices.setCookieName(\"kookie\");\n\t\tassertThat(services.getCookieName()).isEqualTo(\"kookie\");\n\t\tservices.setTokenValiditySeconds(600);\n\t\tassertThat(services.getTokenValiditySeconds()).isEqualTo(600);\n\t\tassertThat(services.getUserDetailsService()).isSameAs(this.uds);\n\t\tAuthenticationDetailsSource ads = Mockito.mock(AuthenticationDetailsSource.class);\n\t\tservices.setAuthenticationDetailsSource(ads);\n\t\tassertThat(services.getAuthenticationDetailsSource()).isSameAs(ads);\n\t\tservices.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void cookieShouldBeCorrectlyEncodedAndDecoded() {\n\t\tString[] cookie = new String[] { \"name:with:colon\", \"cookie\", \"tokens\", \"blah\" };\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tString encoded = services.encodeCookie(cookie);\n\t\t// '=' aren't allowed in version 0 cookies.\n\t\tassertThat(encoded).doesNotEndWith(\"=\");\n\t\tString[] decoded = services.decodeCookie(encoded);\n\t\tassertThat(decoded).containsExactly(\"name:with:colon\", \"cookie\", \"tokens\", \"blah\");\n\t}\n\n\t@Test\n\tpublic void cookieWithOpenIDidentifierAsNameIsEncodedAndDecoded() {\n\t\tString[] cookie = new String[] { \"https://id.openid.zz\", \"cookie\", \"tokens\", \"blah\" };\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tString[] decoded = services.decodeCookie(services.encodeCookie(cookie));\n\t\tassertThat(decoded).hasSize(4);\n\t\tassertThat(decoded[0]).isEqualTo(\"https://id.openid.zz\");\n\t\t// Check https (SEC-1410)\n\t\tcookie[0] = \"https://id.openid.zz\";\n\t\tdecoded = services.decodeCookie(services.encodeCookie(cookie));\n\t\tassertThat(decoded).hasSize(4);\n\t\tassertThat(decoded[0]).isEqualTo(\"https://id.openid.zz\");\n\t}\n\n\t@Test\n\tpublic void autoLoginShouldReturnNullIfNoLoginCookieIsPresented() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(services.autoLogin(request, response)).isNull();\n\t\t// shouldn't try to invalidate our cookie\n\t\tassertThat(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)).isNull();\n\t\trequest = new MockHttpServletRequest();\n\t\tresponse = new MockHttpServletResponse();\n\t\t// set non-login cookie\n\t\trequest.setCookies(new Cookie(\"mycookie\", \"cookie\"));\n\t\tassertThat(services.autoLogin(request, response)).isNull();\n\t\tassertThat(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)).isNull();\n\t}\n\n\t@Test\n\tpublic void successfulAutoLoginReturnsExpectedAuthentication() throws Exception {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tservices.afterPropertiesSet();\n\t\tassertThat(services.getUserDetailsService()).isNotNull();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(createLoginCookie(\"cookie:1:2\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication result = services.autoLogin(request, response);\n\t\tassertThat(result).isNotNull();\n\t}\n\n\t@Test\n\tpublic void autoLoginShouldFailIfCookieIsNotBase64() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trequest.setCookies(new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, \"ZZZ\"));\n\t\tAuthentication result = services.autoLogin(request, response);\n\t\tassertThat(result).isNull();\n\t\tassertCookieCancelled(response);\n\t}\n\n\t@Test\n\tpublic void autoLoginShouldFailIfCookieIsEmpty() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trequest.setCookies(new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, \"\"));\n\t\tAuthentication result = services.autoLogin(request, response);\n\t\tassertThat(result).isNull();\n\t\tassertCookieCancelled(response);\n\t}\n\n\t@Test\n\tpublic void autoLoginShouldFailIfInvalidCookieExceptionIsRaised() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(new MockUserDetailsService(joe, true));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\t// Wrong number of tokens\n\t\trequest.setCookies(createLoginCookie(\"cookie:1\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication result = services.autoLogin(request, response);\n\t\tassertThat(result).isNull();\n\t\tassertCookieCancelled(response);\n\t}\n\n\t@Test\n\tpublic void autoLoginShouldFailIfUserNotFound() {\n\t\tthis.uds.setThrowException(true);\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(createLoginCookie(\"cookie:1:2\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication result = services.autoLogin(request, response);\n\t\tassertThat(result).isNull();\n\t\tassertCookieCancelled(response);\n\t}\n\n\t@Test\n\tpublic void autoLoginShouldFailIfUserAccountIsLocked() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tservices.setUserDetailsChecker(new AccountStatusUserDetailsChecker());\n\t\tthis.uds.toReturn = new User(\"joe\", \"password\", false, true, true, true, joe.getAuthorities());\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(createLoginCookie(\"cookie:1:2\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication result = services.autoLogin(request, response);\n\t\tassertThat(result).isNull();\n\t\tassertCookieCancelled(response);\n\t}\n\n\t@Test\n\tpublic void loginFailShouldCancelCookie() {\n\t\tthis.uds.setThrowException(true);\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"contextpath\");\n\t\trequest.setCookies(createLoginCookie(\"cookie:1:2\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tservices.loginFail(request, response);\n\t\tassertCookieCancelled(response);\n\t}\n\n\t@Test\n\tpublic void logoutShouldCancelCookie() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tservices.setCookieDomain(\"spring.io\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"contextpath\");\n\t\trequest.setCookies(createLoginCookie(\"cookie:1:2\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tservices.logout(request, response, Mockito.mock(Authentication.class));\n\t\t// Try again with null Authentication\n\t\tresponse = new MockHttpServletResponse();\n\t\tservices.logout(request, response, null);\n\t\tassertCookieCancelled(response);\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie.getDomain()).isEqualTo(\"spring.io\");\n\t}\n\n\t@Test\n\tpublic void cancelledCookieShouldUseSecureFlag() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tservices.setCookieDomain(\"spring.io\");\n\t\tservices.setUseSecureCookie(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"contextpath\");\n\t\trequest.setCookies(createLoginCookie(\"cookie:1:2\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tservices.logout(request, response, Mockito.mock(Authentication.class));\n\t\t// Try again with null Authentication\n\t\tresponse = new MockHttpServletResponse();\n\t\tservices.logout(request, response, null);\n\t\tassertCookieCancelled(response);\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie.getDomain()).isEqualTo(\"spring.io\");\n\t\tassertThat(returnedCookie.getSecure()).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void cancelledCookieShouldUseRequestIsSecure() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tservices.setCookieDomain(\"spring.io\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"contextpath\");\n\t\trequest.setCookies(createLoginCookie(\"cookie:1:2\"));\n\t\trequest.setSecure(true);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tservices.logout(request, response, Mockito.mock(Authentication.class));\n\t\t// Try again with null Authentication\n\t\tresponse = new MockHttpServletResponse();\n\t\tservices.logout(request, response, null);\n\t\tassertCookieCancelled(response);\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie.getDomain()).isEqualTo(\"spring.io\");\n\t\tassertThat(returnedCookie.getSecure()).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void cookieTheftExceptionShouldBeRethrown() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds) {\n\n\t\t\t@Override\n\t\t\tprotected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,\n\t\t\t\t\tHttpServletResponse response) {\n\t\t\t\tthrow new CookieTheftException(\"Pretending cookie was stolen\");\n\t\t\t}\n\n\t\t};\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(createLoginCookie(\"cookie:1:2\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThatExceptionOfType(CookieTheftException.class).isThrownBy(() -> services.autoLogin(request, response));\n\t}\n\n\t@Test\n\tpublic void loginSuccessCallsOnLoginSuccessCorrectly() {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication auth = UsernamePasswordAuthenticationToken.unauthenticated(\"joe\", \"password\");\n\t\t// No parameter set\n\t\tservices.loginSuccess(request, response, auth);\n\t\tassertThat(services.loginSuccessCalled).isFalse();\n\t\t// Parameter set to true\n\t\tservices = new MockRememberMeServices(this.uds);\n\t\trequest.setParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, \"true\");\n\t\tservices.loginSuccess(request, response, auth);\n\t\tassertThat(services.loginSuccessCalled).isTrue();\n\t\t// Different parameter name, set to true\n\t\tservices = new MockRememberMeServices(this.uds);\n\t\tservices.setParameter(\"my_parameter\");\n\t\trequest.setParameter(\"my_parameter\", \"true\");\n\t\tservices.loginSuccess(request, response, auth);\n\t\tassertThat(services.loginSuccessCalled).isTrue();\n\t\t// Parameter set to false\n\t\tservices = new MockRememberMeServices(this.uds);\n\t\trequest.setParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, \"false\");\n\t\tservices.loginSuccess(request, response, auth);\n\t\tassertThat(services.loginSuccessCalled).isFalse();\n\t\t// alwaysRemember set to true\n\t\tservices = new MockRememberMeServices(this.uds);\n\t\tservices.setAlwaysRemember(true);\n\t\tservices.loginSuccess(request, response, auth);\n\t\tassertThat(services.loginSuccessCalled).isTrue();\n\t}\n\n\t@Test\n\tpublic void setCookieUsesCorrectNamePathAndValue() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trequest.setContextPath(\"contextpath\");\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds) {\n\t\t\t@Override\n\t\t\tprotected String encodeCookie(String[] cookieTokens) {\n\t\t\t\treturn cookieTokens[0];\n\t\t\t}\n\t\t};\n\t\tservices.setCookieName(\"mycookiename\");\n\t\tservices.setCookie(new String[] { \"mycookie\" }, 1000, request, response);\n\t\tCookie cookie = response.getCookie(\"mycookiename\");\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getValue()).isEqualTo(\"mycookie\");\n\t\tassertThat(cookie.getName()).isEqualTo(\"mycookiename\");\n\t\tassertThat(cookie.getPath()).isEqualTo(\"contextpath\");\n\t\tassertThat(cookie.getSecure()).isFalse();\n\t}\n\n\t@Test\n\tpublic void setCookieSetsSecureFlagIfConfigured() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trequest.setContextPath(\"contextpath\");\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds) {\n\t\t\t@Override\n\t\t\tprotected String encodeCookie(String[] cookieTokens) {\n\t\t\t\treturn cookieTokens[0];\n\t\t\t}\n\t\t};\n\t\tservices.setUseSecureCookie(true);\n\t\tservices.setCookie(new String[] { \"mycookie\" }, 1000, request, response);\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie.getSecure()).isTrue();\n\t}\n\n\t@Test\n\tpublic void setCookieSetsIsHttpOnlyFlagByDefault() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\trequest.setContextPath(\"contextpath\");\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tservices.setCookie(new String[] { \"mycookie\" }, 1000, request, response);\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie.isHttpOnly()).isTrue();\n\t}\n\n\t@Test\n\tpublic void setCookieDomainValue() {\n\t\tMockRememberMeServices services = new MockRememberMeServices();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tservices.setCookieName(\"mycookiename\");\n\t\tservices.setCookieDomain(\"spring.io\");\n\t\tservices.setCookie(new String[] { \"mycookie\" }, 1000, request, response);\n\t\tCookie cookie = response.getCookie(\"mycookiename\");\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getDomain()).isEqualTo(\"spring.io\");\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNullThenThrowsException() {\n\t\tMockRememberMeServices services = new MockRememberMeServices();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> services.setMessageSource(null));\n\t}\n\n\t@Test\n\tpublic void setMessageSourceWhenNotNullThenCanGet() {\n\t\tMessageSource source = mock(MessageSource.class);\n\t\tMockRememberMeServices services = new MockRememberMeServices();\n\t\tservices.setMessageSource(source);\n\t\tString code = \"code\";\n\t\tservices.messages.getMessage(code);\n\t\tverify(source).getMessage(eq(code), any(), any());\n\t}\n\n\t@Test\n\tpublic void setCookieCustomAttribute() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tservices.setCookieCustomizer((cookie) -> cookie.setAttribute(\"attr1\", \"value1\"));\n\t\tservices.setCookie(new String[] { \"mycookie\" }, 1000, request, response);\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie.getAttribute(\"attr1\")).isEqualTo(\"value1\");\n\t}\n\n\tprivate Cookie[] createLoginCookie(String cookieToken) {\n\t\tMockRememberMeServices services = new MockRememberMeServices(this.uds);\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tservices.encodeCookie(StringUtils.delimitedListToStringArray(cookieToken, \":\")));\n\t\treturn new Cookie[] { cookie };\n\t}\n\n\tprivate void assertCookieCancelled(MockHttpServletResponse response) {\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t}\n\n\tstatic class MockRememberMeServices extends AbstractRememberMeServices {\n\n\t\tboolean loginSuccessCalled;\n\n\t\tMockRememberMeServices(String key, UserDetailsService userDetailsService) {\n\t\t\tsuper(key, userDetailsService);\n\t\t}\n\n\t\tMockRememberMeServices(UserDetailsService userDetailsService) {\n\t\t\tsuper(\"xxxx\", userDetailsService);\n\t\t}\n\n\t\tMockRememberMeServices() {\n\t\t\tthis(new MockUserDetailsService(null, false));\n\t\t}\n\n\t\t@Override\n\t\tprotected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthentication successfulAuthentication) {\n\t\t\tthis.loginSuccessCalled = true;\n\t\t}\n\n\t\t@Override\n\t\tprotected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,\n\t\t\t\tHttpServletResponse response) throws RememberMeAuthenticationException {\n\t\t\tif (cookieTokens.length != 3) {\n\t\t\t\tthrow new InvalidCookieException(\"deliberate exception\");\n\t\t\t}\n\t\t\tUserDetails user = getUserDetailsService().loadUserByUsername(\"joe\");\n\t\t\treturn user;\n\t\t}\n\n\t}\n\n\tpublic static class MockUserDetailsService implements UserDetailsService {\n\n\t\tprivate UserDetails toReturn;\n\n\t\tprivate boolean throwException;\n\n\t\tpublic MockUserDetailsService() {\n\t\t\tthis(null, false);\n\t\t}\n\n\t\tpublic MockUserDetailsService(UserDetails toReturn, boolean throwException) {\n\t\t\tthis.toReturn = toReturn;\n\t\t\tthis.throwException = throwException;\n\t\t}\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) {\n\t\t\tif (this.throwException) {\n\t\t\t\tthrow new UsernameNotFoundException(\"as requested by mock\");\n\t\t\t}\n\t\t\treturn this.toReturn;\n\t\t}\n\n\t\tpublic void setThrowException(boolean value) {\n\t\t\tthis.throwException = value;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/rememberme/JdbcTokenRepositoryImplTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.sql.Timestamp;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.logging.Log;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.dao.EmptyResultDataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.SingleConnectionDataSource;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Luke Taylor\n */\n@ExtendWith(MockitoExtension.class)\npublic class JdbcTokenRepositoryImplTests {\n\n\t@Mock\n\tprivate Log logger;\n\n\tprivate static SingleConnectionDataSource dataSource;\n\n\tprivate JdbcTokenRepositoryImpl repo;\n\n\tprivate JdbcTemplate template;\n\n\t@BeforeAll\n\tpublic static void createDataSource() {\n\t\tdataSource = new SingleConnectionDataSource(\"jdbc:hsqldb:mem:tokenrepotest\", \"sa\", \"\", true);\n\t\tdataSource.setDriverClassName(\"org.hsqldb.jdbc.JDBCDriver\");\n\t}\n\n\t@AfterAll\n\tpublic static void clearDataSource() {\n\t\tdataSource.destroy();\n\t\tdataSource = null;\n\t}\n\n\t@BeforeEach\n\tpublic void populateDatabase() {\n\t\tthis.repo = new JdbcTokenRepositoryImpl();\n\t\tReflectionTestUtils.setField(this.repo, \"logger\", this.logger);\n\t\tthis.repo.setDataSource(dataSource);\n\t\tthis.repo.initDao();\n\t\tthis.template = this.repo.getJdbcTemplate();\n\t\tthis.template.execute(\"create table persistent_logins (username varchar(100) not null, \"\n\t\t\t\t+ \"series varchar(100) not null, token varchar(500) not null, last_used timestamp not null)\");\n\t}\n\n\t@AfterEach\n\tpublic void clearData() {\n\t\tthis.template.execute(\"drop table persistent_logins\");\n\t}\n\n\t@Test\n\tpublic void createNewTokenInsertsCorrectData() {\n\t\tTimestamp currentDate = new Timestamp(Calendar.getInstance().getTimeInMillis());\n\t\tPersistentRememberMeToken token = new PersistentRememberMeToken(\"joeuser\", \"joesseries\", \"atoken\", currentDate);\n\t\tthis.repo.createNewToken(token);\n\t\tMap<String, Object> results = this.template.queryForMap(\"select * from persistent_logins\");\n\t\tassertThat(results).containsEntry(\"last_used\", currentDate);\n\t\tassertThat(results).containsEntry(\"username\", \"joeuser\");\n\t\tassertThat(results).containsEntry(\"series\", \"joesseries\");\n\t\tassertThat(results).containsEntry(\"token\", \"atoken\");\n\t}\n\n\t@Test\n\tpublic void retrievingTokenReturnsCorrectData() {\n\t\tthis.template.execute(\"insert into persistent_logins (series, username, token, last_used) values \"\n\t\t\t\t+ \"('joesseries', 'joeuser', 'atoken', '2007-10-09 18:19:25.000000000')\");\n\t\tPersistentRememberMeToken token = this.repo.getTokenForSeries(\"joesseries\");\n\t\tassertThat(token.getUsername()).isEqualTo(\"joeuser\");\n\t\tassertThat(token.getSeries()).isEqualTo(\"joesseries\");\n\t\tassertThat(token.getTokenValue()).isEqualTo(\"atoken\");\n\t\tassertThat(token.getDate()).isEqualTo(Timestamp.valueOf(\"2007-10-09 18:19:25.000000000\"));\n\t}\n\n\t@Test\n\tpublic void retrievingTokenWithDuplicateSeriesReturnsNull() {\n\t\tthis.template.execute(\"insert into persistent_logins (series, username, token, last_used) values \"\n\t\t\t\t+ \"('joesseries', 'joeuser', 'atoken2', '2007-10-19 18:19:25.000000000')\");\n\t\tthis.template.execute(\"insert into persistent_logins (series, username, token, last_used) values \"\n\t\t\t\t+ \"('joesseries', 'joeuser', 'atoken', '2007-10-09 18:19:25.000000000')\");\n\t\t// List results =\n\t\t// template.queryForList(\"select * from persistent_logins where series =\n\t\t// 'joesseries'\");\n\t\tassertThat(this.repo.getTokenForSeries(\"joesseries\")).isNull();\n\t}\n\n\t// SEC-1964\n\t@Test\n\tpublic void retrievingTokenWithNoSeriesReturnsNull() {\n\t\tassertThat(this.repo.getTokenForSeries(\"missingSeries\")).isNull();\n\t\tArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);\n\t\tverify(this.logger).debug(captor.capture(), any(EmptyResultDataAccessException.class));\n\t\tverifyNoMoreInteractions(this.logger);\n\t\tassertThat(captor.getValue()).hasToString(\"Querying token for series 'missingSeries' returned no results.\");\n\t}\n\n\t@Test\n\tpublic void removingUserTokensDeletesData() {\n\t\tthis.template.execute(\"insert into persistent_logins (series, username, token, last_used) values \"\n\t\t\t\t+ \"('joesseries2', 'joeuser', 'atoken2', '2007-10-19 18:19:25.000000000')\");\n\t\tthis.template.execute(\"insert into persistent_logins (series, username, token, last_used) values \"\n\t\t\t\t+ \"('joesseries', 'joeuser', 'atoken', '2007-10-09 18:19:25.000000000')\");\n\t\t// List results =\n\t\t// template.queryForList(\"select * from persistent_logins where series =\n\t\t// 'joesseries'\");\n\t\tthis.repo.removeUserTokens(\"joeuser\");\n\t\tList<Map<String, Object>> results = this.template\n\t\t\t.queryForList(\"select * from persistent_logins where username = 'joeuser'\");\n\t\tassertThat(results).isEmpty();\n\t}\n\n\t@Test\n\tpublic void updatingTokenModifiesTokenValueAndLastUsed() {\n\t\tTimestamp ts = new Timestamp(System.currentTimeMillis() - 1);\n\t\tthis.template.execute(\"insert into persistent_logins (series, username, token, last_used) values \"\n\t\t\t\t+ \"('joesseries', 'joeuser', 'atoken', '\" + ts.toString() + \"')\");\n\t\tthis.repo.updateToken(\"joesseries\", \"newtoken\", new Date());\n\t\tMap<String, Object> results = this.template\n\t\t\t.queryForMap(\"select * from persistent_logins where series = 'joesseries'\");\n\t\tassertThat(results).containsEntry(\"username\", \"joeuser\");\n\t\tassertThat(results).containsEntry(\"series\", \"joesseries\");\n\t\tassertThat(results).containsEntry(\"token\", \"newtoken\");\n\t\tDate lastUsed = (Date) results.get(\"last_used\");\n\t\tassertThat(lastUsed.getTime() > ts.getTime()).isTrue();\n\t}\n\n\t@Test\n\tpublic void createTableOnStartupCreatesCorrectTable() {\n\t\tthis.template.execute(\"drop table persistent_logins\");\n\t\tthis.repo = new JdbcTokenRepositoryImpl();\n\t\tthis.repo.setDataSource(dataSource);\n\t\tthis.repo.setCreateTableOnStartup(true);\n\t\tthis.repo.initDao();\n\t\tthis.template.queryForList(\"select username,series,token,last_used from persistent_logins\");\n\t}\n\n\t// SEC-2879\n\t@Test\n\tpublic void updateUsesLastUsed() {\n\t\tJdbcTemplate template = mock(JdbcTemplate.class);\n\t\tDate lastUsed = new Date(1424841314059L);\n\t\tJdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();\n\t\trepository.setJdbcTemplate(template);\n\t\trepository.updateToken(\"series\", \"token\", lastUsed);\n\t\tverify(template).update(anyString(), anyString(), eq(lastUsed), anyString());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/rememberme/NullRememberMeServicesTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.web.authentication.NullRememberMeServices;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests {@link org.springframework.security.web.authentication.NullRememberMeServices}.\n *\n * @author Ben Alex\n */\npublic class NullRememberMeServicesTests {\n\n\t@Test\n\tpublic void testAlwaysReturnsNull() {\n\t\tNullRememberMeServices services = new NullRememberMeServices();\n\t\tassertThat(services.autoLogin(null, null)).isNull();\n\t\tservices.loginFail(null, null);\n\t\tservices.loginSuccess(null, null, null);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServicesTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.util.Date;\nimport java.util.concurrent.TimeUnit;\n\nimport jakarta.servlet.http.Cookie;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Luke Taylor\n */\npublic class PersistentTokenBasedRememberMeServicesTests {\n\n\tprivate PersistentTokenBasedRememberMeServices services;\n\n\tprivate MockTokenRepository repo;\n\n\t@BeforeEach\n\tpublic void setUpData() throws Exception {\n\t\tthis.services = new PersistentTokenBasedRememberMeServices(\"key\",\n\t\t\t\tnew AbstractRememberMeServicesTests.MockUserDetailsService(AbstractRememberMeServicesTests.joe, false),\n\t\t\t\tnew InMemoryTokenRepositoryImpl());\n\t\tthis.services.setCookieName(\"mycookiename\");\n\t\t// Default to 100 days (see SEC-1081).\n\t\tthis.services.setTokenValiditySeconds(100 * 24 * 60 * 60);\n\t\tthis.services.afterPropertiesSet();\n\t}\n\n\t@Test\n\tpublic void loginIsRejectedWithWrongNumberOfCookieTokens() {\n\t\tassertThatExceptionOfType(InvalidCookieException.class)\n\t\t\t.isThrownBy(() -> this.services.processAutoLoginCookie(new String[] { \"series\", \"token\", \"extra\" },\n\t\t\t\t\tnew MockHttpServletRequest(), new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void loginIsRejectedWhenNoTokenMatchingSeriesIsFound() {\n\t\tthis.services = create(null);\n\t\tassertThatExceptionOfType(RememberMeAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.services.processAutoLoginCookie(new String[] { \"series\", \"token\" },\n\t\t\t\t\tnew MockHttpServletRequest(), new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void loginIsRejectedWhenTokenIsExpired() {\n\t\tthis.services = create(new PersistentRememberMeToken(\"joe\", \"series\", \"token\",\n\t\t\t\tnew Date(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(1) - 100)));\n\t\tthis.services.setTokenValiditySeconds(1);\n\t\tassertThatExceptionOfType(RememberMeAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.services.processAutoLoginCookie(new String[] { \"series\", \"token\" },\n\t\t\t\t\tnew MockHttpServletRequest(), new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void cookieTheftIsDetectedWhenSeriesAndTokenDontMatch() {\n\t\tthis.services = create(new PersistentRememberMeToken(\"joe\", \"series\", \"wrongtoken\", new Date()));\n\t\tassertThatExceptionOfType(CookieTheftException.class)\n\t\t\t.isThrownBy(() -> this.services.processAutoLoginCookie(new String[] { \"series\", \"token\" },\n\t\t\t\t\tnew MockHttpServletRequest(), new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void successfulAutoLoginCreatesNewTokenAndCookieWithSameSeries() {\n\t\tthis.services = create(new PersistentRememberMeToken(\"joe\", \"series\", \"token\", new Date()));\n\t\t// 12 => b64 length will be 16\n\t\tthis.services.setTokenLength(12);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.processAutoLoginCookie(new String[] { \"series\", \"token\" }, new MockHttpServletRequest(),\n\t\t\t\tresponse);\n\t\tassertThat(this.repo.getStoredToken().getSeries()).isEqualTo(\"series\");\n\t\tassertThat(this.repo.getStoredToken().getTokenValue()).hasSize(16);\n\t\tString[] cookie = this.services.decodeCookie(response.getCookie(\"mycookiename\").getValue());\n\t\tassertThat(cookie[0]).isEqualTo(\"series\");\n\t\tassertThat(cookie[1]).isEqualTo(this.repo.getStoredToken().getTokenValue());\n\t}\n\n\t@Test\n\tpublic void loginSuccessCreatesNewTokenAndCookieWithNewSeries() {\n\t\tthis.services = create(null);\n\t\tthis.services.setAlwaysRemember(true);\n\t\tthis.services.setTokenLength(12);\n\t\tthis.services.setSeriesLength(12);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.loginSuccess(new MockHttpServletRequest(), response,\n\t\t\t\tUsernamePasswordAuthenticationToken.unauthenticated(\"joe\", \"password\"));\n\t\tassertThat(this.repo.getStoredToken().getSeries()).hasSize(16);\n\t\tassertThat(this.repo.getStoredToken().getTokenValue()).hasSize(16);\n\t\tString[] cookie = this.services.decodeCookie(response.getCookie(\"mycookiename\").getValue());\n\t\tassertThat(cookie[0]).isEqualTo(this.repo.getStoredToken().getSeries());\n\t\tassertThat(cookie[1]).isEqualTo(this.repo.getStoredToken().getTokenValue());\n\t}\n\n\t@Test\n\tpublic void logoutClearsUsersTokenAndCookie() {\n\t\tCookie cookie = new Cookie(\"mycookiename\", \"somevalue\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services = create(new PersistentRememberMeToken(\"joe\", \"series\", \"token\", new Date()));\n\t\tthis.services.logout(request, response, new TestingAuthenticationToken(\"joe\", \"somepass\", \"SOME_AUTH\"));\n\t\tCookie returnedCookie = response.getCookie(\"mycookiename\");\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t\t// SEC-1280\n\t\tthis.services.logout(request, response, null);\n\t}\n\n\tprivate PersistentTokenBasedRememberMeServices create(PersistentRememberMeToken token) {\n\t\tthis.repo = new MockTokenRepository(token);\n\t\tPersistentTokenBasedRememberMeServices services = new PersistentTokenBasedRememberMeServices(\"key\",\n\t\t\t\tnew AbstractRememberMeServicesTests.MockUserDetailsService(AbstractRememberMeServicesTests.joe, false),\n\t\t\t\tthis.repo);\n\t\tservices.setCookieName(\"mycookiename\");\n\t\treturn services;\n\t}\n\n\tprivate final class MockTokenRepository implements PersistentTokenRepository {\n\n\t\tprivate PersistentRememberMeToken storedToken;\n\n\t\tprivate MockTokenRepository(PersistentRememberMeToken token) {\n\t\t\tthis.storedToken = token;\n\t\t}\n\n\t\t@Override\n\t\tpublic void createNewToken(PersistentRememberMeToken token) {\n\t\t\tthis.storedToken = token;\n\t\t}\n\n\t\t@Override\n\t\tpublic void updateToken(String series, String tokenValue, Date lastUsed) {\n\t\t\tthis.storedToken = new PersistentRememberMeToken(this.storedToken.getUsername(),\n\t\t\t\t\tthis.storedToken.getSeries(), tokenValue, lastUsed);\n\t\t}\n\n\t\t@Override\n\t\tpublic PersistentRememberMeToken getTokenForSeries(String seriesId) {\n\t\t\treturn this.storedToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeUserTokens(String username) {\n\t\t}\n\n\t\tPersistentRememberMeToken getStoredToken() {\n\t\t\treturn this.storedToken;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/rememberme/RememberMeAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.authentication.NullRememberMeServices;\nimport org.springframework.security.web.authentication.RememberMeServices;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link RememberMeAuthenticationFilter}.\n *\n * @author Ben Alex\n */\npublic class RememberMeAuthenticationFilterTests {\n\n\tAuthentication remembered = new TestingAuthenticationToken(\"remembered\", \"password\", \"ROLE_REMEMBERED\");\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testDetectsAuthenticationManagerProperty() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new RememberMeAuthenticationFilter(null, new NullRememberMeServices()));\n\t}\n\n\t@Test\n\tpublic void testDetectsRememberMeServicesProperty() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new RememberMeAuthenticationFilter(mock(AuthenticationManager.class), null));\n\t}\n\n\t@Test\n\tpublic void testOperationWhenAuthenticationExistsInContextHolder() throws Exception {\n\t\t// Put an Authentication object into the SecurityContextHolder\n\t\tAuthentication originalAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_A\");\n\t\tSecurityContextHolder.getContext().setAuthentication(originalAuth);\n\t\t// Setup our filter correctly\n\t\tRememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter(mock(AuthenticationManager.class),\n\t\t\t\tnew MockRememberMeServices(this.remembered));\n\t\tfilter.afterPropertiesSet();\n\t\t// Test\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\trequest.setRequestURI(\"x\");\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), fc);\n\t\t// Ensure filter didn't change our original object\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(originalAuth);\n\t\tverify(fc).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void testOperationWhenNoAuthenticationInContextHolder() throws Exception {\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(this.remembered)).willReturn(this.remembered);\n\t\tRememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter(am,\n\t\t\t\tnew MockRememberMeServices(this.remembered));\n\t\tfilter.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\trequest.setRequestURI(\"x\");\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), fc);\n\t\t// Ensure filter setup with our remembered authentication object\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.remembered);\n\t\tverify(fc).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void onUnsuccessfulLoginIsCalledWhenProviderRejectsAuth() throws Exception {\n\t\tfinal Authentication failedAuth = new TestingAuthenticationToken(\"failed\", \"\");\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(any(Authentication.class))).willThrow(new BadCredentialsException(\"\"));\n\t\tRememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter(am,\n\t\t\t\tnew MockRememberMeServices(this.remembered)) {\n\t\t\t@Override\n\t\t\tprotected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\t\tAuthenticationException failed) {\n\t\t\t\tsuper.onUnsuccessfulAuthentication(request, response, failed);\n\t\t\t\tSecurityContextHolder.getContext().setAuthentication(failedAuth);\n\t\t\t}\n\t\t};\n\t\tfilter.setApplicationEventPublisher(mock(ApplicationEventPublisher.class));\n\t\tfilter.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\trequest.setRequestURI(\"x\");\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), fc);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(failedAuth);\n\t\tverify(fc).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticationSuccessHandlerIsInvokedOnSuccessfulAuthenticationIfSet() throws Exception {\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(this.remembered)).willReturn(this.remembered);\n\t\tRememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter(am,\n\t\t\t\tnew MockRememberMeServices(this.remembered));\n\t\tfilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(\"/target\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\trequest.setRequestURI(\"x\");\n\t\tfilter.doFilter(request, response, fc);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/target\");\n\t\t// Should return after success handler is invoked, so chain should not proceed\n\t\tverifyNoMoreInteractions(fc);\n\t}\n\n\t@Test\n\tpublic void securityContextRepositoryInvokedIfSet() throws Exception {\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(this.remembered)).willReturn(this.remembered);\n\t\tRememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter(am,\n\t\t\t\tnew MockRememberMeServices(this.remembered));\n\t\tfilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(\"/target\"));\n\t\tfilter.setSecurityContextRepository(securityContextRepository);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\trequest.setRequestURI(\"x\");\n\t\tfilter.doFilter(request, response, fc);\n\t\tverify(securityContextRepository).saveContext(any(), eq(request), eq(response));\n\t}\n\n\t@Test\n\tpublic void sessionAuthenticationStrategyInvokedIfSet() throws Exception {\n\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class);\n\t\tAuthenticationManager am = mock(AuthenticationManager.class);\n\t\tgiven(am.authenticate(this.remembered)).willReturn(this.remembered);\n\t\tRememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter(am,\n\t\t\t\tnew MockRememberMeServices(this.remembered));\n\t\tfilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(\"/target\"));\n\t\tfilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\trequest.setRequestURI(\"x\");\n\t\tfilter.doFilter(request, response, fc);\n\t\tverify(sessionAuthenticationStrategy).onAuthentication(any(), eq(request), eq(response));\n\t}\n\n\tprivate class MockRememberMeServices implements RememberMeServices {\n\n\t\tprivate Authentication authToReturn;\n\n\t\tMockRememberMeServices(Authentication authToReturn) {\n\t\t\tthis.authToReturn = authToReturn;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {\n\t\t\treturn this.authToReturn;\n\t\t}\n\n\t\t@Override\n\t\tpublic void loginFail(HttpServletRequest request, HttpServletResponse response) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void loginSuccess(HttpServletRequest request, HttpServletResponse response,\n\t\t\t\tAuthentication successfulAuthentication) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServicesTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.rememberme;\n\nimport java.util.Date;\n\nimport jakarta.servlet.http.Cookie;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.test.web.CodecTestUtils;\nimport org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.RememberMeTokenAlgorithm;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests\n * {@link org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices}\n * .\n *\n * @author Ben Alex\n * @author Marcus Da Coregio\n */\npublic class TokenBasedRememberMeServicesTests {\n\n\tprivate UserDetailsService uds;\n\n\tprivate UserDetails user = new User(\"someone\", \"password\", true, true, true, true,\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ABC\"));\n\n\tprivate TokenBasedRememberMeServices services;\n\n\t@BeforeEach\n\tpublic void createTokenBasedRememberMeServices() {\n\t\tthis.uds = mock(UserDetailsService.class);\n\t\tthis.services = new TokenBasedRememberMeServices(\"key\", this.uds);\n\t}\n\n\tvoid udsWillReturnUser() {\n\t\tgiven(this.uds.loadUserByUsername(any(String.class))).willReturn(this.user);\n\t}\n\n\tvoid udsWillThrowNotFound() {\n\t\tgiven(this.uds.loadUserByUsername(any(String.class))).willThrow(new UsernameNotFoundException(\"\"));\n\t}\n\n\tvoid udsWillReturnNull() {\n\t\tgiven(this.uds.loadUserByUsername(any(String.class))).willReturn(null);\n\t}\n\n\tprivate long determineExpiryTimeFromBased64EncodedToken(String validToken) {\n\t\tString cookieAsPlainText = CodecTestUtils.decodeBase64(validToken);\n\t\tString[] cookieTokens = getCookieTokens(cookieAsPlainText);\n\t\tif (isValidCookieTokensLength(cookieTokens)) {\n\t\t\ttry {\n\t\t\t\treturn Long.parseLong(cookieTokens[1]);\n\t\t\t}\n\t\t\tcatch (NumberFormatException ignored) {\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tprivate String[] getCookieTokens(String cookieAsPlainText) {\n\t\treturn StringUtils.delimitedListToStringArray(cookieAsPlainText, \":\");\n\t}\n\n\tprivate String determineAlgorithmNameFromBase64EncodedToken(String validToken) {\n\t\tString cookieAsPlainText = CodecTestUtils.decodeBase64(validToken);\n\t\tString[] cookieTokens = getCookieTokens(cookieAsPlainText);\n\t\tif (isValidCookieTokensLength(cookieTokens)) {\n\t\t\treturn cookieTokens[2];\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate boolean isValidCookieTokensLength(String[] cookieTokens) {\n\t\treturn cookieTokens.length == 3 || cookieTokens.length == 4;\n\t}\n\n\tprivate String generateCorrectCookieContentForTokenNoAlgorithmName(long expiryTime, String username,\n\t\t\tString password, String key) {\n\t\treturn generateCorrectCookieContentForTokenWithAlgorithmName(expiryTime, username, password, key,\n\t\t\t\tRememberMeTokenAlgorithm.MD5);\n\t}\n\n\tprivate String generateCorrectCookieContentForTokenNoAlgorithmName(long expiryTime, String username,\n\t\t\tString password, String key, RememberMeTokenAlgorithm algorithm) {\n\t\t// format is:\n\t\t// username + \":\" + expiryTime + \":\" + Md5Hex(username + \":\" + expiryTime + \":\" +\n\t\t// password + \":\" + key)\n\t\tString signatureValue = CodecTestUtils.algorithmHex(algorithm.getDigestAlgorithm(),\n\t\t\t\tusername + \":\" + expiryTime + \":\" + password + \":\" + key);\n\t\tString tokenValue = username + \":\" + expiryTime + \":\" + signatureValue;\n\t\treturn CodecTestUtils.encodeBase64(tokenValue);\n\t}\n\n\tprivate String generateCorrectCookieContentForTokenWithAlgorithmName(long expiryTime, String username,\n\t\t\tString password, String key, RememberMeTokenAlgorithm algorithm) {\n\t\t// format is:\n\t\t// username + \":\" + expiryTime + \":\" + algorithmName + \":\" + algorithmHex(username\n\t\t// + \":\" + expiryTime + \":\" +\n\t\t// password + \":\" + key)\n\t\tString signatureValue = CodecTestUtils.algorithmHex(algorithm.getDigestAlgorithm(),\n\t\t\t\tusername + \":\" + expiryTime + \":\" + password + \":\" + key);\n\t\tString tokenValue = username + \":\" + expiryTime + \":\" + algorithm.name() + \":\" + signatureValue;\n\t\treturn CodecTestUtils.encodeBase64(tokenValue);\n\t}\n\n\t@Test\n\tpublic void autoLoginReturnsNullIfNoCookiePresented() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication result = this.services.autoLogin(new MockHttpServletRequest(), response);\n\t\tassertThat(result).isNull();\n\t\t// No cookie set\n\t\tassertThat(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)).isNull();\n\t}\n\n\t@Test\n\tpublic void autoLoginIgnoresUnrelatedCookie() {\n\t\tCookie cookie = new Cookie(\"unrelated_cookie\", \"foobar\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication result = this.services.autoLogin(request, response);\n\t\tassertThat(result).isNull();\n\t\tassertThat(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)).isNull();\n\t}\n\n\t@Test\n\tpublic void autoLoginReturnsNullForExpiredCookieAndClearsCookie() {\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() - 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"key\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(this.services.autoLogin(request, response)).isNull();\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void autoLoginReturnsNullAndClearsCookieIfMissingThreeTokensInCookieValue() {\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tCodecTestUtils.encodeBase64(\"x\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(this.services.autoLogin(request, response)).isNull();\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void autoLoginClearsNonBase64EncodedCookie() {\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\t\"NOT_BASE_64_ENCODED\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(this.services.autoLogin(request, response)).isNull();\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void autoLoginClearsCookieIfSignatureBlocksDoesNotMatchExpectedValue() {\n\t\tudsWillReturnUser();\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"WRONG_KEY\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(this.services.autoLogin(request, response)).isNull();\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void autoLoginClearsCookieIfTokenDoesNotContainANumberInCookieValue() {\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tCodecTestUtils.encodeBase64(\"username:NOT_A_NUMBER:signature\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(this.services.autoLogin(request, response)).isNull();\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void autoLoginClearsCookieIfUserNotFound() {\n\t\tudsWillThrowNotFound();\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"key\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThat(this.services.autoLogin(request, response)).isNull();\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void autoLoginClearsCookieIfUserServiceMisconfigured() {\n\t\tudsWillReturnNull();\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"key\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.services.autoLogin(request, response));\n\t}\n\n\t@Test\n\tpublic void autoLoginWithValidTokenAndUserSucceeds() {\n\t\tudsWillReturnUser();\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"key\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthentication result = this.services.autoLogin(request, response);\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.getPrincipal()).isEqualTo(this.user);\n\t}\n\n\t@Test\n\tpublic void autoLoginWhenTokenNoAlgorithmAndDifferentMatchingAlgorithmThenReturnsNullAndClearCookie() {\n\t\tudsWillReturnUser();\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"key\", RememberMeTokenAlgorithm.MD5));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);\n\t\tAuthentication result = this.services.autoLogin(request, response);\n\t\tCookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(result).isNull();\n\t\tassertThat(returnedCookie).isNotNull();\n\t\tassertThat(returnedCookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void autoLoginWhenTokenNoAlgorithmAndSameMatchingAlgorithmThenSucceeds() {\n\t\tudsWillReturnUser();\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenNoAlgorithmName(System.currentTimeMillis() + 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"key\", RememberMeTokenAlgorithm.SHA256));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);\n\t\tAuthentication result = this.services.autoLogin(request, response);\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.getPrincipal()).isEqualTo(this.user);\n\t}\n\n\t@Test\n\tpublic void autoLoginWhenTokenHasAlgorithmAndSameMatchingAlgorithmThenUsesTokenAlgorithmAndSucceeds() {\n\t\tudsWillReturnUser();\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenWithAlgorithmName(System.currentTimeMillis() + 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"key\", RememberMeTokenAlgorithm.SHA256));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);\n\t\tAuthentication result = this.services.autoLogin(request, response);\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.getPrincipal()).isEqualTo(this.user);\n\t}\n\n\t@Test\n\tpublic void autoLoginWhenTokenHasAlgorithmAndDifferentMatchingAlgorithmThenUsesTokenAlgorithmAndSucceeds() {\n\t\tudsWillReturnUser();\n\t\tCookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,\n\t\t\t\tgenerateCorrectCookieContentForTokenWithAlgorithmName(System.currentTimeMillis() + 1000000, \"someone\",\n\t\t\t\t\t\t\"password\", \"key\", RememberMeTokenAlgorithm.SHA256));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(cookie);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);\n\t\tAuthentication result = this.services.autoLogin(request, response);\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.getPrincipal()).isEqualTo(this.user);\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tassertThat(this.services.getUserDetailsService()).isEqualTo(this.uds);\n\t\tassertThat(this.services.getKey()).isEqualTo(\"key\");\n\t\tassertThat(this.services.getParameter()).isEqualTo(AbstractRememberMeServices.DEFAULT_PARAMETER);\n\t\tthis.services.setParameter(\"some_param\");\n\t\tassertThat(this.services.getParameter()).isEqualTo(\"some_param\");\n\t\tthis.services.setTokenValiditySeconds(12);\n\t\tassertThat(this.services.getTokenValiditySeconds()).isEqualTo(12);\n\t}\n\n\t@Test\n\tpublic void loginFailClearsCookie() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.loginFail(request, response);\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void loginSuccessIgnoredIfParameterNotSetOrFalse() {\n\t\tTokenBasedRememberMeServices services = new TokenBasedRememberMeServices(\"key\",\n\t\t\t\tnew AbstractRememberMeServicesTests.MockUserDetailsService(null, false));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, \"false\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tservices.loginSuccess(request, response, new TestingAuthenticationToken(\"someone\", \"password\", \"ROLE_ABC\"));\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie).isNull();\n\t}\n\n\t@Test\n\tpublic void loginSuccessNormalWithNonUserDetailsBasedPrincipalSetsExpectedCookie() {\n\t\t// SEC-822\n\t\tthis.services.setTokenValiditySeconds(500000000);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, \"true\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.loginSuccess(request, response,\n\t\t\t\tnew TestingAuthenticationToken(\"someone\", \"password\", \"ROLE_ABC\"));\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tString expiryTime = this.services.decodeCookie(cookie.getValue())[1];\n\t\tlong expectedExpiryTime = 1000L * 500000000;\n\t\texpectedExpiryTime += System.currentTimeMillis();\n\t\tassertThat(Long.parseLong(expiryTime) > expectedExpiryTime - 10000).isTrue();\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds());\n\t\tassertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();\n\t\tassertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();\n\t}\n\n\t@Test\n\tpublic void loginSuccessNormalWithUserDetailsBasedPrincipalSetsExpectedCookie() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, \"true\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.loginSuccess(request, response,\n\t\t\t\tnew TestingAuthenticationToken(\"someone\", \"password\", \"ROLE_ABC\"));\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds());\n\t\tassertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();\n\t\tassertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();\n\t}\n\n\t@Test\n\tpublic void loginSuccessWhenDefaultEncodingAlgorithmThenContainsAlgorithmName() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, \"true\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.loginSuccess(request, response,\n\t\t\t\tnew TestingAuthenticationToken(\"someone\", \"password\", \"ROLE_ABC\"));\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds());\n\t\tassertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();\n\t\tassertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();\n\t\tassertThat(\"SHA256\").isEqualTo(determineAlgorithmNameFromBase64EncodedToken(cookie.getValue()));\n\t}\n\n\t@Test\n\tpublic void loginSuccessWhenCustomEncodingAlgorithmThenContainsAlgorithmName() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, \"true\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services = new TokenBasedRememberMeServices(\"key\", this.uds, RememberMeTokenAlgorithm.SHA256);\n\t\tthis.services.loginSuccess(request, response,\n\t\t\t\tnew TestingAuthenticationToken(\"someone\", \"password\", \"ROLE_ABC\"));\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getMaxAge()).isEqualTo(this.services.getTokenValiditySeconds());\n\t\tassertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();\n\t\tassertThat(new Date().before(new Date(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())))).isTrue();\n\t\tassertThat(\"SHA256\").isEqualTo(determineAlgorithmNameFromBase64EncodedToken(cookie.getValue()));\n\t}\n\n\t// SEC-933\n\t@Test\n\tpublic void obtainPasswordReturnsNullForTokenWithNullCredentials() {\n\t\tTestingAuthenticationToken token = new TestingAuthenticationToken(\"username\", null);\n\t\tassertThat(this.services.retrievePassword(token)).isNull();\n\t}\n\n\t// SEC-949\n\t@Test\n\tpublic void negativeValidityPeriodIsSetOnCookieButExpiryTimeRemainsAtTwoWeeks() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, \"true\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.services.setTokenValiditySeconds(-1);\n\t\tthis.services.loginSuccess(request, response,\n\t\t\t\tnew TestingAuthenticationToken(\"someone\", \"password\", \"ROLE_ABC\"));\n\t\tCookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);\n\t\tassertThat(cookie).isNotNull();\n\t\t// Check the expiry time is within 50ms of two weeks from current time\n\t\tassertThat(determineExpiryTimeFromBased64EncodedToken(cookie.getValue())\n\t\t\t\t- System.currentTimeMillis() > AbstractRememberMeServices.TWO_WEEKS_S - 50)\n\t\t\t.isTrue();\n\t\tassertThat(cookie.getMaxAge()).isEqualTo(-1);\n\t\tassertThat(CodecTestUtils.isBase64(cookie.getValue().getBytes())).isTrue();\n\t}\n\n\t@Test\n\tpublic void constructorWhenEncodingAlgorithmNullThenException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new TokenBasedRememberMeServices(\"key\", this.uds, null))\n\t\t\t.withMessage(\"encodingAlgorithm cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenNoEncodingAlgorithmSpecifiedThenSha256() {\n\t\tTokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices(\"key\", this.uds);\n\t\tRememberMeTokenAlgorithm encodingAlgorithm = (RememberMeTokenAlgorithm) ReflectionTestUtils\n\t\t\t.getField(rememberMeServices, \"encodingAlgorithm\");\n\t\tassertThat(encodingAlgorithm).isSameAs(RememberMeTokenAlgorithm.SHA256);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/session/ChangeSessionIdAuthenticationStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\npublic class ChangeSessionIdAuthenticationStrategyTests {\n\n\t@Test\n\tpublic void applySessionFixation() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tString id = request.getSession().getId();\n\t\tnew ChangeSessionIdAuthenticationStrategy().applySessionFixation(request);\n\t\tassertThat(request.getSession().getId()).isNotEqualTo(id);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/session/CompositeSessionAuthenticationStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class CompositeSessionAuthenticationStrategyTests {\n\n\t@Mock\n\tprivate SessionAuthenticationStrategy strategy1;\n\n\t@Mock\n\tprivate SessionAuthenticationStrategy strategy2;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\t@Test\n\tpublic void constructorNullDelegates() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CompositeSessionAuthenticationStrategy(null));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyDelegates() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CompositeSessionAuthenticationStrategy(\n\t\t\t\tCollections.<SessionAuthenticationStrategy>emptyList()));\n\t}\n\n\t@Test\n\tpublic void constructorDelegatesContainNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CompositeSessionAuthenticationStrategy(\n\t\t\t\tCollections.<SessionAuthenticationStrategy>singletonList(null)));\n\t}\n\n\t@Test\n\tpublic void delegatesToAll() {\n\t\tCompositeSessionAuthenticationStrategy strategy = new CompositeSessionAuthenticationStrategy(\n\t\t\t\tArrays.asList(this.strategy1, this.strategy2));\n\t\tstrategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tverify(this.strategy1).onAuthentication(this.authentication, this.request, this.response);\n\t\tverify(this.strategy2).onAuthentication(this.authentication, this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void delegateShortCircuits() {\n\t\twillThrow(new SessionAuthenticationException(\"oops\")).given(this.strategy1)\n\t\t\t.onAuthentication(this.authentication, this.request, this.response);\n\t\tCompositeSessionAuthenticationStrategy strategy = new CompositeSessionAuthenticationStrategy(\n\t\t\t\tArrays.asList(this.strategy1, this.strategy2));\n\t\tassertThatExceptionOfType(SessionAuthenticationException.class)\n\t\t\t.isThrownBy(() -> strategy.onAuthentication(this.authentication, this.request, this.response));\n\t\tverify(this.strategy1).onAuthentication(this.authentication, this.request, this.response);\n\t\tverify(this.strategy2, times(0)).onAuthentication(this.authentication, this.request, this.response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * @author Rob Winch\n * @author Claudenir Freitas\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class ConcurrentSessionControlAuthenticationStrategyTests {\n\n\t@Mock\n\tprivate SessionRegistry sessionRegistry;\n\n\tprivate Authentication authentication;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate SessionInformation sessionInformation;\n\n\tprivate ConcurrentSessionControlAuthenticationStrategy strategy;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.sessionInformation = new SessionInformation(this.authentication.getPrincipal(), \"unique\",\n\t\t\t\tnew Date(1374766134216L));\n\t\tthis.strategy = new ConcurrentSessionControlAuthenticationStrategy(this.sessionRegistry);\n\t}\n\n\t@Test\n\tpublic void constructorNullRegistry() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ConcurrentSessionControlAuthenticationStrategy(null));\n\t}\n\n\t@Test\n\tpublic void noRegisteredSession() {\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Collections.<SessionInformation>emptyList());\n\t\tthis.strategy.setMaximumSessions(1);\n\t\tthis.strategy.setExceptionIfMaximumExceeded(true);\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\t// no exception\n\t}\n\n\t@Test\n\tpublic void maxSessionsSameSessionId() {\n\t\tMockHttpSession session = new MockHttpSession(new MockServletContext(), this.sessionInformation.getSessionId());\n\t\tthis.request.setSession(session);\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Collections.<SessionInformation>singletonList(this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(1);\n\t\tthis.strategy.setExceptionIfMaximumExceeded(true);\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\t// no exception\n\t}\n\n\t@Test\n\tpublic void maxSessionsWithException() {\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Collections.<SessionInformation>singletonList(this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(1);\n\t\tthis.strategy.setExceptionIfMaximumExceeded(true);\n\t\tassertThatExceptionOfType(SessionAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.strategy.onAuthentication(this.authentication, this.request, this.response));\n\t}\n\n\t@Test\n\tpublic void maxSessionsExpireExistingUser() {\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Collections.<SessionInformation>singletonList(this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(1);\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tassertThat(this.sessionInformation.isExpired()).isTrue();\n\t}\n\n\t@Test\n\tpublic void maxSessionsExpireLeastRecentExistingUser() {\n\t\tSessionInformation moreRecentSessionInfo = new SessionInformation(this.authentication.getPrincipal(), \"unique\",\n\t\t\t\tnew Date(1374766999999L));\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Arrays.<SessionInformation>asList(moreRecentSessionInfo, this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(2);\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tassertThat(this.sessionInformation.isExpired()).isTrue();\n\t}\n\n\t@Test\n\tpublic void onAuthenticationWhenMaxSessionsExceededByTwoThenTwoSessionsExpired() {\n\t\tSessionInformation oldestSessionInfo = new SessionInformation(this.authentication.getPrincipal(), \"unique1\",\n\t\t\t\tnew Date(1374766134214L));\n\t\tSessionInformation secondOldestSessionInfo = new SessionInformation(this.authentication.getPrincipal(),\n\t\t\t\t\"unique2\", new Date(1374766134215L));\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean())).willReturn(\n\t\t\t\tArrays.<SessionInformation>asList(oldestSessionInfo, secondOldestSessionInfo, this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(2);\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tassertThat(oldestSessionInfo.isExpired()).isTrue();\n\t\tassertThat(secondOldestSessionInfo.isExpired()).isTrue();\n\t\tassertThat(this.sessionInformation.isExpired()).isFalse();\n\t}\n\n\t@Test\n\tpublic void setMaximumSessionsWithNullValue() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> this.strategy.setMaximumSessions(null))\n\t\t\t.withMessage(\"sessionLimit cannot be null\");\n\t}\n\n\t@Test\n\tpublic void noRegisteredSessionUsingSessionLimit() {\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean())).willReturn(Collections.emptyList());\n\t\tthis.strategy.setMaximumSessions(SessionLimit.of(1));\n\t\tthis.strategy.setExceptionIfMaximumExceeded(true);\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\t// no exception\n\t}\n\n\t@Test\n\tpublic void maxSessionsSameSessionIdUsingSessionLimit() {\n\t\tMockHttpSession session = new MockHttpSession(new MockServletContext(), this.sessionInformation.getSessionId());\n\t\tthis.request.setSession(session);\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Collections.singletonList(this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(SessionLimit.of(1));\n\t\tthis.strategy.setExceptionIfMaximumExceeded(true);\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\t// no exception\n\t}\n\n\t@Test\n\tpublic void maxSessionsWithExceptionUsingSessionLimit() {\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Collections.singletonList(this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(SessionLimit.of(1));\n\t\tthis.strategy.setExceptionIfMaximumExceeded(true);\n\t\tassertThatExceptionOfType(SessionAuthenticationException.class)\n\t\t\t.isThrownBy(() -> this.strategy.onAuthentication(this.authentication, this.request, this.response));\n\t}\n\n\t@Test\n\tpublic void maxSessionsExpireExistingUserUsingSessionLimit() {\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Collections.singletonList(this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(SessionLimit.of(1));\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tassertThat(this.sessionInformation.isExpired()).isTrue();\n\t}\n\n\t@Test\n\tpublic void maxSessionsExpireLeastRecentExistingUserUsingSessionLimit() {\n\t\tSessionInformation moreRecentSessionInfo = new SessionInformation(this.authentication.getPrincipal(), \"unique\",\n\t\t\t\tnew Date(1374766999999L));\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Arrays.asList(moreRecentSessionInfo, this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(SessionLimit.of(2));\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tassertThat(this.sessionInformation.isExpired()).isTrue();\n\t}\n\n\t@Test\n\tpublic void onAuthenticationWhenMaxSessionsExceededByTwoThenTwoSessionsExpiredUsingSessionLimit() {\n\t\tSessionInformation oldestSessionInfo = new SessionInformation(this.authentication.getPrincipal(), \"unique1\",\n\t\t\t\tnew Date(1374766134214L));\n\t\tSessionInformation secondOldestSessionInfo = new SessionInformation(this.authentication.getPrincipal(),\n\t\t\t\t\"unique2\", new Date(1374766134215L));\n\t\tgiven(this.sessionRegistry.getAllSessions(any(), anyBoolean()))\n\t\t\t.willReturn(Arrays.asList(oldestSessionInfo, secondOldestSessionInfo, this.sessionInformation));\n\t\tthis.strategy.setMaximumSessions(SessionLimit.of(2));\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tassertThat(oldestSessionInfo.isExpired()).isTrue();\n\t\tassertThat(secondOldestSessionInfo.isExpired()).isTrue();\n\t\tassertThat(this.sessionInformation.isExpired()).isFalse();\n\t}\n\n\t@Test\n\tpublic void onAuthenticationWhenSessionLimitIsUnlimited() {\n\t\tthis.strategy.setMaximumSessions(SessionLimit.UNLIMITED);\n\t\tthis.strategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tverifyNoInteractions(this.sessionRegistry);\n\t}\n\n\t@Test\n\tpublic void setMessageSourceNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.strategy.setMessageSource(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/session/RegisterSessionAuthenticationStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.SessionRegistry;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class RegisterSessionAuthenticationStrategyTests {\n\n\t@Mock\n\tprivate SessionRegistry registry;\n\n\tprivate RegisterSessionAuthenticationStrategy authenticationStrategy;\n\n\tprivate Authentication authentication;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.authenticationStrategy = new RegisterSessionAuthenticationStrategy(this.registry);\n\t\tthis.authentication = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void constructorNullRegistry() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RegisterSessionAuthenticationStrategy(null));\n\t}\n\n\t@Test\n\tpublic void onAuthenticationRegistersSession() {\n\t\tthis.authenticationStrategy.onAuthentication(this.authentication, this.request, this.response);\n\t\tverify(this.registry).registerNewSession(this.request.getSession().getId(), this.authentication.getPrincipal());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/session/SessionLimitTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.session;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.mockito.Mockito;\n\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Claudenir Freitas\n * @since 6.5\n */\nclass SessionLimitTests {\n\n\tprivate final Authentication authentication = Mockito.mock(Authentication.class);\n\n\t@Test\n\tvoid testUnlimitedInstance() {\n\t\tSessionLimit sessionLimit = SessionLimit.UNLIMITED;\n\t\tint result = sessionLimit.apply(this.authentication);\n\t\tassertThat(result).isEqualTo(-1);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(ints = { -1, 1, 2, 3 })\n\tvoid testInstanceWithValidMaxSessions(int maxSessions) {\n\t\tSessionLimit sessionLimit = SessionLimit.of(maxSessions);\n\t\tint result = sessionLimit.apply(this.authentication);\n\t\tassertThat(result).isEqualTo(maxSessions);\n\t}\n\n\t@Test\n\tvoid testInstanceWithInvalidMaxSessions() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> SessionLimit.of(0))\n\t\t\t.withMessage(\n\t\t\t\t\t\"MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.switchuser;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.util.FieldUtils;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests\n * {@link org.springframework.security.web.authentication.switchuser.SwitchUserFilter}.\n *\n * @author Mark St.Godard\n * @author Luke Taylor\n */\npublic class SwitchUserFilterTests {\n\n\tprivate static final List<GrantedAuthority> ROLES_12 = AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\");\n\n\t@BeforeEach\n\tpublic void authenticateCurrentUser() {\n\t\tUsernamePasswordAuthenticationToken auth = UsernamePasswordAuthenticationToken.unauthenticated(\"dano\",\n\t\t\t\t\"hawaii50\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\tprivate MockHttpServletRequest createMockSwitchRequest() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setScheme(\"http\");\n\t\trequest.setServerName(\"localhost\");\n\t\trequest.setRequestURI(\"/login/impersonate\");\n\t\trequest.setMethod(\"POST\");\n\t\treturn request;\n\t}\n\n\tprivate Authentication switchToUser(String name) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(\"myUsernameParameter\", name);\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setUsernameParameter(\"myUsernameParameter\");\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\treturn filter.attemptSwitchUser(request);\n\t}\n\n\tprivate Authentication switchToUserWithAuthorityRole(String name, String switchAuthorityRole) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, name);\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setSwitchAuthorityRole(switchAuthorityRole);\n\t\treturn filter.attemptSwitchUser(request);\n\t}\n\n\t@Test\n\tpublic void requiresExitUserMatchesCorrectly() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setExitUserUrl(\"/j_spring_security_my_exit_user\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/j_spring_security_my_exit_user\");\n\t\tassertThat(filter.requiresExitUser(request)).isTrue();\n\t}\n\n\t@Test\n\t// gh-4249\n\tpublic void requiresExitUserWhenEndsWithThenDoesNotMatch() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setExitUserUrl(\"/j_spring_security_my_exit_user\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/foo/bar/j_spring_security_my_exit_user\");\n\t\tassertThat(filter.requiresExitUser(request)).isFalse();\n\t}\n\n\t@Test\n\t// gh-4183\n\tpublic void requiresExitUserWhenGetThenDoesNotMatch() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setScheme(\"http\");\n\t\trequest.setServerName(\"localhost\");\n\t\trequest.setRequestURI(\"/login/impersonate\");\n\t\trequest.setMethod(\"GET\");\n\t\tassertThat(filter.requiresExitUser(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void requiresExitUserWhenMatcherThenWorks() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setExitUserMatcher(AnyRequestMatcher.INSTANCE);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/foo/bar/j_spring_security_my_exit_user\");\n\t\tassertThat(filter.requiresExitUser(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void requiresSwitchMatchesCorrectly() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSwitchUserUrl(\"/j_spring_security_my_switch_user\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/j_spring_security_my_switch_user\");\n\t\tassertThat(filter.requiresSwitchUser(request)).isTrue();\n\t}\n\n\t@Test\n\t// gh-4249\n\tpublic void requiresSwitchUserWhenEndsWithThenDoesNotMatch() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSwitchUserUrl(\"/j_spring_security_my_exit_user\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/foo/bar/j_spring_security_my_exit_user\");\n\t\tassertThat(filter.requiresSwitchUser(request)).isFalse();\n\t}\n\n\t@Test\n\t// gh-4183\n\tpublic void requiresSwitchUserWhenGetThenDoesNotMatch() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setScheme(\"http\");\n\t\trequest.setServerName(\"localhost\");\n\t\trequest.setRequestURI(\"/login/impersonate\");\n\t\trequest.setMethod(\"GET\");\n\t\tassertThat(filter.requiresSwitchUser(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void requiresSwitchUserWhenMatcherThenWorks() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSwitchUserMatcher(AnyRequestMatcher.INSTANCE);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/foo/bar/j_spring_security_my_exit_user\");\n\t\tassertThat(filter.requiresSwitchUser(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void attemptSwitchToUnknownUserFails() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"user-that-doesnt-exist\");\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> filter.attemptSwitchUser(request));\n\t}\n\n\t@Test\n\tpublic void attemptSwitchToUserThatIsDisabledFails() {\n\t\tassertThatExceptionOfType(DisabledException.class).isThrownBy(() -> switchToUser(\"mcgarrett\"));\n\t}\n\n\t@Test\n\tpublic void attemptSwitchToUserWithAccountExpiredFails() {\n\t\tassertThatExceptionOfType(AccountExpiredException.class).isThrownBy(() -> switchToUser(\"wofat\"));\n\t}\n\n\t@Test\n\tpublic void attemptSwitchToUserWithExpiredCredentialsFails() {\n\t\tassertThatExceptionOfType(CredentialsExpiredException.class).isThrownBy(() -> switchToUser(\"steve\"));\n\t}\n\n\t@Test\n\tpublic void switchUserWithNullUsernameThrowsException() {\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> switchToUser(null));\n\t}\n\n\t@Test\n\tpublic void attemptSwitchUserIsSuccessfulWithValidUser() {\n\t\tassertThat(switchToUser(\"jacklord\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void switchToLockedAccountCausesRedirectToSwitchFailureUrl() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/login/impersonate\");\n\t\trequest.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"mcgarrett\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setTargetUrl(\"/target\");\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.afterPropertiesSet();\n\t\t// Check it with no url set (should get a text response)\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain, never()).doFilter(request, response);\n\t\tassertThat(response.getErrorMessage()).isNotNull();\n\t\t// Now check for the redirect\n\t\trequest.setContextPath(\"/mywebapp\");\n\t\trequest.setRequestURI(\"/mywebapp/login/impersonate\");\n\t\tfilter = new SwitchUserFilter();\n\t\tfilter.setTargetUrl(\"/target\");\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setSwitchFailureUrl(\"/switchfailed\");\n\t\tfilter.afterPropertiesSet();\n\t\tresponse = new MockHttpServletResponse();\n\t\tchain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain, never()).doFilter(request, response);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/mywebapp/switchfailed\");\n\t\tassertThat(FieldUtils.getFieldValue(filter, \"switchFailureUrl\")).isEqualTo(\"/switchfailed\");\n\t}\n\n\t@Test\n\tpublic void configMissingUserDetailsServiceFails() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSwitchUserUrl(\"/login/impersonate\");\n\t\tfilter.setExitUserUrl(\"/logout/impersonate\");\n\t\tfilter.setTargetUrl(\"/main.jsp\");\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void testBadConfigMissingTargetUrl() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setSwitchUserUrl(\"/login/impersonate\");\n\t\tfilter.setExitUserUrl(\"/logout/impersonate\");\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void defaultProcessesFilterUrlMatchesUrlWithPathParameter() {\n\t\tMockHttpServletRequest request = createMockSwitchRequest();\n\t\trequest.setContextPath(\"/webapp\");\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSwitchUserUrl(\"/login/impersonate\");\n\t\trequest.setRequestURI(\"/webapp/login/impersonate;jsessionid=8JHDUD723J8\");\n\t\tassertThat(filter.requiresSwitchUser(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void exitUserJackLordToDanoSucceeds() throws Exception {\n\t\t// original user\n\t\tUsernamePasswordAuthenticationToken source = UsernamePasswordAuthenticationToken.authenticated(\"dano\",\n\t\t\t\t\"hawaii50\", ROLES_12);\n\t\t// set current user (Admin)\n\t\tList<GrantedAuthority> adminAuths = new ArrayList<>();\n\t\tadminAuths.addAll(ROLES_12);\n\t\tadminAuths.add(new SwitchUserGrantedAuthority(\"PREVIOUS_ADMINISTRATOR\", source));\n\t\tUsernamePasswordAuthenticationToken admin = UsernamePasswordAuthenticationToken.authenticated(\"jacklord\",\n\t\t\t\t\"hawaii50\", adminAuths);\n\t\tSecurityContextHolder.getContext().setAuthentication(admin);\n\t\tMockHttpServletRequest request = createMockSwitchRequest();\n\t\trequest.setRequestURI(\"/logout/impersonate\");\n\t\t// setup filter\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setExitUserUrl(\"/logout/impersonate\");\n\t\tfilter.setSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(\"/webapp/someOtherUrl\"));\n\t\t// run 'exit'\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain, never()).doFilter(request, response);\n\t\t// check current user, should be back to original user (dano)\n\t\tAuthentication targetAuth = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(targetAuth).isNotNull();\n\t\tassertThat(targetAuth.getPrincipal()).isEqualTo(\"dano\");\n\t}\n\n\t@Test\n\tpublic void exitUserWithNoCurrentUserFails() throws Exception {\n\t\t// no current user in secure context\n\t\tSecurityContextHolder.clearContext();\n\t\tMockHttpServletRequest request = createMockSwitchRequest();\n\t\trequest.setRequestURI(\"/logout/impersonate\");\n\t\t// setup filter\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setExitUserUrl(\"/logout/impersonate\");\n\t\t// run 'exit', expect fail due to no current user\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tassertThatExceptionOfType(AuthenticationException.class)\n\t\t\t.isThrownBy(() -> filter.doFilter(request, response, chain));\n\t\tverify(chain, never()).doFilter(request, response);\n\t}\n\n\t@Test\n\tpublic void redirectToTargetUrlIsCorrect() throws Exception {\n\t\tMockHttpServletRequest request = createMockSwitchRequest();\n\t\trequest.setContextPath(\"/webapp\");\n\t\trequest.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"jacklord\");\n\t\trequest.setRequestURI(\"/webapp/login/impersonate\");\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSwitchUserUrl(\"/login/impersonate\");\n\t\tfilter.setSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(\"/someOtherUrl\"));\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain, never()).doFilter(request, response);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/webapp/someOtherUrl\");\n\t}\n\n\t@Test\n\tpublic void redirectOmitsContextPathIfUseRelativeContextSet() throws Exception {\n\t\t// set current user\n\t\tUsernamePasswordAuthenticationToken auth = UsernamePasswordAuthenticationToken.unauthenticated(\"dano\",\n\t\t\t\t\"hawaii50\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = createMockSwitchRequest();\n\t\trequest.setContextPath(\"/webapp\");\n\t\trequest.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"jacklord\");\n\t\trequest.setRequestURI(\"/webapp/login/impersonate\");\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSwitchUserUrl(\"/login/impersonate\");\n\t\tSimpleUrlAuthenticationSuccessHandler switchSuccessHandler = new SimpleUrlAuthenticationSuccessHandler(\n\t\t\t\t\"/someOtherUrl\");\n\t\tDefaultRedirectStrategy contextRelativeRedirector = new DefaultRedirectStrategy();\n\t\tcontextRelativeRedirector.setContextRelative(true);\n\t\tswitchSuccessHandler.setRedirectStrategy(contextRelativeRedirector);\n\t\tfilter.setSuccessHandler(switchSuccessHandler);\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain, never()).doFilter(request, response);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/someOtherUrl\");\n\t}\n\n\t@Test\n\tpublic void testSwitchRequestFromDanoToJackLord() throws Exception {\n\t\t// set current user\n\t\tUsernamePasswordAuthenticationToken auth = UsernamePasswordAuthenticationToken.unauthenticated(\"dano\",\n\t\t\t\t\"hawaii50\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\t// http request\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/webapp/login/impersonate\");\n\t\trequest.setContextPath(\"/webapp\");\n\t\trequest.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"jacklord\");\n\t\t// http response\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// setup filter\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setSwitchUserUrl(\"/login/impersonate\");\n\t\tfilter.setSuccessHandler(new SimpleUrlAuthenticationSuccessHandler(\"/webapp/someOtherUrl\"));\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\t// test updates user token and context\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain, never()).doFilter(request, response);\n\t\t// check current user\n\t\tAuthentication targetAuth = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(targetAuth).isNotNull();\n\t\tassertThat(targetAuth.getPrincipal() instanceof UserDetails).isTrue();\n\t\tassertThat(((User) targetAuth.getPrincipal()).getUsername()).isEqualTo(\"jacklord\");\n\t}\n\n\t@Test\n\tpublic void modificationOfAuthoritiesWorks() {\n\t\tUsernamePasswordAuthenticationToken auth = UsernamePasswordAuthenticationToken.unauthenticated(\"dano\",\n\t\t\t\t\"hawaii50\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"jacklord\");\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setSwitchUserAuthorityChanger((targetUser, currentAuthentication, authoritiesToBeGranted) -> {\n\t\t\tList<GrantedAuthority> auths = new ArrayList<>();\n\t\t\tauths.add(new SimpleGrantedAuthority(\"ROLE_NEW\"));\n\t\t\treturn auths;\n\t\t});\n\t\tAuthentication result = filter.attemptSwitchUser(request);\n\t\tassertThat(result != null).isTrue();\n\t\tassertThat(result.getAuthorities()).hasSize(2);\n\t\tassertThat(AuthorityUtils.authorityListToSet(result.getAuthorities())).contains(\"ROLE_NEW\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomSecurityContextRepositoryThenUses() {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = spy(new MockSecurityContextHolderStrategy(\n\t\t\t\tUsernamePasswordAuthenticationToken.unauthenticated(\"dano\", \"hawaii50\")));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"jacklord\");\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tAuthentication result = filter.attemptSwitchUser(request);\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.getName()).isEqualTo(\"jacklord\");\n\t\tverify(securityContextHolderStrategy, atLeastOnce()).getContext();\n\t}\n\n\t// SEC-1763\n\t@Test\n\tpublic void nestedSwitchesAreNotAllowed() {\n\t\t// original user\n\t\tUsernamePasswordAuthenticationToken source = UsernamePasswordAuthenticationToken.authenticated(\"orig\",\n\t\t\t\t\"hawaii50\", ROLES_12);\n\t\tSecurityContextHolder.getContext().setAuthentication(source);\n\t\tSecurityContextHolder.getContext().setAuthentication(switchToUser(\"jacklord\"));\n\t\tAuthentication switched = switchToUser(\"dano\");\n\t\tSwitchUserGrantedAuthority switchedFrom = null;\n\t\tfor (GrantedAuthority ga : switched.getAuthorities()) {\n\t\t\tif (ga instanceof SwitchUserGrantedAuthority) {\n\t\t\t\tswitchedFrom = (SwitchUserGrantedAuthority) ga;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tassertThat(switchedFrom).isNotNull();\n\t\tassertThat(source).isSameAs(switchedFrom.getSource());\n\t}\n\n\t// gh-3697\n\t@Test\n\tpublic void switchAuthorityRoleCannotBeNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> switchToUserWithAuthorityRole(\"dano\", null))\n\t\t\t.withMessage(\"switchAuthorityRole cannot be null\");\n\t}\n\n\t// gh-3697\n\t@Test\n\tpublic void switchAuthorityRoleCanBeChanged() {\n\t\tString switchAuthorityRole = \"PREVIOUS_ADMINISTRATOR\";\n\t\t// original user\n\t\tUsernamePasswordAuthenticationToken source = UsernamePasswordAuthenticationToken.authenticated(\"orig\",\n\t\t\t\t\"hawaii50\", ROLES_12);\n\t\tSecurityContextHolder.getContext().setAuthentication(source);\n\t\tSecurityContextHolder.getContext().setAuthentication(switchToUser(\"jacklord\"));\n\t\tAuthentication switched = switchToUserWithAuthorityRole(\"dano\", switchAuthorityRole);\n\t\tSwitchUserGrantedAuthority switchedFrom = null;\n\t\tfor (GrantedAuthority ga : switched.getAuthorities()) {\n\t\t\tif (ga instanceof SwitchUserGrantedAuthority) {\n\t\t\t\tswitchedFrom = (SwitchUserGrantedAuthority) ga;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tassertThat(switchedFrom).isNotNull();\n\t\tassertThat(switchedFrom.getSource()).isSameAs(source);\n\t\tassertThat(switchAuthorityRole).isEqualTo(switchedFrom.getAuthority());\n\t}\n\n\t@Test\n\tpublic void setSwitchFailureUrlWhenNullThenThrowException() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setSwitchFailureUrl(null));\n\t}\n\n\t@Test\n\tpublic void setSwitchFailureUrlWhenEmptyThenThrowException() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setSwitchFailureUrl(\"\"));\n\t}\n\n\t@Test\n\tpublic void setSwitchFailureUrlWhenValidThenNoException() {\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSwitchFailureUrl(\"/foo\");\n\t}\n\n\t@Test\n\tvoid filterWhenDefaultSecurityContextRepositoryThenHttpSessionRepository() {\n\t\tSwitchUserFilter switchUserFilter = new SwitchUserFilter();\n\t\tassertThat(ReflectionTestUtils.getField(switchUserFilter, \"securityContextRepository\"))\n\t\t\t.isInstanceOf(HttpSessionSecurityContextRepository.class);\n\t}\n\n\t@Test\n\tvoid doFilterWhenSwitchUserThenSaveSecurityContext() throws ServletException, IOException {\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/login/impersonate\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain filterChain = new MockFilterChain();\n\t\trequest.setParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"jacklord\");\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSecurityContextRepository(securityContextRepository);\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setTargetUrl(\"/target\");\n\t\tfilter.afterPropertiesSet();\n\n\t\tfilter.doFilter(request, response, filterChain);\n\n\t\tverify(securityContextRepository).saveContext(any(), any(), any());\n\t}\n\n\t@Test\n\tvoid doFilterWhenExitUserThenSaveSecurityContext() throws ServletException, IOException {\n\t\tUsernamePasswordAuthenticationToken source = UsernamePasswordAuthenticationToken.authenticated(\"dano\",\n\t\t\t\t\"hawaii50\", ROLES_12);\n\t\t// set current user (Admin)\n\t\tList<GrantedAuthority> adminAuths = new ArrayList<>(ROLES_12);\n\t\tadminAuths.add(new SwitchUserGrantedAuthority(\"PREVIOUS_ADMINISTRATOR\", source));\n\t\tUsernamePasswordAuthenticationToken admin = UsernamePasswordAuthenticationToken.authenticated(\"jacklord\",\n\t\t\t\t\"hawaii50\", adminAuths);\n\t\tSecurityContextHolder.getContext().setAuthentication(admin);\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/logout/impersonate\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain filterChain = new MockFilterChain();\n\t\trequest.setParameter(SwitchUserFilter.SPRING_SECURITY_SWITCH_USERNAME_KEY, \"jacklord\");\n\t\tSwitchUserFilter filter = new SwitchUserFilter();\n\t\tfilter.setSecurityContextRepository(securityContextRepository);\n\t\tfilter.setUserDetailsService(new MockUserDetailsService());\n\t\tfilter.setTargetUrl(\"/target\");\n\t\tfilter.afterPropertiesSet();\n\n\t\tfilter.doFilter(request, response, filterChain);\n\n\t\tverify(securityContextRepository).saveContext(any(), any(), any());\n\t}\n\n\tprivate class MockUserDetailsService implements UserDetailsService {\n\n\t\tprivate String password = \"hawaii50\";\n\n\t\t@Override\n\t\tpublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n\t\t\t// jacklord, dano (active)\n\t\t\t// mcgarrett (disabled)\n\t\t\t// wofat (account expired)\n\t\t\t// steve (credentials expired)\n\t\t\tif (\"jacklord\".equals(username) || \"dano\".equals(username)) {\n\t\t\t\treturn new User(username, this.password, true, true, true, true, ROLES_12);\n\t\t\t}\n\t\t\telse if (\"mcgarrett\".equals(username)) {\n\t\t\t\treturn new User(username, this.password, false, true, true, true, ROLES_12);\n\t\t\t}\n\t\t\telse if (\"wofat\".equals(username)) {\n\t\t\t\treturn new User(username, this.password, true, false, true, true, ROLES_12);\n\t\t\t}\n\t\t\telse if (\"steve\".equals(username)) {\n\t\t\t\treturn new User(username, this.password, true, true, false, true, ROLES_12);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new UsernameNotFoundException(\"Could not find: \" + username);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic final class MockSecurityContextHolderStrategy implements SecurityContextHolderStrategy {\n\n\t\tprivate SecurityContext mock;\n\n\t\tprivate MockSecurityContextHolderStrategy(Authentication authentication) {\n\t\t\tthis.mock = new SecurityContextImpl(authentication);\n\t\t}\n\n\t\t@Override\n\t\tpublic void clearContext() {\n\t\t\tthis.mock = null;\n\t\t}\n\n\t\t@Override\n\t\tpublic SecurityContext getContext() {\n\t\t\treturn this.mock;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setContext(SecurityContext context) {\n\t\t\tthis.mock = context;\n\t\t}\n\n\t\t@Override\n\t\tpublic SecurityContext createEmptyContext() {\n\t\t\treturn new SecurityContextImpl();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/switchuser/SwitchUserGrantedAuthorityTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.switchuser;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Clement Ng\n *\n */\npublic class SwitchUserGrantedAuthorityTests {\n\n\t@Test\n\tpublic void authorityWithNullRoleFailsAssertion() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SwitchUserGrantedAuthority(null, null))\n\t\t\t.withMessage(\"role cannot be null\");\n\t}\n\n\t@Test\n\tpublic void authorityWithNullSourceFailsAssertion() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SwitchUserGrantedAuthority(\"role\", null))\n\t\t\t.withMessage(\"source cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.hamcrest.CoreMatchers.containsString;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\npublic class DefaultLogoutPageGeneratingFilterTests {\n\n\tprivate DefaultLogoutPageGeneratingFilter filter = new DefaultLogoutPageGeneratingFilter();\n\n\t@Test\n\tpublic void doFilterWhenNoHiddenInputsThenPageRendered() throws Exception {\n\t\tMockMvc mockMvc = MockMvcBuilders.standaloneSetup(new Object()).addFilter(this.filter).build();\n\t\tmockMvc.perform(get(\"/logout\"))\n\t\t\t.andExpect(content().string(containsString(\"Are you sure you want to log out?\")))\n\t\t\t.andExpect(content().contentType(\"text/html;charset=UTF-8\"));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenHiddenInputsSetThenHiddenInputsRendered() throws Exception {\n\t\tthis.filter.setResolveHiddenInputs((r) -> Collections.singletonMap(\"_csrf\", \"csrf-token-1\"));\n\t\tMockMvc mockMvc = MockMvcBuilders.standaloneSetup(new Object()).addFilters(this.filter).build();\n\t\tmockMvc.perform(get(\"/logout\"))\n\t\t\t.andExpect(content()\n\t\t\t\t.string(containsString(\"<input name=\\\"_csrf\\\" type=\\\"hidden\\\" value=\\\"csrf-token-1\\\" />\")));\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestContextThenActionContainsRequestContext() throws Exception {\n\t\tMockMvc mockMvc = MockMvcBuilders.standaloneSetup(new Object()).addFilters(this.filter).build();\n\t\tmockMvc.perform(get(\"/context/logout\").contextPath(\"/context\"))\n\t\t\t.andExpect(content().string(containsString(\"action=\\\"/context/logout\\\"\")));\n\t}\n\n\t@Test\n\tvoid doFilterWhenRequestContextAndHiddenInputsSetThenRendered() throws Exception {\n\t\tthis.filter.setResolveHiddenInputs((r) -> Collections.singletonMap(\"_csrf\", \"csrf-token-1\"));\n\t\tMockMvc mockMvc = MockMvcBuilders.standaloneSetup(new Object()).addFilters(this.filter).build();\n\n\t\tmockMvc.perform(get(\"/context/logout\").contextPath(\"/context\")).andExpect(content().string(\"\"\"\n\t\t\t\t<!DOCTYPE html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t  <head>\n\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t    <title>Confirm Log Out?</title>\n\t\t\t\t    <link href=\"/context/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t  </head>\n\t\t\t\t  <body>\n\t\t\t\t    <div class=\"content\">\n\t\t\t\t      <form class=\"logout-form\" method=\"post\" action=\"/context/logout\">\n\t\t\t\t        <h2>Are you sure you want to log out?</h2>\n\t\t\t\t        <input name=\"_csrf\" type=\"hidden\" value=\"csrf-token-1\" />\n\t\t\t\t        <button class=\"primary\" type=\"submit\">Log Out</button>\n\t\t\t\t      </form>\n\t\t\t\t    </div>\n\t\t\t\t  </body>\n\t\t\t\t</html>\"\"\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ui/DefaultOneTimeTokenSubmitPageGeneratingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests for {@link DefaultOneTimeTokenSubmitPageGeneratingFilter}\n *\n * @author Marcus da Coregio\n */\nclass DefaultOneTimeTokenSubmitPageGeneratingFilterTests {\n\n\tDefaultOneTimeTokenSubmitPageGeneratingFilter filter = new DefaultOneTimeTokenSubmitPageGeneratingFilter();\n\n\tMockHttpServletRequest request;\n\n\tMockHttpServletResponse response = new MockHttpServletResponse();\n\n\tMockFilterChain filterChain = new MockFilterChain();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.request = get(\"/login/ott\").build();\n\t}\n\n\t@Test\n\tvoid filterWhenTokenQueryParamThenShouldIncludeJavascriptToAutoSubmitFormAndInputHasTokenValue() throws Exception {\n\t\tthis.request.setParameter(\"token\", \"1234\");\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tString response = this.response.getContentAsString();\n\t\tassertThat(response).contains(\n\t\t\t\t\"<input type=\\\"text\\\" id=\\\"token\\\" name=\\\"token\\\" value=\\\"1234\\\" placeholder=\\\"Token\\\" required=\\\"true\\\" autofocus=\\\"autofocus\\\"/>\");\n\t}\n\n\t@Test\n\tvoid setRequestMatcherWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestMatcher(null));\n\t}\n\n\t@Test\n\tvoid setLoginProcessingUrlWhenNullOrEmptyThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setLoginProcessingUrl(null));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setLoginProcessingUrl(\"\"));\n\t}\n\n\t@Test\n\tvoid setLoginProcessingUrlThenUseItForFormAction() throws Exception {\n\t\tthis.filter.setLoginProcessingUrl(\"/login/another\");\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tString response = this.response.getContentAsString();\n\t\tassertThat(response).contains(\"<form class=\\\"login-form\\\" action=\\\"/login/another\\\" method=\\\"post\\\">\");\n\t}\n\n\t@Test\n\tvoid setContextThenGenerates() throws Exception {\n\t\tMockHttpServletRequest request = get().requestUri(\"/context\", \"/login/ott\", null).build();\n\t\tthis.filter.setLoginProcessingUrl(\"/login/another\");\n\t\tthis.filter.doFilterInternal(request, this.response, this.filterChain);\n\t\tString response = this.response.getContentAsString();\n\t\tassertThat(response).contains(\"<form class=\\\"login-form\\\" action=\\\"/context/login/another\\\" method=\\\"post\\\">\");\n\t}\n\n\t@Test\n\tvoid filterWhenTokenQueryParamUsesSpecialCharactersThenValueIsEscaped() throws Exception {\n\t\tthis.request.setParameter(\"token\", \"this<>!@#\\\"\");\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tString response = this.response.getContentAsString();\n\t\tassertThat(response).contains(\n\t\t\t\t\"<input type=\\\"text\\\" id=\\\"token\\\" name=\\\"token\\\" value=\\\"this&lt;&gt;!@#&quot;\\\" placeholder=\\\"Token\\\" required=\\\"true\\\" autofocus=\\\"autofocus\\\"/>\");\n\t}\n\n\t@Test\n\tvoid filterThenRenders() throws Exception {\n\t\tthis.request.setParameter(\"token\", \"this<>!@#\\\"\");\n\t\tthis.filter.setLoginProcessingUrl(\"/login/another\");\n\t\tthis.filter.setResolveHiddenInputs((r) -> Map.of(\"_csrf\", \"csrf-token-value\"));\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tString response = this.response.getContentAsString();\n\t\tassertThat(response).isEqualTo(\n\t\t\t\t\"\"\"\n\t\t\t\t\t\t<!DOCTYPE html>\n\t\t\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t\t  <head>\n\t\t\t\t\t\t    <title>One-Time Token Login</title>\n\t\t\t\t\t\t    <meta charset=\"utf-8\"/>\n\t\t\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"/>\n\t\t\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t\t  </head>\n\t\t\t\t\t\t  <body>\n\t\t\t\t\t\t    <div class=\"container\">\n\t\t\t\t\t\t      <form class=\"login-form\" action=\"/login/another\" method=\"post\">\n\t\t\t\t\t\t        <h2>Please input the token</h2>\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"token\" class=\"screenreader\">Token</label>\n\t\t\t\t\t\t          <input type=\"text\" id=\"token\" name=\"token\" value=\"this&lt;&gt;!@#&quot;\" placeholder=\"Token\" required=\"true\" autofocus=\"autofocus\"/>\n\t\t\t\t\t\t        </p>\n\t\t\t\t\t\t        <button class=\"primary\" type=\"submit\">Sign in</button>\n\t\t\t\t\t\t<input name=\"_csrf\" type=\"hidden\" value=\"csrf-token-value\" />\n\t\t\t\t\t\t      </form>\n\t\t\t\t\t\t    </div>\n\t\t\t\t\t\t  </body>\n\t\t\t\t\t\t</html>\n\t\t\t\t\t\t\"\"\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ui/DefaultResourcesFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.containsString;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n */\npublic class DefaultResourcesFilterTests {\n\n\t@Nested\n\tclass CssFilter {\n\n\t\tprivate final DefaultResourcesFilter cssFilter = DefaultResourcesFilter.css();\n\n\t\tprivate final MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new Object())\n\t\t\t.addFilters(this.cssFilter)\n\t\t\t.build();\n\n\t\t@Test\n\t\tvoid doFilterThenRender() throws Exception {\n\t\t\tthis.mockMvc.perform(get(\"/default-ui.css\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().contentType(\"text/css;charset=UTF-8\"))\n\t\t\t\t.andExpect(content().string(containsString(\"body {\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid doFilterWhenPathDoesNotMatchThenCallsThrough() throws Exception {\n\t\t\tthis.mockMvc.perform(get(\"/does-not-match\")).andExpect(status().isNotFound());\n\t\t}\n\n\t\t@Test\n\t\tvoid toStringPrintsPathAndResource() {\n\t\t\tassertThat(this.cssFilter.toString()).isEqualTo(\n\t\t\t\t\t\"DefaultResourcesFilter [matcher=PathPattern [GET /default-ui.css], resource=org/springframework/security/default-ui.css]\");\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass WebAuthnFilter {\n\n\t\tprivate final DefaultResourcesFilter webauthnFilter = DefaultResourcesFilter.webauthn();\n\n\t\tprivate final MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new Object())\n\t\t\t.addFilters(this.webauthnFilter)\n\t\t\t.build();\n\n\t\t@Test\n\t\tvoid doFilterThenRender() throws Exception {\n\t\t\tthis.mockMvc.perform(get(\"/login/webauthn.js\"))\n\t\t\t\t.andExpect(status().isOk())\n\t\t\t\t.andExpect(content().contentType(\"text/javascript;charset=UTF-8\"))\n\t\t\t\t.andExpect(content().string(containsString(\"async function authenticate(\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid doFilterWhenPathDoesNotMatchThenCallsThrough() throws Exception {\n\t\t\tthis.mockMvc.perform(get(\"/does-not-match\")).andExpect(status().isNotFound());\n\t\t}\n\n\t\t@Test\n\t\tvoid toStringPrintsPathAndResource() {\n\t\t\tassertThat(this.webauthnFilter.toString()).isEqualTo(\n\t\t\t\t\t\"DefaultResourcesFilter [matcher=PathPattern [GET /login/webauthn.js], resource=org/springframework/security/spring-security-webauthn.js]\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/ui/HtmlTemplatesTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.ui;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n */\nclass HtmlTemplatesTests {\n\n\t@Test\n\tvoid processTemplateWhenNoVariablesThenRendersTemplate() {\n\t\tString template = \"\"\"\n\t\t\t\t<ul>\n\t\t\t\t\t<li>Lorem ipsum dolor sit amet</li>\n\t\t\t\t\t<li>consectetur adipiscing elit</li>\n\t\t\t\t\t<li>sed do eiusmod tempor incididunt ut labore</li>\n\t\t\t\t\t<li>et dolore magna aliqua</li>\n\t\t\t\t</ul>\n\t\t\t\t\"\"\";\n\n\t\tassertThat(HtmlTemplates.fromTemplate(template).render()).isEqualTo(template);\n\t}\n\n\t@Test\n\tvoid renderWhenVariablesThenRendersTemplate() {\n\t\tString template = \"\"\"\n\t\t\t\t<ul>\n\t\t\t\t\t<li>{{one}}</li>\n\t\t\t\t\t<li>{{two}}</li>\n\t\t\t\t</ul>\n\t\t\t\t\"\"\";\n\n\t\tString renderedTemplate = HtmlTemplates.fromTemplate(template)\n\t\t\t.withValue(\"one\", \"Lorem ipsum dolor sit amet\")\n\t\t\t.withValue(\"two\", \"consectetur adipiscing elit\")\n\t\t\t.render();\n\n\t\tassertThat(renderedTemplate).isEqualTo(\"\"\"\n\t\t\t\t<ul>\n\t\t\t\t\t<li>Lorem ipsum dolor sit amet</li>\n\t\t\t\t\t<li>consectetur adipiscing elit</li>\n\t\t\t\t</ul>\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid renderWhenVariablesThenEscapedAndRender() {\n\t\tString template = \"<p>{{content}}</p>\";\n\n\t\tString renderedTemplate = HtmlTemplates.fromTemplate(template)\n\t\t\t.withValue(\"content\", \"The <a> tag is very common in HTML.\")\n\t\t\t.render();\n\n\t\tassertThat(renderedTemplate).isEqualTo(\"<p>The &lt;a&gt; tag is very common in HTML.</p>\");\n\t}\n\n\t@Test\n\tvoid renderWhenRawHtmlVariablesThenRendersTemplate() {\n\t\tString template = \"\"\"\n\t\t\t\t<p>\n\t\t\t\t\tThe {{title}} is a placeholder text used in print.\n\t\t\t\t</p>\n\t\t\t\t\"\"\";\n\n\t\tString renderedTemplate = HtmlTemplates.fromTemplate(template)\n\t\t\t.withRawHtml(\"title\", \"<strong>Lorem Ipsum</strong>\")\n\t\t\t.render();\n\n\t\tassertThat(renderedTemplate).isEqualTo(\"\"\"\n\t\t\t\t<p>\n\t\t\t\t\tThe <strong>Lorem Ipsum</strong> is a placeholder text used in print.\n\t\t\t\t</p>\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid renderWhenRawHtmlVariablesThenTrimsTrailingNewline() {\n\t\tString template = \"\"\"\n\t\t\t\t<ul>\n\t\t\t\t{{content}}\n\t\t\t\t</ul>\n\t\t\t\t\"\"\";\n\n\t\tString renderedTemplate = HtmlTemplates.fromTemplate(template)\n\t\t\t.withRawHtml(\"content\", \"<li>Lorem ipsum dolor sit amet</li>\".indent(2))\n\t\t\t.render();\n\n\t\tassertThat(renderedTemplate).isEqualTo(\"\"\"\n\t\t\t\t<ul>\n\t\t\t\t  <li>Lorem ipsum dolor sit amet</li>\n\t\t\t\t</ul>\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid renderWhenEmptyVariablesThenRender() {\n\t\tString template = \"\"\"\n\t\t\t\t<li>One: {{one}}</li>\n\t\t\t\t{{two}}\n\t\t\t\t\"\"\";\n\n\t\tString renderedTemplate = HtmlTemplates.fromTemplate(template)\n\t\t\t.withValue(\"one\", \"\")\n\t\t\t.withRawHtml(\"two\", \"\")\n\t\t\t.render();\n\n\t\tassertThat(renderedTemplate).isEqualTo(\"\"\"\n\t\t\t\t<li>One: </li>\n\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid renderWhenMissingVariablesThenThrows() {\n\t\tString template = \"\"\"\n\t\t\t\t<li>One: {{one}}</li>\n\t\t\t\t<li>Two: {{two}}</li>\n\t\t\t\t{{three}}\n\t\t\t\t\"\"\";\n\n\t\tHtmlTemplates.Builder templateBuilder = HtmlTemplates.fromTemplate(template)\n\t\t\t.withValue(\"one\", \"Lorem ipsum dolor sit amet\");\n\t\tassertThatExceptionOfType(IllegalStateException.class).isThrownBy(templateBuilder::render)\n\t\t\t.withMessage(\"Unused placeholders in template: [two, three]\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.AuthenticationDetailsSource;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.test.web.CodecTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Sergey Bespalov\n * @since 5.2.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class BasicAuthenticationConverterTests {\n\n\t@Mock\n\tprivate AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;\n\n\tprivate BasicAuthenticationConverter converter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.converter = new BasicAuthenticationConverter(this.authenticationDetailsSource);\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() {\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tUsernamePasswordAuthenticationToken authentication = this.converter.convert(request);\n\t\tverify(this.authenticationDetailsSource).buildDetails(any());\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void requestWhenAuthorizationSchemeInMixedCaseThenAuthenticates() {\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"BaSiC \" + CodecTestUtils.encodeBase64(token));\n\t\tUsernamePasswordAuthenticationToken authentication = this.converter.convert(request);\n\t\tverify(this.authenticationDetailsSource).buildDetails(any());\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void testWhenUnsupportedAuthorizationHeaderThenIgnored() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Bearer someOtherToken\");\n\t\tUsernamePasswordAuthenticationToken authentication = this.converter.convert(request);\n\t\tverifyNoMoreInteractions(this.authenticationDetailsSource);\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void testWhenInvalidBasicAuthorizationTokenThenError() {\n\t\tString token = \"NOT_A_VALID_TOKEN_AS_MISSING_COLON\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.converter.convert(request));\n\t}\n\n\t@Test\n\tpublic void testWhenInvalidBase64ThenError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic NOT_VALID_BASE64\");\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.converter.convert(request));\n\t}\n\n\t@Test\n\tpublic void convertWhenEmptyPassword() {\n\t\tString token = \"rod:\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tUsernamePasswordAuthenticationToken authentication = this.converter.convert(request);\n\t\tverify(this.authenticationDetailsSource).buildDetails(any());\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getName()).isEqualTo(\"rod\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void requestWhenEmptyBasicAuthorizationHeaderTokenThenError() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \");\n\t\tassertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.converter.convert(request));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.DisabledException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link BasicAuthenticationEntryPoint}.\n *\n * @author Ben Alex\n * @author Andrey Litvitski\n */\npublic class BasicAuthenticationEntryPointTests {\n\n\t@Test\n\tpublic void testDetectsMissingRealmName() {\n\t\tBasicAuthenticationEntryPoint ep = new BasicAuthenticationEntryPoint();\n\t\tassertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet)\n\t\t\t.withMessage(\"realmName must be specified\");\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tBasicAuthenticationEntryPoint ep = new BasicAuthenticationEntryPoint();\n\t\tep.setRealmName(\"realm\");\n\t\tassertThat(ep.getRealmName()).isEqualTo(\"realm\");\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() throws Exception {\n\t\tBasicAuthenticationEntryPoint ep = new BasicAuthenticationEntryPoint();\n\t\tep.setRealmName(\"hello\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/some_path\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// ep.afterPropertiesSet();\n\t\tep.commence(request, response, new DisabledException(\"These are the jokes kid\"));\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(response.getErrorMessage()).isEqualTo(HttpStatus.UNAUTHORIZED.getReasonPhrase());\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\"Basic realm=\\\"hello\\\", charset=\\\"UTF-8\\\"\");\n\t}\n\n\t// gh-13737\n\t@Test\n\tvoid commenceWhenResponseHasHeaderThenOverride() throws IOException {\n\t\tBasicAuthenticationEntryPoint ep = new BasicAuthenticationEntryPoint();\n\t\tep.setRealmName(\"hello\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/some_path\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tresponse.setHeader(HttpHeaders.WWW_AUTHENTICATE, \"Basic realm=\\\"test\\\"\");\n\t\tep.commence(request, response, new DisabledException(\"Disabled\"));\n\t\tList<String> headers = response.getHeaders(\"WWW-Authenticate\");\n\t\tassertThat(headers).hasSize(1);\n\t\tassertThat(headers.get(0)).isEqualTo(\"Basic realm=\\\"hello\\\", charset=\\\"UTF-8\\\"\");\n\t}\n\n\t@Test\n\tvoid commenceWhenDefaultThenIncludesUtf8Charset() throws Exception {\n\t\tBasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();\n\t\tentryPoint.setRealmName(\"TestRealm\");\n\t\tentryPoint.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tentryPoint.commence(request, response, new BadCredentialsException(\"test\"));\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\"Basic realm=\\\"TestRealm\\\", charset=\\\"UTF-8\\\"\");\n\t}\n\n\t@Test\n\tvoid commenceWhenCharsetIsNullThenOmitsCharset() throws Exception {\n\t\tBasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();\n\t\tentryPoint.setRealmName(\"TestRealm\");\n\t\tentryPoint.setCharset(null);\n\t\tentryPoint.afterPropertiesSet();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tentryPoint.commence(request, response, new BadCredentialsException(\"test\"));\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\")).isEqualTo(\"Basic realm=\\\"TestRealm\\\"\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.NonBuildableAuthenticationToken;\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.test.web.CodecTestUtils;\nimport org.springframework.security.web.authentication.AuthenticationConverter;\nimport org.springframework.security.web.authentication.DefaultEqualsGrantedAuthority;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.web.util.WebUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.AdditionalMatchers.not;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Tests {@link BasicAuthenticationFilter}.\n *\n * @author Ben Alex\n */\npublic class BasicAuthenticationFilterTests {\n\n\tprivate BasicAuthenticationFilter filter;\n\n\tprivate AuthenticationManager manager;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tSecurityContextHolder.clearContext();\n\t\tUsernamePasswordAuthenticationToken rodRequest = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\",\n\t\t\t\t\"koala\");\n\t\trodRequest.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));\n\t\tAuthentication rod = UsernamePasswordAuthenticationToken.authenticated(\"rod\", \"koala\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_1\"));\n\t\tthis.manager = mock(AuthenticationManager.class);\n\t\tgiven(this.manager.authenticate(rodRequest)).willReturn(rod);\n\t\tgiven(this.manager.authenticate(not(eq(rodRequest)))).willThrow(new BadCredentialsException(\"\"));\n\t\tthis.filter = new BasicAuthenticationFilter(this.manager, new BasicAuthenticationEntryPoint());\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testFilterIgnoresRequestsContainingNoAuthorizationHeader() throws Exception {\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\t// Test\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tassertThat(this.filter.getAuthenticationManager()).isNotNull();\n\t\tassertThat(this.filter.getAuthenticationEntryPoint()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testInvalidBasicAuthorizationTokenIsIgnored() throws Exception {\n\t\tString token = \"NOT_A_VALID_TOKEN_AS_MISSING_COLON\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\trequest.setSession(new MockHttpSession());\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\tverify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void invalidBase64IsIgnored() throws Exception {\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic NOT_VALID_BASE64\");\n\t\trequest.setSession(new MockHttpSession());\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\t// The filter chain shouldn't proceed\n\t\tverify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() throws Exception {\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\t// Test\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, new MockHttpServletResponse(), chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void testSecurityContextHolderStrategyUsed() throws Exception {\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token.getBytes()));\n\t\tSecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());\n\t\tthis.filter.setSecurityContextHolderStrategy(strategy);\n\t\tthis.filter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tArgumentCaptor<SecurityContext> captor = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(strategy).setContext(captor.capture());\n\t\tassertThat(captor.getValue().getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t}\n\n\t// gh-5586\n\t@Test\n\tpublic void doFilterWhenSchemeLowercaseThenCaseInsensitveMatchWorks() throws Exception {\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"basic \" + CodecTestUtils.encodeBase64(token));\n\t\t// Test\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, new MockHttpServletResponse(), chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSchemeMixedCaseThenCaseInsensitiveMatchWorks() throws Exception {\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"BaSiC \" + CodecTestUtils.encodeBase64(token));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, new MockHttpServletResponse(), chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void testOtherAuthorizationSchemeIsIgnored() throws Exception {\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"SOME_OTHER_AUTHENTICATION_SCHEME\");\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, new MockHttpServletResponse(), chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void testStartupDetectsMissingAuthenticationEntryPoint() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new BasicAuthenticationFilter(this.manager, null));\n\t}\n\n\t@Test\n\tpublic void testStartupDetectsMissingAuthenticationManager() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new BasicAuthenticationFilter(null));\n\t}\n\n\t@Test\n\tpublic void testSuccessLoginThenFailureLoginResultsInSessionLosingToken() throws Exception {\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tfinal MockHttpServletResponse response1 = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response1, chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\t// Test\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"rod\");\n\t\t// NOW PERFORM FAILED AUTHENTICATION\n\t\ttoken = \"otherUser:WRONG_PASSWORD\";\n\t\trequest = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tfinal MockHttpServletResponse response2 = new MockHttpServletResponse();\n\t\tchain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response2, chain);\n\t\tverify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\t// Test - the filter chain will not be invoked, as we get a 401 forbidden response\n\t\tMockHttpServletResponse response = response2;\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void testWrongPasswordContinuesFilterChainIfIgnoreFailureIsTrue() throws Exception {\n\t\tString token = \"rod:WRONG_PASSWORD\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\trequest.setSession(new MockHttpSession());\n\t\tthis.filter = new BasicAuthenticationFilter(this.manager);\n\t\tassertThat(this.filter.isIgnoreFailure()).isTrue();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, new MockHttpServletResponse(), chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\t// Test - the filter chain will be invoked, as we've set ignoreFailure = true\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void testWrongPasswordReturnsForbiddenIfIgnoreFailureIsFalse() throws Exception {\n\t\tString token = \"rod:WRONG_PASSWORD\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\trequest.setSession(new MockHttpSession());\n\t\tassertThat(this.filter.isIgnoreFailure()).isFalse();\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\t// Test - the filter chain will not be invoked, as we get a 401 forbidden response\n\t\tverify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t// SEC-2054\n\t@Test\n\tpublic void skippedOnErrorDispatch() throws Exception {\n\t\tString token = \"bad:credentials\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\trequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, \"/error\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\tassertThat(response.getStatus()).isEqualTo(200);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenAndFilterCharsetMatchDefaultThenAuthenticated() throws Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tUsernamePasswordAuthenticationToken rodRequest = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\",\n\t\t\t\t\"äöü\");\n\t\trodRequest.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));\n\t\tAuthentication rod = UsernamePasswordAuthenticationToken.authenticated(\"rod\", \"äöü\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_1\"));\n\t\tthis.manager = mock(AuthenticationManager.class);\n\t\tgiven(this.manager.authenticate(rodRequest)).willReturn(rod);\n\t\tgiven(this.manager.authenticate(not(eq(rodRequest)))).willThrow(new BadCredentialsException(\"\"));\n\t\tthis.filter = new BasicAuthenticationFilter(this.manager, new BasicAuthenticationEntryPoint());\n\t\tString token = \"rod:äöü\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\",\n\t\t\t\t\"Basic \" + CodecTestUtils.encodeBase64(token.getBytes(StandardCharsets.UTF_8)));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Test\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"rod\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo(\"äöü\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenAndFilterCharsetMatchNonDefaultThenAuthenticated() throws Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tUsernamePasswordAuthenticationToken rodRequest = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\",\n\t\t\t\t\"äöü\");\n\t\trodRequest.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));\n\t\tAuthentication rod = UsernamePasswordAuthenticationToken.authenticated(\"rod\", \"äöü\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_1\"));\n\t\tthis.manager = mock(AuthenticationManager.class);\n\t\tgiven(this.manager.authenticate(rodRequest)).willReturn(rod);\n\t\tgiven(this.manager.authenticate(not(eq(rodRequest)))).willThrow(new BadCredentialsException(\"\"));\n\t\tthis.filter = new BasicAuthenticationFilter(this.manager, new BasicAuthenticationEntryPoint());\n\t\tthis.filter.setCredentialsCharset(\"ISO-8859-1\");\n\t\tString token = \"rod:äöü\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\",\n\t\t\t\t\"Basic \" + CodecTestUtils.encodeBase64(token.getBytes(StandardCharsets.ISO_8859_1)));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Test\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"rod\");\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo(\"äöü\");\n\t\tassertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenTokenAndFilterCharsetDoNotMatchThenUnauthorized() throws Exception {\n\t\tSecurityContextHolder.clearContext();\n\t\tUsernamePasswordAuthenticationToken rodRequest = UsernamePasswordAuthenticationToken.unauthenticated(\"rod\",\n\t\t\t\t\"äöü\");\n\t\trodRequest.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));\n\t\tAuthentication rod = UsernamePasswordAuthenticationToken.authenticated(\"rod\", \"äöü\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_1\"));\n\t\tthis.manager = mock(AuthenticationManager.class);\n\t\tgiven(this.manager.authenticate(rodRequest)).willReturn(rod);\n\t\tgiven(this.manager.authenticate(not(eq(rodRequest)))).willThrow(new BadCredentialsException(\"\"));\n\t\tthis.filter = new BasicAuthenticationFilter(this.manager, new BasicAuthenticationEntryPoint());\n\t\tthis.filter.setCredentialsCharset(\"ISO-8859-1\");\n\t\tString token = \"rod:äöü\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\",\n\t\t\t\t\"Basic \" + CodecTestUtils.encodeBase64(token.getBytes(StandardCharsets.UTF_8)));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Test\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\tassertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);\n\t\tverify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void requestWhenEmptyBasicAuthorizationHeaderTokenThenUnauthorized() throws Exception {\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \");\n\t\trequest.setSession(new MockHttpSession());\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\tverify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void requestWhenSecurityContextRepository() throws Exception {\n\t\tArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tthis.filter.setSecurityContextRepository(securityContextRepository);\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = get(\"/some_file.html\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\t// Test\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tFilterChain chain = mock(FilterChain.class);\n\t\tthis.filter.doFilter(request, response, chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"rod\");\n\t\tverify(securityContextRepository).saveContext(contextArg.capture(), eq(request), eq(response));\n\t\tassertThat(contextArg.getValue().getAuthentication().getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUsernameDoesNotChangeThenAuthenticationIsNotRequired() throws Exception {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();\n\t\tSecurityContext securityContext = securityContextHolderStrategy.createEmptyContext();\n\t\tAuthentication authentication = UsernamePasswordAuthenticationToken.authenticated(\"rod\", \"koala\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"USER\"));\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tsecurityContextHolderStrategy.setContext(securityContext);\n\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(200);\n\n\t\tverify(this.manager, never()).authenticate(any(Authentication.class));\n\t\tverify(filterChain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tverifyNoMoreInteractions(this.manager, filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUsernameChangesThenAuthenticationIsRequired() throws Exception {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();\n\t\tSecurityContext securityContext = securityContextHolderStrategy.createEmptyContext();\n\t\tAuthentication authentication = UsernamePasswordAuthenticationToken.authenticated(\"user\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"USER\"));\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tsecurityContextHolderStrategy.setContext(securityContext);\n\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(200);\n\n\t\tArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.manager).authenticate(authenticationCaptor.capture());\n\t\tverify(filterChain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tverifyNoMoreInteractions(this.manager, filterChain);\n\n\t\tAuthentication authenticationRequest = authenticationCaptor.getValue();\n\t\tassertThat(authenticationRequest).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(authenticationRequest.getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenUsernameChangesAndNotUsernamePasswordAuthenticationTokenThenAuthenticationIsRequired()\n\t\t\tthrows Exception {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();\n\t\tSecurityContext securityContext = securityContextHolderStrategy.createEmptyContext();\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"user\", \"password\", \"USER\");\n\t\tsecurityContext.setAuthentication(authentication);\n\t\tsecurityContextHolderStrategy.setContext(securityContext);\n\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(200);\n\n\t\tArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.manager).authenticate(authenticationCaptor.capture());\n\t\tverify(filterChain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tverifyNoMoreInteractions(this.manager, filterChain);\n\n\t\tAuthentication authenticationRequest = authenticationCaptor.getValue();\n\t\tassertThat(authenticationRequest).isInstanceOf(UsernamePasswordAuthenticationToken.class);\n\t\tassertThat(authenticationRequest.getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterThatIgnoresRequestThenIgnores() throws Exception {\n\t\tthis.filter.setAuthenticationConverter(new TestAuthenticationConverter());\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = get(\"/ignored\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(200);\n\n\t\tverify(this.manager, never()).authenticate(any(Authentication.class));\n\t\tverify(filterChain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tverifyNoMoreInteractions(this.manager, filterChain);\n\t}\n\n\t@Test\n\tvoid doFilterWhenAuthenticatedThenCombinesAuthorities() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + CodecTestUtils.encodeBase64(\"a:b\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationManager manager = mock(AuthenticationManager.class);\n\t\tgiven(manager.authenticate(any())).willReturn(new TestingAuthenticationToken(\"username\", \"password\", \"TEST\"));\n\t\tBasicAuthenticationFilter filter = new BasicAuthenticationFilter(manager);\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication.getAuthorities()).extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactlyInAnyOrder(ROLE_EXISTING, \"TEST\");\n\t}\n\n\t@Test\n\tvoid doFilterWhenDefaultThenMfaDisabled() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + CodecTestUtils.encodeBase64(\"a:b\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationManager manager = mock(AuthenticationManager.class);\n\t\tTestingAuthenticationToken newAuthn = new TestingAuthenticationToken(\"username\", \"password\", \"TEST\");\n\t\tgiven(manager.authenticate(any())).willReturn(newAuthn);\n\t\tBasicAuthenticationFilter filter = new BasicAuthenticationFilter(manager);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isEqualTo(newAuthn);\n\t}\n\n\t// gh-18112\n\t@Test\n\tvoid doFilterWhenDifferentPrincipalThenDoesNotCombine() throws Exception {\n\t\tString ROLE_EXISTING = \"ROLE_EXISTING\";\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tROLE_EXISTING);\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + CodecTestUtils.encodeBase64(\"a:b\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationManager manager = mock(AuthenticationManager.class);\n\t\tTestingAuthenticationToken newAuthn = new TestingAuthenticationToken(existingAuthn.getName() + \"different\",\n\t\t\t\t\"password\", \"TEST\");\n\t\tgiven(manager.authenticate(any())).willReturn(newAuthn);\n\t\tBasicAuthenticationFilter filter = new BasicAuthenticationFilter(manager);\n\t\tfilter.setMfaEnabled(true);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(authentication).isEqualTo(newAuthn);\n\t}\n\n\t/**\n\t * This is critical to avoid adding duplicate GrantedAuthority instances with the\n\t * same' authority when the issuedAt is too old and a new instance is requested.\n\t * @throws Exception\n\t */\n\t@Test\n\tvoid doFilterWhenDefaultEqualsGrantedAuthorityThenNoDuplicates() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tnew DefaultEqualsGrantedAuthority());\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + CodecTestUtils.encodeBase64(\"a:b\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationManager manager = mock(AuthenticationManager.class);\n\t\tgiven(manager.authenticate(any()))\n\t\t\t.willReturn(new TestingAuthenticationToken(\"username\", \"password\", new DefaultEqualsGrantedAuthority()));\n\t\tBasicAuthenticationFilter filter = new BasicAuthenticationFilter(manager);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tassertThat(new ArrayList<GrantedAuthority>(authentication.getAuthorities()))\n\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactly(DefaultEqualsGrantedAuthority.AUTHORITY);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\", \"FACTORONE\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(HttpHeaders.AUTHORIZATION, \"Basic \" + CodecTestUtils.encodeBase64(\"a:b\"));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tAuthenticationManager manager = mock(AuthenticationManager.class);\n\t\tgiven(manager.authenticate(any()))\n\t\t\t.willReturn(new NonBuildableAuthenticationToken(\"username\", \"password\", \"FACTORTWO\"));\n\t\tBasicAuthenticationFilter filter = new BasicAuthenticationFilter(manager);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tAuthentication authentication = SecurityContextHolder.getContext().getAuthentication();\n\t\tSecurityAssertions.assertThat(authentication)\n\t\t\t.authorities()\n\t\t\t.extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactly(\"FACTORTWO\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomAuthenticationConverterRequestThenAuthenticate() throws Exception {\n\t\tthis.filter.setAuthenticationConverter(new TestAuthenticationConverter());\n\t\tString token = \"rod:koala\";\n\t\tMockHttpServletRequest request = get(\"/ok\").build();\n\t\trequest.addHeader(\"Authorization\", \"Basic \" + CodecTestUtils.encodeBase64(token));\n\t\tFilterChain filterChain = mock(FilterChain.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tthis.filter.doFilter(request, response, filterChain);\n\t\tassertThat(response.getStatus()).isEqualTo(200);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo(\"rod\");\n\t}\n\n\t@Test\n\tpublic void setAuthenticationConverterWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationConverter(null));\n\t}\n\n\tstatic class TestAuthenticationConverter implements AuthenticationConverter {\n\n\t\tprivate final RequestMatcher matcher = pathPattern(\"/ignored\");\n\n\t\tprivate final BasicAuthenticationConverter delegate = new BasicAuthenticationConverter();\n\n\t\t@Override\n\t\tpublic Authentication convert(HttpServletRequest request) {\n\t\t\tif (this.matcher.matches(request)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn this.delegate.convert(request);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthUtilsTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link org.springframework.security.util.StringSplitUtils}.\n *\n * @author Ben Alex\n */\npublic class DigestAuthUtilsTests {\n\n\t@Test\n\tpublic void testSplitEachArrayElementAndCreateMapNormalOperation() {\n\t\t// note it ignores malformed entries (ie those without an equals sign)\n\t\tString unsplit = \"username=\\\"rod\\\", invalidEntryThatHasNoEqualsSign, realm=\\\"Contacts Realm\\\", nonce=\\\"MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ==\\\", uri=\\\"/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4\\\", response=\\\"38644211cf9ac3da63ab639807e2baff\\\", qop=auth, nc=00000004, cnonce=\\\"2b8d329a8571b99a\\\"\";\n\t\tString[] headerEntries = StringUtils.commaDelimitedListToStringArray(unsplit);\n\t\tMap<String, String> headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, \"=\", \"\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"username\", \"rod\");\n\t\tassertThat(headerMap).containsEntry(\"realm\", \"Contacts Realm\");\n\t\tassertThat(headerMap).containsEntry(\"nonce\",\n\t\t\t\t\"MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ==\");\n\t\tassertThat(headerMap).containsEntry(\"uri\",\n\t\t\t\t\"/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4\");\n\t\tassertThat(headerMap).containsEntry(\"response\", \"38644211cf9ac3da63ab639807e2baff\");\n\t\tassertThat(headerMap).containsEntry(\"qop\", \"auth\");\n\t\tassertThat(headerMap).containsEntry(\"nc\", \"00000004\");\n\t\tassertThat(headerMap).containsEntry(\"cnonce\", \"2b8d329a8571b99a\");\n\t\tassertThat(headerMap).hasSize(8);\n\t}\n\n\t@Test\n\tpublic void testSplitEachArrayElementAndCreateMapRespectsInstructionNotToRemoveCharacters() {\n\t\tString unsplit = \"username=\\\"rod\\\", realm=\\\"Contacts Realm\\\", nonce=\\\"MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ==\\\", uri=\\\"/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4\\\", response=\\\"38644211cf9ac3da63ab639807e2baff\\\", qop=auth, nc=00000004, cnonce=\\\"2b8d329a8571b99a\\\"\";\n\t\tString[] headerEntries = StringUtils.commaDelimitedListToStringArray(unsplit);\n\t\tMap<String, String> headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, \"=\", null);\n\t\tassertThat(headerMap).containsEntry(\"username\", \"\\\"rod\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"realm\", \"\\\"Contacts Realm\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"nonce\",\n\t\t\t\t\"\\\"MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ==\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"uri\",\n\t\t\t\t\"\\\"/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"response\", \"\\\"38644211cf9ac3da63ab639807e2baff\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"qop\", \"auth\");\n\t\tassertThat(headerMap).containsEntry(\"nc\", \"00000004\");\n\t\tassertThat(headerMap).containsEntry(\"cnonce\", \"\\\"2b8d329a8571b99a\\\"\");\n\t\tassertThat(headerMap).hasSize(8);\n\t}\n\n\t@Test\n\tpublic void testSplitEachArrayElementAndCreateMapReturnsNullIfArrayEmptyOrNull() {\n\t\tassertThat(DigestAuthUtils.splitEachArrayElementAndCreateMap(null, \"=\", \"\\\"\")).isNull();\n\t\tassertThat(DigestAuthUtils.splitEachArrayElementAndCreateMap(new String[] {}, \"=\", \"\\\"\")).isNull();\n\t}\n\n\t@Test\n\tpublic void testSplitNormalOperation() {\n\t\tString unsplit = \"username=\\\"rod==\\\"\";\n\t\tassertThat(DigestAuthUtils.split(unsplit, \"=\")[0]).isEqualTo(\"username\");\n\t\tassertThat(DigestAuthUtils.split(unsplit, \"=\")[1]).isEqualTo(\"\\\"rod==\\\"\"); // should\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// not\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// remove\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// quotes\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// or\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// extra\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// equals\n\t}\n\n\t@Test\n\tpublic void testSplitRejectsNullsAndIncorrectLengthStrings() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> DigestAuthUtils.split(null, \"=\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> DigestAuthUtils.split(\"\", \"=\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> DigestAuthUtils.split(\"sdch=dfgf\", null));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> DigestAuthUtils.split(\"fvfv=dcdc\", \"\"));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> DigestAuthUtils.split(\"dfdc=dcdc\", \"BIGGER_THAN_ONE_CHARACTER\"));\n\t}\n\n\t@Test\n\tpublic void testSplitWorksWithDifferentDelimiters() {\n\t\tassertThat(DigestAuthUtils.split(\"18/rod\", \"/\")).hasSize(2);\n\t\tassertThat(DigestAuthUtils.split(\"18/rod\", \"!\")).isNull();\n\t\t// only guarantees to split at FIRST delimiter, not EACH delimiter\n\t\tassertThat(DigestAuthUtils.split(\"18|rod|foo|bar\", \"|\")).hasSize(2);\n\t}\n\n\tpublic void testAuthorizationHeaderWithCommasIsSplitCorrectly() {\n\t\tString header = \"Digest username=\\\"hamilton,bob\\\", realm=\\\"bobs,ok,realm\\\", nonce=\\\"the,nonce\\\", \"\n\t\t\t\t+ \"uri=\\\"the,Uri\\\", response=\\\"the,response,Digest\\\", qop=theqop, nc=thenc, cnonce=\\\"the,cnonce\\\"\";\n\t\tString[] parts = DigestAuthUtils.splitIgnoringQuotes(header, ',');\n\t\tassertThat(parts).hasSize(8);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.test.web.CodecTestUtils;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests {@link DigestAuthenticationEntryPoint}.\n *\n * @author Ben Alex\n */\npublic class DigestAuthenticationEntryPointTests {\n\n\tprivate void checkNonceValid(String nonce) {\n\t\t// Check the nonce seems to be generated correctly\n\t\t// format of nonce is:\n\t\t// base64(expirationTime + \":\" + md5Hex(expirationTime + \":\" + key))\n\t\tassertThat(CodecTestUtils.isBase64(nonce.getBytes())).isTrue();\n\t\tString decodedNonce = CodecTestUtils.decodeBase64(nonce);\n\t\tString[] nonceTokens = StringUtils.delimitedListToStringArray(decodedNonce, \":\");\n\t\tassertThat(nonceTokens).hasSize(2);\n\t\tString expectedNonceSignature = CodecTestUtils.md5Hex(nonceTokens[0] + \":\" + \"key\");\n\t\tassertThat(nonceTokens[1]).isEqualTo(expectedNonceSignature);\n\t}\n\n\t@Test\n\tpublic void testDetectsMissingKey() throws Exception {\n\t\tDigestAuthenticationEntryPoint ep = new DigestAuthenticationEntryPoint();\n\t\tep.setRealmName(\"realm\");\n\t\tassertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet).withMessage(\"key must be specified\");\n\t}\n\n\t@Test\n\tpublic void testDetectsMissingRealmName() throws Exception {\n\t\tDigestAuthenticationEntryPoint ep = new DigestAuthenticationEntryPoint();\n\t\tep.setKey(\"dcdc\");\n\t\tep.setNonceValiditySeconds(12);\n\t\tassertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet)\n\t\t\t.withMessage(\"realmName must be specified\");\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tDigestAuthenticationEntryPoint ep = new DigestAuthenticationEntryPoint();\n\t\tassertThat(ep.getNonceValiditySeconds()).isEqualTo(300); // 5 mins default\n\t\tep.setRealmName(\"realm\");\n\t\tassertThat(ep.getRealmName()).isEqualTo(\"realm\");\n\t\tep.setKey(\"dcdc\");\n\t\tassertThat(ep.getKey()).isEqualTo(\"dcdc\");\n\t\tep.setNonceValiditySeconds(12);\n\t\tassertThat(ep.getNonceValiditySeconds()).isEqualTo(12);\n\t}\n\n\t@Test\n\tpublic void testNormalOperation() throws Exception {\n\t\tDigestAuthenticationEntryPoint ep = new DigestAuthenticationEntryPoint();\n\t\tep.setRealmName(\"hello\");\n\t\tep.setKey(\"key\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/some_path\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.afterPropertiesSet();\n\t\tep.commence(request, response, new DisabledException(\"foobar\"));\n\t\t// Check response is properly formed\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\").toString()).startsWith(\"Digest \");\n\t\t// Break up response header\n\t\tString header = response.getHeader(\"WWW-Authenticate\").toString().substring(7);\n\t\tString[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);\n\t\tMap<String, String> headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, \"=\", \"\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"realm\", \"hello\");\n\t\tassertThat(headerMap).containsEntry(\"qop\", \"auth\");\n\t\tassertThat(headerMap.get(\"stale\")).isNull();\n\t\tcheckNonceValid(headerMap.get(\"nonce\"));\n\t}\n\n\t@Test\n\tpublic void testOperationIfDueToStaleNonce() throws Exception {\n\t\tDigestAuthenticationEntryPoint ep = new DigestAuthenticationEntryPoint();\n\t\tep.setRealmName(\"hello\");\n\t\tep.setKey(\"key\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/some_path\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tep.afterPropertiesSet();\n\t\tep.commence(request, response, new NonceExpiredException(\"expired nonce\"));\n\t\t// Check response is properly formed\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(response.getHeader(\"WWW-Authenticate\").toString()).startsWith(\"Digest \");\n\t\t// Break up response header\n\t\tString header = response.getHeader(\"WWW-Authenticate\").toString().substring(7);\n\t\tString[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);\n\t\tMap<String, String> headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, \"=\", \"\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"realm\", \"hello\");\n\t\tassertThat(headerMap).containsEntry(\"qop\", \"auth\");\n\t\tassertThat(headerMap).containsEntry(\"stale\", \"true\");\n\t\tcheckNonceValid(headerMap.get(\"nonce\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.authentication.www;\n\nimport java.io.IOException;\nimport java.util.Map;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.MockSecurityContextHolderStrategy;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.cache.NullUserCache;\nimport org.springframework.security.test.web.CodecTestUtils;\nimport org.springframework.security.web.context.RequestAttributeSecurityContextRepository;\nimport org.springframework.security.web.context.SecurityContextRepository;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * Tests {@link DigestAuthenticationFilter}.\n *\n * @author Ben Alex\n * @author Luke Taylor\n */\npublic class DigestAuthenticationFilterTests {\n\n\tprivate static final String NC = \"00000002\";\n\n\tprivate static final String CNONCE = \"c822c727a648aba7\";\n\n\tprivate static final String REALM = \"The Actual, Correct Realm Name\";\n\n\tprivate static final String KEY = \"springsecurity\";\n\n\tprivate static final String QOP = \"auth\";\n\n\tprivate static final String USERNAME = \"rod,ok\";\n\n\tprivate static final String PASSWORD = \"koala\";\n\n\tprivate static final String REQUEST_URI = \"/some_file.html\";\n\n\t/**\n\t * A standard valid nonce with a validity period of 60 seconds\n\t */\n\tprivate static final String NONCE = generateNonce(60);\n\n\t// private ApplicationContext ctx;\n\tprivate DigestAuthenticationFilter filter;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate String createAuthorizationHeader(String username, String realm, String nonce, String uri,\n\t\t\tString responseDigest, String qop, String nc, String cnonce) {\n\t\treturn \"Digest username=\\\"\" + username + \"\\\", realm=\\\"\" + realm + \"\\\", nonce=\\\"\" + nonce + \"\\\", uri=\\\"\" + uri\n\t\t\t\t+ \"\\\", response=\\\"\" + responseDigest + \"\\\", qop=\" + qop + \", nc=\" + nc + \", cnonce=\\\"\" + cnonce + \"\\\"\";\n\t}\n\n\tprivate MockHttpServletResponse executeFilterInContainerSimulator(Filter filter, final ServletRequest request,\n\t\t\tfinal boolean expectChainToProceed) throws ServletException, IOException {\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfinal FilterChain chain = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain, times(expectChainToProceed ? 1 : 0)).doFilter(request, response);\n\t\treturn response;\n\t}\n\n\tprivate static String generateNonce(int validitySeconds) {\n\t\treturn generateNonce(validitySeconds, KEY);\n\t}\n\n\tprivate static String generateNonce(int validitySeconds, String key) {\n\t\tlong expiryTime = System.currentTimeMillis() + (validitySeconds * 1000);\n\t\tString signatureValue = CodecTestUtils.md5Hex(expiryTime + \":\" + key);\n\t\tString nonceValue = expiryTime + \":\" + signatureValue;\n\t\treturn CodecTestUtils.encodeBase64(nonceValue);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tSecurityContextHolder.clearContext();\n\t\t// Create User Details Service\n\t\tUserDetailsService uds = (username) -> new User(\"rod,ok\", \"koala\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tDigestAuthenticationEntryPoint ep = new DigestAuthenticationEntryPoint();\n\t\tep.setRealmName(REALM);\n\t\tep.setKey(KEY);\n\t\tthis.filter = new DigestAuthenticationFilter();\n\t\tthis.filter.setUserDetailsService(uds);\n\t\tthis.filter.setAuthenticationEntryPoint(ep);\n\t\tthis.request = get(REQUEST_URI).build();\n\t}\n\n\t@Test\n\tpublic void testExpiredNonceReturnsForbiddenWithStaleHeader() throws Exception {\n\t\tString nonce = generateNonce(0);\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, nonce, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tThread.sleep(1000); // ensures token expired\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tString header = response.getHeader(\"WWW-Authenticate\").toString().substring(7);\n\t\tString[] headerEntries = StringUtils.commaDelimitedListToStringArray(header);\n\t\tMap<String, String> headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, \"=\", \"\\\"\");\n\t\tassertThat(headerMap).containsEntry(\"stale\", \"true\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNonceHasBadKeyThenGeneratesError() throws Exception {\n\t\tString badNonce = generateNonce(60, \"badkey\");\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, badNonce, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, badNonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void testFilterIgnoresRequestsContainingNoAuthorizationHeader() throws Exception {\n\t\texecuteFilterInContainerSimulator(this.filter, this.request, true);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void testGettersSetters() {\n\t\tDigestAuthenticationFilter filter = new DigestAuthenticationFilter();\n\t\tfilter.setUserDetailsService(mock(UserDetailsService.class));\n\t\tassertThat(filter.getUserDetailsService() != null).isTrue();\n\t\tfilter.setAuthenticationEntryPoint(new DigestAuthenticationEntryPoint());\n\t\tassertThat(filter.getAuthenticationEntryPoint() != null).isTrue();\n\t\tfilter.setUserCache(null);\n\t\tassertThat(filter.getUserCache()).isNull();\n\t\tfilter.setUserCache(new NullUserCache());\n\t\tassertThat(filter.getUserCache()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void testInvalidDigestAuthorizationTokenGeneratesError() throws Exception {\n\t\tString token = \"NOT_A_VALID_TOKEN_AS_MISSING_COLON\";\n\t\tthis.request.addHeader(\"Authorization\", \"Digest \" + CodecTestUtils.encodeBase64(token));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void testMalformedHeaderReturnsForbidden() throws Exception {\n\t\tthis.request.addHeader(\"Authorization\", \"Digest scsdcsdc\");\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void testNonBase64EncodedNonceReturnsForbidden() throws Exception {\n\t\tString nonce = \"NOT_BASE_64_ENCODED\";\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, nonce, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void testNonceWithIncorrectSignatureForNumericFieldReturnsForbidden() throws Exception {\n\t\tString nonce = CodecTestUtils.encodeBase64(\"123456:incorrectStringPassword\");\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, nonce, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void testNonceWithNonNumericFirstElementReturnsForbidden() throws Exception {\n\t\tString nonce = CodecTestUtils.encodeBase64(\"hello:ignoredSecondElement\");\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, nonce, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void testNonceWithoutTwoColonSeparatedElementsReturnsForbidden() throws Exception {\n\t\tString nonce = CodecTestUtils.encodeBase64(\"a base 64 string without a colon\");\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, nonce, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, nonce, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWhenPasswordIsAlreadyEncoded() throws Exception {\n\t\tString encodedPassword = DigestAuthUtils.encodePasswordInA1Format(USERNAME, REALM, PASSWORD);\n\t\tString responseDigest = DigestAuthUtils.generateDigest(true, USERNAME, REALM, encodedPassword, \"GET\",\n\t\t\t\tREQUEST_URI, QOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\texecuteFilterInContainerSimulator(this.filter, this.request, true);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername())\n\t\t\t.isEqualTo(USERNAME);\n\t\tassertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWhenPasswordNotAlreadyEncoded() throws Exception {\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\texecuteFilterInContainerSimulator(this.filter, this.request, true);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername())\n\t\t\t.isEqualTo(USERNAME);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().isAuthenticated()).isFalse();\n\t\tassertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void testNormalOperationWhenPasswordNotAlreadyEncodedAndWithoutReAuthentication() throws Exception {\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tthis.filter.setCreateAuthenticatedToken(true);\n\t\texecuteFilterInContainerSimulator(this.filter, this.request, true);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername())\n\t\t\t.isEqualTo(USERNAME);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().isAuthenticated()).isTrue();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getAuthorities())\n\t\t\t.isEqualTo(AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tassertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void otherAuthorizationSchemeIsIgnored() throws Exception {\n\t\tthis.request.addHeader(\"Authorization\", \"SOME_OTHER_AUTHENTICATION_SCHEME\");\n\t\texecuteFilterInContainerSimulator(this.filter, this.request, true);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void startupDetectsMissingAuthenticationEntryPoint() {\n\t\tDigestAuthenticationFilter filter = new DigestAuthenticationFilter();\n\t\tfilter.setUserDetailsService(mock(UserDetailsService.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void startupDetectsMissingUserDetailsService() {\n\t\tDigestAuthenticationFilter filter = new DigestAuthenticationFilter();\n\t\tfilter.setAuthenticationEntryPoint(new DigestAuthenticationEntryPoint());\n\t\tassertThatIllegalArgumentException().isThrownBy(filter::afterPropertiesSet);\n\t}\n\n\t@Test\n\tpublic void authenticateUsesCustomSecurityContextHolderStrategy() throws Exception {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = spy(new MockSecurityContextHolderStrategy());\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tthis.filter.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\texecuteFilterInContainerSimulator(this.filter, this.request, true);\n\t\tverify(securityContextHolderStrategy).setContext(any());\n\t}\n\n\t@Test\n\tpublic void successfulLoginThenFailedLoginResultsInSessionLosingToken() throws Exception {\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\texecuteFilterInContainerSimulator(this.filter, this.request, true);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\t// Now retry, giving an invalid nonce\n\t\tresponseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, \"WRONG_PASSWORD\", \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\t// Check we lost our previous authentication\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void wrongCnonceBasedOnDigestReturnsForbidden() throws Exception {\n\t\tString cnonce = \"NOT_SAME_AS_USED_FOR_DIGEST_COMPUTATION\";\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, \"DIFFERENT_CNONCE\");\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, cnonce));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void wrongDigestReturnsForbidden() throws Exception {\n\t\tString password = \"WRONG_PASSWORD\";\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, password, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void wrongRealmReturnsForbidden() throws Exception {\n\t\tString realm = \"WRONG_REALM\";\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, realm, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, realm, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t@Test\n\tpublic void wrongUsernameReturnsForbidden() throws Exception {\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, \"NOT_A_KNOWN_USER\", REALM, PASSWORD, \"GET\",\n\t\t\t\tREQUEST_URI, QOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, false);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tassertThat(response.getStatus()).isEqualTo(401);\n\t}\n\n\t// SEC-3108\n\t@Test\n\tpublic void authenticationCreatesEmptyContext() throws Exception {\n\t\tSecurityContext existingContext = SecurityContextHolder.createEmptyContext();\n\t\tTestingAuthenticationToken existingAuthentication = new TestingAuthenticationToken(\"existingauthenitcated\",\n\t\t\t\t\"pass\", \"ROLE_USER\");\n\t\texistingContext.setAuthentication(existingAuthentication);\n\t\tSecurityContextHolder.setContext(existingContext);\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tthis.filter.setCreateAuthenticatedToken(true);\n\t\texecuteFilterInContainerSimulator(this.filter, this.request, true);\n\t\tassertThat(existingAuthentication).isSameAs(existingContext.getAuthentication());\n\t}\n\n\t@Test\n\tpublic void testSecurityContextRepository() throws Exception {\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tString responseDigest = DigestAuthUtils.generateDigest(false, USERNAME, REALM, PASSWORD, \"GET\", REQUEST_URI,\n\t\t\t\tQOP, NONCE, NC, CNONCE);\n\t\tthis.request.addHeader(\"Authorization\",\n\t\t\t\tcreateAuthorizationHeader(USERNAME, REALM, NONCE, REQUEST_URI, responseDigest, QOP, NC, CNONCE));\n\t\tthis.filter.setSecurityContextRepository(securityContextRepository);\n\t\tthis.filter.setCreateAuthenticatedToken(true);\n\t\tMockHttpServletResponse response = executeFilterInContainerSimulator(this.filter, this.request, true);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();\n\t\tassertThat(((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername())\n\t\t\t.isEqualTo(USERNAME);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().isAuthenticated()).isTrue();\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication().getAuthorities())\n\t\t\t.isEqualTo(AuthorityUtils.createAuthorityList(\"ROLE_ONE\", \"ROLE_TWO\"));\n\t\tverify(securityContextRepository).saveContext(contextArg.capture(), eq(this.request), eq(response));\n\t\tassertThat(contextArg.getValue().getAuthentication().getName()).isEqualTo(USERNAME);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/bind/support/AuthenticationPrincipalArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.bind.support;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.bind.annotation.AuthenticationPrincipal;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rob Winch\n *\n */\n@SuppressWarnings(\"deprecation\")\npublic class AuthenticationPrincipalArgumentResolverTests {\n\n\tprivate Object expectedPrincipal;\n\n\tprivate AuthenticationPrincipalArgumentResolver resolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.resolver = new AuthenticationPrincipalArgumentResolver();\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void supportsParameterNoAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserNoAnnotation())).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsParameterAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserAnnotationObject())).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterCustomAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserCustomAnnotation())).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullAuthentication() throws Exception {\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null, null, null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullPrincipal() throws Exception {\n\t\tsetAuthenticationPrincipal(null);\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null, null, null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentString() throws Exception {\n\t\tsetAuthenticationPrincipal(\"john\");\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentPrincipalStringOnObject() throws Exception {\n\t\tsetAuthenticationPrincipal(\"john\");\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationObject(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentUserDetails() throws Exception {\n\t\tsetAuthenticationPrincipal(new User(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\")));\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationUserDetails(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomUserPrincipal() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationCustomUserPrincipal(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomAnnotation() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserCustomAnnotation(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null, null, null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentErrorOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThatExceptionOfType(ClassCastException.class)\n\t\t\t.isThrownBy(() -> this.resolver.resolveArgument(showUserAnnotationErrorOnInvalidType(), null, null, null));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomserErrorOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> this.resolver\n\t\t\t.resolveArgument(showUserAnnotationCurrentUserErrorOnInvalidType(), null, null, null));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentObject() throws Exception {\n\t\tsetAuthenticationPrincipal(new Object());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationObject(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\tprivate MethodParameter showUserNoAnnotation() {\n\t\treturn getMethodParameter(\"showUserNoAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationString() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationErrorOnInvalidType() {\n\t\treturn getMethodParameter(\"showUserAnnotationErrorOnInvalidType\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationCurrentUserErrorOnInvalidType() {\n\t\treturn getMethodParameter(\"showUserAnnotationCurrentUserErrorOnInvalidType\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationUserDetails() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", UserDetails.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationCustomUserPrincipal() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", CustomUserPrincipal.class);\n\t}\n\n\tprivate MethodParameter showUserCustomAnnotation() {\n\t\treturn getMethodParameter(\"showUserCustomAnnotation\", CustomUserPrincipal.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationObject() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", Object.class);\n\t}\n\n\tprivate MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {\n\t\tMethod method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);\n\t\treturn new MethodParameter(method, 0);\n\t}\n\n\tprivate void setAuthenticationPrincipal(Object principal) {\n\t\tthis.expectedPrincipal = principal;\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(this.expectedPrincipal, \"password\", \"ROLE_USER\"));\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\tstatic @interface CurrentUser {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal(errorOnInvalidType = true)\n\tstatic @interface CurrentUserErrorOnInvalidType {\n\n\t}\n\n\tpublic static class TestController {\n\n\t\tpublic void showUserNoAnnotation(String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotationErrorOnInvalidType(\n\t\t\t\t@AuthenticationPrincipal(errorOnInvalidType = true) String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotationCurrentUserErrorOnInvalidType(@CurrentUserErrorOnInvalidType String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal UserDetails user) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal CustomUserPrincipal user) {\n\t\t}\n\n\t\tpublic void showUserCustomAnnotation(@CurrentUser CustomUserPrincipal user) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal Object user) {\n\t\t}\n\n\t}\n\n\tprivate static class CustomUserPrincipal {\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.concurrent;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.MockSecurityContextHolderStrategy;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.session.SessionInformation;\nimport org.springframework.security.core.session.SessionRegistry;\nimport org.springframework.security.core.session.SessionRegistryImpl;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;\nimport org.springframework.security.web.session.ConcurrentSessionFilter;\nimport org.springframework.security.web.session.SessionInformationExpiredStrategy;\nimport org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link ConcurrentSessionFilter}.\n *\n * @author Ben Alex\n * @author Luke Taylor\n * @author Onur Kagan Ozcan\n * @author Gengwu Zhao\n */\npublic class ConcurrentSessionFilterTests {\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorSessionRegistryWhenSessionRegistryNullThenExceptionThrown() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ConcurrentSessionFilter(null));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void constructorSessionRegistryExpiresUrlWhenInvalidUrlThenExceptionThrown() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ConcurrentSessionFilter(new SessionRegistryImpl(), \"oops\"));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void constructorSessionRegistryExpiresUrlWhenSessionRegistryNullThenExceptionThrown() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ConcurrentSessionFilter(null, \"/expired\"));\n\t}\n\n\t@Test\n\tpublic void constructorSessionRegistrySessionInformationExpiredStrategyWhenStrategyIsNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new ConcurrentSessionFilter(new SessionRegistryImpl(), (SessionInformationExpiredStrategy) null));\n\t}\n\n\t@Test\n\tpublic void detectsExpiredSessions() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSessionRegistry registry = new SessionRegistryImpl();\n\t\tregistry.registerNewSession(session.getId(), \"principal\");\n\t\tregistry.getSessionInformation(session.getId()).expireNow();\n\t\t// Setup our test fixture and registry to want this session to be expired\n\t\tSimpleRedirectSessionInformationExpiredStrategy expiredSessionStrategy = new SimpleRedirectSessionInformationExpiredStrategy(\n\t\t\t\t\"/expired.jsp\");\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry, expiredSessionStrategy);\n\t\tfilter.setLogoutHandlers(new LogoutHandler[] { new SecurityContextLogoutHandler() });\n\t\tfilter.afterPropertiesSet();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, fc);\n\t\t// Expect that the filter chain will not be invoked, as we redirect to expiredUrl\n\t\tverifyNoMoreInteractions(fc);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/expired.jsp\");\n\t}\n\n\t// As above, but with no expiredUrl set.\n\t@Test\n\tpublic void returnsExpectedMessageWhenNoExpiredUrlSet() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSessionRegistry registry = new SessionRegistryImpl();\n\t\tregistry.registerNewSession(session.getId(), \"principal\");\n\t\tregistry.getSessionInformation(session.getId()).expireNow();\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry);\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, fc);\n\t\tverifyNoMoreInteractions(fc);\n\t\tassertThat(response.getContentAsString())\n\t\t\t.isEqualTo(\"This session has been expired (possibly due to multiple concurrent logins being \"\n\t\t\t\t\t+ \"attempted as the same user).\");\n\t}\n\n\t@Test\n\tpublic void detectsMissingSessionRegistry() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ConcurrentSessionFilter(null));\n\t}\n\n\t@Test\n\tpublic void lastRequestTimeUpdatesCorrectly() throws Exception {\n\t\t// Setup our HTTP request\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\t// Setup our test fixture\n\t\tSessionRegistry registry = new SessionRegistryImpl();\n\t\tregistry.registerNewSession(session.getId(), \"principal\");\n\t\tSimpleRedirectSessionInformationExpiredStrategy expiredSessionStrategy = new SimpleRedirectSessionInformationExpiredStrategy(\n\t\t\t\t\"/expired.jsp\");\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry, expiredSessionStrategy);\n\t\tDate lastRequest = registry.getSessionInformation(session.getId()).getLastRequest();\n\t\tThread.sleep(1000);\n\t\tfilter.doFilter(request, response, fc);\n\t\tverify(fc).doFilter(request, response);\n\t\tassertThat(registry.getSessionInformation(session.getId()).getLastRequest().after(lastRequest)).isTrue();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoSessionThenChainIsContinued() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRedirectStrategy redirect = mock(RedirectStrategy.class);\n\t\tString expiredUrl = \"/expired\";\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(mockSessionRegistry(), expiredUrl);\n\t\tfilter.setRedirectStrategy(redirect);\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(chain.getRequest()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoSessionInformationThenChainIsContinued() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(new MockHttpSession());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRedirectStrategy redirect = mock(RedirectStrategy.class);\n\t\tSessionRegistry registry = mock(SessionRegistry.class);\n\t\tString expiredUrl = \"/expired\";\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry, expiredUrl);\n\t\tfilter.setRedirectStrategy(redirect);\n\t\tMockFilterChain chain = new MockFilterChain();\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(chain.getRequest()).isNotNull();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomRedirectStrategyThenCustomRedirectStrategyUsed() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRedirectStrategy redirect = mock(RedirectStrategy.class);\n\t\tString expiredUrl = \"/expired\";\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(mockSessionRegistry(), expiredUrl);\n\t\tfilter.setRedirectStrategy(redirect);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tverify(redirect).sendRedirect(request, response, expiredUrl);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenOverrideThenCustomRedirectStrategyUsed() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRedirectStrategy redirect = mock(RedirectStrategy.class);\n\t\tfinal String expiredUrl = \"/expired\";\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(mockSessionRegistry(),\n\t\t\t\texpiredUrl + \"will-be-overrridden\") {\n\t\t\t@Override\n\t\t\tprotected String determineExpiredUrl(HttpServletRequest request, SessionInformation info) {\n\t\t\t\treturn expiredUrl;\n\t\t\t}\n\t\t};\n\t\tfilter.setRedirectStrategy(redirect);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tverify(redirect).sendRedirect(request, response, expiredUrl);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenNoExpiredUrlThenResponseWritten() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(mockSessionRegistry());\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getContentAsString()).contains(\n\t\t\t\t\"This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).\");\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomLogoutHandlersThenHandlersUsed() throws Exception {\n\t\tLogoutHandler handler = mock(LogoutHandler.class);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(mockSessionRegistry());\n\t\tfilter.setLogoutHandlers(new LogoutHandler[] { handler });\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tverify(handler).logout(eq(request), eq(response), any());\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCustomSecurityContextHolderStrategyThenHandlersUsed() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpSession session = new MockHttpSession();\n\t\trequest.setSession(session);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(mockSessionRegistry());\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = spy(\n\t\t\t\tnew MockSecurityContextHolderStrategy(new TestingAuthenticationToken(\"user\", \"password\")));\n\t\tfilter.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tverify(securityContextHolderStrategy).getContext();\n\t}\n\n\t@Test\n\tpublic void setLogoutHandlersWhenNullThenThrowsException() {\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(new SessionRegistryImpl());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setLogoutHandlers((List<LogoutHandler>) null));\n\t}\n\n\t@Test\n\tpublic void setLogoutHandlersWhenEmptyThenThrowsException() {\n\t\tConcurrentSessionFilter filter = new ConcurrentSessionFilter(new SessionRegistryImpl());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setLogoutHandlers(new LogoutHandler[0]));\n\t}\n\n\tprivate SessionRegistry mockSessionRegistry() {\n\t\tSessionRegistry registry = mock(SessionRegistry.class);\n\t\tSessionInformation information = new SessionInformation(\"user\", \"sessionId\",\n\t\t\t\tnew Date(System.currentTimeMillis() - 1000));\n\t\tinformation.expireNow();\n\t\tgiven(registry.getSessionInformation(anyString())).willReturn(information);\n\t\treturn registry;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/AbstractSecurityWebApplicationInitializerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.EventListener;\nimport java.util.Set;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.FilterRegistration;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.SessionTrackingMode;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.web.session.HttpSessionEventPublisher;\nimport org.springframework.web.context.ContextLoaderListener;\nimport org.springframework.web.filter.DelegatingFilterProxy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willDoNothing;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @author Josh Cummings\n */\npublic class AbstractSecurityWebApplicationInitializerTests {\n\n\tprivate static final EnumSet<DispatcherType> DEFAULT_DISPATCH = EnumSet.of(DispatcherType.REQUEST,\n\t\t\tDispatcherType.ERROR, DispatcherType.ASYNC, DispatcherType.FORWARD, DispatcherType.INCLUDE);\n\n\t@Test\n\tpublic void onStartupWhenDefaultContextThenRegistersSpringSecurityFilterChain() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tnew AbstractSecurityWebApplicationInitializer() {\n\t\t}.onStartup(context);\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(registration).addMappingForUrlPatterns(DEFAULT_DISPATCH, false, \"/*\");\n\t\tverify(registration).setAsyncSupported(true);\n\t\tverifyNoAddListener(context);\n\t}\n\n\t@Test\n\tpublic void onStartupWhenConfigurationClassThenAddsContextLoaderListener() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tnew AbstractSecurityWebApplicationInitializer(MyRootConfiguration.class) {\n\t\t}.onStartup(context);\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(registration).addMappingForUrlPatterns(DEFAULT_DISPATCH, false, \"/*\");\n\t\tverify(registration).setAsyncSupported(true);\n\t\tverify(context).addListener(any(ContextLoaderListener.class));\n\t}\n\n\t@Test\n\tpublic void onStartupWhenEnableHttpSessionEventPublisherIsTrueThenAddsHttpSessionEventPublisher() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tnew AbstractSecurityWebApplicationInitializer() {\n\t\t\t@Override\n\t\t\tprotected boolean enableHttpSessionEventPublisher() {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}.onStartup(context);\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(registration).addMappingForUrlPatterns(DEFAULT_DISPATCH, false, \"/*\");\n\t\tverify(registration).setAsyncSupported(true);\n\t\tverify(context).addListener(HttpSessionEventPublisher.class.getName());\n\t}\n\n\t@Test\n\tpublic void onStartupWhenCustomSecurityDispatcherTypesThenUses() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tnew AbstractSecurityWebApplicationInitializer() {\n\t\t\t@Override\n\t\t\tprotected EnumSet<DispatcherType> getSecurityDispatcherTypes() {\n\t\t\t\treturn EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.FORWARD);\n\t\t\t}\n\t\t}.onStartup(context);\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(registration).addMappingForUrlPatterns(\n\t\t\t\tEnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.FORWARD), false, \"/*\");\n\t\tverify(registration).setAsyncSupported(true);\n\t\tverifyNoAddListener(context);\n\t}\n\n\t@Test\n\tpublic void onStartupWhenCustomDispatcherWebApplicationContextSuffixThenUses() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tnew AbstractSecurityWebApplicationInitializer() {\n\t\t\t@Override\n\t\t\tprotected String getDispatcherWebApplicationContextSuffix() {\n\t\t\t\treturn \"dispatcher\";\n\t\t\t}\n\t\t}.onStartup(context);\n\t\tDelegatingFilterProxy proxy = proxyCaptor.getValue();\n\t\tassertThat(proxy.getContextAttribute())\n\t\t\t.isEqualTo(\"org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher\");\n\t\tassertThat(proxy).hasFieldOrPropertyWithValue(\"targetBeanName\", \"springSecurityFilterChain\");\n\t\tverify(registration).addMappingForUrlPatterns(DEFAULT_DISPATCH, false, \"/*\");\n\t\tverify(registration).setAsyncSupported(true);\n\t\tverifyNoAddListener(context);\n\t}\n\n\t@Test\n\tpublic void onStartupWhenSpringSecurityFilterChainAlreadyRegisteredThenException() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tassertThatIllegalStateException().isThrownBy(() -> new AbstractSecurityWebApplicationInitializer() {\n\t\t}.onStartup(context))\n\t\t\t.withMessage(\"Duplicate Filter registration for 'springSecurityFilterChain'. \"\n\t\t\t\t\t+ \"Check to ensure the Filter is only configured once.\");\n\t}\n\n\t@Test\n\tpublic void onStartupWhenInsertFiltersThenInserted() {\n\t\tFilter filter1 = mock(Filter.class);\n\t\tFilter filter2 = mock(Filter.class);\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tgiven(context.addFilter(anyString(), eq(filter1))).willReturn(registration);\n\t\tgiven(context.addFilter(anyString(), eq(filter2))).willReturn(registration);\n\t\tnew AbstractSecurityWebApplicationInitializer() {\n\t\t\t@Override\n\t\t\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\t\t\t\tinsertFilters(context, filter1, filter2);\n\t\t\t}\n\t\t}.onStartup(context);\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(registration, times(3)).addMappingForUrlPatterns(DEFAULT_DISPATCH, false, \"/*\");\n\t\tverify(registration, times(3)).setAsyncSupported(true);\n\t\tverifyNoAddListener(context);\n\t\tverify(context).addFilter(anyString(), eq(filter1));\n\t\tverify(context).addFilter(anyString(), eq(filter2));\n\t}\n\n\t@Test\n\tpublic void onStartupWhenDuplicateFilterInsertedThenException() {\n\t\tFilter filter1 = mock(Filter.class);\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tassertThatIllegalStateException().isThrownBy(() -> new AbstractSecurityWebApplicationInitializer() {\n\n\t\t\t@Override\n\t\t\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\t\t\t\tinsertFilters(context, filter1);\n\t\t\t}\n\n\t\t}.onStartup(context))\n\t\t\t.withMessage(\n\t\t\t\t\t\"Duplicate Filter registration for 'object'. Check to ensure the Filter is only configured once.\");\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(registration).addMappingForUrlPatterns(DEFAULT_DISPATCH, false, \"/*\");\n\t\tverify(context).addFilter(anyString(), eq(filter1));\n\t}\n\n\t@Test\n\tpublic void onStartupWhenInsertFiltersEmptyThenException() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AbstractSecurityWebApplicationInitializer() {\n\n\t\t\t@Override\n\t\t\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\t\t\t\tinsertFilters(context);\n\t\t\t}\n\n\t\t}.onStartup(context)).withMessage(\"filters cannot be null or empty\");\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t}\n\n\t@Test\n\tpublic void onStartupWhenNullFilterInsertedThenException() {\n\t\tFilter filter = mock(Filter.class);\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tgiven(context.addFilter(anyString(), eq(filter))).willReturn(registration);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AbstractSecurityWebApplicationInitializer() {\n\n\t\t\t@Override\n\t\t\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\t\t\t\tinsertFilters(context, filter, null);\n\t\t\t}\n\n\t\t}.onStartup(context)).withMessageContaining(\"filters cannot contain null values\");\n\t\tverify(context, times(2)).addFilter(anyString(), any(Filter.class));\n\t}\n\n\t@Test\n\tpublic void onStartupWhenAppendFiltersThenAppended() {\n\t\tFilter filter1 = mock(Filter.class);\n\t\tFilter filter2 = mock(Filter.class);\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tgiven(context.addFilter(anyString(), eq(filter1))).willReturn(registration);\n\t\tgiven(context.addFilter(anyString(), eq(filter2))).willReturn(registration);\n\t\tnew AbstractSecurityWebApplicationInitializer() {\n\t\t\t@Override\n\t\t\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\t\t\t\tappendFilters(context, filter1, filter2);\n\t\t\t}\n\t\t}.onStartup(context);\n\t\tverify(registration, times(1)).addMappingForUrlPatterns(DEFAULT_DISPATCH, false, \"/*\");\n\t\tverify(registration, times(2)).addMappingForUrlPatterns(DEFAULT_DISPATCH, true, \"/*\");\n\t\tverify(registration, times(3)).setAsyncSupported(true);\n\t\tverifyNoAddListener(context);\n\t\tverify(context, times(3)).addFilter(anyString(), any(Filter.class));\n\t}\n\n\t@Test\n\tpublic void onStartupWhenDuplicateFilterAppendedThenException() {\n\t\tFilter filter1 = mock(Filter.class);\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tassertThatIllegalStateException().isThrownBy(() -> new AbstractSecurityWebApplicationInitializer() {\n\n\t\t\t@Override\n\t\t\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\t\t\t\tappendFilters(context, filter1);\n\t\t\t}\n\n\t\t}.onStartup(context))\n\t\t\t.withMessage(\"Duplicate Filter registration for 'object'. \"\n\t\t\t\t\t+ \"Check to ensure the Filter is only configured once.\");\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(registration).addMappingForUrlPatterns(DEFAULT_DISPATCH, false, \"/*\");\n\t\tverify(context).addFilter(anyString(), eq(filter1));\n\t}\n\n\t@Test\n\tpublic void onStartupWhenAppendFiltersEmptyThenException() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AbstractSecurityWebApplicationInitializer() {\n\n\t\t\t@Override\n\t\t\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\t\t\t\tappendFilters(context);\n\t\t\t}\n\n\t\t}.onStartup(context)).withMessage(\"filters cannot be null or empty\");\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t}\n\n\t@Test\n\tpublic void onStartupWhenNullFilterAppendedThenException() {\n\t\tFilter filter = mock(Filter.class);\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture())).willReturn(registration);\n\t\tgiven(context.addFilter(anyString(), eq(filter))).willReturn(registration);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AbstractSecurityWebApplicationInitializer() {\n\n\t\t\t@Override\n\t\t\tprotected void afterSpringSecurityFilterChain(ServletContext servletContext) {\n\t\t\t\tappendFilters(context, filter, null);\n\t\t\t}\n\n\t\t}.onStartup(context)).withMessageContaining(\"filters cannot contain null values\");\n\t\tverify(context, times(2)).addFilter(anyString(), any(Filter.class));\n\t}\n\n\t@Test\n\tpublic void onStartupWhenDefaultsThenSessionTrackingModes() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), any(DelegatingFilterProxy.class)))\n\t\t\t.willReturn(registration);\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<Set<SessionTrackingMode>> modesCaptor = ArgumentCaptor.forClass(Set.class);\n\t\tnew AbstractSecurityWebApplicationInitializer() {\n\t\t}.onStartup(context);\n\t\tverify(context).addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture());\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(context).setSessionTrackingModes(modesCaptor.capture());\n\t\tSet<SessionTrackingMode> modes = modesCaptor.getValue();\n\t\tassertThat(modes).hasSize(1);\n\t\tassertThat(modes).containsExactly(SessionTrackingMode.COOKIE);\n\t}\n\n\t@Test\n\tpublic void onStartupWhenSessionTrackingModesConfiguredThenUsed() {\n\t\tServletContext context = mock(ServletContext.class);\n\t\tFilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class);\n\t\tArgumentCaptor<DelegatingFilterProxy> proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class);\n\t\tgiven(context.addFilter(eq(\"springSecurityFilterChain\"), any(DelegatingFilterProxy.class)))\n\t\t\t.willReturn(registration);\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tArgumentCaptor<Set<SessionTrackingMode>> modesCaptor = ArgumentCaptor.forClass(Set.class);\n\t\twillDoNothing().given(context).setSessionTrackingModes(any());\n\t\tnew AbstractSecurityWebApplicationInitializer() {\n\t\t\t@Override\n\t\t\tpublic Set<SessionTrackingMode> getSessionTrackingModes() {\n\t\t\t\treturn Collections.singleton(SessionTrackingMode.SSL);\n\t\t\t}\n\t\t}.onStartup(context);\n\t\tverify(context).addFilter(eq(\"springSecurityFilterChain\"), proxyCaptor.capture());\n\t\tassertProxyDefaults(proxyCaptor.getValue());\n\t\tverify(context).setSessionTrackingModes(modesCaptor.capture());\n\t\tSet<SessionTrackingMode> modes = modesCaptor.getValue();\n\t\tassertThat(modes).hasSize(1);\n\t\tassertThat(modes).containsExactly(SessionTrackingMode.SSL);\n\t}\n\n\t@Test\n\tpublic void defaultFilterNameEqualsSpringSecurityFilterChain() {\n\t\tassertThat(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)\n\t\t\t.isEqualTo(\"springSecurityFilterChain\");\n\t}\n\n\tprivate static void verifyNoAddListener(ServletContext context) {\n\t\tverify(context, times(0)).addListener(anyString());\n\t\tverify(context, times(0)).addListener(any(EventListener.class));\n\t\tverify(context, times(0)).addListener(any(Class.class));\n\t}\n\n\tprivate static void assertProxyDefaults(DelegatingFilterProxy proxy) {\n\t\tassertThat(proxy.getContextAttribute()).isNull();\n\t\tassertThat(proxy).hasFieldOrPropertyWithValue(\"targetBeanName\", \"springSecurityFilterChain\");\n\t}\n\n\t@Configuration\n\tstatic class MyRootConfiguration {\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/DelegatingSecurityContextRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.DeferredSecurityContext;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link DelegatingSecurityContextRepository}.\n *\n * @author Steve Riesenberg\n * @since 5.8\n */\npublic class DelegatingSecurityContextRepositoryTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate SecurityContextHolderStrategy strategy;\n\n\tprivate SecurityContext securityContext;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.strategy = mock(SecurityContextHolderStrategy.class);\n\t\tthis.securityContext = mock(SecurityContext.class);\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ \"0,false\", \"1,false\", \"2,false\", \"-1,true\" })\n\tpublic void loadDeferredContextWhenIsGeneratedThenReturnsSecurityContext(int expectedIndex, boolean isGenerated) {\n\t\tSecurityContext actualSecurityContext = new SecurityContextImpl(\n\t\t\t\tnew TestingAuthenticationToken(\"user\", \"password\"));\n\t\tSecurityContext emptySecurityContext = new SecurityContextImpl();\n\t\tgiven(this.strategy.createEmptyContext()).willReturn(emptySecurityContext);\n\t\tList<SecurityContextRepository> delegates = new ArrayList<>();\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tSecurityContext context = (i == expectedIndex) ? actualSecurityContext : null;\n\t\t\tSecurityContextRepository repository = mock(SecurityContextRepository.class);\n\t\t\tSupplierDeferredSecurityContext supplier = new SupplierDeferredSecurityContext(() -> context,\n\t\t\t\t\tthis.strategy);\n\t\t\tgiven(repository.loadDeferredContext(this.request)).willReturn(supplier);\n\t\t\tdelegates.add(repository);\n\t\t}\n\n\t\tDelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegates);\n\t\tDeferredSecurityContext deferredSecurityContext = repository.loadDeferredContext(this.request);\n\t\tSecurityContext expectedSecurityContext = (isGenerated) ? emptySecurityContext : actualSecurityContext;\n\t\tassertThat(deferredSecurityContext.get()).isEqualTo(expectedSecurityContext);\n\t\tassertThat(deferredSecurityContext.isGenerated()).isEqualTo(isGenerated);\n\n\t\tfor (SecurityContextRepository delegate : delegates) {\n\t\t\tverify(delegate).loadDeferredContext(this.request);\n\t\t\tverifyNoMoreInteractions(delegate);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void saveContextAlwaysCallsDelegates() {\n\t\tList<SecurityContextRepository> delegates = new ArrayList<>();\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tSecurityContextRepository repository = mock(SecurityContextRepository.class);\n\t\t\tdelegates.add(repository);\n\t\t}\n\n\t\tDelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegates);\n\t\trepository.saveContext(this.securityContext, this.request, this.response);\n\t\tfor (SecurityContextRepository delegate : delegates) {\n\t\t\tverify(delegate).saveContext(this.securityContext, this.request, this.response);\n\t\t\tverifyNoMoreInteractions(delegate);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void containsContextWhenAllDelegatesReturnFalseThenReturnsFalse() {\n\t\tList<SecurityContextRepository> delegates = new ArrayList<>();\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tSecurityContextRepository repository = mock(SecurityContextRepository.class);\n\t\t\tgiven(repository.containsContext(this.request)).willReturn(false);\n\t\t\tdelegates.add(repository);\n\t\t}\n\n\t\tDelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegates);\n\t\tassertThat(repository.containsContext(this.request)).isFalse();\n\t\tfor (SecurityContextRepository delegate : delegates) {\n\t\t\tverify(delegate).containsContext(this.request);\n\t\t\tverifyNoMoreInteractions(delegate);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void containsContextWhenFirstDelegatesReturnTrueThenReturnsTrue() {\n\t\tList<SecurityContextRepository> delegates = new ArrayList<>();\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tSecurityContextRepository repository = mock(SecurityContextRepository.class);\n\t\t\tgiven(repository.containsContext(this.request)).willReturn(true);\n\t\t\tdelegates.add(repository);\n\t\t}\n\n\t\tDelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegates);\n\t\tassertThat(repository.containsContext(this.request)).isTrue();\n\t\tverify(delegates.get(0)).containsContext(this.request);\n\t\tverifyNoInteractions(delegates.get(1));\n\t\tverifyNoInteractions(delegates.get(2));\n\t}\n\n\t// gh-12314\n\t@Test\n\tpublic void loadContextWhenSecondDelegateReturnsThenContextFromSecondDelegate() {\n\t\tSecurityContextRepository delegate1 = mock(SecurityContextRepository.class);\n\t\tSecurityContextRepository delegate2 = mock(SecurityContextRepository.class);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(this.request, this.response);\n\t\tSecurityContext securityContext1 = mock(SecurityContext.class);\n\t\tSecurityContext securityContext2 = mock(SecurityContext.class);\n\n\t\tgiven(delegate1.loadContext(holder)).willReturn(securityContext1);\n\t\tgiven(delegate1.containsContext(holder.getRequest())).willReturn(false);\n\t\tgiven(delegate2.loadContext(holder)).willReturn(securityContext2);\n\t\tgiven(delegate2.containsContext(holder.getRequest())).willReturn(true);\n\n\t\tDelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegate1, delegate2);\n\t\tSecurityContext returnedSecurityContext = repository.loadContext(holder);\n\n\t\tassertThat(returnedSecurityContext).isSameAs(securityContext2);\n\t}\n\n\t// gh-12314\n\t@Test\n\tpublic void loadContextWhenBothDelegateReturnsThenContextFromSecondDelegate() {\n\t\tSecurityContextRepository delegate1 = mock(SecurityContextRepository.class);\n\t\tSecurityContextRepository delegate2 = mock(SecurityContextRepository.class);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(this.request, this.response);\n\t\tSecurityContext securityContext1 = mock(SecurityContext.class);\n\t\tSecurityContext securityContext2 = mock(SecurityContext.class);\n\n\t\tgiven(delegate1.loadContext(holder)).willReturn(securityContext1);\n\t\tgiven(delegate1.containsContext(holder.getRequest())).willReturn(true);\n\t\tgiven(delegate2.loadContext(holder)).willReturn(securityContext2);\n\t\tgiven(delegate2.containsContext(holder.getRequest())).willReturn(true);\n\n\t\tDelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegate1, delegate2);\n\t\tSecurityContext returnedSecurityContext = repository.loadContext(holder);\n\n\t\tassertThat(returnedSecurityContext).isSameAs(securityContext2);\n\t}\n\n\t// gh-12314\n\t@Test\n\tpublic void loadContextWhenFirstDelegateReturnsThenContextFromFirstDelegate() {\n\t\tSecurityContextRepository delegate1 = mock(SecurityContextRepository.class);\n\t\tSecurityContextRepository delegate2 = mock(SecurityContextRepository.class);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(this.request, this.response);\n\t\tSecurityContext securityContext1 = mock(SecurityContext.class);\n\t\tSecurityContext securityContext2 = mock(SecurityContext.class);\n\n\t\tgiven(delegate1.loadContext(holder)).willReturn(securityContext1);\n\t\tgiven(delegate1.containsContext(holder.getRequest())).willReturn(true);\n\t\tgiven(delegate2.loadContext(holder)).willReturn(securityContext2);\n\t\tgiven(delegate2.containsContext(holder.getRequest())).willReturn(false);\n\n\t\tDelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegate1, delegate2);\n\t\tSecurityContext returnedSecurityContext = repository.loadContext(holder);\n\n\t\tassertThat(returnedSecurityContext).isSameAs(securityContext1);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/HttpSessionSecurityContextRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.io.IOException;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletOutputStream;\nimport jakarta.servlet.http.HttpServlet;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpServletResponseWrapper;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.Transient;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.context.TransientSecurityContext;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n */\npublic class HttpSessionSecurityContextRepositoryTests {\n\n\tprivate final TestingAuthenticationToken testToken = new TestingAuthenticationToken(\"someone\", \"passwd\", \"ROLE_A\");\n\n\t@AfterEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void startAsyncDisablesSaveOnCommit() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\trepo.loadContext(holder);\n\t\treset(request);\n\t\tholder.getRequest().startAsync();\n\t\tholder.getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST);\n\t\t// ensure that sendError did cause interaction with the HttpSession\n\t\tverify(request, never()).getSession(anyBoolean());\n\t\tverify(request, never()).getSession();\n\t}\n\n\t@Test\n\tpublic void startAsyncRequestResponseDisablesSaveOnCommit() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\trepo.loadContext(holder);\n\t\treset(request);\n\t\tholder.getRequest().startAsync(request, response);\n\t\tholder.getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST);\n\t\t// ensure that sendError did cause interaction with the HttpSession\n\t\tverify(request, never()).getSession(anyBoolean());\n\t\tverify(request, never()).getSession();\n\t}\n\n\t@Test\n\tpublic void sessionIsntCreatedIfContextDoesntChange() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tassertThat(request.getSession(false)).isNull();\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void sessionIsntCreatedIfAllowSessionCreationIsFalse() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setAllowSessionCreation(false);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\t// Change context\n\t\tcontext.setAuthentication(this.testToken);\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void loadContextWhenNullResponse() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, null);\n\t\tassertThat(repo.loadContext(holder)).isEqualTo(SecurityContextHolder.createEmptyContext());\n\t}\n\n\t@Test\n\tpublic void loadContextHttpServletRequestWhenNotSavedThenEmptyContextReturned() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThat(repo.loadDeferredContext(request).get()).isEqualTo(SecurityContextHolder.createEmptyContext());\n\t}\n\n\t@Test\n\tpublic void loadContextHttpServletRequestWhenSavedThenSavedContextReturned() {\n\t\tSecurityContextImpl expectedContext = new SecurityContextImpl(this.testToken);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.saveContext(expectedContext, request, response);\n\t\tassertThat(repo.loadDeferredContext(request).get()).isEqualTo(expectedContext);\n\t}\n\n\t@Test\n\tpublic void loadContextHttpServletRequestWhenNotAccessedThenHttpSessionNotAccessed() {\n\t\tHttpSession session = mock(HttpSession.class);\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setSession(session);\n\t\trepo.loadDeferredContext(request);\n\t\tverifyNoInteractions(session);\n\t}\n\n\t@Test\n\tpublic void existingContextIsSuccessFullyLoadedFromSessionAndSavedBack() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\trequest.getSession().setAttribute(\"imTheContext\", SecurityContextHolder.getContext());\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tassertThat(context).isNotNull();\n\t\tassertThat(context.getAuthentication()).isEqualTo(this.testToken);\n\t\t// Won't actually be saved as it hasn't changed, but go through the use case\n\t\t// anyway\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(context);\n\t}\n\n\t// SEC-1528\n\t@Test\n\tpublic void saveContextCallsSetAttributeIfContextIsModifiedDirectlyDuringRequest() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\t// Set up an existing authenticated context, mocking that it is in the session\n\t\t// already\n\t\tSecurityContext ctx = SecurityContextHolder.getContext();\n\t\tctx.setAuthentication(this.testToken);\n\t\tHttpSession session = mock(HttpSession.class);\n\t\tgiven(session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY)).willReturn(ctx);\n\t\trequest.setSession(session);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, new MockHttpServletResponse());\n\t\tassertThat(repo.loadContext(holder)).isSameAs(ctx);\n\t\t// Modify context contents. Same user, different role\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"someone\", \"passwd\", \"ROLE_B\"));\n\t\trepo.saveContext(ctx, holder.getRequest(), holder.getResponse());\n\t\t// Must be called even though the value in the local VM is already the same\n\t\tverify(session).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, ctx);\n\t}\n\n\t@Test\n\tpublic void saveContextWhenSaveNewContextThenOriginalContextThenOriginalContextSaved() throws Exception {\n\t\tHttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository();\n\t\tSecurityContextPersistenceFilter securityContextPersistenceFilter = new SecurityContextPersistenceFilter(\n\t\t\t\trepository);\n\n\t\tUserDetails original = User.withUsername(\"user\").password(\"password\").roles(\"USER\").build();\n\t\tSecurityContext originalContext = createSecurityContext(original);\n\t\tUserDetails impersonate = User.withUserDetails(original).username(\"impersonate\").build();\n\t\tSecurityContext impersonateContext = createSecurityContext(impersonate);\n\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse mockResponse = new MockHttpServletResponse();\n\n\t\tFilter saveImpersonateContext = (request, response, chain) -> {\n\t\t\tSecurityContextHolder.setContext(impersonateContext);\n\t\t\t// ensure the response is committed to trigger save\n\t\t\tresponse.flushBuffer();\n\t\t\tchain.doFilter(request, response);\n\t\t};\n\t\tFilter saveOriginalContext = (request, response, chain) -> {\n\t\t\tSecurityContextHolder.setContext(originalContext);\n\t\t\tchain.doFilter(request, response);\n\t\t};\n\t\tHttpServlet servlet = new HttpServlet() {\n\t\t\t@Override\n\t\t\tprotected void service(HttpServletRequest req, HttpServletResponse resp)\n\t\t\t\t\tthrows ServletException, IOException {\n\t\t\t\tresp.getWriter().write(\"Hi\");\n\t\t\t}\n\t\t};\n\n\t\tSecurityContextHolder.setContext(originalContext);\n\t\tMockFilterChain chain = new MockFilterChain(servlet, saveImpersonateContext, saveOriginalContext);\n\n\t\tsecurityContextPersistenceFilter.doFilter(mockRequest, mockResponse, chain);\n\n\t\tassertThat(\n\t\t\t\tmockRequest.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY))\n\t\t\t.isEqualTo(originalContext);\n\t}\n\n\t@Test\n\tpublic void nonSecurityContextInSessionIsIgnored() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\trequest.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,\n\t\t\t\t\t\"NotASecurityContextInstance\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tassertThat(context).isNotNull();\n\t\tassertThat(context.getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void sessionIsCreatedAndContextStoredWhenContextChanges() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tassertThat(request.getSession(false)).isNull();\n\t\t// Simulate authentication during the request\n\t\tcontext.setAuthentication(this.testToken);\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t\tassertThat(request.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY))\n\t\t\t.isEqualTo(context);\n\t}\n\n\t@Test\n\tpublic void redirectCausesEarlySaveOfContext() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().sendRedirect(\"/doesntmatter\");\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t\tassertThat(((SaveContextOnUpdateOrErrorResponseWrapper) holder.getResponse()).isContextSaved()).isTrue();\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\t// Check it's still the same\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t}\n\n\t@Test\n\tpublic void sendErrorCausesEarlySaveOfContext() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().sendError(404);\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t\tassertThat(((SaveContextOnUpdateOrErrorResponseWrapper) holder.getResponse()).isContextSaved()).isTrue();\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\t// Check it's still the same\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t}\n\n\t// SEC-2005\n\t@Test\n\tpublic void flushBufferCausesEarlySaveOfContext() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().flushBuffer();\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t\tassertThat(((SaveContextOnUpdateOrErrorResponseWrapper) holder.getResponse()).isContextSaved()).isTrue();\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\t// Check it's still the same\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t}\n\n\t// SEC-2005\n\t@Test\n\tpublic void writerFlushCausesEarlySaveOfContext() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().getWriter().flush();\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t\tassertThat(((SaveContextOnUpdateOrErrorResponseWrapper) holder.getResponse()).isContextSaved()).isTrue();\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\t// Check it's still the same\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t}\n\n\t// SEC-2005\n\t@Test\n\tpublic void writerCloseCausesEarlySaveOfContext() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().getWriter().close();\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t\tassertThat(((SaveContextOnUpdateOrErrorResponseWrapper) holder.getResponse()).isContextSaved()).isTrue();\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\t// Check it's still the same\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t}\n\n\t// SEC-2005\n\t@Test\n\tpublic void outputStreamFlushCausesEarlySaveOfContext() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().getOutputStream().flush();\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t\tassertThat(((SaveContextOnUpdateOrErrorResponseWrapper) holder.getResponse()).isContextSaved()).isTrue();\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\t// Check it's still the same\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t}\n\n\t// SEC-2005\n\t@Test\n\tpublic void outputStreamCloseCausesEarlySaveOfContext() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().getOutputStream().close();\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t\tassertThat(((SaveContextOnUpdateOrErrorResponseWrapper) holder.getResponse()).isContextSaved()).isTrue();\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\t// Check it's still the same\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isEqualTo(SecurityContextHolder.getContext());\n\t}\n\n\t// SEC-SEC-2055\n\t@Test\n\tpublic void outputStreamCloseDelegate() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tServletOutputStream outputstream = mock(ServletOutputStream.class);\n\t\tgiven(response.getOutputStream()).willReturn(outputstream);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().getOutputStream().close();\n\t\tverify(outputstream).close();\n\t}\n\n\t// SEC-SEC-2055\n\t@Test\n\tpublic void outputStreamFlushesDelegate() throws Exception {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tServletOutputStream outputstream = mock(ServletOutputStream.class);\n\t\tgiven(response.getOutputStream()).willReturn(outputstream);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tholder.getResponse().getOutputStream().flush();\n\t\tverify(outputstream).flush();\n\t}\n\n\t@Test\n\tpublic void noSessionIsCreatedIfSessionWasInvalidatedDuringTheRequest() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.getSession();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\trequest.getSession().invalidate();\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t// SEC-1315\n\t@Test\n\tpublic void noSessionIsCreatedIfAnonymousTokenIsUsed() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContextHolder.setContext(repo.loadContext(holder));\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(\n\t\t\t\t\tnew AnonymousAuthenticationToken(\"key\", \"anon\", AuthorityUtils.createAuthorityList(\"ANON\")));\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t// SEC-1587\n\t@Test\n\tpublic void contextIsRemovedFromSessionIfCurrentContextIsAnonymous() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSecurityContext ctxInSession = SecurityContextHolder.createEmptyContext();\n\t\tctxInSession.setAuthentication(this.testToken);\n\t\trequest.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, ctxInSession);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, new MockHttpServletResponse());\n\t\trepo.loadContext(holder);\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new AnonymousAuthenticationToken(\"x\", \"x\", this.testToken.getAuthorities()));\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void contextIsRemovedFromSessionIfCurrentContextIsEmpty() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\trepo.setSpringSecurityContextKey(\"imTheContext\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSecurityContext ctxInSession = SecurityContextHolder.createEmptyContext();\n\t\tctxInSession.setAuthentication(this.testToken);\n\t\trequest.getSession().setAttribute(\"imTheContext\", ctxInSession);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, new MockHttpServletResponse());\n\t\trepo.loadContext(holder);\n\t\t// Save an empty context\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession().getAttribute(\"imTheContext\")).isNull();\n\t}\n\n\t// SEC-1735\n\t@Test\n\tpublic void contextIsNotRemovedFromSessionIfContextBeforeExecutionDefault() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, new MockHttpServletResponse());\n\t\trepo.loadContext(holder);\n\t\tSecurityContext ctxInSession = SecurityContextHolder.createEmptyContext();\n\t\tctxInSession.setAuthentication(this.testToken);\n\t\trequest.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, ctxInSession);\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(\n\t\t\t\t\tnew AnonymousAuthenticationToken(\"x\", \"x\", AuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\")));\n\t\trepo.saveContext(SecurityContextHolder.getContext(), holder.getRequest(), holder.getResponse());\n\t\tassertThat(ctxInSession).isSameAs(\n\t\t\t\trequest.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));\n\t}\n\n\t// SEC-3070\n\t@Test\n\tpublic void logoutInvalidateSessionFalseFails() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSecurityContext ctxInSession = SecurityContextHolder.createEmptyContext();\n\t\tctxInSession.setAuthentication(this.testToken);\n\t\trequest.getSession()\n\t\t\t.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, ctxInSession);\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, new MockHttpServletResponse());\n\t\trepo.loadContext(holder);\n\t\tctxInSession.setAuthentication(null);\n\t\trepo.saveContext(ctxInSession, holder.getRequest(), holder.getResponse());\n\t\tassertThat(request.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void sessionDisableUrlRewritingPreventsSessionIdBeingWrittenToUrl() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfinal String sessionId = \";jsessionid=id\";\n\t\tMockHttpServletResponse response = new MockHttpServletResponse() {\n\t\t\t@Override\n\t\t\tpublic String encodeRedirectURL(String url) {\n\t\t\t\treturn url + sessionId;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String encodeURL(String url) {\n\t\t\t\treturn url + sessionId;\n\t\t\t}\n\t\t};\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\trepo.loadContext(holder);\n\t\tString url = \"/aUrl\";\n\t\tassertThat(holder.getResponse().encodeRedirectURL(url)).isEqualTo(url + sessionId);\n\t\tassertThat(holder.getResponse().encodeURL(url)).isEqualTo(url + sessionId);\n\t\trepo.setDisableUrlRewriting(true);\n\t\tholder = new HttpRequestResponseHolder(request, response);\n\t\trepo.loadContext(holder);\n\t\tassertThat(holder.getResponse().encodeRedirectURL(url)).isEqualTo(url);\n\t\tassertThat(holder.getResponse().encodeURL(url)).isEqualTo(url);\n\t}\n\n\t@Test\n\tpublic void saveContextCustomTrustResolver() {\n\t\tSecurityContext contextToSave = SecurityContextHolder.createEmptyContext();\n\t\tcontextToSave.setAuthentication(this.testToken);\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, new MockHttpServletResponse());\n\t\trepo.loadContext(holder);\n\t\tAuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class);\n\t\trepo.setTrustResolver(trustResolver);\n\t\trepo.saveContext(contextToSave, holder.getRequest(), holder.getResponse());\n\t\tverify(trustResolver).isAnonymous(contextToSave.getAuthentication());\n\t}\n\n\t@Test\n\tpublic void setTrustResolverNull() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> repo.setTrustResolver(null));\n\t}\n\n\t// SEC-2578\n\t@Test\n\tpublic void traverseWrappedRequests() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tassertThat(request.getSession(false)).isNull();\n\t\t// Simulate authentication during the request\n\t\tcontext.setAuthentication(this.testToken);\n\t\trepo.saveContext(context, new HttpServletRequestWrapper(holder.getRequest()),\n\t\t\t\tnew HttpServletResponseWrapper(holder.getResponse()));\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t\tassertThat(request.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY))\n\t\t\t.isEqualTo(context);\n\t}\n\n\t@Test\n\tpublic void standardResponseWorks() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tcontext.setAuthentication(this.testToken);\n\t\trepo.saveContext(context, request, response);\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t\tassertThat(request.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY))\n\t\t\t.isEqualTo(context);\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientSecurityContextThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tSecurityContext transientSecurityContext = new TransientSecurityContext();\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\ttransientSecurityContext.setAuthentication(authentication);\n\t\trepo.saveContext(transientSecurityContext, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientSecurityContextSubclassThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tSecurityContext transientSecurityContext = new TransientSecurityContext() {\n\t\t};\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\ttransientSecurityContext.setAuthentication(authentication);\n\t\trepo.saveContext(transientSecurityContext, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientSecurityContextAndSessionExistsThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.getSession(); // ensure the session exists\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tSecurityContext transientSecurityContext = new TransientSecurityContext();\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\ttransientSecurityContext.setAuthentication(authentication);\n\t\trepo.saveContext(transientSecurityContext, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(Collections.list(session.getAttributeNames())).isEmpty();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientSecurityContextWithCustomAnnotationThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tSecurityContext transientSecurityContext = new TransientSecurityContext();\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\ttransientSecurityContext.setAuthentication(authentication);\n\t\trepo.saveContext(transientSecurityContext, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientAuthenticationThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tSomeTransientAuthentication authentication = new SomeTransientAuthentication();\n\t\tcontext.setAuthentication(authentication);\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientAuthenticationSubclassThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tSomeTransientAuthenticationSubclass authentication = new SomeTransientAuthenticationSubclass();\n\t\tcontext.setAuthentication(authentication);\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientAuthenticationAndSessionExistsThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.getSession(); // ensure the session exists\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tSomeTransientAuthentication authentication = new SomeTransientAuthentication();\n\t\tcontext.setAuthentication(authentication);\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(Collections.list(session.getAttributeNames())).isEmpty();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientAuthenticationWithCustomAnnotationThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSecurityContext context = repo.loadContext(holder);\n\t\tSomeOtherTransientAuthentication authentication = new SomeOtherTransientAuthentication();\n\t\tcontext.setAuthentication(authentication);\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t// gh-8947\n\t@Test\n\tpublic void saveContextWhenSecurityContextAuthenticationUpdatedToNullThenSkipped() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);\n\t\tSomeOtherTransientAuthentication authentication = new SomeOtherTransientAuthentication();\n\t\trepo.loadContext(holder);\n\t\tSecurityContext context = mock(SecurityContext.class);\n\t\tgiven(context.getAuthentication()).willReturn(authentication).willReturn(null);\n\t\trepo.saveContext(context, holder.getRequest(), holder.getResponse());\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(false);\n\t\tassertThat(session).isNull();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenSecurityContextEmptyThenRemoveAttributeFromSession() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContext emptyContext = SecurityContextHolder.createEmptyContext();\n\t\tMockHttpSession session = (MockHttpSession) request.getSession(true);\n\t\tsession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, emptyContext);\n\t\trepo.saveContext(emptyContext, request, response);\n\t\tObject attributeAfterSave = session\n\t\t\t.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);\n\t\tassertThat(attributeAfterSave).isNull();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenSecurityContextEmptyAndNoSessionThenDoesNotCreateSession() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContext emptyContext = SecurityContextHolder.createEmptyContext();\n\t\trepo.saveContext(emptyContext, request, response);\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void saveContextWhenSecurityContextThenSaveInSession() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContext context = createSecurityContext(PasswordEncodedUser.user());\n\t\trepo.saveContext(context, request, response);\n\t\tObject savedContext = request.getSession()\n\t\t\t.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);\n\t\tassertThat(savedContext).isEqualTo(context);\n\t}\n\n\t@Test\n\tpublic void saveContextWhenTransientAuthenticationThenDoNotSave() {\n\t\tHttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tcontext.setAuthentication(new SomeTransientAuthentication());\n\t\trepo.saveContext(context, request, response);\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\tprivate SecurityContext createSecurityContext(UserDetails userDetails) {\n\t\tUsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(userDetails,\n\t\t\t\tuserDetails.getPassword(), userDetails.getAuthorities());\n\t\tSecurityContext securityContext = new SecurityContextImpl(token);\n\t\treturn securityContext;\n\t}\n\n\t@Transient\n\tprivate static class SomeTransientAuthentication extends AbstractAuthenticationToken {\n\n\t\tSomeTransientAuthentication() {\n\t\t\tsuper((Collection<? extends GrantedAuthority>) null);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getCredentials() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getPrincipal() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tprivate static class SomeTransientAuthenticationSubclass extends SomeTransientAuthentication {\n\n\t}\n\n\t@Target(ElementType.TYPE)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Transient\n\tpublic @interface TestTransientAuthentication {\n\n\t}\n\n\t@TestTransientAuthentication\n\tprivate static class SomeOtherTransientAuthentication extends AbstractAuthenticationToken {\n\n\t\tSomeOtherTransientAuthentication() {\n\t\t\tsuper((Collection<? extends GrantedAuthority>) null);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getCredentials() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getPrincipal() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/RequestAttributeSecurityContextRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n */\nclass RequestAttributeSecurityContextRepositoryTests {\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate MockHttpServletResponse response = new MockHttpServletResponse();\n\n\tprivate RequestAttributeSecurityContextRepository repository = new RequestAttributeSecurityContextRepository();\n\n\tprivate SecurityContext expectedSecurityContext = new SecurityContextImpl(TestAuthentication.authenticatedUser());\n\n\t@Test\n\tvoid setSecurityContextHolderStrategyWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.repository.setSecurityContextHolderStrategy(null))\n\t\t\t\t.withMessage(\"securityContextHolderStrategy cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid saveContextAndLoadContextThenFound() {\n\t\tthis.repository.saveContext(this.expectedSecurityContext, this.request, this.response);\n\t\tSecurityContext securityContext = this.repository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response));\n\t\tassertThat(securityContext).isEqualTo(this.expectedSecurityContext);\n\t}\n\n\t@Test\n\tvoid saveContextWhenLoadContextAndNewRequestThenNotFound() {\n\t\tthis.repository.saveContext(this.expectedSecurityContext, this.request, this.response);\n\t\tSecurityContext securityContext = this.repository\n\t\t\t.loadContext(new HttpRequestResponseHolder(new MockHttpServletRequest(), new MockHttpServletResponse()));\n\t\tassertThat(securityContext).isEqualTo(SecurityContextHolder.createEmptyContext());\n\t}\n\n\t@Test\n\tvoid containsContextWhenNotSavedThenFalse() {\n\t\tassertThat(this.repository.containsContext(this.request)).isFalse();\n\t}\n\n\t@Test\n\tvoid containsContextWhenSavedThenTrue() {\n\t\tthis.repository.saveContext(this.expectedSecurityContext, this.request, this.response);\n\t\tassertThat(this.repository.containsContext(this.request)).isTrue();\n\t}\n\n\t@Test\n\tvoid loadDeferredContextWhenNotPresentThenEmptyContext() {\n\t\tSupplier<SecurityContext> deferredContext = this.repository.loadDeferredContext(this.request);\n\t\tassertThat(deferredContext.get()).isEqualTo(SecurityContextHolder.createEmptyContext());\n\t}\n\n\t@Test\n\tvoid loadContextWhenNotPresentThenEmptyContext() {\n\t\tSecurityContext context = this.repository\n\t\t\t.loadContext(new HttpRequestResponseHolder(this.request, this.response));\n\t\tassertThat(context).isEqualTo(SecurityContextHolder.createEmptyContext());\n\t}\n\n\t@Test\n\tvoid loadContextWhenCustomSecurityContextHolderStrategySetThenUsed() {\n\t\tSecurityContextHolderStrategy securityContextHolderStrategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(securityContextHolderStrategy.createEmptyContext()).willReturn(new SecurityContextImpl());\n\t\tthis.repository.setSecurityContextHolderStrategy(securityContextHolderStrategy);\n\n\t\tSupplier<SecurityContext> deferredContext = this.repository.loadDeferredContext(this.request);\n\t\tassertThat(deferredContext.get()).isNotNull();\n\t\tverify(securityContextHolderStrategy).createEmptyContext();\n\t\tverifyNoMoreInteractions(securityContextHolderStrategy);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/SaveContextOnUpdateOrErrorResponseWrapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class SaveContextOnUpdateOrErrorResponseWrapperTests {\n\n\t@Mock\n\tprivate SecurityContext securityContext;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate SaveContextOnUpdateOrErrorResponseWrapperStub wrappedResponse;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.wrappedResponse = new SaveContextOnUpdateOrErrorResponseWrapperStub(this.response, true);\n\t\tSecurityContextHolder.setContext(this.securityContext);\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void sendErrorSavesSecurityContext() throws Exception {\n\t\tint error = HttpServletResponse.SC_FORBIDDEN;\n\t\tthis.wrappedResponse.sendError(error);\n\t\tassertThat(this.wrappedResponse.securityContext).isEqualTo(this.securityContext);\n\t\tassertThat(this.response.getStatus()).isEqualTo(error);\n\t}\n\n\t@Test\n\tpublic void sendErrorSkipsSaveSecurityContextDisables() throws Exception {\n\t\tfinal int error = HttpServletResponse.SC_FORBIDDEN;\n\t\tthis.wrappedResponse.disableSaveOnResponseCommitted();\n\t\tthis.wrappedResponse.sendError(error);\n\t\tassertThat(this.wrappedResponse.securityContext).isNull();\n\t\tassertThat(this.response.getStatus()).isEqualTo(error);\n\t}\n\n\t@Test\n\tpublic void sendErrorWithMessageSavesSecurityContext() throws Exception {\n\t\tint error = HttpServletResponse.SC_FORBIDDEN;\n\t\tString message = \"Forbidden\";\n\t\tthis.wrappedResponse.sendError(error, message);\n\t\tassertThat(this.wrappedResponse.securityContext).isEqualTo(this.securityContext);\n\t\tassertThat(this.response.getStatus()).isEqualTo(error);\n\t\tassertThat(this.response.getErrorMessage()).isEqualTo(message);\n\t}\n\n\t@Test\n\tpublic void sendErrorWithMessageSkipsSaveSecurityContextDisables() throws Exception {\n\t\tfinal int error = HttpServletResponse.SC_FORBIDDEN;\n\t\tfinal String message = \"Forbidden\";\n\t\tthis.wrappedResponse.disableSaveOnResponseCommitted();\n\t\tthis.wrappedResponse.sendError(error, message);\n\t\tassertThat(this.wrappedResponse.securityContext).isNull();\n\t\tassertThat(this.response.getStatus()).isEqualTo(error);\n\t\tassertThat(this.response.getErrorMessage()).isEqualTo(message);\n\t}\n\n\t@Test\n\tpublic void sendRedirectSavesSecurityContext() throws Exception {\n\t\tString url = \"/location\";\n\t\tthis.wrappedResponse.sendRedirect(url);\n\t\tassertThat(this.wrappedResponse.securityContext).isEqualTo(this.securityContext);\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(url);\n\t}\n\n\t@Test\n\tpublic void sendRedirectSkipsSaveSecurityContextDisables() throws Exception {\n\t\tfinal String url = \"/location\";\n\t\tthis.wrappedResponse.disableSaveOnResponseCommitted();\n\t\tthis.wrappedResponse.sendRedirect(url);\n\t\tassertThat(this.wrappedResponse.securityContext).isNull();\n\t\tassertThat(this.response.getRedirectedUrl()).isEqualTo(url);\n\t}\n\n\t@Test\n\tpublic void outputFlushSavesSecurityContext() throws Exception {\n\t\tthis.wrappedResponse.getOutputStream().flush();\n\t\tassertThat(this.wrappedResponse.securityContext).isEqualTo(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void outputFlushSkipsSaveSecurityContextDisables() throws Exception {\n\t\tthis.wrappedResponse.disableSaveOnResponseCommitted();\n\t\tthis.wrappedResponse.getOutputStream().flush();\n\t\tassertThat(this.wrappedResponse.securityContext).isNull();\n\t}\n\n\t@Test\n\tpublic void outputCloseSavesSecurityContext() throws Exception {\n\t\tthis.wrappedResponse.getOutputStream().close();\n\t\tassertThat(this.wrappedResponse.securityContext).isEqualTo(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void outputCloseSkipsSaveSecurityContextDisables() throws Exception {\n\t\tthis.wrappedResponse.disableSaveOnResponseCommitted();\n\t\tthis.wrappedResponse.getOutputStream().close();\n\t\tassertThat(this.wrappedResponse.securityContext).isNull();\n\t}\n\n\t@Test\n\tpublic void writerFlushSavesSecurityContext() throws Exception {\n\t\tthis.wrappedResponse.getWriter().flush();\n\t\tassertThat(this.wrappedResponse.securityContext).isEqualTo(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void writerFlushSkipsSaveSecurityContextDisables() throws Exception {\n\t\tthis.wrappedResponse.disableSaveOnResponseCommitted();\n\t\tthis.wrappedResponse.getWriter().flush();\n\t\tassertThat(this.wrappedResponse.securityContext).isNull();\n\t}\n\n\t@Test\n\tpublic void writerCloseSavesSecurityContext() throws Exception {\n\t\tthis.wrappedResponse.getWriter().close();\n\t\tassertThat(this.wrappedResponse.securityContext).isEqualTo(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void writerCloseSkipsSaveSecurityContextDisables() throws Exception {\n\t\tthis.wrappedResponse.disableSaveOnResponseCommitted();\n\t\tthis.wrappedResponse.getWriter().close();\n\t\tassertThat(this.wrappedResponse.securityContext).isNull();\n\t}\n\n\t@Test\n\tpublic void flushBufferSavesSecurityContext() throws Exception {\n\t\tthis.wrappedResponse.flushBuffer();\n\t\tassertThat(this.wrappedResponse.securityContext).isEqualTo(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void flushBufferSkipsSaveSecurityContextDisables() throws Exception {\n\t\tthis.wrappedResponse.disableSaveOnResponseCommitted();\n\t\tthis.wrappedResponse.flushBuffer();\n\t\tassertThat(this.wrappedResponse.securityContext).isNull();\n\t}\n\n\tprivate static class SaveContextOnUpdateOrErrorResponseWrapperStub\n\t\t\textends SaveContextOnUpdateOrErrorResponseWrapper {\n\n\t\tprivate SecurityContext securityContext;\n\n\t\tSaveContextOnUpdateOrErrorResponseWrapperStub(HttpServletResponse response, boolean disableUrlRewriting) {\n\t\t\tsuper(response, disableUrlRewriting);\n\t\t}\n\n\t\t@Override\n\t\tprotected void saveContext(SecurityContext context) {\n\t\t\tthis.securityContext = context;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/SecurityContextHolderFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InOrder;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.lenient;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n@ExtendWith(MockitoExtension.class)\nclass SecurityContextHolderFilterTests {\n\n\tprivate static final String FILTER_APPLIED = \"org.springframework.security.web.context.SecurityContextHolderFilter.APPLIED\";\n\n\t@Mock\n\tprivate SecurityContextRepository repository;\n\n\t@Mock\n\tprivate SecurityContextHolderStrategy strategy;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\t@Captor\n\tprivate ArgumentCaptor<HttpServletRequest> requestArg;\n\n\tprivate SecurityContextHolderFilter filter;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.filter = new SecurityContextHolderFilter(this.repository);\n\t}\n\n\t@AfterEach\n\tvoid cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tvoid doFilterThenSetsAndClearsSecurityContextHolder() throws Exception {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tSecurityContext expectedContext = new SecurityContextImpl(authentication);\n\t\tgiven(this.repository.loadDeferredContext(this.requestArg.capture()))\n\t\t\t.willReturn(new SupplierDeferredSecurityContext(() -> expectedContext, this.strategy));\n\t\tFilterChain filterChain = (request, response) -> assertThat(SecurityContextHolder.getContext())\n\t\t\t.isEqualTo(expectedContext);\n\n\t\tthis.filter.doFilter(this.request, this.response, filterChain);\n\n\t\tassertThat(SecurityContextHolder.getContext()).isEqualTo(SecurityContextHolder.createEmptyContext());\n\t}\n\n\t@Test\n\tvoid doFilterThenSetsAndClearsSecurityContextHolderStrategy() throws Exception {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tSecurityContext expectedContext = new SecurityContextImpl(authentication);\n\t\tgiven(this.repository.loadDeferredContext(this.requestArg.capture()))\n\t\t\t.willReturn(new SupplierDeferredSecurityContext(() -> expectedContext, this.strategy));\n\t\tFilterChain filterChain = (request, response) -> {\n\t\t};\n\n\t\tthis.filter.setSecurityContextHolderStrategy(this.strategy);\n\t\tthis.filter.doFilter(this.request, this.response, filterChain);\n\n\t\tArgumentCaptor<Supplier<SecurityContext>> deferredContextArg = ArgumentCaptor.forClass(Supplier.class);\n\t\tverify(this.strategy).setDeferredContext(deferredContextArg.capture());\n\t\tassertThat(deferredContextArg.getValue().get()).isEqualTo(expectedContext);\n\t\tverify(this.strategy).clearContext();\n\t}\n\n\t@Test\n\tvoid doFilterWhenFilterAppliedThenDoNothing() throws Exception {\n\t\tgiven(this.request.getAttribute(FILTER_APPLIED)).willReturn(true);\n\t\tthis.filter.doFilter(this.request, this.response, new MockFilterChain());\n\t\tverify(this.request, times(1)).getAttribute(FILTER_APPLIED);\n\t\tverifyNoInteractions(this.repository, this.response);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotAppliedThenSetsAndRemovesAttribute() throws Exception {\n\t\tgiven(this.repository.loadDeferredContext(this.requestArg.capture()))\n\t\t\t.willReturn(new SupplierDeferredSecurityContext(SecurityContextHolder::createEmptyContext, this.strategy));\n\n\t\tthis.filter.doFilter(this.request, this.response, new MockFilterChain());\n\n\t\tInOrder inOrder = inOrder(this.request, this.repository);\n\t\tinOrder.verify(this.request).setAttribute(FILTER_APPLIED, true);\n\t\tinOrder.verify(this.repository).loadDeferredContext(this.request);\n\t\tinOrder.verify(this.request).removeAttribute(FILTER_APPLIED);\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource(DispatcherType.class)\n\tvoid doFilterWhenAnyDispatcherTypeThenFilter(DispatcherType dispatcherType) throws Exception {\n\t\tlenient().when(this.request.getDispatcherType()).thenReturn(dispatcherType);\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tSecurityContext expectedContext = new SecurityContextImpl(authentication);\n\t\tgiven(this.repository.loadDeferredContext(this.requestArg.capture()))\n\t\t\t.willReturn(new SupplierDeferredSecurityContext(() -> expectedContext, this.strategy));\n\t\tFilterChain filterChain = (request, response) -> assertThat(SecurityContextHolder.getContext())\n\t\t\t.isEqualTo(expectedContext);\n\n\t\tthis.filter.doFilter(this.request, this.response, filterChain);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/SecurityContextPersistenceFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIOException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\npublic class SecurityContextPersistenceFilterTests {\n\n\tTestingAuthenticationToken testToken = new TestingAuthenticationToken(\"someone\", \"passwd\", \"ROLE_A\");\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void contextIsClearedAfterChainProceeds() throws Exception {\n\t\tfinal FilterChain chain = mock(FilterChain.class);\n\t\tfinal MockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void contextIsStillClearedIfExceptionIsThrowByFilterChain() throws Exception {\n\t\tfinal FilterChain chain = mock(FilterChain.class);\n\t\tfinal MockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();\n\t\tSecurityContextHolder.getContext().setAuthentication(this.testToken);\n\t\twillThrow(new IOException()).given(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));\n\t\tassertThatIOException().isThrownBy(() -> filter.doFilter(request, response, chain));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t}\n\n\t@Test\n\tpublic void loadedContextContextIsCopiedToSecurityContextHolderAndUpdatedContextIsStored() throws Exception {\n\t\tfinal MockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfinal TestingAuthenticationToken beforeAuth = new TestingAuthenticationToken(\"someoneelse\", \"passwd\", \"ROLE_B\");\n\t\tfinal SecurityContext scBefore = new SecurityContextImpl();\n\t\tfinal SecurityContext scExpectedAfter = new SecurityContextImpl();\n\t\tscExpectedAfter.setAuthentication(this.testToken);\n\t\tscBefore.setAuthentication(beforeAuth);\n\t\tfinal SecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\tSecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter(repo);\n\t\tgiven(repo.loadContext(any(HttpRequestResponseHolder.class))).willReturn(scBefore);\n\t\tfinal FilterChain chain = (request1, response1) -> {\n\t\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isEqualTo(beforeAuth);\n\t\t\t// Change the context here\n\t\t\tSecurityContextHolder.setContext(scExpectedAfter);\n\t\t};\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(repo).saveContext(scExpectedAfter, request, response);\n\t}\n\n\t@Test\n\tpublic void filterIsNotAppliedAgainIfFilterAppliedAttributeIsSet() throws Exception {\n\t\tfinal FilterChain chain = mock(FilterChain.class);\n\t\tfinal MockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter(\n\t\t\t\tmock(SecurityContextRepository.class));\n\t\trequest.setAttribute(SecurityContextPersistenceFilter.FILTER_APPLIED, Boolean.TRUE);\n\t\tfilter.doFilter(request, response, chain);\n\t\tverify(chain).doFilter(request, response);\n\t}\n\n\t@Test\n\tpublic void sessionIsEagerlyCreatedWhenConfigured() throws Exception {\n\t\tfinal FilterChain chain = mock(FilterChain.class);\n\t\tfinal MockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();\n\t\tfilter.setForceEagerSessionCreation(true);\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void nullSecurityContextRepoDoesntSaveContextOrCreateSession() throws Exception {\n\t\tfinal FilterChain chain = mock(FilterChain.class);\n\t\tfinal MockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfinal MockHttpServletResponse response = new MockHttpServletResponse();\n\t\tSecurityContextRepository repo = new NullSecurityContextRepository();\n\t\tSecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter(repo);\n\t\tfilter.doFilter(request, response, chain);\n\t\tassertThat(repo.containsContext(request)).isFalse();\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/SecurityContextRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.context.DeferredSecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n */\nclass SecurityContextRepositoryTests {\n\n\tSecurityContextRepository repository = spy(SecurityContextRepository.class);\n\n\t@Test\n\tvoid loadContextHttpRequestResponseHolderWhenInvokeSupplierTwiceThenOnlyInvokesLoadContextOnce() {\n\t\tgiven(this.repository.loadContext(any(HttpRequestResponseHolder.class))).willReturn(new SecurityContextImpl());\n\t\tDeferredSecurityContext deferredContext = this.repository.loadDeferredContext(mock(HttpServletRequest.class));\n\t\tverify(this.repository).loadDeferredContext(any(HttpServletRequest.class));\n\t\tdeferredContext.get();\n\t\tverify(this.repository).loadContext(any(HttpRequestResponseHolder.class));\n\t\tdeferredContext.get();\n\t\tverifyNoMoreInteractions(this.repository);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/request/async/SecurityContextCallableProcessingInterceptorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context.request.async;\n\nimport java.util.concurrent.Callable;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.context.request.NativeWebRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class SecurityContextCallableProcessingInterceptorTests {\n\n\t@Mock\n\tprivate SecurityContext securityContext;\n\n\t@Mock\n\tprivate Callable<?> callable;\n\n\t@Mock\n\tprivate NativeWebRequest webRequest;\n\n\t@AfterEach\n\tpublic void clearSecurityContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void constructorNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SecurityContextCallableProcessingInterceptor(null));\n\t}\n\n\t@Test\n\tpublic void currentSecurityContext() throws Exception {\n\t\tSecurityContextCallableProcessingInterceptor interceptor = new SecurityContextCallableProcessingInterceptor();\n\t\tSecurityContextHolder.setContext(this.securityContext);\n\t\tinterceptor.beforeConcurrentHandling(this.webRequest, this.callable);\n\t\tSecurityContextHolder.clearContext();\n\t\tinterceptor.preProcess(this.webRequest, this.callable);\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(this.securityContext);\n\t\tinterceptor.postProcess(this.webRequest, this.callable, null);\n\t\tassertThat(SecurityContextHolder.getContext()).isNotSameAs(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void specificSecurityContext() throws Exception {\n\t\tSecurityContextCallableProcessingInterceptor interceptor = new SecurityContextCallableProcessingInterceptor(\n\t\t\t\tthis.securityContext);\n\t\tinterceptor.preProcess(this.webRequest, this.callable);\n\t\tassertThat(SecurityContextHolder.getContext()).isSameAs(this.securityContext);\n\t\tinterceptor.postProcess(this.webRequest, this.callable, null);\n\t\tassertThat(SecurityContextHolder.getContext()).isNotSameAs(this.securityContext);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/context/request/async/WebAsyncManagerIntegrationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.context.request.async;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ThreadFactory;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.task.SimpleAsyncTaskExecutor;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.context.request.async.AsyncWebRequest;\nimport org.springframework.web.context.request.async.CallableProcessingInterceptor;\nimport org.springframework.web.context.request.async.WebAsyncManager;\nimport org.springframework.web.context.request.async.WebAsyncUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class WebAsyncManagerIntegrationFilterTests {\n\n\t@Mock\n\tprivate SecurityContext securityContext;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\t@Mock\n\tprivate AsyncWebRequest asyncWebRequest;\n\n\tprivate WebAsyncManager asyncManager;\n\n\tprivate JoinableThreadFactory threadFactory;\n\n\tprivate MockFilterChain filterChain;\n\n\tprivate WebAsyncManagerIntegrationFilter filter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.filterChain = new MockFilterChain();\n\t\tthis.threadFactory = new JoinableThreadFactory();\n\t\tSimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();\n\t\texecutor.setThreadFactory(this.threadFactory);\n\t\tthis.asyncManager = WebAsyncUtils.getAsyncManager(this.request);\n\t\tthis.asyncManager.setAsyncWebRequest(this.asyncWebRequest);\n\t\tthis.asyncManager.setTaskExecutor(executor);\n\t\tgiven(this.request.getAttribute(WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE)).willReturn(this.asyncManager);\n\t\tthis.filter = new WebAsyncManagerIntegrationFilter();\n\t}\n\n\t@AfterEach\n\tpublic void clearSecurityContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void doFilterInternalRegistersSecurityContextCallableProcessor() throws Exception {\n\t\tSecurityContextHolder.setContext(this.securityContext);\n\t\tthis.asyncManager.registerCallableInterceptors(new CallableProcessingInterceptor() {\n\t\t\t@Override\n\t\t\tpublic <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {\n\t\t\t\tassertThat(SecurityContextHolder.getContext())\n\t\t\t\t\t.isNotSameAs(WebAsyncManagerIntegrationFilterTests.this.securityContext);\n\t\t\t}\n\t\t});\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tVerifyingCallable verifyingCallable = new VerifyingCallable();\n\t\tthis.asyncManager.startCallableProcessing(verifyingCallable);\n\t\tthis.threadFactory.join();\n\t\tassertThat(this.asyncManager.getConcurrentResult()).isSameAs(this.securityContext);\n\t}\n\n\t@Test\n\tpublic void doFilterInternalRegistersSecurityContextCallableProcessorContextUpdated() throws Exception {\n\t\tSecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());\n\t\tthis.asyncManager.registerCallableInterceptors(new CallableProcessingInterceptor() {\n\t\t\t@Override\n\t\t\tpublic <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {\n\t\t\t\tassertThat(SecurityContextHolder.getContext())\n\t\t\t\t\t.isNotSameAs(WebAsyncManagerIntegrationFilterTests.this.securityContext);\n\t\t\t}\n\t\t});\n\t\tthis.filter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tSecurityContextHolder.setContext(this.securityContext);\n\t\tVerifyingCallable verifyingCallable = new VerifyingCallable();\n\t\tthis.asyncManager.startCallableProcessing(verifyingCallable);\n\t\tthis.threadFactory.join();\n\t\tassertThat(this.asyncManager.getConcurrentResult()).isSameAs(this.securityContext);\n\t}\n\n\tprivate static final class JoinableThreadFactory implements ThreadFactory {\n\n\t\tprivate Thread t;\n\n\t\t@Override\n\t\tpublic Thread newThread(Runnable r) {\n\t\t\tthis.t = new Thread(r);\n\t\t\treturn this.t;\n\t\t}\n\n\t\tvoid join() throws InterruptedException {\n\t\t\tthis.t.join();\n\t\t}\n\n\t}\n\n\tprivate class VerifyingCallable implements Callable<SecurityContext> {\n\n\t\t@Override\n\t\tpublic SecurityContext call() {\n\t\t\treturn SecurityContextHolder.getContext();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/CookieCsrfTokenRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;\n\n/**\n * @author Rob Winch\n * @author Alex Montoya\n * @since 4.1\n */\nclass CookieCsrfTokenRepositoryTests {\n\n\tCookieCsrfTokenRepository repository;\n\n\tMockHttpServletResponse response;\n\n\tMockHttpServletRequest request;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.repository = new CookieCsrfTokenRepository();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.request.setContextPath(\"/context\");\n\t}\n\n\t@Test\n\tvoid generateToken() {\n\t\tCsrfToken generateToken = this.repository.generateToken(this.request);\n\t\tassertThat(generateToken).isNotNull();\n\t\tassertThat(generateToken.getHeaderName()).isEqualTo(CookieCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME);\n\t\tassertThat(generateToken.getParameterName()).isEqualTo(CookieCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME);\n\t\tassertThat(generateToken.getToken()).isNotEmpty();\n\t}\n\n\t@Test\n\tvoid generateTokenCustom() {\n\t\tString headerName = \"headerName\";\n\t\tString parameterName = \"paramName\";\n\t\tthis.repository.setHeaderName(headerName);\n\t\tthis.repository.setParameterName(parameterName);\n\t\tCsrfToken generateToken = this.repository.generateToken(this.request);\n\t\tassertThat(generateToken).isNotNull();\n\t\tassertThat(generateToken.getHeaderName()).isEqualTo(headerName);\n\t\tassertThat(generateToken.getParameterName()).isEqualTo(parameterName);\n\t\tassertThat(generateToken.getToken()).isNotEmpty();\n\t}\n\n\t@Test\n\tvoid saveToken() {\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getMaxAge()).isEqualTo(-1);\n\t\tassertThat(tokenCookie.getName()).isEqualTo(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getPath()).isEqualTo(this.request.getContextPath());\n\t\tassertThat(tokenCookie.getSecure()).isEqualTo(this.request.isSecure());\n\t\tassertThat(tokenCookie.getValue()).isEqualTo(token.getToken());\n\t\tassertThat(tokenCookie.isHttpOnly()).isTrue();\n\t}\n\n\t// gh-14131\n\t@Test\n\tvoid saveTokenShouldUseResponseAddCookie() {\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tMockHttpServletResponse spyResponse = spy(this.response);\n\t\tthis.repository.saveToken(token, this.request, spyResponse);\n\t\tverify(spyResponse).addCookie(any(Cookie.class));\n\t}\n\n\t@Test\n\tvoid saveTokenSecure() {\n\t\tthis.request.setSecure(true);\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getSecure()).isTrue();\n\t}\n\n\t@Test\n\tvoid saveTokenSecureFlagTrue() {\n\t\tthis.request.setSecure(false);\n\t\tthis.repository.setCookieCustomizer((cookie) -> cookie.secure(Boolean.TRUE));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getSecure()).isTrue();\n\t}\n\n\t@Test\n\tvoid saveTokenSecureFlagTrueUsingCustomizer() {\n\t\tthis.request.setSecure(false);\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.secure(Boolean.TRUE));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getSecure()).isTrue();\n\t}\n\n\t@Test\n\tvoid saveTokenSecureFlagFalse() {\n\t\tthis.request.setSecure(true);\n\t\tthis.repository.setCookieCustomizer((cookie) -> cookie.secure(Boolean.FALSE));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getSecure()).isFalse();\n\t}\n\n\t@Test\n\tvoid saveTokenSecureFlagFalseUsingCustomizer() {\n\t\tthis.request.setSecure(true);\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.secure(Boolean.FALSE));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getSecure()).isFalse();\n\t}\n\n\t@Test\n\tvoid saveTokenNull() {\n\t\tthis.request.setSecure(true);\n\t\tthis.repository.saveToken(null, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getMaxAge()).isZero();\n\t\tassertThat(tokenCookie.getName()).isEqualTo(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getPath()).isEqualTo(this.request.getContextPath());\n\t\tassertThat(tokenCookie.getSecure()).isEqualTo(this.request.isSecure());\n\t\tassertThat(tokenCookie.getValue()).isEmpty();\n\t}\n\n\t@Test\n\tvoid saveTokenHttpOnlyTrue() {\n\t\tthis.repository.setCookieCustomizer((cookie) -> cookie.httpOnly(true));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.isHttpOnly()).isTrue();\n\t}\n\n\t@Test\n\tvoid saveTokenHttpOnlyTrueUsingCustomizer() {\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.httpOnly(true));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.isHttpOnly()).isTrue();\n\t}\n\n\t@Test\n\tvoid saveTokenHttpOnlyFalse() {\n\t\tthis.repository.setCookieCustomizer((cookie) -> cookie.httpOnly(false));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.isHttpOnly()).isFalse();\n\t}\n\n\t@Test\n\tvoid saveTokenHttpOnlyFalseUsingCustomizer() {\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.httpOnly(false));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.isHttpOnly()).isFalse();\n\t}\n\n\t@Test\n\tvoid saveTokenWithHttpOnlyFalse() {\n\t\tthis.repository = CookieCsrfTokenRepository.withHttpOnlyFalse();\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.isHttpOnly()).isFalse();\n\t}\n\n\t@Test\n\tvoid saveTokenCustomPath() {\n\t\tString customPath = \"/custompath\";\n\t\tthis.repository.setCookiePath(customPath);\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getPath()).isEqualTo(this.repository.getCookiePath());\n\t}\n\n\t@Test\n\tvoid saveTokenEmptyCustomPath() {\n\t\tString customPath = \"\";\n\t\tthis.repository.setCookiePath(customPath);\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getPath()).isEqualTo(this.request.getContextPath());\n\t}\n\n\t@Test\n\tvoid saveTokenNullCustomPath() {\n\t\tString customPath = null;\n\t\tthis.repository.setCookiePath(customPath);\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getPath()).isEqualTo(this.request.getContextPath());\n\t}\n\n\t@Test\n\tvoid saveTokenWithCookieDomain() {\n\t\tString domainName = \"example.com\";\n\t\tthis.repository.setCookieCustomizer((cookie) -> cookie.domain(domainName));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getDomain()).isEqualTo(domainName);\n\t}\n\n\t@Test\n\tvoid saveTokenWithCookieDomainUsingCustomizer() {\n\t\tString domainName = \"example.com\";\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.domain(domainName));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getDomain()).isEqualTo(domainName);\n\t}\n\n\t@Test\n\tvoid saveTokenWithCookieMaxAge() {\n\t\tint maxAge = 1200;\n\t\tthis.repository.setCookieCustomizer((cookie) -> cookie.maxAge(maxAge));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getMaxAge()).isEqualTo(maxAge);\n\t}\n\n\t@Test\n\tvoid saveTokenWithCookieMaxAgeUsingCustomizer() {\n\t\tint maxAge = 1200;\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.maxAge(maxAge));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getMaxAge()).isEqualTo(maxAge);\n\t}\n\n\t@Test\n\tvoid saveTokenWithSameSiteNull() {\n\t\tString sameSitePolicy = null;\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.sameSite(sameSitePolicy));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getAttribute(\"SameSite\")).isNull();\n\t}\n\n\t@Test\n\tvoid saveTokenWithSameSiteStrict() {\n\t\tString sameSitePolicy = \"Strict\";\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.sameSite(sameSitePolicy));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getAttribute(\"SameSite\")).isEqualTo(sameSitePolicy);\n\t}\n\n\t@Test\n\tvoid saveTokenWithSameSiteLax() {\n\t\tString sameSitePolicy = \"Lax\";\n\t\tthis.repository.setCookieCustomizer((customizer) -> customizer.sameSite(sameSitePolicy));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getAttribute(\"SameSite\")).isEqualTo(sameSitePolicy);\n\t}\n\n\t// gh-13075\n\t@Test\n\tvoid saveTokenWithExistingSetCookieThenDoesNotOverwrite() {\n\t\tthis.response.setHeader(HttpHeaders.SET_COOKIE, \"MyCookie=test\");\n\t\tthis.repository = new CookieCsrfTokenRepository();\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tassertThat(this.response.getCookie(\"MyCookie\")).isNotNull();\n\t\tassertThat(this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME)).isNotNull();\n\t}\n\n\t@Test\n\tvoid loadTokenNoCookiesNull() {\n\t\tassertThat(this.repository.loadToken(this.request)).isNull();\n\t}\n\n\t@Test\n\tvoid loadTokenCookieIncorrectNameNull() {\n\t\tthis.request.setCookies(new Cookie(\"other\", \"name\"));\n\t\tassertThat(this.repository.loadToken(this.request)).isNull();\n\t}\n\n\t@Test\n\tvoid loadTokenCookieValueEmptyString() {\n\t\tthis.request.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, \"\"));\n\t\tassertThat(this.repository.loadToken(this.request)).isNull();\n\t}\n\n\t@Test\n\tvoid loadToken() {\n\t\tCsrfToken generateToken = this.repository.generateToken(this.request);\n\t\tthis.request\n\t\t\t.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, generateToken.getToken()));\n\t\tCsrfToken loadToken = this.repository.loadToken(this.request);\n\t\tassertThat(loadToken).isNotNull();\n\t\tassertThat(loadToken.getHeaderName()).isEqualTo(generateToken.getHeaderName());\n\t\tassertThat(loadToken.getParameterName()).isEqualTo(generateToken.getParameterName());\n\t\tassertThat(loadToken.getToken()).isNotEmpty();\n\t}\n\n\t@Test\n\tvoid loadTokenCustom() {\n\t\tString cookieName = \"cookieName\";\n\t\tString value = \"value\";\n\t\tString headerName = \"headerName\";\n\t\tString parameterName = \"paramName\";\n\t\tthis.repository.setHeaderName(headerName);\n\t\tthis.repository.setParameterName(parameterName);\n\t\tthis.repository.setCookieName(cookieName);\n\t\tthis.request.setCookies(new Cookie(cookieName, value));\n\t\tCsrfToken loadToken = this.repository.loadToken(this.request);\n\t\tassertThat(loadToken).isNotNull();\n\t\tassertThat(loadToken.getHeaderName()).isEqualTo(headerName);\n\t\tassertThat(loadToken.getParameterName()).isEqualTo(parameterName);\n\t\tassertThat(loadToken.getToken()).isEqualTo(value);\n\t}\n\n\t@Test\n\tvoid loadDeferredTokenWhenDoesNotExistThenGeneratedAndSaved() {\n\t\tDeferredCsrfToken deferredCsrfToken = this.repository.loadDeferredToken(this.request, this.response);\n\t\tCsrfToken csrfToken = deferredCsrfToken.get();\n\t\tassertThat(csrfToken).isNotNull();\n\t\tassertThat(deferredCsrfToken.isGenerated()).isTrue();\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie).isNotNull();\n\t\tassertThat(tokenCookie.getMaxAge()).isEqualTo(-1);\n\t\tassertThat(tokenCookie.getName()).isEqualTo(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie.getPath()).isEqualTo(this.request.getContextPath());\n\t\tassertThat(tokenCookie.getSecure()).isEqualTo(this.request.isSecure());\n\t\tassertThat(tokenCookie.getValue()).isEqualTo(csrfToken.getToken());\n\t\tassertThat(tokenCookie.isHttpOnly()).isEqualTo(true);\n\t}\n\n\t@Test\n\tvoid loadDeferredTokenWhenExistsAndNullSavedThenGeneratedAndSaved() {\n\t\tCsrfToken generatedToken = this.repository.generateToken(this.request);\n\t\tthis.request\n\t\t\t.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, generatedToken.getToken()));\n\t\tthis.repository.saveToken(null, this.request, this.response);\n\t\tDeferredCsrfToken deferredCsrfToken = this.repository.loadDeferredToken(this.request, this.response);\n\t\tCsrfToken csrfToken = deferredCsrfToken.get();\n\t\tassertThat(csrfToken).isNotNull();\n\t\tassertThat(generatedToken).isNotEqualTo(csrfToken);\n\t\tassertThat(deferredCsrfToken.isGenerated()).isTrue();\n\t}\n\n\t@Test\n\tvoid loadDeferredTokenWhenExistsAndNullSavedAndNonNullSavedThenLoaded() {\n\t\tCsrfToken generatedToken = this.repository.generateToken(this.request);\n\t\tthis.request\n\t\t\t.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, generatedToken.getToken()));\n\t\tthis.repository.saveToken(null, this.request, this.response);\n\t\tthis.repository.saveToken(generatedToken, this.request, this.response);\n\t\tDeferredCsrfToken deferredCsrfToken = this.repository.loadDeferredToken(this.request, this.response);\n\t\tCsrfToken csrfToken = deferredCsrfToken.get();\n\t\tassertThatCsrfToken(csrfToken).isEqualTo(generatedToken);\n\t\tassertThat(deferredCsrfToken.isGenerated()).isFalse();\n\t}\n\n\t@Test\n\tvoid loadDeferredTokenWhenExistsThenLoaded() {\n\t\tCsrfToken generatedToken = this.repository.generateToken(this.request);\n\t\tthis.request\n\t\t\t.setCookies(new Cookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME, generatedToken.getToken()));\n\t\tDeferredCsrfToken deferredCsrfToken = this.repository.loadDeferredToken(this.request, this.response);\n\t\tCsrfToken csrfToken = deferredCsrfToken.get();\n\t\tassertThatCsrfToken(csrfToken).isEqualTo(generatedToken);\n\t\tassertThat(deferredCsrfToken.isGenerated()).isFalse();\n\t}\n\n\t@Test\n\tvoid cookieCustomizer() {\n\t\tString domainName = \"example.com\";\n\t\tString customPath = \"/custompath\";\n\t\tString sameSitePolicy = \"Strict\";\n\t\tthis.repository.setCookieCustomizer((customizer) -> {\n\t\t\tcustomizer.domain(domainName);\n\t\t\tcustomizer.secure(false);\n\t\t\tcustomizer.path(customPath);\n\t\t\tcustomizer.sameSite(sameSitePolicy);\n\t\t});\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie).isNotNull();\n\t\tassertThat(tokenCookie.getMaxAge()).isEqualTo(-1);\n\t\tassertThat(tokenCookie.getDomain()).isEqualTo(domainName);\n\t\tassertThat(tokenCookie.getPath()).isEqualTo(customPath);\n\t\tassertThat(tokenCookie.isHttpOnly()).isEqualTo(Boolean.TRUE);\n\t\tassertThat(tokenCookie.getAttribute(\"SameSite\")).isEqualTo(sameSitePolicy);\n\t}\n\n\t// gh-13659\n\t@Test\n\tvoid withHttpOnlyFalseWhenCookieCustomizerThenStillDefaultsToFalse() {\n\t\tCookieCsrfTokenRepository repository = CookieCsrfTokenRepository.withHttpOnlyFalse();\n\t\trepository.setCookieCustomizer((customizer) -> customizer.maxAge(1000));\n\t\tCsrfToken token = repository.generateToken(this.request);\n\t\trepository.saveToken(token, this.request, this.response);\n\t\tCookie tokenCookie = this.response.getCookie(CookieCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME);\n\t\tassertThat(tokenCookie).isNotNull();\n\t\tassertThat(tokenCookie.getMaxAge()).isEqualTo(1000);\n\t\tassertThat(tokenCookie.isHttpOnly()).isEqualTo(Boolean.FALSE);\n\t}\n\n\t// gh-16173\n\t@Test\n\tvoid saveTokenWhenSameSiteAndServletVersion5ThenUsesAddHeader() {\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\t((MockServletContext) this.request.getServletContext()).setMajorVersion(5);\n\t\tthis.repository.setCookieCustomizer((builder) -> builder.sameSite(\"Strict\"));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, response);\n\t\tverify(response, never()).addCookie(any(Cookie.class));\n\t\tverify(response).addHeader(any(), any());\n\t}\n\n\t// gh-16173\n\t@Test\n\tvoid saveTokenWhenSameSiteAndServletVersion6OrHigherThenUsesAddCookie() {\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tthis.repository.setCookieCustomizer((builder) -> builder.sameSite(\"Strict\"));\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, response);\n\t\tverify(response).addCookie(any(Cookie.class));\n\t\tverify(response, never()).addHeader(any(), any());\n\t}\n\n\t// gh-16173\n\t@Test\n\tvoid saveTokenWhenNoSameSiteThenUsesAddCookie() {\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tCsrfToken token = this.repository.generateToken(this.request);\n\t\tthis.repository.saveToken(token, this.request, response);\n\t\tverify(response).addCookie(any(Cookie.class));\n\t\tverify(response, never()).addHeader(any(), any());\n\t\t((MockServletContext) this.request.getServletContext()).setMajorVersion(5);\n\t\tresponse = mock(HttpServletResponse.class);\n\t\tthis.repository.saveToken(token, this.request, response);\n\t\tverify(response).addCookie(any(Cookie.class));\n\t\tverify(response, never()).addHeader(any(), any());\n\t}\n\n\t@Test\n\tvoid setCookieNameNullIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repository.setCookieName(null));\n\t}\n\n\t@Test\n\tvoid setParameterNameNullIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repository.setParameterName(null));\n\t}\n\n\t@Test\n\tvoid setHeaderNameNullIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repository.setHeaderName(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/CsrfAuthenticationStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class CsrfAuthenticationStrategyTests {\n\n\t@Mock\n\tprivate CsrfTokenRepository csrfTokenRepository;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate CsrfAuthenticationStrategy strategy;\n\n\tprivate CsrfToken existingToken;\n\n\tprivate CsrfToken generatedToken;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.request.setAttribute(HttpServletResponse.class.getName(), this.response);\n\t\tthis.strategy = new CsrfAuthenticationStrategy(this.csrfTokenRepository);\n\t\tthis.existingToken = new DefaultCsrfToken(\"_csrf\", \"_csrf\", \"1\");\n\t\tthis.generatedToken = new DefaultCsrfToken(\"_csrf\", \"_csrf\", \"2\");\n\t}\n\n\t@Test\n\tpublic void constructorNullCsrfTokenRepository() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CsrfAuthenticationStrategy(null));\n\t}\n\n\t@Test\n\tpublic void setRequestHandlerWhenNullThenIllegalStateException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.strategy.setRequestHandler(null))\n\t\t\t.withMessage(\"requestHandler cannot be null\");\n\t}\n\n\t@Test\n\tpublic void onAuthenticationWhenCustomRequestHandlerThenUsed() {\n\t\tgiven(this.csrfTokenRepository.loadToken(this.request)).willReturn(this.existingToken);\n\t\tgiven(this.csrfTokenRepository.loadDeferredToken(this.request, this.response))\n\t\t\t.willReturn(new TestDeferredCsrfToken(this.existingToken, false));\n\n\t\tCsrfTokenRequestHandler requestHandler = mock(CsrfTokenRequestHandler.class);\n\t\tthis.strategy.setRequestHandler(requestHandler);\n\t\tthis.strategy.onAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"), this.request,\n\t\t\t\tthis.response);\n\t\tverify(this.csrfTokenRepository).loadToken(this.request);\n\t\tverify(this.csrfTokenRepository).loadDeferredToken(this.request, this.response);\n\t\tverify(requestHandler).handle(eq(this.request), eq(this.response), any());\n\t\tverifyNoMoreInteractions(requestHandler);\n\t}\n\n\t@Test\n\tpublic void logoutRemovesCsrfTokenAndLoadsNewDeferredCsrfToken() {\n\t\tgiven(this.csrfTokenRepository.loadToken(this.request)).willReturn(this.existingToken);\n\t\tgiven(this.csrfTokenRepository.loadDeferredToken(this.request, this.response))\n\t\t\t.willReturn(new TestDeferredCsrfToken(this.generatedToken, false));\n\t\tthis.strategy.onAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"), this.request,\n\t\t\t\tthis.response);\n\t\tverify(this.csrfTokenRepository).loadToken(this.request);\n\t\tverify(this.csrfTokenRepository).saveToken(null, this.request, this.response);\n\t\tverify(this.csrfTokenRepository).loadDeferredToken(this.request, this.response);\n\t\t// SEC-2404, SEC-2832\n\t\tCsrfToken tokenInRequest = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(tokenInRequest.getToken()).isNotEmpty();\n\t\tassertThat(tokenInRequest.getToken()).isNotEqualTo(this.generatedToken.getToken());\n\t\tassertThat(tokenInRequest.getHeaderName()).isEqualTo(this.generatedToken.getHeaderName());\n\t\tassertThat(tokenInRequest.getParameterName()).isEqualTo(this.generatedToken.getParameterName());\n\t\tassertThat(this.request.getAttribute(this.generatedToken.getParameterName())).isSameAs(tokenInRequest);\n\t}\n\n\t// SEC-2872\n\t@Test\n\tpublic void delaySavingCsrf() {\n\t\tthis.strategy = new CsrfAuthenticationStrategy(this.csrfTokenRepository);\n\t\tgiven(this.csrfTokenRepository.loadToken(this.request)).willReturn(this.existingToken, (CsrfToken) null);\n\t\tgiven(this.csrfTokenRepository.generateToken(this.request)).willReturn(this.generatedToken);\n\t\tgiven(this.csrfTokenRepository.loadDeferredToken(any(), any())).willCallRealMethod();\n\t\tthis.strategy.onAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"), this.request,\n\t\t\t\tthis.response);\n\t\tverify(this.csrfTokenRepository).saveToken(null, this.request, this.response);\n\t\tverify(this.csrfTokenRepository, never()).saveToken(eq(this.generatedToken), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tCsrfToken tokenInRequest = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\ttokenInRequest.getToken();\n\t\tverify(this.csrfTokenRepository, times(2)).loadToken(this.request);\n\t\tverify(this.csrfTokenRepository).generateToken(this.request);\n\t\tverify(this.csrfTokenRepository).saveToken(eq(this.generatedToken), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/CsrfFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Base64;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.crypto.codec.Utf8;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.lenient;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class CsrfFilterTests {\n\n\t@Mock\n\tprivate RequestMatcher requestMatcher;\n\n\t@Mock\n\tprivate CsrfTokenRepository tokenRepository;\n\n\t@Mock\n\tprivate FilterChain filterChain;\n\n\t@Mock\n\tprivate AccessDeniedHandler deniedHandler;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate CsrfToken token;\n\n\tprivate String csrfAttrName = \"_csrf\";\n\n\tprivate CsrfFilter filter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.token = new DefaultCsrfToken(\"headerName\", \"paramName\", \"csrfTokenValue\");\n\t\tresetRequestResponse();\n\t\tthis.filter = createCsrfFilter(this.tokenRepository);\n\t}\n\n\tprivate CsrfFilter createCsrfFilter(CsrfTokenRepository repository) {\n\t\tCsrfFilter filter = new CsrfFilter(repository);\n\t\tfilter.setRequireCsrfProtectionMatcher(this.requestMatcher);\n\t\tfilter.setAccessDeniedHandler(this.deniedHandler);\n\t\treturn filter;\n\t}\n\n\tprivate void resetRequestResponse() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void constructorNullRepository() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CsrfFilter(null));\n\t}\n\n\t// SEC-2276\n\t@Test\n\tpublic void doFilterDoesNotSaveCsrfTokenUntilAccessed() throws ServletException, IOException {\n\t\tthis.filter = createCsrfFilter(this.tokenRepository);\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(false);\n\t\tgiven(this.tokenRepository.generateToken(this.request)).willReturn(this.token);\n\t\tgiven(this.tokenRepository.loadDeferredToken(any(), any())).willCallRealMethod();\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tCsrfToken attrToken = (CsrfToken) this.request.getAttribute(this.csrfAttrName);\n\t\t// no CsrfToken should have been saved yet\n\t\tverify(this.tokenRepository, times(0)).saveToken(any(CsrfToken.class), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\t// access the token\n\t\tattrToken.getToken();\n\t\t// now the CsrfToken should have been saved\n\t\tverify(this.tokenRepository).saveToken(eq(this.token), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterAccessDeniedNoTokenPresent() throws ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.deniedHandler).handle(eq(this.request), eq(this.response), any(InvalidCsrfTokenException.class));\n\t\tverifyNoMoreInteractions(this.filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterAccessDeniedIncorrectTokenPresent() throws ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tthis.request.setParameter(this.token.getParameterName(), this.token.getToken() + \" INVALID\");\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.deniedHandler).handle(eq(this.request), eq(this.response), any(InvalidCsrfTokenException.class));\n\t\tverifyNoMoreInteractions(this.filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterAccessDeniedIncorrectTokenPresentHeader() throws ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tthis.request.addHeader(this.token.getHeaderName(), this.token.getToken() + \" INVALID\");\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.deniedHandler).handle(eq(this.request), eq(this.response), any(InvalidCsrfTokenException.class));\n\t\tverifyNoMoreInteractions(this.filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterAccessDeniedIncorrectTokenPresentHeaderPreferredOverParameter()\n\t\t\tthrows ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tCsrfTokenRequestHandler handler = new XorCsrfTokenRequestAttributeHandler();\n\t\thandler.handle(this.request, this.response, () -> this.token);\n\t\tCsrfToken csrfToken = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tthis.request.setParameter(csrfToken.getParameterName(), csrfToken.getToken());\n\t\tthis.request.addHeader(csrfToken.getHeaderName(), csrfToken.getToken() + \" INVALID\");\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.deniedHandler).handle(eq(this.request), eq(this.response), any(InvalidCsrfTokenException.class));\n\t\tverifyNoMoreInteractions(this.filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterNotCsrfRequestExistingToken() throws ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(false);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\tverifyNoMoreInteractions(this.deniedHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterNotCsrfRequestGenerateToken() throws ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(false);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, true);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\tverifyNoMoreInteractions(this.deniedHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterIsCsrfRequestExistingTokenHeader() throws ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tCsrfTokenRequestHandler handler = new XorCsrfTokenRequestAttributeHandler();\n\t\thandler.handle(this.request, this.response, () -> this.token);\n\t\tCsrfToken csrfToken = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tthis.request.addHeader(csrfToken.getHeaderName(), csrfToken.getToken());\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\tverifyNoMoreInteractions(this.deniedHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterIsCsrfRequestExistingTokenHeaderPreferredOverInvalidParam()\n\t\t\tthrows ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tCsrfTokenRequestHandler handler = new XorCsrfTokenRequestAttributeHandler();\n\t\thandler.handle(this.request, this.response, () -> this.token);\n\t\tCsrfToken csrfToken = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tthis.request.setParameter(csrfToken.getParameterName(), csrfToken.getToken() + \" INVALID\");\n\t\tthis.request.addHeader(csrfToken.getHeaderName(), csrfToken.getToken());\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\tverifyNoMoreInteractions(this.deniedHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterIsCsrfRequestExistingToken() throws ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tCsrfTokenRequestHandler handler = new XorCsrfTokenRequestAttributeHandler();\n\t\thandler.handle(this.request, this.response, () -> this.token);\n\t\tCsrfToken csrfToken = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tthis.request.setParameter(csrfToken.getParameterName(), csrfToken.getToken());\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\tverifyNoMoreInteractions(this.deniedHandler);\n\t\tverify(this.tokenRepository, never()).saveToken(any(CsrfToken.class), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void doFilterIsCsrfRequestGenerateToken() throws ServletException, IOException {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, true);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tCsrfTokenRequestHandler handler = new XorCsrfTokenRequestAttributeHandler();\n\t\thandler.handle(this.request, this.response, () -> this.token);\n\t\tCsrfToken csrfToken = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tthis.request.setParameter(csrfToken.getParameterName(), csrfToken.getToken());\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\tverifyNoMoreInteractions(this.deniedHandler);\n\t}\n\n\t@Test\n\tpublic void doFilterDefaultRequireCsrfProtectionMatcherAllowedMethods() throws ServletException, IOException {\n\t\tthis.filter = new CsrfFilter(this.tokenRepository);\n\t\tthis.filter.setAccessDeniedHandler(this.deniedHandler);\n\t\tfor (String method : Arrays.asList(\"GET\", \"TRACE\", \"OPTIONS\", \"HEAD\")) {\n\t\t\tresetRequestResponse();\n\t\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response))\n\t\t\t\t.willReturn(new TestDeferredCsrfToken(this.token, false));\n\t\t\tthis.request.setMethod(method);\n\t\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\t\tverifyNoMoreInteractions(this.deniedHandler);\n\t\t}\n\t}\n\n\t/**\n\t * SEC-2292 Should not allow other cases through since spec states HTTP method is case\n\t * sensitive https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1\n\t * @throws Exception if an error occurs\n\t *\n\t */\n\t@Test\n\tpublic void doFilterDefaultRequireCsrfProtectionMatcherAllowedMethodsCaseSensitive() throws Exception {\n\t\tthis.filter = new CsrfFilter(this.tokenRepository);\n\t\tthis.filter.setAccessDeniedHandler(this.deniedHandler);\n\t\tfor (String method : Arrays.asList(\"get\", \"TrAcE\", \"oPTIOnS\", \"hEaD\")) {\n\t\t\tresetRequestResponse();\n\t\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response))\n\t\t\t\t.willReturn(new TestDeferredCsrfToken(this.token, false));\n\t\t\tthis.request.setMethod(method);\n\t\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\t\tverify(this.deniedHandler).handle(eq(this.request), eq(this.response),\n\t\t\t\t\tany(InvalidCsrfTokenException.class));\n\t\t\tverifyNoMoreInteractions(this.filterChain);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void doFilterDefaultRequireCsrfProtectionMatcherDeniedMethods() throws ServletException, IOException {\n\t\tthis.filter = new CsrfFilter(this.tokenRepository);\n\t\tthis.filter.setAccessDeniedHandler(this.deniedHandler);\n\t\tfor (String method : Arrays.asList(\"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"INVALID\")) {\n\t\t\tresetRequestResponse();\n\t\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response))\n\t\t\t\t.willReturn(new TestDeferredCsrfToken(this.token, false));\n\t\t\tthis.request.setMethod(method);\n\t\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\t\tverify(this.deniedHandler).handle(eq(this.request), eq(this.response),\n\t\t\t\t\tany(InvalidCsrfTokenException.class));\n\t\t\tverifyNoMoreInteractions(this.filterChain);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void doFilterDefaultAccessDenied() throws ServletException, IOException {\n\t\tthis.filter = new CsrfFilter(this.tokenRepository);\n\t\tthis.filter.setRequireCsrfProtectionMatcher(this.requestMatcher);\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThatCsrfToken(this.request.getAttribute(this.csrfAttrName)).isNotNull();\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);\n\t\tverifyNoMoreInteractions(this.filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSkipRequestInvokedThenSkips() throws Exception {\n\t\tCsrfTokenRepository repository = mock(CsrfTokenRepository.class);\n\t\tCsrfFilter filter = new CsrfFilter(repository);\n\t\tlenient().when(repository.loadToken(any(HttpServletRequest.class))).thenReturn(this.token);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tCsrfFilter.skipRequest(request);\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tverifyNoMoreInteractions(repository);\n\t}\n\n\t// gh-9561\n\t@Test\n\tpublic void doFilterWhenTokenIsNullThenNoNullPointer() throws Exception {\n\t\tCsrfFilter filter = createCsrfFilter(this.tokenRepository);\n\t\tCsrfToken token = mock(CsrfToken.class);\n\t\tgiven(token.getToken()).willReturn(null);\n\t\tgiven(token.getHeaderName()).willReturn(this.token.getHeaderName());\n\t\tgiven(token.getParameterName()).willReturn(this.token.getParameterName());\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tfilter.doFilterInternal(this.request, this.response, this.filterChain);\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenRequestHandlerThenUsed() throws Exception {\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tCsrfTokenRequestHandler requestHandler = mock(CsrfTokenRequestHandler.class);\n\t\tthis.filter = createCsrfFilter(this.tokenRepository);\n\t\tthis.filter.setRequestHandler(requestHandler);\n\t\tthis.request.setParameter(this.token.getParameterName(), this.token.getToken());\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.tokenRepository).loadDeferredToken(this.request, this.response);\n\t\tverify(requestHandler).handle(eq(this.request), eq(this.response), any());\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenXorCsrfTokenRequestAttributeHandlerAndValidTokenThenSuccess() throws Exception {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(false);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(\"_csrf\")).isNotNull();\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.filterChain).doFilter(this.request, this.response);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);\n\n\t\tCsrfToken csrfTokenAttribute = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tbyte[] csrfTokenAttributeBytes = Base64.getUrlDecoder().decode(csrfTokenAttribute.getToken());\n\t\tbyte[] actualTokenBytes = Utf8.encode(this.token.getToken());\n\t\t// XOR'd token length is 2x due to containing the random bytes\n\t\tassertThat(csrfTokenAttributeBytes).hasSize(actualTokenBytes.length * 2);\n\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tthis.request.setParameter(this.token.getParameterName(), csrfTokenAttribute.getToken());\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tverify(this.filterChain, times(2)).doFilter(this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenXorCsrfTokenRequestAttributeHandlerAndRawTokenThenAccessDeniedException() throws Exception {\n\t\tgiven(this.requestMatcher.matches(this.request)).willReturn(true);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(this.token, false);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\t\tthis.request.setParameter(this.token.getParameterName(), this.token.getToken());\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\t\tverify(this.deniedHandler).handle(eq(this.request), eq(this.response), any(AccessDeniedException.class));\n\t\tverifyNoMoreInteractions(this.filterChain);\n\t}\n\n\t@Test\n\tpublic void setRequireCsrfProtectionMatcherNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequireCsrfProtectionMatcher(null));\n\t}\n\n\t@Test\n\tpublic void setAccessDeniedHandlerNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAccessDeniedHandler(null));\n\t}\n\n\t// This ensures that the HttpSession on get requests unless the CsrfToken is used\n\t@Test\n\tpublic void doFilterWhenCsrfRequestAttributeNameThenNoCsrfTokenMethodInvokedOnGet()\n\t\t\tthrows ServletException, IOException {\n\t\tCsrfFilter filter = createCsrfFilter(this.tokenRepository);\n\t\tString csrfAttrName = \"_csrf\";\n\t\tCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();\n\t\trequestHandler.setCsrfRequestAttributeName(csrfAttrName);\n\t\tfilter.setRequestHandler(requestHandler);\n\t\tCsrfToken expectedCsrfToken = mock(CsrfToken.class);\n\t\tDeferredCsrfToken deferredCsrfToken = new TestDeferredCsrfToken(expectedCsrfToken, true);\n\t\tgiven(this.tokenRepository.loadDeferredToken(this.request, this.response)).willReturn(deferredCsrfToken);\n\n\t\tfilter.doFilter(this.request, this.response, this.filterChain);\n\t\tassertThat(this.request.getAttribute(DeferredCsrfToken.class.getName())).isSameAs(deferredCsrfToken);\n\n\t\tverifyNoInteractions(expectedCsrfToken);\n\t\tCsrfToken tokenFromRequest = (CsrfToken) this.request.getAttribute(csrfAttrName);\n\t\tassertThatCsrfToken(tokenFromRequest).isNotNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/CsrfLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 3.2\n */\n@ExtendWith(MockitoExtension.class)\npublic class CsrfLogoutHandlerTests {\n\n\t@Mock\n\tprivate CsrfTokenRepository csrfTokenRepository;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate CsrfLogoutHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.handler = new CsrfLogoutHandler(this.csrfTokenRepository);\n\t}\n\n\t@Test\n\tpublic void constructorNullCsrfTokenRepository() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CsrfLogoutHandler(null));\n\t}\n\n\t@Test\n\tpublic void logoutRemovesCsrfToken() {\n\t\tthis.handler.logout(this.request, this.response,\n\t\t\t\tnew TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"));\n\t\tverify(this.csrfTokenRepository).saveToken(null, this.request, this.response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/CsrfTokenAssert.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.assertj.core.api.Assertions;\n\n/**\n * Assertion for validating the properties on CsrfToken are the same.\n */\npublic class CsrfTokenAssert extends AbstractAssert<CsrfTokenAssert, CsrfToken> {\n\n\tprotected CsrfTokenAssert(CsrfToken csrfToken) {\n\t\tsuper(csrfToken, CsrfTokenAssert.class);\n\t}\n\n\tpublic static CsrfTokenAssert assertThatCsrfToken(Object csrfToken) {\n\t\treturn new CsrfTokenAssert((CsrfToken) csrfToken);\n\t}\n\n\tpublic static CsrfTokenAssert assertThat(CsrfToken csrfToken) {\n\t\treturn new CsrfTokenAssert(csrfToken);\n\t}\n\n\tpublic CsrfTokenAssert isEqualTo(CsrfToken csrfToken) {\n\t\tisNotNull();\n\t\tassertThat(csrfToken).isNotNull();\n\t\tAssertions.assertThat(this.actual.getHeaderName()).isEqualTo(csrfToken.getHeaderName());\n\t\tAssertions.assertThat(this.actual.getParameterName()).isEqualTo(csrfToken.getParameterName());\n\t\tAssertions.assertThat(this.actual.getToken()).isEqualTo(csrfToken.getToken());\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/CsrfTokenRequestAttributeHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;\n\n/**\n * Tests for {@link CsrfTokenRequestAttributeHandler}.\n *\n * @author Steve Riesenberg\n * @since 5.8\n */\npublic class CsrfTokenRequestAttributeHandlerTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate CsrfToken token;\n\n\tprivate CsrfTokenRequestAttributeHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.token = new DefaultCsrfToken(\"headerName\", \"paramName\", \"csrfTokenValue\");\n\t\tthis.handler = new CsrfTokenRequestAttributeHandler();\n\t}\n\n\t@Test\n\tpublic void handleWhenRequestIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.handler.handle(null, this.response, () -> this.token))\n\t\t\t.withMessage(\"request cannot be null\");\n\t}\n\n\t@Test\n\tpublic void handleWhenResponseIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(this.request, null, () -> this.token))\n\t\t\t\t.withMessage(\"response cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfTokenSupplierIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.handle(this.request, this.response, null))\n\t\t\t.withMessage(\"deferredCsrfToken cannot be null\");\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfTokenIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tthis.handler.setCsrfRequestAttributeName(null);\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(this.request, this.response, () -> null))\n\t\t\t\t.withMessage(\"csrfTokenSupplier returned null delegate\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfRequestAttributeSetThenUsed() {\n\t\tthis.handler.setCsrfRequestAttributeName(\"_csrf\");\n\t\tthis.handler.handle(this.request, this.response, () -> this.token);\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isEqualTo(this.token);\n\t\tassertThatCsrfToken(this.request.getAttribute(\"_csrf\")).isEqualTo(this.token);\n\t}\n\n\t@Test\n\tpublic void handleWhenValidParametersThenRequestAttributesSet() {\n\t\tthis.handler.handle(this.request, this.response, () -> this.token);\n\t\tassertThatCsrfToken(this.request.getAttribute(CsrfToken.class.getName())).isEqualTo(this.token);\n\t\tassertThatCsrfToken(this.request.getAttribute(\"_csrf\")).isEqualTo(this.token);\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenRequestIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.resolveCsrfTokenValue(null, this.token))\n\t\t\t.withMessage(\"request cannot be null\");\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenCsrfTokenIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.resolveCsrfTokenValue(this.request, null))\n\t\t\t.withMessage(\"csrfToken cannot be null\");\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenTokenNotSetThenReturnsNull() {\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenParameterSetThenReturnsTokenValue() {\n\t\tthis.request.setParameter(this.token.getParameterName(), this.token.getToken());\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isEqualTo(this.token.getToken());\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenHeaderSetThenReturnsTokenValue() {\n\t\tthis.request.addHeader(this.token.getHeaderName(), this.token.getToken());\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isEqualTo(this.token.getToken());\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenHeaderAndParameterSetThenHeaderIsPreferred() {\n\t\tthis.request.addHeader(this.token.getHeaderName(), \"header\");\n\t\tthis.request.setParameter(this.token.getParameterName(), \"parameter\");\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isEqualTo(\"header\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/DefaultCsrfTokenTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n *\n */\npublic class DefaultCsrfTokenTests {\n\n\tprivate final String headerName = \"headerName\";\n\n\tprivate final String parameterName = \"parameterName\";\n\n\tprivate final String tokenValue = \"tokenValue\";\n\n\t@Test\n\tpublic void constructorNullHeaderName() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultCsrfToken(null, this.parameterName, this.tokenValue));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyHeaderName() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultCsrfToken(\"\", this.parameterName, this.tokenValue));\n\t}\n\n\t@Test\n\tpublic void constructorNullParameterName() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultCsrfToken(this.headerName, null, this.tokenValue));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyParameterName() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultCsrfToken(this.headerName, \"\", this.tokenValue));\n\t}\n\n\t@Test\n\tpublic void constructorNullTokenValue() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultCsrfToken(this.headerName, this.parameterName, null));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyTokenValue() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultCsrfToken(this.headerName, this.parameterName, \"\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/HttpSessionCsrfTokenRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;\n\n/**\n * @author Rob Winch\n *\n */\npublic class HttpSessionCsrfTokenRepositoryTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate CsrfToken token;\n\n\tprivate HttpSessionCsrfTokenRepository repo;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.repo = new HttpSessionCsrfTokenRepository();\n\t}\n\n\t@Test\n\tpublic void generateToken() {\n\t\tthis.token = this.repo.generateToken(this.request);\n\t\tassertThat(this.token.getParameterName()).isEqualTo(\"_csrf\");\n\t\tassertThat(this.token.getToken()).isNotEmpty();\n\t\tCsrfToken loadedToken = this.repo.loadToken(this.request);\n\t\tassertThat(loadedToken).isNull();\n\t}\n\n\t@Test\n\tpublic void generateCustomParameter() {\n\t\tString paramName = \"_csrf\";\n\t\tthis.repo.setParameterName(paramName);\n\t\tthis.token = this.repo.generateToken(this.request);\n\t\tassertThat(this.token.getParameterName()).isEqualTo(paramName);\n\t\tassertThat(this.token.getToken()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void generateCustomHeader() {\n\t\tString headerName = \"CSRF\";\n\t\tthis.repo.setHeaderName(headerName);\n\t\tthis.token = this.repo.generateToken(this.request);\n\t\tassertThat(this.token.getHeaderName()).isEqualTo(headerName);\n\t\tassertThat(this.token.getToken()).isNotEmpty();\n\t}\n\n\t@Test\n\tpublic void loadTokenNull() {\n\t\tassertThat(this.repo.loadToken(this.request)).isNull();\n\t\tassertThat(this.request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void loadTokenNullWhenSessionExists() {\n\t\tthis.request.getSession();\n\t\tassertThat(this.repo.loadToken(this.request)).isNull();\n\t}\n\n\t@Test\n\tpublic void loadDeferredTokenWhenDoesNotExistThenGeneratedAndSaved() {\n\t\tDeferredCsrfToken deferredCsrfToken = this.repo.loadDeferredToken(this.request, this.response);\n\t\tCsrfToken csrfToken = deferredCsrfToken.get();\n\t\tassertThat(csrfToken).isNotNull();\n\t\tassertThat(deferredCsrfToken.isGenerated()).isTrue();\n\t\tString attrName = this.request.getSession().getAttributeNames().nextElement();\n\t\tassertThatCsrfToken(this.request.getSession().getAttribute(attrName)).isEqualTo(csrfToken);\n\t}\n\n\t@Test\n\tpublic void loadDeferredTokenWhenExistsThenLoaded() {\n\t\tCsrfToken tokenToSave = new DefaultCsrfToken(\"123\", \"abc\", \"def\");\n\t\tthis.repo.saveToken(tokenToSave, this.request, this.response);\n\t\tDeferredCsrfToken deferredCsrfToken = this.repo.loadDeferredToken(this.request, this.response);\n\t\tCsrfToken csrfToken = deferredCsrfToken.get();\n\t\tassertThatCsrfToken(csrfToken).isEqualTo(tokenToSave);\n\t\tassertThat(deferredCsrfToken.isGenerated()).isFalse();\n\t}\n\n\t@Test\n\tpublic void saveToken() {\n\t\tCsrfToken tokenToSave = new DefaultCsrfToken(\"123\", \"abc\", \"def\");\n\t\tthis.repo.saveToken(tokenToSave, this.request, this.response);\n\t\tString attrName = this.request.getSession().getAttributeNames().nextElement();\n\t\tCsrfToken loadedToken = (CsrfToken) this.request.getSession().getAttribute(attrName);\n\t\tassertThat(loadedToken).isEqualTo(tokenToSave);\n\t}\n\n\t@Test\n\tpublic void saveTokenCustomSessionAttribute() {\n\t\tCsrfToken tokenToSave = new DefaultCsrfToken(\"123\", \"abc\", \"def\");\n\t\tString sessionAttributeName = \"custom\";\n\t\tthis.repo.setSessionAttributeName(sessionAttributeName);\n\t\tthis.repo.saveToken(tokenToSave, this.request, this.response);\n\t\tCsrfToken loadedToken = (CsrfToken) this.request.getSession().getAttribute(sessionAttributeName);\n\t\tassertThat(loadedToken).isEqualTo(tokenToSave);\n\t}\n\n\t@Test\n\tpublic void saveTokenNullToken() {\n\t\tsaveToken();\n\t\tthis.repo.saveToken(null, this.request, this.response);\n\t\tassertThat(this.request.getSession().getAttributeNames().hasMoreElements()).isFalse();\n\t}\n\n\t@Test\n\tpublic void saveTokenNullTokenWhenSessionNotExists() {\n\t\tthis.repo.saveToken(null, this.request, this.response);\n\t\tassertThat(this.request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void setSessionAttributeNameEmpty() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repo.setSessionAttributeName(\"\"));\n\t}\n\n\t@Test\n\tpublic void setSessionAttributeNameNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repo.setSessionAttributeName(null));\n\t}\n\n\t@Test\n\tpublic void setParameterNameEmpty() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repo.setParameterName(\"\"));\n\t}\n\n\t@Test\n\tpublic void setParameterNameNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.repo.setParameterName(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/MissingCsrfTokenExceptionTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author Rob Winch\n *\n */\npublic class MissingCsrfTokenExceptionTests {\n\n\t// CsrfChannelInterceptor requires this to work\n\t@Test\n\tpublic void nullExpectedTokenDoesNotFail() {\n\t\tnew MissingCsrfTokenException(null);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/TestDeferredCsrfToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nfinal class TestDeferredCsrfToken implements DeferredCsrfToken {\n\n\tprivate final CsrfToken csrfToken;\n\n\tprivate final boolean isGenerated;\n\n\tTestDeferredCsrfToken(CsrfToken csrfToken, boolean isGenerated) {\n\t\tthis.csrfToken = csrfToken;\n\t\tthis.isGenerated = isGenerated;\n\t}\n\n\t@Override\n\tpublic CsrfToken get() {\n\t\treturn this.csrfToken;\n\t}\n\n\t@Override\n\tpublic boolean isGenerated() {\n\t\treturn this.isGenerated;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.csrf;\n\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.stubbing.Answer;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link XorCsrfTokenRequestAttributeHandler}.\n *\n * @author Steve Riesenberg\n * @since 5.8\n */\npublic class XorCsrfTokenRequestAttributeHandlerTests {\n\n\t/*\n\t * Token format: 3 random pad bytes + 3 padded bytes.\n\t */\n\tprivate static final byte[] XOR_CSRF_TOKEN_BYTES = new byte[] { 1, 1, 1, 96, 99, 98 };\n\n\tprivate static final String XOR_CSRF_TOKEN_VALUE = Base64.getEncoder().encodeToString(XOR_CSRF_TOKEN_BYTES);\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate CsrfToken token;\n\n\tprivate SecureRandom secureRandom;\n\n\tprivate XorCsrfTokenRequestAttributeHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.token = new DefaultCsrfToken(\"headerName\", \"paramName\", \"abc\");\n\t\tthis.secureRandom = mock(SecureRandom.class);\n\t\tthis.handler = new XorCsrfTokenRequestAttributeHandler();\n\t}\n\n\t@Test\n\tpublic void setSecureRandomWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.setSecureRandom(null))\n\t\t\t\t.withMessage(\"secureRandom cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenRequestIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(null, this.response, () -> this.token))\n\t\t\t\t.withMessage(\"request cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenResponseIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(this.request, null, () -> this.token))\n\t\t\t\t.withMessage(\"response cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfTokenSupplierIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(this.request, this.response, null))\n\t\t\t\t.withMessage(\"deferredCsrfToken cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfTokenIsNullThenThrowsIllegalStateException() {\n\t\tthis.handler.handle(this.request, this.response, () -> null);\n\t\tCsrfToken csrfTokenAttribute = (CsrfToken) this.request.getAttribute(\"_csrf\");\n\t\t// @formatter:off\n\t\tassertThatIllegalStateException()\n\t\t\t\t.isThrownBy(csrfTokenAttribute::getToken)\n\t\t\t\t.withMessage(\"csrfToken supplier returned null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfRequestAttributeSetThenUsed() {\n\t\twillAnswer(fillByteArray()).given(this.secureRandom).nextBytes(anyByteArray());\n\n\t\tthis.handler.setSecureRandom(this.secureRandom);\n\t\tthis.handler.setCsrfRequestAttributeName(\"_csrf\");\n\t\tthis.handler.handle(this.request, this.response, () -> this.token);\n\t\tassertThat(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(\"_csrf\")).isNotNull();\n\n\t\tCsrfToken csrfTokenAttribute = (CsrfToken) this.request.getAttribute(\"_csrf\");\n\t\tassertThat(csrfTokenAttribute.getToken()).isEqualTo(XOR_CSRF_TOKEN_VALUE);\n\t}\n\n\t@Test\n\tpublic void handleWhenSecureRandomSetThenUsed() {\n\t\twillAnswer(fillByteArray()).given(this.secureRandom).nextBytes(anyByteArray());\n\n\t\tthis.handler.setSecureRandom(this.secureRandom);\n\t\tthis.handler.handle(this.request, this.response, () -> this.token);\n\t\tCsrfToken csrfTokenAttribute = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(csrfTokenAttribute.getToken()).isEqualTo(XOR_CSRF_TOKEN_VALUE);\n\t\tverify(this.secureRandom).nextBytes(anyByteArray());\n\t\tverifyNoMoreInteractions(this.secureRandom);\n\t}\n\n\t@Test\n\tpublic void handleWhenValidParametersThenRequestAttributesSet() {\n\t\twillAnswer(fillByteArray()).given(this.secureRandom).nextBytes(anyByteArray());\n\n\t\tthis.handler.setSecureRandom(this.secureRandom);\n\t\tthis.handler.handle(this.request, this.response, () -> this.token);\n\t\tCsrfToken csrfTokenAttribute = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(csrfTokenAttribute.getToken()).isEqualTo(XOR_CSRF_TOKEN_VALUE);\n\t\tverify(this.secureRandom).nextBytes(anyByteArray());\n\t\tassertThat(this.request.getAttribute(CsrfToken.class.getName())).isNotNull();\n\t\tassertThat(this.request.getAttribute(\"_csrf\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfTokenRequestedTwiceThenCached() {\n\t\tthis.handler.handle(this.request, this.response, () -> this.token);\n\n\t\tCsrfToken csrfTokenAttribute = (CsrfToken) this.request.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(csrfTokenAttribute.getToken()).isNotEqualTo(this.token.getToken());\n\t\tassertThat(csrfTokenAttribute.getToken()).isEqualTo(csrfTokenAttribute.getToken());\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenRequestIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.resolveCsrfTokenValue(null, this.token))\n\t\t\t\t.withMessage(\"request cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenCsrfTokenIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.resolveCsrfTokenValue(this.request, null))\n\t\t\t\t.withMessage(\"csrfToken cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenTokenNotSetThenReturnsNull() {\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenParameterSetThenReturnsTokenValue() {\n\t\tthis.request.setParameter(this.token.getParameterName(), XOR_CSRF_TOKEN_VALUE);\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isEqualTo(this.token.getToken());\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenHeaderSetThenReturnsTokenValue() {\n\t\tthis.request.addHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isEqualTo(this.token.getToken());\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenHeaderAndParameterSetThenHeaderIsPreferred() {\n\t\tthis.request.addHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);\n\t\tthis.request.setParameter(this.token.getParameterName(), \"invalid\");\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isEqualTo(this.token.getToken());\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenCsrfBytesIsShorterThanRandomBytesThenReturnsNull() {\n\t\t/*\n\t\t * Token format: 3 random pad bytes + 2 padded bytes.\n\t\t */\n\t\tbyte[] actualBytes = { 1, 1, 1, 96, 99 };\n\t\tString actualToken = Base64.getEncoder().encodeToString(actualBytes);\n\t\tthis.request.setParameter(this.token.getParameterName(), actualToken);\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenCsrfBytesIsLongerThanRandomBytesThenReturnsNull() {\n\t\t/*\n\t\t * Token format: 3 random pad bytes + 4 padded bytes.\n\t\t */\n\t\tbyte[] actualBytes = { 1, 1, 1, 96, 99, 98, 97 };\n\t\tString actualToken = Base64.getEncoder().encodeToString(actualBytes);\n\t\tthis.request.setParameter(this.token.getParameterName(), actualToken);\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenTokenBytesIsShorterThanActualBytesThenReturnsNull() {\n\t\tthis.request.setParameter(this.token.getParameterName(), XOR_CSRF_TOKEN_VALUE);\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"headerName\", \"paramName\", \"a\");\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, csrfToken);\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenTokenBytesIsLongerThanActualBytesThenReturnsNull() {\n\t\tthis.request.setParameter(this.token.getParameterName(), XOR_CSRF_TOKEN_VALUE);\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"headerName\", \"paramName\", \"abcde\");\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, csrfToken);\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenActualBytesIsEmptyThenReturnsNull() {\n\t\tthis.request.setParameter(this.token.getParameterName(), \"\");\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.request, this.token);\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\tprivate static Answer<Void> fillByteArray() {\n\t\treturn (invocation) -> {\n\t\t\tbyte[] bytes = invocation.getArgument(0);\n\t\t\tArrays.fill(bytes, (byte) 1);\n\t\t\treturn null;\n\t\t};\n\t}\n\n\tprivate static byte[] anyByteArray() {\n\t\treturn any(byte[].class);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/debug/DebugFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.debug;\n\nimport java.util.Collections;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.FilterChainProxy;\nimport org.springframework.security.web.debug.DebugFilter.DebugRequestWrapper;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class DebugFilterTests {\n\n\t@Captor\n\tprivate ArgumentCaptor<HttpServletRequest> requestCaptor;\n\n\t@Captor\n\tprivate ArgumentCaptor<String> logCaptor;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\t@Mock\n\tprivate FilterChain filterChain;\n\n\t@Mock\n\tprivate FilterChainProxy fcp;\n\n\t@Mock\n\tprivate Logger logger;\n\n\tprivate String requestAttr;\n\n\tprivate DebugFilter filter;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.filter = new DebugFilter(this.fcp);\n\t\tReflectionTestUtils.setField(this.filter, \"logger\", this.logger);\n\t\tthis.requestAttr = DebugFilter.ALREADY_FILTERED_ATTR_NAME;\n\t}\n\n\tprivate void setupMocks() {\n\t\tgiven(this.request.getHeaderNames()).willReturn(Collections.enumeration(Collections.<String>emptyList()));\n\t\tgiven(this.request.getServletPath()).willReturn(\"/login\");\n\t}\n\n\t@Test\n\tpublic void doFilterProcessesRequests() throws Exception {\n\t\tsetupMocks();\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tverify(this.logger).info(anyString());\n\t\tverify(this.request).setAttribute(this.requestAttr, Boolean.TRUE);\n\t\tverify(this.fcp).doFilter(this.requestCaptor.capture(), eq(this.response), eq(this.filterChain));\n\t\tassertThat(this.requestCaptor.getValue().getClass()).isEqualTo(DebugRequestWrapper.class);\n\t\tverify(this.request).removeAttribute(this.requestAttr);\n\t}\n\n\t// SEC-1901\n\t@Test\n\tpublic void doFilterProcessesForwardedRequests() throws Exception {\n\t\tsetupMocks();\n\t\tgiven(this.request.getAttribute(this.requestAttr)).willReturn(Boolean.TRUE);\n\t\tHttpServletRequest request = new DebugRequestWrapper(this.request);\n\t\tthis.filter.doFilter(request, this.response, this.filterChain);\n\t\tverify(this.logger).info(anyString());\n\t\tverify(this.fcp).doFilter(request, this.response, this.filterChain);\n\t\tverify(this.request, never()).removeAttribute(this.requestAttr);\n\t}\n\n\t@Test\n\tpublic void doFilterDoesNotWrapWithDebugRequestWrapperAgain() throws Exception {\n\t\tsetupMocks();\n\t\tgiven(this.request.getAttribute(this.requestAttr)).willReturn(Boolean.TRUE);\n\t\tHttpServletRequest fireWalledRequest = new HttpServletRequestWrapper(new DebugRequestWrapper(this.request));\n\t\tthis.filter.doFilter(fireWalledRequest, this.response, this.filterChain);\n\t\tverify(this.fcp).doFilter(fireWalledRequest, this.response, this.filterChain);\n\t}\n\n\t@Test\n\tpublic void doFilterLogsProperly() throws Exception {\n\t\tMockHttpServletRequest request = get().requestUri(null, \"/path\", \"/\").build();\n\t\trequest.addHeader(\"A\", \"A Value\");\n\t\trequest.addHeader(\"A\", \"Another Value\");\n\t\trequest.addHeader(\"B\", \"B Value\");\n\t\tthis.filter.doFilter(request, this.response, this.filterChain);\n\t\tverify(this.logger).info(this.logCaptor.capture());\n\t\tassertThat(this.logCaptor.getValue()).isEqualTo(\"Request received for GET '/path/':\\n\" + \"\\n\" + request + \"\\n\"\n\t\t\t\t+ \"\\n\" + \"servletPath:/path\\n\" + \"pathInfo:/\\n\" + \"headers: \\n\" + \"A: A Value, Another Value\\n\"\n\t\t\t\t+ \"B: B Value\\n\" + \"\\n\" + \"\\n\" + \"Security filter chain: no match\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/firewall/CompositeRequestRejectedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\npublic class CompositeRequestRejectedHandlerTests {\n\n\t@Test\n\tvoid compositeRequestRejectedHandlerRethrowsTheException() {\n\t\tRequestRejectedException requestRejectedException = new RequestRejectedException(\"rejected\");\n\t\tCompositeRequestRejectedHandler handler = new CompositeRequestRejectedHandler(\n\t\t\t\tnew DefaultRequestRejectedHandler());\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> handler.handle(mock(HttpServletRequest.class), mock(HttpServletResponse.class),\n\t\t\t\t\trequestRejectedException))\n\t\t\t.withMessage(\"rejected\");\n\t}\n\n\t@Test\n\tvoid compositeRequestRejectedHandlerForbidsEmptyHandlers() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(CompositeRequestRejectedHandler::new);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/firewall/DefaultHttpFirewallTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * @author Luke Taylor\n */\npublic class DefaultHttpFirewallTests {\n\n\tpublic String[] unnormalizedPaths = { \"/..\", \"/./path/\", \"/path/path/.\", \"/path/path//.\", \"./path/../path//.\",\n\t\t\t\"./path\", \".//path\", \".\" };\n\n\t@Test\n\tpublic void unnormalizedPathsAreRejected() {\n\t\tDefaultHttpFirewall fw = new DefaultHttpFirewall();\n\t\tfor (String path : this.unnormalizedPaths) {\n\t\t\tMockHttpServletRequest request = get().requestUri(path).build();\n\t\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t\t.isThrownBy(() -> fw.getFirewalledRequest(request));\n\t\t\trequest.setPathInfo(path);\n\t\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t\t.isThrownBy(() -> fw.getFirewalledRequest(request));\n\t\t}\n\t}\n\n\t/**\n\t * On WebSphere 8.5 a URL like /context-root/a/b;%2f1/c can bypass a rule on /a/b/c\n\t * because the pathInfo is /a/b;/1/c which ends up being /a/b/1/c while Spring MVC\n\t * will strip the ; content from requestURI before the path is URL decoded.\n\t */\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedPathThenException() {\n\t\tDefaultHttpFirewall fw = new DefaultHttpFirewall();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/context-root/a/b;%2f1/c\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b;/1/c\"); // URL decoded requestURI\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> fw.getFirewalledRequest(request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenUppercaseEncodedPathThenException() {\n\t\tDefaultHttpFirewall fw = new DefaultHttpFirewall();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/context-root/a/b;%2F1/c\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b;/1/c\"); // URL decoded requestURI\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> fw.getFirewalledRequest(request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowUrlEncodedSlashAndLowercaseEncodedPathThenNoException() {\n\t\tDefaultHttpFirewall fw = new DefaultHttpFirewall();\n\t\tfw.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/context-root/a/b;%2f1/c\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b;/1/c\"); // URL decoded requestURI\n\t\tfw.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowUrlEncodedSlashAndUppercaseEncodedPathThenNoException() {\n\t\tDefaultHttpFirewall fw = new DefaultHttpFirewall();\n\t\tfw.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/context-root/a/b;%2F1/c\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b;/1/c\"); // URL decoded requestURI\n\t\tfw.getFirewalledRequest(request);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/firewall/DefaultRequestRejectedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\npublic class DefaultRequestRejectedHandlerTests {\n\n\t@Test\n\tpublic void defaultRequestRejectedHandlerRethrowsTheException() throws Exception {\n\t\tRequestRejectedException requestRejectedException = new RequestRejectedException(\"rejected\");\n\t\tDefaultRequestRejectedHandler sut = new DefaultRequestRejectedHandler();\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> sut.handle(mock(HttpServletRequest.class), mock(HttpServletResponse.class),\n\t\t\t\t\trequestRejectedException))\n\t\t\t.withMessage(\"rejected\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/firewall/FirewalledResponseTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @author Gabriel Lavoie\n */\npublic class FirewalledResponseTests {\n\n\tprivate static final String CRLF_MESSAGE = \"Invalid characters (CR/LF)\";\n\n\tprivate HttpServletResponse response;\n\n\tprivate FirewalledResponse fwResponse;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.response = mock(HttpServletResponse.class);\n\t\tthis.fwResponse = new FirewalledResponse(this.response);\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenValidThenNoException() throws Exception {\n\t\tthis.fwResponse.sendRedirect(\"/theURL\");\n\t\tverify(this.response).sendRedirect(\"/theURL\");\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenNullThenDelegateInvoked() throws Exception {\n\t\tthis.fwResponse.sendRedirect(null);\n\t\tverify(this.response).sendRedirect(null);\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenHasCrlfThenThrowsException() throws Exception {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.fwResponse.sendRedirect(\"/theURL\\r\\nsomething\"))\n\t\t\t.withMessageContaining(CRLF_MESSAGE);\n\t}\n\n\t@Test\n\tpublic void addHeaderWhenValidThenDelegateInvoked() {\n\t\tthis.fwResponse.addHeader(\"foo\", \"bar\");\n\t\tverify(this.response).addHeader(\"foo\", \"bar\");\n\t}\n\n\t@Test\n\tpublic void addHeaderWhenNullValueThenDelegateInvoked() {\n\t\tthis.fwResponse.addHeader(\"foo\", null);\n\t\tverify(this.response).addHeader(\"foo\", null);\n\t}\n\n\t@Test\n\tpublic void addHeaderWhenHeaderValueHasCrlfThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.fwResponse.addHeader(\"foo\", \"abc\\r\\nContent-Length:100\"))\n\t\t\t.withMessageContaining(CRLF_MESSAGE);\n\t}\n\n\t@Test\n\tpublic void addHeaderWhenHeaderNameHasCrlfThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.fwResponse.addHeader(\"abc\\r\\nContent-Length:100\", \"bar\"))\n\t\t\t.withMessageContaining(CRLF_MESSAGE);\n\t}\n\n\t@Test\n\tpublic void addCookieWhenValidThenDelegateInvoked() {\n\t\tCookie cookie = new Cookie(\"foo\", \"bar\");\n\t\tcookie.setPath(\"/foobar\");\n\t\tcookie.setDomain(\"foobar\");\n\t\tthis.fwResponse.addCookie(cookie);\n\t\tverify(this.response).addCookie(cookie);\n\t}\n\n\t@Test\n\tpublic void addCookieWhenNullThenDelegateInvoked() {\n\t\tthis.fwResponse.addCookie(null);\n\t\tverify(this.response).addCookie(null);\n\t}\n\n\t@Test\n\tpublic void addCookieWhenCookieNameContainsCrlfThenException() {\n\t\t// Constructor validates the name\n\t\tCookie cookie = new Cookie(\"valid-since-constructor-validates\", \"bar\") {\n\t\t\t@Override\n\t\t\tpublic String getName() {\n\t\t\t\treturn \"foo\\r\\nbar\";\n\t\t\t}\n\t\t};\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.fwResponse.addCookie(cookie))\n\t\t\t.withMessageContaining(CRLF_MESSAGE);\n\t}\n\n\t@Test\n\tpublic void addCookieWhenCookieValueContainsCrlfThenException() {\n\t\tCookie cookie = new Cookie(\"foo\", \"foo\\r\\nbar\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.fwResponse.addCookie(cookie))\n\t\t\t.withMessageContaining(CRLF_MESSAGE);\n\t}\n\n\t@Test\n\tpublic void addCookieWhenCookiePathContainsCrlfThenException() {\n\t\tCookie cookie = new Cookie(\"foo\", \"bar\");\n\t\tcookie.setPath(\"/foo\\r\\nbar\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.fwResponse.addCookie(cookie))\n\t\t\t.withMessageContaining(CRLF_MESSAGE);\n\t}\n\n\t@Test\n\tpublic void addCookieWhenCookieDomainContainsCrlfThenException() {\n\t\tCookie cookie = new Cookie(\"foo\", \"bar\");\n\t\tcookie.setDomain(\"foo\\r\\nbar\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.fwResponse.addCookie(cookie))\n\t\t\t.withMessageContaining(CRLF_MESSAGE);\n\t}\n\n\t@Test\n\tpublic void rejectAnyLineEndingInNameAndValue() {\n\t\tvalidateLineEnding(\"foo\", \"foo\\rbar\");\n\t\tvalidateLineEnding(\"foo\", \"foo\\r\\nbar\");\n\t\tvalidateLineEnding(\"foo\", \"foo\\nbar\");\n\t\tvalidateLineEnding(\"foo\\rbar\", \"bar\");\n\t\tvalidateLineEnding(\"foo\\r\\nbar\", \"bar\");\n\t\tvalidateLineEnding(\"foo\\nbar\", \"bar\");\n\t}\n\n\tprivate void validateLineEnding(String name, String value) {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.fwResponse.validateCrlf(name, value));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/firewall/HttpStatusRequestRejectedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\npublic class HttpStatusRequestRejectedHandlerTests {\n\n\t@Test\n\tpublic void httpStatusRequestRejectedHandlerUsesStatus400byDefault() throws Exception {\n\t\tHttpStatusRequestRejectedHandler sut = new HttpStatusRequestRejectedHandler();\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tsut.handle(mock(HttpServletRequest.class), response, mock(RequestRejectedException.class));\n\t\tverify(response).sendError(400);\n\t}\n\n\t@Test\n\tpublic void httpStatusRequestRejectedHandlerCanBeConfiguredToUseStatus() throws Exception {\n\t\thttpStatusRequestRejectedHandlerCanBeConfiguredToUseStatusHelper(400);\n\t\thttpStatusRequestRejectedHandlerCanBeConfiguredToUseStatusHelper(403);\n\t\thttpStatusRequestRejectedHandlerCanBeConfiguredToUseStatusHelper(500);\n\t}\n\n\tprivate void httpStatusRequestRejectedHandlerCanBeConfiguredToUseStatusHelper(int status) throws Exception {\n\t\tHttpStatusRequestRejectedHandler sut = new HttpStatusRequestRejectedHandler(status);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tsut.handle(mock(HttpServletRequest.class), response, mock(RequestRejectedException.class));\n\t\tverify(response).sendError(status);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/firewall/RequestWrapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport jakarta.servlet.RequestDispatcher;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Luke Taylor\n */\npublic class RequestWrapperTests {\n\n\tprivate static Map<String, String> testPaths = new LinkedHashMap<>();\n\n\t@BeforeAll\n\t// Some of these may be unrealistic values, but we can't be sure because of the\n\t// inconsistency in the spec.\n\tpublic static void createTestMap() {\n\t\ttestPaths.put(\"/path1;x=y;z=w/path2;x=y/path3;x=y\", \"/path1/path2/path3\");\n\t\ttestPaths.put(\"/path1;x=y/path2;x=y/\", \"/path1/path2/\");\n\t\ttestPaths.put(\"/path1//path2/\", \"/path1/path2/\");\n\t\ttestPaths.put(\"//path1/path2//\", \"/path1/path2/\");\n\t\ttestPaths.put(\";x=y;z=w\", \"\");\n\t}\n\n\t@Test\n\tpublic void pathParametersAreRemovedFromServletPath() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfor (Map.Entry<String, String> entry : testPaths.entrySet()) {\n\t\t\tString path = entry.getKey();\n\t\t\tString expectedResult = entry.getValue();\n\t\t\trequest.setServletPath(path);\n\t\t\tRequestWrapper wrapper = new RequestWrapper(request);\n\t\t\tassertThat(wrapper.getServletPath()).isEqualTo(expectedResult);\n\t\t\twrapper.reset();\n\t\t\tassertThat(wrapper.getServletPath()).isEqualTo(path);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void pathParametersAreRemovedFromPathInfo() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tfor (Map.Entry<String, String> entry : testPaths.entrySet()) {\n\t\t\tString path = entry.getKey();\n\t\t\tString expectedResult = entry.getValue();\n\t\t\t// Should be null when stripped value is empty\n\t\t\tif (expectedResult.isEmpty()) {\n\t\t\t\texpectedResult = null;\n\t\t\t}\n\t\t\trequest.setPathInfo(path);\n\t\t\tRequestWrapper wrapper = new RequestWrapper(request);\n\t\t\tassertThat(wrapper.getPathInfo()).isEqualTo(expectedResult);\n\t\t\twrapper.reset();\n\t\t\tassertThat(wrapper.getPathInfo()).isEqualTo(path);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void resetWhenForward() throws Exception {\n\t\tString denormalizedPath = testPaths.keySet().iterator().next();\n\t\tString forwardPath = \"/forward/path\";\n\t\tHttpServletRequest mockRequest = mock(HttpServletRequest.class);\n\t\tHttpServletResponse mockResponse = mock(HttpServletResponse.class);\n\t\tRequestDispatcher mockDispatcher = mock(RequestDispatcher.class);\n\t\tgiven(mockRequest.getServletPath()).willReturn(\"\");\n\t\tgiven(mockRequest.getPathInfo()).willReturn(denormalizedPath);\n\t\tgiven(mockRequest.getRequestDispatcher(forwardPath)).willReturn(mockDispatcher);\n\t\tRequestWrapper wrapper = new RequestWrapper(mockRequest);\n\t\tRequestDispatcher dispatcher = wrapper.getRequestDispatcher(forwardPath);\n\t\tdispatcher.forward(mockRequest, mockResponse);\n\t\tverify(mockRequest).getRequestDispatcher(forwardPath);\n\t\tverify(mockDispatcher).forward(mockRequest, mockResponse);\n\t\tassertThat(wrapper.getPathInfo()).isEqualTo(denormalizedPath);\n\t\tverify(mockRequest, times(2)).getPathInfo();\n\t\t// validate wrapper.getServletPath() delegates to the mock\n\t\twrapper.getServletPath();\n\t\tverify(mockRequest, times(2)).getServletPath();\n\t\tverifyNoMoreInteractions(mockRequest, mockResponse, mockDispatcher);\n\t}\n\n\t@Test\n\tpublic void requestDispatcherNotWrappedAfterReset() {\n\t\tString path = \"/forward/path\";\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tRequestDispatcher dispatcher = mock(RequestDispatcher.class);\n\t\tgiven(request.getRequestDispatcher(path)).willReturn(dispatcher);\n\t\tRequestWrapper wrapper = new RequestWrapper(request);\n\t\twrapper.reset();\n\t\tassertThat(wrapper.getRequestDispatcher(path)).isSameAs(dispatcher);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.firewall;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\n\n/**\n * @author Rob Winch\n * @author Eddú Meléndez\n * @author Jinwoo Bae\n */\npublic class StrictHttpFirewallTests {\n\n\tpublic String[] unnormalizedPaths = { \"/..\", \"/./path/\", \"/path/path/.\", \"/path/path//.\", \"./path/../path//.\",\n\t\t\t\"./path\", \".//path\", \".\", \"//path\", \"//path/path\", \"//path//path\", \"/path//path\" };\n\n\tprivate StrictHttpFirewall firewall = new StrictHttpFirewall();\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\n\t@Test\n\tpublic void getFirewalledRequestWhenInvalidMethodThenThrowsRequestRejectedException() {\n\t\tthis.request.setMethod(\"INVALID\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t// blocks XST attacks\n\t@Test\n\tpublic void getFirewalledRequestWhenTraceMethodThenThrowsRequestRejectedException() {\n\t\tthis.request.setMethod(HttpMethod.TRACE.name());\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\t// blocks XST attack if request is forwarded to a Microsoft IIS web server\n\tpublic void getFirewalledRequestWhenTrackMethodThenThrowsRequestRejectedException() {\n\t\tthis.request.setMethod(\"TRACK\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\t// HTTP methods are case sensitive\n\tpublic void getFirewalledRequestWhenLowercaseGetThenThrowsRequestRejectedException() {\n\t\tthis.request.setMethod(\"get\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowedThenNoException() {\n\t\tList<String> allowedMethods = Arrays.asList(\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\");\n\t\tfor (String allowedMethod : allowedMethods) {\n\t\t\tthis.request = new MockHttpServletRequest(allowedMethod, \"\");\n\t\t\tthis.firewall.getFirewalledRequest(this.request);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenInvalidMethodAndAnyMethodThenNoException() {\n\t\tthis.firewall.setUnsafeAllowAnyHttpMethod(true);\n\t\tthis.request.setMethod(\"INVALID\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRequestURINotNormalizedThenThrowsRequestRejectedException() {\n\t\tfor (String path : this.unnormalizedPaths) {\n\t\t\tthis.request = new MockHttpServletRequest(\"GET\", \"\");\n\t\t\tthis.request.setRequestURI(path);\n\t\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContextPathNotNormalizedThenThrowsRequestRejectedException() {\n\t\tfor (String path : this.unnormalizedPaths) {\n\t\t\tthis.request = new MockHttpServletRequest(\"GET\", \"\");\n\t\t\tthis.request.setContextPath(path);\n\t\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathNotNormalizedThenThrowsRequestRejectedException() {\n\t\tfor (String path : this.unnormalizedPaths) {\n\t\t\tthis.request = get().requestUri(path).build();\n\t\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenPathInfoNotNormalizedThenThrowsRequestRejectedException() {\n\t\tfor (String path : this.unnormalizedPaths) {\n\t\t\tthis.request = new MockHttpServletRequest(\"GET\", \"\");\n\t\t\tthis.request.setPathInfo(path);\n\t\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenSemicolonInContextPathThenThrowsRequestRejectedException() {\n\t\tthis.request.setContextPath(\";/context\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenSemicolonInServletPathThenThrowsRequestRejectedException() {\n\t\tthis.request.setServletPath(\"/spring;/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenSemicolonInPathInfoThenThrowsRequestRejectedException() {\n\t\tthis.request.setPathInfo(\"/path;/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenSemicolonInRequestUriThenThrowsRequestRejectedException() {\n\t\tthis.request.setRequestURI(\"/path;/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedSemicolonInContextPathThenThrowsRequestRejectedException() {\n\t\tthis.request.setContextPath(\"%3B/context\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedSemicolonInServletPathThenThrowsRequestRejectedException() {\n\t\tthis.request.setServletPath(\"/spring%3B/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedSemicolonInPathInfoThenThrowsRequestRejectedException() {\n\t\tthis.request.setPathInfo(\"/path%3B/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedSemicolonInRequestUriThenThrowsRequestRejectedException() {\n\t\tthis.request.setRequestURI(\"/path%3B/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedSemicolonInContextPathThenThrowsRequestRejectedException() {\n\t\tthis.request.setContextPath(\"%3b/context\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedSemicolonInServletPathThenThrowsRequestRejectedException() {\n\t\tthis.request.setServletPath(\"/spring%3b/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedSemicolonInPathInfoThenThrowsRequestRejectedException() {\n\t\tthis.request.setPathInfo(\"/path%3b/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedSemicolonInRequestUriThenThrowsRequestRejectedException() {\n\t\tthis.request.setRequestURI(\"/path%3b/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenSemicolonInContextPathAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setContextPath(\";/context\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenSemicolonInServletPathAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setServletPath(\"/spring;/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenSemicolonInPathInfoAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setPathInfo(\"/path;/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenSemicolonInRequestUriAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setRequestURI(\"/path;/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedSemicolonInContextPathAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedPercent(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setContextPath(\"%3B/context\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedSemicolonInServletPathAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedPercent(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setServletPath(\"/spring%3B/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedSemicolonInPathInfoAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedPercent(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setPathInfo(\"/path%3B/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedSemicolonInRequestUriAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setRequestURI(\"/path%3B/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedSemicolonInContextPathAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedPercent(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setContextPath(\"%3b/context\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedSemicolonInServletPathAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedPercent(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setServletPath(\"/spring%3b/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedSemicolonInPathInfoAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedPercent(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setPathInfo(\"/path%3b/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedSemicolonInRequestUriAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request.setRequestURI(\"/path%3b/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenEncodedPeriodInThenThrowsRequestRejectedException() {\n\t\tthis.request.setRequestURI(\"/%2E/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedPeriodInThenThrowsRequestRejectedException() {\n\t\tthis.request.setRequestURI(\"/%2e/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowEncodedPeriodAndEncodedPeriodInThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedPeriod(true);\n\t\tthis.request.setRequestURI(\"/%2E/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenExceedsLowerboundAsciiThenException() {\n\t\tthis.request.setRequestURI(\"/\\u0019\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsLowerboundAsciiThenNoException() {\n\t\tthis.request.setRequestURI(\"/ \");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsUpperboundAsciiThenNoException() {\n\t\tthis.request.setRequestURI(\"/~\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenJapaneseCharacterThenNoException() {\n\t\tthis.request.setServletPath(\"/\\u3042\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenExceedsUpperboundAsciiThenException() {\n\t\tthis.request.setRequestURI(\"/\\u007f\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsNullThenException() {\n\t\tthis.request.setRequestURI(\"/\\0\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsEncodedNullThenException() {\n\t\tthis.request.setRequestURI(\"/something%00/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsLowercaseEncodedLineFeedThenException() {\n\t\tthis.request.setRequestURI(\"/something%0a/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsUppercaseEncodedLineFeedThenException() {\n\t\tthis.request.setRequestURI(\"/something%0A/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsLineFeedThenException() {\n\t\tthis.request.setRequestURI(\"/something\\n/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathContainsLineFeedThenException() {\n\t\tthis.request.setServletPath(\"/something\\n/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsLowercaseEncodedCarriageReturnThenException() {\n\t\tthis.request.setRequestURI(\"/something%0d/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsUppercaseEncodedCarriageReturnThenException() {\n\t\tthis.request.setRequestURI(\"/something%0D/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsCarriageReturnThenException() {\n\t\tthis.request.setRequestURI(\"/something\\r/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathContainsCarriageReturnThenException() {\n\t\tthis.request.setServletPath(\"/something\\r/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathContainsLineSeparatorThenException() {\n\t\tthis.request.setServletPath(\"/something\\u2028/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathContainsParagraphSeparatorThenException() {\n\t\tthis.request.setServletPath(\"/something\\u2029/\");\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsLowercaseEncodedLineFeedAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedLineFeed(true);\n\t\tthis.request.setRequestURI(\"/something%0a/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsUppercaseEncodedLineFeedAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedLineFeed(true);\n\t\tthis.request.setRequestURI(\"/something%0A/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsLineFeedAndAllowedThenException() {\n\t\tthis.firewall.setAllowUrlEncodedLineFeed(true);\n\t\tthis.request.setRequestURI(\"/something\\n/\");\n\t\t// Expected an error because the line feed is decoded in an encoded part of the\n\t\t// URL\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathContainsLineFeedAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedLineFeed(true);\n\t\tthis.request.setServletPath(\"/something\\n/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsLowercaseEncodedCarriageReturnAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedCarriageReturn(true);\n\t\tthis.request.setRequestURI(\"/something%0d/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsUppercaseEncodedCarriageReturnAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedCarriageReturn(true);\n\t\tthis.request.setRequestURI(\"/something%0D/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenContainsCarriageReturnAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedCarriageReturn(true);\n\t\tthis.request.setRequestURI(\"/something\\r/\");\n\t\t// Expected an error because the carriage return is decoded in an encoded part of\n\t\t// the URL\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathContainsCarriageReturnAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedCarriageReturn(true);\n\t\tthis.request.setServletPath(\"/something\\r/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathContainsLineSeparatorAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedLineSeparator(true);\n\t\tthis.request.setServletPath(\"/something\\u2028/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenServletPathContainsParagraphSeparatorAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedParagraphSeparator(true);\n\t\tthis.request.setServletPath(\"/something\\u2029/\");\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t/**\n\t * On WebSphere 8.5 a URL like /context-root/a/b;%2f1/c can bypass a rule on /a/b/c\n\t * because the pathInfo is /a/b;/1/c which ends up being /a/b/1/c while Spring MVC\n\t * will strip the ; content from requestURI before the path is URL decoded.\n\t */\n\t@Test\n\tpublic void getFirewalledRequestWhenLowercaseEncodedPathThenException() {\n\t\tthis.request.setRequestURI(\"/context-root/a/b;%2f1/c\");\n\t\tthis.request.setContextPath(\"/context-root\");\n\t\tthis.request.setServletPath(\"\");\n\t\tthis.request.setPathInfo(\"/a/b;/1/c\"); // URL decoded requestURI\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenUppercaseEncodedPathThenException() {\n\t\tthis.request.setRequestURI(\"/context-root/a/b;%2F1/c\");\n\t\tthis.request.setContextPath(\"/context-root\");\n\t\tthis.request.setServletPath(\"\");\n\t\tthis.request.setPathInfo(\"/a/b;/1/c\"); // URL decoded requestURI\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowUrlEncodedSlashAndLowercaseEncodedPathThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b;%2f1/c\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b;/1/c\"); // URL decoded requestURI\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowUrlEncodedSlashAndUppercaseEncodedPathThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b;%2F1/c\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b;/1/c\"); // URL decoded requestURI\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowUrlLowerCaseEncodedDoubleSlashThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowUrlEncodedDoubleSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2f%2fc\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b//c\");\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowUrlUpperCaseEncodedDoubleSlashThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowUrlEncodedDoubleSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2F%2Fc\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b//c\");\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowUrlLowerCaseAndUpperCaseEncodedDoubleSlashThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowUrlEncodedDoubleSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2f%2Fc\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b//c\");\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenAllowUrlUpperCaseAndLowerCaseEncodedDoubleSlashThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowUrlEncodedDoubleSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2F%2fc\");\n\t\trequest.setContextPath(\"/context-root\");\n\t\trequest.setServletPath(\"\");\n\t\trequest.setPathInfo(\"/a/b//c\");\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromUpperCaseEncodedUrlBlacklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2F%2Fc\");\n\t\tthis.firewall.getEncodedUrlBlacklist().removeAll(Arrays.asList(\"%2F%2F\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromLowerCaseEncodedUrlBlacklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2f%2fc\");\n\t\tthis.firewall.getEncodedUrlBlacklist().removeAll(Arrays.asList(\"%2f%2f\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromLowerCaseAndUpperCaseEncodedUrlBlacklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2f%2Fc\");\n\t\tthis.firewall.getEncodedUrlBlacklist().removeAll(Arrays.asList(\"%2f%2F\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromUpperCaseAndLowerCaseEncodedUrlBlacklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2F%2fc\");\n\t\tthis.firewall.getEncodedUrlBlacklist().removeAll(Arrays.asList(\"%2F%2f\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromDecodedUrlBlacklistThenNoException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setPathInfo(\"/a/b//c\");\n\t\tthis.firewall.getDecodedUrlBlacklist().removeAll(Arrays.asList(\"//\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t// blocklist\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromUpperCaseEncodedUrlBlocklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2F%2Fc\");\n\t\tthis.firewall.getEncodedUrlBlocklist().removeAll(Arrays.asList(\"%2F%2F\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromLowerCaseEncodedUrlBlocklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2f%2fc\");\n\t\tthis.firewall.getEncodedUrlBlocklist().removeAll(Arrays.asList(\"%2f%2f\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromLowerCaseAndUpperCaseEncodedUrlBlocklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2f%2Fc\");\n\t\tthis.firewall.getEncodedUrlBlocklist().removeAll(Arrays.asList(\"%2f%2F\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromUpperCaseAndLowerCaseEncodedUrlBlocklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setRequestURI(\"/context-root/a/b%2F%2fc\");\n\t\tthis.firewall.getEncodedUrlBlocklist().removeAll(Arrays.asList(\"%2F%2f\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenRemoveFromDecodedUrlBlocklistThenNoException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"\");\n\t\trequest.setPathInfo(\"/a/b//c\");\n\t\tthis.firewall.getDecodedUrlBlocklist().removeAll(Arrays.asList(\"//\"));\n\t\tthis.firewall.getFirewalledRequest(request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenTrustedDomainThenNoException() {\n\t\tthis.request.addHeader(\"Host\", \"example.org\");\n\t\tthis.firewall.setAllowedHostnames((hostname) -> hostname.equals(\"example.org\"));\n\t\tthis.firewall.getFirewalledRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenUntrustedDomainThenException() {\n\t\tthis.request.addHeader(\"Host\", \"example.org\");\n\t\tthis.firewall.setAllowedHostnames((hostname) -> hostname.equals(\"myexample.org\"));\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderWhenNotAllowedHeaderNameThenException() {\n\t\tthis.firewall.setAllowedHeaderNames((name) -> !name.equals(\"bad name\"));\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader(\"bad name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenHeaderNameNotAllowedWithAugmentedHeaderNamesThenException() {\n\t\tthis.firewall\n\t\t\t.setAllowedHeaderNames(StrictHttpFirewall.ALLOWED_HEADER_NAMES.and((name) -> !name.equals(\"bad name\")));\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader(\"bad name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderWhenNotAllowedHeaderValueThenException() {\n\t\tthis.request.addHeader(\"good name\", \"bad value\");\n\t\tthis.firewall.setAllowedHeaderValues((value) -> !value.equals(\"bad value\"));\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader(\"good name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenHeaderValueNotAllowedWithAugmentedHeaderValuesThenException() {\n\t\tthis.request.addHeader(\"good name\", \"bad value\");\n\t\tthis.firewall.setAllowedHeaderValues(\n\t\t\t\tStrictHttpFirewall.ALLOWED_HEADER_VALUES.and((value) -> !value.equals(\"bad value\")));\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader(\"good name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetDateHeaderWhenControlCharacterInHeaderNameThenException() {\n\t\tthis.request.addHeader(\"Bad\\0Name\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getDateHeader(\"Bad\\0Name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetIntHeaderWhenControlCharacterInHeaderNameThenException() {\n\t\tthis.request.addHeader(\"Bad\\0Name\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getIntHeader(\"Bad\\0Name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderWhenControlCharacterInHeaderNameThenException() {\n\t\tthis.request.addHeader(\"Bad\\0Name\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader(\"Bad\\0Name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderWhenUndefinedCharacterInHeaderNameThenException() {\n\t\tthis.request.addHeader(\"Bad\\uFFFEName\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader(\"Bad\\uFFFEName\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeadersWhenControlCharacterInHeaderNameThenException() {\n\t\tthis.request.addHeader(\"Bad\\0Name\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeaders(\"Bad\\0Name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderNamesWhenControlCharacterInHeaderNameThenException() {\n\t\tthis.request.addHeader(\"Bad\\0Name\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> request.getHeaderNames().nextElement());\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderWhenControlCharacterInHeaderValueThenException() {\n\t\tthis.request.addHeader(\"Something\", \"bad\\0value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader(\"Something\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderWhenHorizontalTabInHeaderValueThenNoException() {\n\t\tthis.request.addHeader(\"Something\", \"tab\\tvalue\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThat(request.getHeader(\"Something\")).isEqualTo(\"tab\\tvalue\");\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderWhenUndefinedCharacterInHeaderValueThenException() {\n\t\tthis.request.addHeader(\"Something\", \"bad\\uFFFEvalue\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader(\"Something\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetHeadersWhenControlCharacterInHeaderValueThenException() {\n\t\tthis.request.addHeader(\"Something\", \"bad\\0value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> request.getHeaders(\"Something\").nextElement());\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetParameterWhenControlCharacterInParameterNameThenException() {\n\t\tthis.request.addParameter(\"Bad\\0Name\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getParameter(\"Bad\\0Name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetParameterMapWhenControlCharacterInParameterNameThenException() {\n\t\tthis.request.addParameter(\"Bad\\0Name\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(request::getParameterMap);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetParameterNamesWhenControlCharacterInParameterNameThenException() {\n\t\tthis.request.addParameter(\"Bad\\0Name\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(request.getParameterNames()::nextElement);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetParameterNamesWhenUndefinedCharacterInParameterNameThenException() {\n\t\tthis.request.addParameter(\"Bad\\uFFFEName\", \"some value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class).isThrownBy(request.getParameterNames()::nextElement);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetParameterValuesWhenNotAllowedInParameterValueThenException() {\n\t\tthis.firewall.setAllowedParameterValues((value) -> !value.equals(\"bad value\"));\n\t\tthis.request.addParameter(\"Something\", \"bad value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> request.getParameterValues(\"Something\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenParameterValueNotAllowedWithAugmentedParameterValuesThenException() {\n\t\tthis.request.addParameter(\"Something\", \"bad value\");\n\t\tthis.firewall.setAllowedParameterValues(\n\t\t\t\tStrictHttpFirewall.ALLOWED_PARAMETER_VALUES.and((value) -> !value.equals(\"bad value\")));\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> request.getParameterValues(\"Something\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetParameterValuesWhenNotAllowedInParameterNameThenException() {\n\t\tthis.firewall.setAllowedParameterNames((value) -> !value.equals(\"bad name\"));\n\t\tthis.request.addParameter(\"bad name\", \"good value\");\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> request.getParameterValues(\"bad name\"));\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestWhenParameterNameNotAllowedWithAugmentedParameterNamesThenException() {\n\t\tthis.request.addParameter(\"bad name\", \"good value\");\n\t\tthis.firewall.setAllowedParameterNames(\n\t\t\t\tStrictHttpFirewall.ALLOWED_PARAMETER_NAMES.and((value) -> !value.equals(\"bad name\")));\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(RequestRejectedException.class)\n\t\t\t.isThrownBy(() -> request.getParameterValues(\"bad name\"));\n\t}\n\n\t// gh-9598\n\t@Test\n\tpublic void getFirewalledRequestGetParameterWhenNameIsNullThenIllegalArgumentException() {\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> request.getParameter(null));\n\t}\n\n\t// gh-9598\n\t@Test\n\tpublic void getFirewalledRequestGetParameterValuesWhenNameIsNullThenIllegalArgumentException() {\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> request.getParameterValues(null));\n\t}\n\n\t// gh-9598\n\t@Test\n\tpublic void getFirewalledRequestGetHeaderWhenNameIsNullThenNull() {\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThat(request.getHeader(null)).isNull();\n\t}\n\n\t// gh-9598\n\t@Test\n\tpublic void getFirewalledRequestGetHeadersWhenNameIsNullThenEmptyEnumeration() {\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThat(request.getHeaders(null).hasMoreElements()).isFalse();\n\t}\n\n\t// gh-9598\n\t@Test\n\tpublic void getFirewalledRequestGetIntHeaderWhenNameIsNullThenNegativeOne() {\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThat(request.getIntHeader(null)).isEqualTo(-1);\n\t}\n\n\t@Test\n\tpublic void getFirewalledRequestGetDateHeaderWhenNameIsNullThenNegativeOne() {\n\t\tHttpServletRequest request = this.firewall.getFirewalledRequest(this.request);\n\t\tassertThat(request.getDateHeader(null)).isEqualTo(-1);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/HeaderWriterFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for the {@code HeadersFilter}\n *\n * @author Marten Deinum\n * @author Rob Winch\n * @since 3.2\n */\n@ExtendWith(MockitoExtension.class)\npublic class HeaderWriterFilterTests {\n\n\t@Mock\n\tprivate HeaderWriter writer1;\n\n\t@Mock\n\tprivate HeaderWriter writer2;\n\n\t@Test\n\tpublic void noHeadersConfigured() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new HeaderWriterFilter(new ArrayList<>()));\n\t}\n\n\t@Test\n\tpublic void constructorNullWriters() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new HeaderWriterFilter(null));\n\t}\n\n\t@Test\n\tpublic void additionalHeadersShouldBeAddedToTheResponse() throws Exception {\n\t\tList<HeaderWriter> headerWriters = new ArrayList<>();\n\t\theaderWriters.add(this.writer1);\n\t\theaderWriters.add(this.writer2);\n\t\tHeaderWriterFilter filter = new HeaderWriterFilter(headerWriters);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tMockFilterChain filterChain = new MockFilterChain();\n\t\tfilter.doFilter(request, response, filterChain);\n\t\tverify(this.writer1).writeHeaders(request, response);\n\t\tverify(this.writer2).writeHeaders(request, response);\n\t\tHeaderWriterFilter.HeaderWriterRequest wrappedRequest = (HeaderWriterFilter.HeaderWriterRequest) filterChain\n\t\t\t.getRequest();\n\t\tassertThat(wrappedRequest.getRequest()).isEqualTo(request); // verify the\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// filterChain\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// continued\n\t}\n\n\t// gh-2953\n\t@Test\n\tpublic void headersDelayed() throws Exception {\n\t\tHeaderWriterFilter filter = new HeaderWriterFilter(Arrays.<HeaderWriter>asList(this.writer1));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, (request1, response1) -> {\n\t\t\tverifyNoMoreInteractions(HeaderWriterFilterTests.this.writer1);\n\t\t\tresponse1.flushBuffer();\n\t\t\tverify(HeaderWriterFilterTests.this.writer1).writeHeaders(any(HttpServletRequest.class),\n\t\t\t\t\tany(HttpServletResponse.class));\n\t\t});\n\t\tverifyNoMoreInteractions(this.writer1);\n\t}\n\n\t// gh-5499\n\t@Test\n\tpublic void doFilterWhenRequestContainsIncludeThenHeadersStillWritten() throws Exception {\n\t\tHeaderWriterFilter filter = new HeaderWriterFilter(Collections.singletonList(this.writer1));\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest();\n\t\tMockHttpServletResponse mockResponse = new MockHttpServletResponse();\n\t\tfilter.doFilter(mockRequest, mockResponse, (request, response) -> {\n\t\t\tverifyNoMoreInteractions(HeaderWriterFilterTests.this.writer1);\n\t\t\trequest.getRequestDispatcher(\"/\").include(request, response);\n\t\t\tverify(HeaderWriterFilterTests.this.writer1).writeHeaders(any(HttpServletRequest.class),\n\t\t\t\t\tany(HttpServletResponse.class));\n\t\t});\n\t\tverifyNoMoreInteractions(this.writer1);\n\t}\n\n\t@Test\n\tpublic void headersWrittenAtBeginningOfRequest() throws Exception {\n\t\tHeaderWriterFilter filter = new HeaderWriterFilter(Collections.singletonList(this.writer1));\n\t\tfilter.setShouldWriteHeadersEagerly(true);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, (request1, response1) -> verify(HeaderWriterFilterTests.this.writer1)\n\t\t\t.writeHeaders(any(HttpServletRequest.class), any(HttpServletResponse.class)));\n\t\tverifyNoMoreInteractions(this.writer1);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/CacheControlHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\npublic class CacheControlHeadersWriterTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate CacheControlHeadersWriter writer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new CacheControlHeadersWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeaders() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(3);\n\t\tassertThat(this.response.getHeaderValues(\"Cache-Control\"))\n\t\t\t.containsOnly(\"no-cache, no-store, max-age=0, must-revalidate\");\n\t\tassertThat(this.response.getHeaderValues(\"Pragma\")).containsOnly(\"no-cache\");\n\t\tassertThat(this.response.getHeaderValues(\"Expires\")).containsOnly(\"0\");\n\t}\n\n\t// gh-2953\n\t@Test\n\tpublic void writeHeadersDisabledIfCacheControl() {\n\t\tthis.response.setHeader(\"Cache-Control\", \"max-age: 123\");\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeaderValues(\"Cache-Control\")).containsOnly(\"max-age: 123\");\n\t\tassertThat(this.response.getHeaderValue(\"Pragma\")).isNull();\n\t\tassertThat(this.response.getHeaderValue(\"Expires\")).isNull();\n\t}\n\n\t@Test\n\tpublic void writeHeadersDisabledIfPragma() {\n\t\tthis.response.setHeader(\"Pragma\", \"mock\");\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeaderValues(\"Pragma\")).containsOnly(\"mock\");\n\t\tassertThat(this.response.getHeaderValue(\"Expires\")).isNull();\n\t\tassertThat(this.response.getHeaderValue(\"Cache-Control\")).isNull();\n\t}\n\n\t@Test\n\tpublic void writeHeadersDisabledIfExpires() {\n\t\tthis.response.setHeader(\"Expires\", \"mock\");\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeaderValues(\"Expires\")).containsOnly(\"mock\");\n\t\tassertThat(this.response.getHeaderValue(\"Cache-Control\")).isNull();\n\t\tassertThat(this.response.getHeaderValue(\"Pragma\")).isNull();\n\t}\n\n\t@Test\n\t// gh-5534\n\tpublic void writeHeadersDisabledIfNotModified() {\n\t\tthis.response.setStatus(HttpStatus.NOT_MODIFIED.value());\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).isEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/ClearSiteDataHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.header.writers.ClearSiteDataHeaderWriter.Directive;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Rafiullah Hamedy\n * @author Josh Cummings\n * @see ClearSiteDataHeaderWriter\n */\npublic class ClearSiteDataHeaderWriterTests {\n\n\tprivate static final String HEADER_NAME = \"Clear-Site-Data\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.request.setSecure(true);\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void createInstanceWhenMissingSourceThenThrowsException() {\n\t\tassertThatExceptionOfType(Exception.class).isThrownBy(() -> new ClearSiteDataHeaderWriter())\n\t\t\t.withMessage(\"directives cannot be empty or null\");\n\t}\n\n\t@Test\n\tpublic void writeHeaderWhenRequestNotSecureThenHeaderIsNotPresent() {\n\t\tthis.request.setSecure(false);\n\t\tClearSiteDataHeaderWriter headerWriter = new ClearSiteDataHeaderWriter(Directive.CACHE);\n\t\theaderWriter.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(HEADER_NAME)).isNull();\n\t}\n\n\t@Test\n\tpublic void writeHeaderWhenRequestIsSecureThenHeaderValueMatchesPassedSource() {\n\t\tClearSiteDataHeaderWriter headerWriter = new ClearSiteDataHeaderWriter(Directive.STORAGE);\n\t\theaderWriter.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(HEADER_NAME)).isEqualTo(\"\\\"storage\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeHeaderWhenRequestIsSecureThenHeaderValueMatchesPassedSources() {\n\t\tClearSiteDataHeaderWriter headerWriter = new ClearSiteDataHeaderWriter(Directive.CACHE, Directive.COOKIES,\n\t\t\t\tDirective.STORAGE, Directive.EXECUTION_CONTEXTS);\n\t\theaderWriter.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(HEADER_NAME))\n\t\t\t.isEqualTo(\"\\\"cache\\\", \\\"cookies\\\", \\\"storage\\\", \\\"executionContexts\\\"\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/CompositeHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.web.header.HeaderWriter;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for class {@link CompositeHeaderWriter}/.\n *\n * @author Ankur Pathak\n * @since 5.2\n */\npublic class CompositeHeaderWriterTests {\n\n\t@Test\n\tpublic void writeHeadersWhenConfiguredWithDelegatesThenInvokesEach() {\n\t\tHttpServletRequest request = mock(HttpServletRequest.class);\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tHeaderWriter one = mock(HeaderWriter.class);\n\t\tHeaderWriter two = mock(HeaderWriter.class);\n\t\tCompositeHeaderWriter headerWriter = new CompositeHeaderWriter(Arrays.asList(one, two));\n\t\theaderWriter.writeHeaders(request, response);\n\t\tverify(one).writeHeaders(request, response);\n\t\tverify(two).writeHeaders(request, response);\n\t}\n\n\t@Test\n\tpublic void constructorWhenPassingEmptyListThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new CompositeHeaderWriter(Collections.emptyList()));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/ContentSecurityPolicyHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Joe Grandja\n * @author Ankur Pathak\n */\npublic class ContentSecurityPolicyHeaderWriterTests {\n\n\tprivate static final String DEFAULT_POLICY_DIRECTIVES = \"default-src 'self'\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate ContentSecurityPolicyHeaderWriter writer;\n\n\tprivate static final String CONTENT_SECURITY_POLICY_HEADER = \"Content-Security-Policy\";\n\n\tprivate static final String CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER = \"Content-Security-Policy-Report-Only\";\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.request.setSecure(true);\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new ContentSecurityPolicyHeaderWriter(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenNoPolicyDirectivesThenUsesDefault() {\n\t\tContentSecurityPolicyHeaderWriter noPolicyWriter = new ContentSecurityPolicyHeaderWriter();\n\t\tnoPolicyWriter.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Content-Security-Policy\")).isEqualTo(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersContentSecurityPolicyDefault() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Content-Security-Policy\")).isEqualTo(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersContentSecurityPolicyCustom() {\n\t\tString policyDirectives = \"default-src 'self'; \" + \"object-src plugins1.example.com plugins2.example.com; \"\n\t\t\t\t+ \"script-src trustedscripts.example.com\";\n\t\tthis.writer = new ContentSecurityPolicyHeaderWriter(policyDirectives);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Content-Security-Policy\")).isEqualTo(policyDirectives);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenNoPolicyDirectivesReportOnlyThenUsesDefault() {\n\t\tContentSecurityPolicyHeaderWriter noPolicyWriter = new ContentSecurityPolicyHeaderWriter();\n\t\tthis.writer.setReportOnly(true);\n\t\tnoPolicyWriter.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Content-Security-Policy\")).isEqualTo(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersContentSecurityPolicyReportOnlyDefault() {\n\t\tthis.writer.setReportOnly(true);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Content-Security-Policy-Report-Only\")).isEqualTo(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersContentSecurityPolicyReportOnlyCustom() {\n\t\tString policyDirectives = \"default-src https:; report-uri https://example.com/\";\n\t\tthis.writer = new ContentSecurityPolicyHeaderWriter(policyDirectives);\n\t\tthis.writer.setReportOnly(true);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Content-Security-Policy-Report-Only\")).isEqualTo(policyDirectives);\n\t}\n\n\t@Test\n\tpublic void writeHeadersContentSecurityPolicyInvalid() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ContentSecurityPolicyHeaderWriter(\"\"));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ContentSecurityPolicyHeaderWriter(null));\n\t}\n\n\t@Test\n\tpublic void writeContentSecurityPolicyHeaderWhenNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(CONTENT_SECURITY_POLICY_HEADER, value);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(CONTENT_SECURITY_POLICY_HEADER)).isSameAs(value);\n\t}\n\n\t@Test\n\tpublic void writeContentSecurityPolicyReportOnlyHeaderWhenNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER, value);\n\t\tthis.writer.setReportOnly(true);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/CrossOriginEmbedderPolicyHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\nclass CrossOriginEmbedderPolicyHeaderWriterTests {\n\n\tprivate static final String EMBEDDER_HEADER_NAME = \"Cross-Origin-Embedder-Policy\";\n\n\tprivate CrossOriginEmbedderPolicyHeaderWriter writer;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.writer = new CrossOriginEmbedderPolicyHeaderWriter();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tvoid setEmbedderPolicyWhenNullEmbedderPolicyThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))\n\t\t\t.withMessage(\"embedderPolicy cannot be null\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenDefaultValuesThenDontWriteHeaders() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(0);\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenResponseHeaderExistsThenDontOverride() {\n\t\tthis.response.addHeader(EMBEDDER_HEADER_NAME, \"require-corp\");\n\t\tthis.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.UNSAFE_NONE);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo(\"require-corp\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetHeaderValuesThenWrites() {\n\t\tthis.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo(\"require-corp\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetEmbedderPolicyThenWritesEmbedderPolicy() {\n\t\tthis.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.UNSAFE_NONE);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo(\"unsafe-none\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/CrossOriginOpenerPolicyHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\nclass CrossOriginOpenerPolicyHeaderWriterTests {\n\n\tprivate static final String OPENER_HEADER_NAME = \"Cross-Origin-Opener-Policy\";\n\n\tprivate CrossOriginOpenerPolicyHeaderWriter writer;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.writer = new CrossOriginOpenerPolicyHeaderWriter();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tvoid setOpenerPolicyWhenNullOpenerPolicyThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))\n\t\t\t.withMessage(\"openerPolicy cannot be null\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenDefaultValuesThenDontWriteHeaders() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(0);\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenResponseHeaderExistsThenDontOverride() {\n\t\tthis.response.addHeader(OPENER_HEADER_NAME, \"same-origin\");\n\t\tthis.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo(\"same-origin\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetHeaderValuesThenWrites() {\n\t\tthis.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo(\"same-origin-allow-popups\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetOpenerPolicyThenWritesOpenerPolicy() {\n\t\tthis.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo(\"same-origin-allow-popups\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/CrossOriginResourcePolicyHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\nclass CrossOriginResourcePolicyHeaderWriterTests {\n\n\tprivate static final String RESOURCE_HEADER_NAME = \"Cross-Origin-Resource-Policy\";\n\n\tprivate CrossOriginResourcePolicyHeaderWriter writer;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.writer = new CrossOriginResourcePolicyHeaderWriter();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tvoid setResourcePolicyWhenNullThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))\n\t\t\t.withMessage(\"resourcePolicy cannot be null\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenDefaultValuesThenDontWriteHeaders() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(0);\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenResponseHeaderExistsThenDontOverride() {\n\t\tthis.response.addHeader(RESOURCE_HEADER_NAME, \"same-site\");\n\t\tthis.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.CROSS_ORIGIN);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo(\"same-site\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetHeaderValuesThenWrites() {\n\t\tthis.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo(\"same-origin\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetResourcePolicyThenWritesResourcePolicy() {\n\t\tthis.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_SITE);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo(\"same-site\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/DelegatingRequestMatcherHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.header.HeaderWriter;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingRequestMatcherHeaderWriterTests {\n\n\t@Mock\n\tprivate RequestMatcher matcher;\n\n\t@Mock\n\tprivate HeaderWriter delegate;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate DelegatingRequestMatcherHeaderWriter headerWriter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.headerWriter = new DelegatingRequestMatcherHeaderWriter(this.matcher, this.delegate);\n\t}\n\n\t@Test\n\tpublic void constructorNullRequestMatcher() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingRequestMatcherHeaderWriter(null, this.delegate));\n\t}\n\n\t@Test\n\tpublic void constructorNullDelegate() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingRequestMatcherHeaderWriter(this.matcher, null));\n\t}\n\n\t@Test\n\tpublic void writeHeadersOnMatch() {\n\t\tgiven(this.matcher.matches(this.request)).willReturn(true);\n\t\tthis.headerWriter.writeHeaders(this.request, this.response);\n\t\tverify(this.delegate).writeHeaders(this.request, this.response);\n\t}\n\n\t@Test\n\tpublic void writeHeadersOnNoMatch() {\n\t\tgiven(this.matcher.matches(this.request)).willReturn(false);\n\t\tthis.headerWriter.writeHeaders(this.request, this.response);\n\t\tverify(this.delegate, times(0)).writeHeaders(this.request, this.response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/FeaturePolicyHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link FeaturePolicyHeaderWriter}.\n *\n * @author Vedran Pavic\n * @author Ankur Pathak\n */\npublic class FeaturePolicyHeaderWriterTests {\n\n\tprivate static final String DEFAULT_POLICY_DIRECTIVES = \"geolocation 'self'\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate FeaturePolicyHeaderWriter writer;\n\n\tprivate static final String FEATURE_POLICY_HEADER = \"Feature-Policy\";\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new FeaturePolicyHeaderWriter(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersFeaturePolicyDefault() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Feature-Policy\")).isEqualTo(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void createWriterWithNullDirectivesShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new FeaturePolicyHeaderWriter(null))\n\t\t\t.withMessage(\"policyDirectives must not be null or empty\");\n\t}\n\n\t@Test\n\tpublic void createWriterWithEmptyDirectivesShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new FeaturePolicyHeaderWriter(\"\"))\n\t\t\t.withMessage(\"policyDirectives must not be null or empty\");\n\t}\n\n\t@Test\n\tpublic void writeHeaderOnlyIfNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(FEATURE_POLICY_HEADER, value);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(FEATURE_POLICY_HEADER)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/HpkpHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Tim Ysewyn\n * @author Ankur Pathak\n *\n */\npublic class HpkpHeaderWriterTests {\n\n\tprivate static final Map<String, String> DEFAULT_PINS;\n\tstatic {\n\t\tMap<String, String> defaultPins = new LinkedHashMap<>();\n\t\tdefaultPins.put(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\");\n\t\tDEFAULT_PINS = Collections.unmodifiableMap(defaultPins);\n\t}\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate HpkpHeaderWriter writer;\n\n\tprivate static final String HPKP_HEADER_NAME = \"Public-Key-Pins\";\n\n\tprivate static final String HPKP_RO_HEADER_NAME = \"Public-Key-Pins-Report-Only\";\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new HpkpHeaderWriter();\n\t\tMap<String, String> defaultPins = new LinkedHashMap<>();\n\t\tdefaultPins.put(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", \"sha256\");\n\t\tthis.writer.setPins(defaultPins);\n\t\tthis.request.setSecure(true);\n\t}\n\n\t@Test\n\tpublic void writeHeadersDefaultValues() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins-Report-Only\"))\n\t\t\t.isEqualTo(\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\");\n\t}\n\n\t@Test\n\tpublic void maxAgeCustomConstructorWriteHeaders() {\n\t\tthis.writer = new HpkpHeaderWriter(2592000);\n\t\tthis.writer.setPins(DEFAULT_PINS);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins-Report-Only\"))\n\t\t\t.isEqualTo(\"max-age=2592000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\");\n\t}\n\n\t@Test\n\tpublic void maxAgeAndIncludeSubdomainsCustomConstructorWriteHeaders() {\n\t\tthis.writer = new HpkpHeaderWriter(2592000, true);\n\t\tthis.writer.setPins(DEFAULT_PINS);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins-Report-Only\")).isEqualTo(\n\t\t\t\t\"max-age=2592000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; includeSubDomains\");\n\t}\n\n\t@Test\n\tpublic void allArgsCustomConstructorWriteHeaders() {\n\t\tthis.writer = new HpkpHeaderWriter(2592000, true, false);\n\t\tthis.writer.setPins(DEFAULT_PINS);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins\")).isEqualTo(\n\t\t\t\t\"max-age=2592000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; includeSubDomains\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersCustomMaxAgeInSeconds() {\n\t\tthis.writer.setMaxAgeInSeconds(2592000);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins-Report-Only\"))\n\t\t\t.isEqualTo(\"max-age=2592000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersIncludeSubDomains() {\n\t\tthis.writer.setIncludeSubDomains(true);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins-Report-Only\")).isEqualTo(\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; includeSubDomains\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersTerminateConnection() {\n\t\tthis.writer.setReportOnly(false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins\"))\n\t\t\t.isEqualTo(\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersTerminateConnectionWithURI() throws URISyntaxException {\n\t\tthis.writer.setReportOnly(false);\n\t\tthis.writer.setReportUri(new URI(\"https://example.com/pkp-report\"));\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins\")).isEqualTo(\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; report-uri=\\\"https://example.com/pkp-report\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersTerminateConnectionWithURIAsString() {\n\t\tthis.writer.setReportOnly(false);\n\t\tthis.writer.setReportUri(\"https://example.com/pkp-report\");\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins\")).isEqualTo(\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; report-uri=\\\"https://example.com/pkp-report\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersAddSha256Pins() {\n\t\tthis.writer.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\",\n\t\t\t\t\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\");\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Public-Key-Pins-Report-Only\")).isEqualTo(\n\t\t\t\t\"max-age=5184000 ; pin-sha256=\\\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\\\" ; pin-sha256=\\\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\\\"\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersInsecureRequestDoesNotWriteHeader() {\n\t\tthis.request.setSecure(false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void setMaxAgeInSecondsToNegative() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setMaxAgeInSeconds(-1));\n\t}\n\n\t@Test\n\tpublic void addSha256PinsWithNullPin() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.writer.addSha256Pins(\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\", null));\n\t}\n\n\t@Test\n\tpublic void setIncorrectReportUri() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setReportUri(\"some url here...\"));\n\t}\n\n\t@Test\n\tpublic void writePublicKeyPinsHeaderOnlyWhenNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(HPKP_HEADER_NAME, value);\n\t\tthis.writer.setReportOnly(false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(HPKP_HEADER_NAME)).isSameAs(value);\n\t}\n\n\t@Test\n\tpublic void writePublicKeyPinsReportOnlyHeaderWhenNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(HPKP_RO_HEADER_NAME, value);\n\t\tthis.writer.setReportOnly(false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(HPKP_RO_HEADER_NAME)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/HstsHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.util.matcher.AnyRequestMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @author Ankur Pathak\n *\n */\npublic class HstsHeaderWriterTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate HstsHeaderWriter writer;\n\n\tprivate static final String HSTS_HEADER_NAME = \"Strict-Transport-Security\";\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.request.setSecure(true);\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new HstsHeaderWriter();\n\t}\n\n\t@Test\n\tpublic void allArgsCustomConstructorWriteHeaders() {\n\t\tthis.request.setSecure(false);\n\t\tthis.writer = new HstsHeaderWriter(AnyRequestMatcher.INSTANCE, 15768000, false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Strict-Transport-Security\")).isEqualTo(\"max-age=15768000\");\n\t}\n\n\t@Test\n\tpublic void maxAgeAndIncludeSubdomainsCustomConstructorWriteHeaders() {\n\t\tthis.request.setSecure(false);\n\t\tthis.writer = new HstsHeaderWriter(AnyRequestMatcher.INSTANCE, 15768000, false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Strict-Transport-Security\")).isEqualTo(\"max-age=15768000\");\n\t}\n\n\t@Test\n\tpublic void maxAgeCustomConstructorWriteHeaders() {\n\t\tthis.writer = new HstsHeaderWriter(15768000);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Strict-Transport-Security\"))\n\t\t\t.isEqualTo(\"max-age=15768000 ; includeSubDomains\");\n\t}\n\n\t@Test\n\tpublic void includeSubDomainsCustomConstructorWriteHeaders() {\n\t\tthis.writer = new HstsHeaderWriter(false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Strict-Transport-Security\")).isEqualTo(\"max-age=31536000\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersDefaultValues() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Strict-Transport-Security\"))\n\t\t\t.isEqualTo(\"max-age=31536000 ; includeSubDomains\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersIncludeSubDomainsFalse() {\n\t\tthis.writer.setIncludeSubDomains(false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Strict-Transport-Security\")).isEqualTo(\"max-age=31536000\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersCustomMaxAgeInSeconds() {\n\t\tthis.writer.setMaxAgeInSeconds(1);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Strict-Transport-Security\")).isEqualTo(\"max-age=1 ; includeSubDomains\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersInsecureRequestDoesNotWriteHeader() {\n\t\tthis.request.setSecure(false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void writeHeadersAnyRequestMatcher() {\n\t\tthis.writer.setRequestMatcher(AnyRequestMatcher.INSTANCE);\n\t\tthis.request.setSecure(false);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Strict-Transport-Security\"))\n\t\t\t.isEqualTo(\"max-age=31536000 ; includeSubDomains\");\n\t}\n\n\t@Test\n\tpublic void setMaxAgeInSecondsToNegative() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setMaxAgeInSeconds(-1));\n\t}\n\n\t@Test\n\tpublic void setRequestMatcherToNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setRequestMatcher(null));\n\t}\n\n\t@Test\n\tpublic void writeHeaderWhenNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(HSTS_HEADER_NAME, value);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(HSTS_HEADER_NAME)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/PermissionsPolicyHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link PermissionsPolicyHeaderWriter}.\n *\n * @author Christophe Gilles\n */\npublic class PermissionsPolicyHeaderWriterTests {\n\n\tprivate static final String DEFAULT_POLICY_DIRECTIVES = \"geolocation=(self)\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate PermissionsPolicyHeaderWriter writer;\n\n\tprivate static final String PERMISSIONS_POLICY_HEADER = \"Permissions-Policy\";\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new PermissionsPolicyHeaderWriter(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersPermissionsPolicyDefault() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Permissions-Policy\")).isEqualTo(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void createWriterWithNullPolicyShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PermissionsPolicyHeaderWriter(null))\n\t\t\t.withMessage(\"policy can not be null or empty\");\n\t}\n\n\t@Test\n\tpublic void createWriterWithEmptyPolicyShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PermissionsPolicyHeaderWriter(\"\"))\n\t\t\t.withMessage(\"policy can not be null or empty\");\n\t}\n\n\t@Test\n\tpublic void writeHeaderOnlyIfNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(PERMISSIONS_POLICY_HEADER, value);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(PERMISSIONS_POLICY_HEADER)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/ReferrerPolicyHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Eddú Meléndez\n * @author Ankur Pathak\n */\npublic class ReferrerPolicyHeaderWriterTests {\n\n\tprivate final String DEFAULT_REFERRER_POLICY = \"no-referrer\";\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate ReferrerPolicyHeaderWriter writer;\n\n\tprivate static final String REFERRER_POLICY_HEADER = \"Referrer-Policy\";\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.request.setSecure(true);\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new ReferrerPolicyHeaderWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeadersReferrerPolicyDefault() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Referrer-Policy\")).isEqualTo(this.DEFAULT_REFERRER_POLICY);\n\t}\n\n\t@Test\n\tpublic void writeHeadersReferrerPolicyCustom() {\n\t\tthis.writer = new ReferrerPolicyHeaderWriter(ReferrerPolicy.SAME_ORIGIN);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(\"Referrer-Policy\")).isEqualTo(\"same-origin\");\n\t}\n\n\t@Test\n\tpublic void writeHeaderReferrerPolicyInvalid() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ReferrerPolicyHeaderWriter(null));\n\t}\n\n\t@Test\n\tpublic void writeHeaderWhenNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(REFERRER_POLICY_HEADER, value);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(REFERRER_POLICY_HEADER)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/StaticHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.header.Header;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Test for the {@code StaticHeadersWriter}\n *\n * @author Marten Deinum\n * @author Rob Winch\n * @author Ankur Pathak\n * @since 3.2\n */\npublic class StaticHeaderWriterTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void constructorNullHeaders() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new StaticHeadersWriter(null));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyHeaders() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new StaticHeadersWriter(Collections.<Header>emptyList()));\n\t}\n\n\t@Test\n\tpublic void constructorNullHeaderName() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new StaticHeadersWriter(null, \"value1\"));\n\t}\n\n\t@Test\n\tpublic void constructorNullHeaderValues() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new StaticHeadersWriter(\"name\", (String[]) null));\n\t}\n\n\t@Test\n\tpublic void constructorContainsNullHeaderValue() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new StaticHeadersWriter(\"name\", \"value1\", null));\n\t}\n\n\t@Test\n\tpublic void sameHeaderShouldBeReturned() {\n\t\tString headerName = \"X-header\";\n\t\tString headerValue = \"foo\";\n\t\tStaticHeadersWriter factory = new StaticHeadersWriter(headerName, headerValue);\n\t\tfactory.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderValues(headerName)).isEqualTo(Arrays.asList(headerValue));\n\t}\n\n\t@Test\n\tpublic void writeHeadersMulti() {\n\t\tHeader pragma = new Header(\"Pragma\", \"no-cache\");\n\t\tHeader cacheControl = new Header(\"Cache-Control\", \"no-cache\", \"no-store\", \"must-revalidate\");\n\t\tStaticHeadersWriter factory = new StaticHeadersWriter(Arrays.asList(pragma, cacheControl));\n\t\tfactory.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(2);\n\t\tassertThat(this.response.getHeaderValues(pragma.getName())).isEqualTo(pragma.getValues());\n\t\tassertThat(this.response.getHeaderValues(cacheControl.getName())).isEqualTo(cacheControl.getValues());\n\t}\n\n\t@Test\n\tpublic void writeHeaderWhenNotPresent() {\n\t\tString pragmaValue = new String(\"pragmaValue\");\n\t\tString cacheControlValue = new String(\"cacheControlValue\");\n\t\tthis.response.setHeader(\"Pragma\", pragmaValue);\n\t\tthis.response.setHeader(\"Cache-Control\", cacheControlValue);\n\t\tHeader pragma = new Header(\"Pragma\", \"no-cache\");\n\t\tHeader cacheControl = new Header(\"Cache-Control\", \"no-cache\", \"no-store\", \"must-revalidate\");\n\t\tStaticHeadersWriter factory = new StaticHeadersWriter(Arrays.asList(pragma, cacheControl));\n\t\tfactory.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(2);\n\t\tassertThat(this.response.getHeader(\"Pragma\")).isSameAs(pragmaValue);\n\t\tassertThat(this.response.getHeader(\"Cache-Control\")).isSameAs(cacheControlValue);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/XContentTypeOptionsHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\npublic class XContentTypeOptionsHeaderWriterTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate XContentTypeOptionsHeaderWriter writer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new XContentTypeOptionsHeaderWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeaders() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeaderValues(\"X-Content-Type-Options\")).containsExactly(\"nosniff\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/XXssProtectionHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @author Ankur Pathak\n * @author Daniel Garnier-Moiroux\n *\n */\npublic class XXssProtectionHeaderWriterTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate XXssProtectionHeaderWriter writer;\n\n\tprivate static final String XSS_PROTECTION_HEADER = \"X-XSS-Protection\";\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.writer = new XXssProtectionHeaderWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeaders() {\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeaderValues(\"X-XSS-Protection\")).containsOnly(\"0\");\n\t}\n\n\t@Test\n\tpublic void writeHeaderWhenNotPresent() {\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(XSS_PROTECTION_HEADER, value);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(XSS_PROTECTION_HEADER)).isSameAs(value);\n\t}\n\n\t@Test\n\tvoid writeHeaderWhenDisabled() {\n\t\tthis.writer.setHeaderValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeaderValues(\"X-XSS-Protection\")).containsOnly(\"0\");\n\t}\n\n\t@Test\n\tvoid writeHeaderWhenEnabled() {\n\t\tthis.writer.setHeaderValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeaderValues(\"X-XSS-Protection\")).containsOnly(\"1\");\n\t}\n\n\t@Test\n\tvoid writeHeaderWhenEnabledModeBlock() {\n\t\tthis.writer.setHeaderValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeaderValues(\"X-XSS-Protection\")).containsOnly(\"1; mode=block\");\n\t}\n\n\t@Test\n\tpublic void setHeaderValueNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setHeaderValue(null))\n\t\t\t.withMessage(\"headerValue cannot be null\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/frameoptions/AbstractRequestParameterAllowFromStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\npublic class AbstractRequestParameterAllowFromStrategyTests {\n\n\tprivate MockHttpServletRequest request;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t}\n\n\t@Test\n\tpublic void nullAllowFromParameterValue() {\n\t\tRequestParameterAllowFromStrategyStub strategy = new RequestParameterAllowFromStrategyStub(true);\n\t\tassertThat(strategy.getAllowFromValue(this.request)).isEqualTo(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void emptyAllowFromParameterValue() {\n\t\tthis.request.setParameter(\"x-frames-allow-from\", \"\");\n\t\tRequestParameterAllowFromStrategyStub strategy = new RequestParameterAllowFromStrategyStub(true);\n\t\tassertThat(strategy.getAllowFromValue(this.request)).isEqualTo(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void emptyAllowFromCustomParameterValue() {\n\t\tString customParam = \"custom\";\n\t\tthis.request.setParameter(customParam, \"\");\n\t\tRequestParameterAllowFromStrategyStub strategy = new RequestParameterAllowFromStrategyStub(true);\n\t\tstrategy.setAllowFromParameterName(customParam);\n\t\tassertThat(strategy.getAllowFromValue(this.request)).isEqualTo(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void allowFromParameterValueAllowed() {\n\t\tString value = \"https://example.com\";\n\t\tthis.request.setParameter(\"x-frames-allow-from\", value);\n\t\tRequestParameterAllowFromStrategyStub strategy = new RequestParameterAllowFromStrategyStub(true);\n\t\tassertThat(strategy.getAllowFromValue(this.request)).isEqualTo(value);\n\t}\n\n\t@Test\n\tpublic void allowFromParameterValueDenied() {\n\t\tString value = \"https://example.com\";\n\t\tthis.request.setParameter(\"x-frames-allow-from\", value);\n\t\tRequestParameterAllowFromStrategyStub strategy = new RequestParameterAllowFromStrategyStub(false);\n\t\tassertThat(strategy.getAllowFromValue(this.request)).isEqualTo(\"DENY\");\n\t}\n\n\tprivate static class RequestParameterAllowFromStrategyStub extends AbstractRequestParameterAllowFromStrategy {\n\n\t\tprivate boolean match;\n\n\t\tRequestParameterAllowFromStrategyStub(boolean match) {\n\t\t\tthis.match = match;\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean allowed(String allowFromOrigin) {\n\t\t\treturn this.match;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/frameoptions/FrameOptionsHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class FrameOptionsHeaderWriterTests {\n\n\t@Mock\n\tprivate AllowFromStrategy strategy;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate XFrameOptionsHeaderWriter writer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@Test\n\tpublic void constructorNullMode() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new XFrameOptionsHeaderWriter((XFrameOptionsMode) null));\n\t}\n\n\t@Test\n\tpublic void constructorAllowFromNoAllowFromStrategy() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new XFrameOptionsHeaderWriter(XFrameOptionsMode.ALLOW_FROM));\n\t}\n\n\t@Test\n\tpublic void constructorNullAllowFromStrategy() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new XFrameOptionsHeaderWriter((AllowFromStrategy) null));\n\t}\n\n\t@Test\n\tpublic void writeHeadersAllowFromReturnsNull() {\n\t\tthis.writer = new XFrameOptionsHeaderWriter(this.strategy);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void writeHeadersAllowFrom() {\n\t\tString allowFromValue = \"https://example.com/\";\n\t\tgiven(this.strategy.getAllowFromValue(this.request)).willReturn(allowFromValue);\n\t\tthis.writer = new XFrameOptionsHeaderWriter(this.strategy);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(XFrameOptionsHeaderWriter.XFRAME_OPTIONS_HEADER))\n\t\t\t.isEqualTo(\"ALLOW-FROM \" + allowFromValue);\n\t}\n\n\t@Test\n\tpublic void writeHeadersDeny() {\n\t\tthis.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(XFrameOptionsHeaderWriter.XFRAME_OPTIONS_HEADER)).isEqualTo(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersSameOrigin() {\n\t\tthis.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(XFrameOptionsHeaderWriter.XFRAME_OPTIONS_HEADER)).isEqualTo(\"SAMEORIGIN\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersTwiceLastWins() {\n\t\tthis.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tthis.writer = new XFrameOptionsHeaderWriter(XFrameOptionsMode.DENY);\n\t\tthis.writer.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderNames()).hasSize(1);\n\t\tassertThat(this.response.getHeader(XFrameOptionsHeaderWriter.XFRAME_OPTIONS_HEADER)).isEqualTo(\"DENY\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/frameoptions/RegExpAllowFromStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport java.util.regex.PatternSyntaxException;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Marten Deinum\n */\npublic class RegExpAllowFromStrategyTests {\n\n\t@Test\n\tpublic void invalidRegularExpressionShouldLeadToException() {\n\t\tassertThatExceptionOfType(PatternSyntaxException.class).isThrownBy(() -> new RegExpAllowFromStrategy(\"[a-z\"));\n\t}\n\n\t@Test\n\tpublic void nullRegularExpressionShouldLeadToException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RegExpAllowFromStrategy(null));\n\t}\n\n\t@Test\n\tpublic void subdomainMatchingRegularExpression() {\n\t\tRegExpAllowFromStrategy strategy = new RegExpAllowFromStrategy(\"^https://([a-z0-9]*?\\\\.)test\\\\.com\");\n\t\tstrategy.setAllowFromParameterName(\"from\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"from\", \"https://www.test.com\");\n\t\tString result1 = strategy.getAllowFromValue(request);\n\t\tassertThat(result1).isEqualTo(\"https://www.test.com\");\n\t\trequest.setParameter(\"from\", \"https://www.test.com\");\n\t\tString result2 = strategy.getAllowFromValue(request);\n\t\tassertThat(result2).isEqualTo(\"https://www.test.com\");\n\t\trequest.setParameter(\"from\", \"https://test.foobar.com\");\n\t\tString result3 = strategy.getAllowFromValue(request);\n\t\tassertThat(result3).isEqualTo(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void noParameterShouldDeny() {\n\t\tRegExpAllowFromStrategy strategy = new RegExpAllowFromStrategy(\"^https://([a-z0-9]*?\\\\.)test\\\\.com\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tString result1 = strategy.getAllowFromValue(request);\n\t\tassertThat(result1).isEqualTo(\"DENY\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/frameoptions/StaticAllowFromStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport java.net.URI;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Test for the StaticAllowFromStrategy.\n *\n * @author Marten Deinum\n * @since 3.2\n */\npublic class StaticAllowFromStrategyTests {\n\n\t@Test\n\tpublic void shouldReturnUri() {\n\t\tString uri = \"https://www.test.com\";\n\t\tStaticAllowFromStrategy strategy = new StaticAllowFromStrategy(URI.create(uri));\n\t\tassertThat(strategy.getAllowFromValue(new MockHttpServletRequest())).isEqualTo(uri);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/frameoptions/WhiteListedAllowFromStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Test for the {@code WhiteListedAllowFromStrategy}.\n *\n * @author Marten Deinum\n * @since 3.2\n */\npublic class WhiteListedAllowFromStrategyTests {\n\n\t@Test\n\tpublic void emptyListShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new WhiteListedAllowFromStrategy(new ArrayList<>()));\n\t}\n\n\t@Test\n\tpublic void nullListShouldThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new WhiteListedAllowFromStrategy(null));\n\t}\n\n\t@Test\n\tpublic void listWithSingleElementShouldMatch() {\n\t\tList<String> allowed = new ArrayList<>();\n\t\tallowed.add(\"https://www.test.com\");\n\t\tWhiteListedAllowFromStrategy strategy = new WhiteListedAllowFromStrategy(allowed);\n\t\tstrategy.setAllowFromParameterName(\"from\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"from\", \"https://www.test.com\");\n\t\tString result = strategy.getAllowFromValue(request);\n\t\tassertThat(result).isEqualTo(\"https://www.test.com\");\n\t}\n\n\t@Test\n\tpublic void listWithMultipleElementShouldMatch() {\n\t\tList<String> allowed = new ArrayList<>();\n\t\tallowed.add(\"https://www.test.com\");\n\t\tallowed.add(\"https://www.springsource.org\");\n\t\tWhiteListedAllowFromStrategy strategy = new WhiteListedAllowFromStrategy(allowed);\n\t\tstrategy.setAllowFromParameterName(\"from\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"from\", \"https://www.test.com\");\n\t\tString result = strategy.getAllowFromValue(request);\n\t\tassertThat(result).isEqualTo(\"https://www.test.com\");\n\t}\n\n\t@Test\n\tpublic void listWithSingleElementShouldNotMatch() {\n\t\tList<String> allowed = new ArrayList<>();\n\t\tallowed.add(\"https://www.test.com\");\n\t\tWhiteListedAllowFromStrategy strategy = new WhiteListedAllowFromStrategy(allowed);\n\t\tstrategy.setAllowFromParameterName(\"from\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setParameter(\"from\", \"https://www.test123.com\");\n\t\tString result = strategy.getAllowFromValue(request);\n\t\tassertThat(result).isEqualTo(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void requestWithoutParameterShouldNotMatch() {\n\t\tList<String> allowed = new ArrayList<>();\n\t\tallowed.add(\"https://www.test.com\");\n\t\tWhiteListedAllowFromStrategy strategy = new WhiteListedAllowFromStrategy(allowed);\n\t\tstrategy.setAllowFromParameterName(\"from\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tString result = strategy.getAllowFromValue(request);\n\t\tassertThat(result).isEqualTo(\"DENY\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/header/writers/frameoptions/XFrameOptionsHeaderWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.header.writers.frameoptions;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @author Ankur Pathak\n * @since 5.0\n */\npublic class XFrameOptionsHeaderWriterTests {\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate MockHttpServletResponse response = new MockHttpServletResponse();\n\n\tprivate static final String XFRAME_OPTIONS_HEADER = \"X-Frame-Options\";\n\n\t@Test\n\tpublic void writeHeadersWhenWhiteList() {\n\t\tWhiteListedAllowFromStrategy whitelist = new WhiteListedAllowFromStrategy(Arrays.asList(\"example.com\"));\n\t\tXFrameOptionsHeaderWriter writer = new XFrameOptionsHeaderWriter(whitelist);\n\t\twriter.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeaderValue(XFrameOptionsHeaderWriter.XFRAME_OPTIONS_HEADER)).isEqualTo(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void writeHeaderWhenNotPresent() {\n\t\tWhiteListedAllowFromStrategy whitelist = new WhiteListedAllowFromStrategy(\n\t\t\t\tCollections.singletonList(\"example.com\"));\n\t\tXFrameOptionsHeaderWriter writer = new XFrameOptionsHeaderWriter(whitelist);\n\t\tString value = new String(\"value\");\n\t\tthis.response.setHeader(XFRAME_OPTIONS_HEADER, value);\n\t\twriter.writeHeaders(this.request, this.response);\n\t\tassertThat(this.response.getHeader(XFRAME_OPTIONS_HEADER)).isSameAs(value);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jaasapi/JaasApiIntegrationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jaasapi;\n\nimport java.io.IOException;\nimport java.util.HashMap;\n\nimport javax.security.auth.Subject;\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.PasswordCallback;\nimport javax.security.auth.callback.TextInputCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.auth.login.AppConfigurationEntry;\nimport javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;\nimport javax.security.auth.login.Configuration;\nimport javax.security.auth.login.LoginContext;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.ServletRequest;\nimport jakarta.servlet.ServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.jaas.JaasAuthenticationToken;\nimport org.springframework.security.authentication.jaas.TestLoginModule;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests the JaasApiIntegrationFilter.\n *\n * @author Rob Winch\n */\npublic class JaasApiIntegrationFilterTests {\n\n\tprivate JaasApiIntegrationFilter filter;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate Authentication token;\n\n\tprivate Subject authenticatedSubject;\n\n\tprivate Configuration testConfiguration;\n\n\tprivate CallbackHandler callbackHandler;\n\n\t@BeforeEach\n\tpublic void onBeforeTests() throws Exception {\n\t\tthis.filter = new JaasApiIntegrationFilter();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t\tthis.authenticatedSubject = new Subject();\n\t\tthis.authenticatedSubject.getPrincipals().add(() -> \"principal\");\n\t\tthis.authenticatedSubject.getPrivateCredentials().add(\"password\");\n\t\tthis.authenticatedSubject.getPublicCredentials().add(\"username\");\n\t\tthis.callbackHandler = (callbacks) -> {\n\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\tif (callback instanceof NameCallback) {\n\t\t\t\t\t((NameCallback) callback).setName(\"user\");\n\t\t\t\t}\n\t\t\t\telse if (callback instanceof PasswordCallback) {\n\t\t\t\t\t((PasswordCallback) callback).setPassword(\"password\".toCharArray());\n\t\t\t\t}\n\t\t\t\telse if (callback instanceof TextInputCallback) {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new UnsupportedCallbackException(callback, \"Unrecognized Callback \" + callback);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tthis.testConfiguration = new Configuration() {\n\t\t\t@Override\n\t\t\tpublic void refresh() {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic AppConfigurationEntry[] getAppConfigurationEntry(String name) {\n\t\t\t\treturn new AppConfigurationEntry[] { new AppConfigurationEntry(TestLoginModule.class.getName(),\n\t\t\t\t\t\tLoginModuleControlFlag.REQUIRED, new HashMap<>()) };\n\t\t\t}\n\t\t};\n\t\tLoginContext ctx = new LoginContext(\"SubjectDoAsFilterTest\", this.authenticatedSubject, this.callbackHandler,\n\t\t\t\tthis.testConfiguration);\n\t\tctx.login();\n\t\tthis.token = new JaasAuthenticationToken(\"username\", \"password\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ADMIN\"), ctx);\n\t\t// just in case someone forgot to clear the context\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@AfterEach\n\tpublic void onAfterTests() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t/**\n\t * Ensure a Subject was not setup in some other manner.\n\t */\n\t@Test\n\tpublic void currentSubjectNull() {\n\t\tassertThat(Subject.current()).isNull();\n\t}\n\n\t@Test\n\tpublic void obtainSubjectNullAuthentication() {\n\t\tassertNullSubject(this.filter.obtainSubject(this.request));\n\t}\n\n\t@Test\n\tpublic void obtainSubjectNonJaasAuthentication() {\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"un\", \"pwd\");\n\t\tauthentication.setAuthenticated(true);\n\t\tSecurityContextHolder.getContext().setAuthentication(authentication);\n\t\tassertNullSubject(this.filter.obtainSubject(this.request));\n\t}\n\n\t@Test\n\tpublic void obtainSubjectNullLoginContext() {\n\t\tthis.token = new JaasAuthenticationToken(\"un\", \"pwd\", AuthorityUtils.createAuthorityList(\"ROLE_ADMIN\"), null);\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tassertNullSubject(this.filter.obtainSubject(this.request));\n\t}\n\n\t@Test\n\tpublic void obtainSubjectNullSubject() throws Exception {\n\t\tLoginContext ctx = new LoginContext(\"obtainSubjectNullSubject\", null, this.callbackHandler,\n\t\t\t\tthis.testConfiguration);\n\t\tassertThat(ctx.getSubject()).isNull();\n\t\tthis.token = new JaasAuthenticationToken(\"un\", \"pwd\", AuthorityUtils.createAuthorityList(\"ROLE_ADMIN\"), ctx);\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tassertNullSubject(this.filter.obtainSubject(this.request));\n\t}\n\n\t@Test\n\tpublic void obtainSubject() {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tassertThat(this.filter.obtainSubject(this.request)).isEqualTo(this.authenticatedSubject);\n\t}\n\n\t@Test\n\tpublic void doFilterCurrentSubjectPopulated() throws Exception {\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tassertJaasSubjectEquals(this.authenticatedSubject);\n\t}\n\n\t@Test\n\tpublic void doFilterAuthenticationNotAuthenticated() throws Exception {\n\t\t// Authentication is null, so no Subject is populated.\n\t\tthis.token.setAuthenticated(false);\n\t\tSecurityContextHolder.getContext().setAuthentication(this.token);\n\t\tassertJaasSubjectEquals(null);\n\t\tthis.filter.setCreateEmptySubject(true);\n\t\tassertJaasSubjectEquals(new Subject());\n\t}\n\n\t@Test\n\tpublic void doFilterAuthenticationNull() throws Exception {\n\t\tassertJaasSubjectEquals(null);\n\t\tthis.filter.setCreateEmptySubject(true);\n\t\tassertJaasSubjectEquals(new Subject());\n\t}\n\n\t@Test\n\tpublic void doFilterUsesCustomSecurityContextHolderStrategy() throws Exception {\n\t\tSecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);\n\t\tgiven(strategy.getContext()).willReturn(new SecurityContextImpl(this.token));\n\t\tthis.filter.setSecurityContextHolderStrategy(strategy);\n\t\tassertJaasSubjectEquals(this.authenticatedSubject);\n\t}\n\n\tprivate void assertJaasSubjectEquals(final Subject expectedValue) throws Exception {\n\t\tMockFilterChain chain = new MockFilterChain() {\n\t\t\t@Override\n\t\t\tpublic void doFilter(ServletRequest request, ServletResponse response)\n\t\t\t\t\tthrows IOException, ServletException {\n\t\t\t\t// See if the subject was updated\n\t\t\t\tSubject currentSubject = Subject.current();\n\t\t\t\tassertThat(currentSubject).isEqualTo(expectedValue);\n\t\t\t\t// run so we know the chain was executed\n\t\t\t\tsuper.doFilter(request, response);\n\t\t\t}\n\t\t};\n\t\tthis.filter.doFilter(this.request, this.response, chain);\n\t\t// ensure that the chain was actually invoked\n\t\tassertThat(chain.getRequest()).isNotNull();\n\t}\n\n\tprivate void assertNullSubject(Subject subject) {\n\t\tassertThat(subject).withFailMessage(\"Subject is expected to be null, but is not. Got \" + subject).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson/AbstractMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.jackson.SecurityJacksonModules;\n\n/**\n * @author Sebastien Deleuze\n * @author Jitenra Singh\n */\npublic abstract class AbstractMixinTests {\n\n\tprotected JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson/CookieMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport jakarta.servlet.http.Cookie;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class CookieMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String COOKIE_JSON = \"{\" +\n\t\t\"\t\\\"@class\\\": \\\"jakarta.servlet.http.Cookie\\\",\" +\n\t\t\"\t\\\"name\\\": \\\"demo\\\",\" +\n\t\t\"\t\\\"value\\\": \\\"cookie1\\\",\" +\n\t\t\"\t\\\"attributes\\\":{\\\"@class\\\":\\\"java.util.Collections$EmptyMap\\\"},\" +\n\t\t\"\t\\\"comment\\\": null,\" +\n\t\t\"\t\\\"maxAge\\\": -1,\" +\n\t\t\"\t\\\"path\\\": null,\" +\n\t\t\"\t\\\"secure\\\": false,\" +\n\t\t\"\t\\\"version\\\": 0,\" +\n\t\t\"\t\\\"domain\\\": null\" +\n\t\t\"}\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String COOKIE_HTTP_ONLY_JSON = \"{\" +\n\t\t\"\t\\\"@class\\\": \\\"jakarta.servlet.http.Cookie\\\",\" +\n\t\t\"\t\\\"name\\\": \\\"demo\\\",\" +\n\t\t\"\t\\\"value\\\": \\\"cookie1\\\",\" +\n\t\t\"\t\\\"attributes\\\":{\\\"@class\\\":\\\"java.util.Collections$UnmodifiableMap\\\", \\\"HttpOnly\\\": \\\"\\\"},\" +\n\t\t\"\t\\\"comment\\\": null,\" +\n\t\t\"\t\\\"maxAge\\\": -1,\" +\n\t\t\"\t\\\"path\\\": null,\" +\n\t\t\"\t\\\"secure\\\": false,\" +\n\t\t\"\t\\\"version\\\": 0,\" +\n\t\t\"\t\\\"domain\\\": null\" +\n\t\t\"}\";\n\t// @formatter:on\n\n\t@Test\n\tpublic void serializeCookie() throws JacksonException, JSONException {\n\t\tCookie cookie = new Cookie(\"demo\", \"cookie1\");\n\t\tString actualString = this.mapper.writeValueAsString(cookie);\n\t\tJSONAssert.assertEquals(COOKIE_JSON, actualString, true);\n\t}\n\n\t@Test\n\tpublic void deserializeCookie() {\n\t\tCookie cookie = this.mapper.readValue(COOKIE_JSON, Cookie.class);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getName()).isEqualTo(\"demo\");\n\t\tassertThat(cookie.getDomain()).isNull();\n\t}\n\n\t@Test\n\tpublic void serializeCookieWithHttpOnly() throws JacksonException, JSONException {\n\t\tCookie cookie = new Cookie(\"demo\", \"cookie1\");\n\t\tcookie.setHttpOnly(true);\n\t\tString actualString = this.mapper.writeValueAsString(cookie);\n\t\tJSONAssert.assertEquals(COOKIE_HTTP_ONLY_JSON, actualString, true);\n\t}\n\n\t@Test\n\tpublic void deserializeCookieWithHttpOnly() {\n\t\tCookie cookie = this.mapper.readValue(COOKIE_HTTP_ONLY_JSON, Cookie.class);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getName()).isEqualTo(\"demo\");\n\t\tassertThat(cookie.getDomain()).isNull();\n\t\tassertThat(cookie.isHttpOnly()).isEqualTo(true);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson/DefaultCsrfTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\n\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class DefaultCsrfTokenMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tpublic static final String CSRF_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.csrf.DefaultCsrfToken\\\", \"\n\t\t+ \"\\\"headerName\\\": \\\"csrf-header\\\", \"\n\t\t+ \"\\\"parameterName\\\": \\\"_csrf\\\", \"\n\t\t+ \"\\\"token\\\": \\\"1\\\"\"\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void defaultCsrfTokenSerializedTest() throws JacksonException, JSONException {\n\t\tDefaultCsrfToken token = new DefaultCsrfToken(\"csrf-header\", \"_csrf\", \"1\");\n\t\tString serializedJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(CSRF_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeTest() {\n\t\tDefaultCsrfToken token = this.mapper.readValue(CSRF_JSON, DefaultCsrfToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getHeaderName()).isEqualTo(\"csrf-header\");\n\t\tassertThat(token.getParameterName()).isEqualTo(\"_csrf\");\n\t\tassertThat(token.getToken()).isEqualTo(\"1\");\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeWithoutClassTest() {\n\t\tString tokenJson = \"{\\\"headerName\\\": \\\"csrf-header\\\", \\\"parameterName\\\": \\\"_csrf\\\", \\\"token\\\": \\\"1\\\"}\";\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(tokenJson, DefaultCsrfToken.class));\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeNullValuesTest() {\n\t\tString tokenJson = \"{\\\"@class\\\": \\\"org.springframework.security.web.csrf.DefaultCsrfToken\\\", \\\"headerName\\\": \\\"\\\", \\\"parameterName\\\": null, \\\"token\\\": \\\"1\\\"}\";\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(tokenJson, DefaultCsrfToken.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson/DefaultSavedRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.Locale;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\nimport org.springframework.security.web.savedrequest.SavedCookie;\nimport org.springframework.security.web.util.UrlUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class DefaultSavedRequestMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String COOKIES_JSON = \"[\\\"java.util.ArrayList\\\", [{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.savedrequest.SavedCookie\\\", \"\n\t\t+ \"\\\"name\\\": \\\"SESSION\\\", \"\n\t\t+ \"\\\"value\\\": \\\"123456789\\\", \"\n\t\t+ \"\\\"maxAge\\\": -1, \"\n\t\t+ \"\\\"path\\\": null, \"\n\t\t+ \"\\\"secure\\\":false, \"\n\t\t+ \"\\\"domain\\\": null\"\n\t+ \"}]]\";\n\t// @formatter:on\n\t// @formatter:off\n\tprivate static final String REQUEST_JSON = \"{\" +\n\t\t\t\"\\\"@class\\\": \\\"org.springframework.security.web.savedrequest.DefaultSavedRequest\\\", \"\n\t\t\t+ \"\\\"cookies\\\": \" + COOKIES_JSON + \",\"\n\t\t\t+ \"\\\"locales\\\": [\\\"java.util.ArrayList\\\", [\\\"en\\\"]], \"\n\t\t\t+ \"\\\"headers\\\": {\\\"@class\\\": \\\"java.util.TreeMap\\\", \\\"x-auth-token\\\": [\\\"java.util.ArrayList\\\", [\\\"12\\\"]]}, \"\n\t\t\t+ \"\\\"parameters\\\": {\\\"@class\\\": \\\"java.util.TreeMap\\\"},\"\n\t\t\t+ \"\\\"contextPath\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"method\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"pathInfo\\\": null, \"\n\t\t\t+ \"\\\"queryString\\\": null, \"\n\t\t\t+ \"\\\"requestURI\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"requestURL\\\": \\\"http://localhost\\\", \"\n\t\t\t+ \"\\\"scheme\\\": \\\"http\\\", \"\n\t\t\t+ \"\\\"serverName\\\": \\\"localhost\\\", \"\n\t\t\t+ \"\\\"servletPath\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"serverPort\\\": 80\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\t// @formatter:off\n\tprivate static final String REQUEST_WITH_MATCHING_REQUEST_PARAM_NAME_JSON = \"{\" +\n\t\t\t\"\\\"@class\\\": \\\"org.springframework.security.web.savedrequest.DefaultSavedRequest\\\", \"\n\t\t\t+ \"\\\"cookies\\\": \" + COOKIES_JSON + \",\"\n\t\t\t+ \"\\\"locales\\\": [\\\"java.util.ArrayList\\\", [\\\"en\\\"]], \"\n\t\t\t+ \"\\\"headers\\\": {\\\"@class\\\": \\\"java.util.TreeMap\\\", \\\"x-auth-token\\\": [\\\"java.util.ArrayList\\\", [\\\"12\\\"]]}, \"\n\t\t\t+ \"\\\"parameters\\\": {\\\"@class\\\": \\\"java.util.TreeMap\\\"},\"\n\t\t\t+ \"\\\"contextPath\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"method\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"pathInfo\\\": null, \"\n\t\t\t+ \"\\\"queryString\\\": null, \"\n\t\t\t+ \"\\\"requestURI\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"requestURL\\\": \\\"http://localhost\\\", \"\n\t\t\t+ \"\\\"scheme\\\": \\\"http\\\", \"\n\t\t\t+ \"\\\"serverName\\\": \\\"localhost\\\", \"\n\t\t\t+ \"\\\"servletPath\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"serverPort\\\": 80, \"\n\t\t\t+ \"\\\"matchingRequestParameterName\\\": \\\"success\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void matchRequestBuildWithConstructorAndBuilder() {\n\t\tDefaultSavedRequest request = new DefaultSavedRequest.Builder()\n\t\t\t.setCookies(Collections.singletonList(new SavedCookie(new Cookie(\"SESSION\", \"123456789\"))))\n\t\t\t.setHeaders(Collections.singletonMap(\"x-auth-token\", Collections.singletonList(\"12\")))\n\t\t\t.setScheme(\"http\")\n\t\t\t.setRequestURL(\"http://localhost\")\n\t\t\t.setServerName(\"localhost\")\n\t\t\t.setRequestURI(\"\")\n\t\t\t.setLocales(Collections.singletonList(new Locale(\"en\")))\n\t\t\t.setContextPath(\"\")\n\t\t\t.setMethod(\"\")\n\t\t\t.setServletPath(\"\")\n\t\t\t.build();\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest();\n\t\tmockRequest.setCookies(new Cookie(\"SESSION\", \"123456789\"));\n\t\tmockRequest.addHeader(\"x-auth-token\", \"12\");\n\t\tString currentUrl = UrlUtils.buildFullRequestUrl(mockRequest);\n\t\tassertThat(request.getRedirectUrl().equals(currentUrl)).isTrue();\n\t}\n\n\t@Test\n\tpublic void serializeDefaultRequestBuildWithConstructorTest() throws IOException, JSONException {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"x-auth-token\", \"12\");\n\t\t// Spring 5 MockHttpServletRequest automatically adds a header when the cookies\n\t\t// are set. To get consistency we override the request.\n\t\tHttpServletRequest requestToWrite = new HttpServletRequestWrapper(request) {\n\t\t\t@Override\n\t\t\tpublic Cookie[] getCookies() {\n\t\t\t\treturn new Cookie[] { new Cookie(\"SESSION\", \"123456789\") };\n\t\t\t}\n\t\t};\n\t\tString actualString = this.mapper.writerWithDefaultPrettyPrinter()\n\t\t\t.writeValueAsString(new DefaultSavedRequest(requestToWrite));\n\t\tJSONAssert.assertEquals(REQUEST_JSON, actualString, true);\n\t}\n\n\t@Test\n\tpublic void serializeDefaultRequestBuildWithBuilderTest() throws IOException, JSONException {\n\t\tDefaultSavedRequest request = new DefaultSavedRequest.Builder()\n\t\t\t.setCookies(Collections.singletonList(new SavedCookie(new Cookie(\"SESSION\", \"123456789\"))))\n\t\t\t.setHeaders(Collections.singletonMap(\"x-auth-token\", Collections.singletonList(\"12\")))\n\t\t\t.setScheme(\"http\")\n\t\t\t.setRequestURL(\"http://localhost\")\n\t\t\t.setServerName(\"localhost\")\n\t\t\t.setRequestURI(\"\")\n\t\t\t.setLocales(Collections.singletonList(new Locale(\"en\")))\n\t\t\t.setContextPath(\"\")\n\t\t\t.setMethod(\"\")\n\t\t\t.setServletPath(\"\")\n\t\t\t.build();\n\t\tString actualString = this.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(request);\n\t\tJSONAssert.assertEquals(REQUEST_JSON, actualString, true);\n\t}\n\n\t@Test\n\tpublic void deserializeDefaultSavedRequest() {\n\t\tDefaultSavedRequest request = (DefaultSavedRequest) this.mapper.readValue(REQUEST_JSON, Object.class);\n\t\tassertThat(request).isNotNull();\n\t\tassertThat(request.getCookies()).hasSize(1);\n\t\tassertThat(request.getLocales()).hasSize(1).contains(new Locale(\"en\"));\n\t\tassertThat(request.getHeaderNames()).hasSize(1).contains(\"x-auth-token\");\n\t\tassertThat(request.getHeaderValues(\"x-auth-token\")).hasSize(1).contains(\"12\");\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMatchingRequestParameterNameThenRedirectUrlContainsParam() {\n\t\tDefaultSavedRequest request = (DefaultSavedRequest) this.mapper\n\t\t\t.readValue(REQUEST_WITH_MATCHING_REQUEST_PARAM_NAME_JSON, Object.class);\n\t\tassertThat(request.getRedirectUrl()).isEqualTo(\"http://localhost?success\");\n\t}\n\n\t@Test\n\tpublic void deserializeWhenNullMatchingRequestParameterNameThenRedirectUrlDoesNotContainParam() {\n\t\tDefaultSavedRequest request = (DefaultSavedRequest) this.mapper.readValue(REQUEST_JSON, Object.class);\n\t\tassertThat(request.getRedirectUrl()).isEqualTo(\"http://localhost\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson/PreAuthenticatedAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson2.SimpleGrantedAuthorityMixinTests;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 4.2\n */\npublic class PreAuthenticatedAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String PREAUTH_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken\\\",\"\n\t\t+ \"\\\"principal\\\": \\\"principal\\\", \"\n\t\t+ \"\\\"credentials\\\": \\\"credentials\\\", \"\n\t\t+ \"\\\"authenticated\\\": true, \"\n\t\t+ \"\\\"details\\\": null, \"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON\n\t+ \"}\";\n\t// @formatter:on\n\tPreAuthenticatedAuthenticationToken expected;\n\n\t@BeforeEach\n\tpublic void setupExpected() {\n\t\tthis.expected = new PreAuthenticatedAuthenticationToken(\"principal\", \"credentials\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void serializeWhenPrincipalCredentialsAuthoritiesThenSuccess() throws JacksonException, JSONException {\n\t\tString serializedJson = this.mapper.writeValueAsString(this.expected);\n\t\tJSONAssert.assertEquals(PREAUTH_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest() {\n\t\tPreAuthenticatedAuthenticationToken deserialized = this.mapper.readValue(PREAUTH_JSON,\n\t\t\t\tPreAuthenticatedAuthenticationToken.class);\n\t\tassertThat(deserialized).isNotNull();\n\t\tassertThat(deserialized.isAuthenticated()).isTrue();\n\t\tassertThat(deserialized.getAuthorities()).isEqualTo(this.expected.getAuthorities());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson/SavedCookieMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jakarta.servlet.http.Cookie;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\n\nimport org.springframework.security.web.savedrequest.SavedCookie;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n */\npublic class SavedCookieMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String COOKIE_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.savedrequest.SavedCookie\\\", \"\n\t\t+ \"\\\"name\\\": \\\"SESSION\\\", \"\n\t\t+ \"\\\"value\\\": \\\"123456789\\\", \"\n\t\t+ \"\\\"maxAge\\\": -1, \"\n\t\t+ \"\\\"path\\\": null, \"\n\t\t+ \"\\\"secure\\\":false, \"\n\t\t+ \"\\\"domain\\\": null\"\n\t+ \"}\";\n\t// @formatter:on\n\t// @formatter:off\n\tprivate static final String COOKIES_JSON = \"[\\\"java.util.ArrayList\\\", [\"\n\t\t+ COOKIE_JSON\n\t+ \"]]\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeWithDefaultConfigurationTest() throws JacksonException, JSONException {\n\t\tSavedCookie savedCookie = new SavedCookie(new Cookie(\"SESSION\", \"123456789\"));\n\t\tString actualJson = this.mapper.writeValueAsString(savedCookie);\n\t\tJSONAssert.assertEquals(COOKIE_JSON, actualJson, true);\n\t}\n\n\t@Test\n\t@Disabled(\"No supported by Jackson 3 as ObjectMapper/JsonMapper is immutable\")\n\tpublic void serializeWithOverrideConfigurationTest() throws JacksonException, JSONException {\n\t\tSavedCookie savedCookie = new SavedCookie(new Cookie(\"SESSION\", \"123456789\"));\n\t\t// this.mapper.setVisibility(PropertyAccessor.FIELD,\n\t\t// JsonAutoDetect.Visibility.PUBLIC_ONLY)\n\t\t// .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.ANY);\n\t\tString actualJson = this.mapper.writeValueAsString(savedCookie);\n\t\tJSONAssert.assertEquals(COOKIE_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeSavedCookieWithList() throws JacksonException, JSONException {\n\t\tList<SavedCookie> savedCookies = new ArrayList<>();\n\t\tsavedCookies.add(new SavedCookie(new Cookie(\"SESSION\", \"123456789\")));\n\t\tString actualJson = this.mapper.writeValueAsString(savedCookies);\n\t\tJSONAssert.assertEquals(COOKIES_JSON, actualJson, true);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void deserializeSavedCookieWithList() {\n\t\tList<SavedCookie> savedCookies = (List<SavedCookie>) this.mapper.readValue(COOKIES_JSON, Object.class);\n\t\tassertThat(savedCookies).isNotNull().hasSize(1);\n\t\tassertThat(savedCookies.get(0).getName()).isEqualTo(\"SESSION\");\n\t\tassertThat(savedCookies.get(0).getValue()).isEqualTo(\"123456789\");\n\t}\n\n\t@Test\n\tpublic void deserializeSavedCookieJsonTest() {\n\t\tSavedCookie savedCookie = (SavedCookie) this.mapper.readValue(COOKIE_JSON, Object.class);\n\t\tassertThat(savedCookie).isNotNull();\n\t\tassertThat(savedCookie.getName()).isEqualTo(\"SESSION\");\n\t\tassertThat(savedCookie.getValue()).isEqualTo(\"123456789\");\n\t\tassertThat(savedCookie.isSecure()).isEqualTo(false);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson/SwitchUserGrantedAuthorityMixInTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.jackson.SimpleGrantedAuthorityMixinTests;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Markus Heiden\n * @since 6.3\n */\npublic class SwitchUserGrantedAuthorityMixInTests {\n\n\t// language=JSON\n\tprivate static final String SWITCH_JSON = \"\"\"\n\t\t\t{\n\t\t\t\t\"@class\": \"org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority\",\n\t\t\t\t\"role\": \"switched\",\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"@class\": \"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\",\n\t\t\t\t\t\"principal\": \"principal\",\n\t\t\t\t\t\"credentials\": \"credentials\",\n\t\t\t\t\t\"authenticated\": true,\n\t\t\t\t\t\"details\": null,\n\t\t\t\t\t\"authorities\": %s\n\t\t\t\t}\n\t\t\t}\n\t\t\t\"\"\".formatted(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON);\n\n\tprivate Authentication source;\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.source = new UsernamePasswordAuthenticationToken(\"principal\", \"credentials\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tClassLoader classLoader = SwitchUserGrantedAuthorityMixInTests.class.getClassLoader();\n\t\tthis.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(classLoader)).build();\n\t}\n\n\t@Test\n\tpublic void serializeWhenPrincipalCredentialsAuthoritiesThenSuccess() throws Exception {\n\t\tSwitchUserGrantedAuthority expected = new SwitchUserGrantedAuthority(\"switched\", this.source);\n\t\tString serializedJson = this.mapper.writeValueAsString(expected);\n\t\tJSONAssert.assertEquals(SWITCH_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeWhenSourceIsUsernamePasswordAuthenticationTokenThenSuccess() {\n\t\tSwitchUserGrantedAuthority deserialized = this.mapper.readValue(SWITCH_JSON, SwitchUserGrantedAuthority.class);\n\t\tassertThat(deserialized).isNotNull();\n\t\tassertThat(deserialized.getAuthority()).isEqualTo(\"switched\");\n\t\tassertThat(deserialized.getSource()).isEqualTo(this.source);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson/WebAuthenticationDetailsMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class WebAuthenticationDetailsMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String AUTHENTICATION_DETAILS_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.authentication.WebAuthenticationDetails\\\",\"\n\t\t+ \"\\\"sessionId\\\": \\\"1\\\", \"\n\t\t+ \"\\\"remoteAddress\\\": \"\n\t\t+ \"\\\"/localhost\\\"\"\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void buildWebAuthenticationDetailsUsingDifferentConstructors() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRemoteAddr(\"localhost\");\n\t\trequest.setSession(new MockHttpSession(null, \"1\"));\n\t\tWebAuthenticationDetails details = new WebAuthenticationDetails(request);\n\t\tWebAuthenticationDetails authenticationDetails = this.mapper.readValue(AUTHENTICATION_DETAILS_JSON,\n\t\t\t\tWebAuthenticationDetails.class);\n\t\tassertThat(details.equals(authenticationDetails));\n\t}\n\n\t@Test\n\tpublic void webAuthenticationDetailsSerializeTest() throws JacksonException, JSONException {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRemoteAddr(\"/localhost\");\n\t\trequest.setSession(new MockHttpSession(null, \"1\"));\n\t\tWebAuthenticationDetails details = new WebAuthenticationDetails(request);\n\t\tString actualJson = this.mapper.writeValueAsString(details);\n\t\tJSONAssert.assertEquals(AUTHENTICATION_DETAILS_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void webAuthenticationDetailsJackson2SerializeTest() throws JacksonException, JSONException {\n\t\tWebAuthenticationDetails details = new WebAuthenticationDetails(\"/localhost\", \"1\");\n\t\tString actualJson = this.mapper.writeValueAsString(details);\n\t\tJSONAssert.assertEquals(AUTHENTICATION_DETAILS_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void webAuthenticationDetailsDeserializeTest() {\n\t\tWebAuthenticationDetails details = this.mapper.readValue(AUTHENTICATION_DETAILS_JSON,\n\t\t\t\tWebAuthenticationDetails.class);\n\t\tassertThat(details).isNotNull();\n\t\tassertThat(details.getRemoteAddress()).isEqualTo(\"/localhost\");\n\t\tassertThat(details.getSessionId()).isEqualTo(\"1\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson2/AbstractMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\n\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\n\n/**\n * @author Jitenra Singh\n * @since 4.2\n */\n@SuppressWarnings(\"removal\")\npublic abstract class AbstractMixinTests {\n\n\tprotected ObjectMapper mapper;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tClassLoader loader = getClass().getClassLoader();\n\t\tthis.mapper.registerModules(SecurityJackson2Modules.getModules(loader));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson2/CookieMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport jakarta.servlet.http.Cookie;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class CookieMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String COOKIE_JSON = \"{\" +\n\t\t\"\t\\\"@class\\\": \\\"jakarta.servlet.http.Cookie\\\",\" +\n\t\t\"\t\\\"name\\\": \\\"demo\\\",\" +\n\t\t\"\t\\\"value\\\": \\\"cookie1\\\",\" +\n\t\t\"\t\\\"attributes\\\":{\\\"@class\\\":\\\"java.util.Collections$EmptyMap\\\"},\" +\n\t\t\"\t\\\"comment\\\": null,\" +\n\t\t\"\t\\\"maxAge\\\": -1,\" +\n\t\t\"\t\\\"path\\\": null,\" +\n\t\t\"\t\\\"secure\\\": false,\" +\n\t\t\"\t\\\"version\\\": 0,\" +\n\t\t\"\t\\\"domain\\\": null\" +\n\t\t\"}\";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String COOKIE_HTTP_ONLY_JSON = \"{\" +\n\t\t\"\t\\\"@class\\\": \\\"jakarta.servlet.http.Cookie\\\",\" +\n\t\t\"\t\\\"name\\\": \\\"demo\\\",\" +\n\t\t\"\t\\\"value\\\": \\\"cookie1\\\",\" +\n\t\t\"\t\\\"attributes\\\":{\\\"@class\\\":\\\"java.util.Collections$UnmodifiableMap\\\", \\\"HttpOnly\\\": \\\"\\\"},\" +\n\t\t\"\t\\\"comment\\\": null,\" +\n\t\t\"\t\\\"maxAge\\\": -1,\" +\n\t\t\"\t\\\"path\\\": null,\" +\n\t\t\"\t\\\"secure\\\": false,\" +\n\t\t\"\t\\\"version\\\": 0,\" +\n\t\t\"\t\\\"domain\\\": null\" +\n\t\t\"}\";\n\t// @formatter:on\n\n\t@Test\n\tpublic void serializeCookie() throws JsonProcessingException, JSONException {\n\t\tCookie cookie = new Cookie(\"demo\", \"cookie1\");\n\t\tString actualString = this.mapper.writeValueAsString(cookie);\n\t\tJSONAssert.assertEquals(COOKIE_JSON, actualString, true);\n\t}\n\n\t@Test\n\tpublic void deserializeCookie() throws IOException {\n\t\tCookie cookie = this.mapper.readValue(COOKIE_JSON, Cookie.class);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getName()).isEqualTo(\"demo\");\n\t\tassertThat(cookie.getDomain()).isEqualTo(\"\");\n\t}\n\n\t@Test\n\tpublic void serializeCookieWithHttpOnly() throws JsonProcessingException, JSONException {\n\t\tCookie cookie = new Cookie(\"demo\", \"cookie1\");\n\t\tcookie.setHttpOnly(true);\n\t\tString actualString = this.mapper.writeValueAsString(cookie);\n\t\tJSONAssert.assertEquals(COOKIE_HTTP_ONLY_JSON, actualString, true);\n\t}\n\n\t@Test\n\tpublic void deserializeCookieWithHttpOnly() throws IOException {\n\t\tCookie cookie = this.mapper.readValue(COOKIE_HTTP_ONLY_JSON, Cookie.class);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getName()).isEqualTo(\"demo\");\n\t\tassertThat(cookie.getDomain()).isEqualTo(\"\");\n\t\tassertThat(cookie.isHttpOnly()).isEqualTo(true);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson2/DefaultCsrfTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class DefaultCsrfTokenMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tpublic static final String CSRF_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.csrf.DefaultCsrfToken\\\", \"\n\t\t+ \"\\\"headerName\\\": \\\"csrf-header\\\", \"\n\t\t+ \"\\\"parameterName\\\": \\\"_csrf\\\", \"\n\t\t+ \"\\\"token\\\": \\\"1\\\"\"\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void defaultCsrfTokenSerializedTest() throws JsonProcessingException, JSONException {\n\t\tDefaultCsrfToken token = new DefaultCsrfToken(\"csrf-header\", \"_csrf\", \"1\");\n\t\tString serializedJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(CSRF_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeTest() throws IOException {\n\t\tDefaultCsrfToken token = this.mapper.readValue(CSRF_JSON, DefaultCsrfToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getHeaderName()).isEqualTo(\"csrf-header\");\n\t\tassertThat(token.getParameterName()).isEqualTo(\"_csrf\");\n\t\tassertThat(token.getToken()).isEqualTo(\"1\");\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeWithoutClassTest() throws IOException {\n\t\tString tokenJson = \"{\\\"headerName\\\": \\\"csrf-header\\\", \\\"parameterName\\\": \\\"_csrf\\\", \\\"token\\\": \\\"1\\\"}\";\n\t\tassertThatExceptionOfType(JsonMappingException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(tokenJson, DefaultCsrfToken.class));\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeNullValuesTest() throws IOException {\n\t\tString tokenJson = \"{\\\"@class\\\": \\\"org.springframework.security.web.csrf.DefaultCsrfToken\\\", \\\"headerName\\\": \\\"\\\", \\\"parameterName\\\": null, \\\"token\\\": \\\"1\\\"}\";\n\t\tassertThatExceptionOfType(JsonMappingException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(tokenJson, DefaultCsrfToken.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson2/DefaultSavedRequestMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.Locale;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletRequestWrapper;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.savedrequest.DefaultSavedRequest;\nimport org.springframework.security.web.savedrequest.SavedCookie;\nimport org.springframework.security.web.util.UrlUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class DefaultSavedRequestMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String COOKIES_JSON = \"[\\\"java.util.ArrayList\\\", [{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.savedrequest.SavedCookie\\\", \"\n\t\t+ \"\\\"name\\\": \\\"SESSION\\\", \"\n\t\t+ \"\\\"value\\\": \\\"123456789\\\", \"\n\t\t+ \"\\\"maxAge\\\": -1, \"\n\t\t+ \"\\\"path\\\": null, \"\n\t\t+ \"\\\"secure\\\":false, \"\n\t\t+ \"\\\"domain\\\": null\"\n\t+ \"}]]\";\n\t// @formatter:on\n\t// @formatter:off\n\tprivate static final String REQUEST_JSON = \"{\" +\n\t\t\t\"\\\"@class\\\": \\\"org.springframework.security.web.savedrequest.DefaultSavedRequest\\\", \"\n\t\t\t+ \"\\\"cookies\\\": \" + COOKIES_JSON + \",\"\n\t\t\t+ \"\\\"locales\\\": [\\\"java.util.ArrayList\\\", [\\\"en\\\"]], \"\n\t\t\t+ \"\\\"headers\\\": {\\\"@class\\\": \\\"java.util.TreeMap\\\", \\\"x-auth-token\\\": [\\\"java.util.ArrayList\\\", [\\\"12\\\"]]}, \"\n\t\t\t+ \"\\\"parameters\\\": {\\\"@class\\\": \\\"java.util.TreeMap\\\"},\"\n\t\t\t+ \"\\\"contextPath\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"method\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"pathInfo\\\": null, \"\n\t\t\t+ \"\\\"queryString\\\": null, \"\n\t\t\t+ \"\\\"requestURI\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"requestURL\\\": \\\"http://localhost\\\", \"\n\t\t\t+ \"\\\"scheme\\\": \\\"http\\\", \"\n\t\t\t+ \"\\\"serverName\\\": \\\"localhost\\\", \"\n\t\t\t+ \"\\\"servletPath\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"serverPort\\\": 80\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\t// @formatter:off\n\tprivate static final String REQUEST_WITH_MATCHING_REQUEST_PARAM_NAME_JSON = \"{\" +\n\t\t\t\"\\\"@class\\\": \\\"org.springframework.security.web.savedrequest.DefaultSavedRequest\\\", \"\n\t\t\t+ \"\\\"cookies\\\": \" + COOKIES_JSON + \",\"\n\t\t\t+ \"\\\"locales\\\": [\\\"java.util.ArrayList\\\", [\\\"en\\\"]], \"\n\t\t\t+ \"\\\"headers\\\": {\\\"@class\\\": \\\"java.util.TreeMap\\\", \\\"x-auth-token\\\": [\\\"java.util.ArrayList\\\", [\\\"12\\\"]]}, \"\n\t\t\t+ \"\\\"parameters\\\": {\\\"@class\\\": \\\"java.util.TreeMap\\\"},\"\n\t\t\t+ \"\\\"contextPath\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"method\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"pathInfo\\\": null, \"\n\t\t\t+ \"\\\"queryString\\\": null, \"\n\t\t\t+ \"\\\"requestURI\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"requestURL\\\": \\\"http://localhost\\\", \"\n\t\t\t+ \"\\\"scheme\\\": \\\"http\\\", \"\n\t\t\t+ \"\\\"serverName\\\": \\\"localhost\\\", \"\n\t\t\t+ \"\\\"servletPath\\\": \\\"\\\", \"\n\t\t\t+ \"\\\"serverPort\\\": 80, \"\n\t\t\t+ \"\\\"matchingRequestParameterName\\\": \\\"success\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void matchRequestBuildWithConstructorAndBuilder() {\n\t\tDefaultSavedRequest request = new DefaultSavedRequest.Builder()\n\t\t\t.setCookies(Collections.singletonList(new SavedCookie(new Cookie(\"SESSION\", \"123456789\"))))\n\t\t\t.setHeaders(Collections.singletonMap(\"x-auth-token\", Collections.singletonList(\"12\")))\n\t\t\t.setScheme(\"http\")\n\t\t\t.setRequestURL(\"http://localhost\")\n\t\t\t.setServerName(\"localhost\")\n\t\t\t.setRequestURI(\"\")\n\t\t\t.setLocales(Collections.singletonList(new Locale(\"en\")))\n\t\t\t.setContextPath(\"\")\n\t\t\t.setMethod(\"\")\n\t\t\t.setServletPath(\"\")\n\t\t\t.build();\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest();\n\t\tmockRequest.setCookies(new Cookie(\"SESSION\", \"123456789\"));\n\t\tmockRequest.addHeader(\"x-auth-token\", \"12\");\n\t\tString currentUrl = UrlUtils.buildFullRequestUrl(mockRequest);\n\t\tassertThat(request.getRedirectUrl().equals(currentUrl)).isTrue();\n\t}\n\n\t@Test\n\tpublic void serializeDefaultRequestBuildWithConstructorTest() throws IOException, JSONException {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"x-auth-token\", \"12\");\n\t\t// Spring 5 MockHttpServletRequest automatically adds a header when the cookies\n\t\t// are set. To get consistency we override the request.\n\t\tHttpServletRequest requestToWrite = new HttpServletRequestWrapper(request) {\n\t\t\t@Override\n\t\t\tpublic Cookie[] getCookies() {\n\t\t\t\treturn new Cookie[] { new Cookie(\"SESSION\", \"123456789\") };\n\t\t\t}\n\t\t};\n\t\tString actualString = this.mapper.writerWithDefaultPrettyPrinter()\n\t\t\t.writeValueAsString(new DefaultSavedRequest(requestToWrite));\n\t\tJSONAssert.assertEquals(REQUEST_JSON, actualString, true);\n\t}\n\n\t@Test\n\tpublic void serializeDefaultRequestBuildWithBuilderTest() throws IOException, JSONException {\n\t\tDefaultSavedRequest request = new DefaultSavedRequest.Builder()\n\t\t\t.setCookies(Collections.singletonList(new SavedCookie(new Cookie(\"SESSION\", \"123456789\"))))\n\t\t\t.setHeaders(Collections.singletonMap(\"x-auth-token\", Collections.singletonList(\"12\")))\n\t\t\t.setScheme(\"http\")\n\t\t\t.setRequestURL(\"http://localhost\")\n\t\t\t.setServerName(\"localhost\")\n\t\t\t.setRequestURI(\"\")\n\t\t\t.setLocales(Collections.singletonList(new Locale(\"en\")))\n\t\t\t.setContextPath(\"\")\n\t\t\t.setMethod(\"\")\n\t\t\t.setServletPath(\"\")\n\t\t\t.build();\n\t\tString actualString = this.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(request);\n\t\tJSONAssert.assertEquals(REQUEST_JSON, actualString, true);\n\t}\n\n\t@Test\n\tpublic void deserializeDefaultSavedRequest() throws IOException {\n\t\tDefaultSavedRequest request = (DefaultSavedRequest) this.mapper.readValue(REQUEST_JSON, Object.class);\n\t\tassertThat(request).isNotNull();\n\t\tassertThat(request.getCookies()).hasSize(1);\n\t\tassertThat(request.getLocales()).hasSize(1).contains(new Locale(\"en\"));\n\t\tassertThat(request.getHeaderNames()).hasSize(1).contains(\"x-auth-token\");\n\t\tassertThat(request.getHeaderValues(\"x-auth-token\")).hasSize(1).contains(\"12\");\n\t}\n\n\t@Test\n\tpublic void deserializeWhenMatchingRequestParameterNameThenRedirectUrlContainsParam() throws IOException {\n\t\tDefaultSavedRequest request = (DefaultSavedRequest) this.mapper\n\t\t\t.readValue(REQUEST_WITH_MATCHING_REQUEST_PARAM_NAME_JSON, Object.class);\n\t\tassertThat(request.getRedirectUrl()).isEqualTo(\"http://localhost?success\");\n\t}\n\n\t@Test\n\tpublic void deserializeWhenNullMatchingRequestParameterNameThenRedirectUrlDoesNotContainParam() throws IOException {\n\t\tDefaultSavedRequest request = (DefaultSavedRequest) this.mapper.readValue(REQUEST_JSON, Object.class);\n\t\tassertThat(request.getRedirectUrl()).isEqualTo(\"http://localhost\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson2/PreAuthenticatedAuthenticationTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson2.SimpleGrantedAuthorityMixinTests;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 4.2\n */\npublic class PreAuthenticatedAuthenticationTokenMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String PREAUTH_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken\\\",\"\n\t\t+ \"\\\"principal\\\": \\\"principal\\\", \"\n\t\t+ \"\\\"credentials\\\": \\\"credentials\\\", \"\n\t\t+ \"\\\"authenticated\\\": true, \"\n\t\t+ \"\\\"details\\\": null, \"\n\t\t+ \"\\\"authorities\\\": \" + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON\n\t+ \"}\";\n\t// @formatter:on\n\tPreAuthenticatedAuthenticationToken expected;\n\n\t@BeforeEach\n\tpublic void setupExpected() {\n\t\tthis.expected = new PreAuthenticatedAuthenticationToken(\"principal\", \"credentials\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t}\n\n\t@Test\n\tpublic void serializeWhenPrincipalCredentialsAuthoritiesThenSuccess()\n\t\t\tthrows JsonProcessingException, JSONException {\n\t\tString serializedJson = this.mapper.writeValueAsString(this.expected);\n\t\tJSONAssert.assertEquals(PREAUTH_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void deserializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest() throws Exception {\n\t\tPreAuthenticatedAuthenticationToken deserialized = this.mapper.readValue(PREAUTH_JSON,\n\t\t\t\tPreAuthenticatedAuthenticationToken.class);\n\t\tassertThat(deserialized).isNotNull();\n\t\tassertThat(deserialized.isAuthenticated()).isTrue();\n\t\tassertThat(deserialized.getAuthorities()).isEqualTo(this.expected.getAuthorities());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson2/SavedCookieMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport jakarta.servlet.http.Cookie;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.web.savedrequest.SavedCookie;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n */\npublic class SavedCookieMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String COOKIE_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.savedrequest.SavedCookie\\\", \"\n\t\t+ \"\\\"name\\\": \\\"SESSION\\\", \"\n\t\t+ \"\\\"value\\\": \\\"123456789\\\", \"\n\t\t+ \"\\\"maxAge\\\": -1, \"\n\t\t+ \"\\\"path\\\": null, \"\n\t\t+ \"\\\"secure\\\":false, \"\n\t\t+ \"\\\"domain\\\": null\"\n\t+ \"}\";\n\t// @formatter:on\n\t// @formatter:off\n\tprivate static final String COOKIES_JSON = \"[\\\"java.util.ArrayList\\\", [\"\n\t\t+ COOKIE_JSON\n\t+ \"]]\";\n\t// @formatter:on\n\t@Test\n\tpublic void serializeWithDefaultConfigurationTest() throws JsonProcessingException, JSONException {\n\t\tSavedCookie savedCookie = new SavedCookie(new Cookie(\"SESSION\", \"123456789\"));\n\t\tString actualJson = this.mapper.writeValueAsString(savedCookie);\n\t\tJSONAssert.assertEquals(COOKIE_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeWithOverrideConfigurationTest() throws JsonProcessingException, JSONException {\n\t\tSavedCookie savedCookie = new SavedCookie(new Cookie(\"SESSION\", \"123456789\"));\n\t\tthis.mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.PUBLIC_ONLY)\n\t\t\t.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.ANY);\n\t\tString actualJson = this.mapper.writeValueAsString(savedCookie);\n\t\tJSONAssert.assertEquals(COOKIE_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void serializeSavedCookieWithList() throws JsonProcessingException, JSONException {\n\t\tList<SavedCookie> savedCookies = new ArrayList<>();\n\t\tsavedCookies.add(new SavedCookie(new Cookie(\"SESSION\", \"123456789\")));\n\t\tString actualJson = this.mapper.writeValueAsString(savedCookies);\n\t\tJSONAssert.assertEquals(COOKIES_JSON, actualJson, true);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void deserializeSavedCookieWithList() throws IOException {\n\t\tList<SavedCookie> savedCookies = (List<SavedCookie>) this.mapper.readValue(COOKIES_JSON, Object.class);\n\t\tassertThat(savedCookies).isNotNull().hasSize(1);\n\t\tassertThat(savedCookies.get(0).getName()).isEqualTo(\"SESSION\");\n\t\tassertThat(savedCookies.get(0).getValue()).isEqualTo(\"123456789\");\n\t}\n\n\t@Test\n\tpublic void deserializeSavedCookieJsonTest() throws IOException {\n\t\tSavedCookie savedCookie = (SavedCookie) this.mapper.readValue(COOKIE_JSON, Object.class);\n\t\tassertThat(savedCookie).isNotNull();\n\t\tassertThat(savedCookie.getName()).isEqualTo(\"SESSION\");\n\t\tassertThat(savedCookie.getValue()).isEqualTo(\"123456789\");\n\t\tassertThat(savedCookie.isSecure()).isEqualTo(false);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson2/SwitchUserGrantedAuthorityMixInTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport java.util.stream.Stream;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.jackson2.CoreJackson2Module;\nimport org.springframework.security.jackson2.SecurityJackson2Modules;\nimport org.springframework.security.jackson2.SimpleGrantedAuthorityMixinTests;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Markus Heiden\n * @since 6.3\n */\n@SuppressWarnings(\"removal\")\npublic class SwitchUserGrantedAuthorityMixInTests {\n\n\t// language=JSON\n\tprivate static final String SWITCH_JSON = \"\"\"\n\t\t\t{\n\t\t\t\t\"@class\": \"org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority\",\n\t\t\t\t\"role\": \"switched\",\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"@class\": \"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\",\n\t\t\t\t\t\"principal\": \"principal\",\n\t\t\t\t\t\"credentials\": \"credentials\",\n\t\t\t\t\t\"authenticated\": true,\n\t\t\t\t\t\"details\": null,\n\t\t\t\t\t\"authorities\": %s\n\t\t\t\t}\n\t\t\t}\n\t\t\t\"\"\".formatted(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON);\n\n\tprivate Authentication source;\n\n\tstatic Stream<Arguments> mappers() {\n\t\tObjectMapper securityJackson2ModulesMapper = new ObjectMapper();\n\t\tClassLoader classLoader = SwitchUserGrantedAuthorityMixInTests.class.getClassLoader();\n\t\tsecurityJackson2ModulesMapper.registerModules(SecurityJackson2Modules.getModules(classLoader));\n\n\t\tObjectMapper webJackson2ModuleMapper = new ObjectMapper();\n\t\twebJackson2ModuleMapper.registerModule(new CoreJackson2Module());\n\t\twebJackson2ModuleMapper.registerModule(new WebJackson2Module());\n\n\t\tObjectMapper webServletJackson2ModuleMapper = new ObjectMapper();\n\t\twebServletJackson2ModuleMapper.registerModule(new CoreJackson2Module());\n\t\twebServletJackson2ModuleMapper.registerModule(new WebServletJackson2Module());\n\n\t\treturn Stream.of(Arguments.of(securityJackson2ModulesMapper), Arguments.of(webJackson2ModuleMapper),\n\t\t\t\tArguments.of(webServletJackson2ModuleMapper));\n\t}\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.source = new UsernamePasswordAuthenticationToken(\"principal\", \"credentials\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"mappers\")\n\tpublic void serializeWhenPrincipalCredentialsAuthoritiesThenSuccess(ObjectMapper mapper) throws Exception {\n\t\tSwitchUserGrantedAuthority expected = new SwitchUserGrantedAuthority(\"switched\", this.source);\n\t\tString serializedJson = mapper.writeValueAsString(expected);\n\t\tJSONAssert.assertEquals(SWITCH_JSON, serializedJson, true);\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"mappers\")\n\tpublic void deserializeWhenSourceIsUsernamePasswordAuthenticationTokenThenSuccess(ObjectMapper mapper)\n\t\t\tthrows Exception {\n\t\tSwitchUserGrantedAuthority deserialized = mapper.readValue(SWITCH_JSON, SwitchUserGrantedAuthority.class);\n\t\tassertThat(deserialized).isNotNull();\n\t\tassertThat(deserialized.getAuthority()).isEqualTo(\"switched\");\n\t\tassertThat(deserialized.getSource()).isEqualTo(this.source);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/jackson2/WebAuthenticationDetailsMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Jitendra Singh\n * @since 4.2\n */\npublic class WebAuthenticationDetailsMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String AUTHENTICATION_DETAILS_JSON = \"{\"\n\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.authentication.WebAuthenticationDetails\\\",\"\n\t\t+ \"\\\"sessionId\\\": \\\"1\\\", \"\n\t\t+ \"\\\"remoteAddress\\\": \"\n\t\t+ \"\\\"/localhost\\\"\"\n\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void buildWebAuthenticationDetailsUsingDifferentConstructors() throws IOException {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRemoteAddr(\"localhost\");\n\t\trequest.setSession(new MockHttpSession(null, \"1\"));\n\t\tWebAuthenticationDetails details = new WebAuthenticationDetails(request);\n\t\tWebAuthenticationDetails authenticationDetails = this.mapper.readValue(AUTHENTICATION_DETAILS_JSON,\n\t\t\t\tWebAuthenticationDetails.class);\n\t\tassertThat(details.equals(authenticationDetails));\n\t}\n\n\t@Test\n\tpublic void webAuthenticationDetailsSerializeTest() throws JsonProcessingException, JSONException {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRemoteAddr(\"/localhost\");\n\t\trequest.setSession(new MockHttpSession(null, \"1\"));\n\t\tWebAuthenticationDetails details = new WebAuthenticationDetails(request);\n\t\tString actualJson = this.mapper.writeValueAsString(details);\n\t\tJSONAssert.assertEquals(AUTHENTICATION_DETAILS_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void webAuthenticationDetailsJackson2SerializeTest() throws JsonProcessingException, JSONException {\n\t\tWebAuthenticationDetails details = new WebAuthenticationDetails(\"/localhost\", \"1\");\n\t\tString actualJson = this.mapper.writeValueAsString(details);\n\t\tJSONAssert.assertEquals(AUTHENTICATION_DETAILS_JSON, actualJson, true);\n\t}\n\n\t@Test\n\tpublic void webAuthenticationDetailsDeserializeTest() throws IOException {\n\t\tWebAuthenticationDetails details = this.mapper.readValue(AUTHENTICATION_DETAILS_JSON,\n\t\t\t\tWebAuthenticationDetails.class);\n\t\tassertThat(details).isNotNull();\n\t\tassertThat(details.getRemoteAddress()).isEqualTo(\"/localhost\");\n\t\tassertThat(details.getSessionId()).isEqualTo(\"1\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/method/ResolvableMethod.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.method;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.aop.framework.ProxyFactory;\nimport org.springframework.aop.target.EmptyTargetSource;\nimport org.springframework.cglib.core.SpringNamingPolicy;\nimport org.springframework.cglib.proxy.Callback;\nimport org.springframework.cglib.proxy.Enhancer;\nimport org.springframework.cglib.proxy.Factory;\nimport org.springframework.cglib.proxy.MethodProxy;\nimport org.springframework.core.MethodIntrospector;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ResolvableType;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.core.annotation.AnnotationUtils;\nimport org.springframework.core.annotation.SynthesizingMethodParameter;\nimport org.springframework.objenesis.ObjenesisException;\nimport org.springframework.objenesis.SpringObjenesis;\nimport org.springframework.util.Assert;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.web.bind.annotation.ValueConstants;\n\n/**\n * Convenience class to resolve method parameters from hints.\n *\n * <h1>Background</h1>\n *\n * <p>\n * When testing annotated methods we create test classes such as \"TestController\" with a\n * diverse range of method signatures representing supported annotations and argument\n * types. It becomes challenging to use naming strategies to keep track of methods and\n * arguments especially in combination with variables for reflection metadata.\n *\n * <p>\n * The idea with {@link ResolvableMethod} is NOT to rely on naming techniques but to use\n * hints to zero in on method parameters. Such hints can be strongly typed and explicit\n * about what is being tested.\n *\n * <h2>1. Declared Return Type</h2>\n *\n * When testing return types it's likely to have many methods with a unique return type,\n * possibly with or without an annotation.\n *\n * <pre>\n *\n * import static org.springframework.web.method.ResolvableMethod.on;\n * import static org.springframework.web.method.MvcAnnotationPredicates.requestMapping;\n *\n * // Return type\n * on(TestController.class).resolveReturnType(Foo.class);\n * on(TestController.class).resolveReturnType(List.class, Foo.class);\n * on(TestController.class).resolveReturnType(Mono.class, responseEntity(Foo.class));\n *\n * // Annotation + return type\n * on(TestController.class).annotPresent(RequestMapping.class).resolveReturnType(Bar.class);\n *\n * // Annotation not present\n * on(TestController.class).annotNotPresent(RequestMapping.class).resolveReturnType();\n *\n * // Annotation with attributes\n * on(TestController.class).annot(requestMapping(\"/foo\").params(\"p\")).resolveReturnType();\n * </pre>\n *\n * <h2>2. Method Arguments</h2>\n *\n * When testing method arguments it's more likely to have one or a small number of methods\n * with a wide array of argument types and parameter annotations.\n *\n * <pre>\n *\n * import static org.springframework.web.method.MvcAnnotationPredicates.requestParam;\n *\n * ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named(\"handle\").build();\n *\n * testMethod.arg(Foo.class);\n * testMethod.annotPresent(RequestParam.class).arg(Integer.class);\n * testMethod.annotNotPresent(RequestParam.class)).arg(Integer.class);\n * testMethod.annot(requestParam().name(\"c\").notRequired()).arg(Integer.class);\n * </pre>\n *\n * <h3>3. Mock Handler Method Invocation</h3>\n *\n * Locate a method by invoking it through a proxy of the target handler:\n *\n * <pre>\n *\n * ResolvableMethod.on(TestController.class).mockCall((o) -> o.handle(null)).method();\n * </pre>\n *\n * @author Rossen Stoyanchev\n * @since 5.0\n */\npublic final class ResolvableMethod {\n\n\tprivate static final Log logger = LogFactory.getLog(ResolvableMethod.class);\n\n\tprivate static final SpringObjenesis objenesis = new SpringObjenesis();\n\n\tprivate final Method method;\n\n\tprivate ResolvableMethod(Method method) {\n\t\tAssert.notNull(method, \"method is required\");\n\t\tthis.method = method;\n\t}\n\n\t/**\n\t * Return the resolved method.\n\t */\n\tpublic Method method() {\n\t\treturn this.method;\n\t}\n\n\t/**\n\t * Return the declared return type of the resolved method.\n\t */\n\tpublic MethodParameter returnType() {\n\t\treturn new SynthesizingMethodParameter(this.method, -1);\n\t}\n\n\t/**\n\t * Find a unique argument matching the given type.\n\t * @param type the expected type\n\t * @param generics optional array of generic types\n\t */\n\tpublic MethodParameter arg(Class<?> type, Class<?>... generics) {\n\t\treturn new ArgResolver().arg(type, generics);\n\t}\n\n\t/**\n\t * Find a unique argument matching the given type.\n\t * @param type the expected type\n\t * @param generic at least one generic type\n\t * @param generics optional array of generic types\n\t */\n\tpublic MethodParameter arg(Class<?> type, ResolvableType generic, ResolvableType... generics) {\n\t\treturn new ArgResolver().arg(type, generic, generics);\n\t}\n\n\t/**\n\t * Find a unique argument matching the given type.\n\t * @param type the expected type\n\t */\n\tpublic MethodParameter arg(ResolvableType type) {\n\t\treturn new ArgResolver().arg(type);\n\t}\n\n\t/**\n\t * Filter on method arguments with annotation. See {@link MvcAnnotationPredicates}.\n\t */\n\t@SafeVarargs\n\tpublic final ArgResolver annot(Predicate<MethodParameter>... filter) {\n\t\treturn new ArgResolver(filter);\n\t}\n\n\t@SafeVarargs\n\tpublic final ArgResolver annotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\treturn new ArgResolver().annotPresent(annotationTypes);\n\t}\n\n\t/**\n\t * Filter on method arguments that don't have the given annotation type(s).\n\t * @param annotationTypes the annotation types\n\t */\n\t@SafeVarargs\n\tpublic final ArgResolver annotNotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\treturn new ArgResolver().annotNotPresent(annotationTypes);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ResolvableMethod=\" + formatMethod();\n\t}\n\n\tprivate String formatMethod() {\n\t\treturn this.method().getName() + Arrays.stream(this.method.getParameters())\n\t\t\t.map(this::formatParameter)\n\t\t\t.collect(Collectors.joining(\",\\n\\t\", \"(\\n\\t\", \"\\n)\"));\n\t}\n\n\tprivate String formatParameter(Parameter param) {\n\t\tAnnotation[] annot = param.getAnnotations();\n\t\treturn (annot.length > 0)\n\t\t\t\t? Arrays.stream(annot).map(this::formatAnnotation).collect(Collectors.joining(\",\", \"[\", \"]\")) + \" \"\n\t\t\t\t\t\t+ param\n\t\t\t\t: param.toString();\n\t}\n\n\tprivate String formatAnnotation(Annotation annotation) {\n\t\tMap<String, Object> map = AnnotationUtils.getAnnotationAttributes(annotation);\n\t\tmap.forEach((key, value) -> {\n\t\t\tif (value.equals(ValueConstants.DEFAULT_NONE)) {\n\t\t\t\tmap.put(key, \"NONE\");\n\t\t\t}\n\t\t});\n\t\treturn annotation.annotationType().getName() + map;\n\t}\n\n\tprivate static ResolvableType toResolvableType(Class<?> type, Class<?>... generics) {\n\t\treturn ObjectUtils.isEmpty(generics) ? ResolvableType.forClass(type)\n\t\t\t\t: ResolvableType.forClassWithGenerics(type, generics);\n\t}\n\n\tprivate static ResolvableType toResolvableType(Class<?> type, ResolvableType generic, ResolvableType... generics) {\n\t\tResolvableType[] genericTypes = new ResolvableType[generics.length + 1];\n\t\tgenericTypes[0] = generic;\n\t\tSystem.arraycopy(generics, 0, genericTypes, 1, generics.length);\n\t\treturn ResolvableType.forClassWithGenerics(type, genericTypes);\n\t}\n\n\t/**\n\t * Main entry point providing access to a {@code ResolvableMethod} builder.\n\t */\n\tpublic static <T> Builder<T> on(Class<T> objectClass) {\n\t\treturn new Builder<>(objectClass);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> T initProxy(Class<?> type, MethodInvocationInterceptor interceptor) {\n\t\tAssert.notNull(type, \"'type' must not be null\");\n\t\tif (type.isInterface()) {\n\t\t\tProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE);\n\t\t\tfactory.addInterface(type);\n\t\t\tfactory.addInterface(Supplier.class);\n\t\t\tfactory.addAdvice(interceptor);\n\t\t\treturn (T) factory.getProxy();\n\t\t}\n\t\telse {\n\t\t\tEnhancer enhancer = new Enhancer();\n\t\t\tenhancer.setSuperclass(type);\n\t\t\tenhancer.setInterfaces(new Class<?>[] { Supplier.class });\n\t\t\tenhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);\n\t\t\tenhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);\n\t\t\tClass<?> proxyClass = enhancer.createClass();\n\t\t\tObject proxy = null;\n\t\t\tif (objenesis.isWorthTrying()) {\n\t\t\t\ttry {\n\t\t\t\t\tproxy = objenesis.newInstance(proxyClass, enhancer.getUseCache());\n\t\t\t\t}\n\t\t\t\tcatch (ObjenesisException ex) {\n\t\t\t\t\tlogger.debug(\"Objenesis failed, falling back to default constructor\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (proxy == null) {\n\t\t\t\ttry {\n\t\t\t\t\tproxy = ReflectionUtils.accessibleConstructor(proxyClass).newInstance();\n\t\t\t\t}\n\t\t\t\tcatch (Throwable ex) {\n\t\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\t\"Unable to instantiate proxy \" + \"via both Objenesis and default constructor fails as well\",\n\t\t\t\t\t\t\tex);\n\t\t\t\t}\n\t\t\t}\n\t\t\t((Factory) proxy).setCallbacks(new Callback[] { interceptor });\n\t\t\treturn (T) proxy;\n\t\t}\n\t}\n\n\t/**\n\t * Builder for {@code ResolvableMethod}.\n\t */\n\tpublic static final class Builder<T> {\n\n\t\tprivate final Class<?> objectClass;\n\n\t\tprivate final List<Predicate<Method>> filters = new ArrayList<>(4);\n\n\t\tprivate Builder(Class<?> objectClass) {\n\t\t\tAssert.notNull(objectClass, \"Class must not be null\");\n\t\t\tthis.objectClass = objectClass;\n\t\t}\n\n\t\tprivate void addFilter(String message, Predicate<Method> filter) {\n\t\t\tthis.filters.add(new LabeledPredicate<>(message, filter));\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods with the given name.\n\t\t */\n\t\tpublic Builder<T> named(String methodName) {\n\t\t\taddFilter(\"methodName=\" + methodName, (m) -> m.getName().equals(methodName));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on annotated methods. See {@link MvcAnnotationPredicates}.\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final Builder<T> annot(Predicate<Method>... filters) {\n\t\t\tthis.filters.addAll(Arrays.asList(filters));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods annotated with the given annotation type.\n\t\t * @see #annot(Predicate[])\n\t\t * @see MvcAnnotationPredicates\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final Builder<T> annotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\t\tString message = \"annotationPresent=\" + Arrays.toString(annotationTypes);\n\t\t\taddFilter(message, (candidate) -> Arrays.stream(annotationTypes)\n\t\t\t\t.allMatch((annotType) -> AnnotatedElementUtils.findMergedAnnotation(candidate, annotType) != null));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods not annotated with the given annotation type.\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final Builder<T> annotNotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\t\tString message = \"annotationNotPresent=\" + Arrays.toString(annotationTypes);\n\t\t\taddFilter(message, (candidate) -> {\n\t\t\t\tif (annotationTypes.length != 0) {\n\t\t\t\t\treturn Arrays.stream(annotationTypes)\n\t\t\t\t\t\t.noneMatch((\n\t\t\t\t\t\t\t\tannotType) -> AnnotatedElementUtils.findMergedAnnotation(candidate, annotType) != null);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn candidate.getAnnotations().length == 0;\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods returning the given type.\n\t\t * @param returnType the return type\n\t\t * @param generics optional array of generic types\n\t\t */\n\t\tpublic Builder<T> returning(Class<?> returnType, Class<?>... generics) {\n\t\t\treturn returning(toResolvableType(returnType, generics));\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods returning the given type with generics.\n\t\t * @param returnType the return type\n\t\t * @param generic at least one generic type\n\t\t * @param generics optional extra generic types\n\t\t */\n\t\tpublic Builder<T> returning(Class<?> returnType, ResolvableType generic, ResolvableType... generics) {\n\t\t\treturn returning(toResolvableType(returnType, generic, generics));\n\t\t}\n\n\t\t/**\n\t\t * Filter on methods returning the given type.\n\t\t * @param returnType the return type\n\t\t */\n\t\tpublic Builder<T> returning(ResolvableType returnType) {\n\t\t\tString expected = returnType.toString();\n\t\t\tString message = \"returnType=\" + expected;\n\t\t\taddFilter(message, (m) -> expected.equals(ResolvableType.forMethodReturnType(m).toString()));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build a {@code ResolvableMethod} from the provided filters which must resolve\n\t\t * to a unique, single method.\n\t\t *\n\t\t * <p>\n\t\t * See additional resolveXxx shortcut methods going directly to {@link Method} or\n\t\t * return type parameter.\n\t\t * @throws IllegalStateException for no match or multiple matches\n\t\t */\n\t\tpublic ResolvableMethod build() {\n\t\t\tSet<Method> methods = MethodIntrospector.selectMethods(this.objectClass, this::isMatch);\n\t\t\tAssert.state(!methods.isEmpty(), () -> \"No matching method: \" + this);\n\t\t\tAssert.state(methods.size() == 1, () -> \"Multiple matching methods: \" + this + formatMethods(methods));\n\t\t\treturn new ResolvableMethod(methods.iterator().next());\n\t\t}\n\n\t\tprivate boolean isMatch(Method method) {\n\t\t\treturn this.filters.stream().allMatch((p) -> p.test(method));\n\t\t}\n\n\t\tprivate String formatMethods(Set<Method> methods) {\n\t\t\treturn \"\\nMatched:\\n\" + methods.stream()\n\t\t\t\t.map(Method::toGenericString)\n\t\t\t\t.collect(Collectors.joining(\",\\n\\t\", \"[\\n\\t\", \"\\n]\"));\n\t\t}\n\n\t\tpublic ResolvableMethod mockCall(Consumer<T> invoker) {\n\t\t\tMethodInvocationInterceptor interceptor = new MethodInvocationInterceptor();\n\t\t\tT proxy = initProxy(this.objectClass, interceptor);\n\t\t\tinvoker.accept(proxy);\n\t\t\tMethod method = interceptor.getInvokedMethod();\n\t\t\treturn new ResolvableMethod(method);\n\t\t}\n\n\t\t// Build & resolve shortcuts...\n\t\t/**\n\t\t * Resolve and return the {@code Method} equivalent to:\n\t\t * <p>\n\t\t * {@code build().method()}\n\t\t */\n\t\tpublic Method resolveMethod() {\n\t\t\treturn build().method();\n\t\t}\n\n\t\t/**\n\t\t * Resolve and return the {@code Method} equivalent to:\n\t\t * <p>\n\t\t * {@code named(methodName).build().method()}\n\t\t */\n\t\tpublic Method resolveMethod(String methodName) {\n\t\t\treturn named(methodName).build().method();\n\t\t}\n\n\t\t/**\n\t\t * Resolve and return the declared return type equivalent to:\n\t\t * <p>\n\t\t * {@code build().returnType()}\n\t\t */\n\t\tpublic MethodParameter resolveReturnType() {\n\t\t\treturn build().returnType();\n\t\t}\n\n\t\t/**\n\t\t * Shortcut to the unique return type equivalent to:\n\t\t * <p>\n\t\t * {@code returning(returnType).build().returnType()}\n\t\t * @param returnType the return type\n\t\t * @param generics optional array of generic types\n\t\t */\n\t\tpublic MethodParameter resolveReturnType(Class<?> returnType, Class<?>... generics) {\n\t\t\treturn returning(returnType, generics).build().returnType();\n\t\t}\n\n\t\t/**\n\t\t * Shortcut to the unique return type equivalent to:\n\t\t * <p>\n\t\t * {@code returning(returnType).build().returnType()}\n\t\t * @param returnType the return type\n\t\t * @param generic at least one generic type\n\t\t * @param generics optional extra generic types\n\t\t */\n\t\tpublic MethodParameter resolveReturnType(Class<?> returnType, ResolvableType generic,\n\t\t\t\tResolvableType... generics) {\n\t\t\treturn returning(returnType, generic, generics).build().returnType();\n\t\t}\n\n\t\tpublic MethodParameter resolveReturnType(ResolvableType returnType) {\n\t\t\treturn returning(returnType).build().returnType();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"ResolvableMethod.Builder[\\n\" + \"\\tobjectClass = \" + this.objectClass.getName() + \",\\n\"\n\t\t\t\t\t+ \"\\tfilters = \" + formatFilters() + \"\\n]\";\n\t\t}\n\n\t\tprivate String formatFilters() {\n\t\t\treturn this.filters.stream()\n\t\t\t\t.map(Object::toString)\n\t\t\t\t.collect(Collectors.joining(\",\\n\\t\\t\", \"[\\n\\t\\t\", \"\\n\\t]\"));\n\t\t}\n\n\t}\n\n\t/**\n\t * Predicate with a descriptive label.\n\t */\n\tprivate static final class LabeledPredicate<T> implements Predicate<T> {\n\n\t\tprivate final String label;\n\n\t\tprivate final Predicate<T> delegate;\n\n\t\tprivate LabeledPredicate(String label, Predicate<T> delegate) {\n\t\t\tthis.label = label;\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean test(T method) {\n\t\t\treturn this.delegate.test(method);\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate<T> and(Predicate<? super T> other) {\n\t\t\treturn this.delegate.and(other);\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate<T> negate() {\n\t\t\treturn this.delegate.negate();\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate<T> or(Predicate<? super T> other) {\n\t\t\treturn this.delegate.or(other);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn this.label;\n\t\t}\n\n\t}\n\n\t/**\n\t * Resolver for method arguments.\n\t */\n\tpublic final class ArgResolver {\n\n\t\tprivate final List<Predicate<MethodParameter>> filters = new ArrayList<>(4);\n\n\t\t@SafeVarargs\n\t\tprivate ArgResolver(Predicate<MethodParameter>... filter) {\n\t\t\tthis.filters.addAll(Arrays.asList(filter));\n\t\t}\n\n\t\t/**\n\t\t * Filter on method arguments with annotations. See\n\t\t * {@link MvcAnnotationPredicates}.\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final ArgResolver annot(Predicate<MethodParameter>... filters) {\n\t\t\tthis.filters.addAll(Arrays.asList(filters));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on method arguments that have the given annotations.\n\t\t * @param annotationTypes the annotation types\n\t\t * @see #annot(Predicate[])\n\t\t * @see MvcAnnotationPredicates\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final ArgResolver annotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\t\tthis.filters.add((param) -> Arrays.stream(annotationTypes).allMatch(param::hasParameterAnnotation));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Filter on method arguments that don't have the given annotations.\n\t\t * @param annotationTypes the annotation types\n\t\t */\n\t\t@SafeVarargs\n\t\tpublic final ArgResolver annotNotPresent(Class<? extends Annotation>... annotationTypes) {\n\t\t\tthis.filters.add((param) -> (annotationTypes.length != 0)\n\t\t\t\t\t? Arrays.stream(annotationTypes).noneMatch(param::hasParameterAnnotation)\n\t\t\t\t\t: param.getParameterAnnotations().length == 0);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Resolve the argument also matching to the given type.\n\t\t * @param type the expected type\n\t\t */\n\t\tpublic MethodParameter arg(Class<?> type, Class<?>... generics) {\n\t\t\treturn arg(toResolvableType(type, generics));\n\t\t}\n\n\t\t/**\n\t\t * Resolve the argument also matching to the given type.\n\t\t * @param type the expected type\n\t\t */\n\t\tpublic MethodParameter arg(Class<?> type, ResolvableType generic, ResolvableType... generics) {\n\t\t\treturn arg(toResolvableType(type, generic, generics));\n\t\t}\n\n\t\t/**\n\t\t * Resolve the argument also matching to the given type.\n\t\t * @param type the expected type\n\t\t */\n\t\tpublic MethodParameter arg(ResolvableType type) {\n\t\t\tthis.filters.add((p) -> type.toString().equals(ResolvableType.forMethodParameter(p).toString()));\n\t\t\treturn arg();\n\t\t}\n\n\t\t/**\n\t\t * Resolve the argument.\n\t\t */\n\t\tpublic MethodParameter arg() {\n\t\t\tList<MethodParameter> matches = applyFilters();\n\t\t\tAssert.state(!matches.isEmpty(), () -> \"No matching arg in method\\n\" + formatMethod());\n\t\t\tAssert.state(matches.size() == 1,\n\t\t\t\t\t() -> \"Multiple matching args in method\\n\" + formatMethod() + \"\\nMatches:\\n\\t\" + matches);\n\t\t\treturn matches.get(0);\n\t\t}\n\n\t\tprivate List<MethodParameter> applyFilters() {\n\t\t\tList<MethodParameter> matches = new ArrayList<>();\n\t\t\tfor (int i = 0; i < ResolvableMethod.this.method.getParameterCount(); i++) {\n\t\t\t\tMethodParameter param = new SynthesizingMethodParameter(ResolvableMethod.this.method, i);\n\t\t\t\tif (this.filters.stream().allMatch((p) -> p.test(param))) {\n\t\t\t\t\tmatches.add(param);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn matches;\n\t\t}\n\n\t}\n\n\tprivate static class MethodInvocationInterceptor\n\t\t\timplements org.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor {\n\n\t\tprivate Method invokedMethod;\n\n\t\tMethod getInvokedMethod() {\n\t\t\treturn this.invokedMethod;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) {\n\t\t\tif (ReflectionUtils.isObjectMethod(method)) {\n\t\t\t\treturn ReflectionUtils.invokeMethod(method, object, args);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.invokedMethod = method;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Object invoke(org.aopalliance.intercept.MethodInvocation inv) {\n\t\t\treturn intercept(inv.getThis(), inv.getMethod(), inv.getArguments(), null);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.method.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.core.annotation.AnnotatedMethod;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.mockito.BDDMockito.verify;\n\n/**\n * @author Rob Winch\n *\n */\npublic class AuthenticationPrincipalArgumentResolverTests {\n\n\tprivate BeanResolver beanResolver;\n\n\tprivate Object expectedPrincipal;\n\n\tprivate AuthenticationPrincipalArgumentResolver resolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.beanResolver = mock(BeanResolver.class);\n\t\tthis.resolver = new AuthenticationPrincipalArgumentResolver();\n\t\tthis.resolver.setBeanResolver(this.beanResolver);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void supportsParameterNoAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserNoAnnotation())).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsParameterAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserAnnotationObject())).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterCustomAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showUserCustomAnnotation())).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullAuthentication() throws Exception {\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null, null, null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullPrincipal() throws Exception {\n\t\tsetAuthenticationPrincipal(null);\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null, null, null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentString() throws Exception {\n\t\tsetAuthenticationPrincipal(\"john\");\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentPrincipalStringOnObject() throws Exception {\n\t\tsetAuthenticationPrincipal(\"john\");\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationObject(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentUserDetails() throws Exception {\n\t\tsetAuthenticationPrincipal(new User(\"user\", \"password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\")));\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationUserDetails(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomUserPrincipal() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationCustomUserPrincipal(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomAnnotation() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserCustomAnnotation(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSpel() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.expectedPrincipal = principal.property;\n\t\tassertThat(this.resolver.resolveArgument(showUserSpel(), null, null, null)).isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSpelBean() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tgiven(this.beanResolver.resolve(any(), eq(\"test\"))).willReturn(principal.property);\n\t\tthis.expectedPrincipal = principal.property;\n\t\tassertThat(this.resolver.resolveArgument(showUserSpelBean(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t\tverify(this.beanResolver).resolve(any(), eq(\"test\"));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSpelCopy() throws Exception {\n\t\tCopyUserPrincipal principal = new CopyUserPrincipal(\"property\");\n\t\tsetAuthenticationPrincipal(principal);\n\t\tObject resolveArgument = this.resolver.resolveArgument(showUserSpelCopy(), null, null, null);\n\t\tassertThat(resolveArgument).isEqualTo(principal);\n\t\tassertThat(resolveArgument).isNotSameAs(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSpelPrimitive() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.expectedPrincipal = principal.id;\n\t\tassertThat(this.resolver.resolveArgument(showUserSpelPrimitive(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationString(), null, null, null)).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentErrorOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThatExceptionOfType(ClassCastException.class)\n\t\t\t.isThrownBy(() -> this.resolver.resolveArgument(showUserAnnotationErrorOnInvalidType(), null, null, null));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomserErrorOnInvalidType() throws Exception {\n\t\tsetAuthenticationPrincipal(new CustomUserPrincipal());\n\t\tassertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> this.resolver\n\t\t\t.resolveArgument(showUserAnnotationCurrentUserErrorOnInvalidType(), null, null, null));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentObject() throws Exception {\n\t\tsetAuthenticationPrincipal(new Object());\n\t\tassertThat(this.resolver.resolveArgument(showUserAnnotationObject(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotation() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.expectedPrincipal = principal.id;\n\t\tassertThat(this.resolver.resolveArgument(showUserCustomMetaAnnotation(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotationTpl() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());\n\t\tthis.expectedPrincipal = principal.id;\n\t\tassertThat(this.resolver.resolveArgument(showUserCustomMetaAnnotationTpl(), null, null, null))\n\t\t\t.isEqualTo(this.expectedPrincipal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAliasForOnInterfaceThenInherits() throws Exception {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tsetAuthenticationPrincipal(principal);\n\t\tassertThat(this.resolver.resolveArgument(showUserNoConcreteAnnotation(), null, null, null))\n\t\t\t.isEqualTo(principal.property);\n\t}\n\n\tprivate MethodParameter showUserNoAnnotation() {\n\t\treturn getMethodParameter(\"showUserNoAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showUserNoConcreteAnnotation() {\n\t\treturn getMethodParameter(\"showUserNoConcreteAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationString() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationErrorOnInvalidType() {\n\t\treturn getMethodParameter(\"showUserAnnotationErrorOnInvalidType\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationCurrentUserErrorOnInvalidType() {\n\t\treturn getMethodParameter(\"showUserAnnotationCurrentUserErrorOnInvalidType\", String.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationUserDetails() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", UserDetails.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationCustomUserPrincipal() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", CustomUserPrincipal.class);\n\t}\n\n\tprivate MethodParameter showUserCustomAnnotation() {\n\t\treturn getMethodParameter(\"showUserCustomAnnotation\", CustomUserPrincipal.class);\n\t}\n\n\tprivate MethodParameter showUserSpel() {\n\t\treturn getMethodParameter(\"showUserSpel\", String.class);\n\t}\n\n\tprivate MethodParameter showUserSpelBean() {\n\t\treturn getMethodParameter(\"showUserSpelBean\", String.class);\n\t}\n\n\tprivate MethodParameter showUserSpelCopy() {\n\t\treturn getMethodParameter(\"showUserSpelCopy\", CopyUserPrincipal.class);\n\t}\n\n\tprivate MethodParameter showUserSpelPrimitive() {\n\t\treturn getMethodParameter(\"showUserSpelPrimitive\", int.class);\n\t}\n\n\tprivate MethodParameter showUserAnnotationObject() {\n\t\treturn getMethodParameter(\"showUserAnnotation\", Object.class);\n\t}\n\n\tprivate MethodParameter showUserCustomMetaAnnotation() {\n\t\treturn getMethodParameter(\"showUserCustomMetaAnnotation\", int.class);\n\t}\n\n\tprivate MethodParameter showUserCustomMetaAnnotationTpl() {\n\t\treturn getMethodParameter(\"showUserCustomMetaAnnotationTpl\", int.class);\n\t}\n\n\tprivate MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {\n\t\tMethod method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);\n\t\treturn new AnnotatedMethod(method).getMethodParameters()[0];\n\t}\n\n\tprivate void setAuthenticationPrincipal(Object principal) {\n\t\tthis.expectedPrincipal = principal;\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(this.expectedPrincipal, \"password\", \"ROLE_USER\"));\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\tstatic @interface CurrentUser {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal(errorOnInvalidType = true)\n\tstatic @interface CurrentUserErrorOnInvalidType {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\t@interface Property {\n\n\t\t@AliasFor(attribute = \"expression\", annotation = AuthenticationPrincipal.class)\n\t\tString value() default \"id\";\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\tpublic @interface CurrentUser2 {\n\n\t\t@AliasFor(annotation = AuthenticationPrincipal.class)\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal(expression = \"principal.{property}\")\n\tpublic @interface CurrentUser3 {\n\n\t\tString property() default \"\";\n\n\t}\n\n\tpublic interface TestInterface {\n\n\t\tvoid showUserNoConcreteAnnotation(@Property(\"property\") String property);\n\n\t}\n\n\tpublic static class TestController implements TestInterface {\n\n\t\tpublic void showUserNoAnnotation(String user) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void showUserNoConcreteAnnotation(String user) {\n\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotationErrorOnInvalidType(\n\t\t\t\t@AuthenticationPrincipal(errorOnInvalidType = true) String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotationCurrentUserErrorOnInvalidType(@CurrentUserErrorOnInvalidType String user) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal UserDetails user) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal CustomUserPrincipal user) {\n\t\t}\n\n\t\tpublic void showUserCustomAnnotation(@CurrentUser CustomUserPrincipal user) {\n\t\t}\n\n\t\tpublic void showUserCustomMetaAnnotation(@CurrentUser2(expression = \"principal.id\") int userId) {\n\t\t}\n\n\t\tpublic void showUserCustomMetaAnnotationTpl(@CurrentUser3(property = \"id\") int userId) {\n\t\t}\n\n\t\tpublic void showUserAnnotation(@AuthenticationPrincipal Object user) {\n\t\t}\n\n\t\tpublic void showUserSpel(@AuthenticationPrincipal(expression = \"property\") String user) {\n\t\t}\n\n\t\tpublic void showUserSpelBean(@AuthenticationPrincipal(expression = \"@test\") String user) {\n\t\t}\n\n\t\tpublic void showUserSpelCopy(@AuthenticationPrincipal(\n\t\t\t\texpression = \"new org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)\") CopyUserPrincipal user) {\n\t\t}\n\n\t\tpublic void showUserSpelPrimitive(@AuthenticationPrincipal(expression = \"id\") int id) {\n\t\t}\n\n\t}\n\n\tstatic class CustomUserPrincipal {\n\n\t\tpublic final String property = \"property\";\n\n\t\tpublic final int id = 1;\n\n\t\tpublic Object getPrincipal() {\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\tpublic static class CopyUserPrincipal {\n\n\t\tpublic final String property;\n\n\t\tpublic CopyUserPrincipal(String property) {\n\t\t\tthis.property = property;\n\t\t}\n\n\t\tpublic CopyUserPrincipal(CopyUserPrincipal toCopy) {\n\t\t\tthis.property = toCopy.property;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (this == obj) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (obj == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (getClass() != obj.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tCopyUserPrincipal other = (CopyUserPrincipal) obj;\n\t\t\tif (this.property == null) {\n\t\t\t\tif (other.property != null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!this.property.equals(other.property)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tfinal int prime = 31;\n\t\t\tint result = 1;\n\t\t\tresult = prime * result + ((this.property == null) ? 0 : this.property.hashCode());\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/method/annotation/CsrfTokenArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.method.annotation;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.web.bind.support.WebDataBinderFactory;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.method.support.ModelAndViewContainer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class CsrfTokenArgumentResolverTests {\n\n\t@Mock\n\tprivate ModelAndViewContainer mavContainer;\n\n\t@Mock\n\tprivate WebDataBinderFactory binderFactory;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate NativeWebRequest webRequest;\n\n\tprivate CsrfToken token;\n\n\tprivate CsrfTokenArgumentResolver resolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.token = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"secret\");\n\t\tthis.resolver = new CsrfTokenArgumentResolver();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.webRequest = new ServletWebRequest(this.request);\n\t}\n\n\t@Test\n\tpublic void supportsParameterFalse() {\n\t\tassertThat(this.resolver.supportsParameter(noToken())).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsParameterTrue() {\n\t\tassertThat(this.resolver.supportsParameter(token())).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNotFound() throws Exception {\n\t\tassertThat(this.resolver.resolveArgument(token(), this.mavContainer, this.webRequest, this.binderFactory))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentFound() throws Exception {\n\t\tthis.request.setAttribute(CsrfToken.class.getName(), this.token);\n\t\tassertThat(this.resolver.resolveArgument(token(), this.mavContainer, this.webRequest, this.binderFactory))\n\t\t\t.isSameAs(this.token);\n\t}\n\n\tprivate MethodParameter noToken() {\n\t\treturn getMethodParameter(\"noToken\", String.class);\n\t}\n\n\tprivate MethodParameter token() {\n\t\treturn getMethodParameter(\"token\", CsrfToken.class);\n\t}\n\n\tprivate MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {\n\t\tMethod method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);\n\t\treturn new MethodParameter(method, 0);\n\t}\n\n\tpublic static class TestController {\n\n\t\tpublic void noToken(String user) {\n\t\t}\n\n\t\tpublic void token(CsrfToken token) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.method.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.spel.SpelEvaluationException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.mock;\nimport static org.mockito.BDDMockito.verify;\n\n/**\n * @author Dan Zheng\n * @since 5.2\n *\n */\npublic class CurrentSecurityContextArgumentResolverTests {\n\n\tprivate BeanResolver beanResolver;\n\n\tprivate CurrentSecurityContextArgumentResolver resolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.beanResolver = mock(BeanResolver.class);\n\t\tthis.resolver = new CurrentSecurityContextArgumentResolver();\n\t\tthis.resolver.setBeanResolver(this.beanResolver);\n\t}\n\n\t@AfterEach\n\tpublic void cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void supportsParameterNoAnnotationWrongType() {\n\t\tassertThat(this.resolver.supportsParameter(showSecurityContextNoAnnotationTypeMismatch())).isFalse();\n\t}\n\n\t@Test\n\tpublic void supportsParameterNoAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showSecurityContextNoAnnotation())).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterCustomSecurityContextNoAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showSecurityContextWithCustomSecurityContextNoAnnotation()))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterNoAnnotationCustomType() {\n\t\tassertThat(this.resolver.supportsParameter(showSecurityContextWithCustomSecurityContextNoAnnotation()))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(showSecurityContextAnnotation())).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithCustomSecurityContext() {\n\t\tString principal = \"custom_security_context\";\n\t\tsetAuthenticationPrincipalWithCustomSecurityContext(principal);\n\t\tCustomSecurityContext customSecurityContext = (CustomSecurityContext) this.resolver\n\t\t\t.resolveArgument(showAnnotationWithCustomSecurityContext(), null, null, null);\n\t\tassertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithCustomSecurityContextNoAnnotation() {\n\t\tString principal = \"custom_security_context\";\n\t\tsetAuthenticationPrincipalWithCustomSecurityContext(principal);\n\t\tCustomSecurityContext customSecurityContext = (CustomSecurityContext) this.resolver\n\t\t\t.resolveArgument(showSecurityContextWithCustomSecurityContextNoAnnotation(), null, null, null);\n\t\tassertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithNoAnnotation() {\n\t\tString principal = \"custom_security_context\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tSecurityContext securityContext = (SecurityContext) this.resolver\n\t\t\t.resolveArgument(showSecurityContextNoAnnotation(), null, null, null);\n\t\tassertThat(securityContext.getAuthentication().getPrincipal()).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithCustomSecurityContextTypeMatch() {\n\t\tString principal = \"custom_security_context_type_match\";\n\t\tsetAuthenticationPrincipalWithCustomSecurityContext(principal);\n\t\tCustomSecurityContext customSecurityContext = (CustomSecurityContext) this.resolver\n\t\t\t.resolveArgument(showAnnotationWithCustomSecurityContext(), null, null, null);\n\t\tassertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentNullAuthentication() {\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tAuthentication authentication = context.getAuthentication();\n\t\tcontext.setAuthentication(null);\n\t\tassertThat(this.resolver.resolveArgument(showSecurityContextAuthenticationAnnotation(), null, null, null))\n\t\t\t.isNull();\n\t\tcontext.setAuthentication(authentication);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithAuthentication() {\n\t\tString principal = \"john\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tAuthentication auth1 = (Authentication) this.resolver\n\t\t\t.resolveArgument(showSecurityContextAuthenticationAnnotation(), null, null, null);\n\t\tassertThat(auth1.getPrincipal()).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithAuthenticationWithBean() throws Exception {\n\t\tString principal = \"john\";\n\t\tgiven(this.beanResolver.resolve(any(), eq(\"test\"))).willReturn(principal);\n\t\tassertThat(this.resolver.resolveArgument(showSecurityContextAuthenticationWithBean(), null, null, null))\n\t\t\t.isEqualTo(principal);\n\t\tverify(this.beanResolver).resolve(any(), eq(\"test\"));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithNullAuthentication() {\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tAuthentication authentication = context.getAuthentication();\n\t\tcontext.setAuthentication(null);\n\t\tassertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> this.resolver\n\t\t\t.resolveArgument(showSecurityContextAuthenticationWithPrincipal(), null, null, null));\n\t\tcontext.setAuthentication(authentication);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithOptionalPrincipal() {\n\t\tSecurityContext context = SecurityContextHolder.getContext();\n\t\tAuthentication authentication = context.getAuthentication();\n\t\tcontext.setAuthentication(null);\n\t\tObject principalResult = this.resolver.resolveArgument(showSecurityContextAuthenticationWithOptionalPrincipal(),\n\t\t\t\tnull, null, null);\n\t\tassertThat(principalResult).isNull();\n\t\tcontext.setAuthentication(authentication);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithPrincipal() {\n\t\tString principal = \"smith\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tString principalResult = (String) this.resolver\n\t\t\t.resolveArgument(showSecurityContextAuthenticationWithPrincipal(), null, null, null);\n\t\tassertThat(principalResult).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentUserDetails() {\n\t\tsetAuthenticationDetail(new User(\"my_user\", \"my_password\", AuthorityUtils.createAuthorityList(\"ROLE_USER\")));\n\t\tUser u = (User) this.resolver.resolveArgument(showSecurityContextWithUserDetail(), null, null, null);\n\t\tassertThat(u.getUsername()).isEqualTo(\"my_user\");\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSecurityContextErrorOnInvalidTypeImplicit() {\n\t\tString principal = \"invalid_type_implicit\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tassertThat(this.resolver.resolveArgument(showSecurityContextErrorOnInvalidTypeImplicit(), null, null, null))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSecurityContextErrorOnInvalidTypeFalse() {\n\t\tString principal = \"invalid_type_false\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tassertThat(this.resolver.resolveArgument(showSecurityContextErrorOnInvalidTypeFalse(), null, null, null))\n\t\t\t.isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentSecurityContextErrorOnInvalidTypeTrue() {\n\t\tString principal = \"invalid_type_true\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tassertThatExceptionOfType(ClassCastException.class).isThrownBy(\n\t\t\t\t() -> this.resolver.resolveArgument(showSecurityContextErrorOnInvalidTypeTrue(), null, null, null));\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenCurrentCustomSecurityContextThenInjectSecurityContext() {\n\t\tassertThat(this.resolver.resolveArgument(showCurrentCustomSecurityContext(), null, null, null)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenCurrentAuthenticationThenInjectAuthentication() {\n\t\tString principal = \"current_authentcation\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tAuthentication auth1 = (Authentication) this.resolver.resolveArgument(showCurrentAuthentication(), null, null,\n\t\t\t\tnull);\n\t\tassertThat(auth1.getPrincipal()).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenCurrentSecurityWithErrorOnInvalidTypeThenInjectSecurityContext() {\n\t\tassertThat(this.resolver.resolveArgument(showCurrentSecurityWithErrorOnInvalidType(), null, null, null))\n\t\t\t.isNotNull();\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenCurrentSecurityWithErrorOnInvalidTypeThenMisMatch() {\n\t\tassertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> this.resolver\n\t\t\t.resolveArgument(showCurrentSecurityWithErrorOnInvalidTypeMisMatch(), null, null, null));\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotation() {\n\t\tString principal = \"current_authentcation\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tString p = (String) this.resolver.resolveArgument(showUserCustomMetaAnnotation(), null, null, null);\n\t\tassertThat(p).isEqualTo(principal);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotationTpl() {\n\t\tString principal = \"current_authentcation\";\n\t\tsetAuthenticationPrincipal(principal);\n\t\tthis.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());\n\t\tString p = (String) this.resolver.resolveArgument(showUserCustomMetaAnnotationTpl(), null, null, null);\n\t\tassertThat(p).isEqualTo(principal);\n\t}\n\n\tprivate MethodParameter showSecurityContextNoAnnotationTypeMismatch() {\n\t\treturn getMethodParameter(\"showSecurityContextNoAnnotation\", String.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextNoAnnotation() {\n\t\treturn getMethodParameter(\"showSecurityContextNoAnnotation\", SecurityContext.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextAnnotation() {\n\t\treturn getMethodParameter(\"showSecurityContextAnnotation\", SecurityContext.class);\n\t}\n\n\tprivate MethodParameter showAnnotationWithCustomSecurityContext() {\n\t\treturn getMethodParameter(\"showAnnotationWithCustomSecurityContext\", CustomSecurityContext.class);\n\t}\n\n\tprivate MethodParameter showAnnotationWithCustomSecurityContextTypeMatch() {\n\t\treturn getMethodParameter(\"showAnnotationWithCustomSecurityContextTypeMatch\", SecurityContext.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextAuthenticationAnnotation() {\n\t\treturn getMethodParameter(\"showSecurityContextAuthenticationAnnotation\", Authentication.class);\n\t}\n\n\tpublic MethodParameter showSecurityContextAuthenticationWithBean() {\n\t\treturn getMethodParameter(\"showSecurityContextAuthenticationWithBean\", String.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextAuthenticationWithOptionalPrincipal() {\n\t\treturn getMethodParameter(\"showSecurityContextAuthenticationWithOptionalPrincipal\", Object.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextAuthenticationWithPrincipal() {\n\t\treturn getMethodParameter(\"showSecurityContextAuthenticationWithPrincipal\", Object.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextWithUserDetail() {\n\t\treturn getMethodParameter(\"showSecurityContextWithUserDetail\", Object.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextErrorOnInvalidTypeImplicit() {\n\t\treturn getMethodParameter(\"showSecurityContextErrorOnInvalidTypeImplicit\", String.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextErrorOnInvalidTypeFalse() {\n\t\treturn getMethodParameter(\"showSecurityContextErrorOnInvalidTypeFalse\", String.class);\n\t}\n\n\tprivate MethodParameter showSecurityContextErrorOnInvalidTypeTrue() {\n\t\treturn getMethodParameter(\"showSecurityContextErrorOnInvalidTypeTrue\", String.class);\n\t}\n\n\tpublic MethodParameter showCurrentCustomSecurityContext() {\n\t\treturn getMethodParameter(\"showCurrentCustomSecurityContext\", SecurityContext.class);\n\t}\n\n\tpublic MethodParameter showCurrentAuthentication() {\n\t\treturn getMethodParameter(\"showCurrentAuthentication\", Authentication.class);\n\t}\n\n\tpublic MethodParameter showUserCustomMetaAnnotation() {\n\t\treturn getMethodParameter(\"showUserCustomMetaAnnotation\", String.class);\n\t}\n\n\tpublic MethodParameter showUserCustomMetaAnnotationTpl() {\n\t\treturn getMethodParameter(\"showUserCustomMetaAnnotationTpl\", String.class);\n\t}\n\n\tpublic MethodParameter showCurrentSecurityWithErrorOnInvalidType() {\n\t\treturn getMethodParameter(\"showCurrentSecurityWithErrorOnInvalidType\", SecurityContext.class);\n\t}\n\n\tpublic MethodParameter showCurrentSecurityWithErrorOnInvalidTypeMisMatch() {\n\t\treturn getMethodParameter(\"showCurrentSecurityWithErrorOnInvalidTypeMisMatch\", String.class);\n\t}\n\n\tpublic MethodParameter showSecurityContextWithCustomSecurityContextNoAnnotation() {\n\t\treturn getMethodParameter(\"showSecurityContextWithCustomSecurityContextNoAnnotation\",\n\t\t\t\tCustomSecurityContext.class);\n\t}\n\n\tprivate MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {\n\t\tMethod method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);\n\t\treturn new MethodParameter(method, 0);\n\t}\n\n\tprivate void setAuthenticationPrincipal(Object principal) {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(principal, \"password\", \"ROLE_USER\"));\n\t}\n\n\tprivate void setAuthenticationPrincipalWithCustomSecurityContext(Object principal) {\n\t\tCustomSecurityContext csc = new CustomSecurityContext();\n\t\tcsc.setAuthentication(new TestingAuthenticationToken(principal, \"password\", \"ROLE_USER\"));\n\t\tSecurityContextHolder.setContext(csc);\n\t}\n\n\tprivate void setAuthenticationDetail(Object detail) {\n\t\tTestingAuthenticationToken tat = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\ttat.setDetails(detail);\n\t\tSecurityContextHolder.getContext().setAuthentication(tat);\n\t}\n\n\tpublic static class TestController {\n\n\t\tpublic void showSecurityContextNoAnnotation(String user) {\n\t\t}\n\n\t\tpublic void showSecurityContextAnnotation(@CurrentSecurityContext SecurityContext context) {\n\t\t}\n\n\t\tpublic void showAnnotationWithCustomSecurityContext(@CurrentSecurityContext CustomSecurityContext context) {\n\t\t}\n\n\t\tpublic void showAnnotationWithCustomSecurityContextTypeMatch(\n\t\t\t\t@CurrentSecurityContext(errorOnInvalidType = true) SecurityContext context) {\n\t\t}\n\n\t\tpublic void showSecurityContextAuthenticationAnnotation(\n\t\t\t\t@CurrentSecurityContext(expression = \"authentication\") Authentication authentication) {\n\t\t}\n\n\t\tpublic void showSecurityContextAuthenticationWithBean(\n\t\t\t\t@CurrentSecurityContext(expression = \"@test\") String name) {\n\t\t}\n\n\t\tpublic void showSecurityContextAuthenticationWithOptionalPrincipal(\n\t\t\t\t@CurrentSecurityContext(expression = \"authentication?.principal\") Object principal) {\n\t\t}\n\n\t\tpublic void showSecurityContextAuthenticationWithPrincipal(\n\t\t\t\t@CurrentSecurityContext(expression = \"authentication.principal\") Object principal) {\n\t\t}\n\n\t\tpublic void showSecurityContextWithUserDetail(\n\t\t\t\t@CurrentSecurityContext(expression = \"authentication.details\") Object detail) {\n\t\t}\n\n\t\tpublic void showSecurityContextErrorOnInvalidTypeImplicit(@CurrentSecurityContext String implicit) {\n\t\t}\n\n\t\tpublic void showSecurityContextErrorOnInvalidTypeFalse(\n\t\t\t\t@CurrentSecurityContext(errorOnInvalidType = false) String implicit) {\n\t\t}\n\n\t\tpublic void showSecurityContextErrorOnInvalidTypeTrue(\n\t\t\t\t@CurrentSecurityContext(errorOnInvalidType = true) String implicit) {\n\t\t}\n\n\t\tpublic void showCurrentCustomSecurityContext(@CurrentCustomSecurityContext SecurityContext context) {\n\t\t}\n\n\t\tpublic void showCurrentAuthentication(@CurrentAuthentication Authentication authentication) {\n\t\t}\n\n\t\tpublic void showUserCustomMetaAnnotation(\n\t\t\t\t@AliasedCurrentSecurityContext(expression = \"authentication.principal\") String name) {\n\t\t}\n\n\t\tpublic void showUserCustomMetaAnnotationTpl(\n\t\t\t\t@CurrentAuthenticationProperty(property = \"principal\") String name) {\n\t\t}\n\n\t\tpublic void showCurrentSecurityWithErrorOnInvalidType(\n\t\t\t\t@CurrentSecurityWithErrorOnInvalidType SecurityContext context) {\n\t\t}\n\n\t\tpublic void showCurrentSecurityWithErrorOnInvalidTypeMisMatch(\n\t\t\t\t@CurrentSecurityWithErrorOnInvalidType String typeMisMatch) {\n\t\t}\n\n\t\tpublic void showSecurityContextNoAnnotation(SecurityContext context) {\n\t\t}\n\n\t\tpublic void showSecurityContextWithCustomSecurityContextNoAnnotation(CustomSecurityContext context) {\n\t\t}\n\n\t}\n\n\tstatic class CustomSecurityContext implements SecurityContext {\n\n\t\tprivate Authentication authentication;\n\n\t\t@Override\n\t\tpublic Authentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAuthentication(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext\n\t@interface CurrentCustomSecurityContext {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(expression = \"authentication\")\n\t@interface CurrentAuthentication {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(errorOnInvalidType = true)\n\tstatic @interface CurrentSecurityWithErrorOnInvalidType {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext\n\t@interface AliasedCurrentSecurityContext {\n\n\t\t@AliasFor(annotation = CurrentSecurityContext.class)\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(expression = \"authentication.{property}\")\n\t@interface CurrentAuthenticationProperty {\n\n\t\tString property() default \"\";\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.reactive.result.method.annotation;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.core.annotation.AnnotatedMethod;\nimport org.springframework.core.annotation.SynthesizingMethodParameter;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.web.method.ResolvableMethod;\nimport org.springframework.web.reactive.BindingContext;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthenticationPrincipalArgumentResolverTests {\n\n\t@Mock\n\tServerWebExchange exchange;\n\n\t@Mock\n\tBindingContext bindingContext;\n\n\t@Mock\n\tAuthentication authentication;\n\n\t@Mock\n\tBeanResolver beanResolver;\n\n\tResolvableMethod authenticationPrincipal = ResolvableMethod.on(getClass()).named(\"authenticationPrincipal\").build();\n\n\tResolvableMethod spel = ResolvableMethod.on(getClass()).named(\"spel\").build();\n\n\tResolvableMethod spelPrimitive = ResolvableMethod.on(getClass()).named(\"spelPrimitive\").build();\n\n\tResolvableMethod meta = ResolvableMethod.on(getClass()).named(\"meta\").build();\n\n\tResolvableMethod bean = ResolvableMethod.on(getClass()).named(\"bean\").build();\n\n\tAuthenticationPrincipalArgumentResolver resolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.resolver = new AuthenticationPrincipalArgumentResolver(new ReactiveAdapterRegistry());\n\t\tthis.resolver.setBeanResolver(this.beanResolver);\n\t}\n\n\t@Test\n\tpublic void supportsParameterAuthenticationPrincipal() {\n\t\tassertThat(this.resolver.supportsParameter(this.authenticationPrincipal.arg(String.class))).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterCurrentUser() {\n\t\tassertThat(this.resolver.supportsParameter(this.meta.arg(String.class))).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenIsAuthenticationThenObtainsPrincipal() {\n\t\tMethodParameter parameter = this.authenticationPrincipal.arg(String.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(\"user\");\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.block()).isEqualTo(this.authentication.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenIsEmptyThenMonoEmpty() {\n\t\tMethodParameter parameter = this.authenticationPrincipal.arg(String.class);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tassertThat(argument).isNotNull();\n\t\tassertThat(argument.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenMonoIsAuthenticationThenObtainsPrincipal() {\n\t\tMethodParameter parameter = this.authenticationPrincipal.arg(Mono.class, String.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(\"user\");\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.cast(Mono.class).block().block()).isEqualTo(this.authentication.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenMonoIsAuthenticationAndNoGenericThenObtainsPrincipal() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"authenticationPrincipalNoGeneric\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(\"user\");\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.cast(Mono.class).block().block()).isEqualTo(this.authentication.getPrincipal());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenSpelThenObtainsPrincipal() {\n\t\tMyUser user = new MyUser(3L);\n\t\tMethodParameter parameter = this.spel.arg(Long.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(user);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.block()).isEqualTo(user.getId());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenSpelWithPrimitiveThenObtainsPrincipal() {\n\t\tMyUserPrimitive user = new MyUserPrimitive(3);\n\t\tMethodParameter parameter = this.spelPrimitive.arg(int.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(user);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.block()).isEqualTo(user.getId());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenBeanThenObtainsPrincipal() throws Exception {\n\t\tMyUser user = new MyUser(3L);\n\t\tMethodParameter parameter = this.bean.arg(Long.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(user);\n\t\tgiven(this.beanResolver.resolve(any(), eq(\"beanName\"))).willReturn(new Bean());\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.block()).isEqualTo(user.getId());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenMetaThenObtainsPrincipal() {\n\t\tMethodParameter parameter = this.meta.arg(String.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(\"user\");\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.block()).isEqualTo(\"user\");\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenErrorOnInvalidTypeImplicit() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"errorOnInvalidTypeWhenImplicit\")\n\t\t\t.build()\n\t\t\t.arg(Integer.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(\"user\");\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenErrorOnInvalidTypeExplicitFalse() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"errorOnInvalidTypeWhenExplicitFalse\")\n\t\t\t.build()\n\t\t\t.arg(Integer.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(\"user\");\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(argument.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenErrorOnInvalidTypeExplicitTrue() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"errorOnInvalidTypeWhenExplicitTrue\")\n\t\t\t.build()\n\t\t\t.arg(Integer.class);\n\t\tgiven(this.authentication.getPrincipal()).willReturn(\"user\");\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> argument.block());\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotation() {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tgiven(this.authentication.getPrincipal()).willReturn(principal);\n\t\tMono<Object> result = this.resolver\n\t\t\t.resolveArgument(arg0(\"showUserCustomMetaAnnotation\"), this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(result.block()).isEqualTo(principal.id);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotationTpl() {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tgiven(this.authentication.getPrincipal()).willReturn(principal);\n\t\tthis.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());\n\t\tMono<Object> result = this.resolver\n\t\t\t.resolveArgument(arg0(\"showUserCustomMetaAnnotationTpl\"), this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(result.block()).isEqualTo(principal.id);\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenAliasForOnInterfaceThenInherits() {\n\t\tCustomUserPrincipal principal = new CustomUserPrincipal();\n\t\tgiven(this.authentication.getPrincipal()).willReturn(principal);\n\t\tResolvableMethod method = ResolvableMethod.on(TestController.class)\n\t\t\t.named(\"showUserNoConcreteAnnotation\")\n\t\t\t.build();\n\t\tMethodParameter parameter = new AnnotatedMethod(method.method()).getMethodParameters()[0];\n\t\tMono<Object> result = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\tassertThat(result.block()).isEqualTo(principal.property);\n\t}\n\n\tprivate MethodParameter arg0(String methodName) {\n\t\tResolvableMethod method = ResolvableMethod.on(getClass()).named(methodName).build();\n\t\treturn new SynthesizingMethodParameter(method.method(), 0);\n\t}\n\n\tpublic void showUserCustomMetaAnnotation(@CurrentUser2(expression = \"principal.id\") int userId) {\n\t}\n\n\tpublic void showUserCustomMetaAnnotationTpl(@CurrentUser3(property = \"id\") int userId) {\n\t}\n\n\tvoid authenticationPrincipal(@AuthenticationPrincipal String principal,\n\t\t\t@AuthenticationPrincipal Mono<String> monoPrincipal) {\n\t}\n\n\tvoid authenticationPrincipalNoGeneric(@AuthenticationPrincipal Mono monoPrincipal) {\n\t}\n\n\tvoid spel(@AuthenticationPrincipal(expression = \"id\") Long id) {\n\t}\n\n\tvoid spelPrimitive(@AuthenticationPrincipal(expression = \"id\") int id) {\n\t}\n\n\tvoid bean(@AuthenticationPrincipal(expression = \"@beanName.methodName(#this)\") Long id) {\n\t}\n\n\tvoid meta(@CurrentUser String principal) {\n\t}\n\n\tvoid errorOnInvalidTypeWhenImplicit(@AuthenticationPrincipal Integer implicit) {\n\t}\n\n\tvoid errorOnInvalidTypeWhenExplicitFalse(@AuthenticationPrincipal(errorOnInvalidType = false) Integer implicit) {\n\t}\n\n\tvoid errorOnInvalidTypeWhenExplicitTrue(@AuthenticationPrincipal(errorOnInvalidType = true) Integer implicit) {\n\t}\n\n\tstatic class Bean {\n\n\t\tpublic Long methodName(MyUser user) {\n\t\t\treturn user.getId();\n\t\t}\n\n\t}\n\n\tstatic class MyUser {\n\n\t\tprivate final Long id;\n\n\t\tMyUser(Long id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic Long getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t}\n\n\tstatic class MyUserPrimitive {\n\n\t\tprivate final int id;\n\n\t\tMyUserPrimitive(int id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic int getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Documented\n\t@AuthenticationPrincipal\n\tpublic @interface CurrentUser {\n\n\t}\n\n\tstatic class CustomUserPrincipal {\n\n\t\tpublic final int id = 1;\n\n\t\tpublic final String property = \"property\";\n\n\t\tpublic Object getPrincipal() {\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\tpublic @interface CurrentUser2 {\n\n\t\t@AliasFor(annotation = AuthenticationPrincipal.class)\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal(expression = \"principal.{property}\")\n\tpublic @interface CurrentUser3 {\n\n\t\tString property() default \"\";\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AuthenticationPrincipal\n\t@interface Property {\n\n\t\t@AliasFor(attribute = \"expression\", annotation = AuthenticationPrincipal.class)\n\t\tString value() default \"id\";\n\n\t}\n\n\tprivate interface TestInterface {\n\n\t\tvoid showUserNoConcreteAnnotation(@Property(\"property\") String property);\n\n\t}\n\n\tprivate static class TestController implements TestInterface {\n\n\t\t@Override\n\t\tpublic void showUserNoConcreteAnnotation(String user) {\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/reactive/result/method/annotation/CurrentSecurityContextArgumentResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.reactive.result.method.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.expression.BeanResolver;\nimport org.springframework.expression.spel.SpelEvaluationException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;\nimport org.springframework.security.core.annotation.CurrentSecurityContext;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.web.method.ResolvableMethod;\nimport org.springframework.web.reactive.BindingContext;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Dan Zheng\n * @since 5.2\n */\n@ExtendWith(MockitoExtension.class)\npublic class CurrentSecurityContextArgumentResolverTests {\n\n\t@Mock\n\tServerWebExchange exchange;\n\n\t@Mock\n\tBindingContext bindingContext;\n\n\t@Mock\n\tAuthentication authentication;\n\n\t@Mock\n\tBeanResolver beanResolver;\n\n\t@Mock\n\tSecurityContext securityContext;\n\n\tResolvableMethod securityContextMethod = ResolvableMethod.on(getClass()).named(\"securityContext\").build();\n\n\tResolvableMethod securityContextNoAnnotationMethod = ResolvableMethod.on(getClass())\n\t\t.named(\"securityContextNoAnnotation\")\n\t\t.build();\n\n\tResolvableMethod customSecurityContextNoAnnotationMethod = ResolvableMethod.on(getClass())\n\t\t.named(\"customSecurityContextNoAnnotation\")\n\t\t.build();\n\n\tResolvableMethod securityContextWithAuthentication = ResolvableMethod.on(getClass())\n\t\t.named(\"securityContextWithAuthentication\")\n\t\t.build();\n\n\tCurrentSecurityContextArgumentResolver resolver;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.resolver = new CurrentSecurityContextArgumentResolver(new ReactiveAdapterRegistry());\n\t\tthis.resolver.setBeanResolver(this.beanResolver);\n\t}\n\n\t@Test\n\tpublic void supportsParameterCurrentSecurityContext() {\n\t\tassertThat(this.resolver.supportsParameter(this.securityContextMethod.arg(Mono.class, SecurityContext.class)))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterCurrentSecurityContextNoAnnotation() {\n\t\tassertThat(this.resolver\n\t\t\t.supportsParameter(this.securityContextNoAnnotationMethod.arg(Mono.class, SecurityContext.class))).isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterCurrentCustomSecurityContextNoAnnotation() {\n\t\tassertThat(this.resolver.supportsParameter(\n\t\t\t\tthis.customSecurityContextNoAnnotationMethod.arg(Mono.class, CustomSecurityContext.class)))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void supportsParameterWithAuthentication() {\n\t\tassertThat(this.resolver\n\t\t\t.supportsParameter(this.securityContextWithAuthentication.arg(Mono.class, Authentication.class))).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithNullSecurityContext() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContext\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, SecurityContext.class);\n\t\tContext context = ReactiveSecurityContextHolder.withSecurityContext(Mono.empty());\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tObject obj = argument.contextWrite(context).block();\n\t\tassertThat(obj).isNull();\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithSecurityContext() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContext\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, SecurityContext.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"hello\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tSecurityContext securityContext = (SecurityContext) argument.contextWrite(context)\n\t\t\t.cast(Mono.class)\n\t\t\t.block()\n\t\t\t.block();\n\t\tassertThat(securityContext.getAuthentication()).isSameAs(auth);\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithSecurityContextNoAnnotation() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContextNoAnnotation\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, SecurityContext.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"hello\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tSecurityContext securityContext = (SecurityContext) argument.contextWrite(context)\n\t\t\t.cast(Mono.class)\n\t\t\t.block()\n\t\t\t.block();\n\t\tassertThat(securityContext.getAuthentication()).isSameAs(auth);\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithCustomSecurityContextNoAnnotation() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"customSecurityContextNoAnnotation\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, CustomSecurityContext.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"hello\");\n\t\tContext context = ReactiveSecurityContextHolder.withSecurityContext(Mono.just(new CustomSecurityContext(auth)));\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tCustomSecurityContext securityContext = (CustomSecurityContext) argument.contextWrite(context)\n\t\t\t.cast(Mono.class)\n\t\t\t.block()\n\t\t\t.block();\n\t\tassertThat(securityContext.getAuthentication()).isSameAs(auth);\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithCustomSecurityContext() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"customSecurityContext\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, SecurityContext.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"hello\");\n\t\tContext context = ReactiveSecurityContextHolder.withSecurityContext(Mono.just(new CustomSecurityContext(auth)));\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tCustomSecurityContext securityContext = (CustomSecurityContext) argument.contextWrite(context)\n\t\t\t.cast(Mono.class)\n\t\t\t.block()\n\t\t\t.block();\n\t\tassertThat(securityContext.getAuthentication()).isSameAs(auth);\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithNullAuthentication1() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContext\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, SecurityContext.class);\n\t\tAuthentication auth = null;\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tSecurityContext securityContext = (SecurityContext) argument.contextWrite(context)\n\t\t\t.cast(Mono.class)\n\t\t\t.block()\n\t\t\t.block();\n\t\tassertThat(securityContext.getAuthentication()).isNull();\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithNullAuthentication2() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContextWithAuthentication\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, Authentication.class);\n\t\tAuthentication auth = null;\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tMono<Object> r = (Mono<Object>) argument.contextWrite(context).block();\n\t\tassertThat(r.block()).isNull();\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithAuthentication1() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContextWithAuthentication\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, Authentication.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"authentication1\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tMono<Authentication> auth1 = (Mono<Authentication>) argument.contextWrite(context).block();\n\t\tassertThat(auth1.block()).isSameAs(auth);\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithNullAuthenticationOptional1() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContextWithDepthPropOptional\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, Object.class);\n\t\tAuthentication auth = null;\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tMono<Object> obj = (Mono<Object>) argument.contextWrite(context).block();\n\t\tassertThat(obj.block()).isNull();\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithAuthenticationOptional1() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContextWithDepthPropOptional\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, Object.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"auth_optional\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tMono<Object> obj = (Mono<Object>) argument.contextWrite(context).block();\n\t\tassertThat(obj.block()).isEqualTo(\"auth_optional\");\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithNullDepthProp1() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContextWithDepthProp\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, Object.class);\n\t\tAuthentication auth = null;\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tassertThatExceptionOfType(SpelEvaluationException.class)\n\t\t\t.isThrownBy(() -> argument.contextWrite(context).block());\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWithStringDepthProp() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"securityContextWithDepthStringProp\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, String.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"auth_string\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tMono<String> obj = (Mono<String>) argument.contextWrite(context).block();\n\t\tassertThat(obj.block()).isEqualTo(\"auth_string\");\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentWhenErrorOnInvalidTypeImplicit() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"errorOnInvalidTypeWhenImplicit\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, String.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"invalid_type_implicit\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tMono<String> obj = (Mono<String>) argument.contextWrite(context).block();\n\t\tassertThat(obj.block()).isNull();\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentErrorOnInvalidTypeWhenExplicitFalse() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"errorOnInvalidTypeWhenExplicitFalse\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, String.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"error_on_invalid_type_explicit_false\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tMono<String> obj = (Mono<String>) argument.contextWrite(context).block();\n\t\tassertThat(obj.block()).isNull();\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentErrorOnInvalidTypeWhenExplicitTrue() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"errorOnInvalidTypeWhenExplicitTrue\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, String.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"error_on_invalid_type_explicit_true\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tassertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> argument.contextWrite(context).block());\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenDefaultSecurityContextThenInjectSecurityContext() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"currentCustomSecurityContext\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, SecurityContext.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"current_custom_security_context\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tSecurityContext securityContext = (SecurityContext) argument.contextWrite(context)\n\t\t\t.cast(Mono.class)\n\t\t\t.block()\n\t\t\t.block();\n\t\tassertThat(securityContext.getAuthentication()).isSameAs(auth);\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenCurrentAuthenticationThenInjectAuthentication() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"currentAuthentication\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, Authentication.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"current_authentication\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tAuthentication authentication = (Authentication) argument.contextWrite(context)\n\t\t\t.cast(Mono.class)\n\t\t\t.block()\n\t\t\t.block();\n\t\tassertThat(authentication).isSameAs(auth);\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenCurrentSecurityWithErrorOnInvalidTypeThenInjectSecurityContext() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"currentSecurityWithErrorOnInvalidType\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, SecurityContext.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"current_security_with_error_on_invalid_type\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tSecurityContext securityContext = (SecurityContext) argument.contextWrite(context)\n\t\t\t.cast(Mono.class)\n\t\t\t.block()\n\t\t\t.block();\n\t\tassertThat(securityContext.getAuthentication()).isSameAs(auth);\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void metaAnnotationWhenCurrentSecurityWithErrorOnInvalidTypeThenMisMatch() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"currentSecurityWithErrorOnInvalidTypeMisMatch\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, String.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"current_security_with_error_on_invalid_type_mismatch\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tassertThatExceptionOfType(ClassCastException.class)\n\t\t\t.isThrownBy(() -> argument.contextWrite(context).cast(Mono.class).block().block());\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotation() {\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"showUserCustomMetaAnnotation\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, String.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"current_authentication\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tString principal = (String) argument.contextWrite(context).cast(Mono.class).block().block();\n\t\tassertThat(principal).isSameAs(auth.getPrincipal());\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void resolveArgumentCustomMetaAnnotationTpl() {\n\t\tthis.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());\n\t\tMethodParameter parameter = ResolvableMethod.on(getClass())\n\t\t\t.named(\"showUserCustomMetaAnnotationTpl\")\n\t\t\t.build()\n\t\t\t.arg(Mono.class, String.class);\n\t\tAuthentication auth = buildAuthenticationWithPrincipal(\"current_authentication\");\n\t\tContext context = ReactiveSecurityContextHolder.withAuthentication(auth);\n\t\tMono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);\n\t\tString principal = (String) argument.contextWrite(context).cast(Mono.class).block().block();\n\t\tassertThat(principal).isSameAs(auth.getPrincipal());\n\t\tReactiveSecurityContextHolder.clearContext();\n\t}\n\n\tvoid showUserCustomMetaAnnotation(\n\t\t\t@AliasedCurrentSecurityContext(expression = \"authentication.principal\") Mono<String> user) {\n\t}\n\n\tvoid showUserCustomMetaAnnotationTpl(@CurrentAuthenticationProperty(property = \"principal\") Mono<String> user) {\n\t}\n\n\tvoid securityContext(@CurrentSecurityContext Mono<SecurityContext> monoSecurityContext) {\n\t}\n\n\tvoid securityContextNoAnnotation(Mono<SecurityContext> securityContextMono) {\n\t}\n\n\tvoid customSecurityContextNoAnnotation(Mono<CustomSecurityContext> securityContextMono) {\n\t}\n\n\tvoid customSecurityContext(@CurrentSecurityContext Mono<SecurityContext> monoSecurityContext) {\n\t}\n\n\tvoid securityContextWithAuthentication(\n\t\t\t@CurrentSecurityContext(expression = \"authentication\") Mono<Authentication> authentication) {\n\t}\n\n\tvoid securityContextWithDepthPropOptional(\n\t\t\t@CurrentSecurityContext(expression = \"authentication?.principal\") Mono<Object> principal) {\n\t}\n\n\tvoid securityContextWithDepthProp(\n\t\t\t@CurrentSecurityContext(expression = \"authentication.principal\") Mono<Object> principal) {\n\t}\n\n\tvoid securityContextWithDepthStringProp(\n\t\t\t@CurrentSecurityContext(expression = \"authentication.principal\") Mono<String> principal) {\n\t}\n\n\tvoid errorOnInvalidTypeWhenImplicit(@CurrentSecurityContext Mono<String> implicit) {\n\t}\n\n\tvoid errorOnInvalidTypeWhenExplicitFalse(\n\t\t\t@CurrentSecurityContext(errorOnInvalidType = false) Mono<String> implicit) {\n\t}\n\n\tvoid errorOnInvalidTypeWhenExplicitTrue(@CurrentSecurityContext(errorOnInvalidType = true) Mono<String> implicit) {\n\t}\n\n\tvoid currentCustomSecurityContext(@CurrentCustomSecurityContext Mono<SecurityContext> monoSecurityContext) {\n\t}\n\n\tvoid currentAuthentication(@CurrentAuthentication Mono<Authentication> authentication) {\n\t}\n\n\tvoid currentSecurityWithErrorOnInvalidType(\n\t\t\t@CurrentSecurityWithErrorOnInvalidType Mono<SecurityContext> monoSecurityContext) {\n\t}\n\n\tvoid currentSecurityWithErrorOnInvalidTypeMisMatch(\n\t\t\t@CurrentSecurityWithErrorOnInvalidType Mono<String> typeMisMatch) {\n\t}\n\n\tprivate Authentication buildAuthenticationWithPrincipal(Object principal) {\n\t\treturn new TestingAuthenticationToken(principal, \"password\", \"ROLE_USER\");\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext\n\t@interface CurrentCustomSecurityContext {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(expression = \"authentication\")\n\t@interface CurrentAuthentication {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(errorOnInvalidType = true)\n\t@interface CurrentSecurityWithErrorOnInvalidType {\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext\n\t@interface AliasedCurrentSecurityContext {\n\n\t\t@AliasFor(annotation = CurrentSecurityContext.class)\n\t\tString expression() default \"\";\n\n\t}\n\n\t@Target({ ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@CurrentSecurityContext(expression = \"authentication.{property}\")\n\t@interface CurrentAuthenticationProperty {\n\n\t\tString property() default \"\";\n\n\t}\n\n\tstatic class CustomSecurityContext implements SecurityContext {\n\n\t\tprivate Authentication authentication;\n\n\t\tCustomSecurityContext(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic Authentication getAuthentication() {\n\t\t\treturn this.authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAuthentication(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.reactive.result.view;\n\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.csrf.CsrfToken;\nimport org.springframework.security.web.server.csrf.DefaultCsrfToken;\nimport org.springframework.util.ReflectionUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class CsrfRequestDataValueProcessorTests {\n\n\tprivate MockServerWebExchange exchange = exchange(HttpMethod.GET);\n\n\tprivate CsrfRequestDataValueProcessor processor = new CsrfRequestDataValueProcessor();\n\n\tprivate CsrfToken token = new DefaultCsrfToken(\"1\", \"a\", \"b\");\n\n\tprivate Map<String, String> expected = new HashMap<>();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.expected.put(this.token.getParameterName(), this.token.getToken());\n\t\tthis.exchange.getAttributes().put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, this.token);\n\t}\n\n\t@Test\n\tpublic void assertAllMethodsDeclared() {\n\t\tMethod[] expectedMethods = ReflectionUtils.getAllDeclaredMethods(CsrfRequestDataValueProcessor.class);\n\t\tfor (Method expected : expectedMethods) {\n\t\t\tassertThat(ReflectionUtils.findMethod(CsrfRequestDataValueProcessor.class, expected.getName(),\n\t\t\t\t\texpected.getParameterTypes()))\n\t\t\t\t.as(\"Expected to find \" + expected + \" defined on \" + CsrfRequestDataValueProcessor.class)\n\t\t\t\t.isNotNull();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsNoCsrfToken() {\n\t\tthis.exchange.getAttributes().clear();\n\t\tassertThat(this.processor.getExtraHiddenFields(this.exchange)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfTokenNoMethodSet() {\n\t\tassertThat(this.processor.getExtraHiddenFields(this.exchange)).isEqualTo(this.expected);\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfToken_GET() {\n\t\tthis.processor.processAction(this.exchange, \"action\", \"GET\");\n\t\tassertThat(this.processor.getExtraHiddenFields(this.exchange)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfToken_get() {\n\t\tthis.processor.processAction(this.exchange, \"action\", \"get\");\n\t\tassertThat(this.processor.getExtraHiddenFields(this.exchange)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfToken_POST() {\n\t\tthis.processor.processAction(this.exchange, \"action\", \"POST\");\n\t\tassertThat(this.processor.getExtraHiddenFields(this.exchange)).isEqualTo(this.expected);\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfToken_post() {\n\t\tthis.processor.processAction(this.exchange, \"action\", \"post\");\n\t\tassertThat(this.processor.getExtraHiddenFields(this.exchange)).isEqualTo(this.expected);\n\t}\n\n\t@Test\n\tpublic void processActionWithMethodArg() {\n\t\tString action = \"action\";\n\t\tassertThat(this.processor.processAction(this.exchange, action, null)).isEqualTo(action);\n\t}\n\n\t@Test\n\tpublic void processFormFieldValue() {\n\t\tString value = \"action\";\n\t\tassertThat(this.processor.processFormFieldValue(this.exchange, \"name\", value, \"hidden\")).isEqualTo(value);\n\t}\n\n\t@Test\n\tpublic void processUrl() {\n\t\tString url = \"url\";\n\t\tassertThat(this.processor.processUrl(this.exchange, url)).isEqualTo(url);\n\t}\n\n\t@Test\n\tpublic void createGetExtraHiddenFieldsHasCsrfToken() {\n\t\tCsrfToken token = new DefaultCsrfToken(\"1\", \"a\", \"b\");\n\t\tthis.exchange.getAttributes().put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, token);\n\t\tMap<String, String> expected = new HashMap<>();\n\t\texpected.put(token.getParameterName(), token.getToken());\n\t\tCsrfRequestDataValueProcessor processor = new CsrfRequestDataValueProcessor();\n\t\tassertThat(this.processor.getExtraHiddenFields(this.exchange)).isEqualTo(expected);\n\t}\n\n\tprivate MockServerWebExchange exchange(HttpMethod method) {\n\t\treturn MockServerWebExchange.from(MockServerHttpRequest.method(HttpMethod.GET, \"/\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/savedrequest/CookieRequestCacheTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Collections;\nimport java.util.Locale;\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Zeeshan Adnan\n */\npublic class CookieRequestCacheTests {\n\n\tprivate static final String DEFAULT_COOKIE_NAME = \"REDIRECT_URI\";\n\n\t@Test\n\tpublic void saveRequestWhenMatchesThenSavedRequestInACookieOnResponse() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setServerPort(443);\n\t\trequest.setSecure(true);\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"abc.com\");\n\t\trequest.setRequestURI(\"/destination\");\n\t\trequest.setQueryString(\"param1=a&param2=b&param3=1122\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tcookieRequestCache.saveRequest(request, response);\n\t\tCookie savedCookie = response.getCookie(DEFAULT_COOKIE_NAME);\n\t\tassertThat(savedCookie).isNotNull();\n\t\tString redirectUrl = decodeCookie(savedCookie.getValue());\n\t\tassertThat(redirectUrl).isEqualTo(\"https://abc.com/destination?param1=a&param2=b&param3=1122\");\n\t\tassertThat(savedCookie.getMaxAge()).isEqualTo(-1);\n\t\tassertThat(savedCookie.getPath()).isEqualTo(\"/\");\n\t\tassertThat(savedCookie.isHttpOnly()).isTrue();\n\t\tassertThat(savedCookie.getSecure()).isTrue();\n\t}\n\n\t@Test\n\tpublic void setRequestMatcherWhenRequestMatcherIsSetNullThenThrowsIllegalArgumentException() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> cookieRequestCache.setRequestMatcher(null));\n\t}\n\n\t@Test\n\tpublic void getMatchingRequestWhenRequestMatcherDefinedThenReturnsCorrectSubsetOfCachedRequests() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tcookieRequestCache.setRequestMatcher((request) -> request.getRequestURI().equals(\"/expected-destination\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/destination\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tcookieRequestCache.saveRequest(request, response);\n\t\tSavedRequest savedRequest = cookieRequestCache.getRequest(request, response);\n\t\tassertThat(savedRequest).isNull();\n\t\tHttpServletRequest matchingRequest = cookieRequestCache.getMatchingRequest(request, response);\n\t\tassertThat(matchingRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void getRequestWhenRequestIsWithoutCookiesThenReturnsNullSavedRequest() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tSavedRequest savedRequest = cookieRequestCache.getRequest(new MockHttpServletRequest(),\n\t\t\t\tnew MockHttpServletResponse());\n\t\tassertThat(savedRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void getRequestWhenRequestDoesNotContainSavedRequestCookieThenReturnsNull() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(new Cookie(\"abc_cookie\", \"value\"));\n\t\tSavedRequest savedRequest = cookieRequestCache.getRequest(request, new MockHttpServletResponse());\n\t\tassertThat(savedRequest).isNull();\n\t}\n\n\t@Test\n\tpublic void getRequestWhenRequestContainsSavedRequestCookieThenReturnsSaveRequest() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tString redirectUrl = \"https://abc.com/destination?param1=a&param2=b&param3=1122\";\n\t\trequest.setCookies(new Cookie(DEFAULT_COOKIE_NAME, encodeCookie(redirectUrl)));\n\t\tSavedRequest savedRequest = cookieRequestCache.getRequest(request, new MockHttpServletResponse());\n\t\tassertThat(savedRequest).isNotNull();\n\t\tassertThat(savedRequest.getRedirectUrl()).isEqualTo(redirectUrl);\n\t}\n\n\t@Test\n\tpublic void getRequestWhenRequestContainsSavedRequestCookieThenSavedRequestContainsRequestParameters() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(new Cookie(DEFAULT_COOKIE_NAME, encodeCookie(\"https://abc.com/destination\")));\n\t\trequest.setParameter(\"single\", \"first\");\n\t\trequest.addParameter(\"multi\", \"second\");\n\t\trequest.addParameter(\"multi\", \"third\");\n\t\tSavedRequest savedRequest = cookieRequestCache.getRequest(request, new MockHttpServletResponse());\n\t\tassertThat(savedRequest).isNotNull();\n\t\tassertThat(savedRequest.getParameterValues(\"single\")).containsExactly(\"first\");\n\t\tassertThat(savedRequest.getParameterValues(\"multi\")).containsExactly(\"second\", \"third\");\n\t\tassertThat(savedRequest.getParameterMap()).containsKeys(\"single\", \"multi\");\n\t}\n\n\t@Test\n\tpublic void matchingRequestWhenRequestDoesNotContainSavedRequestCookieThenReturnsNull() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpServletRequest matchingRequest = cookieRequestCache.getMatchingRequest(new MockHttpServletRequest(),\n\t\t\t\tresponse);\n\t\tassertThat(matchingRequest).isNull();\n\t\tassertThat(response.getCookie(DEFAULT_COOKIE_NAME)).isNull();\n\t}\n\n\t@Test\n\tpublic void matchingRequestWhenRequestContainsSavedRequestCookieThenSetsAnExpiredCookieInResponse() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setServerPort(443);\n\t\trequest.setSecure(true);\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"abc.com\");\n\t\trequest.setRequestURI(\"/destination\");\n\t\trequest.setQueryString(\"param1=a&param2=b&param3=1122\");\n\t\tString redirectUrl = \"https://abc.com/destination?param1=a&param2=b&param3=1122\";\n\t\trequest.setCookies(new Cookie(DEFAULT_COOKIE_NAME, encodeCookie(redirectUrl)));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tcookieRequestCache.getMatchingRequest(request, response);\n\t\tCookie expiredCookie = response.getCookie(DEFAULT_COOKIE_NAME);\n\t\tassertThat(expiredCookie).isNotNull();\n\t\tassertThat(expiredCookie.getValue()).isEmpty();\n\t\tassertThat(expiredCookie.getMaxAge()).isZero();\n\t}\n\n\t@Test\n\tpublic void requestWhenDoesNotMatchSavedRequestThenDoesNotClearCookie() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setServerPort(443);\n\t\trequest.setSecure(true);\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"abc.com\");\n\t\trequest.setRequestURI(\"/destination\");\n\t\tString redirectUrl = \"https://abc.com/api\";\n\t\trequest.setCookies(new Cookie(DEFAULT_COOKIE_NAME, encodeCookie(redirectUrl)));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfinal HttpServletRequest matchingRequest = cookieRequestCache.getMatchingRequest(request, response);\n\t\tassertThat(matchingRequest).isNull();\n\t\tCookie expiredCookie = response.getCookie(DEFAULT_COOKIE_NAME);\n\t\tassertThat(expiredCookie).isNull();\n\t}\n\n\t@Test\n\tpublic void matchingRequestWhenUrlEncodedQueryParametersThenDoesNotDuplicate() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setServerPort(443);\n\t\trequest.setSecure(true);\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"abc.com\");\n\t\trequest.setRequestURI(\"/destination\");\n\t\trequest.setQueryString(\"goto=https%3A%2F%2Fstart.spring.io\");\n\t\trequest.setParameter(\"goto\", \"https://start.spring.io\");\n\t\tString redirectUrl = \"https://abc.com/destination?goto=https%3A%2F%2Fstart.spring.io\";\n\t\trequest.setCookies(new Cookie(DEFAULT_COOKIE_NAME, encodeCookie(redirectUrl)));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfinal HttpServletRequest matchingRequest = cookieRequestCache.getMatchingRequest(request, response);\n\t\tassertThat(matchingRequest).isNotNull();\n\t\tassertThat(matchingRequest.getParameterValues(\"goto\")).containsExactly(\"https://start.spring.io\");\n\t}\n\n\t@Test\n\tpublic void removeRequestWhenInvokedThenSetsAnExpiredCookieOnResponse() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tcookieRequestCache.removeRequest(new MockHttpServletRequest(), response);\n\t\tCookie expiredCookie = response.getCookie(DEFAULT_COOKIE_NAME);\n\t\tassertThat(expiredCookie).isNotNull();\n\t\tassertThat(expiredCookie.getValue()).isEmpty();\n\t\tassertThat(expiredCookie.getMaxAge()).isZero();\n\t}\n\n\t// gh-13792\n\t@Test\n\tpublic void matchingRequestWhenMatchThenKeepOriginalRequestLocale() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setServerPort(443);\n\t\trequest.setSecure(true);\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerName(\"example.com\");\n\t\trequest.setRequestURI(\"/destination\");\n\t\trequest.setPreferredLocales(Arrays.asList(Locale.FRENCH, Locale.GERMANY));\n\t\tString redirectUrl = \"https://example.com/destination\";\n\t\trequest.setCookies(new Cookie(DEFAULT_COOKIE_NAME, encodeCookie(redirectUrl)));\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpServletRequest matchingRequest = cookieRequestCache.getMatchingRequest(request, response);\n\t\tassertThat(matchingRequest).isNotNull();\n\t\tassertThat(Collections.list(matchingRequest.getLocales())).contains(Locale.FRENCH, Locale.GERMANY);\n\t}\n\n\t@Test\n\tpublic void setCookieCustomizer() {\n\t\tConsumer<Cookie> cookieCustomizer = (cookie) -> {\n\t\t\tcookie.setAttribute(\"SameSite\", \"Strict\");\n\t\t\tcookie.setAttribute(\"CustomAttribute\", \"CustomValue\");\n\t\t};\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tcookieRequestCache.setCookieCustomizer(cookieCustomizer);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tcookieRequestCache.saveRequest(new MockHttpServletRequest(), response);\n\t\tCookie savedCookie = response.getCookie(DEFAULT_COOKIE_NAME);\n\t\tassertThat(savedCookie).isNotNull();\n\t\tassertThat(savedCookie.getAttribute(\"SameSite\")).isEqualTo(\"Strict\");\n\t\tassertThat(savedCookie.getAttribute(\"CustomAttribute\")).isEqualTo(\"CustomValue\");\n\t}\n\n\tprivate static String encodeCookie(String cookieValue) {\n\t\treturn Base64.getEncoder().encodeToString(cookieValue.getBytes());\n\t}\n\n\tprivate static String decodeCookie(String encodedCookieValue) {\n\t\treturn new String(Base64.getDecoder().decode(encodedCookieValue.getBytes()));\n\t}\n\n\t// gh-15905\n\t@Test\n\tpublic void illegalCookieValueReturnNull() {\n\t\tCookieRequestCache cookieRequestCache = new CookieRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setCookies(new Cookie(DEFAULT_COOKIE_NAME, \"123^456\"));\n\t\tSavedRequest savedRequest = cookieRequestCache.getRequest(request, new MockHttpServletResponse());\n\t\tassertThat(savedRequest).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/savedrequest/DefaultSavedRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.net.URL;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n *\n */\npublic class DefaultSavedRequestTests {\n\n\t// SEC-308, SEC-315\n\t@Test\n\tpublic void headersAreCaseInsensitive() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"USER-aGenT\", \"Mozilla\");\n\t\tDefaultSavedRequest saved = new DefaultSavedRequest(request);\n\t\tassertThat(saved.getHeaderValues(\"user-agent\").get(0)).isEqualTo(\"Mozilla\");\n\t}\n\n\t// SEC-1412\n\t@Test\n\tpublic void discardsIfNoneMatchHeader() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"If-None-Match\", \"somehashvalue\");\n\t\tDefaultSavedRequest saved = new DefaultSavedRequest(request);\n\t\tassertThat(saved.getHeaderValues(\"if-none-match\")).isEmpty();\n\t}\n\n\t// SEC-3082\n\t@Test\n\tpublic void parametersAreCaseSensitive() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addParameter(\"AnotHerTest\", \"Hi dad\");\n\t\trequest.addParameter(\"thisisatest\", \"Hi mom\");\n\t\tDefaultSavedRequest saved = new DefaultSavedRequest(request);\n\t\tassertThat(saved.getParameterValues(\"thisisatest\")[0]).isEqualTo(\"Hi mom\");\n\t\tassertThat(saved.getParameterValues(\"anothertest\")).isNull();\n\t}\n\n\t@Test\n\tpublic void getRedirectUrlWhenNoQueryAndDefaultMatchingRequestParameterNameThenNoQuery() throws Exception {\n\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest());\n\t\tassertThat(savedRequest.getParameterMap()).doesNotContainKey(\"success\");\n\t\tassertThat(new URL(savedRequest.getRedirectUrl())).hasNoQuery();\n\t}\n\n\t@Test\n\tpublic void getRedirectUrlWhenQueryAndDefaultMatchingRequestParameterNameNullThenNoQuery() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setQueryString(\"foo=bar\");\n\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(request);\n\t\tassertThat(savedRequest.getParameterMap()).doesNotContainKey(\"success\");\n\t\tassertThat(new URL(savedRequest.getRedirectUrl())).hasQuery(\"foo=bar\");\n\t}\n\n\t@Test\n\tpublic void getRedirectUrlWhenNoQueryAndNullMatchingRequestParameterNameThenNoQuery() throws Exception {\n\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest());\n\t\tassertThat(savedRequest.getParameterMap()).doesNotContainKey(\"success\");\n\t\tassertThat(new URL(savedRequest.getRedirectUrl())).hasNoQuery();\n\t}\n\n\t@Test\n\tpublic void getRedirectUrlWhenNoQueryAndMatchingRequestParameterNameThenQuery() throws Exception {\n\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(new MockHttpServletRequest(), \"success\");\n\t\tassertThat(savedRequest.getParameterMap()).doesNotContainKey(\"success\");\n\t\tassertThat(new URL(savedRequest.getRedirectUrl())).hasQuery(\"success\");\n\t}\n\n\t@Test\n\tpublic void getRedirectUrlWhenQueryEmptyAndMatchingRequestParameterNameThenQuery() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setQueryString(\"\");\n\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(request, \"success\");\n\t\tassertThat(savedRequest.getParameterMap()).doesNotContainKey(\"success\");\n\t\tassertThat(new URL(savedRequest.getRedirectUrl())).hasQuery(\"success\");\n\t}\n\n\t@Test\n\tpublic void getRedirectUrlWhenQueryEndsAmpersandAndMatchingRequestParameterNameThenQuery() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setQueryString(\"foo=bar&\");\n\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(request, \"success\");\n\t\tassertThat(savedRequest.getParameterMap()).doesNotContainKey(\"success\");\n\t\tassertThat(new URL(savedRequest.getRedirectUrl())).hasQuery(\"foo=bar&success\");\n\t}\n\n\t@Test\n\tpublic void getRedirectUrlWhenQueryDoesNotEndAmpersandAndMatchingRequestParameterNameThenQuery() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setQueryString(\"foo=bar\");\n\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(request, \"success\");\n\t\tassertThat(savedRequest.getParameterMap()).doesNotContainKey(\"success\");\n\t\tassertThat(new URL(savedRequest.getRedirectUrl())).hasQuery(\"foo=bar&success\");\n\t}\n\n\t// gh-13438\n\t@Test\n\tpublic void getRedirectUrlWhenQueryAlreadyHasSuccessThenDoesNotAdd() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setQueryString(\"foo=bar&success\");\n\t\tDefaultSavedRequest savedRequest = new DefaultSavedRequest(request, \"success\");\n\t\tassertThat(savedRequest.getRedirectUrl()).contains(\"foo=bar&success\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/savedrequest/HttpSessionRequestCacheTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport jakarta.servlet.http.Cookie;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n * @author Eddú Meléndez\n * @since 3.0\n */\npublic class HttpSessionRequestCacheTests {\n\n\t@Test\n\tpublic void originalGetRequestDoesntMatchIncomingPost() {\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/destination\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tcache.saveRequest(request, response);\n\t\tassertThat(request.getSession().getAttribute(HttpSessionRequestCache.SAVED_REQUEST)).isNotNull();\n\t\tassertThat(cache.getRequest(request, response)).isNotNull();\n\t\tMockHttpServletRequest newRequest = new MockHttpServletRequest(\"POST\", \"/destination\");\n\t\tnewRequest.setSession(request.getSession());\n\t\tassertThat(cache.getMatchingRequest(newRequest, response)).isNull();\n\t}\n\n\t@Test\n\tpublic void requestMatcherDefinesCorrectSubsetOfCachedRequests() {\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache();\n\t\tcache.setRequestMatcher((request) -> request.getMethod().equals(\"GET\"));\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/destination\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tcache.saveRequest(request, response);\n\t\tassertThat(cache.getRequest(request, response)).isNull();\n\t\tassertThat(cache.getRequest(new MockHttpServletRequest(), new MockHttpServletResponse())).isNull();\n\t\tassertThat(cache.getMatchingRequest(request, response)).isNull();\n\t}\n\n\t// SEC-2246\n\t@Test\n\tpublic void getRequestCustomNoClassCastException() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/destination\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache() {\n\t\t\t@Override\n\t\t\tpublic void saveRequest(HttpServletRequest request, HttpServletResponse response) {\n\t\t\t\trequest.getSession()\n\t\t\t\t\t.setAttribute(SAVED_REQUEST, new CustomSavedRequest(new DefaultSavedRequest(request)));\n\t\t\t}\n\t\t};\n\t\tcache.saveRequest(request, response);\n\t\tcache.saveRequest(request, response);\n\t\tassertThat(cache.getRequest(request, response)).isInstanceOf(CustomSavedRequest.class);\n\t}\n\n\t@Test\n\tpublic void testCustomSessionAttrName() {\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache();\n\t\tcache.setSessionAttrName(\"CUSTOM_SAVED_REQUEST\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/destination\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tcache.saveRequest(request, response);\n\t\tassertThat(request.getSession().getAttribute(HttpSessionRequestCache.SAVED_REQUEST)).isNull();\n\t\tassertThat(request.getSession().getAttribute(\"CUSTOM_SAVED_REQUEST\")).isNotNull();\n\t}\n\n\t@Test\n\tpublic void getMatchingRequestWhenMatchingRequestParameterNameSetThenSessionNotAccessed() {\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache();\n\t\tcache.setMatchingRequestParameterName(\"success\");\n\t\tHttpServletRequest request = spy(new MockHttpServletRequest());\n\t\tHttpServletRequest matchingRequest = cache.getMatchingRequest(request, new MockHttpServletResponse());\n\t\tassertThat(matchingRequest).isNull();\n\t\tverify(request, never()).getSession();\n\t\tverify(request, never()).getSession(anyBoolean());\n\t}\n\n\t@Test\n\tpublic void getMatchingRequestWhenMatchingRequestParameterNameSetAndParameterExistThenLookedUp() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache();\n\t\tcache.setMatchingRequestParameterName(\"success\");\n\t\tcache.saveRequest(request, new MockHttpServletResponse());\n\t\tMockHttpServletRequest requestToMatch = new MockHttpServletRequest();\n\t\trequestToMatch.setQueryString(\"success\"); // gh-12665\n\t\trequestToMatch.setSession(request.getSession());\n\t\tHttpServletRequest matchingRequest = cache.getMatchingRequest(requestToMatch, new MockHttpServletResponse());\n\t\tassertThat(matchingRequest).isNotNull();\n\t}\n\n\t// gh-12665\n\t@Test\n\tpublic void getMatchingRequestWhenMatchingRequestParameterNameSetAndParameterExistAndQueryThenLookedUp() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setQueryString(\"param=true\");\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache();\n\t\tcache.setMatchingRequestParameterName(\"success\");\n\t\tcache.saveRequest(request, new MockHttpServletResponse());\n\t\tMockHttpServletRequest requestToMatch = new MockHttpServletRequest();\n\t\trequestToMatch.setQueryString(\"param=true&success\");\n\t\trequestToMatch.setSession(request.getSession());\n\t\tHttpServletRequest matchingRequest = cache.getMatchingRequest(requestToMatch, new MockHttpServletResponse());\n\t\tassertThat(matchingRequest).isNotNull();\n\t}\n\n\t@Test\n\tpublic void getMatchingRequestWhenMatchesThenRemoved() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache();\n\t\tcache.setMatchingRequestParameterName(\"success\");\n\t\tcache.saveRequest(request, new MockHttpServletResponse());\n\t\tassertThat(request.getSession().getAttribute(HttpSessionRequestCache.SAVED_REQUEST)).isNotNull();\n\t\tMockHttpServletRequest requestToMatch = new MockHttpServletRequest();\n\t\trequestToMatch.setQueryString(\"success\");\n\t\trequestToMatch.setSession(request.getSession());\n\t\tHttpServletRequest matchingRequest = cache.getMatchingRequest(requestToMatch, new MockHttpServletResponse());\n\t\tassertThat(matchingRequest).isNotNull();\n\t\tassertThat(request.getSession().getAttribute(HttpSessionRequestCache.SAVED_REQUEST)).isNull();\n\t}\n\n\t// gh-13731\n\t@Test\n\tpublic void getMatchingRequestWhenMatchingRequestParameterNameSetThenDoesNotInvokeGetParameterMethods() {\n\t\tHttpSessionRequestCache cache = new HttpSessionRequestCache();\n\t\tcache.setMatchingRequestParameterName(\"success\");\n\t\tMockHttpServletRequest mockRequest = new MockHttpServletRequest();\n\t\tmockRequest.setQueryString(\"success\");\n\t\tHttpServletRequest request = spy(mockRequest);\n\t\tHttpServletRequest matchingRequest = cache.getMatchingRequest(request, new MockHttpServletResponse());\n\t\tassertThat(matchingRequest).isNull();\n\t\tverify(request, never()).getParameter(anyString());\n\t\tverify(request, never()).getParameterValues(anyString());\n\t\tverify(request, never()).getParameterNames();\n\t\tverify(request, never()).getParameterMap();\n\t}\n\n\tprivate static final class CustomSavedRequest implements SavedRequest {\n\n\t\tprivate final SavedRequest delegate;\n\n\t\tprivate CustomSavedRequest(SavedRequest delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getRedirectUrl() {\n\t\t\treturn this.delegate.getRedirectUrl();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<Cookie> getCookies() {\n\t\t\treturn this.delegate.getCookies();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getMethod() {\n\t\t\treturn this.delegate.getMethod();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<String> getHeaderValues(String name) {\n\t\t\treturn this.delegate.getHeaderValues(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<String> getHeaderNames() {\n\t\t\treturn this.delegate.getHeaderNames();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<Locale> getLocales() {\n\t\t\treturn this.delegate.getLocales();\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] getParameterValues(String name) {\n\t\t\treturn this.delegate.getParameterValues(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String[]> getParameterMap() {\n\t\t\treturn this.delegate.getParameterMap();\n\t\t}\n\n\t\tprivate static final long serialVersionUID = 2426831999233621470L;\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/savedrequest/RequestCacheAwareFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.util.Base64;\n\nimport jakarta.servlet.http.Cookie;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\npublic class RequestCacheAwareFilterTests {\n\n\t@Test\n\tpublic void doFilterWhenHttpSessionRequestCacheConfiguredThenSavedRequestRemovedAfterMatch() throws Exception {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/destination\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tRequestCache requestCache = mock(RequestCache.class);\n\t\tRequestCacheAwareFilter filter = new RequestCacheAwareFilter(requestCache);\n\t\tgiven(requestCache.getMatchingRequest(request, response)).willReturn(request);\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tverify(requestCache).getMatchingRequest(request, response);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenCookieRequestCacheConfiguredThenExpiredSavedRequestCookieSetAfterMatch() throws Exception {\n\t\tCookieRequestCache cache = new CookieRequestCache();\n\t\tRequestCacheAwareFilter filter = new RequestCacheAwareFilter(cache);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setServerName(\"abc.com\");\n\t\trequest.setRequestURI(\"/destination\");\n\t\trequest.setScheme(\"https\");\n\t\trequest.setServerPort(443);\n\t\trequest.setSecure(true);\n\t\tString encodedRedirectUrl = Base64.getEncoder().encodeToString(\"https://abc.com/destination\".getBytes());\n\t\tCookie savedRequest = new Cookie(\"REDIRECT_URI\", encodedRedirectUrl);\n\t\tsavedRequest.setMaxAge(-1);\n\t\tsavedRequest.setSecure(request.isSecure());\n\t\tsavedRequest.setPath(\"/\");\n\t\tsavedRequest.setHttpOnly(true);\n\t\trequest.setCookies(savedRequest);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tCookie expiredCookie = response.getCookie(\"REDIRECT_URI\");\n\t\tassertThat(expiredCookie).isNotNull();\n\t\tassertThat(expiredCookie.getValue()).isEmpty();\n\t\tassertThat(expiredCookie.getMaxAge()).isZero();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/savedrequest/SavedCookieTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.io.Serializable;\n\nimport jakarta.servlet.http.Cookie;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class SavedCookieTests {\n\n\tCookie cookie;\n\n\tSavedCookie savedCookie;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.cookie = new Cookie(\"name\", \"value\");\n\t\tthis.cookie.setDomain(\"domain\");\n\t\tthis.cookie.setMaxAge(100);\n\t\tthis.cookie.setPath(\"path\");\n\t\tthis.cookie.setSecure(true);\n\t\tthis.savedCookie = new SavedCookie(this.cookie);\n\t}\n\n\t@Test\n\tpublic void testGetName() {\n\t\tassertThat(this.savedCookie.getName()).isEqualTo(this.cookie.getName());\n\t}\n\n\t@Test\n\tpublic void testGetValue() {\n\t\tassertThat(this.savedCookie.getValue()).isEqualTo(this.cookie.getValue());\n\t}\n\n\t@Test\n\tpublic void testGetDomain() {\n\t\tassertThat(this.savedCookie.getDomain()).isEqualTo(this.cookie.getDomain());\n\t}\n\n\t@Test\n\tpublic void testGetMaxAge() {\n\t\tassertThat(this.savedCookie.getMaxAge()).isEqualTo(this.cookie.getMaxAge());\n\t}\n\n\t@Test\n\tpublic void testGetPath() {\n\t\tassertThat(this.savedCookie.getPath()).isEqualTo(this.cookie.getPath());\n\t}\n\n\t@Test\n\tpublic void testGetCookie() {\n\t\tCookie other = this.savedCookie.getCookie();\n\t\tassertThat(other.getDomain()).isEqualTo(this.cookie.getDomain());\n\t\tassertThat(other.getMaxAge()).isEqualTo(this.cookie.getMaxAge());\n\t\tassertThat(other.getName()).isEqualTo(this.cookie.getName());\n\t\tassertThat(other.getPath()).isEqualTo(this.cookie.getPath());\n\t\tassertThat(other.getSecure()).isEqualTo(this.cookie.getSecure());\n\t\tassertThat(other.getValue()).isEqualTo(this.cookie.getValue());\n\t}\n\n\t@Test\n\tpublic void testSerializable() {\n\t\tassertThat(this.savedCookie instanceof Serializable).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Enumeration;\nimport java.util.Locale;\n\nimport jakarta.servlet.http.Cookie;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\npublic class SavedRequestAwareWrapperTests {\n\n\tprivate SavedRequestAwareWrapper createWrapper(MockHttpServletRequest requestToSave,\n\t\t\tMockHttpServletRequest requestToWrap) {\n\t\tDefaultSavedRequest saved = new DefaultSavedRequest(requestToSave);\n\t\treturn new SavedRequestAwareWrapper(saved, requestToWrap);\n\t}\n\n\t// SEC-2569\n\t@Test\n\tpublic void savedRequestCookiesAreIgnored() {\n\t\tMockHttpServletRequest newRequest = new MockHttpServletRequest();\n\t\tnewRequest.setCookies(new Cookie(\"cookie\", \"fromnew\"));\n\t\tMockHttpServletRequest savedRequest = new MockHttpServletRequest();\n\t\tsavedRequest.setCookies(new Cookie(\"cookie\", \"fromsaved\"));\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(savedRequest, newRequest);\n\t\tassertThat(wrapper.getCookies()).hasSize(1);\n\t\tassertThat(wrapper.getCookies()[0].getValue()).isEqualTo(\"fromnew\");\n\t}\n\n\t@Test\n\tpublic void savedRequesthHeaderIsReturnedIfSavedRequestIsSet() {\n\t\tMockHttpServletRequest savedRequest = new MockHttpServletRequest();\n\t\tsavedRequest.addHeader(\"header\", \"savedheader\");\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(savedRequest, new MockHttpServletRequest());\n\t\tassertThat(wrapper.getHeader(\"nonexistent\")).isNull();\n\t\tEnumeration<String> headers = wrapper.getHeaders(\"nonexistent\");\n\t\tassertThat(headers.hasMoreElements()).isFalse();\n\t\tassertThat(wrapper.getHeader(\"Header\")).isEqualTo(\"savedheader\");\n\t\theaders = wrapper.getHeaders(\"heaDer\");\n\t\tassertThat(headers.hasMoreElements()).isTrue();\n\t\tassertThat(headers.nextElement()).isEqualTo(\"savedheader\");\n\t\tassertThat(headers.hasMoreElements()).isFalse();\n\t\tassertThat(wrapper.getHeaderNames().hasMoreElements()).isTrue();\n\t\tassertThat(wrapper.getHeaderNames().nextElement()).isEqualTo(\"header\");\n\t}\n\n\t@Test\n\t/*\n\t * SEC-830. Assume we have a request to /someUrl?action=foo (the saved request) and\n\t * then RequestDispatcher.forward() it to /someUrl?action=bar. What should action\n\t * parameter be before and during the forward?\n\t */\n\tpublic void wrappedRequestParameterTakesPrecedenceOverSavedRequest() {\n\t\tMockHttpServletRequest savedRequest = new MockHttpServletRequest();\n\t\tsavedRequest.setParameter(\"action\", \"foo\");\n\t\tMockHttpServletRequest wrappedRequest = new MockHttpServletRequest();\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(savedRequest, wrappedRequest);\n\t\tassertThat(wrapper.getParameter(\"action\")).isEqualTo(\"foo\");\n\t\t// The request after forward\n\t\twrappedRequest.setParameter(\"action\", \"bar\");\n\t\tassertThat(wrapper.getParameter(\"action\")).isEqualTo(\"bar\");\n\t\t// Both values should be set, but \"bar\" should be first\n\t\tassertThat(wrapper.getParameterValues(\"action\")).hasSize(2);\n\t\tassertThat(wrapper.getParameterValues(\"action\")[0]).isEqualTo(\"bar\");\n\t}\n\n\t@Test\n\tpublic void savedRequestDoesntCreateDuplicateParams() {\n\t\tMockHttpServletRequest savedRequest = new MockHttpServletRequest();\n\t\tsavedRequest.setParameter(\"action\", \"foo\");\n\t\tMockHttpServletRequest wrappedRequest = new MockHttpServletRequest();\n\t\twrappedRequest.setParameter(\"action\", \"foo\");\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(savedRequest, wrappedRequest);\n\t\tassertThat(wrapper.getParameterValues(\"action\")).hasSize(1);\n\t\tassertThat(wrapper.getParameterMap()).hasSize(1);\n\t\tassertThat(wrapper.getParameterMap().get(\"action\")).hasSize(1);\n\t}\n\n\t@Test\n\tpublic void savedRequestHeadersTakePrecedence() {\n\t\tMockHttpServletRequest savedRequest = new MockHttpServletRequest();\n\t\tsavedRequest.addHeader(\"Authorization\", \"foo\");\n\t\tMockHttpServletRequest wrappedRequest = new MockHttpServletRequest();\n\t\twrappedRequest.addHeader(\"Authorization\", \"bar\");\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(savedRequest, wrappedRequest);\n\t\tassertThat(wrapper.getHeader(\"Authorization\")).isEqualTo(\"foo\");\n\t}\n\n\t@Test\n\tpublic void getParameterValuesReturnsNullIfParameterIsntSet() {\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(new MockHttpServletRequest(), new MockHttpServletRequest());\n\t\tassertThat(wrapper.getParameterValues(\"action\")).isNull();\n\t\tassertThat(wrapper.getParameterMap().get(\"action\")).isNull();\n\t}\n\n\t@Test\n\tpublic void getParameterValuesReturnsCombinedSavedAndWrappedRequestValues() {\n\t\tMockHttpServletRequest savedRequest = new MockHttpServletRequest();\n\t\tsavedRequest.setParameter(\"action\", \"foo\");\n\t\tMockHttpServletRequest wrappedRequest = new MockHttpServletRequest();\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(savedRequest, wrappedRequest);\n\t\tassertThat(wrapper.getParameterValues(\"action\")).isEqualTo(new Object[] { \"foo\" });\n\t\twrappedRequest.setParameter(\"action\", \"bar\");\n\t\tassertThat(wrapper.getParameterValues(\"action\")).isEqualTo(new Object[] { \"bar\", \"foo\" });\n\t\t// Check map is consistent\n\t\tString[] valuesFromMap = wrapper.getParameterMap().get(\"action\");\n\t\tassertThat(valuesFromMap).hasSize(2);\n\t\tassertThat(valuesFromMap[0]).isEqualTo(\"bar\");\n\t}\n\n\t@Test\n\tpublic void expecteDateHeaderIsReturnedFromSavedRequest() throws Exception {\n\t\tSimpleDateFormat formatter = new SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss zzz\", Locale.US);\n\t\tString nowString = FastHttpDateFormat.getCurrentDate();\n\t\tDate now = formatter.parse(nowString);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"header\", nowString);\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(request, new MockHttpServletRequest());\n\t\tassertThat(wrapper.getDateHeader(\"header\")).isEqualTo(now.getTime());\n\t\tassertThat(wrapper.getDateHeader(\"nonexistent\")).isEqualTo(-1L);\n\t}\n\n\t@Test\n\tpublic void invalidDateHeaderIsRejected() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"header\", \"notadate\");\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(request, new MockHttpServletRequest());\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> wrapper.getDateHeader(\"header\"));\n\t}\n\n\t@Test\n\tpublic void correctHttpMethodIsReturned() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"PUT\", \"/notused\");\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(request, new MockHttpServletRequest(\"GET\", \"/notused\"));\n\t\tassertThat(wrapper.getMethod()).isEqualTo(\"PUT\");\n\t}\n\n\t@Test\n\tpublic void correctIntHeaderIsReturned() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"header\", \"999\");\n\t\trequest.addHeader(\"header\", \"1000\");\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(request, new MockHttpServletRequest());\n\t\tassertThat(wrapper.getIntHeader(\"header\")).isEqualTo(999);\n\t\tassertThat(wrapper.getIntHeader(\"nonexistent\")).isEqualTo(-1);\n\t}\n\n\t@Test\n\tpublic void correctContentTypeIsReturned() {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"PUT\", \"/notused\");\n\t\trequest.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\n\t\tSavedRequestAwareWrapper wrapper = createWrapper(request, new MockHttpServletRequest(\"GET\", \"/notused\"));\n\t\tassertThat(wrapper.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/savedrequest/SimpleSavedRequestTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.savedrequest;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport jakarta.servlet.http.Cookie;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class SimpleSavedRequestTests {\n\n\t@Test\n\tpublic void constructorWhenGivenSavedRequestThenCopies() {\n\t\tSavedRequest savedRequest = new SimpleSavedRequest(prepareSavedRequest());\n\t\tassertThat(savedRequest.getMethod()).isEqualTo(\"POST\");\n\t\tList<Cookie> cookies = savedRequest.getCookies();\n\t\tassertThat(cookies).hasSize(1);\n\t\tCookie cookie = cookies.get(0);\n\t\tassertThat(cookie.getName()).isEqualTo(\"cookiename\");\n\t\tassertThat(cookie.getValue()).isEqualTo(\"cookievalue\");\n\t\tCollection<String> headerNames = savedRequest.getHeaderNames();\n\t\tassertThat(headerNames).hasSize(1);\n\t\tString headerName = headerNames.iterator().next();\n\t\tassertThat(headerName).isEqualTo(\"headername\");\n\t\tList<String> headerValues = savedRequest.getHeaderValues(\"headername\");\n\t\tassertThat(headerValues).hasSize(1);\n\t\tString headerValue = headerValues.get(0);\n\t\tassertThat(headerValue).isEqualTo(\"headervalue\");\n\t\tList<Locale> locales = savedRequest.getLocales();\n\t\tassertThat(locales).hasSize(1);\n\t\tLocale locale = locales.get(0);\n\t\tassertThat(locale).isEqualTo(Locale.ENGLISH);\n\t\tMap<String, String[]> parameterMap = savedRequest.getParameterMap();\n\t\tassertThat(parameterMap).hasSize(1);\n\t\tString[] values = parameterMap.get(\"key\");\n\t\tassertThat(values).hasSize(1);\n\t\tassertThat(values[0]).isEqualTo(\"value\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenGivenRedirectUrlThenDefaultValues() {\n\t\tSavedRequest savedRequest = new SimpleSavedRequest(\"redirectUrl\");\n\t\tassertThat(savedRequest.getMethod()).isEqualTo(\"GET\");\n\t\tassertThat(savedRequest.getCookies()).isEmpty();\n\t\tassertThat(savedRequest.getHeaderNames()).isEmpty();\n\t\tassertThat(savedRequest.getHeaderValues(\"headername\")).isEmpty();\n\t\tassertThat(savedRequest.getLocales()).isEmpty();\n\t\tassertThat(savedRequest.getParameterMap()).isEmpty();\n\t}\n\n\tprivate SimpleSavedRequest prepareSavedRequest() {\n\t\tSimpleSavedRequest simpleSavedRequest = new SimpleSavedRequest(\"redirectUrl\");\n\t\tsimpleSavedRequest.setCookies(Collections.singletonList(new Cookie(\"cookiename\", \"cookievalue\")));\n\t\tsimpleSavedRequest.setMethod(\"POST\");\n\t\tsimpleSavedRequest.setHeaders(Collections.singletonMap(\"headername\", Collections.singletonList(\"headervalue\")));\n\t\tsimpleSavedRequest.setLocales(Collections.singletonList(Locale.ENGLISH));\n\t\tsimpleSavedRequest.setParameters(Collections.singletonMap(\"key\", new String[] { \"value\" }));\n\t\treturn simpleSavedRequest;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/DefaultServerRedirectStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.net.URI;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class DefaultServerRedirectStrategyTests {\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\tprivate URI location = URI.create(\"/login\");\n\n\tprivate DefaultServerRedirectStrategy strategy = new DefaultServerRedirectStrategy();\n\n\t@Test\n\tpublic void sendRedirectWhenLocationNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.strategy.sendRedirect(this.exchange, null));\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenExchangeNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.strategy.sendRedirect(null, this.location));\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenNoSubscribersThenNoActions() {\n\t\tthis.strategy.sendRedirect(this.exchange, this.location);\n\t\tverifyNoMoreInteractions(this.exchange);\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenNoContextPathThenStatusAndLocationSet() {\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"/\"));\n\t\tthis.strategy.sendRedirect(this.exchange, this.location).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation()).hasPath(this.location.getPath());\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenContextPathSetThenStatusAndLocationSet() {\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"/context/foo\").contextPath(\"/context\"));\n\t\tthis.strategy.sendRedirect(this.exchange, this.location).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation())\n\t\t\t.hasPath(\"/context\" + this.location.getPath());\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenContextPathSetAndAbsoluteURLThenStatusAndLocationSet() {\n\t\tthis.location = URI.create(\"https://example.com/foo/bar\");\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"/context/foo\").contextPath(\"/context\"));\n\t\tthis.strategy.sendRedirect(this.exchange, this.location).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation()).hasPath(this.location.getPath());\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenContextPathSetAndDisabledThenStatusAndLocationSet() {\n\t\tthis.strategy.setContextRelative(false);\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"/context/foo\").contextPath(\"/context\"));\n\t\tthis.strategy.sendRedirect(this.exchange, this.location).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation()).hasPath(this.location.getPath());\n\t}\n\n\t@Test\n\tpublic void sendRedirectWhenCustomStatusThenStatusSet() {\n\t\tHttpStatus status = HttpStatus.MOVED_PERMANENTLY;\n\t\tthis.strategy.setHttpStatus(status);\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"/\"));\n\t\tthis.strategy.sendRedirect(this.exchange, this.location).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(status);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation()).hasPath(this.location.getPath());\n\t}\n\n\t@Test\n\tpublic void setHttpStatusWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.strategy.setHttpStatus(null));\n\t}\n\n\tprivate static MockServerWebExchange exchange(MockServerHttpRequest.BaseBuilder<?> request) {\n\t\treturn MockServerWebExchange.from(request.build());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/DelegatingServerAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingServerAuthenticationEntryPointTests {\n\n\tprivate ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\t@Mock\n\tprivate ServerWebExchangeMatcher matcher1;\n\n\t@Mock\n\tprivate ServerWebExchangeMatcher matcher2;\n\n\t@Mock\n\tprivate ServerAuthenticationEntryPoint delegate1;\n\n\t@Mock\n\tprivate ServerAuthenticationEntryPoint delegate2;\n\n\tprivate AuthenticationException e = new AuthenticationCredentialsNotFoundException(\"Log In\");\n\n\tprivate DelegatingServerAuthenticationEntryPoint entryPoint;\n\n\t@Test\n\tpublic void commenceWhenNotMatchThenMatchThenOnlySecondDelegateInvoked() {\n\t\tMono<Void> expectedResult = Mono.empty();\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tgiven(this.matcher2.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.match());\n\t\tgiven(this.delegate2.commence(this.exchange, this.e)).willReturn(expectedResult);\n\t\tthis.entryPoint = new DelegatingServerAuthenticationEntryPoint(new DelegateEntry(this.matcher1, this.delegate1),\n\t\t\t\tnew DelegateEntry(this.matcher2, this.delegate2));\n\t\tMono<Void> actualResult = this.entryPoint.commence(this.exchange, this.e);\n\t\tactualResult.block();\n\t\tverifyNoMoreInteractions(this.delegate1);\n\t\tverify(this.delegate2).commence(this.exchange, this.e);\n\t}\n\n\t@Test\n\tpublic void commenceWhenNotMatchThenDefault() {\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tthis.entryPoint = new DelegatingServerAuthenticationEntryPoint(\n\t\t\t\tnew DelegateEntry(this.matcher1, this.delegate1));\n\t\tthis.entryPoint.commence(this.exchange, this.e).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t\tverifyNoMoreInteractions(this.delegate1);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/ExchangeMatcherRedirectWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.server.handler.FilteringWebHandler;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ExchangeMatcherRedirectWebFilter}.\n *\n * @author Evgeniy Cheban\n */\npublic class ExchangeMatcherRedirectWebFilterTests {\n\n\t@Test\n\tpublic void filterWhenRequestMatchThenRedirectToSpecifiedUrl() {\n\t\tExchangeMatcherRedirectWebFilter filter = new ExchangeMatcherRedirectWebFilter(\n\t\t\t\tnew PathPatternParserServerWebExchangeMatcher(\"/context\"), \"/test\");\n\t\tFilteringWebHandler handler = new FilteringWebHandler((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.singletonList(filter));\n\n\t\tWebTestClient client = WebTestClient.bindToWebHandler(handler).build();\n\t\tclient.get()\n\t\t\t.uri(\"/context\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isFound()\n\t\t\t.expectHeader()\n\t\t\t.valueEquals(HttpHeaders.LOCATION, \"/test\");\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestNotMatchThenNextFilter() {\n\t\tExchangeMatcherRedirectWebFilter filter = new ExchangeMatcherRedirectWebFilter(\n\t\t\t\tnew PathPatternParserServerWebExchangeMatcher(\"/context\"), \"/test\");\n\t\tFilteringWebHandler handler = new FilteringWebHandler((e) -> e.getResponse().setComplete(),\n\t\t\t\tCollections.singletonList(filter));\n\n\t\tWebTestClient client = WebTestClient.bindToWebHandler(handler).build();\n\t\tclient.get().uri(\"/test\").exchange().expectStatus().isOk();\n\t}\n\n\t@Test\n\tpublic void constructWhenExchangeMatcherNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ExchangeMatcherRedirectWebFilter(null, \"/test\"))\n\t\t\t.withMessage(\"exchangeMatcher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructWhenRedirectUrlNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new ExchangeMatcherRedirectWebFilter(new PathPatternParserServerWebExchangeMatcher(\"/**\"), null))\n\t\t\t.withMessage(\"redirectUrl cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructWhenRedirectUrlEmpty() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new ExchangeMatcherRedirectWebFilter(new PathPatternParserServerWebExchangeMatcher(\"/**\"), \"\"))\n\t\t\t.withMessage(\"redirectUrl cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void constructWhenRedirectUrlBlank() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new ExchangeMatcherRedirectWebFilter(new PathPatternParserServerWebExchangeMatcher(\"/**\"), \" \"))\n\t\t\t.withMessage(\"redirectUrl cannot be empty\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/FormPostServerRedirectStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.net.URI;\n\nimport org.assertj.core.api.ThrowingConsumer;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpResponse;\nimport org.springframework.mock.web.server.MockServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link FormPostServerRedirectStrategy}.\n *\n * @author Max Batischev\n */\npublic class FormPostServerRedirectStrategyTests {\n\n\tprivate static final String POLICY_DIRECTIVE_PATTERN = \"script-src 'nonce-(.+)'\";\n\n\tprivate final ServerRedirectStrategy redirectStrategy = new FormPostServerRedirectStrategy();\n\n\tprivate final MockServerHttpRequest request = MockServerHttpRequest.get(\"https://localhost\").build();\n\n\tprivate final MockServerWebExchange webExchange = MockServerWebExchange.from(this.request);\n\n\t@Test\n\tpublic void redirectWhetLocationAbsoluteUriIsPresentThenRedirect() {\n\t\tthis.redirectStrategy.sendRedirect(this.webExchange, URI.create(\"https://example.com\")).block();\n\n\t\tMockServerHttpResponse response = this.webExchange.getResponse();\n\t\tassertThat(response.getBodyAsString().block()).contains(\"action=\\\"https://example.com\\\"\");\n\t\tassertThat(this.webExchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);\n\t\tassertThat(this.webExchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_HTML);\n\t\tassertThat(this.webExchange.getResponse()).satisfies(hasScriptSrcNonce());\n\t}\n\n\t@Test\n\tpublic void redirectWhetLocationRootRelativeUriIsPresentThenRedirect() {\n\t\tthis.redirectStrategy.sendRedirect(this.webExchange, URI.create(\"/test\")).block();\n\n\t\tMockServerHttpResponse response = this.webExchange.getResponse();\n\t\tassertThat(response.getBodyAsString().block()).contains(\"action=\\\"/test\\\"\");\n\t\tassertThat(this.webExchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);\n\t\tassertThat(this.webExchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_HTML);\n\t\tassertThat(this.webExchange.getResponse()).satisfies(hasScriptSrcNonce());\n\t}\n\n\t@Test\n\tpublic void redirectWhetLocationRelativeUriIsPresentThenRedirect() {\n\t\tthis.redirectStrategy.sendRedirect(this.webExchange, URI.create(\"test\")).block();\n\n\t\tMockServerHttpResponse response = this.webExchange.getResponse();\n\t\tassertThat(response.getBodyAsString().block()).contains(\"action=\\\"test\\\"\");\n\t\tassertThat(this.webExchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);\n\t\tassertThat(this.webExchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_HTML);\n\t\tassertThat(this.webExchange.getResponse()).satisfies(hasScriptSrcNonce());\n\t}\n\n\t@Test\n\tpublic void redirectWhenLocationAbsoluteUriWithFragmentIsPresentThenRedirect() {\n\t\tthis.redirectStrategy.sendRedirect(this.webExchange, URI.create(\"https://example.com/path#fragment\")).block();\n\n\t\tMockServerHttpResponse response = this.webExchange.getResponse();\n\t\tassertThat(response.getBodyAsString().block()).contains(\"action=\\\"https://example.com/path#fragment\\\"\");\n\t\tassertThat(this.webExchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);\n\t\tassertThat(this.webExchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_HTML);\n\t\tassertThat(this.webExchange.getResponse()).satisfies(hasScriptSrcNonce());\n\t}\n\n\t@Test\n\tpublic void redirectWhenLocationAbsoluteUriWithQueryParamsIsPresentThenRedirect() {\n\t\tthis.redirectStrategy\n\t\t\t.sendRedirect(this.webExchange, URI.create(\"https://example.com/path?param1=one&param2=two#fragment\"))\n\t\t\t.block();\n\n\t\tMockServerHttpResponse response = this.webExchange.getResponse();\n\t\tString content = response.getBodyAsString().block();\n\t\tassertThat(this.webExchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);\n\t\tassertThat(this.webExchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.TEXT_HTML);\n\t\tassertThat(content).contains(\"action=\\\"https://example.com/path#fragment\\\"\");\n\t\tassertThat(content).contains(\"<input name=\\\"param1\\\" type=\\\"hidden\\\" value=\\\"one\\\" />\");\n\t\tassertThat(content).contains(\"<input name=\\\"param2\\\" type=\\\"hidden\\\" value=\\\"two\\\" />\");\n\t}\n\n\tprivate ThrowingConsumer<MockServerHttpResponse> hasScriptSrcNonce() {\n\t\treturn (response) -> {\n\t\t\tfinal String policyDirective = response.getHeaders().getFirst(\"Content-Security-Policy\");\n\t\t\tassertThat(policyDirective).isNotEmpty();\n\t\t\tassertThat(policyDirective).matches(POLICY_DIRECTIVE_PATTERN);\n\n\t\t\tfinal String nonce = policyDirective.replaceFirst(POLICY_DIRECTIVE_PATTERN, \"$1\");\n\t\t\tassertThat(response.getBodyAsString().block()).contains(\"<script nonce=\\\"%s\\\">\".formatted(nonce));\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/ObservationWebFilterChainDecoratorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link ObservationWebFilterChainDecorator}\n */\npublic class ObservationWebFilterChainDecoratorTests {\n\n\t@Test\n\tvoid decorateWhenDefaultsThenObserves() {\n\t\tObservationHandler<?> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);\n\t\tWebFilterChain chain = mock(WebFilterChain.class);\n\t\tgiven(chain.filter(any())).willReturn(Mono.empty());\n\t\tWebFilterChain decorated = decorator.decorate(chain);\n\t\tdecorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build())).block();\n\t\tverify(handler).onStart(any());\n\t}\n\n\t@Test\n\tvoid decorateWhenNoopThenDoesNotObserve() {\n\t\tObservationHandler<?> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.NOOP;\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);\n\t\tWebFilterChain chain = mock(WebFilterChain.class);\n\t\tgiven(chain.filter(any())).willReturn(Mono.empty());\n\t\tWebFilterChain decorated = decorator.decorate(chain);\n\t\tdecorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build())).block();\n\t\tverifyNoInteractions(handler);\n\t}\n\n\t@Test\n\tvoid decorateWhenTerminatingFilterThenObserves() {\n\t\tAccumulatingObservationHandler handler = new AccumulatingObservationHandler();\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);\n\t\tWebFilterChain chain = mock(WebFilterChain.class);\n\t\tgiven(chain.filter(any())).willReturn(Mono.error(() -> new Exception(\"ack\")));\n\t\tWebFilterChain decorated = decorator.decorate(chain,\n\t\t\t\tList.of(new BasicAuthenticationFilter(), new TerminatingFilter()));\n\t\tObservation http = Observation.start(\"http\", registry).contextualName(\"http\");\n\t\ttry {\n\t\t\tdecorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build()))\n\t\t\t\t.contextWrite((context) -> context.put(ObservationThreadLocalAccessor.KEY, http))\n\t\t\t\t.block();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\thttp.error(ex);\n\t\t}\n\t\tfinally {\n\t\t\thttp.stop();\n\t\t}\n\t\thandler.assertSpanStart(0, \"http\", null);\n\t\thandler.assertSpanStart(1, \"spring.security.filterchains\", \"http\");\n\t\thandler.assertSpanStop(2, \"security filterchain before\");\n\t\thandler.assertSpanStart(3, \"spring.security.filterchains\", \"http\");\n\t\thandler.assertSpanStop(4, \"security filterchain after\");\n\t\thandler.assertSpanStop(5, \"http\");\n\t}\n\n\t@Test\n\tvoid decorateWhenFilterErrorThenStopsObservation() {\n\t\tAccumulatingObservationHandler handler = new AccumulatingObservationHandler();\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);\n\t\tWebFilterChain chain = mock(WebFilterChain.class);\n\t\tWebFilterChain decorated = decorator.decorate(chain, List.of(new ErroringFilter()));\n\t\tObservation http = Observation.start(\"http\", registry).contextualName(\"http\");\n\t\ttry {\n\t\t\tdecorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build()))\n\t\t\t\t.contextWrite((context) -> context.put(ObservationThreadLocalAccessor.KEY, http))\n\t\t\t\t.block();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\thttp.error(ex);\n\t\t}\n\t\tfinally {\n\t\t\thttp.stop();\n\t\t}\n\t\thandler.assertSpanStart(0, \"http\", null);\n\t\thandler.assertSpanStart(1, \"spring.security.filterchains\", \"http\");\n\t\thandler.assertSpanError(2);\n\t\thandler.assertSpanStop(3, \"security filterchain before\");\n\t\thandler.assertSpanError(4);\n\t\thandler.assertSpanStop(5, \"http\");\n\t}\n\n\t@Test\n\tvoid decorateWhenErrorSignalThenStopsObservation() {\n\t\tAccumulatingObservationHandler handler = new AccumulatingObservationHandler();\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);\n\t\tWebFilterChain chain = mock(WebFilterChain.class);\n\t\tgiven(chain.filter(any())).willReturn(Mono.error(() -> new Exception(\"ack\")));\n\t\tWebFilterChain decorated = decorator.decorate(chain, List.of(new BasicAuthenticationFilter()));\n\t\tObservation http = Observation.start(\"http\", registry).contextualName(\"http\");\n\t\ttry {\n\t\t\tdecorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build()))\n\t\t\t\t.contextWrite((context) -> context.put(ObservationThreadLocalAccessor.KEY, http))\n\t\t\t\t.block();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\thttp.error(ex);\n\t\t}\n\t\tfinally {\n\t\t\thttp.stop();\n\t\t}\n\t\thandler.assertSpanStart(0, \"http\", null);\n\t\thandler.assertSpanStart(1, \"spring.security.filterchains\", \"http\");\n\t\thandler.assertSpanStop(2, \"security filterchain before\");\n\t\thandler.assertSpanStart(3, \"secured request\", \"security filterchain before\");\n\t\thandler.assertSpanError(4);\n\t\thandler.assertSpanStop(5, \"secured request\");\n\t\thandler.assertSpanStart(6, \"spring.security.filterchains\", \"http\");\n\t\thandler.assertSpanError(7);\n\t\thandler.assertSpanStop(8, \"security filterchain after\");\n\t\thandler.assertSpanError(9);\n\t\thandler.assertSpanStop(10, \"http\");\n\t}\n\n\t// gh-12849\n\t@Test\n\tvoid decorateWhenCustomAfterFilterThenObserves() {\n\t\tAccumulatingObservationHandler handler = new AccumulatingObservationHandler();\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);\n\t\tWebFilter mock = mock(WebFilter.class);\n\t\tgiven(mock.filter(any(), any())).willReturn(Mono.empty());\n\t\tWebFilterChain chain = mock(WebFilterChain.class);\n\t\tgiven(chain.filter(any())).willReturn(Mono.empty());\n\t\tWebFilterChain decorated = decorator.decorate(chain,\n\t\t\t\tList.of((e, c) -> c.filter(e).then(Mono.deferContextual((context) -> {\n\t\t\t\t\tObservation parentObservation = context.getOrDefault(ObservationThreadLocalAccessor.KEY, null);\n\t\t\t\t\tObservation observation = Observation.createNotStarted(\"custom\", registry)\n\t\t\t\t\t\t.parentObservation(parentObservation)\n\t\t\t\t\t\t.contextualName(\"custom\")\n\t\t\t\t\t\t.start();\n\t\t\t\t\treturn Mono.just(\"3\")\n\t\t\t\t\t\t.doOnSuccess((v) -> observation.stop())\n\t\t\t\t\t\t.doOnCancel(observation::stop)\n\t\t\t\t\t\t.doOnError((t) -> {\n\t\t\t\t\t\t\tobservation.error(t);\n\t\t\t\t\t\t\tobservation.stop();\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then(Mono.empty());\n\t\t\t\t}))));\n\t\tObservation http = Observation.start(\"http\", registry).contextualName(\"http\");\n\t\ttry {\n\t\t\tdecorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build()))\n\t\t\t\t.contextWrite((context) -> context.put(ObservationThreadLocalAccessor.KEY, http))\n\t\t\t\t.block();\n\t\t}\n\t\tfinally {\n\t\t\thttp.stop();\n\t\t}\n\t\thandler.assertSpanStart(0, \"http\", null);\n\t\thandler.assertSpanStart(1, \"spring.security.filterchains\", \"http\");\n\t\thandler.assertSpanStop(2, \"security filterchain before\");\n\t\thandler.assertSpanStart(3, \"secured request\", \"security filterchain before\");\n\t\thandler.assertSpanStop(4, \"secured request\");\n\t\thandler.assertSpanStart(5, \"spring.security.filterchains\", \"http\");\n\t\thandler.assertSpanStart(6, \"custom\", \"spring.security.filterchains\");\n\t\thandler.assertSpanStop(7, \"custom\");\n\t\thandler.assertSpanStop(8, \"security filterchain after\");\n\t\thandler.assertSpanStop(9, \"http\");\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"decorateFiltersWhenCompletesThenHasSpringSecurityReachedFilterNameTagArguments\")\n\tvoid decorateFiltersWhenCompletesThenHasSpringSecurityReachedFilterNameTag(WebFilter filter,\n\t\t\tString expectedFilterNameTag) {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);\n\t\tWebFilterChain chain = mock(WebFilterChain.class);\n\t\tgiven(chain.filter(any())).willReturn(Mono.empty());\n\t\tWebFilterChain decorated = decorator.decorate(chain, List.of(filter));\n\t\tdecorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build())).block();\n\n\t\tArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(3)).onStop(context.capture());\n\n\t\tassertThat(context.getValue().getLowCardinalityKeyValue(\"spring.security.reached.filter.name\").getValue())\n\t\t\t.isEqualTo(expectedFilterNameTag);\n\t}\n\n\tstatic Stream<Arguments> decorateFiltersWhenCompletesThenHasSpringSecurityReachedFilterNameTagArguments() {\n\t\tWebFilter filterWithName = new BasicAuthenticationFilter();\n\n\t\t// Anonymous class leads to an empty filter-name\n\t\tWebFilter filterWithoutName = new WebFilter() {\n\t\t\t@Override\n\t\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\t\treturn chain.filter(exchange);\n\t\t\t}\n\t\t};\n\n\t\treturn Stream.of(Arguments.of(filterWithName, \"BasicAuthenticationFilter\"),\n\t\t\t\tArguments.of(filterWithoutName, \"none\"));\n\t}\n\n\tstatic class BasicAuthenticationFilter implements WebFilter {\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn chain.filter(exchange);\n\t\t}\n\n\t}\n\n\tstatic class ErroringFilter implements WebFilter {\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn Mono.error(() -> new RuntimeException(\"ack\"));\n\t\t}\n\n\t}\n\n\tstatic class TerminatingFilter implements WebFilter {\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn Mono.empty();\n\t\t}\n\n\t}\n\n\tstatic class AccumulatingObservationHandler implements ObservationHandler<Observation.Context> {\n\n\t\tList<Event> contexts = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic boolean supportsContext(Observation.Context context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onStart(Observation.Context context) {\n\t\t\tthis.contexts.add(new Event(\"start\", context));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onError(Observation.Context context) {\n\t\t\tthis.contexts.add(new Event(\"error\", context));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEvent(Observation.Event event, Observation.Context context) {\n\t\t\tthis.contexts.add(new Event(\"event\", context));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onScopeOpened(Observation.Context context) {\n\t\t\tthis.contexts.add(new Event(\"opened\", context));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onScopeClosed(Observation.Context context) {\n\t\t\tthis.contexts.add(new Event(\"closed\", context));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onScopeReset(Observation.Context context) {\n\t\t\tthis.contexts.add(new Event(\"reset\", context));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onStop(Observation.Context context) {\n\t\t\tthis.contexts.add(new Event(\"stop\", context));\n\t\t}\n\n\t\tprivate void assertSpanStart(int index, String name, String parentName) {\n\t\t\tEvent event = this.contexts.get(index);\n\t\t\tassertThat(event.event).isEqualTo(\"start\");\n\t\t\tif (event.contextualName == null) {\n\t\t\t\tassertThat(event.name).isEqualTo(name);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tassertThat(event.contextualName).isEqualTo(name);\n\t\t\t}\n\t\t\tif (parentName == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (event.parentContextualName == null) {\n\t\t\t\tassertThat(event.parentName).isEqualTo(parentName);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tassertThat(event.parentContextualName).isEqualTo(parentName);\n\t\t\t}\n\t\t}\n\n\t\tprivate void assertSpanStop(int index, String name) {\n\t\t\tEvent event = this.contexts.get(index);\n\t\t\tassertThat(event.event).isEqualTo(\"stop\");\n\t\t\tif (event.contextualName == null) {\n\t\t\t\tassertThat(event.name).isEqualTo(name);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tassertThat(event.contextualName).isEqualTo(name);\n\t\t\t}\n\t\t}\n\n\t\tprivate void assertSpanError(int index) {\n\t\t\tEvent event = this.contexts.get(index);\n\t\t\tassertThat(event.event).isEqualTo(\"error\");\n\t\t}\n\n\t\tstatic class Event {\n\n\t\t\tString event;\n\n\t\t\tString name;\n\n\t\t\tString contextualName;\n\n\t\t\tString parentName;\n\n\t\t\tString parentContextualName;\n\n\t\t\tEvent(String event, Observation.Context context) {\n\t\t\t\tthis.event = event;\n\t\t\t\tthis.name = context.getName();\n\t\t\t\tthis.contextualName = context.getContextualName();\n\t\t\t\tif (context.getParentObservation() != null) {\n\t\t\t\t\tthis.parentName = context.getParentObservation().getContextView().getName();\n\t\t\t\t\tthis.parentContextualName = context.getParentObservation().getContextView().getContextualName();\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/WebFilterChainProxyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport io.micrometer.observation.Observation;\nimport io.micrometer.observation.ObservationHandler;\nimport io.micrometer.observation.ObservationRegistry;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.ObservationWebFilterChainDecorator.WebFilterChainObservationContext;\nimport org.springframework.security.web.server.ObservationWebFilterChainDecorator.WebFilterChainObservationConvention;\nimport org.springframework.security.web.server.ObservationWebFilterChainDecorator.WebFilterObservation;\nimport org.springframework.security.web.server.firewall.ServerExchangeRejectedException;\nimport org.springframework.security.web.server.firewall.ServerExchangeRejectedHandler;\nimport org.springframework.security.web.server.firewall.ServerWebExchangeFirewall;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebFilterChainProxyTests {\n\n\t// gh-4668\n\t@Test\n\tpublic void filterWhenNoMatchThenContinuesChainAnd404() {\n\t\tList<WebFilter> filters = Arrays.asList(new Http200WebFilter());\n\t\tServerWebExchangeMatcher notMatch = (exchange) -> MatchResult.notMatch();\n\t\tMatcherSecurityWebFilterChain chain = new MatcherSecurityWebFilterChain(notMatch, filters);\n\t\tWebFilterChainProxy filter = new WebFilterChainProxy(chain);\n\t\tWebTestClient.bindToController(new Object())\n\t\t\t.webFilter(filter)\n\t\t\t.build()\n\t\t\t.get()\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isNotFound();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMatchesThenObservationRegistryObserves() {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tList<WebFilter> filters = Arrays.asList(new PassthroughWebFilter());\n\t\tServerWebExchangeMatcher match = (exchange) -> MatchResult.match();\n\t\tMatcherSecurityWebFilterChain chain = new MatcherSecurityWebFilterChain(match, filters);\n\t\tWebFilterChainProxy fcp = new WebFilterChainProxy(chain);\n\t\tfcp.setFilterChainDecorator(new ObservationWebFilterChainDecorator(registry));\n\t\tWebFilter filter = WebFilterObservation.create(Observation.createNotStarted(\"wrap\", registry)).wrap(fcp);\n\t\tWebFilterChain mockChain = mock(WebFilterChain.class);\n\t\tgiven(mockChain.filter(any())).willReturn(Mono.empty());\n\t\tfilter.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\")), mockChain).block();\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(4)).onStart(captor.capture());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertFilterChainObservation(contexts.next(), \"before\", 1);\n\t\tassertThat(contexts.next().getName()).isEqualTo(ObservationWebFilterChainDecorator.SECURED_OBSERVATION_NAME);\n\t\tassertFilterChainObservation(contexts.next(), \"after\", 1);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenMismatchesThenObservationRegistryObserves() {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tList<WebFilter> filters = Arrays.asList(new PassthroughWebFilter());\n\t\tServerWebExchangeMatcher notMatch = (exchange) -> MatchResult.notMatch();\n\t\tMatcherSecurityWebFilterChain chain = new MatcherSecurityWebFilterChain(notMatch, filters);\n\t\tWebFilterChainProxy fcp = new WebFilterChainProxy(chain);\n\t\tfcp.setFilterChainDecorator(new ObservationWebFilterChainDecorator(registry));\n\t\tWebFilter filter = WebFilterObservation.create(Observation.createNotStarted(\"wrap\", registry)).wrap(fcp);\n\t\tWebFilterChain mockChain = mock(WebFilterChain.class);\n\t\tgiven(mockChain.filter(any())).willReturn(Mono.empty());\n\t\tfilter.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\")), mockChain).block();\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(2)).onStart(captor.capture());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertThat(contexts.next().getName()).isEqualTo(ObservationWebFilterChainDecorator.UNSECURED_OBSERVATION_NAME);\n\t}\n\n\t@Test\n\tpublic void doFilterWhenFilterExceptionThenObservationRegistryObserves() {\n\t\tObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);\n\t\tgiven(handler.supportsContext(any())).willReturn(true);\n\t\tObservationRegistry registry = ObservationRegistry.create();\n\t\tregistry.observationConfig().observationHandler(handler);\n\t\tWebFilter error = mock(WebFilter.class);\n\t\tgiven(error.filter(any(), any())).willReturn(Mono.error(new IllegalStateException()));\n\t\tList<WebFilter> filters = Arrays.asList(error);\n\t\tServerWebExchangeMatcher match = (exchange) -> MatchResult.match();\n\t\tMatcherSecurityWebFilterChain chain = new MatcherSecurityWebFilterChain(match, filters);\n\t\tWebFilterChainProxy fcp = new WebFilterChainProxy(chain);\n\t\tfcp.setFilterChainDecorator(new ObservationWebFilterChainDecorator(registry));\n\t\tWebFilter filter = WebFilterObservation.create(Observation.createNotStarted(\"wrap\", registry)).wrap(fcp);\n\t\tWebFilterChain mockChain = mock(WebFilterChain.class);\n\t\tgiven(mockChain.filter(any())).willReturn(Mono.empty());\n\t\tassertThatExceptionOfType(IllegalStateException.class).isThrownBy(\n\t\t\t\t() -> filter.filter(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\")), mockChain).block());\n\t\tArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);\n\t\tverify(handler, times(2)).onStart(captor.capture());\n\t\tverify(handler, atLeastOnce()).onError(any());\n\t\tIterator<Observation.Context> contexts = captor.getAllValues().iterator();\n\t\tassertThat(contexts.next().getName()).isEqualTo(\"wrap\");\n\t\tassertFilterChainObservation(contexts.next(), \"before\", 1);\n\t}\n\n\t@Test\n\tvoid doFilterWhenFirewallThenBadRequest() {\n\t\tList<WebFilter> filters = Arrays.asList(new Http200WebFilter());\n\t\tServerWebExchangeMatcher notMatch = (exchange) -> MatchResult.notMatch();\n\t\tMatcherSecurityWebFilterChain chain = new MatcherSecurityWebFilterChain(notMatch, filters);\n\t\tWebFilterChainProxy filter = new WebFilterChainProxy(chain);\n\t\tWebTestClient.bindToController(new Object())\n\t\t\t.webFilter(filter)\n\t\t\t.build()\n\t\t\t.method(HttpMethod.valueOf(\"INVALID\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isBadRequest();\n\t}\n\n\t@Test\n\tvoid doFilterWhenCustomFirewallThenInvoked() {\n\t\tList<WebFilter> filters = Arrays.asList(new Http200WebFilter());\n\t\tServerWebExchangeMatcher notMatch = (exchange) -> MatchResult.notMatch();\n\t\tMatcherSecurityWebFilterChain chain = new MatcherSecurityWebFilterChain(notMatch, filters);\n\t\tWebFilterChainProxy filter = new WebFilterChainProxy(chain);\n\t\tServerExchangeRejectedHandler handler = mock(ServerExchangeRejectedHandler.class);\n\t\tServerWebExchangeFirewall firewall = mock(ServerWebExchangeFirewall.class);\n\t\tfilter.setFirewall(firewall);\n\t\tfilter.setExchangeRejectedHandler(handler);\n\t\tWebTestClient.bindToController(new Object()).webFilter(filter).build().get().exchange();\n\t\tverify(firewall).getFirewalledExchange(any());\n\t\tverifyNoInteractions(handler);\n\t}\n\n\t@Test\n\tvoid doFilterWhenCustomExchangeRejectedHandlerThenInvoked() {\n\t\tList<WebFilter> filters = Arrays.asList(new Http200WebFilter());\n\t\tServerWebExchangeMatcher notMatch = (exchange) -> MatchResult.notMatch();\n\t\tMatcherSecurityWebFilterChain chain = new MatcherSecurityWebFilterChain(notMatch, filters);\n\t\tWebFilterChainProxy filter = new WebFilterChainProxy(chain);\n\t\tServerExchangeRejectedHandler handler = mock(ServerExchangeRejectedHandler.class);\n\t\tServerWebExchangeFirewall firewall = mock(ServerWebExchangeFirewall.class);\n\t\tgiven(firewall.getFirewalledExchange(any()))\n\t\t\t.willReturn(Mono.error(new ServerExchangeRejectedException(\"Oops\")));\n\t\tfilter.setFirewall(firewall);\n\t\tfilter.setExchangeRejectedHandler(handler);\n\t\tWebTestClient.bindToController(new Object()).webFilter(filter).build().get().exchange();\n\t\tverify(firewall).getFirewalledExchange(any());\n\t\tverify(handler).handle(any(), any());\n\t}\n\n\t@Test\n\tvoid doFilterWhenDelayedServerExchangeRejectedException() {\n\t\tList<WebFilter> filters = Arrays.asList(new WebFilter() {\n\t\t\t@Override\n\t\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\t\t// simulate a delayed error (e.g. reading parameters)\n\t\t\t\treturn Mono.error(new ServerExchangeRejectedException(\"Ooops\"));\n\t\t\t}\n\t\t});\n\t\tServerWebExchangeMatcher match = (exchange) -> MatchResult.match();\n\t\tMatcherSecurityWebFilterChain chain = new MatcherSecurityWebFilterChain(match, filters);\n\t\tWebFilterChainProxy filter = new WebFilterChainProxy(chain);\n\t\tServerExchangeRejectedHandler handler = mock(ServerExchangeRejectedHandler.class);\n\t\tfilter.setExchangeRejectedHandler(handler);\n\t\t// @formatter:off\n\t\tWebTestClient.bindToController(new Object())\n\t\t\t.webFilter(filter)\n\t\t\t.build()\n\t\t\t.get()\n\t\t\t.exchange();\n\t\t// @formatter:on\n\t\tverify(handler).handle(any(), any());\n\t}\n\n\tstatic void assertFilterChainObservation(Observation.Context context, String filterSection, int chainPosition) {\n\t\tassertThat(context).isInstanceOf(WebFilterChainObservationContext.class);\n\t\tWebFilterChainObservationContext filterChainObservationContext = (WebFilterChainObservationContext) context;\n\t\tassertThat(context.getName()).isEqualTo(WebFilterChainObservationConvention.CHAIN_OBSERVATION_NAME);\n\t\tassertThat(context.getContextualName()).endsWith(filterSection);\n\t\tassertThat(filterChainObservationContext.getChainPosition()).isEqualTo(chainPosition);\n\t}\n\n\tstatic class Http200WebFilter implements WebFilter {\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn Mono.fromRunnable(() -> exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN));\n\t\t}\n\n\t}\n\n\tstatic class PassthroughWebFilter implements WebFilter {\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn chain.filter(exchange);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/WebFilterExchangeTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class WebFilterExchangeTests {\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\t@Mock\n\tprivate WebFilterChain chain;\n\n\t@Test\n\tpublic void constructorServerWebExchangeWebFilterChainWhenExchangeNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new WebFilterExchange(null, this.chain));\n\t}\n\n\t@Test\n\tpublic void constructorServerWebExchangeWebFilterChainWhenChainNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new WebFilterExchange(this.exchange, null));\n\t}\n\n\t@Test\n\tpublic void getExchange() {\n\t\tWebFilterExchange filterExchange = new WebFilterExchange(this.exchange, this.chain);\n\t\tassertThat(filterExchange.getExchange()).isEqualTo(this.exchange);\n\t}\n\n\t@Test\n\tpublic void getChain() {\n\t\tWebFilterExchange filterExchange = new WebFilterExchange(this.exchange, this.chain);\n\t\tassertThat(filterExchange.getChain()).isEqualTo(this.chain);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/AnonymousAuthenticationWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.UUID;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * @author Ankur Pathak\n * @since 5.2.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class AnonymousAuthenticationWebFilterTests {\n\n\t@Test\n\tpublic void anonymousAuthenticationFilterWorking() {\n\t\tWebTestClient client = WebTestClientBuilder\n\t\t\t.bindToControllerAndWebFilters(HttpMeController.class,\n\t\t\t\t\tnew AnonymousAuthenticationWebFilter(UUID.randomUUID().toString()))\n\t\t\t.build();\n\t\tclient.get().uri(\"/me\").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo(\"anonymousUser\");\n\t}\n\n\t@RestController\n\t@RequestMapping(\"/me\")\n\tpublic static class HttpMeController {\n\n\t\t@GetMapping\n\t\tpublic Mono<String> me(ServerWebExchange exchange) {\n\t\t\treturn ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.map(Authentication::getPrincipal)\n\t\t\t\t.ofType(String.class);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/AuthenticationConverterServerWebExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNullPointerException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author David Kovac\n * @since 5.4\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthenticationConverterServerWebExchangeMatcherTests {\n\n\tprivate MockServerWebExchange exchange;\n\n\tprivate AuthenticationConverterServerWebExchangeMatcher matcher;\n\n\tprivate Authentication authentication = new TestingAuthenticationToken(\"user\", \"password\");\n\n\t@Mock\n\tprivate ServerAuthenticationConverter converter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/path\").build();\n\t\tthis.exchange = MockServerWebExchange.from(request);\n\t\tthis.matcher = new AuthenticationConverterServerWebExchangeMatcher(this.converter);\n\t}\n\n\t@Test\n\tpublic void constructorConverterWhenConverterNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AuthenticationConverterServerWebExchangeMatcher(null));\n\t}\n\n\t@Test\n\tpublic void matchesWhenNotEmptyThenReturnTrue() {\n\t\tgiven(this.converter.convert(any())).willReturn(Mono.just(this.authentication));\n\t\tassertThat(this.matcher.matches(this.exchange).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenEmptyThenReturnFalse() {\n\t\tgiven(this.converter.convert(any())).willReturn(Mono.empty());\n\t\tassertThat(this.matcher.matches(this.exchange).block().isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenErrorThenReturnFalse() {\n\t\tgiven(this.converter.convert(any())).willReturn(Mono.error(new RuntimeException()));\n\t\tassertThat(this.matcher.matches(this.exchange).block().isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenNullThenThrowsException() {\n\t\tgiven(this.converter.convert(any())).willReturn(null);\n\t\tassertThatNullPointerException().isThrownBy(() -> this.matcher.matches(this.exchange).block());\n\t}\n\n\t@Test\n\tpublic void matchesWhenExceptionThenPropagates() {\n\t\tgiven(this.converter.convert(any())).willThrow(RuntimeException.class);\n\t\tassertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.matcher.matches(this.exchange).block());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/AuthenticationWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.web.authentication.DefaultEqualsGrantedAuthority;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.test.web.reactive.server.EntityExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @author Rafiullah Hamedy\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthenticationWebFilterTests {\n\n\t@Mock\n\tprivate ServerAuthenticationSuccessHandler successHandler;\n\n\t@Mock\n\tprivate ServerAuthenticationConverter authenticationConverter;\n\n\t@Mock\n\tprivate ReactiveAuthenticationManager authenticationManager;\n\n\t@Mock\n\tprivate ServerAuthenticationFailureHandler failureHandler;\n\n\t@Mock\n\tprivate ServerSecurityContextRepository securityContextRepository;\n\n\t@Mock\n\tprivate ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver;\n\n\tprivate AuthenticationWebFilter filter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.filter = new AuthenticationWebFilter(this.authenticationManager);\n\t\tthis.filter.setAuthenticationSuccessHandler(this.successHandler);\n\t\tthis.filter.setServerAuthenticationConverter(this.authenticationConverter);\n\t\tthis.filter.setSecurityContextRepository(this.securityContextRepository);\n\t\tthis.filter.setAuthenticationFailureHandler(this.failureHandler);\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultsAndNoAuthenticationThenContinues() {\n\t\tthis.filter = new AuthenticationWebFilter(this.authenticationManager);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t.returnResult();\n\t\tverifyNoMoreInteractions(this.authenticationManager);\n\t\tassertThat(result.getResponseCookies()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticationManagerResolverDefaultsAndNoAuthenticationThenContinues() {\n\t\tthis.filter = new AuthenticationWebFilter(this.authenticationManagerResolver);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t.returnResult();\n\t\tverifyNoMoreInteractions(this.authenticationManagerResolver);\n\t\tassertThat(result.getResponseCookies()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultsAndAuthenticationSuccessThenContinues() {\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"test\", \"this\", \"ROLE\")));\n\t\tthis.filter = new AuthenticationWebFilter(this.authenticationManager);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"test\", \"this\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t.returnResult();\n\t\tassertThat(result.getResponseCookies()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticationManagerResolverDefaultsAndAuthenticationSuccessThenContinues() {\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.just(new TestingAuthenticationToken(\"test\", \"this\", \"ROLE\")));\n\t\tgiven(this.authenticationManagerResolver.resolve(any())).willReturn(Mono.just(this.authenticationManager));\n\t\tthis.filter = new AuthenticationWebFilter(this.authenticationManagerResolver);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"test\", \"this\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t.returnResult();\n\t\tassertThat(result.getResponseCookies()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultsAndAuthenticationFailThenUnauthorized() {\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.error(new BadCredentialsException(\"failed\")));\n\t\tthis.filter = new AuthenticationWebFilter(this.authenticationManager);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tEntityExchangeResult<Void> result = client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"test\", \"this\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isUnauthorized()\n\t\t\t.expectHeader()\n\t\t\t.valueMatches(\"WWW-Authenticate\", \"Basic realm=\\\"Realm\\\"\")\n\t\t\t.expectBody()\n\t\t\t.isEmpty();\n\t\tassertThat(result.getResponseCookies()).isEmpty();\n\t}\n\n\t/**\n\t * This is critical to avoid adding duplicate GrantedAuthority instances with the\n\t * same' authority when the issuedAt is too old and a new instance is requested.\n\t * @throws Exception\n\t */\n\t@Test\n\tpublic void filterWhenDefaultEqualsAuthorityThenNoDuplicates() {\n\t\tTestingAuthenticationToken existingAuthn = new TestingAuthenticationToken(\"username\", \"password\",\n\t\t\t\tnew DefaultEqualsGrantedAuthority());\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(\n\t\t\t\tMono.just(new TestingAuthenticationToken(\"user\", \"password\", new DefaultEqualsGrantedAuthority())));\n\t\tgiven(this.securityContextRepository.save(any(), any())).willReturn(Mono.empty());\n\t\tthis.filter = new AuthenticationWebFilter(this.authenticationManager);\n\t\tthis.filter.setSecurityContextRepository(this.securityContextRepository);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(new RunAsWebFilter(existingAuthn), this.filter)\n\t\t\t.build();\n\t\tclient.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"test\", \"this\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk();\n\t\tArgumentCaptor<SecurityContext> context = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(this.securityContextRepository).save(any(), context.capture());\n\t\tAuthentication authentication = context.getValue().getAuthentication();\n\t\tassertThat(authentication.getAuthorities()).extracting(GrantedAuthority::getAuthority)\n\t\t\t.containsExactly(DefaultEqualsGrantedAuthority.AUTHORITY);\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticationManagerResolverDefaultsAndAuthenticationFailThenUnauthorized() {\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.error(new BadCredentialsException(\"failed\")));\n\t\tgiven(this.authenticationManagerResolver.resolve(any())).willReturn(Mono.just(this.authenticationManager));\n\t\tthis.filter = new AuthenticationWebFilter(this.authenticationManagerResolver);\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tEntityExchangeResult<Void> result = client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"test\", \"this\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isUnauthorized()\n\t\t\t.expectHeader()\n\t\t\t.valueMatches(\"WWW-Authenticate\", \"Basic realm=\\\"Realm\\\"\")\n\t\t\t.expectBody()\n\t\t\t.isEmpty();\n\t\tassertThat(result.getResponseCookies()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertEmptyThenOk() {\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(Mono.empty());\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tclient.get()\n\t\t\t.uri(\"/\")\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t.returnResult();\n\t\tverify(this.securityContextRepository, never()).save(any(), any());\n\t\tverifyNoMoreInteractions(this.authenticationManager, this.successHandler, this.failureHandler);\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertErrorThenServerError() {\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(Mono.error(new RuntimeException(\"Unexpected\")));\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tclient.get().uri(\"/\").exchange().expectStatus().is5xxServerError().expectBody().isEmpty();\n\t\tverify(this.securityContextRepository, never()).save(any(), any());\n\t\tverifyNoMoreInteractions(this.authenticationManager, this.successHandler, this.failureHandler);\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertAndAuthenticationSuccessThenSuccess() {\n\t\tMono<Authentication> authentication = Mono.just(new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\"));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tgiven(this.successHandler.onAuthenticationSuccess(any(), any())).willReturn(Mono.empty());\n\t\tgiven(this.securityContextRepository.save(any(), any())).willAnswer((a) -> Mono.just(a.getArguments()[0]));\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tclient.get().uri(\"/\").exchange().expectStatus().isOk().expectBody().isEmpty();\n\t\tverify(this.successHandler).onAuthenticationSuccess(any(), eq(authentication.block()));\n\t\tverify(this.securityContextRepository).save(any(), any());\n\t\tverifyNoMoreInteractions(this.failureHandler);\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertAndAuthenticationEmptyThenServerError() {\n\t\tMono<Authentication> authentication = Mono.just(new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\"));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(Mono.empty());\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tclient.get().uri(\"/\").exchange().expectStatus().is5xxServerError().expectBody().isEmpty();\n\t\tverify(this.securityContextRepository, never()).save(any(), any());\n\t\tverifyNoMoreInteractions(this.successHandler, this.failureHandler);\n\t}\n\n\t@Test\n\tpublic void filterWhenNotMatchAndConvertAndAuthenticationSuccessThenContinues() {\n\t\tthis.filter.setRequiresAuthenticationMatcher((e) -> ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tEntityExchangeResult<String> result = client.get()\n\t\t\t.uri(\"/\")\n\t\t\t.headers((headers) -> headers.setBasicAuth(\"test\", \"this\"))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isOk()\n\t\t\t.expectBody(String.class)\n\t\t\t.consumeWith((b) -> assertThat(b.getResponseBody()).isEqualTo(\"ok\"))\n\t\t\t.returnResult();\n\t\tassertThat(result.getResponseCookies()).isEmpty();\n\t\tverifyNoMoreInteractions(this.authenticationConverter, this.authenticationManager, this.successHandler);\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertAndAuthenticationFailThenEntryPoint() {\n\t\tMono<Authentication> authentication = Mono.just(new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\"));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any()))\n\t\t\t.willReturn(Mono.error(new BadCredentialsException(\"Failed\")));\n\t\tgiven(this.failureHandler.onAuthenticationFailure(any(), any())).willReturn(Mono.empty());\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tclient.get().uri(\"/\").exchange().expectStatus().isOk().expectBody().isEmpty();\n\t\tverify(this.failureHandler).onAuthenticationFailure(any(), any());\n\t\tverify(this.securityContextRepository, never()).save(any(), any());\n\t\tverifyNoMoreInteractions(this.successHandler);\n\t}\n\n\t@Test\n\tpublic void filterWhenConvertAndAuthenticationExceptionThenServerError() {\n\t\tMono<Authentication> authentication = Mono.just(new TestingAuthenticationToken(\"test\", \"this\", \"ROLE_USER\"));\n\t\tgiven(this.authenticationConverter.convert(any())).willReturn(authentication);\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(Mono.error(new RuntimeException(\"Failed\")));\n\t\tWebTestClient client = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\tclient.get().uri(\"/\").exchange().expectStatus().is5xxServerError().expectBody().isEmpty();\n\t\tverify(this.securityContextRepository, never()).save(any(), any());\n\t\tverifyNoMoreInteractions(this.successHandler, this.failureHandler);\n\t}\n\n\t@Test\n\tpublic void setRequiresAuthenticationMatcherWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequiresAuthenticationMatcher(null));\n\t}\n\n\t/**\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tprivate static final class RunAsWebFilter implements WebFilter {\n\n\t\tprivate final Authentication authentication;\n\n\t\tprivate RunAsWebFilter(Authentication authentication) {\n\t\t\tthis.authentication = authentication;\n\t\t}\n\n\t\t@Override\n\t\tpublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {\n\t\t\treturn chain.filter(exchange)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author DingHao\n * @since 6.3\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingServerAuthenticationConverterTests {\n\n\tDelegatingServerAuthenticationConverter converter = new DelegatingServerAuthenticationConverter(\n\t\t\tnew ApkServerAuthenticationConverter(), new ServerHttpBasicAuthenticationConverter());\n\n\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\");\n\n\t@Test\n\tpublic void applyServerHttpBasicAuthenticationConverter() {\n\t\tMono<Authentication> result = this.converter.convert(MockServerWebExchange\n\t\t\t.from(this.request.header(HttpHeaders.AUTHORIZATION, \"Basic dXNlcjpwYXNzd29yZA==\").build()));\n\t\tUsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)\n\t\t\t.block();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"user\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"password\");\n\t}\n\n\t@Test\n\tpublic void applyApkServerAuthenticationConverter() {\n\t\tString apk = \"123e4567e89b12d3a456426655440000\";\n\t\tMono<Authentication> result = this.converter\n\t\t\t.convert(MockServerWebExchange.from(this.request.header(\"APK\", apk).build()));\n\t\tApkTokenAuthenticationToken authentication = result.cast(ApkTokenAuthenticationToken.class).block();\n\t\tassertThat(authentication.getApk()).isEqualTo(apk);\n\t}\n\n\t@Test\n\tpublic void applyServerHttpBasicAuthenticationConverterWhenFirstAuthenticationConverterException() {\n\t\tthis.converter.setContinueOnError(true);\n\t\tMono<Authentication> result = this.converter.convert(MockServerWebExchange.from(this.request.header(\"APK\", \"\")\n\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Basic dXNlcjpwYXNzd29yZA==\")\n\t\t\t.build()));\n\t\tUsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)\n\t\t\t.block();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"user\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"password\");\n\t}\n\n\t@Test\n\tpublic void applyApkServerAuthenticationConverterThenDelegate2NotInvokedAndError() {\n\t\tMono<Authentication> result = this.converter.convert(MockServerWebExchange.from(this.request.header(\"APK\", \"\")\n\t\t\t.header(HttpHeaders.AUTHORIZATION, \"Basic dXNlcjpwYXNzd29yZA==\")\n\t\t\t.build()));\n\t\tStepVerifier.create(result.cast(ApkTokenAuthenticationToken.class))\n\t\t\t.expectError(ApkAuthenticationException.class)\n\t\t\t.verify();\n\t}\n\n\tpublic static class ApkServerAuthenticationConverter implements ServerAuthenticationConverter {\n\n\t\t@Override\n\t\tpublic Mono<Authentication> convert(ServerWebExchange exchange) {\n\t\t\treturn Mono.fromCallable(() -> exchange.getRequest().getHeaders().getFirst(\"APK\")).map((apk) -> {\n\t\t\t\tif (apk.isEmpty()) {\n\t\t\t\t\tthrow new ApkAuthenticationException(\"apk invalid\");\n\t\t\t\t}\n\t\t\t\treturn new ApkTokenAuthenticationToken(apk);\n\t\t\t});\n\t\t}\n\n\t}\n\n\tpublic static class ApkTokenAuthenticationToken extends AbstractAuthenticationToken {\n\n\t\tprivate final String apk;\n\n\t\tpublic ApkTokenAuthenticationToken(String apk) {\n\t\t\tsuper(AuthorityUtils.NO_AUTHORITIES);\n\t\t\tthis.apk = apk;\n\t\t}\n\n\t\tpublic String getApk() {\n\t\t\treturn this.apk;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getCredentials() {\n\t\t\treturn this.getApk();\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getPrincipal() {\n\t\t\treturn this.getApk();\n\t\t}\n\n\t}\n\n\tpublic static class ApkAuthenticationException extends AuthenticationException {\n\n\t\tpublic ApkAuthenticationException(String msg) {\n\t\t\tsuper(msg);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingServerAuthenticationSuccessHandlerTests {\n\n\t@Mock\n\tprivate ServerAuthenticationSuccessHandler delegate1;\n\n\t@Mock\n\tprivate ServerAuthenticationSuccessHandler delegate2;\n\n\tprivate PublisherProbe<Void> delegate1Result = PublisherProbe.empty();\n\n\tprivate PublisherProbe<Void> delegate2Result = PublisherProbe.empty();\n\n\t@Mock\n\tprivate WebFilterExchange exchange;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\tprivate void givenDelegate1WillReturnMock() {\n\t\tgiven(this.delegate1.onAuthenticationSuccess(any(), any())).willReturn(this.delegate1Result.mono());\n\t}\n\n\tprivate void givenDelegate2WillReturnMock() {\n\t\tgiven(this.delegate2.onAuthenticationSuccess(any(), any())).willReturn(this.delegate2Result.mono());\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new DelegatingServerAuthenticationSuccessHandler((ServerAuthenticationSuccessHandler[]) null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullListThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new DelegatingServerAuthenticationSuccessHandler(\n\t\t\t\t(List<ServerAuthenticationSuccessHandler>) null));\n\t}\n\n\t@Test\n\tpublic void constructorWhenEmptyListThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingServerAuthenticationSuccessHandler(Collections.emptyList()));\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessWhenSingleThenExecuted() {\n\t\tgivenDelegate1WillReturnMock();\n\t\tDelegatingServerAuthenticationSuccessHandler handler = new DelegatingServerAuthenticationSuccessHandler(\n\t\t\t\tthis.delegate1);\n\t\thandler.onAuthenticationSuccess(this.exchange, this.authentication).block();\n\t\tthis.delegate1Result.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessWhenMultipleThenExecuted() {\n\t\tgivenDelegate1WillReturnMock();\n\t\tgivenDelegate2WillReturnMock();\n\t\tDelegatingServerAuthenticationSuccessHandler handler = new DelegatingServerAuthenticationSuccessHandler(\n\t\t\t\tthis.delegate1, this.delegate2);\n\t\thandler.onAuthenticationSuccess(this.exchange, this.authentication).block();\n\t\tthis.delegate1Result.assertWasSubscribed();\n\t\tthis.delegate2Result.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void onAuthenticationSuccessSequential() throws Exception {\n\t\tAtomicBoolean slowDone = new AtomicBoolean();\n\t\tCountDownLatch latch = new CountDownLatch(1);\n\t\tServerAuthenticationSuccessHandler slow = (exchange,\n\t\t\t\tauthentication) -> Mono.delay(Duration.ofMillis(100)).doOnSuccess((__) -> slowDone.set(true)).then();\n\t\tServerAuthenticationSuccessHandler second = (exchange, authentication) -> Mono.fromRunnable(() -> {\n\t\t\tlatch.countDown();\n\t\t\tassertThat(slowDone.get()).describedAs(\"ServerAuthenticationSuccessHandler should be executed sequentially\")\n\t\t\t\t.isTrue();\n\t\t});\n\t\tDelegatingServerAuthenticationSuccessHandler handler = new DelegatingServerAuthenticationSuccessHandler(slow,\n\t\t\t\tsecond);\n\t\thandler.onAuthenticationSuccess(this.exchange, this.authentication).block();\n\t\tassertThat(latch.await(3, TimeUnit.SECONDS)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/HttpBasicServerAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class HttpBasicServerAuthenticationEntryPointTests {\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\tprivate HttpBasicServerAuthenticationEntryPoint entryPoint = new HttpBasicServerAuthenticationEntryPoint();\n\n\tprivate AuthenticationException exception = new AuthenticationCredentialsNotFoundException(\"Authenticate\");\n\n\t@Test\n\tpublic void commenceWhenNoSubscribersThenNoActions() {\n\t\tthis.entryPoint.commence(this.exchange, this.exception);\n\t\tverifyNoMoreInteractions(this.exchange);\n\t}\n\n\t@Test\n\tpublic void commenceWhenSubscribeThenStatusAndHeaderSet() {\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"/\"));\n\t\tthis.entryPoint.commence(this.exchange, this.exception).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t\tassertThat(this.exchange.getResponse().getHeaders().get(\"WWW-Authenticate\"))\n\t\t\t.containsOnly(\"Basic realm=\\\"Realm\\\"\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenCustomRealmThenStatusAndHeaderSet() {\n\t\tthis.entryPoint.setRealm(\"Custom\");\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"/\"));\n\t\tthis.entryPoint.commence(this.exchange, this.exception).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t\tassertThat(this.exchange.getResponse().getHeaders().get(\"WWW-Authenticate\"))\n\t\t\t.containsOnly(\"Basic realm=\\\"Custom\\\"\");\n\t}\n\n\t@Test\n\tpublic void setRealmWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.entryPoint.setRealm(null));\n\t}\n\n\tprivate static MockServerWebExchange exchange(MockServerHttpRequest.BaseBuilder<?> request) {\n\t\treturn MockServerWebExchange.from(request.build());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/HttpStatusServerEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.core.AuthenticationException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Eric Deandrea\n * @since 5.1\n */\npublic class HttpStatusServerEntryPointTests {\n\n\tprivate MockServerHttpRequest request;\n\n\tprivate MockServerWebExchange exchange;\n\n\tprivate AuthenticationException authException;\n\n\tprivate HttpStatusServerEntryPoint entryPoint;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = MockServerHttpRequest.get(\"/\").build();\n\t\tthis.exchange = MockServerWebExchange.from(this.request);\n\t\tthis.authException = new AuthenticationException(\"\") {\n\t\t};\n\t\tthis.entryPoint = new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void constructorNullStatus() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new HttpStatusServerEntryPoint(null))\n\t\t\t.withMessage(\"httpStatus cannot be null\");\n\t}\n\n\t@Test\n\tpublic void unauthorized() {\n\t\tthis.entryPoint.commence(this.exchange, this.authException).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ReactivePreAuthenticatedAuthenticationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AccountExpiredException;\nimport org.springframework.security.authentication.CredentialsExpiredException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.LockedException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Alexey Nesterov\n * @since 5.2\n */\npublic class ReactivePreAuthenticatedAuthenticationManagerTests {\n\n\tprivate ReactiveUserDetailsService mockUserDetailsService = mock(ReactiveUserDetailsService.class);\n\n\tprivate ReactivePreAuthenticatedAuthenticationManager manager = new ReactivePreAuthenticatedAuthenticationManager(\n\t\t\tthis.mockUserDetailsService);\n\n\tprivate final User validAccount = new User(\"valid\", \"\", Collections.emptySet());\n\n\tprivate final User nonExistingAccount = new User(\"non existing\", \"\", Collections.emptySet());\n\n\tprivate final User disabledAccount = new User(\"disabled\", \"\", false, true, true, true, Collections.emptySet());\n\n\tprivate final User expiredAccount = new User(\"expired\", \"\", true, false, true, true, Collections.emptySet());\n\n\tprivate final User accountWithExpiredCredentials = new User(\"credentials expired\", \"\", true, true, false, true,\n\t\t\tCollections.emptySet());\n\n\tprivate final User lockedAccount = new User(\"locked\", \"\", true, true, true, false, Collections.emptySet());\n\n\t@Test\n\tpublic void returnsAuthenticatedTokenForValidAccount() {\n\t\tgiven(this.mockUserDetailsService.findByUsername(anyString())).willReturn(Mono.just(this.validAccount));\n\t\tAuthentication authentication = this.manager.authenticate(tokenForUser(this.validAccount.getUsername()))\n\t\t\t.block();\n\t\tassertThat(authentication.isAuthenticated()).isEqualTo(true);\n\t}\n\n\t@Test\n\tpublic void returnsNullForNonExistingAccount() {\n\t\tgiven(this.mockUserDetailsService.findByUsername(anyString())).willReturn(Mono.empty());\n\t\tassertThatExceptionOfType(UsernameNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(tokenForUser(this.nonExistingAccount.getUsername())).block());\n\t}\n\n\t@Test\n\tpublic void throwsExceptionForLockedAccount() {\n\t\tgiven(this.mockUserDetailsService.findByUsername(anyString())).willReturn(Mono.just(this.lockedAccount));\n\t\tassertThatExceptionOfType(LockedException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(tokenForUser(this.lockedAccount.getUsername())).block());\n\t}\n\n\t@Test\n\tpublic void throwsExceptionForDisabledAccount() {\n\t\tgiven(this.mockUserDetailsService.findByUsername(anyString())).willReturn(Mono.just(this.disabledAccount));\n\t\tassertThatExceptionOfType(DisabledException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(tokenForUser(this.disabledAccount.getUsername())).block());\n\t}\n\n\t@Test\n\tpublic void throwsExceptionForExpiredAccount() {\n\t\tgiven(this.mockUserDetailsService.findByUsername(anyString())).willReturn(Mono.just(this.expiredAccount));\n\t\tassertThatExceptionOfType(AccountExpiredException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(tokenForUser(this.expiredAccount.getUsername())).block());\n\t}\n\n\t@Test\n\tpublic void throwsExceptionForAccountWithExpiredCredentials() {\n\t\tgiven(this.mockUserDetailsService.findByUsername(anyString()))\n\t\t\t.willReturn(Mono.just(this.accountWithExpiredCredentials));\n\t\tassertThatExceptionOfType(CredentialsExpiredException.class)\n\t\t\t.isThrownBy(() -> this.manager.authenticate(tokenForUser(this.accountWithExpiredCredentials.getUsername()))\n\t\t\t\t.block());\n\t}\n\n\tprivate Authentication tokenForUser(String username) {\n\t\treturn new PreAuthenticatedAuthenticationToken(username, null);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/RedirectServerAuthenticationEntryPointTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class RedirectServerAuthenticationEntryPointTests {\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\t@Mock\n\tprivate ServerRedirectStrategy redirectStrategy;\n\n\tprivate String location = \"/login\";\n\n\tprivate RedirectServerAuthenticationEntryPoint entryPoint = new RedirectServerAuthenticationEntryPoint(\n\t\t\tthis.location);\n\n\tprivate AuthenticationException exception = new AuthenticationCredentialsNotFoundException(\n\t\t\t\"Authentication Required\");\n\n\t@Test\n\tpublic void constructorStringWhenNullLocationThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RedirectServerAuthenticationEntryPoint(null));\n\t}\n\n\t@Test\n\tpublic void commenceWhenNoSubscribersThenNoActions() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.entryPoint.commence(this.exchange, this.exception);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation()).isNull();\n\t\tassertThat(this.exchange.getSession().block().isStarted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void commenceWhenSubscribeThenStatusAndLocationSet() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.entryPoint.commence(this.exchange, this.exception).block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation()).hasPath(this.location);\n\t}\n\n\t@Test\n\tpublic void commenceWhenCustomServerRedirectStrategyThenCustomServerRedirectStrategyUsed() {\n\t\tPublisherProbe<Void> redirectResult = PublisherProbe.empty();\n\t\tgiven(this.redirectStrategy.sendRedirect(any(), any())).willReturn(redirectResult.mono());\n\t\tthis.entryPoint.setRedirectStrategy(this.redirectStrategy);\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.entryPoint.commence(this.exchange, this.exception).block();\n\t\tredirectResult.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void setRedirectStrategyWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.entryPoint.setRedirectStrategy(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/RedirectServerAuthenticationFailureHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.handler.DefaultWebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class RedirectServerAuthenticationFailureHandlerTests {\n\n\tprivate WebFilterExchange exchange;\n\n\t@Mock\n\tprivate ServerRedirectStrategy redirectStrategy;\n\n\tprivate String location = \"/login\";\n\n\tprivate RedirectServerAuthenticationFailureHandler handler = new RedirectServerAuthenticationFailureHandler(\n\t\t\tthis.location);\n\n\tprivate AuthenticationException exception = new AuthenticationCredentialsNotFoundException(\n\t\t\t\"Authentication Required\");\n\n\t@Test\n\tpublic void constructorStringWhenNullLocationThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RedirectServerAuthenticationEntryPoint(null));\n\t}\n\n\t@Test\n\tpublic void commenceWhenNoSubscribersThenNoActions() {\n\t\tthis.exchange = createExchange();\n\t\tthis.handler.onAuthenticationFailure(this.exchange, this.exception);\n\t\tassertThat(this.exchange.getExchange().getResponse().getHeaders().getLocation()).isNull();\n\t\tassertThat(this.exchange.getExchange().getSession().block().isStarted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void commenceWhenSubscribeThenStatusAndLocationSet() {\n\t\tthis.exchange = createExchange();\n\t\tthis.handler.onAuthenticationFailure(this.exchange, this.exception).block();\n\t\tassertThat(this.exchange.getExchange().getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(this.exchange.getExchange().getResponse().getHeaders().getLocation()).hasPath(this.location);\n\t}\n\n\t@Test\n\tpublic void commenceWhenCustomServerRedirectStrategyThenCustomServerRedirectStrategyUsed() {\n\t\tPublisherProbe<Void> redirectResult = PublisherProbe.empty();\n\t\tgiven(this.redirectStrategy.sendRedirect(any(), any())).willReturn(redirectResult.mono());\n\t\tthis.handler.setRedirectStrategy(this.redirectStrategy);\n\t\tthis.exchange = createExchange();\n\t\tthis.handler.onAuthenticationFailure(this.exchange, this.exception).block();\n\t\tredirectResult.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void setRedirectStrategyWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setRedirectStrategy(null));\n\t}\n\n\tprivate WebFilterExchange createExchange() {\n\t\treturn new WebFilterExchange(MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build()),\n\t\t\t\tnew DefaultWebFilterChain((e) -> Mono.empty(), Collections.emptyList()));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/RedirectServerAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.net.URI;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.ServerRedirectStrategy;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class RedirectServerAuthenticationSuccessHandlerTests {\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\t@Mock\n\tprivate WebFilterChain chain;\n\n\t@Mock\n\tprivate ServerRedirectStrategy redirectStrategy;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\tprivate URI location = URI.create(\"/\");\n\n\tprivate RedirectServerAuthenticationSuccessHandler handler = new RedirectServerAuthenticationSuccessHandler();\n\n\t@Test\n\tpublic void constructorStringWhenNullLocationThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RedirectServerAuthenticationEntryPoint(null));\n\t}\n\n\t@Test\n\tpublic void successWhenNoSubscribersThenNoActions() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.handler.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), this.authentication);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation()).isNull();\n\t\tassertThat(this.exchange.getSession().block().isStarted()).isFalse();\n\t}\n\n\t@Test\n\tpublic void successWhenSubscribeThenStatusAndLocationSet() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.handler.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), this.authentication)\n\t\t\t.block();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(this.exchange.getResponse().getHeaders().getLocation()).isEqualTo(this.location);\n\t}\n\n\t@Test\n\tpublic void successWhenCustomLocationThenCustomLocationUsed() {\n\t\tPublisherProbe<Void> redirectResult = PublisherProbe.empty();\n\t\tgiven(this.redirectStrategy.sendRedirect(any(), any())).willReturn(redirectResult.mono());\n\t\tthis.handler.setRedirectStrategy(this.redirectStrategy);\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.handler.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), this.authentication)\n\t\t\t.block();\n\t\tredirectResult.assertWasSubscribed();\n\t\tverify(this.redirectStrategy).sendRedirect(any(), eq(this.location));\n\t}\n\n\t@Test\n\tpublic void setRedirectStrategyWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setRedirectStrategy(null));\n\t}\n\n\t@Test\n\tpublic void setLocationWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.handler.setLocation(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ServerAuthenticationEntryPointFailureHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServerAuthenticationEntryPointFailureHandlerTests {\n\n\t@Mock\n\tprivate ServerAuthenticationEntryPoint authenticationEntryPoint;\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\t@Mock\n\tprivate WebFilterChain chain;\n\n\t@InjectMocks\n\tprivate WebFilterExchange filterExchange;\n\n\t@InjectMocks\n\tprivate ServerAuthenticationEntryPointFailureHandler handler;\n\n\t@Test\n\tpublic void constructorWhenNullEntryPointThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ServerAuthenticationEntryPointFailureHandler(null));\n\t}\n\n\t@Test\n\tpublic void onAuthenticationFailureWhenInvokedThenDelegatesToEntryPoint() {\n\t\tMono<Void> result = Mono.empty();\n\t\tBadCredentialsException e = new BadCredentialsException(\"Failed\");\n\t\tgiven(this.authenticationEntryPoint.commence(this.exchange, e)).willReturn(result);\n\t\tassertThat(this.handler.onAuthenticationFailure(this.filterExchange, e)).isEqualTo(result);\n\t}\n\n\t@Test\n\tvoid onAuthenticationFailureWhenRethrownFalseThenAuthenticationServiceExceptionSwallowed() {\n\t\tAuthenticationServiceException e = new AuthenticationServiceException(\"fail\");\n\t\tthis.handler.setRethrowAuthenticationServiceException(false);\n\t\tgiven(this.authenticationEntryPoint.commence(this.exchange, e)).willReturn(Mono.empty());\n\t\tthis.handler.onAuthenticationFailure(this.filterExchange, e).block();\n\t}\n\n\t@Test\n\tvoid handleWhenDefaultsThenAuthenticationServiceExceptionRethrown() {\n\t\tAuthenticationServiceException e = new AuthenticationServiceException(\"fail\");\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> this.handler.onAuthenticationFailure(this.filterExchange, e).block());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ServerFormLoginAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServerFormLoginAuthenticationConverterTests {\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\tprivate MultiValueMap<String, String> data = new LinkedMultiValueMap<>();\n\n\tprivate ServerFormLoginAuthenticationConverter converter = new ServerFormLoginAuthenticationConverter();\n\n\tpublic void setupMocks() {\n\t\tgiven(this.exchange.getFormData()).willReturn(Mono.just(this.data));\n\t}\n\n\t@Test\n\tpublic void applyWhenUsernameAndPasswordThenCreatesTokenSuccess() {\n\t\tsetupMocks();\n\t\tString username = \"username\";\n\t\tString password = \"password\";\n\t\tthis.data.add(\"username\", username);\n\t\tthis.data.add(\"password\", password);\n\t\tAuthentication authentication = this.converter.convert(this.exchange).block();\n\t\tassertThat(authentication.getName()).isEqualTo(username);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(password);\n\t\tassertThat(authentication.getAuthorities()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void applyWhenCustomParametersAndUsernameAndPasswordThenCreatesTokenSuccess() {\n\t\tsetupMocks();\n\t\tString usernameParameter = \"j_username\";\n\t\tString passwordParameter = \"j_password\";\n\t\tString username = \"username\";\n\t\tString password = \"password\";\n\t\tthis.converter.setUsernameParameter(usernameParameter);\n\t\tthis.converter.setPasswordParameter(passwordParameter);\n\t\tthis.data.add(usernameParameter, username);\n\t\tthis.data.add(passwordParameter, password);\n\t\tAuthentication authentication = this.converter.convert(this.exchange).block();\n\t\tassertThat(authentication.getName()).isEqualTo(username);\n\t\tassertThat(authentication.getCredentials()).isEqualTo(password);\n\t\tassertThat(authentication.getAuthorities()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void applyWhenNoDataThenCreatesTokenSuccess() {\n\t\tsetupMocks();\n\t\tAuthentication authentication = this.converter.convert(this.exchange).block();\n\t\tassertThat(authentication.getName()).isNullOrEmpty();\n\t\tassertThat(authentication.getCredentials()).isNull();\n\t\tassertThat(authentication.getAuthorities()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void setUsernameParameterWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.converter.setUsernameParameter(null));\n\t}\n\n\t@Test\n\tpublic void setPasswordParameterWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.converter.setPasswordParameter(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ServerHttpBasicAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.nio.charset.StandardCharsets;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class ServerHttpBasicAuthenticationConverterTests {\n\n\tServerHttpBasicAuthenticationConverter converter = new ServerHttpBasicAuthenticationConverter();\n\n\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\");\n\n\t@Test\n\tpublic void setCredentialsCharsetWhenNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.converter.setCredentialsCharset(null))\n\t\t\t.withMessage(\"credentialsCharset cannot be null\");\n\t}\n\n\t@Test\n\tpublic void applyWhenNoAuthorizationHeaderThenEmpty() {\n\t\tMono<Authentication> result = apply(this.request);\n\t\tassertThat(result.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void applyWhenEmptyAuthorizationHeaderThenEmpty() {\n\t\tMono<Authentication> result = apply(this.request.header(HttpHeaders.AUTHORIZATION, \"\"));\n\t\tassertThat(result.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void applyWhenOnlyBasicAuthorizationHeaderThenEmpty() {\n\t\tMono<Authentication> result = apply(this.request.header(HttpHeaders.AUTHORIZATION, \"Basic \"));\n\t\tassertThat(result.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void applyWhenNotBase64ThenEmpty() {\n\t\tMono<Authentication> result = apply(this.request.header(HttpHeaders.AUTHORIZATION, \"Basic z\"));\n\t\tassertThat(result.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void applyWhenNoColonThenEmpty() {\n\t\tMono<Authentication> result = apply(this.request.header(HttpHeaders.AUTHORIZATION, \"Basic dXNlcg==\"));\n\t\tassertThat(result.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void applyWhenUserPasswordThenAuthentication() {\n\t\tMono<Authentication> result = apply(\n\t\t\t\tthis.request.header(HttpHeaders.AUTHORIZATION, \"Basic dXNlcjpwYXNzd29yZA==\"));\n\t\tUsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)\n\t\t\t.block();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"user\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"password\");\n\t}\n\n\t@Test\n\tpublic void applyWhenUserPasswordHasColonThenAuthentication() {\n\t\tMono<Authentication> result = apply(\n\t\t\t\tthis.request.header(HttpHeaders.AUTHORIZATION, \"Basic dXNlcjpwYXNzOndvcmQ=\"));\n\t\tUsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)\n\t\t\t.block();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"user\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"pass:word\");\n\t}\n\n\t@Test\n\tpublic void applyWhenLowercaseSchemeThenAuthentication() {\n\t\tMono<Authentication> result = apply(\n\t\t\t\tthis.request.header(HttpHeaders.AUTHORIZATION, \"basic dXNlcjpwYXNzd29yZA==\"));\n\t\tUsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)\n\t\t\t.block();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"user\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"password\");\n\t}\n\n\t@Test\n\tpublic void applyWhenWrongSchemeThenEmpty() {\n\t\tMono<Authentication> result = apply(\n\t\t\t\tthis.request.header(HttpHeaders.AUTHORIZATION, \"token dXNlcjpwYXNzd29yZA==\"));\n\t\tassertThat(result.block()).isNull();\n\t}\n\n\t@Test\n\tpublic void applyWhenNonAsciiThenAuthentication() {\n\t\tMono<Authentication> result = apply(\n\t\t\t\tthis.request.header(HttpHeaders.AUTHORIZATION, \"Basic w7xzZXI6cGFzc3fDtnJk\"));\n\t\tUsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)\n\t\t\t.block();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"üser\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"passwörd\");\n\t}\n\n\t@Test\n\tpublic void applyWhenIsoOnlyAsciiThenAuthentication() {\n\t\tthis.converter.setCredentialsCharset(StandardCharsets.ISO_8859_1);\n\t\tMono<Authentication> result = apply(\n\t\t\t\tthis.request.header(HttpHeaders.AUTHORIZATION, \"Basic dXNlcjpwYXNzd29yZA==\"));\n\t\tUsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)\n\t\t\t.block();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"user\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"password\");\n\t}\n\n\t@Test\n\tpublic void applyWhenIsoNonAsciiThenAuthentication() {\n\t\tthis.converter.setCredentialsCharset(StandardCharsets.ISO_8859_1);\n\t\tMono<Authentication> result = apply(\n\t\t\t\tthis.request.header(HttpHeaders.AUTHORIZATION, \"Basic /HNlcjpwYXNzd/ZyZA==\"));\n\t\tUsernamePasswordAuthenticationToken authentication = result.cast(UsernamePasswordAuthenticationToken.class)\n\t\t\t.block();\n\t\tassertThat(authentication.getPrincipal()).isEqualTo(\"üser\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(\"passwörd\");\n\t}\n\n\tprivate Mono<Authentication> apply(MockServerHttpRequest.BaseBuilder<?> request) {\n\t\treturn this.converter.convert(MockServerWebExchange.from(this.request.build()));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ServerWebExchangeDelegatingReactiveAuthenticationManagerResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.authentication.ReactiveAuthenticationManager;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\n/**\n * Tests for {@link ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver}\n *\n * @author Josh Cummings\n */\npublic class ServerWebExchangeDelegatingReactiveAuthenticationManagerResolverTests {\n\n\tprivate ReactiveAuthenticationManager one = mock(ReactiveAuthenticationManager.class);\n\n\tprivate ReactiveAuthenticationManager two = mock(ReactiveAuthenticationManager.class);\n\n\t@Test\n\tpublic void resolveWhenMatchesThenReturnsReactiveAuthenticationManager() {\n\t\tServerWebExchangeDelegatingReactiveAuthenticationManagerResolver resolver = ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver\n\t\t\t.builder()\n\t\t\t.add(new PathPatternParserServerWebExchangeMatcher(\"/one/**\"), this.one)\n\t\t\t.add(new PathPatternParserServerWebExchangeMatcher(\"/two/**\"), this.two)\n\t\t\t.build();\n\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/one/location\").build();\n\t\tassertThat(resolver.resolve(MockServerWebExchange.from(request)).block()).isEqualTo(this.one);\n\t}\n\n\t@Test\n\tpublic void resolveWhenDoesNotMatchThenReturnsDefaultReactiveAuthenticationManager() {\n\t\tServerWebExchangeDelegatingReactiveAuthenticationManagerResolver resolver = ServerWebExchangeDelegatingReactiveAuthenticationManagerResolver\n\t\t\t.builder()\n\t\t\t.add(new PathPatternParserServerWebExchangeMatcher(\"/one/**\"), this.one)\n\t\t\t.add(new PathPatternParserServerWebExchangeMatcher(\"/two/**\"), this.two)\n\t\t\t.build();\n\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/wrong/location\").build();\n\t\tReactiveAuthenticationManager authenticationManager = resolver.resolve(MockServerWebExchange.from(request))\n\t\t\t.block();\n\n\t\tAuthentication authentication = new TestingAuthenticationToken(\"principal\", \"creds\");\n\t\tassertThatExceptionOfType(AuthenticationServiceException.class)\n\t\t\t.isThrownBy(() -> authenticationManager.authenticate(authentication).block());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ServerX509AuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.security.cert.X509Certificate;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.server.reactive.SslInfo;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;\nimport org.springframework.security.web.authentication.preauth.x509.X509TestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n@ExtendWith(MockitoExtension.class)\npublic class ServerX509AuthenticationConverterTests {\n\n\t@Mock\n\tprivate X509PrincipalExtractor principalExtractor;\n\n\t@InjectMocks\n\tprivate ServerX509AuthenticationConverter converter;\n\n\tprivate X509Certificate certificate;\n\n\tprivate MockServerHttpRequest.BaseBuilder<?> request;\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tthis.request = MockServerHttpRequest.get(\"/\");\n\t\tthis.certificate = X509TestUtils.buildTestCertificate();\n\t}\n\n\tprivate void givenExtractPrincipalWillReturn() {\n\t\tgiven(this.principalExtractor.extractPrincipal(any())).willReturn(\"Luke Taylor\");\n\t}\n\n\t@Test\n\tpublic void shouldReturnNullForInvalidCertificate() {\n\t\tAuthentication authentication = this.converter.convert(MockServerWebExchange.from(this.request.build()))\n\t\t\t.block();\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tpublic void shouldReturnAuthenticationForValidCertificate() {\n\t\tgivenExtractPrincipalWillReturn();\n\t\tthis.request.sslInfo(SslInfo.from(\"123\", this.certificate));\n\t\tAuthentication authentication = this.converter.convert(MockServerWebExchange.from(this.request.build()))\n\t\t\t.block();\n\t\tassertThat(authentication.getName()).isEqualTo(\"Luke Taylor\");\n\t\tassertThat(authentication.getCredentials()).isEqualTo(this.certificate);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/SwitchUserWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication;\n\nimport java.security.Principal;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.util.context.Context;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.AccountStatusUserDetailsChecker;\nimport org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;\nimport org.springframework.security.authentication.DisabledException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.core.userdetails.ReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.context.ServerSecurityContextRepository;\nimport org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;\nimport org.springframework.test.util.ReflectionTestUtils;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * @author Artur Otrzonsek\n */\n@ExtendWith(MockitoExtension.class)\npublic class SwitchUserWebFilterTests {\n\n\tprivate SwitchUserWebFilter switchUserWebFilter;\n\n\t@Mock\n\tprivate ReactiveUserDetailsService userDetailsService;\n\n\t@Mock\n\tServerAuthenticationSuccessHandler successHandler;\n\n\t@Mock\n\tprivate ServerAuthenticationFailureHandler failureHandler;\n\n\t@Mock\n\tprivate ServerSecurityContextRepository serverSecurityContextRepository;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.switchUserWebFilter = new SwitchUserWebFilter(this.userDetailsService, this.successHandler,\n\t\t\t\tthis.failureHandler);\n\t\tthis.switchUserWebFilter.setSecurityContextRepository(this.serverSecurityContextRepository);\n\t}\n\n\t@Test\n\tpublic void switchUserWhenRequestNotMatchThenDoesNothing() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/not/existing\"));\n\t\tWebFilterChain chain = mock(WebFilterChain.class);\n\t\tgiven(chain.filter(exchange)).willReturn(Mono.empty());\n\t\tthis.switchUserWebFilter.filter(exchange, chain).block();\n\t\tverifyNoInteractions(this.userDetailsService);\n\t\tverifyNoInteractions(this.successHandler);\n\t\tverifyNoInteractions(this.failureHandler);\n\t\tverifyNoInteractions(this.serverSecurityContextRepository);\n\t\tverify(chain).filter(exchange);\n\t}\n\n\t@Test\n\tpublic void switchUser() {\n\t\tfinal String targetUsername = \"TEST_USERNAME\";\n\t\tfinal UserDetails switchUserDetails = switchUserDetails(targetUsername, true);\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/login/impersonate?username={targetUser}\", targetUsername));\n\t\tfinal WebFilterChain chain = mock(WebFilterChain.class);\n\t\tfinal Authentication originalAuthentication = UsernamePasswordAuthenticationToken.unauthenticated(\"principal\",\n\t\t\t\t\"credentials\");\n\t\tfinal SecurityContextImpl securityContext = new SecurityContextImpl(originalAuthentication);\n\t\tgiven(this.userDetailsService.findByUsername(targetUsername)).willReturn(Mono.just(switchUserDetails));\n\t\tgiven(this.serverSecurityContextRepository.save(eq(exchange), any(SecurityContext.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tgiven(this.successHandler.onAuthenticationSuccess(any(WebFilterExchange.class), any(Authentication.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tthis.switchUserWebFilter.filter(exchange, chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))\n\t\t\t.block();\n\t\tverifyNoInteractions(chain);\n\t\tverify(this.userDetailsService).findByUsername(targetUsername);\n\t\tfinal ArgumentCaptor<SecurityContext> securityContextCaptor = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(this.serverSecurityContextRepository).save(eq(exchange), securityContextCaptor.capture());\n\t\tfinal SecurityContext savedSecurityContext = securityContextCaptor.getValue();\n\t\tfinal ArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.successHandler).onAuthenticationSuccess(any(WebFilterExchange.class),\n\t\t\t\tauthenticationCaptor.capture());\n\t\tfinal Authentication switchUserAuthentication = authenticationCaptor.getValue();\n\t\tassertThat(switchUserAuthentication).isSameAs(savedSecurityContext.getAuthentication());\n\t\tassertThat(switchUserAuthentication.getName()).isEqualTo(targetUsername);\n\t\tassertThat(switchUserAuthentication.getAuthorities()).anyMatch(SwitchUserGrantedAuthority.class::isInstance);\n\t\tassertThat(switchUserAuthentication.getAuthorities())\n\t\t\t.anyMatch((a) -> a.getAuthority().contains(SwitchUserWebFilter.ROLE_PREVIOUS_ADMINISTRATOR));\n\t\tassertThat(switchUserAuthentication.getAuthorities()\n\t\t\t.stream()\n\t\t\t.filter((a) -> a instanceof SwitchUserGrantedAuthority)\n\t\t\t.map((a) -> ((SwitchUserGrantedAuthority) a).getSource())\n\t\t\t.map(Principal::getName)).contains(originalAuthentication.getName());\n\t}\n\n\t@Test\n\tpublic void switchUserWhenUserAlreadySwitchedThenExitSwitchAndSwitchAgain() {\n\t\tfinal Authentication originalAuthentication = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(\"origPrincipal\", \"origCredentials\");\n\t\tfinal GrantedAuthority switchAuthority = new SwitchUserGrantedAuthority(\n\t\t\t\tSwitchUserWebFilter.ROLE_PREVIOUS_ADMINISTRATOR, originalAuthentication);\n\t\tfinal Authentication switchUserAuthentication = UsernamePasswordAuthenticationToken\n\t\t\t.authenticated(\"switchPrincipal\", \"switchCredentials\", Collections.singleton(switchAuthority));\n\t\tfinal SecurityContextImpl securityContext = new SecurityContextImpl(switchUserAuthentication);\n\t\tfinal String targetUsername = \"newSwitchPrincipal\";\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/login/impersonate?username={targetUser}\", targetUsername));\n\t\tfinal WebFilterChain chain = mock(WebFilterChain.class);\n\t\tgiven(this.serverSecurityContextRepository.save(eq(exchange), any(SecurityContext.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tgiven(this.successHandler.onAuthenticationSuccess(any(WebFilterExchange.class), any(Authentication.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tgiven(this.userDetailsService.findByUsername(targetUsername))\n\t\t\t.willReturn(Mono.just(switchUserDetails(targetUsername, true)));\n\t\tthis.switchUserWebFilter.filter(exchange, chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))\n\t\t\t.block();\n\t\tfinal ArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.successHandler).onAuthenticationSuccess(any(WebFilterExchange.class),\n\t\t\t\tauthenticationCaptor.capture());\n\t\tfinal Authentication secondSwitchUserAuthentication = authenticationCaptor.getValue();\n\t\tassertThat(secondSwitchUserAuthentication.getName()).isEqualTo(targetUsername);\n\t\tassertThat(secondSwitchUserAuthentication.getAuthorities()\n\t\t\t.stream()\n\t\t\t.filter((a) -> a instanceof SwitchUserGrantedAuthority)\n\t\t\t.map((a) -> ((SwitchUserGrantedAuthority) a).getSource())\n\t\t\t.map(Principal::getName)\n\t\t\t.findFirst()\n\t\t\t.orElse(null)).isEqualTo(originalAuthentication.getName());\n\t}\n\n\t@Test\n\tpublic void switchUserWhenUsernameIsMissingThenThrowException() {\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/login/impersonate\"));\n\t\tfinal WebFilterChain chain = mock(WebFilterChain.class);\n\t\tfinal SecurityContextImpl securityContext = new SecurityContextImpl(mock(Authentication.class));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> {\n\t\t\tContext securityContextHolder = ReactiveSecurityContextHolder\n\t\t\t\t.withSecurityContext(Mono.just(securityContext));\n\t\t\tthis.switchUserWebFilter.filter(exchange, chain).contextWrite(securityContextHolder).block();\n\t\t}).withMessage(\"The userName can not be null.\");\n\t\tverifyNoInteractions(chain);\n\t}\n\n\t@Test\n\tpublic void switchUserWhenExceptionThenCallFailureHandler() {\n\t\tfinal String targetUsername = \"TEST_USERNAME\";\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/login/impersonate?username={targetUser}\", targetUsername));\n\t\tfinal WebFilterChain chain = mock(WebFilterChain.class);\n\t\tfinal SecurityContextImpl securityContext = new SecurityContextImpl(mock(Authentication.class));\n\t\tfinal UserDetails switchUserDetails = switchUserDetails(targetUsername, false);\n\t\tgiven(this.userDetailsService.findByUsername(any(String.class))).willReturn(Mono.just(switchUserDetails));\n\t\tgiven(this.failureHandler.onAuthenticationFailure(any(WebFilterExchange.class), any(DisabledException.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tthis.switchUserWebFilter.filter(exchange, chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))\n\t\t\t.block();\n\t\tverify(this.failureHandler).onAuthenticationFailure(any(WebFilterExchange.class), any(DisabledException.class));\n\t\tverifyNoInteractions(chain);\n\t}\n\n\t@Test\n\tpublic void switchUserWhenFailureHandlerNotDefinedThenReturnError() {\n\t\tthis.switchUserWebFilter = new SwitchUserWebFilter(this.userDetailsService, this.successHandler, null);\n\t\tfinal String targetUsername = \"TEST_USERNAME\";\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/login/impersonate?username={targetUser}\", targetUsername));\n\t\tfinal WebFilterChain chain = mock(WebFilterChain.class);\n\t\tfinal SecurityContextImpl securityContext = new SecurityContextImpl(mock(Authentication.class));\n\t\tfinal UserDetails switchUserDetails = switchUserDetails(targetUsername, false);\n\t\tgiven(this.userDetailsService.findByUsername(any(String.class))).willReturn(Mono.just(switchUserDetails));\n\t\tassertThatExceptionOfType(DisabledException.class).isThrownBy(() -> {\n\t\t\tContext securityContextHolder = ReactiveSecurityContextHolder\n\t\t\t\t.withSecurityContext(Mono.just(securityContext));\n\t\t\tthis.switchUserWebFilter.filter(exchange, chain).contextWrite(securityContextHolder).block();\n\t\t});\n\t\tverifyNoInteractions(chain);\n\t}\n\n\t@Test\n\tpublic void exitSwitchThenReturnToOriginalAuthentication() {\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/logout/impersonate\"));\n\t\tfinal Authentication originalAuthentication = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(\"origPrincipal\", \"origCredentials\");\n\t\tfinal GrantedAuthority switchAuthority = new SwitchUserGrantedAuthority(\n\t\t\t\tSwitchUserWebFilter.ROLE_PREVIOUS_ADMINISTRATOR, originalAuthentication);\n\t\tfinal Authentication switchUserAuthentication = UsernamePasswordAuthenticationToken\n\t\t\t.authenticated(\"switchPrincipal\", \"switchCredentials\", Collections.singleton(switchAuthority));\n\t\tfinal WebFilterChain chain = mock(WebFilterChain.class);\n\t\tfinal SecurityContextImpl securityContext = new SecurityContextImpl(switchUserAuthentication);\n\t\tgiven(this.serverSecurityContextRepository.save(eq(exchange), any(SecurityContext.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tgiven(this.successHandler.onAuthenticationSuccess(any(WebFilterExchange.class), any(Authentication.class)))\n\t\t\t.willReturn(Mono.empty());\n\t\tthis.switchUserWebFilter.filter(exchange, chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))\n\t\t\t.block();\n\t\tfinal ArgumentCaptor<SecurityContext> securityContextCaptor = ArgumentCaptor.forClass(SecurityContext.class);\n\t\tverify(this.serverSecurityContextRepository).save(eq(exchange), securityContextCaptor.capture());\n\t\tfinal SecurityContext savedSecurityContext = securityContextCaptor.getValue();\n\t\tfinal ArgumentCaptor<Authentication> authenticationCaptor = ArgumentCaptor.forClass(Authentication.class);\n\t\tverify(this.successHandler).onAuthenticationSuccess(any(WebFilterExchange.class),\n\t\t\t\tauthenticationCaptor.capture());\n\t\tfinal Authentication originalAuthenticationValue = authenticationCaptor.getValue();\n\t\tassertThat(savedSecurityContext.getAuthentication()).isSameAs(originalAuthentication);\n\t\tassertThat(originalAuthenticationValue).isSameAs(originalAuthentication);\n\t\tverifyNoInteractions(chain);\n\t}\n\n\t@Test\n\tpublic void exitSwitchWhenUserNotSwitchedThenThrowError() {\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/logout/impersonate\"));\n\t\tfinal Authentication originalAuthentication = UsernamePasswordAuthenticationToken\n\t\t\t.unauthenticated(\"origPrincipal\", \"origCredentials\");\n\t\tfinal WebFilterChain chain = mock(WebFilterChain.class);\n\t\tfinal SecurityContextImpl securityContext = new SecurityContextImpl(originalAuthentication);\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class).isThrownBy(() -> {\n\t\t\tContext securityContextHolder = ReactiveSecurityContextHolder\n\t\t\t\t.withSecurityContext(Mono.just(securityContext));\n\t\t\tthis.switchUserWebFilter.filter(exchange, chain).contextWrite(securityContextHolder).block();\n\t\t}).withMessage(\"Could not find original Authentication object\");\n\t\tverifyNoInteractions(chain);\n\t}\n\n\t@Test\n\tpublic void exitSwitchWhenNoCurrentUserThenThrowError() {\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/logout/impersonate\"));\n\t\tfinal WebFilterChain chain = mock(WebFilterChain.class);\n\t\tassertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)\n\t\t\t.isThrownBy(() -> this.switchUserWebFilter.filter(exchange, chain).block())\n\t\t\t.withMessage(\"No current user associated with this request\");\n\t\tverifyNoInteractions(chain);\n\t}\n\n\t@Test\n\tpublic void constructorUserDetailsServiceRequired() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.switchUserWebFilter = new SwitchUserWebFilter(null,\n\t\t\t\t\tmock(ServerAuthenticationSuccessHandler.class), mock(ServerAuthenticationFailureHandler.class)))\n\t\t\t.withMessage(\"userDetailsService must be specified\");\n\t}\n\n\t@Test\n\tpublic void constructorServerAuthenticationSuccessHandlerRequired() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.switchUserWebFilter = new SwitchUserWebFilter(mock(ReactiveUserDetailsService.class),\n\t\t\t\t\tnull, mock(ServerAuthenticationFailureHandler.class)))\n\t\t\t.withMessage(\"successHandler must be specified\");\n\t}\n\n\t@Test\n\tpublic void constructorSuccessTargetUrlRequired() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.switchUserWebFilter = new SwitchUserWebFilter(mock(ReactiveUserDetailsService.class),\n\t\t\t\t\tnull, \"failure/target/url\"))\n\t\t\t.withMessage(\"successTargetUrl must be specified\");\n\t}\n\n\t@Test\n\tpublic void constructorFirstDefaultValues() {\n\t\tthis.switchUserWebFilter = new SwitchUserWebFilter(mock(ReactiveUserDetailsService.class),\n\t\t\t\tmock(ServerAuthenticationSuccessHandler.class), mock(ServerAuthenticationFailureHandler.class));\n\t\tfinal Object securityContextRepository = ReflectionTestUtils.getField(this.switchUserWebFilter,\n\t\t\t\t\"securityContextRepository\");\n\t\tassertThat(securityContextRepository).isInstanceOf(WebSessionServerSecurityContextRepository.class);\n\t\tfinal Object userDetailsChecker = ReflectionTestUtils.getField(this.switchUserWebFilter, \"userDetailsChecker\");\n\t\tassertThat(userDetailsChecker).isInstanceOf(AccountStatusUserDetailsChecker.class);\n\t}\n\n\t@Test\n\tpublic void constructorSecondDefaultValues() {\n\t\tthis.switchUserWebFilter = new SwitchUserWebFilter(mock(ReactiveUserDetailsService.class), \"success/target/url\",\n\t\t\t\t\"failure/target/url\");\n\t\tfinal Object successHandler = ReflectionTestUtils.getField(this.switchUserWebFilter, \"successHandler\");\n\t\tassertThat(successHandler).isInstanceOf(RedirectServerAuthenticationSuccessHandler.class);\n\t\tfinal Object failureHandler = ReflectionTestUtils.getField(this.switchUserWebFilter, \"failureHandler\");\n\t\tassertThat(failureHandler).isInstanceOf(RedirectServerAuthenticationFailureHandler.class);\n\t\tfinal Object securityContextRepository = ReflectionTestUtils.getField(this.switchUserWebFilter,\n\t\t\t\t\"securityContextRepository\");\n\t\tassertThat(securityContextRepository).isInstanceOf(WebSessionServerSecurityContextRepository.class);\n\t\tfinal Object userDetailsChecker = ReflectionTestUtils.getField(this.switchUserWebFilter, \"userDetailsChecker\");\n\t\tassertThat(userDetailsChecker instanceof AccountStatusUserDetailsChecker).isTrue();\n\t}\n\n\t@Test\n\tpublic void setSecurityContextRepositoryWhenNullThenThrowException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.switchUserWebFilter.setSecurityContextRepository(null))\n\t\t\t.withMessage(\"securityContextRepository cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setSecurityContextRepositoryWhenDefinedThenChangeDefaultValue() {\n\t\tfinal Object oldSecurityContextRepository = ReflectionTestUtils.getField(this.switchUserWebFilter,\n\t\t\t\t\"securityContextRepository\");\n\t\tassertThat(oldSecurityContextRepository).isSameAs(this.serverSecurityContextRepository);\n\t\tfinal ServerSecurityContextRepository newSecurityContextRepository = mock(\n\t\t\t\tServerSecurityContextRepository.class);\n\t\tthis.switchUserWebFilter.setSecurityContextRepository(newSecurityContextRepository);\n\t\tfinal Object currentSecurityContextRepository = ReflectionTestUtils.getField(this.switchUserWebFilter,\n\t\t\t\t\"securityContextRepository\");\n\t\tassertThat(currentSecurityContextRepository).isSameAs(newSecurityContextRepository);\n\t}\n\n\t@Test\n\tpublic void setExitUserUrlWhenNullThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.switchUserWebFilter.setExitUserUrl(null))\n\t\t\t.withMessage(\"exitUserUrl cannot be empty and must be a valid redirect URL\");\n\t}\n\n\t@Test\n\tpublic void setExitUserUrlWhenInvalidUrlThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.switchUserWebFilter.setExitUserUrl(\"wrongUrl\"))\n\t\t\t.withMessage(\"exitUserUrl cannot be empty and must be a valid redirect URL\");\n\t}\n\n\t@Test\n\tpublic void setExitUserUrlWhenDefinedThenChangeDefaultValue() {\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/logout/impersonate\"));\n\t\tfinal ServerWebExchangeMatcher oldExitUserMatcher = (ServerWebExchangeMatcher) ReflectionTestUtils\n\t\t\t.getField(this.switchUserWebFilter, \"exitUserMatcher\");\n\t\tassertThat(oldExitUserMatcher.matches(exchange).block().isMatch()).isTrue();\n\t\tthis.switchUserWebFilter.setExitUserUrl(\"/exit-url\");\n\t\tfinal MockServerWebExchange newExchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/exit-url\"));\n\t\tfinal ServerWebExchangeMatcher newExitUserMatcher = (ServerWebExchangeMatcher) ReflectionTestUtils\n\t\t\t.getField(this.switchUserWebFilter, \"exitUserMatcher\");\n\t\tassertThat(newExitUserMatcher.matches(newExchange).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void setExitUserMatcherWhenNullThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.switchUserWebFilter.setExitUserMatcher(null))\n\t\t\t.withMessage(\"exitUserMatcher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setExitUserMatcherWhenDefinedThenChangeDefaultValue() {\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/logout/impersonate\"));\n\t\tfinal ServerWebExchangeMatcher oldExitUserMatcher = (ServerWebExchangeMatcher) ReflectionTestUtils\n\t\t\t.getField(this.switchUserWebFilter, \"exitUserMatcher\");\n\t\tassertThat(oldExitUserMatcher.matches(exchange).block().isMatch()).isTrue();\n\t\tfinal ServerWebExchangeMatcher newExitUserMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,\n\t\t\t\t\"/exit-url\");\n\t\tthis.switchUserWebFilter.setExitUserMatcher(newExitUserMatcher);\n\t\tfinal ServerWebExchangeMatcher currentExitUserMatcher = (ServerWebExchangeMatcher) ReflectionTestUtils\n\t\t\t.getField(this.switchUserWebFilter, \"exitUserMatcher\");\n\t\tassertThat(currentExitUserMatcher).isSameAs(newExitUserMatcher);\n\t}\n\n\t@Test\n\tpublic void setSwitchUserUrlWhenNullThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.switchUserWebFilter.setSwitchUserUrl(null))\n\t\t\t.withMessage(\"switchUserUrl cannot be empty and must be a valid redirect URL\");\n\t}\n\n\t@Test\n\tpublic void setSwitchUserUrlWhenInvalidThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.switchUserWebFilter.setSwitchUserUrl(\"wrongUrl\"))\n\t\t\t.withMessage(\"switchUserUrl cannot be empty and must be a valid redirect URL\");\n\t}\n\n\t@Test\n\tpublic void setSwitchUserUrlWhenDefinedThenChangeDefaultValue() {\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/login/impersonate\"));\n\t\tfinal ServerWebExchangeMatcher oldSwitchUserMatcher = (ServerWebExchangeMatcher) ReflectionTestUtils\n\t\t\t.getField(this.switchUserWebFilter, \"switchUserMatcher\");\n\t\tassertThat(oldSwitchUserMatcher.matches(exchange).block().isMatch()).isTrue();\n\t\tthis.switchUserWebFilter.setSwitchUserUrl(\"/switch-url\");\n\t\tfinal MockServerWebExchange newExchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/switch-url\"));\n\t\tfinal ServerWebExchangeMatcher newSwitchUserMatcher = (ServerWebExchangeMatcher) ReflectionTestUtils\n\t\t\t.getField(this.switchUserWebFilter, \"switchUserMatcher\");\n\t\tassertThat(newSwitchUserMatcher.matches(newExchange).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void setSwitchUserMatcherWhenNullThenThrowException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.switchUserWebFilter.setSwitchUserMatcher(null))\n\t\t\t.withMessage(\"switchUserMatcher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void setSwitchUserMatcherWhenDefinedThenChangeDefaultValue() {\n\t\tfinal MockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/login/impersonate\"));\n\t\tfinal ServerWebExchangeMatcher oldSwitchUserMatcher = (ServerWebExchangeMatcher) ReflectionTestUtils\n\t\t\t.getField(this.switchUserWebFilter, \"switchUserMatcher\");\n\t\tassertThat(oldSwitchUserMatcher.matches(exchange).block().isMatch()).isTrue();\n\t\tfinal ServerWebExchangeMatcher newSwitchUserMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST,\n\t\t\t\t\"/switch-url\");\n\t\tthis.switchUserWebFilter.setSwitchUserMatcher(newSwitchUserMatcher);\n\t\tfinal ServerWebExchangeMatcher currentExitUserMatcher = (ServerWebExchangeMatcher) ReflectionTestUtils\n\t\t\t.getField(this.switchUserWebFilter, \"switchUserMatcher\");\n\t\tassertThat(currentExitUserMatcher).isSameAs(newSwitchUserMatcher);\n\t}\n\n\tprivate UserDetails switchUserDetails(String username, boolean enabled) {\n\t\tfinal SimpleGrantedAuthority authority = new SimpleGrantedAuthority(\"ROLE_SWITCH_TEST\");\n\t\treturn new User(username, \"NA\", enabled, true, true, true, Collections.singleton(authority));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/logout/DelegatingServerLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Eric Deandrea\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class DelegatingServerLogoutHandlerTests {\n\n\t@Mock\n\tprivate ServerLogoutHandler delegate1;\n\n\t@Mock\n\tprivate ServerLogoutHandler delegate2;\n\n\tprivate PublisherProbe<Void> delegate1Result = PublisherProbe.empty();\n\n\tprivate PublisherProbe<Void> delegate2Result = PublisherProbe.empty();\n\n\t@Mock\n\tprivate WebFilterExchange exchange;\n\n\t@Mock\n\tprivate Authentication authentication;\n\n\tprivate void givenDelegate1WillReturn() {\n\t\tgiven(this.delegate1.logout(any(WebFilterExchange.class), any(Authentication.class)))\n\t\t\t.willReturn(this.delegate1Result.mono());\n\t}\n\n\tprivate void givenDelegate2WillReturn() {\n\t\tgiven(this.delegate2.logout(any(WebFilterExchange.class), any(Authentication.class)))\n\t\t\t.willReturn(this.delegate2Result.mono());\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullVargsThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingServerLogoutHandler((ServerLogoutHandler[]) null))\n\t\t\t.withMessage(\"delegates cannot be null or empty\")\n\t\t\t.withNoCause();\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullListThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingServerLogoutHandler((List<ServerLogoutHandler>) null))\n\t\t\t.withMessage(\"delegates cannot be null or empty\")\n\t\t\t.withNoCause();\n\t}\n\n\t@Test\n\tpublic void constructorWhenEmptyThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DelegatingServerLogoutHandler(new ServerLogoutHandler[0]))\n\t\t\t.withMessage(\"delegates cannot be null or empty\")\n\t\t\t.withNoCause();\n\t}\n\n\t@Test\n\tpublic void logoutWhenSingleThenExecuted() {\n\t\tgivenDelegate1WillReturn();\n\t\tDelegatingServerLogoutHandler handler = new DelegatingServerLogoutHandler(this.delegate1);\n\t\thandler.logout(this.exchange, this.authentication).block();\n\t\tthis.delegate1Result.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void logoutWhenMultipleThenExecuted() {\n\t\tgivenDelegate1WillReturn();\n\t\tgivenDelegate2WillReturn();\n\t\tDelegatingServerLogoutHandler handler = new DelegatingServerLogoutHandler(this.delegate1, this.delegate2);\n\t\thandler.logout(this.exchange, this.authentication).block();\n\t\tthis.delegate1Result.assertWasSubscribed();\n\t\tthis.delegate2Result.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void logoutSequential() throws Exception {\n\t\tAtomicBoolean slowDone = new AtomicBoolean();\n\t\tCountDownLatch latch = new CountDownLatch(1);\n\t\tServerLogoutHandler slow = (exchange,\n\t\t\t\tauthentication) -> Mono.delay(Duration.ofMillis(100)).doOnSuccess((__) -> slowDone.set(true)).then();\n\t\tServerLogoutHandler second = (exchange, authentication) -> Mono.fromRunnable(() -> {\n\t\t\tlatch.countDown();\n\t\t\tassertThat(slowDone.get()).describedAs(\"ServerLogoutHandler should be executed sequentially\").isTrue();\n\t\t});\n\t\tDelegatingServerLogoutHandler handler = new DelegatingServerLogoutHandler(slow, second);\n\t\thandler.logout(this.exchange, this.authentication).block();\n\t\tassertThat(latch.await(3, TimeUnit.SECONDS)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/logout/HeaderWriterServerLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.header.ServerHttpHeadersWriter;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author MD Sayem Ahmed\n * @since 5.2\n */\npublic class HeaderWriterServerLogoutHandlerTests {\n\n\t@Test\n\tpublic void constructorWhenHeadersWriterIsNullThenExceptionThrown() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new HeaderWriterServerLogoutHandler(null));\n\t}\n\n\t@Test\n\tpublic void logoutWhenInvokedThenWritesResponseHeaders() {\n\t\tServerHttpHeadersWriter headersWriter = mock(ServerHttpHeadersWriter.class);\n\t\tHeaderWriterServerLogoutHandler handler = new HeaderWriterServerLogoutHandler(headersWriter);\n\t\tServerWebExchange serverWebExchange = mock(ServerWebExchange.class);\n\t\tWebFilterExchange filterExchange = mock(WebFilterExchange.class);\n\t\tgiven(filterExchange.getExchange()).willReturn(serverWebExchange);\n\t\tAuthentication authentication = mock(Authentication.class);\n\t\thandler.logout(filterExchange, authentication);\n\t\tverify(headersWriter).writeHttpHeaders(serverWebExchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/logout/HttpStatusReturningServerLogoutSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Eric Deandrea\n * @since 5.1\n */\npublic class HttpStatusReturningServerLogoutSuccessHandlerTests {\n\n\t@Test\n\tpublic void defaultHttpStatusBeingReturned() {\n\t\tWebFilterExchange filterExchange = buildFilterExchange();\n\t\tnew HttpStatusReturningServerLogoutSuccessHandler().onLogoutSuccess(filterExchange, mock(Authentication.class))\n\t\t\t.block();\n\t\tassertThat(filterExchange.getExchange().getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);\n\t}\n\n\t@Test\n\tpublic void customHttpStatusBeingReturned() {\n\t\tWebFilterExchange filterExchange = buildFilterExchange();\n\t\tnew HttpStatusReturningServerLogoutSuccessHandler(HttpStatus.NO_CONTENT)\n\t\t\t.onLogoutSuccess(filterExchange, mock(Authentication.class))\n\t\t\t.block();\n\t\tassertThat(filterExchange.getExchange().getResponse().getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);\n\t}\n\n\t@Test\n\tpublic void nullHttpStatusThrowsException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new HttpStatusReturningServerLogoutSuccessHandler(null))\n\t\t\t.withMessage(\"The provided HttpStatus must not be null.\");\n\t}\n\n\tprivate static WebFilterExchange buildFilterExchange() {\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/\").build();\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(request);\n\t\treturn new WebFilterExchange(exchange, mock(WebFilterChain.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/logout/LogoutWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Eric Deandrea\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class LogoutWebFilterTests {\n\n\t@Mock\n\tprivate ServerLogoutHandler handler1;\n\n\t@Mock\n\tprivate ServerLogoutHandler handler2;\n\n\t@Mock\n\tprivate ServerLogoutHandler handler3;\n\n\tprivate LogoutWebFilter logoutWebFilter = new LogoutWebFilter();\n\n\t@Test\n\tpublic void defaultLogoutHandler() {\n\t\tassertThat(getLogoutHandler()).isNotNull().isExactlyInstanceOf(SecurityContextServerLogoutHandler.class);\n\t}\n\n\t@Test\n\tpublic void singleLogoutHandler() {\n\t\tthis.logoutWebFilter.setLogoutHandler(this.handler1);\n\t\tthis.logoutWebFilter.setLogoutHandler(this.handler2);\n\t\tassertThat(getLogoutHandler()).isNotNull()\n\t\t\t.isInstanceOf(ServerLogoutHandler.class)\n\t\t\t.isNotInstanceOf(SecurityContextServerLogoutHandler.class)\n\t\t\t.extracting(ServerLogoutHandler::getClass)\n\t\t\t.isEqualTo(this.handler2.getClass());\n\t}\n\n\t@Test\n\tpublic void multipleLogoutHandlers() {\n\t\tthis.logoutWebFilter\n\t\t\t.setLogoutHandler(new DelegatingServerLogoutHandler(this.handler1, this.handler2, this.handler3));\n\t\tassertThat(getLogoutHandler()).isNotNull()\n\t\t\t.isExactlyInstanceOf(DelegatingServerLogoutHandler.class)\n\t\t\t.extracting((delegatingLogoutHandler) -> ((Collection<ServerLogoutHandler>) ReflectionTestUtils\n\t\t\t\t.getField(delegatingLogoutHandler, DelegatingServerLogoutHandler.class, \"delegates\")).stream()\n\t\t\t\t.map(ServerLogoutHandler::getClass)\n\t\t\t\t.collect(Collectors.toList()))\n\t\t\t.isEqualTo(Arrays.asList(this.handler1.getClass(), this.handler2.getClass(), this.handler3.getClass()));\n\t}\n\n\tprivate ServerLogoutHandler getLogoutHandler() {\n\t\treturn (ServerLogoutHandler) ReflectionTestUtils.getField(this.logoutWebFilter, LogoutWebFilter.class,\n\t\t\t\t\"logoutHandler\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/logout/WebSessionServerLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.logout;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class WebSessionServerLogoutHandlerTests {\n\n\t@Mock\n\tServerWebExchange webExchange;\n\n\t@Mock\n\tWebFilterExchange filterExchange;\n\n\t@Mock\n\tWebSession webSession;\n\n\t@Test\n\tpublic void shouldInvalidateWebSession() {\n\t\tdoReturn(this.webExchange).when(this.filterExchange).getExchange();\n\t\tdoReturn(Mono.just(this.webSession)).when(this.webExchange).getSession();\n\t\tdoReturn(Mono.empty()).when(this.webSession).invalidate();\n\n\t\tWebSessionServerLogoutHandler handler = new WebSessionServerLogoutHandler();\n\t\thandler.logout(this.filterExchange, mock(Authentication.class)).block();\n\n\t\tverify(this.webSession).invalidate();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ott/DefaultServerGenerateOneTimeTokenRequestResolverTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport java.time.Duration;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link DefaultServerGenerateOneTimeTokenRequestResolver}\n *\n * @author Max Batischev\n */\npublic class DefaultServerGenerateOneTimeTokenRequestResolverTests {\n\n\tprivate final DefaultServerGenerateOneTimeTokenRequestResolver resolver = new DefaultServerGenerateOneTimeTokenRequestResolver();\n\n\t@Test\n\tvoid resolveWhenUsernameParameterIsPresentThenResolvesGenerateRequest() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/ott/generate\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"username=user\"));\n\n\t\tGenerateOneTimeTokenRequest request = this.resolver.resolve(exchange).block();\n\n\t\tassertThat(request).isNotNull();\n\t\tassertThat(request.getUsername()).isEqualTo(\"user\");\n\t\tassertThat(request.getExpiresIn()).isEqualTo(Duration.ofMinutes(5));\n\t}\n\n\t@Test\n\tvoid resolveWhenUsernameParameterIsNotPresentThenNull() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/ott/generate\").contentType(MediaType.APPLICATION_FORM_URLENCODED));\n\n\t\tGenerateOneTimeTokenRequest request = this.resolver.resolve(exchange).block();\n\n\t\tassertThat(request).isNull();\n\t}\n\n\t@Test\n\tvoid resolveWhenExpiresInSetThenResolvesGenerateRequest() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/ott/generate\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"username=user\"));\n\t\tthis.resolver.setExpiresIn(Duration.ofSeconds(600));\n\n\t\tGenerateOneTimeTokenRequest generateRequest = this.resolver.resolve(exchange).block();\n\n\t\tassertThat(generateRequest.getExpiresIn()).isEqualTo(Duration.ofSeconds(600));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ott/GenerateOneTimeTokenWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport java.time.Instant;\n\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentMatchers;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.ott.DefaultOneTimeToken;\nimport org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;\nimport org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link GenerateOneTimeTokenWebFilter}\n *\n * @author Max Batischev\n */\npublic class GenerateOneTimeTokenWebFilterTests {\n\n\tprivate final ReactiveOneTimeTokenService oneTimeTokenService = mock(ReactiveOneTimeTokenService.class);\n\n\tprivate final ServerRedirectOneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler = new ServerRedirectOneTimeTokenGenerationSuccessHandler(\n\t\t\t\"/login/ott\");\n\n\tprivate static final String TOKEN = \"token\";\n\n\tprivate static final String USERNAME = \"user\";\n\n\t@Test\n\tvoid filterWhenUsernameFormParamIsPresentThenSuccess() {\n\t\tgiven(this.oneTimeTokenService.generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class)))\n\t\t\t.willReturn(Mono.just(new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now())));\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/ott/generate\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(\"username=user\"));\n\t\tGenerateOneTimeTokenWebFilter filter = new GenerateOneTimeTokenWebFilter(this.oneTimeTokenService,\n\t\t\t\tthis.oneTimeTokenGenerationSuccessHandler);\n\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\n\t\tverify(this.oneTimeTokenService).generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class));\n\t\tAssertions.assertThat(exchange.getResponse().getHeaders().getLocation()).hasPath(\"/login/ott\");\n\t}\n\n\t@Test\n\tvoid filterWhenUsernameFormParamIsEmptyThenNull() {\n\t\tgiven(this.oneTimeTokenService.generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class)))\n\t\t\t.willReturn(Mono.just(new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now())));\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.post(\"/ott/generate\");\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(request);\n\t\tGenerateOneTimeTokenWebFilter filter = new GenerateOneTimeTokenWebFilter(this.oneTimeTokenService,\n\t\t\t\tthis.oneTimeTokenGenerationSuccessHandler);\n\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\n\t\tverify(this.oneTimeTokenService, never()).generate(ArgumentMatchers.any(GenerateOneTimeTokenRequest.class));\n\t}\n\n\t@Test\n\tpublic void constructorWhenOneTimeTokenServiceNullThenIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new GenerateOneTimeTokenWebFilter(null, this.oneTimeTokenGenerationSuccessHandler));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void setWhenRequestMatcherNullThenIllegalArgumentException() {\n\t\tGenerateOneTimeTokenWebFilter filter = new GenerateOneTimeTokenWebFilter(this.oneTimeTokenService,\n\t\t\t\tthis.oneTimeTokenGenerationSuccessHandler);\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> filter.setRequestMatcher(null));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ott/ServerOneTimeTokenAuthenticationConverterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;\nimport org.springframework.security.core.Authentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ServerOneTimeTokenAuthenticationConverter}\n *\n * @author Max Batischev\n */\npublic class ServerOneTimeTokenAuthenticationConverterTests {\n\n\tprivate final ServerOneTimeTokenAuthenticationConverter converter = new ServerOneTimeTokenAuthenticationConverter();\n\n\tprivate static final String TOKEN = \"token\";\n\n\tprivate static final String USERNAME = \"Max\";\n\n\t@Test\n\tvoid convertWhenTokenParameterThenReturnOneTimeTokenAuthenticationToken() {\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\").queryParam(\"token\", TOKEN);\n\n\t\tOneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter\n\t\t\t.convert(MockServerWebExchange.from(request))\n\t\t\t.block();\n\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getTokenValue()).isEqualTo(TOKEN);\n\t\tassertThat(authentication.getPrincipal()).isNull();\n\t}\n\n\t@Test\n\tvoid convertWhenOnlyUsernameParameterThenReturnNull() {\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\").queryParam(\"username\", USERNAME);\n\n\t\tOneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter\n\t\t\t.convert(MockServerWebExchange.from(request))\n\t\t\t.block();\n\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tvoid convertWhenNoTokenParameterThenNull() {\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/\");\n\n\t\tAuthentication authentication = this.converter.convert(MockServerWebExchange.from(request)).block();\n\n\t\tassertThat(authentication).isNull();\n\t}\n\n\t@Test\n\tvoid convertWhenTokenEncodedFormParameterThenReturnOneTimeTokenAuthenticationToken() {\n\t\t// @formatter:off\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t\t.body(\"token=token\"));\n\n\t\t// @formatter:on\n\n\t\tOneTimeTokenAuthenticationToken authentication = (OneTimeTokenAuthenticationToken) this.converter\n\t\t\t.convert(exchange)\n\t\t\t.block();\n\n\t\tassertThat(authentication).isNotNull();\n\t\tassertThat(authentication.getTokenValue()).isEqualTo(TOKEN);\n\t\tassertThat(authentication.getPrincipal()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/ott/ServerRedirectOneTimeTokenGenerationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.ott;\n\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.ott.DefaultOneTimeToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ServerRedirectOneTimeTokenGenerationSuccessHandler}\n *\n * @author Max Batischev\n */\npublic class ServerRedirectOneTimeTokenGenerationSuccessHandlerTests {\n\n\tprivate static final String TOKEN = \"token\";\n\n\tprivate static final String USERNAME = \"Max\";\n\n\tprivate final MockServerHttpRequest request = MockServerHttpRequest.get(\"/\").build();\n\n\t@Test\n\tvoid handleThenRedirectToDefaultLocation() {\n\t\tServerOneTimeTokenGenerationSuccessHandler handler = new ServerRedirectOneTimeTokenGenerationSuccessHandler(\n\t\t\t\t\"/login/ott\");\n\t\tMockServerWebExchange webExchange = MockServerWebExchange.from(this.request);\n\n\t\thandler.handle(webExchange, new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now())).block();\n\n\t\tassertThat(webExchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(webExchange.getResponse().getHeaders().getLocation()).hasPath(\"/login/ott\");\n\t}\n\n\t@Test\n\tvoid handleWhenUrlChangedThenRedirectToUrl() {\n\t\tServerOneTimeTokenGenerationSuccessHandler handler = new ServerRedirectOneTimeTokenGenerationSuccessHandler(\n\t\t\t\t\"/redirected\");\n\t\tMockServerWebExchange webExchange = MockServerWebExchange.from(this.request);\n\n\t\thandler.handle(webExchange, new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now())).block();\n\n\t\tassertThat(webExchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FOUND);\n\t\tassertThat(webExchange.getResponse().getHeaders().getLocation()).hasPath(\"/redirected\");\n\t}\n\n\t@Test\n\tvoid setRedirectUrlWhenNullOrEmptyThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerRedirectOneTimeTokenGenerationSuccessHandler(null))\n\t\t\t.withMessage(\"redirectUri cannot be empty or null\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerRedirectOneTimeTokenGenerationSuccessHandler(\"\"))\n\t\t\t.withMessage(\"redirectUri cannot be empty or null\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/session/ConcurrentSessionControlServerAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.session;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpResponse;\nimport org.springframework.mock.web.server.MockWebSession;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.ReactiveSessionInformation;\nimport org.springframework.security.core.session.ReactiveSessionRegistry;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.ConcurrentSessionControlServerAuthenticationSuccessHandler;\nimport org.springframework.security.web.server.authentication.MaximumSessionsContext;\nimport org.springframework.security.web.server.authentication.ServerMaximumSessionsExceededHandler;\nimport org.springframework.security.web.server.authentication.SessionLimit;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * Tests for {@link ConcurrentSessionControlServerAuthenticationSuccessHandler}.\n *\n * @author Marcus da Coregio\n */\nclass ConcurrentSessionControlServerAuthenticationSuccessHandlerTests {\n\n\tprivate ConcurrentSessionControlServerAuthenticationSuccessHandler strategy;\n\n\tReactiveSessionRegistry sessionRegistry = mock();\n\n\tServerWebExchange exchange = mock();\n\n\tWebFilterChain chain = mock();\n\n\tServerMaximumSessionsExceededHandler handler = mock();\n\n\tArgumentCaptor<MaximumSessionsContext> contextCaptor = ArgumentCaptor.forClass(MaximumSessionsContext.class);\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tgiven(this.exchange.getResponse()).willReturn(new MockServerHttpResponse());\n\t\tgiven(this.exchange.getRequest()).willReturn(MockServerHttpRequest.get(\"/\").build());\n\t\tgiven(this.exchange.getSession()).willReturn(Mono.just(new MockWebSession()));\n\t\tgiven(this.handler.handle(any())).willReturn(Mono.empty());\n\t\tthis.strategy = new ConcurrentSessionControlServerAuthenticationSuccessHandler(this.sessionRegistry,\n\t\t\t\tthis.handler);\n\t}\n\n\t@Test\n\tvoid constructorWhenNullRegistryThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ConcurrentSessionControlServerAuthenticationSuccessHandler(null, this.handler))\n\t\t\t.withMessage(\"sessionRegistry cannot be null\");\n\t}\n\n\t@Test\n\tvoid constructorWhenNullHandlerThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(\n\t\t\t\t\t() -> new ConcurrentSessionControlServerAuthenticationSuccessHandler(this.sessionRegistry, null))\n\t\t\t.withMessage(\"maximumSessionsExceededHandler cannot be null\");\n\t}\n\n\t@Test\n\tvoid setMaximumSessionsForAuthenticationWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.strategy.setSessionLimit(null))\n\t\t\t.withMessage(\"sessionLimit cannot be null\");\n\t}\n\n\t@Test\n\tvoid onAuthenticationWhenSessionLimitIsUnlimitedThenDoNothing() {\n\t\tServerMaximumSessionsExceededHandler handler = mock(ServerMaximumSessionsExceededHandler.class);\n\t\tthis.strategy = new ConcurrentSessionControlServerAuthenticationSuccessHandler(this.sessionRegistry, handler);\n\t\tthis.strategy.setSessionLimit(SessionLimit.UNLIMITED);\n\t\tthis.strategy.onAuthenticationSuccess(null, TestAuthentication.authenticatedUser()).block();\n\t\tverifyNoInteractions(handler, this.sessionRegistry);\n\t}\n\n\t@Test\n\tvoid onAuthenticationWhenMaximumSessionsIsOneAndExceededThenHandlerIsCalled() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tList<ReactiveSessionInformation> sessions = Arrays.asList(createSessionInformation(\"100\"),\n\t\t\t\tcreateSessionInformation(\"101\"));\n\t\tgiven(this.sessionRegistry.getAllSessions(authentication.getPrincipal()))\n\t\t\t.willReturn(Flux.fromIterable(sessions));\n\t\tthis.strategy.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), authentication).block();\n\t\tverify(this.handler).handle(this.contextCaptor.capture());\n\t\tassertThat(this.contextCaptor.getValue().getMaximumSessionsAllowed()).isEqualTo(1);\n\t\tassertThat(this.contextCaptor.getValue().getSessions()).isEqualTo(sessions);\n\t\tassertThat(this.contextCaptor.getValue().getAuthentication()).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tvoid onAuthenticationWhenMaximumSessionsIsGreaterThanOneAndExceededThenHandlerIsCalled() {\n\t\tthis.strategy.setSessionLimit(SessionLimit.of(5));\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tList<ReactiveSessionInformation> sessions = Arrays.asList(createSessionInformation(\"100\"),\n\t\t\t\tcreateSessionInformation(\"101\"), createSessionInformation(\"102\"), createSessionInformation(\"103\"),\n\t\t\t\tcreateSessionInformation(\"104\"));\n\t\tgiven(this.sessionRegistry.getAllSessions(authentication.getPrincipal()))\n\t\t\t.willReturn(Flux.fromIterable(sessions));\n\t\tthis.strategy.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), authentication).block();\n\t\tverify(this.handler).handle(this.contextCaptor.capture());\n\t\tassertThat(this.contextCaptor.getValue().getMaximumSessionsAllowed()).isEqualTo(5);\n\t\tassertThat(this.contextCaptor.getValue().getSessions()).isEqualTo(sessions);\n\t\tassertThat(this.contextCaptor.getValue().getAuthentication()).isEqualTo(authentication);\n\t}\n\n\t@Test\n\tvoid onAuthenticationWhenMaximumSessionsForUsersAreDifferentThenHandlerIsCalledWhereNeeded() {\n\t\tAuthentication user = TestAuthentication.authenticatedUser();\n\t\tAuthentication admin = TestAuthentication.authenticatedAdmin();\n\t\tthis.strategy.setSessionLimit((authentication) -> {\n\t\t\tif (authentication.equals(user)) {\n\t\t\t\treturn Mono.just(1);\n\t\t\t}\n\t\t\treturn Mono.just(3);\n\t\t});\n\n\t\tList<ReactiveSessionInformation> userSessions = Arrays.asList(createSessionInformation(\"100\"));\n\t\tList<ReactiveSessionInformation> adminSessions = Arrays.asList(createSessionInformation(\"200\"),\n\t\t\t\tcreateSessionInformation(\"201\"));\n\n\t\tgiven(this.sessionRegistry.getAllSessions(user.getPrincipal())).willReturn(Flux.fromIterable(userSessions));\n\t\tgiven(this.sessionRegistry.getAllSessions(admin.getPrincipal())).willReturn(Flux.fromIterable(adminSessions));\n\n\t\tthis.strategy.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), user).block();\n\t\tthis.strategy.onAuthenticationSuccess(new WebFilterExchange(this.exchange, this.chain), admin).block();\n\t\tverify(this.handler).handle(this.contextCaptor.capture());\n\t\tassertThat(this.contextCaptor.getValue().getMaximumSessionsAllowed()).isEqualTo(1);\n\t\tassertThat(this.contextCaptor.getValue().getSessions()).isEqualTo(userSessions);\n\t\tassertThat(this.contextCaptor.getValue().getAuthentication()).isEqualTo(user);\n\t}\n\n\tprivate ReactiveSessionInformation createSessionInformation(String sessionId) {\n\t\treturn new ReactiveSessionInformation(sessionId, \"principal\", Instant.now());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/session/InMemoryReactiveSessionRegistryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.session;\n\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.ZoneOffset;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.InMemoryReactiveSessionRegistry;\nimport org.springframework.security.core.session.ReactiveSessionInformation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link InMemoryReactiveSessionRegistry}.\n */\nclass InMemoryReactiveSessionRegistryTests {\n\n\tInMemoryReactiveSessionRegistry sessionRegistry = new InMemoryReactiveSessionRegistry();\n\n\tInstant now = LocalDate.of(2023, 11, 21).atStartOfDay().toInstant(ZoneOffset.UTC);\n\n\t@Test\n\tvoid saveWhenPrincipalThenRegisterPrincipalSession() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tReactiveSessionInformation sessionInformation = new ReactiveSessionInformation(authentication.getPrincipal(),\n\t\t\t\t\"1234\", this.now);\n\t\tthis.sessionRegistry.saveSessionInformation(sessionInformation).block();\n\t\tList<ReactiveSessionInformation> principalSessions = this.sessionRegistry\n\t\t\t.getAllSessions(authentication.getPrincipal())\n\t\t\t.collectList()\n\t\t\t.block();\n\t\tassertThat(principalSessions).hasSize(1);\n\t\tassertThat(this.sessionRegistry.getSessionInformation(\"1234\").block()).isNotNull();\n\t}\n\n\t@Test\n\tvoid getAllSessionsWhenMultipleSessionsThenReturnAll() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tReactiveSessionInformation sessionInformation1 = new ReactiveSessionInformation(authentication.getPrincipal(),\n\t\t\t\t\"1234\", this.now);\n\t\tReactiveSessionInformation sessionInformation2 = new ReactiveSessionInformation(authentication.getPrincipal(),\n\t\t\t\t\"4321\", this.now);\n\t\tReactiveSessionInformation sessionInformation3 = new ReactiveSessionInformation(authentication.getPrincipal(),\n\t\t\t\t\"9876\", this.now);\n\t\tthis.sessionRegistry.saveSessionInformation(sessionInformation1).block();\n\t\tthis.sessionRegistry.saveSessionInformation(sessionInformation2).block();\n\t\tthis.sessionRegistry.saveSessionInformation(sessionInformation3).block();\n\t\tList<ReactiveSessionInformation> sessions = this.sessionRegistry.getAllSessions(authentication.getPrincipal())\n\t\t\t.collectList()\n\t\t\t.block();\n\t\tassertThat(sessions).hasSize(3);\n\t\tassertThat(this.sessionRegistry.getSessionInformation(\"1234\").block()).isNotNull();\n\t\tassertThat(this.sessionRegistry.getSessionInformation(\"4321\").block()).isNotNull();\n\t\tassertThat(this.sessionRegistry.getSessionInformation(\"9876\").block()).isNotNull();\n\t}\n\n\t@Test\n\tvoid removeSessionInformationThenSessionIsRemoved() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tReactiveSessionInformation sessionInformation = new ReactiveSessionInformation(authentication.getPrincipal(),\n\t\t\t\t\"1234\", this.now);\n\t\tthis.sessionRegistry.saveSessionInformation(sessionInformation).block();\n\t\tthis.sessionRegistry.removeSessionInformation(\"1234\").block();\n\t\tList<ReactiveSessionInformation> sessions = this.sessionRegistry.getAllSessions(authentication.getName())\n\t\t\t.collectList()\n\t\t\t.block();\n\t\tassertThat(this.sessionRegistry.getSessionInformation(\"1234\").block()).isNull();\n\t\tassertThat(sessions).isEmpty();\n\t}\n\n\t@Test\n\tvoid updateLastAccessTimeThenUpdated() {\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tReactiveSessionInformation sessionInformation = new ReactiveSessionInformation(authentication.getPrincipal(),\n\t\t\t\t\"1234\", this.now);\n\t\tthis.sessionRegistry.saveSessionInformation(sessionInformation).block();\n\t\tReactiveSessionInformation saved = this.sessionRegistry.getSessionInformation(\"1234\").block();\n\t\tassertThat(saved.getLastAccessTime()).isNotNull();\n\t\tInstant lastAccessTimeBefore = saved.getLastAccessTime();\n\t\tthis.sessionRegistry.updateLastAccessTime(\"1234\").block();\n\t\tsaved = this.sessionRegistry.getSessionInformation(\"1234\").block();\n\t\tassertThat(saved.getLastAccessTime()).isNotNull();\n\t\tassertThat(saved.getLastAccessTime()).isAfter(lastAccessTimeBefore);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/session/InvalidateLeastUsedServerMaximumSessionsExceededHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.session;\n\nimport java.time.Instant;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.web.server.MockWebSession;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.ReactiveSessionInformation;\nimport org.springframework.security.web.server.authentication.InvalidateLeastUsedServerMaximumSessionsExceededHandler;\nimport org.springframework.security.web.server.authentication.MaximumSessionsContext;\nimport org.springframework.web.server.session.InMemoryWebSessionStore;\nimport org.springframework.web.server.session.WebSessionStore;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.atLeastOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests for {@link InvalidateLeastUsedServerMaximumSessionsExceededHandler}\n *\n * @author Marcus da Coregio\n */\nclass InvalidateLeastUsedServerMaximumSessionsExceededHandlerTests {\n\n\tInvalidateLeastUsedServerMaximumSessionsExceededHandler handler;\n\n\tWebSessionStore webSessionStore = spy(new InMemoryWebSessionStore());\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.handler = new InvalidateLeastUsedServerMaximumSessionsExceededHandler(this.webSessionStore);\n\t}\n\n\t@Test\n\tvoid handleWhenInvokedThenInvalidatesLeastRecentlyUsedSessions() {\n\t\tReactiveSessionInformation session1 = mock(ReactiveSessionInformation.class);\n\t\tReactiveSessionInformation session2 = mock(ReactiveSessionInformation.class);\n\t\tgiven(session1.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760010L));\n\t\tgiven(session2.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760000L));\n\t\tgiven(session1.getSessionId()).willReturn(\"session1\");\n\t\tgiven(session2.getSessionId()).willReturn(\"session2\");\n\t\tgiven(session2.invalidate()).willReturn(Mono.empty());\n\n\t\tMaximumSessionsContext context = new MaximumSessionsContext(mock(Authentication.class),\n\t\t\t\tList.of(session1, session2), 2, createWebSession());\n\n\t\tthis.handler.handle(context).block();\n\n\t\tverify(session2).invalidate();\n\t\tverify(session1).getLastAccessTime(); // used by comparator to sort the sessions\n\t\tverify(session2).getLastAccessTime(); // used by comparator to sort the sessions\n\t\tverify(session1).getSessionId();\n\t\tverify(session2, times(2)).getSessionId(); // used to invalidate session against\n\t\t\t\t\t\t\t\t\t\t\t\t\t// the\n\t\t// WebSessionStore\n\t\tverify(this.webSessionStore).removeSession(\"session2\");\n\t\tverifyNoMoreInteractions(this.webSessionStore);\n\t\tverifyNoMoreInteractions(session2);\n\t\tverifyNoMoreInteractions(session1);\n\t}\n\n\t@Test\n\tvoid handleWhenMoreThanOneSessionToInvalidateThenInvalidatesAllOfThem() {\n\t\tReactiveSessionInformation session1 = mock(ReactiveSessionInformation.class);\n\t\tReactiveSessionInformation session2 = mock(ReactiveSessionInformation.class);\n\t\tReactiveSessionInformation session3 = mock(ReactiveSessionInformation.class);\n\t\tgiven(session1.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760010L));\n\t\tgiven(session2.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760020L));\n\t\tgiven(session3.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760030L));\n\t\tgiven(session1.invalidate()).willReturn(Mono.empty());\n\t\tgiven(session2.invalidate()).willReturn(Mono.empty());\n\t\tgiven(session1.getSessionId()).willReturn(\"session1\");\n\t\tgiven(session2.getSessionId()).willReturn(\"session2\");\n\t\tgiven(session3.getSessionId()).willReturn(\"session3\");\n\n\t\tMaximumSessionsContext context = new MaximumSessionsContext(mock(Authentication.class),\n\t\t\t\tList.of(session1, session2, session3), 2, createWebSession());\n\t\tthis.handler.handle(context).block();\n\n\t\t// @formatter:off\n\t\tverify(session1).invalidate();\n\t\tverify(session2).invalidate();\n\t\tverify(session1, times(2)).getSessionId();\n\t\tverify(session2, times(2)).getSessionId();\n\t\tverify(session3).getSessionId();\n\t\tverify(session1, atLeastOnce()).getLastAccessTime(); // used by comparator to sort the sessions\n\t\tverify(session2, atLeastOnce()).getLastAccessTime(); // used by comparator to sort the sessions\n\t\tverify(session3, atLeastOnce()).getLastAccessTime(); // used by comparator to sort the sessions\n\t\tverify(this.webSessionStore).removeSession(\"session1\");\n\t\tverify(this.webSessionStore).removeSession(\"session2\");\n\t\tverifyNoMoreInteractions(this.webSessionStore);\n\t\tverifyNoMoreInteractions(session1);\n\t\tverifyNoMoreInteractions(session2);\n\t\tverifyNoMoreInteractions(session3);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid handleWhenCurrentSessionIsRegisteredThenDoNotInvalidateCurrentSession() {\n\t\tReactiveSessionInformation session1 = mock(ReactiveSessionInformation.class);\n\t\tReactiveSessionInformation session2 = mock(ReactiveSessionInformation.class);\n\t\tMockWebSession currentSession = createWebSession();\n\t\tgiven(session1.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760010L));\n\t\tgiven(session2.getLastAccessTime()).willReturn(Instant.ofEpochMilli(1700827760000L));\n\t\tgiven(session1.getSessionId()).willReturn(\"session1\");\n\t\tgiven(session2.getSessionId()).willReturn(currentSession.getId());\n\t\tgiven(session1.invalidate()).willReturn(Mono.empty());\n\n\t\tMaximumSessionsContext context = new MaximumSessionsContext(mock(Authentication.class),\n\t\t\t\tList.of(session1, session2), 1, currentSession);\n\n\t\tthis.handler.handle(context).block();\n\n\t\tverify(session1).invalidate();\n\t\tverify(session2).getSessionId();\n\t\tverify(session1, times(2)).getSessionId();\n\t\tverify(this.webSessionStore).removeSession(\"session1\");\n\t\tverifyNoMoreInteractions(this.webSessionStore);\n\t\tverifyNoMoreInteractions(session2);\n\t\tverifyNoMoreInteractions(session1);\n\t}\n\n\tprivate MockWebSession createWebSession() {\n\t\treturn new MockWebSession();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/session/PreventLoginServerMaximumSessionsExceededHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.session;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\nimport org.springframework.security.web.server.authentication.MaximumSessionsContext;\nimport org.springframework.security.web.server.authentication.PreventLoginServerMaximumSessionsExceededHandler;\nimport org.springframework.web.server.WebSession;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link PreventLoginServerMaximumSessionsExceededHandler}.\n *\n * @author Marcus da Coregio\n */\nclass PreventLoginServerMaximumSessionsExceededHandlerTests {\n\n\t@Test\n\tvoid handleWhenInvokedThenInvalidateWebSessionAndThrowsSessionAuthenticationException() {\n\t\tPreventLoginServerMaximumSessionsExceededHandler handler = new PreventLoginServerMaximumSessionsExceededHandler();\n\t\tWebSession webSession = mock();\n\t\tgiven(webSession.invalidate()).willReturn(Mono.empty());\n\t\tMaximumSessionsContext context = new MaximumSessionsContext(TestAuthentication.authenticatedUser(),\n\t\t\t\tCollections.emptyList(), 1, webSession);\n\t\tStepVerifier.create(handler.handle(context)).expectErrorSatisfies((ex) -> {\n\t\t\tassertThat(ex).isInstanceOf(SessionAuthenticationException.class);\n\t\t\tassertThat(ex.getMessage()).isEqualTo(\"Maximum sessions exceeded\");\n\t\t}).verify();\n\t\tverify(webSession).invalidate();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authentication/session/RegisterSessionServerAuthenticationSuccessHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authentication.session;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.mock.web.server.MockWebSession;\nimport org.springframework.security.authentication.TestAuthentication;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.session.ReactiveSessionInformation;\nimport org.springframework.security.core.session.ReactiveSessionRegistry;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.security.web.server.authentication.RegisterSessionServerAuthenticationSuccessHandler;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.WebSession;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\nclass RegisterSessionServerAuthenticationSuccessHandlerTests {\n\n\t@InjectMocks\n\tRegisterSessionServerAuthenticationSuccessHandler strategy;\n\n\t@Mock\n\tReactiveSessionRegistry sessionRegistry;\n\n\t@Mock\n\tWebFilterChain filterChain;\n\n\tWebSession session = new MockWebSession();\n\n\tServerWebExchange serverWebExchange = MockServerWebExchange.builder(MockServerHttpRequest.get(\"\"))\n\t\t.session(this.session)\n\t\t.build();\n\n\t@Test\n\tvoid constructorWhenSessionRegistryNullThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new RegisterSessionServerAuthenticationSuccessHandler(null))\n\t\t\t.withMessage(\"sessionRegistry cannot be null\");\n\t}\n\n\t@Test\n\tvoid onAuthenticationWhenSessionExistsThenSaveSessionInformation() {\n\t\tgiven(this.sessionRegistry.saveSessionInformation(any())).willReturn(Mono.empty());\n\t\tWebFilterExchange webFilterExchange = new WebFilterExchange(this.serverWebExchange, this.filterChain);\n\t\tAuthentication authentication = TestAuthentication.authenticatedUser();\n\t\tthis.strategy.onAuthenticationSuccess(webFilterExchange, authentication).block();\n\t\tArgumentCaptor<ReactiveSessionInformation> captor = ArgumentCaptor.forClass(ReactiveSessionInformation.class);\n\t\tverify(this.sessionRegistry).saveSessionInformation(captor.capture());\n\t\tassertThat(captor.getValue().getSessionId()).isEqualTo(this.session.getId());\n\t\tassertThat(captor.getValue().getLastAccessTime()).isEqualTo(this.session.getLastAccessTime());\n\t\tassertThat(captor.getValue().getPrincipal()).isEqualTo(authentication.getPrincipal());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authorization/AuthorizationWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class AuthorizationWebFilterTests {\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\t@Mock\n\tprivate WebFilterChain chain;\n\n\tPublisherProbe<Void> chainResult = PublisherProbe.empty();\n\n\t@Test\n\tpublic void filterWhenNoSecurityContextThenThrowsAccessDenied() {\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(this.chainResult.mono());\n\t\tAuthorizationWebFilter filter = new AuthorizationWebFilter(\n\t\t\t\t(a, e) -> Mono.error(new AccessDeniedException(\"Denied\")));\n\t\tMono<Void> result = filter.filter(this.exchange, this.chain);\n\t\tStepVerifier.create(result).expectError(AccessDeniedException.class).verify();\n\t\tthis.chainResult.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenNoAuthenticationThenThrowsAccessDenied() {\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(this.chainResult.mono());\n\t\tAuthorizationWebFilter filter = new AuthorizationWebFilter(\n\t\t\t\t(a, e) -> a.flatMap((auth) -> Mono.error(new AccessDeniedException(\"Denied\"))));\n\t\tMono<Void> result = filter.filter(this.exchange, this.chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(new SecurityContextImpl())));\n\t\tStepVerifier.create(result).expectError(AccessDeniedException.class).verify();\n\t\tthis.chainResult.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenAuthenticationThenThrowsAccessDenied() {\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(this.chainResult.mono());\n\t\tAuthorizationWebFilter filter = new AuthorizationWebFilter(\n\t\t\t\t(a, e) -> Mono.error(new AccessDeniedException(\"Denied\")));\n\t\tMono<Void> result = filter.filter(this.exchange, this.chain)\n\t\t\t.contextWrite(\n\t\t\t\t\tReactiveSecurityContextHolder.withAuthentication(new TestingAuthenticationToken(\"a\", \"b\", \"R\")));\n\t\tStepVerifier.create(result).expectError(AccessDeniedException.class).verify();\n\t\tthis.chainResult.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenDoesNotAccessAuthenticationThenSecurityContextNotSubscribed() {\n\t\tPublisherProbe<SecurityContext> context = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(this.chainResult.mono());\n\t\tAuthorizationWebFilter filter = new AuthorizationWebFilter(\n\t\t\t\t(a, e) -> Mono.error(new AccessDeniedException(\"Denied\")));\n\t\tMono<Void> result = filter.filter(this.exchange, this.chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(context.mono()));\n\t\tStepVerifier.create(result).expectError(AccessDeniedException.class).verify();\n\t\tthis.chainResult.assertWasNotSubscribed();\n\t\tcontext.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenGrantedAndDoesNotAccessAuthenticationThenChainSubscribedAndSecurityContextNotSubscribed() {\n\t\tPublisherProbe<SecurityContext> context = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(this.chainResult.mono());\n\t\tAuthorizationWebFilter filter = new AuthorizationWebFilter(\n\t\t\t\t(a, e) -> Mono.just(new AuthorizationDecision(true)));\n\t\tMono<Void> result = filter.filter(this.exchange, this.chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(context.mono()));\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tthis.chainResult.assertWasSubscribed();\n\t\tcontext.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenGrantedAndDoeAccessAuthenticationThenChainSubscribedAndSecurityContextSubscribed() {\n\t\tPublisherProbe<SecurityContext> context = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(this.chainResult.mono());\n\t\tAuthorizationWebFilter filter = new AuthorizationWebFilter(\n\t\t\t\t(a, e) -> a.map((auth) -> (AuthorizationResult) new AuthorizationDecision(true))\n\t\t\t\t\t.defaultIfEmpty(new AuthorizationDecision(true)));\n\t\tMono<Void> result = filter.filter(this.exchange, this.chain)\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(context.mono()));\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tthis.chainResult.assertWasSubscribed();\n\t\tcontext.assertWasSubscribed();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mock;\nimport org.mockito.MockitoAnnotations;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authorization.AuthorityReactiveAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationDecision;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcherEntry;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class DelegatingReactiveAuthorizationManagerTests {\n\n\t@Mock\n\tServerWebExchangeMatcher match1;\n\n\t@Mock\n\tServerWebExchangeMatcher match2;\n\n\t@Mock\n\tAuthorityReactiveAuthorizationManager<AuthorizationContext> delegate1;\n\n\t@Mock\n\tAuthorityReactiveAuthorizationManager<AuthorizationContext> delegate2;\n\n\tServerWebExchange exchange;\n\n\t@Mock\n\tMono<Authentication> authentication;\n\n\t@Mock\n\tAuthorizationDecision decision;\n\n\tDelegatingReactiveAuthorizationManager manager;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tMockitoAnnotations.initMocks(this);\n\t\tthis.manager = DelegatingReactiveAuthorizationManager.builder()\n\t\t\t.add(new ServerWebExchangeMatcherEntry<>(this.match1, this.delegate1))\n\t\t\t.add(new ServerWebExchangeMatcherEntry<>(this.match2, this.delegate2))\n\t\t\t.build();\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/test\").build();\n\t\tthis.exchange = MockServerWebExchange.from(request);\n\t}\n\n\t@Test\n\tpublic void checkWhenFirstMatchesThenNoMoreMatchersAndNoMoreDelegatesInvoked() {\n\t\tgiven(this.match1.matches(any())).willReturn(ServerWebExchangeMatcher.MatchResult.match());\n\t\tgiven(this.delegate1.authorize(eq(this.authentication), any(AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(this.decision));\n\t\tassertThat(this.manager.authorize(this.authentication, this.exchange).block()).isEqualTo(this.decision);\n\t\tverifyNoMoreInteractions(this.match2, this.delegate2);\n\t}\n\n\t@Test\n\tpublic void checkWhenSecondMatchesThenNoMoreMatchersAndNoMoreDelegatesInvoked() {\n\t\tgiven(this.match1.matches(any())).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tgiven(this.match2.matches(any())).willReturn(ServerWebExchangeMatcher.MatchResult.match());\n\t\tgiven(this.delegate2.authorize(eq(this.authentication), any(AuthorizationContext.class)))\n\t\t\t.willReturn(Mono.just(this.decision));\n\t\tassertThat(this.manager.authorize(this.authentication, this.exchange).block()).isEqualTo(this.decision);\n\t\tverifyNoMoreInteractions(this.delegate1);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authorization/ExceptionTranslationWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport java.security.Principal;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpResponse;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.server.ServerAuthenticationEntryPoint;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @author César Revert\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class ExceptionTranslationWebFilterTests {\n\n\t@Mock\n\tprivate Principal principal;\n\n\t@Mock\n\tprivate AnonymousAuthenticationToken anonymousPrincipal;\n\n\t@Mock\n\tprivate ServerWebExchange exchange;\n\n\t@Mock\n\tprivate WebFilterChain chain;\n\n\t@Mock\n\tprivate ServerAccessDeniedHandler deniedHandler;\n\n\t@Mock\n\tprivate ServerAuthenticationEntryPoint entryPoint;\n\n\tprivate PublisherProbe<Void> deniedPublisher = PublisherProbe.empty();\n\n\tprivate PublisherProbe<Void> entryPointPublisher = PublisherProbe.empty();\n\n\tprivate ExceptionTranslationWebFilter filter = new ExceptionTranslationWebFilter();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.filter.setAuthenticationEntryPoint(this.entryPoint);\n\t\tthis.filter.setAccessDeniedHandler(this.deniedHandler);\n\t}\n\n\t@Test\n\tpublic void filterWhenNoExceptionThenNotHandled() {\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(Mono.empty());\n\t\tStepVerifier.create(this.filter.filter(this.exchange, this.chain)).expectComplete().verify();\n\t\tthis.deniedPublisher.assertWasNotSubscribed();\n\t\tthis.entryPointPublisher.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenNotAccessDeniedExceptionThenNotHandled() {\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(Mono.error(new IllegalArgumentException(\"oops\")));\n\t\tStepVerifier.create(this.filter.filter(this.exchange, this.chain))\n\t\t\t.expectError(IllegalArgumentException.class)\n\t\t\t.verify();\n\t\tthis.deniedPublisher.assertWasNotSubscribed();\n\t\tthis.entryPointPublisher.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenAccessDeniedExceptionAndNotAuthenticatedThenHandled() {\n\t\tgiven(this.entryPoint.commence(any(), any())).willReturn(this.entryPointPublisher.mono());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.empty());\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(Mono.error(new AccessDeniedException(\"Not Authorized\")));\n\t\tStepVerifier.create(this.filter.filter(this.exchange, this.chain)).verifyComplete();\n\t\tthis.deniedPublisher.assertWasNotSubscribed();\n\t\tthis.entryPointPublisher.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultsAndAccessDeniedExceptionAndAuthenticatedThenForbidden() {\n\t\tgiven(this.exchange.getResponse()).willReturn(new MockServerHttpResponse());\n\t\tthis.filter = new ExceptionTranslationWebFilter();\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(this.principal));\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(Mono.error(new AccessDeniedException(\"Not Authorized\")));\n\t\tStepVerifier.create(this.filter.filter(this.exchange, this.chain)).expectComplete().verify();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t}\n\n\t@Test\n\tpublic void filterWhenDefaultsAndAccessDeniedExceptionAndNotAuthenticatedThenUnauthorized() {\n\t\tgiven(this.exchange.getResponse()).willReturn(new MockServerHttpResponse());\n\t\tthis.filter = new ExceptionTranslationWebFilter();\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.empty());\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(Mono.error(new AccessDeniedException(\"Not Authorized\")));\n\t\tStepVerifier.create(this.filter.filter(this.exchange, this.chain)).expectComplete().verify();\n\t\tassertThat(this.exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);\n\t}\n\n\t@Test\n\tpublic void filterWhenAccessDeniedExceptionAndAuthenticatedThenHandled() {\n\t\tgiven(this.deniedHandler.handle(any(), any())).willReturn(this.deniedPublisher.mono());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(this.principal));\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(Mono.error(new AccessDeniedException(\"Not Authorized\")));\n\t\tStepVerifier.create(this.filter.filter(this.exchange, this.chain)).expectComplete().verify();\n\t\tthis.deniedPublisher.assertWasSubscribed();\n\t\tthis.entryPointPublisher.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenAccessDeniedExceptionAndAnonymousAuthenticatedThenHandled() {\n\t\tgiven(this.entryPoint.commence(any(), any())).willReturn(this.entryPointPublisher.mono());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(this.anonymousPrincipal));\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(Mono.error(new AccessDeniedException(\"Not Authorized\")));\n\t\tStepVerifier.create(this.filter.filter(this.exchange, this.chain)).expectComplete().verify();\n\t\tthis.deniedPublisher.assertWasNotSubscribed();\n\t\tthis.entryPointPublisher.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenAccessDeniedExceptionAndAnonymousAuthenticatedThenIncludesAuthenticationRequest() {\n\t\tgiven(this.entryPoint.commence(any(), any())).willReturn(this.entryPointPublisher.mono());\n\t\tgiven(this.exchange.getPrincipal()).willReturn(Mono.just(this.anonymousPrincipal));\n\t\tgiven(this.chain.filter(this.exchange)).willReturn(Mono.error(new AccessDeniedException(\"Not Authorized\")));\n\t\tStepVerifier.create(this.filter.filter(this.exchange, this.chain)).expectComplete().verify();\n\t\tArgumentCaptor<AuthenticationException> ex = ArgumentCaptor.forClass(AuthenticationException.class);\n\t\tverify(this.entryPoint).commence(any(), ex.capture());\n\t\tassertThat(ex.getValue().getAuthenticationRequest()).isEqualTo(this.anonymousPrincipal);\n\t}\n\n\t@Test\n\tpublic void setAccessDeniedHandlerWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAccessDeniedHandler(null));\n\t}\n\n\t@Test\n\tpublic void setAuthenticationEntryPointWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationEntryPoint(null));\n\t}\n\n\t@Test\n\tpublic void setAuthenticationTrustResolver() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationTrustResolver(null));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authorization/HttpStatusServerAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpResponse;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.access.AccessDeniedException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class HttpStatusServerAccessDeniedHandlerTests {\n\n\t@Mock\n\tprivate MockServerWebExchange exchange;\n\n\tprivate HttpStatus httpStatus = HttpStatus.FORBIDDEN;\n\n\tprivate HttpStatusServerAccessDeniedHandler handler = new HttpStatusServerAccessDeniedHandler(this.httpStatus);\n\n\tprivate AccessDeniedException exception = new AccessDeniedException(\"Forbidden\");\n\n\t@Test\n\tpublic void constructorHttpStatusWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new HttpStatusServerAccessDeniedHandler(null));\n\t}\n\n\t@Test\n\tpublic void commenceWhenNoSubscribersThenNoActions() {\n\t\tthis.handler.handle(this.exchange, this.exception);\n\t\tverifyNoMoreInteractions(this.exchange);\n\t}\n\n\t@Test\n\tpublic void commenceWhenSubscribeThenStatusSet() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.handler.handle(this.exchange, this.exception).block();\n\t\tMockServerHttpResponse response = this.exchange.getResponse();\n\t\tassertThat(response.getStatusCode()).isEqualTo(this.httpStatus);\n\t\tassertThat(response.getBodyAsString().block()).isEqualTo(\"Access Denied\");\n\t}\n\n\t@Test\n\tpublic void commenceWhenCustomStatusSubscribeThenStatusSet() {\n\t\tthis.httpStatus = HttpStatus.NOT_FOUND;\n\t\tthis.handler = new HttpStatusServerAccessDeniedHandler(this.httpStatus);\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.handler.handle(this.exchange, this.exception).block();\n\t\tMockServerHttpResponse response = this.exchange.getResponse();\n\t\tassertThat(response.getStatusCode()).isEqualTo(this.httpStatus);\n\t\tassertThat(response.getBodyAsString().block()).isEqualTo(\"Access Denied\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authorization/IpAddressReactiveAuthorizationManagerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.UnknownHostException;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link IpAddressReactiveAuthorizationManager}\n *\n * @author Guirong Hu\n */\npublic class IpAddressReactiveAuthorizationManagerTests {\n\n\t@Test\n\tpublic void checkWhenHasIpv6AddressThenReturnTrue() throws UnknownHostException {\n\t\tIpAddressReactiveAuthorizationManager v6manager = IpAddressReactiveAuthorizationManager\n\t\t\t.hasIpAddress(\"fe80::21f:5bff:fe33:bd68\");\n\t\tboolean granted = v6manager.authorize(null, context(\"fe80::21f:5bff:fe33:bd68\")).block().isGranted();\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasIpv6AddressThenReturnFalse() throws UnknownHostException {\n\t\tIpAddressReactiveAuthorizationManager v6manager = IpAddressReactiveAuthorizationManager\n\t\t\t.hasIpAddress(\"fe80::21f:5bff:fe33:bd68\");\n\t\tboolean granted = v6manager.authorize(null, context(\"fe80::1c9a:7cfd:29a8:a91e\")).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasIpv4AddressThenReturnTrue() throws UnknownHostException {\n\t\tIpAddressReactiveAuthorizationManager v4manager = IpAddressReactiveAuthorizationManager\n\t\t\t.hasIpAddress(\"192.168.1.104\");\n\t\tboolean granted = v4manager.authorize(null, context(\"192.168.1.104\")).block().isGranted();\n\t\tassertThat(granted).isTrue();\n\t}\n\n\t@Test\n\tpublic void checkWhenHasIpv4AddressThenReturnFalse() throws UnknownHostException {\n\t\tIpAddressReactiveAuthorizationManager v4manager = IpAddressReactiveAuthorizationManager\n\t\t\t.hasIpAddress(\"192.168.1.104\");\n\t\tboolean granted = v4manager.authorize(null, context(\"192.168.100.15\")).block().isGranted();\n\t\tassertThat(granted).isFalse();\n\t}\n\n\tprivate static AuthorizationContext context(String ipAddress) throws UnknownHostException {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.get(\"/\")\n\t\t\t\t.remoteAddress(new InetSocketAddress(InetAddress.getByName(ipAddress), 8080)))\n\t\t\t.build();\n\t\treturn new AuthorizationContext(exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/authorization/ServerWebExchangeDelegatingServerAccessDeniedHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.authorization;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.security.web.server.authorization.ServerWebExchangeDelegatingServerAccessDeniedHandler.DelegateEntry;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\npublic class ServerWebExchangeDelegatingServerAccessDeniedHandlerTests {\n\n\tprivate ServerWebExchangeDelegatingServerAccessDeniedHandler delegator;\n\n\tprivate List<ServerWebExchangeDelegatingServerAccessDeniedHandler.DelegateEntry> entries;\n\n\tprivate ServerAccessDeniedHandler accessDeniedHandler;\n\n\tprivate ServerWebExchange exchange;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.accessDeniedHandler = mock(ServerAccessDeniedHandler.class);\n\t\tthis.entries = new ArrayList<>();\n\t\tthis.exchange = mock(ServerWebExchange.class);\n\t}\n\n\t@Test\n\tpublic void handleWhenNothingMatchesThenOnlyDefaultHandlerInvoked() {\n\t\tServerAccessDeniedHandler handler = mock(ServerAccessDeniedHandler.class);\n\t\tServerWebExchangeMatcher matcher = mock(ServerWebExchangeMatcher.class);\n\t\tgiven(matcher.matches(this.exchange)).willReturn(MatchResult.notMatch());\n\t\tgiven(handler.handle(this.exchange, null)).willReturn(Mono.empty());\n\t\tgiven(this.accessDeniedHandler.handle(this.exchange, null)).willReturn(Mono.empty());\n\t\tthis.entries.add(new DelegateEntry(matcher, handler));\n\t\tthis.delegator = new ServerWebExchangeDelegatingServerAccessDeniedHandler(this.entries);\n\t\tthis.delegator.setDefaultAccessDeniedHandler(this.accessDeniedHandler);\n\t\tthis.delegator.handle(this.exchange, null).block();\n\t\tverify(this.accessDeniedHandler).handle(this.exchange, null);\n\t\tverify(handler, never()).handle(this.exchange, null);\n\t}\n\n\t@Test\n\tpublic void handleWhenFirstMatchesThenOnlyFirstInvoked() {\n\t\tServerAccessDeniedHandler firstHandler = mock(ServerAccessDeniedHandler.class);\n\t\tServerWebExchangeMatcher firstMatcher = mock(ServerWebExchangeMatcher.class);\n\t\tServerAccessDeniedHandler secondHandler = mock(ServerAccessDeniedHandler.class);\n\t\tServerWebExchangeMatcher secondMatcher = mock(ServerWebExchangeMatcher.class);\n\t\tgiven(firstMatcher.matches(this.exchange)).willReturn(MatchResult.match());\n\t\tgiven(firstHandler.handle(this.exchange, null)).willReturn(Mono.empty());\n\t\tgiven(secondHandler.handle(this.exchange, null)).willReturn(Mono.empty());\n\t\tthis.entries.add(new DelegateEntry(firstMatcher, firstHandler));\n\t\tthis.entries.add(new DelegateEntry(secondMatcher, secondHandler));\n\t\tthis.delegator = new ServerWebExchangeDelegatingServerAccessDeniedHandler(this.entries);\n\t\tthis.delegator.setDefaultAccessDeniedHandler(this.accessDeniedHandler);\n\t\tthis.delegator.handle(this.exchange, null).block();\n\t\tverify(firstHandler).handle(this.exchange, null);\n\t\tverify(secondHandler, never()).handle(this.exchange, null);\n\t\tverify(this.accessDeniedHandler, never()).handle(this.exchange, null);\n\t\tverify(secondMatcher, never()).matches(this.exchange);\n\t}\n\n\t@Test\n\tpublic void handleWhenSecondMatchesThenOnlySecondInvoked() {\n\t\tServerAccessDeniedHandler firstHandler = mock(ServerAccessDeniedHandler.class);\n\t\tServerWebExchangeMatcher firstMatcher = mock(ServerWebExchangeMatcher.class);\n\t\tServerAccessDeniedHandler secondHandler = mock(ServerAccessDeniedHandler.class);\n\t\tServerWebExchangeMatcher secondMatcher = mock(ServerWebExchangeMatcher.class);\n\t\tgiven(firstMatcher.matches(this.exchange)).willReturn(MatchResult.notMatch());\n\t\tgiven(secondMatcher.matches(this.exchange)).willReturn(MatchResult.match());\n\t\tgiven(firstHandler.handle(this.exchange, null)).willReturn(Mono.empty());\n\t\tgiven(secondHandler.handle(this.exchange, null)).willReturn(Mono.empty());\n\t\tthis.entries.add(new DelegateEntry(firstMatcher, firstHandler));\n\t\tthis.entries.add(new DelegateEntry(secondMatcher, secondHandler));\n\t\tthis.delegator = new ServerWebExchangeDelegatingServerAccessDeniedHandler(this.entries);\n\t\tthis.delegator.handle(this.exchange, null).block();\n\t\tverify(secondHandler).handle(this.exchange, null);\n\t\tverify(firstHandler, never()).handle(this.exchange, null);\n\t\tverify(this.accessDeniedHandler, never()).handle(this.exchange, null);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/context/NoOpServerSecurityContextRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.web.server.ServerWebExchange;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class NoOpServerSecurityContextRepositoryTests {\n\n\tNoOpServerSecurityContextRepository repository = NoOpServerSecurityContextRepository.getInstance();\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\t@Test\n\tpublic void saveAndLoad() {\n\t\tSecurityContext context = new SecurityContextImpl();\n\t\tMono<SecurityContext> result = this.repository.save(this.exchange, context)\n\t\t\t.then(this.repository.load(this.exchange));\n\t\tStepVerifier.create(result).verifyComplete();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/context/ReactorContextWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport java.util.List;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.TestPublisher;\nimport reactor.util.context.Context;\n\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.test.web.reactive.server.WebTestHandler;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.handler.DefaultWebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class ReactorContextWebFilterTests {\n\n\t@Mock\n\tprivate Authentication principal;\n\n\t@Mock\n\tprivate ServerSecurityContextRepository repository;\n\n\tprivate MockServerHttpRequest.BaseBuilder<?> exchange = MockServerHttpRequest.get(\"/\");\n\n\tprivate TestPublisher<SecurityContext> securityContext = TestPublisher.create();\n\n\tprivate ReactorContextWebFilter filter;\n\n\tprivate WebTestHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.filter = new ReactorContextWebFilter(this.repository);\n\t\tthis.handler = WebTestHandler.bindToWebFilters(this.filter);\n\t}\n\n\t@Test\n\tpublic void constructorNullSecurityContextRepository() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ReactorContextWebFilter(null));\n\t}\n\n\t@Test\n\tpublic void filterWhenNoPrincipalAccessThenNoInteractions() {\n\t\tgiven(this.repository.load(any())).willReturn(this.securityContext.mono());\n\t\tthis.handler.exchange(this.exchange);\n\t\tthis.securityContext.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenGetPrincipalMonoThenNoInteractions() {\n\t\tgiven(this.repository.load(any())).willReturn(this.securityContext.mono());\n\t\tthis.handler = WebTestHandler.bindToWebFilters(this.filter, (e, c) -> {\n\t\t\tReactiveSecurityContextHolder.getContext();\n\t\t\treturn c.filter(e);\n\t\t});\n\t\tthis.handler.exchange(this.exchange);\n\t\tthis.securityContext.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenPrincipalAndGetPrincipalThenInteractAndUseOriginalPrincipal() {\n\t\tSecurityContextImpl context = new SecurityContextImpl(this.principal);\n\t\tgiven(this.repository.load(any())).willReturn(Mono.just(context));\n\t\tthis.handler = WebTestHandler.bindToWebFilters(this.filter,\n\t\t\t\t(e, c) -> ReactiveSecurityContextHolder.getContext()\n\t\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t\t.doOnSuccess((p) -> assertThat(p).isSameAs(this.principal))\n\t\t\t\t\t.flatMap((p) -> c.filter(e)));\n\t\tWebTestHandler.WebHandlerResult result = this.handler.exchange(this.exchange);\n\t\tthis.securityContext.assertWasNotSubscribed();\n\t}\n\n\t@Test\n\t// gh-4962\n\tpublic void filterWhenMainContextThenDoesNotOverride() {\n\t\tgiven(this.repository.load(any())).willReturn(this.securityContext.mono());\n\t\tString contextKey = \"main\";\n\t\tWebFilter mainContextWebFilter = (e, c) -> c.filter(e).contextWrite(Context.of(contextKey, true));\n\t\tWebFilterChain chain = new DefaultWebFilterChain((e) -> Mono.empty(),\n\t\t\t\tList.of(mainContextWebFilter, this.filter));\n\t\tMono<Void> filter = chain.filter(MockServerWebExchange.from(this.exchange.build()));\n\t\tStepVerifier.create(filter).expectAccessibleContext().hasKey(contextKey).then().verifyComplete();\n\t}\n\n\t@Test\n\tpublic void filterWhenThreadFactoryIsPlatformThenSecurityContextLoaded() {\n\t\tThreadFactory threadFactory = Executors.defaultThreadFactory();\n\t\tassertSecurityContextLoaded(threadFactory);\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void filterWhenThreadFactoryIsVirtualThenSecurityContextLoaded() {\n\t\tThreadFactory threadFactory = new VirtualThreadTaskExecutor().getVirtualThreadFactory();\n\t\tassertSecurityContextLoaded(threadFactory);\n\t}\n\n\tprivate void assertSecurityContextLoaded(ThreadFactory threadFactory) {\n\t\tSecurityContextImpl context = new SecurityContextImpl(this.principal);\n\t\tgiven(this.repository.load(any())).willReturn(Mono.just(context));\n\t\t// @formatter:off\n\t\tWebFilter subscribeOnThreadFactory = (exchange, chain) -> chain.filter(exchange)\n\t\t\t\t.subscribeOn(Schedulers.newSingle(threadFactory));\n\t\tWebFilter assertSecurityContext = (exchange, chain) -> ReactiveSecurityContextHolder.getContext()\n\t\t\t\t.map(SecurityContext::getAuthentication)\n\t\t\t\t.doOnSuccess((authentication) -> assertThat(authentication).isSameAs(this.principal))\n\t\t\t\t.then(chain.filter(exchange));\n\t\t// @formatter:on\n\t\tthis.handler = WebTestHandler.bindToWebFilters(subscribeOnThreadFactory, this.filter, assertSecurityContext);\n\t\tthis.handler.exchange(this.exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/context/SecurityContextServerWebExchangeWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport java.util.Collections;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ThreadFactory;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.core.task.VirtualThreadTaskExecutor;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.ReactiveSecurityContextHolder;\nimport org.springframework.security.test.web.reactive.server.WebTestHandler;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.handler.DefaultWebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class SecurityContextServerWebExchangeWebFilterTests {\n\n\tSecurityContextServerWebExchangeWebFilter filter = new SecurityContextServerWebExchangeWebFilter();\n\n\tAuthentication principal = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\t@Test\n\tpublic void filterWhenExistingContextAndPrincipalNotNullThenContextPopulated() {\n\t\tMono<Void> result = this.filter\n\t\t\t.filter(this.exchange,\n\t\t\t\t\tnew DefaultWebFilterChain((e) -> e.getPrincipal()\n\t\t\t\t\t\t.doOnSuccess((contextPrincipal) -> assertThat(contextPrincipal).isEqualTo(this.principal))\n\t\t\t\t\t\t.flatMap((contextPrincipal) -> Mono.deferContextual(Mono::just))\n\t\t\t\t\t\t.doOnSuccess((context) -> assertThat(context.<String>get(\"foo\")).isEqualTo(\"bar\"))\n\t\t\t\t\t\t.then(), Collections.emptyList()))\n\t\t\t.contextWrite((context) -> context.put(\"foo\", \"bar\"))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.principal));\n\t\tStepVerifier.create(result).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void filterWhenPrincipalNotNullThenContextPopulated() {\n\t\tMono<Void> result = this.filter\n\t\t\t.filter(this.exchange,\n\t\t\t\t\tnew DefaultWebFilterChain((e) -> e.getPrincipal()\n\t\t\t\t\t\t.doOnSuccess((contextPrincipal) -> assertThat(contextPrincipal).isEqualTo(this.principal))\n\t\t\t\t\t\t.then(), Collections.emptyList()))\n\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.principal));\n\t\tStepVerifier.create(result).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void filterWhenPrincipalNullThenContextEmpty() {\n\t\tAuthentication defaultAuthentication = new TestingAuthenticationToken(\"anonymouse\", \"anonymous\", \"TEST\");\n\t\tMono<Void> result = this.filter.filter(this.exchange,\n\t\t\t\tnew DefaultWebFilterChain((e) -> e.getPrincipal()\n\t\t\t\t\t.defaultIfEmpty(defaultAuthentication)\n\t\t\t\t\t.doOnSuccess((contextPrincipal) -> assertThat(contextPrincipal).isEqualTo(defaultAuthentication))\n\t\t\t\t\t.then(), Collections.emptyList()));\n\t\tStepVerifier.create(result).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void filterWhenThreadFactoryIsPlatformThenContextPopulated() {\n\t\tThreadFactory threadFactory = Executors.defaultThreadFactory();\n\t\tassertPrincipalPopulated(threadFactory);\n\t}\n\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_17)\n\tpublic void filterWhenThreadFactoryIsVirtualThenContextPopulated() {\n\t\tThreadFactory threadFactory = new VirtualThreadTaskExecutor().getVirtualThreadFactory();\n\t\tassertPrincipalPopulated(threadFactory);\n\t}\n\n\tprivate void assertPrincipalPopulated(ThreadFactory threadFactory) {\n\t\t// @formatter:off\n\t\tWebFilter subscribeOnThreadFactory = (exchange, chain) -> chain.filter(exchange)\n\t\t\t\t.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.principal))\n\t\t\t\t.subscribeOn(Schedulers.newSingle(threadFactory));\n\t\tWebFilter assertPrincipal = (exchange, chain) -> exchange.getPrincipal()\n\t\t\t\t.doOnSuccess((principal) -> assertThat(principal).isSameAs(this.principal))\n\t\t\t\t.then(chain.filter(exchange));\n\t\t// @formatter:on\n\t\tWebTestHandler handler = WebTestHandler.bindToWebFilters(subscribeOnThreadFactory, this.filter,\n\t\t\t\tassertPrincipal);\n\t\thandler.exchange(this.exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/context/WebSessionServerSecurityContextRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.context;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebSessionServerSecurityContextRepositoryTests {\n\n\tprivate MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\n\tprivate WebSessionServerSecurityContextRepository repository = new WebSessionServerSecurityContextRepository();\n\n\t@Test\n\tpublic void saveAndLoadWhenDefaultsThenFound() {\n\t\tSecurityContext expected = new SecurityContextImpl();\n\t\tthis.repository.save(this.exchange, expected).block();\n\t\tSecurityContext actual = this.repository.load(this.exchange).block();\n\t\tassertThat(actual).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void saveAndLoadWhenCustomAttributeThenFound() {\n\t\tString attrName = \"attr\";\n\t\tthis.repository.setSpringSecurityContextAttrName(attrName);\n\t\tSecurityContext expected = new SecurityContextImpl();\n\t\tthis.repository.save(this.exchange, expected).block();\n\t\tWebSession session = this.exchange.getSession().block();\n\t\tassertThat(session.<SecurityContext>getAttribute(attrName)).isEqualTo(expected);\n\t\tSecurityContext actual = this.repository.load(this.exchange).block();\n\t\tassertThat(actual).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void saveAndLoadWhenNullThenDeletes() {\n\t\tSecurityContext context = new SecurityContextImpl();\n\t\tthis.repository.save(this.exchange, context).block();\n\t\tthis.repository.save(this.exchange, null).block();\n\t\tSecurityContext actual = this.repository.load(this.exchange).block();\n\t\tassertThat(actual).isNull();\n\t}\n\n\t@Test\n\tpublic void saveWhenNewContextThenChangeSessionId() {\n\t\tString originalSessionId = this.exchange.getSession().block().getId();\n\t\tthis.repository.save(this.exchange, new SecurityContextImpl()).block();\n\t\tWebSession session = this.exchange.getSession().block();\n\t\tassertThat(session.getId()).isNotEqualTo(originalSessionId);\n\t}\n\n\t@Test\n\tpublic void loadWhenNullThenNull() {\n\t\tSecurityContext context = this.repository.load(this.exchange).block();\n\t\tassertThat(context).isNull();\n\t}\n\n\t@Test\n\tpublic void loadWhenCacheSecurityContextThenSubscribeOnce() {\n\t\tPublisherProbe<WebSession> webSession = PublisherProbe.empty();\n\t\tServerWebExchange exchange = mock(ServerWebExchange.class);\n\t\tgiven(exchange.getSession()).willReturn(webSession.mono());\n\t\tthis.repository.setCacheSecurityContext(true);\n\t\tMono<SecurityContext> context = this.repository.load(exchange);\n\t\tassertThat(context.block()).isSameAs(context.block());\n\t\tassertThat(webSession.subscribeCount()).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void loadWhenNotCacheSecurityContextThenSubscribeMultiple() {\n\t\tPublisherProbe<WebSession> webSession = PublisherProbe.empty();\n\t\tServerWebExchange exchange = mock(ServerWebExchange.class);\n\t\tgiven(exchange.getSession()).willReturn(webSession.mono());\n\t\tMono<SecurityContext> context = this.repository.load(exchange);\n\t\tassertThat(context.block()).isSameAs(context.block());\n\t\tassertThat(webSession.subscribeCount()).isEqualTo(2);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpCookie;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.http.server.reactive.SslInfo;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.util.StringUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Eric Deandrea\n * @author Thomas Vitale\n * @author Alonso Araya\n * @author Alex Montoya\n * @since 5.1\n */\nclass CookieServerCsrfTokenRepositoryTests {\n\n\tprivate CookieServerCsrfTokenRepository csrfTokenRepository;\n\n\tprivate MockServerHttpRequest.BaseBuilder<?> request;\n\n\tprivate String expectedHeaderName = CookieServerCsrfTokenRepository.DEFAULT_CSRF_HEADER_NAME;\n\n\tprivate String expectedParameterName = CookieServerCsrfTokenRepository.DEFAULT_CSRF_PARAMETER_NAME;\n\n\tprivate Duration expectedMaxAge = Duration.ofSeconds(-1);\n\n\tprivate String expectedDomain = null;\n\n\tprivate String expectedPath = \"/\";\n\n\tprivate boolean expectedSecure = false;\n\n\tprivate boolean expectedHttpOnly = true;\n\n\tprivate String expectedCookieName = CookieServerCsrfTokenRepository.DEFAULT_CSRF_COOKIE_NAME;\n\n\tprivate String expectedCookieValue = \"csrfToken\";\n\n\tprivate String expectedSameSitePolicy = null;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.csrfTokenRepository = new CookieServerCsrfTokenRepository();\n\t\tthis.request = MockServerHttpRequest.get(\"/someUri\");\n\t}\n\n\t@Test\n\tvoid generateTokenWhenDefaultThenDefaults() {\n\t\tgenerateTokenAndAssertExpectedValues();\n\t}\n\n\t@Test\n\tvoid generateTokenWhenCustomHeaderThenCustomHeader() {\n\t\tsetExpectedHeaderName(\"someHeader\");\n\t\tgenerateTokenAndAssertExpectedValues();\n\t}\n\n\t@Test\n\tvoid generateTokenWhenCustomParameterThenCustomParameter() {\n\t\tsetExpectedParameterName(\"someParam\");\n\t\tgenerateTokenAndAssertExpectedValues();\n\t}\n\n\t@Test\n\tvoid generateTokenWhenCustomHeaderAndParameterThenCustomHeaderAndParameter() {\n\t\tsetExpectedHeaderName(\"someHeader\");\n\t\tsetExpectedParameterName(\"someParam\");\n\t\tgenerateTokenAndAssertExpectedValues();\n\t}\n\n\t@Test\n\tvoid saveTokenWhenNoSubscriptionThenNotWritten() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tthis.csrfTokenRepository.saveToken(exchange, createToken());\n\t\tassertThat(exchange.getResponse().getCookies().getFirst(this.expectedCookieName)).isNull();\n\t}\n\n\t@Test\n\tvoid saveTokenWhenDefaultThenDefaults() {\n\t\tsaveAndAssertExpectedValues(createToken());\n\t}\n\n\t@Test\n\tvoid saveTokenWhenNullThenDeletes() {\n\t\tsaveAndAssertExpectedValues(null);\n\t}\n\n\t@Test\n\tvoid saveTokenWhenHttpOnlyFalseThenHttpOnlyFalse() {\n\t\tsetExpectedHttpOnly(false);\n\t\tsaveAndAssertExpectedValues(createToken());\n\t}\n\n\t@Test\n\tvoid saveTokenWhenCookieMaxAgeThenCookieMaxAge() {\n\t\tsetExpectedCookieMaxAge(3600);\n\t\tsaveAndAssertExpectedValues(createToken());\n\t}\n\n\t@Test\n\tvoid saveTokenWhenSameSiteThenCookieSameSite() {\n\t\tsetExpectedSameSitePolicy(\"Lax\");\n\t\tsaveAndAssertExpectedValues(createToken());\n\t}\n\n\t@Test\n\tvoid saveTokenWhenCustomPropertiesThenCustomProperties() {\n\t\tsetExpectedCookieName(\"csrfCookie\");\n\t\tsetExpectedHeaderName(\"headerName\");\n\t\tsetExpectedParameterName(\"paramName\");\n\t\tthis.csrfTokenRepository.setCookieCustomizer((cookie) -> {\n\t\t\tthis.expectedPath = \"/some/path\";\n\t\t\tcookie.path(this.expectedPath);\n\t\t\tthis.expectedDomain = \"spring.io\";\n\t\t\tcookie.domain(this.expectedDomain);\n\t\t\tthis.expectedMaxAge = Duration.ofSeconds(3600);\n\t\t\tcookie.maxAge(this.expectedMaxAge);\n\t\t\tthis.expectedSameSitePolicy = \"Strict\";\n\t\t\tcookie.sameSite(this.expectedSameSitePolicy);\n\t\t});\n\t\tsaveAndAssertExpectedValues(createToken());\n\t}\n\n\t@Test\n\tvoid saveTokenWhenCustomPropertiesThenCustomPropertiesUsingCustomizer() {\n\t\tString expectedDomain = \"spring.io\";\n\t\tint expectedMaxAge = 3600;\n\t\tString expectedPath = \"/some/path\";\n\t\tString expectedSameSite = \"Strict\";\n\n\t\tsetExpectedCookieName(\"csrfCookie\");\n\n\t\tsetExpectedHeaderName(\"headerName\");\n\t\tsetExpectedParameterName(\"paramName\");\n\n\t\tCsrfToken token = createToken();\n\n\t\tthis.csrfTokenRepository.setCookieCustomizer((customizer) -> {\n\t\t\tcustomizer.domain(expectedDomain);\n\t\t\tcustomizer.maxAge(expectedMaxAge);\n\t\t\tcustomizer.path(expectedPath);\n\t\t\tcustomizer.sameSite(expectedSameSite);\n\t\t});\n\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tthis.csrfTokenRepository.saveToken(exchange, token).block();\n\t\tResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getMaxAge()).isEqualTo(Duration.of(expectedMaxAge, ChronoUnit.SECONDS));\n\t\tassertThat(cookie.getDomain()).isEqualTo(expectedDomain);\n\t\tassertThat(cookie.getPath()).isEqualTo(expectedPath);\n\t\tassertThat(cookie.getSameSite()).isEqualTo(expectedSameSite);\n\t\tassertThat(cookie.isSecure()).isEqualTo(this.expectedSecure);\n\t\tassertThat(cookie.isHttpOnly()).isEqualTo(this.expectedHttpOnly);\n\t\tassertThat(cookie.getName()).isEqualTo(this.expectedCookieName);\n\t\tassertThat(cookie.getValue()).isEqualTo(this.expectedCookieValue);\n\t}\n\n\t@Test\n\tvoid saveTokenWhenSslInfoPresentThenSecure() {\n\t\tthis.request.sslInfo(SslInfo.from(\"sessionId\"));\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tthis.csrfTokenRepository.saveToken(exchange, createToken()).block();\n\t\tResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.isSecure()).isTrue();\n\t}\n\n\t@Test\n\tvoid saveTokenWhenSslInfoNullThenNotSecure() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tthis.csrfTokenRepository.saveToken(exchange, createToken()).block();\n\t\tResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.isSecure()).isFalse();\n\t}\n\n\t@Test\n\tvoid saveTokenWhenSecureFlagTrueThenSecureUsingCustomizer() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tthis.csrfTokenRepository.setCookieCustomizer((customizer) -> customizer.secure(true));\n\t\tthis.csrfTokenRepository.saveToken(exchange, createToken()).block();\n\t\tResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.isSecure()).isTrue();\n\t}\n\n\t@Test\n\tvoid saveTokenWhenSecureFlagFalseThenNotSecureUsingCustomizer() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tthis.csrfTokenRepository.setCookieCustomizer((customizer) -> customizer.secure(false));\n\t\tthis.csrfTokenRepository.saveToken(exchange, createToken()).block();\n\t\tResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.isSecure()).isFalse();\n\t}\n\n\t@Test\n\tvoid saveTokenWhenSecureFlagFalseAndSslInfoThenNotSecureUsingCustomizer() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tthis.request.sslInfo(SslInfo.from(\"sessionId\"));\n\t\tthis.csrfTokenRepository.setCookieCustomizer((customizer) -> customizer.secure(false));\n\t\tthis.csrfTokenRepository.saveToken(exchange, createToken()).block();\n\t\tResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.isSecure()).isFalse();\n\t}\n\n\t@Test\n\tvoid loadTokenWhenCookieExistThenTokenFound() {\n\t\tloadAndAssertExpectedValues();\n\t}\n\n\t@Test\n\tvoid loadTokenWhenCustomThenTokenFound() {\n\t\tsetExpectedParameterName(\"paramName\");\n\t\tsetExpectedHeaderName(\"headerName\");\n\t\tsetExpectedCookieName(\"csrfCookie\");\n\t\tsaveAndAssertExpectedValues(createToken());\n\t}\n\n\t@Test\n\tvoid loadTokenWhenNoCookiesThenNullToken() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tCsrfToken csrfToken = this.csrfTokenRepository.loadToken(exchange).block();\n\t\tassertThat(csrfToken).isNull();\n\t}\n\n\t@Test\n\tvoid loadTokenWhenCookieExistsWithNoValue() {\n\t\tsetExpectedCookieValue(\"\");\n\t\tloadAndAssertExpectedValues();\n\t}\n\n\t@Test\n\tvoid loadTokenWhenCookieExistsWithNullValue() {\n\t\tsetExpectedCookieValue(null);\n\t\tloadAndAssertExpectedValues();\n\t}\n\n\t// gh-16820\n\t@Test\n\tvoid withHttpOnlyFalseWhenCookieCustomizerThenStillDefaultsToFalse() {\n\t\tCookieServerCsrfTokenRepository repository = CookieServerCsrfTokenRepository.withHttpOnlyFalse();\n\t\trepository.setCookieCustomizer((customizer) -> customizer.maxAge(1000));\n\t\tMockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get(\"/dummy\");\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(request);\n\t\tCsrfToken csrfToken = repository.generateToken(exchange).block();\n\t\trepository.saveToken(exchange, csrfToken).block();\n\t\tResponseCookie cookie = exchange.getResponse().getCookies().getFirst(\"XSRF-TOKEN\");\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getMaxAge().getSeconds()).isEqualTo(1000);\n\t\tassertThat(cookie.isHttpOnly()).isEqualTo(Boolean.FALSE);\n\t}\n\n\tprivate void setExpectedHeaderName(String expectedHeaderName) {\n\t\tthis.csrfTokenRepository.setHeaderName(expectedHeaderName);\n\t\tthis.expectedHeaderName = expectedHeaderName;\n\t}\n\n\tprivate void setExpectedParameterName(String expectedParameterName) {\n\t\tthis.csrfTokenRepository.setParameterName(expectedParameterName);\n\t\tthis.expectedParameterName = expectedParameterName;\n\t}\n\n\tprivate void setExpectedPath(String expectedPath) {\n\t\tthis.csrfTokenRepository.setCookiePath(expectedPath);\n\t\tthis.expectedPath = expectedPath;\n\t}\n\n\tprivate void setExpectedHttpOnly(boolean expectedHttpOnly) {\n\t\tthis.expectedHttpOnly = expectedHttpOnly;\n\t\tthis.csrfTokenRepository.setCookieCustomizer((cookie) -> cookie.httpOnly(expectedHttpOnly));\n\t}\n\n\tprivate void setExpectedCookieName(String expectedCookieName) {\n\t\tthis.expectedCookieName = expectedCookieName;\n\t\tthis.csrfTokenRepository.setCookieName(expectedCookieName);\n\t}\n\n\tprivate void setExpectedCookieMaxAge(int expectedCookieMaxAge) {\n\t\tDuration duration = Duration.ofSeconds(expectedCookieMaxAge);\n\t\tthis.csrfTokenRepository.setCookieCustomizer((cookie) -> cookie.maxAge(duration));\n\t\tthis.expectedMaxAge = Duration.ofSeconds(expectedCookieMaxAge);\n\t}\n\n\tprivate void setExpectedSameSitePolicy(String sameSitePolicy) {\n\t\tthis.csrfTokenRepository.setCookieCustomizer((customizer) -> customizer.sameSite(sameSitePolicy));\n\t\tthis.expectedSameSitePolicy = sameSitePolicy;\n\t}\n\n\tprivate void setExpectedCookieValue(String expectedCookieValue) {\n\t\tthis.expectedCookieValue = expectedCookieValue;\n\t}\n\n\tprivate void loadAndAssertExpectedValues() {\n\t\tMockServerHttpRequest.BodyBuilder request = MockServerHttpRequest.post(\"/someUri\")\n\t\t\t.cookie(new HttpCookie(this.expectedCookieName, this.expectedCookieValue));\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(request);\n\t\tCsrfToken csrfToken = this.csrfTokenRepository.loadToken(exchange).block();\n\t\tif (StringUtils.hasText(this.expectedCookieValue)) {\n\t\t\tassertThat(csrfToken).isNotNull();\n\t\t\tassertThat(csrfToken.getHeaderName()).isEqualTo(this.expectedHeaderName);\n\t\t\tassertThat(csrfToken.getParameterName()).isEqualTo(this.expectedParameterName);\n\t\t\tassertThat(csrfToken.getToken()).isEqualTo(this.expectedCookieValue);\n\t\t}\n\t\telse {\n\t\t\tassertThat(csrfToken).isNull();\n\t\t}\n\t}\n\n\tprivate void saveAndAssertExpectedValues(CsrfToken token) {\n\t\tif (token == null) {\n\t\t\tthis.expectedMaxAge = Duration.ofSeconds(0);\n\t\t\tthis.expectedCookieValue = \"\";\n\t\t}\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tthis.csrfTokenRepository.saveToken(exchange, token).block();\n\t\tResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.getMaxAge()).isEqualTo(this.expectedMaxAge);\n\t\tassertThat(cookie.getDomain()).isEqualTo(this.expectedDomain);\n\t\tassertThat(cookie.getPath()).isEqualTo(this.expectedPath);\n\t\tassertThat(cookie.isSecure()).isEqualTo(this.expectedSecure);\n\t\tassertThat(cookie.isHttpOnly()).isEqualTo(this.expectedHttpOnly);\n\t\tassertThat(cookie.getName()).isEqualTo(this.expectedCookieName);\n\t\tassertThat(cookie.getValue()).isEqualTo(this.expectedCookieValue);\n\t\tassertThat(cookie.getSameSite()).isEqualTo(this.expectedSameSitePolicy);\n\t}\n\n\tprivate void generateTokenAndAssertExpectedValues() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request);\n\t\tCsrfToken csrfToken = this.csrfTokenRepository.generateToken(exchange).block();\n\t\tassertThat(csrfToken).isNotNull();\n\t\tassertThat(csrfToken.getHeaderName()).isEqualTo(this.expectedHeaderName);\n\t\tassertThat(csrfToken.getParameterName()).isEqualTo(this.expectedParameterName);\n\t\tassertThat(csrfToken.getToken()).isNotBlank();\n\t}\n\n\tprivate CsrfToken createToken() {\n\t\treturn createToken(this.expectedHeaderName, this.expectedParameterName, this.expectedCookieValue);\n\t}\n\n\tprivate static CsrfToken createToken(String headerName, String parameterName, String tokenValue) {\n\t\treturn new DefaultCsrfToken(headerName, parameterName, tokenValue);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/csrf/CsrfServerLogoutHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.web.server.WebFilterExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Eric Deandrea\n * @since 5.1\n */\n@ExtendWith(MockitoExtension.class)\npublic class CsrfServerLogoutHandlerTests {\n\n\t@Mock\n\tprivate ServerCsrfTokenRepository csrfTokenRepository;\n\n\t@Mock\n\tprivate WebFilterChain filterChain;\n\n\tprivate MockServerWebExchange exchange;\n\n\tprivate WebFilterExchange filterExchange;\n\n\tprivate CsrfServerLogoutHandler handler;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\tthis.filterExchange = new WebFilterExchange(this.exchange, this.filterChain);\n\t\tthis.handler = new CsrfServerLogoutHandler(this.csrfTokenRepository);\n\t}\n\n\t@Test\n\tpublic void constructorNullCsrfTokenRepository() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new CsrfServerLogoutHandler(null))\n\t\t\t.withMessage(\"csrfTokenRepository cannot be null\")\n\t\t\t.withNoCause();\n\t}\n\n\t@Test\n\tpublic void logoutRemovesCsrfToken() {\n\t\tgiven(this.csrfTokenRepository.saveToken(this.exchange, null)).willReturn(Mono.empty());\n\t\tthis.handler.logout(this.filterExchange, new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\"))\n\t\t\t.block();\n\t\tverify(this.csrfTokenRepository).saveToken(this.exchange, null);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/csrf/CsrfWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\nimport reactor.test.publisher.PublisherProbe;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.WebSession;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @author Parikshit Dutta\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class CsrfWebFilterTests {\n\n\t@Mock\n\tprivate WebFilterChain chain;\n\n\t@Mock\n\tprivate ServerCsrfTokenRepository repository;\n\n\tprivate CsrfToken token = new DefaultCsrfToken(\"csrf\", \"CSRF\", \"a\");\n\n\tprivate CsrfWebFilter csrfFilter = new CsrfWebFilter();\n\n\tprivate MockServerWebExchange get = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\n\tprivate MockServerWebExchange post = MockServerWebExchange.from(MockServerHttpRequest.post(\"/\"));\n\n\t@Test\n\tpublic void setRequestHandlerWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.csrfFilter.setRequestHandler(null))\n\t\t\t\t.withMessage(\"requestHandler cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void filterWhenGetThenSessionNotCreatedAndChainContinues() {\n\t\tPublisherProbe<Void> chainResult = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(this.get)).willReturn(chainResult.mono());\n\t\tMono<Void> result = this.csrfFilter.filter(this.get, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tMono<Boolean> isSessionStarted = this.get.getSession().map(WebSession::isStarted);\n\t\tStepVerifier.create(isSessionStarted).expectNext(false).verifyComplete();\n\t\tchainResult.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenPostAndNoTokenThenCsrfException() {\n\t\tMono<Void> result = this.csrfFilter.filter(this.post, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tassertThat(this.post.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t}\n\n\t@Test\n\tpublic void filterWhenPostAndEstablishedCsrfTokenAndRequestMissingTokenThenCsrfException() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tMono<Void> result = this.csrfFilter.filter(this.post, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tassertThat(this.post.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t\tStepVerifier.create(this.post.getResponse().getBodyAsString())\n\t\t\t.assertNext((body) -> assertThat(body).contains(\"An expected CSRF token cannot be found\"));\n\t}\n\n\t@Test\n\tpublic void filterWhenPostAndEstablishedCsrfTokenAndRequestParamInvalidTokenThenCsrfException() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tthis.post = MockServerWebExchange.from(MockServerHttpRequest.post(\"/\")\n\t\t\t.body(this.token.getParameterName() + \"=\" + this.token.getToken() + \"INVALID\"));\n\t\tMono<Void> result = this.csrfFilter.filter(this.post, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tassertThat(this.post.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t}\n\n\t@Test\n\tpublic void filterWhenPostAndEstablishedCsrfTokenAndRequestParamValidTokenThenContinues() {\n\t\tPublisherProbe<Void> chainResult = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(any())).willReturn(chainResult.mono());\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tgiven(this.repository.generateToken(any())).willReturn(Mono.just(this.token));\n\t\tCsrfToken csrfToken = createXorCsrfToken();\n\t\tthis.post = MockServerWebExchange.from(MockServerHttpRequest.post(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.body(csrfToken.getParameterName() + \"=\" + csrfToken.getToken()));\n\t\tMono<Void> result = this.csrfFilter.filter(this.post, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tchainResult.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenPostAndEstablishedCsrfTokenAndHeaderInvalidTokenThenCsrfException() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tthis.post = MockServerWebExchange.from(\n\t\t\t\tMockServerHttpRequest.post(\"/\").header(this.token.getHeaderName(), this.token.getToken() + \"INVALID\"));\n\t\tMono<Void> result = this.csrfFilter.filter(this.post, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tassertThat(this.post.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t}\n\n\t@Test\n\tpublic void filterWhenPostAndEstablishedCsrfTokenAndHeaderValidTokenThenContinues() {\n\t\tPublisherProbe<Void> chainResult = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(any())).willReturn(chainResult.mono());\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tgiven(this.repository.generateToken(any())).willReturn(Mono.just(this.token));\n\t\tCsrfToken csrfToken = createXorCsrfToken();\n\t\tthis.post = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/\").header(csrfToken.getHeaderName(), csrfToken.getToken()));\n\t\tMono<Void> result = this.csrfFilter.filter(this.post, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tchainResult.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestHandlerSetThenUsed() {\n\t\tServerCsrfTokenRequestHandler requestHandler = mock(ServerCsrfTokenRequestHandler.class);\n\t\tgiven(requestHandler.resolveCsrfTokenValue(any(ServerWebExchange.class), any(CsrfToken.class)))\n\t\t\t.willReturn(Mono.just(this.token.getToken()));\n\t\tthis.csrfFilter.setRequestHandler(requestHandler);\n\n\t\tPublisherProbe<Void> chainResult = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(any())).willReturn(chainResult.mono());\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tgiven(this.repository.generateToken(any())).willReturn(Mono.just(this.token));\n\t\tthis.post = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/\").header(this.token.getHeaderName(), this.token.getToken()));\n\t\tMono<Void> result = this.csrfFilter.filter(this.post, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tchainResult.assertWasSubscribed();\n\n\t\tverify(requestHandler).handle(eq(this.post), any());\n\t\tverify(requestHandler).resolveCsrfTokenValue(this.post, this.token);\n\t}\n\n\t@Test\n\tpublic void filterWhenXorServerCsrfTokenRequestAttributeHandlerAndValidTokenThenSuccess() {\n\t\tPublisherProbe<Void> chainResult = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(any())).willReturn(chainResult.mono());\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.generateToken(any())).willReturn(Mono.just(this.token));\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\n\t\tCsrfToken csrfToken = createXorCsrfToken();\n\t\tthis.post = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/\").header(csrfToken.getHeaderName(), csrfToken.getToken()));\n\t\tStepVerifier.create(this.csrfFilter.filter(this.post, this.chain)).verifyComplete();\n\t\tchainResult.assertWasSubscribed();\n\t}\n\n\t@Test\n\tpublic void filterWhenXorServerCsrfTokenRequestAttributeHandlerAndRawTokenThenAccessDeniedException() {\n\t\tPublisherProbe<Void> chainResult = PublisherProbe.empty();\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tXorServerCsrfTokenRequestAttributeHandler requestHandler = new XorServerCsrfTokenRequestAttributeHandler();\n\t\tthis.csrfFilter.setRequestHandler(requestHandler);\n\t\tthis.post = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.post(\"/\").header(this.token.getHeaderName(), this.token.getToken()));\n\t\tMono<Void> result = this.csrfFilter.filter(this.post, this.chain);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tchainResult.assertWasNotSubscribed();\n\t\tassertThat(this.post.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);\n\t}\n\n\t@Test\n\t// gh-8452\n\tpublic void matchesRequireCsrfProtectionWhenNonStandardHTTPMethodIsUsed() {\n\t\tMockServerWebExchange nonStandardHttpExchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.method(HttpMethod.valueOf(\"non-standard-http-method\"), \"/\"));\n\t\tServerWebExchangeMatcher serverWebExchangeMatcher = CsrfWebFilter.DEFAULT_CSRF_MATCHER;\n\t\tassertThat(serverWebExchangeMatcher.matches(nonStandardHttpExchange).map(MatchResult::isMatch).block())\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void doFilterWhenSkipExchangeInvokedThenSkips() {\n\t\tPublisherProbe<Void> chainResult = PublisherProbe.empty();\n\t\tgiven(this.chain.filter(any())).willReturn(chainResult.mono());\n\t\tServerWebExchangeMatcher matcher = mock(ServerWebExchangeMatcher.class);\n\t\tthis.csrfFilter.setRequireCsrfProtectionMatcher(matcher);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/post\").build());\n\t\tCsrfWebFilter.skipExchange(exchange);\n\t\tthis.csrfFilter.filter(exchange, this.chain).block();\n\t\tverifyNoMoreInteractions(matcher);\n\t}\n\n\t@Test\n\tpublic void filterWhenMultipartFormDataAndNotEnabledThenDenied() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tWebTestClient client = WebTestClient.bindToController(new OkController()).webFilter(this.csrfFilter).build();\n\t\tclient.post()\n\t\t\t.uri(\"/\")\n\t\t\t.contentType(MediaType.MULTIPART_FORM_DATA)\n\t\t\t.body(BodyInserters.fromMultipartData(this.token.getParameterName(), this.token.getToken()))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isForbidden();\n\t}\n\n\t@Test\n\tpublic void filterWhenMultipartFormDataAndEnabledThenGranted() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();\n\t\trequestHandler.setTokenFromMultipartDataEnabled(true);\n\t\tthis.csrfFilter.setRequestHandler(requestHandler);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tgiven(this.repository.generateToken(any())).willReturn(Mono.just(this.token));\n\t\tWebTestClient client = WebTestClient.bindToController(new OkController()).webFilter(this.csrfFilter).build();\n\t\tclient.post()\n\t\t\t.uri(\"/\")\n\t\t\t.contentType(MediaType.MULTIPART_FORM_DATA)\n\t\t\t.body(BodyInserters.fromMultipartData(this.token.getParameterName(), this.token.getToken()))\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.is2xxSuccessful();\n\t}\n\n\t@Test\n\tpublic void filterWhenPostAndMultipartFormDataEnabledAndNoBodyProvided() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();\n\t\trequestHandler.setTokenFromMultipartDataEnabled(true);\n\t\tthis.csrfFilter.setRequestHandler(requestHandler);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tgiven(this.repository.generateToken(any())).willReturn(Mono.just(this.token));\n\t\tWebTestClient client = WebTestClient.bindToController(new OkController()).webFilter(this.csrfFilter).build();\n\t\tclient.post()\n\t\t\t.uri(\"/\")\n\t\t\t.header(this.token.getHeaderName(), this.token.getToken())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.is2xxSuccessful();\n\t}\n\n\t@Test\n\tpublic void filterWhenFormDataAndEnabledThenGranted() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();\n\t\trequestHandler.setTokenFromMultipartDataEnabled(true);\n\t\tthis.csrfFilter.setRequestHandler(requestHandler);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tgiven(this.repository.generateToken(any())).willReturn(Mono.just(this.token));\n\t\tWebTestClient client = WebTestClient.bindToController(new OkController()).webFilter(this.csrfFilter).build();\n\t\tclient.post()\n\t\t\t.uri(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.bodyValue(this.token.getParameterName() + \"=\" + this.token.getToken())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.is2xxSuccessful();\n\t}\n\n\t@Test\n\tpublic void filterWhenMultipartMixedAndEnabledThenNotRead() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();\n\t\trequestHandler.setTokenFromMultipartDataEnabled(true);\n\t\tthis.csrfFilter.setRequestHandler(requestHandler);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(this.token));\n\t\tWebTestClient client = WebTestClient.bindToController(new OkController()).webFilter(this.csrfFilter).build();\n\t\tclient.post()\n\t\t\t.uri(\"/\")\n\t\t\t.contentType(MediaType.MULTIPART_MIXED)\n\t\t\t.bodyValue(this.token.getParameterName() + \"=\" + this.token.getToken())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isForbidden();\n\t}\n\n\t// gh-9561\n\n\t@Test\n\tpublic void doFilterWhenTokenIsNullThenNoNullPointer() {\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tCsrfToken token = mock(CsrfToken.class);\n\t\tgiven(token.getToken()).willReturn(null);\n\t\tgiven(token.getHeaderName()).willReturn(this.token.getHeaderName());\n\t\tgiven(token.getParameterName()).willReturn(this.token.getParameterName());\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.just(token));\n\t\tWebTestClient client = WebTestClient.bindToController(new OkController()).webFilter(this.csrfFilter).build();\n\t\tclient.post()\n\t\t\t.uri(\"/\")\n\t\t\t.contentType(MediaType.APPLICATION_FORM_URLENCODED)\n\t\t\t.bodyValue(this.token.getParameterName() + \"=\" + this.token.getToken())\n\t\t\t.exchange()\n\t\t\t.expectStatus()\n\t\t\t.isForbidden();\n\t}\n\t// gh-9113\n\n\t@Test\n\tpublic void filterWhenSubscribingCsrfTokenMultipleTimesThenGenerateOnlyOnce() {\n\t\tPublisherProbe<CsrfToken> chainResult = PublisherProbe.empty();\n\t\tthis.csrfFilter.setCsrfTokenRepository(this.repository);\n\t\tgiven(this.repository.loadToken(any())).willReturn(Mono.empty());\n\t\tgiven(this.repository.generateToken(any())).willReturn(chainResult.mono());\n\t\tgiven(this.chain.filter(any())).willReturn(Mono.empty());\n\t\tthis.csrfFilter.filter(this.get, this.chain).block();\n\t\tMono<CsrfToken> result = this.get.getAttribute(CsrfToken.class.getName());\n\t\tresult.block();\n\t\tresult.block();\n\t\tassertThat(chainResult.subscribeCount()).isEqualTo(1);\n\t}\n\n\tprivate CsrfToken createXorCsrfToken() {\n\t\tServerCsrfTokenRequestHandler handler = new XorServerCsrfTokenRequestAttributeHandler();\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\thandler.handle(exchange, Mono.just(this.token));\n\t\tMono<CsrfToken> csrfToken = exchange.getAttribute(CsrfToken.class.getName());\n\t\treturn csrfToken.block();\n\t}\n\n\t@RestController\n\tstatic class OkController {\n\n\t\t@RequestMapping(\"/**\")\n\t\tString ok() {\n\t\t\treturn \"ok\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/csrf/ServerCsrfTokenRequestAttributeHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link ServerCsrfTokenRequestAttributeHandler}.\n *\n * @author Steve Riesenberg\n * @since 5.8\n */\npublic class ServerCsrfTokenRequestAttributeHandlerTests {\n\n\tprivate ServerCsrfTokenRequestAttributeHandler handler;\n\n\tprivate MockServerWebExchange exchange;\n\n\tprivate CsrfToken token;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.handler = new ServerCsrfTokenRequestAttributeHandler();\n\t\tthis.exchange = MockServerWebExchange.builder(MockServerHttpRequest.get(\"/\")).build();\n\t\tthis.token = new DefaultCsrfToken(\"headerName\", \"paramName\", \"csrfTokenValue\");\n\t}\n\n\t@Test\n\tpublic void handleWhenExchangeIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(null, Mono.just(this.token)))\n\t\t\t\t.withMessage(\"exchange cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfTokenIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(this.exchange, null))\n\t\t\t\t.withMessage(\"csrfToken cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenValidParametersThenExchangeAttributeSet() {\n\t\tMono<CsrfToken> csrfToken = Mono.just(this.token);\n\t\tthis.handler.handle(this.exchange, csrfToken);\n\t\tMono<CsrfToken> csrfTokenAttribute = this.exchange.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(csrfTokenAttribute).isNotNull();\n\t\tassertThat(csrfTokenAttribute).isEqualTo(csrfToken);\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenExchangeIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.resolveCsrfTokenValue(null, this.token))\n\t\t\t\t.withMessage(\"exchange cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenCsrfTokenIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.resolveCsrfTokenValue(this.exchange, null))\n\t\t\t\t.withMessage(\"csrfToken cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenTokenNotSetThenReturnsEmptyMono() {\n\t\tMono<String> csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, this.token);\n\t\tStepVerifier.create(csrfToken).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenFormDataSetThenReturnsTokenValue() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.body(this.token.getParameterName() + \"=\" + this.token.getToken()))\n\t\t\t.build();\n\t\tMono<String> csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, this.token);\n\t\tStepVerifier.create(csrfToken).expectNext(this.token.getToken()).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenHeaderSetThenReturnsTokenValue() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), this.token.getToken()))\n\t\t\t.build();\n\t\tMono<String> csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, this.token);\n\t\tStepVerifier.create(csrfToken).expectNext(this.token.getToken()).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenHeaderAndFormDataSetThenFormDataIsPreferred() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), \"header\")\n\t\t\t\t.body(this.token.getParameterName() + \"=\" + this.token.getToken()))\n\t\t\t.build();\n\t\tMono<String> csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, this.token);\n\t\tStepVerifier.create(csrfToken).expectNext(this.token.getToken()).verifyComplete();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/csrf/WebSessionServerCsrfTokenRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.WebSession;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebSessionServerCsrfTokenRepositoryTests {\n\n\tprivate WebSessionServerCsrfTokenRepository repository = new WebSessionServerCsrfTokenRepository();\n\n\tprivate MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\n\t@Test\n\tpublic void generateTokenThenNoSession() {\n\t\tMono<CsrfToken> result = this.repository.generateToken(this.exchange);\n\t\tMono<Boolean> isSessionStarted = this.exchange.getSession().map(WebSession::isStarted);\n\t\tStepVerifier.create(isSessionStarted).expectNext(false).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void generateTokenWhenSubscriptionThenNoSession() {\n\t\tMono<CsrfToken> result = this.repository.generateToken(this.exchange);\n\t\tMono<Boolean> isSessionStarted = this.exchange.getSession().map(WebSession::isStarted);\n\t\tStepVerifier.create(isSessionStarted).expectNext(false).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void saveTokenWhenDefaultThenAddsToSession() {\n\t\tMono<CsrfToken> result = this.repository.generateToken(this.exchange)\n\t\t\t.delayUntil((t) -> this.repository.saveToken(this.exchange, t));\n\t\tresult.block();\n\t\tWebSession session = this.exchange.getSession().block();\n\t\tMap<String, Object> attributes = session.getAttributes();\n\t\tassertThat(session.isStarted()).isTrue();\n\t\tassertThat(attributes).hasSize(1);\n\t\tassertThat(attributes.values().iterator().next()).isInstanceOf(CsrfToken.class);\n\t}\n\n\t@Test\n\tpublic void saveTokenWhenNullThenDeletes() {\n\t\tCsrfToken token = this.repository.generateToken(this.exchange).block();\n\t\tMono<Void> result = this.repository.saveToken(this.exchange, null);\n\t\tStepVerifier.create(result).verifyComplete();\n\t\tWebSession session = this.exchange.getSession().block();\n\t\tassertThat(session.getAttributes()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void saveTokenChangeSessionId() {\n\t\tString originalSessionId = this.exchange.getSession().block().getId();\n\t\tthis.repository.saveToken(this.exchange, null).block();\n\t\tWebSession session = this.exchange.getSession().block();\n\t\tassertThat(session.getId()).isNotEqualTo(originalSessionId);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.csrf;\n\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.stubbing.Answer;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link XorServerCsrfTokenRequestAttributeHandler}.\n *\n * @author Steve Riesenberg\n * @since 5.8\n */\npublic class XorServerCsrfTokenRequestAttributeHandlerTests {\n\n\t/*\n\t * Token format: 3 random pad bytes + 3 padded bytes.\n\t */\n\tprivate static final byte[] XOR_CSRF_TOKEN_BYTES = new byte[] { 1, 1, 1, 96, 99, 98 };\n\n\tprivate static final String XOR_CSRF_TOKEN_VALUE = Base64.getEncoder().encodeToString(XOR_CSRF_TOKEN_BYTES);\n\n\tprivate XorServerCsrfTokenRequestAttributeHandler handler;\n\n\tprivate MockServerWebExchange exchange;\n\n\tprivate CsrfToken token;\n\n\tprivate SecureRandom secureRandom;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.handler = new XorServerCsrfTokenRequestAttributeHandler();\n\t\tthis.exchange = MockServerWebExchange.builder(MockServerHttpRequest.get(\"/\")).build();\n\t\tthis.token = new DefaultCsrfToken(\"headerName\", \"paramName\", \"abc\");\n\t\tthis.secureRandom = mock(SecureRandom.class);\n\t}\n\n\t@Test\n\tpublic void setSecureRandomWhenNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.setSecureRandom(null))\n\t\t\t\t.withMessage(\"secureRandom cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenExchangeIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(null, Mono.just(this.token)))\n\t\t\t\t.withMessage(\"exchange cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfTokenIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.handle(this.exchange, null))\n\t\t\t\t.withMessage(\"csrfToken cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void handleWhenSecureRandomSetThenUsed() {\n\t\tthis.handler.setSecureRandom(this.secureRandom);\n\t\tthis.handler.handle(this.exchange, Mono.just(this.token));\n\t\tMono<CsrfToken> csrfTokenAttribute = this.exchange.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(csrfTokenAttribute).isNotNull();\n\t\tStepVerifier.create(csrfTokenAttribute).expectNextCount(1).verifyComplete();\n\t\tverify(this.secureRandom).nextBytes(anyByteArray());\n\t}\n\n\t@Test\n\tpublic void handleWhenValidParametersThenExchangeAttributeSet() {\n\t\twillAnswer(fillByteArray()).given(this.secureRandom).nextBytes(anyByteArray());\n\n\t\tthis.handler.setSecureRandom(this.secureRandom);\n\t\tthis.handler.handle(this.exchange, Mono.just(this.token));\n\t\tMono<CsrfToken> csrfTokenAttribute = this.exchange.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(csrfTokenAttribute).isNotNull();\n\t\t// @formatter:off\n\t\tStepVerifier.create(csrfTokenAttribute)\n\t\t\t\t.assertNext((csrfToken) -> assertThat(csrfToken.getToken()).isEqualTo(XOR_CSRF_TOKEN_VALUE))\n\t\t\t\t.verifyComplete();\n\t\t// @formatter:on\n\t\tverify(this.secureRandom).nextBytes(anyByteArray());\n\t}\n\n\t@Test\n\tpublic void handleWhenCsrfTokenRequestedTwiceThenCached() {\n\t\tthis.handler.handle(this.exchange, Mono.just(this.token));\n\t\tMono<CsrfToken> csrfTokenAttribute = this.exchange.getAttribute(CsrfToken.class.getName());\n\t\tassertThat(csrfTokenAttribute).isNotNull();\n\t\tCsrfToken csrfToken1 = csrfTokenAttribute.block();\n\t\tCsrfToken csrfToken2 = csrfTokenAttribute.block();\n\t\tassertThat(csrfToken1.getToken()).isNotEqualTo(this.token.getToken());\n\t\tassertThat(csrfToken1.getToken()).isEqualTo(csrfToken2.getToken());\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenExchangeIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.resolveCsrfTokenValue(null, this.token))\n\t\t\t\t.withMessage(\"exchange cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenCsrfTokenIsNullThenThrowsIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.handler.resolveCsrfTokenValue(this.exchange, null))\n\t\t\t\t.withMessage(\"csrfToken cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenTokenNotSetThenReturnsEmptyMono() {\n\t\tMono<String> csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, this.token);\n\t\tStepVerifier.create(csrfToken).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenFormDataSetThenReturnsTokenValue() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.body(this.token.getParameterName() + \"=\" + XOR_CSRF_TOKEN_VALUE))\n\t\t\t.build();\n\t\tMono<String> csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, this.token);\n\t\tStepVerifier.create(csrfToken).expectNext(this.token.getToken()).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenHeaderSetThenReturnsTokenValue() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE))\n\t\t\t.build();\n\t\tMono<String> csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, this.token);\n\t\tStepVerifier.create(csrfToken).expectNext(this.token.getToken()).verifyComplete();\n\t}\n\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenHeaderAndFormDataSetThenFormDataIsPreferred() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), \"header\")\n\t\t\t\t.body(this.token.getParameterName() + \"=\" + XOR_CSRF_TOKEN_VALUE))\n\t\t\t.build();\n\t\tMono<String> csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, this.token);\n\t\tStepVerifier.create(csrfToken).expectNext(this.token.getToken()).verifyComplete();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenCsrfBytesIsShorterThanRandomBytesThenReturnsNull() {\n\t\t/*\n\t\t * Token format: 3 random pad bytes + 2 padded bytes.\n\t\t */\n\t\tbyte[] actualBytes = { 1, 1, 1, 96, 99 };\n\t\tString actualToken = Base64.getEncoder().encodeToString(actualBytes);\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), actualToken))\n\t\t\t.build();\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.exchange, this.token).block();\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenCsrfBytesIsLongerThanRandomBytesThenReturnsNull() {\n\t\t/*\n\t\t * Token format: 3 random pad bytes + 4 padded bytes.\n\t\t */\n\t\tbyte[] actualBytes = { 1, 1, 1, 96, 99, 98, 97 };\n\t\tString actualToken = Base64.getEncoder().encodeToString(actualBytes);\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), actualToken))\n\t\t\t.build();\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.exchange, this.token).block();\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenTokenBytesIsShorterThanActualBytesThenReturnsNull() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE))\n\t\t\t.build();\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"headerName\", \"paramName\", \"a\");\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.exchange, csrfToken).block();\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenTokenBytesIsLongerThanActualBytesThenReturnsNull() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE))\n\t\t\t.build();\n\t\tCsrfToken csrfToken = new DefaultCsrfToken(\"headerName\", \"paramName\", \"abcde\");\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.exchange, csrfToken).block();\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\t// gh-13310, gh-15184\n\t@Test\n\tpublic void resolveCsrfTokenValueWhenActualBytesIsEmptyThenReturnsNull() {\n\t\tthis.exchange = MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.post(\"/\")\n\t\t\t\t.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n\t\t\t\t.header(this.token.getHeaderName(), \"\"))\n\t\t\t.build();\n\t\tString tokenValue = this.handler.resolveCsrfTokenValue(this.exchange, this.token).block();\n\t\tassertThat(tokenValue).isNull();\n\t}\n\n\tprivate static Answer<Void> fillByteArray() {\n\t\treturn (invocation) -> {\n\t\t\tbyte[] bytes = invocation.getArgument(0);\n\t\t\tArrays.fill(bytes, (byte) 1);\n\t\t\treturn null;\n\t\t};\n\t}\n\n\tprivate static byte[] anyByteArray() {\n\t\treturn any(byte[].class);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/firewall/StrictServerWebExchangeFirewallTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.firewall;\n\nimport java.net.URI;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link StrictServerWebExchangeFirewall}.\n *\n * @author Rob Winch\n * @since 6.4\n */\nclass StrictServerWebExchangeFirewallTests {\n\n\tpublic String[] unnormalizedPaths = { \"http://exploit.example/..\", \"http://exploit.example/./path/\",\n\t\t\t\"http://exploit.example/path/path/.\", \"http://exploit.example/path/path//.\",\n\t\t\t\"http://exploit.example/./path/../path//.\", \"http://exploit.example/./path\",\n\t\t\t\"http://exploit.example/.//path\", \"http://exploit.example/.\", \"http://exploit.example//path\",\n\t\t\t\"http://exploit.example//path/path\", \"http://exploit.example//path//path\",\n\t\t\t\"http://exploit.example/path//path\" };\n\n\tprivate StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();\n\n\tprivate MockServerHttpRequest.BaseBuilder<?> request = get(\"/\");\n\n\t@Test\n\tvoid cookieWhenHasNewLineThenThrowsException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> ResponseCookie.from(\"test\").value(\"Something\\nhere\").build());\n\t}\n\n\t@Test\n\tvoid cookieWhenHasLineFeedThenThrowsException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> ResponseCookie.from(\"test\").value(\"Something\\rhere\").build());\n\t}\n\n\t@Test\n\tvoid responseHeadersWhenValueHasNewLineThenThrowsException() {\n\t\tthis.request = MockServerHttpRequest.get(\"/\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\texchange.getResponse().getHeaders().set(\"FOO\", \"new\\nline\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> exchange.getResponse().setComplete().block());\n\t}\n\n\t@Test\n\tvoid responseHeadersWhenValueHasLineFeedThenThrowsException() {\n\t\tthis.request = MockServerHttpRequest.get(\"/\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\texchange.getResponse().getHeaders().set(\"FOO\", \"line\\rfeed\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> exchange.getResponse().setComplete().block());\n\t}\n\n\t@Test\n\tvoid responseHeadersWhenNameHasNewLineThenThrowsException() {\n\t\tthis.request = MockServerHttpRequest.get(\"/\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\texchange.getResponse().getHeaders().set(\"new\\nline\", \"FOO\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> exchange.getResponse().setComplete().block());\n\t}\n\n\t@Test\n\tvoid responseHeadersWhenNameHasLineFeedThenThrowsException() {\n\t\tthis.request = MockServerHttpRequest.get(\"/\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\texchange.getResponse().getHeaders().set(\"line\\rfeed\", \"FOO\");\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> exchange.getResponse().setComplete().block());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenInvalidMethodThenThrowsServerExchangeRejectedException() {\n\t\tthis.request = MockServerHttpRequest.method(HttpMethod.valueOf(\"INVALID\"), \"/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\tprivate ServerWebExchange getFirewalledExchange() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(this.request.build());\n\t\treturn this.firewall.getFirewalledExchange(exchange).block();\n\t}\n\n\tprivate MockServerHttpRequest.BodyBuilder get(String uri) {\n\t\tURI url = URI.create(uri);\n\t\treturn MockServerHttpRequest.method(HttpMethod.GET, url);\n\t}\n\n\t// blocks XST attacks\n\t@Test\n\tvoid getFirewalledExchangeWhenTraceMethodThenThrowsServerExchangeRejectedException() {\n\t\tthis.request = MockServerHttpRequest.method(HttpMethod.TRACE, \"/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\t// blocks XST attack if request is forwarded to a Microsoft IIS web server\n\tvoid getFirewalledExchangeWhenTrackMethodThenThrowsServerExchangeRejectedException() {\n\t\tthis.request = MockServerHttpRequest.method(HttpMethod.TRACE, \"/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\t// HTTP methods are case sensitive\n\tvoid getFirewalledExchangeWhenLowercaseGetThenThrowsServerExchangeRejectedException() {\n\t\tthis.request = MockServerHttpRequest.method(HttpMethod.valueOf(\"get\"), \"/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenAllowedThenNoException() {\n\t\tList<String> allowedMethods = Arrays.asList(\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\");\n\t\tfor (String allowedMethod : allowedMethods) {\n\t\t\tthis.request = MockServerHttpRequest.method(HttpMethod.valueOf(allowedMethod), \"/\");\n\t\t\tgetFirewalledExchange();\n\t\t}\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenInvalidMethodAndAnyMethodThenNoException() {\n\t\tthis.firewall.setUnsafeAllowAnyHttpMethod(true);\n\t\tthis.request = MockServerHttpRequest.method(HttpMethod.valueOf(\"INVALID\"), \"/\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenURINotNormalizedThenThrowsServerExchangeRejectedException() {\n\t\tfor (String path : this.unnormalizedPaths) {\n\t\t\tthis.request = get(path);\n\t\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class)\n\t\t\t\t.describedAs(\"The path '\" + path + \"' is not normalized\")\n\t\t\t\t.isThrownBy(() -> getFirewalledExchange());\n\t\t}\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenSemicolonInRequestUriThenThrowsServerExchangeRejectedException() {\n\t\tthis.request = get(\"/path;/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenEncodedSemicolonInRequestUriThenThrowsServerExchangeRejectedException() {\n\t\tthis.request = get(\"/path%3B/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenLowercaseEncodedSemicolonInRequestUriThenThrowsServerExchangeRejectedException() {\n\t\tthis.request = MockServerHttpRequest.method(HttpMethod.GET, URI.create(\"/path%3b/\"));\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenSemicolonInRequestUriAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request = get(\"/path;/\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenEncodedSemicolonInRequestUriAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request = get(\"/path%3B/\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenLowercaseEncodedSemicolonInRequestUriAndAllowSemicolonThenNoException() {\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request = get(\"/path%3b/\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenLowercaseEncodedPeriodInThenThrowsServerExchangeRejectedException() {\n\t\tthis.request = get(\"/%2e/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsLowerboundAsciiThenNoException() {\n\t\tthis.request = get(\"/%20\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsUpperboundAsciiThenNoException() {\n\t\tthis.request = get(\"/~\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenJapaneseCharacterThenNoException() {\n\t\t// FIXME: .method(HttpMethod.GET to .get and similar methods\n\t\tthis.request = get(\"/\\u3042\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsEncodedNullThenException() {\n\t\tthis.request = get(\"/something%00/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsLowercaseEncodedLineFeedThenException() {\n\t\tthis.request = get(\"/something%0a/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsUppercaseEncodedLineFeedThenException() {\n\t\tthis.request = get(\"/something%0A/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsLowercaseEncodedCarriageReturnThenException() {\n\t\tthis.request = get(\"/something%0d/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsUppercaseEncodedCarriageReturnThenException() {\n\t\tthis.request = get(\"/something%0D/\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsLowercaseEncodedLineFeedAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedLineFeed(true);\n\t\tthis.request = get(\"/something%0a/\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsUppercaseEncodedLineFeedAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedLineFeed(true);\n\t\tthis.request = get(\"/something%0A/\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsLowercaseEncodedCarriageReturnAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedCarriageReturn(true);\n\t\tthis.request = get(\"/something%0d/\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenContainsUppercaseEncodedCarriageReturnAndAllowedThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedCarriageReturn(true);\n\t\tthis.request = get(\"/something%0D/\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t/**\n\t * On WebSphere 8.5 a URL like /context-root/a/b;%2f1/c can bypass a rule on /a/b/c\n\t * because the pathInfo is /a/b;/1/c which ends up being /a/b/1/c while Spring MVC\n\t * will strip the ; content from requestURI before the path is URL decoded.\n\t */\n\t@Test\n\tvoid getFirewalledExchangeWhenLowercaseEncodedPathThenException() {\n\t\tthis.request = get(\"/context-root/a/b;%2f1/c\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenUppercaseEncodedPathThenException() {\n\t\tthis.request = get(\"/context-root/a/b;%2F1/c\");\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenAllowUrlEncodedSlashAndLowercaseEncodedPathThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request = get(\"/context-root/a/b;%2f1/c\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenAllowUrlEncodedSlashAndUppercaseEncodedPathThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowSemicolon(true);\n\t\tthis.request = get(\"/context-root/a/b;%2F1/c\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenAllowUrlLowerCaseEncodedDoubleSlashThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowUrlEncodedDoubleSlash(true);\n\t\tthis.request = get(\"/context-root/a/b%2f%2fc\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenAllowUrlUpperCaseEncodedDoubleSlashThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowUrlEncodedDoubleSlash(true);\n\t\tthis.request = get(\"/context-root/a/b%2F%2Fc\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenAllowUrlLowerCaseAndUpperCaseEncodedDoubleSlashThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowUrlEncodedDoubleSlash(true);\n\t\tthis.request = get(\"/context-root/a/b%2f%2Fc\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenAllowUrlUpperCaseAndLowerCaseEncodedDoubleSlashThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.firewall.setAllowUrlEncodedDoubleSlash(true);\n\t\tthis.request = get(\"/context-root/a/b%2F%2fc\");\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenRemoveFromUpperCaseEncodedUrlBlocklistThenNoException() {\n\t\tthis.firewall.setAllowUrlEncodedSlash(true);\n\t\tthis.request = get(\"/context-root/a/b%2Fc\");\n\t\tthis.firewall.getEncodedUrlBlocklist().removeAll(Arrays.asList(\"%2F%2F\"));\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenRemoveFromDecodedUrlBlocklistThenNoException() {\n\t\tthis.request = get(\"/a/b%2F%2Fc\");\n\t\tthis.firewall.getDecodedUrlBlocklist().removeAll(Arrays.asList(\"//\"));\n\t\tthis.firewall.getEncodedUrlBlocklist().removeAll(Arrays.asList(\"%2F%2F\"));\n\t\tthis.firewall.getEncodedUrlBlocklist().removeAll(Arrays.asList(\"%2F\"));\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenTrustedDomainThenNoException() {\n\t\tthis.request.header(\"Host\", \"example.org\");\n\t\tthis.firewall.setAllowedHostnames((hostname) -> hostname.equals(\"example.org\"));\n\t\tgetFirewalledExchange();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenUntrustedDomainThenException() {\n\t\tthis.request = get(\"https://example.org\");\n\t\tthis.firewall.setAllowedHostnames((hostname) -> hostname.equals(\"myexample.org\"));\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> getFirewalledExchange());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeaderWhenNotAllowedHeaderNameThenException() {\n\t\tthis.firewall.setAllowedHeaderNames((name) -> !name.equals(\"bad name\"));\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.get(\"bad name\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenHeaderNameNotAllowedWithAugmentedHeaderNamesThenException() {\n\t\tthis.firewall.setAllowedHeaderNames(\n\t\t\t\tStrictServerWebExchangeFirewall.ALLOWED_HEADER_NAMES.and((name) -> !name.equals(\"bad name\")));\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.getFirst(\"bad name\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeaderWhenNotAllowedHeaderValueThenException() {\n\t\tthis.request.header(\"good name\", \"bad value\");\n\t\tthis.firewall.setAllowedHeaderValues((value) -> !value.equals(\"bad value\"));\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.get(\"good name\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenHeaderValueNotAllowedWithAugmentedHeaderValuesThenException() {\n\t\tthis.request.header(\"good name\", \"bad value\");\n\t\tthis.firewall.setAllowedHeaderValues(\n\t\t\t\tStrictServerWebExchangeFirewall.ALLOWED_HEADER_VALUES.and((value) -> !value.equals(\"bad value\")));\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.get(\"good name\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetDateHeaderWhenControlCharacterInHeaderNameThenException() {\n\t\tthis.request.header(\"Bad\\0Name\", \"some value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.get(\"Bad\\0Name\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeaderWhenUndefinedCharacterInHeaderNameThenException() {\n\t\tthis.request.header(\"Bad\\uFFFEName\", \"some value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.get(\"Bad\\uFFFEName\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeadersWhenControlCharacterInHeaderNameThenException() {\n\t\tthis.request.header(\"Bad\\0Name\", \"some value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.get(\"Bad\\0Name\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeaderNamesWhenControlCharacterInHeaderNameThenException() {\n\t\tthis.request.header(\"Bad\\0Name\", \"some value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class)\n\t\t\t.isThrownBy(() -> headers.headerNames().iterator().next());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeaderWhenControlCharacterInHeaderValueThenException() {\n\t\tthis.request.header(\"Something\", \"bad\\0value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.get(\"Something\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeaderWhenHorizontalTabInHeaderValueThenNoException() {\n\t\tthis.request.header(\"Something\", \"tab\\tvalue\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThat(headers.getFirst(\"Something\")).isEqualTo(\"tab\\tvalue\");\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeaderWhenUndefinedCharacterInHeaderValueThenException() {\n\t\tthis.request.header(\"Something\", \"bad\\uFFFEvalue\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class)\n\t\t\t.isThrownBy(() -> headers.getFirst(\"Something\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetHeadersWhenControlCharacterInHeaderValueThenException() {\n\t\tthis.request.header(\"Something\", \"bad\\0value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> headers.get(\"Something\"));\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetParameterWhenControlCharacterInParameterNameThenException() {\n\t\tthis.request.queryParam(\"Bad\\0Name\", \"some value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tServerHttpRequest request = exchange.getRequest();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(request::getQueryParams);\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetParameterValuesWhenNotAllowedInParameterValueThenException() {\n\t\tthis.firewall.setAllowedParameterValues((value) -> !value.equals(\"bad value\"));\n\t\tthis.request.queryParam(\"Something\", \"bad value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tServerHttpRequest request = exchange.getRequest();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> request.getQueryParams());\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeGetParameterValuesWhenNotAllowedInParameterNameThenException() {\n\t\tthis.firewall.setAllowedParameterNames((value) -> !value.equals(\"bad name\"));\n\t\tthis.request.queryParam(\"bad name\", \"good value\");\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tServerHttpRequest request = exchange.getRequest();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class).isThrownBy(() -> request.getQueryParams());\n\t}\n\n\t// gh-9598\n\t@Test\n\tvoid getFirewalledExchangeGetHeaderWhenNameIsNullThenNull() {\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tassertThat(exchange.getRequest().getHeaders().get(null)).isNull();\n\t}\n\n\t@Test\n\tvoid getFirewalledExchangeWhenMutateThenHeadersStillFirewalled() {\n\t\tString invalidHeaderName = \"bad name\";\n\t\tthis.firewall.setAllowedHeaderNames((name) -> !name.equals(invalidHeaderName));\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tServerWebExchange mutatedExchange = exchange.mutate().request(exchange.getRequest().mutate().build()).build();\n\t\tHttpHeaders headers = mutatedExchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class)\n\t\t\t.isThrownBy(() -> headers.get(invalidHeaderName));\n\t}\n\n\t@Test\n\tvoid getMutatedFirewalledExchangeGetHeaderWhenNotAllowedHeaderNameThenException() {\n\t\tString invalidHeaderName = \"bad name\";\n\t\tthis.firewall.setAllowedHeaderNames((name) -> !name.equals(invalidHeaderName));\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tHttpHeaders headers = exchange.getRequest().mutate().build().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class)\n\t\t\t.isThrownBy(() -> headers.get(invalidHeaderName));\n\t}\n\n\t// gh-16978\n\t@Test\n\tvoid getMutatedFirewalledExchangeHeadersGetHeaderWhenNotAllowedHeaderNameThenException() {\n\t\tString invalidHeaderName = \"bad name\";\n\t\tthis.firewall.setAllowedHeaderNames((name) -> !name.equals(invalidHeaderName));\n\t\tServerWebExchange exchange = getFirewalledExchange();\n\t\tvar mutatedRequest = exchange.getRequest().mutate().method(HttpMethod.POST).build();\n\t\tvar mutatedExchange = exchange.mutate().request(mutatedRequest).build();\n\t\tHttpHeaders headers = mutatedExchange.getRequest().getHeaders();\n\t\tassertThatExceptionOfType(ServerExchangeRejectedException.class)\n\t\t\t.isThrownBy(() -> headers.get(invalidHeaderName));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/CacheControlServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n *\n */\npublic class CacheControlServerHttpHeadersWriterTests {\n\n\tCacheControlServerHttpHeadersWriter writer = new CacheControlServerHttpHeadersWriter();\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\n\t@Test\n\tpublic void writeHeadersWhenCacheHeadersThenWritesAllCacheControl() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(3);\n\t\tassertThat(this.headers.get(HttpHeaders.CACHE_CONTROL))\n\t\t\t.containsOnly(CacheControlServerHttpHeadersWriter.CACHE_CONTROL_VALUE);\n\t\tassertThat(this.headers.get(HttpHeaders.EXPIRES))\n\t\t\t.containsOnly(CacheControlServerHttpHeadersWriter.EXPIRES_VALUE);\n\t\tassertThat(this.headers.get(HttpHeaders.PRAGMA)).containsOnly(CacheControlServerHttpHeadersWriter.PRAGMA_VALUE);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenCacheControlThenNoCacheControlHeaders() {\n\t\tString cacheControl = \"max-age=1234\";\n\t\tthis.headers.set(HttpHeaders.CACHE_CONTROL, cacheControl);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.get(HttpHeaders.CACHE_CONTROL)).containsOnly(cacheControl);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenPragmaThenNoCacheControlHeaders() {\n\t\tString pragma = \"1\";\n\t\tthis.headers.set(HttpHeaders.PRAGMA, pragma);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(HttpHeaders.PRAGMA)).containsOnly(pragma);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenExpiresThenNoCacheControlHeaders() {\n\t\tString expires = \"1\";\n\t\tthis.headers.set(HttpHeaders.EXPIRES, expires);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(HttpHeaders.EXPIRES)).containsOnly(expires);\n\t}\n\n\t@Test\n\t// gh-5534\n\tpublic void writeHeadersWhenNotModifiedThenNoCacheControlHeaders() {\n\t\tthis.exchange.getResponse().setStatusCode(HttpStatus.NOT_MODIFIED);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).isEmpty();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/ClearSiteDataServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.AbstractAssert;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.header.ClearSiteDataServerHttpHeadersWriter.Directive;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author MD Sayem Ahmed\n * @since 5.2\n */\npublic class ClearSiteDataServerHttpHeadersWriterTests {\n\n\t@Test\n\tpublic void constructorWhenMissingDirectivesThenThrowsException() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(ClearSiteDataServerHttpHeadersWriter::new);\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenSecureConnectionThenHeaderWritten() {\n\t\tClearSiteDataServerHttpHeadersWriter writer = new ClearSiteDataServerHttpHeadersWriter(Directive.ALL);\n\t\tServerWebExchange secureExchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"https://localhost\").build());\n\t\twriter.writeHttpHeaders(secureExchange);\n\t\tassertThat(secureExchange.getResponse()).hasClearSiteDataHeaderDirectives(Directive.ALL);\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenInsecureConnectionThenHeaderNotWritten() {\n\t\tClearSiteDataServerHttpHeadersWriter writer = new ClearSiteDataServerHttpHeadersWriter(Directive.ALL);\n\t\tServerWebExchange insecureExchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\t\twriter.writeHttpHeaders(insecureExchange);\n\t\tassertThat(insecureExchange.getResponse()).doesNotHaveClearSiteDataHeaderSet();\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenMultipleDirectivesSpecifiedThenHeaderContainsAll() {\n\t\tClearSiteDataServerHttpHeadersWriter writer = new ClearSiteDataServerHttpHeadersWriter(Directive.CACHE,\n\t\t\t\tDirective.COOKIES);\n\t\tServerWebExchange secureExchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"https://localhost\").build());\n\t\twriter.writeHttpHeaders(secureExchange);\n\t\tassertThat(secureExchange.getResponse()).hasClearSiteDataHeaderDirectives(Directive.CACHE, Directive.COOKIES);\n\t}\n\n\tprivate static ClearSiteDataAssert assertThat(ServerHttpResponse response) {\n\t\treturn new ClearSiteDataAssert(response);\n\t}\n\n\tprivate static class ClearSiteDataAssert extends AbstractAssert<ClearSiteDataAssert, ServerHttpResponse> {\n\n\t\tClearSiteDataAssert(ServerHttpResponse response) {\n\t\t\tsuper(response, ClearSiteDataAssert.class);\n\t\t}\n\n\t\tvoid hasClearSiteDataHeaderDirectives(Directive... directives) {\n\t\t\tisNotNull();\n\t\t\tList<String> header = getHeader();\n\t\t\tString actualHeaderValue = String.join(\"\", header);\n\t\t\tString expectedHeaderVale = Stream.of(directives)\n\t\t\t\t.map(Directive::getHeaderValue)\n\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t\tif (!actualHeaderValue.equals(expectedHeaderVale)) {\n\t\t\t\tfailWithMessage(\"Expected to have %s as Clear-Site-Data header value but found %s\", expectedHeaderVale,\n\t\t\t\t\t\tactualHeaderValue);\n\t\t\t}\n\t\t}\n\n\t\tvoid doesNotHaveClearSiteDataHeaderSet() {\n\t\t\tisNotNull();\n\t\t\tList<String> header = getHeader();\n\t\t\tif (!CollectionUtils.isEmpty(header)) {\n\t\t\t\tfailWithMessage(\"Expected not to have Clear-Site-Data header set but found %s\",\n\t\t\t\t\t\tString.join(\"\", header));\n\t\t\t}\n\t\t}\n\n\t\tList<String> getHeader() {\n\t\t\treturn this.actual.getHeaders().get(ClearSiteDataServerHttpHeadersWriter.CLEAR_SITE_DATA_HEADER);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/CompositeServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.time.Duration;\nimport java.util.Arrays;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class CompositeServerHttpHeadersWriterTests {\n\n\t@Mock\n\tServerHttpHeadersWriter writer1;\n\n\t@Mock\n\tServerHttpHeadersWriter writer2;\n\n\tCompositeServerHttpHeadersWriter writer;\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.writer = new CompositeServerHttpHeadersWriter(Arrays.asList(this.writer1, this.writer2));\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenErrorNoErrorThenError() {\n\t\tgiven(this.writer1.writeHttpHeaders(this.exchange)).willReturn(Mono.error(new RuntimeException()));\n\t\tMono<Void> result = this.writer.writeHttpHeaders(this.exchange);\n\t\tStepVerifier.create(result).expectError().verify();\n\t\tverify(this.writer1).writeHttpHeaders(this.exchange);\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenErrorErrorThenError() {\n\t\tgiven(this.writer1.writeHttpHeaders(this.exchange)).willReturn(Mono.error(new RuntimeException()));\n\t\tMono<Void> result = this.writer.writeHttpHeaders(this.exchange);\n\t\tStepVerifier.create(result).expectError().verify();\n\t\tverify(this.writer1).writeHttpHeaders(this.exchange);\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenNoErrorThenNoError() {\n\t\tgiven(this.writer1.writeHttpHeaders(this.exchange)).willReturn(Mono.empty());\n\t\tgiven(this.writer2.writeHttpHeaders(this.exchange)).willReturn(Mono.empty());\n\t\tMono<Void> result = this.writer.writeHttpHeaders(this.exchange);\n\t\tStepVerifier.create(result).expectComplete().verify();\n\t\tverify(this.writer1).writeHttpHeaders(this.exchange);\n\t\tverify(this.writer2).writeHttpHeaders(this.exchange);\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersSequential() throws Exception {\n\t\tAtomicBoolean slowDone = new AtomicBoolean();\n\t\tCountDownLatch latch = new CountDownLatch(1);\n\t\tServerHttpHeadersWriter slow = (\n\t\t\t\texchange) -> Mono.delay(Duration.ofMillis(100)).doOnSuccess((__) -> slowDone.set(true)).then();\n\t\tServerHttpHeadersWriter second = (exchange) -> Mono.fromRunnable(() -> {\n\t\t\tlatch.countDown();\n\t\t\tassertThat(slowDone.get()).describedAs(\"ServerLogoutHandler should be executed sequentially\").isTrue();\n\t\t});\n\t\tCompositeServerHttpHeadersWriter writer = new CompositeServerHttpHeadersWriter(slow, second);\n\t\twriter.writeHttpHeaders(this.exchange).block();\n\t\tassertThat(latch.await(3, TimeUnit.SECONDS)).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/ContentSecurityPolicyServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ContentSecurityPolicyServerHttpHeadersWriter}.\n *\n * @author Vedran Pavic\n */\npublic class ContentSecurityPolicyServerHttpHeadersWriterTests {\n\n\tprivate static final String DEFAULT_POLICY_DIRECTIVES = \"default-src 'self'\";\n\n\tprivate ServerWebExchange exchange;\n\n\tprivate ContentSecurityPolicyServerHttpHeadersWriter writer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tthis.writer = new ContentSecurityPolicyServerHttpHeadersWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingDefaultsThenDoesNotWrite() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingPolicyThenWritesPolicy() {\n\t\tthis.writer.setPolicyDirectives(DEFAULT_POLICY_DIRECTIVES);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY))\n\t\t\t.containsOnly(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenReportPolicyThenWritesReportPolicy() {\n\t\tthis.writer.setPolicyDirectives(DEFAULT_POLICY_DIRECTIVES);\n\t\tthis.writer.setReportOnly(true);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY_REPORT_ONLY))\n\t\t\t.containsOnly(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenOnlyReportOnlySetThenDoesNotWrite() {\n\t\tthis.writer.setReportOnly(true);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenAlreadyWrittenThenWritesHeader() {\n\t\tString headerValue = \"default-src https: 'self'\";\n\t\tthis.exchange.getResponse()\n\t\t\t.getHeaders()\n\t\t\t.set(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY))\n\t\t\t.containsOnly(headerValue);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/ContentTypeOptionsServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ContentTypeOptionsServerHttpHeadersWriter}\n *\n * @author Marcus da Coregio\n */\nclass ContentTypeOptionsServerHttpHeadersWriterTests {\n\n\tContentTypeOptionsServerHttpHeadersWriter writer = new ContentTypeOptionsServerHttpHeadersWriter();\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\n\t@Test\n\tvoid writeHeadersWhenNoHeadersThenWriteHeaders() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS))\n\t\t\t.containsOnly(ContentTypeOptionsServerHttpHeadersWriter.NOSNIFF);\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenHeaderWrittenThenDoesNotOverride() {\n\t\tString headerValue = \"value\";\n\t\tthis.headers.set(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS))\n\t\t\t.containsOnly(headerValue);\n\t}\n\n\t@Test\n\tvoid constantsMatchExpectedHeaderAndValue() {\n\t\tassertThat(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS).isEqualTo(\"X-Content-Type-Options\");\n\t\tassertThat(ContentTypeOptionsServerHttpHeadersWriter.NOSNIFF).isEqualTo(\"nosniff\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/CrossOriginEmbedderPolicyServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\nclass CrossOriginEmbedderPolicyServerHttpHeadersWriterTests {\n\n\tprivate ServerWebExchange exchange;\n\n\tprivate CrossOriginEmbedderPolicyServerHttpHeadersWriter writer;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tthis.writer = new CrossOriginEmbedderPolicyServerHttpHeadersWriter();\n\t}\n\n\t@Test\n\tvoid setEmbedderPolicyWhenNullEmbedderPolicyThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))\n\t\t\t.withMessage(\"embedderPolicy cannot be null\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenResponseHeaderExistsThenDontOverride() {\n\t\tthis.exchange.getResponse()\n\t\t\t.getHeaders()\n\t\t\t.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY, \"require-corp\");\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY))\n\t\t\t.containsOnly(\"require-corp\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetHeaderValuesThenWrites() {\n\t\tthis.writer.setPolicy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY))\n\t\t\t.containsOnly(\"require-corp\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/CrossOriginOpenerPolicyServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\nclass CrossOriginOpenerPolicyServerHttpHeadersWriterTests {\n\n\tprivate ServerWebExchange exchange;\n\n\tprivate CrossOriginOpenerPolicyServerHttpHeadersWriter writer;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tthis.writer = new CrossOriginOpenerPolicyServerHttpHeadersWriter();\n\t}\n\n\t@Test\n\tvoid setOpenerPolicyWhenNullOpenerPolicyThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))\n\t\t\t.withMessage(\"openerPolicy cannot be null\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenResponseHeaderExistsThenDontOverride() {\n\t\tthis.exchange.getResponse()\n\t\t\t.getHeaders()\n\t\t\t.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY, \"same-origin\");\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY))\n\t\t\t.containsOnly(\"same-origin\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetHeaderValuesThenWrites() {\n\t\tthis.writer\n\t\t\t.setPolicy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY))\n\t\t\t.containsOnly(\"same-origin-allow-popups\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/CrossOriginResourcePolicyServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\nclass CrossOriginResourcePolicyServerHttpHeadersWriterTests {\n\n\tprivate ServerWebExchange exchange;\n\n\tprivate CrossOriginResourcePolicyServerHttpHeadersWriter writer;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tthis.writer = new CrossOriginResourcePolicyServerHttpHeadersWriter();\n\t}\n\n\t@Test\n\tvoid setResourcePolicyWhenNullThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))\n\t\t\t.withMessage(\"resourcePolicy cannot be null\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenResponseHeaderExistsThenDontOverride() {\n\t\tthis.exchange.getResponse()\n\t\t\t.getHeaders()\n\t\t\t.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY, \"same-origin\");\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY))\n\t\t\t.containsOnly(\"same-origin\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenSetHeaderValuesThenWrites() {\n\t\tthis.writer.setPolicy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY))\n\t\t\t.containsOnly(\"same-origin\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/FeaturePolicyServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link FeaturePolicyServerHttpHeadersWriter}.\n *\n * @author Vedran Pavic\n */\npublic class FeaturePolicyServerHttpHeadersWriterTests {\n\n\tprivate static final String DEFAULT_POLICY_DIRECTIVES = \"geolocation 'self'\";\n\n\tprivate ServerWebExchange exchange;\n\n\tprivate FeaturePolicyServerHttpHeadersWriter writer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tthis.writer = new FeaturePolicyServerHttpHeadersWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingDefaultsThenDoesNotWrite() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingPolicyThenWritesPolicy() {\n\t\tthis.writer.setPolicyDirectives(DEFAULT_POLICY_DIRECTIVES);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(FeaturePolicyServerHttpHeadersWriter.FEATURE_POLICY))\n\t\t\t.containsOnly(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenAlreadyWrittenThenWritesHeader() {\n\t\tthis.writer.setPolicyDirectives(DEFAULT_POLICY_DIRECTIVES);\n\t\tString headerValue = \"camera: 'self'\";\n\t\tthis.exchange.getResponse().getHeaders().set(FeaturePolicyServerHttpHeadersWriter.FEATURE_POLICY, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(FeaturePolicyServerHttpHeadersWriter.FEATURE_POLICY)).containsOnly(headerValue);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/HttpHeaderWriterWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.security.test.web.reactive.server.WebTestClientBuilder;\nimport org.springframework.security.test.web.reactive.server.WebTestHandler;\nimport org.springframework.security.test.web.reactive.server.WebTestHandler.WebHandlerResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class HttpHeaderWriterWebFilterTests {\n\n\t@Mock\n\tServerHttpHeadersWriter writer;\n\n\tHttpHeaderWriterWebFilter filter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tgiven(this.writer.writeHttpHeaders(any())).willReturn(Mono.empty());\n\t\tthis.filter = new HttpHeaderWriterWebFilter(this.writer);\n\t}\n\n\t@Test\n\tpublic void filterWhenCompleteThenWritten() {\n\t\tWebTestClient rest = WebTestClientBuilder.bindToWebFilters(this.filter).build();\n\t\trest.get().uri(\"/foo\").exchange();\n\t\tverify(this.writer).writeHttpHeaders(any());\n\t}\n\n\t@Test\n\tpublic void filterWhenNotCompleteThenNotWritten() {\n\t\tWebTestHandler handler = WebTestHandler.bindToWebFilters(this.filter);\n\t\tWebHandlerResult result = handler.exchange(MockServerHttpRequest.get(\"/foo\"));\n\t\tverify(this.writer, never()).writeHttpHeaders(any());\n\t\tresult.getExchange().getResponse().setComplete().block();\n\t\tverify(this.writer).writeHttpHeaders(any());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/PermissionsPolicyServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link PermissionsPolicyServerHttpHeadersWriter}.\n *\n * @author Christophe Gilles\n */\npublic class PermissionsPolicyServerHttpHeadersWriterTests {\n\n\tprivate static final String DEFAULT_POLICY_DIRECTIVES = \"geolocation=(self)\";\n\n\tprivate ServerWebExchange exchange;\n\n\tprivate PermissionsPolicyServerHttpHeadersWriter writer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tthis.writer = new PermissionsPolicyServerHttpHeadersWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingDefaultsThenDoesNotWrite() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingPolicyThenWritesPolicy() {\n\t\tthis.writer.setPolicy(DEFAULT_POLICY_DIRECTIVES);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(PermissionsPolicyServerHttpHeadersWriter.PERMISSIONS_POLICY))\n\t\t\t.containsOnly(DEFAULT_POLICY_DIRECTIVES);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenAlreadyWrittenThenWritesHeader() {\n\t\tthis.writer.setPolicy(DEFAULT_POLICY_DIRECTIVES);\n\t\tString headerValue = \"camera=(self)\";\n\t\tthis.exchange.getResponse()\n\t\t\t.getHeaders()\n\t\t\t.set(PermissionsPolicyServerHttpHeadersWriter.PERMISSIONS_POLICY, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(PermissionsPolicyServerHttpHeadersWriter.PERMISSIONS_POLICY)).containsOnly(headerValue);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/ReferrerPolicyServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ReferrerPolicyServerHttpHeadersWriter}.\n *\n * @author Vedran Pavic\n */\npublic class ReferrerPolicyServerHttpHeadersWriterTests {\n\n\tprivate ServerWebExchange exchange;\n\n\tprivate ReferrerPolicyServerHttpHeadersWriter writer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tthis.writer = new ReferrerPolicyServerHttpHeadersWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingDefaultsThenDoesNotWrite() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY))\n\t\t\t.containsOnly(ReferrerPolicy.NO_REFERRER.getPolicy());\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingPolicyThenWritesPolicy() {\n\t\tthis.writer.setPolicy(ReferrerPolicy.SAME_ORIGIN);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY))\n\t\t\t.containsOnly(ReferrerPolicy.SAME_ORIGIN.getPolicy());\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenAlreadyWrittenThenWritesHeader() {\n\t\tString headerValue = ReferrerPolicy.SAME_ORIGIN.getPolicy();\n\t\tthis.exchange.getResponse()\n\t\t\t.getHeaders()\n\t\t\t.set(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY)).containsOnly(headerValue);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/ServerWebExchangeDelegatingServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcherEntry;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author David Herberth\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServerWebExchangeDelegatingServerHttpHeadersWriterTests {\n\n\t@Mock\n\tprivate ServerWebExchangeMatcher matcher;\n\n\t@Mock\n\tprivate ServerHttpHeadersWriter delegate;\n\n\tprivate ServerWebExchange exchange;\n\n\tprivate ServerWebExchangeDelegatingServerHttpHeadersWriter headerWriter;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tthis.headerWriter = new ServerWebExchangeDelegatingServerHttpHeadersWriter(this.matcher, this.delegate);\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullWebExchangeMatcherThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerWebExchangeDelegatingServerHttpHeadersWriter(null, this.delegate))\n\t\t\t.withMessage(\"webExchangeMatcher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullWebExchangeMatcherEntryThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerWebExchangeDelegatingServerHttpHeadersWriter(null))\n\t\t\t.withMessage(\"headersWriter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullDelegateHeadersWriterThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerWebExchangeDelegatingServerHttpHeadersWriter(this.matcher, null))\n\t\t\t.withMessage(\"delegateHeadersWriter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenEntryWithNullMatcherThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerWebExchangeDelegatingServerHttpHeadersWriter(\n\t\t\t\t\tnew ServerWebExchangeMatcherEntry<>(null, this.delegate)))\n\t\t\t.withMessage(\"webExchangeMatcher cannot be null\");\n\t}\n\n\t@Test\n\tpublic void constructorWhenEntryWithNullEntryThenException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new ServerWebExchangeDelegatingServerHttpHeadersWriter(\n\t\t\t\t\tnew ServerWebExchangeMatcherEntry<>(this.matcher, null)))\n\t\t\t.withMessage(\"delegateHeadersWriter cannot be null\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenMatchThenDelegateWriteHttpHeaders() {\n\t\tgiven(this.matcher.matches(this.exchange))\n\t\t\t.willReturn(ServerWebExchangeMatcher.MatchResult.match(Collections.emptyMap()));\n\t\tgiven(this.delegate.writeHttpHeaders(this.exchange)).willReturn(Mono.empty());\n\t\tthis.headerWriter.writeHttpHeaders(this.exchange).block();\n\t\tverify(this.delegate).writeHttpHeaders(this.exchange);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenNoMatchThenDelegateNotCalled() {\n\t\tgiven(this.matcher.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tthis.headerWriter.writeHttpHeaders(this.exchange).block();\n\t\tverify(this.matcher).matches(this.exchange);\n\t\tverify(this.delegate, times(0)).writeHttpHeaders(this.exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/StaticServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.util.Locale;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class StaticServerHttpHeadersWriterTests {\n\n\tStaticServerHttpHeadersWriter writer = StaticServerHttpHeadersWriter.builder()\n\t\t.header(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS,\n\t\t\t\tContentTypeOptionsServerHttpHeadersWriter.NOSNIFF)\n\t\t.build();\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\n\t@Test\n\tpublic void writeHeadersWhenSingleHeaderThenWritesHeader() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.get(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS))\n\t\t\t.containsOnly(ContentTypeOptionsServerHttpHeadersWriter.NOSNIFF);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenSingleHeaderAndHeaderWrittenThenSuccess() {\n\t\tString headerValue = \"other\";\n\t\tthis.headers.set(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.get(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS))\n\t\t\t.containsOnly(headerValue);\n\t}\n\n\t// gh-10557\n\t@Test\n\tpublic void writeHeadersWhenHeaderWrittenWithDifferentCaseThenDoesNotWriteHeaders() {\n\t\tString headerName = HttpHeaders.CACHE_CONTROL.toLowerCase(Locale.ROOT);\n\t\tString headerValue = \"max-age=120\";\n\t\tthis.headers.set(headerName, headerValue);\n\t\t// Note: This test inverts which collection uses case sensitive headers,\n\t\t// due to the fact that gh-10557 reports NettyHeadersAdapter as the\n\t\t// response headers implementation, which is not accessible here.\n\t\tHttpHeaders caseSensitiveHeaders = new HttpHeaders(new LinkedMultiValueMap<>());\n\t\tcaseSensitiveHeaders.set(HttpHeaders.CACHE_CONTROL, CacheControlServerHttpHeadersWriter.CACHE_CONTROL_VALUE);\n\t\tcaseSensitiveHeaders.set(HttpHeaders.PRAGMA, CacheControlServerHttpHeadersWriter.PRAGMA_VALUE);\n\t\tcaseSensitiveHeaders.set(HttpHeaders.EXPIRES, CacheControlServerHttpHeadersWriter.EXPIRES_VALUE);\n\t\tthis.writer = new StaticServerHttpHeadersWriter(caseSensitiveHeaders);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.get(headerName)).containsOnly(headerValue);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenMultiHeaderThenWritesAllHeaders() {\n\t\tthis.writer = StaticServerHttpHeadersWriter.builder()\n\t\t\t.header(HttpHeaders.CACHE_CONTROL, CacheControlServerHttpHeadersWriter.CACHE_CONTROL_VALUE)\n\t\t\t.header(HttpHeaders.PRAGMA, CacheControlServerHttpHeadersWriter.PRAGMA_VALUE)\n\t\t\t.header(HttpHeaders.EXPIRES, CacheControlServerHttpHeadersWriter.EXPIRES_VALUE)\n\t\t\t.build();\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.get(HttpHeaders.CACHE_CONTROL))\n\t\t\t.containsOnly(CacheControlServerHttpHeadersWriter.CACHE_CONTROL_VALUE);\n\t\tassertThat(this.headers.get(HttpHeaders.PRAGMA)).containsOnly(CacheControlServerHttpHeadersWriter.PRAGMA_VALUE);\n\t\tassertThat(this.headers.get(HttpHeaders.EXPIRES))\n\t\t\t.containsOnly(CacheControlServerHttpHeadersWriter.EXPIRES_VALUE);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenMultiHeaderAndSingleWrittenThenNoHeadersOverridden() {\n\t\tString headerValue = \"other\";\n\t\tthis.headers.set(HttpHeaders.CACHE_CONTROL, headerValue);\n\t\tthis.writer = StaticServerHttpHeadersWriter.builder()\n\t\t\t.header(HttpHeaders.CACHE_CONTROL, CacheControlServerHttpHeadersWriter.CACHE_CONTROL_VALUE)\n\t\t\t.header(HttpHeaders.PRAGMA, CacheControlServerHttpHeadersWriter.PRAGMA_VALUE)\n\t\t\t.header(HttpHeaders.EXPIRES, CacheControlServerHttpHeadersWriter.EXPIRES_VALUE)\n\t\t\t.build();\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(HttpHeaders.CACHE_CONTROL)).containsOnly(headerValue);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/StrictTransportSecurityServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport java.time.Duration;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class StrictTransportSecurityServerHttpHeadersWriterTests {\n\n\tStrictTransportSecurityServerHttpHeadersWriter hsts = new StrictTransportSecurityServerHttpHeadersWriter();\n\n\tServerWebExchange exchange;\n\n\t@Test\n\tpublic void writeHttpHeadersWhenHttpsThenWrites() {\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"https://example.com/\"));\n\t\tthis.hsts.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.containsHeaderValue(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=31536000 ; includeSubDomains\"))\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenCustomMaxAgeThenWrites() {\n\t\tDuration maxAge = Duration.ofDays(1);\n\t\tthis.hsts.setMaxAge(maxAge);\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"https://example.com/\"));\n\t\tthis.hsts.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.containsHeaderValue(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=\" + maxAge.getSeconds() + \" ; includeSubDomains\"));\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenCustomIncludeSubDomainsThenWrites() {\n\t\tthis.hsts.setIncludeSubDomains(false);\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"https://example.com/\"));\n\t\tthis.hsts.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.containsHeaderValue(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY,\n\t\t\t\t\"max-age=31536000\"));\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenNullSchemeThenNoHeaders() {\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"/\"));\n\t\tthis.hsts.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\t@Test\n\tpublic void writeHttpHeadersWhenHttpThenNoHeaders() {\n\t\tthis.exchange = exchange(MockServerHttpRequest.get(\"http://localhost/\"));\n\t\tthis.hsts.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).isEmpty();\n\t}\n\n\tprivate static MockServerWebExchange exchange(MockServerHttpRequest.BaseBuilder<?> request) {\n\t\treturn MockServerWebExchange.from(request.build());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/XContentTypeOptionsServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link XContentTypeOptionsServerHttpHeadersWriter}\n *\n * @author Rob Winch\n * @since 5.0\n */\npublic class XContentTypeOptionsServerHttpHeadersWriterTests {\n\n\tXContentTypeOptionsServerHttpHeadersWriter writer = new XContentTypeOptionsServerHttpHeadersWriter();\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\n\t@Test\n\tpublic void writeHeadersWhenNoHeadersThenWriteHeadersForXContentTypeOptionsServerHttpHeadersWriter() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(XContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS))\n\t\t\t.containsOnly(XContentTypeOptionsServerHttpHeadersWriter.NOSNIFF);\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenHeaderWrittenThenDoesNotOverrideForXContentTypeOptionsServerHttpHeadersWriter() {\n\t\tString headerValue = \"value\";\n\t\tthis.headers.set(XContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(XContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS))\n\t\t\t.containsOnly(headerValue);\n\t}\n\n\t@Test\n\tpublic void constantsMatchExpectedHeaderAndValueForXContentTypeOptionsServerHttpHeadersWriter() {\n\t\tassertThat(XContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS).isEqualTo(\"X-Content-Type-Options\");\n\t\tassertThat(XContentTypeOptionsServerHttpHeadersWriter.NOSNIFF).isEqualTo(\"nosniff\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/XFrameOptionsServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class XFrameOptionsServerHttpHeadersWriterTests {\n\n\tServerWebExchange exchange = exchange(MockServerHttpRequest.get(\"/\"));\n\n\tXFrameOptionsServerHttpHeadersWriter writer;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.writer = new XFrameOptionsServerHttpHeadersWriter();\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingDefaultsThenWritesDeny() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS)).containsOnly(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingExplicitDenyThenWritesDeny() {\n\t\tthis.writer.setMode(XFrameOptionsServerHttpHeadersWriter.Mode.DENY);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS)).containsOnly(\"DENY\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenUsingSameOriginThenWritesSameOrigin() {\n\t\tthis.writer.setMode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS)).containsOnly(\"SAMEORIGIN\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenAlreadyWrittenThenWritesHeader() {\n\t\tString headerValue = \"other\";\n\t\tthis.exchange.getResponse().getHeaders().set(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\t\tassertThat(headers.headerNames()).hasSize(1);\n\t\tassertThat(headers.get(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS)).containsOnly(headerValue);\n\t}\n\n\tprivate static MockServerWebExchange exchange(MockServerHttpRequest.BaseBuilder<?> request) {\n\t\treturn MockServerWebExchange.from(request.build());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/header/XXssProtectionServerHttpHeadersWriterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.header;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class XXssProtectionServerHttpHeadersWriterTests {\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\tHttpHeaders headers = this.exchange.getResponse().getHeaders();\n\n\tXXssProtectionServerHttpHeadersWriter writer = new XXssProtectionServerHttpHeadersWriter();\n\n\t@Test\n\tvoid setHeaderValueNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.writer.setHeaderValue(null))\n\t\t\t.withMessage(\"headerValue cannot be null\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenNoHeadersThenWriteHeaders() {\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly(\"0\");\n\t}\n\n\t@Test\n\tpublic void writeHeadersWhenHeaderWrittenThenDoesNotOverride() {\n\t\tString headerValue = \"value\";\n\t\tthis.headers.set(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, headerValue);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly(headerValue);\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenDisabledThenWriteHeaders() {\n\t\tthis.writer.setHeaderValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly(\"0\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenEnabledThenWriteHeaders() {\n\t\tthis.writer.setHeaderValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly(\"1\");\n\t}\n\n\t@Test\n\tvoid writeHeadersWhenEnabledModeBlockThenWriteHeaders() {\n\t\tthis.writer.setHeaderValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK);\n\t\tthis.writer.writeHttpHeaders(this.exchange);\n\t\tassertThat(this.headers.headerNames()).hasSize(1);\n\t\tassertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION))\n\t\t\t.containsOnly(\"1; mode=block\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/jackson/DefaultCsrfServerTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.jackson;\n\nimport java.io.IOException;\n\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.JacksonException;\n\nimport org.springframework.security.web.jackson.AbstractMixinTests;\nimport org.springframework.security.web.server.csrf.DefaultCsrfToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Sebastien Deleuze\n * @author Boris Finkelshteyn\n */\npublic class DefaultCsrfServerTokenMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String CSRF_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.server.csrf.DefaultCsrfToken\\\", \"\n\t\t\t+ \"\\\"headerName\\\": \\\"csrf-header\\\", \"\n\t\t\t+ \"\\\"parameterName\\\": \\\"_csrf\\\", \"\n\t\t\t+ \"\\\"token\\\": \\\"1\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void defaultCsrfTokenSerializedTest() throws JacksonException, JSONException {\n\t\tDefaultCsrfToken token = new DefaultCsrfToken(\"csrf-header\", \"_csrf\", \"1\");\n\t\tString serializedJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(CSRF_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeTest() throws IOException {\n\t\tDefaultCsrfToken token = this.mapper.readValue(CSRF_JSON, DefaultCsrfToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getHeaderName()).isEqualTo(\"csrf-header\");\n\t\tassertThat(token.getParameterName()).isEqualTo(\"_csrf\");\n\t\tassertThat(token.getToken()).isEqualTo(\"1\");\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeWithoutClassTest() throws IOException {\n\t\tString tokenJson = \"{\\\"headerName\\\": \\\"csrf-header\\\", \\\"parameterName\\\": \\\"_csrf\\\", \\\"token\\\": \\\"1\\\"}\";\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(tokenJson, DefaultCsrfToken.class));\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeNullValuesTest() throws IOException {\n\t\tString tokenJson = \"{\\\"@class\\\": \\\"org.springframework.security.web.server.csrf.DefaultCsrfToken\\\", \\\"headerName\\\": \\\"\\\", \\\"parameterName\\\": null, \\\"token\\\": \\\"1\\\"}\";\n\t\tassertThatExceptionOfType(JacksonException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(tokenJson, DefaultCsrfToken.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/jackson2/DefaultCsrfServerTokenMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.jackson2;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport org.json.JSONException;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.web.jackson2.AbstractMixinTests;\nimport org.springframework.security.web.server.csrf.DefaultCsrfToken;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\n/**\n * @author Boris Finkelshteyn\n * @since 5.1\n */\npublic class DefaultCsrfServerTokenMixinTests extends AbstractMixinTests {\n\n\t// @formatter:off\n\tprivate static final String CSRF_JSON = \"{\"\n\t\t\t+ \"\\\"@class\\\": \\\"org.springframework.security.web.server.csrf.DefaultCsrfToken\\\", \"\n\t\t\t+ \"\\\"headerName\\\": \\\"csrf-header\\\", \"\n\t\t\t+ \"\\\"parameterName\\\": \\\"_csrf\\\", \"\n\t\t\t+ \"\\\"token\\\": \\\"1\\\"\"\n\t\t\t+ \"}\";\n\t// @formatter:on\n\t@Test\n\tpublic void defaultCsrfTokenSerializedTest() throws JsonProcessingException, JSONException {\n\t\tDefaultCsrfToken token = new DefaultCsrfToken(\"csrf-header\", \"_csrf\", \"1\");\n\t\tString serializedJson = this.mapper.writeValueAsString(token);\n\t\tJSONAssert.assertEquals(CSRF_JSON, serializedJson, true);\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeTest() throws IOException {\n\t\tDefaultCsrfToken token = this.mapper.readValue(CSRF_JSON, DefaultCsrfToken.class);\n\t\tassertThat(token).isNotNull();\n\t\tassertThat(token.getHeaderName()).isEqualTo(\"csrf-header\");\n\t\tassertThat(token.getParameterName()).isEqualTo(\"_csrf\");\n\t\tassertThat(token.getToken()).isEqualTo(\"1\");\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeWithoutClassTest() throws IOException {\n\t\tString tokenJson = \"{\\\"headerName\\\": \\\"csrf-header\\\", \\\"parameterName\\\": \\\"_csrf\\\", \\\"token\\\": \\\"1\\\"}\";\n\t\tassertThatExceptionOfType(JsonMappingException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(tokenJson, DefaultCsrfToken.class));\n\t}\n\n\t@Test\n\tpublic void defaultCsrfTokenDeserializeNullValuesTest() throws IOException {\n\t\tString tokenJson = \"{\\\"@class\\\": \\\"org.springframework.security.web.server.csrf.DefaultCsrfToken\\\", \\\"headerName\\\": \\\"\\\", \\\"parameterName\\\": null, \\\"token\\\": \\\"1\\\"}\";\n\t\tassertThatExceptionOfType(JsonMappingException.class)\n\t\t\t.isThrownBy(() -> this.mapper.readValue(tokenJson, DefaultCsrfToken.class));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/savedrequest/CookieServerRequestCacheTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.savedrequest;\n\nimport java.net.URI;\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpCookie;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseCookie;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.util.MultiValueMap;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link CookieServerRequestCache}\n *\n * @author Eleftheria Stein\n */\npublic class CookieServerRequestCacheTests {\n\n\tprivate CookieServerRequestCache cache = new CookieServerRequestCache();\n\n\t@Test\n\tpublic void saveRequestWhenGetRequestThenRequestUriInCookie() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/secured/\").accept(MediaType.TEXT_HTML));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tMultiValueMap<String, ResponseCookie> cookies = exchange.getResponse().getCookies();\n\t\tassertThat(cookies).hasSize(1);\n\t\tResponseCookie cookie = cookies.getFirst(\"REDIRECT_URI\");\n\t\tassertThat(cookie).isNotNull();\n\t\tString encodedRedirectUrl = Base64.getEncoder().encodeToString(\"/secured/\".getBytes());\n\t\tassertThat(cookie.toString())\n\t\t\t.isEqualTo(\"REDIRECT_URI=\" + encodedRedirectUrl + \"; Path=/; HttpOnly; SameSite=Lax\");\n\t}\n\n\t@Test\n\tpublic void saveRequestWhenGetRequestWithQueryParamsThenRequestUriInCookie() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/secured/\").queryParam(\"key\", \"value\").accept(MediaType.TEXT_HTML));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tMultiValueMap<String, ResponseCookie> cookies = exchange.getResponse().getCookies();\n\t\tassertThat(cookies).hasSize(1);\n\t\tResponseCookie cookie = cookies.getFirst(\"REDIRECT_URI\");\n\t\tassertThat(cookie).isNotNull();\n\t\tString encodedRedirectUrl = Base64.getEncoder().encodeToString(\"/secured/?key=value\".getBytes());\n\t\tassertThat(cookie.toString())\n\t\t\t.isEqualTo(\"REDIRECT_URI=\" + encodedRedirectUrl + \"; Path=/; HttpOnly; SameSite=Lax\");\n\t}\n\n\t@Test\n\tpublic void saveRequestWhenGetRequestFaviconThenNoCookie() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/favicon.png\").accept(MediaType.TEXT_HTML));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tMultiValueMap<String, ResponseCookie> cookies = exchange.getResponse().getCookies();\n\t\tassertThat(cookies).isEmpty();\n\t}\n\n\t@Test\n\tpublic void saveRequestWhenPostRequestThenNoCookie() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/secured/\"));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tMultiValueMap<String, ResponseCookie> cookies = exchange.getResponse().getCookies();\n\t\tassertThat(cookies).isEmpty();\n\t}\n\n\t@Test\n\tpublic void saveRequestWhenPostRequestAndCustomMatcherThenRequestUriInCookie() {\n\t\tthis.cache.setSaveRequestMatcher((e) -> ServerWebExchangeMatcher.MatchResult.match());\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/secured/\"));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tMultiValueMap<String, ResponseCookie> cookies = exchange.getResponse().getCookies();\n\t\tResponseCookie cookie = cookies.getFirst(\"REDIRECT_URI\");\n\t\tassertThat(cookie).isNotNull();\n\t\tString encodedRedirectUrl = Base64.getEncoder().encodeToString(\"/secured/\".getBytes());\n\t\tassertThat(cookie.toString())\n\t\t\t.isEqualTo(\"REDIRECT_URI=\" + encodedRedirectUrl + \"; Path=/; HttpOnly; SameSite=Lax\");\n\t}\n\n\t@Test\n\tpublic void getRedirectUriWhenCookieThenReturnsRedirectUriFromCookie() {\n\t\tString encodedRedirectUrl = Base64.getEncoder().encodeToString(\"/secured/\".getBytes());\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/secured/\")\n\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t.cookie(new HttpCookie(\"REDIRECT_URI\", encodedRedirectUrl)));\n\t\tURI redirectUri = this.cache.getRedirectUri(exchange).block();\n\t\tassertThat(redirectUri).isEqualTo(URI.create(\"/secured/\"));\n\t}\n\n\t@Test\n\tpublic void getRedirectUriWhenCookieValueNotEncodedThenRedirectUriIsNull() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/secured/\")\n\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t.cookie(new HttpCookie(\"REDIRECT_URI\", \"/secured/\")));\n\t\tURI redirectUri = this.cache.getRedirectUri(exchange).block();\n\t\tassertThat(redirectUri).isNull();\n\t}\n\n\t@Test\n\tpublic void getRedirectUriWhenNoCookieThenRedirectUriIsNull() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/secured/\").accept(MediaType.TEXT_HTML));\n\t\tURI redirectUri = this.cache.getRedirectUri(exchange).block();\n\t\tassertThat(redirectUri).isNull();\n\t}\n\n\t@Test\n\tpublic void removeMatchingRequestThenRedirectUriCookieExpired() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/secured/\")\n\t\t\t.accept(MediaType.TEXT_HTML)\n\t\t\t.cookie(new HttpCookie(\"REDIRECT_URI\", \"/secured/\")));\n\t\tthis.cache.removeMatchingRequest(exchange).block();\n\t\tMultiValueMap<String, ResponseCookie> cookies = exchange.getResponse().getCookies();\n\t\tResponseCookie cookie = cookies.getFirst(\"REDIRECT_URI\");\n\t\tassertThat(cookie).isNotNull();\n\t\tassertThat(cookie.toString()).isEqualTo(\n\t\t\t\t\"REDIRECT_URI=; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax\");\n\t}\n\n\t@Test\n\tpublic void saveRequestWithCookieCustomizerThenSameSiteStrict() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/secured/\").accept(MediaType.TEXT_HTML));\n\t\tCookieServerRequestCache cacheWithCustomizer = new CookieServerRequestCache();\n\t\tcacheWithCustomizer.setCookieCustomizer(((cookieBuilder) -> cookieBuilder.sameSite(\"Strict\")));\n\t\tcacheWithCustomizer.saveRequest(exchange).block();\n\t\tMultiValueMap<String, ResponseCookie> cookies = exchange.getResponse().getCookies();\n\t\tassertThat(cookies).hasSize(1);\n\t\tResponseCookie cookie = cookies.getFirst(\"REDIRECT_URI\");\n\t\tassertThat(cookie).isNotNull();\n\t\tString encodedRedirectUrl = Base64.getEncoder().encodeToString(\"/secured/\".getBytes());\n\t\tassertThat(cookie.toString())\n\t\t\t.isEqualTo(\"REDIRECT_URI=\" + encodedRedirectUrl + \"; Path=/; HttpOnly; SameSite=Strict\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/savedrequest/ServerRequestCacheWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.savedrequest;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link ServerRequestCacheWebFilter}\n *\n * @author Eleftheria Stein\n */\n@ExtendWith(MockitoExtension.class)\npublic class ServerRequestCacheWebFilterTests {\n\n\tprivate ServerRequestCacheWebFilter requestCacheFilter;\n\n\t@Mock\n\tprivate WebFilterChain chain;\n\n\t@Mock\n\tprivate ServerRequestCache requestCache;\n\n\t@Captor\n\tprivate ArgumentCaptor<ServerWebExchange> exchangeCaptor;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.requestCacheFilter = new ServerRequestCacheWebFilter();\n\t\tthis.requestCacheFilter.setRequestCache(this.requestCache);\n\t\tgiven(this.chain.filter(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestMatchesThenRequestUpdated() {\n\t\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\"));\n\t\tServerHttpRequest savedRequest = MockServerHttpRequest.get(\"/\")\n\t\t\t.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML.getType())\n\t\t\t.build();\n\t\tgiven(this.requestCache.removeMatchingRequest(any())).willReturn(Mono.just(savedRequest));\n\t\tthis.requestCacheFilter.filter(exchange, this.chain).block();\n\t\tverify(this.chain).filter(this.exchangeCaptor.capture());\n\t\tServerWebExchange updatedExchange = this.exchangeCaptor.getValue();\n\t\tassertThat(updatedExchange.getRequest()).isEqualTo(savedRequest);\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestDoesNotMatchThenRequestDoesNotChange() {\n\t\tMockServerHttpRequest initialRequest = MockServerHttpRequest.get(\"/\").build();\n\t\tServerWebExchange exchange = MockServerWebExchange.from(initialRequest);\n\t\tgiven(this.requestCache.removeMatchingRequest(any())).willReturn(Mono.empty());\n\t\tthis.requestCacheFilter.filter(exchange, this.chain).block();\n\t\tverify(this.chain).filter(this.exchangeCaptor.capture());\n\t\tServerWebExchange updatedExchange = this.exchangeCaptor.getValue();\n\t\tassertThat(updatedExchange.getRequest()).isEqualTo(initialRequest);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/savedrequest/WebSessionServerRequestCacheTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.savedrequest;\n\nimport java.net.URI;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class WebSessionServerRequestCacheTests {\n\n\tprivate WebSessionServerRequestCache cache = new WebSessionServerRequestCache();\n\n\t@Test\n\tpublic void saveRequestGetRequestWhenGetThenFound() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/secured/\").accept(MediaType.TEXT_HTML));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tURI saved = this.cache.getRedirectUri(exchange).block();\n\t\tassertThat(saved).isEqualTo(exchange.getRequest().getURI());\n\t}\n\n\t@Test\n\tpublic void saveRequestGetRequestWithQueryParamsWhenGetThenFound() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/secured/\").queryParam(\"key\", \"value\").accept(MediaType.TEXT_HTML));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tURI saved = this.cache.getRedirectUri(exchange).block();\n\t\tassertThat(saved).isEqualTo(exchange.getRequest().getURI());\n\t}\n\n\t@Test\n\tpublic void saveRequestGetRequestWhenFaviconThenNotFound() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/favicon.png\").accept(MediaType.TEXT_HTML));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tURI saved = this.cache.getRedirectUri(exchange).block();\n\t\tassertThat(saved).isNull();\n\t}\n\n\t@Test\n\tpublic void saveRequestGetRequestWhenPostThenNotFound() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/secured/\"));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tassertThat(this.cache.getRedirectUri(exchange).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void saveRequestGetRequestWhenPostAndCustomMatcherThenFound() {\n\t\tthis.cache.setSaveRequestMatcher((e) -> ServerWebExchangeMatcher.MatchResult.match());\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.post(\"/secured/\"));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tURI saved = this.cache.getRedirectUri(exchange).block();\n\t\tassertThat(saved).isEqualTo(exchange.getRequest().getURI());\n\t}\n\n\t@Test\n\tpublic void saveRequestRemoveRequestWhenThenFound() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/secured/\").accept(MediaType.TEXT_HTML));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tServerHttpRequest saved = this.cache.removeMatchingRequest(exchange).block();\n\t\tassertThat(saved.getURI()).isEqualTo(exchange.getRequest().getURI());\n\t}\n\n\t@Test\n\tpublic void removeRequestGetRequestWhenDefaultThenNotFound() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/secured/\"));\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tthis.cache.removeMatchingRequest(exchange).block();\n\t\tassertThat(this.cache.getRedirectUri(exchange).block()).isNull();\n\t}\n\n\t@Test\n\tpublic void removeMatchingRequestWhenNoParameter() {\n\t\tthis.cache.setMatchingRequestParameterName(\"success\");\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/secured/\").build();\n\t\tServerWebExchange exchange = mock(ServerWebExchange.class);\n\t\tgiven(exchange.getRequest()).willReturn(request);\n\t\tassertThat(this.cache.removeMatchingRequest(exchange).block()).isNull();\n\t\tverify(exchange, never()).getSession();\n\t}\n\n\t@Test\n\tpublic void removeMatchingRequestWhenParameter() {\n\t\tthis.cache.setMatchingRequestParameterName(\"success\");\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/secured/\").accept(MediaType.TEXT_HTML).build();\n\t\tServerWebExchange exchange = MockServerWebExchange.from(request);\n\t\tthis.cache.saveRequest(exchange).block();\n\t\tString redirectUri = \"/secured/?success\";\n\t\tassertThat(this.cache.getRedirectUri(exchange).block()).isEqualTo(URI.create(redirectUri));\n\t\tMockServerHttpRequest redirectRequest = MockServerHttpRequest.get(redirectUri).build();\n\t\tServerWebExchange redirectExchange = exchange.mutate().request(redirectRequest).build();\n\t\tassertThat(this.cache.removeMatchingRequest(redirectExchange).block()).isNotNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/transport/HttpsRedirectWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.transport;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link HttpsRedirectWebFilter}\n *\n * @author Josh Cummings\n */\n@ExtendWith(MockitoExtension.class)\npublic class HttpsRedirectWebFilterTests {\n\n\tHttpsRedirectWebFilter filter;\n\n\t@Mock\n\tWebFilterChain chain;\n\n\t@BeforeEach\n\tpublic void configureFilter() {\n\t\tthis.filter = new HttpsRedirectWebFilter();\n\t}\n\n\t@Test\n\tpublic void filterWhenExchangeIsInsecureThenRedirects() {\n\t\tgiven(this.chain.filter(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tServerWebExchange exchange = get(\"http://localhost\");\n\t\tthis.filter.filter(exchange, this.chain).block();\n\t\tassertThat(statusCode(exchange)).isEqualTo(302);\n\t\tassertThat(redirectedUrl(exchange)).isEqualTo(\"https://localhost\");\n\t}\n\n\t@Test\n\tpublic void filterWhenExchangeIsSecureThenNoRedirect() {\n\t\tgiven(this.chain.filter(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tServerWebExchange exchange = get(\"https://localhost\");\n\t\tthis.filter.filter(exchange, this.chain).block();\n\t\tassertThat(exchange.getResponse().getStatusCode()).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenExchangeMismatchesThenNoRedirect() {\n\t\tgiven(this.chain.filter(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tServerWebExchangeMatcher matcher = mock(ServerWebExchangeMatcher.class);\n\t\tgiven(matcher.matches(any(ServerWebExchange.class)))\n\t\t\t.willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tthis.filter.setRequiresHttpsRedirectMatcher(matcher);\n\t\tServerWebExchange exchange = get(\"http://localhost:8080\");\n\t\tthis.filter.filter(exchange, this.chain).block();\n\t\tassertThat(exchange.getResponse().getStatusCode()).isNull();\n\t}\n\n\t@Test\n\tpublic void filterWhenExchangeMatchesAndRequestIsInsecureThenRedirects() {\n\t\tgiven(this.chain.filter(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tServerWebExchangeMatcher matcher = mock(ServerWebExchangeMatcher.class);\n\t\tgiven(matcher.matches(any(ServerWebExchange.class))).willReturn(ServerWebExchangeMatcher.MatchResult.match());\n\t\tthis.filter.setRequiresHttpsRedirectMatcher(matcher);\n\t\tServerWebExchange exchange = get(\"http://localhost:8080\");\n\t\tthis.filter.filter(exchange, this.chain).block();\n\t\tassertThat(statusCode(exchange)).isEqualTo(302);\n\t\tassertThat(redirectedUrl(exchange)).isEqualTo(\"https://localhost:8443\");\n\t\tverify(matcher).matches(any(ServerWebExchange.class));\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestIsInsecureThenPortMapperRemapsPort() {\n\t\tgiven(this.chain.filter(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tPortMapper portMapper = mock(PortMapper.class);\n\t\tgiven(portMapper.lookupHttpsPort(314)).willReturn(159);\n\t\tthis.filter.setPortMapper(portMapper);\n\t\tServerWebExchange exchange = get(\"http://localhost:314\");\n\t\tthis.filter.filter(exchange, this.chain).block();\n\t\tassertThat(statusCode(exchange)).isEqualTo(302);\n\t\tassertThat(redirectedUrl(exchange)).isEqualTo(\"https://localhost:159\");\n\t\tverify(portMapper).lookupHttpsPort(314);\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestIsInsecureAndNoPortMappingThenThrowsIllegalState() {\n\t\tgiven(this.chain.filter(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tServerWebExchange exchange = get(\"http://localhost:1234\");\n\t\tassertThatIllegalStateException().isThrownBy(() -> this.filter.filter(exchange, this.chain).block());\n\t}\n\n\t@Test\n\tpublic void filterWhenInsecureRequestHasAPathThenRedirects() {\n\t\tgiven(this.chain.filter(any(ServerWebExchange.class))).willReturn(Mono.empty());\n\t\tServerWebExchange exchange = get(\"http://localhost:8080/path/page.html?query=string\");\n\t\tthis.filter.filter(exchange, this.chain).block();\n\t\tassertThat(statusCode(exchange)).isEqualTo(302);\n\t\tassertThat(redirectedUrl(exchange)).isEqualTo(\"https://localhost:8443/path/page.html?query=string\");\n\t}\n\n\t@Test\n\tpublic void setRequiresTransportSecurityMatcherWhenSetWithNullValueThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequiresHttpsRedirectMatcher(null));\n\t}\n\n\t@Test\n\tpublic void setPortMapperWhenSetWithNullValueThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setPortMapper(null));\n\t}\n\n\tprivate String redirectedUrl(ServerWebExchange exchange) {\n\t\treturn exchange.getResponse().getHeaders().get(HttpHeaders.LOCATION).iterator().next();\n\t}\n\n\tprivate int statusCode(ServerWebExchange exchange) {\n\t\treturn exchange.getResponse().getStatusCode().value();\n\t}\n\n\tprivate ServerWebExchange get(String uri) {\n\t\treturn MockServerWebExchange.from(MockServerHttpRequest.get(uri).build());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/ui/DefaultResourcesWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.WebFilterChain;\nimport org.springframework.web.server.WebHandler;\nimport org.springframework.web.server.handler.DefaultWebFilterChain;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n */\nclass DefaultResourcesWebFilterTests {\n\n\tprivate final WebHandler notFoundHandler = (exchange) -> {\n\t\texchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);\n\t\treturn Mono.empty();\n\t};\n\n\tprivate final DefaultResourcesWebFilter filter = DefaultResourcesWebFilter.css();\n\n\t@Test\n\tvoid filterWhenPathMatchesThenRenders() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/default-ui.css\"));\n\t\tWebFilterChain filterChain = new DefaultWebFilterChain(this.notFoundHandler, List.of(this.filter));\n\n\t\tfilterChain.filter(exchange).block();\n\n\t\tassertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);\n\t\tassertThat(exchange.getResponse().getHeaders().getContentType())\n\t\t\t.isEqualTo(new MediaType(\"text\", \"css\", StandardCharsets.UTF_8));\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\"body {\");\n\t}\n\n\t@Test\n\tvoid filterWhenPathDoesNotMatchThenCallsThrough() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/does-not-match\"));\n\t\tWebFilterChain filterChain = new DefaultWebFilterChain(this.notFoundHandler, List.of(this.filter));\n\n\t\tfilterChain.filter(exchange).block();\n\n\t\tassertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);\n\t}\n\n\t@Test\n\tvoid toStringPrintsPathAndResource() {\n\t\tassertThat(this.filter.toString()).isEqualTo(\n\t\t\t\t\"DefaultResourcesWebFilter{matcher=PathMatcherServerWebExchangeMatcher{pattern='/default-ui.css', method=GET}, resource='org/springframework/security/default-ui.css'}\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class LoginPageGeneratingWebFilterTests {\n\n\t@Test\n\tpublic void filterWhenLoginWithContextPathThenActionContainsContextPath() throws Exception {\n\t\tLoginPageGeneratingWebFilter filter = new LoginPageGeneratingWebFilter();\n\t\tfilter.setFormLoginEnabled(true);\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/test/login\").contextPath(\"/test\"));\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\"action=\\\"/test/login\\\"\");\n\t}\n\n\t@Test\n\tpublic void filterWhenLoginWithNoContextPathThenActionDoesNotContainsContextPath() throws Exception {\n\t\tLoginPageGeneratingWebFilter filter = new LoginPageGeneratingWebFilter();\n\t\tfilter.setFormLoginEnabled(true);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/login\"));\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\"action=\\\"/login\\\"\");\n\t}\n\n\t@Test\n\tvoid filtersThenRendersPage() {\n\t\tString clientName = \"Google < > \\\" \\' &\";\n\t\tLoginPageGeneratingWebFilter filter = new LoginPageGeneratingWebFilter();\n\t\tfilter.setOauth2AuthenticationUrlToClientName(\n\t\t\t\tCollections.singletonMap(\"/oauth2/authorization/google\", clientName));\n\t\tfilter.setFormLoginEnabled(true);\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/test/login\").contextPath(\"/test\"));\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).isEqualTo(\"\"\"\n\t\t\t\t<!DOCTYPE html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t  <head>\n\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t    <title>Please sign in</title>\n\t\t\t\t    <link href=\"/test/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t  </head>\n\t\t\t\t  <body>\n\t\t\t\t    <div class=\"content\">\n\t\t\t\t      <form class=\"login-form\" method=\"post\" action=\"/test/login\">\n\t\t\t\t        <h2>Please sign in</h2>\n\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"username\" class=\"screenreader\">Username</label>\n\t\t\t\t          <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n\t\t\t\t        </p>\n\t\t\t\t        <p>\n\t\t\t\t          <label for=\"password\" class=\"screenreader\">Password</label>\n\t\t\t\t          <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n\t\t\t\t        </p>\n\n\t\t\t\t        <button type=\"submit\" class=\"primary\">Sign in</button>\n\t\t\t\t      </form>\n\n\t\t\t\t<h2>Login with OAuth 2.0</h2>\n\n\t\t\t\t<table class=\"table table-striped\">\n\t\t\t\t  <tr><td><a href=\"/test/oauth2/authorization/google\">Google &lt; &gt; &quot; &#39; &amp;</a></td></tr>\n\t\t\t\t</table>\n\t\t\t\t    </div>\n\t\t\t\t  </body>\n\t\t\t\t</html>\"\"\");\n\t}\n\n\t@Test\n\tpublic void filterWhenOneTimeTokenLoginThenOttForm() {\n\t\tLoginPageGeneratingWebFilter filter = new LoginPageGeneratingWebFilter();\n\t\tfilter.setOneTimeTokenEnabled(true);\n\t\tfilter.setGenerateOneTimeTokenUrl(\"/ott/authenticate\");\n\t\tfilter.setFormLoginEnabled(true);\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/login\"));\n\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\"Request a One-Time Token\");\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\"\"\"\n\t\t\t\t <form id=\"ott-form\" class=\"login-form\" method=\"post\" action=\"/ott/authenticate\">\n\t\t\t\t\"\"\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/ui/LogoutPageGeneratingWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class LogoutPageGeneratingWebFilterTests {\n\n\t@Test\n\tpublic void filterWhenLogoutWithContextPathThenActionContainsContextPath() throws Exception {\n\t\tLogoutPageGeneratingWebFilter filter = new LogoutPageGeneratingWebFilter();\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/test/logout\").contextPath(\"/test\"));\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\"action=\\\"/test/logout\\\"\");\n\t}\n\n\t@Test\n\tpublic void filterWhenLogoutWithNoContextPathThenActionDoesNotContainsContextPath() throws Exception {\n\t\tLogoutPageGeneratingWebFilter filter = new LogoutPageGeneratingWebFilter();\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/logout\"));\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\"action=\\\"/logout\\\"\");\n\t}\n\n\t@Test\n\tvoid filterThenRendersPage() {\n\t\tLogoutPageGeneratingWebFilter filter = new LogoutPageGeneratingWebFilter();\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/test/logout\").contextPath(\"/test\"));\n\t\tfilter.filter(exchange, (e) -> Mono.empty()).block();\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).isEqualTo(\"\"\"\n\t\t\t\t<!DOCTYPE html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t  <head>\n\t\t\t\t    <meta charset=\"utf-8\">\n\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t    <meta name=\"description\" content=\"\">\n\t\t\t\t    <meta name=\"author\" content=\"\">\n\t\t\t\t    <title>Confirm Log Out?</title>\n\t\t\t\t    <link href=\"/test/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t  </head>\n\t\t\t\t  <body>\n\t\t\t\t    <div class=\"content\">\n\t\t\t\t      <form class=\"logout-form\" method=\"post\" action=\"/test/logout\">\n\t\t\t\t        <h2>Are you sure you want to log out?</h2>\n\n\t\t\t\t        <button class=\"primary\" type=\"submit\">Log Out</button>\n\t\t\t\t      </form>\n\t\t\t\t    </div>\n\t\t\t\t  </body>\n\t\t\t\t</html>\"\"\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/ui/OneTimeTokenSubmitPageGeneratingWebFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.ui;\n\nimport org.junit.jupiter.api.Test;\nimport reactor.core.publisher.Mono;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link OneTimeTokenSubmitPageGeneratingWebFilter}\n *\n * @author Max Batischev\n */\npublic class OneTimeTokenSubmitPageGeneratingWebFilterTests {\n\n\tprivate final OneTimeTokenSubmitPageGeneratingWebFilter filter = new OneTimeTokenSubmitPageGeneratingWebFilter();\n\n\t@Test\n\tvoid filterWhenTokenQueryParamThenShouldIncludeJavascriptToAutoSubmitFormAndInputHasTokenValue() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/login/ott\").queryParam(\"token\", \"test\"));\n\n\t\tthis.filter.filter(exchange, (e) -> Mono.empty()).block();\n\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\n\t\t\t\t\"<input type=\\\"text\\\" id=\\\"token\\\" name=\\\"token\\\" value=\\\"test\\\" placeholder=\\\"Token\\\" required=\\\"true\\\" autofocus=\\\"autofocus\\\"/>\");\n\t}\n\n\t@Test\n\tvoid setRequestMatcherWhenNullThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestMatcher(null));\n\t}\n\n\t@Test\n\tvoid setLoginProcessingUrlWhenNullOrEmptyThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setLoginProcessingUrl(null));\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setLoginProcessingUrl(\"\"));\n\t}\n\n\t@Test\n\tvoid setLoginProcessingUrlThenUseItForFormAction() {\n\t\tthis.filter.setLoginProcessingUrl(\"/login/another\");\n\n\t\tMockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/login/ott\"));\n\n\t\tthis.filter.filter(exchange, (e) -> Mono.empty()).block();\n\n\t\tassertThat(exchange.getResponse().getBodyAsString().block())\n\t\t\t.contains(\"<form class=\\\"login-form\\\" action=\\\"/login/another\\\" method=\\\"post\\\">\");\n\t}\n\n\t@Test\n\tvoid setContextThenGenerates() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/test/login/ott\").contextPath(\"/test\"));\n\t\tthis.filter.setLoginProcessingUrl(\"/login/another\");\n\n\t\tthis.filter.filter(exchange, (e) -> Mono.empty()).block();\n\n\t\tassertThat(exchange.getResponse().getBodyAsString().block())\n\t\t\t.contains(\"<form class=\\\"login-form\\\" action=\\\"/test/login/another\\\" method=\\\"post\\\">\");\n\t}\n\n\t@Test\n\tvoid filterWhenTokenQueryParamUsesSpecialCharactersThenValueIsEscaped() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/login/ott\").queryParam(\"token\", \"this<>!@#\\\"\"));\n\n\t\tthis.filter.filter(exchange, (e) -> Mono.empty()).block();\n\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).contains(\n\t\t\t\t\"<input type=\\\"text\\\" id=\\\"token\\\" name=\\\"token\\\" value=\\\"this&lt;&gt;!@#&quot;\\\" placeholder=\\\"Token\\\" required=\\\"true\\\" autofocus=\\\"autofocus\\\"/>\");\n\t}\n\n\t@Test\n\tvoid filterThenRenders() {\n\t\tMockServerWebExchange exchange = MockServerWebExchange\n\t\t\t.from(MockServerHttpRequest.get(\"/login/ott\").queryParam(\"token\", \"this<>!@#\\\"\"));\n\t\tthis.filter.setLoginProcessingUrl(\"/login/another\");\n\n\t\tthis.filter.filter(exchange, (e) -> Mono.empty()).block();\n\n\t\tassertThat(exchange.getResponse().getBodyAsString().block()).isEqualTo(\n\t\t\t\t\"\"\"\n\t\t\t\t\t\t<!DOCTYPE html>\n\t\t\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t\t  <head>\n\t\t\t\t\t\t    <title>One-Time Token Login</title>\n\t\t\t\t\t\t    <meta charset=\"utf-8\"/>\n\t\t\t\t\t\t    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"/>\n\t\t\t\t\t\t    <link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t\t  </head>\n\t\t\t\t\t\t  <body>\n\t\t\t\t\t\t    <div class=\"container\">\n\t\t\t\t\t\t      <form class=\"login-form\" action=\"/login/another\" method=\"post\">\n\t\t\t\t\t\t        <h2>Please input the token</h2>\n\t\t\t\t\t\t        <p>\n\t\t\t\t\t\t          <label for=\"token\" class=\"screenreader\">Token</label>\n\t\t\t\t\t\t          <input type=\"text\" id=\"token\" name=\"token\" value=\"this&lt;&gt;!@#&quot;\" placeholder=\"Token\" required=\"true\" autofocus=\"autofocus\"/>\n\t\t\t\t\t\t        </p>\n\n\t\t\t\t\t\t        <button class=\"primary\" type=\"submit\">Sign in</button>\n\t\t\t\t\t\t      </form>\n\t\t\t\t\t\t    </div>\n\t\t\t\t\t\t  </body>\n\t\t\t\t\t\t</html>\n\t\t\t\t\t\t\"\"\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/util/matcher/AndServerWebExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class AndServerWebExchangeMatcherTests {\n\n\t@Mock\n\tServerWebExchange exchange;\n\n\t@Mock\n\tServerWebExchangeMatcher matcher1;\n\n\t@Mock\n\tServerWebExchangeMatcher matcher2;\n\n\tAndServerWebExchangeMatcher matcher;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.matcher = new AndServerWebExchangeMatcher(this.matcher1, this.matcher2);\n\t}\n\n\t@Test\n\tpublic void matchesWhenTrueTrueThenTrue() {\n\t\tMap<String, Object> params1 = Collections.singletonMap(\"foo\", \"bar\");\n\t\tMap<String, Object> params2 = Collections.singletonMap(\"x\", \"y\");\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.match(params1));\n\t\tgiven(this.matcher2.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.match(params2));\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isTrue();\n\t\tassertThat(matches.getVariables()).hasSize(2);\n\t\tassertThat(matches.getVariables()).containsAllEntriesOf(params1);\n\t\tassertThat(matches.getVariables()).containsAllEntriesOf(params2);\n\t\tverify(this.matcher1).matches(this.exchange);\n\t\tverify(this.matcher2).matches(this.exchange);\n\t}\n\n\t@Test\n\tpublic void matchesWhenFalseFalseThenFalseAndMatcher2NotInvoked() {\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isFalse();\n\t\tassertThat(matches.getVariables()).isEmpty();\n\t\tverify(this.matcher1).matches(this.exchange);\n\t\tverify(this.matcher2, never()).matches(this.exchange);\n\t}\n\n\t@Test\n\tpublic void matchesWhenTrueFalseThenFalse() {\n\t\tMap<String, Object> params = Collections.singletonMap(\"foo\", \"bar\");\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.match(params));\n\t\tgiven(this.matcher2.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isFalse();\n\t\tassertThat(matches.getVariables()).isEmpty();\n\t\tverify(this.matcher1).matches(this.exchange);\n\t\tverify(this.matcher2).matches(this.exchange);\n\t}\n\n\t@Test\n\tpublic void matchesWhenFalseTrueThenFalse() {\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isFalse();\n\t\tassertThat(matches.getVariables()).isEmpty();\n\t\tverify(this.matcher1).matches(this.exchange);\n\t\tverify(this.matcher2, never()).matches(this.exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/util/matcher/IpAddressServerWebExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.UnknownHostException;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link IpAddressServerWebExchangeMatcher}\n *\n * @author Guirong Hu\n */\n@ExtendWith(MockitoExtension.class)\npublic class IpAddressServerWebExchangeMatcherTests {\n\n\t@Test\n\tpublic void matchesWhenIpv6RangeAndIpv6AddressThenTrue() throws UnknownHostException {\n\t\tServerWebExchange ipv6Exchange = exchange(\"fe80::21f:5bff:fe33:bd68\");\n\t\tServerWebExchangeMatcher.MatchResult matches = new IpAddressServerWebExchangeMatcher(\"fe80::21f:5bff:fe33:bd68\")\n\t\t\t.matches(ipv6Exchange)\n\t\t\t.block();\n\t\tassertThat(matches.isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenIpv6RangeAndIpv4AddressThenFalse() throws UnknownHostException {\n\t\tServerWebExchange ipv4Exchange = exchange(\"192.168.1.104\");\n\t\tServerWebExchangeMatcher.MatchResult matches = new IpAddressServerWebExchangeMatcher(\"fe80::21f:5bff:fe33:bd68\")\n\t\t\t.matches(ipv4Exchange)\n\t\t\t.block();\n\t\tassertThat(matches.isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenIpv4RangeAndIpv4AddressThenTrue() throws UnknownHostException {\n\t\tServerWebExchange ipv4Exchange = exchange(\"192.168.1.104\");\n\t\tServerWebExchangeMatcher.MatchResult matches = new IpAddressServerWebExchangeMatcher(\"192.168.1.104\")\n\t\t\t.matches(ipv4Exchange)\n\t\t\t.block();\n\t\tassertThat(matches.isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenIpv4SubnetAndIpv4AddressThenTrue() throws UnknownHostException {\n\t\tServerWebExchange ipv4Exchange = exchange(\"192.168.1.104\");\n\t\tIpAddressServerWebExchangeMatcher matcher = new IpAddressServerWebExchangeMatcher(\"192.168.1.0/24\");\n\t\tassertThat(matcher.matches(ipv4Exchange).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenIpv4SubnetAndIpv4AddressThenFalse() throws UnknownHostException {\n\t\tServerWebExchange ipv4Exchange = exchange(\"192.168.1.104\");\n\t\tIpAddressServerWebExchangeMatcher matcher = new IpAddressServerWebExchangeMatcher(\"192.168.1.128/25\");\n\t\tassertThat(matcher.matches(ipv4Exchange).block().isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenIpv6SubnetAndIpv6AddressThenTrue() throws UnknownHostException {\n\t\tServerWebExchange ipv6Exchange = exchange(\"2001:DB8:0:FFFF:FFFF:FFFF:FFFF:FFFF\");\n\t\tIpAddressServerWebExchangeMatcher matcher = new IpAddressServerWebExchangeMatcher(\"2001:DB8::/48\");\n\t\tassertThat(matcher.matches(ipv6Exchange).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenIpv6SubnetAndIpv6AddressThenFalse() throws UnknownHostException {\n\t\tServerWebExchange ipv6Exchange = exchange(\"2001:DB8:1:0:0:0:0:0\");\n\t\tIpAddressServerWebExchangeMatcher matcher = new IpAddressServerWebExchangeMatcher(\"2001:DB8::/48\");\n\t\tassertThat(matcher.matches(ipv6Exchange).block().isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenZeroMaskAndAnythingThenTrue() throws UnknownHostException {\n\t\tIpAddressServerWebExchangeMatcher matcher = new IpAddressServerWebExchangeMatcher(\"0.0.0.0/0\");\n\t\tassertThat(matcher.matches(exchange(\"123.4.5.6\")).block().isMatch()).isTrue();\n\t\tassertThat(matcher.matches(exchange(\"192.168.0.159\")).block().isMatch()).isTrue();\n\t\tmatcher = new IpAddressServerWebExchangeMatcher(\"192.168.0.159/0\");\n\t\tassertThat(matcher.matches(exchange(\"123.4.5.6\")).block().isMatch()).isTrue();\n\t\tassertThat(matcher.matches(exchange(\"192.168.0.159\")).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenIpv4UnresolvedThenTrue() throws UnknownHostException {\n\t\tServerWebExchange ipv4Exchange = exchange(\"192.168.1.104\", true);\n\t\tServerWebExchangeMatcher.MatchResult matches = new IpAddressServerWebExchangeMatcher(\"192.168.1.104\")\n\t\t\t.matches(ipv4Exchange)\n\t\t\t.block();\n\t\tassertThat(matches.isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenIpv6UnresolvedThenTrue() throws UnknownHostException {\n\t\tServerWebExchange ipv6Exchange = exchange(\"fe80::21f:5bff:fe33:bd68\", true);\n\t\tServerWebExchangeMatcher.MatchResult matches = new IpAddressServerWebExchangeMatcher(\"fe80::21f:5bff:fe33:bd68\")\n\t\t\t.matches(ipv6Exchange)\n\t\t\t.block();\n\t\tassertThat(matches.isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void constructorWhenIpv4AddressMaskTooLongThenIllegalArgumentException() {\n\t\tString ipv4AddressWithTooLongMask = \"192.168.1.104/33\";\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new IpAddressServerWebExchangeMatcher(ipv4AddressWithTooLongMask))\n\t\t\t.withMessage(String.format(\"IP address %s is too short for bitmask of length %d\", \"192.168.1.104\", 33));\n\t}\n\n\t@Test\n\tpublic void constructorWhenIpv6AddressMaskTooLongThenIllegalArgumentException() {\n\t\tString ipv6AddressWithTooLongMask = \"fe80::21f:5bff:fe33:bd68/129\";\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new IpAddressServerWebExchangeMatcher(ipv6AddressWithTooLongMask))\n\t\t\t.withMessage(String.format(\"IP address %s is too short for bitmask of length %d\",\n\t\t\t\t\t\"fe80::21f:5bff:fe33:bd68\", 129));\n\t}\n\n\tprivate static ServerWebExchange exchange(String ipAddress) throws UnknownHostException {\n\t\treturn exchange(ipAddress, false);\n\t}\n\n\tprivate static ServerWebExchange exchange(String ipAddress, boolean unresolved) throws UnknownHostException {\n\t\treturn MockServerWebExchange\n\t\t\t.builder(MockServerHttpRequest.get(\"/\")\n\t\t\t\t.remoteAddress(unresolved ? InetSocketAddress.createUnresolved(ipAddress, 8080)\n\t\t\t\t\t\t: new InetSocketAddress(InetAddress.getByName(ipAddress), 8080)))\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/util/matcher/MediaTypeServerWebExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class MediaTypeServerWebExchangeMatcherTests {\n\n\tprivate MediaTypeServerWebExchangeMatcher matcher;\n\n\t@Test\n\tpublic void constructorMediaTypeArrayWhenNullThenThrowsIllegalArgumentException() {\n\t\tMediaType[] types = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MediaTypeServerWebExchangeMatcher(types));\n\t}\n\n\t@Test\n\tpublic void constructorListOfDoesNotThrowNullPointerException() {\n\t\tnew MediaTypeServerWebExchangeMatcher(List.of(MediaType.ALL));\n\t}\n\n\t@Test\n\tpublic void constructorMediaTypeArrayWhenContainsNullThenThrowsIllegalArgumentException() {\n\t\tMediaType[] types = { null };\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MediaTypeServerWebExchangeMatcher(types));\n\t}\n\n\t@Test\n\tpublic void constructorMediaTypeListWhenNullThenThrowsIllegalArgumentException() {\n\t\tList<MediaType> types = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MediaTypeServerWebExchangeMatcher(types));\n\t}\n\n\t@Test\n\tpublic void constructorMediaTypeListWhenContainsNullThenThrowsIllegalArgumentException() {\n\t\tList<MediaType> types = Collections.singletonList(null);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MediaTypeServerWebExchangeMatcher(types));\n\t}\n\n\t@Test\n\tpublic void matchWhenDefaultResolverAndAcceptEqualThenMatch() {\n\t\tMediaType acceptType = MediaType.TEXT_HTML;\n\t\tMediaTypeServerWebExchangeMatcher matcher = new MediaTypeServerWebExchangeMatcher(acceptType);\n\t\tassertThat(matcher.matches(exchange(acceptType)).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenDefaultResolverAndAcceptEqualAndIgnoreThenMatch() {\n\t\tMediaType acceptType = MediaType.TEXT_HTML;\n\t\tMediaTypeServerWebExchangeMatcher matcher = new MediaTypeServerWebExchangeMatcher(acceptType);\n\t\tmatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(matcher.matches(exchange(acceptType)).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenDefaultResolverAndAcceptEqualAndIgnoreThenNotMatch() {\n\t\tMediaType acceptType = MediaType.TEXT_HTML;\n\t\tMediaTypeServerWebExchangeMatcher matcher = new MediaTypeServerWebExchangeMatcher(acceptType);\n\t\tmatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(matcher.matches(exchange(MediaType.ALL)).block().isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchWhenDefaultResolverAndAcceptImpliedThenMatch() {\n\t\tMediaTypeServerWebExchangeMatcher matcher = new MediaTypeServerWebExchangeMatcher(\n\t\t\t\tMediaType.parseMediaTypes(\"text/*\"));\n\t\tassertThat(matcher.matches(exchange(MediaType.TEXT_HTML)).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenDefaultResolverAndAcceptImpliedAndUseEqualsThenNotMatch() {\n\t\tMediaTypeServerWebExchangeMatcher matcher = new MediaTypeServerWebExchangeMatcher(MediaType.ALL);\n\t\tmatcher.setUseEquals(true);\n\t\tassertThat(matcher.matches(exchange(MediaType.TEXT_HTML)).block().isMatch()).isFalse();\n\t}\n\n\tprivate static ServerWebExchange exchange(MediaType... accept) {\n\t\treturn MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").accept(accept).build());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/util/matcher/NegatedServerWebExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Tao Qian\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class NegatedServerWebExchangeMatcherTests {\n\n\t@Mock\n\tServerWebExchange exchange;\n\n\t@Mock\n\tServerWebExchangeMatcher matcher1;\n\n\tNegatedServerWebExchangeMatcher matcher;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.matcher = new NegatedServerWebExchangeMatcher(this.matcher1);\n\t}\n\n\t@Test\n\tpublic void matchesWhenFalseThenTrue() {\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isTrue();\n\t\tassertThat(matches.getVariables()).isEmpty();\n\t\tverify(this.matcher1).matches(this.exchange);\n\t}\n\n\t@Test\n\tpublic void matchesWhenTrueThenFalse() {\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.match());\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isFalse();\n\t\tassertThat(matches.getVariables()).isEmpty();\n\t\tverify(this.matcher1).matches(this.exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/util/matcher/OrServerWebExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.Collections;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class OrServerWebExchangeMatcherTests {\n\n\t@Mock\n\tServerWebExchange exchange;\n\n\t@Mock\n\tServerWebExchangeMatcher matcher1;\n\n\t@Mock\n\tServerWebExchangeMatcher matcher2;\n\n\tOrServerWebExchangeMatcher matcher;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.matcher = new OrServerWebExchangeMatcher(this.matcher1, this.matcher2);\n\t}\n\n\t@Test\n\tpublic void matchesWhenFalseFalseThenFalse() {\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tgiven(this.matcher2.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isFalse();\n\t\tassertThat(matches.getVariables()).isEmpty();\n\t\tverify(this.matcher1).matches(this.exchange);\n\t\tverify(this.matcher2).matches(this.exchange);\n\t}\n\n\t@Test\n\tpublic void matchesWhenTrueFalseThenTrueAndMatcher2NotInvoked() {\n\t\tMap<String, Object> params = Collections.singletonMap(\"foo\", \"bar\");\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.match(params));\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isTrue();\n\t\tassertThat(matches.getVariables()).isEqualTo(params);\n\t\tverify(this.matcher1).matches(this.exchange);\n\t\tverify(this.matcher2, never()).matches(this.exchange);\n\t}\n\n\t@Test\n\tpublic void matchesWhenFalseTrueThenTrue() {\n\t\tMap<String, Object> params = Collections.singletonMap(\"foo\", \"bar\");\n\t\tgiven(this.matcher1.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.notMatch());\n\t\tgiven(this.matcher2.matches(this.exchange)).willReturn(ServerWebExchangeMatcher.MatchResult.match(params));\n\t\tServerWebExchangeMatcher.MatchResult matches = this.matcher.matches(this.exchange).block();\n\t\tassertThat(matches.isMatch()).isTrue();\n\t\tassertThat(matches.getVariables()).isEqualTo(params);\n\t\tverify(this.matcher1).matches(this.exchange);\n\t\tverify(this.matcher2).matches(this.exchange);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport java.util.HashMap;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.http.server.reactive.MockServerHttpResponse;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.session.DefaultWebSessionManager;\nimport org.springframework.web.util.pattern.PathPattern;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\n@ExtendWith(MockitoExtension.class)\npublic class PathMatcherServerWebExchangeMatcherTests {\n\n\t@Mock\n\tPathPattern pattern;\n\n\t@Mock\n\tPathPattern.PathMatchInfo pathMatchInfo;\n\n\tMockServerWebExchange exchange;\n\n\tPathPatternParserServerWebExchangeMatcher matcher;\n\n\tString path;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tMockServerHttpRequest request = MockServerHttpRequest.post(\"/path\").build();\n\t\tMockServerHttpResponse response = new MockServerHttpResponse();\n\t\tDefaultWebSessionManager sessionManager = new DefaultWebSessionManager();\n\t\tthis.exchange = MockServerWebExchange.from(request);\n\t\tthis.path = \"/path\";\n\t\tthis.matcher = new PathPatternParserServerWebExchangeMatcher(this.pattern);\n\t}\n\n\t@Test\n\tpublic void constructorPatternWhenPatternNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new PathPatternParserServerWebExchangeMatcher((PathPattern) null));\n\t}\n\n\t@Test\n\tpublic void constructorPatternAndMethodWhenPatternNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new PathPatternParserServerWebExchangeMatcher((PathPattern) null, HttpMethod.GET));\n\t}\n\n\t@Test\n\tpublic void matchesWhenPathMatcherTrueThenReturnTrue() {\n\t\tgiven(this.pattern.matchAndExtract(any())).willReturn(this.pathMatchInfo);\n\t\tgiven(this.pathMatchInfo.getUriVariables()).willReturn(new HashMap<>());\n\t\tassertThat(this.matcher.matches(this.exchange).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenPathMatcherFalseThenReturnFalse() {\n\t\tgiven(this.pattern.matchAndExtract(any())).willReturn(null);\n\t\tassertThat(this.matcher.matches(this.exchange).block().isMatch()).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenPathMatcherTrueAndMethodTrueThenReturnTrue() {\n\t\tthis.matcher = new PathPatternParserServerWebExchangeMatcher(this.pattern,\n\t\t\t\tthis.exchange.getRequest().getMethod());\n\t\tgiven(this.pattern.matchAndExtract(any())).willReturn(this.pathMatchInfo);\n\t\tgiven(this.pathMatchInfo.getUriVariables()).willReturn(new HashMap<>());\n\t\tassertThat(this.matcher.matches(this.exchange).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenPathMatcherTrueAndMethodFalseThenReturnFalse() {\n\t\tHttpMethod method = HttpMethod.OPTIONS;\n\t\tassertThat(this.exchange.getRequest().getMethod()).isNotEqualTo(method);\n\t\tthis.matcher = new PathPatternParserServerWebExchangeMatcher(this.pattern, method);\n\t\tassertThat(this.matcher.matches(this.exchange).block().isMatch()).isFalse();\n\t\tverifyNoMoreInteractions(this.pattern);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/util/matcher/PathPatternParserServerWebExchangeMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link PathPatternParserServerWebExchangeMatcher}\n *\n * @author Marcus da Coregio\n */\nclass PathPatternParserServerWebExchangeMatcherTests {\n\n\t@Test\n\tvoid matchesWhenConfiguredWithNoTrailingSlashAndPathContainsSlashThenMatches() {\n\t\tPathPatternParserServerWebExchangeMatcher matcher = new PathPatternParserServerWebExchangeMatcher(\"user/**\");\n\t\tMockServerHttpRequest request = MockServerHttpRequest.get(\"/user/test\").build();\n\t\tassertThat(matcher.matches(MockServerWebExchange.from(request)).block().isMatch()).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.server.util.matcher;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.http.server.reactive.MockServerHttpRequest;\nimport org.springframework.mock.web.server.MockServerWebExchange;\nimport org.springframework.web.server.ServerWebExchange;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * @author Rob Winch\n * @since 5.0\n */\npublic class ServerWebExchangeMatchersTests {\n\n\tServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(\"/\").build());\n\n\t@Test\n\tpublic void pathMatchersWhenSingleAndSamePatternThenMatches() {\n\t\tassertThat(ServerWebExchangeMatchers.pathMatchers(\"/\").matches(this.exchange).block().isMatch()).isTrue();\n\t}\n\n\t@Test\n\tpublic void pathMatchersWhenSingleAndSamePatternAndMethodThenMatches() {\n\t\tassertThat(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, \"/\").matches(this.exchange).block().isMatch())\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void pathMatchersWhenSingleAndSamePatternAndDiffMethodThenDoesNotMatch() {\n\t\tassertThat(\n\t\t\t\tServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, \"/\").matches(this.exchange).block().isMatch())\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void pathMatchersWhenSingleAndDifferentPatternThenDoesNotMatch() {\n\t\tassertThat(ServerWebExchangeMatchers.pathMatchers(\"/foobar\").matches(this.exchange).block().isMatch())\n\t\t\t.isFalse();\n\t}\n\n\t@Test\n\tpublic void pathMatchersWhenMultiThenMatches() {\n\t\tassertThat(ServerWebExchangeMatchers.pathMatchers(\"/foobar\", \"/\").matches(this.exchange).block().isMatch())\n\t\t\t.isTrue();\n\t}\n\n\t@Test\n\tpublic void anyExchangeWhenMockThenMatches() {\n\t\tServerWebExchange mockExchange = mock(ServerWebExchange.class);\n\t\tassertThat(ServerWebExchangeMatchers.anyExchange().matches(mockExchange).block().isMatch()).isTrue();\n\t\tverifyNoMoreInteractions(mockExchange);\n\t}\n\n\t/**\n\t * If a LinkedMap is used and anyRequest equals anyRequest then the following is\n\t * added: anyRequest() -> authenticated() pathMatchers(\"/admin/**\") ->\n\t * hasRole(\"ADMIN\") anyRequest() -> permitAll\n\t *\n\t * will result in the first entry being overridden\n\t */\n\t@Test\n\tpublic void anyExchangeWhenTwoCreatedThenDifferentToPreventIssuesInMap() {\n\t\tassertThat(ServerWebExchangeMatchers.anyExchange()).isNotEqualTo(ServerWebExchangeMatchers.anyExchange());\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/servlet/MockServletContext.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servlet;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport jakarta.servlet.MultipartConfigElement;\nimport jakarta.servlet.Servlet;\nimport jakarta.servlet.ServletRegistration;\nimport jakarta.servlet.ServletSecurityElement;\n\nimport org.springframework.web.servlet.DispatcherServlet;\n\npublic class MockServletContext extends org.springframework.mock.web.MockServletContext {\n\n\tprivate final Map<String, ServletRegistration> registrations = new LinkedHashMap<>();\n\n\tpublic static MockServletContext mvc() {\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tservletContext.addServlet(\"dispatcherServlet\", DispatcherServlet.class).addMapping(\"/\");\n\t\treturn servletContext;\n\t}\n\n\t@Override\n\tpublic ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> clazz) {\n\t\tServletRegistration.Dynamic dynamic = new MockServletRegistration(servletName, clazz);\n\t\tthis.registrations.put(servletName, dynamic);\n\t\treturn dynamic;\n\t}\n\n\t@Override\n\tpublic Map<String, ? extends ServletRegistration> getServletRegistrations() {\n\t\treturn this.registrations;\n\t}\n\n\t@Override\n\tpublic ServletRegistration getServletRegistration(String servletName) {\n\t\treturn this.registrations.get(servletName);\n\t}\n\n\tprivate static class MockServletRegistration implements ServletRegistration.Dynamic {\n\n\t\tprivate final String name;\n\n\t\tprivate final Class<?> clazz;\n\n\t\tprivate final Set<String> mappings = new LinkedHashSet<>();\n\n\t\tMockServletRegistration(String name, Class<?> clazz) {\n\t\t\tthis.name = name;\n\t\t\tthis.clazz = clazz;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setLoadOnStartup(int loadOnStartup) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<String> setServletSecurity(ServletSecurityElement constraint) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setMultipartConfig(MultipartConfigElement multipartConfig) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void setRunAsRole(String roleName) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAsyncSupported(boolean isAsyncSupported) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<String> addMapping(String... urlPatterns) {\n\t\t\tthis.mappings.addAll(Arrays.asList(urlPatterns));\n\t\t\treturn this.mappings;\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<String> getMappings() {\n\t\t\treturn this.mappings;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getRunAsRole() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getClassName() {\n\t\t\treturn this.clazz.getName();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean setInitParameter(String name, String value) {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getInitParameter(String name) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<String> setInitParameters(Map<String, String> initParameters) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> getInitParameters() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/servlet/TestMockHttpServletMappings.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servlet;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.MappingMatch;\n\nimport org.springframework.mock.web.MockHttpServletMapping;\n\npublic final class TestMockHttpServletMappings {\n\n\tprivate TestMockHttpServletMappings() {\n\n\t}\n\n\tpublic static MockHttpServletMapping extension(HttpServletRequest request, String extension) {\n\t\tString uri = request.getRequestURI();\n\t\tString matchValue = uri.substring(0, uri.lastIndexOf(extension));\n\t\treturn new MockHttpServletMapping(matchValue, \"*\" + extension, \"extension\", MappingMatch.EXTENSION);\n\t}\n\n\tpublic static MockHttpServletMapping path(HttpServletRequest request, String path) {\n\t\tString uri = request.getRequestURI();\n\t\tString matchValue = uri.substring(path.length());\n\t\treturn new MockHttpServletMapping(matchValue, path + \"/*\", \"path\", MappingMatch.PATH);\n\t}\n\n\tpublic static MockHttpServletMapping defaultMapping() {\n\t\treturn new MockHttpServletMapping(\"\", \"/\", \"default\", MappingMatch.DEFAULT);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/servlet/TestMockHttpServletRequests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servlet;\n\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.UriComponentsBuilder;\n\npublic final class TestMockHttpServletRequests {\n\n\tprivate TestMockHttpServletRequests() {\n\n\t}\n\n\tpublic static Builder get() {\n\t\treturn new Builder(HttpMethod.GET);\n\t}\n\n\tpublic static Builder get(String url) {\n\t\treturn get().applyUrl(url);\n\t}\n\n\tpublic static Builder post() {\n\t\treturn new Builder(HttpMethod.POST);\n\t}\n\n\tpublic static Builder post(String url) {\n\t\treturn post().applyUrl(url);\n\t}\n\n\tpublic static Builder request(String method) {\n\t\treturn new Builder(HttpMethod.valueOf(method));\n\t}\n\n\tpublic static final class Builder {\n\n\t\tprivate static final Pattern URL = Pattern.compile(\"((?<scheme>https?)://)?\"\n\t\t\t\t+ \"((?<hostname>[^:/]+)(:(?<port>\\\\d+))?)?\" + \"(?<path>[^?]+)?\" + \"(\\\\?(?<query>.*))?\");\n\n\t\tprivate final HttpMethod method;\n\n\t\tprivate String requestUri;\n\n\t\tprivate final Map<String, String> parameters = new LinkedHashMap<>();\n\n\t\tprivate String scheme = MockHttpServletRequest.DEFAULT_SCHEME;\n\n\t\tprivate int port = MockHttpServletRequest.DEFAULT_SERVER_PORT;\n\n\t\tprivate String hostname = MockHttpServletRequest.DEFAULT_SERVER_NAME;\n\n\t\tprivate String contextPath;\n\n\t\tprivate String servletPath;\n\n\t\tprivate String pathInfo;\n\n\t\tprivate String queryString;\n\n\t\tprivate Builder(HttpMethod method) {\n\t\t\tthis.method = method;\n\t\t}\n\n\t\tprivate Builder applyUrl(String url) {\n\t\t\tMatcher matcher = URL.matcher(url);\n\t\t\tif (matcher.matches()) {\n\t\t\t\tapplyElement(this::scheme, matcher.group(\"scheme\"));\n\t\t\t\tapplyElement(this::port, matcher.group(\"port\"));\n\t\t\t\tapplyElement(this::serverName, matcher.group(\"hostname\"));\n\t\t\t\tapplyElement(this::requestUri, matcher.group(\"path\"));\n\t\t\t\tapplyElement(this::queryString, matcher.group(\"query\"));\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate <T> void applyElement(Consumer<T> apply, T value) {\n\t\t\tif (value != null) {\n\t\t\t\tapply.accept(value);\n\t\t\t}\n\t\t}\n\n\t\tpublic Builder requestUri(String contextPath, String servletPath, String pathInfo) {\n\t\t\tthis.contextPath = contextPath;\n\t\t\tthis.servletPath = servletPath;\n\t\t\tthis.pathInfo = pathInfo;\n\t\t\tthis.requestUri = Stream.of(contextPath, servletPath, pathInfo)\n\t\t\t\t.filter(StringUtils::hasText)\n\t\t\t\t.collect(Collectors.joining());\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder requestUri(String requestUri) {\n\t\t\treturn requestUri(null, requestUri, null);\n\t\t}\n\n\t\tpublic Builder param(String name, String value) {\n\t\t\tthis.parameters.put(name, value);\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate Builder port(String port) {\n\t\t\tif (port != null) {\n\t\t\t\tthis.port = Integer.parseInt(port);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder port(int port) {\n\t\t\tthis.port = port;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder queryString(String queryString) {\n\t\t\tthis.queryString = queryString;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder scheme(String scheme) {\n\t\t\tthis.scheme = scheme;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder serverName(String serverName) {\n\t\t\tthis.hostname = serverName;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic MockHttpServletRequest build() {\n\t\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\t\tMap<String, List<String>> params = UriComponentsBuilder.fromUriString(\"?\" + this.queryString)\n\t\t\t\t.build()\n\t\t\t\t.getQueryParams();\n\t\t\tfor (Map.Entry<String, List<String>> entry : params.entrySet()) {\n\t\t\t\tfor (String value : entry.getValue()) {\n\t\t\t\t\trequest.addParameter(entry.getKey(), value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tapplyElement(request::setContextPath, this.contextPath);\n\t\t\tapplyElement(request::setContextPath, this.contextPath);\n\t\t\tapplyElement(request::setMethod, this.method.name());\n\t\t\tapplyElement(request::setParameters, this.parameters);\n\t\t\tapplyElement(request::setPathInfo, this.pathInfo);\n\t\t\tapplyElement(request::setServletPath, this.servletPath);\n\t\t\tapplyElement(request::setScheme, this.scheme);\n\t\t\tapplyElement(request::setServerPort, this.port);\n\t\t\tapplyElement(request::setServerName, this.hostname);\n\t\t\tapplyElement(request::setQueryString, this.queryString);\n\t\t\tapplyElement(request::setRequestURI, this.requestUri);\n\t\t\trequest.setSecure(\"https\".equals(this.scheme));\n\t\t\treturn request;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/servlet/support/csrf/CsrfRequestDataValueProcessorTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servlet.support.csrf;\n\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.util.ReflectionUtils;\nimport org.springframework.web.servlet.support.RequestDataValueProcessor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n *\n */\npublic class CsrfRequestDataValueProcessorTests {\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate CsrfRequestDataValueProcessor processor;\n\n\tprivate CsrfToken token;\n\n\tprivate Map<String, String> expected = new HashMap<>();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.processor = new CsrfRequestDataValueProcessor();\n\t\tthis.token = new DefaultCsrfToken(\"1\", \"a\", \"b\");\n\t\tthis.request.setAttribute(CsrfToken.class.getName(), this.token);\n\t\tthis.expected.put(this.token.getParameterName(), this.token.getToken());\n\t}\n\n\t@Test\n\tpublic void assertAllMethodsDeclared() {\n\t\tMethod[] expectedMethods = ReflectionUtils.getAllDeclaredMethods(RequestDataValueProcessor.class);\n\t\tfor (Method expected : expectedMethods) {\n\t\t\tassertThat(ReflectionUtils.findMethod(CsrfRequestDataValueProcessor.class, expected.getName(),\n\t\t\t\t\texpected.getParameterTypes()))\n\t\t\t\t.as(\"Expected to find \" + expected + \" defined on \" + CsrfRequestDataValueProcessor.class)\n\t\t\t\t.isNotNull();\n\t\t}\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsNoCsrfToken() {\n\t\tthis.request = new MockHttpServletRequest();\n\t\tassertThat(this.processor.getExtraHiddenFields(this.request)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfTokenNoMethodSet() {\n\t\tassertThat(this.processor.getExtraHiddenFields(this.request)).isEqualTo(this.expected);\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfToken_GET() {\n\t\tthis.processor.processAction(this.request, \"action\", \"GET\");\n\t\tassertThat(this.processor.getExtraHiddenFields(this.request)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfToken_get() {\n\t\tthis.processor.processAction(this.request, \"action\", \"get\");\n\t\tassertThat(this.processor.getExtraHiddenFields(this.request)).isEmpty();\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfToken_POST() {\n\t\tthis.processor.processAction(this.request, \"action\", \"POST\");\n\t\tassertThat(this.processor.getExtraHiddenFields(this.request)).isEqualTo(this.expected);\n\t}\n\n\t@Test\n\tpublic void getExtraHiddenFieldsHasCsrfToken_post() {\n\t\tthis.processor.processAction(this.request, \"action\", \"post\");\n\t\tassertThat(this.processor.getExtraHiddenFields(this.request)).isEqualTo(this.expected);\n\t}\n\n\t@Test\n\tpublic void processAction() {\n\t\tString action = \"action\";\n\t\tassertThat(this.processor.processAction(this.request, action)).isEqualTo(action);\n\t}\n\n\t@Test\n\tpublic void processActionWithMethodArg() {\n\t\tString action = \"action\";\n\t\tassertThat(this.processor.processAction(this.request, action, null)).isEqualTo(action);\n\t}\n\n\t@Test\n\tpublic void processFormFieldValue() {\n\t\tString value = \"action\";\n\t\tassertThat(this.processor.processFormFieldValue(this.request, \"name\", value, \"hidden\")).isEqualTo(value);\n\t}\n\n\t@Test\n\tpublic void processUrl() {\n\t\tString url = \"url\";\n\t\tassertThat(this.processor.processUrl(this.request, url)).isEqualTo(url);\n\t}\n\n\t@Test\n\tpublic void createGetExtraHiddenFieldsHasCsrfToken() {\n\t\tCsrfToken token = new DefaultCsrfToken(\"1\", \"a\", \"b\");\n\t\tthis.request.setAttribute(CsrfToken.class.getName(), token);\n\t\tMap<String, String> expected = new HashMap<>();\n\t\texpected.put(token.getParameterName(), token.getToken());\n\t\tRequestDataValueProcessor processor = new CsrfRequestDataValueProcessor();\n\t\tassertThat(processor.getExtraHiddenFields(this.request)).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/servlet/util/matcher/PathPatternRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servlet.util.matcher;\n\nimport jakarta.servlet.Servlet;\nimport jakarta.servlet.ServletContext;\nimport jakarta.servlet.ServletRegistration;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.web.servlet.MockServletContext;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.web.util.ServletRequestPathUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n/**\n * Tests for {@link PathPatternRequestMatcher}\n */\npublic class PathPatternRequestMatcherTests {\n\n\t@Test\n\tvoid matcherWhenPatternMatchesRequestThenMatchResult() {\n\t\tRequestMatcher matcher = pathPattern(\"/uri\");\n\t\tassertThat(matcher.matches(request(\"/uri\"))).isTrue();\n\t}\n\n\t@Test\n\tvoid matcherWhenPatternContainsPlaceholdersThenMatchResult() {\n\t\tRequestMatcher matcher = pathPattern(\"/uri/{username}\");\n\t\tassertThat(matcher.matcher(request(\"/uri/bob\")).getVariables()).containsEntry(\"username\", \"bob\");\n\t}\n\n\t@Test\n\tvoid matcherWhenOnlyPathInfoMatchesThenNoMatch() {\n\t\tRequestMatcher matcher = pathPattern(\"/uri\");\n\t\tassertThat(matcher.matches(request(\"GET\", \"/mvc/uri\", \"/mvc\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matcherWhenUriContainsServletPathThenMatch() {\n\t\tRequestMatcher matcher = pathPattern(\"/mvc/uri\");\n\t\tassertThat(matcher.matches(request(\"GET\", \"/mvc/uri\", \"/mvc\"))).isTrue();\n\t}\n\n\t@Test\n\tvoid matcherWhenSameMethodThenMatchResult() {\n\t\tRequestMatcher matcher = pathPattern(HttpMethod.GET, \"/uri\");\n\t\tassertThat(matcher.matches(request(\"/uri\"))).isTrue();\n\t}\n\n\t@Test\n\tvoid matcherWhenDifferentPathThenNoMatch() {\n\t\tRequestMatcher matcher = pathPattern(HttpMethod.GET, \"/uri\");\n\t\tassertThat(matcher.matches(request(\"GET\", \"/urj\", \"\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matcherWhenDifferentMethodThenNoMatch() {\n\t\tRequestMatcher matcher = pathPattern(HttpMethod.GET, \"/uri\");\n\t\tassertThat(matcher.matches(request(\"POST\", \"/mvc/uri\", \"/mvc\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matcherWhenNoMethodThenMatches() {\n\t\tRequestMatcher matcher = pathPattern(\"/uri\");\n\t\tassertThat(matcher.matches(request(\"POST\", \"/uri\", \"\"))).isTrue();\n\t\tassertThat(matcher.matches(request(\"GET\", \"/uri\", \"\"))).isTrue();\n\t}\n\n\t@Test\n\tvoid matcherWhenServletPathThenMatchesOnlyServletPath() {\n\t\tPathPatternRequestMatcher.Builder servlet = PathPatternRequestMatcher.withDefaults().basePath(\"/servlet/path\");\n\t\tRequestMatcher matcher = servlet.matcher(HttpMethod.GET, \"/endpoint\");\n\t\tServletContext servletContext = servletContext(\"/servlet/path\");\n\t\tMockHttpServletRequest mock = get(\"/servlet/path/endpoint\").servletPath(\"/servlet/path\")\n\t\t\t.buildRequest(servletContext);\n\t\tServletRequestPathUtils.parseAndCache(mock);\n\t\tassertThat(matcher.matches(mock)).isTrue();\n\t\tmock = get(\"/endpoint\").servletPath(\"/endpoint\").buildRequest(servletContext);\n\t\tServletRequestPathUtils.parseAndCache(mock);\n\t\tassertThat(matcher.matches(mock)).isFalse();\n\t}\n\n\t@Test\n\tvoid matcherWhenRequestPathThenRequiresServletPath() {\n\t\tPathPatternRequestMatcher.Builder request = PathPatternRequestMatcher.withDefaults();\n\t\tRequestMatcher matcher = request.matcher(HttpMethod.GET, \"/endpoint\");\n\t\tMockHttpServletRequest mock = get(\"/servlet/path/endpoint\").servletPath(\"/servlet/path\").buildRequest(null);\n\t\tServletRequestPathUtils.parseAndCache(mock);\n\t\tassertThat(matcher.matches(mock)).isFalse();\n\t\tmock = get(\"/endpoint\").servletPath(\"/endpoint\").buildRequest(null);\n\t\tServletRequestPathUtils.parseAndCache(mock);\n\t\tassertThat(matcher.matches(mock)).isTrue();\n\t}\n\n\t@Test\n\tvoid matcherWhenMultiServletPathThenMatches() {\n\t\tPathPatternRequestMatcher.Builder servlet = PathPatternRequestMatcher.withDefaults().basePath(\"/servlet/path\");\n\t\tRequestMatcher matcher = servlet.matcher(HttpMethod.GET, \"/endpoint\");\n\t\tMockHttpServletRequest mock = get(\"/servlet/path/endpoint\").servletPath(\"/servlet/path\").buildRequest(null);\n\t\tassertThat(matcher.matches(mock)).isTrue();\n\t}\n\n\t@Test\n\tvoid matcherWhenMultiContextPathThenMatches() {\n\t\tPathPatternRequestMatcher.Builder servlet = PathPatternRequestMatcher.withDefaults().basePath(\"/servlet/path\");\n\t\tRequestMatcher matcher = servlet.matcher(HttpMethod.GET, \"/endpoint\");\n\t\tassertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> matcher.matches(\n\t\t\t\tget(\"/servlet/path/endpoint\").servletPath(\"/servlet/path\").contextPath(\"/app\").buildRequest(null)));\n\t}\n\n\t@Test\n\tvoid servletPathWhenEndsWithSlashOrStarThenIllegalArgument() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> PathPatternRequestMatcher.withDefaults().basePath(\"/path/**\"));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> PathPatternRequestMatcher.withDefaults().basePath(\"/path/*\"));\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> PathPatternRequestMatcher.withDefaults().basePath(\"/path/\"));\n\t}\n\n\t@Test\n\tvoid matcherWhenBasePathIsRootThenNoDoubleSlash() {\n\t\tPathPatternRequestMatcher.Builder builder = PathPatternRequestMatcher.withDefaults().basePath(\"/\");\n\t\tRequestMatcher matcher = builder.matcher(HttpMethod.GET, \"/path\");\n\t\tMockHttpServletRequest mock = get(\"/path\").servletPath(\"/path\").buildRequest(null);\n\t\tassertThat(matcher.matches(mock)).isTrue();\n\t}\n\n\t@Test\n\tvoid matcherWhenRequestMethodIsNullThenNoNullPointerException() {\n\t\tRequestMatcher matcher = pathPattern(HttpMethod.GET, \"/\");\n\t\tMockHttpServletRequest mock = new MockHttpServletRequest(null, \"/\");\n\t\tServletRequestPathUtils.parseAndCache(mock);\n\t\tassertThat(matcher.matches(mock)).isFalse();\n\t}\n\n\t@Test\n\tvoid matcherWhenRequestPathNotParsedThenDoesNotLeaveParsedRequestPath() {\n\t\tRequestMatcher matcher = pathPattern(\"/uri\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/uri\");\n\t\tassertThat(ServletRequestPathUtils.hasParsedRequestPath(request)).isFalse();\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t\tassertThat(ServletRequestPathUtils.hasParsedRequestPath(request)).isFalse();\n\t}\n\n\t@Test\n\tvoid matcherWhenRequestPathAlreadyParsedThenLeavesParsedRequestPath() {\n\t\tRequestMatcher matcher = pathPattern(\"/uri\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/uri\");\n\t\tServletRequestPathUtils.parseAndCache(request);\n\t\tassertThat(ServletRequestPathUtils.hasParsedRequestPath(request)).isTrue();\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t\tassertThat(ServletRequestPathUtils.hasParsedRequestPath(request)).isTrue();\n\t}\n\n\tMockHttpServletRequest request(String uri) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", uri);\n\t\tServletRequestPathUtils.parseAndCache(request);\n\t\treturn request;\n\t}\n\n\tMockHttpServletRequest request(String method, String uri, String servletPath) {\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(method, uri);\n\t\trequest.setServletPath(servletPath);\n\t\tServletRequestPathUtils.parseAndCache(request);\n\t\treturn request;\n\t}\n\n\tMockServletContext servletContext(String... servletPath) {\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tServletRegistration.Dynamic registration = servletContext.addServlet(\"servlet\", Servlet.class);\n\t\tfor (String s : servletPath) {\n\t\t\tregistration.addMapping(s + \"/*\");\n\t\t}\n\t\treturn servletContext;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilterTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006, 2021 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servletapi;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport jakarta.servlet.AsyncContext;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.concurrent.DelegatingSecurityContextRunnable;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.security.web.authentication.WebAuthenticationDetails;\nimport org.springframework.security.web.authentication.logout.LogoutHandler;\nimport org.springframework.test.util.ReflectionTestUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\n/**\n * Tests {@link SecurityContextHolderAwareRequestFilter}.\n *\n * @author Ben Alex\n * @author Rob Winch\n * @author Eddú Meléndez\n */\n@ExtendWith(MockitoExtension.class)\npublic class SecurityContextHolderAwareRequestFilterTests {\n\n\t@Captor\n\tprivate ArgumentCaptor<HttpServletRequest> requestCaptor;\n\n\t@Mock\n\tprivate AuthenticationManager authenticationManager;\n\n\t@Mock\n\tprivate AuthenticationEntryPoint authenticationEntryPoint;\n\n\t@Mock\n\tprivate LogoutHandler logoutHandler;\n\n\t@Mock\n\tprivate FilterChain filterChain;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\tprivate List<LogoutHandler> logoutHandlers;\n\n\tprivate SecurityContextHolderAwareRequestFilter filter;\n\n\t@BeforeEach\n\tpublic void setUp() throws Exception {\n\t\tthis.logoutHandlers = Arrays.asList(this.logoutHandler);\n\t\tthis.filter = new SecurityContextHolderAwareRequestFilter();\n\t\tthis.filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);\n\t\tthis.filter.setAuthenticationManager(this.authenticationManager);\n\t\tthis.filter.setLogoutHandlers(this.logoutHandlers);\n\t\tthis.filter.afterPropertiesSet();\n\t}\n\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void expectedRequestWrapperClassIsUsed() throws Exception {\n\t\tthis.filter.setRolePrefix(\"ROLE_\");\n\t\tthis.filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), this.filterChain);\n\t\t// Now re-execute the filter, ensuring our replacement wrapper is still used\n\t\tthis.filter.doFilter(new MockHttpServletRequest(), new MockHttpServletResponse(), this.filterChain);\n\t\tverify(this.filterChain, times(2)).doFilter(any(SecurityContextHolderAwareRequestWrapper.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t\tthis.filter.destroy();\n\t}\n\n\t@Test\n\tpublic void authenticateFalse() throws Exception {\n\t\tassertThat(wrappedRequest().authenticate(this.response)).isFalse();\n\t\tverify(this.authenticationEntryPoint).commence(eq(this.requestCaptor.getValue()), eq(this.response),\n\t\t\t\tany(AuthenticationException.class));\n\t\tverifyNoMoreInteractions(this.authenticationManager, this.logoutHandler);\n\t\tverify(this.request, times(0)).authenticate(any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticateTrue() throws Exception {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"test\", \"password\", \"ROLE_USER\"));\n\t\tassertThat(wrappedRequest().authenticate(this.response)).isTrue();\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.authenticationManager, this.logoutHandler);\n\t\tverify(this.request, times(0)).authenticate(any(HttpServletResponse.class));\n\t}\n\n\t@Test\n\tpublic void authenticateNullEntryPointFalse() throws Exception {\n\t\tthis.filter.setAuthenticationEntryPoint(null);\n\t\tthis.filter.afterPropertiesSet();\n\t\tassertThat(wrappedRequest().authenticate(this.response)).isFalse();\n\t\tverify(this.request).authenticate(this.response);\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.authenticationManager, this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void authenticateNullEntryPointTrue() throws Exception {\n\t\tgiven(this.request.authenticate(this.response)).willReturn(true);\n\t\tthis.filter.setAuthenticationEntryPoint(null);\n\t\tthis.filter.afterPropertiesSet();\n\t\tassertThat(wrappedRequest().authenticate(this.response)).isTrue();\n\t\tverify(this.request).authenticate(this.response);\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.authenticationManager, this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void login() throws Exception {\n\t\tTestingAuthenticationToken expectedAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tgiven(this.authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class)))\n\t\t\t.willReturn(expectedAuth);\n\t\twrappedRequest().login(expectedAuth.getName(), String.valueOf(expectedAuth.getCredentials()));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(expectedAuth);\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.logoutHandler);\n\t\tverify(this.request, times(0)).login(anyString(), anyString());\n\t}\n\n\t// SEC-2296\n\t@Test\n\tpublic void loginWithExistingUser() throws Exception {\n\t\tTestingAuthenticationToken expectedAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(expectedAuth);\n\t\tassertThatExceptionOfType(ServletException.class).isThrownBy(\n\t\t\t\t() -> wrappedRequest().login(expectedAuth.getName(), String.valueOf(expectedAuth.getCredentials())));\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(expectedAuth);\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.logoutHandler);\n\t\tverify(this.request, times(0)).login(anyString(), anyString());\n\t}\n\n\t@Test\n\tpublic void loginFail() throws Exception {\n\t\tAuthenticationException authException = new BadCredentialsException(\"Invalid\");\n\t\tgiven(this.authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class)))\n\t\t\t.willThrow(authException);\n\t\tassertThatExceptionOfType(ServletException.class)\n\t\t\t.isThrownBy(() -> wrappedRequest().login(\"invalid\", \"credentials\"))\n\t\t\t.withCause(authException);\n\t\tassertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.logoutHandler);\n\t\tverify(this.request, times(0)).login(anyString(), anyString());\n\t}\n\n\t@Test\n\tpublic void loginNullAuthenticationManager() throws Exception {\n\t\tthis.filter.setAuthenticationManager(null);\n\t\tthis.filter.afterPropertiesSet();\n\t\tString username = \"username\";\n\t\tString password = \"password\";\n\t\twrappedRequest().login(username, password);\n\t\tverify(this.request).login(username, password);\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.authenticationManager, this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void loginNullAuthenticationManagerFail() throws Exception {\n\t\tthis.filter.setAuthenticationManager(null);\n\t\tthis.filter.afterPropertiesSet();\n\t\tString username = \"username\";\n\t\tString password = \"password\";\n\t\tServletException authException = new ServletException(\"Failed Login\");\n\t\twillThrow(authException).given(this.request).login(username, password);\n\t\tassertThatExceptionOfType(ServletException.class).isThrownBy(() -> wrappedRequest().login(username, password))\n\t\t\t.isEqualTo(authException);\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.authenticationManager, this.logoutHandler);\n\t}\n\n\t@Test\n\tpublic void loginWhenHttpServletRequestHasAuthenticationDetailsThenAuthenticationRequestHasDetails()\n\t\t\tthrows Exception {\n\t\tString ipAddress = \"10.0.0.100\";\n\t\tString sessionId = \"session-id\";\n\t\tgiven(this.request.getRemoteAddr()).willReturn(ipAddress);\n\t\tgiven(this.request.getSession(anyBoolean())).willReturn(new MockHttpSession(null, sessionId));\n\t\twrappedRequest().login(\"username\", \"password\");\n\n\t\tArgumentCaptor<UsernamePasswordAuthenticationToken> authenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(UsernamePasswordAuthenticationToken.class);\n\t\tverify(this.authenticationManager).authenticate(authenticationCaptor.capture());\n\n\t\tUsernamePasswordAuthenticationToken authenticationRequest = authenticationCaptor.getValue();\n\t\tassertThat(authenticationRequest.getDetails()).isInstanceOf(WebAuthenticationDetails.class);\n\n\t\tWebAuthenticationDetails details = (WebAuthenticationDetails) authenticationRequest.getDetails();\n\t\tassertThat(details.getRemoteAddress()).isEqualTo(ipAddress);\n\t\tassertThat(details.getSessionId()).isEqualTo(sessionId);\n\t}\n\n\t@Test\n\tpublic void logout() throws Exception {\n\t\tTestingAuthenticationToken expectedAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(expectedAuth);\n\t\tHttpServletRequest wrappedRequest = wrappedRequest();\n\t\twrappedRequest.logout();\n\t\tverify(this.logoutHandler).logout(wrappedRequest, this.response, expectedAuth);\n\t\tverifyNoMoreInteractions(this.authenticationManager, this.logoutHandler);\n\t\tverify(this.request, times(0)).logout();\n\t}\n\n\t@Test\n\tpublic void logoutNullLogoutHandler() throws Exception {\n\t\tthis.filter.setLogoutHandlers(null);\n\t\tthis.filter.afterPropertiesSet();\n\t\twrappedRequest().logout();\n\t\tverify(this.request).logout();\n\t\tverifyNoMoreInteractions(this.authenticationEntryPoint, this.authenticationManager, this.logoutHandler);\n\t}\n\n\t// gh-3780\n\t@Test\n\tpublic void getAsyncContextNullFromSuper() throws Exception {\n\t\tassertThat(wrappedRequest().getAsyncContext()).isNull();\n\t}\n\n\t@Test\n\tpublic void getAsyncContextStart() throws Exception {\n\t\tArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tTestingAuthenticationToken expectedAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tcontext.setAuthentication(expectedAuth);\n\t\tSecurityContextHolder.setContext(context);\n\t\tAsyncContext asyncContext = mock(AsyncContext.class);\n\t\tgiven(this.request.getAsyncContext()).willReturn(asyncContext);\n\t\tRunnable runnable = () -> {\n\t\t};\n\t\twrappedRequest().getAsyncContext().start(runnable);\n\t\tverifyNoMoreInteractions(this.authenticationManager, this.logoutHandler);\n\t\tverify(asyncContext).start(runnableCaptor.capture());\n\t\tDelegatingSecurityContextRunnable wrappedRunnable = (DelegatingSecurityContextRunnable) runnableCaptor\n\t\t\t.getValue();\n\t\tassertThat(ReflectionTestUtils.getField(wrappedRunnable, \"delegateSecurityContext\")).isEqualTo(context);\n\t\tassertThat(ReflectionTestUtils.getField(wrappedRunnable, \"delegate\"));\n\t}\n\n\t@Test\n\tpublic void startAsyncStart() throws Exception {\n\t\tArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tTestingAuthenticationToken expectedAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tcontext.setAuthentication(expectedAuth);\n\t\tSecurityContextHolder.setContext(context);\n\t\tAsyncContext asyncContext = mock(AsyncContext.class);\n\t\tgiven(this.request.startAsync()).willReturn(asyncContext);\n\t\tRunnable runnable = () -> {\n\t\t};\n\t\twrappedRequest().startAsync().start(runnable);\n\t\tverifyNoMoreInteractions(this.authenticationManager, this.logoutHandler);\n\t\tverify(asyncContext).start(runnableCaptor.capture());\n\t\tDelegatingSecurityContextRunnable wrappedRunnable = (DelegatingSecurityContextRunnable) runnableCaptor\n\t\t\t.getValue();\n\t\tassertThat(ReflectionTestUtils.getField(wrappedRunnable, \"delegateSecurityContext\")).isEqualTo(context);\n\t\tassertThat(ReflectionTestUtils.getField(wrappedRunnable, \"delegate\"));\n\t}\n\n\t@Test\n\tpublic void startAsyncWithRequestResponseStart() throws Exception {\n\t\tArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);\n\t\tSecurityContext context = SecurityContextHolder.createEmptyContext();\n\t\tTestingAuthenticationToken expectedAuth = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tcontext.setAuthentication(expectedAuth);\n\t\tSecurityContextHolder.setContext(context);\n\t\tAsyncContext asyncContext = mock(AsyncContext.class);\n\t\tgiven(this.request.startAsync(this.request, this.response)).willReturn(asyncContext);\n\t\tRunnable runnable = () -> {\n\t\t};\n\t\twrappedRequest().startAsync(this.request, this.response).start(runnable);\n\t\tverifyNoMoreInteractions(this.authenticationManager, this.logoutHandler);\n\t\tverify(asyncContext).start(runnableCaptor.capture());\n\t\tDelegatingSecurityContextRunnable wrappedRunnable = (DelegatingSecurityContextRunnable) runnableCaptor\n\t\t\t.getValue();\n\t\tassertThat(ReflectionTestUtils.getField(wrappedRunnable, \"delegateSecurityContext\")).isEqualTo(context);\n\t\tassertThat(ReflectionTestUtils.getField(wrappedRunnable, \"delegate\"));\n\t}\n\n\t// SEC-3047\n\t@Test\n\tpublic void updateRequestFactory() throws Exception {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"password\", \"PREFIX_USER\"));\n\t\tthis.filter.setRolePrefix(\"PREFIX_\");\n\t\tassertThat(wrappedRequest().isUserInRole(\"PREFIX_USER\")).isTrue();\n\t}\n\n\tprivate HttpServletRequest wrappedRequest() throws Exception {\n\t\tthis.filter.doFilter(this.request, this.response, this.filterChain);\n\t\tverify(this.filterChain).doFilter(this.requestCaptor.capture(), any(HttpServletResponse.class));\n\t\treturn this.requestCaptor.getValue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/servletapi/SecurityContextHolderAwareRequestWrapperTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.servletapi;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.AuthenticatedPrincipal;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.User;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests {@link SecurityContextHolderAwareRequestWrapper}.\n *\n * @author Ben Alex\n */\npublic class SecurityContextHolderAwareRequestWrapperTests {\n\n\t@BeforeEach\n\tpublic void tearDown() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void testCorrectOperationWithStringBasedPrincipal() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"rod\", \"koala\", \"ROLE_FOO\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/\");\n\t\tSecurityContextHolderAwareRequestWrapper wrapper = new SecurityContextHolderAwareRequestWrapper(request, \"\");\n\t\tassertThat(wrapper.getRemoteUser()).isEqualTo(\"rod\");\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_FOO\")).isTrue();\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_NOT_GRANTED\")).isFalse();\n\t\tassertThat(wrapper.getUserPrincipal()).isEqualTo(auth);\n\t}\n\n\t@Test\n\tpublic void testUseOfRolePrefixMeansItIsntNeededWhenCallngIsUserInRole() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"rod\", \"koala\", \"ROLE_FOO\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/\");\n\t\tSecurityContextHolderAwareRequestWrapper wrapper = new SecurityContextHolderAwareRequestWrapper(request,\n\t\t\t\t\"ROLE_\");\n\t\tassertThat(wrapper.isUserInRole(\"FOO\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void testCorrectOperationWithUserDetailsBasedPrincipal() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\n\t\t\t\tnew User(\"rodAsUserDetails\", \"koala\", true, true, true, true, AuthorityUtils.NO_AUTHORITIES), \"koala\",\n\t\t\t\t\"ROLE_HELLO\", \"ROLE_FOOBAR\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/\");\n\t\tSecurityContextHolderAwareRequestWrapper wrapper = new SecurityContextHolderAwareRequestWrapper(request, \"\");\n\t\tassertThat(wrapper.getRemoteUser()).isEqualTo(\"rodAsUserDetails\");\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_FOO\")).isFalse();\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_NOT_GRANTED\")).isFalse();\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_FOOBAR\")).isTrue();\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_HELLO\")).isTrue();\n\t\tassertThat(wrapper.getUserPrincipal()).isEqualTo(auth);\n\t}\n\n\t@Test\n\tpublic void testRoleIsntHeldIfAuthenticationIsNull() {\n\t\tSecurityContextHolder.getContext().setAuthentication(null);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/\");\n\t\tSecurityContextHolderAwareRequestWrapper wrapper = new SecurityContextHolderAwareRequestWrapper(request, \"\");\n\t\tassertThat(wrapper.getRemoteUser()).isNull();\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_ANY\")).isFalse();\n\t\tassertThat(wrapper.getUserPrincipal()).isNull();\n\t}\n\n\t@Test\n\tpublic void testRolesArentHeldIfAuthenticationPrincipalIsNull() {\n\t\tAuthentication auth = new TestingAuthenticationToken(null, \"koala\", \"ROLE_HELLO\", \"ROLE_FOOBAR\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/\");\n\t\tSecurityContextHolderAwareRequestWrapper wrapper = new SecurityContextHolderAwareRequestWrapper(request, \"\");\n\t\tassertThat(wrapper.getRemoteUser()).isNull();\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_HELLO\")).isFalse(); // principal is null, so\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// reject\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_FOOBAR\")).isFalse(); // principal is null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// so reject\n\t\tassertThat(wrapper.getUserPrincipal()).isNull();\n\t}\n\n\t@Test\n\tpublic void testRolePrefix() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"user\", \"koala\", \"ROLE_HELLO\", \"ROLE_FOOBAR\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSecurityContextHolderAwareRequestWrapper wrapper = new SecurityContextHolderAwareRequestWrapper(request,\n\t\t\t\t\"ROLE_\");\n\t\tassertThat(wrapper.isUserInRole(\"HELLO\")).isTrue();\n\t\tassertThat(wrapper.isUserInRole(\"FOOBAR\")).isTrue();\n\t}\n\n\t// SEC-3020\n\t@Test\n\tpublic void testRolePrefixNotAppliedIfRoleStartsWith() {\n\t\tAuthentication auth = new TestingAuthenticationToken(\"user\", \"koala\", \"ROLE_HELLO\", \"ROLE_FOOBAR\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tSecurityContextHolderAwareRequestWrapper wrapper = new SecurityContextHolderAwareRequestWrapper(request,\n\t\t\t\t\"ROLE_\");\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_HELLO\")).isTrue();\n\t\tassertThat(wrapper.isUserInRole(\"ROLE_FOOBAR\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void testGetRemoteUserStringWithAuthenticatedPrincipal() {\n\t\tString username = \"authPrincipalUsername\";\n\t\tAuthenticatedPrincipal principal = mock(AuthenticatedPrincipal.class);\n\t\tgiven(principal.getName()).willReturn(username);\n\t\tAuthentication auth = new TestingAuthenticationToken(principal, \"user\", \"ROLE_USER\");\n\t\tSecurityContextHolder.getContext().setAuthentication(auth);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestURI(\"/\");\n\t\tSecurityContextHolderAwareRequestWrapper wrapper = new SecurityContextHolderAwareRequestWrapper(request, \"\");\n\t\tassertThat(wrapper.getRemoteUser()).isEqualTo(username);\n\t\tverify(principal, times(1)).getName();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpSession;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.ArgumentCaptor;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.session.SessionFixationProtectionEvent;\nimport org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * @author Luke Taylor\n */\npublic class DefaultSessionAuthenticationStrategyTests {\n\n\t@Test\n\tpublic void newSessionShouldNotBeCreatedIfNoSessionExistsAndAlwaysCreateIsFalse() {\n\t\tSessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tstrategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());\n\t\tassertThat(request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tpublic void newSessionIsCreatedIfSessionAlreadyExists() {\n\t\tSessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tString sessionId = request.getSession().getId();\n\t\tstrategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());\n\t\tassertThat(sessionId.equals(request.getSession().getId())).isFalse();\n\t}\n\n\t// SEC-2002\n\t@Test\n\tpublic void newSessionIsCreatedIfSessionAlreadyExistsWithEventPublisher() {\n\t\tSessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpSession session = request.getSession();\n\t\tsession.setAttribute(\"blah\", \"blah\");\n\t\tsession.setAttribute(\"SPRING_SECURITY_SAVED_REQUEST_KEY\", \"DefaultSavedRequest\");\n\t\tString oldSessionId = session.getId();\n\t\tApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);\n\t\tstrategy.setApplicationEventPublisher(eventPublisher);\n\t\tAuthentication mockAuthentication = mock(Authentication.class);\n\t\tstrategy.onAuthentication(mockAuthentication, request, new MockHttpServletResponse());\n\t\tArgumentCaptor<ApplicationEvent> eventArgumentCaptor = ArgumentCaptor.forClass(ApplicationEvent.class);\n\t\tverify(eventPublisher).publishEvent(eventArgumentCaptor.capture());\n\t\tassertThat(oldSessionId.equals(request.getSession().getId())).isFalse();\n\t\tassertThat(request.getSession().getAttribute(\"blah\")).isNotNull();\n\t\tassertThat(request.getSession().getAttribute(\"SPRING_SECURITY_SAVED_REQUEST_KEY\")).isNotNull();\n\t\tassertThat(eventArgumentCaptor.getValue()).isNotNull();\n\t\tassertThat(eventArgumentCaptor.getValue() instanceof SessionFixationProtectionEvent).isTrue();\n\t\tSessionFixationProtectionEvent event = (SessionFixationProtectionEvent) eventArgumentCaptor.getValue();\n\t\tassertThat(event.getOldSessionId()).isEqualTo(oldSessionId);\n\t\tassertThat(event.getNewSessionId()).isEqualTo(request.getSession().getId());\n\t\tassertThat(event.getAuthentication()).isSameAs(mockAuthentication);\n\t}\n\n\t// See SEC-1077\n\t@Test\n\tpublic void onlySavedRequestAttributeIsMigratedIfMigrateAttributesIsFalse() {\n\t\tSessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();\n\t\tstrategy.setMigrateSessionAttributes(false);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpSession session = request.getSession();\n\t\tsession.setAttribute(\"blah\", \"blah\");\n\t\tsession.setAttribute(\"SPRING_SECURITY_SAVED_REQUEST_KEY\", \"DefaultSavedRequest\");\n\t\tstrategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());\n\t\tassertThat(request.getSession().getAttribute(\"blah\")).isNull();\n\t\tassertThat(request.getSession().getAttribute(\"SPRING_SECURITY_SAVED_REQUEST_KEY\")).isNotNull();\n\t}\n\n\t// SEC-2002\n\t@Test\n\tpublic void onlySavedRequestAttributeIsMigratedIfMigrateAttributesIsFalseWithEventPublisher() {\n\t\tSessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();\n\t\tstrategy.setMigrateSessionAttributes(false);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpSession session = request.getSession();\n\t\tsession.setAttribute(\"blah\", \"blah\");\n\t\tsession.setAttribute(\"SPRING_SECURITY_SAVED_REQUEST_KEY\", \"DefaultSavedRequest\");\n\t\tString oldSessionId = session.getId();\n\t\tApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);\n\t\tstrategy.setApplicationEventPublisher(eventPublisher);\n\t\tAuthentication mockAuthentication = mock(Authentication.class);\n\t\tstrategy.onAuthentication(mockAuthentication, request, new MockHttpServletResponse());\n\t\tArgumentCaptor<ApplicationEvent> eventArgumentCaptor = ArgumentCaptor.forClass(ApplicationEvent.class);\n\t\tverify(eventPublisher).publishEvent(eventArgumentCaptor.capture());\n\t\tassertThat(request.getSession().getAttribute(\"blah\")).isNull();\n\t\tassertThat(request.getSession().getAttribute(\"SPRING_SECURITY_SAVED_REQUEST_KEY\")).isNotNull();\n\t\tassertThat(eventArgumentCaptor.getValue()).isNotNull();\n\t\tassertThat(eventArgumentCaptor.getValue() instanceof SessionFixationProtectionEvent).isTrue();\n\t\tSessionFixationProtectionEvent event = (SessionFixationProtectionEvent) eventArgumentCaptor.getValue();\n\t\tassertThat(event.getOldSessionId()).isEqualTo(oldSessionId);\n\t\tassertThat(event.getNewSessionId()).isEqualTo(request.getSession().getId());\n\t\tassertThat(event.getAuthentication()).isSameAs(mockAuthentication);\n\t}\n\n\t@Test\n\tpublic void sessionIsCreatedIfAlwaysCreateTrue() {\n\t\tSessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();\n\t\tstrategy.setAlwaysCreateSession(true);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tstrategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t}\n\n\t@Test\n\tpublic void onAuthenticationWhenMigrateSessionAttributesTrueThenMaxInactiveIntervalIsMigrated() {\n\t\tSessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpSession session = request.getSession();\n\t\tsession.setMaxInactiveInterval(1);\n\t\tAuthentication mockAuthentication = mock(Authentication.class);\n\t\tstrategy.onAuthentication(mockAuthentication, request, new MockHttpServletResponse());\n\t\tassertThat(request.getSession().getMaxInactiveInterval()).isEqualTo(1);\n\t}\n\n\t@Test\n\tpublic void onAuthenticationWhenMigrateSessionAttributesFalseThenMaxInactiveIntervalIsNotMigrated() {\n\t\tSessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();\n\t\tstrategy.setMigrateSessionAttributes(false);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpSession session = request.getSession();\n\t\tsession.setMaxInactiveInterval(1);\n\t\tAuthentication mockAuthentication = mock(Authentication.class);\n\t\tstrategy.onAuthentication(mockAuthentication, request, new MockHttpServletResponse());\n\t\tassertThat(request.getSession().getMaxInactiveInterval()).isNotEqualTo(1);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/session/DisableEncodeUrlFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.util.function.Consumer;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n/**\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\nclass DisableEncodeUrlFilterTests {\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Mock\n\tprivate HttpServletResponse response;\n\n\tprivate DisableEncodeUrlFilter filter = new DisableEncodeUrlFilter();\n\n\t@Test\n\tvoid doFilterDisablesEncodeURL() throws Exception {\n\t\tverifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeURL(\"/\"));\n\t}\n\n\t@Test\n\tvoid doFilterDisablesEncodeRedirectURL() throws Exception {\n\t\tverifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeRedirectURL(\"/\"));\n\t}\n\n\tprivate void verifyDoFilterDoesNotInteractWithResponse(Consumer<HttpServletResponse> toInvoke) throws Exception {\n\t\tthis.filter.doFilter(this.request, this.response, (request, response) -> {\n\t\t\tHttpServletResponse httpResponse = (HttpServletResponse) response;\n\t\t\ttoInvoke.accept(httpResponse);\n\t\t});\n\t\tverifyNoInteractions(this.response);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/session/ForceEagerSessionCreationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass ForceEagerSessionCreationFilterTests {\n\n\t@Test\n\tvoid createsSession() throws Exception {\n\t\tForceEagerSessionCreationFilter filter = new ForceEagerSessionCreationFilter();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tMockFilterChain chain = new MockFilterChain();\n\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), chain);\n\n\t\tassertThat(request.getSession(false)).isNotNull();\n\t\tassertThat(chain.getRequest()).isEqualTo(request);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/session/HttpSessionDestroyedEventTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextImpl;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n/**\n * @author Rob Winch\n *\n */\npublic class HttpSessionDestroyedEventTests {\n\n\tprivate MockHttpSession session;\n\n\tprivate HttpSessionDestroyedEvent destroyedEvent;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\tthis.session = new MockHttpSession();\n\t\tthis.session.setAttribute(\"notcontext\", \"notcontext\");\n\t\tthis.session.setAttribute(\"null\", null);\n\t\tthis.session.setAttribute(\"context\", new SecurityContextImpl());\n\t\tthis.destroyedEvent = new HttpSessionDestroyedEvent(this.session);\n\t}\n\n\t// SEC-1870\n\t@Test\n\tpublic void getSecurityContexts() {\n\t\tList<SecurityContext> securityContexts = this.destroyedEvent.getSecurityContexts();\n\t\tassertThat(securityContexts).hasSize(1);\n\t\tassertThat(securityContexts.get(0)).isSameAs(this.session.getAttribute(\"context\"));\n\t}\n\n\t@Test\n\tpublic void getSecurityContextsMulti() {\n\t\tthis.session.setAttribute(\"another\", new SecurityContextImpl());\n\t\tList<SecurityContext> securityContexts = this.destroyedEvent.getSecurityContexts();\n\t\tassertThat(securityContexts).hasSize(2);\n\t}\n\n\t@Test\n\tpublic void getSecurityContextsDiffImpl() {\n\t\tthis.session.setAttribute(\"context\", mock(SecurityContext.class));\n\t\tList<SecurityContext> securityContexts = this.destroyedEvent.getSecurityContexts();\n\t\tassertThat(securityContexts).hasSize(1);\n\t\tassertThat(securityContexts.get(0)).isSameAs(this.session.getAttribute(\"context\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/session/HttpSessionEventPublisherTests.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport jakarta.servlet.http.HttpSessionEvent;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.web.context.WebApplicationContext;\nimport org.springframework.web.context.support.StaticWebApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\n\n/**\n * The HttpSessionEventPublisher tests\n *\n * @author Ray Krueger\n */\npublic class HttpSessionEventPublisherTests {\n\n\t/**\n\t * It's not that complicated so we'll just run it straight through here.\n\t */\n\t@Test\n\tpublic void publishedEventIsReceivedByListener() {\n\t\tHttpSessionEventPublisher publisher = new HttpSessionEventPublisher();\n\t\tStaticWebApplicationContext context = new StaticWebApplicationContext();\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tservletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);\n\t\tcontext.setServletContext(servletContext);\n\t\tcontext.registerSingleton(\"listener\", MockApplicationListener.class, null);\n\t\tcontext.refresh();\n\t\tMockHttpSession session = new MockHttpSession(servletContext);\n\t\tMockApplicationListener listener = (MockApplicationListener) context.getBean(\"listener\");\n\t\tHttpSessionEvent event = new HttpSessionEvent(session);\n\t\tpublisher.sessionCreated(event);\n\t\tassertThat(listener.getCreatedEvent()).isNotNull();\n\t\tassertThat(listener.getDestroyedEvent()).isNull();\n\t\tassertThat(listener.getCreatedEvent().getSession()).isEqualTo(session);\n\t\tlistener.setCreatedEvent(null);\n\t\tlistener.setDestroyedEvent(null);\n\t\tpublisher.sessionDestroyed(event);\n\t\tassertThat(listener.getDestroyedEvent()).isNotNull();\n\t\tassertThat(listener.getCreatedEvent()).isNull();\n\t\tassertThat(listener.getDestroyedEvent().getSession()).isEqualTo(session);\n\t\tpublisher.sessionIdChanged(event, \"oldSessionId\");\n\t\tassertThat(listener.getSessionIdChangedEvent()).isNotNull();\n\t\tassertThat(listener.getSessionIdChangedEvent().getOldSessionId()).isEqualTo(\"oldSessionId\");\n\t\tlistener.setSessionIdChangedEvent(null);\n\t}\n\n\t@Test\n\tpublic void publishedEventIsReceivedByListenerChildContext() {\n\t\tHttpSessionEventPublisher publisher = new HttpSessionEventPublisher();\n\t\tStaticWebApplicationContext context = new StaticWebApplicationContext();\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tservletContext.setAttribute(\"org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher\", context);\n\t\tcontext.setServletContext(servletContext);\n\t\tcontext.registerSingleton(\"listener\", MockApplicationListener.class, null);\n\t\tcontext.refresh();\n\t\tMockHttpSession session = new MockHttpSession(servletContext);\n\t\tMockApplicationListener listener = (MockApplicationListener) context.getBean(\"listener\");\n\t\tHttpSessionEvent event = new HttpSessionEvent(session);\n\t\tpublisher.sessionCreated(event);\n\t\tassertThat(listener.getCreatedEvent()).isNotNull();\n\t\tassertThat(listener.getDestroyedEvent()).isNull();\n\t\tassertThat(listener.getCreatedEvent().getSession()).isEqualTo(session);\n\t\tlistener.setCreatedEvent(null);\n\t\tlistener.setDestroyedEvent(null);\n\t\tpublisher.sessionDestroyed(event);\n\t\tassertThat(listener.getDestroyedEvent()).isNotNull();\n\t\tassertThat(listener.getCreatedEvent()).isNull();\n\t\tassertThat(listener.getDestroyedEvent().getSession()).isEqualTo(session);\n\t\tpublisher.sessionIdChanged(event, \"oldSessionId\");\n\t\tassertThat(listener.getSessionIdChangedEvent()).isNotNull();\n\t\tassertThat(listener.getSessionIdChangedEvent().getOldSessionId()).isEqualTo(\"oldSessionId\");\n\t\tlistener.setSessionIdChangedEvent(null);\n\t}\n\n\t// SEC-2599\n\t@Test\n\tpublic void sessionCreatedNullApplicationContext() {\n\t\tHttpSessionEventPublisher publisher = new HttpSessionEventPublisher();\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tMockHttpSession session = new MockHttpSession(servletContext);\n\t\tHttpSessionEvent event = new HttpSessionEvent(session);\n\t\tassertThatIllegalStateException().isThrownBy(() -> publisher.sessionCreated(event));\n\t}\n\n\t@Test // SEC-2599\n\tpublic void sessionDestroyedNullApplicationContext() {\n\t\tHttpSessionEventPublisher publisher = new HttpSessionEventPublisher();\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tMockHttpSession session = new MockHttpSession(servletContext);\n\t\tHttpSessionEvent event = new HttpSessionEvent(session);\n\t\tassertThatIllegalStateException().isThrownBy(() -> publisher.sessionDestroyed(event));\n\t}\n\n\t@Test\n\tpublic void sessionIdChangeNullApplicationContext() {\n\t\tHttpSessionEventPublisher publisher = new HttpSessionEventPublisher();\n\t\tMockServletContext servletContext = new MockServletContext();\n\t\tMockHttpSession session = new MockHttpSession(servletContext);\n\t\tHttpSessionEvent event = new HttpSessionEvent(session);\n\t\tassertThatIllegalStateException().isThrownBy(() -> publisher.sessionIdChanged(event, \"oldSessionId\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/session/MockApplicationListener.java",
    "content": "/*\n * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationListener;\n\n/**\n * Listener for tests\n *\n * @author Ray Krueger\n */\npublic class MockApplicationListener implements ApplicationListener<ApplicationEvent> {\n\n\tprivate HttpSessionCreatedEvent createdEvent;\n\n\tprivate HttpSessionDestroyedEvent destroyedEvent;\n\n\tprivate HttpSessionIdChangedEvent sessionIdChangedEvent;\n\n\tpublic HttpSessionCreatedEvent getCreatedEvent() {\n\t\treturn this.createdEvent;\n\t}\n\n\tpublic HttpSessionDestroyedEvent getDestroyedEvent() {\n\t\treturn this.destroyedEvent;\n\t}\n\n\t@Override\n\tpublic void onApplicationEvent(ApplicationEvent event) {\n\t\tif (event instanceof HttpSessionCreatedEvent) {\n\t\t\tthis.createdEvent = (HttpSessionCreatedEvent) event;\n\t\t}\n\t\telse if (event instanceof HttpSessionDestroyedEvent) {\n\t\t\tthis.destroyedEvent = (HttpSessionDestroyedEvent) event;\n\t\t}\n\t\telse if (event instanceof HttpSessionIdChangedEvent) {\n\t\t\tthis.sessionIdChangedEvent = (HttpSessionIdChangedEvent) event;\n\t\t}\n\t}\n\n\tpublic void setCreatedEvent(HttpSessionCreatedEvent createdEvent) {\n\t\tthis.createdEvent = createdEvent;\n\t}\n\n\tpublic void setDestroyedEvent(HttpSessionDestroyedEvent destroyedEvent) {\n\t\tthis.destroyedEvent = destroyedEvent;\n\t}\n\n\tpublic void setSessionIdChangedEvent(HttpSessionIdChangedEvent sessionIdChangedEvent) {\n\t\tthis.sessionIdChangedEvent = sessionIdChangedEvent;\n\t}\n\n\tpublic HttpSessionIdChangedEvent getSessionIdChangedEvent() {\n\t\treturn this.sessionIdChangedEvent;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/session/SessionInformationExpiredEventTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport java.util.Date;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.core.session.SessionInformation;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n * @since 4.2\n */\npublic class SessionInformationExpiredEventTests {\n\n\t@Test\n\tpublic void constructorWhenSessionInformationNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SessionInformationExpiredEvent(null,\n\t\t\t\tnew MockHttpServletRequest(), new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenRequestNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SessionInformationExpiredEvent(\n\t\t\t\tnew SessionInformation(\"fake\", \"sessionId\", new Date()), null, new MockHttpServletResponse()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenResponseNullThenThrowsException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new SessionInformationExpiredEvent(\n\t\t\t\tnew SessionInformation(\"fake\", \"sessionId\", new Date()), new MockHttpServletRequest(), null));\n\t}\n\n\t@Test\n\tvoid constructorWhenFilterChainThenGetFilterChainReturnsNotNull() {\n\t\tMockFilterChain filterChain = new MockFilterChain();\n\t\tSessionInformationExpiredEvent event = new SessionInformationExpiredEvent(\n\t\t\t\tnew SessionInformation(\"fake\", \"sessionId\", new Date()), new MockHttpServletRequest(),\n\t\t\t\tnew MockHttpServletResponse(), filterChain);\n\t\tassertThat(event.getFilterChain()).isSameAs(filterChain);\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.session;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Answers;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationException;\nimport org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;\nimport org.springframework.security.web.context.SecurityContextRepository;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.willThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.mockito.Mockito.withSettings;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n * @author Mark Chesney\n */\npublic class SessionManagementFilterTests {\n\n\t@BeforeEach\n\t@AfterEach\n\tpublic void clearContext() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tpublic void newSessionShouldNotBeCreatedIfSessionExistsAndUserIsNotAuthenticated() throws Exception {\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tString sessionId = request.getSession().getId();\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tassertThat(request.getSession().getId()).isEqualTo(sessionId);\n\t}\n\n\t@Test\n\tpublic void strategyIsNotInvokedIfSecurityContextAlreadyExistsForRequest() throws Exception {\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\tSessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);\n\t\t// mock that repo contains a security context\n\t\tgiven(repo.containsContext(any(HttpServletRequest.class))).willReturn(true);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo, strategy);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tauthenticateUser();\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tverifyNoMoreInteractions(strategy);\n\t}\n\n\t@Test\n\tpublic void strategyIsNotInvokedIfAuthenticationIsNull() throws Exception {\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\tSessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo, strategy);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tverifyNoMoreInteractions(strategy);\n\t}\n\n\t@Test\n\tpublic void strategyIsInvokedIfUserIsNewlyAuthenticated() throws Exception {\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\t// repo will return false to containsContext()\n\t\tSessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo, strategy);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tauthenticateUser();\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tverify(strategy).onAuthentication(any(Authentication.class), any(HttpServletRequest.class),\n\t\t\t\tany(HttpServletResponse.class));\n\t\t// Check that it is only applied once to the request\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tverifyNoMoreInteractions(strategy);\n\t}\n\n\t@Test\n\tpublic void strategyFailureInvokesFailureHandler() throws Exception {\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\t// repo will return false to containsContext()\n\t\tSessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);\n\t\tAuthenticationFailureHandler failureHandler = mock(AuthenticationFailureHandler.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo, strategy);\n\t\tfilter.setAuthenticationFailureHandler(failureHandler);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\tauthenticateUser();\n\t\tSessionAuthenticationException exception = new SessionAuthenticationException(\"Failure\");\n\t\twillThrow(exception).given(strategy)\n\t\t\t.onAuthentication(SecurityContextHolder.getContext().getAuthentication(), request, response);\n\t\tfilter.doFilter(request, response, fc);\n\t\tverifyNoMoreInteractions(fc);\n\t\tverify(failureHandler).onAuthenticationFailure(request, response, exception);\n\t}\n\n\t@Test\n\tpublic void responseIsRedirectedToTimeoutUrlIfSetAndSessionIsInvalid() throws Exception {\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\t// repo will return false to containsContext()\n\t\tSessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo, strategy);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestedSessionId(\"xxx\");\n\t\trequest.setRequestedSessionIdValid(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getRedirectedUrl()).isNull();\n\t\t// Now set a redirect URL\n\t\trequest = new MockHttpServletRequest();\n\t\trequest.setRequestedSessionId(\"xxx\");\n\t\trequest.setRequestedSessionIdValid(false);\n\t\tSimpleRedirectInvalidSessionStrategy iss = new SimpleRedirectInvalidSessionStrategy(\"/timedOut\");\n\t\tiss.setCreateNewSession(true);\n\t\tfilter.setInvalidSessionStrategy(iss);\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, fc);\n\t\tverifyNoMoreInteractions(fc);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/timedOut\");\n\t}\n\n\t@Test\n\tpublic void responseIsRedirectedToRequestedUrlIfSetAndSessionIsInvalid() throws Exception {\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\t// repo will return false to containsContext()\n\t\tSessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo, strategy);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestedSessionId(\"xxx\");\n\t\trequest.setRequestedSessionIdValid(false);\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tfilter.doFilter(request, response, new MockFilterChain());\n\t\tassertThat(response.getRedirectedUrl()).isNull();\n\t\t// Now set a redirect URL\n\t\trequest = new MockHttpServletRequest();\n\t\trequest.setRequestedSessionId(\"xxx\");\n\t\trequest.setRequestedSessionIdValid(false);\n\t\trequest.setRequestURI(\"/requested\");\n\t\tRequestedUrlRedirectInvalidSessionStrategy iss = new RequestedUrlRedirectInvalidSessionStrategy();\n\t\tiss.setCreateNewSession(true);\n\t\tfilter.setInvalidSessionStrategy(iss);\n\t\tFilterChain fc = mock(FilterChain.class);\n\t\tfilter.doFilter(request, response, fc);\n\t\tverifyNoMoreInteractions(fc);\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/requested\");\n\t}\n\n\t@Test\n\tpublic void responseIsRedirectedToRequestedUrlIfContextPathIsSetAndSessionIsInvalid() throws Exception {\n\t\t// given\n\t\tDefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\t\tredirectStrategy.setContextRelative(true);\n\t\tRequestedUrlRedirectInvalidSessionStrategy invalidSessionStrategy = new RequestedUrlRedirectInvalidSessionStrategy();\n\t\tinvalidSessionStrategy.setCreateNewSession(true);\n\t\tinvalidSessionStrategy.setRedirectStrategy(redirectStrategy);\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(securityContextRepository,\n\t\t\t\tsessionAuthenticationStrategy);\n\t\tfilter.setInvalidSessionStrategy(invalidSessionStrategy);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setContextPath(\"/context\");\n\t\trequest.setRequestedSessionId(\"xxx\");\n\t\trequest.setRequestedSessionIdValid(false);\n\t\trequest.setRequestURI(\"/context/requested\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\n\t\t// when\n\t\tfilter.doFilter(request, response, chain);\n\n\t\t// then\n\t\tverify(securityContextRepository).containsContext(request);\n\t\tverifyNoMoreInteractions(securityContextRepository, sessionAuthenticationStrategy, chain);\n\t\tassertThat(response.isCommitted()).isTrue();\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/context/requested\");\n\t\tassertThat(response.getStatus()).isEqualTo(302);\n\t}\n\n\t@Test\n\tpublic void responseIsRedirectedToRequestedUrlIfStatusCodeIsSetAndSessionIsInvalid() throws Exception {\n\t\t// given\n\t\tDefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\t\tredirectStrategy.setStatusCode(HttpStatus.TEMPORARY_REDIRECT);\n\t\tRequestedUrlRedirectInvalidSessionStrategy invalidSessionStrategy = new RequestedUrlRedirectInvalidSessionStrategy();\n\t\tinvalidSessionStrategy.setCreateNewSession(true);\n\t\tinvalidSessionStrategy.setRedirectStrategy(redirectStrategy);\n\t\tSecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);\n\t\tSessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(securityContextRepository,\n\t\t\t\tsessionAuthenticationStrategy);\n\t\tfilter.setInvalidSessionStrategy(invalidSessionStrategy);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRequestedSessionId(\"xxx\");\n\t\trequest.setRequestedSessionIdValid(false);\n\t\trequest.setRequestURI(\"/requested\");\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tFilterChain chain = mock(FilterChain.class);\n\n\t\t// when\n\t\tfilter.doFilter(request, response, chain);\n\n\t\t// then\n\t\tverify(securityContextRepository).containsContext(request);\n\t\tverifyNoMoreInteractions(securityContextRepository, sessionAuthenticationStrategy, chain);\n\t\tassertThat(response.isCommitted()).isTrue();\n\t\tassertThat(response.getRedirectedUrl()).isEqualTo(\"/requested\");\n\t\tassertThat(response.getStatus()).isEqualTo(307);\n\t}\n\n\t@Test\n\tpublic void customAuthenticationTrustResolver() throws Exception {\n\t\tAuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class,\n\t\t\t\twithSettings().defaultAnswer(Answers.CALLS_REAL_METHODS));\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo);\n\t\tfilter.setTrustResolver(trustResolver);\n\t\tHttpServletRequest request = new MockHttpServletRequest();\n\t\tauthenticateUser();\n\t\tfilter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());\n\t\tverify(trustResolver).isAnonymous(any(Authentication.class));\n\t}\n\n\t@Test\n\tpublic void setTrustResolverNull() {\n\t\tSecurityContextRepository repo = mock(SecurityContextRepository.class);\n\t\tSessionManagementFilter filter = new SessionManagementFilter(repo);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> filter.setTrustResolver(null));\n\t}\n\n\tprivate void authenticateUser() {\n\t\tSecurityContextHolder.getContext()\n\t\t\t.setAuthentication(new TestingAuthenticationToken(\"user\", \"pass\", \"ROLE_USER\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/transport/HttpsRedirectFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.transport;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.PortMapper;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.web.util.UriComponents;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\n/**\n * Tests for {@link HttpsRedirectFilter}\n *\n * @author Josh Cummings\n */\n@ExtendWith(MockitoExtension.class)\npublic class HttpsRedirectFilterTests {\n\n\tHttpsRedirectFilter filter;\n\n\t@Mock\n\tFilterChain chain;\n\n\t@BeforeEach\n\tpublic void configureFilter() {\n\t\tthis.filter = new HttpsRedirectFilter();\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestIsInsecureThenRedirects() throws Exception {\n\t\tHttpServletRequest request = get(\"http://localhost\");\n\t\tHttpServletResponse response = ok();\n\t\tthis.filter.doFilter(request, response, this.chain);\n\t\tassertThat(statusCode(response)).isEqualTo(302);\n\t\tassertThat(redirectedUrl(response)).isEqualTo(\"https://localhost\");\n\t}\n\n\t@Test\n\tpublic void filterWhenExchangeIsSecureThenNoRedirect() throws Exception {\n\t\tHttpServletRequest request = get(\"https://localhost\");\n\t\tHttpServletResponse response = ok();\n\t\tthis.filter.doFilter(request, response, this.chain);\n\t\tassertThat(statusCode(response)).isEqualTo(200);\n\t}\n\n\t@Test\n\tpublic void filterWhenExchangeMismatchesThenNoRedirect() throws Exception {\n\t\tRequestMatcher matcher = mock(RequestMatcher.class);\n\t\tthis.filter.setRequestMatcher(matcher);\n\t\tHttpServletRequest request = get(\"http://localhost:8080\");\n\t\tHttpServletResponse response = ok();\n\t\tthis.filter.doFilter(request, response, this.chain);\n\t\tassertThat(statusCode(response)).isEqualTo(200);\n\t}\n\n\t@Test\n\tpublic void filterWhenExchangeMatchesAndRequestIsInsecureThenRedirects() throws Exception {\n\t\tRequestMatcher matcher = mock(RequestMatcher.class);\n\t\tgiven(matcher.matches(any())).willReturn(true);\n\t\tthis.filter.setRequestMatcher(matcher);\n\t\tHttpServletRequest request = get(\"http://localhost:8080\");\n\t\tHttpServletResponse response = ok();\n\t\tthis.filter.doFilter(request, response, this.chain);\n\t\tassertThat(statusCode(response)).isEqualTo(302);\n\t\tassertThat(redirectedUrl(response)).isEqualTo(\"https://localhost:8443\");\n\t\tverify(matcher).matches(any(HttpServletRequest.class));\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestIsInsecureThenPortMapperRemapsPort() throws Exception {\n\t\tPortMapper portMapper = mock(PortMapper.class);\n\t\tgiven(portMapper.lookupHttpsPort(314)).willReturn(159);\n\t\tthis.filter.setPortMapper(portMapper);\n\t\tHttpServletRequest request = get(\"http://localhost:314\");\n\t\tHttpServletResponse response = ok();\n\t\tthis.filter.doFilter(request, response, this.chain);\n\t\tassertThat(statusCode(response)).isEqualTo(302);\n\t\tassertThat(redirectedUrl(response)).isEqualTo(\"https://localhost:159\");\n\t\tverify(portMapper).lookupHttpsPort(314);\n\t}\n\n\t@Test\n\tpublic void filterWhenRequestIsInsecureAndNoPortMappingThenThrowsIllegalState() {\n\t\tHttpServletRequest request = get(\"http://localhost:1234\");\n\t\tHttpServletResponse response = ok();\n\t\tassertThatIllegalStateException().isThrownBy(() -> this.filter.doFilter(request, response, this.chain));\n\t}\n\n\t@Test\n\tpublic void filterWhenInsecureRequestHasAPathThenRedirects() throws Exception {\n\t\tHttpServletRequest request = get(\"http://localhost:8080/path/page.html?query=string\");\n\t\tHttpServletResponse response = ok();\n\t\tthis.filter.doFilter(request, response, this.chain);\n\t\tassertThat(statusCode(response)).isEqualTo(302);\n\t\tassertThat(redirectedUrl(response)).isEqualTo(\"https://localhost:8443/path/page.html?query=string\");\n\t}\n\n\t@Test\n\tpublic void setRequiresTransportSecurityMatcherWhenSetWithNullValueThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestMatcher(null));\n\t}\n\n\t@Test\n\tpublic void setPortMapperWhenSetWithNullValueThenThrowsIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setPortMapper(null));\n\t}\n\n\tprivate String redirectedUrl(HttpServletResponse response) {\n\t\treturn response.getHeader(HttpHeaders.LOCATION);\n\t}\n\n\tprivate int statusCode(HttpServletResponse response) {\n\t\treturn response.getStatus();\n\t}\n\n\tprivate HttpServletRequest get(String uri) {\n\t\tUriComponents components = UriComponentsBuilder.fromUriString(uri).build();\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", components.getPath());\n\t\trequest.setQueryString(components.getQuery());\n\t\tif (components.getScheme() != null) {\n\t\t\trequest.setScheme(components.getScheme());\n\t\t}\n\t\tint port = components.getPort();\n\t\tif (port != -1) {\n\t\t\trequest.setServerPort(port);\n\t\t}\n\t\treturn request;\n\t}\n\n\tprivate HttpServletResponse ok() {\n\t\tMockHttpServletResponse response = new MockHttpServletResponse();\n\t\tresponse.setStatus(200);\n\t\treturn response;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/OnCommittedResponseWrapperTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.Locale;\n\nimport jakarta.servlet.ServletOutputStream;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\n\n@ExtendWith(MockitoExtension.class)\npublic class OnCommittedResponseWrapperTests {\n\n\tprivate static final String NL = \"\\r\\n\";\n\n\t@Mock\n\tHttpServletResponse delegate;\n\n\t@Mock\n\tPrintWriter writer;\n\n\t@Mock\n\tServletOutputStream out;\n\n\tOnCommittedResponseWrapper response;\n\n\tboolean committed;\n\n\t@BeforeEach\n\tpublic void setup() throws Exception {\n\t\tthis.response = new OnCommittedResponseWrapper(this.delegate) {\n\t\t\t@Override\n\t\t\tprotected void onResponseCommitted() {\n\t\t\t\tOnCommittedResponseWrapperTests.this.committed = true;\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate void givenGetWriterThenReturn() throws IOException {\n\t\tgiven(this.delegate.getWriter()).willReturn(this.writer);\n\t}\n\n\tprivate void givenGetOutputStreamThenReturn() throws IOException {\n\t\tgiven(this.delegate.getOutputStream()).willReturn(this.out);\n\t}\n\n\t@Test\n\tpublic void printWriterHashCode() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = this.writer.hashCode();\n\t\tassertThat(this.response.getWriter().hashCode()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void printWriterCheckError() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tboolean expected = true;\n\t\tgiven(this.writer.checkError()).willReturn(expected);\n\t\tassertThat(this.response.getWriter().checkError()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void printWriterWriteInt() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = 1;\n\t\tthis.response.getWriter().write(expected);\n\t\tverify(this.writer).write(expected);\n\t}\n\n\t@Test\n\tpublic void printWriterWriteCharIntInt() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar[] buff = new char[0];\n\t\tint off = 2;\n\t\tint len = 3;\n\t\tthis.response.getWriter().write(buff, off, len);\n\t\tverify(this.writer).write(buff, off, len);\n\t}\n\n\t@Test\n\tpublic void printWriterWriteChar() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar[] buff = new char[0];\n\t\tthis.response.getWriter().write(buff);\n\t\tverify(this.writer).write(buff);\n\t}\n\n\t@Test\n\tpublic void printWriterWriteStringIntInt() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString s = \"\";\n\t\tint off = 2;\n\t\tint len = 3;\n\t\tthis.response.getWriter().write(s, off, len);\n\t\tverify(this.writer).write(s, off, len);\n\t}\n\n\t@Test\n\tpublic void printWriterWriteString() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString s = \"\";\n\t\tthis.response.getWriter().write(s);\n\t\tverify(this.writer).write(s);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintBoolean() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tboolean b = true;\n\t\tthis.response.getWriter().print(b);\n\t\tverify(this.writer).print(b);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintChar() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar c = 1;\n\t\tthis.response.getWriter().print(c);\n\t\tverify(this.writer).print(c);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintInt() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint i = 1;\n\t\tthis.response.getWriter().print(i);\n\t\tverify(this.writer).print(i);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintLong() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tlong l = 1;\n\t\tthis.response.getWriter().print(l);\n\t\tverify(this.writer).print(l);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintFloat() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tfloat f = 1;\n\t\tthis.response.getWriter().print(f);\n\t\tverify(this.writer).print(f);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintDouble() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tdouble x = 1;\n\t\tthis.response.getWriter().print(x);\n\t\tverify(this.writer).print(x);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintCharArray() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar[] x = new char[0];\n\t\tthis.response.getWriter().print(x);\n\t\tverify(this.writer).print(x);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintString() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString x = \"1\";\n\t\tthis.response.getWriter().print(x);\n\t\tverify(this.writer).print(x);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintObject() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tObject x = \"1\";\n\t\tthis.response.getWriter().print(x);\n\t\tverify(this.writer).print(x);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintln() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tthis.response.getWriter().println();\n\t\tverify(this.writer).println();\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnBoolean() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tboolean b = true;\n\t\tthis.response.getWriter().println(b);\n\t\tverify(this.writer).println(b);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnChar() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar c = 1;\n\t\tthis.response.getWriter().println(c);\n\t\tverify(this.writer).println(c);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnInt() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint i = 1;\n\t\tthis.response.getWriter().println(i);\n\t\tverify(this.writer).println(i);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnLong() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tlong l = 1;\n\t\tthis.response.getWriter().println(l);\n\t\tverify(this.writer).println(l);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnFloat() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tfloat f = 1;\n\t\tthis.response.getWriter().println(f);\n\t\tverify(this.writer).println(f);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnDouble() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tdouble x = 1;\n\t\tthis.response.getWriter().println(x);\n\t\tverify(this.writer).println(x);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnCharArray() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar[] x = new char[0];\n\t\tthis.response.getWriter().println(x);\n\t\tverify(this.writer).println(x);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnString() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString x = \"1\";\n\t\tthis.response.getWriter().println(x);\n\t\tverify(this.writer).println(x);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintlnObject() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tObject x = \"1\";\n\t\tthis.response.getWriter().println(x);\n\t\tverify(this.writer).println(x);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintfStringObjectVargs() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString format = \"format\";\n\t\tObject[] args = new Object[] { \"1\" };\n\t\tthis.response.getWriter().printf(format, args);\n\t\tverify(this.writer).printf(format, args);\n\t}\n\n\t@Test\n\tpublic void printWriterPrintfLocaleStringObjectVargs() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tLocale l = Locale.US;\n\t\tString format = \"format\";\n\t\tObject[] args = new Object[] { \"1\" };\n\t\tthis.response.getWriter().printf(l, format, args);\n\t\tverify(this.writer).printf(l, format, args);\n\t}\n\n\t@Test\n\tpublic void printWriterFormatStringObjectVargs() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString format = \"format\";\n\t\tObject[] args = new Object[] { \"1\" };\n\t\tthis.response.getWriter().format(format, args);\n\t\tverify(this.writer).format(format, args);\n\t}\n\n\t@Test\n\tpublic void printWriterFormatLocaleStringObjectVargs() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tLocale l = Locale.US;\n\t\tString format = \"format\";\n\t\tObject[] args = new Object[] { \"1\" };\n\t\tthis.response.getWriter().format(l, format, args);\n\t\tverify(this.writer).format(l, format, args);\n\t}\n\n\t@Test\n\tpublic void printWriterAppendCharSequence() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString x = \"a\";\n\t\tthis.response.getWriter().append(x);\n\t\tverify(this.writer).append(x);\n\t}\n\n\t@Test\n\tpublic void printWriterAppendCharSequenceIntInt() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString x = \"abcdef\";\n\t\tint start = 1;\n\t\tint end = 3;\n\t\tthis.response.getWriter().append(x, start, end);\n\t\tverify(this.writer).append(x, start, end);\n\t}\n\n\t@Test\n\tpublic void printWriterAppendChar() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar x = 1;\n\t\tthis.response.getWriter().append(x);\n\t\tverify(this.writer).append(x);\n\t}\n\n\t// servletoutputstream\n\t@Test\n\tpublic void outputStreamHashCode() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint expected = this.out.hashCode();\n\t\tassertThat(this.response.getOutputStream().hashCode()).isEqualTo(expected);\n\t}\n\n\t@Test\n\tpublic void outputStreamWriteInt() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint expected = 1;\n\t\tthis.response.getOutputStream().write(expected);\n\t\tverify(this.out).write(expected);\n\t}\n\n\t@Test\n\tpublic void outputStreamWriteByte() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tbyte[] expected = new byte[0];\n\t\tthis.response.getOutputStream().write(expected);\n\t\tverify(this.out).write(expected);\n\t}\n\n\t@Test\n\tpublic void outputStreamWriteByteIntInt() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint start = 1;\n\t\tint end = 2;\n\t\tbyte[] expected = new byte[0];\n\t\tthis.response.getOutputStream().write(expected, start, end);\n\t\tverify(this.out).write(expected, start, end);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintBoolean() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tboolean b = true;\n\t\tthis.response.getOutputStream().print(b);\n\t\tverify(this.out).print(b);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintChar() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tchar c = 1;\n\t\tthis.response.getOutputStream().print(c);\n\t\tverify(this.out).print(c);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintInt() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint i = 1;\n\t\tthis.response.getOutputStream().print(i);\n\t\tverify(this.out).print(i);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintLong() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tlong l = 1;\n\t\tthis.response.getOutputStream().print(l);\n\t\tverify(this.out).print(l);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintFloat() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tfloat f = 1;\n\t\tthis.response.getOutputStream().print(f);\n\t\tverify(this.out).print(f);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintDouble() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tdouble x = 1;\n\t\tthis.response.getOutputStream().print(x);\n\t\tverify(this.out).print(x);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintString() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tString x = \"1\";\n\t\tthis.response.getOutputStream().print(x);\n\t\tverify(this.out).print(x);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintln() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tthis.response.getOutputStream().println();\n\t\tverify(this.out).println();\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintlnBoolean() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tboolean b = true;\n\t\tthis.response.getOutputStream().println(b);\n\t\tverify(this.out).println(b);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintlnChar() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tchar c = 1;\n\t\tthis.response.getOutputStream().println(c);\n\t\tverify(this.out).println(c);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintlnInt() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint i = 1;\n\t\tthis.response.getOutputStream().println(i);\n\t\tverify(this.out).println(i);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintlnLong() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tlong l = 1;\n\t\tthis.response.getOutputStream().println(l);\n\t\tverify(this.out).println(l);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintlnFloat() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tfloat f = 1;\n\t\tthis.response.getOutputStream().println(f);\n\t\tverify(this.out).println(f);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintlnDouble() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tdouble x = 1;\n\t\tthis.response.getOutputStream().println(x);\n\t\tverify(this.out).println(x);\n\t}\n\n\t@Test\n\tpublic void outputStreamPrintlnString() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tString x = \"1\";\n\t\tthis.response.getOutputStream().println(x);\n\t\tverify(this.out).println(x);\n\t}\n\n\t// The amount of content specified in the setContentLength method of the response\n\t// has been greater than zero and has been written to the response.\n\t// gh-3823\n\t@Test\n\tpublic void contentLengthPrintWriterWriteNullCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString expected = null;\n\t\tthis.response.setContentLength(String.valueOf(expected).length() + 1);\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isFalse();\n\t\tthis.response.getWriter().write(\"a\");\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterWriteIntCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = 1;\n\t\tthis.response.setContentLength(String.valueOf(expected).length());\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterWriteIntMultiDigitCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = 10000;\n\t\tthis.response.setContentLength(String.valueOf(expected).length());\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPlus1PrintWriterWriteIntMultiDigitCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = 10000;\n\t\tthis.response.setContentLength(String.valueOf(expected).length() + 1);\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isFalse();\n\t\tthis.response.getWriter().write(1);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterWriteCharIntIntCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar[] buff = new char[0];\n\t\tint off = 2;\n\t\tint len = 3;\n\t\tthis.response.setContentLength(3);\n\t\tthis.response.getWriter().write(buff, off, len);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterWriteCharCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar[] buff = new char[4];\n\t\tthis.response.setContentLength(buff.length);\n\t\tthis.response.getWriter().write(buff);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterWriteStringIntIntCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString s = \"\";\n\t\tint off = 2;\n\t\tint len = 3;\n\t\tthis.response.setContentLength(3);\n\t\tthis.response.getWriter().write(s, off, len);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterWriteStringCommits() throws IOException {\n\t\tgivenGetWriterThenReturn();\n\t\tString body = \"something\";\n\t\tthis.response.setContentLength(body.length());\n\t\tthis.response.getWriter().write(body);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void printWriterWriteStringContentLengthCommits() throws IOException {\n\t\tgivenGetWriterThenReturn();\n\t\tString body = \"something\";\n\t\tthis.response.getWriter().write(body);\n\t\tthis.response.setContentLength(body.length());\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void printWriterWriteStringDoesNotCommit() throws IOException {\n\t\tgivenGetWriterThenReturn();\n\t\tString body = \"something\";\n\t\tthis.response.getWriter().write(body);\n\t\tassertThat(this.committed).isFalse();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintBooleanCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tboolean b = true;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getWriter().print(b);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintCharCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar c = 1;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getWriter().print(c);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintIntCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint i = 1234;\n\t\tthis.response.setContentLength(String.valueOf(i).length());\n\t\tthis.response.getWriter().print(i);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintLongCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tlong l = 12345;\n\t\tthis.response.setContentLength(String.valueOf(l).length());\n\t\tthis.response.getWriter().print(l);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintFloatCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tfloat f = 12345;\n\t\tthis.response.setContentLength(String.valueOf(f).length());\n\t\tthis.response.getWriter().print(f);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintDoubleCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tdouble x = 1.2345;\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getWriter().print(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintCharArrayCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar[] x = new char[10];\n\t\tthis.response.setContentLength(x.length);\n\t\tthis.response.getWriter().print(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintStringCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString x = \"12345\";\n\t\tthis.response.setContentLength(x.length());\n\t\tthis.response.getWriter().print(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintObjectCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tObject x = \"12345\";\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getWriter().print(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tthis.response.setContentLength(NL.length());\n\t\tthis.response.getWriter().println();\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnBooleanCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tboolean b = true;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getWriter().println(b);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnCharCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar c = 1;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getWriter().println(c);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnIntCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint i = 12345;\n\t\tthis.response.setContentLength(String.valueOf(i).length());\n\t\tthis.response.getWriter().println(i);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnLongCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tlong l = 12345678;\n\t\tthis.response.setContentLength(String.valueOf(l).length());\n\t\tthis.response.getWriter().println(l);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnFloatCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tfloat f = 1234;\n\t\tthis.response.setContentLength(String.valueOf(f).length());\n\t\tthis.response.getWriter().println(f);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnDoubleCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tdouble x = 1;\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getWriter().println(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnCharArrayCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar[] x = new char[20];\n\t\tthis.response.setContentLength(x.length);\n\t\tthis.response.getWriter().println(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnStringCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString x = \"1\";\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getWriter().println(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterPrintlnObjectCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tObject x = \"1\";\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getWriter().println(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterAppendCharSequenceCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString x = \"a\";\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getWriter().append(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterAppendCharSequenceIntIntCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString x = \"abcdef\";\n\t\tint start = 1;\n\t\tint end = 3;\n\t\tthis.response.setContentLength(end - start);\n\t\tthis.response.getWriter().append(x, start, end);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPrintWriterAppendCharCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tchar x = 1;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getWriter().append(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamWriteIntCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint expected = 1;\n\t\tthis.response.setContentLength(String.valueOf(expected).length());\n\t\tthis.response.getOutputStream().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamWriteIntMultiDigitCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint expected = 10000;\n\t\tthis.response.setContentLength(String.valueOf(expected).length());\n\t\tthis.response.getOutputStream().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthPlus1OutputStreamWriteIntMultiDigitCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint expected = 10000;\n\t\tthis.response.setContentLength(String.valueOf(expected).length() + 1);\n\t\tthis.response.getOutputStream().write(expected);\n\t\tassertThat(this.committed).isFalse();\n\t\tthis.response.getOutputStream().write(1);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t// gh-171\n\t@Test\n\tpublic void contentLengthPlus1OutputStreamWriteByteArrayMultiDigitCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tString expected = \"{\\n\" + \"  \\\"parameterName\\\" : \\\"_csrf\\\",\\n\"\n\t\t\t\t+ \"  \\\"token\\\" : \\\"06300b65-c4aa-4c8f-8cda-39ee17f545a0\\\",\\n\" + \"  \\\"headerName\\\" : \\\"X-CSRF-TOKEN\\\"\\n\"\n\t\t\t\t+ \"}\";\n\t\tthis.response.setContentLength(expected.length() + 1);\n\t\tthis.response.getOutputStream().write(expected.getBytes());\n\t\tassertThat(this.committed).isFalse();\n\t\tthis.response.getOutputStream().write(\"1\".getBytes(\"UTF-8\"));\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintBooleanCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tboolean b = true;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getOutputStream().print(b);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintCharCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tchar c = 1;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getOutputStream().print(c);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintIntCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint i = 1234;\n\t\tthis.response.setContentLength(String.valueOf(i).length());\n\t\tthis.response.getOutputStream().print(i);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintLongCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tlong l = 12345;\n\t\tthis.response.setContentLength(String.valueOf(l).length());\n\t\tthis.response.getOutputStream().print(l);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintFloatCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tfloat f = 12345;\n\t\tthis.response.setContentLength(String.valueOf(f).length());\n\t\tthis.response.getOutputStream().print(f);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintDoubleCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tdouble x = 1.2345;\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getOutputStream().print(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintStringCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tString x = \"12345\";\n\t\tthis.response.setContentLength(x.length());\n\t\tthis.response.getOutputStream().print(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintlnCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tthis.response.setContentLength(NL.length());\n\t\tthis.response.getOutputStream().println();\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintlnBooleanCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tboolean b = true;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getOutputStream().println(b);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintlnCharCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tchar c = 1;\n\t\tthis.response.setContentLength(1);\n\t\tthis.response.getOutputStream().println(c);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintlnIntCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tint i = 12345;\n\t\tthis.response.setContentLength(String.valueOf(i).length());\n\t\tthis.response.getOutputStream().println(i);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintlnLongCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tlong l = 12345678;\n\t\tthis.response.setContentLength(String.valueOf(l).length());\n\t\tthis.response.getOutputStream().println(l);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintlnFloatCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tfloat f = 1234;\n\t\tthis.response.setContentLength(String.valueOf(f).length());\n\t\tthis.response.getOutputStream().println(f);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintlnDoubleCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tdouble x = 1;\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getOutputStream().println(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamPrintlnStringCommits() throws Exception {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tString x = \"1\";\n\t\tthis.response.setContentLength(String.valueOf(x).length());\n\t\tthis.response.getOutputStream().println(x);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void contentLengthDoesNotCommit() {\n\t\tString body = \"something\";\n\t\tthis.response.setContentLength(body.length());\n\t\tassertThat(this.committed).isFalse();\n\t}\n\n\t@Test\n\tpublic void contentLengthOutputStreamWriteStringCommits() throws IOException {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tString body = \"something\";\n\t\tthis.response.setContentLength(body.length());\n\t\tthis.response.getOutputStream().print(body);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t// gh-7261\n\t@Test\n\tpublic void contentLengthLongOutputStreamWriteStringCommits() throws IOException {\n\t\tgivenGetOutputStreamThenReturn();\n\t\tString body = \"something\";\n\t\tthis.response.setContentLengthLong(body.length());\n\t\tthis.response.getOutputStream().print(body);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void addHeaderContentLengthPrintWriterWriteStringCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = 1234;\n\t\tthis.response.addHeader(\"Content-Length\", String.valueOf(String.valueOf(expected).length()));\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void addIntHeaderContentLengthPrintWriterWriteStringCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = 1234;\n\t\tthis.response.addIntHeader(\"Content-Length\", String.valueOf(expected).length());\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void setHeaderContentLengthPrintWriterWriteStringCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = 1234;\n\t\tthis.response.setHeader(\"Content-Length\", String.valueOf(String.valueOf(expected).length()));\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void setIntHeaderContentLengthPrintWriterWriteStringCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tint expected = 1234;\n\t\tthis.response.setIntHeader(\"Content-Length\", String.valueOf(expected).length());\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void bufferSizePrintWriterWriteCommits() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString expected = \"1234567890\";\n\t\tgiven(this.response.getBufferSize()).willReturn(expected.length());\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t}\n\n\t@Test\n\tpublic void bufferSizeCommitsOnce() throws Exception {\n\t\tgivenGetWriterThenReturn();\n\t\tString expected = \"1234567890\";\n\t\tgiven(this.response.getBufferSize()).willReturn(expected.length());\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isTrue();\n\t\tthis.committed = false;\n\t\tthis.response.getWriter().write(expected);\n\t\tassertThat(this.committed).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/TextEscapeUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\npublic class TextEscapeUtilsTests {\n\n\t/**\n\t * &amp;, &lt;, &gt;, &#34;, &#39 and&#32;(space) escaping\n\t */\n\t@Test\n\tpublic void charactersAreEscapedCorrectly() {\n\t\tassertThat(TextEscapeUtils.escapeEntities(\"& a<script>\\\"'\")).isEqualTo(\"&amp;&#32;a&lt;script&gt;&#34;&#39;\");\n\t}\n\n\t@Test\n\tpublic void nullOrEmptyStringIsHandled() {\n\t\tassertThat(TextEscapeUtils.escapeEntities(\"\")).isEqualTo(\"\");\n\t\tassertThat(TextEscapeUtils.escapeEntities(null)).isNull();\n\t}\n\n\t@Test\n\tpublic void invalidLowSurrogateIsDetected() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> TextEscapeUtils.escapeEntities(\"abc\\uDCCCdef\"));\n\t}\n\n\t@Test\n\tpublic void missingLowSurrogateIsDetected() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> TextEscapeUtils.escapeEntities(\"abc\\uD888a\"));\n\t}\n\n\t@Test\n\tpublic void highSurrogateAtEndOfStringIsRejected() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> TextEscapeUtils.escapeEntities(\"abc\\uD888\"));\n\t}\n\n\t/**\n\t * Delta char: &#66560;\n\t */\n\t@Test\n\tpublic void validSurrogatePairIsAccepted() {\n\t\tassertThat(TextEscapeUtils.escapeEntities(\"abc\\uD801\\uDC00a\")).isEqualTo(\"abc&#66560;a\");\n\t}\n\n\t@Test\n\tpublic void undefinedSurrogatePairIsIgnored() {\n\t\tassertThat(TextEscapeUtils.escapeEntities(\"abc\\uDBFF\\uDFFFa\")).isEqualTo(\"abca\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/ThrowableAnalyzerTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport java.lang.reflect.InvocationTargetException;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Test cases for {@link ThrowableAnalyzer}.\n *\n * @author Andreas Senft\n */\n@SuppressWarnings(\"unchecked\")\npublic class ThrowableAnalyzerTests {\n\n\t/**\n\t * An array of nested throwables for testing. The cause of element 0 is element 1, the\n\t * cause of element 1 is element 2 and so on.\n\t */\n\tprivate Throwable[] testTrace;\n\n\t/**\n\t * Plain <code>ThrowableAnalyzer</code>.\n\t */\n\tprivate ThrowableAnalyzer standardAnalyzer;\n\n\t/**\n\t * Enhanced <code>ThrowableAnalyzer</code> capable to process\n\t * <code>NonStandardException</code>s.\n\t */\n\tprivate ThrowableAnalyzer nonstandardAnalyzer;\n\n\t@BeforeEach\n\tpublic void setUp() {\n\t\t// Set up test trace\n\t\tthis.testTrace = new Throwable[7];\n\t\tthis.testTrace[6] = new IllegalArgumentException(\"Test_6\");\n\t\tthis.testTrace[5] = new Throwable(\"Test_5\", this.testTrace[6]);\n\t\tthis.testTrace[4] = new InvocationTargetException(this.testTrace[5], \"Test_4\");\n\t\tthis.testTrace[3] = new Exception(\"Test_3\", this.testTrace[4]);\n\t\tthis.testTrace[2] = new NonStandardException(\"Test_2\", this.testTrace[3]);\n\t\tthis.testTrace[1] = new RuntimeException(\"Test_1\", this.testTrace[2]);\n\t\tthis.testTrace[0] = new Exception(\"Test_0\", this.testTrace[1]);\n\t\t// Set up standard analyzer\n\t\tthis.standardAnalyzer = new ThrowableAnalyzer();\n\t\t// Set up nonstandard analyzer\n\t\tthis.nonstandardAnalyzer = new ThrowableAnalyzer() {\n\t\t\t/**\n\t\t\t * @see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()\n\t\t\t */\n\t\t\t@Override\n\t\t\tprotected void initExtractorMap() {\n\t\t\t\tsuper.initExtractorMap();\n\t\t\t\t// register extractor for NonStandardException\n\t\t\t\tregisterExtractor(NonStandardException.class, new NonStandardExceptionCauseExtractor());\n\t\t\t}\n\t\t};\n\t}\n\n\t@Test\n\tpublic void testRegisterExtractorWithInvalidExtractor() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new ThrowableAnalyzer() {\n\n\t\t\t@Override\n\t\t\tprotected void initExtractorMap() {\n\t\t\t\t// null is no valid extractor\n\t\t\t\tsuper.registerExtractor(Exception.class, null);\n\t\t\t}\n\n\t\t});\n\t}\n\n\t@Test\n\tpublic void testGetRegisteredTypes() {\n\t\tClass[] registeredTypes = this.nonstandardAnalyzer.getRegisteredTypes();\n\t\tfor (int i = 0; i < registeredTypes.length; ++i) {\n\t\t\tClass clazz = registeredTypes[i];\n\t\t\t// The most specific types have to occur first.\n\t\t\tfor (int j = 0; j < i; ++j) {\n\t\t\t\tClass prevClazz = registeredTypes[j];\n\t\t\t\tassertThat(prevClazz.isAssignableFrom(clazz))\n\t\t\t\t\t.withFailMessage(\n\t\t\t\t\t\t\t\"Unexpected order of registered classes: \" + prevClazz + \" is assignable from \" + clazz)\n\t\t\t\t\t.isFalse();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testDetermineCauseChainWithNoExtractors() {\n\t\tThrowableAnalyzer analyzer = new ThrowableAnalyzer() {\n\t\t\t/**\n\t\t\t * @see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()\n\t\t\t */\n\t\t\t@Override\n\t\t\tprotected void initExtractorMap() {\n\t\t\t\t// skip default initialization\n\t\t\t}\n\t\t};\n\t\tassertThat(analyzer.getRegisteredTypes().length).withFailMessage(\"Unexpected number of registered types\")\n\t\t\t.isZero();\n\t\tThrowable t = this.testTrace[0];\n\t\tThrowable[] chain = analyzer.determineCauseChain(t);\n\t\t// Without extractors only the root throwable is available\n\t\tassertThat(chain.length).as(\"Unexpected chain size\").isEqualTo(1);\n\t\tassertThat(chain[0]).as(\"Unexpected chain entry\").isEqualTo(t);\n\t}\n\n\t@Test\n\tpublic void testDetermineCauseChainWithDefaultExtractors() {\n\t\tThrowableAnalyzer analyzer = this.standardAnalyzer;\n\t\tassertThat(analyzer.getRegisteredTypes().length).withFailMessage(\"Unexpected number of registered types\")\n\t\t\t.isEqualTo(2);\n\t\tThrowable[] chain = analyzer.determineCauseChain(this.testTrace[0]);\n\t\t// Element at index 2 is a NonStandardException which cannot be analyzed further\n\t\t// by default\n\t\tassertThat(chain.length).as(\"Unexpected chain size\").isEqualTo(3);\n\t\tfor (int i = 0; i < 3; ++i) {\n\t\t\tassertThat(chain[i]).withFailMessage(\"Unexpected chain entry: \" + i).isEqualTo(this.testTrace[i]);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testDetermineCauseChainWithCustomExtractors() {\n\t\tThrowableAnalyzer analyzer = this.nonstandardAnalyzer;\n\t\tThrowable[] chain = analyzer.determineCauseChain(this.testTrace[0]);\n\t\tassertThat(chain.length).as(\"Unexpected chain size\").isEqualTo(this.testTrace.length);\n\t\tfor (int i = 0; i < chain.length; ++i) {\n\t\t\tassertThat(chain[i]).withFailMessage(\"Unexpected chain entry: \" + i).isEqualTo(this.testTrace[i]);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testGetFirstThrowableOfTypeWithSuccess1() {\n\t\tThrowableAnalyzer analyzer = this.nonstandardAnalyzer;\n\t\tThrowable[] chain = analyzer.determineCauseChain(this.testTrace[0]);\n\t\tThrowable result = analyzer.getFirstThrowableOfType(Exception.class, chain);\n\t\tassertThat(result).as(\"null not expected\").isNotNull();\n\t\tassertThat(result).as(\"Unexpected throwable found\").isEqualTo(this.testTrace[0]);\n\t}\n\n\t@Test\n\tpublic void testGetFirstThrowableOfTypeWithSuccess2() {\n\t\tThrowableAnalyzer analyzer = this.nonstandardAnalyzer;\n\t\tThrowable[] chain = analyzer.determineCauseChain(this.testTrace[0]);\n\t\tThrowable result = analyzer.getFirstThrowableOfType(NonStandardException.class, chain);\n\t\tassertThat(result).as(\"null not expected\").isNotNull();\n\t\tassertThat(result).as(\"Unexpected throwable found\").isEqualTo(this.testTrace[2]);\n\t}\n\n\t@Test\n\tpublic void testGetFirstThrowableOfTypeWithFailure() {\n\t\tThrowableAnalyzer analyzer = this.nonstandardAnalyzer;\n\t\tThrowable[] chain = analyzer.determineCauseChain(this.testTrace[0]);\n\t\t// IllegalStateException not in trace\n\t\tThrowable result = analyzer.getFirstThrowableOfType(IllegalStateException.class, chain);\n\t\tassertThat(result).as(\"null expected\").isNull();\n\t}\n\n\t@Test\n\tpublic void testVerifyThrowableHierarchyWithExactType() {\n\t\tThrowable throwable = new IllegalStateException(\"Test\");\n\t\tThrowableAnalyzer.verifyThrowableHierarchy(throwable, IllegalStateException.class);\n\t\t// No exception expected\n\t}\n\n\t@Test\n\tpublic void testVerifyThrowableHierarchyWithCompatibleType() {\n\t\tThrowable throwable = new IllegalStateException(\"Test\");\n\t\tThrowableAnalyzer.verifyThrowableHierarchy(throwable, Exception.class);\n\t\t// No exception expected\n\t}\n\n\t@Test\n\tpublic void testVerifyThrowableHierarchyWithNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> ThrowableAnalyzer.verifyThrowableHierarchy(null, Throwable.class));\n\t}\n\n\t@Test\n\tpublic void testVerifyThrowableHierarchyWithNonmatchingType() {\n\t\tThrowable throwable = new IllegalStateException(\"Test\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> ThrowableAnalyzer.verifyThrowableHierarchy(throwable, InvocationTargetException.class));\n\t}\n\n\t/**\n\t * Exception for testing purposes. The cause is not retrievable by\n\t * {@link #getCause()}.\n\t */\n\tpublic static final class NonStandardException extends Exception {\n\n\t\tprivate final Throwable cause;\n\n\t\tpublic NonStandardException(String message, Throwable cause) {\n\t\t\tsuper(message);\n\t\t\tthis.cause = cause;\n\t\t}\n\n\t\tpublic Throwable resolveCause() {\n\t\t\treturn this.cause;\n\t\t}\n\n\t}\n\n\t/**\n\t * <code>ThrowableCauseExtractor</code> for handling <code>NonStandardException</code>\n\t * instances.\n\t */\n\tpublic static final class NonStandardExceptionCauseExtractor implements ThrowableCauseExtractor {\n\n\t\t@Override\n\t\tpublic Throwable extractCause(Throwable throwable) {\n\t\t\tThrowableAnalyzer.verifyThrowableHierarchy(throwable, NonStandardException.class);\n\t\t\treturn ((NonStandardException) throwable).resolveCause();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/UrlUtilsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Luke Taylor\n */\npublic class UrlUtilsTests {\n\n\t@Test\n\tpublic void absoluteUrlsAreMatchedAsAbsolute() {\n\t\tassertThat(UrlUtils.isAbsoluteUrl(\"https://something/\")).isTrue();\n\t\tassertThat(UrlUtils.isAbsoluteUrl(\"http1://something/\")).isTrue();\n\t\tassertThat(UrlUtils.isAbsoluteUrl(\"HTTP://something/\")).isTrue();\n\t\tassertThat(UrlUtils.isAbsoluteUrl(\"https://something/\")).isTrue();\n\t\tassertThat(UrlUtils.isAbsoluteUrl(\"a://something/\")).isTrue();\n\t\tassertThat(UrlUtils.isAbsoluteUrl(\"zz+zz.zz-zz://something/\")).isTrue();\n\t}\n\n\t@Test\n\tpublic void isAbsoluteUrlWhenNullThenFalse() {\n\t\tassertThat(UrlUtils.isAbsoluteUrl(null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void isAbsoluteUrlWhenEmptyThenFalse() {\n\t\tassertThat(UrlUtils.isAbsoluteUrl(\"\")).isFalse();\n\t}\n\n\t@Test\n\tpublic void isValidRedirectUrlWhenNullThenFalse() {\n\t\tassertThat(UrlUtils.isValidRedirectUrl(null)).isFalse();\n\t}\n\n\t@Test\n\tpublic void isValidRedirectUrlWhenEmptyThenFalse() {\n\t\tassertThat(UrlUtils.isValidRedirectUrl(\"\")).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/AndRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNullPointerException;\nimport static org.mockito.BDDMockito.given;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class AndRequestMatcherTests {\n\n\t@Mock\n\tprivate RequestMatcher delegate;\n\n\t@Mock\n\tprivate RequestMatcher delegate2;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\tprivate RequestMatcher matcher;\n\n\t@Test\n\tpublic void constructorNullArray() {\n\t\tassertThatNullPointerException().isThrownBy(() -> new AndRequestMatcher((RequestMatcher[]) null));\n\t}\n\n\t@Test\n\tpublic void constructorListOfDoesNotThrowNullPointer() {\n\t\tnew AndRequestMatcher(List.of(pathPattern(\"/test\")));\n\t}\n\n\t@Test\n\tpublic void constructorArrayContainsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AndRequestMatcher((RequestMatcher) null));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyArray() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AndRequestMatcher(new RequestMatcher[0]));\n\t}\n\n\t@Test\n\tpublic void constructorNullList() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new AndRequestMatcher((List<RequestMatcher>) null));\n\t}\n\n\t@Test\n\tpublic void constructorListContainsNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AndRequestMatcher(Arrays.asList((RequestMatcher) null)));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyList() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new AndRequestMatcher(Collections.<RequestMatcher>emptyList()));\n\t}\n\n\t@Test\n\tpublic void matchesSingleTrue() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(true);\n\t\tthis.matcher = new AndRequestMatcher(this.delegate);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesMultiTrue() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(true);\n\t\tgiven(this.delegate2.matches(this.request)).willReturn(true);\n\t\tthis.matcher = new AndRequestMatcher(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesSingleFalse() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(false);\n\t\tthis.matcher = new AndRequestMatcher(this.delegate);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMultiBothFalse() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(false);\n\t\tthis.matcher = new AndRequestMatcher(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMultiSingleFalse() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(true);\n\t\tgiven(this.delegate2.matches(this.request)).willReturn(false);\n\t\tthis.matcher = new AndRequestMatcher(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matcherWhenMatchersHavePlaceholdersThenPropagatesMatches() {\n\t\tthis.matcher = new AndRequestMatcher(this.delegate, this.delegate2);\n\n\t\tgiven(this.delegate.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"value\")));\n\t\tgiven(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"othervalue\")));\n\t\tMatchResult result = this.matcher.matcher(this.request);\n\t\tassertThat(result.getVariables()).containsExactlyEntriesOf(Map.of(\"param\", \"othervalue\"));\n\n\t\tgiven(this.delegate.matcher(this.request)).willReturn(MatchResult.match());\n\t\tgiven(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"value\")));\n\t\tresult = this.matcher.matcher(this.request);\n\t\tassertThat(result.getVariables()).containsExactlyEntriesOf(Map.of(\"param\", \"value\"));\n\n\t\tgiven(this.delegate.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"value\")));\n\t\tgiven(this.delegate2.matcher(this.request)).willReturn(MatchResult.notMatch());\n\t\tresult = this.matcher.matcher(this.request);\n\t\tassertThat(result.getVariables()).isEmpty();\n\n\t\tgiven(this.delegate.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"otherparam\", \"value\")));\n\t\tgiven(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"value\")));\n\t\tresult = this.matcher.matcher(this.request);\n\t\tassertThat(result.getVariables())\n\t\t\t.containsExactlyInAnyOrderEntriesOf(Map.of(\"otherparam\", \"value\", \"param\", \"value\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/DispatcherTypeRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport jakarta.servlet.DispatcherType;\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Nick McKinney\n */\npublic class DispatcherTypeRequestMatcherTests {\n\n\t@Test\n\tpublic void matches_dispatcher_type() {\n\t\tHttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);\n\t\tDispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR);\n\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matches_dispatcher_type_and_http_method() {\n\t\tHttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);\n\t\tDispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, HttpMethod.GET);\n\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void does_not_match_wrong_type() {\n\t\tHttpServletRequest request = mockHttpServletRequest(DispatcherType.FORWARD, HttpMethod.GET);\n\t\tDispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR);\n\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void does_not_match_with_wrong_http_method() {\n\t\tHttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);\n\t\tDispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, HttpMethod.POST);\n\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void null_http_method_matches_any_http_method() {\n\t\tHttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.POST);\n\t\tDispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, null);\n\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\tprivate HttpServletRequest mockHttpServletRequest(DispatcherType dispatcherType, HttpMethod httpMethod) {\n\t\tMockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();\n\t\tmockHttpServletRequest.setDispatcherType(dispatcherType);\n\t\tmockHttpServletRequest.setMethod(httpMethod.name());\n\t\treturn mockHttpServletRequest;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/ELRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Mike Wiesner\n * @since 3.0.2\n */\npublic class ELRequestMatcherTests {\n\n\t@Test\n\tpublic void testHasIpAddressTrue() {\n\t\tELRequestMatcher requestMatcher = new ELRequestMatcher(\"hasIpAddress('1.1.1.1')\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRemoteAddr(\"1.1.1.1\");\n\t\tassertThat(requestMatcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testHasIpAddressFalse() {\n\t\tELRequestMatcher requestMatcher = new ELRequestMatcher(\"hasIpAddress('1.1.1.1')\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.setRemoteAddr(\"1.1.1.2\");\n\t\tassertThat(requestMatcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testHasHeaderTrue() {\n\t\tELRequestMatcher requestMatcher = new ELRequestMatcher(\"hasHeader('User-Agent','MSIE')\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"User-Agent\", \"MSIE\");\n\t\tassertThat(requestMatcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testHasHeaderTwoEntries() {\n\t\tELRequestMatcher requestMatcher = new ELRequestMatcher(\n\t\t\t\t\"hasHeader('User-Agent','MSIE') or hasHeader('User-Agent','Mozilla')\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"User-Agent\", \"MSIE\");\n\t\tassertThat(requestMatcher.matches(request)).isTrue();\n\t\trequest = new MockHttpServletRequest();\n\t\trequest.addHeader(\"User-Agent\", \"Mozilla\");\n\t\tassertThat(requestMatcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void testHasHeaderFalse() {\n\t\tELRequestMatcher requestMatcher = new ELRequestMatcher(\"hasHeader('User-Agent','MSIE')\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\trequest.addHeader(\"User-Agent\", \"wrong\");\n\t\tassertThat(requestMatcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void testHasHeaderNull() {\n\t\tELRequestMatcher requestMatcher = new ELRequestMatcher(\"hasHeader('User-Agent','MSIE')\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest();\n\t\tassertThat(requestMatcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void toStringThenFormatted() {\n\t\tELRequestMatcher requestMatcher = new ELRequestMatcher(\"hasHeader('User-Agent','MSIE')\");\n\t\tassertThat(requestMatcher.toString()).isEqualTo(\"EL [el=\\\"hasHeader('User-Agent','MSIE')\\\"]\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/InetAddressMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link InetAddressMatcher}.\n *\n * @author Rob Winch\n */\nclass InetAddressMatcherTests {\n\n\t@Test\n\tvoid matchesWhenStringValidIpv4ThenReturnsTrue() {\n\t\tInetAddressMatcher matcher = (address) -> address.getHostAddress().equals(\"192.168.1.1\");\n\t\tassertThat(matcher.matches(\"192.168.1.1\")).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenStringValidIpv6ThenReturnsTrue() {\n\t\tInetAddressMatcher matcher = (address) -> address.getHostAddress().equals(\"fe80:0:0:0:21f:5bff:fe33:bd68\");\n\t\tassertThat(matcher.matches(\"fe80::21f:5bff:fe33:bd68\")).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenStringNullThenReturnsFalse() {\n\t\tInetAddressMatcher matcher = (address) -> true;\n\t\tassertThat(matcher.matches((String) null)).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenStringInvalidThenThrowsIllegalArgumentException() {\n\t\tInetAddressMatcher matcher = (address) -> true;\n\t\tassertThat(matcher.matches(\"192.168.1.1\")).isTrue();\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> matcher.matches(\"not.an.ip.address\"));\n\t}\n\n\t@Test\n\tvoid matchesWhenStringMatchesPredicateThenReturnsTrue() {\n\t\tInetAddressMatcher matcher = (address) -> address.getHostAddress().startsWith(\"192.168\");\n\t\tassertThat(matcher.matches(\"192.168.1.1\")).isTrue();\n\t\tassertThat(matcher.matches(\"192.168.100.200\")).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenStringDoesNotMatchPredicateThenReturnsFalse() {\n\t\tInetAddressMatcher matcher = (address) -> address.getHostAddress().startsWith(\"192.168\");\n\t\tassertThat(matcher.matches(\"10.0.0.1\")).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/InetAddressMatchersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.net.InetAddress;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link InetAddressMatchers}.\n *\n * @author Rob Winch\n */\nclass InetAddressMatchersTests {\n\n\t@Test\n\tvoid builderWhenInvokedThenReturnsBuilder() {\n\t\tassertThat(InetAddressMatchers.builder()).isNotNull();\n\t}\n\n\t@Test\n\tvoid matchExternalWhenInvokedThenReturnsBuilder() {\n\t\tInetAddressMatchers.Builder builder = InetAddressMatchers.matchExternal();\n\t\tassertThat(builder).isNotNull();\n\t}\n\n\t@Test\n\tvoid matchInternalWhenInvokedThenReturnsBuilder() {\n\t\tInetAddressMatchers.Builder builder = InetAddressMatchers.matchInternal();\n\t\tassertThat(builder).isNotNull();\n\t}\n\n\t@Nested\n\tclass BuilderTests {\n\n\t\t@Test\n\t\tvoid includeAddressesWhenNullThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException().isThrownBy(() -> InetAddressMatchers.builder().includeAddresses(null))\n\t\t\t\t.withMessage(\"addresses cannot be empty\");\n\t\t}\n\n\t\t@Test\n\t\tvoid includeAddressesWhenEmptyListThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> InetAddressMatchers.builder().includeAddresses(List.of()))\n\t\t\t\t.withMessage(\"addresses cannot be empty\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"192.168.1.2\" })\n\t\tvoid includeAddressesWhenSingleAddressThenMatchesOnlyThatAddress(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder().includeAddresses(List.of(\"192.168.1.1\")).build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = testAddress.equals(\"192.168.1.1\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"10.0.0.1\", \"8.8.8.8\" })\n\t\tvoid includeAddressesWhenMultipleAddressesThenMatchesAny(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.includeAddresses(List.of(\"192.168.1.1\", \"10.0.0.1\"))\n\t\t\t\t.build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = testAddress.equals(\"192.168.1.1\") || testAddress.equals(\"10.0.0.1\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"192.168.1.255\", \"192.168.2.1\" })\n\t\tvoid includeAddressesWhenCidrNotationThenMatchesSubnet(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.includeAddresses(List.of(\"192.168.1.0/24\"))\n\t\t\t\t.build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = testAddress.startsWith(\"192.168.1.\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t\t@Test\n\t\tvoid excludeAddressesWhenNullThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException().isThrownBy(() -> InetAddressMatchers.builder().excludeAddresses(null))\n\t\t\t\t.withMessage(\"addresses cannot be empty\");\n\t\t}\n\n\t\t@Test\n\t\tvoid excludeAddressesWhenEmptyListThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> InetAddressMatchers.builder().excludeAddresses(List.of()))\n\t\t\t\t.withMessage(\"addresses cannot be empty\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"192.168.1.2\" })\n\t\tvoid excludeAddressesWhenSingleAddressThenBlocksOnlyThatAddress(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder().excludeAddresses(List.of(\"192.168.1.1\")).build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = !testAddress.equals(\"192.168.1.1\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"10.0.0.1\", \"8.8.8.8\" })\n\t\tvoid excludeAddressesWhenMultipleAddressesThenBlocksAll(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.excludeAddresses(List.of(\"192.168.1.1\", \"10.0.0.1\"))\n\t\t\t\t.build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = !testAddress.equals(\"192.168.1.1\") && !testAddress.equals(\"10.0.0.1\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"192.168.1.255\", \"192.168.2.1\" })\n\t\tvoid excludeAddressesWhenCidrNotationThenBlocksSubnet(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.excludeAddresses(List.of(\"192.168.1.0/24\"))\n\t\t\t\t.build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = !testAddress.startsWith(\"192.168.1.\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"10.0.0.1\", \"192.168.1.1\" })\n\t\tvoid matchAllWhenVarargsThenAddsMatchersToChain(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher customMatcher = (address) -> address.getHostAddress().startsWith(\"10.\");\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder().matchAll(customMatcher).build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = testAddress.startsWith(\"10.\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t\t@Test\n\t\tvoid matchAllWhenNullVarargsThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> InetAddressMatchers.builder().matchAll((InetAddressMatcher[]) null))\n\t\t\t\t.withMessage(\"matchers cannot be empty\");\n\t\t}\n\n\t\t@Test\n\t\tvoid matchAllWhenEmptyVarargsThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> InetAddressMatchers.builder().matchAll(new InetAddressMatcher[0]))\n\t\t\t\t.withMessage(\"matchers cannot be empty\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"10.0.0.1\", \"10.0.0.2\", \"192.168.1.1\" })\n\t\tvoid matchAllWhenMultipleMatchersThenAppliesAndLogic(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher startsWithTen = (address) -> address.getHostAddress().startsWith(\"10.\");\n\t\t\tInetAddressMatcher endsWithOne = (address) -> address.getHostAddress().endsWith(\".1\");\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder().matchAll(startsWithTen, endsWithOne).build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = testAddress.startsWith(\"10.\") && testAddress.endsWith(\".1\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"8.8.8.8\" })\n\t\tvoid reportOnlyWhenSetThenAllowsAllAddresses(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.excludeAddresses(List.of(\"192.168.1.1\"))\n\t\t\t\t.reportOnly()\n\t\t\t\t.build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tassertThat(matcher.matches(address)).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"192.168.1.100\", \"192.168.2.1\" })\n\t\tvoid buildWhenMultipleMatchersThenAppliesAndLogic(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.includeAddresses(List.of(\"192.168.1.0/24\"))\n\t\t\t\t.excludeAddresses(List.of(\"192.168.1.100\"))\n\t\t\t\t.build();\n\t\t\tInetAddress address = InetAddress.getByName(testAddress);\n\t\t\tboolean expected = testAddress.startsWith(\"192.168.1.\") && !testAddress.equals(\"192.168.1.100\");\n\t\t\tassertThat(matcher.matches(address)).isEqualTo(expected);\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass IncludeListInetAddressMatcherTests {\n\n\t\t@Test\n\t\tvoid constructorWhenNullListThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new InetAddressMatchers.IncludeListInetAddressMatcher(null))\n\t\t\t\t.withMessage(\"includeList cannot be null or empty\");\n\t\t}\n\n\t\t@Test\n\t\tvoid constructorWhenEmptyListThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new InetAddressMatchers.IncludeListInetAddressMatcher(List.of()))\n\t\t\t\t.withMessage(\"includeList cannot be null or empty\");\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenAddressInListThenReturnsTrue() throws Exception {\n\t\t\tString addressString = \"192.168.1.1\";\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder().includeAddresses(List.of(addressString)).build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(addressString))).isTrue();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenAddressNotInListThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder().includeAddresses(List.of(\"192.168.1.1\")).build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.2\"))).isFalse();\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ExcludeListInetAddressMatcherTests {\n\n\t\t@Test\n\t\tvoid constructorWhenNullListThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new InetAddressMatchers.ExcludeListInetAddressMatcher(null))\n\t\t\t\t.withMessage(\"excludeList cannot be null or empty\");\n\t\t}\n\n\t\t@Test\n\t\tvoid constructorWhenEmptyListThenThrowsIllegalArgumentException() {\n\t\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new InetAddressMatchers.ExcludeListInetAddressMatcher(List.of()))\n\t\t\t\t.withMessage(\"excludeList cannot be null or empty\");\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenAddressInListThenReturnsFalse() throws Exception {\n\t\t\tString addressString = \"192.168.1.1\";\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder().excludeAddresses(List.of(addressString)).build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(addressString))).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenAddressNotInListThenReturnsTrue() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder().excludeAddresses(List.of(\"192.168.1.1\")).build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.2\"))).isTrue();\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass InternalInetAddressMatcherTests {\n\n\t\t@Test\n\t\tvoid matchesWhenInetAddressNullThenReturnsFalse() {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches((InetAddress) null)).isFalse();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"127.0.0.1\", \"127.0.0.255\" })\n\t\tvoid matchesWhenIpv4LoopbackThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv6LoopbackThenReturnsTrue() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"::1\"))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"10.0.0.1\", \"10.255.255.255\" })\n\t\tvoid matchesWhenIpv4PrivateClass10ThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.0.1\", \"192.168.255.255\" })\n\t\tvoid matchesWhenIpv4PrivateClass192ThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"169.254.0.0\", \"169.254.169.254\", \"169.254.255.255\" })\n\t\tvoid matchesWhenIpv4LinkLocalThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"172.16.0.1\", \"172.16.255.255\", \"172.17.1.1\", \"172.31.255.255\" })\n\t\tvoid matchesWhenIpv4PrivateClass172ThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"::ffff:192.168.1.1\", \"::ffff:169.254.169.254\", \"::ffff:10.0.0.1\" })\n\t\tvoid matchesWhenIpv4MappedIpv6InternalThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"fc00::1\", \"fd00::1\" })\n\t\tvoid matchesWhenIpv6UniqueLocalThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(\n\t\t\t\tstrings = { \"64:ff9b::10.0.0.1\", \"64:ff9b::127.0.0.1\", \"64:ff9b::192.168.1.1\", \"64:ff9b::172.16.0.1\" })\n\t\tvoid matchesWhenIpv6TranslationWithInternalIpv4ThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"64:ff9b::192.0.2.1\", \"64:ff9b::192.167.1.1\" })\n\t\tvoid matchesWhenIpv6TranslationWithIpv4StartsWith192ButNot168ThenReturnsFalse(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isFalse();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"64:ff9b::172.16.0.1\", \"64:ff9b::172.16.255.255\" })\n\t\tvoid matchesWhenIpv6TranslationWithIpv4StartsWith172And16ThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"64:ff9b::8.8.8.8\", \"64:ff9b::1.1.1.1\" })\n\t\tvoid matchesWhenIpv6TranslationWithExternalIpv4ThenReturnsFalse(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv6NonTranslationPrefixByte0ThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"65:ff9b::10.0.0.1\"))).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv6NonTranslationPrefixByte1ThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"64:fe9b::10.0.0.1\"))).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv6NonTranslationPrefixByte2ThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"64:ff9a::10.0.0.1\"))).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv6NonTranslationPrefixByte3ThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"64:ff9c::10.0.0.1\"))).isFalse();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"8.8.8.8\", \"1.1.1.1\" })\n\t\tvoid matchesWhenIpv4PublicThenReturnsFalse(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isFalse();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.0.2.1\", \"192.167.1.1\", \"192.169.1.1\" })\n\t\tvoid matchesWhenIpv4StartsWith192ButNot168ThenReturnsFalse(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isFalse();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"172.15.1.1\", \"172.32.1.1\" })\n\t\tvoid matchesWhenIpv4StartsWith172ButNotPrivate16To31ThenReturnsFalse(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv6PublicThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"2001:4860:4860::8888\"))).isFalse();\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ExternalInetAddressMatcherTests {\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"8.8.8.8\", \"1.1.1.1\" })\n\t\tvoid matchesWhenIpv4PublicThenReturnsTrue(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchExternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isTrue();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv6PublicThenReturnsTrue() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchExternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"2001:4860:4860::8888\"))).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"10.0.0.1\", \"172.16.0.1\" })\n\t\tvoid matchesWhenIpv4PrivateThenReturnsFalse(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchExternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv4LoopbackThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchExternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"127.0.0.1\"))).isFalse();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"169.254.0.0\", \"169.254.169.254\", \"169.254.255.255\" })\n\t\tvoid matchesWhenIpv4LinkLocalThenReturnsFalse(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchExternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenIpv6LoopbackThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchExternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"::1\"))).isFalse();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"fc00::1\", \"fd00::1\" })\n\t\tvoid matchesWhenIpv6UniqueLocalThenReturnsFalse(String address) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.matchExternal().build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(address))).isFalse();\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass CompositeInetAddressMatcherTests {\n\n\t\t@Test\n\t\tvoid matchesWhenAllMatchersTrueThenReturnsTrue() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.includeAddresses(List.of(\"192.168.1.0/24\"))\n\t\t\t\t.matchAll((address) -> address.getHostAddress().endsWith(\".1\"))\n\t\t\t\t.build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.1\"))).isTrue();\n\t\t}\n\n\t\t@Test\n\t\tvoid matchesWhenOneMatcherFalseThenReturnsFalse() throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.includeAddresses(List.of(\"192.168.1.0/24\"))\n\t\t\t\t.matchAll((address) -> address.getHostAddress().endsWith(\".1\"))\n\t\t\t\t.build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.2\"))).isFalse();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"192.168.1.1\", \"8.8.8.8\" })\n\t\tvoid matchesWhenReportOnlyThenAlwaysReturnsTrue(String testAddress) throws Exception {\n\t\t\tInetAddressMatcher matcher = InetAddressMatchers.builder()\n\t\t\t\t.excludeAddresses(List.of(\"192.168.1.1\"))\n\t\t\t\t.reportOnly()\n\t\t\t\t.build();\n\t\t\tassertThat(matcher.matches(InetAddress.getByName(testAddress))).isTrue();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/IpAddressMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatException;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Luke Taylor\n * @author Andrey Litvitski\n */\npublic class IpAddressMatcherTests {\n\n\tfinal IpAddressMatcher v6matcher = new IpAddressMatcher(\"fe80::21f:5bff:fe33:bd68\");\n\n\tfinal IpAddressMatcher v4matcher = new IpAddressMatcher(\"192.168.1.104\");\n\n\tMockHttpServletRequest ipv4Request = new MockHttpServletRequest();\n\n\tMockHttpServletRequest ipv6Request = new MockHttpServletRequest();\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.ipv6Request.setRemoteAddr(\"fe80::21f:5bff:fe33:bd68\");\n\t\tthis.ipv4Request.setRemoteAddr(\"192.168.1.104\");\n\t}\n\n\t@Test\n\tpublic void ipv6MatcherMatchesIpv6Address() {\n\t\tassertThat(this.v6matcher.matches(this.ipv6Request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void ipv6MatcherDoesntMatchIpv4Address() {\n\t\tassertThat(this.v6matcher.matches(this.ipv4Request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void ipv4MatcherMatchesIpv4Address() {\n\t\tassertThat(this.v4matcher.matches(this.ipv4Request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void ipv4SubnetMatchesCorrectly() {\n\t\tIpAddressMatcher matcher = new IpAddressMatcher(\"192.168.1.0/24\");\n\t\tassertThat(matcher.matches(this.ipv4Request)).isTrue();\n\t\tmatcher = new IpAddressMatcher(\"192.168.1.128/25\");\n\t\tassertThat(matcher.matches(this.ipv4Request)).isFalse();\n\t\tthis.ipv4Request.setRemoteAddr(\"192.168.1.159\"); // 159 = 0x9f\n\t\tassertThat(matcher.matches(this.ipv4Request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void ipv6RangeMatches() {\n\t\tIpAddressMatcher matcher = new IpAddressMatcher(\"2001:DB8::/48\");\n\t\tassertThat(matcher.matches(\"2001:DB8:0:0:0:0:0:0\")).isTrue();\n\t\tassertThat(matcher.matches(\"2001:DB8:0:0:0:0:0:1\")).isTrue();\n\t\tassertThat(matcher.matches(\"2001:DB8:0:FFFF:FFFF:FFFF:FFFF:FFFF\")).isTrue();\n\t\tassertThat(matcher.matches(\"2001:DB8:1:0:0:0:0:0\")).isFalse();\n\t}\n\n\t// SEC-1733\n\t@Test\n\tpublic void zeroMaskMatchesAnything() {\n\t\tIpAddressMatcher matcher = new IpAddressMatcher(\"0.0.0.0/0\");\n\t\tassertThat(matcher.matches(\"123.4.5.6\")).isTrue();\n\t\tassertThat(matcher.matches(\"192.168.0.159\")).isTrue();\n\t\tmatcher = new IpAddressMatcher(\"192.168.0.159/0\");\n\t\tassertThat(matcher.matches(\"123.4.5.6\")).isTrue();\n\t\tassertThat(matcher.matches(\"192.168.0.159\")).isTrue();\n\t}\n\n\t// SEC-2576\n\t@Test\n\tpublic void ipv4RequiredAddressMaskTooLongThenIllegalArgumentException() {\n\t\tString ipv4AddressWithTooLongMask = \"192.168.1.104/33\";\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpAddressMatcher(ipv4AddressWithTooLongMask))\n\t\t\t.withMessage(String.format(\"IP address %s is too short for bitmask of length %d\", \"192.168.1.104\", 33));\n\t}\n\n\t// SEC-2576\n\t@Test\n\tpublic void ipv6RequiredAddressMaskTooLongThenIllegalArgumentException() {\n\t\tString ipv6AddressWithTooLongMask = \"fe80::21f:5bff:fe33:bd68/129\";\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpAddressMatcher(ipv6AddressWithTooLongMask))\n\t\t\t.withMessage(String.format(\"IP address %s is too short for bitmask of length %d\",\n\t\t\t\t\t\"fe80::21f:5bff:fe33:bd68\", 129));\n\t}\n\n\t@Test\n\tpublic void invalidAddressThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpAddressMatcher(\"invalid-ip\"))\n\t\t\t.withMessage(\"ipAddress invalid-ip doesn't look like an IP Address. Is it a host name?\");\n\t}\n\n\t// gh-15172\n\t@Test\n\tpublic void hexadecimalDomainNameThenIllegalArgumentException() {\n\t\tassertThatException().isThrownBy(() -> new IpAddressMatcher(\"deadbeef.abc\"))\n\t\t\t.withMessage(\"ipAddress deadbeef.abc doesn't look like an IP Address. Is it a host name?\");\n\t}\n\n\t// gh-15172\n\t@Test\n\tpublic void numericDomainNameThenIllegalArgumentException() {\n\t\tassertThatException().isThrownBy(() -> new IpAddressMatcher(\"123.156.7.18.org\"))\n\t\t\t.withMessage(\"ipAddress 123.156.7.18.org doesn't look like an IP Address. Is it a host name?\");\n\t}\n\n\t// gh-15527\n\t@Test\n\tpublic void matchesWhenIpAddressIsLoopbackAndAddressIsNullThenFalse() {\n\t\tIpAddressMatcher ipAddressMatcher = new IpAddressMatcher(\"127.0.0.1\");\n\t\tassertThat(ipAddressMatcher.matches((String) null)).isFalse();\n\t}\n\n\t// gh-15527\n\t@Test\n\tpublic void matchesWhenAddressIsNullThenFalse() {\n\t\tassertThat(this.v4matcher.matches((String) null)).isFalse();\n\t}\n\n\t// gh-15527\n\t@Test\n\tpublic void constructorWhenRequiredAddressIsNullThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpAddressMatcher(null))\n\t\t\t.withMessage(\"ipAddress cannot be empty\");\n\t}\n\n\t// gh-15527\n\t@Test\n\tpublic void constructorWhenRequiredAddressIsEmptyThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpAddressMatcher(\"\"))\n\t\t\t.withMessage(\"ipAddress cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void isIpAddressWhenEmptyOrBlankThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpAddressMatcher(\"   \"))\n\t\t\t.withMessage(\"ipAddress cannot be empty\");\n\t}\n\n\t// gh-16795\n\t@Test\n\tpublic void toStringWhenCidrIsProvidedThenReturnsIpAddressWithCidr() {\n\t\tIpAddressMatcher matcher = new IpAddressMatcher(\"192.168.1.0/24\");\n\t\tassertThat(matcher.toString()).hasToString(\"IpAddress [192.168.1.0/24]\");\n\t}\n\n\t// gh-16795\n\t@Test\n\tpublic void toStringWhenOnlyIpIsProvidedThenReturnsIpAddressOnly() {\n\t\tIpAddressMatcher matcher = new IpAddressMatcher(\"127.0.0.1\");\n\t\tassertThat(matcher.toString()).hasToString(\"IpAddress [127.0.0.1]\");\n\t}\n\n\t// gh-17499\n\t@Test\n\tpublic void constructorRejectsInvalidIpv4WithX() {\n\t\tString badIp = \"10x1x1x1\";\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpAddressMatcher(badIp))\n\t\t\t.withMessage(\"ipAddress 10x1x1x1 doesn't look like an IP Address. Is it a host name?\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/IpInetAddressMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.net.InetAddress;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link IpInetAddressMatcher}.\n *\n * @author Rob Winch\n */\nclass IpInetAddressMatcherTests {\n\n\t@Test\n\tvoid constructorWhenNullIpAddressThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpInetAddressMatcher(null))\n\t\t\t.withMessage(\"ipAddress cannot be empty\");\n\t}\n\n\t@Test\n\tvoid constructorWhenEmptyIpAddressThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpInetAddressMatcher(\"\"))\n\t\t\t.withMessage(\"ipAddress cannot be empty\");\n\t}\n\n\t@Test\n\tvoid constructorWhenHostnameThenThrowsIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new IpInetAddressMatcher(\"example.com\"))\n\t\t\t.withMessageContaining(\"doesn't look like an IP Address\");\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv4ExactMatchThenReturnsTrue() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.1\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.1\"))).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv4NoMatchThenReturnsFalse() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.1\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.2\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv6ExactMatchThenReturnsTrue() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"fe80::21f:5bff:fe33:bd68\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"fe80::21f:5bff:fe33:bd68\"))).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv6NoMatchThenReturnsFalse() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"fe80::21f:5bff:fe33:bd68\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"fe80::21f:5bff:fe33:bd69\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv4WithCidrMatchesSubnetThenReturnsTrue() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.0/24\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.1\"))).isTrue();\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.255\"))).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv4WithCidrOutsideSubnetThenReturnsFalse() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.0/24\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.2.1\"))).isFalse();\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.0.255\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv6WithCidrMatchesSubnetThenReturnsTrue() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"2001:db8::/48\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"2001:db8:0:0:0:0:0:0\"))).isTrue();\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"2001:db8:0:ffff:ffff:ffff:ffff:ffff\"))).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv6WithCidrOutsideSubnetThenReturnsFalse() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"2001:db8::/48\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"2001:db8:1:0:0:0:0:0\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv4AndIpv6AddressThenReturnsFalse() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.1\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"fe80::21f:5bff:fe33:bd68\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenIpv6AndIpv4AddressThenReturnsFalse() throws Exception {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"fe80::21f:5bff:fe33:bd68\");\n\t\tassertThat(matcher.matches(InetAddress.getByName(\"192.168.1.1\"))).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenStringIpv4MatchThenReturnsTrue() {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.1\");\n\t\tassertThat(matcher.matches(\"192.168.1.1\")).isTrue();\n\t}\n\n\t@Test\n\tvoid matchesWhenStringIpv4NoMatchThenReturnsFalse() {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.1\");\n\t\tassertThat(matcher.matches(\"192.168.1.2\")).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenStringNullThenReturnsFalse() {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.1\");\n\t\tassertThat(matcher.matches((String) null)).isFalse();\n\t}\n\n\t@Test\n\tvoid matchesWhenInetAddressNullThenReturnsFalse() {\n\t\tIpInetAddressMatcher matcher = new IpInetAddressMatcher(\"192.168.1.1\");\n\t\tassertThat(matcher.matches((InetAddress) null)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcherRequestHCNSTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.accept.HeaderContentNegotiationStrategy;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Verify how integrates with {@link HeaderContentNegotiationStrategy}.\n *\n * @author Rob Winch\n *\n */\npublic class MediaTypeRequestMatcherRequestHCNSTests {\n\n\tprivate MediaTypeRequestMatcher matcher;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate ContentNegotiationStrategy negotiationStrategy;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.negotiationStrategy = new HeaderContentNegotiationStrategy();\n\t\tthis.request = new MockHttpServletRequest();\n\t}\n\n\t@Test\n\tpublic void mediaAllMatches() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE);\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_HTML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.APPLICATION_XHTML_XML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t// ignoreMediaTypeAll\n\t@Test\n\tpublic void mediaAllIgnoreMediaTypeAll() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE);\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_HTML);\n\t\tthis.matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void mediaAllAndTextHtmlIgnoreMediaTypeAll() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE + \",\" + MediaType.TEXT_HTML_VALUE);\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_HTML);\n\t\tthis.matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t// JavaDoc\n\t@Test\n\tpublic void javadocJsonJson() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.APPLICATION_JSON_VALUE);\n\t\tMediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(this.negotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tassertThat(matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void javadocAllJson() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE);\n\t\tMediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(this.negotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tassertThat(matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void javadocAllJsonIgnoreAll() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE);\n\t\tMediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(this.negotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tmatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void javadocJsonJsonIgnoreAll() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.APPLICATION_JSON_VALUE);\n\t\tMediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(this.negotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tmatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void javadocJsonJsonUseEquals() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.APPLICATION_JSON_VALUE);\n\t\tMediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(this.negotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tmatcher.setUseEquals(true);\n\t\tassertThat(matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void javadocAllJsonUseEquals() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE);\n\t\tMediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(this.negotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tmatcher.setUseEquals(true);\n\t\tassertThat(matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void javadocApplicationAllJsonUseEquals() {\n\t\tthis.request.addHeader(\"Accept\", new MediaType(\"application\", \"*\"));\n\t\tMediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(this.negotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tmatcher.setUseEquals(true);\n\t\tassertThat(matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void javadocAllJsonUseFalse() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE);\n\t\tMediaTypeRequestMatcher matcher = new MediaTypeRequestMatcher(this.negotiationStrategy,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tmatcher.setUseEquals(true);\n\t\tassertThat(matcher.matches(this.request)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.web.HttpMediaTypeNotAcceptableException;\nimport org.springframework.web.accept.ContentNegotiationStrategy;\nimport org.springframework.web.context.request.NativeWebRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n * @author Dan Zheng\n */\n@ExtendWith(MockitoExtension.class)\npublic class MediaTypeRequestMatcherTests {\n\n\tprivate MediaTypeRequestMatcher matcher;\n\n\tprivate MockHttpServletRequest request;\n\n\t@Mock\n\tprivate ContentNegotiationStrategy negotiationStrategy;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t}\n\n\t@Test\n\tpublic void constructorWhenNullCNSThenIAE() {\n\t\tContentNegotiationStrategy c = null;\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MediaTypeRequestMatcher(c, MediaType.ALL));\n\t}\n\n\t@Test\n\tpublic void constructorNullCNSSet() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new MediaTypeRequestMatcher(null, Collections.singleton(MediaType.ALL)));\n\t}\n\n\t@Test\n\tpublic void constructorNoVarargs() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new MediaTypeRequestMatcher(this.negotiationStrategy));\n\t}\n\n\t@Test\n\tpublic void constructorNullMediaTypes() {\n\t\tCollection<MediaType> mediaTypes = null;\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new MediaTypeRequestMatcher(this.negotiationStrategy, mediaTypes));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyMediaTypes() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new MediaTypeRequestMatcher(this.negotiationStrategy, Collections.<MediaType>emptyList()));\n\t}\n\n\t@Test\n\tpublic void constructorWhenEmptyMediaTypeThenIAE() {\n\t\tassertThatIllegalArgumentException().isThrownBy(MediaTypeRequestMatcher::new);\n\t}\n\n\t@Test\n\tpublic void constructorWhenEmptyMediaTypeCollectionThenIAE() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new MediaTypeRequestMatcher(Collections.<MediaType>emptyList()));\n\t}\n\n\t@Test\n\tpublic void negotiationStrategyThrowsHMTNAE() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willThrow(new HttpMediaTypeNotAcceptableException(\"oops\"));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.ALL);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void mediaAllMatches() throws Exception {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(MediaType.ALL));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_HTML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.APPLICATION_XHTML_XML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenAcceptHeaderAsteriskThenAll() {\n\t\tthis.request.addHeader(\"Accept\", \"*/*\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.ALL);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenAcceptHeaderAsteriskThenAnyone() {\n\t\tthis.request.addHeader(\"Accept\", \"*/*\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_HTML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenAcceptHeaderAsteriskThenAllInCollection() {\n\t\tthis.request.addHeader(\"Accept\", \"*/*\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenAcceptHeaderAsteriskThenAnyoneInCollection() {\n\t\tthis.request.addHeader(\"Accept\", \"*/*\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(Collections.singleton(MediaType.TEXT_HTML));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenNoAcceptHeaderThenAll() {\n\t\tthis.request.removeHeader(\"Accept\");\n\t\t// if not set Accept, it is match all\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.ALL);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenNoAcceptHeaderThenAnyone() {\n\t\tthis.request.removeHeader(\"Accept\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_HTML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenSingleAcceptHeaderThenOne() {\n\t\tthis.request.addHeader(\"Accept\", \"text/html\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_HTML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenSingleAcceptHeaderThenOneWithCollection() {\n\t\tthis.request.addHeader(\"Accept\", \"text/html\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(Collections.singleton(MediaType.TEXT_HTML));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenMultipleAcceptHeaderThenMatchMultiple() {\n\t\tthis.request.addHeader(\"Accept\", \"text/html, application/xhtml+xml, application/xml;q=0.9\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML,\n\t\t\t\tMediaType.APPLICATION_XML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenMultipleAcceptHeaderThenAnyoneInCollection() {\n\t\tthis.request.addHeader(\"Accept\", \"text/html, application/xhtml+xml, application/xml;q=0.9\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(Arrays.asList(MediaType.APPLICATION_XHTML_XML));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void multipleMediaType() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(MediaType.TEXT_PLAIN, MediaType.APPLICATION_XHTML_XML, MediaType.TEXT_HTML));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.APPLICATION_ATOM_XML,\n\t\t\t\tMediaType.TEXT_HTML);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.APPLICATION_XHTML_XML,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.APPLICATION_FORM_URLENCODED,\n\t\t\t\tMediaType.APPLICATION_JSON);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void resolveTextPlainMatchesTextAll() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(MediaType.TEXT_PLAIN));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, new MediaType(\"text\", \"*\"));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenAcceptHeaderIsTextThenMediaTypeAllIsMatched() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.TEXT_PLAIN_VALUE);\n\t\tthis.matcher = new MediaTypeRequestMatcher(new MediaType(\"text\", \"*\"));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void resolveTextAllMatchesTextPlain() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(new MediaType(\"text\", \"*\")));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_PLAIN);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchWhenAcceptHeaderIsTextWildcardThenMediaTypeTextIsMatched() {\n\t\tthis.request.addHeader(\"Accept\", \"text/*\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_PLAIN);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t// useEquals\n\t@Test\n\tpublic void useEqualsResolveTextAllMatchesTextPlain() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(new MediaType(\"text\", \"*\")));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_PLAIN);\n\t\tthis.matcher.setUseEquals(true);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void useEqualsWhenTrueThenMediaTypeTextIsNotMatched() {\n\t\tthis.request.addHeader(\"Accept\", \"text/*\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_PLAIN);\n\t\tthis.matcher.setUseEquals(true);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void useEqualsResolveTextPlainMatchesTextAll() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(MediaType.TEXT_PLAIN));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, new MediaType(\"text\", \"*\"));\n\t\tthis.matcher.setUseEquals(true);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void useEqualsWhenTrueThenMediaTypeTextAllIsNotMatched() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.TEXT_PLAIN_VALUE);\n\t\tthis.matcher = new MediaTypeRequestMatcher(new MediaType(\"text\", \"*\"));\n\t\tthis.matcher.setUseEquals(true);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void useEqualsSame() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(MediaType.TEXT_PLAIN));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_PLAIN);\n\t\tthis.matcher.setUseEquals(true);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void useEqualsWhenTrueThenMediaTypeIsMatchedWithEqualString() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.TEXT_PLAIN_VALUE);\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_PLAIN);\n\t\tthis.matcher.setUseEquals(true);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void useEqualsWithCustomMediaType() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(new MediaType(\"text\", \"unique\")));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, new MediaType(\"text\", \"unique\"));\n\t\tthis.matcher.setUseEquals(true);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void useEqualsWhenTrueThenCustomMediaTypeIsMatched() {\n\t\tthis.request.addHeader(\"Accept\", \"text/unique\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(new MediaType(\"text\", \"unique\"));\n\t\tthis.matcher.setUseEquals(true);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t// ignoreMediaTypeAll\n\t@Test\n\tpublic void mediaAllIgnoreMediaTypeAll() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(MediaType.ALL));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_HTML);\n\t\tthis.matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void ignoredMediaTypesWhenAllThenAnyoneIsNotMatched() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE);\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_HTML);\n\t\tthis.matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void mediaAllAndTextHtmlIgnoreMediaTypeAll() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(MediaType.ALL, MediaType.TEXT_HTML));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_HTML);\n\t\tthis.matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void ignoredMediaTypesWhenAllAndTextThenTextCanBeMatched() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.ALL_VALUE + \", \" + MediaType.TEXT_HTML_VALUE);\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_HTML);\n\t\tthis.matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void mediaAllQ08AndTextPlainIgnoreMediaTypeAll() throws HttpMediaTypeNotAcceptableException {\n\t\tgiven(this.negotiationStrategy.resolveMediaTypes(any(NativeWebRequest.class)))\n\t\t\t.willReturn(Arrays.asList(MediaType.TEXT_PLAIN, MediaType.parseMediaType(\"*/*;q=0.8\")));\n\t\tthis.matcher = new MediaTypeRequestMatcher(this.negotiationStrategy, MediaType.TEXT_HTML);\n\t\tthis.matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void ignoredMediaTypesWhenAllThenQ08WithTextIsNotMatched() {\n\t\tthis.request.addHeader(\"Accept\", MediaType.TEXT_PLAIN + \", */*;q=0.8\");\n\t\tthis.matcher = new MediaTypeRequestMatcher(MediaType.TEXT_HTML);\n\t\tthis.matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/NegatedRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class NegatedRequestMatcherTests {\n\n\t@Mock\n\tprivate RequestMatcher delegate;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\tprivate RequestMatcher matcher;\n\n\t@Test\n\tpublic void constructorNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new NegatedRequestMatcher(null));\n\t}\n\n\t@Test\n\tpublic void matchesDelegateFalse() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(false);\n\t\tthis.matcher = new NegatedRequestMatcher(this.delegate);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesDelegateTrue() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(true);\n\t\tthis.matcher = new NegatedRequestMatcher(this.delegate);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/OrRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNullPointerException;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * @author Rob Winch\n *\n */\n@ExtendWith(MockitoExtension.class)\npublic class OrRequestMatcherTests {\n\n\t@Mock\n\tprivate RequestMatcher delegate;\n\n\t@Mock\n\tprivate RequestMatcher delegate2;\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\tprivate RequestMatcher matcher;\n\n\t@Test\n\tpublic void constructorNullArray() {\n\t\tassertThatNullPointerException().isThrownBy(() -> new OrRequestMatcher((RequestMatcher[]) null));\n\t}\n\n\t@Test\n\tpublic void constructorListOfDoesNotThrowNullPointer() {\n\t\tnew OrRequestMatcher(List.of(pathPattern(\"/test\")));\n\t}\n\n\t@Test\n\tpublic void constructorArrayContainsNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OrRequestMatcher((RequestMatcher) null));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyArray() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OrRequestMatcher(new RequestMatcher[0]));\n\t}\n\n\t@Test\n\tpublic void constructorNullList() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new OrRequestMatcher((List<RequestMatcher>) null));\n\t}\n\n\t@Test\n\tpublic void constructorListContainsNull() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OrRequestMatcher(Arrays.asList((RequestMatcher) null)));\n\t}\n\n\t@Test\n\tpublic void constructorEmptyList() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new OrRequestMatcher(Collections.<RequestMatcher>emptyList()));\n\t}\n\n\t@Test\n\tpublic void matchesSingleTrue() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(true);\n\t\tthis.matcher = new OrRequestMatcher(this.delegate);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesMultiTrue() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(true);\n\t\tthis.matcher = new OrRequestMatcher(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesSingleFalse() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(false);\n\t\tthis.matcher = new OrRequestMatcher(this.delegate);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMultiBothFalse() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(false);\n\t\tgiven(this.delegate2.matches(this.request)).willReturn(false);\n\t\tthis.matcher = new OrRequestMatcher(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesMultiSingleFalse() {\n\t\tgiven(this.delegate.matches(this.request)).willReturn(true);\n\t\tthis.matcher = new OrRequestMatcher(this.delegate, this.delegate2);\n\t\tassertThat(this.matcher.matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matcherWhenMatchersHavePlaceholdersThenPropagatesFirstMatch() {\n\t\tthis.matcher = new OrRequestMatcher(this.delegate, this.delegate2);\n\n\t\tgiven(this.delegate.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"value\")));\n\t\tgiven(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"othervalue\")));\n\t\tMatchResult result = this.matcher.matcher(this.request);\n\t\tassertThat(result.getVariables()).containsExactlyEntriesOf(Map.of(\"param\", \"value\"));\n\t\tverifyNoInteractions(this.delegate2);\n\n\t\tgiven(this.delegate.matcher(this.request)).willReturn(MatchResult.match());\n\t\tgiven(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"value\")));\n\t\tresult = this.matcher.matcher(this.request);\n\t\tassertThat(result.getVariables()).isEmpty();\n\t\tverifyNoInteractions(this.delegate2);\n\n\t\tgiven(this.delegate.matcher(this.request)).willReturn(MatchResult.notMatch());\n\t\tgiven(this.delegate2.matcher(this.request)).willReturn(MatchResult.match(Map.of(\"param\", \"value\")));\n\t\tresult = this.matcher.matcher(this.request);\n\t\tassertThat(result.getVariables()).containsExactlyEntriesOf(Map.of(\"param\", \"value\"));\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/ParameterRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link ParameterRequestMatcher}\n *\n * @author Josh Cummings\n */\n@ExtendWith(MockitoExtension.class)\npublic class ParameterRequestMatcherTests {\n\n\t@Test\n\tpublic void matchesWhenNameThenMatchesOnParameterName() {\n\t\tParameterRequestMatcher matcher = new ParameterRequestMatcher(\"name\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/foo/bar\");\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t\trequest.setParameter(\"name\", \"value\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenNameAndValueThenMatchesOnBoth() {\n\t\tParameterRequestMatcher matcher = new ParameterRequestMatcher(\"name\", \"value\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/foo/bar\");\n\t\trequest.setParameter(\"name\", \"value\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t\trequest.setParameter(\"name\", \"wrong\");\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenValuePlaceholderThenMatchesOnName() {\n\t\tParameterRequestMatcher matcher = new ParameterRequestMatcher(\"name\", \"{placeholder}\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/foo/bar\");\n\t\trequest.setParameter(\"name\", \"value\");\n\t\tRequestMatcher.MatchResult result = matcher.matcher(request);\n\t\tassertThat(result.isMatch()).isTrue();\n\t\tassertThat(result.getVariables().get(\"placeholder\")).isEqualTo(\"value\");\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/RegexRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.BDDMockito.given;\nimport static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;\nimport static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher;\n\n/**\n * @author Luke Taylor\n * @author Rob Winch\n */\n@ExtendWith(MockitoExtension.class)\npublic class RegexRequestMatcherTests {\n\n\t@Mock\n\tprivate HttpServletRequest request;\n\n\t@Test\n\tpublic void doesntMatchIfHttpMethodIsDifferent() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\".*\", \"GET\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"POST\", \"/anything\");\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesIfHttpMethodAndPathMatch() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\".*\", \"GET\");\n\t\tMockHttpServletRequest request = get(\"/anything\").build();\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void queryStringIsMatcherCorrectly() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\".*\\\\?x=y\", \"GET\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/any/path?x=y\");\n\t\trequest.setServletPath(\"/any\");\n\t\trequest.setPathInfo(\"/path\");\n\t\trequest.setQueryString(\"x=y\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void requestHasNullMethodMatches() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\"/something/.*\", \"GET\");\n\t\tHttpServletRequest request = createRequestWithNullMethod(\"/something/here\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t// SEC-2084\n\t@Test\n\tpublic void requestHasNullMethodNoMatch() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\"/something/.*\", \"GET\");\n\t\tHttpServletRequest request = createRequestWithNullMethod(\"/nomatch\");\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void requestHasNullMethodAndNullMatcherMatches() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\"/something/.*\", null);\n\t\tHttpServletRequest request = createRequestWithNullMethod(\"/something/here\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void requestHasNullMethodAndNullMatcherNoMatch() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\"/something/.*\", null);\n\t\tHttpServletRequest request = createRequestWithNullMethod(\"/nomatch\");\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t// SEC-2831\n\t@Test\n\tpublic void matchesWithInvalidMethod() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\"/blah\", \"GET\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"INVALID\", \"/blah\");\n\t\trequest.setMethod(\"INVALID\");\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWithCarriageReturn() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\".*\", null);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/blah%0a\");\n\t\trequest.setServletPath(\"/blah\\n\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWithLineFeed() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\".*\", null);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/blah%0d\");\n\t\trequest.setServletPath(\"/blah\\r\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void toStringThenFormatted() {\n\t\tRegexRequestMatcher matcher = new RegexRequestMatcher(\"/blah\", \"GET\");\n\t\tassertThat(matcher.toString()).isEqualTo(\"Regex [pattern='/blah', GET]\");\n\t}\n\n\t@Test\n\tpublic void matchesWhenRequestUriMatchesThenMatchesTrue() {\n\t\tRegexRequestMatcher matcher = regexMatcher(\".*\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/something/anything\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenRequestUriDontMatchThenMatchesFalse() {\n\t\tRegexRequestMatcher matcher = regexMatcher(\".*\\\\?param=value\");\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/something/anything\");\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesWhenRequestMethodMatchesThenMatchesTrue() {\n\t\tRegexRequestMatcher matcher = regexMatcher(HttpMethod.GET);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/something/anything\");\n\t\tassertThat(matcher.matches(request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesWhenRequestMethodDontMatchThenMatchesFalse() {\n\t\tRegexRequestMatcher matcher = regexMatcher(HttpMethod.POST);\n\t\tMockHttpServletRequest request = new MockHttpServletRequest(\"GET\", \"/something/anything\");\n\t\tassertThat(matcher.matches(request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void staticRegexMatcherWhenNoPatternThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> regexMatcher((String) null))\n\t\t\t.withMessage(\"pattern cannot be empty\");\n\t}\n\n\t@Test\n\tpublic void staticRegexMatcherNoMethodThenException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> regexMatcher((HttpMethod) null))\n\t\t\t.withMessage(\"method cannot be null\");\n\t}\n\n\tprivate HttpServletRequest createRequestWithNullMethod(String path) {\n\t\tgiven(this.request.getQueryString()).willReturn(\"doesntMatter\");\n\t\tgiven(this.request.getServletPath()).willReturn(path);\n\t\treturn this.request;\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/RequestHeaderRequestMatcherTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * @author Rob Winch\n *\n */\npublic class RequestHeaderRequestMatcherTests {\n\n\tprivate final String headerName = \"headerName\";\n\n\tprivate final String headerValue = \"headerValue\";\n\n\tprivate MockHttpServletRequest request;\n\n\t@BeforeEach\n\tpublic void setup() {\n\t\tthis.request = new MockHttpServletRequest();\n\t}\n\n\t@Test\n\tpublic void constructorNullHeaderName() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RequestHeaderRequestMatcher(null));\n\t}\n\n\t@Test\n\tpublic void constructorNullHeaderNameNonNullHeaderValue() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new RequestHeaderRequestMatcher(null, \"v\"));\n\t}\n\n\t@Test\n\tpublic void matchesHeaderNameMatches() {\n\t\tthis.request.addHeader(this.headerName, this.headerValue);\n\t\tassertThat(new RequestHeaderRequestMatcher(this.headerName).matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesHeaderNameDoesNotMatch() {\n\t\tthis.request.addHeader(this.headerName + \"notMatch\", this.headerValue);\n\t\tassertThat(new RequestHeaderRequestMatcher(this.headerName).matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesHeaderNameValueMatches() {\n\t\tthis.request.addHeader(this.headerName, this.headerValue);\n\t\tassertThat(new RequestHeaderRequestMatcher(this.headerName, this.headerValue).matches(this.request)).isTrue();\n\t}\n\n\t@Test\n\tpublic void matchesHeaderNameValueHeaderNameNotMatch() {\n\t\tthis.request.addHeader(this.headerName + \"notMatch\", this.headerValue);\n\t\tassertThat(new RequestHeaderRequestMatcher(this.headerName, this.headerValue).matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesHeaderNameValueHeaderValueNotMatch() {\n\t\tthis.request.addHeader(this.headerName, this.headerValue + \"notMatch\");\n\t\tassertThat(new RequestHeaderRequestMatcher(this.headerName, this.headerValue).matches(this.request)).isFalse();\n\t}\n\n\t@Test\n\tpublic void matchesHeaderNameValueHeaderValueMultiNotMatch() {\n\t\tthis.request.addHeader(this.headerName, this.headerValue + \"notMatch\");\n\t\tthis.request.addHeader(this.headerName, this.headerValue);\n\t\tassertThat(new RequestHeaderRequestMatcher(this.headerName, this.headerValue).matches(this.request)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/RequestMatcherEntryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nclass RequestMatcherEntryTests {\n\n\t@Test\n\tvoid constructWhenGetRequestMatcherAndEntryThenSameRequestMatcherAndEntry() {\n\t\tRequestMatcher requestMatcher = mock(RequestMatcher.class);\n\t\tRequestMatcherEntry<String> entry = new RequestMatcherEntry<>(requestMatcher, \"entry\");\n\t\tassertThat(entry.getRequestMatcher()).isSameAs(requestMatcher);\n\t\tassertThat(entry.getEntry()).isEqualTo(\"entry\");\n\t}\n\n\t@Test\n\tvoid constructWhenNullValuesThenNullValues() {\n\t\tRequestMatcherEntry<String> entry = new RequestMatcherEntry<>(null, null);\n\t\tassertThat(entry.getRequestMatcher()).isNull();\n\t\tassertThat(entry.getEntry()).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/java/org/springframework/security/web/util/matcher/RequestMatchersTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.util.matcher;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link RequestMatchers}.\n *\n * @author Christian Schuster\n */\nclass RequestMatchersTests {\n\n\t@Test\n\tvoid checkAnyOfWhenOneMatchThenMatch() {\n\t\tRequestMatcher composed = RequestMatchers.anyOf((r) -> false, (r) -> true);\n\t\tboolean match = composed.matches(null);\n\t\tassertThat(match).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWhenNoneMatchThenNotMatch() {\n\t\tRequestMatcher composed = RequestMatchers.anyOf((r) -> false, (r) -> false);\n\t\tboolean match = composed.matches(null);\n\t\tassertThat(match).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAnyOfWhenEmptyThenNotMatch() {\n\t\tRequestMatcher composed = RequestMatchers.anyOf();\n\t\tboolean match = composed.matches(null);\n\t\tassertThat(match).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenOneNotMatchThenNotMatch() {\n\t\tRequestMatcher composed = RequestMatchers.allOf((r) -> false, (r) -> true);\n\t\tboolean match = composed.matches(null);\n\t\tassertThat(match).isFalse();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenAllMatchThenMatch() {\n\t\tRequestMatcher composed = RequestMatchers.allOf((r) -> true, (r) -> true);\n\t\tboolean match = composed.matches(null);\n\t\tassertThat(match).isTrue();\n\t}\n\n\t@Test\n\tvoid checkAllOfWhenEmptyThenMatch() {\n\t\tRequestMatcher composed = RequestMatchers.allOf();\n\t\tboolean match = composed.matches(null);\n\t\tassertThat(match).isTrue();\n\t}\n\n\t@Test\n\tvoid checkNotWhenMatchThenNotMatch() {\n\t\tRequestMatcher composed = RequestMatchers.not((r) -> true);\n\t\tboolean match = composed.matches(null);\n\t\tassertThat(match).isFalse();\n\t}\n\n\t@Test\n\tvoid checkNotWhenNotMatchThenMatch() {\n\t\tRequestMatcher composed = RequestMatchers.not((r) -> false);\n\t\tboolean match = composed.matches(null);\n\t\tassertThat(match).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "web/src/test/resources/logback-test.xml",
    "content": "<configuration>\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n\t<encoder>\n\t\t<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n\t</encoder>\n\t</appender>\n\n\t<logger name=\"org.springframework.security\" level=\"${sec.log.level:-WARN}\"/>\n\n\n\t<root level=\"${root.level:-WARN}\">\n\t<appender-ref ref=\"STDOUT\" />\n\t</root>\n\n</configuration>\n"
  },
  {
    "path": "web/src/test/resources/org/springframework/security/test.css",
    "content": "body {\n    color: #6db33f;\n}\n"
  },
  {
    "path": "web/src/test/resources/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTest-context.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd\">\n\n\t<bean id=\"daep\" class=\"org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint\">\n\t\t<constructor-arg>\n\t\t\t<map>\n\t\t\t\t<entry key=\"hasIpAddress('192.168.1.0/24') and hasHeader('User-Agent','Mozilla')\" value-ref=\"firstAEP\" />\n\t\t\t</map>\n\t\t</constructor-arg>\n\t\t<property name=\"defaultEntryPoint\" ref=\"defaultAEP\"/>\n\t</bean>\n\n\t<bean id=\"firstAEP\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<constructor-arg value=\"org.springframework.security.web.AuthenticationEntryPoint\" type=\"java.lang.Class\"/>\n\t</bean>\n\n\n\t<bean id=\"defaultAEP\" class=\"org.mockito.Mockito\" factory-method=\"mock\">\n\t\t<constructor-arg value=\"org.springframework.security.web.AuthenticationEntryPoint\" type=\"java.lang.Class\"/>\n\t</bean>\n\n\n</beans>\n"
  },
  {
    "path": "web/src/test/resources/webxml/NoRoles.web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE web-app PUBLIC \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\" \"https://java.sun.com/dtd/web-app_2_3.dtd\">\n<web-app id=\"WebApp\">\n\t<display-name>poc-acegi-web</display-name>\n\t<context-param>\n\t\t<param-name>contextConfigLocation</param-name>\n\t\t<param-value>classpath:j2ee-acegi-security.xml</param-value>\n\t</context-param>\n\t<context-param>\n\t\t<param-name>log4jConfigLocation</param-name>\n\t\t<param-value>/WEB-INF/classes/log4j.properties</param-value>\n\t</context-param>\n\n\t<filter>\n\t\t<filter-name>springSecurityFilterChain</filter-name>\n\t\t<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\n\t</filter>\n\n\t<filter-mapping>\n\t\t<filter-name>springSecurityFilterChain</filter-name>\n\t\t<url-pattern>/*</url-pattern>\n\t</filter-mapping>\n\n\t<listener>\n\t\t<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\n\t</listener>\n\t<listener>\n\t\t<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n\t</listener>\n\t<listener>\n\t\t<listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>\n\t</listener>\n\n\t<servlet>\n\t\t<servlet-name>dispatch</servlet-name>\n\t\t<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n\t\t<load-on-startup>1</load-on-startup>\n\t</servlet>\n\t<servlet-mapping>\n\t\t<servlet-name>dispatch</servlet-name>\n\t\t<url-pattern>*.form</url-pattern>\n\t</servlet-mapping>\n\t<welcome-file-list>\n\t\t<welcome-file>index.html</welcome-file>\n\t\t<welcome-file>index.htm</welcome-file>\n\t\t<welcome-file>index.jsp</welcome-file>\n\t\t<welcome-file>default.html</welcome-file>\n\t\t<welcome-file>default.htm</welcome-file>\n\t\t<welcome-file>default.jsp</welcome-file>\n\t</welcome-file-list>\n</web-app>\n"
  },
  {
    "path": "web/src/test/resources/webxml/Role1-4.web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE web-app PUBLIC \"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN\" \"https://java.sun.com/dtd/web-app_2_3.dtd\">\n<web-app id=\"WebApp\">\n\t<display-name>poc-acegi-web</display-name>\n\t<context-param>\n\t\t<param-name>contextConfigLocation</param-name>\n\t\t<param-value>classpath:j2ee-acegi-security.xml</param-value>\n\t</context-param>\n\t<context-param>\n\t\t<param-name>log4jConfigLocation</param-name>\n\t\t<param-value>/WEB-INF/classes/log4j.properties</param-value>\n\t</context-param>\n\n\t<filter>\n\t\t<filter-name>springSecurityFilterChain</filter-name>\n\t\t<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\n\t</filter>\n\n\t<filter-mapping>\n\t\t<filter-name>springSecurityFilterChain</filter-name>\n\t\t<url-pattern>/*</url-pattern>\n\t</filter-mapping>\n\n\t<listener>\n\t\t<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>\n\t</listener>\n\t<listener>\n\t\t<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n\t</listener>\n\t<listener>\n\t\t<listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>\n\t</listener>\n\n\t<servlet>\n\t\t<servlet-name>dispatch</servlet-name>\n\t\t<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>\n\t\t<load-on-startup>1</load-on-startup>\n\t</servlet>\n\t<servlet-mapping>\n\t\t<servlet-name>dispatch</servlet-name>\n\t\t<url-pattern>*.form</url-pattern>\n\t</servlet-mapping>\n\t<welcome-file-list>\n\t\t<welcome-file>index.html</welcome-file>\n\t\t<welcome-file>index.htm</welcome-file>\n\t\t<welcome-file>index.jsp</welcome-file>\n\t\t<welcome-file>default.html</welcome-file>\n\t\t<welcome-file>default.htm</welcome-file>\n\t\t<welcome-file>default.jsp</welcome-file>\n\t</welcome-file-list>\n\t<resource-ref id=\"ResourceRef_1185189465160\">\n\t\t<res-ref-name>jms/testQueue</res-ref-name>\n\t\t<res-type>javax.jms.Queue</res-type>\n\t\t<res-auth>Container</res-auth>\n\t\t<res-sharing-scope>Shareable</res-sharing-scope>\n\t</resource-ref>\n\t<resource-ref id=\"ResourceRef_1185189465170\">\n\t\t<res-ref-name>jms/testQCF</res-ref-name>\n\t\t<res-type>javax.jms.QueueConnectionFactory</res-type>\n\t\t<res-auth>Container</res-auth>\n\t\t<res-sharing-scope>Shareable</res-sharing-scope>\n\t</resource-ref>\n\t<security-constraint>\n\t\t<web-resource-collection>\n\t\t\t<web-resource-name>Default</web-resource-name>\n\t\t\t<url-pattern>/*</url-pattern>\n\t\t</web-resource-collection>\n\t\t<auth-constraint>\n\t\t\t<role-name>*</role-name>\n\t\t</auth-constraint>\n\t</security-constraint>\n\t<security-role>\n\t\t<description></description>\n\t\t<role-name>Role1</role-name>\n\t</security-role>\n\t<security-role>\n\t\t<description></description>\n\t\t<role-name>Role2</role-name>\n\t</security-role>\n\t<security-role>\n\t\t<description></description>\n\t\t<role-name>Role3</role-name>\n\t</security-role>\n\t<security-role>\n\t\t<description></description>\n\t\t<role-name>Role4</role-name>\n\t</security-role>\n</web-app>\n"
  },
  {
    "path": "webauthn/spring-security-webauthn.gradle",
    "content": "plugins {\n\tid 'compile-warnings-error'\n\tid 'security-nullability'\n\tid 'javadoc-warnings-error'\n}\n\napply plugin: 'io.spring.convention.spring-module'\n\ndependencies {\n\tmanagement platform(project(\":spring-security-dependencies\"))\n\tapi project(':spring-security-core')\n\tapi project(':spring-security-web')\n\tapi 'org.springframework:spring-core'\n\tapi 'org.springframework:spring-beans'\n\tapi 'org.springframework:spring-web'\n\tapi libs.webauthn4j.core\n\n\toptional 'org.springframework:spring-jdbc'\n\toptional 'org.springframework:spring-tx'\n\toptional 'tools.jackson.core:jackson-databind'\n\toptional 'com.fasterxml.jackson.core:jackson-databind'\n\n\tprovided 'jakarta.servlet:jakarta.servlet-api'\n\n\ttestImplementation project(path: ':spring-security-core', configuration: 'tests')\n\ttestImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor'\n\ttestImplementation 'io.projectreactor:reactor-test'\n\ttestImplementation 'jakarta.xml.bind:jakarta.xml.bind-api'\n\ttestImplementation 'jakarta.websocket:jakarta.websocket-api'\n\ttestImplementation 'jakarta.websocket:jakarta.websocket-client-api'\n\ttestImplementation 'org.hamcrest:hamcrest'\n\ttestImplementation 'org.mockito:mockito-core'\n\ttestImplementation 'org.skyscreamer:jsonassert'\n\ttestImplementation 'org.springframework:spring-webflux'\n\ttestImplementation 'org.synchronoss.cloud:nio-multipart-parser'\n\ttestImplementation \"org.assertj:assertj-core\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-api\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-params\"\n\ttestImplementation \"org.junit.jupiter:junit-jupiter-engine\"\n\ttestImplementation \"org.mockito:mockito-core\"\n\ttestImplementation \"org.mockito:mockito-junit-jupiter\"\n\ttestImplementation \"org.springframework:spring-test\"\n\ttestImplementation \"org.springframework:spring-webmvc\"\n\ttestImplementation 'com.squareup.okhttp3:mockwebserver'\n\n\ttestRuntimeOnly 'org.hsqldb:hsqldb'\n\ttestRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/aot/PublicKeyCredentialUserEntityRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.aot;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;\n\n/**\n *\n * A JDBC implementation of an {@link PublicKeyCredentialUserEntityRepository} that uses a\n * {@link JdbcOperations} for {@link PublicKeyCredentialUserEntity} persistence.\n *\n * @author Max Batischev\n * @since 6.5\n */\nclass PublicKeyCredentialUserEntityRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\thints.resources().registerPattern(\"org/springframework/security/user-entities-schema.sql\");\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/aot/UserCredentialRuntimeHints.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.aot;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository;\n\n/**\n *\n * A JDBC implementation of an {@link UserCredentialRepository} that uses a\n * {@link JdbcOperations} for {@link CredentialRecord} persistence.\n *\n * @author Max Batischev\n * @since 6.5\n */\nclass UserCredentialRuntimeHints implements RuntimeHintsRegistrar {\n\n\t@Override\n\tpublic void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {\n\t\thints.resources()\n\t\t\t.registerPattern(\"org/springframework/security/user-credentials-schema.sql\")\n\t\t\t.registerPattern(\"org/springframework/security/user-credentials-schema-postgres.sql\");\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/aot/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WebAuthn AOT support.\n */\n@NullMarked\npackage org.springframework.security.web.webauthn.aot;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AttestationConveyancePreference.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * <a href=\"https://www.w3.org/TR/webauthn-3/#webauthn-relying-party\">WebAuthn Relying\n * Parties</a> may use <a href=\n * \"https://www.w3.org/TR/webauthn-3/#enumdef-attestationconveyancepreference\">AttestationConveyancePreference</a>\n * to specify their preference regarding attestation conveyance during credential\n * generation.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class AttestationConveyancePreference implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -4252430175801658788L;\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-attestationconveyancepreference-none\">none</a>\n\t * preference indicates that the Relying Party is not interested in\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#authenticator\">authenticator</a>\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#attestation\">attestation</a>.\n\t */\n\tpublic static final AttestationConveyancePreference NONE = new AttestationConveyancePreference(\"none\");\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-attestationconveyancepreference-indirect\">indirect</a>\n\t * preference indicates that the Relying Party wants to receive a verifiable\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#attestation-statement\">attestation\n\t * statement</a>, but allows the client to decide how to obtain such an attestation\n\t * statement.\n\t */\n\tpublic static final AttestationConveyancePreference INDIRECT = new AttestationConveyancePreference(\"indirect\");\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-attestationconveyancepreference-direct\">direct</a>\n\t * preference indicates that the Relying Party wants to receive the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#attestation-statement\">attestation\n\t * statement</a> as generated by the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#authenticator\">authenticator</a>.\n\t */\n\tpublic static final AttestationConveyancePreference DIRECT = new AttestationConveyancePreference(\"direct\");\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-attestationconveyancepreference-enterprise\">enterprise</a>\n\t * preference indicates that the Relying Party wants to receive an\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#attestation-statement\">attestation\n\t * statement</a> that may include uniquely identifying information.\n\t */\n\tpublic static final AttestationConveyancePreference ENTERPRISE = new AttestationConveyancePreference(\"enterprise\");\n\n\tprivate final String value;\n\n\tAttestationConveyancePreference(String value) {\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Gets the String value of the preference.\n\t * @return the String value of the preference.\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t/**\n\t * Gets an instance of {@link AttestationConveyancePreference}\n\t * @param value the {@link #getValue()}\n\t * @return an {@link AttestationConveyancePreference}\n\t */\n\tpublic static AttestationConveyancePreference valueOf(String value) {\n\t\tswitch (value) {\n\t\t\tcase \"none\":\n\t\t\t\treturn NONE;\n\t\t\tcase \"indirect\":\n\t\t\t\treturn INDIRECT;\n\t\t\tcase \"direct\":\n\t\t\t\treturn DIRECT;\n\t\t\tcase \"enterprise\":\n\t\t\t\treturn ENTERPRISE;\n\t\t\tdefault:\n\t\t\t\treturn new AttestationConveyancePreference(value);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticationExtensionsClientInput.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serializable;\n\n/**\n * A <a href=\"https://www.w3.org/TR/webauthn-3/#client-extension-input\">client extension\n * input</a> entry in the {@link AuthenticationExtensionsClientInputs}.\n *\n * @param <T>\n * @author Rob Winch\n * @since 6.4\n * @see ImmutableAuthenticationExtensionsClientInput\n */\npublic interface AuthenticationExtensionsClientInput<T> extends Serializable {\n\n\t/**\n\t * Gets the <a href=\"https://www.w3.org/TR/webauthn-3/#extension-identifier\">extension\n\t * identifier</a>.\n\t * @return the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#extension-identifier\">extension\n\t * identifier</a>.\n\t */\n\tString getExtensionId();\n\n\t/**\n\t * Gets the <a href=\"https://www.w3.org/TR/webauthn-3/#client-extension-input\">client\n\t * extension</a>.\n\t * @return the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#client-extension-input\">client\n\t * extension</a>.\n\t */\n\tT getInput();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticationExtensionsClientInputs.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#iface-authentication-extensions-client-inputs\">AuthenticationExtensionsClientInputs</a>\n * is a dictionary containing the\n * <a href=\"https://www.w3.org/TR/webauthn-3/#client-extension-input\">client extension\n * input</a> values for zero or more\n * <a href=\"https://www.w3.org/TR/webauthn-3/#webauthn-extensions\">WebAuthn\n * Extensions</a>.\n *\n * @author Rob Winch\n * @since 6.4\n * @see PublicKeyCredentialCreationOptions#getExtensions()\n */\npublic interface AuthenticationExtensionsClientInputs extends Serializable {\n\n\t/**\n\t * Gets all of the {@link AuthenticationExtensionsClientInput}.\n\t * @return a non-null {@link List} of {@link AuthenticationExtensionsClientInput}.\n\t */\n\tList<AuthenticationExtensionsClientInput> getInputs();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticationExtensionsClientOutput.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serializable;\n\n/**\n * A <a href=\"https://www.w3.org/TR/webauthn-3/#client-extension-output\">client extension\n * output</a> entry in {@link AuthenticationExtensionsClientOutputs}.\n *\n * @param <T>\n * @see AuthenticationExtensionsClientOutputs#getOutputs()\n * @see CredentialPropertiesOutput\n */\npublic interface AuthenticationExtensionsClientOutput<T> extends Serializable {\n\n\t/**\n\t * Gets the <a href=\"https://www.w3.org/TR/webauthn-3/#extension-identifier\">extension\n\t * identifier</a>.\n\t * @return the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#extension-identifier\">extension\n\t * identifier</a>.\n\t */\n\tString getExtensionId();\n\n\t/**\n\t * The <a href=\"https://www.w3.org/TR/webauthn-3/#client-extension-output\">client\n\t * extension output</a>.\n\t * @return the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#client-extension-output\">client\n\t * extension output</a>.\n\t */\n\tT getOutput();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticationExtensionsClientOutputs.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientoutputs\">AuthenticationExtensionsClientOutputs</a>\n * is a dictionary containing the\n * <a href=\"https://www.w3.org/TR/webauthn-3/#client-extension-output\">client extension\n * output</a> values for zero or more\n * <a href=\"https://www.w3.org/TR/webauthn-3/#webauthn-extensions\">WebAuthn\n * Extensions</a>.\n *\n * @author Rob Winch\n * @since 6.4\n * @see PublicKeyCredential#getClientExtensionResults()\n */\npublic interface AuthenticationExtensionsClientOutputs extends Serializable {\n\n\t/**\n\t * Gets all of the {@link AuthenticationExtensionsClientOutput}.\n\t * @return a non-null {@link List} of {@link AuthenticationExtensionsClientOutput}.\n\t */\n\tList<AuthenticationExtensionsClientOutput<?>> getOutputs();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAssertionResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * The <a href=\n * \"https://www.w3.org/TR/webauthn-3/#authenticatorassertionresponse\">AuthenticatorAssertionResponse</a>\n * interface represents an\n * <a href=\"https://www.w3.org/TR/webauthn-3/#authenticator\">authenticator</a>'s response\n * to a client's request for generation of a new\n * <a href=\"https://www.w3.org/TR/webauthn-3/#authentication-assertion\">authentication\n * assertion</a> given the\n * <a href=\"https://www.w3.org/TR/webauthn-3/#webauthn-relying-party\">WebAuthn Relying\n * Party</a>'s challenge and OPTIONAL list of credentials it is aware of. This response\n * contains a cryptographic signature proving possession of the\n * <a href=\"https://www.w3.org/TR/webauthn-3/#credential-private-key\">credential private\n * key</a>, and optionally evidence of\n * <a href=\"https://www.w3.org/TR/webauthn-3/#user-consent\">user consent</a> to a specific\n * transaction.\n *\n * @author Rob Winch\n * @since 6.4\n * @see PublicKeyCredential#getResponse()\n */\npublic final class AuthenticatorAssertionResponse extends AuthenticatorResponse {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 324976481675434298L;\n\n\tprivate final Bytes authenticatorData;\n\n\tprivate final Bytes signature;\n\n\tprivate final @Nullable Bytes userHandle;\n\n\tprivate final @Nullable Bytes attestationObject;\n\n\tprivate AuthenticatorAssertionResponse(Bytes clientDataJSON, Bytes authenticatorData, Bytes signature,\n\t\t\t@Nullable Bytes userHandle, @Nullable Bytes attestationObject) {\n\t\tsuper(clientDataJSON);\n\t\tthis.authenticatorData = authenticatorData;\n\t\tthis.signature = signature;\n\t\tthis.userHandle = userHandle;\n\t\tthis.attestationObject = attestationObject;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponse-authenticatordata\">authenticatorData</a>\n\t * contains the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#authenticator-data\">authenticator\n\t * data</a> returned by the authenticator. See\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data\">6.1\n\t * Authenticator Data.</a>.\n\t * @return the {@code authenticatorData}\n\t */\n\tpublic Bytes getAuthenticatorData() {\n\t\treturn this.authenticatorData;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponse-signature\">signature</a>\n\t * contains the raw signature returned from the authenticator. See\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#sctn-op-get-assertion\">6.3.3 The\n\t * authenticatorGetAssertion Operation</a>.\n\t * @return the {@code signature}\n\t */\n\tpublic Bytes getSignature() {\n\t\treturn this.signature;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponse-userhandle\">userHandle</a>\n\t * is the <a href=\"https://www.w3.org/TR/webauthn-3/#user-handle\">user handle</a>\n\t * which is returned from the authenticator, or null if the authenticator did not\n\t * return a user handle. See\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#sctn-op-get-assertion\">6.3.3 The\n\t * authenticatorGetAssertion Operation</a>. The authenticator MUST always return a\n\t * user handle if the <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials\">allowCredentials</a>\n\t * option used in the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#authentication-ceremony\">authentication\n\t * ceremony</a> is empty, and MAY return one otherwise.\n\t * @return the <a href=\"https://www.w3.org/TR/webauthn-3/#user-handle\">user handle</a>\n\t */\n\tpublic @Nullable Bytes getUserHandle() {\n\t\treturn this.userHandle;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-attestationobject\">attestationObject</a>\n\t * is an OPTIONAL attribute contains an\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#attestation-object\">attestation\n\t * object</a>, if the authenticator supports attestation in assertions.\n\t * @return the {@code attestationObject}\n\t */\n\tpublic @Nullable Bytes getAttestationObject() {\n\t\treturn this.attestationObject;\n\t}\n\n\t/**\n\t * Creates a new {@link AuthenticatorAssertionResponseBuilder}\n\t * @return the {@link AuthenticatorAssertionResponseBuilder}\n\t */\n\tpublic static AuthenticatorAssertionResponseBuilder builder() {\n\t\treturn new AuthenticatorAssertionResponseBuilder();\n\t}\n\n\t/**\n\t * Builds a {@link AuthenticatorAssertionResponse}.\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class AuthenticatorAssertionResponseBuilder {\n\n\t\tprivate @Nullable Bytes authenticatorData;\n\n\t\tprivate @Nullable Bytes signature;\n\n\t\tprivate @Nullable Bytes userHandle;\n\n\t\tprivate @Nullable Bytes attestationObject;\n\n\t\tprivate @Nullable Bytes clientDataJSON;\n\n\t\tprivate AuthenticatorAssertionResponseBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link #getAuthenticatorData()} property\n\t\t * @param authenticatorData the authenticator data.\n\t\t * @return the {@link AuthenticatorAssertionResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAssertionResponseBuilder authenticatorData(@Nullable Bytes authenticatorData) {\n\t\t\tthis.authenticatorData = authenticatorData;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link #getSignature()} property\n\t\t * @param signature the signature\n\t\t * @return the {@link AuthenticatorAssertionResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAssertionResponseBuilder signature(@Nullable Bytes signature) {\n\t\t\tthis.signature = signature;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link #getUserHandle()} property\n\t\t * @param userHandle the user handle\n\t\t * @return the {@link AuthenticatorAssertionResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAssertionResponseBuilder userHandle(@Nullable Bytes userHandle) {\n\t\t\tthis.userHandle = userHandle;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link #attestationObject} property\n\t\t * @param attestationObject the attestation object\n\t\t * @return the {@link AuthenticatorAssertionResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAssertionResponseBuilder attestationObject(@Nullable Bytes attestationObject) {\n\t\t\tthis.attestationObject = attestationObject;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link #getClientDataJSON()} property\n\t\t * @param clientDataJSON the client data JSON\n\t\t * @return the {@link AuthenticatorAssertionResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAssertionResponseBuilder clientDataJSON(@Nullable Bytes clientDataJSON) {\n\t\t\tthis.clientDataJSON = clientDataJSON;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds the {@link AuthenticatorAssertionResponse}\n\t\t * @return the {@link AuthenticatorAssertionResponse}\n\t\t */\n\t\tpublic AuthenticatorAssertionResponse build() {\n\t\t\tAssert.notNull(this.clientDataJSON, \"clientDataJSON cannot be null\");\n\t\t\tAssert.notNull(this.authenticatorData, \"authenticatorData cannot be null\");\n\t\t\tAssert.notNull(this.signature, \"signature cannot be null\");\n\t\t\treturn new AuthenticatorAssertionResponse(this.clientDataJSON, this.authenticatorData, this.signature,\n\t\t\t\t\tthis.userHandle, this.attestationObject);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttachment.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.ObjectStreamException;\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * The <a href=\n * \"https://www.w3.org/TR/webauthn-3/#enumdef-authenticatorattachment\">AuthenticatorAttachment</a>.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class AuthenticatorAttachment implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 8446133215195918090L;\n\n\t/**\n\t * Indicates <a href=\n\t * \"https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#cross-platform-attachment\">cross-platform\n\t * attachment</a>.\n\t *\n\t * <p>\n\t * Authenticators of this class are removable from, and can \"roam\" among, client\n\t * platforms.\n\t */\n\tpublic static final AuthenticatorAttachment CROSS_PLATFORM = new AuthenticatorAttachment(\"cross-platform\");\n\n\t/**\n\t * Indicates <a href=\n\t * \"https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#platform-attachment\">platform\n\t * attachment</a>.\n\t *\n\t * <p>\n\t * Usually, authenticators of this class are not removable from the platform.\n\t */\n\tpublic static final AuthenticatorAttachment PLATFORM = new AuthenticatorAttachment(\"platform\");\n\n\tprivate final String value;\n\n\tAuthenticatorAttachment(String value) {\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Gets the value.\n\t * @return the value.\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AuthenticatorAttachment [\" + this.value + \"]\";\n\t}\n\n\t/**\n\t * Gets an instance of {@link AuthenticatorAttachment} based upon the value passed in.\n\t * @param value the value to obtain the {@link AuthenticatorAttachment}\n\t * @return the {@link AuthenticatorAttachment}\n\t */\n\tpublic static AuthenticatorAttachment valueOf(String value) {\n\t\tswitch (value) {\n\t\t\tcase \"cross-platform\":\n\t\t\t\treturn CROSS_PLATFORM;\n\t\t\tcase \"platform\":\n\t\t\t\treturn PLATFORM;\n\t\t\tdefault:\n\t\t\t\treturn new AuthenticatorAttachment(value);\n\t\t}\n\t}\n\n\tpublic static AuthenticatorAttachment[] values() {\n\t\treturn new AuthenticatorAttachment[] { CROSS_PLATFORM, PLATFORM };\n\t}\n\n\t@Serial\n\tprivate Object readResolve() throws ObjectStreamException {\n\t\treturn valueOf(this.value);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorAttestationResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#authenticatorattestationresponse\">AuthenticatorAttestationResponse</a>\n * represents the\n * <a href=\"https://www.w3.org/TR/webauthn-3/#authenticator\">authenticator</a>'s response\n * to a client's request for the creation of a new\n * <a href=\"https://www.w3.org/TR/webauthn-3/#public-key-credential\">public key\n * credential</a>.\n *\n * @author Rob Winch\n * @since 6.4\n * @see PublicKeyCredential#getResponse()\n */\npublic final class AuthenticatorAttestationResponse extends AuthenticatorResponse {\n\n\tprivate final Bytes attestationObject;\n\n\tprivate final @Nullable List<AuthenticatorTransport> transports;\n\n\tprivate AuthenticatorAttestationResponse(Bytes clientDataJSON, Bytes attestationObject,\n\t\t\t@Nullable List<AuthenticatorTransport> transports) {\n\t\tsuper(clientDataJSON);\n\t\tthis.attestationObject = attestationObject;\n\t\tthis.transports = transports;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-attestationobject\">attestationObject</a>\n\t * attribute contains an attestation object, which is opaque to, and cryptographically\n\t * protected against tampering by, the client.\n\t * @return the attestationObject\n\t */\n\tpublic Bytes getAttestationObject() {\n\t\treturn this.attestationObject;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-gettransports\">transports</a>\n\t * returns the <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponse-transports-slot\">transports</a>\n\t * @return the transports\n\t */\n\tpublic @Nullable List<AuthenticatorTransport> getTransports() {\n\t\treturn this.transports;\n\t}\n\n\t/**\n\t * Creates a new instance.\n\t * @return the {@link AuthenticatorAttestationResponseBuilder}\n\t */\n\tpublic static AuthenticatorAttestationResponseBuilder builder() {\n\t\treturn new AuthenticatorAttestationResponseBuilder();\n\t}\n\n\t/**\n\t * Builds {@link AuthenticatorAssertionResponse}.\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class AuthenticatorAttestationResponseBuilder {\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Bytes attestationObject;\n\n\t\tprivate @Nullable List<AuthenticatorTransport> transports;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Bytes clientDataJSON;\n\n\t\tprivate AuthenticatorAttestationResponseBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getAttestationObject()} property.\n\t\t * @param attestationObject the attestation object.\n\t\t * @return the {@link AuthenticatorAttestationResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAttestationResponseBuilder attestationObject(Bytes attestationObject) {\n\t\t\tthis.attestationObject = attestationObject;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getTransports()} property.\n\t\t * @param transports the transports\n\t\t * @return the {@link AuthenticatorAttestationResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAttestationResponseBuilder transports(AuthenticatorTransport... transports) {\n\t\t\treturn transports(Arrays.asList(transports));\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getTransports()} property.\n\t\t * @param transports the transports\n\t\t * @return the {@link AuthenticatorAttestationResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAttestationResponseBuilder transports(@Nullable List<AuthenticatorTransport> transports) {\n\t\t\tthis.transports = transports;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getClientDataJSON()} property.\n\t\t * @param clientDataJSON the client data JSON.\n\t\t * @return the {@link AuthenticatorAttestationResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAttestationResponseBuilder clientDataJSON(Bytes clientDataJSON) {\n\t\t\tthis.clientDataJSON = clientDataJSON;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a {@link AuthenticatorAssertionResponse}.\n\t\t * @return the {@link AuthenticatorAttestationResponseBuilder}\n\t\t */\n\t\tpublic AuthenticatorAttestationResponse build() {\n\t\t\treturn new AuthenticatorAttestationResponse(this.clientDataJSON, this.attestationObject, this.transports);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorResponse.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serializable;\n\n/**\n * The <a href=\n * \"https://www.w3.org/TR/webauthn-3/#iface-authenticatorresponse\">AuthenticatorResponse</a>\n * represents <a href=\"https://www.w3.org/TR/webauthn-3/#authenticator\">Authenticators</a>\n * respond to <a href=\"https://www.w3.org/TR/webauthn-3/#relying-party\">Relying Party</a>\n * requests.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic abstract class AuthenticatorResponse implements Serializable {\n\n\tprivate final Bytes clientDataJSON;\n\n\t/**\n\t * Creates a new instance\n\t * @param clientDataJSON the {@link #getClientDataJSON()}\n\t */\n\tAuthenticatorResponse(Bytes clientDataJSON) {\n\t\tthis.clientDataJSON = clientDataJSON;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorresponse-clientdatajson\">clientDataJSON</a>\n\t * contains a JSON-compatible serialization of the client data, the hash of which is\n\t * passed to the authenticator by the client in its call to either create() or get()\n\t * (i.e., the client data itself is not sent to the authenticator).\n\t * @return the client data JSON\n\t */\n\tpublic Bytes getClientDataJSON() {\n\t\treturn this.clientDataJSON;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorSelectionCriteria.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorselectioncriteria\">AuthenticatorAttachment</a>\n * can be used by\n * <a href=\"https://www.w3.org/TR/webauthn-3/#webauthn-relying-party\">WebAuthn Relying\n * Parties</a> to specify their requirements regarding authenticator attributes.\n *\n * There is no <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-requireresidentkey\">requireResidentKey</a>\n * property because it is only for backwards compatibility with WebAuthn Level 1.\n *\n * @author Rob Winch\n * @since 6.4\n * @see PublicKeyCredentialCreationOptions#getAuthenticatorSelection()\n */\npublic final class AuthenticatorSelectionCriteria implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5923595063546985635L;\n\n\tprivate final @Nullable AuthenticatorAttachment authenticatorAttachment;\n\n\tprivate final @Nullable ResidentKeyRequirement residentKey;\n\n\tprivate final @Nullable UserVerificationRequirement userVerification;\n\n\t// NOTE: There is no requireResidentKey property because it is only for backward\n\t// compatibility with WebAuthn Level 1\n\n\t/**\n\t * Creates a new instance\n\t * @param authenticatorAttachment the authenticator attachment\n\t * @param residentKey the resident key requirement\n\t * @param userVerification the user verification\n\t */\n\tprivate AuthenticatorSelectionCriteria(@Nullable AuthenticatorAttachment authenticatorAttachment,\n\t\t\t@Nullable ResidentKeyRequirement residentKey, @Nullable UserVerificationRequirement userVerification) {\n\t\tthis.authenticatorAttachment = authenticatorAttachment;\n\t\tthis.residentKey = residentKey;\n\t\tthis.userVerification = userVerification;\n\t}\n\n\t/**\n\t * If <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-authenticatorattachment\">\n\t * authenticatorAttachment</a> is present, eligible\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#authenticator\">authenticators</a> are\n\t * filtered to be only those authenticators attached with the specified\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#enum-attachment\">authenticator\n\t * attachment modality</a> (see also <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#sctn-authenticator-attachment-modality\">6.2.1\n\t * Authenticator Attachment Modality</a>).\n\t * @return the authenticator attachment\n\t */\n\tpublic @Nullable AuthenticatorAttachment getAuthenticatorAttachment() {\n\t\treturn this.authenticatorAttachment;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-residentkey\">residentKey</a>\n\t * specifies the extent to which the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#relying-party\">Relying Party</a> desires\n\t * to create a <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#client-side-discoverable-credential\">client-side\n\t * discoverable credential</a>.\n\t * @return the resident key requirement\n\t */\n\tpublic @Nullable ResidentKeyRequirement getResidentKey() {\n\t\treturn this.residentKey;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-userverification\">userVerification</a>\n\t * specifies the <a href=\"https://www.w3.org/TR/webauthn-3/#relying-party\">Relying\n\t * Party</a>'s requirements regarding\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#user-verification\">user verification</a>\n\t * for the <a href=\n\t * \"https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-create\">create()</a>\n\t * operation.\n\t * @return the user verification requirement\n\t */\n\tpublic @Nullable UserVerificationRequirement getUserVerification() {\n\t\treturn this.userVerification;\n\t}\n\n\t/**\n\t * Creates a new {@link AuthenticatorSelectionCriteriaBuilder}\n\t * @return a new {@link AuthenticatorSelectionCriteriaBuilder}\n\t */\n\tpublic static AuthenticatorSelectionCriteriaBuilder builder() {\n\t\treturn new AuthenticatorSelectionCriteriaBuilder();\n\t}\n\n\t/**\n\t * Creates a {@link AuthenticatorSelectionCriteria}\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class AuthenticatorSelectionCriteriaBuilder {\n\n\t\tprivate @Nullable AuthenticatorAttachment authenticatorAttachment;\n\n\t\tprivate @Nullable ResidentKeyRequirement residentKey;\n\n\t\tprivate @Nullable UserVerificationRequirement userVerification;\n\n\t\tprivate AuthenticatorSelectionCriteriaBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getAuthenticatorAttachment()} property.\n\t\t * @param authenticatorAttachment the authenticator attachment\n\t\t * @return the {@link AuthenticatorSelectionCriteriaBuilder}\n\t\t */\n\t\tpublic AuthenticatorSelectionCriteriaBuilder authenticatorAttachment(\n\t\t\t\t@Nullable AuthenticatorAttachment authenticatorAttachment) {\n\t\t\tthis.authenticatorAttachment = authenticatorAttachment;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getResidentKey()} property.\n\t\t * @param residentKey the resident key\n\t\t * @return the {@link AuthenticatorSelectionCriteriaBuilder}\n\t\t */\n\t\tpublic AuthenticatorSelectionCriteriaBuilder residentKey(@Nullable ResidentKeyRequirement residentKey) {\n\t\t\tthis.residentKey = residentKey;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getUserVerification()} property.\n\t\t * @param userVerification the user verification requirement\n\t\t * @return the {@link AuthenticatorSelectionCriteriaBuilder}\n\t\t */\n\t\tpublic AuthenticatorSelectionCriteriaBuilder userVerification(\n\t\t\t\t@Nullable UserVerificationRequirement userVerification) {\n\t\t\tthis.userVerification = userVerification;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a {@link AuthenticatorSelectionCriteria}\n\t\t * @return a new {@link AuthenticatorSelectionCriteria}\n\t\t */\n\t\tpublic AuthenticatorSelectionCriteria build() {\n\t\t\treturn new AuthenticatorSelectionCriteria(this.authenticatorAttachment, this.residentKey,\n\t\t\t\t\tthis.userVerification);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/AuthenticatorTransport.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport\">AuthenticatorTransport</a>\n * defines hints as to how clients might communicate with a particular authenticator in\n * order to obtain an assertion for a specific credential.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class AuthenticatorTransport implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -5617945441117386982L;\n\n\t/**\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-usb\">usbc</a>\n\t * indicates the respective authenticator can be contacted over removable USB.\n\t */\n\tpublic static final AuthenticatorTransport USB = new AuthenticatorTransport(\"usb\");\n\n\t/**\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-nfc\">nfc</a>\n\t * indicates the respective authenticator can be contacted over Near Field\n\t * Communication (NFC).\n\t */\n\tpublic static final AuthenticatorTransport NFC = new AuthenticatorTransport(\"nfc\");\n\n\t/**\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-ble\">ble</a>\n\t * Indicates the respective authenticator can be contacted over Bluetooth Smart\n\t * (Bluetooth Low Energy / BLE).\n\t */\n\tpublic static final AuthenticatorTransport BLE = new AuthenticatorTransport(\"ble\");\n\n\t/**\n\t * <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-smart-card\">smart-card</a>\n\t * indicates the respective authenticator can be contacted over ISO/IEC 7816 smart\n\t * card with contacts.\n\t */\n\tpublic static final AuthenticatorTransport SMART_CARD = new AuthenticatorTransport(\"smart-card\");\n\n\t/**\n\t * <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-hybrid\">hybrid</a>\n\t * indicates the respective authenticator can be contacted using a combination of\n\t * (often separate) data-transport and proximity mechanisms. This supports, for\n\t * example, authentication on a desktop computer using a smartphone.\n\t */\n\tpublic static final AuthenticatorTransport HYBRID = new AuthenticatorTransport(\"hybrid\");\n\n\t/**\n\t * <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-internal\">internal</a>\n\t * indicates the respective authenticator is contacted using a client device-specific\n\t * transport, i.e., it is a platform authenticator. These authenticators are not\n\t * removable from the client device.\n\t */\n\tpublic static final AuthenticatorTransport INTERNAL = new AuthenticatorTransport(\"internal\");\n\n\tprivate final String value;\n\n\tAuthenticatorTransport(String value) {\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Get's the value.\n\t * @return the value.\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t/**\n\t * Gets an instance of {@link AuthenticatorTransport}.\n\t * @param value the value of the {@link AuthenticatorTransport}\n\t * @return the {@link AuthenticatorTransport}\n\t */\n\tpublic static AuthenticatorTransport valueOf(String value) {\n\t\tswitch (value) {\n\t\t\tcase \"usb\":\n\t\t\t\treturn USB;\n\t\t\tcase \"nfc\":\n\t\t\t\treturn NFC;\n\t\t\tcase \"ble\":\n\t\t\t\treturn BLE;\n\t\t\tcase \"smart-card\":\n\t\t\t\treturn SMART_CARD;\n\t\t\tcase \"hybrid\":\n\t\t\t\treturn HYBRID;\n\t\t\tcase \"internal\":\n\t\t\t\treturn INTERNAL;\n\t\t\tdefault:\n\t\t\t\treturn new AuthenticatorTransport(value);\n\t\t}\n\t}\n\n\tpublic static AuthenticatorTransport[] values() {\n\t\treturn new AuthenticatorTransport[] { USB, NFC, BLE, HYBRID, INTERNAL };\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/Bytes.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Base64;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * An object representation of byte[].\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class Bytes implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3278138671365709777L;\n\n\tprivate static final SecureRandom RANDOM = new SecureRandom();\n\n\tprivate static final Base64.Encoder ENCODER = Base64.getUrlEncoder().withoutPadding();\n\n\tprivate static final Base64.Decoder DECODER = Base64.getUrlDecoder();\n\n\tprivate final byte[] bytes;\n\n\t/**\n\t * Creates a new instance\n\t * @param bytes the raw base64UrlString that will be encoded.\n\t */\n\tpublic Bytes(byte[] bytes) {\n\t\tAssert.notNull(bytes, \"bytes cannot be null\");\n\t\tthis.bytes = bytes;\n\t}\n\n\t/**\n\t * Gets the raw bytes.\n\t * @return the bytes\n\t */\n\tpublic byte[] getBytes() {\n\t\treturn Arrays.copyOf(this.bytes, this.bytes.length);\n\t}\n\n\t/**\n\t * Gets the bytes as Base64 URL encoded String.\n\t * @return\n\t */\n\tpublic String toBase64UrlString() {\n\t\treturn ENCODER.encodeToString(getBytes());\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof Bytes that) {\n\t\t\treturn that.toBase64UrlString().equals(toBase64UrlString());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn toBase64UrlString().hashCode();\n\t}\n\n\tpublic String toString() {\n\t\treturn \"Bytes[\" + toBase64UrlString() + \"]\";\n\t}\n\n\t/**\n\t * Creates a secure random {@link Bytes} with random bytes and sufficient entropy.\n\t * @return a new secure random generated {@link Bytes}\n\t */\n\tpublic static Bytes random() {\n\t\tbyte[] bytes = new byte[32];\n\t\tRANDOM.nextBytes(bytes);\n\t\treturn new Bytes(bytes);\n\t}\n\n\t/**\n\t * Creates a new instance from a base64 url string.\n\t * @param base64UrlString the base64 url string\n\t * @return the {@link Bytes}\n\t */\n\tpublic static Bytes fromBase64(@Nullable String base64UrlString) {\n\t\tbyte[] bytes = DECODER.decode(base64UrlString);\n\t\treturn new Bytes(bytes);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/COSEAlgorithmIdentifier.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier\">COSEAlgorithmIdentifier</a> is\n * used to identify a cryptographic algorithm.\n *\n * @author Rob Winch\n * @since 6.4\n * @see PublicKeyCredentialParameters#getAlg()\n */\npublic final class COSEAlgorithmIdentifier implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -4907140472964185350L;\n\n\tpublic static final COSEAlgorithmIdentifier EdDSA = new COSEAlgorithmIdentifier(-8);\n\n\tpublic static final COSEAlgorithmIdentifier ES256 = new COSEAlgorithmIdentifier(-7);\n\n\tpublic static final COSEAlgorithmIdentifier ES384 = new COSEAlgorithmIdentifier(-35);\n\n\tpublic static final COSEAlgorithmIdentifier ES512 = new COSEAlgorithmIdentifier(-36);\n\n\tpublic static final COSEAlgorithmIdentifier RS256 = new COSEAlgorithmIdentifier(-257);\n\n\tpublic static final COSEAlgorithmIdentifier RS384 = new COSEAlgorithmIdentifier(-258);\n\n\tpublic static final COSEAlgorithmIdentifier RS512 = new COSEAlgorithmIdentifier(-259);\n\n\tpublic static final COSEAlgorithmIdentifier RS1 = new COSEAlgorithmIdentifier(-65535);\n\n\tprivate final long value;\n\n\tprivate COSEAlgorithmIdentifier(long value) {\n\t\tthis.value = value;\n\t}\n\n\tpublic long getValue() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn String.valueOf(this.value);\n\t}\n\n\tpublic static COSEAlgorithmIdentifier[] values() {\n\t\treturn new COSEAlgorithmIdentifier[] { EdDSA, ES256, ES384, ES512, RS256, RS384, RS512, RS1 };\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/CredProtectAuthenticationExtensionsClientInput.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * Implements <a href=\n * \"https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension\">\n * Credential Protection (credProtect)</a>.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class CredProtectAuthenticationExtensionsClientInput\n\t\timplements AuthenticationExtensionsClientInput<CredProtectAuthenticationExtensionsClientInput.CredProtect> {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -6418175591005843455L;\n\n\tprivate final CredProtect input;\n\n\tpublic CredProtectAuthenticationExtensionsClientInput(CredProtect input) {\n\t\tthis.input = input;\n\t}\n\n\t@Override\n\tpublic String getExtensionId() {\n\t\treturn \"credProtect\";\n\t}\n\n\t@Override\n\tpublic CredProtect getInput() {\n\t\treturn this.input;\n\t}\n\n\tpublic static class CredProtect implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 109597301115842688L;\n\n\t\tprivate final ProtectionPolicy credProtectionPolicy;\n\n\t\tprivate final boolean enforceCredentialProtectionPolicy;\n\n\t\tpublic CredProtect(ProtectionPolicy credProtectionPolicy, boolean enforceCredentialProtectionPolicy) {\n\t\t\tthis.enforceCredentialProtectionPolicy = enforceCredentialProtectionPolicy;\n\t\t\tthis.credProtectionPolicy = credProtectionPolicy;\n\t\t}\n\n\t\tpublic boolean isEnforceCredentialProtectionPolicy() {\n\t\t\treturn this.enforceCredentialProtectionPolicy;\n\t\t}\n\n\t\tpublic ProtectionPolicy getCredProtectionPolicy() {\n\t\t\treturn this.credProtectionPolicy;\n\t\t}\n\n\t\tpublic enum ProtectionPolicy {\n\n\t\t\tUSER_VERIFICATION_OPTIONAL, USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST, USER_VERIFICATION_REQUIRED\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/CredentialPropertiesOutput.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-credentialpropertiesoutput\">CredentialPropertiesOutput</a>\n * is the Client extension output.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class CredentialPropertiesOutput\n\t\timplements AuthenticationExtensionsClientOutput<CredentialPropertiesOutput.ExtensionOutput> {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3201699313968303331L;\n\n\t/**\n\t * The extension id.\n\t */\n\tpublic static final String EXTENSION_ID = \"credProps\";\n\n\tprivate final ExtensionOutput output;\n\n\t/**\n\t * Creates a new instance.\n\t * @param rk is the resident key is discoverable\n\t */\n\tpublic CredentialPropertiesOutput(boolean rk) {\n\t\tthis.output = new ExtensionOutput(rk);\n\t}\n\n\t@Override\n\tpublic String getExtensionId() {\n\t\treturn EXTENSION_ID;\n\t}\n\n\t@Override\n\tpublic ExtensionOutput getOutput() {\n\t\treturn this.output;\n\t}\n\n\t/**\n\t * The output for {@link CredentialPropertiesOutput}\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t * @see #getOutput()\n\t */\n\tpublic static final class ExtensionOutput implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 4557406414847424019L;\n\n\t\tprivate final boolean rk;\n\n\t\tprivate ExtensionOutput(boolean rk) {\n\t\t\tthis.rk = rk;\n\t\t}\n\n\t\t/**\n\t\t * This OPTIONAL property, known abstractly as the resident key credential\n\t\t * property (i.e., client-side discoverable credential property), is a Boolean\n\t\t * value indicating whether the PublicKeyCredential returned as a result of a\n\t\t * registration ceremony is a client-side discoverable credential.\n\t\t * @return is resident key credential property\n\t\t */\n\t\tpublic boolean isRk() {\n\t\t\treturn this.rk;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/CredentialRecord.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.time.Instant;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Represents a <a href=\"https://www.w3.org/TR/webauthn-3/#credential-record\">Credential\n * Record</a> that is stored by the Relying Party\n * <a href=\"https://www.w3.org/TR/webauthn-3/#reg-ceremony-store-credential-record\">after\n * successful registration</a>.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface CredentialRecord {\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-type\">credential.type</a>\n\t * @return\n\t */\n\t@Nullable PublicKeyCredentialType getCredentialType();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-id\">credential.id</a>.\n\t * @return\n\t */\n\tBytes getCredentialId();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-publickey\">publicKey</a>\n\t * @return\n\t */\n\tPublicKeyCose getPublicKey();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-signcount\">authData.signCount</a>\n\t * @return\n\t */\n\tlong getSignatureCount();\n\n\t/**\n\t * <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-uvinitialized\">uvInitialized</a>\n\t * is the value of the UV (user verified) flag in authData and indicates whether any\n\t * credential from this public key credential source has had the UV flag set.\n\t * @return\n\t */\n\tboolean isUvInitialized();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-transports\">transports</a>\n\t * is the value returned from {@code response.getTransports()}.\n\t * @return\n\t */\n\tSet<AuthenticatorTransport> getTransports();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-backupeligible\">backupElgible</a>\n\t * flag is the same as the BE flag in authData.\n\t * @return\n\t */\n\tboolean isBackupEligible();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-backupstate\">backupState</a>\n\t * flag is the same as the BS flag in authData.\n\t * @return\n\t */\n\tboolean isBackupState();\n\n\t/**\n\t * A reference to the associated {@link PublicKeyCredentialUserEntity#getId()}\n\t * @return\n\t */\n\tBytes getUserEntityUserId();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-attestationobject\">attestationObject</a>\n\t * is the value of the attestationObject attribute when the public key credential\n\t * source was registered.\n\t * @return the attestationObject\n\t */\n\t@Nullable Bytes getAttestationObject();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#abstract-opdef-credential-record-attestationclientdatajson\">attestationClientDataJSON</a>\n\t * is the value of the attestationObject attribute when the public key credential\n\t * source was registered.\n\t * @return\n\t */\n\t@Nullable Bytes getAttestationClientDataJSON();\n\n\t/**\n\t * A human-readable label for this {@link CredentialRecord} assigned by the user.\n\t * @return a label\n\t */\n\tString getLabel();\n\n\t/**\n\t * The last time this {@link CredentialRecord} was used.\n\t * @return the last time this {@link CredentialRecord} was used.\n\t */\n\tInstant getLastUsed();\n\n\t/**\n\t * When this {@link CredentialRecord} was created.\n\t * @return When this {@link CredentialRecord} was created.\n\t */\n\tInstant getCreated();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutableAuthenticationExtensionsClientInput.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\n\n/**\n * An immutable {@link AuthenticationExtensionsClientInput}.\n *\n * @param <T> the input type\n * @author Rob Winch\n * @since 6.4\n * @see AuthenticationExtensionsClientInputs\n */\npublic class ImmutableAuthenticationExtensionsClientInput<T> implements AuthenticationExtensionsClientInput<T> {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1738152485672656808L;\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-authenticator-credential-properties-extension\n\t */\n\tpublic static final AuthenticationExtensionsClientInput<Boolean> credProps = new ImmutableAuthenticationExtensionsClientInput<>(\n\t\t\t\"credProps\", true);\n\n\tprivate final String extensionId;\n\n\tprivate final T input;\n\n\t/**\n\t * Creates a new instance\n\t * @param extensionId the extension id.\n\t * @param input the input.\n\t */\n\tpublic ImmutableAuthenticationExtensionsClientInput(String extensionId, T input) {\n\t\tthis.extensionId = extensionId;\n\t\tthis.input = input;\n\t}\n\n\t@Override\n\tpublic String getExtensionId() {\n\t\treturn this.extensionId;\n\t}\n\n\t@Override\n\tpublic T getInput() {\n\t\treturn this.input;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutableAuthenticationExtensionsClientInputs.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * An immutable implementation of {@link AuthenticationExtensionsClientInputs}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class ImmutableAuthenticationExtensionsClientInputs implements AuthenticationExtensionsClientInputs {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 4277817521578485720L;\n\n\tprivate final List<AuthenticationExtensionsClientInput> inputs;\n\n\tpublic ImmutableAuthenticationExtensionsClientInputs(List<AuthenticationExtensionsClientInput> inputs) {\n\t\tthis.inputs = inputs;\n\t}\n\n\tpublic ImmutableAuthenticationExtensionsClientInputs(AuthenticationExtensionsClientInput... inputs) {\n\t\tthis(Arrays.asList(inputs));\n\t}\n\n\t@Override\n\tpublic List<AuthenticationExtensionsClientInput> getInputs() {\n\t\treturn this.inputs;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutableAuthenticationExtensionsClientOutputs.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * An immutable implementation of {@link AuthenticationExtensionsClientOutputs}.\n *\n * @author Rob Winch\n */\npublic class ImmutableAuthenticationExtensionsClientOutputs implements AuthenticationExtensionsClientOutputs {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -4656390173585180393L;\n\n\tprivate final List<AuthenticationExtensionsClientOutput<?>> outputs;\n\n\tpublic ImmutableAuthenticationExtensionsClientOutputs(List<AuthenticationExtensionsClientOutput<?>> outputs) {\n\t\tthis.outputs = outputs;\n\t}\n\n\tpublic ImmutableAuthenticationExtensionsClientOutputs(AuthenticationExtensionsClientOutput<?>... outputs) {\n\t\tthis(Arrays.asList(outputs));\n\t}\n\n\t@Override\n\tpublic List<AuthenticationExtensionsClientOutput<?>> getOutputs() {\n\t\treturn this.outputs;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutableCredentialRecord.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.time.Instant;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * An immutable {@link CredentialRecord}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class ImmutableCredentialRecord implements CredentialRecord {\n\n\tprivate final @Nullable PublicKeyCredentialType credentialType;\n\n\tprivate final Bytes credentialId;\n\n\tprivate final Bytes userEntityUserId;\n\n\tprivate final PublicKeyCose publicKey;\n\n\tprivate final long signatureCount;\n\n\tprivate final boolean uvInitialized;\n\n\tprivate final Set<AuthenticatorTransport> transports;\n\n\tprivate final boolean backupEligible;\n\n\tprivate final boolean backupState;\n\n\tprivate final @Nullable Bytes attestationObject;\n\n\tprivate final @Nullable Bytes attestationClientDataJSON;\n\n\tprivate final Instant created;\n\n\tprivate final Instant lastUsed;\n\n\tprivate final String label;\n\n\tprivate ImmutableCredentialRecord(@Nullable PublicKeyCredentialType credentialType, Bytes credentialId,\n\t\t\tBytes userEntityUserId, PublicKeyCose publicKey, long signatureCount, boolean uvInitialized,\n\t\t\tSet<AuthenticatorTransport> transports, boolean backupEligible, boolean backupState,\n\t\t\t@Nullable Bytes attestationObject, @Nullable Bytes attestationClientDataJSON, Instant created,\n\t\t\tInstant lastUsed, String label) {\n\t\tthis.credentialType = credentialType;\n\t\tthis.credentialId = credentialId;\n\t\tthis.userEntityUserId = userEntityUserId;\n\t\tthis.publicKey = publicKey;\n\t\tthis.signatureCount = signatureCount;\n\t\tthis.uvInitialized = uvInitialized;\n\t\tthis.transports = transports;\n\t\tthis.backupEligible = backupEligible;\n\t\tthis.backupState = backupState;\n\t\tthis.attestationObject = attestationObject;\n\t\tthis.attestationClientDataJSON = attestationClientDataJSON;\n\t\tthis.created = created;\n\t\tthis.lastUsed = lastUsed;\n\t\tthis.label = label;\n\t}\n\n\t@Override\n\tpublic @Nullable PublicKeyCredentialType getCredentialType() {\n\t\treturn this.credentialType;\n\t}\n\n\t@Override\n\tpublic Bytes getCredentialId() {\n\t\treturn this.credentialId;\n\t}\n\n\t@Override\n\tpublic Bytes getUserEntityUserId() {\n\t\treturn this.userEntityUserId;\n\t}\n\n\t@Override\n\tpublic PublicKeyCose getPublicKey() {\n\t\treturn this.publicKey;\n\t}\n\n\t@Override\n\tpublic long getSignatureCount() {\n\t\treturn this.signatureCount;\n\t}\n\n\t@Override\n\tpublic boolean isUvInitialized() {\n\t\treturn this.uvInitialized;\n\t}\n\n\t@Override\n\tpublic Set<AuthenticatorTransport> getTransports() {\n\t\treturn this.transports;\n\t}\n\n\t@Override\n\tpublic boolean isBackupEligible() {\n\t\treturn this.backupEligible;\n\t}\n\n\t@Override\n\tpublic boolean isBackupState() {\n\t\treturn this.backupState;\n\t}\n\n\t@Override\n\tpublic @Nullable Bytes getAttestationObject() {\n\t\treturn this.attestationObject;\n\t}\n\n\t@Override\n\tpublic @Nullable Bytes getAttestationClientDataJSON() {\n\t\treturn this.attestationClientDataJSON;\n\t}\n\n\t@Override\n\tpublic Instant getCreated() {\n\t\treturn this.created;\n\t}\n\n\t@Override\n\tpublic Instant getLastUsed() {\n\t\treturn this.lastUsed;\n\t}\n\n\t@Override\n\tpublic String getLabel() {\n\t\treturn this.label;\n\t}\n\n\tpublic static ImmutableCredentialRecordBuilder builder() {\n\t\treturn new ImmutableCredentialRecordBuilder();\n\t}\n\n\tpublic static ImmutableCredentialRecordBuilder fromCredentialRecord(CredentialRecord credentialRecord) {\n\t\treturn new ImmutableCredentialRecordBuilder(credentialRecord);\n\t}\n\n\tpublic static final class ImmutableCredentialRecordBuilder {\n\n\t\tprivate @Nullable PublicKeyCredentialType credentialType;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Bytes credentialId;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Bytes userEntityUserId;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate PublicKeyCose publicKey;\n\n\t\tprivate long signatureCount;\n\n\t\tprivate boolean uvInitialized;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Set<AuthenticatorTransport> transports;\n\n\t\tprivate boolean backupEligible;\n\n\t\tprivate boolean backupState;\n\n\t\tprivate @Nullable Bytes attestationObject;\n\n\t\tprivate @Nullable Bytes attestationClientDataJSON;\n\n\t\tprivate Instant created = Instant.now();\n\n\t\tprivate Instant lastUsed = this.created;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate String label;\n\n\t\tprivate ImmutableCredentialRecordBuilder() {\n\t\t}\n\n\t\tprivate ImmutableCredentialRecordBuilder(CredentialRecord other) {\n\t\t\tthis.credentialType = other.getCredentialType();\n\t\t\tthis.credentialId = other.getCredentialId();\n\t\t\tthis.userEntityUserId = other.getUserEntityUserId();\n\t\t\tthis.publicKey = other.getPublicKey();\n\t\t\tthis.signatureCount = other.getSignatureCount();\n\t\t\tthis.uvInitialized = other.isUvInitialized();\n\t\t\tthis.transports = other.getTransports();\n\t\t\tthis.backupEligible = other.isBackupEligible();\n\t\t\tthis.backupState = other.isBackupState();\n\t\t\tthis.attestationObject = other.getAttestationObject();\n\t\t\tthis.attestationClientDataJSON = other.getAttestationClientDataJSON();\n\t\t\tthis.created = other.getCreated();\n\t\t\tthis.lastUsed = other.getLastUsed();\n\t\t\tthis.label = other.getLabel();\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder credentialType(@Nullable PublicKeyCredentialType credentialType) {\n\t\t\tthis.credentialType = credentialType;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder credentialId(Bytes credentialId) {\n\t\t\tthis.credentialId = credentialId;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder userEntityUserId(Bytes userEntityUserId) {\n\t\t\tthis.userEntityUserId = userEntityUserId;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder publicKey(PublicKeyCose publicKey) {\n\t\t\tthis.publicKey = publicKey;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder signatureCount(long signatureCount) {\n\t\t\tthis.signatureCount = signatureCount;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder uvInitialized(boolean uvInitialized) {\n\t\t\tthis.uvInitialized = uvInitialized;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder transports(Set<AuthenticatorTransport> transports) {\n\t\t\tthis.transports = transports;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder backupEligible(boolean backupEligible) {\n\t\t\tthis.backupEligible = backupEligible;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder backupState(boolean backupState) {\n\t\t\tthis.backupState = backupState;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder attestationObject(@Nullable Bytes attestationObject) {\n\t\t\tthis.attestationObject = attestationObject;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder attestationClientDataJSON(@Nullable Bytes attestationClientDataJSON) {\n\t\t\tthis.attestationClientDataJSON = attestationClientDataJSON;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder created(Instant created) {\n\t\t\tthis.created = created;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder lastUsed(Instant lastUsed) {\n\t\t\tthis.lastUsed = lastUsed;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecordBuilder label(String label) {\n\t\t\tthis.label = label;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic ImmutableCredentialRecord build() {\n\t\t\treturn new ImmutableCredentialRecord(this.credentialType, this.credentialId, this.userEntityUserId,\n\t\t\t\t\tthis.publicKey, this.signatureCount, this.uvInitialized, this.transports, this.backupEligible,\n\t\t\t\t\tthis.backupState, this.attestationObject, this.attestationClientDataJSON, this.created,\n\t\t\t\t\tthis.lastUsed, this.label);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCose.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.util.Arrays;\nimport java.util.Base64;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * An immutable {@link PublicKeyCose}\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class ImmutablePublicKeyCose implements PublicKeyCose {\n\n\tprivate final byte[] bytes;\n\n\t/**\n\t * Creates a new instance.\n\t * @param bytes the raw bytes of the public key.\n\t */\n\tpublic ImmutablePublicKeyCose(byte @Nullable [] bytes) {\n\t\tAssert.notNull(bytes, \"bytes cannot be null\");\n\t\tthis.bytes = Arrays.copyOf(bytes, bytes.length);\n\t}\n\n\t@Override\n\tpublic byte[] getBytes() {\n\t\treturn Arrays.copyOf(this.bytes, this.bytes.length);\n\t}\n\n\t/**\n\t * Creates a new instance form a Base64 URL encoded String\n\t * @param base64EncodedString the base64EncodedString encoded String\n\t * @return\n\t */\n\tpublic static ImmutablePublicKeyCose fromBase64(String base64EncodedString) {\n\t\tbyte[] decode = Base64.getUrlDecoder().decode(base64EncodedString);\n\t\treturn new ImmutablePublicKeyCose(decode);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/ImmutablePublicKeyCredentialUserEntity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity\">PublicKeyCredentialUserEntity</a>\n * is used to supply additional\n * <a href=\"https://www.w3.org/TR/webauthn-3/#user-account\">user account</a> attributes\n * when creating a new credential.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class ImmutablePublicKeyCredentialUserEntity implements PublicKeyCredentialUserEntity {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -3438693960347279759L;\n\n\t/**\n\t * When inherited by PublicKeyCredentialUserEntity, it is a human-palatable identifier\n\t * for a user account. It is intended only for display, i.e., aiding the user in\n\t * determining the difference between user accounts with similar displayNames. For\n\t * example, \"alexm\", \"alex.mueller@example.com\" or \"+14255551234\".\n\t *\n\t * The Relying Party MAY let the user choose this value. The Relying Party SHOULD\n\t * perform enforcement, as prescribed in Section 3.4.3 of [RFC8265] for the\n\t * UsernameCasePreserved Profile of the PRECIS IdentifierClass [RFC8264], when setting\n\t * name's value, or displaying the value to the user.\n\t *\n\t * This string MAY contain language and direction metadata. Relying Parties SHOULD\n\t * consider providing this information. See 6.4.2 Language and Direction Encoding\n\t * about how this metadata is encoded.\n\t *\n\t * Clients SHOULD perform enforcement, as prescribed in Section 3.4.3 of [RFC8265] for\n\t * the UsernameCasePreserved Profile of the PRECIS IdentifierClass [RFC8264], on\n\t * name's value prior to displaying the value to the user or including the value as a\n\t * parameter of the authenticatorMakeCredential operation.\n\t */\n\tprivate final String name;\n\n\t/**\n\t * The user handle of the user account entity. A user handle is an opaque byte\n\t * sequence with a maximum size of 64 bytes, and is not meant to be displayed to the\n\t * user.\n\t *\n\t * To ensure secure operation, authentication and authorization decisions MUST be made\n\t * on the basis of this id member, not the displayName nor name members. See Section\n\t * 6.1 of [RFC8266].\n\t *\n\t * The user handle MUST NOT contain personally identifying information about the user,\n\t * such as a username or e-mail address; see 14.6.1 User Handle Contents for details.\n\t * The user handle MUST NOT be empty, though it MAY be null.\n\t *\n\t * Note: the user handle ought not be a constant value across different accounts, even\n\t * for non-discoverable credentials, because some authenticators always create\n\t * discoverable credentials. Thus a constant user handle would prevent a user from\n\t * using such an authenticator with more than one account at the Relying Party.\n\t */\n\tprivate final Bytes id;\n\n\t/**\n\t * A human-palatable name for the user account, intended only for display. The Relying\n\t * Party SHOULD let the user choose this, and SHOULD NOT restrict the choice more than\n\t * necessary.\n\t *\n\t * Relying Parties SHOULD perform enforcement, as prescribed in Section 2.3 of\n\t * [RFC8266] for the Nickname Profile of the PRECIS FreeformClass [RFC8264], when\n\t * setting displayName's value, or displaying the value to the user.\n\t *\n\t * This string MAY contain language and direction metadata. Relying Parties SHOULD\n\t * consider providing this information. See 6.4.2 Language and Direction Encoding\n\t * about how this metadata is encoded.\n\t *\n\t * Clients SHOULD perform enforcement, as prescribed in Section 2.3 of [RFC8266] for\n\t * the Nickname Profile of the PRECIS FreeformClass [RFC8264], on displayName's value\n\t * prior to displaying the value to the user or including the value as a parameter of\n\t * the authenticatorMakeCredential operation.\n\t *\n\t * When clients, client platforms, or authenticators display a displayName's value,\n\t * they should always use UI elements to provide a clear boundary around the displayed\n\t * value, and not allow overflow into other elements [css-overflow-3].\n\t *\n\t * Authenticators MUST accept and store a 64-byte minimum length for a displayName\n\t * member's value. Authenticators MAY truncate a displayName member's value so that it\n\t * fits within 64 bytes. See 6.4.1 String Truncation about truncation and other\n\t * considerations.\n\t */\n\tprivate final @Nullable String displayName;\n\n\tprivate ImmutablePublicKeyCredentialUserEntity(String name, Bytes id, @Nullable String displayName) {\n\t\tthis.name = name;\n\t\tthis.id = id;\n\t\tthis.displayName = displayName;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\t@Override\n\tpublic Bytes getId() {\n\t\treturn this.id;\n\t}\n\n\t@Override\n\tpublic @Nullable String getDisplayName() {\n\t\treturn this.displayName;\n\t}\n\n\t/**\n\t * Create a new {@link PublicKeyCredentialUserEntityBuilder}\n\t * @return a new {@link PublicKeyCredentialUserEntityBuilder}\n\t */\n\tpublic static PublicKeyCredentialUserEntityBuilder builder() {\n\t\treturn new PublicKeyCredentialUserEntityBuilder();\n\t}\n\n\t/**\n\t * Used to build {@link PublicKeyCredentialUserEntity}.\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class PublicKeyCredentialUserEntityBuilder {\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate String name;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Bytes id;\n\n\t\tprivate @Nullable String displayName;\n\n\t\tprivate PublicKeyCredentialUserEntityBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getName()} property.\n\t\t * @param name the name\n\t\t * @return the {@link PublicKeyCredentialUserEntityBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialUserEntityBuilder name(String name) {\n\t\t\tthis.name = name;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getId()} property.\n\t\t * @param id the id\n\t\t * @return the {@link PublicKeyCredentialUserEntityBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialUserEntityBuilder id(Bytes id) {\n\t\t\tthis.id = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getDisplayName()} property.\n\t\t * @param displayName the display name\n\t\t * @return the {@link PublicKeyCredentialUserEntityBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialUserEntityBuilder displayName(@Nullable String displayName) {\n\t\t\tthis.displayName = displayName;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link PublicKeyCredentialUserEntity}\n\t\t * @return a new {@link PublicKeyCredentialUserEntity}\n\t\t */\n\t\tpublic PublicKeyCredentialUserEntity build() {\n\t\t\treturn new ImmutablePublicKeyCredentialUserEntity(this.name, this.id, this.displayName);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCose.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\n/**\n * A <a href=\"https://www.w3.org/TR/webauthn-3/#sctn-encoded-credPubKey-examples\">COSE\n * encoded public key</a>.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface PublicKeyCose {\n\n\t/**\n\t * The byes of a COSE encoded public key.\n\t * @return the bytes of a COSE encoded public key.\n\t */\n\tbyte[] getBytes();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredential.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * <a href=\"https://www.w3.org/TR/webauthn-3/#iface-pkcredential\">PublicKeyCredential</a>\n * contains the attributes that are returned to the caller when a new credential is\n * created, or a new assertion is requested.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class PublicKeyCredential<R extends AuthenticatorResponse> implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1864035469276082606L;\n\n\tprivate final String id;\n\n\tprivate final @Nullable PublicKeyCredentialType type;\n\n\tprivate final Bytes rawId;\n\n\tprivate final R response;\n\n\tprivate final @Nullable AuthenticatorAttachment authenticatorAttachment;\n\n\tprivate final @Nullable AuthenticationExtensionsClientOutputs clientExtensionResults;\n\n\tprivate PublicKeyCredential(String id, @Nullable PublicKeyCredentialType type, Bytes rawId, R response,\n\t\t\t@Nullable AuthenticatorAttachment authenticatorAttachment,\n\t\t\t@Nullable AuthenticationExtensionsClientOutputs clientExtensionResults) {\n\t\tthis.id = id;\n\t\tthis.type = type;\n\t\tthis.rawId = rawId;\n\t\tthis.response = response;\n\t\tthis.authenticatorAttachment = authenticatorAttachment;\n\t\tthis.clientExtensionResults = clientExtensionResults;\n\t}\n\n\t/**\n\t * The\n\t * <a href=\"https://www.w3.org/TR/credential-management-1/#dom-credential-id\">id</a>\n\t * attribute is inherited from Credential, though PublicKeyCredential overrides\n\t * Credential's getter, instead returning the base64url encoding of the data contained\n\t * in the object's [[identifier]] internal slot.\n\t */\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/credential-management-1/#dom-credential-type\">type</a>\n\t * attribute returns the value of the object's interface object's [[type]] slot, which\n\t * specifies the credential type represented by this object.\n\t * @return the credential type\n\t */\n\tpublic @Nullable PublicKeyCredentialType getType() {\n\t\treturn this.type;\n\t}\n\n\t/**\n\t * The\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-rawid\">rawId</a>\n\t * returns the raw identifier.\n\t * @return the raw id\n\t */\n\tpublic Bytes getRawId() {\n\t\treturn this.rawId;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-response\">response</a>\n\t * to the client's request to either create a public key credential, or generate an\n\t * authentication assertion.\n\t * @return the response\n\t */\n\tpublic R getResponse() {\n\t\treturn this.response;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-authenticatorattachment\">authenticatorAttachment</a>\n\t * reports the <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#authenticator-attachment-modality\">authenticator\n\t * attachment modality</a> in effect at the time the navigator.credentials.create() or\n\t * navigator.credentials.get() methods successfully complete.\n\t * @return the authenticator attachment\n\t */\n\tpublic @Nullable AuthenticatorAttachment getAuthenticatorAttachment() {\n\t\treturn this.authenticatorAttachment;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-getclientextensionresults\">clientExtensionsResults</a>\n\t * is a mapping of extension identifier to client extension output.\n\t * @return the extension results\n\t */\n\tpublic @Nullable AuthenticationExtensionsClientOutputs getClientExtensionResults() {\n\t\treturn this.clientExtensionResults;\n\t}\n\n\t/**\n\t * Creates a new {@link PublicKeyCredentialBuilder}\n\t * @param <T> the response type\n\t * @return the {@link PublicKeyCredentialBuilder}\n\t */\n\tpublic static <T extends AuthenticatorResponse> PublicKeyCredentialBuilder<T> builder() {\n\t\treturn new PublicKeyCredentialBuilder<>();\n\t}\n\n\t/**\n\t * The {@link PublicKeyCredentialBuilder}\n\t *\n\t * @param <R> the response type\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class PublicKeyCredentialBuilder<R extends AuthenticatorResponse> {\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate String id;\n\n\t\tprivate @Nullable PublicKeyCredentialType type;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Bytes rawId;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate R response;\n\n\t\tprivate @Nullable AuthenticatorAttachment authenticatorAttachment;\n\n\t\tprivate @Nullable AuthenticationExtensionsClientOutputs clientExtensionResults;\n\n\t\tprivate PublicKeyCredentialBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getId()} property\n\t\t * @param id the id\n\t\t * @return the PublicKeyCredentialBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialBuilder id(String id) {\n\t\t\tthis.id = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getType()} property.\n\t\t * @param type the type\n\t\t * @return the PublicKeyCredentialBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialBuilder type(@Nullable PublicKeyCredentialType type) {\n\t\t\tthis.type = type;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getRawId()} property.\n\t\t * @param rawId the raw id\n\t\t * @return the PublicKeyCredentialBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialBuilder rawId(Bytes rawId) {\n\t\t\tthis.rawId = rawId;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getResponse()} property.\n\t\t * @param response the response\n\t\t * @return the PublicKeyCredentialBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialBuilder response(R response) {\n\t\t\tthis.response = response;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getAuthenticatorAttachment()} property.\n\t\t * @param authenticatorAttachment the authenticator attachment\n\t\t * @return the PublicKeyCredentialBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialBuilder authenticatorAttachment(\n\t\t\t\t@Nullable AuthenticatorAttachment authenticatorAttachment) {\n\t\t\tthis.authenticatorAttachment = authenticatorAttachment;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getClientExtensionResults()} property.\n\t\t * @param clientExtensionResults the client extension results\n\t\t * @return the PublicKeyCredentialBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialBuilder clientExtensionResults(\n\t\t\t\t@Nullable AuthenticationExtensionsClientOutputs clientExtensionResults) {\n\t\t\tthis.clientExtensionResults = clientExtensionResults;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Creates a new {@link PublicKeyCredential}\n\t\t * @return a new {@link PublicKeyCredential}\n\t\t */\n\t\tpublic PublicKeyCredential<R> build() {\n\t\t\tAssert.notNull(this.id, \"id cannot be null\");\n\t\t\tAssert.notNull(this.rawId, \"rawId cannot be null\");\n\t\t\tAssert.notNull(this.response, \"response cannot be null\");\n\t\t\treturn new PublicKeyCredential(this.id, this.type, this.rawId, this.response, this.authenticatorAttachment,\n\t\t\t\t\tthis.clientExtensionResults);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialCreationOptions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Represents the <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions\">PublicKeyCredentialCreationOptions</a>\n * which is an argument to <a href=\n * \"https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-create\">creating</a>\n * a new credential.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class PublicKeyCredentialCreationOptions implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 800506727675279143L;\n\n\tprivate final PublicKeyCredentialRpEntity rp;\n\n\tprivate final PublicKeyCredentialUserEntity user;\n\n\tprivate final Bytes challenge;\n\n\tprivate final List<PublicKeyCredentialParameters> pubKeyCredParams;\n\n\tprivate final @Nullable Duration timeout;\n\n\tprivate final List<PublicKeyCredentialDescriptor> excludeCredentials;\n\n\tprivate final AuthenticatorSelectionCriteria authenticatorSelection;\n\n\tprivate final @Nullable AttestationConveyancePreference attestation;\n\n\tprivate final @Nullable AuthenticationExtensionsClientInputs extensions;\n\n\tprivate PublicKeyCredentialCreationOptions(PublicKeyCredentialRpEntity rp, PublicKeyCredentialUserEntity user,\n\t\t\tBytes challenge, List<PublicKeyCredentialParameters> pubKeyCredParams, @Nullable Duration timeout,\n\t\t\tList<PublicKeyCredentialDescriptor> excludeCredentials,\n\t\t\tAuthenticatorSelectionCriteria authenticatorSelection,\n\t\t\t@Nullable AttestationConveyancePreference attestation,\n\t\t\t@Nullable AuthenticationExtensionsClientInputs extensions) {\n\t\tthis.rp = rp;\n\t\tthis.user = user;\n\t\tthis.challenge = challenge;\n\t\tthis.pubKeyCredParams = pubKeyCredParams;\n\t\tthis.timeout = timeout;\n\t\tthis.excludeCredentials = excludeCredentials;\n\t\tthis.authenticatorSelection = authenticatorSelection;\n\t\tthis.attestation = attestation;\n\t\tthis.extensions = extensions;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-rp\">rp</a>\n\t * property contains data about the Relying Party responsible for the request.\n\t * @return the relying party\n\t */\n\tpublic PublicKeyCredentialRpEntity getRp() {\n\t\treturn this.rp;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-user\">user</a>\n\t * contains names and an identifier for the user account performing the registration.\n\t * @return the user\n\t */\n\tpublic PublicKeyCredentialUserEntity getUser() {\n\t\treturn this.user;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-challenge\">challenge</a>\n\t * specifies the challenge that the authenticator signs, along with other data, when\n\t * producing an attestation object for the newly created credential.\n\t * @return the challenge\n\t */\n\tpublic Bytes getChallenge() {\n\t\treturn this.challenge;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams\">publicKeyCredParams</a>\n\t * params list the key types and signature algorithms the Relying Party Supports,\n\t * ordered from most preferred to least preferred.\n\t * @return the public key credential parameters\n\t */\n\tpublic List<PublicKeyCredentialParameters> getPubKeyCredParams() {\n\t\treturn this.pubKeyCredParams;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-timeout\">timeout</a>\n\t * property specifies a time, in milliseconds, that the Relying Party is willing to\n\t * wait for the call to complete.\n\t * @return the timeout\n\t */\n\tpublic @Nullable Duration getTimeout() {\n\t\treturn this.timeout;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-excludecredentials\">excludeCredentials</a>\n\t * property is the OPTIONAL member used by the Relying Party to list any existing\n\t * credentials mapped to this user account (as identified by user.id).\n\t * @return exclude credentials\n\t */\n\tpublic List<PublicKeyCredentialDescriptor> getExcludeCredentials() {\n\t\treturn this.excludeCredentials;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-authenticatorselection\">authenticatorSelection</a>\n\t * property is an OPTIONAL member used by the Relying Party to list any existing\n\t * credentials mapped to this user account (as identified by user.id).\n\t * @return the authenticatorSelection\n\t */\n\tpublic AuthenticatorSelectionCriteria getAuthenticatorSelection() {\n\t\treturn this.authenticatorSelection;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-attestation\">attestation</a>\n\t * property is an OPTIONAL member used by the Relying Party to specify a preference\n\t * regarding attestation conveyance.\n\t * @return the attestation preference\n\t */\n\tpublic @Nullable AttestationConveyancePreference getAttestation() {\n\t\treturn this.attestation;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-extensions\">extensions</a>\n\t * property is an OPTIONAL member used by the Relying Party to provide client\n\t * extension inputs requesting additional processing by the client and authenticator.\n\t * @return the extensions\n\t */\n\tpublic @Nullable AuthenticationExtensionsClientInputs getExtensions() {\n\t\treturn this.extensions;\n\t}\n\n\t/**\n\t * Creates a new {@link PublicKeyCredentialCreationOptions}\n\t * @return a new {@link PublicKeyCredentialCreationOptions}\n\t */\n\tpublic static PublicKeyCredentialCreationOptionsBuilder builder() {\n\t\treturn new PublicKeyCredentialCreationOptionsBuilder();\n\t}\n\n\t/**\n\t * Used to build {@link PublicKeyCredentialCreationOptions}.\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class PublicKeyCredentialCreationOptionsBuilder {\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate PublicKeyCredentialRpEntity rp;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate PublicKeyCredentialUserEntity user;\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate Bytes challenge;\n\n\t\tprivate List<PublicKeyCredentialParameters> pubKeyCredParams = new ArrayList<>();\n\n\t\tprivate @Nullable Duration timeout;\n\n\t\tprivate List<PublicKeyCredentialDescriptor> excludeCredentials = new ArrayList<>();\n\n\t\t@SuppressWarnings(\"NullAway.Init\")\n\t\tprivate AuthenticatorSelectionCriteria authenticatorSelection;\n\n\t\tprivate @Nullable AttestationConveyancePreference attestation;\n\n\t\tprivate @Nullable AuthenticationExtensionsClientInputs extensions;\n\n\t\tprivate PublicKeyCredentialCreationOptionsBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getRp()} property.\n\t\t * @param rp the relying party\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder rp(PublicKeyCredentialRpEntity rp) {\n\t\t\tthis.rp = rp;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getUser()} property.\n\t\t * @param user the user entity\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder user(PublicKeyCredentialUserEntity user) {\n\t\t\tthis.user = user;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getChallenge()} property.\n\t\t * @param challenge the challenge\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder challenge(Bytes challenge) {\n\t\t\tthis.challenge = challenge;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getPubKeyCredParams()} property.\n\t\t * @param pubKeyCredParams the public key credential parameters\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder pubKeyCredParams(\n\t\t\t\tPublicKeyCredentialParameters... pubKeyCredParams) {\n\t\t\treturn pubKeyCredParams(Arrays.asList(pubKeyCredParams));\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getPubKeyCredParams()} property.\n\t\t * @param pubKeyCredParams the public key credential parameters\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder pubKeyCredParams(\n\t\t\t\tList<PublicKeyCredentialParameters> pubKeyCredParams) {\n\t\t\tthis.pubKeyCredParams = pubKeyCredParams;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getTimeout()} property.\n\t\t * @param timeout the timeout\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder timeout(@Nullable Duration timeout) {\n\t\t\tthis.timeout = timeout;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getExcludeCredentials()} property.\n\t\t * @param excludeCredentials the excluded credentials.\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder excludeCredentials(\n\t\t\t\tList<PublicKeyCredentialDescriptor> excludeCredentials) {\n\t\t\tthis.excludeCredentials = excludeCredentials;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getAuthenticatorSelection()} property.\n\t\t * @param authenticatorSelection the authenticator selection\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder authenticatorSelection(\n\t\t\t\tAuthenticatorSelectionCriteria authenticatorSelection) {\n\t\t\tthis.authenticatorSelection = authenticatorSelection;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getAttestation()} property.\n\t\t * @param attestation the attestation\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder attestation(\n\t\t\t\t@Nullable AttestationConveyancePreference attestation) {\n\t\t\tthis.attestation = attestation;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getExtensions()} property.\n\t\t * @param extensions the extensions\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder extensions(\n\t\t\t\t@Nullable AuthenticationExtensionsClientInputs extensions) {\n\t\t\tthis.extensions = extensions;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows customizing the builder using the {@link Consumer} that is passed in.\n\t\t * @param customizer the {@link Consumer} that can be used to customize the\n\t\t * {@link PublicKeyCredentialCreationOptionsBuilder}\n\t\t * @return the PublicKeyCredentialCreationOptionsBuilder\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptionsBuilder customize(\n\t\t\t\tConsumer<PublicKeyCredentialCreationOptionsBuilder> customizer) {\n\t\t\tcustomizer.accept(this);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link PublicKeyCredentialCreationOptions}\n\t\t * @return the new {@link PublicKeyCredentialCreationOptions}\n\t\t */\n\t\tpublic PublicKeyCredentialCreationOptions build() {\n\t\t\treturn new PublicKeyCredentialCreationOptions(this.rp, this.user, this.challenge, this.pubKeyCredParams,\n\t\t\t\t\tthis.timeout, this.excludeCredentials, this.authenticatorSelection, this.attestation,\n\t\t\t\t\tthis.extensions);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialDescriptor.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptor\">PublicKeyCredentialDescriptor</a>\n * identifies a specific public key credential. It is used in create() to prevent creating\n * duplicate credentials on the same authenticator, and in get() to determine if and how\n * the credential can currently be reached by the client. It mirrors some fields of the\n * PublicKeyCredential object returned by create() and get().\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class PublicKeyCredentialDescriptor implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 8793385059692676240L;\n\n\tprivate final PublicKeyCredentialType type;\n\n\tprivate final @Nullable Bytes id;\n\n\tprivate final @Nullable Set<AuthenticatorTransport> transports;\n\n\tprivate PublicKeyCredentialDescriptor(PublicKeyCredentialType type, @Nullable Bytes id,\n\t\t\t@Nullable Set<AuthenticatorTransport> transports) {\n\t\tthis.type = type;\n\t\tthis.id = id;\n\t\tthis.transports = transports;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-type\">type</a>\n\t * property contains the type of the public key credential the caller is referring to.\n\t * @return the type\n\t */\n\tpublic PublicKeyCredentialType getType() {\n\t\treturn this.type;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-id\">id</a>\n\t * property contains the credential ID of the public key credential the caller is\n\t * referring to.\n\t * @return the id\n\t */\n\tpublic @Nullable Bytes getId() {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports\">transports</a>\n\t * property is an OPTIONAL member that contains a hint as to how the client might\n\t * communicate with the managing authenticator of the public key credential the caller\n\t * is referring to.\n\t * @return the transports\n\t */\n\tpublic @Nullable Set<AuthenticatorTransport> getTransports() {\n\t\treturn this.transports;\n\t}\n\n\t/**\n\t * Creates a new {@link PublicKeyCredentialDescriptorBuilder}\n\t * @return a new {@link PublicKeyCredentialDescriptorBuilder}\n\t */\n\tpublic static PublicKeyCredentialDescriptorBuilder builder() {\n\t\treturn new PublicKeyCredentialDescriptorBuilder();\n\t}\n\n\t/**\n\t * Used to create {@link PublicKeyCredentialDescriptor}\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class PublicKeyCredentialDescriptorBuilder {\n\n\t\tprivate PublicKeyCredentialType type = PublicKeyCredentialType.PUBLIC_KEY;\n\n\t\tprivate @Nullable Bytes id;\n\n\t\tprivate @Nullable Set<AuthenticatorTransport> transports;\n\n\t\tprivate PublicKeyCredentialDescriptorBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getType()} property.\n\t\t * @param type the type\n\t\t * @return the {@link PublicKeyCredentialDescriptorBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialDescriptorBuilder type(PublicKeyCredentialType type) {\n\t\t\tthis.type = type;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getId()} property.\n\t\t * @param id the id\n\t\t * @return the {@link PublicKeyCredentialDescriptorBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialDescriptorBuilder id(@Nullable Bytes id) {\n\t\t\tthis.id = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getTransports()} property.\n\t\t * @param transports the transports\n\t\t * @return the {@link PublicKeyCredentialDescriptorBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialDescriptorBuilder transports(@Nullable Set<AuthenticatorTransport> transports) {\n\t\t\tthis.transports = transports;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getTransports()} property.\n\t\t * @param transports the transports\n\t\t * @return the {@link PublicKeyCredentialDescriptorBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialDescriptorBuilder transports(AuthenticatorTransport... transports) {\n\t\t\treturn transports(Set.of(transports));\n\t\t}\n\n\t\t/**\n\t\t * Create a new {@link PublicKeyCredentialDescriptor}\n\t\t * @return a new {@link PublicKeyCredentialDescriptor}\n\t\t */\n\t\tpublic PublicKeyCredentialDescriptor build() {\n\t\t\treturn new PublicKeyCredentialDescriptor(this.type, this.id, this.transports);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialParameters.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * The <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialparameters\">PublicKeyCredentialParameters</a>\n * is used to supply additional parameters when creating a new credential.\n *\n * @author Rob Winch\n * @since 6.4\n * @see PublicKeyCredentialCreationOptions#getPubKeyCredParams()\n */\npublic final class PublicKeyCredentialParameters implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -623985171385991L;\n\n\tpublic static final PublicKeyCredentialParameters EdDSA = new PublicKeyCredentialParameters(\n\t\t\tCOSEAlgorithmIdentifier.EdDSA);\n\n\tpublic static final PublicKeyCredentialParameters ES256 = new PublicKeyCredentialParameters(\n\t\t\tCOSEAlgorithmIdentifier.ES256);\n\n\tpublic static final PublicKeyCredentialParameters ES384 = new PublicKeyCredentialParameters(\n\t\t\tCOSEAlgorithmIdentifier.ES384);\n\n\tpublic static final PublicKeyCredentialParameters ES512 = new PublicKeyCredentialParameters(\n\t\t\tCOSEAlgorithmIdentifier.ES512);\n\n\tpublic static final PublicKeyCredentialParameters RS256 = new PublicKeyCredentialParameters(\n\t\t\tCOSEAlgorithmIdentifier.RS256);\n\n\tpublic static final PublicKeyCredentialParameters RS384 = new PublicKeyCredentialParameters(\n\t\t\tCOSEAlgorithmIdentifier.RS384);\n\n\tpublic static final PublicKeyCredentialParameters RS512 = new PublicKeyCredentialParameters(\n\t\t\tCOSEAlgorithmIdentifier.RS512);\n\n\tpublic static final PublicKeyCredentialParameters RS1 = new PublicKeyCredentialParameters(\n\t\t\tCOSEAlgorithmIdentifier.RS1);\n\n\t/**\n\t * This member specifies the type of credential to be created. The value SHOULD be a\n\t * member of PublicKeyCredentialType but client platforms MUST ignore unknown values,\n\t * ignoring any PublicKeyCredentialParameters with an unknown type.\n\t */\n\tprivate final PublicKeyCredentialType type;\n\n\t/**\n\t * This member specifies the cryptographic signature algorithm with which the newly\n\t * generated credential will be used, and thus also the type of asymmetric key pair to\n\t * be generated, e.g., RSA or Elliptic Curve.\n\t */\n\tprivate final COSEAlgorithmIdentifier alg;\n\n\tprivate PublicKeyCredentialParameters(COSEAlgorithmIdentifier alg) {\n\t\tthis(PublicKeyCredentialType.PUBLIC_KEY, alg);\n\t}\n\n\tprivate PublicKeyCredentialParameters(PublicKeyCredentialType type, COSEAlgorithmIdentifier alg) {\n\t\tthis.type = type;\n\t\tthis.alg = alg;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialparameters-type\">type</a>\n\t * property member specifies the type of credential to be created.\n\t * @return the type\n\t */\n\tpublic PublicKeyCredentialType getType() {\n\t\treturn this.type;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialparameters-alg\">alg</a>\n\t * member specifies the cryptographic signature algorithm with which the newly\n\t * generated credential will be used, and thus also the type of asymmetric key pair to\n\t * be generated, e.g., RSA or Elliptic Curve.\n\t * @return the algorithm\n\t */\n\tpublic COSEAlgorithmIdentifier getAlg() {\n\t\treturn this.alg;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRequestOptions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions\">PublicKeyCredentialRequestOptions</a>\n * contains the information to create an assertion used for authentication.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class PublicKeyCredentialRequestOptions implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2970057592835694354L;\n\n\tprivate final Bytes challenge;\n\n\tprivate final Duration timeout;\n\n\tprivate final @Nullable String rpId;\n\n\tprivate final List<PublicKeyCredentialDescriptor> allowCredentials;\n\n\tprivate final @Nullable UserVerificationRequirement userVerification;\n\n\tprivate final AuthenticationExtensionsClientInputs extensions;\n\n\tprivate PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, @Nullable String rpId,\n\t\t\tList<PublicKeyCredentialDescriptor> allowCredentials,\n\t\t\t@Nullable UserVerificationRequirement userVerification, AuthenticationExtensionsClientInputs extensions) {\n\t\tAssert.notNull(challenge, \"challenge cannot be null\");\n\t\tAssert.hasText(rpId, \"rpId cannot be empty\");\n\t\tthis.challenge = challenge;\n\t\tthis.timeout = timeout;\n\t\tthis.rpId = rpId;\n\t\tthis.allowCredentials = allowCredentials;\n\t\tthis.userVerification = userVerification;\n\t\tthis.extensions = extensions;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-challenge\">challenge</a>\n\t * property specifies a challenge that the authenticator signs, along with other data,\n\t * when producing an authentication assertion.\n\t * @return the challenge\n\t */\n\tpublic Bytes getChallenge() {\n\t\treturn this.challenge;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-timeout\">timeout</a>\n\t * property is an OPTIONAL member specifies a time, in milliseconds, that the Relying\n\t * Party is willing to wait for the call to complete.\n\t * @return the timeout\n\t */\n\tpublic Duration getTimeout() {\n\t\treturn this.timeout;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-rpid\">rpId</a>\n\t * is an OPTIONAL member specifies the RP ID claimed by the Relying Party. The client\n\t * MUST verify that the Relying Party's origin matches the scope of this RP ID.\n\t * @return the relying party id\n\t */\n\tpublic @Nullable String getRpId() {\n\t\treturn this.rpId;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials\">allowCredentials</a>\n\t * property is an OPTIONAL member is used by the client to find authenticators\n\t * eligible for this authentication ceremony.\n\t * @return the allowCredentials property\n\t */\n\tpublic List<PublicKeyCredentialDescriptor> getAllowCredentials() {\n\t\treturn this.allowCredentials;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-userverification\">userVerification</a>\n\t * property is an OPTIONAL member specifies the Relying Party's requirements regarding\n\t * user verification for the get() operation.\n\t * @return the user verification\n\t */\n\tpublic @Nullable UserVerificationRequirement getUserVerification() {\n\t\treturn this.userVerification;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-extensions\">extensions</a>\n\t * is an OPTIONAL property used by the Relying Party to provide client extension\n\t * inputs requesting additional processing by the client and authenticator.\n\t * @return the extensions\n\t */\n\tpublic AuthenticationExtensionsClientInputs getExtensions() {\n\t\treturn this.extensions;\n\t}\n\n\t/**\n\t * Creates a {@link PublicKeyCredentialRequestOptionsBuilder}\n\t * @return the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t */\n\tpublic static PublicKeyCredentialRequestOptionsBuilder builder() {\n\t\treturn new PublicKeyCredentialRequestOptionsBuilder();\n\t}\n\n\t/**\n\t * Used to build a {@link PublicKeyCredentialCreationOptions}.\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class PublicKeyCredentialRequestOptionsBuilder {\n\n\t\tprivate @Nullable Bytes challenge;\n\n\t\tprivate Duration timeout = Duration.ofMinutes(5);\n\n\t\tprivate @Nullable String rpId;\n\n\t\tprivate List<PublicKeyCredentialDescriptor> allowCredentials = Collections.emptyList();\n\n\t\tprivate @Nullable UserVerificationRequirement userVerification;\n\n\t\tprivate AuthenticationExtensionsClientInputs extensions = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tnew ArrayList<>());\n\n\t\tprivate PublicKeyCredentialRequestOptionsBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getChallenge()} property.\n\t\t * @param challenge the challenge\n\t\t * @return the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRequestOptionsBuilder challenge(@Nullable Bytes challenge) {\n\t\t\tthis.challenge = challenge;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getTimeout()} property.\n\t\t * @param timeout the timeout\n\t\t * @return the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRequestOptionsBuilder timeout(Duration timeout) {\n\t\t\tAssert.notNull(timeout, \"timeout cannot be null\");\n\t\t\tthis.timeout = timeout;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getRpId()} property.\n\t\t * @param rpId the rpId property\n\t\t * @return the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRequestOptionsBuilder rpId(@Nullable String rpId) {\n\t\t\tthis.rpId = rpId;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getAllowCredentials()} property\n\t\t * @param allowCredentials the allowed credentials\n\t\t * @return the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRequestOptionsBuilder allowCredentials(\n\t\t\t\tList<PublicKeyCredentialDescriptor> allowCredentials) {\n\t\t\tAssert.notNull(allowCredentials, \"allowCredentials cannot be null\");\n\t\t\tthis.allowCredentials = allowCredentials;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getUserVerification()} property.\n\t\t * @param userVerification the user verification\n\t\t * @return the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRequestOptionsBuilder userVerification(\n\t\t\t\t@Nullable UserVerificationRequirement userVerification) {\n\t\t\tthis.userVerification = userVerification;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getExtensions()} property\n\t\t * @param extensions the extensions\n\t\t * @return the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRequestOptionsBuilder extensions(AuthenticationExtensionsClientInputs extensions) {\n\t\t\tthis.extensions = extensions;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Allows customizing the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t\t * @param customizer the {@link Consumer} used to customize the builder\n\t\t * @return the {@link PublicKeyCredentialRequestOptionsBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRequestOptionsBuilder customize(\n\t\t\t\tConsumer<PublicKeyCredentialRequestOptionsBuilder> customizer) {\n\t\t\tcustomizer.accept(this);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Builds a new {@link PublicKeyCredentialRequestOptions}\n\t\t * @return a new {@link PublicKeyCredentialRequestOptions}\n\t\t */\n\t\tpublic PublicKeyCredentialRequestOptions build() {\n\t\t\tif (this.challenge == null) {\n\t\t\t\tthis.challenge = Bytes.random();\n\t\t\t}\n\t\t\treturn new PublicKeyCredentialRequestOptions(this.challenge, this.timeout, this.rpId, this.allowCredentials,\n\t\t\t\t\tthis.userVerification, this.extensions);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialRpEntity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.util.Assert;\n\n/**\n * The <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrpentity\">PublicKeyCredentialRpEntity</a>\n * dictionary is used to supply additional Relying Party attributes when creating a new\n * credential.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class PublicKeyCredentialRpEntity implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2741017471467698174L;\n\n\tprivate final String name;\n\n\tprivate final String id;\n\n\tprivate PublicKeyCredentialRpEntity(String name, String id) {\n\t\tthis.name = name;\n\t\tthis.id = id;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name\">name</a>\n\t * property is a human-palatable name for the entity. Its function depends on what the\n\t * PublicKeyCredentialEntity represents for the Relying Party, intended only for\n\t * display.\n\t * @return the name\n\t */\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrpentity-id\">id</a>\n\t * property is a unique identifier for the Relying Party entity, which sets the\n\t * <a href=\"https://www.w3.org/TR/webauthn-3/#rp-id\">RP ID</a>.\n\t * @return the relying party id\n\t */\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\t/**\n\t * Creates a new {@link PublicKeyCredentialRpEntityBuilder}\n\t * @return a new {@link PublicKeyCredentialRpEntityBuilder}\n\t */\n\tpublic static PublicKeyCredentialRpEntityBuilder builder() {\n\t\treturn new PublicKeyCredentialRpEntityBuilder();\n\t}\n\n\t/**\n\t * Used to create a {@link PublicKeyCredentialRpEntity}.\n\t *\n\t * @author Rob Winch\n\t * @since 6.4\n\t */\n\tpublic static final class PublicKeyCredentialRpEntityBuilder {\n\n\t\tprivate @Nullable String name;\n\n\t\tprivate @Nullable String id;\n\n\t\tprivate PublicKeyCredentialRpEntityBuilder() {\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getName()} property.\n\t\t * @param name the name property\n\t\t * @return the {@link PublicKeyCredentialRpEntityBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRpEntityBuilder name(@Nullable String name) {\n\t\t\tthis.name = name;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Sets the {@link #getId()} property.\n\t\t * @param id the id\n\t\t * @return the {@link PublicKeyCredentialRpEntityBuilder}\n\t\t */\n\t\tpublic PublicKeyCredentialRpEntityBuilder id(@Nullable String id) {\n\t\t\tthis.id = id;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Creates a new {@link PublicKeyCredentialRpEntity}.\n\t\t * @return a new {@link PublicKeyCredentialRpEntity}.\n\t\t */\n\t\tpublic PublicKeyCredentialRpEntity build() {\n\t\t\tAssert.notNull(this.name, \"name cannot be null\");\n\t\t\tAssert.notNull(this.id, \"id cannot be null\");\n\t\t\treturn new PublicKeyCredentialRpEntity(this.name, this.id);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialType.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * The <a href=\n * \"https://www.w3.org/TR/webauthn-3/#enum-credentialType\">PublicKeyCredentialType</a>\n * defines the credential types.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class PublicKeyCredentialType implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 7025333122210061679L;\n\n\t/**\n\t * The only credential type that currently exists.\n\t */\n\tpublic static final PublicKeyCredentialType PUBLIC_KEY = new PublicKeyCredentialType(\"public-key\");\n\n\tprivate final String value;\n\n\tprivate PublicKeyCredentialType(String value) {\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Gets the value.\n\t * @return the value\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\tpublic static PublicKeyCredentialType valueOf(String value) {\n\t\tif (PUBLIC_KEY.getValue().equals(value)) {\n\t\t\treturn PUBLIC_KEY;\n\t\t}\n\t\treturn new PublicKeyCredentialType(value);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/PublicKeyCredentialUserEntity.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serializable;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity\">PublicKeyCredentialUserEntity</a>\n * is used to supply additional\n * <a href=\"https://www.w3.org/TR/webauthn-3/#user-account\">user account</a> attributes\n * when creating a new credential.\n *\n * @author Rob Winch\n * @since 6.4\n * @see WebAuthnRelyingPartyOperations#authenticate(RelyingPartyAuthenticationRequest)\n */\npublic interface PublicKeyCredentialUserEntity extends Serializable {\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name\">name</a>\n\t * property is a human-palatable identifier for a user account.\n\t * @return the name\n\t */\n\tString getName();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-id\">id</a> is\n\t * the user handle of the user account. A user handle is an opaque byte sequence with\n\t * a maximum size of 64 bytes, and is not meant to be displayed to the user.\n\t * @return the user handle of the user account\n\t */\n\tBytes getId();\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-displayname\">displayName</a>\n\t * is a human-palatable name for the user account, intended only for display.\n\t * @return the display name\n\t */\n\t@Nullable String getDisplayName();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/ResidentKeyRequirement.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * The <a href=\n * \"https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement\">ResidentKeyRequirement</a>\n * describes the Relying Party requirements for client-side discoverable credentials.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class ResidentKeyRequirement implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1730700608982834858L;\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-discouraged\">discouraged</a>\n\t * requirement indicates that the Relying Party prefers creating a server-side\n\t * credential, but will accept a client-side discoverable credential.\n\t */\n\tpublic static final ResidentKeyRequirement DISCOURAGED = new ResidentKeyRequirement(\"discouraged\");\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-preferred\">preferred</a>\n\t * requirement indicates that the Relying Party strongly prefers creating a\n\t * client-side discoverable credential, but will accept a server-side credential.\n\t */\n\tpublic static final ResidentKeyRequirement PREFERRED = new ResidentKeyRequirement(\"preferred\");\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-required\">required</a>\n\t * value indicates that the Relying Party requires a client-side discoverable\n\t * credential.\n\t */\n\tpublic static final ResidentKeyRequirement REQUIRED = new ResidentKeyRequirement(\"required\");\n\n\tprivate final String value;\n\n\tprivate ResidentKeyRequirement(String value) {\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Gets the value.\n\t * @return the value\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\tpublic static ResidentKeyRequirement valueOf(String value) {\n\t\tif (DISCOURAGED.getValue().equals(value)) {\n\t\t\treturn DISCOURAGED;\n\t\t}\n\t\tif (PREFERRED.getValue().equals(value)) {\n\t\t\treturn PREFERRED;\n\t\t}\n\t\tif (REQUIRED.getValue().equals(value)) {\n\t\t\treturn REQUIRED;\n\t\t}\n\t\treturn new ResidentKeyRequirement(value);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/UserVerificationRequirement.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\n/**\n * <a href=\n * \"https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement\">UserVerificationRequirement</a>\n * is used by the Relying Party to indicate if user verification is needed.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class UserVerificationRequirement implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -2801001231345540040L;\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-discouraged\">discouraged</a>\n\t * value indicates that the Relying Party does not want user verification employed\n\t * during the operation (e.g., in the interest of minimizing disruption to the user\n\t * interaction flow).\n\t */\n\tpublic static final UserVerificationRequirement DISCOURAGED = new UserVerificationRequirement(\"discouraged\");\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-preferred\">preferred</a>\n\t * value indicates that the Relying Party prefers user verification for the operation\n\t * if possible, but will not fail the operation if the response does not have the UV\n\t * flag set.\n\t */\n\tpublic static final UserVerificationRequirement PREFERRED = new UserVerificationRequirement(\"preferred\");\n\n\t/**\n\t * The <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-required\">required</a>\n\t * value indicates that the Relying Party requires user verification for the operation\n\t * and will fail the overall ceremony if the response does not have the UV flag set.\n\t */\n\tpublic static final UserVerificationRequirement REQUIRED = new UserVerificationRequirement(\"required\");\n\n\tprivate final String value;\n\n\tUserVerificationRequirement(String value) {\n\t\tthis.value = value;\n\t}\n\n\t/**\n\t * Gets the value\n\t * @return the value\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/api/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WebAuthn APIs.\n */\n@NullMarked\npackage org.springframework.security.web.webauthn.api;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/HttpSessionPublicKeyCredentialRequestOptionsRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link PublicKeyCredentialRequestOptionsRepository} that stores the\n * {@link PublicKeyCredentialRequestOptions} in the\n * {@link jakarta.servlet.http.HttpSession}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class HttpSessionPublicKeyCredentialRequestOptionsRepository\n\t\timplements PublicKeyCredentialRequestOptionsRepository {\n\n\tstatic final String DEFAULT_ATTR_NAME = PublicKeyCredentialRequestOptionsRepository.class.getName()\n\t\t.concat(\".ATTR_NAME\");\n\n\tprivate String attrName = DEFAULT_ATTR_NAME;\n\n\t@Override\n\tpublic void save(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable PublicKeyCredentialRequestOptions options) {\n\t\tHttpSession session = request.getSession();\n\t\tsession.setAttribute(this.attrName, options);\n\t}\n\n\t@Override\n\tpublic @Nullable PublicKeyCredentialRequestOptions load(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (PublicKeyCredentialRequestOptions) session.getAttribute(this.attrName);\n\t}\n\n\tpublic void setAttrName(String attrName) {\n\t\tAssert.notNull(attrName, \"attrName cannot be null\");\n\t\tthis.attrName = attrName;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule;\nimport org.springframework.security.web.webauthn.management.ImmutablePublicKeyCredentialRequestOptionsRequest;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@link jakarta.servlet.Filter} that renders the\n * {@link PublicKeyCredentialRequestOptions} in order to <a href=\n * \"https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-get\">get</a>\n * a credential.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class PublicKeyCredentialRequestOptionsFilter extends OncePerRequestFilter {\n\n\tprivate RequestMatcher matcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.POST, \"/webauthn/authenticate/options\");\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate final WebAuthnRelyingPartyOperations rpOptions;\n\n\tprivate PublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository();\n\n\tprivate HttpMessageConverter<Object> converter = new JacksonJsonHttpMessageConverter(\n\t\t\tJsonMapper.builder().addModule(new WebauthnJacksonModule()).build());\n\n\t/**\n\t * Creates a new instance with the provided {@link WebAuthnRelyingPartyOperations}.\n\t * @param rpOptions the {@link WebAuthnRelyingPartyOperations} to use. Cannot be null.\n\t */\n\tpublic PublicKeyCredentialRequestOptionsFilter(WebAuthnRelyingPartyOperations rpOptions) {\n\t\tAssert.notNull(rpOptions, \"rpOperations cannot be null\");\n\t\tthis.rpOptions = rpOptions;\n\t}\n\n\t/**\n\t * Sets the {@link RequestMatcher} used to trigger this filter. By default, the\n\t * {@link RequestMatcher} is {@code POST /webauthn/authenticate/options}.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t * @since 6.5\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.matcher = requestMatcher;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!this.matcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tSecurityContext context = this.securityContextHolderStrategy.getContext();\n\t\tImmutablePublicKeyCredentialRequestOptionsRequest optionsRequest = new ImmutablePublicKeyCredentialRequestOptionsRequest(\n\t\t\t\tcontext.getAuthentication());\n\t\tPublicKeyCredentialRequestOptions credentialRequestOptions = this.rpOptions\n\t\t\t.createCredentialRequestOptions(optionsRequest);\n\t\tthis.requestOptionsRepository.save(request, response, credentialRequestOptions);\n\t\tresponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\tthis.converter.write(credentialRequestOptions, MediaType.APPLICATION_JSON,\n\t\t\t\tnew ServletServerHttpResponse(response));\n\n\t}\n\n\t/**\n\t * Sets the {@link PublicKeyCredentialRequestOptionsRepository} to use.\n\t * @param requestOptionsRepository the\n\t * {@link PublicKeyCredentialRequestOptionsRepository} to use. Cannot be null.\n\t */\n\tpublic void setRequestOptionsRepository(PublicKeyCredentialRequestOptionsRepository requestOptionsRepository) {\n\t\tAssert.notNull(requestOptionsRepository, \"requestOptionsRepository cannot be null\");\n\t\tthis.requestOptionsRepository = requestOptionsRepository;\n\t}\n\n\t/**\n\t * Sets the {@link HttpMessageConverter} to use.\n\t * @param converter the {@link HttpMessageConverter} to use. Cannot be null.\n\t */\n\tpublic void setConverter(HttpMessageConverter<Object> converter) {\n\t\tAssert.notNull(converter, \"converter cannot be null\");\n\t\tthis.converter = converter;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use.\n\t * @param securityContextHolderStrategy the {@link SecurityContextHolderStrategy} to\n\t * use. Cannot be null.\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tAssert.notNull(securityContextHolderStrategy, \"securityContextHolderStrategy cannot be null\");\n\t\tthis.securityContextHolderStrategy = securityContextHolderStrategy;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\n\n/**\n * Saves {@link PublicKeyCredentialRequestOptions} between a request to generate an\n * assertion and the validation of the assertion.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface PublicKeyCredentialRequestOptionsRepository {\n\n\t/**\n\t * Saves the provided {@link PublicKeyCredentialRequestOptions} or clears an existing\n\t * {@link PublicKeyCredentialRequestOptions} if {@code options} is null.\n\t * @param request the {@link HttpServletRequest}\n\t * @param response the {@link HttpServletResponse}\n\t * @param options the {@link PublicKeyCredentialRequestOptions} to save or null if an\n\t * existing {@link PublicKeyCredentialRequestOptions} should be removed.\n\t */\n\tvoid save(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable PublicKeyCredentialRequestOptions options);\n\n\t/**\n\t * Gets a saved {@link PublicKeyCredentialRequestOptions} if it exists, otherwise\n\t * null.\n\t * @param request the {@link HttpServletRequest}\n\t * @return the {@link PublicKeyCredentialRequestOptions} that was saved, otherwise\n\t * null.\n\t */\n\t@Nullable PublicKeyCredentialRequestOptions load(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthentication.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport java.io.Serial;\nimport java.util.Collection;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link WebAuthnAuthentication} is used to represent successful authentication with\n * WebAuthn.\n *\n * @author Rob Winch\n * @since 6.4\n * @see WebAuthnAuthenticationRequestToken\n */\npublic class WebAuthnAuthentication extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -4879907158750659197L;\n\n\tprivate final PublicKeyCredentialUserEntity principal;\n\n\tpublic WebAuthnAuthentication(PublicKeyCredentialUserEntity principal,\n\t\t\tCollection<? extends GrantedAuthority> authorities) {\n\t\tsuper(authorities);\n\t\tthis.principal = principal;\n\t\tsuper.setAuthenticated(true);\n\t}\n\n\tprivate WebAuthnAuthentication(Builder<?> builder) {\n\t\tsuper(builder);\n\t\tthis.principal = builder.principal;\n\t}\n\n\t@Override\n\tpublic void setAuthenticated(boolean authenticated) {\n\t\tAssert.isTrue(!authenticated, \"Cannot set this token to trusted\");\n\t\tsuper.setAuthenticated(authenticated);\n\t}\n\n\t@Override\n\tpublic @Nullable Object getCredentials() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic PublicKeyCredentialUserEntity getPrincipal() {\n\t\treturn this.principal;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn this.principal.getName();\n\t}\n\n\t@Override\n\tpublic Builder<?> toBuilder() {\n\t\treturn new Builder<>(this);\n\t}\n\n\t/**\n\t * A builder of {@link WebAuthnAuthentication} instances\n\t *\n\t * @since 7.0\n\t */\n\tpublic static final class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {\n\n\t\tprivate PublicKeyCredentialUserEntity principal;\n\n\t\tprivate Builder(WebAuthnAuthentication token) {\n\t\t\tsuper(token);\n\t\t\tthis.principal = token.principal;\n\t\t}\n\n\t\t/**\n\t\t * Use this principal. It must be of type {@link PublicKeyCredentialUserEntity}\n\t\t * @param principal the principal to use\n\t\t * @return the {@link Builder} for further configurations\n\t\t */\n\t\t@Override\n\t\tpublic B principal(@Nullable Object principal) {\n\t\t\tAssert.isInstanceOf(PublicKeyCredentialUserEntity.class, principal,\n\t\t\t\t\t\"principal must be of type PublicKeyCredentialUserEntity\");\n\t\t\tthis.principal = (PublicKeyCredentialUserEntity) principal;\n\t\t\treturn (B) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic WebAuthnAuthentication build() {\n\t\t\treturn new WebAuthnAuthentication(this);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.core.ResolvableType;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractSmartHttpMessageConverter;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;\nimport org.springframework.security.web.authentication.HttpMessageConverterAuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.HttpStatusEntryPoint;\nimport org.springframework.security.web.context.HttpSessionSecurityContextRepository;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule;\nimport org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;\nimport org.springframework.util.Assert;\n\nimport static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;\n\n/**\n * Authenticates {@code PublicKeyCredential<AuthenticatorAssertionResponse>} that is\n * parsed from the body of the {@link HttpServletRequest} using the\n * {@link #setConverter(GenericHttpMessageConverter)}. An example request is provided\n * below:\n *\n * <pre>\n * {\n * \t\"id\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n * \t\"rawId\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n * \t\"response\": {\n * \t\t\"authenticatorData\": \"y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA\",\n * \t\t\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRFVsRzRDbU9naWhKMG1vdXZFcE9HdUk0ZVJ6MGRRWmxUQmFtbjdHQ1FTNCIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n * \t\t\"signature\": \"MEYCIQCW2BcUkRCAXDmGxwMi78jknenZ7_amWrUJEYoTkweldAIhAMD0EMp1rw2GfwhdrsFIeDsL7tfOXVPwOtfqJntjAo4z\",\n * \t\t\"userHandle\": \"Q3_0Xd64_HW0BlKRAJnVagJTpLKLgARCj8zjugpRnVo\"\n * \t    },\n * \t\"clientExtensionResults\": {},\n * \t\"authenticatorAttachment\": \"platform\"\n * }\n * </pre>\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n\tprivate SmartHttpMessageConverter<Object> converter = new JacksonJsonHttpMessageConverter(\n\t\t\tJsonMapper.builder().addModule(new WebauthnJacksonModule()).build());\n\n\tprivate PublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository();\n\n\tpublic WebAuthnAuthenticationFilter() {\n\t\tsuper(pathPattern(HttpMethod.POST, \"/login/webauthn\"));\n\t\tsetSecurityContextRepository(new HttpSessionSecurityContextRepository());\n\t\tsetAuthenticationFailureHandler(\n\t\t\t\tnew AuthenticationEntryPointFailureHandler(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)));\n\t\tsetAuthenticationSuccessHandler(new HttpMessageConverterAuthenticationSuccessHandler());\n\t}\n\n\t@Override\n\tpublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)\n\t\t\tthrows AuthenticationException, IOException, ServletException {\n\t\tServletServerHttpRequest httpRequest = new ServletServerHttpRequest(request);\n\t\tResolvableType resolvableType = ResolvableType.forClassWithGenerics(PublicKeyCredential.class,\n\t\t\t\tAuthenticatorAssertionResponse.class);\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> publicKeyCredential = null;\n\t\ttry {\n\t\t\tpublicKeyCredential = (PublicKeyCredential<AuthenticatorAssertionResponse>) this.converter\n\t\t\t\t.read(resolvableType, httpRequest, null);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new BadCredentialsException(\"Unable to authenticate the PublicKeyCredential\", ex);\n\t\t}\n\t\tPublicKeyCredentialRequestOptions requestOptions = this.requestOptionsRepository.load(request);\n\t\tif (requestOptions == null) {\n\t\t\tthrow new BadCredentialsException(\n\t\t\t\t\t\"Unable to authenticate the PublicKeyCredential. No PublicKeyCredentialRequestOptions found.\");\n\t\t}\n\t\tthis.requestOptionsRepository.save(request, response, null);\n\t\tRelyingPartyAuthenticationRequest authenticationRequest = new RelyingPartyAuthenticationRequest(requestOptions,\n\t\t\t\tpublicKeyCredential);\n\t\tWebAuthnAuthenticationRequestToken token = new WebAuthnAuthenticationRequestToken(authenticationRequest);\n\t\treturn getAuthenticationManager().authenticate(token);\n\t}\n\n\t/**\n\t * Sets the {@link GenericHttpMessageConverter} to use for writing\n\t * {@code PublicKeyCredential<AuthenticatorAssertionResponse>} to the response. The\n\t * default is @{code MappingJackson2HttpMessageConverter}\n\t * @param converter the {@link GenericHttpMessageConverter} to use. Cannot be null.\n\t * @deprecated use {@link #setConverter(SmartHttpMessageConverter)}\n\t */\n\t@Deprecated(forRemoval = true, since = \"7.0\")\n\tpublic void setConverter(GenericHttpMessageConverter<Object> converter) {\n\t\tAssert.notNull(converter, \"converter cannot be null\");\n\t\tthis.converter = new GenericHttpMessageConverterAdapter<>(converter);\n\t}\n\n\t/**\n\t * Sets the {@link SmartHttpMessageConverter} to use for writing\n\t * {@code PublicKeyCredential<AuthenticatorAssertionResponse>} to the response. The\n\t * default is @{code MappingJackson2HttpMessageConverter}\n\t * @param converter the {@link SmartHttpMessageConverter} to use. Cannot be null.\n\t * @since 7.0\n\t */\n\tpublic void setConverter(SmartHttpMessageConverter<Object> converter) {\n\t\tAssert.notNull(converter, \"converter cannot be null\");\n\t\tthis.converter = converter;\n\t}\n\n\t/**\n\t * Sets the {@link PublicKeyCredentialRequestOptionsRepository} to use. The default is\n\t * {@link HttpSessionPublicKeyCredentialRequestOptionsRepository}.\n\t * @param requestOptionsRepository the\n\t * {@link PublicKeyCredentialRequestOptionsRepository} to use. Cannot be null.\n\t */\n\tpublic void setRequestOptionsRepository(PublicKeyCredentialRequestOptionsRepository requestOptionsRepository) {\n\t\tAssert.notNull(requestOptionsRepository, \"requestOptionsRepository cannot be null\");\n\t\tthis.requestOptionsRepository = requestOptionsRepository;\n\t}\n\n\t/**\n\t * Adapts a {@link GenericHttpMessageConverter} to a\n\t * {@link SmartHttpMessageConverter}.\n\t *\n\t * @param <T> The type\n\t * @author Rob Winch\n\t * @since 7.0\n\t */\n\tprivate static final class GenericHttpMessageConverterAdapter<T> extends AbstractSmartHttpMessageConverter<T> {\n\n\t\tprivate final GenericHttpMessageConverter<T> delegate;\n\n\t\tprivate GenericHttpMessageConverterAdapter(GenericHttpMessageConverter<T> delegate) {\n\t\t\tAssert.notNull(delegate, \"delegate cannot be null\");\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean canRead(ResolvableType type, @Nullable MediaType mediaType) {\n\t\t\tClass<?> clazz = type.resolve();\n\t\t\treturn (clazz != null) ? canRead(clazz, mediaType) : canRead(mediaType);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\t\treturn this.delegate.canRead(clazz, mediaType);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {\n\t\t\treturn this.delegate.canWrite(clazz, mediaType);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<MediaType> getSupportedMediaTypes() {\n\t\t\treturn this.delegate.getSupportedMediaTypes();\n\t\t}\n\n\t\t@Override\n\t\tpublic List<MediaType> getSupportedMediaTypes(Class<?> clazz) {\n\t\t\treturn this.delegate.getSupportedMediaTypes(clazz);\n\t\t}\n\n\t\t@Override\n\t\tprotected void writeInternal(T t, ResolvableType type, HttpOutputMessage outputMessage,\n\t\t\t\t@Nullable Map<String, Object> hints) throws IOException, HttpMessageNotWritableException {\n\t\t\tthis.delegate.write(t, null, outputMessage);\n\t\t}\n\n\t\t@Override\n\t\tpublic T read(ResolvableType type, HttpInputMessage inputMessage, @Nullable Map<String, Object> hints)\n\t\t\t\tthrows IOException, HttpMessageNotReadableException {\n\t\t\treturn this.delegate.read(type.getType(), null, inputMessage);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean canWrite(ResolvableType targetType, Class<?> valueClass, @Nullable MediaType mediaType) {\n\t\t\treturn this.delegate.canWrite(targetType.getType(), valueClass, mediaType);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationProvider.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport java.util.Collection;\nimport java.util.HashSet;\n\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link AuthenticationProvider} that uses {@link WebAuthnRelyingPartyOperations} for\n * authentication using an {@link WebAuthnAuthenticationRequestToken}. First\n * {@link WebAuthnRelyingPartyOperations#authenticate(RelyingPartyAuthenticationRequest)}\n * is invoked. The result is a username passed into {@link UserDetailsService}. The\n * {@link UserDetails} is used to create an {@link Authentication}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class WebAuthnAuthenticationProvider implements AuthenticationProvider {\n\n\tprivate static final String AUTHORITY = FactorGrantedAuthority.WEBAUTHN_AUTHORITY;\n\n\tprivate final WebAuthnRelyingPartyOperations relyingPartyOperations;\n\n\tprivate final UserDetailsService userDetailsService;\n\n\t/**\n\t * Creates a new instance.\n\t * @param relyingPartyOperations the {@link WebAuthnRelyingPartyOperations} to use.\n\t * Cannot be null.\n\t * @param userDetailsService the {@link UserDetailsService} to use. Cannot be null.\n\t */\n\tpublic WebAuthnAuthenticationProvider(WebAuthnRelyingPartyOperations relyingPartyOperations,\n\t\t\tUserDetailsService userDetailsService) {\n\t\tAssert.notNull(relyingPartyOperations, \"relyingPartyOperations cannot be null\");\n\t\tAssert.notNull(userDetailsService, \"userDetailsService cannot be null\");\n\t\tthis.relyingPartyOperations = relyingPartyOperations;\n\t\tthis.userDetailsService = userDetailsService;\n\t}\n\n\t@Override\n\tpublic Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tWebAuthnAuthenticationRequestToken webAuthnRequest = (WebAuthnAuthenticationRequestToken) authentication;\n\t\ttry {\n\t\t\tPublicKeyCredentialUserEntity userEntity = this.relyingPartyOperations\n\t\t\t\t.authenticate(webAuthnRequest.getWebAuthnRequest());\n\t\t\tString username = userEntity.getName();\n\t\t\tUserDetails userDetails = this.userDetailsService.loadUserByUsername(username);\n\t\t\tCollection<GrantedAuthority> authorities = new HashSet<>(userDetails.getAuthorities());\n\t\t\tauthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));\n\t\t\treturn new WebAuthnAuthentication(userEntity, authorities);\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tthrow new BadCredentialsException(ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean supports(Class<?> authentication) {\n\t\treturn WebAuthnAuthenticationRequestToken.class.isAssignableFrom(authentication);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationRequestToken.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport java.io.Serial;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;\nimport org.springframework.util.Assert;\n\n/**\n * An {@link org.springframework.security.core.Authentication} used in\n * {@link WebAuthnAuthenticationProvider} for authenticating via WebAuthn.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class WebAuthnAuthenticationRequestToken extends AbstractAuthenticationToken {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -1682693433877522403L;\n\n\tprivate final RelyingPartyAuthenticationRequest webAuthnRequest;\n\n\t/**\n\t * Creates a new instance.\n\t * @param webAuthnRequest the {@link RelyingPartyAuthenticationRequest} to use for\n\t * authentication. Cannot be null.\n\t */\n\tpublic WebAuthnAuthenticationRequestToken(RelyingPartyAuthenticationRequest webAuthnRequest) {\n\t\tsuper(AuthorityUtils.NO_AUTHORITIES);\n\t\tAssert.notNull(webAuthnRequest, \"webAuthnRequest cannot be null\");\n\t\tthis.webAuthnRequest = webAuthnRequest;\n\t}\n\n\t/**\n\t * Gets the {@link RelyingPartyAuthenticationRequest}\n\t * @return the {@link RelyingPartyAuthenticationRequest}\n\t */\n\tpublic RelyingPartyAuthenticationRequest getWebAuthnRequest() {\n\t\treturn this.webAuthnRequest;\n\t}\n\n\t@Override\n\tpublic void setAuthenticated(boolean authenticated) {\n\t\tAssert.isTrue(!authenticated, \"Cannot set this token to trusted\");\n\t\tsuper.setAuthenticated(authenticated);\n\t}\n\n\t@Override\n\tpublic Object getCredentials() {\n\t\treturn this.webAuthnRequest.getPublicKey();\n\t}\n\n\t@Override\n\tpublic @Nullable Object getPrincipal() {\n\t\treturn this.webAuthnRequest.getPublicKey().getResponse().getUserHandle();\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WebAuthn Authentication support.\n */\n@NullMarked\npackage org.springframework.security.web.webauthn.authentication;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AttestationConveyancePreference;\n\n/**\n * Jackson mixin for {@link AttestationConveyancePreference}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AttestationConveyancePreferenceMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = AttestationConveyancePreferenceJackson2Serializer.class)\nclass AttestationConveyancePreferenceJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.AttestationConveyancePreference;\n\n/**\n * Jackson serializer for {@link AttestationConveyancePreference}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AttestationConveyancePreferenceSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass AttestationConveyancePreferenceJackson2Serializer extends StdSerializer<AttestationConveyancePreference> {\n\n\tAttestationConveyancePreferenceJackson2Serializer() {\n\t\tsuper(AttestationConveyancePreference.class);\n\t}\n\n\t@Override\n\tpublic void serialize(AttestationConveyancePreference preference, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeString(preference.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AttestationConveyancePreference;\n\n/**\n * Jackson mixin for {@link AttestationConveyancePreference}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonSerialize(using = AttestationConveyancePreferenceSerializer.class)\nclass AttestationConveyancePreferenceMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AttestationConveyancePreferenceSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.AttestationConveyancePreference;\n\n/**\n * Jackson serializer for {@link AttestationConveyancePreference}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass AttestationConveyancePreferenceSerializer extends StdSerializer<AttestationConveyancePreference> {\n\n\tAttestationConveyancePreferenceSerializer() {\n\t\tsuper(AttestationConveyancePreference.class);\n\t}\n\n\t@Override\n\tpublic void serialize(AttestationConveyancePreference preference, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writeString(preference.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\n\n/**\n * Jackson mixin for {@link AuthenticationExtensionsClientInputs}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticationExtensionsClientInputMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = AuthenticationExtensionsClientInputJackson2Serializer.class)\nclass AuthenticationExtensionsClientInputJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;\n\n/**\n * Provides Jackson serialization of {@link AuthenticationExtensionsClientInput}.\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticationExtensionsClientInputSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass AuthenticationExtensionsClientInputJackson2Serializer extends StdSerializer<AuthenticationExtensionsClientInput> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tAuthenticationExtensionsClientInputJackson2Serializer() {\n\t\tsuper(AuthenticationExtensionsClientInput.class);\n\t}\n\n\t@Override\n\tpublic void serialize(AuthenticationExtensionsClientInput input, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeObjectField(input.getExtensionId(), input.getInput());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\n\n/**\n * Jackson mixin for {@link AuthenticationExtensionsClientInputs}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonSerialize(using = AuthenticationExtensionsClientInputSerializer.class)\nclass AuthenticationExtensionsClientInputMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;\n\n/**\n * Provides Jackson serialization of {@link AuthenticationExtensionsClientInput}.\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass AuthenticationExtensionsClientInputSerializer extends StdSerializer<AuthenticationExtensionsClientInput> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tAuthenticationExtensionsClientInputSerializer() {\n\t\tsuper(AuthenticationExtensionsClientInput.class);\n\t}\n\n\t@Override\n\tpublic void serialize(AuthenticationExtensionsClientInput input, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writePOJOProperty(input.getExtensionId(), input.getInput());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\n\n/**\n * Jackson mixin for {@link AuthenticationExtensionsClientInputs}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticationExtensionsClientInputsMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = AuthenticationExtensionsClientInputsJackson2Serializer.class)\nclass AuthenticationExtensionsClientInputsJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\n\n/**\n * Provides Jackson serialization of {@link AuthenticationExtensionsClientInputs}.\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticationExtensionsClientInputsSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass AuthenticationExtensionsClientInputsJackson2Serializer\n\t\textends StdSerializer<AuthenticationExtensionsClientInputs> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tAuthenticationExtensionsClientInputsJackson2Serializer() {\n\t\tsuper(AuthenticationExtensionsClientInputs.class);\n\t}\n\n\t@Override\n\tpublic void serialize(AuthenticationExtensionsClientInputs inputs, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeStartObject();\n\t\tfor (AuthenticationExtensionsClientInput input : inputs.getInputs()) {\n\t\t\tjgen.writeObject(input);\n\t\t}\n\t\tjgen.writeEndObject();\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\n\n/**\n * Jackson mixin for {@link AuthenticationExtensionsClientInputs}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonSerialize(using = AuthenticationExtensionsClientInputsSerializer.class)\nclass AuthenticationExtensionsClientInputsMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientInputsSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\n\n/**\n * Provides Jackson serialization of {@link AuthenticationExtensionsClientInputs}.\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass AuthenticationExtensionsClientInputsSerializer extends StdSerializer<AuthenticationExtensionsClientInputs> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tAuthenticationExtensionsClientInputsSerializer() {\n\t\tsuper(AuthenticationExtensionsClientInputs.class);\n\t}\n\n\t@Override\n\tpublic void serialize(AuthenticationExtensionsClientInputs inputs, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writeStartObject();\n\t\tfor (AuthenticationExtensionsClientInput input : inputs.getInputs()) {\n\t\t\tjgen.writePOJO(input);\n\t\t}\n\t\tjgen.writeEndObject();\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.core.JsonToken;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.deser.std.StdDeserializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutput;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;\n\n/**\n * Provides Jackson deserialization of {@link AuthenticationExtensionsClientOutputs}.\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass AuthenticationExtensionsClientOutputsDeserializer extends StdDeserializer<AuthenticationExtensionsClientOutputs> {\n\n\tprivate static final Log logger = LogFactory.getLog(AuthenticationExtensionsClientOutputsDeserializer.class);\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tAuthenticationExtensionsClientOutputsDeserializer() {\n\t\tsuper(AuthenticationExtensionsClientOutputs.class);\n\t}\n\n\t@Override\n\tpublic AuthenticationExtensionsClientOutputs deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tList<AuthenticationExtensionsClientOutput<?>> outputs = new ArrayList<>();\n\t\tfor (String key = parser.nextName(); key != null; key = parser.nextName()) {\n\t\t\tJsonToken next = parser.nextToken();\n\t\t\tif (next == JsonToken.START_OBJECT && CredentialPropertiesOutput.EXTENSION_ID.equals(key)) {\n\t\t\t\tCredentialPropertiesOutput output = parser.readValueAs(CredentialPropertiesOutput.class);\n\t\t\t\toutputs.add(output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"Skipping unknown extension with id \" + key);\n\t\t\t\t}\n\t\t\t\tif (next.isStructStart()) {\n\t\t\t\t\tparser.skipChildren();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn new ImmutableAuthenticationExtensionsClientOutputs(outputs);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsJackson2Deserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.fasterxml.jackson.core.JacksonException;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.core.JsonToken;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StdDeserializer;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutput;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;\n\n/**\n * Provides Jackson deserialization of {@link AuthenticationExtensionsClientOutputs}.\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticationExtensionsClientOutputsDeserializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass AuthenticationExtensionsClientOutputsJackson2Deserializer\n\t\textends StdDeserializer<AuthenticationExtensionsClientOutputs> {\n\n\tprivate static final Log logger = LogFactory\n\t\t.getLog(AuthenticationExtensionsClientOutputsJackson2Deserializer.class);\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tAuthenticationExtensionsClientOutputsJackson2Deserializer() {\n\t\tsuper(AuthenticationExtensionsClientOutputs.class);\n\t}\n\n\t@Override\n\tpublic AuthenticationExtensionsClientOutputs deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows IOException, JacksonException {\n\t\tList<AuthenticationExtensionsClientOutput<?>> outputs = new ArrayList<>();\n\t\tfor (String key = parser.nextFieldName(); key != null; key = parser.nextFieldName()) {\n\t\t\tJsonToken next = parser.nextToken();\n\t\t\tif (next == JsonToken.START_OBJECT && CredentialPropertiesOutput.EXTENSION_ID.equals(key)) {\n\t\t\t\tCredentialPropertiesOutput output = parser.readValueAs(CredentialPropertiesOutput.class);\n\t\t\t\toutputs.add(output);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"Skipping unknown extension with id \" + key);\n\t\t\t\t}\n\t\t\t\tif (next.isStructStart()) {\n\t\t\t\t\tparser.skipChildren();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn new ImmutableAuthenticationExtensionsClientOutputs(outputs);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\n\n/**\n * Jackson mixin for {@link AuthenticationExtensionsClientOutputs}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticationExtensionsClientOutputsMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonDeserialize(using = AuthenticationExtensionsClientOutputsJackson2Deserializer.class)\nclass AuthenticationExtensionsClientOutputsJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticationExtensionsClientOutputsMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonDeserialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\n\n/**\n * Jackson mixin for {@link AuthenticationExtensionsClientOutputs}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonDeserialize(using = AuthenticationExtensionsClientOutputsDeserializer.class)\nclass AuthenticationExtensionsClientOutputsMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\n\n/**\n * Jackson mixin for {@link AuthenticatorAssertionResponse}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAssertionResponseMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonDeserialize(builder = AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder.class)\nclass AuthenticatorAssertionResponseJackson2Mixin {\n\n\t@JsonPOJOBuilder(withPrefix = \"\")\n\tabstract class AuthenticatorAssertionResponseBuilderMixin {\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAssertionResponseMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonDeserialize;\nimport tools.jackson.databind.annotation.JsonPOJOBuilder;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\n\n/**\n * Jackson mixin for {@link AuthenticatorAssertionResponse}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonDeserialize(builder = AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder.class)\nclass AuthenticatorAssertionResponseMixin {\n\n\t@JsonPOJOBuilder(withPrefix = \"\")\n\tabstract class AuthenticatorAssertionResponseBuilderMixin {\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.deser.std.StdDeserializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\n\n/**\n * Jackson deserializer for {@link AuthenticatorAttachment}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass AuthenticatorAttachmentDeserializer extends StdDeserializer<AuthenticatorAttachment> {\n\n\tAuthenticatorAttachmentDeserializer() {\n\t\tsuper(AuthenticatorAttachment.class);\n\t}\n\n\t@Override\n\tpublic @Nullable AuthenticatorAttachment deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tString type = parser.readValueAs(String.class);\n\t\tfor (AuthenticatorAttachment publicKeyCredentialType : AuthenticatorAttachment.values()) {\n\t\t\tif (publicKeyCredentialType.getValue().equals(type)) {\n\t\t\t\treturn publicKeyCredentialType;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Deserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JacksonException;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StdDeserializer;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\n\n/**\n * Jackson deserializer for {@link AuthenticatorAttachment}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAttachmentDeserializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass AuthenticatorAttachmentJackson2Deserializer extends StdDeserializer<AuthenticatorAttachment> {\n\n\tAuthenticatorAttachmentJackson2Deserializer() {\n\t\tsuper(AuthenticatorAttachment.class);\n\t}\n\n\t@Override\n\tpublic @Nullable AuthenticatorAttachment deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows IOException, JacksonException {\n\t\tString type = parser.readValueAs(String.class);\n\t\tfor (AuthenticatorAttachment publicKeyCredentialType : AuthenticatorAttachment.values()) {\n\t\t\tif (publicKeyCredentialType.getValue().equals(type)) {\n\t\t\t\treturn publicKeyCredentialType;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\n\n/**\n * Jackson mixin for {@link AuthenticatorAttachment}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAttachmentMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonDeserialize(using = AuthenticatorAttachmentJackson2Deserializer.class)\n@JsonSerialize(using = AuthenticatorAttachmentJackson2Serializer.class)\nclass AuthenticatorAttachmentJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\n\n/**\n * Jackson serializer for {@link AuthenticatorAttachment}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAttachmentSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass AuthenticatorAttachmentJackson2Serializer extends StdSerializer<AuthenticatorAttachment> {\n\n\tAuthenticatorAttachmentJackson2Serializer() {\n\t\tsuper(AuthenticatorAttachment.class);\n\t}\n\n\t@Override\n\tpublic void serialize(AuthenticatorAttachment attachment, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeString(attachment.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonDeserialize;\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\n\n/**\n * Jackson mixin for {@link AuthenticatorAttachment}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonDeserialize(using = AuthenticatorAttachmentDeserializer.class)\n@JsonSerialize(using = AuthenticatorAttachmentSerializer.class)\nclass AuthenticatorAttachmentMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttachmentSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\n\n/**\n * Jackson serializer for {@link AuthenticatorAttachment}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass AuthenticatorAttachmentSerializer extends StdSerializer<AuthenticatorAttachment> {\n\n\tAuthenticatorAttachmentSerializer() {\n\t\tsuper(AuthenticatorAttachment.class);\n\t}\n\n\t@Override\n\tpublic void serialize(AuthenticatorAttachment attachment, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writeString(attachment.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttestationResponseJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonSetter;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\n\n/**\n * Jackson mixin for {@link AuthenticatorAttestationResponse}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorAttestationResponseMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonDeserialize(builder = AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder.class)\nclass AuthenticatorAttestationResponseJackson2Mixin {\n\n\t@JsonPOJOBuilder(withPrefix = \"\")\n\t@JsonIgnoreProperties(ignoreUnknown = true)\n\tabstract class AuthenticatorAttestationResponseBuilderMixin {\n\n\t\t@JsonSetter\n\t\tabstract AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder transports(\n\t\t\t\tList<AuthenticatorTransport> transports);\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorAttestationResponseMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.util.List;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonSetter;\nimport tools.jackson.databind.annotation.JsonDeserialize;\nimport tools.jackson.databind.annotation.JsonPOJOBuilder;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\n\n/**\n * Jackson mixin for {@link AuthenticatorAttestationResponse}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonDeserialize(builder = AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder.class)\nclass AuthenticatorAttestationResponseMixin {\n\n\t@JsonPOJOBuilder(withPrefix = \"\")\n\t@JsonIgnoreProperties(ignoreUnknown = true)\n\tabstract class AuthenticatorAttestationResponseBuilderMixin {\n\n\t\t@JsonSetter\n\t\tabstract AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder transports(\n\t\t\t\tList<AuthenticatorTransport> transports);\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorSelectionCriteriaMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;\n\n/**\n * Jackson mixin for {@link AuthenticatorSelectionCriteria}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonInclude(JsonInclude.Include.NON_NULL)\nabstract class AuthenticatorSelectionCriteriaMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.deser.std.StdDeserializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\n\n/**\n * Jackson deserializer for {@link AuthenticatorTransport}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass AuthenticatorTransportDeserializer extends StdDeserializer<AuthenticatorTransport> {\n\n\tAuthenticatorTransportDeserializer() {\n\t\tsuper(AuthenticatorTransport.class);\n\t}\n\n\t@Override\n\tpublic @Nullable AuthenticatorTransport deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tString transportValue = parser.readValueAs(String.class);\n\t\tfor (AuthenticatorTransport transport : AuthenticatorTransport.values()) {\n\t\t\tif (transport.getValue().equals(transportValue)) {\n\t\t\t\treturn transport;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Deserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JacksonException;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StdDeserializer;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\n\n/**\n * Jackson deserializer for {@link AuthenticatorTransport}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorTransportDeserializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass AuthenticatorTransportJackson2Deserializer extends StdDeserializer<AuthenticatorTransport> {\n\n\tAuthenticatorTransportJackson2Deserializer() {\n\t\tsuper(AuthenticatorTransport.class);\n\t}\n\n\t@Override\n\tpublic @Nullable AuthenticatorTransport deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows IOException, JacksonException {\n\t\tString transportValue = parser.readValueAs(String.class);\n\t\tfor (AuthenticatorTransport transport : AuthenticatorTransport.values()) {\n\t\t\tif (transport.getValue().equals(transportValue)) {\n\t\t\t\treturn transport;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\n\n/**\n * Jackson mixin for {@link AuthenticatorTransport}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorTransportMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonDeserialize(using = AuthenticatorTransportJackson2Deserializer.class)\n@JsonSerialize(using = AuthenticatorTransportJackson2Serializer.class)\nclass AuthenticatorTransportJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\n\n/**\n * Jackson serializer for {@link AuthenticatorTransport}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.AuthenticatorTransportSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\nclass AuthenticatorTransportJackson2Serializer extends JsonSerializer<AuthenticatorTransport> {\n\n\t@Override\n\tpublic void serialize(AuthenticatorTransport transport, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeString(transport.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonDeserialize;\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\n\n/**\n * Jackson mixin for {@link AuthenticatorTransport}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonDeserialize(using = AuthenticatorTransportDeserializer.class)\n@JsonSerialize(using = AuthenticatorTransportSerializer.class)\nclass AuthenticatorTransportMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/AuthenticatorTransportSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ValueSerializer;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\n\n/**\n * Jackson serializer for {@link AuthenticatorTransport}\n *\n * @author Rob Winch\n * @since 6.4\n */\nclass AuthenticatorTransportSerializer extends ValueSerializer<AuthenticatorTransport> {\n\n\t@Override\n\tpublic void serialize(AuthenticatorTransport transport, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writeString(transport.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\n\n/**\n * Jackson mixin for {@link Bytes}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.BytesMixin} based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = BytesJackson2Serializer.class)\nfinal class BytesJackson2Mixin {\n\n\t@JsonCreator\n\tstatic Bytes fromBase64(String value) {\n\t\treturn Bytes.fromBase64(value);\n\t}\n\n\tprivate BytesJackson2Mixin() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\n\n/**\n * Jackson serializer for {@link Bytes}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.BytesSerializer} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass BytesJackson2Serializer extends StdSerializer<Bytes> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tBytesJackson2Serializer() {\n\t\tsuper(Bytes.class);\n\t}\n\n\t@Override\n\tpublic void serialize(Bytes bytes, JsonGenerator jgen, SerializerProvider provider) throws IOException {\n\t\tjgen.writeString(bytes.toBase64UrlString());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\n\n/**\n * Jackson mixin for {@link Bytes}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonSerialize(using = BytesSerializer.class)\nfinal class BytesMixin {\n\n\t@JsonCreator\n\tstatic Bytes fromBase64(String value) {\n\t\treturn Bytes.fromBase64(value);\n\t}\n\n\tprivate BytesMixin() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/BytesSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\n\n/**\n * Jackson serializer for {@link Bytes}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass BytesSerializer extends StdSerializer<Bytes> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tBytesSerializer() {\n\t\tsuper(Bytes.class);\n\t}\n\n\t@Override\n\tpublic void serialize(Bytes bytes, JsonGenerator jgen, SerializationContext provider) throws JacksonException {\n\t\tjgen.writeString(bytes.toBase64UrlString());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.deser.std.StdDeserializer;\n\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\n\n/**\n * Jackson serializer for {@link COSEAlgorithmIdentifier}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass COSEAlgorithmIdentifierDeserializer extends StdDeserializer<COSEAlgorithmIdentifier> {\n\n\tCOSEAlgorithmIdentifierDeserializer() {\n\t\tsuper(COSEAlgorithmIdentifier.class);\n\t}\n\n\t@Override\n\tpublic @Nullable COSEAlgorithmIdentifier deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tLong transportValue = parser.readValueAs(Long.class);\n\t\tfor (COSEAlgorithmIdentifier identifier : COSEAlgorithmIdentifier.values()) {\n\t\t\tif (identifier.getValue() == transportValue.longValue()) {\n\t\t\t\treturn identifier;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Deserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JacksonException;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StdDeserializer;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\n\n/**\n * Jackson serializer for {@link COSEAlgorithmIdentifier}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.COSEAlgorithmIdentifierDeserializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass COSEAlgorithmIdentifierJackson2Deserializer extends StdDeserializer<COSEAlgorithmIdentifier> {\n\n\tCOSEAlgorithmIdentifierJackson2Deserializer() {\n\t\tsuper(COSEAlgorithmIdentifier.class);\n\t}\n\n\t@Override\n\tpublic @Nullable COSEAlgorithmIdentifier deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows IOException, JacksonException {\n\t\tLong transportValue = parser.readValueAs(Long.class);\n\t\tfor (COSEAlgorithmIdentifier identifier : COSEAlgorithmIdentifier.values()) {\n\t\t\tif (identifier.getValue() == transportValue.longValue()) {\n\t\t\t\treturn identifier;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\n\n/**\n * Jackson mixin for {@link COSEAlgorithmIdentifier}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.COSEAlgorithmIdentifierMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = COSEAlgorithmIdentifierJackson2Serializer.class)\n@JsonDeserialize(using = COSEAlgorithmIdentifierJackson2Deserializer.class)\nabstract class COSEAlgorithmIdentifierJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\n\n/**\n * Jackson serializer for {@link COSEAlgorithmIdentifier}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.COSEAlgorithmIdentifierSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass COSEAlgorithmIdentifierJackson2Serializer extends StdSerializer<COSEAlgorithmIdentifier> {\n\n\tCOSEAlgorithmIdentifierJackson2Serializer() {\n\t\tsuper(COSEAlgorithmIdentifier.class);\n\t}\n\n\t@Override\n\tpublic void serialize(COSEAlgorithmIdentifier identifier, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeNumber(identifier.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonDeserialize;\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\n\n/**\n * Jackson mixin for {@link COSEAlgorithmIdentifier}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonSerialize(using = COSEAlgorithmIdentifierSerializer.class)\n@JsonDeserialize(using = COSEAlgorithmIdentifierDeserializer.class)\nabstract class COSEAlgorithmIdentifierMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/COSEAlgorithmIdentifierSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\n\n/**\n * Jackson serializer for {@link COSEAlgorithmIdentifier}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass COSEAlgorithmIdentifierSerializer extends StdSerializer<COSEAlgorithmIdentifier> {\n\n\tCOSEAlgorithmIdentifierSerializer() {\n\t\tsuper(COSEAlgorithmIdentifier.class);\n\t}\n\n\t@Override\n\tpublic void serialize(COSEAlgorithmIdentifier identifier, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writeNumber(identifier.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\n/**\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.CredProtectAuthenticationExtensionsClientInputMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = CredProtectAuthenticationExtensionsClientInputJackson2Serializer.class)\nclass CredProtectAuthenticationExtensionsClientInputJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;\n\n/**\n * Serializes <a href=\n * \"https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension\">credProtect\n * extension</a>.\n *\n * @author Rob Winch\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.CredProtectAuthenticationExtensionsClientInputSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass CredProtectAuthenticationExtensionsClientInputJackson2Serializer\n\t\textends StdSerializer<CredProtectAuthenticationExtensionsClientInput> {\n\n\tprotected CredProtectAuthenticationExtensionsClientInputJackson2Serializer() {\n\t\tsuper(CredProtectAuthenticationExtensionsClientInput.class);\n\t}\n\n\t@Override\n\tpublic void serialize(CredProtectAuthenticationExtensionsClientInput input, JsonGenerator jgen,\n\t\t\tSerializerProvider provider) throws IOException {\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = input.getInput();\n\t\tString policy = toString(credProtect.getCredProtectionPolicy());\n\t\tjgen.writeObjectField(\"credentialProtectionPolicy\", policy);\n\t\tjgen.writeObjectField(\"enforceCredentialProtectionPolicy\", credProtect.isEnforceCredentialProtectionPolicy());\n\t}\n\n\tprivate static String toString(CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy policy) {\n\t\tswitch (policy) {\n\t\t\tcase USER_VERIFICATION_OPTIONAL:\n\t\t\t\treturn \"userVerificationOptional\";\n\t\t\tcase USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST:\n\t\t\t\treturn \"userVerificationOptionalWithCredentialIdList\";\n\t\t\tcase USER_VERIFICATION_REQUIRED:\n\t\t\t\treturn \"userVerificationRequired\";\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Unsupported ProtectionPolicy \" + policy);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonSerialize;\n\n@JsonSerialize(using = CredProtectAuthenticationExtensionsClientInputSerializer.class)\nclass CredProtectAuthenticationExtensionsClientInputMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;\n\n/**\n * Serializes <a href=\n * \"https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension\">credProtect\n * extension</a>.\n *\n * @author Rob Winch\n */\n@SuppressWarnings(\"serial\")\nclass CredProtectAuthenticationExtensionsClientInputSerializer\n\t\textends StdSerializer<CredProtectAuthenticationExtensionsClientInput> {\n\n\tprotected CredProtectAuthenticationExtensionsClientInputSerializer() {\n\t\tsuper(CredProtectAuthenticationExtensionsClientInput.class);\n\t}\n\n\t@Override\n\tpublic void serialize(CredProtectAuthenticationExtensionsClientInput input, JsonGenerator jgen,\n\t\t\tSerializationContext ctxt) throws JacksonException {\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = input.getInput();\n\t\tString policy = toString(credProtect.getCredProtectionPolicy());\n\t\tjgen.writePOJOProperty(\"credentialProtectionPolicy\", policy);\n\t\tjgen.writePOJOProperty(\"enforceCredentialProtectionPolicy\", credProtect.isEnforceCredentialProtectionPolicy());\n\t}\n\n\tprivate static String toString(CredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy policy) {\n\t\tswitch (policy) {\n\t\t\tcase USER_VERIFICATION_OPTIONAL:\n\t\t\t\treturn \"userVerificationOptional\";\n\t\t\tcase USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST:\n\t\t\t\treturn \"userVerificationOptionalWithCredentialIdList\";\n\t\t\tcase USER_VERIFICATION_REQUIRED:\n\t\t\t\treturn \"userVerificationRequired\";\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"Unsupported ProtectionPolicy \" + policy);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredentialPropertiesOutputJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\n\n/**\n * Jackson mixin for {@link CredentialPropertiesOutput}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.CredentialPropertiesOutputMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class CredentialPropertiesOutputJackson2Mixin {\n\n\tCredentialPropertiesOutputJackson2Mixin(@JsonProperty(\"rk\") boolean rk) {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/CredentialPropertiesOutputMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\n\n/**\n * Jackson mixin for {@link CredentialPropertiesOutput}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonIgnoreProperties(ignoreUnknown = true)\nabstract class CredentialPropertiesOutputMixin {\n\n\tCredentialPropertiesOutputMixin(@JsonProperty(\"rk\") boolean rk) {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\nimport java.time.Duration;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\n/**\n * Jackson serializer for {@link Duration}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.DurationSerializer} based on\n * Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass DurationJackson2Serializer extends StdSerializer<Duration> {\n\n\t/**\n\t * Creates an instance.\n\t */\n\tDurationJackson2Serializer() {\n\t\tsuper(Duration.class);\n\t}\n\n\t@Override\n\tpublic void serialize(Duration duration, JsonGenerator jgen, SerializerProvider provider) throws IOException {\n\t\tjgen.writeNumber(duration.toMillis());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/DurationSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.time.Duration;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\n/**\n * Jackson serializer for {@link Duration}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass DurationSerializer extends StdSerializer<Duration> {\n\n\t/**\n\t * Creates an instance.\n\t */\n\tDurationSerializer() {\n\t\tsuper(Duration.class);\n\t}\n\n\t@Override\n\tpublic void serialize(Duration duration, JsonGenerator jgen, SerializationContext ctxt) throws JacksonException {\n\t\tjgen.writeNumber(duration.toMillis());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ImmutablePublicKeyCredentialUserEntityMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;\n\n/**\n * Jackson mixin for {@link ImmutablePublicKeyCredentialUserEntity}\n *\n * @author Toshiaki Maki\n * @since 7.0.4\n */\nabstract class ImmutablePublicKeyCredentialUserEntityMixin {\n\n\tImmutablePublicKeyCredentialUserEntityMixin(@JsonProperty(\"name\") String name, @JsonProperty(\"id\") Bytes id,\n\t\t\t@JsonProperty(\"displayName\") String displayName) {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.time.Duration;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\n\n/**\n * Jackson mixin for {@link PublicKeyCredentialCreationOptions}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialCreationOptionsMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonInclude(JsonInclude.Include.NON_NULL)\nabstract class PublicKeyCredentialCreationOptionsJackson2Mixin {\n\n\t@JsonSerialize(using = DurationJackson2Serializer.class)\n\tprivate @Nullable Duration timeout;\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialCreationOptionsMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.time.Duration;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\n\n/**\n * Jackson mixin for {@link PublicKeyCredentialCreationOptions}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonInclude(JsonInclude.Include.NON_NULL)\nabstract class PublicKeyCredentialCreationOptionsMixin {\n\n\t@JsonSerialize(using = DurationSerializer.class)\n\tprivate @Nullable Duration timeout;\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\n\n/**\n * Jackson mixin for {@link PublicKeyCredential}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@JsonDeserialize(builder = PublicKeyCredential.PublicKeyCredentialBuilder.class)\nclass PublicKeyCredentialJackson2Mixin {\n\n\t@JsonPOJOBuilder(withPrefix = \"\")\n\tstatic class PublicKeyCredentialBuilderMixin {\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonDeserialize;\nimport tools.jackson.databind.annotation.JsonPOJOBuilder;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\n\n/**\n * Jackson mixin for {@link PublicKeyCredential}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonDeserialize(builder = PublicKeyCredential.PublicKeyCredentialBuilder.class)\nclass PublicKeyCredentialMixin {\n\n\t@JsonPOJOBuilder(withPrefix = \"\")\n\tstatic class PublicKeyCredentialBuilderMixin {\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.time.Duration;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\n\n/**\n * Jackson mixin for {@link PublicKeyCredentialRequestOptions}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialRequestOptionsMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonInclude(content = JsonInclude.Include.NON_NULL)\nclass PublicKeyCredentialRequestOptionsJackson2Mixin {\n\n\t@JsonSerialize(using = DurationJackson2Serializer.class)\n\tprivate final @Nullable Duration timeout = null;\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialRequestOptionsMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.time.Duration;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\n\n/**\n * Jackson mixin for {@link PublicKeyCredentialRequestOptions}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonInclude(content = JsonInclude.Include.NON_NULL)\nclass PublicKeyCredentialRequestOptionsMixin {\n\n\t@JsonSerialize(using = DurationSerializer.class)\n\tprivate final @Nullable Duration timeout = null;\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeDeserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonParser;\nimport tools.jackson.databind.DeserializationContext;\nimport tools.jackson.databind.deser.std.StdDeserializer;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\n\n/**\n * Jackson deserializer for {@link PublicKeyCredentialType}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass PublicKeyCredentialTypeDeserializer extends StdDeserializer<PublicKeyCredentialType> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tPublicKeyCredentialTypeDeserializer() {\n\t\tsuper(PublicKeyCredentialType.class);\n\t}\n\n\t@Override\n\tpublic PublicKeyCredentialType deserialize(JsonParser parser, DeserializationContext ctxt) throws JacksonException {\n\t\tString type = parser.readValueAs(String.class);\n\t\treturn PublicKeyCredentialType.valueOf(type);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Deserializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JacksonException;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.deser.std.StdDeserializer;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\n\n/**\n * Jackson deserializer for {@link PublicKeyCredentialType}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialTypeDeserializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass PublicKeyCredentialTypeJackson2Deserializer extends StdDeserializer<PublicKeyCredentialType> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tPublicKeyCredentialTypeJackson2Deserializer() {\n\t\tsuper(PublicKeyCredentialType.class);\n\t}\n\n\t@Override\n\tpublic PublicKeyCredentialType deserialize(JsonParser parser, DeserializationContext ctxt)\n\t\t\tthrows IOException, JacksonException {\n\t\tString type = parser.readValueAs(String.class);\n\t\treturn PublicKeyCredentialType.valueOf(type);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\n\n/**\n * Jackson mixin for {@link PublicKeyCredentialType}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialTypeMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = PublicKeyCredentialTypeJackson2Serializer.class)\n@JsonDeserialize(using = PublicKeyCredentialTypeJackson2Deserializer.class)\nabstract class PublicKeyCredentialTypeJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\n\n/**\n * Jackson serializer for {@link PublicKeyCredentialType}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.PublicKeyCredentialTypeSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass PublicKeyCredentialTypeJackson2Serializer extends StdSerializer<PublicKeyCredentialType> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tPublicKeyCredentialTypeJackson2Serializer() {\n\t\tsuper(PublicKeyCredentialType.class);\n\t}\n\n\t@Override\n\tpublic void serialize(PublicKeyCredentialType type, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeString(type.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonDeserialize;\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\n\n/**\n * Jackson mixin for {@link PublicKeyCredentialType}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonSerialize(using = PublicKeyCredentialTypeSerializer.class)\n@JsonDeserialize(using = PublicKeyCredentialTypeDeserializer.class)\nabstract class PublicKeyCredentialTypeMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialTypeSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\n\n/**\n * Jackson serializer for {@link PublicKeyCredentialType}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass PublicKeyCredentialTypeSerializer extends StdSerializer<PublicKeyCredentialType> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tPublicKeyCredentialTypeSerializer() {\n\t\tsuper(PublicKeyCredentialType.class);\n\t}\n\n\t@Override\n\tpublic void serialize(PublicKeyCredentialType type, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writeString(type.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/RelyingPartyPublicKeyJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;\n\n/**\n * Jackson mixin for {@link RelyingPartyPublicKey}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.RelyingPartyPublicKeyMixin}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\nabstract class RelyingPartyPublicKeyJackson2Mixin {\n\n\tRelyingPartyPublicKeyJackson2Mixin(\n\t\t\t@JsonProperty(\"credential\") PublicKeyCredential<AuthenticatorAttestationResponse> credential,\n\t\t\t@JsonProperty(\"label\") String label) {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/RelyingPartyPublicKeyMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;\n\n/**\n * Jackson mixin for {@link RelyingPartyPublicKey}\n *\n * @author Rob Winch\n * @since 6.4\n */\nabstract class RelyingPartyPublicKeyMixin {\n\n\tRelyingPartyPublicKeyMixin(\n\t\t\t@JsonProperty(\"credential\") PublicKeyCredential<AuthenticatorAttestationResponse> credential,\n\t\t\t@JsonProperty(\"label\") String label) {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.ResidentKeyRequirement;\n\n/**\n * Jackson mixin for {@link ResidentKeyRequirement}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.ResidentKeyRequirementMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = ResidentKeyRequirementJackson2Serializer.class)\nabstract class ResidentKeyRequirementJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.ResidentKeyRequirement;\n\n/**\n * Jackson serializer for {@link ResidentKeyRequirement}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.ResidentKeyRequirementSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass ResidentKeyRequirementJackson2Serializer extends StdSerializer<ResidentKeyRequirement> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tResidentKeyRequirementJackson2Serializer() {\n\t\tsuper(ResidentKeyRequirement.class);\n\t}\n\n\t@Override\n\tpublic void serialize(ResidentKeyRequirement requirement, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeString(requirement.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.ResidentKeyRequirement;\n\n/**\n * Jackson mixin for {@link ResidentKeyRequirement}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonSerialize(using = ResidentKeyRequirementSerializer.class)\nabstract class ResidentKeyRequirementMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/ResidentKeyRequirementSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.ResidentKeyRequirement;\n\n/**\n * Jackson serializer for {@link ResidentKeyRequirement}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass ResidentKeyRequirementSerializer extends StdSerializer<ResidentKeyRequirement> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tResidentKeyRequirementSerializer() {\n\t\tsuper(ResidentKeyRequirement.class);\n\t}\n\n\t@Override\n\tpublic void serialize(ResidentKeyRequirement requirement, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writeString(requirement.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementJackson2Mixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\n\n/**\n * Jackson mixin for {@link UserVerificationRequirement}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.UserVerificationRequirementMixin}\n * based on Jackson 3\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(forRemoval = true)\n@JsonSerialize(using = UserVerificationRequirementJackson2Serializer.class)\nabstract class UserVerificationRequirementJackson2Mixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementJackson2Serializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.io.IOException;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\n\n/**\n * Jackson serializer for {@link UserVerificationRequirement}\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.UserVerificationRequirementSerializer}\n * based on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings(\"serial\")\nclass UserVerificationRequirementJackson2Serializer extends StdSerializer<UserVerificationRequirement> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tUserVerificationRequirementJackson2Serializer() {\n\t\tsuper(UserVerificationRequirement.class);\n\t}\n\n\t@Override\n\tpublic void serialize(UserVerificationRequirement requirement, JsonGenerator jgen, SerializerProvider provider)\n\t\t\tthrows IOException {\n\t\tjgen.writeString(requirement.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.databind.annotation.JsonSerialize;\n\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\n\n/**\n * Jackson mixin for {@link UserVerificationRequirement}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@JsonSerialize(using = UserVerificationRequirementSerializer.class)\nabstract class UserVerificationRequirementMixin {\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/UserVerificationRequirementSerializer.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.JacksonException;\nimport tools.jackson.core.JsonGenerator;\nimport tools.jackson.databind.SerializationContext;\nimport tools.jackson.databind.ser.std.StdSerializer;\n\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\n\n/**\n * Jackson serializer for {@link UserVerificationRequirement}\n *\n * @author Rob Winch\n * @since 6.4\n */\n@SuppressWarnings(\"serial\")\nclass UserVerificationRequirementSerializer extends StdSerializer<UserVerificationRequirement> {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tUserVerificationRequirementSerializer() {\n\t\tsuper(UserVerificationRequirement.class);\n\t}\n\n\t@Override\n\tpublic void serialize(UserVerificationRequirement requirement, JsonGenerator jgen, SerializationContext ctxt)\n\t\t\tthrows JacksonException {\n\t\tjgen.writeString(requirement.getValue());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebAuthnAuthenticationMixin.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.util.Collection;\n\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication;\n\n/**\n * Jackson mixin for {@link WebAuthnAuthentication}\n *\n * @author Toshiaki Maki\n * @since 7.0.4\n */\n@JsonIgnoreProperties({ \"authenticated\" })\nabstract class WebAuthnAuthenticationMixin {\n\n\tWebAuthnAuthenticationMixin(@JsonProperty(\"principal\") PublicKeyCredentialUserEntity principal,\n\t\t\t@JsonProperty(\"authorities\") Collection<? extends GrantedAuthority> authorities) {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebauthnJackson2Module.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.core.Version;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\n\nimport org.springframework.security.web.webauthn.api.AttestationConveyancePreference;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\nimport org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.security.web.webauthn.api.ResidentKeyRequirement;\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\nimport org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;\n\n/**\n * Adds Jackson support for Spring Security WebAuthn. It is automatically registered when\n * using Jackson's SPI support.\n *\n * @author Rob Winch\n * @since 6.4\n * @deprecated as of 7.0 in favor of\n * {@link org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule} based\n * on Jackson 3\n */\n@Deprecated(forRemoval = true)\n@SuppressWarnings({ \"serial\", \"removal\" })\npublic class WebauthnJackson2Module extends SimpleModule {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tpublic WebauthnJackson2Module() {\n\t\tsuper(WebauthnJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixInAnnotations(Bytes.class, BytesJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(AttestationConveyancePreference.class,\n\t\t\t\tAttestationConveyancePreferenceJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticationExtensionsClientInput.class,\n\t\t\t\tAuthenticationExtensionsClientInputJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticationExtensionsClientInputs.class,\n\t\t\t\tAuthenticationExtensionsClientInputsJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticationExtensionsClientOutputs.class,\n\t\t\t\tAuthenticationExtensionsClientOutputsJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder.class,\n\t\t\t\tAuthenticatorAssertionResponseJackson2Mixin.AuthenticatorAssertionResponseBuilderMixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticatorAssertionResponse.class,\n\t\t\t\tAuthenticatorAssertionResponseJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticatorAttachment.class, AuthenticatorAttachmentJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticatorAttestationResponse.class,\n\t\t\t\tAuthenticatorAttestationResponseJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder.class,\n\t\t\t\tAuthenticatorAttestationResponseJackson2Mixin.AuthenticatorAttestationResponseBuilderMixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticatorSelectionCriteria.class, AuthenticatorSelectionCriteriaMixin.class);\n\t\tcontext.setMixInAnnotations(AuthenticatorTransport.class, AuthenticatorTransportJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(COSEAlgorithmIdentifier.class, COSEAlgorithmIdentifierJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(CredentialPropertiesOutput.class, CredentialPropertiesOutputJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(CredProtectAuthenticationExtensionsClientInput.class,\n\t\t\t\tCredProtectAuthenticationExtensionsClientInputJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(PublicKeyCredential.PublicKeyCredentialBuilder.class,\n\t\t\t\tPublicKeyCredentialJackson2Mixin.PublicKeyCredentialBuilderMixin.class);\n\t\tcontext.setMixInAnnotations(PublicKeyCredential.class, PublicKeyCredentialJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(PublicKeyCredentialCreationOptions.class,\n\t\t\t\tPublicKeyCredentialCreationOptionsJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(PublicKeyCredentialRequestOptions.class,\n\t\t\t\tPublicKeyCredentialRequestOptionsJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(PublicKeyCredentialType.class, PublicKeyCredentialTypeJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(RelyingPartyPublicKey.class, RelyingPartyPublicKeyJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(ResidentKeyRequirement.class, ResidentKeyRequirementJackson2Mixin.class);\n\t\tcontext.setMixInAnnotations(UserVerificationRequirement.class, UserVerificationRequirementJackson2Mixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/WebauthnJacksonModule.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport tools.jackson.core.Version;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.jackson.SecurityJacksonModule;\nimport org.springframework.security.web.webauthn.api.AttestationConveyancePreference;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientInputs;\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.COSEAlgorithmIdentifier;\nimport org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.security.web.webauthn.api.ResidentKeyRequirement;\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\nimport org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication;\nimport org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;\n\n/**\n * Adds Jackson support for Spring Security WebAuthn. It is automatically registered when\n * using Jackson's SPI support.\n *\n * @author Sebastien Deleuze\n * @author Rob Winch\n * @author Toshiaki Maki\n * @since 7.0\n */\n@SuppressWarnings(\"serial\")\npublic class WebauthnJacksonModule extends SecurityJacksonModule {\n\n\t/**\n\t * Creates a new instance.\n\t */\n\tpublic WebauthnJacksonModule() {\n\t\tsuper(WebauthnJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));\n\t}\n\n\t@Override\n\tpublic void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {\n\t\tbuilder.allowIfSubType(WebAuthnAuthentication.class)\n\t\t\t.allowIfSubType(ImmutablePublicKeyCredentialUserEntity.class);\n\t}\n\n\t@Override\n\tpublic void setupModule(SetupContext context) {\n\t\tcontext.setMixIn(Bytes.class, BytesMixin.class);\n\t\tcontext.setMixIn(AttestationConveyancePreference.class, AttestationConveyancePreferenceMixin.class);\n\t\tcontext.setMixIn(AuthenticationExtensionsClientInput.class, AuthenticationExtensionsClientInputMixin.class);\n\t\tcontext.setMixIn(AuthenticationExtensionsClientInputs.class, AuthenticationExtensionsClientInputsMixin.class);\n\t\tcontext.setMixIn(AuthenticationExtensionsClientOutputs.class, AuthenticationExtensionsClientOutputsMixin.class);\n\t\tcontext.setMixIn(AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder.class,\n\t\t\t\tAuthenticatorAssertionResponseMixin.AuthenticatorAssertionResponseBuilderMixin.class);\n\t\tcontext.setMixIn(AuthenticatorAssertionResponse.class, AuthenticatorAssertionResponseMixin.class);\n\t\tcontext.setMixIn(AuthenticatorAttachment.class, AuthenticatorAttachmentMixin.class);\n\t\tcontext.setMixIn(AuthenticatorAttestationResponse.class, AuthenticatorAttestationResponseMixin.class);\n\t\tcontext.setMixIn(AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder.class,\n\t\t\t\tAuthenticatorAttestationResponseMixin.AuthenticatorAttestationResponseBuilderMixin.class);\n\t\tcontext.setMixIn(AuthenticatorSelectionCriteria.class, AuthenticatorSelectionCriteriaMixin.class);\n\t\tcontext.setMixIn(AuthenticatorTransport.class, AuthenticatorTransportMixin.class);\n\t\tcontext.setMixIn(COSEAlgorithmIdentifier.class, COSEAlgorithmIdentifierMixin.class);\n\t\tcontext.setMixIn(CredentialPropertiesOutput.class, CredentialPropertiesOutputMixin.class);\n\t\tcontext.setMixIn(CredProtectAuthenticationExtensionsClientInput.class,\n\t\t\t\tCredProtectAuthenticationExtensionsClientInputMixin.class);\n\t\tcontext.setMixIn(PublicKeyCredential.PublicKeyCredentialBuilder.class,\n\t\t\t\tPublicKeyCredentialMixin.PublicKeyCredentialBuilderMixin.class);\n\t\tcontext.setMixIn(PublicKeyCredential.class, PublicKeyCredentialMixin.class);\n\t\tcontext.setMixIn(PublicKeyCredentialCreationOptions.class, PublicKeyCredentialCreationOptionsMixin.class);\n\t\tcontext.setMixIn(PublicKeyCredentialRequestOptions.class, PublicKeyCredentialRequestOptionsMixin.class);\n\t\tcontext.setMixIn(PublicKeyCredentialType.class, PublicKeyCredentialTypeMixin.class);\n\t\tcontext.setMixIn(RelyingPartyPublicKey.class, RelyingPartyPublicKeyMixin.class);\n\t\tcontext.setMixIn(ResidentKeyRequirement.class, ResidentKeyRequirementMixin.class);\n\t\tcontext.setMixIn(UserVerificationRequirement.class, UserVerificationRequirementMixin.class);\n\t\tcontext.setMixIn(WebAuthnAuthentication.class, WebAuthnAuthenticationMixin.class);\n\t\tcontext.setMixIn(ImmutablePublicKeyCredentialUserEntity.class,\n\t\t\t\tImmutablePublicKeyCredentialUserEntityMixin.class);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/jackson/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WebAuthn Jackson Support.\n */\n@NullMarked\npackage org.springframework.security.web.webauthn.jackson;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/ImmutablePublicKeyCredentialCreationOptionsRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.util.Assert;\n\n/**\n * An immutable implementation of {@link PublicKeyCredentialCreationOptionsRequest}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class ImmutablePublicKeyCredentialCreationOptionsRequest implements PublicKeyCredentialCreationOptionsRequest {\n\n\tprivate final Authentication authentication;\n\n\tpublic ImmutablePublicKeyCredentialCreationOptionsRequest(Authentication authentication) {\n\t\tAssert.notNull(authentication, \"authentication cannot be null\");\n\t\tthis.authentication = authentication;\n\t}\n\n\t@Override\n\tpublic Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/ImmutablePublicKeyCredentialRequestOptionsRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\npublic class ImmutablePublicKeyCredentialRequestOptionsRequest implements PublicKeyCredentialRequestOptionsRequest {\n\n\tprivate final @Nullable Authentication authentication;\n\n\tpublic ImmutablePublicKeyCredentialRequestOptionsRequest(@Nullable Authentication authentication) {\n\t\tthis.authentication = authentication;\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication getAuthentication() {\n\t\treturn this.authentication;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/ImmutableRelyingPartyRegistrationRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.util.Assert;\n\n/**\n * Contains the information necessary to register a new Credential.\n *\n * @author Rob Winch\n * @since 6.4\n * @see Webauthn4JRelyingPartyOperations#registerCredential(RelyingPartyRegistrationRequest)\n */\npublic class ImmutableRelyingPartyRegistrationRequest implements RelyingPartyRegistrationRequest {\n\n\tprivate final PublicKeyCredentialCreationOptions options;\n\n\tprivate final RelyingPartyPublicKey publicKey;\n\n\t/**\n\t * Creates a new instance.\n\t * @param options the {@link PublicKeyCredentialCreationOptions} that were saved when\n\t * {@link WebAuthnRelyingPartyOperations#createCredentialRequestOptions(PublicKeyCredentialRequestOptionsRequest)}\n\t * was called.\n\t * @param publicKey this is submitted by the client and if validated stored.\n\t */\n\tpublic ImmutableRelyingPartyRegistrationRequest(PublicKeyCredentialCreationOptions options,\n\t\t\t@Nullable RelyingPartyPublicKey publicKey) {\n\t\tAssert.notNull(options, \"options cannot be null\");\n\t\tAssert.notNull(publicKey, \"publicKey cannot be null\");\n\t\tthis.options = options;\n\t\tthis.publicKey = publicKey;\n\t}\n\n\t@Override\n\tpublic PublicKeyCredentialCreationOptions getCreationOptions() {\n\t\treturn this.options;\n\t}\n\n\t@Override\n\tpublic RelyingPartyPublicKey getPublicKey() {\n\t\treturn this.publicKey;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/JdbcPublicKeyCredentialUserEntityRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.util.Assert;\n\n/**\n * A JDBC implementation of an {@link PublicKeyCredentialUserEntityRepository} that uses a\n * {@link JdbcOperations} for {@link PublicKeyCredentialUserEntity} persistence.\n *\n * <b>NOTE:</b> This {@code PublicKeyCredentialUserEntityRepository} depends on the table\n * definition described in\n * \"classpath:org/springframework/security/user-entities-schema.sql\" and therefore MUST be\n * defined in the database schema.\n *\n * @author Max Batischev\n * @since 6.5\n * @see PublicKeyCredentialUserEntityRepository\n * @see PublicKeyCredentialUserEntity\n * @see JdbcOperations\n * @see RowMapper\n */\npublic final class JdbcPublicKeyCredentialUserEntityRepository implements PublicKeyCredentialUserEntityRepository {\n\n\tprivate RowMapper<PublicKeyCredentialUserEntity> userEntityRowMapper = new UserEntityRecordRowMapper();\n\n\tprivate Function<PublicKeyCredentialUserEntity, List<SqlParameterValue>> userEntityParametersMapper = new UserEntityParametersMapper();\n\n\tprivate final JdbcOperations jdbcOperations;\n\n\tprivate static final String TABLE_NAME = \"user_entities\";\n\n\t// @formatter:off\n\tprivate static final String COLUMN_NAMES = \"id, \"\n\t\t\t+ \"name, \"\n\t\t\t+ \"display_name \";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String SAVE_USER_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?)\";\n\t// @formatter:on\n\n\tprivate static final String ID_FILTER = \"id = ? \";\n\n\tprivate static final String USER_NAME_FILTER = \"name = ? \";\n\n\t// @formatter:off\n\tprivate static final String FIND_USER_BY_ID_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + ID_FILTER;\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String FIND_USER_BY_NAME_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + USER_NAME_FILTER;\n\t// @formatter:on\n\n\tprivate static final String DELETE_USER_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + ID_FILTER;\n\n\t// @formatter:off\n\tprivate static final String UPDATE_USER_SQL = \"UPDATE \" + TABLE_NAME\n\t\t\t+ \" SET name = ?, display_name = ? \"\n\t\t\t+ \" WHERE \" + ID_FILTER;\n\t// @formatter:on\n\n\t/**\n\t * Constructs a {@code JdbcPublicKeyCredentialUserEntityRepository} using the provided\n\t * parameters.\n\t * @param jdbcOperations the JDBC operations\n\t */\n\tpublic JdbcPublicKeyCredentialUserEntityRepository(JdbcOperations jdbcOperations) {\n\t\tAssert.notNull(jdbcOperations, \"jdbcOperations cannot be null\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t}\n\n\t@Override\n\tpublic @Nullable PublicKeyCredentialUserEntity findById(Bytes id) {\n\t\tAssert.notNull(id, \"id cannot be null\");\n\t\tList<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_ID_SQL,\n\t\t\t\tthis.userEntityRowMapper, id.toBase64UrlString());\n\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t}\n\n\t@Override\n\tpublic @Nullable PublicKeyCredentialUserEntity findByUsername(String username) {\n\t\tAssert.hasText(username, \"name cannot be null or empty\");\n\t\tList<PublicKeyCredentialUserEntity> result = this.jdbcOperations.query(FIND_USER_BY_NAME_SQL,\n\t\t\t\tthis.userEntityRowMapper, username);\n\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t}\n\n\t@Override\n\tpublic void save(PublicKeyCredentialUserEntity userEntity) {\n\t\tAssert.notNull(userEntity, \"userEntity cannot be null\");\n\t\tint rows = updateUserEntity(userEntity);\n\t\tif (rows == 0) {\n\t\t\tinsertUserEntity(userEntity);\n\t\t}\n\t}\n\n\tprivate void insertUserEntity(PublicKeyCredentialUserEntity userEntity) {\n\t\tList<SqlParameterValue> parameters = this.userEntityParametersMapper.apply(userEntity);\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\tthis.jdbcOperations.update(SAVE_USER_SQL, pss);\n\t}\n\n\tprivate int updateUserEntity(PublicKeyCredentialUserEntity userEntity) {\n\t\tList<SqlParameterValue> parameters = this.userEntityParametersMapper.apply(userEntity);\n\t\tSqlParameterValue userEntityId = parameters.remove(0);\n\t\tparameters.add(userEntityId);\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());\n\t\treturn this.jdbcOperations.update(UPDATE_USER_SQL, pss);\n\t}\n\n\t@Override\n\tpublic void delete(Bytes id) {\n\t\tAssert.notNull(id, \"id cannot be null\");\n\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, id.toBase64UrlString()), };\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\tthis.jdbcOperations.update(DELETE_USER_SQL, pss);\n\t}\n\n\tprivate static class UserEntityParametersMapper\n\t\t\timplements Function<PublicKeyCredentialUserEntity, List<SqlParameterValue>> {\n\n\t\t@Override\n\t\tpublic List<SqlParameterValue> apply(PublicKeyCredentialUserEntity userEntity) {\n\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, userEntity.getId().toBase64UrlString()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, userEntity.getName()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, userEntity.getDisplayName()));\n\n\t\t\treturn parameters;\n\t\t}\n\n\t}\n\n\tprivate static class UserEntityRecordRowMapper implements RowMapper<PublicKeyCredentialUserEntity> {\n\n\t\t@Override\n\t\tpublic PublicKeyCredentialUserEntity mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\tBytes id = Bytes.fromBase64(new String(rs.getString(\"id\").getBytes()));\n\t\t\tString name = rs.getString(\"name\");\n\t\t\tString displayName = rs.getString(\"display_name\");\n\n\t\t\treturn ImmutablePublicKeyCredentialUserEntity.builder().id(id).name(name).displayName(displayName).build();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.jdbc.core.ArgumentPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.SqlParameterValue;\nimport org.springframework.jdbc.support.lob.LobCreator;\nimport org.springframework.jdbc.support.lob.LobHandler;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCose;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\n/**\n * A JDBC implementation of an {@link UserCredentialRepository} that uses a\n * {@link JdbcOperations} for {@link CredentialRecord} persistence.\n *\n * <b>NOTE:</b> This {@code UserCredentialRepository} depends on the table definition\n * described in \"classpath:org/springframework/security/user-credentials-schema.sql\" and\n * therefore MUST be defined in the database schema.\n *\n * @author Max Batischev\n * @since 6.5\n * @see UserCredentialRepository\n * @see CredentialRecord\n * @see JdbcOperations\n * @see RowMapper\n */\npublic final class JdbcUserCredentialRepository implements UserCredentialRepository {\n\n\tprivate RowMapper<CredentialRecord> credentialRecordRowMapper = new CredentialRecordRowMapper(ResultSet::getBytes);\n\n\tprivate Function<CredentialRecord, List<SqlParameterValue>> credentialRecordParametersMapper = new CredentialRecordParametersMapper();\n\n\tprivate SetBytes setBytes = PreparedStatement::setBytes;\n\n\tprivate final JdbcOperations jdbcOperations;\n\n\tprivate static final String TABLE_NAME = \"user_credentials\";\n\n\t// @formatter:off\n\tprivate static final String COLUMN_NAMES = \"credential_id, \"\n\t\t\t+ \"user_entity_user_id, \"\n\t\t\t+ \"public_key, \"\n\t\t\t+ \"signature_count, \"\n\t\t\t+ \"uv_initialized, \"\n\t\t\t+ \"backup_eligible, \"\n\t\t\t+ \"authenticator_transports, \"\n\t\t\t+ \"public_key_credential_type, \"\n\t\t\t+ \"backup_state, \"\n\t\t\t+ \"attestation_object, \"\n\t\t\t+ \"attestation_client_data_json, \"\n\t\t\t+ \"created, \"\n\t\t\t+ \"last_used, \"\n\t\t\t+ \"label \";\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String SAVE_CREDENTIAL_RECORD_SQL = \"INSERT INTO \" + TABLE_NAME\n\t\t\t+ \" (\" + COLUMN_NAMES + \") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\";\n\t// @formatter:on\n\n\tprivate static final String ID_FILTER = \"credential_id = ? \";\n\n\tprivate static final String USER_ID_FILTER = \"user_entity_user_id = ? \";\n\n\t// @formatter:off\n\tprivate static final String FIND_CREDENTIAL_RECORD_BY_ID_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + ID_FILTER;\n\t// @formatter:on\n\n\t// @formatter:off\n\tprivate static final String FIND_CREDENTIAL_RECORD_BY_USER_ID_SQL = \"SELECT \" + COLUMN_NAMES\n\t\t\t+ \" FROM \" + TABLE_NAME\n\t\t\t+ \" WHERE \" + USER_ID_FILTER;\n\t// @formatter:on\n\n\tprivate static final String DELETE_CREDENTIAL_RECORD_SQL = \"DELETE FROM \" + TABLE_NAME + \" WHERE \" + ID_FILTER;\n\n\t// @formatter:off\n\tprivate static final String UPDATE_CREDENTIAL_RECORD_SQL = \"UPDATE \" + TABLE_NAME\n\t\t\t+ \" SET user_entity_user_id = ?, \" +\n\t\t\t\"public_key = ?, \" +\n\t\t\t\"signature_count = ?, \" +\n\t\t\t\"uv_initialized = ?, \" +\n\t\t\t\"backup_eligible = ? ,\" +\n\t\t\t\"authenticator_transports = ?, \" +\n\t\t\t\"public_key_credential_type = ?, \" +\n\t\t\t\"backup_state = ?, \" +\n\t\t\t\"attestation_object = ?, \" +\n\t\t\t\"attestation_client_data_json = ?, \" +\n\t\t\t\"created = ?, \" +\n\t\t\t\"last_used = ?, \" +\n\t\t\t\"label = ?\"\n\t\t\t+ \" WHERE \" + ID_FILTER;\n\t// @formatter:on\n\n\t/**\n\t * Constructs a {@code JdbcUserCredentialRepository} using the provided parameters.\n\t * @param jdbcOperations the JDBC operations\n\t */\n\tpublic JdbcUserCredentialRepository(JdbcOperations jdbcOperations) {\n\t\tAssert.notNull(jdbcOperations, \"jdbcOperations cannot be null\");\n\t\tthis.jdbcOperations = jdbcOperations;\n\t}\n\n\t@Override\n\tpublic void delete(Bytes credentialId) {\n\t\tAssert.notNull(credentialId, \"credentialId cannot be null\");\n\t\tSqlParameterValue[] parameters = new SqlParameterValue[] {\n\t\t\t\tnew SqlParameterValue(Types.VARCHAR, credentialId.toBase64UrlString()), };\n\t\tPreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);\n\t\tthis.jdbcOperations.update(DELETE_CREDENTIAL_RECORD_SQL, pss);\n\t}\n\n\t@Override\n\tpublic void save(CredentialRecord record) {\n\t\tAssert.notNull(record, \"record cannot be null\");\n\t\tint rows = updateCredentialRecord(record);\n\t\tif (rows == 0) {\n\t\t\tinsertCredentialRecord(record);\n\t\t}\n\t}\n\n\tprivate void insertCredentialRecord(CredentialRecord record) {\n\t\tList<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);\n\t\tPreparedStatementSetter pss = new BlobArgumentPreparedStatementSetter(this.setBytes, parameters.toArray());\n\t\tthis.jdbcOperations.update(SAVE_CREDENTIAL_RECORD_SQL, pss);\n\t}\n\n\tprivate int updateCredentialRecord(CredentialRecord record) {\n\t\tList<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);\n\t\tSqlParameterValue credentialId = parameters.remove(0);\n\t\tparameters.add(credentialId);\n\t\tPreparedStatementSetter pss = new BlobArgumentPreparedStatementSetter(this.setBytes, parameters.toArray());\n\t\treturn this.jdbcOperations.update(UPDATE_CREDENTIAL_RECORD_SQL, pss);\n\t}\n\n\t@Override\n\tpublic @Nullable CredentialRecord findByCredentialId(Bytes credentialId) {\n\t\tAssert.notNull(credentialId, \"credentialId cannot be null\");\n\t\tList<CredentialRecord> result = this.jdbcOperations.query(FIND_CREDENTIAL_RECORD_BY_ID_SQL,\n\t\t\t\tthis.credentialRecordRowMapper, credentialId.toBase64UrlString());\n\t\treturn !result.isEmpty() ? result.get(0) : null;\n\t}\n\n\t@Override\n\tpublic List<CredentialRecord> findByUserId(Bytes userId) {\n\t\tAssert.notNull(userId, \"userId cannot be null\");\n\t\treturn this.jdbcOperations.query(FIND_CREDENTIAL_RECORD_BY_USER_ID_SQL, this.credentialRecordRowMapper,\n\t\t\t\tuserId.toBase64UrlString());\n\t}\n\n\t/**\n\t * Sets a {@link LobHandler} for large binary fields and large text field parameters.\n\t * @param lobHandler the lob handler\n\t * @deprecated {@link LobHandler} is deprecated without replacement, as such this\n\t * method will also be removed without replacement\n\t */\n\t@Deprecated(since = \"6.5\", forRemoval = true)\n\tpublic void setLobHandler(LobHandler lobHandler) {\n\t\tAssert.notNull(lobHandler, \"lobHandler cannot be null\");\n\t\tthis.setBytes = (ps, index, bytes) -> {\n\t\t\ttry (LobCreator creator = lobHandler.getLobCreator()) {\n\t\t\t\tcreator.setBlobAsBytes(ps, index, bytes);\n\t\t\t}\n\t\t};\n\t\tthis.credentialRecordRowMapper = new CredentialRecordRowMapper(lobHandler::getBlobAsBytes);\n\t}\n\n\tprivate static class CredentialRecordParametersMapper\n\t\t\timplements Function<CredentialRecord, List<SqlParameterValue>> {\n\n\t\t@Override\n\t\tpublic List<SqlParameterValue> apply(CredentialRecord record) {\n\t\t\tList<SqlParameterValue> parameters = new ArrayList<>();\n\n\t\t\tList<String> transports = new ArrayList<>();\n\t\t\tif (!CollectionUtils.isEmpty(record.getTransports())) {\n\t\t\t\tfor (AuthenticatorTransport transport : record.getTransports()) {\n\t\t\t\t\ttransports.add(transport.getValue());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getCredentialId().toBase64UrlString()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getUserEntityUserId().toBase64UrlString()));\n\t\t\tparameters.add(new SqlParameterValue(Types.BLOB, record.getPublicKey().getBytes()));\n\t\t\tparameters.add(new SqlParameterValue(Types.BIGINT, record.getSignatureCount()));\n\t\t\tparameters.add(new SqlParameterValue(Types.BOOLEAN, record.isUvInitialized()));\n\t\t\tparameters.add(new SqlParameterValue(Types.BOOLEAN, record.isBackupEligible()));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR,\n\t\t\t\t\t(!CollectionUtils.isEmpty(record.getTransports())) ? String.join(\",\", transports) : \"\"));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR,\n\t\t\t\t\t(record.getCredentialType() != null) ? record.getCredentialType().getValue() : null));\n\t\t\tparameters.add(new SqlParameterValue(Types.BOOLEAN, record.isBackupState()));\n\t\t\tparameters.add(new SqlParameterValue(Types.BLOB,\n\t\t\t\t\t(record.getAttestationObject() != null) ? record.getAttestationObject().getBytes() : null));\n\t\t\tparameters.add(new SqlParameterValue(Types.BLOB, (record.getAttestationClientDataJSON() != null)\n\t\t\t\t\t? record.getAttestationClientDataJSON().getBytes() : null));\n\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP, fromInstant(record.getCreated())));\n\t\t\tparameters.add(new SqlParameterValue(Types.TIMESTAMP, fromInstant(record.getLastUsed())));\n\t\t\tparameters.add(new SqlParameterValue(Types.VARCHAR, record.getLabel()));\n\n\t\t\treturn parameters;\n\t\t}\n\n\t\tprivate @Nullable Timestamp fromInstant(Instant instant) {\n\t\t\tif (instant == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn Timestamp.from(instant);\n\t\t}\n\n\t}\n\n\tprivate interface SetBytes {\n\n\t\tvoid setBytes(PreparedStatement ps, int index, byte @Nullable [] bytes) throws SQLException;\n\n\t}\n\n\tprivate interface GetBytes {\n\n\t\tbyte @Nullable [] getBytes(ResultSet rs, String columnName) throws SQLException;\n\n\t}\n\n\tprivate static final class BlobArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {\n\n\t\tprivate final SetBytes setBytes;\n\n\t\tprivate BlobArgumentPreparedStatementSetter(SetBytes setBytes, Object[] args) {\n\t\t\tsuper(args);\n\t\t\tthis.setBytes = setBytes;\n\t\t}\n\n\t\t@Override\n\t\tprotected void doSetValue(PreparedStatement ps, int parameterPosition, @Nullable Object argValue)\n\t\t\t\tthrows SQLException {\n\t\t\tif (argValue instanceof SqlParameterValue paramValue) {\n\t\t\t\tif (paramValue.getSqlType() == Types.BLOB) {\n\t\t\t\t\tif (paramValue.getValue() != null) {\n\t\t\t\t\t\tAssert.isInstanceOf(byte[].class, paramValue.getValue(),\n\t\t\t\t\t\t\t\t\"Value of blob parameter must be byte[]\");\n\t\t\t\t\t}\n\t\t\t\t\tbyte[] valueBytes = (byte[]) paramValue.getValue();\n\t\t\t\t\tthis.setBytes.setBytes(ps, parameterPosition, valueBytes);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsuper.doSetValue(ps, parameterPosition, argValue);\n\t\t}\n\n\t}\n\n\tprivate static class CredentialRecordRowMapper implements RowMapper<CredentialRecord> {\n\n\t\tprivate final GetBytes getBytes;\n\n\t\tCredentialRecordRowMapper(GetBytes getBytes) {\n\t\t\tthis.getBytes = getBytes;\n\t\t}\n\n\t\t@Override\n\t\tpublic CredentialRecord mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\t\tBytes credentialId = Bytes.fromBase64(new String(rs.getString(\"credential_id\").getBytes()));\n\t\t\tBytes userEntityUserId = Bytes.fromBase64(new String(rs.getString(\"user_entity_user_id\").getBytes()));\n\t\t\tImmutablePublicKeyCose publicKey = new ImmutablePublicKeyCose(this.getBytes.getBytes(rs, \"public_key\"));\n\t\t\tlong signatureCount = rs.getLong(\"signature_count\");\n\t\t\tboolean uvInitialized = rs.getBoolean(\"uv_initialized\");\n\t\t\tboolean backupEligible = rs.getBoolean(\"backup_eligible\");\n\t\t\tPublicKeyCredentialType credentialType = PublicKeyCredentialType\n\t\t\t\t.valueOf(rs.getString(\"public_key_credential_type\"));\n\t\t\tboolean backupState = rs.getBoolean(\"backup_state\");\n\n\t\t\tBytes attestationObject = null;\n\t\t\tbyte[] rawAttestationObject = this.getBytes.getBytes(rs, \"attestation_object\");\n\t\t\tif (rawAttestationObject != null) {\n\t\t\t\tattestationObject = new Bytes(rawAttestationObject);\n\t\t\t}\n\n\t\t\tBytes attestationClientDataJson = null;\n\t\t\tbyte[] rawAttestationClientDataJson = this.getBytes.getBytes(rs, \"attestation_client_data_json\");\n\t\t\tif (rawAttestationClientDataJson != null) {\n\t\t\t\tattestationClientDataJson = new Bytes(rawAttestationClientDataJson);\n\t\t\t}\n\n\t\t\tInstant created = fromTimestamp(rs.getTimestamp(\"created\"));\n\t\t\tInstant lastUsed = fromTimestamp(rs.getTimestamp(\"last_used\"));\n\t\t\tString label = rs.getString(\"label\");\n\t\t\tString[] transports = rs.getString(\"authenticator_transports\").split(\",\");\n\n\t\t\tSet<AuthenticatorTransport> authenticatorTransports = new HashSet<>();\n\t\t\tfor (String transport : transports) {\n\t\t\t\tauthenticatorTransports.add(AuthenticatorTransport.valueOf(transport));\n\t\t\t}\n\t\t\tAssert.notNull(lastUsed, \"last_used cannot be null\");\n\t\t\tAssert.notNull(created, \"created cannot be null\");\n\t\t\treturn ImmutableCredentialRecord.builder()\n\t\t\t\t.credentialId(credentialId)\n\t\t\t\t.userEntityUserId(userEntityUserId)\n\t\t\t\t.publicKey(publicKey)\n\t\t\t\t.signatureCount(signatureCount)\n\t\t\t\t.uvInitialized(uvInitialized)\n\t\t\t\t.backupEligible(backupEligible)\n\t\t\t\t.credentialType(credentialType)\n\t\t\t\t.backupState(backupState)\n\t\t\t\t.attestationObject(attestationObject)\n\t\t\t\t.attestationClientDataJSON(attestationClientDataJson)\n\t\t\t\t.created(created)\n\t\t\t\t.label(label)\n\t\t\t\t.lastUsed(lastUsed)\n\t\t\t\t.transports(authenticatorTransports)\n\t\t\t\t.build();\n\t\t}\n\n\t\tprivate @Nullable Instant fromTimestamp(Timestamp timestamp) {\n\t\t\tif (timestamp == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn timestamp.toInstant();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapPublicKeyCredentialUserEntityRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Map} based implementation of {@link PublicKeyCredentialUserEntityRepository}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class MapPublicKeyCredentialUserEntityRepository implements PublicKeyCredentialUserEntityRepository {\n\n\tprivate final Map<String, PublicKeyCredentialUserEntity> usernameToUserEntity = new HashMap<>();\n\n\tprivate final Map<Bytes, PublicKeyCredentialUserEntity> idToUserEntity = new HashMap<>();\n\n\t@Override\n\tpublic @Nullable PublicKeyCredentialUserEntity findById(Bytes id) {\n\t\tAssert.notNull(id, \"id cannot be null\");\n\t\treturn this.idToUserEntity.get(id);\n\t}\n\n\t@Override\n\tpublic @Nullable PublicKeyCredentialUserEntity findByUsername(String username) {\n\t\tAssert.notNull(username, \"username cannot be null\");\n\t\treturn this.usernameToUserEntity.get(username);\n\t}\n\n\t@Override\n\tpublic void save(PublicKeyCredentialUserEntity userEntity) {\n\t\tif (userEntity == null) {\n\t\t\tthrow new IllegalArgumentException(\"userEntity cannot be null\");\n\t\t}\n\t\tthis.usernameToUserEntity.put(userEntity.getName(), userEntity);\n\t\tthis.idToUserEntity.put(userEntity.getId(), userEntity);\n\t}\n\n\t@Override\n\tpublic void delete(Bytes id) {\n\t\tPublicKeyCredentialUserEntity existing = this.idToUserEntity.remove(id);\n\t\tif (existing != null) {\n\t\t\tthis.usernameToUserEntity.remove(existing.getName());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/MapUserCredentialRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.util.Assert;\n\n/**\n * A {@link Map} based implementation of {@link UserCredentialRepository}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class MapUserCredentialRepository implements UserCredentialRepository {\n\n\tprivate final Map<Bytes, CredentialRecord> credentialIdToUserCredential = new HashMap<>();\n\n\tprivate final Map<Bytes, Set<Bytes>> userEntityIdToUserCredentialIds = new HashMap<>();\n\n\t@Override\n\tpublic void delete(Bytes credentialId) {\n\t\tAssert.notNull(credentialId, \"credentialId cannot be null\");\n\t\tCredentialRecord credentialRecord = this.credentialIdToUserCredential.remove(credentialId);\n\t\tif (credentialRecord != null) {\n\t\t\tSet<Bytes> credentialIds = this.userEntityIdToUserCredentialIds.get(credentialRecord.getUserEntityUserId());\n\t\t\tif (credentialIds != null) {\n\t\t\t\tcredentialIds.remove(credentialId);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void save(CredentialRecord credentialRecord) {\n\t\tAssert.notNull(credentialRecord, \"credentialRecord cannot be null\");\n\t\tthis.credentialIdToUserCredential.put(credentialRecord.getCredentialId(), credentialRecord);\n\t\tthis.userEntityIdToUserCredentialIds\n\t\t\t.computeIfAbsent(credentialRecord.getUserEntityUserId(), (id) -> new HashSet<>())\n\t\t\t.add(credentialRecord.getCredentialId());\n\t}\n\n\t@Override\n\tpublic @Nullable CredentialRecord findByCredentialId(Bytes credentialId) {\n\t\tAssert.notNull(credentialId, \"credentialId cannot be null\");\n\t\treturn this.credentialIdToUserCredential.get(credentialId);\n\t}\n\n\t@Override\n\tpublic List<CredentialRecord> findByUserId(Bytes userId) {\n\t\tAssert.notNull(userId, \"userId cannot be null\");\n\t\tSet<Bytes> credentialIds = this.userEntityIdToUserCredentialIds.getOrDefault(userId, Collections.emptySet());\n\t\treturn credentialIds.stream().map(this::findByCredentialId).collect(Collectors.toUnmodifiableList());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialCreationOptionsRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\n\n/**\n * A request to create a new {@link PublicKeyCredentialCreationOptions}.\n *\n * @author Rob Winch\n * @since 6.4\n * @see WebAuthnRelyingPartyOperations#createPublicKeyCredentialCreationOptions(PublicKeyCredentialCreationOptionsRequest)\n */\npublic interface PublicKeyCredentialCreationOptionsRequest {\n\n\t/**\n\t * The current {@link Authentication}. It must be authenticated to associate the\n\t * credential to a user.\n\t * @return the {@link Authentication} to use.\n\t */\n\tAuthentication getAuthentication();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialRequestOptionsRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.core.Authentication;\n\npublic interface PublicKeyCredentialRequestOptionsRequest {\n\n\t/**\n\t * The current {@link Authentication}. Possibly null or an anonymous.\n\t * @return the current {@link Authentication}\n\t */\n\t@Nullable Authentication getAuthentication();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/PublicKeyCredentialUserEntityRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\n\n/**\n * A repository for managing {@link PublicKeyCredentialUserEntity} instances.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface PublicKeyCredentialUserEntityRepository {\n\n\t/**\n\t * Finds the {@link PublicKeyCredentialUserEntity} by\n\t * {@link PublicKeyCredentialUserEntity#getId()}\n\t * @param id the id to lookup the username by\n\t * @return the username or null if not found.\n\t */\n\t@Nullable PublicKeyCredentialUserEntity findById(Bytes id);\n\n\t/**\n\t * Finds the {@link PublicKeyCredentialUserEntity} by the username.\n\t * @param username the username to lookup the {@link PublicKeyCredentialUserEntity}\n\t * @return the {@link PublicKeyCredentialUserEntity} or null if not found.\n\t */\n\t@Nullable PublicKeyCredentialUserEntity findByUsername(String username);\n\n\t/**\n\t * Saves the {@link PublicKeyCredentialUserEntity} to the associated username.\n\t * @param userEntity the {@link PublicKeyCredentialUserEntity} to associate to the\n\t * provided username. If null, any existing entry is deleted.\n\t */\n\tvoid save(PublicKeyCredentialUserEntity userEntity);\n\n\tvoid delete(Bytes id);\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/RelyingPartyAuthenticationRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.io.Serial;\nimport java.io.Serializable;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.util.Assert;\n\n/**\n * The data object used to provide the information necessary to authenticate a user with\n * WebAuthn.\n *\n * @author Rob Winch\n * @since 6.4\n * @see WebAuthnRelyingPartyOperations#authenticate(RelyingPartyAuthenticationRequest)\n */\npublic class RelyingPartyAuthenticationRequest implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = -928083091875202086L;\n\n\tprivate final PublicKeyCredentialRequestOptions requestOptions;\n\n\tprivate final PublicKeyCredential<AuthenticatorAssertionResponse> publicKey;\n\n\t/**\n\t * Creates a new instance.\n\t * @param requestOptions the {@link PublicKeyCredentialRequestOptions}\n\t * @param publicKey the {@link PublicKeyCredential}\n\t */\n\tpublic RelyingPartyAuthenticationRequest(PublicKeyCredentialRequestOptions requestOptions,\n\t\t\tPublicKeyCredential<AuthenticatorAssertionResponse> publicKey) {\n\t\tAssert.notNull(requestOptions, \"requestOptions cannot be null\");\n\t\tAssert.notNull(publicKey, \"publicKey cannot be null\");\n\t\tthis.requestOptions = requestOptions;\n\t\tthis.publicKey = publicKey;\n\t}\n\n\t/**\n\t * Ges the request options.\n\t * @return the request options.\n\t */\n\tpublic PublicKeyCredentialRequestOptions getRequestOptions() {\n\t\treturn this.requestOptions;\n\t}\n\n\t/**\n\t * Gets the public key.\n\t * @return the public key.\n\t */\n\tpublic PublicKeyCredential<AuthenticatorAssertionResponse> getPublicKey() {\n\t\treturn this.publicKey;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/RelyingPartyPublicKey.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.util.Assert;\n\n/**\n * Submitted by a client to request registration of a new credential.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class RelyingPartyPublicKey {\n\n\tprivate final PublicKeyCredential<AuthenticatorAttestationResponse> credential;\n\n\tprivate final String label;\n\n\t/**\n\t * Creates a new instance.\n\t * @param credential the credential\n\t * @param label a human readable label that will be associated to the credential\n\t */\n\tpublic RelyingPartyPublicKey(PublicKeyCredential<AuthenticatorAttestationResponse> credential, String label) {\n\t\tAssert.notNull(credential, \"credential cannot be null\");\n\t\tAssert.notNull(label, \"label cannot be null\");\n\t\tthis.credential = credential;\n\t\tthis.label = label;\n\t}\n\n\tpublic PublicKeyCredential<AuthenticatorAttestationResponse> getCredential() {\n\t\treturn this.credential;\n\t}\n\n\tpublic String getLabel() {\n\t\treturn this.label;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/RelyingPartyRegistrationRequest.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\n\n/**\n * @author Rob Winch\n * @since 6.4\n * @see WebAuthnRelyingPartyOperations#registerCredential(RelyingPartyRegistrationRequest)\n */\npublic interface RelyingPartyRegistrationRequest {\n\n\tPublicKeyCredentialCreationOptions getCreationOptions();\n\n\tRelyingPartyPublicKey getPublicKey();\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/UserCredentialRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\n\n/**\n * A repository for managing {@link CredentialRecord}s associated to a user.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface UserCredentialRepository {\n\n\t/**\n\t * Deletes an entry by credential id\n\t * @param credentialId {@link CredentialRecord#getCredentialId()}\n\t */\n\tvoid delete(Bytes credentialId);\n\n\t/**\n\t * Saves a {@link CredentialRecord}\n\t * @param credentialRecord the {@link CredentialRecord} to save.\n\t */\n\tvoid save(CredentialRecord credentialRecord);\n\n\t/**\n\t * Finds an entry by credential id.\n\t * @param credentialId {@link CredentialRecord#getCredentialId()}\n\t * @return the {@link CredentialRecord} or null if not found.\n\t */\n\t@Nullable CredentialRecord findByCredentialId(Bytes credentialId);\n\n\t/**\n\t * Finds all {@link CredentialRecord} instances for a specific user.\n\t * @param userId the {@link PublicKeyCredentialUserEntity#getId()} to search for a\n\t * user.\n\t * @return all {@link CredentialRecord} instances for a specific user or empty if no\n\t * results found. Never null.\n\t * @see PublicKeyCredentialUserEntityRepository\n\t */\n\tList<CredentialRecord> findByUserId(Bytes userId);\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/WebAuthnRelyingPartyOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\n\n/**\n * An API for <a href=\"https://www.w3.org/TR/webauthn-3/#sctn-rp-operations\">WebAuthn\n * Relying Party Operations</a>\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface WebAuthnRelyingPartyOperations {\n\n\t/**\n\t * Creates the {@link PublicKeyCredentialCreationOptions} used to register new\n\t * credentials.\n\t * @param request the {@link PublicKeyCredentialCreationOptionsRequest} to create the\n\t * {@link PublicKeyCredentialCreationOptions}\n\t * @return the {@link PublicKeyCredentialCreationOptions} for the\n\t * {@link Authentication} passed in. Cannot be null.\n\t */\n\tPublicKeyCredentialCreationOptions createPublicKeyCredentialCreationOptions(\n\t\t\tPublicKeyCredentialCreationOptionsRequest request);\n\n\t/**\n\t * If {@link RelyingPartyRegistrationRequest} is valid, will <a href=\n\t * \"https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\">register</a>\n\t * and return a new {@link CredentialRecord}.\n\t * @param relyingPartyRegistrationRequest the {@link RelyingPartyRegistrationRequest}\n\t * to process.\n\t * @return a new {@link CredentialRecord}\n\t * @throws RuntimeException if the {@link RelyingPartyRegistrationRequest} is not\n\t * valid.\n\t */\n\tCredentialRecord registerCredential(RelyingPartyRegistrationRequest relyingPartyRegistrationRequest);\n\n\t/**\n\t * Creates the {@link PublicKeyCredentialRequestOptions} used to authenticate a user.\n\t * @param request the {@link PublicKeyCredentialRequestOptionsRequest}.\n\t * @return the {@link PublicKeyCredentialRequestOptions} used to authenticate a user.\n\t */\n\tPublicKeyCredentialRequestOptions createCredentialRequestOptions(PublicKeyCredentialRequestOptionsRequest request);\n\n\t/**\n\t * Authenticates the {@link RelyingPartyAuthenticationRequest} passed in\n\t * @param request the {@link RelyingPartyAuthenticationRequest}\n\t * @return the principal name (e.g. username) if authentication was successful\n\t * @throws RuntimeException if authentication fails\n\t */\n\tPublicKeyCredentialUserEntity authenticate(RelyingPartyAuthenticationRequest request);\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport com.webauthn4j.WebAuthnManager;\nimport com.webauthn4j.converter.util.CborConverter;\nimport com.webauthn4j.converter.util.ObjectConverter;\nimport com.webauthn4j.credential.CredentialRecordImpl;\nimport com.webauthn4j.data.AuthenticationData;\nimport com.webauthn4j.data.AuthenticationParameters;\nimport com.webauthn4j.data.RegistrationData;\nimport com.webauthn4j.data.RegistrationParameters;\nimport com.webauthn4j.data.RegistrationRequest;\nimport com.webauthn4j.data.attestation.AttestationObject;\nimport com.webauthn4j.data.attestation.authenticator.AttestedCredentialData;\nimport com.webauthn4j.data.attestation.authenticator.AuthenticatorData;\nimport com.webauthn4j.data.attestation.authenticator.COSEKey;\nimport com.webauthn4j.data.client.Origin;\nimport com.webauthn4j.data.client.challenge.Challenge;\nimport com.webauthn4j.data.client.challenge.DefaultChallenge;\nimport com.webauthn4j.data.extension.authenticator.AuthenticationExtensionAuthenticatorOutput;\nimport com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput;\nimport com.webauthn4j.server.ServerProperty;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.authentication.AuthenticationTrustResolver;\nimport org.springframework.security.authentication.AuthenticationTrustResolverImpl;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.webauthn.api.AttestationConveyancePreference;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;\nimport org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCose;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions.PublicKeyCredentialCreationOptionsBuilder;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialParameters;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions.PublicKeyCredentialRequestOptionsBuilder;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.ResidentKeyRequirement;\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\nimport org.springframework.util.Assert;\n\n/**\n * A <a href=\"https://webauthn4j.github.io/webauthn4j/en/\">WebAuthn4j</a> implementation\n * of {@link WebAuthnRelyingPartyOperations}.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class Webauthn4JRelyingPartyOperations implements WebAuthnRelyingPartyOperations {\n\n\tprivate final PublicKeyCredentialUserEntityRepository userEntities;\n\n\tprivate final UserCredentialRepository userCredentials;\n\n\tprivate final Set<String> allowedOrigins;\n\n\tprivate final PublicKeyCredentialRpEntity rp;\n\n\tprivate ObjectConverter objectConverter = new ObjectConverter();\n\n\tprivate final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();\n\n\tprivate WebAuthnManager webAuthnManager = WebAuthnManager.createNonStrictWebAuthnManager();\n\n\tprivate Consumer<PublicKeyCredentialCreationOptionsBuilder> customizeCreationOptions = (options) -> {\n\t};\n\n\tprivate Consumer<PublicKeyCredentialRequestOptionsBuilder> customizeRequestOptions = (options) -> {\n\t};\n\n\t/**\n\t * Creates a new instance.\n\t * @param userEntities the {@link PublicKeyCredentialUserEntityRepository} to use.\n\t * @param userCredentials the {@link UserCredentialRepository} to use.\n\t * @param rpEntity the {@link PublicKeyCredentialRpEntity} to use.\n\t * @param allowedOrigins the allowed origins.\n\t */\n\tpublic Webauthn4JRelyingPartyOperations(PublicKeyCredentialUserEntityRepository userEntities,\n\t\t\tUserCredentialRepository userCredentials, PublicKeyCredentialRpEntity rpEntity,\n\t\t\tSet<String> allowedOrigins) {\n\t\tAssert.notNull(userEntities, \"userEntities cannot be null\");\n\t\tAssert.notNull(userCredentials, \"userCredentials cannot be null\");\n\t\tAssert.notNull(rpEntity, \"rpEntity cannot be null\");\n\t\tAssert.notNull(allowedOrigins, \"allowedOrigins cannot be null\");\n\t\tthis.userEntities = userEntities;\n\t\tthis.userCredentials = userCredentials;\n\t\tthis.rp = rpEntity;\n\t\tthis.allowedOrigins = allowedOrigins;\n\t}\n\n\t/**\n\t * Sets the {@link WebAuthnManager} to use. The default is\n\t * {@link WebAuthnManager#createNonStrictWebAuthnManager()}\n\t * @param webAuthnManager the {@link WebAuthnManager}.\n\t */\n\tpublic void setWebAuthnManager(WebAuthnManager webAuthnManager) {\n\t\tAssert.notNull(webAuthnManager, \"webAuthnManager cannot be null\");\n\t\tthis.webAuthnManager = webAuthnManager;\n\t}\n\n\t/**\n\t * Sets the {@link ObjectConverter} to use.\n\t * @param objectConverter the {@link ObjectConverter} to use. Cannot be null.\n\t */\n\tvoid setObjectConverter(ObjectConverter objectConverter) {\n\t\tAssert.notNull(objectConverter, \"objectConverter cannot be null\");\n\t\tthis.objectConverter = objectConverter;\n\t}\n\n\t/**\n\t * Sets a {@link Consumer} used to customize the\n\t * {@link PublicKeyCredentialCreationOptionsBuilder} for\n\t * {@link #createPublicKeyCredentialCreationOptions(PublicKeyCredentialCreationOptionsRequest)}.\n\t * The default values are always populated, but can be overridden with this property.\n\t * @param customizeCreationOptions the {@link Consumer} to customize the\n\t * {@link PublicKeyCredentialCreationOptionsBuilder}\n\t */\n\tpublic void setCustomizeCreationOptions(\n\t\t\tConsumer<PublicKeyCredentialCreationOptionsBuilder> customizeCreationOptions) {\n\t\tAssert.notNull(customizeCreationOptions, \"customizeCreationOptions must not be null\");\n\t\tthis.customizeCreationOptions = customizeCreationOptions;\n\t}\n\n\t/**\n\t * Sets a {@link Consumer} used to customize the\n\t * {@link PublicKeyCredentialRequestOptionsBuilder} for\n\t * {@link #createCredentialRequestOptions(PublicKeyCredentialRequestOptionsRequest)}.The\n\t * default values are always populated, but can be overridden with this property.\n\t * @param customizeRequestOptions the {@link Consumer} to customize the\n\t * {@link PublicKeyCredentialRequestOptionsBuilder}\n\t */\n\tpublic void setCustomizeRequestOptions(Consumer<PublicKeyCredentialRequestOptionsBuilder> customizeRequestOptions) {\n\t\tAssert.notNull(customizeRequestOptions, \"customizeRequestOptions cannot be null\");\n\t\tthis.customizeRequestOptions = customizeRequestOptions;\n\t}\n\n\t@Override\n\tpublic PublicKeyCredentialCreationOptions createPublicKeyCredentialCreationOptions(\n\t\t\tPublicKeyCredentialCreationOptionsRequest request) {\n\t\tif (request == null) {\n\t\t\tthrow new IllegalArgumentException(\"request cannot be null\");\n\t\t}\n\t\tAuthentication authentication = request.getAuthentication();\n\t\tif (!this.trustResolver.isAuthenticated(authentication)) {\n\t\t\tthrow new IllegalArgumentException(\"Authentication must be authenticated\");\n\t\t}\n\t\tAuthenticatorSelectionCriteria authenticatorSelection = AuthenticatorSelectionCriteria.builder()\n\t\t\t.userVerification(UserVerificationRequirement.PREFERRED)\n\t\t\t.residentKey(ResidentKeyRequirement.REQUIRED)\n\t\t\t.build();\n\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tImmutableAuthenticationExtensionsClientInput.credProps);\n\n\t\tPublicKeyCredentialUserEntity userEntity = findUserEntityOrCreateAndSave(authentication.getName());\n\t\tList<CredentialRecord> credentialRecords = this.userCredentials.findByUserId(userEntity.getId());\n\n\t\tPublicKeyCredentialCreationOptions options = PublicKeyCredentialCreationOptions.builder()\n\t\t\t.attestation(AttestationConveyancePreference.NONE)\n\t\t\t.pubKeyCredParams(PublicKeyCredentialParameters.EdDSA, PublicKeyCredentialParameters.ES256,\n\t\t\t\t\tPublicKeyCredentialParameters.RS256)\n\t\t\t.authenticatorSelection(authenticatorSelection)\n\t\t\t.challenge(Bytes.random())\n\t\t\t.extensions(clientInputs)\n\t\t\t.timeout(Duration.ofMinutes(5))\n\t\t\t.user(userEntity)\n\t\t\t.rp(this.rp)\n\t\t\t.excludeCredentials(credentialDescriptors(credentialRecords))\n\t\t\t.customize(this.customizeCreationOptions)\n\t\t\t.build();\n\t\treturn options;\n\t}\n\n\tprivate static List<PublicKeyCredentialDescriptor> credentialDescriptors(List<CredentialRecord> credentialRecords) {\n\t\tList<PublicKeyCredentialDescriptor> result = new ArrayList<>();\n\t\tfor (CredentialRecord credentialRecord : credentialRecords) {\n\t\t\tBytes id = Bytes.fromBase64(credentialRecord.getCredentialId().toBase64UrlString());\n\t\t\tPublicKeyCredentialDescriptor credentialDescriptor = PublicKeyCredentialDescriptor.builder()\n\t\t\t\t.id(id)\n\t\t\t\t.transports(credentialRecord.getTransports())\n\t\t\t\t.build();\n\t\t\tresult.add(credentialDescriptor);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate PublicKeyCredentialUserEntity findUserEntityOrCreateAndSave(String username) {\n\t\tfinal PublicKeyCredentialUserEntity foundUserEntity = this.userEntities.findByUsername(username);\n\t\tif (foundUserEntity != null) {\n\t\t\treturn foundUserEntity;\n\t\t}\n\n\t\tPublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t.displayName(username)\n\t\t\t.id(Bytes.random())\n\t\t\t.name(username)\n\t\t\t.build();\n\t\tthis.userEntities.save(userEntity);\n\t\treturn userEntity;\n\t}\n\n\t@Override\n\tpublic CredentialRecord registerCredential(RelyingPartyRegistrationRequest rpRegistrationRequest) {\n\t\tAssert.notNull(rpRegistrationRequest, \"rpRegistrationRequest cannot be null\");\n\t\tBytes credentialId = rpRegistrationRequest.getPublicKey().getCredential().getRawId();\n\t\tCredentialRecord existingCredential = this.userCredentials.findByCredentialId(credentialId);\n\t\tif (existingCredential != null) {\n\t\t\tthrow new IllegalArgumentException(\"Credential with id \" + credentialId + \" already exists\");\n\t\t}\n\t\tPublicKeyCredentialCreationOptions creationOptions = rpRegistrationRequest.getCreationOptions();\n\t\tString rpId = creationOptions.getRp().getId();\n\t\tRelyingPartyPublicKey publicKey = rpRegistrationRequest.getPublicKey();\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> credential = publicKey.getCredential();\n\t\tAuthenticatorAttestationResponse response = credential.getResponse();\n\t\t// Server properties\n\t\tSet<Origin> origins = toOrigins();\n\t\tbyte[] base64Challenge = creationOptions.getChallenge().getBytes();\n\t\tbyte[] attestationObject = response.getAttestationObject().getBytes();\n\t\tbyte[] clientDataJSON = response.getClientDataJSON().getBytes();\n\t\tChallenge challenge = new DefaultChallenge(base64Challenge);\n\t\tServerProperty serverProperty = new ServerProperty(origins, rpId, challenge);\n\t\tboolean userVerificationRequired = creationOptions.getAuthenticatorSelection()\n\t\t\t.getUserVerification() == UserVerificationRequirement.REQUIRED;\n\t\t// requireUserPresence The constant Boolean value true\n\t\t// https://www.w3.org/TR/webauthn-3/#sctn-op-make-cred\n\t\tboolean userPresenceRequired = true;\n\t\tList<com.webauthn4j.data.PublicKeyCredentialParameters> pubKeyCredParams = convertCredentialParamsToWebauthn4j(\n\t\t\t\tcreationOptions.getPubKeyCredParams());\n\t\tSet<String> transports = convertTransportsToString(response);\n\t\tRegistrationRequest webauthn4jRegistrationRequest = new RegistrationRequest(attestationObject, clientDataJSON,\n\t\t\t\ttransports);\n\t\tRegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, pubKeyCredParams,\n\t\t\t\tuserVerificationRequired, userPresenceRequired);\n\t\tRegistrationData wa4jRegistrationData = this.webAuthnManager.verify(webauthn4jRegistrationRequest,\n\t\t\t\tregistrationParameters);\n\t\tAttestationObject wa4jAttestationObject = wa4jRegistrationData.getAttestationObject();\n\t\tAssert.notNull(wa4jAttestationObject, \"attestationObject cannot be null\");\n\t\tAuthenticatorData<RegistrationExtensionAuthenticatorOutput> wa4jAuthData = wa4jAttestationObject\n\t\t\t.getAuthenticatorData();\n\n\t\tCborConverter cborConverter = this.objectConverter.getCborConverter();\n\t\tAttestedCredentialData wa4jCredData = wa4jAuthData.getAttestedCredentialData();\n\t\tAssert.notNull(wa4jCredData, \"attestedCredentialData cannot be null\");\n\t\tCOSEKey coseKey = wa4jCredData.getCOSEKey();\n\t\tbyte[] rawCoseKey = cborConverter.writeValueAsBytes(coseKey);\n\t\tImmutableCredentialRecord userCredential = ImmutableCredentialRecord.builder()\n\t\t\t.userEntityUserId(creationOptions.getUser().getId())\n\t\t\t.credentialType(credential.getType())\n\t\t\t.credentialId(credential.getRawId())\n\t\t\t.publicKey(new ImmutablePublicKeyCose(rawCoseKey))\n\t\t\t.signatureCount(wa4jAuthData.getSignCount())\n\t\t\t.uvInitialized(wa4jAuthData.isFlagUV())\n\t\t\t.transports(convertTransports(wa4jRegistrationData.getTransports()))\n\t\t\t.backupEligible(wa4jAuthData.isFlagBE())\n\t\t\t.backupState(wa4jAuthData.isFlagBS())\n\t\t\t.label(publicKey.getLabel())\n\t\t\t.attestationClientDataJSON(credential.getResponse().getClientDataJSON())\n\t\t\t.attestationObject(credential.getResponse().getAttestationObject())\n\t\t\t.build();\n\t\tthis.userCredentials.save(userCredential);\n\t\treturn userCredential;\n\t}\n\n\tprivate static @Nullable Set<String> convertTransportsToString(AuthenticatorAttestationResponse response) {\n\t\tif (response.getTransports() == null) {\n\t\t\treturn null;\n\t\t}\n\t\tSet<String> transports = new HashSet<>(response.getTransports().size());\n\t\tfor (AuthenticatorTransport transport : response.getTransports()) {\n\t\t\ttransports.add(transport.getValue());\n\t\t}\n\t\treturn transports;\n\t}\n\n\tprivate List<com.webauthn4j.data.PublicKeyCredentialParameters> convertCredentialParamsToWebauthn4j(\n\t\t\tList<PublicKeyCredentialParameters> parameters) {\n\t\treturn parameters.stream().map(this::convertParamToWebauthn4j).toList();\n\t}\n\n\tprivate com.webauthn4j.data.PublicKeyCredentialParameters convertParamToWebauthn4j(\n\t\t\tPublicKeyCredentialParameters parameter) {\n\t\tPublicKeyCredentialType credentialType = PublicKeyCredentialType.valueOf(parameter.getType().getValue());\n\t\tif (credentialType != PublicKeyCredentialType.PUBLIC_KEY) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Cannot convert unknown credential type \" + parameter.getType() + \" to webauthn4j\");\n\t\t}\n\t\tlong algValue = parameter.getAlg().getValue();\n\t\tcom.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier alg = com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier\n\t\t\t.create(algValue);\n\t\treturn new com.webauthn4j.data.PublicKeyCredentialParameters(\n\t\t\t\tcom.webauthn4j.data.PublicKeyCredentialType.PUBLIC_KEY, alg);\n\t}\n\n\tprivate Set<Origin> toOrigins() {\n\t\treturn this.allowedOrigins.stream().map(Origin::new).collect(Collectors.toSet());\n\t}\n\n\tprivate static Set<AuthenticatorTransport> convertTransports(\n\t\t\t@Nullable Set<com.webauthn4j.data.AuthenticatorTransport> transports) {\n\t\tif (transports == null) {\n\t\t\treturn Collections.emptySet();\n\t\t}\n\t\treturn transports.stream()\n\t\t\t.map((t) -> AuthenticatorTransport.valueOf(t.getValue()))\n\t\t\t.collect(Collectors.toUnmodifiableSet());\n\t}\n\n\t@Override\n\tpublic PublicKeyCredentialRequestOptions createCredentialRequestOptions(\n\t\t\tPublicKeyCredentialRequestOptionsRequest request) {\n\t\tAuthentication authentication = request.getAuthentication();\n\t\tList<CredentialRecord> credentialRecords = findCredentialRecords(authentication);\n\t\treturn PublicKeyCredentialRequestOptions.builder()\n\t\t\t.allowCredentials(credentialDescriptors(credentialRecords))\n\t\t\t.challenge(Bytes.random())\n\t\t\t.rpId(this.rp.getId())\n\t\t\t.timeout(Duration.ofMinutes(5))\n\t\t\t.userVerification(UserVerificationRequirement.PREFERRED)\n\t\t\t.customize(this.customizeRequestOptions)\n\t\t\t.build();\n\t}\n\n\tprivate List<CredentialRecord> findCredentialRecords(@Nullable Authentication authentication) {\n\t\tif (!this.trustResolver.isAuthenticated(authentication)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\tPublicKeyCredentialUserEntity userEntity = this.userEntities.findByUsername(authentication.getName());\n\t\tif (userEntity == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn this.userCredentials.findByUserId(userEntity.getId());\n\t}\n\n\t@Override\n\tpublic PublicKeyCredentialUserEntity authenticate(RelyingPartyAuthenticationRequest request) {\n\t\tPublicKeyCredentialRequestOptions requestOptions = request.getRequestOptions();\n\t\tAuthenticatorAssertionResponse assertionResponse = request.getPublicKey().getResponse();\n\t\tBytes keyId = request.getPublicKey().getRawId();\n\t\tCredentialRecord credentialRecord = this.userCredentials.findByCredentialId(keyId);\n\t\tif (credentialRecord == null) {\n\t\t\tthrow new IllegalArgumentException(\"Unable to find CredentialRecord with id \" + keyId);\n\t\t}\n\t\tCborConverter cborConverter = this.objectConverter.getCborConverter();\n\t\tBytes attestationObject = credentialRecord.getAttestationObject();\n\t\tAssert.notNull(attestationObject, \"attestationObject cannot be null\");\n\t\tAttestationObject wa4jAttestationObject = cborConverter.readValue(attestationObject.getBytes(),\n\t\t\t\tAttestationObject.class);\n\t\tAssert.notNull(wa4jAttestationObject, \"attestationObject cannot be null\");\n\t\tAuthenticatorData<RegistrationExtensionAuthenticatorOutput> wa4jAuthData = wa4jAttestationObject\n\t\t\t.getAuthenticatorData();\n\t\tAttestedCredentialData wa4jCredData = wa4jAuthData.getAttestedCredentialData();\n\t\tAssert.notNull(wa4jCredData, \"attestedCredentialData cannot be null\");\n\n\t\tSet<Origin> origins = toOrigins();\n\t\tChallenge challenge = new DefaultChallenge(requestOptions.getChallenge().getBytes());\n\t\tString rpId = requestOptions.getRpId();\n\t\tAssert.notNull(rpId, \"rpId cannot be null\");\n\t\tServerProperty serverProperty = new ServerProperty(origins, rpId, challenge);\n\t\tboolean userVerificationRequired = request.getRequestOptions()\n\t\t\t.getUserVerification() == UserVerificationRequirement.REQUIRED;\n\n\t\tcom.webauthn4j.data.AuthenticationRequest authenticationRequest = new com.webauthn4j.data.AuthenticationRequest(\n\t\t\t\trequest.getPublicKey().getRawId().getBytes(), assertionResponse.getAuthenticatorData().getBytes(),\n\t\t\t\tassertionResponse.getClientDataJSON().getBytes(), assertionResponse.getSignature().getBytes());\n\n\t\t// CollectedClientData and ExtensionsClientOutputs is registration data, and can\n\t\t// be null at authentication time.\n\t\tcom.webauthn4j.credential.CredentialRecord wa4jCredentialRecord = new CredentialRecordImpl(\n\t\t\t\twa4jAttestationObject, null, null, convertTransportsToWebauthn4j(credentialRecord.getTransports()));\n\t\tList<byte[]> allowCredentials = convertAllowedCredentialsToWebauthn4j(\n\t\t\t\trequest.getRequestOptions().getAllowCredentials());\n\t\tAuthenticationParameters authenticationParameters = new AuthenticationParameters(serverProperty,\n\t\t\t\twa4jCredentialRecord, allowCredentials.isEmpty() ? null : allowCredentials, userVerificationRequired);\n\n\t\tAuthenticationData wa4jAuthenticationData = this.webAuthnManager.verify(authenticationRequest,\n\t\t\t\tauthenticationParameters);\n\n\t\tAuthenticatorData<AuthenticationExtensionAuthenticatorOutput> wa4jValidatedAuthData = wa4jAuthenticationData\n\t\t\t.getAuthenticatorData();\n\t\tAssert.notNull(wa4jValidatedAuthData, \"authenticatorData cannot be null\");\n\t\tlong updatedSignCount = wa4jValidatedAuthData.getSignCount();\n\t\tImmutableCredentialRecord updatedRecord = ImmutableCredentialRecord.fromCredentialRecord(credentialRecord)\n\t\t\t.lastUsed(Instant.now())\n\t\t\t.signatureCount(updatedSignCount)\n\t\t\t.build();\n\t\tthis.userCredentials.save(updatedRecord);\n\n\t\tPublicKeyCredentialUserEntity userEntity = this.userEntities.findById(credentialRecord.getUserEntityUserId());\n\t\tif (userEntity == null) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Unable to find UserEntity with id \" + credentialRecord.getUserEntityUserId() + \" for \" + request);\n\t\t}\n\t\treturn userEntity;\n\t}\n\n\tprivate static Set<com.webauthn4j.data.AuthenticatorTransport> convertTransportsToWebauthn4j(\n\t\t\tSet<AuthenticatorTransport> transports) {\n\t\treturn transports.stream()\n\t\t\t.map(AuthenticatorTransport::getValue)\n\t\t\t.map(com.webauthn4j.data.AuthenticatorTransport::create)\n\t\t\t.collect(Collectors.toSet());\n\t}\n\n\tprivate static List<byte[]> convertAllowedCredentialsToWebauthn4j(\n\t\t\tList<PublicKeyCredentialDescriptor> allowedCredentials) {\n\t\treturn allowedCredentials.stream()\n\t\t\t.map(PublicKeyCredentialDescriptor::getId)\n\t\t\t.filter(Objects::nonNull)\n\t\t\t.map(Bytes::getBytes)\n\t\t\t.collect(Collectors.toList());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/management/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Management of the WebAuthn APIs.\n */\n@NullMarked\npackage org.springframework.security.web.webauthn.management;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/registration/DefaultWebAuthnRegistrationPageGeneratingFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@link jakarta.servlet.Filter} that renders a default WebAuthn registration page.\n *\n * @author Rob Winch\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n */\npublic class DefaultWebAuthnRegistrationPageGeneratingFilter extends OncePerRequestFilter {\n\n\tprivate RequestMatcher matcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.GET, \"/webauthn/register\");\n\n\tprivate final PublicKeyCredentialUserEntityRepository userEntities;\n\n\tprivate final UserCredentialRepository userCredentials;\n\n\t/**\n\t * Creates a new instance.\n\t * @param userEntities the {@link PublicKeyCredentialUserEntity}\n\t * @param userCredentials\n\t */\n\tpublic DefaultWebAuthnRegistrationPageGeneratingFilter(PublicKeyCredentialUserEntityRepository userEntities,\n\t\t\tUserCredentialRepository userCredentials) {\n\t\tAssert.notNull(userEntities, \"userEntities cannot be null\");\n\t\tAssert.notNull(userCredentials, \"userCredentials cannot be null\");\n\t\tthis.userEntities = userEntities;\n\t\tthis.userCredentials = userCredentials;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!this.matcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tCsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());\n\t\tresponse.setContentType(MediaType.TEXT_HTML_VALUE);\n\t\tresponse.setStatus(HttpServletResponse.SC_OK);\n\t\tString processedTemplate = HtmlTemplates.fromTemplate(HTML_TEMPLATE)\n\t\t\t.withValue(\"contextPath\", request.getContextPath())\n\t\t\t.withRawHtml(\"csrfHeaders\", renderCsrfHeader(csrfToken))\n\t\t\t.withRawHtml(\"passkeys\", passkeyRows(request.getRemoteUser(), request.getContextPath(), csrfToken))\n\t\t\t.render();\n\n\t\tresponse.getWriter().write(processedTemplate);\n\t}\n\n\tprivate String passkeyRows(String username, String contextPath, CsrfToken csrfToken) {\n\t\tPublicKeyCredentialUserEntity userEntity = this.userEntities.findByUsername(username);\n\t\tList<CredentialRecord> credentials = (userEntity != null)\n\t\t\t\t? this.userCredentials.findByUserId(userEntity.getId()) : Collections.emptyList();\n\t\tif (credentials.isEmpty()) {\n\t\t\treturn \"\"\"\n\t\t\t\t\t\t\t\t\t\t<tr><td colspan=\"5\">No Passkeys</td></tr>\n\t\t\t\t\t\"\"\";\n\t\t}\n\t\treturn credentials.stream()\n\t\t\t.map((credentialRecord) -> renderPasskeyRow(credentialRecord, contextPath, csrfToken))\n\t\t\t.collect(Collectors.joining(\"\\n\"));\n\t}\n\n\tprivate String renderPasskeyRow(CredentialRecord credential, String contextPath, CsrfToken csrfToken) {\n\t\treturn HtmlTemplates.fromTemplate(PASSKEY_ROW_TEMPLATE)\n\t\t\t.withValue(\"label\", credential.getLabel())\n\t\t\t.withValue(\"created\", formatInstant(credential.getCreated()))\n\t\t\t.withValue(\"lastUsed\", formatInstant(credential.getLastUsed()))\n\t\t\t.withValue(\"signatureCount\", credential.getSignatureCount())\n\t\t\t.withValue(\"credentialId\", credential.getCredentialId().toBase64UrlString())\n\t\t\t.withValue(\"csrfParameterName\", csrfToken.getParameterName())\n\t\t\t.withValue(\"csrfToken\", csrfToken.getToken())\n\t\t\t.withValue(\"contextPath\", contextPath)\n\t\t\t.render();\n\t}\n\n\tprivate static String formatInstant(Instant created) {\n\t\treturn ZonedDateTime.ofInstant(created, ZoneId.of(\"UTC\"))\n\t\t\t.truncatedTo(ChronoUnit.SECONDS)\n\t\t\t.format(DateTimeFormatter.ISO_INSTANT);\n\t}\n\n\tprivate String renderCsrfHeader(CsrfToken csrfToken) {\n\t\treturn HtmlTemplates.fromTemplate(CSRF_HEADERS)\n\t\t\t.withValue(\"headerName\", csrfToken.getHeaderName())\n\t\t\t.withValue(\"headerValue\", csrfToken.getToken())\n\t\t\t.render();\n\t}\n\n\tprivate static final String HTML_TEMPLATE = \"\"\"\n\t\t\t<html>\n\t\t\t\t<head>\n\t\t\t\t\t<meta charset=\"utf-8\">\n\t\t\t\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t\t<meta name=\"description\" content=\"\">\n\t\t\t\t\t<meta name=\"author\" content=\"\">\n\t\t\t\t\t<title>WebAuthn Registration</title>\n\t\t\t\t\t<link href=\"{{contextPath}}/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t<script type=\"text/javascript\" src=\"{{contextPath}}/login/webauthn.js\"></script>\n\t\t\t\t\t<script type=\"text/javascript\">\n\t\t\t\t\t<!--\n\t\t\t\t\t\tconst ui = {\n\t\t\t\t\t\t\tgetRegisterButton: function() {\n\t\t\t\t\t\t\t\treturn document.getElementById('register')\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tgetSuccess: function() {\n\t\t\t\t\t\t\t\treturn document.getElementById('success')\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tgetError: function() {\n\t\t\t\t\t\t\t\treturn document.getElementById('error')\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tgetLabelInput: function() {\n\t\t\t\t\t\t\t\treturn document.getElementById('label')\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tgetDeleteForms: function() {\n\t\t\t\t\t\t\t\treturn Array.from(document.getElementsByClassName(\"delete-form\"))\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdocument.addEventListener(\"DOMContentLoaded\",() => setupRegistration({{csrfHeaders}}, \"{{contextPath}}\", ui));\n\t\t\t\t\t//-->\n\t\t\t\t\t</script>\n\t\t\t\t</head>\n\t\t\t\t<body>\n\t\t\t\t\t<div class=\"content\">\n\t\t\t\t\t\t<h2 class=\"center\">WebAuthn Registration</h2>\n\t\t\t\t\t\t<form class=\"default-form\" method=\"post\" action=\"#\" onclick=\"return false\">\n\t\t\t\t\t\t\t<div id=\"success\" class=\"alert alert-success\" role=\"alert\">Success!</div>\n\t\t\t\t\t\t\t<div id=\"error\" class=\"alert alert-danger\" role=\"alert\"></div>\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t<label for=\"label\" class=\"screenreader\">Passkey Label</label>\n\t\t\t\t\t\t\t\t<input type=\"text\" id=\"label\" name=\"label\" placeholder=\"Passkey Label\" required autofocus>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<button id=\"register\" class=\"primary\" type=\"submit\">Register</button>\n\t\t\t\t\t\t</form>\n\t\t\t\t\t\t<table class=\"table table-striped\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr class=\"table-header\">\n\t\t\t\t\t\t\t\t\t<th>Label</th>\n\t\t\t\t\t\t\t\t\t<th>Created</th>\n\t\t\t\t\t\t\t\t\t<th>Last Used</th>\n\t\t\t\t\t\t\t\t\t<th>Signature Count</th>\n\t\t\t\t\t\t\t\t\t<th>Delete</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t{{passkeys}}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>\n\t\t\t\t</body>\n\t\t\t</html>\n\t\t\t\"\"\";\n\n\tprivate static final String PASSKEY_ROW_TEMPLATE = \"\"\"\n\t\t\t\t\t\t\t\t<tr class=\"v-middle\">\n\t\t\t\t\t\t\t\t\t<td>{{label}}</td>\n\t\t\t\t\t\t\t\t\t<td>{{created}}</td>\n\t\t\t\t\t\t\t\t\t<td>{{lastUsed}}</td>\n\t\t\t\t\t\t\t\t\t<td class=\"center\">{{signatureCount}}</td>\n\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t<form class=\"delete-form no-margin\" method=\"post\" action=\"{{contextPath}}/webauthn/register/{{credentialId}}\">\n\t\t\t\t\t\t\t\t\t\t\t<input type=\"hidden\" name=\"method\" value=\"delete\">\n\t\t\t\t\t\t\t\t\t\t\t<input type=\"hidden\" name=\"{{csrfParameterName}}\" value=\"{{csrfToken}}\">\n\t\t\t\t\t\t\t\t\t\t\t<button class=\"primary small\" type=\"submit\">Delete</button>\n\t\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\"\"\";\n\n\tprivate static final String CSRF_HEADERS = \"\"\"\n\t\t\t{\"{{headerName}}\" : \"{{headerValue}}\"}\"\"\";\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/registration/HtmlTemplates.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.util.HtmlUtils;\n\n/**\n * Render HTML templates using string substitution. Intended for internal use. Variables\n * can be templated using double curly-braces: {@code {{name}}}.\n *\n * @author Daniel Garnier-Moiroux\n * @since 6.4\n * @see org.springframework.security.web.authentication.ui.HtmlTemplates\n */\nfinal class HtmlTemplates {\n\n\tprivate HtmlTemplates() {\n\t}\n\n\tstatic HtmlTemplates.Builder fromTemplate(String template) {\n\t\treturn new HtmlTemplates.Builder(template);\n\t}\n\n\tstatic final class Builder {\n\n\t\tprivate final String template;\n\n\t\tprivate final Map<String, String> values = new HashMap<>();\n\n\t\tprivate Builder(String template) {\n\t\t\tthis.template = template;\n\t\t}\n\n\t\t/**\n\t\t * HTML-escape, and inject value {@code value} in every {@code {{key}}}\n\t\t * placeholder.\n\t\t * @param key the placeholder name\n\t\t * @param value the value to inject\n\t\t * @return this instance for further templating\n\t\t */\n\t\tHtmlTemplates.Builder withValue(String key, Object value) {\n\t\t\tAssert.notNull(value, \"value cannot be null\");\n\t\t\tthis.values.put(key, HtmlUtils.htmlEscape(value.toString()));\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Inject value {@code value} in every {@code {{key}}} placeholder without\n\t\t * HTML-escaping. Useful for injecting \"sub-templates\".\n\t\t * @param key the placeholder name\n\t\t * @param value the value to inject\n\t\t * @return this instance for further templating\n\t\t */\n\t\tHtmlTemplates.Builder withRawHtml(String key, String value) {\n\t\t\tif (!value.isEmpty() && value.charAt(value.length() - 1) == '\\n') {\n\t\t\t\tvalue = value.substring(0, value.length() - 1);\n\t\t\t}\n\t\t\tthis.values.put(key, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Render the template. All placeholders MUST have a corresponding value. If a\n\t\t * placeholder does not have a corresponding value, throws\n\t\t * {@link IllegalStateException}.\n\t\t * @return the rendered template\n\t\t */\n\t\tString render() {\n\t\t\tString template = this.template;\n\t\t\tfor (String key : this.values.keySet()) {\n\t\t\t\tString pattern = Pattern.quote(\"{{\" + key + \"}}\");\n\t\t\t\ttemplate = template.replaceAll(pattern, this.values.get(key));\n\t\t\t}\n\n\t\t\tString unusedPlaceholders = Pattern.compile(\"\\\\{\\\\{([a-zA-Z0-9]+)}}\")\n\t\t\t\t.matcher(template)\n\t\t\t\t.results()\n\t\t\t\t.map((result) -> result.group(1))\n\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\t\tif (StringUtils.hasLength(unusedPlaceholders)) {\n\t\t\t\tthrow new IllegalStateException(\"Unused placeholders in template: [%s]\".formatted(unusedPlaceholders));\n\t\t\t}\n\n\t\t\treturn template;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/registration/HttpSessionPublicKeyCredentialCreationOptionsRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport jakarta.servlet.http.HttpSession;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.util.Assert;\n\npublic class HttpSessionPublicKeyCredentialCreationOptionsRepository\n\t\timplements PublicKeyCredentialCreationOptionsRepository {\n\n\tstatic final String DEFAULT_ATTR_NAME = PublicKeyCredentialCreationOptions.class.getName().concat(\"ATTR_NAME\");\n\n\tprivate String attrName = DEFAULT_ATTR_NAME;\n\n\t@Override\n\tpublic void save(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable PublicKeyCredentialCreationOptions options) {\n\t\trequest.getSession().setAttribute(this.attrName, options);\n\t}\n\n\tpublic @Nullable PublicKeyCredentialCreationOptions load(HttpServletRequest request) {\n\t\tHttpSession session = request.getSession(false);\n\t\tif (session == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn (PublicKeyCredentialCreationOptions) session.getAttribute(this.attrName);\n\t}\n\n\tpublic void setAttrName(String attrName) {\n\t\tAssert.notNull(attrName, \"attrName cannot be null\");\n\t\tthis.attrName = attrName;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/registration/PublicKeyCredentialCreationOptionsFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport java.io.IOException;\nimport java.util.function.Supplier;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.authorization.AuthenticatedAuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationManager;\nimport org.springframework.security.authorization.AuthorizationResult;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContext;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule;\nimport org.springframework.security.web.webauthn.management.ImmutablePublicKeyCredentialCreationOptionsRequest;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * A {@link jakarta.servlet.Filter} that renders the\n * {@link PublicKeyCredentialCreationOptions} for <a href=\n * \"https://w3c.github.io/webappsec-credential-management/#dom-credentialscontainer-create\">creating</a>\n * a new credential.\n *\n * @author DingHao\n */\npublic class PublicKeyCredentialCreationOptionsFilter extends OncePerRequestFilter {\n\n\tprivate PublicKeyCredentialCreationOptionsRepository repository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();\n\n\tprivate SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder\n\t\t.getContextHolderStrategy();\n\n\tprivate RequestMatcher matcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.POST, \"/webauthn/register/options\");\n\n\tprivate AuthorizationManager<HttpServletRequest> authorization = AuthenticatedAuthorizationManager.authenticated();\n\n\tprivate final WebAuthnRelyingPartyOperations rpOperations;\n\n\tprivate HttpMessageConverter<Object> converter = new JacksonJsonHttpMessageConverter(\n\t\t\tJsonMapper.builder().addModule(new WebauthnJacksonModule()).build());\n\n\t/**\n\t * Creates a new instance.\n\t * @param rpOperations the {@link WebAuthnRelyingPartyOperations} to use. Cannot be\n\t * null.\n\t */\n\tpublic PublicKeyCredentialCreationOptionsFilter(WebAuthnRelyingPartyOperations rpOperations) {\n\t\tAssert.notNull(rpOperations, \"rpOperations cannot be null\");\n\t\tthis.rpOperations = rpOperations;\n\t}\n\n\t/**\n\t * Sets the {@link RequestMatcher} used to trigger this filter.\n\t * <p>\n\t * By default, the {@link RequestMatcher} is {@code POST /webauthn/register/options}.\n\t * @param requestMatcher the {@link RequestMatcher} to use\n\t * @since 6.5\n\t */\n\tpublic void setRequestMatcher(RequestMatcher requestMatcher) {\n\t\tAssert.notNull(requestMatcher, \"requestMatcher cannot be null\");\n\t\tthis.matcher = requestMatcher;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (!this.matcher.matches(request)) {\n\t\t\tfilterChain.doFilter(request, response);\n\t\t\treturn;\n\t\t}\n\n\t\tSupplier<SecurityContext> context = this.securityContextHolderStrategy.getDeferredContext();\n\t\tSupplier<Authentication> authentication = () -> context.get().getAuthentication();\n\t\tAuthorizationResult result = this.authorization.authorize(authentication, request);\n\t\tif (result == null || !result.isGranted()) {\n\t\t\tresponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n\t\t\treturn;\n\t\t}\n\t\tPublicKeyCredentialCreationOptions options = this.rpOperations.createPublicKeyCredentialCreationOptions(\n\t\t\t\tnew ImmutablePublicKeyCredentialCreationOptionsRequest(authentication.get()));\n\t\tthis.repository.save(request, response, options);\n\t\tresponse.setStatus(HttpServletResponse.SC_OK);\n\t\tresponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n\t\tthis.converter.write(options, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));\n\t}\n\n\t/**\n\t * Sets the {@link PublicKeyCredentialCreationOptionsRepository} to use. The default\n\t * is {@link HttpSessionPublicKeyCredentialCreationOptionsRepository}.\n\t * @param creationOptionsRepository the\n\t * {@link PublicKeyCredentialCreationOptionsRepository} to use. Cannot be null.\n\t */\n\tpublic void setCreationOptionsRepository(PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {\n\t\tAssert.notNull(creationOptionsRepository, \"creationOptionsRepository cannot be null\");\n\t\tthis.repository = creationOptionsRepository;\n\t}\n\n\t/**\n\t * Set the {@link HttpMessageConverter} to read the\n\t * {@link WebAuthnRegistrationFilter.WebAuthnRegistrationRequest} and write the\n\t * response. The default is {@link JacksonJsonHttpMessageConverter}.\n\t * @param converter the {@link HttpMessageConverter} to use. Cannot be null.\n\t */\n\tpublic void setConverter(HttpMessageConverter<Object> converter) {\n\t\tAssert.notNull(converter, \"converter cannot be null\");\n\t\tthis.converter = converter;\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/registration/PublicKeyCredentialCreationOptionsRepository.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.jspecify.annotations.Nullable;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\n\n/**\n * Saves {@link PublicKeyCredentialCreationOptions} between a request to generate an\n * assertion and the validation of the assertion.\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic interface PublicKeyCredentialCreationOptionsRepository {\n\n\t/**\n\t * Saves the provided {@link PublicKeyCredentialCreationOptions} or clears an existing\n\t * {@link PublicKeyCredentialCreationOptions} if {@code options} is null.\n\t * @param request the {@link HttpServletRequest}\n\t * @param response the {@link HttpServletResponse}\n\t * @param options the {@link PublicKeyCredentialCreationOptions} to save or null if an\n\t * existing {@link PublicKeyCredentialCreationOptions} should be removed.\n\t */\n\tvoid save(HttpServletRequest request, HttpServletResponse response,\n\t\t\t@Nullable PublicKeyCredentialCreationOptions options);\n\n\t/**\n\t * Gets a saved {@link PublicKeyCredentialCreationOptions} if it exists, otherwise\n\t * null.\n\t * @param request the {@link HttpServletRequest}\n\t * @return the {@link PublicKeyCredentialCreationOptions} that was saved, otherwise\n\t * null.\n\t */\n\t@Nullable PublicKeyCredentialCreationOptions load(HttpServletRequest request);\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilter.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.ServletException;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\nimport org.jspecify.annotations.Nullable;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule;\nimport org.springframework.security.web.webauthn.management.ImmutableRelyingPartyRegistrationRequest;\nimport org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.util.Assert;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\n/**\n * Authenticates {@code PublicKeyCredential<AuthenticatorAssertionResponse>} that is\n * parsed from the body of the {@link HttpServletRequest} using the\n * {@link #setConverter(HttpMessageConverter)}. An example request is provided below:\n *\n * <pre>\n * {\n * \t\"publicKey\": {\n * \t\t\"credential\": {\n * \t\t\t\"id\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n * \t\t\t\"rawId\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n * \t\t\t\"response\": {\n * \t\t\t\t\"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAALraVWanqkAfvZZFYZpVEg0AEHWBexBp0RRSF5KV4vV1NsKlAQIDJiABIVggQjmrekPGzyqtoKK9HPUH-8Z2FLpoqkklFpFPQVICQ3IiWCD6I9Jvmor685fOZOyGXqUd87tXfvJk8rxj9OhuZvUALA\",\n * \t\t\t\t\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSl9RTi10SFJYRWVKYjlNcUNrWmFPLUdOVmlibXpGVGVWMk43Z0ptQUdrQSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n * \t\t\t\t\"transports\": [\n * \t\t\t\t\t\"internal\",\n * \t\t\t\t\t\"hybrid\"\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t\"type\": \"public-key\",\n * \t\t\t\"clientExtensionResults\": {},\n * \t\t\t\"authenticatorAttachment\": \"platform\"\n * \t\t},\n * \t\t\"label\": \"1password\"\n * }\n * </pre>\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic class WebAuthnRegistrationFilter extends OncePerRequestFilter {\n\n\tstatic final String DEFAULT_REGISTER_CREDENTIAL_URL = \"/webauthn/register\";\n\n\tprivate static final Log logger = LogFactory.getLog(WebAuthnRegistrationFilter.class);\n\n\tprivate final WebAuthnRelyingPartyOperations rpOptions;\n\n\tprivate final UserCredentialRepository userCredentials;\n\n\tprivate HttpMessageConverter<Object> converter = new JacksonJsonHttpMessageConverter(\n\t\t\tJsonMapper.builder().addModule(new WebauthnJacksonModule()).build());\n\n\tprivate PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();\n\n\tprivate RequestMatcher registerCredentialMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.POST, DEFAULT_REGISTER_CREDENTIAL_URL);\n\n\tprivate RequestMatcher removeCredentialMatcher = PathPatternRequestMatcher.withDefaults()\n\t\t.matcher(HttpMethod.DELETE, \"/webauthn/register/{id}\");\n\n\tpublic WebAuthnRegistrationFilter(UserCredentialRepository userCredentials,\n\t\t\tWebAuthnRelyingPartyOperations rpOptions) {\n\t\tAssert.notNull(userCredentials, \"userCredentials must not be null\");\n\t\tAssert.notNull(rpOptions, \"rpOptions must not be null\");\n\t\tthis.userCredentials = userCredentials;\n\t\tthis.rpOptions = rpOptions;\n\t}\n\n\t/**\n\t * Sets the {@link RequestMatcher} to trigger this filter's the credential\n\t * registration operation .\n\t * <p/>\n\t * By default, the {@link RequestMatcher} is {@code POST /webauthn/register}.\n\t * @param registerCredentialMatcher the {@link RequestMatcher} to use\n\t * @since 6.5\n\t */\n\tpublic void setRegisterCredentialMatcher(RequestMatcher registerCredentialMatcher) {\n\t\tAssert.notNull(registerCredentialMatcher, \"registerCredentialMatcher cannot be null\");\n\t\tthis.registerCredentialMatcher = registerCredentialMatcher;\n\t}\n\n\t/**\n\t * Sets the {@link RequestMatcher} to trigger this filter's the credential removal\n\t * operation .\n\t * <p/>\n\t * By default, the {@link RequestMatcher} is {@code DELETE /webauthn/register/{id}}.\n\t * @param removeCredentialMatcher the {@link RequestMatcher} to use\n\t * @since 6.5\n\t */\n\tpublic void setRemoveCredentialMatcher(RequestMatcher removeCredentialMatcher) {\n\t\tAssert.notNull(removeCredentialMatcher, \"removeCredentialMatcher cannot be null\");\n\t\tthis.removeCredentialMatcher = removeCredentialMatcher;\n\t}\n\n\t@Override\n\tprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n\t\t\tthrows ServletException, IOException {\n\t\tif (this.registerCredentialMatcher.matches(request)) {\n\t\t\tregisterCredential(request, response);\n\t\t\treturn;\n\t\t}\n\t\tRequestMatcher.MatchResult removeMatchResult = this.removeCredentialMatcher.matcher(request);\n\t\tif (removeMatchResult.isMatch()) {\n\t\t\tString id = removeMatchResult.getVariables().get(\"id\");\n\t\t\tremoveCredential(request, response, id);\n\t\t\treturn;\n\t\t}\n\t\tfilterChain.doFilter(request, response);\n\t}\n\n\t/**\n\t * Set the {@link HttpMessageConverter} to read the\n\t * {@link WebAuthnRegistrationRequest} and write the response. The default is\n\t * {@link JacksonJsonHttpMessageConverter}.\n\t * @param converter the {@link HttpMessageConverter} to use. Cannot be null.\n\t */\n\tpublic void setConverter(HttpMessageConverter<Object> converter) {\n\t\tAssert.notNull(converter, \"converter cannot be null\");\n\t\tthis.converter = converter;\n\t}\n\n\t/**\n\t * Sets the {@link PublicKeyCredentialCreationOptionsRepository} to use. The default\n\t * is {@link HttpSessionPublicKeyCredentialCreationOptionsRepository}.\n\t * @param creationOptionsRepository the\n\t * {@link PublicKeyCredentialCreationOptionsRepository} to use. Cannot be null.\n\t */\n\tpublic void setCreationOptionsRepository(PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {\n\t\tAssert.notNull(creationOptionsRepository, \"creationOptionsRepository cannot be null\");\n\t\tthis.creationOptionsRepository = creationOptionsRepository;\n\t}\n\n\tprivate void registerCredential(HttpServletRequest request, HttpServletResponse response) throws IOException {\n\t\tWebAuthnRegistrationRequest registrationRequest = readRegistrationRequest(request);\n\t\tif (registrationRequest == null) {\n\t\t\tresponse.setStatus(HttpStatus.BAD_REQUEST.value());\n\t\t\treturn;\n\t\t}\n\t\tPublicKeyCredentialCreationOptions options = this.creationOptionsRepository.load(request);\n\t\tif (options == null) {\n\t\t\tresponse.setStatus(HttpStatus.BAD_REQUEST.value());\n\t\t\treturn;\n\t\t}\n\t\tthis.creationOptionsRepository.save(request, response, null);\n\t\tCredentialRecord credentialRecord = this.rpOptions.registerCredential(\n\t\t\t\tnew ImmutableRelyingPartyRegistrationRequest(options, registrationRequest.getPublicKey()));\n\t\tSuccessfulUserRegistrationResponse registrationResponse = new SuccessfulUserRegistrationResponse(\n\t\t\t\tcredentialRecord);\n\t\tServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);\n\t\tthis.converter.write(registrationResponse, MediaType.APPLICATION_JSON, outputMessage);\n\t}\n\n\tprivate @Nullable WebAuthnRegistrationRequest readRegistrationRequest(HttpServletRequest request) {\n\t\tHttpInputMessage inputMessage = new ServletServerHttpRequest(request);\n\t\ttry {\n\t\t\treturn (WebAuthnRegistrationRequest) this.converter.read(WebAuthnRegistrationRequest.class, inputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.debug(\"Unable to parse WebAuthnRegistrationRequest\", ex);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate void removeCredential(HttpServletRequest request, HttpServletResponse response, @Nullable String id)\n\t\t\tthrows IOException {\n\t\tthis.userCredentials.delete(Bytes.fromBase64(id));\n\t\tresponse.setStatus(HttpStatus.NO_CONTENT.value());\n\t}\n\n\tstatic class WebAuthnRegistrationRequest {\n\n\t\tprivate @Nullable RelyingPartyPublicKey publicKey;\n\n\t\t@Nullable RelyingPartyPublicKey getPublicKey() {\n\t\t\treturn this.publicKey;\n\t\t}\n\n\t\tvoid setPublicKey(RelyingPartyPublicKey publicKey) {\n\t\t\tthis.publicKey = publicKey;\n\t\t}\n\n\t}\n\n\tpublic static class SuccessfulUserRegistrationResponse {\n\n\t\tprivate final CredentialRecord credentialRecord;\n\n\t\tSuccessfulUserRegistrationResponse(CredentialRecord credentialRecord) {\n\t\t\tthis.credentialRecord = credentialRecord;\n\t\t}\n\n\t\tpublic boolean isSuccess() {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/main/java/org/springframework/security/web/webauthn/registration/package-info.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WebAuthn Registration support.\n */\n@NullMarked\npackage org.springframework.security.web.webauthn.registration;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "webauthn/src/main/resources/META-INF/spring/aot.factories",
    "content": "org.springframework.aot.hint.RuntimeHintsRegistrar=\\\norg.springframework.security.web.webauthn.aot.UserCredentialRuntimeHints,\\\norg.springframework.security.web.webauthn.aot.PublicKeyCredentialUserEntityRuntimeHints\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/aot/PublicKeyCredentialUserEntityRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.aot;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link PublicKeyCredentialUserEntityRuntimeHints}\n *\n * @author Max Batischev\n */\npublic class PublicKeyCredentialUserEntityRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getUserEntitiesSqlFiles\")\n\tvoid userEntitiesSqlFilesHasHints(String schemaFile) {\n\t\tassertThat(RuntimeHintsPredicates.resource().forResource(schemaFile)).accepts(this.hints);\n\t}\n\n\tprivate static Stream<String> getUserEntitiesSqlFiles() {\n\t\treturn Stream.of(\"org/springframework/security/user-entities-schema.sql\");\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/aot/UserCredentialRuntimeHintsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.aot;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport org.springframework.aot.hint.RuntimeHints;\nimport org.springframework.aot.hint.RuntimeHintsRegistrar;\nimport org.springframework.aot.hint.predicate.RuntimeHintsPredicates;\nimport org.springframework.core.io.support.SpringFactoriesLoader;\nimport org.springframework.util.ClassUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link UserCredentialRuntimeHints}\n *\n * @author Max Batischev\n */\npublic class UserCredentialRuntimeHintsTests {\n\n\tprivate final RuntimeHints hints = new RuntimeHints();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tSpringFactoriesLoader.forResourceLocation(\"META-INF/spring/aot.factories\")\n\t\t\t.load(RuntimeHintsRegistrar.class)\n\t\t\t.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"getClientRecordsSqlFiles\")\n\tvoid credentialRecordsSqlFilesHasHints(String schemaFile) {\n\t\tassertThat(RuntimeHintsPredicates.resource().forResource(schemaFile)).accepts(this.hints);\n\t}\n\n\tprivate static Stream<String> getClientRecordsSqlFiles() {\n\t\treturn Stream.of(\"org/springframework/security/user-credentials-schema.sql\",\n\t\t\t\t\"org/springframework/security/user-credentials-schema-postgres.sql\");\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/COSEAlgorithmIdentifierTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass COSEAlgorithmIdentifierTests {\n\n\t@Test\n\tvoid valuesContainsAll() {\n\t\tList<COSEAlgorithmIdentifier> allMembers = Arrays.stream(COSEAlgorithmIdentifier.class.getFields())\n\t\t\t.filter((f) -> f.getType().isAssignableFrom(COSEAlgorithmIdentifier.class))\n\t\t\t.map((f) -> (COSEAlgorithmIdentifier) getValue(f))\n\t\t\t.collect(Collectors.toUnmodifiableList());\n\t\tassertThat(COSEAlgorithmIdentifier.values()).containsExactlyInAnyOrderElementsOf(allMembers);\n\t}\n\n\tprivate <T> T getValue(Field f) {\n\t\ttry {\n\t\t\treturn (T) f.get(null);\n\t\t}\n\t\tcatch (IllegalAccessException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/TestAuthenticationAssertionResponses.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\n/**\n * @author Max Batischev\n */\npublic final class TestAuthenticationAssertionResponses {\n\n\tpublic static AuthenticatorAssertionResponse.AuthenticatorAssertionResponseBuilder createAuthenticatorAssertionResponse() {\n\t\treturn AuthenticatorAssertionResponse.builder()\n\t\t\t.authenticatorData(Bytes.fromBase64(\"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA\"))\n\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaDB2Z3dHUWpvQ3pBekRVc216UHBrLUpWSUpSUmduMEw0S1ZTWU5SY0VaYyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t.signature(Bytes.fromBase64(\n\t\t\t\t\t\"MEUCIAdfzPAn3voyXynwa0IXk1S0envMY5KP3NEe9aj4B2BuAiEAm_KJhQoWXdvfhbzwACU3NM4ltQe7_Il46qFUwtpuTdg\"))\n\t\t\t.userHandle(Bytes.fromBase64(\"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\"));\n\t}\n\n\tprivate TestAuthenticationAssertionResponses() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/TestAuthenticatorAttestationResponses.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\npublic final class TestAuthenticatorAttestationResponses {\n\n\tpublic static AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder createAuthenticatorAttestationResponse() {\n\t\treturn AuthenticatorAttestationResponse.builder()\n\t\t\t.attestationObject(Bytes.fromBase64(\n\t\t\t\t\t\"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAALraVWanqkAfvZZFYZpVEg0AEDWRLOHq0Wxw4cOkCemynKqlAQIDJiABIVgg4Hkrn2kbGmpZTdoDZUNrppo93OqgQV7ONzVvo5GLCFciWCCrf6yIQggq2BfZntawxRsBBbWG_FWkYAoU8yPipS-5hg==\"))\n\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoicTdsQ2RkM1NWUXhkQy12OHBuUkFHRW4xQjJNLXQ3WkVDV1B3Q0FtaFd2YyIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t.transports(AuthenticatorTransport.HYBRID, AuthenticatorTransport.INTERNAL);\n\t}\n\n\tprivate TestAuthenticatorAttestationResponses() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/TestBytes.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\n/**\n * @author Rob Winch\n */\npublic final class TestBytes {\n\n\tpublic static Bytes get() {\n\t\treturn Bytes.fromBase64(\"OSCtNugR-n4YR4ozlHRa-CKXzY9v-yMKtQGcvui5xN8\");\n\t}\n\n\tprivate TestBytes() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/TestCredentialRecords.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.time.Instant;\nimport java.util.Set;\n\npublic final class TestCredentialRecords {\n\n\tpublic static ImmutableCredentialRecord.ImmutableCredentialRecordBuilder userCredential() {\n\t\treturn ImmutableCredentialRecord.builder()\n\t\t\t.label(\"label\")\n\t\t\t.credentialId(Bytes.fromBase64(\"NauGCN7bZ5jEBwThcde51g\"))\n\t\t\t.userEntityUserId(Bytes.fromBase64(\"vKBFhsWT3gQnn-gHdT4VXIvjDkVXVYg5w8CLGHPunMM\"))\n\t\t\t.publicKey(ImmutablePublicKeyCose.fromBase64(\n\t\t\t\t\t\"pQECAyYgASFYIC7DAiV_trHFPjieOxXbec7q2taBcgLnIi19zrUwVhCdIlggvN6riHORK_velHcTLFK_uJhyKK0oBkJqzNqR2E-2xf8=\"))\n\t\t\t.backupEligible(true)\n\t\t\t.backupState(true);\n\t}\n\n\tpublic static ImmutableCredentialRecord.ImmutableCredentialRecordBuilder fullUserCredential() {\n\t\treturn ImmutableCredentialRecord.builder()\n\t\t\t.label(\"label\")\n\t\t\t.credentialId(Bytes.fromBase64(\"NauGCN7bZ5jEBwThcde51g\"))\n\t\t\t.userEntityUserId(Bytes.fromBase64(\"vKBFhsWT3gQnn-gHdT4VXIvjDkVXVYg5w8CLGHPunMM\"))\n\t\t\t.publicKey(ImmutablePublicKeyCose.fromBase64(\n\t\t\t\t\t\"pQECAyYgASFYIC7DAiV_trHFPjieOxXbec7q2taBcgLnIi19zrUwVhCdIlggvN6riHORK_velHcTLFK_uJhyKK0oBkJqzNqR2E-2xf8=\"))\n\t\t\t.backupEligible(true)\n\t\t\t.created(Instant.now())\n\t\t\t.transports(Set.of(AuthenticatorTransport.BLE, AuthenticatorTransport.HYBRID))\n\t\t\t.signatureCount(100)\n\t\t\t.uvInitialized(false)\n\t\t\t.credentialType(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t.attestationObject(new Bytes(\"test\".getBytes()))\n\t\t\t.attestationClientDataJSON(new Bytes((\"test\").getBytes()))\n\t\t\t.backupState(true);\n\t}\n\n\tprivate TestCredentialRecords() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/TestPublicKeyCredentialCreationOptions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.time.Duration;\n\npublic final class TestPublicKeyCredentialCreationOptions {\n\n\tpublic static PublicKeyCredentialCreationOptions.PublicKeyCredentialCreationOptionsBuilder createPublicKeyCredentialCreationOptions() {\n\n\t\tAuthenticatorSelectionCriteria authenticatorSelection = AuthenticatorSelectionCriteria.builder()\n\t\t\t.userVerification(UserVerificationRequirement.PREFERRED)\n\t\t\t.residentKey(ResidentKeyRequirement.REQUIRED)\n\t\t\t.build();\n\t\tBytes challenge = Bytes.fromBase64(\"q7lCdd3SVQxdC-v8pnRAGEn1B2M-t7ZECWPwCAmhWvc\");\n\t\tPublicKeyCredentialRpEntity rp = PublicKeyCredentialRpEntity.builder()\n\t\t\t.id(\"example.localhost\")\n\t\t\t.name(\"SimpleWebAuthn Example\")\n\t\t\t.build();\n\t\tBytes userId = Bytes.fromBase64(\"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\");\n\t\tPublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t.displayName(\"user@example.localhost\")\n\t\t\t.id(userId)\n\t\t\t.name(\"user@example.localhost\")\n\t\t\t.build();\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tImmutableAuthenticationExtensionsClientInput.credProps);\n\t\treturn PublicKeyCredentialCreationOptions.builder()\n\t\t\t.attestation(AttestationConveyancePreference.NONE)\n\t\t\t.user(userEntity)\n\t\t\t.pubKeyCredParams(PublicKeyCredentialParameters.EdDSA, PublicKeyCredentialParameters.ES256,\n\t\t\t\t\tPublicKeyCredentialParameters.RS256)\n\t\t\t.authenticatorSelection(authenticatorSelection)\n\t\t\t.challenge(challenge)\n\t\t\t.rp(rp)\n\t\t\t.extensions(clientInputs)\n\t\t\t.timeout(Duration.ofMinutes(5));\n\t}\n\n\tprivate TestPublicKeyCredentialCreationOptions() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/TestPublicKeyCredentialRequestOptions.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport java.time.Duration;\n\npublic final class TestPublicKeyCredentialRequestOptions {\n\n\tpublic static PublicKeyCredentialRequestOptions.PublicKeyCredentialRequestOptionsBuilder create() {\n\t\treturn PublicKeyCredentialRequestOptions.builder()\n\t\t\t.timeout(Duration.ofMinutes(5))\n\t\t\t.rpId(\"example.localhost\")\n\t\t\t.userVerification(UserVerificationRequirement.PREFERRED)\n\t\t\t.challenge(Bytes.fromBase64(\"cQfdGrj9zDg3zNBkOH3WPL954FTOShVy0-CoNgSewNM\"));\n\t}\n\n\tprivate TestPublicKeyCredentialRequestOptions() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/TestPublicKeyCredentialUserEntities.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity.PublicKeyCredentialUserEntityBuilder;\n\npublic final class TestPublicKeyCredentialUserEntities {\n\n\tpublic static PublicKeyCredentialUserEntityBuilder userEntity() {\n\t\treturn ImmutablePublicKeyCredentialUserEntity.builder().name(\"user\").id(TestBytes.get()).displayName(\"user\");\n\t}\n\n\tprivate TestPublicKeyCredentialUserEntities() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/api/TestPublicKeyCredentials.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.api;\n\npublic final class TestPublicKeyCredentials {\n\n\tpublic static PublicKeyCredential.PublicKeyCredentialBuilder<AuthenticatorAttestationResponse> createPublicKeyCredential() {\n\t\tAuthenticatorAttestationResponse response = TestAuthenticatorAttestationResponses\n\t\t\t.createAuthenticatorAttestationResponse()\n\t\t\t.build();\n\t\treturn createPublicKeyCredential(response);\n\t}\n\n\tpublic static <R extends AuthenticatorResponse> PublicKeyCredential.PublicKeyCredentialBuilder<R> createPublicKeyCredential(\n\t\t\tR response) {\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\t\treturn PublicKeyCredential.builder()\n\t\t\t.id(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\")\n\t\t\t.rawId(Bytes\n\t\t\t\t.fromBase64(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\"))\n\t\t\t.response(response)\n\t\t\t.type(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t.clientExtensionResults(clientExtensionResults);\n\t}\n\n\tpublic static <R extends AuthenticatorResponse> PublicKeyCredential.PublicKeyCredentialBuilder<R> createPublicKeyCredential(\n\t\t\tR response, AuthenticationExtensionsClientOutputs outputs) {\n\t\treturn PublicKeyCredential.builder()\n\t\t\t.id(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\")\n\t\t\t.rawId(Bytes\n\t\t\t\t.fromBase64(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\"))\n\t\t\t.response(response)\n\t\t\t.type(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t.clientExtensionResults(outputs);\n\t}\n\n\tprivate TestPublicKeyCredentials() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/HttpSessionPublicKeyCredentialRequestOptionsRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link HttpSessionPublicKeyCredentialRequestOptionsRepository}.\n *\n * @author Rob Winch\n * @since 6.4\n */\nclass HttpSessionPublicKeyCredentialRequestOptionsRepositoryTests {\n\n\tprivate HttpSessionPublicKeyCredentialRequestOptionsRepository repository = new HttpSessionPublicKeyCredentialRequestOptionsRepository();\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate MockHttpServletResponse response = new MockHttpServletResponse();\n\n\t@Test\n\tvoid integrationTests() {\n\t\tPublicKeyCredentialRequestOptions expected = TestPublicKeyCredentialRequestOptions.create().build();\n\t\tthis.repository.save(this.request, this.response, expected);\n\t\tObject attrValue = this.request.getSession()\n\t\t\t.getAttribute(HttpSessionPublicKeyCredentialRequestOptionsRepository.DEFAULT_ATTR_NAME);\n\t\tassertThat(attrValue).isEqualTo(expected);\n\t\tPublicKeyCredentialRequestOptions loadOptions = this.repository.load(this.request);\n\t\tassertThat(loadOptions).isEqualTo(expected);\n\n\t\tthis.repository.save(this.request, this.response, null);\n\n\t\tObject attrValueAfterRemoval = this.request.getSession()\n\t\t\t.getAttribute(HttpSessionPublicKeyCredentialRequestOptionsRepository.DEFAULT_ATTR_NAME);\n\t\tassertThat(attrValueAfterRemoval).isNull();\n\t\tassertThat(this.request.getSession().getAttributeNames())\n\t\t\t.doesNotHaveToString(HttpSessionPublicKeyCredentialRequestOptionsRepository.DEFAULT_ATTR_NAME);\n\t\tPublicKeyCredentialRequestOptions loadOptionsAfterRemoval = this.repository.load(this.request);\n\t\tassertThat(loadOptionsAfterRemoval).isNull();\n\t}\n\n\t@Test\n\tvoid loadWhenNullSessionThenDoesNotCreate() {\n\t\tPublicKeyCredentialRequestOptions options = this.repository.load(this.request);\n\t\tassertThat(options).isNull();\n\t\tassertThat(this.request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tvoid saveWhenSetAttrThenCustomAttr() {\n\t\tPublicKeyCredentialRequestOptions expected = TestPublicKeyCredentialRequestOptions.create().build();\n\t\tString customAttr = \"custom-attr\";\n\t\tthis.repository.setAttrName(customAttr);\n\t\tthis.repository.save(this.request, this.response, expected);\n\t\tassertThat(this.request.getSession().getAttribute(customAttr)).isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid loadWhenSetAttrThenCustomAttr() {\n\t\tPublicKeyCredentialRequestOptions expected = TestPublicKeyCredentialRequestOptions.create().build();\n\t\tString customAttr = \"custom-attr\";\n\t\tthis.repository.setAttrName(customAttr);\n\t\tthis.request.getSession().setAttribute(customAttr, expected);\n\t\tPublicKeyCredentialRequestOptions actual = this.repository.load(this.request);\n\t\tassertThat(actual).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/PublicKeyCredentialRequestOptionsFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport java.nio.charset.StandardCharsets;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.invocation.InvocationOnMock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.stubbing.Answer;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextHolderStrategy;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.BDDMockito.verifyNoInteractions;\nimport static org.mockito.BDDMockito.willAnswer;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link PublicKeyCredentialRequestOptionsFilter}.\n *\n * @author Rob Winch\n * @since 6.4\n */\n@ExtendWith(MockitoExtension.class)\nclass PublicKeyCredentialRequestOptionsFilterTests {\n\n\t@Mock\n\tprivate WebAuthnRelyingPartyOperations relyingPartyOperations;\n\n\t@Mock\n\tprivate PublicKeyCredentialRequestOptionsRepository requestOptionsRepository;\n\n\t@Mock\n\tprivate HttpMessageConverter<Object> converter;\n\n\t@Mock\n\tprivate SecurityContextHolderStrategy contextHolderStrategy;\n\n\tprivate PublicKeyCredentialRequestOptionsFilter filter;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\tprivate MockMvc mockMvc;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.filter = new PublicKeyCredentialRequestOptionsFilter(this.relyingPartyOperations);\n\t\tthis.filter.setRequestOptionsRepository(this.requestOptionsRepository);\n\t\tthis.mockMvc = MockMvcBuilders.standaloneSetup().addFilter(this.filter).build();\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@AfterEach\n\tvoid cleanup() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tvoid doFilterWhenCustomRequestMatcherThenUses() throws Exception {\n\t\tRequestMatcher requestMatcher = mock(RequestMatcher.class);\n\t\tthis.filter.setRequestMatcher(requestMatcher);\n\t\tFilterChain mock = mock(FilterChain.class);\n\t\tthis.filter.doFilter(this.request, this.response, mock);\n\t\tverify(requestMatcher).matches(any());\n\t}\n\n\t@Test\n\tvoid constructorWhenNull() {\n\t\tassertThatExceptionOfType(IllegalArgumentException.class)\n\t\t\t.isThrownBy(() -> new PublicKeyCredentialRequestOptionsFilter(null));\n\t}\n\n\t@Test\n\tvoid doFilterWhenNoMatch() throws Exception {\n\t\tthis.mockMvc.perform(post(\"/nomatch\"))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andDo((result) -> assertThat(result.getResponse().getContentAsString()).isEmpty());\n\t\tverifyNoInteractions(this.relyingPartyOperations, this.requestOptionsRepository);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotPost() throws Exception {\n\t\tthis.mockMvc.perform(get(\"/webauthn/authenticate/options\"))\n\t\t\t.andExpect(status().isNotFound())\n\t\t\t.andDo((result) -> assertThat(result.getResponse().getContentAsString()).isEmpty());\n\t\tverifyNoInteractions(this.relyingPartyOperations, this.requestOptionsRepository);\n\t}\n\n\t@Test\n\tvoid doFilterWhenMatches() throws Exception {\n\t\tPublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions.create().build();\n\t\tgiven(this.relyingPartyOperations.createCredentialRequestOptions(any())).willReturn(options);\n\n\t\tPublicKeyCredentialCreationOptions mockResult = this.relyingPartyOperations\n\t\t\t.createPublicKeyCredentialCreationOptions(null);\n\t\tthis.mockMvc.perform(post(\"/webauthn/authenticate/options\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andDo((result) -> JSONAssert.assertEquals(result.getResponse().getContentAsString(), \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"challenge\": \"cQfdGrj9zDg3zNBkOH3WPL954FTOShVy0-CoNgSewNM\",\n\t\t\t\t\t\t\"timeout\": 300000,\n\t\t\t\t\t\t\"rpId\": \"example.localhost\",\n\t\t\t\t\t\t\"allowCredentials\": [],\n\t\t\t\t\t\t\"userVerification\": \"preferred\",\n\t\t\t\t\t\t\"extensions\": {}\n\t\t\t\t\t}\n\t\t\t\t\t\"\"\", false));\n\t}\n\n\t@Test\n\tvoid doFilterWhenCustom() throws Exception {\n\t\tString body = \"custom body\";\n\t\twillAnswer(new Answer<Void>() {\n\t\t\t@Override\n\t\t\tpublic Void answer(InvocationOnMock invocation) throws Throwable {\n\t\t\t\tServletServerHttpResponse response = invocation.getArgument(2);\n\t\t\t\tresponse.getBody().write(body.getBytes(StandardCharsets.UTF_8));\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}).given(this.converter).write(any(), any(), any());\n\t\tgiven(this.contextHolderStrategy.getContext())\n\t\t\t.willReturn(new SecurityContextImpl(new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\")));\n\t\tthis.filter.setConverter(this.converter);\n\t\tthis.filter.setSecurityContextHolderStrategy(this.contextHolderStrategy);\n\t\tPublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions.create().build();\n\t\tgiven(this.relyingPartyOperations.createCredentialRequestOptions(any())).willReturn(options);\n\n\t\tPublicKeyCredentialCreationOptions mockResult = this.relyingPartyOperations\n\t\t\t.createPublicKeyCredentialCreationOptions(null);\n\t\tthis.mockMvc.perform(post(\"/webauthn/authenticate/options\"))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andDo((result) -> assertThat(result.getResponse().getContentAsString()).isEqualTo(body));\n\t}\n\n\t@Test\n\tvoid setConverterWhenNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setConverter(null));\n\t}\n\n\t@Test\n\tvoid setSecurityContextHolderStrategyWhenNull() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setSecurityContextHolderStrategy(null));\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.http.converter.SmartHttpMessageConverter;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;\nimport org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.isNull;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\n\n/**\n * Tests for {@link WebAuthnAuthenticationFilter}.\n *\n * @author Rob Winch\n * @since 6.4\n */\n@ExtendWith(MockitoExtension.class)\nclass WebAuthnAuthenticationFilterTests {\n\n\tprivate static final String VALID_BODY = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n\t\t\t\t\t\"rawId\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n\t\t\t\t\t\"response\": {\n\t\t\t\t\t\t\"authenticatorData\": \"y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA\",\n\t\t\t\t\t\t\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRFVsRzRDbU9naWhKMG1vdXZFcE9HdUk0ZVJ6MGRRWmxUQmFtbjdHQ1FTNCIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\t\t\t\t\t\t\"signature\": \"MEYCIQCW2BcUkRCAXDmGxwMi78jknenZ7_amWrUJEYoTkweldAIhAMD0EMp1rw2GfwhdrsFIeDsL7tfOXVPwOtfqJntjAo4z\",\n\t\t\t\t\t\t\"userHandle\": \"Q3_0Xd64_HW0BlKRAJnVagJTpLKLgARCj8zjugpRnVo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"clientExtensionResults\": {},\n\t\t\t\t\t\"authenticatorAttachment\": \"platform\"\n\t\t\t\t}\n\t\t\t\"\"\";\n\n\t@Mock\n\tprivate SmartHttpMessageConverter<Object> converter;\n\n\t@Mock\n\tprivate PublicKeyCredentialRequestOptionsRepository requestOptionsRepository;\n\n\t@Mock\n\tprivate AuthenticationManager authenticationManager;\n\n\tprivate MockHttpServletResponse response = new MockHttpServletResponse();\n\n\t@Mock\n\tprivate FilterChain chain;\n\n\tprivate WebAuthnAuthenticationFilter filter = new WebAuthnAuthenticationFilter();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.filter.setAuthenticationManager(this.authenticationManager);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid setConverterWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.filter.setConverter((GenericHttpMessageConverter<Object>) null));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.filter.setConverter((SmartHttpMessageConverter<Object>) null));\n\t}\n\n\t@Test\n\tvoid setRequestOptionsRepositoryWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestOptionsRepository(null));\n\t}\n\n\t@Test\n\tvoid doFilterWhenUrlDoesNotMatchThenChainContinues() throws Exception {\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tthis.filter.setConverter(this.converter);\n\t\tthis.filter.setRequestOptionsRepository(this.requestOptionsRepository);\n\t\tthis.filter.doFilter(post(\"/nomatch\").buildRequest(new MockServletContext()), response, this.chain);\n\t\tverifyNoInteractions(this.converter, this.requestOptionsRepository, response);\n\t\tverify(this.chain).doFilter(any(), any());\n\t}\n\n\t@Test\n\tvoid doFilterWhenMethodDoesNotMatchThenChainContinues() throws Exception {\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tthis.filter.setConverter(this.converter);\n\t\tthis.filter.setRequestOptionsRepository(this.requestOptionsRepository);\n\t\tthis.filter.doFilter(get(\"/login/webauthn\").buildRequest(new MockServletContext()), response, this.chain);\n\t\tverifyNoInteractions(this.converter, this.requestOptionsRepository, response);\n\t\tverify(this.chain).doFilter(any(), any());\n\t}\n\n\t@Test\n\tvoid doFilterWhenNoBodyThenUnauthorized() throws Exception {\n\t\tthis.filter.doFilter(matchingRequest(\"\"), this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());\n\t}\n\n\t@Test\n\tvoid doFilterWhenInvalidJsonThenUnauthorized() throws Exception {\n\t\tMockHttpServletRequest request = matchingRequest(\"<>\");\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());\n\t}\n\n\t@Test\n\tvoid doFilterWhenOptionsNullThenUnAuthorized() throws Exception {\n\t\tMockHttpServletRequest request = matchingRequest(VALID_BODY);\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());\n\t}\n\n\t@Test\n\tvoid doFilterWhenValidThenOk() throws Exception {\n\t\tPublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions.create().build();\n\t\tgiven(this.requestOptionsRepository.load(any())).willReturn(options);\n\t\tPublicKeyCredentialUserEntity principal = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tWebAuthnAuthentication authentication = new WebAuthnAuthentication(principal,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\t\tgiven(this.authenticationManager.authenticate(any())).willReturn(authentication);\n\t\tthis.filter.setRequestOptionsRepository(this.requestOptionsRepository);\n\t\tMockHttpServletRequest request = matchingRequest(VALID_BODY);\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tverify(this.requestOptionsRepository).save(any(), any(), isNull());\n\t\tArgumentCaptor<WebAuthnAuthenticationRequestToken> authenticationCaptor = ArgumentCaptor\n\t\t\t.forClass(WebAuthnAuthenticationRequestToken.class);\n\t\tverify(this.authenticationManager).authenticate(authenticationCaptor.capture());\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tWebAuthnAuthenticationRequestToken token = authenticationCaptor.getValue();\n\t\tassertThat(token).isNotNull();\n\t\tRelyingPartyAuthenticationRequest authnRequest = token.getWebAuthnRequest();\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> publicKey = authnRequest.getPublicKey();\n\t\tAuthenticatorAssertionResponse assertionResponse = publicKey.getResponse();\n\t\tassertThat(publicKey.getId()).isEqualTo(\"dYF7EGnRFFIXkpXi9XU2wg\");\n\t\tassertThat(publicKey.getRawId().toBase64UrlString()).isEqualTo(\"dYF7EGnRFFIXkpXi9XU2wg\");\n\t\tassertThat(assertionResponse.getAuthenticatorData().toBase64UrlString())\n\t\t\t.isEqualTo(\"y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA\");\n\t\tassertThat(assertionResponse.getClientDataJSON().toBase64UrlString()).isEqualTo(\n\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRFVsRzRDbU9naWhKMG1vdXZFcE9HdUk0ZVJ6MGRRWmxUQmFtbjdHQ1FTNCIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0\");\n\t\tassertThat(assertionResponse.getSignature().toBase64UrlString()).isEqualTo(\n\t\t\t\t\"MEYCIQCW2BcUkRCAXDmGxwMi78jknenZ7_amWrUJEYoTkweldAIhAMD0EMp1rw2GfwhdrsFIeDsL7tfOXVPwOtfqJntjAo4z\");\n\t\tassertThat(assertionResponse.getUserHandle().toBase64UrlString())\n\t\t\t.isEqualTo(\"Q3_0Xd64_HW0BlKRAJnVagJTpLKLgARCj8zjugpRnVo\");\n\t\tassertThat(publicKey.getClientExtensionResults().getOutputs()).isEmpty();\n\t\tassertThat(authnRequest.getRequestOptions()).isEqualTo(options);\n\t\tassertThat(authnRequest.getPublicKey().getAuthenticatorAttachment())\n\t\t\t.isEqualTo(AuthenticatorAttachment.PLATFORM);\n\t\tString expectedBody = \"\"\"\n\t\t\t\t{\"redirectUrl\":\"/\",\"authenticated\":true}\n\t\t\t\t\"\"\";\n\t\tJSONAssert.assertEquals(expectedBody, this.response.getContentAsString(), false);\n\t}\n\n\tprivate static MockHttpServletRequest matchingRequest(String body) {\n\t\treturn MockMvcRequestBuilders.post(\"/login/webauthn\").content(body).buildRequest(new MockServletContext());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationProviderTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.authentication.SecurityAssertions;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.authority.FactorGrantedAuthority;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.TestAuthenticationAssertionResponses;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentials;\nimport org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\n\nclass WebAuthnAuthenticationProviderTests {\n\n\t@Test\n\tvoid authenticateWhenSuccessThenIssuesFactor() {\n\t\tWebAuthnRelyingPartyOperations operations = mock(WebAuthnRelyingPartyOperations.class);\n\t\tUserDetailsService users = mock(UserDetailsService.class);\n\t\tPublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions.create().build();\n\t\tAuthenticatorAssertionResponse response = TestAuthenticationAssertionResponses\n\t\t\t.createAuthenticatorAssertionResponse()\n\t\t\t.build();\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> credentials = TestPublicKeyCredentials\n\t\t\t.createPublicKeyCredential(response)\n\t\t\t.build();\n\t\tAuthentication request = new WebAuthnAuthenticationRequestToken(\n\t\t\t\tnew RelyingPartyAuthenticationRequest(options, credentials));\n\t\tWebAuthnAuthenticationProvider provider = new WebAuthnAuthenticationProvider(operations, users);\n\t\tgiven(users.loadUserByUsername(any())).willReturn(PasswordEncodedUser.user());\n\t\tgiven(operations.authenticate(any())).willReturn(TestPublicKeyCredentialUserEntities.userEntity().build());\n\t\tAuthentication result = provider.authenticate(request);\n\t\tSecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.WEBAUTHN_AUTHORITY);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.authentication;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\nclass WebAuthnAuthenticationTests {\n\n\t@Test\n\tvoid isAuthenticatedThenTrue() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tWebAuthnAuthentication authentication = new WebAuthnAuthentication(userEntity, authorities);\n\t\tassertThat(authentication.isAuthenticated()).isTrue();\n\t}\n\n\t@Test\n\tvoid setAuthenticationWhenTrueThenException() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tWebAuthnAuthentication authentication = new WebAuthnAuthentication(userEntity, authorities);\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> authentication.setAuthenticated(true));\n\t}\n\n\t@Test\n\tvoid setAuthenticationWhenFalseThenNotAuthenticated() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tList<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(\"ROLE_USER\");\n\t\tWebAuthnAuthentication authentication = new WebAuthnAuthentication(userEntity, authorities);\n\t\tauthentication.setAuthenticated(false);\n\t\tassertThat(authentication.isAuthenticated()).isFalse();\n\t}\n\n\t@Test\n\tvoid toBuilderWhenApplyThenCopies() {\n\t\tPublicKeyCredentialUserEntity alice = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tWebAuthnAuthentication factorOne = new WebAuthnAuthentication(alice,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_ONE\"));\n\t\tPublicKeyCredentialUserEntity bob = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tWebAuthnAuthentication factorTwo = new WebAuthnAuthentication(bob,\n\t\t\t\tAuthorityUtils.createAuthorityList(\"FACTOR_TWO\"));\n\t\tWebAuthnAuthentication result = factorOne.toBuilder()\n\t\t\t.authorities((a) -> a.addAll(factorTwo.getAuthorities()))\n\t\t\t.principal(factorTwo.getPrincipal())\n\t\t\t.build();\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());\n\t\tassertThat(result.getPrincipal()).isSameAs(factorTwo.getPrincipal());\n\t\tassertThat(authorities).containsExactlyInAnyOrder(\"FACTOR_ONE\", \"FACTOR_TWO\");\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJackson2Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;\n\n/**\n * Test Jackson serialization of CredProtectAuthenticationExtensionsClientInput\n *\n * @author Rob Winch\n */\n@SuppressWarnings(\"removal\")\nclass CredProtectAuthenticationExtensionsClientInputJackson2Tests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModule(new WebauthnJackson2Module());\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationOptional() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationOptional\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,\n\t\t\t\ttrue);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationOptionalWithCredentialIdList()\n\t\t\tthrows Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationOptionalWithCredentialIdList\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST,\n\t\t\t\ttrue);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationRequired() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationRequired\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_REQUIRED,\n\t\t\t\ttrue);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenEnforceCredentialProtectionPolicyTrue() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationOptional\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,\n\t\t\t\ttrue);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenEnforceCredentialProtectionPolicyFalse() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationOptional\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": false\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,\n\t\t\t\tfalse);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/CredProtectAuthenticationExtensionsClientInputJacksonTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;\n\n/**\n * Test Jackson serialization of CredProtectAuthenticationExtensionsClientInput\n *\n * @author Rob Winch\n */\nclass CredProtectAuthenticationExtensionsClientInputJacksonTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.mapper = JsonMapper.builder().addModule(new WebauthnJacksonModule()).build();\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationOptional() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationOptional\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,\n\t\t\t\ttrue);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationOptionalWithCredentialIdList()\n\t\t\tthrows Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationOptionalWithCredentialIdList\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST,\n\t\t\t\ttrue);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenCredProtectUserVerificationRequired() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationRequired\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_REQUIRED,\n\t\t\t\ttrue);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenEnforceCredentialProtectionPolicyTrue() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationOptional\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,\n\t\t\t\ttrue);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenEnforceCredentialProtectionPolicyFalse() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credentialProtectionPolicy\": \"userVerificationOptional\",\n\t\t\t\t\t\t\"enforceCredentialProtectionPolicy\": false\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect credProtect = new CredProtectAuthenticationExtensionsClientInput.CredProtect(\n\t\t\t\tCredProtectAuthenticationExtensionsClientInput.CredProtect.ProtectionPolicy.USER_VERIFICATION_OPTIONAL,\n\t\t\t\tfalse);\n\t\tCredProtectAuthenticationExtensionsClientInput credProtectInput = new CredProtectAuthenticationExtensionsClientInput(\n\t\t\t\tcredProtect);\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tcredProtectInput);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/Jackson2Tests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.time.Duration;\nimport java.util.Arrays;\n\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@SuppressWarnings(\"removal\")\nclass Jackson2Tests {\n\n\tprivate ObjectMapper mapper;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.mapper = new ObjectMapper();\n\t\tthis.mapper.registerModule(new WebauthnJackson2Module());\n\t}\n\n\t@Test\n\tvoid readAuthenticatorTransport() throws Exception {\n\t\tAuthenticatorTransport transport = this.mapper.readValue(\"\\\"hybrid\\\"\", AuthenticatorTransport.class);\n\n\t\tassertThat(transport).isEqualTo(AuthenticatorTransport.HYBRID);\n\t}\n\n\t@Test\n\tvoid readAuthenticatorAttachment() throws Exception {\n\t\tAuthenticatorAttachment value = this.mapper.readValue(\"\\\"cross-platform\\\"\", AuthenticatorAttachment.class);\n\t\tassertThat(value).isEqualTo(AuthenticatorAttachment.CROSS_PLATFORM);\n\t}\n\n\t@Test\n\tvoid writeAuthenticatorAttachment() throws Exception {\n\t\tString value = this.mapper.writeValueAsString(AuthenticatorAttachment.CROSS_PLATFORM);\n\t\tassertThat(value).isEqualTo(\"\\\"cross-platform\\\"\");\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputs() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"credProps\": {\n\t\t\t\t\t\t\"rk\": false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tAuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,\n\t\t\t\tAuthenticationExtensionsClientOutputs.class);\n\t\tassertThat(outputs).usingRecursiveComparison().isEqualTo(clientExtensionResults);\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputsWhenAuthenticatorDisplayName() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"credProps\": {\n\t\t\t\t\t\t\"rk\": false,\n\t\t\t\t\t\t\"authenticatorDisplayName\": \"1Password\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tAuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,\n\t\t\t\tAuthenticationExtensionsClientOutputs.class);\n\t\tassertThat(outputs).usingRecursiveComparison().isEqualTo(clientExtensionResults);\n\t}\n\n\t@Test\n\tvoid readCredPropsWhenAuthenticatorDisplayName() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"rk\": false,\n\t\t\t\t\t\"authenticatorDisplayName\": \"1Password\"\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tCredentialPropertiesOutput credProps = new CredentialPropertiesOutput(false);\n\n\t\tCredentialPropertiesOutput outputs = this.mapper.readValue(json, CredentialPropertiesOutput.class);\n\t\tassertThat(outputs).usingRecursiveComparison().isEqualTo(credProps);\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputsWhenAppId() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t  \"appid\": false,\n\t\t\t\t  \"credProps\": {\n\t\t\t\t    \"rk\": false\n\t\t\t\t  }\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tCredentialPropertiesOutput credProps = new CredentialPropertiesOutput(false);\n\n\t\tAuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,\n\t\t\t\tAuthenticationExtensionsClientOutputs.class);\n\t\tassertThat(outputs.getOutputs()).usingRecursiveFieldByFieldElementComparator().contains(credProps);\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputsWhenUnknownExtension() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t  \"unknownObject1\": {\n\t\t\t\t    \"key\": \"value\"\n\t\t\t\t  },\n\t\t\t\t  \"unknownArray\": [\n\t\t\t\t    { \"key\": \"value1\" },\n\t\t\t\t    { \"key\": \"value2\" }\n\t\t\t\t  ],\n\t\t\t\t  \"credProps\": {\n\t\t\t\t    \"rk\": false\n\t\t\t\t  },\n\t\t\t\t  \"unknownObject2\": {}\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tCredentialPropertiesOutput credProps = new CredentialPropertiesOutput(false);\n\n\t\tAuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,\n\t\t\t\tAuthenticationExtensionsClientOutputs.class);\n\t\tassertThat(outputs.getOutputs()).usingRecursiveFieldByFieldElementComparator().contains(credProps);\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputsWhenFieldAfter() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"clientOutputs\": {\n\t\t\t\t\t\t\"credProps\": {\n\t\t\t\t\t\t\t\"rk\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"label\": \"Cell Phone\"\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tClassWithOutputsAndAnotherField expected = new ClassWithOutputsAndAnotherField();\n\t\texpected.setClientOutputs(clientExtensionResults);\n\t\texpected.setLabel(\"Cell Phone\");\n\n\t\tClassWithOutputsAndAnotherField actual = this.mapper.readValue(json, ClassWithOutputsAndAnotherField.class);\n\t\tassertThat(actual).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid writePublicKeyCredentialCreationOptions() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t{\n\t\t\t\t    \"attestation\": \"none\",\n\t\t\t\t    \"authenticatorSelection\": {\n\t\t\t\t        \"residentKey\": \"required\"\n\t\t\t\t    },\n\t\t\t\t    \"challenge\": \"q7lCdd3SVQxdC-v8pnRAGEn1B2M-t7ZECWPwCAmhWvc\",\n\t\t\t\t    \"excludeCredentials\": [],\n\t\t\t\t    \"extensions\": {\n\t\t\t\t        \"credProps\": true\n\t\t\t\t    },\n\t\t\t\t    \"pubKeyCredParams\": [\n\t\t\t\t        {\n\t\t\t\t            \"alg\": -7,\n\t\t\t\t            \"type\": \"public-key\"\n\t\t\t\t        },{\n\t\t\t\t            \"alg\": -8,\n\t\t\t\t            \"type\": \"public-key\"\n\t\t\t\t        },\n\t\t\t\t        {\n\t\t\t\t            \"alg\": -257,\n\t\t\t\t            \"type\": \"public-key\"\n\t\t\t\t        }\n\t\t\t\t    ],\n\t\t\t\t    \"rp\": {\n\t\t\t\t        \"id\": \"example.localhost\",\n\t\t\t\t        \"name\": \"SimpleWebAuthn Example\"\n\t\t\t\t    },\n\t\t\t\t    \"timeout\": 300000,\n\t\t\t\t    \"user\": {\n\t\t\t\t        \"displayName\": \"user@example.localhost\",\n\t\t\t\t        \"id\": \"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\",\n\t\t\t\t        \"name\": \"user@example.localhost\"\n\t\t\t\t    }\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\n\t\tString string = this.mapper.writeValueAsString(options);\n\n\t\tJSONAssert.assertEquals(expected, string, false);\n\t}\n\n\t@Test\n\tvoid readPublicKeyCredentialAuthenticatorAttestationResponse() throws Exception {\n\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> publicKeyCredential = this.mapper.readValue(\n\t\t\t\tPublicKeyCredentialJson.PUBLIC_KEY_JSON,\n\t\t\t\tnew TypeReference<PublicKeyCredential<AuthenticatorAttestationResponse>>() {\n\t\t\t\t});\n\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> expected = PublicKeyCredential.builder()\n\t\t\t.id(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\")\n\t\t\t.rawId(Bytes\n\t\t\t\t.fromBase64(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\"))\n\t\t\t.response(AuthenticatorAttestationResponse.builder()\n\t\t\t\t.attestationObject(Bytes.fromBase64(\n\t\t\t\t\t\t\"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\"))\n\t\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t\t.transports(AuthenticatorTransport.HYBRID, AuthenticatorTransport.INTERNAL)\n\t\t\t\t.build())\n\t\t\t.type(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t.clientExtensionResults(clientExtensionResults)\n\t\t\t.authenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM)\n\t\t\t.build();\n\n\t\tassertThat(publicKeyCredential).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid readPublicKeyCredentialAuthenticatorAttestationResponseWhenExtraFields() throws Exception {\n\t\tfinal String json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\",\n\t\t\t\t\t \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\t\t\t\t\t \"transports\": [\n\t\t\t\t\t   \"hybrid\",\n\t\t\t\t\t   \"internal\"\n\t\t\t\t\t ],\n\t\t\t\t\t \"publicKeyAlgorithm\": -7,\n\t\t\t\t\t \"publicKey\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkr7Z6k8TDS6Mc36C9WnYend5_wLNTfOrA7nKXHwvY6wrnHk6VMYQ_EtL7zlMAAG6bhqpUrgJJYnstgN2SO4EuQ\",\n\t\t\t\t\t \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\"\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tAuthenticatorAttestationResponse response = this.mapper.readValue(json, AuthenticatorAttestationResponse.class);\n\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tAuthenticatorAttestationResponse expected = AuthenticatorAttestationResponse.builder()\n\t\t\t.attestationObject(Bytes.fromBase64(\n\t\t\t\t\t\"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\"))\n\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t.transports(AuthenticatorTransport.HYBRID, AuthenticatorTransport.INTERNAL)\n\t\t\t.build();\n\n\t\tassertThat(response).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationOptions() throws Exception {\n\t\tPublicKeyCredentialRequestOptions credentialRequestOptions = PublicKeyCredentialRequestOptions.builder()\n\t\t\t.allowCredentials(Arrays.asList())\n\t\t\t.challenge(Bytes.fromBase64(\"I69THX904Q8ONhCgUgOu2PCQCcEjTDiNmokdbgsAsYU\"))\n\t\t\t.rpId(\"example.localhost\")\n\t\t\t.timeout(Duration.ofMinutes(5))\n\t\t\t.userVerification(UserVerificationRequirement.REQUIRED)\n\t\t\t.build();\n\t\tString actual = this.mapper.writeValueAsString(credentialRequestOptions);\n\n\t\tString expected = \"\"\"\n\t\t\t\t\t\t{\n\t\t\t\t    \"challenge\": \"I69THX904Q8ONhCgUgOu2PCQCcEjTDiNmokdbgsAsYU\",\n\t\t\t\t    \"allowCredentials\": [],\n\t\t\t\t    \"timeout\": 300000,\n\t\t\t\t    \"userVerification\": \"required\",\n\t\t\t\t    \"rpId\": \"example.localhost\"\n\t\t\t\t  }\n\n\t\t\t\t\"\"\";\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid readPublicKeyCredentialAuthenticatorAssertionResponse() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t   \"id\": \"IquGb208Fffq2cROa1ZxMg\",\n\t\t\t\t\t   \"rawId\": \"IquGb208Fffq2cROa1ZxMg\",\n\t\t\t\t\t   \"response\": {\n\t\t\t\t\t\t \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA\",\n\t\t\t\t\t\t \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaDB2Z3dHUWpvQ3pBekRVc216UHBrLUpWSUpSUmduMEw0S1ZTWU5SY0VaYyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\t\t\t\t\t\t \"signature\": \"MEUCIAdfzPAn3voyXynwa0IXk1S0envMY5KP3NEe9aj4B2BuAiEAm_KJhQoWXdvfhbzwACU3NM4ltQe7_Il46qFUwtpuTdg\",\n\t\t\t\t\t\t \"userHandle\": \"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\"\n\t\t\t\t\t   },\n\t\t\t\t\t   \"type\": \"public-key\",\n\t\t\t\t\t   \"clientExtensionResults\": {},\n\t\t\t\t\t   \"authenticatorAttachment\": \"cross-platform\"\n\t\t\t\t\t }\n\t\t\t\t\"\"\";\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> publicKeyCredential = this.mapper.readValue(json,\n\t\t\t\tnew TypeReference<PublicKeyCredential<AuthenticatorAssertionResponse>>() {\n\t\t\t\t});\n\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs();\n\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> expected = PublicKeyCredential.builder()\n\t\t\t.id(\"IquGb208Fffq2cROa1ZxMg\")\n\t\t\t.rawId(Bytes.fromBase64(\"IquGb208Fffq2cROa1ZxMg\"))\n\t\t\t.response(AuthenticatorAssertionResponse.builder()\n\t\t\t\t.authenticatorData(Bytes.fromBase64(\"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA\"))\n\t\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaDB2Z3dHUWpvQ3pBekRVc216UHBrLUpWSUpSUmduMEw0S1ZTWU5SY0VaYyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t\t.signature(Bytes.fromBase64(\n\t\t\t\t\t\t\"MEUCIAdfzPAn3voyXynwa0IXk1S0envMY5KP3NEe9aj4B2BuAiEAm_KJhQoWXdvfhbzwACU3NM4ltQe7_Il46qFUwtpuTdg\"))\n\t\t\t\t.userHandle(Bytes.fromBase64(\"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\"))\n\t\t\t\t.build())\n\t\t\t.type(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t.clientExtensionResults(clientExtensionResults)\n\t\t\t.authenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM)\n\t\t\t.build();\n\n\t\tassertThat(publicKeyCredential).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenCredPropsTrue() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credProps\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tImmutableAuthenticationExtensionsClientInput.credProps);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\tpublic static class ClassWithOutputsAndAnotherField {\n\n\t\tprivate String label;\n\n\t\tprivate AuthenticationExtensionsClientOutputs clientOutputs;\n\n\t\tpublic String getLabel() {\n\t\t\treturn this.label;\n\t\t}\n\n\t\tpublic void setLabel(String label) {\n\t\t\tthis.label = label;\n\t\t}\n\n\t\tpublic AuthenticationExtensionsClientOutputs getClientOutputs() {\n\t\t\treturn this.clientOutputs;\n\t\t}\n\n\t\tpublic void setClientOutputs(AuthenticationExtensionsClientOutputs clientOutputs) {\n\t\t\tthis.clientOutputs = clientOutputs;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/JacksonTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.time.Duration;\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.core.type.TypeReference;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientInputs;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass JacksonTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.mapper = JsonMapper.builder().addModule(new WebauthnJacksonModule()).build();\n\t}\n\n\t@Test\n\tvoid readAuthenticatorTransport() throws Exception {\n\t\tAuthenticatorTransport transport = this.mapper.readValue(\"\\\"hybrid\\\"\", AuthenticatorTransport.class);\n\n\t\tassertThat(transport).isEqualTo(AuthenticatorTransport.HYBRID);\n\t}\n\n\t@Test\n\tvoid readAuthenticatorAttachment() throws Exception {\n\t\tAuthenticatorAttachment value = this.mapper.readValue(\"\\\"cross-platform\\\"\", AuthenticatorAttachment.class);\n\t\tassertThat(value).isEqualTo(AuthenticatorAttachment.CROSS_PLATFORM);\n\t}\n\n\t@Test\n\tvoid writeAuthenticatorAttachment() throws Exception {\n\t\tString value = this.mapper.writeValueAsString(AuthenticatorAttachment.CROSS_PLATFORM);\n\t\tassertThat(value).isEqualTo(\"\\\"cross-platform\\\"\");\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputs() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"credProps\": {\n\t\t\t\t\t\t\"rk\": false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tAuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,\n\t\t\t\tAuthenticationExtensionsClientOutputs.class);\n\t\tassertThat(outputs).usingRecursiveComparison().isEqualTo(clientExtensionResults);\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputsWhenAuthenticatorDisplayName() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"credProps\": {\n\t\t\t\t\t\t\"rk\": false,\n\t\t\t\t\t\t\"authenticatorDisplayName\": \"1Password\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tAuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,\n\t\t\t\tAuthenticationExtensionsClientOutputs.class);\n\t\tassertThat(outputs).usingRecursiveComparison().isEqualTo(clientExtensionResults);\n\t}\n\n\t@Test\n\tvoid readCredPropsWhenAuthenticatorDisplayName() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"rk\": false,\n\t\t\t\t\t\"authenticatorDisplayName\": \"1Password\"\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tCredentialPropertiesOutput credProps = new CredentialPropertiesOutput(false);\n\n\t\tCredentialPropertiesOutput outputs = this.mapper.readValue(json, CredentialPropertiesOutput.class);\n\t\tassertThat(outputs).usingRecursiveComparison().isEqualTo(credProps);\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputsWhenAppId() {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t  \"appid\": false,\n\t\t\t\t  \"credProps\": {\n\t\t\t\t    \"rk\": false\n\t\t\t\t  }\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tCredentialPropertiesOutput credProps = new CredentialPropertiesOutput(false);\n\n\t\tAuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,\n\t\t\t\tAuthenticationExtensionsClientOutputs.class);\n\t\tassertThat(outputs.getOutputs()).usingRecursiveFieldByFieldElementComparator().contains(credProps);\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputsWhenUnknownExtension() {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t  \"unknownObject1\": {\n\t\t\t\t    \"key\": \"value\"\n\t\t\t\t  },\n\t\t\t\t  \"unknownArray\": [\n\t\t\t\t    { \"key\": \"value1\" },\n\t\t\t\t    { \"key\": \"value2\" }\n\t\t\t\t  ],\n\t\t\t\t  \"credProps\": {\n\t\t\t\t    \"rk\": false\n\t\t\t\t  },\n\t\t\t\t  \"unknownObject2\": {}\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tCredentialPropertiesOutput credProps = new CredentialPropertiesOutput(false);\n\n\t\tAuthenticationExtensionsClientOutputs outputs = this.mapper.readValue(json,\n\t\t\t\tAuthenticationExtensionsClientOutputs.class);\n\t\tassertThat(outputs.getOutputs()).usingRecursiveFieldByFieldElementComparator().contains(credProps);\n\t}\n\n\t@Test\n\tvoid readAuthenticationExtensionsClientOutputsWhenFieldAfter() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"clientOutputs\": {\n\t\t\t\t\t\t\"credProps\": {\n\t\t\t\t\t\t\t\"rk\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"label\": \"Cell Phone\"\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tClassWithOutputsAndAnotherField expected = new ClassWithOutputsAndAnotherField();\n\t\texpected.setClientOutputs(clientExtensionResults);\n\t\texpected.setLabel(\"Cell Phone\");\n\n\t\tClassWithOutputsAndAnotherField actual = this.mapper.readValue(json, ClassWithOutputsAndAnotherField.class);\n\t\tassertThat(actual).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid writePublicKeyCredentialCreationOptions() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t{\n\t\t\t\t    \"attestation\": \"none\",\n\t\t\t\t    \"authenticatorSelection\": {\n\t\t\t\t        \"residentKey\": \"required\"\n\t\t\t\t    },\n\t\t\t\t    \"challenge\": \"q7lCdd3SVQxdC-v8pnRAGEn1B2M-t7ZECWPwCAmhWvc\",\n\t\t\t\t    \"excludeCredentials\": [],\n\t\t\t\t    \"extensions\": {\n\t\t\t\t        \"credProps\": true\n\t\t\t\t    },\n\t\t\t\t    \"pubKeyCredParams\": [\n\t\t\t\t        {\n\t\t\t\t            \"alg\": -7,\n\t\t\t\t            \"type\": \"public-key\"\n\t\t\t\t        },{\n\t\t\t\t            \"alg\": -8,\n\t\t\t\t            \"type\": \"public-key\"\n\t\t\t\t        },\n\t\t\t\t        {\n\t\t\t\t            \"alg\": -257,\n\t\t\t\t            \"type\": \"public-key\"\n\t\t\t\t        }\n\t\t\t\t    ],\n\t\t\t\t    \"rp\": {\n\t\t\t\t        \"id\": \"example.localhost\",\n\t\t\t\t        \"name\": \"SimpleWebAuthn Example\"\n\t\t\t\t    },\n\t\t\t\t    \"timeout\": 300000,\n\t\t\t\t    \"user\": {\n\t\t\t\t        \"displayName\": \"user@example.localhost\",\n\t\t\t\t        \"id\": \"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\",\n\t\t\t\t        \"name\": \"user@example.localhost\"\n\t\t\t\t    }\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\n\t\tString string = this.mapper.writeValueAsString(options);\n\n\t\tJSONAssert.assertEquals(expected, string, false);\n\t}\n\n\t@Test\n\tvoid readPublicKeyCredentialAuthenticatorAttestationResponse() throws Exception {\n\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> publicKeyCredential = this.mapper.readValue(\n\t\t\t\tPublicKeyCredentialJson.PUBLIC_KEY_JSON,\n\t\t\t\tnew TypeReference<PublicKeyCredential<AuthenticatorAttestationResponse>>() {\n\t\t\t\t});\n\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> expected = PublicKeyCredential.builder()\n\t\t\t.id(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\")\n\t\t\t.rawId(Bytes\n\t\t\t\t.fromBase64(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\"))\n\t\t\t.response(AuthenticatorAttestationResponse.builder()\n\t\t\t\t.attestationObject(Bytes.fromBase64(\n\t\t\t\t\t\t\"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\"))\n\t\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t\t.transports(AuthenticatorTransport.HYBRID, AuthenticatorTransport.INTERNAL)\n\t\t\t\t.build())\n\t\t\t.type(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t.clientExtensionResults(clientExtensionResults)\n\t\t\t.authenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM)\n\t\t\t.build();\n\n\t\tassertThat(publicKeyCredential).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid readPublicKeyCredentialAuthenticatorAttestationResponseWhenExtraFields() throws Exception {\n\t\tfinal String json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\",\n\t\t\t\t\t \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\t\t\t\t\t \"transports\": [\n\t\t\t\t\t   \"hybrid\",\n\t\t\t\t\t   \"internal\"\n\t\t\t\t\t ],\n\t\t\t\t\t \"publicKeyAlgorithm\": -7,\n\t\t\t\t\t \"publicKey\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkr7Z6k8TDS6Mc36C9WnYend5_wLNTfOrA7nKXHwvY6wrnHk6VMYQ_EtL7zlMAAG6bhqpUrgJJYnstgN2SO4EuQ\",\n\t\t\t\t\t \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\"\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tAuthenticatorAttestationResponse response = this.mapper.readValue(json, AuthenticatorAttestationResponse.class);\n\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tAuthenticatorAttestationResponse expected = AuthenticatorAttestationResponse.builder()\n\t\t\t.attestationObject(Bytes.fromBase64(\n\t\t\t\t\t\"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\"))\n\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t.transports(AuthenticatorTransport.HYBRID, AuthenticatorTransport.INTERNAL)\n\t\t\t.build();\n\n\t\tassertThat(response).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationOptions() throws Exception {\n\t\tPublicKeyCredentialRequestOptions credentialRequestOptions = PublicKeyCredentialRequestOptions.builder()\n\t\t\t.allowCredentials(Arrays.asList())\n\t\t\t.challenge(Bytes.fromBase64(\"I69THX904Q8ONhCgUgOu2PCQCcEjTDiNmokdbgsAsYU\"))\n\t\t\t.rpId(\"example.localhost\")\n\t\t\t.timeout(Duration.ofMinutes(5))\n\t\t\t.userVerification(UserVerificationRequirement.REQUIRED)\n\t\t\t.build();\n\t\tString actual = this.mapper.writeValueAsString(credentialRequestOptions);\n\n\t\tString expected = \"\"\"\n\t\t\t\t\t\t{\n\t\t\t\t    \"challenge\": \"I69THX904Q8ONhCgUgOu2PCQCcEjTDiNmokdbgsAsYU\",\n\t\t\t\t    \"allowCredentials\": [],\n\t\t\t\t    \"timeout\": 300000,\n\t\t\t\t    \"userVerification\": \"required\",\n\t\t\t\t    \"rpId\": \"example.localhost\"\n\t\t\t\t  }\n\n\t\t\t\t\"\"\";\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\t@Test\n\tvoid readPublicKeyCredentialAuthenticatorAssertionResponse() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t   \"id\": \"IquGb208Fffq2cROa1ZxMg\",\n\t\t\t\t\t   \"rawId\": \"IquGb208Fffq2cROa1ZxMg\",\n\t\t\t\t\t   \"response\": {\n\t\t\t\t\t\t \"authenticatorData\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA\",\n\t\t\t\t\t\t \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaDB2Z3dHUWpvQ3pBekRVc216UHBrLUpWSUpSUmduMEw0S1ZTWU5SY0VaYyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\t\t\t\t\t\t \"signature\": \"MEUCIAdfzPAn3voyXynwa0IXk1S0envMY5KP3NEe9aj4B2BuAiEAm_KJhQoWXdvfhbzwACU3NM4ltQe7_Il46qFUwtpuTdg\",\n\t\t\t\t\t\t \"userHandle\": \"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\"\n\t\t\t\t\t   },\n\t\t\t\t\t   \"type\": \"public-key\",\n\t\t\t\t\t   \"clientExtensionResults\": {},\n\t\t\t\t\t   \"authenticatorAttachment\": \"cross-platform\"\n\t\t\t\t\t }\n\t\t\t\t\"\"\";\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> publicKeyCredential = this.mapper.readValue(json,\n\t\t\t\tnew TypeReference<PublicKeyCredential<AuthenticatorAssertionResponse>>() {\n\t\t\t\t});\n\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs();\n\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> expected = PublicKeyCredential.builder()\n\t\t\t.id(\"IquGb208Fffq2cROa1ZxMg\")\n\t\t\t.rawId(Bytes.fromBase64(\"IquGb208Fffq2cROa1ZxMg\"))\n\t\t\t.response(AuthenticatorAssertionResponse.builder()\n\t\t\t\t.authenticatorData(Bytes.fromBase64(\"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA\"))\n\t\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaDB2Z3dHUWpvQ3pBekRVc216UHBrLUpWSUpSUmduMEw0S1ZTWU5SY0VaYyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t\t.signature(Bytes.fromBase64(\n\t\t\t\t\t\t\"MEUCIAdfzPAn3voyXynwa0IXk1S0envMY5KP3NEe9aj4B2BuAiEAm_KJhQoWXdvfhbzwACU3NM4ltQe7_Il46qFUwtpuTdg\"))\n\t\t\t\t.userHandle(Bytes.fromBase64(\"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\"))\n\t\t\t\t.build())\n\t\t\t.type(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t.clientExtensionResults(clientExtensionResults)\n\t\t\t.authenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM)\n\t\t\t.build();\n\n\t\tassertThat(publicKeyCredential).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid writeAuthenticationExtensionsClientInputsWhenCredPropsTrue() throws Exception {\n\t\tString expected = \"\"\"\n\t\t\t\t\t{\n\t\t\t\t\t\t\"credProps\": true\n\t\t\t\t\t}\n\t\t\t\t\"\"\";\n\n\t\tImmutableAuthenticationExtensionsClientInputs clientInputs = new ImmutableAuthenticationExtensionsClientInputs(\n\t\t\t\tImmutableAuthenticationExtensionsClientInput.credProps);\n\n\t\tString actual = this.mapper.writeValueAsString(clientInputs);\n\n\t\tJSONAssert.assertEquals(expected, actual, false);\n\t}\n\n\tpublic static class ClassWithOutputsAndAnotherField {\n\n\t\tprivate String label;\n\n\t\tprivate AuthenticationExtensionsClientOutputs clientOutputs;\n\n\t\tpublic String getLabel() {\n\t\t\treturn this.label;\n\t\t}\n\n\t\tpublic void setLabel(String label) {\n\t\t\tthis.label = label;\n\t\t}\n\n\t\tpublic AuthenticationExtensionsClientOutputs getClientOutputs() {\n\t\t\treturn this.clientOutputs;\n\t\t}\n\n\t\tpublic void setClientOutputs(AuthenticationExtensionsClientOutputs clientOutputs) {\n\t\t\tthis.clientOutputs = clientOutputs;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/PublicKeyCredentialJson.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\n/**\n * JSON for {@code PublicKeyCredential<AuthenticatorAttestationResponse>}\n *\n * @author Rob Winch\n * @since 6.4\n */\npublic final class PublicKeyCredentialJson {\n\n\tpublic static final String PUBLIC_KEY_JSON = \"\"\"\n\t\t\t\t{\n\t\t\t\t   \"id\": \"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\",\n\t\t\t\t   \"rawId\": \"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\",\n\t\t\t\t   \"response\": {\n\t\t\t\t\t \"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\",\n\t\t\t\t\t \"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\t\t\t\t\t \"transports\": [\n\t\t\t\t\t   \"hybrid\",\n\t\t\t\t\t   \"internal\"\n\t\t\t\t\t ]\n\t\t\t\t   },\n\t\t\t\t   \"type\": \"public-key\",\n\t\t\t\t   \"clientExtensionResults\": {\n\t\t\t\t\t \"credProps\": {\n\t\t\t\t\t   \"rk\": false\n\t\t\t\t\t }\n\t\t\t\t   },\n\t\t\t\t   \"authenticatorAttachment\": \"cross-platform\"\n\t\t\t\t }\n\t\t\t\"\"\";\n\n\tprivate PublicKeyCredentialJson() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/jackson/WebAuthnAuthenticationMixinTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.jackson;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.skyscreamer.jsonassert.JSONAssert;\nimport tools.jackson.databind.JacksonModule;\nimport tools.jackson.databind.json.JsonMapper;\nimport tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;\n\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.jackson.SecurityJacksonModules;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for {@link WebAuthnAuthenticationMixin} and\n * {@link ImmutablePublicKeyCredentialUserEntityMixin} with polymorphic type handling.\n *\n * <p>\n * This test class is separate from {@link JacksonTests} because it requires a\n * {@link JsonMapper} configured with {@link SecurityJacksonModules} to enable polymorphic\n * type information ({@code @class}). {@link JacksonTests} uses a {@link JsonMapper}\n * configured only with {@link WebauthnJacksonModule}, and its existing custom serializers\n * are not compatible with the automatic inclusion of type information enabled by\n * {@link SecurityJacksonModules}.\n *\n * @author Toshiaki Maki\n * @since 7.1\n */\nclass WebAuthnAuthenticationMixinTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tClassLoader classLoader = getClass().getClassLoader();\n\t\tWebauthnJacksonModule webauthnJacksonModule = new WebauthnJacksonModule();\n\t\tBasicPolymorphicTypeValidator.Builder typeValidatorBuilder = BasicPolymorphicTypeValidator.builder();\n\t\twebauthnJacksonModule.configurePolymorphicTypeValidator(typeValidatorBuilder);\n\t\tList<JacksonModule> modules = SecurityJacksonModules.getModules(classLoader, typeValidatorBuilder);\n\t\tmodules.add(webauthnJacksonModule);\n\t\tthis.mapper = JsonMapper.builder().addModules(modules).build();\n\t}\n\n\t@Test\n\tvoid writeWebAuthnAuthentication() throws Exception {\n\t\tImmutablePublicKeyCredentialUserEntity principal = (ImmutablePublicKeyCredentialUserEntity) ImmutablePublicKeyCredentialUserEntity\n\t\t\t.builder()\n\t\t\t.name(\"user@example.localhost\")\n\t\t\t.id(Bytes.fromBase64(\"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\"))\n\t\t\t.displayName(\"User\")\n\t\t\t.build();\n\t\tWebAuthnAuthentication authentication = new WebAuthnAuthentication(principal,\n\t\t\t\tList.of(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\n\t\tString json = this.mapper.writeValueAsString(authentication);\n\n\t\tString expected = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"@class\": \"org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication\",\n\t\t\t\t\t\"principal\": {\n\t\t\t\t\t\t\"@class\": \"org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity\",\n\t\t\t\t\t\t\"name\": \"user@example.localhost\",\n\t\t\t\t\t\t\"id\": \"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\",\n\t\t\t\t\t\t\"displayName\": \"User\"\n\t\t\t\t\t},\n\t\t\t\t\t\"authorities\": [\"java.util.Collections$UnmodifiableRandomAccessList\", [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\",\n\t\t\t\t\t\t\t\"authority\": \"ROLE_USER\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]]\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tJSONAssert.assertEquals(expected, json, false);\n\t\tassertThat(json).doesNotContain(\"\\\"authenticated\\\"\");\n\t}\n\n\t@Test\n\tvoid readWebAuthnAuthentication() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"@class\": \"org.springframework.security.web.webauthn.authentication.WebAuthnAuthentication\",\n\t\t\t\t\t\"principal\": {\n\t\t\t\t\t\t\"@class\": \"org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity\",\n\t\t\t\t\t\t\"name\": \"user@example.localhost\",\n\t\t\t\t\t\t\"id\": \"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\",\n\t\t\t\t\t\t\"displayName\": \"User\"\n\t\t\t\t\t},\n\t\t\t\t\t\"authorities\": [\"java.util.Collections$UnmodifiableRandomAccessList\", [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\",\n\t\t\t\t\t\t\t\"authority\": \"ROLE_USER\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]]\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tImmutablePublicKeyCredentialUserEntity expectedPrincipal = (ImmutablePublicKeyCredentialUserEntity) ImmutablePublicKeyCredentialUserEntity\n\t\t\t.builder()\n\t\t\t.name(\"user@example.localhost\")\n\t\t\t.id(Bytes.fromBase64(\"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\"))\n\t\t\t.displayName(\"User\")\n\t\t\t.build();\n\t\tWebAuthnAuthentication expected = new WebAuthnAuthentication(expectedPrincipal,\n\t\t\t\tList.of(new SimpleGrantedAuthority(\"ROLE_USER\")));\n\n\t\tWebAuthnAuthentication authentication = this.mapper.readValue(json, WebAuthnAuthentication.class);\n\n\t\tassertThat(authentication).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/management/JdbcPublicKeyCredentialUserEntityRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link JdbcPublicKeyCredentialUserEntityRepository}\n *\n * @author Max Batischev\n */\npublic class JdbcPublicKeyCredentialUserEntityRepositoryTests {\n\n\tprivate EmbeddedDatabase db;\n\n\tprivate JdbcPublicKeyCredentialUserEntityRepository repository;\n\n\tprivate static final String USER_ENTITIES_SQL_RESOURCE = \"org/springframework/security/user-entities-schema.sql\";\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.db = createDb();\n\t\tJdbcOperations jdbcOperations = new JdbcTemplate(this.db);\n\t\tthis.repository = new JdbcPublicKeyCredentialUserEntityRepository(jdbcOperations);\n\t}\n\n\t@AfterEach\n\tvoid tearDown() {\n\t\tthis.db.shutdown();\n\t}\n\n\tprivate static EmbeddedDatabase createDb() {\n\t\t// @formatter:off\n\t\treturn new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(USER_ENTITIES_SQL_RESOURCE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new JdbcPublicKeyCredentialUserEntityRepository(null))\n\t\t\t\t.withMessage(\"jdbcOperations cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid saveWhenUserEntityIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.repository.save(null))\n\t\t\t\t.withMessage(\"userEntity cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid findByUserEntityIdWheIdIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.repository.findById(null))\n\t\t\t\t.withMessage(\"id cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid findByUserNameWheUserNameIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.repository.findByUsername(null))\n\t\t\t\t.withMessage(\"name cannot be null or empty\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid saveUserEntityWhenSaveThenReturnsSaved() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\n\t\tthis.repository.save(userEntity);\n\n\t\tPublicKeyCredentialUserEntity savedUserEntity = this.repository.findById(userEntity.getId());\n\t\tassertThat(savedUserEntity).isNotNull();\n\t\tassertThat(savedUserEntity.getId()).isEqualTo(userEntity.getId());\n\t\tassertThat(savedUserEntity.getDisplayName()).isEqualTo(userEntity.getDisplayName());\n\t\tassertThat(savedUserEntity.getName()).isEqualTo(userEntity.getName());\n\t}\n\n\t@Test\n\tvoid saveUserEntityWhenUserEntityExistsThenUpdates() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tthis.repository.save(userEntity);\n\n\t\tthis.repository.save(testUserEntity(userEntity.getId()));\n\n\t\tPublicKeyCredentialUserEntity savedUserEntity = this.repository.findById(userEntity.getId());\n\t\tassertThat(savedUserEntity).isNotNull();\n\t\tassertThat(savedUserEntity.getId()).isEqualTo(userEntity.getId());\n\t\tassertThat(savedUserEntity.getDisplayName()).isEqualTo(\"user2\");\n\t\tassertThat(savedUserEntity.getName()).isEqualTo(\"user2\");\n\t}\n\n\t@Test\n\tvoid findUserEntityByUserNameWhenUserEntityExistsThenReturnsSaved() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tthis.repository.save(userEntity);\n\n\t\tPublicKeyCredentialUserEntity savedUserEntity = this.repository.findByUsername(userEntity.getName());\n\n\t\tassertThat(savedUserEntity).isNotNull();\n\t}\n\n\t@Test\n\tvoid deleteUserEntityWhenRecordExistThenSuccess() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tthis.repository.save(userEntity);\n\n\t\tthis.repository.delete(userEntity.getId());\n\n\t\tPublicKeyCredentialUserEntity savedUserEntity = this.repository.findById(userEntity.getId());\n\t\tassertThat(savedUserEntity).isNull();\n\t}\n\n\t@Test\n\tvoid findUserEntityByIdWhenUserEntityDoesNotExistThenReturnsNull() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\n\t\tPublicKeyCredentialUserEntity savedUserEntity = this.repository.findById(userEntity.getId());\n\t\tassertThat(savedUserEntity).isNull();\n\t}\n\n\t@Test\n\tvoid findUserEntityByUserNameWhenUserEntityDoesNotExistThenReturnsEmpty() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\n\t\tPublicKeyCredentialUserEntity savedUserEntity = this.repository.findByUsername(userEntity.getName());\n\t\tassertThat(savedUserEntity).isNull();\n\t}\n\n\tprivate PublicKeyCredentialUserEntity testUserEntity(Bytes id) {\n\t\t// @formatter:off\n\t\treturn ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t\t.name(\"user2\")\n\t\t\t\t.id(id)\n\t\t\t\t.displayName(\"user2\")\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.jdbc.core.JdbcOperations;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;\nimport org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.security.web.webauthn.api.TestCredentialRecords;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\n\n/**\n * Tests for {@link JdbcUserCredentialRepository}\n *\n * @author Max Batischev\n */\npublic class JdbcUserCredentialRepositoryTests {\n\n\tprivate EmbeddedDatabase db;\n\n\tprivate JdbcUserCredentialRepository jdbcUserCredentialRepository;\n\n\tprivate static final String USER_CREDENTIALS_SQL_RESOURCE = \"org/springframework/security/user-credentials-schema.sql\";\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.db = createDb();\n\t\tJdbcOperations jdbcOperations = new JdbcTemplate(this.db);\n\t\tthis.jdbcUserCredentialRepository = new JdbcUserCredentialRepository(jdbcOperations);\n\t}\n\n\t@AfterEach\n\tvoid tearDown() {\n\t\tthis.db.shutdown();\n\t}\n\n\tprivate static EmbeddedDatabase createDb() {\n\t\t// @formatter:off\n\t\treturn new EmbeddedDatabaseBuilder()\n\t\t\t\t.generateUniqueName(true)\n\t\t\t\t.setType(EmbeddedDatabaseType.HSQL)\n\t\t\t\t.setScriptEncoding(\"UTF-8\")\n\t\t\t\t.addScript(USER_CREDENTIALS_SQL_RESOURCE)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> new JdbcUserCredentialRepository(null))\n\t\t\t\t.withMessage(\"jdbcOperations cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid saveWhenCredentialRecordIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jdbcUserCredentialRepository.save(null))\n\t\t\t\t.withMessage(\"record cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid findByCredentialIdWheCredentialIdIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jdbcUserCredentialRepository.findByCredentialId(null))\n\t\t\t\t.withMessage(\"credentialId cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid findByCredentialIdWheUserIdIsNullThenThrowIllegalArgumentException() {\n\t\t// @formatter:off\n\t\tassertThatIllegalArgumentException()\n\t\t\t\t.isThrownBy(() -> this.jdbcUserCredentialRepository.findByUserId(null))\n\t\t\t\t.withMessage(\"userId cannot be null\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid saveCredentialRecordWhenSaveThenReturnsSaved() {\n\t\tCredentialRecord userCredential = TestCredentialRecords.fullUserCredential().build();\n\t\tthis.jdbcUserCredentialRepository.save(userCredential);\n\n\t\tCredentialRecord savedUserCredential = this.jdbcUserCredentialRepository\n\t\t\t.findByCredentialId(userCredential.getCredentialId());\n\n\t\tassertThat(savedUserCredential).isNotNull();\n\t\tassertThat(savedUserCredential.getCredentialId()).isEqualTo(userCredential.getCredentialId());\n\t\tassertThat(savedUserCredential.getUserEntityUserId()).isEqualTo(userCredential.getUserEntityUserId());\n\t\tassertThat(savedUserCredential.getLabel()).isEqualTo(userCredential.getLabel());\n\t\tassertThat(savedUserCredential.getPublicKey().getBytes()).isEqualTo(userCredential.getPublicKey().getBytes());\n\t\tassertThat(savedUserCredential.isBackupEligible()).isEqualTo(userCredential.isBackupEligible());\n\t\tassertThat(savedUserCredential.isBackupState()).isEqualTo(userCredential.isBackupState());\n\t\tassertThat(savedUserCredential.getCreated()).isNotNull();\n\t\tassertThat(savedUserCredential.getLastUsed()).isNotNull();\n\t\tassertThat(savedUserCredential.isUvInitialized()).isFalse();\n\t\tassertThat(savedUserCredential.getSignatureCount()).isEqualTo(100);\n\t\tassertThat(savedUserCredential.getCredentialType()).isEqualTo(PublicKeyCredentialType.PUBLIC_KEY);\n\t\tassertThat(savedUserCredential.getTransports().contains(AuthenticatorTransport.HYBRID)).isTrue();\n\t\tassertThat(savedUserCredential.getTransports().contains(AuthenticatorTransport.BLE)).isTrue();\n\t\tassertThat(new String(savedUserCredential.getAttestationObject().getBytes())).isEqualTo(\"test\");\n\t\tassertThat(new String(savedUserCredential.getAttestationClientDataJSON().getBytes())).isEqualTo(\"test\");\n\t}\n\n\t@Test\n\tvoid saveCredentialRecordWhenRecordExistsThenReturnsUpdated() {\n\t\tCredentialRecord userCredential = TestCredentialRecords.fullUserCredential().build();\n\t\tthis.jdbcUserCredentialRepository.save(userCredential);\n\t\t// @formatter:off\n\t\tCredentialRecord updatedRecord = ImmutableCredentialRecord.fromCredentialRecord(userCredential)\n\t\t\t\t.backupEligible(false)\n\t\t\t\t.uvInitialized(true)\n\t\t\t\t.signatureCount(200).build();\n\t\t// @formatter:on\n\n\t\tthis.jdbcUserCredentialRepository.save(updatedRecord);\n\n\t\tCredentialRecord record = this.jdbcUserCredentialRepository\n\t\t\t.findByCredentialId(userCredential.getCredentialId());\n\n\t\tassertThat(record.getSignatureCount()).isEqualTo(200);\n\t\tassertThat(record.isUvInitialized()).isTrue();\n\t\tassertThat(record.isBackupEligible()).isFalse();\n\t}\n\n\t@Test\n\tvoid findCredentialRecordByUserIdWhenRecordExistsThenReturnsSaved() {\n\t\tCredentialRecord userCredential = TestCredentialRecords.fullUserCredential().build();\n\t\tthis.jdbcUserCredentialRepository.save(userCredential);\n\n\t\tList<CredentialRecord> credentialRecords = this.jdbcUserCredentialRepository\n\t\t\t.findByUserId(userCredential.getUserEntityUserId());\n\n\t\tassertThat(credentialRecords).isNotNull();\n\t\tassertThat(credentialRecords.size()).isEqualTo(1);\n\t}\n\n\t@Test\n\tvoid findCredentialRecordByUserIdWhenRecordDoesNotExistThenReturnsEmpty() {\n\t\tCredentialRecord userCredential = TestCredentialRecords.fullUserCredential().build();\n\n\t\tList<CredentialRecord> credentialRecords = this.jdbcUserCredentialRepository\n\t\t\t.findByUserId(userCredential.getUserEntityUserId());\n\n\t\tassertThat(credentialRecords.size()).isEqualTo(0);\n\t}\n\n\t@Test\n\tvoid findCredentialRecordByCredentialIdWhenRecordDoesNotExistThenReturnsNull() {\n\t\tCredentialRecord userCredential = TestCredentialRecords.fullUserCredential().build();\n\n\t\tCredentialRecord credentialRecord = this.jdbcUserCredentialRepository\n\t\t\t.findByCredentialId(userCredential.getCredentialId());\n\n\t\tassertThat(credentialRecord).isNull();\n\t}\n\n\t@Test\n\tvoid deleteCredentialRecordWhenRecordExistThenSuccess() {\n\t\tCredentialRecord userCredential = TestCredentialRecords.fullUserCredential().build();\n\t\tthis.jdbcUserCredentialRepository.save(userCredential);\n\n\t\tthis.jdbcUserCredentialRepository.delete(userCredential.getCredentialId());\n\n\t\tCredentialRecord credentialRecord = this.jdbcUserCredentialRepository\n\t\t\t.findByCredentialId(userCredential.getCredentialId());\n\t\tassertThat(credentialRecord).isNull();\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/management/MapPublicKeyCredentialUserEntityRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Tests for {@link MapPublicKeyCredentialUserEntityRepository}.\n *\n * @author Rob Winch\n * @since 6.4\n */\nclass MapPublicKeyCredentialUserEntityRepositoryTests {\n\n\tprivate MapPublicKeyCredentialUserEntityRepository userEntities = new MapPublicKeyCredentialUserEntityRepository();\n\n\tprivate String username = \"username\";\n\n\tprivate PublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity()\n\t\t.name(this.username)\n\t\t.build();\n\n\t@Test\n\tvoid findByIdWhenExistsThenFound() {\n\t\tthis.userEntities.save(this.userEntity);\n\t\tPublicKeyCredentialUserEntity findById = this.userEntities.findById(this.userEntity.getId());\n\t\tassertThat(findById).isEqualTo(this.userEntity);\n\t}\n\n\t@Test\n\tvoid findByIdWhenDoesNotExistThenNull() {\n\t\tPublicKeyCredentialUserEntity findById = this.userEntities.findById(this.userEntity.getId());\n\t\tassertThat(findById).isNull();\n\t}\n\n\t@Test\n\tvoid findByUsernameWhenExistsThenFound() {\n\t\tthis.userEntities.save(this.userEntity);\n\t\tPublicKeyCredentialUserEntity foundUserEntity = this.userEntities.findByUsername(this.username);\n\t\tassertThat(foundUserEntity).isEqualTo(this.userEntity);\n\t}\n\n\t@Test\n\tvoid findByUsernameReturnsNullWhenUsernameDoesNotExist() {\n\t\tPublicKeyCredentialUserEntity foundUserEntity = this.userEntities.findByUsername(this.username);\n\t\tassertThat(foundUserEntity).isNull();\n\t}\n\n\t@Test\n\tvoid saveWhenNonNullThenSuccess() {\n\t\tthis.userEntities.save(this.userEntity);\n\t\tPublicKeyCredentialUserEntity foundUserEntity = this.userEntities.findByUsername(this.username);\n\t\tassertThat(foundUserEntity).isEqualTo(this.userEntity);\n\t}\n\n\t@Test\n\tvoid saveWhenUpdateThenUpdated() {\n\t\tPublicKeyCredentialUserEntity newUserEntity = TestPublicKeyCredentialUserEntities.userEntity()\n\t\t\t.name(this.userEntity.getName())\n\t\t\t.displayName(\"Updated\")\n\t\t\t.build();\n\t\tthis.userEntities.save(this.userEntity);\n\t\tthis.userEntities.save(newUserEntity);\n\t\tPublicKeyCredentialUserEntity foundUserEntity = this.userEntities.findByUsername(this.username);\n\t\tassertThat(foundUserEntity).isEqualTo(newUserEntity);\n\t}\n\n\t@Test\n\tvoid deleteWhenExistsThenRemovesExistingEntry() {\n\t\tthis.userEntities.save(this.userEntity);\n\t\tthis.userEntities.delete(this.userEntity.getId());\n\t\tassertThat(this.userEntities.findByUsername(this.username)).isNull();\n\t\tassertThat(this.userEntities.findById(this.userEntity.getId())).isNull();\n\t}\n\n\t@Test\n\tvoid deleteWhenNullAndDoesNotExistThenNoException() {\n\t\tassertThatNoException().isThrownBy(() -> this.userEntities.delete(this.userEntity.getId()));\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/management/MapUserCredentialRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.time.Instant;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;\nimport org.springframework.security.web.webauthn.api.TestBytes;\nimport org.springframework.security.web.webauthn.api.TestCredentialRecords;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\n\n/**\n * Tests for {@link MapUserCredentialRepository}\n *\n * @author Rob Winch\n * @since 6.4\n */\nclass MapUserCredentialRepositoryTests {\n\n\tprivate final MapUserCredentialRepository userCredentials = new MapUserCredentialRepository();\n\n\t@Test\n\tvoid findByUserIdWhenNotFoundThenEmpty() {\n\t\tassertThat(this.userCredentials.findByUserId(TestBytes.get())).isEmpty();\n\t}\n\n\t@Test\n\tvoid findByUserIdWhenNullIdThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userCredentials.findByUserId(null));\n\t}\n\n\t@Test\n\tvoid findByCredentialIdWhenIdNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userCredentials.findByCredentialId(null));\n\t}\n\n\t@Test\n\tvoid findByCredentialIdWhenNotFoundThenIllegalArgumentException() {\n\t\tassertThat(this.userCredentials.findByCredentialId(TestBytes.get())).isNull();\n\t}\n\n\t@Test\n\tvoid deleteWhenCredentialNotFoundThenNoException() {\n\t\tImmutableCredentialRecord credentialRecord = TestCredentialRecords.userCredential().build();\n\t\tassertThatNoException().isThrownBy(() -> this.userCredentials.delete(credentialRecord.getCredentialId()));\n\t}\n\n\t@Test\n\tvoid deleteWhenNullIdThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userCredentials.delete(null));\n\t}\n\n\t@Test\n\tvoid saveThenFound() {\n\t\tImmutableCredentialRecord credentialRecord = TestCredentialRecords.userCredential().build();\n\t\tthis.userCredentials.save(credentialRecord);\n\t\tassertThat(this.userCredentials.findByCredentialId(credentialRecord.getCredentialId()))\n\t\t\t.isEqualTo(credentialRecord);\n\t\tassertThat(this.userCredentials.findByUserId(credentialRecord.getUserEntityUserId()))\n\t\t\t.containsOnly(credentialRecord);\n\t}\n\n\t@Test\n\tvoid saveWhenNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.userCredentials.save(null));\n\t}\n\n\t@Test\n\tvoid saveAndDeleteThenNotFound() {\n\t\tImmutableCredentialRecord credentialRecord = TestCredentialRecords.userCredential().build();\n\t\tthis.userCredentials.save(credentialRecord);\n\t\tthis.userCredentials.delete(credentialRecord.getCredentialId());\n\t\tassertThat(this.userCredentials.findByCredentialId(credentialRecord.getCredentialId())).isNull();\n\t\tassertThat(this.userCredentials.findByUserId(credentialRecord.getUserEntityUserId())).isEmpty();\n\t}\n\n\t@Test\n\tvoid saveWhenUpdateThenUpdated() {\n\t\tImmutableCredentialRecord credentialRecord = TestCredentialRecords.userCredential().build();\n\t\tthis.userCredentials.save(credentialRecord);\n\t\tInstant updatedLastUsed = credentialRecord.getLastUsed().plusSeconds(120);\n\t\tCredentialRecord updatedCredentialRecord = ImmutableCredentialRecord.fromCredentialRecord(credentialRecord)\n\t\t\t.lastUsed(updatedLastUsed)\n\t\t\t.build();\n\t\tthis.userCredentials.save(updatedCredentialRecord);\n\t\tassertThat(this.userCredentials.findByCredentialId(credentialRecord.getCredentialId()))\n\t\t\t.isEqualTo(updatedCredentialRecord);\n\t\tassertThat(this.userCredentials.findByUserId(credentialRecord.getUserEntityUserId()))\n\t\t\t.containsOnly(updatedCredentialRecord);\n\t}\n\n\t@Test\n\tvoid saveWhenSameUserThenUpdated() {\n\t\tImmutableCredentialRecord credentialRecord = TestCredentialRecords.userCredential().build();\n\t\tthis.userCredentials.save(credentialRecord);\n\t\tCredentialRecord newCredentialRecord = ImmutableCredentialRecord.fromCredentialRecord(credentialRecord)\n\t\t\t.credentialId(TestBytes.get())\n\t\t\t.build();\n\t\tthis.userCredentials.save(newCredentialRecord);\n\t\tassertThat(this.userCredentials.findByCredentialId(credentialRecord.getCredentialId()))\n\t\t\t.isEqualTo(credentialRecord);\n\t\tassertThat(this.userCredentials.findByCredentialId(newCredentialRecord.getCredentialId()))\n\t\t\t.isEqualTo(newCredentialRecord);\n\t\tassertThat(this.userCredentials.findByUserId(credentialRecord.getUserEntityUserId()))\n\t\t\t.containsOnly(credentialRecord, newCredentialRecord);\n\t}\n\n\t@Test\n\tvoid saveWhenDifferentUserThenNewEntryAdded() {\n\t\tImmutableCredentialRecord credentialRecord = TestCredentialRecords.userCredential().build();\n\t\tthis.userCredentials.save(credentialRecord);\n\t\tCredentialRecord newCredentialRecord = ImmutableCredentialRecord.fromCredentialRecord(credentialRecord)\n\t\t\t.userEntityUserId(TestBytes.get())\n\t\t\t.credentialId(TestBytes.get())\n\t\t\t.build();\n\t\tthis.userCredentials.save(newCredentialRecord);\n\t\tassertThat(this.userCredentials.findByCredentialId(credentialRecord.getCredentialId()))\n\t\t\t.isEqualTo(credentialRecord);\n\t\tassertThat(this.userCredentials.findByCredentialId(newCredentialRecord.getCredentialId()))\n\t\t\t.isEqualTo(newCredentialRecord);\n\t\tassertThat(this.userCredentials.findByUserId(credentialRecord.getUserEntityUserId()))\n\t\t\t.containsOnly(credentialRecord);\n\t\tassertThat(this.userCredentials.findByUserId(newCredentialRecord.getUserEntityUserId()))\n\t\t\t.containsOnly(newCredentialRecord);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/management/TestPublicKeyCredentialRpEntities.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;\n\npublic final class TestPublicKeyCredentialRpEntities {\n\n\tpublic static PublicKeyCredentialRpEntity.PublicKeyCredentialRpEntityBuilder createRpEntity() {\n\t\treturn PublicKeyCredentialRpEntity.builder().id(\"example.localhost\").name(\"Spring Security Relying Party\");\n\t}\n\n\tprivate TestPublicKeyCredentialRpEntities() {\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/management/Webauthn4jRelyingPartyOperationsTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.management;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.time.Duration;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.fasterxml.jackson.core.Base64Variants;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.JsonNodeFactory;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\nimport com.fasterxml.jackson.dataformat.cbor.CBORFactory;\nimport com.webauthn4j.WebAuthnManager;\nimport com.webauthn4j.converter.AttestationObjectConverter;\nimport com.webauthn4j.converter.util.ObjectConverter;\nimport com.webauthn4j.data.AuthenticationData;\nimport com.webauthn4j.data.AuthenticationRequest;\nimport com.webauthn4j.data.attestation.AttestationObject;\nimport com.webauthn4j.data.attestation.authenticator.AttestedCredentialData;\nimport com.webauthn4j.data.attestation.authenticator.AuthenticatorData;\nimport com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput;\nimport org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport tools.jackson.databind.json.JsonMapper;\nimport tools.jackson.dataformat.cbor.CBORMapper;\n\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.PasswordEncodedUser;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse.AuthenticatorAttestationResponseBuilder;\nimport org.springframework.security.web.webauthn.api.AuthenticatorSelectionCriteria;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialRecord;\nimport org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialParameters;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.TestAuthenticationAssertionResponses;\nimport org.springframework.security.web.webauthn.api.TestAuthenticatorAttestationResponses;\nimport org.springframework.security.web.webauthn.api.TestCredentialRecords;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentials;\nimport org.springframework.security.web.webauthn.api.UserVerificationRequirement;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.assertj.core.api.Assertions.assertThatNoException;\nimport static org.assertj.core.api.Assertions.assertThatRuntimeException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.RETURNS_SELF;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoInteractions;\n\n@ExtendWith(MockitoExtension.class)\nclass Webauthn4jRelyingPartyOperationsTests {\n\n\t@Mock\n\tprivate PublicKeyCredentialUserEntityRepository userEntities;\n\n\t@Mock\n\tprivate UserCredentialRepository userCredentials;\n\n\t// AuthenticatorDataFlags.Bitmasks\n\tprivate static byte UP = 0x01;\n\n\tprivate static byte UV = 0x04;\n\n\tprivate static byte BE = 0x08;\n\n\tprivate static byte BS = 0x10;\n\n\tprivate Set<String> origins = Set.of(\"https://example.localhost:8443\");\n\n\tprivate UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(\"user\", \"password\",\n\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_USER\"));\n\n\tprivate PublicKeyCredentialRpEntity rpEntity = TestPublicKeyCredentialRpEntities.createRpEntity().build();\n\n\tprivate Webauthn4JRelyingPartyOperations rpOperations;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tthis.rpOperations = new Webauthn4JRelyingPartyOperations(this.userEntities, this.userCredentials, this.rpEntity,\n\t\t\t\tthis.origins);\n\t}\n\n\tString label = \"Phone\";\n\n\t@Test\n\tvoid constructorWhenUserEntitiesNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new Webauthn4JRelyingPartyOperations(null, this.userCredentials, this.rpEntity, this.origins));\n\t}\n\n\t@Test\n\tvoid constructorWhenUserCredentialsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(\n\t\t\t\t() -> new Webauthn4JRelyingPartyOperations(this.userEntities, null, this.rpEntity, this.origins));\n\t}\n\n\t@Test\n\tvoid constructorWhenRpEntityNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Webauthn4JRelyingPartyOperations(this.userEntities,\n\t\t\t\tthis.userCredentials, null, this.origins));\n\t}\n\n\t@Test\n\tvoid constructorWhenOriginsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new Webauthn4JRelyingPartyOperations(this.userEntities,\n\t\t\t\tthis.userCredentials, this.rpEntity, null));\n\t}\n\n\t@Test\n\tvoid createPublicKeyCredentialCreationOptionsWhenRequestNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.rpOperations.createPublicKeyCredentialCreationOptions(null));\n\t}\n\n\t@Test\n\tvoid createPublicKeyCredentialCreationOptionsWhenAnonymousThenIllegalArgumentException() {\n\t\tAnonymousAuthenticationToken anonymous = new AnonymousAuthenticationToken(\"key\", \"notAuthenticated\",\n\t\t\t\tSet.of(() -> \"ROLE_ANONYMOUS\"));\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.rpOperations.createPublicKeyCredentialCreationOptions(\n\t\t\t\t\tnew ImmutablePublicKeyCredentialCreationOptionsRequest(anonymous)));\n\t}\n\n\t@Test\n\tvoid createPublicKeyCredentialCreationOptionsWhenNotIsAuthenticatedThenIllegalArgumentException() {\n\t\tUsernamePasswordAuthenticationToken notAuthenticated = new UsernamePasswordAuthenticationToken(\"user\",\n\t\t\t\t\"password\");\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> this.rpOperations.createPublicKeyCredentialCreationOptions(\n\t\t\t\t\tnew ImmutablePublicKeyCredentialCreationOptionsRequest(notAuthenticated)));\n\t}\n\n\t@Test\n\tvoid createPublicKeyCredentialCreationOptionsWhenDefaultsThenSuccess() {\n\t\tPublicKeyCredentialCreationOptions expectedCreationOptions = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.rp(this.rpEntity)\n\t\t\t.user(TestPublicKeyCredentialUserEntities.userEntity().build())\n\t\t\t.build();\n\t\tPublicKeyCredentialCreationOptions creationOptions = this.rpOperations.createPublicKeyCredentialCreationOptions(\n\t\t\t\tnew ImmutablePublicKeyCredentialCreationOptionsRequest(this.user));\n\t\tassertThat(creationOptions).usingRecursiveComparison()\n\t\t\t.ignoringFields(\"challenge\", \"user.id\")\n\t\t\t.isEqualTo(expectedCreationOptions);\n\t\t// https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-rp\n\t\tassertThat(creationOptions.getRp()).isNotNull();\n\t\tassertThat(creationOptions.getRp().getName()).describedAs(\"Its value's name member is REQUIRED\").isNotNull();\n\t\t// https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-user\n\t\tPublicKeyCredentialUserEntity userEntity = creationOptions.getUser();\n\t\tassertThat(userEntity).isNotNull();\n\t\tassertThat(userEntity.getName()).isNotNull();\n\t\tassertThat(userEntity.getDisplayName()).isNotNull();\n\t\t// https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-id\n\t\tBytes userId = userEntity.getId();\n\t\tassertThat(userId).isNotNull();\n\t\tassertThat(userId.getBytes()).describedAs(\"user id is a maximum size of 64 bytes\").hasSizeLessThanOrEqualTo(64);\n\t\tassertThat(userId.getBytes())\n\t\t\t.describedAs(\"we want enough entropy in the user id, so it should be at least 16 bytes\")\n\t\t\t.hasSizeGreaterThanOrEqualTo(16);\n\n\t\tBytes challenge = creationOptions.getChallenge();\n\t\tassertThat(challenge).isNotNull();\n\t\tbyte[] challengeBytes = challenge.getBytes();\n\t\t// https://www.w3.org/TR/webauthn-3/#sctn-cryptographic-challenges\n\t\tassertThat(challengeBytes).describedAs(\"Challenges should be at least 16 bytes\")\n\t\t\t.hasSizeGreaterThanOrEqualTo(16);\n\t\t// https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams\n\t\tassertThat(creationOptions.getPubKeyCredParams()).describedAs(\n\t\t\t\t\"Relying Parties that wish to support a wide range of authenticators SHOULD include at least the following COSEAlgorithmIdentifier values\")\n\t\t\t.containsExactly(PublicKeyCredentialParameters.EdDSA, PublicKeyCredentialParameters.ES256,\n\t\t\t\t\tPublicKeyCredentialParameters.RS256);\n\t}\n\n\t@Test\n\tvoid createPublicKeyCredentialCreationOptionsWhenCustomizeThenCustomized() {\n\t\tDuration overriddenTimeout = Duration.ofMinutes(10);\n\t\tthis.rpOperations.setCustomizeCreationOptions((options) -> options.timeout(overriddenTimeout));\n\t\tPublicKeyCredentialCreationOptions creationOptions = this.rpOperations.createPublicKeyCredentialCreationOptions(\n\t\t\t\tnew ImmutablePublicKeyCredentialCreationOptionsRequest(this.user));\n\t\tassertThat(creationOptions.getTimeout()).isEqualTo(overriddenTimeout);\n\t}\n\n\t@Test\n\tvoid createPublicKeyCredentialCreationOptionsWhenExcludesThenSuccess() {\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tCredentialRecord credentialRecord = TestCredentialRecords.userCredential().build();\n\t\tPublicKeyCredentialDescriptor descriptor = PublicKeyCredentialDescriptor.builder()\n\t\t\t.id(credentialRecord.getCredentialId())\n\t\t\t.transports(credentialRecord.getTransports())\n\t\t\t.build();\n\t\tgiven(this.userEntities.findByUsername(this.user.getName())).willReturn(userEntity);\n\t\tgiven(this.userCredentials.findByUserId(userEntity.getId())).willReturn(Arrays.asList(credentialRecord));\n\t\tPublicKeyCredentialCreationOptions creationOptions = this.rpOperations.createPublicKeyCredentialCreationOptions(\n\t\t\t\tnew ImmutablePublicKeyCredentialCreationOptionsRequest(this.user));\n\n\t\tRecursiveComparisonConfiguration configuration = RecursiveComparisonConfiguration.builder().build();\n\t\tassertThat(creationOptions.getExcludeCredentials()).usingRecursiveFieldByFieldElementComparator(configuration)\n\t\t\t.containsOnly(descriptor);\n\t}\n\n\t// registerCredential\n\n\t@Test\n\tvoid registerCredentialWhenRpRegistrationRequestNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.rpOperations.registerCredential(null));\n\t}\n\n\t@Test\n\tvoid registerCredentialWhenDefaultTransportsThenSuccess() {\n\t\tPublicKeyCredentialCreationOptions creationOptions = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> publicKeyCredential = TestPublicKeyCredentials\n\t\t\t.createPublicKeyCredential()\n\t\t\t.build();\n\t\tRelyingPartyPublicKey rpPublicKey = new RelyingPartyPublicKey(publicKeyCredential, this.label);\n\n\t\tImmutableRelyingPartyRegistrationRequest rpRegistrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\tcreationOptions, rpPublicKey);\n\t\tCredentialRecord credentialRecord = this.rpOperations.registerCredential(rpRegistrationRequest);\n\t\tassertThat(credentialRecord).isNotNull();\n\t\tassertThat(credentialRecord.getCredentialId()).isNotNull();\n\t\tassertThat(credentialRecord.getTransports()).containsExactlyInAnyOrder(AuthenticatorTransport.INTERNAL,\n\t\t\t\tAuthenticatorTransport.HYBRID);\n\t}\n\n\t@Test\n\tvoid registerCredentialWhenCreationOptionsAreJavaDeserializedThenDoesNotThrow()\n\t\t\tthrows IOException, ClassNotFoundException {\n\t\tPublicKeyCredentialCreationOptions creationOptions = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> publicKeyCredential = TestPublicKeyCredentials\n\t\t\t.createPublicKeyCredential()\n\t\t\t.build();\n\t\tRelyingPartyPublicKey rpPublicKey = new RelyingPartyPublicKey(publicKeyCredential, this.label);\n\n\t\ttry (ByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\t\tObjectOutputStream objectOutputStream = new ObjectOutputStream(out)) {\n\t\t\tobjectOutputStream.writeObject(creationOptions);\n\t\t\tobjectOutputStream.flush();\n\n\t\t\ttry (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());\n\t\t\t\t\tObjectInputStream objectInputStream = new ObjectInputStream(in)) {\n\t\t\t\tPublicKeyCredentialCreationOptions deserialized = (PublicKeyCredentialCreationOptions) objectInputStream\n\t\t\t\t\t.readObject();\n\t\t\t\tImmutableRelyingPartyRegistrationRequest rpRegistrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\t\t\tdeserialized, rpPublicKey);\n\t\t\t\tassertThatNoException().isThrownBy(() -> this.rpOperations.registerCredential(rpRegistrationRequest));\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tvoid registerCredentialWhenInternalTransportThenCredentialRecordHasTransport() {\n\t\tPublicKeyCredentialCreationOptions creationOptions = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tAuthenticatorAttestationResponse response = TestAuthenticatorAttestationResponses\n\t\t\t.createAuthenticatorAttestationResponse()\n\t\t\t.transports(AuthenticatorTransport.INTERNAL)\n\t\t\t.build();\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> publicKeyCredential = TestPublicKeyCredentials\n\t\t\t.createPublicKeyCredential()\n\t\t\t.response(response)\n\t\t\t.build();\n\t\tRelyingPartyPublicKey rpPublicKey = new RelyingPartyPublicKey(publicKeyCredential, this.label);\n\n\t\tImmutableRelyingPartyRegistrationRequest rpRegistrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\tcreationOptions, rpPublicKey);\n\t\tCredentialRecord credentialRecord = this.rpOperations.registerCredential(rpRegistrationRequest);\n\t\tassertThat(credentialRecord).isNotNull();\n\t\tassertThat(credentialRecord.getTransports()).containsExactlyInAnyOrder(AuthenticatorTransport.INTERNAL);\n\t}\n\n\t@Test\n\tvoid registerCredentialWhenExistsThenException() {\n\t\tPublicKeyCredentialCreationOptions creationOptions = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> publicKeyCredential = TestPublicKeyCredentials\n\t\t\t.createPublicKeyCredential()\n\t\t\t.build();\n\t\tRelyingPartyPublicKey rpPublicKey = new RelyingPartyPublicKey(publicKeyCredential, this.label);\n\n\t\tImmutableRelyingPartyRegistrationRequest rpRegistrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\tcreationOptions, rpPublicKey);\n\t\tgiven(this.userCredentials.findByCredentialId(publicKeyCredential.getRawId()))\n\t\t\t.willReturn(TestCredentialRecords.userCredential().build());\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(rpRegistrationRequest));\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 7. Verify that the value of C.type is webauthn.create.\n\t */\n\t@Test\n\tvoid registerCredentialWhenCTypeIsNotWebAuthn() {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tString originalClientDataJSON = new String(\n\t\t\t\tTestAuthenticatorAttestationResponses.createAuthenticatorAttestationResponse()\n\t\t\t\t\t.build()\n\t\t\t\t\t.getClientDataJSON()\n\t\t\t\t\t.getBytes());\n\t\tString invalidTypeClientDataJSON = originalClientDataJSON.replace(\"webauthn.create\", \"webauthn.INVALID\");\n\t\tAuthenticatorAttestationResponseBuilder responseBldr = TestAuthenticatorAttestationResponses\n\t\t\t.createAuthenticatorAttestationResponse()\n\t\t\t.clientDataJSON(new Bytes(invalidTypeClientDataJSON.getBytes(StandardCharsets.UTF_8)));\n\t\tPublicKeyCredential publicKey = TestPublicKeyCredentials.createPublicKeyCredential(responseBldr.build())\n\t\t\t.build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(registrationRequest))\n\t\t\t.withMessageContaining(\"ClientData.type\");\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 8. Verify that the value of C.challengeBytes equals the base64url encoding of\n\t * options.challengeBytes.\n\t */\n\t@Test\n\tvoid registerCredentialWhenCChallengeNotEqualBase64UrlEncodingOptionsChallenge() {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t// change the expected challenge so it does not match\n\t\t\t.challenge(Bytes.fromBase64(\"h0vgwGQjoCzAzDUsmzPpk-JVIJRRgn0L4KVSYNRcEZc\"))\n\t\t\t.build();\n\t\tAuthenticatorAttestationResponseBuilder responseBldr = TestAuthenticatorAttestationResponses\n\t\t\t.createAuthenticatorAttestationResponse();\n\t\tPublicKeyCredential publicKey = TestPublicKeyCredentials.createPublicKeyCredential(responseBldr.build())\n\t\t\t.build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(registrationRequest))\n\t\t\t.withMessageContaining(\"challenge\");\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 9. Verify that the value of C.origin is an origin expected by the Relying Party.\n\t * See 13.4.9 Validating the origin of a credential for guidance.\n\t */\n\t@Test\n\tvoid registerCredentialWhenCOriginNotExpected() {\n\t\tthis.rpOperations = new Webauthn4JRelyingPartyOperations(this.userEntities, this.userCredentials, this.rpEntity,\n\t\t\t\tSet.of(\"https://doesnotmatch.localhost:8443\"));\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tAuthenticatorAttestationResponseBuilder responseBldr = TestAuthenticatorAttestationResponses\n\t\t\t.createAuthenticatorAttestationResponse();\n\t\tPublicKeyCredential publicKey = TestPublicKeyCredentials.createPublicKeyCredential(responseBldr.build())\n\t\t\t.build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(registrationRequest))\n\t\t\t.withMessageContaining(\"origin\");\n\t}\n\n\t// FIXME: Need to add 10. If C.topOrigin is present\n\t// https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 13. Verify that the rpIdHash in authData is the SHA-256 hash of the RP ID expected\n\t * by the Relying Party.\n\t */\n\t@Test\n\tvoid registerCredentialWhenClientDataJSONDoesNotMatchHash() {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.rp(PublicKeyCredentialRpEntity.builder().id(\"invalid\").name(\"Spring Security\").build())\n\t\t\t.build();\n\t\tAuthenticatorAttestationResponseBuilder responseBldr = TestAuthenticatorAttestationResponses\n\t\t\t.createAuthenticatorAttestationResponse();\n\t\tPublicKeyCredential publicKey = TestPublicKeyCredentials.createPublicKeyCredential(responseBldr.build())\n\t\t\t.build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(registrationRequest))\n\t\t\t.withMessageContaining(\"hash\");\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 14. Verify that the UP bit of the flags in authData is set.\n\t */\n\t@Test\n\tvoid registerCredentialWhenUPFlagsNotSet() throws Exception {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\n\t\tPublicKeyCredential publicKey = TestPublicKeyCredentials.createPublicKeyCredential(setFlag(UP)).build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(registrationRequest))\n\t\t\t.withMessageContaining(\"UP flag\");\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 15. If the Relying Party requires user verification for this registration, verify\n\t * that the UV bit of the flags in authData is set.\n\t */\n\t@Test\n\tvoid registerCredentialWhenUVBitNotSet() throws Exception {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.authenticatorSelection(AuthenticatorSelectionCriteria.builder()\n\t\t\t\t.userVerification(UserVerificationRequirement.REQUIRED)\n\t\t\t\t.build())\n\t\t\t.build();\n\t\tPublicKeyCredential publicKey = TestPublicKeyCredentials.createPublicKeyCredential(setFlag(UV)).build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(registrationRequest))\n\t\t\t.withMessageContaining(\"UV flag\");\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 16. If the BE bit of the flags in authData is not set, verify that the BS bit is\n\t * not set.\n\t */\n\t@Test\n\tvoid registerCredentialWhenBENotSetAndBSSet() throws Exception {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tPublicKeyCredential publicKey = TestPublicKeyCredentials.createPublicKeyCredential(setFlag(BE)).build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(registrationRequest))\n\t\t\t.withMessageContaining(\"Backup state bit\");\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 17. If the Relying Party uses the credential's backup eligibility to inform its\n\t * user experience flows and/or policies, evaluate the BE bit of the flags in\n\t * authData.\n\t */\n\t@Test\n\tvoid registerCredentialWhenBEInformsUserExperienceBETrue() {\n\t\t// FIXME: Implement this\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 18. If the Relying Party uses the credential's backup state to inform its user\n\t * experience flows and/or policies, evaluate the BS bit of the flags in authData.\n\t */\n\t@Test\n\tvoid registerCredentialWhenBSInformsUserExperienceBSTrue() {\n\t\t// FIXME: Search for AuthenticatorDataFlags.BS to implement\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 19. Verify that the \"alg\" parameter in the credential public key in authData\n\t * matches the alg attribute of one of the items in options.pubKeyCredParams.\n\t */\n\t@Test\n\tvoid registerCredentialWhenAlgDoesNotMatchOptions() {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.pubKeyCredParams(PublicKeyCredentialParameters.RS1)\n\t\t\t.build();\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> publicKey = TestPublicKeyCredentials\n\t\t\t.createPublicKeyCredential()\n\t\t\t.build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\n\t\tassertThatRuntimeException().isThrownBy(() -> this.rpOperations.registerCredential(registrationRequest))\n\t\t\t.withMessageContaining(\"options.pubKeyCredParams\");\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential\n\t *\n\t * 20. Verify that the values of the client extension outputs in\n\t * clientExtensionResults and the authenticator extension outputs in the extensions in\n\t * authData are as expected, considering the client extension input values that were\n\t * given in options.extensions and any specific policy of the Relying Party regarding\n\t * unsolicited extensions, i.e., those that were not specified as part of\n\t * options.extensions. In the general case, the meaning of \"are as expected\" is\n\t * specific to the Relying Party and which extensions are in use.\n\t */\n\t@Test\n\tvoid registerCredentialWhenClientExtensionOutputsDoNotMatch() {\n\t\t// FIXME: Implement this\n\t}\n\n\t/**\n\t * https://www.w3.org/TR/webauthn-3/#reg-ceremony-verify-attestation\n\t *\n\t * 22. Verify that attStmt is a correct attestation statement, conveying a valid\n\t * attestation signature, by using the attestation statement format fmt's verification\n\t * procedure given attStmt, authData and hash.\n\t */\n\t@Test\n\tvoid registerCredentialWhenFmtNotValid() throws Exception {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tPublicKeyCredential publicKey = TestPublicKeyCredentials.createPublicKeyCredential() // setFmt(\"packed\")\n\t\t\t.build();\n\t\tImmutableRelyingPartyRegistrationRequest registrationRequest = new ImmutableRelyingPartyRegistrationRequest(\n\t\t\t\toptions, new RelyingPartyPublicKey(publicKey, this.label));\n\n\t\t// FIXME: Implement this test\n\t\t// assertThatThrownBy(() ->\n\t\t// this.rpOperations.registerCredential(registrationRequest)).hasMessageContaining(\"Flag\n\t\t// combination is invalid\");\n\t}\n\n\t@Test\n\tvoid createCredentialRequestOptionsThenUserVerificationSameAsCreation() {\n\t\tPublicKeyCredentialCreationOptions creationOptions = this.rpOperations.createPublicKeyCredentialCreationOptions(\n\t\t\t\tnew ImmutablePublicKeyCredentialCreationOptionsRequest(this.user));\n\t\tPublicKeyCredentialRequestOptionsRequest createRequest = new ImmutablePublicKeyCredentialRequestOptionsRequest(\n\t\t\t\tthis.user);\n\t\tPublicKeyCredentialRequestOptions credentialRequestOptions = this.rpOperations\n\t\t\t.createCredentialRequestOptions(createRequest);\n\t\tassertThat(credentialRequestOptions.getUserVerification())\n\t\t\t.isEqualTo(creationOptions.getAuthenticatorSelection().getUserVerification());\n\t}\n\n\t@Test\n\tvoid createCredentialRequestOptionsWhenAnonymousAuthentication() {\n\t\tAnonymousAuthenticationToken authentication = new AnonymousAuthenticationToken(\"key\", \"anonymousUser\",\n\t\t\t\tSet.of(() -> \"ROLE_ANONYMOUS\"));\n\t\tPublicKeyCredentialRequestOptionsRequest createRequest = new ImmutablePublicKeyCredentialRequestOptionsRequest(\n\t\t\t\tauthentication);\n\t\tPublicKeyCredentialRequestOptions credentialRequestOptions = this.rpOperations\n\t\t\t.createCredentialRequestOptions(createRequest);\n\n\t\tassertThat(credentialRequestOptions.getAllowCredentials()).isEmpty();\n\t\t// verify anonymous user not saved\n\t\tverifyNoInteractions(this.userEntities);\n\t}\n\n\t@Test\n\tvoid createCredentialRequestOptionsWhenNullAuthentication() {\n\t\tPublicKeyCredentialRequestOptionsRequest createRequest = new ImmutablePublicKeyCredentialRequestOptionsRequest(\n\t\t\t\tnull);\n\t\tPublicKeyCredentialRequestOptions credentialRequestOptions = this.rpOperations\n\t\t\t.createCredentialRequestOptions(createRequest);\n\n\t\tassertThat(credentialRequestOptions.getAllowCredentials()).isEmpty();\n\t\t// verify anonymous user not saved\n\t\tverifyNoInteractions(this.userEntities);\n\t}\n\n\t@Test\n\tvoid createCredentialRequestOptionsWhenAuthenticated() {\n\t\tUserDetails user = PasswordEncodedUser.user();\n\t\tUsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, null,\n\t\t\t\tuser.getAuthorities());\n\t\tPublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntities.userEntity().build();\n\t\tCredentialRecord credentialRecord = TestCredentialRecords.userCredential().build();\n\t\tgiven(this.userEntities.findByUsername(user.getUsername())).willReturn(userEntity);\n\t\tgiven(this.userCredentials.findByUserId(userEntity.getId())).willReturn(Arrays.asList(credentialRecord));\n\t\tPublicKeyCredentialRequestOptionsRequest createRequest = new ImmutablePublicKeyCredentialRequestOptionsRequest(\n\t\t\t\tauth);\n\t\tPublicKeyCredentialRequestOptions credentialRequestOptions = this.rpOperations\n\t\t\t.createCredentialRequestOptions(createRequest);\n\n\t\tassertThat(credentialRequestOptions.getAllowCredentials()).extracting(PublicKeyCredentialDescriptor::getId)\n\t\t\t.containsExactly(credentialRecord.getCredentialId());\n\t}\n\n\t// gh-18158\n\t@Test\n\tvoid authenticateThenWa4jRequestCredentialIdIsRawIdBytes() throws Exception {\n\t\tPublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions.create().build();\n\t\tAuthenticatorAssertionResponse response = TestAuthenticationAssertionResponses\n\t\t\t.createAuthenticatorAssertionResponse()\n\t\t\t.build();\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> credentials = TestPublicKeyCredentials\n\t\t\t.createPublicKeyCredential(response)\n\t\t\t.build();\n\t\tRelyingPartyAuthenticationRequest request = new RelyingPartyAuthenticationRequest(options, credentials);\n\t\tPublicKeyCredential<AuthenticatorAssertionResponse> publicKey = request.getPublicKey();\n\n\t\tImmutableCredentialRecord credentialRecord = TestCredentialRecords.fullUserCredential().build();\n\t\tgiven(this.userCredentials.findByCredentialId(publicKey.getRawId())).willReturn(credentialRecord);\n\t\tJsonMapper json = new JsonMapper();\n\t\tCBORMapper cbor = mock(CBORMapper.class);\n\t\tCBORMapper.Builder builder = mock(CBORMapper.Builder.class, RETURNS_SELF);\n\t\tgiven(builder.build()).willReturn(cbor);\n\t\tgiven(cbor.rebuild()).willReturn(builder);\n\t\tAttestationObject attestationObject = mock(AttestationObject.class);\n\t\tAuthenticatorData wa4jAuthData = mock(AuthenticatorData.class);\n\t\tgiven(attestationObject.getAuthenticatorData()).willReturn(wa4jAuthData);\n\t\tgiven(wa4jAuthData.getAttestedCredentialData()).willReturn(mock(AttestedCredentialData.class));\n\t\tgiven(cbor.readValue(credentialRecord.getAttestationObject().getBytes(), AttestationObject.class))\n\t\t\t.willReturn(attestationObject);\n\t\tthis.rpOperations.setObjectConverter(new ObjectConverter(json, cbor));\n\n\t\tWebAuthnManager manager = mock(WebAuthnManager.class);\n\t\tArgumentCaptor<AuthenticationRequest> wa4jRequest = ArgumentCaptor.forClass(AuthenticationRequest.class);\n\t\tAuthenticationData wa4jData = mock(AuthenticationData.class);\n\t\tgiven(wa4jData.getAuthenticatorData()).willReturn(mock(AuthenticatorData.class));\n\t\tgiven(manager.verify(wa4jRequest.capture(), any())).willReturn(wa4jData);\n\t\tgiven(this.userEntities.findById(any())).willReturn(TestPublicKeyCredentialUserEntities.userEntity().build());\n\t\tthis.rpOperations.setWebAuthnManager(manager);\n\n\t\tthis.rpOperations.authenticate(request);\n\n\t\t// this ensures that our next assertion is valid (we want the rawId bytes, not the\n\t\t// id bytes to be used)\n\t\tassertThat(publicKey.getRawId().getBytes()).isNotEqualTo(publicKey.getId().getBytes());\n\t\t// ensure that the raw id bytes are passed into webauthn4j (not the id bytes which\n\t\t// are base64 encoded)\n\t\tassertThat(wa4jRequest.getValue().getCredentialId()).isEqualTo(publicKey.getRawId().getBytes());\n\t}\n\n\tprivate static AuthenticatorAttestationResponse setFlag(byte... flags) throws Exception {\n\t\tAuthenticatorAttestationResponseBuilder authAttResponseBldr = TestAuthenticatorAttestationResponses\n\t\t\t.createAuthenticatorAttestationResponse();\n\t\tbyte[] originalAttestationObjBytes = authAttResponseBldr.build().getAttestationObject().getBytes();\n\t\tObjectMapper cbor = cbor();\n\t\tAttestationObjectConverter attestationObjectConverter = new AttestationObjectConverter(new ObjectConverter());\n\t\tObjectNode attObj = (ObjectNode) cbor.readTree(originalAttestationObjBytes);\n\n\t\tbyte[] rawAuthData = attObj.get(\"authData\").binaryValue();\n\n\t\tfor (byte flag : flags) {\n\t\t\trawAuthData[32] ^= flag;\n\t\t}\n\t\tJsonNodeFactory f = JsonNodeFactory.instance;\n\t\tbyte[] updatedAttObjBytes = cbor\n\t\t\t.writeValueAsBytes(attObj.setAll(Map.of(\"authData\", f.binaryNode(rawAuthData))));\n\n\t\tAttestationObject attestationObject = attestationObjectConverter.convert(updatedAttObjBytes);\n\t\tAuthenticatorData<RegistrationExtensionAuthenticatorOutput> authenticatorData = attestationObject\n\t\t\t.getAuthenticatorData();\n\t\tboolean flagBE = authenticatorData.isFlagBE();\n\t\tboolean flagBS = authenticatorData.isFlagBS();\n\t\tauthAttResponseBldr.attestationObject(new Bytes(updatedAttObjBytes));\n\t\treturn authAttResponseBldr.build();\n\t}\n\n\tprivate static AuthenticatorAttestationResponse setFmt(String fmt) throws Exception {\n\t\tAuthenticatorAttestationResponseBuilder authAttResponseBldr = TestAuthenticatorAttestationResponses\n\t\t\t.createAuthenticatorAttestationResponse();\n\t\tbyte[] originalAttestationObjBytes = authAttResponseBldr.build().getAttestationObject().getBytes();\n\t\tObjectMapper cbor = cbor();\n\t\tObjectNode attObj = (ObjectNode) cbor.readTree(originalAttestationObjBytes);\n\t\tJsonNodeFactory f = JsonNodeFactory.instance;\n\t\tbyte[] updatedAttObjBytes = cbor.writeValueAsBytes(attObj.setAll(Map.of(\"fmt\", f.textNode(fmt))));\n\t\tauthAttResponseBldr.attestationObject(new Bytes(updatedAttObjBytes));\n\t\treturn authAttResponseBldr.build();\n\t}\n\n\tprivate static ObjectMapper cbor() {\n\t\treturn new ObjectMapper(new CBORFactory()).setBase64Variant(Base64Variants.MODIFIED_FOR_URL);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/registration/DefaultWebAuthnRegistrationPageGeneratingFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport java.time.LocalDateTime;\nimport java.time.ZoneOffset;\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.csrf.CsrfToken;\nimport org.springframework.security.web.csrf.DefaultCsrfToken;\nimport org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;\nimport org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;\nimport org.springframework.security.web.webauthn.api.TestBytes;\nimport org.springframework.security.web.webauthn.api.TestCredentialRecords;\nimport org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.RequestBuilder;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.web.util.HtmlUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\n\n@ExtendWith(MockitoExtension.class)\nclass DefaultWebAuthnRegistrationPageGeneratingFilterTests {\n\n\t@Mock\n\tprivate PublicKeyCredentialUserEntityRepository userEntities;\n\n\t@Mock\n\tprivate UserCredentialRepository userCredentials;\n\n\t@Test\n\tvoid constructorWhenNullUserEntities() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultWebAuthnRegistrationPageGeneratingFilter(null, this.userCredentials))\n\t\t\t.withMessage(\"userEntities cannot be null\");\n\t}\n\n\t@Test\n\tvoid constructorWhenNullUserCredentials() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new DefaultWebAuthnRegistrationPageGeneratingFilter(this.userEntities, null))\n\t\t\t.withMessage(\"userCredentials cannot be null\");\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotMatchThenNoInteractions() throws Exception {\n\t\tMockMvc mockMvc = mockMvc();\n\t\tmockMvc.perform(get(\"/not-match\"));\n\n\t\tverifyNoInteractions(this.userEntities, this.userCredentials);\n\t}\n\n\t@Test\n\tvoid doFilterThenCsrfDataAttrsPresent() throws Exception {\n\t\tPublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t.name(\"user\")\n\t\t\t.id(TestBytes.get())\n\t\t\t.displayName(\"User\")\n\t\t\t.build();\n\t\tgiven(this.userEntities.findByUsername(any())).willReturn(userEntity);\n\t\tgiven(this.userCredentials.findByUserId(userEntity.getId()))\n\t\t\t.willReturn(Arrays.asList(TestCredentialRecords.userCredential().build()));\n\t\tString body = bodyAsString(matchingRequest());\n\t\tassertThat(body).contains(\"setupRegistration({\\\"X-CSRF-TOKEN\\\" : \\\"CSRF_TOKEN\\\"}\");\n\t\tassertThat(body.replaceAll(\"\\\\s\", \"\")).contains(\"\"\"\n\t\t\t\t<form class=\"delete-form no-margin\" method=\"post\" action=\"/webauthn/register/NauGCN7bZ5jEBwThcde51g\">\n\t\t\t\t\t<input type=\"hidden\" name=\"method\" value=\"delete\">\n\t\t\t\t\t<input type=\"hidden\" name=\"_csrf\" value=\"CSRF_TOKEN\">\n\t\t\t\t\t<button class=\"primary small\" type=\"submit\">Delete</button>\n\t\t\t\t</form>\"\"\".replaceAll(\"\\\\s\", \"\"));\n\t}\n\n\t@Test\n\tvoid doFilterWhenNullPublicKeyCredentialUserEntityThenNoResults() throws Exception {\n\t\tString body = bodyAsString(matchingRequest());\n\t\tassertThat(body).contains(\"No Passkeys\");\n\t\tverifyNoInteractions(this.userCredentials);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNoCredentialsThenNoResults() throws Exception {\n\t\tPublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t.name(\"user\")\n\t\t\t.id(TestBytes.get())\n\t\t\t.displayName(\"User\")\n\t\t\t.build();\n\t\tgiven(this.userEntities.findByUsername(any())).willReturn(userEntity);\n\t\tgiven(this.userCredentials.findByUserId(userEntity.getId())).willReturn(Collections.emptyList());\n\t\tString body = bodyAsString(matchingRequest());\n\t\tassertThat(body).contains(\"No Passkeys\");\n\t\tverify(this.userCredentials).findByUserId(any());\n\t}\n\n\t@Test\n\tvoid doFilterWhenResultsThenDisplayed() throws Exception {\n\t\tPublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t.name(\"user\")\n\t\t\t.id(TestBytes.get())\n\t\t\t.displayName(\"User\")\n\t\t\t.build();\n\n\t\tImmutableCredentialRecord credential = TestCredentialRecords.userCredential()\n\t\t\t.created(LocalDateTime.of(2024, 9, 17, 10, 10, 42, 999_999_999).toInstant(ZoneOffset.UTC))\n\t\t\t.lastUsed(LocalDateTime.of(2024, 9, 18, 11, 11, 42, 999_999_999).toInstant(ZoneOffset.UTC))\n\t\t\t.build();\n\t\tgiven(this.userEntities.findByUsername(any())).willReturn(userEntity);\n\t\tgiven(this.userCredentials.findByUserId(userEntity.getId())).willReturn(Arrays.asList(credential));\n\t\tString body = bodyAsString(matchingRequest());\n\t\tassertThat(body).isEqualTo(\n\t\t\t\t\"\"\"\n\t\t\t\t\t\t<html>\n\t\t\t\t\t\t\t<head>\n\t\t\t\t\t\t\t\t<meta charset=\"utf-8\">\n\t\t\t\t\t\t\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\t\t\t\t\t\t\t\t<meta name=\"description\" content=\"\">\n\t\t\t\t\t\t\t\t<meta name=\"author\" content=\"\">\n\t\t\t\t\t\t\t\t<title>WebAuthn Registration</title>\n\t\t\t\t\t\t\t\t<link href=\"/default-ui.css\" rel=\"stylesheet\" />\n\t\t\t\t\t\t\t\t<script type=\"text/javascript\" src=\"/login/webauthn.js\"></script>\n\t\t\t\t\t\t\t\t<script type=\"text/javascript\">\n\t\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\t\tconst ui = {\n\t\t\t\t\t\t\t\t\t\tgetRegisterButton: function() {\n\t\t\t\t\t\t\t\t\t\t\treturn document.getElementById('register')\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tgetSuccess: function() {\n\t\t\t\t\t\t\t\t\t\t\treturn document.getElementById('success')\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tgetError: function() {\n\t\t\t\t\t\t\t\t\t\t\treturn document.getElementById('error')\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tgetLabelInput: function() {\n\t\t\t\t\t\t\t\t\t\t\treturn document.getElementById('label')\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tgetDeleteForms: function() {\n\t\t\t\t\t\t\t\t\t\t\treturn Array.from(document.getElementsByClassName(\"delete-form\"))\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tdocument.addEventListener(\"DOMContentLoaded\",() => setupRegistration({\"X-CSRF-TOKEN\" : \"CSRF_TOKEN\"}, \"\", ui));\n\t\t\t\t\t\t\t\t//-->\n\t\t\t\t\t\t\t\t</script>\n\t\t\t\t\t\t\t</head>\n\t\t\t\t\t\t\t<body>\n\t\t\t\t\t\t\t\t<div class=\"content\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"center\">WebAuthn Registration</h2>\n\t\t\t\t\t\t\t\t\t<form class=\"default-form\" method=\"post\" action=\"#\" onclick=\"return false\">\n\t\t\t\t\t\t\t\t\t\t<div id=\"success\" class=\"alert alert-success\" role=\"alert\">Success!</div>\n\t\t\t\t\t\t\t\t\t\t<div id=\"error\" class=\"alert alert-danger\" role=\"alert\"></div>\n\t\t\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\t\t\t<label for=\"label\" class=\"screenreader\">Passkey Label</label>\n\t\t\t\t\t\t\t\t\t\t\t<input type=\"text\" id=\"label\" name=\"label\" placeholder=\"Passkey Label\" required autofocus>\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t<button id=\"register\" class=\"primary\" type=\"submit\">Register</button>\n\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t\t<table class=\"table table-striped\">\n\t\t\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t\t\t<tr class=\"table-header\">\n\t\t\t\t\t\t\t\t\t\t\t\t<th>Label</th>\n\t\t\t\t\t\t\t\t\t\t\t\t<th>Created</th>\n\t\t\t\t\t\t\t\t\t\t\t\t<th>Last Used</th>\n\t\t\t\t\t\t\t\t\t\t\t\t<th>Signature Count</th>\n\t\t\t\t\t\t\t\t\t\t\t\t<th>Delete</th>\n\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t\t\t<tr class=\"v-middle\">\n\t\t\t\t\t\t\t\t\t\t\t\t<td>label</td>\n\t\t\t\t\t\t\t\t\t\t\t\t<td>2024-09-17T10:10:42Z</td>\n\t\t\t\t\t\t\t\t\t\t\t\t<td>2024-09-18T11:11:42Z</td>\n\t\t\t\t\t\t\t\t\t\t\t\t<td class=\"center\">0</td>\n\t\t\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<form class=\"delete-form no-margin\" method=\"post\" action=\"/webauthn/register/NauGCN7bZ5jEBwThcde51g\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input type=\"hidden\" name=\"method\" value=\"delete\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input type=\"hidden\" name=\"_csrf\" value=\"CSRF_TOKEN\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button class=\"primary small\" type=\"submit\">Delete</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</body>\n\t\t\t\t\t\t</html>\n\t\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid doFilterWhenResultsContainEntitiesThenEncoded() throws Exception {\n\t\tString label = \"<script>alert('Hello');</script>\";\n\t\tString htmlEncodedLabel = HtmlUtils.htmlEscape(label);\n\t\tassertThat(label).isNotEqualTo(htmlEncodedLabel);\n\t\tPublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t.name(\"user\")\n\t\t\t.id(TestBytes.get())\n\t\t\t.displayName(\"User\")\n\t\t\t.build();\n\t\tImmutableCredentialRecord credential = TestCredentialRecords.userCredential().label(label).build();\n\t\tgiven(this.userEntities.findByUsername(any())).willReturn(userEntity);\n\t\tgiven(this.userCredentials.findByUserId(userEntity.getId())).willReturn(Arrays.asList(credential));\n\t\tString body = bodyAsString(matchingRequest());\n\t\tassertThat(body).doesNotContain(credential.getLabel());\n\t\tassertThat(body).contains(htmlEncodedLabel);\n\t}\n\n\t@Test\n\tvoid doFilterWhenContextEmptyThenUrlsEmptyPrefix() throws Exception {\n\t\tPublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t.name(\"user\")\n\t\t\t.id(TestBytes.get())\n\t\t\t.displayName(\"User\")\n\t\t\t.build();\n\t\tImmutableCredentialRecord credential = TestCredentialRecords.userCredential().build();\n\t\tgiven(this.userEntities.findByUsername(any())).willReturn(userEntity);\n\t\tgiven(this.userCredentials.findByUserId(userEntity.getId())).willReturn(Arrays.asList(credential));\n\t\tString body = bodyAsString(matchingRequest());\n\t\tassertThat(body).contains(\"<script type=\\\"text/javascript\\\" src=\\\"/login/webauthn.js\\\"></script>\");\n\t\tassertThat(body).contains(\n\t\t\t\t\"document.addEventListener(\\\"DOMContentLoaded\\\",() => setupRegistration({\\\"X-CSRF-TOKEN\\\" : \\\"CSRF_TOKEN\\\"}, \\\"\\\",\");\n\t}\n\n\t@Test\n\tvoid doFilterWhenContextNotEmptyThenUrlsPrefixed() throws Exception {\n\t\tPublicKeyCredentialUserEntity userEntity = ImmutablePublicKeyCredentialUserEntity.builder()\n\t\t\t.name(\"user\")\n\t\t\t.id(TestBytes.get())\n\t\t\t.displayName(\"User\")\n\t\t\t.build();\n\t\tImmutableCredentialRecord credential = TestCredentialRecords.userCredential().build();\n\t\tgiven(this.userEntities.findByUsername(any())).willReturn(userEntity);\n\t\tgiven(this.userCredentials.findByUserId(userEntity.getId())).willReturn(Arrays.asList(credential));\n\t\tString body = bodyAsString(matchingRequest(\"/foo\"));\n\t\tassertThat(body).contains(\"<script type=\\\"text/javascript\\\" src=\\\"/foo/login/webauthn.js\\\"></script>\");\n\t\tassertThat(body).contains(\"setupRegistration({\\\"X-CSRF-TOKEN\\\" : \\\"CSRF_TOKEN\\\"}, \\\"/foo\\\",\");\n\t}\n\n\tprivate String bodyAsString(RequestBuilder request) throws Exception {\n\t\tMockMvc mockMvc = mockMvc();\n\t\tMvcResult result = mockMvc.perform(request).andReturn();\n\t\tMockHttpServletResponse response = result.getResponse();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tassertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);\n\t\treturn response.getContentAsString();\n\t}\n\n\tprivate MockHttpServletRequestBuilder matchingRequest() {\n\t\treturn matchingRequest(\"\");\n\t}\n\n\tprivate MockHttpServletRequestBuilder matchingRequest(String contextPath) {\n\t\tDefaultCsrfToken token = new DefaultCsrfToken(\"X-CSRF-TOKEN\", \"_csrf\", \"CSRF_TOKEN\");\n\t\treturn get(contextPath + \"/webauthn/register\").contextPath(contextPath)\n\t\t\t.requestAttr(CsrfToken.class.getName(), token);\n\t}\n\n\tprivate MockMvc mockMvc() {\n\t\treturn MockMvcBuilders.standaloneSetup(new Object())\n\t\t\t.addFilter(new DefaultWebAuthnRegistrationPageGeneratingFilter(this.userEntities, this.userCredentials))\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/registration/HttpSessionPublicKeyCredentialCreationOptionsRepositoryTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport org.junit.jupiter.api.Test;\n\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Tests for\n * {@link org.springframework.security.web.webauthn.authentication.HttpSessionPublicKeyCredentialRequestOptionsRepository}.\n *\n * @author Rob Winch\n * @since 6.4\n */\nclass HttpSessionPublicKeyCredentialCreationOptionsRepositoryTests {\n\n\tprivate HttpSessionPublicKeyCredentialCreationOptionsRepository repository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate MockHttpServletResponse response = new MockHttpServletResponse();\n\n\t@Test\n\tvoid integrationTests() {\n\t\tPublicKeyCredentialCreationOptions expected = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tthis.repository.save(this.request, this.response, expected);\n\t\tObject attrValue = this.request.getSession()\n\t\t\t.getAttribute(HttpSessionPublicKeyCredentialCreationOptionsRepository.DEFAULT_ATTR_NAME);\n\t\tassertThat(attrValue).isEqualTo(expected);\n\t\tPublicKeyCredentialCreationOptions loadOptions = this.repository.load(this.request);\n\t\tassertThat(loadOptions).isEqualTo(expected);\n\n\t\tthis.repository.save(this.request, this.response, null);\n\n\t\tObject attrValueAfterRemoval = this.request.getSession()\n\t\t\t.getAttribute(HttpSessionPublicKeyCredentialCreationOptionsRepository.DEFAULT_ATTR_NAME);\n\t\tassertThat(attrValueAfterRemoval).isNull();\n\t\tassertThat(this.request.getSession().getAttributeNames())\n\t\t\t.doesNotHaveToString(HttpSessionPublicKeyCredentialCreationOptionsRepository.DEFAULT_ATTR_NAME);\n\t\tPublicKeyCredentialCreationOptions loadOptionsAfterRemoval = this.repository.load(this.request);\n\t\tassertThat(loadOptionsAfterRemoval).isNull();\n\t}\n\n\t@Test\n\tvoid loadWhenNullSessionThenDoesNotCreate() {\n\t\tPublicKeyCredentialCreationOptions options = this.repository.load(this.request);\n\t\tassertThat(options).isNull();\n\t\tassertThat(this.request.getSession(false)).isNull();\n\t}\n\n\t@Test\n\tvoid saveWhenSetAttrThenCustomAttr() {\n\t\tPublicKeyCredentialCreationOptions expected = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tString customAttr = \"custom-attr\";\n\t\tthis.repository.setAttrName(customAttr);\n\t\tthis.repository.save(this.request, this.response, expected);\n\t\tassertThat(this.request.getSession().getAttribute(customAttr)).isEqualTo(expected);\n\t}\n\n\t@Test\n\tvoid loadWhenSetAttrThenCustomAttr() {\n\t\tPublicKeyCredentialCreationOptions expected = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tString customAttr = \"custom-attr\";\n\t\tthis.repository.setAttrName(customAttr);\n\t\tthis.request.getSession().setAttribute(customAttr, expected);\n\t\tPublicKeyCredentialCreationOptions actual = this.repository.load(this.request);\n\t\tassertThat(actual).isEqualTo(expected);\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/registration/PublicKeyCredentialCreationOptionsFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport java.util.Arrays;\n\nimport jakarta.servlet.FilterChain;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.security.authentication.AnonymousAuthenticationToken;\nimport org.springframework.security.authentication.TestingAuthenticationToken;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.context.SecurityContextImpl;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialDescriptor;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * Tests for {@link PublicKeyCredentialCreationOptionsFilter}.\n *\n * @author Rob Winch\n * @since 6.4\n */\n@ExtendWith(MockitoExtension.class)\nclass PublicKeyCredentialCreationOptionsFilterTests {\n\n\tprivate static String REGISTER_OPTONS_URL = \"/webauthn/register/options\";\n\n\t@Mock\n\tprivate WebAuthnRelyingPartyOperations rpOperations;\n\n\tprivate PublicKeyCredentialCreationOptionsFilter filter;\n\n\tprivate MockHttpServletRequest request;\n\n\tprivate MockHttpServletResponse response;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.filter = new PublicKeyCredentialCreationOptionsFilter(this.rpOperations);\n\t\tthis.request = new MockHttpServletRequest();\n\t\tthis.response = new MockHttpServletResponse();\n\t}\n\n\t@AfterEach\n\tvoid clear() {\n\t\tSecurityContextHolder.clearContext();\n\t}\n\n\t@Test\n\tvoid doFilterWhenCustomRequestMatcherThenUses() throws Exception {\n\t\tRequestMatcher requestMatcher = mock(RequestMatcher.class);\n\t\tthis.filter.setRequestMatcher(requestMatcher);\n\t\tFilterChain mock = mock(FilterChain.class);\n\t\tthis.filter.doFilter(this.request, this.response, mock);\n\t\tverify(requestMatcher).matches(any());\n\t}\n\n\t@Test\n\tvoid setRequestMatcherWhenNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestMatcher(null));\n\t}\n\n\t@Test\n\tvoid constructorWhenRpOperationsIsNullThenIllegalArgumentException() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new PublicKeyCredentialCreationOptionsFilter(null))\n\t\t\t.withMessage(\"rpOperations cannot be null\");\n\t}\n\n\t@Test\n\tvoid doFilterWhenWrongUrlThenNoInteractions() throws Exception {\n\t\tMockMvc mockMvc = mockMvc();\n\t\tmockMvc.perform(post(\"/foo\"));\n\t\tverifyNoInteractions(this.rpOperations);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNotAuthenticatedThenNoInvocations() throws Exception {\n\t\tMockMvc mockMvc = mockMvc();\n\t\tMockHttpServletResponse response = mockMvc.perform(post(REGISTER_OPTONS_URL)).andReturn().getResponse();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t}\n\n\t@Test\n\tvoid doFilterWhenAnonymousThenNoInvocations() throws Exception {\n\t\tAnonymousAuthenticationToken anonymous = new AnonymousAuthenticationToken(\"key\", \"anonymousUser\",\n\t\t\t\tAuthorityUtils.createAuthorityList(\"ROLE_ANONYMOUS\"));\n\t\tSecurityContextImpl context = new SecurityContextImpl(anonymous);\n\t\tSecurityContextHolder.setContext(context);\n\t\tMockMvc mockMvc = mockMvc();\n\t\tMockHttpServletResponse response = mockMvc.perform(post(REGISTER_OPTONS_URL)).andReturn().getResponse();\n\t\tassertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t}\n\n\t@Test\n\tvoid doFilterWhenGetThenNoInteractions() throws Exception {\n\t\tMockMvc mockMvc = mockMvc();\n\t\tmockMvc.perform(get(REGISTER_OPTONS_URL));\n\t\tverifyNoInteractions(this.rpOperations);\n\t}\n\n\t@Test\n\tvoid doFilterWhenNoCredentials() throws Exception {\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tgiven(this.rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);\n\t\tMockMvc mockMvc = mockMvc();\n\t\tmockMvc.perform(matchingRequest())\n\t\t\t.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().json(\"\"\"\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"rp\": {\n\t\t\t\t\t\t\t\t\t\"name\": \"SimpleWebAuthn Example\",\n\t\t\t\t\t\t\t\t\t\"id\": \"example.localhost\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"user\": {\n\t\t\t\t\t\t\t\t\t\"name\": \"user@example.localhost\",\n\t\t\t\t\t\t\t\t\t\"id\": \"oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w\",\n\t\t\t\t\t\t\t\t\t\"displayName\": \"user@example.localhost\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"challenge\": \"q7lCdd3SVQxdC-v8pnRAGEn1B2M-t7ZECWPwCAmhWvc\",\n\t\t\t\t\t\t\t\t\"pubKeyCredParams\": [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"type\": \"public-key\",\n\t\t\t\t\t\t\t\t\t\t\"alg\": -8\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"type\": \"public-key\",\n\t\t\t\t\t\t\t\t\t\t\"alg\": -7\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"type\": \"public-key\",\n\t\t\t\t\t\t\t\t\t\t\"alg\": -257\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\"timeout\": 300000,\n\t\t\t\t\t\t\t\t\"excludeCredentials\": [],\n\t\t\t\t\t\t\t\t\"authenticatorSelection\": {\n\t\t\t\t\t\t\t\t\t\"residentKey\": \"required\",\n\t\t\t\t\t\t\t\t\t\"userVerification\": \"preferred\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"attestation\": \"none\",\n\t\t\t\t\t\t\t\t\"extensions\": {\n\t\t\t\t\t\t\t\t\t\"credProps\": true\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\"\"\"));\n\t}\n\n\t@Test\n\tvoid doFilterWhenExcludeCredentialsThenIncludedInResponse() throws Exception {\n\t\tPublicKeyCredentialDescriptor credentialDescriptor = PublicKeyCredentialDescriptor.builder()\n\t\t\t.transports(AuthenticatorTransport.HYBRID)\n\t\t\t.id(Bytes.fromBase64(\"ChfoCM8CJA_wwUGDdzdtuw\"))\n\t\t\t.build();\n\t\tPublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.excludeCredentials(Arrays.asList(credentialDescriptor))\n\t\t\t.build();\n\t\tgiven(this.rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);\n\t\tMockMvc mockMvc = mockMvc();\n\t\tmockMvc.perform(matchingRequest())\n\t\t\t.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))\n\t\t\t.andExpect(status().isOk())\n\t\t\t.andExpect(content().json(\"\"\"\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"excludeCredentials\": [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"type\": \"public-key\",\n\t\t\t\t\t\t\t\t\t\t\"id\": \"ChfoCM8CJA_wwUGDdzdtuw\",\n\t\t\t\t\t\t\t\t\t\t\"transports\": [\n\t\t\t\t\t\t\t\t\t\t\t\"hybrid\"\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\"\"\"));\n\t}\n\n\tprivate MockHttpServletRequestBuilder matchingRequest() {\n\t\tTestingAuthenticationToken user = new TestingAuthenticationToken(\"user\", \"password\", \"ROLE_USER\");\n\t\tSecurityContextHolder.setContext(new SecurityContextImpl(user));\n\t\treturn post(REGISTER_OPTONS_URL);\n\t}\n\n\tprivate MockMvc mockMvc() {\n\t\treturn MockMvcBuilders.standaloneSetup(new Object())\n\t\t\t.addFilter(new PublicKeyCredentialCreationOptionsFilter(this.rpOperations))\n\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationFilterTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.skyscreamer.jsonassert.JSONAssert;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.converter.GenericHttpMessageConverter;\nimport org.springframework.mock.web.MockFilterChain;\nimport org.springframework.mock.web.MockHttpServletRequest;\nimport org.springframework.mock.web.MockHttpServletResponse;\nimport org.springframework.mock.web.MockServletContext;\nimport org.springframework.security.web.util.matcher.RequestMatcher;\nimport org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.api.TestCredentialRecords;\nimport org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;\nimport org.springframework.security.web.webauthn.management.UserCredentialRepository;\nimport org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.BDDMockito.given;\nimport static org.mockito.Mockito.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\n\n/**\n * Tests for {@link WebAuthnRegistrationFilter}.\n *\n * @author Rob Winch\n * @since 6.4\n */\n@ExtendWith(MockitoExtension.class)\nclass WebAuthnRegistrationFilterTests {\n\n\t@Mock\n\tprivate UserCredentialRepository userCredentials;\n\n\t@Mock\n\tprivate WebAuthnRelyingPartyOperations operations;\n\n\t@Mock\n\tprivate GenericHttpMessageConverter<Object> converter;\n\n\t@Mock\n\tprivate PublicKeyCredentialCreationOptionsRepository creationOptionsRepository;\n\n\t@Mock\n\tprivate FilterChain chain;\n\n\tprivate MockHttpServletRequest request = new MockHttpServletRequest();\n\n\tprivate MockHttpServletResponse response = new MockHttpServletResponse();\n\n\tprivate static final String REGISTRATION_REQUEST_BODY = \"\"\"\n\t\t\t{\n\t\t\t\t\"publicKey\": {\n\t\t\t\t\t\"credential\": {\n\t\t\t\t\t\t\"id\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n\t\t\t\t\t\t\"rawId\": \"dYF7EGnRFFIXkpXi9XU2wg\",\n\t\t\t\t\t\t\"response\": {\n\t\t\t\t\t\t\t\"attestationObject\": \"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAALraVWanqkAfvZZFYZpVEg0AEHWBexBp0RRSF5KV4vV1NsKlAQIDJiABIVggQjmrekPGzyqtoKK9HPUH-8Z2FLpoqkklFpFPQVICQ3IiWCD6I9Jvmor685fOZOyGXqUd87tXfvJk8rxj9OhuZvUALA\",\n\t\t\t\t\t\t\t\"clientDataJSON\": \"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSl9RTi10SFJYRWVKYjlNcUNrWmFPLUdOVmlibXpGVGVWMk43Z0ptQUdrQSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0\",\n\t\t\t\t\t\t\t\"transports\": [\n\t\t\t\t\t\t\t\t\"internal\",\n\t\t\t\t\t\t\t\t\"hybrid\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": \"public-key\",\n\t\t\t\t\t\t\"clientExtensionResults\": {},\n\t\t\t\t\t\t\"authenticatorAttachment\": \"platform\"\n\t\t\t\t\t},\n\t\t\t\t\t\"label\": \"1password\"\n\t\t\t\t}\n\t\t\t}\n\t\t\t\"\"\";\n\n\tprivate WebAuthnRegistrationFilter filter;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.filter = new WebAuthnRegistrationFilter(this.userCredentials, this.operations);\n\t}\n\n\t@Test\n\tvoid doFilterWhenCustomRequestRegisterCredentialMatcherThenUses() throws Exception {\n\t\tRequestMatcher requestMatcher = mock(RequestMatcher.class);\n\t\tthis.filter.setRegisterCredentialMatcher(requestMatcher);\n\t\tthis.filter.doFilter(this.request, this.response, new MockFilterChain());\n\t\tverify(requestMatcher).matches(any());\n\t}\n\n\t@Test\n\tvoid doFilterWhenCustomRequestRemoveCredentialMatcherThenUses() throws Exception {\n\t\tRequestMatcher requestMatcher = mock(RequestMatcher.class);\n\t\tgiven(requestMatcher.matcher(any())).willReturn(RequestMatcher.MatchResult.notMatch());\n\t\tthis.filter.setRemoveCredentialMatcher(requestMatcher);\n\t\tthis.filter.doFilter(this.request, this.response, new MockFilterChain());\n\t\tverify(requestMatcher).matcher(any());\n\t}\n\n\t@Test\n\tvoid setRequestRegisterCredentialWhenNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRegisterCredentialMatcher(null));\n\t}\n\n\t@Test\n\tvoid setRequestRemoveCredentialWhenNullThenIllegalArgument() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRemoveCredentialMatcher(null));\n\t}\n\n\t@Test\n\tvoid constructorWhenNullUserCredentials() {\n\t\tassertThatIllegalArgumentException().isThrownBy(() -> new WebAuthnRegistrationFilter(null, this.operations));\n\t}\n\n\t@Test\n\tvoid constructorWhenNullOperations() {\n\t\tassertThatIllegalArgumentException()\n\t\t\t.isThrownBy(() -> new WebAuthnRegistrationFilter(this.userCredentials, null));\n\t}\n\n\t@Test\n\tvoid doFilterWhenRegisterUrlDoesNotMatchThenChainContinues() throws Exception {\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tthis.filter.setConverter(this.converter);\n\t\tthis.filter.setCreationOptionsRepository(this.creationOptionsRepository);\n\t\tthis.filter.doFilter(post(\"/nomatch\").buildRequest(new MockServletContext()), response, this.chain);\n\t\tverifyNoInteractions(this.converter, this.creationOptionsRepository, response);\n\t\tverify(this.chain).doFilter(any(), any());\n\t}\n\n\t@Test\n\tvoid doFilterWhenRegisterMethodDoesNotMatchThenChainContinues() throws Exception {\n\t\tHttpServletResponse response = mock(HttpServletResponse.class);\n\t\tthis.filter.setConverter(this.converter);\n\t\tthis.filter.setCreationOptionsRepository(this.creationOptionsRepository);\n\t\tthis.filter.doFilter(\n\t\t\t\tget(WebAuthnRegistrationFilter.DEFAULT_REGISTER_CREDENTIAL_URL).buildRequest(new MockServletContext()),\n\t\t\t\tresponse, this.chain);\n\t\tverifyNoInteractions(this.converter, this.creationOptionsRepository, response);\n\t\tverify(this.chain).doFilter(any(), any());\n\t}\n\n\t@Test\n\tvoid doFilterWhenRegisterNoBodyThenBadRequest() throws Exception {\n\t\tthis.filter.doFilter(registerCredentialRequest(\"\"), this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t}\n\n\t@Test\n\tvoid doFilterWhenInvalidJsonThenBadRequest() throws Exception {\n\t\tthis.filter.doFilter(registerCredentialRequest(\"{\"), this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t}\n\n\t@Test\n\tvoid doFilterWhenRegisterOptionsNullThenBadRequest() throws Exception {\n\t\tthis.filter.setCreationOptionsRepository(this.creationOptionsRepository);\n\t\tMockHttpServletRequest request = registerCredentialRequest(REGISTRATION_REQUEST_BODY);\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());\n\t}\n\n\t@Test\n\tvoid doFilterWhenRegisterSuccessThenOk() throws Exception {\n\t\tthis.filter.setCreationOptionsRepository(this.creationOptionsRepository);\n\t\tPublicKeyCredentialCreationOptions creationOptions = TestPublicKeyCredentialCreationOptions\n\t\t\t.createPublicKeyCredentialCreationOptions()\n\t\t\t.build();\n\t\tgiven(this.creationOptionsRepository.load(any())).willReturn(creationOptions);\n\t\tImmutableCredentialRecord userCredential = TestCredentialRecords.userCredential().build();\n\t\tgiven(this.operations.registerCredential(any())).willReturn(userCredential);\n\t\tMockHttpServletRequest request = registerCredentialRequest(REGISTRATION_REQUEST_BODY);\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());\n\t\tString actualBody = this.response.getContentAsString();\n\t\tString expectedBody = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"success\": true\n\t\t\t\t}\n\t\t\t\t\"\"\";\n\t\tJSONAssert.assertEquals(expectedBody, actualBody, false);\n\t\tverify(this.creationOptionsRepository).save(any(), any(), eq(null));\n\t}\n\n\t@Test\n\tvoid doFilterWhenDeleteSuccessThenNoContent() throws Exception {\n\t\tMockHttpServletRequest request = MockMvcRequestBuilders.delete(\"/webauthn/register/123456\")\n\t\t\t.buildRequest(new MockServletContext());\n\t\tthis.filter.doFilter(request, this.response, this.chain);\n\t\tassertThat(this.response.getStatus()).isEqualTo(HttpStatus.NO_CONTENT.value());\n\t}\n\n\tprivate static MockHttpServletRequest registerCredentialRequest(String body) {\n\t\treturn MockMvcRequestBuilders.post(WebAuthnRegistrationFilter.DEFAULT_REGISTER_CREDENTIAL_URL)\n\t\t\t.content(body)\n\t\t\t.buildRequest(new MockServletContext());\n\t}\n\n}\n"
  },
  {
    "path": "webauthn/src/test/java/org/springframework/security/web/webauthn/registration/WebAuthnRegistrationRequestJacksonTests.java",
    "content": "/*\n * Copyright 2004-present the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.springframework.security.web.webauthn.registration;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport tools.jackson.databind.json.JsonMapper;\n\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttachment;\nimport org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;\nimport org.springframework.security.web.webauthn.api.AuthenticatorTransport;\nimport org.springframework.security.web.webauthn.api.Bytes;\nimport org.springframework.security.web.webauthn.api.CredentialPropertiesOutput;\nimport org.springframework.security.web.webauthn.api.ImmutableAuthenticationExtensionsClientOutputs;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredential;\nimport org.springframework.security.web.webauthn.api.PublicKeyCredentialType;\nimport org.springframework.security.web.webauthn.jackson.PublicKeyCredentialJson;\nimport org.springframework.security.web.webauthn.jackson.WebauthnJacksonModule;\nimport org.springframework.security.web.webauthn.management.RelyingPartyPublicKey;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author Rob Winch\n * @since 6.4\n */\nclass WebAuthnRegistrationRequestJacksonTests {\n\n\tprivate JsonMapper mapper;\n\n\t@BeforeEach\n\tvoid setup() {\n\t\tthis.mapper = JsonMapper.builder().addModule(new WebauthnJacksonModule()).build();\n\t}\n\n\t@Test\n\tvoid readRelyingPartyRequest() throws Exception {\n\t\tString json = \"\"\"\n\t\t\t\t{\n\t\t\t\t\t\"publicKey\": {\n\t\t\t\t\t\t\"label\": \"Cell Phone\",\n\t\t\t\t\t\t\"credential\": %s\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\"\"\".formatted(PublicKeyCredentialJson.PUBLIC_KEY_JSON);\n\t\tWebAuthnRegistrationFilter.WebAuthnRegistrationRequest registrationRequest = this.mapper.readValue(json,\n\t\t\t\tWebAuthnRegistrationFilter.WebAuthnRegistrationRequest.class);\n\n\t\tImmutableAuthenticationExtensionsClientOutputs clientExtensionResults = new ImmutableAuthenticationExtensionsClientOutputs(\n\t\t\t\tnew CredentialPropertiesOutput(false));\n\n\t\tPublicKeyCredential<AuthenticatorAttestationResponse> credential = PublicKeyCredential.builder()\n\t\t\t.id(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\")\n\t\t\t.rawId(Bytes\n\t\t\t\t.fromBase64(\"AX6nVVERrH6opMafUGn3Z9EyNEy6cftfBKV_2YxYl1jdW8CSJxMKGXFV3bnrKTiMSJeInkG7C6B2lPt8E5i3KaM\"))\n\t\t\t.response(AuthenticatorAttestationResponse.builder()\n\t\t\t\t.attestationObject(Bytes.fromBase64(\n\t\t\t\t\t\t\"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQF-p1VREax-qKTGn1Bp92fRMjRMunH7XwSlf9mMWJdY3VvAkicTChlxVd256yk4jEiXiJ5BuwugdpT7fBOYtymjpQECAyYgASFYIJK-2epPEw0ujHN-gvVp2Hp3ef8CzU3zqwO5ylx8L2OsIlggK5x5OlTGEPxLS-85TAABum4aqVK4CSWJ7LYDdkjuBLk\"))\n\t\t\t\t.clientDataJSON(Bytes.fromBase64(\n\t\t\t\t\t\t\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSUJRbnVZMVowSzFIcUJvRldDcDJ4bEpsOC1vcV9hRklYenlUX0YwLTBHVSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0\"))\n\t\t\t\t.transports(AuthenticatorTransport.HYBRID, AuthenticatorTransport.INTERNAL)\n\t\t\t\t.build())\n\t\t\t.type(PublicKeyCredentialType.PUBLIC_KEY)\n\t\t\t.clientExtensionResults(clientExtensionResults)\n\t\t\t.authenticatorAttachment(AuthenticatorAttachment.CROSS_PLATFORM)\n\t\t\t.build();\n\n\t\tWebAuthnRegistrationFilter.WebAuthnRegistrationRequest expected = new WebAuthnRegistrationFilter.WebAuthnRegistrationRequest();\n\t\texpected.setPublicKey(new RelyingPartyPublicKey(credential, \"Cell Phone\"));\n\t\tassertThat(registrationRequest).usingRecursiveComparison().isEqualTo(expected);\n\t}\n\n}\n"
  }
]